diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-06-19 03:21:54 +0300 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-06-19 03:21:54 +0300 |
| commit | 8c13415c8a4383447c21ec832b20b3b283f0e01a (patch) | |
| tree | 1a2eba52dc89ec2339ef2d63a1a7e496d40a64c0 | |
| parent | 5cd1731cc883a9914d91e3b93d4597317b5b5339 (diff) | |
| parent | 06cb687a5132fcffe624c0070576ab852ac6b568 (diff) | |
| download | linux-8c13415c8a4383447c21ec832b20b3b283f0e01a.tar.xz | |
Merge tag 'media/v7.2-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab:
- v4l2:
- core: fix subdev sensor ownership
- subdev: Allow accessing routes with STREAMS client capability
- ctrls: Add validation for HEVC active reference counts and
background detection control
- common: Add YUV24 format info and has_alpha helper
- vb2: Change vb2_read() and vb2_write() return types to ssize_t
- i2c: cvs: Add driver of Intel Computer Vision Sensing Controller(CVS)
- atmel-isc: remove deprecated driver
- cec: Add CEC Latency Indication Protocol (LIP) support
- imon: Add iMON VFD HID OEM v1.2 key mappings
- AVMatrix: new HWS capture driver
- isp4: new AMD capture driver
- qcom:
- iris: Add hierarchical coding, B-frame, and Long-Term Reference
support for encoder
- camss: Add SM6350 platform support
- venus: Add SM6115 platform support
- chips-media: wave5: Add support for Packed YUV422, CBP profile, and
background detection
- csi2rx: Add multistream support and 32 dma chans
- Several cleanups and fixes
* tag 'media/v7.2-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (394 commits)
media: v4l2-fwnode: Fix subdev owner overwritten in v4l2_async_register_subdev_sensor()
media: qcom: iris: vdec: allow GEN2 decoding into 10bit format
media: qcom: iris: vdec: update find_format to handle 8bit and 10bit formats
media: qcom: iris: vdec: update size and stride calculations for 10bit formats
media: qcom: iris: gen2: add support for 10bit decoding
media: qcom: iris: add QC10C & P010 buffer size calculations
media: qcom: iris: add helpers for 8bit and 10bit formats
media: qcom: iris: Fix FPS calculation and VPP FW overhead
media: qcom: camss: vfe-340: Support for PIX client
media: qcom: camss: vfe-340: Proper client handling
media: qcom: camss: csid-340: Enable PIX interface routing
media: qcom: camss: csid-340: Add port-to-interface mapping
media: qcom: camss: csid-340: Switch to generic CSID_CFG/CTRL registers
media: iris: Initialize HFI ops after firmware load in core init
media: iris: drop struct iris_fmt
media: iris: Add platform data for X1P42100
media: iris: Add hardware power on/off ops for X1P42100
media: iris: optimize COMV buffer allocation for VPU3x and VPU4x
media: iris: add FPS calculation and VPP FW overhead in frequency formula
media: qcom: iris: Simplify COMV size calculation
...
523 files changed, 22836 insertions, 11348 deletions
diff --git a/Documentation/admin-guide/media/amdisp4-1.rst b/Documentation/admin-guide/media/amdisp4-1.rst new file mode 100644 index 000000000000..878141154f96 --- /dev/null +++ b/Documentation/admin-guide/media/amdisp4-1.rst @@ -0,0 +1,63 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: <isonum.txt> + +==================================== +AMD Image Signal Processor (amdisp4) +==================================== + +Introduction +============ + +This file documents the driver for the AMD ISP4 that is part of +AMD Ryzen AI Max 300 Series. + +The driver is located under drivers/media/platform/amd/isp4 and uses +the Media-Controller API. + +The driver exposes one video capture device to userspace and provide +web camera like interface. Internally the video device is connected +to the isp4 sub-device responsible for communication with the CCPU FW. + +Topology +======== + +.. _amdisp4_topology_graph: + +.. kernel-figure:: amdisp4.dot + :alt: Diagram of the media pipeline topology + :align: center + + + +The driver has 1 sub-device: Representing isp4 image signal processor. +The driver has 1 video device: Capture device for retrieving images. + +- ISP4 Image Signal Processing Subdevice Node + +--------------------------------------------- + +The isp4 is represented as a single V4L2 subdev, the sub-device does not +provide interface to the user space. The sub-device is connected to one video node +(isp4_capture) with immutable active link. The sub-device represents ISP with +connected sensor similar to smart cameras (sensors with integrated ISP). +sub-device has only one link to the video device for capturing the frames. +The sub-device communicates with CCPU FW for streaming configuration and +buffer management. + + +- isp4_capture - Frames Capture Video Node + +------------------------------------------ + +Isp4_capture is a capture device to capture frames to memory. +The entity is connected to isp4 sub-device. The video device +provides web camera like interface to userspace. It supports +mmap and dma buf types of memory. + +Capturing Video Frames Example +============================== + +.. code-block:: bash + + v4l2-ctl "-d" "/dev/video0" "--set-fmt-video=width=1920,height=1080,pixelformat=NV12" "--stream-mmap" "--stream-count=10" diff --git a/Documentation/admin-guide/media/amdisp4.dot b/Documentation/admin-guide/media/amdisp4.dot new file mode 100644 index 000000000000..978f30c1a31a --- /dev/null +++ b/Documentation/admin-guide/media/amdisp4.dot @@ -0,0 +1,6 @@ +digraph board { + rankdir=TB + n00000001 [label="{{} | amd isp4\n | {<port0> 0}}", shape=Mrecord, style=filled, fillcolor=green] + n00000001:port0 -> n00000003 [style=bold] + n00000003 [label="Preview\n/dev/video0", shape=box, style=filled, fillcolor=yellow] +} diff --git a/Documentation/admin-guide/media/rkcif-rk3588-vicap.dot b/Documentation/admin-guide/media/rkcif-rk3588-vicap.dot new file mode 100644 index 000000000000..f6d3404920b5 --- /dev/null +++ b/Documentation/admin-guide/media/rkcif-rk3588-vicap.dot @@ -0,0 +1,29 @@ +digraph board { + rankdir=TB + n00000007 [label="{{<port0> 0} | rkcif-mipi2\n/dev/v4l-subdev0 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000007:port1 -> n0000000a + n00000007:port1 -> n00000010 [style=dashed] + n00000007:port1 -> n00000016 [style=dashed] + n00000007:port1 -> n0000001c [style=dashed] + n0000000a [label="rkcif-mipi2-id0\n/dev/video0", shape=box, style=filled, fillcolor=yellow] + n00000010 [label="rkcif-mipi2-id1\n/dev/video1", shape=box, style=filled, fillcolor=yellow] + n00000016 [label="rkcif-mipi2-id2\n/dev/video2", shape=box, style=filled, fillcolor=yellow] + n0000001c [label="rkcif-mipi2-id3\n/dev/video3", shape=box, style=filled, fillcolor=yellow] + n00000025 [label="{{<port0> 0} | rkcif-mipi4\n/dev/v4l-subdev1 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000025:port1 -> n00000028 + n00000025:port1 -> n0000002e [style=dashed] + n00000025:port1 -> n00000034 [style=dashed] + n00000025:port1 -> n0000003a [style=dashed] + n00000028 [label="rkcif-mipi4-id0\n/dev/video4", shape=box, style=filled, fillcolor=yellow] + n0000002e [label="rkcif-mipi4-id1\n/dev/video5", shape=box, style=filled, fillcolor=yellow] + n00000034 [label="rkcif-mipi4-id2\n/dev/video6", shape=box, style=filled, fillcolor=yellow] + n0000003a [label="rkcif-mipi4-id3\n/dev/video7", shape=box, style=filled, fillcolor=yellow] + n00000043 [label="{{<port0> 0} | dw-mipi-csi2rx fdd30000.csi\n/dev/v4l-subdev2 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000043:port1 -> n00000007:port0 + n00000048 [label="{{<port0> 0} | dw-mipi-csi2rx fdd50000.csi\n/dev/v4l-subdev3 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000048:port1 -> n00000025:port0 + n0000004d [label="{{} | imx415 3-001a\n/dev/v4l-subdev4 | {<port0> 0}}", shape=Mrecord, style=filled, fillcolor=green] + n0000004d:port0 -> n00000043:port0 + n00000051 [label="{{} | imx415 4-001a\n/dev/v4l-subdev5 | {<port0> 0}}", shape=Mrecord, style=filled, fillcolor=green] + n00000051:port0 -> n00000048:port0 +} diff --git a/Documentation/admin-guide/media/rkcif.rst b/Documentation/admin-guide/media/rkcif.rst index 2558c121abc4..313a0ea45d16 100644 --- a/Documentation/admin-guide/media/rkcif.rst +++ b/Documentation/admin-guide/media/rkcif.rst @@ -77,3 +77,35 @@ and the following video devices: .. kernel-figure:: rkcif-rk3568-vicap.dot :alt: Topology of the RK3568 Video Capture (VICAP) unit :align: center + +Rockchip RK3588 Video Capture (VICAP) +------------------------------------- + +The RK3588 Video Capture (VICAP) unit features a digital video port and six +MIPI CSI-2 capture interfaces that can receive video data independently. +The DVP accepts parallel video data, BT.656 and BT.1120. +Since the BT.1120 protocol may feature more than one stream, the RK3588 VICAP +DVP features four DMA engines that can capture different streams. +Similarly, the RK3588 VICAP MIPI CSI-2 receivers feature four DMA engines each +to handle different Virtual Channels (VCs). + +The rkcif driver represents this hardware variant by exposing the following +V4L2 subdevices: + +* dw-mipi-csi2rx fdd30000.csi: MIPI CSI-2 receiver connected to MIPI DPHY0 +* dw-mipi-csi2rx fdd50000.csi: MIPI CSI-2 receiver connected to MIPI DPHY1 +* rkcif-mipi2: INTERFACE/CROP block for the MIPI CSI-2 receiver connected to + MIPI DPHY0 +* rkcif-mipi4: INTERFACE/CROP block for the MIPI CSI-2 receiver connected to + MIPI DPHY1 + +and the following video devices: + +* rkcif-mipi2-id{0,1,2,3}: The DMA engines connected to the rkcif-mipi2 + INTERFACE/CROP block. +* rkcif-mipi4-id{0,1,2,3}: The DMA engines connected to the rkcif-mipi4 + INTERFACE/CROP block. + +.. kernel-figure:: rkcif-rk3588-vicap.dot + :alt: Topology of the RK3588 Video Capture (VICAP) unit + :align: center diff --git a/Documentation/admin-guide/media/v4l-drivers.rst b/Documentation/admin-guide/media/v4l-drivers.rst index d31da8e0a54f..4621eae9fa1e 100644 --- a/Documentation/admin-guide/media/v4l-drivers.rst +++ b/Documentation/admin-guide/media/v4l-drivers.rst @@ -9,6 +9,7 @@ Video4Linux (V4L) driver-specific documentation .. toctree:: :maxdepth: 2 + amdisp4-1 bttv c3-isp cafe_ccic diff --git a/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-video-engine.yaml b/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-video-engine.yaml index 541325f900a1..01f2afa023f0 100644 --- a/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-video-engine.yaml +++ b/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-video-engine.yaml @@ -63,6 +63,16 @@ properties: CMA pool to use for buffers allocation instead of the default CMA pool. + # FIXME: This should be made required eventually once every SoC will + # have the MBUS declared. + interconnects: + maxItems: 1 + + # FIXME: This should be made required eventually once every SoC will + # have the MBUS declared. + interconnect-names: + const: dma-mem + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/media/qcom,msm8939-venus.yaml b/Documentation/devicetree/bindings/media/qcom,msm8939-venus.yaml new file mode 100644 index 000000000000..10a50a410748 --- /dev/null +++ b/Documentation/devicetree/bindings/media/qcom,msm8939-venus.yaml @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/qcom,msm8939-venus.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm MSM8939 Venus video encode and decode accelerators + +maintainers: + - André Apitzsch <git@apitzsch.eu> + - Erikas Bitovtas <xerikasxx@gmail.com> + +description: + The Venus IP is a video encode and decode accelerator present + on Qualcomm platforms + +allOf: + - $ref: qcom,venus-common.yaml# + +properties: + compatible: + const: qcom,msm8939-venus + + power-domains: + maxItems: 3 + + power-domain-names: + items: + - const: venus + - const: vcodec0 + - const: vcodec1 + + clocks: + maxItems: 5 + + clock-names: + items: + - const: core + - const: iface + - const: bus + - const: vcodec0_core + - const: vcodec1_core + + iommus: + maxItems: 1 + +required: + - compatible + - iommus + - power-domain-names + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/clock/qcom,gcc-msm8939.h> + + video-codec@1d00000 { + compatible = "qcom,msm8939-venus"; + reg = <0x01d00000 0xff000>; + interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&gcc GCC_VENUS0_VCODEC0_CLK>, + <&gcc GCC_VENUS0_AHB_CLK>, + <&gcc GCC_VENUS0_AXI_CLK>, + <&gcc GCC_VENUS0_CORE0_VCODEC0_CLK>, + <&gcc GCC_VENUS0_CORE1_VCODEC0_CLK>; + clock-names = "core", + "iface", + "bus", + "vcodec0_core", + "vcodec1_core"; + power-domains = <&gcc VENUS_GDSC>, + <&gcc VENUS_CORE0_GDSC>, + <&gcc VENUS_CORE1_GDSC>; + power-domain-names = "venus", "vcodec0", "vcodec1"; + iommus = <&apps_iommu 5>; + memory-region = <&venus_mem>; + }; diff --git a/Documentation/devicetree/bindings/media/qcom,qcm2290-venus.yaml b/Documentation/devicetree/bindings/media/qcom,qcm2290-venus.yaml index 7e6dc410c2d2..5977e7d0a71b 100644 --- a/Documentation/devicetree/bindings/media/qcom,qcm2290-venus.yaml +++ b/Documentation/devicetree/bindings/media/qcom,qcm2290-venus.yaml @@ -18,7 +18,11 @@ allOf: properties: compatible: - const: qcom,qcm2290-venus + oneOf: + - items: + - const: qcom,sm6115-venus + - const: qcom,qcm2290-venus + - const: qcom,qcm2290-venus power-domains: maxItems: 3 diff --git a/Documentation/devicetree/bindings/media/qcom,sc7180-venus.yaml b/Documentation/devicetree/bindings/media/qcom,sc7180-venus.yaml index bfd8b1ad4731..b21bed314848 100644 --- a/Documentation/devicetree/bindings/media/qcom,sc7180-venus.yaml +++ b/Documentation/devicetree/bindings/media/qcom,sc7180-venus.yaml @@ -91,6 +91,21 @@ properties: deprecated: true additionalProperties: false + video-firmware: + type: object + additionalProperties: false + + description: | + Firmware subnode is needed when the platform does not + have TrustZone. + + properties: + iommus: + maxItems: 1 + + required: + - iommus + required: - compatible - power-domain-names diff --git a/Documentation/devicetree/bindings/media/qcom,sc7280-venus.yaml b/Documentation/devicetree/bindings/media/qcom,sc7280-venus.yaml index 413c5b4ee650..9725fcb761dc 100644 --- a/Documentation/devicetree/bindings/media/qcom,sc7280-venus.yaml +++ b/Documentation/devicetree/bindings/media/qcom,sc7280-venus.yaml @@ -43,8 +43,7 @@ properties: - const: vcodec_bus iommus: - minItems: 1 - maxItems: 2 + maxItems: 1 interconnects: maxItems: 2 @@ -120,12 +119,7 @@ examples: <&mmss_noc MASTER_VIDEO_P0 0 &mc_virt SLAVE_EBI1 0>; interconnect-names = "cpu-cfg", "video-mem"; - iommus = <&apps_smmu 0x2180 0x20>, - <&apps_smmu 0x2184 0x20>; + iommus = <&apps_smmu 0x2180 0x20>; memory-region = <&video_mem>; - - video-firmware { - iommus = <&apps_smmu 0x21a2 0x0>; - }; }; diff --git a/Documentation/devicetree/bindings/media/qcom,sm6350-camss.yaml b/Documentation/devicetree/bindings/media/qcom,sm6350-camss.yaml new file mode 100644 index 000000000000..96974d90d8c4 --- /dev/null +++ b/Documentation/devicetree/bindings/media/qcom,sm6350-camss.yaml @@ -0,0 +1,471 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/qcom,sm6350-camss.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm SM6350 Camera Subsystem (CAMSS) + +maintainers: + - Luca Weiss <luca.weiss@fairphone.com> + +description: + The CAMSS IP is a CSI decoder and ISP present on Qualcomm platforms. + +properties: + compatible: + const: qcom,sm6350-camss + + reg: + maxItems: 24 + + reg-names: + items: + - const: csid0 + - const: csid1 + - const: csid2 + - const: csid_lite + - const: csiphy0 + - const: csiphy1 + - const: csiphy2 + - const: csiphy3 + - const: vfe0 + - const: vfe1 + - const: vfe2 + - const: vfe_lite + - const: a5_csr + - const: a5_qgic + - const: a5_sierra + - const: bps + - const: camnoc + - const: core_top_csr_tcsr + - const: cpas_cdm + - const: cpas_top + - const: ipe + - const: jpeg_dma + - const: jpeg_enc + - const: lrme + + clocks: + maxItems: 39 + + clock-names: + items: + - const: cam_axi + - const: soc_ahb + - const: camnoc_axi + - const: core_ahb + - const: cpas_ahb + - const: csiphy0 + - const: csiphy0_timer + - const: csiphy1 + - const: csiphy1_timer + - const: csiphy2 + - const: csiphy2_timer + - const: csiphy3 + - const: csiphy3_timer + - const: vfe0_axi + - const: vfe0 + - const: vfe0_cphy_rx + - const: vfe0_csid + - const: vfe1_axi + - const: vfe1 + - const: vfe1_cphy_rx + - const: vfe1_csid + - const: vfe2_axi + - const: vfe2 + - const: vfe2_cphy_rx + - const: vfe2_csid + - const: vfe_lite + - const: vfe_lite_cphy_rx + - const: vfe_lite_csid + - const: bps + - const: bps_ahb + - const: bps_areg + - const: bps_axi + - const: icp + - const: ipe0 + - const: ipe0_ahb + - const: ipe0_areg + - const: ipe0_axi + - const: jpeg + - const: lrme + + interrupts: + maxItems: 18 + + interrupt-names: + items: + - const: csid0 + - const: csid1 + - const: csid2 + - const: csid_lite + - const: csiphy0 + - const: csiphy1 + - const: csiphy2 + - const: csiphy3 + - const: vfe0 + - const: vfe1 + - const: vfe2 + - const: vfe_lite + - const: a5 + - const: cpas + - const: cpas_cdm + - const: jpeg_dma + - const: jpeg_enc + - const: lrme + + interconnects: + maxItems: 4 + + interconnect-names: + items: + - const: ahb + - const: hf_mnoc + - const: sf_mnoc + - const: sf_icp_mnoc + + iommus: + maxItems: 14 + + power-domains: + maxItems: 6 + + power-domain-names: + items: + - const: ife0 + - const: ife1 + - const: ife2 + - const: top + - const: bps + - const: ipe + + vdd-csiphy0-0p9-supply: + description: + Phandle to a 0.9V regulator supply to CSIPHY0. + + vdd-csiphy0-1p25-supply: + description: + Phandle to a 1.25V regulator supply to CSIPHY0. + + vdd-csiphy1-0p9-supply: + description: + Phandle to a 0.9V regulator supply to CSIPHY1. + + vdd-csiphy1-1p25-supply: + description: + Phandle to a 1.25V regulator supply to CSIPHY1. + + vdd-csiphy2-0p9-supply: + description: + Phandle to a 0.9V regulator supply to CSIPHY2. + + vdd-csiphy2-1p25-supply: + description: + Phandle to a 1.25V regulator supply to CSIPHY2. + + vdd-csiphy3-0p9-supply: + description: + Phandle to a 0.9V regulator supply to CSIPHY3. + + vdd-csiphy3-1p25-supply: + description: + Phandle to a 1.25V regulator supply to CSIPHY3. + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + description: + CSI input ports. + + patternProperties: + "^port@[0-3]$": + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + + description: + Input port for receiving CSI data from a CSIPHY. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + minItems: 1 + maxItems: 4 + + bus-type: + enum: + - 1 # MEDIA_BUS_TYPE_CSI2_CPHY + - 4 # MEDIA_BUS_TYPE_CSI2_DPHY + + required: + - data-lanes + +required: + - compatible + - reg + - reg-names + - clocks + - clock-names + - interrupts + - interrupt-names + - interconnects + - interconnect-names + - iommus + - power-domains + - power-domain-names + - ports + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/qcom,gcc-sm6350.h> + #include <dt-bindings/clock/qcom,sm6350-camcc.h> + #include <dt-bindings/interconnect/qcom,icc.h> + #include <dt-bindings/interconnect/qcom,sm6350.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/media/video-interfaces.h> + #include <dt-bindings/power/qcom-rpmpd.h> + + soc { + #address-cells = <2>; + #size-cells = <2>; + + isp@acb3000 { + compatible = "qcom,sm6350-camss"; + + reg = <0x0 0x0acb3000 0x0 0x1000>, + <0x0 0x0acba000 0x0 0x1000>, + <0x0 0x0acc1000 0x0 0x1000>, + <0x0 0x0acc8000 0x0 0x1000>, + <0x0 0x0ac65000 0x0 0x1000>, + <0x0 0x0ac66000 0x0 0x1000>, + <0x0 0x0ac67000 0x0 0x1000>, + <0x0 0x0ac68000 0x0 0x1000>, + <0x0 0x0acaf000 0x0 0x4000>, + <0x0 0x0acb6000 0x0 0x4000>, + <0x0 0x0acbd000 0x0 0x4000>, + <0x0 0x0acc4000 0x0 0x4000>, + <0x0 0x0ac18000 0x0 0x3000>, + <0x0 0x0ac00000 0x0 0x6000>, + <0x0 0x0ac10000 0x0 0x8000>, + <0x0 0x0ac6f000 0x0 0x8000>, + <0x0 0x0ac42000 0x0 0x4600>, + <0x0 0x01fc0000 0x0 0x40000>, + <0x0 0x0ac48000 0x0 0x1000>, + <0x0 0x0ac40000 0x0 0x1000>, + <0x0 0x0ac87000 0x0 0xa000>, + <0x0 0x0ac52000 0x0 0x4000>, + <0x0 0x0ac4e000 0x0 0x4000>, + <0x0 0x0ac6b000 0x0 0xa00>; + reg-names = "csid0", + "csid1", + "csid2", + "csid_lite", + "csiphy0", + "csiphy1", + "csiphy2", + "csiphy3", + "vfe0", + "vfe1", + "vfe2", + "vfe_lite", + "a5_csr", + "a5_qgic", + "a5_sierra", + "bps", + "camnoc", + "core_top_csr_tcsr", + "cpas_cdm", + "cpas_top", + "ipe", + "jpeg_dma", + "jpeg_enc", + "lrme"; + + clocks = <&gcc GCC_CAMERA_AXI_CLK>, + <&camcc CAMCC_SOC_AHB_CLK>, + <&camcc CAMCC_CAMNOC_AXI_CLK>, + <&camcc CAMCC_CORE_AHB_CLK>, + <&camcc CAMCC_CPAS_AHB_CLK>, + <&camcc CAMCC_CSIPHY0_CLK>, + <&camcc CAMCC_CSI0PHYTIMER_CLK>, + <&camcc CAMCC_CSIPHY1_CLK>, + <&camcc CAMCC_CSI1PHYTIMER_CLK>, + <&camcc CAMCC_CSIPHY2_CLK>, + <&camcc CAMCC_CSI2PHYTIMER_CLK>, + <&camcc CAMCC_CSIPHY3_CLK>, + <&camcc CAMCC_CSI3PHYTIMER_CLK>, + <&camcc CAMCC_IFE_0_AXI_CLK>, + <&camcc CAMCC_IFE_0_CLK>, + <&camcc CAMCC_IFE_0_CPHY_RX_CLK>, + <&camcc CAMCC_IFE_0_CSID_CLK>, + <&camcc CAMCC_IFE_1_AXI_CLK>, + <&camcc CAMCC_IFE_1_CLK>, + <&camcc CAMCC_IFE_1_CPHY_RX_CLK>, + <&camcc CAMCC_IFE_1_CSID_CLK>, + <&camcc CAMCC_IFE_2_AXI_CLK>, + <&camcc CAMCC_IFE_2_CLK>, + <&camcc CAMCC_IFE_2_CPHY_RX_CLK>, + <&camcc CAMCC_IFE_2_CSID_CLK>, + <&camcc CAMCC_IFE_LITE_CLK>, + <&camcc CAMCC_IFE_LITE_CPHY_RX_CLK>, + <&camcc CAMCC_IFE_LITE_CSID_CLK>, + <&camcc CAMCC_BPS_CLK>, + <&camcc CAMCC_BPS_AHB_CLK>, + <&camcc CAMCC_BPS_AREG_CLK>, + <&camcc CAMCC_BPS_AXI_CLK>, + <&camcc CAMCC_ICP_CLK>, + <&camcc CAMCC_IPE_0_CLK>, + <&camcc CAMCC_IPE_0_AHB_CLK>, + <&camcc CAMCC_IPE_0_AREG_CLK>, + <&camcc CAMCC_IPE_0_AXI_CLK>, + <&camcc CAMCC_JPEG_CLK>, + <&camcc CAMCC_LRME_CLK>; + clock-names = "cam_axi", + "soc_ahb", + "camnoc_axi", + "core_ahb", + "cpas_ahb", + "csiphy0", + "csiphy0_timer", + "csiphy1", + "csiphy1_timer", + "csiphy2", + "csiphy2_timer", + "csiphy3", + "csiphy3_timer", + "vfe0_axi", + "vfe0", + "vfe0_cphy_rx", + "vfe0_csid", + "vfe1_axi", + "vfe1", + "vfe1_cphy_rx", + "vfe1_csid", + "vfe2_axi", + "vfe2", + "vfe2_cphy_rx", + "vfe2_csid", + "vfe_lite", + "vfe_lite_cphy_rx", + "vfe_lite_csid", + "bps", + "bps_ahb", + "bps_areg", + "bps_axi", + "icp", + "ipe0", + "ipe0_ahb", + "ipe0_areg", + "ipe0_axi", + "jpeg", + "lrme"; + + interrupts = <GIC_SPI 464 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 466 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 717 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 473 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 477 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 478 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 479 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 461 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 465 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 467 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 718 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 472 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 463 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 459 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 469 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 475 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 474 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 476 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "csid0", + "csid1", + "csid2", + "csid_lite", + "csiphy0", + "csiphy1", + "csiphy2", + "csiphy3", + "vfe0", + "vfe1", + "vfe2", + "vfe_lite", + "a5", + "cpas", + "cpas_cdm", + "jpeg_dma", + "jpeg_enc", + "lrme"; + + interconnects = <&gem_noc MASTER_AMPSS_M0 QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_CAMERA_CFG QCOM_ICC_TAG_ACTIVE_ONLY>, + <&mmss_noc MASTER_CAMNOC_HF QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_EBI_CH0 QCOM_ICC_TAG_ALWAYS>, + <&mmss_noc MASTER_CAMNOC_SF QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_EBI_CH0 QCOM_ICC_TAG_ALWAYS>, + <&mmss_noc MASTER_CAMNOC_ICP QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_EBI_CH0 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "ahb", + "hf_mnoc", + "sf_mnoc", + "sf_icp_mnoc"; + + iommus = <&apps_smmu 0x820 0xc0>, + <&apps_smmu 0x840 0x0>, + <&apps_smmu 0x860 0xc0>, + <&apps_smmu 0x880 0x0>, + <&apps_smmu 0xc40 0x20>, + <&apps_smmu 0xc60 0x20>, + <&apps_smmu 0xc80 0x0>, + <&apps_smmu 0xca2 0x0>, + <&apps_smmu 0xcc0 0x20>, + <&apps_smmu 0xce0 0x20>, + <&apps_smmu 0xd00 0x20>, + <&apps_smmu 0xd20 0x20>, + <&apps_smmu 0xd40 0x20>, + <&apps_smmu 0xd60 0x20>; + + power-domains = <&camcc IFE_0_GDSC>, + <&camcc IFE_1_GDSC>, + <&camcc IFE_2_GDSC>, + <&camcc TITAN_TOP_GDSC>, + <&camcc BPS_GDSC>, + <&camcc IPE_0_GDSC>; + power-domain-names = "ife0", + "ife1", + "ife2", + "top", + "bps", + "ipe"; + + vdd-csiphy0-0p9-supply = <&vreg_l18a>; + vdd-csiphy0-1p25-supply = <&vreg_l22a>; + vdd-csiphy1-0p9-supply = <&vreg_l18a>; + vdd-csiphy1-1p25-supply = <&vreg_l22a>; + vdd-csiphy2-0p9-supply = <&vreg_l18a>; + vdd-csiphy2-1p25-supply = <&vreg_l22a>; + vdd-csiphy3-0p9-supply = <&vreg_l18a>; + vdd-csiphy3-1p25-supply = <&vreg_l22a>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + csiphy0_ep: endpoint { + data-lanes = <0 1 2 3>; + bus-type = <MEDIA_BUS_TYPE_CSI2_DPHY>; + remote-endpoint = <&sensor_ep>; + }; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/media/qcom,sm8250-venus.yaml b/Documentation/devicetree/bindings/media/qcom,sm8250-venus.yaml index da54493220c9..aca748e42aca 100644 --- a/Documentation/devicetree/bindings/media/qcom,sm8250-venus.yaml +++ b/Documentation/devicetree/bindings/media/qcom,sm8250-venus.yaml @@ -10,19 +10,25 @@ maintainers: - Stanimir Varbanov <stanimir.varbanov@linaro.org> description: | - The Venus IP is a video encode and decode accelerator present - on Qualcomm platforms + The Iris v2.xx IP is a video encode and decode accelerator present on + Qualcomm platforms allOf: - $ref: qcom,venus-common.yaml# properties: compatible: - const: qcom,sm8250-venus + oneOf: + - const: qcom,sm8250-venus + - items: + - enum: + - qcom,sc8280xp-iris + - qcom,sm8350-iris + - const: qcom,sm8250-venus power-domains: minItems: 2 - maxItems: 3 + maxItems: 4 power-domain-names: minItems: 2 @@ -30,6 +36,7 @@ properties: - const: venus - const: vcodec0 - const: mx + - const: mmcx clocks: maxItems: 3 @@ -114,8 +121,12 @@ examples: interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>; power-domains = <&videocc MVS0C_GDSC>, <&videocc MVS0_GDSC>, - <&rpmhpd RPMHPD_MX>; - power-domain-names = "venus", "vcodec0", "mx"; + <&rpmhpd RPMHPD_MX>, + <&rpmhpd RPMHPD_MMCX>; + power-domain-names = "venus", + "vcodec0", + "mx", + "mmcx"; clocks = <&gcc GCC_VIDEO_AXI0_CLK>, <&videocc VIDEO_CC_MVS0C_CLK>, diff --git a/Documentation/devicetree/bindings/media/qcom,sm8550-iris.yaml b/Documentation/devicetree/bindings/media/qcom,sm8550-iris.yaml index 9c4b760508b5..0400ca1bff05 100644 --- a/Documentation/devicetree/bindings/media/qcom,sm8550-iris.yaml +++ b/Documentation/devicetree/bindings/media/qcom,sm8550-iris.yaml @@ -26,6 +26,7 @@ properties: - qcom,qcs8300-iris - qcom,sm8550-iris - qcom,sm8650-iris + - qcom,x1p42100-iris reg: maxItems: 1 @@ -41,13 +42,16 @@ properties: - const: mmcx clocks: - maxItems: 3 + minItems: 3 + maxItems: 4 clock-names: + minItems: 3 items: - const: iface - const: core - const: vcodec0_core + - const: vcodec0_bse firmware-name: maxItems: 1 @@ -115,6 +119,23 @@ allOf: maxItems: 1 reset-names: maxItems: 1 + - if: + properties: + compatible: + enum: + - qcom,x1p42100-iris + then: + properties: + clocks: + minItems: 4 + clock-names: + minItems: 4 + else: + properties: + clocks: + maxItems: 3 + clock-names: + maxItems: 3 unevaluatedProperties: false diff --git a/Documentation/devicetree/bindings/media/qcom,venus-common.yaml b/Documentation/devicetree/bindings/media/qcom,venus-common.yaml index 3153d91f9d18..59a3fde846d2 100644 --- a/Documentation/devicetree/bindings/media/qcom,venus-common.yaml +++ b/Documentation/devicetree/bindings/media/qcom,venus-common.yaml @@ -47,21 +47,6 @@ properties: minItems: 1 maxItems: 4 - video-firmware: - type: object - additionalProperties: false - - description: | - Firmware subnode is needed when the platform does not - have TrustZone. - - properties: - iommus: - maxItems: 1 - - required: - - iommus - required: - reg - clocks diff --git a/Documentation/devicetree/bindings/media/renesas,fcp.yaml b/Documentation/devicetree/bindings/media/renesas,fcp.yaml index b5eff6fec8a9..5e11ae0ee456 100644 --- a/Documentation/devicetree/bindings/media/renesas,fcp.yaml +++ b/Documentation/devicetree/bindings/media/renesas,fcp.yaml @@ -30,6 +30,8 @@ properties: - renesas,r9a07g043u-fcpvd # RZ/G2UL - renesas,r9a07g044-fcpvd # RZ/G2{L,LC} - renesas,r9a07g054-fcpvd # RZ/V2L + - renesas,r9a08g046-fcpvd # RZ/G3L + - renesas,r9a09g047-fcpvd # RZ/G3E - renesas,r9a09g056-fcpvd # RZ/V2N - renesas,r9a09g057-fcpvd # RZ/V2H(P) - const: renesas,fcpv # Generic FCP for VSP fallback @@ -77,6 +79,8 @@ allOf: - renesas,r9a07g043u-fcpvd - renesas,r9a07g044-fcpvd - renesas,r9a07g054-fcpvd + - renesas,r9a08g046-fcpvd + - renesas,r9a09g047-fcpvd - renesas,r9a09g056-fcpvd - renesas,r9a09g057-fcpvd then: diff --git a/Documentation/devicetree/bindings/media/renesas,vsp1.yaml b/Documentation/devicetree/bindings/media/renesas,vsp1.yaml index 07a97dd87a5b..803358780f01 100644 --- a/Documentation/devicetree/bindings/media/renesas,vsp1.yaml +++ b/Documentation/devicetree/bindings/media/renesas,vsp1.yaml @@ -25,6 +25,8 @@ properties: - enum: - renesas,r9a07g043u-vsp2 # RZ/G2UL - renesas,r9a07g054-vsp2 # RZ/V2L + - renesas,r9a08g046-vsp2 # RZ/G3L + - renesas,r9a09g047-vsp2 # RZ/G3E - renesas,r9a09g056-vsp2 # RZ/V2N - renesas,r9a09g057-vsp2 # RZ/V2H(P) - const: renesas,r9a07g044-vsp2 # RZ/G2L fallback diff --git a/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml b/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml index 4ac4a3b6f406..8bfad0fca3b7 100644 --- a/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml +++ b/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml @@ -16,9 +16,15 @@ description: properties: compatible: - enum: - - fsl,imx93-mipi-csi2 - - rockchip,rk3568-mipi-csi2 + oneOf: + - enum: + - fsl,imx93-mipi-csi2 + - fsl,imx95-mipi-csi2 + - rockchip,rk3568-mipi-csi2 + - items: + - enum: + - rockchip,rk3588-mipi-csi2 + - const: rockchip,rk3568-mipi-csi2 reg: maxItems: 1 @@ -135,6 +141,21 @@ allOf: clock-names: minItems: 2 + - if: + properties: + compatible: + contains: + const: fsl,imx95-mipi-csi2 + then: + properties: + interrupts: + maxItems: 1 + interrupt-names: false + clocks: + maxItems: 1 + clock-names: + maxItems: 1 + examples: - | #include <dt-bindings/clock/rk3568-cru.h> diff --git a/Documentation/devicetree/bindings/media/rockchip,rk3568-vicap.yaml b/Documentation/devicetree/bindings/media/rockchip,rk3568-vicap.yaml index 18cd0a5a5318..080b64503b1b 100644 --- a/Documentation/devicetree/bindings/media/rockchip,rk3568-vicap.yaml +++ b/Documentation/devicetree/bindings/media/rockchip,rk3568-vicap.yaml @@ -15,9 +15,15 @@ description: the data from camera sensors, video decoders, or other companion ICs and transfers it into system main memory by AXI bus. + The Rockchip RK3588 Video Capture (VICAP) is similar to its RK3568 + counterpart, but features six MIPI CSI-2 ports and additional connections + to the image signal processor (ISP) blocks. + properties: compatible: - const: rockchip,rk3568-vicap + enum: + - rockchip,rk3568-vicap + - rockchip,rk3588-vicap reg: maxItems: 1 @@ -26,11 +32,8 @@ properties: maxItems: 1 clocks: - items: - - description: ACLK - - description: HCLK - - description: DCLK - - description: ICLK + minItems: 4 + maxItems: 5 clock-names: items: @@ -38,25 +41,19 @@ properties: - const: hclk - const: dclk - const: iclk + - const: iclk1 + minItems: 4 iommus: maxItems: 1 resets: - items: - - description: ARST - - description: HRST - - description: DRST - - description: PRST - - description: IRST + minItems: 5 + maxItems: 9 reset-names: - items: - - const: arst - - const: hrst - - const: drst - - const: prst - - const: irst + minItems: 5 + maxItems: 9 rockchip,grf: $ref: /schemas/types.yaml#/definitions/phandle @@ -67,8 +64,15 @@ properties: ports: $ref: /schemas/graph.yaml#/properties/ports + additionalProperties: false properties: + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + port@0: $ref: /schemas/graph.yaml#/$defs/port-base unevaluatedProperties: false @@ -100,13 +104,75 @@ properties: port@1: $ref: /schemas/graph.yaml#/properties/port - description: Port connected to the MIPI CSI-2 receiver output. + description: Port connected to the MIPI CSI-2 receiver 0 output. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + port@2: + $ref: /schemas/graph.yaml#/properties/port + description: Port connected to the MIPI CSI-2 receiver 1 output. properties: endpoint: $ref: video-interfaces.yaml# unevaluatedProperties: false + port@3: + $ref: /schemas/graph.yaml#/properties/port + description: Port connected to the MIPI CSI-2 receiver 2 output. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + port@4: + $ref: /schemas/graph.yaml#/properties/port + description: Port connected to the MIPI CSI-2 receiver 3 output. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + port@5: + $ref: /schemas/graph.yaml#/properties/port + description: Port connected to the MIPI CSI-2 receiver 4 output. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + port@6: + $ref: /schemas/graph.yaml#/properties/port + description: Port connected to the MIPI CSI-2 receiver 5 output. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + port@10: + $ref: /schemas/graph.yaml#/properties/port + description: Port connected to the ISP0 input. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + port@11: + $ref: /schemas/graph.yaml#/properties/port + description: Port connected to the ISP1 input. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false required: - compatible - reg @@ -114,6 +180,75 @@ required: - clocks - ports +allOf: + - if: + properties: + compatible: + contains: + const: rockchip,rk3568-vicap + then: + properties: + clocks: + maxItems: 4 + + clock-names: + maxItems: 4 + + resets: + maxItems: 5 + + reset-names: + items: + - const: arst + - const: hrst + - const: drst + - const: prst + - const: irst + + ports: + properties: + port@2: false + + port@3: false + + port@4: false + + port@5: false + + port@6: false + + port@10: false + + port@11: false + + - if: + properties: + compatible: + contains: + const: rockchip,rk3588-vicap + then: + properties: + clocks: + minItems: 5 + + clock-names: + minItems: 5 + + resets: + minItems: 9 + + reset-names: + items: + - const: arst + - const: hrst + - const: drst + - const: irst0 + - const: irst1 + - const: irst2 + - const: irst3 + - const: irst4 + - const: irst5 + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/media/rockchip-rga.yaml b/Documentation/devicetree/bindings/media/rockchip-rga.yaml index ac17cda65191..7bd92f733666 100644 --- a/Documentation/devicetree/bindings/media/rockchip-rga.yaml +++ b/Documentation/devicetree/bindings/media/rockchip-rga.yaml @@ -9,7 +9,11 @@ title: Rockchip 2D raster graphic acceleration controller (RGA) description: RGA is a standalone 2D raster graphic acceleration unit. It accelerates 2D graphics operations, such as point/line drawing, image scaling, rotation, - BitBLT, alpha blending and image blur/sharpness. + BitBLT, alpha blending and image blur/sharpness. There exist many versions + of this unit that differ in the supported inputs/output formats, + the attached IOMMU and the supported operations on the input. As some SoCs + include multiple RGA units with different versions, a more specific + compatible name to differentiate the concrete unit is used for them. maintainers: - Jacob Chen <jacob-chen@iotwrt.com> @@ -20,6 +24,7 @@ properties: oneOf: - const: rockchip,rk3288-rga - const: rockchip,rk3399-rga + - const: rockchip,rk3588-rga3 - items: - enum: - rockchip,rk3228-rga @@ -45,6 +50,9 @@ properties: power-domains: maxItems: 1 + iommus: + maxItems: 1 + resets: maxItems: 3 diff --git a/Documentation/devicetree/bindings/media/ti,j721e-csi2rx-shim.yaml b/Documentation/devicetree/bindings/media/ti,j721e-csi2rx-shim.yaml index b9f033f2f3ce..bf62998b0445 100644 --- a/Documentation/devicetree/bindings/media/ti,j721e-csi2rx-shim.yaml +++ b/Documentation/devicetree/bindings/media/ti,j721e-csi2rx-shim.yaml @@ -20,11 +20,44 @@ properties: const: ti,j721e-csi2rx-shim dmas: - maxItems: 1 + minItems: 1 + maxItems: 32 dma-names: + minItems: 1 items: - const: rx0 + - const: rx1 + - const: rx2 + - const: rx3 + - const: rx4 + - const: rx5 + - const: rx6 + - const: rx7 + - const: rx8 + - const: rx9 + - const: rx10 + - const: rx11 + - const: rx12 + - const: rx13 + - const: rx14 + - const: rx15 + - const: rx16 + - const: rx17 + - const: rx18 + - const: rx19 + - const: rx20 + - const: rx21 + - const: rx22 + - const: rx23 + - const: rx24 + - const: rx25 + - const: rx26 + - const: rx27 + - const: rx28 + - const: rx29 + - const: rx30 + - const: rx31 reg: maxItems: 1 @@ -62,8 +95,8 @@ examples: ti_csi2rx0: ticsi2rx@4500000 { compatible = "ti,j721e-csi2rx-shim"; - dmas = <&main_udmap 0x4940>; - dma-names = "rx0"; + dmas = <&main_udmap 0x4940>, <&main_udmap 0x4941>; + dma-names = "rx0", "rx1"; reg = <0x4500000 0x1000>; power-domains = <&k3_pds 26 TI_SCI_PD_EXCLUSIVE>; #address-cells = <1>; diff --git a/Documentation/driver-api/media/camera-sensor.rst b/Documentation/driver-api/media/camera-sensor.rst index 94bd1dae82d5..c8552f70f496 100644 --- a/Documentation/driver-api/media/camera-sensor.rst +++ b/Documentation/driver-api/media/camera-sensor.rst @@ -114,7 +114,7 @@ of the device. This is because the power state of the device is only changed after the power state transition has taken place. The ``s_ctrl`` callback can be used to obtain device's power state after the power state transition: -.. c:function:: int pm_runtime_get_if_in_use(struct device *dev); +.. c:function:: int pm_runtime_get_if_active(struct device *dev); The function returns a non-zero value if it succeeded getting the power count or runtime PM was disabled, in either of which cases the driver may proceed to diff --git a/Documentation/driver-api/media/tx-rx.rst b/Documentation/driver-api/media/tx-rx.rst index 22e1b13ecde9..7df2407817b3 100644 --- a/Documentation/driver-api/media/tx-rx.rst +++ b/Documentation/driver-api/media/tx-rx.rst @@ -93,7 +93,8 @@ where * - variable or constant - description * - link_freq - - The value of the ``V4L2_CID_LINK_FREQ`` integer64 menu item. + - The value of the :ref:`V4L2_CID_LINK_FREQ <v4l2-cid-link-freq>` integer64 + menu item. * - nr_of_lanes - Number of data lanes used on the CSI-2 link. * - 2 diff --git a/Documentation/userspace-api/media/cec/cec.h.rst.exceptions b/Documentation/userspace-api/media/cec/cec.h.rst.exceptions index 65e8be062bdb..5d6f7747c023 100644 --- a/Documentation/userspace-api/media/cec/cec.h.rst.exceptions +++ b/Documentation/userspace-api/media/cec/cec.h.rst.exceptions @@ -524,6 +524,29 @@ ignore define CEC_OP_AUD_OUT_COMPENSATED_DELAY ignore define CEC_OP_AUD_OUT_COMPENSATED_NO_DELAY ignore define CEC_OP_AUD_OUT_COMPENSATED_PARTIAL_DELAY +ignore define CEC_MSG_REQUEST_LIP_SUPPORT +ignore define CEC_MSG_REPORT_LIP_SUPPORT +ignore define CEC_MSG_REQUEST_AUDIO_AND_VIDEO_LATENCY + +ignore define CEC_OP_HDR_FORMAT_GAMMA_SDR +ignore define CEC_OP_HDR_FORMAT_GAMMA_HDR +ignore define CEC_OP_HDR_FORMAT_PQ +ignore define CEC_OP_HDR_FORMAT_HLG +ignore define CEC_OP_HDR_FORMAT_DYNAMIC_HDR_TYPE_1 +ignore define CEC_OP_HDR_FORMAT_DYNAMIC_HDR_TYPE_2 +ignore define CEC_OP_HDR_FORMAT_DYNAMIC_HDR_TYPE_4 +ignore define CEC_OP_HDR_FORMAT_DV_SINK_LED +ignore define CEC_OP_HDR_FORMAT_DV_SOURCE_LED +ignore define CEC_OP_HDR_FORMAT_HDR10PLUS +ignore define CEC_OP_HDR_FORMAT_ETSI_TS_103_433 + +ignore define CEC_MSG_REPORT_AUDIO_AND_VIDEO_LATENCY +ignore define CEC_MSG_REQUEST_AUDIO_LATENCY +ignore define CEC_MSG_REPORT_AUDIO_LATENCY +ignore define CEC_MSG_REQUEST_VIDEO_LATENCY +ignore define CEC_MSG_REPORT_VIDEO_LATENCY +ignore define CEC_MSG_UPDATE_SQID + ignore define CEC_MSG_CDC_MESSAGE ignore define CEC_MSG_CDC_HEC_INQUIRE_STATE diff --git a/Documentation/userspace-api/media/drivers/uvcvideo.rst b/Documentation/userspace-api/media/drivers/uvcvideo.rst index dbb30ad389ae..b09d2f8ba66e 100644 --- a/Documentation/userspace-api/media/drivers/uvcvideo.rst +++ b/Documentation/userspace-api/media/drivers/uvcvideo.rst @@ -109,6 +109,8 @@ IOCTL reference UVCIOC_CTRL_MAP - Map a UVC control to a V4L2 control ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +**This IOCTL is deprecated and will be eventually removed** + Argument: struct uvc_xu_control_mapping **Description**: diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-codec-stateless.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-codec-stateless.rst index 3b1e05c6eb13..d7a3b8ef03a7 100644 --- a/Documentation/userspace-api/media/v4l/ext-ctrls-codec-stateless.rst +++ b/Documentation/userspace-api/media/v4l/ext-ctrls-codec-stateless.rst @@ -1877,7 +1877,7 @@ params syntax' of the :ref:`vp9` specification for more details. :stub-columns: 0 :widths: 1 1 2 - * - __u8 + * - __s16 - ``feature_data[8][4]`` - Data attached to each feature. Data entry is only valid if the feature is enabled. The array shall be indexed with segment number as the first dimension diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst index c8890cb5e00a..ab865a1a6ba9 100644 --- a/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst +++ b/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst @@ -737,6 +737,12 @@ enum v4l2_mpeg_video_frame_skip_mode - Enable writing sample aspect ratio in the Video Usability Information. Applicable to the H264 encoder. +``V4L2_CID_MPEG_VIDEO_BACKGROUND_DETECTION (boolean)`` + If enabled, the encoder detect a background region in frame and + use low bits or skip mode to encode the background region. + If a lot of scenes are stationary or background, It may help to + reduce the video bitrate. Applicable to the encoder. + .. _v4l2-mpeg-video-h264-vui-sar-idc: ``V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC`` diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-image-process.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-image-process.rst index 6d516f041ca2..57c2baf34acf 100644 --- a/Documentation/userspace-api/media/v4l/ext-ctrls-image-process.rst +++ b/Documentation/userspace-api/media/v4l/ext-ctrls-image-process.rst @@ -24,7 +24,10 @@ Image Process Control IDs .. _v4l2-cid-link-freq: ``V4L2_CID_LINK_FREQ (integer menu)`` - The frequency of the data bus (e.g. parallel or CSI-2). + The fundamental frequency of the operating symbol rate (serial interfaces + such as CSI-2) or the sampling rate (parallel interfaces such as DVP or + Bt.565) of the data interface. For CSI-2, the frequency is equal to + _1 / (2 * UI)_. .. _v4l2-cid-pixel-rate: diff --git a/MAINTAINERS b/MAINTAINERS index 0ef0a11a5f25..16ce0c0537e8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1167,6 +1167,31 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/iommu/linux.git F: drivers/iommu/amd/ F: include/linux/amd-iommu.h +AMD ISP4 DRIVER +M: Bin Du <bin.du@amd.com> +M: Nirujogi Pratap <pratap.nirujogi@amd.com> +L: linux-media@vger.kernel.org +S: Maintained +T: git git://linuxtv.org/media.git +F: Documentation/admin-guide/media/amdisp4-1.rst +F: Documentation/admin-guide/media/amdisp4.dot +F: drivers/media/platform/amd/Kconfig +F: drivers/media/platform/amd/Makefile +F: drivers/media/platform/amd/isp4/Kconfig +F: drivers/media/platform/amd/isp4/Makefile +F: drivers/media/platform/amd/isp4/isp4.c +F: drivers/media/platform/amd/isp4/isp4.h +F: drivers/media/platform/amd/isp4/isp4_debug.c +F: drivers/media/platform/amd/isp4/isp4_debug.h +F: drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h +F: drivers/media/platform/amd/isp4/isp4_hw_reg.h +F: drivers/media/platform/amd/isp4/isp4_interface.c +F: drivers/media/platform/amd/isp4/isp4_interface.h +F: drivers/media/platform/amd/isp4/isp4_subdev.c +F: drivers/media/platform/amd/isp4/isp4_subdev.h +F: drivers/media/platform/amd/isp4/isp4_video.c +F: drivers/media/platform/amd/isp4/isp4_video.h + AMD KFD M: Felix Kuehling <Felix.Kuehling@amd.com> L: amd-gfx@lists.freedesktop.org @@ -4339,6 +4364,12 @@ S: Maintained F: Documentation/devicetree/bindings/iio/adc/avia-hx711.yaml F: drivers/iio/adc/hx711.c +AVMATRIX HWS CAPTURE DRIVER +M: Ben Hoff <hoff.benjamin.k@gmail.com> +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/media/pci/hws/ + AWINIC AW99706 WLED BACKLIGHT DRIVER M: Junjie Cao <caojunjie650@gmail.com> S: Maintained @@ -12876,6 +12907,12 @@ S: Orphan T: git git://git.code.sf.net/p/intel-sas/isci F: drivers/scsi/isci/ +INTEL COMPUTER VISION SENSING (CVS) DRIVER +M: Miguel Vadillo <miguel.vadillo@intel.com> +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/media/i2c/cvs/ + INTEL CPU family model numbers M: Tony Luck <tony.luck@intel.com> M: x86@kernel.org @@ -17487,8 +17524,6 @@ F: Documentation/devicetree/bindings/media/atmel,isc.yaml F: Documentation/devicetree/bindings/media/microchip,xisc.yaml F: drivers/media/platform/microchip/microchip-isc* F: drivers/media/platform/microchip/microchip-sama*-isc* -F: drivers/staging/media/deprecated/atmel/atmel-isc* -F: drivers/staging/media/deprecated/atmel/atmel-sama*-isc* F: include/linux/atmel-isc-media.h MICROCHIP ISI DRIVER @@ -22001,10 +22036,9 @@ F: drivers/bluetooth/btqcomsmd.c F: drivers/bluetooth/hci_qca.c QUALCOMM CAMERA SUBSYSTEM DRIVER -M: Robert Foss <rfoss@kernel.org> -M: Todor Tomov <todor.too@gmail.com> M: Bryan O'Donoghue <bryan.odonoghue@linaro.org> R: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org> +R: Loic Poulain <loic.poulain@oss.qualcomm.com> L: linux-media@vger.kernel.org S: Maintained F: Documentation/admin-guide/media/qcom_camss.rst @@ -22850,6 +22884,15 @@ S: Supported F: Documentation/devicetree/bindings/timer/renesas,rz-mtu3.yaml F: drivers/counter/rz-mtu3-cnt.c +RENESAS RZ/G2L / RZ/V2H(P) CRU +M: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com> +M: Jacopo Mondi <jacopo.mondi@ideasonboard.com> +L: linux-renesas-soc@vger.kernel.org +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/media/renesas,rzg2l-cru.yaml +F: drivers/media/platform/renesas/rzg2l-cru/ + RENESAS RZ/T2H / RZ/N2H A/D DRIVER M: Cosmin Tanislav <cosmin-gabriel.tanislav.xa@renesas.com> L: linux-iio@vger.kernel.org @@ -22918,6 +22961,7 @@ F: drivers/net/ethernet/stmicro/stmmac/dwmac-renesas-gbeth.c RENESAS RZ/V2H(P) INPUT VIDEO CONTROL BLOCK DRIVER M: Daniel Scally <dan.scally@ideasonboard.com> +M: Jacopo Mondi <jacopo.mondi@ideasonboard.com> L: linux-media@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/media/renesas,r9a09g057-ivc.yaml diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c b/drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c index 6b413b379a86..ae9f381b03c8 100644 --- a/drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c @@ -87,12 +87,12 @@ void rcar_du_vsp_enable(struct rcar_du_crtc *crtc) __rcar_du_plane_setup(crtc->group, &state); - vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, &cfg); + vsp1_du_enable(crtc->vsp->vsp, crtc->vsp_pipe, &cfg); } void rcar_du_vsp_disable(struct rcar_du_crtc *crtc) { - vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, NULL); + vsp1_du_disable(crtc->vsp->vsp, crtc->vsp_pipe); } void rcar_du_vsp_atomic_begin(struct rcar_du_crtc *crtc) diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.c index bc205c25cd21..1efa0f0451fe 100644 --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.c +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.c @@ -56,12 +56,12 @@ void rzg2l_du_vsp_enable(struct rzg2l_du_crtc *crtc) .callback_data = crtc, }; - vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, &cfg); + vsp1_du_enable(crtc->vsp->vsp, crtc->vsp_pipe, &cfg); } void rzg2l_du_vsp_disable(struct rzg2l_du_crtc *crtc) { - vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, NULL); + vsp1_du_disable(crtc->vsp->vsp, crtc->vsp_pipe); } void rzg2l_du_vsp_atomic_flush(struct rzg2l_du_crtc *crtc) diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c index 8f7244ac1d43..a90cb84a4b4d 100644 --- a/drivers/media/cec/core/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -1098,6 +1098,15 @@ static const u8 cec_msg_size[256] = { [CEC_MSG_REQUEST_CURRENT_LATENCY] = 4 | BCAST, [CEC_MSG_REPORT_CURRENT_LATENCY] = 6 | BCAST, [CEC_MSG_CDC_MESSAGE] = 2 | BCAST, + [CEC_MSG_REQUEST_LIP_SUPPORT] = 4 | DIRECTED, + [CEC_MSG_REPORT_LIP_SUPPORT] = 6 | DIRECTED, + [CEC_MSG_REQUEST_AUDIO_AND_VIDEO_LATENCY] = 6 | DIRECTED, + [CEC_MSG_REPORT_AUDIO_AND_VIDEO_LATENCY] = 6 | DIRECTED, + [CEC_MSG_REQUEST_AUDIO_LATENCY] = 3 | DIRECTED, + [CEC_MSG_REPORT_AUDIO_LATENCY] = 4 | DIRECTED, + [CEC_MSG_REQUEST_VIDEO_LATENCY] = 5 | DIRECTED, + [CEC_MSG_REPORT_VIDEO_LATENCY] = 4 | DIRECTED, + [CEC_MSG_UPDATE_SQID] = 6 | DIRECTED, }; /* Called by the CEC adapter if a message is received */ diff --git a/drivers/media/cec/i2c/tda9950.c b/drivers/media/cec/i2c/tda9950.c index cbff851e0c85..2bece4e63687 100644 --- a/drivers/media/cec/i2c/tda9950.c +++ b/drivers/media/cec/i2c/tda9950.c @@ -486,7 +486,7 @@ static void tda9950_remove(struct i2c_client *client) } static struct i2c_device_id tda9950_ids[] = { - { "tda9950" }, + { .name = "tda9950" }, { } }; MODULE_DEVICE_TABLE(i2c, tda9950_ids); diff --git a/drivers/media/cec/platform/cros-ec/cros-ec-cec.c b/drivers/media/cec/platform/cros-ec/cros-ec-cec.c index 419b9a7abcce..ec1da9cd3674 100644 --- a/drivers/media/cec/platform/cros-ec/cros-ec-cec.c +++ b/drivers/media/cec/platform/cros-ec/cros-ec-cec.c @@ -334,6 +334,12 @@ static const struct cec_dmi_match cec_dmi_match_table[] = { { "Google", "Dirks", "0000:00:02.0", port_ab_conns }, /* Google Moxie */ { "Google", "Moxie", "0000:00:02.0", port_b_conns }, + /* Google Kulnex */ + { "Google", "Kulnex", "0000:00:02.0", port_b_conns }, + /* Google Moxoe */ + { "Google", "Moxoe", "0000:00:02.0", port_b_conns }, + /* Google Dirkson */ + { "Google", "Dirkson", "0000:00:02.0", port_ab_conns }, }; static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev, diff --git a/drivers/media/cec/platform/seco/seco-cec.c b/drivers/media/cec/platform/seco/seco-cec.c index b7bb49f02395..97ed9654c78a 100644 --- a/drivers/media/cec/platform/seco/seco-cec.c +++ b/drivers/media/cec/platform/seco/seco-cec.c @@ -649,7 +649,7 @@ static int secocec_probe(struct platform_device *pdev) ret = secocec_ir_probe(secocec); if (ret) - goto err_notifier; + goto err_unregister_adapter; platform_set_drvdata(pdev, secocec); @@ -657,6 +657,10 @@ static int secocec_probe(struct platform_device *pdev) return ret; +err_unregister_adapter: + cec_notifier_cec_adap_unregister(secocec->notifier, secocec->cec_adap); + cec_unregister_adapter(secocec->cec_adap); + goto err; err_notifier: cec_notifier_cec_adap_unregister(secocec->notifier, secocec->cec_adap); err_delete_adapter: diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index adf668b213c2..b0a6084f1757 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -2990,8 +2990,8 @@ static int __vb2_cleanup_fileio(struct vb2_queue *q) * @nonblock: mode selector (1 means blocking calls, 0 means nonblocking) * @read: access mode selector (1 means read, 0 means write) */ -static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_t count, - loff_t *ppos, int nonblock, int read) +static ssize_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_t count, + loff_t *ppos, int nonblock, int read) { struct vb2_fileio_data *fileio; struct vb2_fileio_buf *buf; @@ -3154,15 +3154,15 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ return ret; } -size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count, - loff_t *ppos, int nonblocking) +ssize_t vb2_read(struct vb2_queue *q, char __user *data, size_t count, + loff_t *ppos, int nonblocking) { return __vb2_perform_fileio(q, data, count, ppos, nonblocking, 1); } EXPORT_SYMBOL_GPL(vb2_read); -size_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count, - loff_t *ppos, int nonblocking) +ssize_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count, + loff_t *ppos, int nonblocking) { return __vb2_perform_fileio(q, (char __user *) data, count, ppos, nonblocking, 0); diff --git a/drivers/media/dvb-frontends/a8293.c b/drivers/media/dvb-frontends/a8293.c index 7c0963054a8f..52e3dc928327 100644 --- a/drivers/media/dvb-frontends/a8293.c +++ b/drivers/media/dvb-frontends/a8293.c @@ -256,8 +256,8 @@ static void a8293_remove(struct i2c_client *client) } static const struct i2c_device_id a8293_id_table[] = { - { "a8293" }, - {} + { .name = "a8293" }, + { } }; MODULE_DEVICE_TABLE(i2c, a8293_id_table); diff --git a/drivers/media/dvb-frontends/af9013.c b/drivers/media/dvb-frontends/af9013.c index 75f3063c193e..d335bfd18e44 100644 --- a/drivers/media/dvb-frontends/af9013.c +++ b/drivers/media/dvb-frontends/af9013.c @@ -1553,8 +1553,8 @@ static void af9013_remove(struct i2c_client *client) } static const struct i2c_device_id af9013_id_table[] = { - { "af9013" }, - {} + { .name = "af9013" }, + { } }; MODULE_DEVICE_TABLE(i2c, af9013_id_table); diff --git a/drivers/media/dvb-frontends/af9033.c b/drivers/media/dvb-frontends/af9033.c index 9a3a4d6513dd..01761861b01f 100644 --- a/drivers/media/dvb-frontends/af9033.c +++ b/drivers/media/dvb-frontends/af9033.c @@ -1173,8 +1173,8 @@ static void af9033_remove(struct i2c_client *client) } static const struct i2c_device_id af9033_id_table[] = { - { "af9033" }, - {} + { .name = "af9033" }, + { } }; MODULE_DEVICE_TABLE(i2c, af9033_id_table); diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c index 58b959b272c6..9ae47ea3976b 100644 --- a/drivers/media/dvb-frontends/au8522_decoder.c +++ b/drivers/media/dvb-frontends/au8522_decoder.c @@ -768,8 +768,8 @@ static void au8522_remove(struct i2c_client *client) } static const struct i2c_device_id au8522_id[] = { - { "au8522" }, - {} + { .name = "au8522" }, + { } }; MODULE_DEVICE_TABLE(i2c, au8522_id); diff --git a/drivers/media/dvb-frontends/cxd2099.c b/drivers/media/dvb-frontends/cxd2099.c index f95950a61307..e6110af1edcd 100644 --- a/drivers/media/dvb-frontends/cxd2099.c +++ b/drivers/media/dvb-frontends/cxd2099.c @@ -672,8 +672,8 @@ static void cxd2099_remove(struct i2c_client *client) } static const struct i2c_device_id cxd2099_id[] = { - { "cxd2099" }, - {} + { .name = "cxd2099" }, + { } }; MODULE_DEVICE_TABLE(i2c, cxd2099_id); diff --git a/drivers/media/dvb-frontends/cxd2820r_core.c b/drivers/media/dvb-frontends/cxd2820r_core.c index 3deefeff4bd1..fbbffe9e7251 100644 --- a/drivers/media/dvb-frontends/cxd2820r_core.c +++ b/drivers/media/dvb-frontends/cxd2820r_core.c @@ -723,8 +723,8 @@ static void cxd2820r_remove(struct i2c_client *client) } static const struct i2c_device_id cxd2820r_id_table[] = { - { "cxd2820r" }, - {} + { .name = "cxd2820r" }, + { } }; MODULE_DEVICE_TABLE(i2c, cxd2820r_id_table); diff --git a/drivers/media/dvb-frontends/dvb-pll.c b/drivers/media/dvb-frontends/dvb-pll.c index f8fd23f60e3f..d36c163b772f 100644 --- a/drivers/media/dvb-frontends/dvb-pll.c +++ b/drivers/media/dvb-frontends/dvb-pll.c @@ -911,28 +911,28 @@ static void dvb_pll_remove(struct i2c_client *client) static const struct i2c_device_id dvb_pll_id[] = { - {"dtt7579", DVB_PLL_THOMSON_DTT7579}, - {"dtt759x", DVB_PLL_THOMSON_DTT759X}, - {"z201", DVB_PLL_LG_Z201}, - {"unknown_1", DVB_PLL_UNKNOWN_1}, - {"tua6010xs", DVB_PLL_TUA6010XS}, - {"env57h1xd5", DVB_PLL_ENV57H1XD5}, - {"tua6034", DVB_PLL_TUA6034}, - {"tda665x", DVB_PLL_TDA665X}, - {"tded4", DVB_PLL_TDED4}, - {"tdhu2", DVB_PLL_TDHU2}, - {"tbmv", DVB_PLL_SAMSUNG_TBMV}, - {"sd1878_tda8261", DVB_PLL_PHILIPS_SD1878_TDA8261}, - {"opera1", DVB_PLL_OPERA1}, - {"dtos403ih102a", DVB_PLL_SAMSUNG_DTOS403IH102A}, - {"tdtc9251dh0", DVB_PLL_SAMSUNG_TDTC9251DH0}, - {"tbdu18132", DVB_PLL_SAMSUNG_TBDU18132}, - {"tbmu24112", DVB_PLL_SAMSUNG_TBMU24112}, - {"tdee4", DVB_PLL_TDEE4}, - {"dtt7520x", DVB_PLL_THOMSON_DTT7520X}, - {"tua6034_friio", DVB_PLL_TUA6034_FRIIO}, - {"tda665x_earthpt1", DVB_PLL_TDA665X_EARTH_PT1}, - {} + { .name = "dtt7579", .driver_data = DVB_PLL_THOMSON_DTT7579 }, + { .name = "dtt759x", .driver_data = DVB_PLL_THOMSON_DTT759X }, + { .name = "z201", .driver_data = DVB_PLL_LG_Z201 }, + { .name = "unknown_1", .driver_data = DVB_PLL_UNKNOWN_1 }, + { .name = "tua6010xs", .driver_data = DVB_PLL_TUA6010XS }, + { .name = "env57h1xd5", .driver_data = DVB_PLL_ENV57H1XD5 }, + { .name = "tua6034", .driver_data = DVB_PLL_TUA6034 }, + { .name = "tda665x", .driver_data = DVB_PLL_TDA665X }, + { .name = "tded4", .driver_data = DVB_PLL_TDED4 }, + { .name = "tdhu2", .driver_data = DVB_PLL_TDHU2 }, + { .name = "tbmv", .driver_data = DVB_PLL_SAMSUNG_TBMV }, + { .name = "sd1878_tda8261", .driver_data = DVB_PLL_PHILIPS_SD1878_TDA8261 }, + { .name = "opera1", .driver_data = DVB_PLL_OPERA1 }, + { .name = "dtos403ih102a", .driver_data = DVB_PLL_SAMSUNG_DTOS403IH102A }, + { .name = "tdtc9251dh0", .driver_data = DVB_PLL_SAMSUNG_TDTC9251DH0 }, + { .name = "tbdu18132", .driver_data = DVB_PLL_SAMSUNG_TBDU18132 }, + { .name = "tbmu24112", .driver_data = DVB_PLL_SAMSUNG_TBMU24112 }, + { .name = "tdee4", .driver_data = DVB_PLL_TDEE4 }, + { .name = "dtt7520x", .driver_data = DVB_PLL_THOMSON_DTT7520X }, + { .name = "tua6034_friio", .driver_data = DVB_PLL_TUA6034_FRIIO }, + { .name = "tda665x_earthpt1", .driver_data = DVB_PLL_TDA665X_EARTH_PT1 }, + { } }; diff --git a/drivers/media/dvb-frontends/helene.c b/drivers/media/dvb-frontends/helene.c index 1402d124544e..993280fefc2c 100644 --- a/drivers/media/dvb-frontends/helene.c +++ b/drivers/media/dvb-frontends/helene.c @@ -1101,8 +1101,8 @@ static int helene_probe(struct i2c_client *client) } static const struct i2c_device_id helene_id[] = { - { "helene", }, - {} + { .name = "helene" }, + { } }; MODULE_DEVICE_TABLE(i2c, helene_id); diff --git a/drivers/media/dvb-frontends/lgdt3306a.c b/drivers/media/dvb-frontends/lgdt3306a.c index b6a66e122ed5..f4a3136baea3 100644 --- a/drivers/media/dvb-frontends/lgdt3306a.c +++ b/drivers/media/dvb-frontends/lgdt3306a.c @@ -2244,8 +2244,8 @@ static void lgdt3306a_remove(struct i2c_client *client) } static const struct i2c_device_id lgdt3306a_id_table[] = { - { "lgdt3306a" }, - {} + { .name = "lgdt3306a" }, + { } }; MODULE_DEVICE_TABLE(i2c, lgdt3306a_id_table); diff --git a/drivers/media/dvb-frontends/lgdt330x.c b/drivers/media/dvb-frontends/lgdt330x.c index 19a4a05b3499..d90f642e9237 100644 --- a/drivers/media/dvb-frontends/lgdt330x.c +++ b/drivers/media/dvb-frontends/lgdt330x.c @@ -983,8 +983,8 @@ static void lgdt330x_remove(struct i2c_client *client) } static const struct i2c_device_id lgdt330x_id_table[] = { - { "lgdt330x" }, - {} + { .name = "lgdt330x" }, + { } }; MODULE_DEVICE_TABLE(i2c, lgdt330x_id_table); diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c index 44bee1f3c5e9..d79b88848a8c 100644 --- a/drivers/media/dvb-frontends/m88ds3103.c +++ b/drivers/media/dvb-frontends/m88ds3103.c @@ -2221,11 +2221,11 @@ static void m88ds3103_remove(struct i2c_client *client) } static const struct i2c_device_id m88ds3103_id_table[] = { - {"m88ds3103", M88DS3103_CHIPTYPE_3103}, - {"m88rs6000", M88DS3103_CHIPTYPE_RS6000}, - {"m88ds3103b", M88DS3103_CHIPTYPE_3103B}, - {"m88ds3103c", M88DS3103_CHIPTYPE_3103C}, - {} + { .name = "m88ds3103", .driver_data = M88DS3103_CHIPTYPE_3103 }, + { .name = "m88rs6000", .driver_data = M88DS3103_CHIPTYPE_RS6000 }, + { .name = "m88ds3103b", .driver_data = M88DS3103_CHIPTYPE_3103B }, + { .name = "m88ds3103c", .driver_data = M88DS3103_CHIPTYPE_3103C }, + { } }; MODULE_DEVICE_TABLE(i2c, m88ds3103_id_table); diff --git a/drivers/media/dvb-frontends/mn88443x.c b/drivers/media/dvb-frontends/mn88443x.c index 818c4e67364c..cc6bd17daf73 100644 --- a/drivers/media/dvb-frontends/mn88443x.c +++ b/drivers/media/dvb-frontends/mn88443x.c @@ -787,10 +787,10 @@ static const struct of_device_id mn88443x_of_match[] = { 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 }, - {} + { .name = "mn884433", .driver_data = (kernel_ulong_t)&mn88443x_spec_pri }, + { .name = "mn884434-0", .driver_data = (kernel_ulong_t)&mn88443x_spec_pri }, + { .name = "mn884434-1", .driver_data = (kernel_ulong_t)&mn88443x_spec_sec }, + { } }; MODULE_DEVICE_TABLE(i2c, mn88443x_i2c_id); diff --git a/drivers/media/dvb-frontends/mn88472.c b/drivers/media/dvb-frontends/mn88472.c index 275c404ce286..42b78b9aba20 100644 --- a/drivers/media/dvb-frontends/mn88472.c +++ b/drivers/media/dvb-frontends/mn88472.c @@ -708,8 +708,8 @@ static void mn88472_remove(struct i2c_client *client) } static const struct i2c_device_id mn88472_id_table[] = { - { "mn88472" }, - {} + { .name = "mn88472" }, + { } }; MODULE_DEVICE_TABLE(i2c, mn88472_id_table); diff --git a/drivers/media/dvb-frontends/mn88473.c b/drivers/media/dvb-frontends/mn88473.c index 40a0cb1d9c67..b7b11a000969 100644 --- a/drivers/media/dvb-frontends/mn88473.c +++ b/drivers/media/dvb-frontends/mn88473.c @@ -743,8 +743,8 @@ static void mn88473_remove(struct i2c_client *client) } static const struct i2c_device_id mn88473_id_table[] = { - { "mn88473" }, - {} + { .name = "mn88473" }, + { } }; MODULE_DEVICE_TABLE(i2c, mn88473_id_table); diff --git a/drivers/media/dvb-frontends/mxl692.c b/drivers/media/dvb-frontends/mxl692.c index 2d8eaa20723f..bca4a4c3dd66 100644 --- a/drivers/media/dvb-frontends/mxl692.c +++ b/drivers/media/dvb-frontends/mxl692.c @@ -1346,8 +1346,8 @@ static void mxl692_remove(struct i2c_client *client) } static const struct i2c_device_id mxl692_id_table[] = { - { "mxl692" }, - {} + { .name = "mxl692" }, + { } }; MODULE_DEVICE_TABLE(i2c, mxl692_id_table); diff --git a/drivers/media/dvb-frontends/rtl2830.c b/drivers/media/dvb-frontends/rtl2830.c index f0ee7c38ee79..4c44e2d695f8 100644 --- a/drivers/media/dvb-frontends/rtl2830.c +++ b/drivers/media/dvb-frontends/rtl2830.c @@ -876,8 +876,8 @@ static void rtl2830_remove(struct i2c_client *client) } static const struct i2c_device_id rtl2830_id_table[] = { - { "rtl2830" }, - {} + { .name = "rtl2830" }, + { } }; MODULE_DEVICE_TABLE(i2c, rtl2830_id_table); diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index d8e1546aea5e..b31cdb44b383 100644 --- a/drivers/media/dvb-frontends/rtl2832.c +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -1115,18 +1115,18 @@ static void rtl2832_remove(struct i2c_client *client) dev_dbg(&client->dev, "\n"); - cancel_delayed_work_sync(&dev->i2c_gate_work); - i2c_mux_del_adapters(dev->muxc); + cancel_delayed_work_sync(&dev->i2c_gate_work); + regmap_exit(dev->regmap); kfree(dev); } static const struct i2c_device_id rtl2832_id_table[] = { - { "rtl2832" }, - {} + { .name = "rtl2832" }, + { } }; MODULE_DEVICE_TABLE(i2c, rtl2832_id_table); diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c index 422d1a7b5456..c564485e3bbb 100644 --- a/drivers/media/dvb-frontends/rtl2832_sdr.c +++ b/drivers/media/dvb-frontends/rtl2832_sdr.c @@ -399,7 +399,8 @@ static int rtl2832_sdr_alloc_urbs(struct rtl2832_sdr_dev *dev) } /* Must be called with vb_queue_lock hold */ -static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_dev *dev) +static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_dev *dev, + enum vb2_buffer_state state) { struct platform_device *pdev = dev->pdev; unsigned long flags; @@ -413,7 +414,7 @@ static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_dev *dev) buf = list_entry(dev->queued_bufs.next, struct rtl2832_sdr_frame_buf, list); list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, state); } spin_unlock_irqrestore(&dev->queued_bufs_lock, flags); } @@ -855,11 +856,15 @@ static int rtl2832_sdr_start_streaming(struct vb2_queue *vq, unsigned int count) dev_dbg(&pdev->dev, "\n"); - if (!dev->udev) + if (!dev->udev) { + rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED); return -ENODEV; + } - if (mutex_lock_interruptible(&dev->v4l2_lock)) + if (mutex_lock_interruptible(&dev->v4l2_lock)) { + rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED); return -ERESTARTSYS; + } if (d->props->power_ctrl) d->props->power_ctrl(d, 1); @@ -900,7 +905,11 @@ static int rtl2832_sdr_start_streaming(struct vb2_queue *vq, unsigned int count) if (ret) goto err; + mutex_unlock(&dev->v4l2_lock); + return 0; + err: + rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED); mutex_unlock(&dev->v4l2_lock); return ret; @@ -920,7 +929,7 @@ static void rtl2832_sdr_stop_streaming(struct vb2_queue *vq) rtl2832_sdr_kill_urbs(dev); rtl2832_sdr_free_urbs(dev); rtl2832_sdr_free_stream_bufs(dev); - rtl2832_sdr_cleanup_queued_bufs(dev); + rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_ERROR); rtl2832_sdr_unset_adc(dev); /* sleep tuner */ diff --git a/drivers/media/dvb-frontends/si2165.c b/drivers/media/dvb-frontends/si2165.c index 4170696af9f0..f1241b63aa5c 100644 --- a/drivers/media/dvb-frontends/si2165.c +++ b/drivers/media/dvb-frontends/si2165.c @@ -1281,8 +1281,8 @@ static void si2165_remove(struct i2c_client *client) } static const struct i2c_device_id si2165_id_table[] = { - { "si2165" }, - {} + { .name = "si2165" }, + { } }; MODULE_DEVICE_TABLE(i2c, si2165_id_table); diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index 9c5bac8cda47..8bc3b6eb1dd3 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -790,8 +790,8 @@ static void si2168_remove(struct i2c_client *client) } static const struct i2c_device_id si2168_id_table[] = { - { "si2168" }, - {} + { .name = "si2168" }, + { } }; MODULE_DEVICE_TABLE(i2c, si2168_id_table); diff --git a/drivers/media/dvb-frontends/sp2.c b/drivers/media/dvb-frontends/sp2.c index 071c7371c699..63dec4b4fd2f 100644 --- a/drivers/media/dvb-frontends/sp2.c +++ b/drivers/media/dvb-frontends/sp2.c @@ -407,8 +407,8 @@ static void sp2_remove(struct i2c_client *client) } static const struct i2c_device_id sp2_id[] = { - { "sp2" }, - {} + { .name = "sp2" }, + { } }; MODULE_DEVICE_TABLE(i2c, sp2_id); diff --git a/drivers/media/dvb-frontends/stv090x.c b/drivers/media/dvb-frontends/stv090x.c index 657df713865e..932bbed5497a 100644 --- a/drivers/media/dvb-frontends/stv090x.c +++ b/drivers/media/dvb-frontends/stv090x.c @@ -5079,8 +5079,8 @@ error: EXPORT_SYMBOL_GPL(stv090x_attach); static const struct i2c_device_id stv090x_id_table[] = { - { "stv090x" }, - {} + { .name = "stv090x" }, + { } }; MODULE_DEVICE_TABLE(i2c, stv090x_id_table); diff --git a/drivers/media/dvb-frontends/stv6110x.c b/drivers/media/dvb-frontends/stv6110x.c index 7506f39ead55..5075333d7ffc 100644 --- a/drivers/media/dvb-frontends/stv6110x.c +++ b/drivers/media/dvb-frontends/stv6110x.c @@ -470,8 +470,8 @@ const struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe, EXPORT_SYMBOL_GPL(stv6110x_attach); static const struct i2c_device_id stv6110x_id_table[] = { - { "stv6110x" }, - {} + { .name = "stv6110x" }, + { } }; MODULE_DEVICE_TABLE(i2c, stv6110x_id_table); diff --git a/drivers/media/dvb-frontends/tc90522.c b/drivers/media/dvb-frontends/tc90522.c index f1343ba67b97..9a5ffb5f70fc 100644 --- a/drivers/media/dvb-frontends/tc90522.c +++ b/drivers/media/dvb-frontends/tc90522.c @@ -830,9 +830,9 @@ static void tc90522_remove(struct i2c_client *client) static const struct i2c_device_id tc90522_id[] = { - { TC90522_I2C_DEV_SAT, 0 }, - { TC90522_I2C_DEV_TER, 1 }, - {} + { .name = TC90522_I2C_DEV_SAT, .driver_data = 0 }, + { .name = TC90522_I2C_DEV_TER, .driver_data = 1 }, + { } }; MODULE_DEVICE_TABLE(i2c, tc90522_id); diff --git a/drivers/media/dvb-frontends/tda10071.c b/drivers/media/dvb-frontends/tda10071.c index 6e6c2e5c427e..7a635c86b86c 100644 --- a/drivers/media/dvb-frontends/tda10071.c +++ b/drivers/media/dvb-frontends/tda10071.c @@ -1230,8 +1230,8 @@ static void tda10071_remove(struct i2c_client *client) } static const struct i2c_device_id tda10071_id_table[] = { - { "tda10071_cx24118" }, - {} + { .name = "tda10071_cx24118" }, + { } }; MODULE_DEVICE_TABLE(i2c, tda10071_id_table); diff --git a/drivers/media/dvb-frontends/ts2020.c b/drivers/media/dvb-frontends/ts2020.c index 8b6006635e49..8775083f4dd6 100644 --- a/drivers/media/dvb-frontends/ts2020.c +++ b/drivers/media/dvb-frontends/ts2020.c @@ -716,9 +716,9 @@ static void ts2020_remove(struct i2c_client *client) } static const struct i2c_device_id ts2020_id_table[] = { - { "ts2020" }, - { "ts2022" }, - {} + { .name = "ts2020" }, + { .name = "ts2022" }, + { } }; MODULE_DEVICE_TABLE(i2c, ts2020_id_table); diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 8f2ba4121586..5d173e0ecf42 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -237,7 +237,7 @@ config VIDEO_IMX319 config VIDEO_IMX334 tristate "Sony IMX334 sensor support" - depends on OF_GPIO + depends on OF select V4L2_CCI_I2C help This is a Video4Linux2 sensor driver for the Sony @@ -248,7 +248,7 @@ config VIDEO_IMX334 config VIDEO_IMX335 tristate "Sony IMX335 sensor support" - depends on OF_GPIO + depends on OF select V4L2_CCI_I2C help This is a Video4Linux2 sensor driver for the Sony @@ -268,7 +268,7 @@ config VIDEO_IMX355 config VIDEO_IMX412 tristate "Sony IMX412 sensor support" - depends on OF_GPIO + depends on OF help This is a Video4Linux2 sensor driver for the Sony IMX412 camera. @@ -278,7 +278,7 @@ config VIDEO_IMX412 config VIDEO_IMX415 tristate "Sony IMX415 sensor support" - depends on OF_GPIO + depends on OF select V4L2_CCI_I2C help This is a Video4Linux2 sensor driver for the Sony @@ -703,7 +703,7 @@ config VIDEO_OV8865 config VIDEO_OV9282 tristate "OmniVision OV9282 sensor support" - depends on OF_GPIO + depends on OF select V4L2_CCI_I2C help This is a Video4Linux2 sensor driver for the OmniVision @@ -1271,7 +1271,6 @@ config VIDEO_BT866 config VIDEO_ISL7998X tristate "Intersil ISL7998x video decoder" depends on VIDEO_DEV && I2C - depends on OF_GPIO select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE @@ -1309,7 +1308,6 @@ config VIDEO_MAX9286 tristate "Maxim MAX9286 GMSL deserializer support" depends on I2C && I2C_MUX depends on VIDEO_DEV - depends on OF_GPIO select V4L2_FWNODE select VIDEO_V4L2_SUBDEV_API select MEDIA_CONTROLLER @@ -1659,6 +1657,8 @@ endmenu menu "Miscellaneous helper chips" visible if !MEDIA_HIDE_ANCILLARY_SUBDRV +source "drivers/media/i2c/cvs/Kconfig" + config VIDEO_I2C tristate "I2C transport video support" depends on VIDEO_DEV && I2C diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 90b276a7417a..e45359efe0e4 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -171,3 +171,4 @@ obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o obj-$(CONFIG_VIDEO_WM8739) += wm8739.o obj-$(CONFIG_VIDEO_WM8775) += wm8775.o +obj-$(CONFIG_VIDEO_INTEL_CVS) += cvs/ diff --git a/drivers/media/i2c/ad5820.c b/drivers/media/i2c/ad5820.c index f60271082fb5..fcce2857b69e 100644 --- a/drivers/media/i2c/ad5820.c +++ b/drivers/media/i2c/ad5820.c @@ -347,8 +347,8 @@ static void ad5820_remove(struct i2c_client *client) } static const struct i2c_device_id ad5820_id_table[] = { - { "ad5820" }, - { "ad5821" }, + { .name = "ad5820" }, + { .name = "ad5821" }, { } }; MODULE_DEVICE_TABLE(i2c, ad5820_id_table); diff --git a/drivers/media/i2c/adp1653.c b/drivers/media/i2c/adp1653.c index 391bc75bfcd0..177a6e8d7fb8 100644 --- a/drivers/media/i2c/adp1653.c +++ b/drivers/media/i2c/adp1653.c @@ -522,7 +522,7 @@ static void adp1653_remove(struct i2c_client *client) } static const struct i2c_device_id adp1653_id_table[] = { - { ADP1653_NAME }, + { .name = ADP1653_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, adp1653_id_table); diff --git a/drivers/media/i2c/adv7170.c b/drivers/media/i2c/adv7170.c index ef8682b980b4..812998729207 100644 --- a/drivers/media/i2c/adv7170.c +++ b/drivers/media/i2c/adv7170.c @@ -377,8 +377,8 @@ static void adv7170_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id adv7170_id[] = { - { "adv7170" }, - { "adv7171" }, + { .name = "adv7170" }, + { .name = "adv7171" }, { } }; MODULE_DEVICE_TABLE(i2c, adv7170_id); diff --git a/drivers/media/i2c/adv7175.c b/drivers/media/i2c/adv7175.c index 384da1ec5bf9..f1caab8e2abd 100644 --- a/drivers/media/i2c/adv7175.c +++ b/drivers/media/i2c/adv7175.c @@ -432,8 +432,8 @@ static void adv7175_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id adv7175_id[] = { - { "adv7175" }, - { "adv7176" }, + { .name = "adv7175" }, + { .name = "adv7176" }, { } }; MODULE_DEVICE_TABLE(i2c, adv7175_id); diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index 669b0b3165b1..e5d11a6e6766 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -1626,18 +1626,18 @@ static SIMPLE_DEV_PM_OPS(adv7180_pm_ops, adv7180_suspend, adv7180_resume); #endif static const struct i2c_device_id adv7180_id[] = { - { "adv7180", (kernel_ulong_t)&adv7180_info }, - { "adv7180cp", (kernel_ulong_t)&adv7180_info }, - { "adv7180st", (kernel_ulong_t)&adv7180_info }, - { "adv7182", (kernel_ulong_t)&adv7182_info }, - { "adv7280", (kernel_ulong_t)&adv7280_info }, - { "adv7280-m", (kernel_ulong_t)&adv7280_m_info }, - { "adv7281", (kernel_ulong_t)&adv7281_info }, - { "adv7281-m", (kernel_ulong_t)&adv7281_m_info }, - { "adv7281-ma", (kernel_ulong_t)&adv7281_ma_info }, - { "adv7282", (kernel_ulong_t)&adv7282_info }, - { "adv7282-m", (kernel_ulong_t)&adv7282_m_info }, - {} + { .name = "adv7180", .driver_data = (kernel_ulong_t)&adv7180_info }, + { .name = "adv7180cp", .driver_data = (kernel_ulong_t)&adv7180_info }, + { .name = "adv7180st", .driver_data = (kernel_ulong_t)&adv7180_info }, + { .name = "adv7182", .driver_data = (kernel_ulong_t)&adv7182_info }, + { .name = "adv7280", .driver_data = (kernel_ulong_t)&adv7280_info }, + { .name = "adv7280-m", .driver_data = (kernel_ulong_t)&adv7280_m_info }, + { .name = "adv7281", .driver_data = (kernel_ulong_t)&adv7281_info }, + { .name = "adv7281-m", .driver_data = (kernel_ulong_t)&adv7281_m_info }, + { .name = "adv7281-ma", .driver_data = (kernel_ulong_t)&adv7281_ma_info }, + { .name = "adv7282", .driver_data = (kernel_ulong_t)&adv7282_info }, + { .name = "adv7282-m", .driver_data = (kernel_ulong_t)&adv7282_m_info }, + { } }; MODULE_DEVICE_TABLE(i2c, adv7180_id); diff --git a/drivers/media/i2c/adv7183.c b/drivers/media/i2c/adv7183.c index 25a31a6dd456..a04a1a205fe0 100644 --- a/drivers/media/i2c/adv7183.c +++ b/drivers/media/i2c/adv7183.c @@ -619,8 +619,8 @@ static void adv7183_remove(struct i2c_client *client) } static const struct i2c_device_id adv7183_id[] = { - { "adv7183" }, - {} + { .name = "adv7183" }, + { } }; MODULE_DEVICE_TABLE(i2c, adv7183_id); diff --git a/drivers/media/i2c/adv7343.c b/drivers/media/i2c/adv7343.c index b96443404a26..9b91d7073d3c 100644 --- a/drivers/media/i2c/adv7343.c +++ b/drivers/media/i2c/adv7343.c @@ -502,8 +502,8 @@ static void adv7343_remove(struct i2c_client *client) } static const struct i2c_device_id adv7343_id[] = { - { "adv7343" }, - {} + { .name = "adv7343" }, + { } }; MODULE_DEVICE_TABLE(i2c, adv7343_id); diff --git a/drivers/media/i2c/adv7393.c b/drivers/media/i2c/adv7393.c index c7994bd0bbd4..6f948ba02f86 100644 --- a/drivers/media/i2c/adv7393.c +++ b/drivers/media/i2c/adv7393.c @@ -446,8 +446,8 @@ static void adv7393_remove(struct i2c_client *client) } static const struct i2c_device_id adv7393_id[] = { - { "adv7393" }, - {} + { .name = "adv7393" }, + { } }; MODULE_DEVICE_TABLE(i2c, adv7393_id); diff --git a/drivers/media/i2c/adv7511-v4l2.c b/drivers/media/i2c/adv7511-v4l2.c index 853c7806de92..860cff50c522 100644 --- a/drivers/media/i2c/adv7511-v4l2.c +++ b/drivers/media/i2c/adv7511-v4l2.c @@ -2008,7 +2008,7 @@ static void adv7511_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id adv7511_id[] = { - { "adv7511-v4l2" }, + { .name = "adv7511-v4l2" }, { } }; MODULE_DEVICE_TABLE(i2c, adv7511_id); diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 67116a4ef134..ac9c69ce438f 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -3236,10 +3236,10 @@ static const struct adv76xx_chip_info adv76xx_chip_info[] = { }; static const struct i2c_device_id adv76xx_i2c_id[] = { - { "adv7604", (kernel_ulong_t)&adv76xx_chip_info[ADV7604] }, - { "adv7610", (kernel_ulong_t)&adv76xx_chip_info[ADV7611] }, - { "adv7611", (kernel_ulong_t)&adv76xx_chip_info[ADV7611] }, - { "adv7612", (kernel_ulong_t)&adv76xx_chip_info[ADV7612] }, + { .name = "adv7604", .driver_data = (kernel_ulong_t)&adv76xx_chip_info[ADV7604] }, + { .name = "adv7610", .driver_data = (kernel_ulong_t)&adv76xx_chip_info[ADV7611] }, + { .name = "adv7611", .driver_data = (kernel_ulong_t)&adv76xx_chip_info[ADV7611] }, + { .name = "adv7612", .driver_data = (kernel_ulong_t)&adv76xx_chip_info[ADV7612] }, { } }; MODULE_DEVICE_TABLE(i2c, adv76xx_i2c_id); @@ -3668,6 +3668,12 @@ static int adv76xx_probe(struct i2c_client *client) state->source_pad = state->info->num_dv_ports + (state->info->has_afe ? 2 : 0); + if (WARN_ON(state->source_pad >= ADV76XX_PAD_MAX)) { + err = -EINVAL; + v4l2_err(sd, "invalid chip info\n"); + goto err_i2c; + } + 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; diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index ea6966c0605e..3cfae89ce944 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -3675,7 +3675,7 @@ static void adv7842_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id adv7842_id[] = { - { "adv7842" }, + { .name = "adv7842" }, { } }; MODULE_DEVICE_TABLE(i2c, adv7842_id); diff --git a/drivers/media/i2c/ak881x.c b/drivers/media/i2c/ak881x.c index ee575d01a676..cea46f01997d 100644 --- a/drivers/media/i2c/ak881x.c +++ b/drivers/media/i2c/ak881x.c @@ -304,8 +304,8 @@ static void ak881x_remove(struct i2c_client *client) } static const struct i2c_device_id ak881x_id[] = { - { "ak8813" }, - { "ak8814" }, + { .name = "ak8813" }, + { .name = "ak8814" }, { } }; MODULE_DEVICE_TABLE(i2c, ak881x_id); diff --git a/drivers/media/i2c/alvium-csi2.c b/drivers/media/i2c/alvium-csi2.c index 955b7072a560..dd991c2ee700 100644 --- a/drivers/media/i2c/alvium-csi2.c +++ b/drivers/media/i2c/alvium-csi2.c @@ -2100,20 +2100,21 @@ static int alvium_ctrl_init(struct alvium_dev *alvium) V4L2_CID_PIXEL_RATE, 0, ALVIUM_DEFAULT_PIXEL_RATE_MHZ, 1, ALVIUM_DEFAULT_PIXEL_RATE_MHZ); - ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; /* Link freq is fixed */ ctrls->link_freq = v4l2_ctrl_new_int_menu(hdl, ops, V4L2_CID_LINK_FREQ, 0, 0, &alvium->link_freq); - ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + if (ctrls->link_freq) + ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; /* Auto/manual white balance */ if (alvium->avail_ft.auto_whiteb) { ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); - v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false); + if (ctrls->auto_wb) + v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false); } ctrls->blue_balance = v4l2_ctrl_new_std(hdl, ops, @@ -2122,6 +2123,7 @@ static int alvium_ctrl_init(struct alvium_dev *alvium) alvium->max_bbalance, alvium->inc_bbalance, alvium->dft_bbalance); + ctrls->red_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE, alvium->min_rbalance, @@ -2136,7 +2138,9 @@ static int alvium_ctrl_init(struct alvium_dev *alvium) V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO); - v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, 1, true); + if (ctrls->auto_exp) + v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, + V4L2_EXPOSURE_MANUAL, true); } ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, @@ -2145,14 +2149,16 @@ static int alvium_ctrl_init(struct alvium_dev *alvium) alvium->max_exp, alvium->inc_exp, alvium->dft_exp); - ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE; + if (ctrls->exposure) + ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE; /* Auto/manual gain */ if (alvium->avail_ft.auto_gain) { ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN, 0, 1, 1, 1); - v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true); + if (ctrls->auto_gain) + v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true); } if (alvium->avail_ft.gain) { @@ -2162,7 +2168,8 @@ static int alvium_ctrl_init(struct alvium_dev *alvium) alvium->max_gain, alvium->inc_gain, alvium->dft_gain); - ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE; + if (ctrls->gain) + ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE; } if (alvium->avail_ft.sat) diff --git a/drivers/media/i2c/bt819.c b/drivers/media/i2c/bt819.c index f97245f91f88..da44100062fa 100644 --- a/drivers/media/i2c/bt819.c +++ b/drivers/media/i2c/bt819.c @@ -457,9 +457,9 @@ static void bt819_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id bt819_id[] = { - { "bt819a" }, - { "bt817a" }, - { "bt815a" }, + { .name = "bt819a" }, + { .name = "bt817a" }, + { .name = "bt815a" }, { } }; MODULE_DEVICE_TABLE(i2c, bt819_id); diff --git a/drivers/media/i2c/bt856.c b/drivers/media/i2c/bt856.c index 6852aa47cafb..656719158bb4 100644 --- a/drivers/media/i2c/bt856.c +++ b/drivers/media/i2c/bt856.c @@ -230,7 +230,7 @@ static void bt856_remove(struct i2c_client *client) } static const struct i2c_device_id bt856_id[] = { - { "bt856" }, + { .name = "bt856" }, { } }; MODULE_DEVICE_TABLE(i2c, bt856_id); diff --git a/drivers/media/i2c/bt866.c b/drivers/media/i2c/bt866.c index a2cc34d35ed2..f5104c7d9f90 100644 --- a/drivers/media/i2c/bt866.c +++ b/drivers/media/i2c/bt866.c @@ -197,7 +197,7 @@ static void bt866_remove(struct i2c_client *client) } static const struct i2c_device_id bt866_id[] = { - { "bt866" }, + { .name = "bt866" }, { } }; MODULE_DEVICE_TABLE(i2c, bt866_id); diff --git a/drivers/media/i2c/cs3308.c b/drivers/media/i2c/cs3308.c index 3f2f993e16a6..ad37aeecf05e 100644 --- a/drivers/media/i2c/cs3308.c +++ b/drivers/media/i2c/cs3308.c @@ -109,7 +109,7 @@ static void cs3308_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id cs3308_id[] = { - { "cs3308" }, + { .name = "cs3308" }, { } }; MODULE_DEVICE_TABLE(i2c, cs3308_id); diff --git a/drivers/media/i2c/cs5345.c b/drivers/media/i2c/cs5345.c index 3a9797a50e82..49b8020fbea2 100644 --- a/drivers/media/i2c/cs5345.c +++ b/drivers/media/i2c/cs5345.c @@ -189,7 +189,7 @@ static void cs5345_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id cs5345_id[] = { - { "cs5345" }, + { .name = "cs5345" }, { } }; MODULE_DEVICE_TABLE(i2c, cs5345_id); diff --git a/drivers/media/i2c/cs53l32a.c b/drivers/media/i2c/cs53l32a.c index c4cad3293905..a894dcf6b5a9 100644 --- a/drivers/media/i2c/cs53l32a.c +++ b/drivers/media/i2c/cs53l32a.c @@ -200,7 +200,7 @@ static void cs53l32a_remove(struct i2c_client *client) } static const struct i2c_device_id cs53l32a_id[] = { - { "cs53l32a" }, + { .name = "cs53l32a" }, { } }; MODULE_DEVICE_TABLE(i2c, cs53l32a_id); diff --git a/drivers/media/i2c/cvs/Kconfig b/drivers/media/i2c/cvs/Kconfig new file mode 100644 index 000000000000..4309d20dd726 --- /dev/null +++ b/drivers/media/i2c/cvs/Kconfig @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config VIDEO_INTEL_CVS + tristate "Intel CVS CSI-2 bridge support" + depends on I2C && ACPI && VIDEO_DEV + depends on IPU_BRIDGE || !IPU_BRIDGE + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This adds support for the Intel Computer Vision Sensing (CVS). + + The driver registers a V4L2 sub-device to arbitrate CSI-2 link + ownership between the host and CVS firmware, and configures the + device for camera sensor streaming. + + The driver can operate with full I2C transport or in a reduced + platform (GPIO-only) mode when I2C is unavailable. + + Say Y to build into the kernel, or M to build as a module. + The module will be named intel_cvs. If unsure, say N. diff --git a/drivers/media/i2c/cvs/Makefile b/drivers/media/i2c/cvs/Makefile new file mode 100644 index 000000000000..0aeb2c2e3100 --- /dev/null +++ b/drivers/media/i2c/cvs/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only +intel_cvs-y := core.o v4l2.o + +obj-$(CONFIG_VIDEO_INTEL_CVS) += intel_cvs.o diff --git a/drivers/media/i2c/cvs/core.c b/drivers/media/i2c/cvs/core.c new file mode 100644 index 000000000000..4282f33c7295 --- /dev/null +++ b/drivers/media/i2c/cvs/core.c @@ -0,0 +1,1043 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026 Intel Corporation + */ + +#include <linux/acpi.h> +#include <linux/cleanup.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/jiffies.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/time64.h> +#include <linux/workqueue.h> + +#include <media/ipu-bridge.h> +#include <media/ipu6-pci-table.h> + +#include "icvs.h" + +/* Command timeouts determined experimentally */ +#define CMD_TIMEOUT (5 * HZ) +#define FW_READY_DELAY_MS 100 + +#define PCI_DEVICE_ID_INTEL_IPU7 0x645d /* MTL / LNL */ +#define PCI_DEVICE_ID_INTEL_IPU7P5 0xb05d /* ARL / PTL */ + +/* + * IPU7 PCI device IDs not covered by ipu6_pci_tbl in ipu6-pci-table.h. + * Once the IPU6 driver gains support for IPU7, this table can be dropped. + */ +static const struct pci_device_id icvs_ipu7_tbl[] = { + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU7) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU7P5) }, + { } +}; + +static const struct acpi_gpio_params gpio_wake = { 0, 0, false }; +static const struct acpi_gpio_params gpio_rst = { 1, 0, false }; +static const struct acpi_gpio_params gpio_req = { 2, 0, false }; +static const struct acpi_gpio_params gpio_resp = { 3, 0, false }; +static const struct acpi_gpio_mapping icvs_acpi_gpios[] = { + { "wake-gpio", &gpio_wake, 1 }, + { "rst-gpio", &gpio_rst, 1 }, + { "req-gpio", &gpio_req, 1 }, + { "resp-gpio", &gpio_resp, 1 }, + { } +}; + +static const struct acpi_gpio_params lgpio_req = { 0, 0, false }; +static const struct acpi_gpio_params lgpio_resp = { 1, 0, false }; +static const struct acpi_gpio_mapping icvs_acpi_lgpios[] = { + { "req-gpio", &lgpio_req, 1 }, + { "resp-gpio", &lgpio_resp, 1 }, + { } +}; + +/* Device quirk table */ +static const struct icvs_device_quirk cvs_quirk_table[] = { + { 0x2ac1, 0x20d0, ICVS_NO_MIPI_CONFIG | + ICVS_NO_CAPS | + ICVS_NO_FW_UPDATE + }, /* Lattice NX33 */ + { 0x06CB, 0x0701, ICVS_SKIP_FW_RESET | + ICVS_HOST_SENSOR_PWR_CTRL | + ICVS_HOST_PRIV_CTRL | + ICVS_FW_BUF_SIZE_256 | + ICVS_FW_HEADER_SIZE_256 + }, /* Synaptics SVP7xxx */ + { } +}; + +/** + * cvs_set_quirks - Match device VID/PID and set quirks + * @ctx: CVS device context + * @vid: Vendor ID + * @pid: Product ID + * + * Searches the quirk table for a matching VID/PID and populates ctx->quirks + * with the corresponding quirk flags. + * If no match is found, quirks is set to 0. + */ +static void cvs_set_quirks(struct icvs *ctx, u16 vid, u16 pid) +{ + ctx->quirks = 0; + + for (unsigned int i = 0; i < ARRAY_SIZE(cvs_quirk_table); i++) { + if (cvs_quirk_table[i].vid == vid && + cvs_quirk_table[i].pid == pid) { + ctx->quirks = cvs_quirk_table[i].quirks; + dev_info(cvs_dev(ctx), + "Quirks: 0x%lx (VID:0x%04x PID:0x%04x)\n", + ctx->quirks, vid, pid); + return; + } + } + + dev_info(cvs_dev(ctx), + "No quirks for device (VID:0x%04x PID:0x%04x)\n", vid, pid); +} + +/* I2C transport helpers */ + +/** + * cvs_read_i2c - Issue a read-type command and fetch device response + * @ctx: CVS device context + * @cmd_id: Command identifier (big endian) + * @resp: Destination buffer for response payload + * @size: Size of payload to read into @resp (without prefix) + * + * Sends @cmd_id and reads back the response in a single I2C transaction. + * When the device prepends a 4-byte protocol prefix, the combined + * prefix+payload is read into a temporary buffer and only the payload is + * copied to @resp, avoiding any dependency on the layout of the caller's + * buffer. + * + * Return: 0 on success or negative errno. + */ +static int cvs_read_i2c(struct icvs *ctx, __be16 cmd_id, void *resp, + size_t size) +{ + size_t prefix_size = ctx->prefix ? sizeof(u32) : 0; + size_t read_size = size + prefix_size; + struct i2c_client *i2c = ctx->i2c_client; + u8 *buf __free(kfree) = NULL; + int cnt; + + if (!resp || !size) + return -EINVAL; + + cnt = i2c_master_send(i2c, (const char *)&cmd_id, sizeof(cmd_id)); + if (cnt != sizeof(cmd_id)) + return cnt < 0 ? cnt : -EIO; + + buf = kmalloc(read_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + cnt = i2c_master_recv(i2c, buf, read_size); + if (cnt != read_size) { + dev_dbg(cvs_dev(ctx), "recv cmd 0x%04x short read (%d/%zu)\n", + be16_to_cpu(cmd_id), cnt, read_size); + return cnt < 0 ? cnt : -EIO; + } + + memcpy(resp, buf + prefix_size, size); + + return 0; +} + +/** + * cvs_write_i2c - Write a raw command buffer to the device over I2C + * @ctx: CVS device context + * @data: Buffer containing command + payload + * @size: Total bytes to write + * + * Return: 0 on success or negative errno. + */ +static int cvs_write_i2c(struct icvs *ctx, const void *data, int size) +{ + struct i2c_client *i2c = ctx->i2c_client; + int cnt; + + if (size < 0 || !data) + return -EINVAL; + + cnt = i2c_master_send(i2c, data, size); + if (cnt != size) { + dev_dbg(cvs_dev(ctx), "send short (%d/%d)\n", cnt, size); + return cnt < 0 ? cnt : -EIO; + } + + return 0; +} + +/** + * cvs_checksum - Simple additive checksum helper + * @data: 32-bit aligned data buffer + * @len: Length in bytes (multiple of 4) + * + * Return: 32-bit additive checksum of the dwords in @data. + */ +static u32 cvs_checksum(const void *data, size_t len) +{ + const u32 *words = data; + u32 csum = 0; + + if (WARN_ON_ONCE(len % sizeof(u32))) + return 0; + + for (unsigned int i = 0; i < len / sizeof(u32); i++) + csum += words[i]; + + return csum; +} + +/** + * cvs_schedule_and_wait - Schedule polling work then wait for completion + * @ctx: CVS device context + * @work_delay_ms: Delay in milliseconds before polling work executes + * @wait_jiffies: Timeout (jiffies) to wait for cmd completion + * + * Queues ctx->work to run after @work_delay_ms and then waits up to + * @wait_jiffies for ctx->cmd_completion. + * + * Return: 0 on success, -ETIMEDOUT on timeout, negative errno on error. + */ +static int cvs_schedule_and_wait(struct icvs *ctx, unsigned int work_delay_ms, + unsigned long wait_jiffies) +{ + int ret; + + schedule_delayed_work(&ctx->work, msecs_to_jiffies(work_delay_ms)); + ret = wait_for_completion_killable_timeout(&ctx->cmd_completion, + wait_jiffies); + if (ret < 0) + return ret; + if (!ret) + return -ETIMEDOUT; + + return 0; +} + +/** + * cvs_wait_wake_or_sleep - Wait for wake IRQ or sleep fallback + * @ctx: CVS device context + * @timeout_jiffies: Timeout (jiffies) for wake event on full-cap devices + * @sleep_ms: Milliseconds to sleep on light-cap devices + * + * For full capability devices (ICVS_FULLCAP) waits interruptibly on + * ctx->hostwake_event until ctx->hostwake_event_arg becomes true or + * @timeout_jiffies elapses. The flag is cleared after the wait. + * For light capability devices performs a blocking msleep(@sleep_ms). + * + * Return codes normalized for caller switch handling: + * <0 : error / interrupted + * -ETIMEDOUT : timeout (wake event not observed) + * 0 : success (wake observed OR light-cap sleep elapsed) + * + * Return: negative errno, -ETIMEDOUT on timeout, 0 on success. + */ +static int cvs_wait_wake_or_sleep(struct icvs *ctx, + unsigned long timeout_jiffies, + unsigned int sleep_ms) +{ + int ret; + + if (ctx->res == ICVS_FULLCAP) { + ret = wait_event_interruptible_timeout(ctx->hostwake_event, + ctx->hostwake_event_arg, + timeout_jiffies); + ctx->hostwake_event_arg = false; + if (ret < 0) + return ret; + if (!ret) + return -ETIMEDOUT; + return 0; + } + + msleep(sleep_ms); + + return 0; /* treat sleep path as success */ +} + +/** + * cvs_config_mipi - Send a HOST_SET_MIPI_CONFIG command + * @ctx: CVS device context + * @c: Command container with conf field populated + * @len: Length of original command structure (unused except for symmetry) + * + * Packages @c->param.conf into a cvs_mipi_data_packet including size and + * checksum, then writes it to the device. + * + * Firmware note: the CVS firmware expects the MIPI configuration to be + * sent as a single transaction, including all relevant parameters and checksum. + * + * Return: 0 on success, NO_MIPI_CONFIG or negative errno from I2C write. + */ +static int cvs_config_mipi(struct icvs *ctx, struct icvs_cmd *c, size_t len) +{ + struct icvs_mipi_data_packet pkt = { + .cmd_id = c->cmd_id, + .size = sizeof(c->param.conf), + .crc = cvs_checksum(&c->param.conf, sizeof(c->param.conf)), + .conf = c->param.conf, + }; + + if (ctx->quirks & ICVS_NO_MIPI_CONFIG) + return 0; + + return cvs_write_i2c(ctx, &pkt, sizeof(pkt)); +} + +/** + * cvs_get_device_state - Query current device state bitfield + * @ctx: CVS device context + * @state: Returned state value + * + * Issues GET_DEV_STATE and fills @state. + * + * Return: 0 on success or negative errno. + */ +static int cvs_get_device_state(struct icvs *ctx, u8 *state) +{ + struct icvs_resp n = { + .cmd_id = cpu_to_be16(ICVS_GET_DEV_STATE), + }; + int ret; + + ret = cvs_read_i2c(ctx, n.cmd_id, &n.resp.state, sizeof(n.resp.state)); + if (ret) + return ret; + + *state = n.resp.state; + + return 0; +} + +/** + * cvs_get_device_caps - Read protocol capabilities + * @ctx: CVS device context + * @caps: Capability structure to populate + * + * Return: 0 on success or negative errno. + */ +static int cvs_get_device_caps(struct icvs *ctx, + struct icvs_dev_capabilities *caps) +{ + struct icvs_resp n = { + .cmd_id = cpu_to_be16(ICVS_GET_DEV_CAPABILITY), + }; + int ret; + + if (ctx->quirks & ICVS_NO_CAPS) + return 0; + + ret = cvs_read_i2c(ctx, n.cmd_id, &n.resp.cap, sizeof(n.resp.cap)); + if (ret) + return ret; + + *caps = n.resp.cap; + + return 0; +} + +/** + * cvs_hw_init - Probe device for prefix support and apply quirks + * @ctx: CVS device context + * + * Sends GET_DEV_VID_PID and probes for a 32-bit prefix. + * If it matches ICVS_PREFIX_VAL, sets ctx->prefix for subsequent reads. + * Then reads VID/PID and applies matching quirks. + * GET_DEV_VID_PID is supported by all protocol versions. + * + * Return: 0 on success or negative errno. + */ +static int cvs_hw_init(struct icvs *ctx) +{ + struct icvs_resp n = { }; + __be16 cmd = cpu_to_be16(ICVS_GET_DEV_VID_PID); + u32 resp; + int ret; + + /* + * Clear prefix so cvs_read_i2c always reads exactly sizeof(u32) bytes + * here, regardless of any value left over from a previous call (e.g. + * on resume). + */ + ctx->prefix = false; + ret = cvs_read_i2c(ctx, cmd, &resp, sizeof(resp)); + if (ret) + return ret; + + ctx->prefix = resp == ICVS_PREFIX_VAL; + + /* Now read VID/PID to apply quirks */ + ret = cvs_read_i2c(ctx, cmd, + &n.resp.vid_pid, sizeof(n.resp.vid_pid)); + if (ret) + return ret; + + cvs_set_quirks(ctx, n.resp.vid_pid.v_id, n.resp.vid_pid.p_id); + + return 0; +} + +/** + * cvs_irq_handler - Wake IRQ handler (full capability devices) + * @irq: IRQ number + * @dev_id: Device context pointer + * + * Sets a waitqueue flag and wakes up sleeping waiters. + * + * Return: IRQ_HANDLED always. + */ +static irqreturn_t cvs_irq_handler(int irq, void *dev_id) +{ + struct icvs *ctx = dev_id; + + ctx->hostwake_event_arg = true; + wake_up_interruptible(&ctx->hostwake_event); + + return IRQ_HANDLED; +} + +/** + * cvs_reset - Toggle reset GPIO for full capability devices + * @ctx: CVS device context + * + * Drives reset low briefly then high if device resources indicate full + * capability. Light devices have no reset line. + */ +static void cvs_reset(struct icvs *ctx) +{ + if (ctx->quirks & ICVS_SKIP_FW_RESET) + return; + + if (ctx->res == ICVS_FULLCAP) { + gpiod_set_value(ctx->rst, 0); + fsleep(2000); + gpiod_set_value(ctx->rst, 1); + } +} + +/** + * cvs_recv - Delayed work handler polling for command completion + * @work: Embedded delayed_work member + * + * Re-reads device state; if device_busy remains set, re-schedules itself. + * Otherwise stores state into wq_resp and completes the command. + */ +static void cvs_recv(struct work_struct *work) +{ + struct icvs *ctx = container_of(work, struct icvs, work.work); + u8 state = 0; + int ret; + + ret = cvs_get_device_state(ctx, &state); + if (ret < 0) { + dev_dbg(cvs_dev(ctx), "state read failed: %d\n", ret); + return; + } + + if (state & ICVS_DEV_STATE_BUSY) { + dev_dbg(cvs_dev(ctx), "device busy, reschedule\n"); + schedule_delayed_work(&ctx->work, + msecs_to_jiffies(FW_READY_DELAY_MS)); + return; + } + + ctx->wq_resp.resp.state = state; + complete(&ctx->cmd_completion); +} + +/** + * cvs_send - Common command submission path + * @ctx: CVS device context + * @cmd: Command buffer (icvs_cmd) with cmd_id and param populated + * @len: Buffer length + * + * Dispatches a set of supported commands: + * - ICVS_SET_DEV_HOST_ID, + * - ICVS_HOST_SENSOR_OWNER, + * - ICVS_HOST_SET_MIPI_CONFIG + * - ICVS_FW_LOADER_* + * + * For I2C based commands it sets big-endian cmd ids, writes to the device + * and waits (via delayed work) for completion or timeout. + * GPIO based ownership toggles are handled locally. + * + * Caller must hold ctx->lock when invoking this function and check for i2c + * bus availability. + * + * Return: 0 on success, negative errno, -EINVAL for unsupported command + * or status from device in ctx->wq_resp. + */ +int cvs_send(struct icvs *ctx, struct icvs_cmd *cmd, size_t len) +{ + int ret, status = 0; + + lockdep_assert_held(&ctx->lock); + + dev_dbg(cvs_dev(ctx), "send cmd = 0x%04x", be16_to_cpu(cmd->cmd_id)); + + reinit_completion(&ctx->cmd_completion); + + switch (be16_to_cpu(cmd->cmd_id)) { + case ICVS_SET_DEV_HOST_ID: + cmd->cmd_id = cpu_to_be16(ICVS_SET_DEV_HOST_ID); + ret = cvs_write_i2c(ctx, cmd, len); + if (ret < 0) + break; + + ret = cvs_schedule_and_wait(ctx, FW_READY_DELAY_MS, + CMD_TIMEOUT); + if (ret < 0) + break; + + status = ctx->wq_resp.resp.state & + ICVS_DEV_STATE_ERROR ? -EINVAL : 0; + break; + case ICVS_HOST_SENSOR_OWNER: + gpiod_set_value_cansleep(ctx->req, cmd->param.param); + fsleep(FW_READY_DELAY_MS * USEC_PER_MSEC); + ret = gpiod_get_value_cansleep(ctx->resp); + status = cmd->param.param == ret ? 0 : -EINVAL; + ret = 0; /* success */ + break; + case ICVS_HOST_SET_MIPI_CONFIG: + cmd->cmd_id = cpu_to_be16(ICVS_HOST_SET_MIPI_CONFIG); + ret = cvs_config_mipi(ctx, cmd, len); + if (ret < 0) + break; + + ret = cvs_schedule_and_wait(ctx, FW_READY_DELAY_MS, + CMD_TIMEOUT); + status = (ctx->wq_resp.resp.state & + ICVS_DEV_STATE_ERROR) ? -EINVAL : 0; + break; + case ICVS_FW_LOADER_START: + cmd->cmd_id = cpu_to_be16(ICVS_FW_LOADER_START); + ret = cvs_write_i2c(ctx, cmd, len); + if (ret < 0) + break; + + ret = cvs_wait_wake_or_sleep(ctx, CMD_TIMEOUT, + FW_READY_DELAY_MS); + if (ret) + break; + + ret = cvs_schedule_and_wait(ctx, FW_READY_DELAY_MS, + CMD_TIMEOUT); + status = (ctx->wq_resp.resp.state & + ICVS_DEV_STATE_DOWNLOAD) ? 0 : -EINVAL; + break; + case ICVS_FW_LOADER_DATA: + /* Quirk for older protocols */ + if (ctx->caps.protocol_version_major >= 2 && + ctx->caps.protocol_version_minor >= 2) { + cmd->cmd_id = cpu_to_be16(ICVS_FW_LOADER_DATA); + ret = cvs_write_i2c(ctx, cmd, len); + } else { + ret = cvs_write_i2c(ctx, &cmd->param, + len - sizeof(cmd->cmd_id)); + } + + if (ret < 0) + return ret; + + ret = cvs_wait_wake_or_sleep(ctx, FW_READY_DELAY_MS, + FW_READY_DELAY_MS); + if (ret) + break; + + ret = cvs_schedule_and_wait(ctx, FW_READY_DELAY_MS, + CMD_TIMEOUT); + status = ctx->wq_resp.resp.state & + ICVS_DEV_STATE_ERROR ? -EINVAL : 0; + break; + case ICVS_FW_LOADER_END: + cmd->cmd_id = cpu_to_be16(ICVS_FW_LOADER_END); + ret = cvs_write_i2c(ctx, cmd, len); + if (ret < 0) + break; + + ret = cvs_wait_wake_or_sleep(ctx, CMD_TIMEOUT, + FW_READY_DELAY_MS); + if (ret) + break; + + ret = cvs_schedule_and_wait(ctx, FW_READY_DELAY_MS, + CMD_TIMEOUT); + status = !(ctx->wq_resp.resp.state & + ICVS_DEV_STATE_DOWNLOAD) ? 0 : -EINVAL; + break; + default: + ret = -EINVAL; + break; + } + + if (ret < 0) + return ret; + + return ctx->wq_resp.status = status; +} + +/** + * cvs_set_link_owner - Switch CSI-2 link ownership between host and device + * @ctx: CVS device context + * @owner: Desired owner (ICVS_CSI_LINK_HOST or ICVS_CSI_LINK_CVS) + * + * Called from runtime PM callbacks to claim or release the CSI-2 link. + * Also callable directly for error recovery paths. + * + * Return: 0 on success or negative errno. + */ +int cvs_set_link_owner(struct icvs *ctx, enum icvs_csi_link_owner owner) +{ + struct icvs_cmd cmd = { + .cmd_id = cpu_to_be16(ICVS_HOST_SENSOR_OWNER), + .param.param = owner, + }; + size_t cmd_size = sizeof(cmd.cmd_id) + sizeof(cmd.param.param); + + guard(mutex)(&ctx->lock); + return cvs_send(ctx, &cmd, cmd_size); +} + +/** + * cvs_configure_dev_caps - Configure device capability ownership bits + * @ctx: CVS device context + * + * Tells the CVS device which of its features (privacy LED, RGB camera + * power-up, vision sensing) are controlled by the host, then sends + * SET_DEV_HOST_ID. + * + * Return: 0 on success or negative errno. + */ +static int cvs_configure_dev_caps(struct icvs *ctx) +{ + struct icvs_cmd cmd = { .cmd_id = cpu_to_be16(ICVS_SET_DEV_HOST_ID) }; + size_t sz = sizeof(cmd.cmd_id) + sizeof(cmd.param.host_id); + + if (ctx->quirks & ICVS_NO_CAPS) + return 0; + + if (ctx->quirks & ICVS_HOST_VISION_SENSING) + cmd.param.host_id |= ICVS_HOST_ID_VISION_SENSING; + if (ctx->quirks & ICVS_HOST_PRIV_CTRL) + cmd.param.host_id |= ICVS_HOST_ID_PRIVACY_LED; + if (ctx->quirks & ICVS_HOST_SENSOR_PWR_CTRL) + cmd.param.host_id |= ICVS_HOST_ID_RGBCAMERA_PWRUP; + + guard(mutex)(&ctx->lock); + return cvs_send(ctx, &cmd, sz); +} + +/** + * cvs_core_probe - Shared probe path for I2C & platform instantiation + * @dev: Parent device + * @i2c: I2C client (NULL for platform devices) + * + * Discovers IPU, parses ACPI resources, sets up GPIOs/IRQs, initializes + * sub-device (CSI) and host identifier, and exposes sysfs firmware interface. + * + * Return: 0 on success or negative errno. + */ +static int cvs_core_probe(struct device *dev, struct i2c_client *i2c) +{ + struct pci_dev *ipu = NULL; + struct icvs *ctx; + int ret; + + /* Locate IPU device */ + for (unsigned int i = 0; !ipu && ipu6_pci_tbl[i].vendor; i++) + ipu = pci_get_device(ipu6_pci_tbl[i].vendor, + ipu6_pci_tbl[i].device, NULL); + for (unsigned int i = 0; !ipu && icvs_ipu7_tbl[i].vendor; i++) + ipu = pci_get_device(icvs_ipu7_tbl[i].vendor, + icvs_ipu7_tbl[i].device, NULL); + if (!ipu) + return -ENODEV; + + ret = ipu_bridge_init(&ipu->dev, ipu_bridge_parse_ssdb); + if (ret < 0) + goto err_put_ipu; + + if (!dev_fwnode(dev)) { + ret = -ENXIO; + goto err_put_ipu; + } + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + ret = -ENOMEM; + goto err_put_ipu; + } + + ctx->i2c_client = i2c; + + ret = gpiod_count(dev, NULL); + switch (ret) { + case ICVS_GPIO_SYNC: + ctx->res = ICVS_LIGHTCAP; + break; + case ICVS_GPIO_ASYNC: + ctx->res = ICVS_FULLCAP; + break; + default: + dev_err(dev, "unexpected GPIO count %d\n", ret); + ret = -EINVAL; + goto err_put_ipu; + } + + ret = devm_acpi_dev_add_driver_gpios(dev, + ctx->res == ICVS_FULLCAP ? + icvs_acpi_gpios : + icvs_acpi_lgpios); + if (ret) { + dev_err_probe(dev, ret, "failed to add ACPI GPIOs\n"); + goto err_put_ipu; + } + + ctx->req = devm_gpiod_get(dev, "req", GPIOD_OUT_HIGH); + if (IS_ERR(ctx->req)) { + ret = dev_err_probe(dev, PTR_ERR(ctx->req), + "failed to get req GPIO\n"); + goto err_put_ipu; + } + + ctx->resp = devm_gpiod_get(dev, "resp", GPIOD_IN); + if (IS_ERR(ctx->resp)) { + ret = dev_err_probe(dev, PTR_ERR(ctx->resp), + "failed to get resp GPIO\n"); + goto err_put_ipu; + } + + if (ctx->res == ICVS_FULLCAP) { + struct gpio_desc *wake; + + ctx->rst = devm_gpiod_get(dev, "rst", GPIOD_OUT_HIGH); + if (IS_ERR(ctx->rst)) { + ret = dev_err_probe(dev, PTR_ERR(ctx->rst), + "failed to get rst GPIO\n"); + goto err_put_ipu; + } + + wake = devm_gpiod_get(dev, "wake", GPIOD_IN); + if (IS_ERR(wake)) { + ret = dev_err_probe(dev, PTR_ERR(wake), + "failed to get wake GPIO\n"); + goto err_put_ipu; + } + + ctx->irq = gpiod_to_irq(wake); + if (ctx->irq < 0) { + ret = dev_err_probe(dev, ctx->irq, + "failed to get wake IRQ\n"); + goto err_put_ipu; + } + + ret = devm_request_threaded_irq(dev, ctx->irq, NULL, + cvs_irq_handler, + IRQF_ONESHOT | IRQF_NO_SUSPEND, + "cvs_wake", ctx); + if (ret) { + dev_err_probe(dev, ret, "failed to request IRQ\n"); + goto err_put_ipu; + } + } + + ret = devm_mutex_init(dev, &ctx->lock); + if (ret) + goto err_put_ipu; + + init_completion(&ctx->cmd_completion); + init_waitqueue_head(&ctx->hostwake_event); + INIT_DELAYED_WORK(&ctx->work, cvs_recv); + + if (i2c) { + ret = cvs_hw_init(ctx); + if (ret) { + dev_err(dev, "HW init failed (%d)\n", ret); + /* + * Fallback to GPIO-only mode. + * Some BIOS show the device on the I2C bus, however, + * the device is not accessible via I2C. + */ + ctx->i2c_client = NULL; + goto fail_i2c; + } + + ret = cvs_get_device_caps(ctx, &ctx->caps); + if (ret) { + dev_err_probe(dev, ret, "get caps failed\n"); + goto err_put_ipu; + } + + ret = cvs_configure_dev_caps(ctx); + if (ret) { + dev_err_probe(dev, ret, + "configure dev caps failed\n"); + goto err_put_ipu; + } + } + +fail_i2c: + ret = cvs_csi_init(ctx, dev, i2c); + if (ret) { + dev_err_probe(dev, ret, "CSI init failed\n"); + goto err_put_ipu; + } + + dev_set_drvdata(dev, ctx); + pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_use_autosuspend(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + /* + * Create a PM runtime device link with IPU as consumer and CVS as + * supplier. When the IPU runtime-resumes to start streaming, the PM + * framework automatically resumes CVS first, triggering + * cvs_runtime_resume() which hands CSI-2 link ownership to the host. + */ + ctx->ipu_link = device_link_add(&ipu->dev, dev, + DL_FLAG_PM_RUNTIME | + DL_FLAG_RPM_ACTIVE | + DL_FLAG_STATELESS); + if (!ctx->ipu_link) { + dev_err(dev, "IPU device link failed\n"); + ret = -ENODEV; + goto err_csi_remove; + } + + if (has_acpi_companion(dev)) + acpi_dev_clear_dependencies(ACPI_COMPANION(dev)); + + put_device(&ipu->dev); + + return 0; + +err_csi_remove: + if (ctx->ipu_link) + device_link_del(ctx->ipu_link); + cvs_csi_remove(ctx); + pm_runtime_dont_use_autosuspend(dev); + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + +err_put_ipu: + put_device(&ipu->dev); + + return ret; +} + +/** + * cvs_probe - I2C driver probe entry + * @i2c: I2C client + * + * Return: 0 on success or negative errno. + */ +static int cvs_probe(struct i2c_client *i2c) +{ + return cvs_core_probe(&i2c->dev, i2c); +} + +/** + * cvs_core_remove - Shared remove logic + * @dev: Device + */ +static void cvs_core_remove(struct device *dev) +{ + struct icvs *ctx = dev_get_drvdata(dev); + + cancel_delayed_work_sync(&ctx->work); + cvs_csi_remove(ctx); + + if (ctx->ipu_link) + device_link_del(ctx->ipu_link); + + pm_runtime_put_noidle(dev); + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + + cvs_reset(ctx); +} + +/** + * cvs_remove - I2C driver remove + * @client: I2C client + */ +static void cvs_remove(struct i2c_client *client) +{ + cvs_core_remove(&client->dev); +} + +/** + * cvs_suspend - System suspend callback + * @dev: Device + * + * Return: 0. + */ +static int __maybe_unused cvs_suspend(struct device *dev) +{ + struct icvs *ctx = dev_get_drvdata(dev); + + cancel_delayed_work_sync(&ctx->work); + + return 0; +} + +/** + * cvs_resume - System resume callback + * @dev: Device + * + * Re-validates I2C link prefix and re-sends host id if transport available. + * + * Return: 0 on success or negative errno if I2C check fails. + */ +static int __maybe_unused cvs_resume(struct device *dev) +{ + struct icvs *ctx = dev_get_drvdata(dev); + int ret; + + if (ctx->i2c_client) { + ret = cvs_hw_init(ctx); + if (ret) + return ret; + return cvs_configure_dev_caps(ctx); + } + + return 0; +} + +/** + * cvs_runtime_resume - Runtime PM resume: claim CSI-2 link ownership + * @dev: Device + * + * Triggered automatically when the IPU (consumer) runtime-resumes, because + * a DL_FLAG_PM_RUNTIME device link makes CVS the supplier. Transfers CSI-2 + * link ownership to the host so the IPU can start receiving sensor frames. + * + * Return: 0 on success or negative errno. + */ +static int __maybe_unused cvs_runtime_resume(struct device *dev) +{ + struct icvs *ctx = dev_get_drvdata(dev); + + return cvs_set_link_owner(ctx, ICVS_CSI_LINK_HOST); +} + +/** + * cvs_runtime_suspend - Runtime PM suspend: release CSI-2 link ownership + * @dev: Device + * + * Called when the streaming reference is dropped by cvs_csi_disable_streams + * via pm_runtime_put_autosuspend. Returns CSI-2 link ownership to CVS firmware. + * + * Return: 0 on success or negative errno. + */ +static int __maybe_unused cvs_runtime_suspend(struct device *dev) +{ + struct icvs *ctx = dev_get_drvdata(dev); + + return cvs_set_link_owner(ctx, ICVS_CSI_LINK_CVS); +} + +static const struct dev_pm_ops __maybe_unused cvs_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(cvs_suspend, cvs_resume) + SET_RUNTIME_PM_OPS(cvs_runtime_suspend, cvs_runtime_resume, NULL) +}; + +static const struct acpi_device_id intel_cvs_acpi_match[] = { + { "INTC10DE" }, /* LNL */ + { "INTC10E0" }, /* ARL */ + { "INTC10E1" }, /* PTL */ + { } +}; +MODULE_DEVICE_TABLE(acpi, intel_cvs_acpi_match); + +static struct i2c_driver cvs_driver = { + .driver = { + .name = "intel_cvs", + .acpi_match_table = intel_cvs_acpi_match, + .pm = pm_ptr(&cvs_pm_ops), + }, + .probe = cvs_probe, + .remove = cvs_remove, +}; + +static int cvs_platform_probe(struct platform_device *pdev) +{ + return cvs_core_probe(&pdev->dev, NULL); +} + +static void cvs_platform_remove(struct platform_device *pdev) +{ + cvs_core_remove(&pdev->dev); +} + +/* + * Platform driver structure. + * + * Some platforms may instantiate the CVS device as a platform device + * without I2C support. This driver binding allows such platforms to use the + * CVS core functionality (GPIOs, CSI sub-device) without I2C. + */ +static struct platform_driver cvs_platform_driver = { + .driver = { + .name = "cvs_platform", + .acpi_match_table = intel_cvs_acpi_match, + .pm = pm_ptr(&cvs_pm_ops), + }, + .probe = cvs_platform_probe, + .remove = cvs_platform_remove, +}; + +/** + * cvs_init - Module init registering I2C and platform drivers + * + * Return: 0 on success or negative errno. + */ +static int __init cvs_init(void) +{ + int ret; + + ret = i2c_add_driver(&cvs_driver); + if (ret) + return ret; + + ret = platform_driver_register(&cvs_platform_driver); + if (ret) { + i2c_del_driver(&cvs_driver); + return ret; + } + + return 0; +} + +/** + * cvs_exit - Module exit unregistering drivers + */ +static void __exit cvs_exit(void) +{ + platform_driver_unregister(&cvs_platform_driver); + i2c_del_driver(&cvs_driver); +} +module_init(cvs_init); +module_exit(cvs_exit); + +MODULE_IMPORT_NS("INTEL_IPU_BRIDGE"); +MODULE_AUTHOR("Miguel Vadillo <miguel.vadillo@intel.com>"); +MODULE_DESCRIPTION("Intel Vision Sensing Controller driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/cvs/icvs.h b/drivers/media/i2c/cvs/icvs.h new file mode 100644 index 000000000000..cfa8ef5d975c --- /dev/null +++ b/drivers/media/i2c/cvs/icvs.h @@ -0,0 +1,495 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2026 Intel Corporation + */ + +#ifndef _ICVS_H +#define _ICVS_H + +#include <linux/bits.h> +#include <linux/completion.h> +#include <linux/mutex.h> +#include <linux/workqueue.h> +#include <linux/types.h> +#include <linux/wait.h> + +#include <media/media-entity.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-subdev.h> + +/* + * PCI device IDs for all IPU generations that co-exist with the CVS bridge. + * IPU7 (0x645d) is shared by MTL and LNL; IPU7P5 (0xb05d) is shared by + * ARL and PTL. These are not yet in the shared ipu6-pci-table so they are + * listed here alongside the IPU6 family for probe-time IPU discovery. + */ +struct gpio_desc; +struct i2c_client; + +/* + * GPIO resource counts (ACPI enumerated). + * + * 4-GPIO configuration has a wake IRQ and supports asynchronous messaging. + * 2-GPIO configuration has no IRQ, so communication is synchronous only. + */ +#define ICVS_GPIO_ASYNC 4 +#define ICVS_GPIO_SYNC 2 + +/* Firmware response prefix (optional, for protocol revision 2.x or newer) */ +#define ICVS_PREFIX_VAL 0xCAFEB0BA + +/* + * CSI bridge sub-device definitions + */ +/** + * enum icvs_csi_cmd_id - Low-level CSI bridge command identifiers + * + * These numeric IDs are part of the legacy CSI-side protocol not mapped + * directly to the higher level firmware opcodes in enum icvs_command. Only + * a minimal subset is presently issued/handled by the driver. Others are + * reserved for future expansion of the CSI bridge feature set. + * + * @ICVS_CSI_SET_OWNER: Set CSI sensor ownership between host and CVS + * @ICVS_CSI_SET_CONF: Apply CSI link configuration parameters + * @ICVS_CSI_PRIVACY_NOTIF: Notify host of a privacy state transition + */ +enum icvs_csi_cmd_id { + ICVS_CSI_SET_OWNER = 0, + ICVS_CSI_SET_CONF = 2, + ICVS_CSI_PRIVACY_NOTIF = 6, +}; + +/** + * enum icvs_csi_link_owner - CSI-2 link / sensor ownership + * + * Ownership reflects which endpoint currently controls the attached image + * sensor over the CSI-2 link. Transitions may gate streaming or reconfigure + * link parameters. The host requests ownership changes via protocol opcodes + * and may need to assert GPIO signals on platforms without full capability. + * + * @ICVS_CSI_LINK_HOST: Host (Linux) owns the sensor and may start streaming + * @ICVS_CSI_LINK_CVS: CVS firmware owns the sensor (host must not stream) + */ +enum icvs_csi_link_owner { + ICVS_CSI_LINK_HOST, + ICVS_CSI_LINK_CVS, +}; + +/** + * enum icvs_csi_privacy_status - Reported privacy state + * + * Reflects user privacy (e.g. camera LED assertion and stream gating). The + * MAX value is a sentinel used for bounds checking and is not a real state. + * + * @ICVS_CSI_PRIVACY_OFF: Privacy not asserted (LED off, streaming permitted) + * @ICVS_CSI_PRIVACY_ON: Privacy asserted (LED on and/or stream gated) + * @ICVS_CSI_PRIVACY_MAX: Sentinel; not a valid operational value + */ +enum icvs_csi_privacy_status { + ICVS_CSI_PRIVACY_OFF, + ICVS_CSI_PRIVACY_ON, + ICVS_CSI_PRIVACY_MAX +}; + +/** + * enum icvs_csi_pads - Media pads exposed by the CVS sub-device + * + * The bridge presents a single sink (from the remote sensor) and a single + * source (toward the rest of the media graph / consumers). NUM_PADS is used + * for sizing arrays and iteration; it is not a real pad index. + * + * @ICVS_CSI_PAD_SINK: Sink pad receiving frames from remote sensor + * @ICVS_CSI_PAD_SOURCE: Source pad emitting frames to downstream entities + * @ICVS_CSI_NUM_PADS: Count sentinel (array size / iteration bound) + */ +enum icvs_csi_pads { + ICVS_CSI_PAD_SINK, + ICVS_CSI_PAD_SOURCE, + ICVS_CSI_NUM_PADS +}; + +/* + * Core driver structures and functions used by both I2C and platform modes + */ + +/** + * DOC: CVS device quirk flags + * + * These bit flag macros describe per-device behavioral adjustments applied + * after VID/PID matching (see cvs_i2c_check() and cvs_apply_quirks()). They + * allow the driver to selectively alter logic paths for firmware / hardware + * variants without introducing hard-coded conditionals at each call site. + * + * @ICVS_NO_MIPI_CONFIG: Device firmware performs its own MIPI link setup; + * skip sending HOST_SET_MIPI_CONFIG. + * @ICVS_SKIP_FW_RESET: Skip issuing a post firmware-update reset sequence. + * @ICVS_NO_CAPS: Device firmware does not support GET_DEV_CAPABILITY; + * skip capability query and treat caps as unsupported. + * @ICVS_FW_BUF_SIZE_256: Firmware expects chunk transfer buffer size of 256 + * bytes (override defaults if they differ). + * @ICVS_FW_HEADER_SIZE_256: Firmware header size fixed at 256 bytes offset + * for start of payload data. + * @ICVS_HOST_SENSOR_PWR_CTRL: Host must control sensor power sequencing. + * @ICVS_HOST_PRIV_CTRL: Host owns privacy LED gating. + * @ICVS_HOST_VISION_SENSING: Host enables vision sensing capability bit + * @ICVS_NO_FW_UPDATE: Device does not support firmware update. + */ +#define ICVS_NO_MIPI_CONFIG BIT(0) +#define ICVS_SKIP_FW_RESET BIT(1) +#define ICVS_NO_CAPS BIT(2) +#define ICVS_FW_BUF_SIZE_256 BIT(3) +#define ICVS_FW_HEADER_SIZE_256 BIT(4) +#define ICVS_HOST_SENSOR_PWR_CTRL BIT(5) +#define ICVS_HOST_PRIV_CTRL BIT(6) +#define ICVS_HOST_VISION_SENSING BIT(7) +#define ICVS_NO_FW_UPDATE BIT(8) + +/** + * struct icvs_device_quirk - Device-specific quirk entry + * @vid: Vendor ID + * @pid: Product ID + * @quirks: Quirk flags for this device + */ +struct icvs_device_quirk { + u16 vid; + u16 pid; + unsigned long quirks; +}; + +/** + * struct icvs_dt_config - Data type configuration for a virtual channel + * @pixel_width: Pixel width in pixels + * @pixel_height: Pixel height in pixels + * @data_type: MIPI CSI-2 data type (RAW10, RAW12, etc.) + * @reserved: Reserved for future use + */ +struct icvs_dt_config { + u16 pixel_width; + u16 pixel_height; + u8 data_type; + u8 reserved[3]; +}; + +/** + * struct icvs_vc_config - Virtual channel configuration + * @vc: Virtual channel index (0-31) + * @dt_count: Number of data types configured + * @dt_configs: Array of data type configurations (up to 4) + * @reserved: Reserved for future use + */ +struct icvs_vc_config { + u8 vc; + u8 dt_count; + struct icvs_dt_config dt_configs[4]; + u8 reserved[6]; +}; + +/** + * struct icvs_link_cfg - Host to CVS CSI link configuration + * @fps: Frames per second + * @nr_of_lanes: Number of CSI-2 data lanes used + * @phy_mode: 0 = DPHY, 1 = CPHY + * @vc_count: Number of virtual channels enabled + * @vc_configs: Per-VC configuration + * @link_freq: Link frequency in Hz + * @reserved: Reserved for future use + */ +struct icvs_link_cfg { + u8 fps; + u8 nr_of_lanes; + u8 phy_mode; + u8 vc_count; + struct icvs_vc_config vc_configs[4]; + u32 link_freq; + u8 reserved[8]; +}; + +/** + * struct icvs_fw_version - Firmware version tuple + * @major: Major version + * @minor: Minor version + * @hotfix: Hotfix/patch level + * @build: Build number + */ +struct icvs_fw_version { + u32 major; + u32 minor; + u32 hotfix; + u32 build; +}; + +/** + * struct icvs_vid_pid - Device vendor/product IDs + * @v_id: Vendor ID + * @p_id: Product ID + */ +struct icvs_vid_pid { + u16 v_id; + u16 p_id; +}; + +/** + * struct icvs_mipi_data_packet - Encapsulated MIPI link configuration packet + * @cmd_id: Command identifier + * @size: Payload size (bytes) + * @crc: Checksum over payload + * @conf: CSI link configuration + * @reserved: Reserved for future use + */ +struct icvs_mipi_data_packet { + __be16 cmd_id; + u32 size; + u32 crc; + struct icvs_link_cfg conf; + u8 reserved[70]; +} __packed; + +/** + * struct icvs_mipi_read_packet - Read-back MIPI configuration + * @size: Payload size + * @crc: Payload checksum + * @conf: CSI link configuration + */ +struct icvs_mipi_read_packet { + u32 size; + u32 crc; + struct icvs_link_cfg conf; +}; + +/* Host identifier bitfield masks */ +#define ICVS_HOST_ID_RGBCAMERA_PWRUP BIT(31) +#define ICVS_HOST_ID_PRIVACY_LED BIT(30) +#define ICVS_HOST_ID_DEVICE_POWER GENMASK(29, 28) +#define ICVS_HOST_ID_VISION_SENSING BIT(27) + +/* Device state bitfield masks (u8) */ +#define ICVS_DEV_STATE_PRIVACY BIT(0) +#define ICVS_DEV_STATE_ON BIT(1) +#define ICVS_DEV_STATE_SENSOR_OWNER BIT(2) +#define ICVS_DEV_STATE_DOWNLOAD BIT(4) +#define ICVS_DEV_STATE_ERROR BIT(6) +#define ICVS_DEV_STATE_BUSY BIT(7) + +/* Device capability bitfield masks (u16) */ +#define ICVS_CAP_HOST_MIPI_REQUIRED BIT(15) +#define ICVS_CAP_FW_ANTIROLLBACK BIT(14) +#define ICVS_CAP_PRIVACY2VISIONDRIVER BIT(13) +#define ICVS_CAP_FWUPDATE_RESET_HOST BIT(12) +#define ICVS_CAP_NO_CAMERA_FWUPDATE BIT(11) +#define ICVS_CAP_POWER_DOMAIN_SUPPORT BIT(10) +#define ICVS_CAP_FW_FLASHED_IN_PLACE BIT(9) +#define ICVS_CAP_IO_CONTEXT_HOT BIT(8) + +/** + * struct icvs_dev_capabilities - Protocol capabilities reported by device + * @protocol_version_major: Major protocol version + * @protocol_version_minor: Minor protocol version + * @capability: Capability bitfield - use ICVS_CAP_* masks + * @max_packet_time: Max packet processing time (ms) + * @max_post_dl_time: Max post-download time (s) + */ +struct icvs_dev_capabilities { + u8 protocol_version_major; + u8 protocol_version_minor; + u16 capability; + u16 max_packet_time; + u16 max_post_dl_time; +}; + +/** + * struct icvs_cmd - Generic command container + * @cmd_id: Command identifier + * @param: Parameter union providing multiple variants + * @param.param: Raw parameter + * @param.host_id: Host identifier (ICVS_SET_DEV_HOST_ID) - use + * ICVS_HOST_ID_* masks + * @param.conf: CSI link configuration (ICVS_HOST_SET_MIPI_CONFIG) + */ +struct icvs_cmd { + __be16 cmd_id; + union { + u32 param; + u32 host_id; + struct icvs_link_cfg conf; + } param; +} __packed; + +/** + * struct icvs_resp - Firmware response container + * @status: Internal status code + * @cmd_id: Original command identifier + * @resp: Response union containing variant payload + * @resp.state: Device state response (u8, use ICVS_DEV_STATE_* masks) + * @resp.cap: Capability response + * @resp.conf: Link configuration response + * @resp.mipi_read: Raw link config read-back + * @resp.vid_pid: Vendor/product identifiers + * @resp.fw_version: Firmware version tuple + */ +struct icvs_resp { + u32 status; + __be16 cmd_id; + union { + u8 state; + struct icvs_dev_capabilities cap; + struct icvs_link_cfg conf; + struct icvs_mipi_read_packet mipi_read; + struct icvs_vid_pid vid_pid; + struct icvs_fw_version fw_version; + } resp; +}; + +/** + * enum icvs_resources - Device capability / resource category + * + * Categorizes hardware resource availability which influences protocol + * features (e.g. reset control, wake IRQ, GPIO mediated ownership). Light + * capability devices expose a reduced set of control GPIOs; full capability + * devices provide all optional signals and features. NOTSUP represents an + * unsupported or uninitialized state. + * + * @ICVS_NOTSUP: Capability not supported / not yet determined + * @ICVS_LIGHTCAP: Light capability (limited GPIO / no dedicated wake IRQ) + * @ICVS_FULLCAP: Full capability (reset GPIO, wake IRQ, extended protocol) + */ +enum icvs_resources { + ICVS_NOTSUP, + ICVS_LIGHTCAP, + ICVS_FULLCAP, +}; + +/** + * enum icvs_command - Protocol command opcodes (firmware space 0x0800+) + * @ICVS_GET_DEV_STATE: Query current device state bitfield + * @ICVS_GET_DEV_FW_VERSION: Retrieve firmware version tuple + * @ICVS_GET_DEV_VID_PID: Read vendor / product identifiers + * @ICVS_GET_DEV_ERR_CODE: Fetch last error code (if any) + * @ICVS_GET_DEV_CAPABILITY: Read protocol capability structure + * @ICVS_SET_DEV_HOST_ID: Set host identity / ownership bits + * @ICVS_GET_DEV_HOST_ID: Read back host identity + * @ICVS_FW_LOADER_START: Begin firmware download sequence + * @ICVS_FW_LOADER_DATA: Stream a chunk of firmware payload + * @ICVS_FW_LOADER_END: End firmware download / trigger flash + * @ICVS_HOST_GET_MIPI_CONFIG: Request current MIPI CSI link configuration + * @ICVS_HOST_SET_MIPI_CONFIG: Apply new MIPI CSI link configuration + * @ICVS_HOST_SENSOR_OWNER: Toggle CSI sensor ownership (GPIO assist) + */ +enum icvs_command { + ICVS_GET_DEV_STATE = 0x0800, + ICVS_GET_DEV_FW_VERSION = 0x0801, + ICVS_GET_DEV_VID_PID = 0x0802, + ICVS_GET_DEV_ERR_CODE = 0x0803, + ICVS_GET_DEV_CAPABILITY = 0x0804, + ICVS_SET_DEV_HOST_ID = 0x0805, + ICVS_GET_DEV_HOST_ID = 0x0806, + ICVS_FW_LOADER_START = 0x0820, + ICVS_FW_LOADER_DATA = 0x0821, + ICVS_FW_LOADER_END = 0x0822, + ICVS_HOST_GET_MIPI_CONFIG = 0x082F, + ICVS_HOST_SET_MIPI_CONFIG = 0x0830, + ICVS_HOST_SENSOR_OWNER = 0x0831, +}; + +/** + * enum icvs_state - Device state bitfield flags + * + * These flags correspond to bits in the device state byte returned via + * GET_DEV_STATE and decoded in union icvs_dev_state. Multiple bits may be + * asserted simultaneously. Reserved bits are omitted. + * + * @ICVS_DEVICE_OFF_STATE: Raw zero value; device is powered off or + * not yet ready + * @ICVS_DEVICE_PRIVACY_ON: Privacy mode active (LED asserted and/or + * stream gated) + * @ICVS_DEVICE_ON_STATE: Device powered and responsive to protocol commands + * @ICVS_DEVICE_SENSOR_OWNER: CVS currently owns the attached CSI sensor + * @ICVS_DEVICE_DWNLD_STATE: Firmware download / flash operation in progress + * @ICVS_DEVICE_ERROR_STATE: Device has reported an error (query + * ICVS_GET_DEV_ERR_CODE) + * @ICVS_DEVICE_BUSY_STATE: Device is busy processing a prior command + */ +enum icvs_state { + ICVS_DEVICE_OFF_STATE = 0x00, + ICVS_DEVICE_PRIVACY_ON = BIT(0), + ICVS_DEVICE_ON_STATE = BIT(1), + ICVS_DEVICE_SENSOR_OWNER = BIT(2), + ICVS_DEVICE_DWNLD_STATE = BIT(4), + ICVS_DEVICE_ERROR_STATE = BIT(6), + ICVS_DEVICE_BUSY_STATE = BIT(7), +}; + +/** + * struct icvs - Core CVS device context + * @i2c_client: I2C client (NULL in platform-only mode) + * @work: Delayed work for polling device state / completion + * @wq_resp: Last response container populated by workqueue + * @cmd_completion: Completion for command waiters + * @lock: Mutex protecting command submission & shared state + * @subdev: V4L2 sub-device representing the CSI bridge entity + * @remote: Remote media pad connected to upstream camera sensor + * @notifier: Async notifier for remote sensor discovery + * @ctrl_handler: V4L2 control handler + * @freq_ctrl: (future) frequency control pointer + * @pads: Local media pads (sink/source) + * @nr_of_lanes: Active CSI-2 lane count + * @link_freq: Current link frequency (Hz) + * @ipu_link: PM runtime device link (IPU consumer, CVS supplier) + * @res: Resource capability (light/full) + * @caps: Reported device protocol capabilities + * @prefix: Firmware response prefix present + * @quirks: Device-specific quirk flags + * @rst: Reset GPIO (full capability only) + * @req: Request GPIO (ownership signaling) + * @resp: Response GPIO (ownership signaling) + * @irq: Wake IRQ (full capability) + * @hostwake_event: Waitqueue for wake events + * @hostwake_event_arg: Wake event flag + */ +struct icvs { + struct i2c_client *i2c_client; + struct delayed_work work; + struct icvs_resp wq_resp; + struct completion cmd_completion; + struct mutex lock; /* Protects command execution and device state */ + struct v4l2_subdev subdev; + struct media_pad *remote; + struct v4l2_async_notifier notifier; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *freq_ctrl; + struct media_pad pads[ICVS_CSI_NUM_PADS]; + u32 nr_of_lanes; + u64 link_freq; + struct device_link *ipu_link; + enum icvs_resources res; + struct icvs_dev_capabilities caps; + bool prefix; + unsigned long quirks; + struct gpio_desc *rst; + struct gpio_desc *req; + struct gpio_desc *resp; + int irq; + wait_queue_head_t hostwake_event; + bool hostwake_event_arg; +}; + +/** + * cvs_dev - Helper returning the struct device for a CVS context + * @ctx: CVS context + * + * Avoids repeating transport conditional logic at each call site when + * acquiring the device pointer for logging or PM operations. + * + * Return: Device pointer (never NULL if @ctx is valid). + */ +static inline struct device *cvs_dev(struct icvs *ctx) +{ + return ctx->i2c_client ? &ctx->i2c_client->dev : ctx->subdev.dev; +} + +/* Cross-unit interfaces */ +int cvs_send(struct icvs *ctx, struct icvs_cmd *cmd, size_t len); +int cvs_set_link_owner(struct icvs *ctx, enum icvs_csi_link_owner owner); +int cvs_csi_init(struct icvs *ctx, struct device *dev, struct i2c_client *i2c); +void cvs_csi_remove(struct icvs *ctx); + +#endif /* _ICVS_H */ diff --git a/drivers/media/i2c/cvs/v4l2.c b/drivers/media/i2c/cvs/v4l2.c new file mode 100644 index 000000000000..3a1ec0059ef7 --- /dev/null +++ b/drivers/media/i2c/cvs/v4l2.c @@ -0,0 +1,618 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026 Intel Corporation + * CVS driver - CSI/V4L2 subdev support + */ + +#include <linux/cleanup.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/property.h> + +#include <media/v4l2-async.h> +#include <media/v4l2-common.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-event.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-mc.h> +#include <media/v4l2-subdev.h> + +#include "icvs.h" + +/* + * Helpers + */ +static inline struct icvs *notifier_to_csi(struct v4l2_async_notifier *n) +{ + return container_of(n, struct icvs, notifier); +} + +static inline struct icvs *sd_to_csi(struct v4l2_subdev *sd) +{ + return container_of(sd, struct icvs, subdev); +} + +/* + * Default formats + */ +static const struct v4l2_mbus_framefmt cvs_csi_format_mbus_default = { + .width = 1, + .height = 1, + .code = MEDIA_BUS_FMT_Y8_1X8, + .field = V4L2_FIELD_NONE, +}; + +/** + * csi_set_link_cfg - Program default CSI-2 link parameters + * @ctx: CVS device context + * + * Populates a HOST_SET_MIPI_CONFIG command using current lane count and + * link frequency, then submits it to the device. + * Rest of the link parameters are left at firmware defaults. + * + * Return: 0 on success or negative errno. + */ +static int csi_set_link_cfg(struct icvs *ctx) +{ + struct icvs_cmd cmd = { + .cmd_id = cpu_to_be16(ICVS_HOST_SET_MIPI_CONFIG), + .param.conf.nr_of_lanes = ctx->nr_of_lanes, + .param.conf.link_freq = ctx->link_freq, + }; + size_t cmd_size = sizeof(cmd.cmd_id) + sizeof(cmd.param.conf); + + guard(mutex)(&ctx->lock); + return cvs_send(ctx, &cmd, cmd_size); +} + +/* + * Streaming + */ + +/** + * cvs_csi_enable_streams - Start streaming through the bridge + * @sd: Sub-device pointer + * @state: Active state + * @pad: Pad identifier (must be ICVS_CSI_PAD_SOURCE) + * @streams_mask: Streams to enable (bit 0 supported) + * + * Runtime-resumes the bridge (triggering cvs_runtime_resume() to claim CSI-2 + * link ownership), fetches the link frequency, programs the MIPI configuration, + * and forwards the enable request downstream. + * + * Return: 0 on success or negative errno. + */ +static int cvs_csi_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct icvs *ctx = sd_to_csi(sd); + struct v4l2_subdev *remote_sd = + media_entity_to_v4l2_subdev(ctx->remote->entity); + struct device *dev = cvs_dev(ctx); + s64 freq; + int ret; + + /* cvs_set_link_owner(ICVS_CSI_LINK_HOST) */ + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + freq = v4l2_get_link_freq(ctx->remote, 0, 0); + if (freq < 0) { + ret = freq; + goto err_rpm_put; + } + ctx->link_freq = freq; + + if (ctx->i2c_client) { + ret = csi_set_link_cfg(ctx); + if (ret < 0) + goto err_rpm_put_sync; + } + + ret = v4l2_subdev_enable_streams(remote_sd, + ctx->remote->index, + streams_mask); + if (ret) + goto err_rpm_put_sync; + + return 0; + +err_rpm_put_sync: + /* Bypass autosuspend to immediately release ownership on error */ + pm_runtime_put_sync(dev); + return ret; +err_rpm_put: + pm_runtime_put_autosuspend(dev); + return ret; +} + +/** + * cvs_csi_disable_streams - Stop streaming through the bridge + * @sd: Sub-device pointer + * @state: Active state + * @pad: Pad identifier (must be ICVS_CSI_PAD_SOURCE) + * @streams_mask: Streams to disable (bit 0 supported) + * + * Disables the remote sensor stream then drops the PM reference acquired + * during enable. After the autosuspend delay, cvs_runtime_suspend() will + * return CSI-2 link ownership to CVS firmware. + * + * Return: 0 on success or negative errno. + */ +static int cvs_csi_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct icvs *ctx = sd_to_csi(sd); + struct v4l2_subdev *remote_sd = + media_entity_to_v4l2_subdev(ctx->remote->entity); + struct device *dev = cvs_dev(ctx); + int ret; + + ret = v4l2_subdev_disable_streams(remote_sd, + ctx->remote->index, + streams_mask); + if (ret) + dev_err(dev, "disable streams failed: %d\n", ret); + + /* cvs_set_link_owner(ICVS_CSI_LINK_CVS) */ + pm_runtime_put_autosuspend(dev); + + return ret; +} + +/* + * Pad operations / formats + */ +/** + * cvs_csi_init_state - Initialize pad formats in subdev state + * @sd: Sub-device + * @state: State container + * + * Sets all pad formats to a minimal 1x1 default. + * + * Return: 0. + */ +static int cvs_csi_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + for (unsigned int i = 0; i < sd->entity.num_pads; i++) + *v4l2_subdev_state_get_format(state, i) = + cvs_csi_format_mbus_default; + + return 0; +} + +/** + * cvs_csi_set_fmt - Negotiate pad format + * @sd: Sub-device + * @state: State + * @format: Desired / returned format + * + * Mirrors sink format onto source pad. Accepts many media bus codes, falling + * back to Y8 if unsupported. Normalizes field setting. + * + * Return: 0. + */ +static int cvs_csi_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *src = + v4l2_subdev_state_get_format(state, ICVS_CSI_PAD_SOURCE); + struct v4l2_mbus_framefmt *sink = + v4l2_subdev_state_get_format(state, ICVS_CSI_PAD_SINK); + + if (format->pad == ICVS_CSI_PAD_SOURCE) { /* source pad mirrors sink */ + *src = *sink; + return 0; + } + + v4l_bound_align_image(&format->format.width, 1, 65536, 0, + &format->format.height, 1, 65536, 0, 0); + + switch (format->format.code) { + /* Accept a large list; default fallback to Y8 */ + 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: + case MEDIA_BUS_FMT_SBGGR8_1X8: + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SRGGB8_1X8: + case MEDIA_BUS_FMT_SBGGR10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SBGGR12_1X12: + case MEDIA_BUS_FMT_SGBRG12_1X12: + case MEDIA_BUS_FMT_SGRBG12_1X12: + case MEDIA_BUS_FMT_SRGGB12_1X12: + case MEDIA_BUS_FMT_SBGGR14_1X14: + case MEDIA_BUS_FMT_SGBRG14_1X14: + case MEDIA_BUS_FMT_SGRBG14_1X14: + case MEDIA_BUS_FMT_SRGGB14_1X14: + case MEDIA_BUS_FMT_SBGGR16_1X16: + case MEDIA_BUS_FMT_SGBRG16_1X16: + case MEDIA_BUS_FMT_SGRBG16_1X16: + case MEDIA_BUS_FMT_SRGGB16_1X16: + break; + default: + format->format.code = MEDIA_BUS_FMT_Y8_1X8; + break; + } + + if (format->format.field == V4L2_FIELD_ANY) + format->format.field = V4L2_FIELD_NONE; + + *sink = format->format; + *src = *sink; + + return 0; +} + +/** + * cvs_csi_get_mbus_config - Provide current CSI-2 bus configuration + * @sd: Sub-device + * @pad: Pad index + * @cfg: Returned bus config + * + * Fills lane ordering and number of lanes; retrieves link frequency from + * remote entity. + * + * Return: 0 on success or negative errno. + */ +static int cvs_csi_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_config *cfg) +{ + struct icvs *ctx = sd_to_csi(sd); + s64 freq; + + cfg->type = V4L2_MBUS_CSI2_DPHY; + for (unsigned int i = 0; i < V4L2_MBUS_CSI2_MAX_DATA_LANES; i++) + cfg->bus.mipi_csi2.data_lanes[i] = i + 1; + cfg->bus.mipi_csi2.num_data_lanes = ctx->nr_of_lanes; + + freq = v4l2_get_link_freq(ctx->remote, 0, 0); + if (freq < 0) + return -EINVAL; + + ctx->link_freq = freq; + cfg->link_freq = freq; + + return 0; +} + +static const struct v4l2_subdev_core_ops cvs_csi_subdev_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_video_ops cvs_csi_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_pad_ops cvs_csi_pad_ops = { + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = cvs_csi_set_fmt, + .get_mbus_config = cvs_csi_get_mbus_config, + .enable_streams = cvs_csi_enable_streams, + .disable_streams = cvs_csi_disable_streams, +}; + +static const struct v4l2_subdev_ops cvs_csi_subdev_ops = { + .core = &cvs_csi_subdev_core_ops, + .video = &cvs_csi_video_ops, + .pad = &cvs_csi_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops cvs_csi_internal_ops = { + .init_state = cvs_csi_init_state, +}; + +static const struct media_entity_operations cvs_csi_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +/* + * Async notifier + */ +/** + * cvs_csi_notify_bound - Remote sensor bound callback + * @notifier: Async notifier + * @sd: Remote subdev + * @asc: Async match connection + * + * Locates the source pad of the remote sensor and creates a media link to + * the CVS bridge sink pad enabling it by default. + * + * Return: 0 on success or negative errno. + */ +static int cvs_csi_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_connection *asc) +{ + struct icvs *ctx = notifier_to_csi(notifier); + int pad; + + pad = media_entity_get_fwnode_pad(&sd->entity, asc->match.fwnode, + MEDIA_PAD_FL_SOURCE); + if (pad < 0) + return pad; + + ctx->remote = &sd->entity.pads[pad]; + + return media_create_pad_link(&sd->entity, pad, &ctx->subdev.entity, + ICVS_CSI_PAD_SINK, MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); +} + +/** + * cvs_csi_notify_unbind - Remote sensor unbind callback + * @notifier: Notifier + * @sd: Remote subdev + * @asc: Connection + */ +static void cvs_csi_notify_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_connection *asc) +{ + struct icvs *ctx = notifier_to_csi(notifier); + + ctx->remote = NULL; +} + +static const struct v4l2_async_notifier_operations cvs_csi_notify_ops = { + .bound = cvs_csi_notify_bound, + .unbind = cvs_csi_notify_unbind, +}; + +/* + * Controls + */ +/** + * cvs_csi_init_controls - Initialize V4L2 controls + * @ctx: CVS context + * + * Currently sets up a read-only privacy control placeholder. + * + * Return: 0 on success or negative errno. + */ +static int cvs_csi_init_controls(struct icvs *ctx) +{ + struct v4l2_ctrl *privacy_ctrl; + + v4l2_ctrl_handler_init(&ctx->ctrl_handler, 1); + + privacy_ctrl = v4l2_ctrl_new_std(&ctx->ctrl_handler, NULL, + V4L2_CID_PRIVACY, 0, 1, 1, 0); + if (privacy_ctrl) + privacy_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + if (ctx->ctrl_handler.error) { + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + return ctx->ctrl_handler.error; + } + + ctx->subdev.ctrl_handler = &ctx->ctrl_handler; + + return 0; +} + +/* + * Firmware (graph) parsing + */ +/** + * cvs_csi_parse_firmware - Parse firmware (ACPI graph) endpoints + * @ctx: CVS context + * + * Discovers sink and remote source endpoints, validates lane counts and + * registers an async notifier for the remote sensor. + * + * Return: 0 on success or negative errno. + */ +static int cvs_csi_parse_firmware(struct icvs *ctx) +{ + struct v4l2_fwnode_endpoint ep = { .bus_type = V4L2_MBUS_CSI2_DPHY }; + struct device *dev = cvs_dev(ctx); + struct fwnode_handle *sink_ep, *source_ep; + struct v4l2_async_connection *asc; + int ret; + + sink_ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0); + v4l2_async_subdev_nf_init(&ctx->notifier, &ctx->subdev); + ctx->notifier.ops = &cvs_csi_notify_ops; + + ret = v4l2_fwnode_endpoint_parse(sink_ep, &ep); + if (ret) + goto err_nf_cleanup; + + ctx->nr_of_lanes = ep.bus.mipi_csi2.num_data_lanes; + source_ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 1, 0, 0); + ret = v4l2_fwnode_endpoint_parse(source_ep, &ep); + fwnode_handle_put(source_ep); + if (ret) + goto err_nf_cleanup; + + if (ctx->nr_of_lanes != ep.bus.mipi_csi2.num_data_lanes) { + ret = -EINVAL; + goto err_nf_cleanup; + } + + asc = v4l2_async_nf_add_fwnode_remote(&ctx->notifier, sink_ep, + struct v4l2_async_connection); + if (IS_ERR(asc)) { + ret = PTR_ERR(asc); + goto err_nf_cleanup; + } + + ret = v4l2_async_nf_register(&ctx->notifier); + if (ret) + goto err_nf_cleanup; + + fwnode_handle_put(sink_ep); + + return 0; + +err_nf_cleanup: + v4l2_async_nf_cleanup(&ctx->notifier); + fwnode_handle_put(sink_ep); + + return ret; +} + +/* + * Public CSI init/cleanup used by core probe/remove + */ +/** + * cvs_csi_init - Initialize CSI/V4L2 sub-device side of bridge + * @ctx: CVS context + * @dev: Parent device + * @i2c: I2C client (may be NULL for platform mode) + * + * Sets up sub-device, media entity pads, async notifier, controls and + * registers with the V4L2 framework. + * + * Return: 0 on success or negative errno. + */ +int cvs_csi_init(struct icvs *ctx, struct device *dev, struct i2c_client *i2c) +{ + int ret; + + if (i2c) { + v4l2_i2c_subdev_init(&ctx->subdev, i2c, &cvs_csi_subdev_ops); + } else { + v4l2_subdev_init(&ctx->subdev, &cvs_csi_subdev_ops); + ctx->subdev.dev = dev; + } + + ctx->subdev.internal_ops = &cvs_csi_internal_ops; + v4l2_set_subdevdata(&ctx->subdev, ctx); + ctx->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; + ctx->subdev.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + ctx->subdev.entity.ops = &cvs_csi_entity_ops; + snprintf(ctx->subdev.name, sizeof(ctx->subdev.name), "Intel CVS"); + + ret = cvs_csi_parse_firmware(ctx); + if (ret) + return ret; + + ret = cvs_csi_init_controls(ctx); + if (ret) + goto err_nf_unreg; + + ctx->pads[ICVS_CSI_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + ctx->pads[ICVS_CSI_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&ctx->subdev.entity, ICVS_CSI_NUM_PADS, + ctx->pads); + if (ret) + goto err_ctrl_cleanup; + + ctx->subdev.state_lock = ctx->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&ctx->subdev); + if (ret) + goto err_entity_cleanup; + + ret = v4l2_async_register_subdev(&ctx->subdev); + if (ret) + goto err_entity_cleanup; + + return 0; + +err_entity_cleanup: + media_entity_cleanup(&ctx->subdev.entity); + +err_ctrl_cleanup: + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + +err_nf_unreg: + v4l2_async_nf_unregister(&ctx->notifier); + v4l2_async_nf_cleanup(&ctx->notifier); + + return ret; +} + +/** + * cvs_csi_remove - Cleanup CSI/V4L2 sub-device + * @ctx: CVS context + */ +void cvs_csi_remove(struct icvs *ctx) +{ + v4l2_async_nf_unregister(&ctx->notifier); + v4l2_async_nf_cleanup(&ctx->notifier); + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + v4l2_async_unregister_subdev(&ctx->subdev); + v4l2_subdev_cleanup(&ctx->subdev); + media_entity_cleanup(&ctx->subdev.entity); +} + +MODULE_AUTHOR("Miguel Vadillo <miguel.vadillo@intel.com>"); +MODULE_DESCRIPTION("CSI/V4L2 support for Intel Vision Sensing Controller"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c index 69d5cc648c0f..8110d40931d9 100644 --- a/drivers/media/i2c/cx25840/cx25840-core.c +++ b/drivers/media/i2c/cx25840/cx25840-core.c @@ -3989,7 +3989,7 @@ static void cx25840_remove(struct i2c_client *client) } static const struct i2c_device_id cx25840_id[] = { - { "cx25840" }, + { .name = "cx25840" }, { } }; MODULE_DEVICE_TABLE(i2c, cx25840_id); diff --git a/drivers/media/i2c/ds90ub913.c b/drivers/media/i2c/ds90ub913.c index 49aa5f4a172c..6abb5e324ae1 100644 --- a/drivers/media/i2c/ds90ub913.c +++ b/drivers/media/i2c/ds90ub913.c @@ -872,8 +872,8 @@ static void ub913_remove(struct i2c_client *client) } static const struct i2c_device_id ub913_id[] = { - { "ds90ub913a-q1" }, - {} + { .name = "ds90ub913a-q1" }, + { } }; MODULE_DEVICE_TABLE(i2c, ub913_id); diff --git a/drivers/media/i2c/ds90ub953.c b/drivers/media/i2c/ds90ub953.c index a8ab67f4137f..d4228e1134ff 100644 --- a/drivers/media/i2c/ds90ub953.c +++ b/drivers/media/i2c/ds90ub953.c @@ -1347,9 +1347,9 @@ static const struct ub953_hw_data ds90ub971_hw = { }; static const struct i2c_device_id ub953_id[] = { - { "ds90ub953-q1", (kernel_ulong_t)&ds90ub953_hw }, - { "ds90ub971-q1", (kernel_ulong_t)&ds90ub971_hw }, - {} + { .name = "ds90ub953-q1", .driver_data = (kernel_ulong_t)&ds90ub953_hw }, + { .name = "ds90ub971-q1", .driver_data = (kernel_ulong_t)&ds90ub971_hw }, + { } }; MODULE_DEVICE_TABLE(i2c, ub953_id); diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c index d50e977cf6ce..15a9797b47ac 100644 --- a/drivers/media/i2c/ds90ub960.c +++ b/drivers/media/i2c/ds90ub960.c @@ -5266,10 +5266,10 @@ static const struct ub960_hw_data ds90ub9702_hw = { }; static const struct i2c_device_id ub960_id[] = { - { "ds90ub954-q1", (kernel_ulong_t)&ds90ub954_hw }, - { "ds90ub960-q1", (kernel_ulong_t)&ds90ub960_hw }, - { "ds90ub9702-q1", (kernel_ulong_t)&ds90ub9702_hw }, - {} + { .name = "ds90ub954-q1", .driver_data = (kernel_ulong_t)&ds90ub954_hw }, + { .name = "ds90ub960-q1", .driver_data = (kernel_ulong_t)&ds90ub960_hw }, + { .name = "ds90ub9702-q1", .driver_data = (kernel_ulong_t)&ds90ub9702_hw }, + { } }; MODULE_DEVICE_TABLE(i2c, ub960_id); diff --git a/drivers/media/i2c/dw9714.c b/drivers/media/i2c/dw9714.c index 3288de539452..1d686b1d2fd7 100644 --- a/drivers/media/i2c/dw9714.c +++ b/drivers/media/i2c/dw9714.c @@ -307,7 +307,7 @@ static int __maybe_unused dw9714_vcm_resume(struct device *dev) } static const struct i2c_device_id dw9714_id_table[] = { - { DW9714_NAME }, + { .name = DW9714_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, dw9714_id_table); diff --git a/drivers/media/i2c/dw9719.c b/drivers/media/i2c/dw9719.c index 59558335989e..3b7ba88fd67c 100644 --- a/drivers/media/i2c/dw9719.c +++ b/drivers/media/i2c/dw9719.c @@ -439,6 +439,15 @@ static void dw9719_remove(struct i2c_client *client) pm_runtime_set_suspended(&client->dev); } +static const struct i2c_device_id dw9719_id_table[] = { + { .name = "dw9718s", .driver_data = (kernel_ulong_t)DW9718S }, + { .name = "dw9719", .driver_data = (kernel_ulong_t)DW9719 }, + { .name = "dw9761", .driver_data = (kernel_ulong_t)DW9761 }, + { .name = "dw9800k", .driver_data = (kernel_ulong_t)DW9800K }, + { } +}; +MODULE_DEVICE_TABLE(i2c, dw9719_id_table); + static const struct of_device_id dw9719_of_table[] = { { .compatible = "dongwoon,dw9718s", .data = (const void *)DW9718S }, { .compatible = "dongwoon,dw9719", .data = (const void *)DW9719 }, @@ -459,6 +468,7 @@ static struct i2c_driver dw9719_i2c_driver = { }, .probe = dw9719_probe, .remove = dw9719_remove, + .id_table = dw9719_id_table, }; module_i2c_driver(dw9719_i2c_driver); diff --git a/drivers/media/i2c/et8ek8/et8ek8_driver.c b/drivers/media/i2c/et8ek8/et8ek8_driver.c index 50121c3e5b48..738e2801016a 100644 --- a/drivers/media/i2c/et8ek8/et8ek8_driver.c +++ b/drivers/media/i2c/et8ek8/et8ek8_driver.c @@ -1485,7 +1485,7 @@ static const struct of_device_id et8ek8_of_table[] = { MODULE_DEVICE_TABLE(of, et8ek8_of_table); static const struct i2c_device_id et8ek8_id_table[] = { - { ET8EK8_NAME }, + { .name = ET8EK8_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, et8ek8_id_table); diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 7da02ce5da15..223d3753cc93 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -72,7 +72,7 @@ /* V_TIMING internal */ #define IMX219_REG_FRM_LENGTH_A CCI_REG16(0x0160) -#define IMX219_FLL_MAX 0xffff +#define IMX219_FLL_MAX 0xfffe #define IMX219_VBLANK_MIN 32 #define IMX219_REG_LINE_LENGTH_A CCI_REG16(0x0162) #define IMX219_LLP_MIN 0x0d78 @@ -142,10 +142,10 @@ /* IMX219 native and active pixel array size. */ #define IMX219_NATIVE_WIDTH 3296U #define IMX219_NATIVE_HEIGHT 2480U -#define IMX219_PIXEL_ARRAY_LEFT 8U -#define IMX219_PIXEL_ARRAY_TOP 8U -#define IMX219_PIXEL_ARRAY_WIDTH 3280U -#define IMX219_PIXEL_ARRAY_HEIGHT 2464U +#define IMX219_ACTIVE_AREA_LEFT 8U +#define IMX219_ACTIVE_AREA_TOP 8U +#define IMX219_ACTIVE_AREA_WIDTH 3280U +#define IMX219_ACTIVE_AREA_HEIGHT 2464U /* Mode : resolution and related config&values */ struct imx219_mode { @@ -675,13 +675,13 @@ static int imx219_set_framefmt(struct imx219 *imx219, bpp = imx219_get_format_bpp(format); cci_write(imx219->regmap, IMX219_REG_X_ADD_STA_A, - crop->left - IMX219_PIXEL_ARRAY_LEFT, &ret); + crop->left - IMX219_ACTIVE_AREA_LEFT, &ret); cci_write(imx219->regmap, IMX219_REG_X_ADD_END_A, - crop->left - IMX219_PIXEL_ARRAY_LEFT + crop->width - 1, &ret); + crop->left - IMX219_ACTIVE_AREA_LEFT + crop->width - 1, &ret); cci_write(imx219->regmap, IMX219_REG_Y_ADD_STA_A, - crop->top - IMX219_PIXEL_ARRAY_TOP, &ret); + crop->top - IMX219_ACTIVE_AREA_TOP, &ret); cci_write(imx219->regmap, IMX219_REG_Y_ADD_END_A, - crop->top - IMX219_PIXEL_ARRAY_TOP + crop->height - 1, &ret); + crop->top - IMX219_ACTIVE_AREA_TOP + crop->height - 1, &ret); imx219_get_binning(state, &bin_h, &bin_v); cci_write(imx219->regmap, IMX219_REG_BINNING_MODE_H, bin_h, &ret); @@ -837,11 +837,9 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; u8 bin_h, bin_v, binning; - u32 prev_line_len; int ret; format = v4l2_subdev_state_get_format(state, 0); - prev_line_len = format->width + imx219->hblank->val; /* * Adjust the requested format to match the closest mode. The Bayer @@ -867,8 +865,8 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, * Use binning to maximize the crop rectangle size, and centre it in the * sensor. */ - bin_h = min(IMX219_PIXEL_ARRAY_WIDTH / format->width, 2U); - bin_v = min(IMX219_PIXEL_ARRAY_HEIGHT / format->height, 2U); + bin_h = min(IMX219_ACTIVE_AREA_WIDTH / format->width, 2U); + bin_v = min(IMX219_ACTIVE_AREA_HEIGHT / format->height, 2U); /* Ensure bin_h and bin_v are same to avoid 1:2 or 2:1 stretching */ binning = min(bin_h, bin_v); @@ -882,7 +880,7 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { int exposure_max; int exposure_def; - int hblank, llp_min; + int llp_min; int pixel_rate; /* Update limits and set FPS to default */ @@ -924,15 +922,8 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, llp_min - mode->width); if (ret) return ret; - /* - * Retain PPL setting from previous mode so that the - * line time does not change on a mode change. - * Limits have to be recomputed as the controls define - * the blanking only, so PPL values need to have the - * mode width subtracted. - */ - hblank = prev_line_len - mode->width; - ret = __v4l2_ctrl_s_ctrl(imx219->hblank, hblank); + + ret = __v4l2_ctrl_s_ctrl(imx219->hblank, llp_min - mode->width); if (ret) return ret; @@ -967,10 +958,10 @@ static int imx219_get_selection(struct v4l2_subdev *sd, case V4L2_SEL_TGT_CROP_DEFAULT: case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.top = IMX219_PIXEL_ARRAY_TOP; - sel->r.left = IMX219_PIXEL_ARRAY_LEFT; - sel->r.width = IMX219_PIXEL_ARRAY_WIDTH; - sel->r.height = IMX219_PIXEL_ARRAY_HEIGHT; + sel->r.top = IMX219_ACTIVE_AREA_TOP; + sel->r.left = IMX219_ACTIVE_AREA_LEFT; + sel->r.width = IMX219_ACTIVE_AREA_WIDTH; + sel->r.height = IMX219_ACTIVE_AREA_HEIGHT; return 0; } diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index 8ec78b60bea6..76da647f9cf9 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -182,7 +182,7 @@ struct imx274_mode { }; /* - * imx274 test pattern related structure + * imx274 test pattern related enum */ enum { TEST_PATTERN_DISABLED = 0, @@ -533,7 +533,7 @@ static const struct imx274_mode imx274_modes[] = { /* * struct imx274_ctrls - imx274 ctrl structure * @handler: V4L2 ctrl handler structure - * @exposure: Pointer to expsure ctrl structure + * @exposure: Pointer to exposure ctrl structure * @gain: Pointer to gain ctrl structure * @vflip: Pointer to vflip ctrl structure * @test_pattern: Pointer to test pattern ctrl structure @@ -547,7 +547,7 @@ struct imx274_ctrls { }; /* - * struct stim274 - imx274 device structure + * struct stimx274 - imx274 device structure * @sd: V4L2 subdevice structure * @pad: Media pad structure * @client: Pointer to I2C client @@ -587,9 +587,6 @@ struct stimx274 { ? rounddown((dim), (step)) \ : rounddown((dim) + (step) / 2, (step)))) -/* - * Function declaration - */ static int imx274_set_gain(struct stimx274 *priv, struct v4l2_ctrl *ctrl); static int imx274_set_exposure(struct stimx274 *priv, int val); static int imx274_set_vflip(struct stimx274 *priv, int val); @@ -640,9 +637,9 @@ static int imx274_write_table(struct stimx274 *priv, const struct reg_8 table[]) for (next = table;; next++) { if ((next->addr != range_start + range_count) || - (next->addr == IMX274_TABLE_END) || - (next->addr == IMX274_TABLE_WAIT_MS) || - (range_count == max_range_vals)) { + next->addr == IMX274_TABLE_END || + next->addr == IMX274_TABLE_WAIT_MS || + range_count == max_range_vals) { if (range_count == 1) err = regmap_write(regmap, range_start, range_vals[0]); @@ -650,8 +647,6 @@ static int imx274_write_table(struct stimx274 *priv, const struct reg_8 table[]) err = regmap_bulk_write(regmap, range_start, &range_vals[0], range_count); - else - err = 0; if (err) return err; @@ -897,14 +892,6 @@ static int imx274_regulators_get(struct device *dev, struct stimx274 *imx274) imx274->supplies); } -/** - * imx274_s_ctrl - This is used to set the imx274 V4L2 controls - * @ctrl: V4L2 control to be set - * - * This function is used to set the V4L2 controls for the imx274 sensor. - * - * Return: 0 on success, errors otherwise - */ static int imx274_s_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = ctrl_to_sd(ctrl); @@ -1059,16 +1046,6 @@ static int __imx274_change_compose(struct stimx274 *imx274, return 0; } -/** - * imx274_get_fmt - Get the pad format - * @sd: Pointer to V4L2 Sub device structure - * @sd_state: Pointer to sub device state structure - * @fmt: Pointer to pad level media bus format - * - * This function is used to get the pad format information. - * - * Return: 0 on success - */ static int imx274_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -1081,16 +1058,6 @@ static int imx274_get_fmt(struct v4l2_subdev *sd, return 0; } -/** - * imx274_set_fmt - This is used to set the pad format - * @sd: Pointer to V4L2 Sub device structure - * @sd_state: Pointer to sub device state information structure - * @format: Pointer to pad level media bus format - * - * This function is used to set the pad format. - * - * Return: 0 on success - */ static int imx274_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) @@ -1423,16 +1390,6 @@ static void imx274_load_default(struct stimx274 *priv) priv->ctrls.test_pattern->val = TEST_PATTERN_DISABLED; } -/** - * imx274_s_stream - It is used to start/stop the streaming. - * @sd: V4L2 Sub device - * @on: Flag (True / False) - * - * This function controls the start or stop of streaming for the - * imx274 sensor. - * - * Return: 0 on success, errors otherwise - */ static int imx274_s_stream(struct v4l2_subdev *sd, int on) { struct stimx274 *imx274 = to_imx274(sd); @@ -1951,7 +1908,7 @@ static const struct of_device_id imx274_of_id_table[] = { MODULE_DEVICE_TABLE(of, imx274_of_id_table); static const struct i2c_device_id imx274_id[] = { - { "IMX274" }, + { .name = "IMX274" }, { } }; MODULE_DEVICE_TABLE(i2c, imx274_id); @@ -1998,7 +1955,6 @@ static int imx274_probe(struct i2c_client *client) struct device *dev = &client->dev; int ret; - /* initialize imx274 */ imx274 = devm_kzalloc(dev, sizeof(*imx274), GFP_KERNEL); if (!imx274) return -ENOMEM; @@ -2163,7 +2119,7 @@ static const struct dev_pm_ops imx274_pm_ops = { static struct i2c_driver imx274_i2c_driver = { .driver = { - .name = DRIVER_NAME, + .name = DRIVER_NAME, .pm = &imx274_pm_ops, .of_match_table = imx274_of_id_table, }, diff --git a/drivers/media/i2c/imx334.c b/drivers/media/i2c/imx334.c index 9654f9268056..553a16b84f4d 100644 --- a/drivers/media/i2c/imx334.c +++ b/drivers/media/i2c/imx334.c @@ -566,18 +566,6 @@ static int imx334_update_exp_gain(struct imx334 *imx334, u32 exposure, u32 gain) return ret; } -/** - * imx334_set_ctrl() - Set subdevice control - * @ctrl: pointer to v4l2_ctrl structure - * - * Supported controls: - * - V4L2_CID_VBLANK - * - cluster controls: - * - V4L2_CID_ANALOGUE_GAIN - * - V4L2_CID_EXPOSURE - * - * Return: 0 if successful, error code otherwise. - */ static int imx334_set_ctrl(struct v4l2_ctrl *ctrl) { struct imx334 *imx334 = @@ -678,14 +666,6 @@ static int imx334_get_format_code(struct imx334 *imx334, u32 code) return imx334_mbus_codes[0]; } -/** - * imx334_enum_mbus_code() - Enumerate V4L2 sub-device mbus codes - * @sd: pointer to imx334 V4L2 sub-device structure - * @sd_state: V4L2 sub-device state - * @code: V4L2 sub-device code enumeration need to be filled - * - * Return: 0 if successful, error code otherwise. - */ static int imx334_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) @@ -698,14 +678,6 @@ static int imx334_enum_mbus_code(struct v4l2_subdev *sd, return 0; } -/** - * imx334_enum_frame_size() - Enumerate V4L2 sub-device frame sizes - * @sd: pointer to imx334 V4L2 sub-device structure - * @sd_state: V4L2 sub-device state - * @fsize: V4L2 sub-device size enumeration need to be filled - * - * Return: 0 if successful, error code otherwise. - */ static int imx334_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fsize) @@ -749,14 +721,6 @@ static void imx334_fill_pad_format(struct imx334 *imx334, fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; } -/** - * imx334_get_pad_format() - Get subdevice pad format - * @sd: pointer to imx334 V4L2 sub-device structure - * @sd_state: V4L2 sub-device state - * @fmt: V4L2 sub-device format need to be set - * - * Return: 0 if successful, error code otherwise. - */ static int imx334_get_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -776,14 +740,6 @@ static int imx334_get_pad_format(struct v4l2_subdev *sd, return 0; } -/** - * imx334_set_pad_format() - Set subdevice pad format - * @sd: pointer to imx334 V4L2 sub-device structure - * @sd_state: V4L2 sub-device state - * @fmt: V4L2 sub-device format need to be set - * - * Return: 0 if successful, error code otherwise. - */ static int imx334_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -815,13 +771,6 @@ static int imx334_set_pad_format(struct v4l2_subdev *sd, return ret; } -/** - * imx334_init_state() - Initialize sub-device state - * @sd: pointer to imx334 V4L2 sub-device structure - * @sd_state: V4L2 sub-device state - * - * Return: 0 if successful, error code otherwise. - */ static int imx334_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state) { @@ -856,15 +805,6 @@ static int imx334_set_framefmt(struct imx334 *imx334) return -EINVAL; } -/** - * imx334_enable_streams() - Enable specified streams for the sensor - * @sd: pointer to the V4L2 subdevice - * @state: pointer to the subdevice state - * @pad: pad number for which streams are enabled - * @streams_mask: bitmask specifying the streams to enable - * - * Return: 0 if successful, error code otherwise. - */ static int imx334_enable_streams(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, u32 pad, u64 streams_mask) @@ -929,15 +869,6 @@ err_rpm_put: return ret; } -/** - * imx334_disable_streams() - Enable specified streams for the sensor - * @sd: pointer to the V4L2 subdevice - * @state: pointer to the subdevice state - * @pad: pad number for which streams are disabled - * @streams_mask: bitmask specifying the streams to disable - * - * Return: 0 if successful, error code otherwise. - */ static int imx334_disable_streams(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, u32 pad, u64 streams_mask) @@ -1067,12 +998,6 @@ static const struct v4l2_subdev_internal_ops imx334_internal_ops = { .init_state = imx334_init_state, }; -/** - * imx334_power_on() - Sensor power on sequence - * @dev: pointer to i2c device - * - * Return: 0 if successful, error code otherwise. - */ static int imx334_power_on(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); @@ -1101,12 +1026,6 @@ error_reset: return ret; } -/** - * imx334_power_off() - Sensor power off sequence - * @dev: pointer to i2c device - * - * Return: 0 if successful, error code otherwise. - */ static int imx334_power_off(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); @@ -1206,12 +1125,6 @@ static int imx334_init_controls(struct imx334 *imx334) return 0; } -/** - * imx334_probe() - I2C client device binding - * @client: pointer to i2c client device - * - * Return: 0 if successful, error code otherwise. - */ static int imx334_probe(struct i2c_client *client) { struct imx334 *imx334; @@ -1311,12 +1224,6 @@ error_power_off: return ret; } -/** - * imx334_remove() - I2C client device unbinding - * @client: pointer to I2C client device - * - * Return: 0 if successful, error code otherwise. - */ static void imx334_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); diff --git a/drivers/media/i2c/imx335.c b/drivers/media/i2c/imx335.c index 5790aa4fabeb..1f777a1a8192 100644 --- a/drivers/media/i2c/imx335.c +++ b/drivers/media/i2c/imx335.c @@ -698,18 +698,6 @@ static int imx335_update_test_pattern(struct imx335 *imx335, u32 pattern_index) return ret; } -/** - * imx335_set_ctrl() - Set subdevice control - * @ctrl: pointer to v4l2_ctrl structure - * - * Supported controls: - * - V4L2_CID_VBLANK - * - cluster controls: - * - V4L2_CID_ANALOGUE_GAIN - * - V4L2_CID_EXPOSURE - * - * Return: 0 if successful, error code otherwise. - */ static int imx335_set_ctrl(struct v4l2_ctrl *ctrl) { struct imx335 *imx335 = @@ -800,14 +788,6 @@ static int imx335_get_format_code(struct imx335 *imx335, u32 code) return imx335_mbus_codes[0]; } -/** - * imx335_enum_mbus_code() - Enumerate V4L2 sub-device mbus codes - * @sd: pointer to imx335 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @code: V4L2 sub-device code enumeration need to be filled - * - * Return: 0 if successful, error code otherwise. - */ static int imx335_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) @@ -820,14 +800,6 @@ static int imx335_enum_mbus_code(struct v4l2_subdev *sd, return 0; } -/** - * imx335_enum_frame_size() - Enumerate V4L2 sub-device frame sizes - * @sd: pointer to imx335 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @fsize: V4L2 sub-device size enumeration need to be filled - * - * Return: 0 if successful, error code otherwise. - */ static int imx335_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fsize) @@ -871,14 +843,6 @@ static void imx335_fill_pad_format(struct imx335 *imx335, fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; } -/** - * imx335_set_pad_format() - Set subdevice pad format - * @sd: pointer to imx335 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @fmt: V4L2 sub-device format need to be set - * - * Return: 0 if successful, error code otherwise. - */ static int imx335_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -923,13 +887,6 @@ static int imx335_set_pad_format(struct v4l2_subdev *sd, return ret; } -/** - * imx335_init_state() - Initialize sub-device state - * @sd: pointer to imx335 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * - * Return: 0 if successful, error code otherwise. - */ static int imx335_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state) { @@ -947,14 +904,6 @@ static int imx335_init_state(struct v4l2_subdev *sd, return imx335_set_pad_format(sd, sd_state, &fmt); } -/** - * imx335_get_selection() - Selection API - * @sd: pointer to imx335 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @sel: V4L2 selection info - * - * Return: 0 if successful, error code otherwise. - */ static int imx335_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) @@ -1011,15 +960,6 @@ static int imx335_set_framefmt(struct imx335 *imx335) return ret; } -/** - * imx335_enable_streams() - Enable sensor streams - * @sd: V4L2 subdevice - * @state: V4L2 subdevice state - * @pad: The pad to enable - * @streams_mask: Bitmask of streams to enable - * - * Return: 0 if successful, error code otherwise. - */ static int imx335_enable_streams(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, u32 pad, u64 streams_mask) @@ -1097,15 +1037,6 @@ err_rpm_put: return ret; } -/** - * imx335_disable_streams() - Disable sensor streams - * @sd: V4L2 subdevice - * @state: V4L2 subdevice state - * @pad: The pad to disable - * @streams_mask: Bitmask of streams to disable - * - * Return: 0 if successful, error code otherwise. - */ static int imx335_disable_streams(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, u32 pad, u64 streams_mask) @@ -1299,12 +1230,6 @@ error_reset: return ret; } -/** - * imx335_power_off() - Sensor power off sequence - * @dev: pointer to i2c device - * - * Return: 0 if successful, error code otherwise. - */ static int imx335_power_off(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); @@ -1430,12 +1355,6 @@ static int imx335_init_controls(struct imx335 *imx335) return 0; } -/** - * imx335_probe() - I2C client device binding - * @client: pointer to i2c client device - * - * Return: 0 if successful, error code otherwise. - */ static int imx335_probe(struct i2c_client *client) { struct imx335 *imx335; @@ -1530,12 +1449,6 @@ error_power_off: return ret; } -/** - * imx335_remove() - I2C client device unbinding - * @client: pointer to I2C client device - * - * Return: 0 if successful, error code otherwise. - */ static void imx335_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); diff --git a/drivers/media/i2c/imx412.c b/drivers/media/i2c/imx412.c index e25e0a9ff65c..2705af2f16c0 100644 --- a/drivers/media/i2c/imx412.c +++ b/drivers/media/i2c/imx412.c @@ -570,18 +570,6 @@ error_release_group_hold: return ret; } -/** - * imx412_set_ctrl() - Set subdevice control - * @ctrl: pointer to v4l2_ctrl structure - * - * Supported controls: - * - V4L2_CID_VBLANK - * - cluster controls: - * - V4L2_CID_ANALOGUE_GAIN - * - V4L2_CID_EXPOSURE - * - * Return: 0 if successful, error code otherwise. - */ static int imx412_set_ctrl(struct v4l2_ctrl *ctrl) { struct imx412 *imx412 = @@ -634,14 +622,6 @@ static const struct v4l2_ctrl_ops imx412_ctrl_ops = { .s_ctrl = imx412_set_ctrl, }; -/** - * imx412_enum_mbus_code() - Enumerate V4L2 sub-device mbus codes - * @sd: pointer to imx412 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @code: V4L2 sub-device code enumeration need to be filled - * - * Return: 0 if successful, error code otherwise. - */ static int imx412_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) @@ -654,14 +634,6 @@ static int imx412_enum_mbus_code(struct v4l2_subdev *sd, return 0; } -/** - * imx412_enum_frame_size() - Enumerate V4L2 sub-device frame sizes - * @sd: pointer to imx412 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @fsize: V4L2 sub-device size enumeration need to be filled - * - * Return: 0 if successful, error code otherwise. - */ static int imx412_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fsize) @@ -701,14 +673,6 @@ static void imx412_fill_pad_format(struct imx412 *imx412, fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; } -/** - * imx412_get_pad_format() - Get subdevice pad format - * @sd: pointer to imx412 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @fmt: V4L2 sub-device format need to be set - * - * Return: 0 if successful, error code otherwise. - */ static int imx412_get_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -731,14 +695,6 @@ static int imx412_get_pad_format(struct v4l2_subdev *sd, return 0; } -/** - * imx412_set_pad_format() - Set subdevice pad format - * @sd: pointer to imx412 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @fmt: V4L2 sub-device format need to be set - * - * Return: 0 if successful, error code otherwise. - */ static int imx412_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -768,13 +724,6 @@ static int imx412_set_pad_format(struct v4l2_subdev *sd, return ret; } -/** - * imx412_init_state() - Initialize sub-device state - * @sd: pointer to imx412 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * - * Return: 0 if successful, error code otherwise. - */ static int imx412_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state) { @@ -840,13 +789,6 @@ static int imx412_stop_streaming(struct imx412 *imx412) 1, IMX412_MODE_STANDBY); } -/** - * imx412_set_stream() - Enable sensor streaming - * @sd: pointer to imx412 subdevice - * @enable: set to enable sensor streaming - * - * Return: 0 if successful, error code otherwise. - */ static int imx412_set_stream(struct v4l2_subdev *sd, int enable) { struct imx412 *imx412 = to_imx412(sd); @@ -1010,12 +952,6 @@ static const struct v4l2_subdev_internal_ops imx412_internal_ops = { .init_state = imx412_init_state, }; -/** - * imx412_power_on() - Sensor power on sequence - * @dev: pointer to i2c device - * - * Return: 0 if successful, error code otherwise. - */ static int imx412_power_on(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); @@ -1053,12 +989,6 @@ error_reset: return ret; } -/** - * imx412_power_off() - Sensor power off sequence - * @dev: pointer to i2c device - * - * Return: 0 if successful, error code otherwise. - */ static int imx412_power_off(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); @@ -1159,12 +1089,6 @@ static int imx412_init_controls(struct imx412 *imx412) return 0; } -/** - * imx412_probe() - I2C client device binding - * @client: pointer to i2c client device - * - * Return: 0 if successful, error code otherwise. - */ static int imx412_probe(struct i2c_client *client) { struct imx412 *imx412; @@ -1254,12 +1178,6 @@ error_mutex_destroy: return ret; } -/** - * imx412_remove() - I2C client device unbinding - * @client: pointer to I2C client device - * - * Return: 0 if successful, error code otherwise. - */ static void imx412_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c index 604745317004..f2bf2b354000 100644 --- a/drivers/media/i2c/ir-kbd-i2c.c +++ b/drivers/media/i2c/ir-kbd-i2c.c @@ -978,10 +978,10 @@ static void ir_remove(struct i2c_client *client) static const struct i2c_device_id ir_kbd_id[] = { /* Generic entry for any IR receiver */ - { "ir_video", 0 }, + { .name = "ir_video", .driver_data = 0 }, /* IR device specific entries should be added here */ - { "ir_z8f0811_haup", FLAG_TX }, - { "ir_z8f0811_hdpvr", FLAG_TX | FLAG_HDPVR }, + { .name = "ir_z8f0811_haup", .driver_data = FLAG_TX }, + { .name = "ir_z8f0811_hdpvr", .driver_data = FLAG_TX | FLAG_HDPVR }, { } }; MODULE_DEVICE_TABLE(i2c, ir_kbd_id); diff --git a/drivers/media/i2c/isl7998x.c b/drivers/media/i2c/isl7998x.c index 5ffd53e005ee..a77538d2343c 100644 --- a/drivers/media/i2c/isl7998x.c +++ b/drivers/media/i2c/isl7998x.c @@ -1561,7 +1561,7 @@ static const struct of_device_id isl7998x_of_match[] = { MODULE_DEVICE_TABLE(of, isl7998x_of_match); static const struct i2c_device_id isl7998x_id[] = { - { "isl79987" }, + { .name = "isl79987" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, isl7998x_id); diff --git a/drivers/media/i2c/ks0127.c b/drivers/media/i2c/ks0127.c index f3fba9179684..8c66be38adc3 100644 --- a/drivers/media/i2c/ks0127.c +++ b/drivers/media/i2c/ks0127.c @@ -677,9 +677,9 @@ static void ks0127_remove(struct i2c_client *client) } static const struct i2c_device_id ks0127_id[] = { - { "ks0127" }, - { "ks0127b" }, - { "ks0122s" }, + { .name = "ks0127" }, + { .name = "ks0127b" }, + { .name = "ks0122s" }, { } }; MODULE_DEVICE_TABLE(i2c, ks0127_id); diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c index f4cc844f4e3c..c3c90d830ee2 100644 --- a/drivers/media/i2c/lm3560.c +++ b/drivers/media/i2c/lm3560.c @@ -9,16 +9,22 @@ * Ldd-Mlp <ldd-mlp@list.ti.com> */ +#include <linux/bitmap.h> #include <linux/delay.h> #include <linux/module.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/slab.h> +#include <linux/mod_devicetable.h> #include <linux/mutex.h> +#include <linux/pm_runtime.h> +#include <linux/property.h> #include <linux/regmap.h> +#include <linux/regulator/consumer.h> #include <linux/videodev2.h> -#include <media/i2c/lm3560.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> +#include <media/v4l2-subdev.h> /* registers definitions */ #define REG_ENABLE 0x10 @@ -33,32 +39,83 @@ #define FAULT_OVERTEMP (1<<1) #define FAULT_SHORT_CIRCUIT (1<<2) +#define LM3560_FLASH_TOUT_MIN 32 +#define LM3560_FLASH_TOUT_STEP 32 +#define LM3560_FLASH_TOUT_MAX 1024 +#define LM3560_FLASH_TOUT_ms_TO_REG(a) \ + ((a) < LM3560_FLASH_TOUT_MIN ? 0 : \ + (((a) - LM3560_FLASH_TOUT_MIN) / LM3560_FLASH_TOUT_STEP)) +#define LM3560_FLASH_TOUT_REG_TO_ms(a) \ + ((a) * LM3560_FLASH_TOUT_STEP + LM3560_FLASH_TOUT_MIN) + +enum lm3560_led_id { + LM3560_LED0 = 0, + LM3560_LED1, + LM3560_LED_MAX +}; + +enum lm3560_peak_current { + LM3560_PEAK_1600mA = 0x00, + LM3560_PEAK_2300mA = 0x20, + LM3560_PEAK_3000mA = 0x40, + LM3560_PEAK_3600mA = 0x60 +}; + enum led_enable { MODE_SHDN = 0x0, MODE_TORCH = 0x2, MODE_FLASH = 0x3, }; +struct lm3560_flash_config { + u32 flash_brt_min_ua; + u32 flash_brt_max_ua; + u32 flash_brt_step_ua; + + u32 torch_brt_min_ua; + u32 torch_brt_max_ua; + u32 torch_brt_step_ua; +}; + /** * struct lm3560_flash * * @dev: pointer to &struct device - * @pdata: platform data * @regmap: reg. map for i2c * @lock: muxtex for serial access. + * @hwen_gpio: line connected to HWEN pin + * @vin_supply: line connected to IN supply (2.5V - 5.5V) * @led_mode: V4L2 LED mode * @ctrls_led: V4L2 controls * @subdev_led: V4L2 subdev + * @led_id: LED status holder + * @peak: peak current + * @max_flash_timeout: flash timeout + * @max_flash_brt: flash mode led brightness + * @max_torch_brt: torch mode led brightness + * @config: device specific current configuration */ struct lm3560_flash { struct device *dev; - struct lm3560_platform_data *pdata; struct regmap *regmap; struct mutex lock; + struct gpio_desc *hwen_gpio; + struct regulator *vin_supply; + enum v4l2_flash_led_mode led_mode; struct v4l2_ctrl_handler ctrls_led[LM3560_LED_MAX]; struct v4l2_subdev subdev_led[LM3560_LED_MAX]; + + DECLARE_BITMAP(led_id, LM3560_LED_MAX); + + enum lm3560_peak_current peak; + u32 max_flash_timeout; + + u32 max_flash_brt[LM3560_LED_MAX]; + u32 max_torch_brt[LM3560_LED_MAX]; + + const struct lm3560_flash_config *config; }; #define to_lm3560_flash(_ctrl, _no) \ @@ -114,15 +171,20 @@ static int lm3560_enable_ctrl(struct lm3560_flash *flash, static int lm3560_torch_brt_ctrl(struct lm3560_flash *flash, enum lm3560_led_id led_no, unsigned int brt) { + const struct lm3560_flash_config *config = flash->config; int rval; - u8 br_bits; + u32 br_bits; - if (brt < LM3560_TORCH_BRT_MIN) + if (brt < config->torch_brt_min_ua) return lm3560_enable_ctrl(flash, led_no, false); else rval = lm3560_enable_ctrl(flash, led_no, true); - br_bits = LM3560_TORCH_BRT_uA_TO_REG(brt); + br_bits = clamp(brt, config->torch_brt_min_ua, + config->torch_brt_max_ua); + br_bits = (br_bits - config->torch_brt_min_ua) / + config->torch_brt_step_ua; + if (led_no == LM3560_LED0) rval = regmap_update_bits(flash->regmap, REG_TORCH_BR, 0x07, br_bits); @@ -137,15 +199,20 @@ static int lm3560_torch_brt_ctrl(struct lm3560_flash *flash, static int lm3560_flash_brt_ctrl(struct lm3560_flash *flash, enum lm3560_led_id led_no, unsigned int brt) { + const struct lm3560_flash_config *config = flash->config; int rval; - u8 br_bits; + u32 br_bits; - if (brt < LM3560_FLASH_BRT_MIN) + if (brt < config->flash_brt_min_ua) return lm3560_enable_ctrl(flash, led_no, false); else rval = lm3560_enable_ctrl(flash, led_no, true); - br_bits = LM3560_FLASH_BRT_uA_TO_REG(brt); + br_bits = clamp(brt, config->flash_brt_min_ua, + config->flash_brt_max_ua); + br_bits = (br_bits - config->flash_brt_min_ua) / + config->flash_brt_step_ua; + if (led_no == LM3560_LED0) rval = regmap_update_bits(flash->regmap, REG_FLASH_BR, 0x0f, br_bits); @@ -162,14 +229,17 @@ static int lm3560_get_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no) struct lm3560_flash *flash = to_lm3560_flash(ctrl, led_no); int rval = -EINVAL; - mutex_lock(&flash->lock); + if (!pm_runtime_get_if_active(flash->dev)) + return 0; if (ctrl->id == V4L2_CID_FLASH_FAULT) { s32 fault = 0; unsigned int reg_val; rval = regmap_read(flash->regmap, REG_FLAG, ®_val); - if (rval < 0) - goto out; + if (rval < 0) { + pm_runtime_put(flash->dev); + return rval; + } if (reg_val & FAULT_SHORT_CIRCUIT) fault |= V4L2_FLASH_FAULT_SHORT_CIRCUIT; if (reg_val & FAULT_OVERTEMP) @@ -179,8 +249,8 @@ static int lm3560_get_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no) ctrl->cur.val = fault; } -out: - mutex_unlock(&flash->lock); + pm_runtime_put(flash->dev); + return rval; } @@ -190,7 +260,8 @@ static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no) u8 tout_bits; int rval = -EINVAL; - mutex_lock(&flash->lock); + if (!pm_runtime_get_if_active(flash->dev)) + return 0; switch (ctrl->id) { case V4L2_CID_FLASH_LED_MODE: @@ -202,14 +273,12 @@ static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no) case V4L2_CID_FLASH_STROBE_SOURCE: rval = regmap_update_bits(flash->regmap, REG_CONFIG1, 0x04, (ctrl->val) << 2); - if (rval < 0) - goto err_out; break; case V4L2_CID_FLASH_STROBE: if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) { rval = -EBUSY; - goto err_out; + break; } flash->led_mode = V4L2_FLASH_LED_MODE_FLASH; rval = lm3560_mode_ctrl(flash); @@ -218,7 +287,7 @@ static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no) case V4L2_CID_FLASH_STROBE_STOP: if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) { rval = -EBUSY; - goto err_out; + break; } flash->led_mode = V4L2_FLASH_LED_MODE_NONE; rval = lm3560_mode_ctrl(flash); @@ -239,8 +308,8 @@ static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no) break; } -err_out: - mutex_unlock(&flash->lock); + pm_runtime_put(flash->dev); + return rval; } @@ -279,10 +348,11 @@ static int lm3560_init_controls(struct lm3560_flash *flash, enum lm3560_led_id led_no) { struct v4l2_ctrl *fault; - u32 max_flash_brt = flash->pdata->max_flash_brt[led_no]; - u32 max_torch_brt = flash->pdata->max_torch_brt[led_no]; + u32 max_flash_brt = flash->max_flash_brt[led_no]; + u32 max_torch_brt = flash->max_torch_brt[led_no]; struct v4l2_ctrl_handler *hdl = &flash->ctrls_led[led_no]; const struct v4l2_ctrl_ops *ops = &lm3560_led_ctrl_ops[led_no]; + const struct lm3560_flash_config *config = flash->config; v4l2_ctrl_handler_init(hdl, 8); @@ -305,19 +375,19 @@ static int lm3560_init_controls(struct lm3560_flash *flash, /* flash strobe timeout */ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_TIMEOUT, LM3560_FLASH_TOUT_MIN, - flash->pdata->max_flash_timeout, + flash->max_flash_timeout, LM3560_FLASH_TOUT_STEP, - flash->pdata->max_flash_timeout); + flash->max_flash_timeout); /* flash brt */ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_INTENSITY, - LM3560_FLASH_BRT_MIN, max_flash_brt, - LM3560_FLASH_BRT_STEP, max_flash_brt); + config->flash_brt_min_ua, max_flash_brt, + config->flash_brt_step_ua, max_flash_brt); /* torch brt */ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_TORCH_INTENSITY, - LM3560_TORCH_BRT_MIN, max_torch_brt, - LM3560_TORCH_BRT_STEP, max_torch_brt); + config->torch_brt_min_ua, max_torch_brt, + config->torch_brt_step_ua, max_torch_brt); /* fault */ fault = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_FAULT, 0, @@ -328,6 +398,8 @@ static int lm3560_init_controls(struct lm3560_flash *flash, if (fault != NULL) fault->flags |= V4L2_CTRL_FLAG_VOLATILE; + hdl->lock = &flash->lock; + if (hdl->error) return hdl->error; @@ -347,15 +419,18 @@ static const struct regmap_config lm3560_regmap = { }; static int lm3560_subdev_init(struct lm3560_flash *flash, - enum lm3560_led_id led_no, char *led_name) + enum lm3560_led_id led_no, + struct fwnode_handle *fwnode) { struct i2c_client *client = to_i2c_client(flash->dev); int rval; v4l2_i2c_subdev_init(&flash->subdev_led[led_no], client, &lm3560_ops); flash->subdev_led[led_no].flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - strscpy(flash->subdev_led[led_no].name, led_name, - sizeof(flash->subdev_led[led_no].name)); + snprintf(flash->subdev_led[led_no].name, + sizeof(flash->subdev_led[led_no].name), + "lm3560-led%d", led_no); + flash->subdev_led[led_no].fwnode = fwnode_handle_get(fwnode); rval = lm3560_init_controls(flash, led_no); if (rval) goto err_out; @@ -363,9 +438,17 @@ static int lm3560_subdev_init(struct lm3560_flash *flash, if (rval < 0) goto err_out; flash->subdev_led[led_no].entity.function = MEDIA_ENT_F_FLASH; + flash->subdev_led[led_no].state_lock = &flash->lock; - return rval; + rval = v4l2_async_register_subdev(&flash->subdev_led[led_no]); + if (rval < 0) { + dev_err(flash->dev, "failed to register V4L2 subdev"); + goto error_out_media; + } + return rval; +error_out_media: + media_entity_cleanup(&flash->subdev_led[led_no].entity); err_out: v4l2_ctrl_handler_free(&flash->ctrls_led[led_no]); return rval; @@ -378,7 +461,7 @@ static int lm3560_init_device(struct lm3560_flash *flash) /* set peak current */ rval = regmap_update_bits(flash->regmap, - REG_FLASH_TOUT, 0x60, flash->pdata->peak); + REG_FLASH_TOUT, 0x60, flash->peak); if (rval < 0) return rval; /* output disable */ @@ -391,81 +474,247 @@ static int lm3560_init_device(struct lm3560_flash *flash) return rval; } +static int __maybe_unused lm3560_power_off(struct device *dev) +{ + struct lm3560_flash *flash = dev_get_drvdata(dev); + + gpiod_set_value_cansleep(flash->hwen_gpio, 0); + regulator_disable(flash->vin_supply); + + return 0; +} + +static int __maybe_unused lm3560_power_on(struct device *dev) +{ + struct lm3560_flash *flash = dev_get_drvdata(dev); + int rval; + + rval = regulator_enable(flash->vin_supply); + if (rval < 0) { + dev_err(flash->dev, "failed to enable vin power supply\n"); + return rval; + } + + gpiod_set_value_cansleep(flash->hwen_gpio, 1); + + rval = lm3560_init_device(flash); + if (rval < 0) { + lm3560_power_off(dev); + return rval; + } + + return 0; +} + +static void lm3560_subdev_cleanup(struct lm3560_flash *flash) +{ + int led_no; + + for_each_set_bit(led_no, flash->led_id, LM3560_LED_MAX) { + v4l2_async_unregister_subdev(&flash->subdev_led[led_no]); + v4l2_ctrl_handler_free(&flash->ctrls_led[led_no]); + media_entity_cleanup(&flash->subdev_led[led_no].entity); + } +} + static int lm3560_probe(struct i2c_client *client) { struct lm3560_flash *flash; - struct lm3560_platform_data *pdata = dev_get_platdata(&client->dev); + u32 peak_ua; int rval; flash = devm_kzalloc(&client->dev, sizeof(*flash), GFP_KERNEL); if (flash == NULL) return -ENOMEM; + flash->config = device_get_match_data(&client->dev); + if (!flash->config) + return -ENODEV; + flash->regmap = devm_regmap_init_i2c(client, &lm3560_regmap); if (IS_ERR(flash->regmap)) { rval = PTR_ERR(flash->regmap); return rval; } - /* if there is no platform data, use chip default value */ - if (pdata == NULL) { - pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); - if (pdata == NULL) - return -ENODEV; - pdata->peak = LM3560_PEAK_3600mA; - pdata->max_flash_timeout = LM3560_FLASH_TOUT_MAX; - /* led 1 */ - pdata->max_flash_brt[LM3560_LED0] = LM3560_FLASH_BRT_MAX; - pdata->max_torch_brt[LM3560_LED0] = LM3560_TORCH_BRT_MAX; - /* led 2 */ - pdata->max_flash_brt[LM3560_LED1] = LM3560_FLASH_BRT_MAX; - pdata->max_torch_brt[LM3560_LED1] = LM3560_TORCH_BRT_MAX; - } - flash->pdata = pdata; flash->dev = &client->dev; mutex_init(&flash->lock); - rval = lm3560_subdev_init(flash, LM3560_LED0, "lm3560-led0"); - if (rval < 0) - return rval; + bitmap_zero(flash->led_id, LM3560_LED_MAX); + + flash->hwen_gpio = devm_gpiod_get_optional(flash->dev, "enable", + GPIOD_OUT_LOW); + if (IS_ERR(flash->hwen_gpio)) + return dev_err_probe(flash->dev, PTR_ERR(flash->hwen_gpio), + "failed to get hwen gpio\n"); + + flash->vin_supply = devm_regulator_get(flash->dev, "vin"); + if (IS_ERR(flash->vin_supply)) + return dev_err_probe(flash->dev, PTR_ERR(flash->vin_supply), + "failed to get vin-supply\n"); + + flash->peak = LM3560_PEAK_1600mA; + rval = device_property_read_u32(flash->dev, + "ti,peak-current-microamp", &peak_ua); + if (!rval) { + /* + * LM3559 has lower peak current limit, but + * bit configuration matches LM3560. + * Correct current restrictions are enforced + * by the LM3560 schema. + */ + switch (peak_ua) { + case 1400000: + case 1600000: + flash->peak = LM3560_PEAK_1600mA; + break; + case 2100000: + case 2300000: + flash->peak = LM3560_PEAK_2300mA; + break; + case 2700000: + case 3000000: + flash->peak = LM3560_PEAK_3000mA; + break; + case 3200000: + case 3600000: + flash->peak = LM3560_PEAK_3600mA; + break; + default: + return -EINVAL; + } + } + + flash->max_flash_timeout = LM3560_FLASH_TOUT_MIN * 1000; + device_property_read_u32(flash->dev, "flash-max-timeout-us", + &flash->max_flash_timeout); + flash->max_flash_timeout /= 1000; - rval = lm3560_subdev_init(flash, LM3560_LED1, "lm3560-led1"); + rval = regulator_enable(flash->vin_supply); if (rval < 0) - return rval; + return dev_err_probe(flash->dev, rval, + "failed to enable vin power supply\n"); + + gpiod_set_value_cansleep(flash->hwen_gpio, 1); rval = lm3560_init_device(flash); if (rval < 0) - return rval; + goto error_disable; + + pm_runtime_set_active(flash->dev); + pm_runtime_enable(flash->dev); + + device_for_each_child_node_scoped(flash->dev, node) { + const struct lm3560_flash_config *config = flash->config; + u32 reg; + + rval = fwnode_property_read_u32(node, "reg", ®); + if (rval < 0) + /* We care only about nodes with reg property */ + continue; + + if (reg == LM3560_LED0 || reg == LM3560_LED1) { + flash->max_flash_brt[reg] = config->flash_brt_min_ua; + fwnode_property_read_u32(node, "flash-max-microamp", + &flash->max_flash_brt[reg]); + + flash->max_torch_brt[reg] = config->torch_brt_min_ua; + fwnode_property_read_u32(node, "led-max-microamp", + &flash->max_torch_brt[reg]); + + rval = lm3560_subdev_init(flash, reg, node); + if (rval < 0) { + dev_err(flash->dev, + "failed to register led%d: %d\n", + reg, rval); + goto error_clean; + } + + set_bit(reg, flash->led_id); + } + } i2c_set_clientdata(client, flash); + pm_runtime_set_autosuspend_delay(flash->dev, 1000); + pm_runtime_use_autosuspend(flash->dev); + pm_runtime_idle(flash->dev); + return 0; + +error_clean: + pm_runtime_disable(flash->dev); + pm_runtime_set_suspended(flash->dev); + + lm3560_subdev_cleanup(flash); + +error_disable: + gpiod_set_value_cansleep(flash->hwen_gpio, 0); + regulator_disable(flash->vin_supply); + + return rval; } static void lm3560_remove(struct i2c_client *client) { struct lm3560_flash *flash = i2c_get_clientdata(client); - unsigned int i; - for (i = LM3560_LED0; i < LM3560_LED_MAX; i++) { - v4l2_device_unregister_subdev(&flash->subdev_led[i]); - v4l2_ctrl_handler_free(&flash->ctrls_led[i]); - media_entity_cleanup(&flash->subdev_led[i].entity); + lm3560_subdev_cleanup(flash); + + /* + * Disable runtime PM. In case runtime PM is disabled in the kernel, + * make sure to turn power off manually. + */ + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) { + lm3560_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); } } -static const struct i2c_device_id lm3560_id_table[] = { - { LM3559_NAME }, - { LM3560_NAME }, - {} +static const struct dev_pm_ops lm3560_pm_ops = { + SET_RUNTIME_PM_OPS(lm3560_power_off, lm3560_power_on, NULL) }; +static const struct lm3560_flash_config lm3559_config = { + .flash_brt_min_ua = 56250, + .flash_brt_max_ua = 900000, + .flash_brt_step_ua = 56250, + + .torch_brt_min_ua = 28125, + .torch_brt_max_ua = 225000, + .torch_brt_step_ua = 28125, +}; + +static const struct lm3560_flash_config lm3560_config = { + .flash_brt_min_ua = 62500, + .flash_brt_max_ua = 1000000, + .flash_brt_step_ua = 62500, + + .torch_brt_min_ua = 31250, + .torch_brt_max_ua = 250000, + .torch_brt_step_ua = 31250, +}; + +static const struct of_device_id lm3560_of_match[] = { + { .compatible = "ti,lm3559", .data = &lm3559_config }, + { .compatible = "ti,lm3560", .data = &lm3560_config }, + { } +}; +MODULE_DEVICE_TABLE(of, lm3560_of_match); + +static const struct i2c_device_id lm3560_id_table[] = { + { .name = "lm3559", .driver_data = (kernel_ulong_t)&lm3559_config }, + { .name = "lm3560", .driver_data = (kernel_ulong_t)&lm3560_config }, + { } +}; MODULE_DEVICE_TABLE(i2c, lm3560_id_table); static struct i2c_driver lm3560_i2c_driver = { .driver = { - .name = LM3560_NAME, - .pm = NULL, + .name = "lm3560", + .pm = pm_ptr(&lm3560_pm_ops), + .of_match_table = lm3560_of_match, }, .probe = lm3560_probe, .remove = lm3560_remove, diff --git a/drivers/media/i2c/lm3646.c b/drivers/media/i2c/lm3646.c index 2d16e42ec224..9030cbe32afc 100644 --- a/drivers/media/i2c/lm3646.c +++ b/drivers/media/i2c/lm3646.c @@ -386,8 +386,8 @@ static void lm3646_remove(struct i2c_client *client) } static const struct i2c_device_id lm3646_id_table[] = { - { LM3646_NAME }, - {} + { .name = LM3646_NAME }, + { } }; MODULE_DEVICE_TABLE(i2c, lm3646_id_table); diff --git a/drivers/media/i2c/m52790.c b/drivers/media/i2c/m52790.c index 9e1ecfd01e2a..f3adf8c2b27f 100644 --- a/drivers/media/i2c/m52790.c +++ b/drivers/media/i2c/m52790.c @@ -163,7 +163,7 @@ static void m52790_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id m52790_id[] = { - { "m52790" }, + { .name = "m52790" }, { } }; MODULE_DEVICE_TABLE(i2c, m52790_id); diff --git a/drivers/media/i2c/max2175.c b/drivers/media/i2c/max2175.c index bf02ca23a284..1cc388b52902 100644 --- a/drivers/media/i2c/max2175.c +++ b/drivers/media/i2c/max2175.c @@ -1413,8 +1413,8 @@ static void max2175_remove(struct i2c_client *client) } static const struct i2c_device_id max2175_id[] = { - { DRIVER_NAME }, - {} + { .name = DRIVER_NAME }, + { } }; MODULE_DEVICE_TABLE(i2c, max2175_id); diff --git a/drivers/media/i2c/ml86v7667.c b/drivers/media/i2c/ml86v7667.c index 57ba3693649a..48b7d589df31 100644 --- a/drivers/media/i2c/ml86v7667.c +++ b/drivers/media/i2c/ml86v7667.c @@ -424,8 +424,8 @@ static void ml86v7667_remove(struct i2c_client *client) } static const struct i2c_device_id ml86v7667_id[] = { - { DRV_NAME }, - {} + { .name = DRV_NAME }, + { } }; MODULE_DEVICE_TABLE(i2c, ml86v7667_id); diff --git a/drivers/media/i2c/msp3400-driver.c b/drivers/media/i2c/msp3400-driver.c index 4c0b0ad68c08..413cfbc2dd94 100644 --- a/drivers/media/i2c/msp3400-driver.c +++ b/drivers/media/i2c/msp3400-driver.c @@ -874,7 +874,7 @@ static const struct dev_pm_ops msp3400_pm_ops = { }; static const struct i2c_device_id msp_id[] = { - { "msp3400" }, + { .name = "msp3400" }, { } }; MODULE_DEVICE_TABLE(i2c, msp_id); diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index 7a6114d18dfc..0ade967b357b 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -855,7 +855,7 @@ static void mt9m001_remove(struct i2c_client *client) } static const struct i2c_device_id mt9m001_id[] = { - { "mt9m001" }, + { .name = "mt9m001" }, { } }; MODULE_DEVICE_TABLE(i2c, mt9m001_id); diff --git a/drivers/media/i2c/mt9m111.c b/drivers/media/i2c/mt9m111.c index 3532c7c38bec..4e748080b798 100644 --- a/drivers/media/i2c/mt9m111.c +++ b/drivers/media/i2c/mt9m111.c @@ -1384,7 +1384,7 @@ static const struct of_device_id mt9m111_of_match[] = { MODULE_DEVICE_TABLE(of, mt9m111_of_match); static const struct i2c_device_id mt9m111_id[] = { - { "mt9m111" }, + { .name = "mt9m111" }, { } }; MODULE_DEVICE_TABLE(i2c, mt9m111_id); diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c index ea5d43d925ff..8dc57eeba606 100644 --- a/drivers/media/i2c/mt9p031.c +++ b/drivers/media/i2c/mt9p031.c @@ -796,7 +796,8 @@ static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl) data = (1 << 6) | (ctrl->val >> 1); } else { ctrl->val &= ~7; - data = ((ctrl->val - 64) << 5) | (1 << 6) | 32; + data = ((ctrl->val - 64) >> 3) & 0x7f; + data = (data << 8) | (1 << 6) | 32; } return mt9p031_write(client, MT9P031_GLOBAL_GAIN, data); diff --git a/drivers/media/i2c/mt9t112.c b/drivers/media/i2c/mt9t112.c index 2d2c840fc002..bd2268154ca7 100644 --- a/drivers/media/i2c/mt9t112.c +++ b/drivers/media/i2c/mt9t112.c @@ -1108,7 +1108,7 @@ static void mt9t112_remove(struct i2c_client *client) } static const struct i2c_device_id mt9t112_id[] = { - { "mt9t112" }, + { .name = "mt9t112" }, { } }; MODULE_DEVICE_TABLE(i2c, mt9t112_id); diff --git a/drivers/media/i2c/mt9v011.c b/drivers/media/i2c/mt9v011.c index 055b7915260a..985517f1cff7 100644 --- a/drivers/media/i2c/mt9v011.c +++ b/drivers/media/i2c/mt9v011.c @@ -582,7 +582,7 @@ static void mt9v011_remove(struct i2c_client *c) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id mt9v011_id[] = { - { "mt9v011" }, + { .name = "mt9v011" }, { } }; MODULE_DEVICE_TABLE(i2c, mt9v011_id); diff --git a/drivers/media/i2c/ov13858.c b/drivers/media/i2c/ov13858.c index 162b49046990..09d58e8b1c7f 100644 --- a/drivers/media/i2c/ov13858.c +++ b/drivers/media/i2c/ov13858.c @@ -1747,8 +1747,8 @@ static void ov13858_remove(struct i2c_client *client) } static const struct i2c_device_id ov13858_id_table[] = { - { "ov13858" }, - {} + { .name = "ov13858" }, + { } }; MODULE_DEVICE_TABLE(i2c, ov13858_id_table); diff --git a/drivers/media/i2c/ov13b10.c b/drivers/media/i2c/ov13b10.c index 5421874732bc..b0d34141a13a 100644 --- a/drivers/media/i2c/ov13b10.c +++ b/drivers/media/i2c/ov13b10.c @@ -8,6 +8,7 @@ #include <linux/i2c.h> #include <linux/module.h> #include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-fwnode.h> @@ -699,6 +700,12 @@ static const struct ov13b10_mode supported_2_lanes_modes[] = { }, }; +static const char * const ov13b10_supply_names[] = { + "dovdd", /* Digital I/O power */ + "avdd", /* Analog power */ + "dvdd", /* Digital core power */ +}; + struct ov13b10 { struct device *dev; @@ -708,7 +715,7 @@ struct ov13b10 { struct v4l2_ctrl_handler ctrl_handler; struct clk *img_clk; - struct regulator *avdd; + struct regulator_bulk_data supplies[ARRAY_SIZE(ov13b10_supply_names)]; struct gpio_desc *reset; /* V4L2 Controls */ @@ -1194,9 +1201,8 @@ static int ov13b10_power_off(struct device *dev) struct ov13b10 *ov13b10 = to_ov13b10(sd); gpiod_set_value_cansleep(ov13b10->reset, 1); - - if (ov13b10->avdd) - regulator_disable(ov13b10->avdd); + regulator_bulk_disable(ARRAY_SIZE(ov13b10_supply_names), + ov13b10->supplies); clk_disable_unprepare(ov13b10->img_clk); @@ -1214,14 +1220,12 @@ static int ov13b10_power_on(struct device *dev) dev_err(dev, "failed to enable imaging clock: %d", ret); return ret; } - - if (ov13b10->avdd) { - ret = regulator_enable(ov13b10->avdd); - if (ret < 0) { - dev_err(dev, "failed to enable avdd: %d", ret); - clk_disable_unprepare(ov13b10->img_clk); - return ret; - } + ret = regulator_bulk_enable(ARRAY_SIZE(ov13b10_supply_names), + ov13b10->supplies); + if (ret < 0) { + dev_err(dev, "failed to enable regulators\n"); + clk_disable_unprepare(ov13b10->img_clk); + return ret; } gpiod_set_value_cansleep(ov13b10->reset, 0); @@ -1475,7 +1479,8 @@ static int ov13b10_get_pm_resources(struct ov13b10 *ov13b) unsigned long freq; int ret; - ov13b->reset = devm_gpiod_get_optional(ov13b->dev, "reset", GPIOD_OUT_LOW); + ov13b->reset = devm_gpiod_get_optional(ov13b->dev, "reset", + GPIOD_OUT_LOW); if (IS_ERR(ov13b->reset)) return dev_err_probe(ov13b->dev, PTR_ERR(ov13b->reset), "failed to get reset gpio\n"); @@ -1491,15 +1496,15 @@ static int ov13b10_get_pm_resources(struct ov13b10 *ov13b) "external clock %lu is not supported\n", freq); - ov13b->avdd = devm_regulator_get_optional(ov13b->dev, "avdd"); - if (IS_ERR(ov13b->avdd)) { - ret = PTR_ERR(ov13b->avdd); - ov13b->avdd = NULL; - if (ret != -ENODEV) - return dev_err_probe(ov13b->dev, ret, - "failed to get avdd regulator\n"); - } + for (unsigned int i = 0; i < ARRAY_SIZE(ov13b10_supply_names); i++) + ov13b->supplies[i].supply = ov13b10_supply_names[i]; + ret = devm_regulator_bulk_get(ov13b->dev, + ARRAY_SIZE(ov13b10_supply_names), + ov13b->supplies); + if (ret) + return dev_err_probe(ov13b->dev, ret, + "failed to get regulators\n"); return 0; } diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c index d27fc2df64e6..50feb608b92b 100644 --- a/drivers/media/i2c/ov2640.c +++ b/drivers/media/i2c/ov2640.c @@ -1271,7 +1271,7 @@ static void ov2640_remove(struct i2c_client *client) } static const struct i2c_device_id ov2640_id[] = { - { "ov2640" }, + { .name = "ov2640" }, { } }; MODULE_DEVICE_TABLE(i2c, ov2640_id); diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c index 061401b020fc..7d8c7c3465a4 100644 --- a/drivers/media/i2c/ov2659.c +++ b/drivers/media/i2c/ov2659.c @@ -1553,7 +1553,7 @@ static const struct dev_pm_ops ov2659_pm_ops = { }; static const struct i2c_device_id ov2659_id[] = { - { "ov2659" }, + { .name = "ov2659" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, ov2659_id); diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 85ecc23b3587..92d2d6cd4ba4 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -3999,8 +3999,8 @@ static const struct dev_pm_ops ov5640_pm_ops = { }; static const struct i2c_device_id ov5640_id[] = { - { "ov5640" }, - {} + { .name = "ov5640" }, + { } }; MODULE_DEVICE_TABLE(i2c, ov5640_id); diff --git a/drivers/media/i2c/ov5645.c b/drivers/media/i2c/ov5645.c index b10d408034a1..c772ef6e51d2 100644 --- a/drivers/media/i2c/ov5645.c +++ b/drivers/media/i2c/ov5645.c @@ -1219,8 +1219,8 @@ static void ov5645_remove(struct i2c_client *client) } static const struct i2c_device_id ov5645_id[] = { - { "ov5645" }, - {} + { .name = "ov5645" }, + { } }; MODULE_DEVICE_TABLE(i2c, ov5645_id); diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index db9bd2892140..3facf92b3841 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -1278,7 +1278,7 @@ static const struct dev_pm_ops ov5647_pm_ops = { }; static const struct i2c_device_id ov5647_id[] = { - { "ov5647" }, + { .name = "ov5647" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, ov5647_id); diff --git a/drivers/media/i2c/ov7640.c b/drivers/media/i2c/ov7640.c index 9f68d89936eb..0fd90bc67e29 100644 --- a/drivers/media/i2c/ov7640.c +++ b/drivers/media/i2c/ov7640.c @@ -77,7 +77,7 @@ static void ov7640_remove(struct i2c_client *client) } static const struct i2c_device_id ov7640_id[] = { - { "ov7640" }, + { .name = "ov7640" }, { } }; MODULE_DEVICE_TABLE(i2c, ov7640_id); diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 0cb96b6c9990..b6d238ba0d53 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -1997,8 +1997,8 @@ static const struct ov7670_devtype ov7675_devdata = { }; static const struct i2c_device_id ov7670_id[] = { - { "ov7670", (kernel_ulong_t)&ov7670_devdata }, - { "ov7675", (kernel_ulong_t)&ov7675_devdata }, + { .name = "ov7670", .driver_data = (kernel_ulong_t)&ov7670_devdata }, + { .name = "ov7675", .driver_data = (kernel_ulong_t)&ov7675_devdata }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, ov7670_id); diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c index 062e1023a411..be3ba284ee0b 100644 --- a/drivers/media/i2c/ov772x.c +++ b/drivers/media/i2c/ov772x.c @@ -1546,7 +1546,7 @@ static void ov772x_remove(struct i2c_client *client) } static const struct i2c_device_id ov772x_id[] = { - { "ov772x" }, + { .name = "ov772x" }, { } }; MODULE_DEVICE_TABLE(i2c, ov772x_id); diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c index 632fb80469be..c2e02f191816 100644 --- a/drivers/media/i2c/ov7740.c +++ b/drivers/media/i2c/ov7740.c @@ -1149,7 +1149,7 @@ static int __maybe_unused ov7740_runtime_resume(struct device *dev) } static const struct i2c_device_id ov7740_id[] = { - { "ov7740" }, + { .name = "ov7740" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, ov7740_id); diff --git a/drivers/media/i2c/ov9282.c b/drivers/media/i2c/ov9282.c index 2167fb73ea41..5b6f897a74fc 100644 --- a/drivers/media/i2c/ov9282.c +++ b/drivers/media/i2c/ov9282.c @@ -586,18 +586,6 @@ static u32 ov9282_flash_duration_to_us(struct ov9282 *ov9282, u32 value) return DIV_ROUND_UP(value * frame_width, OV9282_STROBE_SPAN_FACTOR); } -/** - * ov9282_set_ctrl() - Set subdevice control - * @ctrl: pointer to v4l2_ctrl structure - * - * Supported controls: - * - V4L2_CID_VBLANK - * - cluster controls: - * - V4L2_CID_ANALOGUE_GAIN - * - V4L2_CID_EXPOSURE - * - * Return: 0 if successful, error code otherwise. - */ static int ov9282_set_ctrl(struct v4l2_ctrl *ctrl) { struct ov9282 *ov9282 = @@ -704,14 +692,6 @@ static const struct v4l2_ctrl_ops ov9282_ctrl_ops = { .try_ctrl = ov9282_try_ctrl, }; -/** - * ov9282_enum_mbus_code() - Enumerate V4L2 sub-device mbus codes - * @sd: pointer to ov9282 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @code: V4L2 sub-device code enumeration need to be filled - * - * Return: 0 if successful, error code otherwise. - */ static int ov9282_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) @@ -780,14 +760,6 @@ static void ov9282_fill_pad_format(struct ov9282 *ov9282, fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; } -/** - * ov9282_get_pad_format() - Get subdevice pad format - * @sd: pointer to ov9282 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @fmt: V4L2 sub-device format need to be set - * - * Return: 0 if successful, error code otherwise. - */ static int ov9282_get_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -807,14 +779,6 @@ static int ov9282_get_pad_format(struct v4l2_subdev *sd, return 0; } -/** - * ov9282_set_pad_format() - Set subdevice pad format - * @sd: pointer to ov9282 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @fmt: V4L2 sub-device format need to be set - * - * Return: 0 if successful, error code otherwise. - */ static int ov9282_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -852,13 +816,6 @@ static int ov9282_set_pad_format(struct v4l2_subdev *sd, return ret; } -/** - * ov9282_init_state() - Initialize sub-device state - * @sd: pointer to ov9282 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * - * Return: 0 if successful, error code otherwise. - */ static int ov9282_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state) { @@ -1157,12 +1114,6 @@ static const struct v4l2_subdev_internal_ops ov9282_internal_ops = { .init_state = ov9282_init_state, }; -/** - * ov9282_power_on() - Sensor power on sequence - * @dev: pointer to i2c device - * - * Return: 0 if successful, error code otherwise. - */ static int ov9282_power_on(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); @@ -1206,12 +1157,6 @@ error_reset: return ret; } -/** - * ov9282_power_off() - Sensor power off sequence - * @dev: pointer to i2c device - * - * Return: 0 if successful, error code otherwise. - */ static int ov9282_power_off(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); @@ -1333,12 +1278,6 @@ static int ov9282_init_controls(struct ov9282 *ov9282) return 0; } -/** - * ov9282_probe() - I2C client device binding - * @client: pointer to i2c client device - * - * Return: 0 if successful, error code otherwise. - */ static int ov9282_probe(struct i2c_client *client) { struct ov9282 *ov9282; @@ -1435,12 +1374,6 @@ error_power_off: return ret; } -/** - * ov9282_remove() - I2C client device unbinding - * @client: pointer to I2C client device - * - * Return: 0 if successful, error code otherwise. - */ static void ov9282_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c index 2190c52b1433..122f411044ce 100644 --- a/drivers/media/i2c/ov9640.c +++ b/drivers/media/i2c/ov9640.c @@ -752,7 +752,7 @@ static void ov9640_remove(struct i2c_client *client) } static const struct i2c_device_id ov9640_id[] = { - { "ov9640" }, + { .name = "ov9640" }, { } }; MODULE_DEVICE_TABLE(i2c, ov9640_id); diff --git a/drivers/media/i2c/ov9650.c b/drivers/media/i2c/ov9650.c index c94e8fe29f22..5c85db8a4a38 100644 --- a/drivers/media/i2c/ov9650.c +++ b/drivers/media/i2c/ov9650.c @@ -1567,8 +1567,8 @@ static void ov965x_remove(struct i2c_client *client) } static const struct i2c_device_id ov965x_id[] = { - { "OV9650" }, - { "OV9652" }, + { .name = "OV9650" }, + { .name = "OV9652" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, ov965x_id); diff --git a/drivers/media/i2c/rj54n1cb0c.c b/drivers/media/i2c/rj54n1cb0c.c index e95342d706c3..23352d71a108 100644 --- a/drivers/media/i2c/rj54n1cb0c.c +++ b/drivers/media/i2c/rj54n1cb0c.c @@ -1413,7 +1413,7 @@ static void rj54n1_remove(struct i2c_client *client) } static const struct i2c_device_id rj54n1_id[] = { - { "rj54n1cb0c" }, + { .name = "rj54n1cb0c" }, { } }; MODULE_DEVICE_TABLE(i2c, rj54n1_id); diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c index ab31ee2b596b..551387cea521 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -1728,7 +1728,7 @@ static void s5c73m3_remove(struct i2c_client *client) } static const struct i2c_device_id s5c73m3_id[] = { - { DRIVER_NAME }, + { .name = DRIVER_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, s5c73m3_id); diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c index d1d00eca8708..378d273055ee 100644 --- a/drivers/media/i2c/s5k5baf.c +++ b/drivers/media/i2c/s5k5baf.c @@ -2007,7 +2007,7 @@ static void s5k5baf_remove(struct i2c_client *c) } static const struct i2c_device_id s5k5baf_id[] = { - { S5K5BAF_DRIVER_NAME }, + { .name = S5K5BAF_DRIVER_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, s5k5baf_id); diff --git a/drivers/media/i2c/saa6588.c b/drivers/media/i2c/saa6588.c index 90ae4121a68a..56bfa3d2604b 100644 --- a/drivers/media/i2c/saa6588.c +++ b/drivers/media/i2c/saa6588.c @@ -495,7 +495,7 @@ static void saa6588_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id saa6588_id[] = { - { "saa6588" }, + { .name = "saa6588" }, { } }; MODULE_DEVICE_TABLE(i2c, saa6588_id); diff --git a/drivers/media/i2c/saa6752hs.c b/drivers/media/i2c/saa6752hs.c index 1c0031ba43b4..c6bf0b0902e8 100644 --- a/drivers/media/i2c/saa6752hs.c +++ b/drivers/media/i2c/saa6752hs.c @@ -770,7 +770,7 @@ static void saa6752hs_remove(struct i2c_client *client) } static const struct i2c_device_id saa6752hs_id[] = { - { "saa6752hs" }, + { .name = "saa6752hs" }, { } }; MODULE_DEVICE_TABLE(i2c, saa6752hs_id); diff --git a/drivers/media/i2c/saa7110.c b/drivers/media/i2c/saa7110.c index 942aeeb40c52..652058b8f766 100644 --- a/drivers/media/i2c/saa7110.c +++ b/drivers/media/i2c/saa7110.c @@ -439,7 +439,7 @@ static void saa7110_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id saa7110_id[] = { - { "saa7110" }, + { .name = "saa7110" }, { } }; MODULE_DEVICE_TABLE(i2c, saa7110_id); diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c index 48d6730d9271..7cce90750c93 100644 --- a/drivers/media/i2c/saa7115.c +++ b/drivers/media/i2c/saa7115.c @@ -1928,13 +1928,13 @@ static void saa711x_remove(struct i2c_client *client) } static const struct i2c_device_id saa711x_id[] = { - { "saa7115_auto", 1 }, /* autodetect */ - { "saa7111", 0 }, - { "saa7113", 0 }, - { "saa7114", 0 }, - { "saa7115", 0 }, - { "saa7118", 0 }, - { "gm7113c", 0 }, + { .name = "saa7115_auto", .driver_data = 1 }, /* autodetect */ + { .name = "saa7111", .driver_data = 0 }, + { .name = "saa7113", .driver_data = 0 }, + { .name = "saa7114", .driver_data = 0 }, + { .name = "saa7115", .driver_data = 0 }, + { .name = "saa7118", .driver_data = 0 }, + { .name = "gm7113c", .driver_data = 0 }, { } }; MODULE_DEVICE_TABLE(i2c, saa711x_id); diff --git a/drivers/media/i2c/saa7127.c b/drivers/media/i2c/saa7127.c index a42a7ffe3768..fdf17f6fae67 100644 --- a/drivers/media/i2c/saa7127.c +++ b/drivers/media/i2c/saa7127.c @@ -797,11 +797,11 @@ static void saa7127_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id saa7127_id[] = { - { "saa7127_auto", 0 }, /* auto-detection */ - { "saa7126", SAA7127 }, - { "saa7127", SAA7127 }, - { "saa7128", SAA7129 }, - { "saa7129", SAA7129 }, + { .name = "saa7127_auto", .driver_data = 0 }, /* auto-detection */ + { .name = "saa7126", .driver_data = SAA7127 }, + { .name = "saa7127", .driver_data = SAA7127 }, + { .name = "saa7128", .driver_data = SAA7129 }, + { .name = "saa7129", .driver_data = SAA7129 }, { } }; MODULE_DEVICE_TABLE(i2c, saa7127_id); diff --git a/drivers/media/i2c/saa717x.c b/drivers/media/i2c/saa717x.c index 713331be947c..0536ceb54650 100644 --- a/drivers/media/i2c/saa717x.c +++ b/drivers/media/i2c/saa717x.c @@ -1334,7 +1334,7 @@ static void saa717x_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id saa717x_id[] = { - { "saa717x" }, + { .name = "saa717x" }, { } }; MODULE_DEVICE_TABLE(i2c, saa717x_id); diff --git a/drivers/media/i2c/saa7185.c b/drivers/media/i2c/saa7185.c index c04e452a332b..8e5c0eab907d 100644 --- a/drivers/media/i2c/saa7185.c +++ b/drivers/media/i2c/saa7185.c @@ -334,7 +334,7 @@ static void saa7185_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id saa7185_id[] = { - { "saa7185" }, + { .name = "saa7185" }, { } }; MODULE_DEVICE_TABLE(i2c, saa7185_id); diff --git a/drivers/media/i2c/sony-btf-mpx.c b/drivers/media/i2c/sony-btf-mpx.c index 16072a9f8247..4c87de0fe1f0 100644 --- a/drivers/media/i2c/sony-btf-mpx.c +++ b/drivers/media/i2c/sony-btf-mpx.c @@ -366,7 +366,7 @@ static void sony_btf_mpx_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id sony_btf_mpx_id[] = { - { "sony-btf-mpx" }, + { .name = "sony-btf-mpx" }, { } }; MODULE_DEVICE_TABLE(i2c, sony_btf_mpx_id); diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index a0ca19359c43..fbd38bbfee03 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -2358,8 +2358,8 @@ static void tc358743_remove(struct i2c_client *client) } static const struct i2c_device_id tc358743_id[] = { - { "tc358743" }, - {} + { .name = "tc358743" }, + { } }; MODULE_DEVICE_TABLE(i2c, tc358743_id); diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c index 5c6dda5338f5..afa1d6f34c9c 100644 --- a/drivers/media/i2c/tda1997x.c +++ b/drivers/media/i2c/tda1997x.c @@ -2274,9 +2274,9 @@ static int tda1997x_set_power(struct tda1997x_state *state, bool on) } static const struct i2c_device_id tda1997x_i2c_id[] = { - {"tda19971", (kernel_ulong_t)&tda1997x_chip_info[TDA19971]}, - {"tda19973", (kernel_ulong_t)&tda1997x_chip_info[TDA19973]}, - { }, + { .name = "tda19971", .driver_data = (kernel_ulong_t)&tda1997x_chip_info[TDA19971] }, + { .name = "tda19973", .driver_data = (kernel_ulong_t)&tda1997x_chip_info[TDA19973] }, + { } }; MODULE_DEVICE_TABLE(i2c, tda1997x_i2c_id); diff --git a/drivers/media/i2c/tda7432.c b/drivers/media/i2c/tda7432.c index 76ef0fdddf76..a0b65a595ba8 100644 --- a/drivers/media/i2c/tda7432.c +++ b/drivers/media/i2c/tda7432.c @@ -400,7 +400,7 @@ static void tda7432_remove(struct i2c_client *client) } static const struct i2c_device_id tda7432_id[] = { - { "tda7432" }, + { .name = "tda7432" }, { } }; MODULE_DEVICE_TABLE(i2c, tda7432_id); diff --git a/drivers/media/i2c/tda9840.c b/drivers/media/i2c/tda9840.c index e3b266db571f..b34d992777fb 100644 --- a/drivers/media/i2c/tda9840.c +++ b/drivers/media/i2c/tda9840.c @@ -182,7 +182,7 @@ static void tda9840_remove(struct i2c_client *client) } static const struct i2c_device_id tda9840_id[] = { - { "tda9840" }, + { .name = "tda9840" }, { } }; MODULE_DEVICE_TABLE(i2c, tda9840_id); diff --git a/drivers/media/i2c/tea6415c.c b/drivers/media/i2c/tea6415c.c index 0cd2e6c52e20..ea7730a7ee2c 100644 --- a/drivers/media/i2c/tea6415c.c +++ b/drivers/media/i2c/tea6415c.c @@ -141,7 +141,7 @@ static void tea6415c_remove(struct i2c_client *client) } static const struct i2c_device_id tea6415c_id[] = { - { "tea6415c" }, + { .name = "tea6415c" }, { } }; MODULE_DEVICE_TABLE(i2c, tea6415c_id); diff --git a/drivers/media/i2c/tea6420.c b/drivers/media/i2c/tea6420.c index 400883fc0c0f..bebb9a8095db 100644 --- a/drivers/media/i2c/tea6420.c +++ b/drivers/media/i2c/tea6420.c @@ -123,7 +123,7 @@ static void tea6420_remove(struct i2c_client *client) } static const struct i2c_device_id tea6420_id[] = { - { "tea6420" }, + { .name = "tea6420" }, { } }; MODULE_DEVICE_TABLE(i2c, tea6420_id); diff --git a/drivers/media/i2c/ths7303.c b/drivers/media/i2c/ths7303.c index ff268ebeb4d9..fa5b9c884c1d 100644 --- a/drivers/media/i2c/ths7303.c +++ b/drivers/media/i2c/ths7303.c @@ -369,9 +369,9 @@ static void ths7303_remove(struct i2c_client *client) } static const struct i2c_device_id ths7303_id[] = { - { "ths7303" }, - { "ths7353" }, - {} + { .name = "ths7303" }, + { .name = "ths7353" }, + { } }; MODULE_DEVICE_TABLE(i2c, ths7303_id); diff --git a/drivers/media/i2c/ths8200.c b/drivers/media/i2c/ths8200.c index 686f10641c7a..808ef16ec3b3 100644 --- a/drivers/media/i2c/ths8200.c +++ b/drivers/media/i2c/ths8200.c @@ -487,8 +487,8 @@ static void ths8200_remove(struct i2c_client *client) } static const struct i2c_device_id ths8200_id[] = { - { "ths8200" }, - {} + { .name = "ths8200" }, + { } }; MODULE_DEVICE_TABLE(i2c, ths8200_id); diff --git a/drivers/media/i2c/tlv320aic23b.c b/drivers/media/i2c/tlv320aic23b.c index 6f6bc5236565..7deeef317714 100644 --- a/drivers/media/i2c/tlv320aic23b.c +++ b/drivers/media/i2c/tlv320aic23b.c @@ -188,7 +188,7 @@ static void tlv320aic23b_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id tlv320aic23b_id[] = { - { "tlv320aic23b" }, + { .name = "tlv320aic23b" }, { } }; MODULE_DEVICE_TABLE(i2c, tlv320aic23b_id); diff --git a/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c index 6267e9ad39c0..f136310b899d 100644 --- a/drivers/media/i2c/tvaudio.c +++ b/drivers/media/i2c/tvaudio.c @@ -2086,7 +2086,7 @@ static void tvaudio_remove(struct i2c_client *client) detect which device is present. So rather than listing all supported devices here, we pretend to support a single, fake device type. */ static const struct i2c_device_id tvaudio_id[] = { - { "tvaudio" }, + { .name = "tvaudio" }, { } }; MODULE_DEVICE_TABLE(i2c, tvaudio_id); diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c index f9c9c80c33ac..376eecb0b673 100644 --- a/drivers/media/i2c/tvp514x.c +++ b/drivers/media/i2c/tvp514x.c @@ -686,13 +686,6 @@ static int tvp514x_s_routing(struct v4l2_subdev *sd, return 0; } -/** - * tvp514x_s_ctrl() - V4L2 decoder interface handler for s_ctrl - * @ctrl: pointer to v4l2_ctrl structure - * - * If the requested control is supported, sets the control's current - * value in HW. Otherwise, returns -EINVAL if the control is not supported. - */ static int tvp514x_s_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = to_sd(ctrl); @@ -789,13 +782,6 @@ tvp514x_set_frame_interval(struct v4l2_subdev *sd, return 0; } -/** - * tvp514x_s_stream() - V4L2 decoder i/f handler for s_stream - * @sd: pointer to standard V4L2 sub-device structure - * @enable: streaming enable or disable - * - * Sets streaming to enable or disable, if possible. - */ static int tvp514x_s_stream(struct v4l2_subdev *sd, int enable) { int err = 0; @@ -850,14 +836,6 @@ static const struct v4l2_ctrl_ops tvp514x_ctrl_ops = { .s_ctrl = tvp514x_s_ctrl, }; -/** - * tvp514x_enum_mbus_code() - V4L2 decoder interface handler for enum_mbus_code - * @sd: pointer to standard V4L2 sub-device structure - * @sd_state: subdev state - * @code: pointer to v4l2_subdev_mbus_code_enum structure - * - * Enumertaes mbus codes supported - */ static int tvp514x_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) @@ -877,14 +855,6 @@ static int tvp514x_enum_mbus_code(struct v4l2_subdev *sd, return 0; } -/** - * tvp514x_get_pad_format() - V4L2 decoder interface handler for get pad format - * @sd: pointer to standard V4L2 sub-device structure - * @sd_state: subdev state - * @format: pointer to v4l2_subdev_format structure - * - * Retrieves pad format which is active or tried based on requirement - */ static int tvp514x_get_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) @@ -909,14 +879,6 @@ static int tvp514x_get_pad_format(struct v4l2_subdev *sd, return 0; } -/** - * tvp514x_set_pad_format() - V4L2 decoder interface handler for set pad format - * @sd: pointer to standard V4L2 sub-device structure - * @sd_state: subdev state - * @fmt: pointer to v4l2_subdev_format structure - * - * Set pad format for the output pad - */ static int tvp514x_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -1014,15 +976,7 @@ done: return pdata; } -/** - * tvp514x_probe() - decoder driver i2c probe handler - * @client: i2c driver client device structure - * - * Register decoder as an i2c client device and V4L2 - * device. - */ -static int -tvp514x_probe(struct i2c_client *client) +static int tvp514x_probe(struct i2c_client *client) { struct tvp514x_platform_data *pdata = tvp514x_get_pdata(client); struct tvp514x_decoder *decoder; @@ -1113,13 +1067,6 @@ done: return ret; } -/** - * tvp514x_remove() - decoder driver i2c remove handler - * @client: i2c driver client device structure - * - * Unregister decoder as an i2c client device and V4L2 - * device. Complement of tvp514x_probe(). - */ static void tvp514x_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); @@ -1182,10 +1129,10 @@ static const struct tvp514x_reg tvp514xm_init_reg_seq[] = { * driver_data - Driver data */ static const struct i2c_device_id tvp514x_id[] = { - {"tvp5146", (kernel_ulong_t)tvp5146_init_reg_seq }, - {"tvp5146m2", (kernel_ulong_t)tvp514xm_init_reg_seq }, - {"tvp5147", (kernel_ulong_t)tvp5147_init_reg_seq }, - {"tvp5147m1", (kernel_ulong_t)tvp514xm_init_reg_seq }, + { .name = "tvp5146", .driver_data = (kernel_ulong_t)tvp5146_init_reg_seq }, + { .name = "tvp5146m2", .driver_data = (kernel_ulong_t)tvp514xm_init_reg_seq }, + { .name = "tvp5147", .driver_data = (kernel_ulong_t)tvp5147_init_reg_seq }, + { .name = "tvp5147m1", .driver_data = (kernel_ulong_t)tvp514xm_init_reg_seq }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, tvp514x_id); diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index e3675c744d9e..9c204f38935d 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -2265,7 +2265,7 @@ static const struct dev_pm_ops tvp5150_pm_ops = { }; static const struct i2c_device_id tvp5150_id[] = { - { "tvp5150" }, + { .name = "tvp5150" }, { } }; MODULE_DEVICE_TABLE(i2c, tvp5150_id); diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c index c09a5bd71fd0..3979ccde5a95 100644 --- a/drivers/media/i2c/tvp7002.c +++ b/drivers/media/i2c/tvp7002.c @@ -1070,7 +1070,7 @@ static void tvp7002_remove(struct i2c_client *c) /* I2C Device ID table */ static const struct i2c_device_id tvp7002_id[] = { - { "tvp7002" }, + { .name = "tvp7002" }, { } }; MODULE_DEVICE_TABLE(i2c, tvp7002_id); diff --git a/drivers/media/i2c/tw2804.c b/drivers/media/i2c/tw2804.c index 3d154f4fb5f9..713e078ff3da 100644 --- a/drivers/media/i2c/tw2804.c +++ b/drivers/media/i2c/tw2804.c @@ -414,7 +414,7 @@ static void tw2804_remove(struct i2c_client *client) } static const struct i2c_device_id tw2804_id[] = { - { "tw2804" }, + { .name = "tw2804" }, { } }; MODULE_DEVICE_TABLE(i2c, tw2804_id); diff --git a/drivers/media/i2c/tw9900.c b/drivers/media/i2c/tw9900.c index 53efdeaed1db..617fcf7f0b45 100644 --- a/drivers/media/i2c/tw9900.c +++ b/drivers/media/i2c/tw9900.c @@ -753,7 +753,7 @@ static const struct dev_pm_ops tw9900_pm_ops = { }; static const struct i2c_device_id tw9900_id[] = { - { "tw9900" }, + { .name = "tw9900" }, { } }; MODULE_DEVICE_TABLE(i2c, tw9900_id); diff --git a/drivers/media/i2c/tw9903.c b/drivers/media/i2c/tw9903.c index c3eafd5d5dc8..db063b885b09 100644 --- a/drivers/media/i2c/tw9903.c +++ b/drivers/media/i2c/tw9903.c @@ -246,7 +246,7 @@ static void tw9903_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id tw9903_id[] = { - { "tw9903" }, + { .name = "tw9903" }, { } }; MODULE_DEVICE_TABLE(i2c, tw9903_id); diff --git a/drivers/media/i2c/tw9906.c b/drivers/media/i2c/tw9906.c index 0ab43fe42d7f..079e2f49f38f 100644 --- a/drivers/media/i2c/tw9906.c +++ b/drivers/media/i2c/tw9906.c @@ -214,7 +214,7 @@ static void tw9906_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id tw9906_id[] = { - { "tw9906" }, + { .name = "tw9906" }, { } }; MODULE_DEVICE_TABLE(i2c, tw9906_id); diff --git a/drivers/media/i2c/tw9910.c b/drivers/media/i2c/tw9910.c index f3e400304e04..872207e688bf 100644 --- a/drivers/media/i2c/tw9910.c +++ b/drivers/media/i2c/tw9910.c @@ -996,7 +996,7 @@ static void tw9910_remove(struct i2c_client *client) } static const struct i2c_device_id tw9910_id[] = { - { "tw9910" }, + { .name = "tw9910" }, { } }; MODULE_DEVICE_TABLE(i2c, tw9910_id); diff --git a/drivers/media/i2c/uda1342.c b/drivers/media/i2c/uda1342.c index 2e4540ee2df2..437726788ba0 100644 --- a/drivers/media/i2c/uda1342.c +++ b/drivers/media/i2c/uda1342.c @@ -79,7 +79,7 @@ static void uda1342_remove(struct i2c_client *client) } static const struct i2c_device_id uda1342_id[] = { - { "uda1342" }, + { .name = "uda1342" }, { } }; MODULE_DEVICE_TABLE(i2c, uda1342_id); diff --git a/drivers/media/i2c/upd64031a.c b/drivers/media/i2c/upd64031a.c index a178af46e695..670118c16872 100644 --- a/drivers/media/i2c/upd64031a.c +++ b/drivers/media/i2c/upd64031a.c @@ -219,7 +219,7 @@ static void upd64031a_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id upd64031a_id[] = { - { "upd64031a" }, + { .name = "upd64031a" }, { } }; MODULE_DEVICE_TABLE(i2c, upd64031a_id); diff --git a/drivers/media/i2c/upd64083.c b/drivers/media/i2c/upd64083.c index 5421dc5e32c9..e610dffa9e10 100644 --- a/drivers/media/i2c/upd64083.c +++ b/drivers/media/i2c/upd64083.c @@ -190,7 +190,7 @@ static void upd64083_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id upd64083_id[] = { - { "upd64083" }, + { .name = "upd64083" }, { } }; MODULE_DEVICE_TABLE(i2c, upd64083_id); diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c index fef3993f4e2d..56b99eea54a1 100644 --- a/drivers/media/i2c/video-i2c.c +++ b/drivers/media/i2c/video-i2c.c @@ -454,8 +454,9 @@ static int video_i2c_thread_vid_cap(void *priv) spin_lock(&data->slock); if (!list_empty(&data->vid_cap_active)) { - vid_cap_buf = list_last_entry(&data->vid_cap_active, - struct video_i2c_buffer, list); + vid_cap_buf = list_first_entry(&data->vid_cap_active, + struct video_i2c_buffer, + list); list_del(&vid_cap_buf->list); } @@ -888,7 +889,7 @@ static void video_i2c_remove(struct i2c_client *client) if (data->chip->set_power) data->chip->set_power(data, false); - video_unregister_device(&data->vdev); + vb2_video_unregister_device(&data->vdev); } #ifdef CONFIG_PM @@ -921,9 +922,9 @@ static const struct dev_pm_ops video_i2c_pm_ops = { }; static const struct i2c_device_id video_i2c_id_table[] = { - { "amg88xx", (kernel_ulong_t)&video_i2c_chip[AMG88XX] }, - { "mlx90640", (kernel_ulong_t)&video_i2c_chip[MLX90640] }, - {} + { .name = "amg88xx", .driver_data = (kernel_ulong_t)&video_i2c_chip[AMG88XX] }, + { .name = "mlx90640", .driver_data = (kernel_ulong_t)&video_i2c_chip[MLX90640] }, + { } }; MODULE_DEVICE_TABLE(i2c, video_i2c_id_table); diff --git a/drivers/media/i2c/vp27smpx.c b/drivers/media/i2c/vp27smpx.c index df21950be24f..21fcfdd4c163 100644 --- a/drivers/media/i2c/vp27smpx.c +++ b/drivers/media/i2c/vp27smpx.c @@ -172,7 +172,7 @@ static void vp27smpx_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id vp27smpx_id[] = { - { "vp27smpx" }, + { .name = "vp27smpx" }, { } }; MODULE_DEVICE_TABLE(i2c, vp27smpx_id); diff --git a/drivers/media/i2c/vpx3220.c b/drivers/media/i2c/vpx3220.c index 5f1a22284168..29bcbb5a5fbb 100644 --- a/drivers/media/i2c/vpx3220.c +++ b/drivers/media/i2c/vpx3220.c @@ -535,9 +535,9 @@ static void vpx3220_remove(struct i2c_client *client) } static const struct i2c_device_id vpx3220_id[] = { - { "vpx3220a" }, - { "vpx3216b" }, - { "vpx3214c" }, + { .name = "vpx3220a" }, + { .name = "vpx3216b" }, + { .name = "vpx3214c" }, { } }; MODULE_DEVICE_TABLE(i2c, vpx3220_id); diff --git a/drivers/media/i2c/wm8739.c b/drivers/media/i2c/wm8739.c index 72eb10339d06..db62bafb447c 100644 --- a/drivers/media/i2c/wm8739.c +++ b/drivers/media/i2c/wm8739.c @@ -243,7 +243,7 @@ static void wm8739_remove(struct i2c_client *client) } static const struct i2c_device_id wm8739_id[] = { - { "wm8739" }, + { .name = "wm8739" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8739_id); diff --git a/drivers/media/i2c/wm8775.c b/drivers/media/i2c/wm8775.c index 56778d3bc28a..5db197bb6fda 100644 --- a/drivers/media/i2c/wm8775.c +++ b/drivers/media/i2c/wm8775.c @@ -289,7 +289,7 @@ static void wm8775_remove(struct i2c_client *client) } static const struct i2c_device_id wm8775_id[] = { - { "wm8775" }, + { .name = "wm8775" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8775_id); diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig index eebb16c58f3d..bfdb200f85a3 100644 --- a/drivers/media/pci/Kconfig +++ b/drivers/media/pci/Kconfig @@ -13,6 +13,7 @@ if MEDIA_PCI_SUPPORT if MEDIA_CAMERA_SUPPORT comment "Media capture support" +source "drivers/media/pci/hws/Kconfig" source "drivers/media/pci/mgb4/Kconfig" source "drivers/media/pci/solo6x10/Kconfig" source "drivers/media/pci/tw5864/Kconfig" diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile index 02763ad88511..c4508b6723a9 100644 --- a/drivers/media/pci/Makefile +++ b/drivers/media/pci/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_VIDEO_CX23885) += cx23885/ obj-$(CONFIG_VIDEO_CX25821) += cx25821/ obj-$(CONFIG_VIDEO_CX88) += cx88/ obj-$(CONFIG_VIDEO_DT3155) += dt3155/ +obj-$(CONFIG_VIDEO_HWS) += hws/ obj-$(CONFIG_VIDEO_IVTV) += ivtv/ obj-$(CONFIG_VIDEO_MGB4) += mgb4/ obj-$(CONFIG_VIDEO_SAA7134) += saa7134/ diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c index 4a8af8b88d84..9b92e8db494c 100644 --- a/drivers/media/pci/cx23885/cx23885-core.c +++ b/drivers/media/pci/cx23885/cx23885-core.c @@ -1002,8 +1002,12 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) } /* PCIe stuff */ - dev->lmmio = ioremap(pci_resource_start(dev->pci, 0), - pci_resource_len(dev->pci, 0)); + dev->lmmio = pci_ioremap_bar(dev->pci, 0); + if (!dev->lmmio) { + dev_err(&dev->pci->dev, "CORE %s: can't ioremap MMIO memory\n", + dev->name); + goto err_release_region; + } dev->bmmio = (u8 __iomem *)dev->lmmio; @@ -1109,6 +1113,12 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) } return 0; + +err_release_region: + release_mem_region(pci_resource_start(dev->pci, 0), + pci_resource_len(dev->pci, 0)); + cx23885_devcount--; + return -ENODEV; } static void cx23885_dev_unregister(struct cx23885_dev *dev) diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c index bbd24769ae56..80d2e143384b 100644 --- a/drivers/media/pci/dm1105/dm1105.c +++ b/drivers/media/pci/dm1105/dm1105.c @@ -768,6 +768,8 @@ static void dm1105_ir_exit(struct dm1105_dev *dm1105) static int dm1105_hw_init(struct dm1105_dev *dev) { + int ret; + dm1105_disable_irqs(dev); dm_writeb(DM1105_HOST_CTR, 0); @@ -778,7 +780,10 @@ static int dm1105_hw_init(struct dm1105_dev *dev) dm_writew(DM1105_TSCTR, 0xc10a); /* map DMA and set address */ - dm1105_dma_map(dev); + ret = dm1105_dma_map(dev); + if (ret) + return -ENOMEM; + dm1105_set_dma_addr(dev); /* big buffer */ dm_writel(DM1105_RLEN, 5 * DM1105_DMA_BYTES); @@ -1194,6 +1199,7 @@ static void dm1105_remove(struct pci_dev *pdev) dm1105_hw_exit(dev); free_irq(pdev->irq, dev); + destroy_workqueue(dev->wq); pci_iounmap(pdev, dev->io_mem); pci_release_regions(pdev); pci_disable_device(pdev); diff --git a/drivers/media/pci/hws/Kconfig b/drivers/media/pci/hws/Kconfig new file mode 100644 index 000000000000..ab0bbf49ca71 --- /dev/null +++ b/drivers/media/pci/hws/Kconfig @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_HWS + tristate "AVMatrix HWS PCIe capture devices" + depends on PCI && VIDEO_DEV + select VIDEOBUF2_DMA_CONTIG + help + Enable support for AVMatrix HWS series multi-channel PCIe capture + devices that provide HDMI video capture. + + To compile this driver as a module, choose M here: the module + will be named hws. diff --git a/drivers/media/pci/hws/Makefile b/drivers/media/pci/hws/Makefile new file mode 100644 index 000000000000..f9c7dc4f2d8d --- /dev/null +++ b/drivers/media/pci/hws/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only +hws-objs := hws_pci.o hws_irq.o hws_video.o hws_v4l2_ioctl.o + +obj-$(CONFIG_VIDEO_HWS) += hws.o diff --git a/drivers/media/pci/hws/hws.h b/drivers/media/pci/hws/hws.h new file mode 100644 index 000000000000..8fbe1fe27844 --- /dev/null +++ b/drivers/media/pci/hws/hws.h @@ -0,0 +1,173 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef HWS_PCIE_H +#define HWS_PCIE_H + +#include <linux/types.h> +#include <linux/compiler.h> +#include <linux/dma-mapping.h> +#include <linux/kthread.h> +#include <linux/pci.h> +#include <linux/list.h> +#include <linux/spinlock.h> +#include <linux/sizes.h> +#include <linux/atomic.h> + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-dv-timings.h> +#include <media/videobuf2-dma-sg.h> + +#include "hws_reg.h" + +struct hwsmem_param { + u32 index; + u32 type; + u32 status; +}; + +struct hws_pix_state { + u32 width; + u32 height; + u32 fourcc; /* V4L2_PIX_FMT_* (YUYV only here) */ + u32 bytesperline; /* stride */ + u32 sizeimage; /* full frame */ + enum v4l2_field field; /* V4L2_FIELD_NONE or INTERLACED */ + enum v4l2_colorspace colorspace; /* e.g., REC709 */ + enum v4l2_ycbcr_encoding ycbcr_enc; /* V4L2_YCBCR_ENC_DEFAULT */ + enum v4l2_quantization quantization; /* V4L2_QUANTIZATION_LIM_RANGE */ + enum v4l2_xfer_func xfer_func; /* V4L2_XFER_FUNC_DEFAULT */ + bool interlaced; /* cached hardware state */ + u32 half_size; /* hardware half-frame size */ +}; + +#define UNSET (-1U) + +struct hws_pcie_dev; +struct hws_adapter; +struct hws_video; + +struct hwsvideo_buffer { + struct vb2_v4l2_buffer vb; + struct list_head list; + int slot; +}; + +struct hws_video { + /* Linkage */ + struct hws_pcie_dev *parent; + struct video_device *video_device; + + struct vb2_queue buffer_queue; + struct list_head capture_queue; + struct hwsvideo_buffer *active; + struct hwsvideo_buffer *next_prepared; + + /* Locking */ + struct mutex state_lock; + spinlock_t irq_lock; /* Protects capture_queue and active buffers. */ + + /* Indices */ + int channel_index; + + /* Color controls */ + int current_brightness; + int current_contrast; + int current_saturation; + int current_hue; + + /* V4L2 controls */ + struct v4l2_ctrl_handler control_handler; + struct v4l2_ctrl *ctrl_brightness; + struct v4l2_ctrl *ctrl_contrast; + struct v4l2_ctrl *ctrl_saturation; + struct v4l2_ctrl *ctrl_hue; + + /* Capture queue status */ + struct hws_pix_state pix; + struct v4l2_dv_timings cur_dv_timings; /* last configured/notified DV timings */ + u32 current_fps; /* Hz, updated by mode changes, not by read-only queries */ + + /* Per-channel capture state */ + bool cap_active; + bool stop_requested; + u8 last_buf_half_toggle; + bool half_seen; + atomic_t sequence_number; + u32 queued_count; + + /* Timeout and error handling */ + u32 timeout_count; + u32 error_count; + + bool window_valid; + u32 last_dma_hi; + u32 last_dma_page; + u32 last_pci_addr; + u32 last_half16; + + /* Misc counters */ + int signal_loss_cnt; +}; + +static inline void hws_set_current_dv_timings(struct hws_video *vid, + u32 width, u32 height, + bool interlaced) +{ + if (!vid) + return; + + vid->cur_dv_timings = (struct v4l2_dv_timings) { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = width, + .height = height, + .interlaced = interlaced, + }, + }; +} + +struct hws_scratch_dma { + void *cpu; + dma_addr_t dma; + size_t size; +}; + +struct hws_pcie_dev { + /* Core objects */ + struct pci_dev *pdev; + struct hws_video video[MAX_VID_CHANNELS]; + + /* BAR and workqueues */ + void __iomem *bar0_base; + + /* Device identity and capabilities */ + u16 vendor_id; + u16 device_id; + u16 device_ver; + u16 hw_ver; + u32 sub_ver; + u32 port_id; + /* Tri-state support flag used by set_video_format_size(). */ + u32 support_yv12; + u32 max_hw_video_buf_sz; + u8 max_channels; + u8 cur_max_video_ch; + bool start_run; + + bool buf_allocated; + + /* V4L2 framework objects */ + struct v4l2_device v4l2_device; + + /* Kernel thread */ + struct task_struct *main_task; + struct hws_scratch_dma scratch_vid[MAX_VID_CHANNELS]; + + bool suspended; + int irq; + + /* Error flags */ + int pci_lost; +}; + +#endif diff --git a/drivers/media/pci/hws/hws_irq.c b/drivers/media/pci/hws/hws_irq.c new file mode 100644 index 000000000000..eebb4b8a5cd5 --- /dev/null +++ b/drivers/media/pci/hws/hws_irq.c @@ -0,0 +1,269 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include <linux/compiler.h> +#include <linux/moduleparam.h> +#include <linux/io.h> +#include <linux/dma-mapping.h> +#include <linux/interrupt.h> +#include <linux/minmax.h> +#include <linux/string.h> + +#include <media/videobuf2-dma-contig.h> + +#include "hws_irq.h" +#include "hws_reg.h" +#include "hws_video.h" +#include "hws.h" + +#define MAX_INT_LOOPS 100 + +static bool hws_toggle_debug; +module_param_named(toggle_debug, hws_toggle_debug, bool, 0644); +MODULE_PARM_DESC(toggle_debug, + "Read toggle registers in IRQ handler for debug logging"); + +static int hws_arm_next(struct hws_pcie_dev *hws, u32 ch) +{ + struct hws_video *v = &hws->video[ch]; + unsigned long flags; + struct hwsvideo_buffer *buf; + + dev_dbg(&hws->pdev->dev, + "arm_next(ch=%u): stop=%d cap=%d queued=%d\n", + ch, READ_ONCE(v->stop_requested), READ_ONCE(v->cap_active), + !list_empty(&v->capture_queue)); + + if (READ_ONCE(hws->suspended)) { + dev_dbg(&hws->pdev->dev, "arm_next(ch=%u): suspended\n", ch); + return -EBUSY; + } + + if (READ_ONCE(v->stop_requested) || !READ_ONCE(v->cap_active)) { + dev_dbg(&hws->pdev->dev, + "arm_next(ch=%u): stop=%d cap=%d -> cancel\n", ch, + v->stop_requested, v->cap_active); + return -ECANCELED; + } + + spin_lock_irqsave(&v->irq_lock, flags); + if (list_empty(&v->capture_queue)) { + spin_unlock_irqrestore(&v->irq_lock, flags); + dev_dbg(&hws->pdev->dev, "arm_next(ch=%u): queue empty\n", ch); + return -EAGAIN; + } + + buf = list_first_entry(&v->capture_queue, struct hwsvideo_buffer, list); + list_del_init(&buf->list); /* keep buffer safe for later cleanup */ + if (v->queued_count) + v->queued_count--; + v->active = buf; + spin_unlock_irqrestore(&v->irq_lock, flags); + dev_dbg(&hws->pdev->dev, "arm_next(ch=%u): picked buffer %p\n", ch, + buf); + + /* Publish descriptor(s) before doorbell/MMIO kicks. */ + wmb(); + + /* Avoid MMIO during suspend */ + if (READ_ONCE(hws->suspended)) { + unsigned long f; + + dev_dbg(&hws->pdev->dev, + "arm_next(ch=%u): suspended after pick\n", ch); + spin_lock_irqsave(&v->irq_lock, f); + if (v->active) { + list_add(&buf->list, &v->capture_queue); + v->queued_count++; + v->active = NULL; + } + spin_unlock_irqrestore(&v->irq_lock, f); + return -EBUSY; + } + + /* Also program the DMA address register directly */ + { + dma_addr_t dma_addr = + vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); + hws_program_dma_for_addr(hws, ch, dma_addr); + iowrite32(lower_32_bits(dma_addr), + hws->bar0_base + HWS_REG_DMA_ADDR(ch)); + } + + dev_dbg(&hws->pdev->dev, "arm_next(ch=%u): programmed buffer %p\n", ch, + buf); + spin_lock_irqsave(&v->irq_lock, flags); + hws_prime_next_locked(v); + spin_unlock_irqrestore(&v->irq_lock, flags); + return 0; +} + +static void hws_video_handle_vdone(struct hws_video *v) +{ + struct hws_pcie_dev *hws = v->parent; + unsigned int ch = v->channel_index; + struct hwsvideo_buffer *done; + unsigned long flags; + bool promoted = false; + + dev_dbg(&hws->pdev->dev, + "bh_video(ch=%u): stop=%d cap=%d active=%p\n", + ch, READ_ONCE(v->stop_requested), READ_ONCE(v->cap_active), + v->active); + + int ret; + + dev_dbg(&hws->pdev->dev, + "bh_video(ch=%u): entry stop=%d cap=%d\n", ch, + v->stop_requested, v->cap_active); + if (READ_ONCE(hws->suspended)) + return; + + if (READ_ONCE(v->stop_requested) || !READ_ONCE(v->cap_active)) + return; + + spin_lock_irqsave(&v->irq_lock, flags); + done = v->active; + if (done && v->next_prepared) { + v->active = v->next_prepared; + v->next_prepared = NULL; + promoted = true; + } + spin_unlock_irqrestore(&v->irq_lock, flags); + + /* 1) Complete the buffer the HW just finished (if any) */ + if (done) { + struct vb2_v4l2_buffer *vb2v = &done->vb; + size_t expected = v->pix.sizeimage; + size_t plane_size = vb2_plane_size(&vb2v->vb2_buf, 0); + + if (expected > plane_size) { + dev_warn_ratelimited(&hws->pdev->dev, + "bh_video(ch=%u): sizeimage %zu > plane %zu, dropping seq=%u\n", + ch, expected, plane_size, + (u32)atomic_read(&v->sequence_number) + 1); + vb2_buffer_done(&vb2v->vb2_buf, VB2_BUF_STATE_ERROR); + goto arm_next; + } + vb2_set_plane_payload(&vb2v->vb2_buf, 0, expected); + + dma_rmb(); /* device writes visible before userspace sees it */ + + vb2v->sequence = (u32)atomic_inc_return(&v->sequence_number); + vb2v->vb2_buf.timestamp = ktime_get_ns(); + dev_dbg(&hws->pdev->dev, + "bh_video(ch=%u): DONE buf=%p seq=%u half_seen=%d toggle=%u\n", + ch, done, vb2v->sequence, v->half_seen, + v->last_buf_half_toggle); + + if (!promoted) + v->active = NULL; /* channel no longer owns this buffer */ + vb2_buffer_done(&vb2v->vb2_buf, VB2_BUF_STATE_DONE); + } + + if (READ_ONCE(hws->suspended)) + return; + + if (promoted) { + dev_dbg(&hws->pdev->dev, + "bh_video(ch=%u): promoted pre-armed buffer active=%p\n", + ch, v->active); + spin_lock_irqsave(&v->irq_lock, flags); + hws_prime_next_locked(v); + spin_unlock_irqrestore(&v->irq_lock, flags); + return; + } + +arm_next: + /* 2) Immediately arm the next queued buffer (if present) */ + ret = hws_arm_next(hws, ch); + if (ret == -EAGAIN) { + dev_dbg(&hws->pdev->dev, + "bh_video(ch=%u): no queued buffer to arm\n", ch); + return; + } + dev_dbg(&hws->pdev->dev, + "bh_video(ch=%u): armed next buffer, active=%p\n", ch, + v->active); + /* On success the engine now points at v->active's DMA address */ +} + +irqreturn_t hws_irq_handler(int irq, void *info) +{ + struct hws_pcie_dev *pdx = info; + u32 int_state; + + dev_dbg(&pdx->pdev->dev, "irq: entry\n"); + if (pdx->bar0_base) { + dev_dbg(&pdx->pdev->dev, + "irq: INT_EN=0x%08x INT_STATUS=0x%08x\n", + readl(pdx->bar0_base + INT_EN_REG_BASE), + readl(pdx->bar0_base + HWS_REG_INT_STATUS)); + } + + /* Fast path: if suspended, quietly ack and exit */ + if (READ_ONCE(pdx->suspended)) { + int_state = readl_relaxed(pdx->bar0_base + HWS_REG_INT_STATUS); + if (int_state) { + writel(int_state, pdx->bar0_base + HWS_REG_INT_STATUS); + (void)readl_relaxed(pdx->bar0_base + HWS_REG_INT_STATUS); + } + return int_state ? IRQ_HANDLED : IRQ_NONE; + } + int_state = readl_relaxed(pdx->bar0_base + HWS_REG_INT_STATUS); + if (!int_state || int_state == 0xFFFFFFFF) { + dev_dbg(&pdx->pdev->dev, + "irq: spurious or device-gone int_state=0x%08x\n", + int_state); + return IRQ_NONE; + } + dev_dbg(&pdx->pdev->dev, "irq: entry INT_STATUS=0x%08x\n", int_state); + + /* Loop until all pending bits are serviced (max 100 iterations) */ + for (u32 cnt = 0; int_state && cnt < MAX_INT_LOOPS; ++cnt) { + for (unsigned int ch = 0; ch < pdx->cur_max_video_ch; ++ch) { + u32 vbit = HWS_INT_VDONE_BIT(ch); + + if (!(int_state & vbit)) + continue; + + if (READ_ONCE(pdx->video[ch].cap_active) && + !READ_ONCE(pdx->video[ch].stop_requested)) { + if (hws_toggle_debug) { + u32 toggle = + readl_relaxed(pdx->bar0_base + + HWS_REG_VBUF_TOGGLE(ch)) & 0x01; + WRITE_ONCE(pdx->video[ch].last_buf_half_toggle, + toggle); + } + dma_rmb(); + WRITE_ONCE(pdx->video[ch].half_seen, true); + dev_dbg(&pdx->pdev->dev, + "irq: VDONE ch=%u toggle=%u handling inline (cap=%d)\n", + ch, + READ_ONCE(pdx->video[ch].last_buf_half_toggle), + READ_ONCE(pdx->video[ch].cap_active)); + hws_video_handle_vdone(&pdx->video[ch]); + } else { + dev_dbg(&pdx->pdev->dev, + "irq: VDONE ch=%u ignored (cap=%d stop=%d)\n", + ch, + READ_ONCE(pdx->video[ch].cap_active), + READ_ONCE(pdx->video[ch].stop_requested)); + } + + writel(vbit, pdx->bar0_base + HWS_REG_INT_STATUS); + (void)readl_relaxed(pdx->bar0_base + HWS_REG_INT_STATUS); + } + + /* Re-read in case new interrupt bits popped while processing */ + int_state = readl_relaxed(pdx->bar0_base + HWS_REG_INT_STATUS); + dev_dbg(&pdx->pdev->dev, + "irq: loop cnt=%u new INT_STATUS=0x%08x\n", cnt, + int_state); + if (cnt + 1 == MAX_INT_LOOPS) + dev_warn_ratelimited(&pdx->pdev->dev, + "IRQ storm? status=0x%08x\n", + int_state); + } + + return IRQ_HANDLED; +} diff --git a/drivers/media/pci/hws/hws_irq.h b/drivers/media/pci/hws/hws_irq.h new file mode 100644 index 000000000000..a42867aa0c46 --- /dev/null +++ b/drivers/media/pci/hws/hws_irq.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef HWS_INTERRUPT_H +#define HWS_INTERRUPT_H + +#include <linux/pci.h> +#include "hws.h" + +irqreturn_t hws_irq_handler(int irq, void *info); + +#endif /* HWS_INTERRUPT_H */ diff --git a/drivers/media/pci/hws/hws_pci.c b/drivers/media/pci/hws/hws_pci.c new file mode 100644 index 000000000000..30bb7d34465b --- /dev/null +++ b/drivers/media/pci/hws/hws_pci.c @@ -0,0 +1,865 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include <linux/pci.h> +#include <linux/types.h> +#include <linux/iopoll.h> +#include <linux/bitfield.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kthread.h> +#include <linux/interrupt.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/ktime.h> +#include <linux/math64.h> +#include <linux/pm.h> +#include <linux/freezer.h> +#include <linux/pci_regs.h> + +#include <media/v4l2-ctrls.h> + +#include "hws.h" +#include "hws_reg.h" +#include "hws_video.h" +#include "hws_irq.h" +#include "hws_v4l2_ioctl.h" + +#define DRV_NAME "hws" +#define HWS_BUSY_POLL_DELAY_US 10 +#define HWS_BUSY_POLL_TIMEOUT_US 1000000 + +static unsigned long long hws_elapsed_us(u64 start_ns) +{ + return div_u64(ktime_get_mono_fast_ns() - start_ns, 1000); +} + +/* register layout inside HWS_REG_DEVICE_INFO */ +#define DEVINFO_VER GENMASK(7, 0) +#define DEVINFO_SUBVER GENMASK(15, 8) +#define DEVINFO_YV12 GENMASK(31, 28) +#define DEVINFO_HWKEY GENMASK(27, 24) +#define DEVINFO_PORTID GENMASK(25, 24) /* low 2 bits of HW-key */ + +#define MAKE_ENTRY(__vend, __chip, __subven, __subdev, __configptr) \ + { .vendor = (__vend), \ + .device = (__chip), \ + .subvendor = (__subven), \ + .subdevice = (__subdev), \ + .driver_data = (unsigned long)(__configptr) } + +/* + * PCI IDs for HWS family cards. + * + * The subsystem IDs are fixed at 0x8888:0x0007 for this family. Some boards + * enumerate with vendor ID 0x8888 or 0x1f33. Exact SKU names are not fully + * pinned down yet; update these comments when vendor documentation or INF + * strings are available. + */ +static const struct pci_device_id hws_pci_table[] = { + /* HWS family, SKU unknown. */ + MAKE_ENTRY(0x8888, 0x9534, 0x8888, 0x0007, NULL), + MAKE_ENTRY(0x1F33, 0x8534, 0x8888, 0x0007, NULL), + MAKE_ENTRY(0x1F33, 0x8554, 0x8888, 0x0007, NULL), + + /* HWS 2x2 HDMI family. */ + MAKE_ENTRY(0x8888, 0x8524, 0x8888, 0x0007, NULL), + /* HWS 2x2 SDI family. */ + MAKE_ENTRY(0x1F33, 0x6524, 0x8888, 0x0007, NULL), + + /* HWS X4 HDMI family. */ + MAKE_ENTRY(0x8888, 0x8504, 0x8888, 0x0007, NULL), + /* HWS X4 SDI family. */ + MAKE_ENTRY(0x8888, 0x6504, 0x8888, 0x0007, NULL), + + /* HWS family, SKU unknown. */ + MAKE_ENTRY(0x8888, 0x8532, 0x8888, 0x0007, NULL), + MAKE_ENTRY(0x8888, 0x8512, 0x8888, 0x0007, NULL), + MAKE_ENTRY(0x8888, 0x8501, 0x8888, 0x0007, NULL), + MAKE_ENTRY(0x1F33, 0x6502, 0x8888, 0x0007, NULL), + + /* HWS X4 HDMI family (alternate vendor ID). */ + MAKE_ENTRY(0x1F33, 0x8504, 0x8888, 0x0007, NULL), + /* HWS 2x2 HDMI family (alternate vendor ID). */ + MAKE_ENTRY(0x1F33, 0x8524, 0x8888, 0x0007, NULL), + + {} +}; + +static void enable_pcie_relaxed_ordering(struct pci_dev *dev) +{ + pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_RELAX_EN); +} + +static void hws_configure_hardware_capabilities(struct hws_pcie_dev *hdev) +{ + u16 id = hdev->device_id; + + /* select per-chip channel counts */ + switch (id) { + case 0x9534: + case 0x6524: + case 0x8524: + case 0x8504: + case 0x6504: + hdev->cur_max_video_ch = 4; + break; + case 0x8532: + hdev->cur_max_video_ch = 2; + break; + case 0x8512: + case 0x6502: + hdev->cur_max_video_ch = 2; + break; + case 0x8501: + hdev->cur_max_video_ch = 1; + break; + default: + hdev->cur_max_video_ch = 4; + break; + } + + /* universal buffer capacity */ + hdev->max_hw_video_buf_sz = MAX_MM_VIDEO_SIZE; + + /* decide hardware-version and program DMA max size if needed */ + if (hdev->device_ver > 121) { + if (id == 0x8501 && hdev->device_ver == 122) { + hdev->hw_ver = 0; + } else { + hdev->hw_ver = 1; + u32 dma_max = (u32)(MAX_VIDEO_SCALER_SIZE / 16); + + writel(dma_max, hdev->bar0_base + HWS_REG_DMA_MAX_SIZE); + /* readback to flush posted MMIO write */ + (void)readl(hdev->bar0_base + HWS_REG_DMA_MAX_SIZE); + } + } else { + hdev->hw_ver = 0; + } +} + +static void hws_stop_device(struct hws_pcie_dev *hws); + +static void hws_log_lifecycle_snapshot(struct hws_pcie_dev *hws, + const char *action, + const char *phase) +{ + struct device *dev; + u32 int_en, int_status, vcap, sys_status, dec_mode; + + if (!hws || !hws->pdev) + return; + + dev = &hws->pdev->dev; + if (!hws->bar0_base) { + dev_dbg(dev, + "lifecycle:%s:%s bar0-unmapped suspended=%d start_run=%d pci_lost=%d irq=%d\n", + action, phase, READ_ONCE(hws->suspended), hws->start_run, + hws->pci_lost, hws->irq); + return; + } + + int_en = readl(hws->bar0_base + INT_EN_REG_BASE); + int_status = readl(hws->bar0_base + HWS_REG_INT_STATUS); + vcap = readl(hws->bar0_base + HWS_REG_VCAP_ENABLE); + sys_status = readl(hws->bar0_base + HWS_REG_SYS_STATUS); + dec_mode = readl(hws->bar0_base + HWS_REG_DEC_MODE); + + dev_dbg(dev, + "lifecycle:%s:%s suspended=%d start_run=%d pci_lost=%d irq=%d INT_EN=0x%08x INT_STATUS=0x%08x VCAP=0x%08x SYS=0x%08x DEC=0x%08x\n", + action, phase, READ_ONCE(hws->suspended), hws->start_run, + hws->pci_lost, hws->irq, int_en, int_status, vcap, + sys_status, dec_mode); +} + +static int read_chip_id(struct hws_pcie_dev *hdev) +{ + u32 reg; + /* mirror PCI IDs for later switches */ + hdev->device_id = hdev->pdev->device; + hdev->vendor_id = hdev->pdev->vendor; + + reg = readl(hdev->bar0_base + HWS_REG_DEVICE_INFO); + + hdev->device_ver = FIELD_GET(DEVINFO_VER, reg); + hdev->sub_ver = FIELD_GET(DEVINFO_SUBVER, reg); + hdev->support_yv12 = FIELD_GET(DEVINFO_YV12, reg); + hdev->port_id = FIELD_GET(DEVINFO_PORTID, reg); + + hdev->max_hw_video_buf_sz = MAX_MM_VIDEO_SIZE; + hdev->max_channels = 4; + hdev->buf_allocated = false; + hdev->main_task = NULL; + hdev->start_run = false; + hdev->pci_lost = 0; + + writel(0x00, hdev->bar0_base + HWS_REG_DEC_MODE); + writel(0x10, hdev->bar0_base + HWS_REG_DEC_MODE); + + hws_configure_hardware_capabilities(hdev); + + dev_info(&hdev->pdev->dev, + "chip detected: ver=%u subver=%u port=%u yv12=%u\n", + hdev->device_ver, hdev->sub_ver, hdev->port_id, + hdev->support_yv12); + + return 0; +} + +static int main_ks_thread_handle(void *data) +{ + struct hws_pcie_dev *pdx = data; + + set_freezable(); + + while (!kthread_should_stop()) { + /* If we're suspending, don't touch hardware; just sleep/freeze. */ + if (READ_ONCE(pdx->suspended)) { + try_to_freeze(); + schedule_timeout_interruptible(msecs_to_jiffies(1000)); + continue; + } + + /* avoid MMIO when suspended (guarded above) */ + check_video_format(pdx); + + try_to_freeze(); /* cooperate with freezer each loop */ + + /* Sleep 1s or until signaled to wake/stop */ + schedule_timeout_interruptible(msecs_to_jiffies(1000)); + } + + dev_dbg(&pdx->pdev->dev, "%s: exiting\n", __func__); + return 0; +} + +static void hws_stop_kthread_action(void *data) +{ + struct hws_pcie_dev *hws = data; + struct task_struct *t; + u64 start_ns; + + if (!hws) + return; + + t = READ_ONCE(hws->main_task); + if (!IS_ERR_OR_NULL(t)) { + start_ns = ktime_get_mono_fast_ns(); + dev_dbg(&hws->pdev->dev, + "lifecycle:kthread-stop:begin task=%s[%d]\n", + t->comm, t->pid); + WRITE_ONCE(hws->main_task, NULL); + kthread_stop(t); + dev_dbg(&hws->pdev->dev, + "lifecycle:kthread-stop:done (%lluus)\n", + hws_elapsed_us(start_ns)); + } +} + +static int hws_alloc_seed_buffers(struct hws_pcie_dev *hws) +{ + int ch; + /* 64 KiB is plenty for a safe dummy; hardware needs 64-byte alignment. */ + const size_t need = ALIGN(64 * 1024, 64); + + for (ch = 0; ch < hws->cur_max_video_ch; ch++) { +#if defined(CONFIG_HAS_DMA) /* normal on PCIe platforms */ + void *cpu = dma_alloc_coherent(&hws->pdev->dev, need, + &hws->scratch_vid[ch].dma, + GFP_KERNEL); +#else + void *cpu = NULL; +#endif + if (!cpu) { + dev_warn(&hws->pdev->dev, + "scratch: dma_alloc_coherent failed ch=%d\n", ch); + /* not fatal: free earlier ones and continue without seeding */ + while (--ch >= 0) { + if (hws->scratch_vid[ch].cpu) + dma_free_coherent(&hws->pdev->dev, + hws->scratch_vid[ch].size, + hws->scratch_vid[ch].cpu, + hws->scratch_vid[ch].dma); + hws->scratch_vid[ch].cpu = NULL; + hws->scratch_vid[ch].size = 0; + } + return -ENOMEM; + } + hws->scratch_vid[ch].cpu = cpu; + hws->scratch_vid[ch].size = need; + } + return 0; +} + +static void hws_free_seed_buffers(struct hws_pcie_dev *hws) +{ + int ch; + + for (ch = 0; ch < hws->cur_max_video_ch; ch++) { + if (hws->scratch_vid[ch].cpu) { + dma_free_coherent(&hws->pdev->dev, + hws->scratch_vid[ch].size, + hws->scratch_vid[ch].cpu, + hws->scratch_vid[ch].dma); + hws->scratch_vid[ch].cpu = NULL; + hws->scratch_vid[ch].size = 0; + } + } +} + +static void hws_seed_channel(struct hws_pcie_dev *hws, int ch) +{ + dma_addr_t paddr = hws->scratch_vid[ch].dma; + u32 lo = lower_32_bits(paddr); + u32 hi = upper_32_bits(paddr); + u32 pci_addr = lo & PCI_E_BAR_ADD_LOWMASK; + + lo &= PCI_E_BAR_ADD_MASK; + + /* Program 64-bit BAR remap entry for this channel (table @ 0x208 + ch * 8) */ + writel_relaxed(hi, hws->bar0_base + + PCI_ADDR_TABLE_BASE + 0x208 + ch * 8); + writel_relaxed(lo, hws->bar0_base + + PCI_ADDR_TABLE_BASE + 0x208 + ch * 8 + + PCIE_BARADDROFSIZE); + + /* Program capture engine per-channel base/half */ + writel_relaxed((ch + 1) * PCIEBAR_AXI_BASE + pci_addr, + hws->bar0_base + CVBS_IN_BUF_BASE + + ch * PCIE_BARADDROFSIZE); + + /* Half size: use either the current format's half or half of scratch. */ + { + u32 half = hws->video[ch].pix.half_size ? + hws->video[ch].pix.half_size : + (u32)(hws->scratch_vid[ch].size / 2); + + writel_relaxed(half / 16, + hws->bar0_base + CVBS_IN_BUF_BASE2 + + ch * PCIE_BARADDROFSIZE); + } + + (void)readl(hws->bar0_base + HWS_REG_INT_STATUS); /* flush posted writes */ +} + +static void hws_seed_all_channels(struct hws_pcie_dev *hws) +{ + int ch; + + for (ch = 0; ch < hws->cur_max_video_ch; ch++) { + if (hws->scratch_vid[ch].cpu) + hws_seed_channel(hws, ch); + } +} + +static void hws_irq_mask_gate(struct hws_pcie_dev *hws) +{ + writel(0x00000000, hws->bar0_base + INT_EN_REG_BASE); + (void)readl(hws->bar0_base + INT_EN_REG_BASE); +} + +static void hws_irq_unmask_gate(struct hws_pcie_dev *hws) +{ + writel(HWS_INT_EN_MASK, hws->bar0_base + INT_EN_REG_BASE); + (void)readl(hws->bar0_base + INT_EN_REG_BASE); +} + +static void hws_irq_clear_pending(struct hws_pcie_dev *hws) +{ + u32 st = readl(hws->bar0_base + HWS_REG_INT_STATUS); + + if (st) { + writel(st, hws->bar0_base + HWS_REG_INT_STATUS); /* W1C */ + (void)readl(hws->bar0_base + HWS_REG_INT_STATUS); + } +} + +static void hws_block_hotpaths(struct hws_pcie_dev *hws) +{ + WRITE_ONCE(hws->suspended, true); + if (hws->irq >= 0) + disable_irq(hws->irq); + + if (!hws->bar0_base) + return; + + hws_irq_mask_gate(hws); + hws_irq_clear_pending(hws); +} + +static int hws_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) +{ + struct hws_pcie_dev *hws; + int i, ret, irq; + unsigned long irqf = 0; + bool v4l2_registered = false; + + /* devres-backed device object */ + hws = devm_kzalloc(&pdev->dev, sizeof(*hws), GFP_KERNEL); + if (!hws) + return -ENOMEM; + + hws->pdev = pdev; + hws->irq = -1; + hws->suspended = false; + pci_set_drvdata(pdev, hws); + + /* 1) Enable device + bus mastering (managed) */ + ret = pcim_enable_device(pdev); + if (ret) + return dev_err_probe(&pdev->dev, ret, "pcim_enable_device\n"); + pci_set_master(pdev); + + /* 2) Map BAR0 (managed) */ + ret = pcim_iomap_regions(pdev, BIT(0), KBUILD_MODNAME); + if (ret) + return dev_err_probe(&pdev->dev, ret, "pcim_iomap_regions BAR0\n"); + hws->bar0_base = pcim_iomap_table(pdev)[0]; + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + if (ret) { + dev_warn(&pdev->dev, + "64-bit DMA mask unavailable, falling back to 32-bit (%d)\n", + ret); + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "No suitable DMA configuration\n"); + } else { + dev_dbg(&pdev->dev, "Using 64-bit DMA mask\n"); + } + + /* 3) Apply optional PCIe tuning. */ + enable_pcie_relaxed_ordering(pdev); +#ifdef CONFIG_ARCH_TI816X + pcie_set_readrq(pdev, 128); +#endif + + /* 4) Identify chip & capabilities */ + read_chip_id(hws); + dev_info(&pdev->dev, "Device VID=0x%04x DID=0x%04x\n", + pdev->vendor, pdev->device); + hws_init_video_sys(hws, false); + + /* 5) Init channels (video state, locks, vb2, ctrls) */ + for (i = 0; i < hws->max_channels; i++) { + ret = hws_video_init_channel(hws, i); + if (ret) { + dev_err(&pdev->dev, "video channel init failed (ch=%d)\n", i); + goto err_unwind_channels; + } + } + + /* 6) Allocate scratch DMA and seed BAR table + channel base/half (legacy SetDMAAddress) */ + ret = hws_alloc_seed_buffers(hws); + if (!ret) + hws_seed_all_channels(hws); + + /* 7) Start-run sequence. */ + hws_init_video_sys(hws, false); + + /* A) Force legacy INTx; legacy used request_irq(pdev->irq, ..., IRQF_SHARED) */ + pci_intx(pdev, 1); + irqf = IRQF_SHARED; + irq = pdev->irq; + hws->irq = irq; + dev_info(&pdev->dev, "IRQ mode: legacy INTx (shared), irq=%d\n", irq); + + /* B) Mask the device's global/bridge gate (INT_EN_REG_BASE) */ + hws_irq_mask_gate(hws); + + /* C) Clear any sticky pending interrupt status (W1C) before we arm the line */ + hws_irq_clear_pending(hws); + + /* D) Request the legacy shared interrupt line (no vectors/MSI/MSI-X) */ + ret = devm_request_irq(&pdev->dev, irq, hws_irq_handler, irqf, + dev_name(&pdev->dev), hws); + if (ret) { + dev_err(&pdev->dev, "request_irq(%d) failed: %d\n", irq, ret); + goto err_unwind_channels; + } + + /* E) Set the global interrupt enable bit in main control register */ + { + u32 ctl_reg = readl(hws->bar0_base + HWS_REG_CTL); + + ctl_reg |= HWS_CTL_IRQ_ENABLE_BIT; + writel(ctl_reg, hws->bar0_base + HWS_REG_CTL); + (void)readl(hws->bar0_base + HWS_REG_CTL); /* flush write */ + dev_info(&pdev->dev, "Global IRQ enable bit set in control register\n"); + } + + /* F) Open the global gate just like legacy did */ + hws_irq_unmask_gate(hws); + dev_info(&pdev->dev, "INT_EN_GATE readback=0x%08x\n", + readl(hws->bar0_base + INT_EN_REG_BASE)); + + /* 11) Register V4L2 */ + ret = hws_video_register(hws); + if (ret) { + dev_err(&pdev->dev, "video_register: %d\n", ret); + goto err_unwind_channels; + } + v4l2_registered = true; + + /* 12) Background monitor thread (managed) */ + hws->main_task = kthread_run(main_ks_thread_handle, hws, "hws-mon"); + if (IS_ERR(hws->main_task)) { + ret = PTR_ERR(hws->main_task); + hws->main_task = NULL; + dev_err(&pdev->dev, "kthread_run: %d\n", ret); + goto err_unregister_va; + } + ret = devm_add_action_or_reset(&pdev->dev, hws_stop_kthread_action, hws); + if (ret) { + dev_err(&pdev->dev, "devm_add_action kthread_stop: %d\n", ret); + goto err_unregister_va; /* reset already stopped the thread */ + } + + /* 13) Final: show the line is armed */ + dev_info(&pdev->dev, "irq handler installed on irq=%d\n", irq); + return 0; + +err_unregister_va: + hws_stop_device(hws); + hws_video_unregister(hws); + hws_free_seed_buffers(hws); + return ret; +err_unwind_channels: + hws_free_seed_buffers(hws); + if (!v4l2_registered) { + while (--i >= 0) + hws_video_cleanup_channel(hws, i); + } + return ret; +} + +static int hws_check_busy(struct hws_pcie_dev *pdx) +{ + void __iomem *reg = pdx->bar0_base + HWS_REG_SYS_STATUS; + u32 val; + int ret; + + /* poll until !(val & BUSY_BIT), sleeping HWS_BUSY_POLL_DELAY_US between reads */ + ret = readl_poll_timeout(reg, val, !(val & HWS_SYS_DMA_BUSY_BIT), + HWS_BUSY_POLL_DELAY_US, + HWS_BUSY_POLL_TIMEOUT_US); + if (ret) { + dev_err(&pdx->pdev->dev, + "SYS_STATUS busy bit never cleared (0x%08x)\n", val); + return -ETIMEDOUT; + } + + return 0; +} + +static void hws_stop_dsp(struct hws_pcie_dev *hws) +{ + u32 status; + + /* Read the decoder mode/status register */ + status = readl(hws->bar0_base + HWS_REG_DEC_MODE); + dev_dbg(&hws->pdev->dev, "%s: status=0x%08x\n", __func__, status); + + /* If the device looks unplugged/stuck, bail out */ + if (status == 0xFFFFFFFF) + return; + + /* Tell the DSP to stop */ + writel(0x10, hws->bar0_base + HWS_REG_DEC_MODE); + + if (hws_check_busy(hws)) + dev_warn(&hws->pdev->dev, "DSP busy timeout on stop\n"); + /* Disable video capture engine in the DSP */ + writel(0x0, hws->bar0_base + HWS_REG_VCAP_ENABLE); +} + +/* Publish stop so ISR/BH will not touch video buffers anymore. */ +static void hws_publish_stop_flags(struct hws_pcie_dev *hws) +{ + unsigned int i; + + for (i = 0; i < hws->cur_max_video_ch; ++i) { + struct hws_video *v = &hws->video[i]; + + WRITE_ONCE(v->cap_active, false); + WRITE_ONCE(v->stop_requested, true); + } + + smp_wmb(); /* make flags visible before we touch MMIO/queues */ +} + +/* Drain engines + ISR/BH after flags are published. */ +static void hws_drain_after_stop(struct hws_pcie_dev *hws) +{ + u32 ackmask = 0; + unsigned int i; + u64 start_ns = ktime_get_mono_fast_ns(); + + /* Mask device enables: no new DMA starts. */ + writel(0x0, hws->bar0_base + HWS_REG_VCAP_ENABLE); + (void)readl(hws->bar0_base + HWS_REG_INT_STATUS); /* flush */ + + /* Let any in-flight DMAs finish (best-effort). */ + (void)hws_check_busy(hws); + + /* Ack any latched VDONE. */ + for (i = 0; i < hws->cur_max_video_ch; ++i) + ackmask |= HWS_INT_VDONE_BIT(i); + if (ackmask) { + writel(ackmask, hws->bar0_base + HWS_REG_INT_STATUS); + (void)readl(hws->bar0_base + HWS_REG_INT_STATUS); + } + + /* Ensure no hard IRQ is still running. */ + if (hws->irq >= 0) + synchronize_irq(hws->irq); + + dev_dbg(&hws->pdev->dev, "lifecycle:drain-after-stop:done (%lluus)\n", + hws_elapsed_us(start_ns)); +} + +static void hws_stop_device(struct hws_pcie_dev *hws) +{ + u32 status = readl(hws->bar0_base + HWS_REG_SYS_STATUS); + u64 start_ns = ktime_get_mono_fast_ns(); + bool live = status != 0xFFFFFFFF; + + dev_dbg(&hws->pdev->dev, "%s: status=0x%08x\n", __func__, status); + if (!live) { + hws->pci_lost = true; + goto out; + } + hws_log_lifecycle_snapshot(hws, "stop-device", "begin"); + + /* Make ISR/BH a no-op, then drain engines/IRQ. */ + hws_publish_stop_flags(hws); + hws_drain_after_stop(hws); + + /* 1) Stop the on-board DSP */ + hws_stop_dsp(hws); + +out: + hws->start_run = false; + if (live) + hws_log_lifecycle_snapshot(hws, "stop-device", "end"); + else + dev_dbg(&hws->pdev->dev, "lifecycle:stop-device:device-lost\n"); + dev_dbg(&hws->pdev->dev, "lifecycle:stop-device:done (%lluus)\n", + hws_elapsed_us(start_ns)); + dev_dbg(&hws->pdev->dev, "%s: complete\n", __func__); +} + +static int hws_quiesce_for_transition(struct hws_pcie_dev *hws, + const char *action, + bool stop_thread) +{ + struct device *dev = &hws->pdev->dev; + u64 start_ns = ktime_get_mono_fast_ns(); + u64 step_ns; + int vret; + + hws_log_lifecycle_snapshot(hws, action, "begin"); + + step_ns = ktime_get_mono_fast_ns(); + hws_block_hotpaths(hws); + dev_dbg(dev, "lifecycle:%s:block-hotpaths (%lluus)\n", action, + hws_elapsed_us(step_ns)); + hws_log_lifecycle_snapshot(hws, action, "blocked"); + + if (stop_thread) { + step_ns = ktime_get_mono_fast_ns(); + hws_stop_kthread_action(hws); + dev_dbg(dev, "lifecycle:%s:stop-kthread (%lluus)\n", action, + hws_elapsed_us(step_ns)); + } + + step_ns = ktime_get_mono_fast_ns(); + vret = hws_video_quiesce(hws, action); + dev_dbg(dev, "lifecycle:%s:video-quiesce ret=%d (%lluus)\n", action, + vret, hws_elapsed_us(step_ns)); + if (vret) + dev_warn(dev, "lifecycle:%s video quiesce returned %d\n", + action, vret); + + step_ns = ktime_get_mono_fast_ns(); + hws_stop_device(hws); + dev_dbg(dev, "lifecycle:%s:stop-device (%lluus)\n", action, + hws_elapsed_us(step_ns)); + hws_log_lifecycle_snapshot(hws, action, "end"); + dev_dbg(dev, "lifecycle:%s:quiesce-done ret=%d (%lluus)\n", action, + vret, hws_elapsed_us(start_ns)); + + return vret; +} + +static void hws_remove(struct pci_dev *pdev) +{ + struct hws_pcie_dev *hws = pci_get_drvdata(pdev); + u64 start_ns; + + if (!hws) + return; + + start_ns = ktime_get_mono_fast_ns(); + dev_info(&pdev->dev, "lifecycle:remove begin\n"); + hws_log_lifecycle_snapshot(hws, "remove", "begin"); + + /* Stop the monitor thread before tearing down V4L2/vb2 objects. */ + hws_block_hotpaths(hws); + hws_stop_kthread_action(hws); + + /* Stop hardware and capture cleanly. */ + hws_stop_device(hws); + + /* Unregister V4L2 resources. */ + hws_video_unregister(hws); + + /* Release seeded DMA buffers */ + hws_free_seed_buffers(hws); + /* kthread is stopped by the devm action registered in probe. */ + hws_log_lifecycle_snapshot(hws, "remove", "end"); + dev_info(&pdev->dev, "lifecycle:remove done (%lluus)\n", + hws_elapsed_us(start_ns)); +} + +#ifdef CONFIG_PM_SLEEP +static int hws_pm_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct hws_pcie_dev *hws = pci_get_drvdata(pdev); + int vret; + u64 start_ns = ktime_get_mono_fast_ns(); + u64 step_ns; + + dev_info(dev, "lifecycle:pm_suspend begin\n"); + vret = hws_quiesce_for_transition(hws, "pm_suspend", false); + + step_ns = ktime_get_mono_fast_ns(); + pci_save_state(pdev); + pci_clear_master(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, PCI_D3hot); + dev_dbg(dev, "lifecycle:pm_suspend:pci-d3hot (%lluus)\n", + hws_elapsed_us(step_ns)); + dev_info(dev, "lifecycle:pm_suspend done ret=%d (%lluus)\n", vret, + hws_elapsed_us(start_ns)); + + return 0; +} + +static int hws_pm_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct hws_pcie_dev *hws = pci_get_drvdata(pdev); + int ret; + u64 start_ns = ktime_get_mono_fast_ns(); + u64 step_ns; + + dev_info(dev, "lifecycle:pm_resume begin\n"); + + /* Back to D0 and re-enable the function */ + step_ns = ktime_get_mono_fast_ns(); + pci_set_power_state(pdev, PCI_D0); + + ret = pci_enable_device(pdev); + if (ret) { + dev_err(dev, "pci_enable_device: %d\n", ret); + return ret; + } + pci_restore_state(pdev); + pci_set_master(pdev); + dev_dbg(dev, "lifecycle:pm_resume:pci-enable (%lluus)\n", + hws_elapsed_us(step_ns)); + + /* Reapply any PCIe tuning lost across D3 */ + enable_pcie_relaxed_ordering(pdev); + + /* Reinitialize chip-side capabilities / registers */ + step_ns = ktime_get_mono_fast_ns(); + read_chip_id(hws); + /* Re-seed BAR remaps/DMA windows and restart the capture core */ + hws_seed_all_channels(hws); + hws_init_video_sys(hws, true); + hws_irq_clear_pending(hws); + dev_dbg(dev, "lifecycle:pm_resume:chip-reinit (%lluus)\n", + hws_elapsed_us(step_ns)); + + /* IRQs can be re-enabled now that MMIO is sane */ + step_ns = ktime_get_mono_fast_ns(); + if (hws->irq >= 0) + enable_irq(hws->irq); + + WRITE_ONCE(hws->suspended, false); + dev_dbg(dev, "lifecycle:pm_resume:irq-unsuspend (%lluus)\n", + hws_elapsed_us(step_ns)); + + /* vb2: nothing mandatory; userspace will STREAMON again when ready */ + step_ns = ktime_get_mono_fast_ns(); + hws_video_pm_resume(hws); + dev_dbg(dev, "lifecycle:pm_resume:video-resume (%lluus)\n", + hws_elapsed_us(step_ns)); + hws_log_lifecycle_snapshot(hws, "pm_resume", "end"); + dev_info(dev, "lifecycle:pm_resume done (%lluus)\n", + hws_elapsed_us(start_ns)); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(hws_pm_ops, hws_pm_suspend, hws_pm_resume); +# define HWS_PM_OPS (&hws_pm_ops) +#else +# define HWS_PM_OPS NULL +#endif + +static void hws_shutdown(struct pci_dev *pdev) +{ + struct hws_pcie_dev *hws = pci_get_drvdata(pdev); + int vret = 0; + u64 start_ns = ktime_get_mono_fast_ns(); + u64 step_ns; + + if (!hws) + return; + + dev_info(&pdev->dev, "lifecycle:pci_shutdown begin\n"); + vret = hws_quiesce_for_transition(hws, "pci_shutdown", true); + + step_ns = ktime_get_mono_fast_ns(); + pci_clear_master(pdev); + dev_dbg(&pdev->dev, "lifecycle:pci_shutdown:clear-master (%lluus)\n", + hws_elapsed_us(step_ns)); + dev_info(&pdev->dev, "lifecycle:pci_shutdown done ret=%d (%lluus)\n", + vret, hws_elapsed_us(start_ns)); +} + +static struct pci_driver hws_pci_driver = { + .name = KBUILD_MODNAME, + .id_table = hws_pci_table, + .probe = hws_probe, + .remove = hws_remove, + .shutdown = hws_shutdown, + .driver = { + .pm = HWS_PM_OPS, + }, +}; + +MODULE_DEVICE_TABLE(pci, hws_pci_table); + +static int __init pcie_hws_init(void) +{ + return pci_register_driver(&hws_pci_driver); +} + +static void __exit pcie_hws_exit(void) +{ + pci_unregister_driver(&hws_pci_driver); +} + +module_init(pcie_hws_init); +module_exit(pcie_hws_exit); + +MODULE_DESCRIPTION(DRV_NAME); +MODULE_AUTHOR("Ben Hoff <hoff.benjamin.k@gmail.com>"); +MODULE_AUTHOR("Sales <sales@avmatrix.com>"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("DMA_BUF"); diff --git a/drivers/media/pci/hws/hws_reg.h b/drivers/media/pci/hws/hws_reg.h new file mode 100644 index 000000000000..e4fb4af44434 --- /dev/null +++ b/drivers/media/pci/hws/hws_reg.h @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _HWS_PCIE_REG_H +#define _HWS_PCIE_REG_H + +#include <linux/bits.h> +#include <linux/sizes.h> + +#define XDMA_CHANNEL_NUM_MAX (1) +#define MAX_NUM_ENGINES (XDMA_CHANNEL_NUM_MAX * 2) + +#define PCIE_BARADDROFSIZE 4u + +#define PCI_BUS_ACCESS_BASE 0x00000000U +#define INT_EN_REG_BASE (PCI_BUS_ACCESS_BASE + 0x0134U) +#define PCIEBR_EN_REG_BASE (PCI_BUS_ACCESS_BASE + 0x0148U) +#define PCIE_INT_DEC_REG_BASE (PCI_BUS_ACCESS_BASE + 0x0138U) + +#define HWS_INT_EN_MASK 0x0003FFFFU + +#define PCIEBAR_AXI_BASE 0x20000000U + +#define CTL_REG_ACC_BASE 0x0 +#define PCI_ADDR_TABLE_BASE CTL_REG_ACC_BASE + +#define CVBS_IN_BASE 0x00004000U +#define CVBS_IN_BUF_BASE (CVBS_IN_BASE + (16U * PCIE_BARADDROFSIZE)) +#define CVBS_IN_BUF_BASE2 (CVBS_IN_BASE + (50U * PCIE_BARADDROFSIZE)) + +/* 2 Mib */ +#define MAX_L_VIDEO_SIZE 0x200000U + +#define PCI_E_BAR_PAGE_SIZE 0x20000000 +#define PCI_E_BAR_ADD_MASK 0xE0000000 +#define PCI_E_BAR_ADD_LOWMASK 0x1FFFFFFF + +#define MAX_VID_CHANNELS 4 + +#define MAX_MM_VIDEO_SIZE SZ_4M + +#define MAX_VIDEO_HW_W 1920 +#define MAX_VIDEO_HW_H 1080 +#define MAX_VIDEO_SCALER_SIZE (1920U * 1080U * 2U) + +#define MIN_VAMP_BRIGHTNESS_UNITS 0 +#define MAX_VAMP_BRIGHTNESS_UNITS 0xff + +#define MIN_VAMP_CONTRAST_UNITS 0 +#define MAX_VAMP_CONTRAST_UNITS 0xff + +#define MIN_VAMP_SATURATION_UNITS 0 +#define MAX_VAMP_SATURATION_UNITS 0xff + +#define MIN_VAMP_HUE_UNITS 0 +#define MAX_VAMP_HUE_UNITS 0xff + +#define HWS_BRIGHTNESS_DEFAULT 0x80 +#define HWS_CONTRAST_DEFAULT 0x80 +#define HWS_SATURATION_DEFAULT 0x80 +#define HWS_HUE_DEFAULT 0x00 + +/* Core/global status. */ +#define HWS_REG_SYS_STATUS (CVBS_IN_BASE + 0 * PCIE_BARADDROFSIZE) +/* bit3: DMA busy, bit2: int, ... */ + +#define HWS_SYS_DMA_BUSY_BIT BIT(3) /* 0x08 = DMA busy flag */ + +#define HWS_REG_DEC_MODE (CVBS_IN_BASE + 0 * PCIE_BARADDROFSIZE) +/* Main control register */ +#define HWS_REG_CTL (CVBS_IN_BASE + 4 * PCIE_BARADDROFSIZE) +#define HWS_CTL_IRQ_ENABLE_BIT BIT(0) /* Global interrupt enable bit */ +/* Write 0x00 to fully reset decoder, + * set bit 31=1 to "start run", + * low byte=0x13 selects YUYV/BT.709/etc, + * in ReadChipId() we also write 0x00 and 0x10 here for chip-ID sequencing. + */ + +/* Per-channel done flags. */ +#define HWS_REG_INT_STATUS (CVBS_IN_BASE + 1 * PCIE_BARADDROFSIZE) +#define HWS_SYS_BUSY_BIT BIT(2) /* matches old 0x04 test */ + +/* Capture enable switches. */ +/* bit0-3: CH0-CH3 video enable */ +#define HWS_REG_VCAP_ENABLE (CVBS_IN_BASE + 2 * PCIE_BARADDROFSIZE) +/* bits0-3: signal present, bits8-11: interlace */ +#define HWS_REG_ACTIVE_STATUS (CVBS_IN_BASE + 5 * PCIE_BARADDROFSIZE) +/* bits0-3: HDCP detected */ +#define HWS_REG_HDCP_STATUS (CVBS_IN_BASE + 8 * PCIE_BARADDROFSIZE) +#define HWS_REG_DMA_MAX_SIZE (CVBS_IN_BASE + 9 * PCIE_BARADDROFSIZE) + +/* Buffer addresses (written once during init/reset). */ +/* Base of host-visible buffer. */ +#define HWS_REG_VBUF1_ADDR (CVBS_IN_BASE + 25 * PCIE_BARADDROFSIZE) +/* Per-channel DMA address. */ +#define HWS_REG_DMA_ADDR(ch) (CVBS_IN_BASE + (26 + (ch)) * PCIE_BARADDROFSIZE) + +/* Per-channel live buffer toggles (read-only). */ +#define HWS_REG_VBUF_TOGGLE(ch) (CVBS_IN_BASE + (32 + (ch)) * PCIE_BARADDROFSIZE) +/* + * Returns 0 or 1 = which half of the video ring the DMA engine is + * currently filling for channel *ch* (0-3). + */ + +/* Per-interrupt bits (video 0-3). */ +#define HWS_INT_VDONE_BIT(ch) BIT(ch) /* 0x01,0x02,0x04,0x08 */ + +#define HWS_REG_INT_ACK (CVBS_IN_BASE + 0x4000 + 1 * PCIE_BARADDROFSIZE) + +/* 16-bit W | 16-bit H. */ +#define HWS_REG_IN_RES(ch) (CVBS_IN_BASE + (90 + (ch) * 2) * PCIE_BARADDROFSIZE) +/* B|C|H|S packed bytes. */ +#define HWS_REG_BCHS(ch) (CVBS_IN_BASE + (91 + (ch) * 2) * PCIE_BARADDROFSIZE) + +/* Input fps. */ +#define HWS_REG_FRAME_RATE(ch) (CVBS_IN_BASE + (110 + (ch)) * PCIE_BARADDROFSIZE) +/* Programmed out W|H. */ +#define HWS_REG_OUT_RES(ch) (CVBS_IN_BASE + (120 + (ch)) * PCIE_BARADDROFSIZE) +/* Programmed out fps. */ +#define HWS_REG_OUT_FRAME_RATE(ch) (CVBS_IN_BASE + (130 + (ch)) * PCIE_BARADDROFSIZE) + +/* Device version/port ID/subversion register. */ +#define HWS_REG_DEVICE_INFO (CVBS_IN_BASE + 88 * PCIE_BARADDROFSIZE) +/* + * Reading this 32-bit word returns: + * bits 7:0 = "device version" + * bits 15:8 = "device sub-version" + * bits 23:24 = "HW key / port ID" etc. + * bits 31:28 = "support YV12" flags + */ + +/* Convenience aliases for individual channels. */ +#define HWS_REG_VBUF_TOGGLE_CH0 HWS_REG_VBUF_TOGGLE(0) +#define HWS_REG_VBUF_TOGGLE_CH1 HWS_REG_VBUF_TOGGLE(1) +#define HWS_REG_VBUF_TOGGLE_CH2 HWS_REG_VBUF_TOGGLE(2) +#define HWS_REG_VBUF_TOGGLE_CH3 HWS_REG_VBUF_TOGGLE(3) + +#endif /* _HWS_PCIE_REG_H */ diff --git a/drivers/media/pci/hws/hws_v4l2_ioctl.c b/drivers/media/pci/hws/hws_v4l2_ioctl.c new file mode 100644 index 000000000000..0303e311ee4a --- /dev/null +++ b/drivers/media/pci/hws/hws_v4l2_ioctl.c @@ -0,0 +1,919 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/pci.h> +#include <linux/errno.h> +#include <linux/io.h> +#include <linux/math64.h> + +#include <media/v4l2-ioctl.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-dv-timings.h> +#include <media/videobuf2-core.h> +#include <media/videobuf2-v4l2.h> + +#include "hws.h" +#include "hws_reg.h" +#include "hws_video.h" +#include "hws_v4l2_ioctl.h" + +struct hws_dv_mode { + struct v4l2_dv_timings timings; + u32 refresh_hz; +}; + +static const struct hws_dv_mode * +hws_find_dv_by_wh(u32 w, u32 h, bool interlaced); +static const struct hws_dv_mode * +hws_find_dv_by_wh_fps(u32 w, u32 h, bool interlaced, u32 fps); +static u32 hws_get_live_fps(struct hws_video *vid); +static u32 hws_input_status(struct hws_video *vid); +static int hws_fill_dv_timings(u32 w, u32 h, bool interlace, u32 fps, + struct v4l2_dv_timings *timings); + +static const struct hws_dv_mode hws_dv_modes[] = { + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 1920, + .height = 1080, + .hfrontporch = 88, + .hsync = 44, + .hbackporch = 148, + .vfrontporch = 4, + .vsync = 5, + .vbackporch = 36, + .pixelclock = 148500000, + .polarities = V4L2_DV_VSYNC_POS_POL | + V4L2_DV_HSYNC_POS_POL, + .interlaced = 0, + }, + }, + 60, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 1920, + .height = 1080, + .hfrontporch = 88, + .hsync = 44, + .hbackporch = 148, + .vfrontporch = 4, + .vsync = 5, + .vbackporch = 36, + .pixelclock = 74250000, + .polarities = V4L2_DV_VSYNC_POS_POL | + V4L2_DV_HSYNC_POS_POL, + .interlaced = 0, + }, + }, + 30, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 1280, + .height = 720, + .hfrontporch = 110, + .hsync = 40, + .hbackporch = 220, + .vfrontporch = 5, + .vsync = 5, + .vbackporch = 20, + .pixelclock = 74250000, + .polarities = V4L2_DV_VSYNC_POS_POL | + V4L2_DV_HSYNC_POS_POL, + .interlaced = 0, + }, + }, + 60, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 720, + .height = 480, + .interlaced = 0, + }, + }, + 60, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 720, + .height = 576, + .interlaced = 0, + }, + }, + 50, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 800, + .height = 600, + .interlaced = 0, + }, + }, + 60, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 640, + .height = 480, + .interlaced = 0, + }, + }, + 60, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 1024, + .height = 768, + .interlaced = 0, + }, + }, + 60, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 1280, + .height = 768, + .interlaced = 0, + }, + }, + 60, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 1280, + .height = 800, + .interlaced = 0, + }, + }, + 60, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 1280, + .height = 1024, + .interlaced = 0, + }, + }, + 60, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 1360, + .height = 768, + .interlaced = 0, + }, + }, + 60, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 1440, + .height = 900, + .interlaced = 0, + }, + }, + 60, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 1680, + .height = 1050, + .interlaced = 0, + }, + }, + 60, + }, + /* Portrait */ + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 1080, + .height = 1920, + .interlaced = 0, + }, + }, + 60, + }, +}; + +static const size_t hws_dv_modes_cnt = ARRAY_SIZE(hws_dv_modes); + +/* YUYV: 16 bpp; align to 64 as you did elsewhere */ +static inline u32 hws_calc_bpl_yuyv(u32 w) { return ALIGN(w * 2, 64); } +static inline u32 hws_calc_size_yuyv(u32 w, u32 h) { return hws_calc_bpl_yuyv(w) * h; } +static inline u32 hws_calc_half_size(u32 sizeimage) +{ + return sizeimage / 2; +} + +static inline void hws_hw_write_bchs(struct hws_pcie_dev *hws, unsigned int ch, + u8 br, u8 co, u8 hu, u8 sa) +{ + u32 packed = (sa << 24) | (hu << 16) | (co << 8) | br; + + if (!hws || !hws->bar0_base || ch >= hws->max_channels) + return; + writel_relaxed(packed, hws->bar0_base + HWS_REG_BCHS(ch)); + (void)readl(hws->bar0_base + HWS_REG_BCHS(ch)); /* post write */ +} + +/* Helper: find a supported DV mode by W/H + interlace flag */ +static const struct hws_dv_mode * +hws_match_supported_dv(const struct v4l2_dv_timings *req) +{ + const struct v4l2_bt_timings *bt; + u32 fps; + + if (!req || req->type != V4L2_DV_BT_656_1120) + return NULL; + + bt = &req->bt; + fps = 0; + if (bt->pixelclock) { + u32 total_w = bt->width + bt->hfrontporch + bt->hsync + + bt->hbackporch; + u32 total_h = bt->height + bt->vfrontporch + bt->vsync + + bt->vbackporch; + + if (total_w && total_h) + fps = DIV_ROUND_CLOSEST_ULL((u64)bt->pixelclock, + (u64)total_w * total_h); + } + if (fps) { + const struct hws_dv_mode *exact = + hws_find_dv_by_wh_fps(bt->width, bt->height, + !!bt->interlaced, fps); + if (exact) + return exact; + } + return hws_find_dv_by_wh(bt->width, bt->height, !!bt->interlaced); +} + +/* Helper: find a supported DV mode by W/H + interlace flag */ +static const struct hws_dv_mode * +hws_find_dv_by_wh(u32 w, u32 h, bool interlaced) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(hws_dv_modes); i++) { + const struct hws_dv_mode *t = &hws_dv_modes[i]; + const struct v4l2_bt_timings *bt = &t->timings.bt; + + if (t->timings.type != V4L2_DV_BT_656_1120) + continue; + + if (bt->width == w && bt->height == h && + !!bt->interlaced == interlaced) + return t; + } + return NULL; +} + +static const struct hws_dv_mode * +hws_find_dv_by_wh_fps(u32 w, u32 h, bool interlaced, u32 fps) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(hws_dv_modes); i++) { + const struct hws_dv_mode *t = &hws_dv_modes[i]; + const struct v4l2_bt_timings *bt = &t->timings.bt; + + if (t->timings.type != V4L2_DV_BT_656_1120) + continue; + + if (bt->width == w && bt->height == h && + !!bt->interlaced == interlaced && + t->refresh_hz == fps) + return t; + } + return NULL; +} + +static bool hws_get_live_dv_geometry(struct hws_video *vid, + u32 *w, u32 *h, bool *interlaced) +{ + struct hws_pcie_dev *pdx; + u32 reg; + + if (!vid) + return false; + + pdx = vid->parent; + if (!pdx || !pdx->bar0_base) + return false; + + reg = readl(pdx->bar0_base + HWS_REG_IN_RES(vid->channel_index)); + if (!reg || reg == 0xFFFFFFFF) + return false; + + if (w) + *w = reg & 0xFFFF; + if (h) + *h = (reg >> 16) & 0xFFFF; + if (interlaced) { + reg = readl(pdx->bar0_base + HWS_REG_ACTIVE_STATUS); + *interlaced = !!(reg & BIT(8 + vid->channel_index)); + } + return true; +} + +static u32 hws_get_live_fps(struct hws_video *vid) +{ + struct hws_pcie_dev *pdx; + u32 fps; + + if (!vid) + return 0; + + pdx = vid->parent; + if (!pdx || !pdx->bar0_base) + return 0; + + fps = readl(pdx->bar0_base + HWS_REG_FRAME_RATE(vid->channel_index)); + if (!fps || fps == 0xFFFFFFFF || fps > 240) + return 0; + + return fps; +} + +static u32 hws_pick_fps_from_mode(u32 w, u32 h, bool interlaced) +{ + const struct hws_dv_mode *m = hws_find_dv_by_wh(w, h, interlaced); + + if (m && m->refresh_hz) + return m->refresh_hz; + /* Fallback to a sane default */ + return 60; +} + +static int hws_fill_dv_timings(u32 w, u32 h, bool interlace, u32 fps, + struct v4l2_dv_timings *timings) +{ + const struct hws_dv_mode *m; + + m = fps ? hws_find_dv_by_wh_fps(w, h, interlace, fps) : NULL; + if (!m) + m = hws_find_dv_by_wh(w, h, interlace); + if (!m) + return -ENOLINK; + + *timings = m->timings; + return 0; +} + +static u32 hws_input_status(struct hws_video *vid) +{ + struct hws_pcie_dev *pdx; + u32 reg; + + if (!vid) + return V4L2_IN_ST_NO_SIGNAL; + + pdx = vid->parent; + if (!pdx || !pdx->bar0_base) + return V4L2_IN_ST_NO_SIGNAL; + + reg = readl(pdx->bar0_base + HWS_REG_ACTIVE_STATUS); + if (reg == 0xffffffff) + return V4L2_IN_ST_NO_SIGNAL; + + return (reg & BIT(vid->channel_index)) ? 0 : V4L2_IN_ST_NO_SIGNAL; +} + +/* Query the *current detected* DV timings on the input. + * If you have a real hardware detector, call it here; otherwise we + * derive from the cached pix state and map to the closest supported DV mode. + */ +int hws_vidioc_query_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings) +{ + struct hws_video *vid = video_drvdata(file); + u32 w, h; + u32 fps; + bool interlace; + + if (!timings) + return -EINVAL; + + w = vid->pix.width; + h = vid->pix.height; + interlace = vid->pix.interlaced; + hws_get_live_dv_geometry(vid, &w, &h, &interlace); + fps = hws_get_live_fps(vid); + if (!fps) + fps = vid->current_fps ? vid->current_fps : + hws_pick_fps_from_mode(w, h, interlace); + + return hws_fill_dv_timings(w, h, interlace, fps, timings); +} + +/* Enumerate the Nth supported DV timings from our static table. */ +int hws_vidioc_enum_dv_timings(struct file *file, void *fh, + struct v4l2_enum_dv_timings *edv) +{ + struct hws_video *vid = video_drvdata(file); + const struct hws_dv_mode *m; + u32 w, h; + u32 fps; + bool interlace; + + if (!edv) + return -EINVAL; + + if (edv->pad) + return -EINVAL; + + w = 0; + h = 0; + interlace = false; + if (hws_get_live_dv_geometry(vid, &w, &h, &interlace)) { + fps = hws_get_live_fps(vid); + if (!fps) + fps = vid->current_fps ? vid->current_fps : + hws_pick_fps_from_mode(w, h, interlace); + m = fps ? hws_find_dv_by_wh_fps(w, h, interlace, fps) : NULL; + if (!m) + m = hws_find_dv_by_wh(w, h, interlace); + if (m) { + if (edv->index) + return -EINVAL; + edv->timings = m->timings; + return 0; + } + } + + if (edv->index >= hws_dv_modes_cnt) + return -EINVAL; + + edv->timings = hws_dv_modes[edv->index].timings; + return 0; +} + +/* Get the *currently configured* DV timings. */ +int hws_vidioc_g_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings) +{ + struct hws_video *vid = video_drvdata(file); + u32 w, h; + u32 fps; + bool interlace; + + if (!timings) + return -EINVAL; + + w = vid->pix.width; + h = vid->pix.height; + interlace = vid->pix.interlaced; + if (hws_get_live_dv_geometry(vid, &w, &h, &interlace)) { + fps = hws_get_live_fps(vid); + if (!fps) + fps = vid->current_fps ? vid->current_fps : + hws_pick_fps_from_mode(w, h, interlace); + return hws_fill_dv_timings(w, h, interlace, fps, timings); + } + + *timings = vid->cur_dv_timings; + return 0; +} + +static inline void hws_set_colorimetry_state(struct hws_pix_state *p) +{ + bool sd = p->height <= 576; + + p->colorspace = sd ? V4L2_COLORSPACE_SMPTE170M : V4L2_COLORSPACE_REC709; + p->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + p->quantization = V4L2_QUANTIZATION_FULL_RANGE; + p->xfer_func = V4L2_XFER_FUNC_DEFAULT; +} + +/* Set DV timings: must match one of our supported modes. + * If buffers are queued and this implies a size change, we reject with -EBUSY. + * Otherwise we update pix state and (optionally) reprogram the HW. + */ +int hws_vidioc_s_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings) +{ + struct hws_video *vid = video_drvdata(file); + const struct hws_dv_mode *m; + const struct v4l2_bt_timings *bt; + u32 new_w, new_h; + bool interlaced; + int ret = 0; + unsigned long was_busy; + u32 live_w, live_h; + u32 live_fps; + bool live_interlaced; + bool live_present; + + if (!timings) + return -EINVAL; + + m = hws_match_supported_dv(timings); + if (!m) + return -EINVAL; + + bt = &m->timings.bt; + if (bt->interlaced) + return -EINVAL; /* only progressive modes are advertised */ + new_w = bt->width; + new_h = bt->height; + interlaced = false; + + lockdep_assert_held(&vid->state_lock); + live_present = hws_get_live_dv_geometry(vid, &live_w, &live_h, + &live_interlaced); + + /* If vb2 has active buffers and size would change, reject. */ + was_busy = vb2_is_busy(&vid->buffer_queue); + if (was_busy && + (new_w != vid->pix.width || new_h != vid->pix.height || + interlaced != vid->pix.interlaced)) { + ret = -EBUSY; + return ret; + } + + /* When a live input signal is present, the receiver owns the timing. + * Allow setting the already-active timings so v4l2-compliance can + * round-trip them, but reject attempts to retime the live source. + */ + if (live_present) { + live_fps = hws_get_live_fps(vid); + if (!live_fps) + live_fps = vid->current_fps ? vid->current_fps : + hws_pick_fps_from_mode(live_w, live_h, + live_interlaced); + if (live_w == new_w && live_h == new_h && + live_interlaced == interlaced && + m->refresh_hz == live_fps) + return 0; + return -EBUSY; + } + + /* Update software pixel state (and recalc sizes) */ + vid->pix.width = new_w; + vid->pix.height = new_h; + vid->pix.field = interlaced ? V4L2_FIELD_INTERLACED + : V4L2_FIELD_NONE; + vid->pix.interlaced = interlaced; + vid->pix.fourcc = V4L2_PIX_FMT_YUYV; + + hws_set_colorimetry_state(&vid->pix); + + /* Recompute stride, sizeimage, and half_size. */ + vid->pix.bytesperline = hws_calc_bpl_yuyv(new_w); + vid->pix.sizeimage = hws_calc_size_yuyv(new_w, new_h); + vid->pix.half_size = hws_calc_half_size(vid->pix.sizeimage); + vid->cur_dv_timings = m->timings; + vid->current_fps = m->refresh_hz; + return ret; +} + +/* Report DV timings capability: advertise BT.656/1120 with + * the min/max WxH derived from our table and basic progressive support. + */ +int hws_vidioc_dv_timings_cap(struct file *file, void *fh, + struct v4l2_dv_timings_cap *cap) +{ + u32 min_w = ~0U, min_h = ~0U; + u32 max_w = 0, max_h = 0; + size_t i, n = 0; + + if (!cap) + return -EINVAL; + + memset(cap, 0, sizeof(*cap)); + cap->type = V4L2_DV_BT_656_1120; + + for (i = 0; i < ARRAY_SIZE(hws_dv_modes); i++) { + const struct v4l2_bt_timings *bt = &hws_dv_modes[i].timings.bt; + + if (hws_dv_modes[i].timings.type != V4L2_DV_BT_656_1120) + continue; + n++; + + if (bt->width < min_w) + min_w = bt->width; + if (bt->height < min_h) + min_h = bt->height; + if (bt->width > max_w) + max_w = bt->width; + if (bt->height > max_h) + max_h = bt->height; + } + + /* If the table was empty, fail gracefully. */ + if (!n || min_w == U32_MAX) + return -ENODATA; + + cap->bt.min_width = min_w; + cap->bt.max_width = max_w; + cap->bt.min_height = min_h; + cap->bt.max_height = max_h; + + /* We support both CEA-861- and VESA-style modes in the list. */ + cap->bt.standards = + V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT; + + /* Only progressive modes are advertised. */ + cap->bt.capabilities = V4L2_DV_BT_CAP_PROGRESSIVE; + + /* Leave pixelclock/porch limits unconstrained (0) for now. */ + return 0; +} + +static int hws_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct hws_video *vid = + container_of(ctrl->handler, struct hws_video, control_handler); + struct hws_pcie_dev *pdx = vid->parent; + bool program = false; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + vid->current_brightness = ctrl->val; + program = true; + break; + case V4L2_CID_CONTRAST: + vid->current_contrast = ctrl->val; + program = true; + break; + case V4L2_CID_SATURATION: + vid->current_saturation = ctrl->val; + program = true; + break; + case V4L2_CID_HUE: + vid->current_hue = ctrl->val; + program = true; + break; + default: + return -EINVAL; + } + + if (program) { + hws_hw_write_bchs(pdx, vid->channel_index, + (u8)vid->current_brightness, + (u8)vid->current_contrast, + (u8)vid->current_hue, + (u8)vid->current_saturation); + } + return 0; +} + +const struct v4l2_ctrl_ops hws_ctrl_ops = { + .s_ctrl = hws_s_ctrl, +}; + +int hws_vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) +{ + struct hws_video *vid = video_drvdata(file); + int vi_index = vid->channel_index + 1; /* keep it simple */ + + strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); + snprintf(cap->card, sizeof(cap->card), + "AVMatrix HWS Capture %d", vi_index); + return 0; +} + +int hws_vidioc_enum_fmt_vid_cap(struct file *file, void *priv_fh, struct v4l2_fmtdesc *f) +{ + if (f->index != 0) + return -EINVAL; /* only one format */ + + f->pixelformat = V4L2_PIX_FMT_YUYV; + return 0; +} + +int hws_vidioc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct hws_video *vid = video_drvdata(file); + + fmt->fmt.pix.width = vid->pix.width; + fmt->fmt.pix.height = vid->pix.height; + fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; + fmt->fmt.pix.field = vid->pix.field; + fmt->fmt.pix.bytesperline = vid->pix.bytesperline; + fmt->fmt.pix.sizeimage = vid->pix.sizeimage; + fmt->fmt.pix.colorspace = vid->pix.colorspace; + fmt->fmt.pix.ycbcr_enc = vid->pix.ycbcr_enc; + fmt->fmt.pix.quantization = vid->pix.quantization; + fmt->fmt.pix.xfer_func = vid->pix.xfer_func; + return 0; +} + +static inline void hws_set_colorimetry_fmt(struct v4l2_pix_format *p) +{ + bool sd = p->height <= 576; + + p->colorspace = sd ? V4L2_COLORSPACE_SMPTE170M : V4L2_COLORSPACE_REC709; + p->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + p->quantization = V4L2_QUANTIZATION_FULL_RANGE; + p->xfer_func = V4L2_XFER_FUNC_DEFAULT; +} + +int hws_vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + struct hws_video *vid = file ? video_drvdata(file) : NULL; + struct hws_pcie_dev *pdev = vid ? vid->parent : NULL; + struct v4l2_pix_format *pix = &f->fmt.pix; + u32 req_w = pix->width, req_h = pix->height; + u32 w, h, min_bpl, bpl; + size_t size; /* wider than u32 for overflow check */ + size_t max_frame = pdev ? pdev->max_hw_video_buf_sz : MAX_MM_VIDEO_SIZE; + + /* Only YUYV */ + pix->pixelformat = V4L2_PIX_FMT_YUYV; + + /* Defaults then clamp */ + w = (req_w ? req_w : 640); + h = (req_h ? req_h : 480); + if (w > MAX_VIDEO_HW_W) + w = MAX_VIDEO_HW_W; + if (h > MAX_VIDEO_HW_H) + h = MAX_VIDEO_HW_H; + if (!w) + w = 640; /* hard fallback in case macros are odd */ + if (!h) + h = 480; + + /* Field policy */ + pix->field = V4L2_FIELD_NONE; + + /* Stride policy for packed 16bpp, 64B align */ + min_bpl = ALIGN(w * 2, 64); + + /* Bound requested bpl to something sane, then align */ + bpl = pix->bytesperline; + if (bpl < min_bpl) { + bpl = min_bpl; + } else { + /* Cap at 16x width to avoid silly values that overflow sizeimage */ + u32 max_bpl = ALIGN(w * 2 * 16, 64); + + if (bpl > max_bpl) + bpl = max_bpl; + bpl = ALIGN(bpl, 64); + } + if (h && max_frame) { + size_t max_bpl_hw = max_frame / h; + + if (max_bpl_hw < min_bpl) + return -ERANGE; + max_bpl_hw = rounddown(max_bpl_hw, 64); + if (!max_bpl_hw) + return -ERANGE; + if (bpl > max_bpl_hw) { + if (pdev) + dev_dbg(&pdev->pdev->dev, + "try_fmt: clamp bpl %u -> %zu due to hw buf cap %zu\n", + bpl, max_bpl_hw, max_frame); + bpl = (u32)max_bpl_hw; + } + } + size = (size_t)bpl * (size_t)h; + if (size > max_frame) + return -ERANGE; + + pix->width = w; + pix->height = h; + pix->bytesperline = bpl; + pix->sizeimage = (u32)size; /* logical size, not page-aligned */ + + hws_set_colorimetry_fmt(pix); + if (pdev) + dev_dbg(&pdev->pdev->dev, + "try_fmt: w=%u h=%u bpl=%u size=%u field=%u\n", + pix->width, pix->height, pix->bytesperline, + pix->sizeimage, pix->field); + return 0; +} + +int hws_vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) +{ + struct hws_video *vid = video_drvdata(file); + int ret; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + /* Normalize the request */ + ret = hws_vidioc_try_fmt_vid_cap(file, priv, f); + if (ret) + return ret; + + /* Don't allow buffer layout changes while buffers are queued. */ + if (vb2_is_busy(&vid->buffer_queue)) { + if (f->fmt.pix.width != vid->pix.width || + f->fmt.pix.height != vid->pix.height || + f->fmt.pix.bytesperline != vid->pix.bytesperline) + return -EBUSY; + } + + /* Apply to driver state */ + vid->pix.width = f->fmt.pix.width; + vid->pix.height = f->fmt.pix.height; + vid->pix.fourcc = V4L2_PIX_FMT_YUYV; + vid->pix.field = f->fmt.pix.field; + vid->pix.colorspace = f->fmt.pix.colorspace; + vid->pix.ycbcr_enc = f->fmt.pix.ycbcr_enc; + vid->pix.quantization = f->fmt.pix.quantization; + vid->pix.xfer_func = f->fmt.pix.xfer_func; + + /* Update negotiated buffer sizes. */ + vid->pix.bytesperline = f->fmt.pix.bytesperline; /* aligned */ + vid->pix.sizeimage = f->fmt.pix.sizeimage; /* logical */ + vid->pix.half_size = hws_calc_half_size(vid->pix.sizeimage); + vid->pix.interlaced = false; + /* S_FMT negotiates buffer layout only. Keep detector-owned DV timing + * state unchanged so a harmless restart cannot clobber the live FPS. + */ + /* Or: + * hws_calc_sizeimage(vid, vid->pix.width, vid->pix.height, false); + */ + + dev_dbg(&vid->parent->pdev->dev, + "s_fmt: w=%u h=%u bpl=%u size=%u\n", + vid->pix.width, vid->pix.height, vid->pix.bytesperline, + vid->pix.sizeimage); + + return 0; +} + +int hws_vidioc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *param) +{ + struct hws_video *vid = video_drvdata(file); + u32 fps; + + if (param->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + fps = hws_get_live_fps(vid); + if (!fps) + fps = vid->current_fps ? vid->current_fps : 60; + + /* HDMI receivers report the detected frame period, they don't set it. */ + param->parm.capture.capability = 0; + param->parm.capture.capturemode = 0; + param->parm.capture.timeperframe.numerator = 1; + param->parm.capture.timeperframe.denominator = fps; + param->parm.capture.extendedmode = 0; + param->parm.capture.readbuffers = 0; + + return 0; +} + +int hws_vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + struct hws_video *vid = video_drvdata(file); + + if (input->index) + return -EINVAL; + input->type = V4L2_INPUT_TYPE_CAMERA; + strscpy(input->name, KBUILD_MODNAME, sizeof(input->name)); + input->capabilities = V4L2_IN_CAP_DV_TIMINGS; + input->status = hws_input_status(vid); + + return 0; +} + +int hws_vidioc_g_input(struct file *file, void *priv, unsigned int *index) +{ + *index = 0; + return 0; +} + +int hws_vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + return i ? -EINVAL : 0; +} diff --git a/drivers/media/pci/hws/hws_v4l2_ioctl.h b/drivers/media/pci/hws/hws_v4l2_ioctl.h new file mode 100644 index 000000000000..53044f78d6fa --- /dev/null +++ b/drivers/media/pci/hws/hws_v4l2_ioctl.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef HWS_V4L2_IOCTL_H +#define HWS_V4L2_IOCTL_H + +#include <media/v4l2-ctrls.h> +#include <linux/fs.h> + +extern const struct v4l2_ctrl_ops hws_ctrl_ops; + +int hws_vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap); +int hws_vidioc_enum_fmt_vid_cap(struct file *file, void *priv_fh, struct v4l2_fmtdesc *f); +int hws_vidioc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt); +int hws_vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f); +int hws_vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorms); +int hws_vidioc_s_std(struct file *file, void *priv, v4l2_std_id tvnorms); +int hws_vidioc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *setfps); +int hws_vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *i); +int hws_vidioc_g_input(struct file *file, void *priv, unsigned int *i); +int hws_vidioc_s_input(struct file *file, void *priv, unsigned int i); +int hws_vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *a); +int hws_vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a); +int hws_vidioc_dv_timings_cap(struct file *file, void *fh, + struct v4l2_dv_timings_cap *cap); +int hws_vidioc_s_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings); + +int hws_vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *a); +int hws_vidioc_g_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings); +int hws_vidioc_enum_dv_timings(struct file *file, void *fh, + struct v4l2_enum_dv_timings *edv); +int hws_vidioc_query_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings); +int hws_vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f); + +#endif diff --git a/drivers/media/pci/hws/hws_video.c b/drivers/media/pci/hws/hws_video.c new file mode 100644 index 000000000000..18e4bc6901d3 --- /dev/null +++ b/drivers/media/pci/hws/hws_video.c @@ -0,0 +1,1490 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include <linux/pci.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/compiler.h> +#include <linux/overflow.h> +#include <linux/delay.h> +#include <linux/bits.h> +#include <linux/jiffies.h> +#include <linux/ktime.h> +#include <linux/math64.h> +#include <linux/interrupt.h> +#include <linux/moduleparam.h> + +#include <media/v4l2-ioctl.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-event.h> +#include <media/videobuf2-v4l2.h> +#include <media/v4l2-device.h> +#include <media/videobuf2-dma-contig.h> + +#include "hws.h" +#include "hws_reg.h" +#include "hws_video.h" +#include "hws_irq.h" +#include "hws_v4l2_ioctl.h" + +#define HWS_REMAP_SLOT_OFF(ch) (0x208 + (ch) * 8) /* one 64-bit slot per ch */ +#define HWS_BUF_BASE_OFF(ch) (CVBS_IN_BUF_BASE + (ch) * PCIE_BARADDROFSIZE) +#define HWS_HALF_SZ_OFF(ch) (CVBS_IN_BUF_BASE2 + (ch) * PCIE_BARADDROFSIZE) + +static void update_live_resolution(struct hws_pcie_dev *pdx, unsigned int ch, + bool interlace); +static bool hws_read_active_state(struct hws_pcie_dev *pdx, unsigned int ch, + bool *interlace); +static void handle_hwv2_path(struct hws_pcie_dev *hws, unsigned int ch); +static void handle_legacy_path(struct hws_pcie_dev *hws, unsigned int ch); +static u32 hws_calc_sizeimage(struct hws_video *v, u16 w, u16 h, + bool interlaced); + +/* DMA helper functions */ +static void hws_program_dma_window(struct hws_video *vid, dma_addr_t dma); +static struct hwsvideo_buffer * +hws_take_queued_buffer_locked(struct hws_video *vid); + +static unsigned long long hws_elapsed_us(u64 start_ns) +{ + return div_u64(ktime_get_mono_fast_ns() - start_ns, 1000); +} + +static inline bool list_node_unlinked(const struct list_head *n) +{ + return n->next == LIST_POISON1 || n->prev == LIST_POISON2; +} + +static bool dma_window_verify; +module_param_named(dma_window_verify, dma_window_verify, bool, 0644); +MODULE_PARM_DESC(dma_window_verify, + "Read back DMA window registers after programming (debug)"); + +void hws_set_dma_doorbell(struct hws_pcie_dev *hws, unsigned int ch, + dma_addr_t dma, const char *tag) +{ + iowrite32(lower_32_bits(dma), hws->bar0_base + HWS_REG_DMA_ADDR(ch)); + dev_dbg(&hws->pdev->dev, "dma_doorbell ch%u: dma=0x%llx tag=%s\n", ch, + (u64)dma, tag ? tag : ""); +} + +static void hws_program_dma_window(struct hws_video *vid, dma_addr_t dma) +{ + const u32 addr_mask = PCI_E_BAR_ADD_MASK; + const u32 addr_low_mask = PCI_E_BAR_ADD_LOWMASK; + struct hws_pcie_dev *hws = vid->parent; + unsigned int ch = vid->channel_index; + u32 table_off = HWS_REMAP_SLOT_OFF(ch); + u32 lo = lower_32_bits(dma); + u32 hi = upper_32_bits(dma); + u32 pci_addr = lo & addr_low_mask; + u32 page_lo = lo & addr_mask; + + bool wrote = false; + + /* Remap entry only when DMA crosses into a new 512 MB page */ + if (!vid->window_valid || vid->last_dma_hi != hi || + vid->last_dma_page != page_lo) { + writel(hi, hws->bar0_base + PCI_ADDR_TABLE_BASE + table_off); + writel(page_lo, + hws->bar0_base + PCI_ADDR_TABLE_BASE + table_off + + PCIE_BARADDROFSIZE); + vid->last_dma_hi = hi; + vid->last_dma_page = page_lo; + wrote = true; + } + + /* Base pointer only needs low 29 bits */ + if (!vid->window_valid || vid->last_pci_addr != pci_addr) { + writel((ch + 1) * PCIEBAR_AXI_BASE + pci_addr, + hws->bar0_base + HWS_BUF_BASE_OFF(ch)); + vid->last_pci_addr = pci_addr; + wrote = true; + } + + /* Half-size only changes when resolution changes */ + if (!vid->window_valid || vid->last_half16 != vid->pix.half_size / 16) { + writel(vid->pix.half_size / 16, + hws->bar0_base + HWS_HALF_SZ_OFF(ch)); + vid->last_half16 = vid->pix.half_size / 16; + wrote = true; + } + + vid->window_valid = true; + + if (dma_window_verify && wrote) { + u32 r_hi = + readl(hws->bar0_base + PCI_ADDR_TABLE_BASE + table_off); + u32 r_lo = + readl(hws->bar0_base + PCI_ADDR_TABLE_BASE + table_off + + PCIE_BARADDROFSIZE); + u32 r_base = readl(hws->bar0_base + HWS_BUF_BASE_OFF(ch)); + u32 r_half = readl(hws->bar0_base + HWS_HALF_SZ_OFF(ch)); + + dev_dbg(&hws->pdev->dev, + "ch%u remap verify: hi=0x%08x page_lo=0x%08x exp_page=0x%08x base=0x%08x exp_base=0x%08x half16B=0x%08x exp_half=0x%08x\n", + ch, r_hi, r_lo, page_lo, r_base, + (ch + 1) * PCIEBAR_AXI_BASE + pci_addr, r_half, + vid->pix.half_size / 16); + } else if (wrote) { + /* Flush posted writes before arming DMA */ + readl_relaxed(hws->bar0_base + HWS_HALF_SZ_OFF(ch)); + } +} + +static struct hwsvideo_buffer * +hws_take_queued_buffer_locked(struct hws_video *vid) +{ + struct hwsvideo_buffer *buf; + + if (!vid || list_empty(&vid->capture_queue)) + return NULL; + + buf = list_first_entry(&vid->capture_queue, + struct hwsvideo_buffer, list); + list_del_init(&buf->list); + if (vid->queued_count) + vid->queued_count--; + return buf; +} + +void hws_prime_next_locked(struct hws_video *vid) +{ + struct hws_pcie_dev *hws; + struct hwsvideo_buffer *next; + dma_addr_t dma; + + if (!vid) + return; + + hws = vid->parent; + if (!hws || !hws->bar0_base) + return; + + if (!READ_ONCE(vid->cap_active) || !vid->active || vid->next_prepared) + return; + + next = hws_take_queued_buffer_locked(vid); + if (!next) + return; + + vid->next_prepared = next; + dma = vb2_dma_contig_plane_dma_addr(&next->vb.vb2_buf, 0); + hws_program_dma_for_addr(hws, vid->channel_index, dma); + iowrite32(lower_32_bits(dma), + hws->bar0_base + HWS_REG_DMA_ADDR(vid->channel_index)); + dev_dbg(&hws->pdev->dev, + "ch%u pre-armed next buffer %p dma=0x%llx\n", + vid->channel_index, next, (u64)dma); +} + +static bool hws_force_no_signal_frame(struct hws_video *v, const char *tag) +{ + struct hws_pcie_dev *hws; + unsigned long flags; + struct hwsvideo_buffer *buf = NULL, *next = NULL; + bool have_next = false; + bool doorbell = false; + + if (!v) + return false; + hws = v->parent; + if (!hws || READ_ONCE(v->stop_requested) || !READ_ONCE(v->cap_active)) + return false; + spin_lock_irqsave(&v->irq_lock, flags); + if (v->active) { + buf = v->active; + v->active = NULL; + buf->slot = 0; + } else if (!list_empty(&v->capture_queue)) { + buf = list_first_entry(&v->capture_queue, + struct hwsvideo_buffer, list); + list_del_init(&buf->list); + if (v->queued_count) + v->queued_count--; + buf->slot = 0; + } + if (v->next_prepared) { + next = v->next_prepared; + v->next_prepared = NULL; + next->slot = 0; + v->active = next; + have_next = true; + } else if (!list_empty(&v->capture_queue)) { + next = list_first_entry(&v->capture_queue, + struct hwsvideo_buffer, list); + list_del_init(&next->list); + if (v->queued_count) + v->queued_count--; + next->slot = 0; + v->active = next; + have_next = true; + } else { + v->active = NULL; + } + spin_unlock_irqrestore(&v->irq_lock, flags); + if (!buf) + return false; + /* Complete buffer with a neutral frame so dequeuers keep running. */ + { + struct vb2_v4l2_buffer *vb2v = &buf->vb; + void *dst = vb2_plane_vaddr(&vb2v->vb2_buf, 0); + + if (dst) + memset(dst, 0x10, v->pix.sizeimage); + vb2_set_plane_payload(&vb2v->vb2_buf, 0, v->pix.sizeimage); + vb2v->sequence = (u32)atomic_inc_return(&v->sequence_number); + vb2v->vb2_buf.timestamp = ktime_get_ns(); + vb2_buffer_done(&vb2v->vb2_buf, VB2_BUF_STATE_DONE); + } + if (have_next && next) { + dma_addr_t dma = + vb2_dma_contig_plane_dma_addr(&next->vb.vb2_buf, 0); + hws_program_dma_for_addr(hws, v->channel_index, dma); + hws_set_dma_doorbell(hws, v->channel_index, dma, + tag ? tag : "nosignal_zero"); + doorbell = true; + } + if (doorbell) { + wmb(); /* ensure descriptors visible before enabling capture */ + hws_enable_video_capture(hws, v->channel_index, true); + } + return true; +} + +static int hws_ctrls_init(struct hws_video *vid) +{ + struct v4l2_ctrl_handler *hdl = &vid->control_handler; + + /* Create BCHS controls. */ + v4l2_ctrl_handler_init(hdl, 4); + + vid->ctrl_brightness = v4l2_ctrl_new_std(hdl, &hws_ctrl_ops, + V4L2_CID_BRIGHTNESS, + MIN_VAMP_BRIGHTNESS_UNITS, + MAX_VAMP_BRIGHTNESS_UNITS, 1, + HWS_BRIGHTNESS_DEFAULT); + + vid->ctrl_contrast = + v4l2_ctrl_new_std(hdl, &hws_ctrl_ops, V4L2_CID_CONTRAST, + MIN_VAMP_CONTRAST_UNITS, MAX_VAMP_CONTRAST_UNITS, + 1, HWS_CONTRAST_DEFAULT); + + vid->ctrl_saturation = v4l2_ctrl_new_std(hdl, &hws_ctrl_ops, + V4L2_CID_SATURATION, + MIN_VAMP_SATURATION_UNITS, + MAX_VAMP_SATURATION_UNITS, 1, + HWS_SATURATION_DEFAULT); + + vid->ctrl_hue = v4l2_ctrl_new_std(hdl, &hws_ctrl_ops, V4L2_CID_HUE, + MIN_VAMP_HUE_UNITS, + MAX_VAMP_HUE_UNITS, 1, + HWS_HUE_DEFAULT); + if (hdl->error) { + int err = hdl->error; + + v4l2_ctrl_handler_free(hdl); + return err; + } + return 0; +} + +int hws_video_init_channel(struct hws_pcie_dev *pdev, int ch) +{ + struct hws_video *vid; + + /* basic sanity */ + if (!pdev || ch < 0 || ch >= pdev->max_channels) + return -EINVAL; + + vid = &pdev->video[ch]; + + /* hard reset the per-channel struct (safe here since we init everything next) */ + memset(vid, 0, sizeof(*vid)); + + /* identity */ + vid->parent = pdev; + vid->channel_index = ch; + + /* locks & lists */ + mutex_init(&vid->state_lock); + spin_lock_init(&vid->irq_lock); + INIT_LIST_HEAD(&vid->capture_queue); + atomic_set(&vid->sequence_number, 0); + vid->active = NULL; + + /* DMA watchdog removed; retain counters for diagnostics */ + vid->timeout_count = 0; + vid->error_count = 0; + + vid->queued_count = 0; + vid->window_valid = false; + + /* Default format. */ + vid->pix.width = 1920; + vid->pix.height = 1080; + vid->pix.fourcc = V4L2_PIX_FMT_YUYV; + vid->pix.bytesperline = ALIGN(vid->pix.width * 2, 64); + vid->pix.sizeimage = vid->pix.bytesperline * vid->pix.height; + vid->pix.field = V4L2_FIELD_NONE; + vid->pix.colorspace = V4L2_COLORSPACE_REC709; + vid->pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + vid->pix.quantization = V4L2_QUANTIZATION_FULL_RANGE; + vid->pix.xfer_func = V4L2_XFER_FUNC_DEFAULT; + vid->pix.interlaced = false; + vid->pix.half_size = vid->pix.sizeimage / 2; + hws_set_current_dv_timings(vid, vid->pix.width, + vid->pix.height, vid->pix.interlaced); + vid->current_fps = 60; + + /* color controls default (mid-scale) */ + vid->current_brightness = 0x80; + vid->current_contrast = 0x80; + vid->current_saturation = 0x80; + vid->current_hue = 0x80; + + /* capture state */ + vid->cap_active = false; + vid->stop_requested = false; + vid->last_buf_half_toggle = 0; + vid->half_seen = false; + vid->signal_loss_cnt = 0; + + /* Create BCHS + DV power-present as modern controls */ + { + int err = hws_ctrls_init(vid); + + if (err) { + dev_err(&pdev->pdev->dev, + "v4l2 ctrl init failed on ch%d: %d\n", ch, err); + return err; + } + } + + return 0; +} + +static void hws_video_drain_queue_locked(struct hws_video *vid) +{ + /* Return in-flight first */ + if (vid->active) { + vb2_buffer_done(&vid->active->vb.vb2_buf, VB2_BUF_STATE_ERROR); + vid->active = NULL; + } + + /* Then everything queued */ + while (!list_empty(&vid->capture_queue)) { + struct hwsvideo_buffer *b = + list_first_entry(&vid->capture_queue, + struct hwsvideo_buffer, + list); + list_del_init(&b->list); + vb2_buffer_done(&b->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } +} + +static void hws_video_release_registration(struct hws_video *vid) +{ + if (vid->buffer_queue.ops) { + vb2_queue_release(&vid->buffer_queue); + vid->buffer_queue.ops = NULL; + } + + if (!vid->video_device) + return; + + if (video_is_registered(vid->video_device)) + vb2_video_unregister_device(vid->video_device); + else + video_device_release(vid->video_device); + vid->video_device = NULL; +} + +static void hws_video_collect_done_locked(struct hws_video *vid, + struct list_head *done) +{ + struct hwsvideo_buffer *b; + + if (vid->active) { + if (!list_node_unlinked(&vid->active->list)) { + list_move_tail(&vid->active->list, done); + } else { + INIT_LIST_HEAD(&vid->active->list); + list_add_tail(&vid->active->list, done); + } + vid->active = NULL; + } + + if (vid->next_prepared) { + if (!list_node_unlinked(&vid->next_prepared->list)) { + list_move_tail(&vid->next_prepared->list, done); + } else { + INIT_LIST_HEAD(&vid->next_prepared->list); + list_add_tail(&vid->next_prepared->list, done); + } + vid->next_prepared = NULL; + } + + while (!list_empty(&vid->capture_queue)) { + b = list_first_entry(&vid->capture_queue, struct hwsvideo_buffer, + list); + list_move_tail(&b->list, done); + } + + vid->queued_count = 0; +} + +void hws_video_cleanup_channel(struct hws_pcie_dev *pdev, int ch) +{ + struct hws_video *vid; + unsigned long flags; + + if (!pdev || ch < 0 || ch >= pdev->max_channels) + return; + + vid = &pdev->video[ch]; + + /* 1) Stop HW best-effort for this channel */ + hws_enable_video_capture(vid->parent, vid->channel_index, false); + + /* 2) Flip software state so IRQ/BH will be no-ops if they run */ + WRITE_ONCE(vid->stop_requested, true); + WRITE_ONCE(vid->cap_active, false); + + /* 3) Ensure the IRQ handler finished any in-flight completions */ + if (vid->parent && vid->parent->irq >= 0) + synchronize_irq(vid->parent->irq); + + /* 4) Drain SW capture queue & in-flight under lock */ + spin_lock_irqsave(&vid->irq_lock, flags); + hws_video_drain_queue_locked(vid); + spin_unlock_irqrestore(&vid->irq_lock, flags); + + /* 5) Release VB2 queue if initialized */ + hws_video_release_registration(vid); + + /* 6) Free V4L2 controls */ + v4l2_ctrl_handler_free(&vid->control_handler); + + /* 8) Reset simple state; do not memset the whole struct here. */ + mutex_destroy(&vid->state_lock); + INIT_LIST_HEAD(&vid->capture_queue); + vid->active = NULL; + vid->stop_requested = false; + vid->last_buf_half_toggle = 0; + vid->half_seen = false; + vid->signal_loss_cnt = 0; +} + +/* Convenience cast */ +static inline struct hwsvideo_buffer *to_hwsbuf(struct vb2_buffer *vb) +{ + return container_of(to_vb2_v4l2_buffer(vb), struct hwsvideo_buffer, vb); +} + +static int hws_buf_init(struct vb2_buffer *vb) +{ + struct hwsvideo_buffer *b = to_hwsbuf(vb); + + INIT_LIST_HEAD(&b->list); + return 0; +} + +static void hws_buf_finish(struct vb2_buffer *vb) +{ + /* vb2 core handles cache maintenance for dma-contig buffers */ + (void)vb; +} + +static void hws_buf_cleanup(struct vb2_buffer *vb) +{ + struct hwsvideo_buffer *b = to_hwsbuf(vb); + + if (!list_empty(&b->list)) + list_del_init(&b->list); +} + +void hws_program_dma_for_addr(struct hws_pcie_dev *hws, unsigned int ch, + dma_addr_t dma) +{ + struct hws_video *vid = &hws->video[ch]; + + hws_program_dma_window(vid, dma); +} + +void hws_enable_video_capture(struct hws_pcie_dev *hws, unsigned int chan, + bool on) +{ + u32 status; + + if (!hws || hws->pci_lost || chan >= hws->max_channels) + return; + + status = readl(hws->bar0_base + HWS_REG_VCAP_ENABLE); + status = on ? (status | BIT(chan)) : (status & ~BIT(chan)); + writel(status, hws->bar0_base + HWS_REG_VCAP_ENABLE); + (void)readl(hws->bar0_base + HWS_REG_VCAP_ENABLE); + + WRITE_ONCE(hws->video[chan].cap_active, on); + + dev_dbg(&hws->pdev->dev, "vcap %s ch%u (reg=0x%08x)\n", + on ? "ON" : "OFF", chan, status); +} + +static void hws_seed_dma_windows(struct hws_pcie_dev *hws) +{ + const u32 addr_mask = PCI_E_BAR_ADD_MASK; + const u32 addr_low_mask = PCI_E_BAR_ADD_LOWMASK; + u32 table = 0x208; /* one 64-bit entry per channel */ + unsigned int ch; + + if (!hws || !hws->bar0_base) + return; + + /* If cur_max_video_ch is not set yet, default to max_channels. */ + if (!hws->cur_max_video_ch || hws->cur_max_video_ch > hws->max_channels) + hws->cur_max_video_ch = hws->max_channels; + + for (ch = 0; ch < hws->cur_max_video_ch; ch++, table += 8) { + if (!hws->scratch_vid[ch].cpu) + continue; + + /* Program 64-bit BAR remap entry for this channel */ + { + dma_addr_t p = hws->scratch_vid[ch].dma; + u32 lo = lower_32_bits(p) & addr_mask; + u32 hi = upper_32_bits(p); + u32 pci_addr_low = lower_32_bits(p) & addr_low_mask; + + writel_relaxed(hi, + hws->bar0_base + PCI_ADDR_TABLE_BASE + + table); + writel_relaxed(lo, + hws->bar0_base + PCI_ADDR_TABLE_BASE + + table + PCIE_BARADDROFSIZE); + + /* Per-channel AXI base + PCI low */ + writel_relaxed((ch + 1) * PCIEBAR_AXI_BASE + + pci_addr_low, + hws->bar0_base + CVBS_IN_BUF_BASE + + ch * PCIE_BARADDROFSIZE); + + /* Half-frame length in /16 units. + * Prefer the current channel's computed half_size if available. + * Fall back to half of the probe-owned scratch buffer. + */ + { + u32 half_bytes = hws->video[ch].pix.half_size ? + hws->video[ch].pix.half_size : + (u32)(hws->scratch_vid[ch].size / 2); + writel_relaxed(half_bytes / 16, + hws->bar0_base + + CVBS_IN_BUF_BASE2 + + ch * PCIE_BARADDROFSIZE); + } + } + } + + /* Post writes so device sees them before we move on */ + (void)readl(hws->bar0_base + HWS_REG_INT_STATUS); +} + +static void hws_ack_all_irqs(struct hws_pcie_dev *hws) +{ + u32 st = readl(hws->bar0_base + HWS_REG_INT_STATUS); + + if (st) { + writel(st, hws->bar0_base + HWS_REG_INT_STATUS); /* W1C */ + (void)readl(hws->bar0_base + HWS_REG_INT_STATUS); + } +} + +static void hws_open_irq_fabric(struct hws_pcie_dev *hws) +{ + /* Route all sources to vector 0. */ + writel(0x00000000, hws->bar0_base + PCIE_INT_DEC_REG_BASE); + (void)readl(hws->bar0_base + PCIE_INT_DEC_REG_BASE); + + /* Enable the PCIe bridge. */ + writel(0x00000001, hws->bar0_base + PCIEBR_EN_REG_BASE); + (void)readl(hws->bar0_base + PCIEBR_EN_REG_BASE); + + /* Open the global/bridge gate (legacy 0x3FFFF) */ + writel(HWS_INT_EN_MASK, hws->bar0_base + INT_EN_REG_BASE); + (void)readl(hws->bar0_base + INT_EN_REG_BASE); +} + +void hws_init_video_sys(struct hws_pcie_dev *hws, bool enable) +{ + int i; + + if (hws->start_run && !enable) + return; + + /* 1) reset the decoder mode register to 0 */ + writel(0x00000000, hws->bar0_base + HWS_REG_DEC_MODE); + hws_seed_dma_windows(hws); + + /* 3) on a full reset, clear all per-channel status and indices */ + if (!enable) { + for (i = 0; i < hws->max_channels; i++) { + /* helpers to arm/disable capture engines */ + hws_enable_video_capture(hws, i, false); + } + } + + /* 4) Start run: set bit31, wait a bit, then program low 24 bits. */ + writel(0x80000000, hws->bar0_base + HWS_REG_DEC_MODE); + writel(0x80FFFFFF, hws->bar0_base + HWS_REG_DEC_MODE); + writel(0x13, hws->bar0_base + HWS_REG_DEC_MODE); + hws_ack_all_irqs(hws); + hws_open_irq_fabric(hws); + /* 6) record that we're now running */ + hws->start_run = true; +} + +int hws_check_card_status(struct hws_pcie_dev *hws) +{ + u32 status; + + if (!hws || !hws->bar0_base) + return -ENODEV; + + status = readl(hws->bar0_base + HWS_REG_SYS_STATUS); + + /* Common device-missing pattern. */ + if (status == 0xFFFFFFFF) { + hws->pci_lost = true; + dev_err(&hws->pdev->dev, "PCIe device not responding\n"); + return -ENODEV; + } + + /* If RUN/READY bit (bit0) is not set, reinitialize the video core. */ + if (!(status & BIT(0))) { + dev_dbg(&hws->pdev->dev, + "SYS_STATUS not ready (0x%08x), reinitializing\n", + status); + hws_init_video_sys(hws, true); + } + + return 0; +} + +void check_video_format(struct hws_pcie_dev *pdx) +{ + int i; + + for (i = 0; i < pdx->cur_max_video_ch; i++) { + bool interlace = false; + + if (!hws_read_active_state(pdx, i, &interlace)) { + /* No active video; optionally feed neutral frames to keep streaming. */ + if (pdx->video[i].signal_loss_cnt == 0) + pdx->video[i].signal_loss_cnt = 1; + if (READ_ONCE(pdx->video[i].cap_active)) + hws_force_no_signal_frame(&pdx->video[i], + "monitor_nosignal"); + } else { + if (pdx->hw_ver > 0) + handle_hwv2_path(pdx, i); + else + /* Legacy path stub; see handle_legacy_path() comment. */ + handle_legacy_path(pdx, i); + + update_live_resolution(pdx, i, interlace); + pdx->video[i].signal_loss_cnt = 0; + } + } +} + +static inline void hws_write_if_diff(struct hws_pcie_dev *hws, u32 reg_off, + u32 new_val) +{ + void __iomem *addr; + u32 old; + + if (!hws || !hws->bar0_base) + return; + + addr = hws->bar0_base + reg_off; + + old = readl(addr); + /* Treat all-ones as device gone; avoid writing garbage. */ + if (old == 0xFFFFFFFF) { + hws->pci_lost = true; + return; + } + + if (old != new_val) { + writel(new_val, addr); + /* Post the write on some bridges / enforce ordering. */ + (void)readl(addr); + } +} + +static bool hws_read_active_state(struct hws_pcie_dev *pdx, unsigned int ch, + bool *interlace) +{ + u32 reg; + bool active; + + if (ch >= pdx->cur_max_video_ch) + return false; + + reg = readl(pdx->bar0_base + HWS_REG_ACTIVE_STATUS); + active = !!(reg & BIT(ch)); + if (interlace) + *interlace = !!(reg & BIT(8 + ch)); + return active; +} + +/* Modern hardware path: keep HW registers in sync with current per-channel + * software state. + */ +static void handle_hwv2_path(struct hws_pcie_dev *hws, unsigned int ch) +{ + struct hws_video *vid; + u32 reg, in_fps, cur_out_res, want_out_res; + + if (!hws || !hws->bar0_base || ch >= hws->max_channels) + return; + + vid = &hws->video[ch]; + + /* 1) Input frame rate (read-only; log or export via debugfs if wanted) */ + in_fps = readl(hws->bar0_base + HWS_REG_FRAME_RATE(ch)); + /* dev_dbg(&hws->pdev->dev, "ch%u input fps=%u\n", ch, in_fps); */ + (void)in_fps; + + /* 2) Output resolution programming. + * For now, mirror the current format to OUT_RES. + */ + want_out_res = (vid->pix.height << 16) | vid->pix.width; + cur_out_res = readl(hws->bar0_base + HWS_REG_OUT_RES(ch)); + if (cur_out_res != want_out_res) + hws_write_if_diff(hws, HWS_REG_OUT_RES(ch), want_out_res); + + /* 3) Output FPS: only program if you actually track a target. + * Example heuristic (disabled by default): + * + * u32 out_fps = (vid->fmt_curr.height >= 1080) ? 60 : 30; + * hws_write_if_diff(hws, HWS_REG_OUT_FRAME_RATE(ch), out_fps); + */ + + /* 4) BCHS controls: pack from per-channel current_* fields */ + reg = readl(hws->bar0_base + HWS_REG_BCHS(ch)); + { + u8 br = reg & 0xFF; + u8 co = (reg >> 8) & 0xFF; + u8 hu = (reg >> 16) & 0xFF; + u8 sa = (reg >> 24) & 0xFF; + + if (br != vid->current_brightness || + co != vid->current_contrast || hu != vid->current_hue || + sa != vid->current_saturation) { + u32 packed = (vid->current_saturation << 24) | + (vid->current_hue << 16) | + (vid->current_contrast << 8) | + vid->current_brightness; + hws_write_if_diff(hws, HWS_REG_BCHS(ch), packed); + } + } + + /* 5) HDCP detect: read only (no cache field in your structs today) */ + reg = readl(hws->bar0_base + HWS_REG_HDCP_STATUS); + /* bool hdcp = !!(reg & BIT(ch)); // use if you later add a field/control */ +} + +static void handle_legacy_path(struct hws_pcie_dev *hws, unsigned int ch) +{ + /* + * Legacy (hw_ver == 0) expected behavior: + * - A per-channel SW FPS accumulator incremented on each VDONE. + * - A once-per-second poll mapped the count to discrete FPS: + * >55*2 => 60, >45*2 => 50, >25*2 => 30, >20*2 => 25, else 60, + * then reset the accumulator to 0. + * - The *2 factor assumed VDONE fired per-field; if legacy VDONE is + * per-frame, drop the factor. + * + * Current code keeps this path as a no-op; vid->current_fps stays at the + * default or mode-derived value. If accurate legacy FPS reporting is + * needed (V4L2 g_parm/timeperframe), reintroduce the accumulator in the + * IRQ path and perform the mapping/reset here. + * + * No-op by default. If you introduce a SW FPS accumulator, map it here. + * + * Example skeleton: + * + * u32 sw_rate = READ_ONCE(hws->sw_fps[ch]); // incremented elsewhere + * if (sw_rate > THRESHOLD) { + * u32 fps = pick_fps_from_rate(sw_rate); + * hws_write_if_diff(hws, HWS_REG_OUT_FRAME_RATE(ch), fps); + * WRITE_ONCE(hws->sw_fps[ch], 0); + * } + */ + (void)hws; + (void)ch; +} + +static void hws_video_apply_mode_change(struct hws_pcie_dev *pdx, + unsigned int ch, u16 w, u16 h, + bool interlaced, u32 fps) +{ + struct hws_video *v = &pdx->video[ch]; + unsigned long flags; + bool queue_busy; + bool geometry_changed; + struct list_head done; + struct hwsvideo_buffer *b, *tmp; + + if (!pdx || !pdx->bar0_base) + return; + if (ch >= pdx->max_channels) + return; + if (!w || !h || w > MAX_VIDEO_HW_W || + (!interlaced && h > MAX_VIDEO_HW_H) || + (interlaced && (h * 2) > MAX_VIDEO_HW_H)) + return; + if (!fps || fps == 0xFFFFFFFF || fps > 240) + fps = (h == 576) ? 50 : 60; + + geometry_changed = w != v->pix.width || h != v->pix.height || + interlaced != v->pix.interlaced; + if (!geometry_changed && fps == v->current_fps) + return; + + if (!geometry_changed) { + /* Refresh cached live timing state, but don't emit a resolution + * change event when only the frame rate changes. + */ + mutex_lock(&v->state_lock); + v->pix.interlaced = interlaced; + v->pix.field = interlaced ? V4L2_FIELD_INTERLACED : + V4L2_FIELD_NONE; + hws_set_current_dv_timings(v, w, h, interlaced); + v->current_fps = fps; + mutex_unlock(&v->state_lock); + return; + } + + if (!mutex_trylock(&v->state_lock)) + return; + + INIT_LIST_HEAD(&done); + queue_busy = vb2_is_busy(&v->buffer_queue); + + WRITE_ONCE(v->stop_requested, true); + WRITE_ONCE(v->cap_active, false); + /* Publish software stop first so the IRQ completion path sees the stop + * before we touch MMIO or the lists. Pairs with READ_ONCE() checks in the + * VDONE handler and hws_arm_next() to prevent completions while modes + * change. + */ + smp_wmb(); + + hws_enable_video_capture(pdx, ch, false); + readl(pdx->bar0_base + HWS_REG_INT_STATUS); + + if (v->parent && v->parent->irq >= 0) + synchronize_irq(v->parent->irq); + + spin_lock_irqsave(&v->irq_lock, flags); + hws_video_collect_done_locked(v, &done); + spin_unlock_irqrestore(&v->irq_lock, flags); + + /* Update software pixel state */ + v->pix.width = w; + v->pix.height = h; + v->pix.interlaced = interlaced; + hws_set_current_dv_timings(v, w, h, interlaced); + v->current_fps = fps; + + hws_calc_sizeimage(v, w, h, interlaced); + v->window_valid = false; + + /* Geometry changes require userspace renegotiation once buffers exist. + * Emit SOURCE_CHANGE, mark the queue in error, and let userspace + * STREAMOFF/REQBUFS/STREAMON rather than trying to restart capture + * with partially drained in-flight state. + */ + if (queue_busy) { + struct v4l2_event ev = { + .type = V4L2_EVENT_SOURCE_CHANGE, + }; + + ev.u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION; + v4l2_event_queue(v->video_device, &ev); + vb2_queue_error(&v->buffer_queue); + } else { + WRITE_ONCE(v->stop_requested, false); + } + + /* Program HW with new resolution */ + hws_write_if_diff(pdx, HWS_REG_OUT_RES(ch), (h << 16) | w); + + /* Legacy half-buffer programming */ + writel(v->pix.half_size / 16, + pdx->bar0_base + CVBS_IN_BUF_BASE2 + ch * PCIE_BARADDROFSIZE); + (void)readl(pdx->bar0_base + CVBS_IN_BUF_BASE2 + + ch * PCIE_BARADDROFSIZE); + + /* Reset per-channel toggles/counters */ + WRITE_ONCE(v->last_buf_half_toggle, 0); + atomic_set(&v->sequence_number, 0); + + mutex_unlock(&v->state_lock); + + list_for_each_entry_safe(b, tmp, &done, list) { + list_del_init(&b->list); + vb2_buffer_done(&b->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } +} + +static void update_live_resolution(struct hws_pcie_dev *pdx, unsigned int ch, + bool interlace) +{ + u32 reg = readl(pdx->bar0_base + HWS_REG_IN_RES(ch)); + u32 fps = readl(pdx->bar0_base + HWS_REG_FRAME_RATE(ch)); + u16 res_w = reg & 0xFFFF; + u16 res_h = (reg >> 16) & 0xFFFF; + struct hws_video *vid = &pdx->video[ch]; + bool geometry_changed; + bool fps_changed; + + bool within_hw = (res_w <= MAX_VIDEO_HW_W) && + ((!interlace && res_h <= MAX_VIDEO_HW_H) || + (interlace && (res_h * 2) <= MAX_VIDEO_HW_H)); + + if (!within_hw) + return; + + geometry_changed = res_w != vid->pix.width || + res_h != vid->pix.height || + interlace != vid->pix.interlaced; + fps_changed = fps && fps != 0xFFFFFFFF && fps <= 240 && + fps != vid->current_fps; + + if (geometry_changed || fps_changed) + hws_video_apply_mode_change(pdx, ch, res_w, res_h, interlace, + fps); +} + +static int hws_open(struct file *file) +{ + return v4l2_fh_open(file); +} + +static const struct v4l2_file_operations hws_fops = { + .owner = THIS_MODULE, + .open = hws_open, + .release = vb2_fop_release, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, +}; + +static int hws_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_src_change_event_subscribe(fh, sub); + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subscribe_event(fh, sub); + default: + return -EINVAL; + } +} + +static const struct v4l2_ioctl_ops hws_ioctl_fops = { + /* Core caps/info */ + .vidioc_querycap = hws_vidioc_querycap, + + /* Pixel format: still needed to report YUYV etc. */ + .vidioc_enum_fmt_vid_cap = hws_vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = hws_vidioc_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = hws_vidioc_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = hws_vidioc_try_fmt_vid_cap, + + /* Buffer queueing / streaming */ + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + /* Inputs */ + .vidioc_enum_input = hws_vidioc_enum_input, + .vidioc_g_input = hws_vidioc_g_input, + .vidioc_s_input = hws_vidioc_s_input, + + /* DV timings (HDMI/DVI/VESA modes) */ + .vidioc_query_dv_timings = hws_vidioc_query_dv_timings, + .vidioc_enum_dv_timings = hws_vidioc_enum_dv_timings, + .vidioc_g_dv_timings = hws_vidioc_g_dv_timings, + .vidioc_s_dv_timings = hws_vidioc_s_dv_timings, + .vidioc_dv_timings_cap = hws_vidioc_dv_timings_cap, + + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = hws_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_g_parm = hws_vidioc_g_parm, +}; + +static u32 hws_calc_sizeimage(struct hws_video *v, u16 w, u16 h, + bool interlaced) +{ + /* HWS captures packed YUYV only; stride is 16 bpp aligned to 64 bytes. */ + u32 lines = h; /* full frame lines for sizeimage */ + u32 bytesperline = ALIGN(w * 2, 64); + u32 sizeimage, half0; + + /* publish into pix, since we now carry these in-state */ + v->pix.bytesperline = bytesperline; + sizeimage = bytesperline * lines; + + half0 = sizeimage / 2; + + v->pix.sizeimage = sizeimage; + v->pix.half_size = half0; /* first half; second = sizeimage - half0 */ + v->pix.field = interlaced ? V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE; + + return v->pix.sizeimage; +} + +static int hws_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct hws_video *vid = q->drv_priv; + + if (*nplanes) { + if (sizes[0] < vid->pix.sizeimage) + return -EINVAL; + } else { + *nplanes = 1; + sizes[0] = vid->pix.sizeimage; + } + + return 0; +} + +static int hws_buffer_prepare(struct vb2_buffer *vb) +{ + struct hws_video *vid = vb->vb2_queue->drv_priv; + struct hws_pcie_dev *hws = vid->parent; + size_t need = vid->pix.sizeimage; + dma_addr_t dma_addr; + + if (vb2_plane_size(vb, 0) < need) + return -EINVAL; + + /* Validate DMA address alignment */ + dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0); + if (dma_addr & 0x3F) { /* 64-byte alignment required */ + dev_err(&hws->pdev->dev, + "Buffer DMA address 0x%llx not 64-byte aligned\n", + (unsigned long long)dma_addr); + return -EINVAL; + } + + vb2_set_plane_payload(vb, 0, need); + return 0; +} + +static void hws_buffer_queue(struct vb2_buffer *vb) +{ + struct hws_video *vid = vb->vb2_queue->drv_priv; + struct hwsvideo_buffer *buf = to_hwsbuf(vb); + struct hws_pcie_dev *hws = vid->parent; + unsigned long flags; + + dev_dbg(&hws->pdev->dev, + "buffer_queue(ch=%u): vb=%p sizeimage=%u q_active=%d\n", + vid->channel_index, vb, vid->pix.sizeimage, + READ_ONCE(vid->cap_active)); + + /* Initialize buffer slot */ + buf->slot = 0; + + spin_lock_irqsave(&vid->irq_lock, flags); + list_add_tail(&buf->list, &vid->capture_queue); + vid->queued_count++; + + /* If streaming and no in-flight buffer, prime HW immediately */ + if (READ_ONCE(vid->cap_active) && !vid->active) { + dma_addr_t dma_addr; + + dev_dbg(&hws->pdev->dev, + "buffer_queue(ch=%u): priming first vb=%p\n", + vid->channel_index, &buf->vb.vb2_buf); + list_del_init(&buf->list); + vid->queued_count--; + vid->active = buf; + + dma_addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); + hws_program_dma_for_addr(vid->parent, vid->channel_index, + dma_addr); + iowrite32(lower_32_bits(dma_addr), + hws->bar0_base + HWS_REG_DMA_ADDR(vid->channel_index)); + + wmb(); /* ensure descriptors visible before enabling capture */ + hws_enable_video_capture(hws, vid->channel_index, true); + hws_prime_next_locked(vid); + } else if (READ_ONCE(vid->cap_active) && vid->active) { + hws_prime_next_locked(vid); + } + spin_unlock_irqrestore(&vid->irq_lock, flags); +} + +static int hws_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct hws_video *v = q->drv_priv; + struct hws_pcie_dev *hws = v->parent; + struct hwsvideo_buffer *to_program = NULL; /* local copy */ + struct vb2_buffer *prog_vb2 = NULL; + unsigned long flags; + int ret; + + dev_dbg(&hws->pdev->dev, "start_streaming: ch=%u count=%u\n", + v->channel_index, count); + + ret = hws_check_card_status(hws); + if (ret) { + struct hwsvideo_buffer *b, *tmp; + unsigned long f; + LIST_HEAD(queued); + + spin_lock_irqsave(&v->irq_lock, f); + if (v->active) { + list_add_tail(&v->active->list, &queued); + v->active = NULL; + } + if (v->next_prepared) { + list_add_tail(&v->next_prepared->list, &queued); + v->next_prepared = NULL; + } + while (!list_empty(&v->capture_queue)) { + b = list_first_entry(&v->capture_queue, + struct hwsvideo_buffer, list); + list_move_tail(&b->list, &queued); + } + spin_unlock_irqrestore(&v->irq_lock, f); + + list_for_each_entry_safe(b, tmp, &queued, list) { + list_del_init(&b->list); + vb2_buffer_done(&b->vb.vb2_buf, VB2_BUF_STATE_QUEUED); + } + return ret; + } + (void)hws_read_active_state(hws, v->channel_index, + &v->pix.interlaced); + + lockdep_assert_held(&v->state_lock); + /* init per-stream state */ + WRITE_ONCE(v->stop_requested, false); + WRITE_ONCE(v->cap_active, true); + WRITE_ONCE(v->half_seen, false); + WRITE_ONCE(v->last_buf_half_toggle, 0); + + /* Try to prime a buffer, but it's OK if none are queued yet */ + spin_lock_irqsave(&v->irq_lock, flags); + if (!v->active && !list_empty(&v->capture_queue)) { + to_program = list_first_entry(&v->capture_queue, + struct hwsvideo_buffer, list); + list_del_init(&to_program->list); + v->queued_count--; + v->active = to_program; + prog_vb2 = &to_program->vb.vb2_buf; + dev_dbg(&hws->pdev->dev, + "start_streaming: ch=%u took buffer %p\n", + v->channel_index, to_program); + } + spin_unlock_irqrestore(&v->irq_lock, flags); + + /* Only program/enable HW if we actually have a buffer */ + if (to_program) { + if (!prog_vb2) + prog_vb2 = &to_program->vb.vb2_buf; + { + dma_addr_t dma_addr; + + dma_addr = vb2_dma_contig_plane_dma_addr(prog_vb2, 0); + hws_program_dma_for_addr(hws, v->channel_index, dma_addr); + iowrite32(lower_32_bits(dma_addr), + hws->bar0_base + + HWS_REG_DMA_ADDR(v->channel_index)); + dev_dbg(&hws->pdev->dev, + "start_streaming: ch=%u programmed buffer %p dma=0x%08x\n", + v->channel_index, to_program, + lower_32_bits(dma_addr)); + (void)readl(hws->bar0_base + HWS_REG_INT_STATUS); + } + + wmb(); /* ensure descriptors visible before enabling capture */ + hws_enable_video_capture(hws, v->channel_index, true); + { + unsigned long pf; + + spin_lock_irqsave(&v->irq_lock, pf); + hws_prime_next_locked(v); + spin_unlock_irqrestore(&v->irq_lock, pf); + } + } else { + dev_dbg(&hws->pdev->dev, + "start_streaming: ch=%u no buffer yet (will arm on QBUF)\n", + v->channel_index); + } + + return 0; +} + +static void hws_log_video_state(struct hws_video *v, const char *action, + const char *phase) +{ + struct hws_pcie_dev *hws = v->parent; + unsigned long flags; + unsigned int queued = 0; + unsigned int tracked = 0; + unsigned int seq = 0; + struct hwsvideo_buffer *b; + bool streaming = vb2_is_streaming(&v->buffer_queue); + bool cap_active; + bool stop_requested; + struct hwsvideo_buffer *active; + struct hwsvideo_buffer *next_prepared; + + spin_lock_irqsave(&v->irq_lock, flags); + list_for_each_entry(b, &v->capture_queue, list) + queued++; + cap_active = READ_ONCE(v->cap_active); + stop_requested = READ_ONCE(v->stop_requested); + active = v->active; + next_prepared = v->next_prepared; + tracked = v->queued_count; + seq = (u32)atomic_read(&v->sequence_number); + spin_unlock_irqrestore(&v->irq_lock, flags); + + dev_dbg(&hws->pdev->dev, + "video:%s:%s ch=%u streaming=%d cap=%d stop=%d active=%p next=%p queued=%u tracked=%u seq=%u\n", + action, phase, v->channel_index, streaming, cap_active, + stop_requested, active, next_prepared, queued, tracked, seq); +} + +static void hws_stop_streaming(struct vb2_queue *q) +{ + struct hws_video *v = q->drv_priv; + struct hws_pcie_dev *hws = v->parent; + unsigned long flags; + struct hwsvideo_buffer *b, *tmp; + LIST_HEAD(done); + unsigned int done_cnt = 0; + u64 start_ns = ktime_get_mono_fast_ns(); + + hws_log_video_state(v, "streamoff", "begin"); + + /* 1) Quiesce SW/HW first */ + lockdep_assert_held(&v->state_lock); + WRITE_ONCE(v->cap_active, false); + WRITE_ONCE(v->stop_requested, true); + + hws_enable_video_capture(v->parent, v->channel_index, false); + + /* 2) Collect in-flight + queued under the IRQ lock */ + spin_lock_irqsave(&v->irq_lock, flags); + hws_video_collect_done_locked(v, &done); + spin_unlock_irqrestore(&v->irq_lock, flags); + + /* 3) Complete outside the lock */ + list_for_each_entry_safe(b, tmp, &done, list) { + /* Unlink from 'done' before completing */ + list_del_init(&b->list); + vb2_buffer_done(&b->vb.vb2_buf, VB2_BUF_STATE_ERROR); + done_cnt++; + } + dev_dbg(&hws->pdev->dev, + "video:streamoff:done ch=%u completed=%u (%lluus)\n", + v->channel_index, done_cnt, hws_elapsed_us(start_ns)); + hws_log_video_state(v, "streamoff", "end"); +} + +static const struct vb2_ops hwspcie_video_qops = { + .queue_setup = hws_queue_setup, + .buf_prepare = hws_buffer_prepare, + .buf_init = hws_buf_init, + .buf_finish = hws_buf_finish, + .buf_cleanup = hws_buf_cleanup, + .buf_queue = hws_buffer_queue, + .start_streaming = hws_start_streaming, + .stop_streaming = hws_stop_streaming, +}; + +int hws_video_register(struct hws_pcie_dev *dev) +{ + int i, ret; + + ret = v4l2_device_register(&dev->pdev->dev, &dev->v4l2_device); + if (ret) { + dev_err(&dev->pdev->dev, "v4l2_device_register failed: %d\n", + ret); + return ret; + } + + for (i = 0; i < dev->cur_max_video_ch; i++) { + struct hws_video *ch = &dev->video[i]; + struct video_device *vdev; + struct vb2_queue *q; + + /* hws_video_init_channel() should have set: + * - ch->parent, ch->channel_index + * - locks (state_lock, irq_lock) + * - capture_queue (INIT_LIST_HEAD) + * - control_handler + controls + * - fmt_curr (width/height) + * Do not reinitialize any of those here. + */ + + vdev = video_device_alloc(); + if (!vdev) { + dev_err(&dev->pdev->dev, + "video_device_alloc ch%u failed\n", i); + ret = -ENOMEM; + goto err_unwind; + } + ch->video_device = vdev; + + /* Basic V4L2 node setup */ + snprintf(vdev->name, sizeof(vdev->name), "%s-hdmi%u", + KBUILD_MODNAME, i); + vdev->v4l2_dev = &dev->v4l2_device; + vdev->fops = &hws_fops; + vdev->ioctl_ops = &hws_ioctl_fops; + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + vdev->lock = &ch->state_lock; /* serialize file ops */ + vdev->ctrl_handler = &ch->control_handler; + vdev->vfl_dir = VFL_DIR_RX; + vdev->release = video_device_release; + if (ch->control_handler.error) { + ret = ch->control_handler.error; + goto err_unwind; + } + video_set_drvdata(vdev, ch); + + /* vb2 queue init (dma-contig) */ + q = &ch->buffer_queue; + memset(q, 0, sizeof(*q)); + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_DMABUF; + q->drv_priv = ch; + q->buf_struct_size = sizeof(struct hwsvideo_buffer); + q->ops = &hwspcie_video_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &ch->state_lock; + q->min_queued_buffers = 1; + q->dev = &dev->pdev->dev; + + ret = vb2_queue_init(q); + vdev->queue = q; + if (ret) { + dev_err(&dev->pdev->dev, + "vb2_queue_init ch%u failed: %d\n", i, ret); + goto err_unwind; + } + + /* Make controls live (no-op if none or already set up) */ + if (ch->control_handler.error) { + ret = ch->control_handler.error; + dev_err(&dev->pdev->dev, + "ctrl handler ch%u error: %d\n", i, ret); + goto err_unwind; + } + v4l2_ctrl_handler_setup(&ch->control_handler); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) { + dev_err(&dev->pdev->dev, + "video_register_device ch%u failed: %d\n", i, + ret); + goto err_unwind; + } + } + + return 0; + +err_unwind: + for (; i >= 0; i--) { + struct hws_video *ch = &dev->video[i]; + + hws_video_release_registration(ch); + } + v4l2_device_unregister(&dev->v4l2_device); + return ret; +} + +void hws_video_unregister(struct hws_pcie_dev *dev) +{ + int i; + + if (!dev) + return; + + for (i = 0; i < dev->cur_max_video_ch; i++) { + struct hws_video *ch = &dev->video[i]; + + hws_video_release_registration(ch); + v4l2_ctrl_handler_free(&ch->control_handler); + } + v4l2_device_unregister(&dev->v4l2_device); +} + +int hws_video_quiesce(struct hws_pcie_dev *hws, const char *reason) +{ + int i, ret = 0; + u64 start_ns = ktime_get_mono_fast_ns(); + + dev_dbg(&hws->pdev->dev, "video:%s:begin channels=%u\n", reason, + hws->cur_max_video_ch); + for (i = 0; i < hws->cur_max_video_ch; i++) { + struct hws_video *vid = &hws->video[i]; + struct vb2_queue *q = &vid->buffer_queue; + u64 ch_start_ns = ktime_get_mono_fast_ns(); + bool streaming; + + if (!q || !q->ops) { + dev_dbg(&hws->pdev->dev, + "video:%s:ch=%d skipped queue-unavailable\n", + reason, i); + continue; + } + + streaming = vb2_is_streaming(q); + hws_log_video_state(vid, reason, "channel"); + if (streaming) { + /* Stop via vb2, which runs .stop_streaming. */ + int r = vb2_streamoff(q, q->type); + + dev_dbg(&hws->pdev->dev, + "video:%s:ch=%d streamoff ret=%d (%lluus)\n", + reason, i, r, hws_elapsed_us(ch_start_ns)); + if (r && !ret) + ret = r; + } else { + dev_dbg(&hws->pdev->dev, + "video:%s:ch=%d idle (%lluus)\n", + reason, i, hws_elapsed_us(ch_start_ns)); + } + } + dev_dbg(&hws->pdev->dev, "video:%s:done ret=%d (%lluus)\n", reason, + ret, hws_elapsed_us(start_ns)); + return ret; +} + +void hws_video_pm_resume(struct hws_pcie_dev *hws) +{ + /* Nothing mandatory to do here for vb2; userspace will STREAMON + * again when ready. + */ +} diff --git a/drivers/media/pci/hws/hws_video.h b/drivers/media/pci/hws/hws_video.h new file mode 100644 index 000000000000..4feaf5b2f5a9 --- /dev/null +++ b/drivers/media/pci/hws/hws_video.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef HWS_VIDEO_H +#define HWS_VIDEO_H + +struct hws_video; + +int hws_video_register(struct hws_pcie_dev *dev); +void hws_video_unregister(struct hws_pcie_dev *dev); +void hws_enable_video_capture(struct hws_pcie_dev *hws, + unsigned int chan, + bool on); +void hws_prime_next_locked(struct hws_video *vid); + +int hws_video_init_channel(struct hws_pcie_dev *pdev, int ch); +void hws_video_cleanup_channel(struct hws_pcie_dev *pdev, int ch); +void check_video_format(struct hws_pcie_dev *pdx); +int hws_check_card_status(struct hws_pcie_dev *hws); +void hws_init_video_sys(struct hws_pcie_dev *hws, bool enable); + +void hws_program_dma_for_addr(struct hws_pcie_dev *hws, + unsigned int ch, + dma_addr_t dma); +void hws_set_dma_doorbell(struct hws_pcie_dev *hws, unsigned int ch, + dma_addr_t dma, const char *tag); + +int hws_video_quiesce(struct hws_pcie_dev *hws, const char *reason); +void hws_video_pm_resume(struct hws_pcie_dev *hws); + +#endif diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c index fc6608e33de4..88581a4c081d 100644 --- a/drivers/media/pci/intel/ipu-bridge.c +++ b/drivers/media/pci/intel/ipu-bridge.c @@ -168,6 +168,9 @@ static const struct acpi_device_id ivsc_acpi_ids[] = { { "INTC1095" }, { "INTC100A" }, { "INTC10CF" }, + { "INTC10DE" }, /* LNL */ + { "INTC10E0" }, /* ARL */ + { "INTC10E1" }, /* PTL */ }; static struct acpi_device *ipu_bridge_get_ivsc_acpi_dev(struct acpi_device *adev) @@ -221,7 +224,13 @@ static struct device *ipu_bridge_get_ivsc_csi_dev(struct acpi_device *adev) return csi_dev; } - return NULL; + /* Try to locate CVS device on the I2C bus */ + csi_dev = bus_find_device_by_acpi_dev(&i2c_bus_type, adev); + if (csi_dev) + return csi_dev; + + /* Fallback to platform bus for CVS device */ + return bus_find_device_by_acpi_dev(&platform_bus_type, adev); } static int ipu_bridge_check_ivsc_dev(struct ipu_sensor *sensor, @@ -235,7 +244,7 @@ static int ipu_bridge_check_ivsc_dev(struct ipu_sensor *sensor, csi_dev = ipu_bridge_get_ivsc_csi_dev(adev); if (!csi_dev) { acpi_dev_put(adev); - dev_err(ADEV_DEV(adev), "Failed to find MEI CSI dev\n"); + dev_err(ADEV_DEV(adev), "Failed to find MEI or CVS CSI dev\n"); return -ENODEV; } diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c b/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c index db2874843453..237906cb131a 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c @@ -288,15 +288,27 @@ static const struct dwc_dphy_freq_range freqranges[DPHY_FREQ_RANGE_NUM] = { static u16 get_hsfreq_by_mbps(u32 mbps) { - unsigned int i = DPHY_FREQ_RANGE_NUM; - - while (i--) { - if (freqranges[i].default_mbps == mbps || - (mbps >= freqranges[i].min && mbps <= freqranges[i].max)) - return i; + u16 best = DPHY_FREQ_RANGE_INVALID_INDEX; + unsigned int i; + + for (i = 0; i < DPHY_FREQ_RANGE_NUM; i++) { + if (mbps > freqranges[i].max) + continue; + + if (mbps < freqranges[i].min) + break; + + if (best == DPHY_FREQ_RANGE_INVALID_INDEX || + freqranges[i].osc_freq_target > + freqranges[best].osc_freq_target || + (freqranges[i].osc_freq_target == + freqranges[best].osc_freq_target && + abs((int)mbps - (int)freqranges[i].default_mbps) < + abs((int)mbps - (int)freqranges[best].default_mbps))) + best = i; } - return DPHY_FREQ_RANGE_INVALID_INDEX; + return best; } static int ipu6_isys_dwc_phy_config(struct ipu6_isys *isys, diff --git a/drivers/media/pci/ivtv/ivtv-ioctl.c b/drivers/media/pci/ivtv/ivtv-ioctl.c index 8d5ea3aec06f..fc95f0bf48d5 100644 --- a/drivers/media/pci/ivtv/ivtv-ioctl.c +++ b/drivers/media/pci/ivtv/ivtv-ioctl.c @@ -467,15 +467,13 @@ static int ivtv_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format int h = fmt->fmt.pix.height; int min_h = 2; - w = min(w, 720); - w = max(w, 2); + w = clamp(w, 2, 720); if (id->type == IVTV_ENC_STREAM_TYPE_YUV) { /* YUV height must be a multiple of 32 */ h &= ~0x1f; min_h = 32; } - h = min(h, itv->is_50hz ? 576 : 480); - h = max(h, min_h); + h = clamp(h, min_h, itv->is_50hz ? 576 : 480); ivtv_g_fmt_vid_cap(file, fh, fmt); fmt->fmt.pix.width = w; fmt->fmt.pix.height = h; @@ -516,8 +514,7 @@ static int ivtv_try_fmt_vid_out(struct file *file, void *fh, struct v4l2_format int field = fmt->fmt.pix.field; int ret = ivtv_g_fmt_vid_out(file, fh, fmt); - w = min(w, 720); - w = max(w, 2); + w = clamp(w, 2, 720); /* Why can the height be 576 even when the output is NTSC? Internally the buffers of the PVR350 are always set to 720x576. The @@ -533,8 +530,7 @@ static int ivtv_try_fmt_vid_out(struct file *file, void *fh, struct v4l2_format resolution is locked to the broadcast standard and not scaled. Thanks to Ian Armstrong for this explanation. */ - h = min(h, 576); - h = max(h, 2); + h = clamp(h, 2, 576); if (id->type == IVTV_DEC_STREAM_TYPE_YUV) fmt->fmt.pix.field = field; fmt->fmt.pix.width = w; diff --git a/drivers/media/pci/mgb4/mgb4_vin.c b/drivers/media/pci/mgb4/mgb4_vin.c index 74fb00c4a408..b24785455691 100644 --- a/drivers/media/pci/mgb4/mgb4_vin.c +++ b/drivers/media/pci/mgb4/mgb4_vin.c @@ -85,12 +85,12 @@ static const struct mgb4_i2c_kv gmsl1_i2c[] = { static const struct v4l2_dv_timings_cap video_timings_cap = { .type = V4L2_DV_BT_656_1120, .bt = { - .min_width = 240, + .min_width = 64, .max_width = 4096, - .min_height = 240, + .min_height = 64, .max_height = 4096, - .min_pixelclock = 1843200, /* 320 x 240 x 24Hz */ - .max_pixelclock = 530841600, /* 4096 x 2160 x 60Hz */ + .min_pixelclock = 20000000, + .max_pixelclock = 200000000, .standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT | V4L2_DV_BT_STD_GTF, .capabilities = V4L2_DV_BT_CAP_PROGRESSIVE | diff --git a/drivers/media/pci/mgb4/mgb4_vout.c b/drivers/media/pci/mgb4/mgb4_vout.c index 7725bcd55e4c..22fbc9e53819 100644 --- a/drivers/media/pci/mgb4/mgb4_vout.c +++ b/drivers/media/pci/mgb4/mgb4_vout.c @@ -53,12 +53,12 @@ static const struct mgb4_i2c_kv gmsl1_i2c[] = { static const struct v4l2_dv_timings_cap video_timings_cap = { .type = V4L2_DV_BT_656_1120, .bt = { - .min_width = 240, + .min_width = 64, .max_width = 4096, - .min_height = 240, + .min_height = 64, .max_height = 4096, - .min_pixelclock = 1843200, /* 320 x 240 x 24Hz */ - .max_pixelclock = 530841600, /* 4096 x 2160 x 60Hz */ + .min_pixelclock = 25000000, + .max_pixelclock = 189284000, .standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT | V4L2_DV_BT_STD_GTF, .capabilities = V4L2_DV_BT_CAP_PROGRESSIVE | diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index 4a51b873e47a..2b1672737d84 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -1714,8 +1714,10 @@ int saa7134_video_init1(struct saa7134_dev *dev) q->dev = &dev->pci->dev; ret = vb2_queue_init(q); if (ret) - return ret; - saa7134_pgtable_alloc(dev->pci, &dev->video_q.pt); + goto err_free_ctrl; + ret = saa7134_pgtable_alloc(dev->pci, &dev->video_q.pt); + if (ret) + goto err_free_ctrl; q = &dev->vbi_vbq; q->type = V4L2_BUF_TYPE_VBI_CAPTURE; @@ -1732,11 +1734,24 @@ int saa7134_video_init1(struct saa7134_dev *dev) q->lock = &dev->lock; q->dev = &dev->pci->dev; ret = vb2_queue_init(q); - if (ret) - return ret; - saa7134_pgtable_alloc(dev->pci, &dev->vbi_q.pt); + if (ret) { + saa7134_pgtable_free(dev->pci, &dev->video_q.pt); + goto err_free_ctrl; + } + + ret = saa7134_pgtable_alloc(dev->pci, &dev->vbi_q.pt); + if (ret) { + saa7134_pgtable_free(dev->pci, &dev->video_q.pt); + goto err_free_ctrl; + } return 0; + +err_free_ctrl: + v4l2_ctrl_handler_free(&dev->ctrl_handler); + if (card_has_radio(dev)) + v4l2_ctrl_handler_free(&dev->radio_ctrl_handler); + return ret; } void saa7134_video_fini(struct saa7134_dev *dev) diff --git a/drivers/media/pci/solo6x10/Kconfig b/drivers/media/pci/solo6x10/Kconfig index adb247847e00..cc4a7b088370 100644 --- a/drivers/media/pci/solo6x10/Kconfig +++ b/drivers/media/pci/solo6x10/Kconfig @@ -8,7 +8,6 @@ config VIDEO_SOLO6X10 select VIDEOBUF2_DMA_SG select VIDEOBUF2_DMA_CONTIG select SND_PCM - select FONT_8x16 help This driver supports the Bluecherry H.264 and MPEG-4 hardware compression capture cards and other Softlogic-based ones. diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 3f0b7bb68cc9..0b33e927bd59 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -63,6 +63,7 @@ config VIDEO_MUX # Platform drivers - Please keep it alphabetically sorted source "drivers/media/platform/allegro-dvt/Kconfig" +source "drivers/media/platform/amd/Kconfig" source "drivers/media/platform/amlogic/Kconfig" source "drivers/media/platform/amphion/Kconfig" source "drivers/media/platform/arm/Kconfig" diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 6d5f79ddfcc3..16c185752474 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -6,6 +6,7 @@ # Place here, alphabetically sorted by directory # (e. g. LC_ALL=C sort Makefile) obj-y += allegro-dvt/ +obj-y += amd/ obj-y += amlogic/ obj-y += amphion/ obj-y += arm/ diff --git a/drivers/media/platform/amd/Kconfig b/drivers/media/platform/amd/Kconfig new file mode 100644 index 000000000000..25af49f246b2 --- /dev/null +++ b/drivers/media/platform/amd/Kconfig @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0+ + +source "drivers/media/platform/amd/isp4/Kconfig" diff --git a/drivers/media/platform/amd/Makefile b/drivers/media/platform/amd/Makefile new file mode 100644 index 000000000000..8bfc1955f22e --- /dev/null +++ b/drivers/media/platform/amd/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-y += isp4/ diff --git a/drivers/media/platform/amd/isp4/Kconfig b/drivers/media/platform/amd/isp4/Kconfig new file mode 100644 index 000000000000..9d1927af1cb8 --- /dev/null +++ b/drivers/media/platform/amd/isp4/Kconfig @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0+ + +config VIDEO_AMD_ISP4_CAPTURE + tristate "AMD ISP4 and camera driver" + depends on DRM_AMDGPU && DRM_AMD_ISP + depends on HAS_DMA + depends on VIDEO_DEV + select VIDEOBUF2_CORE + select VIDEOBUF2_MEMOPS + select VIDEOBUF2_V4L2 + select VIDEOBUF2_VMALLOC + select VIDEO_V4L2_SUBDEV_API + help + This is support for AMD ISP4 and camera subsystem driver. + Say Y here to enable the ISP4 and camera device for video capture. + To compile this driver as a module, choose M here. The module will + be called amd_isp4_capture. diff --git a/drivers/media/platform/amd/isp4/Makefile b/drivers/media/platform/amd/isp4/Makefile new file mode 100644 index 000000000000..3849062e17f3 --- /dev/null +++ b/drivers/media/platform/amd/isp4/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (C) 2025 Advanced Micro Devices, Inc. + +obj-$(CONFIG_VIDEO_AMD_ISP4_CAPTURE) += amd_isp4_capture.o +amd_isp4_capture-objs := isp4.o \ + isp4_debug.o \ + isp4_interface.o \ + isp4_subdev.o \ + isp4_video.o diff --git a/drivers/media/platform/amd/isp4/isp4.c b/drivers/media/platform/amd/isp4/isp4.c new file mode 100644 index 000000000000..bf6b8e26c2c0 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4.c @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#include <linux/irq.h> +#include <linux/pm_runtime.h> +#include <linux/vmalloc.h> +#include <media/v4l2-ioctl.h> + +#include "isp4.h" +#include "isp4_debug.h" +#include "isp4_hw_reg.h" + +#define ISP4_DRV_NAME "amd_isp_capture" +#define ISP4_FW_RESP_RB_IRQ_STATUS_MASK \ + (ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT9_INT_MASK | \ + ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT12_INT_MASK) + +static const struct { + const char *name; + u32 status_mask; + u32 en_mask; + u32 ack_mask; + u32 rb_int_num; +} isp4_irq[ISP4SD_MAX_FW_RESP_STREAM_NUM] = { + /* The IRQ order is aligned with the isp4_subdev.fw_resp_thread order */ + { + .name = "isp_irq_global", + .status_mask = + ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT12_INT_MASK, + .en_mask = ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT12_EN_MASK, + .ack_mask = ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT12_ACK_MASK, + .rb_int_num = 4, /* ISP_4_1__SRCID__ISP_RINGBUFFER_WPT12 */ + }, + { + .name = "isp_irq_stream1", + .status_mask = + ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT9_INT_MASK, + .en_mask = ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT9_EN_MASK, + .ack_mask = ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT9_ACK_MASK, + .rb_int_num = 0, /* ISP_4_1__SRCID__ISP_RINGBUFFER_WPT9 */ + }, +}; + +void isp4_intr_enable(struct isp4_subdev *isp_subdev, u32 index, bool enable) +{ + u32 intr_en; + + /* Synchronize ISP_SYS_INT0_EN writes with the IRQ handler's writes */ + spin_lock_irq(&isp_subdev->irq_lock); + intr_en = isp4hw_rreg(isp_subdev->mmio, ISP_SYS_INT0_EN); + if (enable) + intr_en |= isp4_irq[index].en_mask; + else + intr_en &= ~isp4_irq[index].en_mask; + + isp4hw_wreg(isp_subdev->mmio, ISP_SYS_INT0_EN, intr_en); + spin_unlock_irq(&isp_subdev->irq_lock); +} + +static void isp4_wake_up_resp_thread(struct isp4_subdev *isp_subdev, u32 index) +{ + struct isp4sd_thread_handler *thread_ctx = + &isp_subdev->fw_resp_thread[index]; + + thread_ctx->resp_ready = true; + wake_up_interruptible(&thread_ctx->waitq); +} + +static irqreturn_t isp4_irq_handler(int irq, void *arg) +{ + struct isp4_subdev *isp_subdev = arg; + u32 intr_ack = 0, intr_en = 0, intr_status; + int seen = 0; + + /* Get the ISP_SYS interrupt status */ + intr_status = isp4hw_rreg(isp_subdev->mmio, ISP_SYS_INT0_STATUS); + intr_status &= ISP4_FW_RESP_RB_IRQ_STATUS_MASK; + + /* Find which ISP_SYS interrupts fired */ + for (size_t i = 0; i < ARRAY_SIZE(isp4_irq); i++) { + if (intr_status & isp4_irq[i].status_mask) { + intr_ack |= isp4_irq[i].ack_mask; + intr_en |= isp4_irq[i].en_mask; + seen |= BIT(i); + } + } + + /* + * Disable the ISP_SYS interrupts that fired. Must be done before waking + * the response threads, since they re-enable interrupts when finished. + * The lock synchronizes RMW of INT0_EN with isp4_enable_interrupt(). + */ + spin_lock(&isp_subdev->irq_lock); + intr_en = isp4hw_rreg(isp_subdev->mmio, ISP_SYS_INT0_EN) & ~intr_en; + isp4hw_wreg(isp_subdev->mmio, ISP_SYS_INT0_EN, intr_en); + spin_unlock(&isp_subdev->irq_lock); + + /* + * Clear the ISP_SYS interrupts. This must be done after the interrupts + * are disabled, so that ISP FW won't flag any new interrupts on these + * streams, and thus we don't need to clear interrupts again before + * re-enabling them in the response thread. + */ + isp4hw_wreg(isp_subdev->mmio, ISP_SYS_INT0_ACK, intr_ack); + + /* + * The operation `(seen >> i) << i` is logically equivalent to + * `seen &= ~BIT(i)`, with fewer instructions after compilation. + */ + for (int i; (i = ffs(seen)); seen = (seen >> i) << i) + isp4_wake_up_resp_thread(isp_subdev, i - 1); + + return IRQ_HANDLED; +} + +static int isp4_capture_probe(struct platform_device *pdev) +{ + int irq[ISP4SD_MAX_FW_RESP_STREAM_NUM]; + struct device *dev = &pdev->dev; + struct isp4_subdev *isp_subdev; + struct isp4_device *isp_dev; + int ret; + + isp_dev = devm_kzalloc(dev, sizeof(*isp_dev), GFP_KERNEL); + if (!isp_dev) + return -ENOMEM; + + dev->init_name = ISP4_DRV_NAME; + + isp_subdev = &isp_dev->isp_subdev; + isp_subdev->mmio = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(isp_subdev->mmio)) + return dev_err_probe(dev, PTR_ERR(isp_subdev->mmio), + "isp ioremap fail\n"); + + for (size_t i = 0; i < ARRAY_SIZE(isp4_irq); i++) { + irq[i] = platform_get_irq(pdev, isp4_irq[i].rb_int_num); + if (irq[i] < 0) + return dev_err_probe(dev, irq[i], + "fail to get irq %d\n", + isp4_irq[i].rb_int_num); + + ret = devm_request_irq(dev, irq[i], isp4_irq_handler, + IRQF_NO_AUTOEN, isp4_irq[i].name, + isp_subdev); + if (ret) + return dev_err_probe(dev, ret, "fail to req irq %d\n", + irq[i]); + } + + isp_dev->v4l2_dev.mdev = &isp_dev->mdev; + + strscpy(isp_dev->mdev.model, "amd_isp41_mdev", + sizeof(isp_dev->mdev.model)); + isp_dev->mdev.dev = dev; + media_device_init(&isp_dev->mdev); + + snprintf(isp_dev->v4l2_dev.name, sizeof(isp_dev->v4l2_dev.name), + "AMD-V4L2-ROOT"); + ret = v4l2_device_register(dev, &isp_dev->v4l2_dev); + if (ret) { + dev_err_probe(dev, ret, "fail register v4l2 device\n"); + goto err_clean_media; + } + + pm_runtime_set_suspended(dev); + pm_runtime_enable(dev); + spin_lock_init(&isp_subdev->irq_lock); + ret = isp4sd_init(&isp_dev->isp_subdev, &isp_dev->v4l2_dev, irq); + if (ret) { + dev_err_probe(dev, ret, "fail init isp4 sub dev\n"); + goto err_pm_disable; + } + + ret = media_create_pad_link(&isp_dev->isp_subdev.sdev.entity, + 0, + &isp_dev->isp_subdev.isp_vdev.vdev.entity, + 0, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) { + dev_err_probe(dev, ret, "fail to create pad link\n"); + goto err_isp4_deinit; + } + + ret = media_device_register(&isp_dev->mdev); + if (ret) { + dev_err_probe(dev, ret, "fail to register media device\n"); + goto err_isp4_deinit; + } + + platform_set_drvdata(pdev, isp_dev); + isp_debugfs_create(isp_dev); + + return 0; + +err_isp4_deinit: + isp4sd_deinit(&isp_dev->isp_subdev); +err_pm_disable: + pm_runtime_disable(dev); + v4l2_device_unregister(&isp_dev->v4l2_dev); +err_clean_media: + media_device_cleanup(&isp_dev->mdev); + + return ret; +} + +static void isp4_capture_remove(struct platform_device *pdev) +{ + struct isp4_device *isp_dev = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + + isp_debugfs_remove(isp_dev); + + media_device_unregister(&isp_dev->mdev); + isp4sd_deinit(&isp_dev->isp_subdev); + pm_runtime_disable(dev); + v4l2_device_unregister(&isp_dev->v4l2_dev); + media_device_cleanup(&isp_dev->mdev); +} + +static struct platform_driver isp4_capture_drv = { + .probe = isp4_capture_probe, + .remove = isp4_capture_remove, + .driver = { + .name = ISP4_DRV_NAME, + } +}; + +module_platform_driver(isp4_capture_drv); + +MODULE_ALIAS("platform:" ISP4_DRV_NAME); +MODULE_IMPORT_NS("DMA_BUF"); + +MODULE_DESCRIPTION("AMD ISP4 Driver"); +MODULE_AUTHOR("Bin Du <bin.du@amd.com>"); +MODULE_AUTHOR("Pratap Nirujogi <pratap.nirujogi@amd.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/amd/isp4/isp4.h b/drivers/media/platform/amd/isp4/isp4.h new file mode 100644 index 000000000000..2db6683d6d8b --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _ISP4_H_ +#define _ISP4_H_ + +#include <drm/amd/isp.h> +#include "isp4_subdev.h" + +struct isp4_device { + struct v4l2_device v4l2_dev; + struct isp4_subdev isp_subdev; + struct media_device mdev; +}; + +void isp4_intr_enable(struct isp4_subdev *isp_subdev, u32 index, bool enable); + +#endif /* _ISP4_H_ */ diff --git a/drivers/media/platform/amd/isp4/isp4_debug.c b/drivers/media/platform/amd/isp4/isp4_debug.c new file mode 100644 index 000000000000..2fc00fc9a194 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_debug.c @@ -0,0 +1,271 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#include "isp4.h" +#include "isp4_debug.h" +#include "isp4_hw_reg.h" +#include "isp4_interface.h" + +#define ISP4DBG_FW_LOG_RINGBUF_SIZE (2 * 1024 * 1024) +#define ISP4DBG_MACRO_2_STR(X) #X +#define ISP4DBG_ONE_TIME_LOG_LEN 510 + +#ifdef CONFIG_DEBUG_FS + +void isp_debugfs_create(struct isp4_device *isp_dev) +{ + isp_dev->isp_subdev.debugfs_dir = debugfs_create_dir("amd_isp4", NULL); + debugfs_create_bool("fw_log_enable", 0644, + isp_dev->isp_subdev.debugfs_dir, + &isp_dev->isp_subdev.enable_fw_log); + isp_dev->isp_subdev.fw_log_output = + devm_kzalloc(isp_dev->isp_subdev.dev, + ISP4DBG_FW_LOG_RINGBUF_SIZE + 32, + GFP_KERNEL); +} + +void isp_debugfs_remove(struct isp4_device *isp_dev) +{ + debugfs_remove_recursive(isp_dev->isp_subdev.debugfs_dir); + isp_dev->isp_subdev.debugfs_dir = NULL; +} + +static u32 isp_fw_fill_rb_log(struct isp4_subdev *isp, void *sys, u32 rb_size) +{ + struct isp4_interface *ispif = &isp->ispif; + char *buf = isp->fw_log_output; + struct device *dev = isp->dev; + u32 rd_ptr, wr_ptr; + u32 total_cnt = 0; + u32 offset = 0; + u32 cnt; + + if (!sys || !rb_size) + return 0; + + guard(mutex)(&ispif->isp4if_mutex); + + rd_ptr = isp4hw_rreg(isp->mmio, ISP_LOG_RB_RPTR0); + wr_ptr = isp4hw_rreg(isp->mmio, ISP_LOG_RB_WPTR0); + + do { + if (wr_ptr > rd_ptr) + cnt = wr_ptr - rd_ptr; + else if (wr_ptr < rd_ptr) + cnt = rb_size - rd_ptr; + else + goto quit; + + if (cnt > rb_size) { + dev_err(dev, "fail bad fw log size %u\n", cnt); + goto quit; + } + + memcpy(buf + offset, sys + rd_ptr, cnt); + + offset += cnt; + total_cnt += cnt; + rd_ptr = (rd_ptr + cnt) % rb_size; + } while (rd_ptr < wr_ptr); + + isp4hw_wreg(isp->mmio, ISP_LOG_RB_RPTR0, rd_ptr); + +quit: + return total_cnt; +} + +void isp_fw_log_print(struct isp4_subdev *isp) +{ + struct isp4_interface *ispif = &isp->ispif; + char *fw_log_buf = isp->fw_log_output; + u32 cnt; + + if (!isp->enable_fw_log || !fw_log_buf) + return; + + cnt = isp_fw_fill_rb_log(isp, ispif->fw_log_buf->sys_addr, + ispif->fw_log_buf->mem_size); + + if (cnt) { + char temp_ch; + char *str; + char *end; + /* line end */ + char *le; + + str = (char *)fw_log_buf; + end = ((char *)fw_log_buf + cnt); + fw_log_buf[cnt] = 0; + + while (str < end) { + le = strchr(str, 0x0A); + if ((le && str + ISP4DBG_ONE_TIME_LOG_LEN >= le) || + (!le && str + ISP4DBG_ONE_TIME_LOG_LEN >= end)) { + if (le) + *le = 0; + + if (*str != '\0') + dev_dbg(isp->dev, "%s", str); + + if (le) { + *le = 0x0A; + str = le + 1; + } else { + break; + } + } else { + u32 tmp_len = ISP4DBG_ONE_TIME_LOG_LEN; + + temp_ch = str[tmp_len]; + str[tmp_len] = 0; + dev_dbg(isp->dev, "%s", str); + str[tmp_len] = temp_ch; + str = &str[tmp_len]; + } + } + } +} +#endif + +char *isp4dbg_get_buf_src_str(u32 src) +{ + switch (src) { + case ISP4FW_BUFFER_SOURCE_STREAM: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_SOURCE_STREAM); + default: + return "Unknown buf source"; + } +} + +char *isp4dbg_get_buf_done_str(u32 status) +{ + switch (status) { + case ISP4FW_BUFFER_STATUS_INVALID: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_INVALID); + case ISP4FW_BUFFER_STATUS_SKIPPED: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_SKIPPED); + case ISP4FW_BUFFER_STATUS_EXIST: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_EXIST); + case ISP4FW_BUFFER_STATUS_DONE: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_DONE); + case ISP4FW_BUFFER_STATUS_LACK: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_LACK); + case ISP4FW_BUFFER_STATUS_DIRTY: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_DIRTY); + case ISP4FW_BUFFER_STATUS_MAX: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_MAX); + default: + return "Unknown Buf Done Status"; + } +} + +char *isp4dbg_get_img_fmt_str(int fmt /* enum isp4fw_image_format * */) +{ + switch (fmt) { + case ISP4FW_IMAGE_FORMAT_NV12: + return "NV12"; + case ISP4FW_IMAGE_FORMAT_YUV422INTERLEAVED: + return "YUV422INTERLEAVED"; + default: + return "unknown fmt"; + } +} + +void isp4dbg_show_bufmeta_info(struct device *dev, char *pre, + void *in, void *orig_buf) +{ + struct isp4fw_buffer_meta_info *p; + struct isp4if_img_buf_info *orig; + + if (!in) + return; + + if (!pre) + pre = ""; + + p = in; + orig = orig_buf; + + dev_dbg(dev, "%s(%s) en:%d,stat:%s(%u),src:%s\n", pre, + isp4dbg_get_img_fmt_str(p->image_prop.image_format), + p->enabled, isp4dbg_get_buf_done_str(p->status), p->status, + isp4dbg_get_buf_src_str(p->source)); + + dev_dbg(dev, "%p,0x%llx(%u) %p,0x%llx(%u) %p,0x%llx(%u)\n", + orig->planes[0].sys_addr, orig->planes[0].mc_addr, + orig->planes[0].len, orig->planes[1].sys_addr, + orig->planes[1].mc_addr, orig->planes[1].len, + orig->planes[2].sys_addr, orig->planes[2].mc_addr, + orig->planes[2].len); +} + +char *isp4dbg_get_buf_type(u32 type) +{ + /* enum isp4fw_buffer_type */ + switch (type) { + case ISP4FW_BUFFER_TYPE_PREVIEW: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_TYPE_PREVIEW); + case ISP4FW_BUFFER_TYPE_META_INFO: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_TYPE_META_INFO); + case ISP4FW_BUFFER_TYPE_MEM_POOL: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_TYPE_MEM_POOL); + default: + return "unknown type"; + } +} + +char *isp4dbg_get_cmd_str(u32 cmd) +{ + switch (cmd) { + case ISP4FW_CMD_ID_START_STREAM: + return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_START_STREAM); + case ISP4FW_CMD_ID_STOP_STREAM: + return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_STOP_STREAM); + case ISP4FW_CMD_ID_SEND_BUFFER: + return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_SEND_BUFFER); + case ISP4FW_CMD_ID_SET_STREAM_CONFIG: + return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_SET_STREAM_CONFIG); + case ISP4FW_CMD_ID_SET_OUT_CHAN_PROP: + return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_SET_OUT_CHAN_PROP); + case ISP4FW_CMD_ID_ENABLE_OUT_CHAN: + return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_ENABLE_OUT_CHAN); + default: + return "unknown cmd"; + } +} + +char *isp4dbg_get_resp_str(u32 cmd) +{ + switch (cmd) { + case ISP4FW_RESP_ID_CMD_DONE: + return ISP4DBG_MACRO_2_STR(ISP4FW_RESP_ID_CMD_DONE); + case ISP4FW_RESP_ID_NOTI_FRAME_DONE: + return ISP4DBG_MACRO_2_STR(ISP4FW_RESP_ID_NOTI_FRAME_DONE); + default: + return "unknown respid"; + } +} + +char *isp4dbg_get_if_stream_str(u32 stream /* enum fw_cmd_resp_stream_id */) +{ + switch (stream) { + case ISP4IF_STREAM_ID_GLOBAL: + return "STREAM_GLOBAL"; + case ISP4IF_STREAM_ID_1: + return "STREAM1"; + default: + return "unknown streamID"; + } +} + +char *isp4dbg_get_out_ch_str(int ch /* enum isp4fw_pipe_out_ch */) +{ + switch ((enum isp4fw_pipe_out_ch)ch) { + case ISP4FW_ISP_PIPE_OUT_CH_PREVIEW: + return "prev"; + default: + return "unknown channel"; + } +} diff --git a/drivers/media/platform/amd/isp4/isp4_debug.h b/drivers/media/platform/amd/isp4/isp4_debug.h new file mode 100644 index 000000000000..d1262e03ae64 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_debug.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _ISP4_DEBUG_H_ +#define _ISP4_DEBUG_H_ + +#include <linux/dev_printk.h> +#include <linux/printk.h> + +#include "isp4_subdev.h" + +#ifdef CONFIG_DEBUG_FS +struct isp4_device; + +void isp_debugfs_create(struct isp4_device *isp_dev); +void isp_debugfs_remove(struct isp4_device *isp_dev); +void isp_fw_log_print(struct isp4_subdev *isp); + +#else + +/* to avoid checkpatch warning */ +#define isp_debugfs_create(cam) ((void)(cam)) +#define isp_debugfs_remove(cam) ((void)(cam)) +#define isp_fw_log_print(isp) ((void)(isp)) + +#endif /* CONFIG_DEBUG_FS */ + +void isp4dbg_show_bufmeta_info(struct device *dev, char *pre, void *p, + void *orig_buf /* struct sys_img_buf_handle */); +char *isp4dbg_get_img_fmt_str(int fmt /* enum _image_format_t */); +char *isp4dbg_get_out_ch_str(int ch /* enum _isp_pipe_out_ch_t */); +char *isp4dbg_get_cmd_str(u32 cmd); +char *isp4dbg_get_buf_type(u32 type);/* enum _buffer_type_t */ +char *isp4dbg_get_resp_str(u32 resp); +char *isp4dbg_get_buf_src_str(u32 src); +char *isp4dbg_get_buf_done_str(u32 status); +char *isp4dbg_get_if_stream_str(u32 stream); + +#endif /* _ISP4_DEBUG_H_ */ diff --git a/drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h b/drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h new file mode 100644 index 000000000000..88bacb00355c --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h @@ -0,0 +1,318 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _ISP4_FW_CMD_RESP_H_ +#define _ISP4_FW_CMD_RESP_H_ + +/* + * Two types of command/response channel. + * Type Global Command has one command/response channel. + * Type Stream Command has one command/response channel. + *----------- ------------ + *| | --------------------------- | | + *| | ---->| Global Command |----> | | + *| | --------------------------- | | + *| | | | + *| | | | + *| | --------------------------- | | + *| | ---->| Stream Command |----> | | + *| | --------------------------- | | + *| | | | + *| | | | + *| | | | + *| HOST | | Firmware | + *| | | | + *| | | | + *| | -------------------------- | | + *| | <----| Global Response |<---- | | + *| | -------------------------- | | + *| | | | + *| | | | + *| | -------------------------- | | + *| | <----| Stream Response |<---- | | + *| | -------------------------- | | + *| | | | + *| | | | + *----------- ------------ + */ + +/* + * cmd_id is in the format of following type: + * type: indicate command type, global/stream commands. + * group: indicate the command group. + * id: A unique command identification in one type and group. + * |<-Bit31 ~ Bit24->|<-Bit23 ~ Bit16->|<-Bit15 ~ Bit0->| + * | type | group | id | + */ + +#define ISP4FW_CMD_TYPE_SHIFT 24 +#define ISP4FW_CMD_GROUP_SHIFT 16 +#define ISP4FW_CMD_TYPE_STREAM_CTRL (0x2U << ISP4FW_CMD_TYPE_SHIFT) + +#define ISP4FW_CMD_GROUP_STREAM_CTRL (0x1U << ISP4FW_CMD_GROUP_SHIFT) +#define ISP4FW_CMD_GROUP_STREAM_BUFFER (0x4U << ISP4FW_CMD_GROUP_SHIFT) + +/* Stream Command */ +#define ISP4FW_CMD_ID_SET_STREAM_CONFIG (ISP4FW_CMD_TYPE_STREAM_CTRL\ + | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x1) +#define ISP4FW_CMD_ID_SET_OUT_CHAN_PROP (ISP4FW_CMD_TYPE_STREAM_CTRL\ + | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x3) +#define ISP4FW_CMD_ID_ENABLE_OUT_CHAN (ISP4FW_CMD_TYPE_STREAM_CTRL\ + | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x5) +#define ISP4FW_CMD_ID_START_STREAM (ISP4FW_CMD_TYPE_STREAM_CTRL\ + | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x7) +#define ISP4FW_CMD_ID_STOP_STREAM (ISP4FW_CMD_TYPE_STREAM_CTRL\ + | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x8) + +/* Stream Buffer Command */ +#define ISP4FW_CMD_ID_SEND_BUFFER (ISP4FW_CMD_TYPE_STREAM_CTRL\ + | ISP4FW_CMD_GROUP_STREAM_BUFFER | 0x1) + +/* + * resp_id is in the format of following type: + * type: indicate command type, global/stream commands. + * group: indicate the command group. + * id: A unique command identification in one type and group. + * |<-Bit31 ~ Bit24->|<-Bit23 ~ Bit16->|<-Bit15 ~ Bit0->| + * | type | group | id | + */ + +#define ISP4FW_RESP_GROUP_SHIFT 16 + +#define ISP4FW_RESP_GROUP_GENERAL (0x1 << ISP4FW_RESP_GROUP_SHIFT) +#define ISP4FW_RESP_GROUP_NOTIFICATION (0x3 << ISP4FW_RESP_GROUP_SHIFT) + +/* General Response */ +#define ISP4FW_RESP_ID_CMD_DONE (ISP4FW_RESP_GROUP_GENERAL | 0x1) + +/* Notification */ +#define ISP4FW_RESP_ID_NOTI_FRAME_DONE (ISP4FW_RESP_GROUP_NOTIFICATION | 0x1) + +#define ISP4FW_CMD_STATUS_SUCCESS 0 +#define ISP4FW_CMD_STATUS_FAIL 1 +#define ISP4FW_CMD_STATUS_SKIPPED 2 + +#define ISP4FW_ADDR_SPACE_TYPE_GPU_VA 4 + +#define ISP4FW_MEMORY_POOL_SIZE (100 * 1024 * 1024) + +/* + * standard ISP pipeline: mipicsi=>isp + */ +#define ISP4FW_MIPI0_ISP_PIPELINE_ID 0x5f91 + +enum isp4fw_sensor_id { + /* Sensor id for ISP input from MIPI port 0 */ + ISP4FW_SENSOR_ID_ON_MIPI0 = 0, +}; + +enum isp4fw_stream_id { + ISP4FW_STREAM_ID_INVALID = -1, + ISP4FW_STREAM_ID_1 = 0, + ISP4FW_STREAM_ID_2 = 1, + ISP4FW_STREAM_ID_3 = 2, + ISP4FW_STREAM_ID_MAXIMUM +}; + +enum isp4fw_image_format { + /* 4:2:0,semi-planar, 8-bit */ + ISP4FW_IMAGE_FORMAT_NV12 = 1, + /* interleave, 4:2:2, 8-bit */ + ISP4FW_IMAGE_FORMAT_YUV422INTERLEAVED = 7, +}; + +enum isp4fw_pipe_out_ch { + ISP4FW_ISP_PIPE_OUT_CH_PREVIEW = 0, +}; + +enum isp4fw_yuv_range { + ISP4FW_ISP_YUV_RANGE_FULL = 0, /* YUV value range in 0~255 */ + ISP4FW_ISP_YUV_RANGE_NARROW = 1, /* YUV value range in 16~235 */ + ISP4FW_ISP_YUV_RANGE_MAX +}; + +enum isp4fw_buffer_type { + ISP4FW_BUFFER_TYPE_PREVIEW = 8, + ISP4FW_BUFFER_TYPE_META_INFO = 10, + ISP4FW_BUFFER_TYPE_MEM_POOL = 15, +}; + +enum isp4fw_buffer_status { + /* The buffer is INVALID */ + ISP4FW_BUFFER_STATUS_INVALID, + /* The buffer is not filled with image data */ + ISP4FW_BUFFER_STATUS_SKIPPED, + /* The buffer is available and awaiting to be filled */ + ISP4FW_BUFFER_STATUS_EXIST, + /* The buffer is filled with image data */ + ISP4FW_BUFFER_STATUS_DONE, + /* The buffer is unavailable */ + ISP4FW_BUFFER_STATUS_LACK, + /* The buffer is dirty, probably caused by LMI leakage */ + ISP4FW_BUFFER_STATUS_DIRTY, + ISP4FW_BUFFER_STATUS_MAX +}; + +enum isp4fw_buffer_source { + /* The buffer is from the stream buffer queue */ + ISP4FW_BUFFER_SOURCE_STREAM, +}; + +struct isp4fw_error_code { + u32 code1; + u32 code2; + u32 code3; + u32 code4; + u32 code5; +}; + +/* Command Structure for FW */ + +struct isp4fw_cmd { + u32 cmd_seq_num; + u32 cmd_id; + u32 cmd_param[12]; + u16 cmd_stream_id; + u8 cmd_silent_resp; + u8 reserved; + u32 cmd_check_sum; +}; + +struct isp4fw_resp_cmd_done { + /* + * The host2fw command seqNum. + * To indicate which command this response refers to. + */ + u32 cmd_seq_num; + /* The host2fw command id for host double check. */ + u32 cmd_id; + /* + * Indicate the command process status. + * 0 means success. 1 means fail. 2 means skipped + */ + u16 cmd_status; + /* + * If cmd_status is 1, the command failed. The host can check + * isp4fw_error_code for details. + */ + u16 isp4fw_error_code; + /* The response payload type varies by cmd. */ + u8 payload[36]; +}; + +struct isp4fw_resp_param_package { + u32 package_addr_lo; /* The low 32 bit of the pkg address. */ + u32 package_addr_hi; /* The high 32 bit of the pkg address. */ + u32 package_size; /* The total pkg size in bytes. */ + u32 package_check_sum; /* The byte sum of the pkg. */ +}; + +struct isp4fw_resp { + u32 resp_seq_num; + u32 resp_id; + union { + struct isp4fw_resp_cmd_done cmd_done; + struct isp4fw_resp_param_package frame_done; + u32 resp_param[12]; + } param; + u8 reserved[4]; + u32 resp_check_sum; +}; + +struct isp4fw_mipi_pipe_path_cfg { + u32 b_enable; + enum isp4fw_sensor_id isp4fw_sensor_id; +}; + +struct isp4fw_isp_pipe_path_cfg { + u32 isp_pipe_id; /* pipe ids for pipeline construction */ +}; + +struct isp4fw_isp_stream_cfg { + /* Isp mipi path */ + struct isp4fw_mipi_pipe_path_cfg mipi_pipe_path_cfg; + /* Isp pipe path */ + struct isp4fw_isp_pipe_path_cfg isp_pipe_path_cfg; + /* enable TNR */ + u32 b_enable_tnr; + /* + * Number of frames for RTA processing. + * Set to 0 to use the firmware's default value. + */ + u32 rta_frames_per_proc; +}; + +struct isp4fw_image_prop { + enum isp4fw_image_format image_format; + u32 width; + u32 height; + u32 luma_pitch; + u32 chroma_pitch; + enum isp4fw_yuv_range yuv_range; +}; + +struct isp4fw_buffer { + /* + * A check num for debug usage, host can set the buf_tags + * to different numbers + */ + u32 buf_tags; + union { + u32 value; + struct { + u32 space : 16; + u32 vmid : 16; + } bit; + } vmid_space; + u32 buf_base_a_lo; /* Low address of buffer A */ + u32 buf_base_a_hi; /* High address of buffer A */ + u32 buf_size_a; /* Buffer size of buffer A */ + + u32 buf_base_b_lo; /* Low address of buffer B */ + u32 buf_base_b_hi; /* High address of buffer B */ + u32 buf_size_b; /* Buffer size of buffer B */ + + u32 buf_base_c_lo; /* Low address of buffer C */ + u32 buf_base_c_hi; /* High address of buffer C */ + u32 buf_size_c; /* Buffer size of buffer C */ +}; + +struct isp4fw_buffer_meta_info { + u32 enabled; /* enabled flag */ + enum isp4fw_buffer_status status; /* BufferStatus */ + struct isp4fw_error_code err; /* err code */ + enum isp4fw_buffer_source source; /* BufferSource */ + struct isp4fw_image_prop image_prop; /* image_prop */ + struct isp4fw_buffer buffer; /* buffer info */ +}; + +struct isp4fw_meta_info { + u32 poc; /* frame id */ + u32 fc_id; /* frame ctl id */ + u32 time_stamp_lo; /* timestamp low 32 bits */ + u32 time_stamp_hi; /* timestamp_high 32 bits */ + struct isp4fw_buffer_meta_info preview; /* preview BufferMetaInfo */ +}; + +struct isp4fw_cmd_send_buffer { + enum isp4fw_buffer_type buffer_type; + struct isp4fw_buffer buffer; /* buffer info */ +}; + +struct isp4fw_cmd_set_out_ch_prop { + enum isp4fw_pipe_out_ch ch; /* ISP output channel */ + struct isp4fw_image_prop image_prop; /* image property */ +}; + +struct isp4fw_cmd_enable_out_ch { + enum isp4fw_pipe_out_ch ch; /* ISP output channel */ + u32 is_enable; /* If channel is enabled or not */ +}; + +struct isp4fw_cmd_set_stream_cfg { + struct isp4fw_isp_stream_cfg stream_cfg; /* stream path config */ +}; + +#endif /* _ISP4_FW_CMD_RESP_H_ */ diff --git a/drivers/media/platform/amd/isp4/isp4_hw_reg.h b/drivers/media/platform/amd/isp4/isp4_hw_reg.h new file mode 100644 index 000000000000..09c76f75c5ee --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_hw_reg.h @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _ISP4_HW_REG_H_ +#define _ISP4_HW_REG_H_ + +#include <linux/io.h> + +#define ISP_SOFT_RESET 0x62000 +#define ISP_SYS_INT0_EN 0x62010 +#define ISP_SYS_INT0_STATUS 0x62014 +#define ISP_SYS_INT0_ACK 0x62018 +#define ISP_CCPU_CNTL 0x62054 +#define ISP_STATUS 0x62058 +#define ISP_LOG_RB_BASE_LO0 0x62148 +#define ISP_LOG_RB_BASE_HI0 0x6214c +#define ISP_LOG_RB_SIZE0 0x62150 +#define ISP_LOG_RB_RPTR0 0x62154 +#define ISP_LOG_RB_WPTR0 0x62158 +#define ISP_RB_BASE_LO1 0x62170 +#define ISP_RB_BASE_HI1 0x62174 +#define ISP_RB_SIZE1 0x62178 +#define ISP_RB_RPTR1 0x6217c +#define ISP_RB_WPTR1 0x62180 +#define ISP_RB_BASE_LO2 0x62184 +#define ISP_RB_BASE_HI2 0x62188 +#define ISP_RB_SIZE2 0x6218c +#define ISP_RB_RPTR2 0x62190 +#define ISP_RB_WPTR2 0x62194 +#define ISP_RB_BASE_LO3 0x62198 +#define ISP_RB_BASE_HI3 0x6219c +#define ISP_RB_SIZE3 0x621a0 +#define ISP_RB_RPTR3 0x621a4 +#define ISP_RB_WPTR3 0x621a8 +#define ISP_RB_BASE_LO4 0x621ac +#define ISP_RB_BASE_HI4 0x621b0 +#define ISP_RB_SIZE4 0x621b4 +#define ISP_RB_RPTR4 0x621b8 +#define ISP_RB_WPTR4 0x621bc +#define ISP_RB_BASE_LO5 0x621c0 +#define ISP_RB_BASE_HI5 0x621c4 +#define ISP_RB_SIZE5 0x621c8 +#define ISP_RB_RPTR5 0x621cc +#define ISP_RB_WPTR5 0x621d0 +#define ISP_RB_BASE_LO6 0x621d4 +#define ISP_RB_BASE_HI6 0x621d8 +#define ISP_RB_SIZE6 0x621dc +#define ISP_RB_RPTR6 0x621e0 +#define ISP_RB_WPTR6 0x621e4 +#define ISP_RB_BASE_LO7 0x621e8 +#define ISP_RB_BASE_HI7 0x621ec +#define ISP_RB_SIZE7 0x621f0 +#define ISP_RB_RPTR7 0x621f4 +#define ISP_RB_WPTR7 0x621f8 +#define ISP_RB_BASE_LO8 0x621fc +#define ISP_RB_BASE_HI8 0x62200 +#define ISP_RB_SIZE8 0x62204 +#define ISP_RB_RPTR8 0x62208 +#define ISP_RB_WPTR8 0x6220c +#define ISP_RB_BASE_LO9 0x62210 +#define ISP_RB_BASE_HI9 0x62214 +#define ISP_RB_SIZE9 0x62218 +#define ISP_RB_RPTR9 0x6221c +#define ISP_RB_WPTR9 0x62220 +#define ISP_RB_BASE_LO10 0x62224 +#define ISP_RB_BASE_HI10 0x62228 +#define ISP_RB_SIZE10 0x6222c +#define ISP_RB_RPTR10 0x62230 +#define ISP_RB_WPTR10 0x62234 +#define ISP_RB_BASE_LO11 0x62238 +#define ISP_RB_BASE_HI11 0x6223c +#define ISP_RB_SIZE11 0x62240 +#define ISP_RB_RPTR11 0x62244 +#define ISP_RB_WPTR11 0x62248 +#define ISP_RB_BASE_LO12 0x6224c +#define ISP_RB_BASE_HI12 0x62250 +#define ISP_RB_SIZE12 0x62254 +#define ISP_RB_RPTR12 0x62258 +#define ISP_RB_WPTR12 0x6225c + +#define ISP_POWER_STATUS 0x60000 + +/* ISP_SOFT_RESET */ +#define ISP_SOFT_RESET__CCPU_SOFT_RESET_MASK 0x00000001UL + +/* ISP_CCPU_CNTL */ +#define ISP_CCPU_CNTL__CCPU_HOST_SOFT_RST_MASK 0x00040000UL + +/* ISP_STATUS */ +#define ISP_STATUS__CCPU_REPORT_MASK 0x000000feUL + +/* ISP_SYS_INT0_STATUS */ +#define ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT9_INT_MASK 0x00010000UL +#define ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT10_INT_MASK 0x00040000UL +#define ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT11_INT_MASK 0x00100000UL +#define ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT12_INT_MASK 0x00400000UL + +/* ISP_SYS_INT0_EN */ +#define ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT9_EN_MASK 0x00010000UL +#define ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT10_EN_MASK 0x00040000UL +#define ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT11_EN_MASK 0x00100000UL +#define ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT12_EN_MASK 0x00400000UL + +/* ISP_SYS_INT0_ACK */ +#define ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT9_ACK_MASK 0x00010000UL +#define ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT10_ACK_MASK 0x00040000UL +#define ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT11_ACK_MASK 0x00100000UL +#define ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT12_ACK_MASK 0x00400000UL + +/* Helper functions for reading isp registers */ +static inline u32 isp4hw_rreg(void __iomem *base, u32 reg) +{ + return readl(base + reg); +} + +/* Helper functions for writing isp registers */ +static inline void isp4hw_wreg(void __iomem *base, u32 reg, u32 val) +{ + return writel(val, base + reg); +} + +#endif /* _ISP4_HW_REG_H_ */ diff --git a/drivers/media/platform/amd/isp4/isp4_interface.c b/drivers/media/platform/amd/isp4/isp4_interface.c new file mode 100644 index 000000000000..8d73f66bb42c --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_interface.c @@ -0,0 +1,832 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#include <linux/iopoll.h> + +#include "isp4_debug.h" +#include "isp4_fw_cmd_resp.h" +#include "isp4_hw_reg.h" +#include "isp4_interface.h" + +#define ISP4IF_FW_RESP_RB_IRQ_EN_MASK \ + (ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT9_EN_MASK\ + | ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT12_EN_MASK) + +#define ISP4IF_FW_CMD_TIMEOUT (HZ / 2) + +struct isp4if_rb_config { + const char *name; + u32 index; + u32 reg_rptr; + u32 reg_wptr; + u32 reg_base_lo; + u32 reg_base_hi; + u32 reg_size; + u32 val_size; + u64 base_mc_addr; + void *base_sys_addr; +}; + +/* FW cmd ring buffer configuration */ +static struct isp4if_rb_config isp4if_cmd_rb_config[ISP4IF_STREAM_ID_MAX] = { + { + .name = "CMD_RB_GBL0", + .index = 3, + .reg_rptr = ISP_RB_RPTR4, + .reg_wptr = ISP_RB_WPTR4, + .reg_base_lo = ISP_RB_BASE_LO4, + .reg_base_hi = ISP_RB_BASE_HI4, + .reg_size = ISP_RB_SIZE4, + }, + { + .name = "CMD_RB_STR1", + .index = 0, + .reg_rptr = ISP_RB_RPTR1, + .reg_wptr = ISP_RB_WPTR1, + .reg_base_lo = ISP_RB_BASE_LO1, + .reg_base_hi = ISP_RB_BASE_HI1, + .reg_size = ISP_RB_SIZE1, + }, + { + .name = "CMD_RB_STR2", + .index = 1, + .reg_rptr = ISP_RB_RPTR2, + .reg_wptr = ISP_RB_WPTR2, + .reg_base_lo = ISP_RB_BASE_LO2, + .reg_base_hi = ISP_RB_BASE_HI2, + .reg_size = ISP_RB_SIZE2, + }, + { + .name = "CMD_RB_STR3", + .index = 2, + .reg_rptr = ISP_RB_RPTR3, + .reg_wptr = ISP_RB_WPTR3, + .reg_base_lo = ISP_RB_BASE_LO3, + .reg_base_hi = ISP_RB_BASE_HI3, + .reg_size = ISP_RB_SIZE3, + }, +}; + +/* FW resp ring buffer configuration */ +static struct isp4if_rb_config isp4if_resp_rb_config[ISP4IF_STREAM_ID_MAX] = { + { + .name = "RES_RB_GBL0", + .index = 3, + .reg_rptr = ISP_RB_RPTR12, + .reg_wptr = ISP_RB_WPTR12, + .reg_base_lo = ISP_RB_BASE_LO12, + .reg_base_hi = ISP_RB_BASE_HI12, + .reg_size = ISP_RB_SIZE12, + }, + { + .name = "RES_RB_STR1", + .index = 0, + .reg_rptr = ISP_RB_RPTR9, + .reg_wptr = ISP_RB_WPTR9, + .reg_base_lo = ISP_RB_BASE_LO9, + .reg_base_hi = ISP_RB_BASE_HI9, + .reg_size = ISP_RB_SIZE9, + }, + { + .name = "RES_RB_STR2", + .index = 1, + .reg_rptr = ISP_RB_RPTR10, + .reg_wptr = ISP_RB_WPTR10, + .reg_base_lo = ISP_RB_BASE_LO10, + .reg_base_hi = ISP_RB_BASE_HI10, + .reg_size = ISP_RB_SIZE10, + }, + { + .name = "RES_RB_STR3", + .index = 2, + .reg_rptr = ISP_RB_RPTR11, + .reg_wptr = ISP_RB_WPTR11, + .reg_base_lo = ISP_RB_BASE_LO11, + .reg_base_hi = ISP_RB_BASE_HI11, + .reg_size = ISP_RB_SIZE11, + }, +}; + +/* FW log ring buffer configuration */ +static struct isp4if_rb_config isp4if_log_rb_config = { + .name = "LOG_RB", + .index = 0, + .reg_rptr = ISP_LOG_RB_RPTR0, + .reg_wptr = ISP_LOG_RB_WPTR0, + .reg_base_lo = ISP_LOG_RB_BASE_LO0, + .reg_base_hi = ISP_LOG_RB_BASE_HI0, + .reg_size = ISP_LOG_RB_SIZE0, +}; + +static struct isp4if_gpu_mem_info * +isp4if_gpu_mem_alloc(struct isp4_interface *ispif, u32 mem_size) +{ + struct isp4if_gpu_mem_info *mem_info; + struct device *dev = ispif->dev; + int ret; + + mem_info = kmalloc_obj(*mem_info, GFP_KERNEL); + if (!mem_info) + return NULL; + + mem_info->mem_size = mem_size; + ret = isp_kernel_buffer_alloc(dev, mem_info->mem_size, + &mem_info->mem_handle, + &mem_info->gpu_mc_addr, + &mem_info->sys_addr); + if (ret) { + kfree(mem_info); + return NULL; + } + + return mem_info; +} + +static void isp4if_gpu_mem_free(struct isp4_interface *ispif, + struct isp4if_gpu_mem_info **mem_info_ptr) +{ + struct isp4if_gpu_mem_info *mem_info = *mem_info_ptr; + struct device *dev = ispif->dev; + + if (!mem_info) { + dev_err(dev, "invalid mem_info\n"); + return; + } + + *mem_info_ptr = NULL; + isp_kernel_buffer_free(&mem_info->mem_handle, &mem_info->gpu_mc_addr, + &mem_info->sys_addr); + kfree(mem_info); +} + +static void isp4if_dealloc_fw_gpumem(struct isp4_interface *ispif) +{ + isp4if_gpu_mem_free(ispif, &ispif->fw_mem_pool); + isp4if_gpu_mem_free(ispif, &ispif->fw_cmd_resp_buf); + isp4if_gpu_mem_free(ispif, &ispif->fw_log_buf); + + for (unsigned int i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++) + isp4if_gpu_mem_free(ispif, &ispif->meta_info_buf[i]); +} + +static int isp4if_alloc_fw_gpumem(struct isp4_interface *ispif) +{ + struct device *dev = ispif->dev; + + ispif->fw_mem_pool = isp4if_gpu_mem_alloc(ispif, + ISP4FW_MEMORY_POOL_SIZE); + if (!ispif->fw_mem_pool) + goto error_no_memory; + + ispif->fw_cmd_resp_buf = + isp4if_gpu_mem_alloc(ispif, ISP4IF_RB_PMBMAP_MEM_SIZE); + if (!ispif->fw_cmd_resp_buf) + goto error_no_memory; + + ispif->fw_log_buf = + isp4if_gpu_mem_alloc(ispif, ISP4IF_FW_LOG_RINGBUF_SIZE); + if (!ispif->fw_log_buf) + goto error_no_memory; + + for (unsigned int i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++) { + ispif->meta_info_buf[i] = + isp4if_gpu_mem_alloc(ispif, ISP4IF_META_INFO_BUF_SIZE); + if (!ispif->meta_info_buf[i]) + goto error_no_memory; + } + + return 0; + +error_no_memory: + dev_err(dev, "failed to allocate gpu memory\n"); + return -ENOMEM; +} + +static u32 isp4if_compute_check_sum(const void *buf, size_t buf_size) +{ + const u8 *surplus_ptr; + const u32 *buffer; + u32 checksum = 0; + size_t i; + + buffer = (const u32 *)buf; + for (i = 0; i < buf_size / sizeof(u32); i++) + checksum += buffer[i]; + + surplus_ptr = (const u8 *)&buffer[i]; + /* add surplus data crc checksum */ + for (i = 0; i < buf_size % sizeof(u32); i++) + checksum += surplus_ptr[i]; + + return checksum; +} + +void isp4if_clear_cmdq(struct isp4_interface *ispif) +{ + struct isp4if_cmd_element *buf_node, *tmp_node; + LIST_HEAD(free_list); + + scoped_guard(spinlock, &ispif->cmdq_lock) + list_splice_init(&ispif->cmdq, &free_list); + + list_for_each_entry_safe(buf_node, tmp_node, &free_list, list) + kfree(buf_node); +} + +static bool isp4if_is_cmdq_rb_full(struct isp4_interface *ispif, + enum isp4if_stream_id stream) +{ + struct isp4if_rb_config *rb_config = &isp4if_cmd_rb_config[stream]; + u32 rreg = rb_config->reg_rptr, wreg = rb_config->reg_wptr; + u32 len = rb_config->val_size; + u32 rd_ptr, wr_ptr; + u32 bytes_free; + + rd_ptr = isp4hw_rreg(ispif->mmio, rreg); + wr_ptr = isp4hw_rreg(ispif->mmio, wreg); + + /* + * Read and write pointers are equal, indicating the ring buffer + * is empty + */ + if (wr_ptr == rd_ptr) + return false; + + if (wr_ptr > rd_ptr) + bytes_free = len - (wr_ptr - rd_ptr); + else + bytes_free = rd_ptr - wr_ptr; + + /* + * Ignore one byte from the bytes free to prevent rd_ptr from equaling + * wr_ptr when the ring buffer is full, because rd_ptr == wr_ptr is + * supposed to indicate that the ring buffer is empty. + */ + return bytes_free <= sizeof(struct isp4fw_cmd); +} + +struct isp4if_cmd_element *isp4if_rm_cmd_from_cmdq(struct isp4_interface *ispif, + u32 seq_num, u32 cmd_id) +{ + struct isp4if_cmd_element *ele; + + guard(spinlock)(&ispif->cmdq_lock); + + list_for_each_entry(ele, &ispif->cmdq, list) { + if (ele->seq_num == seq_num && ele->cmd_id == cmd_id) { + list_del(&ele->list); + return ele; + } + } + + return NULL; +} + +/* Must check that isp4if_is_cmdq_rb_full() == false before calling */ +static int isp4if_insert_isp_fw_cmd(struct isp4_interface *ispif, + enum isp4if_stream_id stream, + const struct isp4fw_cmd *cmd) +{ + struct isp4if_rb_config *rb_config = &isp4if_cmd_rb_config[stream]; + u32 rreg = rb_config->reg_rptr, wreg = rb_config->reg_wptr; + void *mem_sys = rb_config->base_sys_addr; + const u32 cmd_sz = sizeof(*cmd); + struct device *dev = ispif->dev; + u32 len = rb_config->val_size; + const void *src = cmd; + u32 rd_ptr, wr_ptr; + u32 bytes_to_end; + + rd_ptr = isp4hw_rreg(ispif->mmio, rreg); + wr_ptr = isp4hw_rreg(ispif->mmio, wreg); + if (rd_ptr >= len || wr_ptr >= len) { + dev_err(dev, + "rb invalid: stream=%u(%s), rd=%u, wr=%u, len=%u, cmd_sz=%u\n", + stream, isp4dbg_get_if_stream_str(stream), rd_ptr, + wr_ptr, len, cmd_sz); + return -EINVAL; + } + + bytes_to_end = len - wr_ptr; + if (bytes_to_end >= cmd_sz) { + /* FW cmd is just a straight copy to the write pointer */ + memcpy(mem_sys + wr_ptr, src, cmd_sz); + isp4hw_wreg(ispif->mmio, wreg, (wr_ptr + cmd_sz) % len); + } else { + /* + * FW cmd is split because the ring buffer needs to wrap + * around + */ + memcpy(mem_sys + wr_ptr, src, bytes_to_end); + memcpy(mem_sys, src + bytes_to_end, cmd_sz - bytes_to_end); + isp4hw_wreg(ispif->mmio, wreg, cmd_sz - bytes_to_end); + } + + return 0; +} + +static inline enum isp4if_stream_id isp4if_get_fw_stream(u32 cmd_id) +{ + return ISP4IF_STREAM_ID_1; +} + +static int isp4if_send_fw_cmd(struct isp4_interface *ispif, u32 cmd_id, + const void *package, + u32 package_size, bool sync) +{ + enum isp4if_stream_id stream = isp4if_get_fw_stream(cmd_id); + struct isp4if_cmd_element *ele = NULL; + struct device *dev = ispif->dev; + struct isp4fw_cmd cmd; + u32 seq_num; + int ret; + + if (package_size > sizeof(cmd.cmd_param)) { + dev_err(dev, "fail pkgsize(%u) > %zu cmd:0x%x, stream %d\n", + package_size, sizeof(cmd.cmd_param), cmd_id, stream); + return -EINVAL; + } + + /* + * The struct will be shared with ISP FW, use memset() to guarantee + * padding bits are zeroed, since this is not guaranteed on all + * compilers. + */ + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = cmd_id; + switch (stream) { + case ISP4IF_STREAM_ID_GLOBAL: + cmd.cmd_stream_id = ISP4FW_STREAM_ID_INVALID; + break; + case ISP4IF_STREAM_ID_1: + cmd.cmd_stream_id = ISP4FW_STREAM_ID_1; + break; + default: + dev_err(dev, "fail bad stream id %d\n", stream); + return -EINVAL; + } + + /* Allocate the sync command object early and outside of the lock */ + if (sync) { + ele = kmalloc_obj(*ele, GFP_KERNEL); + if (!ele) + return -ENOMEM; + + /* Get two references: one for the resp thread, one for us */ + atomic_set(&ele->refcnt, 2); + init_completion(&ele->cmd_done); + } + + if (package && package_size) + memcpy(cmd.cmd_param, package, package_size); + + scoped_guard(mutex, &ispif->isp4if_mutex) { + ret = read_poll_timeout(isp4if_is_cmdq_rb_full, ret, !ret, + ISP4IF_RB_FULL_SLEEP_US, + ISP4IF_RB_FULL_TIMEOUT_US, false, ispif, + stream); + if (ret) { + struct isp4if_rb_config *rb_config = + &isp4if_resp_rb_config[stream]; + u32 rd_ptr = isp4hw_rreg(ispif->mmio, + rb_config->reg_rptr); + u32 wr_ptr = isp4hw_rreg(ispif->mmio, + rb_config->reg_wptr); + + dev_err(dev, + "failed to get free cmdq slot, stream %s(%d),rd %u, wr %u\n", + isp4dbg_get_if_stream_str(stream), stream, + rd_ptr, wr_ptr); + ret = -ETIMEDOUT; + goto free_ele; + } + + seq_num = ispif->host2fw_seq_num++; + cmd.cmd_seq_num = seq_num; + cmd.cmd_check_sum = isp4if_compute_check_sum(&cmd, sizeof(cmd) + - sizeof(u32)); + + /* + * only append the fw cmd to queue when its response needs to + * be waited for, currently there are only two such commands, + * disable channel and stop stream which are only sent after + * close camera + */ + if (ele) { + ele->seq_num = seq_num; + ele->cmd_id = cmd_id; + scoped_guard(spinlock, &ispif->cmdq_lock) + list_add_tail(&ele->list, &ispif->cmdq); + } + + ret = isp4if_insert_isp_fw_cmd(ispif, stream, &cmd); + if (ret) { + dev_err(dev, + "fail for insert_isp_fw_cmd cmd_id %s(0x%08x)\n", + isp4dbg_get_cmd_str(cmd_id), cmd_id); + goto err_dequeue_ele; + } + } + + if (ele) { + ret = wait_for_completion_timeout(&ele->cmd_done, + ISP4IF_FW_CMD_TIMEOUT); + if (!ret) { + ret = -ETIMEDOUT; + goto err_dequeue_ele; + } + + ret = 0; + goto put_ele_ref; + } + + return 0; + +err_dequeue_ele: + /* + * Try to remove the command from the queue. If that fails, then it + * means the response thread is currently using the object, and we need + * to use the refcount to avoid a use-after-free by either side. + */ + if (ele && isp4if_rm_cmd_from_cmdq(ispif, seq_num, cmd_id)) + goto free_ele; + +put_ele_ref: + /* Don't free the command if we didn't put the last reference */ + if (ele && atomic_dec_return(&ele->refcnt)) + ele = NULL; + +free_ele: + kfree(ele); + return ret; +} + +static int isp4if_send_buffer(struct isp4_interface *ispif, + struct isp4if_img_buf_info *buf_info) +{ + struct isp4fw_cmd_send_buffer cmd; + + /* + * The struct will be shared with ISP FW, use memset() to guarantee + * padding bits are zeroed, since this is not guaranteed on all + * compilers. + */ + memset(&cmd, 0, sizeof(cmd)); + cmd.buffer_type = ISP4FW_BUFFER_TYPE_PREVIEW; + cmd.buffer.vmid_space.bit.space = ISP4FW_ADDR_SPACE_TYPE_GPU_VA; + isp4if_split_addr64(buf_info->planes[0].mc_addr, + &cmd.buffer.buf_base_a_lo, + &cmd.buffer.buf_base_a_hi); + cmd.buffer.buf_size_a = buf_info->planes[0].len; + + isp4if_split_addr64(buf_info->planes[1].mc_addr, + &cmd.buffer.buf_base_b_lo, + &cmd.buffer.buf_base_b_hi); + cmd.buffer.buf_size_b = buf_info->planes[1].len; + + isp4if_split_addr64(buf_info->planes[2].mc_addr, + &cmd.buffer.buf_base_c_lo, + &cmd.buffer.buf_base_c_hi); + cmd.buffer.buf_size_c = buf_info->planes[2].len; + + return isp4if_send_fw_cmd(ispif, ISP4FW_CMD_ID_SEND_BUFFER, &cmd, + sizeof(cmd), false); +} + +static void isp4if_init_rb_config(struct isp4_interface *ispif, + struct isp4if_rb_config *rb_config) +{ + isp4hw_wreg(ispif->mmio, rb_config->reg_rptr, 0x0); + isp4hw_wreg(ispif->mmio, rb_config->reg_wptr, 0x0); + isp4hw_wreg(ispif->mmio, rb_config->reg_base_lo, + rb_config->base_mc_addr); + isp4hw_wreg(ispif->mmio, rb_config->reg_base_hi, + rb_config->base_mc_addr >> 32); + isp4hw_wreg(ispif->mmio, rb_config->reg_size, rb_config->val_size); +} + +static int isp4if_fw_init(struct isp4_interface *ispif) +{ + u32 aligned_rb_chunk_size = ISP4IF_RB_PMBMAP_MEM_CHUNK & 0xffffffc0; + struct isp4if_rb_config *rb_config; + u32 offset; + unsigned int i; + + /* initialize CMD_RB streams */ + for (i = 0; i < ISP4IF_STREAM_ID_MAX; i++) { + rb_config = (isp4if_cmd_rb_config + i); + offset = aligned_rb_chunk_size * rb_config->index; + + rb_config->val_size = ISP4IF_FW_CMD_BUF_SIZE; + rb_config->base_sys_addr = + ispif->fw_cmd_resp_buf->sys_addr + offset; + rb_config->base_mc_addr = + ispif->fw_cmd_resp_buf->gpu_mc_addr + offset; + + isp4if_init_rb_config(ispif, rb_config); + } + + /* initialize RESP_RB streams */ + for (i = 0; i < ISP4IF_STREAM_ID_MAX; i++) { + rb_config = (isp4if_resp_rb_config + i); + offset = aligned_rb_chunk_size * + (rb_config->index + ISP4IF_RESP_CHAN_TO_RB_OFFSET - 1); + + rb_config->val_size = ISP4IF_FW_CMD_BUF_SIZE; + rb_config->base_sys_addr = + ispif->fw_cmd_resp_buf->sys_addr + offset; + rb_config->base_mc_addr = + ispif->fw_cmd_resp_buf->gpu_mc_addr + offset; + + isp4if_init_rb_config(ispif, rb_config); + } + + /* initialize LOG_RB stream */ + rb_config = &isp4if_log_rb_config; + rb_config->val_size = ISP4IF_FW_LOG_RINGBUF_SIZE; + rb_config->base_mc_addr = ispif->fw_log_buf->gpu_mc_addr; + rb_config->base_sys_addr = ispif->fw_log_buf->sys_addr; + + isp4if_init_rb_config(ispif, rb_config); + + return 0; +} + +static int isp4if_wait_fw_ready(struct isp4_interface *ispif, + u32 isp_status_addr) +{ + struct device *dev = ispif->dev; + u32 timeout_ms = 100; + u32 interval_ms = 1; + u32 reg_val; + + /* wait for FW initialize done! */ + if (!read_poll_timeout(isp4hw_rreg, reg_val, reg_val + & ISP_STATUS__CCPU_REPORT_MASK, + interval_ms * 1000, timeout_ms * 1000, false, + ispif->mmio, isp_status_addr)) + return 0; + + dev_err(dev, "ISP CCPU FW boot failed\n"); + + return -ETIME; +} + +static void isp4if_enable_ccpu(struct isp4_interface *ispif) +{ + u32 reg_val; + + reg_val = isp4hw_rreg(ispif->mmio, ISP_SOFT_RESET); + reg_val &= (~ISP_SOFT_RESET__CCPU_SOFT_RESET_MASK); + isp4hw_wreg(ispif->mmio, ISP_SOFT_RESET, reg_val); + + usleep_range(100, 150); + + reg_val = isp4hw_rreg(ispif->mmio, ISP_CCPU_CNTL); + reg_val &= (~ISP_CCPU_CNTL__CCPU_HOST_SOFT_RST_MASK); + isp4hw_wreg(ispif->mmio, ISP_CCPU_CNTL, reg_val); +} + +static void isp4if_disable_ccpu(struct isp4_interface *ispif) +{ + u32 reg_val; + + reg_val = isp4hw_rreg(ispif->mmio, ISP_CCPU_CNTL); + reg_val |= ISP_CCPU_CNTL__CCPU_HOST_SOFT_RST_MASK; + isp4hw_wreg(ispif->mmio, ISP_CCPU_CNTL, reg_val); + + usleep_range(100, 150); + + reg_val = isp4hw_rreg(ispif->mmio, ISP_SOFT_RESET); + reg_val |= ISP_SOFT_RESET__CCPU_SOFT_RESET_MASK; + isp4hw_wreg(ispif->mmio, ISP_SOFT_RESET, reg_val); +} + +static int isp4if_fw_boot(struct isp4_interface *ispif) +{ + struct device *dev = ispif->dev; + + if (ispif->status != ISP4IF_STATUS_PWR_ON) { + dev_err(dev, "invalid isp power status %d\n", ispif->status); + return -EINVAL; + } + + isp4if_disable_ccpu(ispif); + + isp4if_fw_init(ispif); + + /* clear ccpu status */ + isp4hw_wreg(ispif->mmio, ISP_STATUS, 0x0); + + isp4if_enable_ccpu(ispif); + + if (isp4if_wait_fw_ready(ispif, ISP_STATUS)) { + isp4if_disable_ccpu(ispif); + return -EINVAL; + } + + /* enable interrupts */ + isp4hw_wreg(ispif->mmio, ISP_SYS_INT0_EN, + ISP4IF_FW_RESP_RB_IRQ_EN_MASK); + + ispif->status = ISP4IF_STATUS_FW_RUNNING; + + dev_dbg(dev, "ISP CCPU FW boot success\n"); + + return 0; +} + +int isp4if_f2h_resp(struct isp4_interface *ispif, enum isp4if_stream_id stream, + struct isp4fw_resp *resp) +{ + struct isp4if_rb_config *rb_config = &isp4if_resp_rb_config[stream]; + u32 rreg = rb_config->reg_rptr, wreg = rb_config->reg_wptr; + void *mem_sys = rb_config->base_sys_addr; + const u32 resp_sz = sizeof(*resp); + struct device *dev = ispif->dev; + u32 len = rb_config->val_size; + u32 rd_ptr, wr_ptr; + u32 bytes_to_end; + void *dst = resp; + u32 checksum; + + rd_ptr = isp4hw_rreg(ispif->mmio, rreg); + wr_ptr = isp4hw_rreg(ispif->mmio, wreg); + if (rd_ptr >= len || wr_ptr >= len) + goto err_rb_invalid; + + /* + * Read and write pointers are equal, indicating the ring buffer is + * empty + */ + if (rd_ptr == wr_ptr) + return -ENODATA; + + bytes_to_end = len - rd_ptr; + if (bytes_to_end >= resp_sz) { + /* FW response is just a straight copy from the read pointer */ + if (wr_ptr > rd_ptr && wr_ptr - rd_ptr < resp_sz) + goto err_rb_invalid; + + memcpy(dst, mem_sys + rd_ptr, resp_sz); + isp4hw_wreg(ispif->mmio, rreg, (rd_ptr + resp_sz) % len); + } else { + /* + * FW response is split because the ring buffer wrapped + * around + */ + if (wr_ptr > rd_ptr || wr_ptr < resp_sz - bytes_to_end) + goto err_rb_invalid; + + memcpy(dst, mem_sys + rd_ptr, bytes_to_end); + memcpy(dst + bytes_to_end, mem_sys, resp_sz - bytes_to_end); + isp4hw_wreg(ispif->mmio, rreg, resp_sz - bytes_to_end); + } + + checksum = isp4if_compute_check_sum(resp, resp_sz - sizeof(u32)); + if (checksum != resp->resp_check_sum) { + dev_err(dev, "resp checksum 0x%x,should 0x%x,rptr %u,wptr %u\n", + checksum, resp->resp_check_sum, rd_ptr, wr_ptr); + dev_err(dev, "%s(%u), seqNo %u, resp_id %s(0x%x)\n", + isp4dbg_get_if_stream_str(stream), stream, + resp->resp_seq_num, isp4dbg_get_resp_str(resp->resp_id), + resp->resp_id); + return -EINVAL; + } + + return 0; + +err_rb_invalid: + dev_err(dev, + "rb invalid: stream=%u(%s), rd=%u, wr=%u, len=%u, resp_sz=%u\n", + stream, isp4dbg_get_if_stream_str(stream), rd_ptr, wr_ptr, len, + resp_sz); + return -EINVAL; +} + +int isp4if_send_command(struct isp4_interface *ispif, u32 cmd_id, + const void *package, u32 package_size) +{ + return isp4if_send_fw_cmd(ispif, cmd_id, package, package_size, false); +} + +int isp4if_send_command_sync(struct isp4_interface *ispif, u32 cmd_id, + const void *package, u32 package_size) +{ + return isp4if_send_fw_cmd(ispif, cmd_id, package, package_size, true); +} + +void isp4if_clear_bufq(struct isp4_interface *ispif) +{ + struct isp4if_img_buf_node *buf_node, *tmp_node; + LIST_HEAD(free_list); + + scoped_guard(spinlock, &ispif->bufq_lock) + list_splice_init(&ispif->bufq, &free_list); + + list_for_each_entry_safe(buf_node, tmp_node, &free_list, node) + kfree(buf_node); +} + +void isp4if_dealloc_buffer_node(struct isp4if_img_buf_node *buf_node) +{ + kfree(buf_node); +} + +struct isp4if_img_buf_node * +isp4if_alloc_buffer_node(struct isp4if_img_buf_info *buf_info) +{ + struct isp4if_img_buf_node *node; + + node = kmalloc_obj(*node, GFP_KERNEL); + if (node) + node->buf_info = *buf_info; + + return node; +} + +struct isp4if_img_buf_node *isp4if_dequeue_buffer(struct isp4_interface *ispif) +{ + struct isp4if_img_buf_node *buf_node; + + guard(spinlock)(&ispif->bufq_lock); + + buf_node = list_first_entry_or_null(&ispif->bufq, typeof(*buf_node), + node); + if (buf_node) + list_del(&buf_node->node); + + return buf_node; +} + +int isp4if_queue_buffer(struct isp4_interface *ispif, + struct isp4if_img_buf_node *buf_node) +{ + int ret; + + ret = isp4if_send_buffer(ispif, &buf_node->buf_info); + if (ret) + return ret; + + scoped_guard(spinlock, &ispif->bufq_lock) + list_add_tail(&buf_node->node, &ispif->bufq); + + return 0; +} + +int isp4if_stop(struct isp4_interface *ispif) +{ + isp4if_disable_ccpu(ispif); + + isp4if_dealloc_fw_gpumem(ispif); + + return 0; +} + +int isp4if_start(struct isp4_interface *ispif) +{ + int ret; + + ret = isp4if_alloc_fw_gpumem(ispif); + if (ret) + return ret; + + ret = isp4if_fw_boot(ispif); + if (ret) + goto failed_fw_boot; + + return 0; + +failed_fw_boot: + isp4if_dealloc_fw_gpumem(ispif); + return ret; +} + +int isp4if_deinit(struct isp4_interface *ispif) +{ + isp4if_clear_cmdq(ispif); + + isp4if_clear_bufq(ispif); + + mutex_destroy(&ispif->isp4if_mutex); + + return 0; +} + +int isp4if_init(struct isp4_interface *ispif, struct device *dev, + void __iomem *isp_mmio) +{ + ispif->dev = dev; + ispif->mmio = isp_mmio; + + spin_lock_init(&ispif->cmdq_lock); /* used for cmdq access */ + spin_lock_init(&ispif->bufq_lock); /* used for bufq access */ + mutex_init(&ispif->isp4if_mutex); /* used for commands sent to ispfw */ + + INIT_LIST_HEAD(&ispif->cmdq); + INIT_LIST_HEAD(&ispif->bufq); + + return 0; +} diff --git a/drivers/media/platform/amd/isp4/isp4_interface.h b/drivers/media/platform/amd/isp4/isp4_interface.h new file mode 100644 index 000000000000..ce3ac9b9e5cd --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_interface.h @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _ISP4_INTERFACE_H_ +#define _ISP4_INTERFACE_H_ + +#include <drm/amd/isp.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> + +struct isp4fw_resp; + +#define ISP4IF_RB_MAX 25 +#define ISP4IF_RESP_CHAN_TO_RB_OFFSET 9 +#define ISP4IF_RB_PMBMAP_MEM_SIZE (SZ_16M - 1) +#define ISP4IF_RB_PMBMAP_MEM_CHUNK \ + (ISP4IF_RB_PMBMAP_MEM_SIZE / (ISP4IF_RB_MAX - 1)) +#define ISP4IF_HOST2FW_COMMAND_SIZE sizeof(struct isp4fw_cmd) +#define ISP4IF_MAX_NUM_HOST2FW_COMMAND 40 +#define ISP4IF_FW_CMD_BUF_SIZE \ + (ISP4IF_MAX_NUM_HOST2FW_COMMAND * ISP4IF_HOST2FW_COMMAND_SIZE) +#define ISP4IF_RB_FULL_SLEEP_US (33 * USEC_PER_MSEC) +#define ISP4IF_RB_FULL_TIMEOUT_US (10 * ISP4IF_RB_FULL_SLEEP_US) + +#define ISP4IF_META_INFO_BUF_SIZE ALIGN(sizeof(struct isp4fw_meta_info), 0x8000) +#define ISP4IF_MAX_STREAM_BUF_COUNT 8 + +#define ISP4IF_FW_LOG_RINGBUF_SIZE SZ_2M + +enum isp4if_stream_id { + ISP4IF_STREAM_ID_GLOBAL = 0, + ISP4IF_STREAM_ID_1 = 1, + ISP4IF_STREAM_ID_MAX = 4 +}; + +enum isp4if_status { + ISP4IF_STATUS_PWR_OFF, + ISP4IF_STATUS_PWR_ON, + ISP4IF_STATUS_FW_RUNNING, + ISP4IF_FSM_STATUS_MAX +}; + +struct isp4if_gpu_mem_info { + u64 mem_size; + u64 gpu_mc_addr; + void *sys_addr; + void *mem_handle; +}; + +struct isp4if_img_buf_info { + struct { + void *sys_addr; + u64 mc_addr; + u32 len; + } planes[3]; +}; + +struct isp4if_img_buf_node { + struct list_head node; + struct isp4if_img_buf_info buf_info; +}; + +struct isp4if_cmd_element { + struct list_head list; + u32 seq_num; + u32 cmd_id; + struct completion cmd_done; + atomic_t refcnt; +}; + +struct isp4_interface { + struct device *dev; + void __iomem *mmio; + + spinlock_t cmdq_lock; /* used for cmdq access */ + spinlock_t bufq_lock; /* used for bufq access */ + struct mutex isp4if_mutex; /* used to send fw cmd and read fw log */ + + struct list_head cmdq; /* commands sent to fw */ + struct list_head bufq; /* buffers sent to fw */ + + enum isp4if_status status; + u32 host2fw_seq_num; + + /* ISP fw buffers */ + struct isp4if_gpu_mem_info *fw_log_buf; + struct isp4if_gpu_mem_info *fw_cmd_resp_buf; + struct isp4if_gpu_mem_info *fw_mem_pool; + struct isp4if_gpu_mem_info *meta_info_buf[ISP4IF_MAX_STREAM_BUF_COUNT]; +}; + +static inline void isp4if_split_addr64(u64 addr, u32 *lo, u32 *hi) +{ + if (lo) + *lo = addr & 0xffffffff; + + if (hi) + *hi = addr >> 32; +} + +static inline u64 isp4if_join_addr64(u32 lo, u32 hi) +{ + return (((u64)hi) << 32) | (u64)lo; +} + +int isp4if_f2h_resp(struct isp4_interface *ispif, enum isp4if_stream_id stream, + struct isp4fw_resp *resp); + +int isp4if_send_command(struct isp4_interface *ispif, u32 cmd_id, + const void *package, u32 package_size); + +int isp4if_send_command_sync(struct isp4_interface *ispif, u32 cmd_id, + const void *package, u32 package_size); + +struct isp4if_cmd_element *isp4if_rm_cmd_from_cmdq(struct isp4_interface *ispif, + u32 seq_num, u32 cmd_id); + +void isp4if_clear_cmdq(struct isp4_interface *ispif); + +void isp4if_clear_bufq(struct isp4_interface *ispif); + +void isp4if_dealloc_buffer_node(struct isp4if_img_buf_node *buf_node); + +struct isp4if_img_buf_node * +isp4if_alloc_buffer_node(struct isp4if_img_buf_info *buf_info); + +struct isp4if_img_buf_node *isp4if_dequeue_buffer(struct isp4_interface *ispif); + +int isp4if_queue_buffer(struct isp4_interface *ispif, + struct isp4if_img_buf_node *buf_node); + +int isp4if_stop(struct isp4_interface *ispif); + +int isp4if_start(struct isp4_interface *ispif); + +int isp4if_deinit(struct isp4_interface *ispif); + +int isp4if_init(struct isp4_interface *ispif, struct device *dev, + void __iomem *isp_mmio); + +#endif /* _ISP4_INTERFACE_H_ */ diff --git a/drivers/media/platform/amd/isp4/isp4_subdev.c b/drivers/media/platform/amd/isp4/isp4_subdev.c new file mode 100644 index 000000000000..48deea79ce6c --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_subdev.c @@ -0,0 +1,1047 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#include <linux/pm_domain.h> +#include <linux/units.h> + +#include "isp4.h" +#include "isp4_debug.h" +#include "isp4_fw_cmd_resp.h" +#include "isp4_interface.h" + +#define ISP4SD_MIN_BUF_CNT_BEF_START_STREAM 4 + +#define ISP4SD_PERFORMANCE_STATE_LOW 0 +#define ISP4SD_PERFORMANCE_STATE_HIGH 1 + +/* align 32KB */ +#define ISP4SD_META_BUF_SIZE ALIGN(sizeof(struct isp4fw_meta_info), 0x8000) + +#define to_isp4_subdev(sd) container_of(sd, struct isp4_subdev, sdev) + +static const char *isp4sd_entity_name = "amd isp4"; + +static const char *isp4sd_thread_name[ISP4SD_MAX_FW_RESP_STREAM_NUM] = { + "amd_isp4_thread_global", + "amd_isp4_thread_stream1", +}; + +static void isp4sd_module_enable(struct isp4_subdev *isp_subdev, bool enable) +{ + if (isp_subdev->enable_gpio) { + gpiod_set_value(isp_subdev->enable_gpio, enable ? 1 : 0); + dev_dbg(isp_subdev->dev, "%s isp_subdev module\n", + enable ? "enable" : "disable"); + } +} + +static int isp4sd_setup_fw_mem_pool(struct isp4_subdev *isp_subdev) +{ + struct isp4_interface *ispif = &isp_subdev->ispif; + struct isp4fw_cmd_send_buffer buf_type; + struct device *dev = isp_subdev->dev; + int ret; + + if (!ispif->fw_mem_pool) { + dev_err(dev, "fail to alloc mem pool\n"); + return -ENOMEM; + } + + /* + * The struct will be shared with ISP FW, use memset() to guarantee + * padding bits are zeroed, since this is not guaranteed on all + * compilers. + */ + memset(&buf_type, 0, sizeof(buf_type)); + buf_type.buffer_type = ISP4FW_BUFFER_TYPE_MEM_POOL; + buf_type.buffer.vmid_space.bit.space = ISP4FW_ADDR_SPACE_TYPE_GPU_VA; + isp4if_split_addr64(ispif->fw_mem_pool->gpu_mc_addr, + &buf_type.buffer.buf_base_a_lo, + &buf_type.buffer.buf_base_a_hi); + buf_type.buffer.buf_size_a = ispif->fw_mem_pool->mem_size; + + ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_SEND_BUFFER, + &buf_type, sizeof(buf_type)); + if (ret) { + dev_err(dev, "send fw mem pool 0x%llx(%u) fail %d\n", + ispif->fw_mem_pool->gpu_mc_addr, + buf_type.buffer.buf_size_a, ret); + return ret; + } + + dev_dbg(dev, "send fw mem pool 0x%llx(%u) suc\n", + ispif->fw_mem_pool->gpu_mc_addr, buf_type.buffer.buf_size_a); + + return 0; +} + +static int isp4sd_set_stream_path(struct isp4_subdev *isp_subdev) +{ + struct isp4_interface *ispif = &isp_subdev->ispif; + struct isp4fw_cmd_set_stream_cfg cmd; + struct device *dev = isp_subdev->dev; + + /* + * The struct will be shared with ISP FW, use memset() to guarantee + * padding bits are zeroed, since this is not guaranteed on all + * compilers. + */ + memset(&cmd, 0, sizeof(cmd)); + cmd.stream_cfg.mipi_pipe_path_cfg.isp4fw_sensor_id = + ISP4FW_SENSOR_ID_ON_MIPI0; + cmd.stream_cfg.mipi_pipe_path_cfg.b_enable = true; + cmd.stream_cfg.isp_pipe_path_cfg.isp_pipe_id = + ISP4FW_MIPI0_ISP_PIPELINE_ID; + + cmd.stream_cfg.b_enable_tnr = true; + dev_dbg(dev, "isp4fw_sensor_id %d, pipeId 0x%x EnableTnr %u\n", + cmd.stream_cfg.mipi_pipe_path_cfg.isp4fw_sensor_id, + cmd.stream_cfg.isp_pipe_path_cfg.isp_pipe_id, + cmd.stream_cfg.b_enable_tnr); + + return isp4if_send_command(ispif, ISP4FW_CMD_ID_SET_STREAM_CONFIG, + &cmd, sizeof(cmd)); +} + +static int isp4sd_send_meta_buf(struct isp4_subdev *isp_subdev) +{ + struct isp4_interface *ispif = &isp_subdev->ispif; + struct isp4fw_cmd_send_buffer buf_type; + struct device *dev = isp_subdev->dev; + + /* + * The struct will be shared with ISP FW, use memset() to guarantee + * padding bits are zeroed, since this is not guaranteed on all + * compilers. + */ + memset(&buf_type, 0, sizeof(buf_type)); + for (unsigned int i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++) { + struct isp4if_gpu_mem_info *meta_info_buf = + isp_subdev->ispif.meta_info_buf[i]; + int ret; + + if (!meta_info_buf) { + dev_err(dev, "fail for no meta info buf(%u)\n", i); + return -ENOMEM; + } + + buf_type.buffer_type = ISP4FW_BUFFER_TYPE_META_INFO; + buf_type.buffer.vmid_space.bit.space = + ISP4FW_ADDR_SPACE_TYPE_GPU_VA; + isp4if_split_addr64(meta_info_buf->gpu_mc_addr, + &buf_type.buffer.buf_base_a_lo, + &buf_type.buffer.buf_base_a_hi); + buf_type.buffer.buf_size_a = meta_info_buf->mem_size; + ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_SEND_BUFFER, + &buf_type, sizeof(buf_type)); + if (ret) { + dev_err(dev, "send meta info(%u) fail\n", i); + return ret; + } + } + + dev_dbg(dev, "send meta info suc\n"); + return 0; +} + +static bool isp4sd_get_str_out_prop(struct isp4_subdev *isp_subdev, + struct isp4fw_image_prop *out_prop, + struct v4l2_subdev_state *state, u32 pad) +{ + struct device *dev = isp_subdev->dev; + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_state_get_format(state, pad, 0); + if (!format) { + dev_err(dev, "fail get subdev state format\n"); + return false; + } + + switch (format->code) { + case MEDIA_BUS_FMT_YUYV8_1_5X8: + out_prop->image_format = ISP4FW_IMAGE_FORMAT_NV12; + out_prop->width = format->width; + out_prop->height = format->height; + out_prop->luma_pitch = format->width; + out_prop->chroma_pitch = out_prop->width; + break; + case MEDIA_BUS_FMT_YUYV8_1X16: + out_prop->image_format = ISP4FW_IMAGE_FORMAT_YUV422INTERLEAVED; + out_prop->width = format->width; + out_prop->height = format->height; + out_prop->luma_pitch = format->width * 2; + out_prop->chroma_pitch = 0; + break; + default: + dev_err(dev, "fail for bad image format:0x%x\n", + format->code); + return false; + } + + if (!out_prop->width || !out_prop->height) + return false; + + return true; +} + +static int isp4sd_kickoff_stream(struct isp4_subdev *isp_subdev, u32 w, u32 h) +{ + struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info; + struct isp4_interface *ispif = &isp_subdev->ispif; + struct device *dev = isp_subdev->dev; + + if (sensor_info->status == ISP4SD_START_STATUS_STARTED) + return 0; + + if (sensor_info->status == ISP4SD_START_STATUS_START_FAIL) { + dev_err(dev, "fail for previous start fail\n"); + return -EINVAL; + } + + dev_dbg(dev, "w:%u,h:%u\n", w, h); + + if (isp4sd_send_meta_buf(isp_subdev)) { + dev_err(dev, "fail to send meta buf\n"); + sensor_info->status = ISP4SD_START_STATUS_START_FAIL; + return -EINVAL; + } + + sensor_info->status = ISP4SD_START_STATUS_OFF; + + if (!sensor_info->start_stream_cmd_sent && + sensor_info->buf_sent_cnt >= ISP4SD_MIN_BUF_CNT_BEF_START_STREAM) { + int ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_START_STREAM, + NULL, 0); + if (ret) { + dev_err(dev, "fail to start stream\n"); + return ret; + } + + sensor_info->start_stream_cmd_sent = true; + } else { + dev_dbg(dev, + "no send START_STREAM, start_sent %u, buf_sent %u\n", + sensor_info->start_stream_cmd_sent, + sensor_info->buf_sent_cnt); + } + + return 0; +} + +static int isp4sd_setup_output(struct isp4_subdev *isp_subdev, + struct v4l2_subdev_state *state, u32 pad) +{ + struct isp4sd_output_info *output_info = + &isp_subdev->sensor_info.output_info; + struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info; + struct isp4_interface *ispif = &isp_subdev->ispif; + struct isp4fw_cmd_set_out_ch_prop cmd_ch_prop; + struct isp4fw_cmd_enable_out_ch cmd_ch_en; + struct device *dev = isp_subdev->dev; + int ret; + + if (output_info->start_status == ISP4SD_START_STATUS_STARTED) + return 0; + + if (output_info->start_status == ISP4SD_START_STATUS_START_FAIL) { + dev_err(dev, "fail for previous start fail\n"); + return -EINVAL; + } + + /* + * The struct will be shared with ISP FW, use memset() to guarantee + * padding bits are zeroed, since this is not guaranteed on all + * compilers. + */ + memset(&cmd_ch_prop, 0, sizeof(cmd_ch_prop)); + cmd_ch_prop.ch = ISP4FW_ISP_PIPE_OUT_CH_PREVIEW; + + if (!isp4sd_get_str_out_prop(isp_subdev, + &cmd_ch_prop.image_prop, state, pad)) { + dev_err(dev, "fail to get out prop\n"); + return -EINVAL; + } + + dev_dbg(dev, "channel:%s,fmt %s,w:h=%u:%u,lp:%u,cp%u\n", + isp4dbg_get_out_ch_str(cmd_ch_prop.ch), + isp4dbg_get_img_fmt_str(cmd_ch_prop.image_prop.image_format), + cmd_ch_prop.image_prop.width, cmd_ch_prop.image_prop.height, + cmd_ch_prop.image_prop.luma_pitch, + cmd_ch_prop.image_prop.chroma_pitch); + + ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_SET_OUT_CHAN_PROP, + &cmd_ch_prop, sizeof(cmd_ch_prop)); + if (ret) { + output_info->start_status = ISP4SD_START_STATUS_START_FAIL; + dev_err(dev, "fail to set out prop\n"); + return ret; + } + + /* + * The struct will be shared with ISP FW, use memset() to guarantee + * padding bits are zeroed, since this is not guaranteed on all + * compilers. + */ + memset(&cmd_ch_en, 0, sizeof(cmd_ch_en)); + cmd_ch_en.ch = ISP4FW_ISP_PIPE_OUT_CH_PREVIEW; + cmd_ch_en.is_enable = true; + ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_ENABLE_OUT_CHAN, + &cmd_ch_en, sizeof(cmd_ch_en)); + if (ret) { + output_info->start_status = ISP4SD_START_STATUS_START_FAIL; + dev_err(dev, "fail to enable channel\n"); + return ret; + } + + dev_dbg(dev, "enable channel %s\n", + isp4dbg_get_out_ch_str(cmd_ch_en.ch)); + + if (!sensor_info->start_stream_cmd_sent) { + ret = isp4sd_kickoff_stream(isp_subdev, + cmd_ch_prop.image_prop.width, + cmd_ch_prop.image_prop.height); + if (ret) { + dev_err(dev, "kickoff stream fail %d\n", ret); + return ret; + } + /* + * sensor_info->start_stream_cmd_sent will be set to true + * 1. in isp4sd_kickoff_stream, if app first send buffer then + * start stream + * 2. in isp_set_stream_buf, if app first start stream, then + * send buffer because ISP FW has the requirement, host needs + * to send buffer before send start stream cmd + */ + if (sensor_info->start_stream_cmd_sent) { + sensor_info->status = ISP4SD_START_STATUS_STARTED; + output_info->start_status = ISP4SD_START_STATUS_STARTED; + dev_dbg(dev, "kickoff stream suc,start cmd sent\n"); + } + } else { + dev_dbg(dev, "stream running, no need kickoff\n"); + output_info->start_status = ISP4SD_START_STATUS_STARTED; + } + + dev_dbg(dev, "setup output suc\n"); + return 0; +} + +static int isp4sd_init_stream(struct isp4_subdev *isp_subdev) +{ + struct device *dev = isp_subdev->dev; + int ret; + + ret = isp4sd_setup_fw_mem_pool(isp_subdev); + if (ret) { + dev_err(dev, "fail to setup fw mem pool\n"); + return ret; + } + + ret = isp4sd_set_stream_path(isp_subdev); + if (ret) { + dev_err(dev, "fail to setup stream path\n"); + return ret; + } + + return 0; +} + +static void isp4sd_uninit_stream(struct isp4_subdev *isp_subdev, + struct v4l2_subdev_state *state, u32 pad) +{ + struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info; + struct isp4sd_output_info *output_info = &sensor_info->output_info; + struct isp4_interface *ispif = &isp_subdev->ispif; + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_state_get_format(state, pad, 0); + if (!format) { + dev_err(isp_subdev->dev, "fail to get v4l2 format\n"); + } else { + memset(format, 0, sizeof(*format)); + format->code = MEDIA_BUS_FMT_YUYV8_1_5X8; + } + + isp4if_clear_bufq(ispif); + isp4if_clear_cmdq(ispif); + + sensor_info->start_stream_cmd_sent = false; + sensor_info->buf_sent_cnt = 0; + + sensor_info->status = ISP4SD_START_STATUS_OFF; + output_info->start_status = ISP4SD_START_STATUS_OFF; +} + +static void isp4sd_fw_resp_cmd_done(struct isp4_subdev *isp_subdev, + enum isp4if_stream_id stream_id, + struct isp4fw_resp_cmd_done *para) +{ + struct isp4_interface *ispif = &isp_subdev->ispif; + struct isp4if_cmd_element *ele = + isp4if_rm_cmd_from_cmdq(ispif, para->cmd_seq_num, para->cmd_id); + struct device *dev = isp_subdev->dev; + + dev_dbg(dev, "stream %d,cmd %s(0x%08x)(%d),seq %u, ele %p\n", + stream_id, + isp4dbg_get_cmd_str(para->cmd_id), + para->cmd_id, para->cmd_status, para->cmd_seq_num, + ele); + + if (ele) { + complete(&ele->cmd_done); + if (atomic_dec_and_test(&ele->refcnt)) + kfree(ele); + } +} + +static struct isp4fw_meta_info * +isp4sd_get_meta_by_mc(struct isp4_subdev *isp_subdev, u64 mc) +{ + for (unsigned int i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++) { + struct isp4if_gpu_mem_info *meta_info_buf = + isp_subdev->ispif.meta_info_buf[i]; + + if (meta_info_buf->gpu_mc_addr == mc) + return meta_info_buf->sys_addr; + } + + return NULL; +} + +static void isp4sd_send_meta_info(struct isp4_subdev *isp_subdev, + u64 meta_info_mc) +{ + struct isp4_interface *ispif = &isp_subdev->ispif; + struct isp4fw_cmd_send_buffer buf_type; + struct device *dev = isp_subdev->dev; + + if (isp_subdev->sensor_info.status != ISP4SD_START_STATUS_STARTED) { + dev_warn(dev, "not working status %i, meta_info 0x%llx\n", + isp_subdev->sensor_info.status, meta_info_mc); + return; + } + + /* + * The struct will be shared with ISP FW, use memset() to guarantee + * padding bits are zeroed, since this is not guaranteed on all + * compilers. + */ + memset(&buf_type, 0, sizeof(buf_type)); + buf_type.buffer_type = ISP4FW_BUFFER_TYPE_META_INFO; + buf_type.buffer.vmid_space.bit.space = ISP4FW_ADDR_SPACE_TYPE_GPU_VA; + isp4if_split_addr64(meta_info_mc, + &buf_type.buffer.buf_base_a_lo, + &buf_type.buffer.buf_base_a_hi); + buf_type.buffer.buf_size_a = ISP4SD_META_BUF_SIZE; + + if (isp4if_send_command(ispif, ISP4FW_CMD_ID_SEND_BUFFER, + &buf_type, sizeof(buf_type))) + dev_err(dev, "fail send meta_info 0x%llx\n", + meta_info_mc); + else + dev_dbg(dev, "resend meta_info 0x%llx\n", meta_info_mc); +} + +static void isp4sd_fw_resp_frame_done(struct isp4_subdev *isp_subdev, + enum isp4if_stream_id stream_id, + struct isp4fw_resp_param_package *para) +{ + struct isp4_interface *ispif = &isp_subdev->ispif; + struct device *dev = isp_subdev->dev; + struct isp4if_img_buf_node *prev; + struct isp4fw_meta_info *meta; + u64 mc; + + mc = isp4if_join_addr64(para->package_addr_lo, para->package_addr_hi); + meta = isp4sd_get_meta_by_mc(isp_subdev, mc); + if (!meta) { + dev_err(dev, "fail to get meta from mc %llx\n", mc); + return; + } + + dev_dbg(dev, "ts:%llu,streamId:%d,poc:%u,preview_en:%u,status:%s(%i)\n", + ktime_get_ns(), stream_id, meta->poc, meta->preview.enabled, + isp4dbg_get_buf_done_str(meta->preview.status), + meta->preview.status); + + if (meta->preview.enabled && + (meta->preview.status == ISP4FW_BUFFER_STATUS_SKIPPED || + meta->preview.status == ISP4FW_BUFFER_STATUS_DONE || + meta->preview.status == ISP4FW_BUFFER_STATUS_DIRTY)) { + prev = isp4if_dequeue_buffer(ispif); + if (prev) { + isp4dbg_show_bufmeta_info(dev, "prev", &meta->preview, + &prev->buf_info); + isp4vid_handle_frame_done(&isp_subdev->isp_vdev, + &prev->buf_info); + isp4if_dealloc_buffer_node(prev); + } else { + dev_err(dev, "fail null prev buf\n"); + } + } else if (meta->preview.enabled) { + dev_err(dev, "fail bad preview status %u(%s)\n", + meta->preview.status, + isp4dbg_get_buf_done_str(meta->preview.status)); + } + + if (isp_subdev->sensor_info.status == ISP4SD_START_STATUS_STARTED) + isp4sd_send_meta_info(isp_subdev, mc); + + dev_dbg(dev, "stream_id:%d, status:%d\n", stream_id, + isp_subdev->sensor_info.status); +} + +static void isp4sd_fw_resp_func(struct isp4_subdev *isp_subdev, + enum isp4if_stream_id stream_id) +{ + struct isp4_interface *ispif = &isp_subdev->ispif; + struct device *dev = isp_subdev->dev; + struct isp4fw_resp resp; + + if (stream_id == ISP4IF_STREAM_ID_1) + isp_fw_log_print(isp_subdev); + + while (true) { + if (isp4if_f2h_resp(ispif, stream_id, &resp)) { + /* Re-enable the interrupt */ + isp4_intr_enable(isp_subdev, stream_id, true); + /* + * Recheck to see if there is a new response. + * To ensure that an in-flight interrupt is not lost, + * enabling the interrupt must occur _before_ checking + * for a new response, hence a memory barrier is needed. + * Disable the interrupt again if there was a new + * response. + */ + mb(); + if (likely(isp4if_f2h_resp(ispif, stream_id, &resp))) + break; + + isp4_intr_enable(isp_subdev, stream_id, false); + } + + switch (resp.resp_id) { + case ISP4FW_RESP_ID_CMD_DONE: + isp4sd_fw_resp_cmd_done(isp_subdev, stream_id, + &resp.param.cmd_done); + break; + case ISP4FW_RESP_ID_NOTI_FRAME_DONE: + isp4sd_fw_resp_frame_done(isp_subdev, stream_id, + &resp.param.frame_done); + break; + default: + dev_err(dev, "-><- fail respid %s(0x%x)\n", + isp4dbg_get_resp_str(resp.resp_id), + resp.resp_id); + break; + } + } +} + +static s32 isp4sd_fw_resp_thread(void *context) +{ + struct isp4_subdev_thread_param *para = context; + struct isp4_subdev *isp_subdev = para->isp_subdev; + struct isp4sd_thread_handler *thread_ctx = + &isp_subdev->fw_resp_thread[para->idx]; + struct device *dev = isp_subdev->dev; + + dev_dbg(dev, "[%u] fw resp thread started\n", para->idx); + while (true) { + wait_event_interruptible(thread_ctx->waitq, + thread_ctx->resp_ready); + thread_ctx->resp_ready = false; + + if (kthread_should_stop()) { + dev_dbg(dev, "[%u] fw resp thread quit\n", para->idx); + break; + } + + isp4sd_fw_resp_func(isp_subdev, para->idx); + } + + return 0; +} + +static int isp4sd_stop_resp_proc_threads(struct isp4_subdev *isp_subdev) +{ + for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++) { + struct isp4sd_thread_handler *thread_ctx = + &isp_subdev->fw_resp_thread[i]; + + if (thread_ctx->thread) { + kthread_stop(thread_ctx->thread); + thread_ctx->thread = NULL; + } + } + + return 0; +} + +static int isp4sd_start_resp_proc_threads(struct isp4_subdev *isp_subdev) +{ + struct device *dev = isp_subdev->dev; + + for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++) { + struct isp4sd_thread_handler *thread_ctx = + &isp_subdev->fw_resp_thread[i]; + + isp_subdev->isp_resp_para[i].idx = i; + isp_subdev->isp_resp_para[i].isp_subdev = isp_subdev; + init_waitqueue_head(&thread_ctx->waitq); + thread_ctx->resp_ready = false; + + thread_ctx->thread = kthread_run(isp4sd_fw_resp_thread, + &isp_subdev->isp_resp_para[i], + isp4sd_thread_name[i]); + if (IS_ERR(thread_ctx->thread)) { + dev_err(dev, "create thread [%d] fail\n", i); + thread_ctx->thread = NULL; + isp4sd_stop_resp_proc_threads(isp_subdev); + return -EINVAL; + } + } + + return 0; +} + +int isp4sd_pwroff_and_deinit(struct v4l2_subdev *sd) +{ + struct isp4_subdev *isp_subdev = to_isp4_subdev(sd); + struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info; + unsigned int perf_state = ISP4SD_PERFORMANCE_STATE_LOW; + struct isp4_interface *ispif = &isp_subdev->ispif; + struct device *dev = isp_subdev->dev; + int ret; + + guard(mutex)(&isp_subdev->ops_mutex); + if (sensor_info->status == ISP4SD_START_STATUS_STARTED) { + dev_err(dev, "fail for stream still running\n"); + return -EINVAL; + } + + sensor_info->status = ISP4SD_START_STATUS_OFF; + + if (isp_subdev->irq_enabled) { + for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++) + disable_irq(isp_subdev->irq[i]); + isp_subdev->irq_enabled = false; + } + + isp4sd_stop_resp_proc_threads(isp_subdev); + dev_dbg(dev, "isp_subdev stop resp proc threads suc\n"); + + isp4if_stop(ispif); + + ret = dev_pm_genpd_set_performance_state(dev, perf_state); + if (ret) + dev_err(dev, + "fail to set isp_subdev performance state %u,ret %d\n", + perf_state, ret); + + /* hold ccpu reset */ + isp4hw_wreg(isp_subdev->mmio, ISP_SOFT_RESET, 0); + isp4hw_wreg(isp_subdev->mmio, ISP_POWER_STATUS, 0); + ret = pm_runtime_put_sync(dev); + if (ret) + dev_err(dev, "power off isp_subdev fail %d\n", ret); + else + dev_dbg(dev, "power off isp_subdev suc\n"); + + ispif->status = ISP4IF_STATUS_PWR_OFF; + isp4if_clear_cmdq(ispif); + isp4sd_module_enable(isp_subdev, false); + + /* + * When opening the camera, isp4sd_module_enable(isp_subdev, true) is + * called. Hardware requires at least a 20ms delay between disabling + * and enabling the module, so a sleep is added to ensure ISP stability + * during quick reopen scenarios. + */ + msleep(20); + + return 0; +} + +int isp4sd_pwron_and_init(struct v4l2_subdev *sd) +{ + struct isp4_subdev *isp_subdev = to_isp4_subdev(sd); + struct isp4_interface *ispif = &isp_subdev->ispif; + struct device *dev = isp_subdev->dev; + int ret; + + guard(mutex)(&isp_subdev->ops_mutex); + if (ispif->status == ISP4IF_STATUS_FW_RUNNING) { + dev_dbg(dev, "camera already opened, do nothing\n"); + return 0; + } + + isp4sd_module_enable(isp_subdev, true); + + if (ispif->status < ISP4IF_STATUS_PWR_ON) { + unsigned int perf_state = ISP4SD_PERFORMANCE_STATE_HIGH; + + ret = pm_runtime_resume_and_get(dev); + if (ret) { + dev_err(dev, "fail to power on isp_subdev ret %d\n", + ret); + goto err_deinit; + } + + /* ISPPG ISP Power Status */ + isp4hw_wreg(isp_subdev->mmio, ISP_POWER_STATUS, 0x7FF); + ret = dev_pm_genpd_set_performance_state(dev, perf_state); + if (ret) { + dev_err(dev, + "fail to set performance state %u, ret %d\n", + perf_state, ret); + goto err_deinit; + } + + ispif->status = ISP4IF_STATUS_PWR_ON; + } + + isp_subdev->sensor_info.start_stream_cmd_sent = false; + isp_subdev->sensor_info.buf_sent_cnt = 0; + + ret = isp4if_start(ispif); + if (ret) { + dev_err(dev, "fail to start isp_subdev interface\n"); + goto err_deinit; + } + + if (isp4sd_start_resp_proc_threads(isp_subdev)) { + dev_err(dev, "isp_start_resp_proc_threads fail\n"); + goto err_deinit; + } + + dev_dbg(dev, "create resp threads ok\n"); + + for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++) + enable_irq(isp_subdev->irq[i]); + isp_subdev->irq_enabled = true; + + return 0; +err_deinit: + isp4sd_pwroff_and_deinit(sd); + return -EINVAL; +} + +static int isp4sd_stop_stream(struct isp4_subdev *isp_subdev, + struct v4l2_subdev_state *state, u32 pad) +{ + struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info; + struct isp4sd_output_info *output_info = &sensor_info->output_info; + struct isp4_interface *ispif = &isp_subdev->ispif; + struct device *dev = isp_subdev->dev; + + guard(mutex)(&isp_subdev->ops_mutex); + dev_dbg(dev, "status %i\n", output_info->start_status); + + if (output_info->start_status == ISP4SD_START_STATUS_STARTED) { + struct isp4fw_cmd_enable_out_ch cmd_ch_disable; + int ret; + + /* + * The struct will be shared with ISP FW, use memset() to + * guarantee padding bits are zeroed, since this is not + * guaranteed on all compilers. + */ + memset(&cmd_ch_disable, 0, sizeof(cmd_ch_disable)); + cmd_ch_disable.ch = ISP4FW_ISP_PIPE_OUT_CH_PREVIEW; + /* `cmd_ch_disable.is_enable` is already false */ + ret = isp4if_send_command_sync(ispif, + ISP4FW_CMD_ID_ENABLE_OUT_CHAN, + &cmd_ch_disable, + sizeof(cmd_ch_disable)); + if (ret) + dev_err(dev, "fail to disable stream\n"); + else + dev_dbg(dev, "wait disable stream suc\n"); + + ret = isp4if_send_command_sync(ispif, ISP4FW_CMD_ID_STOP_STREAM, + NULL, 0); + if (ret) + dev_err(dev, "fail to stop stream\n"); + else + dev_dbg(dev, "wait stop stream suc\n"); + } + + isp4sd_uninit_stream(isp_subdev, state, pad); + + /* + * Return success to ensure the stop process proceeds, + * and disregard any errors since they are not fatal. + */ + return 0; +} + +static int isp4sd_start_stream(struct isp4_subdev *isp_subdev, + struct v4l2_subdev_state *state, u32 pad) +{ + struct isp4sd_output_info *output_info = + &isp_subdev->sensor_info.output_info; + struct isp4_interface *ispif = &isp_subdev->ispif; + struct device *dev = isp_subdev->dev; + int ret; + + guard(mutex)(&isp_subdev->ops_mutex); + + if (ispif->status != ISP4IF_STATUS_FW_RUNNING) { + dev_err(dev, "fail, bad fsm %d\n", ispif->status); + return -EINVAL; + } + + switch (output_info->start_status) { + case ISP4SD_START_STATUS_OFF: + break; + case ISP4SD_START_STATUS_STARTED: + dev_dbg(dev, "stream already started, do nothing\n"); + return 0; + case ISP4SD_START_STATUS_START_FAIL: + dev_err(dev, "stream previously failed to start\n"); + return -EINVAL; + } + + ret = isp4sd_init_stream(isp_subdev); + if (ret) { + dev_err(dev, "fail to init isp_subdev stream\n"); + goto err_stop_stream; + } + + ret = isp4sd_setup_output(isp_subdev, state, pad); + if (ret) { + dev_err(dev, "fail to setup output\n"); + goto err_stop_stream; + } + + return 0; + +err_stop_stream: + isp4sd_stop_stream(isp_subdev, state, pad); + return ret; +} + +int isp4sd_ioc_send_img_buf(struct v4l2_subdev *sd, + struct isp4if_img_buf_info *buf_info) +{ + struct isp4_subdev *isp_subdev = to_isp4_subdev(sd); + struct isp4_interface *ispif = &isp_subdev->ispif; + struct isp4if_img_buf_node *buf_node; + struct device *dev = isp_subdev->dev; + int ret; + + guard(mutex)(&isp_subdev->ops_mutex); + + if (ispif->status != ISP4IF_STATUS_FW_RUNNING) { + dev_err(dev, "fail send img buf for bad fsm %d\n", + ispif->status); + return -EINVAL; + } + + buf_node = isp4if_alloc_buffer_node(buf_info); + if (!buf_node) { + dev_err(dev, "fail alloc sys img buf info node\n"); + return -ENOMEM; + } + + ret = isp4if_queue_buffer(ispif, buf_node); + if (ret) { + dev_err(dev, "fail to queue image buf, %d\n", ret); + goto error_release_buf_node; + } + + if (!isp_subdev->sensor_info.start_stream_cmd_sent) { + isp_subdev->sensor_info.buf_sent_cnt++; + + if (isp_subdev->sensor_info.buf_sent_cnt >= + ISP4SD_MIN_BUF_CNT_BEF_START_STREAM) { + ret = isp4if_send_command(ispif, + ISP4FW_CMD_ID_START_STREAM, + NULL, 0); + if (ret) { + dev_err(dev, "fail to START_STREAM"); + goto error_release_buf_node; + } + isp_subdev->sensor_info.start_stream_cmd_sent = true; + isp_subdev->sensor_info.output_info.start_status = + ISP4SD_START_STATUS_STARTED; + isp_subdev->sensor_info.status = + ISP4SD_START_STATUS_STARTED; + } else { + dev_dbg(dev, + "no send start, required %u, buf sent %u\n", + ISP4SD_MIN_BUF_CNT_BEF_START_STREAM, + isp_subdev->sensor_info.buf_sent_cnt); + } + } + + return 0; + +error_release_buf_node: + isp4if_dealloc_buffer_node(buf_node); + return ret; +} + +static const struct v4l2_subdev_video_ops isp4sd_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static int isp4sd_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *format) +{ + struct isp4sd_output_info *stream_info = + &(to_isp4_subdev(sd)->sensor_info.output_info); + struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); + + if (!fmt) { + dev_err(sd->dev, "fail to get state format\n"); + return -EINVAL; + } + + *fmt = format->format; + switch (fmt->code) { + case MEDIA_BUS_FMT_YUYV8_1X16: + stream_info->image_size = fmt->width * fmt->height * 2; + break; + case MEDIA_BUS_FMT_YUYV8_1_5X8: + default: + stream_info->image_size = fmt->width * fmt->height * 3 / 2; + break; + } + + if (!stream_info->image_size) { + dev_err(sd->dev, + "fail set pad format,code 0x%x,width %u, height %u\n", + fmt->code, fmt->width, fmt->height); + return -EINVAL; + } + + dev_dbg(sd->dev, "set pad format suc, code:%x w:%u h:%u size:%u\n", + fmt->code, fmt->width, fmt->height, + stream_info->image_size); + + return 0; +} + +static int isp4sd_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct isp4_subdev *isp_subdev = to_isp4_subdev(sd); + + return isp4sd_start_stream(isp_subdev, state, pad); +} + +static int isp4sd_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct isp4_subdev *isp_subdev = to_isp4_subdev(sd); + + return isp4sd_stop_stream(isp_subdev, state, pad); +} + +static const struct v4l2_subdev_pad_ops isp4sd_pad_ops = { + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = isp4sd_set_pad_format, + .enable_streams = isp4sd_enable_streams, + .disable_streams = isp4sd_disable_streams, +}; + +static const struct v4l2_subdev_ops isp4sd_subdev_ops = { + .video = &isp4sd_video_ops, + .pad = &isp4sd_pad_ops, +}; + +int isp4sd_init(struct isp4_subdev *isp_subdev, struct v4l2_device *v4l2_dev, + int irq[ISP4SD_MAX_FW_RESP_STREAM_NUM]) +{ + struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info; + struct isp4_interface *ispif = &isp_subdev->ispif; + struct device *dev = v4l2_dev->dev; + int ret; + + isp_subdev->dev = dev; + v4l2_subdev_init(&isp_subdev->sdev, &isp4sd_subdev_ops); + isp_subdev->sdev.owner = THIS_MODULE; + isp_subdev->sdev.dev = dev; + snprintf(isp_subdev->sdev.name, sizeof(isp_subdev->sdev.name), "%s", + dev_name(dev)); + + isp_subdev->sdev.entity.name = isp4sd_entity_name; + isp_subdev->sdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_ISP; + isp_subdev->sdev_pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&isp_subdev->sdev.entity, 1, + &isp_subdev->sdev_pad); + if (ret) { + dev_err(dev, "fail to init isp4 subdev entity pad %d\n", ret); + return ret; + } + + ret = v4l2_subdev_init_finalize(&isp_subdev->sdev); + if (ret < 0) { + dev_err(dev, "fail to init finalize isp4 subdev %d\n", + ret); + return ret; + } + + ret = v4l2_device_register_subdev(v4l2_dev, &isp_subdev->sdev); + if (ret) { + dev_err(dev, "fail to register isp4 subdev to V4L2 device %d\n", + ret); + goto err_media_clean_up; + } + + isp4if_init(ispif, dev, isp_subdev->mmio); + + mutex_init(&isp_subdev->ops_mutex); + sensor_info->status = ISP4SD_START_STATUS_OFF; + + /* create ISP enable gpio control */ + isp_subdev->enable_gpio = devm_gpiod_get(isp_subdev->dev, + "enable_isp", + GPIOD_OUT_LOW); + if (IS_ERR(isp_subdev->enable_gpio)) { + ret = PTR_ERR(isp_subdev->enable_gpio); + dev_err(dev, "fail to get gpiod %d\n", ret); + goto err_subdev_unreg; + } + + for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++) + isp_subdev->irq[i] = irq[i]; + + isp_subdev->host2fw_seq_num = 1; + ispif->status = ISP4IF_STATUS_PWR_OFF; + + ret = isp4vid_dev_init(&isp_subdev->isp_vdev, &isp_subdev->sdev); + if (ret) + goto err_subdev_unreg; + + return 0; + +err_subdev_unreg: + v4l2_device_unregister_subdev(&isp_subdev->sdev); +err_media_clean_up: + v4l2_subdev_cleanup(&isp_subdev->sdev); + media_entity_cleanup(&isp_subdev->sdev.entity); + return ret; +} + +void isp4sd_deinit(struct isp4_subdev *isp_subdev) +{ + struct isp4_interface *ispif = &isp_subdev->ispif; + + isp4vid_dev_deinit(&isp_subdev->isp_vdev); + v4l2_device_unregister_subdev(&isp_subdev->sdev); + media_entity_cleanup(&isp_subdev->sdev.entity); + isp4if_deinit(ispif); + isp4sd_module_enable(isp_subdev, false); + + ispif->status = ISP4IF_STATUS_PWR_OFF; +} diff --git a/drivers/media/platform/amd/isp4/isp4_subdev.h b/drivers/media/platform/amd/isp4/isp4_subdev.h new file mode 100644 index 000000000000..20ea08a830af --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_subdev.h @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _ISP4_SUBDEV_H_ +#define _ISP4_SUBDEV_H_ + +#include <linux/debugfs.h> +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/pm_runtime.h> +#include <linux/types.h> +#include <linux/uaccess.h> +#include <media/v4l2-device.h> + +#include "isp4_fw_cmd_resp.h" +#include "isp4_hw_reg.h" +#include "isp4_interface.h" +#include "isp4_video.h" + +/* + * One is for none sensor specific response which is not used now. + * Another is for sensor specific response + */ +#define ISP4SD_MAX_FW_RESP_STREAM_NUM 2 + +/* Indicates the ISP status */ +enum isp4sd_status { + ISP4SD_STATUS_PWR_OFF, + ISP4SD_STATUS_PWR_ON, + ISP4SD_STATUS_FW_RUNNING, + ISP4SD_STATUS_MAX +}; + +/* Indicates sensor and output stream status */ +enum isp4sd_start_status { + ISP4SD_START_STATUS_OFF, + ISP4SD_START_STATUS_STARTED, + ISP4SD_START_STATUS_START_FAIL, +}; + +struct isp4sd_img_buf_node { + struct list_head node; + struct isp4if_img_buf_info buf_info; +}; + +/* This is ISP output after processing Bayer raw sensor input */ +struct isp4sd_output_info { + enum isp4sd_start_status start_status; + u32 image_size; +}; + +/* + * Struct for sensor info used as ISP input or source. + * status: sensor status. + * output_info: ISP output after processing the sensor input. + * start_stream_cmd_sent: indicates if ISP4FW_CMD_ID_START_STREAM was sent + * to firmware. + * buf_sent_cnt: number of buffers sent to receive images. + */ +struct isp4sd_sensor_info { + struct isp4sd_output_info output_info; + enum isp4sd_start_status status; + bool start_stream_cmd_sent; + u32 buf_sent_cnt; +}; + +/* + * The thread is created by the driver to handle firmware responses which will + * be waken up when a firmware-to-driver response interrupt occurs. + */ +struct isp4sd_thread_handler { + struct task_struct *thread; + wait_queue_head_t waitq; + bool resp_ready; +}; + +struct isp4_subdev_thread_param { + u32 idx; + struct isp4_subdev *isp_subdev; +}; + +struct isp4_subdev { + struct v4l2_subdev sdev; + struct isp4_interface ispif; + struct isp4vid_dev isp_vdev; + + struct media_pad sdev_pad; + + enum isp4sd_status isp_status; + /* mutex used to synchronize the operation with firmware */ + struct mutex ops_mutex; + + struct isp4sd_thread_handler + fw_resp_thread[ISP4SD_MAX_FW_RESP_STREAM_NUM]; + + u32 host2fw_seq_num; + + struct isp4sd_sensor_info sensor_info; + + /* gpio descriptor */ + struct gpio_desc *enable_gpio; + struct device *dev; + void __iomem *mmio; + struct isp4_subdev_thread_param + isp_resp_para[ISP4SD_MAX_FW_RESP_STREAM_NUM]; + int irq[ISP4SD_MAX_FW_RESP_STREAM_NUM]; + bool irq_enabled; + /* spin lock to access ISP_SYS_INT0_EN exclusively */ + spinlock_t irq_lock; +#ifdef CONFIG_DEBUG_FS + bool enable_fw_log; + struct dentry *debugfs_dir; + char *fw_log_output; +#endif +}; + +int isp4sd_init(struct isp4_subdev *isp_subdev, struct v4l2_device *v4l2_dev, + int irq[ISP4SD_MAX_FW_RESP_STREAM_NUM]); +void isp4sd_deinit(struct isp4_subdev *isp_subdev); +int isp4sd_ioc_send_img_buf(struct v4l2_subdev *sd, + struct isp4if_img_buf_info *buf_info); +int isp4sd_pwron_and_init(struct v4l2_subdev *sd); +int isp4sd_pwroff_and_deinit(struct v4l2_subdev *sd); + +#endif /* _ISP4_SUBDEV_H_ */ diff --git a/drivers/media/platform/amd/isp4/isp4_video.c b/drivers/media/platform/amd/isp4/isp4_video.c new file mode 100644 index 000000000000..0cebb39f98e1 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_video.c @@ -0,0 +1,797 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#include <media/v4l2-ioctl.h> +#include <media/v4l2-mc.h> +#include <media/videobuf2-vmalloc.h> + +#include "isp4_interface.h" +#include "isp4_subdev.h" +#include "isp4_video.h" + +#define ISP4VID_ISP_DRV_NAME "amd_isp_capture" +#define ISP4VID_MAX_PREVIEW_FPS 30 +#define ISP4VID_DEFAULT_FMT V4L2_PIX_FMT_NV12 + +#define ISP4VID_PAD_VIDEO_OUTPUT 0 + +/* time perframe default */ +#define ISP4VID_ISP_TPF_DEFAULT isp4vid_tpfs[0] + +static const char *const isp4vid_video_dev_name = "Preview"; + +/* Sizes must be in increasing order */ +static const struct v4l2_frmsize_discrete isp4vid_frmsize[] = { + {640, 360}, + {640, 480}, + {1280, 720}, + {1280, 960}, + {1920, 1080}, + {1920, 1440}, + {2560, 1440}, + {2880, 1620}, + {2880, 1624}, + {2888, 1808}, +}; + +static const u32 isp4vid_formats[] = { + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_YUYV +}; + +/* time perframe list */ +static const struct v4l2_fract isp4vid_tpfs[] = { + { 1, ISP4VID_MAX_PREVIEW_FPS } +}; + +void isp4vid_handle_frame_done(struct isp4vid_dev *isp_vdev, + const struct isp4if_img_buf_info *img_buf) +{ + struct isp4vid_capture_buffer *isp4vid_buf; + void *vbuf; + + scoped_guard(mutex, &isp_vdev->buf_list_lock) { + isp4vid_buf = list_first_entry_or_null(&isp_vdev->buf_list, + typeof(*isp4vid_buf), + list); + if (!isp4vid_buf) + return; + + vbuf = vb2_plane_vaddr(&isp4vid_buf->vb2.vb2_buf, 0); + + if (vbuf != img_buf->planes[0].sys_addr) { + dev_err(isp_vdev->dev, "Invalid vbuf\n"); + return; + } + + list_del(&isp4vid_buf->list); + } + + /* Fill the buffer */ + isp4vid_buf->vb2.vb2_buf.timestamp = ktime_get_ns(); + isp4vid_buf->vb2.sequence = isp_vdev->sequence++; + isp4vid_buf->vb2.field = V4L2_FIELD_ANY; + + vb2_set_plane_payload(&isp4vid_buf->vb2.vb2_buf, + 0, isp_vdev->format.sizeimage); + + vb2_buffer_done(&isp4vid_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE); + + dev_dbg(isp_vdev->dev, "call vb2_buffer_done(size=%u)\n", + isp_vdev->format.sizeimage); +} + +static const struct v4l2_pix_format isp4vid_fmt_default = { + .width = 1920, + .height = 1080, + .pixelformat = ISP4VID_DEFAULT_FMT, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, +}; + +static void isp4vid_capture_return_all_buffers(struct isp4vid_dev *isp_vdev, + enum vb2_buffer_state state) +{ + struct isp4vid_capture_buffer *vbuf, *node; + + scoped_guard(mutex, &isp_vdev->buf_list_lock) { + list_for_each_entry_safe(vbuf, node, &isp_vdev->buf_list, list) + vb2_buffer_done(&vbuf->vb2.vb2_buf, state); + INIT_LIST_HEAD(&isp_vdev->buf_list); + } + + dev_dbg(isp_vdev->dev, "call vb2_buffer_done(%d)\n", state); +} + +static int isp4vid_vdev_link_validate(struct media_link *link) +{ + return 0; +} + +static const struct media_entity_operations isp4vid_vdev_ent_ops = { + .link_validate = isp4vid_vdev_link_validate, +}; + +static const struct v4l2_file_operations isp4vid_vdev_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, +}; + +static int isp4vid_ioctl_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + + strscpy(cap->driver, ISP4VID_ISP_DRV_NAME, sizeof(cap->driver)); + snprintf(cap->card, sizeof(cap->card), "%s", ISP4VID_ISP_DRV_NAME); + cap->capabilities |= V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE; + + dev_dbg(isp_vdev->dev, "%s|capabilities=0x%X\n", isp_vdev->vdev.name, + cap->capabilities); + + return 0; +} + +static int isp4vid_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + + f->fmt.pix = isp_vdev->format; + + return 0; +} + +static int isp4vid_fill_buffer_size(struct v4l2_pix_format *fmt) +{ + int ret = 0; + + switch (fmt->pixelformat) { + case V4L2_PIX_FMT_NV12: + fmt->bytesperline = fmt->width; + fmt->sizeimage = fmt->bytesperline * fmt->height * 3 / 2; + break; + case V4L2_PIX_FMT_YUYV: + fmt->bytesperline = fmt->width * 2; + fmt->sizeimage = fmt->bytesperline * fmt->height; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int isp4vid_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + struct v4l2_pix_format *format = &f->fmt.pix; + const struct v4l2_frmsize_discrete *fsz; + size_t i; + + /* + * Check if the hardware supports the requested format, use the default + * format otherwise. + */ + for (i = 0; i < ARRAY_SIZE(isp4vid_formats); i++) + if (isp4vid_formats[i] == format->pixelformat) + break; + + if (i == ARRAY_SIZE(isp4vid_formats)) + format->pixelformat = ISP4VID_DEFAULT_FMT; + + switch (format->pixelformat) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_YUYV: + fsz = v4l2_find_nearest_size(isp4vid_frmsize, + ARRAY_SIZE(isp4vid_frmsize), + width, height, format->width, + format->height); + format->width = fsz->width; + format->height = fsz->height; + break; + default: + dev_err(isp_vdev->dev, "%s|unsupported fmt=%u\n", + isp_vdev->vdev.name, + format->pixelformat); + return -EINVAL; + } + + /* + * There is no need to check the return value, as failure will never + * happen here + */ + isp4vid_fill_buffer_size(format); + + if (format->field == V4L2_FIELD_ANY) + format->field = isp4vid_fmt_default.field; + + if (format->colorspace == V4L2_COLORSPACE_DEFAULT) + format->colorspace = isp4vid_fmt_default.colorspace; + + return 0; +} + +static int isp4vid_set_fmt_2_isp(struct v4l2_subdev *sdev, + struct v4l2_pix_format *pix_fmt) +{ + struct v4l2_subdev_format fmt = {}; + + switch (pix_fmt->pixelformat) { + case V4L2_PIX_FMT_NV12: + fmt.format.code = MEDIA_BUS_FMT_YUYV8_1_5X8; + break; + case V4L2_PIX_FMT_YUYV: + fmt.format.code = MEDIA_BUS_FMT_YUYV8_1X16; + break; + default: + return -EINVAL; + } + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt.pad = ISP4VID_PAD_VIDEO_OUTPUT; + fmt.format.width = pix_fmt->width; + fmt.format.height = pix_fmt->height; + return v4l2_subdev_call(sdev, pad, set_fmt, NULL, &fmt); +} + +static int isp4vid_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + int ret; + + /* Do not change the format while stream is on */ + if (vb2_is_busy(&isp_vdev->vbq)) + return -EBUSY; + + ret = isp4vid_try_fmt_vid_cap(file, priv, f); + if (ret) + return ret; + + dev_dbg(isp_vdev->dev, "%s|width height:%ux%u->%ux%u\n", + isp_vdev->vdev.name, + isp_vdev->format.width, isp_vdev->format.height, + f->fmt.pix.width, f->fmt.pix.height); + dev_dbg(isp_vdev->dev, "%s|pixelformat:0x%x-0x%x\n", + isp_vdev->vdev.name, isp_vdev->format.pixelformat, + f->fmt.pix.pixelformat); + dev_dbg(isp_vdev->dev, "%s|bytesperline:%u->%u\n", + isp_vdev->vdev.name, isp_vdev->format.bytesperline, + f->fmt.pix.bytesperline); + dev_dbg(isp_vdev->dev, "%s|sizeimage:%u->%u\n", + isp_vdev->vdev.name, isp_vdev->format.sizeimage, + f->fmt.pix.sizeimage); + + isp_vdev->format = f->fmt.pix; + ret = isp4vid_set_fmt_2_isp(isp_vdev->isp_sdev, &isp_vdev->format); + + return ret; +} + +static int isp4vid_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + + switch (f->index) { + case 0: + f->pixelformat = V4L2_PIX_FMT_NV12; + break; + case 1: + f->pixelformat = V4L2_PIX_FMT_YUYV; + break; + default: + return -EINVAL; + } + + dev_dbg(isp_vdev->dev, "%s|index=%d, pixelformat=0x%X\n", + isp_vdev->vdev.name, f->index, f->pixelformat); + + return 0; +} + +static int isp4vid_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(isp4vid_formats); i++) { + if (isp4vid_formats[i] == fsize->pixel_format) + break; + } + + if (i == ARRAY_SIZE(isp4vid_formats)) + return -EINVAL; + + if (fsize->index < ARRAY_SIZE(isp4vid_frmsize)) { + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete = isp4vid_frmsize[fsize->index]; + dev_dbg(isp_vdev->dev, "%s|size[%d]=%dx%d\n", + isp_vdev->vdev.name, fsize->index, + fsize->discrete.width, fsize->discrete.height); + } else { + return -EINVAL; + } + + return 0; +} + +static int isp4vid_ioctl_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fival) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + size_t i; + + if (fival->index >= ARRAY_SIZE(isp4vid_tpfs)) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(isp4vid_formats); i++) + if (isp4vid_formats[i] == fival->pixel_format) + break; + + if (i == ARRAY_SIZE(isp4vid_formats)) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(isp4vid_frmsize); i++) + if (isp4vid_frmsize[i].width == fival->width && + isp4vid_frmsize[i].height == fival->height) + break; + + if (i == ARRAY_SIZE(isp4vid_frmsize)) + return -EINVAL; + + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete = isp4vid_tpfs[fival->index]; + v4l2_simplify_fraction(&fival->discrete.numerator, + &fival->discrete.denominator, 8, 333); + + dev_dbg(isp_vdev->dev, "%s|interval[%d]=%d/%d\n", + isp_vdev->vdev.name, fival->index, + fival->discrete.numerator, + fival->discrete.denominator); + + return 0; +} + +static int isp4vid_ioctl_g_param(struct file *file, void *priv, + struct v4l2_streamparm *param) +{ + struct v4l2_captureparm *capture = ¶m->parm.capture; + struct isp4vid_dev *isp_vdev = video_drvdata(file); + + if (param->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + capture->capability = V4L2_CAP_TIMEPERFRAME; + capture->timeperframe = isp_vdev->timeperframe; + capture->readbuffers = 0; + + dev_dbg(isp_vdev->dev, "%s|timeperframe=%d/%d\n", isp_vdev->vdev.name, + capture->timeperframe.numerator, + capture->timeperframe.denominator); + + return 0; +} + +static const struct v4l2_ioctl_ops isp4vid_vdev_ioctl_ops = { + .vidioc_querycap = isp4vid_ioctl_querycap, + .vidioc_enum_fmt_vid_cap = isp4vid_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = isp4vid_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = isp4vid_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = isp4vid_try_fmt_vid_cap, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_g_parm = isp4vid_ioctl_g_param, + .vidioc_s_parm = isp4vid_ioctl_g_param, + .vidioc_enum_framesizes = isp4vid_enum_framesizes, + .vidioc_enum_frameintervals = isp4vid_ioctl_enum_frameintervals, +}; + +static unsigned int isp4vid_get_image_size(struct v4l2_pix_format *fmt) +{ + switch (fmt->pixelformat) { + case V4L2_PIX_FMT_NV12: + return fmt->width * fmt->height * 3 / 2; + case V4L2_PIX_FMT_YUYV: + return fmt->width * fmt->height * 2; + default: + return 0; + } +} + +static int isp4vid_qops_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vq); + unsigned int q_num_bufs = vb2_get_num_buffers(vq); + + if (*nplanes > 1) { + dev_err(isp_vdev->dev, + "fail to setup queue, no mplane supported %u\n", + *nplanes); + return -EINVAL; + } + + if (*nplanes == 1) { + unsigned int size; + + size = isp4vid_get_image_size(&isp_vdev->format); + if (sizes[0] < size) { + dev_err(isp_vdev->dev, + "fail for small plane size %u, %u expected\n", + sizes[0], size); + return -EINVAL; + } + } + + if (q_num_bufs + *nbuffers < ISP4IF_MAX_STREAM_BUF_COUNT) + *nbuffers = ISP4IF_MAX_STREAM_BUF_COUNT - q_num_bufs; + + switch (isp_vdev->format.pixelformat) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_YUYV: { + *nplanes = 1; + sizes[0] = max(sizes[0], isp_vdev->format.sizeimage); + isp_vdev->format.sizeimage = sizes[0]; + } + break; + default: + dev_err(isp_vdev->dev, "%s|unsupported fmt=%u\n", + isp_vdev->vdev.name, isp_vdev->format.pixelformat); + return -EINVAL; + } + + dev_dbg(isp_vdev->dev, "%s|*nbuffers=%u *nplanes=%u sizes[0]=%u\n", + isp_vdev->vdev.name, + *nbuffers, *nplanes, sizes[0]); + + return 0; +} + +static void isp4vid_qops_buffer_queue(struct vb2_buffer *vb) +{ + struct isp4vid_capture_buffer *buf = + container_of(vb, struct isp4vid_capture_buffer, vb2.vb2_buf); + struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vb->vb2_queue); + struct isp4if_img_buf_info *img_buf = &buf->img_buf; + void *vaddr = vb2_plane_vaddr(vb, 0); + + dev_dbg(isp_vdev->dev, "queue buf, vaddr %p, gpuva 0x%llx, size %u\n", + vaddr, buf->gpu_addr, vb->planes[0].length); + + switch (isp_vdev->format.pixelformat) { + case V4L2_PIX_FMT_NV12: { + u32 y_size = isp_vdev->format.sizeimage / 3 * 2; + u32 uv_size = isp_vdev->format.sizeimage / 3; + + img_buf->planes[0].len = y_size; + img_buf->planes[0].sys_addr = vaddr; + img_buf->planes[0].mc_addr = buf->gpu_addr; + + dev_dbg(isp_vdev->dev, "img_buf[0]: mc=0x%llx size=%u\n", + img_buf->planes[0].mc_addr, + img_buf->planes[0].len); + + img_buf->planes[1].len = uv_size; + img_buf->planes[1].sys_addr = vaddr + y_size; + img_buf->planes[1].mc_addr = buf->gpu_addr + y_size; + + dev_dbg(isp_vdev->dev, "img_buf[1]: mc=0x%llx size=%u\n", + img_buf->planes[1].mc_addr, + img_buf->planes[1].len); + + img_buf->planes[2].len = 0; + } + break; + case V4L2_PIX_FMT_YUYV: { + img_buf->planes[0].len = isp_vdev->format.sizeimage; + img_buf->planes[0].sys_addr = vaddr; + img_buf->planes[0].mc_addr = buf->gpu_addr; + + dev_dbg(isp_vdev->dev, "img_buf[0]: mc=0x%llx size=%u\n", + img_buf->planes[0].mc_addr, + img_buf->planes[0].len); + + img_buf->planes[1].len = 0; + img_buf->planes[2].len = 0; + } + break; + default: + dev_err(isp_vdev->dev, "%s|unsupported fmt=%u\n", + isp_vdev->vdev.name, isp_vdev->format.pixelformat); + return; + } + + if (isp_vdev->stream_started) + isp4sd_ioc_send_img_buf(isp_vdev->isp_sdev, img_buf); + + scoped_guard(mutex, &isp_vdev->buf_list_lock) + list_add_tail(&buf->list, &isp_vdev->buf_list); +} + +static int isp4vid_qops_start_streaming(struct vb2_queue *vq, + unsigned int count) +{ + struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vq); + struct isp4vid_capture_buffer *isp4vid_buf; + struct media_entity *entity; + struct v4l2_subdev *subdev; + struct media_pad *pad; + int ret = 0; + + isp_vdev->sequence = 0; + + ret = isp4sd_pwron_and_init(isp_vdev->isp_sdev); + if (ret) { + dev_err(isp_vdev->dev, "power up isp fail %d\n", ret); + goto release_buffers; + } + + entity = &isp_vdev->vdev.entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_pad_remote_pad_first(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + ret = v4l2_subdev_call(subdev, video, s_stream, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) { + dev_dbg(isp_vdev->dev, "fail start streaming: %s %d\n", + subdev->name, ret); + goto release_buffers; + } + } + + list_for_each_entry(isp4vid_buf, &isp_vdev->buf_list, list) + isp4sd_ioc_send_img_buf(isp_vdev->isp_sdev, + &isp4vid_buf->img_buf); + + isp_vdev->stream_started = true; + + return 0; + +release_buffers: + isp4vid_capture_return_all_buffers(isp_vdev, VB2_BUF_STATE_QUEUED); + return ret; +} + +static void isp4vid_qops_stop_streaming(struct vb2_queue *vq) +{ + struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vq); + struct media_entity *entity; + struct v4l2_subdev *subdev; + struct media_pad *pad; + int ret; + + entity = &isp_vdev->vdev.entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_pad_remote_pad_first(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + ret = v4l2_subdev_call(subdev, video, s_stream, 0); + + if (ret < 0 && ret != -ENOIOCTLCMD) + dev_dbg(isp_vdev->dev, "fail stop streaming: %s %d\n", + subdev->name, ret); + } + + isp_vdev->stream_started = false; + isp4sd_pwroff_and_deinit(isp_vdev->isp_sdev); + + /* Release all active buffers */ + isp4vid_capture_return_all_buffers(isp_vdev, VB2_BUF_STATE_ERROR); +} + +static int isp4vid_qops_buf_init(struct vb2_buffer *vb) +{ + struct isp4vid_capture_buffer *buf = + container_of(vb, struct isp4vid_capture_buffer, vb2.vb2_buf); + struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vb->vb2_queue); + void *mem_priv = vb->planes[0].mem_priv; + struct device *dev = isp_vdev->dev; + u64 gpu_addr; + void *bo; + int ret; + + if (vb->planes[0].dbuf) { + buf->dbuf = vb->planes[0].dbuf; + } else { + /* + * HAS_DMA is a Kconfig dependency so CONFIG_HAS_DMA is always + * defined when this driver is compiled. The #else branch is + * kept as a safeguard in case the dependency is ever removed. + */ +#ifdef CONFIG_HAS_DMA + buf->dbuf = vb2_vmalloc_memops.get_dmabuf(vb, mem_priv, 0); + if (IS_ERR_OR_NULL(buf->dbuf)) { + dev_err(dev, "fail to get dma buf\n"); + return -EINVAL; + } +#else + dev_err(dev, "get dmabuf fail -- CONFIG_HAS_DMA not defined\n"); + buf->dbuf = NULL; + return -EINVAL; +#endif + } + + /* create isp user BO and obtain gpu_addr */ + ret = isp_user_buffer_alloc(dev, buf->dbuf, &bo, &gpu_addr); + if (ret) { + dev_err(dev, "fail to create isp user BO\n"); + if (!vb->planes[0].dbuf) { + dma_buf_put(buf->dbuf); + buf->dbuf = NULL; + } + + return ret; + } + + buf->bo = bo; + buf->gpu_addr = gpu_addr; + return 0; +} + +static void isp4vid_qops_buf_cleanup(struct vb2_buffer *vb) +{ + struct isp4vid_capture_buffer *buf = + container_of(vb, struct isp4vid_capture_buffer, vb2.vb2_buf); + + if (buf->bo) { + isp_user_buffer_free(buf->bo); + buf->bo = NULL; + } + + /* + * Only put dmabufs we obtained ourselves via get_dmabuf, not ones + * provided by the framework for DMABUF import + */ + if (buf->dbuf && buf->dbuf != vb->planes[0].dbuf) + dma_buf_put(buf->dbuf); + + buf->dbuf = NULL; +} + +static const struct vb2_ops isp4vid_qops = { + .queue_setup = isp4vid_qops_queue_setup, + .buf_init = isp4vid_qops_buf_init, + .buf_cleanup = isp4vid_qops_buf_cleanup, + .start_streaming = isp4vid_qops_start_streaming, + .stop_streaming = isp4vid_qops_stop_streaming, + .buf_queue = isp4vid_qops_buffer_queue, +}; + +int isp4vid_dev_init(struct isp4vid_dev *isp_vdev, struct v4l2_subdev *isp_sd) +{ + const char *vdev_name = isp4vid_video_dev_name; + struct v4l2_device *v4l2_dev; + struct video_device *vdev; + struct vb2_queue *q; + int ret; + + if (!isp_vdev || !isp_sd || !isp_sd->v4l2_dev) + return -EINVAL; + + v4l2_dev = isp_sd->v4l2_dev; + vdev = &isp_vdev->vdev; + + isp_vdev->isp_sdev = isp_sd; + isp_vdev->dev = v4l2_dev->dev; + + /* Initialize the vb2_queue struct */ + mutex_init(&isp_vdev->vbq_lock); + q = &isp_vdev->vbq; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_DMABUF; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->buf_struct_size = sizeof(struct isp4vid_capture_buffer); + q->min_queued_buffers = 2; + q->ops = &isp4vid_qops; + q->drv_priv = isp_vdev; + q->mem_ops = &vb2_vmalloc_memops; + q->lock = &isp_vdev->vbq_lock; + q->dev = v4l2_dev->dev; + ret = vb2_queue_init(q); + if (ret) { + dev_err(v4l2_dev->dev, "vb2_queue_init error:%d\n", ret); + return ret; + } + + /* Initialize buffer list and its lock */ + mutex_init(&isp_vdev->buf_list_lock); + INIT_LIST_HEAD(&isp_vdev->buf_list); + + /* Set default frame format */ + isp_vdev->format = isp4vid_fmt_default; + isp_vdev->timeperframe = ISP4VID_ISP_TPF_DEFAULT; + v4l2_simplify_fraction(&isp_vdev->timeperframe.numerator, + &isp_vdev->timeperframe.denominator, 8, 333); + + ret = isp4vid_fill_buffer_size(&isp_vdev->format); + if (ret) { + dev_err(v4l2_dev->dev, "fail to fill buffer size: %d\n", ret); + goto err_release_vb2_queue; + } + + ret = isp4vid_set_fmt_2_isp(isp_sd, &isp_vdev->format); + if (ret) { + dev_err(v4l2_dev->dev, "fail init format :%d\n", ret); + goto err_release_vb2_queue; + } + + /* Initialize the video_device struct */ + isp_vdev->vdev.entity.name = vdev_name; + isp_vdev->vdev.entity.function = MEDIA_ENT_F_IO_V4L; + isp_vdev->vdev_pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&isp_vdev->vdev.entity, 1, + &isp_vdev->vdev_pad); + + if (ret) { + dev_err(v4l2_dev->dev, "init media entity pad fail:%d\n", ret); + goto err_release_vb2_queue; + } + + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING | V4L2_CAP_IO_MC; + vdev->entity.ops = &isp4vid_vdev_ent_ops; + vdev->release = video_device_release_empty; + vdev->fops = &isp4vid_vdev_fops; + vdev->ioctl_ops = &isp4vid_vdev_ioctl_ops; + vdev->lock = NULL; + vdev->queue = q; + vdev->v4l2_dev = v4l2_dev; + vdev->vfl_dir = VFL_DIR_RX; + strscpy(vdev->name, vdev_name, sizeof(vdev->name)); + video_set_drvdata(vdev, isp_vdev); + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) { + dev_err(v4l2_dev->dev, "register video device fail:%d\n", ret); + goto err_entity_cleanup; + } + + return 0; + +err_entity_cleanup: + media_entity_cleanup(&isp_vdev->vdev.entity); +err_release_vb2_queue: + vb2_queue_release(q); + return ret; +} + +void isp4vid_dev_deinit(struct isp4vid_dev *isp_vdev) +{ + vb2_video_unregister_device(&isp_vdev->vdev); +} diff --git a/drivers/media/platform/amd/isp4/isp4_video.h b/drivers/media/platform/amd/isp4/isp4_video.h new file mode 100644 index 000000000000..c66451e26166 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_video.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _ISP4_VIDEO_H_ +#define _ISP4_VIDEO_H_ + +#include <media/v4l2-dev.h> +#include <media/videobuf2-memops.h> + +#include "isp4_interface.h" + +struct isp4vid_capture_buffer { + /* + * struct vb2_v4l2_buffer must be the first element + * the videobuf2 framework will allocate this struct based on + * buf_struct_size and use the first sizeof(struct vb2_buffer) bytes of + * memory as a vb2_buffer + */ + struct vb2_v4l2_buffer vb2; + struct isp4if_img_buf_info img_buf; + struct list_head list; + struct dma_buf *dbuf; + void *bo; + u64 gpu_addr; +}; + +struct isp4vid_dev { + struct video_device vdev; + struct media_pad vdev_pad; + struct v4l2_pix_format format; + + /* mutex that protects vbq */ + struct mutex vbq_lock; + struct vb2_queue vbq; + + /* mutex that protects buf_list */ + struct mutex buf_list_lock; + struct list_head buf_list; + + u32 sequence; + bool stream_started; + + struct device *dev; + struct v4l2_subdev *isp_sdev; + struct v4l2_fract timeperframe; +}; + +int isp4vid_dev_init(struct isp4vid_dev *isp_vdev, struct v4l2_subdev *isp_sd); + +void isp4vid_dev_deinit(struct isp4vid_dev *isp_vdev); + +void isp4vid_handle_frame_done(struct isp4vid_dev *isp_vdev, + const struct isp4if_img_buf_info *img_buf); + +#endif /* _ISP4_VIDEO_H_ */ diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c b/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c index 6f9ca7a7dd88..aec3eed0e443 100644 --- a/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c +++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c @@ -104,6 +104,8 @@ static void c3_isp_params_awb_wt(struct c3_isp_device *isp, c3_isp_write(isp, ISP_AWB_BLK_WT_ADDR, 0); zones_num = cfg->horiz_zones_num * cfg->vert_zones_num; + if (zones_num > C3_ISP_AWB_MAX_ZONES) + zones_num = C3_ISP_AWB_MAX_ZONES; /* Need to write 8 weights at once */ for (i = 0; i < zones_num / 8; i++) { @@ -220,6 +222,8 @@ static void c3_isp_params_ae_wt(struct c3_isp_device *isp, c3_isp_write(isp, ISP_AE_BLK_WT_ADDR, 0); zones_num = cfg->horiz_zones_num * cfg->vert_zones_num; + if (zones_num > C3_ISP_AE_MAX_ZONES) + zones_num = C3_ISP_AE_MAX_ZONES; /* Need to write 8 weights at once */ for (i = 0; i < zones_num / 8; i++) { diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-core.c b/drivers/media/platform/arm/mali-c55/mali-c55-core.c index c1a562cd214e..ee4a4267415e 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-core.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-core.c @@ -458,7 +458,7 @@ static int mali_c55_media_frameworks_init(struct mali_c55 *mali_c55) if (ret) { dev_err(mali_c55->dev, "failed to register V4L2 device\n"); goto err_unregister_media_device; - }; + } mali_c55->notifier.ops = &mali_c55_notifier_ops; v4l2_async_nf_init(&mali_c55->notifier, &mali_c55->v4l2_dev); @@ -806,8 +806,10 @@ static int mali_c55_probe(struct platform_device *pdev) vb2_dma_contig_set_max_seg_size(dev, UINT_MAX); ret = __mali_c55_power_on(mali_c55); - if (ret) - return dev_err_probe(dev, ret, "failed to power on\n"); + if (ret) { + dev_err_probe(dev, ret, "failed to power on\n"); + goto err_release_mem; + } ret = mali_c55_check_hwcfg(mali_c55); if (ret) @@ -826,14 +828,13 @@ static int mali_c55_probe(struct platform_device *pdev) ret = mali_c55_media_frameworks_init(mali_c55); if (ret) - goto err_free_context_registers; + goto err_pm_runtime_disable; pm_runtime_idle(&pdev->dev); mali_c55->irqnum = platform_get_irq(pdev, 0); if (mali_c55->irqnum < 0) { ret = mali_c55->irqnum; - dev_err(dev, "failed to get interrupt\n"); goto err_deinit_media_frameworks; } @@ -841,11 +842,14 @@ static int mali_c55_probe(struct platform_device *pdev) err_deinit_media_frameworks: mali_c55_media_frameworks_deinit(mali_c55); +err_pm_runtime_disable: + pm_runtime_set_suspended(&pdev->dev); pm_runtime_disable(&pdev->dev); -err_free_context_registers: kfree(mali_c55->context.registers); err_power_off: __mali_c55_power_off(mali_c55); +err_release_mem: + of_reserved_mem_device_release(dev); return ret; } @@ -854,8 +858,14 @@ static void mali_c55_remove(struct platform_device *pdev) { struct mali_c55 *mali_c55 = platform_get_drvdata(pdev); - kfree(mali_c55->context.registers); mali_c55_media_frameworks_deinit(mali_c55); + if (!pm_runtime_suspended(&pdev->dev)) { + __mali_c55_power_off(mali_c55); + pm_runtime_set_suspended(&pdev->dev); + } + pm_runtime_disable(&pdev->dev); + kfree(mali_c55->context.registers); + of_reserved_mem_device_release(&pdev->dev); } static const struct of_device_id mali_c55_of_match[] = { diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-isp.c b/drivers/media/platform/arm/mali-c55/mali-c55-isp.c index 4c0fd1ec741c..e128adf6ee37 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-isp.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-isp.c @@ -333,6 +333,13 @@ static int mali_c55_isp_enable_streams(struct v4l2_subdev *sd, sink_pad = &isp->pads[MALI_C55_ISP_PAD_SINK_VIDEO]; isp->remote_src = media_pad_remote_pad_unique(sink_pad); + if (IS_ERR(isp->remote_src)) { + ret = PTR_ERR(isp->remote_src); + dev_err(mali_c55->dev, "Failed to get remote source pad: %d\n", ret); + isp->remote_src = NULL; + return ret; + } + src_sd = media_entity_to_v4l2_subdev(isp->remote_src->entity); isp->frame_sequence = 0; @@ -583,6 +590,7 @@ int mali_c55_register_isp(struct mali_c55 *mali_c55) sd->entity.ops = &mali_c55_isp_media_ops; sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_ISP; sd->internal_ops = &mali_c55_isp_internal_ops; + sd->dev = mali_c55->dev; strscpy(sd->name, MALI_C55_DRIVER_NAME " isp", sizeof(sd->name)); isp->pads[MALI_C55_ISP_PAD_SINK_VIDEO].flags = MEDIA_PAD_FL_SINK | diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c b/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c index a8d739af74b6..c4f46651dcee 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c @@ -1070,6 +1070,7 @@ static int mali_c55_register_resizer(struct mali_c55 *mali_c55, sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER; sd->internal_ops = &mali_c55_resizer_internal_ops; + sd->dev = mali_c55->dev; rsz->pads[MALI_C55_RSZ_SINK_PAD].flags = MEDIA_PAD_FL_SINK; rsz->pads[MALI_C55_RSZ_SOURCE_PAD].flags = MEDIA_PAD_FL_SOURCE; diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-tpg.c b/drivers/media/platform/arm/mali-c55/mali-c55-tpg.c index 1af5d2759a83..894f4cf377af 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-tpg.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-tpg.c @@ -370,6 +370,7 @@ int mali_c55_register_tpg(struct mali_c55 *mali_c55) sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; sd->internal_ops = &mali_c55_tpg_internal_ops; + sd->dev = mali_c55->dev; strscpy(sd->name, MALI_C55_DRIVER_NAME " tpg", sizeof(sd->name)); pad->flags = MEDIA_PAD_FL_SOURCE; diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platform/aspeed/aspeed-video.c index 41cb96f60110..a292275f6b7b 100644 --- a/drivers/media/platform/aspeed/aspeed-video.c +++ b/drivers/media/platform/aspeed/aspeed-video.c @@ -2343,6 +2343,7 @@ static int aspeed_video_probe(struct platform_device *pdev) rc = aspeed_video_setup_video(video); if (rc) { aspeed_video_free_buf(video, &video->jpeg); + of_reserved_mem_device_release(&pdev->dev); clk_unprepare(video->vclk); clk_unprepare(video->eclk); return rc; diff --git a/drivers/media/platform/cadence/Kconfig b/drivers/media/platform/cadence/Kconfig index 1aa608c00dbc..ea85ef82760e 100644 --- a/drivers/media/platform/cadence/Kconfig +++ b/drivers/media/platform/cadence/Kconfig @@ -5,6 +5,7 @@ comment "Cadence media platform drivers" config VIDEO_CADENCE_CSI2RX tristate "Cadence MIPI-CSI2 RX Controller" depends on VIDEO_DEV + depends on PM select MEDIA_CONTROLLER select 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 cde690c6fdee..1ff2d8f78d5b 100644 --- a/drivers/media/platform/cadence/cdns-csi2rx.c +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -125,12 +125,6 @@ struct csi2rx_priv { unsigned int count; int error_irq; - /* - * Used to prevent race conditions between multiple, - * concurrent calls to start and stop. - */ - struct mutex lock; - void __iomem *base; struct clk *sys_clk; struct clk *p_clk; @@ -141,6 +135,7 @@ struct csi2rx_priv { struct phy *dphy; u8 num_pixels[CSI2RX_STREAMS_MAX]; + u32 vc_select[CSI2RX_STREAMS_MAX]; u8 lanes[CSI2RX_LANES_MAX]; u8 num_lanes; u8 max_lanes; @@ -235,6 +230,21 @@ static const struct csi2rx_fmt *csi2rx_get_fmt_by_code(u32 code) return NULL; } +static int csi2rx_get_frame_desc_from_source(struct csi2rx_priv *csi2rx, + struct v4l2_mbus_frame_desc *fd) +{ + struct media_pad *remote_pad; + + remote_pad = media_entity_remote_source_pad_unique(&csi2rx->subdev.entity); + if (IS_ERR(remote_pad)) { + dev_err(csi2rx->dev, "No remote pad found for sink\n"); + return PTR_ERR(remote_pad); + } + + return v4l2_subdev_call(csi2rx->source_subdev, pad, get_frame_desc, + remote_pad->index, fd); +} + static inline struct csi2rx_priv *v4l2_subdev_to_csi2rx(struct v4l2_subdev *subdev) { @@ -264,29 +274,46 @@ static void csi2rx_reset(struct csi2rx_priv *csi2rx) static int csi2rx_configure_ext_dphy(struct csi2rx_priv *csi2rx) { - struct media_pad *src_pad = - &csi2rx->source_subdev->entity.pads[csi2rx->source_pad]; union phy_configure_opts opts = { }; struct phy_configure_opts_mipi_dphy *cfg = &opts.mipi_dphy; - struct v4l2_subdev_format sd_fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .pad = CSI2RX_PAD_SINK, - }; + struct v4l2_mbus_framefmt *framefmt; + struct v4l2_subdev_state *state; const struct csi2rx_fmt *fmt; + struct v4l2_subdev_route *route; + int source_pad = csi2rx->source_pad; + struct media_pad *pad = &csi2rx->source_subdev->entity.pads[source_pad]; s64 link_freq; int ret; + u32 bpp; - ret = v4l2_subdev_call_state_active(&csi2rx->subdev, pad, get_fmt, - &sd_fmt); - if (ret < 0) - return ret; + state = v4l2_subdev_get_locked_active_state(&csi2rx->subdev); + + /* + * For multi-stream transmitters there is no single pixel rate. + * + * In multistream usecase pass bpp as 0 so that v4l2_get_link_freq() + * returns an error if it falls back to V4L2_CID_PIXEL_RATE. + */ + if (state->routing.num_routes > 1) { + bpp = 0; + } else { + route = &state->routing.routes[0]; + framefmt = v4l2_subdev_state_get_format(state, CSI2RX_PAD_SINK, + route->sink_stream); + if (!framefmt) { + dev_err(csi2rx->dev, "Did not find active sink format\n"); + return -EINVAL; + } - fmt = csi2rx_get_fmt_by_code(sd_fmt.format.code); + fmt = csi2rx_get_fmt_by_code(framefmt->code); + bpp = fmt->bpp; + } - link_freq = v4l2_get_link_freq(src_pad, - fmt->bpp, 2 * csi2rx->num_lanes); - if (link_freq < 0) + link_freq = v4l2_get_link_freq(pad, bpp, 2 * csi2rx->num_lanes); + if (link_freq < 0) { + dev_err(csi2rx->dev, "Unable to calculate link frequency\n"); return link_freq; + } ret = phy_mipi_dphy_get_default_config_for_hsclk(link_freq, csi2rx->num_lanes, cfg); @@ -313,11 +340,6 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) u32 reg; int ret; - ret = clk_prepare_enable(csi2rx->p_clk); - if (ret) - return ret; - - reset_control_deassert(csi2rx->p_rst); csi2rx_reset(csi2rx); if (csi2rx->error_irq >= 0) @@ -358,7 +380,7 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) if (ret) { dev_err(csi2rx->dev, "Failed to configure external DPHY: %d\n", ret); - goto err_disable_pclk; + return ret; } } @@ -373,58 +395,20 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) * hence the reference counting. */ for (i = 0; i < csi2rx->max_streams; i++) { - ret = clk_prepare_enable(csi2rx->pixel_clk[i]); - if (ret) - goto err_disable_pixclk; - - reset_control_deassert(csi2rx->pixel_rst[i]); - writel(CSI2RX_STREAM_CFG_FIFO_MODE_LARGE_BUF | FIELD_PREP(CSI2RX_STREAM_CFG_NUM_PIXELS_MASK, csi2rx->num_pixels[i]), csi2rx->base + CSI2RX_STREAM_CFG_REG(i)); - /* - * Enable one virtual channel. When multiple virtual channels - * are supported this will have to be changed. - */ - writel(CSI2RX_STREAM_DATA_CFG_VC_SELECT(0), + writel(csi2rx->vc_select[i], csi2rx->base + CSI2RX_STREAM_DATA_CFG_REG(i)); writel(CSI2RX_STREAM_CTRL_START, csi2rx->base + CSI2RX_STREAM_CTRL_REG(i)); } - ret = clk_prepare_enable(csi2rx->sys_clk); - if (ret) - goto err_disable_pixclk; - - reset_control_deassert(csi2rx->sys_rst); - - ret = v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, true); - if (ret) - goto err_disable_sysclk; - - clk_disable_unprepare(csi2rx->p_clk); return 0; - -err_disable_sysclk: - clk_disable_unprepare(csi2rx->sys_clk); -err_disable_pixclk: - for (; i > 0; i--) { - reset_control_assert(csi2rx->pixel_rst[i - 1]); - clk_disable_unprepare(csi2rx->pixel_clk[i - 1]); - } - - if (csi2rx->dphy) { - writel(0, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG); - phy_power_off(csi2rx->dphy); - } -err_disable_pclk: - clk_disable_unprepare(csi2rx->p_clk); - - return ret; } static void csi2rx_stop(struct csi2rx_priv *csi2rx) @@ -433,10 +417,6 @@ static void csi2rx_stop(struct csi2rx_priv *csi2rx) u32 val; int ret; - clk_prepare_enable(csi2rx->p_clk); - reset_control_assert(csi2rx->sys_rst); - clk_disable_unprepare(csi2rx->sys_clk); - writel(0, csi2rx->base + CSI2RX_ERROR_IRQS_MASK_REG); for (i = 0; i < csi2rx->max_streams; i++) { @@ -451,17 +431,8 @@ static void csi2rx_stop(struct csi2rx_priv *csi2rx) if (ret) dev_warn(csi2rx->dev, "Failed to stop streaming on pad%u\n", i); - - reset_control_assert(csi2rx->pixel_rst[i]); - clk_disable_unprepare(csi2rx->pixel_clk[i]); } - reset_control_assert(csi2rx->p_rst); - clk_disable_unprepare(csi2rx->p_clk); - - if (v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, false)) - dev_warn(csi2rx->dev, "Couldn't disable our subdev\n"); - if (csi2rx->dphy) { writel(0, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG); @@ -485,40 +456,117 @@ static int csi2rx_log_status(struct v4l2_subdev *sd) return 0; } -static int csi2rx_s_stream(struct v4l2_subdev *subdev, int enable) +static void csi2rx_update_vc_select(struct csi2rx_priv *csi2rx, + struct v4l2_subdev_state *state) { - struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev); - int ret = 0; + struct v4l2_mbus_frame_desc fd = {0}; + struct v4l2_subdev_route *route; + unsigned int i; + int ret; + + ret = csi2rx_get_frame_desc_from_source(csi2rx, &fd); + if (ret || fd.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { + dev_dbg(csi2rx->dev, + "Failed to get source frame desc, allowing only VC=0\n"); + for (i = 0; i < CSI2RX_STREAMS_MAX; i++) + csi2rx->vc_select[i] = CSI2RX_STREAM_DATA_CFG_VC_SELECT(0); + return; + } + + /* If source provides per-stream VC info, use it to filter by VC */ + memset(csi2rx->vc_select, 0, sizeof(csi2rx->vc_select)); + + for_each_active_route(&state->routing, route) { + u32 cdns_stream = route->source_pad - CSI2RX_PAD_SOURCE_STREAM0; + + for (i = 0; i < fd.num_entries; i++) { + if (fd.entry[i].stream != route->sink_stream) + continue; - mutex_lock(&csi2rx->lock); - - if (enable) { - /* - * If we're not the first users, there's no need to - * enable the whole controller. - */ - if (!csi2rx->count) { - ret = csi2rx_start(csi2rx); - if (ret) - goto out; + csi2rx->vc_select[cdns_stream] |= + CSI2RX_STREAM_DATA_CFG_VC_SELECT(fd.entry[i].bus.csi2.vc); } + } +} - csi2rx->count++; - } else { - csi2rx->count--; +static int csi2rx_enable_streams(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev); + u64 sink_streams; + int ret; - /* - * Let the last user turn off the lights. - */ - if (!csi2rx->count) - csi2rx_stop(csi2rx); + sink_streams = v4l2_subdev_state_xlate_streams(state, pad, + CSI2RX_PAD_SINK, + &streams_mask); + + /* + * If we're not the first users, there's no need to + * enable the whole controller. + */ + if (!csi2rx->count) { + ret = pm_runtime_resume_and_get(csi2rx->dev); + if (ret < 0) + goto err; + + csi2rx_update_vc_select(csi2rx, state); + + ret = csi2rx_start(csi2rx); + if (ret) + goto err_put_pm; } -out: - mutex_unlock(&csi2rx->lock); + /* Start streaming on the source */ + ret = v4l2_subdev_enable_streams(csi2rx->source_subdev, csi2rx->source_pad, + sink_streams); + if (ret) { + dev_err(csi2rx->dev, + "Failed to start streams %#llx on subdev\n", + sink_streams); + goto err_stop_csi; + } + + csi2rx->count++; + return 0; + +err_stop_csi: + if (!csi2rx->count) + csi2rx_stop(csi2rx); +err_put_pm: + if (!csi2rx->count) + pm_runtime_put(csi2rx->dev); +err: return ret; } +static int csi2rx_disable_streams(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev); + u64 sink_streams; + + sink_streams = v4l2_subdev_state_xlate_streams(state, pad, + CSI2RX_PAD_SINK, + &streams_mask); + + if (v4l2_subdev_disable_streams(csi2rx->source_subdev, + csi2rx->source_pad, sink_streams)) { + dev_err(csi2rx->dev, "Couldn't disable our subdev\n"); + } + + csi2rx->count--; + + /* Let the last user turn off the lights. */ + if (!csi2rx->count) { + csi2rx_stop(csi2rx); + pm_runtime_put(csi2rx->dev); + } + + return 0; +} + static int csi2rx_enum_mbus_code(struct v4l2_subdev *subdev, struct v4l2_subdev_state *state, struct v4l2_subdev_mbus_code_enum *code_enum) @@ -531,12 +579,53 @@ static int csi2rx_enum_mbus_code(struct v4l2_subdev *subdev, return 0; } +static int _csi2rx_set_routing(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing) +{ + static const struct v4l2_mbus_framefmt format = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_LIM_RANGE, + .xfer_func = V4L2_XFER_FUNC_SRGB, + }; + int ret; + + ret = v4l2_subdev_routing_validate(subdev, routing, + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); + if (ret) + return ret; + + return v4l2_subdev_set_routing_with_fmt(subdev, state, routing, &format); +} + +static int csi2rx_set_routing(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev); + int ret; + + if (which == V4L2_SUBDEV_FORMAT_ACTIVE && csi2rx->count) + return -EBUSY; + + ret = _csi2rx_set_routing(subdev, state, routing); + if (ret) + return ret; + + return 0; +} + static int csi2rx_set_fmt(struct v4l2_subdev *subdev, struct v4l2_subdev_state *state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *fmt; - unsigned int i; /* No transcoding, source and sink formats must match. */ if (format->pad != CSI2RX_PAD_SINK) @@ -548,14 +637,16 @@ static int csi2rx_set_fmt(struct v4l2_subdev *subdev, format->format.field = V4L2_FIELD_NONE; /* Set sink format */ - fmt = v4l2_subdev_state_get_format(state, format->pad); + fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); *fmt = format->format; - /* Propagate to source formats */ - for (i = CSI2RX_PAD_SOURCE_STREAM0; i < CSI2RX_PAD_MAX; i++) { - fmt = v4l2_subdev_state_get_format(state, i); - *fmt = format->format; - } + /* Propagate to source format */ + fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, + format->stream); + if (!fmt) + return -EINVAL; + + *fmt = format->format; return 0; } @@ -563,21 +654,22 @@ static int csi2rx_set_fmt(struct v4l2_subdev *subdev, static int csi2rx_init_state(struct v4l2_subdev *subdev, struct v4l2_subdev_state *state) { - struct v4l2_subdev_format format = { - .pad = CSI2RX_PAD_SINK, - .format = { - .width = 640, - .height = 480, - .code = MEDIA_BUS_FMT_UYVY8_1X16, - .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 v4l2_subdev_route routes[] = { + { + .sink_pad = CSI2RX_PAD_SINK, + .sink_stream = 0, + .source_pad = CSI2RX_PAD_SOURCE_STREAM0, + .source_stream = 0, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, }, }; - return csi2rx_set_fmt(subdev, state, &format); + struct v4l2_subdev_krouting routing = { + .num_routes = ARRAY_SIZE(routes), + .routes = routes, + }; + + return _csi2rx_set_routing(subdev, state, &routing); } int cdns_csi2rx_negotiate_ppc(struct v4l2_subdev *subdev, unsigned int pad, @@ -585,36 +677,55 @@ int cdns_csi2rx_negotiate_ppc(struct v4l2_subdev *subdev, unsigned int pad, { struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev); const struct csi2rx_fmt *csi_fmt; + struct v4l2_subdev_route *route; struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *fmt; + int ret = 0; if (!ppc || pad < CSI2RX_PAD_SOURCE_STREAM0 || pad >= CSI2RX_PAD_MAX) return -EINVAL; state = v4l2_subdev_lock_and_get_active_state(subdev); - fmt = v4l2_subdev_state_get_format(state, pad); - csi_fmt = csi2rx_get_fmt_by_code(fmt->code); + /* Check all streams on requested pad */ + for_each_active_route(&state->routing, route) { + if (route->source_pad != pad) + continue; + + fmt = v4l2_subdev_state_get_format(state, route->source_pad, + route->source_stream); + if (!fmt) { + ret = -EPIPE; + *ppc = 1; + break; + } - /* Reduce requested PPC if it is too high */ - *ppc = min(*ppc, csi_fmt->max_pixels); + csi_fmt = csi2rx_get_fmt_by_code(fmt->code); + if (!csi_fmt) { + ret = -EINVAL; + *ppc = 1; + break; + } + /* Reduce requested PPC if it is too high for this stream */ + *ppc = min(*ppc, csi_fmt->max_pixels); + } v4l2_subdev_unlock_state(state); csi2rx->num_pixels[pad - CSI2RX_PAD_SOURCE_STREAM0] = CSI2RX_STREAM_CFG_NUM_PIXELS(*ppc); - return 0; + return ret; } EXPORT_SYMBOL_FOR_MODULES(cdns_csi2rx_negotiate_ppc, "j721e-csi2rx"); static const struct v4l2_subdev_pad_ops csi2rx_pad_ops = { - .enum_mbus_code = csi2rx_enum_mbus_code, - .get_fmt = v4l2_subdev_get_fmt, - .set_fmt = csi2rx_set_fmt, -}; - -static const struct v4l2_subdev_video_ops csi2rx_video_ops = { - .s_stream = csi2rx_s_stream, + .enum_mbus_code = csi2rx_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = csi2rx_set_fmt, + .get_frame_desc = v4l2_subdev_get_frame_desc_passthrough, + .set_routing = csi2rx_set_routing, + .enable_streams = csi2rx_enable_streams, + .disable_streams = csi2rx_disable_streams, }; static const struct v4l2_subdev_core_ops csi2rx_core_ops = { @@ -623,7 +734,6 @@ static const struct v4l2_subdev_core_ops csi2rx_core_ops = { static const struct v4l2_subdev_ops csi2rx_subdev_ops = { .core = &csi2rx_core_ops, - .video = &csi2rx_video_ops, .pad = &csi2rx_pad_ops, }; @@ -634,6 +744,7 @@ static const struct v4l2_subdev_internal_ops csi2rx_internal_ops = { static const struct media_entity_operations csi2rx_media_ops = { .link_validate = v4l2_subdev_link_validate, .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1, + .has_pad_interdep = v4l2_subdev_has_pad_interdep, }; static int csi2rx_async_bound(struct v4l2_async_notifier *notifier, @@ -829,7 +940,6 @@ static int csi2rx_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, csi2rx); csi2rx->dev = &pdev->dev; - mutex_init(&csi2rx->lock); ret = csi2rx_get_resources(csi2rx, pdev); if (ret) @@ -852,7 +962,8 @@ static int csi2rx_probe(struct platform_device *pdev) csi2rx->pads[CSI2RX_PAD_SINK].flags = MEDIA_PAD_FL_SINK; for (i = CSI2RX_PAD_SOURCE_STREAM0; i < CSI2RX_PAD_MAX; i++) csi2rx->pads[i].flags = MEDIA_PAD_FL_SOURCE; - csi2rx->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + csi2rx->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_STREAMS; csi2rx->subdev.entity.ops = &csi2rx_media_ops; ret = media_entity_pads_init(&csi2rx->subdev.entity, CSI2RX_PAD_MAX, @@ -879,6 +990,7 @@ static int csi2rx_probe(struct platform_device *pdev) if (ret) goto err_cleanup; + pm_runtime_enable(csi2rx->dev); ret = v4l2_async_register_subdev(&csi2rx->subdev); if (ret < 0) goto err_free_state; @@ -893,6 +1005,7 @@ static int csi2rx_probe(struct platform_device *pdev) err_free_state: v4l2_subdev_cleanup(&csi2rx->subdev); + pm_runtime_disable(csi2rx->dev); err_cleanup: v4l2_async_nf_unregister(&csi2rx->notifier); v4l2_async_nf_cleanup(&csi2rx->notifier); @@ -911,9 +1024,72 @@ static void csi2rx_remove(struct platform_device *pdev) v4l2_async_unregister_subdev(&csi2rx->subdev); v4l2_subdev_cleanup(&csi2rx->subdev); media_entity_cleanup(&csi2rx->subdev.entity); + pm_runtime_disable(csi2rx->dev); kfree(csi2rx); } +static int csi2rx_runtime_suspend(struct device *dev) +{ + struct csi2rx_priv *csi2rx = dev_get_drvdata(dev); + + reset_control_assert(csi2rx->sys_rst); + clk_disable_unprepare(csi2rx->sys_clk); + + for (unsigned int i = 0; i < csi2rx->max_streams; i++) { + reset_control_assert(csi2rx->pixel_rst[i]); + clk_disable_unprepare(csi2rx->pixel_clk[i]); + } + + reset_control_assert(csi2rx->p_rst); + clk_disable_unprepare(csi2rx->p_clk); + + return 0; +} + +static int csi2rx_runtime_resume(struct device *dev) +{ + struct csi2rx_priv *csi2rx = dev_get_drvdata(dev); + unsigned int i; + int ret; + + ret = clk_prepare_enable(csi2rx->p_clk); + if (ret) + return ret; + + reset_control_deassert(csi2rx->p_rst); + + for (i = 0; i < csi2rx->max_streams; i++) { + ret = clk_prepare_enable(csi2rx->pixel_clk[i]); + if (ret) + goto err_disable_pixclk; + + reset_control_deassert(csi2rx->pixel_rst[i]); + } + + ret = clk_prepare_enable(csi2rx->sys_clk); + if (ret) + goto err_disable_pixclk; + + reset_control_deassert(csi2rx->sys_rst); + + return 0; + +err_disable_pixclk: + while (i--) { + reset_control_assert(csi2rx->pixel_rst[i]); + clk_disable_unprepare(csi2rx->pixel_clk[i]); + } + + reset_control_assert(csi2rx->p_rst); + clk_disable_unprepare(csi2rx->p_clk); + + return ret; +} + +static const struct dev_pm_ops csi2rx_pm_ops = { + RUNTIME_PM_OPS(csi2rx_runtime_suspend, csi2rx_runtime_resume, NULL) +}; + static const struct of_device_id csi2rx_of_table[] = { { .compatible = "starfive,jh7110-csi2rx" }, { .compatible = "cdns,csi2rx" }, @@ -928,6 +1104,7 @@ static struct platform_driver csi2rx_driver = { .driver = { .name = "cdns-csi2rx", .of_match_table = csi2rx_of_table, + .pm = &csi2rx_pm_ops, }, }; module_platform_driver(csi2rx_driver); diff --git a/drivers/media/platform/chips-media/wave5/wave5-helper.c b/drivers/media/platform/chips-media/wave5/wave5-helper.c index 53a0ac068c2e..c3d34be833ff 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-helper.c +++ b/drivers/media/platform/chips-media/wave5/wave5-helper.c @@ -68,7 +68,6 @@ int wave5_vpu_release_device(struct file *filp, int ret = 0; unsigned long flags; - v4l2_m2m_ctx_release(inst->v4l2_fh.m2m_ctx); /* * To prevent Null reference exception, the existing irq handler were * separated to two modules. @@ -89,6 +88,9 @@ int wave5_vpu_release_device(struct file *filp, list_del_init(&inst->list); spin_unlock_irqrestore(&inst->dev->irq_spinlock, flags); mutex_unlock(&inst->dev->irq_lock); + + v4l2_m2m_ctx_release(inst->v4l2_fh.m2m_ctx); + if (inst->state != VPU_INST_STATE_NONE) { u32 fail_res; diff --git a/drivers/media/platform/chips-media/wave5/wave5-helper.h b/drivers/media/platform/chips-media/wave5/wave5-helper.h index d61fdbda359d..e6f241012c3b 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-helper.h +++ b/drivers/media/platform/chips-media/wave5/wave5-helper.h @@ -11,7 +11,7 @@ #include "wave5-vpu.h" #define FMT_TYPES 2 -#define MAX_FMTS 12 +#define MAX_FMTS 16 const char *state_to_str(enum vpu_instance_state state); void wave5_cleanup_instance(struct vpu_instance *inst, struct file *filp); diff --git a/drivers/media/platform/chips-media/wave5/wave5-hw.c b/drivers/media/platform/chips-media/wave5/wave5-hw.c index 687ce6ccf3ae..2392bce8d840 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-hw.c +++ b/drivers/media/platform/chips-media/wave5/wave5-hw.c @@ -49,6 +49,7 @@ #define FASTIO_ADDRESS_MASK GENMASK(15, 0) #define SEQ_PARAM_PROFILE_MASK GENMASK(30, 24) +#define SEQ_BG_PARAM_REG_DATA 0x3800410 static void _wave5_print_reg_err(struct vpu_device *vpu_dev, u32 reg_fail_reason, const char *func); @@ -1762,6 +1763,9 @@ int wave5_vpu_enc_init_seq(struct vpu_instance *inst) (p_param->skip_intra_trans << 25) | (p_param->strong_intra_smooth_enable << 27) | (p_param->en_still_picture << 30); + else if (inst->std == W_AVC_ENC) + reg_val |= (p_param->constraint_set1_flag << 29); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_SPS_PARAM, reg_val); reg_val = (p_param->lossless_enable) | @@ -1838,7 +1842,8 @@ int wave5_vpu_enc_init_seq(struct vpu_instance *inst) vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_BIT_RATIO_LAYER_4_7, 0); vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_ROT_PARAM, rot_mir_mode); - vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_BG_PARAM, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_BG_PARAM, + SEQ_BG_PARAM_REG_DATA | p_param->bg_detection); vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CUSTOM_LAMBDA_ADDR, 0); vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CONF_WIN_TOP_BOT, p_param->conf_win_bot << 16 | p_param->conf_win_top); diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c index d419076d7052..bb2ba9204a83 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c @@ -283,10 +283,23 @@ static void send_eos_event(struct vpu_instance *inst) inst->sent_eos = true; } +static void wave5_update_min_bufs_ctrl(struct vpu_instance *inst, u32 fbc_buf_count) +{ + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + struct v4l2_ctrl *ctrl; + + if (!fbc_buf_count || fbc_buf_count == v4l2_m2m_num_dst_bufs_ready(m2m_ctx)) + return; + + ctrl = v4l2_ctrl_find(&inst->v4l2_ctrl_hdl, + V4L2_CID_MIN_BUFFERS_FOR_CAPTURE); + if (ctrl) + v4l2_ctrl_s_ctrl(ctrl, fbc_buf_count); +} + static int handle_dynamic_resolution_change(struct vpu_instance *inst) { struct v4l2_fh *fh = &inst->v4l2_fh; - struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; static const struct v4l2_event vpu_event_src_ch = { .type = V4L2_EVENT_SOURCE_CHANGE, @@ -305,14 +318,6 @@ static int handle_dynamic_resolution_change(struct vpu_instance *inst) inst->needs_reallocation = true; inst->fbc_buf_count = initial_info->min_frame_buffer_count + 1; - if (inst->fbc_buf_count != v4l2_m2m_num_dst_bufs_ready(m2m_ctx)) { - struct v4l2_ctrl *ctrl; - - ctrl = v4l2_ctrl_find(&inst->v4l2_ctrl_hdl, - V4L2_CID_MIN_BUFFERS_FOR_CAPTURE); - if (ctrl) - v4l2_ctrl_s_ctrl(ctrl, inst->fbc_buf_count); - } if (p_dec_info->initial_info_obtained) { const struct vpu_format *vpu_fmt; @@ -439,19 +444,24 @@ static void wave5_vpu_dec_finish_decode(struct vpu_instance *inst) if ((dec_info.index_frame_display == DISPLAY_IDX_FLAG_SEQ_END || dec_info.sequence_changed)) { unsigned long flags; + u32 fbc_buf_count = 0; spin_lock_irqsave(&inst->state_spinlock, flags); if (!v4l2_m2m_has_stopped(m2m_ctx)) { switch_state(inst, VPU_INST_STATE_STOP); - if (dec_info.sequence_changed) + if (dec_info.sequence_changed) { handle_dynamic_resolution_change(inst); - else + fbc_buf_count = inst->fbc_buf_count; + } else { send_eos_event(inst); + } flag_last_buffer_done(inst); } spin_unlock_irqrestore(&inst->state_spinlock, flags); + + wave5_update_min_bufs_ctrl(inst, fbc_buf_count); } if (inst->sent_eos && @@ -1592,8 +1602,9 @@ static const struct vpu_instance_ops wave5_vpu_dec_inst_ops = { static int initialize_sequence(struct vpu_instance *inst) { struct dec_initial_info initial_info; - int ret = 0; unsigned long flags; + u32 fbc_buf_count; + int ret = 0; memset(&initial_info, 0, sizeof(struct dec_initial_info)); @@ -1617,8 +1628,11 @@ static int initialize_sequence(struct vpu_instance *inst) spin_lock_irqsave(&inst->state_spinlock, flags); handle_dynamic_resolution_change(inst); + fbc_buf_count = inst->fbc_buf_count; spin_unlock_irqrestore(&inst->state_spinlock, flags); + wave5_update_min_bufs_ctrl(inst, fbc_buf_count); + return 0; } @@ -1659,6 +1673,7 @@ static void wave5_vpu_dec_device_run(void *priv) ret = initialize_sequence(inst); if (ret) { unsigned long flags; + u32 fbc_buf_count = 0; spin_lock_irqsave(&inst->state_spinlock, flags); if (wave5_is_draining_or_eos(inst) && @@ -1667,14 +1682,18 @@ static void wave5_vpu_dec_device_run(void *priv) switch_state(inst, VPU_INST_STATE_STOP); - if (vb2_is_streaming(dst_vq)) + if (vb2_is_streaming(dst_vq)) { send_eos_event(inst); - else + } else { handle_dynamic_resolution_change(inst); + fbc_buf_count = inst->fbc_buf_count; + } flag_last_buffer_done(inst); } spin_unlock_irqrestore(&inst->state_spinlock, flags); + + wave5_update_min_bufs_ctrl(inst, fbc_buf_count); } else { set_instance_state(inst, VPU_INST_STATE_INIT_SEQ); } diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c index 7613fcdbafed..e6c94b6f2671 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c @@ -90,6 +90,22 @@ static const struct vpu_format enc_fmt_list[FMT_TYPES][MAX_FMTS] = { .v4l2_pix_fmt = V4L2_PIX_FMT_NV61M, .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW], }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_YUYV, + .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW], + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_YVYU, + .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW], + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_UYVY, + .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW], + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_VYUY, + .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW], + }, } }; @@ -226,13 +242,6 @@ static int start_encode(struct vpu_instance *inst, u32 *fail_res) } else { dev_dbg(inst->dev->dev, "%s: wave5_vpu_enc_start_one_frame success\n", __func__); - /* - * Remove the source buffer from the ready-queue now and finish - * it in the videobuf2 framework once the index is returned by the - * firmware in finish_encode - */ - if (src_buf) - v4l2_m2m_src_buf_remove_by_idx(m2m_ctx, src_buf->vb2_buf.index); } return 0; @@ -259,27 +268,13 @@ static void wave5_vpu_enc_finish_encode(struct vpu_instance *inst) __func__, enc_output_info.pic_type, enc_output_info.recon_frame_index, enc_output_info.enc_src_idx, enc_output_info.enc_pic_byte, enc_output_info.pts); - /* - * The source buffer will not be found in the ready-queue as it has been - * dropped after sending of the encode firmware command, locate it in - * the videobuf2 queue directly - */ if (enc_output_info.enc_src_idx >= 0) { - struct vb2_buffer *vb = vb2_get_buffer(v4l2_m2m_get_src_vq(m2m_ctx), - enc_output_info.enc_src_idx); - if (vb->state != VB2_BUF_STATE_ACTIVE) - dev_warn(inst->dev->dev, - "%s: encoded buffer (%d) was not in ready queue %i.", - __func__, enc_output_info.enc_src_idx, vb->state); - else - src_buf = to_vb2_v4l2_buffer(vb); - - if (src_buf) { + src_buf = v4l2_m2m_src_buf_remove_by_idx(m2m_ctx, enc_output_info.enc_src_idx); + if (!src_buf) { + dev_warn(inst->dev->dev, "%s: no source buffer found\n", __func__); + } else { inst->timestamp = src_buf->vb2_buf.timestamp; v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); - } else { - dev_warn(inst->dev->dev, "%s: no source buffer with index: %d found\n", - __func__, enc_output_info.enc_src_idx); } } @@ -780,6 +775,9 @@ static int wave5_vpu_enc_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_BITRATE: inst->bit_rate = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_BACKGROUND_DETECTION: + inst->enc_param.bg_detection = ctrl->val; + break; case V4L2_CID_MPEG_VIDEO_GOP_SIZE: inst->enc_param.avc_idr_period = ctrl->val; break; @@ -936,6 +934,8 @@ static int wave5_vpu_enc_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE: inst->enc_param.profile = H264_PROFILE_BP; inst->bit_depth = 8; + if (ctrl->val == V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) + inst->enc_param.constraint_set1_flag = 1; break; case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: inst->enc_param.profile = H264_PROFILE_MP; @@ -1156,6 +1156,22 @@ static int wave5_set_enc_openparam(struct enc_open_param *open_param, else open_param->src_format = FORMAT_420; + switch (info->format) { + case V4L2_PIX_FMT_YUYV: + open_param->packed_format = PACKED_YUYV; + break; + case V4L2_PIX_FMT_YVYU: + open_param->packed_format = PACKED_YVYU; + break; + case V4L2_PIX_FMT_UYVY: + open_param->packed_format = PACKED_UYVY; + break; + case V4L2_PIX_FMT_VYUY: + open_param->packed_format = PACKED_VYUY; + break; + default: + break; + } open_param->wave_param.gop_preset_idx = PRESET_IDX_IPP_SINGLE; open_param->wave_param.hvs_qp_scale = 2; open_param->wave_param.hvs_max_delta_qp = 10; @@ -1205,12 +1221,14 @@ static int wave5_set_enc_openparam(struct enc_open_param *open_param, open_param->wave_param.beta_offset_div2 = input.beta_offset_div2; open_param->wave_param.decoding_refresh_type = input.decoding_refresh_type; open_param->wave_param.intra_period = input.intra_period; + open_param->wave_param.bg_detection = input.bg_detection; if (inst->std == W_HEVC_ENC) { if (input.intra_period == 0) { open_param->wave_param.decoding_refresh_type = DEC_REFRESH_TYPE_IDR; open_param->wave_param.intra_period = input.avc_idr_period; } } else { + open_param->wave_param.constraint_set1_flag = input.constraint_set1_flag; open_param->wave_param.avc_idr_period = input.avc_idr_period; } open_param->wave_param.entropy_coding_mode = input.entropy_coding_mode; @@ -1683,7 +1701,7 @@ static int wave5_vpu_open_enc(struct file *filp) -6, 6, 1, 0); v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM, - 0, 1, 1, 1); + 0, 1, 1, 0); v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION, 0, 1, 1, 0); @@ -1701,6 +1719,9 @@ static int wave5_vpu_open_enc(struct file *filp) V4L2_CID_MPEG_VIDEO_AU_DELIMITER, 0, 1, 1, 1); v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BACKGROUND_DETECTION, + 0, 1, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c index d26ffc942219..f77abd5e122a 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c @@ -584,8 +584,15 @@ int wave5_vpu_dec_get_output_info(struct vpu_instance *inst, struct dec_output_i p_dec_info->num_of_decoding_fbs : p_dec_info->num_of_display_fbs; if (info->index_frame_display >= 0 && - info->index_frame_display < (int)max_dec_index) - info->disp_frame = inst->frame_buf[val + info->index_frame_display]; + info->index_frame_display < (int)max_dec_index) { + u32 idx = val + info->index_frame_display; + + if (WARN_ON(idx >= MAX_REG_FRAME)) { + ret = -EINVAL; + goto err_out; + } + info->disp_frame = inst->frame_buf[idx]; + } info->rd_ptr = p_dec_info->stream_rd_ptr; info->wr_ptr = p_dec_info->stream_wr_ptr; diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h index c64135769869..7b08fef58217 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h @@ -570,6 +570,8 @@ struct enc_wave_param { u32 transform8x8_enable: 1; /* enable 8x8 intra prediction and 8x8 transform */ u32 mb_level_rc_enable: 1; /* enable MB-level rate control */ u32 forced_idr_header_enable: 1; /* enable header encoding before IDR frame */ + u32 constraint_set1_flag: 1; /* enable CBP */ + u32 bg_detection: 1; /* enable background detection */ }; struct enc_open_param { diff --git a/drivers/media/platform/marvell/cafe-driver.c b/drivers/media/platform/marvell/cafe-driver.c index 632c15572aa8..22034df6cba9 100644 --- a/drivers/media/platform/marvell/cafe-driver.c +++ b/drivers/media/platform/marvell/cafe-driver.c @@ -609,6 +609,7 @@ static void cafe_pci_remove(struct pci_dev *pdev) return; } cafe_shutdown(cam); + pci_disable_device(pdev); kfree(cam); } diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c index 8c684756d5fc..d147ec483081 100644 --- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c @@ -1202,7 +1202,8 @@ static int mtk_jpeg_release(struct file *file) struct mtk_jpeg_dev *jpeg = video_drvdata(file); struct mtk_jpeg_ctx *ctx = mtk_jpeg_file_to_ctx(file); - cancel_work_sync(&ctx->jpeg_work); + if (jpeg->variant->jpeg_worker) + cancel_work_sync(&ctx->jpeg_work); mutex_lock(&jpeg->lock); v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); v4l2_ctrl_handler_free(&ctx->ctrl_hdl); diff --git a/drivers/media/platform/nuvoton/npcm-video.c b/drivers/media/platform/nuvoton/npcm-video.c index b2a562e1ee1c..52505af35c08 100644 --- a/drivers/media/platform/nuvoton/npcm-video.c +++ b/drivers/media/platform/nuvoton/npcm-video.c @@ -1720,10 +1720,12 @@ static int npcm_video_init(struct npcm_video *video) if (rc) { dev_err(dev, "Failed to set DMA mask\n"); of_reserved_mem_device_release(dev); + return rc; } rc = npcm_video_ece_init(video); if (rc) { + of_reserved_mem_device_release(dev); dev_err(dev, "Failed to initialize ECE\n"); return rc; } @@ -1748,42 +1750,55 @@ static int npcm_video_probe(struct platform_device *pdev) regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(regs)) { dev_err(&pdev->dev, "Failed to parse VCD reg in DTS\n"); - return PTR_ERR(regs); + rc = PTR_ERR(regs); + goto err_free; } video->vcd_regmap = devm_regmap_init_mmio(&pdev->dev, regs, &npcm_video_regmap_cfg); if (IS_ERR(video->vcd_regmap)) { dev_err(&pdev->dev, "Failed to initialize VCD regmap\n"); - return PTR_ERR(video->vcd_regmap); + rc = PTR_ERR(video->vcd_regmap); + goto err_free; } video->reset = devm_reset_control_get(&pdev->dev, NULL); if (IS_ERR(video->reset)) { dev_err(&pdev->dev, "Failed to get VCD reset control in DTS\n"); - return PTR_ERR(video->reset); + rc = PTR_ERR(video->reset); + goto err_free; } video->gcr_regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "nuvoton,sysgcr"); - if (IS_ERR(video->gcr_regmap)) - return PTR_ERR(video->gcr_regmap); + if (IS_ERR(video->gcr_regmap)) { + rc = PTR_ERR(video->gcr_regmap); + goto err_free; + } video->gfx_regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "nuvoton,sysgfxi"); - if (IS_ERR(video->gfx_regmap)) - return PTR_ERR(video->gfx_regmap); + if (IS_ERR(video->gfx_regmap)) { + rc = PTR_ERR(video->gfx_regmap); + goto err_free; + } rc = npcm_video_init(video); if (rc) - return rc; + goto err_free; rc = npcm_video_setup_video(video); if (rc) - return rc; + goto err_release_mem; dev_info(video->dev, "NPCM video driver probed\n"); return 0; + +err_release_mem: + of_reserved_mem_device_release(&pdev->dev); +err_free: + kfree(video); + return rc; } static void npcm_video_remove(struct platform_device *pdev) @@ -1798,6 +1813,7 @@ static void npcm_video_remove(struct platform_device *pdev) v4l2_device_unregister(v4l2_dev); if (video->ece.enable) npcm_video_ece_stop(video); + kfree(video); of_reserved_mem_device_release(dev); } diff --git a/drivers/media/platform/nvidia/tegra-vde/Kconfig b/drivers/media/platform/nvidia/tegra-vde/Kconfig index 2fe13f39c95b..f05fcc94deca 100644 --- a/drivers/media/platform/nvidia/tegra-vde/Kconfig +++ b/drivers/media/platform/nvidia/tegra-vde/Kconfig @@ -2,6 +2,7 @@ config VIDEO_TEGRA_VDE tristate "NVIDIA Tegra Video Decoder Engine driver" depends on V4L_MEM2MEM_DRIVERS depends on ARCH_TEGRA || COMPILE_TEST + depends on HAS_IOMEM depends on VIDEO_DEV select DMA_SHARED_BUFFER select IOMMU_IOVA diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c index 4bf8570e1b9e..e8545761b5ff 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c @@ -538,6 +538,8 @@ static int mxc_isi_probe(struct platform_device *pdev) return 0; err_xbar: + while (i--) + mxc_isi_pipe_cleanup(&isi->pipes[i]); mxc_isi_crossbar_cleanup(&isi->crossbar); return ret; @@ -556,8 +558,8 @@ static void mxc_isi_remove(struct platform_device *pdev) mxc_isi_pipe_cleanup(pipe); } - mxc_isi_crossbar_cleanup(&isi->crossbar); mxc_isi_v4l2_cleanup(isi); + mxc_isi_crossbar_cleanup(&isi->crossbar); } static const struct of_device_id mxc_isi_of_match[] = { diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h index 14d63ec36416..7547a6559d4c 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h @@ -11,6 +11,7 @@ #define __MXC_ISI_CORE_H__ #include <linux/list.h> +#include <linux/math.h> #include <linux/mutex.h> #include <linux/spinlock.h> #include <linux/types.h> @@ -414,4 +415,19 @@ static inline void mxc_isi_debug_cleanup(struct mxc_isi_dev *isi) } #endif +/* + * ISI scaling engine works in two parts: it performs pre-decimation of + * the image followed by bilinear filtering to achieve the desired + * downscaling factor. + * + * The decimation filter provides a maximum downscaling factor of 8, and + * the subsequent bilinear filter provides a maximum downscaling factor + * of 2. Combined, the maximum scaling factor can be up to 16. + */ +static inline unsigned int +mxc_isi_clamp_downscale_16(unsigned int val, unsigned int max_val) +{ + return clamp(val, max(1U, DIV_ROUND_UP(max_val, 16)), max_val); +} + #endif /* __MXC_ISI_CORE_H__ */ diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c index 605a45124103..c580c831972e 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c @@ -491,6 +491,7 @@ err_free: void mxc_isi_crossbar_cleanup(struct mxc_isi_crossbar *xbar) { + v4l2_subdev_cleanup(&xbar->sd); media_entity_cleanup(&xbar->sd.entity); kfree(xbar->pads); kfree(xbar->inputs); diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c index 0187d4ab97e8..16b20ea2d1db 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c @@ -112,7 +112,14 @@ static u32 mxc_isi_channel_scaling_ratio(unsigned int from, unsigned int to, else *dec = 8; - return min_t(u32, from * 0x1000 / (to * *dec), ISI_DOWNSCALE_THRESHOLD); + /* + * The ISI rounds output dimensions up to the next integer (i.MX93 RM + * section 57.7.8). Calculate the scale factor such that the theoretical + * output (input / scale_factor) rounds up to exactly the desired + * output. + */ + return min_t(u32, DIV_ROUND_UP(from * 0x1000, to * *dec), + ISI_DOWNSCALE_THRESHOLD); } static void mxc_isi_channel_set_scaling(struct mxc_isi_pipe *pipe, diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c index a39ad7a1ab18..de398b232d74 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c @@ -509,9 +509,14 @@ __mxc_isi_m2m_try_fmt_vid(struct mxc_isi_m2m_ctx *ctx, const enum mxc_isi_video_type type) { if (type == MXC_ISI_VIDEO_M2M_CAP) { - /* Downscaling only */ - pix->width = min(pix->width, ctx->queues.out.format.width); - pix->height = min(pix->height, ctx->queues.out.format.height); + const struct v4l2_pix_format_mplane *format = + &ctx->queues.out.format; + + /* Downscaling only, by up to 16. */ + pix->width = mxc_isi_clamp_downscale_16(pix->width, + format->width); + pix->height = mxc_isi_clamp_downscale_16(pix->height, + format->height); } return mxc_isi_format_try(ctx->m2m->pipe, pix, type); diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c index a41c51dd9ce0..2d0843c86534 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c @@ -641,16 +641,19 @@ static int mxc_isi_pipe_set_selection(struct v4l2_subdev *sd, /* Composing is supported on the sink only. */ return -EINVAL; - /* The sink crop is bound by the sink format downscaling only). */ + /* + * The ISI supports downscaling only, with a factor up to 16. + * Clamp the compose rectangle size accordingly. + */ format = mxc_isi_pipe_get_pad_format(pipe, state, MXC_ISI_PIPE_PAD_SINK); sel->r.left = 0; sel->r.top = 0; - sel->r.width = clamp(sel->r.width, MXC_ISI_MIN_WIDTH, - format->width); - sel->r.height = clamp(sel->r.height, MXC_ISI_MIN_HEIGHT, - format->height); + sel->r.width = mxc_isi_clamp_downscale_16(sel->r.width, + format->width); + sel->r.height = mxc_isi_clamp_downscale_16(sel->r.height, + format->height); rect = mxc_isi_pipe_get_pad_compose(pipe, state, MXC_ISI_PIPE_PAD_SINK); @@ -796,18 +799,20 @@ int mxc_isi_pipe_init(struct mxc_isi_dev *isi, unsigned int id) irq = platform_get_irq(to_platform_device(isi->dev), id); if (irq < 0) { ret = irq; - goto error; + goto error_subdev; } ret = devm_request_irq(isi->dev, irq, mxc_isi_pipe_irq_handler, 0, dev_name(isi->dev), pipe); if (ret < 0) { dev_err(isi->dev, "failed to request IRQ (%d)\n", ret); - goto error; + goto error_subdev; } return 0; +error_subdev: + v4l2_subdev_cleanup(sd); error: media_entity_cleanup(&sd->entity); mutex_destroy(&pipe->lock); @@ -819,6 +824,7 @@ void mxc_isi_pipe_cleanup(struct mxc_isi_pipe *pipe) { struct v4l2_subdev *sd = &pipe->sd; + v4l2_subdev_cleanup(sd); media_entity_cleanup(&sd->entity); mutex_destroy(&pipe->lock); } diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c index 1be3a728f32f..fe4adfa3a1f0 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c @@ -792,7 +792,11 @@ static void mxc_isi_video_queue_first_buffers(struct mxc_isi_video *video) struct mxc_isi_buffer *buf; struct list_head *list; - list = i < discard ? &video->out_discard : &video->out_pending; + /* + * Queue buffers: prioritize pending buffers, then discard + * buffers. + */ + list = (i < 2 - discard) ? &video->out_pending : &video->out_discard; buf = list_first_entry(list, struct mxc_isi_buffer, list); mxc_isi_channel_set_outbuf(video->pipe, buf->dma_addrs, buf_id); diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile index 5e349b491513..27898b3cc7d3 100644 --- a/drivers/media/platform/qcom/camss/Makefile +++ b/drivers/media/platform/qcom/camss/Makefile @@ -10,10 +10,14 @@ qcom-camss-objs += \ camss-csid-680.o \ camss-csid-gen2.o \ camss-csid-gen3.o \ + camss-csiphy.o \ camss-csiphy-2ph-1-0.o \ camss-csiphy-3ph-1-0.o \ - camss-csiphy.o \ + camss-format.o \ camss-ispif.o \ + camss-tpg.o \ + camss-tpg-gen1.o \ + camss-vfe.o \ camss-vfe-4-1.o \ camss-vfe-4-7.o \ camss-vfe-4-8.o \ @@ -21,11 +25,9 @@ qcom-camss-objs += \ camss-vfe-340.o \ camss-vfe-480.o \ camss-vfe-680.o \ - camss-vfe-gen3.o \ camss-vfe-gen1.o \ + camss-vfe-gen3.o \ camss-vfe-vbif.o \ - camss-vfe.o \ - camss-video.o \ - camss-format.o \ + camss-video.o obj-$(CONFIG_VIDEO_QCOM_CAMSS) += qcom-camss.o diff --git a/drivers/media/platform/qcom/camss/camss-csid-340.c b/drivers/media/platform/qcom/camss/camss-csid-340.c index 2b50f9b96a34..eca3f07b8a8a 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-340.c +++ b/drivers/media/platform/qcom/camss/camss-csid-340.c @@ -41,19 +41,33 @@ #define CSI2_RX_CFG1_MISR_EN BIT(6) #define CSI2_RX_CFG1_CGC_MODE BIT(7) -#define CSID_RDI_CFG0(rdi) (0x300 + 0x100 * (rdi)) -#define CSID_RDI_CFG0_BYTE_CNTR_EN BIT(0) -#define CSID_RDI_CFG0_TIMESTAMP_EN BIT(1) -#define CSID_RDI_CFG0_DECODE_FORMAT_MASK GENMASK(15, 12) -#define CSID_RDI_CFG0_DECODE_FORMAT_NOP CSID_RDI_CFG0_DECODE_FORMAT_MASK -#define CSID_RDI_CFG0_DT_MASK GENMASK(21, 16) -#define CSID_RDI_CFG0_VC_MASK GENMASK(23, 22) -#define CSID_RDI_CFG0_DTID_MASK GENMASK(28, 27) -#define CSID_RDI_CFG0_ENABLE BIT(31) - -#define CSID_RDI_CTRL(rdi) (0x308 + 0x100 * (rdi)) -#define CSID_RDI_CTRL_HALT_AT_FRAME_BOUNDARY 0 -#define CSID_RDI_CTRL_RESUME_AT_FRAME_BOUNDARY 1 +#define CSID_CFG0(iface) (0x200 + 0x100 * (iface)) +#define CSID_CFG0_BYTE_CNTR_EN BIT(0) +#define CSID_CFG0_TIMESTAMP_EN BIT(1) +#define CSID_CFG0_DECODE_FORMAT_MASK GENMASK(15, 12) +#define CSID_CFG0_DECODE_FORMAT_NOP CSID_CFG0_DECODE_FORMAT_MASK +#define CSID_CFG0_DT_MASK GENMASK(21, 16) +#define CSID_CFG0_VC_MASK GENMASK(23, 22) +#define CSID_CFG0_DTID_MASK GENMASK(28, 27) +#define CSID_CFG0_ENABLE BIT(31) + +#define CSID_CTRL(iface) (0x208 + 0x100 * (iface)) +#define CSID_CTRL_HALT_AT_FRAME_BOUNDARY 0 +#define CSID_CTRL_RESUME_AT_FRAME_BOUNDARY 1 + +enum csid_iface { + CSID_IFACE_PIX, + CSID_IFACE_RDI0, + CSID_IFACE_RDI1, + CSID_IFACE_RDI2, +}; + +static enum csid_iface csid_port_iface_map[MSM_CSID_MAX_SRC_STREAMS] = { + [0] = CSID_IFACE_RDI0, + [1] = CSID_IFACE_RDI1, + [2] = CSID_IFACE_RDI2, + [3] = CSID_IFACE_PIX, +}; static void __csid_configure_rx(struct csid_device *csid, struct csid_phy_config *phy) { @@ -69,17 +83,14 @@ static void __csid_configure_rx(struct csid_device *csid, struct csid_phy_config writel_relaxed(val, csid->base + CSID_CSI2_RX_CFG1); } -static void __csid_ctrl_rdi(struct csid_device *csid, int enable, u8 rdi) -{ - writel_relaxed(!!enable, csid->base + CSID_RDI_CTRL(rdi)); -} - -static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc) +static void __csid_configure_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc) { - struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc]; + struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port]; const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, csid->res->formats->nformats, input_format->code); + enum csid_iface iface = csid_port_iface_map[port]; + u8 dt_id; u32 val; @@ -88,40 +99,44 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 * the four least significant bits of the five bit VC * bitfield to generate an internal CID value. * - * CSID_RDI_CFG0(vc) + * CSID_CFG0(port) * DT_ID : 28:27 * VC : 26:22 * DT : 21:16 * * CID : VC 3:0 << 2 | DT_ID 1:0 */ - dt_id = vc & 0x03; + dt_id = port & 0x03; + + if (iface == CSID_IFACE_PIX) + val = FIELD_PREP(CSID_CFG0_DECODE_FORMAT_MASK, format->decode_format); + else /* RDI is raw, no decoding */ + val = CSID_CFG0_DECODE_FORMAT_NOP; - val = CSID_RDI_CFG0_DECODE_FORMAT_NOP; /* only for RDI path */ - val |= FIELD_PREP(CSID_RDI_CFG0_DT_MASK, format->data_type); - val |= FIELD_PREP(CSID_RDI_CFG0_VC_MASK, vc); - val |= FIELD_PREP(CSID_RDI_CFG0_DTID_MASK, dt_id); + val |= FIELD_PREP(CSID_CFG0_DT_MASK, format->data_type); + val |= FIELD_PREP(CSID_CFG0_VC_MASK, vc); + val |= FIELD_PREP(CSID_CFG0_DTID_MASK, dt_id); if (enable) - val |= CSID_RDI_CFG0_ENABLE; + val |= CSID_CFG0_ENABLE; - dev_dbg(csid->camss->dev, "CSID%u: Stream %s (dt:0x%x vc=%u)\n", - csid->id, enable ? "enable" : "disable", format->data_type, vc); + dev_dbg(csid->camss->dev, "CSID%u: Stream %s (dt:0x%x df=0x%x port=%u vc=%u)\n", + csid->id, enable ? "enable" : "disable", format->data_type, + format->decode_format, port, vc); - writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc)); + writel_relaxed(val, csid->base + CSID_CFG0(iface)); + writel_relaxed(enable, csid->base + CSID_CTRL(iface)); } -static void csid_configure_stream(struct csid_device *csid, u8 enable) +static void csid_configure_streams(struct csid_device *csid, u8 enable) { int i; __csid_configure_rx(csid, &csid->phy); for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) { - if (csid->phy.en_vc & BIT(i)) { - __csid_configure_rdi_stream(csid, enable, i); - __csid_ctrl_rdi(csid, enable, i); - } + if (csid->phy.en_vc & BIT(i)) + __csid_configure_stream(csid, !!enable, i, 0); } } @@ -177,7 +192,7 @@ static void csid_subdev_init(struct csid_device *csid) {} const struct csid_hw_ops csid_ops_340 = { .configure_testgen_pattern = csid_configure_testgen_pattern, - .configure_stream = csid_configure_stream, + .configure_stream = csid_configure_streams, .hw_version = csid_hw_version, .isr = csid_isr, .reset = csid_reset, diff --git a/drivers/media/platform/qcom/camss/camss-csid-680.c b/drivers/media/platform/qcom/camss/camss-csid-680.c index 3ad3a174bcfb..345a67c8fb94 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-680.c +++ b/drivers/media/platform/qcom/camss/camss-csid-680.c @@ -103,6 +103,8 @@ #define CSI2_RX_CFG0_PHY_NUM_SEL 20 #define CSI2_RX_CFG0_PHY_SEL_BASE_IDX 1 #define CSI2_RX_CFG0_PHY_TYPE_SEL 24 +#define CSI2_RX_CFG0_TPG_MUX_EN BIT(27) +#define CSI2_RX_CFG0_TPG_MUX_SEL GENMASK(29, 28) #define CSID_CSI2_RX_CFG1 0x204 #define CSI2_RX_CFG1_PACKET_ECC_CORRECTION_EN BIT(0) @@ -185,10 +187,20 @@ static void __csid_configure_rx(struct csid_device *csid, struct csid_phy_config *phy, int vc) { u32 val; + struct camss *camss; + camss = csid->camss; val = (phy->lane_cnt - 1) << CSI2_RX_CFG0_NUM_ACTIVE_LANES; val |= phy->lane_assign << CSI2_RX_CFG0_DL0_INPUT_SEL; - val |= (phy->csiphy_id + CSI2_RX_CFG0_PHY_SEL_BASE_IDX) << CSI2_RX_CFG0_PHY_NUM_SEL; + + if (camss->tpg && csid->tpg_linked && + camss->tpg[phy->csiphy_id].testgen.mode != TPG_PAYLOAD_MODE_DISABLED) { + val |= FIELD_PREP(CSI2_RX_CFG0_TPG_MUX_SEL, phy->csiphy_id + 1); + val |= CSI2_RX_CFG0_TPG_MUX_EN; + } else { + val |= (phy->csiphy_id + CSI2_RX_CFG0_PHY_SEL_BASE_IDX) + << CSI2_RX_CFG0_PHY_NUM_SEL; + } writel(val, csid->base + CSID_CSI2_RX_CFG0); @@ -219,9 +231,9 @@ static void __csid_configure_top(struct csid_device *csid) CSID_TOP_IO_PATH_CFG0(csid->id)); } -static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc) +static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc) { - struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc]; + struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port]; const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, csid->res->formats->nformats, input_format->code); @@ -233,28 +245,28 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 lane_cnt = 4; val = 0; - writel(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(vc)); + writel(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(port)); /* * DT_ID is a two bit bitfield that is concatenated with * the four least significant bits of the five bit VC * bitfield to generate an internal CID value. * - * CSID_RDI_CFG0(vc) + * CSID_RDI_CFG0(port) * DT_ID : 28:27 * VC : 26:22 * DT : 21:16 * * CID : VC 3:0 << 2 | DT_ID 1:0 */ - dt_id = vc & 0x03; + dt_id = port & 0x03; /* note: for non-RDI path, this should be format->decode_format */ val |= DECODE_FORMAT_PAYLOAD_ONLY << RDI_CFG0_DECODE_FORMAT; val |= format->data_type << RDI_CFG0_DATA_TYPE; val |= vc << RDI_CFG0_VIRTUAL_CHANNEL; val |= dt_id << RDI_CFG0_DT_ID; - writel(val, csid->base + CSID_RDI_CFG0(vc)); + writel(val, csid->base + CSID_RDI_CFG0(port)); val = RDI_CFG1_TIMESTAMP_STB_FRAME; val |= RDI_CFG1_BYTE_CNTR_EN; @@ -265,23 +277,23 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 val |= RDI_CFG1_CROP_V_EN; val |= RDI_CFG1_PACKING_MIPI; - writel(val, csid->base + CSID_RDI_CFG1(vc)); + writel(val, csid->base + CSID_RDI_CFG1(port)); val = 0; - writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc)); + writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(port)); val = 1; - writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc)); + writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(port)); val = 0; - writel(val, csid->base + CSID_RDI_CTRL(vc)); + writel(val, csid->base + CSID_RDI_CTRL(port)); - val = readl(csid->base + CSID_RDI_CFG0(vc)); + val = readl(csid->base + CSID_RDI_CFG0(port)); if (enable) val |= RDI_CFG0_ENABLE; else val &= ~RDI_CFG0_ENABLE; - writel(val, csid->base + CSID_RDI_CFG0(vc)); + writel(val, csid->base + CSID_RDI_CFG0(port)); } static void csid_configure_stream(struct csid_device *csid, u8 enable) @@ -290,11 +302,11 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) __csid_configure_top(csid); - /* Loop through all enabled VCs and configure stream for each */ + /* Loop through all enabled ports and configure a stream for each */ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) { if (csid->phy.en_vc & BIT(i)) { - __csid_configure_rdi_stream(csid, enable, i); - __csid_configure_rx(csid, &csid->phy, i); + __csid_configure_rdi_stream(csid, enable, i, 0); + __csid_configure_rx(csid, &csid->phy, 0); __csid_ctrl_rdi(csid, enable, i); } } diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen2.c b/drivers/media/platform/qcom/camss/camss-csid-gen2.c index 2a1746dcc1c5..eadcb2f7e3aa 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-gen2.c +++ b/drivers/media/platform/qcom/camss/camss-csid-gen2.c @@ -203,10 +203,10 @@ static void __csid_ctrl_rdi(struct csid_device *csid, int enable, u8 rdi) writel_relaxed(val, csid->base + CSID_RDI_CTRL(rdi)); } -static void __csid_configure_testgen(struct csid_device *csid, u8 enable, u8 vc) +static void __csid_configure_testgen(struct csid_device *csid, u8 enable, u8 port, u8 vc) { struct csid_testgen_config *tg = &csid->testgen; - struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc]; + struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port]; const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, csid->res->formats->nformats, input_format->code); @@ -253,10 +253,10 @@ static void __csid_configure_testgen(struct csid_device *csid, u8 enable, u8 vc) writel_relaxed(val, csid->base + CSID_TPG_CTRL); } -static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc) +static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc) { /* Source pads matching RDI channels on hardware. Pad 1 -> RDI0, Pad 2 -> RDI1, etc. */ - struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc]; + struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port]; const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, csid->res->formats->nformats, input_format->code); @@ -267,14 +267,14 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 * the four least significant bits of the five bit VC * bitfield to generate an internal CID value. * - * CSID_RDI_CFG0(vc) + * CSID_RDI_CFG0(port) * DT_ID : 28:27 * VC : 26:22 * DT : 21:16 * * CID : VC 3:0 << 2 | DT_ID 1:0 */ - u8 dt_id = vc & 0x03; + u8 dt_id = port & 0x03; val = 1 << RDI_CFG0_BYTE_CNTR_EN; val |= 1 << RDI_CFG0_FORMAT_MEASURE_EN; @@ -284,56 +284,57 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 val |= format->data_type << RDI_CFG0_DATA_TYPE; val |= vc << RDI_CFG0_VIRTUAL_CHANNEL; val |= dt_id << RDI_CFG0_DT_ID; - writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc)); + writel_relaxed(val, csid->base + CSID_RDI_CFG0(port)); /* CSID_TIMESTAMP_STB_POST_IRQ */ val = 2 << RDI_CFG1_TIMESTAMP_STB_SEL; - writel_relaxed(val, csid->base + CSID_RDI_CFG1(vc)); + writel_relaxed(val, csid->base + CSID_RDI_CFG1(port)); val = 1; - writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(vc)); + writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(port)); val = 0; - writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PATTERN(vc)); + writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PATTERN(port)); val = 1; - writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc)); + writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(port)); val = 0; - writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc)); + writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(port)); val = 1; - writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PERIOD(vc)); + writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PERIOD(port)); val = 0; - writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PATTERN(vc)); + writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PATTERN(port)); val = 1; - writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PERIOD(vc)); + writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PERIOD(port)); val = 0; - writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PATTERN(vc)); + writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PATTERN(port)); val = 0; - writel_relaxed(val, csid->base + CSID_RDI_CTRL(vc)); + writel_relaxed(val, csid->base + CSID_RDI_CTRL(port)); - val = readl_relaxed(csid->base + CSID_RDI_CFG0(vc)); + val = readl_relaxed(csid->base + CSID_RDI_CFG0(port)); val |= enable << RDI_CFG0_ENABLE; - writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc)); + writel_relaxed(val, csid->base + CSID_RDI_CFG0(port)); } static void csid_configure_stream(struct csid_device *csid, u8 enable) { struct csid_testgen_config *tg = &csid->testgen; u8 i; - /* Loop through all enabled VCs and configure stream for each */ + + /* Loop through all enabled ports and configure a stream for each */ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) if (csid->phy.en_vc & BIT(i)) { if (tg->enabled) - __csid_configure_testgen(csid, enable, i); + __csid_configure_testgen(csid, enable, i, 0); - __csid_configure_rdi_stream(csid, enable, i); - __csid_configure_rx(csid, &csid->phy, i); + __csid_configure_rdi_stream(csid, enable, i, 0); + __csid_configure_rx(csid, &csid->phy, 0); __csid_ctrl_rdi(csid, enable, i); } } diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen3.c b/drivers/media/platform/qcom/camss/camss-csid-gen3.c index bd059243790e..0fdbf75fb27d 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-gen3.c +++ b/drivers/media/platform/qcom/camss/camss-csid-gen3.c @@ -66,6 +66,8 @@ #define CSI2_RX_CFG0_VC_MODE 3 #define CSI2_RX_CFG0_DL0_INPUT_SEL 4 #define CSI2_RX_CFG0_PHY_NUM_SEL 20 +#define CSI2_RX_CFG0_TPG_MUX_EN BIT(27) +#define CSI2_RX_CFG0_TPG_MUX_SEL GENMASK(29, 28) #define CSID_CSI2_RX_CFG1 0x204 #define CSI2_RX_CFG1_ECC_CORRECTION_EN BIT(0) @@ -109,10 +111,20 @@ static void __csid_configure_rx(struct csid_device *csid, struct csid_phy_config *phy, int vc) { int val; + struct camss *camss; + camss = csid->camss; val = (phy->lane_cnt - 1) << CSI2_RX_CFG0_NUM_ACTIVE_LANES; val |= phy->lane_assign << CSI2_RX_CFG0_DL0_INPUT_SEL; - val |= (phy->csiphy_id + CSI2_RX_CFG0_PHY_SEL_BASE_IDX) << CSI2_RX_CFG0_PHY_NUM_SEL; + + if (camss->tpg && csid->tpg_linked && + camss->tpg[phy->csiphy_id].testgen.mode != TPG_PAYLOAD_MODE_DISABLED) { + val |= FIELD_PREP(CSI2_RX_CFG0_TPG_MUX_SEL, phy->csiphy_id + 1); + val |= CSI2_RX_CFG0_TPG_MUX_EN; + } else { + val |= (phy->csiphy_id + CSI2_RX_CFG0_PHY_SEL_BASE_IDX) + << CSI2_RX_CFG0_PHY_NUM_SEL; + } writel(val, csid->base + CSID_CSI2_RX_CFG0); @@ -145,12 +157,12 @@ static void __csid_configure_wrapper(struct csid_device *csid) writel(val, csid->camss->csid_wrapper_base + CSID_IO_PATH_CFG0(csid->id)); } -static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc) +static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc) { u32 val; u8 lane_cnt = csid->phy.lane_cnt; /* Source pads matching RDI channels on hardware. Pad 1 -> RDI0, Pad 2 -> RDI1, etc. */ - struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc]; + struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port]; const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, csid->res->formats->nformats, input_format->code); @@ -163,14 +175,14 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 * the four least significant bits of the five bit VC * bitfield to generate an internal CID value. * - * CSID_RDI_CFG0(vc) + * CSID_RDI_CFG0(port) * DT_ID : 28:27 * VC : 26:22 * DT : 21:16 * * CID : VC 3:0 << 2 | DT_ID 1:0 */ - u8 dt_id = vc & 0x03; + u8 dt_id = port & 0x03; val = RDI_CFG0_TIMESTAMP_EN; val |= RDI_CFG0_TIMESTAMP_STB_SEL; @@ -180,7 +192,7 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 val |= format->data_type << RDI_CFG0_DT; val |= dt_id << RDI_CFG0_DT_ID; - writel(val, csid->base + CSID_RDI_CFG0(vc)); + writel(val, csid->base + CSID_RDI_CFG0(port)); val = RDI_CFG1_PACKING_FORMAT_MIPI; val |= RDI_CFG1_PIX_STORE; @@ -189,22 +201,22 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 val |= RDI_CFG1_CROP_H_EN; val |= RDI_CFG1_CROP_V_EN; - writel(val, csid->base + CSID_RDI_CFG1(vc)); + writel(val, csid->base + CSID_RDI_CFG1(port)); val = 0; - writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc)); + writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(port)); val = 1; - writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc)); + writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(port)); val = 0; - writel(val, csid->base + CSID_RDI_CTRL(vc)); + writel(val, csid->base + CSID_RDI_CTRL(port)); - val = readl(csid->base + CSID_RDI_CFG0(vc)); + val = readl(csid->base + CSID_RDI_CFG0(port)); if (enable) val |= RDI_CFG0_EN; - writel(val, csid->base + CSID_RDI_CFG0(vc)); + writel(val, csid->base + CSID_RDI_CFG0(port)); } static void csid_configure_stream(struct csid_device *csid, u8 enable) @@ -213,11 +225,11 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) __csid_configure_wrapper(csid); - /* Loop through all enabled VCs and configure stream for each */ + /* Loop through all enabled ports and configure a stream for each */ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) if (csid->phy.en_vc & BIT(i)) { - __csid_configure_rdi_stream(csid, enable, i); - __csid_configure_rx(csid, &csid->phy, i); + __csid_configure_rdi_stream(csid, enable, i, 0); + __csid_configure_rx(csid, &csid->phy, 0); __csid_ctrl_rdi(csid, enable, i); } } diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index ed1820488c98..48459b46a981 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -35,6 +35,8 @@ #define HW_VERSION_REVISION 16 #define HW_VERSION_GENERATION 28 +#define LANE_CFG_BITWIDTH 4 + #define MSM_CSID_NAME "msm_csid" const char * const csid_testgen_modes[] = { @@ -1215,18 +1217,22 @@ void msm_csid_get_csid_id(struct media_entity *entity, u8 *id) } /* - * csid_get_lane_assign - Calculate CSI2 lane assign configuration parameter - * @lane_cfg - CSI2 lane configuration + * csid_get_lane_assign - Calculate lane assign by csiphy/tpg lane num + * @lane_cfg: CSI2 lane configuration + * @num_lanes: lane num * * Return lane assign */ -static u32 csid_get_lane_assign(struct csiphy_lanes_cfg *lane_cfg) +static u32 csid_get_lane_assign(struct csiphy_lanes_cfg *lane_cfg, int num_lanes) { u32 lane_assign = 0; + int pos; int i; - for (i = 0; i < lane_cfg->num_data; i++) - lane_assign |= lane_cfg->data[i].pos << (i * 4); + for (i = 0; i < num_lanes; i++) { + pos = lane_cfg ? lane_cfg->data[i].pos : i; + lane_assign |= pos << (i * LANE_CFG_BITWIDTH); + } return lane_assign; } @@ -1251,6 +1257,7 @@ static int csid_link_setup(struct media_entity *entity, if ((local->flags & MEDIA_PAD_FL_SINK) && (flags & MEDIA_LNK_FL_ENABLED)) { struct v4l2_subdev *sd; + struct tpg_device *tpg; struct csid_device *csid; struct csiphy_device *csiphy; struct csiphy_lanes_cfg *lane_cfg; @@ -1265,18 +1272,28 @@ static int csid_link_setup(struct media_entity *entity, return -EBUSY; sd = media_entity_to_v4l2_subdev(remote->entity); - csiphy = v4l2_get_subdevdata(sd); + if (sd->grp_id == TPG_GRP_ID) { + tpg = v4l2_get_subdevdata(sd); - /* If a sensor is not linked to CSIPHY */ - /* do no allow a link from CSIPHY to CSID */ - if (!csiphy->cfg.csi2) - return -EPERM; + csid->phy.lane_cnt = tpg->res->lane_cnt; + csid->phy.csiphy_id = tpg->id; + csid->phy.lane_assign = csid_get_lane_assign(NULL, csid->phy.lane_cnt); + csid->tpg_linked = true; + } else { + csiphy = v4l2_get_subdevdata(sd); - csid->phy.csiphy_id = csiphy->id; + /* If a sensor is not linked to CSIPHY */ + /* do no allow a link from CSIPHY to CSID */ + if (!csiphy->cfg.csi2) + return -EPERM; - lane_cfg = &csiphy->cfg.csi2->lane_cfg; - csid->phy.lane_cnt = lane_cfg->num_data; - csid->phy.lane_assign = csid_get_lane_assign(lane_cfg); + csid->phy.csiphy_id = csiphy->id; + + lane_cfg = &csiphy->cfg.csi2->lane_cfg; + csid->phy.lane_cnt = lane_cfg->num_data; + csid->phy.lane_assign = csid_get_lane_assign(lane_cfg, lane_cfg->num_data); + csid->tpg_linked = false; + } } /* Decide which virtual channels to enable based on which source pads are enabled */ if (local->flags & MEDIA_PAD_FL_SOURCE) { diff --git a/drivers/media/platform/qcom/camss/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h index aedc96ed84b2..5296b10f6bac 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.h +++ b/drivers/media/platform/qcom/camss/camss-csid.h @@ -161,6 +161,7 @@ struct csid_device { int num_supplies; struct completion reset_complete; struct csid_testgen_config testgen; + bool tpg_linked; struct csid_phy_config phy; struct v4l2_mbus_framefmt fmt[MSM_CSID_PADS_NUM]; struct v4l2_ctrl_handler ctrls; diff --git a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c index 415483274552..dac8d2ecf799 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c @@ -399,6 +399,126 @@ csiphy_lane_regs lane_regs_sm8250[] = { {0x0884, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, }; +/* GEN2 1.2.3 2PH */ +static const struct +csiphy_lane_regs lane_regs_sm6350[] = { + {0x0030, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0904, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0910, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0900, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0908, 0x06, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0904, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x002c, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0034, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0010, 0x50, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x001c, 0x0a, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0014, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0028, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x003c, 0xb8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0000, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0004, 0x0c, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0020, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0008, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0010, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0038, 0xfe, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x005c, 0xc0, 0x00, CSIPHY_SKEW_CAL}, + {0x0060, 0x0d, 0x00, CSIPHY_SKEW_CAL}, + {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0000, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0730, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c84, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c90, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c80, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c88, 0x06, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c84, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x072c, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0734, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0710, 0x50, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x071c, 0x0a, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0714, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0728, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x073c, 0xb8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0700, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0704, 0x0c, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0720, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0708, 0x04, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x070c, 0xff, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0710, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0738, 0x1f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0000, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0000, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0230, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0a04, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0a10, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0a00, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0a08, 0x06, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0a04, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x022c, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0234, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0210, 0x50, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x021c, 0x0a, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0214, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0228, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x023c, 0xb8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0200, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0204, 0x0c, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0220, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0208, 0x04, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0210, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0238, 0xfe, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x025c, 0xc0, 0x00, CSIPHY_SKEW_CAL}, + {0x0260, 0x0d, 0x00, CSIPHY_SKEW_CAL}, + {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0000, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0430, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0b04, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0b10, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0b00, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0b08, 0x06, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0b04, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x042c, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0434, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0410, 0x50, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x041c, 0x0a, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0414, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0428, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x043c, 0xb8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0400, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0404, 0x0c, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0420, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0408, 0x04, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0410, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0438, 0xfe, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x045c, 0xc0, 0x00, CSIPHY_SKEW_CAL}, + {0x0460, 0x0d, 0x00, CSIPHY_SKEW_CAL}, + {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0000, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0630, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c04, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c10, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c00, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c08, 0x06, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c04, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x062c, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0634, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0610, 0x50, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x061c, 0x0a, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0614, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0628, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x063c, 0xb8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0600, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0604, 0x0c, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0620, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0608, 0x04, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0610, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0638, 0xfe, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x065c, 0xc0, 0x00, CSIPHY_SKEW_CAL}, + {0x0660, 0x0d, 0x00, CSIPHY_SKEW_CAL}, + {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0000, 0x00, 0x00, CSIPHY_DNP_PARAMS}, +}; + /* 14nm 2PH v 2.0.1 2p5Gbps 4 lane DPHY mode */ static const struct csiphy_lane_regs lane_regs_qcm2290[] = { @@ -1011,6 +1131,7 @@ static bool csiphy_is_gen2(u32 version) switch (version) { case CAMSS_2290: case CAMSS_6150: + case CAMSS_6350: case CAMSS_7280: case CAMSS_8250: case CAMSS_8280XP: @@ -1105,6 +1226,10 @@ static int csiphy_init(struct csiphy_device *csiphy) regs->lane_regs = &lane_regs_qcm2290[0]; regs->lane_array_size = ARRAY_SIZE(lane_regs_qcm2290); break; + case CAMSS_6350: + regs->lane_regs = &lane_regs_sm6350[0]; + regs->lane_array_size = ARRAY_SIZE(lane_regs_sm6350); + break; case CAMSS_7280: case CAMSS_8250: regs->lane_regs = &lane_regs_sm8250[0]; diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index 78a1b568dbae..539ac4888b60 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -793,6 +793,7 @@ int msm_csiphy_register_entity(struct csiphy_device *csiphy, sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d", MSM_CSIPHY_NAME, csiphy->id); + sd->grp_id = CSIPHY_GRP_ID; v4l2_set_subdevdata(sd, csiphy); ret = csiphy_init_formats(sd, NULL); diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h index 2d5054819df7..9d9657b82f74 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.h +++ b/drivers/media/platform/qcom/camss/camss-csiphy.h @@ -21,6 +21,8 @@ #define MSM_CSIPHY_PAD_SRC 1 #define MSM_CSIPHY_PADS_NUM 2 +#define CSIPHY_GRP_ID 1 + struct csiphy_lane { u8 pos; u8 pol; diff --git a/drivers/media/platform/qcom/camss/camss-format.c b/drivers/media/platform/qcom/camss/camss-format.c index 4a3d5549615c..52cb01306ee0 100644 --- a/drivers/media/platform/qcom/camss/camss-format.c +++ b/drivers/media/platform/qcom/camss/camss-format.c @@ -7,8 +7,10 @@ * Copyright (c) 2023, The Linux Foundation. All rights reserved. * Copyright (c) 2023 Qualcomm Technologies, Inc. */ +#include <linux/bits.h> #include <linux/bug.h> #include <linux/errno.h> +#include <linux/lcm.h> #include "camss-format.h" @@ -34,6 +36,18 @@ u8 camss_format_get_bpp(const struct camss_format_info *formats, unsigned int nf } /* + * camss_format_get_bpl_alignment - Retrieve required BPL alignment for a given format. + * @format: a pointer to the format + * + * Return the required alignment, in bytes. + */ +unsigned int camss_format_get_bpl_alignment(const struct camss_format_info *format) +{ + /* Minimal number of bytes required to keep the line length an integer number of pixels */ + return lcm_not_zero(format->mbus_bpp, BITS_PER_BYTE) / BITS_PER_BYTE; +} + +/* * camss_format_find_code - Find a format code in an array * @code: a pointer to media bus format codes array * @n_code: size of @code array diff --git a/drivers/media/platform/qcom/camss/camss-format.h b/drivers/media/platform/qcom/camss/camss-format.h index 923a48c9c3fb..4f87ac8c4975 100644 --- a/drivers/media/platform/qcom/camss/camss-format.h +++ b/drivers/media/platform/qcom/camss/camss-format.h @@ -55,6 +55,7 @@ struct camss_formats { }; u8 camss_format_get_bpp(const struct camss_format_info *formats, unsigned int nformats, u32 code); +unsigned int camss_format_get_bpl_alignment(const struct camss_format_info *f); u32 camss_format_find_code(u32 *code, unsigned int n_code, unsigned int index, u32 req_code); int camss_format_find_format(u32 code, u32 pixelformat, const struct camss_format_info *formats, unsigned int nformats); diff --git a/drivers/media/platform/qcom/camss/camss-tpg-gen1.c b/drivers/media/platform/qcom/camss/camss-tpg-gen1.c new file mode 100644 index 000000000000..d29de5f93c18 --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-tpg-gen1.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * + * Qualcomm MSM Camera Subsystem - TPG (Test Pattern Generator) Module + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ +#include <linux/io.h> +#include <linux/kernel.h> + +#include "camss-tpg.h" +#include "camss.h" + +/* TPG global registers */ +#define TPG_HW_VERSION 0x0 +# define HW_VERSION_STEPPING GENMASK(15, 0) +# define HW_VERSION_REVISION GENMASK(27, 16) +# define HW_VERSION_GENERATION GENMASK(31, 28) + +#define TPG_HW_VER(gen, rev, step) \ + (((u32)(gen) << 28) | ((u32)(rev) << 16) | (u32)(step)) + +#define TPG_HW_VER_2_0_0 TPG_HW_VER(2, 0, 0) +#define TPG_HW_VER_2_1_0 TPG_HW_VER(2, 1, 0) + +#define TPG_HW_STATUS 0x4 + +#define TPG_CTRL 0x64 +# define TPG_CTRL_TEST_EN BIT(0) +# define TPG_CTRL_PHY_SEL BIT(3) +# define TPG_CTRL_NUM_ACTIVE_LANES GENMASK(5, 4) +# define TPG_CTRL_VC_DT_PATTERN_ID GENMASK(8, 6) +# define TPG_CTRL_OVERLAP_SHDR_EN BIT(10) +# define TPG_CTRL_NUM_ACTIVE_VC GENMASK(31, 30) + +#define TPG_CLEAR 0x1F4 + +/* TPG VC-based registers */ +#define TPG_VC_n_GAIN_CFG(n) (0x60 + (n) * 0x60) + +#define TPG_VC_n_CFG0(n) (0x68 + (n) * 0x60) +# define TPG_VC_n_CFG0_VC_NUM GENMASK(4, 0) +# define TPG_VC_n_CFG0_NUM_ACTIVE_DT GENMASK(9, 8) +# define TPG_VC_n_CFG0_NUM_BATCH GENMASK(15, 12) +# define TPG_VC_n_CFG0_NUM_FRAMES GENMASK(31, 16) + +#define TPG_VC_n_LSFR_SEED(n) (0x6C + (n) * 0x60) +#define TPG_VC_n_HBI_CFG(n) (0x70 + (n) * 0x60) +#define TPG_VC_n_VBI_CFG(n) (0x74 + (n) * 0x60) + +#define TPG_VC_n_COLOR_BARS_CFG(n) (0x78 + (n) * 0x60) +# define TPG_VC_n_COLOR_BARS_CFG_PIX_PATTERN GENMASK(2, 0) +# define TPG_VC_n_COLOR_BARS_CFG_QCFA_EN BIT(3) +# define TPG_VC_n_COLOR_BARS_CFG_SPLIT_EN BIT(4) +# define TPG_VC_n_COLOR_BARS_CFG_NOISE_EN BIT(5) +# define TPG_VC_n_COLOR_BARS_CFG_ROTATE_PERIOD GENMASK(13, 8) +# define TPG_VC_n_COLOR_BARS_CFG_XCFA_EN BIT(16) +# define TPG_VC_n_COLOR_BARS_CFG_SIZE_X GENMASK(26, 24) +# define TPG_VC_n_COLOR_BARS_CFG_SIZE_Y GENMASK(30, 28) + +/* TPG DT-based registers */ +#define TPG_VC_m_DT_n_CFG_0(m, n) (0x7C + (m) * 0x60 + (n) * 0xC) +# define TPG_VC_m_DT_n_CFG_0_FRAME_HEIGHT GENMASK(15, 0) +# define TPG_VC_m_DT_n_CFG_0_FRAME_WIDTH GENMASK(31, 16) + +#define TPG_VC_m_DT_n_CFG_1(m, n) (0x80 + (m) * 0x60 + (n) * 0xC) +# define TPG_VC_m_DT_n_CFG_1_DATA_TYPE GENMASK(5, 0) +# define TPG_VC_m_DT_n_CFG_1_ECC_XOR_MASK GENMASK(13, 8) +# define TPG_VC_m_DT_n_CFG_1_CRC_XOR_MASK GENMASK(31, 16) + +#define TPG_VC_m_DT_n_CFG_2(m, n) (0x84 + (m) * 0x60 + (n) * 0xC) +# define TPG_VC_m_DT_n_CFG_2_PAYLOAD_MODE GENMASK(3, 0) +/* v2.0.0: USER[19:4], ENC[23:20] */ +# define TPG_V2_0_0_VC_m_DT_n_CFG_2_USER_SPECIFIED_PAYLOAD GENMASK(19, 4) +# define TPG_V2_0_0_VC_m_DT_n_CFG_2_ENCODE_FORMAT GENMASK(23, 20) +/* v2.1.0: USER[27:4], ENC[31:28] */ +# define TPG_V2_1_0_VC_m_DT_n_CFG_2_USER_SPECIFIED_PAYLOAD GENMASK(27, 4) +# define TPG_V2_1_0_VC_m_DT_n_CFG_2_ENCODE_FORMAT GENMASK(31, 28) + +#define TPG_HBI_PCT_DEFAULT 545 /* 545% */ +#define TPG_VBI_PCT_DEFAULT 10 /* 10% */ +#define PERCENT_BASE 100 + +/* Default user-specified payload for TPG test generator. + * Keep consistent with CSID TPG default: 0xBE. + */ +#define TPG_USER_SPECIFIED_PAYLOAD_DEFAULT 0xBE +#define TPG_LFSR_SEED_DEFAULT 0x12345678 +#define TPG_COLOR_BARS_CFG_STANDARD \ + FIELD_PREP(TPG_VC_n_COLOR_BARS_CFG_ROTATE_PERIOD, 0xA) + +static const char * const testgen_payload_modes[] = { + [TPG_PAYLOAD_MODE_DISABLED] = "Disabled", + [TPG_PAYLOAD_MODE_INCREMENTING] = "Incrementing", + [TPG_PAYLOAD_MODE_ALTERNATING_55_AA] = "Alternating 0x55/0xAA", + [TPG_PAYLOAD_MODE_RANDOM] = "Pseudo-random Data", + [TPG_PAYLOAD_MODE_USER_SPECIFIED] = "User Specified", + [TPG_PAYLOAD_MODE_COLOR_BARS] = "Color bars", +}; + +static int tpg_stream_on(struct tpg_device *tpg) +{ + struct tpg_testgen_config *tg = &tpg->testgen; + struct v4l2_mbus_framefmt *input_format; + const struct tpg_format_info *format; + u8 payload_mode = (tg->mode > TPG_PAYLOAD_MODE_DISABLED) ? + tg->mode - 1 : 0; + u8 lane_cnt = tpg->res->lane_cnt; + u8 vc, dt, last_vc = 0; + u32 val; + + for (vc = 0; vc <= MSM_TPG_ACTIVE_VC; vc++) { + last_vc = vc; + + input_format = &tpg->fmt; + format = tpg_get_fmt_entry(tpg->res->formats->formats, + tpg->res->formats->nformats, + input_format->code); + if (IS_ERR(format)) + return -EINVAL; + + /* VC configuration */ + val = FIELD_PREP(TPG_VC_n_CFG0_NUM_ACTIVE_DT, MSM_TPG_ACTIVE_DT) | + FIELD_PREP(TPG_VC_n_CFG0_NUM_FRAMES, 0); + writel(val, tpg->base + TPG_VC_n_CFG0(vc)); + + writel(TPG_LFSR_SEED_DEFAULT, tpg->base + TPG_VC_n_LSFR_SEED(vc)); + + val = DIV_ROUND_UP(input_format->width * format->bpp * TPG_HBI_PCT_DEFAULT, + BITS_PER_BYTE * lane_cnt * PERCENT_BASE); + writel(val, tpg->base + TPG_VC_n_HBI_CFG(vc)); + + val = input_format->height * TPG_VBI_PCT_DEFAULT / PERCENT_BASE; + writel(val, tpg->base + TPG_VC_n_VBI_CFG(vc)); + + writel(TPG_COLOR_BARS_CFG_STANDARD, tpg->base + TPG_VC_n_COLOR_BARS_CFG(vc)); + + /* DT configuration */ + for (dt = 0; dt <= MSM_TPG_ACTIVE_DT; dt++) { + val = FIELD_PREP(TPG_VC_m_DT_n_CFG_0_FRAME_HEIGHT, + input_format->height & 0xffff) | + FIELD_PREP(TPG_VC_m_DT_n_CFG_0_FRAME_WIDTH, + input_format->width & 0xffff); + writel(val, tpg->base + TPG_VC_m_DT_n_CFG_0(vc, dt)); + + val = FIELD_PREP(TPG_VC_m_DT_n_CFG_1_DATA_TYPE, format->data_type); + writel(val, tpg->base + TPG_VC_m_DT_n_CFG_1(vc, dt)); + + if (tpg->hw_version == TPG_HW_VER_2_0_0) { + val = FIELD_PREP(TPG_VC_m_DT_n_CFG_2_PAYLOAD_MODE, payload_mode) | + FIELD_PREP(TPG_V2_0_0_VC_m_DT_n_CFG_2_USER_SPECIFIED_PAYLOAD, + TPG_USER_SPECIFIED_PAYLOAD_DEFAULT) | + FIELD_PREP(TPG_V2_0_0_VC_m_DT_n_CFG_2_ENCODE_FORMAT, + format->encode_format); + } else if (tpg->hw_version >= TPG_HW_VER_2_1_0) { + val = FIELD_PREP(TPG_VC_m_DT_n_CFG_2_PAYLOAD_MODE, payload_mode) | + FIELD_PREP(TPG_V2_1_0_VC_m_DT_n_CFG_2_USER_SPECIFIED_PAYLOAD, + TPG_USER_SPECIFIED_PAYLOAD_DEFAULT) | + FIELD_PREP(TPG_V2_1_0_VC_m_DT_n_CFG_2_ENCODE_FORMAT, + format->encode_format); + } + writel(val, tpg->base + TPG_VC_m_DT_n_CFG_2(vc, dt)); + } + } + + /* Global TPG control */ + val = FIELD_PREP(TPG_CTRL_TEST_EN, 1) | + FIELD_PREP(TPG_CTRL_NUM_ACTIVE_LANES, lane_cnt - 1) | + FIELD_PREP(TPG_CTRL_NUM_ACTIVE_VC, last_vc); + writel(val, tpg->base + TPG_CTRL); + + return 0; +} + +static int tpg_reset(struct tpg_device *tpg) +{ + writel(0, tpg->base + TPG_CTRL); + writel(1, tpg->base + TPG_CLEAR); + + return 0; +} + +static void tpg_stream_off(struct tpg_device *tpg) +{ + tpg_reset(tpg); +} + +static int tpg_configure_stream(struct tpg_device *tpg, u8 enable) +{ + if (enable) + return tpg_stream_on(tpg); + + tpg_stream_off(tpg); + + return 0; +} + +static int tpg_configure_testgen_pattern(struct tpg_device *tpg, s32 val) +{ + if (val >= 0 && val <= TPG_PAYLOAD_MODE_COLOR_BARS) + tpg->testgen.mode = val; + + return 0; +} + +static u32 tpg_hw_version(struct tpg_device *tpg) +{ + u32 hw_version = readl(tpg->base + TPG_HW_VERSION); + + tpg->hw_version = hw_version; + dev_dbg(tpg->camss->dev, "tpg HW Version = %u.%u.%u\n", + (u32)FIELD_GET(HW_VERSION_GENERATION, hw_version), + (u32)FIELD_GET(HW_VERSION_REVISION, hw_version), + (u32)FIELD_GET(HW_VERSION_STEPPING, hw_version)); + + return hw_version; +} + +static void tpg_subdev_init(struct tpg_device *tpg) +{ + tpg->testgen.modes = testgen_payload_modes; + tpg->testgen.nmodes = TPG_PAYLOAD_MODE_NUM_SUPPORTED_GEN1; +} + +const struct tpg_hw_ops tpg_ops_gen1 = { + .configure_stream = tpg_configure_stream, + .configure_testgen_pattern = tpg_configure_testgen_pattern, + .hw_version = tpg_hw_version, + .reset = tpg_reset, + .subdev_init = tpg_subdev_init, +}; diff --git a/drivers/media/platform/qcom/camss/camss-tpg.c b/drivers/media/platform/qcom/camss/camss-tpg.c new file mode 100644 index 000000000000..c5b75132add4 --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-tpg.c @@ -0,0 +1,519 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * + * Qualcomm MSM Camera Subsystem - TPG Module + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ +#include <linux/clk.h> +#include <linux/io.h> +#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> +#include <media/mipi-csi2.h> + +#include "camss-tpg.h" +#include "camss.h" + +static const struct tpg_format_info formats_gen1[] = { + { + MEDIA_BUS_FMT_SBGGR8_1X8, + MIPI_CSI2_DT_RAW8, + ENCODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + }, + { + MEDIA_BUS_FMT_SGBRG8_1X8, + MIPI_CSI2_DT_RAW8, + ENCODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + }, + { + MEDIA_BUS_FMT_SGRBG8_1X8, + MIPI_CSI2_DT_RAW8, + ENCODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + }, + { + MEDIA_BUS_FMT_SRGGB8_1X8, + MIPI_CSI2_DT_RAW8, + ENCODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + }, + { + MEDIA_BUS_FMT_SBGGR10_1X10, + MIPI_CSI2_DT_RAW10, + ENCODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + }, + { + MEDIA_BUS_FMT_SGBRG10_1X10, + MIPI_CSI2_DT_RAW10, + ENCODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + }, + { + MEDIA_BUS_FMT_SGRBG10_1X10, + MIPI_CSI2_DT_RAW10, + ENCODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + }, + { + MEDIA_BUS_FMT_SRGGB10_1X10, + MIPI_CSI2_DT_RAW10, + ENCODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + }, + { + MEDIA_BUS_FMT_SBGGR12_1X12, + MIPI_CSI2_DT_RAW12, + ENCODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + }, + { + MEDIA_BUS_FMT_SGBRG12_1X12, + MIPI_CSI2_DT_RAW12, + ENCODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + }, + { + MEDIA_BUS_FMT_SGRBG12_1X12, + MIPI_CSI2_DT_RAW12, + ENCODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + }, + { + MEDIA_BUS_FMT_SRGGB12_1X12, + MIPI_CSI2_DT_RAW12, + ENCODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + }, + { + MEDIA_BUS_FMT_Y8_1X8, + MIPI_CSI2_DT_RAW8, + ENCODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + }, + { + MEDIA_BUS_FMT_Y10_1X10, + MIPI_CSI2_DT_RAW10, + ENCODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + }, +}; + +const struct tpg_formats tpg_formats_gen1 = { + .nformats = ARRAY_SIZE(formats_gen1), + .formats = formats_gen1 +}; + +const struct tpg_format_info *tpg_get_fmt_entry(const struct tpg_format_info *formats, + unsigned int nformats, + u32 code) +{ + unsigned int i; + + for (i = 0; i < nformats; i++) + if (code == formats[i].code) + return &formats[i]; + + return ERR_PTR(-EINVAL); +} + +static int tpg_set_clock_rates(struct tpg_device *tpg) +{ + struct device *dev = tpg->camss->dev; + int i, ret; + + for (i = 0; i < tpg->nclocks; i++) { + struct camss_clock *clock = &tpg->clock[i]; + long round_rate; + + if (clock->freq) { + round_rate = clk_round_rate(clock->clk, clock->freq[0]); + if (round_rate < 0) { + dev_err(dev, "clk round rate failed: %ld\n", + round_rate); + return -EINVAL; + } + + ret = clk_set_rate(clock->clk, round_rate); + if (ret < 0) { + dev_err(dev, "clk set rate failed: %d\n", ret); + return ret; + } + } + } + + return 0; +} + +static int tpg_set_power(struct v4l2_subdev *sd, int on) +{ + struct tpg_device *tpg = v4l2_get_subdevdata(sd); + struct device *dev = tpg->camss->dev; + + if (on) { + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + ret = tpg_set_clock_rates(tpg); + if (ret < 0) { + pm_runtime_put_sync(dev); + return ret; + } + + ret = camss_enable_clocks(tpg->nclocks, tpg->clock, dev); + if (ret < 0) { + pm_runtime_put_sync(dev); + return ret; + } + + tpg->res->hw_ops->reset(tpg); + + tpg->res->hw_ops->hw_version(tpg); + } else { + camss_disable_clocks(tpg->nclocks, tpg->clock); + + pm_runtime_put_sync(dev); + } + + return 0; +} + +static int tpg_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct tpg_device *tpg = v4l2_get_subdevdata(sd); + int ret; + + if (enable) { + ret = v4l2_ctrl_handler_setup(&tpg->ctrls); + if (ret < 0) { + dev_err(tpg->camss->dev, + "could not sync v4l2 controls: %d\n", ret); + return ret; + } + } + + return tpg->res->hw_ops->configure_stream(tpg, enable); +} + +static struct v4l2_mbus_framefmt * +__tpg_get_format(struct tpg_device *tpg, + struct v4l2_subdev_state *sd_state, + unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_state_get_format(sd_state, + pad); + + return &tpg->fmt; +} + +static void tpg_try_format(struct tpg_device *tpg, + struct v4l2_mbus_framefmt *fmt) +{ + unsigned int i; + + for (i = 0; i < tpg->res->formats->nformats; i++) + if (tpg->res->formats->formats[i].code == fmt->code) + break; + + if (i >= tpg->res->formats->nformats) + fmt->code = MEDIA_BUS_FMT_SBGGR8_1X8; + + fmt->width = clamp_t(u32, fmt->width, TPG_MIN_WIDTH, TPG_MAX_WIDTH); + fmt->height = clamp_t(u32, fmt->height, TPG_MIN_HEIGHT, TPG_MAX_HEIGHT); + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_SRGB; +} + +static int tpg_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct tpg_device *tpg = v4l2_get_subdevdata(sd); + + if (code->index >= tpg->res->formats->nformats) + return -EINVAL; + + code->code = tpg->res->formats->formats[code->index].code; + + return 0; +} + +static int tpg_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct tpg_device *tpg = v4l2_get_subdevdata(sd); + unsigned int i; + + if (fse->index != 0) + return -EINVAL; + + for (i = 0; i < tpg->res->formats->nformats; i++) + if (tpg->res->formats->formats[i].code == fse->code) + break; + + if (i >= tpg->res->formats->nformats) + return -EINVAL; + + fse->min_width = TPG_MIN_WIDTH; + fse->min_height = TPG_MIN_HEIGHT; + fse->max_width = TPG_MAX_WIDTH; + fse->max_height = TPG_MAX_HEIGHT; + + return 0; +} + +static int tpg_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct tpg_device *tpg = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __tpg_get_format(tpg, sd_state, fmt->pad, fmt->which); + if (!format) + return -EINVAL; + + fmt->format = *format; + + return 0; +} + +static int tpg_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct tpg_device *tpg = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __tpg_get_format(tpg, sd_state, fmt->pad, fmt->which); + if (!format) + return -EINVAL; + + tpg_try_format(tpg, &fmt->format); + *format = fmt->format; + + return 0; +} + +static int tpg_init_formats(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct v4l2_subdev_format format = { + .pad = MSM_TPG_PAD_SRC, + .which = fh ? V4L2_SUBDEV_FORMAT_TRY : + V4L2_SUBDEV_FORMAT_ACTIVE, + .format = { + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .width = 1920, + .height = 1080, + } + }; + + return tpg_set_format(sd, fh ? fh->state : NULL, &format); +} + +static int tpg_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct tpg_device *tpg = container_of(ctrl->handler, + struct tpg_device, ctrls); + int ret = -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_TEST_PATTERN: + ret = tpg->res->hw_ops->configure_testgen_pattern(tpg, ctrl->val); + break; + } + + return ret; +} + +static const struct v4l2_ctrl_ops tpg_ctrl_ops = { + .s_ctrl = tpg_s_ctrl, +}; + +int msm_tpg_subdev_init(struct camss *camss, + struct tpg_device *tpg, + const struct camss_subdev_resources *res, u8 id) +{ + struct platform_device *pdev; + struct device *dev; + int i, j; + + dev = camss->dev; + pdev = to_platform_device(dev); + + tpg->camss = camss; + tpg->id = id; + tpg->res = &res->tpg; + tpg->res->hw_ops->subdev_init(tpg); + + tpg->base = devm_platform_ioremap_resource_byname(pdev, res->reg[0]); + if (IS_ERR(tpg->base)) + return PTR_ERR(tpg->base); + + tpg->nclocks = 0; + while (res->clock[tpg->nclocks]) + tpg->nclocks++; + + if (!tpg->nclocks) + return 0; + + tpg->clock = devm_kcalloc(dev, tpg->nclocks, + sizeof(*tpg->clock), GFP_KERNEL); + if (!tpg->clock) + return -ENOMEM; + + for (i = 0; i < tpg->nclocks; i++) { + struct camss_clock *clock = &tpg->clock[i]; + + clock->clk = devm_clk_get(dev, res->clock[i]); + if (IS_ERR(clock->clk)) + return PTR_ERR(clock->clk); + + clock->name = res->clock[i]; + + clock->nfreqs = 0; + while (res->clock_rate[i][clock->nfreqs]) + clock->nfreqs++; + + if (!clock->nfreqs) { + clock->freq = NULL; + continue; + } + + clock->freq = devm_kcalloc(dev, clock->nfreqs, + sizeof(*clock->freq), GFP_KERNEL); + if (!clock->freq) + return -ENOMEM; + + for (j = 0; j < clock->nfreqs; j++) + clock->freq[j] = res->clock_rate[i][j]; + } + + return 0; +} + +static int tpg_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + if (flags & MEDIA_LNK_FL_ENABLED) + if (media_pad_remote_pad_first(local)) + return -EBUSY; + + return 0; +} + +static const struct v4l2_subdev_core_ops tpg_core_ops = { + .s_power = tpg_set_power, +}; + +static const struct v4l2_subdev_video_ops tpg_video_ops = { + .s_stream = tpg_set_stream, +}; + +static const struct v4l2_subdev_pad_ops tpg_pad_ops = { + .enum_mbus_code = tpg_enum_mbus_code, + .enum_frame_size = tpg_enum_frame_size, + .get_fmt = tpg_get_format, + .set_fmt = tpg_set_format, +}; + +static const struct v4l2_subdev_ops tpg_v4l2_ops = { + .core = &tpg_core_ops, + .video = &tpg_video_ops, + .pad = &tpg_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops tpg_v4l2_internal_ops = { + .open = tpg_init_formats, +}; + +static const struct media_entity_operations tpg_media_ops = { + .link_setup = tpg_link_setup, + .link_validate = v4l2_subdev_link_validate, +}; + +int msm_tpg_register_entity(struct tpg_device *tpg, + struct v4l2_device *v4l2_dev) +{ + struct v4l2_subdev *sd = &tpg->subdev; + struct device *dev = tpg->camss->dev; + int ret; + + v4l2_subdev_init(sd, &tpg_v4l2_ops); + sd->internal_ops = &tpg_v4l2_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; + snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d", + "msm_tpg", tpg->id); + sd->grp_id = TPG_GRP_ID; + v4l2_set_subdevdata(sd, tpg); + + ret = v4l2_ctrl_handler_init(&tpg->ctrls, 1); + if (ret < 0) { + dev_err(dev, "Failed to init ctrl handler: %d\n", ret); + return ret; + } + + tpg->testgen_mode = v4l2_ctrl_new_std_menu_items(&tpg->ctrls, + &tpg_ctrl_ops, V4L2_CID_TEST_PATTERN, + tpg->testgen.nmodes, 0, 0, + tpg->testgen.modes); + if (tpg->ctrls.error) { + dev_err(dev, "Failed to init ctrl: %d\n", tpg->ctrls.error); + ret = tpg->ctrls.error; + goto free_ctrl; + } + + tpg->subdev.ctrl_handler = &tpg->ctrls; + + ret = tpg_init_formats(sd, NULL); + if (ret < 0) { + dev_err(dev, "Failed to init format: %d\n", ret); + goto free_ctrl; + } + + tpg->pad.flags = MEDIA_PAD_FL_SOURCE; + + sd->entity.ops = &tpg_media_ops; + ret = media_entity_pads_init(&sd->entity, 1, &tpg->pad); + if (ret < 0) { + dev_err(dev, "Failed to init media entity: %d\n", ret); + goto free_ctrl; + } + + ret = v4l2_device_register_subdev(v4l2_dev, sd); + if (ret < 0) { + dev_err(dev, "Failed to register subdev: %d\n", ret); + media_entity_cleanup(&sd->entity); + goto free_ctrl; + } + + return 0; + +free_ctrl: + v4l2_ctrl_handler_free(&tpg->ctrls); + + return ret; +} + +void msm_tpg_unregister_entity(struct tpg_device *tpg) +{ + v4l2_device_unregister_subdev(&tpg->subdev); + media_entity_cleanup(&tpg->subdev.entity); + v4l2_ctrl_handler_free(&tpg->ctrls); +} diff --git a/drivers/media/platform/qcom/camss/camss-tpg.h b/drivers/media/platform/qcom/camss/camss-tpg.h new file mode 100644 index 000000000000..7fb35a97dd06 --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-tpg.h @@ -0,0 +1,118 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * camss-tpg.h + * + * Qualcomm MSM Camera Subsystem - TPG Module + * + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ +#ifndef QC_MSM_CAMSS_TPG_H +#define QC_MSM_CAMSS_TPG_H + +#include <linux/clk.h> +#include <linux/bitfield.h> +#include <media/media-entity.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-mediabus.h> +#include <media/v4l2-subdev.h> + +#define ENCODE_FORMAT_UNCOMPRESSED_8_BIT 0x1 +#define ENCODE_FORMAT_UNCOMPRESSED_10_BIT 0x2 +#define ENCODE_FORMAT_UNCOMPRESSED_12_BIT 0x3 +#define ENCODE_FORMAT_UNCOMPRESSED_14_BIT 0x4 +#define ENCODE_FORMAT_UNCOMPRESSED_16_BIT 0x5 +#define ENCODE_FORMAT_UNCOMPRESSED_20_BIT 0x6 +#define ENCODE_FORMAT_UNCOMPRESSED_24_BIT 0x7 + +#define MSM_TPG_PAD_SRC 0 +#define MSM_TPG_ACTIVE_VC 0 +#define MSM_TPG_ACTIVE_DT 0 + +#define TPG_MIN_WIDTH 1 +#define TPG_MIN_HEIGHT 1 +#define TPG_MAX_WIDTH 8191 +#define TPG_MAX_HEIGHT 8191 + +#define TPG_GRP_ID 0 + +enum tpg_testgen_mode { + TPG_PAYLOAD_MODE_DISABLED = 0, + TPG_PAYLOAD_MODE_INCREMENTING = 1, + TPG_PAYLOAD_MODE_ALTERNATING_55_AA = 2, + TPG_PAYLOAD_MODE_RANDOM = 5, + TPG_PAYLOAD_MODE_USER_SPECIFIED = 6, + TPG_PAYLOAD_MODE_COLOR_BARS = 9, + TPG_PAYLOAD_MODE_NUM_SUPPORTED_GEN1 = 9, +}; + +struct tpg_testgen_config { + enum tpg_testgen_mode mode; + const char * const*modes; + u8 nmodes; +}; + +struct tpg_format_info { + u32 code; + u8 data_type; + u8 encode_format; + u8 bpp; +}; + +struct tpg_formats { + unsigned int nformats; + const struct tpg_format_info *formats; +}; + +struct tpg_device; + +struct tpg_hw_ops { + int (*configure_stream)(struct tpg_device *tpg, u8 enable); + int (*configure_testgen_pattern)(struct tpg_device *tpg, s32 val); + u32 (*hw_version)(struct tpg_device *tpg); + int (*reset)(struct tpg_device *tpg); + void (*subdev_init)(struct tpg_device *tpg); +}; + +struct tpg_subdev_resources { + u8 lane_cnt; + const struct tpg_formats *formats; + const struct tpg_hw_ops *hw_ops; +}; + +struct tpg_device { + struct camss *camss; + u8 id; + struct v4l2_subdev subdev; + struct media_pad pad; + void __iomem *base; + struct camss_clock *clock; + int nclocks; + struct tpg_testgen_config testgen; + struct v4l2_mbus_framefmt fmt; + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *testgen_mode; + const struct tpg_subdev_resources *res; + u32 hw_version; +}; + +struct camss_subdev_resources; + +const struct tpg_format_info *tpg_get_fmt_entry(const struct tpg_format_info *formats, + unsigned int nformats, + u32 code); + +int msm_tpg_subdev_init(struct camss *camss, + struct tpg_device *tpg, + const struct camss_subdev_resources *res, u8 id); + +int msm_tpg_register_entity(struct tpg_device *tpg, + struct v4l2_device *v4l2_dev); + +void msm_tpg_unregister_entity(struct tpg_device *tpg); + +extern const struct tpg_formats tpg_formats_gen1; + +extern const struct tpg_hw_ops tpg_ops_gen1; + +#endif /* QC_MSM_CAMSS_TPG_H */ diff --git a/drivers/media/platform/qcom/camss/camss-vfe-340.c b/drivers/media/platform/qcom/camss/camss-vfe-340.c index 30d7630b3e8b..2526d568686d 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-340.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-340.c @@ -54,6 +54,7 @@ #define TFE_BUS_CLIENT_CFG(c) BUS_REG(0x200 + (c) * 0x100) #define TFE_BUS_CLIENT_CFG_EN BIT(0) +#define TFE_BUS_CLIENT_CFG_AUTORECOVER BIT(4) #define TFE_BUS_CLIENT_CFG_MODE_FRAME BIT(16) #define TFE_BUS_IMAGE_ADDR(c) BUS_REG(0x204 + (c) * 0x100) #define TFE_BUS_FRAME_INCR(c) BUS_REG(0x208 + (c) * 0x100) @@ -63,30 +64,36 @@ #define TFE_BUS_IMAGE_CFG_2(c) BUS_REG(0x214 + (c) * 0x100) #define TFE_BUS_IMAGE_CFG_2_DEFAULT 0xffff #define TFE_BUS_PACKER_CFG(c) BUS_REG(0x218 + (c) * 0x100) +#define TFE_BUS_PACKER_CFG_FMT_PLAIN8 0x1 #define TFE_BUS_PACKER_CFG_FMT_PLAIN64 0xa +#define TFE_BUS_PACKER_CFG_FMT_MIPI10 0xc +#define TFE_BUS_PACKER_CFG_FMT_MIPI12 0xd #define TFE_BUS_IRQ_SUBSAMPLE_CFG_0(c) BUS_REG(0x230 + (c) * 0x100) #define TFE_BUS_IRQ_SUBSAMPLE_CFG_1(c) BUS_REG(0x234 + (c) * 0x100) #define TFE_BUS_FRAMEDROP_CFG_0(c) BUS_REG(0x238 + (c) * 0x100) #define TFE_BUS_FRAMEDROP_CFG_1(c) BUS_REG(0x23c + (c) * 0x100) -/* - * TODO: differentiate the port id based on requested type of RDI, BHIST etc - * - * TFE write master IDs (clients) - * - * BAYER 0 - * IDEAL_RAW 1 - * STATS_TINTLESS_BG 2 - * STATS_BHIST 3 - * STATS_AWB_BG 4 - * STATS_AEC_BG 5 - * STATS_BAF 6 - * RDI0 7 - * RDI1 8 - * RDI2 9 - */ -#define RDI_WM(n) (7 + (n)) -#define TFE_WM_NUM 10 +#define PP_CROP_REG(a) (0x2800 + (a)) +#define TFE_PP_CROP_CFG PP_CROP_REG(0x60) +#define TFE_PP_CROP_CFG_EN (BIT(0) | BIT(9)) +#define TFE_PP_CROP_LINE_CFG PP_CROP_REG(0x68) +#define TFE_PP_CROP_FIRST GENMASK(29, 16) +#define TFE_PP_CROP_LAST GENMASK(13, 0) +#define TFE_PP_CROP_PIX_CFG PP_CROP_REG(0x6C) + +enum tfe_client { + TFE_CLI_BAYER, + TFE_CLI_IDEAL_RAW, + TFE_CLI_STATS_TINTLESS_BG, + TFE_CLI_STATS_BHIST, + TFE_CLI_STATS_AWB_BG, + TFE_CLI_STATS_AEC_BG, + TFE_CLI_STATS_BAF, + TFE_CLI_RDI0, + TFE_CLI_RDI1, + TFE_CLI_RDI2, + TFE_CLI_NUM +}; enum tfe_iface { TFE_IFACE_PIX, @@ -108,6 +115,13 @@ enum tfe_subgroups { TFE_SUBGROUP_NUM }; +static enum tfe_client tfe_wm_client_map[VFE_LINE_NUM_MAX] = { + [VFE_LINE_RDI0] = TFE_CLI_RDI0, + [VFE_LINE_RDI1] = TFE_CLI_RDI1, + [VFE_LINE_RDI2] = TFE_CLI_RDI2, + [VFE_LINE_PIX] = TFE_CLI_BAYER, +}; + static enum tfe_iface tfe_line_iface_map[VFE_LINE_NUM_MAX] = { [VFE_LINE_RDI0] = TFE_IFACE_RDI0, [VFE_LINE_RDI1] = TFE_IFACE_RDI1, @@ -209,10 +223,10 @@ static irqreturn_t vfe_isr(int irq, void *dev) status = readl_relaxed(vfe->base + TFE_BUS_OVERFLOW_STATUS); if (status) { writel_relaxed(status, vfe->base + TFE_BUS_STATUS_CLEAR); - for (i = 0; i < TFE_WM_NUM; i++) { + for (i = 0; i < TFE_CLI_NUM; i++) { if (status & BIT(i)) dev_err_ratelimited(vfe->camss->dev, - "VFE%u: bus overflow for wm %u\n", + "VFE%u: bus overflow for client %u\n", vfe->id, i); } } @@ -235,49 +249,99 @@ static void vfe_enable_irq(struct vfe_device *vfe) TFE_BUS_IRQ_MASK_0_IMG_VIOL, vfe->base + TFE_BUS_IRQ_MASK_0); } -static void vfe_wm_update(struct vfe_device *vfe, u8 rdi, u32 addr, +static void vfe_wm_update(struct vfe_device *vfe, u8 wm, u32 addr, struct vfe_line *line) { - u8 wm = RDI_WM(rdi); + u8 client = tfe_wm_client_map[wm]; + + writel_relaxed(addr, vfe->base + TFE_BUS_IMAGE_ADDR(client)); +} + +static u32 vfe_packer_format(struct vfe_device *vfe, u32 pixelformat) +{ + const struct camss_formats *fmt = vfe->res->formats_rdi; + unsigned int bpp = 0; + int i; - writel_relaxed(addr, vfe->base + TFE_BUS_IMAGE_ADDR(wm)); + for (i = 0; i < fmt->nformats; i++) { + if (fmt->formats[i].pixelformat == pixelformat) { + bpp = fmt->formats[i].mbus_bpp; + break; + } + } + + switch (bpp) { + case 10: + return TFE_BUS_PACKER_CFG_FMT_MIPI10; + case 12: + return TFE_BUS_PACKER_CFG_FMT_MIPI12; + default: + return TFE_BUS_PACKER_CFG_FMT_PLAIN8; + } } -static void vfe_wm_start(struct vfe_device *vfe, u8 rdi, struct vfe_line *line) +static void vfe_wm_start(struct vfe_device *vfe, u8 wm, struct vfe_line *line) { struct v4l2_pix_format_mplane *pix = &line->video_out.active_fmt.fmt.pix_mp; u32 stride = pix->plane_fmt[0].bytesperline; - u8 wm = RDI_WM(rdi); - - /* Configuration for plain RDI frames */ - writel_relaxed(TFE_BUS_IMAGE_CFG_0_DEFAULT, vfe->base + TFE_BUS_IMAGE_CFG_0(wm)); - writel_relaxed(0u, vfe->base + TFE_BUS_IMAGE_CFG_1(wm)); - writel_relaxed(TFE_BUS_IMAGE_CFG_2_DEFAULT, vfe->base + TFE_BUS_IMAGE_CFG_2(wm)); - writel_relaxed(stride * pix->height, vfe->base + TFE_BUS_FRAME_INCR(wm)); - writel_relaxed(TFE_BUS_PACKER_CFG_FMT_PLAIN64, vfe->base + TFE_BUS_PACKER_CFG(wm)); + u8 client = tfe_wm_client_map[wm]; + u32 cfg = TFE_BUS_CLIENT_CFG_EN; + + if (client == TFE_CLI_BAYER) { /* PIX - Line based */ + struct v4l2_rect *crop = &line->crop; + + /* Cropping */ + writel_relaxed(TFE_PP_CROP_CFG_EN, vfe->base + TFE_PP_CROP_CFG); + writel_relaxed(FIELD_PREP(TFE_PP_CROP_FIRST, crop->top) | + FIELD_PREP(TFE_PP_CROP_LAST, crop->top + crop->height - 1), + vfe->base + TFE_PP_CROP_LINE_CFG); + writel_relaxed(FIELD_PREP(TFE_PP_CROP_FIRST, crop->left) | + FIELD_PREP(TFE_PP_CROP_LAST, crop->left + crop->width - 1), + vfe->base + TFE_PP_CROP_PIX_CFG); + + /* Write Engine */ + writel_relaxed(pix->width | (pix->height << 16), + vfe->base + TFE_BUS_IMAGE_CFG_0(client)); + writel_relaxed(0u, vfe->base + TFE_BUS_IMAGE_CFG_1(client)); + writel_relaxed(stride, vfe->base + TFE_BUS_IMAGE_CFG_2(client)); + writel_relaxed(stride * pix->height, vfe->base + TFE_BUS_FRAME_INCR(client)); + writel_relaxed(vfe_packer_format(vfe, pix->pixelformat), + vfe->base + TFE_BUS_PACKER_CFG(client)); + + cfg |= TFE_BUS_CLIENT_CFG_AUTORECOVER; + } else { /* RDI - Frame based */ + writel_relaxed(TFE_BUS_IMAGE_CFG_0_DEFAULT, + vfe->base + TFE_BUS_IMAGE_CFG_0(client)); + writel_relaxed(0u, vfe->base + TFE_BUS_IMAGE_CFG_1(client)); + writel_relaxed(TFE_BUS_IMAGE_CFG_2_DEFAULT, + vfe->base + TFE_BUS_IMAGE_CFG_2(client)); + writel_relaxed(stride * pix->height, vfe->base + TFE_BUS_FRAME_INCR(client)); + writel_relaxed(TFE_BUS_PACKER_CFG_FMT_PLAIN64, + vfe->base + TFE_BUS_PACKER_CFG(client)); + cfg |= TFE_BUS_CLIENT_CFG_MODE_FRAME; + } /* No dropped frames, one irq per frame */ - writel_relaxed(0, vfe->base + TFE_BUS_FRAMEDROP_CFG_0(wm)); - writel_relaxed(1, vfe->base + TFE_BUS_FRAMEDROP_CFG_1(wm)); - writel_relaxed(0, vfe->base + TFE_BUS_IRQ_SUBSAMPLE_CFG_0(wm)); - writel_relaxed(1, vfe->base + TFE_BUS_IRQ_SUBSAMPLE_CFG_1(wm)); + writel_relaxed(0, vfe->base + TFE_BUS_FRAMEDROP_CFG_0(client)); + writel_relaxed(1, vfe->base + TFE_BUS_FRAMEDROP_CFG_1(client)); + writel_relaxed(0, vfe->base + TFE_BUS_IRQ_SUBSAMPLE_CFG_0(client)); + writel_relaxed(1, vfe->base + TFE_BUS_IRQ_SUBSAMPLE_CFG_1(client)); vfe_enable_irq(vfe); - writel(TFE_BUS_CLIENT_CFG_EN | TFE_BUS_CLIENT_CFG_MODE_FRAME, - vfe->base + TFE_BUS_CLIENT_CFG(wm)); + writel(cfg, vfe->base + TFE_BUS_CLIENT_CFG(client)); - dev_dbg(vfe->camss->dev, "VFE%u: Started RDI%u width %u height %u stride %u\n", - vfe->id, rdi, pix->width, pix->height, stride); + dev_dbg(vfe->camss->dev, "VFE%u: Started client %u width %u height %u stride %u\n", + vfe->id, client, pix->width, pix->height, stride); } -static void vfe_wm_stop(struct vfe_device *vfe, u8 rdi) +static void vfe_wm_stop(struct vfe_device *vfe, u8 wm) { - u8 wm = RDI_WM(rdi); + u8 client = tfe_wm_client_map[wm]; - writel(0, vfe->base + TFE_BUS_CLIENT_CFG(wm)); + writel(0, vfe->base + TFE_BUS_CLIENT_CFG(client)); - dev_dbg(vfe->camss->dev, "VFE%u: Stopped RDI%u\n", vfe->id, rdi); + dev_dbg(vfe->camss->dev, "VFE%u: Stopped client %u\n", vfe->id, client); } static const struct camss_video_ops vfe_video_ops_520 = { diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index 5baf0e3d4bc4..319d19158988 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -343,6 +343,7 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, case CAMSS_660: case CAMSS_2290: case CAMSS_6150: + case CAMSS_6350: case CAMSS_7280: case CAMSS_8x96: case CAMSS_8250: @@ -1997,12 +1998,13 @@ static const struct media_entity_operations vfe_media_ops = { .link_validate = v4l2_subdev_link_validate, }; -static int vfe_bpl_align(struct vfe_device *vfe) +static int vfe_bpl_align_rdi(struct vfe_device *vfe) { int ret = 8; switch (vfe->camss->res->version) { case CAMSS_6150: + case CAMSS_6350: case CAMSS_7280: case CAMSS_8250: case CAMSS_8280XP: @@ -2021,6 +2023,24 @@ static int vfe_bpl_align(struct vfe_device *vfe) return ret; } +static int vfe_bpl_align_pix(struct vfe_device *vfe) +{ + int ret = 16; + + switch (vfe->camss->res->version) { + case CAMSS_2290: + /* The alignment/bpl depends solely on the pixel format and is + * computed dynamically in camss_format_get_bpl_alignment(). + */ + ret = 0; + break; + default: + break; + } + + return ret; +} + /* * msm_vfe_register_entities - Register subdev node for VFE module * @vfe: VFE device @@ -2053,7 +2073,7 @@ int msm_vfe_register_entities(struct vfe_device *vfe, v4l2_subdev_init(sd, &vfe_v4l2_ops); sd->internal_ops = &vfe_v4l2_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - if (i == VFE_LINE_PIX) + if (i == VFE_LINE_PIX && vfe->res->is_lite == false) snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s", MSM_VFE_NAME, vfe->id, "pix"); else @@ -2087,11 +2107,12 @@ int msm_vfe_register_entities(struct vfe_device *vfe, } video_out->ops = &vfe->video_ops; - video_out->bpl_alignment = vfe_bpl_align(vfe); - video_out->line_based = 0; if (i == VFE_LINE_PIX) { - video_out->bpl_alignment = 16; + video_out->bpl_alignment = vfe_bpl_align_pix(vfe); video_out->line_based = 1; + } else { + video_out->bpl_alignment = vfe_bpl_align_rdi(vfe); + video_out->line_based = 0; } video_out->nformats = vfe->line[i].nformats; diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index 831486e14754..0852eb6f1315 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -47,6 +47,9 @@ static int video_mbus_to_pix_mp(const struct v4l2_mbus_framefmt *mbus, unsigned int i; u32 bytesperline; + if (!alignment) + alignment = camss_format_get_bpl_alignment(f); + memset(pix, 0, sizeof(*pix)); v4l2_fill_pix_format_mplane(pix, mbus); pix->pixelformat = f->pixelformat; @@ -54,7 +57,7 @@ static int video_mbus_to_pix_mp(const struct v4l2_mbus_framefmt *mbus, for (i = 0; i < pix->num_planes; i++) { bytesperline = pix->width / f->hsub[i].numerator * f->hsub[i].denominator * f->bpp[i] / 8; - bytesperline = ALIGN(bytesperline, alignment); + bytesperline = roundup(bytesperline, alignment); pix->plane_fmt[i].bytesperline = bytesperline; pix->plane_fmt[i].sizeimage = pix->height / f->vsub[i].numerator * f->vsub[i].denominator * @@ -215,6 +218,12 @@ static int video_check_format(struct camss_video *video) if (ret < 0) return ret; + dev_dbg(video->camss->dev, + "%s: format is (%ux%u %p4cc/%up field:%u), trying (%ux%u %p4cc/%up field:%u)", + video->vdev.name, sd_pix->width, sd_pix->height, &sd_pix->pixelformat, + sd_pix->num_planes, sd_pix->field, pix->width, pix->height, &pix->pixelformat, + pix->num_planes, pix->field); + if (pix->pixelformat != sd_pix->pixelformat || pix->height != sd_pix->height || pix->width != sd_pix->width || @@ -453,6 +462,7 @@ static int video_g_fmt(struct file *file, void *fh, struct v4l2_format *f) static int __video_try_fmt(struct camss_video *video, struct v4l2_format *f) { + unsigned int alignment = video->bpl_alignment; struct v4l2_pix_format_mplane *pix_mp; const struct camss_format_info *fi; struct v4l2_plane_pix_format *p; @@ -485,6 +495,9 @@ static int __video_try_fmt(struct camss_video *video, struct v4l2_format *f) width = pix_mp->width; height = pix_mp->height; + if (!alignment) + alignment = camss_format_get_bpl_alignment(fi); + memset(pix_mp, 0, sizeof(*pix_mp)); pix_mp->pixelformat = fi->pixelformat; @@ -494,7 +507,7 @@ static int __video_try_fmt(struct camss_video *video, struct v4l2_format *f) for (i = 0; i < pix_mp->num_planes; i++) { bpl = pix_mp->width / fi->hsub[i].numerator * fi->hsub[i].denominator * fi->bpp[i] / 8; - bpl = ALIGN(bpl, video->bpl_alignment); + bpl = roundup(bpl, alignment); pix_mp->plane_fmt[i].bytesperline = bpl; pix_mp->plane_fmt[i].sizeimage = pix_mp->height / fi->vsub[i].numerator * fi->vsub[i].denominator * bpl; @@ -519,7 +532,7 @@ static int __video_try_fmt(struct camss_video *video, struct v4l2_format *f) lines = p->sizeimage / p->bytesperline; if (p->bytesperline < bytesperline[i]) - p->bytesperline = ALIGN(bytesperline[i], 8); + p->bytesperline = roundup(bytesperline[i], alignment); if (p->sizeimage < p->bytesperline * lines) p->sizeimage = p->bytesperline * lines; diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 9335636d7c4d..2123f6388e3d 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -1703,6 +1703,253 @@ static const struct resources_icc icc_res_sm6150[] = { }, }; +static const struct camss_subdev_resources csiphy_res_sm6350[] = { + /* CSIPHY0 */ + { + .regulators = { + { .supply = "vdd-csiphy0-0p9", .init_load_uA = 80000 }, + { .supply = "vdd-csiphy0-1p25", .init_load_uA = 80000 }, + }, + .clock = { "csiphy0", "csiphy0_timer" }, + .clock_rate = { { 300000000, 384000000, 400000000 }, + { 300000000 } }, + .reg = { "csiphy0" }, + .interrupt = { "csiphy0" }, + .csiphy = { + .id = 0, + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } + }, + /* CSIPHY1 */ + { + .regulators = { + { .supply = "vdd-csiphy1-0p9", .init_load_uA = 80000 }, + { .supply = "vdd-csiphy1-1p25", .init_load_uA = 80000 }, + }, + .clock = { "csiphy1", "csiphy1_timer" }, + .clock_rate = { { 300000000, 384000000, 400000000 }, + { 300000000 } }, + .reg = { "csiphy1" }, + .interrupt = { "csiphy1" }, + .csiphy = { + .id = 1, + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } + }, + /* CSIPHY2 */ + { + .regulators = { + { .supply = "vdd-csiphy2-0p9", .init_load_uA = 80000 }, + { .supply = "vdd-csiphy2-1p25", .init_load_uA = 80000 }, + }, + .clock = { "csiphy2", "csiphy2_timer" }, + .clock_rate = { { 300000000, 384000000, 400000000 }, + { 300000000 } }, + .reg = { "csiphy2" }, + .interrupt = { "csiphy2" }, + .csiphy = { + .id = 2, + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } + }, + /* CSIPHY3 */ + { + .regulators = { + { .supply = "vdd-csiphy3-0p9", .init_load_uA = 80000 }, + { .supply = "vdd-csiphy3-1p25", .init_load_uA = 80000 }, + }, + .clock = { "csiphy3", "csiphy3_timer" }, + .clock_rate = { { 300000000, 384000000, 400000000 }, + { 300000000 } }, + .reg = { "csiphy3" }, + .interrupt = { "csiphy3" }, + .csiphy = { + .id = 3, + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } + } +}; + +static const struct camss_subdev_resources csid_res_sm6350[] = { + /* CSID0 */ + { + .regulators = {}, + .clock = { "vfe0_csid", "vfe0_cphy_rx", "vfe0" }, + .clock_rate = { { 300000000, 384000000, 400000000 }, + { 0 }, + { 320000000, 404000000, 480000000, 600000000 } }, + .reg = { "csid0" }, + .interrupt = { "csid0" }, + .csid = { + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + }, + /* CSID1 */ + { + .regulators = {}, + .clock = { "vfe1_csid", "vfe1_cphy_rx", "vfe1" }, + .clock_rate = { { 300000000, 384000000, 400000000 }, + { 0 }, + { 320000000, 404000000, 480000000, 600000000 } }, + .reg = { "csid1" }, + .interrupt = { "csid1" }, + .csid = { + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + }, + /* CSID2 */ + { + .regulators = {}, + .clock = { "vfe2_csid", "vfe2_cphy_rx", "vfe2" }, + .clock_rate = { { 300000000, 384000000, 400000000 }, + { 0 }, + { 320000000, 404000000, 480000000, 600000000 } }, + .reg = { "csid2" }, + .interrupt = { "csid2" }, + .csid = { + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + }, + /* CSID3 (lite) */ + { + .regulators = {}, + .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx", "vfe_lite" }, + .clock_rate = { { 300000000, 384000000, 400000000 }, + { 0 }, + { 400000000, 480000000 } }, + .reg = { "csid_lite" }, + .interrupt = { "csid_lite" }, + .csid = { + .is_lite = true, + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + } +}; + +static const struct camss_subdev_resources vfe_res_sm6350[] = { + /* VFE0 */ + { + .regulators = {}, + .clock = { "cpas_ahb", "camnoc_axi", "vfe0", + "vfe0_axi", "cam_axi", "soc_ahb" }, + .clock_rate = { { 19200000 }, + { 0 }, + { 320000000, 404000000, 480000000, 600000000 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "vfe0" }, + .interrupt = { "vfe0" }, + .vfe = { + .line_num = 3, + .has_pd = true, + .pd_name = "ife0", + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE1 */ + { + .regulators = {}, + .clock = { "cpas_ahb", "camnoc_axi", "vfe1", + "vfe1_axi", "cam_axi", "soc_ahb" }, + .clock_rate = { { 19200000 }, + { 0 }, + { 320000000, 404000000, 480000000, 600000000 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "vfe1" }, + .interrupt = { "vfe1" }, + .vfe = { + .line_num = 3, + .has_pd = true, + .pd_name = "ife1", + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE2 */ + { + .regulators = {}, + .clock = { "cpas_ahb", "camnoc_axi", "vfe2", + "vfe2_axi", "cam_axi", "soc_ahb" }, + .clock_rate = { { 19200000 }, + { 0 }, + { 320000000, 404000000, 480000000, 600000000 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "vfe2" }, + .interrupt = { "vfe2" }, + .vfe = { + .line_num = 3, + .has_pd = true, + .pd_name = "ife2", + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE3 (lite) */ + { + .regulators = {}, + .clock = { "cpas_ahb", "camnoc_axi", "vfe_lite", + "cam_axi", "soc_ahb" }, + .clock_rate = { { 19200000 }, + { 0 }, + { 400000000, 480000000 }, + { 0 }, + { 0 } }, + .reg = { "vfe_lite" }, + .interrupt = { "vfe_lite" }, + .vfe = { + .is_lite = true, + .line_num = 4, + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, +}; + +static const struct resources_icc icc_res_sm6350[] = { + { + .name = "ahb", + .icc_bw_tbl.avg = 0, + .icc_bw_tbl.peak = 300000, + }, + { + .name = "hf_mnoc", + .icc_bw_tbl.avg = 2097152, + .icc_bw_tbl.peak = 2097152, + }, + { + .name = "sf_mnoc", + .icc_bw_tbl.avg = 2097152, + .icc_bw_tbl.peak = 2097152, + }, + { + .name = "sf_icp_mnoc", + .icc_bw_tbl.avg = 2097152, + .icc_bw_tbl.peak = 2097152, + }, +}; + static const struct camss_subdev_resources csiphy_res_8250[] = { /* CSIPHY0 */ { @@ -3559,6 +3806,54 @@ static const struct camss_subdev_resources csiphy_res_8775p[] = { }, }; +static const struct camss_subdev_resources tpg_res_8775p[] = { + /* TPG0 */ + { + .regulators = {}, + .clock = { "cpas_ahb", "csiphy_rx" }, + .clock_rate = { + { 0 }, + { 400000000 }, + }, + .reg = { "tpg0" }, + .tpg = { + .lane_cnt = 4, + .formats = &tpg_formats_gen1, + .hw_ops = &tpg_ops_gen1 + } + }, + /* TPG1 */ + { + .regulators = {}, + .clock = { "cpas_ahb", "csiphy_rx" }, + .clock_rate = { + { 0 }, + { 400000000 }, + }, + .reg = { "tpg1" }, + .tpg = { + .lane_cnt = 4, + .formats = &tpg_formats_gen1, + .hw_ops = &tpg_ops_gen1 + } + }, + /* TPG2 */ + { + .regulators = {}, + .clock = { "cpas_ahb", "csiphy_rx" }, + .clock_rate = { + { 0 }, + { 400000000 }, + }, + .reg = { "tpg2" }, + .tpg = { + .lane_cnt = 4, + .formats = &tpg_formats_gen1, + .hw_ops = &tpg_ops_gen1 + } + }, +}; + static const struct camss_subdev_resources csid_res_8775p[] = { /* CSID0 */ { @@ -3963,6 +4258,54 @@ static const struct camss_subdev_resources csiphy_res_x1e80100[] = { }, }; +static const struct camss_subdev_resources tpg_res_x1e80100[] = { + /* TPG0 */ + { + .regulators = {}, + .clock = { "cpas_ahb", "csid_csiphy_rx" }, + .clock_rate = { + { 0 }, + { 400000000 }, + }, + .reg = { "csitpg0" }, + .tpg = { + .lane_cnt = 4, + .formats = &tpg_formats_gen1, + .hw_ops = &tpg_ops_gen1 + } + }, + /* TPG1 */ + { + .regulators = {}, + .clock = { "cpas_ahb", "csid_csiphy_rx" }, + .clock_rate = { + { 0 }, + { 400000000 }, + }, + .reg = { "csitpg1" }, + .tpg = { + .lane_cnt = 4, + .formats = &tpg_formats_gen1, + .hw_ops = &tpg_ops_gen1 + } + }, + /* TPG2 */ + { + .regulators = {}, + .clock = { "cpas_ahb", "csid_csiphy_rx" }, + .clock_rate = { + { 0 }, + { 400000000 }, + }, + .reg = { "csitpg2" }, + .tpg = { + .lane_cnt = 4, + .formats = &tpg_formats_gen1, + .hw_ops = &tpg_ops_gen1 + } + }, +}; + static const struct camss_subdev_resources csid_res_x1e80100[] = { /* CSID0 */ { @@ -4076,7 +4419,7 @@ static const struct camss_subdev_resources vfe_res_x1e80100[] = { .clock = {"camnoc_rt_axi", "camnoc_nrt_axi", "cpas_ahb", "cpas_fast_ahb", "cpas_vfe0", "vfe0_fast_ahb", "vfe0" }, - .clock_rate = { { 0 }, + .clock_rate = { { 400000000 }, { 0 }, { 0 }, { 0 }, @@ -4100,7 +4443,7 @@ static const struct camss_subdev_resources vfe_res_x1e80100[] = { .clock = { "camnoc_rt_axi", "camnoc_nrt_axi", "cpas_ahb", "cpas_fast_ahb", "cpas_vfe1", "vfe1_fast_ahb", "vfe1" }, - .clock_rate = { { 0 }, + .clock_rate = { { 400000000 }, { 0 }, { 0 }, { 0 }, @@ -4124,7 +4467,7 @@ static const struct camss_subdev_resources vfe_res_x1e80100[] = { .clock = { "camnoc_rt_axi", "camnoc_nrt_axi", "cpas_ahb", "vfe_lite_ahb", "cpas_vfe_lite", "vfe_lite", "vfe_lite_csid" }, - .clock_rate = { { 0 }, + .clock_rate = { { 400000000 }, { 0 }, { 0 }, { 0 }, @@ -4147,7 +4490,7 @@ static const struct camss_subdev_resources vfe_res_x1e80100[] = { .clock = { "camnoc_rt_axi", "camnoc_nrt_axi", "cpas_ahb", "vfe_lite_ahb", "cpas_vfe_lite", "vfe_lite", "vfe_lite_csid" }, - .clock_rate = { { 0 }, + .clock_rate = { { 400000000 }, { 0 }, { 0 }, { 0 }, @@ -4501,6 +4844,19 @@ static int camss_init_subdevices(struct camss *camss) } } + if (camss->tpg) { + for (i = 0; i < camss->res->tpg_num; i++) { + ret = msm_tpg_subdev_init(camss, &camss->tpg[i], + &res->tpg_res[i], i); + if (ret < 0) { + dev_err(camss->dev, + "Failed to init tpg%d sub-device: %d\n", + i, ret); + return ret; + } + } + } + /* note: SM8250 requires VFE to be initialized before CSID */ for (i = 0; i < camss->res->vfe_num; i++) { ret = msm_vfe_subdev_init(camss, &camss->vfe[i], @@ -4589,6 +4945,23 @@ static int camss_link_entities(struct camss *camss) } } + for (i = 0; i < camss->res->tpg_num; i++) { + for (j = 0; j < camss->res->csid_num; j++) { + ret = media_create_pad_link(&camss->tpg[i].subdev.entity, + MSM_TPG_PAD_SRC, + &camss->csid[j].subdev.entity, + MSM_CSID_PAD_SINK, + 0); + if (ret < 0) { + camss_link_err(camss, + camss->tpg[i].subdev.entity.name, + camss->csid[j].subdev.entity.name, + ret); + return ret; + } + } + } + if (camss->ispif) { for (i = 0; i < camss->res->csid_num; i++) { for (j = 0; j < camss->ispif->line_num; j++) { @@ -4693,6 +5066,19 @@ static int camss_register_entities(struct camss *camss) } } + if (camss->tpg) { + for (i = 0; i < camss->res->tpg_num; i++) { + ret = msm_tpg_register_entity(&camss->tpg[i], + &camss->v4l2_dev); + if (ret < 0) { + dev_err(camss->dev, + "Failed to register tpg%d entity: %d\n", + i, ret); + goto err_reg_tpg; + } + } + } + for (i = 0; i < camss->res->csid_num; i++) { ret = msm_csid_register_entity(&camss->csid[i], &camss->v4l2_dev); @@ -4736,6 +5122,13 @@ err_reg_csid: for (i--; i >= 0; i--) msm_csid_unregister_entity(&camss->csid[i]); + i = camss->res->tpg_num; +err_reg_tpg: + if (camss->tpg) { + for (i--; i >= 0; i--) + msm_tpg_unregister_entity(&camss->tpg[i]); + } + i = camss->res->csiphy_num; err_reg_csiphy: for (i--; i >= 0; i--) @@ -4757,6 +5150,11 @@ static void camss_unregister_entities(struct camss *camss) for (i = 0; i < camss->res->csiphy_num; i++) msm_csiphy_unregister_entity(&camss->csiphy[i]); + if (camss->tpg) { + for (i = 0; i < camss->res->tpg_num; i++) + msm_tpg_unregister_entity(&camss->tpg[i]); + } + for (i = 0; i < camss->res->csid_num; i++) msm_csid_unregister_entity(&camss->csid[i]); @@ -4975,6 +5373,13 @@ static int camss_probe(struct platform_device *pdev) if (!camss->csiphy) return -ENOMEM; + if (camss->res->tpg_num > 0) { + camss->tpg = devm_kcalloc(dev, camss->res->tpg_num, + sizeof(*camss->tpg), GFP_KERNEL); + if (!camss->tpg) + return -ENOMEM; + } + camss->csid = devm_kcalloc(dev, camss->res->csid_num, sizeof(*camss->csid), GFP_KERNEL); if (!camss->csid) @@ -5164,11 +5569,13 @@ static const struct camss_resources qcs8300_resources = { .version = CAMSS_8300, .pd_name = "top", .csiphy_res = csiphy_res_8300, + .tpg_res = tpg_res_8775p, .csid_res = csid_res_8775p, .csid_wrapper_res = &csid_wrapper_res_sm8550, .vfe_res = vfe_res_8775p, .icc_res = icc_res_qcs8300, .csiphy_num = ARRAY_SIZE(csiphy_res_8300), + .tpg_num = ARRAY_SIZE(tpg_res_8775p), .csid_num = ARRAY_SIZE(csid_res_8775p), .vfe_num = ARRAY_SIZE(vfe_res_8775p), .icc_path_num = ARRAY_SIZE(icc_res_qcs8300), @@ -5178,11 +5585,13 @@ static const struct camss_resources sa8775p_resources = { .version = CAMSS_8775P, .pd_name = "top", .csiphy_res = csiphy_res_8775p, + .tpg_res = tpg_res_8775p, .csid_res = csid_res_8775p, .csid_wrapper_res = &csid_wrapper_res_sm8550, .vfe_res = vfe_res_8775p, .icc_res = icc_res_sa8775p, .csiphy_num = ARRAY_SIZE(csiphy_res_8775p), + .tpg_num = ARRAY_SIZE(tpg_res_8775p), .csid_num = ARRAY_SIZE(csid_res_8775p), .vfe_num = ARRAY_SIZE(vfe_res_8775p), .icc_path_num = ARRAY_SIZE(icc_res_sa8775p), @@ -5233,6 +5642,19 @@ static const struct camss_resources sm6150_resources = { .vfe_num = ARRAY_SIZE(vfe_res_sm6150), }; +static const struct camss_resources sm6350_resources = { + .version = CAMSS_6350, + .pd_name = "top", + .csiphy_res = csiphy_res_sm6350, + .csid_res = csid_res_sm6350, + .vfe_res = vfe_res_sm6350, + .icc_res = icc_res_sm6350, + .icc_path_num = ARRAY_SIZE(icc_res_sm6350), + .csiphy_num = ARRAY_SIZE(csiphy_res_sm6350), + .csid_num = ARRAY_SIZE(csid_res_sm6350), + .vfe_num = ARRAY_SIZE(vfe_res_sm6350), +}; + static const struct camss_resources sm8250_resources = { .version = CAMSS_8250, .pd_name = "top", @@ -5305,12 +5727,14 @@ static const struct camss_resources x1e80100_resources = { .version = CAMSS_X1E80100, .pd_name = "top", .csiphy_res = csiphy_res_x1e80100, + .tpg_res = tpg_res_x1e80100, .csid_res = csid_res_x1e80100, .vfe_res = vfe_res_x1e80100, .csid_wrapper_res = &csid_wrapper_res_x1e80100, .icc_res = icc_res_x1e80100, .icc_path_num = ARRAY_SIZE(icc_res_x1e80100), .csiphy_num = ARRAY_SIZE(csiphy_res_x1e80100), + .tpg_num = ARRAY_SIZE(tpg_res_x1e80100), .csid_num = ARRAY_SIZE(csid_res_x1e80100), .vfe_num = ARRAY_SIZE(vfe_res_x1e80100), }; @@ -5329,6 +5753,7 @@ static const struct of_device_id camss_dt_match[] = { { .compatible = "qcom,sdm670-camss", .data = &sdm670_resources }, { .compatible = "qcom,sdm845-camss", .data = &sdm845_resources }, { .compatible = "qcom,sm6150-camss", .data = &sm6150_resources }, + { .compatible = "qcom,sm6350-camss", .data = &sm6350_resources }, { .compatible = "qcom,sm8250-camss", .data = &sm8250_resources }, { .compatible = "qcom,sm8550-camss", .data = &sm8550_resources }, { .compatible = "qcom,sm8650-camss", .data = &sm8650_resources }, diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h index 6d048414c919..93d691c8ac63 100644 --- a/drivers/media/platform/qcom/camss/camss.h +++ b/drivers/media/platform/qcom/camss/camss.h @@ -21,6 +21,7 @@ #include "camss-csid.h" #include "camss-csiphy.h" #include "camss-ispif.h" +#include "camss-tpg.h" #include "camss-vfe.h" #include "camss-format.h" @@ -52,6 +53,7 @@ struct camss_subdev_resources { char *interrupt[CAMSS_RES_MAX]; union { struct csiphy_subdev_resources csiphy; + struct tpg_subdev_resources tpg; struct csid_subdev_resources csid; struct vfe_subdev_resources vfe; }; @@ -81,6 +83,7 @@ enum camss_version { CAMSS_660, CAMSS_2290, CAMSS_6150, + CAMSS_6350, CAMSS_7280, CAMSS_8x16, CAMSS_8x39, @@ -105,6 +108,7 @@ struct camss_resources { enum camss_version version; const char *pd_name; const struct camss_subdev_resources *csiphy_res; + const struct camss_subdev_resources *tpg_res; const struct camss_subdev_resources *csid_res; const struct camss_subdev_resources *ispif_res; const struct camss_subdev_resources *vfe_res; @@ -112,6 +116,7 @@ struct camss_resources { const struct resources_icc *icc_res; const unsigned int icc_path_num; const unsigned int csiphy_num; + const unsigned int tpg_num; const unsigned int csid_num; const unsigned int vfe_num; }; @@ -122,6 +127,7 @@ struct camss { struct media_device media_dev; struct device *dev; struct csiphy_device *csiphy; + struct tpg_device *tpg; struct csid_device *csid; struct ispif_device *ispif; struct vfe_device *vfe; diff --git a/drivers/media/platform/qcom/iris/Kconfig b/drivers/media/platform/qcom/iris/Kconfig index 5498f48362d1..af78a1775937 100644 --- a/drivers/media/platform/qcom/iris/Kconfig +++ b/drivers/media/platform/qcom/iris/Kconfig @@ -5,6 +5,7 @@ config VIDEO_QCOM_IRIS select V4L2_MEM2MEM_DEV select QCOM_MDT_LOADER select QCOM_SCM + select QCOM_UBWC_CONFIG select VIDEOBUF2_DMA_CONTIG help This is a V4L2 driver for Qualcomm iris video accelerator diff --git a/drivers/media/platform/qcom/iris/Makefile b/drivers/media/platform/qcom/iris/Makefile index 2abbd3aeb4af..48e415cbc439 100644 --- a/drivers/media/platform/qcom/iris/Makefile +++ b/drivers/media/platform/qcom/iris/Makefile @@ -4,13 +4,16 @@ qcom-iris-objs += iris_buffer.o \ iris_ctrls.o \ iris_firmware.o \ iris_hfi_common.o \ + iris_hfi_gen1.o \ iris_hfi_gen1_command.o \ iris_hfi_gen1_response.o \ + iris_hfi_gen2.o \ iris_hfi_gen2_command.o \ iris_hfi_gen2_packet.o \ iris_hfi_gen2_response.o \ iris_hfi_queue.o \ - iris_platform_gen2.o \ + iris_platform_vpu2.o \ + iris_platform_vpu3x.o \ iris_power.o \ iris_probe.o \ iris_resources.o \ @@ -26,8 +29,4 @@ qcom-iris-objs += iris_buffer.o \ iris_vpu_buffer.o \ iris_vpu_common.o \ -ifeq ($(CONFIG_VIDEO_QCOM_VENUS),) -qcom-iris-objs += iris_platform_gen1.o -endif - obj-$(CONFIG_VIDEO_QCOM_IRIS) += qcom-iris.o diff --git a/drivers/media/platform/qcom/iris/iris_buffer.c b/drivers/media/platform/qcom/iris/iris_buffer.c index 1d53c7414b75..246ad0abbac3 100644 --- a/drivers/media/platform/qcom/iris/iris_buffer.c +++ b/drivers/media/platform/qcom/iris/iris_buffer.c @@ -15,8 +15,11 @@ #define MAX_WIDTH 4096 #define MAX_HEIGHT 2304 #define Y_STRIDE_ALIGN 128 +#define Y_STRIDE_ALIGN_P010 256 #define UV_STRIDE_ALIGN 128 +#define UV_STRIDE_ALIGN_P010 256 #define Y_SCANLINE_ALIGN 32 +#define Y_SCANLINE_ALIGN_QC10C 16 #define UV_SCANLINE_ALIGN 16 #define UV_SCANLINE_ALIGN_QC08C 32 #define META_STRIDE_ALIGNED 64 @@ -81,6 +84,63 @@ static u32 iris_yuv_buffer_size_nv12(struct iris_inst *inst) } /* + * P010: + * YUV 4:2:0 image with a plane of 10 bit Y samples followed + * by an interleaved U/V plane containing 10 bit 2x2 subsampled + * colour difference samples. + * + * <-Y/UV_Stride (aligned to 256)-> + * <----- Width*2 -------> + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . ^ ^ + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . Height | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | y_scanlines (aligned to 32) + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . V | + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . V + * U V U V U V U V U V U V . . . . ^ + * U V U V U V U V U V U V . . . . | + * U V U V U V U V U V U V . . . . | + * U V U V U V U V U V U V . . . . uv_scanlines (aligned to 16) + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . V + * . . . . . . . . . . . . . . . . --> Buffer size aligned to 4K + * + * y_stride : Width*2 aligned to 256 + * uv_stride : Width*2 aligned to 256 + * y_scanlines: Height aligned to 32 + * uv_scanlines: Height/2 aligned to 16 + * Total size = align((y_stride * y_scanlines + * + uv_stride * uv_scanlines , 4096) + * + * Note: All the alignments are hardware requirements. + */ +static u32 iris_yuv_buffer_size_p010(struct iris_inst *inst) +{ + u32 y_plane, uv_plane, y_stride, uv_stride, y_scanlines, uv_scanlines; + struct v4l2_format *f; + + if (inst->domain == DECODER) + f = inst->fmt_dst; + else + f = inst->fmt_src; + + y_stride = ALIGN(f->fmt.pix_mp.width * 2, Y_STRIDE_ALIGN_P010); + uv_stride = ALIGN(f->fmt.pix_mp.width * 2, UV_STRIDE_ALIGN_P010); + y_scanlines = ALIGN(f->fmt.pix_mp.height, Y_SCANLINE_ALIGN); + uv_scanlines = ALIGN((f->fmt.pix_mp.height + 1) >> 1, UV_SCANLINE_ALIGN); + y_plane = y_stride * y_scanlines; + uv_plane = uv_stride * uv_scanlines; + + return ALIGN(y_plane + uv_plane, PIXELS_4K); +} + +/* * QC08C: * Compressed Macro-tile format for NV12. * Contains 4 planes in the following order - @@ -204,6 +264,132 @@ static u32 iris_yuv_buffer_size_qc08c(struct iris_inst *inst) return ALIGN(y_meta_plane + y_plane + uv_meta_plane + uv_plane, PIXELS_4K); } +/* + * QC10C: + * UBWC-compressed format for P010. + * Contains 4 planes in the following order - + * (A) Y_Meta_Plane + * (B) Y_UBWC_Plane + * (C) UV_Meta_Plane + * (D) UV_UBWC_Plane + * + * Y_Meta_Plane consists of meta information to decode compressed + * tile data in Y_UBWC_Plane. + * Y_UBWC_Plane consists of Y data in compressed macro-tile format. + * UBWC decoder block will use the Y_Meta_Plane data together with + * Y_UBWC_Plane data to produce loss-less uncompressed 10 bit Y samples. + * + * UV_Meta_Plane consists of meta information to decode compressed + * tile data in UV_UBWC_Plane. + * UV_UBWC_Plane consists of UV data in compressed macro-tile format. + * UBWC decoder block will use UV_Meta_Plane data together with + * UV_UBWC_Plane data to produce loss-less uncompressed 10 bit 2x2 + * subsampled color difference samples. + * + * Each tile in Y_UBWC_Plane/UV_UBWC_Plane is independently decodable + * and randomly accessible. There is no dependency between tiles. + * + * <----- Y Meta stride -----> (aligned to 64) + * <-------- Width ----------> (aligned to 48) + * M M M M M M M M M M M M . . ^ ^ + * M M M M M M M M M M M M . . | | + * M M M M M M M M M M M M . . Height | + * M M M M M M M M M M M M . . | Meta_Y_Scanlines (aligned to 16) + * M M M M M M M M M M M M . . | | + * M M M M M M M M M M M M . . | | + * M M M M M M M M M M M M . . | | + * M M M M M M M M M M M M . . V | + * . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . -------> Buffer size aligned to 4k + * . . . . . . . . . . . . . . V + * <--Compressed tile Y stride --> (aligned to 256) + * <------- Width * 4/3 ---------> (aligned to 48) + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . ^ ^ + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | | + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . Height | + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | Macro_tile_Y_Scanlines (aligned to 16) + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | | + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | | + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | | + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . V | + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . -------> Buffer size aligned to 4k + * . . . . . . . . . . . . . . . . V + * <---- UV Meta stride ----> (aligned to 64) + * <----- Width / 2 --------> (aligned to 24) + * M M M M M M M M M M M M . . ^ ^ + * M M M M M M M M M M M M . . | | + * M M M M M M M M M M M M . . Height/2 | + * M M M M M M M M M M M M . . V M_UV_Scanlines (aligned to 16) + * . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . V + * . . . . . . . . . . . . . . -------> Buffer size aligned to 4k + * <--Compressed tile UV stride--> (aligned to 256) + * <------- Width * 4/3 ---------> (aligned to 48) + * U* V* U* V* U* V* U* V* . . . . ^ + * U* V* U* V* U* V* U* V* . . . . | + * U* V* U* V* U* V* U* V* . . . . | + * U* V* U* V* U* V* U* V* . . . . UV_Scanlines (aligned to 16) + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . V + * . . . . . . . . . . . . . . . . -------> Buffer size aligned to 4k + * + * y_stride: width aligned to 256 + * uv_stride: width aligned to 256 + * y_scanlines: height aligned to 16 + * uv_scanlines: height aligned to 16 + * y_plane: buffer size aligned to 4096 + * uv_plane: buffer size aligned to 4096 + * y_meta_stride: width aligned to 64 + * y_meta_scanlines: height aligned to 16 + * y_meta_plane: buffer size aligned to 4096 + * uv_meta_stride: width aligned to 64 + * uv_meta_scanlines: height aligned to 16 + * uv_meta_plane: buffer size aligned to 4096 + * + * Total size = align( y_plane + uv_plane + + * y_meta_plane + uv_meta_plane, 4096) + * + * Note: All the alignments are hardware requirements. + */ +static u32 iris_yuv_buffer_size_qc10c(struct iris_inst *inst) +{ + u32 y_plane, uv_plane, y_stride, uv_stride; + u32 uv_meta_stride, uv_meta_plane; + u32 y_meta_stride, y_meta_plane; + struct v4l2_format *f; + + if (inst->domain == DECODER) + f = inst->fmt_dst; + else + f = inst->fmt_src; + + y_meta_stride = ALIGN(DIV_ROUND_UP(f->fmt.pix_mp.width, 48), + META_STRIDE_ALIGNED); + y_meta_plane = y_meta_stride * ALIGN(DIV_ROUND_UP(f->fmt.pix_mp.height, 4), + META_SCANLINE_ALIGNED); + y_meta_plane = ALIGN(y_meta_plane, PIXELS_4K); + + y_stride = ALIGN(f->fmt.pix_mp.width * 4 / 3, Y_STRIDE_ALIGN_P010); + y_plane = ALIGN(y_stride * ALIGN(f->fmt.pix_mp.height, Y_SCANLINE_ALIGN_QC10C), + PIXELS_4K); + + uv_meta_stride = ALIGN(DIV_ROUND_UP((f->fmt.pix_mp.width + 1) >> 1, 24), + META_STRIDE_ALIGNED); + uv_meta_plane = uv_meta_stride * + ALIGN(DIV_ROUND_UP((f->fmt.pix_mp.height + 1) >> 1, 4), + META_SCANLINE_ALIGNED); + uv_meta_plane = ALIGN(uv_meta_plane, PIXELS_4K); + + uv_stride = ALIGN(f->fmt.pix_mp.width * 4 / 3, UV_STRIDE_ALIGN_P010); + uv_plane = ALIGN(uv_stride * ALIGN((f->fmt.pix_mp.height + 1) >> 1, UV_SCANLINE_ALIGN), + PIXELS_4K); + + return ALIGN(y_meta_plane + y_plane + uv_meta_plane + uv_plane, PIXELS_4K); +} + static u32 iris_dec_bitstream_buffer_size(struct iris_inst *inst) { struct platform_inst_caps *caps = inst->core->iris_platform_data->inst_caps; @@ -268,10 +454,17 @@ int iris_get_buffer_size(struct iris_inst *inst, case BUF_OUTPUT: if (inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC08C) return iris_yuv_buffer_size_qc08c(inst); + else if (inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC10C) + return iris_yuv_buffer_size_qc10c(inst); + else if (inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_P010) + return iris_yuv_buffer_size_p010(inst); else return iris_yuv_buffer_size_nv12(inst); case BUF_DPB: - return iris_yuv_buffer_size_qc08c(inst); + if (iris_fmt_is_10bit(inst->fmt_dst->fmt.pix_mp.pixelformat)) + return iris_yuv_buffer_size_qc10c(inst); + else + return iris_yuv_buffer_size_qc08c(inst); default: return 0; } @@ -295,37 +488,37 @@ static void iris_fill_internal_buf_info(struct iris_inst *inst, { struct iris_buffers *buffers = &inst->buffers[buffer_type]; - buffers->size = inst->core->iris_platform_data->get_vpu_buffer_size(inst, buffer_type); + buffers->size = inst->core->iris_firmware_desc->get_vpu_buffer_size(inst, buffer_type); buffers->min_count = iris_vpu_buf_count(inst, buffer_type); } void iris_get_internal_buffers(struct iris_inst *inst, u32 plane) { - const struct iris_platform_data *platform_data = inst->core->iris_platform_data; + const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data; const u32 *internal_buf_type; u32 internal_buffer_count, i; if (inst->domain == DECODER) { if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = platform_data->dec_ip_int_buf_tbl; - internal_buffer_count = platform_data->dec_ip_int_buf_tbl_size; + internal_buf_type = firmware_data->dec_ip_int_buf_tbl; + internal_buffer_count = firmware_data->dec_ip_int_buf_tbl_size; for (i = 0; i < internal_buffer_count; i++) iris_fill_internal_buf_info(inst, internal_buf_type[i]); } else { - internal_buf_type = platform_data->dec_op_int_buf_tbl; - internal_buffer_count = platform_data->dec_op_int_buf_tbl_size; + internal_buf_type = firmware_data->dec_op_int_buf_tbl; + internal_buffer_count = firmware_data->dec_op_int_buf_tbl_size; for (i = 0; i < internal_buffer_count; i++) iris_fill_internal_buf_info(inst, internal_buf_type[i]); } } else { if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = platform_data->enc_ip_int_buf_tbl; - internal_buffer_count = platform_data->enc_ip_int_buf_tbl_size; + internal_buf_type = firmware_data->enc_ip_int_buf_tbl; + internal_buffer_count = firmware_data->enc_ip_int_buf_tbl_size; for (i = 0; i < internal_buffer_count; i++) iris_fill_internal_buf_info(inst, internal_buf_type[i]); } else { - internal_buf_type = platform_data->enc_op_int_buf_tbl; - internal_buffer_count = platform_data->enc_op_int_buf_tbl_size; + internal_buf_type = firmware_data->enc_op_int_buf_tbl; + internal_buffer_count = firmware_data->enc_op_int_buf_tbl_size; for (i = 0; i < internal_buffer_count; i++) iris_fill_internal_buf_info(inst, internal_buf_type[i]); } @@ -366,7 +559,7 @@ static int iris_create_internal_buffer(struct iris_inst *inst, int iris_create_internal_buffers(struct iris_inst *inst, u32 plane) { - const struct iris_platform_data *platform_data = inst->core->iris_platform_data; + const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data; u32 internal_buffer_count, i, j; struct iris_buffers *buffers; const u32 *internal_buf_type; @@ -374,19 +567,19 @@ int iris_create_internal_buffers(struct iris_inst *inst, u32 plane) if (inst->domain == DECODER) { if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = platform_data->dec_ip_int_buf_tbl; - internal_buffer_count = platform_data->dec_ip_int_buf_tbl_size; + internal_buf_type = firmware_data->dec_ip_int_buf_tbl; + internal_buffer_count = firmware_data->dec_ip_int_buf_tbl_size; } else { - internal_buf_type = platform_data->dec_op_int_buf_tbl; - internal_buffer_count = platform_data->dec_op_int_buf_tbl_size; + internal_buf_type = firmware_data->dec_op_int_buf_tbl; + internal_buffer_count = firmware_data->dec_op_int_buf_tbl_size; } } else { if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = platform_data->enc_ip_int_buf_tbl; - internal_buffer_count = platform_data->enc_ip_int_buf_tbl_size; + internal_buf_type = firmware_data->enc_ip_int_buf_tbl; + internal_buffer_count = firmware_data->enc_ip_int_buf_tbl_size; } else { - internal_buf_type = platform_data->enc_op_int_buf_tbl; - internal_buffer_count = platform_data->enc_op_int_buf_tbl_size; + internal_buf_type = firmware_data->enc_op_int_buf_tbl; + internal_buffer_count = firmware_data->enc_op_int_buf_tbl_size; } } @@ -404,7 +597,7 @@ int iris_create_internal_buffers(struct iris_inst *inst, u32 plane) int iris_queue_buffer(struct iris_inst *inst, struct iris_buffer *buf) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; int ret; ret = hfi_ops->session_queue_buf(inst, buf); @@ -442,7 +635,7 @@ int iris_queue_internal_deferred_buffers(struct iris_inst *inst, enum iris_buffe int iris_queue_internal_buffers(struct iris_inst *inst, u32 plane) { - const struct iris_platform_data *platform_data = inst->core->iris_platform_data; + const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data; struct iris_buffer *buffer, *next; struct iris_buffers *buffers; const u32 *internal_buf_type; @@ -451,19 +644,19 @@ int iris_queue_internal_buffers(struct iris_inst *inst, u32 plane) if (inst->domain == DECODER) { if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = platform_data->dec_ip_int_buf_tbl; - internal_buffer_count = platform_data->dec_ip_int_buf_tbl_size; + internal_buf_type = firmware_data->dec_ip_int_buf_tbl; + internal_buffer_count = firmware_data->dec_ip_int_buf_tbl_size; } else { - internal_buf_type = platform_data->dec_op_int_buf_tbl; - internal_buffer_count = platform_data->dec_op_int_buf_tbl_size; + internal_buf_type = firmware_data->dec_op_int_buf_tbl; + internal_buffer_count = firmware_data->dec_op_int_buf_tbl_size; } } else { if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = platform_data->enc_ip_int_buf_tbl; - internal_buffer_count = platform_data->enc_ip_int_buf_tbl_size; + internal_buf_type = firmware_data->enc_ip_int_buf_tbl; + internal_buffer_count = firmware_data->enc_ip_int_buf_tbl_size; } else { - internal_buf_type = platform_data->enc_op_int_buf_tbl; - internal_buffer_count = platform_data->enc_op_int_buf_tbl_size; + internal_buf_type = firmware_data->enc_op_int_buf_tbl; + internal_buffer_count = firmware_data->enc_op_int_buf_tbl_size; } } @@ -501,7 +694,7 @@ int iris_destroy_internal_buffer(struct iris_inst *inst, struct iris_buffer *buf static int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane, bool force) { - const struct iris_platform_data *platform_data = inst->core->iris_platform_data; + const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data; struct iris_buffer *buf, *next; struct iris_buffers *buffers; const u32 *internal_buf_type; @@ -510,19 +703,19 @@ static int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane, bool if (inst->domain == DECODER) { if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = platform_data->dec_ip_int_buf_tbl; - len = platform_data->dec_ip_int_buf_tbl_size; + internal_buf_type = firmware_data->dec_ip_int_buf_tbl; + len = firmware_data->dec_ip_int_buf_tbl_size; } else { - internal_buf_type = platform_data->dec_op_int_buf_tbl; - len = platform_data->dec_op_int_buf_tbl_size; + internal_buf_type = firmware_data->dec_op_int_buf_tbl; + len = firmware_data->dec_op_int_buf_tbl_size; } } else { if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = platform_data->enc_ip_int_buf_tbl; - len = platform_data->enc_ip_int_buf_tbl_size; + internal_buf_type = firmware_data->enc_ip_int_buf_tbl; + len = firmware_data->enc_ip_int_buf_tbl_size; } else { - internal_buf_type = platform_data->enc_op_int_buf_tbl; - len = platform_data->enc_op_int_buf_tbl_size; + internal_buf_type = firmware_data->enc_op_int_buf_tbl; + len = firmware_data->enc_op_int_buf_tbl_size; } } @@ -572,7 +765,7 @@ int iris_destroy_dequeued_internal_buffers(struct iris_inst *inst, u32 plane) static int iris_release_internal_buffers(struct iris_inst *inst, enum iris_buffer_type buffer_type) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; struct iris_buffers *buffers = &inst->buffers[buffer_type]; struct iris_buffer *buffer, *next; int ret; @@ -595,17 +788,17 @@ static int iris_release_internal_buffers(struct iris_inst *inst, static int iris_release_input_internal_buffers(struct iris_inst *inst) { - const struct iris_platform_data *platform_data = inst->core->iris_platform_data; + const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data; const u32 *internal_buf_type; u32 internal_buffer_count, i; int ret; if (inst->domain == DECODER) { - internal_buf_type = platform_data->dec_ip_int_buf_tbl; - internal_buffer_count = platform_data->dec_ip_int_buf_tbl_size; + internal_buf_type = firmware_data->dec_ip_int_buf_tbl; + internal_buffer_count = firmware_data->dec_ip_int_buf_tbl_size; } else { - internal_buf_type = platform_data->enc_ip_int_buf_tbl; - internal_buffer_count = platform_data->enc_ip_int_buf_tbl_size; + internal_buf_type = firmware_data->enc_ip_int_buf_tbl; + internal_buffer_count = firmware_data->enc_ip_int_buf_tbl_size; } for (i = 0; i < internal_buffer_count; i++) { diff --git a/drivers/media/platform/qcom/iris/iris_common.c b/drivers/media/platform/qcom/iris/iris_common.c index 7f1c7fe144f7..25836561bcf3 100644 --- a/drivers/media/platform/qcom/iris/iris_common.c +++ b/drivers/media/platform/qcom/iris/iris_common.c @@ -48,7 +48,7 @@ void iris_set_ts_metadata(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf) int iris_process_streamon_input(struct iris_inst *inst) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; enum iris_inst_sub_state set_sub_state = 0; int ret; @@ -90,7 +90,7 @@ int iris_process_streamon_input(struct iris_inst *inst) int iris_process_streamon_output(struct iris_inst *inst) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; enum iris_inst_sub_state clear_sub_state = 0; bool drain_active, drc_active, first_ipsc; int ret = 0; @@ -189,7 +189,7 @@ static void iris_flush_deferred_buffers(struct iris_inst *inst, static void iris_kill_session(struct iris_inst *inst) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; if (!inst->session_id) return; @@ -200,7 +200,7 @@ static void iris_kill_session(struct iris_inst *inst) int iris_session_streamoff(struct iris_inst *inst, u32 plane) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; enum iris_buffer_type buffer_type; int ret; diff --git a/drivers/media/platform/qcom/iris/iris_core.c b/drivers/media/platform/qcom/iris/iris_core.c index dbaac01eb15a..52bf56e517f9 100644 --- a/drivers/media/platform/qcom/iris/iris_core.c +++ b/drivers/media/platform/qcom/iris/iris_core.c @@ -28,14 +28,13 @@ void iris_core_deinit(struct iris_core *core) static int iris_wait_for_system_response(struct iris_core *core) { - u32 hw_response_timeout_val = core->iris_platform_data->hw_response_timeout; int ret; if (core->state == IRIS_CORE_ERROR) return -EIO; ret = wait_for_completion_timeout(&core->core_init_done, - msecs_to_jiffies(hw_response_timeout_val)); + msecs_to_jiffies(HW_RESPONSE_TIMEOUT_VALUE)); if (!ret) { core->state = IRIS_CORE_ERROR; return -ETIMEDOUT; @@ -79,6 +78,8 @@ int iris_core_init(struct iris_core *core) if (ret) goto error_unload_fw; + core->iris_firmware_data->init_hfi_ops(core); + ret = iris_hfi_core_init(core); if (ret) goto error_unload_fw; diff --git a/drivers/media/platform/qcom/iris/iris_core.h b/drivers/media/platform/qcom/iris/iris_core.h index fb194c967ad4..24da60448cf2 100644 --- a/drivers/media/platform/qcom/iris/iris_core.h +++ b/drivers/media/platform/qcom/iris/iris_core.h @@ -30,6 +30,8 @@ enum domain_type { DECODER = BIT(1), }; +struct qcom_ubwc_cfg_data; + /** * struct iris_core - holds core parameters valid for all instances * @@ -52,6 +54,9 @@ enum domain_type { * @resets: table of iris reset clocks * @controller_resets: table of controller reset clocks * @iris_platform_data: a structure for platform data + * @iris_firmware_data: a pointer to the firmware (or HFI) specific data + * @iris_firmware_desc: a pointer to the firmware-specific descriptive data + * @ubwc_cfg: UBWC configuration for the platform * @state: current state of core * @iface_q_table_daddr: device address for interface queue table memory * @sfr_daddr: device address for SFR (Sub System Failure Reason) register memory @@ -65,8 +70,7 @@ enum domain_type { * @header_id: id of packet header * @packet_id: id of packet * @power: a structure for clock and bw information - * @hfi_ops: iris hfi command ops - * @hfi_response_ops: iris hfi response ops + * @hfi_sys_ops: iris HFI system ops * @core_init_done: structure of signal completion for system response * @intr_status: interrupt status * @sys_error_handler: a delayed work for handling system fatal error @@ -95,6 +99,9 @@ struct iris_core { struct reset_control_bulk_data *resets; struct reset_control_bulk_data *controller_resets; const struct iris_platform_data *iris_platform_data; + const struct iris_firmware_data *iris_firmware_data; + const struct iris_firmware_desc *iris_firmware_desc; + const struct qcom_ubwc_cfg_data *ubwc_cfg; enum iris_core_state state; dma_addr_t iface_q_table_daddr; dma_addr_t sfr_daddr; @@ -108,8 +115,7 @@ struct iris_core { u32 header_id; u32 packet_id; struct iris_core_power power; - const struct iris_hfi_command_ops *hfi_ops; - const struct iris_hfi_response_ops *hfi_response_ops; + const struct iris_hfi_sys_ops *hfi_sys_ops; struct completion core_init_done; u32 intr_status; struct delayed_work sys_error_handler; diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.c b/drivers/media/platform/qcom/iris/iris_ctrls.c index 3cec957580f5..10e33b8a73f6 100644 --- a/drivers/media/platform/qcom/iris/iris_ctrls.c +++ b/drivers/media/platform/qcom/iris/iris_ctrls.c @@ -112,6 +112,48 @@ static enum platform_inst_fw_cap_type iris_get_cap_id(u32 id) return IR_TYPE; case V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD: return IR_PERIOD; + case V4L2_CID_MPEG_VIDEO_LTR_COUNT: + return LTR_COUNT; + case V4L2_CID_MPEG_VIDEO_USE_LTR_FRAMES: + return USE_LTR; + case V4L2_CID_MPEG_VIDEO_FRAME_LTR_INDEX: + return MARK_LTR; + case V4L2_CID_MPEG_VIDEO_B_FRAMES: + return B_FRAME; + case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING: + return LAYER_ENABLE; + case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_TYPE: + return LAYER_TYPE_H264; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_TYPE: + return LAYER_TYPE_HEVC; + case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER: + return LAYER_COUNT_H264; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_LAYER: + return LAYER_COUNT_HEVC; + case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L0_BR: + return LAYER0_BITRATE_H264; + case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L1_BR: + return LAYER1_BITRATE_H264; + case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L2_BR: + return LAYER2_BITRATE_H264; + case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L3_BR: + return LAYER3_BITRATE_H264; + case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L4_BR: + return LAYER4_BITRATE_H264; + case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L5_BR: + return LAYER5_BITRATE_H264; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_BR: + return LAYER0_BITRATE_HEVC; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_BR: + return LAYER1_BITRATE_HEVC; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_BR: + return LAYER2_BITRATE_HEVC; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_BR: + return LAYER3_BITRATE_HEVC; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_BR: + return LAYER4_BITRATE_HEVC; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_BR: + return LAYER5_BITRATE_HEVC; default: return INST_FW_CAP_MAX; } @@ -213,6 +255,48 @@ static u32 iris_get_v4l2_id(enum platform_inst_fw_cap_type cap_id) return V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE; case IR_PERIOD: return V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD; + case LTR_COUNT: + return V4L2_CID_MPEG_VIDEO_LTR_COUNT; + case USE_LTR: + return V4L2_CID_MPEG_VIDEO_USE_LTR_FRAMES; + case MARK_LTR: + return V4L2_CID_MPEG_VIDEO_FRAME_LTR_INDEX; + case B_FRAME: + return V4L2_CID_MPEG_VIDEO_B_FRAMES; + case LAYER_ENABLE: + return V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING; + case LAYER_TYPE_H264: + return V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_TYPE; + case LAYER_TYPE_HEVC: + return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_TYPE; + case LAYER_COUNT_H264: + return V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER; + case LAYER_COUNT_HEVC: + return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_LAYER; + case LAYER0_BITRATE_H264: + return V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L0_BR; + case LAYER1_BITRATE_H264: + return V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L1_BR; + case LAYER2_BITRATE_H264: + return V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L2_BR; + case LAYER3_BITRATE_H264: + return V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L3_BR; + case LAYER4_BITRATE_H264: + return V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L4_BR; + case LAYER5_BITRATE_H264: + return V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L5_BR; + case LAYER0_BITRATE_HEVC: + return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_BR; + case LAYER1_BITRATE_HEVC: + return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_BR; + case LAYER2_BITRATE_HEVC: + return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_BR; + case LAYER3_BITRATE_HEVC: + return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_BR; + case LAYER4_BITRATE_HEVC: + return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_BR; + case LAYER5_BITRATE_HEVC: + return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_BR; default: return 0; } @@ -332,8 +416,8 @@ void iris_session_init_caps(struct iris_core *core) const struct platform_inst_fw_cap *caps; u32 i, num_cap, cap_id; - caps = core->iris_platform_data->inst_fw_caps_dec; - num_cap = core->iris_platform_data->inst_fw_caps_dec_size; + caps = core->iris_firmware_data->inst_fw_caps_dec; + num_cap = core->iris_firmware_data->inst_fw_caps_dec_size; for (i = 0; i < num_cap; i++) { cap_id = caps[i].cap_id; @@ -360,8 +444,8 @@ void iris_session_init_caps(struct iris_core *core) } } - caps = core->iris_platform_data->inst_fw_caps_enc; - num_cap = core->iris_platform_data->inst_fw_caps_enc_size; + caps = core->iris_firmware_data->inst_fw_caps_enc; + num_cap = core->iris_firmware_data->inst_fw_caps_enc_size; for (i = 0; i < num_cap; i++) { cap_id = caps[i].cap_id; @@ -399,7 +483,7 @@ static u32 iris_get_port_info(struct iris_inst *inst, int iris_set_u32_enum(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 hfi_value = inst->fw_caps[cap_id].value; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; @@ -412,7 +496,7 @@ int iris_set_u32_enum(struct iris_inst *inst, enum platform_inst_fw_cap_type cap int iris_set_u32(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 hfi_value = inst->fw_caps[cap_id].value; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; @@ -425,7 +509,7 @@ int iris_set_u32(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) int iris_set_stage(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; struct v4l2_format *inp_f = inst->fmt_src; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; u32 height = inp_f->fmt.pix_mp.height; @@ -446,7 +530,7 @@ int iris_set_stage(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id int iris_set_pipe(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 work_route = inst->fw_caps[PIPE].value; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; @@ -459,7 +543,7 @@ int iris_set_pipe(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) int iris_set_profile(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 hfi_id, hfi_value; if (inst->codec == V4L2_PIX_FMT_H264) { @@ -479,7 +563,7 @@ int iris_set_profile(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_ int iris_set_level(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 hfi_id, hfi_value; if (inst->codec == V4L2_PIX_FMT_H264) { @@ -499,7 +583,7 @@ int iris_set_level(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id int iris_set_profile_level_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; struct hfi_profile_level pl; @@ -520,7 +604,7 @@ int iris_set_profile_level_gen1(struct iris_inst *inst, enum platform_inst_fw_ca int iris_set_header_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 header_mode = inst->fw_caps[cap_id].value; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; u32 hfi_val; @@ -539,7 +623,7 @@ int iris_set_header_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_ int iris_set_header_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 prepend_sps_pps = inst->fw_caps[PREPEND_SPSPPS_TO_IDR].value; u32 header_mode = inst->fw_caps[cap_id].value; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; @@ -559,9 +643,66 @@ int iris_set_header_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_ &hfi_val, sizeof(u32)); } -int iris_set_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +int iris_set_bitrate_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + u32 entropy_mode = inst->fw_caps[ENTROPY_MODE].value; + u32 bitrate = inst->fw_caps[cap_id].value; + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + struct hfi_bitrate hfi_val; + u32 max_bitrate; + + if (!(inst->fw_caps[cap_id].flags & CAP_FLAG_CLIENT_SET) && cap_id != BITRATE) + return -EINVAL; + + if (inst->codec == V4L2_PIX_FMT_HEVC) { + max_bitrate = CABAC_MAX_BITRATE; + } else { + if (entropy_mode == V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC) + max_bitrate = CABAC_MAX_BITRATE; + else + max_bitrate = CAVLC_MAX_BITRATE; + } + + hfi_val.bitrate = min(bitrate, max_bitrate); + + switch (cap_id) { + case BITRATE: + case LAYER0_BITRATE_H264: + hfi_val.layer_id = 0; + break; + case LAYER1_BITRATE_H264: + hfi_val.layer_id = 1; + break; + case LAYER2_BITRATE_H264: + hfi_val.layer_id = 2; + break; + case LAYER3_BITRATE_H264: + hfi_val.layer_id = 3; + break; + case LAYER4_BITRATE_H264: + hfi_val.layer_id = 4; + break; + case LAYER5_BITRATE_H264: + hfi_val.layer_id = 5; + break; + default: + return -EINVAL; + } + + if (hfi_val.layer_id > 0 && !inst->fw_caps[LAYER_ENABLE].value) + return -EINVAL; + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_STRUCTURE, + &hfi_val, sizeof(hfi_val)); +} + +int iris_set_bitrate_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 entropy_mode = inst->fw_caps[ENTROPY_MODE].value; u32 bitrate = inst->fw_caps[cap_id].value; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; @@ -586,7 +727,7 @@ int iris_set_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_ int iris_set_peak_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 rc_mode = inst->fw_caps[BITRATE_MODE].value; u32 peak_bitrate = inst->fw_caps[cap_id].value; u32 bitrate = inst->fw_caps[BITRATE].value; @@ -613,7 +754,7 @@ int iris_set_peak_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type int iris_set_bitrate_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 bitrate_mode = inst->fw_caps[BITRATE_MODE].value; u32 frame_rc = inst->fw_caps[FRAME_RC_ENABLE].value; u32 frame_skip = inst->fw_caps[FRAME_SKIP_MODE].value; @@ -640,7 +781,7 @@ int iris_set_bitrate_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap int iris_set_bitrate_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 bitrate_mode = inst->fw_caps[BITRATE_MODE].value; u32 frame_rc = inst->fw_caps[FRAME_RC_ENABLE].value; u32 frame_skip = inst->fw_caps[FRAME_SKIP_MODE].value; @@ -667,7 +808,7 @@ int iris_set_bitrate_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap int iris_set_entropy_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 entropy_mode = inst->fw_caps[cap_id].value; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; u32 hfi_val; @@ -687,7 +828,7 @@ int iris_set_entropy_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap int iris_set_entropy_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 entropy_mode = inst->fw_caps[cap_id].value; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; u32 profile; @@ -712,7 +853,7 @@ int iris_set_entropy_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap int iris_set_min_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 i_qp_enable = 0, p_qp_enable = 0, b_qp_enable = 0; u32 i_frame_qp = 0, p_frame_qp = 0, b_frame_qp = 0; u32 min_qp_enable = 0, client_qp_enable = 0; @@ -776,7 +917,7 @@ int iris_set_min_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_i int iris_set_max_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 i_qp_enable = 0, p_qp_enable = 0, b_qp_enable = 0; u32 max_qp_enable = 0, client_qp_enable; u32 i_frame_qp, p_frame_qp, b_frame_qp; @@ -841,7 +982,7 @@ int iris_set_max_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_i int iris_set_frame_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 i_qp_enable = 0, p_qp_enable = 0, b_qp_enable = 0, client_qp_enable; u32 i_frame_qp, p_frame_qp, b_frame_qp; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; @@ -902,7 +1043,7 @@ int iris_set_frame_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap int iris_set_qp_range(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; struct hfi_quantization_range_v2 range; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; @@ -923,7 +1064,7 @@ int iris_set_qp_range(struct iris_inst *inst, enum platform_inst_fw_cap_type cap int iris_set_rotation(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; u32 hfi_val; @@ -953,7 +1094,7 @@ int iris_set_rotation(struct iris_inst *inst, enum platform_inst_fw_cap_type cap int iris_set_flip(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; u32 hfi_val = HFI_DISABLE_FLIP; @@ -970,9 +1111,46 @@ int iris_set_flip(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) &hfi_val, sizeof(u32)); } -int iris_set_ir_period(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +int iris_set_ir_period_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + struct v4l2_pix_format_mplane *fmt = &inst->fmt_dst->fmt.pix_mp; + u32 codec_align = inst->codec == V4L2_PIX_FMT_HEVC ? 32 : 16; + u32 ir_period = inst->fw_caps[cap_id].value; + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + struct hfi_intra_refresh hfi_val; + + if (!ir_period) + return -EINVAL; + + if (inst->fw_caps[IR_TYPE].value == + V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM) { + hfi_val.mode = HFI_INTRA_REFRESH_RANDOM; + } else if (inst->fw_caps[IR_TYPE].value == + V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_CYCLIC) { + hfi_val.mode = HFI_INTRA_REFRESH_CYCLIC; + } else { + return -EINVAL; + } + + /* + * Calculate the number of macroblocks in a frame, + * then determine how many macroblocks need to be + * refreshed within one ir_period. + */ + hfi_val.mbs = (fmt->width / codec_align) * (fmt->height / codec_align); + hfi_val.mbs /= ir_period; + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_STRUCTURE, + &hfi_val, sizeof(hfi_val)); +} + +int iris_set_ir_period_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; struct vb2_queue *q = v4l2_m2m_get_dst_vq(inst->m2m_ctx); u32 ir_period = inst->fw_caps[cap_id].value; u32 ir_type = 0; @@ -996,9 +1174,312 @@ int iris_set_ir_period(struct iris_inst *inst, enum platform_inst_fw_cap_type ca &ir_period, sizeof(u32)); } +int iris_set_ltr_count_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + u32 ltr_count = inst->fw_caps[cap_id].value; + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + struct hfi_ltr_mode ltr_mode; + + if (!ltr_count) + return -EINVAL; + + ltr_mode.count = ltr_count; + ltr_mode.mode = HFI_LTR_MODE_MANUAL; + ltr_mode.trust_mode = 1; + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_STRUCTURE, + <r_mode, sizeof(ltr_mode)); +} + +int iris_set_use_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + struct vb2_queue *sq = v4l2_m2m_get_src_vq(inst->m2m_ctx); + struct vb2_queue *dq = v4l2_m2m_get_dst_vq(inst->m2m_ctx); + u32 ltr_count = inst->fw_caps[LTR_COUNT].value; + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + struct hfi_ltr_use ltr_use; + + if (!vb2_is_streaming(sq) && !vb2_is_streaming(dq)) + return -EINVAL; + + if (!ltr_count) + return -EINVAL; + + ltr_use.ref_ltr = inst->fw_caps[cap_id].value; + ltr_use.use_constrnt = true; + ltr_use.frames = 0; + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_STRUCTURE, + <r_use, sizeof(ltr_use)); +} + +int iris_set_mark_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + struct vb2_queue *sq = v4l2_m2m_get_src_vq(inst->m2m_ctx); + struct vb2_queue *dq = v4l2_m2m_get_dst_vq(inst->m2m_ctx); + u32 ltr_count = inst->fw_caps[LTR_COUNT].value; + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + struct hfi_ltr_mark ltr_mark; + + if (!vb2_is_streaming(sq) && !vb2_is_streaming(dq)) + return -EINVAL; + + if (!ltr_count) + return -EINVAL; + + ltr_mark.mark_frame = inst->fw_caps[cap_id].value; + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_STRUCTURE, + <r_mark, sizeof(ltr_mark)); +} + +int iris_set_ltr_count_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + u32 ltr_count = inst->fw_caps[cap_id].value; + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + + if (!ltr_count) + return -EINVAL; + + if (inst->hfi_rc_type == HFI_RC_CBR_VFR || + inst->hfi_rc_type == HFI_RC_CBR_CFR || + inst->hfi_rc_type == HFI_RC_OFF) { + inst->fw_caps[LTR_COUNT].value = 0; + return -EINVAL; + } + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_U32, + <r_count, sizeof(u32)); +} + +int iris_set_use_and_mark_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + struct vb2_queue *sq = v4l2_m2m_get_src_vq(inst->m2m_ctx); + struct vb2_queue *dq = v4l2_m2m_get_dst_vq(inst->m2m_ctx); + u32 ltr_count = inst->fw_caps[LTR_COUNT].value; + u32 hfi_val = inst->fw_caps[cap_id].value; + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + + if (!vb2_is_streaming(sq) && !vb2_is_streaming(dq)) + return -EINVAL; + + if (!ltr_count || hfi_val == INVALID_DEFAULT_MARK_OR_USE_LTR) + return -EINVAL; + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_U32, + &hfi_val, sizeof(u32)); +} + +int iris_set_intra_period(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + u32 gop_size = inst->fw_caps[GOP_SIZE].value; + u32 b_frame = inst->fw_caps[B_FRAME].value; + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + struct hfi_intra_period intra_period; + + if (!gop_size || b_frame >= gop_size) + return -EINVAL; + + /* + * intra_period represents the length of a GOP, which includes both P-frames + * and B-frames. The counts of P-frames and B-frames within a GOP must be + * communicated to the firmware. + */ + intra_period.pframes = (gop_size - 1) / (b_frame + 1); + intra_period.bframes = b_frame; + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_STRUCTURE, + &intra_period, sizeof(intra_period)); +} + +int iris_set_layer_type(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + u32 layer_enable = inst->fw_caps[LAYER_ENABLE].value; + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + u32 layer_type; + + if (inst->hfi_rc_type == HFI_RATE_CONTROL_CQ || + inst->hfi_rc_type == HFI_RATE_CONTROL_OFF) + return -EINVAL; + + if (inst->codec == V4L2_PIX_FMT_H264) { + if (!layer_enable || !inst->fw_caps[LAYER_COUNT_H264].value) + return -EINVAL; + + if (inst->fw_caps[LAYER_TYPE_H264].value == + V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P) { + if (inst->hfi_rc_type == HFI_RC_VBR_CFR) + layer_type = HFI_HIER_P_HYBRID_LTR; + else + layer_type = HFI_HIER_P_SLIDING_WINDOW; + } else if (inst->fw_caps[LAYER_TYPE_H264].value == + V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_B) { + if (inst->hfi_rc_type == HFI_RC_VBR_CFR) + layer_type = HFI_HIER_B; + else + return -EINVAL; + } else { + return -EINVAL; + } + } else if (inst->codec == V4L2_PIX_FMT_HEVC) { + if (!inst->fw_caps[LAYER_COUNT_HEVC].value) + return -EINVAL; + + if (inst->fw_caps[LAYER_TYPE_HEVC].value == + V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P) { + layer_type = HFI_HIER_P_SLIDING_WINDOW; + } else if (inst->fw_caps[LAYER_TYPE_HEVC].value == + V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_B) { + if (inst->hfi_rc_type == HFI_RC_VBR_CFR) + layer_type = HFI_HIER_B; + else + return -EINVAL; + } else { + return -EINVAL; + } + } else { + return -EINVAL; + } + + inst->hfi_layer_type = layer_type; + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_U32_ENUM, + &layer_type, sizeof(u32)); +} + +int iris_set_layer_count_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + struct vb2_queue *sq = v4l2_m2m_get_src_vq(inst->m2m_ctx); + struct vb2_queue *dq = v4l2_m2m_get_dst_vq(inst->m2m_ctx); + u32 layer_enable = inst->fw_caps[LAYER_ENABLE].value; + u32 layer_count = inst->fw_caps[cap_id].value; + u32 hfi_id, ret; + + if (!layer_enable || !layer_count) + return -EINVAL; + + inst->hfi_layer_count = layer_count; + + if (!vb2_is_streaming(sq) && !vb2_is_streaming(dq)) { + hfi_id = HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER; + ret = hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_U32, + &layer_count, sizeof(u32)); + if (ret) + return ret; + } + + hfi_id = inst->fw_caps[cap_id].hfi_id; + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_U32, + &layer_count, sizeof(u32)); +} + +int iris_set_layer_count_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + u32 layer_type = inst->hfi_layer_type; + u32 layer_count, layer_count_max; + + layer_count = (inst->codec == V4L2_PIX_FMT_H264) ? + inst->fw_caps[LAYER_COUNT_H264].value : + inst->fw_caps[LAYER_COUNT_HEVC].value; + + if (!layer_count) + return -EINVAL; + + if (layer_type == HFI_HIER_B) { + layer_count_max = MAX_LAYER_HB; + } else if (layer_type == HFI_HIER_P_HYBRID_LTR) { + layer_count_max = MAX_AVC_LAYER_HP_HYBRID_LTR; + } else if (layer_type == HFI_HIER_P_SLIDING_WINDOW) { + if (inst->codec == V4L2_PIX_FMT_H264) { + layer_count_max = MAX_AVC_LAYER_HP_SLIDING_WINDOW; + } else { + if (inst->hfi_rc_type == HFI_RC_VBR_CFR) + layer_count_max = MAX_HEVC_VBR_LAYER_HP_SLIDING_WINDOW; + else + layer_count_max = MAX_HEVC_LAYER_HP_SLIDING_WINDOW; + } + } else { + return -EINVAL; + } + + if (layer_count > layer_count_max) + layer_count = layer_count_max; + + layer_count += 1; /* base layer */ + inst->hfi_layer_count = layer_count; + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_U32, + &layer_count, sizeof(u32)); +} + +int iris_set_layer_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + struct vb2_queue *sq = v4l2_m2m_get_src_vq(inst->m2m_ctx); + struct vb2_queue *dq = v4l2_m2m_get_dst_vq(inst->m2m_ctx); + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + u32 bitrate = inst->fw_caps[cap_id].value; + + /* ignore layer bitrate when total bitrate is set */ + if (inst->fw_caps[BITRATE].flags & CAP_FLAG_CLIENT_SET) + return 0; + + if (!(inst->fw_caps[cap_id].flags & CAP_FLAG_CLIENT_SET)) + return -EINVAL; + + if (!vb2_is_streaming(sq) && !vb2_is_streaming(dq)) + return -EINVAL; + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_U32, + &bitrate, sizeof(u32)); +} + int iris_set_properties(struct iris_inst *inst, u32 plane) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; struct platform_inst_fw_cap *cap; int ret; u32 i; diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.h b/drivers/media/platform/qcom/iris/iris_ctrls.h index 9518803577bc..3c462ec9190b 100644 --- a/drivers/media/platform/qcom/iris/iris_ctrls.h +++ b/drivers/media/platform/qcom/iris/iris_ctrls.h @@ -22,7 +22,8 @@ int iris_set_level(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id int iris_set_profile_level_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_header_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_header_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); -int iris_set_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_bitrate_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_bitrate_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_peak_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_bitrate_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_bitrate_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); @@ -34,7 +35,18 @@ int iris_set_frame_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap int iris_set_qp_range(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_rotation(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_flip(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); -int iris_set_ir_period(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_ir_period_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_ir_period_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_ltr_count_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_ltr_count_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_use_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_mark_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_use_and_mark_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_intra_period(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_layer_type(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_layer_count_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_layer_count_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_layer_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_properties(struct iris_inst *inst, u32 plane); #endif diff --git a/drivers/media/platform/qcom/iris/iris_firmware.c b/drivers/media/platform/qcom/iris/iris_firmware.c index 5f408024e967..1a476146d758 100644 --- a/drivers/media/platform/qcom/iris/iris_firmware.c +++ b/drivers/media/platform/qcom/iris/iris_firmware.c @@ -12,11 +12,12 @@ #include "iris_core.h" #include "iris_firmware.h" +#define IRIS_PAS_ID 9 + #define MAX_FIRMWARE_NAME_SIZE 128 static int iris_load_fw_to_memory(struct iris_core *core, const char *fw_name) { - u32 pas_id = core->iris_platform_data->pas_id; const struct firmware *firmware = NULL; struct device *dev = core->dev; struct resource res; @@ -53,7 +54,7 @@ static int iris_load_fw_to_memory(struct iris_core *core, const char *fw_name) } ret = qcom_mdt_load(dev, firmware, fw_name, - pas_id, mem_virt, mem_phys, res_size, NULL); + IRIS_PAS_ID, mem_virt, mem_phys, res_size, NULL); memunmap(mem_virt); err_release_fw: @@ -71,7 +72,7 @@ int iris_fw_load(struct iris_core *core) ret = of_property_read_string_index(core->dev->of_node, "firmware-name", 0, &fwpath); if (ret) - fwpath = core->iris_platform_data->fwname; + fwpath = core->iris_firmware_desc->fwname; ret = iris_load_fw_to_memory(core, fwpath); if (ret) { @@ -79,7 +80,7 @@ int iris_fw_load(struct iris_core *core) return -ENOMEM; } - ret = qcom_scm_pas_auth_and_reset(core->iris_platform_data->pas_id); + ret = qcom_scm_pas_auth_and_reset(IRIS_PAS_ID); if (ret) { dev_err(core->dev, "auth and reset failed: %d\n", ret); return ret; @@ -93,7 +94,7 @@ int iris_fw_load(struct iris_core *core) cp_config->cp_nonpixel_size); if (ret) { dev_err(core->dev, "qcom_scm_mem_protect_video_var failed: %d\n", ret); - qcom_scm_pas_shutdown(core->iris_platform_data->pas_id); + qcom_scm_pas_shutdown(IRIS_PAS_ID); return ret; } } @@ -103,7 +104,7 @@ int iris_fw_load(struct iris_core *core) int iris_fw_unload(struct iris_core *core) { - return qcom_scm_pas_shutdown(core->iris_platform_data->pas_id); + return qcom_scm_pas_shutdown(IRIS_PAS_ID); } int iris_set_hw_state(struct iris_core *core, bool resume) diff --git a/drivers/media/platform/qcom/iris/iris_hfi_common.c b/drivers/media/platform/qcom/iris/iris_hfi_common.c index 621c66593d88..8769ec61f117 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_common.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_common.c @@ -76,7 +76,7 @@ u32 iris_hfi_get_v4l2_matrix_coefficients(u32 hfi_coefficients) int iris_hfi_core_init(struct iris_core *core) { - const struct iris_hfi_command_ops *hfi_ops = core->hfi_ops; + const struct iris_hfi_sys_ops *hfi_ops = core->hfi_sys_ops; int ret; ret = hfi_ops->sys_init(core); @@ -109,7 +109,7 @@ irqreturn_t iris_hfi_isr_handler(int irq, void *data) iris_vpu_clear_interrupt(core); mutex_unlock(&core->lock); - core->hfi_response_ops->hfi_response_handler(core); + core->hfi_sys_ops->sys_hfi_response_handler(core); if (!iris_vpu_watchdog(core, core->intr_status)) enable_irq(irq); @@ -144,7 +144,7 @@ error: int iris_hfi_pm_resume(struct iris_core *core) { - const struct iris_hfi_command_ops *ops = core->hfi_ops; + const struct iris_hfi_sys_ops *ops = core->hfi_sys_ops; int ret; ret = iris_vpu_power_on(core); diff --git a/drivers/media/platform/qcom/iris/iris_hfi_common.h b/drivers/media/platform/qcom/iris/iris_hfi_common.h index 3edb5ae582b4..a27447eb2519 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_common.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_common.h @@ -105,11 +105,18 @@ struct iris_hfi_prop_type_handle { int (*handle)(struct iris_inst *inst, u32 plane); }; -struct iris_hfi_command_ops { +struct iris_hfi_sys_ops { int (*sys_init)(struct iris_core *core); int (*sys_image_version)(struct iris_core *core); int (*sys_interframe_powercollapse)(struct iris_core *core); int (*sys_pc_prep)(struct iris_core *core); + + void (*sys_hfi_response_handler)(struct iris_core *core); + + struct iris_inst *(*sys_get_instance)(void); +}; + +struct iris_hfi_session_ops { int (*session_set_config_params)(struct iris_inst *inst, u32 plane); int (*session_set_property)(struct iris_inst *inst, u32 packet_type, u32 flag, u32 plane, u32 payload_type, @@ -126,10 +133,6 @@ struct iris_hfi_command_ops { int (*session_close)(struct iris_inst *inst); }; -struct iris_hfi_response_ops { - void (*hfi_response_handler)(struct iris_core *core); -}; - struct hfi_subscription_params { u32 bitstream_resolution; u32 crop_offsets[2]; diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen1.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1.c index df8e6bf9430e..ca1545d28b53 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen1.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1.c @@ -3,38 +3,16 @@ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ -#include "iris_core.h" #include "iris_ctrls.h" #include "iris_platform_common.h" -#include "iris_resources.h" #include "iris_hfi_gen1.h" #include "iris_hfi_gen1_defines.h" #include "iris_vpu_buffer.h" -#include "iris_vpu_common.h" -#include "iris_instance.h" - -#include "iris_platform_sc7280.h" #define BITRATE_MIN 32000 #define BITRATE_MAX 160000000 -#define BITRATE_PEAK_DEFAULT (BITRATE_DEFAULT * 2) #define BITRATE_STEP 100 -static struct iris_fmt platform_fmts_sm8250_dec[] = { - [IRIS_FMT_H264] = { - .pixfmt = V4L2_PIX_FMT_H264, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, - [IRIS_FMT_HEVC] = { - .pixfmt = V4L2_PIX_FMT_HEVC, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, - [IRIS_FMT_VP9] = { - .pixfmt = V4L2_PIX_FMT_VP9, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, -}; - static struct platform_inst_fw_cap inst_fw_cap_sm8250_dec[] = { { .cap_id = PIPE, @@ -158,7 +136,7 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8250_enc[] = { .hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE, .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, - .set = iris_set_bitrate, + .set = iris_set_bitrate_gen1, }, { .cap_id = BITRATE_MODE, @@ -246,60 +224,164 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8250_enc[] = { .flags = CAP_FLAG_OUTPUT_PORT, .set = iris_set_qp_range, }, -}; - -static struct platform_inst_caps platform_inst_cap_sm8250 = { - .min_frame_width = 128, - .max_frame_width = 8192, - .min_frame_height = 128, - .max_frame_height = 8192, - .max_mbpf = 138240, - .mb_cycles_vsp = 25, - .mb_cycles_vpp = 200, - .max_frame_rate = MAXIMUM_FPS, - .max_operating_rate = MAXIMUM_FPS, -}; - -static void iris_set_sm8250_preset_registers(struct iris_core *core) -{ - writel(0x0, core->reg_base + 0xB0088); -} - -static const struct icc_info sm8250_icc_table[] = { - { "cpu-cfg", 1000, 1000 }, - { "video-mem", 1000, 15000000 }, -}; - -static const char * const sm8250_clk_reset_table[] = { "bus", "core" }; - -static const struct bw_info sm8250_bw_table_dec[] = { - { ((4096 * 2160) / 256) * 60, 2403000 }, - { ((4096 * 2160) / 256) * 30, 1224000 }, - { ((1920 * 1080) / 256) * 60, 812000 }, - { ((1920 * 1080) / 256) * 30, 416000 }, -}; - -static const char * const sm8250_pmdomain_table[] = { "venus", "vcodec0" }; - -static const char * const sm8250_opp_pd_table[] = { "mx" }; - -static const struct platform_clk_data sm8250_clk_table[] = { - {IRIS_AXI_CLK, "iface" }, - {IRIS_CTRL_CLK, "core" }, - {IRIS_HW_CLK, "vcodec0_core" }, -}; - -static const char * const sm8250_opp_clk_table[] = { - "vcodec0_core", - NULL, -}; - -static const struct tz_cp_config tz_cp_config_sm8250[] = { { - .cp_start = 0, - .cp_size = 0x25800000, - .cp_nonpixel_start = 0x01000000, - .cp_nonpixel_size = 0x24800000, + .cap_id = IR_TYPE, + .min = V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM, + .max = V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_CYCLIC, + .step_or_mask = BIT(V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM) | + BIT(V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_CYCLIC), + .value = V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + }, + { + .cap_id = IR_PERIOD, + .min = 0, + .max = ((4096 * 2304) >> 8), + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH, + .flags = CAP_FLAG_OUTPUT_PORT, + .set = iris_set_ir_period_gen1, + }, + { + .cap_id = LTR_COUNT, + .min = 0, + .max = MAX_LTR_FRAME_COUNT_GEN1, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROPERTY_PARAM_VENC_LTRMODE, + .flags = CAP_FLAG_OUTPUT_PORT, + .set = iris_set_ltr_count_gen1, + }, + { + .cap_id = USE_LTR, + .min = 0, + .max = ((1 << MAX_LTR_FRAME_COUNT_GEN1) - 1), + .step_or_mask = 0, + .value = 0, + .hfi_id = HFI_PROPERTY_CONFIG_VENC_USELTRFRAME, + .flags = CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_use_ltr, + }, + { + .cap_id = MARK_LTR, + .min = 0, + .max = (MAX_LTR_FRAME_COUNT_GEN1 - 1), + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME, + .flags = CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_mark_ltr, + }, + { + .cap_id = B_FRAME, + .min = 0, + .max = 3, + .step_or_mask = 1, + .value = 0, + .flags = CAP_FLAG_OUTPUT_PORT, + }, + { + .cap_id = INTRA_PERIOD, + .min = 0, + .max = 1, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD, + .flags = CAP_FLAG_OUTPUT_PORT, + .set = iris_set_intra_period, + }, + { + .cap_id = LAYER_ENABLE, + .min = 0, + .max = 1, + .step_or_mask = 1, + .value = 0, + .flags = CAP_FLAG_OUTPUT_PORT, + }, + { + .cap_id = LAYER_TYPE_H264, + .min = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P, + .max = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P), + .value = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + }, + { + .cap_id = LAYER_COUNT_H264, + .min = 0, + .max = MAX_HIER_CODING_LAYER_GEN1, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER, + .flags = CAP_FLAG_OUTPUT_PORT, + .set = iris_set_layer_count_gen1, + }, + { + .cap_id = LAYER0_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_bitrate_gen1, + }, + { + .cap_id = LAYER1_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_bitrate_gen1, + }, + { + .cap_id = LAYER2_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_bitrate_gen1, + }, + { + .cap_id = LAYER3_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_bitrate_gen1, + }, + { + .cap_id = LAYER4_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_bitrate_gen1, + }, + { + .cap_id = LAYER5_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_bitrate_gen1, }, }; @@ -337,99 +419,14 @@ static const u32 sm8250_enc_ip_int_buf_tbl[] = { BUF_SCRATCH_2, }; -const struct iris_platform_data sm8250_data = { - .get_instance = iris_hfi_gen1_get_instance, - .init_hfi_command_ops = &iris_hfi_gen1_command_ops_init, - .init_hfi_response_ops = iris_hfi_gen1_response_ops_init, - .get_vpu_buffer_size = iris_vpu_buf_size, - .vpu_ops = &iris_vpu2_ops, - .set_preset_registers = iris_set_sm8250_preset_registers, - .icc_tbl = sm8250_icc_table, - .icc_tbl_size = ARRAY_SIZE(sm8250_icc_table), - .clk_rst_tbl = sm8250_clk_reset_table, - .clk_rst_tbl_size = ARRAY_SIZE(sm8250_clk_reset_table), - .bw_tbl_dec = sm8250_bw_table_dec, - .bw_tbl_dec_size = ARRAY_SIZE(sm8250_bw_table_dec), - .pmdomain_tbl = sm8250_pmdomain_table, - .pmdomain_tbl_size = ARRAY_SIZE(sm8250_pmdomain_table), - .opp_pd_tbl = sm8250_opp_pd_table, - .opp_pd_tbl_size = ARRAY_SIZE(sm8250_opp_pd_table), - .clk_tbl = sm8250_clk_table, - .clk_tbl_size = ARRAY_SIZE(sm8250_clk_table), - .opp_clk_tbl = sm8250_opp_clk_table, - /* Upper bound of DMA address range */ - .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu-1.0/venus.mbn", - .pas_id = IRIS_PAS_ID, - .inst_iris_fmts = platform_fmts_sm8250_dec, - .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8250_dec), - .inst_caps = &platform_inst_cap_sm8250, - .inst_fw_caps_dec = inst_fw_cap_sm8250_dec, - .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8250_dec), - .inst_fw_caps_enc = inst_fw_cap_sm8250_enc, - .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8250_enc), - .tz_cp_config_data = tz_cp_config_sm8250, - .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8250), - .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, - .num_vpp_pipe = 4, - .max_session_count = 16, - .max_core_mbpf = NUM_MBS_8K, - .max_core_mbps = ((7680 * 4320) / 256) * 60, - .dec_input_config_params_default = - sm8250_vdec_input_config_param_default, - .dec_input_config_params_default_size = - ARRAY_SIZE(sm8250_vdec_input_config_param_default), - .enc_input_config_params = sm8250_venc_input_config_param, - .enc_input_config_params_size = - ARRAY_SIZE(sm8250_venc_input_config_param), - - .dec_ip_int_buf_tbl = sm8250_dec_ip_int_buf_tbl, - .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8250_dec_ip_int_buf_tbl), - .dec_op_int_buf_tbl = sm8250_dec_op_int_buf_tbl, - .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8250_dec_op_int_buf_tbl), - - .enc_ip_int_buf_tbl = sm8250_enc_ip_int_buf_tbl, - .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8250_enc_ip_int_buf_tbl), -}; +const struct iris_firmware_data iris_hfi_gen1_data = { + .init_hfi_ops = &iris_hfi_gen1_sys_ops_init, -const struct iris_platform_data sc7280_data = { - .get_instance = iris_hfi_gen1_get_instance, - .init_hfi_command_ops = &iris_hfi_gen1_command_ops_init, - .init_hfi_response_ops = iris_hfi_gen1_response_ops_init, - .get_vpu_buffer_size = iris_vpu_buf_size, - .vpu_ops = &iris_vpu2_ops, - .set_preset_registers = iris_set_sm8250_preset_registers, - .icc_tbl = sm8250_icc_table, - .icc_tbl_size = ARRAY_SIZE(sm8250_icc_table), - .bw_tbl_dec = sc7280_bw_table_dec, - .bw_tbl_dec_size = ARRAY_SIZE(sc7280_bw_table_dec), - .pmdomain_tbl = sm8250_pmdomain_table, - .pmdomain_tbl_size = ARRAY_SIZE(sm8250_pmdomain_table), - .opp_pd_tbl = sc7280_opp_pd_table, - .opp_pd_tbl_size = ARRAY_SIZE(sc7280_opp_pd_table), - .clk_tbl = sc7280_clk_table, - .clk_tbl_size = ARRAY_SIZE(sc7280_clk_table), - .opp_clk_tbl = sc7280_opp_clk_table, - /* Upper bound of DMA address range */ - .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu/vpu20_p1.mbn", - .pas_id = IRIS_PAS_ID, - .inst_iris_fmts = platform_fmts_sm8250_dec, - .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8250_dec), - .inst_caps = &platform_inst_cap_sm8250, .inst_fw_caps_dec = inst_fw_cap_sm8250_dec, .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8250_dec), .inst_fw_caps_enc = inst_fw_cap_sm8250_enc, .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8250_enc), - .tz_cp_config_data = tz_cp_config_sm8250, - .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8250), - .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, - .num_vpp_pipe = 1, - .no_aon = true, - .max_session_count = 16, - .max_core_mbpf = 4096 * 2176 / 256 * 2 + 1920 * 1088 / 256, - /* max spec for SC7280 is 4096x2176@60fps */ - .max_core_mbps = 4096 * 2176 / 256 * 60, + .dec_input_config_params_default = sm8250_vdec_input_config_param_default, .dec_input_config_params_default_size = diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1.h b/drivers/media/platform/qcom/iris/iris_hfi_gen1.h index 19b8e9054a75..c37adf65055a 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1.h @@ -7,10 +7,8 @@ #define __IRIS_HFI_GEN1_H__ struct iris_core; -struct iris_inst; -void iris_hfi_gen1_command_ops_init(struct iris_core *core); -void iris_hfi_gen1_response_ops_init(struct iris_core *core); -struct iris_inst *iris_hfi_gen1_get_instance(void); +void iris_hfi_gen1_sys_ops_init(struct iris_core *core); +void iris_hfi_gen1_response_handler(struct iris_core *core); #endif diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c index e42d17653c2c..7674b47ad6c4 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c @@ -485,7 +485,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p { void *prop_data = &packet->data[1]; - packet->shdr.hdr.size = sizeof(*packet); + packet->shdr.hdr.size = sizeof(*packet) + sizeof(ptype); packet->shdr.hdr.pkt_type = HFI_CMD_SESSION_SET_PROPERTY; packet->shdr.session_id = inst->session_id; packet->num_properties = 1; @@ -498,14 +498,14 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p fsize->buffer_type = in->buffer_type; fsize->height = in->height; fsize->width = in->width; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*fsize); + packet->shdr.hdr.size += sizeof(*fsize); 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; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*cu); + packet->shdr.hdr.size += sizeof(*cu); break; } case HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT: { @@ -514,7 +514,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p hfi->buffer_type = in->buffer_type; hfi->format = in->format; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*hfi); + packet->shdr.hdr.size += sizeof(*hfi); break; } case HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO: { @@ -533,7 +533,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p info->plane_format[1].buffer_alignment = 256; } - packet->shdr.hdr.size += sizeof(u32) + sizeof(*info); + packet->shdr.hdr.size += sizeof(*info); break; } case HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL: { @@ -543,7 +543,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p count->type = in->type; count->count_actual = in->count_actual; count->count_min_host = in->count_min_host; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*count); + packet->shdr.hdr.size += sizeof(*count); break; } case HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM: { @@ -552,7 +552,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p multi->buffer_type = in->buffer_type; multi->enable = in->enable; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*multi); + packet->shdr.hdr.size += sizeof(*multi); break; } case HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL: { @@ -560,7 +560,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p sz->size = in->size; sz->type = in->type; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*sz); + packet->shdr.hdr.size += sizeof(*sz); break; } case HFI_PROPERTY_PARAM_WORK_ROUTE: { @@ -568,7 +568,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p u32 *in = pdata; wr->video_work_route = *in; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*wr); + packet->shdr.hdr.size += sizeof(*wr); break; } case HFI_PROPERTY_PARAM_WORK_MODE: { @@ -576,7 +576,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p u32 *in = pdata; wm->video_work_mode = *in; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*wm); + packet->shdr.hdr.size += sizeof(*wm); break; } case HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT: { @@ -592,7 +592,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p /* Level not supported, falling back to 1 */ pl->level = 1; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*pl); + packet->shdr.hdr.size += sizeof(*pl); break; } case HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER: { @@ -600,16 +600,15 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p u32 *in = pdata; en->enable = *in; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*en); + packet->shdr.hdr.size += sizeof(*en); break; } case HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE: { - struct hfi_bitrate *brate = prop_data; - u32 *in = pdata; + struct hfi_bitrate *in = pdata, *brate = prop_data; - brate->bitrate = *in; - brate->layer_id = 0; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*brate); + brate->bitrate = in->bitrate; + brate->layer_id = in->layer_id; + packet->shdr.hdr.size += sizeof(*brate); break; } case HFI_PROPERTY_PARAM_VENC_RATE_CONTROL: { @@ -628,7 +627,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p } packet->data[1] = *in; - packet->shdr.hdr.size += sizeof(u32) * 2; + packet->shdr.hdr.size += sizeof(u32); break; } case HFI_PROPERTY_PARAM_VENC_H264_ENTROPY_CONTROL: { @@ -638,7 +637,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p entropy->entropy_mode = *in; if (entropy->entropy_mode == HFI_H264_ENTROPY_CABAC) entropy->cabac_model = HFI_H264_CABAC_MODEL_0; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*entropy); + packet->shdr.hdr.size += sizeof(*entropy); break; } case HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE_V2: { @@ -663,7 +662,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p ((max_qp & 0xFF) << 16); range->min_qp.enable = 7; range->max_qp.enable = 7; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*range); + packet->shdr.hdr.size += sizeof(*range); break; } case HFI_PROPERTY_CONFIG_FRAME_RATE: { @@ -672,7 +671,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p frate->buffer_type = in->buffer_type; frate->framerate = in->framerate; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*frate); + packet->shdr.hdr.size += sizeof(*frate); break; } case HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO: { @@ -684,7 +683,62 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p plane_actual_info->plane_format[0] = in->plane_format[0]; if (in->num_planes > 1) plane_actual_info->plane_format[1] = in->plane_format[1]; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*plane_actual_info); + packet->shdr.hdr.size += sizeof(*plane_actual_info); + break; + } + case HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH: { + struct hfi_intra_refresh *in = pdata, *intra_refresh = prop_data; + + intra_refresh->mode = in->mode; + intra_refresh->mbs = in->mbs; + packet->shdr.hdr.size += sizeof(*intra_refresh); + break; + } + case HFI_PROPERTY_PARAM_VENC_LTRMODE: { + struct hfi_ltr_mode *in = pdata, *ltr_mode = prop_data; + + ltr_mode->mode = in->mode; + ltr_mode->count = in->count; + ltr_mode->trust_mode = in->trust_mode; + packet->shdr.hdr.size += sizeof(*ltr_mode); + break; + } + case HFI_PROPERTY_CONFIG_VENC_USELTRFRAME: { + struct hfi_ltr_use *in = pdata, *ltr_use = prop_data; + + ltr_use->frames = in->frames; + ltr_use->ref_ltr = in->ref_ltr; + ltr_use->use_constrnt = in->use_constrnt; + packet->shdr.hdr.size += sizeof(*ltr_use); + break; + } + case HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME: { + struct hfi_ltr_mark *in = pdata, *ltr_mark = prop_data; + + ltr_mark->mark_frame = in->mark_frame; + packet->shdr.hdr.size += sizeof(*ltr_mark); + break; + } + case HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD: { + struct hfi_intra_period *in = pdata, *intra_period = prop_data; + + intra_period->pframes = in->pframes; + intra_period->bframes = in->bframes; + packet->shdr.hdr.size += sizeof(*intra_period); + break; + } + case HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER: { + u32 *in = pdata; + + packet->data[1] = *in; + packet->shdr.hdr.size += sizeof(u32); + break; + } + case HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER: { + u32 *in = pdata; + + packet->data[1] = *in; + packet->shdr.hdr.size += sizeof(u32); break; } default: @@ -918,7 +972,7 @@ static int iris_hfi_gen1_set_bufsize(struct iris_inst *inst, u32 plane) if (iris_split_mode_enabled(inst)) { bufsz.type = HFI_BUFFER_OUTPUT; - bufsz.size = inst->core->iris_platform_data->get_vpu_buffer_size(inst, BUF_DPB); + bufsz.size = inst->core->iris_firmware_desc->get_vpu_buffer_size(inst, BUF_DPB); ret = hfi_gen1_set_property(inst, ptype, &bufsz, sizeof(bufsz)); if (ret) @@ -1033,8 +1087,8 @@ static int iris_hfi_gen1_session_set_config_params(struct iris_inst *inst, u32 p }; if (inst->domain == DECODER) { - config_params = core->iris_platform_data->dec_input_config_params_default; - config_params_size = core->iris_platform_data->dec_input_config_params_default_size; + config_params = core->iris_firmware_data->dec_input_config_params_default; + config_params_size = core->iris_firmware_data->dec_input_config_params_default_size; if (V4L2_TYPE_IS_OUTPUT(plane)) { handler = vdec_prop_type_handle_inp_arr; handler_size = ARRAY_SIZE(vdec_prop_type_handle_inp_arr); @@ -1043,8 +1097,8 @@ static int iris_hfi_gen1_session_set_config_params(struct iris_inst *inst, u32 p handler_size = ARRAY_SIZE(vdec_prop_type_handle_out_arr); } } else { - config_params = core->iris_platform_data->enc_input_config_params; - config_params_size = core->iris_platform_data->enc_input_config_params_size; + config_params = core->iris_firmware_data->enc_input_config_params; + config_params_size = core->iris_firmware_data->enc_input_config_params_size; handler = venc_prop_type_handle_inp_arr; handler_size = ARRAY_SIZE(venc_prop_type_handle_inp_arr); } @@ -1063,11 +1117,7 @@ static int iris_hfi_gen1_session_set_config_params(struct iris_inst *inst, u32 p return 0; } -static const struct iris_hfi_command_ops iris_hfi_gen1_command_ops = { - .sys_init = iris_hfi_gen1_sys_init, - .sys_image_version = iris_hfi_gen1_sys_image_version, - .sys_interframe_powercollapse = iris_hfi_gen1_sys_interframe_powercollapse, - .sys_pc_prep = iris_hfi_gen1_sys_pc_prep, +static const struct iris_hfi_session_ops iris_hfi_gen1_session_ops = { .session_open = iris_hfi_gen1_session_open, .session_set_config_params = iris_hfi_gen1_session_set_config_params, .session_set_property = iris_hfi_gen1_session_set_property, @@ -1080,12 +1130,31 @@ static const struct iris_hfi_command_ops iris_hfi_gen1_command_ops = { .session_close = iris_hfi_gen1_session_close, }; -void iris_hfi_gen1_command_ops_init(struct iris_core *core) +static struct iris_inst *iris_hfi_gen1_get_instance(void) { - core->hfi_ops = &iris_hfi_gen1_command_ops; + struct iris_inst *out; + + out = kzalloc_obj(*out); + if (!out) + return NULL; + + out->hfi_session_ops = &iris_hfi_gen1_session_ops; + + return out; } -struct iris_inst *iris_hfi_gen1_get_instance(void) +static const struct iris_hfi_sys_ops iris_hfi_gen1_sys_ops = { + .sys_init = iris_hfi_gen1_sys_init, + .sys_image_version = iris_hfi_gen1_sys_image_version, + .sys_interframe_powercollapse = iris_hfi_gen1_sys_interframe_powercollapse, + .sys_pc_prep = iris_hfi_gen1_sys_pc_prep, + + .sys_hfi_response_handler = iris_hfi_gen1_response_handler, + + .sys_get_instance = iris_hfi_gen1_get_instance, +}; + +void iris_hfi_gen1_sys_ops_init(struct iris_core *core) { - return kzalloc_obj(struct iris_inst); + core->hfi_sys_ops = &iris_hfi_gen1_sys_ops; } diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h index 42226ccee3d9..0e4dee192384 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h @@ -139,9 +139,28 @@ #define HFI_PROPERTY_PARAM_VENC_H264_DEBLOCK_CONTROL 0x2005003 #define HFI_PROPERTY_PARAM_VENC_RATE_CONTROL 0x2005004 #define HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE_V2 0x2005009 + +#define HFI_INTRA_REFRESH_NONE 0x1 +#define HFI_INTRA_REFRESH_CYCLIC 0x2 +#define HFI_INTRA_REFRESH_ADAPTIVE 0x3 +#define HFI_INTRA_REFRESH_CYCLIC_ADAPTIVE 0x4 +#define HFI_INTRA_REFRESH_RANDOM 0x5 + +#define HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH 0x200500d + +#define HFI_LTR_MODE_DISABLE 0x0 +#define HFI_LTR_MODE_MANUAL 0x1 +#define HFI_LTR_MODE_PERIODIC 0x2 + +#define HFI_PROPERTY_PARAM_VENC_LTRMODE 0x200501c #define HFI_PROPERTY_PARAM_VENC_MAX_NUM_B_FRAMES 0x2005020 +#define HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER 0x2005026 #define HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE 0x2006001 +#define HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD 0x2006003 +#define HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME 0x2006009 +#define HFI_PROPERTY_CONFIG_VENC_USELTRFRAME 0x200600a #define HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER 0x2006008 +#define HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER 0x200600b struct hfi_pkt_hdr { u32 size; @@ -447,6 +466,36 @@ struct hfi_framerate { u32 framerate; }; +struct hfi_intra_refresh { + u32 mode; + u32 mbs; +}; + +struct hfi_ltr_mode { + u32 mode; + u32 count; + u32 trust_mode; +}; + +struct hfi_ltr_use { + u32 ref_ltr; + u32 use_constrnt; + u32 frames; +}; + +struct hfi_ltr_mark { + u32 mark_frame; +}; + +struct hfi_max_num_b_frames { + u32 max_num_b_frames; +}; + +struct hfi_intra_period { + u32 pframes; + u32 bframes; +}; + struct hfi_event_data { u32 error; u32 height; diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c index 8e864c239e29..bfd7495bf44f 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c @@ -688,7 +688,7 @@ static void iris_hfi_gen1_flush_debug_queue(struct iris_core *core, u8 *packet) } } -static void iris_hfi_gen1_response_handler(struct iris_core *core) +void iris_hfi_gen1_response_handler(struct iris_core *core) { memset(core->response_packet, 0, sizeof(struct hfi_pkt_hdr)); while (!iris_hfi_queue_msg_read(core, core->response_packet)) { @@ -698,12 +698,3 @@ static void iris_hfi_gen1_response_handler(struct iris_core *core) iris_hfi_gen1_flush_debug_queue(core, core->response_packet); } - -static const struct iris_hfi_response_ops iris_hfi_gen1_response_ops = { - .hfi_response_handler = iris_hfi_gen1_response_handler, -}; - -void iris_hfi_gen1_response_ops_init(struct iris_core *core) -{ - core->hfi_response_ops = &iris_hfi_gen1_response_ops; -} diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c index 5da90d47f9c6..acc0ed8adda1 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c @@ -4,40 +4,15 @@ * Copyright (c) 2025 Linaro Ltd */ -#include "iris_core.h" #include "iris_ctrls.h" #include "iris_hfi_gen2.h" #include "iris_hfi_gen2_defines.h" #include "iris_platform_common.h" #include "iris_vpu_buffer.h" -#include "iris_vpu_common.h" - -#include "iris_platform_qcs8300.h" -#include "iris_platform_sm8650.h" -#include "iris_platform_sm8750.h" #define VIDEO_ARCH_LX 1 #define BITRATE_MAX 245000000 -static struct iris_fmt platform_fmts_sm8550_dec[] = { - [IRIS_FMT_H264] = { - .pixfmt = V4L2_PIX_FMT_H264, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, - [IRIS_FMT_HEVC] = { - .pixfmt = V4L2_PIX_FMT_HEVC, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, - [IRIS_FMT_VP9] = { - .pixfmt = V4L2_PIX_FMT_VP9, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, - [IRIS_FMT_AV1] = { - .pixfmt = V4L2_PIX_FMT_AV1, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, -}; - static const struct platform_inst_fw_cap inst_fw_cap_sm8550_dec[] = { { .cap_id = PROFILE_H264, @@ -56,9 +31,10 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_dec[] = { { .cap_id = PROFILE_HEVC, .min = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, - .max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE, + .max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10, .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN) | - BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE), + BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE) | + BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10), .value = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, .hfi_id = HFI_PROP_PROFILE, .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, @@ -287,7 +263,7 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_dec[] = { { .cap_id = BIT_DEPTH, .min = BIT_DEPTH_8, - .max = BIT_DEPTH_8, + .max = BIT_DEPTH_10, .step_or_mask = 1, .value = BIT_DEPTH_8, .hfi_id = HFI_PROP_LUMA_CHROMA_BIT_DEPTH, @@ -416,7 +392,7 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_enc[] = { .hfi_id = HFI_PROP_TOTAL_BITRATE, .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, - .set = iris_set_bitrate, + .set = iris_set_bitrate_gen2, }, { .cap_id = BITRATE_PEAK, @@ -738,75 +714,230 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_enc[] = { .value = 0, .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, - .set = iris_set_ir_period, + .set = iris_set_ir_period_gen2, + }, + { + .cap_id = LTR_COUNT, + .min = 0, + .max = MAX_LTR_FRAME_COUNT_GEN2, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROP_LTR_COUNT, + .flags = CAP_FLAG_OUTPUT_PORT, + .set = iris_set_ltr_count_gen2, + }, + { + .cap_id = USE_LTR, + .min = 0, + .max = ((1 << MAX_LTR_FRAME_COUNT_GEN2) - 1), + .step_or_mask = 0, + .value = 0, + .hfi_id = HFI_PROP_LTR_USE, + .flags = CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_use_and_mark_ltr, + }, + { + .cap_id = MARK_LTR, + .min = INVALID_DEFAULT_MARK_OR_USE_LTR, + .max = (MAX_LTR_FRAME_COUNT_GEN2 - 1), + .step_or_mask = 1, + .value = INVALID_DEFAULT_MARK_OR_USE_LTR, + .hfi_id = HFI_PROP_LTR_MARK, + .flags = CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_use_and_mark_ltr, + }, + { + .cap_id = B_FRAME, + .min = 0, + .max = 1, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROP_MAX_B_FRAMES, + .flags = CAP_FLAG_OUTPUT_PORT, + .set = iris_set_u32, + }, + { + .cap_id = LAYER_ENABLE, + .min = 0, + .max = 1, + .step_or_mask = 1, + .value = 0, + .flags = CAP_FLAG_OUTPUT_PORT, + }, + { + .cap_id = LAYER_TYPE_H264, + .min = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_B, + .max = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_B) | + BIT(V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P), + .value = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P, + .hfi_id = HFI_PROP_LAYER_ENCODING_TYPE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_layer_type, + }, + { + .cap_id = LAYER_TYPE_HEVC, + .min = V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_B, + .max = V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_B) | + BIT(V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P), + .value = V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P, + .hfi_id = HFI_PROP_LAYER_ENCODING_TYPE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_layer_type, + }, + { + .cap_id = LAYER_COUNT_H264, + .min = 0, + .max = 5, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROP_LAYER_COUNT, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_count_gen2, + }, + { + .cap_id = LAYER_COUNT_HEVC, + .min = 0, + .max = 5, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROP_LAYER_COUNT, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_count_gen2, + }, + { + .cap_id = LAYER0_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER1, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, }, -}; - -static struct platform_inst_caps platform_inst_cap_sm8550 = { - .min_frame_width = 96, - .max_frame_width = 8192, - .min_frame_height = 96, - .max_frame_height = 8192, - .max_mbpf = (8192 * 4352) / 256, - .mb_cycles_vpp = 200, - .mb_cycles_fw = 489583, - .mb_cycles_fw_vpp = 66234, - .num_comv = 0, - .max_frame_rate = MAXIMUM_FPS, - .max_operating_rate = MAXIMUM_FPS, -}; - -static void iris_set_sm8550_preset_registers(struct iris_core *core) -{ - writel(0x0, core->reg_base + 0xB0088); -} - -static const struct icc_info sm8550_icc_table[] = { - { "cpu-cfg", 1000, 1000 }, - { "video-mem", 1000, 15000000 }, -}; - -static const char * const sm8550_clk_reset_table[] = { "bus" }; - -static const struct bw_info sm8550_bw_table_dec[] = { - { ((4096 * 2160) / 256) * 60, 1608000 }, - { ((4096 * 2160) / 256) * 30, 826000 }, - { ((1920 * 1080) / 256) * 60, 567000 }, - { ((1920 * 1080) / 256) * 30, 294000 }, -}; - -static const char * const sm8550_pmdomain_table[] = { "venus", "vcodec0" }; - -static const char * const sm8550_opp_pd_table[] = { "mxc", "mmcx" }; - -static const struct platform_clk_data sm8550_clk_table[] = { - {IRIS_AXI_CLK, "iface" }, - {IRIS_CTRL_CLK, "core" }, - {IRIS_HW_CLK, "vcodec0_core" }, -}; - -static const char * const sm8550_opp_clk_table[] = { - "vcodec0_core", - NULL, -}; - -static struct ubwc_config_data ubwc_config_sm8550 = { - .max_channels = 8, - .mal_length = 32, - .highest_bank_bit = 16, - .bank_swzl_level = 0, - .bank_swz2_level = 1, - .bank_swz3_level = 1, - .bank_spreading = 1, -}; - -static const struct tz_cp_config tz_cp_config_sm8550[] = { { - .cp_start = 0, - .cp_size = 0x25800000, - .cp_nonpixel_start = 0x01000000, - .cp_nonpixel_size = 0x24800000, + .cap_id = LAYER1_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER2, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, }, + { + .cap_id = LAYER2_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER3, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER3_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER4, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER4_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER5, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER5_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER6, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER0_BITRATE_HEVC, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER1, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER1_BITRATE_HEVC, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER2, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER2_BITRATE_HEVC, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER3, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER3_BITRATE_HEVC, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER4, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER4_BITRATE_HEVC, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER5, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER5_BITRATE_HEVC, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER6, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + } }; static const u32 sm8550_vdec_input_config_params_default[] = { @@ -866,6 +997,7 @@ static const u32 sm8550_vdec_output_config_params[] = { HFI_PROP_OPB_ENABLE, HFI_PROP_COLOR_FORMAT, HFI_PROP_LINEAR_STRIDE_SCANLINE, + HFI_PROP_UBWC_STRIDE_SCANLINE, }; static const u32 sm8550_venc_output_config_params[] = { @@ -921,347 +1053,16 @@ static const u32 sm8550_enc_op_int_buf_tbl[] = { BUF_SCRATCH_2, }; -const struct iris_platform_data sm8550_data = { - .get_instance = iris_hfi_gen2_get_instance, - .init_hfi_command_ops = iris_hfi_gen2_command_ops_init, - .init_hfi_response_ops = iris_hfi_gen2_response_ops_init, - .get_vpu_buffer_size = iris_vpu_buf_size, - .vpu_ops = &iris_vpu3_ops, - .set_preset_registers = iris_set_sm8550_preset_registers, - .icc_tbl = sm8550_icc_table, - .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table), - .clk_rst_tbl = sm8550_clk_reset_table, - .clk_rst_tbl_size = ARRAY_SIZE(sm8550_clk_reset_table), - .bw_tbl_dec = sm8550_bw_table_dec, - .bw_tbl_dec_size = ARRAY_SIZE(sm8550_bw_table_dec), - .pmdomain_tbl = sm8550_pmdomain_table, - .pmdomain_tbl_size = ARRAY_SIZE(sm8550_pmdomain_table), - .opp_pd_tbl = sm8550_opp_pd_table, - .opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table), - .clk_tbl = sm8550_clk_table, - .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table), - .opp_clk_tbl = sm8550_opp_clk_table, - /* Upper bound of DMA address range */ - .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu/vpu30_p4.mbn", - .pas_id = IRIS_PAS_ID, - .inst_iris_fmts = platform_fmts_sm8550_dec, - .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec), - .inst_caps = &platform_inst_cap_sm8550, - .inst_fw_caps_dec = inst_fw_cap_sm8550_dec, - .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8550_dec), - .inst_fw_caps_enc = inst_fw_cap_sm8550_enc, - .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8550_enc), - .tz_cp_config_data = tz_cp_config_sm8550, - .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), - .core_arch = VIDEO_ARCH_LX, - .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, - .ubwc_config = &ubwc_config_sm8550, - .num_vpp_pipe = 4, - .max_session_count = 16, - .max_core_mbpf = NUM_MBS_8K * 2, - .max_core_mbps = ((7680 * 4320) / 256) * 60, - .dec_input_config_params_default = - sm8550_vdec_input_config_params_default, - .dec_input_config_params_default_size = - ARRAY_SIZE(sm8550_vdec_input_config_params_default), - .dec_input_config_params_hevc = - sm8550_vdec_input_config_param_hevc, - .dec_input_config_params_hevc_size = - ARRAY_SIZE(sm8550_vdec_input_config_param_hevc), - .dec_input_config_params_vp9 = - sm8550_vdec_input_config_param_vp9, - .dec_input_config_params_vp9_size = - ARRAY_SIZE(sm8550_vdec_input_config_param_vp9), - .dec_input_config_params_av1 = - sm8550_vdec_input_config_param_av1, - .dec_input_config_params_av1_size = - ARRAY_SIZE(sm8550_vdec_input_config_param_av1), - .dec_output_config_params = - sm8550_vdec_output_config_params, - .dec_output_config_params_size = - ARRAY_SIZE(sm8550_vdec_output_config_params), - - .enc_input_config_params = - sm8550_venc_input_config_params, - .enc_input_config_params_size = - ARRAY_SIZE(sm8550_venc_input_config_params), - .enc_output_config_params = - sm8550_venc_output_config_params, - .enc_output_config_params_size = - ARRAY_SIZE(sm8550_venc_output_config_params), - - .dec_input_prop = sm8550_vdec_subscribe_input_properties, - .dec_input_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_input_properties), - .dec_output_prop_avc = sm8550_vdec_subscribe_output_properties_avc, - .dec_output_prop_avc_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_avc), - .dec_output_prop_hevc = sm8550_vdec_subscribe_output_properties_hevc, - .dec_output_prop_hevc_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_hevc), - .dec_output_prop_vp9 = sm8550_vdec_subscribe_output_properties_vp9, - .dec_output_prop_vp9_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_vp9), - .dec_output_prop_av1 = sm8550_vdec_subscribe_output_properties_av1, - .dec_output_prop_av1_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_av1), - - .dec_ip_int_buf_tbl = sm8550_dec_ip_int_buf_tbl, - .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_ip_int_buf_tbl), - .dec_op_int_buf_tbl = sm8550_dec_op_int_buf_tbl, - .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_op_int_buf_tbl), - - .enc_ip_int_buf_tbl = sm8550_enc_ip_int_buf_tbl, - .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_ip_int_buf_tbl), - .enc_op_int_buf_tbl = sm8550_enc_op_int_buf_tbl, - .enc_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_op_int_buf_tbl), -}; +const struct iris_firmware_data iris_hfi_gen2_data = { + .init_hfi_ops = iris_hfi_gen2_sys_ops_init, -/* - * Shares most of SM8550 data except: - * - vpu_ops to iris_vpu33_ops - * - clk_rst_tbl to sm8650_clk_reset_table - * - controller_rst_tbl to sm8650_controller_reset_table - * - fwname to "qcom/vpu/vpu33_p4.mbn" - */ -const struct iris_platform_data sm8650_data = { - .get_instance = iris_hfi_gen2_get_instance, - .init_hfi_command_ops = iris_hfi_gen2_command_ops_init, - .init_hfi_response_ops = iris_hfi_gen2_response_ops_init, - .get_vpu_buffer_size = iris_vpu33_buf_size, - .vpu_ops = &iris_vpu33_ops, - .set_preset_registers = iris_set_sm8550_preset_registers, - .icc_tbl = sm8550_icc_table, - .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table), - .clk_rst_tbl = sm8650_clk_reset_table, - .clk_rst_tbl_size = ARRAY_SIZE(sm8650_clk_reset_table), - .controller_rst_tbl = sm8650_controller_reset_table, - .controller_rst_tbl_size = ARRAY_SIZE(sm8650_controller_reset_table), - .bw_tbl_dec = sm8550_bw_table_dec, - .bw_tbl_dec_size = ARRAY_SIZE(sm8550_bw_table_dec), - .pmdomain_tbl = sm8550_pmdomain_table, - .pmdomain_tbl_size = ARRAY_SIZE(sm8550_pmdomain_table), - .opp_pd_tbl = sm8550_opp_pd_table, - .opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table), - .clk_tbl = sm8550_clk_table, - .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table), - .opp_clk_tbl = sm8550_opp_clk_table, - /* Upper bound of DMA address range */ - .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu/vpu33_p4.mbn", - .pas_id = IRIS_PAS_ID, - .inst_iris_fmts = platform_fmts_sm8550_dec, - .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec), - .inst_caps = &platform_inst_cap_sm8550, - .inst_fw_caps_dec = inst_fw_cap_sm8550_dec, - .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8550_dec), - .inst_fw_caps_enc = inst_fw_cap_sm8550_enc, - .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8550_enc), - .tz_cp_config_data = tz_cp_config_sm8550, - .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), .core_arch = VIDEO_ARCH_LX, - .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, - .ubwc_config = &ubwc_config_sm8550, - .num_vpp_pipe = 4, - .max_session_count = 16, - .max_core_mbpf = NUM_MBS_8K * 2, - .max_core_mbps = ((7680 * 4320) / 256) * 60, - .dec_input_config_params_default = - sm8550_vdec_input_config_params_default, - .dec_input_config_params_default_size = - ARRAY_SIZE(sm8550_vdec_input_config_params_default), - .dec_input_config_params_hevc = - sm8550_vdec_input_config_param_hevc, - .dec_input_config_params_hevc_size = - ARRAY_SIZE(sm8550_vdec_input_config_param_hevc), - .dec_input_config_params_vp9 = - sm8550_vdec_input_config_param_vp9, - .dec_input_config_params_vp9_size = - ARRAY_SIZE(sm8550_vdec_input_config_param_vp9), - .dec_input_config_params_av1 = - sm8550_vdec_input_config_param_av1, - .dec_input_config_params_av1_size = - ARRAY_SIZE(sm8550_vdec_input_config_param_av1), - .dec_output_config_params = - sm8550_vdec_output_config_params, - .dec_output_config_params_size = - ARRAY_SIZE(sm8550_vdec_output_config_params), - .enc_input_config_params = - sm8550_venc_input_config_params, - .enc_input_config_params_size = - ARRAY_SIZE(sm8550_venc_input_config_params), - .enc_output_config_params = - sm8550_venc_output_config_params, - .enc_output_config_params_size = - ARRAY_SIZE(sm8550_venc_output_config_params), - - .dec_input_prop = sm8550_vdec_subscribe_input_properties, - .dec_input_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_input_properties), - .dec_output_prop_avc = sm8550_vdec_subscribe_output_properties_avc, - .dec_output_prop_avc_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_avc), - .dec_output_prop_hevc = sm8550_vdec_subscribe_output_properties_hevc, - .dec_output_prop_hevc_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_hevc), - .dec_output_prop_vp9 = sm8550_vdec_subscribe_output_properties_vp9, - .dec_output_prop_vp9_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_vp9), - .dec_output_prop_av1 = sm8550_vdec_subscribe_output_properties_av1, - .dec_output_prop_av1_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_av1), - - .dec_ip_int_buf_tbl = sm8550_dec_ip_int_buf_tbl, - .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_ip_int_buf_tbl), - .dec_op_int_buf_tbl = sm8550_dec_op_int_buf_tbl, - .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_op_int_buf_tbl), - - .enc_ip_int_buf_tbl = sm8550_enc_ip_int_buf_tbl, - .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_ip_int_buf_tbl), - .enc_op_int_buf_tbl = sm8550_enc_op_int_buf_tbl, - .enc_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_op_int_buf_tbl), -}; - -const struct iris_platform_data sm8750_data = { - .get_instance = iris_hfi_gen2_get_instance, - .init_hfi_command_ops = iris_hfi_gen2_command_ops_init, - .init_hfi_response_ops = iris_hfi_gen2_response_ops_init, - .get_vpu_buffer_size = iris_vpu33_buf_size, - .vpu_ops = &iris_vpu35_ops, - .set_preset_registers = iris_set_sm8550_preset_registers, - .icc_tbl = sm8550_icc_table, - .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table), - .clk_rst_tbl = sm8750_clk_reset_table, - .clk_rst_tbl_size = ARRAY_SIZE(sm8750_clk_reset_table), - .bw_tbl_dec = sm8550_bw_table_dec, - .bw_tbl_dec_size = ARRAY_SIZE(sm8550_bw_table_dec), - .pmdomain_tbl = sm8550_pmdomain_table, - .pmdomain_tbl_size = ARRAY_SIZE(sm8550_pmdomain_table), - .opp_pd_tbl = sm8550_opp_pd_table, - .opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table), - .clk_tbl = sm8750_clk_table, - .clk_tbl_size = ARRAY_SIZE(sm8750_clk_table), - .opp_clk_tbl = sm8550_opp_clk_table, - /* Upper bound of DMA address range */ - .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu/vpu35_p4.mbn", - .pas_id = IRIS_PAS_ID, - .inst_iris_fmts = platform_fmts_sm8550_dec, - .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec), - .inst_caps = &platform_inst_cap_sm8550, .inst_fw_caps_dec = inst_fw_cap_sm8550_dec, .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8550_dec), .inst_fw_caps_enc = inst_fw_cap_sm8550_enc, .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8550_enc), - .tz_cp_config_data = tz_cp_config_sm8550, - .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), - .core_arch = VIDEO_ARCH_LX, - .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, - .ubwc_config = &ubwc_config_sm8550, - .num_vpp_pipe = 4, - .max_session_count = 16, - .max_core_mbpf = NUM_MBS_8K * 2, - .max_core_mbps = ((7680 * 4320) / 256) * 60, - .dec_input_config_params_default = - sm8550_vdec_input_config_params_default, - .dec_input_config_params_default_size = - ARRAY_SIZE(sm8550_vdec_input_config_params_default), - .dec_input_config_params_hevc = - sm8550_vdec_input_config_param_hevc, - .dec_input_config_params_hevc_size = - ARRAY_SIZE(sm8550_vdec_input_config_param_hevc), - .dec_input_config_params_vp9 = - sm8550_vdec_input_config_param_vp9, - .dec_input_config_params_vp9_size = - ARRAY_SIZE(sm8550_vdec_input_config_param_vp9), - .dec_input_config_params_av1 = - sm8550_vdec_input_config_param_av1, - .dec_input_config_params_av1_size = - ARRAY_SIZE(sm8550_vdec_input_config_param_av1), - .dec_output_config_params = - sm8550_vdec_output_config_params, - .dec_output_config_params_size = - ARRAY_SIZE(sm8550_vdec_output_config_params), - .enc_input_config_params = - sm8550_venc_input_config_params, - .enc_input_config_params_size = - ARRAY_SIZE(sm8550_venc_input_config_params), - .enc_output_config_params = - sm8550_venc_output_config_params, - .enc_output_config_params_size = - ARRAY_SIZE(sm8550_venc_output_config_params), - - .dec_input_prop = sm8550_vdec_subscribe_input_properties, - .dec_input_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_input_properties), - .dec_output_prop_avc = sm8550_vdec_subscribe_output_properties_avc, - .dec_output_prop_avc_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_avc), - .dec_output_prop_hevc = sm8550_vdec_subscribe_output_properties_hevc, - .dec_output_prop_hevc_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_hevc), - .dec_output_prop_vp9 = sm8550_vdec_subscribe_output_properties_vp9, - .dec_output_prop_vp9_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_vp9), - .dec_output_prop_av1 = sm8550_vdec_subscribe_output_properties_av1, - .dec_output_prop_av1_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_av1), - - .dec_ip_int_buf_tbl = sm8550_dec_ip_int_buf_tbl, - .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_ip_int_buf_tbl), - .dec_op_int_buf_tbl = sm8550_dec_op_int_buf_tbl, - .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_op_int_buf_tbl), - - .enc_ip_int_buf_tbl = sm8550_enc_ip_int_buf_tbl, - .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_ip_int_buf_tbl), - .enc_op_int_buf_tbl = sm8550_enc_op_int_buf_tbl, - .enc_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_op_int_buf_tbl), -}; - -/* - * Shares most of SM8550 data except: - * - inst_caps to platform_inst_cap_qcs8300 - */ -const struct iris_platform_data qcs8300_data = { - .get_instance = iris_hfi_gen2_get_instance, - .init_hfi_command_ops = iris_hfi_gen2_command_ops_init, - .init_hfi_response_ops = iris_hfi_gen2_response_ops_init, - .get_vpu_buffer_size = iris_vpu_buf_size, - .vpu_ops = &iris_vpu3_ops, - .set_preset_registers = iris_set_sm8550_preset_registers, - .icc_tbl = sm8550_icc_table, - .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table), - .clk_rst_tbl = sm8550_clk_reset_table, - .clk_rst_tbl_size = ARRAY_SIZE(sm8550_clk_reset_table), - .bw_tbl_dec = sm8550_bw_table_dec, - .bw_tbl_dec_size = ARRAY_SIZE(sm8550_bw_table_dec), - .pmdomain_tbl = sm8550_pmdomain_table, - .pmdomain_tbl_size = ARRAY_SIZE(sm8550_pmdomain_table), - .opp_pd_tbl = sm8550_opp_pd_table, - .opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table), - .clk_tbl = sm8550_clk_table, - .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table), - .opp_clk_tbl = sm8550_opp_clk_table, - /* Upper bound of DMA address range */ - .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu/vpu30_p4_s6.mbn", - .pas_id = IRIS_PAS_ID, - .inst_iris_fmts = platform_fmts_sm8550_dec, - .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec), - .inst_caps = &platform_inst_cap_qcs8300, - .inst_fw_caps_dec = inst_fw_cap_sm8550_dec, - .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8550_dec), - .inst_fw_caps_enc = inst_fw_cap_sm8550_enc, - .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8550_enc), - .tz_cp_config_data = tz_cp_config_sm8550, - .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), - .core_arch = VIDEO_ARCH_LX, - .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, - .ubwc_config = &ubwc_config_sm8550, - .num_vpp_pipe = 2, - .max_session_count = 16, - .max_core_mbpf = ((4096 * 2176) / 256) * 4, - .max_core_mbps = (((3840 * 2176) / 256) * 120), .dec_input_config_params_default = sm8550_vdec_input_config_params_default, .dec_input_config_params_default_size = diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2.h b/drivers/media/platform/qcom/iris/iris_hfi_gen2.h index b9d3749a10ef..21ab58e0aa84 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2.h @@ -34,8 +34,7 @@ struct iris_inst_hfi_gen2 { struct hfi_subscription_params dst_subcr_params; }; -void iris_hfi_gen2_command_ops_init(struct iris_core *core); -void iris_hfi_gen2_response_ops_init(struct iris_core *core); -struct iris_inst *iris_hfi_gen2_get_instance(void); +void iris_hfi_gen2_sys_ops_init(struct iris_core *core); +void iris_hfi_gen2_response_handler(struct iris_core *core); #endif diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c index 30bfd90d423b..ca2954f8bd3a 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c @@ -10,7 +10,6 @@ #define UNSPECIFIED_COLOR_FORMAT 5 #define NUM_SYS_INIT_PACKETS 8 -#define NUM_COMV_AV1 18 #define SYS_INIT_PKT_SIZE (sizeof(struct iris_hfi_header) + \ NUM_SYS_INIT_PACKETS * (sizeof(struct iris_hfi_packet) + sizeof(u32))) @@ -481,8 +480,20 @@ static int iris_hfi_gen2_set_colorformat(struct iris_inst *inst, u32 plane) if (inst->domain == DECODER) { pixelformat = inst->fmt_dst->fmt.pix_mp.pixelformat; - hfi_colorformat = pixelformat == V4L2_PIX_FMT_NV12 ? - HFI_COLOR_FMT_NV12 : HFI_COLOR_FMT_NV12_UBWC; + switch (pixelformat) { + case V4L2_PIX_FMT_NV12: + hfi_colorformat = HFI_COLOR_FMT_NV12; + break; + case V4L2_PIX_FMT_QC08C: + hfi_colorformat = HFI_COLOR_FMT_NV12_UBWC; + break; + case V4L2_PIX_FMT_P010: + hfi_colorformat = HFI_COLOR_FMT_P010; + break; + case V4L2_PIX_FMT_QC10C: + hfi_colorformat = HFI_COLOR_FMT_TP10_UBWC; + break; + } } else { pixelformat = inst->fmt_src->fmt.pix_mp.pixelformat; hfi_colorformat = pixelformat == V4L2_PIX_FMT_NV12 ? @@ -517,7 +528,8 @@ static int iris_hfi_gen2_set_linear_stride_scanline(struct iris_inst *inst, u32 stride_uv = stride_y; scanline_uv = scanline_y / 2; - if (pixelformat != V4L2_PIX_FMT_NV12) + if (pixelformat != V4L2_PIX_FMT_NV12 && + pixelformat != V4L2_PIX_FMT_P010) return 0; payload[0] = stride_y << 16 | scanline_y; @@ -532,6 +544,61 @@ static int iris_hfi_gen2_set_linear_stride_scanline(struct iris_inst *inst, u32 sizeof(u64)); } +static int iris_hfi_gen2_set_ubwc_stride_scanline(struct iris_inst *inst, u32 plane) +{ + u32 meta_stride_y, meta_scanline_y, meta_stride_uv, meta_scanline_uv; + u32 stride_y, scanline_y, stride_uv, scanline_uv; + u32 port = iris_hfi_gen2_get_port(inst, plane); + u32 pixelformat, width, height; + u32 payload[4]; + + if (inst->domain != DECODER || + inst->fmt_src->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_AV1) + return 0; + + pixelformat = inst->fmt_dst->fmt.pix_mp.pixelformat; + width = inst->fmt_dst->fmt.pix_mp.width; + height = inst->fmt_dst->fmt.pix_mp.height; + + switch (pixelformat) { + case V4L2_PIX_FMT_QC08C: + stride_y = ALIGN(width, 128); + scanline_y = ALIGN(height, 32); + stride_uv = ALIGN(width, 128); + scanline_uv = ALIGN((height + 1) >> 1, 32); + meta_stride_y = ALIGN(DIV_ROUND_UP(width, 32), 64); + meta_scanline_y = ALIGN(DIV_ROUND_UP(height, 8), 16); + meta_stride_uv = ALIGN(DIV_ROUND_UP((width + 1) >> 1, 16), 64); + meta_scanline_uv = ALIGN(DIV_ROUND_UP((height + 1) >> 1, 8), 16); + break; + case V4L2_PIX_FMT_QC10C: + stride_y = ALIGN(width * 4 / 3, 256); + scanline_y = ALIGN(height, 16); + stride_uv = ALIGN(width * 4 / 3, 256); + scanline_uv = ALIGN((height + 1) >> 1, 16); + meta_stride_y = ALIGN(DIV_ROUND_UP(width, 48), 64); + meta_scanline_y = ALIGN(DIV_ROUND_UP(height, 4), 16); + meta_stride_uv = ALIGN(DIV_ROUND_UP((width + 1) >> 1, 24), 64); + meta_scanline_uv = ALIGN(DIV_ROUND_UP((height + 1) >> 1, 4), 16); + break; + default: + return 0; + } + + payload[0] = stride_y << 16 | scanline_y; + payload[1] = stride_uv << 16 | scanline_uv; + payload[2] = meta_stride_y << 16 | meta_scanline_y; + payload[3] = meta_stride_uv << 16 | meta_scanline_uv; + + return iris_hfi_gen2_session_set_property(inst, + HFI_PROP_UBWC_STRIDE_SCANLINE, + HFI_HOST_FLAGS_NONE, + port, + HFI_PAYLOAD_U32_ARRAY, + &payload[0], + sizeof(u32) * 4); +} + static int iris_hfi_gen2_set_tier(struct iris_inst *inst, u32 plane) { u32 port = iris_hfi_gen2_get_port(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); @@ -601,7 +668,7 @@ static int iris_hfi_gen2_set_super_block(struct iris_inst *inst, u32 plane) static int iris_hfi_gen2_session_set_config_params(struct iris_inst *inst, u32 plane) { - const struct iris_platform_data *pdata = inst->core->iris_platform_data; + const struct iris_firmware_data *fdata = inst->core->iris_firmware_data; u32 config_params_size = 0, i, j; const u32 *config_params = NULL; int ret; @@ -620,6 +687,7 @@ static int iris_hfi_gen2_session_set_config_params(struct iris_inst *inst, u32 p {HFI_PROP_OPB_ENABLE, iris_hfi_gen2_set_opb_enable }, {HFI_PROP_COLOR_FORMAT, iris_hfi_gen2_set_colorformat }, {HFI_PROP_LINEAR_STRIDE_SCANLINE, iris_hfi_gen2_set_linear_stride_scanline }, + {HFI_PROP_UBWC_STRIDE_SCANLINE, iris_hfi_gen2_set_ubwc_stride_scanline }, {HFI_PROP_TIER, iris_hfi_gen2_set_tier }, {HFI_PROP_FRAME_RATE, iris_hfi_gen2_set_frame_rate }, {HFI_PROP_AV1_FILM_GRAIN_PRESENT, iris_hfi_gen2_set_film_grain }, @@ -630,31 +698,31 @@ static int iris_hfi_gen2_session_set_config_params(struct iris_inst *inst, u32 p if (inst->domain == DECODER) { if (V4L2_TYPE_IS_OUTPUT(plane)) { if (inst->codec == V4L2_PIX_FMT_H264) { - config_params = pdata->dec_input_config_params_default; - config_params_size = pdata->dec_input_config_params_default_size; + config_params = fdata->dec_input_config_params_default; + config_params_size = fdata->dec_input_config_params_default_size; } else if (inst->codec == V4L2_PIX_FMT_HEVC) { - config_params = pdata->dec_input_config_params_hevc; - config_params_size = pdata->dec_input_config_params_hevc_size; + config_params = fdata->dec_input_config_params_hevc; + config_params_size = fdata->dec_input_config_params_hevc_size; } else if (inst->codec == V4L2_PIX_FMT_VP9) { - config_params = pdata->dec_input_config_params_vp9; - config_params_size = pdata->dec_input_config_params_vp9_size; + config_params = fdata->dec_input_config_params_vp9; + config_params_size = fdata->dec_input_config_params_vp9_size; } else if (inst->codec == V4L2_PIX_FMT_AV1) { - config_params = pdata->dec_input_config_params_av1; - config_params_size = pdata->dec_input_config_params_av1_size; + config_params = fdata->dec_input_config_params_av1; + config_params_size = fdata->dec_input_config_params_av1_size; } else { return -EINVAL; } } else { - config_params = pdata->dec_output_config_params; - config_params_size = pdata->dec_output_config_params_size; + config_params = fdata->dec_output_config_params; + config_params_size = fdata->dec_output_config_params_size; } } else { if (V4L2_TYPE_IS_OUTPUT(plane)) { - config_params = pdata->enc_input_config_params; - config_params_size = pdata->enc_input_config_params_size; + config_params = fdata->enc_input_config_params; + config_params_size = fdata->enc_input_config_params_size; } else { - config_params = pdata->enc_output_config_params; - config_params_size = pdata->enc_output_config_params_size; + config_params = fdata->enc_output_config_params; + config_params_size = fdata->enc_output_config_params_size; } } @@ -849,24 +917,24 @@ static int iris_hfi_gen2_subscribe_change_param(struct iris_inst *inst, u32 plan switch (inst->codec) { case V4L2_PIX_FMT_H264: - change_param = core->iris_platform_data->dec_input_config_params_default; + change_param = core->iris_firmware_data->dec_input_config_params_default; change_param_size = - core->iris_platform_data->dec_input_config_params_default_size; + core->iris_firmware_data->dec_input_config_params_default_size; break; case V4L2_PIX_FMT_HEVC: - change_param = core->iris_platform_data->dec_input_config_params_hevc; + change_param = core->iris_firmware_data->dec_input_config_params_hevc; change_param_size = - core->iris_platform_data->dec_input_config_params_hevc_size; + core->iris_firmware_data->dec_input_config_params_hevc_size; break; case V4L2_PIX_FMT_VP9: - change_param = core->iris_platform_data->dec_input_config_params_vp9; + change_param = core->iris_firmware_data->dec_input_config_params_vp9; change_param_size = - core->iris_platform_data->dec_input_config_params_vp9_size; + core->iris_firmware_data->dec_input_config_params_vp9_size; break; case V4L2_PIX_FMT_AV1: - change_param = core->iris_platform_data->dec_input_config_params_av1; + change_param = core->iris_firmware_data->dec_input_config_params_av1; change_param_size = - core->iris_platform_data->dec_input_config_params_av1_size; + core->iris_firmware_data->dec_input_config_params_av1_size; break; } @@ -996,29 +1064,29 @@ static int iris_hfi_gen2_subscribe_property(struct iris_inst *inst, u32 plane) return 0; if (V4L2_TYPE_IS_OUTPUT(plane)) { - subscribe_prop_size = core->iris_platform_data->dec_input_prop_size; - subcribe_prop = core->iris_platform_data->dec_input_prop; + subscribe_prop_size = core->iris_firmware_data->dec_input_prop_size; + subcribe_prop = core->iris_firmware_data->dec_input_prop; } else { switch (inst->codec) { case V4L2_PIX_FMT_H264: - subcribe_prop = core->iris_platform_data->dec_output_prop_avc; + subcribe_prop = core->iris_firmware_data->dec_output_prop_avc; subscribe_prop_size = - core->iris_platform_data->dec_output_prop_avc_size; + core->iris_firmware_data->dec_output_prop_avc_size; break; case V4L2_PIX_FMT_HEVC: - subcribe_prop = core->iris_platform_data->dec_output_prop_hevc; + subcribe_prop = core->iris_firmware_data->dec_output_prop_hevc; subscribe_prop_size = - core->iris_platform_data->dec_output_prop_hevc_size; + core->iris_firmware_data->dec_output_prop_hevc_size; break; case V4L2_PIX_FMT_VP9: - subcribe_prop = core->iris_platform_data->dec_output_prop_vp9; + subcribe_prop = core->iris_firmware_data->dec_output_prop_vp9; subscribe_prop_size = - core->iris_platform_data->dec_output_prop_vp9_size; + core->iris_firmware_data->dec_output_prop_vp9_size; break; case V4L2_PIX_FMT_AV1: - subcribe_prop = core->iris_platform_data->dec_output_prop_av1; + subcribe_prop = core->iris_firmware_data->dec_output_prop_av1; subscribe_prop_size = - core->iris_platform_data->dec_output_prop_av1_size; + core->iris_firmware_data->dec_output_prop_av1_size; break; } } @@ -1205,27 +1273,19 @@ static u32 iris_hfi_gen2_buf_type_from_driver(u32 domain, enum iris_buffer_type } } -static int iris_set_num_comv(struct iris_inst *inst) +static int iris_hfi_gen2_set_num_comv(struct iris_inst *inst) { - struct platform_inst_caps *caps; - struct iris_core *core = inst->core; - u32 num_comv; - - caps = core->iris_platform_data->inst_caps; - - /* - * AV1 needs more comv buffers than other codecs. - * Update accordingly. - */ - num_comv = (inst->codec == V4L2_PIX_FMT_AV1) ? - NUM_COMV_AV1 : caps->num_comv; - - return core->hfi_ops->session_set_property(inst, - HFI_PROP_COMV_BUFFER_COUNT, - HFI_HOST_FLAGS_NONE, - HFI_PORT_BITSTREAM, - HFI_PAYLOAD_U32, - &num_comv, sizeof(u32)); + u32 num_comv = inst->buffers[BUF_OUTPUT].min_count; + + if (inst->fw_min_count) + num_comv = inst->fw_min_count; + + return iris_hfi_gen2_session_set_property(inst, + HFI_PROP_COMV_BUFFER_COUNT, + HFI_HOST_FLAGS_NONE, + HFI_PORT_BITSTREAM, + HFI_PAYLOAD_U32, + &num_comv, sizeof(u32)); } static void iris_hfi_gen2_get_buffer(u32 domain, struct iris_buffer *buffer, @@ -1257,7 +1317,7 @@ static int iris_hfi_gen2_session_queue_buffer(struct iris_inst *inst, struct iri iris_hfi_gen2_get_buffer(inst->domain, buffer, &hfi_buffer); if (buffer->type == BUF_COMV) { - ret = iris_set_num_comv(inst); + ret = iris_hfi_gen2_set_num_comv(inst); if (ret) return ret; } @@ -1300,11 +1360,7 @@ static int iris_hfi_gen2_session_release_buffer(struct iris_inst *inst, struct i inst_hfi_gen2->packet->size); } -static const struct iris_hfi_command_ops iris_hfi_gen2_command_ops = { - .sys_init = iris_hfi_gen2_sys_init, - .sys_image_version = iris_hfi_gen2_sys_image_version, - .sys_interframe_powercollapse = iris_hfi_gen2_sys_interframe_powercollapse, - .sys_pc_prep = iris_hfi_gen2_sys_pc_prep, +static const struct iris_hfi_session_ops iris_hfi_gen2_session_ops = { .session_open = iris_hfi_gen2_session_open, .session_set_config_params = iris_hfi_gen2_session_set_config_params, .session_set_property = iris_hfi_gen2_session_set_property, @@ -1319,17 +1375,32 @@ static const struct iris_hfi_command_ops iris_hfi_gen2_command_ops = { .session_close = iris_hfi_gen2_session_close, }; -void iris_hfi_gen2_command_ops_init(struct iris_core *core) -{ - core->hfi_ops = &iris_hfi_gen2_command_ops; -} - -struct iris_inst *iris_hfi_gen2_get_instance(void) +static struct iris_inst *iris_hfi_gen2_get_instance(void) { struct iris_inst_hfi_gen2 *out; /* The allocation is intentionally larger than struct iris_inst. */ out = kzalloc_obj(*out); + if (!out) + return NULL; + + out->inst.hfi_session_ops = &iris_hfi_gen2_session_ops; return &out->inst; } + +static const struct iris_hfi_sys_ops iris_hfi_gen2_sys_ops = { + .sys_init = iris_hfi_gen2_sys_init, + .sys_image_version = iris_hfi_gen2_sys_image_version, + .sys_interframe_powercollapse = iris_hfi_gen2_sys_interframe_powercollapse, + .sys_pc_prep = iris_hfi_gen2_sys_pc_prep, + + .sys_hfi_response_handler = iris_hfi_gen2_response_handler, + + .sys_get_instance = iris_hfi_gen2_get_instance, +}; + +void iris_hfi_gen2_sys_ops_init(struct iris_core *core) +{ + core->hfi_sys_ops = &iris_hfi_gen2_sys_ops; +} diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h index cecf771c55dd..776b21cd11b2 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h @@ -71,7 +71,25 @@ enum hfi_rate_control { #define HFI_PROP_MIN_QP_PACKED 0x0300012f #define HFI_PROP_MAX_QP_PACKED 0x03000130 #define HFI_PROP_IR_RANDOM_PERIOD 0x03000131 +#define HFI_PROP_LTR_COUNT 0x03000134 +#define HFI_PROP_LTR_MARK 0x03000135 +#define HFI_PROP_LTR_USE 0x03000136 + +enum hfi_layer_encoding_type { + HFI_HIER_P_SLIDING_WINDOW = 0x1, + HFI_HIER_P_HYBRID_LTR = 0x2, + HFI_HIER_B = 0x3, +}; + +#define HFI_PROP_LAYER_ENCODING_TYPE 0x03000138 +#define HFI_PROP_LAYER_COUNT 0x03000139 #define HFI_PROP_TOTAL_BITRATE 0x0300013b +#define HFI_PROP_BITRATE_LAYER1 0x0300013c +#define HFI_PROP_BITRATE_LAYER2 0x0300013d +#define HFI_PROP_BITRATE_LAYER3 0x0300013e +#define HFI_PROP_BITRATE_LAYER4 0x0300013f +#define HFI_PROP_BITRATE_LAYER5 0x03000140 +#define HFI_PROP_BITRATE_LAYER6 0x03000141 #define HFI_PROP_MAX_GOP_FRAMES 0x03000146 #define HFI_PROP_MAX_B_FRAMES 0x03000147 #define HFI_PROP_QUALITY_MODE 0x03000148 @@ -118,6 +136,7 @@ enum hfi_flip { #define HFI_PROP_OPB_ENABLE 0x03000184 #define HFI_PROP_AV1_TILE_ROWS_COLUMNS 0x03000187 #define HFI_PROP_AV1_DRAP_CONFIG 0x03000189 +#define HFI_PROP_UBWC_STRIDE_SCANLINE 0x03000190 #define HFI_PROP_COMV_BUFFER_COUNT 0x03000193 #define HFI_PROP_AV1_UNIFORM_TILE_SPACING 0x03000197 #define HFI_PROP_END 0x03FFFFFF diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c index d77fa29f44fc..0d05dd2afc07 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c @@ -3,6 +3,9 @@ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ +#include <linux/printk.h> +#include <linux/soc/qcom/ubwc.h> + #include "iris_hfi_common.h" #include "iris_hfi_gen2.h" #include "iris_hfi_gen2_packet.h" @@ -120,6 +123,7 @@ static void iris_hfi_gen2_create_packet(struct iris_hfi_header *hdr, u32 pkt_typ void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_header *hdr) { + const struct qcom_ubwc_cfg_data *ubwc = core->ubwc_cfg; u32 payload = 0; iris_hfi_gen2_create_header(hdr, 0, core->header_id++); @@ -136,7 +140,7 @@ void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_heade &payload, sizeof(u32)); - payload = core->iris_platform_data->ubwc_config->max_channels; + payload = qcom_ubwc_macrotile_mode(ubwc) ? 8 : 4; iris_hfi_gen2_create_packet(hdr, HFI_PROP_UBWC_MAX_CHANNELS, HFI_HOST_FLAGS_NONE, @@ -146,7 +150,7 @@ void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_heade &payload, sizeof(u32)); - payload = core->iris_platform_data->ubwc_config->mal_length; + payload = qcom_ubwc_min_acc_length_64b(ubwc) ? 64 : 32; iris_hfi_gen2_create_packet(hdr, HFI_PROP_UBWC_MAL_LENGTH, HFI_HOST_FLAGS_NONE, @@ -156,7 +160,7 @@ void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_heade &payload, sizeof(u32)); - payload = core->iris_platform_data->ubwc_config->highest_bank_bit; + payload = ubwc->highest_bank_bit; iris_hfi_gen2_create_packet(hdr, HFI_PROP_UBWC_HBB, HFI_HOST_FLAGS_NONE, @@ -166,7 +170,7 @@ void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_heade &payload, sizeof(u32)); - payload = core->iris_platform_data->ubwc_config->bank_swzl_level; + payload = !!(qcom_ubwc_swizzle(ubwc) & UBWC_SWIZZLE_ENABLE_LVL1); iris_hfi_gen2_create_packet(hdr, HFI_PROP_UBWC_BANK_SWZL_LEVEL1, HFI_HOST_FLAGS_NONE, @@ -176,7 +180,7 @@ void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_heade &payload, sizeof(u32)); - payload = core->iris_platform_data->ubwc_config->bank_swz2_level; + payload = !!(qcom_ubwc_swizzle(ubwc) & UBWC_SWIZZLE_ENABLE_LVL2); iris_hfi_gen2_create_packet(hdr, HFI_PROP_UBWC_BANK_SWZL_LEVEL2, HFI_HOST_FLAGS_NONE, @@ -186,7 +190,7 @@ void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_heade &payload, sizeof(u32)); - payload = core->iris_platform_data->ubwc_config->bank_swz3_level; + payload = !!(qcom_ubwc_swizzle(ubwc) & UBWC_SWIZZLE_ENABLE_LVL3); iris_hfi_gen2_create_packet(hdr, HFI_PROP_UBWC_BANK_SWZL_LEVEL3, HFI_HOST_FLAGS_NONE, @@ -196,7 +200,7 @@ void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_heade &payload, sizeof(u32)); - payload = core->iris_platform_data->ubwc_config->bank_spreading; + payload = qcom_ubwc_bank_spread(ubwc); iris_hfi_gen2_create_packet(hdr, HFI_PROP_UBWC_BANK_SPREADING, HFI_HOST_FLAGS_NONE, diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c index 8e19f61bbbf9..25162ae71357 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c @@ -542,9 +542,33 @@ static void iris_hfi_gen2_read_input_subcr_params(struct iris_inst *inst) pixmp_ip->width = width; pixmp_ip->height = height; - pixmp_op->width = ALIGN(width, 128); - pixmp_op->height = ALIGN(height, 32); - pixmp_op->plane_fmt[0].bytesperline = ALIGN(width, 128); + if (subsc_params.bit_depth == BIT_DEPTH_8 && + pixmp_op->pixelformat != V4L2_PIX_FMT_NV12 && + pixmp_op->pixelformat != V4L2_PIX_FMT_QC08C) + pixmp_op->pixelformat = V4L2_PIX_FMT_NV12; + else if (subsc_params.bit_depth == BIT_DEPTH_10 && + pixmp_op->pixelformat != V4L2_PIX_FMT_P010 && + pixmp_op->pixelformat != V4L2_PIX_FMT_QC10C) + pixmp_op->pixelformat = V4L2_PIX_FMT_P010; + + switch (pixmp_op->pixelformat) { + case V4L2_PIX_FMT_P010: + pixmp_op->width = ALIGN(width, 128); + pixmp_op->height = ALIGN(height, 32); + pixmp_op->plane_fmt[0].bytesperline = ALIGN(width * 2, 256); + break; + case V4L2_PIX_FMT_QC10C: + pixmp_op->width = roundup(width, 192); + pixmp_op->height = ALIGN(height, 16); + pixmp_op->plane_fmt[0].bytesperline = ALIGN(pixmp_op->width * 4 / 3, 256); + break; + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_QC08C: + pixmp_op->width = ALIGN(width, 128); + pixmp_op->height = ALIGN(height, 32); + pixmp_op->plane_fmt[0].bytesperline = pixmp_op->width; + break; + } pixmp_op->plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_OUTPUT); matrix_coeff = subsc_params.color_info & 0xFF; @@ -610,7 +634,12 @@ static void iris_hfi_gen2_read_input_subcr_params(struct iris_inst *inst) inst->fw_caps[POC].value = subsc_params.pic_order_cnt; inst->fw_caps[TIER].value = subsc_params.tier; - if (subsc_params.bit_depth != BIT_DEPTH_8 || + if (subsc_params.bit_depth == BIT_DEPTH_8) + inst->fw_caps[BIT_DEPTH].value = BIT_DEPTH_8; + else + inst->fw_caps[BIT_DEPTH].value = BIT_DEPTH_10; + + if ((subsc_params.bit_depth != BIT_DEPTH_8 && subsc_params.bit_depth != BIT_DEPTH_10) || !(subsc_params.coded_frames & HFI_BITMASK_FRAME_MBS_ONLY_FLAG)) { dev_err(core->dev, "unsupported content, bit depth: %x, pic_struct = %x\n", subsc_params.bit_depth, subsc_params.coded_frames); @@ -977,7 +1006,7 @@ static void iris_hfi_gen2_flush_debug_queue(struct iris_core *core, u8 *packet) } } -static void iris_hfi_gen2_response_handler(struct iris_core *core) +void iris_hfi_gen2_response_handler(struct iris_core *core) { if (iris_vpu_watchdog(core, core->intr_status)) { struct iris_hfi_packet pkt = {.type = HFI_SYS_ERROR_WD_TIMEOUT}; @@ -997,12 +1026,3 @@ static void iris_hfi_gen2_response_handler(struct iris_core *core) iris_hfi_gen2_flush_debug_queue(core, core->response_packet); } - -static const struct iris_hfi_response_ops iris_hfi_gen2_response_ops = { - .hfi_response_handler = iris_hfi_gen2_response_handler, -}; - -void iris_hfi_gen2_response_ops_init(struct iris_core *core) -{ - core->hfi_response_ops = &iris_hfi_gen2_response_ops; -} diff --git a/drivers/media/platform/qcom/iris/iris_instance.h b/drivers/media/platform/qcom/iris/iris_instance.h index 16965150f427..a770331d1675 100644 --- a/drivers/media/platform/qcom/iris/iris_instance.h +++ b/drivers/media/platform/qcom/iris/iris_instance.h @@ -15,6 +15,8 @@ #define DEFAULT_WIDTH 320 #define DEFAULT_HEIGHT 240 +struct iris_hfi_session_ops; + enum iris_fmt_type_out { IRIS_FMT_H264, IRIS_FMT_HEVC, @@ -25,11 +27,8 @@ enum iris_fmt_type_out { enum iris_fmt_type_cap { IRIS_FMT_NV12, IRIS_FMT_QC08C, -}; - -struct iris_fmt { - u32 pixfmt; - u32 type; + IRIS_FMT_TP10, + IRIS_FMT_QC10C, }; /** @@ -38,6 +37,7 @@ struct iris_fmt { * @list: used for attach an instance to the core * @core: pointer to core structure * @session_id: id of current video session + * @hfi_session_ops: iris HFI session ops * @ctx_q_lock: lock to serialize queues related ioctls * @lock: lock to seralise forward and reverse threads * @fh: reference of v4l2 file handler @@ -67,6 +67,8 @@ struct iris_fmt { * @metadata_idx: index for metadata buffer * @codec: codec type * @last_buffer_dequeued: a flag to indicate that last buffer is sent by driver + * @last_buf_ns: start time of received input buffer for current one second FPS window + * @frame_counter: input buffer counter for current one second FPS window * @frame_rate: frame rate of current instance * @operating_rate: operating rate of current instance * @hfi_rc_type: rate control type @@ -74,12 +76,15 @@ struct iris_fmt { * @enc_raw_height: source image height for encoder instance * @enc_scale_width: scale width for encoder instance * @enc_scale_height: scale height for encoder instance + * @hfi_layer_type: hierarchical coding layer type + * @hfi_layer_count: hierarchical coding layer count */ struct iris_inst { struct list_head list; struct iris_core *core; u32 session_id; + const struct iris_hfi_session_ops *hfi_session_ops; struct mutex ctx_q_lock;/* lock to serialize queues related ioctls */ struct mutex lock; /* lock to serialize forward and reverse threads */ struct v4l2_fh fh; @@ -109,6 +114,8 @@ struct iris_inst { u32 metadata_idx; u32 codec; bool last_buffer_dequeued; + u64 last_buf_ns; + u32 frame_counter; u32 frame_rate; u32 operating_rate; u32 hfi_rc_type; @@ -116,6 +123,8 @@ struct iris_inst { u32 enc_raw_height; u32 enc_scale_width; u32 enc_scale_height; + u32 hfi_layer_type; + u32 hfi_layer_count; }; #endif diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index 5a489917580e..c9256f2323dc 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -12,12 +12,12 @@ struct iris_core; struct iris_inst; -#define IRIS_PAS_ID 9 #define HW_RESPONSE_TIMEOUT_VALUE (1000) /* milliseconds */ #define AUTOSUSPEND_DELAY_VALUE (HW_RESPONSE_TIMEOUT_VALUE + 500) /* milliseconds */ #define REGISTER_BIT_DEPTH(luma, chroma) ((luma) << 16 | (chroma)) #define BIT_DEPTH_8 REGISTER_BIT_DEPTH(8, 8) +#define BIT_DEPTH_10 REGISTER_BIT_DEPTH(10, 10) #define CODED_FRAMES_PROGRESSIVE 0x0 #define DEFAULT_MAX_HOST_BUF_COUNT 64 #define DEFAULT_MAX_HOST_BURST_BUF_COUNT 256 @@ -29,6 +29,15 @@ struct iris_inst; #define MAX_QP_HEVC 63 #define DEFAULT_QP 20 #define BITRATE_DEFAULT 20000000 +#define INVALID_DEFAULT_MARK_OR_USE_LTR -1 +#define MAX_LTR_FRAME_COUNT_GEN1 4 +#define MAX_LTR_FRAME_COUNT_GEN2 2 +#define MAX_LAYER_HB 3 +#define MAX_AVC_LAYER_HP_HYBRID_LTR 5 +#define MAX_AVC_LAYER_HP_SLIDING_WINDOW 3 +#define MAX_HEVC_LAYER_HP_SLIDING_WINDOW 3 +#define MAX_HEVC_VBR_LAYER_HP_SLIDING_WINDOW 5 +#define MAX_HIER_CODING_LAYER_GEN1 6 enum stage_type { STAGE_1 = 1, @@ -41,12 +50,16 @@ enum pipe_type { PIPE_4 = 4, }; +extern const struct iris_firmware_data iris_hfi_gen1_data; +extern const struct iris_firmware_data iris_hfi_gen2_data; + extern const struct iris_platform_data qcs8300_data; extern const struct iris_platform_data sc7280_data; extern const struct iris_platform_data sm8250_data; extern const struct iris_platform_data sm8550_data; extern const struct iris_platform_data sm8650_data; extern const struct iris_platform_data sm8750_data; +extern const struct iris_platform_data x1p42100_data; enum platform_clk_type { IRIS_AXI_CLK, /* AXI0 in case of platforms with multiple AXI clocks */ @@ -75,16 +88,6 @@ struct tz_cp_config { u32 cp_nonpixel_size; }; -struct ubwc_config_data { - u32 max_channels; - u32 mal_length; - u32 highest_bank_bit; - u32 bank_swzl_level; - u32 bank_swz2_level; - u32 bank_swz3_level; - u32 bank_spreading; -}; - struct platform_inst_caps { u32 min_frame_width; u32 max_frame_width; @@ -95,7 +98,6 @@ struct platform_inst_caps { u32 mb_cycles_vpp; u32 mb_cycles_fw; u32 mb_cycles_fw_vpp; - u32 num_comv; u32 max_frame_rate; u32 max_operating_rate; }; @@ -159,6 +161,28 @@ enum platform_inst_fw_cap_type { VFLIP, IR_TYPE, IR_PERIOD, + LTR_COUNT, + USE_LTR, + MARK_LTR, + B_FRAME, + INTRA_PERIOD, + LAYER_ENABLE, + LAYER_TYPE_H264, + LAYER_TYPE_HEVC, + LAYER_COUNT_H264, + LAYER_COUNT_HEVC, + LAYER0_BITRATE_H264, + LAYER1_BITRATE_H264, + LAYER2_BITRATE_H264, + LAYER3_BITRATE_H264, + LAYER4_BITRATE_H264, + LAYER5_BITRATE_H264, + LAYER0_BITRATE_HEVC, + LAYER1_BITRATE_HEVC, + LAYER2_BITRATE_HEVC, + LAYER3_BITRATE_HEVC, + LAYER4_BITRATE_HEVC, + LAYER5_BITRATE_HEVC, INST_FW_CAP_MAX, }; @@ -212,50 +236,16 @@ enum platform_pm_domain_type { IRIS_APV_HW_POWER_DOMAIN, }; -struct iris_platform_data { - void (*init_hfi_command_ops)(struct iris_core *core); - void (*init_hfi_response_ops)(struct iris_core *core); - struct iris_inst *(*get_instance)(void); - u32 (*get_vpu_buffer_size)(struct iris_inst *inst, enum iris_buffer_type buffer_type); - const struct vpu_ops *vpu_ops; - void (*set_preset_registers)(struct iris_core *core); - const struct icc_info *icc_tbl; - unsigned int icc_tbl_size; - const struct bw_info *bw_tbl_dec; - unsigned int bw_tbl_dec_size; - const char * const *pmdomain_tbl; - unsigned int pmdomain_tbl_size; - const char * const *opp_pd_tbl; - unsigned int opp_pd_tbl_size; - const struct platform_clk_data *clk_tbl; - const char * const *opp_clk_tbl; - unsigned int clk_tbl_size; - const char * const *clk_rst_tbl; - unsigned int clk_rst_tbl_size; - const char * const *controller_rst_tbl; - unsigned int controller_rst_tbl_size; - u64 dma_mask; - const char *fwname; - u32 pas_id; - struct iris_fmt *inst_iris_fmts; - u32 inst_iris_fmts_size; - struct platform_inst_caps *inst_caps; +struct iris_firmware_data { + void (*init_hfi_ops)(struct iris_core *core); + + u32 core_arch; + const struct platform_inst_fw_cap *inst_fw_caps_dec; u32 inst_fw_caps_dec_size; const struct platform_inst_fw_cap *inst_fw_caps_enc; u32 inst_fw_caps_enc_size; - const struct tz_cp_config *tz_cp_config_data; - u32 tz_cp_config_data_size; - u32 core_arch; - u32 hw_response_timeout; - struct ubwc_config_data *ubwc_config; - u32 num_vpp_pipe; - bool no_aon; - u32 max_session_count; - /* max number of macroblocks per frame supported */ - u32 max_core_mbpf; - /* max number of macroblocks per second supported */ - u32 max_core_mbps; + const u32 *dec_input_config_params_default; unsigned int dec_input_config_params_default_size; const u32 *dec_input_config_params_hevc; @@ -270,6 +260,7 @@ struct iris_platform_data { unsigned int enc_input_config_params_size; const u32 *enc_output_config_params; unsigned int enc_output_config_params_size; + const u32 *dec_input_prop; unsigned int dec_input_prop_size; const u32 *dec_output_prop_avc; @@ -280,6 +271,7 @@ struct iris_platform_data { unsigned int dec_output_prop_vp9_size; const u32 *dec_output_prop_av1; unsigned int dec_output_prop_av1_size; + const u32 *dec_ip_int_buf_tbl; unsigned int dec_ip_int_buf_tbl_size; const u32 *dec_op_int_buf_tbl; @@ -290,4 +282,48 @@ struct iris_platform_data { unsigned int enc_op_int_buf_tbl_size; }; +struct iris_firmware_desc { + const struct iris_firmware_data *firmware_data; + u32 (*get_vpu_buffer_size)(struct iris_inst *inst, enum iris_buffer_type buffer_type); + const char *fwname; +}; + +struct iris_platform_data { + /* + * XXX: replace with gen1 / gen2 pointers once we have platforms + * supporting both firmware kinds. + */ + const struct iris_firmware_desc *firmware_desc; + + const struct vpu_ops *vpu_ops; + const struct icc_info *icc_tbl; + unsigned int icc_tbl_size; + const struct bw_info *bw_tbl_dec; + unsigned int bw_tbl_dec_size; + const char * const *pmdomain_tbl; + unsigned int pmdomain_tbl_size; + const char * const *opp_pd_tbl; + unsigned int opp_pd_tbl_size; + const struct platform_clk_data *clk_tbl; + const char * const *opp_clk_tbl; + unsigned int clk_tbl_size; + const char * const *clk_rst_tbl; + unsigned int clk_rst_tbl_size; + const char * const *controller_rst_tbl; + unsigned int controller_rst_tbl_size; + u64 dma_mask; + const u32 *inst_iris_fmts; + u32 inst_iris_fmts_size; + struct platform_inst_caps *inst_caps; + const struct tz_cp_config *tz_cp_config_data; + u32 tz_cp_config_data_size; + u32 num_vpp_pipe; + bool no_aon; + u32 max_session_count; + /* max number of macroblocks per frame supported */ + u32 max_core_mbpf; + /* max number of macroblocks per second supported */ + u32 max_core_mbps; +}; + #endif diff --git a/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h b/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h index 61025f1e965b..3cfecae80d1e 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h +++ b/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h @@ -15,7 +15,6 @@ static struct platform_inst_caps platform_inst_cap_qcs8300 = { .mb_cycles_vpp = 200, .mb_cycles_fw = 326389, .mb_cycles_fw_vpp = 44156, - .num_comv = 0, .max_frame_rate = MAXIMUM_FPS, .max_operating_rate = MAXIMUM_FPS, }; diff --git a/drivers/media/platform/qcom/iris/iris_platform_sm8250.h b/drivers/media/platform/qcom/iris/iris_platform_sm8250.h new file mode 100644 index 000000000000..50306043eb8e --- /dev/null +++ b/drivers/media/platform/qcom/iris/iris_platform_sm8250.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __IRIS_PLATFORM_SM8250_H__ +#define __IRIS_PLATFORM_SM8250_H__ + +static const struct bw_info sm8250_bw_table_dec[] = { + { ((4096 * 2160) / 256) * 60, 2403000 }, + { ((4096 * 2160) / 256) * 30, 1224000 }, + { ((1920 * 1080) / 256) * 60, 812000 }, + { ((1920 * 1080) / 256) * 30, 416000 }, +}; + +static const char * const sm8250_opp_pd_table[] = { "mx", "mmcx" }; + +static const struct platform_clk_data sm8250_clk_table[] = { + {IRIS_AXI_CLK, "iface" }, + {IRIS_CTRL_CLK, "core" }, + {IRIS_HW_CLK, "vcodec0_core" }, +}; + +static const char * const sm8250_opp_clk_table[] = { + "vcodec0_core", + NULL, +}; + +#endif diff --git a/drivers/media/platform/qcom/iris/iris_platform_sm8550.h b/drivers/media/platform/qcom/iris/iris_platform_sm8550.h new file mode 100644 index 000000000000..3c9dae995bb2 --- /dev/null +++ b/drivers/media/platform/qcom/iris/iris_platform_sm8550.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef __IRIS_PLATFORM_SM8550_H__ +#define __IRIS_PLATFORM_SM8550_H__ + +static const char * const sm8550_clk_reset_table[] = { "bus" }; + +static const struct platform_clk_data sm8550_clk_table[] = { + {IRIS_AXI_CLK, "iface" }, + {IRIS_CTRL_CLK, "core" }, + {IRIS_HW_CLK, "vcodec0_core" }, +}; + +static struct platform_inst_caps platform_inst_cap_sm8550 = { + .min_frame_width = 96, + .max_frame_width = 8192, + .min_frame_height = 96, + .max_frame_height = 8192, + .max_mbpf = (8192 * 4352) / 256, + .mb_cycles_vpp = 200, + .mb_cycles_fw = 489583, + .mb_cycles_fw_vpp = 66234, + .max_frame_rate = MAXIMUM_FPS, + .max_operating_rate = MAXIMUM_FPS, +}; + +#endif diff --git a/drivers/media/platform/qcom/iris/iris_platform_vpu2.c b/drivers/media/platform/qcom/iris/iris_platform_vpu2.c new file mode 100644 index 000000000000..6e06a32822bb --- /dev/null +++ b/drivers/media/platform/qcom/iris/iris_platform_vpu2.c @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include "iris_core.h" +#include "iris_ctrls.h" +#include "iris_platform_common.h" +#include "iris_resources.h" +#include "iris_hfi_gen1.h" +#include "iris_hfi_gen1_defines.h" +#include "iris_vpu_buffer.h" +#include "iris_vpu_common.h" +#include "iris_instance.h" + +#include "iris_platform_sc7280.h" +#include "iris_platform_sm8250.h" + +static const struct iris_firmware_desc iris_vpu20_p1_gen1_desc = { + .firmware_data = &iris_hfi_gen1_data, + .get_vpu_buffer_size = iris_vpu_buf_size, + .fwname = "qcom/vpu/vpu20_p1.mbn", +}; + +static const struct iris_firmware_desc iris_vpu20_p4_gen1_desc = { + .firmware_data = &iris_hfi_gen1_data, + .get_vpu_buffer_size = iris_vpu_buf_size, + .fwname = "qcom/vpu/vpu20_p4.mbn", +}; + +static const u32 iris_fmts_vpu2_dec[] = { + [IRIS_FMT_H264] = V4L2_PIX_FMT_H264, + [IRIS_FMT_HEVC] = V4L2_PIX_FMT_HEVC, + [IRIS_FMT_VP9] = V4L2_PIX_FMT_VP9, +}; + +static struct platform_inst_caps platform_inst_cap_vpu2 = { + .min_frame_width = 128, + .max_frame_width = 8192, + .min_frame_height = 128, + .max_frame_height = 8192, + .max_mbpf = 138240, + .mb_cycles_vsp = 25, + .mb_cycles_vpp = 200, + .max_frame_rate = MAXIMUM_FPS, + .max_operating_rate = MAXIMUM_FPS, +}; + +static const struct icc_info iris_icc_info_vpu2[] = { + { "cpu-cfg", 1000, 1000 }, + { "video-mem", 1000, 15000000 }, +}; + +static const char * const iris_clk_reset_table_vpu2[] = { "bus", "core" }; + +static const char * const iris_pmdomain_table_vpu2[] = { "venus", "vcodec0" }; + +static const struct tz_cp_config tz_cp_config_vpu2[] = { + { + .cp_start = 0, + .cp_size = 0x25800000, + .cp_nonpixel_start = 0x01000000, + .cp_nonpixel_size = 0x24800000, + }, +}; + +const struct iris_platform_data sc7280_data = { + .firmware_desc = &iris_vpu20_p1_gen1_desc, + .vpu_ops = &iris_vpu2_ops, + .icc_tbl = iris_icc_info_vpu2, + .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu2), + .bw_tbl_dec = sc7280_bw_table_dec, + .bw_tbl_dec_size = ARRAY_SIZE(sc7280_bw_table_dec), + .pmdomain_tbl = iris_pmdomain_table_vpu2, + .pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu2), + .opp_pd_tbl = sc7280_opp_pd_table, + .opp_pd_tbl_size = ARRAY_SIZE(sc7280_opp_pd_table), + .clk_tbl = sc7280_clk_table, + .clk_tbl_size = ARRAY_SIZE(sc7280_clk_table), + .opp_clk_tbl = sc7280_opp_clk_table, + /* Upper bound of DMA address range */ + .dma_mask = 0xe0000000 - 1, + .inst_iris_fmts = iris_fmts_vpu2_dec, + .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu2_dec), + .inst_caps = &platform_inst_cap_vpu2, + .tz_cp_config_data = tz_cp_config_vpu2, + .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu2), + .num_vpp_pipe = 1, + .no_aon = true, + .max_session_count = 16, + .max_core_mbpf = 4096 * 2176 / 256 * 2 + 1920 * 1088 / 256, + /* max spec for SC7280 is 4096x2176@60fps */ + .max_core_mbps = 4096 * 2176 / 256 * 60, +}; + +const struct iris_platform_data sm8250_data = { + .firmware_desc = &iris_vpu20_p4_gen1_desc, + .vpu_ops = &iris_vpu2_ops, + .icc_tbl = iris_icc_info_vpu2, + .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu2), + .clk_rst_tbl = iris_clk_reset_table_vpu2, + .clk_rst_tbl_size = ARRAY_SIZE(iris_clk_reset_table_vpu2), + .bw_tbl_dec = sm8250_bw_table_dec, + .bw_tbl_dec_size = ARRAY_SIZE(sm8250_bw_table_dec), + .pmdomain_tbl = iris_pmdomain_table_vpu2, + .pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu2), + .opp_pd_tbl = sm8250_opp_pd_table, + .opp_pd_tbl_size = ARRAY_SIZE(sm8250_opp_pd_table), + .clk_tbl = sm8250_clk_table, + .clk_tbl_size = ARRAY_SIZE(sm8250_clk_table), + .opp_clk_tbl = sm8250_opp_clk_table, + /* Upper bound of DMA address range */ + .dma_mask = 0xe0000000 - 1, + .inst_iris_fmts = iris_fmts_vpu2_dec, + .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu2_dec), + .inst_caps = &platform_inst_cap_vpu2, + .tz_cp_config_data = tz_cp_config_vpu2, + .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu2), + .num_vpp_pipe = 4, + .max_session_count = 16, + .max_core_mbpf = NUM_MBS_8K, + .max_core_mbps = ((7680 * 4320) / 256) * 60, +}; diff --git a/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c b/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c new file mode 100644 index 000000000000..2c63adbc5579 --- /dev/null +++ b/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c @@ -0,0 +1,249 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2025 Linaro Ltd + */ + +#include "iris_core.h" +#include "iris_ctrls.h" +#include "iris_hfi_gen2.h" +#include "iris_hfi_gen2_defines.h" +#include "iris_platform_common.h" +#include "iris_vpu_buffer.h" +#include "iris_vpu_common.h" + +#include "iris_platform_qcs8300.h" +#include "iris_platform_sm8550.h" +#include "iris_platform_sm8650.h" +#include "iris_platform_sm8750.h" +#include "iris_platform_x1p42100.h" + +static const struct iris_firmware_desc iris_vpu30_p4_s6_gen2_desc = { + .firmware_data = &iris_hfi_gen2_data, + .get_vpu_buffer_size = iris_vpu_buf_size, + .fwname = "qcom/vpu/vpu30_p4_s6.mbn", +}; + +static const struct iris_firmware_desc iris_vpu30_p4_gen2_desc = { + .firmware_data = &iris_hfi_gen2_data, + .get_vpu_buffer_size = iris_vpu_buf_size, + .fwname = "qcom/vpu/vpu30_p4.mbn", +}; + +static const struct iris_firmware_desc iris_vpu30_p1_gen2_desc = { + .firmware_data = &iris_hfi_gen2_data, + .get_vpu_buffer_size = iris_vpu_buf_size, + .fwname = "qcom/vpu/vpu30_p1_s7.mbn", +}; + +static const struct iris_firmware_desc iris_vpu33_p4_gen2_desc = { + .firmware_data = &iris_hfi_gen2_data, + .get_vpu_buffer_size = iris_vpu33_buf_size, + .fwname = "qcom/vpu/vpu33_p4.mbn", +}; + +static const struct iris_firmware_desc iris_vpu35_p4_gen2_desc = { + .firmware_data = &iris_hfi_gen2_data, + .get_vpu_buffer_size = iris_vpu33_buf_size, + .fwname = "qcom/vpu/vpu35_p4.mbn", +}; + +static const u32 iris_fmts_vpu3x_dec[] = { + [IRIS_FMT_H264] = V4L2_PIX_FMT_H264, + [IRIS_FMT_HEVC] = V4L2_PIX_FMT_HEVC, + [IRIS_FMT_VP9] = V4L2_PIX_FMT_VP9, + [IRIS_FMT_AV1] = V4L2_PIX_FMT_AV1, +}; + +static const struct icc_info iris_icc_info_vpu3x[] = { + { "cpu-cfg", 1000, 1000 }, + { "video-mem", 1000, 15000000 }, +}; + +static const struct bw_info iris_bw_table_dec_vpu3x[] = { + { ((4096 * 2160) / 256) * 60, 1608000 }, + { ((4096 * 2160) / 256) * 30, 826000 }, + { ((1920 * 1080) / 256) * 60, 567000 }, + { ((1920 * 1080) / 256) * 30, 294000 }, +}; + +static const char * const iris_pmdomain_table_vpu3x[] = { "venus", "vcodec0" }; + +static const char * const iris_opp_pd_table_vpu3x[] = { "mxc", "mmcx" }; + +static const char * const iris_opp_clk_table_vpu3x[] = { + "vcodec0_core", + NULL, +}; + +static const struct tz_cp_config tz_cp_config_vpu3[] = { + { + .cp_start = 0, + .cp_size = 0x25800000, + .cp_nonpixel_start = 0x01000000, + .cp_nonpixel_size = 0x24800000, + }, +}; + +/* + * Shares most of SM8550 data except: + * - inst_caps to platform_inst_cap_qcs8300 + */ +const struct iris_platform_data qcs8300_data = { + .firmware_desc = &iris_vpu30_p4_s6_gen2_desc, + .vpu_ops = &iris_vpu3_ops, + .icc_tbl = iris_icc_info_vpu3x, + .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x), + .clk_rst_tbl = sm8550_clk_reset_table, + .clk_rst_tbl_size = ARRAY_SIZE(sm8550_clk_reset_table), + .bw_tbl_dec = iris_bw_table_dec_vpu3x, + .bw_tbl_dec_size = ARRAY_SIZE(iris_bw_table_dec_vpu3x), + .pmdomain_tbl = iris_pmdomain_table_vpu3x, + .pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu3x), + .opp_pd_tbl = iris_opp_pd_table_vpu3x, + .opp_pd_tbl_size = ARRAY_SIZE(iris_opp_pd_table_vpu3x), + .clk_tbl = sm8550_clk_table, + .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table), + .opp_clk_tbl = iris_opp_clk_table_vpu3x, + /* Upper bound of DMA address range */ + .dma_mask = 0xe0000000 - 1, + .inst_iris_fmts = iris_fmts_vpu3x_dec, + .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu3x_dec), + .inst_caps = &platform_inst_cap_qcs8300, + .tz_cp_config_data = tz_cp_config_vpu3, + .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu3), + .num_vpp_pipe = 2, + .max_session_count = 16, + .max_core_mbpf = ((4096 * 2176) / 256) * 4, + .max_core_mbps = (((3840 * 2176) / 256) * 120), +}; + +const struct iris_platform_data sm8550_data = { + .firmware_desc = &iris_vpu30_p4_gen2_desc, + .vpu_ops = &iris_vpu3_ops, + .icc_tbl = iris_icc_info_vpu3x, + .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x), + .clk_rst_tbl = sm8550_clk_reset_table, + .clk_rst_tbl_size = ARRAY_SIZE(sm8550_clk_reset_table), + .bw_tbl_dec = iris_bw_table_dec_vpu3x, + .bw_tbl_dec_size = ARRAY_SIZE(iris_bw_table_dec_vpu3x), + .pmdomain_tbl = iris_pmdomain_table_vpu3x, + .pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu3x), + .opp_pd_tbl = iris_opp_pd_table_vpu3x, + .opp_pd_tbl_size = ARRAY_SIZE(iris_opp_pd_table_vpu3x), + .clk_tbl = sm8550_clk_table, + .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table), + .opp_clk_tbl = iris_opp_clk_table_vpu3x, + /* Upper bound of DMA address range */ + .dma_mask = 0xe0000000 - 1, + .inst_iris_fmts = iris_fmts_vpu3x_dec, + .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu3x_dec), + .inst_caps = &platform_inst_cap_sm8550, + .tz_cp_config_data = tz_cp_config_vpu3, + .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu3), + .num_vpp_pipe = 4, + .max_session_count = 16, + .max_core_mbpf = NUM_MBS_8K * 2, + .max_core_mbps = ((7680 * 4320) / 256) * 60, +}; + +/* + * Shares most of SM8550 data except: + * - vpu_ops to iris_vpu33_ops + * - clk_rst_tbl to sm8650_clk_reset_table + * - controller_rst_tbl to sm8650_controller_reset_table + */ +const struct iris_platform_data sm8650_data = { + .firmware_desc = &iris_vpu33_p4_gen2_desc, + .vpu_ops = &iris_vpu33_ops, + .icc_tbl = iris_icc_info_vpu3x, + .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x), + .clk_rst_tbl = sm8650_clk_reset_table, + .clk_rst_tbl_size = ARRAY_SIZE(sm8650_clk_reset_table), + .controller_rst_tbl = sm8650_controller_reset_table, + .controller_rst_tbl_size = ARRAY_SIZE(sm8650_controller_reset_table), + .bw_tbl_dec = iris_bw_table_dec_vpu3x, + .bw_tbl_dec_size = ARRAY_SIZE(iris_bw_table_dec_vpu3x), + .pmdomain_tbl = iris_pmdomain_table_vpu3x, + .pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu3x), + .opp_pd_tbl = iris_opp_pd_table_vpu3x, + .opp_pd_tbl_size = ARRAY_SIZE(iris_opp_pd_table_vpu3x), + .clk_tbl = sm8550_clk_table, + .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table), + .opp_clk_tbl = iris_opp_clk_table_vpu3x, + /* Upper bound of DMA address range */ + .dma_mask = 0xe0000000 - 1, + .inst_iris_fmts = iris_fmts_vpu3x_dec, + .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu3x_dec), + .inst_caps = &platform_inst_cap_sm8550, + .tz_cp_config_data = tz_cp_config_vpu3, + .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu3), + .num_vpp_pipe = 4, + .max_session_count = 16, + .max_core_mbpf = NUM_MBS_8K * 2, + .max_core_mbps = ((7680 * 4320) / 256) * 60, +}; + +const struct iris_platform_data sm8750_data = { + .firmware_desc = &iris_vpu35_p4_gen2_desc, + .vpu_ops = &iris_vpu35_ops, + .icc_tbl = iris_icc_info_vpu3x, + .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x), + .clk_rst_tbl = sm8750_clk_reset_table, + .clk_rst_tbl_size = ARRAY_SIZE(sm8750_clk_reset_table), + .bw_tbl_dec = iris_bw_table_dec_vpu3x, + .bw_tbl_dec_size = ARRAY_SIZE(iris_bw_table_dec_vpu3x), + .pmdomain_tbl = iris_pmdomain_table_vpu3x, + .pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu3x), + .opp_pd_tbl = iris_opp_pd_table_vpu3x, + .opp_pd_tbl_size = ARRAY_SIZE(iris_opp_pd_table_vpu3x), + .clk_tbl = sm8750_clk_table, + .clk_tbl_size = ARRAY_SIZE(sm8750_clk_table), + .opp_clk_tbl = iris_opp_clk_table_vpu3x, + /* Upper bound of DMA address range */ + .dma_mask = 0xe0000000 - 1, + .inst_iris_fmts = iris_fmts_vpu3x_dec, + .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu3x_dec), + .inst_caps = &platform_inst_cap_sm8550, + .tz_cp_config_data = tz_cp_config_vpu3, + .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu3), + .num_vpp_pipe = 4, + .max_session_count = 16, + .max_core_mbpf = NUM_MBS_8K * 2, + .max_core_mbps = ((7680 * 4320) / 256) * 60, +}; + +/* + * Shares most of SM8550 data except: + * - clk_tbl and opp_clk_tbl for x1p42100 + * - different firmware + * - different num_vpp_pipe + */ +const struct iris_platform_data x1p42100_data = { + .firmware_desc = &iris_vpu30_p1_gen2_desc, + .vpu_ops = &iris_vpu3_ops, + .icc_tbl = iris_icc_info_vpu3x, + .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x), + .clk_rst_tbl = sm8550_clk_reset_table, + .clk_rst_tbl_size = ARRAY_SIZE(sm8550_clk_reset_table), + .bw_tbl_dec = iris_bw_table_dec_vpu3x, + .bw_tbl_dec_size = ARRAY_SIZE(iris_bw_table_dec_vpu3x), + .pmdomain_tbl = iris_pmdomain_table_vpu3x, + .pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu3x), + .opp_pd_tbl = iris_opp_pd_table_vpu3x, + .opp_pd_tbl_size = ARRAY_SIZE(iris_opp_pd_table_vpu3x), + .clk_tbl = x1p42100_clk_table, + .clk_tbl_size = ARRAY_SIZE(x1p42100_clk_table), + .opp_clk_tbl = x1p42100_opp_clk_table, + /* Upper bound of DMA address range */ + .dma_mask = 0xe0000000 - 1, + .inst_iris_fmts = iris_fmts_vpu3x_dec, + .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu3x_dec), + .inst_caps = &platform_inst_cap_sm8550, + .tz_cp_config_data = tz_cp_config_vpu3, + .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu3), + .num_vpp_pipe = 1, + .max_session_count = 16, + .max_core_mbpf = NUM_MBS_8K * 2, + .max_core_mbps = ((7680 * 4320) / 256) * 60, +}; diff --git a/drivers/media/platform/qcom/iris/iris_platform_x1p42100.h b/drivers/media/platform/qcom/iris/iris_platform_x1p42100.h new file mode 100644 index 000000000000..d89acfbc1233 --- /dev/null +++ b/drivers/media/platform/qcom/iris/iris_platform_x1p42100.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __IRIS_PLATFORM_X1P42100_H__ +#define __IRIS_PLATFORM_X1P42100_H__ + +static const struct platform_clk_data x1p42100_clk_table[] = { + {IRIS_AXI_CLK, "iface" }, + {IRIS_CTRL_CLK, "core" }, + {IRIS_HW_CLK, "vcodec0_core" }, + {IRIS_BSE_HW_CLK, "vcodec0_bse" }, +}; + +static const char *const x1p42100_opp_clk_table[] = { + "vcodec0_core", + "vcodec0_bse", + NULL, +}; + +#endif diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c index ddaacda523ec..c2dcb50a2782 100644 --- a/drivers/media/platform/qcom/iris/iris_probe.c +++ b/drivers/media/platform/qcom/iris/iris_probe.c @@ -10,6 +10,7 @@ #include <linux/pm_opp.h> #include <linux/pm_runtime.h> #include <linux/reset.h> +#include <linux/soc/qcom/ubwc.h> #include "iris_core.h" #include "iris_ctrls.h" @@ -64,6 +65,13 @@ static int iris_init_power_domains(struct iris_core *core) return ret; ret = devm_pm_domain_attach_list(core->dev, &iris_opp_pd_data, &core->opp_pmdomain_tbl); + /* backwards compatibility for incomplete ABI SM8250 */ + if (ret == -ENODEV && + of_device_is_compatible(core->dev->of_node, "qcom,sm8250-venus")) { + iris_opp_pd_data.num_pd_names--; + ret = devm_pm_domain_attach_list(core->dev, &iris_opp_pd_data, + &core->opp_pmdomain_tbl); + } if (ret < 0) return ret; @@ -243,17 +251,21 @@ static int iris_probe(struct platform_device *pdev) return core->irq; core->iris_platform_data = of_device_get_match_data(core->dev); + core->iris_firmware_desc = core->iris_platform_data->firmware_desc; + core->iris_firmware_data = core->iris_firmware_desc->firmware_data; + + core->ubwc_cfg = qcom_ubwc_config_get_data(); + if (IS_ERR(core->ubwc_cfg)) + return PTR_ERR(core->ubwc_cfg); ret = devm_request_threaded_irq(core->dev, core->irq, iris_hfi_isr, - iris_hfi_isr_handler, IRQF_TRIGGER_HIGH, "iris", core); + iris_hfi_isr_handler, + IRQF_TRIGGER_HIGH | IRQF_NO_AUTOEN, + "iris", core); if (ret) return ret; - disable_irq_nosync(core->irq); - iris_init_ops(core); - core->iris_platform_data->init_hfi_command_ops(core); - core->iris_platform_data->init_hfi_response_ops(core); ret = iris_init_resources(core); if (ret) @@ -352,7 +364,6 @@ static const struct of_device_id iris_dt_match[] = { .compatible = "qcom,qcs8300-iris", .data = &qcs8300_data, }, -#if (!IS_ENABLED(CONFIG_VIDEO_QCOM_VENUS)) { .compatible = "qcom,sc7280-venus", .data = &sc7280_data, @@ -361,7 +372,6 @@ static const struct of_device_id iris_dt_match[] = { .compatible = "qcom,sm8250-venus", .data = &sm8250_data, }, -#endif { .compatible = "qcom,sm8550-iris", .data = &sm8550_data, @@ -374,6 +384,10 @@ static const struct of_device_id iris_dt_match[] = { .compatible = "qcom,sm8750-iris", .data = &sm8750_data, }, + { + .compatible = "qcom,x1p42100-iris", + .data = &x1p42100_data, + }, { }, }; MODULE_DEVICE_TABLE(of, iris_dt_match); diff --git a/drivers/media/platform/qcom/iris/iris_utils.c b/drivers/media/platform/qcom/iris/iris_utils.c index cfc5b576ec56..ba5c8dc1280c 100644 --- a/drivers/media/platform/qcom/iris/iris_utils.c +++ b/drivers/media/platform/qcom/iris/iris_utils.c @@ -35,7 +35,21 @@ int iris_get_mbpf(struct iris_inst *inst) bool iris_split_mode_enabled(struct iris_inst *inst) { return inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_NV12 || - inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC08C; + inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC08C || + inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_P010 || + inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC10C; +} + +bool iris_fmt_is_8bit(u32 pixelformat) +{ + return pixelformat == V4L2_PIX_FMT_NV12 || + pixelformat == V4L2_PIX_FMT_QC08C; +} + +bool iris_fmt_is_10bit(u32 pixelformat) +{ + return pixelformat == V4L2_PIX_FMT_P010 || + pixelformat == V4L2_PIX_FMT_QC10C; } void iris_helper_buffers_done(struct iris_inst *inst, unsigned int type, @@ -55,16 +69,13 @@ void iris_helper_buffers_done(struct iris_inst *inst, unsigned int type, int iris_wait_for_session_response(struct iris_inst *inst, bool is_flush) { - struct iris_core *core = inst->core; - u32 hw_response_timeout_val; struct completion *done; int ret; - hw_response_timeout_val = core->iris_platform_data->hw_response_timeout; done = is_flush ? &inst->flush_completion : &inst->completion; mutex_unlock(&inst->lock); - ret = wait_for_completion_timeout(done, msecs_to_jiffies(hw_response_timeout_val)); + ret = wait_for_completion_timeout(done, msecs_to_jiffies(HW_RESPONSE_TIMEOUT_VALUE)); mutex_lock(&inst->lock); if (!ret) { iris_inst_change_state(inst, IRIS_INST_ERROR); diff --git a/drivers/media/platform/qcom/iris/iris_utils.h b/drivers/media/platform/qcom/iris/iris_utils.h index b5705d156431..228a5f963812 100644 --- a/drivers/media/platform/qcom/iris/iris_utils.h +++ b/drivers/media/platform/qcom/iris/iris_utils.h @@ -45,6 +45,8 @@ bool iris_res_is_less_than(u32 width, u32 height, u32 ref_width, u32 ref_height); int iris_get_mbpf(struct iris_inst *inst); bool iris_split_mode_enabled(struct iris_inst *inst); +bool iris_fmt_is_8bit(u32 pixelformat); +bool iris_fmt_is_10bit(u32 pixelformat); struct iris_inst *iris_get_instance(struct iris_core *core, u32 session_id); void iris_helper_buffers_done(struct iris_inst *inst, unsigned int type, enum vb2_buffer_state state); diff --git a/drivers/media/platform/qcom/iris/iris_vb2.c b/drivers/media/platform/qcom/iris/iris_vb2.c index bf0b8400996e..a2ea2d67f60d 100644 --- a/drivers/media/platform/qcom/iris/iris_vb2.c +++ b/drivers/media/platform/qcom/iris/iris_vb2.c @@ -129,7 +129,7 @@ int iris_vb2_queue_setup(struct vb2_queue *q, if (!inst->once_per_session_set) { inst->once_per_session_set = true; - ret = core->hfi_ops->session_open(inst); + ret = inst->hfi_session_ops->session_open(inst); if (ret) { ret = -EINVAL; dev_err(core->dev, "session open failed\n"); diff --git a/drivers/media/platform/qcom/iris/iris_vdec.c b/drivers/media/platform/qcom/iris/iris_vdec.c index 99d544e2af4f..9e228b70420e 100644 --- a/drivers/media/platform/qcom/iris/iris_vdec.c +++ b/drivers/media/platform/qcom/iris/iris_vdec.c @@ -24,7 +24,7 @@ int iris_vdec_inst_init(struct iris_inst *inst) inst->fmt_src = kzalloc_obj(*inst->fmt_src); inst->fmt_dst = kzalloc_obj(*inst->fmt_dst); - inst->fw_min_count = MIN_BUFFERS; + inst->fw_min_count = 0; f = inst->fmt_src; f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; @@ -54,6 +54,7 @@ int iris_vdec_inst_init(struct iris_inst *inst) f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT; inst->buffers[BUF_OUTPUT].min_count = iris_vpu_buf_count(inst, BUF_OUTPUT); inst->buffers[BUF_OUTPUT].size = f->fmt.pix_mp.plane_fmt[0].sizeimage; + inst->frame_rate = MAXIMUM_FPS; memcpy(&inst->fw_caps[0], &core->inst_fw_caps_dec[0], INST_FW_CAP_MAX * sizeof(struct platform_inst_fw_cap)); @@ -61,23 +62,18 @@ int iris_vdec_inst_init(struct iris_inst *inst) return iris_ctrls_init(inst); } -static const struct iris_fmt iris_vdec_formats_cap[] = { - [IRIS_FMT_NV12] = { - .pixfmt = V4L2_PIX_FMT_NV12, - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, - }, - [IRIS_FMT_QC08C] = { - .pixfmt = V4L2_PIX_FMT_QC08C, - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, - }, +static const u32 iris_vdec_formats_cap[] = { + [IRIS_FMT_NV12] = V4L2_PIX_FMT_NV12, + [IRIS_FMT_QC08C] = V4L2_PIX_FMT_QC08C, + [IRIS_FMT_TP10] = V4L2_PIX_FMT_P010, + [IRIS_FMT_QC10C] = V4L2_PIX_FMT_QC10C, }; -static const struct iris_fmt * -find_format(struct iris_inst *inst, u32 pixfmt, u32 type) +static bool check_format(struct iris_inst *inst, u32 pixfmt, u32 type) { - const struct iris_fmt *fmt = NULL; - unsigned int size = 0; - unsigned int i; + unsigned int size, i; + const u32 *fmt; + switch (type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: fmt = inst->core->iris_platform_data->inst_iris_fmts; @@ -88,25 +84,34 @@ find_format(struct iris_inst *inst, u32 pixfmt, u32 type) size = ARRAY_SIZE(iris_vdec_formats_cap); break; default: - return NULL; + return false; } for (i = 0; i < size; i++) { - if (fmt[i].pixfmt == pixfmt) + if (fmt[i] == pixfmt) break; } - if (i == size || fmt[i].type != type) - return NULL; + if (i == size) + return false; + + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + if (iris_fmt_is_8bit(pixfmt) && + inst->fw_caps[BIT_DEPTH].value == BIT_DEPTH_10) + return false; + + if (iris_fmt_is_10bit(pixfmt) && + inst->fw_caps[BIT_DEPTH].value != BIT_DEPTH_10) + return false; + } - return &fmt[i]; + return true; } -static const struct iris_fmt * -find_format_by_index(struct iris_inst *inst, u32 index, u32 type) +static u32 find_format_by_index(struct iris_inst *inst, u32 index, u32 type) { - const struct iris_fmt *fmt = NULL; - unsigned int size = 0; + unsigned int size; + const u32 *fmt; switch (type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: @@ -118,18 +123,18 @@ find_format_by_index(struct iris_inst *inst, u32 index, u32 type) size = ARRAY_SIZE(iris_vdec_formats_cap); break; default: - return NULL; + return 0; } - if (index >= size || fmt[index].type != type) - return NULL; + if (index >= size) + return 0; - return &fmt[index]; + return fmt[index]; } int iris_vdec_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f) { - const struct iris_fmt *fmt; + u32 fmt; switch (f->type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: @@ -137,14 +142,14 @@ int iris_vdec_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f) if (!fmt) return -EINVAL; - f->pixelformat = fmt->pixfmt; + f->pixelformat = fmt; f->flags = V4L2_FMT_FLAG_COMPRESSED | V4L2_FMT_FLAG_DYN_RESOLUTION; break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: fmt = find_format_by_index(inst, f->index, f->type); if (!fmt) return -EINVAL; - f->pixelformat = fmt->pixfmt; + f->pixelformat = fmt; break; default: return -EINVAL; @@ -157,15 +162,15 @@ int iris_vdec_try_fmt(struct iris_inst *inst, struct v4l2_format *f) { struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx; - const struct iris_fmt *fmt; struct v4l2_format *f_inst; struct vb2_queue *src_q; + bool supported; memset(pixmp->reserved, 0, sizeof(pixmp->reserved)); - fmt = find_format(inst, pixmp->pixelformat, f->type); + supported = check_format(inst, pixmp->pixelformat, f->type); switch (f->type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - if (!fmt) { + if (!supported) { f_inst = inst->fmt_src; f->fmt.pix_mp.width = f_inst->fmt.pix_mp.width; f->fmt.pix_mp.height = f_inst->fmt.pix_mp.height; @@ -173,7 +178,7 @@ int iris_vdec_try_fmt(struct iris_inst *inst, struct v4l2_format *f) } break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - if (!fmt) { + if (!supported) { f_inst = inst->fmt_dst; f->fmt.pix_mp.pixelformat = f_inst->fmt.pix_mp.pixelformat; f->fmt.pix_mp.width = f_inst->fmt.pix_mp.width; @@ -222,7 +227,7 @@ int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f) switch (f->type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - if (!(find_format(inst, f->fmt.pix_mp.pixelformat, f->type))) + if (!check_format(inst, f->fmt.pix_mp.pixelformat, f->type)) return -EINVAL; fmt = inst->fmt_src; @@ -252,6 +257,7 @@ int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f) /* Update capture format based on new ip w/h */ output_fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, 128); output_fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 32); + inst->buffers[BUF_OUTPUT].min_count = iris_vpu_buf_count(inst, BUF_OUTPUT); inst->buffers[BUF_OUTPUT].size = iris_get_buffer_size(inst, BUF_OUTPUT); inst->crop.left = 0; @@ -260,16 +266,34 @@ int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f) inst->crop.height = f->fmt.pix_mp.height; break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - if (!(find_format(inst, f->fmt.pix_mp.pixelformat, f->type))) + if (!check_format(inst, f->fmt.pix_mp.pixelformat, f->type)) return -EINVAL; fmt = inst->fmt_dst; fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; fmt->fmt.pix_mp.pixelformat = f->fmt.pix_mp.pixelformat; - fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, 128); - fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 32); fmt->fmt.pix_mp.num_planes = 1; - fmt->fmt.pix_mp.plane_fmt[0].bytesperline = ALIGN(f->fmt.pix_mp.width, 128); + switch (f->fmt.pix_mp.pixelformat) { + case V4L2_PIX_FMT_P010: + fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, 128); + fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 32); + fmt->fmt.pix_mp.plane_fmt[0].bytesperline = + ALIGN(f->fmt.pix_mp.width * 2, 256); + break; + case V4L2_PIX_FMT_QC10C: + fmt->fmt.pix_mp.width = roundup(f->fmt.pix_mp.width, 192); + fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 16); + fmt->fmt.pix_mp.plane_fmt[0].bytesperline = + ALIGN(f->fmt.pix_mp.width * 4 / 3, 256); + break; + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_QC08C: + fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, 128); + fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 32); + fmt->fmt.pix_mp.plane_fmt[0].bytesperline = + ALIGN(f->fmt.pix_mp.width, 128); + break; + } fmt->fmt.pix_mp.plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_OUTPUT); inst->buffers[BUF_OUTPUT].min_count = iris_vpu_buf_count(inst, BUF_OUTPUT); inst->buffers[BUF_OUTPUT].size = fmt->fmt.pix_mp.plane_fmt[0].sizeimage; @@ -289,16 +313,13 @@ int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f) int iris_vdec_validate_format(struct iris_inst *inst, u32 pixelformat) { - const struct iris_fmt *fmt = NULL; + bool supported; - fmt = find_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); - if (!fmt) { - fmt = find_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - if (!fmt) - return -EINVAL; - } + supported = check_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + if (!supported) + supported = check_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - return 0; + return supported ? 0 : -EINVAL; } int iris_vdec_subscribe_event(struct iris_inst *inst, const struct v4l2_event_subscription *sub) @@ -363,12 +384,14 @@ int iris_vdec_streamon_input(struct iris_inst *inst) if (ret) return ret; + inst->frame_counter = 0; + return iris_process_streamon_input(inst); } int iris_vdec_streamon_output(struct iris_inst *inst) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; int ret; ret = hfi_ops->session_set_config_params(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); @@ -405,6 +428,7 @@ int iris_vdec_qbuf(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf) { struct iris_buffer *buf = to_iris_buffer(vbuf); struct vb2_buffer *vb2 = &vbuf->vb2_buf; + u64 cur_buf_ns, delta_ns; struct vb2_queue *q; int ret; @@ -421,6 +445,22 @@ int iris_vdec_qbuf(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf) return 0; } + if (buf->type == BUF_INPUT) { + cur_buf_ns = ktime_get_ns(); + + if (!inst->frame_counter) + inst->last_buf_ns = cur_buf_ns; + + inst->frame_counter++; + delta_ns = cur_buf_ns - inst->last_buf_ns; + + if (delta_ns >= NSEC_PER_SEC) { + inst->frame_rate = clamp_t(u32, inst->frame_counter, DEFAULT_FPS, + MAXIMUM_FPS); + inst->frame_counter = 0; + } + } + iris_scale_power(inst); return iris_queue_buffer(inst, buf); @@ -428,7 +468,7 @@ int iris_vdec_qbuf(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf) int iris_vdec_start_cmd(struct iris_inst *inst) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; enum iris_inst_sub_state clear_sub_state = 0; struct vb2_queue *dst_vq; int ret; @@ -491,7 +531,7 @@ int iris_vdec_start_cmd(struct iris_inst *inst) int iris_vdec_stop_cmd(struct iris_inst *inst) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; int ret; ret = hfi_ops->session_drain(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); diff --git a/drivers/media/platform/qcom/iris/iris_venc.c b/drivers/media/platform/qcom/iris/iris_venc.c index 4d886769d958..a945992f63aa 100644 --- a/drivers/media/platform/qcom/iris/iris_venc.c +++ b/drivers/media/platform/qcom/iris/iris_venc.c @@ -79,34 +79,21 @@ int iris_venc_inst_init(struct iris_inst *inst) return iris_ctrls_init(inst); } -static const struct iris_fmt iris_venc_formats_cap[] = { - [IRIS_FMT_H264] = { - .pixfmt = V4L2_PIX_FMT_H264, - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, - }, - [IRIS_FMT_HEVC] = { - .pixfmt = V4L2_PIX_FMT_HEVC, - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, - }, +static const u32 iris_venc_formats_cap[] = { + [IRIS_FMT_H264] = V4L2_PIX_FMT_H264, + [IRIS_FMT_HEVC] = V4L2_PIX_FMT_HEVC, }; -static const struct iris_fmt iris_venc_formats_out[] = { - [IRIS_FMT_NV12] = { - .pixfmt = V4L2_PIX_FMT_NV12, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, - [IRIS_FMT_QC08C] = { - .pixfmt = V4L2_PIX_FMT_QC08C, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, +static const u32 iris_venc_formats_out[] = { + [IRIS_FMT_NV12] = V4L2_PIX_FMT_NV12, + [IRIS_FMT_QC08C] = V4L2_PIX_FMT_QC08C, }; -static const struct iris_fmt * -find_format(struct iris_inst *inst, u32 pixfmt, u32 type) +static bool check_format(struct iris_inst *inst, u32 pixfmt, u32 type) { - const struct iris_fmt *fmt = NULL; - unsigned int size = 0; - unsigned int i; + unsigned int size, i; + const u32 *fmt; + switch (type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: fmt = iris_venc_formats_out; @@ -117,25 +104,21 @@ find_format(struct iris_inst *inst, u32 pixfmt, u32 type) size = ARRAY_SIZE(iris_venc_formats_cap); break; default: - return NULL; + return false; } for (i = 0; i < size; i++) { - if (fmt[i].pixfmt == pixfmt) - break; + if (fmt[i] == pixfmt) + return true; } - if (i == size || fmt[i].type != type) - return NULL; - - return &fmt[i]; + return false; } -static const struct iris_fmt * -find_format_by_index(struct iris_inst *inst, u32 index, u32 type) +static u32 find_format_by_index(struct iris_inst *inst, u32 index, u32 type) { - const struct iris_fmt *fmt = NULL; - unsigned int size = 0; + unsigned int size; + const u32 *fmt; switch (type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: @@ -147,18 +130,18 @@ find_format_by_index(struct iris_inst *inst, u32 index, u32 type) size = ARRAY_SIZE(iris_venc_formats_cap); break; default: - return NULL; + return 0; } - if (index >= size || fmt[index].type != type) - return NULL; + if (index >= size) + return 0; - return &fmt[index]; + return fmt[index]; } int iris_venc_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f) { - const struct iris_fmt *fmt; + u32 fmt; switch (f->type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: @@ -166,14 +149,14 @@ int iris_venc_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f) if (!fmt) return -EINVAL; - f->pixelformat = fmt->pixfmt; + f->pixelformat = fmt; break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: fmt = find_format_by_index(inst, f->index, f->type); if (!fmt) return -EINVAL; - f->pixelformat = fmt->pixfmt; + f->pixelformat = fmt; f->flags = V4L2_FMT_FLAG_COMPRESSED | V4L2_FMT_FLAG_ENC_CAP_FRAME_INTERVAL; break; default: @@ -186,14 +169,14 @@ int iris_venc_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f) int iris_venc_try_fmt(struct iris_inst *inst, struct v4l2_format *f) { struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; - const struct iris_fmt *fmt; struct v4l2_format *f_inst; + bool supported; memset(pixmp->reserved, 0, sizeof(pixmp->reserved)); - fmt = find_format(inst, pixmp->pixelformat, f->type); + supported = check_format(inst, pixmp->pixelformat, f->type); switch (f->type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - if (!fmt) { + if (!supported) { f_inst = inst->fmt_src; f->fmt.pix_mp.width = f_inst->fmt.pix_mp.width; f->fmt.pix_mp.height = f_inst->fmt.pix_mp.height; @@ -201,7 +184,7 @@ int iris_venc_try_fmt(struct iris_inst *inst, struct v4l2_format *f) } break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - if (!fmt) { + if (!supported) { f_inst = inst->fmt_dst; f->fmt.pix_mp.width = f_inst->fmt.pix_mp.width; f->fmt.pix_mp.height = f_inst->fmt.pix_mp.height; @@ -222,17 +205,17 @@ int iris_venc_try_fmt(struct iris_inst *inst, struct v4l2_format *f) static int iris_venc_s_fmt_output(struct iris_inst *inst, struct v4l2_format *f) { - const struct iris_fmt *venc_fmt; struct v4l2_format *fmt; u32 codec_align; + bool supported; iris_venc_try_fmt(inst, f); - venc_fmt = find_format(inst, f->fmt.pix_mp.pixelformat, f->type); - if (!venc_fmt) + supported = check_format(inst, f->fmt.pix_mp.pixelformat, f->type); + if (!supported) return -EINVAL; - codec_align = venc_fmt->pixfmt == V4L2_PIX_FMT_HEVC ? 32 : 16; + codec_align = (f->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_HEVC) ? 32 : 16; fmt = inst->fmt_dst; fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; @@ -275,7 +258,7 @@ static int iris_venc_s_fmt_input(struct iris_inst *inst, struct v4l2_format *f) iris_venc_try_fmt(inst, f); - if (!(find_format(inst, f->fmt.pix_mp.pixelformat, f->type))) + if (!check_format(inst, f->fmt.pix_mp.pixelformat, f->type)) return -EINVAL; fmt = inst->fmt_src; @@ -344,16 +327,13 @@ int iris_venc_s_fmt(struct iris_inst *inst, struct v4l2_format *f) int iris_venc_validate_format(struct iris_inst *inst, u32 pixelformat) { - const struct iris_fmt *fmt = NULL; + bool supported; - fmt = find_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - if (!fmt) { - fmt = find_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); - if (!fmt) - return -EINVAL; - } + supported = check_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + if (!supported) + supported = check_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); - return 0; + return supported ? 0 : -EINVAL; } int iris_venc_subscribe_event(struct iris_inst *inst, @@ -575,7 +555,7 @@ int iris_venc_qbuf(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf) int iris_venc_start_cmd(struct iris_inst *inst) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; enum iris_inst_sub_state clear_sub_state = 0; struct vb2_queue *dst_vq; int ret; @@ -617,7 +597,7 @@ int iris_venc_start_cmd(struct iris_inst *inst) int iris_venc_stop_cmd(struct iris_inst *inst) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; int ret; ret = hfi_ops->session_drain(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); diff --git a/drivers/media/platform/qcom/iris/iris_vidc.c b/drivers/media/platform/qcom/iris/iris_vidc.c index 5eb1786b0737..14d63dc76c9b 100644 --- a/drivers/media/platform/qcom/iris/iris_vidc.c +++ b/drivers/media/platform/qcom/iris/iris_vidc.c @@ -156,7 +156,7 @@ int iris_open(struct file *filp) pm_runtime_put_sync(core->dev); - inst = core->iris_platform_data->get_instance(); + inst = core->hfi_sys_ops->sys_get_instance(); if (!inst) return -ENOMEM; @@ -224,7 +224,7 @@ fail_v4l2_fh_deinit: static void iris_session_close(struct iris_inst *inst) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; bool wait_for_response = true; int ret; @@ -243,7 +243,7 @@ static void iris_session_close(struct iris_inst *inst) static void iris_check_num_queued_internal_buffers(struct iris_inst *inst, u32 plane) { - const struct iris_platform_data *platform_data = inst->core->iris_platform_data; + const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data; struct iris_buffer *buf, *next; struct iris_buffers *buffers; const u32 *internal_buf_type; @@ -251,11 +251,11 @@ static void iris_check_num_queued_internal_buffers(struct iris_inst *inst, u32 p u32 count = 0; if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = platform_data->dec_ip_int_buf_tbl; - internal_buffer_count = platform_data->dec_ip_int_buf_tbl_size; + internal_buf_type = firmware_data->dec_ip_int_buf_tbl; + internal_buffer_count = firmware_data->dec_ip_int_buf_tbl_size; } else { - internal_buf_type = platform_data->dec_op_int_buf_tbl; - internal_buffer_count = platform_data->dec_op_int_buf_tbl_size; + internal_buf_type = firmware_data->dec_op_int_buf_tbl; + internal_buffer_count = firmware_data->dec_op_int_buf_tbl_size; } for (i = 0; i < internal_buffer_count; i++) { diff --git a/drivers/media/platform/qcom/iris/iris_vpu2.c b/drivers/media/platform/qcom/iris/iris_vpu2.c index 01ef40f38957..b8714dcbad10 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu2.c +++ b/drivers/media/platform/qcom/iris/iris_vpu2.c @@ -18,7 +18,7 @@ static u64 iris_vpu2_calc_freq(struct iris_inst *inst, size_t data_size) struct v4l2_format *inp_f = inst->fmt_src; u32 mbs_per_second, mbpf, height, width; unsigned long vpp_freq, vsp_freq; - u32 fps = DEFAULT_FPS; + u32 fps = inst->frame_rate; width = max(inp_f->fmt.pix_mp.width, inst->crop.width); height = max(inp_f->fmt.pix_mp.height, inst->crop.height); diff --git a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c index 9270422c1601..fb6f1016415e 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c +++ b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c @@ -735,6 +735,24 @@ static u32 iris_vpu_dec_comv_size(struct iris_inst *inst) return hfi_buffer_comv_h264d(width, height, num_comv); else if (inst->codec == V4L2_PIX_FMT_HEVC) return hfi_buffer_comv_h265d(width, height, num_comv); + + return 0; +} + +static u32 iris_vpu3x_4x_dec_comv_size(struct iris_inst *inst) +{ + u32 num_comv = inst->buffers[BUF_OUTPUT].min_count; + struct v4l2_format *f = inst->fmt_src; + u32 height = f->fmt.pix_mp.height; + u32 width = f->fmt.pix_mp.width; + + if (inst->fw_min_count) + num_comv = inst->fw_min_count; + + if (inst->codec == V4L2_PIX_FMT_H264) + return hfi_buffer_comv_h264d(width, height, num_comv); + else if (inst->codec == V4L2_PIX_FMT_HEVC) + return hfi_buffer_comv_h265d(width, height, num_comv); else if (inst->codec == V4L2_PIX_FMT_AV1) { if (inst->fw_caps[DRAP].value) return 0; @@ -934,6 +952,51 @@ static u32 iris_vpu_enc_bin_size(struct iris_inst *inst) num_vpp_pipes, inst->hfi_rc_type); } +static inline u32 hfi_buffer_get_recon_count(struct iris_inst *inst) +{ + u32 layer_count = inst->hfi_layer_count; + u32 layer_type = inst->hfi_layer_type; + u32 bframe_count, ltr_count; + u32 num_ref = 1; + + bframe_count = inst->fw_caps[B_FRAME].value; + ltr_count = inst->fw_caps[LTR_COUNT].value; + + if (bframe_count) + num_ref = 2; + + /* The shift operation here is rounding logic, similar to [(x+1)/2]. */ + if (layer_type == HFI_HIER_P_HYBRID_LTR) + num_ref = (layer_count + 1) >> 1; + + if (layer_type == HFI_HIER_P_SLIDING_WINDOW) { + if (inst->codec == V4L2_PIX_FMT_HEVC) + num_ref = (layer_count + 1) >> 1; + else if (inst->codec == V4L2_PIX_FMT_H264 && layer_count < 4) + num_ref = (layer_count - 1); + else + num_ref = layer_count; + } + + if (ltr_count) + num_ref = num_ref + ltr_count; + + /* + * The expression (1 << layers - 2) + 1 accounts for the number of reference + * frames in the Adaptive Hierarchical B-frame encoding case. In this scheme, + * the number of frames in a sub-GOP is related to (2^(number of layers) - 1), + * hence the use of the shift operation. + */ + if (layer_type == HFI_HIER_B) { + if (inst->codec == V4L2_PIX_FMT_HEVC) + num_ref = layer_count; + else + num_ref = (1 << (layer_count - 2)) + 1; + } + + return num_ref; +} + static u32 iris_vpu_dec_partial_size(struct iris_inst *inst) { struct v4l2_format *f = inst->fmt_src; @@ -968,17 +1031,14 @@ static u32 iris_vpu_enc_comv_size(struct iris_inst *inst) { u32 height = iris_vpu_enc_get_bitstream_height(inst); u32 width = iris_vpu_enc_get_bitstream_width(inst); - u32 num_recon = 1; - u32 lcu_size = 16; + u32 num_recon = hfi_buffer_get_recon_count(inst); + u32 codec, lcu_size; - if (inst->codec == V4L2_PIX_FMT_HEVC) { - lcu_size = 32; - return hfi_buffer_comv_enc(width, height, lcu_size, - num_recon + 1, HFI_CODEC_ENCODE_HEVC); - } + codec = (inst->codec == V4L2_PIX_FMT_HEVC) ? + HFI_CODEC_ENCODE_HEVC : HFI_CODEC_ENCODE_AVC; + lcu_size = (inst->codec == V4L2_PIX_FMT_HEVC) ? 32 : 16; - return hfi_buffer_comv_enc(width, height, lcu_size, - num_recon + 1, HFI_CODEC_ENCODE_AVC); + return hfi_buffer_comv_enc(width, height, lcu_size, num_recon + 1, codec); } static inline @@ -1677,10 +1737,9 @@ static u32 iris_vpu_enc_scratch2_size(struct iris_inst *inst) { u32 frame_height = iris_vpu_enc_get_bitstream_height(inst); u32 frame_width = iris_vpu_enc_get_bitstream_width(inst); - u32 num_ref = 1; + u32 num_ref = hfi_buffer_get_recon_count(inst); - return hfi_buffer_scratch2_enc(frame_width, frame_height, num_ref, - false); + return hfi_buffer_scratch2_enc(frame_width, frame_height, num_ref, false); } static u32 iris_vpu_enc_vpss_size(struct iris_inst *inst) @@ -2025,7 +2084,7 @@ u32 iris_vpu_buf_size(struct iris_inst *inst, enum iris_buffer_type buffer_type) static const struct iris_vpu_buf_type_handle dec_internal_buf_type_handle[] = { {BUF_BIN, iris_vpu_dec_bin_size }, - {BUF_COMV, iris_vpu_dec_comv_size }, + {BUF_COMV, iris_vpu3x_4x_dec_comv_size }, {BUF_NON_COMV, iris_vpu_dec_non_comv_size }, {BUF_LINE, iris_vpu_dec_line_size }, {BUF_PERSIST, iris_vpu_dec_persist_size }, @@ -2098,7 +2157,7 @@ u32 iris_vpu4x_buf_size(struct iris_inst *inst, enum iris_buffer_type buffer_typ static const struct iris_vpu_buf_type_handle dec_internal_buf_type_handle[] = { {BUF_BIN, iris_vpu_dec_bin_size }, - {BUF_COMV, iris_vpu_dec_comv_size }, + {BUF_COMV, iris_vpu3x_4x_dec_comv_size }, {BUF_NON_COMV, iris_vpu_dec_non_comv_size }, {BUF_LINE, iris_vpu4x_dec_line_size }, {BUF_PERSIST, iris_vpu4x_dec_persist_size }, diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.c b/drivers/media/platform/qcom/iris/iris_vpu_common.c index 69e6126dc4d9..ab41da1f47c8 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_common.c +++ b/drivers/media/platform/qcom/iris/iris_vpu_common.c @@ -63,7 +63,7 @@ static void iris_vpu_setup_ucregion_memory_map(struct iris_core *core) writel(QTBL_ENABLE, core->reg_base + QTBL_INFO); if (core->sfr_daddr) { - value = (u32)core->sfr_daddr + core->iris_platform_data->core_arch; + value = (u32)core->sfr_daddr + core->iris_firmware_data->core_arch; writel(value, core->reg_base + SFR_ADDR); } @@ -149,7 +149,7 @@ int iris_vpu_prepare_pc(struct iris_core *core) if (!wfi_status || !idle_status) goto skip_power_off; - ret = core->hfi_ops->sys_pc_prep(core); + ret = core->hfi_sys_ops->sys_pc_prep(core); if (ret) goto skip_power_off; @@ -224,6 +224,7 @@ void iris_vpu_power_off_hw(struct iris_core *core) { dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN], false); iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]); + iris_disable_unprepare_clock(core, IRIS_BSE_HW_CLK); iris_disable_unprepare_clock(core, IRIS_HW_AHB_CLK); iris_disable_unprepare_clock(core, IRIS_HW_CLK); } @@ -292,8 +293,14 @@ int iris_vpu_power_on_hw(struct iris_core *core) if (ret && ret != -ENOENT) goto err_disable_hw_clock; + ret = iris_prepare_enable_clock(core, IRIS_BSE_HW_CLK); + if (ret && ret != -ENOENT) + goto err_disable_hw_ahb_clock; + return 0; +err_disable_hw_ahb_clock: + iris_disable_unprepare_clock(core, IRIS_HW_AHB_CLK); err_disable_hw_clock: iris_disable_unprepare_clock(core, IRIS_HW_CLK); err_disable_power: @@ -420,7 +427,7 @@ u64 iris_vpu3x_vpu4x_calculate_frequency(struct iris_inst *inst, size_t data_siz u32 height, width, mbs_per_second, mbpf; u64 fw_cycles, fw_vpp_cycles; u64 vsp_cycles, vpp_cycles; - u32 fps = DEFAULT_FPS; + u32 fps = inst->frame_rate; width = max(inp_f->fmt.pix_mp.width, inst->crop.width); height = max(inp_f->fmt.pix_mp.height, inst->crop.height); @@ -439,6 +446,10 @@ u64 iris_vpu3x_vpu4x_calculate_frequency(struct iris_inst *inst, size_t data_siz if (inst->fw_caps[PIPE].value > 1) vpp_cycles += div_u64(vpp_cycles * 59, 1000); + /* 1.05 is VPP FW overhead */ + if (inst->fw_caps[STAGE].value == STAGE_2) + vpp_cycles += div_u64(vpp_cycles * 5, 100); + vsp_cycles = fps * data_size * 8; vsp_cycles = div_u64(vsp_cycles, 2); /* VSP FW overhead 1.05 */ @@ -472,7 +483,7 @@ int iris_vpu_power_on(struct iris_core *core) iris_opp_set_rate(core->dev, freq); - core->iris_platform_data->set_preset_registers(core); + iris_vpu_set_preset_registers(core); iris_vpu_interrupt_init(core); core->intr_status = 0; @@ -489,3 +500,8 @@ err: return ret; } + +void iris_vpu_set_preset_registers(struct iris_core *core) +{ + writel(0x0, core->reg_base + 0xb0088); +} diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.h b/drivers/media/platform/qcom/iris/iris_vpu_common.h index dee3b1349c5e..09799a375c14 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_common.h +++ b/drivers/media/platform/qcom/iris/iris_vpu_common.h @@ -42,4 +42,6 @@ int iris_vpu35_vpu4x_power_on_controller(struct iris_core *core); void iris_vpu35_vpu4x_program_bootup_registers(struct iris_core *core); u64 iris_vpu3x_vpu4x_calculate_frequency(struct iris_inst *inst, size_t data_size); +void iris_vpu_set_preset_registers(struct iris_core *core); + #endif diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index 7e639760c41d..243e342b0ae7 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -18,6 +18,7 @@ #include <linux/types.h> #include <linux/pm_domain.h> #include <linux/pm_runtime.h> +#include <linux/videodev2.h> #include <media/videobuf2-v4l2.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-mem2mem.h> @@ -178,6 +179,8 @@ static void venus_sys_error_handler(struct work_struct *work) static u32 to_v4l2_codec_type(u32 codec) { switch (codec) { + case HFI_VIDEO_CODEC_HEVC: + return V4L2_PIX_FMT_HEVC; case HFI_VIDEO_CODEC_H264: return V4L2_PIX_FMT_H264; case HFI_VIDEO_CODEC_H263: @@ -684,6 +687,47 @@ static const struct venus_resources msm8916_res = { .vmem_addr = 0, .dma_mask = 0xddc00000 - 1, .fwname = "qcom/venus-1.8/venus.mbn", + .dec_codec_blacklist = HFI_VIDEO_CODEC_HEVC | HFI_VIDEO_CODEC_SPARK, + .enc_codec_blacklist = HFI_VIDEO_CODEC_HEVC, + .dec_nodename = "video-decoder", + .enc_nodename = "video-encoder", +}; + +static const struct freq_tbl msm8939_freq_table[] = { + { 489600, 266670000 }, /* 1080p @ 60 */ + { 244800, 133330000 }, /* 1080p @ 30 */ + { 220800, 133330000 }, /* 720p @ 60 */ + { 108000, 133330000 }, /* 720p @ 30 */ + { 72000, 133330000 }, /* VGA @ 60 */ + { 36000, 133330000 }, /* VGA @ 30 */ +}; + +static const struct reg_val msm8939_reg_preset[] = { + { 0xe0020, 0x0aaaaaaa }, + { 0xe0024, 0x0aaaaaaa }, + { 0x80124, 0x00000003 }, +}; + +static const struct venus_resources msm8939_res = { + .freq_tbl = msm8939_freq_table, + .freq_tbl_size = ARRAY_SIZE(msm8939_freq_table), + .reg_tbl = msm8939_reg_preset, + .reg_tbl_size = ARRAY_SIZE(msm8939_reg_preset), + .clks = { "core", "iface", "bus", }, + .clks_num = 3, + .vcodec_clks = { "vcodec0_core", "vcodec1_core" }, + .vcodec_clks_num = 2, + .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0", "vcodec1" }, + .vcodec_pmdomains_num = 3, + .max_load = 489600, /* 1080p@30 + 1080p@30 */ + .hfi_version = HFI_VERSION_1XX, + .vmem_id = VIDC_RESOURCE_NONE, + .vmem_size = 0, + .vmem_addr = 0, + .dma_mask = 0xddc00000 - 1, + .fwname = "qcom/venus-1.8/venus.mbn", + .dec_codec_blacklist = HFI_VIDEO_CODEC_SPARK, + .enc_codec_blacklist = HFI_VIDEO_CODEC_HEVC, .dec_nodename = "video-decoder", .enc_nodename = "video-encoder", }; @@ -882,6 +926,7 @@ static const struct venus_resources sdm845_res_v2 = { .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0", "vcodec1" }, .vcodec_pmdomains_num = 3, .opp_pmdomain = (const char *[]) { "cx" }, + .opp_pmdomain_num = 1, .vcodec_num = 2, .max_load = 3110400, /* 4096x2160@90 */ .hfi_version = HFI_VERSION_4XX, @@ -933,6 +978,7 @@ static const struct venus_resources sc7180_res = { .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" }, .vcodec_pmdomains_num = 2, .opp_pmdomain = (const char *[]) { "cx" }, + .opp_pmdomain_num = 1, .vcodec_num = 1, .hfi_version = HFI_VERSION_4XX, .vpu_version = VPU_VERSION_AR50, @@ -949,6 +995,7 @@ static const struct venus_resources sc7180_res = { .enc_nodename = "video-encoder", }; +#if (!IS_ENABLED(CONFIG_VIDEO_QCOM_IRIS)) static const struct freq_tbl sm8250_freq_table[] = { { 0, 444000000 }, { 0, 366000000 }, @@ -991,7 +1038,8 @@ static const struct venus_resources sm8250_res = { .vcodec_clks_num = 1, .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" }, .vcodec_pmdomains_num = 2, - .opp_pmdomain = (const char *[]) { "mx" }, + .opp_pmdomain = (const char *[]) { "mx", "mmcx" }, + .opp_pmdomain_num = 2, .vcodec_num = 1, .max_load = 7833600, .hfi_version = HFI_VERSION_6XX, @@ -1053,6 +1101,7 @@ static const struct venus_resources sc7280_res = { .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" }, .vcodec_pmdomains_num = 2, .opp_pmdomain = (const char *[]) { "cx" }, + .opp_pmdomain_num = 1, .vcodec_num = 1, .hfi_version = HFI_VERSION_6XX, .vpu_version = VPU_VERSION_IRIS2_1, @@ -1069,6 +1118,7 @@ static const struct venus_resources sc7280_res = { .dec_nodename = "video-decoder", .enc_nodename = "video-encoder", }; +#endif static const struct bw_tbl qcm2290_bw_table_dec[] = { { 352800, 597000, 0, 746000, 0 }, /* 1080p@30 + 720p@30 */ @@ -1100,6 +1150,7 @@ static const struct venus_resources qcm2290_res = { .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" }, .vcodec_pmdomains_num = 2, .opp_pmdomain = (const char *[]) { "cx" }, + .opp_pmdomain_num = 1, .vcodec_num = 1, .hfi_version = HFI_VERSION_4XX, .vpu_version = VPU_VERSION_AR50_LITE, @@ -1121,15 +1172,18 @@ static const struct venus_resources qcm2290_res = { static const struct of_device_id venus_dt_match[] = { { .compatible = "qcom,msm8916-venus", .data = &msm8916_res, }, + { .compatible = "qcom,msm8939-venus", .data = &msm8939_res, }, { .compatible = "qcom,msm8996-venus", .data = &msm8996_res, }, { .compatible = "qcom,msm8998-venus", .data = &msm8998_res, }, { .compatible = "qcom,qcm2290-venus", .data = &qcm2290_res, }, { .compatible = "qcom,sc7180-venus", .data = &sc7180_res, }, - { .compatible = "qcom,sc7280-venus", .data = &sc7280_res, }, { .compatible = "qcom,sdm660-venus", .data = &sdm660_res, }, { .compatible = "qcom,sdm845-venus", .data = &sdm845_res, }, { .compatible = "qcom,sdm845-venus-v2", .data = &sdm845_res_v2, }, +#if (!IS_ENABLED(CONFIG_VIDEO_QCOM_IRIS)) + { .compatible = "qcom,sc7280-venus", .data = &sc7280_res, }, { .compatible = "qcom,sm8250-venus", .data = &sm8250_res, }, +#endif { } }; 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 7506f5d0f609..46705a666776 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -54,8 +54,10 @@ enum vpu_version { VPU_VERSION_AR50, VPU_VERSION_AR50_LITE, VPU_VERSION_IRIS1, +#if (!IS_ENABLED(CONFIG_VIDEO_QCOM_IRIS)) VPU_VERSION_IRIS2, VPU_VERSION_IRIS2_1, +#endif }; struct firmware_version { @@ -77,13 +79,17 @@ struct venus_resources { const struct hfi_ubwc_config *ubwc_conf; const char * const clks[VIDC_CLKS_NUM_MAX]; unsigned int clks_num; + const char * const vcodec_clks[VIDC_VCODEC_CLKS_NUM_MAX]; const char * const vcodec0_clks[VIDC_VCODEC_CLKS_NUM_MAX]; const char * const vcodec1_clks[VIDC_VCODEC_CLKS_NUM_MAX]; unsigned int vcodec_clks_num; const char **vcodec_pmdomains; unsigned int vcodec_pmdomains_num; const char **opp_pmdomain; + unsigned int opp_pmdomain_num; unsigned int vcodec_num; + const u32 dec_codec_blacklist; + const u32 enc_codec_blacklist; const char * const resets[VIDC_RESETS_NUM_MAX]; unsigned int resets_num; enum hfi_version hfi_version; @@ -140,6 +146,7 @@ struct venus_format { * @aon_base: AON base address * @irq: Venus irq * @clks: an array of struct clk pointers + * @vcodec_clks: an array of vcodec struct clk pointers * @vcodec0_clks: an array of vcodec0 struct clk pointers * @vcodec1_clks: an array of vcodec1 struct clk pointers * @video_path: an interconnect handle to video to/from memory path @@ -194,6 +201,7 @@ struct venus_core { void __iomem *aon_base; int irq; struct clk *clks[VIDC_CLKS_NUM_MAX]; + struct clk *vcodec_clks[VIDC_VCODEC_CLKS_NUM_MAX]; struct clk *vcodec0_clks[VIDC_VCODEC_CLKS_NUM_MAX]; struct clk *vcodec1_clks[VIDC_VCODEC_CLKS_NUM_MAX]; struct icc_path *video_path; @@ -525,13 +533,22 @@ struct venus_inst { #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) +#if (!IS_ENABLED(CONFIG_VIDEO_QCOM_IRIS)) #define IS_V6(core) ((core)->res->hfi_version == HFI_VERSION_6XX) +#else +#define IS_V6(core) (((void)(core), 0)) +#endif #define IS_AR50(core) ((core)->res->vpu_version == VPU_VERSION_AR50) #define IS_AR50_LITE(core) ((core)->res->vpu_version == VPU_VERSION_AR50_LITE) #define IS_IRIS1(core) ((core)->res->vpu_version == VPU_VERSION_IRIS1) +#if (!IS_ENABLED(CONFIG_VIDEO_QCOM_IRIS)) #define IS_IRIS2(core) ((core)->res->vpu_version == VPU_VERSION_IRIS2) #define IS_IRIS2_1(core) ((core)->res->vpu_version == VPU_VERSION_IRIS2_1) +#else +#define IS_IRIS2(core) (((void)(core), 0)) +#define IS_IRIS2_1(core) (((void)(core), 0)) +#endif static inline bool is_lite(struct venus_core *core) { diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 747c388fe25f..59eee3dd9e06 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -954,8 +954,8 @@ static u32 get_framesize_raw_nv12(u32 width, u32 height) 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; + uv_plane = uv_stride * uv_sclines; + size = y_plane + uv_plane; return ALIGN(size, SZ_4K); } diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c index 92765f9c8873..b1657443f23f 100644 --- a/drivers/media/platform/qcom/venus/hfi_parser.c +++ b/drivers/media/platform/qcom/venus/hfi_parser.c @@ -206,11 +206,11 @@ static int parse_codecs(struct venus_core *core, void *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; - core->enc_codecs &= ~HFI_VIDEO_CODEC_HEVC; - } + if (core->res->dec_codec_blacklist) + core->dec_codecs &= ~core->res->dec_codec_blacklist; + + if (core->res->enc_codec_blacklist) + core->enc_codecs &= ~core->res->enc_codec_blacklist; return sizeof(*codecs); } @@ -268,7 +268,6 @@ static int hfi_platform_parser(struct venus_core *core, struct venus_inst *inst) const struct hfi_plat_caps *caps = NULL; u32 enc_codecs, dec_codecs, count = 0; unsigned int entries; - int ret; plat = hfi_platform_get(core->res->hfi_version); if (!plat) @@ -277,9 +276,8 @@ static int hfi_platform_parser(struct venus_core *core, struct venus_inst *inst) if (inst) return 0; - ret = hfi_platform_get_codecs(core, &enc_codecs, &dec_codecs, &count); - if (ret) - return ret; + if (plat->codecs) + plat->codecs(core, &enc_codecs, &dec_codecs, &count); if (plat->capabilities) caps = plat->capabilities(core, &entries); diff --git a/drivers/media/platform/qcom/venus/hfi_platform.c b/drivers/media/platform/qcom/venus/hfi_platform.c index cde7f93045ac..f19572ab1d16 100644 --- a/drivers/media/platform/qcom/venus/hfi_platform.c +++ b/drivers/media/platform/qcom/venus/hfi_platform.c @@ -2,9 +2,7 @@ /* * Copyright (c) 2020, The Linux Foundation. All rights reserved. */ -#include <linux/of.h> #include "hfi_platform.h" -#include "core.h" const struct hfi_platform *hfi_platform_get(enum hfi_version version) { @@ -73,25 +71,3 @@ hfi_platform_get_codec_lp_freq(struct venus_core *core, return freq; } - -int -hfi_platform_get_codecs(struct venus_core *core, u32 *enc_codecs, - u32 *dec_codecs, u32 *count) -{ - const struct hfi_platform *plat; - - plat = hfi_platform_get(core->res->hfi_version); - if (!plat) - return -EINVAL; - - if (plat->codecs) - plat->codecs(core, enc_codecs, dec_codecs, count); - - if (IS_IRIS2_1(core)) { - *enc_codecs &= ~HFI_VIDEO_CODEC_VP8; - *dec_codecs &= ~HFI_VIDEO_CODEC_VP8; - } - - return 0; -} - diff --git a/drivers/media/platform/qcom/venus/hfi_platform.h b/drivers/media/platform/qcom/venus/hfi_platform.h index 5e4f8013a6b1..a0b6d19f3e1a 100644 --- a/drivers/media/platform/qcom/venus/hfi_platform.h +++ b/drivers/media/platform/qcom/venus/hfi_platform.h @@ -74,6 +74,4 @@ unsigned long hfi_platform_get_codec_vsp_freq(struct venus_core *core, unsigned long hfi_platform_get_codec_lp_freq(struct venus_core *core, enum hfi_version version, u32 codec, u32 session_type); -int hfi_platform_get_codecs(struct venus_core *core, u32 *enc_codecs, - u32 *dec_codecs, u32 *count); #endif diff --git a/drivers/media/platform/qcom/venus/hfi_platform_v4.c b/drivers/media/platform/qcom/venus/hfi_platform_v4.c index cda888b56b5d..e0b3652bb440 100644 --- a/drivers/media/platform/qcom/venus/hfi_platform_v4.c +++ b/drivers/media/platform/qcom/venus/hfi_platform_v4.c @@ -136,8 +136,8 @@ static const struct hfi_plat_caps caps[] = { .codec = HFI_VIDEO_CODEC_H264, .domain = VIDC_SESSION_TYPE_ENC, .cap_bufs_mode_dynamic = true, - .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16}, - .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16}, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1}, .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, @@ -173,8 +173,8 @@ static const struct hfi_plat_caps caps[] = { .codec = HFI_VIDEO_CODEC_HEVC, .domain = VIDC_SESSION_TYPE_ENC, .cap_bufs_mode_dynamic = true, - .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16}, - .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16}, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1}, .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, @@ -195,8 +195,8 @@ static const struct hfi_plat_caps caps[] = { .caps[19] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1}, .caps[20] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1}, .caps[21] = {HFI_CAPABILITY_ROTATION, 1, 4, 90}, - .caps[22] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16}, - .caps[23] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16}, + .caps[22] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 1}, + .caps[23] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 1}, .num_caps = 24, .pl[0] = {HFI_HEVC_PROFILE_MAIN, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0}, .pl[1] = {HFI_HEVC_PROFILE_MAIN10, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0}, @@ -210,8 +210,8 @@ static const struct hfi_plat_caps caps[] = { .codec = HFI_VIDEO_CODEC_VP8, .domain = VIDC_SESSION_TYPE_ENC, .cap_bufs_mode_dynamic = true, - .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16}, - .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16}, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1}, .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, @@ -229,8 +229,8 @@ static const struct hfi_plat_caps caps[] = { .caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 127, 1}, .caps[17] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 2, 1}, .caps[18] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1}, - .caps[19] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16}, - .caps[20] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16}, + .caps[19] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 1}, + .caps[20] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 1}, .caps[21] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1}, .caps[22] = {HFI_CAPABILITY_ROTATION, 1, 4, 90}, .num_caps = 23, diff --git a/drivers/media/platform/qcom/venus/hfi_platform_v6.c b/drivers/media/platform/qcom/venus/hfi_platform_v6.c index d8568c08cc36..fb8d10ab3404 100644 --- a/drivers/media/platform/qcom/venus/hfi_platform_v6.c +++ b/drivers/media/platform/qcom/venus/hfi_platform_v6.c @@ -173,8 +173,8 @@ static const struct hfi_plat_caps caps[] = { .codec = HFI_VIDEO_CODEC_HEVC, .domain = VIDC_SESSION_TYPE_ENC, .cap_bufs_mode_dynamic = true, - .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 8192, 16}, - .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 8192, 16}, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 8192, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 8192, 1}, .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 64, 138240, 1}, .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 160000000, 1}, .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, @@ -195,8 +195,8 @@ static const struct hfi_plat_caps caps[] = { .caps[19] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1}, .caps[20] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1}, .caps[21] = {HFI_CAPABILITY_ROTATION, 1, 4, 90}, - .caps[22] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16}, - .caps[23] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16}, + .caps[22] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 1}, + .caps[23] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 1}, .num_caps = 24, .pl[0] = {HFI_HEVC_PROFILE_MAIN, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0}, .pl[1] = {HFI_HEVC_PROFILE_MAIN10, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0}, @@ -210,8 +210,8 @@ static const struct hfi_plat_caps caps[] = { .codec = HFI_VIDEO_CODEC_VP8, .domain = VIDC_SESSION_TYPE_ENC, .cap_bufs_mode_dynamic = true, - .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 4096, 16}, - .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 4096, 16}, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 4096, 1}, .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 64, 36864, 1}, .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 74000000, 1}, .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, @@ -229,8 +229,8 @@ static const struct hfi_plat_caps caps[] = { .caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 127, 1}, .caps[17] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 2, 1}, .caps[18] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1}, - .caps[19] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16}, - .caps[20] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16}, + .caps[19] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 1}, + .caps[20] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 1}, .caps[21] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1}, .caps[22] = {HFI_CAPABILITY_ROTATION, 1, 4, 90}, .num_caps = 23, diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c index f0269524ac70..be1cbd5cfe84 100644 --- a/drivers/media/platform/qcom/venus/pm_helpers.c +++ b/drivers/media/platform/qcom/venus/pm_helpers.c @@ -89,12 +89,23 @@ static void core_clks_disable(struct venus_core *core) static int core_clks_set_rate(struct venus_core *core, unsigned long freq) { - int ret; + const struct venus_resources *res = core->res; + int ret, i; ret = dev_pm_opp_set_rate(core->dev, freq); if (ret) return ret; + if (!res->vcodec_clks_num) + goto set_rates; + + for (i = 0; i < res->vcodec_clks_num; i++) { + ret = clk_set_rate(core->vcodec_clks[i], freq); + if (ret) + return ret; + } + +set_rates: ret = clk_set_rate(core->vcodec0_clks[0], freq); if (ret) return ret; @@ -297,10 +308,33 @@ exit: return ret; } +static int vcodec_domains_get_v1(struct venus_core *core) +{ + struct device *dev = core->dev; + const struct venus_resources *res = core->res; + const struct dev_pm_domain_attach_data vcodec_data = { + .pd_names = res->vcodec_pmdomains, + .num_pd_names = res->vcodec_pmdomains_num, + .pd_flags = PD_FLAG_NO_DEV_LINK, + }; + + if (!res->vcodec_pmdomains) + return 0; + + return devm_pm_domain_attach_list(dev, &vcodec_data, + &core->pmdomains); +} + static int core_get_v1(struct venus_core *core) { + const struct venus_resources *res = core->res; + struct device *dev = core->dev; int ret; + ret = vcodec_domains_get_v1(core); + if (ret < 0) + return ret; + ret = core_clks_get(core); if (ret) return ret; @@ -309,9 +343,79 @@ static int core_get_v1(struct venus_core *core) if (ret) return ret; + if (!res->vcodec_pmdomains) + return 0; + + ret = vcodec_clks_get(core, dev, core->vcodec_clks, + res->vcodec_clks); + if (ret) + return ret; + return 0; } +static int vcodec_domains_enable(struct venus_core *core) +{ + const struct venus_resources *res = core->res; + struct device *pd_dev; + int i = 0, ret; + + if (!res->vcodec_pmdomains) + return 0; + + for (; i < res->vcodec_pmdomains_num; i++) { + pd_dev = core->pmdomains->pd_devs[i]; + ret = pm_runtime_resume_and_get(pd_dev); + if (ret) + goto err; + } + + return 0; +err: + while (i--) { + pd_dev = core->pmdomains->pd_devs[i]; + pm_runtime_put_sync(pd_dev); + } + return ret; +} + +static void vcodec_domains_disable(struct venus_core *core) +{ + const struct venus_resources *res = core->res; + struct device *pd_dev; + int i = res->vcodec_pmdomains_num; + + if (!res->vcodec_pmdomains) + return; + + while (i--) { + pd_dev = core->pmdomains->pd_devs[i]; + pm_runtime_put_sync(pd_dev); + } +} + +static int vcodec_domains_set_hw(struct venus_core *core, bool is_hw) +{ + const struct venus_resources *res = core->res; + struct device *pd_dev; + int i = 0, ret; + + for (; i < res->vcodec_pmdomains_num; i++) { + pd_dev = core->pmdomains->pd_devs[i]; + ret = dev_pm_genpd_set_hwmode(pd_dev, is_hw); + if (ret && ret != -EOPNOTSUPP) + goto err; + } + + return 0; +err: + while (i--) { + pd_dev = core->pmdomains->pd_devs[i]; + dev_pm_genpd_set_hwmode(pd_dev, !is_hw); + } + return ret; +} + static void core_put_v1(struct venus_core *core) { } @@ -320,11 +424,43 @@ static int core_power_v1(struct venus_core *core, int on) { int ret = 0; - if (on == POWER_ON) + if (on == POWER_ON) { + ret = vcodec_domains_enable(core); + if (ret) + return ret; + ret = core_clks_enable(core); - else + if (ret) + goto fail_pmdomains; + + if (!core->res->vcodec_pmdomains) + return 0; + + ret = vcodec_clks_enable(core, core->vcodec_clks); + if (ret) + goto fail_core_clks; + + ret = vcodec_domains_set_hw(core, true); + if (ret) + goto fail_vcodec_clks; + + } else { + if (core->res->vcodec_pmdomains) { + vcodec_domains_set_hw(core, false); + vcodec_clks_disable(core, core->vcodec_clks); + } core_clks_disable(core); + vcodec_domains_disable(core); + } + return 0; + +fail_vcodec_clks: + vcodec_clks_disable(core, core->vcodec_clks); +fail_core_clks: + core_clks_disable(core); +fail_pmdomains: + vcodec_domains_disable(core); return ret; } @@ -875,7 +1011,7 @@ static int venc_power_v4(struct device *dev, int on) return ret; } -static int vcodec_domains_get(struct venus_core *core) +static int vcodec_domains_get_v4(struct venus_core *core) { int ret; struct device *dev = core->dev; @@ -887,7 +1023,7 @@ static int vcodec_domains_get(struct venus_core *core) }; struct dev_pm_domain_attach_data opp_pd_data = { .pd_names = res->opp_pmdomain, - .num_pd_names = 1, + .num_pd_names = res->opp_pmdomain_num, .pd_flags = PD_FLAG_DEV_LINK_ON | PD_FLAG_REQUIRED_OPP, }; @@ -904,6 +1040,12 @@ skip_pmdomains: /* Attach the power domain for setting performance state */ ret = devm_pm_domain_attach_list(dev, &opp_pd_data, &core->opp_pmdomain); + /* backwards compatibility for incomplete ABI SM8250 */ + if (ret == -ENODEV && + of_device_is_compatible(dev->of_node, "qcom,sm8250-venus")) { + opp_pd_data.num_pd_names--; + ret = devm_pm_domain_attach_list(dev, &opp_pd_data, &core->opp_pmdomain); + } if (ret < 0) return ret; @@ -993,7 +1135,7 @@ static int core_get_v4(struct venus_core *core) if (ret) return ret; - ret = vcodec_domains_get(core); + ret = vcodec_domains_get_v4(core); if (ret) return ret; diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c index f9af9177e02f..73cda0e2d45a 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c @@ -1494,7 +1494,6 @@ int rvin_dma_register(struct rvin_dev *vin, int irq) q->ops = &rvin_qops; q->mem_ops = &vb2_dma_contig_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_queued_buffers = 4; q->dev = vin->dev; ret = vb2_queue_init(q); diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h index a5a57369ef0e..23cb50ee8e57 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h @@ -14,12 +14,11 @@ #define CRUnIE_EFE BIT(17) -#define CRUnIE2_FSxE(x) BIT(((x) * 3)) #define CRUnIE2_FExE(x) BIT(((x) * 3) + 1) #define CRUnINTS_SFS BIT(16) -#define CRUnINTS2_FSxS(x) BIT(((x) * 3)) +#define CRUnINTS2_FExS(x) BIT(((x) * 3) + 1) #define CRUnRST_VRESETN BIT(0) @@ -60,6 +59,7 @@ #define ICnMC_CSCTHR BIT(5) #define ICnMC_INF(x) ((x) << 16) #define ICnMC_VCSEL(x) ((x) << 22) +#define ICnMC_VCSEL_MASK GENMASK(23, 22) #define ICnMC_INF_MASK GENMASK(21, 16) #define ICnMS_IA BIT(2) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h index 3a200db15730..5bf334e173d2 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h @@ -9,7 +9,9 @@ #define __RZG2L_CRU__ #include <linux/irqreturn.h> +#include <linux/mutex.h> #include <linux/reset.h> +#include <linux/spinlock.h> #include <media/v4l2-async.h> #include <media/v4l2-dev.h> @@ -36,20 +38,6 @@ enum rzg2l_csi2_pads { struct rzg2l_cru_dev; -/** - * enum rzg2l_cru_dma_state - DMA states - * @RZG2L_CRU_DMA_STOPPED: No operation in progress - * @RZG2L_CRU_DMA_STARTING: Capture starting up - * @RZG2L_CRU_DMA_RUNNING: Operation in progress have buffers - * @RZG2L_CRU_DMA_STOPPING: Stopping operation - */ -enum rzg2l_cru_dma_state { - RZG2L_CRU_DMA_STOPPED = 0, - RZG2L_CRU_DMA_STARTING, - RZG2L_CRU_DMA_RUNNING, - RZG2L_CRU_DMA_STOPPING, -}; - struct rzg2l_cru_csi { struct v4l2_async_connection *asd; struct v4l2_subdev *subdev; @@ -109,7 +97,6 @@ struct rzg2l_cru_info { * @v4l2_dev: V4L2 device * @num_buf: Holds the current number of buffers enabled * @svc_channel: SVC0/1/2/3 to use for RZ/G3E - * @buf_addr: Memory addresses where current video data is written. * @notifier: V4L2 asynchronous subdevs notifier * * @ip: Image processing subdev info @@ -118,6 +105,11 @@ struct rzg2l_cru_info { * @mdev_lock: protects the count, notifier and csi members * @pad: media pad for the video device entity * + * @hw_lock: protects the @active_slot counter, hardware programming + * of slot addresses and the @buf_addr[] list + * @buf_addr: Memory addresses where current video data is written + * @active_slot: The slot in use + * * @lock: protects @queue * @queue: vb2 buffers queue * @scratch: cpu address for scratch buffer @@ -147,8 +139,6 @@ struct rzg2l_cru_dev { u8 num_buf; u8 svc_channel; - dma_addr_t buf_addr[RZG2L_CRU_HW_BUFFER_DEFAULT]; - struct v4l2_async_notifier notifier; struct rzg2l_cru_ip ip; @@ -157,6 +147,10 @@ struct rzg2l_cru_dev { struct mutex mdev_lock; struct media_pad pad; + spinlock_t hw_lock; + dma_addr_t buf_addr[RZG2L_CRU_HW_BUFFER_DEFAULT]; + unsigned int active_slot; + struct mutex lock; struct vb2_queue queue; void *scratch; @@ -166,7 +160,6 @@ struct rzg2l_cru_dev { struct vb2_v4l2_buffer *queue_buf[RZG2L_CRU_HW_BUFFER_MAX]; struct list_head buf_list; unsigned int sequence; - enum rzg2l_cru_dma_state state; struct v4l2_pix_format format; }; diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index 162e2ace6931..5185a547461d 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -42,6 +42,24 @@ struct rzg2l_cru_buffer { #define to_buf_list(vb2_buffer) \ (&container_of(vb2_buffer, struct rzg2l_cru_buffer, vb)->list) +/* + * The CRU hardware cycles over its slots when transferring frames. All drivers + * structure that contains programming data for the slots, such as the memory + * destination addresses have to be iterated as they were circular buffers. + * + * Provide here utilities to iterate over slots and the associated data. + */ +static inline unsigned int rzg2l_cru_slot_next(struct rzg2l_cru_dev *cru, + unsigned int slot) +{ + return (slot + 1) % cru->num_buf; +} + +/* Start cycling on cru slots from the one after 'start'. */ +#define for_each_cru_slot_from(cru, slot, start) \ + for ((slot) = rzg2l_cru_slot_next((cru), (start)); \ + (slot) != (start); (slot) = rzg2l_cru_slot_next((cru), (slot))) + /* ----------------------------------------------------------------------------- * DMA operations */ @@ -105,28 +123,36 @@ __rzg2l_cru_read_constant(struct rzg2l_cru_dev *cru, u32 offset) __rzg2l_cru_read_constant(cru, offset) : \ __rzg2l_cru_read(cru, offset)) -/* Need to hold qlock before calling */ -static void return_unused_buffers(struct rzg2l_cru_dev *cru, - enum vb2_buffer_state state) +static void rzg2l_cru_return_buffers(struct rzg2l_cru_dev *cru, + enum vb2_buffer_state state) { struct rzg2l_cru_buffer *buf, *node; - unsigned long flags; - unsigned int i; - spin_lock_irqsave(&cru->qlock, flags); - for (i = 0; i < cru->num_buf; i++) { - if (cru->queue_buf[i]) { - vb2_buffer_done(&cru->queue_buf[i]->vb2_buf, - state); - cru->queue_buf[i] = NULL; + scoped_guard(spinlock_irq, &cru->hw_lock) { + /* Return the buffer in progress first, if not completed yet. */ + unsigned int slot = cru->active_slot; + + if (cru->queue_buf[slot]) { + vb2_buffer_done(&cru->queue_buf[slot]->vb2_buf, state); + cru->queue_buf[slot] = NULL; + } + + /* Return all the pending buffers after the active one. */ + for_each_cru_slot_from(cru, slot, cru->active_slot) { + if (!cru->queue_buf[slot]) + continue; + + vb2_buffer_done(&cru->queue_buf[slot]->vb2_buf, state); + cru->queue_buf[slot] = NULL; } } + guard(spinlock_irq)(&cru->qlock); + list_for_each_entry_safe(buf, node, &cru->buf_list, list) { vb2_buffer_done(&buf->vb.vb2_buf, state); list_del(&buf->list); } - spin_unlock_irqrestore(&cru->qlock, flags); } static int rzg2l_cru_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, @@ -165,13 +191,9 @@ static void rzg2l_cru_buffer_queue(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct rzg2l_cru_dev *cru = vb2_get_drv_priv(vb->vb2_queue); - unsigned long flags; - - spin_lock_irqsave(&cru->qlock, flags); + guard(spinlock_irq)(&cru->qlock); list_add_tail(to_buf_list(vbuf), &cru->buf_list); - - spin_unlock_irqrestore(&cru->qlock, flags); } static void rzg2l_cru_set_slot_addr(struct rzg2l_cru_dev *cru, @@ -192,45 +214,52 @@ static void rzg2l_cru_set_slot_addr(struct rzg2l_cru_dev *cru, } /* - * Moves a buffer from the queue to the HW slot. If no buffer is - * available use the scratch buffer. The scratch buffer is never - * returned to userspace, its only function is to enable the capture - * loop to keep running. + * Move as many buffers as possible from the queue to HW slots If no buffer is + * available use the scratch buffer. The scratch buffer is never returned to + * userspace, its only function is to enable the capture loop to keep running. + * + * @cru: the CRU device + * @slot: the slot that has just completed */ static void rzg2l_cru_fill_hw_slot(struct rzg2l_cru_dev *cru, int slot) { - struct vb2_v4l2_buffer *vbuf; struct rzg2l_cru_buffer *buf; + struct vb2_v4l2_buffer *vbuf; + unsigned int next_slot; dma_addr_t phys_addr; - /* A already populated slot shall never be overwritten. */ - if (WARN_ON(cru->queue_buf[slot])) - return; + lockdep_assert_held(&cru->hw_lock); - dev_dbg(cru->dev, "Filling HW slot: %d\n", slot); + /* Find the next slot which hasn't a valid address programmed. */ + for_each_cru_slot_from(cru, next_slot, slot) { + if (cru->queue_buf[next_slot]) + continue; - if (list_empty(&cru->buf_list)) { - cru->queue_buf[slot] = NULL; - phys_addr = cru->scratch_phys; - } else { - /* Keep track of buffer we give to HW */ - buf = list_entry(cru->buf_list.next, - struct rzg2l_cru_buffer, list); - vbuf = &buf->vb; - list_del_init(to_buf_list(vbuf)); - cru->queue_buf[slot] = vbuf; + scoped_guard(spinlock_irqsave, &cru->qlock) { + buf = list_first_entry_or_null(&cru->buf_list, + struct rzg2l_cru_buffer, list); + if (buf) + list_del_init(&buf->list); + } - /* Setup DMA */ + if (!buf) { + /* Direct frames to the scratch buffer. */ + phys_addr = cru->scratch_phys; + cru->queue_buf[next_slot] = NULL; + rzg2l_cru_set_slot_addr(cru, next_slot, phys_addr); + return; + } + + vbuf = &buf->vb; + cru->queue_buf[next_slot] = vbuf; phys_addr = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); + rzg2l_cru_set_slot_addr(cru, next_slot, phys_addr); } - - rzg2l_cru_set_slot_addr(cru, slot, phys_addr); } static void rzg2l_cru_initialize_axi(struct rzg2l_cru_dev *cru) { const struct rzg2l_cru_info *info = cru->info; - unsigned int slot; u32 amnaxiattr; /* @@ -239,8 +268,14 @@ static void rzg2l_cru_initialize_axi(struct rzg2l_cru_dev *cru) */ rzg2l_cru_write(cru, AMnMBVALID, AMnMBVALID_MBVALID(cru->num_buf - 1)); - for (slot = 0; slot < cru->num_buf; slot++) - rzg2l_cru_fill_hw_slot(cru, slot); + /* + * Program slot#0 with the first available buffer, if any. Pass to the + * function 'num_buf - 1' as rzg2l_cru_fill_hw_slot() calculates which + * is the next slot to program. + */ + scoped_guard(spinlock_irq, &cru->hw_lock) { + rzg2l_cru_fill_hw_slot(cru, cru->num_buf - 1); + } if (info->has_stride) { u32 stride = cru->format.bytesperline; @@ -262,19 +297,24 @@ static void rzg2l_cru_csi2_setup(struct rzg2l_cru_dev *cru, u8 csi_vc) { const struct rzg2l_cru_info *info = cru->info; - u32 icnmc = ICnMC_INF(ip_fmt->datatype); + u32 icnmc = rzg2l_cru_read(cru, info->image_conv) & ~(ICnMC_INF_MASK | + ICnMC_VCSEL_MASK); + icnmc |= ICnMC_INF(ip_fmt->datatype); + /* + * VC filtering goes through SVC register on G3E/V2H. + * + * FIXME: virtual channel filtering is likely broken and only VC=0 + * works. + */ if (cru->info->regs[ICnSVC]) { rzg2l_cru_write(cru, ICnSVCNUM, csi_vc); rzg2l_cru_write(cru, ICnSVC, ICnSVC_SVC0(0) | ICnSVC_SVC1(1) | ICnSVC_SVC2(2) | ICnSVC_SVC3(3)); + } else { + icnmc |= ICnMC_VCSEL(csi_vc); } - icnmc |= rzg2l_cru_read(cru, info->image_conv) & ~ICnMC_INF_MASK; - - /* Set virtual channel CSI2 */ - icnmc |= ICnMC_VCSEL(csi_vc); - rzg2l_cru_write(cru, info->image_conv, icnmc); } @@ -340,30 +380,24 @@ bool rzg2l_fifo_empty(struct rzg2l_cru_dev *cru) void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru) { unsigned int retries = 0; - unsigned long flags; u32 icnms; - spin_lock_irqsave(&cru->qlock, flags); - - /* Disable and clear the interrupt */ - cru->info->disable_interrupts(cru); + scoped_guard(spinlock_irq, &cru->hw_lock) { + /* Disable and clear the interrupt */ + cru->info->disable_interrupts(cru); + } /* Stop the operation of image conversion */ rzg2l_cru_write(cru, ICnEN, 0); /* Wait for streaming to stop */ - while ((rzg2l_cru_read(cru, ICnMS) & ICnMS_IA) && retries++ < RZG2L_RETRIES) { - spin_unlock_irqrestore(&cru->qlock, flags); + while ((rzg2l_cru_read(cru, ICnMS) & ICnMS_IA) && retries++ < RZG2L_RETRIES) msleep(RZG2L_TIMEOUT_MS); - spin_lock_irqsave(&cru->qlock, flags); - } icnms = rzg2l_cru_read(cru, ICnMS) & ICnMS_IA; if (icnms) dev_err(cru->dev, "Failed stop HW, something is seriously broken\n"); - cru->state = RZG2L_CRU_DMA_STOPPED; - /* Wait until the FIFO becomes empty */ for (retries = 5; retries > 0; retries--) { if (cru->info->fifo_empty(cru)) @@ -400,8 +434,6 @@ void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru) /* Resets the image processing module */ rzg2l_cru_write(cru, CRUnRST, 0); - - spin_unlock_irqrestore(&cru->qlock, flags); } static int rzg2l_cru_get_virtual_channel(struct rzg2l_cru_dev *cru) @@ -435,7 +467,6 @@ static int rzg2l_cru_get_virtual_channel(struct rzg2l_cru_dev *cru) void rzg3e_cru_enable_interrupts(struct rzg2l_cru_dev *cru) { - rzg2l_cru_write(cru, CRUnIE2, CRUnIE2_FSxE(cru->svc_channel)); rzg2l_cru_write(cru, CRUnIE2, CRUnIE2_FExE(cru->svc_channel)); } @@ -461,7 +492,6 @@ void rzg2l_cru_disable_interrupts(struct rzg2l_cru_dev *cru) int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru) { struct v4l2_mbus_framefmt *fmt = rzg2l_cru_ip_get_src_fmt(cru); - unsigned long flags; u8 csi_vc; int ret; @@ -471,8 +501,6 @@ int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru) csi_vc = ret; cru->svc_channel = csi_vc; - spin_lock_irqsave(&cru->qlock, flags); - /* Select a video input */ rzg2l_cru_write(cru, CRUnCTRL, CRUnCTRL_VINSEL(0)); @@ -488,7 +516,6 @@ int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru) /* Initialize image convert */ ret = rzg2l_cru_initialize_image_conv(cru, fmt, csi_vc); if (ret) { - spin_unlock_irqrestore(&cru->qlock, flags); return ret; } @@ -498,8 +525,6 @@ int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru) /* Enable image processing reception */ rzg2l_cru_write(cru, ICnEN, ICnEN_ICEN); - spin_unlock_irqrestore(&cru->qlock, flags); - return 0; } @@ -560,69 +585,36 @@ pipe_line_stop: static void rzg2l_cru_stop_streaming(struct rzg2l_cru_dev *cru) { - cru->state = RZG2L_CRU_DMA_STOPPING; - rzg2l_cru_set_stream(cru, 0); } irqreturn_t rzg2l_cru_irq(int irq, void *data) { struct rzg2l_cru_dev *cru = data; - unsigned int handled = 0; - unsigned long flags; u32 irq_status; u32 amnmbs; int slot; - spin_lock_irqsave(&cru->qlock, flags); - irq_status = rzg2l_cru_read(cru, CRUnINTS); if (!irq_status) - goto done; - - handled = 1; + return IRQ_NONE; rzg2l_cru_write(cru, CRUnINTS, rzg2l_cru_read(cru, CRUnINTS)); - /* Nothing to do if capture status is 'RZG2L_CRU_DMA_STOPPED' */ - if (cru->state == RZG2L_CRU_DMA_STOPPED) { - dev_dbg(cru->dev, "IRQ while state stopped\n"); - goto done; - } - - /* Increase stop retries if capture status is 'RZG2L_CRU_DMA_STOPPING' */ - if (cru->state == RZG2L_CRU_DMA_STOPPING) { - if (irq_status & CRUnINTS_SFS) - dev_dbg(cru->dev, "IRQ while state stopping\n"); - goto done; - } + /* Calculate slot and prepare for new capture. */ + guard(spinlock_irqsave)(&cru->hw_lock); - /* Prepare for capture and update state */ amnmbs = rzg2l_cru_read(cru, AMnMBS); - slot = amnmbs & AMnMBS_MBSTS; + cru->active_slot = amnmbs & AMnMBS_MBSTS; /* * AMnMBS.MBSTS indicates the destination of Memory Bank (MB). * Recalculate to get the current transfer complete MB. */ - if (slot == 0) + if (cru->active_slot == 0) slot = cru->num_buf - 1; else - slot--; - - /* - * To hand buffers back in a known order to userspace start - * to capture first from slot 0. - */ - if (cru->state == RZG2L_CRU_DMA_STARTING) { - if (slot != 0) { - dev_dbg(cru->dev, "Starting sync slot: %d\n", slot); - goto done; - } - - dev_dbg(cru->dev, "Capture start synced!\n"); - cru->state = RZG2L_CRU_DMA_RUNNING; - } + slot = cru->active_slot - 1; /* Capture frame */ if (cru->queue_buf[slot]) { @@ -632,9 +624,6 @@ irqreturn_t rzg2l_cru_irq(int irq, void *data) vb2_buffer_done(&cru->queue_buf[slot]->vb2_buf, VB2_BUF_STATE_DONE); cru->queue_buf[slot] = NULL; - } else { - /* Scratch buffer was used, dropping frame. */ - dev_dbg(cru->dev, "Dropping frame %u\n", cru->sequence); } cru->sequence++; @@ -642,35 +631,7 @@ irqreturn_t rzg2l_cru_irq(int irq, void *data) /* Prepare for next frame */ rzg2l_cru_fill_hw_slot(cru, slot); -done: - spin_unlock_irqrestore(&cru->qlock, flags); - - return IRQ_RETVAL(handled); -} - -static int rzg3e_cru_get_current_slot(struct rzg2l_cru_dev *cru) -{ - u64 amnmadrs; - int slot; - - /* - * When AMnMADRSL is read, AMnMADRSH of the higher-order - * address also latches the address. - * - * AMnMADRSH must be read after AMnMADRSL has been read. - */ - amnmadrs = rzg2l_cru_read(cru, AMnMADRSL); - amnmadrs |= (u64)rzg2l_cru_read(cru, AMnMADRSH) << 32; - - /* Ensure amnmadrs is within this buffer range */ - for (slot = 0; slot < cru->num_buf; slot++) { - if (amnmadrs >= cru->buf_addr[slot] && - amnmadrs < cru->buf_addr[slot] + cru->format.sizeimage) - return slot; - } - - dev_err(cru->dev, "Invalid MB address 0x%llx (out of range)\n", amnmadrs); - return -EINVAL; + return IRQ_HANDLED; } irqreturn_t rzg3e_cru_irq(int irq, void *data) @@ -679,69 +640,31 @@ irqreturn_t rzg3e_cru_irq(int irq, void *data) u32 irq_status; int slot; - scoped_guard(spinlock, &cru->qlock) { - irq_status = rzg2l_cru_read(cru, CRUnINTS2); - if (!irq_status) - return IRQ_NONE; - - dev_dbg(cru->dev, "CRUnINTS2 0x%x\n", irq_status); - - rzg2l_cru_write(cru, CRUnINTS2, rzg2l_cru_read(cru, CRUnINTS2)); - - /* Nothing to do if capture status is 'RZG2L_CRU_DMA_STOPPED' */ - if (cru->state == RZG2L_CRU_DMA_STOPPED) { - dev_dbg(cru->dev, "IRQ while state stopped\n"); - return IRQ_HANDLED; - } + irq_status = rzg2l_cru_read(cru, CRUnINTS2); + if (!irq_status) + return IRQ_NONE; - if (cru->state == RZG2L_CRU_DMA_STOPPING) { - if (irq_status & CRUnINTS2_FSxS(0) || - irq_status & CRUnINTS2_FSxS(1) || - irq_status & CRUnINTS2_FSxS(2) || - irq_status & CRUnINTS2_FSxS(3)) - dev_dbg(cru->dev, "IRQ while state stopping\n"); - return IRQ_HANDLED; - } + rzg2l_cru_write(cru, CRUnINTS2, rzg2l_cru_read(cru, CRUnINTS2)); - slot = rzg3e_cru_get_current_slot(cru); - if (slot < 0) - return IRQ_HANDLED; - - dev_dbg(cru->dev, "Current written slot: %d\n", slot); - cru->buf_addr[slot] = 0; - - /* - * To hand buffers back in a known order to userspace start - * to capture first from slot 0. - */ - if (cru->state == RZG2L_CRU_DMA_STARTING) { - if (slot != 0) { - dev_dbg(cru->dev, "Starting sync slot: %d\n", slot); - return IRQ_HANDLED; - } - dev_dbg(cru->dev, "Capture start synced!\n"); - cru->state = RZG2L_CRU_DMA_RUNNING; - } + guard(spinlock)(&cru->hw_lock); + slot = cru->active_slot; + cru->active_slot = rzg2l_cru_slot_next(cru, cru->active_slot); - /* Capture frame */ - if (cru->queue_buf[slot]) { - struct vb2_v4l2_buffer *buf = cru->queue_buf[slot]; + /* Capture frame */ + if (cru->queue_buf[slot]) { + struct vb2_v4l2_buffer *buf = cru->queue_buf[slot]; - buf->field = cru->format.field; - buf->sequence = cru->sequence; - buf->vb2_buf.timestamp = ktime_get_ns(); - vb2_buffer_done(&buf->vb2_buf, VB2_BUF_STATE_DONE); - cru->queue_buf[slot] = NULL; - } else { - /* Scratch buffer was used, dropping frame. */ - dev_dbg(cru->dev, "Dropping frame %u\n", cru->sequence); - } + buf->field = cru->format.field; + buf->sequence = cru->sequence; + buf->vb2_buf.timestamp = ktime_get_ns(); + vb2_buffer_done(&buf->vb2_buf, VB2_BUF_STATE_DONE); + cru->queue_buf[slot] = NULL; + } - cru->sequence++; + cru->sequence++; - /* Prepare for next frame */ - rzg2l_cru_fill_hw_slot(cru, slot); - } + /* Prepare for next frame */ + rzg2l_cru_fill_hw_slot(cru, slot); return IRQ_HANDLED; } @@ -777,21 +700,21 @@ static int rzg2l_cru_start_streaming_vq(struct vb2_queue *vq, unsigned int count cru->scratch = dma_alloc_coherent(cru->dev, cru->format.sizeimage, &cru->scratch_phys, GFP_KERNEL); if (!cru->scratch) { - return_unused_buffers(cru, VB2_BUF_STATE_QUEUED); + rzg2l_cru_return_buffers(cru, VB2_BUF_STATE_QUEUED); dev_err(cru->dev, "Failed to allocate scratch buffer\n"); ret = -ENOMEM; goto assert_presetn; } + cru->active_slot = 0; cru->sequence = 0; ret = rzg2l_cru_set_stream(cru, 1); if (ret) { - return_unused_buffers(cru, VB2_BUF_STATE_QUEUED); + rzg2l_cru_return_buffers(cru, VB2_BUF_STATE_QUEUED); goto out; } - cru->state = RZG2L_CRU_DMA_STARTING; dev_dbg(cru->dev, "Starting to capture\n"); return 0; @@ -824,7 +747,7 @@ static void rzg2l_cru_stop_streaming_vq(struct vb2_queue *vq) dma_free_coherent(cru->dev, cru->format.sizeimage, cru->scratch, cru->scratch_phys); - return_unused_buffers(cru, VB2_BUF_STATE_ERROR); + rzg2l_cru_return_buffers(cru, VB2_BUF_STATE_ERROR); reset_control_assert(cru->presetn); clk_disable_unprepare(cru->vclk); @@ -861,10 +784,9 @@ int rzg2l_cru_dma_register(struct rzg2l_cru_dev *cru) mutex_init(&cru->lock); INIT_LIST_HEAD(&cru->buf_list); + spin_lock_init(&cru->hw_lock); spin_lock_init(&cru->qlock); - cru->state = RZG2L_CRU_DMA_STOPPED; - for (i = 0; i < RZG2L_CRU_HW_BUFFER_MAX; i++) cru->queue_buf[i] = NULL; diff --git a/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c index b167f1bab7ef..932fed38cf3f 100644 --- a/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c +++ b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c @@ -297,12 +297,33 @@ err_return_buffers: static void rzv2h_ivc_stop_streaming(struct vb2_queue *q) { struct rzv2h_ivc *ivc = vb2_get_drv_priv(q); - u32 val = 0; + unsigned int loop = 5; - rzv2h_ivc_write(ivc, RZV2H_IVC_REG_FM_STOP, RZV2H_IVC_REG_FM_STOP_FSTOP); - readl_poll_timeout(ivc->base + RZV2H_IVC_REG_FM_STOP, - val, !(val & RZV2H_IVC_REG_FM_STOP_FSTOP), - 10 * USEC_PER_MSEC, 250 * USEC_PER_MSEC); + /* + * If no frame transfer is in progress, we're done, otherwise, wait for + * the transfer to complete. + * + * Transferring a 1920x1080@10bit frame to the ISP takes less than 5 + * msec so sleep for 2.5 msec (+- 25%) and give up after 5 attempts. + */ + for (; loop > 0; loop--) { + unsigned int vvalid_ifp; + + /* + * Inspect the ivc->vvalid_ifp variable holding the spinlock not + * to the race with the rzv2h_ivc_buffer_done() call in the irq + * handler. + */ + scoped_guard(spinlock_irq, &ivc->spinlock) { + vvalid_ifp = ivc->vvalid_ifp; + } + if (vvalid_ifp < 2) + break; + + fsleep(2500); + } + if (!loop) + dev_err(ivc->dev, "Failed to stop streaming\n"); rzv2h_ivc_return_buffers(ivc, VB2_BUF_STATE_ERROR); video_device_pipeline_stop(&ivc->vdev.dev); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_brx.c b/drivers/media/platform/renesas/vsp1/vsp1_brx.c index 9d93cb8b8e82..325be30836d7 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_brx.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_brx.c @@ -130,15 +130,12 @@ static int brx_set_format(struct v4l2_subdev *subdev, struct vsp1_brx *brx = to_brx(subdev); struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; - int ret = 0; - mutex_lock(&brx->entity.lock); + guard(mutex)(&brx->entity.lock); state = vsp1_entity_get_state(&brx->entity, sd_state, fmt->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; brx_try_format(brx, state, fmt->pad, &fmt->format); @@ -166,9 +163,7 @@ static int brx_set_format(struct v4l2_subdev *subdev, } } -done: - mutex_unlock(&brx->entity.lock); - return ret; + return 0; } static int brx_get_selection(struct v4l2_subdev *subdev, @@ -195,9 +190,10 @@ static int brx_get_selection(struct v4l2_subdev *subdev, if (!state) return -EINVAL; - mutex_lock(&brx->entity.lock); - sel->r = *v4l2_subdev_state_get_compose(state, sel->pad); - mutex_unlock(&brx->entity.lock); + scoped_guard(mutex, &brx->entity.lock) { + sel->r = *v4l2_subdev_state_get_compose(state, sel->pad); + } + return 0; default: @@ -213,7 +209,6 @@ static int brx_set_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; struct v4l2_rect *compose; - int ret = 0; if (sel->pad == brx->entity.source_pad) return -EINVAL; @@ -221,13 +216,11 @@ static int brx_set_selection(struct v4l2_subdev *subdev, if (sel->target != V4L2_SEL_TGT_COMPOSE) return -EINVAL; - mutex_lock(&brx->entity.lock); + guard(mutex)(&brx->entity.lock); state = vsp1_entity_get_state(&brx->entity, sd_state, sel->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; /* * The compose rectangle top left corner must be inside the output @@ -248,9 +241,7 @@ static int brx_set_selection(struct v4l2_subdev *subdev, compose = v4l2_subdev_state_get_compose(state, sel->pad); *compose = sel->r; -done: - mutex_unlock(&brx->entity.lock); - return ret; + return 0; } static const struct v4l2_subdev_pad_ops brx_pad_ops = { diff --git a/drivers/media/platform/renesas/vsp1/vsp1_clu.c b/drivers/media/platform/renesas/vsp1/vsp1_clu.c index 04c466c4da81..a6e4bcab5101 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_clu.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_clu.c @@ -53,9 +53,9 @@ static int clu_set_table(struct vsp1_clu *clu, struct v4l2_ctrl *ctrl) for (i = 0; i < CLU_SIZE; ++i) vsp1_dl_body_write(dlb, VI6_CLU_DATA, ctrl->p_new.p_u32[i]); - spin_lock_irq(&clu->lock); - swap(clu->clu, dlb); - spin_unlock_irq(&clu->lock); + scoped_guard(spinlock_irq, &clu->lock) { + swap(clu->clu, dlb); + } vsp1_dl_body_put(dlb); return 0; @@ -162,7 +162,6 @@ static void clu_configure_frame(struct vsp1_entity *entity, { struct vsp1_clu *clu = to_clu(&entity->subdev); struct vsp1_dl_body *clu_dlb; - unsigned long flags; u32 ctrl = VI6_CLU_CTRL_AAI | VI6_CLU_CTRL_MVS | VI6_CLU_CTRL_EN; /* 2D mode can only be used with the YCbCr pixel encoding. */ @@ -173,10 +172,10 @@ static void clu_configure_frame(struct vsp1_entity *entity, vsp1_clu_write(clu, dlb, VI6_CLU_CTRL, ctrl); - spin_lock_irqsave(&clu->lock, flags); - clu_dlb = clu->clu; - clu->clu = NULL; - spin_unlock_irqrestore(&clu->lock, flags); + scoped_guard(spinlock_irqsave, &clu->lock) { + clu_dlb = clu->clu; + clu->clu = NULL; + } if (clu_dlb) { vsp1_dl_list_add_body(dl, clu_dlb); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_dl.c b/drivers/media/platform/renesas/vsp1/vsp1_dl.c index 6c5578d9d2de..3dc74fed91dc 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_dl.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_dl.c @@ -336,9 +336,8 @@ void vsp1_dl_body_pool_destroy(struct vsp1_dl_body_pool *pool) struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool) { struct vsp1_dl_body *dlb = NULL; - unsigned long flags; - spin_lock_irqsave(&pool->lock, flags); + guard(spinlock_irqsave)(&pool->lock); if (!list_empty(&pool->free)) { dlb = list_first_entry(&pool->free, struct vsp1_dl_body, free); @@ -346,8 +345,6 @@ struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool) refcount_set(&dlb->refcnt, 1); } - spin_unlock_irqrestore(&pool->lock, flags); - return dlb; } @@ -359,8 +356,6 @@ struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool) */ void vsp1_dl_body_put(struct vsp1_dl_body *dlb) { - unsigned long flags; - if (!dlb) return; @@ -369,9 +364,9 @@ void vsp1_dl_body_put(struct vsp1_dl_body *dlb) dlb->num_entries = 0; - spin_lock_irqsave(&dlb->pool->lock, flags); + guard(spinlock_irqsave)(&dlb->pool->lock); + list_add_tail(&dlb->free, &dlb->pool->free); - spin_unlock_irqrestore(&dlb->pool->lock, flags); } /** @@ -493,9 +488,8 @@ 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); + guard(spinlock_irqsave)(&pool->lock); if (!list_empty(&pool->free)) { cmd = list_first_entry(&pool->free, struct vsp1_dl_ext_cmd, @@ -503,24 +497,20 @@ struct vsp1_dl_ext_cmd *vsp1_dl_ext_cmd_get(struct vsp1_dl_cmd_pool *pool) 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); + guard(spinlock_irqsave)(&cmd->pool->lock); + 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) @@ -611,11 +601,10 @@ static void vsp1_dl_list_free(struct vsp1_dl_list *dl) struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm) { struct vsp1_dl_list *dl = NULL; - unsigned long flags; lockdep_assert_not_held(&dlm->lock); - spin_lock_irqsave(&dlm->lock, flags); + guard(spinlock_irqsave)(&dlm->lock); if (!list_empty(&dlm->free)) { dl = list_first_entry(&dlm->free, struct vsp1_dl_list, list); @@ -629,8 +618,6 @@ struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm) dl->allocated = true; } - spin_unlock_irqrestore(&dlm->lock, flags); - return dl; } @@ -690,14 +677,12 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl) */ void vsp1_dl_list_put(struct vsp1_dl_list *dl) { - unsigned long flags; - if (!dl) return; - spin_lock_irqsave(&dl->dlm->lock, flags); + guard(spinlock_irqsave)(&dl->dlm->lock); + __vsp1_dl_list_put(dl); - spin_unlock_irqrestore(&dl->dlm->lock, flags); } /** @@ -937,7 +922,6 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl, unsigned int dl_flags) { struct vsp1_dl_manager *dlm = dl->dlm; struct vsp1_dl_list *dl_next; - unsigned long flags; /* Fill the header for the head and chained display lists. */ vsp1_dl_list_fill_header(dl, list_empty(&dl->chain)); @@ -950,14 +934,12 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl, unsigned int dl_flags) dl->flags = dl_flags & ~VSP1_DL_FRAME_END_COMPLETED; - spin_lock_irqsave(&dlm->lock, flags); + guard(spinlock_irqsave)(&dlm->lock); if (dlm->singleshot) vsp1_dl_list_commit_singleshot(dl); else vsp1_dl_list_commit_continuous(dl); - - spin_unlock_irqrestore(&dlm->lock, flags); } /* ----------------------------------------------------------------------------- @@ -991,7 +973,7 @@ unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) u32 status = vsp1_read(vsp1, VI6_STATUS); unsigned int flags = 0; - spin_lock(&dlm->lock); + guard(spinlock)(&dlm->lock); /* * The mem-to-mem pipelines work in single-shot mode. No new display @@ -1001,7 +983,7 @@ unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) __vsp1_dl_list_put(dlm->active); dlm->active = NULL; flags |= VSP1_DL_FRAME_END_COMPLETED; - goto done; + return flags; } /* @@ -1011,7 +993,7 @@ unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) * and retry. */ if (vsp1_dl_list_hw_update_pending(dlm)) - goto done; + return flags; /* * Progressive streams report only TOP fields. If we have a BOTTOM @@ -1019,7 +1001,7 @@ unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) * next frame end interrupt. */ if (status & VI6_STATUS_FLD_STD(dlm->index)) - goto done; + return flags; /* * If the active display list has the writeback flag set, the frame @@ -1058,9 +1040,6 @@ unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) dlm->pending = NULL; } -done: - spin_unlock(&dlm->lock); - return flags; } @@ -1085,17 +1064,15 @@ void vsp1_dlm_setup(struct vsp1_device *vsp1) void vsp1_dlm_reset(struct vsp1_dl_manager *dlm) { - unsigned long flags; size_t list_count; - spin_lock_irqsave(&dlm->lock, flags); - - __vsp1_dl_list_put(dlm->active); - __vsp1_dl_list_put(dlm->queued); - __vsp1_dl_list_put(dlm->pending); + scoped_guard(spinlock_irqsave, &dlm->lock) { + __vsp1_dl_list_put(dlm->active); + __vsp1_dl_list_put(dlm->queued); + __vsp1_dl_list_put(dlm->pending); - list_count = list_count_nodes(&dlm->free); - spin_unlock_irqrestore(&dlm->lock, flags); + list_count = list_count_nodes(&dlm->free); + } WARN_ON_ONCE(list_count != dlm->list_count); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c index 15d266439564..f6fbd3475329 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c @@ -57,6 +57,50 @@ static void vsp1_du_pipeline_frame_end(struct vsp1_pipeline *pipe, * Pipeline Configuration */ +/* Configure all entities in the pipeline. */ +static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe) +{ + struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe); + struct vsp1_entity *entity; + struct vsp1_entity *next; + struct vsp1_dl_list *dl; + struct vsp1_dl_body *dlb; + unsigned int dl_flags = 0; + + vsp1_pipeline_calculate_partition(pipe, &pipe->part_table[0], + drm_pipe->width, 0); + + if (drm_pipe->force_brx_release) + dl_flags |= VSP1_DL_FRAME_END_INTERNAL; + if (pipe->output->writeback) + dl_flags |= VSP1_DL_FRAME_END_WRITEBACK; + + dl = vsp1_dl_list_get(pipe->output->dlm); + dlb = vsp1_dl_list_get_body0(dl); + + list_for_each_entry_safe(entity, next, &pipe->entities, list_pipe) { + /* Disconnect unused entities from the pipeline. */ + if (!entity->pipe) { + vsp1_dl_body_write(dlb, entity->route->reg, + VI6_DPR_NODE_UNUSED); + + entity->sink = NULL; + list_del(&entity->list_pipe); + + continue; + } + + vsp1_entity_route_setup(entity, pipe, dlb); + vsp1_entity_configure_stream(entity, entity->state, pipe, + dl, dlb); + vsp1_entity_configure_frame(entity, pipe, dl, dlb); + vsp1_entity_configure_partition(entity, pipe, + &pipe->part_table[0], dl, dlb); + } + + vsp1_dl_list_commit(dl, dl_flags); +} + /* * Insert the UIF in the pipeline between the prev and next entities. If no UIF * is available connect the two entities directly. @@ -224,8 +268,6 @@ static int vsp1_du_pipeline_setup_rpf(struct vsp1_device *vsp1, /* Setup the BRx source pad. */ static int vsp1_du_pipeline_setup_inputs(struct vsp1_device *vsp1, struct vsp1_pipeline *pipe); -static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe); - static int vsp1_du_pipeline_setup_brx(struct vsp1_device *vsp1, struct vsp1_pipeline *pipe) { @@ -541,50 +583,6 @@ static int vsp1_du_pipeline_setup_output(struct vsp1_device *vsp1, return 0; } -/* Configure all entities in the pipeline. */ -static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe) -{ - struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe); - struct vsp1_entity *entity; - struct vsp1_entity *next; - struct vsp1_dl_list *dl; - struct vsp1_dl_body *dlb; - unsigned int dl_flags = 0; - - vsp1_pipeline_calculate_partition(pipe, &pipe->part_table[0], - drm_pipe->width, 0); - - if (drm_pipe->force_brx_release) - dl_flags |= VSP1_DL_FRAME_END_INTERNAL; - if (pipe->output->writeback) - dl_flags |= VSP1_DL_FRAME_END_WRITEBACK; - - dl = vsp1_dl_list_get(pipe->output->dlm); - dlb = vsp1_dl_list_get_body0(dl); - - list_for_each_entry_safe(entity, next, &pipe->entities, list_pipe) { - /* Disconnect unused entities from the pipeline. */ - if (!entity->pipe) { - vsp1_dl_body_write(dlb, entity->route->reg, - VI6_DPR_NODE_UNUSED); - - entity->sink = NULL; - list_del(&entity->list_pipe); - - continue; - } - - vsp1_entity_route_setup(entity, pipe, dlb); - vsp1_entity_configure_stream(entity, entity->state, pipe, - dl, dlb); - vsp1_entity_configure_frame(entity, pipe, dl, dlb); - vsp1_entity_configure_partition(entity, pipe, - &pipe->part_table[0], dl, dlb); - } - - vsp1_dl_list_commit(dl, dl_flags); -} - static int vsp1_du_pipeline_set_rwpf_format(struct vsp1_device *vsp1, struct vsp1_rwpf *rwpf, u32 pixelformat, unsigned int pitch) @@ -631,14 +629,14 @@ int vsp1_du_init(struct device *dev) EXPORT_SYMBOL_GPL(vsp1_du_init); /** - * vsp1_du_setup_lif - Setup the output part of the VSP pipeline + * vsp1_du_enable - Setup and enable a DU pipeline * @dev: the VSP device * @pipe_index: the DRM pipeline index * @cfg: the LIF configuration * * Configure the output part of VSP DRM pipeline for the given frame @cfg.width * and @cfg.height. This sets up formats on the BRx source pad, the WPF sink and - * source pads, and the LIF sink pad. + * source pads, and the LIF sink pad, and then starts the pipeline. * * The @pipe_index argument selects which DRM pipeline to setup. The number of * available pipelines depend on the VSP instance. @@ -651,14 +649,12 @@ EXPORT_SYMBOL_GPL(vsp1_du_init); * * Return 0 on success or a negative error code on failure. */ -int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, - const struct vsp1_du_lif_config *cfg) +int vsp1_du_enable(struct device *dev, unsigned int pipe_index, + const struct vsp1_du_lif_config *cfg) { struct vsp1_device *vsp1 = dev_get_drvdata(dev); struct vsp1_drm_pipeline *drm_pipe; struct vsp1_pipeline *pipe; - unsigned long flags; - unsigned int i; int ret; if (pipe_index >= vsp1->info->lif_count) @@ -667,17 +663,87 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, drm_pipe = &vsp1->drm->pipe[pipe_index]; pipe = &drm_pipe->pipe; - if (!cfg) { - struct vsp1_brx *brx; + /* Reset the underrun counter */ + pipe->underrun_count = 0; + + 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%s\n", + __func__, pipe_index, cfg->width, cfg->height, + pipe->interlaced ? "i" : ""); + + scoped_guard(mutex, &vsp1->drm->lock) { + /* Setup formats through the pipeline. */ + ret = vsp1_du_pipeline_setup_inputs(vsp1, pipe); + if (ret < 0) + return ret; - mutex_lock(&vsp1->drm->lock); + ret = vsp1_du_pipeline_setup_output(vsp1, pipe); + if (ret < 0) + return ret; - brx = to_brx(&pipe->brx->subdev); + vsp1_pipeline_dump(pipe, "DU enable"); + + /* Enable the VSP1. */ + ret = vsp1_device_get(vsp1); + if (ret < 0) + return ret; /* - * NULL configuration means the CRTC is being disabled, stop - * the pipeline and turn the light off. + * Register a callback to allow us to notify the DRM driver of frame + * completion events. */ + drm_pipe->du_complete = cfg->callback; + drm_pipe->du_private = cfg->callback_data; + + /* Disable the display interrupts. */ + vsp1_write(vsp1, VI6_DISP_IRQ_STA(pipe_index), 0); + vsp1_write(vsp1, VI6_DISP_IRQ_ENB(pipe_index), 0); + + /* Configure all entities in the pipeline. */ + vsp1_du_pipeline_configure(pipe); + } + + /* Start the pipeline. */ + scoped_guard(spinlock_irqsave, &pipe->irqlock) { + vsp1_pipeline_run(pipe); + } + + dev_dbg(vsp1->dev, "%s: pipeline enabled\n", __func__); + + return 0; +} +EXPORT_SYMBOL_GPL(vsp1_du_enable); + +/** + * vsp1_du_disable - Disable and stop a DU pipeline + * @dev: the VSP device + * @pipe_index: the DRM pipeline index + * + * The @pipe_index argument selects which DRM pipeline to disable. The number + * of available pipelines depend on the VSP instance. + * + * Return 0 on success or a negative error code on failure. + */ +int vsp1_du_disable(struct device *dev, unsigned int pipe_index) +{ + struct vsp1_device *vsp1 = dev_get_drvdata(dev); + struct vsp1_drm_pipeline *drm_pipe; + struct vsp1_pipeline *pipe; + unsigned int i; + int ret; + + if (pipe_index >= vsp1->info->lif_count) + return -EINVAL; + + drm_pipe = &vsp1->drm->pipe[pipe_index]; + pipe = &drm_pipe->pipe; + + scoped_guard(mutex, &vsp1->drm->lock) { + struct vsp1_brx *brx = to_brx(&pipe->brx->subdev); + ret = vsp1_pipeline_stop(pipe); if (ret == -ETIMEDOUT) dev_err(vsp1->dev, "DRM pipeline stop timeout\n"); @@ -710,76 +776,16 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, list_del(&pipe->brx->list_pipe); pipe->brx->pipe = NULL; pipe->brx = NULL; - - mutex_unlock(&vsp1->drm->lock); - - vsp1_dlm_reset(pipe->output->dlm); - vsp1_device_put(vsp1); - - dev_dbg(vsp1->dev, "%s: pipeline disabled\n", __func__); - - return 0; } - /* Reset the underrun counter */ - pipe->underrun_count = 0; - - 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%s\n", - __func__, pipe_index, cfg->width, cfg->height, - pipe->interlaced ? "i" : ""); - - mutex_lock(&vsp1->drm->lock); - - /* Setup formats through the pipeline. */ - ret = vsp1_du_pipeline_setup_inputs(vsp1, pipe); - if (ret < 0) - goto unlock; - - ret = vsp1_du_pipeline_setup_output(vsp1, pipe); - if (ret < 0) - goto unlock; - - vsp1_pipeline_dump(pipe, "LIF setup"); - - /* Enable the VSP1. */ - ret = vsp1_device_get(vsp1); - if (ret < 0) - goto unlock; - - /* - * Register a callback to allow us to notify the DRM driver of frame - * completion events. - */ - drm_pipe->du_complete = cfg->callback; - drm_pipe->du_private = cfg->callback_data; - - /* Disable the display interrupts. */ - vsp1_write(vsp1, VI6_DISP_IRQ_STA(pipe_index), 0); - vsp1_write(vsp1, VI6_DISP_IRQ_ENB(pipe_index), 0); - - /* Configure all entities in the pipeline. */ - vsp1_du_pipeline_configure(pipe); - -unlock: - mutex_unlock(&vsp1->drm->lock); + vsp1_dlm_reset(pipe->output->dlm); + vsp1_device_put(vsp1); - if (ret < 0) - return ret; - - /* Start the pipeline. */ - spin_lock_irqsave(&pipe->irqlock, flags); - vsp1_pipeline_run(pipe); - spin_unlock_irqrestore(&pipe->irqlock, flags); - - dev_dbg(vsp1->dev, "%s: pipeline enabled\n", __func__); + dev_dbg(vsp1->dev, "%s: pipeline disabled\n", __func__); return 0; } -EXPORT_SYMBOL_GPL(vsp1_du_setup_lif); +EXPORT_SYMBOL_GPL(vsp1_du_disable); /** * vsp1_du_atomic_begin - Prepare for an atomic update @@ -904,7 +910,7 @@ void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index, drm_pipe->crc = cfg->crc; - mutex_lock(&vsp1->drm->lock); + guard(mutex)(&vsp1->drm->lock); if (cfg->writeback.pixelformat) { const struct vsp1_du_writeback_config *wb_cfg = &cfg->writeback; @@ -913,7 +919,7 @@ void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index, wb_cfg->pixelformat, wb_cfg->pitch); if (WARN_ON(ret < 0)) - goto done; + return; pipe->output->mem.addr[0] = wb_cfg->mem[0]; pipe->output->mem.addr[1] = wb_cfg->mem[1]; @@ -926,9 +932,6 @@ void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index, vsp1_pipeline_dump(pipe, "atomic update"); vsp1_du_pipeline_configure(pipe); - -done: - mutex_unlock(&vsp1->drm->lock); } EXPORT_SYMBOL_GPL(vsp1_du_atomic_flush); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c index 839b75b62ceb..2ae2a573f0de 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c @@ -172,9 +172,9 @@ int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev, if (!state) return -EINVAL; - mutex_lock(&entity->lock); + guard(mutex)(&entity->lock); + fmt->format = *v4l2_subdev_state_get_format(state, fmt->pad); - mutex_unlock(&entity->lock); return 0; } @@ -216,10 +216,10 @@ int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev, if (!state) return -EINVAL; - mutex_lock(&entity->lock); - format = v4l2_subdev_state_get_format(state, 0); - code->code = format->code; - mutex_unlock(&entity->lock); + scoped_guard(mutex, &entity->lock) { + format = v4l2_subdev_state_get_format(state, 0); + code->code = format->code; + } } return 0; @@ -308,22 +308,19 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, struct v4l2_mbus_framefmt *format; struct v4l2_rect *selection; unsigned int i; - int ret = 0; - mutex_lock(&entity->lock); + guard(mutex)(&entity->lock); state = vsp1_entity_get_state(entity, sd_state, fmt->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; format = v4l2_subdev_state_get_format(state, fmt->pad); if (fmt->pad == entity->source_pad) { /* The output format can't be modified. */ fmt->format = *format; - goto done; + return 0; } /* @@ -369,9 +366,7 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, selection->width = format->width; selection->height = format->height; -done: - mutex_unlock(&entity->lock); - return ret; + return 0; } static int vsp1_entity_init_state(struct v4l2_subdev *subdev, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hgo.c b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c index 2c8ce7175a4e..0ef512e3a94b 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hgo.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c @@ -153,11 +153,11 @@ static void hgo_configure_stream(struct vsp1_entity *entity, (crop->width << VI6_HGO_SIZE_HSIZE_SHIFT) | (crop->height << VI6_HGO_SIZE_VSIZE_SHIFT)); - mutex_lock(hgo->ctrls.handler.lock); - hgo->max_rgb = hgo->ctrls.max_rgb->cur.val; - if (hgo->ctrls.num_bins) - hgo->num_bins = hgo_num_bins[hgo->ctrls.num_bins->cur.val]; - mutex_unlock(hgo->ctrls.handler.lock); + scoped_guard(mutex, hgo->ctrls.handler.lock) { + hgo->max_rgb = hgo->ctrls.max_rgb->cur.val; + if (hgo->ctrls.num_bins) + hgo->num_bins = hgo_num_bins[hgo->ctrls.num_bins->cur.val]; + } hratio = crop->width * 2 / compose->width / 3; vratio = crop->height * 2 / compose->height / 3; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hgt.c b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c index 858f330d44fa..78b5a9201c70 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hgt.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c @@ -152,15 +152,15 @@ static void hgt_configure_stream(struct vsp1_entity *entity, (crop->width << VI6_HGT_SIZE_HSIZE_SHIFT) | (crop->height << VI6_HGT_SIZE_VSIZE_SHIFT)); - mutex_lock(hgt->ctrls.lock); - for (i = 0; i < HGT_NUM_HUE_AREAS; ++i) { - lower = hgt->hue_areas[i*2 + 0]; - upper = hgt->hue_areas[i*2 + 1]; - vsp1_hgt_write(hgt, dlb, VI6_HGT_HUE_AREA(i), - (lower << VI6_HGT_HUE_AREA_LOWER_SHIFT) | - (upper << VI6_HGT_HUE_AREA_UPPER_SHIFT)); + scoped_guard(mutex, hgt->ctrls.lock) { + for (i = 0; i < HGT_NUM_HUE_AREAS; ++i) { + lower = hgt->hue_areas[i*2 + 0]; + upper = hgt->hue_areas[i*2 + 1]; + vsp1_hgt_write(hgt, dlb, VI6_HGT_HUE_AREA(i), + (lower << VI6_HGT_HUE_AREA_LOWER_SHIFT) | + (upper << VI6_HGT_HUE_AREA_UPPER_SHIFT)); + } } - mutex_unlock(hgt->ctrls.lock); hratio = crop->width * 2 / compose->width / 3; vratio = crop->height * 2 / compose->height / 3; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_histo.c b/drivers/media/platform/renesas/vsp1/vsp1_histo.c index 3f87a2c9df0e..97dbfb93abe9 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_histo.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_histo.c @@ -35,20 +35,18 @@ to_vsp1_histogram_buffer(struct vb2_v4l2_buffer *vbuf) struct vsp1_histogram_buffer * vsp1_histogram_buffer_get(struct vsp1_histogram *histo) { - struct vsp1_histogram_buffer *buf = NULL; + struct vsp1_histogram_buffer *buf; - spin_lock(&histo->irqlock); + guard(spinlock)(&histo->irqlock); if (list_empty(&histo->irqqueue)) - goto done; + return NULL; buf = list_first_entry(&histo->irqqueue, struct vsp1_histogram_buffer, queue); list_del(&buf->queue); histo->readout = true; -done: - spin_unlock(&histo->irqlock); return buf; } @@ -68,10 +66,10 @@ void vsp1_histogram_buffer_complete(struct vsp1_histogram *histo, vb2_set_plane_payload(&buf->buf.vb2_buf, 0, size); vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE); - spin_lock(&histo->irqlock); + guard(spinlock)(&histo->irqlock); + histo->readout = false; wake_up(&histo->wait_queue); - spin_unlock(&histo->irqlock); } /* ----------------------------------------------------------------------------- @@ -123,9 +121,9 @@ static void histo_buffer_queue(struct vb2_buffer *vb) struct vsp1_histogram *histo = vb2_get_drv_priv(vb->vb2_queue); struct vsp1_histogram_buffer *buf = to_vsp1_histogram_buffer(vbuf); - spin_lock_irq(&histo->irqlock); + guard(spinlock_irq)(&histo->irqlock); + list_add_tail(&buf->queue, &histo->irqqueue); - spin_unlock_irq(&histo->irqlock); } static int histo_start_streaming(struct vb2_queue *vq, unsigned int count) @@ -138,7 +136,7 @@ static void histo_stop_streaming(struct vb2_queue *vq) struct vsp1_histogram *histo = vb2_get_drv_priv(vq); struct vsp1_histogram_buffer *buffer; - spin_lock_irq(&histo->irqlock); + guard(spinlock_irq)(&histo->irqlock); /* Remove all buffers from the IRQ queue. */ list_for_each_entry(buffer, &histo->irqqueue, queue) @@ -147,8 +145,6 @@ static void histo_stop_streaming(struct vb2_queue *vq) /* Wait for the buffer being read out (if any) to complete. */ wait_event_lock_irq(histo->wait_queue, !histo->readout, histo->irqlock); - - spin_unlock_irq(&histo->irqlock); } static const struct vb2_ops histo_video_queue_qops = { @@ -196,18 +192,15 @@ static int histo_get_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - int ret = 0; if (sel->pad != HISTO_PAD_SINK) return -EINVAL; - mutex_lock(&histo->entity.lock); + guard(mutex)(&histo->entity.lock); state = vsp1_entity_get_state(&histo->entity, sd_state, sel->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; switch (sel->target) { case V4L2_SEL_TGT_COMPOSE_BOUNDS: @@ -237,13 +230,10 @@ static int histo_get_selection(struct v4l2_subdev *subdev, break; default: - ret = -EINVAL; - break; + return -EINVAL; } -done: - mutex_unlock(&histo->entity.lock); - return ret; + return 0; } static int histo_set_crop(struct v4l2_subdev *subdev, @@ -321,29 +311,22 @@ static int histo_set_selection(struct v4l2_subdev *subdev, { struct vsp1_histogram *histo = subdev_to_histo(subdev); struct v4l2_subdev_state *state; - int ret; if (sel->pad != HISTO_PAD_SINK) return -EINVAL; - mutex_lock(&histo->entity.lock); + guard(mutex)(&histo->entity.lock); state = vsp1_entity_get_state(&histo->entity, sd_state, sel->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; if (sel->target == V4L2_SEL_TGT_CROP) - ret = histo_set_crop(subdev, state, sel); + return histo_set_crop(subdev, state, sel); else if (sel->target == V4L2_SEL_TGT_COMPOSE) - ret = histo_set_compose(subdev, state, sel); + return histo_set_compose(subdev, state, sel); else - ret = -EINVAL; - -done: - mutex_unlock(&histo->entity.lock); - return ret; + return -EINVAL; } static int histo_set_format(struct v4l2_subdev *subdev, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c index 830e124beb7b..df069c228243 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c @@ -115,15 +115,12 @@ static int hsit_set_format(struct v4l2_subdev *subdev, struct vsp1_hsit *hsit = to_hsit(subdev); struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; - int ret = 0; - mutex_lock(&hsit->entity.lock); + guard(mutex)(&hsit->entity.lock); state = vsp1_entity_get_state(&hsit->entity, sd_state, fmt->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; format = v4l2_subdev_state_get_format(state, fmt->pad); @@ -133,7 +130,7 @@ static int hsit_set_format(struct v4l2_subdev *subdev, * modified. */ fmt->format = *format; - goto done; + return 0; } format->code = hsit->inverse ? MEDIA_BUS_FMT_AHSV8888_1X32 @@ -161,9 +158,7 @@ static int hsit_set_format(struct v4l2_subdev *subdev, vsp1_entity_adjust_color_space(format); -done: - mutex_unlock(&hsit->entity.lock); - return ret; + return 0; } static const struct v4l2_subdev_pad_ops hsit_pad_ops = { diff --git a/drivers/media/platform/renesas/vsp1/vsp1_lut.c b/drivers/media/platform/renesas/vsp1/vsp1_lut.c index 94bdedcc5c92..a22c31e17cb7 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_lut.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_lut.c @@ -50,9 +50,9 @@ static int lut_set_table(struct vsp1_lut *lut, struct v4l2_ctrl *ctrl) vsp1_dl_body_write(dlb, VI6_LUT_TABLE + 4 * i, ctrl->p_new.p_u32[i]); - spin_lock_irq(&lut->lock); - swap(lut->lut, dlb); - spin_unlock_irq(&lut->lock); + scoped_guard(spinlock_irq, &lut->lock) { + swap(lut->lut, dlb); + } vsp1_dl_body_put(dlb); return 0; @@ -132,12 +132,11 @@ static void lut_configure_frame(struct vsp1_entity *entity, { struct vsp1_lut *lut = to_lut(&entity->subdev); struct vsp1_dl_body *lut_dlb; - unsigned long flags; - spin_lock_irqsave(&lut->lock, flags); - lut_dlb = lut->lut; - lut->lut = NULL; - spin_unlock_irqrestore(&lut->lock, flags); + scoped_guard(spinlock_irqsave, &lut->lock) { + lut_dlb = lut->lut; + lut->lut = NULL; + } if (lut_dlb) { vsp1_dl_list_add_body(dl, lut_dlb); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c index 5d769cc42fe1..32bb02ce0366 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c @@ -229,6 +229,10 @@ static const struct vsp1_format_info vsp1_video_hsit_formats[] = { 1, { 32, 0, 0 }, false, false, 1, 1, false }, }; +#define vsp1_for_each_format(info, formats) \ + for (const struct vsp1_format_info *info = &formats[0]; \ + info < formats + ARRAY_SIZE(formats); ++info) + /** * vsp1_get_format_info - Retrieve format information for a 4CC * @vsp1: the VSP1 device @@ -240,30 +244,20 @@ static const struct vsp1_format_info vsp1_video_hsit_formats[] = { const struct vsp1_format_info *vsp1_get_format_info(struct vsp1_device *vsp1, u32 fourcc) { - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(vsp1_video_formats); ++i) { - const struct vsp1_format_info *info = &vsp1_video_formats[i]; - + vsp1_for_each_format(info, vsp1_video_formats) { if (info->fourcc == fourcc) return info; } if (vsp1->info->gen == 2) { - for (i = 0; i < ARRAY_SIZE(vsp1_video_gen2_formats); ++i) { - const struct vsp1_format_info *info = - &vsp1_video_gen2_formats[i]; - + vsp1_for_each_format(info, vsp1_video_gen2_formats) { if (info->fourcc == fourcc) return info; } } if (vsp1_feature(vsp1, VSP1_HAS_HSIT)) { - for (i = 0; i < ARRAY_SIZE(vsp1_video_hsit_formats); ++i) { - const struct vsp1_format_info *info = - &vsp1_video_hsit_formats[i]; - + vsp1_for_each_format(info, vsp1_video_hsit_formats) { if (info->fourcc == fourcc) return info; } @@ -287,8 +281,6 @@ const struct vsp1_format_info * vsp1_get_format_info_by_index(struct vsp1_device *vsp1, unsigned int index, u32 code) { - unsigned int i; - if (!code) { if (index < ARRAY_SIZE(vsp1_video_formats)) return &vsp1_video_formats[index]; @@ -308,9 +300,7 @@ vsp1_get_format_info_by_index(struct vsp1_device *vsp1, unsigned int index, return NULL; } - for (i = 0; i < ARRAY_SIZE(vsp1_video_formats); ++i) { - const struct vsp1_format_info *info = &vsp1_video_formats[i]; - + vsp1_for_each_format(info, vsp1_video_formats) { if (info->mbus == code) { if (!index) return info; @@ -319,10 +309,7 @@ vsp1_get_format_info_by_index(struct vsp1_device *vsp1, unsigned int index, } if (vsp1->info->gen == 2) { - for (i = 0; i < ARRAY_SIZE(vsp1_video_gen2_formats); ++i) { - const struct vsp1_format_info *info = - &vsp1_video_gen2_formats[i]; - + vsp1_for_each_format(info, vsp1_video_gen2_formats) { if (info->mbus == code) { if (!index) return info; @@ -332,10 +319,7 @@ vsp1_get_format_info_by_index(struct vsp1_device *vsp1, unsigned int index, } if (vsp1_feature(vsp1, VSP1_HAS_HSIT)) { - for (i = 0; i < ARRAY_SIZE(vsp1_video_hsit_formats); ++i) { - const struct vsp1_format_info *info = - &vsp1_video_hsit_formats[i]; - + vsp1_for_each_format(info, vsp1_video_hsit_formats) { if (info->mbus == code) { if (!index) return info; @@ -487,21 +471,15 @@ void vsp1_pipeline_run(struct vsp1_pipeline *pipe) bool vsp1_pipeline_stopped(struct vsp1_pipeline *pipe) { - unsigned long flags; - bool stopped; + guard(spinlock_irqsave)(&pipe->irqlock); - spin_lock_irqsave(&pipe->irqlock, flags); - stopped = pipe->state == VSP1_PIPELINE_STOPPED; - spin_unlock_irqrestore(&pipe->irqlock, flags); - - return stopped; + return pipe->state == VSP1_PIPELINE_STOPPED; } int vsp1_pipeline_stop(struct vsp1_pipeline *pipe) { struct vsp1_device *vsp1 = pipe->output->entity.vsp1; struct vsp1_entity *entity; - unsigned long flags; int ret; if (pipe->lif) { @@ -511,16 +489,16 @@ int vsp1_pipeline_stop(struct vsp1_pipeline *pipe) */ ret = vsp1_reset_wpf(vsp1, pipe->output->entity.index); if (ret == 0) { - spin_lock_irqsave(&pipe->irqlock, flags); - pipe->state = VSP1_PIPELINE_STOPPED; - spin_unlock_irqrestore(&pipe->irqlock, flags); + scoped_guard(spinlock_irqsave, &pipe->irqlock) { + pipe->state = VSP1_PIPELINE_STOPPED; + } } } else { /* Otherwise just request a stop and wait. */ - spin_lock_irqsave(&pipe->irqlock, flags); - if (pipe->state == VSP1_PIPELINE_RUNNING) - pipe->state = VSP1_PIPELINE_STOPPING; - spin_unlock_irqrestore(&pipe->irqlock, flags); + scoped_guard(spinlock_irqsave, &pipe->irqlock) { + if (pipe->state == VSP1_PIPELINE_RUNNING) + pipe->state = VSP1_PIPELINE_STOPPING; + } ret = wait_event_timeout(pipe->wq, vsp1_pipeline_stopped(pipe), msecs_to_jiffies(500)); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c index c72518b29f84..ced01870acd6 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c @@ -116,15 +116,12 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct vsp1_rwpf *rwpf = to_rwpf(subdev); struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; - int ret = 0; - mutex_lock(&rwpf->entity.lock); + guard(mutex)(&rwpf->entity.lock); state = vsp1_entity_get_state(&rwpf->entity, sd_state, fmt->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; /* Default to YUV if the requested format is not supported. */ if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 && @@ -174,7 +171,7 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, fmt->format = *format; fmt->format.flags = flags; - goto done; + return 0; } format->code = fmt->format.code; @@ -213,9 +210,7 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, format->height = fmt->format.width; } -done: - mutex_unlock(&rwpf->entity.lock); - return ret; + return 0; } static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, @@ -225,7 +220,6 @@ static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, struct vsp1_rwpf *rwpf = to_rwpf(subdev); struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; - int ret = 0; /* * Cropping is only supported on the RPF and is implemented on the sink @@ -234,13 +228,11 @@ static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, if (rwpf->entity.type == VSP1_ENTITY_WPF || sel->pad != RWPF_PAD_SINK) return -EINVAL; - mutex_lock(&rwpf->entity.lock); + guard(mutex)(&rwpf->entity.lock); state = vsp1_entity_get_state(&rwpf->entity, sd_state, sel->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; switch (sel->target) { case V4L2_SEL_TGT_CROP: @@ -256,13 +248,10 @@ static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, break; default: - ret = -EINVAL; - break; + return -EINVAL; } -done: - mutex_unlock(&rwpf->entity.lock); - return ret; + return 0; } static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, @@ -275,7 +264,6 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - int ret = 0; /* * Cropping is only supported on the RPF and is implemented on the sink @@ -287,13 +275,11 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, if (sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; - mutex_lock(&rwpf->entity.lock); + guard(mutex)(&rwpf->entity.lock); state = vsp1_entity_get_state(&rwpf->entity, sd_state, sel->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; /* Make sure the crop rectangle is entirely contained in the image. */ format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); @@ -342,9 +328,7 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, format->width = crop->width; format->height = crop->height; -done: - mutex_unlock(&rwpf->entity.lock); - return ret; + return 0; } static const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops = { diff --git a/drivers/media/platform/renesas/vsp1/vsp1_sru.c b/drivers/media/platform/renesas/vsp1/vsp1_sru.c index 94149da0c900..3fd9fde5c724 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_sru.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_sru.c @@ -216,15 +216,12 @@ static int sru_set_format(struct v4l2_subdev *subdev, struct vsp1_sru *sru = to_sru(subdev); struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; - int ret = 0; - mutex_lock(&sru->entity.lock); + guard(mutex)(&sru->entity.lock); state = vsp1_entity_get_state(&sru->entity, sd_state, fmt->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; sru_try_format(sru, state, fmt->pad, &fmt->format); @@ -239,9 +236,7 @@ static int sru_set_format(struct v4l2_subdev *subdev, sru_try_format(sru, state, SRU_PAD_SOURCE, format); } -done: - mutex_unlock(&sru->entity.lock); - return ret; + return 0; } static const struct v4l2_subdev_pad_ops sru_pad_ops = { diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uds.c b/drivers/media/platform/renesas/vsp1/vsp1_uds.c index dd4722315c56..9f7bb112929e 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_uds.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uds.c @@ -199,15 +199,12 @@ static int uds_set_format(struct v4l2_subdev *subdev, struct vsp1_uds *uds = to_uds(subdev); struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; - int ret = 0; - mutex_lock(&uds->entity.lock); + guard(mutex)(&uds->entity.lock); state = vsp1_entity_get_state(&uds->entity, sd_state, fmt->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; uds_try_format(uds, state, fmt->pad, &fmt->format); @@ -222,9 +219,7 @@ static int uds_set_format(struct v4l2_subdev *subdev, uds_try_format(uds, state, UDS_PAD_SOURCE, format); } -done: - mutex_unlock(&uds->entity.lock); - return ret; + return 0; } /* ----------------------------------------------------------------------------- diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uif.c b/drivers/media/platform/renesas/vsp1/vsp1_uif.c index 3aefe5c9d421..52dbfe58a70d 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_uif.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uif.c @@ -60,18 +60,15 @@ static int uif_get_selection(struct v4l2_subdev *subdev, struct vsp1_uif *uif = to_uif(subdev); struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; - int ret = 0; if (sel->pad != UIF_PAD_SINK) return -EINVAL; - mutex_lock(&uif->entity.lock); + guard(mutex)(&uif->entity.lock); state = vsp1_entity_get_state(&uif->entity, sd_state, sel->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: @@ -88,13 +85,10 @@ static int uif_get_selection(struct v4l2_subdev *subdev, break; default: - ret = -EINVAL; - break; + return -EINVAL; } -done: - mutex_unlock(&uif->entity.lock); - return ret; + return 0; } static int uif_set_selection(struct v4l2_subdev *subdev, @@ -105,19 +99,16 @@ static int uif_set_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; struct v4l2_rect *selection; - int ret = 0; if (sel->pad != UIF_PAD_SINK || sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; - mutex_lock(&uif->entity.lock); + guard(mutex)(&uif->entity.lock); state = vsp1_entity_get_state(&uif->entity, sd_state, sel->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; /* The crop rectangle must be inside the input frame. */ format = v4l2_subdev_state_get_format(state, UIF_PAD_SINK); @@ -133,9 +124,7 @@ static int uif_set_selection(struct v4l2_subdev *subdev, selection = v4l2_subdev_state_get_crop(state, sel->pad); *selection = sel->r; -done: - mutex_unlock(&uif->entity.lock); - return ret; + return 0; } /* ----------------------------------------------------------------------------- diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c index fe1dac11d4ae..5a1d284213ad 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_video.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c @@ -209,26 +209,21 @@ vsp1_video_complete_buffer(struct vsp1_video *video) struct vsp1_pipeline *pipe = video->rwpf->entity.pipe; struct vsp1_vb2_buffer *next = NULL; struct vsp1_vb2_buffer *done; - unsigned long flags; unsigned int i; - spin_lock_irqsave(&video->irqlock, flags); - - if (list_empty(&video->irqqueue)) { - spin_unlock_irqrestore(&video->irqlock, flags); - return NULL; - } - - done = list_first_entry(&video->irqqueue, - struct vsp1_vb2_buffer, queue); - - list_del(&done->queue); + scoped_guard(spinlock_irqsave, &video->irqlock) { + if (list_empty(&video->irqqueue)) + return NULL; - if (!list_empty(&video->irqqueue)) - next = list_first_entry(&video->irqqueue, + done = list_first_entry(&video->irqqueue, struct vsp1_vb2_buffer, queue); - spin_unlock_irqrestore(&video->irqlock, flags); + list_del(&done->queue); + + if (!list_empty(&video->irqqueue)) + next = list_first_entry(&video->irqqueue, + struct vsp1_vb2_buffer, queue); + } done->buf.sequence = pipe->sequence; done->buf.vb2_buf.timestamp = ktime_get_ns(); @@ -595,9 +590,9 @@ static void vsp1_video_pipeline_put(struct vsp1_pipeline *pipe) { struct media_device *mdev = &pipe->output->entity.vsp1->media_dev; - mutex_lock(&mdev->graph_mutex); + guard(mutex)(&mdev->graph_mutex); + kref_put(&pipe->kref, vsp1_video_pipeline_release); - mutex_unlock(&mdev->graph_mutex); } /* ----------------------------------------------------------------------------- @@ -661,18 +656,17 @@ static void vsp1_video_buffer_queue(struct vb2_buffer *vb) struct vsp1_video *video = vb2_get_drv_priv(vb->vb2_queue); struct vsp1_pipeline *pipe = video->rwpf->entity.pipe; struct vsp1_vb2_buffer *buf = to_vsp1_vb2_buffer(vbuf); - unsigned long flags; bool empty; - spin_lock_irqsave(&video->irqlock, flags); - empty = list_empty(&video->irqqueue); - list_add_tail(&buf->queue, &video->irqqueue); - spin_unlock_irqrestore(&video->irqlock, flags); + scoped_guard(spinlock_irqsave, &video->irqlock) { + empty = list_empty(&video->irqqueue); + list_add_tail(&buf->queue, &video->irqqueue); + } if (!empty) return; - spin_lock_irqsave(&pipe->irqlock, flags); + guard(spinlock_irqsave)(&pipe->irqlock); video->rwpf->mem = buf->mem; pipe->buffers_ready |= 1 << video->pipe_index; @@ -680,8 +674,6 @@ static void vsp1_video_buffer_queue(struct vb2_buffer *vb) if (vb2_start_streaming_called(&video->queue) && vsp1_pipeline_ready(pipe)) vsp1_video_pipeline_run(pipe); - - spin_unlock_irqrestore(&pipe->irqlock, flags); } static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe) @@ -783,14 +775,13 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe) static void vsp1_video_release_buffers(struct vsp1_video *video) { struct vsp1_vb2_buffer *buffer; - unsigned long flags; /* Remove all buffers from the IRQ queue. */ - spin_lock_irqsave(&video->irqlock, flags); + guard(spinlock_irqsave)(&video->irqlock); + list_for_each_entry(buffer, &video->irqqueue, queue) vb2_buffer_done(&buffer->buf.vb2_buf, VB2_BUF_STATE_ERROR); INIT_LIST_HEAD(&video->irqqueue); - spin_unlock_irqrestore(&video->irqlock, flags); } static void vsp1_video_cleanup_pipeline(struct vsp1_pipeline *pipe) @@ -812,25 +803,23 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) struct vsp1_video *video = vb2_get_drv_priv(vq); struct vsp1_pipeline *pipe = video->rwpf->entity.pipe; bool start_pipeline = false; - unsigned long flags; int ret; - mutex_lock(&pipe->lock); - if (pipe->stream_count == pipe->num_inputs) { - ret = vsp1_video_setup_pipeline(pipe); - if (ret < 0) { - vsp1_video_release_buffers(video); - vsp1_video_cleanup_pipeline(pipe); - mutex_unlock(&pipe->lock); - return ret; + scoped_guard(mutex, &pipe->lock) { + if (pipe->stream_count == pipe->num_inputs) { + ret = vsp1_video_setup_pipeline(pipe); + if (ret < 0) { + vsp1_video_release_buffers(video); + vsp1_video_cleanup_pipeline(pipe); + return ret; + } + + start_pipeline = true; } - start_pipeline = true; + pipe->stream_count++; } - pipe->stream_count++; - mutex_unlock(&pipe->lock); - /* * vsp1_pipeline_ready() is not sufficient to establish that all streams * are prepared and the pipeline is configured, as multiple streams @@ -841,10 +830,10 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) if (!start_pipeline) return 0; - spin_lock_irqsave(&pipe->irqlock, flags); + guard(spinlock_irqsave)(&pipe->irqlock); + if (vsp1_pipeline_ready(pipe)) vsp1_video_pipeline_run(pipe); - spin_unlock_irqrestore(&pipe->irqlock, flags); return 0; } @@ -853,27 +842,27 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq) { struct vsp1_video *video = vb2_get_drv_priv(vq); struct vsp1_pipeline *pipe = video->rwpf->entity.pipe; - unsigned long flags; int ret; /* * Clear the buffers ready flag to make sure the device won't be started * by a QBUF on the video node on the other side of the pipeline. */ - spin_lock_irqsave(&video->irqlock, flags); - pipe->buffers_ready &= ~(1 << video->pipe_index); - spin_unlock_irqrestore(&video->irqlock, flags); - - mutex_lock(&pipe->lock); - if (--pipe->stream_count == pipe->num_inputs) { - /* Stop the pipeline. */ - ret = vsp1_pipeline_stop(pipe); - if (ret == -ETIMEDOUT) - dev_err(video->vsp1->dev, "pipeline stop timeout\n"); - - vsp1_video_cleanup_pipeline(pipe); + scoped_guard(spinlock_irqsave, &video->irqlock) { + pipe->buffers_ready &= ~(1 << video->pipe_index); + } + + scoped_guard(mutex, &pipe->lock) { + if (--pipe->stream_count == pipe->num_inputs) { + /* Stop the pipeline. */ + ret = vsp1_pipeline_stop(pipe); + if (ret == -ETIMEDOUT) + dev_err(video->vsp1->dev, + "pipeline stop timeout\n"); + + vsp1_video_cleanup_pipeline(pipe); + } } - mutex_unlock(&pipe->lock); video_device_pipeline_stop(&video->video); vsp1_video_release_buffers(video); @@ -938,9 +927,9 @@ vsp1_video_get_format(struct file *file, void *fh, struct v4l2_format *format) if (format->type != video->queue.type) return -EINVAL; - mutex_lock(&video->lock); + guard(mutex)(&video->lock); + format->fmt.pix_mp = video->rwpf->format; - mutex_unlock(&video->lock); return 0; } @@ -972,19 +961,15 @@ vsp1_video_set_format(struct file *file, void *fh, struct v4l2_format *format) if (ret < 0) return ret; - mutex_lock(&video->lock); + guard(mutex)(&video->lock); - if (vb2_is_busy(&video->queue)) { - ret = -EBUSY; - goto done; - } + if (vb2_is_busy(&video->queue)) + return -EBUSY; video->rwpf->format = format->fmt.pix_mp; video->rwpf->fmtinfo = info; -done: - mutex_unlock(&video->lock); - return ret; + return 0; } static int @@ -1004,22 +989,16 @@ vsp1_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) * touching an entity in the pipeline can be activated or deactivated * once streaming is started. */ - mutex_lock(&mdev->graph_mutex); - - pipe = vsp1_video_pipeline_get(video); - if (IS_ERR(pipe)) { - mutex_unlock(&mdev->graph_mutex); - return PTR_ERR(pipe); - } + scoped_guard(mutex, &mdev->graph_mutex) { + pipe = vsp1_video_pipeline_get(video); + if (IS_ERR(pipe)) + return PTR_ERR(pipe); - ret = __video_device_pipeline_start(&video->video, &pipe->pipe); - if (ret < 0) { - mutex_unlock(&mdev->graph_mutex); - goto err_pipe; + ret = __video_device_pipeline_start(&video->video, &pipe->pipe); + if (ret < 0) + goto err_pipe; } - mutex_unlock(&mdev->graph_mutex); - /* * Verify that the configured format matches the output of the connected * subdev. @@ -1137,7 +1116,6 @@ static const struct media_entity_operations vsp1_video_media_ops = { void vsp1_video_suspend(struct vsp1_device *vsp1) { - unsigned long flags; unsigned int i; int ret; @@ -1157,10 +1135,10 @@ void vsp1_video_suspend(struct vsp1_device *vsp1) if (pipe == NULL) continue; - spin_lock_irqsave(&pipe->irqlock, flags); - if (pipe->state == VSP1_PIPELINE_RUNNING) - pipe->state = VSP1_PIPELINE_STOPPING; - spin_unlock_irqrestore(&pipe->irqlock, flags); + scoped_guard(spinlock_irqsave, &pipe->irqlock) { + if (pipe->state == VSP1_PIPELINE_RUNNING) + pipe->state = VSP1_PIPELINE_STOPPING; + } } for (i = 0; i < vsp1->info->wpf_count; ++i) { @@ -1184,7 +1162,6 @@ void vsp1_video_suspend(struct vsp1_device *vsp1) void vsp1_video_resume(struct vsp1_device *vsp1) { - unsigned long flags; unsigned int i; /* Resume all running pipelines. */ @@ -1205,10 +1182,10 @@ void vsp1_video_resume(struct vsp1_device *vsp1) */ pipe->configured = false; - spin_lock_irqsave(&pipe->irqlock, flags); - if (vsp1_pipeline_ready(pipe)) - vsp1_video_pipeline_run(pipe); - spin_unlock_irqrestore(&pipe->irqlock, flags); + scoped_guard(spinlock_irqsave, &pipe->irqlock) { + if (vsp1_pipeline_ready(pipe)) + vsp1_video_pipeline_run(pipe); + } } } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c index cd6c5592221b..0ec707d2913f 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c @@ -47,7 +47,6 @@ static int vsp1_wpf_set_rotation(struct vsp1_rwpf *wpf, unsigned int rotation) struct v4l2_mbus_framefmt *sink_format; struct v4l2_mbus_framefmt *source_format; bool rotate; - int ret = 0; /* * Only consider the 0°/180° from/to 90°/270° modifications, the rest @@ -58,19 +57,17 @@ static int vsp1_wpf_set_rotation(struct vsp1_rwpf *wpf, unsigned int rotation) return 0; /* Changing rotation isn't allowed when buffers are allocated. */ - mutex_lock(&video->lock); + guard(mutex)(&video->lock); - if (vb2_is_busy(&video->queue)) { - ret = -EBUSY; - goto done; - } + if (vb2_is_busy(&video->queue)) + return -EBUSY; sink_format = v4l2_subdev_state_get_format(wpf->entity.state, RWPF_PAD_SINK); source_format = v4l2_subdev_state_get_format(wpf->entity.state, RWPF_PAD_SOURCE); - mutex_lock(&wpf->entity.lock); + guard(mutex)(&wpf->entity.lock); if (rotate) { source_format->width = sink_format->height; @@ -82,11 +79,7 @@ static int vsp1_wpf_set_rotation(struct vsp1_rwpf *wpf, unsigned int rotation) wpf->flip.rotate = rotate; - mutex_unlock(&wpf->entity.lock); - -done: - mutex_unlock(&video->lock); - return ret; + return 0; } static int vsp1_wpf_s_ctrl(struct v4l2_ctrl *ctrl) @@ -118,9 +111,9 @@ static int vsp1_wpf_s_ctrl(struct v4l2_ctrl *ctrl) if (rotation == 180 || rotation == 270) flip ^= BIT(WPF_CTRL_VFLIP) | BIT(WPF_CTRL_HFLIP); - spin_lock_irq(&wpf->flip.lock); + guard(spinlock_irq)(&wpf->flip.lock); + wpf->flip.pending = flip; - spin_unlock_irq(&wpf->flip.lock); return 0; } @@ -373,13 +366,12 @@ static void wpf_configure_frame(struct vsp1_entity *entity, const unsigned int mask = BIT(WPF_CTRL_VFLIP) | BIT(WPF_CTRL_HFLIP); struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); - unsigned long flags; u32 outfmt; - spin_lock_irqsave(&wpf->flip.lock, flags); - wpf->flip.active = (wpf->flip.active & ~mask) - | (wpf->flip.pending & mask); - spin_unlock_irqrestore(&wpf->flip.lock, flags); + scoped_guard(spinlock_irqsave, &wpf->flip.lock) { + wpf->flip.active = (wpf->flip.active & ~mask) + | (wpf->flip.pending & mask); + } outfmt = (wpf->alpha << VI6_WPF_OUTFMT_PDV_SHIFT) | wpf->outfmt; diff --git a/drivers/media/platform/rockchip/rga/Kconfig b/drivers/media/platform/rockchip/rga/Kconfig index 727a0f6ea466..846e555829f3 100644 --- a/drivers/media/platform/rockchip/rga/Kconfig +++ b/drivers/media/platform/rockchip/rga/Kconfig @@ -3,6 +3,7 @@ config VIDEO_ROCKCHIP_RGA depends on V4L_MEM2MEM_DRIVERS depends on VIDEO_DEV depends on ARCH_ROCKCHIP || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG select VIDEOBUF2_DMA_SG select V4L2_MEM2MEM_DEV help diff --git a/drivers/media/platform/rockchip/rga/Makefile b/drivers/media/platform/rockchip/rga/Makefile index 1bbecdc3d8df..7326a548f3dc 100644 --- a/drivers/media/platform/rockchip/rga/Makefile +++ b/drivers/media/platform/rockchip/rga/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only -rockchip-rga-objs := rga.o rga-hw.o rga-buf.o +rockchip-rga-objs := rga.o rga-hw.o rga3-hw.o rga-buf.o obj-$(CONFIG_VIDEO_ROCKCHIP_RGA) += rockchip-rga.o diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c index bb575873f2b2..c0ea6003336b 100644 --- a/drivers/media/platform/rockchip/rga/rga-buf.c +++ b/drivers/media/platform/rockchip/rga/rga-buf.c @@ -12,9 +12,9 @@ #include <media/v4l2-ioctl.h> #include <media/v4l2-mem2mem.h> #include <media/videobuf2-dma-sg.h> +#include <media/videobuf2-dma-contig.h> #include <media/videobuf2-v4l2.h> -#include "rga-hw.h" #include "rga.h" static ssize_t fill_descriptors(struct rga_dma_desc *desc, size_t max_desc, @@ -79,11 +79,18 @@ static int rga_buf_init(struct vb2_buffer *vb) struct rockchip_rga *rga = ctx->rga; struct rga_frame *f = rga_get_frame(ctx, vb->vb2_queue->type); size_t n_desc = 0; + u32 size = 0; + u8 i; if (IS_ERR(f)) return PTR_ERR(f); - n_desc = DIV_ROUND_UP(f->size, PAGE_SIZE); + if (!rga_has_internal_iommu(rga)) + return 0; + + for (i = 0; i < f->pix.num_planes; i++) + size += f->pix.plane_fmt[i].sizeimage; + n_desc = DIV_ROUND_UP(size, PAGE_SIZE); rbuf->n_desc = n_desc; rbuf->dma_desc = dma_alloc_coherent(rga->dev, @@ -95,14 +102,19 @@ static int rga_buf_init(struct vb2_buffer *vb) return 0; } -static int get_plane_offset(struct rga_frame *f, int plane) +static int get_plane_offset(struct rga_frame *f, + const struct v4l2_format_info *info, + int plane) { + u32 stride = f->pix.plane_fmt[0].bytesperline; + if (plane == 0) return 0; if (plane == 1) - return f->width * f->height; + return stride * f->pix.height; if (plane == 2) - return f->width * f->height + (f->width * f->height / f->fmt->uv_factor); + return stride * f->pix.height + + (stride * f->pix.height / info->hdiv / info->vdiv); return -EINVAL; } @@ -117,7 +129,7 @@ static int rga_buf_prepare(struct vb2_buffer *vb) size_t curr_desc = 0; int i; const struct v4l2_format_info *info; - unsigned int offsets[VIDEO_MAX_PLANES]; + dma_addr_t dma_addrs[VIDEO_MAX_PLANES]; if (IS_ERR(f)) return PTR_ERR(f); @@ -132,27 +144,31 @@ static int rga_buf_prepare(struct vb2_buffer *vb) for (i = 0; i < vb->num_planes; i++) { vb2_set_plane_payload(vb, i, f->pix.plane_fmt[i].sizeimage); - /* Create local MMU table for RGA */ - n_desc = fill_descriptors(&rbuf->dma_desc[curr_desc], - rbuf->n_desc - curr_desc, - vb2_dma_sg_plane_desc(vb, i)); - if (n_desc < 0) { - v4l2_err(&ctx->rga->v4l2_dev, - "Failed to map video buffer to RGA\n"); - return n_desc; + if (rga_has_internal_iommu(ctx->rga)) { + /* Create local MMU table for RGA */ + n_desc = fill_descriptors(&rbuf->dma_desc[curr_desc], + rbuf->n_desc - curr_desc, + vb2_dma_sg_plane_desc(vb, i)); + if (n_desc < 0) { + v4l2_err(&ctx->rga->v4l2_dev, + "Failed to map video buffer to RGA\n"); + return n_desc; + } + dma_addrs[i] = curr_desc << PAGE_SHIFT; + curr_desc += n_desc; + } else { + dma_addrs[i] = vb2_dma_contig_plane_dma_addr(vb, i); } - offsets[i] = curr_desc << PAGE_SHIFT; - curr_desc += n_desc; } /* Fill the remaining planes */ - info = v4l2_format_info(f->fmt->fourcc); + info = v4l2_format_info(f->pix.pixelformat); for (i = info->mem_planes; i < info->comp_planes; i++) - offsets[i] = get_plane_offset(f, i); + dma_addrs[i] = dma_addrs[0] + get_plane_offset(f, info, i); - rbuf->offset.y_off = offsets[0]; - rbuf->offset.u_off = offsets[1]; - rbuf->offset.v_off = offsets[2]; + rbuf->dma_addrs.y_addr = dma_addrs[0]; + rbuf->dma_addrs.u_addr = dma_addrs[1]; + rbuf->dma_addrs.v_addr = dma_addrs[2]; return 0; } @@ -172,6 +188,9 @@ static void rga_buf_cleanup(struct vb2_buffer *vb) struct rga_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct rockchip_rga *rga = ctx->rga; + if (!rga_has_internal_iommu(rga)) + return; + dma_free_coherent(rga->dev, rbuf->n_desc * sizeof(*rbuf->dma_desc), rbuf->dma_desc, rbuf->dma_desc_pa); } @@ -193,6 +212,33 @@ static void rga_buf_return_buffers(struct vb2_queue *q, } } +static int rga_buf_prepare_streaming(struct vb2_queue *q) +{ + struct rga_ctx *ctx = vb2_get_drv_priv(q); + const struct rga_hw *hw = ctx->rga->hw; + int ret; + + /* It's safe to check the streaming state of the other queue, + * as the streamon ioctl's can't race due to the lock set in + * the queue_init function. + */ + if ((V4L2_TYPE_IS_OUTPUT(q->type) && + vb2_is_streaming(v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx))) || + (V4L2_TYPE_IS_CAPTURE(q->type) && + vb2_is_streaming(v4l2_m2m_get_src_vq(ctx->fh.m2m_ctx)))) { + /* + * As the other side is already streaming, + * check that the max scaling factor isn't exceeded. + */ + ret = rga_check_scaling(hw, &ctx->in.crop, &ctx->out.crop, + ctx->rotate); + if (ret < 0) + return ret; + } + + return 0; +} + static int rga_buf_start_streaming(struct vb2_queue *q, unsigned int count) { struct rga_ctx *ctx = vb2_get_drv_priv(q); @@ -228,6 +274,7 @@ const struct vb2_ops rga_qops = { .buf_prepare = rga_buf_prepare, .buf_queue = rga_buf_queue, .buf_cleanup = rga_buf_cleanup, + .prepare_streaming = rga_buf_prepare_streaming, .start_streaming = rga_buf_start_streaming, .stop_streaming = rga_buf_stop_streaming, }; diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index 43ed742a1649..be1bc8ddbd03 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -16,11 +16,11 @@ enum e_rga_start_pos { RB = 3, }; -struct rga_corners_addr_offset { - struct rga_addr_offset left_top; - struct rga_addr_offset right_top; - struct rga_addr_offset left_bottom; - struct rga_addr_offset right_bottom; +struct rga_corners_addrs { + struct rga_addrs left_top; + struct rga_addrs right_top; + struct rga_addrs left_bottom; + struct rga_addrs right_bottom; }; static unsigned int rga_get_scaling(unsigned int src, unsigned int dst) @@ -36,48 +36,57 @@ static unsigned int rga_get_scaling(unsigned int src, unsigned int dst) return (src > dst) ? ((dst << 16) / src) : ((src << 16) / dst); } -static struct rga_corners_addr_offset -rga_get_addr_offset(struct rga_frame *frm, struct rga_addr_offset *offset, - unsigned int x, unsigned int y, unsigned int w, unsigned int h) +static struct rga_corners_addrs +rga_get_corner_addrs(struct rga_frame *frm, struct rga_addrs *addrs, + unsigned int x, unsigned int y, unsigned int w, unsigned int h) { - struct rga_corners_addr_offset offsets; - struct rga_addr_offset *lt, *lb, *rt, *rb; + struct rga_corners_addrs corner_addrs; + struct rga_addrs *lt, *lb, *rt, *rb; + const struct v4l2_format_info *format_info; unsigned int x_div = 0, - y_div = 0, uv_stride = 0, pixel_width = 0; + y_div = 0, y_stride = 0, uv_stride = 0, pixel_width = 0; - lt = &offsets.left_top; - lb = &offsets.left_bottom; - rt = &offsets.right_top; - rb = &offsets.right_bottom; + lt = &corner_addrs.left_top; + lb = &corner_addrs.left_bottom; + rt = &corner_addrs.right_top; + rb = &corner_addrs.right_bottom; - x_div = frm->fmt->x_div; - y_div = frm->fmt->y_div; - uv_stride = frm->stride / x_div; - pixel_width = frm->stride / frm->width; - - lt->y_off = offset->y_off + y * frm->stride + x * pixel_width; - lt->u_off = offset->u_off + (y / y_div) * uv_stride + x / x_div; - lt->v_off = offset->v_off + (y / y_div) * uv_stride + x / x_div; - - lb->y_off = lt->y_off + (h - 1) * frm->stride; - lb->u_off = lt->u_off + (h / y_div - 1) * uv_stride; - lb->v_off = lt->v_off + (h / y_div - 1) * uv_stride; - - rt->y_off = lt->y_off + (w - 1) * pixel_width; - rt->u_off = lt->u_off + w / x_div - 1; - rt->v_off = lt->v_off + w / x_div - 1; - - rb->y_off = lb->y_off + (w - 1) * pixel_width; - rb->u_off = lb->u_off + w / x_div - 1; - rb->v_off = lb->v_off + w / x_div - 1; - - return offsets; + format_info = v4l2_format_info(frm->pix.pixelformat); + /* x_div is only used for the u/v planes. + * When the format doesn't have these, use 1 to avoid a division by zero. + */ + if (format_info->bpp[1]) + x_div = format_info->hdiv * format_info->bpp_div[1] / + format_info->bpp[1]; + else + x_div = 1; + y_div = format_info->vdiv; + y_stride = frm->pix.plane_fmt[0].bytesperline; + uv_stride = y_stride / x_div; + pixel_width = y_stride / frm->pix.width; + + lt->y_addr = addrs->y_addr + y * y_stride + x * pixel_width; + lt->u_addr = addrs->u_addr + (y / y_div) * uv_stride + x / x_div; + lt->v_addr = addrs->v_addr + (y / y_div) * uv_stride + x / x_div; + + lb->y_addr = lt->y_addr + (h - 1) * y_stride; + lb->u_addr = lt->u_addr + (h / y_div - 1) * uv_stride; + lb->v_addr = lt->v_addr + (h / y_div - 1) * uv_stride; + + rt->y_addr = lt->y_addr + (w - 1) * pixel_width; + rt->u_addr = lt->u_addr + w / x_div - 1; + rt->v_addr = lt->v_addr + w / x_div - 1; + + rb->y_addr = lb->y_addr + (w - 1) * pixel_width; + rb->u_addr = lb->u_addr + w / x_div - 1; + rb->v_addr = lb->v_addr + w / x_div - 1; + + return corner_addrs; } -static struct rga_addr_offset *rga_lookup_draw_pos(struct - rga_corners_addr_offset - * offsets, u32 rotate_mode, - u32 mirr_mode) +static struct rga_addrs *rga_lookup_draw_pos(struct rga_corners_addrs *corner_addrs, + u32 rotate_mode, + u32 mirr_mode) { static enum e_rga_start_pos rot_mir_point_matrix[4][4] = { { @@ -94,18 +103,18 @@ static struct rga_addr_offset *rga_lookup_draw_pos(struct }, }; - if (!offsets) + if (!corner_addrs) return NULL; switch (rot_mir_point_matrix[rotate_mode][mirr_mode]) { case LT: - return &offsets->left_top; + return &corner_addrs->left_top; case LB: - return &offsets->left_bottom; + return &corner_addrs->left_bottom; case RT: - return &offsets->right_top; + return &corner_addrs->right_top; case RB: - return &offsets->right_bottom; + return &corner_addrs->right_bottom; } return NULL; @@ -113,8 +122,7 @@ static struct rga_addr_offset *rga_lookup_draw_pos(struct static void rga_cmd_set_src_addr(struct rga_ctx *ctx, dma_addr_t dma_addr) { - struct rockchip_rga *rga = ctx->rga; - u32 *dest = rga->cmdbuf_virt; + u32 *dest = ctx->cmdbuf_virt; unsigned int reg; reg = RGA_MMU_SRC_BASE - RGA_MODE_BASE_REG; @@ -126,8 +134,7 @@ static void rga_cmd_set_src_addr(struct rga_ctx *ctx, dma_addr_t dma_addr) static void rga_cmd_set_src1_addr(struct rga_ctx *ctx, dma_addr_t dma_addr) { - struct rockchip_rga *rga = ctx->rga; - u32 *dest = rga->cmdbuf_virt; + u32 *dest = ctx->cmdbuf_virt; unsigned int reg; reg = RGA_MMU_SRC1_BASE - RGA_MODE_BASE_REG; @@ -139,8 +146,7 @@ static void rga_cmd_set_src1_addr(struct rga_ctx *ctx, dma_addr_t dma_addr) static void rga_cmd_set_dst_addr(struct rga_ctx *ctx, dma_addr_t dma_addr) { - struct rockchip_rga *rga = ctx->rga; - u32 *dest = rga->cmdbuf_virt; + u32 *dest = ctx->cmdbuf_virt; unsigned int reg; reg = RGA_MMU_DST_BASE - RGA_MODE_BASE_REG; @@ -153,7 +159,7 @@ static void rga_cmd_set_dst_addr(struct rga_ctx *ctx, dma_addr_t dma_addr) static void rga_cmd_set_trans_info(struct rga_ctx *ctx) { struct rockchip_rga *rga = ctx->rga; - u32 *dest = rga->cmdbuf_virt; + u32 *dest = ctx->cmdbuf_virt; unsigned int scale_dst_w, scale_dst_h; unsigned int src_h, src_w, dst_h, dst_w; union rga_src_info src_info; @@ -164,6 +170,9 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) union rga_src_act_info src_act_info; union rga_dst_vir_info dst_vir_info; union rga_dst_act_info dst_act_info; + u32 in_stride, out_stride; + struct rga_fmt *in_fmt = ctx->in.fmt; + struct rga_fmt *out_fmt = ctx->out.fmt; src_h = ctx->in.crop.height; src_w = ctx->in.crop.width; @@ -179,19 +188,19 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) dst_vir_info.val = dest[(RGA_DST_VIR_INFO - RGA_MODE_BASE_REG) >> 2]; dst_act_info.val = dest[(RGA_DST_ACT_INFO - RGA_MODE_BASE_REG) >> 2]; - src_info.data.format = ctx->in.fmt->hw_format; - src_info.data.swap = ctx->in.fmt->color_swap; - dst_info.data.format = ctx->out.fmt->hw_format; - dst_info.data.swap = ctx->out.fmt->color_swap; + src_info.data.format = in_fmt->hw_format; + src_info.data.swap = in_fmt->color_swap; + dst_info.data.format = out_fmt->hw_format; + dst_info.data.swap = out_fmt->color_swap; /* * CSC mode must only be set when the colorspace families differ between * input and output. It must remain unset (zeroed) if both are the same. */ - if (RGA_COLOR_FMT_IS_YUV(ctx->in.fmt->hw_format) && - RGA_COLOR_FMT_IS_RGB(ctx->out.fmt->hw_format)) { - switch (ctx->in.colorspace) { + if (RGA_COLOR_FMT_IS_YUV(in_fmt->hw_format) && + RGA_COLOR_FMT_IS_RGB(out_fmt->hw_format)) { + switch (ctx->in.pix.colorspace) { case V4L2_COLORSPACE_REC709: src_info.data.csc_mode = RGA_SRC_CSC_MODE_BT709_R0; break; @@ -201,9 +210,9 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) } } - if (RGA_COLOR_FMT_IS_RGB(ctx->in.fmt->hw_format) && - RGA_COLOR_FMT_IS_YUV(ctx->out.fmt->hw_format)) { - switch (ctx->out.colorspace) { + if (RGA_COLOR_FMT_IS_RGB(in_fmt->hw_format) && + RGA_COLOR_FMT_IS_YUV(out_fmt->hw_format)) { + switch (ctx->out.pix.colorspace) { case V4L2_COLORSPACE_REC709: dst_info.data.csc_mode = RGA_SRC_CSC_MODE_BT709_R0; break; @@ -286,13 +295,15 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) * Calculate the framebuffer virtual strides and active size, * note that the step of vir_stride / vir_width is 4 byte words */ - src_vir_info.data.vir_stride = ctx->in.stride >> 2; - src_vir_info.data.vir_width = ctx->in.stride >> 2; + in_stride = ctx->in.pix.plane_fmt[0].bytesperline; + src_vir_info.data.vir_stride = in_stride >> 2; + src_vir_info.data.vir_width = in_stride >> 2; src_act_info.data.act_height = src_h - 1; src_act_info.data.act_width = src_w - 1; - dst_vir_info.data.vir_stride = ctx->out.stride >> 2; + out_stride = ctx->out.pix.plane_fmt[0].bytesperline; + dst_vir_info.data.vir_stride = out_stride >> 2; dst_act_info.data.act_height = dst_h - 1; dst_act_info.data.act_width = dst_w - 1; @@ -310,11 +321,10 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) } static void rga_cmd_set_src_info(struct rga_ctx *ctx, - struct rga_addr_offset *offset) + struct rga_addrs *addrs) { - struct rga_corners_addr_offset src_offsets; - struct rockchip_rga *rga = ctx->rga; - u32 *dest = rga->cmdbuf_virt; + struct rga_corners_addrs src_corner_addrs; + u32 *dest = ctx->cmdbuf_virt; unsigned int src_h, src_w, src_x, src_y; src_h = ctx->in.crop.height; @@ -325,24 +335,23 @@ static void rga_cmd_set_src_info(struct rga_ctx *ctx, /* * Calculate the source framebuffer base address with offset pixel. */ - src_offsets = rga_get_addr_offset(&ctx->in, offset, - src_x, src_y, src_w, src_h); + src_corner_addrs = rga_get_corner_addrs(&ctx->in, addrs, + src_x, src_y, src_w, src_h); dest[(RGA_SRC_Y_RGB_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] = - src_offsets.left_top.y_off; + src_corner_addrs.left_top.y_addr; dest[(RGA_SRC_CB_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] = - src_offsets.left_top.u_off; + src_corner_addrs.left_top.u_addr; dest[(RGA_SRC_CR_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] = - src_offsets.left_top.v_off; + src_corner_addrs.left_top.v_addr; } static void rga_cmd_set_dst_info(struct rga_ctx *ctx, - struct rga_addr_offset *offset) + struct rga_addrs *addrs) { - struct rga_addr_offset *dst_offset; - struct rga_corners_addr_offset offsets; - struct rockchip_rga *rga = ctx->rga; - u32 *dest = rga->cmdbuf_virt; + struct rga_addrs *dst_addrs; + struct rga_corners_addrs corner_addrs; + u32 *dest = ctx->cmdbuf_virt; unsigned int dst_h, dst_w, dst_x, dst_y; unsigned int mir_mode = 0; unsigned int rot_mode = 0; @@ -375,21 +384,20 @@ static void rga_cmd_set_dst_info(struct rga_ctx *ctx, /* * Configure the dest framebuffer base address with pixel offset. */ - offsets = rga_get_addr_offset(&ctx->out, offset, dst_x, dst_y, dst_w, dst_h); - dst_offset = rga_lookup_draw_pos(&offsets, rot_mode, mir_mode); + corner_addrs = rga_get_corner_addrs(&ctx->out, addrs, dst_x, dst_y, dst_w, dst_h); + dst_addrs = rga_lookup_draw_pos(&corner_addrs, rot_mode, mir_mode); dest[(RGA_DST_Y_RGB_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] = - dst_offset->y_off; + dst_addrs->y_addr; dest[(RGA_DST_CB_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] = - dst_offset->u_off; + dst_addrs->u_addr; dest[(RGA_DST_CR_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] = - dst_offset->v_off; + dst_addrs->v_addr; } static void rga_cmd_set_mode(struct rga_ctx *ctx) { - struct rockchip_rga *rga = ctx->rga; - u32 *dest = rga->cmdbuf_virt; + u32 *dest = ctx->cmdbuf_virt; union rga_mode_ctrl mode; union rga_alpha_ctrl0 alpha_ctrl0; union rga_alpha_ctrl1 alpha_ctrl1; @@ -414,8 +422,6 @@ static void rga_cmd_set(struct rga_ctx *ctx, { struct rockchip_rga *rga = ctx->rga; - memset(rga->cmdbuf_virt, 0, RGA_CMDBUF_SIZE * 4); - rga_cmd_set_src_addr(ctx, src->dma_desc_pa); /* * Due to hardware bug, @@ -424,21 +430,27 @@ static void rga_cmd_set(struct rga_ctx *ctx, rga_cmd_set_src1_addr(ctx, dst->dma_desc_pa); rga_cmd_set_dst_addr(ctx, dst->dma_desc_pa); - rga_cmd_set_mode(ctx); - rga_cmd_set_src_info(ctx, &src->offset); - rga_cmd_set_dst_info(ctx, &dst->offset); - rga_cmd_set_trans_info(ctx); + rga_cmd_set_src_info(ctx, &src->dma_addrs); + rga_cmd_set_dst_info(ctx, &dst->dma_addrs); - rga_write(rga, RGA_CMD_BASE, rga->cmdbuf_phy); + rga_write(rga, RGA_CMD_BASE, ctx->cmdbuf_phy); /* sync CMD buf for RGA */ - dma_sync_single_for_device(rga->dev, rga->cmdbuf_phy, - PAGE_SIZE, DMA_BIDIRECTIONAL); + dma_sync_single_for_device(rga->dev, ctx->cmdbuf_phy, + PAGE_SIZE, DMA_BIDIRECTIONAL); } -void rga_hw_start(struct rockchip_rga *rga, - struct rga_vb_buffer *src, struct rga_vb_buffer *dst) +static void rga_hw_setup_cmdbuf(struct rga_ctx *ctx) +{ + memset(ctx->cmdbuf_virt, 0, RGA_CMDBUF_SIZE); + + rga_cmd_set_mode(ctx); + rga_cmd_set_trans_info(ctx); +} + +static void rga_hw_start(struct rockchip_rga *rga, + struct rga_vb_buffer *src, struct rga_vb_buffer *dst) { struct rga_ctx *ctx = rga->curr; @@ -452,3 +464,152 @@ void rga_hw_start(struct rockchip_rga *rga, rga_write(rga, RGA_CMD_CTRL, 0x1); } + +static bool rga_handle_irq(struct rockchip_rga *rga) +{ + int intr; + + intr = rga_read(rga, RGA_INT) & 0xf; + + rga_mod(rga, RGA_INT, intr << 4, 0xf << 4); + + return intr & RGA_INT_COMMAND_FINISHED; +} + +static void rga_get_version(struct rockchip_rga *rga) +{ + rga->version.major = (rga_read(rga, RGA_VERSION_INFO) >> 24) & 0xFF; + rga->version.minor = (rga_read(rga, RGA_VERSION_INFO) >> 20) & 0x0F; +} + +static struct rga_fmt formats[] = { + { + .fourcc = V4L2_PIX_FMT_ARGB32, + .color_swap = RGA_COLOR_ALPHA_SWAP, + .hw_format = RGA_COLOR_FMT_ABGR8888, + }, + { + .fourcc = V4L2_PIX_FMT_ABGR32, + .color_swap = RGA_COLOR_RB_SWAP, + .hw_format = RGA_COLOR_FMT_ABGR8888, + }, + { + .fourcc = V4L2_PIX_FMT_XBGR32, + .color_swap = RGA_COLOR_RB_SWAP, + .hw_format = RGA_COLOR_FMT_XBGR8888, + }, + { + .fourcc = V4L2_PIX_FMT_RGB24, + .color_swap = RGA_COLOR_NONE_SWAP, + .hw_format = RGA_COLOR_FMT_RGB888, + }, + { + .fourcc = V4L2_PIX_FMT_BGR24, + .color_swap = RGA_COLOR_RB_SWAP, + .hw_format = RGA_COLOR_FMT_RGB888, + }, + { + .fourcc = V4L2_PIX_FMT_ARGB444, + .color_swap = RGA_COLOR_RB_SWAP, + .hw_format = RGA_COLOR_FMT_ABGR4444, + }, + { + .fourcc = V4L2_PIX_FMT_ARGB555, + .color_swap = RGA_COLOR_RB_SWAP, + .hw_format = RGA_COLOR_FMT_ABGR1555, + }, + { + .fourcc = V4L2_PIX_FMT_RGB565, + .color_swap = RGA_COLOR_RB_SWAP, + .hw_format = RGA_COLOR_FMT_BGR565, + }, + { + .fourcc = V4L2_PIX_FMT_NV21, + .color_swap = RGA_COLOR_UV_SWAP, + .hw_format = RGA_COLOR_FMT_YUV420SP, + }, + { + .fourcc = V4L2_PIX_FMT_NV61, + .color_swap = RGA_COLOR_UV_SWAP, + .hw_format = RGA_COLOR_FMT_YUV422SP, + }, + { + .fourcc = V4L2_PIX_FMT_NV12, + .color_swap = RGA_COLOR_NONE_SWAP, + .hw_format = RGA_COLOR_FMT_YUV420SP, + }, + { + .fourcc = V4L2_PIX_FMT_NV12M, + .color_swap = RGA_COLOR_NONE_SWAP, + .hw_format = RGA_COLOR_FMT_YUV420SP, + }, + { + .fourcc = V4L2_PIX_FMT_NV16, + .color_swap = RGA_COLOR_NONE_SWAP, + .hw_format = RGA_COLOR_FMT_YUV422SP, + }, + { + .fourcc = V4L2_PIX_FMT_YUV420, + .color_swap = RGA_COLOR_NONE_SWAP, + .hw_format = RGA_COLOR_FMT_YUV420P, + }, + { + .fourcc = V4L2_PIX_FMT_YUV422P, + .color_swap = RGA_COLOR_NONE_SWAP, + .hw_format = RGA_COLOR_FMT_YUV422P, + }, + { + .fourcc = V4L2_PIX_FMT_YVU420, + .color_swap = RGA_COLOR_UV_SWAP, + .hw_format = RGA_COLOR_FMT_YUV420P, + }, +}; + +static void *rga_adjust_and_map_format(struct rga_ctx *ctx, + struct v4l2_pix_format_mplane *format, + bool is_output) +{ + unsigned int i; + + if (!format) + return &formats[0]; + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + if (formats[i].fourcc == format->pixelformat) + return &formats[i]; + } + + format->pixelformat = formats[0].fourcc; + return &formats[0]; +} + +static int rga_enum_format(struct v4l2_fmtdesc *f) +{ + if (f->index >= ARRAY_SIZE(formats)) + return -EINVAL; + + f->pixelformat = formats[f->index].fourcc; + return 0; +} + +const struct rga_hw rga2_hw = { + .card_type = "rga2", + .has_internal_iommu = true, + .cmdbuf_size = RGA_CMDBUF_SIZE, + .min_width = MIN_WIDTH, + .max_width = MAX_WIDTH, + .min_height = MIN_HEIGHT, + .max_height = MAX_HEIGHT, + .max_scaling_factor = MAX_SCALING_FACTOR, + .stride_alignment = 4, + .features = RGA_FEATURE_FLIP + | RGA_FEATURE_ROTATE + | RGA_FEATURE_BG_COLOR, + + .setup_cmdbuf = rga_hw_setup_cmdbuf, + .start = rga_hw_start, + .handle_irq = rga_handle_irq, + .get_version = rga_get_version, + .adjust_and_map_format = rga_adjust_and_map_format, + .enum_format = rga_enum_format, +}; diff --git a/drivers/media/platform/rockchip/rga/rga-hw.h b/drivers/media/platform/rockchip/rga/rga-hw.h index cc6bd7f5b030..14ffa5ebd453 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.h +++ b/drivers/media/platform/rockchip/rga/rga-hw.h @@ -6,7 +6,9 @@ #ifndef __RGA_HW_H__ #define __RGA_HW_H__ -#define RGA_CMDBUF_SIZE 0x20 +#include <linux/types.h> + +#define RGA_CMDBUF_SIZE 0x80 /* Hardware limits */ #define MAX_WIDTH 8192 @@ -14,9 +16,7 @@ #define MIN_WIDTH 34 #define MIN_HEIGHT 34 - -#define DEFAULT_WIDTH 100 -#define DEFAULT_HEIGHT 100 +#define MAX_SCALING_FACTOR 16 #define RGA_TIMEOUT 500 @@ -178,6 +178,8 @@ #define RGA_ALPHA_COLOR_NORMAL 0 #define RGA_ALPHA_COLOR_MULTIPLY_CAL 1 +#define RGA_INT_COMMAND_FINISHED 4 + /* Registers union */ union rga_mode_ctrl { unsigned int val; @@ -431,4 +433,10 @@ union rga_pat_con { } data; }; +struct rga_fmt { + u32 fourcc; + u8 color_swap; + u8 hw_format; +}; + #endif diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index fea63b94c5f3..b3cb6bf8eb86 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -23,9 +23,9 @@ #include <media/v4l2-ioctl.h> #include <media/v4l2-mem2mem.h> #include <media/videobuf2-dma-sg.h> +#include <media/videobuf2-dma-contig.h> #include <media/videobuf2-v4l2.h> -#include "rga-hw.h" #include "rga.h" static int debug; @@ -39,6 +39,11 @@ static void device_run(void *prv) unsigned long flags; spin_lock_irqsave(&rga->ctrl_lock, flags); + if (ctx->cmdbuf_dirty) { + ctx->cmdbuf_dirty = false; + rga->hw->setup_cmdbuf(ctx); + } + spin_unlock_irqrestore(&rga->ctrl_lock, flags); rga->curr = ctx; @@ -47,21 +52,14 @@ static void device_run(void *prv) dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - rga_hw_start(rga, vb_to_rga(src), vb_to_rga(dst)); - - spin_unlock_irqrestore(&rga->ctrl_lock, flags); + rga->hw->start(rga, vb_to_rga(src), vb_to_rga(dst)); } static irqreturn_t rga_isr(int irq, void *prv) { struct rockchip_rga *rga = prv; - int intr; - - intr = rga_read(rga, RGA_INT) & 0xf; - rga_mod(rga, RGA_INT, intr << 4, 0xf << 4); - - if (intr & 0x04) { + if (rga->hw->handle_irq(rga)) { struct vb2_v4l2_buffer *src, *dst; struct rga_ctx *ctx = rga->curr; @@ -101,7 +99,10 @@ queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) src_vq->io_modes = VB2_MMAP | VB2_DMABUF; src_vq->drv_priv = ctx; src_vq->ops = &rga_qops; - src_vq->mem_ops = &vb2_dma_sg_memops; + if (rga_has_internal_iommu(ctx->rga)) + src_vq->mem_ops = &vb2_dma_sg_memops; + else + src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->gfp_flags = __GFP_DMA32; src_vq->buf_struct_size = sizeof(struct rga_vb_buffer); src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; @@ -116,7 +117,10 @@ queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; dst_vq->drv_priv = ctx; dst_vq->ops = &rga_qops; - dst_vq->mem_ops = &vb2_dma_sg_memops; + if (rga_has_internal_iommu(ctx->rga)) + dst_vq->mem_ops = &vb2_dma_sg_memops; + else + dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->gfp_flags = __GFP_DMA32; dst_vq->buf_struct_size = sizeof(struct rga_vb_buffer); dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; @@ -130,7 +134,9 @@ static int rga_s_ctrl(struct v4l2_ctrl *ctrl) { struct rga_ctx *ctx = container_of(ctrl->handler, struct rga_ctx, ctrl_handler); + const struct rga_hw *hw = ctx->rga->hw; unsigned long flags; + int ret = 0; spin_lock_irqsave(&ctx->rga->ctrl_lock, flags); switch (ctrl->id) { @@ -141,14 +147,24 @@ static int rga_s_ctrl(struct v4l2_ctrl *ctrl) ctx->vflip = ctrl->val; break; case V4L2_CID_ROTATE: + if (vb2_is_streaming(v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx)) && + vb2_is_streaming(v4l2_m2m_get_src_vq(ctx->fh.m2m_ctx))) { + ret = rga_check_scaling(hw, &ctx->in.crop, + &ctx->out.crop, ctrl->val); + if (ret < 0) + goto s_ctrl_done; + } ctx->rotate = ctrl->val; break; case V4L2_CID_BG_COLOR: ctx->fill_color = ctrl->val; break; } + ctx->cmdbuf_dirty = true; + +s_ctrl_done: spin_unlock_irqrestore(&ctx->rga->ctrl_lock, flags); - return 0; + return ret; } static const struct v4l2_ctrl_ops rga_ctrl_ops = { @@ -161,17 +177,21 @@ static int rga_setup_ctrls(struct rga_ctx *ctx) v4l2_ctrl_handler_init(&ctx->ctrl_handler, 4); - v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); + if (rga->hw->features & RGA_FEATURE_FLIP) { + v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + } - v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops, - V4L2_CID_ROTATE, 0, 270, 90, 0); + if (rga->hw->features & RGA_FEATURE_ROTATE) + v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops, + V4L2_CID_ROTATE, 0, 270, 90, 0); - v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops, - V4L2_CID_BG_COLOR, 0, 0xffffffff, 1, 0); + if (rga->hw->features & RGA_FEATURE_BG_COLOR) + v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops, + V4L2_CID_BG_COLOR, 0, 0xffffffff, 1, 0); if (ctx->ctrl_handler.error) { int err = ctx->ctrl_handler.error; @@ -184,176 +204,37 @@ static int rga_setup_ctrls(struct rga_ctx *ctx) return 0; } -static struct rga_fmt formats[] = { - { - .fourcc = V4L2_PIX_FMT_ARGB32, - .color_swap = RGA_COLOR_ALPHA_SWAP, - .hw_format = RGA_COLOR_FMT_ABGR8888, - .depth = 32, - .uv_factor = 1, - .y_div = 1, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_ABGR32, - .color_swap = RGA_COLOR_RB_SWAP, - .hw_format = RGA_COLOR_FMT_ABGR8888, - .depth = 32, - .uv_factor = 1, - .y_div = 1, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_XBGR32, - .color_swap = RGA_COLOR_RB_SWAP, - .hw_format = RGA_COLOR_FMT_XBGR8888, - .depth = 32, - .uv_factor = 1, - .y_div = 1, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_RGB24, - .color_swap = RGA_COLOR_NONE_SWAP, - .hw_format = RGA_COLOR_FMT_RGB888, - .depth = 24, - .uv_factor = 1, - .y_div = 1, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_BGR24, - .color_swap = RGA_COLOR_RB_SWAP, - .hw_format = RGA_COLOR_FMT_RGB888, - .depth = 24, - .uv_factor = 1, - .y_div = 1, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_ARGB444, - .color_swap = RGA_COLOR_RB_SWAP, - .hw_format = RGA_COLOR_FMT_ABGR4444, - .depth = 16, - .uv_factor = 1, - .y_div = 1, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_ARGB555, - .color_swap = RGA_COLOR_RB_SWAP, - .hw_format = RGA_COLOR_FMT_ABGR1555, - .depth = 16, - .uv_factor = 1, - .y_div = 1, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_RGB565, - .color_swap = RGA_COLOR_RB_SWAP, - .hw_format = RGA_COLOR_FMT_BGR565, - .depth = 16, - .uv_factor = 1, - .y_div = 1, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_NV21, - .color_swap = RGA_COLOR_UV_SWAP, - .hw_format = RGA_COLOR_FMT_YUV420SP, - .depth = 12, - .uv_factor = 4, - .y_div = 2, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_NV61, - .color_swap = RGA_COLOR_UV_SWAP, - .hw_format = RGA_COLOR_FMT_YUV422SP, - .depth = 16, - .uv_factor = 2, - .y_div = 1, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_NV12, - .color_swap = RGA_COLOR_NONE_SWAP, - .hw_format = RGA_COLOR_FMT_YUV420SP, - .depth = 12, - .uv_factor = 4, - .y_div = 2, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_NV12M, - .color_swap = RGA_COLOR_NONE_SWAP, - .hw_format = RGA_COLOR_FMT_YUV420SP, - .depth = 12, - .uv_factor = 4, - .y_div = 2, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_NV16, - .color_swap = RGA_COLOR_NONE_SWAP, - .hw_format = RGA_COLOR_FMT_YUV422SP, - .depth = 16, - .uv_factor = 2, - .y_div = 1, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_YUV420, - .color_swap = RGA_COLOR_NONE_SWAP, - .hw_format = RGA_COLOR_FMT_YUV420P, - .depth = 12, - .uv_factor = 4, - .y_div = 2, - .x_div = 2, - }, - { - .fourcc = V4L2_PIX_FMT_YUV422P, - .color_swap = RGA_COLOR_NONE_SWAP, - .hw_format = RGA_COLOR_FMT_YUV422P, - .depth = 16, - .uv_factor = 2, - .y_div = 1, - .x_div = 2, - }, - { - .fourcc = V4L2_PIX_FMT_YVU420, - .color_swap = RGA_COLOR_UV_SWAP, - .hw_format = RGA_COLOR_FMT_YUV420P, - .depth = 12, - .uv_factor = 4, - .y_div = 2, - .x_div = 2, - }, -}; - -#define NUM_FORMATS ARRAY_SIZE(formats) +static bool check_scaling_factor(const struct rga_hw *hw, u32 src_size, + u32 dst_size) +{ + if (src_size < dst_size) + return src_size * hw->max_scaling_factor >= dst_size; + else + return dst_size * hw->max_scaling_factor >= src_size; +} -static struct rga_fmt *rga_fmt_find(u32 pixelformat) +int rga_check_scaling(const struct rga_hw *hw, const struct v4l2_rect *crop_in, + const struct v4l2_rect *crop_out, u32 rotate) { - unsigned int i; + u32 scaled_width; + u32 scaled_height; - for (i = 0; i < NUM_FORMATS; i++) { - if (formats[i].fourcc == pixelformat) - return &formats[i]; + if (rotate == 90 || rotate == 270) { + scaled_width = crop_out->height; + scaled_height = crop_out->width; + } else { + scaled_width = crop_out->width; + scaled_height = crop_out->height; } - return NULL; -} -static struct rga_frame def_frame = { - .width = DEFAULT_WIDTH, - .height = DEFAULT_HEIGHT, - .colorspace = V4L2_COLORSPACE_DEFAULT, - .crop.left = 0, - .crop.top = 0, - .crop.width = DEFAULT_WIDTH, - .crop.height = DEFAULT_HEIGHT, - .fmt = &formats[0], -}; + if (!check_scaling_factor(hw, crop_in->width, scaled_width)) + return -EINVAL; + + if (!check_scaling_factor(hw, crop_in->height, scaled_height)) + return -EINVAL; + + return 0; +} struct rga_frame *rga_get_frame(struct rga_ctx *ctx, enum v4l2_buf_type type) { @@ -369,30 +250,50 @@ static int rga_open(struct file *file) struct rockchip_rga *rga = video_drvdata(file); struct rga_ctx *ctx = NULL; int ret = 0; + u32 def_width = clamp(DEFAULT_WIDTH, rga->hw->min_width, rga->hw->max_width); + u32 def_height = clamp(DEFAULT_HEIGHT, rga->hw->min_height, rga->hw->max_height); + struct rga_frame def_frame = { + .crop.left = 0, + .crop.top = 0, + .crop.width = def_width, + .crop.height = def_height, + }; ctx = kzalloc_obj(*ctx); if (!ctx) return -ENOMEM; + + /* Create CMD buffer */ + ctx->cmdbuf_virt = dma_alloc_attrs(rga->dev, rga->hw->cmdbuf_size, + &ctx->cmdbuf_phy, GFP_KERNEL, + DMA_ATTR_WRITE_COMBINE); + if (!ctx->cmdbuf_virt) { + ret = -ENOMEM; + goto rel_ctx; + } + ctx->cmdbuf_dirty = true; + ctx->rga = rga; /* Set default formats */ ctx->in = def_frame; ctx->out = def_frame; - v4l2_fill_pixfmt_mp(&ctx->in.pix, - ctx->in.fmt->fourcc, ctx->out.width, ctx->out.height); - v4l2_fill_pixfmt_mp(&ctx->out.pix, - ctx->out.fmt->fourcc, ctx->out.width, ctx->out.height); + ctx->in.fmt = rga->hw->adjust_and_map_format(ctx, &ctx->in.pix, true); + v4l2_fill_pixfmt_mp_aligned(&ctx->in.pix, ctx->in.pix.pixelformat, + def_width, def_height, rga->hw->stride_alignment); + ctx->out.fmt = + rga->hw->adjust_and_map_format(ctx, &ctx->out.pix, false); + v4l2_fill_pixfmt_mp_aligned(&ctx->out.pix, ctx->out.pix.pixelformat, + def_width, def_height, rga->hw->stride_alignment); if (mutex_lock_interruptible(&rga->mutex)) { - kfree(ctx); - return -ERESTARTSYS; + ret = -ERESTARTSYS; + goto rel_cmdbuf; } ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(rga->m2m_dev, ctx, &queue_init); if (IS_ERR(ctx->fh.m2m_ctx)) { ret = PTR_ERR(ctx->fh.m2m_ctx); - mutex_unlock(&rga->mutex); - kfree(ctx); - return ret; + goto unlock_mutex; } v4l2_fh_init(&ctx->fh, video_devdata(file)); v4l2_fh_add(&ctx->fh, file); @@ -406,6 +307,15 @@ static int rga_open(struct file *file) mutex_unlock(&rga->mutex); return 0; + +unlock_mutex: + mutex_unlock(&rga->mutex); +rel_cmdbuf: + dma_free_attrs(rga->dev, rga->hw->cmdbuf_size, ctx->cmdbuf_virt, + ctx->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE); +rel_ctx: + kfree(ctx); + return ret; } static int rga_release(struct file *file) @@ -420,6 +330,10 @@ static int rga_release(struct file *file) v4l2_ctrl_handler_free(&ctx->ctrl_handler); v4l2_fh_del(&ctx->fh, file); v4l2_fh_exit(&ctx->fh); + + dma_free_attrs(rga->dev, rga->hw->cmdbuf_size, ctx->cmdbuf_virt, + ctx->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE); + kfree(ctx); mutex_unlock(&rga->mutex); @@ -439,8 +353,10 @@ static const struct v4l2_file_operations rga_fops = { static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { + struct rockchip_rga *rga = video_drvdata(file); + strscpy(cap->driver, RGA_NAME, sizeof(cap->driver)); - strscpy(cap->card, "rockchip-rga", sizeof(cap->card)); + strscpy(cap->card, rga->hw->card_type, sizeof(cap->card)); strscpy(cap->bus_info, "platform:rga", sizeof(cap->bus_info)); return 0; @@ -448,13 +364,21 @@ vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) static int vidioc_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - struct rga_fmt *fmt; + struct rockchip_rga *rga = video_drvdata(file); + int ret; - if (f->index >= NUM_FORMATS) - return -EINVAL; + ret = rga->hw->enum_format(f); + if (ret != 0) + return ret; - fmt = &formats[f->index]; - f->pixelformat = fmt->fourcc; + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return 0; + + /* allow changing the quantization and xfer func for YUV formats */ + if (v4l2_is_format_yuv(v4l2_format_info(f->pixelformat))) + f->flags |= V4L2_FMT_FLAG_CSC_QUANTIZATION | + V4L2_FMT_FLAG_CSC_YCBCR_ENC; return 0; } @@ -469,10 +393,8 @@ static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) if (IS_ERR(frm)) return PTR_ERR(frm); - v4l2_fill_pixfmt_mp(pix_fmt, frm->fmt->fourcc, frm->width, frm->height); - + *pix_fmt = frm->pix; pix_fmt->field = V4L2_FIELD_NONE; - pix_fmt->colorspace = frm->colorspace; return 0; } @@ -480,18 +402,43 @@ static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) { struct v4l2_pix_format_mplane *pix_fmt = &f->fmt.pix_mp; - struct rga_fmt *fmt; + struct rga_ctx *ctx = file_to_rga_ctx(file); + const struct rga_hw *hw = ctx->rga->hw; + struct v4l2_frmsize_stepwise frmsize = { + .min_width = hw->min_width, + .max_width = hw->max_width, + .min_height = hw->min_height, + .max_height = hw->max_height, + .step_width = 1, + .step_height = 1, + }; + + if (v4l2_is_format_yuv(v4l2_format_info(pix_fmt->pixelformat))) { + frmsize.step_width = 2; + frmsize.step_height = 2; + } + + if (V4L2_TYPE_IS_CAPTURE(f->type)) { + const struct rga_frame *frm; - fmt = rga_fmt_find(pix_fmt->pixelformat); - if (!fmt) - fmt = &formats[0]; + frm = rga_get_frame(ctx, f->type); + if (IS_ERR(frm)) + return PTR_ERR(frm); - pix_fmt->width = clamp(pix_fmt->width, - (u32)MIN_WIDTH, (u32)MAX_WIDTH); - pix_fmt->height = clamp(pix_fmt->height, - (u32)MIN_HEIGHT, (u32)MAX_HEIGHT); + if (!(pix_fmt->flags & V4L2_PIX_FMT_FLAG_SET_CSC)) { + pix_fmt->quantization = frm->pix.quantization; + pix_fmt->ycbcr_enc = frm->pix.ycbcr_enc; + } + /* disallow values not announced in vidioc_enum_fmt */ + pix_fmt->colorspace = frm->pix.colorspace; + pix_fmt->xfer_func = frm->pix.xfer_func; + } + + hw->adjust_and_map_format(ctx, pix_fmt, V4L2_TYPE_IS_OUTPUT(f->type)); - v4l2_fill_pixfmt_mp(pix_fmt, fmt->fourcc, pix_fmt->width, pix_fmt->height); + v4l2_apply_frmsize_constraints(&pix_fmt->width, &pix_fmt->height, &frmsize); + v4l2_fill_pixfmt_mp_aligned(pix_fmt, pix_fmt->pixelformat, + pix_fmt->width, pix_fmt->height, hw->stride_alignment); pix_fmt->field = V4L2_FIELD_NONE; return 0; @@ -521,28 +468,34 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) frm = rga_get_frame(ctx, f->type); if (IS_ERR(frm)) return PTR_ERR(frm); - frm->width = pix_fmt->width; - frm->height = pix_fmt->height; - frm->size = 0; - for (i = 0; i < pix_fmt->num_planes; i++) - frm->size += pix_fmt->plane_fmt[i].sizeimage; - frm->fmt = rga_fmt_find(pix_fmt->pixelformat); - frm->stride = pix_fmt->plane_fmt[0].bytesperline; - frm->colorspace = pix_fmt->colorspace; + frm->fmt = rga->hw->adjust_and_map_format(ctx, pix_fmt, + V4L2_TYPE_IS_OUTPUT(f->type)); + + /* + * Copy colorimetry from output to capture as required by the + * v4l2-compliance tests + */ + if (V4L2_TYPE_IS_OUTPUT(f->type)) { + ctx->out.pix.colorspace = pix_fmt->colorspace; + ctx->out.pix.ycbcr_enc = pix_fmt->ycbcr_enc; + ctx->out.pix.quantization = pix_fmt->quantization; + ctx->out.pix.xfer_func = pix_fmt->xfer_func; + } /* Reset crop settings */ frm->crop.left = 0; frm->crop.top = 0; - frm->crop.width = frm->width; - frm->crop.height = frm->height; + frm->crop.width = pix_fmt->width; + frm->crop.height = pix_fmt->height; frm->pix = *pix_fmt; + ctx->cmdbuf_dirty = true; v4l2_dbg(debug, 1, &rga->v4l2_dev, - "[%s] fmt - %p4cc %dx%d (stride %d, sizeimage %d)\n", + "[%s] fmt - %p4cc %dx%d (stride %d)\n", V4L2_TYPE_IS_OUTPUT(f->type) ? "OUTPUT" : "CAPTURE", - &frm->fmt->fourcc, frm->width, frm->height, - frm->stride, frm->size); + &pix_fmt->pixelformat, pix_fmt->width, pix_fmt->height, + pix_fmt->plane_fmt[0].bytesperline); for (i = 0; i < pix_fmt->num_planes; i++) { v4l2_dbg(debug, 1, &rga->v4l2_dev, @@ -595,8 +548,8 @@ static int vidioc_g_selection(struct file *file, void *priv, } else { s->r.left = 0; s->r.top = 0; - s->r.width = f->width; - s->r.height = f->height; + s->r.width = f->pix.width; + s->r.height = f->pix.height; } return 0; @@ -608,7 +561,6 @@ static int vidioc_s_selection(struct file *file, void *priv, struct rga_ctx *ctx = file_to_rga_ctx(file); struct rockchip_rga *rga = ctx->rga; struct rga_frame *f; - int ret = 0; f = rga_get_frame(ctx, s->type); if (IS_ERR(f)) @@ -645,16 +597,32 @@ static int vidioc_s_selection(struct file *file, void *priv, return -EINVAL; } - if (s->r.left + s->r.width > f->width || - s->r.top + s->r.height > f->height || - s->r.width < MIN_WIDTH || s->r.height < MIN_HEIGHT) { + if (s->r.left + s->r.width > f->pix.width || + s->r.top + s->r.height > f->pix.height || + s->r.width < rga->hw->min_width || s->r.height < rga->hw->min_height) { v4l2_dbg(debug, 1, &rga->v4l2_dev, "unsupported crop value.\n"); return -EINVAL; } + if (vb2_is_streaming(v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx)) && + vb2_is_streaming(v4l2_m2m_get_src_vq(ctx->fh.m2m_ctx))) { + int ret = 0; + + if (V4L2_TYPE_IS_OUTPUT(s->type)) + ret = rga_check_scaling(rga->hw, &s->r, &ctx->out.crop, + ctx->rotate); + else + ret = rga_check_scaling(rga->hw, &ctx->in.crop, &s->r, + ctx->rotate); + + if (ret < 0) + return ret; + } + f->crop = s->r; + ctx->cmdbuf_dirty = true; - return ret; + return 0; } static const struct v4l2_ioctl_ops rga_ioctl_ops = { @@ -698,48 +666,10 @@ static const struct video_device rga_videodev = { .device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING, }; -static int rga_enable_clocks(struct rockchip_rga *rga) -{ - int ret; - - ret = clk_prepare_enable(rga->sclk); - if (ret) { - dev_err(rga->dev, "Cannot enable rga sclk: %d\n", ret); - return ret; - } - - ret = clk_prepare_enable(rga->aclk); - if (ret) { - dev_err(rga->dev, "Cannot enable rga aclk: %d\n", ret); - goto err_disable_sclk; - } - - ret = clk_prepare_enable(rga->hclk); - if (ret) { - dev_err(rga->dev, "Cannot enable rga hclk: %d\n", ret); - goto err_disable_aclk; - } - - return 0; - -err_disable_aclk: - clk_disable_unprepare(rga->aclk); -err_disable_sclk: - clk_disable_unprepare(rga->sclk); - - return ret; -} - -static void rga_disable_clocks(struct rockchip_rga *rga) -{ - clk_disable_unprepare(rga->sclk); - clk_disable_unprepare(rga->hclk); - clk_disable_unprepare(rga->aclk); -} - static int rga_parse_dt(struct rockchip_rga *rga) { struct reset_control *core_rst, *axi_rst, *ahb_rst; + int ret; core_rst = devm_reset_control_get(rga->dev, "core"); if (IS_ERR(core_rst)) { @@ -771,22 +701,54 @@ static int rga_parse_dt(struct rockchip_rga *rga) udelay(1); reset_control_deassert(ahb_rst); - rga->sclk = devm_clk_get(rga->dev, "sclk"); - if (IS_ERR(rga->sclk)) { - dev_err(rga->dev, "failed to get sclk clock\n"); - return PTR_ERR(rga->sclk); + ret = devm_clk_bulk_get_all(rga->dev, &rga->clks); + if (ret < 0) { + dev_err(rga->dev, "failed to get clocks\n"); + return ret; } + rga->num_clks = ret; - rga->aclk = devm_clk_get(rga->dev, "aclk"); - if (IS_ERR(rga->aclk)) { - dev_err(rga->dev, "failed to get aclk clock\n"); - return PTR_ERR(rga->aclk); - } + return 0; +} + +/* + * Some SoCs, like RK3588 have multiple identical RGA3 cores, but the + * kernel is currently missing support for multi-core handling. Exposing + * separate devices for each core to userspace is bad, since that does + * not allow scheduling tasks properly (and creates ABI). With this workaround + * the driver will only probe for the first core and early exit for the other + * cores. Once the driver gains multi-core support, the same technique + * for detecting the main core can be used to cluster all cores together. + */ +static int rga_disable_multicore(struct device *dev) +{ + struct device_node *node = NULL; + const char *compatible; + bool is_main_core; + int ret; + + /* Intentionally ignores the fallback strings */ + ret = of_property_read_string(dev->of_node, "compatible", &compatible); + if (ret) + return ret; + + /* The first compatible and available node found is considered the main core */ + do { + node = of_find_compatible_node(node, NULL, compatible); + if (of_device_is_available(node)) + break; + } while (node); + + if (!node) + return -EINVAL; - rga->hclk = devm_clk_get(rga->dev, "hclk"); - if (IS_ERR(rga->hclk)) { - dev_err(rga->dev, "failed to get hclk clock\n"); - return PTR_ERR(rga->hclk); + is_main_core = (dev->of_node == node); + + of_node_put(node); + + if (!is_main_core) { + dev_info(dev, "missing multi-core support, ignoring this instance\n"); + return -ENODEV; } return 0; @@ -802,10 +764,18 @@ static int rga_probe(struct platform_device *pdev) if (!pdev->dev.of_node) return -ENODEV; + ret = rga_disable_multicore(&pdev->dev); + if (ret) + return ret; + rga = devm_kzalloc(&pdev->dev, sizeof(*rga), GFP_KERNEL); if (!rga) return -ENOMEM; + rga->hw = of_device_get_match_data(&pdev->dev); + if (!rga->hw) + return dev_err_probe(&pdev->dev, -ENODEV, "failed to get match data\n"); + rga->dev = &pdev->dev; spin_lock_init(&rga->ctrl_lock); mutex_init(&rga->mutex); @@ -828,7 +798,8 @@ static int rga_probe(struct platform_device *pdev) goto err_put_clk; } - ret = devm_request_irq(rga->dev, irq, rga_isr, 0, + ret = devm_request_irq(rga->dev, irq, rga_isr, + rga_has_internal_iommu(rga) ? 0 : IRQF_SHARED, dev_name(rga->dev), rga); if (ret < 0) { dev_err(rga->dev, "failed to request irq\n"); @@ -869,30 +840,17 @@ static int rga_probe(struct platform_device *pdev) if (ret < 0) goto rel_m2m; - rga->version.major = (rga_read(rga, RGA_VERSION_INFO) >> 24) & 0xFF; - rga->version.minor = (rga_read(rga, RGA_VERSION_INFO) >> 20) & 0x0F; + rga->hw->get_version(rga); v4l2_info(&rga->v4l2_dev, "HW Version: 0x%02x.%02x\n", rga->version.major, rga->version.minor); pm_runtime_put(rga->dev); - /* Create CMD buffer */ - rga->cmdbuf_virt = dma_alloc_attrs(rga->dev, RGA_CMDBUF_SIZE, - &rga->cmdbuf_phy, GFP_KERNEL, - DMA_ATTR_WRITE_COMBINE); - if (!rga->cmdbuf_virt) { - ret = -ENOMEM; - goto rel_m2m; - } - - def_frame.stride = (def_frame.width * def_frame.fmt->depth) >> 3; - def_frame.size = def_frame.stride * def_frame.height; - ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(&rga->v4l2_dev, "Failed to register video device\n"); - goto free_dma; + goto rel_m2m; } v4l2_info(&rga->v4l2_dev, "Registered %s as /dev/%s\n", @@ -900,9 +858,6 @@ static int rga_probe(struct platform_device *pdev) return 0; -free_dma: - dma_free_attrs(rga->dev, RGA_CMDBUF_SIZE, rga->cmdbuf_virt, - rga->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE); rel_m2m: v4l2_m2m_release(rga->m2m_dev); rel_vdev: @@ -919,9 +874,6 @@ static void 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, - rga->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE); - v4l2_info(&rga->v4l2_dev, "Removing\n"); v4l2_m2m_release(rga->m2m_dev); @@ -935,7 +887,7 @@ static int __maybe_unused rga_runtime_suspend(struct device *dev) { struct rockchip_rga *rga = dev_get_drvdata(dev); - rga_disable_clocks(rga); + clk_bulk_disable_unprepare(rga->num_clks, rga->clks); return 0; } @@ -944,7 +896,7 @@ static int __maybe_unused rga_runtime_resume(struct device *dev) { struct rockchip_rga *rga = dev_get_drvdata(dev); - return rga_enable_clocks(rga); + return clk_bulk_prepare_enable(rga->num_clks, rga->clks); } static const struct dev_pm_ops rga_pm = { @@ -955,9 +907,15 @@ static const struct dev_pm_ops rga_pm = { static const struct of_device_id rockchip_rga_match[] = { { .compatible = "rockchip,rk3288-rga", + .data = &rga2_hw, }, { .compatible = "rockchip,rk3399-rga", + .data = &rga2_hw, + }, + { + .compatible = "rockchip,rk3588-rga3", + .data = &rga3_hw, }, {}, }; diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index 72a28b120fab..bd431534d0d3 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -6,6 +6,8 @@ #ifndef __RGA_H__ #define __RGA_H__ +#include <linux/bits.h> +#include <linux/clk.h> #include <linux/platform_device.h> #include <media/videobuf2-v4l2.h> #include <media/v4l2-ctrls.h> @@ -13,32 +15,16 @@ #define RGA_NAME "rockchip-rga" -struct rga_fmt { - u32 fourcc; - int depth; - u8 uv_factor; - u8 y_div; - u8 x_div; - u8 color_swap; - u8 hw_format; -}; +#define DEFAULT_WIDTH 100 +#define DEFAULT_HEIGHT 100 struct rga_frame { - /* Original dimensions */ - u32 width; - u32 height; - u32 colorspace; - /* Crop */ struct v4l2_rect crop; /* Image format */ - struct rga_fmt *fmt; + void *fmt; struct v4l2_pix_format_mplane pix; - - /* Variables that can calculated once and reused */ - u32 stride; - u32 size; }; struct rga_dma_desc { @@ -57,6 +43,10 @@ struct rga_ctx { struct rga_frame out; struct v4l2_ctrl_handler ctrl_handler; + void *cmdbuf_virt; + dma_addr_t cmdbuf_phy; + bool cmdbuf_dirty; + int osequence; int csequence; @@ -73,6 +63,8 @@ static inline struct rga_ctx *file_to_rga_ctx(struct file *filp) return container_of(file_to_v4l2_fh(filp), struct rga_ctx, fh); } +struct rga_hw; + struct rockchip_rga { struct v4l2_device v4l2_dev; struct v4l2_m2m_dev *m2m_dev; @@ -81,9 +73,8 @@ struct rockchip_rga { struct device *dev; struct regmap *grf; void __iomem *regs; - struct clk *sclk; - struct clk *aclk; - struct clk *hclk; + struct clk_bulk_data *clks; + int num_clks; struct rockchip_rga_version version; /* vfd lock */ @@ -92,14 +83,14 @@ struct rockchip_rga { spinlock_t ctrl_lock; struct rga_ctx *curr; - dma_addr_t cmdbuf_phy; - void *cmdbuf_virt; + + const struct rga_hw *hw; }; -struct rga_addr_offset { - unsigned int y_off; - unsigned int u_off; - unsigned int v_off; +struct rga_addrs { + dma_addr_t y_addr; + dma_addr_t u_addr; + dma_addr_t v_addr; }; struct rga_vb_buffer { @@ -111,8 +102,8 @@ struct rga_vb_buffer { dma_addr_t dma_desc_pa; size_t n_desc; - /* Plane offsets of this buffer into the mapping */ - struct rga_addr_offset offset; + /* Plane DMA addresses after the MMU mapping of the buffer */ + struct rga_addrs dma_addrs; }; static inline struct rga_vb_buffer *vb_to_rga(struct vb2_v4l2_buffer *vb) @@ -122,6 +113,9 @@ static inline struct rga_vb_buffer *vb_to_rga(struct vb2_v4l2_buffer *vb) struct rga_frame *rga_get_frame(struct rga_ctx *ctx, enum v4l2_buf_type type); +int rga_check_scaling(const struct rga_hw *hw, const struct v4l2_rect *crop_in, + const struct v4l2_rect *crop_out, u32 rotate); + /* RGA Buffers Manage */ extern const struct vb2_ops rga_qops; @@ -144,7 +138,37 @@ static inline void rga_mod(struct rockchip_rga *rga, u32 reg, u32 val, u32 mask) rga_write(rga, reg, temp); }; -void rga_hw_start(struct rockchip_rga *rga, - struct rga_vb_buffer *src, struct rga_vb_buffer *dst); +#define RGA_FEATURE_FLIP BIT(0) +#define RGA_FEATURE_ROTATE BIT(1) +#define RGA_FEATURE_BG_COLOR BIT(2) + +struct rga_hw { + const char *card_type; + bool has_internal_iommu; + size_t cmdbuf_size; + u32 min_width, min_height; + u32 max_width, max_height; + u8 max_scaling_factor; + u8 stride_alignment; + u8 features; + + void (*setup_cmdbuf)(struct rga_ctx *ctx); + void (*start)(struct rockchip_rga *rga, + struct rga_vb_buffer *src, struct rga_vb_buffer *dst); + bool (*handle_irq)(struct rockchip_rga *rga); + void (*get_version)(struct rockchip_rga *rga); + void *(*adjust_and_map_format)(struct rga_ctx *ctx, + struct v4l2_pix_format_mplane *format, + bool is_output); + int (*enum_format)(struct v4l2_fmtdesc *f); +}; + +static inline bool rga_has_internal_iommu(const struct rockchip_rga *rga) +{ + return rga->hw->has_internal_iommu; +} + +extern const struct rga_hw rga2_hw; +extern const struct rga_hw rga3_hw; #endif diff --git a/drivers/media/platform/rockchip/rga/rga3-hw.c b/drivers/media/platform/rockchip/rga/rga3-hw.c new file mode 100644 index 000000000000..ca1c268303dd --- /dev/null +++ b/drivers/media/platform/rockchip/rga/rga3-hw.c @@ -0,0 +1,507 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2025-2026 Pengutronix e.K. + * Author: Sven Püschel <s.pueschel@pengutronix.de> + */ + +#include <linux/pm_runtime.h> +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/printk.h> + +#include <media/v4l2-common.h> + +#include "rga3-hw.h" +#include "rga.h" + +static unsigned int rga3_get_scaling(unsigned int src, unsigned int dst) +{ + /* + * RGA3 scaling factor calculation as described in chapter 5.4.7 Resize + * of the TRM Part 2. The resulting scaling factor is a 16-bit value + * and therefore normalized with 2^16. + * + * While the TRM also mentions (dst-1)/(src-1) for the up-scaling case, + * it didn't work as the value always exceeds 16 bit. Flipping the + * factors results in a correct up-scaling. This is possible as the + * RGA3 has the RGA3_WIN_SCALE_XXX_UP bit to determine if it does + * an up or downscale. + * + * The scaling factor can potentially cause a slightly larger scaling + * (e.g. 1/2px larger scale and then cropped to the destination size). + * This can be seen when scaling 128x128px RGBA to 256x256px RGBA. + * The RGA2 scaling factor calculation (without the various +/-1 + * doesn't work for the RGA3. It's assumed that this is an hardware + * accuracy limitation, as the vendor kernel driver uses the same + * scaling factor calculation. + * + * With a scaling factor of 1.0 the calculation technically also + * overflows 16 bit. This isn't relevant, as in this case the + * RGA3_WIN_SCALE_XXX_BYPASS bit completely skips the scaling operation. + */ + if (dst > src) { + if (((src - 1) << 16) % (dst - 1) == 0) + return ((src - 1) << 16) / (dst - 1) - 1; + else + return ((src - 1) << 16) / (dst - 1); + } else { + return ((dst - 1) << 16) / (src - 1) + 1; + } +} + +/* + * Check if the given format can be captured, as the RGA3 doesn't support all + * input formats also on it's output. + */ +static bool rga3_can_capture(const struct rga3_fmt *fmt) +{ + return fmt->hw_format <= RGA3_COLOR_FMT_LAST_OUTPUT; +} + +/* + * Map the transformations to the RGA3 command buffer. + * Currently this is just the scaling settings and a fixed alpha value. + */ +static void rga3_cmd_set_trans_info(struct rga_ctx *ctx) +{ + u32 *cmd = ctx->cmdbuf_virt; + unsigned int src_h, src_w, dst_h, dst_w; + unsigned int reg; + u16 hor_scl_fac, ver_scl_fac; + const struct rga3_fmt *in = ctx->in.fmt; + + /* Support basic input cropping to support 1088px inputs */ + src_h = ctx->in.crop.height; + src_w = ctx->in.crop.width; + dst_h = ctx->out.pix.height; + dst_w = ctx->out.pix.width; + + reg = RGA3_WIN0_RD_CTRL - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] |= FIELD_PREP(RGA3_WIN_SCALE_HOR_UP, dst_w > src_w) + | FIELD_PREP(RGA3_WIN_SCALE_HOR_BYPASS, dst_w == src_w) + | FIELD_PREP(RGA3_WIN_SCALE_VER_UP, dst_h > src_h) + | FIELD_PREP(RGA3_WIN_SCALE_VER_BYPASS, dst_h == src_h); + + hor_scl_fac = rga3_get_scaling(src_w, dst_w); + ver_scl_fac = rga3_get_scaling(src_h, dst_h); + reg = RGA3_WIN0_SCL_FAC - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = FIELD_PREP(RGA3_SCALE_HOR_FAC, hor_scl_fac) + | FIELD_PREP(RGA3_SCALE_VER_FAC, ver_scl_fac); + + if (v4l2_format_info(in->fourcc)->has_alpha) { + /* copy alpha from input */ + reg = RGA3_OVLP_TOP_ALPHA - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = FIELD_PREP(RGA3_ALPHA_SELECT_MODE, 1) + | FIELD_PREP(RGA3_ALPHA_BLEND_MODE, 1); + reg = RGA3_OVLP_BOT_ALPHA - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = FIELD_PREP(RGA3_ALPHA_SELECT_MODE, 1) + | FIELD_PREP(RGA3_ALPHA_BLEND_MODE, 1); + } else { + /* just use a 255 alpha value */ + reg = RGA3_OVLP_TOP_CTRL - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = FIELD_PREP(RGA3_OVLP_GLOBAL_ALPHA, 0xff) + | FIELD_PREP(RGA3_OVLP_COLOR_MODE, 1); + reg = RGA3_OVLP_BOT_CTRL - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = FIELD_PREP(RGA3_OVLP_GLOBAL_ALPHA, 0xff) + | FIELD_PREP(RGA3_OVLP_COLOR_MODE, 1); + } +} + +static void rga3_cmd_set_win0_addr(struct rga_ctx *ctx, + const struct rga_addrs *addrs) +{ + u32 *cmd = ctx->cmdbuf_virt; + unsigned int reg; + + reg = RGA3_WIN0_Y_BASE - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = addrs->y_addr; + reg = RGA3_WIN0_U_BASE - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = addrs->u_addr; +} + +static void rga3_cmd_set_wr_addr(struct rga_ctx *ctx, + const struct rga_addrs *addrs) +{ + u32 *cmd = ctx->cmdbuf_virt; + unsigned int reg; + + reg = RGA3_WR_Y_BASE - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = addrs->y_addr; + reg = RGA3_WR_U_BASE - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = addrs->u_addr; +} + +/* Map the input pixel format to win0 of the comamnd buffer. */ +static void rga3_cmd_set_win0_format(struct rga_ctx *ctx) +{ + u32 *cmd = ctx->cmdbuf_virt; + const struct rga3_fmt *in = ctx->in.fmt; + const struct rga3_fmt *out = ctx->out.fmt; + const struct v4l2_format_info *in_fmt, *out_fmt; + unsigned int act_h, act_w, src_h, src_w; + bool r2y, y2r; + u8 rd_format; + const struct v4l2_pix_format_mplane *csc_pix; + u8 csc_mode; + unsigned int reg; + + act_h = ctx->in.pix.height; + act_w = ctx->in.pix.width; + /* Support basic input cropping to support 1088px inputs */ + src_h = ctx->in.crop.height; + src_w = ctx->in.crop.width; + + in_fmt = v4l2_format_info(in->fourcc); + out_fmt = v4l2_format_info(out->fourcc); + r2y = v4l2_is_format_rgb(in_fmt) && v4l2_is_format_yuv(out_fmt); + y2r = v4l2_is_format_yuv(in_fmt) && v4l2_is_format_rgb(out_fmt); + + /* The Hardware only supports formats with 1/2 planes */ + if (in_fmt->comp_planes == 2) + rd_format = RGA3_RDWR_FORMAT_SEMI_PLANAR; + else + rd_format = RGA3_RDWR_FORMAT_INTERLEAVED; + + /* set pixel format and CSC */ + csc_pix = r2y ? &ctx->out.pix : &ctx->in.pix; + switch (csc_pix->ycbcr_enc) { + case V4L2_YCBCR_ENC_BT2020: + csc_mode = RGA3_WIN_CSC_MODE_BT2020_L; + break; + case V4L2_YCBCR_ENC_709: + csc_mode = RGA3_WIN_CSC_MODE_BT709_L; + break; + default: /* should be fixed to BT601 in adjust_and_map_format */ + if (csc_pix->quantization == V4L2_QUANTIZATION_LIM_RANGE) + csc_mode = RGA3_WIN_CSC_MODE_BT601_L; + else + csc_mode = RGA3_WIN_CSC_MODE_BT601_F; + break; + } + + reg = RGA3_WIN0_RD_CTRL - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] |= FIELD_PREP(RGA3_WIN_ENABLE, 1) + | FIELD_PREP(RGA3_WIN_PIC_FORMAT, in->hw_format) + | FIELD_PREP(RGA3_WIN_YC_SWAP, in->yc_swap) + | FIELD_PREP(RGA3_WIN_RBUV_SWAP, in->rbuv_swap) + | FIELD_PREP(RGA3_WIN_RD_FORMAT, rd_format) + | FIELD_PREP(RGA3_WIN_R2Y, r2y) + | FIELD_PREP(RGA3_WIN_Y2R, y2r) + | FIELD_PREP(RGA3_WIN_CSC_MODE, csc_mode); + + /* set stride */ + reg = RGA3_WIN0_VIR_STRIDE - RGA3_FIRST_CMD_REG; + /* stride needs to be in words */ + cmd[reg >> 2] = ctx->in.pix.plane_fmt[0].bytesperline >> 2; + reg = RGA3_WIN0_UV_VIR_STRIDE - RGA3_FIRST_CMD_REG; + /* The Hardware only supports formats with 1/2 planes */ + if (ctx->in.pix.num_planes == 2) + cmd[reg >> 2] = ctx->in.pix.plane_fmt[1].bytesperline >> 2; + else + cmd[reg >> 2] = ctx->in.pix.plane_fmt[0].bytesperline >> 2; + + /* set size */ + reg = RGA3_WIN0_ACT_SIZE - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = FIELD_PREP(RGA3_WIDTH, act_w) + | FIELD_PREP(RGA3_HEIGHT, act_h); + reg = RGA3_WIN0_SRC_SIZE - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = FIELD_PREP(RGA3_WIDTH, src_w) + | FIELD_PREP(RGA3_HEIGHT, src_h); +} + +/* Map the output pixel format to the command buffer */ +static void rga3_cmd_set_wr_format(struct rga_ctx *ctx) +{ + u32 *cmd = ctx->cmdbuf_virt; + const struct rga3_fmt *out = ctx->out.fmt; + const struct v4l2_format_info *out_fmt; + unsigned int dst_h, dst_w; + u8 wr_format; + unsigned int reg; + + dst_h = ctx->out.pix.height; + dst_w = ctx->out.pix.width; + + out_fmt = v4l2_format_info(out->fourcc); + + /* The Hardware only supports formats with 1/2 planes */ + if (out_fmt->comp_planes == 2) + wr_format = RGA3_RDWR_FORMAT_SEMI_PLANAR; + else + wr_format = RGA3_RDWR_FORMAT_INTERLEAVED; + + /* set pixel format */ + reg = RGA3_WR_CTRL - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = FIELD_PREP(RGA3_WR_PIC_FORMAT, out->hw_format) + | FIELD_PREP(RGA3_WR_YC_SWAP, out->yc_swap) + | FIELD_PREP(RGA3_WR_RBUV_SWAP, out->rbuv_swap) + | FIELD_PREP(RGA3_WR_FORMAT, wr_format) + /* Use the max value to avoid limiting the write speed */ + | FIELD_PREP(RGA3_WR_SW_OUTSTANDING_MAX, 63); + + /* set stride */ + reg = RGA3_WR_VIR_STRIDE - RGA3_FIRST_CMD_REG; + /* stride needs to be in words */ + cmd[reg >> 2] = ctx->out.pix.plane_fmt[0].bytesperline >> 2; + reg = RGA3_WR_PL_VIR_STRIDE - RGA3_FIRST_CMD_REG; + /* The Hardware only supports formats with 1/2 planes */ + if (ctx->out.pix.num_planes == 2) + cmd[reg >> 2] = ctx->out.pix.plane_fmt[1].bytesperline >> 2; + else + cmd[reg >> 2] = ctx->out.pix.plane_fmt[0].bytesperline >> 2; + + /* Set size. + * As two inputs are not supported, we don't use win1. + * Therefore only set the size for win0. + */ + reg = RGA3_WIN0_DST_SIZE - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = FIELD_PREP(RGA3_WIDTH, dst_w) + | FIELD_PREP(RGA3_HEIGHT, dst_h); +} + +static void rga3_hw_setup_cmdbuf(struct rga_ctx *ctx) +{ + memset(ctx->cmdbuf_virt, 0, RGA3_CMDBUF_SIZE); + + rga3_cmd_set_win0_format(ctx); + rga3_cmd_set_trans_info(ctx); + rga3_cmd_set_wr_format(ctx); +} + +static void rga3_hw_start(struct rockchip_rga *rga, + struct rga_vb_buffer *src, struct rga_vb_buffer *dst) +{ + struct rga_ctx *ctx = rga->curr; + + rga3_cmd_set_win0_addr(ctx, &src->dma_addrs); + rga3_cmd_set_wr_addr(ctx, &dst->dma_addrs); + + rga_write(rga, RGA3_CMD_ADDR, ctx->cmdbuf_phy); + + /* sync CMD buf for RGA */ + dma_sync_single_for_device(rga->dev, ctx->cmdbuf_phy, + PAGE_SIZE, DMA_BIDIRECTIONAL); + + /* set to master mode and start the conversion */ + rga_write(rga, RGA3_SYS_CTRL, + FIELD_PREP(RGA3_CMD_MODE, RGA3_CMD_MODE_MASTER)); + rga_write(rga, RGA3_INT_EN, FIELD_PREP(RGA3_INT_FRM_DONE, 1)); + rga_write(rga, RGA3_CMD_CTRL, + FIELD_PREP(RGA3_CMD_LINE_START_PULSE, 1)); +} + +static bool rga3_handle_irq(struct rockchip_rga *rga) +{ + u32 intr; + + intr = rga_read(rga, RGA3_INT_RAW); + /* clear all interrupts */ + rga_write(rga, RGA3_INT_CLR, intr); + + return FIELD_GET(RGA3_INT_FRM_DONE, intr); +} + +static void rga3_get_version(struct rockchip_rga *rga) +{ + u32 version = rga_read(rga, RGA3_VERSION_NUM); + + rga->version.major = FIELD_GET(RGA3_VERSION_NUM_MAJOR, version); + rga->version.minor = FIELD_GET(RGA3_VERSION_NUM_MINOR, version); +} + +static struct rga3_fmt rga3_formats[] = { + { + .fourcc = V4L2_PIX_FMT_RGB24, + .hw_format = RGA3_COLOR_FMT_BGR888, + .rbuv_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_BGR24, + .hw_format = RGA3_COLOR_FMT_BGR888, + }, + { + .fourcc = V4L2_PIX_FMT_ABGR32, + .hw_format = RGA3_COLOR_FMT_BGRA8888, + }, + { + .fourcc = V4L2_PIX_FMT_RGBA32, + .hw_format = RGA3_COLOR_FMT_BGRA8888, + .rbuv_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_XBGR32, + .hw_format = RGA3_COLOR_FMT_BGRA8888, + }, + { + .fourcc = V4L2_PIX_FMT_RGBX32, + .hw_format = RGA3_COLOR_FMT_BGRA8888, + .rbuv_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_RGB565, + .hw_format = RGA3_COLOR_FMT_BGR565, + .rbuv_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_NV12M, + .hw_format = RGA3_COLOR_FMT_YUV420, + }, + { + .fourcc = V4L2_PIX_FMT_NV12, + .hw_format = RGA3_COLOR_FMT_YUV420, + }, + { + .fourcc = V4L2_PIX_FMT_NV21M, + .hw_format = RGA3_COLOR_FMT_YUV420, + .rbuv_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_NV21, + .hw_format = RGA3_COLOR_FMT_YUV420, + .rbuv_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_NV16M, + .hw_format = RGA3_COLOR_FMT_YUV422, + }, + { + .fourcc = V4L2_PIX_FMT_NV16, + .hw_format = RGA3_COLOR_FMT_YUV422, + }, + { + .fourcc = V4L2_PIX_FMT_NV61M, + .hw_format = RGA3_COLOR_FMT_YUV422, + .rbuv_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_NV61, + .hw_format = RGA3_COLOR_FMT_YUV422, + .rbuv_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_YUYV, + .hw_format = RGA3_COLOR_FMT_YUV422, + .yc_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_YVYU, + .hw_format = RGA3_COLOR_FMT_YUV422, + .yc_swap = 1, + .rbuv_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_UYVY, + .hw_format = RGA3_COLOR_FMT_YUV422, + }, + { + .fourcc = V4L2_PIX_FMT_VYUY, + .hw_format = RGA3_COLOR_FMT_YUV422, + .rbuv_swap = 1, + }, + /* Input only formats last to keep rga3_enum_format simple */ + { + .fourcc = V4L2_PIX_FMT_ARGB32, + .hw_format = RGA3_COLOR_FMT_ABGR8888, + .rbuv_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_BGRA32, + .hw_format = RGA3_COLOR_FMT_ABGR8888, + }, + { + .fourcc = V4L2_PIX_FMT_XRGB32, + .hw_format = RGA3_COLOR_FMT_ABGR8888, + .rbuv_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_BGRX32, + .hw_format = RGA3_COLOR_FMT_ABGR8888, + }, +}; + +static int rga3_enum_format(struct v4l2_fmtdesc *f) +{ + struct rga3_fmt *fmt; + + if (f->index >= ARRAY_SIZE(rga3_formats)) + return -EINVAL; + + fmt = &rga3_formats[f->index]; + if (V4L2_TYPE_IS_CAPTURE(f->type) && !rga3_can_capture(fmt)) + return -EINVAL; + + f->pixelformat = fmt->fourcc; + return 0; +} + +static void *rga3_adjust_and_map_format(struct rga_ctx *ctx, + struct v4l2_pix_format_mplane *format, + bool is_output) +{ + unsigned int i; + const struct v4l2_format_info *format_info; + const struct v4l2_pix_format_mplane *other_format; + const struct v4l2_format_info *other_format_info; + + if (!format) + return &rga3_formats[0]; + + format_info = v4l2_format_info(format->pixelformat); + other_format = is_output ? &ctx->out.pix : &ctx->in.pix; + other_format_info = v4l2_format_info(other_format->pixelformat); + + if ((v4l2_is_format_rgb(format_info) && + v4l2_is_format_yuv(other_format_info)) || + (v4l2_is_format_yuv(format_info) && + v4l2_is_format_rgb(other_format_info))) { + /* + * The RGA3 only supports BT601, BT709 and BT2020 RGB<->YUV conversions + * Additionally BT709 and BT2020 only support limited range YUV. + */ + switch (format->ycbcr_enc) { + case V4L2_YCBCR_ENC_601: + /* supports full and limited range */ + break; + case V4L2_YCBCR_ENC_709: + case V4L2_YCBCR_ENC_BT2020: + format->quantization = V4L2_QUANTIZATION_LIM_RANGE; + break; + default: + format->ycbcr_enc = V4L2_YCBCR_ENC_601; + format->quantization = V4L2_QUANTIZATION_FULL_RANGE; + break; + } + } + + for (i = 0; i < ARRAY_SIZE(rga3_formats); i++) { + if (!is_output && !rga3_can_capture(&rga3_formats[i])) + continue; + + if (rga3_formats[i].fourcc == format->pixelformat) + return &rga3_formats[i]; + } + + format->pixelformat = rga3_formats[0].fourcc; + return &rga3_formats[0]; +} + +const struct rga_hw rga3_hw = { + .card_type = "rga3", + .has_internal_iommu = false, + .cmdbuf_size = RGA3_CMDBUF_SIZE, + .min_width = RGA3_MIN_WIDTH, + .min_height = RGA3_MIN_HEIGHT, + /* use output size, as it's a bit smaller than the input size */ + .max_width = RGA3_MAX_OUTPUT_WIDTH, + .max_height = RGA3_MAX_OUTPUT_HEIGHT, + .max_scaling_factor = RGA3_MAX_SCALING_FACTOR, + .stride_alignment = 16, + .features = 0, + + .setup_cmdbuf = rga3_hw_setup_cmdbuf, + .start = rga3_hw_start, + .handle_irq = rga3_handle_irq, + .get_version = rga3_get_version, + .enum_format = rga3_enum_format, + .adjust_and_map_format = rga3_adjust_and_map_format, +}; diff --git a/drivers/media/platform/rockchip/rga/rga3-hw.h b/drivers/media/platform/rockchip/rga/rga3-hw.h new file mode 100644 index 000000000000..85fd8ae257ec --- /dev/null +++ b/drivers/media/platform/rockchip/rga/rga3-hw.h @@ -0,0 +1,192 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) Pengutronix e.K. + * Author: Sven Püschel <s.pueschel@pengutronix.de> + */ +#ifndef __RGA3_HW_H__ +#define __RGA3_HW_H__ + +#include <linux/bits.h> +#include <linux/types.h> + +#define RGA3_CMDBUF_SIZE 0xb8 + +#define RGA3_MIN_WIDTH 128 +#define RGA3_MIN_HEIGHT 128 +#define RGA3_MAX_INPUT_WIDTH (8192 - 16) +#define RGA3_MAX_INPUT_HEIGHT (8192 - 16) +#define RGA3_MAX_OUTPUT_WIDTH (8192 - 64) +#define RGA3_MAX_OUTPUT_HEIGHT (8192 - 64) +#define RGA3_MAX_SCALING_FACTOR 8 +#define RGA3_RESET_TIMEOUT 1000 + +/* Registers address */ +/* sys reg */ +#define RGA3_SYS_CTRL 0x000 +#define RGA3_CMD_CTRL 0x004 +#define RGA3_CMD_ADDR 0x008 +#define RGA3_MI_GROUP_CTRL 0x00c +#define RGA3_ARQOS_CTRL 0x010 +#define RGA3_VERSION_NUM 0x018 +#define RGA3_VERSION_TIM 0x01c +#define RGA3_INT_EN 0x020 +#define RGA3_INT_RAW 0x024 +#define RGA3_INT_MSK 0x028 +#define RGA3_INT_CLR 0x02c +#define RGA3_RO_SRST 0x030 +#define RGA3_STATUS0 0x034 +#define RGA3_SCAN_CNT 0x038 +#define RGA3_CMD_STATE 0x040 + +/* cmd reg */ +#define RGA3_WIN0_RD_CTRL 0x100 +#define RGA3_FIRST_CMD_REG RGA3_WIN0_RD_CTRL +#define RGA3_WIN0_Y_BASE 0x110 +#define RGA3_WIN0_U_BASE 0x114 +#define RGA3_WIN0_V_BASE 0x118 +#define RGA3_WIN0_VIR_STRIDE 0x11c +#define RGA3_WIN0_FBC_OFF 0x120 +#define RGA3_WIN0_SRC_SIZE 0x124 +#define RGA3_WIN0_ACT_OFF 0x128 +#define RGA3_WIN0_ACT_SIZE 0x12c +#define RGA3_WIN0_DST_SIZE 0x130 +#define RGA3_WIN0_SCL_FAC 0x134 +#define RGA3_WIN0_UV_VIR_STRIDE 0x138 +#define RGA3_WIN1_RD_CTRL 0x140 +#define RGA3_WIN1_Y_BASE 0x150 +#define RGA3_WIN1_U_BASE 0x154 +#define RGA3_WIN1_V_BASE 0x158 +#define RGA3_WIN1_VIR_STRIDE 0x15c +#define RGA3_WIN1_FBC_OFF 0x160 +#define RGA3_WIN1_SRC_SIZE 0x164 +#define RGA3_WIN1_ACT_OFF 0x168 +#define RGA3_WIN1_ACT_SIZE 0x16c +#define RGA3_WIN1_DST_SIZE 0x170 +#define RGA3_WIN1_SCL_FAC 0x174 +#define RGA3_WIN1_UV_VIR_STRIDE 0x178 +#define RGA3_OVLP_CTRL 0x180 +#define RGA3_OVLP_OFF 0x184 +#define RGA3_OVLP_TOP_KEY_MIN 0x188 +#define RGA3_OVLP_TOP_KEY_MAX 0x18c +#define RGA3_OVLP_TOP_CTRL 0x190 +#define RGA3_OVLP_BOT_CTRL 0x194 +#define RGA3_OVLP_TOP_ALPHA 0x198 +#define RGA3_OVLP_BOT_ALPHA 0x19c +#define RGA3_WR_CTRL 0x1a0 +#define RGA3_WR_FBCE_CTRL 0x1a4 +#define RGA3_WR_VIR_STRIDE 0x1a8 +#define RGA3_WR_PL_VIR_STRIDE 0x1ac +#define RGA3_WR_Y_BASE 0x1b0 +#define RGA3_WR_U_BASE 0x1b4 +#define RGA3_WR_V_BASE 0x1b8 + +/* Registers value */ +#define RGA3_COLOR_FMT_YUV420 0x0 +#define RGA3_COLOR_FMT_YUV422 0x1 +#define RGA3_COLOR_FMT_YUV420_10B 0x2 +#define RGA3_COLOR_FMT_YUV422_10B 0x3 +/* + * Use memory ordering names + * instead of the datasheet naming RGB formats in big endian order + */ +#define RGA3_COLOR_FMT_BGR565 0x4 +#define RGA3_COLOR_FMT_BGR888 0x5 +#define RGA3_COLOR_FMT_FIRST_HAS_ALPHA RGA3_COLOR_FMT_BGRA8888 +#define RGA3_COLOR_FMT_BGRA8888 0x6 +#define RGA3_COLOR_FMT_LAST_OUTPUT RGA3_COLOR_FMT_BGRA8888 +/* the following are only supported as inputs */ +#define RGA3_COLOR_FMT_ABGR8888 0x7 +/* + * the following seem to be unnecessary, + * as they can be achieved with RB swaps + */ +#define RGA3_COLOR_FMT_RGBA8888 0x8 +#define RGA3_COLOR_FMT_ARGB8888 0x9 + +#define RGA3_RDWR_FORMAT_SEMI_PLANAR 0x1 +#define RGA3_RDWR_FORMAT_INTERLEAVED 0x2 + +#define RGA3_CMD_MODE_MASTER 0x1 + +#define RGA3_WIN_CSC_MODE_BT601_L 0x0 +#define RGA3_WIN_CSC_MODE_BT709_L 0x1 +#define RGA3_WIN_CSC_MODE_BT601_F 0x2 +#define RGA3_WIN_CSC_MODE_BT2020_L 0x3 + +/* RGA masks */ +/* SYS_CTRL */ +#define RGA3_CCLK_SRESET BIT(4) +#define RGA3_ACLK_SRESET BIT(3) +#define RGA3_CMD_MODE BIT(1) + +/* CMD_CTRL */ +#define RGA3_CMD_LINE_START_PULSE BIT(0) + +/* VERSION_NUM */ +#define RGA3_VERSION_NUM_MAJOR GENMASK(31, 28) +#define RGA3_VERSION_NUM_MINOR GENMASK(27, 20) + +/* INT_* */ +#define RGA3_INT_FRM_DONE BIT(0) +#define RGA3_INT_DMA_READ_BUS_ERR BIT(2) +#define RGA3_INT_WIN0_FBC_DEC_ERR BIT(5) +#define RGA3_INT_WIN0_HOR_ERR BIT(6) +#define RGA3_INT_WIN0_VER_ERR BIT(7) +#define RGA3_INT_WR_VER_ERR BIT(13) +#define RGA3_INT_WR_HOR_ERR BIT(14) +#define RGA3_INT_WR_BUS_ERR BIT(15) +#define RGA3_INT_WIN0_IN_FIFO_WR_ERR BIT(16) +#define RGA3_INT_WIN0_IN_FIFO_RD_ERR BIT(17) +#define RGA3_INT_WIN0_HOR_FIFO_WR_ERR BIT(18) +#define RGA3_INT_WIN0_HOR_FIFO_RD_ERR BIT(19) +#define RGA3_INT_WIN0_VER_FIFO_WR_ERR BIT(20) +#define RGA3_INT_WIN0_VER_FIFO_RD_ERR BIT(21) + +/* RO_SRST */ +#define RGA3_RO_SRST_DONE GENMASK(5, 0) + +/* *_SIZE */ +#define RGA3_HEIGHT GENMASK(28, 16) +#define RGA3_WIDTH GENMASK(12, 0) + +/* SCL_FAC */ +#define RGA3_SCALE_VER_FAC GENMASK(31, 16) +#define RGA3_SCALE_HOR_FAC GENMASK(15, 0) + +/* WINx_CTRL */ +#define RGA3_WIN_CSC_MODE GENMASK(27, 26) +#define RGA3_WIN_R2Y BIT(25) +#define RGA3_WIN_Y2R BIT(24) +#define RGA3_WIN_SCALE_VER_UP BIT(23) +#define RGA3_WIN_SCALE_VER_BYPASS BIT(22) +#define RGA3_WIN_SCALE_HOR_UP BIT(21) +#define RGA3_WIN_SCALE_HOR_BYPASS BIT(20) +#define RGA3_WIN_YC_SWAP BIT(13) +#define RGA3_WIN_RBUV_SWAP BIT(12) +#define RGA3_WIN_RD_FORMAT GENMASK(9, 8) +#define RGA3_WIN_PIC_FORMAT GENMASK(7, 4) +#define RGA3_WIN_ENABLE BIT(0) + +/* COLOR_CTRL */ +#define RGA3_OVLP_GLOBAL_ALPHA GENMASK(23, 16) +#define RGA3_OVLP_COLOR_MODE BIT(0) + +/* ALPHA_CTRL */ +#define RGA3_ALPHA_SELECT_MODE BIT(4) +#define RGA3_ALPHA_BLEND_MODE GENMASK(3, 2) + +/* WR_CTRL */ +#define RGA3_WR_YC_SWAP BIT(20) +#define RGA3_WR_SW_OUTSTANDING_MAX GENMASK(18, 13) +#define RGA3_WR_RBUV_SWAP BIT(12) +#define RGA3_WR_FORMAT GENMASK(9, 8) +#define RGA3_WR_PIC_FORMAT GENMASK(7, 4) + +struct rga3_fmt { + u32 fourcc; + u8 hw_format; + bool rbuv_swap; + bool yc_swap; +}; + +#endif diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c index 9e67160a16e4..bc9518f8db50 100644 --- a/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c +++ b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c @@ -30,6 +30,14 @@ #define RK3568_MIPI_CTRL0_CROP_EN BIT(5) #define RK3568_MIPI_CTRL0_WRDDR(type) ((type) << 1) +#define RK3588_MIPI_CTRL0_DMA_EN BIT(28) +#define RK3588_MIPI_CTRL0_HIGH_ALIGN BIT(27) +#define RK3588_MIPI_CTRL0_WRDDR(type) ((type) << 5) +#define RK3588_MIPI_CTRL0_CROP_EN BIT(4) +#define RK3588_MIPI_CTRL0_PARSE(type) ((type) << 1) + +#define RK3588_MIPI_CTRL_CAP_EN BIT(0) + #define RKCIF_MIPI_CTRL0_DT_ID(id) ((id) << 10) #define RKCIF_MIPI_CTRL0_VC_ID(id) ((id) << 8) #define RKCIF_MIPI_CTRL0_CAP_EN BIT(0) @@ -375,11 +383,8 @@ static u32 rkcif_rk3568_mipi_ctrl0(struct rkcif_stream *stream, const struct rkcif_output_fmt *active_out_fmt) { - u32 ctrl0 = 0; - - ctrl0 |= RKCIF_MIPI_CTRL0_DT_ID(active_out_fmt->mipi.dt); - ctrl0 |= RKCIF_MIPI_CTRL0_CAP_EN; - ctrl0 |= RK3568_MIPI_CTRL0_CROP_EN; + u32 ctrl0 = RKCIF_MIPI_CTRL0_DT_ID(active_out_fmt->mipi.dt) | + RKCIF_MIPI_CTRL0_CAP_EN | RK3568_MIPI_CTRL0_CROP_EN; if (active_out_fmt->mipi.compact) ctrl0 |= RK3568_MIPI_CTRL0_COMPACT_EN; @@ -481,6 +486,132 @@ const struct rkcif_mipi_match_data rkcif_rk3568_vicap_mipi_match_data = { }, }; +static u32 +rkcif_rk3588_mipi_ctrl0(struct rkcif_stream *stream, + const struct rkcif_output_fmt *active_out_fmt) +{ + u32 ctrl0 = 0; + + ctrl0 |= RK3588_MIPI_CTRL0_DMA_EN; + ctrl0 |= RKCIF_MIPI_CTRL0_DT_ID(active_out_fmt->mipi.dt); + ctrl0 |= RK3588_MIPI_CTRL0_CROP_EN; + ctrl0 |= RKCIF_MIPI_CTRL0_CAP_EN; + + switch (active_out_fmt->mipi.type) { + case RKCIF_MIPI_TYPE_RAW8: + break; + case RKCIF_MIPI_TYPE_RAW10: + ctrl0 |= RK3588_MIPI_CTRL0_PARSE(0x1); + if (!active_out_fmt->mipi.compact) + ctrl0 |= RK3588_MIPI_CTRL0_WRDDR(0x1); + break; + case RKCIF_MIPI_TYPE_RAW12: + ctrl0 |= RK3588_MIPI_CTRL0_PARSE(0x2); + if (!active_out_fmt->mipi.compact) + ctrl0 |= RK3588_MIPI_CTRL0_WRDDR(0x1); + break; + case RKCIF_MIPI_TYPE_RGB888: + break; + case RKCIF_MIPI_TYPE_YUV422SP: + ctrl0 |= RK3588_MIPI_CTRL0_WRDDR(0x4); + break; + case RKCIF_MIPI_TYPE_YUV420SP: + ctrl0 |= RK3588_MIPI_CTRL0_WRDDR(0x5); + break; + case RKCIF_MIPI_TYPE_YUV400: + ctrl0 |= RK3588_MIPI_CTRL0_WRDDR(0x3); + break; + default: + break; + } + + return ctrl0; +} + +const struct rkcif_mipi_match_data rkcif_rk3588_vicap_mipi_match_data = { + .mipi_num = 6, + .mipi_ctrl0 = rkcif_rk3588_mipi_ctrl0, + .regs = { + [RKCIF_MIPI_CTRL] = 0x20, + [RKCIF_MIPI_INTEN] = 0x74, + [RKCIF_MIPI_INTSTAT] = 0x78, + }, + .regs_id = { + [RKCIF_ID0] = { + [RKCIF_MIPI_CTRL0] = 0x00, + [RKCIF_MIPI_CTRL1] = 0x04, + [RKCIF_MIPI_FRAME0_ADDR_Y] = 0x24, + [RKCIF_MIPI_FRAME0_ADDR_UV] = 0x2c, + [RKCIF_MIPI_FRAME0_VLW_Y] = 0x34, + [RKCIF_MIPI_FRAME0_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_FRAME1_ADDR_Y] = 0x28, + [RKCIF_MIPI_FRAME1_ADDR_UV] = 0x30, + [RKCIF_MIPI_FRAME1_VLW_Y] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_FRAME1_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_CROP_START] = 0x8c, + }, + [RKCIF_ID1] = { + [RKCIF_MIPI_CTRL0] = 0x08, + [RKCIF_MIPI_CTRL1] = 0x0c, + [RKCIF_MIPI_FRAME0_ADDR_Y] = 0x38, + [RKCIF_MIPI_FRAME0_ADDR_UV] = 0x40, + [RKCIF_MIPI_FRAME0_VLW_Y] = 0x48, + [RKCIF_MIPI_FRAME0_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_FRAME1_ADDR_Y] = 0x3c, + [RKCIF_MIPI_FRAME1_ADDR_UV] = 0x44, + [RKCIF_MIPI_FRAME1_VLW_Y] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_FRAME1_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_CROP_START] = 0x90, + }, + [RKCIF_ID2] = { + [RKCIF_MIPI_CTRL0] = 0x10, + [RKCIF_MIPI_CTRL1] = 0x14, + [RKCIF_MIPI_FRAME0_ADDR_Y] = 0x4c, + [RKCIF_MIPI_FRAME0_ADDR_UV] = 0x54, + [RKCIF_MIPI_FRAME0_VLW_Y] = 0x5c, + [RKCIF_MIPI_FRAME0_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_FRAME1_ADDR_Y] = 0x50, + [RKCIF_MIPI_FRAME1_ADDR_UV] = 0x58, + [RKCIF_MIPI_FRAME1_VLW_Y] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_FRAME1_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_CROP_START] = 0x94, + }, + [RKCIF_ID3] = { + [RKCIF_MIPI_CTRL0] = 0x18, + [RKCIF_MIPI_CTRL1] = 0x1c, + [RKCIF_MIPI_FRAME0_ADDR_Y] = 0x60, + [RKCIF_MIPI_FRAME0_ADDR_UV] = 0x68, + [RKCIF_MIPI_FRAME0_VLW_Y] = 0x70, + [RKCIF_MIPI_FRAME0_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_FRAME1_ADDR_Y] = 0x64, + [RKCIF_MIPI_FRAME1_ADDR_UV] = 0x6c, + [RKCIF_MIPI_FRAME1_VLW_Y] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_FRAME1_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_CROP_START] = 0x98, + }, + }, + .blocks = { + { + .offset = 0x100, + }, + { + .offset = 0x200, + }, + { + .offset = 0x300, + }, + { + .offset = 0x400, + }, + { + .offset = 0x500, + }, + { + .offset = 0x600, + }, + }, +}; + static inline unsigned int rkcif_mipi_get_reg(struct rkcif_interface *interface, unsigned int index) { @@ -631,6 +762,13 @@ static int rkcif_mipi_start_streaming(struct rkcif_stream *stream) rkcif_mipi_stream_write(stream, RKCIF_MIPI_CTRL1, ctrl1); rkcif_mipi_stream_write(stream, RKCIF_MIPI_CTRL0, ctrl0); + /* + * TODO: This bit has a different meaning on the RK3568, but it is + * set there by default anyway. While correct, this is not exactly + * nice and shall be reworked during the next refactoring. + */ + rkcif_mipi_write(interface, RKCIF_MIPI_CTRL, RK3588_MIPI_CTRL_CAP_EN); + ret = 0; out: diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.h b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.h index 7f16eadc474c..7edaca44f653 100644 --- a/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.h +++ b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.h @@ -13,6 +13,7 @@ #include "rkcif-common.h" extern const struct rkcif_mipi_match_data rkcif_rk3568_vicap_mipi_match_data; +extern const struct rkcif_mipi_match_data rkcif_rk3588_vicap_mipi_match_data; int rkcif_mipi_register(struct rkcif_device *rkcif); diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-common.h b/drivers/media/platform/rockchip/rkcif/rkcif-common.h index dd92cfbc879f..4d9211ba9bda 100644 --- a/drivers/media/platform/rockchip/rkcif/rkcif-common.h +++ b/drivers/media/platform/rockchip/rkcif/rkcif-common.h @@ -27,7 +27,7 @@ #include "rkcif-regs.h" #define RKCIF_DRIVER_NAME "rockchip-cif" -#define RKCIF_CLK_MAX 4 +#define RKCIF_CLK_MAX 5 enum rkcif_format_type { RKCIF_FMT_TYPE_INVALID, diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-dev.c b/drivers/media/platform/rockchip/rkcif/rkcif-dev.c index b4cf1146f131..be3a174b9aab 100644 --- a/drivers/media/platform/rockchip/rkcif/rkcif-dev.c +++ b/drivers/media/platform/rockchip/rkcif/rkcif-dev.c @@ -53,6 +53,20 @@ static const struct rkcif_match_data rk3568_vicap_match_data = { .mipi = &rkcif_rk3568_vicap_mipi_match_data, }; +static const char *const rk3588_vicap_clks[] = { + "aclk", + "hclk", + "dclk", + "iclk", + "iclk1", +}; + +static const struct rkcif_match_data rk3588_vicap_match_data = { + .clks = rk3588_vicap_clks, + .clks_num = ARRAY_SIZE(rk3588_vicap_clks), + .mipi = &rkcif_rk3588_vicap_mipi_match_data, +}; + static const struct of_device_id rkcif_plat_of_match[] = { { .compatible = "rockchip,px30-vip", @@ -62,6 +76,10 @@ static const struct of_device_id rkcif_plat_of_match[] = { .compatible = "rockchip,rk3568-vicap", .data = &rk3568_vicap_match_data, }, + { + .compatible = "rockchip,rk3588-vicap", + .data = &rk3588_vicap_match_data, + }, {} }; MODULE_DEVICE_TABLE(of, rkcif_plat_of_match); diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c index 6442436a5e42..042b759eba62 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c @@ -64,6 +64,7 @@ union rkisp1_ext_params_config { struct rkisp1_ext_params_compand_bls_config compand_bls; struct rkisp1_ext_params_compand_curve_config compand_curve; struct rkisp1_ext_params_wdr_config wdr; + struct rkisp1_ext_params_cac_config cac; }; enum rkisp1_params_formats { @@ -1413,6 +1414,47 @@ static void rkisp1_wdr_config(struct rkisp1_params *params, RKISP1_CIF_ISP_WDR_TONE_CURVE_YM_MASK); } +static void rkisp1_cac_config(struct rkisp1_params *params, + const struct rkisp1_cif_isp_cac_config *arg) +{ + u32 val; + + /* + * The enable bit is in the same register (RKISP1_CIF_ISP_CAC_CTRL), + * so only set the clipping mode, and do not modify the other bits. + */ + val = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_CAC_CTRL); + val &= ~(RKISP1_CIF_ISP_CAC_CTRL_H_CLIP_MODE | + RKISP1_CIF_ISP_CAC_CTRL_V_CLIP_MODE); + val |= FIELD_PREP(RKISP1_CIF_ISP_CAC_CTRL_H_CLIP_MODE, arg->h_clip_mode) | + FIELD_PREP(RKISP1_CIF_ISP_CAC_CTRL_V_CLIP_MODE, arg->v_clip_mode); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_CTRL, val); + + val = FIELD_PREP(RKISP1_CIF_ISP_CAC_COUNT_START_H_MASK, arg->h_count_start) | + FIELD_PREP(RKISP1_CIF_ISP_CAC_COUNT_START_V_MASK, arg->v_count_start); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_COUNT_START, val); + + val = FIELD_PREP(RKISP1_CIF_ISP_CAC_RED_MASK, arg->red[0]) | + FIELD_PREP(RKISP1_CIF_ISP_CAC_BLUE_MASK, arg->blue[0]); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_A, val); + + val = FIELD_PREP(RKISP1_CIF_ISP_CAC_RED_MASK, arg->red[1]) | + FIELD_PREP(RKISP1_CIF_ISP_CAC_BLUE_MASK, arg->blue[1]); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_B, val); + + val = FIELD_PREP(RKISP1_CIF_ISP_CAC_RED_MASK, arg->red[2]) | + FIELD_PREP(RKISP1_CIF_ISP_CAC_BLUE_MASK, arg->blue[2]); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_C, val); + + val = FIELD_PREP(RKISP1_CIF_ISP_CAC_NF_MASK, arg->x_nf) | + FIELD_PREP(RKISP1_CIF_ISP_CAC_NS_MASK, arg->x_ns); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_X_NORM, val); + + val = FIELD_PREP(RKISP1_CIF_ISP_CAC_NF_MASK, arg->y_nf) | + FIELD_PREP(RKISP1_CIF_ISP_CAC_NS_MASK, arg->y_ns); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_Y_NORM, val); +} + static void rkisp1_isp_isr_other_config(struct rkisp1_params *params, const struct rkisp1_params_cfg *new_params) @@ -2089,6 +2131,25 @@ static void rkisp1_ext_params_wdr(struct rkisp1_params *params, RKISP1_CIF_ISP_WDR_CTRL_ENABLE); } +static void rkisp1_ext_params_cac(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_cac_config *cac = &block->cac; + + if (cac->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CAC_CTRL, + RKISP1_CIF_ISP_CAC_CTRL_ENABLE); + return; + } + + rkisp1_cac_config(params, &cac->config); + + if ((cac->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(cac->header.type))) + rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CAC_CTRL, + RKISP1_CIF_ISP_CAC_CTRL_ENABLE); +} + typedef void (*rkisp1_block_handler)(struct rkisp1_params *params, const union rkisp1_ext_params_config *config); @@ -2185,6 +2246,10 @@ static const struct rkisp1_ext_params_handler { .handler = rkisp1_ext_params_wdr, .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_CAC] = { + .handler = rkisp1_ext_params_cac, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, }; #define RKISP1_PARAMS_BLOCK_INFO(block, data) \ @@ -2215,6 +2280,7 @@ rkisp1_ext_params_block_types_info[] = { RKISP1_PARAMS_BLOCK_INFO(COMPAND_EXPAND, compand_curve), RKISP1_PARAMS_BLOCK_INFO(COMPAND_COMPRESS, compand_curve), RKISP1_PARAMS_BLOCK_INFO(WDR, wdr), + RKISP1_PARAMS_BLOCK_INFO(CAC, cac), }; static_assert(ARRAY_SIZE(rkisp1_ext_params_handlers) == @@ -2474,6 +2540,8 @@ void rkisp1_params_disable(struct rkisp1_params *params) rkisp1_ie_enable(params, false); rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_DPF_MODE, RKISP1_CIF_ISP_DPF_MODE_EN); + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CAC_CTRL, + RKISP1_CIF_ISP_CAC_CTRL_ENABLE); } static const struct rkisp1_params_ops rkisp1_v10_params_ops = { diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h index fbeb186cde0d..2b842194de8f 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h @@ -724,6 +724,17 @@ #define RKISP1_CIF_ISP_WDR_DMIN_STRENGTH_MASK GENMASK(20, 16) #define RKISP1_CIF_ISP_WDR_DMIN_STRENGTH_MAX 16U +/* CAC */ +#define RKISP1_CIF_ISP_CAC_CTRL_ENABLE BIT(0) +#define RKISP1_CIF_ISP_CAC_CTRL_V_CLIP_MODE GENMASK(2, 1) +#define RKISP1_CIF_ISP_CAC_CTRL_H_CLIP_MODE BIT(3) +#define RKISP1_CIF_ISP_CAC_COUNT_START_H_MASK GENMASK(12, 0) +#define RKISP1_CIF_ISP_CAC_COUNT_START_V_MASK GENMASK(28, 16) +#define RKISP1_CIF_ISP_CAC_RED_MASK GENMASK(8, 0) +#define RKISP1_CIF_ISP_CAC_BLUE_MASK GENMASK(24, 16) +#define RKISP1_CIF_ISP_CAC_NF_MASK GENMASK(4, 0) +#define RKISP1_CIF_ISP_CAC_NS_MASK GENMASK(19, 16) + /* =================================================================== */ /* CIF Registers */ /* =================================================================== */ @@ -1196,8 +1207,8 @@ #define RKISP1_CIF_ISP_CAC_A (RKISP1_CIF_ISP_CAC_BASE + 0x00000008) #define RKISP1_CIF_ISP_CAC_B (RKISP1_CIF_ISP_CAC_BASE + 0x0000000c) #define RKISP1_CIF_ISP_CAC_C (RKISP1_CIF_ISP_CAC_BASE + 0x00000010) -#define RKISP1_CIF_ISP_X_NORM (RKISP1_CIF_ISP_CAC_BASE + 0x00000014) -#define RKISP1_CIF_ISP_Y_NORM (RKISP1_CIF_ISP_CAC_BASE + 0x00000018) +#define RKISP1_CIF_ISP_CAC_X_NORM (RKISP1_CIF_ISP_CAC_BASE + 0x00000014) +#define RKISP1_CIF_ISP_CAC_Y_NORM (RKISP1_CIF_ISP_CAC_BASE + 0x00000018) #define RKISP1_CIF_ISP_EXP_BASE 0x00002600 #define RKISP1_CIF_ISP_EXP_CTRL (RKISP1_CIF_ISP_EXP_BASE + 0x00000000) diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-bitwriter.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-bitwriter.h new file mode 100644 index 000000000000..288467941abf --- /dev/null +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-bitwriter.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Rockchip Video Decoder bit writer + * + * Copyright (C) 2026 Collabora, Ltd. + * Detlev Casanova <detlev.casanova@collabora.com> + * Copyright (C) 2019 Collabora, Ltd. + * Boris Brezillon <boris.brezillon@collabora.com> + */ + +#ifndef RKVDEC_BIT_WRITER_H_ +#define RKVDEC_BIT_WRITER_H_ + +#include <linux/types.h> +#include <linux/bits.h> + +struct rkvdec_bw_field { + u16 offset; + u8 len; +}; + +#define BW_FIELD(_offset, _len) ((struct rkvdec_bw_field){ _offset, _len }) + +static inline void rkvdec_set_bw_field(u32 *buf, struct rkvdec_bw_field field, u32 value) +{ + u8 bit = field.offset % 32; + u16 word = field.offset / 32; + u64 mask = GENMASK_ULL(bit + field.len - 1, bit); + u64 val = ((u64)value << bit) & mask; + + buf[word] &= ~mask; + buf[word] |= val; + if (bit + field.len > 32) { + buf[word + 1] &= ~(mask >> 32); + buf[word + 1] |= val >> 32; + } +} + +#endif /* RKVDEC_BIT_WRITER_H_ */ diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.c index e28f06394470..54639512e456 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.c @@ -21,51 +21,6 @@ #define RKVDEC_NUM_REFLIST 3 -static void set_dpb_info(struct rkvdec_rps_entry *entries, - u8 reflist, - u8 refnum, - u8 info, - bool bottom) -{ - struct rkvdec_rps_entry *entry = &entries[(reflist * 4) + refnum / 8]; - u8 idx = refnum % 8; - - switch (idx) { - case 0: - entry->dpb_info0 = info; - entry->bottom_flag0 = bottom; - break; - case 1: - entry->dpb_info1 = info; - entry->bottom_flag1 = bottom; - break; - case 2: - entry->dpb_info2 = info; - entry->bottom_flag2 = bottom; - break; - case 3: - entry->dpb_info3 = info; - entry->bottom_flag3 = bottom; - break; - case 4: - entry->dpb_info4 = info; - entry->bottom_flag4 = bottom; - break; - case 5: - entry->dpb_info5 = info; - entry->bottom_flag5 = bottom; - break; - case 6: - entry->dpb_info6 = info; - entry->bottom_flag6 = bottom; - break; - case 7: - entry->dpb_info7 = info; - entry->bottom_flag7 = bottom; - break; - } -} - void lookup_ref_buf_idx(struct rkvdec_ctx *ctx, struct rkvdec_h264_run *run) { @@ -111,7 +66,7 @@ void assemble_hw_rps(struct v4l2_h264_reflist_builder *builder, if (!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) continue; - hw_rps->frame_num[i] = builder->refs[i].frame_num; + rkvdec_set_bw_field(hw_rps->info, RPS_FRAME_NUM(i), builder->refs[i].frame_num); } for (j = 0; j < RKVDEC_NUM_REFLIST; j++) { @@ -138,7 +93,9 @@ void assemble_hw_rps(struct v4l2_h264_reflist_builder *builder, dpb_valid = !!(run->ref_buf[ref->index]); bottom = ref->fields == V4L2_H264_BOTTOM_FIELD_REF; - set_dpb_info(hw_rps->entries, j, i, ref->index | (dpb_valid << 4), bottom); + rkvdec_set_bw_field(hw_rps->info, RPS_ENTRY_DPB_INFO(j, i), + ref->index | (dpb_valid << 4)); + rkvdec_set_bw_field(hw_rps->info, RPS_ENTRY_BOTTOM_FLAG(j, i), bottom); } } } diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.h index 5336370507d6..f04b700b863c 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.h +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.h @@ -16,6 +16,7 @@ #include <media/v4l2-mem2mem.h> #include "rkvdec.h" +#include "rkvdec-bitwriter.h" struct rkvdec_h264_scaling_list { u8 scaling_list_4x4[6][16]; @@ -38,39 +39,16 @@ struct rkvdec_h264_run { struct vb2_buffer *ref_buf[V4L2_H264_NUM_DPB_ENTRIES]; }; -struct rkvdec_rps_entry { - u32 dpb_info0: 5; - u32 bottom_flag0: 1; - u32 view_index_off0: 1; - u32 dpb_info1: 5; - u32 bottom_flag1: 1; - u32 view_index_off1: 1; - u32 dpb_info2: 5; - u32 bottom_flag2: 1; - u32 view_index_off2: 1; - u32 dpb_info3: 5; - u32 bottom_flag3: 1; - u32 view_index_off3: 1; - u32 dpb_info4: 5; - u32 bottom_flag4: 1; - u32 view_index_off4: 1; - u32 dpb_info5: 5; - u32 bottom_flag5: 1; - u32 view_index_off5: 1; - u32 dpb_info6: 5; - u32 bottom_flag6: 1; - u32 view_index_off6: 1; - u32 dpb_info7: 5; - u32 bottom_flag7: 1; - u32 view_index_off7: 1; -} __packed; +#define RPS_FRAME_NUM(i) BW_FIELD((i) * 16, 16) +#define RPS_ENTRY_DPB_INFO(l, e) BW_FIELD(288 + (l) * 7 * 32 + (e) * 7, 5) //l: 0-2, e: 0-31 +#define RPS_ENTRY_BOTTOM_FLAG(l, e) BW_FIELD(293 + (l) * 7 * 32 + (e) * 7, 1) //l: 0-2, e: 0-31 +#define RPS_ENTRY_VIEW_INDEX_OFF(l, e) BW_FIELD(294 + (l) * 7 * 32 + (e) * 7, 1) //l: 0-2, e: 0-31 + +#define RKVDEC_H264_RPS_SIZE ALIGN(288 + 3 * 7 * 32, 128) struct rkvdec_rps { - u16 frame_num[16]; - u32 reserved0; - struct rkvdec_rps_entry entries[12]; - u32 reserved1[66]; -} __packed; + u32 info[RKVDEC_H264_RPS_SIZE / 8 / 4]; +}; void lookup_ref_buf_idx(struct rkvdec_ctx *ctx, struct rkvdec_h264_run *run); void assemble_hw_rps(struct v4l2_h264_reflist_builder *builder, diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c index d3202cecb988..ffa606038192 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c @@ -16,6 +16,7 @@ #include "rkvdec-regs.h" #include "rkvdec-cabac.h" #include "rkvdec-h264-common.h" +#include "rkvdec-bitwriter.h" /* Size with u32 units. */ #define RKV_CABAC_INIT_BUFFER_SIZE (3680 + 128) @@ -25,56 +26,48 @@ struct rkvdec_sps_pps_packet { u32 info[8]; }; -struct rkvdec_ps_field { - u16 offset; - u8 len; -}; - -#define PS_FIELD(_offset, _len) \ - ((struct rkvdec_ps_field){ _offset, _len }) - -#define SEQ_PARAMETER_SET_ID PS_FIELD(0, 4) -#define PROFILE_IDC PS_FIELD(4, 8) -#define CONSTRAINT_SET3_FLAG PS_FIELD(12, 1) -#define CHROMA_FORMAT_IDC PS_FIELD(13, 2) -#define BIT_DEPTH_LUMA PS_FIELD(15, 3) -#define BIT_DEPTH_CHROMA PS_FIELD(18, 3) -#define QPPRIME_Y_ZERO_TRANSFORM_BYPASS_FLAG PS_FIELD(21, 1) -#define LOG2_MAX_FRAME_NUM_MINUS4 PS_FIELD(22, 4) -#define MAX_NUM_REF_FRAMES PS_FIELD(26, 5) -#define PIC_ORDER_CNT_TYPE PS_FIELD(31, 2) -#define LOG2_MAX_PIC_ORDER_CNT_LSB_MINUS4 PS_FIELD(33, 4) -#define DELTA_PIC_ORDER_ALWAYS_ZERO_FLAG PS_FIELD(37, 1) -#define PIC_WIDTH_IN_MBS PS_FIELD(38, 9) -#define PIC_HEIGHT_IN_MBS PS_FIELD(47, 9) -#define FRAME_MBS_ONLY_FLAG PS_FIELD(56, 1) -#define MB_ADAPTIVE_FRAME_FIELD_FLAG PS_FIELD(57, 1) -#define DIRECT_8X8_INFERENCE_FLAG PS_FIELD(58, 1) -#define MVC_EXTENSION_ENABLE PS_FIELD(59, 1) -#define NUM_VIEWS PS_FIELD(60, 2) -#define VIEW_ID(i) PS_FIELD(62 + ((i) * 10), 10) -#define NUM_ANCHOR_REFS_L(i) PS_FIELD(82 + ((i) * 11), 1) -#define ANCHOR_REF_L(i) PS_FIELD(83 + ((i) * 11), 10) -#define NUM_NON_ANCHOR_REFS_L(i) PS_FIELD(104 + ((i) * 11), 1) -#define NON_ANCHOR_REFS_L(i) PS_FIELD(105 + ((i) * 11), 10) -#define PIC_PARAMETER_SET_ID PS_FIELD(128, 8) -#define PPS_SEQ_PARAMETER_SET_ID PS_FIELD(136, 5) -#define ENTROPY_CODING_MODE_FLAG PS_FIELD(141, 1) -#define BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT_FLAG PS_FIELD(142, 1) -#define NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(i) PS_FIELD(143 + ((i) * 5), 5) -#define WEIGHTED_PRED_FLAG PS_FIELD(153, 1) -#define WEIGHTED_BIPRED_IDC PS_FIELD(154, 2) -#define PIC_INIT_QP_MINUS26 PS_FIELD(156, 7) -#define PIC_INIT_QS_MINUS26 PS_FIELD(163, 6) -#define CHROMA_QP_INDEX_OFFSET PS_FIELD(169, 5) -#define DEBLOCKING_FILTER_CONTROL_PRESENT_FLAG PS_FIELD(174, 1) -#define CONSTRAINED_INTRA_PRED_FLAG PS_FIELD(175, 1) -#define REDUNDANT_PIC_CNT_PRESENT PS_FIELD(176, 1) -#define TRANSFORM_8X8_MODE_FLAG PS_FIELD(177, 1) -#define SECOND_CHROMA_QP_INDEX_OFFSET PS_FIELD(178, 5) -#define SCALING_LIST_ENABLE_FLAG PS_FIELD(183, 1) -#define SCALING_LIST_ADDRESS PS_FIELD(184, 32) -#define IS_LONG_TERM(i) PS_FIELD(216 + (i), 1) +#define SEQ_PARAMETER_SET_ID BW_FIELD(0, 4) +#define PROFILE_IDC BW_FIELD(4, 8) +#define CONSTRAINT_SET3_FLAG BW_FIELD(12, 1) +#define CHROMA_FORMAT_IDC BW_FIELD(13, 2) +#define BIT_DEPTH_LUMA BW_FIELD(15, 3) +#define BIT_DEPTH_CHROMA BW_FIELD(18, 3) +#define QPPRIME_Y_ZERO_TRANSFORM_BYPASS_FLAG BW_FIELD(21, 1) +#define LOG2_MAX_FRAME_NUM_MINUS4 BW_FIELD(22, 4) +#define MAX_NUM_REF_FRAMES BW_FIELD(26, 5) +#define PIC_ORDER_CNT_TYPE BW_FIELD(31, 2) +#define LOG2_MAX_PIC_ORDER_CNT_LSB_MINUS4 BW_FIELD(33, 4) +#define DELTA_PIC_ORDER_ALWAYS_ZERO_FLAG BW_FIELD(37, 1) +#define PIC_WIDTH_IN_MBS BW_FIELD(38, 9) +#define PIC_HEIGHT_IN_MBS BW_FIELD(47, 9) +#define FRAME_MBS_ONLY_FLAG BW_FIELD(56, 1) +#define MB_ADAPTIVE_FRAME_FIELD_FLAG BW_FIELD(57, 1) +#define DIRECT_8X8_INFERENCE_FLAG BW_FIELD(58, 1) +#define MVC_EXTENSION_ENABLE BW_FIELD(59, 1) +#define NUM_VIEWS BW_FIELD(60, 2) +#define VIEW_ID(i) BW_FIELD(62 + ((i) * 10), 10) +#define NUM_ANCHOR_REFS_L(i) BW_FIELD(82 + ((i) * 11), 1) +#define ANCHOR_REF_L(i) BW_FIELD(83 + ((i) * 11), 10) +#define NUM_NON_ANCHOR_REFS_L(i) BW_FIELD(104 + ((i) * 11), 1) +#define NON_ANCHOR_REFS_L(i) BW_FIELD(105 + ((i) * 11), 10) +#define PIC_PARAMETER_SET_ID BW_FIELD(128, 8) +#define PPS_SEQ_PARAMETER_SET_ID BW_FIELD(136, 5) +#define ENTROPY_CODING_MODE_FLAG BW_FIELD(141, 1) +#define BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT_FLAG BW_FIELD(142, 1) +#define NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(i) BW_FIELD(143 + ((i) * 5), 5) +#define WEIGHTED_PRED_FLAG BW_FIELD(153, 1) +#define WEIGHTED_BIPRED_IDC BW_FIELD(154, 2) +#define PIC_INIT_QP_MINUS26 BW_FIELD(156, 7) +#define PIC_INIT_QS_MINUS26 BW_FIELD(163, 6) +#define CHROMA_QP_INDEX_OFFSET BW_FIELD(169, 5) +#define DEBLOCKING_FILTER_CONTROL_PRESENT_FLAG BW_FIELD(174, 1) +#define CONSTRAINED_INTRA_PRED_FLAG BW_FIELD(175, 1) +#define REDUNDANT_PIC_CNT_PRESENT BW_FIELD(176, 1) +#define TRANSFORM_8X8_MODE_FLAG BW_FIELD(177, 1) +#define SECOND_CHROMA_QP_INDEX_OFFSET BW_FIELD(178, 5) +#define SCALING_LIST_ENABLE_FLAG BW_FIELD(183, 1) +#define SCALING_LIST_ADDRESS BW_FIELD(184, 32) +#define IS_LONG_TERM(i) BW_FIELD(216 + (i), 1) /* Data structure describing auxiliary buffer format. */ struct rkvdec_h264_priv_tbl { @@ -91,20 +84,6 @@ struct rkvdec_h264_ctx { struct rkvdec_regs regs; }; -static void set_ps_field(u32 *buf, struct rkvdec_ps_field field, u32 value) -{ - u8 bit = field.offset % 32, word = field.offset / 32; - u64 mask = GENMASK_ULL(bit + field.len - 1, bit); - u64 val = ((u64)value << bit) & mask; - - buf[word] &= ~mask; - buf[word] |= val; - if (bit + field.len > 32) { - buf[word + 1] &= ~(mask >> 32); - buf[word + 1] |= val >> 32; - } -} - static void assemble_hw_pps(struct rkvdec_ctx *ctx, struct rkvdec_h264_run *run) { @@ -128,7 +107,7 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx, hw_ps = &priv_tbl->param_set[pps->pic_parameter_set_id]; memset(hw_ps, 0, sizeof(*hw_ps)); -#define WRITE_PPS(value, field) set_ps_field(hw_ps->info, field, value) +#define WRITE_PPS(value, field) rkvdec_set_bw_field(hw_ps->info, field, value) /* write sps */ WRITE_PPS(sps->seq_parameter_set_id, SEQ_PARAMETER_SET_ID); WRITE_PPS(sps->profile_idc, PROFILE_IDC); diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c index 3119f3bc9f98..f89602075121 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c @@ -74,72 +74,6 @@ void compute_tiles_non_uniform(struct rkvdec_hevc_run *run, u16 log2_min_cb_size row_height[i] = pic_in_cts_height - sum; } -static void set_ref_poc(struct rkvdec_rps_short_term_ref_set *set, int poc, int value, int flag) -{ - switch (poc) { - case 0: - set->delta_poc0 = value; - set->used_flag0 = flag; - break; - case 1: - set->delta_poc1 = value; - set->used_flag1 = flag; - break; - case 2: - set->delta_poc2 = value; - set->used_flag2 = flag; - break; - case 3: - set->delta_poc3 = value; - set->used_flag3 = flag; - break; - case 4: - set->delta_poc4 = value; - set->used_flag4 = flag; - break; - case 5: - set->delta_poc5 = value; - set->used_flag5 = flag; - break; - case 6: - set->delta_poc6 = value; - set->used_flag6 = flag; - break; - case 7: - set->delta_poc7 = value; - set->used_flag7 = flag; - break; - case 8: - set->delta_poc8 = value; - set->used_flag8 = flag; - break; - case 9: - set->delta_poc9 = value; - set->used_flag9 = flag; - break; - case 10: - set->delta_poc10 = value; - set->used_flag10 = flag; - break; - case 11: - set->delta_poc11 = value; - set->used_flag11 = flag; - break; - case 12: - set->delta_poc12 = value; - set->used_flag12 = flag; - break; - case 13: - set->delta_poc13 = value; - set->used_flag13 = flag; - break; - case 14: - set->delta_poc14 = value; - set->used_flag14 = flag; - break; - } -} - static void assemble_scalingfactor0(struct rkvdec_ctx *ctx, u8 *output, const struct v4l2_ctrl_hevc_scaling_matrix *input) { @@ -218,10 +152,11 @@ static void rkvdec_hevc_assemble_hw_lt_rps(struct rkvdec_hevc_run *run, struct r return; for (int i = 0; i < sps->num_long_term_ref_pics_sps; i++) { - rps->refs[i].lt_ref_pic_poc_lsb = - run->ext_sps_lt_rps[i].lt_ref_pic_poc_lsb_sps; - rps->refs[i].used_by_curr_pic_lt_flag = - !!(run->ext_sps_lt_rps[i].flags & V4L2_HEVC_EXT_SPS_LT_RPS_FLAG_USED_LT); + rkvdec_set_bw_field(rps->info, RPS_LT_REF_PIC_POC_LSB(i), + run->ext_sps_lt_rps[i].lt_ref_pic_poc_lsb_sps); + rkvdec_set_bw_field(rps->info, RPS_LT_REF_USED_BY_CURR_PIC(i), + !!(run->ext_sps_lt_rps[i].flags & + V4L2_HEVC_EXT_SPS_LT_RPS_FLAG_USED_LT)); } } @@ -235,18 +170,24 @@ static void rkvdec_hevc_assemble_hw_st_rps(struct rkvdec_hevc_run *run, struct r int j = 0; const struct calculated_rps_st_set *set = &calculated_rps_st_sets[i]; - rps->short_term_ref_sets[i].num_negative = set->num_negative_pics; - rps->short_term_ref_sets[i].num_positive = set->num_positive_pics; + rkvdec_set_bw_field(rps->info, RPS_ST_REF_SET_NUM_NEGATIVE(i), + set->num_negative_pics); + rkvdec_set_bw_field(rps->info, RPS_ST_REF_SET_NUM_POSITIVE(i), + set->num_positive_pics); for (; j < set->num_negative_pics; j++) { - set_ref_poc(&rps->short_term_ref_sets[i], j, - set->delta_poc_s0[j], set->used_by_curr_pic_s0[j]); + rkvdec_set_bw_field(rps->info, RPS_ST_REF_SET_DELTA_POC(i, j), + set->delta_poc_s0[j]); + rkvdec_set_bw_field(rps->info, RPS_ST_REF_SET_USED(i, j), + set->used_by_curr_pic_s0[j]); } poc = j; for (j = 0; j < set->num_positive_pics; j++) { - set_ref_poc(&rps->short_term_ref_sets[i], poc + j, - set->delta_poc_s1[j], set->used_by_curr_pic_s1[j]); + rkvdec_set_bw_field(rps->info, RPS_ST_REF_SET_DELTA_POC(i, poc + j), + set->delta_poc_s1[j]); + rkvdec_set_bw_field(rps->info, RPS_ST_REF_SET_USED(i, poc + j), + set->used_by_curr_pic_s1[j]); } } } diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.h index 6f4faca4c091..2a9b7719ab2d 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.h +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.h @@ -19,53 +19,24 @@ #include <linux/types.h> #include "rkvdec.h" +#include "rkvdec-bitwriter.h" -struct rkvdec_rps_refs { - u16 lt_ref_pic_poc_lsb; - u16 used_by_curr_pic_lt_flag : 1; - u16 reserved : 15; -} __packed; +#define RPS_LT_REF_PIC_POC_LSB(i) BW_FIELD(0 + (i) * 32, 16) // i: 0-31 +#define RPS_LT_REF_USED_BY_CURR_PIC(i) BW_FIELD(16 + (i) * 32, 1) // i: 0-31 -struct rkvdec_rps_short_term_ref_set { - u32 num_negative : 4; - u32 num_positive : 4; - u32 delta_poc0 : 16; - u32 used_flag0 : 1; - u32 delta_poc1 : 16; - u32 used_flag1 : 1; - u32 delta_poc2 : 16; - u32 used_flag2 : 1; - u32 delta_poc3 : 16; - u32 used_flag3 : 1; - u32 delta_poc4 : 16; - u32 used_flag4 : 1; - u32 delta_poc5 : 16; - u32 used_flag5 : 1; - u32 delta_poc6 : 16; - u32 used_flag6 : 1; - u32 delta_poc7 : 16; - u32 used_flag7 : 1; - u32 delta_poc8 : 16; - u32 used_flag8 : 1; - u32 delta_poc9 : 16; - u32 used_flag9 : 1; - u32 delta_poc10 : 16; - u32 used_flag10 : 1; - u32 delta_poc11 : 16; - u32 used_flag11 : 1; - u32 delta_poc12 : 16; - u32 used_flag12 : 1; - u32 delta_poc13 : 16; - u32 used_flag13 : 1; - u32 delta_poc14 : 16; - u32 used_flag14 : 1; - u32 reserved_bits : 25; - u32 reserved[3]; -} __packed; +#define RPS_ST_REF_SET_NUM_NEGATIVE(i) BW_FIELD(1024 + ((i) * 384), 4) // i: 0-63 +#define RPS_ST_REF_SET_NUM_POSITIVE(i) BW_FIELD(1028 + ((i) * 384), 4) // i: 0-63 + +// i: 0-63, j: 0-14 +#define RPS_ST_REF_SET_DELTA_POC(i, j) BW_FIELD(1032 + ((i) * 384) + ((j) * 17), 16) + +// i: 0-63, j: 0-14 +#define RPS_ST_REF_SET_USED(i, j) BW_FIELD(1048 + ((i) * 384) + ((j) * 17), 1) + +#define RKVDEC_RPS_HEVC_SIZE ALIGN(1032 + 64 * 384, 128) struct rkvdec_rps { - struct rkvdec_rps_refs refs[32]; - struct rkvdec_rps_short_term_ref_set short_term_ref_sets[64]; + u32 info[RKVDEC_RPS_HEVC_SIZE / 8 / 4]; } __packed; struct rkvdec_hevc_run { diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c index ac8b825d080a..87abf93dfd5e 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c @@ -18,6 +18,7 @@ #include "rkvdec-regs.h" #include "rkvdec-cabac.h" #include "rkvdec-hevc-common.h" +#include "rkvdec-bitwriter.h" /* Size in u8/u32 units. */ #define RKV_SCALING_LIST_SIZE 1360 @@ -34,80 +35,72 @@ struct rkvdec_rps_packet { u32 info[RKV_RPS_SIZE]; }; -struct rkvdec_ps_field { - u16 offset; - u8 len; -}; - -#define PS_FIELD(_offset, _len) \ - ((struct rkvdec_ps_field){ _offset, _len }) - /* SPS */ -#define VIDEO_PARAMETER_SET_ID PS_FIELD(0, 4) -#define SEQ_PARAMETER_SET_ID PS_FIELD(4, 4) -#define CHROMA_FORMAT_IDC PS_FIELD(8, 2) -#define PIC_WIDTH_IN_LUMA_SAMPLES PS_FIELD(10, 13) -#define PIC_HEIGHT_IN_LUMA_SAMPLES PS_FIELD(23, 13) -#define BIT_DEPTH_LUMA PS_FIELD(36, 4) -#define BIT_DEPTH_CHROMA PS_FIELD(40, 4) -#define LOG2_MAX_PIC_ORDER_CNT_LSB PS_FIELD(44, 5) -#define LOG2_DIFF_MAX_MIN_LUMA_CODING_BLOCK_SIZE PS_FIELD(49, 2) -#define LOG2_MIN_LUMA_CODING_BLOCK_SIZE PS_FIELD(51, 3) -#define LOG2_MIN_TRANSFORM_BLOCK_SIZE PS_FIELD(54, 3) -#define LOG2_DIFF_MAX_MIN_LUMA_TRANSFORM_BLOCK_SIZE PS_FIELD(57, 2) -#define MAX_TRANSFORM_HIERARCHY_DEPTH_INTER PS_FIELD(59, 3) -#define MAX_TRANSFORM_HIERARCHY_DEPTH_INTRA PS_FIELD(62, 3) -#define SCALING_LIST_ENABLED_FLAG PS_FIELD(65, 1) -#define AMP_ENABLED_FLAG PS_FIELD(66, 1) -#define SAMPLE_ADAPTIVE_OFFSET_ENABLED_FLAG PS_FIELD(67, 1) -#define PCM_ENABLED_FLAG PS_FIELD(68, 1) -#define PCM_SAMPLE_BIT_DEPTH_LUMA PS_FIELD(69, 4) -#define PCM_SAMPLE_BIT_DEPTH_CHROMA PS_FIELD(73, 4) -#define PCM_LOOP_FILTER_DISABLED_FLAG PS_FIELD(77, 1) -#define LOG2_DIFF_MAX_MIN_PCM_LUMA_CODING_BLOCK_SIZE PS_FIELD(78, 3) -#define LOG2_MIN_PCM_LUMA_CODING_BLOCK_SIZE PS_FIELD(81, 3) -#define NUM_SHORT_TERM_REF_PIC_SETS PS_FIELD(84, 7) -#define LONG_TERM_REF_PICS_PRESENT_FLAG PS_FIELD(91, 1) -#define NUM_LONG_TERM_REF_PICS_SPS PS_FIELD(92, 6) -#define SPS_TEMPORAL_MVP_ENABLED_FLAG PS_FIELD(98, 1) -#define STRONG_INTRA_SMOOTHING_ENABLED_FLAG PS_FIELD(99, 1) +#define VIDEO_PARAMETER_SET_ID BW_FIELD(0, 4) +#define SEQ_PARAMETER_SET_ID BW_FIELD(4, 4) +#define CHROMA_FORMAT_IDC BW_FIELD(8, 2) +#define PIC_WIDTH_IN_LUMA_SAMPLES BW_FIELD(10, 13) +#define PIC_HEIGHT_IN_LUMA_SAMPLES BW_FIELD(23, 13) +#define BIT_DEPTH_LUMA BW_FIELD(36, 4) +#define BIT_DEPTH_CHROMA BW_FIELD(40, 4) +#define LOG2_MAX_PIC_ORDER_CNT_LSB BW_FIELD(44, 5) +#define LOG2_DIFF_MAX_MIN_LUMA_CODING_BLOCK_SIZE BW_FIELD(49, 2) +#define LOG2_MIN_LUMA_CODING_BLOCK_SIZE BW_FIELD(51, 3) +#define LOG2_MIN_TRANSFORM_BLOCK_SIZE BW_FIELD(54, 3) +#define LOG2_DIFF_MAX_MIN_LUMA_TRANSFORM_BLOCK_SIZE BW_FIELD(57, 2) +#define MAX_TRANSFORM_HIERARCHY_DEPTH_INTER BW_FIELD(59, 3) +#define MAX_TRANSFORM_HIERARCHY_DEPTH_INTRA BW_FIELD(62, 3) +#define SCALING_LIST_ENABLED_FLAG BW_FIELD(65, 1) +#define AMP_ENABLED_FLAG BW_FIELD(66, 1) +#define SAMPLE_ADAPTIVE_OFFSET_ENABLED_FLAG BW_FIELD(67, 1) +#define PCM_ENABLED_FLAG BW_FIELD(68, 1) +#define PCM_SAMPLE_BIT_DEPTH_LUMA BW_FIELD(69, 4) +#define PCM_SAMPLE_BIT_DEPTH_CHROMA BW_FIELD(73, 4) +#define PCM_LOOP_FILTER_DISABLED_FLAG BW_FIELD(77, 1) +#define LOG2_DIFF_MAX_MIN_PCM_LUMA_CODING_BLOCK_SIZE BW_FIELD(78, 3) +#define LOG2_MIN_PCM_LUMA_CODING_BLOCK_SIZE BW_FIELD(81, 3) +#define NUM_SHORT_TERM_REF_PIC_SETS BW_FIELD(84, 7) +#define LONG_TERM_REF_PICS_PRESENT_FLAG BW_FIELD(91, 1) +#define NUM_LONG_TERM_REF_PICS_SPS BW_FIELD(92, 6) +#define SPS_TEMPORAL_MVP_ENABLED_FLAG BW_FIELD(98, 1) +#define STRONG_INTRA_SMOOTHING_ENABLED_FLAG BW_FIELD(99, 1) /* PPS */ -#define PIC_PARAMETER_SET_ID PS_FIELD(128, 6) -#define PPS_SEQ_PARAMETER_SET_ID PS_FIELD(134, 4) -#define DEPENDENT_SLICE_SEGMENTS_ENABLED_FLAG PS_FIELD(138, 1) -#define OUTPUT_FLAG_PRESENT_FLAG PS_FIELD(139, 1) -#define NUM_EXTRA_SLICE_HEADER_BITS PS_FIELD(140, 13) -#define SIGN_DATA_HIDING_ENABLED_FLAG PS_FIELD(153, 1) -#define CABAC_INIT_PRESENT_FLAG PS_FIELD(154, 1) -#define NUM_REF_IDX_L0_DEFAULT_ACTIVE PS_FIELD(155, 4) -#define NUM_REF_IDX_L1_DEFAULT_ACTIVE PS_FIELD(159, 4) -#define INIT_QP_MINUS26 PS_FIELD(163, 7) -#define CONSTRAINED_INTRA_PRED_FLAG PS_FIELD(170, 1) -#define TRANSFORM_SKIP_ENABLED_FLAG PS_FIELD(171, 1) -#define CU_QP_DELTA_ENABLED_FLAG PS_FIELD(172, 1) -#define LOG2_MIN_CU_QP_DELTA_SIZE PS_FIELD(173, 3) -#define PPS_CB_QP_OFFSET PS_FIELD(176, 5) -#define PPS_CR_QP_OFFSET PS_FIELD(181, 5) -#define PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT_FLAG PS_FIELD(186, 1) -#define WEIGHTED_PRED_FLAG PS_FIELD(187, 1) -#define WEIGHTED_BIPRED_FLAG PS_FIELD(188, 1) -#define TRANSQUANT_BYPASS_ENABLED_FLAG PS_FIELD(189, 1) -#define TILES_ENABLED_FLAG PS_FIELD(190, 1) -#define ENTROPY_CODING_SYNC_ENABLED_FLAG PS_FIELD(191, 1) -#define PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG PS_FIELD(192, 1) -#define LOOP_FILTER_ACROSS_TILES_ENABLED_FLAG PS_FIELD(193, 1) -#define DEBLOCKING_FILTER_OVERRIDE_ENABLED_FLAG PS_FIELD(194, 1) -#define PPS_DEBLOCKING_FILTER_DISABLED_FLAG PS_FIELD(195, 1) -#define PPS_BETA_OFFSET_DIV2 PS_FIELD(196, 4) -#define PPS_TC_OFFSET_DIV2 PS_FIELD(200, 4) -#define LISTS_MODIFICATION_PRESENT_FLAG PS_FIELD(204, 1) -#define LOG2_PARALLEL_MERGE_LEVEL PS_FIELD(205, 3) -#define SLICE_SEGMENT_HEADER_EXTENSION_PRESENT_FLAG PS_FIELD(208, 1) -#define NUM_TILE_COLUMNS PS_FIELD(212, 5) -#define NUM_TILE_ROWS PS_FIELD(217, 5) -#define COLUMN_WIDTH(i) PS_FIELD(256 + ((i) * 8), 8) -#define ROW_HEIGHT(i) PS_FIELD(416 + ((i) * 8), 8) -#define SCALING_LIST_ADDRESS PS_FIELD(592, 32) +#define PIC_PARAMETER_SET_ID BW_FIELD(128, 6) +#define PPS_SEQ_PARAMETER_SET_ID BW_FIELD(134, 4) +#define DEPENDENT_SLICE_SEGMENTS_ENABLED_FLAG BW_FIELD(138, 1) +#define OUTPUT_FLAG_PRESENT_FLAG BW_FIELD(139, 1) +#define NUM_EXTRA_SLICE_HEADER_BITS BW_FIELD(140, 13) +#define SIGN_DATA_HIDING_ENABLED_FLAG BW_FIELD(153, 1) +#define CABAC_INIT_PRESENT_FLAG BW_FIELD(154, 1) +#define NUM_REF_IDX_L0_DEFAULT_ACTIVE BW_FIELD(155, 4) +#define NUM_REF_IDX_L1_DEFAULT_ACTIVE BW_FIELD(159, 4) +#define INIT_QP_MINUS26 BW_FIELD(163, 7) +#define CONSTRAINED_INTRA_PRED_FLAG BW_FIELD(170, 1) +#define TRANSFORM_SKIP_ENABLED_FLAG BW_FIELD(171, 1) +#define CU_QP_DELTA_ENABLED_FLAG BW_FIELD(172, 1) +#define LOG2_MIN_CU_QP_DELTA_SIZE BW_FIELD(173, 3) +#define PPS_CB_QP_OFFSET BW_FIELD(176, 5) +#define PPS_CR_QP_OFFSET BW_FIELD(181, 5) +#define PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT_FLAG BW_FIELD(186, 1) +#define WEIGHTED_PRED_FLAG BW_FIELD(187, 1) +#define WEIGHTED_BIPRED_FLAG BW_FIELD(188, 1) +#define TRANSQUANT_BYPASS_ENABLED_FLAG BW_FIELD(189, 1) +#define TILES_ENABLED_FLAG BW_FIELD(190, 1) +#define ENTROPY_CODING_SYNC_ENABLED_FLAG BW_FIELD(191, 1) +#define PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG BW_FIELD(192, 1) +#define LOOP_FILTER_ACROSS_TILES_ENABLED_FLAG BW_FIELD(193, 1) +#define DEBLOCKING_FILTER_OVERRIDE_ENABLED_FLAG BW_FIELD(194, 1) +#define PPS_DEBLOCKING_FILTER_DISABLED_FLAG BW_FIELD(195, 1) +#define PPS_BETA_OFFSET_DIV2 BW_FIELD(196, 4) +#define PPS_TC_OFFSET_DIV2 BW_FIELD(200, 4) +#define LISTS_MODIFICATION_PRESENT_FLAG BW_FIELD(204, 1) +#define LOG2_PARALLEL_MERGE_LEVEL BW_FIELD(205, 3) +#define SLICE_SEGMENT_HEADER_EXTENSION_PRESENT_FLAG BW_FIELD(208, 1) +#define NUM_TILE_COLUMNS BW_FIELD(212, 5) +#define NUM_TILE_ROWS BW_FIELD(217, 5) +#define COLUMN_WIDTH(i) BW_FIELD(256 + ((i) * 8), 8) +#define ROW_HEIGHT(i) BW_FIELD(416 + ((i) * 8), 8) +#define SCALING_LIST_ADDRESS BW_FIELD(592, 32) /* Data structure describing auxiliary buffer format. */ struct rkvdec_hevc_priv_tbl { @@ -123,20 +116,6 @@ struct rkvdec_hevc_ctx { struct rkvdec_regs regs; }; -static void set_ps_field(u32 *buf, struct rkvdec_ps_field field, u32 value) -{ - u8 bit = field.offset % 32, word = field.offset / 32; - u64 mask = GENMASK_ULL(bit + field.len - 1, bit); - u64 val = ((u64)value << bit) & mask; - - buf[word] &= ~mask; - buf[word] |= val; - if (bit + field.len > 32) { - buf[word + 1] &= ~(mask >> 32); - buf[word + 1] |= val >> 32; - } -} - static void assemble_hw_pps(struct rkvdec_ctx *ctx, struct rkvdec_hevc_run *run) { @@ -159,7 +138,7 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx, hw_ps = &priv_tbl->param_set[pps->pic_parameter_set_id]; memset(hw_ps, 0, sizeof(*hw_ps)); -#define WRITE_PPS(value, field) set_ps_field(hw_ps->info, field, value) +#define WRITE_PPS(value, field) rkvdec_set_bw_field(hw_ps->info, field, value) /* write sps */ WRITE_PPS(sps->video_parameter_set_id, VIDEO_PARAMETER_SET_ID); WRITE_PPS(sps->seq_parameter_set_id, SEQ_PARAMETER_SET_ID); @@ -321,17 +300,17 @@ static void assemble_sw_rps(struct rkvdec_ctx *ctx, int i, j; unsigned int lowdelay; -#define WRITE_RPS(value, field) set_ps_field(hw_ps->info, field, value) +#define WRITE_RPS(value, field) rkvdec_set_bw_field(hw_ps->info, field, value) -#define REF_PIC_LONG_TERM_L0(i) PS_FIELD((i) * 5, 1) -#define REF_PIC_IDX_L0(i) PS_FIELD(1 + ((i) * 5), 4) -#define REF_PIC_LONG_TERM_L1(i) PS_FIELD(((i) < 5 ? 75 : 132) + ((i) * 5), 1) -#define REF_PIC_IDX_L1(i) PS_FIELD(((i) < 4 ? 76 : 128) + ((i) * 5), 4) +#define REF_PIC_LONG_TERM_L0(n) BW_FIELD((n) * 5, 1) +#define REF_PIC_IDX_L0(n) BW_FIELD(1 + ((n) * 5), 4) +#define REF_PIC_LONG_TERM_L1(n) BW_FIELD(((n) < 5 ? 75 : 132) + ((n) * 5), 1) +#define REF_PIC_IDX_L1(n) BW_FIELD(((n) < 4 ? 76 : 128) + ((n) * 5), 4) -#define LOWDELAY PS_FIELD(182, 1) -#define LONG_TERM_RPS_BIT_OFFSET PS_FIELD(183, 10) -#define SHORT_TERM_RPS_BIT_OFFSET PS_FIELD(193, 9) -#define NUM_RPS_POC PS_FIELD(202, 4) +#define LOWDELAY BW_FIELD(182, 1) +#define LONG_TERM_RPS_BIT_OFFSET BW_FIELD(183, 10) +#define SHORT_TERM_RPS_BIT_OFFSET BW_FIELD(193, 9) +#define NUM_RPS_POC BW_FIELD(202, 4) for (j = 0; j < run->num_slices; j++) { uint st_bit_offset = 0; diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-h264.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-h264.c index fb4f849d7366..5ec755733916 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-h264.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-h264.c @@ -15,105 +15,64 @@ #include "rkvdec-cabac.h" #include "rkvdec-vdpu383-regs.h" #include "rkvdec-h264-common.h" - -struct rkvdec_sps { - u16 seq_parameter_set_id: 4; - u16 profile_idc: 8; - u16 constraint_set3_flag: 1; - u16 chroma_format_idc: 2; - u16 bit_depth_luma: 3; - u16 bit_depth_chroma: 3; - u16 qpprime_y_zero_transform_bypass_flag: 1; - u16 log2_max_frame_num_minus4: 4; - u16 max_num_ref_frames: 5; - u16 pic_order_cnt_type: 2; - u16 log2_max_pic_order_cnt_lsb_minus4: 4; - u16 delta_pic_order_always_zero_flag: 1; - - u16 pic_width_in_mbs: 16; - u16 pic_height_in_mbs: 16; - - u16 frame_mbs_only_flag: 1; - u16 mb_adaptive_frame_field_flag: 1; - u16 direct_8x8_inference_flag: 1; - u16 mvc_extension_enable: 1; - u16 num_views: 2; - u16 view_id0: 10; - u16 view_id1: 10; -} __packed; - -struct rkvdec_pps { - u32 pic_parameter_set_id: 8; - u32 pps_seq_parameter_set_id: 5; - u32 entropy_coding_mode_flag: 1; - u32 bottom_field_pic_order_in_frame_present_flag: 1; - u32 num_ref_idx_l0_default_active_minus1: 5; - u32 num_ref_idx_l1_default_active_minus1: 5; - u32 weighted_pred_flag: 1; - u32 weighted_bipred_idc: 2; - u32 pic_init_qp_minus26: 7; - u32 pic_init_qs_minus26: 6; - u32 chroma_qp_index_offset: 5; - u32 deblocking_filter_control_present_flag: 1; - u32 constrained_intra_pred_flag: 1; - u32 redundant_pic_cnt_present: 1; - u32 transform_8x8_mode_flag: 1; - u32 second_chroma_qp_index_offset: 5; - u32 scaling_list_enable_flag: 1; - u32 is_longterm: 16; - u32 voidx: 16; - - // dpb - u32 pic_field_flag: 1; - u32 pic_associated_flag: 1; - u32 cur_top_field: 32; - u32 cur_bot_field: 32; - - u32 top_field_order_cnt0: 32; - u32 bot_field_order_cnt0: 32; - u32 top_field_order_cnt1: 32; - u32 bot_field_order_cnt1: 32; - u32 top_field_order_cnt2: 32; - u32 bot_field_order_cnt2: 32; - u32 top_field_order_cnt3: 32; - u32 bot_field_order_cnt3: 32; - u32 top_field_order_cnt4: 32; - u32 bot_field_order_cnt4: 32; - u32 top_field_order_cnt5: 32; - u32 bot_field_order_cnt5: 32; - u32 top_field_order_cnt6: 32; - u32 bot_field_order_cnt6: 32; - u32 top_field_order_cnt7: 32; - u32 bot_field_order_cnt7: 32; - u32 top_field_order_cnt8: 32; - u32 bot_field_order_cnt8: 32; - u32 top_field_order_cnt9: 32; - u32 bot_field_order_cnt9: 32; - u32 top_field_order_cnt10: 32; - u32 bot_field_order_cnt10: 32; - u32 top_field_order_cnt11: 32; - u32 bot_field_order_cnt11: 32; - u32 top_field_order_cnt12: 32; - u32 bot_field_order_cnt12: 32; - u32 top_field_order_cnt13: 32; - u32 bot_field_order_cnt13: 32; - u32 top_field_order_cnt14: 32; - u32 bot_field_order_cnt14: 32; - u32 top_field_order_cnt15: 32; - u32 bot_field_order_cnt15: 32; - - u32 ref_field_flags: 16; - u32 ref_topfield_used: 16; - u32 ref_botfield_used: 16; - u32 ref_colmv_use_flag: 16; - - u32 reserved0: 30; - u32 reserved[3]; -} __packed; +#include "rkvdec-bitwriter.h" + +#define SEQ_PARAMETER_SET_ID BW_FIELD(0, 4) +#define PROFILE_IDC BW_FIELD(4, 8) +#define CONSTRAINT_SET3_FLAG BW_FIELD(12, 1) +#define CHROMA_FORMAT_IDC BW_FIELD(13, 2) +#define BIT_DEPTH_LUMA BW_FIELD(15, 3) +#define BIT_DEPTH_CHROMA BW_FIELD(18, 3) +#define QPPRIME_Y_ZERO_TRANSFORM_BYPASS_FLAG BW_FIELD(21, 1) +#define LOG2_MAX_FRAME_NUM_MINUS4 BW_FIELD(22, 4) +#define MAX_NUM_REF_FRAMES BW_FIELD(26, 5) +#define PIC_ORDER_CNT_TYPE BW_FIELD(31, 2) +#define LOG2_MAX_PIC_ORDER_CNT_LSB_MINUS4 BW_FIELD(33, 4) +#define DELTA_PIC_ORDER_ALWAYS_ZERO_FLAG BW_FIELD(37, 1) +#define PIC_WIDTH_IN_MBS BW_FIELD(38, 16) +#define PIC_HEIGHT_IN_MBS BW_FIELD(54, 16) +#define FRAME_MBS_ONLY_FLAG BW_FIELD(70, 1) +#define MB_ADAPTIVE_FRAME_FIELD_FLAG BW_FIELD(71, 1) +#define DIRECT_8X8_INFERENCE_FLAG BW_FIELD(72, 1) +#define MVC_EXTENSION_ENABLE BW_FIELD(73, 1) +#define NUM_VIEWS BW_FIELD(74, 2) +#define VIEW_ID(i) BW_FIELD(76 + ((i) * 10), 10) // i: 0-1 + +#define PIC_PARAMETER_SET_ID BW_FIELD(96, 8) +#define PPS_SEQ_PARAMETER_SET_ID BW_FIELD(104, 5) +#define ENTROPY_CODING_MODE_FLAG BW_FIELD(109, 1) +#define BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT_FLAG BW_FIELD(110, 1) +#define NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(i) BW_FIELD(111 + ((i) * 5), 5) // i: 0-1 +#define WEIGHTED_PRED_FLAG BW_FIELD(121, 1) +#define WEIGHTED_BIPRED_IDC BW_FIELD(122, 2) +#define PIC_INIT_QP_MINUS26 BW_FIELD(124, 7) +#define PIC_INIT_QS_MINUS26 BW_FIELD(131, 6) +#define CHROMA_QP_INDEX_OFFSET BW_FIELD(137, 5) +#define DEBLOCKING_FILTER_CONTROL_PRESENT_FLAG BW_FIELD(142, 1) +#define CONSTRAINED_INTRA_PRED_FLAG BW_FIELD(143, 1) +#define REDUNDANT_PIC_CNT_PRESENT BW_FIELD(144, 1) +#define TRANSFORM_8X8_MODE_FLAG BW_FIELD(145, 1) +#define SECOND_CHROMA_QP_INDEX_OFFSET BW_FIELD(146, 5) +#define SCALING_LIST_ENABLE_FLAG BW_FIELD(151, 1) +#define IS_LONG_TERM(i) BW_FIELD(152 + (i), 1) // i: 0-15 + +#define PIC_FIELD_FLAG BW_FIELD(184, 1) +#define PIC_ASSOCIATED_FLAG BW_FIELD(185, 1) +#define CUR_TOP_FIELD BW_FIELD(186, 32) +#define CUR_BOT_FIELD BW_FIELD(218, 32) + +#define TOP_FIELD_ORDER_CNT(i) BW_FIELD(250 + (i) * 64, 32) // i: 0-15 +#define BOT_FIELD_ORDER_CNT(i) BW_FIELD(282 + (i) * 64, 32) // i: 0-15 + +#define REF_FIELD_FLAGS(i) BW_FIELD(1274 + (i), 1) // i: 0-15 +#define REF_TOPFIELD_USED(i) BW_FIELD(1290 + (i), 1) // i: 0-15 +#define REF_BOTFIELD_USED(i) BW_FIELD(1306 + (i), 1) // i: 0-15 +#define REF_COLMV_USE_FLAG(i) BW_FIELD(1322 + (i), 1) // i: 0-15 + +#define SPS_SIZE ALIGN(1322 + 16, 128) struct rkvdec_sps_pps { - struct rkvdec_sps sps; - struct rkvdec_pps pps; + u32 info[SPS_SIZE / 8 / 4]; } __packed; /* Data structure describing auxiliary buffer format. */ @@ -130,67 +89,6 @@ struct rkvdec_h264_ctx { struct vdpu383_regs_h26x regs; }; -static noinline_for_stack void set_field_order_cnt(struct rkvdec_pps *pps, const struct v4l2_h264_dpb_entry *dpb) -{ - pps->top_field_order_cnt0 = dpb[0].top_field_order_cnt; - pps->bot_field_order_cnt0 = dpb[0].bottom_field_order_cnt; - pps->top_field_order_cnt1 = dpb[1].top_field_order_cnt; - pps->bot_field_order_cnt1 = dpb[1].bottom_field_order_cnt; - pps->top_field_order_cnt2 = dpb[2].top_field_order_cnt; - pps->bot_field_order_cnt2 = dpb[2].bottom_field_order_cnt; - pps->top_field_order_cnt3 = dpb[3].top_field_order_cnt; - pps->bot_field_order_cnt3 = dpb[3].bottom_field_order_cnt; - pps->top_field_order_cnt4 = dpb[4].top_field_order_cnt; - pps->bot_field_order_cnt4 = dpb[4].bottom_field_order_cnt; - pps->top_field_order_cnt5 = dpb[5].top_field_order_cnt; - pps->bot_field_order_cnt5 = dpb[5].bottom_field_order_cnt; - pps->top_field_order_cnt6 = dpb[6].top_field_order_cnt; - pps->bot_field_order_cnt6 = dpb[6].bottom_field_order_cnt; - pps->top_field_order_cnt7 = dpb[7].top_field_order_cnt; - pps->bot_field_order_cnt7 = dpb[7].bottom_field_order_cnt; - pps->top_field_order_cnt8 = dpb[8].top_field_order_cnt; - pps->bot_field_order_cnt8 = dpb[8].bottom_field_order_cnt; - pps->top_field_order_cnt9 = dpb[9].top_field_order_cnt; - pps->bot_field_order_cnt9 = dpb[9].bottom_field_order_cnt; - pps->top_field_order_cnt10 = dpb[10].top_field_order_cnt; - pps->bot_field_order_cnt10 = dpb[10].bottom_field_order_cnt; - pps->top_field_order_cnt11 = dpb[11].top_field_order_cnt; - pps->bot_field_order_cnt11 = dpb[11].bottom_field_order_cnt; - pps->top_field_order_cnt12 = dpb[12].top_field_order_cnt; - pps->bot_field_order_cnt12 = dpb[12].bottom_field_order_cnt; - pps->top_field_order_cnt13 = dpb[13].top_field_order_cnt; - pps->bot_field_order_cnt13 = dpb[13].bottom_field_order_cnt; - pps->top_field_order_cnt14 = dpb[14].top_field_order_cnt; - pps->bot_field_order_cnt14 = dpb[14].bottom_field_order_cnt; - pps->top_field_order_cnt15 = dpb[15].top_field_order_cnt; - pps->bot_field_order_cnt15 = dpb[15].bottom_field_order_cnt; -} - -static noinline_for_stack void set_dec_params(struct rkvdec_pps *pps, const struct v4l2_ctrl_h264_decode_params *dec_params) -{ - const struct v4l2_h264_dpb_entry *dpb = dec_params->dpb; - - for (int i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) { - if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM) - pps->is_longterm |= (1 << i); - pps->ref_field_flags |= - (!!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_FIELD)) << i; - pps->ref_colmv_use_flag |= - (!!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) << i; - pps->ref_topfield_used |= - (!!(dpb[i].fields & V4L2_H264_TOP_FIELD_REF)) << i; - pps->ref_botfield_used |= - (!!(dpb[i].fields & V4L2_H264_BOTTOM_FIELD_REF)) << i; - } - pps->pic_field_flag = - !!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC); - pps->pic_associated_flag = - !!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD); - - pps->cur_top_field = dec_params->top_field_order_cnt; - pps->cur_bot_field = dec_params->bottom_field_order_cnt; -} - static void assemble_hw_pps(struct rkvdec_ctx *ctx, struct rkvdec_h264_run *run) { @@ -202,6 +100,7 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx, struct rkvdec_h264_priv_tbl *priv_tbl = h264_ctx->priv_tbl.cpu; struct rkvdec_sps_pps *hw_ps; u32 pic_width, pic_height; + int i; /* * HW read the SPS/PPS information from PPS packet index by PPS id. @@ -213,23 +112,25 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx, memset(hw_ps, 0, sizeof(*hw_ps)); /* write sps */ - hw_ps->sps.seq_parameter_set_id = sps->seq_parameter_set_id; - hw_ps->sps.profile_idc = sps->profile_idc; - hw_ps->sps.constraint_set3_flag = !!(sps->constraint_set_flags & (1 << 3)); - hw_ps->sps.chroma_format_idc = sps->chroma_format_idc; - hw_ps->sps.bit_depth_luma = sps->bit_depth_luma_minus8; - hw_ps->sps.bit_depth_chroma = sps->bit_depth_chroma_minus8; - hw_ps->sps.qpprime_y_zero_transform_bypass_flag = - !!(sps->flags & V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS); - hw_ps->sps.log2_max_frame_num_minus4 = sps->log2_max_frame_num_minus4; - hw_ps->sps.max_num_ref_frames = sps->max_num_ref_frames; - hw_ps->sps.pic_order_cnt_type = sps->pic_order_cnt_type; - hw_ps->sps.log2_max_pic_order_cnt_lsb_minus4 = - sps->log2_max_pic_order_cnt_lsb_minus4; - hw_ps->sps.delta_pic_order_always_zero_flag = - !!(sps->flags & V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO); - hw_ps->sps.mvc_extension_enable = 0; - hw_ps->sps.num_views = 0; + rkvdec_set_bw_field(hw_ps->info, SEQ_PARAMETER_SET_ID, sps->seq_parameter_set_id); + rkvdec_set_bw_field(hw_ps->info, PROFILE_IDC, sps->profile_idc); + rkvdec_set_bw_field(hw_ps->info, CONSTRAINT_SET3_FLAG, + !!(sps->constraint_set_flags & (1 << 3))); + rkvdec_set_bw_field(hw_ps->info, CHROMA_FORMAT_IDC, sps->chroma_format_idc); + rkvdec_set_bw_field(hw_ps->info, BIT_DEPTH_LUMA, sps->bit_depth_luma_minus8); + rkvdec_set_bw_field(hw_ps->info, BIT_DEPTH_CHROMA, sps->bit_depth_chroma_minus8); + rkvdec_set_bw_field(hw_ps->info, QPPRIME_Y_ZERO_TRANSFORM_BYPASS_FLAG, + !!(sps->flags & V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS)); + rkvdec_set_bw_field(hw_ps->info, LOG2_MAX_FRAME_NUM_MINUS4, + sps->log2_max_frame_num_minus4); + rkvdec_set_bw_field(hw_ps->info, MAX_NUM_REF_FRAMES, sps->max_num_ref_frames); + rkvdec_set_bw_field(hw_ps->info, PIC_ORDER_CNT_TYPE, sps->pic_order_cnt_type); + rkvdec_set_bw_field(hw_ps->info, LOG2_MAX_PIC_ORDER_CNT_LSB_MINUS4, + sps->log2_max_pic_order_cnt_lsb_minus4); + rkvdec_set_bw_field(hw_ps->info, DELTA_PIC_ORDER_ALWAYS_ZERO_FLAG, + !!(sps->flags & V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO)); + rkvdec_set_bw_field(hw_ps->info, MVC_EXTENSION_ENABLE, 0); + rkvdec_set_bw_field(hw_ps->info, NUM_VIEWS, 0); /* * Use the SPS values since they are already in macroblocks @@ -245,48 +146,72 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx, if (!!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC)) pic_height /= 2; - hw_ps->sps.pic_width_in_mbs = pic_width; - hw_ps->sps.pic_height_in_mbs = pic_height; + rkvdec_set_bw_field(hw_ps->info, PIC_WIDTH_IN_MBS, pic_width); + rkvdec_set_bw_field(hw_ps->info, PIC_HEIGHT_IN_MBS, pic_height); - hw_ps->sps.frame_mbs_only_flag = - !!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY); - hw_ps->sps.mb_adaptive_frame_field_flag = - !!(sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD); - hw_ps->sps.direct_8x8_inference_flag = - !!(sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE); + rkvdec_set_bw_field(hw_ps->info, FRAME_MBS_ONLY_FLAG, + !!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY)); + rkvdec_set_bw_field(hw_ps->info, MB_ADAPTIVE_FRAME_FIELD_FLAG, + !!(sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD)); + rkvdec_set_bw_field(hw_ps->info, DIRECT_8X8_INFERENCE_FLAG, + !!(sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE)); /* write pps */ - hw_ps->pps.pic_parameter_set_id = pps->pic_parameter_set_id; - hw_ps->pps.pps_seq_parameter_set_id = pps->seq_parameter_set_id; - hw_ps->pps.entropy_coding_mode_flag = - !!(pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE); - hw_ps->pps.bottom_field_pic_order_in_frame_present_flag = - !!(pps->flags & V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT); - hw_ps->pps.num_ref_idx_l0_default_active_minus1 = - pps->num_ref_idx_l0_default_active_minus1; - hw_ps->pps.num_ref_idx_l1_default_active_minus1 = - pps->num_ref_idx_l1_default_active_minus1; - hw_ps->pps.weighted_pred_flag = - !!(pps->flags & V4L2_H264_PPS_FLAG_WEIGHTED_PRED); - hw_ps->pps.weighted_bipred_idc = pps->weighted_bipred_idc; - hw_ps->pps.pic_init_qp_minus26 = pps->pic_init_qp_minus26; - hw_ps->pps.pic_init_qs_minus26 = pps->pic_init_qs_minus26; - hw_ps->pps.chroma_qp_index_offset = pps->chroma_qp_index_offset; - hw_ps->pps.deblocking_filter_control_present_flag = - !!(pps->flags & V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT); - hw_ps->pps.constrained_intra_pred_flag = - !!(pps->flags & V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED); - hw_ps->pps.redundant_pic_cnt_present = - !!(pps->flags & V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT); - hw_ps->pps.transform_8x8_mode_flag = - !!(pps->flags & V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE); - hw_ps->pps.second_chroma_qp_index_offset = pps->second_chroma_qp_index_offset; - hw_ps->pps.scaling_list_enable_flag = - !!(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT); - - set_field_order_cnt(&hw_ps->pps, dpb); - set_dec_params(&hw_ps->pps, dec_params); + rkvdec_set_bw_field(hw_ps->info, PIC_PARAMETER_SET_ID, pps->pic_parameter_set_id); + rkvdec_set_bw_field(hw_ps->info, PPS_SEQ_PARAMETER_SET_ID, pps->seq_parameter_set_id); + rkvdec_set_bw_field(hw_ps->info, ENTROPY_CODING_MODE_FLAG, + !!(pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE)); + rkvdec_set_bw_field(hw_ps->info, BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT_FLAG, + !!(pps->flags & + V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT)); + rkvdec_set_bw_field(hw_ps->info, NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(0), + pps->num_ref_idx_l0_default_active_minus1); + rkvdec_set_bw_field(hw_ps->info, NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(1), + pps->num_ref_idx_l1_default_active_minus1); + rkvdec_set_bw_field(hw_ps->info, WEIGHTED_PRED_FLAG, + !!(pps->flags & V4L2_H264_PPS_FLAG_WEIGHTED_PRED)); + rkvdec_set_bw_field(hw_ps->info, WEIGHTED_BIPRED_IDC, pps->weighted_bipred_idc); + rkvdec_set_bw_field(hw_ps->info, PIC_INIT_QP_MINUS26, pps->pic_init_qp_minus26); + rkvdec_set_bw_field(hw_ps->info, PIC_INIT_QS_MINUS26, pps->pic_init_qs_minus26); + rkvdec_set_bw_field(hw_ps->info, CHROMA_QP_INDEX_OFFSET, pps->chroma_qp_index_offset); + rkvdec_set_bw_field(hw_ps->info, DEBLOCKING_FILTER_CONTROL_PRESENT_FLAG, + !!(pps->flags & V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT)); + rkvdec_set_bw_field(hw_ps->info, CONSTRAINED_INTRA_PRED_FLAG, + !!(pps->flags & V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED)); + rkvdec_set_bw_field(hw_ps->info, REDUNDANT_PIC_CNT_PRESENT, + !!(pps->flags & V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT)); + rkvdec_set_bw_field(hw_ps->info, TRANSFORM_8X8_MODE_FLAG, + !!(pps->flags & V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE)); + rkvdec_set_bw_field(hw_ps->info, SECOND_CHROMA_QP_INDEX_OFFSET, + pps->second_chroma_qp_index_offset); + rkvdec_set_bw_field(hw_ps->info, SCALING_LIST_ENABLE_FLAG, + !!(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT)); + + for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) { + rkvdec_set_bw_field(hw_ps->info, TOP_FIELD_ORDER_CNT(i), + dpb[i].top_field_order_cnt); + rkvdec_set_bw_field(hw_ps->info, BOT_FIELD_ORDER_CNT(i), + dpb[i].bottom_field_order_cnt); + + rkvdec_set_bw_field(hw_ps->info, IS_LONG_TERM(i), + !!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM)); + rkvdec_set_bw_field(hw_ps->info, REF_FIELD_FLAGS(i), + !!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_FIELD)); + rkvdec_set_bw_field(hw_ps->info, REF_COLMV_USE_FLAG(i), + !!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)); + rkvdec_set_bw_field(hw_ps->info, REF_TOPFIELD_USED(i), + !!(dpb[i].fields & V4L2_H264_TOP_FIELD_REF)); + rkvdec_set_bw_field(hw_ps->info, REF_BOTFIELD_USED(i), + !!(dpb[i].fields & V4L2_H264_BOTTOM_FIELD_REF)); + } + + rkvdec_set_bw_field(hw_ps->info, PIC_FIELD_FLAG, + !!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC)); + rkvdec_set_bw_field(hw_ps->info, PIC_ASSOCIATED_FLAG, + !!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD)); + rkvdec_set_bw_field(hw_ps->info, CUR_TOP_FIELD, dec_params->top_field_order_cnt); + rkvdec_set_bw_field(hw_ps->info, CUR_BOT_FIELD, dec_params->bottom_field_order_cnt); } static void rkvdec_write_regs(struct rkvdec_ctx *ctx) diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c index 96d938ee70b0..3575338a531a 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c @@ -13,149 +13,106 @@ #include "rkvdec-rcb.h" #include "rkvdec-hevc-common.h" #include "rkvdec-vdpu383-regs.h" +#include "rkvdec-bitwriter.h" + +#define VIDEO_PARAMETER_SET_ID BW_FIELD(0, 4) +#define SEQ_PARAMETER_SET_ID BW_FIELD(4, 4) +#define CHROMA_FORMAT_IDC BW_FIELD(8, 2) +#define PIC_WIDTH_IN_LUMA_SAMPLES BW_FIELD(10, 16) +#define PIC_HEIGHT_IN_LUMA_SAMPLES BW_FIELD(26, 16) +#define BIT_DEPTH_LUMA BW_FIELD(42, 3) +#define BIT_DEPTH_CHROMA BW_FIELD(45, 3) +#define LOG2_MAX_PIC_ORDER_CNT_LSB BW_FIELD(48, 5) +#define LOG2_DIFF_MAX_MIN_LUMA_CODING_BLOCK_SIZE BW_FIELD(53, 2) +#define LOG2_MIN_LUMA_CODING_BLOCK_SIZE BW_FIELD(55, 3) +#define LOG2_MIN_TRANSFORM_BLOCK_SIZE BW_FIELD(58, 3) +#define LOG2_DIFF_MAX_MIN_LUMA_TRANSFORM_BLOCK_SIZE BW_FIELD(61, 2) +#define MAX_TRANSFORM_HIERARCHY_DEPTH_INTER BW_FIELD(63, 3) +#define MAX_TRANSFORM_HIERARCHY_DEPTH_INTRA BW_FIELD(66, 3) +#define SCALING_LIST_ENABLED_FLAG BW_FIELD(69, 1) +#define AMP_ENABLED_FLAG BW_FIELD(70, 1) +#define SAMPLE_ADAPTIVE_OFFSET_ENABLED_FLAG BW_FIELD(71, 1) +#define PCM_ENABLED_FLAG BW_FIELD(72, 1) +#define PCM_SAMPLE_BIT_DEPTH_LUMA BW_FIELD(73, 4) +#define PCM_SAMPLE_BIT_DEPTH_CHROMA BW_FIELD(77, 4) +#define PCM_LOOP_FILTER_DISABLED_FLAG BW_FIELD(81, 1) +#define LOG2_DIFF_MAX_MIN_PCM_LUMA_CODING_BLOCK_SIZE BW_FIELD(82, 3) +#define LOG2_MIN_PCM_LUMA_CODING_BLOCK_SIZE BW_FIELD(85, 3) +#define NUM_SHORT_TERM_REF_PIC_SETS BW_FIELD(88, 7) +#define LONG_TERM_REF_PICS_PRESENT_FLAG BW_FIELD(95, 1) +#define NUM_LONG_TERM_REF_PICS_SPS BW_FIELD(96, 6) +#define SPS_TEMPORAL_MVP_ENABLED_FLAG BW_FIELD(102, 1) +#define STRONG_INTRA_SMOOTHING_ENABLED_FLAG BW_FIELD(103, 1) +#define SPS_MAX_DEC_PIC_BUFFERING_MINUS1 BW_FIELD(111, 4) +#define SEPARATE_COLOUR_PLANE_FLAG BW_FIELD(115, 1) +#define HIGH_PRECISION_OFFSETS_ENABLED_FLAG BW_FIELD(116, 1) +#define PERSISTENT_RICE_ADAPTATION_ENABLED_FLAG BW_FIELD(117, 1) + +/* PPS */ +#define PIC_PARAMETER_SET_ID BW_FIELD(118, 6) +#define PPS_SEQ_PARAMETER_SET_ID BW_FIELD(124, 4) +#define DEPENDENT_SLICE_SEGMENTS_ENABLED_FLAG BW_FIELD(128, 1) +#define OUTPUT_FLAG_PRESENT_FLAG BW_FIELD(129, 1) +#define NUM_EXTRA_SLICE_HEADER_BITS BW_FIELD(130, 13) +#define SIGN_DATA_HIDING_ENABLED_FLAG BW_FIELD(143, 1) +#define CABAC_INIT_PRESENT_FLAG BW_FIELD(144, 1) +#define NUM_REF_IDX_L0_DEFAULT_ACTIVE BW_FIELD(145, 4) +#define NUM_REF_IDX_L1_DEFAULT_ACTIVE BW_FIELD(149, 4) +#define INIT_QP_MINUS26 BW_FIELD(153, 7) +#define CONSTRAINED_INTRA_PRED_FLAG BW_FIELD(160, 1) +#define TRANSFORM_SKIP_ENABLED_FLAG BW_FIELD(161, 1) +#define CU_QP_DELTA_ENABLED_FLAG BW_FIELD(162, 1) +#define LOG2_MIN_CU_QP_DELTA_SIZE BW_FIELD(163, 3) +#define PPS_CB_QP_OFFSET BW_FIELD(166, 5) +#define PPS_CR_QP_OFFSET BW_FIELD(171, 5) +#define PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT_FLAG BW_FIELD(176, 1) +#define WEIGHTED_PRED_FLAG BW_FIELD(177, 1) +#define WEIGHTED_BIPRED_FLAG BW_FIELD(178, 1) +#define TRANSQUANT_BYPASS_ENABLED_FLAG BW_FIELD(179, 1) +#define TILES_ENABLED_FLAG BW_FIELD(180, 1) +#define ENTROPY_CODING_SYNC_ENABLED_FLAG BW_FIELD(181, 1) +#define PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG BW_FIELD(182, 1) +#define LOOP_FILTER_ACROSS_TILES_ENABLED_FLAG BW_FIELD(183, 1) +#define DEBLOCKING_FILTER_OVERRIDE_ENABLED_FLAG BW_FIELD(184, 1) +#define PPS_DEBLOCKING_FILTER_DISABLED_FLAG BW_FIELD(185, 1) +#define PPS_BETA_OFFSET_DIV2 BW_FIELD(186, 4) +#define PPS_TC_OFFSET_DIV2 BW_FIELD(190, 4) +#define LISTS_MODIFICATION_PRESENT_FLAG BW_FIELD(194, 1) +#define LOG2_PARALLEL_MERGE_LEVEL BW_FIELD(195, 3) +#define SLICE_SEGMENT_HEADER_EXTENSION_PRESENT_FLAG BW_FIELD(198, 1) + +/* pps extensions */ +#define LOG2_MAX_TRANSFORM_SKIP_BLOCK_SIZE BW_FIELD(202, 2) +#define CROSS_COMPONENT_PREDICTION_ENABLED_FLAG BW_FIELD(204, 1) +#define CHROMA_QP_OFFSET_LIST_ENABLED_FLAG BW_FIELD(205, 1) +#define LOG2_MIN_CU_CHROMA_QP_DELTA_SIZE BW_FIELD(206, 3) +#define CB_QP_OFFSET_LIST(i) BW_FIELD(209 + (i) * 5, 5) // i: 0-5 +#define CB_CR_OFFSET_LIST(i) BW_FIELD(239 + (i) * 5, 5) // i: 0-5 +#define CHROMA_QP_OFFSET_LIST_LEN_MINUS1 BW_FIELD(269, 3) + +/* mvc0 && mvc1 */ +#define MVC_FF BW_FIELD(272, 16) +#define MVC_00 BW_FIELD(288, 9) + +/* poc info */ +#define RESERVED2 BW_FIELD(297, 3) +#define CURRENT_POC BW_FIELD(300, 32) +#define REF_PIC_POC(i) BW_FIELD(332 + (i) * 32, 32) // i: 0-14 +#define RESERVED3 BW_FIELD(812, 32) +#define REF_IS_VALID(i) BW_FIELD(844 + (i), 1) // i: 0-14 +#define RESERVED4 BW_FIELD(859, 1) + +/* tile info*/ +#define NUM_TILE_COLUMNS BW_FIELD(860, 5) +#define NUM_TILE_ROWS BW_FIELD(865, 5) +#define COLUMN_WIDTH(i) BW_FIELD(870 + (i) * 12, 12) // i: 0-19 +#define ROW_HEIGHT(i) BW_FIELD(1110 + (i) * 12, 12) // i: 0-21 + +#define HEVC_SPS_SIZE ALIGN(1110 + 22 * 12, 256) struct rkvdec_hevc_sps_pps { - // SPS - u16 video_parameters_set_id : 4; - u16 seq_parameters_set_id_sps : 4; - u16 chroma_format_idc : 2; - u16 width : 16; - u16 height : 16; - u16 bit_depth_luma : 3; - u16 bit_depth_chroma : 3; - u16 max_pic_order_count_lsb : 5; - u16 diff_max_min_luma_coding_block_size : 2; - u16 min_luma_coding_block_size : 3; - u16 min_transform_block_size : 3; - u16 diff_max_min_transform_block_size : 2; - u16 max_transform_hierarchy_depth_inter : 3; - u16 max_transform_hierarchy_depth_intra : 3; - u16 scaling_list_enabled_flag : 1; - u16 amp_enabled_flag : 1; - u16 sample_adaptive_offset_enabled_flag : 1; - u16 pcm_enabled_flag : 1; - u16 pcm_sample_bit_depth_luma : 4; - u16 pcm_sample_bit_depth_chroma : 4; - u16 pcm_loop_filter_disabled_flag : 1; - u16 diff_max_min_pcm_luma_coding_block_size : 3; - u16 min_pcm_luma_coding_block_size : 3; - u16 num_short_term_ref_pic_sets : 7; - u16 long_term_ref_pics_present_flag : 1; - u16 num_long_term_ref_pics_sps : 6; - u16 sps_temporal_mvp_enabled_flag : 1; - u16 strong_intra_smoothing_enabled_flag : 1; - u16 reserved0 : 7; - u16 sps_max_dec_pic_buffering_minus1 : 4; - u16 separate_colour_plane_flag : 1; - u16 high_precision_offsets_enabled_flag : 1; - u16 persistent_rice_adaptation_enabled_flag : 1; - - // PPS - u16 picture_parameters_set_id : 6; - u16 seq_parameters_set_id_pps : 4; - u16 dependent_slice_segments_enabled_flag : 1; - u16 output_flag_present_flag : 1; - u16 num_extra_slice_header_bits : 13; - u16 sign_data_hiding_enabled_flag : 1; - u16 cabac_init_present_flag : 1; - u16 num_ref_idx_l0_default_active : 4; - u16 num_ref_idx_l1_default_active : 4; - u16 init_qp_minus26 : 7; - u16 constrained_intra_pred_flag : 1; - u16 transform_skip_enabled_flag : 1; - u16 cu_qp_delta_enabled_flag : 1; - u16 log2_min_cb_size : 3; - u16 pps_cb_qp_offset : 5; - u16 pps_cr_qp_offset : 5; - u16 pps_slice_chroma_qp_offsets_present_flag : 1; - u16 weighted_pred_flag : 1; - u16 weighted_bipred_flag : 1; - u16 transquant_bypass_enabled_flag : 1; - u16 tiles_enabled_flag : 1; - u16 entropy_coding_sync_enabled_flag : 1; - u16 pps_loop_filter_across_slices_enabled_flag : 1; - u16 loop_filter_across_tiles_enabled_flag : 1; - u16 deblocking_filter_override_enabled_flag : 1; - u16 pps_deblocking_filter_disabled_flag : 1; - u16 pps_beta_offset_div2 : 4; - u16 pps_tc_offset_div2 : 4; - u16 lists_modification_present_flag : 1; - u16 log2_parallel_merge_level : 3; - u16 slice_segment_header_extension_present_flag : 1; - u16 reserved1 : 3; - - // pps extensions - u16 log2_max_transform_skip_block_size : 2; - u16 cross_component_prediction_enabled_flag : 1; - u16 chroma_qp_offset_list_enabled_flag : 1; - u16 log2_min_cu_chroma_qp_delta_size : 3; - u16 cb_qp_offset_list0 : 5; - u16 cb_qp_offset_list1 : 5; - u16 cb_qp_offset_list2 : 5; - u16 cb_qp_offset_list3 : 5; - u16 cb_qp_offset_list4 : 5; - u16 cb_qp_offset_list5 : 5; - u16 cb_cr_offset_list0 : 5; - u16 cb_cr_offset_list1 : 5; - u16 cb_cr_offset_list2 : 5; - u16 cb_cr_offset_list3 : 5; - u16 cb_cr_offset_list4 : 5; - u16 cb_cr_offset_list5 : 5; - u16 chroma_qp_offset_list_len_minus1 : 3; - - /* mvc0 && mvc1 */ - u16 mvc_ff : 16; - u16 mvc_00 : 9; - - /* poc info */ - u16 reserved2 : 3; - u32 current_poc : 32; - u32 ref_pic_poc0 : 32; - u32 ref_pic_poc1 : 32; - u32 ref_pic_poc2 : 32; - u32 ref_pic_poc3 : 32; - u32 ref_pic_poc4 : 32; - u32 ref_pic_poc5 : 32; - u32 ref_pic_poc6 : 32; - u32 ref_pic_poc7 : 32; - u32 ref_pic_poc8 : 32; - u32 ref_pic_poc9 : 32; - u32 ref_pic_poc10 : 32; - u32 ref_pic_poc11 : 32; - u32 ref_pic_poc12 : 32; - u32 ref_pic_poc13 : 32; - u32 ref_pic_poc14 : 32; - u32 reserved3 : 32; - u32 ref_is_valid : 15; - u32 reserved4 : 1; - - /* tile info*/ - u16 num_tile_columns : 5; - u16 num_tile_rows : 5; - u32 column_width0 : 24; - u32 column_width1 : 24; - u32 column_width2 : 24; - u32 column_width3 : 24; - u32 column_width4 : 24; - u32 column_width5 : 24; - u32 column_width6 : 24; - u32 column_width7 : 24; - u32 column_width8 : 24; - u32 column_width9 : 24; - u32 row_height0 : 24; - u32 row_height1 : 24; - u32 row_height2 : 24; - u32 row_height3 : 24; - u32 row_height4 : 24; - u32 row_height5 : 24; - u32 row_height6 : 24; - u32 row_height7 : 24; - u32 row_height8 : 24; - u32 row_height9 : 24; - u32 row_height10 : 24; - u32 reserved5 : 2; - u32 padding; -} __packed; + u32 info[HEVC_SPS_SIZE / 8 / 4]; +}; struct rkvdec_hevc_priv_tbl { struct rkvdec_hevc_sps_pps param_set; @@ -171,51 +128,6 @@ struct rkvdec_hevc_ctx { struct vdpu383_regs_h26x regs; }; -static void set_column_row(struct rkvdec_hevc_sps_pps *hw_ps, u16 *column, u16 *row) -{ - hw_ps->column_width0 = column[0] | (column[1] << 12); - hw_ps->row_height0 = row[0] | (row[1] << 12); - hw_ps->column_width1 = column[2] | (column[3] << 12); - hw_ps->row_height1 = row[2] | (row[3] << 12); - hw_ps->column_width2 = column[4] | (column[5] << 12); - hw_ps->row_height2 = row[4] | (row[5] << 12); - hw_ps->column_width3 = column[6] | (column[7] << 12); - hw_ps->row_height3 = row[6] | (row[7] << 12); - hw_ps->column_width4 = column[8] | (column[9] << 12); - hw_ps->row_height4 = row[8] | (row[9] << 12); - hw_ps->column_width5 = column[10] | (column[11] << 12); - hw_ps->row_height5 = row[10] | (row[11] << 12); - hw_ps->column_width6 = column[12] | (column[13] << 12); - hw_ps->row_height6 = row[12] | (row[13] << 12); - hw_ps->column_width7 = column[14] | (column[15] << 12); - hw_ps->row_height7 = row[14] | (row[15] << 12); - hw_ps->column_width8 = column[16] | (column[17] << 12); - hw_ps->row_height8 = row[16] | (row[17] << 12); - hw_ps->column_width9 = column[18] | (column[19] << 12); - hw_ps->row_height9 = row[18] | (row[19] << 12); - - hw_ps->row_height10 = row[20] | (row[21] << 12); -} - -static void set_pps_ref_pic_poc(struct rkvdec_hevc_sps_pps *hw_ps, const struct v4l2_hevc_dpb_entry *dpb) -{ - hw_ps->ref_pic_poc0 = dpb[0].pic_order_cnt_val; - hw_ps->ref_pic_poc1 = dpb[1].pic_order_cnt_val; - hw_ps->ref_pic_poc2 = dpb[2].pic_order_cnt_val; - hw_ps->ref_pic_poc3 = dpb[3].pic_order_cnt_val; - hw_ps->ref_pic_poc4 = dpb[4].pic_order_cnt_val; - hw_ps->ref_pic_poc5 = dpb[5].pic_order_cnt_val; - hw_ps->ref_pic_poc6 = dpb[6].pic_order_cnt_val; - hw_ps->ref_pic_poc7 = dpb[7].pic_order_cnt_val; - hw_ps->ref_pic_poc8 = dpb[8].pic_order_cnt_val; - hw_ps->ref_pic_poc9 = dpb[9].pic_order_cnt_val; - hw_ps->ref_pic_poc10 = dpb[10].pic_order_cnt_val; - hw_ps->ref_pic_poc11 = dpb[11].pic_order_cnt_val; - hw_ps->ref_pic_poc12 = dpb[12].pic_order_cnt_val; - hw_ps->ref_pic_poc13 = dpb[13].pic_order_cnt_val; - hw_ps->ref_pic_poc14 = dpb[14].pic_order_cnt_val; -} - static void assemble_hw_pps(struct rkvdec_ctx *ctx, struct rkvdec_hevc_run *run) { @@ -245,104 +157,130 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx, memset(hw_ps, 0, sizeof(*hw_ps)); /* write sps */ - hw_ps->video_parameters_set_id = sps->video_parameter_set_id; - hw_ps->seq_parameters_set_id_sps = sps->seq_parameter_set_id; - hw_ps->chroma_format_idc = sps->chroma_format_idc; + rkvdec_set_bw_field(hw_ps->info, VIDEO_PARAMETER_SET_ID, sps->video_parameter_set_id); + rkvdec_set_bw_field(hw_ps->info, SEQ_PARAMETER_SET_ID, sps->seq_parameter_set_id); + rkvdec_set_bw_field(hw_ps->info, CHROMA_FORMAT_IDC, sps->chroma_format_idc); log2_min_cb_size = sps->log2_min_luma_coding_block_size_minus3 + 3; width = sps->pic_width_in_luma_samples; height = sps->pic_height_in_luma_samples; - hw_ps->width = width; - hw_ps->height = height; - hw_ps->bit_depth_luma = sps->bit_depth_luma_minus8 + 8; - hw_ps->bit_depth_chroma = sps->bit_depth_chroma_minus8 + 8; - hw_ps->max_pic_order_count_lsb = sps->log2_max_pic_order_cnt_lsb_minus4 + 4; - hw_ps->diff_max_min_luma_coding_block_size = sps->log2_diff_max_min_luma_coding_block_size; - hw_ps->min_luma_coding_block_size = sps->log2_min_luma_coding_block_size_minus3 + 3; - hw_ps->min_transform_block_size = sps->log2_min_luma_transform_block_size_minus2 + 2; - hw_ps->diff_max_min_transform_block_size = - sps->log2_diff_max_min_luma_transform_block_size; - hw_ps->max_transform_hierarchy_depth_inter = sps->max_transform_hierarchy_depth_inter; - hw_ps->max_transform_hierarchy_depth_intra = sps->max_transform_hierarchy_depth_intra; - hw_ps->scaling_list_enabled_flag = - !!(sps->flags & V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED); - hw_ps->amp_enabled_flag = !!(sps->flags & V4L2_HEVC_SPS_FLAG_AMP_ENABLED); - hw_ps->sample_adaptive_offset_enabled_flag = - !!(sps->flags & V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET); + + rkvdec_set_bw_field(hw_ps->info, PIC_WIDTH_IN_LUMA_SAMPLES, width); + rkvdec_set_bw_field(hw_ps->info, PIC_HEIGHT_IN_LUMA_SAMPLES, height); + rkvdec_set_bw_field(hw_ps->info, BIT_DEPTH_LUMA, sps->bit_depth_luma_minus8 + 8); + rkvdec_set_bw_field(hw_ps->info, BIT_DEPTH_CHROMA, sps->bit_depth_chroma_minus8 + 8); + rkvdec_set_bw_field(hw_ps->info, LOG2_MAX_PIC_ORDER_CNT_LSB, + sps->log2_max_pic_order_cnt_lsb_minus4 + 4); + rkvdec_set_bw_field(hw_ps->info, LOG2_DIFF_MAX_MIN_LUMA_CODING_BLOCK_SIZE, + sps->log2_diff_max_min_luma_coding_block_size); + rkvdec_set_bw_field(hw_ps->info, LOG2_MIN_LUMA_CODING_BLOCK_SIZE, + sps->log2_min_luma_coding_block_size_minus3 + 3); + rkvdec_set_bw_field(hw_ps->info, LOG2_MIN_TRANSFORM_BLOCK_SIZE, + sps->log2_min_luma_transform_block_size_minus2 + 2); + rkvdec_set_bw_field(hw_ps->info, LOG2_DIFF_MAX_MIN_LUMA_TRANSFORM_BLOCK_SIZE, + sps->log2_diff_max_min_luma_transform_block_size); + rkvdec_set_bw_field(hw_ps->info, MAX_TRANSFORM_HIERARCHY_DEPTH_INTER, + sps->max_transform_hierarchy_depth_inter); + rkvdec_set_bw_field(hw_ps->info, MAX_TRANSFORM_HIERARCHY_DEPTH_INTRA, + sps->max_transform_hierarchy_depth_intra); + rkvdec_set_bw_field(hw_ps->info, SCALING_LIST_ENABLED_FLAG, + !!(sps->flags & V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, AMP_ENABLED_FLAG, + !!(sps->flags & V4L2_HEVC_SPS_FLAG_AMP_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, SAMPLE_ADAPTIVE_OFFSET_ENABLED_FLAG, + !!(sps->flags & V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET)); pcm_enabled = !!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_ENABLED); - hw_ps->pcm_enabled_flag = pcm_enabled; - hw_ps->pcm_sample_bit_depth_luma = - pcm_enabled ? sps->pcm_sample_bit_depth_luma_minus1 + 1 : 0; - hw_ps->pcm_sample_bit_depth_chroma = - pcm_enabled ? sps->pcm_sample_bit_depth_chroma_minus1 + 1 : 0; - hw_ps->pcm_loop_filter_disabled_flag = - !!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED); - hw_ps->diff_max_min_pcm_luma_coding_block_size = - sps->log2_diff_max_min_pcm_luma_coding_block_size; - hw_ps->min_pcm_luma_coding_block_size = - pcm_enabled ? sps->log2_min_pcm_luma_coding_block_size_minus3 + 3 : 0; - hw_ps->num_short_term_ref_pic_sets = sps->num_short_term_ref_pic_sets; - hw_ps->long_term_ref_pics_present_flag = - !!(sps->flags & V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT); - hw_ps->num_long_term_ref_pics_sps = sps->num_long_term_ref_pics_sps; - hw_ps->sps_temporal_mvp_enabled_flag = - !!(sps->flags & V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED); - hw_ps->strong_intra_smoothing_enabled_flag = - !!(sps->flags & V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED); - hw_ps->sps_max_dec_pic_buffering_minus1 = sps->sps_max_dec_pic_buffering_minus1; + rkvdec_set_bw_field(hw_ps->info, PCM_ENABLED_FLAG, pcm_enabled); + rkvdec_set_bw_field(hw_ps->info, PCM_SAMPLE_BIT_DEPTH_LUMA, + pcm_enabled ? sps->pcm_sample_bit_depth_luma_minus1 + 1 : 0); + rkvdec_set_bw_field(hw_ps->info, PCM_SAMPLE_BIT_DEPTH_CHROMA, + pcm_enabled ? sps->pcm_sample_bit_depth_chroma_minus1 + 1 : 0); + rkvdec_set_bw_field(hw_ps->info, PCM_LOOP_FILTER_DISABLED_FLAG, + !!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED)); + rkvdec_set_bw_field(hw_ps->info, LOG2_DIFF_MAX_MIN_PCM_LUMA_CODING_BLOCK_SIZE, + sps->log2_diff_max_min_pcm_luma_coding_block_size); + rkvdec_set_bw_field(hw_ps->info, LOG2_MIN_PCM_LUMA_CODING_BLOCK_SIZE, + pcm_enabled ? sps->log2_min_pcm_luma_coding_block_size_minus3 + 3 : 0); + rkvdec_set_bw_field(hw_ps->info, NUM_SHORT_TERM_REF_PIC_SETS, + sps->num_short_term_ref_pic_sets); + rkvdec_set_bw_field(hw_ps->info, LONG_TERM_REF_PICS_PRESENT_FLAG, + !!(sps->flags & V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT)); + rkvdec_set_bw_field(hw_ps->info, NUM_LONG_TERM_REF_PICS_SPS, + sps->num_long_term_ref_pics_sps); + rkvdec_set_bw_field(hw_ps->info, SPS_TEMPORAL_MVP_ENABLED_FLAG, + !!(sps->flags & V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, STRONG_INTRA_SMOOTHING_ENABLED_FLAG, + !!(sps->flags & V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, SPS_MAX_DEC_PIC_BUFFERING_MINUS1, + sps->sps_max_dec_pic_buffering_minus1); /* write pps */ - hw_ps->picture_parameters_set_id = pps->pic_parameter_set_id; - hw_ps->seq_parameters_set_id_pps = sps->seq_parameter_set_id; - hw_ps->dependent_slice_segments_enabled_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT_ENABLED); - hw_ps->output_flag_present_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_OUTPUT_FLAG_PRESENT); - hw_ps->num_extra_slice_header_bits = pps->num_extra_slice_header_bits; - hw_ps->sign_data_hiding_enabled_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED); - hw_ps->cabac_init_present_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT); - hw_ps->num_ref_idx_l0_default_active = pps->num_ref_idx_l0_default_active_minus1 + 1; - hw_ps->num_ref_idx_l1_default_active = pps->num_ref_idx_l1_default_active_minus1 + 1; - hw_ps->init_qp_minus26 = pps->init_qp_minus26; - hw_ps->constrained_intra_pred_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED); - hw_ps->transform_skip_enabled_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED); - hw_ps->cu_qp_delta_enabled_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED); - hw_ps->log2_min_cb_size = log2_min_cb_size + - sps->log2_diff_max_min_luma_coding_block_size - - pps->diff_cu_qp_delta_depth; - hw_ps->pps_cb_qp_offset = pps->pps_cb_qp_offset; - hw_ps->pps_cr_qp_offset = pps->pps_cr_qp_offset; - hw_ps->pps_slice_chroma_qp_offsets_present_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT); - hw_ps->weighted_pred_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED); - hw_ps->weighted_bipred_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED); - hw_ps->transquant_bypass_enabled_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED); + rkvdec_set_bw_field(hw_ps->info, PIC_PARAMETER_SET_ID, pps->pic_parameter_set_id); + rkvdec_set_bw_field(hw_ps->info, SEQ_PARAMETER_SET_ID, sps->seq_parameter_set_id); + rkvdec_set_bw_field(hw_ps->info, DEPENDENT_SLICE_SEGMENTS_ENABLED_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, OUTPUT_FLAG_PRESENT_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_OUTPUT_FLAG_PRESENT)); + rkvdec_set_bw_field(hw_ps->info, NUM_EXTRA_SLICE_HEADER_BITS, + pps->num_extra_slice_header_bits); + rkvdec_set_bw_field(hw_ps->info, SIGN_DATA_HIDING_ENABLED_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, CABAC_INIT_PRESENT_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT)); + rkvdec_set_bw_field(hw_ps->info, NUM_REF_IDX_L0_DEFAULT_ACTIVE, + pps->num_ref_idx_l0_default_active_minus1 + 1); + rkvdec_set_bw_field(hw_ps->info, NUM_REF_IDX_L1_DEFAULT_ACTIVE, + pps->num_ref_idx_l1_default_active_minus1 + 1); + rkvdec_set_bw_field(hw_ps->info, INIT_QP_MINUS26, pps->init_qp_minus26); + rkvdec_set_bw_field(hw_ps->info, CONSTRAINED_INTRA_PRED_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED)); + rkvdec_set_bw_field(hw_ps->info, TRANSFORM_SKIP_ENABLED_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, CU_QP_DELTA_ENABLED_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, LOG2_MIN_CU_QP_DELTA_SIZE, log2_min_cb_size + + sps->log2_diff_max_min_luma_coding_block_size - + pps->diff_cu_qp_delta_depth); + rkvdec_set_bw_field(hw_ps->info, PPS_CB_QP_OFFSET, pps->pps_cb_qp_offset); + rkvdec_set_bw_field(hw_ps->info, PPS_CR_QP_OFFSET, pps->pps_cr_qp_offset); + rkvdec_set_bw_field(hw_ps->info, PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT_FLAG, + !!(pps->flags & + V4L2_HEVC_PPS_FLAG_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT)); + rkvdec_set_bw_field(hw_ps->info, WEIGHTED_PRED_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED)); + rkvdec_set_bw_field(hw_ps->info, WEIGHTED_BIPRED_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED)); + rkvdec_set_bw_field(hw_ps->info, TRANSQUANT_BYPASS_ENABLED_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED)); tiles_enabled = !!(pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED); - hw_ps->tiles_enabled_flag = tiles_enabled; - hw_ps->entropy_coding_sync_enabled_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED); - hw_ps->pps_loop_filter_across_slices_enabled_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED); - hw_ps->loop_filter_across_tiles_enabled_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED); - hw_ps->deblocking_filter_override_enabled_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_OVERRIDE_ENABLED); - hw_ps->pps_deblocking_filter_disabled_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER); - hw_ps->pps_beta_offset_div2 = pps->pps_beta_offset_div2; - hw_ps->pps_tc_offset_div2 = pps->pps_tc_offset_div2; - hw_ps->lists_modification_present_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_LISTS_MODIFICATION_PRESENT); - hw_ps->log2_parallel_merge_level = pps->log2_parallel_merge_level_minus2 + 2; - hw_ps->slice_segment_header_extension_present_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_SLICE_SEGMENT_HEADER_EXTENSION_PRESENT); - hw_ps->num_tile_columns = tiles_enabled ? pps->num_tile_columns_minus1 + 1 : 1; - hw_ps->num_tile_rows = tiles_enabled ? pps->num_tile_rows_minus1 + 1 : 1; - hw_ps->mvc_ff = 0xffff; + rkvdec_set_bw_field(hw_ps->info, TILES_ENABLED_FLAG, tiles_enabled); + rkvdec_set_bw_field(hw_ps->info, ENTROPY_CODING_SYNC_ENABLED_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG, + !!(pps->flags & + V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, LOOP_FILTER_ACROSS_TILES_ENABLED_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, DEBLOCKING_FILTER_OVERRIDE_ENABLED_FLAG, + !!(pps->flags & + V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_OVERRIDE_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, PPS_DEBLOCKING_FILTER_DISABLED_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER)); + rkvdec_set_bw_field(hw_ps->info, PPS_BETA_OFFSET_DIV2, pps->pps_beta_offset_div2); + rkvdec_set_bw_field(hw_ps->info, PPS_TC_OFFSET_DIV2, pps->pps_tc_offset_div2); + rkvdec_set_bw_field(hw_ps->info, LISTS_MODIFICATION_PRESENT_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_LISTS_MODIFICATION_PRESENT)); + rkvdec_set_bw_field(hw_ps->info, LOG2_PARALLEL_MERGE_LEVEL, + pps->log2_parallel_merge_level_minus2 + 2); + rkvdec_set_bw_field(hw_ps->info, SLICE_SEGMENT_HEADER_EXTENSION_PRESENT_FLAG, + !!(pps->flags & + V4L2_HEVC_PPS_FLAG_SLICE_SEGMENT_HEADER_EXTENSION_PRESENT)); + rkvdec_set_bw_field(hw_ps->info, NUM_TILE_COLUMNS, + tiles_enabled ? pps->num_tile_columns_minus1 + 1 : 1); + rkvdec_set_bw_field(hw_ps->info, NUM_TILE_ROWS, + tiles_enabled ? pps->num_tile_rows_minus1 + 1 : 1); + rkvdec_set_bw_field(hw_ps->info, MVC_FF, 0xffff); // Setup tiles information memset(column_width, 0, sizeof(column_width)); @@ -367,15 +305,19 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx, row_height[0] = (height + max_cu_width - 1) / max_cu_width; } - set_column_row(hw_ps, column_width, row_height); + for (i = 0; i < 20; i++) + rkvdec_set_bw_field(hw_ps->info, COLUMN_WIDTH(i), column_width[i]); + for (i = 0; i < 22; i++) + rkvdec_set_bw_field(hw_ps->info, ROW_HEIGHT(i), row_height[i]); // Setup POC information - hw_ps->current_poc = dec_params->pic_order_cnt_val; + rkvdec_set_bw_field(hw_ps->info, CURRENT_POC, dec_params->pic_order_cnt_val); - set_pps_ref_pic_poc(hw_ps, dec_params->dpb); for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) { - u32 valid = !!(dec_params->num_active_dpb_entries > i); - hw_ps->ref_is_valid |= valid << i; + rkvdec_set_bw_field(hw_ps->info, REF_IS_VALID(i), + !!(dec_params->num_active_dpb_entries > i)); + rkvdec_set_bw_field(hw_ps->info, REF_PIC_POC(i), + dec_params->dpb[i].pic_order_cnt_val); } } diff --git a/drivers/media/platform/st/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c index e5663fbe6422..eeb0199864dd 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmi.c +++ b/drivers/media/platform/st/stm32/stm32-dcmi.c @@ -2195,6 +2195,7 @@ static int dcmi_probe(struct platform_device *pdev) return 0; err_cleanup: + v4l2_async_nf_unregister(&dcmi->notifier); v4l2_async_nf_cleanup(&dcmi->notifier); err_media_entity_cleanup: media_entity_cleanup(&dcmi->vdev->entity); diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c index a42f43d19f9e..f0e809458489 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c @@ -401,8 +401,10 @@ static int dcmipp_bytecap_start_streaming(struct vb2_queue *vq, */ if (!vcap->s_subdev) { pad = media_pad_remote_pad_first(&vcap->vdev.entity.pads[0]); - if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) - return -EINVAL; + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) { + ret = -EINVAL; + goto err_buffer_done; + } vcap->s_subdev = media_entity_to_v4l2_subdev(pad->entity); vcap->s_subdev_pad_nb = pad->index; } diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c index e911c7f7acc5..4781db21c205 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c @@ -234,8 +234,10 @@ static int sun4i_csi_start_streaming(struct vb2_queue *vq, unsigned int count) int ret; csi_fmt = sun4i_csi_find_format(&csi->fmt.pixelformat, NULL); - if (!csi_fmt) - return -EINVAL; + if (!csi_fmt) { + ret = -EINVAL; + goto err_clear_dma_queue; + } dev_dbg(csi->dev, "Starting capture\n"); diff --git a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c index 02eb4a6cafad..41e48365167e 100644 --- a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c +++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c @@ -11,6 +11,7 @@ #include <linux/clk.h> #include <linux/delay.h> #include <linux/io.h> +#include <linux/iopoll.h> #include <linux/module.h> #include <linux/of.h> #include <linux/phy/phy.h> @@ -35,6 +36,8 @@ #define DW_REG_EXIST BIT(31) #define DW_REG(x) (DW_REG_EXIST | (x)) +#define DPHY_STOPSTATE_CLK_LANE BIT(16) + #define DPHY_TEST_CTRL0_TEST_CLR BIT(0) #define IPI_VCID_VC(x) FIELD_PREP(GENMASK(1, 0), (x)) @@ -65,6 +68,7 @@ enum dw_mipi_csi2rx_regs_index { DW_MIPI_CSI2RX_PHY_TST_CTRL0, DW_MIPI_CSI2RX_PHY_TST_CTRL1, DW_MIPI_CSI2RX_PHY_SHUTDOWNZ, + DW_MIPI_CSI2RX_PHY_STOPSTATE, DW_MIPI_CSI2RX_IPI_DATATYPE, DW_MIPI_CSI2RX_IPI_MEM_FLUSH, DW_MIPI_CSI2RX_IPI_MODE, @@ -87,6 +91,7 @@ struct dw_mipi_csi2rx_drvdata { void (*dphy_assert_reset)(struct dw_mipi_csi2rx_device *csi2); void (*dphy_deassert_reset)(struct dw_mipi_csi2rx_device *csi2); void (*ipi_enable)(struct dw_mipi_csi2rx_device *csi2); + int (*wait_for_phy_stopstate)(struct dw_mipi_csi2rx_device *csi2); }; struct dw_mipi_csi2rx_format { @@ -113,6 +118,7 @@ struct dw_mipi_csi2rx_device { enum v4l2_mbus_type bus_type; u32 lanes_num; + u64 enabled_streams; const struct dw_mipi_csi2rx_drvdata *drvdata; }; @@ -138,6 +144,7 @@ static const u32 imx93_regs[DW_MIPI_CSI2RX_MAX] = { [DW_MIPI_CSI2RX_PHY_SHUTDOWNZ] = DW_REG(0x40), [DW_MIPI_CSI2RX_DPHY_RSTZ] = DW_REG(0x44), [DW_MIPI_CSI2RX_PHY_STATE] = DW_REG(0x48), + [DW_MIPI_CSI2RX_PHY_STOPSTATE] = DW_REG(0x4c), [DW_MIPI_CSI2RX_PHY_TST_CTRL0] = DW_REG(0x50), [DW_MIPI_CSI2RX_PHY_TST_CTRL1] = DW_REG(0x54), [DW_MIPI_CSI2RX_IPI_MODE] = DW_REG(0x80), @@ -147,6 +154,17 @@ static const u32 imx93_regs[DW_MIPI_CSI2RX_MAX] = { [DW_MIPI_CSI2RX_IPI_SOFTRSTN] = DW_REG(0xa0), }; +static const u32 imx95_regs[DW_MIPI_CSI2RX_MAX] = { + [DW_MIPI_CSI2RX_N_LANES] = DW_REG(0x4), + [DW_MIPI_CSI2RX_RESETN] = DW_REG(0x8), + [DW_MIPI_CSI2RX_PHY_SHUTDOWNZ] = DW_REG(0x40), + [DW_MIPI_CSI2RX_DPHY_RSTZ] = DW_REG(0x44), + [DW_MIPI_CSI2RX_PHY_STATE] = DW_REG(0x48), + [DW_MIPI_CSI2RX_PHY_STOPSTATE] = DW_REG(0x4c), + [DW_MIPI_CSI2RX_PHY_TST_CTRL0] = DW_REG(0x50), + [DW_MIPI_CSI2RX_PHY_TST_CTRL1] = DW_REG(0x54), +}; + static const struct v4l2_mbus_framefmt default_format = { .width = 3840, .height = 2160, @@ -252,6 +270,26 @@ static const struct dw_mipi_csi2rx_format formats[] = { .depth = 12, .csi_dt = MIPI_CSI2_DT_RAW12, }, + { + .code = MEDIA_BUS_FMT_SBGGR16_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_RAW16, + }, + { + .code = MEDIA_BUS_FMT_SGBRG16_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_RAW16, + }, + { + .code = MEDIA_BUS_FMT_SGRBG16_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_RAW16, + }, + { + .code = MEDIA_BUS_FMT_SRGGB16_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_RAW16, + }, }; static inline struct dw_mipi_csi2rx_device *to_csi2(struct v4l2_subdev *sd) @@ -311,7 +349,7 @@ dw_mipi_csi2rx_find_format(struct dw_mipi_csi2rx_device *csi2, u32 mbus_code) WARN_ON(csi2->formats_num == 0); for (unsigned int i = 0; i < csi2->formats_num; i++) { - const struct dw_mipi_csi2rx_format *format = &csi2->formats[i]; + const struct dw_mipi_csi2rx_format *format = &formats[i]; if (format->code == mbus_code) return format; @@ -433,7 +471,7 @@ dw_mipi_csi2rx_enum_mbus_code(struct v4l2_subdev *sd, if (code->index >= csi2->formats_num) return -EINVAL; - code->code = csi2->formats[code->index].code; + code->code = formats[code->index].code; return 0; default: return -EINVAL; @@ -470,6 +508,17 @@ static int dw_mipi_csi2rx_set_fmt(struct v4l2_subdev *sd, *src = *sink; + /* Store the CSIS format descriptor for active formats. */ + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + csi2->formats = fmt ? : + dw_mipi_csi2rx_find_format(csi2, default_format.code); + + if (!csi2->formats) { + dev_err(csi2->dev, "Failed to find valid format\n"); + return -EINVAL; + } + } + return 0; } @@ -508,26 +557,42 @@ static int dw_mipi_csi2rx_enable_streams(struct v4l2_subdev *sd, DW_MIPI_CSI2RX_PAD_SRC, &streams_mask); - ret = pm_runtime_resume_and_get(dev); - if (ret) - goto err; + if (!csi2->enabled_streams) { + ret = pm_runtime_resume_and_get(dev); + if (ret) + goto err; - ret = dw_mipi_csi2rx_start(csi2); - if (ret) { - dev_err(dev, "failed to enable CSI hardware\n"); - goto err_pm_runtime_put; + ret = dw_mipi_csi2rx_start(csi2); + if (ret) { + dev_err(dev, "failed to enable CSI hardware\n"); + goto err_pm_runtime_put; + } } ret = v4l2_subdev_enable_streams(remote_sd, remote_pad->index, mask); if (ret) goto err_csi_stop; + if (!csi2->enabled_streams && + csi2->drvdata->wait_for_phy_stopstate) { + ret = csi2->drvdata->wait_for_phy_stopstate(csi2); + if (ret) + goto err_disable_streams; + } + + csi2->enabled_streams |= streams_mask; + return 0; +err_disable_streams: + v4l2_subdev_disable_streams(remote_sd, remote_pad->index, mask); err_csi_stop: - dw_mipi_csi2rx_stop(csi2); + /* Stop CSI hardware if no streams are enabled */ + if (!csi2->enabled_streams) + dw_mipi_csi2rx_stop(csi2); err_pm_runtime_put: - pm_runtime_put(dev); + if (!csi2->enabled_streams) + pm_runtime_put(dev); err: return ret; } @@ -552,10 +617,15 @@ static int dw_mipi_csi2rx_disable_streams(struct v4l2_subdev *sd, &streams_mask); ret = v4l2_subdev_disable_streams(remote_sd, remote_pad->index, mask); + if (ret) + dev_err(dev, "failed to disable streams on remote subdev: %d\n", ret); - dw_mipi_csi2rx_stop(csi2); + csi2->enabled_streams &= ~streams_mask; - pm_runtime_put(dev); + if (!csi2->enabled_streams) { + dw_mipi_csi2rx_stop(csi2); + pm_runtime_put(dev); + } return ret; } @@ -827,11 +897,39 @@ static void imx93_csi2rx_dphy_ipi_enable(struct dw_mipi_csi2rx_device *csi2) dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_IPI_MODE, val); } +static int imx93_csi2rx_wait_for_phy_stopstate(struct dw_mipi_csi2rx_device *csi2) +{ + struct device *dev = csi2->dev; + u32 stopstate_mask; + u32 val; + int ret; + + stopstate_mask = DPHY_STOPSTATE_CLK_LANE | GENMASK(csi2->lanes_num - 1, 0); + + ret = read_poll_timeout(dw_mipi_csi2rx_read, val, + (val & stopstate_mask) == stopstate_mask, + 10, 1000, true, + csi2, DW_MIPI_CSI2RX_PHY_STOPSTATE); + if (ret) + dev_err(dev, "lanes are not in stop state: %#x, expected %#x\n", + val, stopstate_mask); + + return ret; +} + static const struct dw_mipi_csi2rx_drvdata imx93_drvdata = { .regs = imx93_regs, .dphy_assert_reset = imx93_csi2rx_dphy_assert_reset, .dphy_deassert_reset = imx93_csi2rx_dphy_deassert_reset, .ipi_enable = imx93_csi2rx_dphy_ipi_enable, + .wait_for_phy_stopstate = imx93_csi2rx_wait_for_phy_stopstate, +}; + +static const struct dw_mipi_csi2rx_drvdata imx95_drvdata = { + .regs = imx95_regs, + .dphy_assert_reset = imx93_csi2rx_dphy_assert_reset, + .dphy_deassert_reset = imx93_csi2rx_dphy_deassert_reset, + .wait_for_phy_stopstate = imx93_csi2rx_wait_for_phy_stopstate, }; static const struct of_device_id dw_mipi_csi2rx_of_match[] = { @@ -840,6 +938,10 @@ static const struct of_device_id dw_mipi_csi2rx_of_match[] = { .data = &imx93_drvdata, }, { + .compatible = "fsl,imx95-mipi-csi2", + .data = &imx95_drvdata, + }, + { .compatible = "rockchip,rk3568-mipi-csi2", .data = &rk3568_drvdata, }, diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c index 1061ab50dd64..25f8ca0d6d94 100644 --- a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c +++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c @@ -506,9 +506,9 @@ static void hdmirx_hpd_ctrl(struct snps_hdmirx_dev *hdmirx_dev, bool en) hdmirx_writel(hdmirx_dev, CORE_CONFIG, hdmirx_dev->hpd_trigger_level_high ? en : !en); - /* 100ms delay as per HDMI spec */ + /* 100ms delay as per HDMI spec + extra 50ms to cover internal delay */ if (!en) - msleep(100); + msleep(100 + 50); } static void hdmirx_write_edid_data(struct snps_hdmirx_dev *hdmirx_dev, diff --git a/drivers/media/platform/ti/Kconfig b/drivers/media/platform/ti/Kconfig index da33facf4467..d0cb05481bd8 100644 --- a/drivers/media/platform/ti/Kconfig +++ b/drivers/media/platform/ti/Kconfig @@ -83,6 +83,7 @@ config VIDEO_TI_J721E_CSI2RX depends on VIDEO_CADENCE_CSI2RX depends on PHY_CADENCE_DPHY_RX || COMPILE_TEST depends on ARCH_K3 || COMPILE_TEST + depends on PM select VIDEOBUF2_DMA_CONTIG select V4L2_FWNODE help diff --git a/drivers/media/platform/ti/davinci/vpif_capture.c b/drivers/media/platform/ti/davinci/vpif_capture.c index 15df3ea2f77e..91cb6223561a 100644 --- a/drivers/media/platform/ti/davinci/vpif_capture.c +++ b/drivers/media/platform/ti/davinci/vpif_capture.c @@ -1498,7 +1498,7 @@ vpif_capture_get_pdata(struct platform_device *pdev, * video ports & endpoints data. */ if (pdev->dev.parent && pdev->dev.parent->of_node) - pdev->dev.of_node = pdev->dev.parent->of_node; + device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent); if (!IS_ENABLED(CONFIG_OF) || !pdev->dev.of_node) return pdev->dev.platform_data; diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c index b75aa363d1bf..4769931b1930 100644 --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/of_platform.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/property.h> #include <media/cadence/cdns-csi2rx.h> @@ -27,23 +28,25 @@ #define SHIM_CNTL 0x10 #define SHIM_CNTL_PIX_RST BIT(0) -#define SHIM_DMACNTX 0x20 +#define SHIM_DMACNTX(i) (0x20 + ((i) * 0x20)) #define SHIM_DMACNTX_EN BIT(31) #define SHIM_DMACNTX_YUV422 GENMASK(27, 26) #define SHIM_DMACNTX_DUAL_PCK_CFG BIT(24) #define SHIM_DMACNTX_SIZE GENMASK(21, 20) +#define SHIM_DMACNTX_VC GENMASK(9, 6) #define SHIM_DMACNTX_FMT GENMASK(5, 0) #define SHIM_DMACNTX_YUV422_MODE_11 3 #define SHIM_DMACNTX_SIZE_8 0 #define SHIM_DMACNTX_SIZE_16 1 #define SHIM_DMACNTX_SIZE_32 2 -#define SHIM_PSI_CFG0 0x24 +#define SHIM_PSI_CFG0(i) (0x24 + ((i) * 0x20)) #define SHIM_PSI_CFG0_SRC_TAG GENMASK(15, 0) #define SHIM_PSI_CFG0_DST_TAG GENMASK(31, 16) #define TI_CSI2RX_MAX_PIX_PER_CLK 4 -#define PSIL_WORD_SIZE_BYTES 16 +#define TI_CSI2RX_MAX_CTX 32 + /* * There are no hard limits on the width or height. The DMA engine can handle * all sizes. The max width and height are arbitrary numbers for this driver. @@ -53,6 +56,11 @@ #define MAX_WIDTH_BYTES SZ_16K #define MAX_HEIGHT_LINES SZ_16K +#define TI_CSI2RX_PAD_SINK 0 +#define TI_CSI2RX_PAD_FIRST_SOURCE 1 +#define TI_CSI2RX_MAX_SOURCE_PADS TI_CSI2RX_MAX_CTX +#define TI_CSI2RX_MAX_PADS (1 + TI_CSI2RX_MAX_SOURCE_PADS) + #define DRAIN_TIMEOUT_MS 50 #define DRAIN_BUFFER_SIZE SZ_32K @@ -70,13 +78,13 @@ struct ti_csi2rx_buffer { /* Common v4l2 buffer. Must be first. */ struct vb2_v4l2_buffer vb; struct list_head list; - struct ti_csi2rx_dev *csi; + struct ti_csi2rx_ctx *ctx; }; enum ti_csi2rx_dma_state { TI_CSI2RX_DMA_STOPPED, /* Streaming not started yet. */ - TI_CSI2RX_DMA_IDLE, /* Streaming but no pending DMA operation. */ TI_CSI2RX_DMA_ACTIVE, /* Streaming and pending DMA operation. */ + TI_CSI2RX_DMA_DRAINING, /* Dumping all the data in drain buffer */ }; struct ti_csi2rx_dma { @@ -90,32 +98,54 @@ struct ti_csi2rx_dma { * Queue of buffers submitted to DMA engine. */ struct list_head submitted; - /* Buffer to drain stale data from PSI-L endpoint */ - struct { - void *vaddr; - dma_addr_t paddr; - size_t len; - } drain; +}; + +struct ti_csi2rx_dev; + +struct ti_csi2rx_ctx { + struct ti_csi2rx_dev *csi; + struct video_device vdev; + struct vb2_queue vidq; + struct mutex mutex; /* To serialize ioctls. */ + struct v4l2_format v_fmt; + struct ti_csi2rx_dma dma; + struct media_pad pad; + struct completion drain_complete; + u32 sequence; + u32 idx; + u32 vc; + u32 dt; + u32 stream; }; struct ti_csi2rx_dev { struct device *dev; void __iomem *shim; + unsigned int enable_count; + unsigned int num_ctx; struct v4l2_device v4l2_dev; - struct video_device vdev; struct media_device mdev; struct media_pipeline pipe; - struct media_pad pad; + struct media_pad pads[TI_CSI2RX_MAX_PADS]; struct v4l2_async_notifier notifier; struct v4l2_subdev *source; - struct vb2_queue vidq; - struct mutex mutex; /* To serialize ioctls. */ - struct v4l2_format v_fmt; - struct ti_csi2rx_dma dma; - u32 sequence; + struct v4l2_subdev subdev; + struct ti_csi2rx_ctx ctx[TI_CSI2RX_MAX_CTX]; + struct notifier_block pm_notifier; u8 pix_per_clk; + /* Buffer to drain stale data from PSI-L endpoint */ + struct { + void *vaddr; + dma_addr_t paddr; + size_t len; + } drain; }; +static inline struct ti_csi2rx_dev *to_csi2rx_dev(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ti_csi2rx_dev, subdev); +} + static const struct ti_csi2rx_fmt ti_csi2rx_formats[] = { { .fourcc = V4L2_PIX_FMT_YUYV, @@ -219,9 +249,13 @@ static const struct ti_csi2rx_fmt ti_csi2rx_formats[] = { }; /* Forward declaration needed by ti_csi2rx_dma_callback. */ -static int ti_csi2rx_start_dma(struct ti_csi2rx_dev *csi, +static int ti_csi2rx_start_dma(struct ti_csi2rx_ctx *ctx, struct ti_csi2rx_buffer *buf); +/* Forward declarations needed by ti_csi2rx_drain_callback. */ +static int ti_csi2rx_drain_dma(struct ti_csi2rx_ctx *ctx); +static int ti_csi2rx_dma_submit_pending(struct ti_csi2rx_ctx *ctx); + static const struct ti_csi2rx_fmt *find_format_by_fourcc(u32 pixelformat) { unsigned int i; @@ -250,19 +284,12 @@ static void ti_csi2rx_fill_fmt(const struct ti_csi2rx_fmt *csi_fmt, struct v4l2_format *v4l2_fmt) { struct v4l2_pix_format *pix = &v4l2_fmt->fmt.pix; - unsigned int pixels_in_word; - - pixels_in_word = PSIL_WORD_SIZE_BYTES * 8 / csi_fmt->bpp; /* Clamp width and height to sensible maximums (16K x 16K) */ pix->width = clamp_t(unsigned int, pix->width, - pixels_in_word, - MAX_WIDTH_BYTES * 8 / csi_fmt->bpp); + 1, MAX_WIDTH_BYTES * 8 / csi_fmt->bpp); pix->height = clamp_t(unsigned int, pix->height, 1, MAX_HEIGHT_LINES); - /* Width should be a multiple of transfer word-size */ - pix->width = rounddown(pix->width, pixels_in_word); - v4l2_fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; pix->pixelformat = csi_fmt->fourcc; pix->bytesperline = pix->width * (csi_fmt->bpp / 8); @@ -309,7 +336,7 @@ static int ti_csi2rx_enum_fmt_vid_cap(struct file *file, void *priv, static int ti_csi2rx_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct ti_csi2rx_dev *csi = video_drvdata(file); + struct ti_csi2rx_ctx *csi = video_drvdata(file); *f = csi->v_fmt; @@ -340,7 +367,7 @@ static int ti_csi2rx_try_fmt_vid_cap(struct file *file, void *priv, static int ti_csi2rx_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct ti_csi2rx_dev *csi = video_drvdata(file); + struct ti_csi2rx_ctx *csi = video_drvdata(file); struct vb2_queue *q = &csi->vidq; int ret; @@ -360,23 +387,15 @@ static int ti_csi2rx_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize) { const struct ti_csi2rx_fmt *fmt; - unsigned int pixels_in_word; fmt = find_format_by_fourcc(fsize->pixel_format); if (!fmt || fsize->index != 0) return -EINVAL; - /* - * Number of pixels in one PSI-L word. The transfer happens in multiples - * of PSI-L word sizes. - */ - pixels_in_word = PSIL_WORD_SIZE_BYTES * 8 / fmt->bpp; - fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; - fsize->stepwise.min_width = pixels_in_word; - fsize->stepwise.max_width = rounddown(MAX_WIDTH_BYTES * 8 / fmt->bpp, - pixels_in_word); - fsize->stepwise.step_width = pixels_in_word; + fsize->stepwise.min_width = 1; + fsize->stepwise.max_width = MAX_WIDTH_BYTES * 8 / fmt->bpp; + fsize->stepwise.step_width = 1; fsize->stepwise.min_height = 1; fsize->stepwise.max_height = MAX_HEIGHT_LINES; fsize->stepwise.step_height = 1; @@ -426,26 +445,50 @@ static int csi_async_notifier_bound(struct v4l2_async_notifier *notifier, static int csi_async_notifier_complete(struct v4l2_async_notifier *notifier) { struct ti_csi2rx_dev *csi = dev_get_drvdata(notifier->v4l2_dev->dev); - struct video_device *vdev = &csi->vdev; - int ret; + int ret, i; + + /* Create link from source to subdev */ + ret = media_create_pad_link(&csi->source->entity, + CSI2RX_BRIDGE_SOURCE_PAD, + &csi->subdev.entity, + TI_CSI2RX_PAD_SINK, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); - ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) return ret; - ret = media_create_pad_link(&csi->source->entity, CSI2RX_BRIDGE_SOURCE_PAD, - &vdev->entity, csi->pad.index, - MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); + /* Create and link video nodes for all DMA contexts */ + for (i = 0; i < csi->num_ctx; i++) { + struct ti_csi2rx_ctx *ctx = &csi->ctx[i]; + struct video_device *vdev = &ctx->vdev; - if (ret) { - video_unregister_device(vdev); - return ret; + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) + goto unregister_dev; + + ret = media_create_pad_link(&csi->subdev.entity, + TI_CSI2RX_PAD_FIRST_SOURCE + ctx->idx, + &vdev->entity, 0, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + if (ret) { + video_unregister_device(vdev); + goto unregister_dev; + } } ret = v4l2_device_register_subdev_nodes(&csi->v4l2_dev); if (ret) - video_unregister_device(vdev); + goto unregister_dev; + return 0; + +unregister_dev: + while (i--) { + media_entity_remove_links(&csi->ctx[i].vdev.entity); + video_unregister_device(&csi->ctx[i].vdev); + } return ret; } @@ -496,7 +539,7 @@ static void ti_csi2rx_request_max_ppc(struct ti_csi2rx_dev *csi) struct media_pad *pad; int ret; - pad = media_entity_remote_source_pad_unique(&csi->vdev.entity); + pad = media_entity_remote_source_pad_unique(&csi->subdev.entity); if (IS_ERR(pad)) return; @@ -509,22 +552,19 @@ static void ti_csi2rx_request_max_ppc(struct ti_csi2rx_dev *csi) } } -static void ti_csi2rx_setup_shim(struct ti_csi2rx_dev *csi) +static void ti_csi2rx_setup_shim(struct ti_csi2rx_ctx *ctx) { + struct ti_csi2rx_dev *csi = ctx->csi; const struct ti_csi2rx_fmt *fmt; unsigned int reg; - fmt = find_format_by_fourcc(csi->v_fmt.fmt.pix.pixelformat); - - /* De-assert the pixel interface reset. */ - reg = SHIM_CNTL_PIX_RST; - writel(reg, csi->shim + SHIM_CNTL); + fmt = find_format_by_fourcc(ctx->v_fmt.fmt.pix.pixelformat); /* Negotiate pixel count from the source */ ti_csi2rx_request_max_ppc(csi); reg = SHIM_DMACNTX_EN; - reg |= FIELD_PREP(SHIM_DMACNTX_FMT, fmt->csi_dt); + reg |= FIELD_PREP(SHIM_DMACNTX_FMT, ctx->dt); /* * The hardware assumes incoming YUV422 8-bit data on MIPI CSI2 bus @@ -563,18 +603,43 @@ static void ti_csi2rx_setup_shim(struct ti_csi2rx_dev *csi) break; } - writel(reg, csi->shim + SHIM_DMACNTX); + reg |= FIELD_PREP(SHIM_DMACNTX_VC, ctx->vc); + + writel(reg, csi->shim + SHIM_DMACNTX(ctx->idx)); reg = FIELD_PREP(SHIM_PSI_CFG0_SRC_TAG, 0) | FIELD_PREP(SHIM_PSI_CFG0_DST_TAG, 0); - writel(reg, csi->shim + SHIM_PSI_CFG0); + writel(reg, csi->shim + SHIM_PSI_CFG0(ctx->idx)); } static void ti_csi2rx_drain_callback(void *param) { - struct completion *drain_complete = param; + struct ti_csi2rx_ctx *ctx = param; + struct ti_csi2rx_dma *dma = &ctx->dma; + unsigned long flags; + + spin_lock_irqsave(&dma->lock, flags); + + if (dma->state == TI_CSI2RX_DMA_STOPPED) { + complete(&ctx->drain_complete); + spin_unlock_irqrestore(&dma->lock, flags); + return; + } - complete(drain_complete); + /* + * If dma->queue is empty, it indicates that no buffer has been + * provided by user space. In this case, initiate a transactions + * to drain the DMA. Since one drain of size DRAIN_BUFFER_SIZE + * will be done here, the subsequent frame will be a + * partial frame, with a size of frame_size - DRAIN_BUFFER_SIZE + */ + if (list_empty(&dma->queue)) { + if (ti_csi2rx_drain_dma(ctx)) + dev_warn(ctx->csi->dev, "DMA drain failed\n"); + } else { + ti_csi2rx_dma_submit_pending(ctx); + } + spin_unlock_irqrestore(&dma->lock, flags); } /* @@ -588,17 +653,15 @@ static void ti_csi2rx_drain_callback(void *param) * To prevent that stale data corrupting the subsequent transactions, it is * required to issue DMA requests to drain it out. */ -static int ti_csi2rx_drain_dma(struct ti_csi2rx_dev *csi) +static int ti_csi2rx_drain_dma(struct ti_csi2rx_ctx *ctx) { + struct ti_csi2rx_dev *csi = ctx->csi; struct dma_async_tx_descriptor *desc; - struct completion drain_complete; dma_cookie_t cookie; int ret; - init_completion(&drain_complete); - - desc = dmaengine_prep_slave_single(csi->dma.chan, csi->dma.drain.paddr, - csi->dma.drain.len, DMA_DEV_TO_MEM, + desc = dmaengine_prep_slave_single(ctx->dma.chan, csi->drain.paddr, + csi->drain.len, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { ret = -EIO; @@ -606,31 +669,46 @@ static int ti_csi2rx_drain_dma(struct ti_csi2rx_dev *csi) } desc->callback = ti_csi2rx_drain_callback; - desc->callback_param = &drain_complete; + desc->callback_param = ctx; cookie = dmaengine_submit(desc); ret = dma_submit_error(cookie); if (ret) goto out; - dma_async_issue_pending(csi->dma.chan); + dma_async_issue_pending(ctx->dma.chan); - if (!wait_for_completion_timeout(&drain_complete, - msecs_to_jiffies(DRAIN_TIMEOUT_MS))) { - dmaengine_terminate_sync(csi->dma.chan); - dev_dbg(csi->dev, "DMA transfer timed out for drain buffer\n"); - ret = -ETIMEDOUT; - goto out; - } out: return ret; } +static int ti_csi2rx_dma_submit_pending(struct ti_csi2rx_ctx *ctx) +{ + struct ti_csi2rx_dma *dma = &ctx->dma; + struct ti_csi2rx_buffer *buf; + int ret = 0; + + /* If there are more buffers to process then start their transfer. */ + while (!list_empty(&dma->queue)) { + buf = list_entry(dma->queue.next, struct ti_csi2rx_buffer, list); + ret = ti_csi2rx_start_dma(ctx, buf); + if (ret) { + dev_err(ctx->csi->dev, + "Failed to queue the next buffer for DMA\n"); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + list_del(&buf->list); + } else { + list_move_tail(&buf->list, &dma->submitted); + } + } + return ret; +} + static void ti_csi2rx_dma_callback(void *param) { struct ti_csi2rx_buffer *buf = param; - struct ti_csi2rx_dev *csi = buf->csi; - struct ti_csi2rx_dma *dma = &csi->dma; + struct ti_csi2rx_ctx *ctx = buf->ctx; + struct ti_csi2rx_dma *dma = &ctx->dma; unsigned long flags; /* @@ -638,44 +716,43 @@ static void ti_csi2rx_dma_callback(void *param) * hardware monitor registers. */ buf->vb.vb2_buf.timestamp = ktime_get_ns(); - buf->vb.sequence = csi->sequence++; + buf->vb.sequence = ctx->sequence++; spin_lock_irqsave(&dma->lock, flags); WARN_ON(!list_is_first(&buf->list, &dma->submitted)); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); - list_del(&buf->list); - /* If there are more buffers to process then start their transfer. */ - while (!list_empty(&dma->queue)) { - buf = list_entry(dma->queue.next, struct ti_csi2rx_buffer, list); - - if (ti_csi2rx_start_dma(csi, buf)) { - dev_err(csi->dev, "Failed to queue the next buffer for DMA\n"); - list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); - } else { - list_move_tail(&buf->list, &dma->submitted); - } + if (dma->state == TI_CSI2RX_DMA_DRAINING) { + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + dma->state = TI_CSI2RX_DMA_ACTIVE; + } else { + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); } - if (list_empty(&dma->submitted)) - dma->state = TI_CSI2RX_DMA_IDLE; + list_del(&buf->list); + + ti_csi2rx_dma_submit_pending(ctx); + if (list_empty(&dma->submitted)) { + dma->state = TI_CSI2RX_DMA_DRAINING; + if (ti_csi2rx_drain_dma(ctx)) + dev_warn(ctx->csi->dev, + "DMA drain failed on one of the transactions\n"); + } spin_unlock_irqrestore(&dma->lock, flags); } -static int ti_csi2rx_start_dma(struct ti_csi2rx_dev *csi, +static int ti_csi2rx_start_dma(struct ti_csi2rx_ctx *ctx, struct ti_csi2rx_buffer *buf) { unsigned long addr; struct dma_async_tx_descriptor *desc; - size_t len = csi->v_fmt.fmt.pix.sizeimage; + size_t len = ctx->v_fmt.fmt.pix.sizeimage; dma_cookie_t cookie; int ret = 0; addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); - desc = dmaengine_prep_slave_single(csi->dma.chan, addr, len, + desc = dmaengine_prep_slave_single(ctx->dma.chan, addr, len, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) @@ -689,23 +766,25 @@ static int ti_csi2rx_start_dma(struct ti_csi2rx_dev *csi, if (ret) return ret; - dma_async_issue_pending(csi->dma.chan); + dma_async_issue_pending(ctx->dma.chan); return 0; } -static void ti_csi2rx_stop_dma(struct ti_csi2rx_dev *csi) +static void ti_csi2rx_stop_dma(struct ti_csi2rx_ctx *ctx) { - struct ti_csi2rx_dma *dma = &csi->dma; + struct ti_csi2rx_dma *dma = &ctx->dma; enum ti_csi2rx_dma_state state; unsigned long flags; int ret; spin_lock_irqsave(&dma->lock, flags); - state = csi->dma.state; + state = ctx->dma.state; dma->state = TI_CSI2RX_DMA_STOPPED; spin_unlock_irqrestore(&dma->lock, flags); + init_completion(&ctx->drain_complete); + if (state != TI_CSI2RX_DMA_STOPPED) { /* * Normal DMA termination does not clean up pending data on @@ -713,30 +792,38 @@ static void ti_csi2rx_stop_dma(struct ti_csi2rx_dev *csi) * is stopped, as the module-level pixel reset cannot be * enforced before terminating DMA. */ - ret = ti_csi2rx_drain_dma(csi); - if (ret && ret != -ETIMEDOUT) - dev_warn(csi->dev, + ret = ti_csi2rx_drain_dma(ctx); + if (ret) + dev_warn(ctx->csi->dev, "Failed to drain DMA. Next frame might be bogus\n"); } - ret = dmaengine_terminate_sync(csi->dma.chan); + /* We wait for the drain to complete so that the stream stops + * cleanly, making sure the shared hardware FIFO is cleared of + * data from the current stream. No more data will be coming from + * the source after this. + */ + wait_for_completion_timeout(&ctx->drain_complete, + msecs_to_jiffies(DRAIN_TIMEOUT_MS)); + + ret = dmaengine_terminate_sync(ctx->dma.chan); if (ret) - dev_err(csi->dev, "Failed to stop DMA: %d\n", ret); + dev_err(ctx->csi->dev, "Failed to stop DMA: %d\n", ret); } -static void ti_csi2rx_cleanup_buffers(struct ti_csi2rx_dev *csi, +static void ti_csi2rx_cleanup_buffers(struct ti_csi2rx_ctx *ctx, enum vb2_buffer_state state) { - struct ti_csi2rx_dma *dma = &csi->dma; + struct ti_csi2rx_dma *dma = &ctx->dma; struct ti_csi2rx_buffer *buf, *tmp; unsigned long flags; spin_lock_irqsave(&dma->lock, flags); - list_for_each_entry_safe(buf, tmp, &csi->dma.queue, list) { + list_for_each_entry_safe(buf, tmp, &ctx->dma.queue, list) { list_del(&buf->list); vb2_buffer_done(&buf->vb.vb2_buf, state); } - list_for_each_entry_safe(buf, tmp, &csi->dma.submitted, list) { + list_for_each_entry_safe(buf, tmp, &ctx->dma.submitted, list) { list_del(&buf->list); vb2_buffer_done(&buf->vb.vb2_buf, state); } @@ -747,8 +834,8 @@ static int ti_csi2rx_queue_setup(struct vb2_queue *q, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], struct device *alloc_devs[]) { - struct ti_csi2rx_dev *csi = vb2_get_drv_priv(q); - unsigned int size = csi->v_fmt.fmt.pix.sizeimage; + struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(q); + unsigned int size = ctx->v_fmt.fmt.pix.sizeimage; if (*nplanes) { if (sizes[0] < size) @@ -764,11 +851,11 @@ static int ti_csi2rx_queue_setup(struct vb2_queue *q, unsigned int *nbuffers, static int ti_csi2rx_buffer_prepare(struct vb2_buffer *vb) { - struct ti_csi2rx_dev *csi = vb2_get_drv_priv(vb->vb2_queue); - unsigned long size = csi->v_fmt.fmt.pix.sizeimage; + struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + unsigned long size = ctx->v_fmt.fmt.pix.sizeimage; if (vb2_plane_size(vb, 0) < size) { - dev_err(csi->dev, "Data will not fit into plane\n"); + dev_err(ctx->csi->dev, "Data will not fit into plane\n"); return -EINVAL; } @@ -778,132 +865,161 @@ static int ti_csi2rx_buffer_prepare(struct vb2_buffer *vb) static void ti_csi2rx_buffer_queue(struct vb2_buffer *vb) { - struct ti_csi2rx_dev *csi = vb2_get_drv_priv(vb->vb2_queue); + struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct ti_csi2rx_buffer *buf; - struct ti_csi2rx_dma *dma = &csi->dma; - bool restart_dma = false; + struct ti_csi2rx_dma *dma = &ctx->dma; unsigned long flags = 0; - int ret; buf = container_of(vb, struct ti_csi2rx_buffer, vb.vb2_buf); - buf->csi = csi; + buf->ctx = ctx; spin_lock_irqsave(&dma->lock, flags); - /* - * Usually the DMA callback takes care of queueing the pending buffers. - * But if DMA has stalled due to lack of buffers, restart it now. - */ - if (dma->state == TI_CSI2RX_DMA_IDLE) { - /* - * Do not restart DMA with the lock held because - * ti_csi2rx_drain_dma() might block for completion. - * There won't be a race on queueing DMA anyway since the - * callback is not being fired. - */ - restart_dma = true; - dma->state = TI_CSI2RX_DMA_ACTIVE; - } else { - list_add_tail(&buf->list, &dma->queue); - } + list_add_tail(&buf->list, &dma->queue); spin_unlock_irqrestore(&dma->lock, flags); +} - if (restart_dma) { - /* - * Once frames start dropping, some data gets stuck in the DMA - * pipeline somewhere. So the first DMA transfer after frame - * drops gives a partial frame. This is obviously not useful to - * the application and will only confuse it. Issue a DMA - * transaction to drain that up. - */ - ret = ti_csi2rx_drain_dma(csi); - if (ret && ret != -ETIMEDOUT) - dev_warn(csi->dev, - "Failed to drain DMA. Next frame might be bogus\n"); +static int ti_csi2rx_get_stream(struct ti_csi2rx_ctx *ctx) +{ + struct ti_csi2rx_dev *csi = ctx->csi; + struct media_pad *pad; + struct v4l2_subdev_state *state; + struct v4l2_subdev_route *r; + + /* Get the source pad connected to this ctx */ + pad = media_entity_remote_source_pad_unique(ctx->pad.entity); + if (IS_ERR(pad)) { + dev_err(csi->dev, "No pad connected to ctx %d\n", ctx->idx); + return PTR_ERR(pad); + } - spin_lock_irqsave(&dma->lock, flags); - ret = ti_csi2rx_start_dma(csi, buf); - if (ret) { - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); - dma->state = TI_CSI2RX_DMA_IDLE; - spin_unlock_irqrestore(&dma->lock, flags); - dev_err(csi->dev, "Failed to start DMA: %d\n", ret); - } else { - list_add_tail(&buf->list, &dma->submitted); - spin_unlock_irqrestore(&dma->lock, flags); + state = v4l2_subdev_get_locked_active_state(&csi->subdev); + + for_each_active_route(&state->routing, r) { + if (r->source_pad == pad->index) { + ctx->stream = r->sink_stream; + return 0; } } + + /* No route found for this ctx */ + return -ENODEV; +} + +static int ti_csi2rx_get_vc_and_dt(struct ti_csi2rx_ctx *ctx) +{ + struct ti_csi2rx_dev *csi = ctx->csi; + struct ti_csi2rx_ctx *curr_ctx; + struct v4l2_mbus_frame_desc fd; + struct media_pad *source_pad; + const struct ti_csi2rx_fmt *fmt; + int ret; + unsigned int i, j; + + /* Get the frame desc from source */ + source_pad = media_entity_remote_pad_unique(&csi->subdev.entity, MEDIA_PAD_FL_SOURCE); + if (IS_ERR(source_pad)) + return PTR_ERR(source_pad); + + ret = v4l2_subdev_call(csi->source, pad, get_frame_desc, source_pad->index, &fd); + if (ret) { + if (ret == -ENOIOCTLCMD) { + ctx->vc = 0; + fmt = find_format_by_fourcc(ctx->v_fmt.fmt.pix.pixelformat); + ctx->dt = fmt->csi_dt; + } + return ret; + } + + if (fd.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) + return -EINVAL; + + for (i = 0; i < csi->num_ctx; i++) { + curr_ctx = &csi->ctx[i]; + + /* Capture VC 0 by default */ + curr_ctx->vc = 0; + + ret = ti_csi2rx_get_stream(curr_ctx); + if (ret) + continue; + + for (j = 0; j < fd.num_entries; j++) { + if (curr_ctx->stream == fd.entry[j].stream) { + curr_ctx->vc = fd.entry[j].bus.csi2.vc; + curr_ctx->dt = fd.entry[j].bus.csi2.dt; + break; + } + + /* Return error if no matching stream found */ + if (j == fd.num_entries) + return -EINVAL; + } + } + + return 0; } static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count) { - struct ti_csi2rx_dev *csi = vb2_get_drv_priv(vq); - struct ti_csi2rx_dma *dma = &csi->dma; - struct ti_csi2rx_buffer *buf; + struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(vq); + struct ti_csi2rx_dev *csi = ctx->csi; + struct ti_csi2rx_dma *dma = &ctx->dma; unsigned long flags; - int ret = 0; + int ret; + + ret = pm_runtime_resume_and_get(csi->dev); + if (ret) + return ret; spin_lock_irqsave(&dma->lock, flags); if (list_empty(&dma->queue)) ret = -EIO; spin_unlock_irqrestore(&dma->lock, flags); if (ret) - return ret; + goto err; - ret = video_device_pipeline_start(&csi->vdev, &csi->pipe); + ret = video_device_pipeline_start(&ctx->vdev, &csi->pipe); if (ret) goto err; - ti_csi2rx_setup_shim(csi); - - csi->sequence = 0; - - spin_lock_irqsave(&dma->lock, flags); - buf = list_entry(dma->queue.next, struct ti_csi2rx_buffer, list); - - ret = ti_csi2rx_start_dma(csi, buf); - if (ret) { - dev_err(csi->dev, "Failed to start DMA: %d\n", ret); - spin_unlock_irqrestore(&dma->lock, flags); - goto err_pipeline; - } - - list_move_tail(&buf->list, &dma->submitted); - dma->state = TI_CSI2RX_DMA_ACTIVE; - spin_unlock_irqrestore(&dma->lock, flags); - - ret = v4l2_subdev_call(csi->source, video, s_stream, 1); + /* Start stream 0, we don't allow multiple streams on the source pad */ + ret = v4l2_subdev_enable_streams(&csi->subdev, + TI_CSI2RX_PAD_FIRST_SOURCE + ctx->idx, + BIT_U64(0)); if (ret) goto err_dma; return 0; err_dma: - ti_csi2rx_stop_dma(csi); -err_pipeline: - video_device_pipeline_stop(&csi->vdev); + ti_csi2rx_stop_dma(ctx); + video_device_pipeline_stop(&ctx->vdev); writel(0, csi->shim + SHIM_CNTL); - writel(0, csi->shim + SHIM_DMACNTX); + writel(0, csi->shim + SHIM_DMACNTX(ctx->idx)); err: - ti_csi2rx_cleanup_buffers(csi, VB2_BUF_STATE_QUEUED); + ti_csi2rx_cleanup_buffers(ctx, VB2_BUF_STATE_QUEUED); + pm_runtime_put(csi->dev); + return ret; } static void ti_csi2rx_stop_streaming(struct vb2_queue *vq) { - struct ti_csi2rx_dev *csi = vb2_get_drv_priv(vq); + struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(vq); + struct ti_csi2rx_dev *csi = ctx->csi; int ret; - video_device_pipeline_stop(&csi->vdev); + video_device_pipeline_stop(&ctx->vdev); - writel(0, csi->shim + SHIM_CNTL); - writel(0, csi->shim + SHIM_DMACNTX); - - ret = v4l2_subdev_call(csi->source, video, s_stream, 0); + ret = v4l2_subdev_disable_streams(&csi->subdev, + TI_CSI2RX_PAD_FIRST_SOURCE + ctx->idx, + BIT_U64(0)); if (ret) dev_err(csi->dev, "Failed to stop subdev stream\n"); - ti_csi2rx_stop_dma(csi); - ti_csi2rx_cleanup_buffers(csi, VB2_BUF_STATE_ERROR); + ti_csi2rx_stop_dma(ctx); + ti_csi2rx_cleanup_buffers(ctx, VB2_BUF_STATE_ERROR); + pm_runtime_put(csi->dev); } static const struct vb2_ops csi_vb2_qops = { @@ -914,20 +1030,265 @@ static const struct vb2_ops csi_vb2_qops = { .stop_streaming = ti_csi2rx_stop_streaming, }; -static int ti_csi2rx_init_vb2q(struct ti_csi2rx_dev *csi) +static int ti_csi2rx_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code_enum) { - struct vb2_queue *q = &csi->vidq; + if (code_enum->index >= ARRAY_SIZE(ti_csi2rx_formats)) + return -EINVAL; + + code_enum->code = ti_csi2rx_formats[code_enum->index].code; + + return 0; +} + +static int ti_csi2rx_sd_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *fmt; + + /* No transcoding, don't allow setting source fmt */ + if (format->pad > TI_CSI2RX_PAD_SINK) + return v4l2_subdev_get_fmt(sd, state, format); + + if (!find_format_by_code(format->format.code)) + format->format.code = ti_csi2rx_formats[0].code; + + format->format.field = V4L2_FIELD_NONE; + + fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); + *fmt = format->format; + + fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, + format->stream); + if (!fmt) + return -EINVAL; + + *fmt = format->format; + + return 0; +} + +static int _ti_csi2rx_sd_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing) +{ + int ret; + + static const struct v4l2_mbus_framefmt format = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .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, + }; + + ret = v4l2_subdev_routing_validate(sd, routing, + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 | + V4L2_SUBDEV_ROUTING_NO_SOURCE_MULTIPLEXING); + + if (ret) + return ret; + + /* Only stream ID 0 allowed on source pads */ + for (unsigned int i = 0; i < routing->num_routes; ++i) { + const struct v4l2_subdev_route *route = &routing->routes[i]; + + if (route->source_stream != 0) + return -EINVAL; + } + + ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format); + + return ret; +} + +static int ti_csi2rx_sd_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + struct ti_csi2rx_dev *csi = to_csi2rx_dev(sd); + + if (csi->enable_count > 0) + return -EBUSY; + + return _ti_csi2rx_sd_set_routing(sd, state, routing); +} + +static int ti_csi2rx_sd_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_route routes[] = { { + .sink_pad = 0, + .sink_stream = 0, + .source_pad = TI_CSI2RX_PAD_FIRST_SOURCE, + .source_stream = 0, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + } }; + + struct v4l2_subdev_krouting routing = { + .num_routes = 1, + .routes = routes, + }; + + /* Initialize routing to single route to the fist source pad */ + return _ti_csi2rx_sd_set_routing(sd, state, &routing); +} + +static int ti_csi2rx_sd_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct ti_csi2rx_dev *csi = to_csi2rx_dev(sd); + struct ti_csi2rx_ctx *ctx = &csi->ctx[pad - TI_CSI2RX_PAD_FIRST_SOURCE]; + struct ti_csi2rx_dma *dma = &ctx->dma; + struct media_pad *remote_pad; + unsigned long flags; + u64 sink_streams; + int ret = 0; + unsigned int reg; + + ret = ti_csi2rx_get_stream(ctx); + if (ret) + return ret; + + /* Get the VC and DT for all enabled ctx on first stream start */ + if (!csi->enable_count) { + ret = ti_csi2rx_get_vc_and_dt(ctx); + if (ret < 0 && ret != -ENOIOCTLCMD) + return ret; + + /* De-assert the pixel interface reset. */ + reg = SHIM_CNTL_PIX_RST; + writel(reg, csi->shim + SHIM_CNTL); + } + + ti_csi2rx_setup_shim(ctx); + ctx->sequence = 0; + + spin_lock_irqsave(&dma->lock, flags); + + ret = ti_csi2rx_dma_submit_pending(ctx); + if (ret) { + spin_unlock_irqrestore(&dma->lock, flags); + return ret; + } + + dma->state = TI_CSI2RX_DMA_ACTIVE; + spin_unlock_irqrestore(&dma->lock, flags); + + remote_pad = media_entity_remote_source_pad_unique(&csi->subdev.entity); + if (IS_ERR(remote_pad)) + return PTR_ERR(remote_pad); + sink_streams = v4l2_subdev_state_xlate_streams(state, pad, + TI_CSI2RX_PAD_SINK, + &streams_mask); + + ret = v4l2_subdev_enable_streams(csi->source, remote_pad->index, + sink_streams); + if (ret) + return ret; + + csi->enable_count++; + + return 0; +} + +static int ti_csi2rx_sd_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct ti_csi2rx_dev *csi = to_csi2rx_dev(sd); + struct ti_csi2rx_ctx *ctx = &csi->ctx[pad - TI_CSI2RX_PAD_FIRST_SOURCE]; + struct media_pad *remote_pad; + u64 sink_streams; + int ret = 0; + + WARN_ON(csi->enable_count == 0); + + writel(0, csi->shim + SHIM_DMACNTX(ctx->idx)); + + /* assert pixel reset to prevent stale data */ + if (csi->enable_count == 1) + writel(0, csi->shim + SHIM_CNTL); + + remote_pad = media_entity_remote_source_pad_unique(&csi->subdev.entity); + if (IS_ERR(remote_pad)) + return PTR_ERR(remote_pad); + sink_streams = v4l2_subdev_state_xlate_streams(state, pad, + TI_CSI2RX_PAD_SINK, + &streams_mask); + + ret = v4l2_subdev_disable_streams(csi->source, remote_pad->index, + sink_streams); + if (!ret) + --csi->enable_count; + + return 0; +} + +static const struct v4l2_subdev_pad_ops ti_csi2rx_subdev_pad_ops = { + .enum_mbus_code = ti_csi2rx_enum_mbus_code, + .set_routing = ti_csi2rx_sd_set_routing, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = ti_csi2rx_sd_set_fmt, + .enable_streams = ti_csi2rx_sd_enable_streams, + .disable_streams = ti_csi2rx_sd_disable_streams, +}; + +static const struct v4l2_subdev_ops ti_csi2rx_subdev_ops = { + .pad = &ti_csi2rx_subdev_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops ti_csi2rx_internal_ops = { + .init_state = ti_csi2rx_sd_init_state, +}; + +static void ti_csi2rx_cleanup_v4l2(struct ti_csi2rx_dev *csi) +{ + v4l2_subdev_cleanup(&csi->subdev); + media_device_unregister(&csi->mdev); + v4l2_device_unregister(&csi->v4l2_dev); + media_device_cleanup(&csi->mdev); +} + +static void ti_csi2rx_cleanup_notifier(struct ti_csi2rx_dev *csi) +{ + v4l2_async_nf_unregister(&csi->notifier); + v4l2_async_nf_cleanup(&csi->notifier); +} + +static void ti_csi2rx_cleanup_ctx(struct ti_csi2rx_ctx *ctx) +{ + if (!pm_runtime_status_suspended(ctx->csi->dev)) + dma_release_channel(ctx->dma.chan); + + vb2_queue_release(&ctx->vidq); + + video_unregister_device(&ctx->vdev); + + mutex_destroy(&ctx->mutex); +} + +static int ti_csi2rx_init_vb2q(struct ti_csi2rx_ctx *ctx) +{ + struct vb2_queue *q = &ctx->vidq; int ret; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; q->io_modes = VB2_MMAP | VB2_DMABUF; - q->drv_priv = csi; + q->drv_priv = ctx; q->buf_struct_size = sizeof(struct ti_csi2rx_buffer); q->ops = &csi_vb2_qops; q->mem_ops = &vb2_dma_contig_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->dev = dmaengine_get_dma_device(csi->dma.chan); - q->lock = &csi->mutex; + q->dev = dmaengine_get_dma_device(ctx->dma.chan); + q->lock = &ctx->mutex; q->min_queued_buffers = 1; q->allow_cache_hints = 1; @@ -935,7 +1296,7 @@ static int ti_csi2rx_init_vb2q(struct ti_csi2rx_dev *csi) if (ret) return ret; - csi->vdev.queue = q; + ctx->vdev.queue = q; return 0; } @@ -944,50 +1305,55 @@ static int ti_csi2rx_link_validate(struct media_link *link) { struct media_entity *entity = link->sink->entity; struct video_device *vdev = media_entity_to_video_device(entity); - struct ti_csi2rx_dev *csi = container_of(vdev, struct ti_csi2rx_dev, vdev); - struct v4l2_pix_format *csi_fmt = &csi->v_fmt.fmt.pix; - struct v4l2_subdev_format source_fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .pad = link->source->index, - }; + struct ti_csi2rx_ctx *ctx = container_of(vdev, struct ti_csi2rx_ctx, vdev); + struct ti_csi2rx_dev *csi = ctx->csi; + struct v4l2_pix_format *csi_fmt = &ctx->v_fmt.fmt.pix; + struct v4l2_mbus_framefmt *format; + struct v4l2_subdev_state *state; const struct ti_csi2rx_fmt *ti_fmt; - int ret; - ret = v4l2_subdev_call_state_active(csi->source, pad, - get_fmt, &source_fmt); - if (ret) - return ret; + state = v4l2_subdev_lock_and_get_active_state(&csi->subdev); + format = v4l2_subdev_state_get_format(state, link->source->index, 0); + v4l2_subdev_unlock_state(state); - if (source_fmt.format.width != csi_fmt->width) { + if (!format) { + dev_err(csi->dev, + "No format present on \"%s\":%u:0\n", + link->source->entity->name, link->source->index); + return 0; + } + + if (format->width != csi_fmt->width) { dev_dbg(csi->dev, "Width does not match (source %u, sink %u)\n", - source_fmt.format.width, csi_fmt->width); + format->width, csi_fmt->width); return -EPIPE; } - if (source_fmt.format.height != csi_fmt->height) { + if (format->height != csi_fmt->height) { dev_dbg(csi->dev, "Height does not match (source %u, sink %u)\n", - source_fmt.format.height, csi_fmt->height); + format->height, csi_fmt->height); return -EPIPE; } - if (source_fmt.format.field != csi_fmt->field && + if (format->field != csi_fmt->field && csi_fmt->field != V4L2_FIELD_NONE) { dev_dbg(csi->dev, "Field does not match (source %u, sink %u)\n", - source_fmt.format.field, csi_fmt->field); + format->field, csi_fmt->field); return -EPIPE; } - ti_fmt = find_format_by_code(source_fmt.format.code); + ti_fmt = find_format_by_code(format->code); if (!ti_fmt) { dev_dbg(csi->dev, "Media bus format 0x%x not supported\n", - source_fmt.format.code); + format->code); return -EPIPE; } if (ti_fmt->fourcc != csi_fmt->pixelformat) { dev_dbg(csi->dev, - "Cannot transform source fmt 0x%x to sink fmt 0x%x\n", - ti_fmt->fourcc, csi_fmt->pixelformat); + "Cannot transform \"%s\":%u format %p4cc to %p4cc\n", + link->source->entity->name, link->source->index, + &ti_fmt->fourcc, &csi_fmt->pixelformat); return -EPIPE; } @@ -998,47 +1364,107 @@ static const struct media_entity_operations ti_csi2rx_video_entity_ops = { .link_validate = ti_csi2rx_link_validate, }; -static int ti_csi2rx_init_dma(struct ti_csi2rx_dev *csi) +static const struct media_entity_operations ti_csi2rx_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, + .has_pad_interdep = v4l2_subdev_has_pad_interdep, +}; + +static int ti_csi2rx_init_dma(struct ti_csi2rx_ctx *ctx) { struct dma_slave_config cfg = { .src_addr_width = DMA_SLAVE_BUSWIDTH_16_BYTES, }; + char name[5]; int ret; - INIT_LIST_HEAD(&csi->dma.queue); - INIT_LIST_HEAD(&csi->dma.submitted); - spin_lock_init(&csi->dma.lock); - - csi->dma.state = TI_CSI2RX_DMA_STOPPED; - - csi->dma.chan = dma_request_chan(csi->dev, "rx0"); - if (IS_ERR(csi->dma.chan)) - return PTR_ERR(csi->dma.chan); + snprintf(name, sizeof(name), "rx%u", ctx->idx); + ctx->dma.chan = dma_request_chan(ctx->csi->dev, name); + if (IS_ERR(ctx->dma.chan)) + return PTR_ERR(ctx->dma.chan); - ret = dmaengine_slave_config(csi->dma.chan, &cfg); + ret = dmaengine_slave_config(ctx->dma.chan, &cfg); if (ret) { - dma_release_channel(csi->dma.chan); + dma_release_channel(ctx->dma.chan); return ret; } - csi->dma.drain.len = DRAIN_BUFFER_SIZE; - csi->dma.drain.vaddr = dma_alloc_coherent(csi->dev, csi->dma.drain.len, - &csi->dma.drain.paddr, - GFP_KERNEL); - if (!csi->dma.drain.vaddr) - return -ENOMEM; - return 0; } static int ti_csi2rx_v4l2_init(struct ti_csi2rx_dev *csi) { struct media_device *mdev = &csi->mdev; - struct video_device *vdev = &csi->vdev; + struct v4l2_subdev *sd = &csi->subdev; + int ret; + + mdev->dev = csi->dev; + mdev->hw_revision = 1; + strscpy(mdev->model, "TI-CSI2RX", sizeof(mdev->model)); + + media_device_init(mdev); + + csi->v4l2_dev.mdev = mdev; + + ret = v4l2_device_register(csi->dev, &csi->v4l2_dev); + if (ret) + goto cleanup_media; + + ret = media_device_register(mdev); + if (ret) + goto unregister_v4l2; + + v4l2_subdev_init(sd, &ti_csi2rx_subdev_ops); + sd->internal_ops = &ti_csi2rx_internal_ops; + sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; + strscpy(sd->name, dev_name(csi->dev), sizeof(sd->name)); + sd->dev = csi->dev; + sd->entity.ops = &ti_csi2rx_subdev_entity_ops; + + csi->pads[TI_CSI2RX_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + + for (unsigned int i = TI_CSI2RX_PAD_FIRST_SOURCE; + i < TI_CSI2RX_PAD_FIRST_SOURCE + csi->num_ctx; i++) + csi->pads[i].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&sd->entity, + TI_CSI2RX_PAD_FIRST_SOURCE + csi->num_ctx, + csi->pads); + if (ret) + goto unregister_media; + + ret = v4l2_subdev_init_finalize(sd); + if (ret) + goto unregister_media; + + ret = v4l2_device_register_subdev(&csi->v4l2_dev, sd); + if (ret) + goto cleanup_subdev; + + return 0; + +cleanup_subdev: + v4l2_subdev_cleanup(sd); +unregister_media: + media_device_unregister(mdev); +unregister_v4l2: + v4l2_device_unregister(&csi->v4l2_dev); +cleanup_media: + media_device_cleanup(mdev); + + return ret; +} + +static int ti_csi2rx_init_ctx(struct ti_csi2rx_ctx *ctx) +{ + struct ti_csi2rx_dev *csi = ctx->csi; + struct video_device *vdev = &ctx->vdev; const struct ti_csi2rx_fmt *fmt; - struct v4l2_pix_format *pix_fmt = &csi->v_fmt.fmt.pix; + struct v4l2_pix_format *pix_fmt = &ctx->v_fmt.fmt.pix; int ret; + mutex_init(&ctx->mutex); + fmt = find_format_by_fourcc(V4L2_PIX_FMT_UYVY); if (!fmt) return -EINVAL; @@ -1047,19 +1473,20 @@ static int ti_csi2rx_v4l2_init(struct ti_csi2rx_dev *csi) pix_fmt->height = 480; pix_fmt->field = V4L2_FIELD_NONE; pix_fmt->colorspace = V4L2_COLORSPACE_SRGB; - pix_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; - pix_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE; - pix_fmt->xfer_func = V4L2_XFER_FUNC_SRGB; - - ti_csi2rx_fill_fmt(fmt, &csi->v_fmt); + pix_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601, + pix_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE, + pix_fmt->xfer_func = V4L2_XFER_FUNC_SRGB, - mdev->dev = csi->dev; - mdev->hw_revision = 1; - strscpy(mdev->model, "TI-CSI2RX", sizeof(mdev->model)); + ti_csi2rx_fill_fmt(fmt, &ctx->v_fmt); - media_device_init(mdev); + ctx->pad.flags = MEDIA_PAD_FL_SINK; + vdev->entity.ops = &ti_csi2rx_video_entity_ops; + ret = media_entity_pads_init(&ctx->vdev.entity, 1, &ctx->pad); + if (ret) + return ret; - strscpy(vdev->name, TI_CSI2RX_MODULE_NAME, sizeof(vdev->name)); + snprintf(vdev->name, sizeof(vdev->name), "%s context %u", + dev_name(csi->dev), ctx->idx); vdev->v4l2_dev = &csi->v4l2_dev; vdev->vfl_dir = VFL_DIR_RX; vdev->fops = &csi_fops; @@ -1067,61 +1494,184 @@ static int ti_csi2rx_v4l2_init(struct ti_csi2rx_dev *csi) vdev->release = video_device_release_empty; vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_IO_MC; - vdev->lock = &csi->mutex; - video_set_drvdata(vdev, csi); + vdev->lock = &ctx->mutex; + video_set_drvdata(vdev, ctx); - csi->pad.flags = MEDIA_PAD_FL_SINK; - vdev->entity.ops = &ti_csi2rx_video_entity_ops; - ret = media_entity_pads_init(&csi->vdev.entity, 1, &csi->pad); + INIT_LIST_HEAD(&ctx->dma.queue); + INIT_LIST_HEAD(&ctx->dma.submitted); + spin_lock_init(&ctx->dma.lock); + ctx->dma.state = TI_CSI2RX_DMA_STOPPED; + + ret = ti_csi2rx_init_dma(ctx); if (ret) return ret; - csi->v4l2_dev.mdev = mdev; - - ret = v4l2_device_register(csi->dev, &csi->v4l2_dev); + ret = ti_csi2rx_init_vb2q(ctx); if (ret) - return ret; + goto cleanup_dma; - ret = media_device_register(mdev); - if (ret) { - v4l2_device_unregister(&csi->v4l2_dev); - media_device_cleanup(mdev); - return ret; - } + return 0; + +cleanup_dma: + dma_release_channel(ctx->dma.chan); + return ret; +} + +static int ti_csi2rx_runtime_suspend(struct device *dev) +{ + struct ti_csi2rx_dev *csi = dev_get_drvdata(dev); + + if (csi->enable_count != 0) + return -EBUSY; + + for (unsigned int i = 0; i < csi->num_ctx; i++) + dma_release_channel(csi->ctx[i].dma.chan); return 0; } -static void ti_csi2rx_cleanup_dma(struct ti_csi2rx_dev *csi) +static int ti_csi2rx_runtime_resume(struct device *dev) { - dma_free_coherent(csi->dev, csi->dma.drain.len, - csi->dma.drain.vaddr, csi->dma.drain.paddr); - csi->dma.drain.vaddr = NULL; - dma_release_channel(csi->dma.chan); + struct ti_csi2rx_dev *csi = dev_get_drvdata(dev); + int ret; + + for (unsigned int i = 0; i < csi->num_ctx; i++) { + ret = ti_csi2rx_init_dma(&csi->ctx[i]); + if (ret) + return ret; + } + + return 0; } -static void ti_csi2rx_cleanup_v4l2(struct ti_csi2rx_dev *csi) +static int ti_csi2rx_suspend(struct device *dev) { - media_device_unregister(&csi->mdev); - v4l2_device_unregister(&csi->v4l2_dev); - media_device_cleanup(&csi->mdev); + struct ti_csi2rx_dev *csi = dev_get_drvdata(dev); + enum ti_csi2rx_dma_state state; + struct ti_csi2rx_ctx *ctx; + struct ti_csi2rx_dma *dma; + unsigned long flags = 0; + int ret = 0; + + /* If device was not in use we can simply suspend */ + if (pm_runtime_status_suspended(dev)) + return 0; + + /* + * If device is running, assert the pixel reset to cleanly stop any + * on-going streams before we suspend. + */ + writel(0, csi->shim + SHIM_CNTL); + + for (unsigned int i = 0; i < csi->num_ctx; i++) { + ctx = &csi->ctx[i]; + dma = &ctx->dma; + + spin_lock_irqsave(&dma->lock, flags); + state = dma->state; + spin_unlock_irqrestore(&dma->lock, flags); + + if (state != TI_CSI2RX_DMA_STOPPED) { + /* Disable source */ + ret = v4l2_subdev_disable_streams(&csi->subdev, + TI_CSI2RX_PAD_FIRST_SOURCE + ctx->idx, + BIT(0)); + if (ret) + dev_err(csi->dev, "Failed to stop subdev stream\n"); + } + + /* Stop any on-going streams */ + writel(0, csi->shim + SHIM_DMACNTX(ctx->idx)); + + /* Drain DMA */ + ti_csi2rx_drain_dma(ctx); + + /* Terminate DMA */ + ret = dmaengine_terminate_sync(ctx->dma.chan); + if (ret) + dev_err(csi->dev, "Failed to stop DMA\n"); + } + + return ret; } -static void ti_csi2rx_cleanup_subdev(struct ti_csi2rx_dev *csi) +static int ti_csi2rx_resume(struct device *dev) { - v4l2_async_nf_unregister(&csi->notifier); - v4l2_async_nf_cleanup(&csi->notifier); + struct ti_csi2rx_dev *csi = dev_get_drvdata(dev); + struct ti_csi2rx_ctx *ctx; + struct ti_csi2rx_dma *dma; + struct ti_csi2rx_buffer *buf; + unsigned long flags = 0; + unsigned int reg; + int ret = 0; + + /* If device was not in use, we can simply wakeup */ + if (pm_runtime_status_suspended(dev)) + return 0; + + /* If device was in use before, restore all the running streams */ + reg = SHIM_CNTL_PIX_RST; + writel(reg, csi->shim + SHIM_CNTL); + + for (unsigned int i = 0; i < csi->num_ctx; i++) { + ctx = &csi->ctx[i]; + dma = &ctx->dma; + spin_lock_irqsave(&dma->lock, flags); + if (dma->state != TI_CSI2RX_DMA_STOPPED) { + /* Re-submit all previously submitted buffers to DMA */ + list_for_each_entry(buf, &ctx->dma.submitted, list) { + ti_csi2rx_start_dma(ctx, buf); + } + spin_unlock_irqrestore(&dma->lock, flags); + + /* Restore stream config */ + ti_csi2rx_setup_shim(ctx); + + ret = v4l2_subdev_enable_streams(&csi->subdev, + TI_CSI2RX_PAD_FIRST_SOURCE + ctx->idx, + BIT(0)); + if (ret) + dev_err(ctx->csi->dev, "Failed to start subdev\n"); + } else { + spin_unlock_irqrestore(&dma->lock, flags); + } + } + + return ret; } -static void ti_csi2rx_cleanup_vb2q(struct ti_csi2rx_dev *csi) +static int ti_csi2rx_pm_notifier(struct notifier_block *nb, + unsigned long action, void *data) { - vb2_queue_release(&csi->vidq); + struct ti_csi2rx_dev *csi = + container_of(nb, struct ti_csi2rx_dev, pm_notifier); + + switch (action) { + case PM_HIBERNATION_PREPARE: + case PM_SUSPEND_PREPARE: + case PM_RESTORE_PREPARE: + ti_csi2rx_suspend(csi->dev); + break; + case PM_POST_SUSPEND: + case PM_POST_HIBERNATION: + case PM_POST_RESTORE: + ti_csi2rx_resume(csi->dev); + break; + } + + return NOTIFY_DONE; } +static const struct dev_pm_ops ti_csi2rx_pm_ops = { + RUNTIME_PM_OPS(ti_csi2rx_runtime_suspend, ti_csi2rx_runtime_resume, + NULL) +}; + static int ti_csi2rx_probe(struct platform_device *pdev) { + struct device_node *np = pdev->dev.of_node; struct ti_csi2rx_dev *csi; - int ret; + int ret = 0, i, count; csi = devm_kzalloc(&pdev->dev, sizeof(*csi), GFP_KERNEL); if (!csi) @@ -1130,47 +1680,86 @@ static int ti_csi2rx_probe(struct platform_device *pdev) csi->dev = &pdev->dev; platform_set_drvdata(pdev, csi); - mutex_init(&csi->mutex); csi->shim = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(csi->shim)) { ret = PTR_ERR(csi->shim); - goto err_mutex; + return ret; } - ret = ti_csi2rx_init_dma(csi); - if (ret) - goto err_mutex; + csi->drain.len = DRAIN_BUFFER_SIZE; + csi->drain.vaddr = dma_alloc_coherent(csi->dev, csi->drain.len, + &csi->drain.paddr, + GFP_KERNEL); + if (!csi->drain.vaddr) + return -ENOMEM; + + /* Only use as many contexts as the number of DMA channels allocated. */ + count = of_property_count_strings(np, "dma-names"); + if (count < 0) { + dev_err(csi->dev, "Failed to get DMA channel count: %d\n", count); + ret = count; + goto err_dma_chan; + } + + csi->num_ctx = count; + if (csi->num_ctx > TI_CSI2RX_MAX_CTX) { + dev_err(csi->dev, + "%u DMA channels passed. Maximum is %u.\n", + csi->num_ctx, TI_CSI2RX_MAX_CTX); + ret = -EINVAL; + goto err_dma_chan; + } ret = ti_csi2rx_v4l2_init(csi); if (ret) - goto err_dma; + goto err_dma_chan; + + for (i = 0; i < csi->num_ctx; i++) { + csi->ctx[i].idx = i; + csi->ctx[i].csi = csi; + ret = ti_csi2rx_init_ctx(&csi->ctx[i]); + if (ret) + goto err_ctx; + } - ret = ti_csi2rx_init_vb2q(csi); - if (ret) - goto err_v4l2; + pm_runtime_set_active(csi->dev); + pm_runtime_enable(csi->dev); ret = ti_csi2rx_notifier_register(csi); if (ret) - goto err_vb2q; + goto err_ctx; ret = devm_of_platform_populate(csi->dev); if (ret) { dev_err(csi->dev, "Failed to create children: %d\n", ret); - goto err_subdev; + goto err_notifier; + } + + /* + * Use PM notifier instead of .suspend/.resume callbacks because the + * ordering of callbacks among camera pipeline devices (sensor, serdes, + * CSI bridge) cannot be enforced even with device links. The notifier + * is called when the system is fully functional, ensuring all + * dependencies are available when stopping/starting streams. + */ + csi->pm_notifier.notifier_call = ti_csi2rx_pm_notifier; + ret = register_pm_notifier(&csi->pm_notifier); + if (ret) { + dev_err(csi->dev, "Failed to create PM notifier: %d\n", ret); + goto err_notifier; } return 0; -err_subdev: - ti_csi2rx_cleanup_subdev(csi); -err_vb2q: - ti_csi2rx_cleanup_vb2q(csi); -err_v4l2: +err_notifier: + ti_csi2rx_cleanup_notifier(csi); +err_ctx: + while (i--) + ti_csi2rx_cleanup_ctx(&csi->ctx[i]); ti_csi2rx_cleanup_v4l2(csi); -err_dma: - ti_csi2rx_cleanup_dma(csi); -err_mutex: - mutex_destroy(&csi->mutex); +err_dma_chan: + dma_free_coherent(csi->dev, csi->drain.len, csi->drain.vaddr, + csi->drain.paddr); return ret; } @@ -1178,14 +1767,19 @@ static void ti_csi2rx_remove(struct platform_device *pdev) { struct ti_csi2rx_dev *csi = platform_get_drvdata(pdev); - video_unregister_device(&csi->vdev); + if (!pm_runtime_status_suspended(&pdev->dev)) + pm_runtime_set_suspended(&pdev->dev); - ti_csi2rx_cleanup_vb2q(csi); - ti_csi2rx_cleanup_subdev(csi); - ti_csi2rx_cleanup_v4l2(csi); - ti_csi2rx_cleanup_dma(csi); + for (unsigned int i = 0; i < csi->num_ctx; i++) + ti_csi2rx_cleanup_ctx(&csi->ctx[i]); + + ti_csi2rx_cleanup_notifier(csi); + unregister_pm_notifier(&csi->pm_notifier); - mutex_destroy(&csi->mutex); + ti_csi2rx_cleanup_v4l2(csi); + dma_free_coherent(csi->dev, csi->drain.len, csi->drain.vaddr, + csi->drain.paddr); + pm_runtime_disable(&pdev->dev); } static const struct of_device_id ti_csi2rx_of_match[] = { @@ -1200,6 +1794,7 @@ static struct platform_driver ti_csi2rx_pdrv = { .driver = { .name = TI_CSI2RX_MODULE_NAME, .of_match_table = ti_csi2rx_of_match, + .pm = &ti_csi2rx_pm_ops, }, }; diff --git a/drivers/media/platform/ti/vpe/vip.c b/drivers/media/platform/ti/vpe/vip.c index 0e91e87bda9b..cb0a5a07a3d4 100644 --- a/drivers/media/platform/ti/vpe/vip.c +++ b/drivers/media/platform/ti/vpe/vip.c @@ -9,6 +9,7 @@ */ #include <linux/clk.h> +#include <linux/cleanup.h> #include <linux/delay.h> #include <linux/dma-mapping.h> #include <linux/err.h> @@ -3389,7 +3390,6 @@ static int vip_probe_complete(struct platform_device *pdev) struct vip_port *port; struct vip_dev *dev; struct device_node *parent = pdev->dev.of_node; - struct fwnode_handle *ep = NULL; unsigned int syscon_args[5]; int ret, i, slice_id, port_id, p; @@ -3411,8 +3411,9 @@ static int vip_probe_complete(struct platform_device *pdev) ctrl->syscon_bit_field[i] = syscon_args[i + 1]; for (p = 0; p < (VIP_NUM_PORTS * VIP_NUM_SLICES); p++) { - ep = fwnode_graph_get_next_endpoint_by_regs(of_fwnode_handle(parent), - p, 0); + struct fwnode_handle *ep __free(fwnode_handle) = + fwnode_graph_get_next_endpoint_by_regs( + of_fwnode_handle(parent), p, 0); if (!ep) continue; @@ -3447,7 +3448,6 @@ static int vip_probe_complete(struct platform_device *pdev) port = dev->ports[port_id]; vip_register_subdev_notify(port, ep); - fwnode_handle_put(ep); } return 0; } @@ -3472,7 +3472,7 @@ static int vip_probe_slice(struct platform_device *pdev, int slice) ret = devm_request_irq(&pdev->dev, dev->irq, vip_irq, 0, VIP_MODULE_NAME, dev); if (ret < 0) - return -ENOMEM; + return ret; spin_lock_init(&dev->slock); mutex_init(&dev->mutex); @@ -3490,7 +3490,7 @@ static int vip_probe_slice(struct platform_device *pdev, int slice) parser = devm_kzalloc(&pdev->dev, sizeof(*dev->parser), GFP_KERNEL); if (!parser) - return PTR_ERR_OR_ZERO(parser); + return -ENOMEM; parser->base = dev->base + (slice ? VIP_SLICE1_PARSER : VIP_SLICE0_PARSER); if (IS_ERR(parser->base)) @@ -3502,7 +3502,7 @@ static int vip_probe_slice(struct platform_device *pdev, int slice) dev->sc_assigned = VIP_NOT_ASSIGNED; sc = devm_kzalloc(&pdev->dev, sizeof(*dev->sc), GFP_KERNEL); if (!sc) - return PTR_ERR_OR_ZERO(sc); + return -ENOMEM; sc->base = dev->base + (slice ? VIP_SLICE1_SC : VIP_SLICE0_SC); if (IS_ERR(sc->base)) @@ -3514,7 +3514,7 @@ static int vip_probe_slice(struct platform_device *pdev, int slice) dev->csc_assigned = VIP_NOT_ASSIGNED; csc = devm_kzalloc(&pdev->dev, sizeof(*dev->csc), GFP_KERNEL); if (!csc) - return PTR_ERR_OR_ZERO(csc); + return -ENOMEM; csc->base = dev->base + (slice ? VIP_SLICE1_CSC : VIP_SLICE0_CSC); if (IS_ERR(csc->base)) diff --git a/drivers/media/platform/ti/vpe/vpe.c b/drivers/media/platform/ti/vpe/vpe.c index a7e5a85e72a1..81bd1f9cee30 100644 --- a/drivers/media/platform/ti/vpe/vpe.c +++ b/drivers/media/platform/ti/vpe/vpe.c @@ -2539,7 +2539,8 @@ static int vpe_probe(struct platform_device *pdev) "vpe_top"); if (!dev->res) { dev_err(&pdev->dev, "missing 'vpe_top' resources data\n"); - return -ENODEV; + ret = -ENODEV; + goto v4l2_dev_unreg; } /* diff --git a/drivers/media/platform/verisilicon/hantro_hw.h b/drivers/media/platform/verisilicon/hantro_hw.h index 5f2011529f02..13e573f1f19d 100644 --- a/drivers/media/platform/verisilicon/hantro_hw.h +++ b/drivers/media/platform/verisilicon/hantro_hw.h @@ -427,7 +427,6 @@ extern const struct hantro_postproc_ops rockchip_vpu981_postproc_ops; extern const u32 hantro_vp8_dec_mc_filter[8][6]; void hantro_watchdog(struct work_struct *work); -void hantro_run(struct hantro_ctx *ctx); void hantro_irq_done(struct hantro_dev *vpu, enum vb2_buffer_state result); void hantro_start_prepare_run(struct hantro_ctx *ctx); diff --git a/drivers/media/platform/verisilicon/hantro_v4l2.c b/drivers/media/platform/verisilicon/hantro_v4l2.c index fcf3bd9bcda2..83af9fa1ce94 100644 --- a/drivers/media/platform/verisilicon/hantro_v4l2.c +++ b/drivers/media/platform/verisilicon/hantro_v4l2.c @@ -222,6 +222,7 @@ static int vidioc_enum_fmt(struct file *file, void *priv, unsigned int num_fmts, i, j = 0; bool skip_mode_none, enum_all_formats; u32 index = f->index & ~V4L2_FMTDESC_FLAG_ENUM_ALL; + bool need_postproc = ctx->need_postproc; /* * If the V4L2_FMTDESC_FLAG_ENUM_ALL flag is set, we want to enumerate all @@ -230,6 +231,9 @@ static int vidioc_enum_fmt(struct file *file, void *priv, enum_all_formats = !!(f->index & V4L2_FMTDESC_FLAG_ENUM_ALL); f->index = index; + if (enum_all_formats) + need_postproc = HANTRO_AUTO_POSTPROC; + /* * When dealing with an encoder: * - on the capture side we want to filter out all MODE_NONE formats. @@ -242,7 +246,7 @@ static int vidioc_enum_fmt(struct file *file, void *priv, */ skip_mode_none = capture == ctx->is_encoder; - formats = hantro_get_formats(ctx, &num_fmts, HANTRO_AUTO_POSTPROC); + formats = hantro_get_formats(ctx, &num_fmts, need_postproc); for (i = 0; i < num_fmts; i++) { bool mode_none = formats[i].codec_mode == HANTRO_MODE_NONE; fmt = &formats[i]; diff --git a/drivers/media/radio/radio-si476x.c b/drivers/media/radio/radio-si476x.c index 9980346cb5ea..bfe89782dce4 100644 --- a/drivers/media/radio/radio-si476x.c +++ b/drivers/media/radio/radio-si476x.c @@ -1493,6 +1493,7 @@ static int si476x_radio_probe(struct platform_device *pdev) return 0; exit: v4l2_ctrl_handler_free(radio->videodev.ctrl_handler); + v4l2_device_unregister(&radio->v4l2dev); return rval; } diff --git a/drivers/media/radio/radio-tea5764.c b/drivers/media/radio/radio-tea5764.c index 156cca2866aa..d9547f625e30 100644 --- a/drivers/media/radio/radio-tea5764.c +++ b/drivers/media/radio/radio-tea5764.c @@ -502,7 +502,7 @@ static void tea5764_i2c_remove(struct i2c_client *client) /* I2C subsystem interface */ static const struct i2c_device_id tea5764_id[] = { - { "radio-tea5764" }, + { .name = "radio-tea5764" }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(i2c, tea5764_id); diff --git a/drivers/media/radio/saa7706h.c b/drivers/media/radio/saa7706h.c index 9572a866defb..bd8bd295a9ec 100644 --- a/drivers/media/radio/saa7706h.c +++ b/drivers/media/radio/saa7706h.c @@ -395,8 +395,8 @@ static void saa7706h_remove(struct i2c_client *client) } static const struct i2c_device_id saa7706h_id[] = { - { DRIVER_NAME }, - {} + { .name = DRIVER_NAME }, + { } }; MODULE_DEVICE_TABLE(i2c, saa7706h_id); diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c index 3932a449a1b1..a1e570af9c59 100644 --- a/drivers/media/radio/si470x/radio-si470x-i2c.c +++ b/drivers/media/radio/si470x/radio-si470x-i2c.c @@ -28,7 +28,7 @@ /* I2C Device ID List */ static const struct i2c_device_id si470x_i2c_id[] = { /* Generic Entry */ - { "si470x" }, + { .name = "si470x" }, /* Terminating entry */ { } }; diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c index 318b5f6d4202..869b1e7e34b9 100644 --- a/drivers/media/radio/si470x/radio-si470x-usb.c +++ b/drivers/media/radio/si470x/radio-si470x-usb.c @@ -565,8 +565,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, { struct si470x_device *radio; struct usb_host_interface *iface_desc; - struct usb_endpoint_descriptor *endpoint; - int i, int_end_size, retval; + int int_end_size, retval; unsigned char version_warning = 0; /* private data allocation and initialization */ @@ -595,12 +594,8 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, iface_desc = intf->cur_altsetting; /* Set up interrupt endpoint information. */ - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - endpoint = &iface_desc->endpoint[i].desc; - if (usb_endpoint_is_int_in(endpoint)) - radio->int_in_endpoint = endpoint; - } - if (!radio->int_in_endpoint) { + retval = usb_find_int_in_endpoint(iface_desc, &radio->int_in_endpoint); + if (retval) { dev_info(&intf->dev, "could not find interrupt in endpoint\n"); retval = -EIO; goto err_usbbuf; diff --git a/drivers/media/radio/si4713/si4713.c b/drivers/media/radio/si4713/si4713.c index e71272c6de37..0c0354566b0a 100644 --- a/drivers/media/radio/si4713/si4713.c +++ b/drivers/media/radio/si4713/si4713.c @@ -1639,7 +1639,7 @@ static void si4713_remove(struct i2c_client *client) /* si4713_i2c_driver - i2c driver interface */ static const struct i2c_device_id si4713_id[] = { - { "si4713" }, + { .name = "si4713" }, { } }; MODULE_DEVICE_TABLE(i2c, si4713_id); diff --git a/drivers/media/radio/tef6862.c b/drivers/media/radio/tef6862.c index 3a6d7926e856..2596d7f4f3f1 100644 --- a/drivers/media/radio/tef6862.c +++ b/drivers/media/radio/tef6862.c @@ -173,8 +173,8 @@ static void tef6862_remove(struct i2c_client *client) } static const struct i2c_device_id tef6862_id[] = { - { DRIVER_NAME }, - {} + { .name = DRIVER_NAME }, + { } }; MODULE_DEVICE_TABLE(i2c, tef6862_id); diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index 9bb27ba8240f..049a73b5f882 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -290,6 +290,10 @@ static const struct imon_usb_dev_descr imon_OEM_VFD = { { 0x000100000000ffeell, KEY_VOLUMEUP }, { 0x010000000000ffeell, KEY_VOLUMEDOWN }, { 0x000000000100ffeell, KEY_MUTE }, + /* iMON VFD HID OEM v1.2 */ + { 0x000000000a00ffeell, KEY_VOLUMEUP }, + { 0x000000000b00ffeell, KEY_VOLUMEDOWN }, + { 0x000000000c00ffeell, KEY_MUTE }, /* 0xffdc iMON MCE VFD */ { 0x00010000ffffffeell, KEY_VOLUMEUP }, { 0x01000000ffffffeell, KEY_VOLUMEDOWN }, diff --git a/drivers/media/rc/imon_raw.c b/drivers/media/rc/imon_raw.c index 3a526dea6532..295acd6ba9e1 100644 --- a/drivers/media/rc/imon_raw.c +++ b/drivers/media/rc/imon_raw.c @@ -105,26 +105,16 @@ static void imon_ir_rx(struct urb *urb) static int imon_probe(struct usb_interface *intf, const struct usb_device_id *id) { - struct usb_endpoint_descriptor *ir_ep = NULL; - struct usb_host_interface *idesc; + struct usb_endpoint_descriptor *ir_ep; struct usb_device *udev; struct rc_dev *rcdev; struct imon *imon; - int i, ret; + int ret; udev = interface_to_usbdev(intf); - idesc = intf->cur_altsetting; - - for (i = 0; i < idesc->desc.bNumEndpoints; i++) { - struct usb_endpoint_descriptor *ep = &idesc->endpoint[i].desc; - - if (usb_endpoint_is_int_in(ep)) { - ir_ep = ep; - break; - } - } - if (!ir_ep) { + ret = usb_find_int_in_endpoint(intf->cur_altsetting, &ir_ep); + if (ret) { dev_err(&intf->dev, "IR endpoint missing"); return -ENODEV; } diff --git a/drivers/media/rc/ir_toy.c b/drivers/media/rc/ir_toy.c index 089833e41178..ee645882dd5a 100644 --- a/drivers/media/rc/ir_toy.c +++ b/drivers/media/rc/ir_toy.c @@ -393,27 +393,15 @@ static int irtoy_probe(struct usb_interface *intf, { struct usb_host_interface *idesc = intf->cur_altsetting; struct usb_device *usbdev = interface_to_usbdev(intf); - struct usb_endpoint_descriptor *ep_in = NULL; - struct usb_endpoint_descriptor *ep_out = NULL; - struct usb_endpoint_descriptor *ep = NULL; + struct usb_endpoint_descriptor *ep_in, *ep_out; struct irtoy *irtoy; struct rc_dev *rc; struct urb *urb; - int i, pipe, err = -ENOMEM; + int pipe, err; - for (i = 0; i < idesc->desc.bNumEndpoints; i++) { - ep = &idesc->endpoint[i].desc; - - if (!ep_in && usb_endpoint_is_bulk_in(ep) && - usb_endpoint_maxp(ep) == MAX_PACKET) - ep_in = ep; - - if (!ep_out && usb_endpoint_is_bulk_out(ep) && - usb_endpoint_maxp(ep) == MAX_PACKET) - ep_out = ep; - } - - if (!ep_in || !ep_out) { + err = usb_find_common_endpoints(idesc, &ep_in, &ep_out, NULL, NULL); + if (err || usb_endpoint_maxp(ep_in) != MAX_PACKET || + usb_endpoint_maxp(ep_out) != MAX_PACKET) { dev_err(&intf->dev, "required endpoints not found\n"); return -ENODEV; } @@ -422,6 +410,7 @@ static int irtoy_probe(struct usb_interface *intf, if (!irtoy) return -ENOMEM; + err = -ENOMEM; irtoy->in = kmalloc(MAX_PACKET, GFP_KERNEL); if (!irtoy->in) goto free_irtoy; diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 6a9e4382a224..39ba7f6a2549 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -397,6 +397,8 @@ static const struct usb_device_id mceusb_dev_table[] = { { USB_DEVICE(VENDOR_COMPRO, 0x3082) }, /* Northstar Systems, Inc. eHome Infrared Transceiver */ { USB_DEVICE(VENDOR_NORTHSTAR, 0xe004) }, + /* Northstar Systems, Inc. eHome Infrared Transceiver - variant */ + { USB_DEVICE(VENDOR_NORTHSTAR, 0xe033) }, /* TiVo PC IR Receiver */ { USB_DEVICE(VENDOR_TIVO, 0x2000), .driver_info = TIVO_KIT }, diff --git a/drivers/media/test-drivers/vidtv/vidtv_bridge.c b/drivers/media/test-drivers/vidtv/vidtv_bridge.c index a8a76434989c..fd69b4ee16f4 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_bridge.c +++ b/drivers/media/test-drivers/vidtv/vidtv_bridge.c @@ -594,8 +594,10 @@ static int __init vidtv_bridge_init(void) int ret; ret = platform_device_register(&vidtv_bridge_dev); - if (ret) + if (ret) { + platform_device_put(&vidtv_bridge_dev); return ret; + } ret = platform_driver_register(&vidtv_bridge_driver); if (ret) diff --git a/drivers/media/test-drivers/vidtv/vidtv_demod.c b/drivers/media/test-drivers/vidtv/vidtv_demod.c index c382e9e94c32..6e5fe402976b 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_demod.c +++ b/drivers/media/test-drivers/vidtv/vidtv_demod.c @@ -407,8 +407,8 @@ static const struct dvb_frontend_ops vidtv_demod_ops = { }; static const struct i2c_device_id vidtv_demod_i2c_id_table[] = { - { "dvb_vidtv_demod" }, - {} + { .name = "dvb_vidtv_demod" }, + { } }; MODULE_DEVICE_TABLE(i2c, vidtv_demod_i2c_id_table); diff --git a/drivers/media/test-drivers/vidtv/vidtv_mux.c b/drivers/media/test-drivers/vidtv/vidtv_mux.c index f0134e38a1fb..ea2ea53102f9 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_mux.c +++ b/drivers/media/test-drivers/vidtv/vidtv_mux.c @@ -101,7 +101,8 @@ static int vidtv_mux_pid_ctx_init(struct vidtv_mux *m) /* add a ctx for all PMT sections */ while (p) { pid = vidtv_psi_get_pat_program_pid(p); - vidtv_mux_create_pid_ctx_once(m, pid); + if (!vidtv_mux_create_pid_ctx_once(m, pid)) + goto free; p = p->next; } @@ -170,6 +171,9 @@ static u32 vidtv_mux_push_si(struct vidtv_mux *m) nit_ctx = vidtv_mux_get_pid_ctx(m, VIDTV_NIT_PID); eit_ctx = vidtv_mux_get_pid_ctx(m, VIDTV_EIT_PID); + if (!pat_ctx || !sdt_ctx || !nit_ctx || !eit_ctx) + return 0; + pat_args.offset = m->mux_buf_offset; pat_args.continuity_counter = &pat_ctx->cc; @@ -186,6 +190,8 @@ static u32 vidtv_mux_push_si(struct vidtv_mux *m) } pmt_ctx = vidtv_mux_get_pid_ctx(m, pmt_pid); + if (!pmt_ctx) + continue; pmt_args.offset = m->mux_buf_offset; pmt_args.pmt = m->si.pmt_secs[i]; diff --git a/drivers/media/test-drivers/vidtv/vidtv_tuner.c b/drivers/media/test-drivers/vidtv/vidtv_tuner.c index ee55df4029bc..bd50b86e927c 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_tuner.c +++ b/drivers/media/test-drivers/vidtv/vidtv_tuner.c @@ -385,8 +385,8 @@ static const struct dvb_tuner_ops vidtv_tuner_ops = { }; static const struct i2c_device_id vidtv_tuner_i2c_id_table[] = { - { "dvb_vidtv_tuner" }, - {} + { .name = "dvb_vidtv_tuner" }, + { } }; MODULE_DEVICE_TABLE(i2c, vidtv_tuner_i2c_id_table); diff --git a/drivers/media/test-drivers/vimc/vimc-core.c b/drivers/media/test-drivers/vimc/vimc-core.c index 15167e127461..fee0c7a09c4f 100644 --- a/drivers/media/test-drivers/vimc/vimc-core.c +++ b/drivers/media/test-drivers/vimc/vimc-core.c @@ -421,6 +421,7 @@ static int __init vimc_init(void) if (ret) { dev_err(&vimc_pdev.dev, "platform device registration failed (err=%d)\n", ret); + platform_device_put(&vimc_pdev); return ret; } diff --git a/drivers/media/test-drivers/visl/visl-core.c b/drivers/media/test-drivers/visl/visl-core.c index 127ab18bce99..5e9bdd425d4a 100644 --- a/drivers/media/test-drivers/visl/visl-core.c +++ b/drivers/media/test-drivers/visl/visl-core.c @@ -339,6 +339,10 @@ static int visl_open(struct file *file) } ctx->tpg_str_buf = kzalloc(TPG_STR_BUF_SZ, GFP_KERNEL); + if (!ctx->tpg_str_buf) { + rc = -ENOMEM; + goto free_ctx; + } v4l2_fh_init(&ctx->fh, video_devdata(file)); ctx->dev = dev; diff --git a/drivers/media/test-drivers/vivid/vivid-core.c b/drivers/media/test-drivers/vivid/vivid-core.c index c8bf9b4d406c..62cfb5feb2cf 100644 --- a/drivers/media/test-drivers/vivid/vivid-core.c +++ b/drivers/media/test-drivers/vivid/vivid-core.c @@ -2289,8 +2289,10 @@ static int __init vivid_init(void) } } ret = platform_device_register(&vivid_pdev); - if (ret) + if (ret) { + platform_device_put(&vivid_pdev); goto free_output_strings; + } ret = platform_driver_register(&vivid_pdrv); if (ret) goto unreg_device; @@ -2311,7 +2313,7 @@ static int __init vivid_init(void) destroy_hdmi_wq: destroy_workqueue(update_hdmi_ctrls_workqueue); unreg_driver: - platform_driver_register(&vivid_pdrv); + platform_driver_unregister(&vivid_pdrv); unreg_device: platform_device_unregister(&vivid_pdev); free_output_strings: diff --git a/drivers/media/test-drivers/vivid/vivid-ctrls.c b/drivers/media/test-drivers/vivid/vivid-ctrls.c index f94c15ff84f7..a8a134b36720 100644 --- a/drivers/media/test-drivers/vivid/vivid-ctrls.c +++ b/drivers/media/test-drivers/vivid/vivid-ctrls.c @@ -609,17 +609,24 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl) break; case VIVID_CID_REDUCED_FPS: dev->reduced_fps = ctrl->val; - vivid_update_format_cap(dev, true); + if (dev->input_type[dev->input] == HDMI) + vivid_update_reduced_fps(dev); break; case VIVID_CID_HAS_CROP_CAP: + if (vb2_is_busy(&dev->vb_vid_cap_q)) + return -EBUSY; dev->has_crop_cap = ctrl->val; vivid_update_format_cap(dev, true); break; case VIVID_CID_HAS_COMPOSE_CAP: + if (vb2_is_busy(&dev->vb_vid_cap_q)) + return -EBUSY; dev->has_compose_cap = ctrl->val; vivid_update_format_cap(dev, true); break; case VIVID_CID_HAS_SCALER_CAP: + if (vb2_is_busy(&dev->vb_vid_cap_q)) + return -EBUSY; dev->has_scaler_cap = ctrl->val; vivid_update_format_cap(dev, true); break; @@ -1116,14 +1123,20 @@ static int vivid_vid_out_s_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case VIVID_CID_HAS_CROP_OUT: + if (vb2_is_busy(&dev->vb_vid_out_q)) + return -EBUSY; dev->has_crop_out = ctrl->val; vivid_update_format_out(dev); break; case VIVID_CID_HAS_COMPOSE_OUT: + if (vb2_is_busy(&dev->vb_vid_out_q)) + return -EBUSY; dev->has_compose_out = ctrl->val; vivid_update_format_out(dev); break; case VIVID_CID_HAS_SCALER_OUT: + if (vb2_is_busy(&dev->vb_vid_out_q)) + return -EBUSY; dev->has_scaler_out = ctrl->val; vivid_update_format_out(dev); break; diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.c b/drivers/media/test-drivers/vivid/vivid-vid-cap.c index b95f06a9b5ae..e20449084709 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.c @@ -364,6 +364,24 @@ static enum tpg_pixel_aspect vivid_get_pixel_aspect(const struct vivid_dev *dev) return TPG_PIXEL_ASPECT_SQUARE; } +void vivid_update_reduced_fps(struct vivid_dev *dev) +{ + struct v4l2_bt_timings *bt = &dev->dv_timings_cap[dev->input].bt; + unsigned int size = V4L2_DV_BT_FRAME_WIDTH(bt) * V4L2_DV_BT_FRAME_HEIGHT(bt); + u64 pixelclock; + + if (dev->reduced_fps && can_reduce_fps(bt)) { + pixelclock = div_u64(bt->pixelclock * 1000, 1001); + bt->flags |= V4L2_DV_FL_REDUCED_FPS; + } else { + pixelclock = bt->pixelclock; + bt->flags &= ~V4L2_DV_FL_REDUCED_FPS; + } + dev->timeperframe_vid_cap = (struct v4l2_fract) { + size / 100, (u32)pixelclock / 100 + }; +} + /* * Called whenever the format has to be reset which can occur when * changing inputs, standard, timings, etc. @@ -372,8 +390,12 @@ void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls) { struct v4l2_bt_timings *bt = &dev->dv_timings_cap[dev->input].bt; u32 dims[V4L2_CTRL_MAX_DIMS] = {}; - unsigned size; - u64 pixelclock; + + /* + * This resets the format, so must never be called while vb2_is_busy(). + */ + if (WARN_ON(vb2_is_busy(&dev->vb_vid_cap_q))) + return; switch (dev->input_type[dev->input]) { case WEBCAM: @@ -402,17 +424,7 @@ void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls) case HDMI: dev->src_rect.width = bt->width; dev->src_rect.height = bt->height; - size = V4L2_DV_BT_FRAME_WIDTH(bt) * V4L2_DV_BT_FRAME_HEIGHT(bt); - if (dev->reduced_fps && can_reduce_fps(bt)) { - pixelclock = div_u64(bt->pixelclock * 1000, 1001); - bt->flags |= V4L2_DV_FL_REDUCED_FPS; - } else { - pixelclock = bt->pixelclock; - bt->flags &= ~V4L2_DV_FL_REDUCED_FPS; - } - dev->timeperframe_vid_cap = (struct v4l2_fract) { - size / 100, (u32)pixelclock / 100 - }; + vivid_update_reduced_fps(dev); if (bt->interlaced) dev->field_cap = V4L2_FIELD_ALTERNATE; else diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.h b/drivers/media/test-drivers/vivid/vivid-vid-cap.h index 38a99f7e038e..d08a85927510 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-cap.h +++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.h @@ -9,6 +9,7 @@ #define _VIVID_VID_CAP_H_ void vivid_update_quality(struct vivid_dev *dev); +void vivid_update_reduced_fps(struct vivid_dev *dev); void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls); void vivid_update_outputs(struct vivid_dev *dev); void vivid_update_connected_outputs(struct vivid_dev *dev); diff --git a/drivers/media/test-drivers/vivid/vivid-vid-out.c b/drivers/media/test-drivers/vivid/vivid-vid-out.c index 8c037b90833e..23e1d5a189ee 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-out.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-out.c @@ -214,6 +214,12 @@ void vivid_update_format_out(struct vivid_dev *dev) unsigned size, p; u64 pixelclock; + /* + * This resets the format, so must never be called while vb2_is_busy(). + */ + if (WARN_ON(vb2_is_busy(&dev->vb_vid_out_q))) + return; + switch (dev->output_type[dev->output]) { case SVID: default: diff --git a/drivers/media/tuners/e4000.c b/drivers/media/tuners/e4000.c index b83f37a77224..94abb3715401 100644 --- a/drivers/media/tuners/e4000.c +++ b/drivers/media/tuners/e4000.c @@ -719,8 +719,8 @@ static void e4000_remove(struct i2c_client *client) } static const struct i2c_device_id e4000_id_table[] = { - { "e4000" }, - {} + { .name = "e4000" }, + { } }; MODULE_DEVICE_TABLE(i2c, e4000_id_table); diff --git a/drivers/media/tuners/fc2580.c b/drivers/media/tuners/fc2580.c index 75087d9b224f..b76fa1320f5f 100644 --- a/drivers/media/tuners/fc2580.c +++ b/drivers/media/tuners/fc2580.c @@ -600,8 +600,8 @@ static void fc2580_remove(struct i2c_client *client) } static const struct i2c_device_id fc2580_id_table[] = { - { "fc2580" }, - {} + { .name = "fc2580" }, + { } }; MODULE_DEVICE_TABLE(i2c, fc2580_id_table); diff --git a/drivers/media/tuners/m88rs6000t.c b/drivers/media/tuners/m88rs6000t.c index 0a724cdf0f6d..1addb3d229cf 100644 --- a/drivers/media/tuners/m88rs6000t.c +++ b/drivers/media/tuners/m88rs6000t.c @@ -709,8 +709,8 @@ static void m88rs6000t_remove(struct i2c_client *client) } static const struct i2c_device_id m88rs6000t_id[] = { - { "m88rs6000t" }, - {} + { .name = "m88rs6000t" }, + { } }; MODULE_DEVICE_TABLE(i2c, m88rs6000t_id); diff --git a/drivers/media/tuners/mt2060.c b/drivers/media/tuners/mt2060.c index ef3196e6bd30..c57233c7441a 100644 --- a/drivers/media/tuners/mt2060.c +++ b/drivers/media/tuners/mt2060.c @@ -514,8 +514,8 @@ static void mt2060_remove(struct i2c_client *client) } static const struct i2c_device_id mt2060_id_table[] = { - { "mt2060" }, - {} + { .name = "mt2060" }, + { } }; MODULE_DEVICE_TABLE(i2c, mt2060_id_table); diff --git a/drivers/media/tuners/mxl301rf.c b/drivers/media/tuners/mxl301rf.c index cfc78891ce03..1d84456cb19b 100644 --- a/drivers/media/tuners/mxl301rf.c +++ b/drivers/media/tuners/mxl301rf.c @@ -317,8 +317,8 @@ static void mxl301rf_remove(struct i2c_client *client) static const struct i2c_device_id mxl301rf_id[] = { - { "mxl301rf" }, - {} + { .name = "mxl301rf" }, + { } }; MODULE_DEVICE_TABLE(i2c, mxl301rf_id); diff --git a/drivers/media/tuners/qm1d1b0004.c b/drivers/media/tuners/qm1d1b0004.c index 07ad84f42c9f..59d98681e674 100644 --- a/drivers/media/tuners/qm1d1b0004.c +++ b/drivers/media/tuners/qm1d1b0004.c @@ -243,8 +243,8 @@ static void qm1d1b0004_remove(struct i2c_client *client) static const struct i2c_device_id qm1d1b0004_id[] = { - { "qm1d1b0004" }, - {} + { .name = "qm1d1b0004" }, + { } }; MODULE_DEVICE_TABLE(i2c, qm1d1b0004_id); diff --git a/drivers/media/tuners/qm1d1c0042.c b/drivers/media/tuners/qm1d1c0042.c index db60562ad698..2d19cfdb67b9 100644 --- a/drivers/media/tuners/qm1d1c0042.c +++ b/drivers/media/tuners/qm1d1c0042.c @@ -434,8 +434,8 @@ static void qm1d1c0042_remove(struct i2c_client *client) static const struct i2c_device_id qm1d1c0042_id[] = { - { "qm1d1c0042" }, - {} + { .name = "qm1d1c0042" }, + { } }; MODULE_DEVICE_TABLE(i2c, qm1d1c0042_id); diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index 4d67e347c22f..d517a91e6fbc 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -1097,11 +1097,11 @@ static void si2157_remove(struct i2c_client *client) * all SiLabs TER tuners, as the driver should auto-detect it. */ static const struct i2c_device_id si2157_id_table[] = { - {"si2157", SI2157}, - {"si2146", SI2146}, - {"si2141", SI2141}, - {"si2177", SI2177}, - {} + { .name = "si2157", .driver_data = SI2157 }, + { .name = "si2146", .driver_data = SI2146 }, + { .name = "si2141", .driver_data = SI2141 }, + { .name = "si2177", .driver_data = SI2177 }, + { } }; MODULE_DEVICE_TABLE(i2c, si2157_id_table); diff --git a/drivers/media/tuners/tda18212.c b/drivers/media/tuners/tda18212.c index 5f583c010408..18d1850691fb 100644 --- a/drivers/media/tuners/tda18212.c +++ b/drivers/media/tuners/tda18212.c @@ -254,8 +254,8 @@ static void tda18212_remove(struct i2c_client *client) } static const struct i2c_device_id tda18212_id[] = { - { "tda18212" }, - {} + { .name = "tda18212" }, + { } }; MODULE_DEVICE_TABLE(i2c, tda18212_id); diff --git a/drivers/media/tuners/tda18250.c b/drivers/media/tuners/tda18250.c index caaf5e0d0c9b..7bb945ba0989 100644 --- a/drivers/media/tuners/tda18250.c +++ b/drivers/media/tuners/tda18250.c @@ -868,8 +868,8 @@ static void tda18250_remove(struct i2c_client *client) } static const struct i2c_device_id tda18250_id_table[] = { - { "tda18250" }, - {} + { .name = "tda18250" }, + { } }; MODULE_DEVICE_TABLE(i2c, tda18250_id_table); diff --git a/drivers/media/tuners/tua9001.c b/drivers/media/tuners/tua9001.c index c0aed1b441e2..fcdbf1a0c1d8 100644 --- a/drivers/media/tuners/tua9001.c +++ b/drivers/media/tuners/tua9001.c @@ -245,8 +245,8 @@ static void tua9001_remove(struct i2c_client *client) } static const struct i2c_device_id tua9001_id_table[] = { - { "tua9001" }, - {} + { .name = "tua9001" }, + { } }; MODULE_DEVICE_TABLE(i2c, tua9001_id_table); diff --git a/drivers/media/usb/airspy/airspy.c b/drivers/media/usb/airspy/airspy.c index 8f6b721ba107..57edb42463e8 100644 --- a/drivers/media/usb/airspy/airspy.c +++ b/drivers/media/usb/airspy/airspy.c @@ -522,11 +522,13 @@ static int airspy_start_streaming(struct vb2_queue *vq, unsigned int count) dev_dbg(s->dev, "\n"); - if (!s->udev) - return -ENODEV; - mutex_lock(&s->v4l2_lock); + if (!s->udev) { + ret = -ENODEV; + goto err_clear_bit; + } + s->sequence = 0; set_bit(POWER_ON, &s->flags); diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index b75535d6abaf..69b24205bc56 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -1573,7 +1573,8 @@ static int cx231xx_init_v4l2(struct cx231xx *dev, dev->video_mode.end_point_addr, dev->video_mode.num_alt); - dev->video_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->video_mode.num_alt, GFP_KERNEL); + dev->video_mode.alt_max_pkt_size = devm_kmalloc_array(&interface->dev, 32, + dev->video_mode.num_alt, GFP_KERNEL); if (dev->video_mode.alt_max_pkt_size == NULL) return -ENOMEM; @@ -1614,7 +1615,8 @@ static int cx231xx_init_v4l2(struct cx231xx *dev, dev->vbi_mode.num_alt); /* compute alternate max packet sizes for vbi */ - dev->vbi_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->vbi_mode.num_alt, GFP_KERNEL); + dev->vbi_mode.alt_max_pkt_size = devm_kmalloc_array(&interface->dev, 32, + dev->vbi_mode.num_alt, GFP_KERNEL); if (dev->vbi_mode.alt_max_pkt_size == NULL) return -ENOMEM; @@ -1656,7 +1658,9 @@ static int cx231xx_init_v4l2(struct cx231xx *dev, "sliced CC EndPoint Addr 0x%x, Alternate settings: %i\n", dev->sliced_cc_mode.end_point_addr, dev->sliced_cc_mode.num_alt); - dev->sliced_cc_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->sliced_cc_mode.num_alt, GFP_KERNEL); + dev->sliced_cc_mode.alt_max_pkt_size = devm_kmalloc_array(&interface->dev, 32, + dev->sliced_cc_mode.num_alt, + GFP_KERNEL); if (dev->sliced_cc_mode.alt_max_pkt_size == NULL) return -ENOMEM; @@ -1720,7 +1724,7 @@ static int cx231xx_usb_probe(struct usb_interface *interface, udev = interface_to_usbdev(interface); /* allocate memory for our device state and initialize it */ - dev = devm_kzalloc(&udev->dev, sizeof(*dev), GFP_KERNEL); + dev = devm_kzalloc(&interface->dev, sizeof(*dev), GFP_KERNEL); if (dev == NULL) { retval = -ENOMEM; goto err_if; @@ -1850,7 +1854,9 @@ static int cx231xx_usb_probe(struct usb_interface *interface, dev->ts1_mode.end_point_addr, dev->ts1_mode.num_alt); - dev->ts1_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->ts1_mode.num_alt, GFP_KERNEL); + dev->ts1_mode.alt_max_pkt_size = devm_kmalloc_array(&interface->dev, 32, + dev->ts1_mode.num_alt, + GFP_KERNEL); if (dev->ts1_mode.alt_max_pkt_size == NULL) { retval = -ENOMEM; goto err_video_alt; diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 4a0ce9c5ee4b..da0422c65e5f 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1248,8 +1248,10 @@ int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count) dev->max_pkt_size, dev->packet_multiplier, em28xx_urb_data_copy); - if (rc < 0) + if (rc < 0) { + res_free(dev, vq->type); return rc; + } /* * djh: it's not clear whether this code is still needed. I'm diff --git a/drivers/media/usb/go7007/s2250-board.c b/drivers/media/usb/go7007/s2250-board.c index 567f851d5896..0901d79e827d 100644 --- a/drivers/media/usb/go7007/s2250-board.c +++ b/drivers/media/usb/go7007/s2250-board.c @@ -611,7 +611,7 @@ static void s2250_remove(struct i2c_client *client) } static const struct i2c_device_id s2250_id[] = { - { "s2250" }, + { .name = "s2250" }, { } }; MODULE_DEVICE_TABLE(i2c, s2250_id); diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c index f3d3f441c851..594d73e50b9f 100644 --- a/drivers/media/usb/gspca/gspca.c +++ b/drivers/media/usb/gspca/gspca.c @@ -208,22 +208,17 @@ error: static void gspca_input_create_urb(struct gspca_dev *gspca_dev) { struct usb_interface *intf; - struct usb_host_interface *intf_desc; struct usb_endpoint_descriptor *ep; - int i; + int ret; if (gspca_dev->sd_desc->int_pkt_scan) { intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface); - intf_desc = intf->cur_altsetting; - for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) { - ep = &intf_desc->endpoint[i].desc; - if (usb_endpoint_dir_in(ep) && - usb_endpoint_xfer_int(ep)) { - alloc_and_submit_int_urb(gspca_dev, ep); - break; - } - } + ret = usb_find_int_in_endpoint(intf->cur_altsetting, &ep); + if (ret) + return; + + alloc_and_submit_int_urb(gspca_dev, ep); } } diff --git a/drivers/media/usb/gspca/sonixb.c b/drivers/media/usb/gspca/sonixb.c index 4d655e2da9cb..f251a886024f 100644 --- a/drivers/media/usb/gspca/sonixb.c +++ b/drivers/media/usb/gspca/sonixb.c @@ -943,7 +943,7 @@ static int sd_config(struct gspca_dev *gspca_dev, /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { - const __u8 stop = 0x09; /* Disable stream turn of LED */ + const __u8 stop = 0x09; /* Disable stream, turn off LED */ reg_w(gspca_dev, 0x01, &stop, 1); diff --git a/drivers/media/usb/gspca/touptek.c b/drivers/media/usb/gspca/touptek.c index dde311c25d9b..734644d928ab 100644 --- a/drivers/media/usb/gspca/touptek.c +++ b/drivers/media/usb/gspca/touptek.c @@ -709,19 +709,4 @@ static struct usb_driver sd_driver = { #endif }; -static int __init sd_mod_init(void) -{ - int ret; - - ret = usb_register(&sd_driver); - if (ret < 0) - return ret; - return 0; -} -static void __exit sd_mod_exit(void) -{ - usb_deregister(&sd_driver); -} - -module_init(sd_mod_init); -module_exit(sd_mod_exit); +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/hdpvr/hdpvr-core.c b/drivers/media/usb/hdpvr/hdpvr-core.c index d42336836b18..849a2be416bd 100644 --- a/drivers/media/usb/hdpvr/hdpvr-core.c +++ b/drivers/media/usb/hdpvr/hdpvr-core.c @@ -265,13 +265,10 @@ static int hdpvr_probe(struct usb_interface *interface, const struct usb_device_id *id) { struct hdpvr_device *dev; - struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; #if IS_ENABLED(CONFIG_I2C) struct i2c_client *client; #endif - size_t buffer_size; - int i; int dev_num; int retval = -ENOMEM; @@ -321,25 +318,18 @@ static int hdpvr_probe(struct usb_interface *interface, /* set up the endpoint information */ /* use only the first bulk-in and bulk-out endpoints */ - iface_desc = interface->cur_altsetting; - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - endpoint = &iface_desc->endpoint[i].desc; - - if (!dev->bulk_in_endpointAddr && - usb_endpoint_is_bulk_in(endpoint)) { - /* USB interface description is buggy, reported max - * packet size is 512 bytes, windows driver uses 8192 */ - buffer_size = 8192; - dev->bulk_in_size = buffer_size; - dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; - } - - } - if (!dev->bulk_in_endpointAddr) { + if (usb_find_bulk_in_endpoint(interface->cur_altsetting, &endpoint)) { v4l2_err(&dev->v4l2_dev, "Could not find bulk-in endpoint\n"); goto error_put_usb; } + /* + * USB interface description is buggy, reported max packet size is 512 + * bytes, windows driver uses 8192 + */ + dev->bulk_in_size = 8192; + dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; + /* init the device */ if (hdpvr_device_init(dev)) { v4l2_err(&dev->v4l2_dev, "device init failed\n"); diff --git a/drivers/media/usb/msi2500/msi2500.c b/drivers/media/usb/msi2500/msi2500.c index 5c512d61dc1b..282256ab812a 100644 --- a/drivers/media/usb/msi2500/msi2500.c +++ b/drivers/media/usb/msi2500/msi2500.c @@ -541,7 +541,8 @@ static int msi2500_isoc_init(struct msi2500_dev *dev) } /* Must be called with vb_queue_lock hold */ -static void msi2500_cleanup_queued_bufs(struct msi2500_dev *dev) +static void msi2500_cleanup_queued_bufs(struct msi2500_dev *dev, + enum vb2_buffer_state state) { unsigned long flags; @@ -554,7 +555,7 @@ static void msi2500_cleanup_queued_bufs(struct msi2500_dev *dev) buf = list_entry(dev->queued_bufs.next, struct msi2500_frame_buf, list); list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, state); } spin_unlock_irqrestore(&dev->queued_bufs_lock, flags); } @@ -831,25 +832,40 @@ static int msi2500_start_streaming(struct vb2_queue *vq, unsigned int count) dev_dbg(dev->dev, "\n"); - if (!dev->udev) - return -ENODEV; + if (!dev->udev) { + ret = -ENODEV; + goto err_cleanup; + } - if (mutex_lock_interruptible(&dev->v4l2_lock)) - return -ERESTARTSYS; + if (mutex_lock_interruptible(&dev->v4l2_lock)) { + ret = -ERESTARTSYS; + goto err_cleanup; + } /* wake-up tuner */ v4l2_subdev_call(dev->v4l2_subdev, core, s_power, 1); ret = msi2500_set_usb_adc(dev); + if (ret) + goto err_unlock_cleanup; ret = msi2500_isoc_init(dev); if (ret) - msi2500_cleanup_queued_bufs(dev); + goto err_unlock_cleanup; ret = msi2500_ctrl_msg(dev, CMD_START_STREAMING, 0); + if (ret) + goto err_isoc_cleanup; mutex_unlock(&dev->v4l2_lock); + return 0; +err_isoc_cleanup: + msi2500_isoc_cleanup(dev); +err_unlock_cleanup: + mutex_unlock(&dev->v4l2_lock); +err_cleanup: + msi2500_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED); return ret; } @@ -864,7 +880,7 @@ static void msi2500_stop_streaming(struct vb2_queue *vq) if (dev->udev) msi2500_isoc_cleanup(dev); - msi2500_cleanup_queued_bufs(dev); + msi2500_cleanup_queued_bufs(dev, VB2_BUF_STATE_ERROR); /* according to tests, at least 700us delay is required */ msleep(20); diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index c416e2fc5754..e2884b04d952 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -710,11 +710,15 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) struct pwc_device *pdev = vb2_get_drv_priv(vq); int r; - if (!pdev->udev) + if (!pdev->udev) { + pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_QUEUED); return -ENODEV; + } - if (mutex_lock_interruptible(&pdev->v4l2_lock)) + if (mutex_lock_interruptible(&pdev->v4l2_lock)) { + pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_QUEUED); return -ERESTARTSYS; + } /* Turn on camera and set LEDS on */ pwc_camera_power(pdev, 1); pwc_set_leds(pdev, leds[0], leds[1]); @@ -726,6 +730,11 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) pwc_camera_power(pdev, 0); /* And cleanup any queued bufs!! */ pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_QUEUED); + if (pdev->fill_buf) { + vb2_buffer_done(&pdev->fill_buf->vb.vb2_buf, + VB2_BUF_STATE_QUEUED); + pdev->fill_buf = NULL; + } } mutex_unlock(&pdev->v4l2_lock); diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index 2c02873d09b5..0b8182edf8e4 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -2240,18 +2240,14 @@ static int s2255_probe(struct usb_interface *interface, iface_desc = interface->cur_altsetting; dev_dbg(&interface->dev, "num EP: %d\n", iface_desc->desc.bNumEndpoints); - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - endpoint = &iface_desc->endpoint[i].desc; - if (!dev->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) { - /* we found the bulk in endpoint */ - dev->read_endpoint = endpoint->bEndpointAddress; - } - } - if (!dev->read_endpoint) { + if (usb_find_bulk_in_endpoint(iface_desc, &endpoint)) { dev_err(&interface->dev, "Could not find bulk-in endpoint\n"); goto errorEP; } + + dev->read_endpoint = endpoint->bEndpointAddress; + timer_setup(&dev->timer, s2255_timer, 0); init_waitqueue_head(&dev->fw_data->wait_fw); for (i = 0; i < MAX_CHANNELS; i++) { diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index b9a3d9257a11..3ca108b83f1d 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -385,6 +385,99 @@ static const struct uvc_control_info uvc_ctrls[] = { | UVC_CTRL_FLAG_GET_RANGE | UVC_CTRL_FLAG_RESTORE, }, + /* + * Allows the control of pan/tilt motor movements for camera models + * that support mechanical pan/tilt. + * + * Bits 0 to 15 control pan, bits 16 to 31 control tilt. + * The unit of the pan/tilt values is 1/64th of a degree and the + * resolution is 1 degree. + */ + { + .entity = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1, + .selector = 1, + .index = 0, + .size = 4, + .flags = UVC_CTRL_FLAG_GET_DEF + | UVC_CTRL_FLAG_GET_MAX + | UVC_CTRL_FLAG_GET_MIN + | UVC_CTRL_FLAG_SET_CUR, + }, + /* + * Reset the pan/tilt motors to their original position for camera + * models that support mechanical pan/tilt. + * + * Setting bit 0 resets the pan position. + * Setting bit 1 resets the tilt position. + * + * Both bits can be set at the same time to reset both, pan and tilt, + * at the same time. + */ + { + .entity = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1, + .selector = 2, + .index = 1, + .size = 1, + .flags = UVC_CTRL_FLAG_GET_DEF + | UVC_CTRL_FLAG_GET_MAX + | UVC_CTRL_FLAG_GET_MIN + | UVC_CTRL_FLAG_SET_CUR, + }, + /* + * Allows the control of focus motor movements for camera models that + * support mechanical focus. + * + * Bits 0 to 7 allow selection of the desired lens position. + * There are no physical units, instead, the focus range is spread over + * 256 logical units with 0 representing infinity focus and 255 being + * macro focus. + */ + { + .entity = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1, + .selector = 3, + .index = 2, + .size = 6, + .flags = UVC_CTRL_FLAG_GET_CUR + | UVC_CTRL_FLAG_GET_DEF + | UVC_CTRL_FLAG_GET_MAX + | UVC_CTRL_FLAG_GET_MIN + | UVC_CTRL_FLAG_SET_CUR, + }, + /* + * Allows the control of pan/tilt motor movements for camera models + * that support mechanical pan/tilt. + * + * Bits 0 to 15 control pan, bits 16 to 31 control tilt. + */ + { + .entity = UVC_GUID_LOGITECH_PERIPHERAL, + .selector = 1, + .index = 0, + .size = 4, + .flags = UVC_CTRL_FLAG_GET_DEF + | UVC_CTRL_FLAG_GET_MAX + | UVC_CTRL_FLAG_GET_MIN + | UVC_CTRL_FLAG_GET_RES + | UVC_CTRL_FLAG_SET_CUR, + }, + /* + * Reset the pan/tilt motors to their original position for camera + * models that support mechanical pan/tilt. + * + * Setting bit 0 resets the pan position. + * Setting bit 1 resets the tilt position. + */ + { + .entity = UVC_GUID_LOGITECH_PERIPHERAL, + .selector = 2, + .index = 1, + .size = 1, + .flags = UVC_CTRL_FLAG_GET_DEF + | UVC_CTRL_FLAG_GET_MAX + | UVC_CTRL_FLAG_GET_MIN + | UVC_CTRL_FLAG_GET_RES + | UVC_CTRL_FLAG_SET_CUR, + }, }; static const u32 uvc_control_classes[] = { @@ -1009,6 +1102,87 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = { .menu_mask = BIT(V4L2_COLORFX_VIVID) | BIT(V4L2_COLORFX_NONE), }, + { + .id = V4L2_CID_PAN_RELATIVE, + .entity = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1, + .selector = 1, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + }, + { + .id = V4L2_CID_TILT_RELATIVE, + .entity = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1, + .selector = 1, + .size = 16, + .offset = 16, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + }, + { + .id = V4L2_CID_PAN_RESET, + .entity = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1, + .selector = 2, + .size = 1, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_BUTTON, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_TILT_RESET, + .entity = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1, + .selector = 2, + .size = 1, + .offset = 1, + .v4l2_type = V4L2_CTRL_TYPE_BUTTON, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_PAN_RELATIVE, + .entity = UVC_GUID_LOGITECH_PERIPHERAL, + .selector = 1, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + }, + { + .id = V4L2_CID_TILT_RELATIVE, + .entity = UVC_GUID_LOGITECH_PERIPHERAL, + .selector = 1, + .size = 16, + .offset = 16, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + }, + { + .id = V4L2_CID_PAN_RESET, + .entity = UVC_GUID_LOGITECH_PERIPHERAL, + .selector = 2, + .size = 1, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_BUTTON, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_TILT_RESET, + .entity = UVC_GUID_LOGITECH_PERIPHERAL, + .selector = 2, + .size = 1, + .offset = 1, + .v4l2_type = V4L2_CTRL_TYPE_BUTTON, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_FOCUS_ABSOLUTE, + .entity = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1, + .selector = 3, + .size = 8, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, }; /* ------------------------------------------------------------------------ @@ -2827,6 +3001,35 @@ static int uvc_ctrl_init_xu_ctrl(struct uvc_device *dev, return ret; } +bool uvc_ctrl_is_privacy_control(u8 entity[16], u8 selector) +{ + /* + * This list is not exhaustive, it is a best effort to block access to + * non documented controls that can affect user's privacy. + */ + struct privacy_control { + u8 entity[16]; + u8 selector; + } privacy_control[] = { + { + .entity = UVC_GUID_LOGITECH_USER_HW_CONTROL_V1, + .selector = 1, + }, + { + .entity = UVC_GUID_LOGITECH_PERIPHERAL, + .selector = 9, + }, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(privacy_control); i++) + if (!memcmp(entity, privacy_control[i].entity, 16) && + selector == privacy_control[i].selector) + return true; + + return false; +} + int uvc_xu_ctrl_query(struct uvc_video_chain *chain, struct uvc_xu_control_query *xqry) { @@ -2871,6 +3074,15 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain, return -ENOENT; } + if (uvc_ctrl_is_privacy_control(entity->guid, xqry->selector) && + !uvc_allow_privacy_override_param) { + dev_warn_once(&chain->dev->intf->dev, + "Privacy related controls can only be accessed if module parameter allow_privacy_override is true\n"); + uvc_dbg(chain->dev, CONTROL, "Blocking access to privacy related Control %pUl/%u\n", + entity->guid, xqry->selector); + return -EACCES; + } + if (mutex_lock_interruptible(&chain->ctrl_mutex)) return -ERESTARTSYS; diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 31b4ac3b48c1..e289cc71ba98 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -36,6 +36,7 @@ unsigned int uvc_no_drop_param = 1; static unsigned int uvc_quirks_param = -1; unsigned int uvc_dbg_param; unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT; +bool uvc_allow_privacy_override_param; static struct usb_driver uvc_driver; @@ -2504,6 +2505,9 @@ module_param_named(trace, uvc_dbg_param, uint, 0644); MODULE_PARM_DESC(trace, "Trace level bitmask"); module_param_named(timeout, uvc_timeout_param, uint, 0644); MODULE_PARM_DESC(timeout, "Streaming control requests timeout"); +module_param_named(allow_privacy_override, uvc_allow_privacy_override_param, bool, 0644); +MODULE_PARM_DESC(allow_privacy_override, + "Allow access to privacy related controls"); /* ------------------------------------------------------------------------ * Driver initialization and cleanup diff --git a/drivers/media/usb/uvc/uvc_status.c b/drivers/media/usb/uvc/uvc_status.c index 65f5356bebb3..b632cf5e3fe9 100644 --- a/drivers/media/usb/uvc/uvc_status.c +++ b/drivers/media/usb/uvc/uvc_status.c @@ -316,6 +316,16 @@ static int uvc_status_start(struct uvc_device *dev, gfp_t flags) if (!dev->int_urb) return 0; + /* + * If the previous uvc_status_stop() call was from the async work, + * the work may still be running. Wait for it to finish before we submit + * the urb. + */ + flush_work(&dev->async_ctrl.work); + + /* Clear the flush status if we were previously stopped. */ + smp_store_release(&dev->flush_status, false); + return usb_submit_urb(dev->int_urb, flags); } @@ -337,6 +347,15 @@ static void uvc_status_stop(struct uvc_device *dev) smp_store_release(&dev->flush_status, true); /* + * If we are called from the event work function, the URB is guaranteed + * to not be in flight as it has completed and has not been resubmitted. + * There's no need to cancel the work (which would deadlock), or to kill + * the URB. + */ + if (current_work() == &w->work) + return; + + /* * Cancel any pending asynchronous work. If any status event was queued, * process it synchronously. */ @@ -354,15 +373,6 @@ static void uvc_status_stop(struct uvc_device *dev) */ if (cancel_work_sync(&w->work)) uvc_ctrl_status_event(w->chain, w->ctrl, w->data); - - /* - * From this point, there are no events on the queue and the status URB - * is dead. No events will be queued until uvc_status_start() is called. - * The barrier is needed to make sure that flush_status is visible to - * uvc_ctrl_status_event_work() when uvc_status_start() will be called - * again. - */ - smp_store_release(&dev->flush_status, false); } int uvc_status_resume(struct uvc_device *dev) diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index cda1697204ea..644c2f1cd8e6 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -133,6 +133,13 @@ static int uvc_ioctl_xu_ctrl_map(struct uvc_video_chain *chain, return -EINVAL; } + if (uvc_ctrl_is_privacy_control(xmap->entity, xmap->selector) && + !uvc_allow_privacy_override_param) { + dev_warn_once(&chain->dev->intf->dev, + "Privacy related controls can only be mapped if module parameter allow_privacy_override is true\n"); + return -EACCES; + } + map = kzalloc_obj(*map); if (map == NULL) return -ENOMEM; @@ -1049,6 +1056,8 @@ static long uvc_ioctl_default(struct file *file, void *priv, bool valid_prio, switch (cmd) { /* Dynamic controls. */ case UVCIOC_CTRL_MAP: + pr_warn_once("uvcvideo: " DEPRECATED + "UVCIOC_CTRL_MAP ioctl will be eventually removed.\n"); return uvc_ioctl_xu_ctrl_map(chain, arg); case UVCIOC_CTRL_QUERY: @@ -1163,6 +1172,8 @@ static long uvc_v4l2_compat_ioctl32(struct file *file, switch (cmd) { case UVCIOC_CTRL_MAP32: + pr_warn_once("uvcvideo: " DEPRECATED + "UVCIOC_CTRL_MAP32 ioctl will be eventually removed.\n"); ret = uvc_v4l2_get_xu_mapping(&karg.xmap, up); if (ret) break; diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index f6c8e3223796..fc3536a4399f 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -494,6 +494,13 @@ static int uvc_commit_video(struct uvc_streaming *stream, * Clocks and timestamps */ +/* + * The accuracy of the hardware timestamping depends on having enough data to + * interpolate between the different clock domains. This value is sof cycles, + * this is, milliseconds. + */ +#define UVC_MIN_HW_TIMESTAMP_DIFF 100 + static inline ktime_t uvc_video_get_time(void) { if (uvc_clock_param == CLOCK_MONOTONIC) @@ -517,7 +524,7 @@ static void uvc_video_clock_add_sample(struct uvc_clock *clock, spin_lock_irqsave(&clock->lock, flags); - if (clock->count > 0 && clock->last_sof > sample->dev_sof) { + if (clock->count > 0 && clock->last_sof_processed > sample->dev_sof) { /* * Remove data from the circular buffer that is older than the * last SOF overflow. We only support one SOF overflow per @@ -537,6 +544,15 @@ static void uvc_video_clock_add_sample(struct uvc_clock *clock, spin_unlock_irqrestore(&clock->lock, flags); } +static inline u16 sof_diff(u16 a, u16 b) +{ + /* + * Because the result is modulo 2048 (via & 2047), we do not need a + * special case for a < b. + */ + return (a - b) & 2047; +} + static void uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, const u8 *data, int len) @@ -583,15 +599,11 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, if (!has_scr) return; - /* - * To limit the amount of data, drop SCRs with an SOF identical to the - * previous one. This filtering is also needed to support UVC 1.5, where - * all the data packets of the same frame contains the same SOF. In that - * case only the first one will match the host_sof. - */ - sample.dev_sof = get_unaligned_le16(&data[header_size - 2]); - if (sample.dev_sof == stream->clock.last_sof) + sample.dev_sof = get_unaligned_le16(&data[header_size - 2]) & 2047; + /* If the sample SOF is identical to the previous one, quit early. */ + if (stream->clock.last_sof_raw == sample.dev_sof) return; + stream->clock.last_sof_raw = sample.dev_sof; sample.dev_stc = get_unaligned_le32(&data[header_size - 6]); @@ -633,8 +645,6 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, if (stream->dev->quirks & UVC_QUIRK_INVALID_DEVICE_SOF) sample.dev_sof = sample.host_sof; - sample.host_time = uvc_video_get_time(); - /* * The UVC specification allows device implementations that can't obtain * the USB frame number to keep their own frame counters as long as they @@ -664,15 +674,30 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, } sample.dev_sof = (sample.dev_sof + stream->clock.sof_offset) & 2047; + + /* + * To limit the amount of data, drop SCRs with an SOF similar to the + * previous one. This filtering is also needed to support UVC 1.5, where + * all the data packets of the same frame contains the same SOF. In that + * case only the first one will match the host_sof. + */ + if (sof_diff(sample.dev_sof, stream->clock.last_sof_processed) <= + (UVC_MIN_HW_TIMESTAMP_DIFF / stream->clock.size)) + return; + + /* This is expensive, only do it if the sample will be added. */ + sample.host_time = uvc_video_get_time(); + uvc_video_clock_add_sample(&stream->clock, &sample); - stream->clock.last_sof = sample.dev_sof; + stream->clock.last_sof_processed = sample.dev_sof; } static void uvc_video_clock_reset(struct uvc_clock *clock) { clock->head = 0; clock->count = 0; - clock->last_sof = -1; + clock->last_sof_processed = -1; + clock->last_sof_raw = -1; clock->last_sof_overflow = -1; clock->sof_offset = -1; } @@ -833,15 +858,22 @@ void uvc_video_clock_update(struct uvc_streaming *stream, y2 += 2048 << 16; /* - * Have at least 1/4 of a second of timestamps before we - * try to do any calculation. Otherwise we do not have enough - * precision. This value was determined by running Android CTS - * on different devices. + * If the buffer is not full, we want to gather at least 1/4th of + * timestamps before using HW timestamping. We do this to avoid jitter + * on the initial frames. + * + * If the buffer is full we would use it regardless of how much data + * it represents. This could be solved with an infinite big circular + * buffer, but RAM is expensive these days, specially the infinitely + * big. * - * dev_sof runs at 1KHz, and we have a fixed point precision of - * 16 bits. + * The value of UVC_MIN_HW_TIMESTAMP_DIFF was determined by running + * Android's CTS on different devices. + * + * y1 and y2 are dev_sof with a fixed point precision of 16 bits. */ - if ((y2 - y1) < ((1000 / 4) << 16)) + if (clock->size != clock->count && + (y2 - y1) < (UVC_MIN_HW_TIMESTAMP_DIFF << 16)) goto done; y = (u64)(y2 - y1) * (1ULL << 31) + (u64)y1 * (u64)x2 @@ -1149,7 +1181,9 @@ static void uvc_video_stats_stop(struct uvc_streaming *stream) * uvc_video_decode_end will never be called with a NULL buffer. */ static int uvc_video_decode_start(struct uvc_streaming *stream, - struct uvc_buffer *buf, const u8 *data, int len) + struct uvc_buffer *buf, + struct uvc_buffer *meta_buf, + const u8 *data, int len) { u8 header_len; u8 fid; @@ -1169,6 +1203,53 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, fid = data[1] & UVC_STREAM_FID; /* + * Mark the buffer as done if we're at the beginning of a new frame. + * End of frame detection is better implemented by checking the EOF + * bit (FID bit toggling is delayed by one frame compared to the EOF + * bit), but some devices don't set the bit at end of frame (and the + * last payload can be lost anyway). We thus must check if the FID has + * been toggled. + * + * stream->last_fid is initialized to -1, and buf->bytesused to 0, + * so the first isochronous frame will never trigger an end of frame + * detection. + * + * Empty buffers (bytesused == 0) don't trigger end of frame detection + * as it doesn't make sense to return an empty buffer. This also + * avoids detecting end of frame conditions at FID toggling if the + * previous payload had the EOF bit set. + */ + if (fid != stream->last_fid && buf && buf->bytesused != 0) { + uvc_dbg(stream->dev, FRAME, + "Frame complete (FID bit toggled)\n"); + buf->state = UVC_BUF_STATE_READY; + + return -EAGAIN; + } + + /* + * Some cameras, when running two parallel streams (one MJPEG alongside + * another non-MJPEG stream), are known to lose the EOF packet for a frame. + * We can detect the end of a frame by checking for a new SOI marker, as + * the SOI always lies on the packet boundary between two frames for + * these devices. + */ + if (stream->dev->quirks & UVC_QUIRK_MJPEG_NO_EOF && + (stream->cur_format->fcc == V4L2_PIX_FMT_MJPEG || + stream->cur_format->fcc == V4L2_PIX_FMT_JPEG) && + buf && buf->bytesused != 0) { + const u8 *packet = data + header_len; + + if (len >= header_len + 2 && + packet[0] == 0xff && packet[1] == JPEG_MARKER_SOI) { + buf->state = UVC_BUF_STATE_READY; + buf->error = 1; + stream->last_fid ^= UVC_STREAM_FID; + return -EAGAIN; + } + } + + /* * Increase the sequence number regardless of any buffer states, so * that discontinuous sequence numbers always indicate lost frames. */ @@ -1176,6 +1257,19 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, stream->sequence++; if (stream->sequence) uvc_video_stats_update(stream); + + /* + * On a FID flip initialize sequence number and timestamp. + * + * The driver already takes care of injecting FID flips for + * UVC_QUIRK_STREAM_NO_FID and UVC_QUIRK_MJPEG_NO_EOF. + */ + if (buf) { + buf->buf.field = V4L2_FIELD_NONE; + buf->buf.sequence = stream->sequence; + buf->buf.vb2_buf.timestamp = + ktime_to_ns(uvc_video_get_time()); + } } uvc_video_clock_decode(stream, buf, data, len); @@ -1216,57 +1310,10 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, return -ENODATA; } - buf->buf.field = V4L2_FIELD_NONE; - buf->buf.sequence = stream->sequence; - buf->buf.vb2_buf.timestamp = ktime_to_ns(uvc_video_get_time()); - /* TODO: Handle PTS and SCR. */ buf->state = UVC_BUF_STATE_ACTIVE; - } - - /* - * Mark the buffer as done if we're at the beginning of a new frame. - * End of frame detection is better implemented by checking the EOF - * bit (FID bit toggling is delayed by one frame compared to the EOF - * bit), but some devices don't set the bit at end of frame (and the - * last payload can be lost anyway). We thus must check if the FID has - * been toggled. - * - * stream->last_fid is initialized to -1, so the first isochronous - * frame will never trigger an end of frame detection. - * - * Empty buffers (bytesused == 0) don't trigger end of frame detection - * as it doesn't make sense to return an empty buffer. This also - * avoids detecting end of frame conditions at FID toggling if the - * previous payload had the EOF bit set. - */ - if (fid != stream->last_fid && buf->bytesused != 0) { - uvc_dbg(stream->dev, FRAME, - "Frame complete (FID bit toggled)\n"); - buf->state = UVC_BUF_STATE_READY; - return -EAGAIN; - } - - /* - * Some cameras, when running two parallel streams (one MJPEG alongside - * another non-MJPEG stream), are known to lose the EOF packet for a frame. - * We can detect the end of a frame by checking for a new SOI marker, as - * the SOI always lies on the packet boundary between two frames for - * these devices. - */ - if (stream->dev->quirks & UVC_QUIRK_MJPEG_NO_EOF && - (stream->cur_format->fcc == V4L2_PIX_FMT_MJPEG || - stream->cur_format->fcc == V4L2_PIX_FMT_JPEG)) { - const u8 *packet = data + header_len; - - if (len >= header_len + 2 && - packet[0] == 0xff && packet[1] == JPEG_MARKER_SOI && - buf->bytesused != 0) { - buf->state = UVC_BUF_STATE_READY; - buf->error = 1; - stream->last_fid ^= UVC_STREAM_FID; - return -EAGAIN; - } + if (meta_buf) + meta_buf->state = UVC_BUF_STATE_ACTIVE; } stream->last_fid = fid; @@ -1424,7 +1471,7 @@ static void uvc_video_decode_meta(struct uvc_streaming *stream, ktime_t time; const u8 *scr; - if (!meta_buf || length == 2) + if (length <= 2 || !meta_buf || meta_buf->state != UVC_BUF_STATE_ACTIVE) return; has_pts = mem[1] & UVC_STREAM_PTS; @@ -1541,7 +1588,7 @@ static void uvc_video_decode_isoc(struct uvc_urb *uvc_urb, /* Decode the payload header. */ mem = urb->transfer_buffer + urb->iso_frame_desc[i].offset; do { - ret = uvc_video_decode_start(stream, buf, mem, + ret = uvc_video_decode_start(stream, buf, meta_buf, mem, urb->iso_frame_desc[i].actual_length); if (ret == -EAGAIN) uvc_video_next_buffers(stream, &buf, &meta_buf); @@ -1590,7 +1637,8 @@ static void uvc_video_decode_bulk(struct uvc_urb *uvc_urb, */ if (stream->bulk.header_size == 0 && !stream->bulk.skip_payload) { do { - ret = uvc_video_decode_start(stream, buf, mem, len); + ret = uvc_video_decode_start(stream, buf, meta_buf, mem, + len); if (ret == -EAGAIN) uvc_video_next_buffers(stream, &buf, &meta_buf); } while (ret == -EAGAIN); @@ -1693,7 +1741,6 @@ static void uvc_video_complete(struct urb *urb) struct vb2_queue *vb2_qmeta = stream->meta.queue.vdev.queue; struct uvc_buffer *buf = NULL; struct uvc_buffer *buf_meta = NULL; - unsigned long flags; int ret; switch (urb->status) { @@ -1719,13 +1766,8 @@ static void uvc_video_complete(struct urb *urb) buf = uvc_queue_get_current_buffer(queue); - if (vb2_qmeta) { - spin_lock_irqsave(&qmeta->irqlock, flags); - if (!list_empty(&qmeta->irqqueue)) - buf_meta = list_first_entry(&qmeta->irqqueue, - struct uvc_buffer, queue); - spin_unlock_irqrestore(&qmeta->irqlock, flags); - } + if (vb2_qmeta) + buf_meta = uvc_queue_get_current_buffer(qmeta); /* Re-initialise the URB async work. */ uvc_urb->async_operations = 0; diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index 0a0c01b2420f..b6bcee4a222f 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -522,7 +522,8 @@ struct uvc_streaming { unsigned int size; unsigned int last_sof_overflow; - u16 last_sof; + u16 last_sof_processed; + u16 last_sof_raw; u16 sof_offset; u8 last_scr[6]; @@ -666,6 +667,7 @@ extern unsigned int uvc_no_drop_param; extern unsigned int uvc_dbg_param; extern unsigned int uvc_timeout_param; extern unsigned int uvc_hw_timestamps_param; +extern bool uvc_allow_privacy_override_param; #define uvc_dbg(_dev, flag, fmt, ...) \ do { \ @@ -791,6 +793,7 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain, struct uvc_xu_control_query *xqry); void uvc_ctrl_cleanup_fh(struct uvc_fh *handle); +bool uvc_ctrl_is_privacy_control(u8 entity[16], u8 selector); /* Utility functions */ struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts, diff --git a/drivers/media/v4l2-core/tuner-core.c b/drivers/media/v4l2-core/tuner-core.c index 004ec4d7beea..1e130e6f903f 100644 --- a/drivers/media/v4l2-core/tuner-core.c +++ b/drivers/media/v4l2-core/tuner-core.c @@ -1401,7 +1401,7 @@ static const struct dev_pm_ops tuner_pm_ops = { }; static const struct i2c_device_id tuner_id[] = { - { "tuner", }, /* autodetect */ + { .name = "tuner" }, /* autodetect */ { } }; MODULE_DEVICE_TABLE(i2c, tuner_id); diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index 554c591e1113..65db7340ad38 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -245,32 +245,59 @@ EXPORT_SYMBOL_GPL(v4l2_s_parm_cap); const struct v4l2_format_info *v4l2_format_info(u32 format) { static const struct v4l2_format_info formats[] = { - /* RGB formats */ - { .format = V4L2_PIX_FMT_BGR24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGB24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_HSV24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_BGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_XBGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_BGRX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_XRGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGBX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_HSV32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_ARGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGBA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_ABGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_BGRA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGB565, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGB565X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_BGR666, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_BGR48_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_BGR48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGB48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_ABGR64_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGBA1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + /* RGB formats (1 or 2 bytes per pixel) */ + { .format = V4L2_PIX_FMT_RGB332, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ARGB444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, + { .format = V4L2_PIX_FMT_XRGB444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGBA444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, + { .format = V4L2_PIX_FMT_RGBX444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ABGR444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, + { .format = V4L2_PIX_FMT_XBGR444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGRA444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, + { .format = V4L2_PIX_FMT_BGRX444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ARGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, + { .format = V4L2_PIX_FMT_XRGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGBA555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, + { .format = V4L2_PIX_FMT_RGBX555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ABGR555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, + { .format = V4L2_PIX_FMT_XBGR555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGRA555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, + { .format = V4L2_PIX_FMT_BGRX555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB565, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB555X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ARGB555X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, + { .format = V4L2_PIX_FMT_XRGB555X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB565X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + + /* RGB formats (3 or 4 bytes per pixel) */ + { .format = V4L2_PIX_FMT_BGR666, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGR24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ABGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, + { .format = V4L2_PIX_FMT_XBGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGRA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, + { .format = V4L2_PIX_FMT_BGRX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGBA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, + { .format = V4L2_PIX_FMT_RGBX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ARGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, + { .format = V4L2_PIX_FMT_XRGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGBX1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_ARGB2101010, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGBA1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, + { .format = V4L2_PIX_FMT_ARGB2101010, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, + + /* RGB formats (6 or 8 bytes per pixel) */ + { .format = V4L2_PIX_FMT_BGR48_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGR48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ABGR64_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, + + /* HSV formats */ + { .format = V4L2_PIX_FMT_HSV24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_HSV32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, /* YUV packed formats */ { .format = V4L2_PIX_FMT_YUYV, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, @@ -281,6 +308,7 @@ const struct v4l2_format_info *v4l2_format_info(u32 format) { .format = V4L2_PIX_FMT_Y212, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, { .format = V4L2_PIX_FMT_Y216, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, { .format = V4L2_PIX_FMT_YUV48_12, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_YUV24, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_MT2110T, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 2, .block_w = { 16, 8, 0, 0 }, .block_h = { 32, 16, 0, 0 }}, { .format = V4L2_PIX_FMT_MT2110R, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 2, @@ -404,14 +432,33 @@ static inline unsigned int v4l2_format_block_height(const struct v4l2_format_inf } static inline unsigned int v4l2_format_plane_stride(const struct v4l2_format_info *info, int plane, - unsigned int width) + unsigned int width, unsigned int byte_alignment) { unsigned int hdiv = plane ? info->hdiv : 1; unsigned int aligned_width = ALIGN(width, v4l2_format_block_width(info, plane)); - return DIV_ROUND_UP(aligned_width, hdiv) * - info->bpp[plane] / info->bpp_div[plane]; + /* + * Formats with a single memory plane derive the stride of the + * other planes from the y stride. To avoid hardware or software + * deriving a different stride for the composite plane, + * multiply the alignment accordingly. + * + * It assumes the following format properties: + * - bpp_div[0] == bpp_div[1] + * - The multiplication factor doesn't differ between the non y planes + * - The multiplication factor is a power of 2 + */ + if (info->mem_planes == 1 && info->comp_planes > 1) { + if (plane == 0) + byte_alignment *= DIV_ROUND_UP(info->hdiv * info->bpp[0], info->bpp[1]); + else + byte_alignment *= DIV_ROUND_UP(info->bpp[1], info->hdiv * info->bpp[0]); + } + + return ALIGN(DIV_ROUND_UP(aligned_width, hdiv) * info->bpp[plane] / + info->bpp_div[plane], + byte_alignment); } static inline unsigned int v4l2_format_plane_height(const struct v4l2_format_info *info, int plane, @@ -425,9 +472,10 @@ static inline unsigned int v4l2_format_plane_height(const struct v4l2_format_inf } static inline unsigned int v4l2_format_plane_size(const struct v4l2_format_info *info, int plane, - unsigned int width, unsigned int height) + unsigned int width, unsigned int height, + u8 stride_alignment) { - return v4l2_format_plane_stride(info, plane, width) * + return v4l2_format_plane_stride(info, plane, width, stride_alignment) * v4l2_format_plane_height(info, plane, height); } @@ -448,8 +496,9 @@ void v4l2_apply_frmsize_constraints(u32 *width, u32 *height, } EXPORT_SYMBOL_GPL(v4l2_apply_frmsize_constraints); -int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt, - u32 pixelformat, u32 width, u32 height) +int v4l2_fill_pixfmt_mp_aligned(struct v4l2_pix_format_mplane *pixfmt, + u32 pixelformat, u32 width, u32 height, + u8 stride_alignment) { const struct v4l2_format_info *info; struct v4l2_plane_pix_format *plane; @@ -466,23 +515,34 @@ int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt, if (info->mem_planes == 1) { plane = &pixfmt->plane_fmt[0]; - plane->bytesperline = v4l2_format_plane_stride(info, 0, width); + plane->bytesperline = v4l2_format_plane_stride(info, 0, width, + stride_alignment); plane->sizeimage = 0; for (i = 0; i < info->comp_planes; i++) plane->sizeimage += - v4l2_format_plane_size(info, i, width, height); + v4l2_format_plane_size(info, i, width, height, + stride_alignment); } else { for (i = 0; i < info->comp_planes; i++) { plane = &pixfmt->plane_fmt[i]; plane->bytesperline = - v4l2_format_plane_stride(info, i, width); + v4l2_format_plane_stride(info, i, width, + stride_alignment); plane->sizeimage = plane->bytesperline * v4l2_format_plane_height(info, i, height); } } return 0; } +EXPORT_SYMBOL_GPL(v4l2_fill_pixfmt_mp_aligned); + +int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt, + u32 pixelformat, u32 width, u32 height) +{ + return v4l2_fill_pixfmt_mp_aligned(pixfmt, pixelformat, + width, height, 1); +} EXPORT_SYMBOL_GPL(v4l2_fill_pixfmt_mp); int v4l2_fill_pixfmt(struct v4l2_pix_format *pixfmt, u32 pixelformat, @@ -502,12 +562,12 @@ int v4l2_fill_pixfmt(struct v4l2_pix_format *pixfmt, u32 pixelformat, pixfmt->width = width; pixfmt->height = height; pixfmt->pixelformat = pixelformat; - pixfmt->bytesperline = v4l2_format_plane_stride(info, 0, width); + pixfmt->bytesperline = v4l2_format_plane_stride(info, 0, width, 1); pixfmt->sizeimage = 0; for (i = 0; i < info->comp_planes; i++) pixfmt->sizeimage += - v4l2_format_plane_size(info, i, width, height); + v4l2_format_plane_size(info, i, width, height, 1); return 0; } EXPORT_SYMBOL_GPL(v4l2_fill_pixfmt); @@ -792,14 +852,15 @@ struct clk *__devm_v4l2_sensor_clk_get(struct device *dev, const char *id, if (ret) return ERR_PTR(ret == -EINVAL ? -EPROBE_DEFER : ret); - if (!id) { + if (id) + clk_id = kasprintf(GFP_KERNEL, "clk-%s-%s", dev_name(dev), id); + else clk_id = kasprintf(GFP_KERNEL, "clk-%s", dev_name(dev)); - if (!clk_id) - return ERR_PTR(-ENOMEM); - id = clk_id; - } - clk_hw = devm_clk_hw_register_fixed_rate(dev, id, NULL, 0, rate); + if (!clk_id) + return ERR_PTR(-ENOMEM); + + clk_hw = devm_clk_hw_register_fixed_rate(dev, clk_id, NULL, 0, rate); if (IS_ERR(clk_hw)) return ERR_CAST(clk_hw); diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c index 6b375720e395..ba047d7d8601 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c @@ -971,6 +971,7 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, struct v4l2_ctrl_hevc_ext_sps_st_rps *p_hevc_st_rps; struct v4l2_ctrl_hevc_sps *p_hevc_sps; struct v4l2_ctrl_hevc_pps *p_hevc_pps; + struct v4l2_ctrl_hevc_slice_params *p_hevc_slice_params; struct v4l2_ctrl_hdr10_mastering_display *p_hdr10_mastering; struct v4l2_ctrl_hevc_decode_params *p_hevc_decode_params; struct v4l2_area *area; @@ -1260,6 +1261,18 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, break; case V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS: + p_hevc_slice_params = p; + + if (p_hevc_slice_params->num_ref_idx_l0_active_minus1 >= + V4L2_HEVC_DPB_ENTRIES_NUM_MAX) + return -EINVAL; + + if (p_hevc_slice_params->slice_type != V4L2_HEVC_SLICE_TYPE_B) + break; + + if (p_hevc_slice_params->num_ref_idx_l1_active_minus1 >= + V4L2_HEVC_DPB_ENTRIES_NUM_MAX) + return -EINVAL; break; case V4L2_CTRL_TYPE_HEVC_EXT_SPS_ST_RPS: diff --git a/drivers/media/v4l2-core/v4l2-ctrls-defs.c b/drivers/media/v4l2-core/v4l2-ctrls-defs.c index 551426c4cd01..e062f2088490 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-defs.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-defs.c @@ -889,6 +889,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY: return "Display Delay"; case V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY_ENABLE: return "Display Delay Enable"; case V4L2_CID_MPEG_VIDEO_AU_DELIMITER: return "Generate Access Unit Delimiters"; + case V4L2_CID_MPEG_VIDEO_BACKGROUND_DETECTION: return "Background Detection"; case V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP: return "H263 I-Frame QP Value"; case V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP: return "H263 P-Frame QP Value"; case V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP: return "H263 B-Frame QP Value"; @@ -1296,6 +1297,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL: case V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER: case V4L2_CID_MPEG_VIDEO_AU_DELIMITER: + case V4L2_CID_MPEG_VIDEO_BACKGROUND_DETECTION: case V4L2_CID_WIDE_DYNAMIC_RANGE: case V4L2_CID_IMAGE_STABILIZATION: case V4L2_CID_RDS_RECEPTION: diff --git a/drivers/media/v4l2-core/v4l2-ctrls-request.c b/drivers/media/v4l2-core/v4l2-ctrls-request.c index 4b7e6981b8d6..5c2fc767c6a6 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-request.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-request.c @@ -348,13 +348,12 @@ void v4l2_ctrl_request_complete(struct media_request *req, ret = v4l2_ctrl_handler_init(hdl, (main_hdl->nr_of_buckets - 1) * 8); if (!ret) ret = v4l2_ctrl_request_bind(req, hdl, main_hdl); - if (ret) { - v4l2_ctrl_handler_free(hdl); - kfree(hdl); - return; - } + if (ret) + goto error; hdl->request_is_queued = true; obj = media_request_object_find(req, &req_ops, main_hdl); + if (!obj) + goto error; } hdl = container_of(obj, struct v4l2_ctrl_handler, req_obj); @@ -389,6 +388,11 @@ void v4l2_ctrl_request_complete(struct media_request *req, mutex_unlock(main_hdl->lock); media_request_object_complete(obj); media_request_object_put(obj); + return; + +error: + v4l2_ctrl_handler_free(hdl); + kfree(hdl); } EXPORT_SYMBOL(v4l2_ctrl_request_complete); diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index 6ce623a1245a..5516b2bbb08f 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -1032,6 +1032,11 @@ int __video_register_device(struct video_device *vdev, vdev->minor = i + minor_offset; vdev->num = nr; + if (WARN_ON(vdev->minor >= VIDEO_NUM_DEVICES)) { + mutex_unlock(&videodev_lock); + return -EINVAL; + } + /* Should not happen since we thought this minor was free */ if (WARN_ON(video_devices[vdev->minor])) { mutex_unlock(&videodev_lock); diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index 77f3298821b5..62a3a452f788 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -1256,7 +1256,7 @@ v4l2_async_nf_parse_fwnode_sensor(struct device *dev, return 0; } -int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd) +int __v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd, struct module *module) { struct v4l2_async_notifier *notifier; int ret; @@ -1282,7 +1282,7 @@ int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd) if (ret < 0) goto out_cleanup; - ret = v4l2_async_register_subdev(sd); + ret = __v4l2_async_register_subdev(sd, module); if (ret < 0) goto out_unregister; @@ -1300,7 +1300,7 @@ out_cleanup: return ret; } -EXPORT_SYMBOL_GPL(v4l2_async_register_subdev_sensor); +EXPORT_SYMBOL_GPL(__v4l2_async_register_subdev_sensor); MODULE_DESCRIPTION("V4L2 fwnode binding parsing library"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 831c69c958b8..e9f81b9be9e2 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -629,6 +629,18 @@ subdev_ioctl_get_state(struct v4l2_subdev *sd, struct v4l2_subdev_fh *subdev_fh, v4l2_subdev_get_unlocked_active_state(sd); } +static void v4l2_subdev_copy_routes(struct v4l2_subdev_routing *routing, + const struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_route *routes = + (struct v4l2_subdev_route *)(uintptr_t)routing->routes; + u32 copy_routes = min(routing->len_routes, state->routing.num_routes); + + memcpy(routes, state->routing.routes, sizeof(*routes) * copy_routes); + + routing->num_routes = state->routing.num_routes; +} + static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, struct v4l2_subdev_state *state) { @@ -1000,7 +1012,6 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, case VIDIOC_SUBDEV_G_ROUTING: { struct v4l2_subdev_routing *routing = arg; - struct v4l2_subdev_krouting *krouting; if (!v4l2_subdev_enable_streams_api) return -ENOIOCTLCMD; @@ -1008,15 +1019,12 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) return -ENOIOCTLCMD; - memset(routing->reserved, 0, sizeof(routing->reserved)); + if (!client_supports_streams) + return -EINVAL; - krouting = &state->routing; + memset(routing->reserved, 0, sizeof(routing->reserved)); - memcpy((struct v4l2_subdev_route *)(uintptr_t)routing->routes, - krouting->routes, - min(krouting->num_routes, routing->len_routes) * - sizeof(*krouting->routes)); - routing->num_routes = krouting->num_routes; + v4l2_subdev_copy_routes(routing, state); return 0; } @@ -1035,6 +1043,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) return -ENOIOCTLCMD; + if (!client_supports_streams) + return -EINVAL; + if (routing->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev) return -EPERM; @@ -1084,11 +1095,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, * the routing table. */ if (!v4l2_subdev_has_op(sd, pad, set_routing)) { - memcpy((struct v4l2_subdev_route *)(uintptr_t)routing->routes, - state->routing.routes, - min(state->routing.num_routes, routing->len_routes) * - sizeof(*state->routing.routes)); - routing->num_routes = state->routing.num_routes; + v4l2_subdev_copy_routes(routing, state); return 0; } @@ -1102,11 +1109,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, if (rval < 0) return rval; - memcpy((struct v4l2_subdev_route *)(uintptr_t)routing->routes, - state->routing.routes, - min(state->routing.num_routes, routing->len_routes) * - sizeof(*state->routing.routes)); - routing->num_routes = state->routing.num_routes; + v4l2_subdev_copy_routes(routing, state); return 0; } @@ -2504,6 +2507,10 @@ int v4l2_subdev_s_stream_helper(struct v4l2_subdev *sd, int enable) u64 source_mask = 0; int pad_index = -1; + if (WARN_ON(!v4l2_subdev_has_op(sd, pad, enable_streams) || + !v4l2_subdev_has_op(sd, pad, disable_streams))) + return -ENOIOCTLCMD; + /* * Find the source pad. This helper is meant for subdevs that have a * single source pad, so failures shouldn't happen, but catch them diff --git a/drivers/platform/x86/intel/int3472/tps68470_board_data.c b/drivers/platform/x86/intel/int3472/tps68470_board_data.c index c1ddbf9a82c0..c53542465fb4 100644 --- a/drivers/platform/x86/intel/int3472/tps68470_board_data.c +++ b/drivers/platform/x86/intel/int3472/tps68470_board_data.c @@ -238,7 +238,7 @@ static struct regulator_consumer_supply ovti5675_dvdd_consumer_supplies[] = { REGULATOR_SUPPLY("dvdd", "i2c-OVTI5675:00"), }; -static const struct regulator_init_data msi_p14_ai_evo_tps68470_core_reg_init_data = { +static const struct regulator_init_data msi_prestige_ai_evo_tps68470_core_reg_init_data = { .constraints = { .min_uV = 1200000, .max_uV = 1200000, @@ -249,7 +249,7 @@ static const struct regulator_init_data msi_p14_ai_evo_tps68470_core_reg_init_da .consumer_supplies = ovti5675_dvdd_consumer_supplies, }; -static const struct regulator_init_data msi_p14_ai_evo_tps68470_ana_reg_init_data = { +static const struct regulator_init_data msi_prestige_ai_evo_tps68470_ana_reg_init_data = { .constraints = { .min_uV = 2815200, .max_uV = 2815200, @@ -260,7 +260,7 @@ static const struct regulator_init_data msi_p14_ai_evo_tps68470_ana_reg_init_dat .consumer_supplies = ovti5675_avdd_consumer_supplies, }; -static const struct regulator_init_data msi_p14_ai_evo_tps68470_vio_reg_init_data = { +static const struct regulator_init_data msi_prestige_ai_evo_tps68470_vio_reg_init_data = { .constraints = { .min_uV = 1800600, .max_uV = 1800600, @@ -269,7 +269,7 @@ static const struct regulator_init_data msi_p14_ai_evo_tps68470_vio_reg_init_dat }, }; -static const struct regulator_init_data msi_p14_ai_evo_tps68470_vsio_reg_init_data = { +static const struct regulator_init_data msi_prestige_ai_evo_tps68470_vsio_reg_init_data = { .constraints = { .min_uV = 1800600, .max_uV = 1800600, @@ -280,12 +280,92 @@ static const struct regulator_init_data msi_p14_ai_evo_tps68470_vsio_reg_init_da .consumer_supplies = ovti5675_dovdd_consumer_supplies, }; -static const struct tps68470_regulator_platform_data msi_p14_ai_evo_tps68470_pdata = { +static const struct tps68470_regulator_platform_data msi_prestige_ai_evo_tps68470_pdata = { .reg_init_data = { - [TPS68470_CORE] = &msi_p14_ai_evo_tps68470_core_reg_init_data, - [TPS68470_ANA] = &msi_p14_ai_evo_tps68470_ana_reg_init_data, - [TPS68470_VIO] = &msi_p14_ai_evo_tps68470_vio_reg_init_data, - [TPS68470_VSIO] = &msi_p14_ai_evo_tps68470_vsio_reg_init_data, + [TPS68470_CORE] = &msi_prestige_ai_evo_tps68470_core_reg_init_data, + [TPS68470_ANA] = &msi_prestige_ai_evo_tps68470_ana_reg_init_data, + [TPS68470_VIO] = &msi_prestige_ai_evo_tps68470_vio_reg_init_data, + [TPS68470_VSIO] = &msi_prestige_ai_evo_tps68470_vsio_reg_init_data, + }, +}; + +/* Settings for Intel NVL platform */ + +static struct regulator_consumer_supply ovti13b1_core_consumer_supplies[] = { + REGULATOR_SUPPLY("dvdd", "i2c-OVTI13B1:01"), +}; + +static struct regulator_consumer_supply ovti13b1_ana_consumer_supplies[] = { + REGULATOR_SUPPLY("avdd", "i2c-OVTI13B1:01"), +}; + +static struct regulator_consumer_supply ovti13b1_vcm_consumer_supplies[] = { + REGULATOR_SUPPLY("vcc", "i2c-OVTI13B1:01-VCM"), +}; + +static struct regulator_consumer_supply ovti13b1_vsio_consumer_supplies[] = { + REGULATOR_SUPPLY("dovdd", "i2c-OVTI13B1:01"), +}; + +static const struct regulator_init_data intel_nvl_tps68470_core_reg_init_data = { + .constraints = { + .min_uV = 1200000, + .max_uV = 1200000, + .apply_uV = true, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(ovti13b1_core_consumer_supplies), + .consumer_supplies = ovti13b1_core_consumer_supplies, +}; + +static const struct regulator_init_data intel_nvl_tps68470_ana_reg_init_data = { + .constraints = { + .min_uV = 2815200, + .max_uV = 2815200, + .apply_uV = true, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(ovti13b1_ana_consumer_supplies), + .consumer_supplies = ovti13b1_ana_consumer_supplies, +}; +static const struct regulator_init_data intel_nvl_tps68470_vcm_reg_init_data = { + .constraints = { + .min_uV = 2815200, + .max_uV = 2815200, + .apply_uV = true, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(ovti13b1_vcm_consumer_supplies), + .consumer_supplies = ovti13b1_vcm_consumer_supplies, +}; + +/* Ensure the always-on VIO regulator has the same voltage as VSIO */ +static const struct regulator_init_data intel_nvl_tps68470_vio_reg_init_data = { + .constraints = { + .min_uV = 1800600, + .max_uV = 1800600, + .apply_uV = true, + .always_on = true, + }, +}; +static const struct regulator_init_data intel_nvl_tps68470_vsio_reg_init_data = { + .constraints = { + .min_uV = 1800600, + .max_uV = 1800600, + .apply_uV = true, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(ovti13b1_vsio_consumer_supplies), + .consumer_supplies = ovti13b1_vsio_consumer_supplies, +}; + +static const struct tps68470_regulator_platform_data intel_nvl_tps68470_pdata = { + .reg_init_data = { + [TPS68470_CORE] = &intel_nvl_tps68470_core_reg_init_data, + [TPS68470_ANA] = &intel_nvl_tps68470_ana_reg_init_data, + [TPS68470_VCM] = &intel_nvl_tps68470_vcm_reg_init_data, + [TPS68470_VIO] = &intel_nvl_tps68470_vio_reg_init_data, + [TPS68470_VSIO] = &intel_nvl_tps68470_vsio_reg_init_data, }, }; @@ -315,7 +395,7 @@ static struct gpiod_lookup_table dell_7212_int3479_gpios = { } }; -static struct gpiod_lookup_table msi_p14_ai_evo_ovti5675_gpios = { +static struct gpiod_lookup_table msi_prestige_ai_evo_ovti5675_gpios = { .dev_id = "i2c-OVTI5675:00", .table = { GPIO_LOOKUP("tps68470-gpio", 9, "reset", GPIO_ACTIVE_LOW), @@ -323,13 +403,21 @@ static struct gpiod_lookup_table msi_p14_ai_evo_ovti5675_gpios = { } }; -static const struct property_entry msi_p14_ai_evo_gpio_props[] = { +static struct gpiod_lookup_table intel_nvl_tps68470_gpios = { + .dev_id = "i2c-OVTI13B1:01", + .table = { + GPIO_LOOKUP("tps68470-gpio", 9, "reset", GPIO_ACTIVE_LOW), + { } + } +}; + +static const struct property_entry int3472_tps68470_daisy_chain_gpio_props[] = { PROPERTY_ENTRY_BOOL("daisy-chain-enable"), { } }; -static const struct software_node msi_p14_ai_evo_tps68470_gpio_swnode = { - .properties = msi_p14_ai_evo_gpio_props, +static const struct software_node int3472_tps68470_daisy_chain_gpio_swnode = { + .properties = int3472_tps68470_daisy_chain_gpio_props, }; static const struct int3472_tps68470_board_data surface_go_tps68470_board_data = { @@ -361,13 +449,23 @@ static const struct int3472_tps68470_board_data dell_7212_tps68470_board_data = }, }; -static const struct int3472_tps68470_board_data msi_p14_ai_evo_tps68470_board_data = { +static const struct int3472_tps68470_board_data msi_prestige_ai_evo_tps68470_board_data = { .dev_name = "i2c-INT3472:06", - .tps68470_regulator_pdata = &msi_p14_ai_evo_tps68470_pdata, - .tps68470_gpio_swnode = &msi_p14_ai_evo_tps68470_gpio_swnode, + .tps68470_regulator_pdata = &msi_prestige_ai_evo_tps68470_pdata, + .tps68470_gpio_swnode = &int3472_tps68470_daisy_chain_gpio_swnode, .n_gpiod_lookups = 1, .tps68470_gpio_lookup_tables = { - &msi_p14_ai_evo_ovti5675_gpios, + &msi_prestige_ai_evo_ovti5675_gpios, + }, +}; + +static const struct int3472_tps68470_board_data intel_nvl_tps68470_board_data = { + .dev_name = "i2c-INT3472:04", + .tps68470_regulator_pdata = &intel_nvl_tps68470_pdata, + .tps68470_gpio_swnode = &int3472_tps68470_daisy_chain_gpio_swnode, + .n_gpiod_lookups = 1, + .tps68470_gpio_lookup_tables = { + &intel_nvl_tps68470_gpios, }, }; @@ -403,9 +501,33 @@ static const struct dmi_system_id int3472_tps68470_board_data_table[] = { { .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Micro-Star International Co., Ltd."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Prestige 13 AI+ Evo A2VMG"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "MS-13Q3"), + }, + .driver_data = (void *)&msi_prestige_ai_evo_tps68470_board_data, + }, + { + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Micro-Star International Co., Ltd."), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Prestige 14 AI+ Evo C2VMG"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "MS-14N3"), + }, + .driver_data = (void *)&msi_prestige_ai_evo_tps68470_board_data, + }, + { + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Micro-Star International Co., Ltd."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Prestige 16 AI+ Evo B2VMG"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "MS-15A3"), + }, + .driver_data = (void *)&msi_prestige_ai_evo_tps68470_board_data, + }, + { + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Nova Lake Client Platform"), }, - .driver_data = (void *)&msi_p14_ai_evo_tps68470_board_data, + .driver_data = (void *)&intel_nvl_tps68470_board_data, }, { } }; diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 1aa31bddf970..06cbc5c548ca 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -50,8 +50,4 @@ menuconfig STAGING_MEDIA_DEPRECATED If in doubt, say N here. -if STAGING_MEDIA_DEPRECATED -source "drivers/staging/media/deprecated/atmel/Kconfig" -endif - endif diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 6f78b0edde1e..6fd7179733d8 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -1,5 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_VIDEO_ATMEL_ISC_BASE) += deprecated/atmel/ obj-$(CONFIG_INTEL_ATOMISP) += atomisp/ obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx/ obj-$(CONFIG_VIDEO_MAX96712) += max96712/ diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c index d3414312e1de..f9cc5f45fe00 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c @@ -433,7 +433,7 @@ static int power_up(struct v4l2_subdev *sd) goto fail_power; } - msleep(5); + fsleep(5000); return 0; fail_clk: @@ -809,7 +809,7 @@ static int gc2235_probe(struct i2c_client *client) ret = gc2235_s_config(&dev->sd, client->irq, gcpdev); if (ret) - goto out_free; + goto err_unregister_subdev; dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; dev->pad.flags = MEDIA_PAD_FL_SOURCE; @@ -818,18 +818,16 @@ static int gc2235_probe(struct i2c_client *client) ret = v4l2_ctrl_handler_init(&dev->ctrl_handler, ARRAY_SIZE(gc2235_controls)); - if (ret) { - gc2235_remove(client); - return ret; - } + if (ret) + goto err_csi_cfg; for (i = 0; i < ARRAY_SIZE(gc2235_controls); i++) v4l2_ctrl_new_custom(&dev->ctrl_handler, &gc2235_controls[i], NULL); if (dev->ctrl_handler.error) { - gc2235_remove(client); - return dev->ctrl_handler.error; + ret = dev->ctrl_handler.error; + goto err_ctrl_handler; } /* Use same lock for controls as for everything else. */ @@ -838,14 +836,23 @@ static int gc2235_probe(struct i2c_client *client) ret = media_entity_pads_init(&dev->sd.entity, 1, &dev->pad); if (ret) - gc2235_remove(client); + goto err_ctrl_handler; + + ret = atomisp_register_i2c_module(&dev->sd, gcpdev); + if (ret) + goto err_media_cleanup; - return atomisp_register_i2c_module(&dev->sd, gcpdev); + return 0; -out_free: +err_media_cleanup: + media_entity_cleanup(&dev->sd.entity); +err_ctrl_handler: + v4l2_ctrl_handler_free(&dev->ctrl_handler); +err_csi_cfg: + dev->platform_data->csi_cfg(&dev->sd, 0); +err_unregister_subdev: v4l2_device_unregister_subdev(&dev->sd); kfree(dev); - return ret; } diff --git a/drivers/staging/media/atomisp/i2c/ov2722.h b/drivers/staging/media/atomisp/i2c/ov2722.h index 00317d105305..7d5ea8801fe7 100644 --- a/drivers/staging/media/atomisp/i2c/ov2722.h +++ b/drivers/staging/media/atomisp/i2c/ov2722.h @@ -232,342 +232,9 @@ struct ov2722_write_ctrl { struct ov2722_write_buffer buffer; }; -/* - * Register settings for various resolution - */ -#if 0 -static const struct ov2722_reg ov2722_QVGA_30fps[] = { - {OV2722_8BIT, 0x3718, 0x10}, - {OV2722_8BIT, 0x3702, 0x0c}, - {OV2722_8BIT, 0x373a, 0x1c}, - {OV2722_8BIT, 0x3715, 0x01}, - {OV2722_8BIT, 0x3703, 0x0c}, - {OV2722_8BIT, 0x3705, 0x06}, - {OV2722_8BIT, 0x3730, 0x0e}, - {OV2722_8BIT, 0x3704, 0x1c}, - {OV2722_8BIT, 0x3f06, 0x00}, - {OV2722_8BIT, 0x371c, 0x00}, - {OV2722_8BIT, 0x371d, 0x46}, - {OV2722_8BIT, 0x371e, 0x00}, - {OV2722_8BIT, 0x371f, 0x63}, - {OV2722_8BIT, 0x3708, 0x61}, - {OV2722_8BIT, 0x3709, 0x12}, - {OV2722_8BIT, 0x3800, 0x01}, - {OV2722_8BIT, 0x3801, 0x42}, /* H crop start: 322 */ - {OV2722_8BIT, 0x3802, 0x00}, - {OV2722_8BIT, 0x3803, 0x20}, /* V crop start: 32 */ - {OV2722_8BIT, 0x3804, 0x06}, - {OV2722_8BIT, 0x3805, 0x95}, /* H crop end: 1685 */ - {OV2722_8BIT, 0x3806, 0x04}, - {OV2722_8BIT, 0x3807, 0x27}, /* V crop end: 1063 */ - {OV2722_8BIT, 0x3808, 0x01}, - {OV2722_8BIT, 0x3809, 0x50}, /* H output size: 336 */ - {OV2722_8BIT, 0x380a, 0x01}, - {OV2722_8BIT, 0x380b, 0x00}, /* V output size: 256 */ - - /* H blank timing */ - {OV2722_8BIT, 0x380c, 0x08}, - {OV2722_8BIT, 0x380d, 0x00}, /* H total size: 2048 */ - {OV2722_8BIT, 0x380e, 0x04}, - {OV2722_8BIT, 0x380f, 0xa0}, /* V total size: 1184 */ - {OV2722_8BIT, 0x3810, 0x00}, - {OV2722_8BIT, 0x3811, 0x04}, /* H window offset: 5 */ - {OV2722_8BIT, 0x3812, 0x00}, - {OV2722_8BIT, 0x3813, 0x01}, /* V window offset: 2 */ - {OV2722_8BIT, 0x3820, 0xc0}, - {OV2722_8BIT, 0x3821, 0x06}, /* flip isp*/ - {OV2722_8BIT, 0x3814, 0x71}, - {OV2722_8BIT, 0x3815, 0x71}, - {OV2722_8BIT, 0x3612, 0x49}, - {OV2722_8BIT, 0x3618, 0x00}, - {OV2722_8BIT, 0x3a08, 0x01}, - {OV2722_8BIT, 0x3a09, 0xc3}, - {OV2722_8BIT, 0x3a0a, 0x01}, - {OV2722_8BIT, 0x3a0b, 0x77}, - {OV2722_8BIT, 0x3a0d, 0x00}, - {OV2722_8BIT, 0x3a0e, 0x00}, - {OV2722_8BIT, 0x4520, 0x09}, - {OV2722_8BIT, 0x4837, 0x1b}, - {OV2722_8BIT, 0x3000, 0xff}, - {OV2722_8BIT, 0x3001, 0xff}, - {OV2722_8BIT, 0x3002, 0xf0}, - {OV2722_8BIT, 0x3600, 0x08}, - {OV2722_8BIT, 0x3621, 0xc0}, - {OV2722_8BIT, 0x3632, 0x53}, /* added for power opt */ - {OV2722_8BIT, 0x3633, 0x63}, - {OV2722_8BIT, 0x3634, 0x24}, - {OV2722_8BIT, 0x3f01, 0x0c}, - {OV2722_8BIT, 0x5001, 0xc1}, /* v_en, h_en, blc_en */ - {OV2722_8BIT, 0x3614, 0xf0}, - {OV2722_8BIT, 0x3630, 0x2d}, - {OV2722_8BIT, 0x370b, 0x62}, - {OV2722_8BIT, 0x3706, 0x61}, - {OV2722_8BIT, 0x4000, 0x02}, - {OV2722_8BIT, 0x4002, 0xc5}, - {OV2722_8BIT, 0x4005, 0x08}, - {OV2722_8BIT, 0x404f, 0x84}, - {OV2722_8BIT, 0x4051, 0x00}, - {OV2722_8BIT, 0x5000, 0xff}, - {OV2722_8BIT, 0x3a18, 0x00}, - {OV2722_8BIT, 0x3a19, 0x80}, - {OV2722_8BIT, 0x4521, 0x00}, - {OV2722_8BIT, 0x5183, 0xb0}, /* AWB red */ - {OV2722_8BIT, 0x5184, 0xb0}, /* AWB green */ - {OV2722_8BIT, 0x5185, 0xb0}, /* AWB blue */ - {OV2722_8BIT, 0x5180, 0x03}, /* AWB manual mode */ - {OV2722_8BIT, 0x370c, 0x0c}, - {OV2722_8BIT, 0x4800, 0x24}, /* clk lane gate enable */ - {OV2722_8BIT, 0x3035, 0x00}, - {OV2722_8BIT, 0x3036, 0x26}, - {OV2722_8BIT, 0x3037, 0xa1}, - {OV2722_8BIT, 0x303e, 0x19}, - {OV2722_8BIT, 0x3038, 0x06}, - {OV2722_8BIT, 0x3018, 0x04}, - - /* Added for power optimization */ - {OV2722_8BIT, 0x3000, 0x00}, - {OV2722_8BIT, 0x3001, 0x00}, - {OV2722_8BIT, 0x3002, 0x00}, - {OV2722_8BIT, 0x3a0f, 0x40}, - {OV2722_8BIT, 0x3a10, 0x38}, - {OV2722_8BIT, 0x3a1b, 0x48}, - {OV2722_8BIT, 0x3a1e, 0x30}, - {OV2722_8BIT, 0x3a11, 0x90}, - {OV2722_8BIT, 0x3a1f, 0x10}, - {OV2722_8BIT, 0x3011, 0x22}, - {OV2722_8BIT, 0x3a00, 0x58}, - {OV2722_8BIT, 0x3503, 0x17}, - {OV2722_8BIT, 0x3500, 0x00}, - {OV2722_8BIT, 0x3501, 0x46}, - {OV2722_8BIT, 0x3502, 0x00}, - {OV2722_8BIT, 0x3508, 0x00}, - {OV2722_8BIT, 0x3509, 0x10}, - {OV2722_TOK_TERM, 0, 0}, - -}; - -static const struct ov2722_reg ov2722_480P_30fps[] = { - {OV2722_8BIT, 0x3718, 0x10}, - {OV2722_8BIT, 0x3702, 0x18}, - {OV2722_8BIT, 0x373a, 0x3c}, - {OV2722_8BIT, 0x3715, 0x01}, - {OV2722_8BIT, 0x3703, 0x1d}, - {OV2722_8BIT, 0x3705, 0x12}, - {OV2722_8BIT, 0x3730, 0x1f}, - {OV2722_8BIT, 0x3704, 0x3f}, - {OV2722_8BIT, 0x3f06, 0x1d}, - {OV2722_8BIT, 0x371c, 0x00}, - {OV2722_8BIT, 0x371d, 0x83}, - {OV2722_8BIT, 0x371e, 0x00}, - {OV2722_8BIT, 0x371f, 0xbd}, - {OV2722_8BIT, 0x3708, 0x63}, - {OV2722_8BIT, 0x3709, 0x52}, - {OV2722_8BIT, 0x3800, 0x00}, - {OV2722_8BIT, 0x3801, 0xf2}, /* H crop start: 322 - 80 = 242*/ - {OV2722_8BIT, 0x3802, 0x00}, - {OV2722_8BIT, 0x3803, 0x20}, /* V crop start: 32*/ - {OV2722_8BIT, 0x3804, 0x06}, - {OV2722_8BIT, 0x3805, 0xBB}, /* H crop end: 1643 + 80 = 1723*/ - {OV2722_8BIT, 0x3806, 0x04}, - {OV2722_8BIT, 0x3807, 0x03}, /* V crop end: 1027*/ - {OV2722_8BIT, 0x3808, 0x02}, - {OV2722_8BIT, 0x3809, 0xE0}, /* H output size: 656 +80 = 736*/ - {OV2722_8BIT, 0x380a, 0x01}, - {OV2722_8BIT, 0x380b, 0xF0}, /* V output size: 496 */ - - /* H blank timing */ - {OV2722_8BIT, 0x380c, 0x08}, - {OV2722_8BIT, 0x380d, 0x00}, /* H total size: 2048 */ - {OV2722_8BIT, 0x380e, 0x04}, - {OV2722_8BIT, 0x380f, 0xa0}, /* V total size: 1184 */ - {OV2722_8BIT, 0x3810, 0x00}, - {OV2722_8BIT, 0x3811, 0x04}, /* H window offset: 5 */ - {OV2722_8BIT, 0x3812, 0x00}, - {OV2722_8BIT, 0x3813, 0x01}, /* V window offset: 2 */ - {OV2722_8BIT, 0x3820, 0x80}, - {OV2722_8BIT, 0x3821, 0x06}, /* flip isp*/ - {OV2722_8BIT, 0x3814, 0x31}, - {OV2722_8BIT, 0x3815, 0x31}, - {OV2722_8BIT, 0x3612, 0x4b}, - {OV2722_8BIT, 0x3618, 0x04}, - {OV2722_8BIT, 0x3a08, 0x02}, - {OV2722_8BIT, 0x3a09, 0x67}, - {OV2722_8BIT, 0x3a0a, 0x02}, - {OV2722_8BIT, 0x3a0b, 0x00}, - {OV2722_8BIT, 0x3a0d, 0x00}, - {OV2722_8BIT, 0x3a0e, 0x00}, - {OV2722_8BIT, 0x4520, 0x0a}, - {OV2722_8BIT, 0x4837, 0x1b}, - {OV2722_8BIT, 0x3000, 0xff}, - {OV2722_8BIT, 0x3001, 0xff}, - {OV2722_8BIT, 0x3002, 0xf0}, - {OV2722_8BIT, 0x3600, 0x08}, - {OV2722_8BIT, 0x3621, 0xc0}, - {OV2722_8BIT, 0x3632, 0x53}, /* added for power opt */ - {OV2722_8BIT, 0x3633, 0x63}, - {OV2722_8BIT, 0x3634, 0x24}, - {OV2722_8BIT, 0x3f01, 0x0c}, - {OV2722_8BIT, 0x5001, 0xc1}, /* v_en, h_en, blc_en */ - {OV2722_8BIT, 0x3614, 0xf0}, - {OV2722_8BIT, 0x3630, 0x2d}, - {OV2722_8BIT, 0x370b, 0x62}, - {OV2722_8BIT, 0x3706, 0x61}, - {OV2722_8BIT, 0x4000, 0x02}, - {OV2722_8BIT, 0x4002, 0xc5}, - {OV2722_8BIT, 0x4005, 0x08}, - {OV2722_8BIT, 0x404f, 0x84}, - {OV2722_8BIT, 0x4051, 0x00}, - {OV2722_8BIT, 0x5000, 0xff}, - {OV2722_8BIT, 0x3a18, 0x00}, - {OV2722_8BIT, 0x3a19, 0x80}, - {OV2722_8BIT, 0x4521, 0x00}, - {OV2722_8BIT, 0x5183, 0xb0}, /* AWB red */ - {OV2722_8BIT, 0x5184, 0xb0}, /* AWB green */ - {OV2722_8BIT, 0x5185, 0xb0}, /* AWB blue */ - {OV2722_8BIT, 0x5180, 0x03}, /* AWB manual mode */ - {OV2722_8BIT, 0x370c, 0x0c}, - {OV2722_8BIT, 0x4800, 0x24}, /* clk lane gate enable */ - {OV2722_8BIT, 0x3035, 0x00}, - {OV2722_8BIT, 0x3036, 0x26}, - {OV2722_8BIT, 0x3037, 0xa1}, - {OV2722_8BIT, 0x303e, 0x19}, - {OV2722_8BIT, 0x3038, 0x06}, - {OV2722_8BIT, 0x3018, 0x04}, - - /* Added for power optimization */ - {OV2722_8BIT, 0x3000, 0x00}, - {OV2722_8BIT, 0x3001, 0x00}, - {OV2722_8BIT, 0x3002, 0x00}, - {OV2722_8BIT, 0x3a0f, 0x40}, - {OV2722_8BIT, 0x3a10, 0x38}, - {OV2722_8BIT, 0x3a1b, 0x48}, - {OV2722_8BIT, 0x3a1e, 0x30}, - {OV2722_8BIT, 0x3a11, 0x90}, - {OV2722_8BIT, 0x3a1f, 0x10}, - {OV2722_8BIT, 0x3011, 0x22}, - {OV2722_8BIT, 0x3a00, 0x58}, - {OV2722_8BIT, 0x3503, 0x17}, - {OV2722_8BIT, 0x3500, 0x00}, - {OV2722_8BIT, 0x3501, 0x46}, - {OV2722_8BIT, 0x3502, 0x00}, - {OV2722_8BIT, 0x3508, 0x00}, - {OV2722_8BIT, 0x3509, 0x10}, - {OV2722_TOK_TERM, 0, 0}, -}; - -static const struct ov2722_reg ov2722_VGA_30fps[] = { - {OV2722_8BIT, 0x3718, 0x10}, - {OV2722_8BIT, 0x3702, 0x18}, - {OV2722_8BIT, 0x373a, 0x3c}, - {OV2722_8BIT, 0x3715, 0x01}, - {OV2722_8BIT, 0x3703, 0x1d}, - {OV2722_8BIT, 0x3705, 0x12}, - {OV2722_8BIT, 0x3730, 0x1f}, - {OV2722_8BIT, 0x3704, 0x3f}, - {OV2722_8BIT, 0x3f06, 0x1d}, - {OV2722_8BIT, 0x371c, 0x00}, - {OV2722_8BIT, 0x371d, 0x83}, - {OV2722_8BIT, 0x371e, 0x00}, - {OV2722_8BIT, 0x371f, 0xbd}, - {OV2722_8BIT, 0x3708, 0x63}, - {OV2722_8BIT, 0x3709, 0x52}, - {OV2722_8BIT, 0x3800, 0x01}, - {OV2722_8BIT, 0x3801, 0x42}, /* H crop start: 322 */ - {OV2722_8BIT, 0x3802, 0x00}, - {OV2722_8BIT, 0x3803, 0x20}, /* V crop start: 32*/ - {OV2722_8BIT, 0x3804, 0x06}, - {OV2722_8BIT, 0x3805, 0x6B}, /* H crop end: 1643*/ - {OV2722_8BIT, 0x3806, 0x04}, - {OV2722_8BIT, 0x3807, 0x03}, /* V crop end: 1027*/ - {OV2722_8BIT, 0x3808, 0x02}, - {OV2722_8BIT, 0x3809, 0x90}, /* H output size: 656 */ - {OV2722_8BIT, 0x380a, 0x01}, - {OV2722_8BIT, 0x380b, 0xF0}, /* V output size: 496 */ - - /* H blank timing */ - {OV2722_8BIT, 0x380c, 0x08}, - {OV2722_8BIT, 0x380d, 0x00}, /* H total size: 2048 */ - {OV2722_8BIT, 0x380e, 0x04}, - {OV2722_8BIT, 0x380f, 0xa0}, /* V total size: 1184 */ - {OV2722_8BIT, 0x3810, 0x00}, - {OV2722_8BIT, 0x3811, 0x04}, /* H window offset: 5 */ - {OV2722_8BIT, 0x3812, 0x00}, - {OV2722_8BIT, 0x3813, 0x01}, /* V window offset: 2 */ - {OV2722_8BIT, 0x3820, 0x80}, - {OV2722_8BIT, 0x3821, 0x06}, /* flip isp*/ - {OV2722_8BIT, 0x3814, 0x31}, - {OV2722_8BIT, 0x3815, 0x31}, - {OV2722_8BIT, 0x3612, 0x4b}, - {OV2722_8BIT, 0x3618, 0x04}, - {OV2722_8BIT, 0x3a08, 0x02}, - {OV2722_8BIT, 0x3a09, 0x67}, - {OV2722_8BIT, 0x3a0a, 0x02}, - {OV2722_8BIT, 0x3a0b, 0x00}, - {OV2722_8BIT, 0x3a0d, 0x00}, - {OV2722_8BIT, 0x3a0e, 0x00}, - {OV2722_8BIT, 0x4520, 0x0a}, - {OV2722_8BIT, 0x4837, 0x29}, - {OV2722_8BIT, 0x3000, 0xff}, - {OV2722_8BIT, 0x3001, 0xff}, - {OV2722_8BIT, 0x3002, 0xf0}, - {OV2722_8BIT, 0x3600, 0x08}, - {OV2722_8BIT, 0x3621, 0xc0}, - {OV2722_8BIT, 0x3632, 0x53}, /* added for power opt */ - {OV2722_8BIT, 0x3633, 0x63}, - {OV2722_8BIT, 0x3634, 0x24}, - {OV2722_8BIT, 0x3f01, 0x0c}, - {OV2722_8BIT, 0x5001, 0xc1}, /* v_en, h_en, blc_en */ - {OV2722_8BIT, 0x3614, 0xf0}, - {OV2722_8BIT, 0x3630, 0x2d}, - {OV2722_8BIT, 0x370b, 0x62}, - {OV2722_8BIT, 0x3706, 0x61}, - {OV2722_8BIT, 0x4000, 0x02}, - {OV2722_8BIT, 0x4002, 0xc5}, - {OV2722_8BIT, 0x4005, 0x08}, - {OV2722_8BIT, 0x404f, 0x84}, - {OV2722_8BIT, 0x4051, 0x00}, - {OV2722_8BIT, 0x5000, 0xff}, - {OV2722_8BIT, 0x3a18, 0x00}, - {OV2722_8BIT, 0x3a19, 0x80}, - {OV2722_8BIT, 0x4521, 0x00}, - {OV2722_8BIT, 0x5183, 0xb0}, /* AWB red */ - {OV2722_8BIT, 0x5184, 0xb0}, /* AWB green */ - {OV2722_8BIT, 0x5185, 0xb0}, /* AWB blue */ - {OV2722_8BIT, 0x5180, 0x03}, /* AWB manual mode */ - {OV2722_8BIT, 0x370c, 0x0c}, - {OV2722_8BIT, 0x4800, 0x24}, /* clk lane gate enable */ - {OV2722_8BIT, 0x3035, 0x00}, - {OV2722_8BIT, 0x3036, 0x26}, - {OV2722_8BIT, 0x3037, 0xa1}, - {OV2722_8BIT, 0x303e, 0x19}, - {OV2722_8BIT, 0x3038, 0x06}, - {OV2722_8BIT, 0x3018, 0x04}, - - /* Added for power optimization */ - {OV2722_8BIT, 0x3000, 0x00}, - {OV2722_8BIT, 0x3001, 0x00}, - {OV2722_8BIT, 0x3002, 0x00}, - {OV2722_8BIT, 0x3a0f, 0x40}, - {OV2722_8BIT, 0x3a10, 0x38}, - {OV2722_8BIT, 0x3a1b, 0x48}, - {OV2722_8BIT, 0x3a1e, 0x30}, - {OV2722_8BIT, 0x3a11, 0x90}, - {OV2722_8BIT, 0x3a1f, 0x10}, - {OV2722_8BIT, 0x3011, 0x22}, - {OV2722_8BIT, 0x3a00, 0x58}, - {OV2722_8BIT, 0x3503, 0x17}, - {OV2722_8BIT, 0x3500, 0x00}, - {OV2722_8BIT, 0x3501, 0x46}, - {OV2722_8BIT, 0x3502, 0x00}, - {OV2722_8BIT, 0x3508, 0x00}, - {OV2722_8BIT, 0x3509, 0x10}, - {OV2722_TOK_TERM, 0, 0}, -}; -#endif - static const struct ov2722_reg ov2722_1632_1092_30fps[] = { - {OV2722_8BIT, 0x3021, 0x03}, /* For stand wait for - a whole frame complete.(vblank) */ + /* For stand wait for a whole frame complete.(vblank) */ + {OV2722_8BIT, 0x3021, 0x03}, {OV2722_8BIT, 0x3718, 0x10}, {OV2722_8BIT, 0x3702, 0x24}, {OV2722_8BIT, 0x373a, 0x60}, @@ -668,8 +335,8 @@ static const struct ov2722_reg ov2722_1632_1092_30fps[] = { }; static const struct ov2722_reg ov2722_1452_1092_30fps[] = { - {OV2722_8BIT, 0x3021, 0x03}, /* For stand wait for - a whole frame complete.(vblank) */ + /* For stand wait for a whole frame complete.(vblank) */ + {OV2722_8BIT, 0x3021, 0x03}, {OV2722_8BIT, 0x3718, 0x10}, {OV2722_8BIT, 0x3702, 0x24}, {OV2722_8BIT, 0x373a, 0x60}, @@ -768,118 +435,9 @@ static const struct ov2722_reg ov2722_1452_1092_30fps[] = { {OV2722_TOK_TERM, 0, 0} }; -#if 0 -static const struct ov2722_reg ov2722_1M3_30fps[] = { - {OV2722_8BIT, 0x3718, 0x10}, - {OV2722_8BIT, 0x3702, 0x24}, - {OV2722_8BIT, 0x373a, 0x60}, - {OV2722_8BIT, 0x3715, 0x01}, - {OV2722_8BIT, 0x3703, 0x2e}, - {OV2722_8BIT, 0x3705, 0x10}, - {OV2722_8BIT, 0x3730, 0x30}, - {OV2722_8BIT, 0x3704, 0x62}, - {OV2722_8BIT, 0x3f06, 0x3a}, - {OV2722_8BIT, 0x371c, 0x00}, - {OV2722_8BIT, 0x371d, 0xc4}, - {OV2722_8BIT, 0x371e, 0x01}, - {OV2722_8BIT, 0x371f, 0x0d}, - {OV2722_8BIT, 0x3708, 0x61}, - {OV2722_8BIT, 0x3709, 0x12}, - {OV2722_8BIT, 0x3800, 0x01}, - {OV2722_8BIT, 0x3801, 0x4a}, /* H crop start: 330 */ - {OV2722_8BIT, 0x3802, 0x00}, - {OV2722_8BIT, 0x3803, 0x03}, /* V crop start: 3 */ - {OV2722_8BIT, 0x3804, 0x06}, - {OV2722_8BIT, 0x3805, 0xe1}, /* H crop end: 1761 */ - {OV2722_8BIT, 0x3806, 0x04}, - {OV2722_8BIT, 0x3807, 0x47}, /* V crop end: 1095 */ - {OV2722_8BIT, 0x3808, 0x05}, - {OV2722_8BIT, 0x3809, 0x88}, /* H output size: 1416 */ - {OV2722_8BIT, 0x380a, 0x04}, - {OV2722_8BIT, 0x380b, 0x0a}, /* V output size: 1034 */ - - /* H blank timing */ - {OV2722_8BIT, 0x380c, 0x08}, - {OV2722_8BIT, 0x380d, 0x00}, /* H total size: 2048 */ - {OV2722_8BIT, 0x380e, 0x04}, - {OV2722_8BIT, 0x380f, 0xa0}, /* V total size: 1184 */ - {OV2722_8BIT, 0x3810, 0x00}, - {OV2722_8BIT, 0x3811, 0x05}, /* H window offset: 5 */ - {OV2722_8BIT, 0x3812, 0x00}, - {OV2722_8BIT, 0x3813, 0x02}, /* V window offset: 2 */ - {OV2722_8BIT, 0x3820, 0x80}, - {OV2722_8BIT, 0x3821, 0x06}, /* flip isp */ - {OV2722_8BIT, 0x3814, 0x11}, - {OV2722_8BIT, 0x3815, 0x11}, - {OV2722_8BIT, 0x3612, 0x0b}, - {OV2722_8BIT, 0x3618, 0x04}, - {OV2722_8BIT, 0x3a08, 0x01}, - {OV2722_8BIT, 0x3a09, 0x50}, - {OV2722_8BIT, 0x3a0a, 0x01}, - {OV2722_8BIT, 0x3a0b, 0x18}, - {OV2722_8BIT, 0x3a0d, 0x03}, - {OV2722_8BIT, 0x3a0e, 0x03}, - {OV2722_8BIT, 0x4520, 0x00}, - {OV2722_8BIT, 0x4837, 0x1b}, - {OV2722_8BIT, 0x3000, 0xff}, - {OV2722_8BIT, 0x3001, 0xff}, - {OV2722_8BIT, 0x3002, 0xf0}, - {OV2722_8BIT, 0x3600, 0x08}, - {OV2722_8BIT, 0x3621, 0xc0}, - {OV2722_8BIT, 0x3632, 0xd2}, /* added for power opt */ - {OV2722_8BIT, 0x3633, 0x23}, - {OV2722_8BIT, 0x3634, 0x54}, - {OV2722_8BIT, 0x3f01, 0x0c}, - {OV2722_8BIT, 0x5001, 0xc1}, /* v_en, h_en, blc_en */ - {OV2722_8BIT, 0x3614, 0xf0}, - {OV2722_8BIT, 0x3630, 0x2d}, - {OV2722_8BIT, 0x370b, 0x62}, - {OV2722_8BIT, 0x3706, 0x61}, - {OV2722_8BIT, 0x4000, 0x02}, - {OV2722_8BIT, 0x4002, 0xc5}, - {OV2722_8BIT, 0x4005, 0x08}, - {OV2722_8BIT, 0x404f, 0x84}, - {OV2722_8BIT, 0x4051, 0x00}, - {OV2722_8BIT, 0x5000, 0xcf}, - {OV2722_8BIT, 0x3a18, 0x00}, - {OV2722_8BIT, 0x3a19, 0x80}, - {OV2722_8BIT, 0x4521, 0x00}, - {OV2722_8BIT, 0x5183, 0xb0}, /* AWB red */ - {OV2722_8BIT, 0x5184, 0xb0}, /* AWB green */ - {OV2722_8BIT, 0x5185, 0xb0}, /* AWB blue */ - {OV2722_8BIT, 0x5180, 0x03}, /* AWB manual mode */ - {OV2722_8BIT, 0x370c, 0x0c}, - {OV2722_8BIT, 0x4800, 0x24}, /* clk lane gate enable */ - {OV2722_8BIT, 0x3035, 0x00}, - {OV2722_8BIT, 0x3036, 0x26}, - {OV2722_8BIT, 0x3037, 0xa1}, - {OV2722_8BIT, 0x303e, 0x19}, - {OV2722_8BIT, 0x3038, 0x06}, - {OV2722_8BIT, 0x3018, 0x04}, - - /* Added for power optimization */ - {OV2722_8BIT, 0x3000, 0x00}, - {OV2722_8BIT, 0x3001, 0x00}, - {OV2722_8BIT, 0x3002, 0x00}, - {OV2722_8BIT, 0x3a0f, 0x40}, - {OV2722_8BIT, 0x3a10, 0x38}, - {OV2722_8BIT, 0x3a1b, 0x48}, - {OV2722_8BIT, 0x3a1e, 0x30}, - {OV2722_8BIT, 0x3a11, 0x90}, - {OV2722_8BIT, 0x3a1f, 0x10}, - {OV2722_8BIT, 0x3503, 0x17}, - {OV2722_8BIT, 0x3500, 0x00}, - {OV2722_8BIT, 0x3501, 0x46}, - {OV2722_8BIT, 0x3502, 0x00}, - {OV2722_8BIT, 0x3508, 0x00}, - {OV2722_8BIT, 0x3509, 0x10}, - {OV2722_TOK_TERM, 0, 0}, -}; -#endif - static const struct ov2722_reg ov2722_1080p_30fps[] = { - {OV2722_8BIT, 0x3021, 0x03}, /* For stand wait for a whole - frame complete.(vblank) */ + /* For stand wait for a whole frame complete.(vblank) */ + {OV2722_8BIT, 0x3021, 0x03}, {OV2722_8BIT, 0x3718, 0x10}, {OV2722_8BIT, 0x3702, 0x24}, {OV2722_8BIT, 0x373a, 0x60}, @@ -982,108 +540,6 @@ static const struct ov2722_reg ov2722_1080p_30fps[] = { {OV2722_TOK_TERM, 0, 0} }; -#if 0 /* Currently unused */ -static const struct ov2722_reg ov2722_720p_30fps[] = { - {OV2722_8BIT, 0x3021, 0x03}, - {OV2722_8BIT, 0x3718, 0x10}, - {OV2722_8BIT, 0x3702, 0x24}, - {OV2722_8BIT, 0x373a, 0x60}, - {OV2722_8BIT, 0x3715, 0x01}, - {OV2722_8BIT, 0x3703, 0x2e}, - {OV2722_8BIT, 0x3705, 0x10}, - {OV2722_8BIT, 0x3730, 0x30}, - {OV2722_8BIT, 0x3704, 0x62}, - {OV2722_8BIT, 0x3f06, 0x3a}, - {OV2722_8BIT, 0x371c, 0x00}, - {OV2722_8BIT, 0x371d, 0xc4}, - {OV2722_8BIT, 0x371e, 0x01}, - {OV2722_8BIT, 0x371f, 0x0d}, - {OV2722_8BIT, 0x3708, 0x61}, - {OV2722_8BIT, 0x3709, 0x12}, - {OV2722_8BIT, 0x3800, 0x01}, - {OV2722_8BIT, 0x3801, 0x40}, /* H crop start: 320 */ - {OV2722_8BIT, 0x3802, 0x00}, - {OV2722_8BIT, 0x3803, 0xb1}, /* V crop start: 177 */ - {OV2722_8BIT, 0x3804, 0x06}, - {OV2722_8BIT, 0x3805, 0x55}, /* H crop end: 1621 */ - {OV2722_8BIT, 0x3806, 0x03}, - {OV2722_8BIT, 0x3807, 0x95}, /* V crop end: 918 */ - {OV2722_8BIT, 0x3808, 0x05}, - {OV2722_8BIT, 0x3809, 0x10}, /* H output size: 0x0788==1928 */ - {OV2722_8BIT, 0x380a, 0x02}, - {OV2722_8BIT, 0x380b, 0xe0}, /* output size: 0x02DE==734 */ - {OV2722_8BIT, 0x380c, 0x08}, - {OV2722_8BIT, 0x380d, 0x00}, /* H timing: 2048 */ - {OV2722_8BIT, 0x380e, 0x04}, - {OV2722_8BIT, 0x380f, 0xa3}, /* V timing: 1187 */ - {OV2722_8BIT, 0x3810, 0x00}, - {OV2722_8BIT, 0x3811, 0x03}, /* H window offset: 3 */ - {OV2722_8BIT, 0x3812, 0x00}, - {OV2722_8BIT, 0x3813, 0x02}, /* V window offset: 2 */ - {OV2722_8BIT, 0x3820, 0x80}, - {OV2722_8BIT, 0x3821, 0x06}, /* mirror */ - {OV2722_8BIT, 0x3814, 0x11}, - {OV2722_8BIT, 0x3815, 0x11}, - {OV2722_8BIT, 0x3612, 0x0b}, - {OV2722_8BIT, 0x3618, 0x04}, - {OV2722_8BIT, 0x3a08, 0x01}, - {OV2722_8BIT, 0x3a09, 0x50}, - {OV2722_8BIT, 0x3a0a, 0x01}, - {OV2722_8BIT, 0x3a0b, 0x18}, - {OV2722_8BIT, 0x3a0d, 0x03}, - {OV2722_8BIT, 0x3a0e, 0x03}, - {OV2722_8BIT, 0x4520, 0x00}, - {OV2722_8BIT, 0x4837, 0x1b}, - {OV2722_8BIT, 0x3600, 0x08}, - {OV2722_8BIT, 0x3621, 0xc0}, - {OV2722_8BIT, 0x3632, 0xd2}, /* added for power opt */ - {OV2722_8BIT, 0x3633, 0x23}, - {OV2722_8BIT, 0x3634, 0x54}, - {OV2722_8BIT, 0x3f01, 0x0c}, - {OV2722_8BIT, 0x5001, 0xc1}, - {OV2722_8BIT, 0x3614, 0xf0}, - {OV2722_8BIT, 0x3630, 0x2d}, - {OV2722_8BIT, 0x370b, 0x62}, - {OV2722_8BIT, 0x3706, 0x61}, - {OV2722_8BIT, 0x4000, 0x02}, - {OV2722_8BIT, 0x4002, 0xc5}, - {OV2722_8BIT, 0x4005, 0x08}, - {OV2722_8BIT, 0x404f, 0x84}, - {OV2722_8BIT, 0x4051, 0x00}, - {OV2722_8BIT, 0x5000, 0xcf}, /* manual 3a */ - {OV2722_8BIT, 0x301d, 0xf0}, /* enable group hold */ - {OV2722_8BIT, 0x3a18, 0x00}, - {OV2722_8BIT, 0x3a19, 0x80}, - {OV2722_8BIT, 0x4521, 0x00}, - {OV2722_8BIT, 0x5183, 0xb0}, - {OV2722_8BIT, 0x5184, 0xb0}, - {OV2722_8BIT, 0x5185, 0xb0}, - {OV2722_8BIT, 0x370c, 0x0c}, - {OV2722_8BIT, 0x3035, 0x00}, - {OV2722_8BIT, 0x3036, 0x26}, /* {0x3036, 0x2c}, //422.4 MHz */ - {OV2722_8BIT, 0x3037, 0xa1}, - {OV2722_8BIT, 0x303e, 0x19}, - {OV2722_8BIT, 0x3038, 0x06}, - {OV2722_8BIT, 0x3018, 0x04}, - {OV2722_8BIT, 0x3000, 0x00}, /* added for power optimization */ - {OV2722_8BIT, 0x3001, 0x00}, - {OV2722_8BIT, 0x3002, 0x00}, - {OV2722_8BIT, 0x3a0f, 0x40}, - {OV2722_8BIT, 0x3a10, 0x38}, - {OV2722_8BIT, 0x3a1b, 0x48}, - {OV2722_8BIT, 0x3a1e, 0x30}, - {OV2722_8BIT, 0x3a11, 0x90}, - {OV2722_8BIT, 0x3a1f, 0x10}, - {OV2722_8BIT, 0x3503, 0x17}, /* manual 3a */ - {OV2722_8BIT, 0x3500, 0x00}, - {OV2722_8BIT, 0x3501, 0x3F}, - {OV2722_8BIT, 0x3502, 0x00}, - {OV2722_8BIT, 0x3508, 0x00}, - {OV2722_8BIT, 0x3509, 0x00}, - {OV2722_TOK_TERM, 0, 0}, -}; -#endif - static struct ov2722_resolution ov2722_res_preview[] = { { .desc = "ov2722_1632_1092_30fps", @@ -1128,99 +584,6 @@ static struct ov2722_resolution ov2722_res_preview[] = { #define N_RES_PREVIEW (ARRAY_SIZE(ov2722_res_preview)) -/* - * Disable non-preview configurations until the configuration selection is - * improved. - */ -#if 0 -struct ov2722_resolution ov2722_res_still[] = { - { - .desc = "ov2722_480P_30fps", - .width = 1632, - .height = 1092, - .fps = 30, - .pix_clk_freq = 85, - .used = 0, - .pixels_per_line = 2260, - .lines_per_frame = 1244, - .skip_frames = 3, - .regs = ov2722_1632_1092_30fps, - .mipi_freq = 422400, - }, - { - .desc = "ov2722_1452_1092_30fps", - .width = 1452, - .height = 1092, - .fps = 30, - .pix_clk_freq = 85, - .used = 0, - .pixels_per_line = 2260, - .lines_per_frame = 1244, - .skip_frames = 3, - .regs = ov2722_1452_1092_30fps, - .mipi_freq = 422400, - }, - { - .desc = "ov2722_1080P_30fps", - .width = 1932, - .height = 1092, - .pix_clk_freq = 69, - .fps = 30, - .used = 0, - .pixels_per_line = 2068, - .lines_per_frame = 1114, - .skip_frames = 3, - .regs = ov2722_1080p_30fps, - .mipi_freq = 345600, - }, -}; - -#define N_RES_STILL (ARRAY_SIZE(ov2722_res_still)) - -struct ov2722_resolution ov2722_res_video[] = { - { - .desc = "ov2722_QVGA_30fps", - .width = 336, - .height = 256, - .fps = 30, - .pix_clk_freq = 73, - .used = 0, - .pixels_per_line = 2048, - .lines_per_frame = 1184, - .skip_frames = 3, - .regs = ov2722_QVGA_30fps, - .mipi_freq = 364800, - }, - { - .desc = "ov2722_480P_30fps", - .width = 736, - .height = 496, - .fps = 30, - .pix_clk_freq = 73, - .used = 0, - .pixels_per_line = 2048, - .lines_per_frame = 1184, - .skip_frames = 3, - .regs = ov2722_480P_30fps, - }, - { - .desc = "ov2722_1080P_30fps", - .width = 1932, - .height = 1092, - .pix_clk_freq = 69, - .fps = 30, - .used = 0, - .pixels_per_line = 2068, - .lines_per_frame = 1114, - .skip_frames = 3, - .regs = ov2722_1080p_30fps, - .mipi_freq = 345600, - }, -}; - -#define N_RES_VIDEO (ARRAY_SIZE(ov2722_res_video)) -#endif - static struct ov2722_resolution *ov2722_res = ov2722_res_preview; static unsigned long N_RES = N_RES_PREVIEW; #endif diff --git a/drivers/staging/media/atomisp/include/linux/atomisp.h b/drivers/staging/media/atomisp/include/linux/atomisp.h index 3c8fa3f5808d..7d7f57c2d67c 100644 --- a/drivers/staging/media/atomisp/include/linux/atomisp.h +++ b/drivers/staging/media/atomisp/include/linux/atomisp.h @@ -22,7 +22,7 @@ #define ATOMISP_HW_STEPPING_A0 0x00 #define ATOMISP_HW_STEPPING_B0 0x10 -/*ISP binary running mode*/ +/* ISP binary running mode */ #define CI_MODE_PREVIEW 0x8000 #define CI_MODE_VIDEO 0x4000 #define CI_MODE_STILL_CAPTURE 0x2000 @@ -59,9 +59,9 @@ /* Configuration used by Bayer noise reduction and YCC noise reduction */ struct atomisp_nr_config { - /* [gain] Strength of noise reduction for Bayer NR (Used by Bayer NR) */ + /* [gain] Strength of noise reduction for Bayer NR */ unsigned int bnr_gain; - /* [gain] Strength of noise reduction for YCC NR (Used by YCC NR) */ + /* [gain] Strength of noise reduction for YCC NR */ unsigned int ynr_gain; /* [intensity] Sensitivity of Edge (Used by Bayer NR) */ unsigned int direction; @@ -78,7 +78,8 @@ struct atomisp_tnr_config { unsigned int threshold_uv;/* [intensity] Motion sensitivity for U/V */ }; -/* Histogram. This contains num_elements values of type unsigned int. +/* + * Contains num_elements values of type unsigned int. * The data pointer is a DDR pointer (virtual address). */ struct atomisp_histogram { @@ -94,7 +95,7 @@ enum atomisp_ob_mode { /* Optical black level configuration */ struct atomisp_ob_config { - /* Obtical black level mode (Fixed / Raster) */ + /* Optical black level mode (Fixed / Raster) */ enum atomisp_ob_mode mode; /* [intensity] optical black level for GR (relevant for fixed mode) */ unsigned int level_gr; @@ -146,8 +147,7 @@ struct atomisp_3a_config { unsigned int ae_y_coef_r; /* [gain] Weight of R for Y */ unsigned int ae_y_coef_g; /* [gain] Weight of G for Y */ unsigned int ae_y_coef_b; /* [gain] Weight of B for Y */ - unsigned int awb_lg_high_raw; /* [intensity] - AWB level gate high for raw */ + unsigned int awb_lg_high_raw; /* [intensity] AWB level gate high for raw */ unsigned int awb_lg_low; /* [intensity] AWB level gate low */ unsigned int awb_lg_high; /* [intensity] AWB level gate high */ int af_fir1_coef[7]; /* [factor] AF FIR coefficients of fir1 */ @@ -188,14 +188,15 @@ struct atomisp_dis_vector { int y; }; -/* DVS 2.0 Coefficient types. This structure contains 4 pointers to - * arrays that contain the coefficients for each type. +/* + * DVS 2.0 Coefficient types. This structure contains 4 pointers to + * arrays that contain the coefficients for each type. */ struct atomisp_dvs2_coef_types { - short __user *odd_real; /** real part of the odd coefficients*/ - short __user *odd_imag; /** imaginary part of the odd coefficients*/ - short __user *even_real;/** real part of the even coefficients*/ - short __user *even_imag;/** imaginary part of the even coefficients*/ + short __user *odd_real; /* Real part of the odd coefficients */ + short __user *odd_imag; /* Imaginary part of the odd coefficients */ + short __user *even_real;/* Real part of the even coefficients */ + short __user *even_imag;/* Imaginary part of the even coefficients */ }; /* @@ -203,10 +204,10 @@ struct atomisp_dvs2_coef_types { * arrays that contain the statistics for each type. */ struct atomisp_dvs2_stat_types { - int __user *odd_real; /** real part of the odd statistics*/ - int __user *odd_imag; /** imaginary part of the odd statistics*/ - int __user *even_real;/** real part of the even statistics*/ - int __user *even_imag;/** imaginary part of the even statistics*/ + int __user *odd_real; /* Real part of the odd statistics */ + int __user *odd_imag; /* Imaginary part of the odd statistics */ + int __user *even_real;/* Real part of the even statistics */ + int __user *even_imag;/* Imaginary part of the even statistics */ }; struct atomisp_dis_coefficients { @@ -234,12 +235,12 @@ struct atomisp_3a_rgby_output { }; /* - * Because we have 2 pipes at max to output metadata, therefore driver will use - * ATOMISP_MAIN_METADATA to specify the metadata from the pipe which keeps - * streaming always and use ATOMISP_SEC_METADATA to specify the metadata from - * the pipe which is streaming by request like capture pipe of ZSL or SDV mode - * as secondary metadata. And for the use case which has only one pipe - * streaming like online capture, ATOMISP_MAIN_METADATA will be used. + * As the driver can output metadata on two pipes at max, + * ATOMISP_MAIN_METADATA is used for the pipe that streams continuously. + * ATOMISP_SEC_METADATA is used for the pipe that streams on demand, e.g., + * the capture pipe in ZSL or SDV modes. + * In use cases with a single streaming pipe (like online capture), + * only ATOMISP_MAIN_METADATA is used. */ enum atomisp_metadata_type { ATOMISP_MAIN_METADATA = 0, @@ -257,7 +258,7 @@ struct atomisp_3a_statistics { struct atomisp_3a_output __user *data; struct atomisp_3a_rgby_output __user *rgby_data; u32 exp_id; /* exposure ID */ - u32 isp_config_id; /* isp config ID */ + u32 isp_config_id; }; /* White Balance (Gain Adjust) */ @@ -272,8 +273,8 @@ struct atomisp_wb_config { /* Color Space Conversion settings */ struct atomisp_cc_config { unsigned int fraction_bits; - int matrix[3 * 3]; /* RGB2YUV Color matrix, signed - <13-fraction_bits>.<fraction_bits> */ + /* RGB2YUV Color matrix, signed <13-fraction_bits>.<fraction_bits> */ + int matrix[3 * 3]; }; /* De pixel noise configuration */ @@ -291,13 +292,15 @@ struct atomisp_ce_config { /* Defect pixel correction configuration */ struct atomisp_dp_config { - /* [intensity] The threshold of defect Pixel Correction, representing + /* + * [intensity] The threshold of defect Pixel Correction, representing * the permissible difference of intensity between one pixel and its * surrounding pixels. Smaller values result in more frequent pixel * corrections. u0_16 */ unsigned int threshold; - /* [gain] The sensitivity of mis-correction. ISP will miss a lot of + /* + * [gain] The sensitivity of mis-correction. ISP will miss a lot of * defects if the value is set too large. u8_8 */ unsigned int gain; @@ -312,7 +315,7 @@ struct atomisp_xnr_config { __u16 threshold; }; -/* metadata config */ +/* Metadata config */ struct atomisp_metadata_config { u32 metadata_height; u32 metadata_stride; @@ -322,31 +325,30 @@ struct atomisp_metadata_config { * Generic resolution structure. */ struct atomisp_resolution { - u32 width; /** Width */ - u32 height; /** Height */ + u32 width; + u32 height; }; /* - * This specifies the coordinates (x,y) + * Specifies the zoom point coordinates (x,y) */ struct atomisp_zoom_point { - s32 x; /** x coordinate */ - s32 y; /** y coordinate */ + s32 x; + s32 y; }; /* - * This specifies the region + * Specifies the zoom region */ struct atomisp_zoom_region { - struct atomisp_zoom_point - origin; /* Starting point coordinates for the region */ + struct atomisp_zoom_point origin; /* Starting point coordinates for the region */ struct atomisp_resolution resolution; /* Region resolution */ }; struct atomisp_dz_config { - u32 dx; /** Horizontal zoom factor */ - u32 dy; /** Vertical zoom factor */ - struct atomisp_zoom_region zoom_region; /** region for zoom */ + u32 dx; /* Horizontal zoom factor */ + u32 dy; /* Vertical zoom factor */ + struct atomisp_zoom_region zoom_region; }; struct atomisp_parm { @@ -367,8 +369,8 @@ struct atomisp_parm { }; struct dvs2_bq_resolution { - int width_bq; /* width [BQ] */ - int height_bq; /* height [BQ] */ + int width_bq; + int height_bq; }; struct atomisp_dvs2_bq_resolutions { @@ -378,9 +380,9 @@ struct atomisp_dvs2_bq_resolutions { struct dvs2_bq_resolution output_bq; /* GDC effective envelope size [BQ] */ struct dvs2_bq_resolution envelope_bq; - /* isp pipe filter size [BQ] */ + /* ISP pipe filter size [BQ] */ struct dvs2_bq_resolution ispfilter_bq; - /* GDC shit size [BQ] */ + /* GDC shift size [BQ] */ struct dvs2_bq_resolution gdc_shift_bq; }; @@ -411,8 +413,8 @@ struct atomisp_parameters { struct atomisp_cnr_config *cnr_config; /* Chroma Noise Reduction */ struct atomisp_macc_config *macc_config; /* MACC */ struct atomisp_ctc_config *ctc_config; /* Chroma Tone Control */ - struct atomisp_aa_config *aa_config; /* Anti-Aliasing */ - struct atomisp_aa_config *baa_config; /* Anti-Aliasing */ + struct atomisp_aa_config *aa_config; /* Anti-Aliasing */ + struct atomisp_aa_config *baa_config; /* Anti-Aliasing */ struct atomisp_ce_config *ce_config; struct atomisp_dvs_6axis_config *dvs_6axis_config; struct atomisp_ob_config *ob_config; /* Objective Black config */ @@ -425,10 +427,8 @@ struct atomisp_parameters { struct atomisp_3a_config *a3a_config; /* 3A Statistics config */ struct atomisp_xnr_config *xnr_config; /* eXtra Noise Reduction */ struct atomisp_dz_config *dz_config; /* Digital Zoom */ - struct atomisp_cc_config *yuv2rgb_cc_config; /* Color - Correction config */ - struct atomisp_cc_config *rgb2yuv_cc_config; /* Color - Correction config */ + struct atomisp_cc_config *yuv2rgb_cc_config; /* Color Correction config */ + struct atomisp_cc_config *rgb2yuv_cc_config; /* Color Correction config */ struct atomisp_macc_table *macc_table; struct atomisp_gamma_table *gamma_table; struct atomisp_ctc_table *ctc_table; @@ -466,8 +466,8 @@ struct atomisp_parameters { void *res_mgr_2500_config; /* - * Output frame pointer the config is to be applied to (optional), - * set to NULL to make this config is applied as global. + * Output frame pointer to which the config will be applied to (optional), + * Set to NULL to make this config be applied as global. */ void *output_frame; /* @@ -490,9 +490,9 @@ struct atomisp_gamma_table { unsigned short data[ATOMISP_GAMMA_TABLE_SIZE]; }; -/* Morphing table for advanced ISP. - * Each line of width elements takes up COORD_TABLE_EXT_WIDTH elements - * in memory. +/* + * Morphing table for advanced ISP. + * Each line of width elements takes up COORD_TABLE_EXT_WIDTH elements in memory. */ #define ATOMISP_MORPH_TABLE_NUM_PLANES 6 struct atomisp_morph_table { @@ -519,7 +519,7 @@ struct atomisp_shading_table { __u16 *data[ATOMISP_NUM_SC_COLORS]; }; -/* parameter for MACC */ +/* Parameter for MACC */ #define ATOMISP_NUM_MACC_AXES 16 struct atomisp_macc_table { short data[4 * ATOMISP_NUM_MACC_AXES]; @@ -530,7 +530,7 @@ struct atomisp_macc_config { struct atomisp_macc_table table; }; -/* Parameter for ctc parameter control */ +/* CTC parameter control */ #define ATOMISP_CTC_TABLE_SIZE 1024 struct atomisp_ctc_table { unsigned short data[ATOMISP_CTC_TABLE_SIZE]; @@ -538,9 +538,10 @@ struct atomisp_ctc_table { /* Parameter for overlay image loading */ struct atomisp_overlay { - /* the frame containing the overlay data The overlay frame width should - * be the multiples of 2*ISP_VEC_NELEMS. The overlay frame height - * should be the multiples of 2. + /* + * The frame containing the overlay data. The overlay frame width should + * be a multiple of 2 * ISP_VEC_NELEMS. The overlay frame height + * should be a multiple of 2. */ struct v4l2_framebuffer *frame; /* Y value of overlay background */ @@ -549,23 +550,27 @@ struct atomisp_overlay { char bg_u; /* V value of overlay background */ char bg_v; - /* the blending percent of input data for Y subpixels */ + /* The blending percentage of input data for Y subpixels */ unsigned char blend_input_perc_y; - /* the blending percent of input data for U subpixels */ + /* The blending percentage of input data for U subpixels */ unsigned char blend_input_perc_u; - /* the blending percent of input data for V subpixels */ + /* The blending percentage of input data for V subpixels */ unsigned char blend_input_perc_v; - /* the blending percent of overlay data for Y subpixels */ + /* The blending percentage of overlay data for Y subpixels */ unsigned char blend_overlay_perc_y; - /* the blending percent of overlay data for U subpixels */ + /* The blending percentage of overlay data for U subpixels */ unsigned char blend_overlay_perc_u; - /* the blending percent of overlay data for V subpixels */ + /* The blending percentage of overlay data for V subpixels */ unsigned char blend_overlay_perc_v; - /* the overlay start x pixel position on output frame It should be the - multiples of 2*ISP_VEC_NELEMS. */ + /* + * The overlay "start x" pixel position on the output frame. It should be a + * multiple of 2 * ISP_VEC_NELEMS. + */ unsigned int overlay_start_x; - /* the overlay start y pixel position on output frame It should be the - multiples of 2. */ + /* + * The overlay "start y" pixel position on the output frame. It should be a + * multiple of 2. + */ unsigned int overlay_start_y; }; @@ -659,7 +664,7 @@ enum atomisp_burst_capture_options { #define EXT_ISP_SHOT_MODE_ANIMATED_PHOTO 10 #define EXT_ISP_SHOT_MODE_SPORTS 11 -/*Private IOCTLs for ISP */ +/* Private IOCTLs for ISP */ #define ATOMISP_IOC_G_XNR \ _IOR('v', BASE_VIDIOC_PRIVATE + 0, int) #define ATOMISP_IOC_S_XNR \ @@ -684,7 +689,8 @@ enum atomisp_burst_capture_options { _IOR('v', BASE_VIDIOC_PRIVATE + 5, struct atomisp_ee_config) #define ATOMISP_IOC_S_EE \ _IOW('v', BASE_VIDIOC_PRIVATE + 5, struct atomisp_ee_config) -/* Digital Image Stabilization: +/* + * Digital Image Stabilization: * 1. get dis statistics: reads DIS statistics from ISP (every frame) * 2. set dis coefficients: set DIS filter coefficients (one time) * 3. set dis motion vector: set motion vector (result of DIS, every frame) @@ -716,54 +722,54 @@ enum atomisp_burst_capture_options { #define ATOMISP_IOC_S_ISP_GDC_TAB \ _IOW('v', BASE_VIDIOC_PRIVATE + 10, struct atomisp_morph_table) -/* macc parameter control*/ +/* MACC parameter control */ #define ATOMISP_IOC_G_ISP_MACC \ _IOR('v', BASE_VIDIOC_PRIVATE + 12, struct atomisp_macc_config) #define ATOMISP_IOC_S_ISP_MACC \ _IOW('v', BASE_VIDIOC_PRIVATE + 12, struct atomisp_macc_config) -/* Defect pixel detection & Correction */ +/* Defect pixel detection & correction */ #define ATOMISP_IOC_G_ISP_BAD_PIXEL_DETECTION \ _IOR('v', BASE_VIDIOC_PRIVATE + 13, struct atomisp_dp_config) #define ATOMISP_IOC_S_ISP_BAD_PIXEL_DETECTION \ _IOW('v', BASE_VIDIOC_PRIVATE + 13, struct atomisp_dp_config) -/* False Color Correction */ +/* False color correction */ #define ATOMISP_IOC_G_ISP_FALSE_COLOR_CORRECTION \ _IOR('v', BASE_VIDIOC_PRIVATE + 14, struct atomisp_de_config) #define ATOMISP_IOC_S_ISP_FALSE_COLOR_CORRECTION \ _IOW('v', BASE_VIDIOC_PRIVATE + 14, struct atomisp_de_config) -/* ctc parameter control */ +/* CTC parameter control */ #define ATOMISP_IOC_G_ISP_CTC \ _IOR('v', BASE_VIDIOC_PRIVATE + 15, struct atomisp_ctc_table) #define ATOMISP_IOC_S_ISP_CTC \ _IOW('v', BASE_VIDIOC_PRIVATE + 15, struct atomisp_ctc_table) -/* white balance Correction */ +/* White balance correction */ #define ATOMISP_IOC_G_ISP_WHITE_BALANCE \ _IOR('v', BASE_VIDIOC_PRIVATE + 16, struct atomisp_wb_config) #define ATOMISP_IOC_S_ISP_WHITE_BALANCE \ _IOW('v', BASE_VIDIOC_PRIVATE + 16, struct atomisp_wb_config) -/* fpn table loading */ +/* FPN table loading */ #define ATOMISP_IOC_S_ISP_FPN_TABLE \ _IOW('v', BASE_VIDIOC_PRIVATE + 17, struct v4l2_framebuffer) -/* overlay image loading */ +/* Overlay image loading */ #define ATOMISP_IOC_G_ISP_OVERLAY \ _IOWR('v', BASE_VIDIOC_PRIVATE + 18, struct atomisp_overlay) #define ATOMISP_IOC_S_ISP_OVERLAY \ _IOW('v', BASE_VIDIOC_PRIVATE + 18, struct atomisp_overlay) -/* bcd driver bridge */ +/* BCD driver bridge */ #define ATOMISP_IOC_CAMERA_BRIDGE \ _IOWR('v', BASE_VIDIOC_PRIVATE + 19, struct atomisp_bc_video_package) #define ATOMISP_IOC_S_EXPOSURE \ _IOW('v', BASE_VIDIOC_PRIVATE + 21, struct atomisp_exposure) -/* white balance Correction */ +/* White balance correction */ #define ATOMISP_IOC_G_3A_CONFIG \ _IOR('v', BASE_VIDIOC_PRIVATE + 23, struct atomisp_3a_config) #define ATOMISP_IOC_S_3A_CONFIG \ @@ -773,7 +779,7 @@ enum atomisp_burst_capture_options { #define ATOMISP_IOC_S_ISP_SHD_TAB \ _IOWR('v', BASE_VIDIOC_PRIVATE + 27, struct atomisp_shading_table) -/* Gamma Correction */ +/* Gamma correction */ #define ATOMISP_IOC_G_ISP_GAMMA_CORRECTION \ _IOR('v', BASE_VIDIOC_PRIVATE + 28, struct atomisp_gc_config) @@ -804,7 +810,7 @@ enum atomisp_burst_capture_options { #define ATOMISP_IOC_S_ARRAY_RESOLUTION \ _IOW('v', BASE_VIDIOC_PRIVATE + 45, struct atomisp_resolution) -/* for depth mode sensor frame sync compensation */ +/* For depth mode sensor frame sync compensation */ #define ATOMISP_IOC_G_DEPTH_SYNC_COMP \ _IOR('v', BASE_VIDIOC_PRIVATE + 46, unsigned int) @@ -822,7 +828,7 @@ enum atomisp_burst_capture_options { * _IOW('v', BASE_VIDIOC_PRIVATE + 56, struct atomisp_sensor_regs) */ -/* ISP Private control IDs */ +/* ISP Private control IDs */ #define V4L2_CID_ATOMISP_BAD_PIXEL_DETECTION \ (V4L2_CID_PRIVATE_BASE + 0) #define V4L2_CID_ATOMISP_POSTPROCESS_GDC_CAC \ @@ -836,8 +842,10 @@ enum atomisp_burst_capture_options { #define V4L2_CID_ATOMISP_LOW_LIGHT \ (V4L2_CID_PRIVATE_BASE + 5) -/* Camera class: - * Exposure, Flash and privacy (indicator) light controls, to be upstreamed */ +/* + * Camera class: + * Exposure, Flash and privacy (indicator) light controls, to be upstreamed + */ #define V4L2_CID_CAMERA_LASTP1 (V4L2_CID_CAMERA_CLASS_BASE + 1024) #define V4L2_CID_RUN_MODE (V4L2_CID_CAMERA_LASTP1 + 20) @@ -879,7 +887,7 @@ enum atomisp_burst_capture_options { #define V4L2_EVENT_ATOMISP_ACC_COMPLETE (V4L2_EVENT_PRIVATE_START + 4) #define V4L2_EVENT_ATOMISP_PAUSE_BUFFER (V4L2_EVENT_PRIVATE_START + 5) #define V4L2_EVENT_ATOMISP_CSS_RESET (V4L2_EVENT_PRIVATE_START + 6) -/* Nonstandard color effects for V4L2_CID_COLORFX */ +/* Non-standard color effects for V4L2_CID_COLORFX */ enum { V4L2_COLORFX_SKIN_WHITEN_LOW = 1001, V4L2_COLORFX_SKIN_WHITEN_HIGH = 1002, diff --git a/drivers/staging/media/atomisp/include/linux/atomisp_gmin_platform.h b/drivers/staging/media/atomisp/include/linux/atomisp_gmin_platform.h index 426c5ee4ec18..74092af1c659 100644 --- a/drivers/staging/media/atomisp/include/linux/atomisp_gmin_platform.h +++ b/drivers/staging/media/atomisp/include/linux/atomisp_gmin_platform.h @@ -15,8 +15,7 @@ int atomisp_gmin_remove_subdev(struct v4l2_subdev *sd); int gmin_get_var_int(struct device *dev, bool is_gmin, const char *var, int def); struct camera_sensor_platform_data * -gmin_camera_platform_data( - struct v4l2_subdev *subdev, - enum atomisp_input_format csi_format, - enum atomisp_bayer_order csi_bayer); + gmin_camera_platform_data(struct v4l2_subdev *subdev, + enum atomisp_input_format csi_format, + enum atomisp_bayer_order csi_bayer); #endif diff --git a/drivers/staging/media/atomisp/include/linux/atomisp_platform.h b/drivers/staging/media/atomisp/include/linux/atomisp_platform.h index 6146555fe9cf..0d6f2a4039f1 100644 --- a/drivers/staging/media/atomisp/include/linux/atomisp_platform.h +++ b/drivers/staging/media/atomisp/include/linux/atomisp_platform.h @@ -57,7 +57,8 @@ enum atomisp_input_format { ATOMISP_INPUT_FORMAT_RAW_16, /* RAW data, 16 bits per pixel */ ATOMISP_INPUT_FORMAT_BINARY_8, /* Binary byte stream. */ - /* CSI2-MIPI specific format: Generic short packet data. It is used to + /* + * CSI2-MIPI specific format: Generic short packet data. It is used to * keep the timing information for the opening/closing of shutters, * triggering of flashes and etc. */ @@ -70,18 +71,18 @@ enum atomisp_input_format { ATOMISP_INPUT_FORMAT_GENERIC_SHORT7, /* Generic Short Packet Code 7 */ ATOMISP_INPUT_FORMAT_GENERIC_SHORT8, /* Generic Short Packet Code 8 */ - /* CSI2-MIPI specific format: YUV data. - */ - ATOMISP_INPUT_FORMAT_YUV420_8_SHIFT, /* YUV420 8-bit (Chroma Shifted - Pixel Sampling) */ - ATOMISP_INPUT_FORMAT_YUV420_10_SHIFT, /* YUV420 8-bit (Chroma Shifted - Pixel Sampling) */ + /* YUV data */ + /* YUV420 8-bit (Chroma Shifted Pixel Sampling) */ + ATOMISP_INPUT_FORMAT_YUV420_8_SHIFT, + /* YUV420 10-bit (Chroma Shifted Pixel Sampling) */ + ATOMISP_INPUT_FORMAT_YUV420_10_SHIFT, - /* CSI2-MIPI specific format: Generic long packet data - */ - ATOMISP_INPUT_FORMAT_EMBEDDED, /* Embedded 8-bit non Image Data */ + /* CSI2-MIPI specific format: Generic long packet data */ + /* Embedded 8-bit non Image Data */ + ATOMISP_INPUT_FORMAT_EMBEDDED, - /* CSI2-MIPI specific format: User defined byte-based data. For example, + /* + * User defined byte-based data. For example, * the data transmitter (e.g. the SoC sensor) can keep the JPEG data as * the User Defined Data Type 4 and the MPEG data as the * User Defined Data Type 7. @@ -105,9 +106,9 @@ struct intel_v4l2_subdev_table { }; /* - * Sensor of external ISP can send multiple streams with different mipi data + * Sensor of external ISP can send multiple streams with different MIPI data * type in the same virtual channel. This information needs to come from the - * sensor or external ISP + * sensor or external ISP. */ struct atomisp_isys_config_info { u8 input_format; @@ -118,16 +119,17 @@ struct atomisp_isys_config_info { struct atomisp_input_stream_info { enum atomisp_input_stream_id stream; u8 enable; - /* Sensor driver fills ch_id with the id - of the virtual channel. */ + /* Sensor driver fills ch_id with the id of the virtual channel. */ u8 ch_id; - /* Tells how many streams in this virtual channel. If 0 ignore rest - * and the input format will be from mipi_info */ + /* + * Tells the number of streams in this virtual channel. If 0, ignore the + * rest and the input format will be from mipi_info. + */ u8 isys_configs; /* - * if more isys_configs is more than 0, sensor needs to configure the - * input format differently. width and height can be 0. If width and - * height is not zero, then the corresponding data needs to be set + * If isys_configs is greater than 0, sensor needs to configure the + * input format differently. Width and height can be 0. If width and + * height are not zero, then the corresponding data needs to be set. */ struct atomisp_isys_config_info isys_info[MAX_STREAMS_PER_CHANNEL]; }; diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c index fec369575d88..6cd500d9fd26 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c +++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c @@ -811,7 +811,7 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error, /* New global dvs 6axis config should be blocked * here if there's a buffer with per-frame parameters * pending in CSS frame buffer queue. - * This is to aviod zooming vibration since global + * This is to avoid zooming vibration since global * parameters take effect immediately while * per-frame parameters are taken after previous * buffers in CSS got processed. @@ -974,7 +974,7 @@ irqreturn_t atomisp_isr_thread(int irq, void *isp_ptr) * to a FIFO, then process the event in the FIFO. * This will not have issue in single stream mode, but it do have some * issue in multiple stream case. The issue is that - * ia_css_pipe_dequeue_buffer() will not return the corrent buffer in + * ia_css_pipe_dequeue_buffer() will not return the correct buffer in * a specific pipe. * * This is due to ia_css_pipe_dequeue_buffer() does not take the @@ -1380,8 +1380,10 @@ static void atomisp_update_grid_info(struct atomisp_sub_device *asd, if (atomisp_css_get_grid_info(asd, pipe_id)) return; - /* We must free all buffers because they no longer match - the grid size. */ + /* + * We must free all buffers because they no longer match + * the grid size. + */ atomisp_css_free_stat_buffers(asd); err = atomisp_alloc_css_stat_bufs(asd, ATOMISP_INPUT_STREAM_GENERAL); @@ -1393,8 +1395,10 @@ static void atomisp_update_grid_info(struct atomisp_sub_device *asd, if (atomisp_alloc_3a_output_buf(asd)) { /* Failure for 3A buffers does not influence DIS buffers */ if (asd->params.s3a_output_bytes != 0) { - /* For SOC sensor happens s3a_output_bytes == 0, - * using if condition to exclude false error log */ + /* + * For SOC sensor happens s3a_output_bytes == 0, + * using if condition to exclude false error log + */ dev_err(isp->dev, "Failed to allocate memory for 3A statistics\n"); } goto err; @@ -1575,7 +1579,7 @@ int atomisp_set_dis_vector(struct atomisp_sub_device *asd, } /* - * Function to set/get image stablization statistics + * Function to set/get image stabilization statistics */ int atomisp_get_dis_stat(struct atomisp_sub_device *asd, struct atomisp_dis_statistics *stats) @@ -1686,9 +1690,11 @@ int atomisp_3a_stat(struct atomisp_sub_device *asd, int flag, return -EINVAL; if (atomisp_compare_grid(asd, &config->grid_info) != 0) { - /* If the grid info in the argument differs from the current - grid info, we tell the caller to reset the grid size and - try again. */ + /* + * If the grid info in the argument differs from the current + * grid info, we tell the caller to reset the grid size and + * try again. + */ return -EAGAIN; } @@ -1762,15 +1768,13 @@ int atomisp_calculate_real_zoom_region(struct atomisp_sub_device *asd, return -EINVAL; } - if (dz_config->zoom_region.resolution.width - == asd->sensor_array_res.width - || dz_config->zoom_region.resolution.height - == asd->sensor_array_res.height) { + if (dz_config->zoom_region.width == asd->sensor_array_res.width || + dz_config->zoom_region.height == asd->sensor_array_res.height) { /*no need crop region*/ - dz_config->zoom_region.origin.x = 0; - dz_config->zoom_region.origin.y = 0; - dz_config->zoom_region.resolution.width = eff_res.width; - dz_config->zoom_region.resolution.height = eff_res.height; + dz_config->zoom_region.left = 0; + dz_config->zoom_region.top = 0; + dz_config->zoom_region.width = eff_res.width; + dz_config->zoom_region.height = eff_res.height; return 0; } @@ -1781,18 +1785,14 @@ int atomisp_calculate_real_zoom_region(struct atomisp_sub_device *asd, */ if (!IS_ISP2401) { - dz_config->zoom_region.origin.x = dz_config->zoom_region.origin.x - * eff_res.width - / asd->sensor_array_res.width; - dz_config->zoom_region.origin.y = dz_config->zoom_region.origin.y - * eff_res.height - / asd->sensor_array_res.height; - dz_config->zoom_region.resolution.width = dz_config->zoom_region.resolution.width - * eff_res.width - / asd->sensor_array_res.width; - dz_config->zoom_region.resolution.height = dz_config->zoom_region.resolution.height - * eff_res.height - / asd->sensor_array_res.height; + dz_config->zoom_region.left = dz_config->zoom_region.left * + eff_res.width / asd->sensor_array_res.width; + dz_config->zoom_region.top = dz_config->zoom_region.top * + eff_res.height / asd->sensor_array_res.height; + dz_config->zoom_region.width = dz_config->zoom_region.width * + eff_res.width / asd->sensor_array_res.width; + dz_config->zoom_region.height = dz_config->zoom_region.height * + eff_res.height / asd->sensor_array_res.height; /* * Set same ratio of crop region resolution and current pipe output * resolution @@ -1819,62 +1819,62 @@ int atomisp_calculate_real_zoom_region(struct atomisp_sub_device *asd, - asd->sensor_array_res.width * out_res.height / out_res.width; h_offset = h_offset / 2; - if (dz_config->zoom_region.origin.y < h_offset) - dz_config->zoom_region.origin.y = 0; + if (dz_config->zoom_region.top < h_offset) + dz_config->zoom_region.top = 0; else - dz_config->zoom_region.origin.y = dz_config->zoom_region.origin.y - h_offset; + dz_config->zoom_region.top = + dz_config->zoom_region.top - h_offset; w_offset = 0; } else { w_offset = asd->sensor_array_res.width - asd->sensor_array_res.height * out_res.width / out_res.height; w_offset = w_offset / 2; - if (dz_config->zoom_region.origin.x < w_offset) - dz_config->zoom_region.origin.x = 0; + if (dz_config->zoom_region.left < w_offset) + dz_config->zoom_region.left = 0; else - dz_config->zoom_region.origin.x = dz_config->zoom_region.origin.x - w_offset; + dz_config->zoom_region.left = + dz_config->zoom_region.left - w_offset; h_offset = 0; } - dz_config->zoom_region.origin.x = dz_config->zoom_region.origin.x - * eff_res.width - / (asd->sensor_array_res.width - 2 * w_offset); - dz_config->zoom_region.origin.y = dz_config->zoom_region.origin.y - * eff_res.height - / (asd->sensor_array_res.height - 2 * h_offset); - dz_config->zoom_region.resolution.width = dz_config->zoom_region.resolution.width - * eff_res.width - / (asd->sensor_array_res.width - 2 * w_offset); - dz_config->zoom_region.resolution.height = dz_config->zoom_region.resolution.height - * eff_res.height - / (asd->sensor_array_res.height - 2 * h_offset); - } - - if (out_res.width * dz_config->zoom_region.resolution.height - > dz_config->zoom_region.resolution.width * out_res.height) { - dz_config->zoom_region.resolution.height = - dz_config->zoom_region.resolution.width - * out_res.height / out_res.width; + dz_config->zoom_region.left = dz_config->zoom_region.left * + eff_res.width / + (asd->sensor_array_res.width - 2 * w_offset); + dz_config->zoom_region.top = dz_config->zoom_region.top * + eff_res.height / + (asd->sensor_array_res.height - 2 * h_offset); + dz_config->zoom_region.width = dz_config->zoom_region.width * + eff_res.width / + (asd->sensor_array_res.width - 2 * w_offset); + dz_config->zoom_region.height = dz_config->zoom_region.height * + eff_res.height / + (asd->sensor_array_res.height - 2 * h_offset); + } + + if (out_res.width * dz_config->zoom_region.height > + dz_config->zoom_region.width * out_res.height) { + dz_config->zoom_region.height = dz_config->zoom_region.width * + out_res.height / out_res.width; } else { - dz_config->zoom_region.resolution.width = - dz_config->zoom_region.resolution.height - * out_res.width / out_res.height; + dz_config->zoom_region.width = dz_config->zoom_region.height * + out_res.width / out_res.height; } dev_dbg(asd->isp->dev, "%s crop region:(%d,%d),(%d,%d) eff_res(%d, %d) array_size(%d,%d) out_res(%d, %d)\n", - __func__, dz_config->zoom_region.origin.x, - dz_config->zoom_region.origin.y, - dz_config->zoom_region.resolution.width, - dz_config->zoom_region.resolution.height, + __func__, dz_config->zoom_region.left, + dz_config->zoom_region.top, + dz_config->zoom_region.width, + dz_config->zoom_region.height, eff_res.width, eff_res.height, asd->sensor_array_res.width, asd->sensor_array_res.height, out_res.width, out_res.height); - if ((dz_config->zoom_region.origin.x + - dz_config->zoom_region.resolution.width + if ((dz_config->zoom_region.left + + dz_config->zoom_region.width > eff_res.width) || - (dz_config->zoom_region.origin.y + - dz_config->zoom_region.resolution.height + (dz_config->zoom_region.top + + dz_config->zoom_region.height > eff_res.height)) return -EINVAL; @@ -1899,10 +1899,10 @@ static bool atomisp_check_zoom_region( config.width = asd->sensor_array_res.width; config.height = asd->sensor_array_res.height; - w = dz_config->zoom_region.origin.x + - dz_config->zoom_region.resolution.width; - h = dz_config->zoom_region.origin.y + - dz_config->zoom_region.resolution.height; + w = dz_config->zoom_region.left + + dz_config->zoom_region.width; + h = dz_config->zoom_region.top + + dz_config->zoom_region.height; if ((w <= config.width) && (h <= config.height) && w > 0 && h > 0) flag = true; @@ -1910,10 +1910,10 @@ static bool atomisp_check_zoom_region( /* setting error zoom region */ dev_err(asd->isp->dev, "%s zoom region ERROR:dz_config:(%d,%d),(%d,%d)array_res(%d, %d)\n", - __func__, dz_config->zoom_region.origin.x, - dz_config->zoom_region.origin.y, - dz_config->zoom_region.resolution.width, - dz_config->zoom_region.resolution.height, + __func__, dz_config->zoom_region.left, + dz_config->zoom_region.top, + dz_config->zoom_region.width, + dz_config->zoom_region.height, config.width, config.height); return flag; @@ -2032,8 +2032,8 @@ static unsigned int long copy_from_compatible(void *to, const void *from, { if (from_user) return copy_from_user(to, (void __user *)from, n); - else - memcpy(to, from, n); + + memcpy(to, from, n); return 0; } @@ -2462,9 +2462,11 @@ int atomisp_css_cp_dvs2_coefs(struct atomisp_sub_device *asd, if (sizeof(*cur) != sizeof(coefs->grid) || memcmp(&coefs->grid, cur, sizeof(coefs->grid))) { dev_err(asd->isp->dev, "dvs grid mismatch!\n"); - /* If the grid info in the argument differs from the current - grid info, we tell the caller to reset the grid size and - try again. */ + /* + * If the grid info in the argument differs from the current + * grid info, we tell the caller to reset the grid size and + * try again. + */ return -EAGAIN; } @@ -3025,9 +3027,11 @@ int atomisp_param(struct atomisp_sub_device *asd, int flag, atomisp_curr_user_grid_info(asd, &config->info); - /* We always return the resolution and stride even if there is + /* + * We always return the resolution and stride even if there is * no valid metadata. This allows the caller to get the - * information needed to allocate user-space buffers. */ + * information needed to allocate user-space buffers. + */ config->metadata_config.metadata_height = asd-> stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream_info. metadata_info.resolution.height; @@ -3232,7 +3236,7 @@ int atomisp_bad_pixel_param(struct atomisp_sub_device *asd, int flag, } /* - * Function to enable/disable video image stablization + * Function to enable/disable video image stabilization */ int atomisp_video_stable(struct atomisp_sub_device *asd, int flag, __s32 *value) @@ -3277,8 +3281,10 @@ atomisp_bytesperline_to_padded_width(unsigned int bytesperline, return bytesperline / 2; case IA_CSS_FRAME_FORMAT_RGBA888: return bytesperline / 4; - /* The following cases could be removed, but we leave them - in to document the formats that are included. */ + /* + * The following cases could be removed, but we leave them + * in to document the formats that are included. + */ case IA_CSS_FRAME_FORMAT_NV11: case IA_CSS_FRAME_FORMAT_NV12: case IA_CSS_FRAME_FORMAT_NV16: @@ -3314,9 +3320,11 @@ atomisp_v4l2_framebuffer_to_css_frame(const struct v4l2_framebuffer *arg, padded_width = atomisp_bytesperline_to_padded_width( arg->fmt.bytesperline, sh_format); - /* Note: the padded width on an ia_css_frame is in elements, not in - bytes. The RAW frame we use here should always be a 16bit RAW - frame. This is why we bytesperline/2 is equal to the padded with */ + /* + * Note: the padded width on an ia_css_frame is in elements, not in + * bytes. The RAW frame we use here should always be a 16bit RAW + * frame. This is why we bytesperline/2 is equal to the padded with + */ if (ia_css_frame_allocate(&res, arg->fmt.width, arg->fmt.height, sh_format, padded_width, 0)) { ret = -ENOMEM; @@ -3364,10 +3372,8 @@ int atomisp_fixed_pattern_table(struct atomisp_sub_device *asd, if (ret) return ret; - if (sh_css_set_black_frame(asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream, - raw_black_frame) != 0) - return -ENOMEM; - + ret = sh_css_set_black_frame(asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream, + raw_black_frame); ia_css_frame_free(raw_black_frame); return ret; } @@ -3925,8 +3931,10 @@ static inline int atomisp_set_sensor_mipi_to_isp( asd->stream_env[stream_id].isys_info[1].height); } - /* Compatibility for sensors which provide no media bus code - * in s_mbus_framefmt() nor support pad formats. */ + /* + * Compatibility for sensors which provide no media bus code + * in s_mbus_framefmt() nor support pad formats. + */ if (mipi_info && mipi_info->input_format != -1) { bayer_order = mipi_info->raw_bayer_order; @@ -4003,7 +4011,7 @@ static int css_input_resolution_changed(struct atomisp_sub_device *asd, struct atomisp_metadata_buf *md_buf = NULL, *_md_buf; unsigned int i; - dev_dbg(asd->isp->dev, "css_input_resolution_changed to %ux%u\n", + dev_dbg(asd->isp->dev, "%s: to %ux%u\n", __func__, ffmt->width, ffmt->height); if (IS_ISP2401) @@ -4384,8 +4392,10 @@ int atomisp_set_fmt(struct video_device *vdev, struct v4l2_format *f) ATOMISP_SUBDEV_PAD_SINK, V4L2_SEL_TGT_CROP); - /* Try to enable YUV downscaling if ISP input is 10 % (either - * width or height) bigger than the desired result. */ + /* + * Try to enable YUV downscaling if ISP input is 10 % (either + * width or height) bigger than the desired result. + */ if (!IS_MOFD || isp_sink_crop.width * 9 / 10 < f->fmt.pix.width || isp_sink_crop.height * 9 / 10 < f->fmt.pix.height || diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.h b/drivers/staging/media/atomisp/pci/atomisp_cmd.h index 82199dc9284e..d3d1f2574e77 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_cmd.h +++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.h @@ -153,7 +153,7 @@ int atomisp_bad_pixel(struct atomisp_sub_device *asd, int flag, int atomisp_bad_pixel_param(struct atomisp_sub_device *asd, int flag, struct atomisp_dp_config *config); -/* Function to enable/disable video image stablization */ +/* Function to enable/disable video image stabilization */ int atomisp_video_stable(struct atomisp_sub_device *asd, int flag, __s32 *value); diff --git a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c index be5f37f4a6fd..0ee52637ea30 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c +++ b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c @@ -27,6 +27,7 @@ #include <linux/io.h> #include <linux/pm_runtime.h> +#include <linux/string_choices.h> /* Assume max number of ACC stages */ #define MAX_ACC_STAGES 20 @@ -841,7 +842,7 @@ int atomisp_css_irq_enable(struct atomisp_device *isp, { dev_dbg(isp->dev, "%s: css irq info 0x%08x: %s (%d).\n", __func__, info, - enable ? "enable" : "disable", enable); + str_enable_disable(enable), enable); if (ia_css_irq_enable(info, enable)) { dev_warn(isp->dev, "%s:Invalid irq info: 0x%08x when %s.\n", __func__, info, @@ -1116,8 +1117,11 @@ int atomisp_css_allocate_stat_buffers(struct atomisp_sub_device *asd, dvs_grid_info); if (!dis_buf->dis_data) { dev_err(isp->dev, "dvs buf allocation failed.\n"); - if (s3a_buf) + if (s3a_buf) { + hmm_vunmap(s3a_buf->s3a_data->data_ptr); + ia_css_isp_3a_statistics_map_free(s3a_buf->s3a_map); ia_css_isp_3a_statistics_free(s3a_buf->s3a_data); + } return -EINVAL; } @@ -1131,10 +1135,16 @@ int atomisp_css_allocate_stat_buffers(struct atomisp_sub_device *asd, md_buf->metadata = ia_css_metadata_allocate( &asd->stream_env[stream_id].stream_info.metadata_info); if (!md_buf->metadata) { - if (s3a_buf) + if (s3a_buf) { + hmm_vunmap(s3a_buf->s3a_data->data_ptr); + ia_css_isp_3a_statistics_map_free(s3a_buf->s3a_map); ia_css_isp_3a_statistics_free(s3a_buf->s3a_data); - if (dis_buf) + } + if (dis_buf) { + hmm_vunmap(dis_buf->dis_data->data_ptr); + ia_css_isp_dvs_statistics_map_free(dis_buf->dvs_map); ia_css_isp_dvs2_statistics_free(dis_buf->dis_data); + } dev_err(isp->dev, "metadata buf allocation failed.\n"); return -EINVAL; } @@ -1957,7 +1967,7 @@ static void __configure_capture_pp_input(struct atomisp_sub_device *asd, /* * For CSS2.1, preview pipe could support bayer downscaling, yuv decimation and - * yuv downscaling, which needs addtional configurations. + * yuv downscaling, which needs additional configurations. */ static void __configure_preview_pp_input(struct atomisp_sub_device *asd, unsigned int width, unsigned int height, @@ -2044,7 +2054,7 @@ static void __configure_preview_pp_input(struct atomisp_sub_device *asd, } } /* - * calculate YUV Decimation, YUV downscaling facor: + * calculate YUV Decimation, YUV downscaling factor: * YUV Downscaling factor must not exceed 2. * YUV Decimation factor could be 2, 4. */ @@ -2085,7 +2095,7 @@ static void __configure_preview_pp_input(struct atomisp_sub_device *asd, /* * For CSS2.1, offline video pipe could support bayer decimation, and - * yuv downscaling, which needs addtional configurations. + * yuv downscaling, which needs additional configurations. */ static void __configure_video_pp_input(struct atomisp_sub_device *asd, unsigned int width, unsigned int height, @@ -3002,7 +3012,7 @@ int atomisp_css_get_zoom_factor(struct atomisp_sub_device *asd, } /* - * Function to set/get image stablization statistics + * Function to set/get image stabilization statistics */ int atomisp_css_get_dis_stat(struct atomisp_sub_device *asd, struct atomisp_dis_statistics *stats) diff --git a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.h b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.h index 75781807544a..5188df4f469c 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.h +++ b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.h @@ -40,7 +40,7 @@ enum atomisp_css_stream_state { }; /* - * Sensor of external ISP can send multiple steams with different mipi data + * Sensor of external ISP can send multiple streams with different mipi data * type in the same virtual channel. This information needs to come from the * sensor or external ISP */ diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c b/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c index ba61cc28fac1..cca91c6d71a5 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c +++ b/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c @@ -113,7 +113,7 @@ static char *gmin_cfg_get_dsm(struct acpi_device *adev, const char *key) if (!obj) return NULL; - for (i = 0; i < obj->package.count - 1; i += 2) { + for (i = 0; i + 1 < obj->package.count; i += 2) { key_el = &obj->package.elements[i + 0]; val_el = &obj->package.elements[i + 1]; diff --git a/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c b/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c index 4026e98c5845..a1d3a16921ea 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c +++ b/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c @@ -11,8 +11,8 @@ #include <linux/mfd/intel_soc_pmic.h> #include <linux/regulator/consumer.h> #include <linux/gpio/consumer.h> -#include <linux/gpio.h> #include <linux/platform_device.h> +#include <linux/string_choices.h> #include "../../include/linux/atomisp_platform.h" #include "../../include/linux/atomisp_gmin_platform.h" @@ -917,7 +917,7 @@ static int gmin_acpi_pm_ctrl(struct v4l2_subdev *subdev, int on) return 0; dev_dbg(subdev->dev, "Setting power state to %s\n", - on ? "on" : "off"); + str_on_off(on)); if (on) ret = acpi_device_set_power(adev, @@ -930,7 +930,7 @@ static int gmin_acpi_pm_ctrl(struct v4l2_subdev *subdev, int on) gs->clock_on = on; else dev_err(subdev->dev, "Couldn't set power state to %s\n", - on ? "on" : "off"); + str_on_off(on)); return ret; } diff --git a/drivers/staging/media/atomisp/pci/atomisp_subdev.c b/drivers/staging/media/atomisp/pci/atomisp_subdev.c index 3d56ca83ecb7..9de9cd884d99 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_subdev.c +++ b/drivers/staging/media/atomisp/pci/atomisp_subdev.c @@ -180,8 +180,8 @@ static int isp_subdev_enum_mbus_code(struct v4l2_subdev *sd, return 0; } -static int isp_subdev_validate_rect(struct v4l2_subdev *sd, uint32_t pad, - uint32_t target) +static int isp_subdev_validate_rect(struct v4l2_subdev *sd, u32 pad, + u32 target) { switch (pad) { case ATOMISP_SUBDEV_PAD_SINK: @@ -203,8 +203,8 @@ static int isp_subdev_validate_rect(struct v4l2_subdev *sd, uint32_t pad, struct v4l2_rect *atomisp_subdev_get_rect(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, - u32 which, uint32_t pad, - uint32_t target) + u32 which, u32 pad, + u32 target) { struct atomisp_sub_device *isp_sd = v4l2_get_subdevdata(sd); @@ -229,8 +229,8 @@ struct v4l2_rect *atomisp_subdev_get_rect(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *atomisp_subdev_get_ffmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, uint32_t which, - uint32_t pad) + struct v4l2_subdev_state *sd_state, u32 which, + u32 pad) { struct atomisp_sub_device *isp_sd = v4l2_get_subdevdata(sd); @@ -242,7 +242,7 @@ struct v4l2_mbus_framefmt static void isp_get_fmt_rect(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, - uint32_t which, + u32 which, struct v4l2_mbus_framefmt **ffmt, struct v4l2_rect *crop[ATOMISP_SUBDEV_PADS_NUM], struct v4l2_rect *comp[ATOMISP_SUBDEV_PADS_NUM]) @@ -291,7 +291,7 @@ static const char *atomisp_pad_str(unsigned int pad) int atomisp_subdev_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, - u32 which, uint32_t pad, uint32_t target, + u32 which, u32 pad, u32 target, u32 flags, struct v4l2_rect *r) { struct atomisp_sub_device *isp_sd = v4l2_get_subdevdata(sd); @@ -467,7 +467,7 @@ static int isp_subdev_set_selection(struct v4l2_subdev *sd, void atomisp_subdev_set_ffmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, - uint32_t which, + u32 which, u32 pad, struct v4l2_mbus_framefmt *ffmt) { struct atomisp_sub_device *isp_sd = v4l2_get_subdevdata(sd); @@ -808,7 +808,7 @@ static int isp_subdev_init_entities(struct atomisp_sub_device *asd) int ret; v4l2_subdev_init(sd, &isp_subdev_v4l2_ops); - sprintf(sd->name, "Atom ISP"); + strscpy(sd->name, "Atom ISP"); v4l2_set_subdevdata(sd, asd); sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; diff --git a/drivers/staging/media/atomisp/pci/atomisp_subdev.h b/drivers/staging/media/atomisp/pci/atomisp_subdev.h index e1d0168cb91d..b12bb65be3f2 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_subdev.h +++ b/drivers/staging/media/atomisp/pci/atomisp_subdev.h @@ -315,20 +315,20 @@ bool atomisp_subdev_format_conversion(struct atomisp_sub_device *asd); /* Get pointer to appropriate format */ struct v4l2_mbus_framefmt *atomisp_subdev_get_ffmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, uint32_t which, - uint32_t pad); + struct v4l2_subdev_state *sd_state, u32 which, + u32 pad); struct v4l2_rect *atomisp_subdev_get_rect(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, - u32 which, uint32_t pad, - uint32_t target); + u32 which, u32 pad, + u32 target); int atomisp_subdev_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, - u32 which, uint32_t pad, uint32_t target, + u32 which, u32 pad, u32 target, u32 flags, struct v4l2_rect *r); /* Actually set the format */ void atomisp_subdev_set_ffmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, - uint32_t which, + u32 which, u32 pad, struct v4l2_mbus_framefmt *ffmt); void atomisp_subdev_cleanup_pending_events(struct atomisp_sub_device *asd); diff --git a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c index 900a67552d6a..812230397409 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c +++ b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c @@ -16,6 +16,7 @@ #include <linux/dmi.h> #include <linux/interrupt.h> #include <linux/bits.h> +#include <linux/string_choices.h> #include <media/v4l2-fwnode.h> #include <asm/iosf_mbi.h> @@ -527,7 +528,7 @@ static int atomisp_mrfld_power(struct atomisp_device *isp, bool enable) u32 val = enable ? MRFLD_ISPSSPM0_IUNIT_POWER_ON : MRFLD_ISPSSPM0_IUNIT_POWER_OFF; - dev_dbg(isp->dev, "IUNIT power-%s.\n", enable ? "on" : "off"); + dev_dbg(isp->dev, "IUNIT power-%s.\n", str_on_off(enable)); /* WA for P-Unit, if DVFS enabled, ISP timeout observed */ if (IS_CHT && enable && !isp->pm_only) { @@ -569,7 +570,7 @@ static int atomisp_mrfld_power(struct atomisp_device *isp, bool enable) usleep_range(100, 150); } while (1); - dev_err(isp->dev, "IUNIT power-%s timeout.\n", enable ? "on" : "off"); + dev_err(isp->dev, "IUNIT power-%s timeout.\n", str_on_off(enable)); return -EBUSY; } diff --git a/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf.h b/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf.h index 86300991d30e..4732b45b25ee 100644 --- a/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf.h +++ b/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf.h @@ -139,21 +139,16 @@ static inline uint8_t ia_css_circbuf_get_pos_at_offset( u32 base, int offset) { - u8 dest; - OP___assert(cb); OP___assert(cb->desc); OP___assert(cb->desc->size > 0); /* step 1: adjudst the offset */ - while (offset < 0) { + while (offset < 0) offset += cb->desc->size; - } /* step 2: shift and round by the upper limit */ - dest = OP_std_modadd(base, offset, cb->desc->size); - - return dest; + return (base + offset) % cb->desc->size; } /** diff --git a/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_desc.h b/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_desc.h index 5645a7bf493c..64f754f1d49b 100644 --- a/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_desc.h +++ b/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_desc.h @@ -47,7 +47,7 @@ static inline bool ia_css_circbuf_desc_is_full( ia_css_circbuf_desc_t *cb_desc) { OP___assert(cb_desc); - return (OP_std_modadd(cb_desc->end, 1, cb_desc->size) == cb_desc->start); + return ((cb_desc->end + 1) % cb_desc->size) == cb_desc->start; } /** @@ -78,20 +78,15 @@ static inline uint8_t ia_css_circbuf_desc_get_pos_at_offset( u32 base, int offset) { - u8 dest; - OP___assert(cb_desc); OP___assert(cb_desc->size > 0); /* step 1: adjust the offset */ - while (offset < 0) { + while (offset < 0) offset += cb_desc->size; - } /* step 2: shift and round by the upper limit */ - dest = OP_std_modadd(base, offset, cb_desc->size); - - return dest; + return (base + offset) % cb_desc->size; } /** diff --git a/drivers/staging/media/atomisp/pci/base/refcount/src/refcount.c b/drivers/staging/media/atomisp/pci/base/refcount/src/refcount.c index 58e4e3173b40..4a8675d0129a 100644 --- a/drivers/staging/media/atomisp/pci/base/refcount/src/refcount.c +++ b/drivers/staging/media/atomisp/pci/base/refcount/src/refcount.c @@ -238,9 +238,8 @@ void ia_css_refcount_clear(s32 id, clear_func clear_func_ptr) hmm_free(entry->data); } - if (entry->count != 0) { + if (entry->count != 0) IA_CSS_WARNING("Ref count for entry %x is not zero!", entry->id); - } assert(entry->count == 0); diff --git a/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_stagedesc.c b/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_stagedesc.c index a9f736398f50..fa2f8ed5b053 100644 --- a/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_stagedesc.c +++ b/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_stagedesc.c @@ -32,9 +32,8 @@ void ia_css_pipe_get_generic_stage_desc( stage_desc->max_input_width = 0; stage_desc->mode = binary->info->sp.pipeline.mode; stage_desc->in_frame = in_frame; - for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) { + for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) stage_desc->out_frame[i] = out_frame[i]; - } stage_desc->vf_frame = vf_frame; ERR: IA_CSS_LEAVE_PRIVATE(""); @@ -59,9 +58,8 @@ void ia_css_pipe_get_firmwares_stage_desc( stage_desc->max_input_width = 0; stage_desc->mode = mode; stage_desc->in_frame = in_frame; - for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) { + for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) stage_desc->out_frame[i] = out_frame[i]; - } stage_desc->vf_frame = vf_frame; } @@ -82,8 +80,7 @@ void ia_css_pipe_get_sp_func_stage_desc( stage_desc->mode = (unsigned int)-1; stage_desc->in_frame = NULL; stage_desc->out_frame[0] = out_frame; - for (i = 1; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) { + for (i = 1; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) stage_desc->out_frame[i] = NULL; - } stage_desc->vf_frame = NULL; } diff --git a/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_util.c b/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_util.c index c7c42b472cc7..6cb3ecbd7297 100644 --- a/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_util.c +++ b/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_util.c @@ -26,9 +26,8 @@ void ia_css_pipe_util_create_output_frames( unsigned int i; assert(frames); - for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) { + for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) frames[i] = NULL; - } } void ia_css_pipe_util_set_output_frames( diff --git a/drivers/staging/media/atomisp/pci/css_2401_system/host/isys_stream2mmio_private.h b/drivers/staging/media/atomisp/pci/css_2401_system/host/isys_stream2mmio_private.h index 3210dd6bf9ca..8e295cd78129 100644 --- a/drivers/staging/media/atomisp/pci/css_2401_system/host/isys_stream2mmio_private.h +++ b/drivers/staging/media/atomisp/pci/css_2401_system/host/isys_stream2mmio_private.h @@ -41,9 +41,8 @@ STORAGE_CLASS_STREAM2MMIO_C void stream2mmio_get_state( * Get the values of the register-set per * stream2mmio-controller sids. */ - for (i = STREAM2MMIO_SID0_ID; i < N_STREAM2MMIO_SID_PROCS[ID]; i++) { + for (i = STREAM2MMIO_SID0_ID; i < N_STREAM2MMIO_SID_PROCS[ID]; i++) stream2mmio_get_sid_state(ID, i, &state->sid_state[i]); - } } /** diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/event_fifo_private.h b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/event_fifo_private.h index 439c69444942..e77d7cf61356 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/event_fifo_private.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/event_fifo_private.h @@ -26,9 +26,8 @@ STORAGE_CLASS_EVENT_C void event_wait_for(const event_ID_t ID) STORAGE_CLASS_EVENT_C void cnd_event_wait_for(const event_ID_t ID, const bool cnd) { - if (cnd) { + if (cnd) event_wait_for(ID); - } } STORAGE_CLASS_EVENT_C hrt_data event_receive_token(const event_ID_t ID) diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gdc.c b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gdc.c index 8bb78b4d7c67..b31e3809c0e4 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gdc.c +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gdc.c @@ -26,9 +26,7 @@ static inline void gdc_reg_store( /* * Exported function implementations */ -void gdc_lut_store( - const gdc_ID_t ID, - const int data[4][HRT_GDC_N]) +void gdc_lut_store(const gdc_ID_t ID, const int data[4][HRT_GDC_N]) { unsigned int i, lut_offset = HRT_GDC_LUT_IDX; @@ -36,20 +34,17 @@ void gdc_lut_store( assert(HRT_GDC_LUT_COEFF_OFFSET <= (4 * sizeof(hrt_data))); for (i = 0; i < HRT_GDC_N; i++) { - hrt_data entry_0 = data[0][i] & HRT_GDC_BCI_COEF_MASK; - hrt_data entry_1 = data[1][i] & HRT_GDC_BCI_COEF_MASK; - hrt_data entry_2 = data[2][i] & HRT_GDC_BCI_COEF_MASK; - hrt_data entry_3 = data[3][i] & HRT_GDC_BCI_COEF_MASK; + hrt_data entry_0 = data[0][i] & HRT_GDC_BCI_COEF_MASK; + hrt_data entry_1 = data[1][i] & HRT_GDC_BCI_COEF_MASK; + hrt_data entry_2 = data[2][i] & HRT_GDC_BCI_COEF_MASK; + hrt_data entry_3 = data[3][i] & HRT_GDC_BCI_COEF_MASK; - hrt_data word_0 = entry_0 | - (entry_1 << HRT_GDC_LUT_COEFF_OFFSET); - hrt_data word_1 = entry_2 | - (entry_3 << HRT_GDC_LUT_COEFF_OFFSET); + hrt_data word_0 = entry_0 | (entry_1 << HRT_GDC_LUT_COEFF_OFFSET); + hrt_data word_1 = entry_2 | (entry_3 << HRT_GDC_LUT_COEFF_OFFSET); gdc_reg_store(ID, lut_offset++, word_0); gdc_reg_store(ID, lut_offset++, word_1); } - return; } /* @@ -85,8 +80,7 @@ void gdc_lut_convert_to_isp_format(const int in_lut[4][HRT_GDC_N], } } -int gdc_get_unity( - const gdc_ID_t ID) +int gdc_get_unity(const gdc_ID_t ID) { assert(ID < N_GDC_ID); (void)ID; diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_formatter.c b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_formatter.c index 40b3f1e48c56..c353461dcdf2 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_formatter.c +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_formatter.c @@ -62,9 +62,8 @@ void input_formatter_rst( * WICH USES THE STREAM2MEMRY BLOCK. * MUST BE FIXED PROPERLY */ - if (!HIVE_IF_BIN_COPY[ID]) { + if (!HIVE_IF_BIN_COPY[ID]) input_formatter_reg_store(ID, addr, rst); - } return; } diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_system.c b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_system.c index 9f1199c4761c..68b0ad27615d 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_system.c +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_system.c @@ -138,11 +138,10 @@ void receiver_port_enable( hrt_data reg = receiver_port_reg_load(ID, port_ID, _HRT_CSS_RECEIVER_DEVICE_READY_REG_IDX); - if (cnd) { + if (cnd) reg |= 0x01; - } else { + else reg &= ~0x01; - } receiver_port_reg_store(ID, port_ID, _HRT_CSS_RECEIVER_DEVICE_READY_REG_IDX, reg); @@ -194,9 +193,8 @@ static void receiver_rst( assert(ID < N_RX_ID); // Disable all ports. - for (port_id = MIPI_PORT0_ID; port_id < N_MIPI_PORT_ID; port_id++) { + for (port_id = MIPI_PORT0_ID; port_id < N_MIPI_PORT_ID; port_id++) receiver_port_enable(ID, port_id, false); - } // AM: Additional actions for stopping receiver? } @@ -830,15 +828,13 @@ input_system_err_t input_system_configuration_commit(void) // The last configuration step is to configure the input buffer. input_system_err_t error = input_buffer_configuration(); - if (error != INPUT_SYSTEM_ERR_NO_ERROR) { + if (error != INPUT_SYSTEM_ERR_NO_ERROR) return error; - } // Translate the whole configuration into registers. error = configuration_to_registers(); - if (error != INPUT_SYSTEM_ERR_NO_ERROR) { + if (error != INPUT_SYSTEM_ERR_NO_ERROR) return error; - } // Translate the whole configuration into ctrl commands etc. diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/irq.c b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/irq.c index b66560bca625..61dea386361a 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/irq.c +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/irq.c @@ -55,9 +55,8 @@ void irq_clear_all( assert(ID < N_IRQ_ID); assert(IRQ_N_CHANNEL[ID] <= HRT_DATA_WIDTH); - if (IRQ_N_CHANNEL[ID] < HRT_DATA_WIDTH) { + if (IRQ_N_CHANNEL[ID] < HRT_DATA_WIDTH) mask = ~((~(hrt_data)0) >> IRQ_N_CHANNEL[ID]); - } irq_reg_store(ID, _HRT_IRQ_CONTROLLER_CLEAR_REG_IDX, mask); @@ -115,9 +114,9 @@ void irq_enable_pulse( { unsigned int edge_out = 0x0; - if (pulse) { + if (pulse) edge_out = 0xffffffff; - } + /* output is given as edge, not pulse */ irq_reg_store(ID, _HRT_IRQ_CONTROLLER_EDGE_NOT_PULSE_REG_IDX, edge_out); @@ -259,9 +258,9 @@ void virq_clear_all(void) { irq_ID_t irq_id; - for (irq_id = (irq_ID_t)0; irq_id < N_IRQ_ID; irq_id++) { + for (irq_id = (irq_ID_t)0; irq_id < N_IRQ_ID; irq_id++) irq_clear_all(irq_id); - } + return; } @@ -301,9 +300,9 @@ void virq_clear_info(struct virq_info *irq_info) assert(irq_info); - for (ID = (irq_ID_t)0 ; ID < N_IRQ_ID; ID++) { + for (ID = (irq_ID_t)0 ; ID < N_IRQ_ID; ID++) irq_info->irq_status_reg[ID] = 0; - } + return; } @@ -324,20 +323,17 @@ enum hrt_isp_css_irq_status virq_get_channel_id( break; } - if (idx == IRQ_N_CHANNEL[IRQ0_ID]) { + if (idx == IRQ_N_CHANNEL[IRQ0_ID]) return hrt_isp_css_irq_status_error; - } /* Check whether there are more bits set on device 0 */ - if (irq_status != (1U << idx)) { + if (irq_status != (1U << idx)) status = hrt_isp_css_irq_status_more_irqs; - } /* Check whether we have an IRQ on one of the nested devices */ for (ID = N_IRQ_ID - 1 ; ID > (irq_ID_t)0; ID--) { - if (IRQ_NESTING_ID[ID] == (enum virq_id)idx) { + if (IRQ_NESTING_ID[ID] == (enum virq_id)idx) break; - } } /* If we have a nested IRQ, load that state, discard the device 0 state */ @@ -350,9 +346,8 @@ enum hrt_isp_css_irq_status virq_get_channel_id( break; } - if (idx == IRQ_N_CHANNEL[ID]) { + if (idx == IRQ_N_CHANNEL[ID]) return hrt_isp_css_irq_status_error; - } /* Alternatively check whether there are more bits set on this device */ if (irq_status != (1U << idx)) { @@ -408,9 +403,8 @@ static inline irq_ID_t virq_get_irq_id( assert(channel_ID); for (ID = (irq_ID_t)0 ; ID < N_IRQ_ID; ID++) { - if (irq_ID < IRQ_N_ID_OFFSET[ID + 1]) { + if (irq_ID < IRQ_N_ID_OFFSET[ID + 1]) break; - } } *channel_ID = (unsigned int)irq_ID - IRQ_N_ID_OFFSET[ID]; diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/mmu.c b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/mmu.c index 064e88a5e064..70d118fe1e70 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/mmu.c +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/mmu.c @@ -32,7 +32,6 @@ void mmu_invalidate_cache_all(void) { mmu_ID_t mmu_id; - for (mmu_id = (mmu_ID_t)0; mmu_id < N_MMU_ID; mmu_id++) { + for (mmu_id = (mmu_ID_t)0; mmu_id < N_MMU_ID; mmu_id++) mmu_invalidate_cache(mmu_id); - } } diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/vmem.c b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/vmem.c index 722b684fbc37..1c5c50406633 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/vmem.c +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/vmem.c @@ -168,9 +168,9 @@ static void store_vector( //load_vector (&v[1][0], &to[ISP_NWAY]); /* Fetch the next vector, since it will be overwritten. */ hive_uedge *data = (hive_uedge *)v; - for (i = 0; i < ISP_NWAY; i++) { + for (i = 0; i < ISP_NWAY; i++) hive_sim_wide_pack(data, (hive_wide)&from[i], ISP_VEC_ELEMBITS, i); - } + assert(ISP_BAMEM_BASE[ID] != (hrt_address) - 1); #if !defined(HRT_MEMORY_ACCESS) ia_css_device_store(ISP_BAMEM_BASE[ID] + (unsigned long)to, &v, size); diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h index 1a435a348318..b321f4101193 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h @@ -11,72 +11,61 @@ #include "device_access.h" #include "assert_support.h" -/*! Set the page table base index of MMU[ID] - - \param ID[in] MMU identifier - \param base_index[in] page table base index - - \return none, MMU[ID].page_table_base_index = base_index +/** + * mmu_set_page_table_base_index() - Set the page table base index of MMU[ID] + * @ID: MMU identifier + * @base_index: page table base index + * + * Return: none, MMU[ID].page_table_base_index = base_index */ -void mmu_set_page_table_base_index( - const mmu_ID_t ID, - const hrt_data base_index); - -/*! Get the page table base index of MMU[ID] +void mmu_set_page_table_base_index(const mmu_ID_t ID, const hrt_data base_index); - \param ID[in] MMU identifier - \param base_index[in] page table base index - - \return MMU[ID].page_table_base_index +/** + * mmu_get_page_table_base_index() - Get the page table base index of MMU[ID] + * @ID: MMU identifier + * + * Return: MMU[ID].page_table_base_index */ -hrt_data mmu_get_page_table_base_index( - const mmu_ID_t ID); - -/*! Invalidate the page table cache of MMU[ID] - - \param ID[in] MMU identifier +hrt_data mmu_get_page_table_base_index(const mmu_ID_t ID); - \return none +/** + * mmu_invalidate_cache() - nvalidate the page table cache of MMU[ID] + * @ID: MMU identifier + * + * Return: none */ -void mmu_invalidate_cache( - const mmu_ID_t ID); +void mmu_invalidate_cache(const mmu_ID_t ID); -/*! Invalidate the page table cache of all MMUs - - \return none +/** + * mmu_invalidate_cache_all() - Invalidate the page table cache of all MMUs + * + * Return: none */ void mmu_invalidate_cache_all(void); -/*! Write to a control register of MMU[ID] - - \param ID[in] MMU identifier - \param reg[in] register index - \param value[in] The data to be written - - \return none, MMU[ID].ctrl[reg] = value +/** + * mmu_reg_store() - Write to a control register of MMU[ID] + * @ID: MMU identifier + * @reg: register index + * @value: The data to be written + * + * Return: none, MMU[ID].ctrl[reg] = value */ -static inline void mmu_reg_store( - const mmu_ID_t ID, - const unsigned int reg, - const hrt_data value) +static inline void mmu_reg_store(const mmu_ID_t ID, const unsigned int reg, const hrt_data value) { assert(ID < N_MMU_ID); assert(MMU_BASE[ID] != (hrt_address) - 1); ia_css_device_store_uint32(MMU_BASE[ID] + reg * sizeof(hrt_data), value); - return; } -/*! Read from a control register of MMU[ID] - - \param ID[in] MMU identifier - \param reg[in] register index - \param value[in] The data to be written - - \return MMU[ID].ctrl[reg] +/** + * mmu_reg_load() - Read from a control register of MMU[ID] + * @ID: MMU identifier + * @reg: register index + * + * Return: MMU[ID].ctrl[reg] */ -static inline hrt_data mmu_reg_load( - const mmu_ID_t ID, - const unsigned int reg) +static inline hrt_data mmu_reg_load(const mmu_ID_t ID, const unsigned int reg) { assert(ID < N_MMU_ID); assert(MMU_BASE[ID] != (hrt_address) - 1); diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_include/math_support.h b/drivers/staging/media/atomisp/pci/hive_isp_css_include/math_support.h index 2cb5c986790a..72a070d94736 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_include/math_support.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_include/math_support.h @@ -14,10 +14,4 @@ #define CEIL_MUL(a, b) (CEIL_DIV(a, b) * (b)) #define CEIL_SHIFT(a, b) (((a) + (1 << (b)) - 1) >> (b)) -/* - * For SP and ISP, SDK provides the definition of OP_std_modadd. - * We need it only for host - */ -#define OP_std_modadd(base, offset, size) ((base + offset) % (size)) - #endif /* __MATH_SUPPORT_H */ diff --git a/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c b/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c index 856561e951a5..0a8f401a1ca1 100644 --- a/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c +++ b/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c @@ -620,14 +620,23 @@ static void free_private_bo_pages(struct hmm_buffer_object *bo) /*Allocate pages which will be used only by ISP*/ static int alloc_private_pages(struct hmm_buffer_object *bo) { - const gfp_t gfp = __GFP_NOWARN | __GFP_RECLAIM | __GFP_FS; + unsigned int nr_allocated = 0; + struct page *page; int ret; - ret = alloc_pages_bulk(gfp, bo->pgnr, bo->pages); - if (ret != bo->pgnr) { - free_pages_bulk_array(ret, bo->pages); - dev_err(atomisp_dev, "alloc_pages_bulk() failed\n"); - return -ENOMEM; + nr_allocated = alloc_pages_bulk(GFP_KERNEL, bo->pgnr, bo->pages); + /* + * alloc_pages_bulk() does not try very hard to get pages under memory + * pressure. If necessary fall back to alloc_page(). + */ + while (nr_allocated < bo->pgnr) { + page = alloc_pages(GFP_KERNEL, 0); + if (!page) { + free_pages_bulk_array(nr_allocated, bo->pages); + return -ENOMEM; + } + bo->pages[nr_allocated] = page; + nr_allocated++; } ret = set_pages_array_uc(bo->pages, bo->pgnr); @@ -975,8 +984,7 @@ void hmm_bo_unref(struct hmm_buffer_object *bo) static void hmm_bo_vm_open(struct vm_area_struct *vma) { - struct hmm_buffer_object *bo = - (struct hmm_buffer_object *)vma->vm_private_data; + struct hmm_buffer_object *bo = vma->vm_private_data; check_bo_null_return_void(bo); @@ -993,8 +1001,7 @@ static void hmm_bo_vm_open(struct vm_area_struct *vma) static void hmm_bo_vm_close(struct vm_area_struct *vma) { - struct hmm_buffer_object *bo = - (struct hmm_buffer_object *)vma->vm_private_data; + struct hmm_buffer_object *bo = vma->vm_private_data; check_bo_null_return_void(bo); diff --git a/drivers/staging/media/atomisp/pci/ia_css_types.h b/drivers/staging/media/atomisp/pci/ia_css_types.h index 676d7e20b282..2b7db9cda23a 100644 --- a/drivers/staging/media/atomisp/pci/ia_css_types.h +++ b/drivers/staging/media/atomisp/pci/ia_css_types.h @@ -15,6 +15,8 @@ * directly but still need to forward parameters for it. */ +#include <linux/videodev2.h> + #include <type_support.h> #include "ia_css_frac.h" @@ -428,21 +430,13 @@ struct ia_css_point { }; /** - * This specifies the region - */ -struct ia_css_region { - struct ia_css_point origin; /** Starting point coordinates for the region */ - struct ia_css_resolution resolution; /** Region resolution */ -}; - -/** * Digital zoom: * This feature is currently available only for video, but will become * available for preview and capture as well. * Set the digital zoom factor, this is a logarithmic scale. The actual zoom * factor will be 64/x. * Setting dx or dy to 0 disables digital zoom for that direction. - * New API change for Digital zoom:(added struct ia_css_region zoom_region) + * * zoom_region specifies the origin of the zoom region and width and * height of that region. * origin : This is the coordinate (x,y) within the effective input resolution @@ -455,7 +449,7 @@ struct ia_css_region { struct ia_css_dz_config { u32 dx; /** Horizontal zoom factor */ u32 dy; /** Vertical zoom factor */ - struct ia_css_region zoom_region; /** region for zoom */ + struct v4l2_rect zoom_region; /** region for zoom */ }; /* The still capture mode, this can be RAW (simply copy sensor input to DDR), diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/aa/aa_2/ia_css_aa2_types.h b/drivers/staging/media/atomisp/pci/isp/kernels/aa/aa_2/ia_css_aa2_types.h index 2f568a7062da..f825f537a536 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/aa/aa_2/ia_css_aa2_types.h +++ b/drivers/staging/media/atomisp/pci/isp/kernels/aa/aa_2/ia_css_aa2_types.h @@ -28,11 +28,13 @@ * ISP block: BAA2 * ISP1: BAA2 is used. * ISP2: BAA2 is used. + * + * @strength: Strength of the filter, in u0.13 fixed-point format. + * Valid range: [0, 8191]. A value of 0 means the filter is + * ineffective (default). */ struct ia_css_aa_config { - u16 strength; /** Strength of the filter. - u0.13, [0,8191], - default/ineffective 0 */ + u16 strength; }; #endif /* __IA_CSS_AA2_TYPES_H */ diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.c index 899d566234b9..f4dd3ca03d75 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.c @@ -11,30 +11,26 @@ #include "ia_css_anr.host.h" const struct ia_css_anr_config default_anr_config = { - 10, - { + .threshold = 10, + .thresholds = { 0, 3, 1, 2, 3, 6, 4, 5, 1, 4, 2, 3, 2, 5, 3, 4, 0, 3, 1, 2, 3, 6, 4, 5, 1, 4, 2, 3, 2, 5, 3, 4, 0, 3, 1, 2, 3, 6, 4, 5, 1, 4, 2, 3, 2, 5, 3, 4, 0, 3, 1, 2, 3, 6, 4, 5, 1, 4, 2, 3, 2, 5, 3, 4 }, - {10, 20, 30} + .factors = {10, 20, 30}, }; -void -ia_css_anr_encode( - struct sh_css_isp_anr_params *to, - const struct ia_css_anr_config *from, - unsigned int size) +void ia_css_anr_encode(struct sh_css_isp_anr_params *to, + const struct ia_css_anr_config *from, + unsigned int size) { (void)size; to->threshold = from->threshold; } -void -ia_css_anr_dump( - const struct sh_css_isp_anr_params *anr, - unsigned int level) +void ia_css_anr_dump(const struct sh_css_isp_anr_params *anr, + unsigned int level) { if (!anr) return; ia_css_debug_dtrace(level, "Advance Noise Reduction:\n"); @@ -42,10 +38,8 @@ ia_css_anr_dump( "anr_threshold", anr->threshold); } -void -ia_css_anr_debug_dtrace( - const struct ia_css_anr_config *config, - unsigned int level) +void ia_css_anr_debug_dtrace(const struct ia_css_anr_config *config, + unsigned int level) { ia_css_debug_dtrace(level, "config.threshold=%d\n", diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.h b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.h index 4f77900871c8..2f17d62b9208 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.h +++ b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.h @@ -12,20 +12,14 @@ extern const struct ia_css_anr_config default_anr_config; -void -ia_css_anr_encode( - struct sh_css_isp_anr_params *to, - const struct ia_css_anr_config *from, - unsigned int size); +void ia_css_anr_encode(struct sh_css_isp_anr_params *to, + const struct ia_css_anr_config *from, + unsigned int size); -void -ia_css_anr_dump( - const struct sh_css_isp_anr_params *anr, - unsigned int level); +void ia_css_anr_dump(const struct sh_css_isp_anr_params *anr, + unsigned int level); -void -ia_css_anr_debug_dtrace( - const struct ia_css_anr_config *config, unsigned int level) -; +void ia_css_anr_debug_dtrace(const struct ia_css_anr_config *config, + unsigned int level); #endif /* __IA_CSS_ANR_HOST_H */ diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.c index 09599884bdae..6ba34dc48ad4 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.c @@ -10,11 +10,9 @@ #include "ia_css_anr2.host.h" -void -ia_css_anr2_vmem_encode( - struct ia_css_isp_anr2_params *to, - const struct ia_css_anr_thres *from, - size_t size) +void ia_css_anr2_vmem_encode(struct ia_css_isp_anr2_params *to, + const struct ia_css_anr_thres *from, + size_t size) { unsigned int i; @@ -22,16 +20,13 @@ ia_css_anr2_vmem_encode( for (i = 0; i < ANR_PARAM_SIZE; i++) { unsigned int j; - for (j = 0; j < ISP_VEC_NELEMS; j++) { + for (j = 0; j < ISP_VEC_NELEMS; j++) to->data[i][j] = from->data[i * ISP_VEC_NELEMS + j]; - } } } -void -ia_css_anr2_debug_dtrace( - const struct ia_css_anr_thres *config, - unsigned int level) +void ia_css_anr2_debug_dtrace(const struct ia_css_anr_thres *config, + unsigned int level) { (void)config; (void)level; diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.h b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.h index 2b1105f21c1e..36fb6c259699 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.h +++ b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.h @@ -13,15 +13,12 @@ #include "ia_css_anr2_param.h" #include "ia_css_anr2_table.host.h" -void -ia_css_anr2_vmem_encode( - struct ia_css_isp_anr2_params *to, - const struct ia_css_anr_thres *from, - size_t size); +void ia_css_anr2_vmem_encode(struct ia_css_isp_anr2_params *to, + const struct ia_css_anr_thres *from, + size_t size); -void -ia_css_anr2_debug_dtrace( - const struct ia_css_anr_thres *config, unsigned int level) +void ia_css_anr2_debug_dtrace(const struct ia_css_anr_thres *config, + unsigned int level) ; #endif /* __IA_CSS_ANR2_HOST_H */ diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.c index 69c87e53f3c2..b87eb1a21b21 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.c @@ -12,10 +12,8 @@ #include "ia_css_bh.host.h" -void -ia_css_bh_hmem_decode( - struct ia_css_3a_rgby_output *out_ptr, - const struct ia_css_bh_table *hmem_buf) +void ia_css_bh_hmem_decode(struct ia_css_3a_rgby_output *out_ptr, + const struct ia_css_bh_table *hmem_buf) { int i; @@ -37,11 +35,9 @@ ia_css_bh_hmem_decode( } } -void -ia_css_bh_encode( - struct sh_css_isp_bh_params *to, - const struct ia_css_3a_config *from, - unsigned int size) +void ia_css_bh_encode(struct sh_css_isp_bh_params *to, + const struct ia_css_3a_config *from, + unsigned int size) { (void)size; /* coefficients to calculate Y */ diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.h b/drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.h index 36b360cfe62e..964d658ceec3 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.h +++ b/drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.h @@ -10,15 +10,11 @@ #include "ia_css_bh_param.h" #include "s3a/s3a_1.0/ia_css_s3a_types.h" -void -ia_css_bh_hmem_decode( - struct ia_css_3a_rgby_output *out_ptr, - const struct ia_css_bh_table *hmem_buf); +void ia_css_bh_hmem_decode(struct ia_css_3a_rgby_output *out_ptr, + const struct ia_css_bh_table *hmem_buf); -void -ia_css_bh_encode( - struct sh_css_isp_bh_params *to, - const struct ia_css_3a_config *from, - unsigned int size); +void ia_css_bh_encode(struct sh_css_isp_bh_params *to, + const struct ia_css_3a_config *from, + unsigned int size); #endif /* __IA_CSS_BH_HOST_H */ diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/bnlm/ia_css_bnlm.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/bnlm/ia_css_bnlm.host.c index cd867937ee13..fcd7d7e2afe8 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/bnlm/ia_css_bnlm.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/bnlm/ia_css_bnlm.host.c @@ -125,14 +125,12 @@ ia_css_bnlm_vmem_encode( bnlm_lut_encode(&to->div_lut, div_lut_nearests, div_lut_slopes, BNLM_DIV_LUT_SIZE); memset(to->div_lut_intercepts, 0, sizeof(to->div_lut_intercepts)); - for (i = 0; i < BNLM_DIV_LUT_SIZE; i++) { + for (i = 0; i < BNLM_DIV_LUT_SIZE; i++) to->div_lut_intercepts[0][i] = div_lut_intercepts[i]; - } memset(to->power_of_2, 0, sizeof(to->power_of_2)); - for (i = 0; i < (ISP_VEC_ELEMBITS - 1); i++) { + for (i = 0; i < (ISP_VEC_ELEMBITS - 1); i++) to->power_of_2[0][i] = 1 << i; - } } /* - Encodes BNLM public parameters into DMEM parameters */ diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/ctc/ctc2/ia_css_ctc2.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/ctc/ctc2/ia_css_ctc2.host.c index 38751b8e9e6a..177487b6b237 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/ctc/ctc2/ia_css_ctc2.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/ctc/ctc2/ia_css_ctc2.host.c @@ -52,13 +52,12 @@ static int ctc2_slope(int y1, int y0, int x1, int x0) /*the slope must lie within the range (-max_slope-1) >= (dydx) >= (max_slope) */ - if (slope <= -max_slope - 1) { + if (slope <= -max_slope - 1) dydx = -max_slope - 1; - } else if (slope >= max_slope) { + else if (slope >= max_slope) dydx = max_slope; - } else { + else dydx = slope; - } return dydx; } diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c index e9d6dd0bbfe2..cf6c29155758 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c @@ -258,12 +258,11 @@ convert_allocate_dvs_6axis_config( return me; } -int -store_dvs_6axis_config( - const struct ia_css_dvs_6axis_config *dvs_6axis_config, - const struct ia_css_binary *binary, - const struct ia_css_frame_info *dvs_in_frame_info, - ia_css_ptr ddr_addr_y) { +int store_dvs_6axis_config(const struct ia_css_dvs_6axis_config *dvs_6axis_config, + const struct ia_css_binary *binary, + const struct ia_css_frame_info *dvs_in_frame_info, + ia_css_ptr ddr_addr_y) +{ struct ia_css_host_data *me; assert(dvs_6axis_config); @@ -274,8 +273,7 @@ store_dvs_6axis_config( binary, dvs_in_frame_info); - if (!me) - { + if (!me) { IA_CSS_LEAVE_ERR_PRIVATE(-ENOMEM); return -ENOMEM; } diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c index 8e4451fcc8e3..76d76ca4401a 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c @@ -140,17 +140,14 @@ ia_css_eed1_8_vmem_encode( assert(tcinv_x[0] == 0); assert(fcinv_x[0] == 0); - for (j = 1; j < NUMBER_OF_CHGRINV_POINTS; j++) { + for (j = 1; j < NUMBER_OF_CHGRINV_POINTS; j++) assert(chgrinv_x[j] > chgrinv_x[j - 1]); - } - for (j = 1; j < NUMBER_OF_TCINV_POINTS; j++) { + for (j = 1; j < NUMBER_OF_TCINV_POINTS; j++) assert(tcinv_x[j] > tcinv_x[j - 1]); - } - for (j = 1; j < NUMBER_OF_FCINV_POINTS; j++) { + for (j = 1; j < NUMBER_OF_FCINV_POINTS; j++) assert(fcinv_x[j] > fcinv_x[j - 1]); - } /* The implementation of the calculating 1/x is based on the availability * of the OP_vec_shuffle16 operation. @@ -260,9 +257,9 @@ ia_css_eed1_8_encode( to->margin_neg_diff = (from->neg_margin1 - from->neg_margin0); /* Encode DEWEnhance exp (e_dew_enh_asr) */ - for (i = 0; i < (IA_CSS_NUMBER_OF_DEW_ENHANCE_SEGMENTS - 1); i++) { + for (i = 0; i < (IA_CSS_NUMBER_OF_DEW_ENHANCE_SEGMENTS - 1); i++) min_exp = max(min_exp, from->dew_enhance_seg_exp[i]); - } + to->e_dew_enh_asr = 13 - clamp(min_exp, 0, 13); to->dedgew_max = from->dedgew_max; diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/raw/raw_1.0/ia_css_raw.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/raw/raw_1.0/ia_css_raw.host.c index a00f8d049a33..fb0e2a88cadb 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/raw/raw_1.0/ia_css_raw.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/raw/raw_1.0/ia_css_raw.host.c @@ -21,10 +21,9 @@ static const struct ia_css_raw_configuration default_config = { }; /* MW: These areMIPI / ISYS properties, not camera function properties */ -static enum sh_stream_format -css2isp_stream_format(enum atomisp_input_format from) { - switch (from) - { +static enum sh_stream_format css2isp_stream_format(enum atomisp_input_format from) +{ + switch (from) { case ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY: return sh_stream_format_yuv420_legacy; case ATOMISP_INPUT_FORMAT_YUV420_8: diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/s3a/s3a_1.0/ia_css_s3a.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/s3a/s3a_1.0/ia_css_s3a.host.c index 13678138c48c..823a1b0c0991 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/s3a/s3a_1.0/ia_css_s3a.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/s3a/s3a_1.0/ia_css_s3a.host.c @@ -240,9 +240,8 @@ ia_css_s3a_hmem_decode( /* Calculate sum of histogram of R, which should not be less than count_for_3a */ sum_r = 0; - for (i = 0; i < HMEM_UNIT_SIZE; i++) { + for (i = 0; i < HMEM_UNIT_SIZE; i++) sum_r += out_ptr[i].r; - } if (sum_r < count_for_3a) { /* histogram is invalid */ return; diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_1.0/ia_css_sdis.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_1.0/ia_css_sdis.host.c index bd2def6c341a..0ca7ef5bb064 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_1.0/ia_css_sdis.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_1.0/ia_css_sdis.host.c @@ -50,9 +50,8 @@ void ia_css_sdis_horicoef_vmem_encode( assert(size % (IA_CSS_DVS_NUM_COEF_TYPES * ISP_VEC_NELEMS * sizeof( short)) == 0); - for (type = 0; type < IA_CSS_DVS_NUM_COEF_TYPES; type++) { + for (type = 0; type < IA_CSS_DVS_NUM_COEF_TYPES; type++) fill_row(&private[type * stride], &public[type * width], width, padding); - } } void ia_css_sdis_vertcoef_vmem_encode( @@ -77,9 +76,8 @@ void ia_css_sdis_vertcoef_vmem_encode( assert(size % (IA_CSS_DVS_NUM_COEF_TYPES * ISP_VEC_NELEMS * sizeof( short)) == 0); - for (type = 0; type < IA_CSS_DVS_NUM_COEF_TYPES; type++) { + for (type = 0; type < IA_CSS_DVS_NUM_COEF_TYPES; type++) fill_row(&private[type * stride], &public[type * height], height, padding); - } } void ia_css_sdis_horiproj_encode( diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c index 4a38d678f334..67860a5e4441 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c @@ -164,10 +164,9 @@ void ia_css_sdis2_clear_coefficients( dvs2_coefs->ver_coefs.even_imag = NULL; } -int -ia_css_get_dvs2_statistics( - struct ia_css_dvs2_statistics *host_stats, - const struct ia_css_isp_dvs_statistics *isp_stats) { +int ia_css_get_dvs2_statistics(struct ia_css_dvs2_statistics *host_stats, + const struct ia_css_isp_dvs_statistics *isp_stats) +{ struct ia_css_isp_dvs_statistics_map *map; int ret = 0; @@ -177,13 +176,11 @@ ia_css_get_dvs2_statistics( assert(isp_stats); map = ia_css_isp_dvs_statistics_map_allocate(isp_stats, NULL); - if (map) - { + if (map) { hmm_load(isp_stats->data_ptr, map->data_ptr, isp_stats->size); ia_css_translate_dvs2_statistics(host_stats, map); ia_css_isp_dvs_statistics_map_free(map); - } else - { + } else { IA_CSS_ERROR("out of memory"); ret = -ENOMEM; } diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf.host.c index 3c675063c4a7..152faab2b169 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf.host.c @@ -45,11 +45,10 @@ int ia_css_vf_config(struct sh_css_isp_vf_isp_config *to, * to the requested viewfinder resolution on the upper side. The output cannot * be smaller than the requested viewfinder resolution. */ -int -sh_css_vf_downscale_log2( - const struct ia_css_frame_info *out_info, - const struct ia_css_frame_info *vf_info, - unsigned int *downscale_log2) { +int sh_css_vf_downscale_log2(const struct ia_css_frame_info *out_info, + const struct ia_css_frame_info *vf_info, + unsigned int *downscale_log2) +{ unsigned int ds_log2 = 0; unsigned int out_width; @@ -65,8 +64,7 @@ sh_css_vf_downscale_log2( * test for the height since the vmem buffers only put restrictions on * the width of a line, not on the number of lines in a frame. */ - while (out_width >= vf_info->res.width) - { + while (out_width >= vf_info->res.width) { ds_log2++; out_width /= 2; } @@ -80,13 +78,12 @@ sh_css_vf_downscale_log2( return 0; } -static int -configure_kernel( - const struct ia_css_binary_info *info, - const struct ia_css_frame_info *out_info, - const struct ia_css_frame_info *vf_info, - unsigned int *downscale_log2, - struct ia_css_vf_configuration *config) { +static int configure_kernel(const struct ia_css_binary_info *info, + const struct ia_css_frame_info *out_info, + const struct ia_css_frame_info *vf_info, + unsigned int *downscale_log2, + struct ia_css_vf_configuration *config) +{ int err; unsigned int vf_log_ds = 0; diff --git a/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c b/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c index af93ca96747c..e9016d7775dc 100644 --- a/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c +++ b/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c @@ -5,6 +5,7 @@ */ #include <linux/math.h> +#include <linux/string_choices.h> #include <math_support.h> #include <gdc_device.h> /* HR_GDC_N */ @@ -347,10 +348,10 @@ ia_css_binary_dvs_stat_grid_info( return; } -int -ia_css_binary_3a_grid_info(const struct ia_css_binary *binary, - struct ia_css_grid_info *info, - struct ia_css_pipe *pipe) { +int ia_css_binary_3a_grid_info(const struct ia_css_binary *binary, + struct ia_css_grid_info *info, + struct ia_css_pipe *pipe) +{ struct ia_css_3a_grid_info *s3a_info; int err = 0; @@ -439,9 +440,9 @@ supports_bds_factor(u32 supported_factors, return ((supported_factors & PACK_BDS_FACTOR(bds_factor)) != 0); } -static int -binary_init_info(struct ia_css_binary_xinfo *info, unsigned int i, - bool *binary_found) { +static int binary_init_info(struct ia_css_binary_xinfo *info, unsigned int i, + bool *binary_found) +{ const unsigned char *blob = sh_css_blob_info[i].blob; unsigned int size = sh_css_blob_info[i].header.blob.size; @@ -464,8 +465,8 @@ binary_init_info(struct ia_css_binary_xinfo *info, unsigned int i, /* When binaries are put at the beginning, they will only * be selected if no other primary matches. */ -int -ia_css_binary_init_infos(void) { +int ia_css_binary_init_infos(void) +{ unsigned int i; unsigned int num_of_isp_binaries = sh_css_num_binaries - NUM_OF_SPS - NUM_OF_BLS; @@ -477,8 +478,7 @@ ia_css_binary_init_infos(void) { if (!all_binaries) return -ENOMEM; - for (i = 0; i < num_of_isp_binaries; i++) - { + for (i = 0; i < num_of_isp_binaries; i++) { int ret; struct ia_css_binary_xinfo *binary = &all_binaries[i]; bool binary_found; @@ -497,13 +497,12 @@ ia_css_binary_init_infos(void) { return 0; } -int -ia_css_binary_uninit(void) { +int ia_css_binary_uninit(void) +{ unsigned int i; struct ia_css_binary_xinfo *b; - for (i = 0; i < IA_CSS_BINARY_NUM_MODES; i++) - { + for (i = 0; i < IA_CSS_BINARY_NUM_MODES; i++) { for (b = binary_infos[i]; b; b = b->next) { if (b->xmem_addr) hmm_free(b->xmem_addr); @@ -625,19 +624,19 @@ binary_in_frame_padded_width(int in_frame_width, return rval; } -int -ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, - bool online, - bool two_ppc, - enum atomisp_input_format stream_format, - const struct ia_css_frame_info *in_info, /* can be NULL */ - const struct ia_css_frame_info *bds_out_info, /* can be NULL */ - const struct ia_css_frame_info *out_info[], /* can be NULL */ - const struct ia_css_frame_info *vf_info, /* can be NULL */ - struct ia_css_binary *binary, - struct ia_css_resolution *dvs_env, - int stream_config_left_padding, - bool accelerator) { +int ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, + bool online, + bool two_ppc, + enum atomisp_input_format stream_format, + const struct ia_css_frame_info *in_info, /* can be NULL */ + const struct ia_css_frame_info *bds_out_info, /* can be NULL */ + const struct ia_css_frame_info *out_info[], /* can be NULL */ + const struct ia_css_frame_info *vf_info, /* can be NULL */ + struct ia_css_binary *binary, + struct ia_css_resolution *dvs_env, + int stream_config_left_padding, + bool accelerator) +{ const struct ia_css_binary_info *info = &xinfo->sp; unsigned int dvs_env_width = 0, dvs_env_height = 0, @@ -664,25 +663,21 @@ ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, assert(binary); binary->info = xinfo; - if (!accelerator) - { + if (!accelerator) { /* binary->css_params has been filled by accelerator itself. */ err = ia_css_isp_param_allocate_isp_parameters( &binary->mem_params, &binary->css_params, &info->mem_initializers); - if (err) { + if (err) return err; - } } - for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) - { + for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) { if (out_info[i] && (out_info[i]->res.width != 0)) { bin_out_info = out_info[i]; break; } } - if (in_info && bin_out_info) - { + if (in_info && bin_out_info) { need_scaling = (in_info->res.width != bin_out_info->res.width) || (in_info->res.height != bin_out_info->res.height); } @@ -713,8 +708,7 @@ ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, binary->internal_frame_info.res.height = isp_internal_height; binary->internal_frame_info.raw_bit_depth = bits_per_pixel; - if (in_info) - { + if (in_info) { binary->effective_in_frame_res.width = in_info->res.width; binary->effective_in_frame_res.height = in_info->res.height; @@ -742,15 +736,13 @@ ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, binary->in_frame_info.crop_info = in_info->crop_info; } - if (online) - { + if (online) { bits_per_pixel = ia_css_util_input_format_bpp( stream_format, two_ppc); } binary->in_frame_info.raw_bit_depth = bits_per_pixel; - for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) - { + for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) { if (out_info[i]) { binary->out_frame_info[i].res.width = out_info[i]->res.width; binary->out_frame_info[i].res.height = out_info[i]->res.height; @@ -769,8 +761,7 @@ ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, } } - if (vf_info && (vf_info->res.width != 0)) - { + if (vf_info && (vf_info->res.width != 0)) { err = ia_css_vf_configure(binary, bin_out_info, (struct ia_css_frame_info *)vf_info, &vf_log_ds); if (err) { @@ -788,8 +779,7 @@ ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, binary->input_format = stream_format; /* viewfinder output info */ - if ((vf_info) && (vf_info->res.width != 0)) - { + if ((vf_info) && (vf_info->res.width != 0)) { unsigned int vf_out_vecs, vf_out_width, vf_out_height; binary->vf_frame_info.format = vf_info->format; @@ -821,23 +811,20 @@ ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, binary->vf_frame_info.padded_width = vf_out_width; binary->vf_frame_info.res.height = vf_out_height; } - } else - { + } else { binary->vf_frame_info.res.width = 0; binary->vf_frame_info.padded_width = 0; binary->vf_frame_info.res.height = 0; } - if (info->enable.ca_gdc) - { + if (info->enable.ca_gdc) { binary->morph_tbl_width = _ISP_MORPH_TABLE_WIDTH(isp_internal_width); binary->morph_tbl_aligned_width = _ISP_MORPH_TABLE_ALIGNED_WIDTH(isp_internal_width); binary->morph_tbl_height = _ISP_MORPH_TABLE_HEIGHT(isp_internal_height); - } else - { + } else { binary->morph_tbl_width = 0; binary->morph_tbl_aligned_width = 0; binary->morph_tbl_height = 0; @@ -847,8 +834,7 @@ ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, sc_3a_dis_padded_width = binary->in_frame_info.padded_width; sc_3a_dis_height = binary->in_frame_info.res.height; if (bds_out_info && in_info && - bds_out_info->res.width != in_info->res.width) - { + bds_out_info->res.width != in_info->res.width) { /* TODO: Next, "internal_frame_info" should be derived from * bds_out. So this part will change once it is in place! */ sc_3a_dis_width = bds_out_info->res.width + info->pipeline.left_cropping; @@ -858,18 +844,15 @@ ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, s3a_isp_width = _ISP_S3A_ELEMS_ISP_WIDTH(sc_3a_dis_padded_width, info->pipeline.left_cropping); - if (info->s3a.fixed_s3a_deci_log) - { + if (info->s3a.fixed_s3a_deci_log) { s3a_log_deci = info->s3a.fixed_s3a_deci_log; - } else - { + } else { s3a_log_deci = binary_grid_deci_factor_log2(s3a_isp_width, sc_3a_dis_height); } binary->deci_factor_log2 = s3a_log_deci; - if (info->enable.s3a) - { + if (info->enable.s3a) { binary->s3atbl_width = _ISP_S3ATBL_WIDTH(sc_3a_dis_width, s3a_log_deci); @@ -882,21 +865,18 @@ ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, binary->s3atbl_isp_height = _ISP_S3ATBL_ISP_HEIGHT(sc_3a_dis_height, s3a_log_deci); - } else - { + } else { binary->s3atbl_width = 0; binary->s3atbl_height = 0; binary->s3atbl_isp_width = 0; binary->s3atbl_isp_height = 0; } - if (info->enable.sc) - { + if (info->enable.sc) { binary->sctbl_width_per_color = _ISP_SCTBL_WIDTH_PER_COLOR(sc_3a_dis_padded_width, s3a_log_deci); binary->sctbl_aligned_width_per_color = SH_CSS_MAX_SCTBL_ALIGNED_WIDTH_PER_COLOR; binary->sctbl_height = _ISP_SCTBL_HEIGHT(sc_3a_dis_height, s3a_log_deci); - } else - { + } else { binary->sctbl_width_per_color = 0; binary->sctbl_aligned_width_per_color = 0; binary->sctbl_height = 0; @@ -1241,7 +1221,7 @@ int ia_css_binary_find(struct ia_css_binary_descr *descr, struct ia_css_binary * dev_dbg(atomisp_dev, "Using binary %s (id %d), type %d, mode %d, continuous %s\n", xcandidate->blob->name, xcandidate->sp.id, xcandidate->type, xcandidate->sp.pipeline.mode, - xcandidate->sp.enable.continuous ? "true" : "false"); + str_true_false(xcandidate->sp.enable.continuous)); if (err) dev_err(atomisp_dev, "Failed to find a firmware binary matching the pipeline parameters\n"); diff --git a/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c b/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c index b411ca2f415e..5113aa5973f3 100644 --- a/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c +++ b/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c @@ -1123,9 +1123,8 @@ ia_css_debug_pipe_graph_dump_prologue(void) void ia_css_debug_pipe_graph_dump_epilogue(void) { - if (strlen(ring_buffer) > 0) { + if (strlen(ring_buffer) > 0) dtrace_dot(ring_buffer); - } if (pg_inst.stream_format != N_ATOMISP_INPUT_FORMAT) { /* An input stream format has been set so assume we have @@ -1256,8 +1255,7 @@ ia_css_debug_pipe_graph_dump_stage( while (ei[p] != ',') p--; /* Last comma found, copy till that comma */ - strscpy(enable_info1, ei, - p > sizeof(enable_info1) ? sizeof(enable_info1) : p); + strscpy(enable_info1, ei, umin(p, sizeof(enable_info1))); ei += p + 1; l = strlen(ei); @@ -1268,8 +1266,7 @@ ia_css_debug_pipe_graph_dump_stage( * it is not guaranteed dword aligned */ - strscpy(enable_info2, ei, - l > sizeof(enable_info2) ? sizeof(enable_info2) : l); + strscpy(enable_info2, ei, umin(l, sizeof(enable_info2))); snprintf(enable_info, sizeof(enable_info), "%s\\n%s", enable_info1, enable_info2); @@ -1280,8 +1277,7 @@ ia_css_debug_pipe_graph_dump_stage( while (ei[p] != ',') p--; - strscpy(enable_info2, ei, - p > sizeof(enable_info2) ? sizeof(enable_info2) : p); + strscpy(enable_info2, ei, umin(p, sizeof(enable_info2))); ei += p + 1; l = strlen(ei); @@ -1303,7 +1299,7 @@ ia_css_debug_pipe_graph_dump_stage( while (ei[p] != ',') p--; strscpy(enable_info3, ei, - p > sizeof(enable_info3) ? sizeof(enable_info3) : p); + umin(p, sizeof(enable_info3))); ei += p + 1; strscpy(enable_info3, ei, sizeof(enable_info3)); @@ -1778,9 +1774,8 @@ static void debug_dump_one_trace(enum TRACE_CORE_ID proc_id) * When tid value is 111b, the data will be interpreted differently: * tid val is ignored, major field contains 2 bits (msb) for format type */ - if (tid_val == FIELD_TID_SEL_FORMAT_PAT) { + if (tid_val == FIELD_TID_SEL_FORMAT_PAT) dump_format = FIELD_FORMAT_UNPACK(trace_read_buf[j]); - } } switch (dump_format) { case TRACE_DUMP_FORMAT_POINT: diff --git a/drivers/staging/media/atomisp/pci/runtime/isp_param/src/isp_param.c b/drivers/staging/media/atomisp/pci/runtime/isp_param/src/isp_param.c index 251dd75a7613..1d20eb650757 100644 --- a/drivers/staging/media/atomisp/pci/runtime/isp_param/src/isp_param.c +++ b/drivers/staging/media/atomisp/pci/runtime/isp_param/src/isp_param.c @@ -93,17 +93,15 @@ ia_css_init_memory_interface( } } -int -ia_css_isp_param_allocate_isp_parameters( - struct ia_css_isp_param_host_segments *mem_params, - struct ia_css_isp_param_css_segments *css_params, - const struct ia_css_isp_param_isp_segments *mem_initializers) { +int ia_css_isp_param_allocate_isp_parameters(struct ia_css_isp_param_host_segments *mem_params, + struct ia_css_isp_param_css_segments *css_params, + const struct ia_css_isp_param_isp_segments *mem_initializers) +{ int err = 0; unsigned int mem, pclass; pclass = IA_CSS_PARAM_CLASS_PARAM; - for (mem = 0; mem < IA_CSS_NUM_MEMORIES; mem++) - { + for (mem = 0; mem < IA_CSS_NUM_MEMORIES; mem++) { for (pclass = 0; pclass < IA_CSS_NUM_PARAM_CLASSES; pclass++) { u32 size = 0; @@ -171,15 +169,13 @@ ia_css_isp_param_load_fw_params( } } -int -ia_css_isp_param_copy_isp_mem_if_to_ddr( - struct ia_css_isp_param_css_segments *ddr, - const struct ia_css_isp_param_host_segments *host, - enum ia_css_param_class pclass) { +int ia_css_isp_param_copy_isp_mem_if_to_ddr(struct ia_css_isp_param_css_segments *ddr, + const struct ia_css_isp_param_host_segments *host, + enum ia_css_param_class pclass) +{ unsigned int mem; - for (mem = 0; mem < N_IA_CSS_ISP_MEMORIES; mem++) - { + for (mem = 0; mem < N_IA_CSS_ISP_MEMORIES; mem++) { size_t size = host->params[pclass][mem].size; ia_css_ptr ddr_mem_ptr = ddr->params[pclass][mem].address; char *host_mem_ptr = host->params[pclass][mem].address; diff --git a/drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c b/drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c index e6c11d5f77b4..8e8433f5b07a 100644 --- a/drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c +++ b/drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c @@ -295,9 +295,8 @@ static bool create_input_system_channel( if (!rc) return false; - if (!acquire_sid(me->stream2mmio_id, &me->stream2mmio_sid_id)) { + if (!acquire_sid(me->stream2mmio_id, &me->stream2mmio_sid_id)) return false; - } if (!acquire_ib_buffer( metadata ? cfg->metadata.bits_per_pixel : diff --git a/drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c b/drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c index 0470871f8fff..8d11466fda1b 100644 --- a/drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c +++ b/drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c @@ -575,9 +575,8 @@ static int pipeline_stage_create( binary = stage_desc->binary; firmware = stage_desc->firmware; vf_frame = stage_desc->vf_frame; - for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) { + for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) out_frame[i] = stage_desc->out_frame[i]; - } stage = kvzalloc_obj(*stage); if (!stage) { diff --git a/drivers/staging/media/atomisp/pci/runtime/queue/src/queue.c b/drivers/staging/media/atomisp/pci/runtime/queue/src/queue.c index afe77d4373f8..d27c6567daeb 100644 --- a/drivers/staging/media/atomisp/pci/runtime/queue/src/queue.c +++ b/drivers/staging/media/atomisp/pci/runtime/queue/src/queue.c @@ -167,7 +167,7 @@ int ia_css_queue_dequeue(ia_css_queue_t *qhandle, uint32_t *item) *item = cb_elem.val; - cb_desc.start = OP_std_modadd(cb_desc.start, 1, cb_desc.size); + cb_desc.start = (cb_desc.start + 1) % cb_desc.size; /* c. Store the queue object */ /* Set only fields requiring update with @@ -315,7 +315,7 @@ int ia_css_queue_peek(ia_css_queue_t *qhandle, u32 offset, uint32_t *element) if (offset > num_elems) return -EINVAL; - offset = OP_std_modadd(cb_desc.start, offset, cb_desc.size); + offset = (cb_desc.start + offset) % cb_desc.size; error = ia_css_queue_item_load(qhandle, (uint8_t)offset, &cb_elem); if (error != 0) return error; diff --git a/drivers/staging/media/atomisp/pci/sh_css.c b/drivers/staging/media/atomisp/pci/sh_css.c index 6cda5925fa45..00082276f1db 100644 --- a/drivers/staging/media/atomisp/pci/sh_css.c +++ b/drivers/staging/media/atomisp/pci/sh_css.c @@ -7,6 +7,7 @@ /*! \file */ #include <linux/mm.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/vmalloc.h> #include "hmm.h" @@ -1478,7 +1479,7 @@ map_sp_threads(struct ia_css_stream *stream, bool map) enum ia_css_pipe_id pipe_id; IA_CSS_ENTER_PRIVATE("stream = %p, map = %s", - stream, map ? "true" : "false"); + stream, str_true_false(map)); if (!stream) { IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL); @@ -2256,8 +2257,7 @@ alloc_continuous_frames(struct ia_css_pipe *pipe, bool init_time) ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, "alloc_continuous_frames() IA_CSS_FRAME_FORMAT_RAW_PACKED\n"); ref_info.format = IA_CSS_FRAME_FORMAT_RAW_PACKED; - } else - { + } else { ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, "alloc_continuous_frames() IA_CSS_FRAME_FORMAT_RAW\n"); ref_info.format = IA_CSS_FRAME_FORMAT_RAW; @@ -5020,7 +5020,6 @@ static int load_primary_binaries( struct ia_css_capture_settings *mycs; unsigned int i; bool need_extra_yuv_scaler = false; - struct ia_css_binary_descr prim_descr[MAX_NUM_PRIMARY_STAGES]; IA_CSS_ENTER_PRIVATE(""); assert(pipe); @@ -5189,15 +5188,16 @@ static int load_primary_binaries( /* Primary */ for (i = 0; i < mycs->num_primary_stage; i++) { + struct ia_css_binary_descr prim_descr; struct ia_css_frame_info *local_vf_info = NULL; if (pipe->enable_viewfinder[IA_CSS_PIPE_OUTPUT_STAGE_0] && (i == mycs->num_primary_stage - 1)) local_vf_info = &vf_info; - ia_css_pipe_get_primary_binarydesc(pipe, &prim_descr[i], + ia_css_pipe_get_primary_binarydesc(pipe, &prim_descr, &prim_in_info, &prim_out_info, local_vf_info, i); - err = ia_css_binary_find(&prim_descr[i], &mycs->primary_binary[i]); + err = ia_css_binary_find(&prim_descr, &mycs->primary_binary[i]); if (err) { IA_CSS_LEAVE_ERR_PRIVATE(err); return err; @@ -5819,36 +5819,37 @@ static int ia_css_pipe_create_cas_scaler_desc_single_output( i *= max_scale_factor_per_stage; } - descr->in_info = kmalloc(descr->num_stage * - sizeof(struct ia_css_frame_info), - GFP_KERNEL); + descr->in_info = kmalloc_objs(*descr->in_info, + descr->num_stage, + GFP_KERNEL); if (!descr->in_info) { err = -ENOMEM; goto ERR; } - descr->internal_out_info = kmalloc(descr->num_stage * - sizeof(struct ia_css_frame_info), - GFP_KERNEL); + descr->internal_out_info = kmalloc_objs(*descr->internal_out_info, + descr->num_stage, + GFP_KERNEL); if (!descr->internal_out_info) { err = -ENOMEM; goto ERR; } - descr->out_info = kmalloc(descr->num_stage * - sizeof(struct ia_css_frame_info), - GFP_KERNEL); + descr->out_info = kmalloc_objs(*descr->out_info, + descr->num_stage, + GFP_KERNEL); if (!descr->out_info) { err = -ENOMEM; goto ERR; } - descr->vf_info = kmalloc(descr->num_stage * - sizeof(struct ia_css_frame_info), - GFP_KERNEL); + descr->vf_info = kmalloc_objs(*descr->vf_info, + descr->num_stage, + GFP_KERNEL); if (!descr->vf_info) { err = -ENOMEM; goto ERR; } - descr->is_output_stage = kmalloc(descr->num_stage * sizeof(bool), - GFP_KERNEL); + descr->is_output_stage = kmalloc_objs(*descr->is_output_stage, + descr->num_stage, + GFP_KERNEL); if (!descr->is_output_stage) { err = -ENOMEM; goto ERR; @@ -5968,35 +5969,37 @@ ia_css_pipe_create_cas_scaler_desc(struct ia_css_pipe *pipe, descr->num_stage = num_stages; - descr->in_info = kmalloc_objs(struct ia_css_frame_info, - descr->num_stage); + descr->in_info = kmalloc_objs(*descr->in_info, + descr->num_stage, + GFP_KERNEL); if (!descr->in_info) { err = -ENOMEM; goto ERR; } - descr->internal_out_info = kmalloc(descr->num_stage * - sizeof(struct ia_css_frame_info), - GFP_KERNEL); + descr->internal_out_info = kmalloc_objs(*descr->internal_out_info, + descr->num_stage, + GFP_KERNEL); if (!descr->internal_out_info) { err = -ENOMEM; goto ERR; } - descr->out_info = kmalloc(descr->num_stage * - sizeof(struct ia_css_frame_info), - GFP_KERNEL); + descr->out_info = kmalloc_objs(*descr->out_info, + descr->num_stage, + GFP_KERNEL); if (!descr->out_info) { err = -ENOMEM; goto ERR; } - descr->vf_info = kmalloc(descr->num_stage * - sizeof(struct ia_css_frame_info), - GFP_KERNEL); + descr->vf_info = kmalloc_objs(*descr->vf_info, + descr->num_stage, + GFP_KERNEL); if (!descr->vf_info) { err = -ENOMEM; goto ERR; } - descr->is_output_stage = kmalloc(descr->num_stage * sizeof(bool), - GFP_KERNEL); + descr->is_output_stage = kmalloc_objs(*descr->is_output_stage, + descr->num_stage, + GFP_KERNEL); if (!descr->is_output_stage) { err = -ENOMEM; goto ERR; @@ -7856,8 +7859,7 @@ ia_css_stream_create(const struct ia_css_stream_config *stream_config, /* check if mipi size specified */ if (stream_config->mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR) - if (!IS_ISP2401 || !stream_config->online) - { + if (!IS_ISP2401 || !stream_config->online) { unsigned int port = (unsigned int)stream_config->source.port.port; if (port >= N_MIPI_PORT_ID) { @@ -8189,6 +8191,48 @@ ERR: return err; } +static void ia_css_stream_destroy_isp2401(struct ia_css_stream *stream) +{ + int i, j; + + for (i = 0; i < stream->num_pipes; i++) { + struct ia_css_pipe *entry = stream->pipes[i]; + unsigned int sp_thread_id; + struct sh_css_sp_pipeline_terminal *terminal; + + if (!entry) + continue; + + if (!ia_css_pipeline_get_sp_thread_id( + ia_css_pipe_get_pipe_num(entry), &sp_thread_id)) + continue; + + terminal = &sh_css_sp_group.pipe_io[sp_thread_id].input; + + for (j = 0; j < IA_CSS_STREAM_MAX_ISYS_STREAM_PER_CH; j++) { + ia_css_isys_stream_h isys_stream = + &terminal->context.virtual_input_system_stream[j]; + if (stream->config.isys_config[j].valid && isys_stream->valid) + ia_css_isys_stream_destroy(isys_stream); + } + } + + if (stream->config.mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR) { + for (i = 0; i < stream->num_pipes; i++) { + /* + * free any mipi frames that are remaining: + * some test stream create-destroy cycles do + * not generate output frames + * and the mipi buffer is not freed in the + * deque function + */ + if (stream->pipes[i]) + free_mipi_frames(stream->pipes[i]); + } + } + stream_unregister_with_csi_rx(stream); +} + int ia_css_stream_destroy(struct ia_css_stream *stream) { @@ -8206,48 +8250,8 @@ ia_css_stream_destroy(struct ia_css_stream *stream) if ((stream->last_pipe) && ia_css_pipeline_is_mapped(stream->last_pipe->pipe_num)) { - if (IS_ISP2401) { - for (i = 0; i < stream->num_pipes; i++) { - struct ia_css_pipe *entry = stream->pipes[i]; - unsigned int sp_thread_id; - struct sh_css_sp_pipeline_terminal *sp_pipeline_input_terminal; - - assert(entry); - if (entry) { - /* get the SP thread id */ - if (!ia_css_pipeline_get_sp_thread_id( - ia_css_pipe_get_pipe_num(entry), &sp_thread_id)) - return -EINVAL; - - /* get the target input terminal */ - sp_pipeline_input_terminal = - &sh_css_sp_group.pipe_io[sp_thread_id].input; - - for (i = 0; i < IA_CSS_STREAM_MAX_ISYS_STREAM_PER_CH; i++) { - ia_css_isys_stream_h isys_stream = - &sp_pipeline_input_terminal->context.virtual_input_system_stream[i]; - if (stream->config.isys_config[i].valid && isys_stream->valid) - ia_css_isys_stream_destroy(isys_stream); - } - } - } - - if (stream->config.mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR) { - for (i = 0; i < stream->num_pipes; i++) { - struct ia_css_pipe *entry = stream->pipes[i]; - /* - * free any mipi frames that are remaining: - * some test stream create-destroy cycles do - * not generate output frames - * and the mipi buffer is not freed in the - * deque function - */ - if (entry) - free_mipi_frames(entry); - } - } - stream_unregister_with_csi_rx(stream); - } + if (IS_ISP2401) + ia_css_stream_destroy_isp2401(stream); for (i = 0; i < stream->num_pipes; i++) { struct ia_css_pipe *curr_pipe = stream->pipes[i]; diff --git a/drivers/staging/media/atomisp/pci/sh_css_firmware.c b/drivers/staging/media/atomisp/pci/sh_css_firmware.c index 57ecf5549c23..af12df2f9b09 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_firmware.c +++ b/drivers/staging/media/atomisp/pci/sh_css_firmware.c @@ -253,9 +253,9 @@ sh_css_load_firmware(struct device *dev, const char *fw_data, sh_css_num_binaries = file_header->binary_nr; /* Only allocate memory for ISP blob info */ if (sh_css_num_binaries > NUM_OF_SPS) { - sh_css_blob_info = kmalloc( - (sh_css_num_binaries - NUM_OF_SPS) * - sizeof(*sh_css_blob_info), GFP_KERNEL); + sh_css_blob_info = + kmalloc_array(sh_css_num_binaries - NUM_OF_SPS, + sizeof(*sh_css_blob_info), GFP_KERNEL); if (!sh_css_blob_info) return -ENOMEM; } else { diff --git a/drivers/staging/media/atomisp/pci/sh_css_param_dvs.c b/drivers/staging/media/atomisp/pci/sh_css_param_dvs.c index 9ccdb66de2df..ad2a9b84e232 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_param_dvs.c +++ b/drivers/staging/media/atomisp/pci/sh_css_param_dvs.c @@ -4,6 +4,7 @@ * Copyright (c) 2015, Intel Corporation. */ +#include <linux/overflow.h> #include "sh_css_param_dvs.h" #include <assert_support.h> #include <type_support.h> @@ -48,7 +49,7 @@ alloc_dvs_6axis_table(const struct ia_css_resolution *frame_res, } /* Generate Y buffers */ - dvs_config->xcoords_y = kvmalloc(width_y * height_y * sizeof(uint32_t), + dvs_config->xcoords_y = kvmalloc(array3_size(width_y, height_y, sizeof(uint32_t)), GFP_KERNEL); if (!dvs_config->xcoords_y) { IA_CSS_ERROR("out of memory"); @@ -56,7 +57,7 @@ alloc_dvs_6axis_table(const struct ia_css_resolution *frame_res, goto exit; } - dvs_config->ycoords_y = kvmalloc(width_y * height_y * sizeof(uint32_t), + dvs_config->ycoords_y = kvmalloc(array3_size(width_y, height_y, sizeof(uint32_t)), GFP_KERNEL); if (!dvs_config->ycoords_y) { IA_CSS_ERROR("out of memory"); @@ -67,7 +68,8 @@ alloc_dvs_6axis_table(const struct ia_css_resolution *frame_res, /* Generate UV buffers */ IA_CSS_LOG("UV W %d H %d", width_uv, height_uv); - dvs_config->xcoords_uv = kvmalloc(width_uv * height_uv * sizeof(uint32_t), + dvs_config->xcoords_uv = kvmalloc(array3_size(width_uv, height_uv, + sizeof(uint32_t)), GFP_KERNEL); if (!dvs_config->xcoords_uv) { IA_CSS_ERROR("out of memory"); @@ -75,7 +77,8 @@ alloc_dvs_6axis_table(const struct ia_css_resolution *frame_res, goto exit; } - dvs_config->ycoords_uv = kvmalloc(width_uv * height_uv * sizeof(uint32_t), + dvs_config->ycoords_uv = kvmalloc(array3_size(width_uv, height_uv, + sizeof(uint32_t)), GFP_KERNEL); if (!dvs_config->ycoords_uv) { IA_CSS_ERROR("out of memory"); @@ -269,5 +272,4 @@ ia_css_dvs_statistics_get(enum dvs_statistics_type type, ia_css_get_dvs2_statistics(host_stats->p_dvs2_statistics_host, isp_stats->p_dvs_statistics_isp); } - return; } diff --git a/drivers/staging/media/atomisp/pci/sh_css_param_shading.c b/drivers/staging/media/atomisp/pci/sh_css_param_shading.c index 9105334c71b1..0ac85cdea010 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_param_shading.c +++ b/drivers/staging/media/atomisp/pci/sh_css_param_shading.c @@ -4,6 +4,7 @@ * Copyright (c) 2015, Intel Corporation. */ +#include <linux/overflow.h> #include <linux/math.h> #include <linux/slab.h> @@ -20,7 +21,8 @@ #include "platform_support.h" -/* Bilinear interpolation on shading tables: +/* + * Bilinear interpolation on shading tables: * For each target point T, we calculate the 4 surrounding source points: * ul (upper left), ur (upper right), ll (lower left) and lr (lower right). * We then calculate the distances from the T to the source points: x0, x1, @@ -116,8 +118,10 @@ crop_and_interpolate(unsigned int cropped_width, */ ty = out_start_row + i * out_cell_size; - /* calculate closest source points in shading table and - make sure they fall within the table */ + /* + * calculate closest source points in shading table and + * make sure they fall within the table + */ src_y0 = ty / (int)in_cell_size; if (in_cell_size < out_cell_size) src_y1 = (ty + out_cell_size) / in_cell_size; @@ -173,7 +177,8 @@ crop_and_interpolate(unsigned int cropped_width, dx0 = tx - sx0; dx1 = sx1 - tx; divx = sx1 - sx0; - /* if we're at the edge, we just use the closest + /* + * if we're at the edge, we just use the closest * point still in the grid. We make up for the divider * in this case by setting the distance to * out_cell_size, since it's actually 0. @@ -291,8 +296,10 @@ prepare_shading_table(const struct ia_css_shading_table *in_table, input_width = min(input_width, in_table->sensor_width); input_height = min(input_height, in_table->sensor_height); - /* This prepare_shading_table() function is called only in legacy API (not in new API). - Then, the legacy shading table width and height should be used. */ + /* + * This prepare_shading_table() function is called only in legacy API (not in new API). + * Then, the legacy shading table width and height should be used. + */ table_width = binary->sctbl_width_per_color; table_height = binary->sctbl_height; @@ -339,7 +346,8 @@ ia_css_shading_table_alloc( me->fraction_bits = 0; for (i = 0; i < IA_CSS_SC_NUM_COLORS; i++) { me->data[i] = - kvmalloc(width * height * sizeof(*me->data[0]), + kvmalloc(array3_size(width, height, + sizeof(*me->data[0])), GFP_KERNEL); if (!me->data[i]) { unsigned int j; diff --git a/drivers/staging/media/atomisp/pci/sh_css_params.c b/drivers/staging/media/atomisp/pci/sh_css_params.c index fcebace11daf..8420a22fd8f0 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_params.c +++ b/drivers/staging/media/atomisp/pci/sh_css_params.c @@ -4,6 +4,7 @@ * Copyright (c) 2015, Intel Corporation. */ +#include <linux/overflow.h> #include <linux/math.h> #include "gdc_device.h" /* gdc_lut_store(), ... */ @@ -657,11 +658,7 @@ static const int zoom_table[4][HRT_GDC_N] = { static const struct ia_css_dz_config default_dz_config = { HRT_GDC_N, HRT_GDC_N, - { - \ - {0, 0}, \ - {0, 0}, \ - } + { 0, 0, 0, 0 } }; static const struct ia_css_vector default_motion_config = { @@ -875,7 +872,8 @@ ia_css_process_kernel(struct ia_css_stream *stream, /* update the other buffers to the pipe specific copies */ for (stage = pipeline->stages; stage; stage = stage->next) { - if (!stage || !stage->binary) continue; + if (!stage || !stage->binary) + continue; process(pipeline->pipe_id, stage, params); } } @@ -1210,8 +1208,8 @@ ia_css_process_zoom_and_motion( } assert(stage->stage_num < SH_CSS_MAX_STAGES); - if (params->dz_config.zoom_region.resolution.width == 0 && - params->dz_config.zoom_region.resolution.height == 0) { + if (params->dz_config.zoom_region.width == 0 && + params->dz_config.zoom_region.height == 0) { sh_css_update_uds_and_crop_info( &info->sp, &binary->in_frame_info, @@ -1381,11 +1379,11 @@ struct ia_css_morph_table *ia_css_morph_table_allocate( } for (i = 0; i < IA_CSS_MORPH_TABLE_NUM_PLANES; i++) { - me->coordinates_x[i] = kvmalloc(height * width * - sizeof(*me->coordinates_x[i]), + me->coordinates_x[i] = kvmalloc(array3_size(height, width, + sizeof(*me->coordinates_x[i])), GFP_KERNEL); - me->coordinates_y[i] = kvmalloc(height * width * - sizeof(*me->coordinates_y[i]), + me->coordinates_y[i] = kvmalloc(array3_size(height, width, + sizeof(*me->coordinates_y[i])), GFP_KERNEL); if ((!me->coordinates_x[i]) || @@ -1928,9 +1926,8 @@ sh_css_set_per_frame_isp_config_on_pipe( params = stream->per_frame_isp_params_configs; /* update new ISP params object with the new config */ - if (!sh_css_init_isp_params_from_global(stream, params, false, pipe)) { + if (!sh_css_init_isp_params_from_global(stream, params, false, pipe)) err1 = -EINVAL; - } err2 = sh_css_init_isp_params_from_config(stream->pipes[0], params, config, pipe); @@ -2004,9 +2001,8 @@ sh_css_init_isp_params_from_config(struct ia_css_pipe *pipe, * user. */ /* we do not exit from this point immediately to allow internal * firmware feature testing. */ - if (is_dp_10bpp) { + if (is_dp_10bpp) err = -EINVAL; - } } else { err = -EINVAL; goto exit; @@ -3034,9 +3030,8 @@ process_kernel_parameters(unsigned int pipe_id, ia_css_ob_configure(¶ms->stream_configs.ob, isp_pipe_version, raw_bit_depth); } - if (params->config_changed[IA_CSS_S3A_ID]) { + if (params->config_changed[IA_CSS_S3A_ID]) ia_css_s3a_configure(raw_bit_depth); - } /* Copy stage uds parameters to config, since they can differ per stage. */ params->crop_config.crop_pos = params->uds[stage->stage_num].crop_pos; @@ -3045,7 +3040,8 @@ process_kernel_parameters(unsigned int pipe_id, /* Call parameter process functions for all kernels */ /* Skip SC, since that is called on a temp sc table */ for (param_id = 0; param_id < IA_CSS_NUM_PARAMETER_IDS; param_id++) { - if (param_id == IA_CSS_SC_ID) continue; + if (param_id == IA_CSS_SC_ID) + continue; if (params->config_changed[param_id]) ia_css_kernel_process_param[param_id](pipe_id, stage, params); } @@ -3600,7 +3596,8 @@ sh_css_params_write_to_ddr_internal( IA_CSS_PARAM_CLASS_PARAM, mem); size_t size = isp_data->size; - if (!size) continue; + if (!size) + continue; buff_realloced = reallocate_buffer(&ddr_map->isp_mem_param[stage_num][mem], &ddr_map_size->isp_mem_param[stage_num][mem], size, @@ -3899,9 +3896,8 @@ sh_css_invalidate_params(struct ia_css_stream *stream) params->isp_params_changed = true; for (i = 0; i < IA_CSS_PIPE_ID_NUM; i++) { for (j = 0; j < SH_CSS_MAX_STAGES; j++) { - for (mem = 0; mem < N_IA_CSS_MEMORIES; mem++) { + for (mem = 0; mem < N_IA_CSS_MEMORIES; mem++) params->isp_mem_params_changed[i][j][mem] = true; - } } } @@ -4096,10 +4092,10 @@ sh_css_update_uds_and_crop_info_based_on_zoom_region( assert(motion_vector); assert(uds); assert(sp_out_crop_pos); - x0 = zoom->zoom_region.origin.x; - y0 = zoom->zoom_region.origin.y; - x1 = zoom->zoom_region.resolution.width + x0; - y1 = zoom->zoom_region.resolution.height + y0; + x0 = zoom->zoom_region.left; + y0 = zoom->zoom_region.top; + x1 = zoom->zoom_region.width + x0; + y1 = zoom->zoom_region.height + y0; if ((x0 > x1) || (y0 > y1) || (x1 > pipe_in_res.width) || (y1 > pipe_in_res.height)) return -EINVAL; @@ -4206,13 +4202,17 @@ ia_css_dvs_statistics_allocate(const struct ia_css_dvs_grid_info *grid) goto err; me->grid = *grid; - me->hor_proj = kvmalloc(grid->height * IA_CSS_DVS_NUM_COEF_TYPES * - sizeof(*me->hor_proj), GFP_KERNEL); + me->hor_proj = kvmalloc(array3_size(grid->height, + IA_CSS_DVS_NUM_COEF_TYPES, + sizeof(*me->hor_proj)), + GFP_KERNEL); if (!me->hor_proj) goto err; - me->ver_proj = kvmalloc(grid->width * IA_CSS_DVS_NUM_COEF_TYPES * - sizeof(*me->ver_proj), GFP_KERNEL); + me->ver_proj = kvmalloc(array3_size(grid->width, + IA_CSS_DVS_NUM_COEF_TYPES, + sizeof(*me->ver_proj)), + GFP_KERNEL); if (!me->ver_proj) goto err; @@ -4478,24 +4478,26 @@ ia_css_dvs2_6axis_config_allocate(const struct ia_css_stream *stream) params->pipe_dvs_6axis_config[IA_CSS_PIPE_ID_VIDEO]->height_uv; IA_CSS_LOG("table Y: W %d H %d", width_y, height_y); IA_CSS_LOG("table UV: W %d H %d", width_uv, height_uv); - dvs_config->xcoords_y = kvmalloc(width_y * height_y * sizeof(uint32_t), + dvs_config->xcoords_y = kvmalloc(array3_size(width_y, height_y, + sizeof(uint32_t)), GFP_KERNEL); if (!dvs_config->xcoords_y) goto err; - dvs_config->ycoords_y = kvmalloc(width_y * height_y * sizeof(uint32_t), + dvs_config->ycoords_y = kvmalloc(array3_size(width_y, height_y, + sizeof(uint32_t)), GFP_KERNEL); if (!dvs_config->ycoords_y) goto err; - dvs_config->xcoords_uv = kvmalloc(width_uv * height_uv * - sizeof(uint32_t), + dvs_config->xcoords_uv = kvmalloc(array3_size(width_uv, height_uv, + sizeof(uint32_t)), GFP_KERNEL); if (!dvs_config->xcoords_uv) goto err; - dvs_config->ycoords_uv = kvmalloc(width_uv * height_uv * - sizeof(uint32_t), + dvs_config->ycoords_uv = kvmalloc(array3_size(width_uv, height_uv, + sizeof(uint32_t)), GFP_KERNEL); if (!dvs_config->ycoords_uv) goto err; diff --git a/drivers/staging/media/atomisp/pci/sh_css_sp.c b/drivers/staging/media/atomisp/pci/sh_css_sp.c index 6da151e7a873..2beb7168517f 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_sp.c +++ b/drivers/staging/media/atomisp/pci/sh_css_sp.c @@ -775,9 +775,13 @@ static int configure_isp_from_args(const struct sh_css_sp_pipeline *pipeline, ret = ia_css_fpn_configure(binary, &binary->in_frame_info); if (ret) return ret; - ret = ia_css_crop_configure(binary, ia_css_frame_get_info(args->delay_frames[0])); - if (ret) - return ret; + + if (binary->info->sp.enable.ref_frame) { + ret = ia_css_crop_configure(binary, ia_css_frame_get_info(args->delay_frames[0])); + if (ret) + return ret; + } + ret = ia_css_qplane_configure(pipeline, binary, &binary->in_frame_info); if (ret) return ret; @@ -790,9 +794,6 @@ static int configure_isp_from_args(const struct sh_css_sp_pipeline *pipeline, ret = ia_css_copy_output_configure(binary, args->copy_output); if (ret) return ret; - ret = ia_css_output0_configure(binary, ia_css_frame_get_info(args->out_frame[0])); - if (ret) - return ret; ret = ia_css_iterator_configure(binary, ia_css_frame_get_info(args->in_frame)); if (ret) return ret; @@ -807,22 +808,18 @@ static int configure_isp_from_args(const struct sh_css_sp_pipeline *pipeline, if (ret) return ret; - /* - * FIXME: args->delay_frames can be NULL here - * - * Somehow, the driver at the Intel Atom Yocto tree doesn't seem to - * suffer from the same issue. - * - * Anyway, the function below should now handle a NULL delay_frames - * without crashing, but the pipeline should likely be built without - * adding it at the first place (or there are a hidden bug somewhere) - */ - ret = ia_css_ref_configure(binary, args->delay_frames, pipeline->dvs_frame_delay); - if (ret) - return ret; - ret = ia_css_tnr_configure(binary, args->tnr_frames); - if (ret) - return ret; + if (binary->info->sp.enable.ref_frame) { + ret = ia_css_ref_configure(binary, args->delay_frames, pipeline->dvs_frame_delay); + if (ret) + return ret; + } + + if (binary->info->sp.enable.tnr) { + ret = ia_css_tnr_configure(binary, args->tnr_frames); + if (ret) + return ret; + } + return ia_css_bayer_io_config(binary, args); } diff --git a/drivers/staging/media/atomisp/pci/system_local.c b/drivers/staging/media/atomisp/pci/system_local.c index a8a93760d5b1..69bcd557a821 100644 --- a/drivers/staging/media/atomisp/pci/system_local.c +++ b/drivers/staging/media/atomisp/pci/system_local.c @@ -86,8 +86,7 @@ const hrt_address GP_DEVICE_BASE[N_GP_DEVICE_ID] = { /*GP TIMER , all timer registers are inter-twined, * so, having multiple base addresses for * different timers does not help*/ -const hrt_address GP_TIMER_BASE = - (hrt_address)0x0000000000000600ULL; +const hrt_address GP_TIMER_BASE = (hrt_address)0x0000000000000600ULL; /* GPIO */ const hrt_address GPIO_BASE[N_GPIO_ID] = { diff --git a/drivers/staging/media/av7110/av7110.c b/drivers/staging/media/av7110/av7110.c index 014d0c6f0a8b..862aee993889 100644 --- a/drivers/staging/media/av7110/av7110.c +++ b/drivers/staging/media/av7110/av7110.c @@ -121,19 +121,22 @@ static void init_av7110_av(struct av7110 *av7110) if (ret < 0) pr_err("cannot set internal volume to maximum:%d\n", ret); - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetMonitorType, - 1, (u16)av7110->display_ar); + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, + AV7110_SET_MONITOR_TYPE, 1, av7110->display_ar); if (ret < 0) pr_err("unable to set aspect ratio\n"); - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetPanScanType, - 1, av7110->display_panscan); + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, + AV7110_SET_PANSCAN_TYPE, 1, + av7110->display_panscan); if (ret < 0) pr_err("unable to set pan scan\n"); - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 2, wss_cfg_4_3); + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, + AV7110_SET_WSS_CONFIG, 2, 2, wss_cfg_4_3); if (ret < 0) pr_err("unable to configure 4:3 wss\n"); - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 3, wss_cfg_16_9); + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, + AV7110_SET_WSS_CONFIG, 2, 3, wss_cfg_16_9); if (ret < 0) pr_err("unable to configure 16:9 wss\n"); @@ -314,17 +317,6 @@ static int DvbDmxFilterCallback(u8 *buffer1, size_t buffer1_len, } } -//#define DEBUG_TIMING -static inline void print_time(char *s) -{ -#ifdef DEBUG_TIMING - struct timespec64 ts; - - ktime_get_real_ts64(&ts); - pr_info("%s(): %ptSp\n", s, &ts); -#endif -} - #define DEBI_READ 0 #define DEBI_WRITE 1 static inline void start_debi_dma(struct av7110 *av7110, int dir, @@ -353,7 +345,6 @@ static void debiirq(struct tasklet_struct *t) int handle = (type >> 8) & 0x1f; unsigned int xfer = 0; - print_time("debi"); dprintk(4, "type 0x%04x\n", type); if (type == -1) { @@ -473,7 +464,6 @@ static void gpioirq(struct tasklet_struct *t) txbuf = irdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); len = (av7110->debilen + 3) & ~3; - print_time("gpio"); dprintk(8, "GPIO0 irq 0x%04x %d\n", av7110->debitype, av7110->debilen); switch (av7110->debitype & 0xff) { @@ -717,7 +707,8 @@ static inline int SetPIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, if (av7110->audiostate.bypass_mode) aflags |= 0x8000; - return av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, MultiPID, 6, + return av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, + AV7110_MULTI_PID, 6, pcrpid, vpid, apid, ttpid, subpid, aflags); } @@ -762,7 +753,6 @@ static int StartHWFilter(struct dvb_demux_filter *dvbdmxfilter) u16 buf[20]; int ret, i; u16 handle; -// u16 mode = 0x0320; u16 mode = 0xb96a; dprintk(4, "%p\n", av7110); @@ -785,7 +775,7 @@ static int StartHWFilter(struct dvb_demux_filter *dvbdmxfilter) av7110_p2t_init(&av7110->p2t_filter[dvbdmxfilter->index], dvbdmxfeed); } - buf[0] = (COMTYPE_PID_FILTER << 8) + AddPIDFilter; + buf[0] = (COMTYPE_PID_FILTER << 8) + AV7110_ADD_PID_FILTER; buf[1] = 16; buf[2] = dvbdmxfeed->pid; buf[3] = mode; @@ -828,7 +818,7 @@ static int StopHWFilter(struct dvb_demux_filter *dvbdmxfilter) av7110->handle2filter[handle] = NULL; - buf[0] = (COMTYPE_PID_FILTER << 8) + DelPIDFilter; + buf[0] = (COMTYPE_PID_FILTER << 8) + AV7110_DEL_PID_FILTER; buf[1] = 1; buf[2] = handle; ret = av7110_fw_request(av7110, buf, 3, answ, 2); @@ -873,7 +863,8 @@ static int dvb_feed_start_pid(struct dvb_demux_feed *dvbdmxfeed) if (dvbdmxfeed->pes_type < 2 && npids[0]) if (av7110->fe_synced) { - ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, + AV7110_SCAN, 0); if (ret) return ret; } @@ -1911,11 +1902,13 @@ static int av7110_fe_lock_fix(struct av7110 *av7110, enum fe_status status) av7110->pids[DMX_PES_TELETEXT], 0, av7110->pids[DMX_PES_PCR]); if (!ret) - ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, + AV7110_SCAN, 0); } else { ret = SetPIDs(av7110, 0, 0, 0, 0, 0); if (!ret) { - ret = av7110_fw_cmd(av7110, COMTYPE_PID_FILTER, FlushTSQueue, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_PID_FILTER, + AV7110_FLUSH_TS_QUEUE, 0); if (!ret) ret = av7110_wait_msgstate(av7110, GPMQBusy); } @@ -2272,7 +2265,7 @@ static int frontend_init(struct av7110 *av7110) * original Roberto Deza's hardware: * * rps1 code for budgetpatch will copy internal HS event to GPIO3 pin. - * GPIO3 is in budget-patch hardware connectd to port B VSYNC + * GPIO3 is in budget-patch hardware connected to port B VSYNC * HS is an internal event of 7146, accessible with RPS * and temporarily raised high every n lines * (n in defined in the RPS_THRESH1 counter threshold) @@ -2785,8 +2778,6 @@ static void av7110_irq(struct saa7146_dev *dev, u32 *isr) { struct av7110 *av7110 = dev->ext_priv; - //print_time("av7110_irq"); - /* Note: Don't try to handle the DEBI error irq (MASK_18), in * intel mode the timeout is asserted all the time... */ diff --git a/drivers/staging/media/av7110/av7110.h b/drivers/staging/media/av7110/av7110.h index b584754f4be0..797dda437cbe 100644 --- a/drivers/staging/media/av7110/av7110.h +++ b/drivers/staging/media/av7110/av7110.h @@ -243,8 +243,8 @@ struct av7110 { struct dvb_video_events video_events; video_size_t video_size; - u16 wssMode; - u16 wssData; + u16 wss_mode; + u16 wss_data; struct infrared ir; diff --git a/drivers/staging/media/av7110/av7110_av.c b/drivers/staging/media/av7110/av7110_av.c index 2993ac43c49c..22f1335d371b 100644 --- a/drivers/staging/media/av7110/av7110_av.c +++ b/drivers/staging/media/av7110/av7110_av.c @@ -111,7 +111,7 @@ int av7110_av_start_record(struct av7110 *av7110, int av, if (av7110->playing || (av7110->rec_mode & av)) return -EBUSY; - av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); + av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, AV7110_REC_PLAY_STOP, 0); dvbdmx->recording = 1; av7110->rec_mode |= av; @@ -121,7 +121,9 @@ int av7110_av_start_record(struct av7110 *av7110, int av, dvbdmx->pesfilter[0]->pid, dvb_filter_pes2ts_cb, (void *)dvbdmx->pesfilter[0]); - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AudioPES, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_RECORD, 2, + AV7110_AUDIO_PES, 0); break; case RP_VIDEO: @@ -129,7 +131,9 @@ int av7110_av_start_record(struct av7110 *av7110, int av, dvbdmx->pesfilter[1]->pid, dvb_filter_pes2ts_cb, (void *)dvbdmx->pesfilter[1]); - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, VideoPES, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_RECORD, 2, + AV7110_VIDEO_PES, 0); break; case RP_AV: @@ -141,7 +145,9 @@ int av7110_av_start_record(struct av7110 *av7110, int av, dvbdmx->pesfilter[1]->pid, dvb_filter_pes2ts_cb, (void *)dvbdmx->pesfilter[1]); - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AV_PES, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_RECORD, 2, + AV7110_AV_PES, 0); break; } return ret; @@ -158,7 +164,7 @@ int av7110_av_start_play(struct av7110 *av7110, int av) if (av7110->playing & av) return -EBUSY; - av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); + av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, AV7110_REC_PLAY_STOP, 0); if (av7110->playing == RP_NONE) { av7110_ipack_reset(&av7110->ipack[0]); @@ -168,15 +174,21 @@ int av7110_av_start_play(struct av7110 *av7110, int av) av7110->playing |= av; switch (av7110->playing) { case RP_AUDIO: - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AudioPES, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_PLAY, 2, + AV7110_AUDIO_PES, 0); break; case RP_VIDEO: - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, VideoPES, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_PLAY, 2, + AV7110_VIDEO_PES, 0); av7110->sinfo = 0; break; case RP_AV: av7110->sinfo = 0; - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AV_PES, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_PLAY, 2, + AV7110_AV_PES, 0); break; } return ret; @@ -190,15 +202,19 @@ int av7110_av_stop(struct av7110 *av7110, int av) if (!(av7110->playing & av) && !(av7110->rec_mode & av)) return 0; - av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); + av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, AV7110_REC_PLAY_STOP, 0); if (av7110->playing) { av7110->playing &= ~av; switch (av7110->playing) { case RP_AUDIO: - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AudioPES, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_PLAY, 2, + AV7110_AUDIO_PES, 0); break; case RP_VIDEO: - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, VideoPES, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_PLAY, 2, + AV7110_VIDEO_PES, 0); break; case RP_NONE: ret = av7110_set_vidmode(av7110, av7110->vidmode); @@ -208,10 +224,14 @@ int av7110_av_stop(struct av7110 *av7110, int av) av7110->rec_mode &= ~av; switch (av7110->rec_mode) { case RP_AUDIO: - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AudioPES, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_RECORD, 2, + AV7110_AUDIO_PES, 0); break; case RP_VIDEO: - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, VideoPES, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_RECORD, 2, + AV7110_VIDEO_PES, 0); break; case RP_NONE: break; @@ -325,7 +345,8 @@ int av7110_set_vidmode(struct av7110 *av7110, enum av7110_video_mode mode) dprintk(2, "av7110:%p\n", av7110); - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, LoadVidCode, 1, mode); + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, + AV7110_LOAD_VID_CODE, 1, mode); if (!ret && !av7110->playing) { ret = ChangePIDs(av7110, av7110->pids[DMX_PES_VIDEO], @@ -333,7 +354,8 @@ int av7110_set_vidmode(struct av7110 *av7110, enum av7110_video_mode mode) av7110->pids[DMX_PES_TELETEXT], 0, av7110->pids[DMX_PES_PCR]); if (!ret) - ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, + AV7110_SCAN, 0); } return ret; } @@ -1168,7 +1190,8 @@ static int dvb_video_ioctl(struct file *file, } if (av7110->videostate.stream_source == VIDEO_SOURCE_MEMORY) { if (av7110->playing == RP_AV) { - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_STOP, 0); if (ret) break; av7110->playing &= ~RP_VIDEO; @@ -1184,7 +1207,8 @@ static int dvb_video_ioctl(struct file *file, case VIDEO_FREEZE: av7110->videostate.play_state = VIDEO_FREEZED; if (av7110->playing & RP_VIDEO) - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Pause, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_PAUSE, 0); else ret = vidcom(av7110, AV_VIDEO_CMD_FREEZE, 1); if (!ret) @@ -1193,7 +1217,8 @@ static int dvb_video_ioctl(struct file *file, case VIDEO_CONTINUE: if (av7110->playing & RP_VIDEO) - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Continue, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_CONTINUE, 0); if (!ret) ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); if (!ret) { @@ -1248,8 +1273,9 @@ static int dvb_video_ioctl(struct file *file, if (ret < 0) break; av7110->videostate.display_format = format; - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetPanScanType, - 1, av7110->display_panscan); + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, + AV7110_SET_PANSCAN_TYPE, 1, + av7110->display_panscan); break; } @@ -1259,8 +1285,8 @@ static int dvb_video_ioctl(struct file *file, break; } av7110->display_ar = arg; - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetMonitorType, - 1, (u16)arg); + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, + AV7110_SET_MONITOR_TYPE, 1, arg); break; #ifdef CONFIG_COMPAT @@ -1291,7 +1317,8 @@ static int dvb_video_ioctl(struct file *file, //note: arg is ignored by firmware if (av7110->playing & RP_VIDEO) ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, - __Scan_I, 2, AV_PES, 0); + AV7110_REC_PLAY_SCAN_I, 2, + AV7110_AV_PES, 0); else ret = vidcom(av7110, AV_VIDEO_CMD_FFWD, arg); if (!ret) { @@ -1303,7 +1330,9 @@ static int dvb_video_ioctl(struct file *file, case VIDEO_SLOWMOTION: if (av7110->playing & RP_VIDEO) { if (av7110->trickmode != TRICK_SLOW) - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Slow, 2, 0, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_SLOW, 2, + 0, 0); if (!ret) ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg); } else { @@ -1329,15 +1358,18 @@ static int dvb_video_ioctl(struct file *file, av7110_ipack_reset(&av7110->ipack[1]); if (av7110->playing == RP_AV) { ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, - __Play, 2, AV_PES, 0); + AV7110_REC_PLAY_PLAY, 2, + AV7110_AV_PES, 0); if (ret) break; if (av7110->trickmode == TRICK_FAST) ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, - __Scan_I, 2, AV_PES, 0); + AV7110_REC_PLAY_SCAN_I, 2, + AV7110_AV_PES, 0); if (av7110->trickmode == TRICK_SLOW) { ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, - __Slow, 2, 0, 0); + AV7110_REC_PLAY_SLOW, 2, + 0, 0); if (!ret) ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg); } @@ -1483,7 +1515,8 @@ static int dvb_audio_ioctl(struct file *file, av7110_ipack_reset(&av7110->ipack[0]); if (av7110->playing == RP_AV) ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, - __Play, 2, AV_PES, 0); + AV7110_REC_PLAY_PLAY, 2, + AV7110_AV_PES, 0); break; case AUDIO_SET_ID: diff --git a/drivers/staging/media/av7110/av7110_ca.c b/drivers/staging/media/av7110/av7110_ca.c index 63d9c97a5190..24876c8fdc8c 100644 --- a/drivers/staging/media/av7110/av7110_ca.c +++ b/drivers/staging/media/av7110/av7110_ca.c @@ -307,7 +307,8 @@ static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) mutex_unlock(&av7110->ioctl_mutex); return -EINVAL; } - av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetDescr, 5, + av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, + AV7110_SET_DESCR, 5, (descr->index << 8) | descr->parity, (descr->cw[0] << 8) | descr->cw[1], (descr->cw[2] << 8) | descr->cw[3], diff --git a/drivers/staging/media/av7110/av7110_hw.c b/drivers/staging/media/av7110/av7110_hw.c index 49ce295771e4..b0bd7666d48b 100644 --- a/drivers/staging/media/av7110/av7110_hw.c +++ b/drivers/staging/media/av7110/av7110_hw.c @@ -95,29 +95,6 @@ u32 av7110_debiread(struct av7110 *av7110, u32 config, int addr, unsigned int co return result; } -/* av7110 ARM core boot stuff */ -#if 0 -void av7110_reset_arm(struct av7110 *av7110) -{ - saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTLO); - - /* Disable DEBI and GPIO irq */ - SAA7146_IER_DISABLE(av7110->dev, MASK_19 | MASK_03); - SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); - - saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTHI); - msleep(30); /* the firmware needs some time to initialize */ - - ARM_ResetMailBox(av7110); - - SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); - SAA7146_IER_ENABLE(av7110->dev, MASK_03); - - av7110->arm_ready = 1; - dprintk(1, "reset ARM\n"); -} -#endif /* 0 */ - static int waitdebi(struct av7110 *av7110, int adr, int state) { int k; @@ -498,29 +475,6 @@ int av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...) return ret; } -#if 0 -int av7110_send_ci_cmd(struct av7110 *av7110, u8 subcom, u8 *buf, u8 len) -{ - int i, ret; - u16 cmd[18] = { ((COMTYPE_COMMON_IF << 8) + subcom), - 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - - dprintk(4, "%p\n", av7110); - - for (i = 0; i < len && i < 32; i++) { - if (i % 2 == 0) - cmd[(i / 2) + 2] = (u16)(buf[i]) << 8; - else - cmd[(i / 2) + 2] |= buf[i]; - } - - ret = av7110_send_fw_cmd(av7110, cmd, 18); - if (ret && ret != -ERESTARTSYS) - pr_err("%s(): error %d\n", __func__, ret); - return ret; -} -#endif /* 0 */ - int av7110_fw_request(struct av7110 *av7110, u16 *request_buf, int request_buf_len, u16 *reply_buf, int reply_buf_len) { diff --git a/drivers/staging/media/av7110/av7110_hw.h b/drivers/staging/media/av7110/av7110_hw.h index d4579f411c56..3afe474da871 100644 --- a/drivers/staging/media/av7110/av7110_hw.h +++ b/drivers/staging/media/av7110/av7110_hw.h @@ -21,12 +21,12 @@ enum av7110_bootstate { }; enum av7110_type_rec_play_format { - RP_None, - AudioPES, - AudioMp2, - AudioPCM, - VideoPES, - AV_PES + AV7110_RP_NONE, + AV7110_AUDIO_PES, + AV7110_AUDIO_MP2, + AV7110_AUDIO_PCM, + AV7110_VIDEO_PES, + AV7110_AV_PES, }; enum av7110_osd_palette_type { @@ -112,19 +112,19 @@ enum av7110_osd_command { }; enum av7110_pid_command { - MultiPID, - VideoPID, - AudioPID, - InitFilt, - FiltError, - NewVersion, - CacheError, - AddPIDFilter, - DelPIDFilter, - Scan, - SetDescr, - SetIR, - FlushTSQueue + AV7110_MULTI_PID, + AV7110_VIDEO_PID, + AV7110_AUDIO_PID, + AV7110_INIT_FILT, + AV7110_FILT_ERROR, + AV7110_NEW_VERSION, + AV7110_CACHE_ERROR, + AV7110_ADD_PID_FILTER, + AV7110_DEL_PID_FILTER, + AV7110_SCAN, + AV7110_SET_DESCR, + AV7110_SET_IR, + AV7110_FLUSH_TS_QUEUE, }; enum av7110_mpeg_command { @@ -158,24 +158,24 @@ enum av7110_request_command { }; enum av7110_encoder_command { - SetVidMode, - SetTestMode, - LoadVidCode, - SetMonitorType, - SetPanScanType, - SetFreezeMode, - SetWSSConfig + AV7110_SET_VID_MODE, + AV7110_SET_TEST_MODE, + AV7110_LOAD_VID_CODE, + AV7110_SET_MONITOR_TYPE, + AV7110_SET_PANSCAN_TYPE, + AV7110_SET_FREEZE_MODE, + AV7110_SET_WSS_CONFIG, }; enum av7110_rec_play_state { - __Record, - __Stop, - __Play, - __Pause, - __Slow, - __FF_IP, - __Scan_I, - __Continue + AV7110_REC_PLAY_RECORD, + AV7110_REC_PLAY_STOP, + AV7110_REC_PLAY_PLAY, + AV7110_REC_PLAY_PAUSE, + AV7110_REC_PLAY_SLOW, + AV7110_REC_PLAY_FF_IP, + AV7110_REC_PLAY_SCAN_I, + AV7110_REC_PLAY_CONTINUE, }; enum av7110_fw_cmd_misc { @@ -452,7 +452,8 @@ static inline int SendDAC(struct av7110 *av7110, u8 addr, u8 data) static inline int av7710_set_video_mode(struct av7110 *av7110, int mode) { - return av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetVidMode, 1, mode); + return av7110_fw_cmd(av7110, COMTYPE_ENCODER, + AV7110_SET_VID_MODE, 1, mode); } static inline int vidcom(struct av7110 *av7110, u32 com, u32 arg) diff --git a/drivers/staging/media/av7110/av7110_ir.c b/drivers/staging/media/av7110/av7110_ir.c index fdae467fd7ab..9b7bf1857868 100644 --- a/drivers/staging/media/av7110/av7110_ir.c +++ b/drivers/staging/media/av7110/av7110_ir.c @@ -71,8 +71,8 @@ int av7110_set_ir_config(struct av7110 *av7110) { dprintk(4, "ir config = %08x\n", av7110->ir.ir_config); - return av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1, - av7110->ir.ir_config); + return av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, + AV7110_SET_IR, 1, av7110->ir.ir_config); } static int change_protocol(struct rc_dev *rcdev, u64 *rc_type) diff --git a/drivers/staging/media/av7110/av7110_v4l.c b/drivers/staging/media/av7110/av7110_v4l.c index 200a7a29ea31..aed7581fa8a5 100644 --- a/drivers/staging/media/av7110/av7110_v4l.c +++ b/drivers/staging/media/av7110/av7110_v4l.c @@ -556,7 +556,7 @@ static int vidioc_g_fmt_sliced_vbi_out(struct file *file, void *fh, if (FW_VERSION(av7110->arm_app) < 0x2623) return -EINVAL; memset(&f->fmt.sliced, 0, sizeof(f->fmt.sliced)); - if (av7110->wssMode) { + if (av7110->wss_mode) { f->fmt.sliced.service_set = V4L2_SLICED_WSS_625; f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625; } @@ -596,14 +596,14 @@ static int vidioc_s_fmt_sliced_vbi_out(struct file *file, void *fh, return -EINVAL; if (f->fmt.sliced.service_set & V4L2_SLICED_WSS_625) { /* WSS controlled by userspace */ - av7110->wssMode = 1; - av7110->wssData = 0; + av7110->wss_mode = 1; + av7110->wss_data = 0; } else { /* WSS controlled by firmware */ - av7110->wssMode = 0; - av7110->wssData = 0; + av7110->wss_mode = 0; + av7110->wss_data = 0; return av7110_fw_cmd(av7110, COMTYPE_ENCODER, - SetWSSConfig, 1, 0); + AV7110_SET_WSS_CONFIG, 1, 0); } return 0; } @@ -616,17 +616,19 @@ static ssize_t av7110_vbi_write(struct file *file, const char __user *data, size int rc; dprintk(2, "\n"); - if (FW_VERSION(av7110->arm_app) < 0x2623 || !av7110->wssMode || count != sizeof(d)) + if (FW_VERSION(av7110->arm_app) < 0x2623 || + !av7110->wss_mode || count != sizeof(d)) return -EINVAL; if (copy_from_user(&d, data, count)) return -EFAULT; if ((d.id != 0 && d.id != V4L2_SLICED_WSS_625) || d.field != 0 || d.line != 23) return -EINVAL; if (d.id) - av7110->wssData = ((d.data[1] << 8) & 0x3f00) | d.data[0]; + av7110->wss_data = ((d.data[1] << 8) & 0x3f00) | d.data[0]; else - av7110->wssData = 0x8000; - rc = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 1, av7110->wssData); + av7110->wss_data = 0x8000; + rc = av7110_fw_cmd(av7110, COMTYPE_ENCODER, + AV7110_SET_WSS_CONFIG, 2, 1, av7110->wss_data); return (rc < 0) ? rc : count; } diff --git a/drivers/staging/media/deprecated/atmel/Kconfig b/drivers/staging/media/deprecated/atmel/Kconfig deleted file mode 100644 index 418841ea5a0d..000000000000 --- a/drivers/staging/media/deprecated/atmel/Kconfig +++ /dev/null @@ -1,47 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only - -comment "Atmel media platform drivers" - -config VIDEO_ATMEL_ISC - tristate "ATMEL Image Sensor Controller (ISC) support (DEPRECATED)" - depends on V4L_PLATFORM_DRIVERS - depends on VIDEO_DEV && COMMON_CLK - depends on ARCH_AT91 || COMPILE_TEST - depends on !VIDEO_MICROCHIP_ISC_BASE || COMPILE_TEST - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select VIDEOBUF2_DMA_CONTIG - select REGMAP_MMIO - select V4L2_FWNODE - select VIDEO_ATMEL_ISC_BASE - help - This module makes the ATMEL Image Sensor Controller available - as a v4l2 device. - - This driver is deprecated and is scheduled for removal by - the beginning of 2026. See the TODO file for more information. - -config VIDEO_ATMEL_XISC - tristate "ATMEL eXtended Image Sensor Controller (XISC) support (DEPRECATED)" - depends on V4L_PLATFORM_DRIVERS - depends on VIDEO_DEV && COMMON_CLK - depends on ARCH_AT91 || COMPILE_TEST - depends on !VIDEO_MICROCHIP_ISC_BASE || COMPILE_TEST - select VIDEOBUF2_DMA_CONTIG - select REGMAP_MMIO - select V4L2_FWNODE - select VIDEO_ATMEL_ISC_BASE - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - help - This module makes the ATMEL eXtended Image Sensor Controller - available as a v4l2 device. - - This driver is deprecated and is scheduled for removal by - the beginning of 2026. See the TODO file for more information. - -config VIDEO_ATMEL_ISC_BASE - tristate - default n - help - ATMEL ISC and XISC common code base. diff --git a/drivers/staging/media/deprecated/atmel/Makefile b/drivers/staging/media/deprecated/atmel/Makefile deleted file mode 100644 index 34eaeeac5bba..000000000000 --- a/drivers/staging/media/deprecated/atmel/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -atmel-isc-objs = atmel-sama5d2-isc.o -atmel-xisc-objs = atmel-sama7g5-isc.o -atmel-isc-common-objs = atmel-isc-base.o atmel-isc-clk.o - -obj-$(CONFIG_VIDEO_ATMEL_ISC_BASE) += atmel-isc-common.o -obj-$(CONFIG_VIDEO_ATMEL_ISC) += atmel-isc.o -obj-$(CONFIG_VIDEO_ATMEL_XISC) += atmel-xisc.o diff --git a/drivers/staging/media/deprecated/atmel/TODO b/drivers/staging/media/deprecated/atmel/TODO deleted file mode 100644 index 71691df07a80..000000000000 --- a/drivers/staging/media/deprecated/atmel/TODO +++ /dev/null @@ -1,34 +0,0 @@ -The Atmel ISC driver is not compliant with media controller specification. -In order to evolve this driver, it has to move to media controller, to -support enhanced features and future products which embed it. -The move to media controller involves several changes which are -not backwards compatible with the current usability of the driver. - -The best example is the way the format is propagated from the top video -driver /dev/videoX down to the sensor. - -In a simple configuration sensor ==> isc , the isc just calls subdev s_fmt -and controls the sensor directly. This is achieved by having a lot of code -inside the driver that will query the subdev at probe time and make a list -of formats which are usable. -Basically the user has nothing to configure, as the isc will handle -everything at the top level. This is an easy way to capture, but also comes -with the drawback of lack of flexibility. -In a more complicated pipeline -sensor ==> controller 1 ==> controller 2 ==> isc -this will not be achievable, as controller 1 and controller 2 might be -media-controller configurable, and will not propagate the formats down to -the sensor. - -After discussions with the media maintainers, the decision is to move -Atmel ISC to staging as-is, to keep the Kconfig symbols and the users -to the driver in staging. Thus, all the existing users of the non -media-controller paradigm will continue to be happy and use the old config -way. - -The new driver was added in the media subsystem with a different -symbol, with the conversion to media controller done, and new users -of the driver will be able to use all the new features. - -The replacement driver is named VIDEO_MICROCHIP_ISC or -VIDEO_MICROCHIP_XISC depending on the product flavor. diff --git a/drivers/staging/media/deprecated/atmel/atmel-isc-base.c b/drivers/staging/media/deprecated/atmel/atmel-isc-base.c deleted file mode 100644 index fb9ee8547392..000000000000 --- a/drivers/staging/media/deprecated/atmel/atmel-isc-base.c +++ /dev/null @@ -1,2008 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Microchip Image Sensor Controller (ISC) common driver base - * - * Copyright (C) 2016-2019 Microchip Technology, Inc. - * - * Author: Songjun Wu - * Author: Eugen Hristev <eugen.hristev@microchip.com> - * - */ -#include <linux/delay.h> -#include <linux/interrupt.h> -#include <linux/math64.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/of_graph.h> -#include <linux/platform_device.h> -#include <linux/pm_runtime.h> -#include <linux/regmap.h> -#include <linux/videodev2.h> -#include <linux/atmel-isc-media.h> - -#include <media/v4l2-ctrls.h> -#include <media/v4l2-device.h> -#include <media/v4l2-event.h> -#include <media/v4l2-image-sizes.h> -#include <media/v4l2-ioctl.h> -#include <media/v4l2-fwnode.h> -#include <media/v4l2-subdev.h> -#include <media/videobuf2-dma-contig.h> - -#include "atmel-isc-regs.h" -#include "atmel-isc.h" - -static unsigned int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "debug level (0-2)"); - -static unsigned int sensor_preferred = 1; -module_param(sensor_preferred, uint, 0644); -MODULE_PARM_DESC(sensor_preferred, - "Sensor is preferred to output the specified format (1-on 0-off), default 1"); - -#define ISC_IS_FORMAT_RAW(mbus_code) \ - (((mbus_code) & 0xf000) == 0x3000) - -#define ISC_IS_FORMAT_GREY(mbus_code) \ - (((mbus_code) == MEDIA_BUS_FMT_Y10_1X10) | \ - (((mbus_code) == MEDIA_BUS_FMT_Y8_1X8))) - -static inline void isc_update_v4l2_ctrls(struct isc_device *isc) -{ - struct isc_ctrls *ctrls = &isc->ctrls; - - /* In here we set the v4l2 controls w.r.t. our pipeline config */ - v4l2_ctrl_s_ctrl(isc->r_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_R]); - v4l2_ctrl_s_ctrl(isc->b_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_B]); - v4l2_ctrl_s_ctrl(isc->gr_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GR]); - v4l2_ctrl_s_ctrl(isc->gb_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GB]); - - v4l2_ctrl_s_ctrl(isc->r_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_R]); - v4l2_ctrl_s_ctrl(isc->b_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_B]); - v4l2_ctrl_s_ctrl(isc->gr_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_GR]); - v4l2_ctrl_s_ctrl(isc->gb_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_GB]); -} - -static inline void isc_update_awb_ctrls(struct isc_device *isc) -{ - struct isc_ctrls *ctrls = &isc->ctrls; - - /* In here we set our actual hw pipeline config */ - - regmap_write(isc->regmap, ISC_WB_O_RGR, - ((ctrls->offset[ISC_HIS_CFG_MODE_R])) | - ((ctrls->offset[ISC_HIS_CFG_MODE_GR]) << 16)); - regmap_write(isc->regmap, ISC_WB_O_BGB, - ((ctrls->offset[ISC_HIS_CFG_MODE_B])) | - ((ctrls->offset[ISC_HIS_CFG_MODE_GB]) << 16)); - regmap_write(isc->regmap, ISC_WB_G_RGR, - ctrls->gain[ISC_HIS_CFG_MODE_R] | - (ctrls->gain[ISC_HIS_CFG_MODE_GR] << 16)); - regmap_write(isc->regmap, ISC_WB_G_BGB, - ctrls->gain[ISC_HIS_CFG_MODE_B] | - (ctrls->gain[ISC_HIS_CFG_MODE_GB] << 16)); -} - -static inline void isc_reset_awb_ctrls(struct isc_device *isc) -{ - unsigned int c; - - for (c = ISC_HIS_CFG_MODE_GR; c <= ISC_HIS_CFG_MODE_B; c++) { - /* gains have a fixed point at 9 decimals */ - isc->ctrls.gain[c] = 1 << 9; - /* offsets are in 2's complements */ - isc->ctrls.offset[c] = 0; - } -} - - -static int isc_queue_setup(struct vb2_queue *vq, - unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], struct device *alloc_devs[]) -{ - struct isc_device *isc = vb2_get_drv_priv(vq); - unsigned int size = isc->fmt.fmt.pix.sizeimage; - - if (*nplanes) - return sizes[0] < size ? -EINVAL : 0; - - *nplanes = 1; - sizes[0] = size; - - return 0; -} - -static int isc_buffer_prepare(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct isc_device *isc = vb2_get_drv_priv(vb->vb2_queue); - unsigned long size = isc->fmt.fmt.pix.sizeimage; - - if (vb2_plane_size(vb, 0) < size) { - v4l2_err(&isc->v4l2_dev, "buffer too small (%lu < %lu)\n", - vb2_plane_size(vb, 0), size); - return -EINVAL; - } - - vb2_set_plane_payload(vb, 0, size); - - vbuf->field = isc->fmt.fmt.pix.field; - - return 0; -} - -static void isc_crop_pfe(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - u32 h, w; - - h = isc->fmt.fmt.pix.height; - w = isc->fmt.fmt.pix.width; - - /* - * In case the sensor is not RAW, it will output a pixel (12-16 bits) - * with two samples on the ISC Data bus (which is 8-12) - * ISC will count each sample, so, we need to multiply these values - * by two, to get the real number of samples for the required pixels. - */ - if (!ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code)) { - h <<= 1; - w <<= 1; - } - - /* - * We limit the column/row count that the ISC will output according - * to the configured resolution that we want. - * This will avoid the situation where the sensor is misconfigured, - * sending more data, and the ISC will just take it and DMA to memory, - * causing corruption. - */ - regmap_write(regmap, ISC_PFE_CFG1, - (ISC_PFE_CFG1_COLMIN(0) & ISC_PFE_CFG1_COLMIN_MASK) | - (ISC_PFE_CFG1_COLMAX(w - 1) & ISC_PFE_CFG1_COLMAX_MASK)); - - regmap_write(regmap, ISC_PFE_CFG2, - (ISC_PFE_CFG2_ROWMIN(0) & ISC_PFE_CFG2_ROWMIN_MASK) | - (ISC_PFE_CFG2_ROWMAX(h - 1) & ISC_PFE_CFG2_ROWMAX_MASK)); - - regmap_update_bits(regmap, ISC_PFE_CFG0, - ISC_PFE_CFG0_COLEN | ISC_PFE_CFG0_ROWEN, - ISC_PFE_CFG0_COLEN | ISC_PFE_CFG0_ROWEN); -} - -static void isc_start_dma(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - u32 sizeimage = isc->fmt.fmt.pix.sizeimage; - u32 dctrl_dview; - dma_addr_t addr0; - - addr0 = vb2_dma_contig_plane_dma_addr(&isc->cur_frm->vb.vb2_buf, 0); - regmap_write(regmap, ISC_DAD0 + isc->offsets.dma, addr0); - - switch (isc->config.fourcc) { - case V4L2_PIX_FMT_YUV420: - regmap_write(regmap, ISC_DAD1 + isc->offsets.dma, - addr0 + (sizeimage * 2) / 3); - regmap_write(regmap, ISC_DAD2 + isc->offsets.dma, - addr0 + (sizeimage * 5) / 6); - break; - case V4L2_PIX_FMT_YUV422P: - regmap_write(regmap, ISC_DAD1 + isc->offsets.dma, - addr0 + sizeimage / 2); - regmap_write(regmap, ISC_DAD2 + isc->offsets.dma, - addr0 + (sizeimage * 3) / 4); - break; - default: - break; - } - - dctrl_dview = isc->config.dctrl_dview; - - regmap_write(regmap, ISC_DCTRL + isc->offsets.dma, - dctrl_dview | ISC_DCTRL_IE_IS); - spin_lock(&isc->awb_lock); - regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_CAPTURE); - spin_unlock(&isc->awb_lock); -} - -static void isc_set_pipeline(struct isc_device *isc, u32 pipeline) -{ - struct regmap *regmap = isc->regmap; - struct isc_ctrls *ctrls = &isc->ctrls; - u32 val, bay_cfg; - const u32 *gamma; - unsigned int i; - - /* WB-->CFA-->CC-->GAM-->CSC-->CBC-->SUB422-->SUB420 */ - for (i = 0; i < ISC_PIPE_LINE_NODE_NUM; i++) { - val = pipeline & BIT(i) ? 1 : 0; - regmap_field_write(isc->pipeline[i], val); - } - - if (!pipeline) - return; - - bay_cfg = isc->config.sd_format->cfa_baycfg; - - regmap_write(regmap, ISC_WB_CFG, bay_cfg); - isc_update_awb_ctrls(isc); - isc_update_v4l2_ctrls(isc); - - regmap_write(regmap, ISC_CFA_CFG, bay_cfg | ISC_CFA_CFG_EITPOL); - - gamma = &isc->gamma_table[ctrls->gamma_index][0]; - regmap_bulk_write(regmap, ISC_GAM_BENTRY, gamma, GAMMA_ENTRIES); - regmap_bulk_write(regmap, ISC_GAM_GENTRY, gamma, GAMMA_ENTRIES); - regmap_bulk_write(regmap, ISC_GAM_RENTRY, gamma, GAMMA_ENTRIES); - - isc->config_dpc(isc); - isc->config_csc(isc); - isc->config_cbc(isc); - isc->config_cc(isc); - isc->config_gam(isc); -} - -static int isc_update_profile(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - u32 sr; - int counter = 100; - - regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_UPPRO); - - regmap_read(regmap, ISC_CTRLSR, &sr); - while ((sr & ISC_CTRL_UPPRO) && counter--) { - usleep_range(1000, 2000); - regmap_read(regmap, ISC_CTRLSR, &sr); - } - - if (counter < 0) { - v4l2_warn(&isc->v4l2_dev, "Time out to update profile\n"); - return -ETIMEDOUT; - } - - return 0; -} - -static void isc_set_histogram(struct isc_device *isc, bool enable) -{ - struct regmap *regmap = isc->regmap; - struct isc_ctrls *ctrls = &isc->ctrls; - - if (enable) { - regmap_write(regmap, ISC_HIS_CFG + isc->offsets.his, - ISC_HIS_CFG_MODE_GR | - (isc->config.sd_format->cfa_baycfg - << ISC_HIS_CFG_BAYSEL_SHIFT) | - ISC_HIS_CFG_RAR); - regmap_write(regmap, ISC_HIS_CTRL + isc->offsets.his, - ISC_HIS_CTRL_EN); - regmap_write(regmap, ISC_INTEN, ISC_INT_HISDONE); - ctrls->hist_id = ISC_HIS_CFG_MODE_GR; - isc_update_profile(isc); - regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ); - - ctrls->hist_stat = HIST_ENABLED; - } else { - regmap_write(regmap, ISC_INTDIS, ISC_INT_HISDONE); - regmap_write(regmap, ISC_HIS_CTRL + isc->offsets.his, - ISC_HIS_CTRL_DIS); - - ctrls->hist_stat = HIST_DISABLED; - } -} - -static int isc_configure(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - u32 pfe_cfg0, dcfg, mask, pipeline; - struct isc_subdev_entity *subdev = isc->current_subdev; - - pfe_cfg0 = isc->config.sd_format->pfe_cfg0_bps; - pipeline = isc->config.bits_pipeline; - - dcfg = isc->config.dcfg_imode | isc->dcfg; - - pfe_cfg0 |= subdev->pfe_cfg0 | ISC_PFE_CFG0_MODE_PROGRESSIVE; - mask = ISC_PFE_CFG0_BPS_MASK | ISC_PFE_CFG0_HPOL_LOW | - ISC_PFE_CFG0_VPOL_LOW | ISC_PFE_CFG0_PPOL_LOW | - ISC_PFE_CFG0_MODE_MASK | ISC_PFE_CFG0_CCIR_CRC | - ISC_PFE_CFG0_CCIR656 | ISC_PFE_CFG0_MIPI; - - regmap_update_bits(regmap, ISC_PFE_CFG0, mask, pfe_cfg0); - - isc->config_rlp(isc); - - regmap_write(regmap, ISC_DCFG + isc->offsets.dma, dcfg); - - /* Set the pipeline */ - isc_set_pipeline(isc, pipeline); - - /* - * The current implemented histogram is available for RAW R, B, GB, GR - * channels. We need to check if sensor is outputting RAW BAYER - */ - if (isc->ctrls.awb && - ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code)) - isc_set_histogram(isc, true); - else - isc_set_histogram(isc, false); - - /* Update profile */ - return isc_update_profile(isc); -} - -static int isc_start_streaming(struct vb2_queue *vq, unsigned int count) -{ - struct isc_device *isc = vb2_get_drv_priv(vq); - struct regmap *regmap = isc->regmap; - struct isc_buffer *buf; - unsigned long flags; - int ret; - - /* Enable stream on the sub device */ - ret = v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 1); - if (ret && ret != -ENOIOCTLCMD) { - v4l2_err(&isc->v4l2_dev, "stream on failed in subdev %d\n", - ret); - goto err_start_stream; - } - - ret = pm_runtime_resume_and_get(isc->dev); - if (ret < 0) { - v4l2_err(&isc->v4l2_dev, "RPM resume failed in subdev %d\n", - ret); - goto err_pm_get; - } - - ret = isc_configure(isc); - if (unlikely(ret)) - goto err_configure; - - /* Enable DMA interrupt */ - regmap_write(regmap, ISC_INTEN, ISC_INT_DDONE); - - spin_lock_irqsave(&isc->dma_queue_lock, flags); - - isc->sequence = 0; - isc->stop = false; - reinit_completion(&isc->comp); - - isc->cur_frm = list_first_entry(&isc->dma_queue, - struct isc_buffer, list); - list_del(&isc->cur_frm->list); - - isc_crop_pfe(isc); - isc_start_dma(isc); - - spin_unlock_irqrestore(&isc->dma_queue_lock, flags); - - /* if we streaming from RAW, we can do one-shot white balance adj */ - if (ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code)) - v4l2_ctrl_activate(isc->do_wb_ctrl, true); - - return 0; - -err_configure: - pm_runtime_put_sync(isc->dev); -err_pm_get: - v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 0); - -err_start_stream: - spin_lock_irqsave(&isc->dma_queue_lock, flags); - list_for_each_entry(buf, &isc->dma_queue, list) - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); - INIT_LIST_HEAD(&isc->dma_queue); - spin_unlock_irqrestore(&isc->dma_queue_lock, flags); - - return ret; -} - -static void isc_stop_streaming(struct vb2_queue *vq) -{ - struct isc_device *isc = vb2_get_drv_priv(vq); - unsigned long flags; - struct isc_buffer *buf; - int ret; - - mutex_lock(&isc->awb_mutex); - v4l2_ctrl_activate(isc->do_wb_ctrl, false); - - isc->stop = true; - - /* Wait until the end of the current frame */ - if (isc->cur_frm && !wait_for_completion_timeout(&isc->comp, 5 * HZ)) - v4l2_err(&isc->v4l2_dev, - "Timeout waiting for end of the capture\n"); - - mutex_unlock(&isc->awb_mutex); - - /* Disable DMA interrupt */ - regmap_write(isc->regmap, ISC_INTDIS, ISC_INT_DDONE); - - pm_runtime_put_sync(isc->dev); - - /* Disable stream on the sub device */ - ret = v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 0); - if (ret && ret != -ENOIOCTLCMD) - v4l2_err(&isc->v4l2_dev, "stream off failed in subdev\n"); - - /* Release all active buffers */ - spin_lock_irqsave(&isc->dma_queue_lock, flags); - if (unlikely(isc->cur_frm)) { - vb2_buffer_done(&isc->cur_frm->vb.vb2_buf, - VB2_BUF_STATE_ERROR); - isc->cur_frm = NULL; - } - list_for_each_entry(buf, &isc->dma_queue, list) - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); - INIT_LIST_HEAD(&isc->dma_queue); - spin_unlock_irqrestore(&isc->dma_queue_lock, flags); -} - -static void isc_buffer_queue(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct isc_buffer *buf = container_of(vbuf, struct isc_buffer, vb); - struct isc_device *isc = vb2_get_drv_priv(vb->vb2_queue); - unsigned long flags; - - spin_lock_irqsave(&isc->dma_queue_lock, flags); - if (!isc->cur_frm && list_empty(&isc->dma_queue) && - vb2_start_streaming_called(vb->vb2_queue)) { - isc->cur_frm = buf; - isc_start_dma(isc); - } else - list_add_tail(&buf->list, &isc->dma_queue); - spin_unlock_irqrestore(&isc->dma_queue_lock, flags); -} - -static struct isc_format *find_format_by_fourcc(struct isc_device *isc, - unsigned int fourcc) -{ - unsigned int num_formats = isc->num_user_formats; - struct isc_format *fmt; - unsigned int i; - - for (i = 0; i < num_formats; i++) { - fmt = isc->user_formats[i]; - if (fmt->fourcc == fourcc) - return fmt; - } - - return NULL; -} - -static const struct vb2_ops isc_vb2_ops = { - .queue_setup = isc_queue_setup, - .buf_prepare = isc_buffer_prepare, - .start_streaming = isc_start_streaming, - .stop_streaming = isc_stop_streaming, - .buf_queue = isc_buffer_queue, -}; - -static int isc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - strscpy(cap->driver, "microchip-isc", sizeof(cap->driver)); - strscpy(cap->card, "Atmel Image Sensor Controller", sizeof(cap->card)); - - return 0; -} - -static int isc_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct isc_device *isc = video_drvdata(file); - u32 index = f->index; - u32 i, supported_index; - - if (index < isc->controller_formats_size) { - f->pixelformat = isc->controller_formats[index].fourcc; - return 0; - } - - index -= isc->controller_formats_size; - - supported_index = 0; - - for (i = 0; i < isc->formats_list_size; i++) { - if (!ISC_IS_FORMAT_RAW(isc->formats_list[i].mbus_code) || - !isc->formats_list[i].sd_support) - continue; - if (supported_index == index) { - f->pixelformat = isc->formats_list[i].fourcc; - return 0; - } - supported_index++; - } - - return -EINVAL; -} - -static int isc_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *fmt) -{ - struct isc_device *isc = video_drvdata(file); - - *fmt = isc->fmt; - - return 0; -} - -/* - * Checks the current configured format, if ISC can output it, - * considering which type of format the ISC receives from the sensor - */ -static int isc_try_validate_formats(struct isc_device *isc) -{ - int ret; - bool bayer = false, yuv = false, rgb = false, grey = false; - - /* all formats supported by the RLP module are OK */ - switch (isc->try_config.fourcc) { - case V4L2_PIX_FMT_SBGGR8: - case V4L2_PIX_FMT_SGBRG8: - case V4L2_PIX_FMT_SGRBG8: - case V4L2_PIX_FMT_SRGGB8: - case V4L2_PIX_FMT_SBGGR10: - case V4L2_PIX_FMT_SGBRG10: - case V4L2_PIX_FMT_SGRBG10: - case V4L2_PIX_FMT_SRGGB10: - case V4L2_PIX_FMT_SBGGR12: - case V4L2_PIX_FMT_SGBRG12: - case V4L2_PIX_FMT_SGRBG12: - case V4L2_PIX_FMT_SRGGB12: - ret = 0; - bayer = true; - break; - - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_YUV422P: - case V4L2_PIX_FMT_YUYV: - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_VYUY: - ret = 0; - yuv = true; - break; - - case V4L2_PIX_FMT_RGB565: - case V4L2_PIX_FMT_ABGR32: - case V4L2_PIX_FMT_XBGR32: - case V4L2_PIX_FMT_ARGB444: - case V4L2_PIX_FMT_ARGB555: - ret = 0; - rgb = true; - break; - case V4L2_PIX_FMT_GREY: - case V4L2_PIX_FMT_Y10: - case V4L2_PIX_FMT_Y16: - ret = 0; - grey = true; - break; - default: - /* any other different formats are not supported */ - ret = -EINVAL; - } - v4l2_dbg(1, debug, &isc->v4l2_dev, - "Format validation, requested rgb=%u, yuv=%u, grey=%u, bayer=%u\n", - rgb, yuv, grey, bayer); - - /* we cannot output RAW if we do not receive RAW */ - if ((bayer) && !ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) - return -EINVAL; - - /* we cannot output GREY if we do not receive RAW/GREY */ - if (grey && !ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code) && - !ISC_IS_FORMAT_GREY(isc->try_config.sd_format->mbus_code)) - return -EINVAL; - - return ret; -} - -/* - * Configures the RLP and DMA modules, depending on the output format - * configured for the ISC. - * If direct_dump == true, just dump raw data 8/16 bits depending on format. - */ -static int isc_try_configure_rlp_dma(struct isc_device *isc, bool direct_dump) -{ - isc->try_config.rlp_cfg_mode = 0; - - switch (isc->try_config.fourcc) { - case V4L2_PIX_FMT_SBGGR8: - case V4L2_PIX_FMT_SGBRG8: - case V4L2_PIX_FMT_SGRBG8: - case V4L2_PIX_FMT_SRGGB8: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT8; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED8; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 8; - isc->try_config.bpp_v4l2 = 8; - break; - case V4L2_PIX_FMT_SBGGR10: - case V4L2_PIX_FMT_SGBRG10: - case V4L2_PIX_FMT_SGRBG10: - case V4L2_PIX_FMT_SRGGB10: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT10; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 16; - isc->try_config.bpp_v4l2 = 16; - break; - case V4L2_PIX_FMT_SBGGR12: - case V4L2_PIX_FMT_SGBRG12: - case V4L2_PIX_FMT_SGRBG12: - case V4L2_PIX_FMT_SRGGB12: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT12; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 16; - isc->try_config.bpp_v4l2 = 16; - break; - case V4L2_PIX_FMT_RGB565: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_RGB565; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 16; - isc->try_config.bpp_v4l2 = 16; - break; - case V4L2_PIX_FMT_ARGB444: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_ARGB444; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 16; - isc->try_config.bpp_v4l2 = 16; - break; - case V4L2_PIX_FMT_ARGB555: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_ARGB555; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 16; - isc->try_config.bpp_v4l2 = 16; - break; - case V4L2_PIX_FMT_ABGR32: - case V4L2_PIX_FMT_XBGR32: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_ARGB32; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 32; - isc->try_config.bpp_v4l2 = 32; - break; - case V4L2_PIX_FMT_YUV420: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YYCC; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_YC420P; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PLANAR; - isc->try_config.bpp = 12; - isc->try_config.bpp_v4l2 = 8; /* only first plane */ - break; - case V4L2_PIX_FMT_YUV422P: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YYCC; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_YC422P; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PLANAR; - isc->try_config.bpp = 16; - isc->try_config.bpp_v4l2 = 8; /* only first plane */ - break; - case V4L2_PIX_FMT_YUYV: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YCYC | ISC_RLP_CFG_YMODE_YUYV; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 16; - isc->try_config.bpp_v4l2 = 16; - break; - case V4L2_PIX_FMT_UYVY: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YCYC | ISC_RLP_CFG_YMODE_UYVY; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 16; - isc->try_config.bpp_v4l2 = 16; - break; - case V4L2_PIX_FMT_VYUY: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YCYC | ISC_RLP_CFG_YMODE_VYUY; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 16; - isc->try_config.bpp_v4l2 = 16; - break; - case V4L2_PIX_FMT_GREY: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DATY8; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED8; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 8; - isc->try_config.bpp_v4l2 = 8; - break; - case V4L2_PIX_FMT_Y16: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DATY10 | ISC_RLP_CFG_LSH; - fallthrough; - case V4L2_PIX_FMT_Y10: - isc->try_config.rlp_cfg_mode |= ISC_RLP_CFG_MODE_DATY10; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 16; - isc->try_config.bpp_v4l2 = 16; - break; - default: - return -EINVAL; - } - - if (direct_dump) { - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT8; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED8; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - return 0; - } - - return 0; -} - -/* - * Configuring pipeline modules, depending on which format the ISC outputs - * and considering which format it has as input from the sensor. - */ -static int isc_try_configure_pipeline(struct isc_device *isc) -{ - switch (isc->try_config.fourcc) { - case V4L2_PIX_FMT_RGB565: - case V4L2_PIX_FMT_ARGB555: - case V4L2_PIX_FMT_ARGB444: - case V4L2_PIX_FMT_ABGR32: - case V4L2_PIX_FMT_XBGR32: - /* if sensor format is RAW, we convert inside ISC */ - if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) { - isc->try_config.bits_pipeline = CFA_ENABLE | - WB_ENABLE | GAM_ENABLES | DPC_BLCENABLE | - CC_ENABLE; - } else { - isc->try_config.bits_pipeline = 0x0; - } - break; - case V4L2_PIX_FMT_YUV420: - /* if sensor format is RAW, we convert inside ISC */ - if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) { - isc->try_config.bits_pipeline = CFA_ENABLE | - CSC_ENABLE | GAM_ENABLES | WB_ENABLE | - SUB420_ENABLE | SUB422_ENABLE | CBC_ENABLE | - DPC_BLCENABLE; - } else { - isc->try_config.bits_pipeline = 0x0; - } - break; - case V4L2_PIX_FMT_YUV422P: - /* if sensor format is RAW, we convert inside ISC */ - if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) { - isc->try_config.bits_pipeline = CFA_ENABLE | - CSC_ENABLE | WB_ENABLE | GAM_ENABLES | - SUB422_ENABLE | CBC_ENABLE | DPC_BLCENABLE; - } else { - isc->try_config.bits_pipeline = 0x0; - } - break; - case V4L2_PIX_FMT_YUYV: - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_VYUY: - /* if sensor format is RAW, we convert inside ISC */ - if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) { - isc->try_config.bits_pipeline = CFA_ENABLE | - CSC_ENABLE | WB_ENABLE | GAM_ENABLES | - SUB422_ENABLE | CBC_ENABLE | DPC_BLCENABLE; - } else { - isc->try_config.bits_pipeline = 0x0; - } - break; - case V4L2_PIX_FMT_GREY: - case V4L2_PIX_FMT_Y16: - /* if sensor format is RAW, we convert inside ISC */ - if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) { - isc->try_config.bits_pipeline = CFA_ENABLE | - CSC_ENABLE | WB_ENABLE | GAM_ENABLES | - CBC_ENABLE | DPC_BLCENABLE; - } else { - isc->try_config.bits_pipeline = 0x0; - } - break; - default: - if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) - isc->try_config.bits_pipeline = WB_ENABLE | DPC_BLCENABLE; - else - isc->try_config.bits_pipeline = 0x0; - } - - /* Tune the pipeline to product specific */ - isc->adapt_pipeline(isc); - - return 0; -} - -static void isc_try_fse(struct isc_device *isc, - struct v4l2_subdev_state *sd_state) -{ - struct v4l2_rect *try_crop = - v4l2_subdev_state_get_crop(sd_state, 0); - struct v4l2_subdev_frame_size_enum fse = { - .which = V4L2_SUBDEV_FORMAT_TRY, - }; - int ret; - - /* - * If we do not know yet which format the subdev is using, we cannot - * do anything. - */ - if (!isc->try_config.sd_format) - return; - - fse.code = isc->try_config.sd_format->mbus_code; - - ret = v4l2_subdev_call(isc->current_subdev->sd, pad, enum_frame_size, - sd_state, &fse); - /* - * Attempt to obtain format size from subdev. If not available, - * just use the maximum ISC can receive. - */ - if (ret) { - try_crop->width = isc->max_width; - try_crop->height = isc->max_height; - } else { - try_crop->width = fse.max_width; - try_crop->height = fse.max_height; - } -} - -static int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f, - u32 *code) -{ - int i; - struct isc_format *sd_fmt = NULL, *direct_fmt = NULL; - struct v4l2_pix_format *pixfmt = &f->fmt.pix; - struct v4l2_subdev_pad_config pad_cfg = {}; - struct v4l2_subdev_state pad_state = { - .pads = &pad_cfg, - }; - struct v4l2_subdev_format format = { - .which = V4L2_SUBDEV_FORMAT_TRY, - }; - u32 mbus_code; - int ret; - bool rlp_dma_direct_dump = false; - - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - /* Step 1: find a RAW format that is supported */ - for (i = 0; i < isc->num_user_formats; i++) { - if (ISC_IS_FORMAT_RAW(isc->user_formats[i]->mbus_code)) { - sd_fmt = isc->user_formats[i]; - break; - } - } - /* Step 2: We can continue with this RAW format, or we can look - * for better: maybe sensor supports directly what we need. - */ - direct_fmt = find_format_by_fourcc(isc, pixfmt->pixelformat); - - /* Step 3: We have both. We decide given the module parameter which - * one to use. - */ - if (direct_fmt && sd_fmt && sensor_preferred) - sd_fmt = direct_fmt; - - /* Step 4: we do not have RAW but we have a direct format. Use it. */ - if (direct_fmt && !sd_fmt) - sd_fmt = direct_fmt; - - /* Step 5: if we are using a direct format, we need to package - * everything as 8 bit data and just dump it - */ - if (sd_fmt == direct_fmt) - rlp_dma_direct_dump = true; - - /* Step 6: We have no format. This can happen if the userspace - * requests some weird/invalid format. - * In this case, default to whatever we have - */ - if (!sd_fmt && !direct_fmt) { - sd_fmt = isc->user_formats[isc->num_user_formats - 1]; - v4l2_dbg(1, debug, &isc->v4l2_dev, - "Sensor not supporting %.4s, using %.4s\n", - (char *)&pixfmt->pixelformat, (char *)&sd_fmt->fourcc); - } - - if (!sd_fmt) { - ret = -EINVAL; - goto isc_try_fmt_err; - } - - /* Step 7: Print out what we decided for debugging */ - v4l2_dbg(1, debug, &isc->v4l2_dev, - "Preferring to have sensor using format %.4s\n", - (char *)&sd_fmt->fourcc); - - /* Step 8: at this moment we decided which format the subdev will use */ - isc->try_config.sd_format = sd_fmt; - - /* Limit to Atmel ISC hardware capabilities */ - if (pixfmt->width > isc->max_width) - pixfmt->width = isc->max_width; - if (pixfmt->height > isc->max_height) - pixfmt->height = isc->max_height; - - /* - * The mbus format is the one the subdev outputs. - * The pixels will be transferred in this format Sensor -> ISC - */ - mbus_code = sd_fmt->mbus_code; - - /* - * Validate formats. If the required format is not OK, default to raw. - */ - - isc->try_config.fourcc = pixfmt->pixelformat; - - if (isc_try_validate_formats(isc)) { - pixfmt->pixelformat = isc->try_config.fourcc = sd_fmt->fourcc; - /* Re-try to validate the new format */ - ret = isc_try_validate_formats(isc); - if (ret) - goto isc_try_fmt_err; - } - - ret = isc_try_configure_rlp_dma(isc, rlp_dma_direct_dump); - if (ret) - goto isc_try_fmt_err; - - ret = isc_try_configure_pipeline(isc); - if (ret) - goto isc_try_fmt_err; - - /* Obtain frame sizes if possible to have crop requirements ready */ - isc_try_fse(isc, &pad_state); - - v4l2_fill_mbus_format(&format.format, pixfmt, mbus_code); - ret = v4l2_subdev_call(isc->current_subdev->sd, pad, set_fmt, - &pad_state, &format); - if (ret < 0) - goto isc_try_fmt_subdev_err; - - v4l2_fill_pix_format(pixfmt, &format.format); - - /* Limit to Atmel ISC hardware capabilities */ - if (pixfmt->width > isc->max_width) - pixfmt->width = isc->max_width; - if (pixfmt->height > isc->max_height) - pixfmt->height = isc->max_height; - - pixfmt->field = V4L2_FIELD_NONE; - pixfmt->bytesperline = (pixfmt->width * isc->try_config.bpp_v4l2) >> 3; - pixfmt->sizeimage = ((pixfmt->width * isc->try_config.bpp) >> 3) * - pixfmt->height; - - if (code) - *code = mbus_code; - - return 0; - -isc_try_fmt_err: - v4l2_err(&isc->v4l2_dev, "Could not find any possible format for a working pipeline\n"); -isc_try_fmt_subdev_err: - memset(&isc->try_config, 0, sizeof(isc->try_config)); - - return ret; -} - -static int isc_set_fmt(struct isc_device *isc, struct v4l2_format *f) -{ - struct v4l2_subdev_format format = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - u32 mbus_code = 0; - int ret; - - ret = isc_try_fmt(isc, f, &mbus_code); - if (ret) - return ret; - - v4l2_fill_mbus_format(&format.format, &f->fmt.pix, mbus_code); - ret = v4l2_subdev_call(isc->current_subdev->sd, pad, - set_fmt, NULL, &format); - if (ret < 0) - return ret; - - /* Limit to Atmel ISC hardware capabilities */ - if (f->fmt.pix.width > isc->max_width) - f->fmt.pix.width = isc->max_width; - if (f->fmt.pix.height > isc->max_height) - f->fmt.pix.height = isc->max_height; - - isc->fmt = *f; - - if (isc->try_config.sd_format && isc->config.sd_format && - isc->try_config.sd_format != isc->config.sd_format) { - isc->ctrls.hist_stat = HIST_INIT; - isc_reset_awb_ctrls(isc); - isc_update_v4l2_ctrls(isc); - } - /* make the try configuration active */ - isc->config = isc->try_config; - - v4l2_dbg(1, debug, &isc->v4l2_dev, "New ISC configuration in place\n"); - - return 0; -} - -static int isc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct isc_device *isc = video_drvdata(file); - - if (vb2_is_busy(&isc->vb2_vidq)) - return -EBUSY; - - return isc_set_fmt(isc, f); -} - -static int isc_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct isc_device *isc = video_drvdata(file); - - return isc_try_fmt(isc, f, NULL); -} - -static int isc_enum_input(struct file *file, void *priv, - struct v4l2_input *inp) -{ - if (inp->index != 0) - return -EINVAL; - - inp->type = V4L2_INPUT_TYPE_CAMERA; - inp->std = 0; - strscpy(inp->name, "Camera", sizeof(inp->name)); - - return 0; -} - -static int isc_g_input(struct file *file, void *priv, unsigned int *i) -{ - *i = 0; - - return 0; -} - -static int isc_s_input(struct file *file, void *priv, unsigned int i) -{ - if (i > 0) - return -EINVAL; - - return 0; -} - -static int isc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) -{ - struct isc_device *isc = video_drvdata(file); - - return v4l2_g_parm_cap(video_devdata(file), isc->current_subdev->sd, a); -} - -static int isc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) -{ - struct isc_device *isc = video_drvdata(file); - - return v4l2_s_parm_cap(video_devdata(file), isc->current_subdev->sd, a); -} - -static int isc_enum_framesizes(struct file *file, void *fh, - struct v4l2_frmsizeenum *fsize) -{ - struct isc_device *isc = video_drvdata(file); - int ret = -EINVAL; - int i; - - if (fsize->index) - return -EINVAL; - - for (i = 0; i < isc->num_user_formats; i++) - if (isc->user_formats[i]->fourcc == fsize->pixel_format) - ret = 0; - - for (i = 0; i < isc->controller_formats_size; i++) - if (isc->controller_formats[i].fourcc == fsize->pixel_format) - ret = 0; - - if (ret) - return ret; - - fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; - - fsize->stepwise.min_width = 16; - fsize->stepwise.max_width = isc->max_width; - fsize->stepwise.min_height = 16; - fsize->stepwise.max_height = isc->max_height; - fsize->stepwise.step_width = 1; - fsize->stepwise.step_height = 1; - - return 0; -} - -static const struct v4l2_ioctl_ops isc_ioctl_ops = { - .vidioc_querycap = isc_querycap, - .vidioc_enum_fmt_vid_cap = isc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = isc_g_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = isc_s_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = isc_try_fmt_vid_cap, - - .vidioc_enum_input = isc_enum_input, - .vidioc_g_input = isc_g_input, - .vidioc_s_input = isc_s_input, - - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_expbuf = vb2_ioctl_expbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_create_bufs = vb2_ioctl_create_bufs, - .vidioc_prepare_buf = vb2_ioctl_prepare_buf, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - - .vidioc_g_parm = isc_g_parm, - .vidioc_s_parm = isc_s_parm, - .vidioc_enum_framesizes = isc_enum_framesizes, - - .vidioc_log_status = v4l2_ctrl_log_status, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static int isc_open(struct file *file) -{ - struct isc_device *isc = video_drvdata(file); - struct v4l2_subdev *sd = isc->current_subdev->sd; - int ret; - - if (mutex_lock_interruptible(&isc->lock)) - return -ERESTARTSYS; - - ret = v4l2_fh_open(file); - if (ret < 0) - goto unlock; - - if (!v4l2_fh_is_singular_file(file)) - goto unlock; - - ret = v4l2_subdev_call(sd, core, s_power, 1); - if (ret < 0 && ret != -ENOIOCTLCMD) { - v4l2_fh_release(file); - goto unlock; - } - - ret = isc_set_fmt(isc, &isc->fmt); - if (ret) { - v4l2_subdev_call(sd, core, s_power, 0); - v4l2_fh_release(file); - } - -unlock: - mutex_unlock(&isc->lock); - return ret; -} - -static int isc_release(struct file *file) -{ - struct isc_device *isc = video_drvdata(file); - struct v4l2_subdev *sd = isc->current_subdev->sd; - bool fh_singular; - int ret; - - mutex_lock(&isc->lock); - - fh_singular = v4l2_fh_is_singular_file(file); - - ret = _vb2_fop_release(file, NULL); - - if (fh_singular) - v4l2_subdev_call(sd, core, s_power, 0); - - mutex_unlock(&isc->lock); - - return ret; -} - -static const struct v4l2_file_operations isc_fops = { - .owner = THIS_MODULE, - .open = isc_open, - .release = isc_release, - .unlocked_ioctl = video_ioctl2, - .read = vb2_fop_read, - .mmap = vb2_fop_mmap, - .poll = vb2_fop_poll, -}; - -irqreturn_t atmel_isc_interrupt(int irq, void *dev_id) -{ - struct isc_device *isc = (struct isc_device *)dev_id; - struct regmap *regmap = isc->regmap; - u32 isc_intsr, isc_intmask, pending; - irqreturn_t ret = IRQ_NONE; - - regmap_read(regmap, ISC_INTSR, &isc_intsr); - regmap_read(regmap, ISC_INTMASK, &isc_intmask); - - pending = isc_intsr & isc_intmask; - - if (likely(pending & ISC_INT_DDONE)) { - spin_lock(&isc->dma_queue_lock); - if (isc->cur_frm) { - struct vb2_v4l2_buffer *vbuf = &isc->cur_frm->vb; - struct vb2_buffer *vb = &vbuf->vb2_buf; - - vb->timestamp = ktime_get_ns(); - vbuf->sequence = isc->sequence++; - vb2_buffer_done(vb, VB2_BUF_STATE_DONE); - isc->cur_frm = NULL; - } - - if (!list_empty(&isc->dma_queue) && !isc->stop) { - isc->cur_frm = list_first_entry(&isc->dma_queue, - struct isc_buffer, list); - list_del(&isc->cur_frm->list); - - isc_start_dma(isc); - } - - if (isc->stop) - complete(&isc->comp); - - ret = IRQ_HANDLED; - spin_unlock(&isc->dma_queue_lock); - } - - if (pending & ISC_INT_HISDONE) { - schedule_work(&isc->awb_work); - ret = IRQ_HANDLED; - } - - return ret; -} -EXPORT_SYMBOL_GPL(atmel_isc_interrupt); - -static void isc_hist_count(struct isc_device *isc, u32 *min, u32 *max) -{ - struct regmap *regmap = isc->regmap; - struct isc_ctrls *ctrls = &isc->ctrls; - u32 *hist_count = &ctrls->hist_count[ctrls->hist_id]; - u32 *hist_entry = &ctrls->hist_entry[0]; - u32 i; - - *min = 0; - *max = HIST_ENTRIES; - - regmap_bulk_read(regmap, ISC_HIS_ENTRY + isc->offsets.his_entry, - hist_entry, HIST_ENTRIES); - - *hist_count = 0; - /* - * we deliberately ignore the end of the histogram, - * the most white pixels - */ - for (i = 1; i < HIST_ENTRIES; i++) { - if (*hist_entry && !*min) - *min = i; - if (*hist_entry) - *max = i; - *hist_count += i * (*hist_entry++); - } - - if (!*min) - *min = 1; - - v4l2_dbg(1, debug, &isc->v4l2_dev, - "isc wb: hist_id %u, hist_count %u", - ctrls->hist_id, *hist_count); -} - -static void isc_wb_update(struct isc_ctrls *ctrls) -{ - struct isc_device *isc = container_of(ctrls, struct isc_device, ctrls); - u32 *hist_count = &ctrls->hist_count[0]; - u32 c, offset[4]; - u64 avg = 0; - /* We compute two gains, stretch gain and grey world gain */ - u32 s_gain[4], gw_gain[4]; - - /* - * According to Grey World, we need to set gains for R/B to normalize - * them towards the green channel. - * Thus we want to keep Green as fixed and adjust only Red/Blue - * Compute the average of the both green channels first - */ - avg = (u64)hist_count[ISC_HIS_CFG_MODE_GR] + - (u64)hist_count[ISC_HIS_CFG_MODE_GB]; - avg >>= 1; - - v4l2_dbg(1, debug, &isc->v4l2_dev, - "isc wb: green components average %llu\n", avg); - - /* Green histogram is null, nothing to do */ - if (!avg) - return; - - for (c = ISC_HIS_CFG_MODE_GR; c <= ISC_HIS_CFG_MODE_B; c++) { - /* - * the color offset is the minimum value of the histogram. - * we stretch this color to the full range by substracting - * this value from the color component. - */ - offset[c] = ctrls->hist_minmax[c][HIST_MIN_INDEX]; - /* - * The offset is always at least 1. If the offset is 1, we do - * not need to adjust it, so our result must be zero. - * the offset is computed in a histogram on 9 bits (0..512) - * but the offset in register is based on - * 12 bits pipeline (0..4096). - * we need to shift with the 3 bits that the histogram is - * ignoring - */ - ctrls->offset[c] = (offset[c] - 1) << 3; - - /* - * the offset is then taken and converted to 2's complements, - * and must be negative, as we subtract this value from the - * color components - */ - ctrls->offset[c] = -ctrls->offset[c]; - - /* - * the stretch gain is the total number of histogram bins - * divided by the actual range of color component (Max - Min) - * If we compute gain like this, the actual color component - * will be stretched to the full histogram. - * We need to shift 9 bits for precision, we have 9 bits for - * decimals - */ - s_gain[c] = (HIST_ENTRIES << 9) / - (ctrls->hist_minmax[c][HIST_MAX_INDEX] - - ctrls->hist_minmax[c][HIST_MIN_INDEX] + 1); - - /* - * Now we have to compute the gain w.r.t. the average. - * Add/lose gain to the component towards the average. - * If it happens that the component is zero, use the - * fixed point value : 1.0 gain. - */ - if (hist_count[c]) - gw_gain[c] = div_u64(avg << 9, hist_count[c]); - else - gw_gain[c] = 1 << 9; - - v4l2_dbg(1, debug, &isc->v4l2_dev, - "isc wb: component %d, s_gain %u, gw_gain %u\n", - c, s_gain[c], gw_gain[c]); - /* multiply both gains and adjust for decimals */ - ctrls->gain[c] = s_gain[c] * gw_gain[c]; - ctrls->gain[c] >>= 9; - - /* make sure we are not out of range */ - ctrls->gain[c] = clamp_val(ctrls->gain[c], 0, GENMASK(12, 0)); - - v4l2_dbg(1, debug, &isc->v4l2_dev, - "isc wb: component %d, final gain %u\n", - c, ctrls->gain[c]); - } -} - -static void isc_awb_work(struct work_struct *w) -{ - struct isc_device *isc = - container_of(w, struct isc_device, awb_work); - struct regmap *regmap = isc->regmap; - struct isc_ctrls *ctrls = &isc->ctrls; - u32 hist_id = ctrls->hist_id; - u32 baysel; - unsigned long flags; - u32 min, max; - int ret; - - if (ctrls->hist_stat != HIST_ENABLED) - return; - - isc_hist_count(isc, &min, &max); - - v4l2_dbg(1, debug, &isc->v4l2_dev, - "isc wb mode %d: hist min %u , max %u\n", hist_id, min, max); - - ctrls->hist_minmax[hist_id][HIST_MIN_INDEX] = min; - ctrls->hist_minmax[hist_id][HIST_MAX_INDEX] = max; - - if (hist_id != ISC_HIS_CFG_MODE_B) { - hist_id++; - } else { - isc_wb_update(ctrls); - hist_id = ISC_HIS_CFG_MODE_GR; - } - - ctrls->hist_id = hist_id; - baysel = isc->config.sd_format->cfa_baycfg << ISC_HIS_CFG_BAYSEL_SHIFT; - - ret = pm_runtime_resume_and_get(isc->dev); - if (ret < 0) - return; - - /* - * only update if we have all the required histograms and controls - * if awb has been disabled, we need to reset registers as well. - */ - if (hist_id == ISC_HIS_CFG_MODE_GR || ctrls->awb == ISC_WB_NONE) { - /* - * It may happen that DMA Done IRQ will trigger while we are - * updating white balance registers here. - * In that case, only parts of the controls have been updated. - * We can avoid that by locking the section. - */ - spin_lock_irqsave(&isc->awb_lock, flags); - isc_update_awb_ctrls(isc); - spin_unlock_irqrestore(&isc->awb_lock, flags); - - /* - * if we are doing just the one time white balance adjustment, - * we are basically done. - */ - if (ctrls->awb == ISC_WB_ONETIME) { - v4l2_info(&isc->v4l2_dev, - "Completed one time white-balance adjustment.\n"); - /* update the v4l2 controls values */ - isc_update_v4l2_ctrls(isc); - ctrls->awb = ISC_WB_NONE; - } - } - regmap_write(regmap, ISC_HIS_CFG + isc->offsets.his, - hist_id | baysel | ISC_HIS_CFG_RAR); - - /* - * We have to make sure the streaming has not stopped meanwhile. - * ISC requires a frame to clock the internal profile update. - * To avoid issues, lock the sequence with a mutex - */ - mutex_lock(&isc->awb_mutex); - - /* streaming is not active anymore */ - if (isc->stop) { - mutex_unlock(&isc->awb_mutex); - return; - } - - isc_update_profile(isc); - - mutex_unlock(&isc->awb_mutex); - - /* if awb has been disabled, we don't need to start another histogram */ - if (ctrls->awb) - regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ); - - pm_runtime_put_sync(isc->dev); -} - -static int isc_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct isc_device *isc = container_of(ctrl->handler, - struct isc_device, ctrls.handler); - struct isc_ctrls *ctrls = &isc->ctrls; - - if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) - return 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrls->brightness = ctrl->val & ISC_CBC_BRIGHT_MASK; - break; - case V4L2_CID_CONTRAST: - ctrls->contrast = ctrl->val & ISC_CBC_CONTRAST_MASK; - break; - case V4L2_CID_GAMMA: - ctrls->gamma_index = ctrl->val; - break; - default: - return -EINVAL; - } - - return 0; -} - -static const struct v4l2_ctrl_ops isc_ctrl_ops = { - .s_ctrl = isc_s_ctrl, -}; - -static int isc_s_awb_ctrl(struct v4l2_ctrl *ctrl) -{ - struct isc_device *isc = container_of(ctrl->handler, - struct isc_device, ctrls.handler); - struct isc_ctrls *ctrls = &isc->ctrls; - - if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) - return 0; - - switch (ctrl->id) { - case V4L2_CID_AUTO_WHITE_BALANCE: - if (ctrl->val == 1) - ctrls->awb = ISC_WB_AUTO; - else - ctrls->awb = ISC_WB_NONE; - - /* configure the controls with new values from v4l2 */ - if (ctrl->cluster[ISC_CTRL_R_GAIN]->is_new) - ctrls->gain[ISC_HIS_CFG_MODE_R] = isc->r_gain_ctrl->val; - if (ctrl->cluster[ISC_CTRL_B_GAIN]->is_new) - ctrls->gain[ISC_HIS_CFG_MODE_B] = isc->b_gain_ctrl->val; - if (ctrl->cluster[ISC_CTRL_GR_GAIN]->is_new) - ctrls->gain[ISC_HIS_CFG_MODE_GR] = isc->gr_gain_ctrl->val; - if (ctrl->cluster[ISC_CTRL_GB_GAIN]->is_new) - ctrls->gain[ISC_HIS_CFG_MODE_GB] = isc->gb_gain_ctrl->val; - - if (ctrl->cluster[ISC_CTRL_R_OFF]->is_new) - ctrls->offset[ISC_HIS_CFG_MODE_R] = isc->r_off_ctrl->val; - if (ctrl->cluster[ISC_CTRL_B_OFF]->is_new) - ctrls->offset[ISC_HIS_CFG_MODE_B] = isc->b_off_ctrl->val; - if (ctrl->cluster[ISC_CTRL_GR_OFF]->is_new) - ctrls->offset[ISC_HIS_CFG_MODE_GR] = isc->gr_off_ctrl->val; - if (ctrl->cluster[ISC_CTRL_GB_OFF]->is_new) - ctrls->offset[ISC_HIS_CFG_MODE_GB] = isc->gb_off_ctrl->val; - - isc_update_awb_ctrls(isc); - - mutex_lock(&isc->awb_mutex); - if (vb2_is_streaming(&isc->vb2_vidq)) { - /* - * If we are streaming, we can update profile to - * have the new settings in place. - */ - isc_update_profile(isc); - } else { - /* - * The auto cluster will activate automatically this - * control. This has to be deactivated when not - * streaming. - */ - v4l2_ctrl_activate(isc->do_wb_ctrl, false); - } - mutex_unlock(&isc->awb_mutex); - - /* if we have autowhitebalance on, start histogram procedure */ - if (ctrls->awb == ISC_WB_AUTO && - vb2_is_streaming(&isc->vb2_vidq) && - ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code)) - isc_set_histogram(isc, true); - - /* - * for one time whitebalance adjustment, check the button, - * if it's pressed, perform the one time operation. - */ - if (ctrls->awb == ISC_WB_NONE && - ctrl->cluster[ISC_CTRL_DO_WB]->is_new && - !(ctrl->cluster[ISC_CTRL_DO_WB]->flags & - V4L2_CTRL_FLAG_INACTIVE)) { - ctrls->awb = ISC_WB_ONETIME; - isc_set_histogram(isc, true); - v4l2_dbg(1, debug, &isc->v4l2_dev, - "One time white-balance started.\n"); - } - return 0; - } - return 0; -} - -static int isc_g_volatile_awb_ctrl(struct v4l2_ctrl *ctrl) -{ - struct isc_device *isc = container_of(ctrl->handler, - struct isc_device, ctrls.handler); - struct isc_ctrls *ctrls = &isc->ctrls; - - switch (ctrl->id) { - /* being a cluster, this id will be called for every control */ - case V4L2_CID_AUTO_WHITE_BALANCE: - ctrl->cluster[ISC_CTRL_R_GAIN]->val = - ctrls->gain[ISC_HIS_CFG_MODE_R]; - ctrl->cluster[ISC_CTRL_B_GAIN]->val = - ctrls->gain[ISC_HIS_CFG_MODE_B]; - ctrl->cluster[ISC_CTRL_GR_GAIN]->val = - ctrls->gain[ISC_HIS_CFG_MODE_GR]; - ctrl->cluster[ISC_CTRL_GB_GAIN]->val = - ctrls->gain[ISC_HIS_CFG_MODE_GB]; - - ctrl->cluster[ISC_CTRL_R_OFF]->val = - ctrls->offset[ISC_HIS_CFG_MODE_R]; - ctrl->cluster[ISC_CTRL_B_OFF]->val = - ctrls->offset[ISC_HIS_CFG_MODE_B]; - ctrl->cluster[ISC_CTRL_GR_OFF]->val = - ctrls->offset[ISC_HIS_CFG_MODE_GR]; - ctrl->cluster[ISC_CTRL_GB_OFF]->val = - ctrls->offset[ISC_HIS_CFG_MODE_GB]; - break; - } - return 0; -} - -static const struct v4l2_ctrl_ops isc_awb_ops = { - .s_ctrl = isc_s_awb_ctrl, - .g_volatile_ctrl = isc_g_volatile_awb_ctrl, -}; - -#define ISC_CTRL_OFF(_name, _id, _name_str) \ - static const struct v4l2_ctrl_config _name = { \ - .ops = &isc_awb_ops, \ - .id = _id, \ - .name = _name_str, \ - .type = V4L2_CTRL_TYPE_INTEGER, \ - .flags = V4L2_CTRL_FLAG_SLIDER, \ - .min = -4095, \ - .max = 4095, \ - .step = 1, \ - .def = 0, \ - } - -ISC_CTRL_OFF(isc_r_off_ctrl, ISC_CID_R_OFFSET, "Red Component Offset"); -ISC_CTRL_OFF(isc_b_off_ctrl, ISC_CID_B_OFFSET, "Blue Component Offset"); -ISC_CTRL_OFF(isc_gr_off_ctrl, ISC_CID_GR_OFFSET, "Green Red Component Offset"); -ISC_CTRL_OFF(isc_gb_off_ctrl, ISC_CID_GB_OFFSET, "Green Blue Component Offset"); - -#define ISC_CTRL_GAIN(_name, _id, _name_str) \ - static const struct v4l2_ctrl_config _name = { \ - .ops = &isc_awb_ops, \ - .id = _id, \ - .name = _name_str, \ - .type = V4L2_CTRL_TYPE_INTEGER, \ - .flags = V4L2_CTRL_FLAG_SLIDER, \ - .min = 0, \ - .max = 8191, \ - .step = 1, \ - .def = 512, \ - } - -ISC_CTRL_GAIN(isc_r_gain_ctrl, ISC_CID_R_GAIN, "Red Component Gain"); -ISC_CTRL_GAIN(isc_b_gain_ctrl, ISC_CID_B_GAIN, "Blue Component Gain"); -ISC_CTRL_GAIN(isc_gr_gain_ctrl, ISC_CID_GR_GAIN, "Green Red Component Gain"); -ISC_CTRL_GAIN(isc_gb_gain_ctrl, ISC_CID_GB_GAIN, "Green Blue Component Gain"); - -static int isc_ctrl_init(struct isc_device *isc) -{ - const struct v4l2_ctrl_ops *ops = &isc_ctrl_ops; - struct isc_ctrls *ctrls = &isc->ctrls; - struct v4l2_ctrl_handler *hdl = &ctrls->handler; - int ret; - - ctrls->hist_stat = HIST_INIT; - isc_reset_awb_ctrls(isc); - - ret = v4l2_ctrl_handler_init(hdl, 13); - if (ret < 0) - return ret; - - /* Initialize product specific controls. For example, contrast */ - isc->config_ctrls(isc, ops); - - ctrls->brightness = 0; - - v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -1024, 1023, 1, 0); - v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAMMA, 0, isc->gamma_max, 1, - isc->gamma_max); - isc->awb_ctrl = v4l2_ctrl_new_std(hdl, &isc_awb_ops, - V4L2_CID_AUTO_WHITE_BALANCE, - 0, 1, 1, 1); - - /* do_white_balance is a button, so min,max,step,default are ignored */ - isc->do_wb_ctrl = v4l2_ctrl_new_std(hdl, &isc_awb_ops, - V4L2_CID_DO_WHITE_BALANCE, - 0, 0, 0, 0); - - if (!isc->do_wb_ctrl) { - ret = hdl->error; - v4l2_ctrl_handler_free(hdl); - return ret; - } - - v4l2_ctrl_activate(isc->do_wb_ctrl, false); - - isc->r_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_r_gain_ctrl, NULL); - isc->b_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_b_gain_ctrl, NULL); - isc->gr_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gr_gain_ctrl, NULL); - isc->gb_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gb_gain_ctrl, NULL); - isc->r_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_r_off_ctrl, NULL); - isc->b_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_b_off_ctrl, NULL); - isc->gr_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gr_off_ctrl, NULL); - isc->gb_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gb_off_ctrl, NULL); - - /* - * The cluster is in auto mode with autowhitebalance enabled - * and manual mode otherwise. - */ - v4l2_ctrl_auto_cluster(10, &isc->awb_ctrl, 0, true); - - v4l2_ctrl_handler_setup(hdl); - - return 0; -} - -static int isc_async_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *subdev, - struct v4l2_async_connection *asd) -{ - struct isc_device *isc = container_of(notifier->v4l2_dev, - struct isc_device, v4l2_dev); - struct isc_subdev_entity *subdev_entity = - container_of(notifier, struct isc_subdev_entity, notifier); - - if (video_is_registered(&isc->video_dev)) { - v4l2_err(&isc->v4l2_dev, "only supports one sub-device.\n"); - return -EBUSY; - } - - subdev_entity->sd = subdev; - - return 0; -} - -static void isc_async_unbind(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *subdev, - struct v4l2_async_connection *asd) -{ - struct isc_device *isc = container_of(notifier->v4l2_dev, - struct isc_device, v4l2_dev); - mutex_destroy(&isc->awb_mutex); - cancel_work_sync(&isc->awb_work); - video_unregister_device(&isc->video_dev); - v4l2_ctrl_handler_free(&isc->ctrls.handler); -} - -static struct isc_format *find_format_by_code(struct isc_device *isc, - unsigned int code, int *index) -{ - struct isc_format *fmt = &isc->formats_list[0]; - unsigned int i; - - for (i = 0; i < isc->formats_list_size; i++) { - if (fmt->mbus_code == code) { - *index = i; - return fmt; - } - - fmt++; - } - - return NULL; -} - -static int isc_formats_init(struct isc_device *isc) -{ - struct isc_format *fmt; - struct v4l2_subdev *subdev = isc->current_subdev->sd; - unsigned int num_fmts, i, j; - u32 list_size = isc->formats_list_size; - struct v4l2_subdev_mbus_code_enum mbus_code = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - - num_fmts = 0; - while (!v4l2_subdev_call(subdev, pad, enum_mbus_code, - NULL, &mbus_code)) { - mbus_code.index++; - - fmt = find_format_by_code(isc, mbus_code.code, &i); - if (!fmt) { - v4l2_warn(&isc->v4l2_dev, "Mbus code %x not supported\n", - mbus_code.code); - continue; - } - - fmt->sd_support = true; - num_fmts++; - } - - if (!num_fmts) - return -ENXIO; - - isc->num_user_formats = num_fmts; - isc->user_formats = devm_kcalloc(isc->dev, - num_fmts, sizeof(*isc->user_formats), - GFP_KERNEL); - if (!isc->user_formats) - return -ENOMEM; - - fmt = &isc->formats_list[0]; - for (i = 0, j = 0; i < list_size; i++) { - if (fmt->sd_support) - isc->user_formats[j++] = fmt; - fmt++; - } - - return 0; -} - -static int isc_set_default_fmt(struct isc_device *isc) -{ - struct v4l2_format f = { - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, - .fmt.pix = { - .width = VGA_WIDTH, - .height = VGA_HEIGHT, - .field = V4L2_FIELD_NONE, - .pixelformat = isc->user_formats[0]->fourcc, - }, - }; - int ret; - - ret = isc_try_fmt(isc, &f, NULL); - if (ret) - return ret; - - isc->fmt = f; - return 0; -} - -static int isc_async_complete(struct v4l2_async_notifier *notifier) -{ - struct isc_device *isc = container_of(notifier->v4l2_dev, - struct isc_device, v4l2_dev); - struct video_device *vdev = &isc->video_dev; - struct vb2_queue *q = &isc->vb2_vidq; - int ret = 0; - - INIT_WORK(&isc->awb_work, isc_awb_work); - - ret = v4l2_device_register_subdev_nodes(&isc->v4l2_dev); - if (ret < 0) { - v4l2_err(&isc->v4l2_dev, "Failed to register subdev nodes\n"); - return ret; - } - - isc->current_subdev = container_of(notifier, - struct isc_subdev_entity, notifier); - mutex_init(&isc->lock); - mutex_init(&isc->awb_mutex); - - init_completion(&isc->comp); - - /* Initialize videobuf2 queue */ - q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; - q->drv_priv = isc; - q->buf_struct_size = sizeof(struct isc_buffer); - q->ops = &isc_vb2_ops; - q->mem_ops = &vb2_dma_contig_memops; - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->lock = &isc->lock; - q->min_queued_buffers = 1; - q->dev = isc->dev; - - ret = vb2_queue_init(q); - if (ret < 0) { - v4l2_err(&isc->v4l2_dev, - "vb2_queue_init() failed: %d\n", ret); - goto isc_async_complete_err; - } - - /* Init video dma queues */ - INIT_LIST_HEAD(&isc->dma_queue); - spin_lock_init(&isc->dma_queue_lock); - spin_lock_init(&isc->awb_lock); - - ret = isc_formats_init(isc); - if (ret < 0) { - v4l2_err(&isc->v4l2_dev, - "Init format failed: %d\n", ret); - goto isc_async_complete_err; - } - - ret = isc_set_default_fmt(isc); - if (ret) { - v4l2_err(&isc->v4l2_dev, "Could not set default format\n"); - goto isc_async_complete_err; - } - - ret = isc_ctrl_init(isc); - if (ret) { - v4l2_err(&isc->v4l2_dev, "Init isc ctrols failed: %d\n", ret); - goto isc_async_complete_err; - } - - /* Register video device */ - strscpy(vdev->name, KBUILD_MODNAME, sizeof(vdev->name)); - vdev->release = video_device_release_empty; - vdev->fops = &isc_fops; - vdev->ioctl_ops = &isc_ioctl_ops; - vdev->v4l2_dev = &isc->v4l2_dev; - vdev->vfl_dir = VFL_DIR_RX; - vdev->queue = q; - vdev->lock = &isc->lock; - vdev->ctrl_handler = &isc->ctrls.handler; - vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE; - video_set_drvdata(vdev, isc); - - ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); - if (ret < 0) { - v4l2_err(&isc->v4l2_dev, - "video_register_device failed: %d\n", ret); - goto isc_async_complete_err; - } - - return 0; - -isc_async_complete_err: - mutex_destroy(&isc->awb_mutex); - mutex_destroy(&isc->lock); - return ret; -} - -const struct v4l2_async_notifier_operations atmel_isc_async_ops = { - .bound = isc_async_bound, - .unbind = isc_async_unbind, - .complete = isc_async_complete, -}; -EXPORT_SYMBOL_GPL(atmel_isc_async_ops); - -void atmel_isc_subdev_cleanup(struct isc_device *isc) -{ - struct isc_subdev_entity *subdev_entity; - - list_for_each_entry(subdev_entity, &isc->subdev_entities, list) { - v4l2_async_nf_unregister(&subdev_entity->notifier); - v4l2_async_nf_cleanup(&subdev_entity->notifier); - } - - INIT_LIST_HEAD(&isc->subdev_entities); -} -EXPORT_SYMBOL_GPL(atmel_isc_subdev_cleanup); - -int atmel_isc_pipeline_init(struct isc_device *isc) -{ - struct device *dev = isc->dev; - struct regmap *regmap = isc->regmap; - struct regmap_field *regs; - unsigned int i; - - /* - * DPCEN-->GDCEN-->BLCEN-->WB-->CFA-->CC--> - * GAM-->VHXS-->CSC-->CBC-->SUB422-->SUB420 - */ - const struct reg_field regfields[ISC_PIPE_LINE_NODE_NUM] = { - REG_FIELD(ISC_DPC_CTRL, 0, 0), - REG_FIELD(ISC_DPC_CTRL, 1, 1), - REG_FIELD(ISC_DPC_CTRL, 2, 2), - REG_FIELD(ISC_WB_CTRL, 0, 0), - REG_FIELD(ISC_CFA_CTRL, 0, 0), - REG_FIELD(ISC_CC_CTRL, 0, 0), - REG_FIELD(ISC_GAM_CTRL, 0, 0), - REG_FIELD(ISC_GAM_CTRL, 1, 1), - REG_FIELD(ISC_GAM_CTRL, 2, 2), - REG_FIELD(ISC_GAM_CTRL, 3, 3), - REG_FIELD(ISC_VHXS_CTRL, 0, 0), - REG_FIELD(ISC_CSC_CTRL + isc->offsets.csc, 0, 0), - REG_FIELD(ISC_CBC_CTRL + isc->offsets.cbc, 0, 0), - REG_FIELD(ISC_SUB422_CTRL + isc->offsets.sub422, 0, 0), - REG_FIELD(ISC_SUB420_CTRL + isc->offsets.sub420, 0, 0), - }; - - for (i = 0; i < ISC_PIPE_LINE_NODE_NUM; i++) { - regs = devm_regmap_field_alloc(dev, regmap, regfields[i]); - if (IS_ERR(regs)) - return PTR_ERR(regs); - - isc->pipeline[i] = regs; - } - - return 0; -} -EXPORT_SYMBOL_GPL(atmel_isc_pipeline_init); - -/* regmap configuration */ -#define ATMEL_ISC_REG_MAX 0xd5c -const struct regmap_config atmel_isc_regmap_config = { - .reg_bits = 32, - .reg_stride = 4, - .val_bits = 32, - .max_register = ATMEL_ISC_REG_MAX, -}; -EXPORT_SYMBOL_GPL(atmel_isc_regmap_config); - -MODULE_AUTHOR("Songjun Wu"); -MODULE_AUTHOR("Eugen Hristev"); -MODULE_DESCRIPTION("Atmel ISC common code base"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/deprecated/atmel/atmel-isc-clk.c b/drivers/staging/media/deprecated/atmel/atmel-isc-clk.c deleted file mode 100644 index d442b5f4c931..000000000000 --- a/drivers/staging/media/deprecated/atmel/atmel-isc-clk.c +++ /dev/null @@ -1,311 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Microchip Image Sensor Controller (ISC) common clock driver setup - * - * Copyright (C) 2016 Microchip Technology, Inc. - * - * Author: Songjun Wu - * Author: Eugen Hristev <eugen.hristev@microchip.com> - * - */ -#include <linux/clk.h> -#include <linux/clkdev.h> -#include <linux/clk-provider.h> -#include <linux/pm_runtime.h> -#include <linux/regmap.h> - -#include "atmel-isc-regs.h" -#include "atmel-isc.h" - -static int isc_wait_clk_stable(struct clk_hw *hw) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - struct regmap *regmap = isc_clk->regmap; - unsigned long timeout = jiffies + usecs_to_jiffies(1000); - unsigned int status; - - while (time_before(jiffies, timeout)) { - regmap_read(regmap, ISC_CLKSR, &status); - if (!(status & ISC_CLKSR_SIP)) - return 0; - - usleep_range(10, 250); - } - - return -ETIMEDOUT; -} - -static int isc_clk_prepare(struct clk_hw *hw) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - int ret; - - ret = pm_runtime_resume_and_get(isc_clk->dev); - if (ret < 0) - return ret; - - return isc_wait_clk_stable(hw); -} - -static void isc_clk_unprepare(struct clk_hw *hw) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - - isc_wait_clk_stable(hw); - - pm_runtime_put_sync(isc_clk->dev); -} - -static int isc_clk_enable(struct clk_hw *hw) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - u32 id = isc_clk->id; - struct regmap *regmap = isc_clk->regmap; - unsigned long flags; - unsigned int status; - - dev_dbg(isc_clk->dev, "ISC CLK: %s, id = %d, div = %d, parent id = %d\n", - __func__, id, isc_clk->div, isc_clk->parent_id); - - spin_lock_irqsave(&isc_clk->lock, flags); - regmap_update_bits(regmap, ISC_CLKCFG, - ISC_CLKCFG_DIV_MASK(id) | ISC_CLKCFG_SEL_MASK(id), - (isc_clk->div << ISC_CLKCFG_DIV_SHIFT(id)) | - (isc_clk->parent_id << ISC_CLKCFG_SEL_SHIFT(id))); - - regmap_write(regmap, ISC_CLKEN, ISC_CLK(id)); - spin_unlock_irqrestore(&isc_clk->lock, flags); - - regmap_read(regmap, ISC_CLKSR, &status); - if (status & ISC_CLK(id)) - return 0; - else - return -EINVAL; -} - -static void isc_clk_disable(struct clk_hw *hw) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - u32 id = isc_clk->id; - unsigned long flags; - - spin_lock_irqsave(&isc_clk->lock, flags); - regmap_write(isc_clk->regmap, ISC_CLKDIS, ISC_CLK(id)); - spin_unlock_irqrestore(&isc_clk->lock, flags); -} - -static int isc_clk_is_enabled(struct clk_hw *hw) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - u32 status; - int ret; - - ret = pm_runtime_resume_and_get(isc_clk->dev); - if (ret < 0) - return 0; - - regmap_read(isc_clk->regmap, ISC_CLKSR, &status); - - pm_runtime_put_sync(isc_clk->dev); - - return status & ISC_CLK(isc_clk->id) ? 1 : 0; -} - -static unsigned long -isc_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - - return DIV_ROUND_CLOSEST(parent_rate, isc_clk->div + 1); -} - -static int isc_clk_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - long best_rate = -EINVAL; - int best_diff = -1; - unsigned int i, div; - - for (i = 0; i < clk_hw_get_num_parents(hw); i++) { - struct clk_hw *parent; - unsigned long parent_rate; - - parent = clk_hw_get_parent_by_index(hw, i); - if (!parent) - continue; - - parent_rate = clk_hw_get_rate(parent); - if (!parent_rate) - continue; - - for (div = 1; div < ISC_CLK_MAX_DIV + 2; div++) { - unsigned long rate; - int diff; - - rate = DIV_ROUND_CLOSEST(parent_rate, div); - diff = abs(req->rate - rate); - - if (best_diff < 0 || best_diff > diff) { - best_rate = rate; - best_diff = diff; - req->best_parent_rate = parent_rate; - req->best_parent_hw = parent; - } - - if (!best_diff || rate < req->rate) - break; - } - - if (!best_diff) - break; - } - - dev_dbg(isc_clk->dev, - "ISC CLK: %s, best_rate = %ld, parent clk: %s @ %ld\n", - __func__, best_rate, - __clk_get_name((req->best_parent_hw)->clk), - req->best_parent_rate); - - if (best_rate < 0) - return best_rate; - - req->rate = best_rate; - - return 0; -} - -static int isc_clk_set_parent(struct clk_hw *hw, u8 index) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - - if (index >= clk_hw_get_num_parents(hw)) - return -EINVAL; - - isc_clk->parent_id = index; - - return 0; -} - -static u8 isc_clk_get_parent(struct clk_hw *hw) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - - return isc_clk->parent_id; -} - -static int isc_clk_set_rate(struct clk_hw *hw, - unsigned long rate, - unsigned long parent_rate) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - u32 div; - - if (!rate) - return -EINVAL; - - div = DIV_ROUND_CLOSEST(parent_rate, rate); - if (div > (ISC_CLK_MAX_DIV + 1) || !div) - return -EINVAL; - - isc_clk->div = div - 1; - - return 0; -} - -static const struct clk_ops isc_clk_ops = { - .prepare = isc_clk_prepare, - .unprepare = isc_clk_unprepare, - .enable = isc_clk_enable, - .disable = isc_clk_disable, - .is_enabled = isc_clk_is_enabled, - .recalc_rate = isc_clk_recalc_rate, - .determine_rate = isc_clk_determine_rate, - .set_parent = isc_clk_set_parent, - .get_parent = isc_clk_get_parent, - .set_rate = isc_clk_set_rate, -}; - -static int isc_clk_register(struct isc_device *isc, unsigned int id) -{ - struct regmap *regmap = isc->regmap; - struct device_node *np = isc->dev->of_node; - struct isc_clk *isc_clk; - struct clk_init_data init; - const char *clk_name = np->name; - const char *parent_names[3]; - int num_parents; - - if (id == ISC_ISPCK && !isc->ispck_required) - return 0; - - num_parents = of_clk_get_parent_count(np); - if (num_parents < 1 || num_parents > 3) - return -EINVAL; - - if (num_parents > 2 && id == ISC_ISPCK) - num_parents = 2; - - of_clk_parent_fill(np, parent_names, num_parents); - - if (id == ISC_MCK) - of_property_read_string(np, "clock-output-names", &clk_name); - else - clk_name = "isc-ispck"; - - init.parent_names = parent_names; - init.num_parents = num_parents; - init.name = clk_name; - init.ops = &isc_clk_ops; - init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; - - isc_clk = &isc->isc_clks[id]; - isc_clk->hw.init = &init; - isc_clk->regmap = regmap; - isc_clk->id = id; - isc_clk->dev = isc->dev; - spin_lock_init(&isc_clk->lock); - - isc_clk->clk = clk_register(isc->dev, &isc_clk->hw); - if (IS_ERR(isc_clk->clk)) { - dev_err(isc->dev, "%s: clock register fail\n", clk_name); - return PTR_ERR(isc_clk->clk); - } else if (id == ISC_MCK) { - of_clk_add_provider(np, of_clk_src_simple_get, isc_clk->clk); - } - - return 0; -} - -int atmel_isc_clk_init(struct isc_device *isc) -{ - unsigned int i; - int ret; - - for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) - isc->isc_clks[i].clk = ERR_PTR(-EINVAL); - - for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) { - ret = isc_clk_register(isc, i); - if (ret) - return ret; - } - - return 0; -} -EXPORT_SYMBOL_GPL(atmel_isc_clk_init); - -void atmel_isc_clk_cleanup(struct isc_device *isc) -{ - unsigned int i; - - of_clk_del_provider(isc->dev->of_node); - - for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) { - struct isc_clk *isc_clk = &isc->isc_clks[i]; - - if (!IS_ERR(isc_clk->clk)) - clk_unregister(isc_clk->clk); - } -} -EXPORT_SYMBOL_GPL(atmel_isc_clk_cleanup); diff --git a/drivers/staging/media/deprecated/atmel/atmel-isc-regs.h b/drivers/staging/media/deprecated/atmel/atmel-isc-regs.h deleted file mode 100644 index d06b72228d4f..000000000000 --- a/drivers/staging/media/deprecated/atmel/atmel-isc-regs.h +++ /dev/null @@ -1,413 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __ATMEL_ISC_REGS_H -#define __ATMEL_ISC_REGS_H - -#include <linux/bitops.h> - -/* ISC Control Enable Register 0 */ -#define ISC_CTRLEN 0x00000000 - -/* ISC Control Disable Register 0 */ -#define ISC_CTRLDIS 0x00000004 - -/* ISC Control Status Register 0 */ -#define ISC_CTRLSR 0x00000008 - -#define ISC_CTRL_CAPTURE BIT(0) -#define ISC_CTRL_UPPRO BIT(1) -#define ISC_CTRL_HISREQ BIT(2) -#define ISC_CTRL_HISCLR BIT(3) - -/* ISC Parallel Front End Configuration 0 Register */ -#define ISC_PFE_CFG0 0x0000000c - -#define ISC_PFE_CFG0_HPOL_LOW BIT(0) -#define ISC_PFE_CFG0_VPOL_LOW BIT(1) -#define ISC_PFE_CFG0_PPOL_LOW BIT(2) -#define ISC_PFE_CFG0_CCIR656 BIT(9) -#define ISC_PFE_CFG0_CCIR_CRC BIT(10) -#define ISC_PFE_CFG0_MIPI BIT(14) - -#define ISC_PFE_CFG0_MODE_PROGRESSIVE (0x0 << 4) -#define ISC_PFE_CFG0_MODE_MASK GENMASK(6, 4) - -#define ISC_PFE_CFG0_BPS_EIGHT (0x4 << 28) -#define ISC_PFG_CFG0_BPS_NINE (0x3 << 28) -#define ISC_PFG_CFG0_BPS_TEN (0x2 << 28) -#define ISC_PFG_CFG0_BPS_ELEVEN (0x1 << 28) -#define ISC_PFG_CFG0_BPS_TWELVE (0x0 << 28) -#define ISC_PFE_CFG0_BPS_MASK GENMASK(30, 28) - -#define ISC_PFE_CFG0_COLEN BIT(12) -#define ISC_PFE_CFG0_ROWEN BIT(13) - -/* ISC Parallel Front End Configuration 1 Register */ -#define ISC_PFE_CFG1 0x00000010 - -#define ISC_PFE_CFG1_COLMIN(v) ((v)) -#define ISC_PFE_CFG1_COLMIN_MASK GENMASK(15, 0) -#define ISC_PFE_CFG1_COLMAX(v) ((v) << 16) -#define ISC_PFE_CFG1_COLMAX_MASK GENMASK(31, 16) - -/* ISC Parallel Front End Configuration 2 Register */ -#define ISC_PFE_CFG2 0x00000014 - -#define ISC_PFE_CFG2_ROWMIN(v) ((v)) -#define ISC_PFE_CFG2_ROWMIN_MASK GENMASK(15, 0) -#define ISC_PFE_CFG2_ROWMAX(v) ((v) << 16) -#define ISC_PFE_CFG2_ROWMAX_MASK GENMASK(31, 16) - -/* ISC Clock Enable Register */ -#define ISC_CLKEN 0x00000018 - -/* ISC Clock Disable Register */ -#define ISC_CLKDIS 0x0000001c - -/* ISC Clock Status Register */ -#define ISC_CLKSR 0x00000020 -#define ISC_CLKSR_SIP BIT(31) - -#define ISC_CLK(n) BIT(n) - -/* ISC Clock Configuration Register */ -#define ISC_CLKCFG 0x00000024 -#define ISC_CLKCFG_DIV_SHIFT(n) ((n)*16) -#define ISC_CLKCFG_DIV_MASK(n) GENMASK(((n)*16 + 7), (n)*16) -#define ISC_CLKCFG_SEL_SHIFT(n) ((n)*16 + 8) -#define ISC_CLKCFG_SEL_MASK(n) GENMASK(((n)*17 + 8), ((n)*16 + 8)) - -/* ISC Interrupt Enable Register */ -#define ISC_INTEN 0x00000028 - -/* ISC Interrupt Disable Register */ -#define ISC_INTDIS 0x0000002c - -/* ISC Interrupt Mask Register */ -#define ISC_INTMASK 0x00000030 - -/* ISC Interrupt Status Register */ -#define ISC_INTSR 0x00000034 - -#define ISC_INT_DDONE BIT(8) -#define ISC_INT_HISDONE BIT(12) - -/* ISC DPC Control Register */ -#define ISC_DPC_CTRL 0x40 - -#define ISC_DPC_CTRL_DPCEN BIT(0) -#define ISC_DPC_CTRL_GDCEN BIT(1) -#define ISC_DPC_CTRL_BLCEN BIT(2) - -/* ISC DPC Config Register */ -#define ISC_DPC_CFG 0x44 - -#define ISC_DPC_CFG_BAYSEL_SHIFT 0 - -#define ISC_DPC_CFG_EITPOL BIT(4) - -#define ISC_DPC_CFG_TA_ENABLE BIT(14) -#define ISC_DPC_CFG_TC_ENABLE BIT(13) -#define ISC_DPC_CFG_TM_ENABLE BIT(12) - -#define ISC_DPC_CFG_RE_MODE BIT(17) - -#define ISC_DPC_CFG_GDCCLP_SHIFT 20 -#define ISC_DPC_CFG_GDCCLP_MASK GENMASK(22, 20) - -#define ISC_DPC_CFG_BLOFF_SHIFT 24 -#define ISC_DPC_CFG_BLOFF_MASK GENMASK(31, 24) - -#define ISC_DPC_CFG_BAYCFG_SHIFT 0 -#define ISC_DPC_CFG_BAYCFG_MASK GENMASK(1, 0) -/* ISC DPC Threshold Median Register */ -#define ISC_DPC_THRESHM 0x48 - -/* ISC DPC Threshold Closest Register */ -#define ISC_DPC_THRESHC 0x4C - -/* ISC DPC Threshold Average Register */ -#define ISC_DPC_THRESHA 0x50 - -/* ISC DPC STatus Register */ -#define ISC_DPC_SR 0x54 - -/* ISC White Balance Control Register */ -#define ISC_WB_CTRL 0x00000058 - -/* ISC White Balance Configuration Register */ -#define ISC_WB_CFG 0x0000005c - -/* ISC White Balance Offset for R, GR Register */ -#define ISC_WB_O_RGR 0x00000060 - -/* ISC White Balance Offset for B, GB Register */ -#define ISC_WB_O_BGB 0x00000064 - -/* ISC White Balance Gain for R, GR Register */ -#define ISC_WB_G_RGR 0x00000068 - -/* ISC White Balance Gain for B, GB Register */ -#define ISC_WB_G_BGB 0x0000006c - -/* ISC Color Filter Array Control Register */ -#define ISC_CFA_CTRL 0x00000070 - -/* ISC Color Filter Array Configuration Register */ -#define ISC_CFA_CFG 0x00000074 -#define ISC_CFA_CFG_EITPOL BIT(4) - -#define ISC_BAY_CFG_GRGR 0x0 -#define ISC_BAY_CFG_RGRG 0x1 -#define ISC_BAY_CFG_GBGB 0x2 -#define ISC_BAY_CFG_BGBG 0x3 - -/* ISC Color Correction Control Register */ -#define ISC_CC_CTRL 0x00000078 - -/* ISC Color Correction RR RG Register */ -#define ISC_CC_RR_RG 0x0000007c - -/* ISC Color Correction RB OR Register */ -#define ISC_CC_RB_OR 0x00000080 - -/* ISC Color Correction GR GG Register */ -#define ISC_CC_GR_GG 0x00000084 - -/* ISC Color Correction GB OG Register */ -#define ISC_CC_GB_OG 0x00000088 - -/* ISC Color Correction BR BG Register */ -#define ISC_CC_BR_BG 0x0000008c - -/* ISC Color Correction BB OB Register */ -#define ISC_CC_BB_OB 0x00000090 - -/* ISC Gamma Correction Control Register */ -#define ISC_GAM_CTRL 0x00000094 - -#define ISC_GAM_CTRL_BIPART BIT(4) - -/* ISC_Gamma Correction Blue Entry Register */ -#define ISC_GAM_BENTRY 0x00000098 - -/* ISC_Gamma Correction Green Entry Register */ -#define ISC_GAM_GENTRY 0x00000198 - -/* ISC_Gamma Correction Green Entry Register */ -#define ISC_GAM_RENTRY 0x00000298 - -/* ISC VHXS Control Register */ -#define ISC_VHXS_CTRL 0x398 - -/* ISC VHXS Source Size Register */ -#define ISC_VHXS_SS 0x39C - -/* ISC VHXS Destination Size Register */ -#define ISC_VHXS_DS 0x3A0 - -/* ISC Vertical Factor Register */ -#define ISC_VXS_FACT 0x3a4 - -/* ISC Horizontal Factor Register */ -#define ISC_HXS_FACT 0x3a8 - -/* ISC Vertical Config Register */ -#define ISC_VXS_CFG 0x3ac - -/* ISC Horizontal Config Register */ -#define ISC_HXS_CFG 0x3b0 - -/* ISC Vertical Tap Register */ -#define ISC_VXS_TAP 0x3b4 - -/* ISC Horizontal Tap Register */ -#define ISC_HXS_TAP 0x434 - -/* Offset for CSC register specific to sama5d2 product */ -#define ISC_SAMA5D2_CSC_OFFSET 0 -/* Offset for CSC register specific to sama7g5 product */ -#define ISC_SAMA7G5_CSC_OFFSET 0x11c - -/* Color Space Conversion Control Register */ -#define ISC_CSC_CTRL 0x00000398 - -/* Color Space Conversion YR YG Register */ -#define ISC_CSC_YR_YG 0x0000039c - -/* Color Space Conversion YB OY Register */ -#define ISC_CSC_YB_OY 0x000003a0 - -/* Color Space Conversion CBR CBG Register */ -#define ISC_CSC_CBR_CBG 0x000003a4 - -/* Color Space Conversion CBB OCB Register */ -#define ISC_CSC_CBB_OCB 0x000003a8 - -/* Color Space Conversion CRR CRG Register */ -#define ISC_CSC_CRR_CRG 0x000003ac - -/* Color Space Conversion CRB OCR Register */ -#define ISC_CSC_CRB_OCR 0x000003b0 - -/* Offset for CBC register specific to sama5d2 product */ -#define ISC_SAMA5D2_CBC_OFFSET 0 -/* Offset for CBC register specific to sama7g5 product */ -#define ISC_SAMA7G5_CBC_OFFSET 0x11c - -/* Contrast And Brightness Control Register */ -#define ISC_CBC_CTRL 0x000003b4 - -/* Contrast And Brightness Configuration Register */ -#define ISC_CBC_CFG 0x000003b8 - -/* Brightness Register */ -#define ISC_CBC_BRIGHT 0x000003bc -#define ISC_CBC_BRIGHT_MASK GENMASK(10, 0) - -/* Contrast Register */ -#define ISC_CBC_CONTRAST 0x000003c0 -#define ISC_CBC_CONTRAST_MASK GENMASK(11, 0) - -/* Hue Register */ -#define ISC_CBCHS_HUE 0x4e0 -/* Saturation Register */ -#define ISC_CBCHS_SAT 0x4e4 - -/* Offset for SUB422 register specific to sama5d2 product */ -#define ISC_SAMA5D2_SUB422_OFFSET 0 -/* Offset for SUB422 register specific to sama7g5 product */ -#define ISC_SAMA7G5_SUB422_OFFSET 0x124 - -/* Subsampling 4:4:4 to 4:2:2 Control Register */ -#define ISC_SUB422_CTRL 0x000003c4 - -/* Offset for SUB420 register specific to sama5d2 product */ -#define ISC_SAMA5D2_SUB420_OFFSET 0 -/* Offset for SUB420 register specific to sama7g5 product */ -#define ISC_SAMA7G5_SUB420_OFFSET 0x124 -/* Subsampling 4:2:2 to 4:2:0 Control Register */ -#define ISC_SUB420_CTRL 0x000003cc - -/* Offset for RLP register specific to sama5d2 product */ -#define ISC_SAMA5D2_RLP_OFFSET 0 -/* Offset for RLP register specific to sama7g5 product */ -#define ISC_SAMA7G5_RLP_OFFSET 0x124 -/* Rounding, Limiting and Packing Configuration Register */ -#define ISC_RLP_CFG 0x000003d0 - -#define ISC_RLP_CFG_MODE_DAT8 0x0 -#define ISC_RLP_CFG_MODE_DAT9 0x1 -#define ISC_RLP_CFG_MODE_DAT10 0x2 -#define ISC_RLP_CFG_MODE_DAT11 0x3 -#define ISC_RLP_CFG_MODE_DAT12 0x4 -#define ISC_RLP_CFG_MODE_DATY8 0x5 -#define ISC_RLP_CFG_MODE_DATY10 0x6 -#define ISC_RLP_CFG_MODE_ARGB444 0x7 -#define ISC_RLP_CFG_MODE_ARGB555 0x8 -#define ISC_RLP_CFG_MODE_RGB565 0x9 -#define ISC_RLP_CFG_MODE_ARGB32 0xa -#define ISC_RLP_CFG_MODE_YYCC 0xb -#define ISC_RLP_CFG_MODE_YYCC_LIMITED 0xc -#define ISC_RLP_CFG_MODE_YCYC 0xd -#define ISC_RLP_CFG_MODE_MASK GENMASK(3, 0) - -#define ISC_RLP_CFG_LSH BIT(5) - -#define ISC_RLP_CFG_YMODE_YUYV (3 << 6) -#define ISC_RLP_CFG_YMODE_YVYU (2 << 6) -#define ISC_RLP_CFG_YMODE_VYUY (0 << 6) -#define ISC_RLP_CFG_YMODE_UYVY (1 << 6) - -#define ISC_RLP_CFG_YMODE_MASK GENMASK(7, 6) - -/* Offset for HIS register specific to sama5d2 product */ -#define ISC_SAMA5D2_HIS_OFFSET 0 -/* Offset for HIS register specific to sama7g5 product */ -#define ISC_SAMA7G5_HIS_OFFSET 0x124 -/* Histogram Control Register */ -#define ISC_HIS_CTRL 0x000003d4 - -#define ISC_HIS_CTRL_EN BIT(0) -#define ISC_HIS_CTRL_DIS 0x0 - -/* Histogram Configuration Register */ -#define ISC_HIS_CFG 0x000003d8 - -#define ISC_HIS_CFG_MODE_GR 0x0 -#define ISC_HIS_CFG_MODE_R 0x1 -#define ISC_HIS_CFG_MODE_GB 0x2 -#define ISC_HIS_CFG_MODE_B 0x3 -#define ISC_HIS_CFG_MODE_Y 0x4 -#define ISC_HIS_CFG_MODE_RAW 0x5 -#define ISC_HIS_CFG_MODE_YCCIR656 0x6 - -#define ISC_HIS_CFG_BAYSEL_SHIFT 4 - -#define ISC_HIS_CFG_RAR BIT(8) - -/* Offset for DMA register specific to sama5d2 product */ -#define ISC_SAMA5D2_DMA_OFFSET 0 -/* Offset for DMA register specific to sama7g5 product */ -#define ISC_SAMA7G5_DMA_OFFSET 0x13c - -/* DMA Configuration Register */ -#define ISC_DCFG 0x000003e0 -#define ISC_DCFG_IMODE_PACKED8 0x0 -#define ISC_DCFG_IMODE_PACKED16 0x1 -#define ISC_DCFG_IMODE_PACKED32 0x2 -#define ISC_DCFG_IMODE_YC422SP 0x3 -#define ISC_DCFG_IMODE_YC422P 0x4 -#define ISC_DCFG_IMODE_YC420SP 0x5 -#define ISC_DCFG_IMODE_YC420P 0x6 -#define ISC_DCFG_IMODE_MASK GENMASK(2, 0) - -#define ISC_DCFG_YMBSIZE_SINGLE (0x0 << 4) -#define ISC_DCFG_YMBSIZE_BEATS4 (0x1 << 4) -#define ISC_DCFG_YMBSIZE_BEATS8 (0x2 << 4) -#define ISC_DCFG_YMBSIZE_BEATS16 (0x3 << 4) -#define ISC_DCFG_YMBSIZE_BEATS32 (0x4 << 4) -#define ISC_DCFG_YMBSIZE_MASK GENMASK(6, 4) - -#define ISC_DCFG_CMBSIZE_SINGLE (0x0 << 8) -#define ISC_DCFG_CMBSIZE_BEATS4 (0x1 << 8) -#define ISC_DCFG_CMBSIZE_BEATS8 (0x2 << 8) -#define ISC_DCFG_CMBSIZE_BEATS16 (0x3 << 8) -#define ISC_DCFG_CMBSIZE_BEATS32 (0x4 << 8) -#define ISC_DCFG_CMBSIZE_MASK GENMASK(10, 8) - -/* DMA Control Register */ -#define ISC_DCTRL 0x000003e4 - -#define ISC_DCTRL_DVIEW_PACKED (0x0 << 1) -#define ISC_DCTRL_DVIEW_SEMIPLANAR (0x1 << 1) -#define ISC_DCTRL_DVIEW_PLANAR (0x2 << 1) -#define ISC_DCTRL_DVIEW_MASK GENMASK(2, 1) - -#define ISC_DCTRL_IE_IS (0x0 << 4) - -/* DMA Descriptor Address Register */ -#define ISC_DNDA 0x000003e8 - -/* DMA Address 0 Register */ -#define ISC_DAD0 0x000003ec - -/* DMA Address 1 Register */ -#define ISC_DAD1 0x000003f4 - -/* DMA Address 2 Register */ -#define ISC_DAD2 0x000003fc - -/* Offset for version register specific to sama5d2 product */ -#define ISC_SAMA5D2_VERSION_OFFSET 0 -#define ISC_SAMA7G5_VERSION_OFFSET 0x13c -/* Version Register */ -#define ISC_VERSION 0x0000040c - -/* Offset for version register specific to sama5d2 product */ -#define ISC_SAMA5D2_HIS_ENTRY_OFFSET 0 -/* Offset for version register specific to sama7g5 product */ -#define ISC_SAMA7G5_HIS_ENTRY_OFFSET 0x14c -/* Histogram Entry */ -#define ISC_HIS_ENTRY 0x00000410 - -#endif diff --git a/drivers/staging/media/deprecated/atmel/atmel-isc.h b/drivers/staging/media/deprecated/atmel/atmel-isc.h deleted file mode 100644 index 31767ea74be6..000000000000 --- a/drivers/staging/media/deprecated/atmel/atmel-isc.h +++ /dev/null @@ -1,362 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Microchip Image Sensor Controller (ISC) driver header file - * - * Copyright (C) 2016-2019 Microchip Technology, Inc. - * - * Author: Songjun Wu - * Author: Eugen Hristev <eugen.hristev@microchip.com> - * - */ -#ifndef _ATMEL_ISC_H_ - -#include <linux/clk-provider.h> -#include <linux/platform_device.h> - -#include <media/v4l2-ctrls.h> -#include <media/v4l2-device.h> -#include <media/videobuf2-dma-contig.h> - -#define ISC_CLK_MAX_DIV 255 - -enum isc_clk_id { - ISC_ISPCK = 0, - ISC_MCK = 1, -}; - -struct isc_clk { - struct clk_hw hw; - struct clk *clk; - struct regmap *regmap; - spinlock_t lock; /* serialize access to clock registers */ - u8 id; - u8 parent_id; - u32 div; - struct device *dev; -}; - -#define to_isc_clk(v) container_of(v, struct isc_clk, hw) - -struct isc_buffer { - struct vb2_v4l2_buffer vb; - struct list_head list; -}; - -struct isc_subdev_entity { - struct v4l2_subdev *sd; - struct v4l2_async_connection *asd; - struct device_node *epn; - struct v4l2_async_notifier notifier; - - u32 pfe_cfg0; - - struct list_head list; -}; - -/* - * struct isc_format - ISC media bus format information - This structure represents the interface between the ISC - and the sensor. It's the input format received by - the ISC. - * @fourcc: Fourcc code for this format - * @mbus_code: V4L2 media bus format code. - * @cfa_baycfg: If this format is RAW BAYER, indicate the type of bayer. - this is either BGBG, RGRG, etc. - * @pfe_cfg0_bps: Number of hardware data lines connected to the ISC - */ - -struct isc_format { - u32 fourcc; - u32 mbus_code; - u32 cfa_baycfg; - - bool sd_support; - u32 pfe_cfg0_bps; -}; - -/* Pipeline bitmap */ -#define DPC_DPCENABLE BIT(0) -#define DPC_GDCENABLE BIT(1) -#define DPC_BLCENABLE BIT(2) -#define WB_ENABLE BIT(3) -#define CFA_ENABLE BIT(4) -#define CC_ENABLE BIT(5) -#define GAM_ENABLE BIT(6) -#define GAM_BENABLE BIT(7) -#define GAM_GENABLE BIT(8) -#define GAM_RENABLE BIT(9) -#define VHXS_ENABLE BIT(10) -#define CSC_ENABLE BIT(11) -#define CBC_ENABLE BIT(12) -#define SUB422_ENABLE BIT(13) -#define SUB420_ENABLE BIT(14) - -#define GAM_ENABLES (GAM_RENABLE | GAM_GENABLE | GAM_BENABLE | GAM_ENABLE) - -/* - * struct fmt_config - ISC format configuration and internal pipeline - This structure represents the internal configuration - of the ISC. - It also holds the format that ISC will present to v4l2. - * @sd_format: Pointer to an isc_format struct that holds the sensor - configuration. - * @fourcc: Fourcc code for this format. - * @bpp: Bytes per pixel in the current format. - * @bpp_v4l2: Bytes per pixel in the current format, for v4l2. - This differs from 'bpp' in the sense that in planar - formats, it refers only to the first plane. - * @rlp_cfg_mode: Configuration of the RLP (rounding, limiting packaging) - * @dcfg_imode: Configuration of the input of the DMA module - * @dctrl_dview: Configuration of the output of the DMA module - * @bits_pipeline: Configuration of the pipeline, which modules are enabled - */ -struct fmt_config { - struct isc_format *sd_format; - - u32 fourcc; - u8 bpp; - u8 bpp_v4l2; - - u32 rlp_cfg_mode; - u32 dcfg_imode; - u32 dctrl_dview; - - u32 bits_pipeline; -}; - -#define HIST_ENTRIES 512 -#define HIST_BAYER (ISC_HIS_CFG_MODE_B + 1) - -enum{ - HIST_INIT = 0, - HIST_ENABLED, - HIST_DISABLED, -}; - -struct isc_ctrls { - struct v4l2_ctrl_handler handler; - - u32 brightness; - u32 contrast; - u8 gamma_index; -#define ISC_WB_NONE 0 -#define ISC_WB_AUTO 1 -#define ISC_WB_ONETIME 2 - u8 awb; - - /* one for each component : GR, R, GB, B */ - u32 gain[HIST_BAYER]; - s32 offset[HIST_BAYER]; - - u32 hist_entry[HIST_ENTRIES]; - u32 hist_count[HIST_BAYER]; - u8 hist_id; - u8 hist_stat; -#define HIST_MIN_INDEX 0 -#define HIST_MAX_INDEX 1 - u32 hist_minmax[HIST_BAYER][2]; -}; - -#define ISC_PIPE_LINE_NODE_NUM 15 - -/* - * struct isc_reg_offsets - ISC device register offsets - * @csc: Offset for the CSC register - * @cbc: Offset for the CBC register - * @sub422: Offset for the SUB422 register - * @sub420: Offset for the SUB420 register - * @rlp: Offset for the RLP register - * @his: Offset for the HIS related registers - * @dma: Offset for the DMA related registers - * @version: Offset for the version register - * @his_entry: Offset for the HIS entries registers - */ -struct isc_reg_offsets { - u32 csc; - u32 cbc; - u32 sub422; - u32 sub420; - u32 rlp; - u32 his; - u32 dma; - u32 version; - u32 his_entry; -}; - -/* - * struct isc_device - ISC device driver data/config struct - * @regmap: Register map - * @hclock: Hclock clock input (refer datasheet) - * @ispck: iscpck clock (refer datasheet) - * @isc_clks: ISC clocks - * @ispck_required: ISC requires ISP Clock initialization - * @dcfg: DMA master configuration, architecture dependent - * - * @dev: Registered device driver - * @v4l2_dev: v4l2 registered device - * @video_dev: registered video device - * - * @vb2_vidq: video buffer 2 video queue - * @dma_queue_lock: lock to serialize the dma buffer queue - * @dma_queue: the queue for dma buffers - * @cur_frm: current isc frame/buffer - * @sequence: current frame number - * @stop: true if isc is not streaming, false if streaming - * @comp: completion reference that signals frame completion - * - * @fmt: current v42l format - * @user_formats: list of formats that are supported and agreed with sd - * @num_user_formats: how many formats are in user_formats - * - * @config: current ISC format configuration - * @try_config: the current ISC try format , not yet activated - * - * @ctrls: holds information about ISC controls - * @do_wb_ctrl: control regarding the DO_WHITE_BALANCE button - * @awb_work: workqueue reference for autowhitebalance histogram - * analysis - * - * @lock: lock for serializing userspace file operations - * with ISC operations - * @awb_mutex: serialize access to streaming status from awb work queue - * @awb_lock: lock for serializing awb work queue operations - * with DMA/buffer operations - * - * @pipeline: configuration of the ISC pipeline - * - * @current_subdev: current subdevice: the sensor - * @subdev_entities: list of subdevice entitites - * - * @gamma_table: pointer to the table with gamma values, has - * gamma_max sets of GAMMA_ENTRIES entries each - * @gamma_max: maximum number of sets of inside the gamma_table - * - * @max_width: maximum frame width, dependent on the internal RAM - * @max_height: maximum frame height, dependent on the internal RAM - * - * @config_dpc: pointer to a function that initializes product - * specific DPC module - * @config_csc: pointer to a function that initializes product - * specific CSC module - * @config_cbc: pointer to a function that initializes product - * specific CBC module - * @config_cc: pointer to a function that initializes product - * specific CC module - * @config_gam: pointer to a function that initializes product - * specific GAMMA module - * @config_rlp: pointer to a function that initializes product - * specific RLP module - * @config_ctrls: pointer to a functoin that initializes product - * specific v4l2 controls. - * - * @adapt_pipeline: pointer to a function that adapts the pipeline bits - * to the product specific pipeline - * - * @offsets: struct holding the product specific register offsets - * @controller_formats: pointer to the array of possible formats that the - * controller can output - * @formats_list: pointer to the array of possible formats that can - * be used as an input to the controller - * @controller_formats_size: size of controller_formats array - * @formats_list_size: size of formats_list array - */ -struct isc_device { - struct regmap *regmap; - struct clk *hclock; - struct clk *ispck; - struct isc_clk isc_clks[2]; - bool ispck_required; - u32 dcfg; - - struct device *dev; - struct v4l2_device v4l2_dev; - struct video_device video_dev; - - struct vb2_queue vb2_vidq; - spinlock_t dma_queue_lock; - struct list_head dma_queue; - struct isc_buffer *cur_frm; - unsigned int sequence; - bool stop; - struct completion comp; - - struct v4l2_format fmt; - struct isc_format **user_formats; - unsigned int num_user_formats; - - struct fmt_config config; - struct fmt_config try_config; - - struct isc_ctrls ctrls; - struct work_struct awb_work; - - struct mutex lock; - struct mutex awb_mutex; - spinlock_t awb_lock; - - struct regmap_field *pipeline[ISC_PIPE_LINE_NODE_NUM]; - - struct isc_subdev_entity *current_subdev; - struct list_head subdev_entities; - - struct { -#define ISC_CTRL_DO_WB 1 -#define ISC_CTRL_R_GAIN 2 -#define ISC_CTRL_B_GAIN 3 -#define ISC_CTRL_GR_GAIN 4 -#define ISC_CTRL_GB_GAIN 5 -#define ISC_CTRL_R_OFF 6 -#define ISC_CTRL_B_OFF 7 -#define ISC_CTRL_GR_OFF 8 -#define ISC_CTRL_GB_OFF 9 - struct v4l2_ctrl *awb_ctrl; - struct v4l2_ctrl *do_wb_ctrl; - struct v4l2_ctrl *r_gain_ctrl; - struct v4l2_ctrl *b_gain_ctrl; - struct v4l2_ctrl *gr_gain_ctrl; - struct v4l2_ctrl *gb_gain_ctrl; - struct v4l2_ctrl *r_off_ctrl; - struct v4l2_ctrl *b_off_ctrl; - struct v4l2_ctrl *gr_off_ctrl; - struct v4l2_ctrl *gb_off_ctrl; - }; - -#define GAMMA_ENTRIES 64 - /* pointer to the defined gamma table */ - const u32 (*gamma_table)[GAMMA_ENTRIES]; - u32 gamma_max; - - u32 max_width; - u32 max_height; - - struct { - void (*config_dpc)(struct isc_device *isc); - void (*config_csc)(struct isc_device *isc); - void (*config_cbc)(struct isc_device *isc); - void (*config_cc)(struct isc_device *isc); - void (*config_gam)(struct isc_device *isc); - void (*config_rlp)(struct isc_device *isc); - - void (*config_ctrls)(struct isc_device *isc, - const struct v4l2_ctrl_ops *ops); - - void (*adapt_pipeline)(struct isc_device *isc); - }; - - struct isc_reg_offsets offsets; - const struct isc_format *controller_formats; - struct isc_format *formats_list; - u32 controller_formats_size; - u32 formats_list_size; -}; - -extern const struct regmap_config atmel_isc_regmap_config; -extern const struct v4l2_async_notifier_operations atmel_isc_async_ops; - -irqreturn_t atmel_isc_interrupt(int irq, void *dev_id); -int atmel_isc_pipeline_init(struct isc_device *isc); -int atmel_isc_clk_init(struct isc_device *isc); -void atmel_isc_subdev_cleanup(struct isc_device *isc); -void atmel_isc_clk_cleanup(struct isc_device *isc); - -#endif diff --git a/drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c b/drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c deleted file mode 100644 index 71e6e278a4b3..000000000000 --- a/drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c +++ /dev/null @@ -1,644 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Microchip Image Sensor Controller (ISC) driver - * - * Copyright (C) 2016-2019 Microchip Technology, Inc. - * - * Author: Songjun Wu - * Author: Eugen Hristev <eugen.hristev@microchip.com> - * - * - * Sensor-->PFE-->WB-->CFA-->CC-->GAM-->CSC-->CBC-->SUB-->RLP-->DMA - * - * ISC video pipeline integrates the following submodules: - * PFE: Parallel Front End to sample the camera sensor input stream - * WB: Programmable white balance in the Bayer domain - * CFA: Color filter array interpolation module - * CC: Programmable color correction - * GAM: Gamma correction - * CSC: Programmable color space conversion - * CBC: Contrast and Brightness control - * SUB: This module performs YCbCr444 to YCbCr420 chrominance subsampling - * RLP: This module performs rounding, range limiting - * and packing of the incoming data - */ - -#include <linux/clk.h> -#include <linux/clkdev.h> -#include <linux/clk-provider.h> -#include <linux/delay.h> -#include <linux/interrupt.h> -#include <linux/math64.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/of_graph.h> -#include <linux/platform_device.h> -#include <linux/pm_runtime.h> -#include <linux/regmap.h> -#include <linux/videodev2.h> - -#include <media/v4l2-ctrls.h> -#include <media/v4l2-device.h> -#include <media/v4l2-event.h> -#include <media/v4l2-image-sizes.h> -#include <media/v4l2-ioctl.h> -#include <media/v4l2-fwnode.h> -#include <media/v4l2-subdev.h> -#include <media/videobuf2-dma-contig.h> - -#include "atmel-isc-regs.h" -#include "atmel-isc.h" - -#define ISC_SAMA5D2_MAX_SUPPORT_WIDTH 2592 -#define ISC_SAMA5D2_MAX_SUPPORT_HEIGHT 1944 - -#define ISC_SAMA5D2_PIPELINE \ - (WB_ENABLE | CFA_ENABLE | CC_ENABLE | GAM_ENABLES | CSC_ENABLE | \ - CBC_ENABLE | SUB422_ENABLE | SUB420_ENABLE) - -/* This is a list of the formats that the ISC can *output* */ -static const struct isc_format sama5d2_controller_formats[] = { - { - .fourcc = V4L2_PIX_FMT_ARGB444, - }, { - .fourcc = V4L2_PIX_FMT_ARGB555, - }, { - .fourcc = V4L2_PIX_FMT_RGB565, - }, { - .fourcc = V4L2_PIX_FMT_ABGR32, - }, { - .fourcc = V4L2_PIX_FMT_XBGR32, - }, { - .fourcc = V4L2_PIX_FMT_YUV420, - }, { - .fourcc = V4L2_PIX_FMT_YUYV, - }, { - .fourcc = V4L2_PIX_FMT_YUV422P, - }, { - .fourcc = V4L2_PIX_FMT_GREY, - }, { - .fourcc = V4L2_PIX_FMT_Y10, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR8, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG8, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG8, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB8, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR10, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG10, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG10, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB10, - }, -}; - -/* This is a list of formats that the ISC can receive as *input* */ -static struct isc_format sama5d2_formats_list[] = { - { - .fourcc = V4L2_PIX_FMT_SBGGR8, - .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - .cfa_baycfg = ISC_BAY_CFG_BGBG, - }, - { - .fourcc = V4L2_PIX_FMT_SGBRG8, - .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - .cfa_baycfg = ISC_BAY_CFG_GBGB, - }, - { - .fourcc = V4L2_PIX_FMT_SGRBG8, - .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - .cfa_baycfg = ISC_BAY_CFG_GRGR, - }, - { - .fourcc = V4L2_PIX_FMT_SRGGB8, - .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - .cfa_baycfg = ISC_BAY_CFG_RGRG, - }, - { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, - .cfa_baycfg = ISC_BAY_CFG_RGRG, - }, - { - .fourcc = V4L2_PIX_FMT_SGBRG10, - .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, - .cfa_baycfg = ISC_BAY_CFG_GBGB, - }, - { - .fourcc = V4L2_PIX_FMT_SGRBG10, - .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, - .cfa_baycfg = ISC_BAY_CFG_GRGR, - }, - { - .fourcc = V4L2_PIX_FMT_SRGGB10, - .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, - .cfa_baycfg = ISC_BAY_CFG_RGRG, - }, - { - .fourcc = V4L2_PIX_FMT_SBGGR12, - .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE, - .cfa_baycfg = ISC_BAY_CFG_BGBG, - }, - { - .fourcc = V4L2_PIX_FMT_SGBRG12, - .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE, - .cfa_baycfg = ISC_BAY_CFG_GBGB, - }, - { - .fourcc = V4L2_PIX_FMT_SGRBG12, - .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE, - .cfa_baycfg = ISC_BAY_CFG_GRGR, - }, - { - .fourcc = V4L2_PIX_FMT_SRGGB12, - .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE, - .cfa_baycfg = ISC_BAY_CFG_RGRG, - }, - { - .fourcc = V4L2_PIX_FMT_GREY, - .mbus_code = MEDIA_BUS_FMT_Y8_1X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - }, - { - .fourcc = V4L2_PIX_FMT_YUYV, - .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - }, - { - .fourcc = V4L2_PIX_FMT_RGB565, - .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - }, - { - .fourcc = V4L2_PIX_FMT_Y10, - .mbus_code = MEDIA_BUS_FMT_Y10_1X10, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, - }, - -}; - -static void isc_sama5d2_config_csc(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - - /* Convert RGB to YUV */ - regmap_write(regmap, ISC_CSC_YR_YG + isc->offsets.csc, - 0x42 | (0x81 << 16)); - regmap_write(regmap, ISC_CSC_YB_OY + isc->offsets.csc, - 0x19 | (0x10 << 16)); - regmap_write(regmap, ISC_CSC_CBR_CBG + isc->offsets.csc, - 0xFDA | (0xFB6 << 16)); - regmap_write(regmap, ISC_CSC_CBB_OCB + isc->offsets.csc, - 0x70 | (0x80 << 16)); - regmap_write(regmap, ISC_CSC_CRR_CRG + isc->offsets.csc, - 0x70 | (0xFA2 << 16)); - regmap_write(regmap, ISC_CSC_CRB_OCR + isc->offsets.csc, - 0xFEE | (0x80 << 16)); -} - -static void isc_sama5d2_config_cbc(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - - regmap_write(regmap, ISC_CBC_BRIGHT + isc->offsets.cbc, - isc->ctrls.brightness); - regmap_write(regmap, ISC_CBC_CONTRAST + isc->offsets.cbc, - isc->ctrls.contrast); -} - -static void isc_sama5d2_config_cc(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - - /* Configure each register at the neutral fixed point 1.0 or 0.0 */ - regmap_write(regmap, ISC_CC_RR_RG, (1 << 8)); - regmap_write(regmap, ISC_CC_RB_OR, 0); - regmap_write(regmap, ISC_CC_GR_GG, (1 << 8) << 16); - regmap_write(regmap, ISC_CC_GB_OG, 0); - regmap_write(regmap, ISC_CC_BR_BG, 0); - regmap_write(regmap, ISC_CC_BB_OB, (1 << 8)); -} - -static void isc_sama5d2_config_ctrls(struct isc_device *isc, - const struct v4l2_ctrl_ops *ops) -{ - struct isc_ctrls *ctrls = &isc->ctrls; - struct v4l2_ctrl_handler *hdl = &ctrls->handler; - - ctrls->contrast = 256; - - v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -2048, 2047, 1, 256); -} - -static void isc_sama5d2_config_dpc(struct isc_device *isc) -{ - /* This module is not present on sama5d2 pipeline */ -} - -static void isc_sama5d2_config_gam(struct isc_device *isc) -{ - /* No specific gamma configuration */ -} - -static void isc_sama5d2_config_rlp(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - u32 rlp_mode = isc->config.rlp_cfg_mode; - - /* - * In sama5d2, the YUV planar modes and the YUYV modes are treated - * in the same way in RLP register. - * Normally, YYCC mode should be Luma(n) - Color B(n) - Color R (n) - * and YCYC should be Luma(n + 1) - Color B (n) - Luma (n) - Color R (n) - * but in sama5d2, the YCYC mode does not exist, and YYCC must be - * selected for both planar and interleaved modes, as in fact - * both modes are supported. - * - * Thus, if the YCYC mode is selected, replace it with the - * sama5d2-compliant mode which is YYCC . - */ - if ((rlp_mode & ISC_RLP_CFG_MODE_MASK) == ISC_RLP_CFG_MODE_YCYC) { - rlp_mode &= ~ISC_RLP_CFG_MODE_MASK; - rlp_mode |= ISC_RLP_CFG_MODE_YYCC; - } - - regmap_update_bits(regmap, ISC_RLP_CFG + isc->offsets.rlp, - ISC_RLP_CFG_MODE_MASK, rlp_mode); -} - -static void isc_sama5d2_adapt_pipeline(struct isc_device *isc) -{ - isc->try_config.bits_pipeline &= ISC_SAMA5D2_PIPELINE; -} - -/* Gamma table with gamma 1/2.2 */ -static const u32 isc_sama5d2_gamma_table[][GAMMA_ENTRIES] = { - /* 0 --> gamma 1/1.8 */ - { 0x65, 0x66002F, 0x950025, 0xBB0020, 0xDB001D, 0xF8001A, - 0x1130018, 0x12B0017, 0x1420016, 0x1580014, 0x16D0013, 0x1810012, - 0x1940012, 0x1A60012, 0x1B80011, 0x1C90010, 0x1DA0010, 0x1EA000F, - 0x1FA000F, 0x209000F, 0x218000F, 0x227000E, 0x235000E, 0x243000E, - 0x251000E, 0x25F000D, 0x26C000D, 0x279000D, 0x286000D, 0x293000C, - 0x2A0000C, 0x2AC000C, 0x2B8000C, 0x2C4000C, 0x2D0000B, 0x2DC000B, - 0x2E7000B, 0x2F3000B, 0x2FE000B, 0x309000B, 0x314000B, 0x31F000A, - 0x32A000A, 0x334000B, 0x33F000A, 0x349000A, 0x354000A, 0x35E000A, - 0x368000A, 0x372000A, 0x37C000A, 0x386000A, 0x3900009, 0x399000A, - 0x3A30009, 0x3AD0009, 0x3B60009, 0x3BF000A, 0x3C90009, 0x3D20009, - 0x3DB0009, 0x3E40009, 0x3ED0009, 0x3F60009 }, - - /* 1 --> gamma 1/2 */ - { 0x7F, 0x800034, 0xB50028, 0xDE0021, 0x100001E, 0x11E001B, - 0x1390019, 0x1520017, 0x16A0015, 0x1800014, 0x1940014, 0x1A80013, - 0x1BB0012, 0x1CD0011, 0x1DF0010, 0x1EF0010, 0x200000F, 0x20F000F, - 0x21F000E, 0x22D000F, 0x23C000E, 0x24A000E, 0x258000D, 0x265000D, - 0x273000C, 0x27F000D, 0x28C000C, 0x299000C, 0x2A5000C, 0x2B1000B, - 0x2BC000C, 0x2C8000B, 0x2D3000C, 0x2DF000B, 0x2EA000A, 0x2F5000A, - 0x2FF000B, 0x30A000A, 0x314000B, 0x31F000A, 0x329000A, 0x333000A, - 0x33D0009, 0x3470009, 0x350000A, 0x35A0009, 0x363000A, 0x36D0009, - 0x3760009, 0x37F0009, 0x3880009, 0x3910009, 0x39A0009, 0x3A30009, - 0x3AC0008, 0x3B40009, 0x3BD0008, 0x3C60008, 0x3CE0008, 0x3D60009, - 0x3DF0008, 0x3E70008, 0x3EF0008, 0x3F70008 }, - - /* 2 --> gamma 1/2.2 */ - { 0x99, 0x9B0038, 0xD4002A, 0xFF0023, 0x122001F, 0x141001B, - 0x15D0019, 0x1760017, 0x18E0015, 0x1A30015, 0x1B80013, 0x1CC0012, - 0x1DE0011, 0x1F00010, 0x2010010, 0x2110010, 0x221000F, 0x230000F, - 0x23F000E, 0x24D000E, 0x25B000D, 0x269000C, 0x276000C, 0x283000C, - 0x28F000C, 0x29B000C, 0x2A7000C, 0x2B3000B, 0x2BF000B, 0x2CA000B, - 0x2D5000B, 0x2E0000A, 0x2EB000A, 0x2F5000A, 0x2FF000A, 0x30A000A, - 0x3140009, 0x31E0009, 0x327000A, 0x3310009, 0x33A0009, 0x3440009, - 0x34D0009, 0x3560009, 0x35F0009, 0x3680008, 0x3710008, 0x3790009, - 0x3820008, 0x38A0008, 0x3930008, 0x39B0008, 0x3A30008, 0x3AB0008, - 0x3B30008, 0x3BB0008, 0x3C30008, 0x3CB0007, 0x3D20008, 0x3DA0007, - 0x3E20007, 0x3E90007, 0x3F00008, 0x3F80007 }, -}; - -static int isc_parse_dt(struct device *dev, struct isc_device *isc) -{ - struct device_node *np = dev->of_node; - struct device_node *epn; - struct isc_subdev_entity *subdev_entity; - unsigned int flags; - int ret = -EINVAL; - - INIT_LIST_HEAD(&isc->subdev_entities); - - for_each_endpoint_of_node(np, epn) { - struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 }; - - ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn), - &v4l2_epn); - if (ret) { - ret = -EINVAL; - dev_err(dev, "Could not parse the endpoint\n"); - break; - } - - subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity), - GFP_KERNEL); - if (!subdev_entity) { - ret = -ENOMEM; - break; - } - subdev_entity->epn = epn; - - flags = v4l2_epn.bus.parallel.flags; - - if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) - subdev_entity->pfe_cfg0 = ISC_PFE_CFG0_HPOL_LOW; - - if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) - subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_VPOL_LOW; - - if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) - subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_PPOL_LOW; - - if (v4l2_epn.bus_type == V4L2_MBUS_BT656) - subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_CCIR_CRC | - ISC_PFE_CFG0_CCIR656; - - list_add_tail(&subdev_entity->list, &isc->subdev_entities); - } - of_node_put(epn); - - return ret; -} - -static int atmel_isc_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct isc_device *isc; - void __iomem *io_base; - struct isc_subdev_entity *subdev_entity; - int irq; - int ret; - u32 ver; - - isc = devm_kzalloc(dev, sizeof(*isc), GFP_KERNEL); - if (!isc) - return -ENOMEM; - - platform_set_drvdata(pdev, isc); - isc->dev = dev; - - io_base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(io_base)) - return PTR_ERR(io_base); - - isc->regmap = devm_regmap_init_mmio(dev, io_base, &atmel_isc_regmap_config); - if (IS_ERR(isc->regmap)) { - ret = PTR_ERR(isc->regmap); - dev_err(dev, "failed to init register map: %d\n", ret); - return ret; - } - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - ret = devm_request_irq(dev, irq, atmel_isc_interrupt, 0, - "atmel-sama5d2-isc", isc); - if (ret < 0) { - dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n", - irq, ret); - return ret; - } - - isc->gamma_table = isc_sama5d2_gamma_table; - isc->gamma_max = 2; - - isc->max_width = ISC_SAMA5D2_MAX_SUPPORT_WIDTH; - isc->max_height = ISC_SAMA5D2_MAX_SUPPORT_HEIGHT; - - isc->config_dpc = isc_sama5d2_config_dpc; - isc->config_csc = isc_sama5d2_config_csc; - isc->config_cbc = isc_sama5d2_config_cbc; - isc->config_cc = isc_sama5d2_config_cc; - isc->config_gam = isc_sama5d2_config_gam; - isc->config_rlp = isc_sama5d2_config_rlp; - isc->config_ctrls = isc_sama5d2_config_ctrls; - - isc->adapt_pipeline = isc_sama5d2_adapt_pipeline; - - isc->offsets.csc = ISC_SAMA5D2_CSC_OFFSET; - isc->offsets.cbc = ISC_SAMA5D2_CBC_OFFSET; - isc->offsets.sub422 = ISC_SAMA5D2_SUB422_OFFSET; - isc->offsets.sub420 = ISC_SAMA5D2_SUB420_OFFSET; - isc->offsets.rlp = ISC_SAMA5D2_RLP_OFFSET; - isc->offsets.his = ISC_SAMA5D2_HIS_OFFSET; - isc->offsets.dma = ISC_SAMA5D2_DMA_OFFSET; - isc->offsets.version = ISC_SAMA5D2_VERSION_OFFSET; - isc->offsets.his_entry = ISC_SAMA5D2_HIS_ENTRY_OFFSET; - - isc->controller_formats = sama5d2_controller_formats; - isc->controller_formats_size = ARRAY_SIZE(sama5d2_controller_formats); - isc->formats_list = sama5d2_formats_list; - isc->formats_list_size = ARRAY_SIZE(sama5d2_formats_list); - - /* sama5d2-isc - 8 bits per beat */ - isc->dcfg = ISC_DCFG_YMBSIZE_BEATS8 | ISC_DCFG_CMBSIZE_BEATS8; - - /* sama5d2-isc : ISPCK is required and mandatory */ - isc->ispck_required = true; - - ret = atmel_isc_pipeline_init(isc); - if (ret) - return ret; - - isc->hclock = devm_clk_get(dev, "hclock"); - if (IS_ERR(isc->hclock)) { - ret = PTR_ERR(isc->hclock); - dev_err(dev, "failed to get hclock: %d\n", ret); - return ret; - } - - ret = clk_prepare_enable(isc->hclock); - if (ret) { - dev_err(dev, "failed to enable hclock: %d\n", ret); - return ret; - } - - ret = atmel_isc_clk_init(isc); - if (ret) { - dev_err(dev, "failed to init isc clock: %d\n", ret); - goto unprepare_hclk; - } - ret = v4l2_device_register(dev, &isc->v4l2_dev); - if (ret) { - dev_err(dev, "unable to register v4l2 device.\n"); - goto unprepare_clk; - } - - ret = isc_parse_dt(dev, isc); - if (ret) { - dev_err(dev, "fail to parse device tree\n"); - goto unregister_v4l2_device; - } - - if (list_empty(&isc->subdev_entities)) { - dev_err(dev, "no subdev found\n"); - ret = -ENODEV; - goto unregister_v4l2_device; - } - - list_for_each_entry(subdev_entity, &isc->subdev_entities, list) { - struct v4l2_async_connection *asd; - struct fwnode_handle *fwnode = - of_fwnode_handle(subdev_entity->epn); - - v4l2_async_nf_init(&subdev_entity->notifier, &isc->v4l2_dev); - - asd = v4l2_async_nf_add_fwnode_remote(&subdev_entity->notifier, - fwnode, - struct v4l2_async_connection); - - of_node_put(subdev_entity->epn); - subdev_entity->epn = NULL; - - if (IS_ERR(asd)) { - ret = PTR_ERR(asd); - goto cleanup_subdev; - } - - subdev_entity->notifier.ops = &atmel_isc_async_ops; - - ret = v4l2_async_nf_register(&subdev_entity->notifier); - if (ret) { - dev_err(dev, "fail to register async notifier\n"); - goto cleanup_subdev; - } - - if (video_is_registered(&isc->video_dev)) - break; - } - - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - pm_request_idle(dev); - - isc->ispck = isc->isc_clks[ISC_ISPCK].clk; - - ret = clk_prepare_enable(isc->ispck); - if (ret) { - dev_err(dev, "failed to enable ispck: %d\n", ret); - goto disable_pm; - } - - /* ispck should be greater or equal to hclock */ - ret = clk_set_rate(isc->ispck, clk_get_rate(isc->hclock)); - if (ret) { - dev_err(dev, "failed to set ispck rate: %d\n", ret); - goto unprepare_clk; - } - - regmap_read(isc->regmap, ISC_VERSION + isc->offsets.version, &ver); - dev_info(dev, "Microchip ISC version %x\n", ver); - - return 0; - -unprepare_clk: - clk_disable_unprepare(isc->ispck); - -disable_pm: - pm_runtime_disable(dev); - -cleanup_subdev: - atmel_isc_subdev_cleanup(isc); - -unregister_v4l2_device: - v4l2_device_unregister(&isc->v4l2_dev); - -unprepare_hclk: - clk_disable_unprepare(isc->hclock); - - atmel_isc_clk_cleanup(isc); - - return ret; -} - -static void atmel_isc_remove(struct platform_device *pdev) -{ - struct isc_device *isc = platform_get_drvdata(pdev); - - pm_runtime_disable(&pdev->dev); - - atmel_isc_subdev_cleanup(isc); - - v4l2_device_unregister(&isc->v4l2_dev); - - clk_disable_unprepare(isc->ispck); - clk_disable_unprepare(isc->hclock); - - atmel_isc_clk_cleanup(isc); -} - -static int __maybe_unused isc_runtime_suspend(struct device *dev) -{ - struct isc_device *isc = dev_get_drvdata(dev); - - clk_disable_unprepare(isc->ispck); - clk_disable_unprepare(isc->hclock); - - return 0; -} - -static int __maybe_unused isc_runtime_resume(struct device *dev) -{ - struct isc_device *isc = dev_get_drvdata(dev); - int ret; - - ret = clk_prepare_enable(isc->hclock); - if (ret) - return ret; - - ret = clk_prepare_enable(isc->ispck); - if (ret) - clk_disable_unprepare(isc->hclock); - - return ret; -} - -static const struct dev_pm_ops atmel_isc_dev_pm_ops = { - SET_RUNTIME_PM_OPS(isc_runtime_suspend, isc_runtime_resume, NULL) -}; - -#if IS_ENABLED(CONFIG_OF) -static const struct of_device_id atmel_isc_of_match[] = { - { .compatible = "atmel,sama5d2-isc" }, - { } -}; -MODULE_DEVICE_TABLE(of, atmel_isc_of_match); -#endif - -static struct platform_driver atmel_isc_driver = { - .probe = atmel_isc_probe, - .remove = atmel_isc_remove, - .driver = { - .name = "atmel-sama5d2-isc", - .pm = &atmel_isc_dev_pm_ops, - .of_match_table = of_match_ptr(atmel_isc_of_match), - }, -}; - -module_platform_driver(atmel_isc_driver); - -MODULE_AUTHOR("Songjun Wu"); -MODULE_DESCRIPTION("The V4L2 driver for Atmel-ISC"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c b/drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c deleted file mode 100644 index 1f74c2dd044c..000000000000 --- a/drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c +++ /dev/null @@ -1,607 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Microchip eXtended Image Sensor Controller (XISC) driver - * - * Copyright (C) 2019-2021 Microchip Technology, Inc. and its subsidiaries - * - * Author: Eugen Hristev <eugen.hristev@microchip.com> - * - * Sensor-->PFE-->DPC-->WB-->CFA-->CC-->GAM-->VHXS-->CSC-->CBHS-->SUB-->RLP-->DMA-->HIS - * - * ISC video pipeline integrates the following submodules: - * PFE: Parallel Front End to sample the camera sensor input stream - * DPC: Defective Pixel Correction with black offset correction, green disparity - * correction and defective pixel correction (3 modules total) - * WB: Programmable white balance in the Bayer domain - * CFA: Color filter array interpolation module - * CC: Programmable color correction - * GAM: Gamma correction - *VHXS: Vertical and Horizontal Scaler - * CSC: Programmable color space conversion - *CBHS: Contrast Brightness Hue and Saturation control - * SUB: This module performs YCbCr444 to YCbCr420 chrominance subsampling - * RLP: This module performs rounding, range limiting - * and packing of the incoming data - * DMA: This module performs DMA master accesses to write frames to external RAM - * HIS: Histogram module performs statistic counters on the frames - */ - -#include <linux/clk.h> -#include <linux/clkdev.h> -#include <linux/clk-provider.h> -#include <linux/delay.h> -#include <linux/interrupt.h> -#include <linux/math64.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/of_graph.h> -#include <linux/platform_device.h> -#include <linux/pm_runtime.h> -#include <linux/regmap.h> -#include <linux/videodev2.h> - -#include <media/v4l2-ctrls.h> -#include <media/v4l2-device.h> -#include <media/v4l2-event.h> -#include <media/v4l2-image-sizes.h> -#include <media/v4l2-ioctl.h> -#include <media/v4l2-fwnode.h> -#include <media/v4l2-subdev.h> -#include <media/videobuf2-dma-contig.h> - -#include "atmel-isc-regs.h" -#include "atmel-isc.h" - -#define ISC_SAMA7G5_MAX_SUPPORT_WIDTH 3264 -#define ISC_SAMA7G5_MAX_SUPPORT_HEIGHT 2464 - -#define ISC_SAMA7G5_PIPELINE \ - (WB_ENABLE | CFA_ENABLE | CC_ENABLE | GAM_ENABLES | CSC_ENABLE | \ - CBC_ENABLE | SUB422_ENABLE | SUB420_ENABLE) - -/* This is a list of the formats that the ISC can *output* */ -static const struct isc_format sama7g5_controller_formats[] = { - { - .fourcc = V4L2_PIX_FMT_ARGB444, - }, { - .fourcc = V4L2_PIX_FMT_ARGB555, - }, { - .fourcc = V4L2_PIX_FMT_RGB565, - }, { - .fourcc = V4L2_PIX_FMT_ABGR32, - }, { - .fourcc = V4L2_PIX_FMT_XBGR32, - }, { - .fourcc = V4L2_PIX_FMT_YUV420, - }, { - .fourcc = V4L2_PIX_FMT_UYVY, - }, { - .fourcc = V4L2_PIX_FMT_VYUY, - }, { - .fourcc = V4L2_PIX_FMT_YUYV, - }, { - .fourcc = V4L2_PIX_FMT_YUV422P, - }, { - .fourcc = V4L2_PIX_FMT_GREY, - }, { - .fourcc = V4L2_PIX_FMT_Y10, - }, { - .fourcc = V4L2_PIX_FMT_Y16, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR8, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG8, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG8, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB8, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR10, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG10, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG10, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB10, - }, -}; - -/* This is a list of formats that the ISC can receive as *input* */ -static struct isc_format sama7g5_formats_list[] = { - { - .fourcc = V4L2_PIX_FMT_SBGGR8, - .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - .cfa_baycfg = ISC_BAY_CFG_BGBG, - }, - { - .fourcc = V4L2_PIX_FMT_SGBRG8, - .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - .cfa_baycfg = ISC_BAY_CFG_GBGB, - }, - { - .fourcc = V4L2_PIX_FMT_SGRBG8, - .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - .cfa_baycfg = ISC_BAY_CFG_GRGR, - }, - { - .fourcc = V4L2_PIX_FMT_SRGGB8, - .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - .cfa_baycfg = ISC_BAY_CFG_RGRG, - }, - { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, - .cfa_baycfg = ISC_BAY_CFG_RGRG, - }, - { - .fourcc = V4L2_PIX_FMT_SGBRG10, - .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, - .cfa_baycfg = ISC_BAY_CFG_GBGB, - }, - { - .fourcc = V4L2_PIX_FMT_SGRBG10, - .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, - .cfa_baycfg = ISC_BAY_CFG_GRGR, - }, - { - .fourcc = V4L2_PIX_FMT_SRGGB10, - .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, - .cfa_baycfg = ISC_BAY_CFG_RGRG, - }, - { - .fourcc = V4L2_PIX_FMT_SBGGR12, - .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE, - .cfa_baycfg = ISC_BAY_CFG_BGBG, - }, - { - .fourcc = V4L2_PIX_FMT_SGBRG12, - .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE, - .cfa_baycfg = ISC_BAY_CFG_GBGB, - }, - { - .fourcc = V4L2_PIX_FMT_SGRBG12, - .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE, - .cfa_baycfg = ISC_BAY_CFG_GRGR, - }, - { - .fourcc = V4L2_PIX_FMT_SRGGB12, - .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE, - .cfa_baycfg = ISC_BAY_CFG_RGRG, - }, - { - .fourcc = V4L2_PIX_FMT_GREY, - .mbus_code = MEDIA_BUS_FMT_Y8_1X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - }, - { - .fourcc = V4L2_PIX_FMT_YUYV, - .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - }, - { - .fourcc = V4L2_PIX_FMT_UYVY, - .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - }, - { - .fourcc = V4L2_PIX_FMT_RGB565, - .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - }, - { - .fourcc = V4L2_PIX_FMT_Y10, - .mbus_code = MEDIA_BUS_FMT_Y10_1X10, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, - }, -}; - -static void isc_sama7g5_config_csc(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - - /* Convert RGB to YUV */ - regmap_write(regmap, ISC_CSC_YR_YG + isc->offsets.csc, - 0x42 | (0x81 << 16)); - regmap_write(regmap, ISC_CSC_YB_OY + isc->offsets.csc, - 0x19 | (0x10 << 16)); - regmap_write(regmap, ISC_CSC_CBR_CBG + isc->offsets.csc, - 0xFDA | (0xFB6 << 16)); - regmap_write(regmap, ISC_CSC_CBB_OCB + isc->offsets.csc, - 0x70 | (0x80 << 16)); - regmap_write(regmap, ISC_CSC_CRR_CRG + isc->offsets.csc, - 0x70 | (0xFA2 << 16)); - regmap_write(regmap, ISC_CSC_CRB_OCR + isc->offsets.csc, - 0xFEE | (0x80 << 16)); -} - -static void isc_sama7g5_config_cbc(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - - /* Configure what is set via v4l2 ctrls */ - regmap_write(regmap, ISC_CBC_BRIGHT + isc->offsets.cbc, isc->ctrls.brightness); - regmap_write(regmap, ISC_CBC_CONTRAST + isc->offsets.cbc, isc->ctrls.contrast); - /* Configure Hue and Saturation as neutral midpoint */ - regmap_write(regmap, ISC_CBCHS_HUE, 0); - regmap_write(regmap, ISC_CBCHS_SAT, (1 << 4)); -} - -static void isc_sama7g5_config_cc(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - - /* Configure each register at the neutral fixed point 1.0 or 0.0 */ - regmap_write(regmap, ISC_CC_RR_RG, (1 << 8)); - regmap_write(regmap, ISC_CC_RB_OR, 0); - regmap_write(regmap, ISC_CC_GR_GG, (1 << 8) << 16); - regmap_write(regmap, ISC_CC_GB_OG, 0); - regmap_write(regmap, ISC_CC_BR_BG, 0); - regmap_write(regmap, ISC_CC_BB_OB, (1 << 8)); -} - -static void isc_sama7g5_config_ctrls(struct isc_device *isc, - const struct v4l2_ctrl_ops *ops) -{ - struct isc_ctrls *ctrls = &isc->ctrls; - struct v4l2_ctrl_handler *hdl = &ctrls->handler; - - ctrls->contrast = 16; - - v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -2048, 2047, 1, 16); -} - -static void isc_sama7g5_config_dpc(struct isc_device *isc) -{ - u32 bay_cfg = isc->config.sd_format->cfa_baycfg; - struct regmap *regmap = isc->regmap; - - regmap_update_bits(regmap, ISC_DPC_CFG, ISC_DPC_CFG_BLOFF_MASK, - (64 << ISC_DPC_CFG_BLOFF_SHIFT)); - regmap_update_bits(regmap, ISC_DPC_CFG, ISC_DPC_CFG_BAYCFG_MASK, - (bay_cfg << ISC_DPC_CFG_BAYCFG_SHIFT)); -} - -static void isc_sama7g5_config_gam(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - - regmap_update_bits(regmap, ISC_GAM_CTRL, ISC_GAM_CTRL_BIPART, - ISC_GAM_CTRL_BIPART); -} - -static void isc_sama7g5_config_rlp(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - u32 rlp_mode = isc->config.rlp_cfg_mode; - - regmap_update_bits(regmap, ISC_RLP_CFG + isc->offsets.rlp, - ISC_RLP_CFG_MODE_MASK | ISC_RLP_CFG_LSH | - ISC_RLP_CFG_YMODE_MASK, rlp_mode); -} - -static void isc_sama7g5_adapt_pipeline(struct isc_device *isc) -{ - isc->try_config.bits_pipeline &= ISC_SAMA7G5_PIPELINE; -} - -/* Gamma table with gamma 1/2.2 */ -static const u32 isc_sama7g5_gamma_table[][GAMMA_ENTRIES] = { - /* index 0 --> gamma bipartite */ - { - 0x980, 0x4c0320, 0x650260, 0x7801e0, 0x8701a0, 0x940180, - 0xa00160, 0xab0120, 0xb40120, 0xbd0120, 0xc60100, 0xce0100, - 0xd600e0, 0xdd00e0, 0xe400e0, 0xeb00c0, 0xf100c0, 0xf700c0, - 0xfd00c0, 0x10300a0, 0x10800c0, 0x10e00a0, 0x11300a0, 0x11800a0, - 0x11d00a0, 0x12200a0, 0x12700a0, 0x12c0080, 0x13000a0, 0x1350080, - 0x13900a0, 0x13e0080, 0x1420076, 0x17d0062, 0x1ae0054, 0x1d8004a, - 0x1fd0044, 0x21f003e, 0x23e003a, 0x25b0036, 0x2760032, 0x28f0030, - 0x2a7002e, 0x2be002c, 0x2d4002c, 0x2ea0028, 0x2fe0028, 0x3120026, - 0x3250024, 0x3370024, 0x3490022, 0x35a0022, 0x36b0020, 0x37b0020, - 0x38b0020, 0x39b001e, 0x3aa001e, 0x3b9001c, 0x3c7001c, 0x3d5001c, - 0x3e3001c, 0x3f1001c, 0x3ff001a, 0x40c001a }, -}; - -static int xisc_parse_dt(struct device *dev, struct isc_device *isc) -{ - struct device_node *np = dev->of_node; - struct device_node *epn; - struct isc_subdev_entity *subdev_entity; - unsigned int flags; - int ret = -EINVAL; - bool mipi_mode; - - INIT_LIST_HEAD(&isc->subdev_entities); - - mipi_mode = of_property_read_bool(np, "microchip,mipi-mode"); - - for_each_endpoint_of_node(np, epn) { - struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 }; - - ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn), - &v4l2_epn); - if (ret) { - ret = -EINVAL; - dev_err(dev, "Could not parse the endpoint\n"); - break; - } - - subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity), - GFP_KERNEL); - if (!subdev_entity) { - ret = -ENOMEM; - break; - } - subdev_entity->epn = epn; - - flags = v4l2_epn.bus.parallel.flags; - - if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) - subdev_entity->pfe_cfg0 = ISC_PFE_CFG0_HPOL_LOW; - - if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) - subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_VPOL_LOW; - - if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) - subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_PPOL_LOW; - - if (v4l2_epn.bus_type == V4L2_MBUS_BT656) - subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_CCIR_CRC | - ISC_PFE_CFG0_CCIR656; - - if (mipi_mode) - subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_MIPI; - - list_add_tail(&subdev_entity->list, &isc->subdev_entities); - } - of_node_put(epn); - - return ret; -} - -static int microchip_xisc_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct isc_device *isc; - void __iomem *io_base; - struct isc_subdev_entity *subdev_entity; - int irq; - int ret; - u32 ver; - - isc = devm_kzalloc(dev, sizeof(*isc), GFP_KERNEL); - if (!isc) - return -ENOMEM; - - platform_set_drvdata(pdev, isc); - isc->dev = dev; - - io_base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(io_base)) - return PTR_ERR(io_base); - - isc->regmap = devm_regmap_init_mmio(dev, io_base, &atmel_isc_regmap_config); - if (IS_ERR(isc->regmap)) { - ret = PTR_ERR(isc->regmap); - dev_err(dev, "failed to init register map: %d\n", ret); - return ret; - } - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - ret = devm_request_irq(dev, irq, atmel_isc_interrupt, 0, - "microchip-sama7g5-xisc", isc); - if (ret < 0) { - dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n", - irq, ret); - return ret; - } - - isc->gamma_table = isc_sama7g5_gamma_table; - isc->gamma_max = 0; - - isc->max_width = ISC_SAMA7G5_MAX_SUPPORT_WIDTH; - isc->max_height = ISC_SAMA7G5_MAX_SUPPORT_HEIGHT; - - isc->config_dpc = isc_sama7g5_config_dpc; - isc->config_csc = isc_sama7g5_config_csc; - isc->config_cbc = isc_sama7g5_config_cbc; - isc->config_cc = isc_sama7g5_config_cc; - isc->config_gam = isc_sama7g5_config_gam; - isc->config_rlp = isc_sama7g5_config_rlp; - isc->config_ctrls = isc_sama7g5_config_ctrls; - - isc->adapt_pipeline = isc_sama7g5_adapt_pipeline; - - isc->offsets.csc = ISC_SAMA7G5_CSC_OFFSET; - isc->offsets.cbc = ISC_SAMA7G5_CBC_OFFSET; - isc->offsets.sub422 = ISC_SAMA7G5_SUB422_OFFSET; - isc->offsets.sub420 = ISC_SAMA7G5_SUB420_OFFSET; - isc->offsets.rlp = ISC_SAMA7G5_RLP_OFFSET; - isc->offsets.his = ISC_SAMA7G5_HIS_OFFSET; - isc->offsets.dma = ISC_SAMA7G5_DMA_OFFSET; - isc->offsets.version = ISC_SAMA7G5_VERSION_OFFSET; - isc->offsets.his_entry = ISC_SAMA7G5_HIS_ENTRY_OFFSET; - - isc->controller_formats = sama7g5_controller_formats; - isc->controller_formats_size = ARRAY_SIZE(sama7g5_controller_formats); - isc->formats_list = sama7g5_formats_list; - isc->formats_list_size = ARRAY_SIZE(sama7g5_formats_list); - - /* sama7g5-isc RAM access port is full AXI4 - 32 bits per beat */ - isc->dcfg = ISC_DCFG_YMBSIZE_BEATS32 | ISC_DCFG_CMBSIZE_BEATS32; - - /* sama7g5-isc : ISPCK does not exist, ISC is clocked by MCK */ - isc->ispck_required = false; - - ret = atmel_isc_pipeline_init(isc); - if (ret) - return ret; - - isc->hclock = devm_clk_get(dev, "hclock"); - if (IS_ERR(isc->hclock)) { - ret = PTR_ERR(isc->hclock); - dev_err(dev, "failed to get hclock: %d\n", ret); - return ret; - } - - ret = clk_prepare_enable(isc->hclock); - if (ret) { - dev_err(dev, "failed to enable hclock: %d\n", ret); - return ret; - } - - ret = atmel_isc_clk_init(isc); - if (ret) { - dev_err(dev, "failed to init isc clock: %d\n", ret); - goto unprepare_hclk; - } - - ret = v4l2_device_register(dev, &isc->v4l2_dev); - if (ret) { - dev_err(dev, "unable to register v4l2 device.\n"); - goto unprepare_hclk; - } - - ret = xisc_parse_dt(dev, isc); - if (ret) { - dev_err(dev, "fail to parse device tree\n"); - goto unregister_v4l2_device; - } - - if (list_empty(&isc->subdev_entities)) { - dev_err(dev, "no subdev found\n"); - ret = -ENODEV; - goto unregister_v4l2_device; - } - - list_for_each_entry(subdev_entity, &isc->subdev_entities, list) { - struct v4l2_async_connection *asd; - struct fwnode_handle *fwnode = - of_fwnode_handle(subdev_entity->epn); - - v4l2_async_nf_init(&subdev_entity->notifier, &isc->v4l2_dev); - - asd = v4l2_async_nf_add_fwnode_remote(&subdev_entity->notifier, - fwnode, - struct v4l2_async_connection); - - of_node_put(subdev_entity->epn); - subdev_entity->epn = NULL; - - if (IS_ERR(asd)) { - ret = PTR_ERR(asd); - goto cleanup_subdev; - } - - subdev_entity->notifier.ops = &atmel_isc_async_ops; - - ret = v4l2_async_nf_register(&subdev_entity->notifier); - if (ret) { - dev_err(dev, "fail to register async notifier\n"); - goto cleanup_subdev; - } - - if (video_is_registered(&isc->video_dev)) - break; - } - - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - pm_request_idle(dev); - - regmap_read(isc->regmap, ISC_VERSION + isc->offsets.version, &ver); - dev_info(dev, "Microchip XISC version %x\n", ver); - - return 0; - -cleanup_subdev: - atmel_isc_subdev_cleanup(isc); - -unregister_v4l2_device: - v4l2_device_unregister(&isc->v4l2_dev); - -unprepare_hclk: - clk_disable_unprepare(isc->hclock); - - atmel_isc_clk_cleanup(isc); - - return ret; -} - -static void microchip_xisc_remove(struct platform_device *pdev) -{ - struct isc_device *isc = platform_get_drvdata(pdev); - - pm_runtime_disable(&pdev->dev); - - atmel_isc_subdev_cleanup(isc); - - v4l2_device_unregister(&isc->v4l2_dev); - - clk_disable_unprepare(isc->hclock); - - atmel_isc_clk_cleanup(isc); -} - -static int __maybe_unused xisc_runtime_suspend(struct device *dev) -{ - struct isc_device *isc = dev_get_drvdata(dev); - - clk_disable_unprepare(isc->hclock); - - return 0; -} - -static int __maybe_unused xisc_runtime_resume(struct device *dev) -{ - struct isc_device *isc = dev_get_drvdata(dev); - int ret; - - ret = clk_prepare_enable(isc->hclock); - if (ret) - return ret; - - return ret; -} - -static const struct dev_pm_ops microchip_xisc_dev_pm_ops = { - SET_RUNTIME_PM_OPS(xisc_runtime_suspend, xisc_runtime_resume, NULL) -}; - -#if IS_ENABLED(CONFIG_OF) -static const struct of_device_id microchip_xisc_of_match[] = { - { .compatible = "microchip,sama7g5-isc" }, - { } -}; -MODULE_DEVICE_TABLE(of, microchip_xisc_of_match); -#endif - -static struct platform_driver microchip_xisc_driver = { - .probe = microchip_xisc_probe, - .remove = microchip_xisc_remove, - .driver = { - .name = "microchip-sama7g5-xisc", - .pm = µchip_xisc_dev_pm_ops, - .of_match_table = of_match_ptr(microchip_xisc_of_match), - }, -}; - -module_platform_driver(microchip_xisc_driver); - -MODULE_AUTHOR("Eugen Hristev <eugen.hristev@microchip.com>"); -MODULE_DESCRIPTION("The V4L2 driver for Microchip-XISC"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index e9cef7af000a..bfd71d25facc 100644 --- a/drivers/staging/media/imx/imx-media-capture.c +++ b/drivers/staging/media/imx/imx-media-capture.c @@ -722,8 +722,8 @@ static int capture_start_streaming(struct vb2_queue *vq, unsigned int count) goto return_bufs; } - ret = imx_media_pipeline_set_stream(priv->md, &priv->src_sd->entity, - true); + ret = imx_media_pipeline_set_stream(priv->md, &priv->vdev, + &priv->src_sd->entity, true); if (ret) { dev_err(priv->dev, "pipeline start failed with %d\n", ret); goto return_bufs; @@ -749,8 +749,8 @@ static void capture_stop_streaming(struct vb2_queue *vq) unsigned long flags; int ret; - ret = imx_media_pipeline_set_stream(priv->md, &priv->src_sd->entity, - false); + ret = imx_media_pipeline_set_stream(priv->md, &priv->vdev, + &priv->src_sd->entity, false); if (ret) dev_warn(priv->dev, "pipeline stop failed with %d\n", ret); diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c index 1b5af8945e6b..f119477cac6b 100644 --- a/drivers/staging/media/imx/imx-media-utils.c +++ b/drivers/staging/media/imx/imx-media-utils.c @@ -589,10 +589,8 @@ int imx_media_alloc_dma_buf(struct device *dev, buf->len = PAGE_ALIGN(size); buf->virt = dma_alloc_coherent(dev, buf->len, &buf->phys, GFP_DMA | GFP_KERNEL); - if (!buf->virt) { - dev_err(dev, "%s: failed\n", __func__); + if (!buf->virt) return -ENOMEM; - } return 0; } @@ -749,10 +747,12 @@ EXPORT_SYMBOL_GPL(imx_media_pipeline_subdev); * Turn current pipeline streaming on/off starting from entity. */ int imx_media_pipeline_set_stream(struct imx_media_dev *imxmd, + struct imx_media_video_dev *vdev, struct media_entity *entity, bool on) { struct v4l2_subdev *sd; + struct media_pad *pad; int ret = 0; if (!is_media_entity_v4l2_subdev(entity)) @@ -761,17 +761,19 @@ int imx_media_pipeline_set_stream(struct imx_media_dev *imxmd, mutex_lock(&imxmd->md.graph_mutex); + pad = &entity->pads[0]; + if (on) { - ret = __media_pipeline_start(entity->pads, &imxmd->pipe); + ret = __media_pipeline_start(pad, &vdev->pipe); if (ret) goto out; ret = v4l2_subdev_call(sd, video, s_stream, 1); if (ret) - __media_pipeline_stop(entity->pads); + __media_pipeline_stop(pad); } else { v4l2_subdev_call(sd, video, s_stream, 0); - if (media_pad_pipeline(entity->pads)) - __media_pipeline_stop(entity->pads); + if (media_pad_is_streaming(pad)) + __media_pipeline_stop(pad); } out: diff --git a/drivers/staging/media/imx/imx-media.h b/drivers/staging/media/imx/imx-media.h index 135daca7d55b..537558a7bece 100644 --- a/drivers/staging/media/imx/imx-media.h +++ b/drivers/staging/media/imx/imx-media.h @@ -104,6 +104,9 @@ struct imx_media_buffer { struct imx_media_video_dev { struct video_device *vfd; + /* the pipeline object */ + struct media_pipeline pipe; + /* the user format */ struct v4l2_pix_format fmt; /* the compose rectangle */ @@ -145,9 +148,6 @@ struct imx_media_dev { struct media_device md; struct v4l2_device v4l2_dev; - /* the pipeline object */ - struct media_pipeline pipe; - struct mutex mutex; /* protect elements below */ /* master video device list */ @@ -223,6 +223,7 @@ int imx_media_alloc_dma_buf(struct device *dev, int size); int imx_media_pipeline_set_stream(struct imx_media_dev *imxmd, + struct imx_media_video_dev *vdev, struct media_entity *entity, bool on); diff --git a/drivers/staging/media/ipu3/ipu3-css-params.c b/drivers/staging/media/ipu3/ipu3-css-params.c index 2c48d57a3180..92cce31e35c5 100644 --- a/drivers/staging/media/ipu3/ipu3-css-params.c +++ b/drivers/staging/media/ipu3/ipu3-css-params.c @@ -1770,6 +1770,8 @@ static int imgu_css_cfg_acc_stripe(struct imgu_css *css, unsigned int pipe, acc->stripe.bds_out_stripes[0].width = ALIGN(css_pipe->rect[IPU3_CSS_RECT_BDS].width, f); } else { + u32 offset; + /* Image processing is divided into two stripes */ acc->stripe.bds_out_stripes[0].width = acc->stripe.bds_out_stripes[1].width = @@ -1788,8 +1790,10 @@ static int imgu_css_cfg_acc_stripe(struct imgu_css *css, unsigned int pipe, acc->stripe.bds_out_stripes[1].width += f; } /* Overlap between stripes is IPU3_UAPI_ISP_VEC_ELEMS * 4 */ - acc->stripe.bds_out_stripes[1].offset = - acc->stripe.bds_out_stripes[0].width - 2 * f; + offset = acc->stripe.bds_out_stripes[0].width - 2 * f; + if (offset > 65535) + return -EINVAL; + acc->stripe.bds_out_stripes[1].offset = offset; } acc->stripe.effective_stripes[0].height = diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h index a1519c4fe661..56b90aab83ea 100644 --- a/drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h +++ b/drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h @@ -42,7 +42,7 @@ struct ia_gofo_logger_config { ((IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_IS_OFFSET) + \ (u32)(IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP)) #define IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_PRIMARY_OFFSET (0U) -#define IA_GOFO_CCG_IPU_BUTTRESS_FW_BOOT_PARAMS_SECONDARY_OFFSET (0x3000U / 4U) +#define IA_GOFO_CCG_IPU_BUTTRESS_FW_BOOT_PARAMS_SECONDARY_OFFSET (0x3000 / 4U) #define IA_GOFO_HKR_IPU_BUTTRESS_FW_BOOT_PARAMS_SECONDARY_OFFSET \ (IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP * 2U) #define IA_GOFO_HKR_HIF_BUTTRESS_FW_BOOT_PARAMS_SECONDARY_OFFSET \ @@ -75,7 +75,7 @@ enum ia_gofo_boot_uc_tile_frequency_units { }; #define IA_GOFO_FW_BOOT_STATE_IS_CRITICAL(boot_state) \ - (0xdead0000U == ((boot_state) & 0xffff0000U)) + (0xdead0000 == ((boot_state) & 0xffff0000)) struct ia_gofo_boot_config { u32 length; @@ -104,35 +104,35 @@ struct ia_gofo_secondary_boot_config { #pragma pack(pop) -#define IA_GOFO_WDT_TIMEOUT_ERR 0xdead0401U -#define IA_GOFO_MEM_FATAL_DME_ERR 0xdead0801U -#define IA_GOFO_MEM_UNCORRECTABLE_LOCAL_ERR 0xdead0802U -#define IA_GOFO_MEM_UNCORRECTABLE_DIRTY_ERR 0xdead0803U -#define IA_GOFO_MEM_UNCORRECTABLE_DTAG_ERR 0xdead0804U -#define IA_GOFO_MEM_UNCORRECTABLE_CACHE_ERR 0xdead0805U -#define IA_GOFO_DOUBLE_EXCEPTION_ERR 0xdead0806U -#define IA_GOFO_BIST_DMEM_FAULT_DETECTION_ERR 0xdead1000U -#define IA_GOFO_BIST_DATA_INTEGRITY_FAILURE 0xdead1010U +#define IA_GOFO_WDT_TIMEOUT_ERR 0xdead0401 +#define IA_GOFO_MEM_FATAL_DME_ERR 0xdead0801 +#define IA_GOFO_MEM_UNCORRECTABLE_LOCAL_ERR 0xdead0802 +#define IA_GOFO_MEM_UNCORRECTABLE_DIRTY_ERR 0xdead0803 +#define IA_GOFO_MEM_UNCORRECTABLE_DTAG_ERR 0xdead0804 +#define IA_GOFO_MEM_UNCORRECTABLE_CACHE_ERR 0xdead0805 +#define IA_GOFO_DOUBLE_EXCEPTION_ERR 0xdead0806 +#define IA_GOFO_BIST_DMEM_FAULT_DETECTION_ERR 0xdead1000 +#define IA_GOFO_BIST_DATA_INTEGRITY_FAILURE 0xdead1010 enum ia_gofo_boot_state { - IA_GOFO_FW_BOOT_STATE_SECONDARY_BOOT_CONFIG_READY = 0x57a7b000U, - IA_GOFO_FW_BOOT_STATE_UNINIT = 0x57a7e000U, - IA_GOFO_FW_BOOT_STATE_STARTING_0 = 0x57a7d000U, - IA_GOFO_FW_BOOT_STATE_CACHE_INIT_DONE = 0x57a7d010U, - IA_GOFO_FW_BOOT_STATE_MEM_INIT_DONE = 0x57a7d020U, - IA_GOFO_FW_BOOT_STATE_STACK_INIT_DONE = 0x57a7d030U, - IA_GOFO_FW_BOOT_STATE_EARLY_BOOT_DONE = 0x57a7d100U, - IA_GOFO_FW_BOOT_STATE_BOOT_CONFIG_START = 0x57a7d200U, - IA_GOFO_FW_BOOT_STATE_QUEUE_INIT_DONE = 0x57a7d300U, - IA_GOFO_FW_BOOT_STATE_READY = 0x57a7e100U, - IA_GOFO_FW_BOOT_STATE_CRIT_UNSPECIFIED = 0xdead0001U, - IA_GOFO_FW_BOOT_STATE_CRIT_CFG_PTR = 0xdead0101U, - IA_GOFO_FW_BOOT_STATE_CRIT_CFG_VERSION = 0xdead0201U, - IA_GOFO_FW_BOOT_STATE_CRIT_MSG_VERSION = 0xdead0301U, + IA_GOFO_FW_BOOT_STATE_SECONDARY_BOOT_CONFIG_READY = 0x57a7b000, + IA_GOFO_FW_BOOT_STATE_UNINIT = 0x57a7e000, + IA_GOFO_FW_BOOT_STATE_STARTING_0 = 0x57a7d000, + IA_GOFO_FW_BOOT_STATE_CACHE_INIT_DONE = 0x57a7d010, + IA_GOFO_FW_BOOT_STATE_MEM_INIT_DONE = 0x57a7d020, + IA_GOFO_FW_BOOT_STATE_STACK_INIT_DONE = 0x57a7d030, + IA_GOFO_FW_BOOT_STATE_EARLY_BOOT_DONE = 0x57a7d100, + IA_GOFO_FW_BOOT_STATE_BOOT_CONFIG_START = 0x57a7d200, + IA_GOFO_FW_BOOT_STATE_QUEUE_INIT_DONE = 0x57a7d300, + IA_GOFO_FW_BOOT_STATE_READY = 0x57a7e100, + IA_GOFO_FW_BOOT_STATE_CRIT_UNSPECIFIED = 0xdead0001, + IA_GOFO_FW_BOOT_STATE_CRIT_CFG_PTR = 0xdead0101, + IA_GOFO_FW_BOOT_STATE_CRIT_CFG_VERSION = 0xdead0201, + IA_GOFO_FW_BOOT_STATE_CRIT_MSG_VERSION = 0xdead0301, IA_GOFO_FW_BOOT_STATE_CRIT_WDT_TIMEOUT = IA_GOFO_WDT_TIMEOUT_ERR, - IA_GOFO_FW_BOOT_STATE_WRONG_DATA_SECTION_UNPACKING = 0xdead0501U, - IA_GOFO_FW_BOOT_STATE_WRONG_RO_DATA_SECTION_UNPACKING = 0xdead0601U, - IA_GOFO_FW_BOOT_STATE_INVALID_UNTRUSTED_ADDR_MIN = 0xdead0701U, + IA_GOFO_FW_BOOT_STATE_WRONG_DATA_SECTION_UNPACKING = 0xdead0501, + IA_GOFO_FW_BOOT_STATE_WRONG_RO_DATA_SECTION_UNPACKING = 0xdead0601, + IA_GOFO_FW_BOOT_STATE_INVALID_UNTRUSTED_ADDR_MIN = 0xdead0701, IA_GOFO_FW_BOOT_STATE_CRIT_MEM_FATAL_DME = IA_GOFO_MEM_FATAL_DME_ERR, IA_GOFO_FW_BOOT_STATE_CRIT_MEM_UNCORRECTABLE_LOCAL = IA_GOFO_MEM_UNCORRECTABLE_LOCAL_ERR, @@ -146,18 +146,18 @@ enum ia_gofo_boot_state { IA_GOFO_DOUBLE_EXCEPTION_ERR, IA_GOFO_FW_BOOT_STATE_CRIT_BIST_DMEM_FAULT_DETECTION_ERR = IA_GOFO_BIST_DMEM_FAULT_DETECTION_ERR, - IA_GOFO_FW_BOOT_STATE_CRIT_DATA_INTEGRITY_FAILURE = 0xdead1010U, - IA_GOFO_FW_BOOT_STATE_CRIT_STACK_CHK_FAILURE = 0xdead1011U, + IA_GOFO_FW_BOOT_STATE_CRIT_DATA_INTEGRITY_FAILURE = 0xdead1010, + IA_GOFO_FW_BOOT_STATE_CRIT_STACK_CHK_FAILURE = 0xdead1011, IA_GOFO_FW_BOOT_STATE_CRIT_SYSCOM_CONTEXT_INTEGRITY_FAILURE = - 0xdead1012U, - IA_GOFO_FW_BOOT_STATE_CRIT_MPU_CONFIG_FAILURE = 0xdead1013U, - IA_GOFO_FW_BOOT_STATE_CRIT_SHARED_BUFFER_FAILURE = 0xdead1014U, - IA_GOFO_FW_BOOT_STATE_CRIT_CMEM_FAILURE = 0xdead1015U, - IA_GOFO_FW_BOOT_STATE_SHUTDOWN_CMD = 0x57a7f001U, - IA_GOFO_FW_BOOT_STATE_SHUTDOWN_START = 0x57a7e200U, - IA_GOFO_FW_BOOT_STATE_INACTIVE = 0x57a7e300U, - IA_GOFO_FW_BOOT_HW_CMD_ACK_TIMEOUT = 0x57a7e400U, - IA_GOFO_FW_BOOT_SYSTEM_CYCLES_ERROR = 0x57a7e500U + 0xdead1012, + IA_GOFO_FW_BOOT_STATE_CRIT_MPU_CONFIG_FAILURE = 0xdead1013, + IA_GOFO_FW_BOOT_STATE_CRIT_SHARED_BUFFER_FAILURE = 0xdead1014, + IA_GOFO_FW_BOOT_STATE_CRIT_CMEM_FAILURE = 0xdead1015, + IA_GOFO_FW_BOOT_STATE_SHUTDOWN_CMD = 0x57a7f001, + IA_GOFO_FW_BOOT_STATE_SHUTDOWN_START = 0x57a7e200, + IA_GOFO_FW_BOOT_STATE_INACTIVE = 0x57a7e300, + IA_GOFO_FW_BOOT_HW_CMD_ACK_TIMEOUT = 0x57a7e400, + IA_GOFO_FW_BOOT_SYSTEM_CYCLES_ERROR = 0x57a7e500 }; #endif diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_common_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_common_abi.h index 7bb6fac585a3..398a13350480 100644 --- a/drivers/staging/media/ipu7/abi/ipu7_fw_common_abi.h +++ b/drivers/staging/media/ipu7/abi/ipu7_fw_common_abi.h @@ -60,7 +60,7 @@ struct ia_gofo_tlv_list { #define IA_GOFO_MSG_ERR_MAX_DETAILS (4U) #define IA_GOFO_MSG_ERR_OK (0U) -#define IA_GOFO_MSG_ERR_UNSPECIFED (0xffffffffU) +#define IA_GOFO_MSG_ERR_UNSPECIFED (0xffffffff) #define IA_GOFO_MSG_ERR_GROUP_UNSPECIFIED (0U) #define IA_GOFO_MSG_ERR_IS_OK(err) (IA_GOFO_MSG_ERR_OK == (err).err_code) @@ -145,7 +145,7 @@ struct ia_gofo_msg_indirect { #define IA_GOFO_MSG_LOG_DOC_FMT_ID_MIN (0U) #define IA_GOFO_MSG_LOG_DOC_FMT_ID_MAX (4095U) -#define IA_GOFO_MSG_LOG_FMT_ID_INVALID (0xfffffffU) +#define IA_GOFO_MSG_LOG_FMT_ID_INVALID (0xfffffff) struct ia_gofo_msg_log_info { u16 log_counter; diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h index 8a78dd0936df..311248385993 100644 --- a/drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h +++ b/drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h @@ -69,11 +69,11 @@ struct ipu7_msg_cb_profile { #define IPU_MSG_NODE_MAX_PROFILES (2U) #define IPU_MSG_NODE_DEF_PROFILE_IDX (0U) -#define IPU_MSG_NODE_RSRC_ID_EXT_IP (0xffU) +#define IPU_MSG_NODE_RSRC_ID_EXT_IP (0xff) -#define IPU_MSG_NODE_DONT_CARE_TEB_HI (0xffffffffU) -#define IPU_MSG_NODE_DONT_CARE_TEB_LO (0xffffffffU) -#define IPU_MSG_NODE_RSRC_ID_IS (0xfeU) +#define IPU_MSG_NODE_DONT_CARE_TEB_HI (0xffffffff) +#define IPU_MSG_NODE_DONT_CARE_TEB_LO (0xffffffff) +#define IPU_MSG_NODE_RSRC_ID_IS (0xfe) struct ipu7_msg_node { struct ia_gofo_tlv_header tlv_header; @@ -160,7 +160,7 @@ struct ipu7_msg_link_ep_pair { #define IPU_MSG_LINK_FOREIGN_KEY_MAX (64U) #define IPU_MSG_LINK_PBK_ID_DONT_CARE (255U) #define IPU_MSG_LINK_PBK_SLOT_ID_DONT_CARE (255U) -#define IPU_MSG_LINK_TERM_ID_DONT_CARE (0xffU) +#define IPU_MSG_LINK_TERM_ID_DONT_CARE (0xff) struct ipu7_msg_link { struct ia_gofo_tlv_header tlv_header; @@ -333,7 +333,7 @@ enum ipu7_msg_err_device { #pragma pack(pop) #pragma pack(push, 1) -#define IPU_MSG_GRAPH_ID_UNKNOWN (0xffU) +#define IPU_MSG_GRAPH_ID_UNKNOWN (0xff) #define IPU_MSG_GRAPH_SEND_MSG_ENABLED 1U #define IPU_MSG_GRAPH_SEND_MSG_DISABLED 0U diff --git a/drivers/staging/media/ipu7/ipu7-buttress-regs.h b/drivers/staging/media/ipu7/ipu7-buttress-regs.h index 3eafd6a3813d..7b646aa538cf 100644 --- a/drivers/staging/media/ipu7/ipu7-buttress-regs.h +++ b/drivers/staging/media/ipu7/ipu7-buttress-regs.h @@ -287,7 +287,7 @@ #define BUTTRESS_TSC_CMD_START_TSC_SYNC BIT(0) #define BUTTRESS_PWR_STATUS_HH_STATUS_SHIFT 11 -#define BUTTRESS_PWR_STATUS_HH_STATUS_MASK (0x3U << 11) +#define BUTTRESS_PWR_STATUS_HH_STATUS_MASK (0x3 << 11) #define BUTTRESS_TSW_WA_SOFT_RESET BIT(8) /* new for PTL */ #define BUTTRESS_SEL_PB_TIMESTAMP BIT(9) @@ -326,8 +326,8 @@ #define BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK 0xffff /* IS/PS freq control */ -#define BUTTRESS_IS_FREQ_CTL_RATIO_MASK 0xffU -#define BUTTRESS_PS_FREQ_CTL_RATIO_MASK 0xffU +#define BUTTRESS_IS_FREQ_CTL_RATIO_MASK 0xff +#define BUTTRESS_PS_FREQ_CTL_RATIO_MASK 0xff #define IPU7_IS_FREQ_MAX 450 #define IPU7_IS_FREQ_MIN 50 @@ -350,11 +350,11 @@ /* buttree power status */ #define IPU_BUTTRESS_PWR_STATE_IS_PWR_SHIFT 0 #define IPU_BUTTRESS_PWR_STATE_IS_PWR_MASK \ - (0x3U << IPU_BUTTRESS_PWR_STATE_IS_PWR_SHIFT) + (0x3 << IPU_BUTTRESS_PWR_STATE_IS_PWR_SHIFT) #define IPU_BUTTRESS_PWR_STATE_PS_PWR_SHIFT 4 #define IPU_BUTTRESS_PWR_STATE_PS_PWR_MASK \ - (0x3U << IPU_BUTTRESS_PWR_STATE_PS_PWR_SHIFT) + (0x3 << IPU_BUTTRESS_PWR_STATE_PS_PWR_SHIFT) #define IPU_BUTTRESS_PWR_STATE_DN_DONE 0x0 #define IPU_BUTTRESS_PWR_STATE_UP_PROCESS 0x1 diff --git a/drivers/staging/media/ipu7/ipu7.c b/drivers/staging/media/ipu7/ipu7.c index c771e763f8c5..310e3f24e571 100644 --- a/drivers/staging/media/ipu7/ipu7.c +++ b/drivers/staging/media/ipu7/ipu7.c @@ -2169,21 +2169,18 @@ ipu7_isys_init(struct pci_dev *pdev, struct device *parent, isys_adev->mmu = ipu7_mmu_init(dev, base, ISYS_MMID, &ipdata->hw_variant); if (IS_ERR(isys_adev->mmu)) { - dev_err_probe(dev, PTR_ERR(isys_adev->mmu), - "ipu7_mmu_init(isys_adev->mmu) failed\n"); + ret = dev_err_probe(dev, PTR_ERR(isys_adev->mmu), + "ipu7_mmu_init(isys_adev->mmu) failed\n"); put_device(&isys_adev->auxdev.dev); - kfree(pdata); - return ERR_CAST(isys_adev->mmu); + return ERR_PTR(ret); } isys_adev->mmu->dev = &isys_adev->auxdev.dev; isys_adev->subsys = IPU_IS; ret = ipu7_bus_add_device(isys_adev); - if (ret) { - kfree(pdata); + if (ret) return ERR_PTR(ret); - } return isys_adev; } @@ -2216,21 +2213,18 @@ ipu7_psys_init(struct pci_dev *pdev, struct device *parent, psys_adev->mmu = ipu7_mmu_init(&pdev->dev, base, PSYS_MMID, &ipdata->hw_variant); if (IS_ERR(psys_adev->mmu)) { - dev_err_probe(&pdev->dev, PTR_ERR(psys_adev->mmu), - "ipu7_mmu_init(psys_adev->mmu) failed\n"); + ret = dev_err_probe(&pdev->dev, PTR_ERR(psys_adev->mmu), + "ipu7_mmu_init(psys_adev->mmu) failed\n"); put_device(&psys_adev->auxdev.dev); - kfree(pdata); - return ERR_CAST(psys_adev->mmu); + return ERR_PTR(ret); } psys_adev->mmu->dev = &psys_adev->auxdev.dev; psys_adev->subsys = IPU_PS; ret = ipu7_bus_add_device(psys_adev); - if (ret) { - kfree(pdata); + if (ret) return ERR_PTR(ret); - } return psys_adev; } diff --git a/drivers/staging/media/meson/vdec/codec_h264.c b/drivers/staging/media/meson/vdec/codec_h264.c index 89e0f8624e5b..a6074de15118 100644 --- a/drivers/staging/media/meson/vdec/codec_h264.c +++ b/drivers/staging/media/meson/vdec/codec_h264.c @@ -16,7 +16,7 @@ #define SIZE_SEI (8 * SZ_1K) /* - * Offset added by the firmware which must be substracted + * Offset added by the firmware which must be subtracted * from the workspace phyaddr */ #define WORKSPACE_BUF_OFFSET 0x1000000 diff --git a/drivers/staging/media/meson/vdec/codec_mpeg12.c b/drivers/staging/media/meson/vdec/codec_mpeg12.c index 76e9ca7191ab..ab4374e3b2ef 100644 --- a/drivers/staging/media/meson/vdec/codec_mpeg12.c +++ b/drivers/staging/media/meson/vdec/codec_mpeg12.c @@ -12,7 +12,7 @@ #include "vdec_helpers.h" #define SIZE_WORKSPACE SZ_128K -/* Offset substracted by the firmware from the workspace paddr */ +/* Offset subtracted by the firmware from the workspace paddr */ #define WORKSPACE_OFFSET (5 * SZ_1K) /* map firmware registers to known MPEG1/2 functions */ diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c index 4b77ec1af5a7..a039d925c0fe 100644 --- a/drivers/staging/media/meson/vdec/vdec.c +++ b/drivers/staging/media/meson/vdec/vdec.c @@ -889,7 +889,7 @@ static int vdec_open(struct file *file) ret = vdec_init_ctrls(sess); if (ret) - goto err_m2m_release; + goto err_m2m_ctx_release; sess->pixfmt_cap = formats[0].pixfmts_cap[0]; sess->fmt_out = &formats[0]; @@ -913,6 +913,8 @@ static int vdec_open(struct file *file) return 0; +err_m2m_ctx_release: + v4l2_m2m_ctx_release(sess->m2m_ctx); err_m2m_release: v4l2_m2m_release(sess->m2m_dev); err_free_sess: diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c index 6600245dff0e..bbd186b8035b 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus.c @@ -391,6 +391,7 @@ static int cedrus_open(struct file *file) err_m2m_release: v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); err_free: + v4l2_fh_exit(&ctx->fh); kfree(ctx); mutex_unlock(&dev->dev_mutex); @@ -476,7 +477,7 @@ static int cedrus_probe(struct platform_device *pdev) ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); if (ret) { dev_err(&pdev->dev, "Failed to register V4L2 device\n"); - return ret; + goto err_hw; } vfd = &dev->vfd; @@ -507,7 +508,7 @@ static int cedrus_probe(struct platform_device *pdev) ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); if (ret) { v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); - goto err_m2m; + goto err_media; } v4l2_info(&dev->v4l2_dev, @@ -533,10 +534,13 @@ err_m2m_mc: v4l2_m2m_unregister_media_controller(dev->m2m_dev); err_video: video_unregister_device(&dev->vfd); -err_m2m: +err_media: + media_device_cleanup(&dev->mdev); v4l2_m2m_release(dev->m2m_dev); err_v4l2: v4l2_device_unregister(&dev->v4l2_dev); +err_hw: + cedrus_hw_remove(dev); return ret; } diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c index 3e2843ef6cce..fc54d993b11f 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c @@ -210,6 +210,9 @@ static void _cedrus_write_ref_list(struct cedrus_ctx *ctx, u8 dpb_idx; dpb_idx = ref_list[i].index; + if (dpb_idx >= V4L2_H264_NUM_DPB_ENTRIES) + continue; + dpb = &decode->dpb[dpb_idx]; if (!(dpb->flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) diff --git a/drivers/staging/media/tegra-video/tegra210.c b/drivers/staging/media/tegra-video/tegra210.c index da99f19a39e7..a6606768fa03 100644 --- a/drivers/staging/media/tegra-video/tegra210.c +++ b/drivers/staging/media/tegra-video/tegra210.c @@ -362,8 +362,7 @@ dequeue_buf_done(struct tegra_vi_channel *chan) buf = list_first_entry(&chan->done, struct tegra_channel_buffer, queue); - if (buf) - list_del_init(&buf->queue); + list_del_init(&buf->queue); spin_unlock(&chan->done_lock); return buf; diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c index f14cdc7b5211..456134a9e8cf 100644 --- a/drivers/staging/media/tegra-video/vi.c +++ b/drivers/staging/media/tegra-video/vi.c @@ -80,8 +80,8 @@ static int tegra_get_format_idx_by_code(struct tegra_vi *vi, static u32 tegra_get_format_fourcc_by_idx(struct tegra_vi *vi, unsigned int index) { - if (index >= vi->soc->nformats) - return -EINVAL; + if (WARN_ON_ONCE(index >= vi->soc->nformats)) + return vi->soc->video_formats[0].fourcc; return vi->soc->video_formats[index].fourcc; } diff --git a/include/linux/usb/uvc.h b/include/linux/usb/uvc.h index 05bfebab42b6..99b070ab860f 100644 --- a/include/linux/usb/uvc.h +++ b/include/linux/usb/uvc.h @@ -43,6 +43,16 @@ #define UVC_GUID_MSXU_1_5 \ {0xdc, 0x95, 0x3f, 0x0f, 0x32, 0x26, 0x4e, 0x4c, \ 0x92, 0xc9, 0xa0, 0x47, 0x82, 0xf4, 0x3b, 0xc8} +#define UVC_GUID_LOGITECH_MOTOR_CONTROL_V1 \ + {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \ + 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x56 } +#define UVC_GUID_LOGITECH_PERIPHERAL \ + {0x21, 0x2d, 0xe5, 0xff, 0x30, 0x80, 0x2c, 0x4e, \ + 0x82, 0xd9, 0xf5, 0x87, 0xd0, 0x05, 0x40, 0xbd } +#define UVC_GUID_LOGITECH_USER_HW_CONTROL_V1 \ + {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \ + 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x1f } + /* https://learn.microsoft.com/en-us/windows-hardware/drivers/stream/uvc-extensions-1-5#222-extension-unit-controls */ #define UVC_MSXU_CONTROL_FOCUS 0x01 diff --git a/include/media/i2c/lm3560.h b/include/media/i2c/lm3560.h deleted file mode 100644 index 770d8c72c94a..000000000000 --- a/include/media/i2c/lm3560.h +++ /dev/null @@ -1,84 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * include/media/i2c/lm3560.h - * - * Copyright (C) 2013 Texas Instruments - * - * Contact: Daniel Jeong <gshark.jeong@gmail.com> - * Ldd-Mlp <ldd-mlp@list.ti.com> - */ - -#ifndef __LM3560_H__ -#define __LM3560_H__ - -#include <media/v4l2-subdev.h> - -#define LM3559_NAME "lm3559" -#define LM3560_NAME "lm3560" -#define LM3560_I2C_ADDR (0x53) - -/* FLASH Brightness - * min 62500uA, step 62500uA, max 1000000uA - */ -#define LM3560_FLASH_BRT_MIN 62500 -#define LM3560_FLASH_BRT_STEP 62500 -#define LM3560_FLASH_BRT_MAX 1000000 -#define LM3560_FLASH_BRT_uA_TO_REG(a) \ - ((a) < LM3560_FLASH_BRT_MIN ? 0 : \ - (((a) - LM3560_FLASH_BRT_MIN) / LM3560_FLASH_BRT_STEP)) -#define LM3560_FLASH_BRT_REG_TO_uA(a) \ - ((a) * LM3560_FLASH_BRT_STEP + LM3560_FLASH_BRT_MIN) - -/* FLASH TIMEOUT DURATION - * min 32ms, step 32ms, max 1024ms - */ -#define LM3560_FLASH_TOUT_MIN 32 -#define LM3560_FLASH_TOUT_STEP 32 -#define LM3560_FLASH_TOUT_MAX 1024 -#define LM3560_FLASH_TOUT_ms_TO_REG(a) \ - ((a) < LM3560_FLASH_TOUT_MIN ? 0 : \ - (((a) - LM3560_FLASH_TOUT_MIN) / LM3560_FLASH_TOUT_STEP)) -#define LM3560_FLASH_TOUT_REG_TO_ms(a) \ - ((a) * LM3560_FLASH_TOUT_STEP + LM3560_FLASH_TOUT_MIN) - -/* TORCH BRT - * min 31250uA, step 31250uA, max 250000uA - */ -#define LM3560_TORCH_BRT_MIN 31250 -#define LM3560_TORCH_BRT_STEP 31250 -#define LM3560_TORCH_BRT_MAX 250000 -#define LM3560_TORCH_BRT_uA_TO_REG(a) \ - ((a) < LM3560_TORCH_BRT_MIN ? 0 : \ - (((a) - LM3560_TORCH_BRT_MIN) / LM3560_TORCH_BRT_STEP)) -#define LM3560_TORCH_BRT_REG_TO_uA(a) \ - ((a) * LM3560_TORCH_BRT_STEP + LM3560_TORCH_BRT_MIN) - -enum lm3560_led_id { - LM3560_LED0 = 0, - LM3560_LED1, - LM3560_LED_MAX -}; - -enum lm3560_peak_current { - LM3560_PEAK_1600mA = 0x00, - LM3560_PEAK_2300mA = 0x20, - LM3560_PEAK_3000mA = 0x40, - LM3560_PEAK_3600mA = 0x60 -}; - -/* struct lm3560_platform_data - * - * @peak : peak current - * @max_flash_timeout: flash timeout - * @max_flash_brt: flash mode led brightness - * @max_torch_brt: torch mode led brightness - */ -struct lm3560_platform_data { - enum lm3560_peak_current peak; - - u32 max_flash_timeout; - u32 max_flash_brt[LM3560_LED_MAX]; - u32 max_torch_brt[LM3560_LED_MAX]; -}; - -#endif /* __LM3560_H__ */ diff --git a/include/media/media-entity.h b/include/media/media-entity.h index b91ff6f8c3bb..d9b72cd87d52 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -726,14 +726,12 @@ int media_entity_pads_init(struct media_entity *entity, u16 num_pads, * the entity (currently, it does nothing). * * Calling media_entity_cleanup() on a media_entity whose memory has been - * zeroed but that has not been initialized with media_entity_pad_init() is + * zeroed but that has not been initialized with media_entity_pads_init() is * valid and is a no-op. */ -#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER) -static inline void media_entity_cleanup(struct media_entity *entity) {} -#else -#define media_entity_cleanup(entity) do { } while (false) -#endif +static inline void media_entity_cleanup(struct media_entity *entity) +{ +} /** * media_get_pad_index() - retrieves a pad index from an entity diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index f26c323e9c96..54a2d9620ed5 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -333,8 +333,10 @@ int __v4l2_async_register_subdev(struct v4l2_subdev *sd, struct module *module); * An error is returned if the module is no longer loaded on any attempts * to register it. */ +#define v4l2_async_register_subdev_sensor(sd) \ + __v4l2_async_register_subdev_sensor(sd, THIS_MODULE) int __must_check -v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd); +__v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd, struct module *module); /** * v4l2_async_unregister_subdev - unregisters a sub-device to the asynchronous diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index f8b1faced79c..edd416178c33 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -520,6 +520,7 @@ enum v4l2_pixel_encoding { * @vdiv: Vertical chroma subsampling factor * @block_w: Per-plane macroblock pixel width (optional) * @block_h: Per-plane macroblock pixel height (optional) + * @has_alpha: Does the format embeds an alpha component? */ struct v4l2_format_info { u32 format; @@ -532,6 +533,7 @@ struct v4l2_format_info { u8 vdiv; u8 block_w[4]; u8 block_h[4]; + bool has_alpha; }; static inline bool v4l2_is_format_rgb(const struct v4l2_format_info *f) @@ -556,6 +558,10 @@ int v4l2_fill_pixfmt(struct v4l2_pix_format *pixfmt, u32 pixelformat, u32 width, u32 height); int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt, u32 pixelformat, u32 width, u32 height); +/* @stride_alignment is a power of 2 value in bytes */ +int v4l2_fill_pixfmt_mp_aligned(struct v4l2_pix_format_mplane *pixfmt, + u32 pixelformat, u32 width, u32 height, + u8 stride_alignment); /** * v4l2_get_link_freq - Get link rate from transmitter diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 4424d481d7f7..4b4f4c15c53a 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -1093,8 +1093,8 @@ __poll_t vb2_core_poll(struct vb2_queue *q, struct file *file, * @ppos: file handle position tracking pointer * @nonblock: mode selector (1 means blocking calls, 0 means nonblocking) */ -size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count, - loff_t *ppos, int nonblock); +ssize_t vb2_read(struct vb2_queue *q, char __user *data, size_t count, + loff_t *ppos, int nonblock); /** * vb2_write() - implements write() syscall logic. * @q: pointer to &struct vb2_queue with videobuf2 queue. @@ -1103,8 +1103,8 @@ size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count, * @ppos: file handle position tracking pointer * @nonblock: mode selector (1 means blocking calls, 0 means nonblocking) */ -size_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count, - loff_t *ppos, int nonblock); +ssize_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count, + loff_t *ppos, int nonblock); /** * typedef vb2_thread_fnc - callback function for use with vb2_thread. diff --git a/include/media/vsp1.h b/include/media/vsp1.h index d9b91ff02761..98089e0a4385 100644 --- a/include/media/vsp1.h +++ b/include/media/vsp1.h @@ -44,8 +44,9 @@ struct vsp1_du_lif_config { void *callback_data; }; -int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, - const struct vsp1_du_lif_config *cfg); +int vsp1_du_enable(struct device *dev, unsigned int pipe_index, + const struct vsp1_du_lif_config *cfg); +int vsp1_du_disable(struct device *dev, unsigned int pipe_index); /** * struct vsp1_du_atomic_config - VSP atomic configuration parameters diff --git a/include/uapi/linux/cec-funcs.h b/include/uapi/linux/cec-funcs.h index 189ecf0e13cd..ba4b47de9bf0 100644 --- a/include/uapi/linux/cec-funcs.h +++ b/include/uapi/linux/cec-funcs.h @@ -1701,6 +1701,188 @@ static inline void cec_ops_request_current_latency(const struct cec_msg *msg, } +/* Latency Indication Protocol Feature */ +/* Only for CEC 2.0 and up */ +static inline void cec_msg_request_lip_support(struct cec_msg *msg, + int reply, __u16 phys_addr) +{ + msg->len = 4; + msg->msg[1] = CEC_MSG_REQUEST_LIP_SUPPORT; + msg->msg[2] = phys_addr >> 8; + msg->msg[3] = phys_addr & 0xff; + msg->reply = reply ? CEC_MSG_REPORT_LIP_SUPPORT : 0; +} + +static inline void cec_ops_request_lip_support(const struct cec_msg *msg, + __u16 *phys_addr) +{ + *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; +} + +static inline void cec_msg_report_lip_support(struct cec_msg *msg, __u32 sqid) +{ + msg->len = 6; + msg->msg[1] = CEC_MSG_REPORT_LIP_SUPPORT; + msg->msg[2] = sqid >> 24; + msg->msg[3] = (sqid >> 16) & 0xff; + msg->msg[4] = (sqid >> 8) & 0xff; + msg->msg[5] = sqid & 0xff; +} + +static inline void cec_ops_report_lip_support(const struct cec_msg *msg, + __u32 *sqid) +{ + *sqid = (msg->msg[2] << 24) | (msg->msg[3] << 16) | + (msg->msg[4] << 8) | msg->msg[5]; +} + +static inline void cec_msg_request_audio_and_video_latency(struct cec_msg *msg, + int reply, __u8 video_format, + __u8 hdr_format, __u8 vrr_format, + __u8 audio_format, + __u8 audio_format_extension) +{ + msg->len = 6; + msg->msg[1] = CEC_MSG_REQUEST_AUDIO_AND_VIDEO_LATENCY; + msg->msg[2] = video_format; + msg->msg[3] = hdr_format; + msg->msg[4] = vrr_format; + msg->msg[5] = audio_format; + if (audio_format >= 1 && audio_format <= 31) { + msg->msg[6] = audio_format_extension; + msg->len++; + } + msg->reply = reply ? CEC_MSG_REPORT_AUDIO_AND_VIDEO_LATENCY : 0; +} + +static inline void cec_ops_request_audio_and_video_latency(const struct cec_msg *msg, + __u8 *video_format, + __u8 *hdr_format, + __u8 *vrr_format, + __u8 *audio_format, + __u8 *audio_format_extension) +{ + *video_format = msg->msg[2]; + *hdr_format = msg->msg[3]; + *vrr_format = msg->msg[4]; + *audio_format = msg->msg[5]; + *audio_format_extension = msg->len > 6 ? msg->msg[6] : 0; +} + +static inline void cec_msg_report_audio_and_video_latency(struct cec_msg *msg, + __u16 video_latency, + __u16 audio_latency) +{ + msg->len = 6; + msg->msg[1] = CEC_MSG_REPORT_AUDIO_AND_VIDEO_LATENCY; + msg->msg[2] = video_latency >> 8; + msg->msg[3] = video_latency & 0xff; + msg->msg[4] = audio_latency >> 8; + msg->msg[5] = audio_latency & 0xff; +} + +static inline void cec_ops_report_audio_and_video_latency(const struct cec_msg *msg, + __u16 *video_latency, + __u16 *audio_latency) +{ + *video_latency = (msg->msg[2] << 8) | msg->msg[3]; + *audio_latency = (msg->msg[4] << 8) | msg->msg[5]; +} + +static inline void cec_msg_request_audio_latency(struct cec_msg *msg, + int reply, + __u8 audio_format, + __u8 audio_format_extension) +{ + msg->len = 3; + msg->msg[1] = CEC_MSG_REQUEST_AUDIO_LATENCY; + msg->msg[2] = audio_format; + if (audio_format >= 1 && audio_format <= 31) { + msg->msg[3] = audio_format_extension; + msg->len++; + } + msg->reply = reply ? CEC_MSG_REPORT_AUDIO_LATENCY : 0; +} + +static inline void cec_ops_request_audio_latency(const struct cec_msg *msg, + __u8 *audio_format, + __u8 *audio_format_extension) +{ + *audio_format = msg->msg[2]; + *audio_format_extension = msg->len > 3 ? msg->msg[3] : 0; +} + +static inline void cec_msg_report_audio_latency(struct cec_msg *msg, + __u16 audio_latency) +{ + msg->len = 4; + msg->msg[1] = CEC_MSG_REPORT_AUDIO_LATENCY; + msg->msg[2] = audio_latency >> 8; + msg->msg[3] = audio_latency & 0xff; +} + +static inline void cec_ops_report_audio_latency(const struct cec_msg *msg, + __u16 *audio_latency) +{ + *audio_latency = (msg->msg[2] << 8) | msg->msg[3]; +} + +static inline void cec_msg_request_video_latency(struct cec_msg *msg, + int reply, __u8 video_format, + __u8 hdr_format, + __u8 vrr_format) +{ + msg->len = 5; + msg->msg[1] = CEC_MSG_REQUEST_VIDEO_LATENCY; + msg->msg[2] = video_format; + msg->msg[3] = hdr_format; + msg->msg[4] = vrr_format; + msg->reply = reply ? CEC_MSG_REPORT_VIDEO_LATENCY : 0; +} + +static inline void cec_ops_request_video_latency(const struct cec_msg *msg, + __u8 *video_format, + __u8 *hdr_format, + __u8 *vrr_format) +{ + *video_format = msg->msg[2]; + *hdr_format = msg->msg[3]; + *vrr_format = msg->msg[4]; +} + +static inline void cec_msg_report_video_latency(struct cec_msg *msg, + __u16 video_latency) +{ + msg->len = 4; + msg->msg[1] = CEC_MSG_REPORT_VIDEO_LATENCY; + msg->msg[2] = video_latency >> 8; + msg->msg[3] = video_latency & 0xff; +} + +static inline void cec_ops_report_video_latency(const struct cec_msg *msg, + __u16 *video_latency) +{ + *video_latency = (msg->msg[2] << 8) | msg->msg[3]; +} + +static inline void cec_msg_update_sqid(struct cec_msg *msg, __u32 sqid) +{ + msg->len = 6; + msg->msg[1] = CEC_MSG_UPDATE_SQID; + msg->msg[2] = sqid >> 24; + msg->msg[3] = (sqid >> 16) & 0xff; + msg->msg[4] = (sqid >> 8) & 0xff; + msg->msg[5] = sqid & 0xff; +} + +static inline void cec_ops_update_sqid(const struct cec_msg *msg, + __u32 *sqid) +{ + *sqid = (msg->msg[2] << 24) | (msg->msg[3] << 16) | + (msg->msg[4] << 8) | msg->msg[5]; +} + + /* Capability Discovery and Control Feature */ static inline void cec_msg_cdc_hec_inquire_state(struct cec_msg *msg, __u16 phys_addr1, diff --git a/include/uapi/linux/cec.h b/include/uapi/linux/cec.h index b2af1dddd4d7..81a05c9c0706 100644 --- a/include/uapi/linux/cec.h +++ b/include/uapi/linux/cec.h @@ -742,7 +742,7 @@ struct cec_event { #define CEC_OP_PRIM_DEVTYPE_PROCESSOR 7 #define CEC_MSG_SET_MENU_LANGUAGE 0x32 -#define CEC_MSG_REPORT_FEATURES 0xa6 /* HDMI 2.0 */ +#define CEC_MSG_REPORT_FEATURES 0xa6 /* CEC 2.0 */ /* All Device Types Operand (all_device_types) */ #define CEC_OP_ALL_DEVTYPE_TV 0x80 #define CEC_OP_ALL_DEVTYPE_RECORD 0x40 @@ -777,7 +777,7 @@ struct cec_event { #define CEC_OP_FEAT_DEV_SOURCE_HAS_ARC_RX 0x02 #define CEC_OP_FEAT_DEV_HAS_SET_AUDIO_VOLUME_LEVEL 0x01 -#define CEC_MSG_GIVE_FEATURES 0xa5 /* HDMI 2.0 */ +#define CEC_MSG_GIVE_FEATURES 0xa5 /* CEC 2.0 */ /* Deck Control Feature */ @@ -1067,7 +1067,7 @@ struct cec_event { #define CEC_OP_AUD_FMT_ID_CEA861 0 #define CEC_OP_AUD_FMT_ID_CEA861_CXT 1 -#define CEC_MSG_SET_AUDIO_VOLUME_LEVEL 0x73 +#define CEC_MSG_SET_AUDIO_VOLUME_LEVEL 0x73 /* CEC 2.0 */ /* Audio Rate Control Feature */ #define CEC_MSG_SET_AUDIO_RATE 0x9a @@ -1091,7 +1091,6 @@ struct cec_event { /* Dynamic Audio Lipsync Feature */ -/* Only for CEC 2.0 and up */ #define CEC_MSG_REQUEST_CURRENT_LATENCY 0xa7 #define CEC_MSG_REPORT_CURRENT_LATENCY 0xa8 /* Low Latency Mode Operand (low_latency_mode) */ @@ -1104,6 +1103,30 @@ struct cec_event { #define CEC_OP_AUD_OUT_COMPENSATED_PARTIAL_DELAY 3 +/* Latency Indication Protocol Feature */ +#define CEC_MSG_REQUEST_LIP_SUPPORT 0x50 /* CEC 2.0 */ +#define CEC_MSG_REPORT_LIP_SUPPORT 0x51 /* CEC 2.0 */ +#define CEC_MSG_REQUEST_AUDIO_AND_VIDEO_LATENCY 0x52 /* CEC 2.0 */ +/* HDR Format Operand (hdr_format) */ +#define CEC_OP_HDR_FORMAT_GAMMA_SDR 0 +#define CEC_OP_HDR_FORMAT_GAMMA_HDR 1 +#define CEC_OP_HDR_FORMAT_PQ 2 +#define CEC_OP_HDR_FORMAT_HLG 3 +#define CEC_OP_HDR_FORMAT_DYNAMIC_HDR_TYPE_1 8 +#define CEC_OP_HDR_FORMAT_DYNAMIC_HDR_TYPE_2 9 +#define CEC_OP_HDR_FORMAT_DYNAMIC_HDR_TYPE_4 11 +#define CEC_OP_HDR_FORMAT_DV_SINK_LED 16 +#define CEC_OP_HDR_FORMAT_DV_SOURCE_LED 17 +#define CEC_OP_HDR_FORMAT_HDR10PLUS 24 +#define CEC_OP_HDR_FORMAT_ETSI_TS_103_433 32 +#define CEC_MSG_REPORT_AUDIO_AND_VIDEO_LATENCY 0x53 /* CEC 2.0 */ +#define CEC_MSG_REQUEST_AUDIO_LATENCY 0x54 /* CEC 2.0 */ +#define CEC_MSG_REPORT_AUDIO_LATENCY 0x55 /* CEC 2.0 */ +#define CEC_MSG_REQUEST_VIDEO_LATENCY 0x56 /* CEC 2.0 */ +#define CEC_MSG_REPORT_VIDEO_LATENCY 0x57 /* CEC 2.0 */ +#define CEC_MSG_UPDATE_SQID 0x58 /* CEC 2.0 */ + + /* Capability Discovery and Control Feature */ #define CEC_MSG_CDC_MESSAGE 0xf8 /* Ethernet-over-HDMI: nobody ever does this... */ diff --git a/include/uapi/linux/rkisp1-config.h b/include/uapi/linux/rkisp1-config.h index b2d2a71f7baf..d97384abc157 100644 --- a/include/uapi/linux/rkisp1-config.h +++ b/include/uapi/linux/rkisp1-config.h @@ -967,6 +967,92 @@ struct rkisp1_cif_isp_wdr_config { __u8 use_iref; }; +/* + * enum rkisp1_cif_isp_cac_h_clip_mode - horizontal clipping mode + * + * @RKISP1_CIF_ISP_CAC_H_CLIP_MODE_4PX: +/- 4 pixels + * @RKISP1_CIF_ISP_CAC_H_CLIP_MODE_4_5PX: +/- 4/5 pixels depending on bayer position + */ +enum rkisp1_cif_isp_cac_h_clip_mode { + RKISP1_CIF_ISP_CAC_H_CLIP_MODE_4PX = 0, + RKISP1_CIF_ISP_CAC_H_CLIP_MODE_4_5PX = 1, +}; + +/** + * enum rkisp1_cif_isp_cac_v_clip_mode - vertical clipping mode + * + * @RKISP1_CIF_ISP_CAC_V_CLIP_MODE_2PX: +/- 2 pixels + * @RKISP1_CIF_ISP_CAC_V_CLIP_MODE_3PX: +/- 3 pixels + * @RKISP1_CIF_ISP_CAC_V_CLIP_MODE_3_4PX: +/- 3/4 pixels depending on bayer position + */ +enum rkisp1_cif_isp_cac_v_clip_mode { + RKISP1_CIF_ISP_CAC_V_CLIP_MODE_2PX = 0, + RKISP1_CIF_ISP_CAC_V_CLIP_MODE_3PX = 1, + RKISP1_CIF_ISP_CAC_V_CLIP_MODE_3_4PX = 2, +}; + +/** + * struct rkisp1_cif_isp_cac_config - chromatic aberration correction configuration + * + * The correction is carried out by shifting the red and blue pixels relative + * to the green ones, depending on the distance from the optical center: + * + * @h_count_start: horizontal coordinate of the optical center (13-bit unsigned integer; [1,8191]) + * @v_count_start: vertical coordinate of the optical center (13-bit unsigned integer; [1,8191]) + * + * For each pixel, the x/y distances from the optical center are calculated and + * then transformed into the [0,255] range based on the following formula: + * + * (((d << 4) >> ns) * nf) >> 5 + * + * where `d` is the distance, `ns` and `nf` are the normalization parameters: + * + * @x_nf: horizontal normalization scale parameter (5-bit unsigned integer; [0,31]) + * @x_ns: horizontal normalization shift parameter (4-bit unsigned integer; [0,15]) + * + * @y_nf: vertical normalization scale parameter (5-bit unsigned integer; [0,31]) + * @y_ns: vertical normalization shift parameter (4-bit unsigned integer; [0,15]) + * + * These parameters should be chosen based on the image resolution, the position + * of the optical center, and the shape of pixels, so that no normalized distance + * is larger than 255. If the pixels have square shape, the two sets of parameters + * should be equal. + * + * The actual amount of correction is calculated with a third degree polynomial: + * + * c[0] * r + c[1] * r^2 + c[2] * r^3 + * + * where `c` is the set of coefficients for the given color, and `r` is distance: + * + * @red: red coefficients (5.4 two's complement; [-16,15.9375]) + * @blue: blue coefficients (5.4 two's complement; [-16,15.9375]) + * + * Finally, the amount is clipped as requested: + * + * @h_clip_mode: maximum horizontal shift (from enum rkisp1_cif_isp_cac_h_clip_mode) + * @v_clip_mode: maximum vertical shift (from enum rkisp1_cif_isp_cac_v_clip_mode) + * + * A positive result will shift away from the optical center, while a negative + * one will shift towards the optical center. In the latter case, the pixel + * values at the edges are duplicated. + */ +struct rkisp1_cif_isp_cac_config { + __u8 h_clip_mode; + __u8 v_clip_mode; + + __u16 h_count_start; + __u16 v_count_start; + + __u16 red[3]; + __u16 blue[3]; + + __u8 x_nf; + __u8 x_ns; + + __u8 y_nf; + __u8 y_ns; +}; + /*---------- PART2: Measurement Statistics ------------*/ /** @@ -1138,6 +1224,7 @@ struct rkisp1_stat_buffer { * @RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_EXPAND: Companding expand curve * @RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_COMPRESS: Companding compress curve * @RKISP1_EXT_PARAMS_BLOCK_TYPE_WDR: Wide dynamic range + * @RKISP1_EXT_PARAMS_BLOCK_TYPE_CAC: Chromatic aberration correction */ enum rkisp1_ext_params_block_type { RKISP1_EXT_PARAMS_BLOCK_TYPE_BLS, @@ -1161,6 +1248,7 @@ enum rkisp1_ext_params_block_type { RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_EXPAND, RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_COMPRESS, RKISP1_EXT_PARAMS_BLOCK_TYPE_WDR, + RKISP1_EXT_PARAMS_BLOCK_TYPE_CAC, }; /* For backward compatibility */ @@ -1507,6 +1595,22 @@ struct rkisp1_ext_params_wdr_config { struct rkisp1_cif_isp_wdr_config config; } __attribute__((aligned(8))); +/** + * struct rkisp1_ext_params_cac_config - RkISP1 extensible params CAC config + * + * RkISP1 extensible parameters CAC block. + * Identified by :c:type:`RKISP1_EXT_PARAMS_BLOCK_TYPE_CAC`. + * + * @header: The RkISP1 extensible parameters header, see + * :c:type:`rkisp1_ext_params_block_header` + * @config: CAC configuration, see + * :c:type:`rkisp1_cif_isp_cac_config` + */ +struct rkisp1_ext_params_cac_config { + struct rkisp1_ext_params_block_header header; + struct rkisp1_cif_isp_cac_config config; +} __attribute__((aligned(8))); + /* * The rkisp1_ext_params_compand_curve_config structure is counted twice as it * is used for both the COMPAND_EXPAND and COMPAND_COMPRESS block types. @@ -1532,14 +1636,15 @@ struct rkisp1_ext_params_wdr_config { sizeof(struct rkisp1_ext_params_compand_bls_config) +\ sizeof(struct rkisp1_ext_params_compand_curve_config) +\ sizeof(struct rkisp1_ext_params_compand_curve_config) +\ - sizeof(struct rkisp1_ext_params_wdr_config)) + sizeof(struct rkisp1_ext_params_wdr_config) +\ + sizeof(struct rkisp1_ext_params_cac_config)) /** - * enum rksip1_ext_param_buffer_version - RkISP1 extensible parameters version + * enum rkisp1_ext_param_buffer_version - RkISP1 extensible parameters version * * @RKISP1_EXT_PARAM_BUFFER_V1: First version of RkISP1 extensible parameters */ -enum rksip1_ext_param_buffer_version { +enum rkisp1_ext_param_buffer_version { RKISP1_EXT_PARAM_BUFFER_V1 = V4L2_ISP_PARAMS_VERSION_V1, }; @@ -1601,7 +1706,7 @@ enum rksip1_ext_param_buffer_version { * +---------------------------------------------------------------------+ * * @version: The RkISP1 extensible parameters buffer version, see - * :c:type:`rksip1_ext_param_buffer_version` + * :c:type:`rkisp1_ext_param_buffer_version` * @data_size: The RkISP1 configuration data effective size, excluding this * header * @data: The RkISP1 extensible configuration data blocks diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 68dd0c4e47b2..affec0ab4781 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -464,6 +464,8 @@ enum v4l2_mpeg_video_intra_refresh_period_type { V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_CYCLIC = 1, }; +#define V4L2_CID_MPEG_VIDEO_BACKGROUND_DETECTION (V4L2_CID_CODEC_BASE + 238) + /* CIDs for the MPEG-2 Part 2 (H.262) codec */ #define V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL (V4L2_CID_CODEC_BASE+270) enum v4l2_mpeg_video_mpeg2_level { |
