diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-08-07 22:00:53 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-08-07 22:00:53 +0200 |
commit | fa73e212318a3277ae1f304febbc617c75d4d2db (patch) | |
tree | 54fea325ba419be2388cb9515e778f5c0d9a2f8c | |
parent | Merge tag 'mailbox-v5.9' of git://git.linaro.org/landing-teams/working/fujits... (diff) | |
parent | media: camss: fix memory leaks on error handling paths in probe (diff) | |
download | linux-fa73e212318a3277ae1f304febbc617c75d4d2db.tar.xz linux-fa73e212318a3277ae1f304febbc617c75d4d2db.zip |
Merge tag 'media/v5.9-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab:
- Legacy soc_camera driver was removed from staging
- New I2C sensor related drivers: dw9768, ch7322, max9271, rdacm20
- TI vpe driver code was re-organized and had new features added
- Added Xilinx MIPI CSI-2 Rx Subsystem driver
- Added support for Infrared Toy and IR Droid devices
- Lots of random driver fixes, new features and cleanups
* tag 'media/v5.9-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (318 commits)
media: camss: fix memory leaks on error handling paths in probe
media: davinci: vpif_capture: fix potential double free
media: radio: remove redundant assignment to variable retval
media: allegro: fix potential null dereference on header
media: mtk-mdp: Fix a refcounting bug on error in init
media: allegro: fix an error pointer vs NULL check
media: meye: fix missing pm_mchip_mode field
media: cafe-driver: use generic power management
media: saa7164: use generic power management
media: v4l2-dev/ioctl: Fix document for VIDIOC_QUERYCAP
media: v4l2: Correct kernel-doc inconsistency
media: v4l2: Correct kernel-doc inconsistency
media: dvbdev.h: keep * together with the type
media: v4l2-subdev.h: keep * together with the type
media: videobuf2: Print videobuf2 buffer state by name
media: colorspaces-details.rst: fix V4L2_COLORSPACE_JPEG description
media: tw68: use generic power management
media: meye: use generic power management
media: cx88: use generic power management
media: cx25821: use generic power management
...
268 files changed, 12784 insertions, 12115 deletions
diff --git a/Documentation/admin-guide/media/fimc.rst b/Documentation/admin-guide/media/fimc.rst index 0b8ddc4a3008..56b149d9a527 100644 --- a/Documentation/admin-guide/media/fimc.rst +++ b/Documentation/admin-guide/media/fimc.rst @@ -2,7 +2,7 @@ .. include:: <isonum.txt> -The Samsung S5P/EXYNOS4 FIMC driver +The Samsung S5P/Exynos4 FIMC driver =================================== Copyright |copy| 2012 - 2013 Samsung Electronics Co., Ltd. @@ -19,7 +19,7 @@ drivers/media/platform/exynos4-is directory. Supported SoCs -------------- -S5PC100 (mem-to-mem only), S5PV210, EXYNOS4210 +S5PC100 (mem-to-mem only), S5PV210, Exynos4210 Supported features ------------------ @@ -45,7 +45,7 @@ Media device interface ~~~~~~~~~~~~~~~~~~~~~~ The driver supports Media Controller API as defined at :ref:`media_controller`. -The media device driver name is "SAMSUNG S5P FIMC". +The media device driver name is "Samsung S5P FIMC". The purpose of this interface is to allow changing assignment of FIMC instances to the SoC peripheral camera input at runtime and optionally to control internal diff --git a/Documentation/admin-guide/media/vivid.rst b/Documentation/admin-guide/media/vivid.rst index 52e57b773f07..6d7175f96f74 100644 --- a/Documentation/admin-guide/media/vivid.rst +++ b/Documentation/admin-guide/media/vivid.rst @@ -293,6 +293,15 @@ all configurable using the following module options: - 0: vmalloc - 1: dma-contig +- cache_hints: + + specifies if the device should set queues' user-space cache and memory + consistency hint capability (V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS). + The hints are valid only when using MMAP streaming I/O. Default is 0. + + - 0: forbid hints + - 1: allow hints + Taken together, all these module options allow you to precisely customize the driver behavior and test your application with all sorts of permutations. It is also very suitable to emulate hardware that is not yet available, e.g. diff --git a/Documentation/devicetree/bindings/media/i2c/chrontel,ch7322.yaml b/Documentation/devicetree/bindings/media/i2c/chrontel,ch7322.yaml new file mode 100644 index 000000000000..daa2869377c5 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/chrontel,ch7322.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/media/i2c/chrontel,ch7322.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Chrontel HDMI-CEC Controller + +maintainers: + - Jeff Chase <jnchase@google.com> + +description: + The Chrontel CH7322 is a discrete HDMI-CEC controller. It is + programmable through I2C and drives a single CEC line. + +properties: + compatible: + const: chrontel,ch7322 + + reg: + description: I2C device address + maxItems: 1 + + clocks: + maxItems: 1 + + interrupts: + maxItems: 1 + + reset-gpios: + description: + Reference to the GPIO connected to the RESET pin, if any. This + pin is active-low. + maxItems: 1 + + standby-gpios: + description: + Reference to the GPIO connected to the OE pin, if any. When low + the device will respond to power status requests with "standby" + if in auto mode. + maxItems: 1 + + # see ../cec.txt + hdmi-phandle: + description: phandle to the HDMI controller + +required: + - compatible + - reg + - interrupts + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/irq.h> + i2c { + #address-cells = <1>; + #size-cells = <0>; + ch7322@75 { + compatible = "chrontel,ch7322"; + reg = <0x75>; + interrupts = <47 IRQ_TYPE_EDGE_RISING>; + standby-gpios = <&gpio 16 GPIO_ACTIVE_LOW>; + reset-gpios = <&gpio 15 GPIO_ACTIVE_LOW>; + hdmi-phandle = <&hdmi>; + }; + }; diff --git a/Documentation/devicetree/bindings/media/i2c/dongwoon,dw9768.yaml b/Documentation/devicetree/bindings/media/i2c/dongwoon,dw9768.yaml new file mode 100644 index 000000000000..cb96e95d7e81 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/dongwoon,dw9768.yaml @@ -0,0 +1,100 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright (c) 2020 MediaTek Inc. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/dongwoon,dw9768.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Dongwoon Anatech DW9768 Voice Coil Motor (VCM) Lens Device Tree Bindings + +maintainers: + - Dongchun Zhu <dongchun.zhu@mediatek.com> + +description: |- + The Dongwoon DW9768 is a single 10-bit digital-to-analog (DAC) converter + with 100 mA output current sink capability. VCM current is controlled with + a linear mode driver. The DAC is controlled via a 2-wire (I2C-compatible) + serial interface that operates at clock rates up to 1MHz. This chip + integrates Advanced Actuator Control (AAC) technology and is intended for + driving voice coil lenses in camera modules. + +properties: + compatible: + enum: + - dongwoon,dw9768 # for DW9768 VCM + - giantec,gt9769 # for GT9769 VCM + + reg: + maxItems: 1 + + vin-supply: + description: + Definition of the regulator used as Digital I/O voltage supply. + + vdd-supply: + description: + Definition of the regulator used as Digital core voltage supply. + + dongwoon,aac-mode: + description: + Indication of AAC mode select. + allOf: + - $ref: "/schemas/types.yaml#/definitions/uint32" + - enum: + - 1 # AAC2 mode(operation time# 0.48 x Tvib) + - 2 # AAC3 mode(operation time# 0.70 x Tvib) + - 3 # AAC4 mode(operation time# 0.75 x Tvib) + - 5 # AAC8 mode(operation time# 1.13 x Tvib) + default: 2 + + dongwoon,aac-timing: + description: + Number of AAC Timing count that controlled by one 6-bit period of + vibration register AACT[5:0], the unit of which is 100 us. + allOf: + - $ref: "/schemas/types.yaml#/definitions/uint32" + - default: 0x20 + minimum: 0x00 + maximum: 0x3f + + dongwoon,clock-presc: + description: + Indication of VCM internal clock dividing rate select, as one multiple + factor to calculate VCM ring periodic time Tvib. + allOf: + - $ref: "/schemas/types.yaml#/definitions/uint32" + - enum: + - 0 # Dividing Rate - 2 + - 1 # Dividing Rate - 1 + - 2 # Dividing Rate - 1/2 + - 3 # Dividing Rate - 1/4 + - 4 # Dividing Rate - 8 + - 5 # Dividing Rate - 4 + default: 1 + +required: + - compatible + - reg + - vin-supply + - vdd-supply + +additionalProperties: false + +examples: + - | + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + dw9768: camera-lens@c { + compatible = "dongwoon,dw9768"; + reg = <0x0c>; + + vin-supply = <&mt6358_vcamio_reg>; + vdd-supply = <&mt6358_vcama2_reg>; + dongwoon,aac-timing = <0x39>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/media/i2c/imi,rdacm2x-gmsl.yaml b/Documentation/devicetree/bindings/media/i2c/imi,rdacm2x-gmsl.yaml new file mode 100644 index 000000000000..5ad4b8c356cf --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/imi,rdacm2x-gmsl.yaml @@ -0,0 +1,159 @@ +# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +# Copyright (C) 2019 Renesas Electronics Corp. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/imi,rdacm2x-gmsl.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: IMI D&D RDACM20 and RDACM21 Automotive Camera Platforms + +maintainers: + - Jacopo Mondi <jacopo+renesas@jmondi.org> + - Kieran Bingham <kieran.bingham+renesas@ideasonboard.com> + - Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> + - Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se> + +description: -| + The IMI D&D RDACM20 and RDACM21 are GMSL-compatible camera designed for + automotive applications. + + The RDACM20 camera module encloses a Maxim Integrated MAX9271 GMSL serializer, + coupled with an OV10635 image sensor and an embedded MCU. Both the MCU and + the image sensor are connected to the serializer local I2C bus and are + accessible by the host SoC by direct addressing. + + The RDACM21 camera module encloses the same serializer, coupled with an + OV10640 image sensor and an OV490 ISP. Only the OV490 ISP is interfaced to + the serializer local I2C bus while the image sensor is not accessible from + the host SoC. + + They both connect to a remote GMSL endpoint through a coaxial cable. + + IMI RDACM20 + +---------------+ +--------------------------------+ + | GMSL | <- Video Stream | <- Video--------\ | + | |< === GMSL Link ====== >|MAX9271<- I2C bus-> <-->OV10635 | + | de-serializer | <- I2C messages -> | \<-->MCU | + +---------------+ +--------------------------------+ + + IMI RDACM21 + +---------------+ +--------------------------------+ + | GMSL | <- Video Stream | <- Video--------\ | + | |< === GMSL Link ====== >|MAX9271<- I2C bus-> <-->OV490 | + | | <- I2C messages -> | | | + | de-serializer | | OV10640 <-------| | + +---------------+ +--------------------------------+ + + Both camera modules serialize video data generated by the embedded camera + sensor on the GMSL serial channel to a remote GMSL de-serializer. They also + receive and transmit I2C messages encapsulated and transmitted on the GMSL + bidirectional control channel. + + All I2C traffic received on the GMSL link not directed to the serializer is + propagated on the local I2C bus to the remote device there connected. All the + I2C traffic generated on the local I2C bus not directed to the serializer is + propagated to the remote de-serializer encapsulated in the GMSL control + channel. + + The RDACM20 and RDACM21 DT node should be a direct child of the GMSL + deserializer's I2C bus corresponding to the GMSL link that the camera is + attached to. + +properties: + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + compatible: + enum: + - imi,rdacm20 + - imi,rdacm21 + + reg: + description: -| + I2C device addresses, the first to be assigned to the serializer, the + following ones to be assigned to the remote devices. + + For RDACM20 the second entry of the property is assigned to the + OV10635 image sensor and the optional third one to the embedded MCU. + + For RDACM21 the second entry is assigned to the OV490 ISP and the optional + third one ignored. + + minItems: 2 + maxItems: 3 + + port: + type: object + additionalProperties: false + description: -| + Connection to the remote GMSL endpoint are modelled using the OF graph + bindings in accordance with the video interface bindings defined in + Documentation/devicetree/bindings/media/video-interfaces.txt. + + The device node contains a single "port" child node with a single + "endpoint" sub-device. + + properties: + endpoint: + type: object + additionalProperties: false + + properties: + remote-endpoint: + description: -| + phandle to the remote GMSL endpoint sub-node in the remote node + port. + maxItems: 1 + + required: + - remote-endpoint + + required: + - endpoint + +required: + - compatible + - reg + - port + +examples: + - | + i2c@e66d8000 { + #address-cells = <1>; + #size-cells = <0>; + + reg = <0 0xe66d8000>; + + camera@31 { + compatible = "imi,rdacm20"; + reg = <0x31>, <0x41>, <0x51>; + + port { + rdacm20_out0: endpoint { + remote-endpoint = <&max9286_in0>; + }; + }; + }; + }; + + - | + i2c@e66d8000 { + #address-cells = <1>; + #size-cells = <0>; + + reg = <0 0xe66d8000>; + + camera@31 { + compatible = "imi,rdacm21"; + reg = <0x31>, <0x41>; + + port { + rdacm21_out0: endpoint { + remote-endpoint = <&max9286_in0>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/media/i2c/maxim,max9286.yaml b/Documentation/devicetree/bindings/media/i2c/maxim,max9286.yaml new file mode 100644 index 000000000000..e7b543159d15 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/maxim,max9286.yaml @@ -0,0 +1,366 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2019 Renesas Electronics Corp. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/maxim,max9286.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Maxim Integrated Quad GMSL Deserializer + +maintainers: + - Jacopo Mondi <jacopo+renesas@jmondi.org> + - Kieran Bingham <kieran.bingham+renesas@ideasonboard.com> + - Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> + - Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se> + +description: | + The MAX9286 deserializer receives video data on up to 4 Gigabit Multimedia + Serial Links (GMSL) and outputs them on a CSI-2 D-PHY port using up to 4 data + lanes. + + In addition to video data, the GMSL links carry a bidirectional control + channel that encapsulates I2C messages. The MAX9286 forwards all I2C traffic + not addressed to itself to the other side of the links, where a GMSL + serializer will output it on a local I2C bus. In the other direction all I2C + traffic received over GMSL by the MAX9286 is output on the local I2C bus. + +properties: + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + compatible: + const: maxim,max9286 + + reg: + description: I2C device address + maxItems: 1 + + poc-supply: + description: Regulator providing Power over Coax to the cameras + maxItems: 1 + + enable-gpios: + description: GPIO connected to the \#PWDN pin with inverted polarity + maxItems: 1 + + gpio-controller: true + + '#gpio-cells': + const: 2 + + ports: + type: object + description: | + The connections to the MAX9286 GMSL and its endpoint nodes are modelled + using the OF graph bindings in accordance with the video interface + bindings defined in + Documentation/devicetree/bindings/media/video-interfaces.txt. + + The following table lists the port number corresponding to each device + port. + + Port Description + ---------------------------------------- + Port 0 GMSL Input 0 + Port 1 GMSL Input 1 + Port 2 GMSL Input 2 + Port 3 GMSL Input 3 + Port 4 CSI-2 Output + + properties: + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + port@[0-3]: + type: object + properties: + reg: + enum: [ 0, 1, 2, 3 ] + + endpoint: + type: object + + properties: + remote-endpoint: + description: | + phandle to the remote GMSL source endpoint subnode in the + remote node port. + + required: + - remote-endpoint + + required: + - reg + - endpoint + + additionalProperties: false + + port@4: + type: object + properties: + reg: + const: 4 + + endpoint: + type: object + + properties: + remote-endpoint: + description: phandle to the remote CSI-2 sink endpoint. + + data-lanes: + description: array of physical CSI-2 data lane indexes. + + required: + - remote-endpoint + - data-lanes + + required: + - reg + - endpoint + + additionalProperties: false + + required: + - port@4 + + i2c-mux: + type: object + description: | + Each GMSL link is modelled as a child bus of an i2c bus + multiplexer/switch, in accordance with bindings described in + Documentation/devicetree/bindings/i2c/i2c-mux.txt. + + properties: + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + patternProperties: + "^i2c@[0-3]$": + type: object + description: | + Child node of the i2c bus multiplexer which represents a GMSL link. + Each serializer device on the GMSL link remote end is represented with + an i2c-mux child node. The MAX9286 chip supports up to 4 GMSL + channels. + + properties: + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + reg: + description: The index of the GMSL channel. + maxItems: 1 + + patternProperties: + "^camera@[a-f0-9]+$": + type: object + description: | + The remote camera device, composed by a GMSL serializer and a + connected video source. + + properties: + compatible: + description: The remote device compatible string. + + reg: + minItems: 2 + maxItems: 3 + description: | + The I2C addresses to be assigned to the remote devices through + address reprogramming. The number of entries depends on the + requirements of the currently connected remote device. + + port: + type: object + + properties: + endpoint: + type: object + + properties: + remote-endpoint: + description: phandle to the MAX9286 sink endpoint. + + required: + - remote-endpoint + + additionalProperties: false + + required: + - endpoint + + additionalProperties: false + + required: + - compatible + - reg + - port + + additionalProperties: false + + additionalProperties: false + + additionalProperties: false + +required: + - compatible + - reg + - ports + - i2c-mux + - gpio-controller + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + i2c@e66d8000 { + #address-cells = <1>; + #size-cells = <0>; + + reg = <0 0xe66d8000>; + + gmsl-deserializer@2c { + compatible = "maxim,max9286"; + reg = <0x2c>; + poc-supply = <&camera_poc_12v>; + enable-gpios = <&gpio 13 GPIO_ACTIVE_HIGH>; + + gpio-controller; + #gpio-cells = <2>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + max9286_in0: endpoint { + remote-endpoint = <&rdacm20_out0>; + }; + }; + + port@1 { + reg = <1>; + + max9286_in1: endpoint { + remote-endpoint = <&rdacm20_out1>; + }; + }; + + port@2 { + reg = <2>; + + max9286_in2: endpoint { + remote-endpoint = <&rdacm20_out2>; + }; + }; + + port@3 { + reg = <3>; + + max9286_in3: endpoint { + remote-endpoint = <&rdacm20_out3>; + }; + }; + + port@4 { + reg = <4>; + + max9286_out: endpoint { + data-lanes = <1 2 3 4>; + remote-endpoint = <&csi40_in>; + }; + }; + }; + + i2c-mux { + #address-cells = <1>; + #size-cells = <0>; + + i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + camera@51 { + compatible = "imi,rdacm20"; + reg = <0x51>, <0x61>; + + port { + rdacm20_out0: endpoint { + remote-endpoint = <&max9286_in0>; + }; + }; + + }; + }; + + i2c@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + + camera@52 { + compatible = "imi,rdacm20"; + reg = <0x52>, <0x62>; + + port { + rdacm20_out1: endpoint { + remote-endpoint = <&max9286_in1>; + }; + }; + }; + }; + + i2c@2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + + camera@53 { + compatible = "imi,rdacm20"; + reg = <0x53>, <0x63>; + + port { + rdacm20_out2: endpoint { + remote-endpoint = <&max9286_in2>; + }; + }; + }; + }; + + i2c@3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + + camera@54 { + compatible = "imi,rdacm20"; + reg = <0x54>, <0x64>; + + port { + rdacm20_out3: endpoint { + remote-endpoint = <&max9286_in3>; + }; + }; + }; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/media/renesas,fcp.txt b/Documentation/devicetree/bindings/media/renesas,fcp.txt deleted file mode 100644 index 79c37395b396..000000000000 --- a/Documentation/devicetree/bindings/media/renesas,fcp.txt +++ /dev/null @@ -1,34 +0,0 @@ -Renesas R-Car Frame Compression Processor (FCP) ------------------------------------------------ - -The FCP is a companion module of video processing modules in the Renesas R-Car -Gen3 and RZ/G2 SoCs. It provides data compression and decompression, data -caching, and conversion of AXI transactions in order to reduce the memory -bandwidth. - -There are three types of FCP: FCP for Codec (FCPC), FCP for VSP (FCPV) and FCP -for FDP (FCPF). Their configuration and behaviour depend on the module they -are paired with. These DT bindings currently support the FCPV and FCPF. - - - compatible: Must be one or more of the following - - - "renesas,fcpv" for generic compatible 'FCP for VSP' - - "renesas,fcpf" for generic compatible 'FCP for FDP' - - - reg: the register base and size for the device registers - - clocks: Reference to the functional clock - -Optional properties: - - power-domains : power-domain property defined with a power domain specifier - to respective power domain. - - -Device node example -------------------- - - fcpvd1: fcp@fea2f000 { - compatible = "renesas,fcpv"; - reg = <0 0xfea2f000 0 0x200>; - clocks = <&cpg CPG_MOD 602>; - power-domains = <&sysc R8A7795_PD_A3VP>; - }; diff --git a/Documentation/devicetree/bindings/media/renesas,fcp.yaml b/Documentation/devicetree/bindings/media/renesas,fcp.yaml new file mode 100644 index 000000000000..43f2fed8cd33 --- /dev/null +++ b/Documentation/devicetree/bindings/media/renesas,fcp.yaml @@ -0,0 +1,66 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/renesas,fcp.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Renesas R-Car Frame Compression Processor (FCP) + +maintainers: + - Laurent Pinchart <laurent.pinchart@ideasonboard.com> + +description: | + The FCP is a companion module of video processing modules in the Renesas + R-Car Gen3 and RZ/G2 SoCs. It provides data compression and decompression, + data caching, and conversion of AXI transactions in order to reduce the + memory bandwidth. + + There are three types of FCP: FCP for Codec (FCPC), FCP for VSP (FCPV) and + FCP for FDP (FCPF). Their configuration and behaviour depend on the module + they are paired with. These DT bindings currently support the FCPV and FCPF. + +properties: + compatible: + enum: + - renesas,fcpv # FCP for VSP + - renesas,fcpf # FCP for FDP + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + iommus: + maxItems: 1 + + power-domains: + maxItems: 1 + + resets: + maxItems: 1 + +required: + - compatible + - reg + - clocks + - power-domains + - resets + +additionalProperties: false + +examples: + # R8A7795 (R-Car H3) FCP for VSP-D1 + - | + #include <dt-bindings/clock/renesas-cpg-mssr.h> + #include <dt-bindings/power/r8a7795-sysc.h> + + fcp@fea2f000 { + compatible = "renesas,fcpv"; + reg = <0xfea2f000 0x200>; + clocks = <&cpg CPG_MOD 602>; + power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; + resets = <&cpg 602>; + iommus = <&ipmmu_vi0 9>; + }; +... diff --git a/Documentation/devicetree/bindings/media/renesas,fdp1.txt b/Documentation/devicetree/bindings/media/renesas,fdp1.txt deleted file mode 100644 index 8dd1007bb573..000000000000 --- a/Documentation/devicetree/bindings/media/renesas,fdp1.txt +++ /dev/null @@ -1,37 +0,0 @@ -Renesas R-Car Fine Display Processor (FDP1) -------------------------------------------- - -The FDP1 is a de-interlacing module which converts interlaced video to -progressive video. It is capable of performing pixel format conversion between -YCbCr/YUV formats and RGB formats. Only YCbCr/YUV formats are supported as -an input to the module. - -Required properties: - - - compatible: must be "renesas,fdp1" - - reg: the register base and size for the device registers - - interrupts : interrupt specifier for the FDP1 instance - - clocks: reference to the functional clock - -Optional properties: - - - power-domains: reference to the power domain that the FDP1 belongs to, if - any. - - renesas,fcp: a phandle referencing the FCP that handles memory accesses - for the FDP1. Not needed on Gen2, mandatory on Gen3. - -Please refer to the binding documentation for the clock and/or power domain -providers for more details. - - -Device node example -------------------- - - fdp1@fe940000 { - compatible = "renesas,fdp1"; - reg = <0 0xfe940000 0 0x2400>; - interrupts = <GIC_SPI 262 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&cpg CPG_MOD 119>; - power-domains = <&sysc R8A7795_PD_A3VP>; - renesas,fcp = <&fcpf0>; - }; diff --git a/Documentation/devicetree/bindings/media/renesas,fdp1.yaml b/Documentation/devicetree/bindings/media/renesas,fdp1.yaml new file mode 100644 index 000000000000..2a27a7296fea --- /dev/null +++ b/Documentation/devicetree/bindings/media/renesas,fdp1.yaml @@ -0,0 +1,69 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/renesas,fdp1.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Renesas R-Car Fine Display Processor (FDP1) + +maintainers: + - Laurent Pinchart <laurent.pinchart@ideasonboard.com> + +description: + The FDP1 is a de-interlacing module which converts interlaced video to + progressive video. It is capable of performing pixel format conversion + between YCbCr/YUV formats and RGB formats. Only YCbCr/YUV formats are + supported as an input to the module. + +properties: + compatible: + enum: + - renesas,fdp1 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + power-domains: + maxItems: 1 + + resets: + maxItems: 1 + + renesas,fcp: + $ref: /schemas/types.yaml#/definitions/phandle + description: + A phandle referencing the FCP that handles memory accesses for the FDP1. + Not allowed on R-Car Gen2, mandatory on R-Car Gen3. + +required: + - compatible + - reg + - interrupts + - clocks + - power-domains + - resets + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/renesas-cpg-mssr.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/power/r8a7795-sysc.h> + + fdp1@fe940000 { + compatible = "renesas,fdp1"; + reg = <0xfe940000 0x2400>; + interrupts = <GIC_SPI 262 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&cpg CPG_MOD 119>; + power-domains = <&sysc R8A7795_PD_A3VP>; + resets = <&cpg 119>; + renesas,fcp = <&fcpf0>; + }; +... diff --git a/Documentation/devicetree/bindings/media/renesas,vsp1.txt b/Documentation/devicetree/bindings/media/renesas,vsp1.txt deleted file mode 100644 index cd5a955b2ea0..000000000000 --- a/Documentation/devicetree/bindings/media/renesas,vsp1.txt +++ /dev/null @@ -1,30 +0,0 @@ -* Renesas VSP Video Processing Engine - -The VSP is a video processing engine that supports up-/down-scaling, alpha -blending, color space conversion and various other image processing features. -It can be found in the Renesas R-Car Gen2, R-Car Gen3, RZ/G1, and RZ/G2 SoCs. - -Required properties: - - - compatible: Must contain one of the following values - - "renesas,vsp1" for the R-Car Gen2 and RZ/G1 VSP1 - - "renesas,vsp2" for the R-Car Gen3 and RZ/G2 VSP2 - - - reg: Base address and length of the registers block for the VSP. - - interrupts: VSP interrupt specifier. - - clocks: A phandle + clock-specifier pair for the VSP functional clock. - -Optional properties: - - - renesas,fcp: A phandle referencing the FCP that handles memory accesses - for the VSP. Not needed on Gen2, mandatory on Gen3. - - -Example: R8A7790 (R-Car H2) VSP1-S node - - vsp@fe928000 { - compatible = "renesas,vsp1"; - reg = <0 0xfe928000 0 0x8000>; - interrupts = <0 267 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&mstp1_clks R8A7790_CLK_VSP1_S>; - }; diff --git a/Documentation/devicetree/bindings/media/renesas,vsp1.yaml b/Documentation/devicetree/bindings/media/renesas,vsp1.yaml new file mode 100644 index 000000000000..990e9c1dbc43 --- /dev/null +++ b/Documentation/devicetree/bindings/media/renesas,vsp1.yaml @@ -0,0 +1,97 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/renesas,vsp1.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Renesas VSP Video Processing Engine + +maintainers: + - Laurent Pinchart <laurent.pinchart@ideasonboard.com> + +description: + The VSP is a video processing engine that supports up-/down-scaling, alpha + blending, color space conversion and various other image processing features. + It can be found in the Renesas R-Car Gen2, R-Car Gen3, RZ/G1, and RZ/G2 SoCs. + +properties: + compatible: + enum: + - renesas,vsp1 # R-Car Gen2 and RZ/G1 + - renesas,vsp2 # R-Car Gen3 and RZ/G2 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + power-domains: + maxItems: 1 + + resets: + maxItems: 1 + + renesas,fcp: + $ref: /schemas/types.yaml#/definitions/phandle + description: + A phandle referencing the FCP that handles memory accesses for the VSP. + +required: + - compatible + - reg + - interrupts + - clocks + - power-domains + - resets + +additionalProperties: false + +if: + properties: + compatible: + items: + - const: renesas,vsp1 +then: + properties: + renesas,fcp: false +else: + required: + - renesas,fcp + +examples: + # R8A7790 (R-Car H2) VSP1-S + - | + #include <dt-bindings/clock/renesas-cpg-mssr.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/power/r8a7790-sysc.h> + + vsp@fe928000 { + compatible = "renesas,vsp1"; + reg = <0xfe928000 0x8000>; + interrupts = <GIC_SPI 267 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&cpg CPG_MOD 131>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 131>; + }; + + # R8A77951 (R-Car H3) VSP2-BC + - | + #include <dt-bindings/clock/renesas-cpg-mssr.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/power/r8a7795-sysc.h> + + vsp@fe920000 { + compatible = "renesas,vsp2"; + reg = <0xfe920000 0x8000>; + interrupts = <GIC_SPI 465 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&cpg CPG_MOD 624>; + power-domains = <&sysc R8A7795_PD_A3VP>; + resets = <&cpg 624>; + + renesas,fcp = <&fcpvb1>; + }; +... diff --git a/Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.yaml b/Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.yaml new file mode 100644 index 000000000000..7b9407c0520e --- /dev/null +++ b/Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.yaml @@ -0,0 +1,237 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/xilinx/xlnx,csi2rxss.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Xilinx MIPI CSI-2 Receiver Subsystem + +maintainers: + - Vishal Sagar <vishal.sagar@xilinx.com> + +description: | + The Xilinx MIPI CSI-2 Receiver Subsystem is used to capture MIPI CSI-2 + traffic from compliant camera sensors and send the output as AXI4 Stream + video data for image processing. + The subsystem consists of a MIPI D-PHY in slave mode which captures the + data packets. This is passed along the MIPI CSI-2 Rx IP which extracts the + packet data. The optional Video Format Bridge (VFB) converts this data to + AXI4 Stream video data. + For more details, please refer to PG232 Xilinx MIPI CSI-2 Receiver Subsystem. + Please note that this bindings includes only the MIPI CSI-2 Rx controller + and Video Format Bridge and not D-PHY. + +properties: + compatible: + items: + - enum: + - xlnx,mipi-csi2-rx-subsystem-5.0 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + description: List of clock specifiers + items: + - description: AXI Lite clock + - description: Video clock + + clock-names: + items: + - const: lite_aclk + - const: video_aclk + + xlnx,csi-pxl-format: + description: | + This denotes the CSI Data type selected in hw design. + Packets other than this data type (except for RAW8 and + User defined data types) will be filtered out. + Possible values are as below - + 0x1e - YUV4228B + 0x1f - YUV42210B + 0x20 - RGB444 + 0x21 - RGB555 + 0x22 - RGB565 + 0x23 - RGB666 + 0x24 - RGB888 + 0x28 - RAW6 + 0x29 - RAW7 + 0x2a - RAW8 + 0x2b - RAW10 + 0x2c - RAW12 + 0x2d - RAW14 + 0x2e - RAW16 + 0x2f - RAW20 + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + - anyOf: + - minimum: 0x1e + - maximum: 0x24 + - minimum: 0x28 + - maximum: 0x2f + + xlnx,vfb: + type: boolean + description: Present when Video Format Bridge is enabled in IP configuration + + xlnx,en-csi-v2-0: + type: boolean + description: Present if CSI v2 is enabled in IP configuration. + + xlnx,en-vcx: + type: boolean + description: | + When present, there are maximum 16 virtual channels, else only 4. + + xlnx,en-active-lanes: + type: boolean + description: | + Present if the number of active lanes can be re-configured at + runtime in the Protocol Configuration Register. Otherwise all lanes, + as set in IP configuration, are always active. + + video-reset-gpios: + description: Optional specifier for a GPIO that asserts video_aresetn. + maxItems: 1 + + ports: + type: object + + properties: + port@0: + type: object + description: | + Input / sink port node, single endpoint describing the + CSI-2 transmitter. + + properties: + reg: + const: 0 + + endpoint: + type: object + + properties: + + data-lanes: + description: | + This is required only in the sink port 0 endpoint which + connects to MIPI CSI-2 source like sensor. + The possible values are - + 1 - For 1 lane enabled in IP. + 1 2 - For 2 lanes enabled in IP. + 1 2 3 - For 3 lanes enabled in IP. + 1 2 3 4 - For 4 lanes enabled in IP. + items: + - const: 1 + - const: 2 + - const: 3 + - const: 4 + + remote-endpoint: true + + required: + - data-lanes + - remote-endpoint + + additionalProperties: false + + additionalProperties: false + + port@1: + type: object + description: | + Output / source port node, endpoint describing modules + connected the CSI-2 receiver. + + properties: + + reg: + const: 1 + + endpoint: + type: object + + properties: + + remote-endpoint: true + + required: + - remote-endpoint + + additionalProperties: false + + additionalProperties: false + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - ports + +allOf: + - if: + required: + - xlnx,vfb + then: + required: + - xlnx,csi-pxl-format + else: + properties: + xlnx,csi-pxl-format: false + + - if: + not: + required: + - xlnx,en-csi-v2-0 + then: + properties: + xlnx,en-vcx: false + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + xcsi2rxss_1: csi2rx@a0020000 { + compatible = "xlnx,mipi-csi2-rx-subsystem-5.0"; + reg = <0xa0020000 0x10000>; + interrupt-parent = <&gic>; + interrupts = <0 95 4>; + xlnx,csi-pxl-format = <0x2a>; + xlnx,vfb; + xlnx,en-active-lanes; + xlnx,en-csi-v2-0; + xlnx,en-vcx; + clock-names = "lite_aclk", "video_aclk"; + clocks = <&misc_clk_0>, <&misc_clk_1>; + video-reset-gpios = <&gpio 86 GPIO_ACTIVE_LOW>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + /* Sink port */ + reg = <0>; + csiss_in: endpoint { + data-lanes = <1 2 3 4>; + /* MIPI CSI-2 Camera handle */ + remote-endpoint = <&camera_out>; + }; + }; + port@1 { + /* Source port */ + reg = <1>; + csiss_out: endpoint { + remote-endpoint = <&vproc_in>; + }; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 403c17a3ff9b..f3d847832fdc 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -473,6 +473,8 @@ patternProperties: description: ILI Technology Corporation (ILITEK) "^img,.*": description: Imagination Technologies Ltd. + "^imi,.*": + description: Integrated Micro-Electronics Inc. "^incircuit,.*": description: In-Circuit GmbH "^inet-tek,.*": diff --git a/Documentation/driver-api/media/drivers/pvrusb2.rst b/Documentation/driver-api/media/drivers/pvrusb2.rst index 83bfaa531ea8..cbd9359c247a 100644 --- a/Documentation/driver-api/media/drivers/pvrusb2.rst +++ b/Documentation/driver-api/media/drivers/pvrusb2.rst @@ -20,7 +20,7 @@ last known snapshot and evolved the driver to the state it is in here. More information on this driver can be found at: -http://www.isely.net/pvrusb2.html +https://www.isely.net/pvrusb2.html This driver has a strong separation of layers. They are very diff --git a/Documentation/driver-api/media/drivers/tuners.rst b/Documentation/driver-api/media/drivers/tuners.rst index 7509be888909..d7924141c544 100644 --- a/Documentation/driver-api/media/drivers/tuners.rst +++ b/Documentation/driver-api/media/drivers/tuners.rst @@ -18,7 +18,7 @@ These differ mainly by the bandswitch byte. Tuner Manufacturers ------------------- -- SAMSUNG Tuner identification: (e.g. TCPM9091PD27) +- Samsung Tuner identification: (e.g. TCPM9091PD27) .. code-block:: none diff --git a/Documentation/userspace-api/media/cec/cec-ioc-adap-g-caps.rst b/Documentation/userspace-api/media/cec/cec-ioc-adap-g-caps.rst index 94e46a11d68d..436a882dfa31 100644 --- a/Documentation/userspace-api/media/cec/cec-ioc-adap-g-caps.rst +++ b/Documentation/userspace-api/media/cec/cec-ioc-adap-g-caps.rst @@ -58,6 +58,9 @@ returns the information to the application. The ioctl never fails. - The name of this CEC adapter. The combination ``driver`` and ``name`` must be unique. * - __u32 + - ``available_log_addrs`` + - The maximum number of logical addresses that can be configured. + * - __u32 - ``capabilities`` - The capabilities of the CEC adapter, see :ref:`cec-capabilities`. diff --git a/Documentation/userspace-api/media/dvb/fe-get-info.rst b/Documentation/userspace-api/media/dvb/fe-get-info.rst index 80d9f8195ac4..6b3ffd301142 100644 --- a/Documentation/userspace-api/media/dvb/fe-get-info.rst +++ b/Documentation/userspace-api/media/dvb/fe-get-info.rst @@ -34,8 +34,7 @@ Arguments File descriptor returned by :ref:`open() <frontend_f_open>`. ``argp`` - pointer to struct struct - :c:type:`dvb_frontend_info` + pointer to struct :c:type:`dvb_frontend_info` Description diff --git a/Documentation/userspace-api/media/v4l/buffer.rst b/Documentation/userspace-api/media/v4l/buffer.rst index 951ae1ed485f..57e752aaf414 100644 --- a/Documentation/userspace-api/media/v4l/buffer.rst +++ b/Documentation/userspace-api/media/v4l/buffer.rst @@ -23,8 +23,8 @@ argument to the :ref:`VIDIOC_QUERYBUF`, :ref:`VIDIOC_QBUF <VIDIOC_QBUF>` and :ref:`VIDIOC_DQBUF <VIDIOC_QBUF>` ioctl. In the multi-planar API, some plane-specific members of struct :c:type:`v4l2_buffer`, -such as pointers and sizes for each plane, are stored in struct -struct :c:type:`v4l2_plane` instead. In that case, struct +such as pointers and sizes for each plane, are stored in +struct :c:type:`v4l2_plane` instead. In that case, struct :c:type:`v4l2_buffer` contains an array of plane structures. Dequeued video buffers come with timestamps. The driver decides at which @@ -577,7 +577,10 @@ Buffer Flags applications shall use this flag if the data captured in the buffer is not going to be touched by the CPU, instead the buffer will, probably, be passed on to a DMA-capable hardware unit for - further processing or output. + further processing or output. This flag is ignored unless the + queue is used for :ref:`memory mapping <mmap>` streaming I/O and + reports :ref:`V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS + <V4L2-BUF-CAP-SUPPORTS-MMAP-CACHE-HINTS>` capability. * .. _`V4L2-BUF-FLAG-NO-CACHE-CLEAN`: - ``V4L2_BUF_FLAG_NO_CACHE_CLEAN`` @@ -585,7 +588,10 @@ Buffer Flags - Caches do not have to be cleaned for this buffer. Typically applications shall use this flag for output buffers if the data in this buffer has not been created by the CPU but by some - DMA-capable unit, in which case caches have not been used. + DMA-capable unit, in which case caches have not been used. This flag + is ignored unless the queue is used for :ref:`memory mapping <mmap>` + streaming I/O and reports :ref:`V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS + <V4L2-BUF-CAP-SUPPORTS-MMAP-CACHE-HINTS>` capability. * .. _`V4L2-BUF-FLAG-M2M-HOLD-CAPTURE-BUF`: - ``V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF`` @@ -681,6 +687,36 @@ Buffer Flags \normalsize +.. _memory-flags: + +Memory Consistency Flags +======================== + +.. tabularcolumns:: |p{7.0cm}|p{2.2cm}|p{8.3cm}| + +.. cssclass:: longtable + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + :widths: 3 1 4 + + * .. _`V4L2-FLAG-MEMORY-NON-CONSISTENT`: + + - ``V4L2_FLAG_MEMORY_NON_CONSISTENT`` + - 0x00000001 + - A buffer is allocated either in consistent (it will be automatically + coherent between the CPU and the bus) or non-consistent memory. The + latter can provide performance gains, for instance the CPU cache + sync/flush operations can be avoided if the buffer is accessed by the + corresponding device only and the CPU does not read/write to/from that + buffer. However, this requires extra care from the driver -- it must + guarantee memory consistency by issuing a cache flush/sync when + consistency is needed. If this flag is set V4L2 will attempt to + allocate the buffer in non-consistent memory. The flag takes effect + only if the buffer is used for :ref:`memory mapping <mmap>` I/O and the + queue reports the :ref:`V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS + <V4L2-BUF-CAP-SUPPORTS-MMAP-CACHE-HINTS>` capability. .. c:type:: v4l2_memory diff --git a/Documentation/userspace-api/media/v4l/colorspaces-details.rst b/Documentation/userspace-api/media/v4l/colorspaces-details.rst index 79ed6f4f76eb..300c5d2e7d0f 100644 --- a/Documentation/userspace-api/media/v4l/colorspaces-details.rst +++ b/Documentation/userspace-api/media/v4l/colorspaces-details.rst @@ -767,8 +767,8 @@ scaled to [-128…128] and then clipped to [-128…127]. information. So if something other than sRGB is used, then the driver will have to set that information explicitly. Effectively ``V4L2_COLORSPACE_JPEG`` can be considered to be an abbreviation for - ``V4L2_COLORSPACE_SRGB``, ``V4L2_YCBCR_ENC_601`` and - ``V4L2_QUANTIZATION_FULL_RANGE``. + ``V4L2_COLORSPACE_SRGB``, ``V4L2_XFER_FUNC_SRGB``, ``V4L2_YCBCR_ENC_601`` + and ``V4L2_QUANTIZATION_FULL_RANGE``. *************************************** Detailed Transfer Function Descriptions diff --git a/Documentation/userspace-api/media/v4l/dev-decoder.rst b/Documentation/userspace-api/media/v4l/dev-decoder.rst index 606b54947e10..3d4138a4ba69 100644 --- a/Documentation/userspace-api/media/v4l/dev-decoder.rst +++ b/Documentation/userspace-api/media/v4l/dev-decoder.rst @@ -247,7 +247,7 @@ Querying Capabilities Initialization ============== -1. Set the coded format on ``OUTPUT`` via :c:func:`VIDIOC_S_FMT` +1. Set the coded format on ``OUTPUT`` via :c:func:`VIDIOC_S_FMT`. * **Required fields:** @@ -803,7 +803,7 @@ it may be affected as per normal decoder operation. * The decoder will drop all the pending ``OUTPUT`` buffers and they must be treated as returned to the client (following standard semantics). -2. Restart the ``OUTPUT`` queue via :c:func:`VIDIOC_STREAMON` +2. Restart the ``OUTPUT`` queue via :c:func:`VIDIOC_STREAMON`. * **Required fields:** @@ -906,7 +906,9 @@ reflected by corresponding queries): * visible resolution (selection rectangles), -* the minimum number of buffers needed for decoding. +* the minimum number of buffers needed for decoding, + +* bit-depth of the bitstream has been changed. Whenever that happens, the decoder must proceed as follows: @@ -1059,7 +1061,7 @@ sequence was started. ``V4L2_DEC_CMD_STOP`` again while the drain sequence is in progress and they will fail with -EBUSY error code if attempted. - Although mandatory, the availability of decoder commands may be queried + Although not mandatory, the availability of decoder commands may be queried using :c:func:`VIDIOC_TRY_DECODER_CMD`. End of Stream diff --git a/Documentation/userspace-api/media/v4l/dev-encoder.rst b/Documentation/userspace-api/media/v4l/dev-encoder.rst new file mode 100644 index 000000000000..fb44f20924de --- /dev/null +++ b/Documentation/userspace-api/media/v4l/dev-encoder.rst @@ -0,0 +1,753 @@ +.. This file is dual-licensed: you can use it either under the terms +.. of the GPL 2.0 or the GFDL 1.1+ license, at your option. Note that this +.. dual licensing only applies to this file, and not this project as a +.. whole. +.. +.. a) This file is free software; you can redistribute it and/or +.. modify it under the terms of the GNU General Public License as +.. published by the Free Software Foundation version 2 of +.. the License. +.. +.. This file is distributed in the hope that it will be useful, +.. but WITHOUT ANY WARRANTY; without even the implied warranty of +.. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +.. GNU General Public License for more details. +.. +.. Or, alternatively, +.. +.. b) Permission is granted to copy, distribute and/or modify this +.. document under the terms of the GNU Free Documentation License, +.. Version 1.1 or any later version published by the Free Software +.. Foundation, with no Invariant Sections, no Front-Cover Texts +.. and no Back-Cover Texts. A copy of the license is included at +.. Documentation/userspace-api/media/fdl-appendix.rst. +.. +.. TODO: replace it to GPL-2.0 OR GFDL-1.1-or-later WITH no-invariant-sections + +.. _encoder: + +************************************************* +Memory-to-Memory Stateful Video Encoder Interface +************************************************* + +A stateful video encoder takes raw video frames in display order and encodes +them into a bytestream. It generates complete chunks of the bytestream, including +all metadata, headers, etc. The resulting bytestream does not require any +further post-processing by the client. + +Performing software stream processing, header generation etc. in the driver +in order to support this interface is strongly discouraged. In case such +operations are needed, use of the Stateless Video Encoder Interface (in +development) is strongly advised. + +Conventions and Notations Used in This Document +=============================================== + +1. The general V4L2 API rules apply if not specified in this document + otherwise. + +2. The meaning of words "must", "may", "should", etc. is as per `RFC + 2119 <https://tools.ietf.org/html/rfc2119>`_. + +3. All steps not marked "optional" are required. + +4. :c:func:`VIDIOC_G_EXT_CTRLS` and :c:func:`VIDIOC_S_EXT_CTRLS` may be used + interchangeably with :c:func:`VIDIOC_G_CTRL` and :c:func:`VIDIOC_S_CTRL`, + unless specified otherwise. + +5. Single-planar API (see :ref:`planar-apis`) and applicable structures may be + used interchangeably with multi-planar API, unless specified otherwise, + depending on encoder capabilities and following the general V4L2 guidelines. + +6. i = [a..b]: sequence of integers from a to b, inclusive, i.e. i = + [0..2]: i = 0, 1, 2. + +7. Given an ``OUTPUT`` buffer A, then A' represents a buffer on the ``CAPTURE`` + queue containing data that resulted from processing buffer A. + +Glossary +======== + +Refer to :ref:`decoder-glossary`. + +State Machine +============= + +.. kernel-render:: DOT + :alt: DOT digraph of encoder state machine + :caption: Encoder State Machine + + digraph encoder_state_machine { + node [shape = doublecircle, label="Encoding"] Encoding; + + node [shape = circle, label="Initialization"] Initialization; + node [shape = circle, label="Stopped"] Stopped; + node [shape = circle, label="Drain"] Drain; + node [shape = circle, label="Reset"] Reset; + + node [shape = point]; qi + qi -> Initialization [ label = "open()" ]; + + Initialization -> Encoding [ label = "Both queues streaming" ]; + + Encoding -> Drain [ label = "V4L2_ENC_CMD_STOP" ]; + Encoding -> Reset [ label = "VIDIOC_STREAMOFF(CAPTURE)" ]; + Encoding -> Stopped [ label = "VIDIOC_STREAMOFF(OUTPUT)" ]; + Encoding -> Encoding; + + Drain -> Stopped [ label = "All CAPTURE\nbuffers dequeued\nor\nVIDIOC_STREAMOFF(OUTPUT)" ]; + Drain -> Reset [ label = "VIDIOC_STREAMOFF(CAPTURE)" ]; + + Reset -> Encoding [ label = "VIDIOC_STREAMON(CAPTURE)" ]; + Reset -> Initialization [ label = "VIDIOC_REQBUFS(OUTPUT, 0)" ]; + + Stopped -> Encoding [ label = "V4L2_ENC_CMD_START\nor\nVIDIOC_STREAMON(OUTPUT)" ]; + Stopped -> Reset [ label = "VIDIOC_STREAMOFF(CAPTURE)" ]; + } + +Querying Capabilities +===================== + +1. To enumerate the set of coded formats supported by the encoder, the + client may call :c:func:`VIDIOC_ENUM_FMT` on ``CAPTURE``. + + * The full set of supported formats will be returned, regardless of the + format set on ``OUTPUT``. + +2. To enumerate the set of supported raw formats, the client may call + :c:func:`VIDIOC_ENUM_FMT` on ``OUTPUT``. + + * Only the formats supported for the format currently active on ``CAPTURE`` + will be returned. + + * In order to enumerate raw formats supported by a given coded format, + the client must first set that coded format on ``CAPTURE`` and then + enumerate the formats on ``OUTPUT``. + +3. The client may use :c:func:`VIDIOC_ENUM_FRAMESIZES` to detect supported + resolutions for a given format, passing the desired pixel format in + :c:type:`v4l2_frmsizeenum` ``pixel_format``. + + * Values returned by :c:func:`VIDIOC_ENUM_FRAMESIZES` for a coded pixel + format will include all possible coded resolutions supported by the + encoder for the given coded pixel format. + + * Values returned by :c:func:`VIDIOC_ENUM_FRAMESIZES` for a raw pixel format + will include all possible frame buffer resolutions supported by the + encoder for the given raw pixel format and coded format currently set on + ``CAPTURE``. + +4. The client may use :c:func:`VIDIOC_ENUM_FRAMEINTERVALS` to detect supported + frame intervals for a given format and resolution, passing the desired pixel + format in :c:type:`v4l2_frmsizeenum` ``pixel_format`` and the resolution + in :c:type:`v4l2_frmsizeenum` ``width`` and :c:type:`v4l2_frmsizeenum` + ``height``. + + * Values returned by :c:func:`VIDIOC_ENUM_FRAMEINTERVALS` for a coded pixel + format and coded resolution will include all possible frame intervals + supported by the encoder for the given coded pixel format and resolution. + + * Values returned by :c:func:`VIDIOC_ENUM_FRAMEINTERVALS` for a raw pixel + format and resolution will include all possible frame intervals supported + by the encoder for the given raw pixel format and resolution and for the + coded format, coded resolution and coded frame interval currently set on + ``CAPTURE``. + + * Support for :c:func:`VIDIOC_ENUM_FRAMEINTERVALS` is optional. If it is + not implemented, then there are no special restrictions other than the + limits of the codec itself. + +5. Supported profiles and levels for the coded format currently set on + ``CAPTURE``, if applicable, may be queried using their respective controls + via :c:func:`VIDIOC_QUERYCTRL`. + +6. Any additional encoder capabilities may be discovered by querying + their respective controls. + +Initialization +============== + +1. Set the coded format on the ``CAPTURE`` queue via :c:func:`VIDIOC_S_FMT`. + + * **Required fields:** + + ``type`` + a ``V4L2_BUF_TYPE_*`` enum appropriate for ``CAPTURE``. + + ``pixelformat`` + the coded format to be produced. + + ``sizeimage`` + desired size of ``CAPTURE`` buffers; the encoder may adjust it to + match hardware requirements. + + ``width``, ``height`` + ignored (read-only). + + other fields + follow standard semantics. + + * **Return fields:** + + ``sizeimage`` + adjusted size of ``CAPTURE`` buffers. + + ``width``, ``height`` + the coded size selected by the encoder based on current state, e.g. + ``OUTPUT`` format, selection rectangles, etc. (read-only). + + .. important:: + + Changing the ``CAPTURE`` format may change the currently set ``OUTPUT`` + format. How the new ``OUTPUT`` format is determined is up to the encoder + and the client must ensure it matches its needs afterwards. + +2. **Optional.** Enumerate supported ``OUTPUT`` formats (raw formats for + source) for the selected coded format via :c:func:`VIDIOC_ENUM_FMT`. + + * **Required fields:** + + ``type`` + a ``V4L2_BUF_TYPE_*`` enum appropriate for ``OUTPUT``. + + other fields + follow standard semantics. + + * **Return fields:** + + ``pixelformat`` + raw format supported for the coded format currently selected on + the ``CAPTURE`` queue. + + other fields + follow standard semantics. + +3. Set the raw source format on the ``OUTPUT`` queue via + :c:func:`VIDIOC_S_FMT`. + + * **Required fields:** + + ``type`` + a ``V4L2_BUF_TYPE_*`` enum appropriate for ``OUTPUT``. + + ``pixelformat`` + raw format of the source. + + ``width``, ``height`` + source resolution. + + other fields + follow standard semantics. + + * **Return fields:** + + ``width``, ``height`` + may be adjusted to match encoder minimums, maximums and alignment + requirements, as required by the currently selected formats, as + reported by :c:func:`VIDIOC_ENUM_FRAMESIZES`. + + other fields + follow standard semantics. + + * Setting the ``OUTPUT`` format will reset the selection rectangles to their + default values, based on the new resolution, as described in the next + step. + +4. Set the raw frame interval on the ``OUTPUT`` queue via + :c:func:`VIDIOC_S_PARM`. This also sets the coded frame interval on the + ``CAPTURE`` queue to the same value. + + * ** Required fields:** + + ``type`` + a ``V4L2_BUF_TYPE_*`` enum appropriate for ``OUTPUT``. + + ``parm.output`` + set all fields except ``parm.output.timeperframe`` to 0. + + ``parm.output.timeperframe`` + the desired frame interval; the encoder may adjust it to + match hardware requirements. + + * **Return fields:** + + ``parm.output.timeperframe`` + the adjusted frame interval. + + .. important:: + + Changing the ``OUTPUT`` frame interval *also* sets the framerate that + the encoder uses to encode the video. So setting the frame interval + to 1/24 (or 24 frames per second) will produce a coded video stream + that can be played back at that speed. The frame interval for the + ``OUTPUT`` queue is just a hint, the application may provide raw + frames at a different rate. It can be used by the driver to help + schedule multiple encoders running in parallel. + + In the next step the ``CAPTURE`` frame interval can optionally be + changed to a different value. This is useful for off-line encoding + were the coded frame interval can be different from the rate at + which raw frames are supplied. + + .. important:: + + ``timeperframe`` deals with *frames*, not fields. So for interlaced + formats this is the time per two fields, since a frame consists of + a top and a bottom field. + + .. note:: + + It is due to historical reasons that changing the ``OUTPUT`` frame + interval also changes the coded frame interval on the ``CAPTURE`` + queue. Ideally these would be independent settings, but that would + break the existing API. + +5. **Optional** Set the coded frame interval on the ``CAPTURE`` queue via + :c:func:`VIDIOC_S_PARM`. This is only necessary if the coded frame + interval is different from the raw frame interval, which is typically + the case for off-line encoding. Support for this feature is signalled + by the :ref:`V4L2_FMT_FLAG_ENC_CAP_FRAME_INTERVAL <fmtdesc-flags>` format flag. + + * ** Required fields:** + + ``type`` + a ``V4L2_BUF_TYPE_*`` enum appropriate for ``CAPTURE``. + + ``parm.capture`` + set all fields except ``parm.capture.timeperframe`` to 0. + + ``parm.capture.timeperframe`` + the desired coded frame interval; the encoder may adjust it to + match hardware requirements. + + * **Return fields:** + + ``parm.capture.timeperframe`` + the adjusted frame interval. + + .. important:: + + Changing the ``CAPTURE`` frame interval sets the framerate for the + coded video. It does *not* set the rate at which buffers arrive on the + ``CAPTURE`` queue, that depends on how fast the encoder is and how + fast raw frames are queued on the ``OUTPUT`` queue. + + .. important:: + + ``timeperframe`` deals with *frames*, not fields. So for interlaced + formats this is the time per two fields, since a frame consists of + a top and a bottom field. + + .. note:: + + Not all drivers support this functionality, in that case just set + the desired coded frame interval for the ``OUTPUT`` queue. + + However, drivers that can schedule multiple encoders based on the + ``OUTPUT`` frame interval must support this optional feature. + +6. **Optional.** Set the visible resolution for the stream metadata via + :c:func:`VIDIOC_S_SELECTION` on the ``OUTPUT`` queue if it is desired + to be different than the full OUTPUT resolution. + + * **Required fields:** + + ``type`` + a ``V4L2_BUF_TYPE_*`` enum appropriate for ``OUTPUT``. + + ``target`` + set to ``V4L2_SEL_TGT_CROP``. + + ``r.left``, ``r.top``, ``r.width``, ``r.height`` + visible rectangle; this must fit within the `V4L2_SEL_TGT_CROP_BOUNDS` + rectangle and may be subject to adjustment to match codec and + hardware constraints. + + * **Return fields:** + + ``r.left``, ``r.top``, ``r.width``, ``r.height`` + visible rectangle adjusted by the encoder. + + * The following selection targets are supported on ``OUTPUT``: + + ``V4L2_SEL_TGT_CROP_BOUNDS`` + equal to the full source frame, matching the active ``OUTPUT`` + format. + + ``V4L2_SEL_TGT_CROP_DEFAULT`` + equal to ``V4L2_SEL_TGT_CROP_BOUNDS``. + + ``V4L2_SEL_TGT_CROP`` + rectangle within the source buffer to be encoded into the + ``CAPTURE`` stream; defaults to ``V4L2_SEL_TGT_CROP_DEFAULT``. + + .. note:: + + A common use case for this selection target is encoding a source + video with a resolution that is not a multiple of a macroblock, + e.g. the common 1920x1080 resolution may require the source + buffers to be aligned to 1920x1088 for codecs with 16x16 macroblock + size. To avoid encoding the padding, the client needs to explicitly + configure this selection target to 1920x1080. + + .. warning:: + + The encoder may adjust the crop/compose rectangles to the nearest + supported ones to meet codec and hardware requirements. The client needs + to check the adjusted rectangle returned by :c:func:`VIDIOC_S_SELECTION`. + +7. Allocate buffers for both ``OUTPUT`` and ``CAPTURE`` via + :c:func:`VIDIOC_REQBUFS`. This may be performed in any order. + + * **Required fields:** + + ``count`` + requested number of buffers to allocate; greater than zero. + + ``type`` + a ``V4L2_BUF_TYPE_*`` enum appropriate for ``OUTPUT`` or + ``CAPTURE``. + + other fields + follow standard semantics. + + * **Return fields:** + + ``count`` + actual number of buffers allocated. + + .. warning:: + + The actual number of allocated buffers may differ from the ``count`` + given. The client must check the updated value of ``count`` after the + call returns. + + .. note:: + + To allocate more than the minimum number of OUTPUT buffers (for pipeline + depth), the client may query the ``V4L2_CID_MIN_BUFFERS_FOR_OUTPUT`` + control to get the minimum number of buffers required, and pass the + obtained value plus the number of additional buffers needed in the + ``count`` field to :c:func:`VIDIOC_REQBUFS`. + + Alternatively, :c:func:`VIDIOC_CREATE_BUFS` can be used to have more + control over buffer allocation. + + * **Required fields:** + + ``count`` + requested number of buffers to allocate; greater than zero. + + ``type`` + a ``V4L2_BUF_TYPE_*`` enum appropriate for ``OUTPUT``. + + other fields + follow standard semantics. + + * **Return fields:** + + ``count`` + adjusted to the number of allocated buffers. + +8. Begin streaming on both ``OUTPUT`` and ``CAPTURE`` queues via + :c:func:`VIDIOC_STREAMON`. This may be performed in any order. The actual + encoding process starts when both queues start streaming. + +.. note:: + + If the client stops the ``CAPTURE`` queue during the encode process and then + restarts it again, the encoder will begin generating a stream independent + from the stream generated before the stop. The exact constraints depend + on the coded format, but may include the following implications: + + * encoded frames produced after the restart must not reference any + frames produced before the stop, e.g. no long term references for + H.264/HEVC, + + * any headers that must be included in a standalone stream must be + produced again, e.g. SPS and PPS for H.264/HEVC. + +Encoding +======== + +This state is reached after the `Initialization` sequence finishes +successfully. In this state, the client queues and dequeues buffers to both +queues via :c:func:`VIDIOC_QBUF` and :c:func:`VIDIOC_DQBUF`, following the +standard semantics. + +The content of encoded ``CAPTURE`` buffers depends on the active coded pixel +format and may be affected by codec-specific extended controls, as stated +in the documentation of each format. + +Both queues operate independently, following standard behavior of V4L2 buffer +queues and memory-to-memory devices. In addition, the order of encoded frames +dequeued from the ``CAPTURE`` queue may differ from the order of queuing raw +frames to the ``OUTPUT`` queue, due to properties of the selected coded format, +e.g. frame reordering. + +The client must not assume any direct relationship between ``CAPTURE`` and +``OUTPUT`` buffers and any specific timing of buffers becoming +available to dequeue. Specifically: + +* a buffer queued to ``OUTPUT`` may result in more than one buffer produced on + ``CAPTURE`` (for example, if returning an encoded frame allowed the encoder + to return a frame that preceded it in display, but succeeded it in the decode + order; however, there may be other reasons for this as well), + +* a buffer queued to ``OUTPUT`` may result in a buffer being produced on + ``CAPTURE`` later into encode process, and/or after processing further + ``OUTPUT`` buffers, or be returned out of order, e.g. if display + reordering is used, + +* buffers may become available on the ``CAPTURE`` queue without additional + buffers queued to ``OUTPUT`` (e.g. during drain or ``EOS``), because of the + ``OUTPUT`` buffers queued in the past whose encoding results are only + available at later time, due to specifics of the encoding process, + +* buffers queued to ``OUTPUT`` may not become available to dequeue instantly + after being encoded into a corresponding ``CAPTURE`` buffer, e.g. if the + encoder needs to use the frame as a reference for encoding further frames. + +.. note:: + + To allow matching encoded ``CAPTURE`` buffers with ``OUTPUT`` buffers they + originated from, the client can set the ``timestamp`` field of the + :c:type:`v4l2_buffer` struct when queuing an ``OUTPUT`` buffer. The + ``CAPTURE`` buffer(s), which resulted from encoding that ``OUTPUT`` buffer + will have their ``timestamp`` field set to the same value when dequeued. + + In addition to the straightforward case of one ``OUTPUT`` buffer producing + one ``CAPTURE`` buffer, the following cases are defined: + + * one ``OUTPUT`` buffer generates multiple ``CAPTURE`` buffers: the same + ``OUTPUT`` timestamp will be copied to multiple ``CAPTURE`` buffers, + + * the encoding order differs from the presentation order (i.e. the + ``CAPTURE`` buffers are out-of-order compared to the ``OUTPUT`` buffers): + ``CAPTURE`` timestamps will not retain the order of ``OUTPUT`` timestamps. + +.. note:: + + To let the client distinguish between frame types (keyframes, intermediate + frames; the exact list of types depends on the coded format), the + ``CAPTURE`` buffers will have corresponding flag bits set in their + :c:type:`v4l2_buffer` struct when dequeued. See the documentation of + :c:type:`v4l2_buffer` and each coded pixel format for exact list of flags + and their meanings. + +Should an encoding error occur, it will be reported to the client with the level +of details depending on the encoder capabilities. Specifically: + +* the ``CAPTURE`` buffer (if any) that contains the results of the failed encode + operation will be returned with the ``V4L2_BUF_FLAG_ERROR`` flag set, + +* if the encoder is able to precisely report the ``OUTPUT`` buffer(s) that triggered + the error, such buffer(s) will be returned with the ``V4L2_BUF_FLAG_ERROR`` flag + set. + +.. note:: + + If a ``CAPTURE`` buffer is too small then it is just returned with the + ``V4L2_BUF_FLAG_ERROR`` flag set. More work is needed to detect that this + error occurred because the buffer was too small, and to provide support to + free existing buffers that were too small. + +In case of a fatal failure that does not allow the encoding to continue, any +further operations on corresponding encoder file handle will return the -EIO +error code. The client may close the file handle and open a new one, or +alternatively reinitialize the instance by stopping streaming on both queues, +releasing all buffers and performing the Initialization sequence again. + +Encoding Parameter Changes +========================== + +The client is allowed to use :c:func:`VIDIOC_S_CTRL` to change encoder +parameters at any time. The availability of parameters is encoder-specific +and the client must query the encoder to find the set of available controls. + +The ability to change each parameter during encoding is encoder-specific, as +per the standard semantics of the V4L2 control interface. The client may +attempt to set a control during encoding and if the operation fails with the +-EBUSY error code, the ``CAPTURE`` queue needs to be stopped for the +configuration change to be allowed. To do this, it may follow the `Drain` +sequence to avoid losing the already queued/encoded frames. + +The timing of parameter updates is encoder-specific, as per the standard +semantics of the V4L2 control interface. If the client needs to apply the +parameters exactly at specific frame, using the Request API +(:ref:`media-request-api`) should be considered, if supported by the encoder. + +Drain +===== + +To ensure that all the queued ``OUTPUT`` buffers have been processed and the +related ``CAPTURE`` buffers are given to the client, the client must follow the +drain sequence described below. After the drain sequence ends, the client has +received all encoded frames for all ``OUTPUT`` buffers queued before the +sequence was started. + +1. Begin the drain sequence by issuing :c:func:`VIDIOC_ENCODER_CMD`. + + * **Required fields:** + + ``cmd`` + set to ``V4L2_ENC_CMD_STOP``. + + ``flags`` + set to 0. + + ``pts`` + set to 0. + + .. warning:: + + The sequence can be only initiated if both ``OUTPUT`` and ``CAPTURE`` + queues are streaming. For compatibility reasons, the call to + :c:func:`VIDIOC_ENCODER_CMD` will not fail even if any of the queues is + not streaming, but at the same time it will not initiate the `Drain` + sequence and so the steps described below would not be applicable. + +2. Any ``OUTPUT`` buffers queued by the client before the + :c:func:`VIDIOC_ENCODER_CMD` was issued will be processed and encoded as + normal. The client must continue to handle both queues independently, + similarly to normal encode operation. This includes: + + * queuing and dequeuing ``CAPTURE`` buffers, until a buffer marked with the + ``V4L2_BUF_FLAG_LAST`` flag is dequeued, + + .. warning:: + + The last buffer may be empty (with :c:type:`v4l2_buffer` + ``bytesused`` = 0) and in that case it must be ignored by the client, + as it does not contain an encoded frame. + + .. note:: + + Any attempt to dequeue more ``CAPTURE`` buffers beyond the buffer + marked with ``V4L2_BUF_FLAG_LAST`` will result in a -EPIPE error from + :c:func:`VIDIOC_DQBUF`. + + * dequeuing processed ``OUTPUT`` buffers, until all the buffers queued + before the ``V4L2_ENC_CMD_STOP`` command are dequeued, + + * dequeuing the ``V4L2_EVENT_EOS`` event, if the client subscribes to it. + + .. note:: + + For backwards compatibility, the encoder will signal a ``V4L2_EVENT_EOS`` + event when the last frame has been encoded and all frames are ready to be + dequeued. It is deprecated behavior and the client must not rely on it. + The ``V4L2_BUF_FLAG_LAST`` buffer flag should be used instead. + +3. Once all ``OUTPUT`` buffers queued before the ``V4L2_ENC_CMD_STOP`` call are + dequeued and the last ``CAPTURE`` buffer is dequeued, the encoder is stopped + and it will accept, but not process any newly queued ``OUTPUT`` buffers + until the client issues any of the following operations: + + * ``V4L2_ENC_CMD_START`` - the encoder will not be reset and will resume + operation normally, with all the state from before the drain, + + * a pair of :c:func:`VIDIOC_STREAMOFF` and :c:func:`VIDIOC_STREAMON` on the + ``CAPTURE`` queue - the encoder will be reset (see the `Reset` sequence) + and then resume encoding, + + * a pair of :c:func:`VIDIOC_STREAMOFF` and :c:func:`VIDIOC_STREAMON` on the + ``OUTPUT`` queue - the encoder will resume operation normally, however any + source frames queued to the ``OUTPUT`` queue between ``V4L2_ENC_CMD_STOP`` + and :c:func:`VIDIOC_STREAMOFF` will be discarded. + +.. note:: + + Once the drain sequence is initiated, the client needs to drive it to + completion, as described by the steps above, unless it aborts the process by + issuing :c:func:`VIDIOC_STREAMOFF` on any of the ``OUTPUT`` or ``CAPTURE`` + queues. The client is not allowed to issue ``V4L2_ENC_CMD_START`` or + ``V4L2_ENC_CMD_STOP`` again while the drain sequence is in progress and they + will fail with -EBUSY error code if attempted. + + For reference, handling of various corner cases is described below: + + * In case of no buffer in the ``OUTPUT`` queue at the time the + ``V4L2_ENC_CMD_STOP`` command was issued, the drain sequence completes + immediately and the encoder returns an empty ``CAPTURE`` buffer with the + ``V4L2_BUF_FLAG_LAST`` flag set. + + * In case of no buffer in the ``CAPTURE`` queue at the time the drain + sequence completes, the next time the client queues a ``CAPTURE`` buffer + it is returned at once as an empty buffer with the ``V4L2_BUF_FLAG_LAST`` + flag set. + + * If :c:func:`VIDIOC_STREAMOFF` is called on the ``CAPTURE`` queue in the + middle of the drain sequence, the drain sequence is canceled and all + ``CAPTURE`` buffers are implicitly returned to the client. + + * If :c:func:`VIDIOC_STREAMOFF` is called on the ``OUTPUT`` queue in the + middle of the drain sequence, the drain sequence completes immediately and + next ``CAPTURE`` buffer will be returned empty with the + ``V4L2_BUF_FLAG_LAST`` flag set. + + Although not mandatory, the availability of encoder commands may be queried + using :c:func:`VIDIOC_TRY_ENCODER_CMD`. + +Reset +===== + +The client may want to request the encoder to reinitialize the encoding, so +that the following stream data becomes independent from the stream data +generated before. Depending on the coded format, that may imply that: + +* encoded frames produced after the restart must not reference any frames + produced before the stop, e.g. no long term references for H.264/HEVC, + +* any headers that must be included in a standalone stream must be produced + again, e.g. SPS and PPS for H.264/HEVC. + +This can be achieved by performing the reset sequence. + +1. Perform the `Drain` sequence to ensure all the in-flight encoding finishes + and respective buffers are dequeued. + +2. Stop streaming on the ``CAPTURE`` queue via :c:func:`VIDIOC_STREAMOFF`. This + will return all currently queued ``CAPTURE`` buffers to the client, without + valid frame data. + +3. Start streaming on the ``CAPTURE`` queue via :c:func:`VIDIOC_STREAMON` and + continue with regular encoding sequence. The encoded frames produced into + ``CAPTURE`` buffers from now on will contain a standalone stream that can be + decoded without the need for frames encoded before the reset sequence, + starting at the first ``OUTPUT`` buffer queued after issuing the + `V4L2_ENC_CMD_STOP` of the `Drain` sequence. + +This sequence may be also used to change encoding parameters for encoders +without the ability to change the parameters on the fly. + +Commit Points +============= + +Setting formats and allocating buffers triggers changes in the behavior of the +encoder. + +1. Setting the format on the ``CAPTURE`` queue may change the set of formats + supported/advertised on the ``OUTPUT`` queue. In particular, it also means + that the ``OUTPUT`` format may be reset and the client must not rely on the + previously set format being preserved. + +2. Enumerating formats on the ``OUTPUT`` queue always returns only formats + supported for the current ``CAPTURE`` format. + +3. Setting the format on the ``OUTPUT`` queue does not change the list of + formats available on the ``CAPTURE`` queue. An attempt to set the ``OUTPUT`` + format that is not supported for the currently selected ``CAPTURE`` format + will result in the encoder adjusting the requested ``OUTPUT`` format to a + supported one. + +4. Enumerating formats on the ``CAPTURE`` queue always returns the full set of + supported coded formats, irrespective of the current ``OUTPUT`` format. + +5. While buffers are allocated on any of the ``OUTPUT`` or ``CAPTURE`` queues, + the client must not change the format on the ``CAPTURE`` queue. Drivers will + return the -EBUSY error code for any such format change attempt. + +To summarize, setting formats and allocation must always start with the +``CAPTURE`` queue and the ``CAPTURE`` queue is the master that governs the +set of supported formats for the ``OUTPUT`` queue. diff --git a/Documentation/userspace-api/media/v4l/dev-mem2mem.rst b/Documentation/userspace-api/media/v4l/dev-mem2mem.rst index 9279d87c08a1..40aff9c95267 100644 --- a/Documentation/userspace-api/media/v4l/dev-mem2mem.rst +++ b/Documentation/userspace-api/media/v4l/dev-mem2mem.rst @@ -46,4 +46,5 @@ devices are given in the following sections. :maxdepth: 1 dev-decoder + dev-encoder dev-stateless-decoder diff --git a/Documentation/userspace-api/media/v4l/dev-osd.rst b/Documentation/userspace-api/media/v4l/dev-osd.rst index 67dc46373a91..ad0c156c7898 100644 --- a/Documentation/userspace-api/media/v4l/dev-osd.rst +++ b/Documentation/userspace-api/media/v4l/dev-osd.rst @@ -51,7 +51,7 @@ other information, the physical address of the framebuffer in the ``base`` field of struct :c:type:`v4l2_framebuffer`. The framebuffer device ioctl ``FBIOGET_FSCREENINFO`` returns the same address in the ``smem_start`` field of struct -struct :c:type:`fb_fix_screeninfo`. The ``FBIOGET_FSCREENINFO`` +:c:type:`fb_fix_screeninfo`. The ``FBIOGET_FSCREENINFO`` ioctl and struct :c:type:`fb_fix_screeninfo` are defined in the ``linux/fb.h`` header file. diff --git a/Documentation/userspace-api/media/v4l/dev-sdr.rst b/Documentation/userspace-api/media/v4l/dev-sdr.rst index c9563bca444e..4a80319a53c6 100644 --- a/Documentation/userspace-api/media/v4l/dev-sdr.rst +++ b/Documentation/userspace-api/media/v4l/dev-sdr.rst @@ -78,7 +78,7 @@ field of a struct :c:type:`v4l2_format` to ``V4L2_BUF_TYPE_SDR_CAPTURE`` or ``V4L2_BUF_TYPE_SDR_OUTPUT`` and use the struct :c:type:`v4l2_sdr_format` ``sdr`` member of the ``fmt`` union as needed per the desired operation. Currently -there is two fields, ``pixelformat`` and ``buffersize``, of struct +there are two fields, ``pixelformat`` and ``buffersize``, of struct :c:type:`v4l2_sdr_format` which are used. Content of the ``pixelformat`` is V4L2 FourCC code of the data format. The ``buffersize`` field is maximum buffer size in bytes required for diff --git a/Documentation/userspace-api/media/v4l/hist-v4l2.rst b/Documentation/userspace-api/media/v4l/hist-v4l2.rst index 7913d017cd33..6dcfe6046e33 100644 --- a/Documentation/userspace-api/media/v4l/hist-v4l2.rst +++ b/Documentation/userspace-api/media/v4l/hist-v4l2.rst @@ -43,7 +43,7 @@ transmission arguments. 1998-09-28: Revamped video standard. Made video controls individually enumerable. -1998-10-02: The ``id`` field was removed from struct +1998-10-02: The ``id`` field was removed from struct ``video_standard`` and the color subcarrier fields were renamed. The :ref:`VIDIOC_QUERYSTD` ioctl was renamed to :ref:`VIDIOC_ENUMSTD`, @@ -260,7 +260,7 @@ multiple tuners into account.) 2000-09-18: ``V4L2_BUF_TYPE_VBI`` was added. This may *break compatibility* as the :ref:`VIDIOC_G_FMT <VIDIOC_G_FMT>` and -:ref:`VIDIOC_S_FMT <VIDIOC_G_FMT>` ioctls may fail now if the struct +:ref:`VIDIOC_S_FMT <VIDIOC_G_FMT>` ioctls may fail now if the struct ``v4l2_fmt`` ``type`` field does not contain ``V4L2_BUF_TYPE_VBI``. In the documentation of the struct :c:type:`v4l2_vbi_format` ``offset`` field the diff --git a/Documentation/userspace-api/media/v4l/pixfmt-srggb14p.rst b/Documentation/userspace-api/media/v4l/pixfmt-srggb14p.rst index ec1239ada316..e2f5a2b36092 100644 --- a/Documentation/userspace-api/media/v4l/pixfmt-srggb14p.rst +++ b/Documentation/userspace-api/media/v4l/pixfmt-srggb14p.rst @@ -69,37 +69,37 @@ Each cell is one byte. B\ :sub:`00low bits 5--0`\ (bits 5--0) - - R\ :sub:`02low bits 3--0`\ (bits 7--4) + - B\ :sub:`02low bits 3--0`\ (bits 7--4) G\ :sub:`01low bits 5--2`\ (bits 3--0) - G\ :sub:`03low bits 5--0`\ (bits 7--2) - R\ :sub:`02low bits 5--4`\ (bits 1--0) + B\ :sub:`02low bits 5--4`\ (bits 1--0) - .. row 2 - start + 7 - - G\ :sub:`00high` + - G\ :sub:`10high` - - R\ :sub:`01high` + - R\ :sub:`11high` - - G\ :sub:`02high` + - G\ :sub:`12high` - - R\ :sub:`03high` + - R\ :sub:`13high` - - R\ :sub:`01low bits 1--0`\ (bits 7--6) + - R\ :sub:`11low bits 1--0`\ (bits 7--6) - G\ :sub:`00low bits 5--0`\ (bits 5--0) + G\ :sub:`10low bits 5--0`\ (bits 5--0) - - G\ :sub:`02low bits 3--0`\ (bits 7--4) + - G\ :sub:`12low bits 3--0`\ (bits 7--4) - R\ :sub:`01low bits 5--2`\ (bits 3--0) + R\ :sub:`11low bits 5--2`\ (bits 3--0) - - R\ :sub:`03low bits 5--0`\ (bits 7--2) + - R\ :sub:`13low bits 5--0`\ (bits 7--2) - G\ :sub:`02low bits 5--4`\ (bits 1--0) + G\ :sub:`12low bits 5--4`\ (bits 1--0) - .. row 3 @@ -117,13 +117,13 @@ Each cell is one byte. B\ :sub:`20low bits 5--0`\ (bits 5--0) - - R\ :sub:`22low bits 3--0`\ (bits 7--4) + - B\ :sub:`22low bits 3--0`\ (bits 7--4) G\ :sub:`21low bits 5--2`\ (bits 3--0) - G\ :sub:`23low bits 5--0`\ (bits 7--2) - R\ :sub:`22low bits 5--4`\ (bits 1--0) + B\ :sub:`22low bits 5--4`\ (bits 1--0) - .. row 4 diff --git a/Documentation/userspace-api/media/v4l/pixfmt-v4l2.rst b/Documentation/userspace-api/media/v4l/pixfmt-v4l2.rst index 759420a872d6..e0ee2823ab1f 100644 --- a/Documentation/userspace-api/media/v4l/pixfmt-v4l2.rst +++ b/Documentation/userspace-api/media/v4l/pixfmt-v4l2.rst @@ -44,6 +44,11 @@ Single-planar format structure inside the stream, when fed to a stateful mem2mem decoder, the fields may be zero to rely on the decoder to detect the right values. For more details see :ref:`decoder` and format descriptions. + + For compressed formats on the CAPTURE side of a stateful mem2mem + encoder, the fields must be zero, since the coded size is expected to + be calculated internally by the encoder itself, based on the OUTPUT + side. For more details see :ref:`encoder` and format descriptions. * - __u32 - ``pixelformat`` - The pixel format or type of compression, set by the application. diff --git a/Documentation/userspace-api/media/v4l/v4l2.rst b/Documentation/userspace-api/media/v4l/v4l2.rst index ab7c97c39b97..35796c4fbe52 100644 --- a/Documentation/userspace-api/media/v4l/v4l2.rst +++ b/Documentation/userspace-api/media/v4l/v4l2.rst @@ -63,6 +63,7 @@ Authors, in alphabetical order: - Figa, Tomasz <tfiga@chromium.org> - Documented the memory-to-memory decoder interface. + - Documented the memory-to-memory encoder interface. - H Schimek, Michael <mschimek@gmx.at> @@ -75,6 +76,7 @@ Authors, in alphabetical order: - Osciak, Pawel <posciak@chromium.org> - Documented the memory-to-memory decoder interface. + - Documented the memory-to-memory encoder interface. - Osciak, Pawel <pawel@osciak.com> diff --git a/Documentation/userspace-api/media/v4l/vidioc-create-bufs.rst b/Documentation/userspace-api/media/v4l/vidioc-create-bufs.rst index e1afc5b504c2..f2a702870fad 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-create-bufs.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-create-bufs.rst @@ -121,7 +121,12 @@ than the number requested. other changes, then set ``count`` to 0, ``memory`` to ``V4L2_MEMORY_MMAP`` and ``format.type`` to the buffer type. * - __u32 - - ``reserved``\ [7] + - ``flags`` + - Specifies additional buffer management attributes. + See :ref:`memory-flags`. + + * - __u32 + - ``reserved``\ [6] - A place holder for future extensions. Drivers and applications must set the array to zero. diff --git a/Documentation/userspace-api/media/v4l/vidioc-dqevent.rst b/Documentation/userspace-api/media/v4l/vidioc-dqevent.rst index a9a176d5256d..9412be0c3747 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-dqevent.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-dqevent.rst @@ -260,7 +260,7 @@ call. :ref:`v4l2_queryctrl <v4l2-queryctrl>`. * - __s32 - ``default_value`` - - The default value value of the control. See struct + - The default value of the control. See struct :ref:`v4l2_queryctrl <v4l2-queryctrl>`. diff --git a/Documentation/userspace-api/media/v4l/vidioc-encoder-cmd.rst b/Documentation/userspace-api/media/v4l/vidioc-encoder-cmd.rst index 16269b3b1715..d0eacce5485e 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-encoder-cmd.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-encoder-cmd.rst @@ -51,25 +51,26 @@ To send a command applications must initialize all fields of a struct ``VIDIOC_ENCODER_CMD`` or ``VIDIOC_TRY_ENCODER_CMD`` with a pointer to this structure. -The ``cmd`` field must contain the command code. The ``flags`` field is -currently only used by the STOP command and contains one bit: If the -``V4L2_ENC_CMD_STOP_AT_GOP_END`` flag is set, encoding will continue -until the end of the current *Group Of Pictures*, otherwise it will stop -immediately. +The ``cmd`` field must contain the command code. Some commands use the +``flags`` field for additional information. -A :ref:`read() <func-read>` or :ref:`VIDIOC_STREAMON <VIDIOC_STREAMON>` -call sends an implicit START command to the encoder if it has not been -started yet. After a STOP command, :ref:`read() <func-read>` calls will read +After a STOP command, :ref:`read() <func-read>` calls will read the remaining data buffered by the driver. When the buffer is empty, :ref:`read() <func-read>` will return zero and the next :ref:`read() <func-read>` call will restart the encoder. +A :ref:`read() <func-read>` or :ref:`VIDIOC_STREAMON <VIDIOC_STREAMON>` +call sends an implicit START command to the encoder if it has not been +started yet. Applies to both queues of mem2mem encoders. + A :ref:`close() <func-close>` or :ref:`VIDIOC_STREAMOFF <VIDIOC_STREAMON>` call of a streaming file descriptor sends an implicit immediate STOP to -the encoder, and all buffered data is discarded. +the encoder, and all buffered data is discarded. Applies to both queues of +mem2mem encoders. These ioctls are optional, not all drivers may support them. They were -introduced in Linux 2.6.21. +introduced in Linux 2.6.21. They are, however, mandatory for stateful mem2mem +encoders (as further documented in :ref:`encoder`). .. tabularcolumns:: |p{4.4cm}|p{4.4cm}|p{8.7cm}| @@ -109,21 +110,24 @@ introduced in Linux 2.6.21. - 0 - Start the encoder. When the encoder is already running or paused, this command does nothing. No flags are defined for this command. + + For a device implementing the :ref:`encoder`, once the drain sequence + is initiated with the ``V4L2_ENC_CMD_STOP`` command, it must be driven + to completion before this command can be invoked. Any attempt to + invoke the command while the drain sequence is in progress will trigger + an ``EBUSY`` error code. See :ref:`encoder` for more details. * - ``V4L2_ENC_CMD_STOP`` - 1 - Stop the encoder. When the ``V4L2_ENC_CMD_STOP_AT_GOP_END`` flag is set, encoding will continue until the end of the current *Group Of Pictures*, otherwise encoding will stop immediately. When the - encoder is already stopped, this command does nothing. mem2mem - encoders will send a ``V4L2_EVENT_EOS`` event when the last frame - has been encoded and all frames are ready to be dequeued and will - set the ``V4L2_BUF_FLAG_LAST`` buffer flag on the last buffer of - the capture queue to indicate there will be no new buffers - produced to dequeue. This buffer may be empty, indicated by the - driver setting the ``bytesused`` field to 0. Once the - ``V4L2_BUF_FLAG_LAST`` flag was set, the - :ref:`VIDIOC_DQBUF <VIDIOC_QBUF>` ioctl will not block anymore, - but return an ``EPIPE`` error code. + encoder is already stopped, this command does nothing. + + For a device implementing the :ref:`encoder`, the command will initiate + the drain sequence as documented in :ref:`encoder`. No flags or other + arguments are accepted in this case. Any attempt to invoke the command + again before the sequence completes will trigger an ``EBUSY`` error + code. * - ``V4L2_ENC_CMD_PAUSE`` - 2 - Pause the encoder. When the encoder has not been started yet, the @@ -152,6 +156,8 @@ introduced in Linux 2.6.21. - Stop encoding at the end of the current *Group Of Pictures*, rather than immediately. + Does not apply to :ref:`encoder`. + Return Value ============ @@ -160,6 +166,11 @@ On success 0 is returned, on error -1 and the ``errno`` variable is set appropriately. The generic error codes are described at the :ref:`Generic Error Codes <gen-errors>` chapter. +EBUSY + A drain sequence of a device implementing the :ref:`encoder` is still in + progress. It is not allowed to issue another encoder command until it + completes. + EINVAL The ``cmd`` field is invalid. diff --git a/Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst b/Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst index a53dd3d7f7e2..05835e04c20b 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst @@ -167,17 +167,37 @@ the ``mbus_code`` field is handled differently: - The hardware decoder for this compressed bytestream format (aka coded format) is capable of parsing a continuous bytestream. Applications do not need to parse the bytestream themselves to find the boundaries - between frames/fields. This flag can only be used in combination with - the ``V4L2_FMT_FLAG_COMPRESSED`` flag, since this applies to compressed + between frames/fields. + + This flag can only be used in combination with the + ``V4L2_FMT_FLAG_COMPRESSED`` flag, since this applies to compressed formats only. This flag is valid for stateful decoders only. * - ``V4L2_FMT_FLAG_DYN_RESOLUTION`` - 0x0008 - Dynamic resolution switching is supported by the device for this compressed bytestream format (aka coded format). It will notify the user via the event ``V4L2_EVENT_SOURCE_CHANGE`` when changes in the video - parameters are detected. This flag can only be used in combination - with the ``V4L2_FMT_FLAG_COMPRESSED`` flag, since this applies to - compressed formats only. It is also only applies to stateful codecs. + parameters are detected. + + This flag can only be used in combination with the + ``V4L2_FMT_FLAG_COMPRESSED`` flag, since this applies to + compressed formats only. This flag is valid for stateful codecs only. + * - ``V4L2_FMT_FLAG_ENC_CAP_FRAME_INTERVAL`` + - 0x0010 + - The hardware encoder supports setting the ``CAPTURE`` coded frame + interval separately from the ``OUTPUT`` raw frame interval. + Setting the ``OUTPUT`` raw frame interval with :ref:`VIDIOC_S_PARM <VIDIOC_G_PARM>` + also sets the ``CAPTURE`` coded frame interval to the same value. + If this flag is set, then the ``CAPTURE`` coded frame interval can be + set to a different value afterwards. This is typically used for + offline encoding where the ``OUTPUT`` raw frame interval is used as + a hint for reserving hardware encoder resources and the ``CAPTURE`` coded + frame interval is the actual frame rate embedded in the encoded video + stream. + + This flag can only be used in combination with the + ``V4L2_FMT_FLAG_COMPRESSED`` flag, since this applies to + compressed formats only. This flag is valid for stateful encoders only. Return Value diff --git a/Documentation/userspace-api/media/v4l/vidioc-g-parm.rst b/Documentation/userspace-api/media/v4l/vidioc-g-parm.rst index 42e9f6ee7a59..59e02aca164c 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-g-parm.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-g-parm.rst @@ -42,12 +42,13 @@ Arguments Description =========== -The current video standard determines a nominal number of frames per -second. If less than this number of frames is to be captured or output, -applications can request frame skipping or duplicating on the driver -side. This is especially useful when using the :ref:`read() <func-read>` or -:ref:`write() <func-write>`, which are not augmented by timestamps or sequence -counters, and to avoid unnecessary data copying. +Applications can request a different frame interval. The capture or +output device will be reconfigured to support the requested frame +interval if possible. Optionally drivers may choose to skip or +repeat frames to achieve the requested frame interval. + +For stateful encoders (see :ref:`encoder`) this represents the +frame interval that is typically embedded in the encoded video stream. Changing the frame interval shall never change the format. Changing the format, on the other hand, may change the frame interval. @@ -57,7 +58,8 @@ internally by a driver in read/write mode. For implications see the section discussing the :ref:`read() <func-read>` function. To get and set the streaming parameters applications call the -:ref:`VIDIOC_G_PARM <VIDIOC_G_PARM>` and :ref:`VIDIOC_S_PARM <VIDIOC_G_PARM>` ioctl, respectively. They take a +:ref:`VIDIOC_G_PARM <VIDIOC_G_PARM>` and +:ref:`VIDIOC_S_PARM <VIDIOC_G_PARM>` ioctl, respectively. They take a pointer to a struct :c:type:`v4l2_streamparm` which contains a union holding separate parameters for input and output devices. @@ -113,14 +115,21 @@ union holding separate parameters for input and output devices. * - struct :c:type:`v4l2_fract` - ``timeperframe`` - This is the desired period between successive frames captured by - the driver, in seconds. The field is intended to skip frames on - the driver side, saving I/O bandwidth. + the driver, in seconds. + * - :cspan:`2` + + This will configure the speed at which the video source (e.g. a sensor) + generates video frames. If the speed is fixed, then the driver may + choose to skip or repeat frames in order to achieve the requested + frame rate. + + For stateful encoders (see :ref:`encoder`) this represents the + frame interval that is typically embedded in the encoded video stream. Applications store here the desired frame period, drivers return - the actual frame period, which must be greater or equal to the - nominal frame period determined by the current video standard - (struct :c:type:`v4l2_standard` ``frameperiod`` - field). Changing the video standard (also implicitly by switching + the actual frame period. + + Changing the video standard (also implicitly by switching the video input) may reset this parameter to the nominal frame period. To reset manually applications can just set this field to zero. @@ -173,11 +182,15 @@ union holding separate parameters for input and output devices. :ref:`write() <func-write>` mode (in streaming mode timestamps can be used to throttle the output), saving I/O bandwidth. + For stateful encoders (see :ref:`encoder`) this represents the + frame interval that is typically embedded in the encoded video stream + and it provides a hint to the encoder of the speed at which raw + frames are queued up to the encoder. + Applications store here the desired frame period, drivers return - the actual frame period, which must be greater or equal to the - nominal frame period determined by the current video standard - (struct :c:type:`v4l2_standard` ``frameperiod`` - field). Changing the video standard (also implicitly by switching + the actual frame period. + + Changing the video standard (also implicitly by switching the video output) may reset this parameter to the nominal frame period. To reset manually applications can just set this field to zero. @@ -216,8 +229,8 @@ union holding separate parameters for input and output devices. * - ``V4L2_CAP_TIMEPERFRAME`` - 0x1000 - - The frame skipping/repeating controlled by the ``timeperframe`` - field is supported. + - The frame period can be modified by setting the ``timeperframe`` + field. diff --git a/Documentation/userspace-api/media/v4l/vidioc-querycap.rst b/Documentation/userspace-api/media/v4l/vidioc-querycap.rst index 666ac4d42051..90347367ef06 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-querycap.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-querycap.rst @@ -168,11 +168,11 @@ specification the ioctl returns an ``EINVAL`` error code. - The device supports the :ref:`multi-planar API <planar-apis>` through the :ref:`Video Output <output>` interface. * - ``V4L2_CAP_VIDEO_M2M`` - - 0x00004000 + - 0x00008000 - The device supports the single-planar API through the Video Memory-To-Memory interface. * - ``V4L2_CAP_VIDEO_M2M_MPLANE`` - - 0x00008000 + - 0x00004000 - The device supports the :ref:`multi-planar API <planar-apis>` through the Video Memory-To-Memory interface. * - ``V4L2_CAP_VIDEO_OVERLAY`` diff --git a/Documentation/userspace-api/media/v4l/vidioc-reqbufs.rst b/Documentation/userspace-api/media/v4l/vidioc-reqbufs.rst index b6d52083707b..75d894d9c36c 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-reqbufs.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-reqbufs.rst @@ -112,10 +112,17 @@ aborting or finishing any DMA in progress, an implicit ``V4L2_MEMORY_MMAP`` and ``type`` set to the buffer type. This will free any previously allocated buffers, so this is typically something that will be done at the start of the application. + * - union { + - (anonymous) + * - __u32 + - ``flags`` + - Specifies additional buffer management attributes. + See :ref:`memory-flags`. * - __u32 - ``reserved``\ [1] - - A place holder for future extensions. Drivers and applications - must set the array to zero. + - Kept for backwards compatibility. Use ``flags`` instead. + * - } + - .. tabularcolumns:: |p{6.1cm}|p{2.2cm}|p{8.7cm}| @@ -126,6 +133,7 @@ aborting or finishing any DMA in progress, an implicit .. _V4L2-BUF-CAP-SUPPORTS-REQUESTS: .. _V4L2-BUF-CAP-SUPPORTS-ORPHANED-BUFS: .. _V4L2-BUF-CAP-SUPPORTS-M2M-HOLD-CAPTURE-BUF: +.. _V4L2-BUF-CAP-SUPPORTS-MMAP-CACHE-HINTS: .. cssclass:: longtable @@ -156,6 +164,15 @@ aborting or finishing any DMA in progress, an implicit - Only valid for stateless decoders. If set, then userspace can set the ``V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF`` flag to hold off on returning the capture buffer until the OUTPUT timestamp changes. + * - ``V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS`` + - 0x00000040 + - This capability is set by the driver to indicate that the queue supports + cache and memory management hints. However, it's only valid when the + queue is used for :ref:`memory mapping <mmap>` streaming I/O. See + :ref:`V4L2_FLAG_MEMORY_NON_CONSISTENT <V4L2-FLAG-MEMORY-NON-CONSISTENT>`, + :ref:`V4L2_BUF_FLAG_NO_CACHE_INVALIDATE <V4L2-BUF-FLAG-NO-CACHE-INVALIDATE>` and + :ref:`V4L2_BUF_FLAG_NO_CACHE_CLEAN <V4L2-BUF-FLAG-NO-CACHE-CLEAN>`. + Return Value ============ diff --git a/Documentation/userspace-api/media/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions index a625fb90e3a9..ca05e4e126b2 100644 --- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions +++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions @@ -187,6 +187,7 @@ replace define V4L2_FMT_FLAG_COMPRESSED fmtdesc-flags replace define V4L2_FMT_FLAG_EMULATED fmtdesc-flags replace define V4L2_FMT_FLAG_CONTINUOUS_BYTESTREAM fmtdesc-flags replace define V4L2_FMT_FLAG_DYN_RESOLUTION fmtdesc-flags +replace define V4L2_FMT_FLAG_ENC_CAP_FRAME_INTERVAL fmtdesc-flags # V4L2 timecode types replace define V4L2_TC_TYPE_24FPS timecode-type diff --git a/MAINTAINERS b/MAINTAINERS index 2000daea0d61..f9ae8ea54e27 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4141,6 +4141,14 @@ F: drivers/power/supply/cros_usbpd-charger.c N: cros_ec N: cros-ec +CHRONTEL CH7322 CEC DRIVER +M: Jeff Chase <jnchase@google.com> +L: linux-media@vger.kernel.org +S: Maintained +T: git git://linuxtv.org/media_tree.git +F: Documentation/devicetree/bindings/media/i2c/chrontel,ch7322.yaml +F: drivers/media/cec/i2c/ch7322.c + CIRRUS LOGIC AUDIO CODEC DRIVERS M: James Schulman <james.schulman@cirrus.com> M: David Rhodes <david.rhodes@cirrus.com> @@ -5267,6 +5275,14 @@ T: git git://linuxtv.org/media_tree.git F: Documentation/devicetree/bindings/media/i2c/dongwoon,dw9714.txt F: drivers/media/i2c/dw9714.c +DONGWOON DW9768 LENS VOICE COIL DRIVER +M: Dongchun Zhu <dongchun.zhu@mediatek.com> +L: linux-media@vger.kernel.org +S: Maintained +T: git git://linuxtv.org/media_tree.git +F: Documentation/devicetree/bindings/media/i2c/dongwoon,dw9768.yaml +F: drivers/media/i2c/dw9768.c + DONGWOON DW9807 LENS VOICE COIL DRIVER M: Sakari Ailus <sakari.ailus@linux.intel.com> L: linux-media@vger.kernel.org @@ -10507,6 +10523,16 @@ F: Documentation/hwmon/max6697.rst F: drivers/hwmon/max6697.c F: include/linux/platform_data/max6697.h +MAX9286 QUAD GMSL DESERIALIZER DRIVER +M: Jacopo Mondi <jacopo+renesas@jmondi.org> +M: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com> +M: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> +M: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se> +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/media/i2c/maxim,max9286.yaml +F: drivers/media/i2c/max9286.c + MAX9860 MONO AUDIO VOICE CODEC DRIVER M: Peter Rosin <peda@axentia.se> L: alsa-devel@alsa-project.org (moderated for non-subscribers) @@ -10785,7 +10811,7 @@ L: linux-media@vger.kernel.org L: linux-renesas-soc@vger.kernel.org S: Supported T: git git://linuxtv.org/media_tree.git -F: Documentation/devicetree/bindings/media/renesas,fcp.txt +F: Documentation/devicetree/bindings/media/renesas,fcp.yaml F: drivers/media/platform/rcar-fcp.c F: include/media/rcar-fcp.h @@ -10795,7 +10821,7 @@ L: linux-media@vger.kernel.org L: linux-renesas-soc@vger.kernel.org S: Supported T: git git://linuxtv.org/media_tree.git -F: Documentation/devicetree/bindings/media/renesas,fdp1.txt +F: Documentation/devicetree/bindings/media/renesas,fdp1.yaml F: drivers/media/platform/rcar_fdp1.c MEDIA DRIVERS FOR RENESAS - VIN @@ -10815,7 +10841,7 @@ L: linux-media@vger.kernel.org L: linux-renesas-soc@vger.kernel.org S: Supported T: git git://linuxtv.org/media_tree.git -F: Documentation/devicetree/bindings/media/renesas,vsp1.txt +F: Documentation/devicetree/bindings/media/renesas,vsp1.yaml F: drivers/media/platform/vsp1/ MEDIA DRIVERS FOR ST STV0910 DEMODULATOR ICs @@ -14483,6 +14509,19 @@ L: linux-wireless@vger.kernel.org S: Orphan F: drivers/net/wireless/ray* +RC-CORE / LIRC FRAMEWORK +M: Sean Young <sean@mess.org> +L: linux-media@vger.kernel.org +S: Maintained +W: http://linuxtv.org +T: git git://linuxtv.org/media_tree.git +F: Documentation/driver-api/media/rc-core.rst +F: Documentation/userspace-api/media/rc/ +F: drivers/media/rc/ +F: include/media/rc-map.h +F: include/media/rc-core.h +F: include/uapi/linux/lirc.h + RCMM REMOTE CONTROLS DECODER M: Patrick Lerda <patrick9876@free.fr> S: Maintained @@ -14499,6 +14538,18 @@ S: Supported T: git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git dev F: tools/testing/selftests/rcutorture +RDACM20 Camera Sensor +M: Jacopo Mondi <jacopo+renesas@jmondi.org> +M: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com> +M: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> +M: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se> +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/media/i2c/imi,rdacm2x-gmsl.yaml +F: drivers/media/i2c/rdacm20.c +F: drivers/media/i2c/max9271.c +F: drivers/media/i2c/max9271.h + RDC R-321X SoC M: Florian Fainelli <florian@openwrt.org> S: Maintained @@ -15887,13 +15938,6 @@ L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/smsc/smsc9420.* -SOC-CAMERA V4L2 SUBSYSTEM -L: linux-media@vger.kernel.org -S: Orphan -T: git git://linuxtv.org/media_tree.git -F: drivers/staging/media/soc_camera/ -F: include/media/soc_camera.h - SOCIONEXT (SNI) AVE NETWORK DRIVER M: Kunihiko Hayashi <hayashi.kunihiko@socionext.com> L: netdev@vger.kernel.org diff --git a/arch/arm/configs/ezx_defconfig b/arch/arm/configs/ezx_defconfig index bd7b7f945e01..81665b7abf83 100644 --- a/arch/arm/configs/ezx_defconfig +++ b/arch/arm/configs/ezx_defconfig @@ -236,8 +236,6 @@ CONFIG_MEDIA_TUNER_CUSTOMISE=y # CONFIG_MEDIA_TUNER_MXL5007T is not set # CONFIG_MEDIA_TUNER_MC44S803 is not set # CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set -CONFIG_SOC_CAMERA=y -CONFIG_SOC_CAMERA_MT9M111=y CONFIG_VIDEO_PXA27x=y # CONFIG_V4L_USB_DRIVERS is not set CONFIG_RADIO_TEA5764=y diff --git a/arch/arm/configs/imote2_defconfig b/arch/arm/configs/imote2_defconfig index 05c5515fa871..ae15a2a33802 100644 --- a/arch/arm/configs/imote2_defconfig +++ b/arch/arm/configs/imote2_defconfig @@ -217,8 +217,6 @@ CONFIG_MEDIA_TUNER_CUSTOMISE=y # CONFIG_MEDIA_TUNER_MXL5007T is not set # CONFIG_MEDIA_TUNER_MC44S803 is not set # CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set -CONFIG_SOC_CAMERA=y -CONFIG_SOC_CAMERA_MT9M111=y CONFIG_VIDEO_PXA27x=y # CONFIG_V4L_USB_DRIVERS is not set # CONFIG_RADIO_ADAPTERS is not set diff --git a/arch/arm/configs/imx_v4_v5_defconfig b/arch/arm/configs/imx_v4_v5_defconfig index 3df90fc38398..f5f1111f2118 100644 --- a/arch/arm/configs/imx_v4_v5_defconfig +++ b/arch/arm/configs/imx_v4_v5_defconfig @@ -110,7 +110,6 @@ CONFIG_REGULATOR_MC13892=y CONFIG_MEDIA_SUPPORT=y CONFIG_MEDIA_CAMERA_SUPPORT=y CONFIG_V4L_PLATFORM_DRIVERS=y -CONFIG_SOC_CAMERA=y CONFIG_V4L_MEM2MEM_DRIVERS=y CONFIG_VIDEO_CODA=y CONFIG_FB=y diff --git a/arch/arm/configs/imx_v6_v7_defconfig b/arch/arm/configs/imx_v6_v7_defconfig index cb335478ac37..82d3ffb18e70 100644 --- a/arch/arm/configs/imx_v6_v7_defconfig +++ b/arch/arm/configs/imx_v6_v7_defconfig @@ -272,7 +272,6 @@ CONFIG_MEDIA_USB_SUPPORT=y CONFIG_USB_VIDEO_CLASS=m CONFIG_V4L_PLATFORM_DRIVERS=y CONFIG_VIDEO_MUX=y -CONFIG_SOC_CAMERA=y CONFIG_V4L_MEM2MEM_DRIVERS=y CONFIG_VIDEO_CODA=m CONFIG_VIDEO_IMX_PXP=y diff --git a/arch/arm/configs/pxa_defconfig b/arch/arm/configs/pxa_defconfig index 1dc7e9d7a294..d7b9eaf4783c 100644 --- a/arch/arm/configs/pxa_defconfig +++ b/arch/arm/configs/pxa_defconfig @@ -447,11 +447,8 @@ CONFIG_VIDEO_V4L2_SUBDEV_API=y CONFIG_MEDIA_USB_SUPPORT=y CONFIG_USB_VIDEO_CLASS=m CONFIG_V4L_PLATFORM_DRIVERS=y -CONFIG_SOC_CAMERA=m -CONFIG_SOC_CAMERA_PLATFORM=m CONFIG_VIDEO_PXA27x=m CONFIG_V4L_MEM2MEM_DRIVERS=y -CONFIG_SOC_CAMERA_MT9M111=m CONFIG_DRM=m CONFIG_FIRMWARE_EDID=y CONFIG_FB_TILEBLITTING=y diff --git a/arch/arm/configs/sama5_defconfig b/arch/arm/configs/sama5_defconfig index 7b7e333157fe..037d3a718a60 100644 --- a/arch/arm/configs/sama5_defconfig +++ b/arch/arm/configs/sama5_defconfig @@ -155,9 +155,7 @@ CONFIG_REGULATOR_PWM=m CONFIG_MEDIA_SUPPORT=y CONFIG_MEDIA_CAMERA_SUPPORT=y CONFIG_V4L_PLATFORM_DRIVERS=y -CONFIG_SOC_CAMERA=y CONFIG_VIDEO_ATMEL_ISI=y -CONFIG_SOC_CAMERA_OV2640=m CONFIG_DRM=y CONFIG_DRM_ATMEL_HLCDC=y CONFIG_DRM_PANEL_SIMPLE=y diff --git a/arch/arm/mach-imx/mach-imx27_visstrim_m10.c b/arch/arm/mach-imx/mach-imx27_visstrim_m10.c index c0350bc20941..3da4c0920198 100644 --- a/arch/arm/mach-imx/mach-imx27_visstrim_m10.c +++ b/arch/arm/mach-imx/mach-imx27_visstrim_m10.c @@ -19,7 +19,6 @@ #include <linux/dma-mapping.h> #include <linux/leds.h> #include <linux/platform_data/asoc-mx27vis.h> -#include <media/soc_camera.h> #include <sound/tlv320aic32x4.h> #include <asm/mach-types.h> #include <asm/mach/arch.h> @@ -191,34 +190,6 @@ static const struct gpio visstrim_m10_gpios[] __initconst = { }; /* Camera */ -static int visstrim_camera_power(struct device *dev, int on) -{ - gpio_set_value(TVP5150_PWDN, on); - - return 0; -}; - -static int visstrim_camera_reset(struct device *dev) -{ - gpio_set_value(TVP5150_RSTN, 0); - ndelay(500); - gpio_set_value(TVP5150_RSTN, 1); - - return 0; -}; - -static struct i2c_board_info visstrim_i2c_camera = { - I2C_BOARD_INFO("tvp5150", 0x5d), -}; - -static struct soc_camera_link iclink_tvp5150 = { - .bus_id = 0, - .board_info = &visstrim_i2c_camera, - .i2c_adapter_id = 0, - .power = visstrim_camera_power, - .reset = visstrim_camera_reset, -}; - static struct mx2_camera_platform_data visstrim_camera = { .flags = MX2_CAMERA_CCIR | MX2_CAMERA_CCIR_INTERLACE | MX2_CAMERA_PCLK_SAMPLE_RISING, @@ -549,8 +520,6 @@ static void __init visstrim_m10_late_init(void) imx_add_platform_device("mx27vis", 0, NULL, 0, &snd_mx27vis_pdata, sizeof(snd_mx27vis_pdata)); - platform_device_register_resndata(NULL, "soc-camera-pdrv", 0, NULL, 0, - &iclink_tvp5150, sizeof(iclink_tvp5150)); gpio_led_register_device(0, &visstrim_m10_led_data); diff --git a/arch/arm/mach-omap1/board-ams-delta.c b/arch/arm/mach-omap1/board-ams-delta.c index 8d32894ecd2e..2ee527c00284 100644 --- a/arch/arm/mach-omap1/board-ams-delta.c +++ b/arch/arm/mach-omap1/board-ams-delta.c @@ -29,8 +29,6 @@ #include <linux/io.h> #include <linux/platform_data/gpio-omap.h> -#include <media/soc_camera.h> - #include <asm/serial.h> #include <asm/mach-types.h> #include <asm/mach/arch.h> @@ -40,7 +38,6 @@ #include <mach/mux.h> #include <mach/hardware.h> -#include "camera.h" #include <mach/usb.h> #include "ams-delta-fiq.h" @@ -459,12 +456,6 @@ static struct gpiod_lookup_table leds_gpio_table = { }, }; -static struct i2c_board_info ams_delta_camera_board_info[] = { - { - I2C_BOARD_INFO("ov6650", 0x60), - }, -}; - #ifdef CONFIG_LEDS_TRIGGERS DEFINE_LED_TRIGGER(ams_delta_camera_led_trigger); @@ -483,27 +474,6 @@ static int ams_delta_camera_power(struct device *dev, int power) #define ams_delta_camera_power NULL #endif -static struct soc_camera_link ams_delta_iclink = { - .bus_id = 0, /* OMAP1 SoC camera bus */ - .i2c_adapter_id = 1, - .board_info = &ams_delta_camera_board_info[0], - .module_name = "ov6650", - .power = ams_delta_camera_power, -}; - -static struct platform_device ams_delta_camera_device = { - .name = "soc-camera-pdrv", - .id = 0, - .dev = { - .platform_data = &ams_delta_iclink, - }, -}; - -static struct omap1_cam_platform_data ams_delta_camera_platform_data = { - .camexclk_khz = 12000, /* default 12MHz clock, no extra DPLL */ - .lclk_khz_max = 1334, /* results in 5fps CIF, 10fps QCIF */ -}; - static struct platform_device ams_delta_audio_device = { .name = "ams-delta-audio", .id = -1, @@ -598,7 +568,6 @@ static struct platform_device *ams_delta_devices[] __initdata = { &latch1_gpio_device, &latch2_gpio_device, &ams_delta_kp_device, - &ams_delta_camera_device, &ams_delta_audio_device, &ams_delta_serio_device, &ams_delta_nand_device, @@ -750,7 +719,6 @@ static void __init ams_delta_init(void) omap_register_i2c_bus(1, 100, NULL, 0); omap1_usb_init(&ams_delta_usb_config); - omap1_set_camera_info(&ams_delta_camera_platform_data); #ifdef CONFIG_LEDS_TRIGGERS led_trigger_register_simple("ams_delta_camera", &ams_delta_camera_led_trigger); diff --git a/arch/arm/mach-omap1/camera.h b/arch/arm/mach-omap1/camera.h deleted file mode 100644 index 8018f410d024..000000000000 --- a/arch/arm/mach-omap1/camera.h +++ /dev/null @@ -1,14 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __ASM_ARCH_CAMERA_H_ -#define __ASM_ARCH_CAMERA_H_ - -#include <linux/platform_data/media/omap1_camera.h> - -void omap1_camera_init(void *); - -static inline void omap1_set_camera_info(struct omap1_cam_platform_data *info) -{ - omap1_camera_init(info); -} - -#endif /* __ASM_ARCH_CAMERA_H_ */ diff --git a/arch/arm/mach-omap1/devices.c b/arch/arm/mach-omap1/devices.c index 3c4900ac72fc..eb0f09edb3d1 100644 --- a/arch/arm/mach-omap1/devices.c +++ b/arch/arm/mach-omap1/devices.c @@ -21,7 +21,6 @@ #include <mach/mux.h> #include <mach/omap7xx.h> -#include "camera.h" #include <mach/hardware.h> #include "common.h" @@ -258,48 +257,6 @@ static inline void omap_init_spi100k(void) } #endif - -#define OMAP1_CAMERA_BASE 0xfffb6800 -#define OMAP1_CAMERA_IOSIZE 0x1c - -static struct resource omap1_camera_resources[] = { - [0] = { - .start = OMAP1_CAMERA_BASE, - .end = OMAP1_CAMERA_BASE + OMAP1_CAMERA_IOSIZE - 1, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = INT_CAMERA, - .flags = IORESOURCE_IRQ, - }, -}; - -static u64 omap1_camera_dma_mask = DMA_BIT_MASK(32); - -static struct platform_device omap1_camera_device = { - .name = "omap1-camera", - .id = 0, /* This is used to put cameras on this interface */ - .dev = { - .dma_mask = &omap1_camera_dma_mask, - .coherent_dma_mask = DMA_BIT_MASK(32), - }, - .num_resources = ARRAY_SIZE(omap1_camera_resources), - .resource = omap1_camera_resources, -}; - -void __init omap1_camera_init(void *info) -{ - struct platform_device *dev = &omap1_camera_device; - int ret; - - dev->dev.platform_data = info; - - ret = platform_device_register(dev); - if (ret) - dev_err(&dev->dev, "unable to register device: %d\n", ret); -} - - /*-------------------------------------------------------------------------*/ static inline void omap_init_sti(void) {} diff --git a/arch/arm/mach-pxa/palmz72.c b/arch/arm/mach-pxa/palmz72.c index 4df443943579..b4a5fe02a0af 100644 --- a/arch/arm/mach-pxa/palmz72.c +++ b/arch/arm/mach-pxa/palmz72.c @@ -47,8 +47,6 @@ #include "pm.h" #include <linux/platform_data/media/camera-pxa.h> -#include <media/soc_camera.h> - #include "generic.h" #include "devices.h" @@ -272,115 +270,6 @@ static int __init palmz72_pm_init(void) device_initcall(palmz72_pm_init); #endif -/****************************************************************************** - * SoC Camera - ******************************************************************************/ -#if defined(CONFIG_SOC_CAMERA_OV9640) || \ - defined(CONFIG_SOC_CAMERA_OV9640_MODULE) -static struct pxacamera_platform_data palmz72_pxacamera_platform_data = { - .flags = PXA_CAMERA_MASTER | PXA_CAMERA_DATAWIDTH_8 | - PXA_CAMERA_PCLK_EN | PXA_CAMERA_MCLK_EN, - .mclk_10khz = 2600, -}; - -/* Board I2C devices. */ -static struct i2c_board_info palmz72_i2c_device[] = { - { - I2C_BOARD_INFO("ov9640", 0x30), - } -}; - -static int palmz72_camera_power(struct device *dev, int power) -{ - gpio_set_value(GPIO_NR_PALMZ72_CAM_PWDN, !power); - mdelay(50); - return 0; -} - -static int palmz72_camera_reset(struct device *dev) -{ - gpio_set_value(GPIO_NR_PALMZ72_CAM_RESET, 1); - mdelay(50); - gpio_set_value(GPIO_NR_PALMZ72_CAM_RESET, 0); - mdelay(50); - return 0; -} - -static struct soc_camera_link palmz72_iclink = { - .bus_id = 0, /* Match id in pxa27x_device_camera in device.c */ - .board_info = &palmz72_i2c_device[0], - .i2c_adapter_id = 0, - .module_name = "ov96xx", - .power = &palmz72_camera_power, - .reset = &palmz72_camera_reset, - .flags = SOCAM_DATAWIDTH_8, -}; - -static struct gpiod_lookup_table palmz72_i2c_gpiod_table = { - .dev_id = "i2c-gpio.0", - .table = { - GPIO_LOOKUP_IDX("gpio-pxa", 118, NULL, 0, - GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN), - GPIO_LOOKUP_IDX("gpio-pxa", 117, NULL, 1, - GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN), - }, -}; - -static struct i2c_gpio_platform_data palmz72_i2c_bus_data = { - .udelay = 10, - .timeout = 100, -}; - -static struct platform_device palmz72_i2c_bus_device = { - .name = "i2c-gpio", - .id = 0, /* we use this as a replacement for i2c-pxa */ - .dev = { - .platform_data = &palmz72_i2c_bus_data, - } -}; - -static struct platform_device palmz72_camera = { - .name = "soc-camera-pdrv", - .id = -1, - .dev = { - .platform_data = &palmz72_iclink, - }, -}; - -/* Here we request the camera GPIOs and configure them. We power up the camera - * module, deassert the reset pin, but put it into powerdown (low to no power - * consumption) mode. This allows us to later bring the module up fast. */ -static struct gpio palmz72_camera_gpios[] = { - { GPIO_NR_PALMZ72_CAM_POWER, GPIOF_INIT_HIGH,"Camera DVDD" }, - { GPIO_NR_PALMZ72_CAM_RESET, GPIOF_INIT_LOW, "Camera RESET" }, - { GPIO_NR_PALMZ72_CAM_PWDN, GPIOF_INIT_LOW, "Camera PWDN" }, -}; - -static inline void __init palmz72_cam_gpio_init(void) -{ - int ret; - - ret = gpio_request_array(ARRAY_AND_SIZE(palmz72_camera_gpios)); - if (!ret) - gpio_free_array(ARRAY_AND_SIZE(palmz72_camera_gpios)); - else - printk(KERN_ERR "Camera GPIO init failed!\n"); - - return; -} - -static void __init palmz72_camera_init(void) -{ - palmz72_cam_gpio_init(); - pxa_set_camera_info(&palmz72_pxacamera_platform_data); - gpiod_add_lookup_table(&palmz72_i2c_gpiod_table); - platform_device_register(&palmz72_i2c_bus_device); - platform_device_register(&palmz72_camera); -} -#else -static inline void palmz72_camera_init(void) {} -#endif - static struct gpiod_lookup_table palmz72_mci_gpio_table = { .dev_id = "pxa2xx-mci.0", .table = { @@ -416,7 +305,6 @@ static void __init palmz72_init(void) palm27x_pmic_init(); palmz72_kpc_init(); palmz72_leds_init(); - palmz72_camera_init(); } MACHINE_START(PALMZ72, "Palm Zire72") diff --git a/arch/arm/mach-pxa/pcm990-baseboard.c b/arch/arm/mach-pxa/pcm990-baseboard.c index bf613f88d70b..8dfcc366d0fe 100644 --- a/arch/arm/mach-pxa/pcm990-baseboard.c +++ b/arch/arm/mach-pxa/pcm990-baseboard.c @@ -24,10 +24,6 @@ #include <linux/pwm.h> #include <linux/pwm_backlight.h> -#include <media/i2c/mt9v022.h> -#include <media/soc_camera.h> - -#include <linux/platform_data/media/camera-pxa.h> #include <asm/mach/map.h> #include "pxa27x.h" #include <mach/audio.h> @@ -375,149 +371,6 @@ static struct pxaohci_platform_data pcm990_ohci_platform_data = { }; /* - * PXA27x Camera specific stuff - */ -#if defined(CONFIG_VIDEO_PXA27x) || defined(CONFIG_VIDEO_PXA27x_MODULE) -static unsigned long pcm990_camera_pin_config[] = { - /* CIF */ - GPIO98_CIF_DD_0, - GPIO105_CIF_DD_1, - GPIO104_CIF_DD_2, - GPIO103_CIF_DD_3, - GPIO95_CIF_DD_4, - GPIO94_CIF_DD_5, - GPIO93_CIF_DD_6, - GPIO108_CIF_DD_7, - GPIO107_CIF_DD_8, - GPIO106_CIF_DD_9, - GPIO42_CIF_MCLK, - GPIO45_CIF_PCLK, - GPIO43_CIF_FV, - GPIO44_CIF_LV, -}; - -/* - * CICR4: PCLK_EN: Pixel clock is supplied by the sensor - * MCLK_EN: Master clock is generated by PXA - * PCP: Data sampled on the falling edge of pixel clock - */ -struct pxacamera_platform_data pcm990_pxacamera_platform_data = { - .flags = PXA_CAMERA_MASTER | PXA_CAMERA_DATAWIDTH_8 | PXA_CAMERA_DATAWIDTH_10 | - PXA_CAMERA_PCLK_EN | PXA_CAMERA_MCLK_EN/* | PXA_CAMERA_PCP*/, - .mclk_10khz = 1000, -}; - -#include <linux/platform_data/pca953x.h> - -static struct pca953x_platform_data pca9536_data = { - .gpio_base = PXA_NR_BUILTIN_GPIO, -}; - -static int gpio_bus_switch = -EINVAL; - -static int pcm990_camera_set_bus_param(struct soc_camera_link *link, - unsigned long flags) -{ - if (gpio_bus_switch < 0) { - if (flags == SOCAM_DATAWIDTH_10) - return 0; - else - return -EINVAL; - } - - if (flags & SOCAM_DATAWIDTH_8) - gpio_set_value_cansleep(gpio_bus_switch, 1); - else - gpio_set_value_cansleep(gpio_bus_switch, 0); - - return 0; -} - -static unsigned long pcm990_camera_query_bus_param(struct soc_camera_link *link) -{ - int ret; - - if (gpio_bus_switch < 0) { - ret = gpio_request(PXA_NR_BUILTIN_GPIO, "camera"); - if (!ret) { - gpio_bus_switch = PXA_NR_BUILTIN_GPIO; - gpio_direction_output(gpio_bus_switch, 0); - } - } - - if (gpio_bus_switch >= 0) - return SOCAM_DATAWIDTH_8 | SOCAM_DATAWIDTH_10; - else - return SOCAM_DATAWIDTH_10; -} - -static void pcm990_camera_free_bus(struct soc_camera_link *link) -{ - if (gpio_bus_switch < 0) - return; - - gpio_free(gpio_bus_switch); - gpio_bus_switch = -EINVAL; -} - -/* Board I2C devices. */ -static struct i2c_board_info __initdata pcm990_i2c_devices[] = { - { - /* Must initialize before the camera(s) */ - I2C_BOARD_INFO("pca9536", 0x41), - .platform_data = &pca9536_data, - }, -}; - -static struct mt9v022_platform_data mt9v022_pdata = { - .y_skip_top = 1, -}; - -static struct i2c_board_info pcm990_camera_i2c[] = { - { - I2C_BOARD_INFO("mt9v022", 0x48), - }, { - I2C_BOARD_INFO("mt9m001", 0x5d), - }, -}; - -static struct soc_camera_link iclink[] = { - { - .bus_id = 0, /* Must match with the camera ID */ - .board_info = &pcm990_camera_i2c[0], - .priv = &mt9v022_pdata, - .i2c_adapter_id = 0, - .query_bus_param = pcm990_camera_query_bus_param, - .set_bus_param = pcm990_camera_set_bus_param, - .free_bus = pcm990_camera_free_bus, - }, { - .bus_id = 0, /* Must match with the camera ID */ - .board_info = &pcm990_camera_i2c[1], - .i2c_adapter_id = 0, - .query_bus_param = pcm990_camera_query_bus_param, - .set_bus_param = pcm990_camera_set_bus_param, - .free_bus = pcm990_camera_free_bus, - }, -}; - -static struct platform_device pcm990_camera[] = { - { - .name = "soc-camera-pdrv", - .id = 0, - .dev = { - .platform_data = &iclink[0], - }, - }, { - .name = "soc-camera-pdrv", - .id = 1, - .dev = { - .platform_data = &iclink[1], - }, - }, -}; -#endif /* CONFIG_VIDEO_PXA27x ||CONFIG_VIDEO_PXA27x_MODULE */ - -/* * system init for baseboard usage. Will be called by pcm027 init. * * Add platform devices present on this baseboard and init @@ -551,15 +404,5 @@ void __init pcm990_baseboard_init(void) pxa_set_i2c_info(NULL); pxa_set_ac97_info(NULL); -#if defined(CONFIG_VIDEO_PXA27x) || defined(CONFIG_VIDEO_PXA27x_MODULE) - pxa2xx_mfp_config(ARRAY_AND_SIZE(pcm990_camera_pin_config)); - pxa_set_camera_info(&pcm990_pxacamera_platform_data); - - i2c_register_board_info(0, ARRAY_AND_SIZE(pcm990_i2c_devices)); - - platform_device_register(&pcm990_camera[0]); - platform_device_register(&pcm990_camera[1]); -#endif - printk(KERN_INFO "PCM-990 Evaluation baseboard initialized\n"); } diff --git a/arch/sh/configs/ap325rxa_defconfig b/arch/sh/configs/ap325rxa_defconfig index cc6e4ce53dac..5193b3e099b9 100644 --- a/arch/sh/configs/ap325rxa_defconfig +++ b/arch/sh/configs/ap325rxa_defconfig @@ -65,9 +65,6 @@ CONFIG_VIDEO_DEV=y # CONFIG_VIDEO_ALLOW_V4L1 is not set # CONFIG_MEDIA_TUNER_CUSTOMISE is not set CONFIG_VIDEO_HELPER_CHIPS_AUTO=y -CONFIG_SOC_CAMERA=y -CONFIG_SOC_CAMERA_PLATFORM=y -CONFIG_SOC_CAMERA_OV772X=y CONFIG_VIDEO_SH_MOBILE_CEU=y # CONFIG_RADIO_ADAPTERS is not set CONFIG_FB=y diff --git a/arch/sh/configs/ecovec24_defconfig b/arch/sh/configs/ecovec24_defconfig index 2fb7db4957ce..03cb916819fa 100644 --- a/arch/sh/configs/ecovec24_defconfig +++ b/arch/sh/configs/ecovec24_defconfig @@ -72,9 +72,6 @@ CONFIG_MEDIA_SUPPORT=y CONFIG_VIDEO_DEV=y # CONFIG_MEDIA_TUNER_CUSTOMISE is not set CONFIG_VIDEO_HELPER_CHIPS_AUTO=y -CONFIG_SOC_CAMERA=y -CONFIG_SOC_CAMERA_MT9T112=y -CONFIG_SOC_CAMERA_TW9910=y CONFIG_VIDEO_SH_MOBILE_CEU=y # CONFIG_V4L_USB_DRIVERS is not set CONFIG_FB=y diff --git a/arch/sh/configs/migor_defconfig b/arch/sh/configs/migor_defconfig index 494a1675c226..37e9521a99e5 100644 --- a/arch/sh/configs/migor_defconfig +++ b/arch/sh/configs/migor_defconfig @@ -62,9 +62,6 @@ CONFIG_VIDEO_DEV=y # CONFIG_VIDEO_ALLOW_V4L1 is not set # CONFIG_MEDIA_TUNER_CUSTOMISE is not set CONFIG_VIDEO_HELPER_CHIPS_AUTO=y -CONFIG_SOC_CAMERA=y -CONFIG_SOC_CAMERA_TW9910=y -CONFIG_SOC_CAMERA_OV772X=y CONFIG_VIDEO_SH_MOBILE_CEU=y # CONFIG_RADIO_ADAPTERS is not set CONFIG_FB=y diff --git a/arch/sh/configs/se7724_defconfig b/arch/sh/configs/se7724_defconfig index 0e8d5cc1e107..a26f7f1841c7 100644 --- a/arch/sh/configs/se7724_defconfig +++ b/arch/sh/configs/se7724_defconfig @@ -70,8 +70,6 @@ CONFIG_VIDEO_DEV=y CONFIG_DVB_CORE=m # CONFIG_MEDIA_TUNER_CUSTOMISE is not set CONFIG_VIDEO_HELPER_CHIPS_AUTO=y -CONFIG_SOC_CAMERA=y -CONFIG_SOC_CAMERA_OV772X=y CONFIG_VIDEO_SH_MOBILE_CEU=y # CONFIG_RADIO_ADAPTERS is not set # CONFIG_DVB_FE_CUSTOMISE is not set diff --git a/drivers/media/cec/Kconfig b/drivers/media/cec/Kconfig index 7e830444bdbb..9ba3a00dce31 100644 --- a/drivers/media/cec/Kconfig +++ b/drivers/media/cec/Kconfig @@ -33,6 +33,7 @@ menuconfig MEDIA_CEC_SUPPORT adapter that supports HDMI CEC. if MEDIA_CEC_SUPPORT +source "drivers/media/cec/i2c/Kconfig" source "drivers/media/cec/platform/Kconfig" source "drivers/media/cec/usb/Kconfig" endif diff --git a/drivers/media/cec/Makefile b/drivers/media/cec/Makefile index 74e80e1b3571..23539339bc81 100644 --- a/drivers/media/cec/Makefile +++ b/drivers/media/cec/Makefile @@ -1,2 +1,2 @@ # SPDX-License-Identifier: GPL-2.0 -obj-y += core/ platform/ usb/ +obj-y += core/ i2c/ platform/ usb/ diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c index 6a04d19a96b2..4efe8014445e 100644 --- a/drivers/media/cec/core/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -1306,7 +1306,6 @@ static int cec_config_log_addr(struct cec_adapter *adap, las->log_addr[idx] = log_addr; las->log_addr_mask |= 1 << log_addr; - adap->phys_addrs[log_addr] = adap->phys_addr; return 1; } @@ -1324,7 +1323,6 @@ static void cec_adap_unconfigure(struct cec_adapter *adap) adap->log_addrs.log_addr_mask = 0; adap->is_configuring = false; adap->is_configured = false; - memset(adap->phys_addrs, 0xff, sizeof(adap->phys_addrs)); cec_flush(adap); wake_up_interruptible(&adap->kthread_waitq); cec_post_state_event(adap); @@ -1974,8 +1972,6 @@ static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg, case CEC_MSG_REPORT_PHYSICAL_ADDR: { u16 pa = (msg->msg[2] << 8) | msg->msg[3]; - if (!from_unregistered) - adap->phys_addrs[init_laddr] = pa; dprintk(1, "reported physical address %x.%x.%x.%x for logical address %d\n", cec_phys_addr_exp(pa), init_laddr); break; diff --git a/drivers/media/cec/core/cec-api.c b/drivers/media/cec/core/cec-api.c index 17d1cb2e5f97..f922a2196b2b 100644 --- a/drivers/media/cec/core/cec-api.c +++ b/drivers/media/cec/core/cec-api.c @@ -147,7 +147,13 @@ static long cec_adap_g_log_addrs(struct cec_adapter *adap, struct cec_log_addrs log_addrs; mutex_lock(&adap->lock); - log_addrs = adap->log_addrs; + /* + * We use memcpy here instead of assignment since there is a + * hole at the end of struct cec_log_addrs that an assignment + * might ignore. So when we do copy_to_user() we could leak + * one byte of memory. + */ + memcpy(&log_addrs, &adap->log_addrs, sizeof(log_addrs)); if (!adap->is_configured) memset(log_addrs.log_addr, CEC_LOG_ADDR_INVALID, sizeof(log_addrs.log_addr)); diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c index 0c52e1bb3910..c599cd94dd62 100644 --- a/drivers/media/cec/core/cec-core.c +++ b/drivers/media/cec/core/cec-core.c @@ -265,7 +265,6 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, adap->sequence = 0; adap->ops = ops; adap->priv = priv; - memset(adap->phys_addrs, 0xff, sizeof(adap->phys_addrs)); mutex_init(&adap->lock); INIT_LIST_HEAD(&adap->transmit_queue); INIT_LIST_HEAD(&adap->wait_queue); diff --git a/drivers/media/cec/core/cec-notifier.c b/drivers/media/cec/core/cec-notifier.c index 517e0035fc99..95f363bb1d19 100644 --- a/drivers/media/cec/core/cec-notifier.c +++ b/drivers/media/cec/core/cec-notifier.c @@ -116,7 +116,8 @@ cec_notifier_conn_register(struct device *hdmi_dev, const char *port_name, else memset(&n->conn_info, 0, sizeof(n->conn_info)); if (n->cec_adap) { - cec_phys_addr_invalidate(n->cec_adap); + if (!n->cec_adap->adap_controls_phys_addr) + cec_phys_addr_invalidate(n->cec_adap); cec_s_conn_info(n->cec_adap, conn_info); } mutex_unlock(&n->lock); @@ -133,7 +134,8 @@ void cec_notifier_conn_unregister(struct cec_notifier *n) memset(&n->conn_info, 0, sizeof(n->conn_info)); n->phys_addr = CEC_PHYS_ADDR_INVALID; if (n->cec_adap) { - cec_phys_addr_invalidate(n->cec_adap); + if (!n->cec_adap->adap_controls_phys_addr) + cec_phys_addr_invalidate(n->cec_adap); cec_s_conn_info(n->cec_adap, NULL); } mutex_unlock(&n->lock); @@ -158,7 +160,8 @@ cec_notifier_cec_adap_register(struct device *hdmi_dev, const char *port_name, n->cec_adap = adap; adap->conn_info = n->conn_info; adap->notifier = n; - cec_s_phys_addr(adap, n->phys_addr, false); + if (!adap->adap_controls_phys_addr) + cec_s_phys_addr(adap, n->phys_addr, false); mutex_unlock(&n->lock); return n; } @@ -185,7 +188,7 @@ void cec_notifier_set_phys_addr(struct cec_notifier *n, u16 pa) mutex_lock(&n->lock); n->phys_addr = pa; - if (n->cec_adap) + if (n->cec_adap && !n->cec_adap->adap_controls_phys_addr) cec_s_phys_addr(n->cec_adap, n->phys_addr, false); mutex_unlock(&n->lock); } diff --git a/drivers/media/cec/i2c/Kconfig b/drivers/media/cec/i2c/Kconfig new file mode 100644 index 000000000000..70432a1d6918 --- /dev/null +++ b/drivers/media/cec/i2c/Kconfig @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# I2C drivers + +config CEC_CH7322 + tristate "Chrontel CH7322 CEC controller" + depends on I2C + select REGMAP_I2C + select CEC_CORE + help + This is a driver for the Chrontel CH7322 CEC controller. It uses the + generic CEC framework interface. + CEC bus is present in the HDMI connector and enables communication + between compatible devices. diff --git a/drivers/media/cec/i2c/Makefile b/drivers/media/cec/i2c/Makefile new file mode 100644 index 000000000000..d7496dfd0fa4 --- /dev/null +++ b/drivers/media/cec/i2c/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the CEC I2C device drivers. +# +obj-$(CONFIG_CEC_CH7322) += ch7322.o diff --git a/drivers/media/cec/i2c/ch7322.c b/drivers/media/cec/i2c/ch7322.c new file mode 100644 index 000000000000..0814338c43e4 --- /dev/null +++ b/drivers/media/cec/i2c/ch7322.c @@ -0,0 +1,604 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the Chrontel CH7322 CEC Controller + * + * Copyright 2020 Google LLC. + */ + +/* + * Notes + * + * - This device powers on in Auto Mode which has limited functionality. This + * driver disables Auto Mode when it attaches. + * + */ + +#include <linux/cec.h> +#include <linux/dmi.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/pci.h> +#include <linux/regmap.h> +#include <media/cec.h> +#include <media/cec-notifier.h> + +#define CH7322_WRITE 0x00 +#define CH7322_WRITE_MSENT 0x80 +#define CH7322_WRITE_BOK 0x40 +#define CH7322_WRITE_NMASK 0x0f + +/* Write buffer is 0x01-0x10 */ +#define CH7322_WRBUF 0x01 +#define CH7322_WRBUF_LEN 0x10 + +#define CH7322_READ 0x40 +#define CH7322_READ_NRDT 0x80 +#define CH7322_READ_MSENT 0x20 +#define CH7322_READ_NMASK 0x0f + +/* Read buffer is 0x41-0x50 */ +#define CH7322_RDBUF 0x41 +#define CH7322_RDBUF_LEN 0x10 + +#define CH7322_MODE 0x11 +#define CH7322_MODE_AUTO 0x78 +#define CH7322_MODE_SW 0xb5 + +#define CH7322_RESET 0x12 +#define CH7322_RESET_RST 0x00 + +#define CH7322_POWER 0x13 +#define CH7322_POWER_FPD 0x04 + +#define CH7322_CFG0 0x17 +#define CH7322_CFG0_EOBEN 0x40 +#define CH7322_CFG0_PEOB 0x20 +#define CH7322_CFG0_CLRSPP 0x10 +#define CH7322_CFG0_FLOW 0x08 + +#define CH7322_CFG1 0x1a +#define CH7322_CFG1_STDBYO 0x04 +#define CH7322_CFG1_HPBP 0x02 +#define CH7322_CFG1_PIO 0x01 + +#define CH7322_INTCTL 0x1b +#define CH7322_INTCTL_INTPB 0x80 +#define CH7322_INTCTL_STDBY 0x40 +#define CH7322_INTCTL_HPDFALL 0x20 +#define CH7322_INTCTL_HPDRISE 0x10 +#define CH7322_INTCTL_RXMSG 0x08 +#define CH7322_INTCTL_TXMSG 0x04 +#define CH7322_INTCTL_NEWPHA 0x02 +#define CH7322_INTCTL_ERROR 0x01 + +#define CH7322_DVCLKFNH 0x1d +#define CH7322_DVCLKFNL 0x1e + +#define CH7322_CTL 0x31 +#define CH7322_CTL_FSTDBY 0x80 +#define CH7322_CTL_PLSEN 0x40 +#define CH7322_CTL_PLSPB 0x20 +#define CH7322_CTL_SPADL 0x10 +#define CH7322_CTL_HINIT 0x08 +#define CH7322_CTL_WPHYA 0x04 +#define CH7322_CTL_H1T 0x02 +#define CH7322_CTL_S1T 0x01 + +#define CH7322_PAWH 0x32 +#define CH7322_PAWL 0x33 + +#define CH7322_ADDLW 0x34 +#define CH7322_ADDLW_MASK 0xf0 + +#define CH7322_ADDLR 0x3d +#define CH7322_ADDLR_HPD 0x80 +#define CH7322_ADDLR_MASK 0x0f + +#define CH7322_INTDATA 0x3e +#define CH7322_INTDATA_MODE 0x80 +#define CH7322_INTDATA_STDBY 0x40 +#define CH7322_INTDATA_HPDFALL 0x20 +#define CH7322_INTDATA_HPDRISE 0x10 +#define CH7322_INTDATA_RXMSG 0x08 +#define CH7322_INTDATA_TXMSG 0x04 +#define CH7322_INTDATA_NEWPHA 0x02 +#define CH7322_INTDATA_ERROR 0x01 + +#define CH7322_EVENT 0x3f +#define CH7322_EVENT_TXERR 0x80 +#define CH7322_EVENT_HRST 0x40 +#define CH7322_EVENT_HFST 0x20 +#define CH7322_EVENT_PHACHG 0x10 +#define CH7322_EVENT_ACTST 0x08 +#define CH7322_EVENT_PHARDY 0x04 +#define CH7322_EVENT_BSOK 0x02 +#define CH7322_EVENT_ERRADCF 0x01 + +#define CH7322_DID 0x51 +#define CH7322_DID_CH7322 0x5b +#define CH7322_DID_CH7323 0x5f + +#define CH7322_REVISIONID 0x52 + +#define CH7322_PARH 0x53 +#define CH7322_PARL 0x54 + +#define CH7322_IOCFG2 0x75 +#define CH7322_IOCFG_CIO 0x80 +#define CH7322_IOCFG_IOCFGMASK 0x78 +#define CH7322_IOCFG_AUDIO 0x04 +#define CH7322_IOCFG_SPAMST 0x02 +#define CH7322_IOCFG_SPAMSP 0x01 + +#define CH7322_CTL3 0x7b +#define CH7322_CTL3_SWENA 0x80 +#define CH7322_CTL3_FC_INIT 0x40 +#define CH7322_CTL3_SML_FL 0x20 +#define CH7322_CTL3_SM_RDST 0x10 +#define CH7322_CTL3_SPP_CIAH 0x08 +#define CH7322_CTL3_SPP_CIAL 0x04 +#define CH7322_CTL3_SPP_ACTH 0x02 +#define CH7322_CTL3_SPP_ACTL 0x01 + +/* BOK status means NACK */ +#define CH7322_TX_FLAG_NACK BIT(0) +/* Device will retry automatically */ +#define CH7322_TX_FLAG_RETRY BIT(1) + +struct ch7322 { + struct i2c_client *i2c; + struct regmap *regmap; + struct cec_adapter *cec; + struct mutex mutex; /* device access mutex */ + u8 tx_flags; +}; + +static const struct regmap_config ch7322_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x7f, + .disable_locking = true, +}; + +static int ch7322_send_message(struct ch7322 *ch7322, const struct cec_msg *msg) +{ + unsigned int val; + unsigned int len = msg->len; + int ret; + int i; + + WARN_ON(!mutex_is_locked(&ch7322->mutex)); + + if (len > CH7322_WRBUF_LEN || len < 1) + return -EINVAL; + + ret = regmap_read(ch7322->regmap, CH7322_WRITE, &val); + if (ret) + return ret; + + /* Buffer not ready */ + if (!(val & CH7322_WRITE_MSENT)) + return -EBUSY; + + if (cec_msg_opcode(msg) == -1 && + cec_msg_initiator(msg) == cec_msg_destination(msg)) { + ch7322->tx_flags = CH7322_TX_FLAG_NACK | CH7322_TX_FLAG_RETRY; + } else if (cec_msg_is_broadcast(msg)) { + ch7322->tx_flags = CH7322_TX_FLAG_NACK; + } else { + ch7322->tx_flags = CH7322_TX_FLAG_RETRY; + } + + ret = regmap_write(ch7322->regmap, CH7322_WRITE, len - 1); + if (ret) + return ret; + + for (i = 0; i < len; i++) { + ret = regmap_write(ch7322->regmap, + CH7322_WRBUF + i, msg->msg[i]); + if (ret) + return ret; + } + + return 0; +} + +static int ch7322_receive_message(struct ch7322 *ch7322, struct cec_msg *msg) +{ + unsigned int val; + int ret = 0; + int i; + + WARN_ON(!mutex_is_locked(&ch7322->mutex)); + + ret = regmap_read(ch7322->regmap, CH7322_READ, &val); + if (ret) + return ret; + + /* Message not ready */ + if (!(val & CH7322_READ_NRDT)) + return -EIO; + + msg->len = (val & CH7322_READ_NMASK) + 1; + + /* Read entire RDBUF to clear state */ + for (i = 0; i < CH7322_RDBUF_LEN; i++) { + ret = regmap_read(ch7322->regmap, CH7322_RDBUF + i, &val); + if (ret) + return ret; + msg->msg[i] = (u8)val; + } + + return 0; +} + +static void ch7322_tx_done(struct ch7322 *ch7322) +{ + int ret; + unsigned int val; + u8 status, flags; + + mutex_lock(&ch7322->mutex); + ret = regmap_read(ch7322->regmap, CH7322_WRITE, &val); + flags = ch7322->tx_flags; + mutex_unlock(&ch7322->mutex); + + /* + * The device returns a one-bit OK status which usually means ACK but + * actually means NACK when sending a logical address query or a + * broadcast. + */ + if (ret) + status = CEC_TX_STATUS_ERROR; + else if ((val & CH7322_WRITE_BOK) && (flags & CH7322_TX_FLAG_NACK)) + status = CEC_TX_STATUS_NACK; + else if (val & CH7322_WRITE_BOK) + status = CEC_TX_STATUS_OK; + else if (flags & CH7322_TX_FLAG_NACK) + status = CEC_TX_STATUS_OK; + else + status = CEC_TX_STATUS_NACK; + + if (status == CEC_TX_STATUS_NACK && (flags & CH7322_TX_FLAG_RETRY)) + status |= CEC_TX_STATUS_MAX_RETRIES; + + cec_transmit_attempt_done(ch7322->cec, status); +} + +static void ch7322_rx_done(struct ch7322 *ch7322) +{ + struct cec_msg msg; + int ret; + + mutex_lock(&ch7322->mutex); + ret = ch7322_receive_message(ch7322, &msg); + mutex_unlock(&ch7322->mutex); + + if (ret) + dev_err(&ch7322->i2c->dev, "cec receive error: %d\n", ret); + else + cec_received_msg(ch7322->cec, &msg); +} + +/* + * This device can either monitor the DDC lines to obtain the physical address + * or it can allow the host to program it. This driver lets the device obtain + * it. + */ +static void ch7322_phys_addr(struct ch7322 *ch7322) +{ + unsigned int pah, pal; + int ret = 0; + + mutex_lock(&ch7322->mutex); + ret |= regmap_read(ch7322->regmap, CH7322_PARH, &pah); + ret |= regmap_read(ch7322->regmap, CH7322_PARL, &pal); + mutex_unlock(&ch7322->mutex); + + if (ret) + dev_err(&ch7322->i2c->dev, "phys addr error\n"); + else + cec_s_phys_addr(ch7322->cec, pal | (pah << 8), false); +} + +static irqreturn_t ch7322_irq(int irq, void *dev) +{ + struct ch7322 *ch7322 = dev; + unsigned int data = 0; + + mutex_lock(&ch7322->mutex); + regmap_read(ch7322->regmap, CH7322_INTDATA, &data); + regmap_write(ch7322->regmap, CH7322_INTDATA, data); + mutex_unlock(&ch7322->mutex); + + if (data & CH7322_INTDATA_HPDFALL) + cec_phys_addr_invalidate(ch7322->cec); + + if (data & CH7322_INTDATA_TXMSG) + ch7322_tx_done(ch7322); + + if (data & CH7322_INTDATA_RXMSG) + ch7322_rx_done(ch7322); + + if (data & CH7322_INTDATA_NEWPHA) + ch7322_phys_addr(ch7322); + + if (data & CH7322_INTDATA_ERROR) + dev_dbg(&ch7322->i2c->dev, "unknown error\n"); + + return IRQ_HANDLED; +} + +/* This device is always enabled */ +static int ch7322_cec_adap_enable(struct cec_adapter *adap, bool enable) +{ + return 0; +} + +static int ch7322_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr) +{ + struct ch7322 *ch7322 = cec_get_drvdata(adap); + int ret; + + mutex_lock(&ch7322->mutex); + ret = regmap_update_bits(ch7322->regmap, CH7322_ADDLW, + CH7322_ADDLW_MASK, log_addr << 4); + mutex_unlock(&ch7322->mutex); + + return ret; +} + +static int ch7322_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, + u32 signal_free_time, struct cec_msg *msg) +{ + struct ch7322 *ch7322 = cec_get_drvdata(adap); + int ret; + + mutex_lock(&ch7322->mutex); + ret = ch7322_send_message(ch7322, msg); + mutex_unlock(&ch7322->mutex); + + return ret; +} + +static const struct cec_adap_ops ch7322_cec_adap_ops = { + .adap_enable = ch7322_cec_adap_enable, + .adap_log_addr = ch7322_cec_adap_log_addr, + .adap_transmit = ch7322_cec_adap_transmit, +}; + +#if IS_ENABLED(CONFIG_PCI) && IS_ENABLED(CONFIG_DMI) + +struct ch7322_conn_match { + const char *dev_name; + const char *pci_name; + const char *port_name; +}; + +static struct ch7322_conn_match google_endeavour[] = { + { "i2c-PRP0001:00", "0000:00:02.0", "Port B" }, + { "i2c-PRP0001:01", "0000:00:02.0", "Port C" }, + { }, +}; + +static const struct dmi_system_id ch7322_dmi_table[] = { + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Google"), + DMI_MATCH(DMI_BOARD_NAME, "Endeavour"), + }, + .driver_data = google_endeavour, + }, + { }, +}; + +/* Make a best-effort attempt to locate a matching HDMI port */ +static int ch7322_get_port(struct i2c_client *client, + struct device **dev, + const char **port) +{ + const struct dmi_system_id *system; + const struct ch7322_conn_match *conn; + + *dev = NULL; + *port = NULL; + + system = dmi_first_match(ch7322_dmi_table); + if (!system) + return 0; + + for (conn = system->driver_data; conn->dev_name; conn++) { + if (!strcmp(dev_name(&client->dev), conn->dev_name)) { + struct device *d; + + d = bus_find_device_by_name(&pci_bus_type, NULL, + conn->pci_name); + if (!d) + return -EPROBE_DEFER; + + put_device(d); + + *dev = d; + *port = conn->port_name; + + return 0; + } + } + + return 0; +} + +#else + +static int ch7322_get_port(struct i2c_client *client, + struct device **dev, + const char **port) +{ + *dev = NULL; + *port = NULL; + + return 0; +} + +#endif + +static int ch7322_probe(struct i2c_client *client) +{ + struct device *hdmi_dev; + const char *port_name; + struct ch7322 *ch7322; + struct cec_notifier *notifier = NULL; + u32 caps = CEC_CAP_DEFAULTS; + int ret; + unsigned int val; + + ret = ch7322_get_port(client, &hdmi_dev, &port_name); + if (ret) + return ret; + + if (hdmi_dev) + caps |= CEC_CAP_CONNECTOR_INFO; + + ch7322 = devm_kzalloc(&client->dev, sizeof(*ch7322), GFP_KERNEL); + if (!ch7322) + return -ENOMEM; + + ch7322->regmap = devm_regmap_init_i2c(client, &ch7322_regmap); + if (IS_ERR(ch7322->regmap)) + return PTR_ERR(ch7322->regmap); + + ret = regmap_read(ch7322->regmap, CH7322_DID, &val); + if (ret) + return ret; + + if (val != CH7322_DID_CH7322) + return -EOPNOTSUPP; + + mutex_init(&ch7322->mutex); + ch7322->i2c = client; + ch7322->tx_flags = 0; + + i2c_set_clientdata(client, ch7322); + + /* Disable auto mode */ + ret = regmap_write(ch7322->regmap, CH7322_MODE, CH7322_MODE_SW); + if (ret) + goto err_mutex; + + /* Enable logical address register */ + ret = regmap_update_bits(ch7322->regmap, CH7322_CTL, + CH7322_CTL_SPADL, CH7322_CTL_SPADL); + if (ret) + goto err_mutex; + + ch7322->cec = cec_allocate_adapter(&ch7322_cec_adap_ops, ch7322, + dev_name(&client->dev), + caps, 1); + + if (IS_ERR(ch7322->cec)) { + ret = PTR_ERR(ch7322->cec); + goto err_mutex; + } + + ch7322->cec->adap_controls_phys_addr = true; + + if (hdmi_dev) { + notifier = cec_notifier_cec_adap_register(hdmi_dev, + port_name, + ch7322->cec); + if (!notifier) { + ret = -ENOMEM; + goto err_cec; + } + } + + /* Configure, mask, and clear interrupt */ + ret = regmap_write(ch7322->regmap, CH7322_CFG1, 0); + if (ret) + goto err_notifier; + ret = regmap_write(ch7322->regmap, CH7322_INTCTL, CH7322_INTCTL_INTPB); + if (ret) + goto err_notifier; + ret = regmap_write(ch7322->regmap, CH7322_INTDATA, 0xff); + if (ret) + goto err_notifier; + + /* If HPD is up read physical address */ + ret = regmap_read(ch7322->regmap, CH7322_ADDLR, &val); + if (ret) + goto err_notifier; + if (val & CH7322_ADDLR_HPD) + ch7322_phys_addr(ch7322); + + ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, + ch7322_irq, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + client->name, ch7322); + if (ret) + goto err_notifier; + + /* Unmask interrupt */ + mutex_lock(&ch7322->mutex); + ret = regmap_write(ch7322->regmap, CH7322_INTCTL, 0xff); + mutex_unlock(&ch7322->mutex); + + if (ret) + goto err_notifier; + + ret = cec_register_adapter(ch7322->cec, &client->dev); + if (ret) + goto err_notifier; + + dev_info(&client->dev, "device registered\n"); + + return 0; + +err_notifier: + if (notifier) + cec_notifier_cec_adap_unregister(notifier, ch7322->cec); +err_cec: + cec_delete_adapter(ch7322->cec); +err_mutex: + mutex_destroy(&ch7322->mutex); + return ret; +} + +static int ch7322_remove(struct i2c_client *client) +{ + struct ch7322 *ch7322 = i2c_get_clientdata(client); + + /* Mask interrupt */ + mutex_lock(&ch7322->mutex); + regmap_write(ch7322->regmap, CH7322_INTCTL, CH7322_INTCTL_INTPB); + mutex_unlock(&ch7322->mutex); + + cec_unregister_adapter(ch7322->cec); + mutex_destroy(&ch7322->mutex); + + dev_info(&client->dev, "device unregistered\n"); + + return 0; +} + +static const struct of_device_id ch7322_of_match[] = { + { .compatible = "chrontel,ch7322", }, + {}, +}; +MODULE_DEVICE_TABLE(of, ch7322_of_match); + +static struct i2c_driver ch7322_i2c_driver = { + .driver = { + .name = "ch7322", + .of_match_table = of_match_ptr(ch7322_of_match), + }, + .probe_new = ch7322_probe, + .remove = ch7322_remove, +}; + +module_i2c_driver(ch7322_i2c_driver); + +MODULE_DESCRIPTION("Chrontel CH7322 CEC Controller Driver"); +MODULE_AUTHOR("Jeff Chase <jnchase@google.com>"); +MODULE_LICENSE("GPL"); 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 0e7e2772f08f..2d95e16cd248 100644 --- a/drivers/media/cec/platform/cros-ec/cros-ec-cec.c +++ b/drivers/media/cec/platform/cros-ec/cros-ec-cec.c @@ -277,11 +277,7 @@ static int cros_ec_cec_probe(struct platform_device *pdev) platform_set_drvdata(pdev, cros_ec_cec); cros_ec_cec->cros_ec = cros_ec; - ret = device_init_wakeup(&pdev->dev, 1); - if (ret) { - dev_err(&pdev->dev, "failed to initialize wakeup\n"); - return ret; - } + device_init_wakeup(&pdev->dev, 1); cros_ec_cec->adap = cec_allocate_adapter(&cros_ec_cec_ops, cros_ec_cec, DRV_NAME, diff --git a/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c index 50f1e0b28b25..630a75e0eeb1 100644 --- a/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c +++ b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c @@ -1927,44 +1927,46 @@ typedef struct { u16 __; u8 _; } __packed x24; static noinline void tpg_print_str_2(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], unsigned p, unsigned first, unsigned div, unsigned step, - int y, int x, char *text, unsigned len) + int y, int x, const char *text, unsigned len) { PRINTSTR(u8); } static noinline void tpg_print_str_4(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], unsigned p, unsigned first, unsigned div, unsigned step, - int y, int x, char *text, unsigned len) + int y, int x, const char *text, unsigned len) { PRINTSTR(u16); } static noinline void tpg_print_str_6(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], unsigned p, unsigned first, unsigned div, unsigned step, - int y, int x, char *text, unsigned len) + int y, int x, const char *text, unsigned len) { PRINTSTR(x24); } static noinline void tpg_print_str_8(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], unsigned p, unsigned first, unsigned div, unsigned step, - int y, int x, char *text, unsigned len) + int y, int x, const char *text, unsigned len) { PRINTSTR(u32); } void tpg_gen_text(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], - int y, int x, char *text) + int y, int x, const char *text) { unsigned step = V4L2_FIELD_HAS_T_OR_B(tpg->field) ? 2 : 1; unsigned div = step; unsigned first = 0; - unsigned len = strlen(text); + unsigned len; unsigned p; - if (font8x16 == NULL || basep == NULL) + if (font8x16 == NULL || basep == NULL || text == NULL) return; + len = strlen(text); + /* Checks if it is possible to show string */ if (y + 16 >= tpg->compose.height || x + 8 >= tpg->compose.width) return; @@ -2006,6 +2008,30 @@ void tpg_gen_text(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], } EXPORT_SYMBOL_GPL(tpg_gen_text); +const char *tpg_g_color_order(const struct tpg_data *tpg) +{ + switch (tpg->pattern) { + case TPG_PAT_75_COLORBAR: + case TPG_PAT_100_COLORBAR: + case TPG_PAT_CSC_COLORBAR: + case TPG_PAT_100_HCOLORBAR: + return "White, yellow, cyan, green, magenta, red, blue, black"; + case TPG_PAT_BLACK: + return "Black"; + case TPG_PAT_WHITE: + return "White"; + case TPG_PAT_RED: + return "Red"; + case TPG_PAT_GREEN: + return "Green"; + case TPG_PAT_BLUE: + return "Blue"; + default: + return NULL; + } +} +EXPORT_SYMBOL_GPL(tpg_g_color_order); + void tpg_update_mv_step(struct tpg_data *tpg) { int factor = tpg->mv_hor_mode > TPG_MOVE_NONE ? -1 : 1; diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index 44d65f5be845..f544d3393e9d 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -34,10 +34,11 @@ static int debug; module_param(debug, int, 0644); -#define dprintk(level, fmt, arg...) \ - do { \ - if (debug >= level) \ - pr_info("%s: " fmt, __func__, ## arg); \ +#define dprintk(q, level, fmt, arg...) \ + do { \ + if (debug >= level) \ + pr_info("[%s] %s: " fmt, (q)->name, __func__, \ + ## arg); \ } while (0) #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -51,8 +52,8 @@ module_param(debug, int, 0644); */ #define log_memop(vb, op) \ - dprintk(2, "call_memop(%p, %d, %s)%s\n", \ - (vb)->vb2_queue, (vb)->index, #op, \ + dprintk((vb)->vb2_queue, 2, "call_memop(%d, %s)%s\n", \ + (vb)->index, #op, \ (vb)->vb2_queue->mem_ops->op ? "" : " (nop)") #define call_memop(vb, op, args...) \ @@ -90,7 +91,7 @@ module_param(debug, int, 0644); }) #define log_qop(q, op) \ - dprintk(2, "call_qop(%p, %s)%s\n", q, #op, \ + dprintk(q, 2, "call_qop(%s)%s\n", #op, \ (q)->ops->op ? "" : " (nop)") #define call_qop(q, op, args...) \ @@ -113,8 +114,8 @@ module_param(debug, int, 0644); }) #define log_vb_qop(vb, op, args...) \ - dprintk(2, "call_vb_qop(%p, %d, %s)%s\n", \ - (vb)->vb2_queue, (vb)->index, #op, \ + dprintk((vb)->vb2_queue, 2, "call_vb_qop(%d, %s)%s\n", \ + (vb)->index, #op, \ (vb)->vb2_queue->ops->op ? "" : " (nop)") #define call_vb_qop(vb, op, args...) \ @@ -190,6 +191,23 @@ module_param(debug, int, 0644); static void __vb2_queue_cancel(struct vb2_queue *q); static void __enqueue_in_driver(struct vb2_buffer *vb); +static const char *vb2_state_name(enum vb2_buffer_state s) +{ + static const char * const state_names[] = { + [VB2_BUF_STATE_DEQUEUED] = "dequeued", + [VB2_BUF_STATE_IN_REQUEST] = "in request", + [VB2_BUF_STATE_PREPARING] = "preparing", + [VB2_BUF_STATE_QUEUED] = "queued", + [VB2_BUF_STATE_ACTIVE] = "active", + [VB2_BUF_STATE_DONE] = "done", + [VB2_BUF_STATE_ERROR] = "error", + }; + + if ((unsigned int)(s) < ARRAY_SIZE(state_names)) + return state_names[s]; + return "unknown"; +} + /* * __vb2_buf_mem_alloc() - allocate video memory for the given buffer */ @@ -246,7 +264,8 @@ static void __vb2_buf_mem_free(struct vb2_buffer *vb) for (plane = 0; plane < vb->num_planes; ++plane) { call_void_memop(vb, put, vb->planes[plane].mem_priv); vb->planes[plane].mem_priv = NULL; - dprintk(3, "freed plane %d of buffer %d\n", plane, vb->index); + dprintk(vb->vb2_queue, 3, "freed plane %d of buffer %d\n", + plane, vb->index); } } @@ -297,6 +316,44 @@ static void __vb2_buf_dmabuf_put(struct vb2_buffer *vb) } /* + * __vb2_buf_mem_prepare() - call ->prepare() on buffer's private memory + * to sync caches + */ +static void __vb2_buf_mem_prepare(struct vb2_buffer *vb) +{ + unsigned int plane; + + if (vb->synced) + return; + + if (vb->need_cache_sync_on_prepare) { + for (plane = 0; plane < vb->num_planes; ++plane) + call_void_memop(vb, prepare, + vb->planes[plane].mem_priv); + } + vb->synced = 1; +} + +/* + * __vb2_buf_mem_finish() - call ->finish on buffer's private memory + * to sync caches + */ +static void __vb2_buf_mem_finish(struct vb2_buffer *vb) +{ + unsigned int plane; + + if (!vb->synced) + return; + + if (vb->need_cache_sync_on_finish) { + for (plane = 0; plane < vb->num_planes; ++plane) + call_void_memop(vb, finish, + vb->planes[plane].mem_priv); + } + vb->synced = 0; +} + +/* * __setup_offsets() - setup unique offsets ("cookies") for every plane in * the buffer. */ @@ -316,7 +373,7 @@ static void __setup_offsets(struct vb2_buffer *vb) for (plane = 0; plane < vb->num_planes; ++plane) { vb->planes[plane].m.offset = off; - dprintk(3, "buffer %d, plane %d offset 0x%08lx\n", + dprintk(q, 3, "buffer %d, plane %d offset 0x%08lx\n", vb->index, plane, off); off += vb->planes[plane].length; @@ -347,7 +404,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, /* Allocate videobuf buffer structures */ vb = kzalloc(q->buf_struct_size, GFP_KERNEL); if (!vb) { - dprintk(1, "memory alloc for buffer struct failed\n"); + dprintk(q, 1, "memory alloc for buffer struct failed\n"); break; } @@ -369,7 +426,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, if (memory == VB2_MEMORY_MMAP) { ret = __vb2_buf_mem_alloc(vb); if (ret) { - dprintk(1, "failed allocating memory for buffer %d\n", + dprintk(q, 1, "failed allocating memory for buffer %d\n", buffer); q->bufs[vb->index] = NULL; kfree(vb); @@ -383,7 +440,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, */ ret = call_vb_qop(vb, buf_init, vb); if (ret) { - dprintk(1, "buffer %d %p initialization failed\n", + dprintk(q, 1, "buffer %d %p initialization failed\n", buffer, vb); __vb2_buf_mem_free(vb); q->bufs[vb->index] = NULL; @@ -393,7 +450,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, } } - dprintk(3, "allocated %d buffers, %d plane(s) each\n", + dprintk(q, 3, "allocated %d buffers, %d plane(s) each\n", buffer, num_planes); return buffer; @@ -445,7 +502,7 @@ static int __vb2_queue_free(struct vb2_queue *q, unsigned int buffers) if (q->bufs[buffer] == NULL) continue; if (q->bufs[buffer]->state == VB2_BUF_STATE_PREPARING) { - dprintk(1, "preparing buffers, cannot free\n"); + dprintk(q, 1, "preparing buffers, cannot free\n"); return -EAGAIN; } } @@ -623,12 +680,12 @@ int vb2_verify_memory_type(struct vb2_queue *q, { if (memory != VB2_MEMORY_MMAP && memory != VB2_MEMORY_USERPTR && memory != VB2_MEMORY_DMABUF) { - dprintk(1, "unsupported memory type\n"); + dprintk(q, 1, "unsupported memory type\n"); return -EINVAL; } if (type != q->type) { - dprintk(1, "requested type is incorrect\n"); + dprintk(q, 1, "requested type is incorrect\n"); return -EINVAL; } @@ -637,17 +694,17 @@ int vb2_verify_memory_type(struct vb2_queue *q, * are available. */ if (memory == VB2_MEMORY_MMAP && __verify_mmap_ops(q)) { - dprintk(1, "MMAP for current setup unsupported\n"); + dprintk(q, 1, "MMAP for current setup unsupported\n"); return -EINVAL; } if (memory == VB2_MEMORY_USERPTR && __verify_userptr_ops(q)) { - dprintk(1, "USERPTR for current setup unsupported\n"); + dprintk(q, 1, "USERPTR for current setup unsupported\n"); return -EINVAL; } if (memory == VB2_MEMORY_DMABUF && __verify_dmabuf_ops(q)) { - dprintk(1, "DMABUF for current setup unsupported\n"); + dprintk(q, 1, "DMABUF for current setup unsupported\n"); return -EINVAL; } @@ -657,33 +714,59 @@ int vb2_verify_memory_type(struct vb2_queue *q, * do the memory and type validation. */ if (vb2_fileio_is_active(q)) { - dprintk(1, "file io in progress\n"); + dprintk(q, 1, "file io in progress\n"); return -EBUSY; } return 0; } EXPORT_SYMBOL(vb2_verify_memory_type); +static void set_queue_consistency(struct vb2_queue *q, bool consistent_mem) +{ + q->dma_attrs &= ~DMA_ATTR_NON_CONSISTENT; + + if (!vb2_queue_allows_cache_hints(q)) + return; + if (!consistent_mem) + q->dma_attrs |= DMA_ATTR_NON_CONSISTENT; +} + +static bool verify_consistency_attr(struct vb2_queue *q, bool consistent_mem) +{ + bool queue_is_consistent = !(q->dma_attrs & DMA_ATTR_NON_CONSISTENT); + + if (consistent_mem != queue_is_consistent) { + dprintk(q, 1, "memory consistency model mismatch\n"); + return false; + } + return true; +} + int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, - unsigned int *count) + unsigned int flags, unsigned int *count) { unsigned int num_buffers, allocated_buffers, num_planes = 0; unsigned plane_sizes[VB2_MAX_PLANES] = { }; + bool consistent_mem = true; unsigned int i; int ret; + if (flags & V4L2_FLAG_MEMORY_NON_CONSISTENT) + consistent_mem = false; + if (q->streaming) { - dprintk(1, "streaming active\n"); + dprintk(q, 1, "streaming active\n"); return -EBUSY; } if (q->waiting_in_dqbuf && *count) { - dprintk(1, "another dup()ped fd is waiting for a buffer\n"); + dprintk(q, 1, "another dup()ped fd is waiting for a buffer\n"); return -EBUSY; } if (*count == 0 || q->num_buffers != 0 || - (q->memory != VB2_MEMORY_UNKNOWN && q->memory != memory)) { + (q->memory != VB2_MEMORY_UNKNOWN && q->memory != memory) || + !verify_consistency_attr(q, consistent_mem)) { /* * We already have buffers allocated, so first check if they * are not in use and can be freed. @@ -691,7 +774,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, mutex_lock(&q->mmap_lock); if (debug && q->memory == VB2_MEMORY_MMAP && __buffers_in_use(q)) - dprintk(1, "memory in use, orphaning buffers\n"); + dprintk(q, 1, "memory in use, orphaning buffers\n"); /* * Call queue_cancel to clean up any buffers in the @@ -720,6 +803,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, num_buffers = min_t(unsigned int, num_buffers, VB2_MAX_FRAME); memset(q->alloc_devs, 0, sizeof(q->alloc_devs)); q->memory = memory; + set_queue_consistency(q, consistent_mem); /* * Ask the driver how many buffers and planes per buffer it requires. @@ -742,7 +826,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, allocated_buffers = __vb2_queue_alloc(q, memory, num_buffers, num_planes, plane_sizes); if (allocated_buffers == 0) { - dprintk(1, "memory allocation failed\n"); + dprintk(q, 1, "memory allocation failed\n"); return -ENOMEM; } @@ -804,29 +888,39 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, EXPORT_SYMBOL_GPL(vb2_core_reqbufs); int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, - unsigned int *count, unsigned requested_planes, - const unsigned requested_sizes[]) + unsigned int flags, unsigned int *count, + unsigned int requested_planes, + const unsigned int requested_sizes[]) { unsigned int num_planes = 0, num_buffers, allocated_buffers; unsigned plane_sizes[VB2_MAX_PLANES] = { }; + bool consistent_mem = true; int ret; + if (flags & V4L2_FLAG_MEMORY_NON_CONSISTENT) + consistent_mem = false; + if (q->num_buffers == VB2_MAX_FRAME) { - dprintk(1, "maximum number of buffers already allocated\n"); + dprintk(q, 1, "maximum number of buffers already allocated\n"); return -ENOBUFS; } if (!q->num_buffers) { if (q->waiting_in_dqbuf && *count) { - dprintk(1, "another dup()ped fd is waiting for a buffer\n"); + dprintk(q, 1, "another dup()ped fd is waiting for a buffer\n"); return -EBUSY; } memset(q->alloc_devs, 0, sizeof(q->alloc_devs)); q->memory = memory; + set_queue_consistency(q, consistent_mem); q->waiting_for_buffers = !q->is_output; - } else if (q->memory != memory) { - dprintk(1, "memory model mismatch\n"); - return -EINVAL; + } else { + if (q->memory != memory) { + dprintk(q, 1, "memory model mismatch\n"); + return -EINVAL; + } + if (!verify_consistency_attr(q, consistent_mem)) + return -EINVAL; } num_buffers = min(*count, VB2_MAX_FRAME - q->num_buffers); @@ -849,7 +943,7 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, allocated_buffers = __vb2_queue_alloc(q, memory, num_buffers, num_planes, plane_sizes); if (allocated_buffers == 0) { - dprintk(1, "memory allocation failed\n"); + dprintk(q, 1, "memory allocation failed\n"); return -ENOMEM; } @@ -922,7 +1016,6 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) { struct vb2_queue *q = vb->vb2_queue; unsigned long flags; - unsigned int plane; if (WARN_ON(vb->state != VB2_BUF_STATE_ACTIVE)) return; @@ -939,15 +1032,11 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) */ vb->cnt_buf_done++; #endif - dprintk(4, "done processing on buffer %d, state: %d\n", - vb->index, state); + dprintk(q, 4, "done processing on buffer %d, state: %s\n", + vb->index, vb2_state_name(state)); - if (state != VB2_BUF_STATE_QUEUED) { - /* sync buffers */ - for (plane = 0; plane < vb->num_planes; ++plane) - call_void_memop(vb, finish, vb->planes[plane].mem_priv); - vb->synced = 0; - } + if (state != VB2_BUF_STATE_QUEUED) + __vb2_buf_mem_finish(vb); spin_lock_irqsave(&q->done_lock, flags); if (state == VB2_BUF_STATE_QUEUED) { @@ -1029,12 +1118,12 @@ static int __prepare_userptr(struct vb2_buffer *vb) && vb->planes[plane].length == planes[plane].length) continue; - dprintk(3, "userspace address for plane %d changed, reacquiring memory\n", + dprintk(q, 3, "userspace address for plane %d changed, reacquiring memory\n", plane); /* Check if the provided plane buffer is large enough */ if (planes[plane].length < vb->planes[plane].min_length) { - dprintk(1, "provided buffer size %u is less than setup size %u for plane %d\n", + dprintk(q, 1, "provided buffer size %u is less than setup size %u for plane %d\n", planes[plane].length, vb->planes[plane].min_length, plane); @@ -1064,7 +1153,7 @@ static int __prepare_userptr(struct vb2_buffer *vb) planes[plane].m.userptr, planes[plane].length, q->dma_dir); if (IS_ERR(mem_priv)) { - dprintk(1, "failed acquiring userspace memory for plane %d\n", + dprintk(q, 1, "failed acquiring userspace memory for plane %d\n", plane); ret = PTR_ERR(mem_priv); goto err; @@ -1091,14 +1180,14 @@ static int __prepare_userptr(struct vb2_buffer *vb) */ ret = call_vb_qop(vb, buf_init, vb); if (ret) { - dprintk(1, "buffer initialization failed\n"); + dprintk(q, 1, "buffer initialization failed\n"); goto err; } } ret = call_vb_qop(vb, buf_prepare, vb); if (ret) { - dprintk(1, "buffer preparation failed\n"); + dprintk(q, 1, "buffer preparation failed\n"); call_void_vb_qop(vb, buf_cleanup, vb); goto err; } @@ -1141,7 +1230,7 @@ static int __prepare_dmabuf(struct vb2_buffer *vb) struct dma_buf *dbuf = dma_buf_get(planes[plane].m.fd); if (IS_ERR_OR_NULL(dbuf)) { - dprintk(1, "invalid dmabuf fd for plane %d\n", + dprintk(q, 1, "invalid dmabuf fd for plane %d\n", plane); ret = -EINVAL; goto err; @@ -1152,7 +1241,7 @@ static int __prepare_dmabuf(struct vb2_buffer *vb) planes[plane].length = dbuf->size; if (planes[plane].length < vb->planes[plane].min_length) { - dprintk(1, "invalid dmabuf length %u for plane %d, minimum length %u\n", + dprintk(q, 1, "invalid dmabuf length %u for plane %d, minimum length %u\n", planes[plane].length, plane, vb->planes[plane].min_length); dma_buf_put(dbuf); @@ -1167,7 +1256,7 @@ static int __prepare_dmabuf(struct vb2_buffer *vb) continue; } - dprintk(3, "buffer for plane %d changed\n", plane); + dprintk(q, 3, "buffer for plane %d changed\n", plane); if (!reacquired) { reacquired = true; @@ -1187,7 +1276,7 @@ static int __prepare_dmabuf(struct vb2_buffer *vb) q->alloc_devs[plane] ? : q->dev, dbuf, planes[plane].length, q->dma_dir); if (IS_ERR(mem_priv)) { - dprintk(1, "failed to attach dmabuf\n"); + dprintk(q, 1, "failed to attach dmabuf\n"); ret = PTR_ERR(mem_priv); dma_buf_put(dbuf); goto err; @@ -1208,7 +1297,7 @@ static int __prepare_dmabuf(struct vb2_buffer *vb) ret = call_memop(vb, map_dmabuf, vb->planes[plane].mem_priv); if (ret) { - dprintk(1, "failed to map dmabuf for plane %d\n", + dprintk(q, 1, "failed to map dmabuf for plane %d\n", plane); goto err; } @@ -1233,14 +1322,14 @@ static int __prepare_dmabuf(struct vb2_buffer *vb) */ ret = call_vb_qop(vb, buf_init, vb); if (ret) { - dprintk(1, "buffer initialization failed\n"); + dprintk(q, 1, "buffer initialization failed\n"); goto err; } } ret = call_vb_qop(vb, buf_prepare, vb); if (ret) { - dprintk(1, "buffer preparation failed\n"); + dprintk(q, 1, "buffer preparation failed\n"); call_void_vb_qop(vb, buf_cleanup, vb); goto err; } @@ -1272,11 +1361,10 @@ static int __buf_prepare(struct vb2_buffer *vb) { struct vb2_queue *q = vb->vb2_queue; enum vb2_buffer_state orig_state = vb->state; - unsigned int plane; int ret; if (q->error) { - dprintk(1, "fatal error occurred on queue\n"); + dprintk(q, 1, "fatal error occurred on queue\n"); return -EIO; } @@ -1287,7 +1375,7 @@ static int __buf_prepare(struct vb2_buffer *vb) if (q->is_output) { ret = call_vb_qop(vb, buf_out_validate, vb); if (ret) { - dprintk(1, "buffer validation failed\n"); + dprintk(q, 1, "buffer validation failed\n"); return ret; } } @@ -1311,16 +1399,12 @@ static int __buf_prepare(struct vb2_buffer *vb) } if (ret) { - dprintk(1, "buffer preparation failed: %d\n", ret); + dprintk(q, 1, "buffer preparation failed: %d\n", ret); vb->state = orig_state; return ret; } - /* sync buffers */ - for (plane = 0; plane < vb->num_planes; ++plane) - call_void_memop(vb, prepare, vb->planes[plane].mem_priv); - - vb->synced = 1; + __vb2_buf_mem_prepare(vb); vb->prepared = 1; vb->state = orig_state; @@ -1423,12 +1507,12 @@ int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb) vb = q->bufs[index]; if (vb->state != VB2_BUF_STATE_DEQUEUED) { - dprintk(1, "invalid buffer state %d\n", - vb->state); + dprintk(q, 1, "invalid buffer state %s\n", + vb2_state_name(vb->state)); return -EINVAL; } if (vb->prepared) { - dprintk(1, "buffer already prepared\n"); + dprintk(q, 1, "buffer already prepared\n"); return -EINVAL; } @@ -1439,7 +1523,7 @@ int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb) /* Fill buffer information for the userspace */ call_void_bufop(q, fill_user_buffer, vb, pb); - dprintk(2, "prepare of buffer %d succeeded\n", vb->index); + dprintk(q, 2, "prepare of buffer %d succeeded\n", vb->index); return 0; } @@ -1477,7 +1561,7 @@ static int vb2_start_streaming(struct vb2_queue *q) q->start_streaming_called = 0; - dprintk(1, "driver refused to start streaming\n"); + dprintk(q, 1, "driver refused to start streaming\n"); /* * If you see this warning, then the driver isn't cleaning up properly * after a failed start_streaming(). See the start_streaming() @@ -1515,7 +1599,7 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, int ret; if (q->error) { - dprintk(1, "fatal error occurred on queue\n"); + dprintk(q, 1, "fatal error occurred on queue\n"); return -EIO; } @@ -1523,14 +1607,14 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, if (!req && vb->state != VB2_BUF_STATE_IN_REQUEST && q->requires_requests) { - dprintk(1, "qbuf requires a request\n"); + dprintk(q, 1, "qbuf requires a request\n"); return -EBADR; } if ((req && q->uses_qbuf) || (!req && vb->state != VB2_BUF_STATE_IN_REQUEST && q->uses_requests)) { - dprintk(1, "queue in wrong mode (qbuf vs requests)\n"); + dprintk(q, 1, "queue in wrong mode (qbuf vs requests)\n"); return -EBUSY; } @@ -1539,7 +1623,7 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, q->uses_requests = 1; if (vb->state != VB2_BUF_STATE_DEQUEUED) { - dprintk(1, "buffer %d not in dequeued state\n", + dprintk(q, 1, "buffer %d not in dequeued state\n", vb->index); return -EINVAL; } @@ -1547,7 +1631,7 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, if (q->is_output && !vb->prepared) { ret = call_vb_qop(vb, buf_out_validate, vb); if (ret) { - dprintk(1, "buffer validation failed\n"); + dprintk(q, 1, "buffer validation failed\n"); return ret; } } @@ -1583,7 +1667,7 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, call_void_bufop(q, fill_user_buffer, vb, pb); } - dprintk(2, "qbuf of buffer %d succeeded\n", vb->index); + dprintk(q, 2, "qbuf of buffer %d succeeded\n", vb->index); return 0; } @@ -1600,10 +1684,11 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, } break; case VB2_BUF_STATE_PREPARING: - dprintk(1, "buffer still being prepared\n"); + dprintk(q, 1, "buffer still being prepared\n"); return -EINVAL; default: - dprintk(1, "invalid buffer state %d\n", vb->state); + dprintk(q, 1, "invalid buffer state %s\n", + vb2_state_name(vb->state)); return -EINVAL; } @@ -1645,7 +1730,7 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, return ret; } - dprintk(2, "qbuf of buffer %d succeeded\n", vb->index); + dprintk(q, 2, "qbuf of buffer %d succeeded\n", vb->index); return 0; } EXPORT_SYMBOL_GPL(vb2_core_qbuf); @@ -1671,22 +1756,22 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) int ret; if (q->waiting_in_dqbuf) { - dprintk(1, "another dup()ped fd is waiting for a buffer\n"); + dprintk(q, 1, "another dup()ped fd is waiting for a buffer\n"); return -EBUSY; } if (!q->streaming) { - dprintk(1, "streaming off, will not wait for buffers\n"); + dprintk(q, 1, "streaming off, will not wait for buffers\n"); return -EINVAL; } if (q->error) { - dprintk(1, "Queue in error state, will not wait for buffers\n"); + dprintk(q, 1, "Queue in error state, will not wait for buffers\n"); return -EIO; } if (q->last_buffer_dequeued) { - dprintk(3, "last buffer dequeued already, will not wait for buffers\n"); + dprintk(q, 3, "last buffer dequeued already, will not wait for buffers\n"); return -EPIPE; } @@ -1698,7 +1783,7 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) } if (nonblocking) { - dprintk(3, "nonblocking and no buffers to dequeue, will not wait\n"); + dprintk(q, 3, "nonblocking and no buffers to dequeue, will not wait\n"); return -EAGAIN; } @@ -1713,7 +1798,7 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) /* * All locks have been released, it is safe to sleep now. */ - dprintk(3, "will sleep waiting for buffers\n"); + dprintk(q, 3, "will sleep waiting for buffers\n"); ret = wait_event_interruptible(q->done_wq, !list_empty(&q->done_list) || !q->streaming || q->error); @@ -1725,7 +1810,7 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) call_void_qop(q, wait_finish, q); q->waiting_in_dqbuf = 0; if (ret) { - dprintk(1, "sleep was interrupted\n"); + dprintk(q, 1, "sleep was interrupted\n"); return ret; } } @@ -1773,7 +1858,7 @@ static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb, int vb2_wait_for_all_buffers(struct vb2_queue *q) { if (!q->streaming) { - dprintk(1, "streaming off, will not wait for buffers\n"); + dprintk(q, 1, "streaming off, will not wait for buffers\n"); return -EINVAL; } @@ -1811,13 +1896,14 @@ int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb, switch (vb->state) { case VB2_BUF_STATE_DONE: - dprintk(3, "returning done buffer\n"); + dprintk(q, 3, "returning done buffer\n"); break; case VB2_BUF_STATE_ERROR: - dprintk(3, "returning done buffer with errors\n"); + dprintk(q, 3, "returning done buffer with errors\n"); break; default: - dprintk(1, "invalid buffer state\n"); + dprintk(q, 1, "invalid buffer state %s\n", + vb2_state_name(vb->state)); return -EINVAL; } @@ -1848,8 +1934,8 @@ int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb, media_request_put(vb->request); vb->request = NULL; - dprintk(2, "dqbuf of buffer %d, with state %d\n", - vb->index, vb->state); + dprintk(q, 2, "dqbuf of buffer %d, state: %s\n", + vb->index, vb2_state_name(vb->state)); return 0; @@ -1940,14 +2026,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q) call_void_vb_qop(vb, buf_request_complete, vb); } - if (vb->synced) { - unsigned int plane; - - for (plane = 0; plane < vb->num_planes; ++plane) - call_void_memop(vb, finish, - vb->planes[plane].mem_priv); - vb->synced = 0; - } + __vb2_buf_mem_finish(vb); if (vb->prepared) { call_void_vb_qop(vb, buf_finish, vb); @@ -1971,22 +2050,22 @@ int vb2_core_streamon(struct vb2_queue *q, unsigned int type) int ret; if (type != q->type) { - dprintk(1, "invalid stream type\n"); + dprintk(q, 1, "invalid stream type\n"); return -EINVAL; } if (q->streaming) { - dprintk(3, "already streaming\n"); + dprintk(q, 3, "already streaming\n"); return 0; } if (!q->num_buffers) { - dprintk(1, "no buffers have been allocated\n"); + dprintk(q, 1, "no buffers have been allocated\n"); return -EINVAL; } if (q->num_buffers < q->min_buffers_needed) { - dprintk(1, "need at least %u allocated buffers\n", + dprintk(q, 1, "need at least %u allocated buffers\n", q->min_buffers_needed); return -EINVAL; } @@ -2006,7 +2085,7 @@ int vb2_core_streamon(struct vb2_queue *q, unsigned int type) q->streaming = 1; - dprintk(3, "successful\n"); + dprintk(q, 3, "successful\n"); return 0; } EXPORT_SYMBOL_GPL(vb2_core_streamon); @@ -2022,7 +2101,7 @@ EXPORT_SYMBOL_GPL(vb2_queue_error); int vb2_core_streamoff(struct vb2_queue *q, unsigned int type) { if (type != q->type) { - dprintk(1, "invalid stream type\n"); + dprintk(q, 1, "invalid stream type\n"); return -EINVAL; } @@ -2039,7 +2118,7 @@ int vb2_core_streamoff(struct vb2_queue *q, unsigned int type) q->waiting_for_buffers = !q->is_output; q->last_buffer_dequeued = false; - dprintk(3, "successful\n"); + dprintk(q, 3, "successful\n"); return 0; } EXPORT_SYMBOL_GPL(vb2_core_streamoff); @@ -2082,39 +2161,39 @@ int vb2_core_expbuf(struct vb2_queue *q, int *fd, unsigned int type, struct dma_buf *dbuf; if (q->memory != VB2_MEMORY_MMAP) { - dprintk(1, "queue is not currently set up for mmap\n"); + dprintk(q, 1, "queue is not currently set up for mmap\n"); return -EINVAL; } if (!q->mem_ops->get_dmabuf) { - dprintk(1, "queue does not support DMA buffer exporting\n"); + dprintk(q, 1, "queue does not support DMA buffer exporting\n"); return -EINVAL; } if (flags & ~(O_CLOEXEC | O_ACCMODE)) { - dprintk(1, "queue does support only O_CLOEXEC and access mode flags\n"); + dprintk(q, 1, "queue does support only O_CLOEXEC and access mode flags\n"); return -EINVAL; } if (type != q->type) { - dprintk(1, "invalid buffer type\n"); + dprintk(q, 1, "invalid buffer type\n"); return -EINVAL; } if (index >= q->num_buffers) { - dprintk(1, "buffer index out of range\n"); + dprintk(q, 1, "buffer index out of range\n"); return -EINVAL; } vb = q->bufs[index]; if (plane >= vb->num_planes) { - dprintk(1, "buffer plane out of range\n"); + dprintk(q, 1, "buffer plane out of range\n"); return -EINVAL; } if (vb2_fileio_is_active(q)) { - dprintk(1, "expbuf: file io in progress\n"); + dprintk(q, 1, "expbuf: file io in progress\n"); return -EBUSY; } @@ -2123,20 +2202,20 @@ int vb2_core_expbuf(struct vb2_queue *q, int *fd, unsigned int type, dbuf = call_ptr_memop(vb, get_dmabuf, vb_plane->mem_priv, flags & O_ACCMODE); if (IS_ERR_OR_NULL(dbuf)) { - dprintk(1, "failed to export buffer %d, plane %d\n", + dprintk(q, 1, "failed to export buffer %d, plane %d\n", index, plane); return -EINVAL; } ret = dma_buf_fd(dbuf, flags & ~O_ACCMODE); if (ret < 0) { - dprintk(3, "buffer %d, plane %d failed to export (%d)\n", + dprintk(q, 3, "buffer %d, plane %d failed to export (%d)\n", index, plane, ret); dma_buf_put(dbuf); return ret; } - dprintk(3, "buffer %d, plane %d exported as %d descriptor\n", + dprintk(q, 3, "buffer %d, plane %d exported as %d descriptor\n", index, plane, ret); *fd = ret; @@ -2153,7 +2232,7 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) unsigned long length; if (q->memory != VB2_MEMORY_MMAP) { - dprintk(1, "queue is not currently set up for mmap\n"); + dprintk(q, 1, "queue is not currently set up for mmap\n"); return -EINVAL; } @@ -2161,17 +2240,17 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) * Check memory area access mode. */ if (!(vma->vm_flags & VM_SHARED)) { - dprintk(1, "invalid vma flags, VM_SHARED needed\n"); + dprintk(q, 1, "invalid vma flags, VM_SHARED needed\n"); return -EINVAL; } if (q->is_output) { if (!(vma->vm_flags & VM_WRITE)) { - dprintk(1, "invalid vma flags, VM_WRITE needed\n"); + dprintk(q, 1, "invalid vma flags, VM_WRITE needed\n"); return -EINVAL; } } else { if (!(vma->vm_flags & VM_READ)) { - dprintk(1, "invalid vma flags, VM_READ needed\n"); + dprintk(q, 1, "invalid vma flags, VM_READ needed\n"); return -EINVAL; } } @@ -2179,7 +2258,7 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) mutex_lock(&q->mmap_lock); if (vb2_fileio_is_active(q)) { - dprintk(1, "mmap: file io in progress\n"); + dprintk(q, 1, "mmap: file io in progress\n"); ret = -EBUSY; goto unlock; } @@ -2200,7 +2279,7 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) */ length = PAGE_ALIGN(vb->planes[plane].length); if (length < (vma->vm_end - vma->vm_start)) { - dprintk(1, + dprintk(q, 1, "MMAP invalid, as it would overflow buffer length\n"); ret = -EINVAL; goto unlock; @@ -2220,7 +2299,7 @@ unlock: if (ret) return ret; - dprintk(3, "buffer %d, plane %d successfully mapped\n", buffer, plane); + dprintk(q, 3, "buffer %d, plane %d successfully mapped\n", buffer, plane); return 0; } EXPORT_SYMBOL_GPL(vb2_mmap); @@ -2239,7 +2318,7 @@ unsigned long vb2_get_unmapped_area(struct vb2_queue *q, int ret; if (q->memory != VB2_MEMORY_MMAP) { - dprintk(1, "queue is not currently set up for mmap\n"); + dprintk(q, 1, "queue is not currently set up for mmap\n"); return -EINVAL; } @@ -2291,6 +2370,10 @@ int vb2_core_queue_init(struct vb2_queue *q) else q->dma_dir = q->is_output ? DMA_TO_DEVICE : DMA_FROM_DEVICE; + if (q->name[0] == '\0') + snprintf(q->name, sizeof(q->name), "%s-%p", + q->is_output ? "out" : "cap", q); + return 0; } EXPORT_SYMBOL_GPL(vb2_core_queue_init); @@ -2479,7 +2562,7 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) */ count = 1; - dprintk(3, "setting up file io: mode %s, count %d, read_once %d, write_immediately %d\n", + dprintk(q, 3, "setting up file io: mode %s, count %d, read_once %d, write_immediately %d\n", (read) ? "read" : "write", count, q->fileio_read_once, q->fileio_write_immediately); @@ -2498,7 +2581,7 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) fileio->memory = VB2_MEMORY_MMAP; fileio->type = q->type; q->fileio = fileio; - ret = vb2_core_reqbufs(q, fileio->memory, &fileio->count); + ret = vb2_core_reqbufs(q, fileio->memory, 0, &fileio->count); if (ret) goto err_kfree; @@ -2555,7 +2638,7 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) err_reqbufs: fileio->count = 0; - vb2_core_reqbufs(q, fileio->memory, &fileio->count); + vb2_core_reqbufs(q, fileio->memory, 0, &fileio->count); err_kfree: q->fileio = NULL; @@ -2575,9 +2658,9 @@ static int __vb2_cleanup_fileio(struct vb2_queue *q) vb2_core_streamoff(q, q->type); q->fileio = NULL; fileio->count = 0; - vb2_core_reqbufs(q, fileio->memory, &fileio->count); + vb2_core_reqbufs(q, fileio->memory, 0, &fileio->count); kfree(fileio); - dprintk(3, "file io emulator closed\n"); + dprintk(q, 3, "file io emulator closed\n"); } return 0; } @@ -2606,7 +2689,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ unsigned index; int ret; - dprintk(3, "mode %s, offset %ld, count %zd, %sblocking\n", + dprintk(q, 3, "mode %s, offset %ld, count %zd, %sblocking\n", read ? "read" : "write", (long)*ppos, count, nonblock ? "non" : ""); @@ -2614,7 +2697,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ return -EINVAL; if (q->waiting_in_dqbuf) { - dprintk(3, "another dup()ped fd is %s\n", + dprintk(q, 3, "another dup()ped fd is %s\n", read ? "reading" : "writing"); return -EBUSY; } @@ -2624,7 +2707,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ */ if (!vb2_fileio_is_active(q)) { ret = __vb2_init_fileio(q, read); - dprintk(3, "vb2_init_fileio result: %d\n", ret); + dprintk(q, 3, "vb2_init_fileio result: %d\n", ret); if (ret) return ret; } @@ -2641,7 +2724,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ * Call vb2_dqbuf to get buffer back. */ ret = vb2_core_dqbuf(q, &index, NULL, nonblock); - dprintk(5, "vb2_dqbuf result: %d\n", ret); + dprintk(q, 5, "vb2_dqbuf result: %d\n", ret); if (ret) return ret; fileio->dq_count += 1; @@ -2672,20 +2755,20 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ */ if (buf->pos + count > buf->size) { count = buf->size - buf->pos; - dprintk(5, "reducing read count: %zd\n", count); + dprintk(q, 5, "reducing read count: %zd\n", count); } /* * Transfer data to userspace. */ - dprintk(3, "copying %zd bytes - buffer %d, offset %u\n", + dprintk(q, 3, "copying %zd bytes - buffer %d, offset %u\n", count, index, buf->pos); if (read) ret = copy_to_user(data, buf->vaddr + buf->pos, count); else ret = copy_from_user(buf->vaddr + buf->pos, data, count); if (ret) { - dprintk(3, "error copying data\n"); + dprintk(q, 3, "error copying data\n"); return -EFAULT; } @@ -2705,7 +2788,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ * Check if this is the last buffer to read. */ if (read && fileio->read_once && fileio->dq_count == 1) { - dprintk(3, "read limit reached\n"); + dprintk(q, 3, "read limit reached\n"); return __vb2_cleanup_fileio(q); } @@ -2717,7 +2800,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ if (copy_timestamp) b->timestamp = ktime_get_ns(); ret = vb2_core_qbuf(q, index, NULL, NULL); - dprintk(5, "vb2_dbuf result: %d\n", ret); + dprintk(q, 5, "vb2_dbuf result: %d\n", ret); if (ret) return ret; @@ -2804,7 +2887,7 @@ static int vb2_thread(void *data) if (!threadio->stop) ret = vb2_core_dqbuf(q, &index, NULL, 0); call_void_qop(q, wait_prepare, q); - dprintk(5, "file io: vb2_dqbuf result: %d\n", ret); + dprintk(q, 5, "file io: vb2_dqbuf result: %d\n", ret); if (!ret) vb = q->bufs[index]; } @@ -2858,7 +2941,7 @@ int vb2_thread_start(struct vb2_queue *q, vb2_thread_fnc fnc, void *priv, threadio->priv = priv; ret = __vb2_init_fileio(q, !q->is_output); - dprintk(3, "file io: vb2_init_fileio result: %d\n", ret); + dprintk(q, 3, "file io: vb2_init_fileio result: %d\n", ret); if (ret) goto nomem; q->threadio = threadio; diff --git a/drivers/media/common/videobuf2/videobuf2-dma-contig.c b/drivers/media/common/videobuf2/videobuf2-dma-contig.c index f4b4a7c135eb..ec3446cc45b8 100644 --- a/drivers/media/common/videobuf2/videobuf2-dma-contig.c +++ b/drivers/media/common/videobuf2/videobuf2-dma-contig.c @@ -42,6 +42,11 @@ struct vb2_dc_buf { struct dma_buf_attachment *db_attach; }; +static inline bool vb2_dc_buffer_consistent(unsigned long attr) +{ + return !(attr & DMA_ATTR_NON_CONSISTENT); +} + /*********************************************/ /* scatterlist table functions */ /*********************************************/ @@ -95,8 +100,7 @@ static void vb2_dc_prepare(void *buf_priv) struct vb2_dc_buf *buf = buf_priv; struct sg_table *sgt = buf->dma_sgt; - /* DMABUF exporter will flush the cache for us */ - if (!sgt || buf->db_attach) + if (!sgt) return; dma_sync_sg_for_device(buf->dev, sgt->sgl, sgt->orig_nents, @@ -108,8 +112,7 @@ static void vb2_dc_finish(void *buf_priv) struct vb2_dc_buf *buf = buf_priv; struct sg_table *sgt = buf->dma_sgt; - /* DMABUF exporter will flush the cache for us */ - if (!sgt || buf->db_attach) + if (!sgt) return; dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir); @@ -149,8 +152,7 @@ static void *vb2_dc_alloc(struct device *dev, unsigned long attrs, if (!buf) return ERR_PTR(-ENOMEM); - if (attrs) - buf->attrs = attrs; + buf->attrs = attrs; buf->cookie = dma_alloc_attrs(dev, size, &buf->dma_addr, GFP_KERNEL | gfp_flags, buf->attrs); if (!buf->cookie) { @@ -335,6 +337,34 @@ static void vb2_dc_dmabuf_ops_release(struct dma_buf *dbuf) vb2_dc_put(dbuf->priv); } +static int +vb2_dc_dmabuf_ops_begin_cpu_access(struct dma_buf *dbuf, + enum dma_data_direction direction) +{ + struct vb2_dc_buf *buf = dbuf->priv; + struct sg_table *sgt = buf->dma_sgt; + + if (vb2_dc_buffer_consistent(buf->attrs)) + return 0; + + dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); + return 0; +} + +static int +vb2_dc_dmabuf_ops_end_cpu_access(struct dma_buf *dbuf, + enum dma_data_direction direction) +{ + struct vb2_dc_buf *buf = dbuf->priv; + struct sg_table *sgt = buf->dma_sgt; + + if (vb2_dc_buffer_consistent(buf->attrs)) + return 0; + + dma_sync_sg_for_device(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); + return 0; +} + static void *vb2_dc_dmabuf_ops_vmap(struct dma_buf *dbuf) { struct vb2_dc_buf *buf = dbuf->priv; @@ -353,6 +383,8 @@ static const struct dma_buf_ops vb2_dc_dmabuf_ops = { .detach = vb2_dc_dmabuf_ops_detach, .map_dma_buf = vb2_dc_dmabuf_ops_map, .unmap_dma_buf = vb2_dc_dmabuf_ops_unmap, + .begin_cpu_access = vb2_dc_dmabuf_ops_begin_cpu_access, + .end_cpu_access = vb2_dc_dmabuf_ops_end_cpu_access, .vmap = vb2_dc_dmabuf_ops_vmap, .mmap = vb2_dc_dmabuf_ops_mmap, .release = vb2_dc_dmabuf_ops_release, diff --git a/drivers/media/common/videobuf2/videobuf2-dma-sg.c b/drivers/media/common/videobuf2/videobuf2-dma-sg.c index 92072a08af25..0a40e00f0d7e 100644 --- a/drivers/media/common/videobuf2/videobuf2-dma-sg.c +++ b/drivers/media/common/videobuf2/videobuf2-dma-sg.c @@ -120,6 +120,12 @@ static void *vb2_dma_sg_alloc(struct device *dev, unsigned long dma_attrs, buf->num_pages = size >> PAGE_SHIFT; buf->dma_sgt = &buf->sg_table; + /* + * NOTE: dma-sg allocates memory using the page allocator directly, so + * there is no memory consistency guarantee, hence dma-sg ignores DMA + * attributes passed from the upper layer. That means that + * V4L2_FLAG_MEMORY_NON_CONSISTENT has no effect on dma-sg buffers. + */ buf->pages = kvmalloc_array(buf->num_pages, sizeof(struct page *), GFP_KERNEL | __GFP_ZERO); if (!buf->pages) @@ -198,10 +204,6 @@ static void vb2_dma_sg_prepare(void *buf_priv) struct vb2_dma_sg_buf *buf = buf_priv; struct sg_table *sgt = buf->dma_sgt; - /* DMABUF exporter will flush the cache for us */ - if (buf->db_attach) - return; - dma_sync_sg_for_device(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir); } @@ -211,10 +213,6 @@ static void vb2_dma_sg_finish(void *buf_priv) struct vb2_dma_sg_buf *buf = buf_priv; struct sg_table *sgt = buf->dma_sgt; - /* DMABUF exporter will flush the cache for us */ - if (buf->db_attach) - return; - dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir); } @@ -469,6 +467,28 @@ static void vb2_dma_sg_dmabuf_ops_release(struct dma_buf *dbuf) vb2_dma_sg_put(dbuf->priv); } +static int +vb2_dma_sg_dmabuf_ops_begin_cpu_access(struct dma_buf *dbuf, + enum dma_data_direction direction) +{ + struct vb2_dma_sg_buf *buf = dbuf->priv; + struct sg_table *sgt = buf->dma_sgt; + + dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); + return 0; +} + +static int +vb2_dma_sg_dmabuf_ops_end_cpu_access(struct dma_buf *dbuf, + enum dma_data_direction direction) +{ + struct vb2_dma_sg_buf *buf = dbuf->priv; + struct sg_table *sgt = buf->dma_sgt; + + dma_sync_sg_for_device(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); + return 0; +} + static void *vb2_dma_sg_dmabuf_ops_vmap(struct dma_buf *dbuf) { struct vb2_dma_sg_buf *buf = dbuf->priv; @@ -487,6 +507,8 @@ static const struct dma_buf_ops vb2_dma_sg_dmabuf_ops = { .detach = vb2_dma_sg_dmabuf_ops_detach, .map_dma_buf = vb2_dma_sg_dmabuf_ops_map, .unmap_dma_buf = vb2_dma_sg_dmabuf_ops_unmap, + .begin_cpu_access = vb2_dma_sg_dmabuf_ops_begin_cpu_access, + .end_cpu_access = vb2_dma_sg_dmabuf_ops_end_cpu_access, .vmap = vb2_dma_sg_dmabuf_ops_vmap, .mmap = vb2_dma_sg_dmabuf_ops_mmap, .release = vb2_dma_sg_dmabuf_ops_release, diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index eb5d5db96552..30caad27281e 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -35,10 +35,11 @@ static int debug; module_param(debug, int, 0644); -#define dprintk(level, fmt, arg...) \ +#define dprintk(q, level, fmt, arg...) \ do { \ if (debug >= level) \ - pr_info("vb2-v4l2: %s: " fmt, __func__, ## arg); \ + pr_info("vb2-v4l2: [%p] %s: " fmt, \ + (q)->name, __func__, ## arg); \ } while (0) /* Flags that are set by us */ @@ -66,12 +67,14 @@ static int __verify_planes_array(struct vb2_buffer *vb, const struct v4l2_buffer /* Is memory for copying plane information present? */ if (b->m.planes == NULL) { - dprintk(1, "multi-planar buffer passed but planes array not provided\n"); + dprintk(vb->vb2_queue, 1, + "multi-planar buffer passed but planes array not provided\n"); return -EINVAL; } if (b->length < vb->num_planes || b->length > VB2_MAX_PLANES) { - dprintk(1, "incorrect planes array length, expected %d, got %d\n", + dprintk(vb->vb2_queue, 1, + "incorrect planes array length, expected %d, got %d\n", vb->num_planes, b->length); return -EINVAL; } @@ -94,7 +97,7 @@ static int __verify_length(struct vb2_buffer *vb, const struct v4l2_buffer *b) unsigned int bytesused; unsigned int plane; - if (!V4L2_TYPE_IS_OUTPUT(b->type)) + if (V4L2_TYPE_IS_CAPTURE(b->type)) return 0; if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) { @@ -114,7 +117,8 @@ static int __verify_length(struct vb2_buffer *vb, const struct v4l2_buffer *b) return -EINVAL; } } else { - length = (b->memory == VB2_MEMORY_USERPTR) + length = (b->memory == VB2_MEMORY_USERPTR || + b->memory == VB2_MEMORY_DMABUF) ? b->length : vb->planes[0].length; if (b->bytesused > length) @@ -179,7 +183,7 @@ static int vb2_fill_vb2_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b ret = __verify_length(vb, b); if (ret < 0) { - dprintk(1, "plane parameters verification failed: %d\n", ret); + dprintk(q, 1, "plane parameters verification failed: %d\n", ret); return ret; } if (b->field == V4L2_FIELD_ALTERNATE && q->is_output) { @@ -192,7 +196,7 @@ static int vb2_fill_vb2_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b * that just says that it is either a top or a bottom field, * but not which of the two it is. */ - dprintk(1, "the field is incorrectly set to ALTERNATE for an output buffer\n"); + dprintk(q, 1, "the field is incorrectly set to ALTERNATE for an output buffer\n"); return -EINVAL; } vbuf->sequence = 0; @@ -307,7 +311,7 @@ static int vb2_fill_vb2_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b /* Zero flags that we handle */ vbuf->flags = b->flags & ~V4L2_BUFFER_MASK_FLAGS; - if (!vb->vb2_queue->copy_timestamp || !V4L2_TYPE_IS_OUTPUT(b->type)) { + if (!vb->vb2_queue->copy_timestamp || V4L2_TYPE_IS_CAPTURE(b->type)) { /* * Non-COPY timestamps and non-OUTPUT queues will get * their timestamp and timestamp source flags from the @@ -337,6 +341,53 @@ static int vb2_fill_vb2_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b return 0; } +static void set_buffer_cache_hints(struct vb2_queue *q, + struct vb2_buffer *vb, + struct v4l2_buffer *b) +{ + /* + * DMA exporter should take care of cache syncs, so we can avoid + * explicit ->prepare()/->finish() syncs. For other ->memory types + * we always need ->prepare() or/and ->finish() cache sync. + */ + if (q->memory == VB2_MEMORY_DMABUF) { + vb->need_cache_sync_on_finish = 0; + vb->need_cache_sync_on_prepare = 0; + return; + } + + /* + * Cache sync/invalidation flags are set by default in order to + * preserve existing behaviour for old apps/drivers. + */ + vb->need_cache_sync_on_prepare = 1; + vb->need_cache_sync_on_finish = 1; + + if (!vb2_queue_allows_cache_hints(q)) { + /* + * Clear buffer cache flags if queue does not support user + * space hints. That's to indicate to userspace that these + * flags won't work. + */ + b->flags &= ~V4L2_BUF_FLAG_NO_CACHE_INVALIDATE; + b->flags &= ~V4L2_BUF_FLAG_NO_CACHE_CLEAN; + return; + } + + /* + * ->finish() cache sync can be avoided when queue direction is + * TO_DEVICE. + */ + if (q->dma_dir == DMA_TO_DEVICE) + vb->need_cache_sync_on_finish = 0; + + if (b->flags & V4L2_BUF_FLAG_NO_CACHE_INVALIDATE) + vb->need_cache_sync_on_finish = 0; + + if (b->flags & V4L2_BUF_FLAG_NO_CACHE_CLEAN) + vb->need_cache_sync_on_prepare = 0; +} + static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *mdev, struct v4l2_buffer *b, bool is_prepare, struct media_request **p_req) @@ -348,23 +399,23 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md int ret; if (b->type != q->type) { - dprintk(1, "%s: invalid buffer type\n", opname); + dprintk(q, 1, "%s: invalid buffer type\n", opname); return -EINVAL; } if (b->index >= q->num_buffers) { - dprintk(1, "%s: buffer index out of range\n", opname); + dprintk(q, 1, "%s: buffer index out of range\n", opname); return -EINVAL; } if (q->bufs[b->index] == NULL) { /* Should never happen */ - dprintk(1, "%s: buffer is NULL\n", opname); + dprintk(q, 1, "%s: buffer is NULL\n", opname); return -EINVAL; } if (b->memory != q->memory) { - dprintk(1, "%s: invalid memory type\n", opname); + dprintk(q, 1, "%s: invalid memory type\n", opname); return -EINVAL; } @@ -376,11 +427,12 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md if (!is_prepare && (b->flags & V4L2_BUF_FLAG_REQUEST_FD) && vb->state != VB2_BUF_STATE_DEQUEUED) { - dprintk(1, "%s: buffer is not in dequeued state\n", opname); + dprintk(q, 1, "%s: buffer is not in dequeued state\n", opname); return -EINVAL; } if (!vb->prepared) { + set_buffer_cache_hints(q, vb, b); /* Copy relevant information provided by the userspace */ memset(vbuf->planes, 0, sizeof(vbuf->planes[0]) * vb->num_planes); @@ -394,19 +446,19 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md if (!(b->flags & V4L2_BUF_FLAG_REQUEST_FD)) { if (q->requires_requests) { - dprintk(1, "%s: queue requires requests\n", opname); + dprintk(q, 1, "%s: queue requires requests\n", opname); return -EBADR; } if (q->uses_requests) { - dprintk(1, "%s: queue uses requests\n", opname); + dprintk(q, 1, "%s: queue uses requests\n", opname); return -EBUSY; } return 0; } else if (!q->supports_requests) { - dprintk(1, "%s: queue does not support requests\n", opname); + dprintk(q, 1, "%s: queue does not support requests\n", opname); return -EBADR; } else if (q->uses_qbuf) { - dprintk(1, "%s: queue does not use requests\n", opname); + dprintk(q, 1, "%s: queue does not use requests\n", opname); return -EBUSY; } @@ -436,13 +488,13 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md return -EINVAL; if (b->request_fd < 0) { - dprintk(1, "%s: request_fd < 0\n", opname); + dprintk(q, 1, "%s: request_fd < 0\n", opname); return -EINVAL; } req = media_request_get_by_fd(mdev, b->request_fd); if (IS_ERR(req)) { - dprintk(1, "%s: invalid request_fd\n", opname); + dprintk(q, 1, "%s: invalid request_fd\n", opname); return PTR_ERR(req); } @@ -452,7 +504,7 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md */ if (req->state != MEDIA_REQUEST_STATE_IDLE && req->state != MEDIA_REQUEST_STATE_UPDATING) { - dprintk(1, "%s: request is not idle\n", opname); + dprintk(q, 1, "%s: request is not idle\n", opname); media_request_put(req); return -EBUSY; } @@ -635,12 +687,12 @@ int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b) int ret; if (b->type != q->type) { - dprintk(1, "wrong buffer type\n"); + dprintk(q, 1, "wrong buffer type\n"); return -EINVAL; } if (b->index >= q->num_buffers) { - dprintk(1, "buffer index out of range\n"); + dprintk(q, 1, "buffer index out of range\n"); return -EINVAL; } vb = q->bufs[b->index]; @@ -662,18 +714,30 @@ static void fill_buf_caps(struct vb2_queue *q, u32 *caps) *caps |= V4L2_BUF_CAP_SUPPORTS_DMABUF; if (q->subsystem_flags & VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF) *caps |= V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF; + if (q->allow_cache_hints && q->io_modes & VB2_MMAP) + *caps |= V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS; #ifdef CONFIG_MEDIA_CONTROLLER_REQUEST_API if (q->supports_requests) *caps |= V4L2_BUF_CAP_SUPPORTS_REQUESTS; #endif } +static void clear_consistency_attr(struct vb2_queue *q, + int memory, + unsigned int *flags) +{ + if (!q->allow_cache_hints || memory != V4L2_MEMORY_MMAP) + *flags &= ~V4L2_FLAG_MEMORY_NON_CONSISTENT; +} + int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) { int ret = vb2_verify_memory_type(q, req->memory, req->type); fill_buf_caps(q, &req->capabilities); - return ret ? ret : vb2_core_reqbufs(q, req->memory, &req->count); + clear_consistency_attr(q, req->memory, &req->flags); + return ret ? ret : vb2_core_reqbufs(q, req->memory, + req->flags, &req->count); } EXPORT_SYMBOL_GPL(vb2_reqbufs); @@ -683,7 +747,7 @@ int vb2_prepare_buf(struct vb2_queue *q, struct media_device *mdev, int ret; if (vb2_fileio_is_active(q)) { - dprintk(1, "file io in progress\n"); + dprintk(q, 1, "file io in progress\n"); return -EBUSY; } @@ -705,6 +769,7 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create) unsigned i; fill_buf_caps(q, &create->capabilities); + clear_consistency_attr(q, create->memory, &create->flags); create->index = q->num_buffers; if (create->count == 0) return ret != -EBUSY ? ret : 0; @@ -748,7 +813,10 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create) if (requested_sizes[i] == 0) return -EINVAL; return ret ? ret : vb2_core_create_bufs(q, create->memory, - &create->count, requested_planes, requested_sizes); + create->flags, + &create->count, + requested_planes, + requested_sizes); } EXPORT_SYMBOL_GPL(vb2_create_bufs); @@ -759,7 +827,7 @@ int vb2_qbuf(struct vb2_queue *q, struct media_device *mdev, int ret; if (vb2_fileio_is_active(q)) { - dprintk(1, "file io in progress\n"); + dprintk(q, 1, "file io in progress\n"); return -EBUSY; } @@ -778,12 +846,12 @@ int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking) int ret; if (vb2_fileio_is_active(q)) { - dprintk(1, "file io in progress\n"); + dprintk(q, 1, "file io in progress\n"); return -EBUSY; } if (b->type != q->type) { - dprintk(1, "invalid buffer type\n"); + dprintk(q, 1, "invalid buffer type\n"); return -EINVAL; } @@ -807,7 +875,7 @@ EXPORT_SYMBOL_GPL(vb2_dqbuf); int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type) { if (vb2_fileio_is_active(q)) { - dprintk(1, "file io in progress\n"); + dprintk(q, 1, "file io in progress\n"); return -EBUSY; } return vb2_core_streamon(q, type); @@ -817,7 +885,7 @@ EXPORT_SYMBOL_GPL(vb2_streamon); int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type) { if (vb2_fileio_is_active(q)) { - dprintk(1, "file io in progress\n"); + dprintk(q, 1, "file io in progress\n"); return -EBUSY; } return vb2_core_streamoff(q, type); @@ -831,7 +899,7 @@ int vb2_expbuf(struct vb2_queue *q, struct v4l2_exportbuffer *eb) } EXPORT_SYMBOL_GPL(vb2_expbuf); -int vb2_queue_init(struct vb2_queue *q) +int vb2_queue_init_name(struct vb2_queue *q, const char *name) { /* * Sanity check @@ -867,8 +935,19 @@ int vb2_queue_init(struct vb2_queue *q) */ q->quirk_poll_must_check_waiting_for_buffers = true; + if (name) + strscpy(q->name, name, sizeof(q->name)); + else + q->name[0] = '\0'; + return vb2_core_queue_init(q); } +EXPORT_SYMBOL_GPL(vb2_queue_init_name); + +int vb2_queue_init(struct vb2_queue *q) +{ + return vb2_queue_init_name(q, NULL); +} EXPORT_SYMBOL_GPL(vb2_queue_init); void vb2_queue_release(struct vb2_queue *q) @@ -919,11 +998,12 @@ int vb2_ioctl_reqbufs(struct file *file, void *priv, int res = vb2_verify_memory_type(vdev->queue, p->memory, p->type); fill_buf_caps(vdev->queue, &p->capabilities); + clear_consistency_attr(vdev->queue, p->memory, &p->flags); if (res) return res; if (vb2_queue_is_busy(vdev, file)) return -EBUSY; - res = vb2_core_reqbufs(vdev->queue, p->memory, &p->count); + res = vb2_core_reqbufs(vdev->queue, p->memory, p->flags, &p->count); /* If count == 0, then the owner has released all buffers and he is no longer owner of the queue. Otherwise we have a new owner. */ if (res == 0) @@ -941,6 +1021,7 @@ int vb2_ioctl_create_bufs(struct file *file, void *priv, p->index = vdev->queue->num_buffers; fill_buf_caps(vdev->queue, &p->capabilities); + clear_consistency_attr(vdev->queue, p->memory, &p->flags); /* * If count == 0, then just check if memory and type are valid. * Any -EBUSY result from vb2_verify_memory_type can be mapped to 0. diff --git a/drivers/media/dvb-core/dvb_vb2.c b/drivers/media/dvb-core/dvb_vb2.c index 6974f1731529..959d110407a4 100644 --- a/drivers/media/dvb-core/dvb_vb2.c +++ b/drivers/media/dvb-core/dvb_vb2.c @@ -342,7 +342,7 @@ int dvb_vb2_reqbufs(struct dvb_vb2_ctx *ctx, struct dmx_requestbuffers *req) ctx->buf_siz = req->size; ctx->buf_cnt = req->count; - ret = vb2_core_reqbufs(&ctx->vb_q, VB2_MEMORY_MMAP, &req->count); + ret = vb2_core_reqbufs(&ctx->vb_q, VB2_MEMORY_MMAP, 0, &req->count); if (ret) { ctx->state = DVB_VB2_STATE_NONE; dprintk(1, "[%s] count=%d size=%d errno=%d\n", ctx->name, diff --git a/drivers/media/dvb-frontends/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c index 7e8e5c308d1c..0ae9d8c72d8d 100644 --- a/drivers/media/dvb-frontends/drxk_hard.c +++ b/drivers/media/dvb-frontends/drxk_hard.c @@ -155,17 +155,6 @@ if (debug >= level) \ printk(KERN_DEBUG KBUILD_MODNAME ": %s " fmt, __func__, ##arg); \ } while (0) - -static inline u32 MulDiv32(u32 a, u32 b, u32 c) -{ - u64 tmp64; - - tmp64 = (u64) a * (u64) b; - do_div(tmp64, c); - - return (u32) tmp64; -} - static inline u32 Frac28a(u32 a, u32 c) { int i = 0; diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c index 60d1e59d2292..720756728f2d 100644 --- a/drivers/media/dvb-frontends/rtl2832_sdr.c +++ b/drivers/media/dvb-frontends/rtl2832_sdr.c @@ -5,7 +5,7 @@ * Copyright (C) 2013 Antti Palosaari <crope@iki.fi> * * GNU Radio plugin "gr-kernel" for device usage will be on: - * http://git.linuxtv.org/anttip/gr-kernel.git + * https://git.linuxtv.org/anttip/gr-kernel.git */ #include "rtl2832_sdr.h" diff --git a/drivers/media/firewire/firedtv-fw.c b/drivers/media/firewire/firedtv-fw.c index 97144734eb05..3f1ca40b9b98 100644 --- a/drivers/media/firewire/firedtv-fw.c +++ b/drivers/media/firewire/firedtv-fw.c @@ -272,6 +272,8 @@ static int node_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) name_len = fw_csr_string(unit->directory, CSR_MODEL, name, sizeof(name)); + if (name_len < 0) + return name_len; for (i = ARRAY_SIZE(model_names); --i; ) if (strlen(model_names[i]) <= name_len && strncmp(name, model_names[i], name_len) == 0) diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index da11036ad804..48ae60a2c603 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -464,6 +464,19 @@ config VIDEO_VPX3220 To compile this driver as a module, choose M here: the module will be called vpx3220. +config VIDEO_MAX9286 + tristate "Maxim MAX9286 GMSL deserializer support" + depends on I2C && I2C_MUX + depends on OF + select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + select MEDIA_CONTROLLER + help + This driver supports the Maxim MAX9286 GMSL deserializer. + + To compile this driver as a module, choose M here: the + module will be called max9286. + comment "Video and audio decoders" config VIDEO_SAA717X @@ -860,6 +873,7 @@ config VIDEO_OV2685 config VIDEO_OV2740 tristate "OmniVision OV2740 sensor support" depends on VIDEO_V4L2 && I2C + depends on ACPI || COMPILE_TEST select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE @@ -1157,6 +1171,19 @@ config VIDEO_NOON010PC30 source "drivers/media/i2c/m5mols/Kconfig" +config VIDEO_RDACM20 + tristate "IMI RDACM20 camera support" + depends on I2C + select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + select MEDIA_CONTROLLER + help + This driver supports the IMI RDACM20 GMSL camera, used in + ADAS systems. + + This camera should be used in conjunction with a GMSL + deserialiser such as the MAX9286. + config VIDEO_RJ54N1 tristate "Sharp RJ54N1CB0C sensor support" depends on I2C && VIDEO_V4L2 @@ -1253,6 +1280,18 @@ config VIDEO_DW9714 capability. This is designed for linear control of voice coil motors, controlled via I2C serial interface. +config VIDEO_DW9768 + tristate "DW9768 lens voice coil support" + depends on I2C && VIDEO_V4L2 + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This is a driver for the DW9768 camera lens voice coil. + DW9768 is a 10 bit DAC with 100mA output current sink + capability. This is designed for linear control of + voice coil motors, controlled via I2C serial interface. + config VIDEO_DW9807_VCM tristate "DW9807 lens voice coil support" depends on I2C && VIDEO_V4L2 diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 993acab81b2c..f0a77473979d 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_VIDEO_SAA6752HS) += saa6752hs.o obj-$(CONFIG_VIDEO_AD5820) += ad5820.o obj-$(CONFIG_VIDEO_AK7375) += ak7375.o obj-$(CONFIG_VIDEO_DW9714) += dw9714.o +obj-$(CONFIG_VIDEO_DW9768) += dw9768.o obj-$(CONFIG_VIDEO_DW9807_VCM) += dw9807-vcm.o obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o @@ -118,6 +119,9 @@ obj-$(CONFIG_VIDEO_IMX274) += imx274.o obj-$(CONFIG_VIDEO_IMX290) += imx290.o obj-$(CONFIG_VIDEO_IMX319) += imx319.o obj-$(CONFIG_VIDEO_IMX355) += imx355.o +obj-$(CONFIG_VIDEO_MAX9286) += max9286.o +rdacm20-camera_module-objs := rdacm20.o max9271.o +obj-$(CONFIG_VIDEO_RDACM20) += rdacm20-camera_module.o obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o obj-$(CONFIG_SDR_MAX2175) += max2175.o diff --git a/drivers/media/i2c/dw9768.c b/drivers/media/i2c/dw9768.c new file mode 100644 index 000000000000..45cdd924b565 --- /dev/null +++ b/drivers/media/i2c/dw9768.c @@ -0,0 +1,554 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2020 MediaTek Inc. + +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> +#include <media/v4l2-async.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> + +#define DW9768_NAME "dw9768" +#define DW9768_MAX_FOCUS_POS (1024 - 1) +/* + * This sets the minimum granularity for the focus positions. + * A value of 1 gives maximum accuracy for a desired focus position + */ +#define DW9768_FOCUS_STEPS 1 + +/* + * Ring control and Power control register + * Bit[1] RING_EN + * 0: Direct mode + * 1: AAC mode (ringing control mode) + * Bit[0] PD + * 0: Normal operation mode + * 1: Power down mode + * DW9768 requires waiting time of Topr after PD reset takes place. + */ +#define DW9768_RING_PD_CONTROL_REG 0x02 +#define DW9768_PD_MODE_OFF 0x00 +#define DW9768_PD_MODE_EN BIT(0) +#define DW9768_AAC_MODE_EN BIT(1) + +/* + * DW9768 separates two registers to control the VCM position. + * One for MSB value, another is LSB value. + * DAC_MSB: D[9:8] (ADD: 0x03) + * DAC_LSB: D[7:0] (ADD: 0x04) + * D[9:0] DAC data input: positive output current = D[9:0] / 1023 * 100[mA] + */ +#define DW9768_MSB_ADDR 0x03 +#define DW9768_LSB_ADDR 0x04 +#define DW9768_STATUS_ADDR 0x05 + +/* + * AAC mode control & prescale register + * Bit[7:5] Namely AC[2:0], decide the VCM mode and operation time. + * 001 AAC2 0.48 x Tvib + * 010 AAC3 0.70 x Tvib + * 011 AAC4 0.75 x Tvib + * 101 AAC8 1.13 x Tvib + * Bit[2:0] Namely PRESC[2:0], set the internal clock dividing rate as follow. + * 000 2 + * 001 1 + * 010 1/2 + * 011 1/4 + * 100 8 + * 101 4 + */ +#define DW9768_AAC_PRESC_REG 0x06 +#define DW9768_AAC_MODE_SEL_MASK GENMASK(7, 5) +#define DW9768_CLOCK_PRE_SCALE_SEL_MASK GENMASK(2, 0) + +/* + * VCM period of vibration register + * Bit[5:0] Defined as VCM rising periodic time (Tvib) together with PRESC[2:0] + * Tvib = (6.3ms + AACT[5:0] * 0.1ms) * Dividing Rate + * Dividing Rate is the internal clock dividing rate that is defined at + * PRESCALE register (ADD: 0x06) + */ +#define DW9768_AAC_TIME_REG 0x07 + +/* + * DW9768 requires waiting time (delay time) of t_OPR after power-up, + * or in the case of PD reset taking place. + */ +#define DW9768_T_OPR_US 1000 +#define DW9768_TVIB_MS_BASE10 (64 - 1) +#define DW9768_AAC_MODE_DEFAULT 2 +#define DW9768_AAC_TIME_DEFAULT 0x20 +#define DW9768_CLOCK_PRE_SCALE_DEFAULT 1 + +/* + * This acts as the minimum granularity of lens movement. + * Keep this value power of 2, so the control steps can be + * uniformly adjusted for gradual lens movement, with desired + * number of control steps. + */ +#define DW9768_MOVE_STEPS 16 + +static const char * const dw9768_supply_names[] = { + "vin", /* Digital I/O power */ + "vdd", /* Digital core power */ +}; + +/* dw9768 device structure */ +struct dw9768 { + struct regulator_bulk_data supplies[ARRAY_SIZE(dw9768_supply_names)]; + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *focus; + struct v4l2_subdev sd; + + u32 aac_mode; + u32 aac_timing; + u32 clock_presc; + u32 move_delay_us; +}; + +static inline struct dw9768 *sd_to_dw9768(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct dw9768, sd); +} + +struct regval_list { + u8 reg_num; + u8 value; +}; + +struct dw9768_aac_mode_ot_multi { + u32 aac_mode_enum; + u32 ot_multi_base100; +}; + +struct dw9768_clk_presc_dividing_rate { + u32 clk_presc_enum; + u32 dividing_rate_base100; +}; + +static const struct dw9768_aac_mode_ot_multi aac_mode_ot_multi[] = { + {1, 48}, + {2, 70}, + {3, 75}, + {5, 113}, +}; + +static const struct dw9768_clk_presc_dividing_rate presc_dividing_rate[] = { + {0, 200}, + {1, 100}, + {2, 50}, + {3, 25}, + {4, 800}, + {5, 400}, +}; + +static u32 dw9768_find_ot_multi(u32 aac_mode_param) +{ + u32 cur_ot_multi_base100 = 70; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(aac_mode_ot_multi); i++) { + if (aac_mode_ot_multi[i].aac_mode_enum == aac_mode_param) { + cur_ot_multi_base100 = + aac_mode_ot_multi[i].ot_multi_base100; + } + } + + return cur_ot_multi_base100; +} + +static u32 dw9768_find_dividing_rate(u32 presc_param) +{ + u32 cur_clk_dividing_rate_base100 = 100; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(presc_dividing_rate); i++) { + if (presc_dividing_rate[i].clk_presc_enum == presc_param) { + cur_clk_dividing_rate_base100 = + presc_dividing_rate[i].dividing_rate_base100; + } + } + + return cur_clk_dividing_rate_base100; +} + +/* + * DW9768_AAC_PRESC_REG & DW9768_AAC_TIME_REG determine VCM operation time. + * For current VCM mode: AAC3, Operation Time would be 0.70 x Tvib. + * Tvib = (6.3ms + AACT[5:0] * 0.1MS) * Dividing Rate. + * Below is calculation of the operation delay for each step. + */ +static inline u32 dw9768_cal_move_delay(u32 aac_mode_param, u32 presc_param, + u32 aac_timing_param) +{ + u32 Tvib_us; + u32 ot_multi_base100; + u32 clk_dividing_rate_base100; + + ot_multi_base100 = dw9768_find_ot_multi(aac_mode_param); + + clk_dividing_rate_base100 = dw9768_find_dividing_rate(presc_param); + + Tvib_us = (DW9768_TVIB_MS_BASE10 + aac_timing_param) * + clk_dividing_rate_base100; + + return Tvib_us * ot_multi_base100 / 100; +} + +static int dw9768_mod_reg(struct dw9768 *dw9768, u8 reg, u8 mask, u8 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&dw9768->sd); + int ret; + + ret = i2c_smbus_read_byte_data(client, reg); + if (ret < 0) + return ret; + + val = ((unsigned char)ret & ~mask) | (val & mask); + + return i2c_smbus_write_byte_data(client, reg, val); +} + +static int dw9768_set_dac(struct dw9768 *dw9768, u16 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&dw9768->sd); + + /* Write VCM position to registers */ + return i2c_smbus_write_word_swapped(client, DW9768_MSB_ADDR, val); +} + +static int dw9768_init(struct dw9768 *dw9768) +{ + struct i2c_client *client = v4l2_get_subdevdata(&dw9768->sd); + int ret, val; + + /* Reset DW9768_RING_PD_CONTROL_REG to default status 0x00 */ + ret = i2c_smbus_write_byte_data(client, DW9768_RING_PD_CONTROL_REG, + DW9768_PD_MODE_OFF); + if (ret < 0) + return ret; + + /* + * DW9769 requires waiting delay time of t_OPR + * after PD reset takes place. + */ + usleep_range(DW9768_T_OPR_US, DW9768_T_OPR_US + 100); + + /* Set DW9768_RING_PD_CONTROL_REG to DW9768_AAC_MODE_EN(0x01) */ + ret = i2c_smbus_write_byte_data(client, DW9768_RING_PD_CONTROL_REG, + DW9768_AAC_MODE_EN); + if (ret < 0) + return ret; + + /* Set AAC mode */ + ret = dw9768_mod_reg(dw9768, DW9768_AAC_PRESC_REG, + DW9768_AAC_MODE_SEL_MASK, + dw9768->aac_mode << 5); + if (ret < 0) + return ret; + + /* Set clock presc */ + if (dw9768->clock_presc != DW9768_CLOCK_PRE_SCALE_DEFAULT) { + ret = dw9768_mod_reg(dw9768, DW9768_AAC_PRESC_REG, + DW9768_CLOCK_PRE_SCALE_SEL_MASK, + dw9768->clock_presc); + if (ret < 0) + return ret; + } + + /* Set AAC Timing */ + if (dw9768->aac_timing != DW9768_AAC_TIME_DEFAULT) { + ret = i2c_smbus_write_byte_data(client, DW9768_AAC_TIME_REG, + dw9768->aac_timing); + if (ret < 0) + return ret; + } + + for (val = dw9768->focus->val % DW9768_MOVE_STEPS; + val <= dw9768->focus->val; + val += DW9768_MOVE_STEPS) { + ret = dw9768_set_dac(dw9768, val); + if (ret) { + dev_err(&client->dev, "I2C failure: %d", ret); + return ret; + } + usleep_range(dw9768->move_delay_us, + dw9768->move_delay_us + 1000); + } + + return 0; +} + +static int dw9768_release(struct dw9768 *dw9768) +{ + struct i2c_client *client = v4l2_get_subdevdata(&dw9768->sd); + int ret, val; + + val = round_down(dw9768->focus->val, DW9768_MOVE_STEPS); + for ( ; val >= 0; val -= DW9768_MOVE_STEPS) { + ret = dw9768_set_dac(dw9768, val); + if (ret) { + dev_err(&client->dev, "I2C write fail: %d", ret); + return ret; + } + usleep_range(dw9768->move_delay_us, + dw9768->move_delay_us + 1000); + } + + ret = i2c_smbus_write_byte_data(client, DW9768_RING_PD_CONTROL_REG, + DW9768_PD_MODE_EN); + if (ret < 0) + return ret; + + /* + * DW9769 requires waiting delay time of t_OPR + * after PD reset takes place. + */ + usleep_range(DW9768_T_OPR_US, DW9768_T_OPR_US + 100); + + return 0; +} + +static int dw9768_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct dw9768 *dw9768 = sd_to_dw9768(sd); + + dw9768_release(dw9768); + regulator_bulk_disable(ARRAY_SIZE(dw9768_supply_names), + dw9768->supplies); + + return 0; +} + +static int dw9768_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct dw9768 *dw9768 = sd_to_dw9768(sd); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(dw9768_supply_names), + dw9768->supplies); + if (ret < 0) { + dev_err(dev, "failed to enable regulators\n"); + return ret; + } + + /* + * The datasheet refers to t_OPR that needs to be waited before sending + * I2C commands after power-up. + */ + usleep_range(DW9768_T_OPR_US, DW9768_T_OPR_US + 100); + + ret = dw9768_init(dw9768); + if (ret < 0) + goto disable_regulator; + + return 0; + +disable_regulator: + regulator_bulk_disable(ARRAY_SIZE(dw9768_supply_names), + dw9768->supplies); + + return ret; +} + +static int dw9768_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct dw9768 *dw9768 = container_of(ctrl->handler, + struct dw9768, ctrls); + + if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE) + return dw9768_set_dac(dw9768, ctrl->val); + + return 0; +} + +static const struct v4l2_ctrl_ops dw9768_ctrl_ops = { + .s_ctrl = dw9768_set_ctrl, +}; + +static int dw9768_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + int ret; + + ret = pm_runtime_get_sync(sd->dev); + if (ret < 0) { + pm_runtime_put_noidle(sd->dev); + return ret; + } + + return 0; +} + +static int dw9768_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + pm_runtime_put(sd->dev); + + return 0; +} + +static const struct v4l2_subdev_internal_ops dw9768_int_ops = { + .open = dw9768_open, + .close = dw9768_close, +}; + +static const struct v4l2_subdev_ops dw9768_ops = { }; + +static int dw9768_init_controls(struct dw9768 *dw9768) +{ + struct v4l2_ctrl_handler *hdl = &dw9768->ctrls; + const struct v4l2_ctrl_ops *ops = &dw9768_ctrl_ops; + + v4l2_ctrl_handler_init(hdl, 1); + + dw9768->focus = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FOCUS_ABSOLUTE, 0, + DW9768_MAX_FOCUS_POS, + DW9768_FOCUS_STEPS, 0); + + if (hdl->error) + return hdl->error; + + dw9768->sd.ctrl_handler = hdl; + + return 0; +} + +static int dw9768_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct dw9768 *dw9768; + unsigned int i; + int ret; + + dw9768 = devm_kzalloc(dev, sizeof(*dw9768), GFP_KERNEL); + if (!dw9768) + return -ENOMEM; + + /* Initialize subdev */ + v4l2_i2c_subdev_init(&dw9768->sd, client, &dw9768_ops); + + dw9768->aac_mode = DW9768_AAC_MODE_DEFAULT; + dw9768->aac_timing = DW9768_AAC_TIME_DEFAULT; + dw9768->clock_presc = DW9768_CLOCK_PRE_SCALE_DEFAULT; + + /* Optional indication of AAC mode select */ + fwnode_property_read_u32(dev_fwnode(dev), "dongwoon,aac-mode", + &dw9768->aac_mode); + + /* Optional indication of clock pre-scale select */ + fwnode_property_read_u32(dev_fwnode(dev), "dongwoon,clock-presc", + &dw9768->clock_presc); + + /* Optional indication of AAC Timing */ + fwnode_property_read_u32(dev_fwnode(dev), "dongwoon,aac-timing", + &dw9768->aac_timing); + + dw9768->move_delay_us = dw9768_cal_move_delay(dw9768->aac_mode, + dw9768->clock_presc, + dw9768->aac_timing); + + for (i = 0; i < ARRAY_SIZE(dw9768_supply_names); i++) + dw9768->supplies[i].supply = dw9768_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(dw9768_supply_names), + dw9768->supplies); + if (ret) { + dev_err(dev, "failed to get regulators\n"); + return ret; + } + + /* Initialize controls */ + ret = dw9768_init_controls(dw9768); + if (ret) + goto err_free_handler; + + /* Initialize subdev */ + dw9768->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + dw9768->sd.internal_ops = &dw9768_int_ops; + + ret = media_entity_pads_init(&dw9768->sd.entity, 0, NULL); + if (ret < 0) + goto err_free_handler; + + dw9768->sd.entity.function = MEDIA_ENT_F_LENS; + + pm_runtime_enable(dev); + if (!pm_runtime_enabled(dev)) { + ret = dw9768_runtime_resume(dev); + if (ret < 0) { + dev_err(dev, "failed to power on: %d\n", ret); + goto err_clean_entity; + } + } + + ret = v4l2_async_register_subdev(&dw9768->sd); + if (ret < 0) { + dev_err(dev, "failed to register V4L2 subdev: %d", ret); + goto err_power_off; + } + + return 0; + +err_power_off: + if (pm_runtime_enabled(dev)) + pm_runtime_disable(dev); + else + dw9768_runtime_suspend(dev); +err_clean_entity: + media_entity_cleanup(&dw9768->sd.entity); +err_free_handler: + v4l2_ctrl_handler_free(&dw9768->ctrls); + + return ret; +} + +static int dw9768_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct dw9768 *dw9768 = sd_to_dw9768(sd); + + v4l2_async_unregister_subdev(&dw9768->sd); + v4l2_ctrl_handler_free(&dw9768->ctrls); + media_entity_cleanup(&dw9768->sd.entity); + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + dw9768_runtime_suspend(&client->dev); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +static const struct of_device_id dw9768_of_table[] = { + { .compatible = "dongwoon,dw9768" }, + { .compatible = "giantec,gt9769" }, + {} +}; +MODULE_DEVICE_TABLE(of, dw9768_of_table); + +static const struct dev_pm_ops dw9768_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(dw9768_runtime_suspend, dw9768_runtime_resume, NULL) +}; + +static struct i2c_driver dw9768_i2c_driver = { + .driver = { + .name = DW9768_NAME, + .pm = &dw9768_pm_ops, + .of_match_table = dw9768_of_table, + }, + .probe_new = dw9768_probe, + .remove = dw9768_remove, +}; +module_i2c_driver(dw9768_i2c_driver); + +MODULE_AUTHOR("Dongchun Zhu <dongchun.zhu@mediatek.com>"); +MODULE_DESCRIPTION("DW9768 VCM driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index f7678e5a5d87..adcddf3204f7 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -25,9 +25,19 @@ #define IMX290_STANDBY 0x3000 #define IMX290_REGHOLD 0x3001 #define IMX290_XMSTA 0x3002 +#define IMX290_FR_FDG_SEL 0x3009 +#define IMX290_BLKLEVEL_LOW 0x300a +#define IMX290_BLKLEVEL_HIGH 0x300b #define IMX290_GAIN 0x3014 +#define IMX290_HMAX_LOW 0x301c +#define IMX290_HMAX_HIGH 0x301d +#define IMX290_PGCTRL 0x308c +#define IMX290_PHY_LANE_NUM 0x3407 +#define IMX290_CSI_LANE_MODE 0x3443 -#define IMX290_DEFAULT_LINK_FREQ 445500000 +#define IMX290_PGCTRL_REGEN BIT(0) +#define IMX290_PGCTRL_THRU BIT(1) +#define IMX290_PGCTRL_MODE(n) ((n) << 4) static const char * const imx290_supply_name[] = { "vdda", @@ -45,8 +55,8 @@ struct imx290_regval { struct imx290_mode { u32 width; u32 height; - u32 pixel_rate; - u32 link_freq_index; + u32 hmax; + u8 link_freq_index; const struct imx290_regval *data; u32 data_size; @@ -56,9 +66,10 @@ struct imx290 { struct device *dev; struct clk *xclk; struct regmap *regmap; + u8 nlanes; + u8 bpp; struct v4l2_subdev sd; - struct v4l2_fwnode_endpoint ep; struct media_pad pad; struct v4l2_mbus_framefmt current_format; const struct imx290_mode *current_mode; @@ -75,10 +86,12 @@ struct imx290 { struct imx290_pixfmt { u32 code; + u8 bpp; }; static const struct imx290_pixfmt imx290_formats[] = { - { MEDIA_BUS_FMT_SRGGB10_1X10 }, + { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, + { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, }; static const struct regmap_config imx290_regmap_config = { @@ -87,16 +100,24 @@ static const struct regmap_config imx290_regmap_config = { .cache_type = REGCACHE_RBTREE, }; +static const char * const imx290_test_pattern_menu[] = { + "Disabled", + "Sequence Pattern 1", + "Horizontal Color-bar Chart", + "Vertical Color-bar Chart", + "Sequence Pattern 2", + "Gradation Pattern 1", + "Gradation Pattern 2", + "000/555h Toggle Pattern", +}; + static const struct imx290_regval imx290_global_init_settings[] = { { 0x3007, 0x00 }, - { 0x3009, 0x00 }, { 0x3018, 0x65 }, { 0x3019, 0x04 }, { 0x301a, 0x00 }, - { 0x3443, 0x03 }, { 0x3444, 0x20 }, { 0x3445, 0x25 }, - { 0x3407, 0x03 }, { 0x303a, 0x0c }, { 0x3040, 0x00 }, { 0x3041, 0x00 }, @@ -169,7 +190,6 @@ static const struct imx290_regval imx290_1080p_settings[] = { { 0x3164, 0x1a }, { 0x3480, 0x49 }, /* data rate settings */ - { 0x3009, 0x01 }, { 0x3405, 0x10 }, { 0x3446, 0x57 }, { 0x3447, 0x00 }, @@ -187,8 +207,6 @@ static const struct imx290_regval imx290_1080p_settings[] = { { 0x3453, 0x00 }, { 0x3454, 0x17 }, { 0x3455, 0x00 }, - { 0x301c, 0x98 }, - { 0x301d, 0x08 }, }; static const struct imx290_regval imx290_720p_settings[] = { @@ -210,7 +228,6 @@ static const struct imx290_regval imx290_720p_settings[] = { { 0x3164, 0x1a }, { 0x3480, 0x49 }, /* data rate settings */ - { 0x3009, 0x01 }, { 0x3405, 0x10 }, { 0x3446, 0x4f }, { 0x3447, 0x00 }, @@ -228,8 +245,6 @@ static const struct imx290_regval imx290_720p_settings[] = { { 0x3453, 0x00 }, { 0x3454, 0x17 }, { 0x3455, 0x00 }, - { 0x301c, 0xe4 }, - { 0x301d, 0x0c }, }; static const struct imx290_regval imx290_10bit_settings[] = { @@ -244,31 +259,105 @@ static const struct imx290_regval imx290_10bit_settings[] = { { 0x300b, 0x00}, }; +static const struct imx290_regval imx290_12bit_settings[] = { + { 0x3005, 0x01 }, + { 0x3046, 0x01 }, + { 0x3129, 0x00 }, + { 0x317c, 0x00 }, + { 0x31ec, 0x0e }, + { 0x3441, 0x0c }, + { 0x3442, 0x0c }, + { 0x300a, 0xf0 }, + { 0x300b, 0x00 }, +}; + /* supported link frequencies */ -static const s64 imx290_link_freq[] = { - IMX290_DEFAULT_LINK_FREQ, +#define FREQ_INDEX_1080P 0 +#define FREQ_INDEX_720P 1 +static const s64 imx290_link_freq_2lanes[] = { + [FREQ_INDEX_1080P] = 445500000, + [FREQ_INDEX_720P] = 297000000, }; +static const s64 imx290_link_freq_4lanes[] = { + [FREQ_INDEX_1080P] = 222750000, + [FREQ_INDEX_720P] = 148500000, +}; + +/* + * In this function and in the similar ones below We rely on imx290_probe() + * to ensure that nlanes is either 2 or 4. + */ +static inline const s64 *imx290_link_freqs_ptr(const struct imx290 *imx290) +{ + if (imx290->nlanes == 2) + return imx290_link_freq_2lanes; + else + return imx290_link_freq_4lanes; +} + +static inline int imx290_link_freqs_num(const struct imx290 *imx290) +{ + if (imx290->nlanes == 2) + return ARRAY_SIZE(imx290_link_freq_2lanes); + else + return ARRAY_SIZE(imx290_link_freq_4lanes); +} /* Mode configs */ -static const struct imx290_mode imx290_modes[] = { +static const struct imx290_mode imx290_modes_2lanes[] = { + { + .width = 1920, + .height = 1080, + .hmax = 0x1130, + .link_freq_index = FREQ_INDEX_1080P, + .data = imx290_1080p_settings, + .data_size = ARRAY_SIZE(imx290_1080p_settings), + }, + { + .width = 1280, + .height = 720, + .hmax = 0x19c8, + .link_freq_index = FREQ_INDEX_720P, + .data = imx290_720p_settings, + .data_size = ARRAY_SIZE(imx290_720p_settings), + }, +}; + +static const struct imx290_mode imx290_modes_4lanes[] = { { .width = 1920, .height = 1080, + .hmax = 0x0898, + .link_freq_index = FREQ_INDEX_1080P, .data = imx290_1080p_settings, .data_size = ARRAY_SIZE(imx290_1080p_settings), - .pixel_rate = 178200000, - .link_freq_index = 0, }, { .width = 1280, .height = 720, + .hmax = 0x0ce4, + .link_freq_index = FREQ_INDEX_720P, .data = imx290_720p_settings, .data_size = ARRAY_SIZE(imx290_720p_settings), - .pixel_rate = 178200000, - .link_freq_index = 0, }, }; +static inline const struct imx290_mode *imx290_modes_ptr(const struct imx290 *imx290) +{ + if (imx290->nlanes == 2) + return imx290_modes_2lanes; + else + return imx290_modes_4lanes; +} + +static inline int imx290_modes_num(const struct imx290 *imx290) +{ + if (imx290->nlanes == 2) + return ARRAY_SIZE(imx290_modes_2lanes); + else + return ARRAY_SIZE(imx290_modes_4lanes); +} + static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd) { return container_of(_sd, struct imx290, sd); @@ -314,11 +403,11 @@ static int imx290_set_register_array(struct imx290 *imx290, ret = imx290_write_reg(imx290, settings->reg, settings->val); if (ret < 0) return ret; - - /* Settle time is 10ms for all registers */ - msleep(10); } + /* Provide 10ms settle time */ + usleep_range(10000, 11000); + return 0; } @@ -391,6 +480,27 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_GAIN: ret = imx290_set_gain(imx290, ctrl->val); break; + case V4L2_CID_TEST_PATTERN: + if (ctrl->val) { + imx290_write_reg(imx290, IMX290_BLKLEVEL_LOW, 0x00); + imx290_write_reg(imx290, IMX290_BLKLEVEL_HIGH, 0x00); + usleep_range(10000, 11000); + imx290_write_reg(imx290, IMX290_PGCTRL, + (u8)(IMX290_PGCTRL_REGEN | + IMX290_PGCTRL_THRU | + IMX290_PGCTRL_MODE(ctrl->val))); + } else { + imx290_write_reg(imx290, IMX290_PGCTRL, 0x00); + usleep_range(10000, 11000); + if (imx290->bpp == 10) + imx290_write_reg(imx290, IMX290_BLKLEVEL_LOW, + 0x3c); + else /* 12 bits per pixel */ + imx290_write_reg(imx290, IMX290_BLKLEVEL_LOW, + 0xf0); + imx290_write_reg(imx290, IMX290_BLKLEVEL_HIGH, 0x00); + } + break; default: ret = -EINVAL; break; @@ -417,6 +527,28 @@ static int imx290_enum_mbus_code(struct v4l2_subdev *sd, return 0; } +static int imx290_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + const struct imx290 *imx290 = to_imx290(sd); + const struct imx290_mode *imx290_modes = imx290_modes_ptr(imx290); + + if ((fse->code != imx290_formats[0].code) && + (fse->code != imx290_formats[1].code)) + return -EINVAL; + + if (fse->index >= imx290_modes_num(imx290)) + return -EINVAL; + + fse->min_width = imx290_modes[fse->index].width; + fse->max_width = imx290_modes[fse->index].width; + fse->min_height = imx290_modes[fse->index].height; + fse->max_height = imx290_modes[fse->index].height; + + return 0; +} + static int imx290_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) @@ -439,6 +571,30 @@ static int imx290_get_fmt(struct v4l2_subdev *sd, return 0; } +static inline u8 imx290_get_link_freq_index(struct imx290 *imx290) +{ + return imx290->current_mode->link_freq_index; +} + +static s64 imx290_get_link_freq(struct imx290 *imx290) +{ + u8 index = imx290_get_link_freq_index(imx290); + + return *(imx290_link_freqs_ptr(imx290) + index); +} + +static u64 imx290_calc_pixel_rate(struct imx290 *imx290) +{ + s64 link_freq = imx290_get_link_freq(imx290); + u8 nlanes = imx290->nlanes; + u64 pixel_rate; + + /* pixel rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ + pixel_rate = link_freq * 2 * nlanes; + do_div(pixel_rate, imx290->bpp); + return pixel_rate; +} + static int imx290_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) @@ -450,9 +606,8 @@ static int imx290_set_fmt(struct v4l2_subdev *sd, mutex_lock(&imx290->lock); - mode = v4l2_find_nearest_size(imx290_modes, - ARRAY_SIZE(imx290_modes), - width, height, + mode = v4l2_find_nearest_size(imx290_modes_ptr(imx290), + imx290_modes_num(imx290), width, height, fmt->format.width, fmt->format.height); fmt->format.width = mode->width; @@ -472,10 +627,15 @@ static int imx290_set_fmt(struct v4l2_subdev *sd, format = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); } else { format = &imx290->current_format; - __v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index); - __v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate, mode->pixel_rate); - imx290->current_mode = mode; + imx290->bpp = imx290_formats[i].bpp; + + if (imx290->link_freq) + __v4l2_ctrl_s_ctrl(imx290->link_freq, + imx290_get_link_freq_index(imx290)); + if (imx290->pixel_rate) + __v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate, + imx290_calc_pixel_rate(imx290)); } *format = fmt->format; @@ -499,12 +659,11 @@ static int imx290_entity_init_cfg(struct v4l2_subdev *subdev, return 0; } -static int imx290_write_current_format(struct imx290 *imx290, - struct v4l2_mbus_framefmt *format) +static int imx290_write_current_format(struct imx290 *imx290) { int ret; - switch (format->code) { + switch (imx290->current_format.code) { case MEDIA_BUS_FMT_SRGGB10_1X10: ret = imx290_set_register_array(imx290, imx290_10bit_settings, ARRAY_SIZE( @@ -514,6 +673,15 @@ static int imx290_write_current_format(struct imx290 *imx290, return ret; } break; + case MEDIA_BUS_FMT_SRGGB12_1X12: + ret = imx290_set_register_array(imx290, imx290_12bit_settings, + ARRAY_SIZE( + imx290_12bit_settings)); + if (ret < 0) { + dev_err(imx290->dev, "Could not set format registers\n"); + return ret; + } + break; default: dev_err(imx290->dev, "Unknown pixel format\n"); return -EINVAL; @@ -522,6 +690,25 @@ static int imx290_write_current_format(struct imx290 *imx290, return 0; } +static int imx290_set_hmax(struct imx290 *imx290, u32 val) +{ + int ret; + + ret = imx290_write_reg(imx290, IMX290_HMAX_LOW, (val & 0xff)); + if (ret) { + dev_err(imx290->dev, "Error setting HMAX register\n"); + return ret; + } + + ret = imx290_write_reg(imx290, IMX290_HMAX_HIGH, ((val >> 8) & 0xff)); + if (ret) { + dev_err(imx290->dev, "Error setting HMAX register\n"); + return ret; + } + + return 0; +} + /* Start streaming */ static int imx290_start_streaming(struct imx290 *imx290) { @@ -536,8 +723,8 @@ static int imx290_start_streaming(struct imx290 *imx290) return ret; } - /* Set current frame format */ - ret = imx290_write_current_format(imx290, &imx290->current_format); + /* Apply the register values related to current frame format */ + ret = imx290_write_current_format(imx290); if (ret < 0) { dev_err(imx290->dev, "Could not set frame format\n"); return ret; @@ -550,6 +737,9 @@ static int imx290_start_streaming(struct imx290 *imx290) dev_err(imx290->dev, "Could not set current mode\n"); return ret; } + ret = imx290_set_hmax(imx290, imx290->current_mode->hmax); + if (ret < 0) + return ret; /* Apply customized values from user */ ret = v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler); @@ -607,6 +797,49 @@ static int imx290_get_regulators(struct device *dev, struct imx290 *imx290) imx290->supplies); } +static int imx290_set_data_lanes(struct imx290 *imx290) +{ + int ret = 0, laneval, frsel; + + switch (imx290->nlanes) { + case 2: + laneval = 0x01; + frsel = 0x02; + break; + case 4: + laneval = 0x03; + frsel = 0x01; + break; + default: + /* + * We should never hit this since the data lane count is + * validated in probe itself + */ + dev_err(imx290->dev, "Lane configuration not supported\n"); + ret = -EINVAL; + goto exit; + } + + ret = imx290_write_reg(imx290, IMX290_PHY_LANE_NUM, laneval); + if (ret) { + dev_err(imx290->dev, "Error setting Physical Lane number register\n"); + goto exit; + } + + ret = imx290_write_reg(imx290, IMX290_CSI_LANE_MODE, laneval); + if (ret) { + dev_err(imx290->dev, "Error setting CSI Lane mode register\n"); + goto exit; + } + + ret = imx290_write_reg(imx290, IMX290_FR_FDG_SEL, frsel); + if (ret) + dev_err(imx290->dev, "Error setting FR/FDG SEL register\n"); + +exit: + return ret; +} + static int imx290_power_on(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -628,9 +861,12 @@ static int imx290_power_on(struct device *dev) } usleep_range(1, 2); - gpiod_set_value_cansleep(imx290->rst_gpio, 1); + gpiod_set_value_cansleep(imx290->rst_gpio, 0); usleep_range(30000, 31000); + /* Set data lane count */ + imx290_set_data_lanes(imx290); + return 0; } @@ -641,14 +877,14 @@ static int imx290_power_off(struct device *dev) struct imx290 *imx290 = to_imx290(sd); clk_disable_unprepare(imx290->xclk); - gpiod_set_value_cansleep(imx290->rst_gpio, 0); + gpiod_set_value_cansleep(imx290->rst_gpio, 1); regulator_bulk_disable(IMX290_NUM_SUPPLIES, imx290->supplies); return 0; } static const struct dev_pm_ops imx290_pm_ops = { - SET_RUNTIME_PM_OPS(imx290_power_on, imx290_power_off, NULL) + SET_RUNTIME_PM_OPS(imx290_power_off, imx290_power_on, NULL) }; static const struct v4l2_subdev_video_ops imx290_video_ops = { @@ -658,6 +894,7 @@ static const struct v4l2_subdev_video_ops imx290_video_ops = { static const struct v4l2_subdev_pad_ops imx290_pad_ops = { .init_cfg = imx290_entity_init_cfg, .enum_mbus_code = imx290_enum_mbus_code, + .enum_frame_size = imx290_enum_frame_size, .get_fmt = imx290_get_fmt, .set_fmt = imx290_set_fmt, }; @@ -671,12 +908,39 @@ static const struct media_entity_operations imx290_subdev_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; +/* + * Returns 0 if all link frequencies used by the driver for the given number + * of MIPI data lanes are mentioned in the device tree, or the value of the + * first missing frequency otherwise. + */ +static s64 imx290_check_link_freqs(const struct imx290 *imx290, + const struct v4l2_fwnode_endpoint *ep) +{ + int i, j; + const s64 *freqs = imx290_link_freqs_ptr(imx290); + int freqs_count = imx290_link_freqs_num(imx290); + + for (i = 0; i < freqs_count; i++) { + for (j = 0; j < ep->nr_of_link_frequencies; j++) + if (freqs[i] == ep->link_frequencies[j]) + break; + if (j == ep->nr_of_link_frequencies) + return freqs[i]; + } + return 0; +} + static int imx290_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct fwnode_handle *endpoint; + /* Only CSI2 is supported for now: */ + struct v4l2_fwnode_endpoint ep = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; struct imx290 *imx290; u32 xclk_freq; + s64 fq; int ret; imx290 = devm_kzalloc(dev, sizeof(*imx290), GFP_KERNEL); @@ -696,35 +960,40 @@ static int imx290_probe(struct i2c_client *client) return -EINVAL; } - ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &imx290->ep); + ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep); fwnode_handle_put(endpoint); - if (ret) { + if (ret == -ENXIO) { + dev_err(dev, "Unsupported bus type, should be CSI2\n"); + goto free_err; + } else if (ret) { dev_err(dev, "Parsing endpoint node failed\n"); goto free_err; } - if (!imx290->ep.nr_of_link_frequencies) { - dev_err(dev, "link-frequency property not found in DT\n"); + /* Get number of data lanes */ + imx290->nlanes = ep.bus.mipi_csi2.num_data_lanes; + if (imx290->nlanes != 2 && imx290->nlanes != 4) { + dev_err(dev, "Invalid data lanes: %d\n", imx290->nlanes); ret = -EINVAL; goto free_err; } - if (imx290->ep.link_frequencies[0] != IMX290_DEFAULT_LINK_FREQ) { - dev_err(dev, "Unsupported link frequency\n"); + dev_dbg(dev, "Using %u data lanes\n", imx290->nlanes); + + if (!ep.nr_of_link_frequencies) { + dev_err(dev, "link-frequency property not found in DT\n"); ret = -EINVAL; goto free_err; } - /* Only CSI2 is supported for now */ - if (imx290->ep.bus_type != V4L2_MBUS_CSI2_DPHY) { - dev_err(dev, "Unsupported bus type, should be CSI2\n"); + /* Check that link frequences for all the modes are in device tree */ + fq = imx290_check_link_freqs(imx290, &ep); + if (fq) { + dev_err(dev, "Link frequency of %lld is not supported\n", fq); ret = -EINVAL; goto free_err; } - /* Set default mode to max resolution */ - imx290->current_mode = &imx290_modes[0]; - /* get system clock (xclk) */ imx290->xclk = devm_clk_get(dev, "xclk"); if (IS_ERR(imx290->xclk)) { @@ -760,7 +1029,8 @@ static int imx290_probe(struct i2c_client *client) goto free_err; } - imx290->rst_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS); + imx290->rst_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); if (IS_ERR(imx290->rst_gpio)) { dev_err(dev, "Cannot get reset gpio\n"); ret = PTR_ERR(imx290->rst_gpio); @@ -769,23 +1039,35 @@ static int imx290_probe(struct i2c_client *client) mutex_init(&imx290->lock); - v4l2_ctrl_handler_init(&imx290->ctrls, 3); + /* + * Initialize the frame format. In particular, imx290->current_mode + * and imx290->bpp are set to defaults: imx290_calc_pixel_rate() call + * below relies on these fields. + */ + imx290_entity_init_cfg(&imx290->sd, NULL); + + v4l2_ctrl_handler_init(&imx290->ctrls, 4); v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, V4L2_CID_GAIN, 0, 72, 1, 0); + imx290->link_freq = - v4l2_ctrl_new_int_menu(&imx290->ctrls, - &imx290_ctrl_ops, + v4l2_ctrl_new_int_menu(&imx290->ctrls, &imx290_ctrl_ops, V4L2_CID_LINK_FREQ, - ARRAY_SIZE(imx290_link_freq) - 1, - 0, imx290_link_freq); + imx290_link_freqs_num(imx290) - 1, 0, + imx290_link_freqs_ptr(imx290)); if (imx290->link_freq) imx290->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; imx290->pixel_rate = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, - V4L2_CID_PIXEL_RATE, 1, - INT_MAX, 1, - imx290_modes[0].pixel_rate); + V4L2_CID_PIXEL_RATE, + 1, INT_MAX, 1, + imx290_calc_pixel_rate(imx290)); + + v4l2_ctrl_new_std_menu_items(&imx290->ctrls, &imx290_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(imx290_test_pattern_menu) - 1, + 0, 0, imx290_test_pattern_menu); imx290->sd.ctrl_handler = &imx290->ctrls; @@ -826,7 +1108,7 @@ static int imx290_probe(struct i2c_client *client) pm_runtime_enable(dev); pm_runtime_idle(dev); - v4l2_fwnode_endpoint_free(&imx290->ep); + v4l2_fwnode_endpoint_free(&ep); return 0; @@ -836,7 +1118,7 @@ free_ctrl: v4l2_ctrl_handler_free(&imx290->ctrls); mutex_destroy(&imx290->lock); free_err: - v4l2_fwnode_endpoint_free(&imx290->ep); + v4l2_fwnode_endpoint_free(&ep); return ret; } diff --git a/drivers/media/i2c/max9271.c b/drivers/media/i2c/max9271.c new file mode 100644 index 000000000000..0f6f7a092a46 --- /dev/null +++ b/drivers/media/i2c/max9271.c @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017-2020 Jacopo Mondi + * Copyright (C) 2017-2020 Kieran Bingham + * Copyright (C) 2017-2020 Laurent Pinchart + * Copyright (C) 2017-2020 Niklas Söderlund + * Copyright (C) 2016 Renesas Electronics Corporation + * Copyright (C) 2015 Cogent Embedded, Inc. + * + * This file exports functions to control the Maxim MAX9271 GMSL serializer + * chip. This is not a self-contained driver, as MAX9271 is usually embedded in + * camera modules with at least one image sensor and optional additional + * components, such as uController units or ISPs/DSPs. + * + * Drivers for the camera modules (i.e. rdacm20/21) are expected to use + * functions exported from this library driver to maximize code re-use. + */ + +#include <linux/delay.h> +#include <linux/i2c.h> + +#include "max9271.h" + +static int max9271_read(struct max9271_device *dev, u8 reg) +{ + int ret; + + dev_dbg(&dev->client->dev, "%s(0x%02x)\n", __func__, reg); + + ret = i2c_smbus_read_byte_data(dev->client, reg); + if (ret < 0) + dev_dbg(&dev->client->dev, + "%s: register 0x%02x read failed (%d)\n", + __func__, reg, ret); + + return ret; +} + +static int max9271_write(struct max9271_device *dev, u8 reg, u8 val) +{ + int ret; + + dev_dbg(&dev->client->dev, "%s(0x%02x, 0x%02x)\n", __func__, reg, val); + + ret = i2c_smbus_write_byte_data(dev->client, reg, val); + if (ret < 0) + dev_err(&dev->client->dev, + "%s: register 0x%02x write failed (%d)\n", + __func__, reg, ret); + + return ret; +} + +/* + * max9271_pclk_detect() - Detect valid pixel clock from image sensor + * + * Wait up to 10ms for a valid pixel clock. + * + * Returns 0 for success, < 0 for pixel clock not properly detected + */ +static int max9271_pclk_detect(struct max9271_device *dev) +{ + unsigned int i; + int ret; + + for (i = 0; i < 100; i++) { + ret = max9271_read(dev, 0x15); + if (ret < 0) + return ret; + + if (ret & MAX9271_PCLKDET) + return 0; + + usleep_range(50, 100); + } + + dev_err(&dev->client->dev, "Unable to detect valid pixel clock\n"); + + return -EIO; +} + +int max9271_set_serial_link(struct max9271_device *dev, bool enable) +{ + int ret; + u8 val = MAX9271_REVCCEN | MAX9271_FWDCCEN; + + if (enable) { + ret = max9271_pclk_detect(dev); + if (ret) + return ret; + + val |= MAX9271_SEREN; + } else { + val |= MAX9271_CLINKEN; + } + + /* + * The serializer temporarily disables the reverse control channel for + * 350µs after starting/stopping the forward serial link, but the + * deserializer synchronization time isn't clearly documented. + * + * According to the serializer datasheet we should wait 3ms, while + * according to the deserializer datasheet we should wait 5ms. + * + * Short delays here appear to show bit-errors in the writes following. + * Therefore a conservative delay seems best here. + */ + max9271_write(dev, 0x04, val); + usleep_range(5000, 8000); + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_set_serial_link); + +int max9271_configure_i2c(struct max9271_device *dev, u8 i2c_config) +{ + int ret; + + ret = max9271_write(dev, 0x0d, i2c_config); + if (ret) + return ret; + + /* The delay required after an I2C bus configuration change is not + * characterized in the serializer manual. Sleep up to 5msec to + * stay safe. + */ + usleep_range(3500, 5000); + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_configure_i2c); + +int max9271_set_high_threshold(struct max9271_device *dev, bool enable) +{ + int ret; + + ret = max9271_read(dev, 0x08); + if (ret < 0) + return ret; + + /* + * Enable or disable reverse channel high threshold to increase + * immunity to power supply noise. + */ + max9271_write(dev, 0x08, enable ? ret | BIT(0) : ret & ~BIT(0)); + usleep_range(2000, 2500); + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_set_high_threshold); + +int max9271_configure_gmsl_link(struct max9271_device *dev) +{ + /* + * Configure the GMSL link: + * + * - Double input mode, high data rate, 24-bit mode + * - Latch input data on PCLKIN rising edge + * - Enable HS/VS encoding + * - 1-bit parity error detection + * + * TODO: Make the GMSL link configuration parametric. + */ + max9271_write(dev, 0x07, MAX9271_DBL | MAX9271_HVEN | + MAX9271_EDC_1BIT_PARITY); + usleep_range(5000, 8000); + + /* + * Adjust spread spectrum to +4% and auto-detect pixel clock + * and serial link rate. + */ + max9271_write(dev, 0x02, MAX9271_SPREAD_SPECT_4 | MAX9271_R02_RES | + MAX9271_PCLK_AUTODETECT | MAX9271_SERIAL_AUTODETECT); + usleep_range(5000, 8000); + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_configure_gmsl_link); + +int max9271_set_gpios(struct max9271_device *dev, u8 gpio_mask) +{ + int ret; + + ret = max9271_read(dev, 0x0f); + if (ret < 0) + return 0; + + ret |= gpio_mask; + ret = max9271_write(dev, 0x0f, ret); + if (ret < 0) { + dev_err(&dev->client->dev, "Failed to set gpio (%d)\n", ret); + return ret; + } + + usleep_range(3500, 5000); + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_set_gpios); + +int max9271_clear_gpios(struct max9271_device *dev, u8 gpio_mask) +{ + int ret; + + ret = max9271_read(dev, 0x0f); + if (ret < 0) + return 0; + + ret &= ~gpio_mask; + ret = max9271_write(dev, 0x0f, ret); + if (ret < 0) { + dev_err(&dev->client->dev, "Failed to clear gpio (%d)\n", ret); + return ret; + } + + usleep_range(3500, 5000); + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_clear_gpios); + +int max9271_enable_gpios(struct max9271_device *dev, u8 gpio_mask) +{ + int ret; + + ret = max9271_read(dev, 0x0f); + if (ret < 0) + return 0; + + /* BIT(0) reserved: GPO is always enabled. */ + ret |= gpio_mask | BIT(0); + ret = max9271_write(dev, 0x0e, ret); + if (ret < 0) { + dev_err(&dev->client->dev, "Failed to enable gpio (%d)\n", ret); + return ret; + } + + usleep_range(3500, 5000); + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_enable_gpios); + +int max9271_disable_gpios(struct max9271_device *dev, u8 gpio_mask) +{ + int ret; + + ret = max9271_read(dev, 0x0f); + if (ret < 0) + return 0; + + /* BIT(0) reserved: GPO cannot be disabled */ + ret &= (~gpio_mask | BIT(0)); + ret = max9271_write(dev, 0x0e, ret); + if (ret < 0) { + dev_err(&dev->client->dev, "Failed to disable gpio (%d)\n", ret); + return ret; + } + + usleep_range(3500, 5000); + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_disable_gpios); + +int max9271_verify_id(struct max9271_device *dev) +{ + int ret; + + ret = max9271_read(dev, 0x1e); + if (ret < 0) { + dev_err(&dev->client->dev, "MAX9271 ID read failed (%d)\n", + ret); + return ret; + } + + if (ret != MAX9271_ID) { + dev_err(&dev->client->dev, "MAX9271 ID mismatch (0x%02x)\n", + ret); + return -ENXIO; + } + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_verify_id); + +int max9271_set_address(struct max9271_device *dev, u8 addr) +{ + int ret; + + ret = max9271_write(dev, 0x00, addr << 1); + if (ret < 0) { + dev_err(&dev->client->dev, + "MAX9271 I2C address change failed (%d)\n", ret); + return ret; + } + usleep_range(3500, 5000); + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_set_address); + +int max9271_set_deserializer_address(struct max9271_device *dev, u8 addr) +{ + int ret; + + ret = max9271_write(dev, 0x01, addr << 1); + if (ret < 0) { + dev_err(&dev->client->dev, + "MAX9271 deserializer address set failed (%d)\n", ret); + return ret; + } + usleep_range(3500, 5000); + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_set_deserializer_address); + +int max9271_set_translation(struct max9271_device *dev, u8 source, u8 dest) +{ + int ret; + + ret = max9271_write(dev, 0x09, source << 1); + if (ret < 0) { + dev_err(&dev->client->dev, + "MAX9271 I2C translation setup failed (%d)\n", ret); + return ret; + } + usleep_range(3500, 5000); + + ret = max9271_write(dev, 0x0a, dest << 1); + if (ret < 0) { + dev_err(&dev->client->dev, + "MAX9271 I2C translation setup failed (%d)\n", ret); + return ret; + } + usleep_range(3500, 5000); + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_set_translation); diff --git a/drivers/media/i2c/max9271.h b/drivers/media/i2c/max9271.h new file mode 100644 index 000000000000..d78fb21441e9 --- /dev/null +++ b/drivers/media/i2c/max9271.h @@ -0,0 +1,224 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2017-2020 Jacopo Mondi + * Copyright (C) 2017-2020 Kieran Bingham + * Copyright (C) 2017-2020 Laurent Pinchart + * Copyright (C) 2017-2020 Niklas Söderlund + * Copyright (C) 2016 Renesas Electronics Corporation + * Copyright (C) 2015 Cogent Embedded, Inc. + */ + +#include <linux/i2c.h> + +#define MAX9271_DEFAULT_ADDR 0x40 + +/* Register 0x02 */ +#define MAX9271_SPREAD_SPECT_0 (0 << 5) +#define MAX9271_SPREAD_SPECT_05 (1 << 5) +#define MAX9271_SPREAD_SPECT_15 (2 << 5) +#define MAX9271_SPREAD_SPECT_1 (5 << 5) +#define MAX9271_SPREAD_SPECT_2 (3 << 5) +#define MAX9271_SPREAD_SPECT_3 (6 << 5) +#define MAX9271_SPREAD_SPECT_4 (7 << 5) +#define MAX9271_R02_RES BIT(4) +#define MAX9271_PCLK_AUTODETECT (3 << 2) +#define MAX9271_SERIAL_AUTODETECT (0x03) +/* Register 0x04 */ +#define MAX9271_SEREN BIT(7) +#define MAX9271_CLINKEN BIT(6) +#define MAX9271_PRBSEN BIT(5) +#define MAX9271_SLEEP BIT(4) +#define MAX9271_INTTYPE_I2C (0 << 2) +#define MAX9271_INTTYPE_UART (1 << 2) +#define MAX9271_INTTYPE_NONE (2 << 2) +#define MAX9271_REVCCEN BIT(1) +#define MAX9271_FWDCCEN BIT(0) +/* Register 0x07 */ +#define MAX9271_DBL BIT(7) +#define MAX9271_DRS BIT(6) +#define MAX9271_BWS BIT(5) +#define MAX9271_ES BIT(4) +#define MAX9271_HVEN BIT(2) +#define MAX9271_EDC_1BIT_PARITY (0 << 0) +#define MAX9271_EDC_6BIT_CRC (1 << 0) +#define MAX9271_EDC_6BIT_HAMMING (2 << 0) +/* Register 0x08 */ +#define MAX9271_INVVS BIT(7) +#define MAX9271_INVHS BIT(6) +#define MAX9271_REV_LOGAIN BIT(3) +#define MAX9271_REV_HIVTH BIT(0) +/* Register 0x09 */ +#define MAX9271_ID 0x09 +/* Register 0x0d */ +#define MAX9271_I2CLOCACK BIT(7) +#define MAX9271_I2CSLVSH_1046NS_469NS (3 << 5) +#define MAX9271_I2CSLVSH_938NS_352NS (2 << 5) +#define MAX9271_I2CSLVSH_469NS_234NS (1 << 5) +#define MAX9271_I2CSLVSH_352NS_117NS (0 << 5) +#define MAX9271_I2CMSTBT_837KBPS (7 << 2) +#define MAX9271_I2CMSTBT_533KBPS (6 << 2) +#define MAX9271_I2CMSTBT_339KBPS (5 << 2) +#define MAX9271_I2CMSTBT_173KBPS (4 << 2) +#define MAX9271_I2CMSTBT_105KBPS (3 << 2) +#define MAX9271_I2CMSTBT_84KBPS (2 << 2) +#define MAX9271_I2CMSTBT_28KBPS (1 << 2) +#define MAX9271_I2CMSTBT_8KBPS (0 << 2) +#define MAX9271_I2CSLVTO_NONE (3 << 0) +#define MAX9271_I2CSLVTO_1024US (2 << 0) +#define MAX9271_I2CSLVTO_256US (1 << 0) +#define MAX9271_I2CSLVTO_64US (0 << 0) +/* Register 0x0f */ +#define MAX9271_GPIO5OUT BIT(5) +#define MAX9271_GPIO4OUT BIT(4) +#define MAX9271_GPIO3OUT BIT(3) +#define MAX9271_GPIO2OUT BIT(2) +#define MAX9271_GPIO1OUT BIT(1) +#define MAX9271_GPO BIT(0) +/* Register 0x15 */ +#define MAX9271_PCLKDET BIT(0) + +/** + * struct max9271_device - max9271 device + * @client: The i2c client for the max9271 instance + */ +struct max9271_device { + struct i2c_client *client; +}; + +/** + * max9271_set_serial_link() - Enable/disable serial link + * @dev: The max9271 device + * @enable: Serial link enable/disable flag + * + * Return 0 on success or a negative error code on failure + */ +int max9271_set_serial_link(struct max9271_device *dev, bool enable); + +/** + * max9271_configure_i2c() - Configure I2C bus parameters + * @dev: The max9271 device + * @i2c_config: The I2C bus configuration bit mask + * + * Configure MAX9271 I2C interface. The bus configuration provided in the + * @i2c_config parameter shall be assembled using bit values defined by the + * MAX9271_I2C* macros. + * + * Return 0 on success or a negative error code on failure + */ +int max9271_configure_i2c(struct max9271_device *dev, u8 i2c_config); + +/** + * max9271_set_high_threshold() - Enable or disable reverse channel high + * threshold + * @dev: The max9271 device + * @enable: High threshold enable/disable flag + * + * Return 0 on success or a negative error code on failure + */ +int max9271_set_high_threshold(struct max9271_device *dev, bool enable); + +/** + * max9271_configure_gmsl_link() - Configure the GMSL link + * @dev: The max9271 device + * + * FIXME: the GMSL link configuration is currently hardcoded and performed + * by programming registers 0x04, 0x07 and 0x02. + * + * Return 0 on success or a negative error code on failure + */ +int max9271_configure_gmsl_link(struct max9271_device *dev); + +/** + * max9271_set_gpios() - Set gpio lines to physical high value + * @dev: The max9271 device + * @gpio_mask: The mask of gpio lines to set to high value + * + * The @gpio_mask parameter shall be assembled using the MAX9271_GP[IO|O]* + * bit values. + * + * Return 0 on success or a negative error code on failure + */ +int max9271_set_gpios(struct max9271_device *dev, u8 gpio_mask); + +/** + * max9271_clear_gpios() - Set gpio lines to physical low value + * @dev: The max9271 device + * @gpio_mask: The mask of gpio lines to set to low value + * + * The @gpio_mask parameter shall be assembled using the MAX9271_GP[IO|O]* + * bit values. + * + * Return 0 on success or a negative error code on failure + */ +int max9271_clear_gpios(struct max9271_device *dev, u8 gpio_mask); + +/** + * max9271_enable_gpios() - Enable gpio lines + * @dev: The max9271 device + * @gpio_mask: The mask of gpio lines to enable + * + * The @gpio_mask parameter shall be assembled using the MAX9271_GPIO* + * bit values. GPO line is always enabled by default. + * + * Return 0 on success or a negative error code on failure + */ +int max9271_enable_gpios(struct max9271_device *dev, u8 gpio_mask); + +/** + * max9271_disable_gpios() - Disable gpio lines + * @dev: The max9271 device + * @gpio_mask: The mask of gpio lines to disable + * + * The @gpio_mask parameter shall be assembled using the MAX9271_GPIO* + * bit values. GPO line is always enabled by default and cannot be disabled. + * + * Return 0 on success or a negative error code on failure + */ +int max9271_disable_gpios(struct max9271_device *dev, u8 gpio_mask); + +/** + * max9271_verify_id() - Read and verify MAX9271 id + * @dev: The max9271 device + * + * Return 0 on success or a negative error code on failure + */ +int max9271_verify_id(struct max9271_device *dev); + +/** + * max9271_set_address() - Program a new I2C address + * @dev: The max9271 device + * @addr: The new I2C address in 7-bit format + * + * This function only takes care of programming the new I2C address @addr to + * in the MAX9271 chip registers, it is responsiblity of the caller to set + * the i2c address client to the @addr value to be able to communicate with + * the MAX9271 chip using the I2C framework APIs after this function returns. + * + * Return 0 on success or a negative error code on failure + */ +int max9271_set_address(struct max9271_device *dev, u8 addr); + +/** + * max9271_set_deserializer_address() - Program the remote deserializer address + * @dev: The max9271 device + * @addr: The deserializer I2C address in 7-bit format + * + * Return 0 on success or a negative error code on failure + */ +int max9271_set_deserializer_address(struct max9271_device *dev, u8 addr); + +/** + * max9271_set_translation() - Program I2C address translation + * @dev: The max9271 device + * @source: The I2C source address + * @dest: The I2C destination address + * + * Program address translation from @source to @dest. This is required to + * communicate with local devices that do not support address reprogramming. + * + * TODO: The device supports translation of two address, this function currently + * supports a single one. + * + * Return 0 on success or a negative error code on failure + */ +int max9271_set_translation(struct max9271_device *dev, u8 source, u8 dest); diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c new file mode 100644 index 000000000000..47f280518fdb --- /dev/null +++ b/drivers/media/i2c/max9286.c @@ -0,0 +1,1320 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Maxim MAX9286 GMSL Deserializer Driver + * + * Copyright (C) 2017-2019 Jacopo Mondi + * Copyright (C) 2017-2019 Kieran Bingham + * Copyright (C) 2017-2019 Laurent Pinchart + * Copyright (C) 2017-2019 Niklas Söderlund + * Copyright (C) 2016 Renesas Electronics Corporation + * Copyright (C) 2015 Cogent Embedded, Inc. + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/fwnode.h> +#include <linux/gpio/consumer.h> +#include <linux/gpio/driver.h> +#include <linux/i2c.h> +#include <linux/i2c-mux.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of_graph.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> + +#include <media/v4l2-async.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> + +/* Register 0x00 */ +#define MAX9286_MSTLINKSEL_AUTO (7 << 5) +#define MAX9286_MSTLINKSEL(n) ((n) << 5) +#define MAX9286_EN_VS_GEN BIT(4) +#define MAX9286_LINKEN(n) (1 << (n)) +/* Register 0x01 */ +#define MAX9286_FSYNCMODE_ECU (3 << 6) +#define MAX9286_FSYNCMODE_EXT (2 << 6) +#define MAX9286_FSYNCMODE_INT_OUT (1 << 6) +#define MAX9286_FSYNCMODE_INT_HIZ (0 << 6) +#define MAX9286_GPIEN BIT(5) +#define MAX9286_ENLMO_RSTFSYNC BIT(2) +#define MAX9286_FSYNCMETH_AUTO (2 << 0) +#define MAX9286_FSYNCMETH_SEMI_AUTO (1 << 0) +#define MAX9286_FSYNCMETH_MANUAL (0 << 0) +#define MAX9286_REG_FSYNC_PERIOD_L 0x06 +#define MAX9286_REG_FSYNC_PERIOD_M 0x07 +#define MAX9286_REG_FSYNC_PERIOD_H 0x08 +/* Register 0x0a */ +#define MAX9286_FWDCCEN(n) (1 << ((n) + 4)) +#define MAX9286_REVCCEN(n) (1 << (n)) +/* Register 0x0c */ +#define MAX9286_HVEN BIT(7) +#define MAX9286_EDC_6BIT_HAMMING (2 << 5) +#define MAX9286_EDC_6BIT_CRC (1 << 5) +#define MAX9286_EDC_1BIT_PARITY (0 << 5) +#define MAX9286_DESEL BIT(4) +#define MAX9286_INVVS BIT(3) +#define MAX9286_INVHS BIT(2) +#define MAX9286_HVSRC_D0 (2 << 0) +#define MAX9286_HVSRC_D14 (1 << 0) +#define MAX9286_HVSRC_D18 (0 << 0) +/* Register 0x0f */ +#define MAX9286_0X0F_RESERVED BIT(3) +/* Register 0x12 */ +#define MAX9286_CSILANECNT(n) (((n) - 1) << 6) +#define MAX9286_CSIDBL BIT(5) +#define MAX9286_DBL BIT(4) +#define MAX9286_DATATYPE_USER_8BIT (11 << 0) +#define MAX9286_DATATYPE_USER_YUV_12BIT (10 << 0) +#define MAX9286_DATATYPE_USER_24BIT (9 << 0) +#define MAX9286_DATATYPE_RAW14 (8 << 0) +#define MAX9286_DATATYPE_RAW11 (7 << 0) +#define MAX9286_DATATYPE_RAW10 (6 << 0) +#define MAX9286_DATATYPE_RAW8 (5 << 0) +#define MAX9286_DATATYPE_YUV422_10BIT (4 << 0) +#define MAX9286_DATATYPE_YUV422_8BIT (3 << 0) +#define MAX9286_DATATYPE_RGB555 (2 << 0) +#define MAX9286_DATATYPE_RGB565 (1 << 0) +#define MAX9286_DATATYPE_RGB888 (0 << 0) +/* Register 0x15 */ +#define MAX9286_VC(n) ((n) << 5) +#define MAX9286_VCTYPE BIT(4) +#define MAX9286_CSIOUTEN BIT(3) +#define MAX9286_0X15_RESV (3 << 0) +/* Register 0x1b */ +#define MAX9286_SWITCHIN(n) (1 << ((n) + 4)) +#define MAX9286_ENEQ(n) (1 << (n)) +/* Register 0x27 */ +#define MAX9286_LOCKED BIT(7) +/* Register 0x31 */ +#define MAX9286_FSYNC_LOCKED BIT(6) +/* Register 0x34 */ +#define MAX9286_I2CLOCACK BIT(7) +#define MAX9286_I2CSLVSH_1046NS_469NS (3 << 5) +#define MAX9286_I2CSLVSH_938NS_352NS (2 << 5) +#define MAX9286_I2CSLVSH_469NS_234NS (1 << 5) +#define MAX9286_I2CSLVSH_352NS_117NS (0 << 5) +#define MAX9286_I2CMSTBT_837KBPS (7 << 2) +#define MAX9286_I2CMSTBT_533KBPS (6 << 2) +#define MAX9286_I2CMSTBT_339KBPS (5 << 2) +#define MAX9286_I2CMSTBT_173KBPS (4 << 2) +#define MAX9286_I2CMSTBT_105KBPS (3 << 2) +#define MAX9286_I2CMSTBT_84KBPS (2 << 2) +#define MAX9286_I2CMSTBT_28KBPS (1 << 2) +#define MAX9286_I2CMSTBT_8KBPS (0 << 2) +#define MAX9286_I2CSLVTO_NONE (3 << 0) +#define MAX9286_I2CSLVTO_1024US (2 << 0) +#define MAX9286_I2CSLVTO_256US (1 << 0) +#define MAX9286_I2CSLVTO_64US (0 << 0) +/* Register 0x3b */ +#define MAX9286_REV_TRF(n) ((n) << 4) +#define MAX9286_REV_AMP(n) ((((n) - 30) / 10) << 1) /* in mV */ +#define MAX9286_REV_AMP_X BIT(0) +/* Register 0x3f */ +#define MAX9286_EN_REV_CFG BIT(6) +#define MAX9286_REV_FLEN(n) ((n) - 20) +/* Register 0x49 */ +#define MAX9286_VIDEO_DETECT_MASK 0x0f +/* Register 0x69 */ +#define MAX9286_LFLTBMONMASKED BIT(7) +#define MAX9286_LOCKMONMASKED BIT(6) +#define MAX9286_AUTOCOMBACKEN BIT(5) +#define MAX9286_AUTOMASKEN BIT(4) +#define MAX9286_MASKLINK(n) ((n) << 0) + +/* + * The sink and source pads are created to match the OF graph port numbers so + * that their indexes can be used interchangeably. + */ +#define MAX9286_NUM_GMSL 4 +#define MAX9286_N_SINKS 4 +#define MAX9286_N_PADS 5 +#define MAX9286_SRC_PAD 4 + +struct max9286_source { + struct v4l2_async_subdev asd; + struct v4l2_subdev *sd; + struct fwnode_handle *fwnode; +}; + +#define asd_to_max9286_source(_asd) \ + container_of(_asd, struct max9286_source, asd) + +struct max9286_priv { + struct i2c_client *client; + struct gpio_desc *gpiod_pwdn; + struct v4l2_subdev sd; + struct media_pad pads[MAX9286_N_PADS]; + struct regulator *regulator; + + struct gpio_chip gpio; + u8 gpio_state; + + struct i2c_mux_core *mux; + unsigned int mux_channel; + bool mux_open; + + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *pixelrate; + + struct v4l2_mbus_framefmt fmt[MAX9286_N_SINKS]; + + /* Protects controls and fmt structures */ + struct mutex mutex; + + unsigned int nsources; + unsigned int source_mask; + unsigned int route_mask; + unsigned int bound_sources; + unsigned int csi2_data_lanes; + struct max9286_source sources[MAX9286_NUM_GMSL]; + struct v4l2_async_notifier notifier; +}; + +static struct max9286_source *next_source(struct max9286_priv *priv, + struct max9286_source *source) +{ + if (!source) + source = &priv->sources[0]; + else + source++; + + for (; source < &priv->sources[MAX9286_NUM_GMSL]; source++) { + if (source->fwnode) + return source; + } + + return NULL; +} + +#define for_each_source(priv, source) \ + for ((source) = NULL; ((source) = next_source((priv), (source))); ) + +#define to_index(priv, source) ((source) - &(priv)->sources[0]) + +static inline struct max9286_priv *sd_to_max9286(struct v4l2_subdev *sd) +{ + return container_of(sd, struct max9286_priv, sd); +} + +/* ----------------------------------------------------------------------------- + * I2C IO + */ + +static int max9286_read(struct max9286_priv *priv, u8 reg) +{ + int ret; + + ret = i2c_smbus_read_byte_data(priv->client, reg); + if (ret < 0) + dev_err(&priv->client->dev, + "%s: register 0x%02x read failed (%d)\n", + __func__, reg, ret); + + return ret; +} + +static int max9286_write(struct max9286_priv *priv, u8 reg, u8 val) +{ + int ret; + + ret = i2c_smbus_write_byte_data(priv->client, reg, val); + if (ret < 0) + dev_err(&priv->client->dev, + "%s: register 0x%02x write failed (%d)\n", + __func__, reg, ret); + + return ret; +} + +/* ----------------------------------------------------------------------------- + * I2C Multiplexer + */ + +static void max9286_i2c_mux_configure(struct max9286_priv *priv, u8 conf) +{ + max9286_write(priv, 0x0a, conf); + + /* + * We must sleep after any change to the forward or reverse channel + * configuration. + */ + usleep_range(3000, 5000); +} + +static void max9286_i2c_mux_open(struct max9286_priv *priv) +{ + /* Open all channels on the MAX9286 */ + max9286_i2c_mux_configure(priv, 0xff); + + priv->mux_open = true; +} + +static void max9286_i2c_mux_close(struct max9286_priv *priv) +{ + /* + * Ensure that both the forward and reverse channel are disabled on the + * mux, and that the channel ID is invalidated to ensure we reconfigure + * on the next max9286_i2c_mux_select() call. + */ + max9286_i2c_mux_configure(priv, 0x00); + + priv->mux_open = false; + priv->mux_channel = -1; +} + +static int max9286_i2c_mux_select(struct i2c_mux_core *muxc, u32 chan) +{ + struct max9286_priv *priv = i2c_mux_priv(muxc); + + /* Channel select is disabled when configured in the opened state. */ + if (priv->mux_open) + return 0; + + if (priv->mux_channel == chan) + return 0; + + priv->mux_channel = chan; + + max9286_i2c_mux_configure(priv, + MAX9286_FWDCCEN(chan) | + MAX9286_REVCCEN(chan)); + + return 0; +} + +static int max9286_i2c_mux_init(struct max9286_priv *priv) +{ + struct max9286_source *source; + int ret; + + if (!i2c_check_functionality(priv->client->adapter, + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) + return -ENODEV; + + priv->mux = i2c_mux_alloc(priv->client->adapter, &priv->client->dev, + priv->nsources, 0, I2C_MUX_LOCKED, + max9286_i2c_mux_select, NULL); + if (!priv->mux) + return -ENOMEM; + + priv->mux->priv = priv; + + for_each_source(priv, source) { + unsigned int index = to_index(priv, source); + + ret = i2c_mux_add_adapter(priv->mux, 0, index, 0); + if (ret < 0) + goto error; + } + + return 0; + +error: + i2c_mux_del_adapters(priv->mux); + return ret; +} + +static void max9286_configure_i2c(struct max9286_priv *priv, bool localack) +{ + u8 config = MAX9286_I2CSLVSH_469NS_234NS | MAX9286_I2CSLVTO_1024US | + MAX9286_I2CMSTBT_105KBPS; + + if (localack) + config |= MAX9286_I2CLOCACK; + + max9286_write(priv, 0x34, config); + usleep_range(3000, 5000); +} + +/* + * max9286_check_video_links() - Make sure video links are detected and locked + * + * Performs safety checks on video link status. Make sure they are detected + * and all enabled links are locked. + * + * Returns 0 for success, -EIO for errors. + */ +static int max9286_check_video_links(struct max9286_priv *priv) +{ + unsigned int i; + int ret; + + /* + * Make sure valid video links are detected. + * The delay is not characterized in de-serializer manual, wait up + * to 5 ms. + */ + for (i = 0; i < 10; i++) { + ret = max9286_read(priv, 0x49); + if (ret < 0) + return -EIO; + + if ((ret & MAX9286_VIDEO_DETECT_MASK) == priv->source_mask) + break; + + usleep_range(350, 500); + } + + if (i == 10) { + dev_err(&priv->client->dev, + "Unable to detect video links: 0x%02x\n", ret); + return -EIO; + } + + /* Make sure all enabled links are locked (4ms max). */ + for (i = 0; i < 10; i++) { + ret = max9286_read(priv, 0x27); + if (ret < 0) + return -EIO; + + if (ret & MAX9286_LOCKED) + break; + + usleep_range(350, 450); + } + + if (i == 10) { + dev_err(&priv->client->dev, "Not all enabled links locked\n"); + return -EIO; + } + + return 0; +} + +/* + * max9286_check_config_link() - Detect and wait for configuration links + * + * Determine if the configuration channel is up and settled for a link. + * + * Returns 0 for success, -EIO for errors. + */ +static int max9286_check_config_link(struct max9286_priv *priv, + unsigned int source_mask) +{ + unsigned int conflink_mask = (source_mask & 0x0f) << 4; + unsigned int i; + int ret; + + /* + * Make sure requested configuration links are detected. + * The delay is not characterized in the chip manual: wait up + * to 5 milliseconds. + */ + for (i = 0; i < 10; i++) { + ret = max9286_read(priv, 0x49) & 0xf0; + if (ret < 0) + return -EIO; + + if (ret == conflink_mask) + break; + + usleep_range(350, 500); + } + + if (ret != conflink_mask) { + dev_err(&priv->client->dev, + "Unable to detect configuration links: 0x%02x expected 0x%02x\n", + ret, conflink_mask); + return -EIO; + } + + dev_info(&priv->client->dev, + "Successfully detected configuration links after %u loops: 0x%02x\n", + i, conflink_mask); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdev + */ + +static int max9286_set_pixelrate(struct max9286_priv *priv) +{ + struct max9286_source *source = NULL; + u64 pixelrate = 0; + + for_each_source(priv, source) { + struct v4l2_ctrl *ctrl; + u64 source_rate = 0; + + /* Pixel rate is mandatory to be reported by sources. */ + ctrl = v4l2_ctrl_find(source->sd->ctrl_handler, + V4L2_CID_PIXEL_RATE); + if (!ctrl) { + pixelrate = 0; + break; + } + + /* All source must report the same pixel rate. */ + source_rate = v4l2_ctrl_g_ctrl_int64(ctrl); + if (!pixelrate) { + pixelrate = source_rate; + } else if (pixelrate != source_rate) { + dev_err(&priv->client->dev, + "Unable to calculate pixel rate\n"); + return -EINVAL; + } + } + + if (!pixelrate) { + dev_err(&priv->client->dev, + "No pixel rate control available in sources\n"); + return -EINVAL; + } + + /* + * The CSI-2 transmitter pixel rate is the single source rate multiplied + * by the number of available sources. + */ + return v4l2_ctrl_s_ctrl_int64(priv->pixelrate, + pixelrate * priv->nsources); +} + +static int max9286_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct max9286_priv *priv = sd_to_max9286(notifier->sd); + struct max9286_source *source = asd_to_max9286_source(asd); + unsigned int index = to_index(priv, source); + unsigned int src_pad; + int ret; + + ret = media_entity_get_fwnode_pad(&subdev->entity, + source->fwnode, + MEDIA_PAD_FL_SOURCE); + if (ret < 0) { + dev_err(&priv->client->dev, + "Failed to find pad for %s\n", subdev->name); + return ret; + } + + priv->bound_sources |= BIT(index); + source->sd = subdev; + src_pad = ret; + + ret = media_create_pad_link(&source->sd->entity, src_pad, + &priv->sd.entity, index, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) { + dev_err(&priv->client->dev, + "Unable to link %s:%u -> %s:%u\n", + source->sd->name, src_pad, priv->sd.name, index); + return ret; + } + + dev_dbg(&priv->client->dev, "Bound %s pad: %u on index %u\n", + subdev->name, src_pad, index); + + /* + * We can only register v4l2_async_notifiers, which do not provide a + * means to register a complete callback. bound_sources allows us to + * identify when all remote serializers have completed their probe. + */ + if (priv->bound_sources != priv->source_mask) + return 0; + + /* + * All enabled sources have probed and enabled their reverse control + * channels: + * + * - Verify all configuration links are properly detected + * - Disable auto-ack as communication on the control channel are now + * stable. + */ + max9286_check_config_link(priv, priv->source_mask); + + /* + * Re-configure I2C with local acknowledge disabled after cameras have + * probed. + */ + max9286_configure_i2c(priv, false); + + return max9286_set_pixelrate(priv); +} + +static void max9286_notify_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct max9286_priv *priv = sd_to_max9286(notifier->sd); + struct max9286_source *source = asd_to_max9286_source(asd); + unsigned int index = to_index(priv, source); + + source->sd = NULL; + priv->bound_sources &= ~BIT(index); +} + +static const struct v4l2_async_notifier_operations max9286_notify_ops = { + .bound = max9286_notify_bound, + .unbind = max9286_notify_unbind, +}; + +static int max9286_v4l2_notifier_register(struct max9286_priv *priv) +{ + struct device *dev = &priv->client->dev; + struct max9286_source *source = NULL; + int ret; + + if (!priv->nsources) + return 0; + + v4l2_async_notifier_init(&priv->notifier); + + for_each_source(priv, source) { + unsigned int i = to_index(priv, source); + + source->asd.match_type = V4L2_ASYNC_MATCH_FWNODE; + source->asd.match.fwnode = source->fwnode; + + ret = v4l2_async_notifier_add_subdev(&priv->notifier, + &source->asd); + if (ret) { + dev_err(dev, "Failed to add subdev for source %d", i); + v4l2_async_notifier_cleanup(&priv->notifier); + return ret; + } + + /* + * Balance the reference counting handled through + * v4l2_async_notifier_cleanup() + */ + fwnode_handle_get(source->fwnode); + } + + priv->notifier.ops = &max9286_notify_ops; + + ret = v4l2_async_subdev_notifier_register(&priv->sd, &priv->notifier); + if (ret) { + dev_err(dev, "Failed to register subdev_notifier"); + v4l2_async_notifier_cleanup(&priv->notifier); + return ret; + } + + return 0; +} + +static void max9286_v4l2_notifier_unregister(struct max9286_priv *priv) +{ + if (!priv->nsources) + return; + + v4l2_async_notifier_unregister(&priv->notifier); + v4l2_async_notifier_cleanup(&priv->notifier); +} + +static int max9286_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct max9286_priv *priv = sd_to_max9286(sd); + struct max9286_source *source; + unsigned int i; + bool sync = false; + int ret; + + if (enable) { + /* + * The frame sync between cameras is transmitted across the + * reverse channel as GPIO. We must open all channels while + * streaming to allow this synchronisation signal to be shared. + */ + max9286_i2c_mux_open(priv); + + /* Start all cameras. */ + for_each_source(priv, source) { + ret = v4l2_subdev_call(source->sd, video, s_stream, 1); + if (ret) + return ret; + } + + ret = max9286_check_video_links(priv); + if (ret) + return ret; + + /* + * Wait until frame synchronization is locked. + * + * Manual says frame sync locking should take ~6 VTS. + * From practical experience at least 8 are required. Give + * 12 complete frames time (~400ms at 30 fps) to achieve frame + * locking before returning error. + */ + for (i = 0; i < 40; i++) { + if (max9286_read(priv, 0x31) & MAX9286_FSYNC_LOCKED) { + sync = true; + break; + } + usleep_range(9000, 11000); + } + + if (!sync) { + dev_err(&priv->client->dev, + "Failed to get frame synchronization\n"); + return -EXDEV; /* Invalid cross-device link */ + } + + /* + * Enable CSI output, VC set according to link number. + * Bit 7 must be set (chip manual says it's 0 and reserved). + */ + max9286_write(priv, 0x15, 0x80 | MAX9286_VCTYPE | + MAX9286_CSIOUTEN | MAX9286_0X15_RESV); + } else { + max9286_write(priv, 0x15, MAX9286_VCTYPE | MAX9286_0X15_RESV); + + /* Stop all cameras. */ + for_each_source(priv, source) + v4l2_subdev_call(source->sd, video, s_stream, 0); + + max9286_i2c_mux_close(priv); + } + + return 0; +} + +static int max9286_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad || code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_UYVY8_1X16; + + return 0; +} + +static struct v4l2_mbus_framefmt * +max9286_get_pad_format(struct max9286_priv *priv, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(&priv->sd, cfg, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &priv->fmt[pad]; + default: + return NULL; + } +} + +static int max9286_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct max9286_priv *priv = sd_to_max9286(sd); + struct v4l2_mbus_framefmt *cfg_fmt; + + if (format->pad == MAX9286_SRC_PAD) + return -EINVAL; + + /* Refuse non YUV422 formats as we hardcode DT to 8 bit YUV422 */ + switch (format->format.code) { + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_VYUY8_1X16: + case MEDIA_BUS_FMT_YUYV8_1X16: + case MEDIA_BUS_FMT_YVYU8_1X16: + break; + default: + format->format.code = MEDIA_BUS_FMT_UYVY8_1X16; + break; + } + + cfg_fmt = max9286_get_pad_format(priv, cfg, format->pad, format->which); + if (!cfg_fmt) + return -EINVAL; + + mutex_lock(&priv->mutex); + *cfg_fmt = format->format; + mutex_unlock(&priv->mutex); + + return 0; +} + +static int max9286_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct max9286_priv *priv = sd_to_max9286(sd); + struct v4l2_mbus_framefmt *cfg_fmt; + unsigned int pad = format->pad; + + /* + * Multiplexed Stream Support: Support link validation by returning the + * format of the first bound link. All links must have the same format, + * as we do not support mixing and matching of cameras connected to the + * max9286. + */ + if (pad == MAX9286_SRC_PAD) + pad = __ffs(priv->bound_sources); + + cfg_fmt = max9286_get_pad_format(priv, cfg, pad, format->which); + if (!cfg_fmt) + return -EINVAL; + + mutex_lock(&priv->mutex); + format->format = *cfg_fmt; + mutex_unlock(&priv->mutex); + + return 0; +} + +static const struct v4l2_subdev_video_ops max9286_video_ops = { + .s_stream = max9286_s_stream, +}; + +static const struct v4l2_subdev_pad_ops max9286_pad_ops = { + .enum_mbus_code = max9286_enum_mbus_code, + .get_fmt = max9286_get_fmt, + .set_fmt = max9286_set_fmt, +}; + +static const struct v4l2_subdev_ops max9286_subdev_ops = { + .video = &max9286_video_ops, + .pad = &max9286_pad_ops, +}; + +static void max9286_init_format(struct v4l2_mbus_framefmt *fmt) +{ + fmt->width = 1280; + fmt->height = 800; + fmt->code = MEDIA_BUS_FMT_UYVY8_1X16; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->field = V4L2_FIELD_NONE; + fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + fmt->quantization = V4L2_QUANTIZATION_DEFAULT; + fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; +} + +static int max9286_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *format; + unsigned int i; + + for (i = 0; i < MAX9286_N_SINKS; i++) { + format = v4l2_subdev_get_try_format(subdev, fh->pad, i); + max9286_init_format(format); + } + + return 0; +} + +static const struct v4l2_subdev_internal_ops max9286_subdev_internal_ops = { + .open = max9286_open, +}; + +static int max9286_s_ctrl(struct v4l2_ctrl *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_PIXEL_RATE: + return 0; + default: + return -EINVAL; + } +} + +static const struct v4l2_ctrl_ops max9286_ctrl_ops = { + .s_ctrl = max9286_s_ctrl, +}; + +static int max9286_v4l2_register(struct max9286_priv *priv) +{ + struct device *dev = &priv->client->dev; + struct fwnode_handle *ep; + int ret; + int i; + + /* Register v4l2 async notifiers for connected Camera subdevices */ + ret = max9286_v4l2_notifier_register(priv); + if (ret) { + dev_err(dev, "Unable to register V4L2 async notifiers\n"); + return ret; + } + + /* Configure V4L2 for the MAX9286 itself */ + + for (i = 0; i < MAX9286_N_SINKS; i++) + max9286_init_format(&priv->fmt[i]); + + v4l2_i2c_subdev_init(&priv->sd, priv->client, &max9286_subdev_ops); + priv->sd.internal_ops = &max9286_subdev_internal_ops; + priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + v4l2_ctrl_handler_init(&priv->ctrls, 1); + priv->pixelrate = v4l2_ctrl_new_std(&priv->ctrls, + &max9286_ctrl_ops, + V4L2_CID_PIXEL_RATE, + 1, INT_MAX, 1, 50000000); + + priv->sd.ctrl_handler = &priv->ctrls; + ret = priv->ctrls.error; + if (ret) + goto err_async; + + priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + + priv->pads[MAX9286_SRC_PAD].flags = MEDIA_PAD_FL_SOURCE; + for (i = 0; i < MAX9286_SRC_PAD; i++) + priv->pads[i].flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&priv->sd.entity, MAX9286_N_PADS, + priv->pads); + if (ret) + goto err_async; + + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), MAX9286_SRC_PAD, + 0, 0); + if (!ep) { + dev_err(dev, "Unable to retrieve endpoint on \"port@4\"\n"); + ret = -ENOENT; + goto err_async; + } + priv->sd.fwnode = ep; + + ret = v4l2_async_register_subdev(&priv->sd); + if (ret < 0) { + dev_err(dev, "Unable to register subdevice\n"); + goto err_put_node; + } + + return 0; + +err_put_node: + fwnode_handle_put(ep); +err_async: + max9286_v4l2_notifier_unregister(priv); + + return ret; +} + +static void max9286_v4l2_unregister(struct max9286_priv *priv) +{ + fwnode_handle_put(priv->sd.fwnode); + v4l2_async_unregister_subdev(&priv->sd); + max9286_v4l2_notifier_unregister(priv); +} + +/* ----------------------------------------------------------------------------- + * Probe/Remove + */ + +static int max9286_setup(struct max9286_priv *priv) +{ + /* + * Link ordering values for all enabled links combinations. Orders must + * be assigned sequentially from 0 to the number of enabled links + * without leaving any hole for disabled links. We thus assign orders to + * enabled links first, and use the remaining order values for disabled + * links are all links must have a different order value; + */ + static const u8 link_order[] = { + (3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xxxx */ + (3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xxx0 */ + (3 << 6) | (2 << 4) | (0 << 2) | (1 << 0), /* xx0x */ + (3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xx10 */ + (3 << 6) | (0 << 4) | (2 << 2) | (1 << 0), /* x0xx */ + (3 << 6) | (1 << 4) | (2 << 2) | (0 << 0), /* x1x0 */ + (3 << 6) | (1 << 4) | (0 << 2) | (2 << 0), /* x10x */ + (3 << 6) | (1 << 4) | (1 << 2) | (0 << 0), /* x210 */ + (0 << 6) | (3 << 4) | (2 << 2) | (1 << 0), /* 0xxx */ + (1 << 6) | (3 << 4) | (2 << 2) | (0 << 0), /* 1xx0 */ + (1 << 6) | (3 << 4) | (0 << 2) | (2 << 0), /* 1x0x */ + (2 << 6) | (3 << 4) | (1 << 2) | (0 << 0), /* 2x10 */ + (1 << 6) | (0 << 4) | (3 << 2) | (2 << 0), /* 10xx */ + (2 << 6) | (1 << 4) | (3 << 2) | (0 << 0), /* 21x0 */ + (2 << 6) | (1 << 4) | (0 << 2) | (3 << 0), /* 210x */ + (3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* 3210 */ + }; + + /* + * Set the I2C bus speed. + * + * Enable I2C Local Acknowledge during the probe sequences of the camera + * only. This should be disabled after the mux is initialised. + */ + max9286_configure_i2c(priv, true); + + /* + * Reverse channel setup. + * + * - Enable custom reverse channel configuration (through register 0x3f) + * and set the first pulse length to 35 clock cycles. + * - Increase the reverse channel amplitude to 170mV to accommodate the + * high threshold enabled by the serializer driver. + */ + max9286_write(priv, 0x3f, MAX9286_EN_REV_CFG | MAX9286_REV_FLEN(35)); + max9286_write(priv, 0x3b, MAX9286_REV_TRF(1) | MAX9286_REV_AMP(70) | + MAX9286_REV_AMP_X); + usleep_range(2000, 2500); + + /* + * Enable GMSL links, mask unused ones and autodetect link + * used as CSI clock source. + */ + max9286_write(priv, 0x00, MAX9286_MSTLINKSEL_AUTO | priv->route_mask); + max9286_write(priv, 0x0b, link_order[priv->route_mask]); + max9286_write(priv, 0x69, (0xf & ~priv->route_mask)); + + /* + * Video format setup: + * Disable CSI output, VC is set according to Link number. + */ + max9286_write(priv, 0x15, MAX9286_VCTYPE | MAX9286_0X15_RESV); + + /* Enable CSI-2 Lane D0-D3 only, DBL mode, YUV422 8-bit. */ + max9286_write(priv, 0x12, MAX9286_CSIDBL | MAX9286_DBL | + MAX9286_CSILANECNT(priv->csi2_data_lanes) | + MAX9286_DATATYPE_YUV422_8BIT); + + /* Automatic: FRAMESYNC taken from the slowest Link. */ + max9286_write(priv, 0x01, MAX9286_FSYNCMODE_INT_HIZ | + MAX9286_FSYNCMETH_AUTO); + + /* Enable HS/VS encoding, use D14/15 for HS/VS, invert VS. */ + max9286_write(priv, 0x0c, MAX9286_HVEN | MAX9286_INVVS | + MAX9286_HVSRC_D14); + + /* + * The overlap window seems to provide additional validation by tracking + * the delay between vsync and frame sync, generating an error if the + * delay is bigger than the programmed window, though it's not yet clear + * what value should be set. + * + * As it's an optional value and can be disabled, we do so by setting + * a 0 overlap value. + */ + max9286_write(priv, 0x63, 0); + max9286_write(priv, 0x64, 0); + + /* + * Wait for 2ms to allow the link to resynchronize after the + * configuration change. + */ + usleep_range(2000, 5000); + + return 0; +} + +static void max9286_gpio_set(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct max9286_priv *priv = gpiochip_get_data(chip); + + if (value) + priv->gpio_state |= BIT(offset); + else + priv->gpio_state &= ~BIT(offset); + + max9286_write(priv, 0x0f, MAX9286_0X0F_RESERVED | priv->gpio_state); +} + +static int max9286_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct max9286_priv *priv = gpiochip_get_data(chip); + + return priv->gpio_state & BIT(offset); +} + +static int max9286_register_gpio(struct max9286_priv *priv) +{ + struct device *dev = &priv->client->dev; + struct gpio_chip *gpio = &priv->gpio; + int ret; + + /* Configure the GPIO */ + gpio->label = dev_name(dev); + gpio->parent = dev; + gpio->owner = THIS_MODULE; + gpio->of_node = dev->of_node; + gpio->ngpio = 2; + gpio->base = -1; + gpio->set = max9286_gpio_set; + gpio->get = max9286_gpio_get; + gpio->can_sleep = true; + + /* GPIO values default to high */ + priv->gpio_state = BIT(0) | BIT(1); + + ret = devm_gpiochip_add_data(dev, gpio, priv); + if (ret) + dev_err(dev, "Unable to create gpio_chip\n"); + + return ret; +} + +static int max9286_init(struct device *dev) +{ + struct max9286_priv *priv; + struct i2c_client *client; + int ret; + + client = to_i2c_client(dev); + priv = i2c_get_clientdata(client); + + /* Enable the bus power. */ + ret = regulator_enable(priv->regulator); + if (ret < 0) { + dev_err(&client->dev, "Unable to turn PoC on\n"); + return ret; + } + + ret = max9286_setup(priv); + if (ret) { + dev_err(dev, "Unable to setup max9286\n"); + goto err_regulator; + } + + /* + * Register all V4L2 interactions for the MAX9286 and notifiers for + * any subdevices connected. + */ + ret = max9286_v4l2_register(priv); + if (ret) { + dev_err(dev, "Failed to register with V4L2\n"); + goto err_regulator; + } + + ret = max9286_i2c_mux_init(priv); + if (ret) { + dev_err(dev, "Unable to initialize I2C multiplexer\n"); + goto err_v4l2_register; + } + + /* Leave the mux channels disabled until they are selected. */ + max9286_i2c_mux_close(priv); + + return 0; + +err_v4l2_register: + max9286_v4l2_unregister(priv); +err_regulator: + regulator_disable(priv->regulator); + + return ret; +} + +static void max9286_cleanup_dt(struct max9286_priv *priv) +{ + struct max9286_source *source; + + for_each_source(priv, source) { + fwnode_handle_put(source->fwnode); + source->fwnode = NULL; + } +} + +static int max9286_parse_dt(struct max9286_priv *priv) +{ + struct device *dev = &priv->client->dev; + struct device_node *i2c_mux; + struct device_node *node = NULL; + unsigned int i2c_mux_mask = 0; + + /* Balance the of_node_put() performed by of_find_node_by_name(). */ + of_node_get(dev->of_node); + i2c_mux = of_find_node_by_name(dev->of_node, "i2c-mux"); + if (!i2c_mux) { + dev_err(dev, "Failed to find i2c-mux node\n"); + return -EINVAL; + } + + /* Identify which i2c-mux channels are enabled */ + for_each_child_of_node(i2c_mux, node) { + u32 id = 0; + + of_property_read_u32(node, "reg", &id); + if (id >= MAX9286_NUM_GMSL) + continue; + + if (!of_device_is_available(node)) { + dev_dbg(dev, "Skipping disabled I2C bus port %u\n", id); + continue; + } + + i2c_mux_mask |= BIT(id); + } + of_node_put(node); + of_node_put(i2c_mux); + + /* Parse the endpoints */ + for_each_endpoint_of_node(dev->of_node, node) { + struct max9286_source *source; + struct of_endpoint ep; + + of_graph_parse_endpoint(node, &ep); + dev_dbg(dev, "Endpoint %pOF on port %d", + ep.local_node, ep.port); + + if (ep.port > MAX9286_NUM_GMSL) { + dev_err(dev, "Invalid endpoint %s on port %d", + of_node_full_name(ep.local_node), ep.port); + continue; + } + + /* For the source endpoint just parse the bus configuration. */ + if (ep.port == MAX9286_SRC_PAD) { + struct v4l2_fwnode_endpoint vep = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + int ret; + + ret = v4l2_fwnode_endpoint_parse( + of_fwnode_handle(node), &vep); + if (ret) { + of_node_put(node); + return ret; + } + + priv->csi2_data_lanes = + vep.bus.mipi_csi2.num_data_lanes; + + continue; + } + + /* Skip if the corresponding GMSL link is unavailable. */ + if (!(i2c_mux_mask & BIT(ep.port))) + continue; + + if (priv->sources[ep.port].fwnode) { + dev_err(dev, + "Multiple port endpoints are not supported: %d", + ep.port); + + continue; + } + + source = &priv->sources[ep.port]; + source->fwnode = fwnode_graph_get_remote_endpoint( + of_fwnode_handle(node)); + if (!source->fwnode) { + dev_err(dev, + "Endpoint %pOF has no remote endpoint connection\n", + ep.local_node); + + continue; + } + + priv->source_mask |= BIT(ep.port); + priv->nsources++; + } + of_node_put(node); + + priv->route_mask = priv->source_mask; + + return 0; +} + +static int max9286_probe(struct i2c_client *client) +{ + struct max9286_priv *priv; + int ret; + + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + mutex_init(&priv->mutex); + + priv->client = client; + i2c_set_clientdata(client, priv); + + priv->gpiod_pwdn = devm_gpiod_get_optional(&client->dev, "enable", + GPIOD_OUT_HIGH); + if (IS_ERR(priv->gpiod_pwdn)) + return PTR_ERR(priv->gpiod_pwdn); + + gpiod_set_consumer_name(priv->gpiod_pwdn, "max9286-pwdn"); + gpiod_set_value_cansleep(priv->gpiod_pwdn, 1); + + /* Wait at least 4ms before the I2C lines latch to the address */ + if (priv->gpiod_pwdn) + usleep_range(4000, 5000); + + /* + * The MAX9286 starts by default with all ports enabled, we disable all + * ports early to ensure that all channels are disabled if we error out + * and keep the bus consistent. + */ + max9286_i2c_mux_close(priv); + + /* + * The MAX9286 initialises with auto-acknowledge enabled by default. + * This can be invasive to other transactions on the same bus, so + * disable it early. It will be enabled only as and when needed. + */ + max9286_configure_i2c(priv, false); + + ret = max9286_register_gpio(priv); + if (ret) + goto err_powerdown; + + priv->regulator = devm_regulator_get(&client->dev, "poc"); + if (IS_ERR(priv->regulator)) { + if (PTR_ERR(priv->regulator) != -EPROBE_DEFER) + dev_err(&client->dev, + "Unable to get PoC regulator (%ld)\n", + PTR_ERR(priv->regulator)); + ret = PTR_ERR(priv->regulator); + goto err_powerdown; + } + + ret = max9286_parse_dt(priv); + if (ret) + goto err_powerdown; + + ret = max9286_init(&client->dev); + if (ret < 0) + goto err_cleanup_dt; + + return 0; + +err_cleanup_dt: + max9286_cleanup_dt(priv); +err_powerdown: + gpiod_set_value_cansleep(priv->gpiod_pwdn, 0); + + return ret; +} + +static int max9286_remove(struct i2c_client *client) +{ + struct max9286_priv *priv = i2c_get_clientdata(client); + + i2c_mux_del_adapters(priv->mux); + + max9286_v4l2_unregister(priv); + + regulator_disable(priv->regulator); + + gpiod_set_value_cansleep(priv->gpiod_pwdn, 0); + + max9286_cleanup_dt(priv); + + return 0; +} + +static const struct of_device_id max9286_dt_ids[] = { + { .compatible = "maxim,max9286" }, + {}, +}; +MODULE_DEVICE_TABLE(of, max9286_dt_ids); + +static struct i2c_driver max9286_i2c_driver = { + .driver = { + .name = "max9286", + .of_match_table = of_match_ptr(max9286_dt_ids), + }, + .probe_new = max9286_probe, + .remove = max9286_remove, +}; + +module_i2c_driver(max9286_i2c_driver); + +MODULE_DESCRIPTION("Maxim MAX9286 GMSL Deserializer Driver"); +MODULE_AUTHOR("Jacopo Mondi, Kieran Bingham, Laurent Pinchart, Niklas Söderlund, Vladimir Barinov"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c index 2dd2609db873..fd0b6a903ec1 100644 --- a/drivers/media/i2c/ov2740.c +++ b/drivers/media/i2c/ov2740.c @@ -7,6 +7,8 @@ #include <linux/i2c.h> #include <linux/module.h> #include <linux/pm_runtime.h> +#include <linux/nvmem-provider.h> +#include <linux/regmap.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-fwnode.h> @@ -59,6 +61,21 @@ #define OV2740_TEST_PATTERN_ENABLE BIT(7) #define OV2740_TEST_PATTERN_BAR_SHIFT 2 +/* ISP CTRL00 */ +#define OV2740_REG_ISP_CTRL00 0x5000 +/* ISP CTRL01 */ +#define OV2740_REG_ISP_CTRL01 0x5001 +/* Customer Addresses: 0x7010 - 0x710F */ +#define CUSTOMER_USE_OTP_SIZE 0x100 +/* OTP registers from sensor */ +#define OV2740_REG_OTP_CUSTOMER 0x7010 + +struct nvm_data { + char *nvm_buffer; + struct nvmem_device *nvmem; + struct regmap *regmap; +}; + enum { OV2740_LINK_FREQ_360MHZ_INDEX, }; @@ -915,6 +932,130 @@ static int ov2740_remove(struct i2c_client *client) return 0; } +static int ov2740_load_otp_data(struct i2c_client *client, struct nvm_data *nvm) +{ + struct ov2740 *ov2740 = to_ov2740(i2c_get_clientdata(client)); + u32 isp_ctrl00 = 0; + u32 isp_ctrl01 = 0; + int ret; + + ret = ov2740_read_reg(ov2740, OV2740_REG_ISP_CTRL00, 1, &isp_ctrl00); + if (ret) { + dev_err(&client->dev, "failed to read ISP CTRL00\n"); + goto exit; + } + ret = ov2740_read_reg(ov2740, OV2740_REG_ISP_CTRL01, 1, &isp_ctrl01); + if (ret) { + dev_err(&client->dev, "failed to read ISP CTRL01\n"); + goto exit; + } + + /* Clear bit 5 of ISP CTRL00 */ + ret = ov2740_write_reg(ov2740, OV2740_REG_ISP_CTRL00, 1, + isp_ctrl00 & ~BIT(5)); + if (ret) { + dev_err(&client->dev, "failed to write ISP CTRL00\n"); + goto exit; + } + + /* Clear bit 7 of ISP CTRL01 */ + ret = ov2740_write_reg(ov2740, OV2740_REG_ISP_CTRL01, 1, + isp_ctrl01 & ~BIT(7)); + if (ret) { + dev_err(&client->dev, "failed to write ISP CTRL01\n"); + goto exit; + } + + ret = ov2740_write_reg(ov2740, OV2740_REG_MODE_SELECT, 1, + OV2740_MODE_STREAMING); + if (ret) { + dev_err(&client->dev, "failed to start streaming\n"); + goto exit; + } + + /* + * Users are not allowed to access OTP-related registers and memory + * during the 20 ms period after streaming starts (0x100 = 0x01). + */ + msleep(20); + + ret = regmap_bulk_read(nvm->regmap, OV2740_REG_OTP_CUSTOMER, + nvm->nvm_buffer, CUSTOMER_USE_OTP_SIZE); + if (ret) { + dev_err(&client->dev, "failed to read OTP data, ret %d\n", ret); + goto exit; + } + + ov2740_write_reg(ov2740, OV2740_REG_MODE_SELECT, 1, + OV2740_MODE_STANDBY); + ov2740_write_reg(ov2740, OV2740_REG_ISP_CTRL01, 1, isp_ctrl01); + ov2740_write_reg(ov2740, OV2740_REG_ISP_CTRL00, 1, isp_ctrl00); + +exit: + return ret; +} + +static int ov2740_nvmem_read(void *priv, unsigned int off, void *val, + size_t count) +{ + struct nvm_data *nvm = priv; + + memcpy(val, nvm->nvm_buffer + off, count); + + return 0; +} + +static int ov2740_register_nvmem(struct i2c_client *client) +{ + struct nvm_data *nvm; + struct regmap_config regmap_config = { }; + struct nvmem_config nvmem_config = { }; + struct regmap *regmap; + struct device *dev = &client->dev; + int ret = 0; + + nvm = devm_kzalloc(dev, sizeof(*nvm), GFP_KERNEL); + if (!nvm) + return -ENOMEM; + + regmap_config.val_bits = 8; + regmap_config.reg_bits = 16; + regmap_config.disable_locking = true; + regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + nvm->regmap = regmap; + + nvmem_config.name = dev_name(dev); + nvmem_config.dev = dev; + nvmem_config.read_only = true; + nvmem_config.root_only = true; + nvmem_config.owner = THIS_MODULE; + nvmem_config.compat = true; + nvmem_config.base_dev = dev; + nvmem_config.reg_read = ov2740_nvmem_read; + nvmem_config.reg_write = NULL; + nvmem_config.priv = nvm; + nvmem_config.stride = 1; + nvmem_config.word_size = 1; + nvmem_config.size = CUSTOMER_USE_OTP_SIZE; + + nvm->nvmem = devm_nvmem_register(dev, &nvmem_config); + if (IS_ERR(nvm->nvmem)) + return PTR_ERR(nvm->nvmem); + + nvm->nvm_buffer = devm_kzalloc(dev, CUSTOMER_USE_OTP_SIZE, GFP_KERNEL); + if (!nvm->nvm_buffer) + return -ENOMEM; + + ret = ov2740_load_otp_data(client, nvm); + if (ret) + dev_err(dev, "failed to load OTP data, ret %d\n", ret); + + return ret; +} + static int ov2740_probe(struct i2c_client *client) { struct ov2740 *ov2740; @@ -964,6 +1105,10 @@ static int ov2740_probe(struct i2c_client *client) goto probe_error_media_entity_cleanup; } + ret = ov2740_register_nvmem(client); + if (ret) + dev_err(&client->dev, "register nvmem failed, ret %d\n", ret); + /* * Device is already turned on by i2c-core with ACPI domain PM. * Enable runtime PM and turn off the device. @@ -988,20 +1133,18 @@ static const struct dev_pm_ops ov2740_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(ov2740_suspend, ov2740_resume) }; -#ifdef CONFIG_ACPI static const struct acpi_device_id ov2740_acpi_ids[] = { {"INT3474"}, {} }; MODULE_DEVICE_TABLE(acpi, ov2740_acpi_ids); -#endif static struct i2c_driver ov2740_i2c_driver = { .driver = { .name = "ov2740", .pm = &ov2740_pm_ops, - .acpi_match_table = ACPI_PTR(ov2740_acpi_ids), + .acpi_match_table = ov2740_acpi_ids, }, .probe_new = ov2740_probe, .remove = ov2740_remove, diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c index 482609665305..3a21f51d9325 100644 --- a/drivers/media/i2c/ov9640.c +++ b/drivers/media/i2c/ov9640.c @@ -772,6 +772,6 @@ static struct i2c_driver ov9640_i2c_driver = { module_i2c_driver(ov9640_i2c_driver); -MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV96xx"); +MODULE_DESCRIPTION("OmniVision OV96xx CMOS Image Sensor driver"); MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/rdacm20.c b/drivers/media/i2c/rdacm20.c new file mode 100644 index 000000000000..1ed928c4ca70 --- /dev/null +++ b/drivers/media/i2c/rdacm20.c @@ -0,0 +1,667 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * IMI RDACM20 GMSL Camera Driver + * + * Copyright (C) 2017-2020 Jacopo Mondi + * Copyright (C) 2017-2020 Kieran Bingham + * Copyright (C) 2017-2019 Laurent Pinchart + * Copyright (C) 2017-2019 Niklas Söderlund + * Copyright (C) 2016 Renesas Electronics Corporation + * Copyright (C) 2015 Cogent Embedded, Inc. + */ + +/* + * The camera is made of an Omnivision OV10635 sensor connected to a Maxim + * MAX9271 GMSL serializer. + */ + +#include <linux/delay.h> +#include <linux/fwnode.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/videodev2.h> + +#include <media/v4l2-async.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-subdev.h> + +#include "max9271.h" + +#define OV10635_I2C_ADDRESS 0x30 + +#define OV10635_SOFTWARE_RESET 0x0103 +#define OV10635_PID 0x300a +#define OV10635_VER 0x300b +#define OV10635_SC_CMMN_SCCB_ID 0x300c +#define OV10635_SC_CMMN_SCCB_ID_SELECT BIT(0) +#define OV10635_VERSION 0xa635 + +#define OV10635_WIDTH 1280 +#define OV10635_HEIGHT 800 + +/* VTS = PCLK / FPS / HTS / 2 (= 88MHz / 1572 / 30 / 2) */ +#define OV10635_HTS 1572 +/* FPS = 29,9998 */ +#define OV10635_VTS 933 + +/* + * As the drivers supports a single MEDIA_BUS_FMT_UYVY8_2X8 format we + * can harcode the pixel rate. + * + * PCLK is fed through the system clock, programmed @88MHz. + * MEDIA_BUS_FMT_UYVY8_2X8 format = 2 samples per pixel. + * + * Pixelrate = PCLK / 2 + * FPS = (OV10635_VTS * OV10635_HTS) / PixelRate + * = 29,9998 + */ +#define OV10635_PIXEL_RATE (44000000) + +static const struct ov10635_reg { + u16 reg; + u8 val; +} ov10635_regs_wizard[] = { + { 0x301b, 0xff }, { 0x301c, 0xff }, { 0x301a, 0xff }, { 0x3011, 0x42 }, + { 0x6900, 0x0c }, { 0x6901, 0x19 }, { 0x3503, 0x10 }, { 0x3025, 0x03 }, + { 0x3003, 0x16 }, { 0x3004, 0x30 }, { 0x3005, 0x40 }, { 0x3006, 0x91 }, + { 0x3600, 0x74 }, { 0x3601, 0x2b }, { 0x3612, 0x00 }, { 0x3611, 0x67 }, + { 0x3633, 0xca }, { 0x3602, 0xaf }, { 0x3603, 0x04 }, { 0x3630, 0x28 }, + { 0x3631, 0x16 }, { 0x3714, 0x10 }, { 0x371d, 0x01 }, { 0x4300, 0x3a }, + { 0x3007, 0x01 }, { 0x3024, 0x03 }, { 0x3020, 0x0a }, { 0x3702, 0x0d }, + { 0x3703, 0x20 }, { 0x3704, 0x15 }, { 0x3709, 0xa8 }, { 0x370c, 0xc7 }, + { 0x370d, 0x80 }, { 0x3712, 0x00 }, { 0x3713, 0x20 }, { 0x3715, 0x04 }, + { 0x381d, 0x40 }, { 0x381c, 0x00 }, { 0x3822, 0x50 }, { 0x3824, 0x10 }, + { 0x3815, 0x8c }, { 0x3804, 0x05 }, { 0x3805, 0x1f }, { 0x3800, 0x00 }, + { 0x3801, 0x00 }, { 0x3806, 0x03 }, { 0x3807, 0x28 }, { 0x3802, 0x00 }, + { 0x3803, 0x07 }, { 0x3808, 0x05 }, { 0x3809, 0x00 }, { 0x380a, 0x03 }, + { 0x380b, 0x20 }, { 0x380c, OV10635_HTS >> 8 }, + { 0x380d, OV10635_HTS & 0xff }, { 0x380e, OV10635_VTS >> 8 }, + { 0x380f, OV10635_VTS & 0xff }, { 0x3813, 0x02 }, { 0x3811, 0x08 }, + { 0x381f, 0x0c }, { 0x3819, 0x04 }, { 0x3804, 0x01 }, { 0x3805, 0x00 }, + { 0x3828, 0x03 }, { 0x3829, 0x10 }, { 0x382a, 0x10 }, { 0x3621, 0x63 }, + { 0x5005, 0x08 }, { 0x56d5, 0x00 }, { 0x56d6, 0x80 }, { 0x56d7, 0x00 }, + { 0x56d8, 0x00 }, { 0x56d9, 0x00 }, { 0x56da, 0x80 }, { 0x56db, 0x00 }, + { 0x56dc, 0x00 }, { 0x56e8, 0x00 }, { 0x56e9, 0x7f }, { 0x56ea, 0x00 }, + { 0x56eb, 0x7f }, { 0x5100, 0x00 }, { 0x5101, 0x80 }, { 0x5102, 0x00 }, + { 0x5103, 0x80 }, { 0x5104, 0x00 }, { 0x5105, 0x80 }, { 0x5106, 0x00 }, + { 0x5107, 0x80 }, { 0x5108, 0x00 }, { 0x5109, 0x00 }, { 0x510a, 0x00 }, + { 0x510b, 0x00 }, { 0x510c, 0x00 }, { 0x510d, 0x00 }, { 0x510e, 0x00 }, + { 0x510f, 0x00 }, { 0x5110, 0x00 }, { 0x5111, 0x80 }, { 0x5112, 0x00 }, + { 0x5113, 0x80 }, { 0x5114, 0x00 }, { 0x5115, 0x80 }, { 0x5116, 0x00 }, + { 0x5117, 0x80 }, { 0x5118, 0x00 }, { 0x5119, 0x00 }, { 0x511a, 0x00 }, + { 0x511b, 0x00 }, { 0x511c, 0x00 }, { 0x511d, 0x00 }, { 0x511e, 0x00 }, + { 0x511f, 0x00 }, { 0x56d0, 0x00 }, { 0x5006, 0x04 }, { 0x5608, 0x05 }, + { 0x52d7, 0x06 }, { 0x528d, 0x08 }, { 0x5293, 0x12 }, { 0x52d3, 0x12 }, + { 0x5288, 0x06 }, { 0x5289, 0x20 }, { 0x52c8, 0x06 }, { 0x52c9, 0x20 }, + { 0x52cd, 0x04 }, { 0x5381, 0x00 }, { 0x5382, 0xff }, { 0x5589, 0x76 }, + { 0x558a, 0x47 }, { 0x558b, 0xef }, { 0x558c, 0xc9 }, { 0x558d, 0x49 }, + { 0x558e, 0x30 }, { 0x558f, 0x67 }, { 0x5590, 0x3f }, { 0x5591, 0xf0 }, + { 0x5592, 0x10 }, { 0x55a2, 0x6d }, { 0x55a3, 0x55 }, { 0x55a4, 0xc3 }, + { 0x55a5, 0xb5 }, { 0x55a6, 0x43 }, { 0x55a7, 0x38 }, { 0x55a8, 0x5f }, + { 0x55a9, 0x4b }, { 0x55aa, 0xf0 }, { 0x55ab, 0x10 }, { 0x5581, 0x52 }, + { 0x5300, 0x01 }, { 0x5301, 0x00 }, { 0x5302, 0x00 }, { 0x5303, 0x0e }, + { 0x5304, 0x00 }, { 0x5305, 0x0e }, { 0x5306, 0x00 }, { 0x5307, 0x36 }, + { 0x5308, 0x00 }, { 0x5309, 0xd9 }, { 0x530a, 0x00 }, { 0x530b, 0x0f }, + { 0x530c, 0x00 }, { 0x530d, 0x2c }, { 0x530e, 0x00 }, { 0x530f, 0x59 }, + { 0x5310, 0x00 }, { 0x5311, 0x7b }, { 0x5312, 0x00 }, { 0x5313, 0x22 }, + { 0x5314, 0x00 }, { 0x5315, 0xd5 }, { 0x5316, 0x00 }, { 0x5317, 0x13 }, + { 0x5318, 0x00 }, { 0x5319, 0x18 }, { 0x531a, 0x00 }, { 0x531b, 0x26 }, + { 0x531c, 0x00 }, { 0x531d, 0xdc }, { 0x531e, 0x00 }, { 0x531f, 0x02 }, + { 0x5320, 0x00 }, { 0x5321, 0x24 }, { 0x5322, 0x00 }, { 0x5323, 0x56 }, + { 0x5324, 0x00 }, { 0x5325, 0x85 }, { 0x5326, 0x00 }, { 0x5327, 0x20 }, + { 0x5609, 0x01 }, { 0x560a, 0x40 }, { 0x560b, 0x01 }, { 0x560c, 0x40 }, + { 0x560d, 0x00 }, { 0x560e, 0xfa }, { 0x560f, 0x00 }, { 0x5610, 0xfa }, + { 0x5611, 0x02 }, { 0x5612, 0x80 }, { 0x5613, 0x02 }, { 0x5614, 0x80 }, + { 0x5615, 0x01 }, { 0x5616, 0x2c }, { 0x5617, 0x01 }, { 0x5618, 0x2c }, + { 0x563b, 0x01 }, { 0x563c, 0x01 }, { 0x563d, 0x01 }, { 0x563e, 0x01 }, + { 0x563f, 0x03 }, { 0x5640, 0x03 }, { 0x5641, 0x03 }, { 0x5642, 0x05 }, + { 0x5643, 0x09 }, { 0x5644, 0x05 }, { 0x5645, 0x05 }, { 0x5646, 0x05 }, + { 0x5647, 0x05 }, { 0x5651, 0x00 }, { 0x5652, 0x80 }, { 0x521a, 0x01 }, + { 0x521b, 0x03 }, { 0x521c, 0x06 }, { 0x521d, 0x0a }, { 0x521e, 0x0e }, + { 0x521f, 0x12 }, { 0x5220, 0x16 }, { 0x5223, 0x02 }, { 0x5225, 0x04 }, + { 0x5227, 0x08 }, { 0x5229, 0x0c }, { 0x522b, 0x12 }, { 0x522d, 0x18 }, + { 0x522f, 0x1e }, { 0x5241, 0x04 }, { 0x5242, 0x01 }, { 0x5243, 0x03 }, + { 0x5244, 0x06 }, { 0x5245, 0x0a }, { 0x5246, 0x0e }, { 0x5247, 0x12 }, + { 0x5248, 0x16 }, { 0x524a, 0x03 }, { 0x524c, 0x04 }, { 0x524e, 0x08 }, + { 0x5250, 0x0c }, { 0x5252, 0x12 }, { 0x5254, 0x18 }, { 0x5256, 0x1e }, + /* fifo_line_length = 2*hts */ + { 0x4606, (2 * OV10635_HTS) >> 8 }, { 0x4607, (2 * OV10635_HTS) & 0xff }, + /* fifo_hsync_start = 2*(hts - xres) */ + { 0x460a, (2 * (OV10635_HTS - OV10635_WIDTH)) >> 8 }, + { 0x460b, (2 * (OV10635_HTS - OV10635_WIDTH)) & 0xff }, + { 0x460c, 0x00 }, { 0x4620, 0x0e }, + /* BT601: 0x08 is also acceptable as HS/VS mode */ + { 0x4700, 0x04 }, { 0x4701, 0x00 }, { 0x4702, 0x01 }, { 0x4004, 0x04 }, + { 0x4005, 0x18 }, { 0x4001, 0x06 }, { 0x4050, 0x22 }, { 0x4051, 0x24 }, + { 0x4052, 0x02 }, { 0x4057, 0x9c }, { 0x405a, 0x00 }, { 0x4202, 0x02 }, + { 0x3023, 0x10 }, { 0x0100, 0x01 }, { 0x0100, 0x01 }, { 0x6f10, 0x07 }, + { 0x6f11, 0x82 }, { 0x6f12, 0x04 }, { 0x6f13, 0x00 }, { 0xd000, 0x19 }, + { 0xd001, 0xa0 }, { 0xd002, 0x00 }, { 0xd003, 0x01 }, { 0xd004, 0xa9 }, + { 0xd005, 0xad }, { 0xd006, 0x10 }, { 0xd007, 0x40 }, { 0xd008, 0x44 }, + { 0xd009, 0x00 }, { 0xd00a, 0x68 }, { 0xd00b, 0x00 }, { 0xd00c, 0x15 }, + { 0xd00d, 0x00 }, { 0xd00e, 0x00 }, { 0xd00f, 0x00 }, { 0xd040, 0x9c }, + { 0xd041, 0x21 }, { 0xd042, 0xff }, { 0xd043, 0xf8 }, { 0xd044, 0xd4 }, + { 0xd045, 0x01 }, { 0xd046, 0x48 }, { 0xd047, 0x00 }, { 0xd048, 0xd4 }, + { 0xd049, 0x01 }, { 0xd04a, 0x50 }, { 0xd04b, 0x04 }, { 0xd04c, 0x18 }, + { 0xd04d, 0x60 }, { 0xd04e, 0x00 }, { 0xd04f, 0x01 }, { 0xd050, 0xa8 }, + { 0xd051, 0x63 }, { 0xd052, 0x02 }, { 0xd053, 0xa4 }, { 0xd054, 0x85 }, + { 0xd055, 0x43 }, { 0xd056, 0x00 }, { 0xd057, 0x00 }, { 0xd058, 0x18 }, + { 0xd059, 0x60 }, { 0xd05a, 0x00 }, { 0xd05b, 0x01 }, { 0xd05c, 0xa8 }, + { 0xd05d, 0x63 }, { 0xd05e, 0x03 }, { 0xd05f, 0xf0 }, { 0xd060, 0x98 }, + { 0xd061, 0xa3 }, { 0xd062, 0x00 }, { 0xd063, 0x00 }, { 0xd064, 0x8c }, + { 0xd065, 0x6a }, { 0xd066, 0x00 }, { 0xd067, 0x6e }, { 0xd068, 0xe5 }, + { 0xd069, 0x85 }, { 0xd06a, 0x18 }, { 0xd06b, 0x00 }, { 0xd06c, 0x10 }, + { 0xd06d, 0x00 }, { 0xd06e, 0x00 }, { 0xd06f, 0x10 }, { 0xd070, 0x9c }, + { 0xd071, 0x80 }, { 0xd072, 0x00 }, { 0xd073, 0x03 }, { 0xd074, 0x18 }, + { 0xd075, 0x60 }, { 0xd076, 0x00 }, { 0xd077, 0x01 }, { 0xd078, 0xa8 }, + { 0xd079, 0x63 }, { 0xd07a, 0x07 }, { 0xd07b, 0x80 }, { 0xd07c, 0x07 }, + { 0xd07d, 0xff }, { 0xd07e, 0xf9 }, { 0xd07f, 0x03 }, { 0xd080, 0x8c }, + { 0xd081, 0x63 }, { 0xd082, 0x00 }, { 0xd083, 0x00 }, { 0xd084, 0xa5 }, + { 0xd085, 0x6b }, { 0xd086, 0x00 }, { 0xd087, 0xff }, { 0xd088, 0x18 }, + { 0xd089, 0x80 }, { 0xd08a, 0x00 }, { 0xd08b, 0x01 }, { 0xd08c, 0xa8 }, + { 0xd08d, 0x84 }, { 0xd08e, 0x01 }, { 0xd08f, 0x04 }, { 0xd090, 0xe1 }, + { 0xd091, 0x6b }, { 0xd092, 0x58 }, { 0xd093, 0x00 }, { 0xd094, 0x94 }, + { 0xd095, 0x6a }, { 0xd096, 0x00 }, { 0xd097, 0x70 }, { 0xd098, 0xe1 }, + { 0xd099, 0x6b }, { 0xd09a, 0x20 }, { 0xd09b, 0x00 }, { 0xd09c, 0x95 }, + { 0xd09d, 0x6b }, { 0xd09e, 0x00 }, { 0xd09f, 0x00 }, { 0xd0a0, 0xe4 }, + { 0xd0a1, 0x8b }, { 0xd0a2, 0x18 }, { 0xd0a3, 0x00 }, { 0xd0a4, 0x0c }, + { 0xd0a5, 0x00 }, { 0xd0a6, 0x00 }, { 0xd0a7, 0x23 }, { 0xd0a8, 0x15 }, + { 0xd0a9, 0x00 }, { 0xd0aa, 0x00 }, { 0xd0ab, 0x00 }, { 0xd0ac, 0x18 }, + { 0xd0ad, 0x60 }, { 0xd0ae, 0x80 }, { 0xd0af, 0x06 }, { 0xd0b0, 0xa8 }, + { 0xd0b1, 0x83 }, { 0xd0b2, 0x40 }, { 0xd0b3, 0x08 }, { 0xd0b4, 0xa8 }, + { 0xd0b5, 0xe3 }, { 0xd0b6, 0x38 }, { 0xd0b7, 0x2a }, { 0xd0b8, 0xa8 }, + { 0xd0b9, 0xc3 }, { 0xd0ba, 0x40 }, { 0xd0bb, 0x09 }, { 0xd0bc, 0xa8 }, + { 0xd0bd, 0xa3 }, { 0xd0be, 0x38 }, { 0xd0bf, 0x29 }, { 0xd0c0, 0x8c }, + { 0xd0c1, 0x65 }, { 0xd0c2, 0x00 }, { 0xd0c3, 0x00 }, { 0xd0c4, 0xd8 }, + { 0xd0c5, 0x04 }, { 0xd0c6, 0x18 }, { 0xd0c7, 0x00 }, { 0xd0c8, 0x8c }, + { 0xd0c9, 0x67 }, { 0xd0ca, 0x00 }, { 0xd0cb, 0x00 }, { 0xd0cc, 0xd8 }, + { 0xd0cd, 0x06 }, { 0xd0ce, 0x18 }, { 0xd0cf, 0x00 }, { 0xd0d0, 0x18 }, + { 0xd0d1, 0x60 }, { 0xd0d2, 0x80 }, { 0xd0d3, 0x06 }, { 0xd0d4, 0xa8 }, + { 0xd0d5, 0xe3 }, { 0xd0d6, 0x67 }, { 0xd0d7, 0x02 }, { 0xd0d8, 0xa9 }, + { 0xd0d9, 0x03 }, { 0xd0da, 0x67 }, { 0xd0db, 0x03 }, { 0xd0dc, 0xa8 }, + { 0xd0dd, 0xc3 }, { 0xd0de, 0x3d }, { 0xd0df, 0x05 }, { 0xd0e0, 0x8c }, + { 0xd0e1, 0x66 }, { 0xd0e2, 0x00 }, { 0xd0e3, 0x00 }, { 0xd0e4, 0xb8 }, + { 0xd0e5, 0x63 }, { 0xd0e6, 0x00 }, { 0xd0e7, 0x18 }, { 0xd0e8, 0xb8 }, + { 0xd0e9, 0x63 }, { 0xd0ea, 0x00 }, { 0xd0eb, 0x98 }, { 0xd0ec, 0xbc }, + { 0xd0ed, 0x03 }, { 0xd0ee, 0x00 }, { 0xd0ef, 0x00 }, { 0xd0f0, 0x10 }, + { 0xd0f1, 0x00 }, { 0xd0f2, 0x00 }, { 0xd0f3, 0x16 }, { 0xd0f4, 0xb8 }, + { 0xd0f5, 0x83 }, { 0xd0f6, 0x00 }, { 0xd0f7, 0x19 }, { 0xd0f8, 0x8c }, + { 0xd0f9, 0x67 }, { 0xd0fa, 0x00 }, { 0xd0fb, 0x00 }, { 0xd0fc, 0xb8 }, + { 0xd0fd, 0xa4 }, { 0xd0fe, 0x00 }, { 0xd0ff, 0x98 }, { 0xd100, 0xb8 }, + { 0xd101, 0x83 }, { 0xd102, 0x00 }, { 0xd103, 0x08 }, { 0xd104, 0x8c }, + { 0xd105, 0x68 }, { 0xd106, 0x00 }, { 0xd107, 0x00 }, { 0xd108, 0xe0 }, + { 0xd109, 0x63 }, { 0xd10a, 0x20 }, { 0xd10b, 0x04 }, { 0xd10c, 0xe0 }, + { 0xd10d, 0x65 }, { 0xd10e, 0x18 }, { 0xd10f, 0x00 }, { 0xd110, 0xa4 }, + { 0xd111, 0x83 }, { 0xd112, 0xff }, { 0xd113, 0xff }, { 0xd114, 0xb8 }, + { 0xd115, 0x64 }, { 0xd116, 0x00 }, { 0xd117, 0x48 }, { 0xd118, 0xd8 }, + { 0xd119, 0x07 }, { 0xd11a, 0x18 }, { 0xd11b, 0x00 }, { 0xd11c, 0xd8 }, + { 0xd11d, 0x08 }, { 0xd11e, 0x20 }, { 0xd11f, 0x00 }, { 0xd120, 0x9c }, + { 0xd121, 0x60 }, { 0xd122, 0x00 }, { 0xd123, 0x00 }, { 0xd124, 0xd8 }, + { 0xd125, 0x06 }, { 0xd126, 0x18 }, { 0xd127, 0x00 }, { 0xd128, 0x00 }, + { 0xd129, 0x00 }, { 0xd12a, 0x00 }, { 0xd12b, 0x08 }, { 0xd12c, 0x15 }, + { 0xd12d, 0x00 }, { 0xd12e, 0x00 }, { 0xd12f, 0x00 }, { 0xd130, 0x8c }, + { 0xd131, 0x6a }, { 0xd132, 0x00 }, { 0xd133, 0x76 }, { 0xd134, 0xbc }, + { 0xd135, 0x23 }, { 0xd136, 0x00 }, { 0xd137, 0x00 }, { 0xd138, 0x13 }, + { 0xd139, 0xff }, { 0xd13a, 0xff }, { 0xd13b, 0xe6 }, { 0xd13c, 0x18 }, + { 0xd13d, 0x60 }, { 0xd13e, 0x80 }, { 0xd13f, 0x06 }, { 0xd140, 0x03 }, + { 0xd141, 0xff }, { 0xd142, 0xff }, { 0xd143, 0xdd }, { 0xd144, 0xa8 }, + { 0xd145, 0x83 }, { 0xd146, 0x40 }, { 0xd147, 0x08 }, { 0xd148, 0x85 }, + { 0xd149, 0x21 }, { 0xd14a, 0x00 }, { 0xd14b, 0x00 }, { 0xd14c, 0x85 }, + { 0xd14d, 0x41 }, { 0xd14e, 0x00 }, { 0xd14f, 0x04 }, { 0xd150, 0x44 }, + { 0xd151, 0x00 }, { 0xd152, 0x48 }, { 0xd153, 0x00 }, { 0xd154, 0x9c }, + { 0xd155, 0x21 }, { 0xd156, 0x00 }, { 0xd157, 0x08 }, { 0x6f0e, 0x03 }, + { 0x6f0f, 0x00 }, { 0x460e, 0x08 }, { 0x460f, 0x01 }, { 0x4610, 0x00 }, + { 0x4611, 0x01 }, { 0x4612, 0x00 }, { 0x4613, 0x01 }, + /* 8 bits */ + { 0x4605, 0x08 }, + /* Swap data bits order [9:0] -> [0:9] */ + { 0x4709, 0x10 }, { 0x4608, 0x00 }, { 0x4609, 0x08 }, { 0x6804, 0x00 }, + { 0x6805, 0x06 }, { 0x6806, 0x00 }, { 0x5120, 0x00 }, { 0x3510, 0x00 }, + { 0x3504, 0x00 }, { 0x6800, 0x00 }, { 0x6f0d, 0x01 }, + /* PCLK falling edge */ + { 0x4708, 0x01 }, { 0x5000, 0xff }, { 0x5001, 0xbf }, { 0x5002, 0x7e }, + { 0x503d, 0x00 }, { 0xc450, 0x01 }, { 0xc452, 0x04 }, { 0xc453, 0x00 }, + { 0xc454, 0x00 }, { 0xc455, 0x01 }, { 0xc456, 0x01 }, { 0xc457, 0x00 }, + { 0xc458, 0x00 }, { 0xc459, 0x00 }, { 0xc45b, 0x00 }, { 0xc45c, 0x01 }, + { 0xc45d, 0x00 }, { 0xc45e, 0x00 }, { 0xc45f, 0x00 }, { 0xc460, 0x00 }, + { 0xc461, 0x01 }, { 0xc462, 0x01 }, { 0xc464, 0x03 }, { 0xc465, 0x00 }, + { 0xc466, 0x8a }, { 0xc467, 0x00 }, { 0xc468, 0x86 }, { 0xc469, 0x00 }, + { 0xc46a, 0x40 }, { 0xc46b, 0x50 }, { 0xc46c, 0x30 }, { 0xc46d, 0x28 }, + { 0xc46e, 0x60 }, { 0xc46f, 0x40 }, { 0xc47c, 0x01 }, { 0xc47d, 0x38 }, + { 0xc47e, 0x00 }, { 0xc47f, 0x00 }, { 0xc480, 0x00 }, { 0xc481, 0xff }, + { 0xc482, 0x00 }, { 0xc483, 0x40 }, { 0xc484, 0x00 }, { 0xc485, 0x18 }, + { 0xc486, 0x00 }, { 0xc487, 0x18 }, + { 0xc488, (OV10635_VTS - 8) * 16 >> 8}, + { 0xc489, (OV10635_VTS - 8) * 16 & 0xff}, + { 0xc48a, (OV10635_VTS - 8) * 16 >> 8}, + { 0xc48b, (OV10635_VTS - 8) * 16 & 0xff}, { 0xc48c, 0x00 }, + { 0xc48d, 0x04 }, { 0xc48e, 0x00 }, { 0xc48f, 0x04 }, { 0xc490, 0x03 }, + { 0xc492, 0x20 }, { 0xc493, 0x08 }, { 0xc498, 0x02 }, { 0xc499, 0x00 }, + { 0xc49a, 0x02 }, { 0xc49b, 0x00 }, { 0xc49c, 0x02 }, { 0xc49d, 0x00 }, + { 0xc49e, 0x02 }, { 0xc49f, 0x60 }, { 0xc4a0, 0x03 }, { 0xc4a1, 0x00 }, + { 0xc4a2, 0x04 }, { 0xc4a3, 0x00 }, { 0xc4a4, 0x00 }, { 0xc4a5, 0x10 }, + { 0xc4a6, 0x00 }, { 0xc4a7, 0x40 }, { 0xc4a8, 0x00 }, { 0xc4a9, 0x80 }, + { 0xc4aa, 0x0d }, { 0xc4ab, 0x00 }, { 0xc4ac, 0x0f }, { 0xc4ad, 0xc0 }, + { 0xc4b4, 0x01 }, { 0xc4b5, 0x01 }, { 0xc4b6, 0x00 }, { 0xc4b7, 0x01 }, + { 0xc4b8, 0x00 }, { 0xc4b9, 0x01 }, { 0xc4ba, 0x01 }, { 0xc4bb, 0x00 }, + { 0xc4bc, 0x01 }, { 0xc4bd, 0x60 }, { 0xc4be, 0x02 }, { 0xc4bf, 0x33 }, + { 0xc4c8, 0x03 }, { 0xc4c9, 0xd0 }, { 0xc4ca, 0x0e }, { 0xc4cb, 0x00 }, + { 0xc4cc, 0x0e }, { 0xc4cd, 0x51 }, { 0xc4ce, 0x0e }, { 0xc4cf, 0x51 }, + { 0xc4d0, 0x04 }, { 0xc4d1, 0x80 }, { 0xc4e0, 0x04 }, { 0xc4e1, 0x02 }, + { 0xc4e2, 0x01 }, { 0xc4e4, 0x10 }, { 0xc4e5, 0x20 }, { 0xc4e6, 0x30 }, + { 0xc4e7, 0x40 }, { 0xc4e8, 0x50 }, { 0xc4e9, 0x60 }, { 0xc4ea, 0x70 }, + { 0xc4eb, 0x80 }, { 0xc4ec, 0x90 }, { 0xc4ed, 0xa0 }, { 0xc4ee, 0xb0 }, + { 0xc4ef, 0xc0 }, { 0xc4f0, 0xd0 }, { 0xc4f1, 0xe0 }, { 0xc4f2, 0xf0 }, + { 0xc4f3, 0x80 }, { 0xc4f4, 0x00 }, { 0xc4f5, 0x20 }, { 0xc4f6, 0x02 }, + { 0xc4f7, 0x00 }, { 0xc4f8, 0x00 }, { 0xc4f9, 0x00 }, { 0xc4fa, 0x00 }, + { 0xc4fb, 0x01 }, { 0xc4fc, 0x01 }, { 0xc4fd, 0x00 }, { 0xc4fe, 0x04 }, + { 0xc4ff, 0x02 }, { 0xc500, 0x48 }, { 0xc501, 0x74 }, { 0xc502, 0x58 }, + { 0xc503, 0x80 }, { 0xc504, 0x05 }, { 0xc505, 0x80 }, { 0xc506, 0x03 }, + { 0xc507, 0x80 }, { 0xc508, 0x01 }, { 0xc509, 0xc0 }, { 0xc50a, 0x01 }, + { 0xc50b, 0xa0 }, { 0xc50c, 0x01 }, { 0xc50d, 0x2c }, { 0xc50e, 0x01 }, + { 0xc50f, 0x0a }, { 0xc510, 0x00 }, { 0xc511, 0x00 }, { 0xc512, 0xe5 }, + { 0xc513, 0x14 }, { 0xc514, 0x04 }, { 0xc515, 0x00 }, { 0xc518, OV10635_VTS >> 8}, + { 0xc519, OV10635_VTS & 0xff}, { 0xc51a, OV10635_HTS >> 8}, + { 0xc51b, OV10635_HTS & 0xff}, { 0xc2e0, 0x00 }, { 0xc2e1, 0x51 }, + { 0xc2e2, 0x00 }, { 0xc2e3, 0xd6 }, { 0xc2e4, 0x01 }, { 0xc2e5, 0x5e }, + { 0xc2e9, 0x01 }, { 0xc2ea, 0x7a }, { 0xc2eb, 0x90 }, { 0xc2ed, 0x00 }, + { 0xc2ee, 0x7a }, { 0xc2ef, 0x64 }, { 0xc308, 0x00 }, { 0xc309, 0x00 }, + { 0xc30a, 0x00 }, { 0xc30c, 0x00 }, { 0xc30d, 0x01 }, { 0xc30e, 0x00 }, + { 0xc30f, 0x00 }, { 0xc310, 0x01 }, { 0xc311, 0x60 }, { 0xc312, 0xff }, + { 0xc313, 0x08 }, { 0xc314, 0x01 }, { 0xc315, 0x00 }, { 0xc316, 0xff }, + { 0xc317, 0x0b }, { 0xc318, 0x00 }, { 0xc319, 0x0c }, { 0xc31a, 0x00 }, + { 0xc31b, 0xe0 }, { 0xc31c, 0x00 }, { 0xc31d, 0x14 }, { 0xc31e, 0x00 }, + { 0xc31f, 0xc5 }, { 0xc320, 0xff }, { 0xc321, 0x4b }, { 0xc322, 0xff }, + { 0xc323, 0xf0 }, { 0xc324, 0xff }, { 0xc325, 0xe8 }, { 0xc326, 0x00 }, + { 0xc327, 0x46 }, { 0xc328, 0xff }, { 0xc329, 0xd2 }, { 0xc32a, 0xff }, + { 0xc32b, 0xe4 }, { 0xc32c, 0xff }, { 0xc32d, 0xbb }, { 0xc32e, 0x00 }, + { 0xc32f, 0x61 }, { 0xc330, 0xff }, { 0xc331, 0xf9 }, { 0xc332, 0x00 }, + { 0xc333, 0xd9 }, { 0xc334, 0x00 }, { 0xc335, 0x2e }, { 0xc336, 0x00 }, + { 0xc337, 0xb1 }, { 0xc338, 0xff }, { 0xc339, 0x64 }, { 0xc33a, 0xff }, + { 0xc33b, 0xeb }, { 0xc33c, 0xff }, { 0xc33d, 0xe8 }, { 0xc33e, 0x00 }, + { 0xc33f, 0x48 }, { 0xc340, 0xff }, { 0xc341, 0xd0 }, { 0xc342, 0xff }, + { 0xc343, 0xed }, { 0xc344, 0xff }, { 0xc345, 0xad }, { 0xc346, 0x00 }, + { 0xc347, 0x66 }, { 0xc348, 0x01 }, { 0xc349, 0x00 }, { 0x6700, 0x04 }, + { 0x6701, 0x7b }, { 0x6702, 0xfd }, { 0x6703, 0xf9 }, { 0x6704, 0x3d }, + { 0x6705, 0x71 }, { 0x6706, 0x78 }, { 0x6708, 0x05 }, { 0x6f06, 0x6f }, + { 0x6f07, 0x00 }, { 0x6f0a, 0x6f }, { 0x6f0b, 0x00 }, { 0x6f00, 0x03 }, + { 0xc34c, 0x01 }, { 0xc34d, 0x00 }, { 0xc34e, 0x46 }, { 0xc34f, 0x55 }, + { 0xc350, 0x00 }, { 0xc351, 0x40 }, { 0xc352, 0x00 }, { 0xc353, 0xff }, + { 0xc354, 0x04 }, { 0xc355, 0x08 }, { 0xc356, 0x01 }, { 0xc357, 0xef }, + { 0xc358, 0x30 }, { 0xc359, 0x01 }, { 0xc35a, 0x64 }, { 0xc35b, 0x46 }, + { 0xc35c, 0x00 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, + { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, + { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, + { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, + { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, + { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, + { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0xc261, 0x01 }, + { 0x301b, 0xf0 }, { 0x301c, 0xf0 }, { 0x301a, 0xf0 }, { 0x6f00, 0xc3 }, + { 0xc46a, 0x30 }, { 0xc46d, 0x20 }, { 0xc464, 0x84 }, { 0xc465, 0x00 }, + { 0x6f00, 0x03 }, { 0x6f00, 0x43 }, { 0x381c, 0x00 }, { 0x381d, 0x40 }, + { 0xc454, 0x01 }, { 0x6f00, 0xc3 }, { 0xc454, 0x00 }, { 0xc4b1, 0x02 }, + { 0xc4b2, 0x01 }, { 0xc4b3, 0x03 }, { 0x6f00, 0x03 }, { 0x6f00, 0x43 }, + /* enable FSIN (FRAMESYNC input) functionality */ + { 0x3832, (0x0d + 2 * 0x20 + 0x15 + 38) >> 8 }, + { 0x3833, (0x0d + 2 * 0x20 + 0x15 + 38) & 0xff }, + { 0x3834, OV10635_VTS >> 8 }, { 0x3835, OV10635_VTS & 0xff }, + { 0x302e, 0x01 }, +}; + +struct rdacm20_device { + struct device *dev; + struct max9271_device *serializer; + struct i2c_client *sensor; + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_ctrl_handler ctrls; + u32 addrs[2]; +}; + +static inline struct rdacm20_device *sd_to_rdacm20(struct v4l2_subdev *sd) +{ + return container_of(sd, struct rdacm20_device, sd); +} + +static inline struct rdacm20_device *i2c_to_rdacm20(struct i2c_client *client) +{ + return sd_to_rdacm20(i2c_get_clientdata(client)); +} + +static int ov10635_read16(struct rdacm20_device *dev, u16 reg) +{ + u8 buf[2] = { reg >> 8, reg & 0xff }; + int ret; + + ret = i2c_master_send(dev->sensor, buf, 2); + if (ret != 2) { + dev_dbg(dev->dev, "%s: register 0x%04x write failed (%d)\n", + __func__, reg, ret); + return ret; + } + + ret = i2c_master_recv(dev->sensor, buf, 2); + if (ret < 0) { + dev_dbg(dev->dev, "%s: register 0x%04x read failed (%d)\n", + __func__, reg, ret); + return ret; + } + + return (buf[0] << 8) | buf[1]; +} + +static int __ov10635_write(struct rdacm20_device *dev, u16 reg, u8 val) +{ + u8 buf[3] = { reg >> 8, reg & 0xff, val }; + int ret; + + dev_dbg(dev->dev, "%s(0x%04x, 0x%02x)\n", __func__, reg, val); + + ret = i2c_master_send(dev->sensor, buf, 3); + return ret < 0 ? ret : 0; +} + +static int ov10635_write(struct rdacm20_device *dev, u16 reg, u8 val) +{ + int ret; + + ret = __ov10635_write(dev, reg, val); + if (ret < 0) + dev_err(dev->dev, "%s: register 0x%04x write failed (%d)\n", + __func__, reg, ret); + + return ret; +} + +static int ov10635_set_regs(struct rdacm20_device *dev, + const struct ov10635_reg *regs, + unsigned int nr_regs) +{ + unsigned int i; + int ret; + + for (i = 0; i < nr_regs; i++) { + ret = __ov10635_write(dev, regs[i].reg, regs[i].val); + if (ret) { + dev_err(dev->dev, + "%s: register %u (0x%04x) write failed (%d)\n", + __func__, i, regs[i].reg, ret); + return ret; + } + } + + return 0; +} + +static int rdacm20_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct rdacm20_device *dev = sd_to_rdacm20(sd); + + return max9271_set_serial_link(dev->serializer, enable); +} + +static int rdacm20_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad || code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_UYVY8_2X8; + + return 0; +} + +static int rdacm20_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mf = &format->format; + + if (format->pad) + return -EINVAL; + + mf->width = OV10635_WIDTH; + mf->height = OV10635_HEIGHT; + mf->code = MEDIA_BUS_FMT_UYVY8_2X8; + mf->colorspace = V4L2_COLORSPACE_RAW; + mf->field = V4L2_FIELD_NONE; + mf->ycbcr_enc = V4L2_YCBCR_ENC_601; + mf->quantization = V4L2_QUANTIZATION_FULL_RANGE; + mf->xfer_func = V4L2_XFER_FUNC_NONE; + + return 0; +} + +static struct v4l2_subdev_video_ops rdacm20_video_ops = { + .s_stream = rdacm20_s_stream, +}; + +static const struct v4l2_subdev_pad_ops rdacm20_subdev_pad_ops = { + .enum_mbus_code = rdacm20_enum_mbus_code, + .get_fmt = rdacm20_get_fmt, + .set_fmt = rdacm20_get_fmt, +}; + +static struct v4l2_subdev_ops rdacm20_subdev_ops = { + .video = &rdacm20_video_ops, + .pad = &rdacm20_subdev_pad_ops, +}; + +static int rdacm20_initialize(struct rdacm20_device *dev) +{ + unsigned int retry = 3; + int ret; + + /* Verify communication with the MAX9271: ping to wakeup. */ + dev->serializer->client->addr = MAX9271_DEFAULT_ADDR; + i2c_smbus_read_byte(dev->serializer->client); + + /* Serial link disabled during config as it needs a valid pixel clock. */ + ret = max9271_set_serial_link(dev->serializer, false); + if (ret) + return ret; + + /* + * Ensure that we have a good link configuration before attempting to + * identify the device. + */ + max9271_configure_i2c(dev->serializer, MAX9271_I2CSLVSH_469NS_234NS | + MAX9271_I2CSLVTO_1024US | + MAX9271_I2CMSTBT_105KBPS); + + max9271_configure_gmsl_link(dev->serializer); + + ret = max9271_verify_id(dev->serializer); + if (ret < 0) + return ret; + + ret = max9271_set_address(dev->serializer, dev->addrs[0]); + if (ret < 0) + return ret; + dev->serializer->client->addr = dev->addrs[0]; + + /* + * Reset the sensor by cycling the OV10635 reset signal connected to the + * MAX9271 GPIO1 and verify communication with the OV10635. + */ + max9271_clear_gpios(dev->serializer, MAX9271_GPIO1OUT); + usleep_range(10000, 15000); + max9271_set_gpios(dev->serializer, MAX9271_GPIO1OUT); + usleep_range(10000, 15000); + +again: + ret = ov10635_read16(dev, OV10635_PID); + if (ret < 0) { + if (retry--) + goto again; + + dev_err(dev->dev, "OV10635 ID read failed (%d)\n", + ret); + return -ENXIO; + } + + if (ret != OV10635_VERSION) { + if (retry--) + goto again; + + dev_err(dev->dev, "OV10635 ID mismatch (0x%04x)\n", + ret); + return -ENXIO; + } + + /* Change the sensor I2C address. */ + ret = ov10635_write(dev, OV10635_SC_CMMN_SCCB_ID, + (dev->addrs[1] << 1) | + OV10635_SC_CMMN_SCCB_ID_SELECT); + if (ret < 0) { + dev_err(dev->dev, + "OV10635 I2C address change failed (%d)\n", ret); + return ret; + } + dev->sensor->addr = dev->addrs[1]; + usleep_range(3500, 5000); + + /* Program the 0V10635 initial configuration. */ + ret = ov10635_set_regs(dev, ov10635_regs_wizard, + ARRAY_SIZE(ov10635_regs_wizard)); + if (ret) + return ret; + + dev_info(dev->dev, "Identified MAX9271 + OV10635 device\n"); + + return 0; +} + +static int rdacm20_probe(struct i2c_client *client) +{ + struct rdacm20_device *dev; + struct fwnode_handle *ep; + int ret; + + dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + dev->dev = &client->dev; + + dev->serializer = devm_kzalloc(&client->dev, sizeof(*dev->serializer), + GFP_KERNEL); + if (!dev->serializer) + return -ENOMEM; + + dev->serializer->client = client; + + ret = of_property_read_u32_array(client->dev.of_node, "reg", + dev->addrs, 2); + if (ret < 0) { + dev_err(dev->dev, "Invalid DT reg property: %d\n", ret); + return -EINVAL; + } + + /* Create the dummy I2C client for the sensor. */ + dev->sensor = i2c_new_dummy_device(client->adapter, + OV10635_I2C_ADDRESS); + if (IS_ERR(dev->sensor)) { + ret = PTR_ERR(dev->sensor); + goto error; + } + + /* Initialize the hardware. */ + ret = rdacm20_initialize(dev); + if (ret < 0) + goto error; + + /* Initialize and register the subdevice. */ + v4l2_i2c_subdev_init(&dev->sd, client, &rdacm20_subdev_ops); + dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + v4l2_ctrl_handler_init(&dev->ctrls, 1); + v4l2_ctrl_new_std(&dev->ctrls, NULL, V4L2_CID_PIXEL_RATE, + OV10635_PIXEL_RATE, OV10635_PIXEL_RATE, 1, + OV10635_PIXEL_RATE); + dev->sd.ctrl_handler = &dev->ctrls; + + ret = dev->ctrls.error; + if (ret) + goto error_free_ctrls; + + dev->pad.flags = MEDIA_PAD_FL_SOURCE; + dev->sd.entity.flags |= MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&dev->sd.entity, 1, &dev->pad); + if (ret < 0) + goto error_free_ctrls; + + ep = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL); + if (!ep) { + dev_err(&client->dev, + "Unable to get endpoint in node %pOF\n", + client->dev.of_node); + ret = -ENOENT; + goto error_free_ctrls; + } + dev->sd.fwnode = ep; + + ret = v4l2_async_register_subdev(&dev->sd); + if (ret) + goto error_put_node; + + return 0; + +error_put_node: + fwnode_handle_put(ep); +error_free_ctrls: + v4l2_ctrl_handler_free(&dev->ctrls); +error: + media_entity_cleanup(&dev->sd.entity); + if (dev->sensor) + i2c_unregister_device(dev->sensor); + + dev_err(&client->dev, "probe failed\n"); + + return ret; +} + +static int rdacm20_remove(struct i2c_client *client) +{ + struct rdacm20_device *dev = i2c_to_rdacm20(client); + + fwnode_handle_put(dev->sd.fwnode); + v4l2_async_unregister_subdev(&dev->sd); + v4l2_ctrl_handler_free(&dev->ctrls); + media_entity_cleanup(&dev->sd.entity); + i2c_unregister_device(dev->sensor); + + return 0; +} + +static void rdacm20_shutdown(struct i2c_client *client) +{ + struct rdacm20_device *dev = i2c_to_rdacm20(client); + + /* make sure stream off during shutdown (reset/reboot) */ + rdacm20_s_stream(&dev->sd, 0); +} + +static const struct of_device_id rdacm20_of_ids[] = { + { .compatible = "imi,rdacm20", }, + { } +}; +MODULE_DEVICE_TABLE(of, rdacm20_of_ids); + +static struct i2c_driver rdacm20_i2c_driver = { + .driver = { + .name = "rdacm20", + .of_match_table = rdacm20_of_ids, + }, + .probe_new = rdacm20_probe, + .remove = rdacm20_remove, + .shutdown = rdacm20_shutdown, +}; + +module_i2c_driver(rdacm20_i2c_driver); + +MODULE_DESCRIPTION("GMSL Camera driver for RDACM20"); +MODULE_AUTHOR("Vladimir Barinov"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/s5k6a3.c b/drivers/media/i2c/s5k6a3.c index bc6cc5a558db..f26c168ef942 100644 --- a/drivers/media/i2c/s5k6a3.c +++ b/drivers/media/i2c/s5k6a3.c @@ -197,7 +197,7 @@ static int __s5k6a3_power_on(struct s5k6a3 *sensor) ret = pm_runtime_get(sensor->dev); if (ret < 0) - return ret; + goto error_rpm_put; ret = regulator_enable(sensor->supplies[i].consumer); if (ret < 0) diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 5e4f6a2ef78e..8a9c7de0c056 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -508,9 +508,7 @@ static int smiapp_set_ctrl(struct v4l2_ctrl *ctrl) break; } - pm_runtime_get_noresume(&client->dev); - pm_status = pm_runtime_get_if_in_use(&client->dev); - pm_runtime_put_noidle(&client->dev); + pm_status = pm_runtime_get_if_active(&client->dev, true); if (!pm_status) return 0; @@ -3103,6 +3101,7 @@ static int smiapp_probe(struct i2c_client *client) return 0; out_disable_runtime_pm: + pm_runtime_put_noidle(&client->dev); pm_runtime_disable(&client->dev); out_media_entity_cleanup: diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index eb39cf5ea089..9df575238952 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -1664,8 +1664,10 @@ static int tvp5150_registered(struct v4l2_subdev *sd) return 0; err: - for (i = 0; i < decoder->connectors_num; i++) + for (i = 0; i < decoder->connectors_num; i++) { media_device_unregister_entity(&decoder->connectors[i].ent); + media_entity_cleanup(&decoder->connectors[i].ent); + } return ret; #endif @@ -2248,8 +2250,10 @@ static int tvp5150_remove(struct i2c_client *c) for (i = 0; i < decoder->connectors_num; i++) v4l2_fwnode_connector_free(&decoder->connectors[i].base); - for (i = 0; i < decoder->connectors_num; i++) + for (i = 0; i < decoder->connectors_num; i++) { media_device_unregister_entity(&decoder->connectors[i].ent); + media_entity_cleanup(&decoder->connectors[i].ent); + } v4l2_async_unregister_subdev(sd); v4l2_ctrl_handler_free(&decoder->hdl); pm_runtime_disable(&c->dev); diff --git a/drivers/media/mc/mc-request.c b/drivers/media/mc/mc-request.c index e3fca436c75b..c0782fd96c59 100644 --- a/drivers/media/mc/mc-request.c +++ b/drivers/media/mc/mc-request.c @@ -296,9 +296,18 @@ int media_request_alloc(struct media_device *mdev, int *alloc_fd) if (WARN_ON(!mdev->ops->req_alloc ^ !mdev->ops->req_free)) return -ENOMEM; + if (mdev->ops->req_alloc) + req = mdev->ops->req_alloc(mdev); + else + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + fd = get_unused_fd_flags(O_CLOEXEC); - if (fd < 0) - return fd; + if (fd < 0) { + ret = fd; + goto err_free_req; + } filp = anon_inode_getfile("request", &request_fops, NULL, O_CLOEXEC); if (IS_ERR(filp)) { @@ -306,15 +315,6 @@ int media_request_alloc(struct media_device *mdev, int *alloc_fd) goto err_put_fd; } - if (mdev->ops->req_alloc) - req = mdev->ops->req_alloc(mdev); - else - req = kzalloc(sizeof(*req), GFP_KERNEL); - if (!req) { - ret = -ENOMEM; - goto err_fput; - } - filp->private_data = req; req->mdev = mdev; req->state = MEDIA_REQUEST_STATE_IDLE; @@ -336,12 +336,15 @@ int media_request_alloc(struct media_device *mdev, int *alloc_fd) return 0; -err_fput: - fput(filp); - err_put_fd: put_unused_fd(fd); +err_free_req: + if (mdev->ops->req_free) + mdev->ops->req_free(req); + else + kfree(req); + return ret; } diff --git a/drivers/media/pci/cx18/cx18-cards.c b/drivers/media/pci/cx18/cx18-cards.c index ecbe76f1ca63..f5a30959a367 100644 --- a/drivers/media/pci/cx18/cx18-cards.c +++ b/drivers/media/pci/cx18/cx18-cards.c @@ -35,7 +35,7 @@ static struct cx18_card_tuner_i2c cx18_i2c_nxp = { .tv = { 0x61, 0x60, I2C_CLIENT_END }, }; -/* Please add new PCI IDs to: http://pci-ids.ucw.cz/ +/* Please add new PCI IDs to: https://pci-ids.ucw.cz/ This keeps the PCI ID database up to date. Note that the entries must be added under vendor 0x4444 (Conexant) as subsystem IDs. New vendor IDs should still be added to the vendor ID list. */ diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c index 7e0b0b7cc2a3..4b0c53f61fb7 100644 --- a/drivers/media/pci/cx23885/cx23885-core.c +++ b/drivers/media/pci/cx23885/cx23885-core.c @@ -2235,9 +2235,6 @@ static struct pci_driver cx23885_pci_driver = { .id_table = cx23885_pci_tbl, .probe = cx23885_initdev, .remove = cx23885_finidev, - /* TODO */ - .suspend = NULL, - .resume = NULL, }; static int __init cx23885_init(void) diff --git a/drivers/media/pci/cx23885/cx23888-ir.c b/drivers/media/pci/cx23885/cx23888-ir.c index d59ca3601785..ad7f8ccad526 100644 --- a/drivers/media/pci/cx23885/cx23888-ir.c +++ b/drivers/media/pci/cx23885/cx23888-ir.c @@ -175,19 +175,6 @@ static inline u16 count_to_clock_divider(unsigned int d) return (u16) d; } -static inline u16 ns_to_clock_divider(unsigned int ns) -{ - return count_to_clock_divider( - DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ / 1000000 * ns, 1000)); -} - -static inline unsigned int clock_divider_to_ns(unsigned int divider) -{ - /* Period of the Rx or Tx clock in ns */ - return DIV_ROUND_CLOSEST((divider + 1) * 1000, - CX23888_IR_REFCLK_FREQ / 1000000); -} - static inline u16 carrier_freq_to_clock_divider(unsigned int freq) { return count_to_clock_divider( @@ -199,13 +186,6 @@ static inline unsigned int clock_divider_to_carrier_freq(unsigned int divider) return DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, (divider + 1) * 16); } -static inline u16 freq_to_clock_divider(unsigned int freq, - unsigned int rollovers) -{ - return count_to_clock_divider( - DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, freq * rollovers)); -} - static inline unsigned int clock_divider_to_freq(unsigned int divider, unsigned int rollovers) { diff --git a/drivers/media/pci/cx25821/cx25821-core.c b/drivers/media/pci/cx25821/cx25821-core.c index 41be22ce66f3..55018d9e439f 100644 --- a/drivers/media/pci/cx25821/cx25821-core.c +++ b/drivers/media/pci/cx25821/cx25821-core.c @@ -1374,9 +1374,6 @@ static struct pci_driver cx25821_pci_driver = { .id_table = cx25821_pci_tbl, .probe = cx25821_initdev, .remove = cx25821_finidev, - /* TODO */ - .suspend = NULL, - .resume = NULL, }; static int __init cx25821_init(void) diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c index ba0e9660a047..e7fd7516787c 100644 --- a/drivers/media/pci/cx88/cx88-video.c +++ b/drivers/media/pci/cx88/cx88-video.c @@ -385,8 +385,7 @@ static int start_video_dma(struct cx8800_dev *dev, return 0; } -#ifdef CONFIG_PM -static int stop_video_dma(struct cx8800_dev *dev) +static int __maybe_unused stop_video_dma(struct cx8800_dev *dev) { struct cx88_core *core = dev->core; @@ -402,8 +401,8 @@ static int stop_video_dma(struct cx8800_dev *dev) return 0; } -static int restart_video_queue(struct cx8800_dev *dev, - struct cx88_dmaqueue *q) +static int __maybe_unused restart_video_queue(struct cx8800_dev *dev, + struct cx88_dmaqueue *q) { struct cx88_buffer *buf; @@ -415,7 +414,6 @@ static int restart_video_queue(struct cx8800_dev *dev, } return 0; } -#endif /* ------------------------------------------------------------------ */ @@ -1551,10 +1549,9 @@ static void cx8800_finidev(struct pci_dev *pci_dev) kfree(dev); } -#ifdef CONFIG_PM -static int cx8800_suspend(struct pci_dev *pci_dev, pm_message_t state) +static int __maybe_unused cx8800_suspend(struct device *dev_d) { - struct cx8800_dev *dev = pci_get_drvdata(pci_dev); + struct cx8800_dev *dev = dev_get_drvdata(dev_d); struct cx88_core *core = dev->core; unsigned long flags; @@ -1575,40 +1572,17 @@ static int cx8800_suspend(struct pci_dev *pci_dev, pm_message_t state) /* FIXME -- shutdown device */ cx88_shutdown(core); - pci_save_state(pci_dev); - if (pci_set_power_state(pci_dev, - pci_choose_state(pci_dev, state)) != 0) { - pci_disable_device(pci_dev); - dev->state.disabled = 1; - } + dev->state.disabled = 1; return 0; } -static int cx8800_resume(struct pci_dev *pci_dev) +static int __maybe_unused cx8800_resume(struct device *dev_d) { - struct cx8800_dev *dev = pci_get_drvdata(pci_dev); + struct cx8800_dev *dev = dev_get_drvdata(dev_d); struct cx88_core *core = dev->core; unsigned long flags; - int err; - if (dev->state.disabled) { - err = pci_enable_device(pci_dev); - if (err) { - pr_err("can't enable device\n"); - return err; - } - - dev->state.disabled = 0; - } - err = pci_set_power_state(pci_dev, PCI_D0); - if (err) { - pr_err("can't set power state\n"); - pci_disable_device(pci_dev); - dev->state.disabled = 1; - - return err; - } - pci_restore_state(pci_dev); + dev->state.disabled = 0; /* FIXME: re-initialize hardware */ cx88_reset(core); @@ -1631,7 +1605,6 @@ static int cx8800_resume(struct pci_dev *pci_dev) return 0; } -#endif /* ----------------------------------------------------------- */ @@ -1647,15 +1620,14 @@ static const struct pci_device_id cx8800_pci_tbl[] = { }; MODULE_DEVICE_TABLE(pci, cx8800_pci_tbl); +static SIMPLE_DEV_PM_OPS(cx8800_pm_ops, cx8800_suspend, cx8800_resume); + static struct pci_driver cx8800_pci_driver = { - .name = "cx8800", - .id_table = cx8800_pci_tbl, - .probe = cx8800_initdev, - .remove = cx8800_finidev, -#ifdef CONFIG_PM - .suspend = cx8800_suspend, - .resume = cx8800_resume, -#endif + .name = "cx8800", + .id_table = cx8800_pci_tbl, + .probe = cx8800_initdev, + .remove = cx8800_finidev, + .driver.pm = &cx8800_pm_ops, }; module_pci_driver(cx8800_pci_driver); diff --git a/drivers/media/pci/dt3155/dt3155.c b/drivers/media/pci/dt3155/dt3155.c index 82581aa5a2a3..ef8d5c9cfffe 100644 --- a/drivers/media/pci/dt3155/dt3155.c +++ b/drivers/media/pci/dt3155/dt3155.c @@ -426,7 +426,7 @@ static int dt3155_init_board(struct dt3155_priv *pd) iowrite32(FIFO_EN | SRST, pd->regs + CSR1); iowrite32(0xEEEEEE01, pd->regs + EVEN_PIXEL_FMT); iowrite32(0xEEEEEE01, pd->regs + ODD_PIXEL_FMT); - iowrite32(0x00000020, pd->regs + FIFO_TRIGER); + iowrite32(0x00000020, pd->regs + FIFO_TRIGGER); iowrite32(0x00000103, pd->regs + XFER_MODE); iowrite32(0, pd->regs + RETRY_WAIT_CNT); iowrite32(0, pd->regs + INT_CSR); diff --git a/drivers/media/pci/dt3155/dt3155.h b/drivers/media/pci/dt3155/dt3155.h index 43d32b8d5feb..c9ce79cb5566 100644 --- a/drivers/media/pci/dt3155/dt3155.h +++ b/drivers/media/pci/dt3155/dt3155.h @@ -31,7 +31,7 @@ #define ODD_DMA_STRIDE 0x24 #define EVEN_PIXEL_FMT 0x30 #define ODD_PIXEL_FMT 0x34 -#define FIFO_TRIGER 0x38 +#define FIFO_TRIGGER 0x38 #define XFER_MODE 0x3C #define CSR1 0x40 #define RETRY_WAIT_CNT 0x44 diff --git a/drivers/media/pci/ivtv/ivtv-cards.c b/drivers/media/pci/ivtv/ivtv-cards.c index ca6daba3a34a..c8f4ed7ff2c6 100644 --- a/drivers/media/pci/ivtv/ivtv-cards.c +++ b/drivers/media/pci/ivtv/ivtv-cards.c @@ -53,7 +53,7 @@ static struct ivtv_card_tuner_i2c ivtv_i2c_tda8290 = { /********************** card configuration *******************************/ -/* Please add new PCI IDs to: http://pci-ids.ucw.cz/ +/* Please add new PCI IDs to: https://pci-ids.ucw.cz/ This keeps the PCI ID database up to date. Note that the entries must be added under vendor 0x4444 (Conexant) as subsystem IDs. New vendor IDs should still be added to the vendor ID list. */ diff --git a/drivers/media/pci/meye/meye.c b/drivers/media/pci/meye/meye.c index 73e064e6f56d..7fb3b1853b87 100644 --- a/drivers/media/pci/meye/meye.c +++ b/drivers/media/pci/meye/meye.c @@ -1528,19 +1528,16 @@ static const struct v4l2_ctrl_ops meye_ctrl_ops = { .s_ctrl = meye_s_ctrl, }; -#ifdef CONFIG_PM -static int meye_suspend(struct pci_dev *pdev, pm_message_t state) +static int __maybe_unused meye_suspend(struct device *dev) { - pci_save_state(pdev); meye.pm_mchip_mode = meye.mchip_mode; mchip_hic_stop(); mchip_set(MCHIP_MM_INTA, 0x0); return 0; } -static int meye_resume(struct pci_dev *pdev) +static int __maybe_unused meye_resume(struct device *dev) { - pci_restore_state(pdev); pci_write_config_word(meye.mchip_dev, MCHIP_PCI_SOFTRESET_SET, 1); mchip_delay(MCHIP_HIC_CMD, 0); @@ -1562,7 +1559,6 @@ static int meye_resume(struct pci_dev *pdev) } return 0; } -#endif static int meye_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) { @@ -1788,15 +1784,14 @@ static const struct pci_device_id meye_pci_tbl[] = { MODULE_DEVICE_TABLE(pci, meye_pci_tbl); +static SIMPLE_DEV_PM_OPS(meye_pm_ops, meye_suspend, meye_resume); + static struct pci_driver meye_driver = { .name = "meye", .id_table = meye_pci_tbl, .probe = meye_probe, .remove = meye_remove, -#ifdef CONFIG_PM - .suspend = meye_suspend, - .resume = meye_resume, -#endif + .driver.pm = &meye_pm_ops, }; static int __init meye_init(void) diff --git a/drivers/media/pci/meye/meye.h b/drivers/media/pci/meye/meye.h index c957d9b55f89..5fa6552cf93d 100644 --- a/drivers/media/pci/meye/meye.h +++ b/drivers/media/pci/meye/meye.h @@ -305,9 +305,7 @@ struct meye { u16 colour; struct meye_params params; /* additional parameters */ unsigned long in_use; /* set to 1 if the device is in use */ -#ifdef CONFIG_PM u8 pm_mchip_mode; /* old mchip mode */ -#endif }; #endif diff --git a/drivers/media/pci/saa7164/saa7164-core.c b/drivers/media/pci/saa7164/saa7164-core.c index 126d085be9a7..4b637891b79a 100644 --- a/drivers/media/pci/saa7164/saa7164-core.c +++ b/drivers/media/pci/saa7164/saa7164-core.c @@ -1539,9 +1539,6 @@ static struct pci_driver saa7164_pci_driver = { .id_table = saa7164_pci_tbl, .probe = saa7164_initdev, .remove = saa7164_finidev, - /* TODO */ - .suspend = NULL, - .resume = NULL, }; static int __init saa7164_init(void) diff --git a/drivers/media/pci/solo6x10/solo6x10-core.c b/drivers/media/pci/solo6x10/solo6x10-core.c index 6e1ba4846ea4..c6e0090f27e8 100644 --- a/drivers/media/pci/solo6x10/solo6x10-core.c +++ b/drivers/media/pci/solo6x10/solo6x10-core.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> + * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> * * Original author: * Ben Collins <bcollins@ubuntu.com> diff --git a/drivers/media/pci/solo6x10/solo6x10-disp.c b/drivers/media/pci/solo6x10/solo6x10-disp.c index 4e28bf927de5..ad98ca7fb98b 100644 --- a/drivers/media/pci/solo6x10/solo6x10-disp.c +++ b/drivers/media/pci/solo6x10/solo6x10-disp.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> + * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> * * Original author: * Ben Collins <bcollins@ubuntu.com> diff --git a/drivers/media/pci/solo6x10/solo6x10-eeprom.c b/drivers/media/pci/solo6x10/solo6x10-eeprom.c index 9aba64395a6b..0d864b8ca0ab 100644 --- a/drivers/media/pci/solo6x10/solo6x10-eeprom.c +++ b/drivers/media/pci/solo6x10/solo6x10-eeprom.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> + * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> * * Original author: * Ben Collins <bcollins@ubuntu.com> diff --git a/drivers/media/pci/solo6x10/solo6x10-enc.c b/drivers/media/pci/solo6x10/solo6x10-enc.c index a9c56897f7bc..14a1d51cfad4 100644 --- a/drivers/media/pci/solo6x10/solo6x10-enc.c +++ b/drivers/media/pci/solo6x10/solo6x10-enc.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> + * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> * * Original author: * Ben Collins <bcollins@ubuntu.com> diff --git a/drivers/media/pci/solo6x10/solo6x10-g723.c b/drivers/media/pci/solo6x10/solo6x10-g723.c index d6d16e8fd997..906ce86437ae 100644 --- a/drivers/media/pci/solo6x10/solo6x10-g723.c +++ b/drivers/media/pci/solo6x10/solo6x10-g723.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> + * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> * * Original author: * Ben Collins <bcollins@ubuntu.com> diff --git a/drivers/media/pci/solo6x10/solo6x10-gpio.c b/drivers/media/pci/solo6x10/solo6x10-gpio.c index 526d67cf9942..084c30760e45 100644 --- a/drivers/media/pci/solo6x10/solo6x10-gpio.c +++ b/drivers/media/pci/solo6x10/solo6x10-gpio.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> + * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> * * Original author: * Ben Collins <bcollins@ubuntu.com> diff --git a/drivers/media/pci/solo6x10/solo6x10-i2c.c b/drivers/media/pci/solo6x10/solo6x10-i2c.c index df1e3f2e9c05..f86f12fa6350 100644 --- a/drivers/media/pci/solo6x10/solo6x10-i2c.c +++ b/drivers/media/pci/solo6x10/solo6x10-i2c.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> + * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> * * Original author: * Ben Collins <bcollins@ubuntu.com> diff --git a/drivers/media/pci/solo6x10/solo6x10-jpeg.h b/drivers/media/pci/solo6x10/solo6x10-jpeg.h index e35aad16ad33..e212f4828c09 100644 --- a/drivers/media/pci/solo6x10/solo6x10-jpeg.h +++ b/drivers/media/pci/solo6x10/solo6x10-jpeg.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> + * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> * * Original author: * Ben Collins <bcollins@ubuntu.com> diff --git a/drivers/media/pci/solo6x10/solo6x10-offsets.h b/drivers/media/pci/solo6x10/solo6x10-offsets.h index e3ae6a02dbb9..f414ee1316f2 100644 --- a/drivers/media/pci/solo6x10/solo6x10-offsets.h +++ b/drivers/media/pci/solo6x10/solo6x10-offsets.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> + * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> * * Original author: * Ben Collins <bcollins@ubuntu.com> diff --git a/drivers/media/pci/solo6x10/solo6x10-p2m.c b/drivers/media/pci/solo6x10/solo6x10-p2m.c index e2816e2629f8..db2afc6a5fcb 100644 --- a/drivers/media/pci/solo6x10/solo6x10-p2m.c +++ b/drivers/media/pci/solo6x10/solo6x10-p2m.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> + * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> * * Original author: * Ben Collins <bcollins@ubuntu.com> diff --git a/drivers/media/pci/solo6x10/solo6x10-regs.h b/drivers/media/pci/solo6x10/solo6x10-regs.h index 804505d01b25..12e0ac190416 100644 --- a/drivers/media/pci/solo6x10/solo6x10-regs.h +++ b/drivers/media/pci/solo6x10/solo6x10-regs.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> + * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> * * Original author: * Ben Collins <bcollins@ubuntu.com> diff --git a/drivers/media/pci/solo6x10/solo6x10-tw28.c b/drivers/media/pci/solo6x10/solo6x10-tw28.c index 126cd1b01266..1b7c22a9bc94 100644 --- a/drivers/media/pci/solo6x10/solo6x10-tw28.c +++ b/drivers/media/pci/solo6x10/solo6x10-tw28.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> + * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> * * Original author: * Ben Collins <bcollins@ubuntu.com> diff --git a/drivers/media/pci/solo6x10/solo6x10-tw28.h b/drivers/media/pci/solo6x10/solo6x10-tw28.h index edbad194d31e..4a8ede3139a8 100644 --- a/drivers/media/pci/solo6x10/solo6x10-tw28.h +++ b/drivers/media/pci/solo6x10/solo6x10-tw28.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> + * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> * * Original author: * Ben Collins <bcollins@ubuntu.com> diff --git a/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c b/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c index cbf85231b708..3cf7bd6186aa 100644 --- a/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c +++ b/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> + * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> * * Original author: * Ben Collins <bcollins@ubuntu.com> diff --git a/drivers/media/pci/solo6x10/solo6x10-v4l2.c b/drivers/media/pci/solo6x10/solo6x10-v4l2.c index 54434f3c428d..24ef0c446bef 100644 --- a/drivers/media/pci/solo6x10/solo6x10-v4l2.c +++ b/drivers/media/pci/solo6x10/solo6x10-v4l2.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> + * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> * * Original author: * Ben Collins <bcollins@ubuntu.com> diff --git a/drivers/media/pci/solo6x10/solo6x10.h b/drivers/media/pci/solo6x10/solo6x10.h index 9f2314688cec..126f6fb7b755 100644 --- a/drivers/media/pci/solo6x10/solo6x10.h +++ b/drivers/media/pci/solo6x10/solo6x10.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> + * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> * * Original author: * Ben Collins <bcollins@ubuntu.com> diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c index 798574cfad35..0fdb0fd6e764 100644 --- a/drivers/media/pci/sta2x11/sta2x11_vip.c +++ b/drivers/media/pci/sta2x11/sta2x11_vip.c @@ -1167,21 +1167,18 @@ static void sta2x11_vip_remove_one(struct pci_dev *pdev) */ } -#ifdef CONFIG_PM - /** * sta2x11_vip_suspend - set device into power save mode - * @pdev: PCI device - * @state: new state of device + * @dev_d: PCI device * * all relevant registers are saved and an attempt to set a new state is made. * * return value: 0 always indicate success, * even if device could not be disabled. (workaround for hardware problem) */ -static int sta2x11_vip_suspend(struct pci_dev *pdev, pm_message_t state) +static int __maybe_unused sta2x11_vip_suspend(struct device *dev_d) { - struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev_d); struct sta2x11_vip *vip = container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev); unsigned long flags; @@ -1198,15 +1195,8 @@ static int sta2x11_vip_suspend(struct pci_dev *pdev, pm_message_t state) vip->register_save_area[SAVE_COUNT + IRQ_COUNT + i] = reg_read(vip, registers_to_save[i]); spin_unlock_irqrestore(&vip->slock, flags); - /* save pci state */ - pci_save_state(pdev); - if (pci_set_power_state(pdev, pci_choose_state(pdev, state))) { - /* - * do not call pci_disable_device on sta2x11 because it - * break all other Bus masters on this EP - */ - vip->disabled = 1; - } + + vip->disabled = 1; pr_info("VIP: suspend\n"); return 0; @@ -1214,45 +1204,23 @@ static int sta2x11_vip_suspend(struct pci_dev *pdev, pm_message_t state) /** * sta2x11_vip_resume - resume device operation - * @pdev : PCI device - * - * re-enable device, set PCI state to powered and restore registers. - * resume normal device operation afterwards. + * @dev_d : PCI device * * return value: 0, no error. * * other, could not set device to power on state. */ -static int sta2x11_vip_resume(struct pci_dev *pdev) +static int __maybe_unused sta2x11_vip_resume(struct device *dev_d) { - struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev_d); struct sta2x11_vip *vip = container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev); unsigned long flags; - int ret, i; + int i; pr_info("VIP: resume\n"); - /* restore pci state */ - if (vip->disabled) { - ret = pci_enable_device(pdev); - if (ret) { - pr_warn("VIP: Can't enable device.\n"); - return ret; - } - vip->disabled = 0; - } - ret = pci_set_power_state(pdev, PCI_D0); - if (ret) { - /* - * do not call pci_disable_device on sta2x11 because it - * break all other Bus masters on this EP - */ - pr_warn("VIP: Can't enable device.\n"); - vip->disabled = 1; - return ret; - } - pci_restore_state(pdev); + vip->disabled = 0; spin_lock_irqsave(&vip->slock, flags); for (i = 1; i < SAVE_COUNT; i++) @@ -1266,22 +1234,21 @@ static int sta2x11_vip_resume(struct pci_dev *pdev) return 0; } -#endif - static const struct pci_device_id sta2x11_vip_pci_tbl[] = { {PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_VIP)}, {0,} }; +static SIMPLE_DEV_PM_OPS(sta2x11_vip_pm_ops, + sta2x11_vip_suspend, + sta2x11_vip_resume); + static struct pci_driver sta2x11_vip_driver = { .name = KBUILD_MODNAME, .probe = sta2x11_vip_init_one, .remove = sta2x11_vip_remove_one, .id_table = sta2x11_vip_pci_tbl, -#ifdef CONFIG_PM - .suspend = sta2x11_vip_suspend, - .resume = sta2x11_vip_resume, -#endif + .driver.pm = &sta2x11_vip_pm_ops, }; static int __init sta2x11_vip_init_module(void) diff --git a/drivers/media/pci/ttpci/av7110.c b/drivers/media/pci/ttpci/av7110.c index d0cdee1c6eb0..bf36b1e22b63 100644 --- a/drivers/media/pci/ttpci/av7110.c +++ b/drivers/media/pci/ttpci/av7110.c @@ -406,14 +406,15 @@ static void debiirq(unsigned long cookie) case DATA_CI_GET: { u8 *data = av7110->debi_virt; + u8 data_0 = data[0]; - if ((data[0] < 2) && data[2] == 0xff) { + if (data_0 < 2 && data[2] == 0xff) { int flags = 0; if (data[5] > 0) flags |= CA_CI_MODULE_PRESENT; if (data[5] > 5) flags |= CA_CI_MODULE_READY; - av7110->ci_slot[data[0]].flags = flags; + av7110->ci_slot[data_0].flags = flags; } else ci_get_data(&av7110->ci_rbuffer, av7110->debi_virt, diff --git a/drivers/media/pci/ttpci/budget-core.c b/drivers/media/pci/ttpci/budget-core.c index fadbdeeb4495..293867b9e796 100644 --- a/drivers/media/pci/ttpci/budget-core.c +++ b/drivers/media/pci/ttpci/budget-core.c @@ -369,20 +369,25 @@ static int budget_register(struct budget *budget) ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &budget->hw_frontend); if (ret < 0) - return ret; + goto err_release_dmx; budget->mem_frontend.source = DMX_MEMORY_FE; ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &budget->mem_frontend); if (ret < 0) - return ret; + goto err_release_dmx; ret = dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, &budget->hw_frontend); if (ret < 0) - return ret; + goto err_release_dmx; dvb_net_init(&budget->dvb_adapter, &budget->dvb_net, &dvbdemux->dmx); return 0; + +err_release_dmx: + dvb_dmxdev_release(&budget->dmxdev); + dvb_dmx_release(&budget->demux); + return ret; } static void budget_unregister(struct budget *budget) diff --git a/drivers/media/pci/tw5864/tw5864-core.c b/drivers/media/pci/tw5864/tw5864-core.c index 66f2a5cabc6e..282f7dfb7aaf 100644 --- a/drivers/media/pci/tw5864/tw5864-core.c +++ b/drivers/media/pci/tw5864/tw5864-core.c @@ -65,7 +65,7 @@ module_param_array(video_nr, int, NULL, 0444); MODULE_PARM_DESC(video_nr, "video devices numbers array"); /* - * Please add any new PCI IDs to: http://pci-ids.ucw.cz. This keeps + * Please add any new PCI IDs to: https://pci-ids.ucw.cz. This keeps * the PCI ID database up to date. Note that the entries must be * added under vendor 0x1797 (Techwell Inc.) as subsystem IDs. */ diff --git a/drivers/media/pci/tw68/tw68-core.c b/drivers/media/pci/tw68/tw68-core.c index b45f3ffa3de5..bf15fa7c0ea1 100644 --- a/drivers/media/pci/tw68/tw68-core.c +++ b/drivers/media/pci/tw68/tw68-core.c @@ -57,7 +57,7 @@ static atomic_t tw68_instance = ATOMIC_INIT(0); /* ------------------------------------------------------------------ */ /* - * Please add any new PCI IDs to: http://pci-ids.ucw.cz. This keeps + * Please add any new PCI IDs to: https://pci-ids.ucw.cz. This keeps * the PCI ID database up to date. Note that the entries must be * added under vendor 0x1797 (Techwell Inc.) as subsystem IDs. */ @@ -359,10 +359,9 @@ static void tw68_finidev(struct pci_dev *pci_dev) v4l2_device_unregister(&dev->v4l2_dev); } -#ifdef CONFIG_PM - -static int tw68_suspend(struct pci_dev *pci_dev , pm_message_t state) +static int __maybe_unused tw68_suspend(struct device *dev_d) { + struct pci_dev *pci_dev = to_pci_dev(dev_d); struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); struct tw68_dev *dev = container_of(v4l2_dev, struct tw68_dev, v4l2_dev); @@ -373,24 +372,19 @@ static int tw68_suspend(struct pci_dev *pci_dev , pm_message_t state) synchronize_irq(pci_dev->irq); - pci_save_state(pci_dev); - pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state)); vb2_discard_done(&dev->vidq); return 0; } -static int tw68_resume(struct pci_dev *pci_dev) +static int __maybe_unused tw68_resume(struct device *dev_d) { - struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev_d); struct tw68_dev *dev = container_of(v4l2_dev, struct tw68_dev, v4l2_dev); struct tw68_buf *buf; unsigned long flags; - pci_set_power_state(pci_dev, PCI_D0); - pci_restore_state(pci_dev); - /* Do things that are done in tw68_initdev , except of initializing memory structures.*/ @@ -408,19 +402,17 @@ static int tw68_resume(struct pci_dev *pci_dev) return 0; } -#endif /* ----------------------------------------------------------- */ +static SIMPLE_DEV_PM_OPS(tw68_pm_ops, tw68_suspend, tw68_resume); + static struct pci_driver tw68_pci_driver = { - .name = "tw68", - .id_table = tw68_pci_tbl, - .probe = tw68_initdev, - .remove = tw68_finidev, -#ifdef CONFIG_PM - .suspend = tw68_suspend, - .resume = tw68_resume -#endif + .name = "tw68", + .id_table = tw68_pci_tbl, + .probe = tw68_initdev, + .remove = tw68_finidev, + .driver.pm = &tw68_pm_ops, }; module_pci_driver(tw68_pci_driver); diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index 66079cc41f38..0fb9f9ba1219 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -26,6 +26,7 @@ #include <media/v4l2-ctrls.h> #include <media/v4l2-event.h> #include <media/v4l2-fwnode.h> +#include <media/v4l2-rect.h> #include "am437x-vpfe.h" @@ -1987,20 +1988,6 @@ vpfe_g_selection(struct file *file, void *fh, struct v4l2_selection *s) return 0; } -static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b) -{ - if (a->left < b->left || a->top < b->top) - return 0; - - if (a->left + a->width > b->left + b->width) - return 0; - - if (a->top + a->height > b->top + b->height) - return 0; - - return 1; -} - static int vpfe_s_selection(struct file *file, void *fh, struct v4l2_selection *s) { @@ -2025,10 +2012,10 @@ vpfe_s_selection(struct file *file, void *fh, struct v4l2_selection *s) r.left = clamp_t(unsigned int, r.left, 0, cr.width - r.width); r.top = clamp_t(unsigned int, r.top, 0, cr.height - r.height); - if (s->flags & V4L2_SEL_FLAG_LE && !enclosed_rectangle(&r, &s->r)) + if (s->flags & V4L2_SEL_FLAG_LE && !v4l2_rect_enclosed(&r, &s->r)) return -ERANGE; - if (s->flags & V4L2_SEL_FLAG_GE && !enclosed_rectangle(&s->r, &r)) + if (s->flags & V4L2_SEL_FLAG_GE && !v4l2_rect_enclosed(&s->r, &r)) return -ERANGE; s->r = vpfe->crop = r; diff --git a/drivers/media/platform/atmel/atmel-isc-base.c b/drivers/media/platform/atmel/atmel-isc-base.c index a6e9797a0ec9..fe3ec8d0eaee 100644 --- a/drivers/media/platform/atmel/atmel-isc-base.c +++ b/drivers/media/platform/atmel/atmel-isc-base.c @@ -225,9 +225,6 @@ const u32 isc_gamma_table[GAMMA_MAX + 1][GAMMA_ENTRIES] = { (((mbus_code) == MEDIA_BUS_FMT_Y10_1X10) | \ (((mbus_code) == MEDIA_BUS_FMT_Y8_1X8))) -#define ISC_CTRL_ISC_TO_V4L2(x) ((x) == ISC_WB_O_ZERO_VAL ? 0 : (x)) -#define ISC_CTRL_V4L2_TO_ISC(x) ((x) ? (x) : ISC_WB_O_ZERO_VAL) - static inline void isc_update_v4l2_ctrls(struct isc_device *isc) { struct isc_ctrls *ctrls = &isc->ctrls; @@ -238,14 +235,10 @@ static inline void isc_update_v4l2_ctrls(struct isc_device *isc) v4l2_ctrl_s_ctrl(isc->gr_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GR]); v4l2_ctrl_s_ctrl(isc->gb_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GB]); - v4l2_ctrl_s_ctrl(isc->r_off_ctrl, - ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_R])); - v4l2_ctrl_s_ctrl(isc->b_off_ctrl, - ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_B])); - v4l2_ctrl_s_ctrl(isc->gr_off_ctrl, - ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_GR])); - v4l2_ctrl_s_ctrl(isc->gb_off_ctrl, - ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_GB])); + 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) @@ -255,11 +248,11 @@ static inline void isc_update_awb_ctrls(struct isc_device *isc) /* In here we set our actual hw pipeline config */ regmap_write(isc->regmap, ISC_WB_O_RGR, - (ISC_WB_O_ZERO_VAL - (ctrls->offset[ISC_HIS_CFG_MODE_R])) | - ((ISC_WB_O_ZERO_VAL - ctrls->offset[ISC_HIS_CFG_MODE_GR]) << 16)); + ((ctrls->offset[ISC_HIS_CFG_MODE_R])) | + ((ctrls->offset[ISC_HIS_CFG_MODE_GR]) << 16)); regmap_write(isc->regmap, ISC_WB_O_BGB, - (ISC_WB_O_ZERO_VAL - (ctrls->offset[ISC_HIS_CFG_MODE_B])) | - ((ISC_WB_O_ZERO_VAL - ctrls->offset[ISC_HIS_CFG_MODE_GB]) << 16)); + ((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)); @@ -275,12 +268,8 @@ static inline void isc_reset_awb_ctrls(struct isc_device *isc) 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, the value - * will be substracted from ISC_WB_O_ZERO_VAL to obtain - * 2's complement of a value between 0 and - * ISC_WB_O_ZERO_VAL >> 1 - */ - isc->ctrls.offset[c] = ISC_WB_O_ZERO_VAL; + /* offsets are in 2's complements */ + isc->ctrls.offset[c] = 0; } } @@ -1774,9 +1763,12 @@ static void isc_wb_update(struct isc_ctrls *ctrls) */ ctrls->offset[c] = (offset[c] - 1) << 3; - /* the offset is then taken and converted to 2's complements */ - if (!ctrls->offset[c]) - ctrls->offset[c] = ISC_WB_O_ZERO_VAL; + /* + * 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 @@ -1938,17 +1930,13 @@ static int isc_s_awb_ctrl(struct v4l2_ctrl *ctrl) ctrls->gain[ISC_HIS_CFG_MODE_GB] = isc->gb_gain_ctrl->val; if (ctrl->cluster[ISC_CTRL_R_OFF]->is_new) - ctrls->offset[ISC_HIS_CFG_MODE_R] = - ISC_CTRL_V4L2_TO_ISC(isc->r_off_ctrl->val); + 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_CTRL_V4L2_TO_ISC(isc->b_off_ctrl->val); + 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_CTRL_V4L2_TO_ISC(isc->gr_off_ctrl->val); + 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_CTRL_V4L2_TO_ISC(isc->gb_off_ctrl->val); + ctrls->offset[ISC_HIS_CFG_MODE_GB] = isc->gb_off_ctrl->val; isc_update_awb_ctrls(isc); @@ -2010,13 +1998,13 @@ static int isc_g_volatile_awb_ctrl(struct v4l2_ctrl *ctrl) ctrls->gain[ISC_HIS_CFG_MODE_GB]; ctrl->cluster[ISC_CTRL_R_OFF]->val = - ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_R]); + ctrls->offset[ISC_HIS_CFG_MODE_R]; ctrl->cluster[ISC_CTRL_B_OFF]->val = - ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_B]); + ctrls->offset[ISC_HIS_CFG_MODE_B]; ctrl->cluster[ISC_CTRL_GR_OFF]->val = - ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_GR]); + ctrls->offset[ISC_HIS_CFG_MODE_GR]; ctrl->cluster[ISC_CTRL_GB_OFF]->val = - ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_GB]); + ctrls->offset[ISC_HIS_CFG_MODE_GB]; break; } return 0; diff --git a/drivers/media/platform/atmel/atmel-isc-regs.h b/drivers/media/platform/atmel/atmel-isc-regs.h index c1283fb21bf6..f1e160ed4351 100644 --- a/drivers/media/platform/atmel/atmel-isc-regs.h +++ b/drivers/media/platform/atmel/atmel-isc-regs.h @@ -108,8 +108,6 @@ /* ISC White Balance Gain for B, GB Register */ #define ISC_WB_G_BGB 0x0000006c -#define ISC_WB_O_ZERO_VAL (1 << 13) - /* ISC Color Filter Array Control Register */ #define ISC_CFA_CTRL 0x00000070 diff --git a/drivers/media/platform/atmel/atmel-isc.h b/drivers/media/platform/atmel/atmel-isc.h index fc56a745c7d1..24b784b893d6 100644 --- a/drivers/media/platform/atmel/atmel-isc.h +++ b/drivers/media/platform/atmel/atmel-isc.h @@ -133,7 +133,7 @@ struct isc_ctrls { /* one for each component : GR, R, GB, B */ u32 gain[HIST_BAYER]; - u32 offset[HIST_BAYER]; + s32 offset[HIST_BAYER]; u32 hist_entry[HIST_ENTRIES]; u32 hist_count[HIST_BAYER]; diff --git a/drivers/media/platform/atmel/atmel-sama5d2-isc.c b/drivers/media/platform/atmel/atmel-sama5d2-isc.c index 78381651238d..a3304f49e499 100644 --- a/drivers/media/platform/atmel/atmel-sama5d2-isc.c +++ b/drivers/media/platform/atmel/atmel-sama5d2-isc.c @@ -321,11 +321,13 @@ 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, diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index 6f41f74d492c..3ab3d976d8ca 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -2653,7 +2653,7 @@ static int coda_open(struct file *file) ret = clk_prepare_enable(dev->clk_per); if (ret) - goto err_clk_per; + goto err_pm_get; ret = clk_prepare_enable(dev->clk_ahb); if (ret) @@ -2692,9 +2692,8 @@ err_ctx_init: clk_disable_unprepare(dev->clk_ahb); err_clk_ahb: clk_disable_unprepare(dev->clk_per); -err_clk_per: - pm_runtime_put_sync(dev->dev); err_pm_get: + pm_runtime_put_sync(dev->dev); v4l2_fh_del(&ctx->fh); v4l2_fh_exit(&ctx->fh); err_coda_name_init: diff --git a/drivers/media/platform/coda/coda-jpeg.c b/drivers/media/platform/coda/coda-jpeg.c index 00d19859db50..b11cfbe166dd 100644 --- a/drivers/media/platform/coda/coda-jpeg.c +++ b/drivers/media/platform/coda/coda-jpeg.c @@ -327,8 +327,11 @@ int coda_jpeg_decode_header(struct coda_ctx *ctx, struct vb2_buffer *vb) "only 8-bit quantization tables supported\n"); continue; } - if (!ctx->params.jpeg_qmat_tab[i]) + if (!ctx->params.jpeg_qmat_tab[i]) { ctx->params.jpeg_qmat_tab[i] = kmalloc(64, GFP_KERNEL); + if (!ctx->params.jpeg_qmat_tab[i]) + return -ENOMEM; + } memcpy(ctx->params.jpeg_qmat_tab[i], quantization_tables[i].start, 64); } diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index 7ab13eb7527d..d19bad997f30 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2010 Texas Instruments Incorporated - https://www.ti.com/ */ #include <linux/kernel.h> #include <linux/init.h> diff --git a/drivers/media/platform/davinci/vpif.c b/drivers/media/platform/davinci/vpif.c index df66461f5d4f..5e67994e62cc 100644 --- a/drivers/media/platform/davinci/vpif.c +++ b/drivers/media/platform/davinci/vpif.c @@ -5,7 +5,7 @@ * The hardware supports SDTV, HDTV formats, raw data capture. * Currently, the driver supports NTSC and PAL standards. * - * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2009 Texas Instruments Incorporated - https://www.ti.com/ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -458,6 +458,7 @@ static int vpif_probe(struct platform_device *pdev) res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res_irq) { dev_warn(&pdev->dev, "Missing IRQ resource.\n"); + pm_runtime_put(&pdev->dev); return -EINVAL; } diff --git a/drivers/media/platform/davinci/vpif.h b/drivers/media/platform/davinci/vpif.h index 2466c7c77deb..c6d1d890478a 100644 --- a/drivers/media/platform/davinci/vpif.h +++ b/drivers/media/platform/davinci/vpif.h @@ -1,7 +1,7 @@ /* * VPIF header file * - * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2009 Texas Instruments Incorporated - https://www.ti.com/ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index d9ec439faefa..72a0e94e2e21 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -1482,8 +1482,6 @@ probe_out: /* Unregister video device */ video_unregister_device(&ch->video_dev); } - kfree(vpif_obj.sd); - v4l2_device_unregister(&vpif_obj.v4l2_dev); return err; } diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index 7d55fd45240e..46afc029138f 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -2,7 +2,7 @@ * vpif-display - VPIF display driver * Display driver for TI DaVinci VPIF * - * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2009 Texas Instruments Incorporated - https://www.ti.com/ * Copyright (C) 2014 Lad, Prabhakar <prabhakar.csengg@gmail.com> * * This program is free software; you can redistribute it and/or diff --git a/drivers/media/platform/davinci/vpif_display.h b/drivers/media/platform/davinci/vpif_display.h index af2765fdcea8..f731a65eefd6 100644 --- a/drivers/media/platform/davinci/vpif_display.h +++ b/drivers/media/platform/davinci/vpif_display.h @@ -1,7 +1,7 @@ /* * VPIF display header file * - * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2009 Texas Instruments Incorporated - https://www.ti.com/ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as diff --git a/drivers/media/platform/davinci/vpss.c b/drivers/media/platform/davinci/vpss.c index d38d2bbb6f0f..7000f0bf0b35 100644 --- a/drivers/media/platform/davinci/vpss.c +++ b/drivers/media/platform/davinci/vpss.c @@ -505,19 +505,31 @@ static void vpss_exit(void) static int __init vpss_init(void) { + int ret; + if (!request_mem_region(VPSS_CLK_CTRL, 4, "vpss_clock_control")) return -EBUSY; oper_cfg.vpss_regs_base2 = ioremap(VPSS_CLK_CTRL, 4); if (unlikely(!oper_cfg.vpss_regs_base2)) { - release_mem_region(VPSS_CLK_CTRL, 4); - return -ENOMEM; + ret = -ENOMEM; + goto err_ioremap; } writel(VPSS_CLK_CTRL_VENCCLKEN | - VPSS_CLK_CTRL_DACCLKEN, oper_cfg.vpss_regs_base2); + VPSS_CLK_CTRL_DACCLKEN, oper_cfg.vpss_regs_base2); + + ret = platform_driver_register(&vpss_driver); + if (ret) + goto err_pd_register; + + return 0; - return platform_driver_register(&vpss_driver); +err_pd_register: + iounmap(oper_cfg.vpss_regs_base2); +err_ioremap: + release_mem_region(VPSS_CLK_CTRL, 4); + return ret; } subsys_initcall(vpss_init); module_exit(vpss_exit); diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index f6650b45bc3d..9f41c2e7097a 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -577,7 +577,7 @@ int gsc_try_selection(struct gsc_ctx *ctx, struct v4l2_selection *s) v4l_bound_align_image(&tmp_w, min_w, max_w, mod_x, &tmp_h, min_h, max_h, mod_y, 0); - if (!V4L2_TYPE_IS_OUTPUT(s->type) && + if (V4L2_TYPE_IS_CAPTURE(s->type) && (ctx->gsc_ctrls.rotate->val == 90 || ctx->gsc_ctrls.rotate->val == 270)) gsc_check_crop_change(tmp_h, tmp_w, diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index e2c162635f72..27a3c92c73bc 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -255,7 +255,7 @@ static int gsc_m2m_buf_prepare(struct vb2_buffer *vb) if (IS_ERR(frame)) return PTR_ERR(frame); - if (!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { + if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) { for (i = 0; i < frame->fmt->num_planes; i++) vb2_set_plane_payload(vb, i, frame->payload[i]); } diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c index 705f182330ca..e7a4b06e6dfe 100644 --- a/drivers/media/platform/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/exynos4-is/fimc-capture.c @@ -21,6 +21,7 @@ #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-mem2mem.h> +#include <media/v4l2-rect.h> #include <media/videobuf2-v4l2.h> #include <media/videobuf2-dma-contig.h> @@ -478,8 +479,10 @@ static int fimc_capture_open(struct file *file) set_bit(ST_CAPT_BUSY, &fimc->state); ret = pm_runtime_get_sync(&fimc->pdev->dev); - if (ret < 0) + if (ret < 0) { + pm_runtime_put_sync(&fimc->pdev->dev); goto unlock; + } ret = v4l2_fh_open(file); if (ret) { @@ -1299,19 +1302,6 @@ static int fimc_cap_g_selection(struct file *file, void *fh, return -EINVAL; } -/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */ -static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b) -{ - if (a->left < b->left || a->top < b->top) - return 0; - if (a->left + a->width > b->left + b->width) - return 0; - if (a->top + a->height > b->top + b->height) - return 0; - - return 1; -} - static int fimc_cap_s_selection(struct file *file, void *fh, struct v4l2_selection *s) { @@ -1334,11 +1324,11 @@ static int fimc_cap_s_selection(struct file *file, void *fh, fimc_capture_try_selection(ctx, &rect, s->target); if (s->flags & V4L2_SEL_FLAG_LE && - !enclosed_rectangle(&rect, &s->r)) + !v4l2_rect_enclosed(&rect, &s->r)) return -ERANGE; if (s->flags & V4L2_SEL_FLAG_GE && - !enclosed_rectangle(&s->r, &rect)) + !v4l2_rect_enclosed(&s->r, &rect)) return -ERANGE; s->r = rect; diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c index 64148b7e0d98..a474014f0a0f 100644 --- a/drivers/media/platform/exynos4-is/fimc-is.c +++ b/drivers/media/platform/exynos4-is/fimc-is.c @@ -871,6 +871,7 @@ err_dfs: err_sd: fimc_is_unregister_subdevs(is); err_pm: + pm_runtime_put_noidle(dev); if (!pm_runtime_enabled(dev)) fimc_is_runtime_suspend(dev); err_irq: diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.c b/drivers/media/platform/exynos4-is/fimc-isp-video.c index 15f443fa7208..612b9872afc8 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp-video.c +++ b/drivers/media/platform/exynos4-is/fimc-isp-video.c @@ -293,6 +293,7 @@ static int isp_video_open(struct file *file) if (!ret) goto unlock; rel_fh: + pm_runtime_put_noidle(&isp->pdev->dev); v4l2_fh_release(file); unlock: mutex_unlock(&isp->video_lock); diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c index 394e0818f2d5..9c666f663ab4 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/exynos4-is/fimc-lite.c @@ -25,6 +25,7 @@ #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-mem2mem.h> +#include <media/v4l2-rect.h> #include <media/videobuf2-v4l2.h> #include <media/videobuf2-dma-contig.h> #include <media/drv-intf/exynos-fimc.h> @@ -868,19 +869,6 @@ static int fimc_lite_reqbufs(struct file *file, void *priv, return ret; } -/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */ -static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b) -{ - if (a->left < b->left || a->top < b->top) - return 0; - if (a->left + a->width > b->left + b->width) - return 0; - if (a->top + a->height > b->top + b->height) - return 0; - - return 1; -} - static int fimc_lite_g_selection(struct file *file, void *fh, struct v4l2_selection *sel) { @@ -922,11 +910,11 @@ static int fimc_lite_s_selection(struct file *file, void *fh, fimc_lite_try_compose(fimc, &rect); if ((sel->flags & V4L2_SEL_FLAG_LE) && - !enclosed_rectangle(&rect, &sel->r)) + !v4l2_rect_enclosed(&rect, &sel->r)) return -ERANGE; if ((sel->flags & V4L2_SEL_FLAG_GE) && - !enclosed_rectangle(&sel->r, &rect)) + !v4l2_rect_enclosed(&sel->r, &rect)) return -ERANGE; sel->r = rect; diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index 9aaf3b8060d5..16dd660137a8 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -1270,6 +1270,9 @@ static int fimc_md_get_pinctrl(struct fimc_md *fmd) pctl->state_idle = pinctrl_lookup_state(pctl->pinctrl, PINCTRL_STATE_IDLE); + if (IS_ERR(pctl->state_idle)) + return PTR_ERR(pctl->state_idle); + return 0; } @@ -1439,7 +1442,7 @@ static int fimc_md_probe(struct platform_device *pdev) INIT_LIST_HEAD(&fmd->pipelines); fmd->pdev = pdev; - strscpy(fmd->media_dev.model, "SAMSUNG S5P FIMC", + strscpy(fmd->media_dev.model, "Samsung S5P FIMC", sizeof(fmd->media_dev.model)); fmd->media_dev.ops = &fimc_md_ops; fmd->media_dev.dev = dev; diff --git a/drivers/media/platform/marvell-ccic/cafe-driver.c b/drivers/media/platform/marvell-ccic/cafe-driver.c index 9a09a10a3631..58b9915ac7a4 100644 --- a/drivers/media/platform/marvell-ccic/cafe-driver.c +++ b/drivers/media/platform/marvell-ccic/cafe-driver.c @@ -604,44 +604,28 @@ static void cafe_pci_remove(struct pci_dev *pdev) } -#ifdef CONFIG_PM /* * Basic power management. */ -static int cafe_pci_suspend(struct pci_dev *pdev, pm_message_t state) +static int __maybe_unused cafe_pci_suspend(struct device *dev) { - struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); struct cafe_camera *cam = to_cam(v4l2_dev); - int ret; - ret = pci_save_state(pdev); - if (ret) - return ret; mccic_suspend(&cam->mcam); - pci_disable_device(pdev); return 0; } -static int cafe_pci_resume(struct pci_dev *pdev) +static int __maybe_unused cafe_pci_resume(struct device *dev) { - struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); struct cafe_camera *cam = to_cam(v4l2_dev); - int ret = 0; - pci_restore_state(pdev); - ret = pci_enable_device(pdev); - - if (ret) { - cam_warn(cam, "Unable to re-enable device on resume!\n"); - return ret; - } cafe_ctlr_init(&cam->mcam); return mccic_resume(&cam->mcam); } -#endif /* CONFIG_PM */ - static const struct pci_device_id cafe_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_88ALP01_CCIC) }, @@ -650,15 +634,14 @@ static const struct pci_device_id cafe_ids[] = { MODULE_DEVICE_TABLE(pci, cafe_ids); +static SIMPLE_DEV_PM_OPS(cafe_pci_pm_ops, cafe_pci_suspend, cafe_pci_resume); + static struct pci_driver cafe_pci_driver = { .name = "cafe1000-ccic", .id_table = cafe_ids, .probe = cafe_pci_probe, .remove = cafe_pci_remove, -#ifdef CONFIG_PM - .suspend = cafe_pci_suspend, - .resume = cafe_pci_resume, -#endif + .driver.pm = &cafe_pci_pm_ops, }; diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index 09775b6624c6..3d4242b8182b 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -1940,6 +1940,7 @@ int mccic_register(struct mcam_camera *cam) out: v4l2_async_notifier_unregister(&cam->notifier); v4l2_device_unregister(&cam->v4l2_dev); + v4l2_async_notifier_cleanup(&cam->notifier); return ret; } EXPORT_SYMBOL_GPL(mccic_register); @@ -1961,14 +1962,13 @@ void mccic_shutdown(struct mcam_camera *cam) v4l2_ctrl_handler_free(&cam->ctrl_handler); v4l2_async_notifier_unregister(&cam->notifier); v4l2_device_unregister(&cam->v4l2_dev); + v4l2_async_notifier_cleanup(&cam->notifier); } EXPORT_SYMBOL_GPL(mccic_shutdown); /* * Power management */ -#ifdef CONFIG_PM - void mccic_suspend(struct mcam_camera *cam) { mutex_lock(&cam->s_mutex); @@ -2015,7 +2015,6 @@ int mccic_resume(struct mcam_camera *cam) return ret; } EXPORT_SYMBOL_GPL(mccic_resume); -#endif /* CONFIG_PM */ MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>"); diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h index 2e3a7567a76a..b55545822fd2 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.h +++ b/drivers/media/platform/marvell-ccic/mcam-core.h @@ -236,10 +236,8 @@ static inline void mcam_reg_set_bit(struct mcam_camera *cam, int mccic_register(struct mcam_camera *cam); int mccic_irq(struct mcam_camera *cam, unsigned int irqs); void mccic_shutdown(struct mcam_camera *cam); -#ifdef CONFIG_PM void mccic_suspend(struct mcam_camera *cam); int mccic_resume(struct mcam_camera *cam); -#endif /* * Register definitions for the m88alp01 camera interface. Offsets in bytes diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c index f82a81a3bdee..61fed1e35a00 100644 --- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c +++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c @@ -731,7 +731,7 @@ static void mtk_jpeg_stop_streaming(struct vb2_queue *q) * subsampling. Update capture queue when the stream is off. */ if (ctx->state == MTK_JPEG_SOURCE_CHANGE && - !V4L2_TYPE_IS_OUTPUT(q->type)) { + V4L2_TYPE_IS_CAPTURE(q->type)) { struct mtk_jpeg_src_buf *src_buf; vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c index 58abfbdfb82d..b3426a551bea 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c @@ -14,46 +14,6 @@ #include "mtk_mdp_comp.h" -static const char * const mtk_mdp_comp_stem[MTK_MDP_COMP_TYPE_MAX] = { - "mdp-rdma", - "mdp-rsz", - "mdp-wdma", - "mdp-wrot", -}; - -struct mtk_mdp_comp_match { - enum mtk_mdp_comp_type type; - int alias_id; -}; - -static const struct mtk_mdp_comp_match mtk_mdp_matches[MTK_MDP_COMP_ID_MAX] = { - { MTK_MDP_RDMA, 0 }, - { MTK_MDP_RDMA, 1 }, - { MTK_MDP_RSZ, 0 }, - { MTK_MDP_RSZ, 1 }, - { MTK_MDP_RSZ, 2 }, - { MTK_MDP_WDMA, 0 }, - { MTK_MDP_WROT, 0 }, - { MTK_MDP_WROT, 1 }, -}; - -int mtk_mdp_comp_get_id(struct device *dev, struct device_node *node, - enum mtk_mdp_comp_type comp_type) -{ - int id = of_alias_get_id(node, mtk_mdp_comp_stem[comp_type]); - int i; - - for (i = 0; i < ARRAY_SIZE(mtk_mdp_matches); i++) { - if (comp_type == mtk_mdp_matches[i].type && - id == mtk_mdp_matches[i].alias_id) - return i; - } - - dev_err(dev, "Failed to get id. type: %d, id: %d\n", comp_type, id); - - return -EINVAL; -} - void mtk_mdp_comp_clock_on(struct device *dev, struct mtk_mdp_comp *comp) { int i, err; @@ -62,8 +22,8 @@ void mtk_mdp_comp_clock_on(struct device *dev, struct mtk_mdp_comp *comp) err = mtk_smi_larb_get(comp->larb_dev); if (err) dev_err(dev, - "failed to get larb, err %d. type:%d id:%d\n", - err, comp->type, comp->id); + "failed to get larb, err %d. type:%d\n", + err, comp->type); } for (i = 0; i < ARRAY_SIZE(comp->clk); i++) { @@ -72,8 +32,8 @@ void mtk_mdp_comp_clock_on(struct device *dev, struct mtk_mdp_comp *comp) err = clk_prepare_enable(comp->clk[i]); if (err) dev_err(dev, - "failed to enable clock, err %d. type:%d id:%d i:%d\n", - err, comp->type, comp->id, i); + "failed to enable clock, err %d. type:%d i:%d\n", + err, comp->type, i); } } @@ -92,29 +52,24 @@ void mtk_mdp_comp_clock_off(struct device *dev, struct mtk_mdp_comp *comp) } int mtk_mdp_comp_init(struct device *dev, struct device_node *node, - struct mtk_mdp_comp *comp, enum mtk_mdp_comp_id comp_id) + struct mtk_mdp_comp *comp, + enum mtk_mdp_comp_type comp_type) { struct device_node *larb_node; struct platform_device *larb_pdev; + int ret; int i; - if (comp_id < 0 || comp_id >= MTK_MDP_COMP_ID_MAX) { - dev_err(dev, "Invalid comp_id %d\n", comp_id); - return -EINVAL; - } - comp->dev_node = of_node_get(node); - comp->id = comp_id; - comp->type = mtk_mdp_matches[comp_id].type; - comp->regs = of_iomap(node, 0); + comp->type = comp_type; for (i = 0; i < ARRAY_SIZE(comp->clk); i++) { comp->clk[i] = of_clk_get(node, i); if (IS_ERR(comp->clk[i])) { if (PTR_ERR(comp->clk[i]) != -EPROBE_DEFER) dev_err(dev, "Failed to get clock\n"); - - return PTR_ERR(comp->clk[i]); + ret = PTR_ERR(comp->clk[i]); + goto put_dev; } /* Only RDMA needs two clocks */ @@ -133,20 +88,27 @@ int mtk_mdp_comp_init(struct device *dev, struct device_node *node, if (!larb_node) { dev_err(dev, "Missing mediadek,larb phandle in %pOF node\n", node); - return -EINVAL; + ret = -EINVAL; + goto put_dev; } larb_pdev = of_find_device_by_node(larb_node); if (!larb_pdev) { dev_warn(dev, "Waiting for larb device %pOF\n", larb_node); of_node_put(larb_node); - return -EPROBE_DEFER; + ret = -EPROBE_DEFER; + goto put_dev; } of_node_put(larb_node); comp->larb_dev = &larb_pdev->dev; return 0; + +put_dev: + of_node_put(comp->dev_node); + + return ret; } void mtk_mdp_comp_deinit(struct device *dev, struct mtk_mdp_comp *comp) diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h index 998a4b953025..1bf0242cce46 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h @@ -22,41 +22,26 @@ enum mtk_mdp_comp_type { MTK_MDP_COMP_TYPE_MAX, }; -enum mtk_mdp_comp_id { - MTK_MDP_COMP_RDMA0, - MTK_MDP_COMP_RDMA1, - MTK_MDP_COMP_RSZ0, - MTK_MDP_COMP_RSZ1, - MTK_MDP_COMP_RSZ2, - MTK_MDP_COMP_WDMA, - MTK_MDP_COMP_WROT0, - MTK_MDP_COMP_WROT1, - MTK_MDP_COMP_ID_MAX, -}; - /** * struct mtk_mdp_comp - the MDP's function component data + * @node: list node to track sibing MDP components * @dev_node: component device node * @clk: clocks required for component - * @regs: Mapped address of component registers. * @larb_dev: SMI device required for component * @type: component type - * @id: component ID */ struct mtk_mdp_comp { + struct list_head node; struct device_node *dev_node; struct clk *clk[2]; - void __iomem *regs; struct device *larb_dev; enum mtk_mdp_comp_type type; - enum mtk_mdp_comp_id id; }; int mtk_mdp_comp_init(struct device *dev, struct device_node *node, - struct mtk_mdp_comp *comp, enum mtk_mdp_comp_id comp_id); + struct mtk_mdp_comp *comp, + enum mtk_mdp_comp_type comp_type); void mtk_mdp_comp_deinit(struct device *dev, struct mtk_mdp_comp *comp); -int mtk_mdp_comp_get_id(struct device *dev, struct device_node *node, - enum mtk_mdp_comp_type comp_type); void mtk_mdp_comp_clock_on(struct device *dev, struct mtk_mdp_comp *comp); void mtk_mdp_comp_clock_off(struct device *dev, struct mtk_mdp_comp *comp); diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c b/drivers/media/platform/mtk-mdp/mtk_mdp_core.c index aeaed2cf4458..f96c8b3bf861 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_core.c @@ -55,19 +55,19 @@ MODULE_DEVICE_TABLE(of, mtk_mdp_of_ids); static void mtk_mdp_clock_on(struct mtk_mdp_dev *mdp) { struct device *dev = &mdp->pdev->dev; - int i; + struct mtk_mdp_comp *comp_node; - for (i = 0; i < ARRAY_SIZE(mdp->comp); i++) - mtk_mdp_comp_clock_on(dev, mdp->comp[i]); + list_for_each_entry(comp_node, &mdp->comp_list, node) + mtk_mdp_comp_clock_on(dev, comp_node); } static void mtk_mdp_clock_off(struct mtk_mdp_dev *mdp) { struct device *dev = &mdp->pdev->dev; - int i; + struct mtk_mdp_comp *comp_node; - for (i = 0; i < ARRAY_SIZE(mdp->comp); i++) - mtk_mdp_comp_clock_off(dev, mdp->comp[i]); + list_for_each_entry(comp_node, &mdp->comp_list, node) + mtk_mdp_comp_clock_off(dev, comp_node); } static void mtk_mdp_wdt_worker(struct work_struct *work) @@ -91,12 +91,25 @@ static void mtk_mdp_reset_handler(void *priv) queue_work(mdp->wdt_wq, &mdp->wdt_work); } +void mtk_mdp_register_component(struct mtk_mdp_dev *mdp, + struct mtk_mdp_comp *comp) +{ + list_add(&mdp->comp_list, &comp->node); +} + +void mtk_mdp_unregister_component(struct mtk_mdp_dev *mdp, + struct mtk_mdp_comp *comp) +{ + list_del(&comp->node); +} + static int mtk_mdp_probe(struct platform_device *pdev) { struct mtk_mdp_dev *mdp; struct device *dev = &pdev->dev; struct device_node *node, *parent; - int i, ret = 0; + struct mtk_mdp_comp *comp, *comp_temp; + int ret = 0; mdp = devm_kzalloc(dev, sizeof(*mdp), GFP_KERNEL); if (!mdp) @@ -104,6 +117,7 @@ static int mtk_mdp_probe(struct platform_device *pdev) mdp->id = pdev->id; mdp->pdev = pdev; + INIT_LIST_HEAD(&mdp->comp_list); INIT_LIST_HEAD(&mdp->ctx_list); mutex_init(&mdp->lock); @@ -123,8 +137,6 @@ static int mtk_mdp_probe(struct platform_device *pdev) for_each_child_of_node(parent, node) { const struct of_device_id *of_id; enum mtk_mdp_comp_type comp_type; - int comp_id; - struct mtk_mdp_comp *comp; of_id = of_match_node(mtk_mdp_comp_dt_ids, node); if (!of_id) @@ -137,12 +149,6 @@ static int mtk_mdp_probe(struct platform_device *pdev) } comp_type = (enum mtk_mdp_comp_type)of_id->data; - comp_id = mtk_mdp_comp_get_id(dev, node, comp_type); - if (comp_id < 0) { - dev_warn(dev, "Skipping unknown component %pOF\n", - node); - continue; - } comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL); if (!comp) { @@ -150,13 +156,14 @@ static int mtk_mdp_probe(struct platform_device *pdev) of_node_put(node); goto err_comp; } - mdp->comp[comp_id] = comp; - ret = mtk_mdp_comp_init(dev, node, comp, comp_id); + ret = mtk_mdp_comp_init(dev, node, comp, comp_type); if (ret) { of_node_put(node); goto err_comp; } + + mtk_mdp_register_component(mdp, comp); } mdp->job_wq = create_singlethread_workqueue(MTK_MDP_MODULE_NAME); @@ -188,12 +195,20 @@ static int mtk_mdp_probe(struct platform_device *pdev) } mdp->vpu_dev = vpu_get_plat_device(pdev); - vpu_wdt_reg_handler(mdp->vpu_dev, mtk_mdp_reset_handler, mdp, - VPU_RST_MDP); + ret = vpu_wdt_reg_handler(mdp->vpu_dev, mtk_mdp_reset_handler, mdp, + VPU_RST_MDP); + if (ret) { + dev_err(&pdev->dev, "Failed to register reset handler\n"); + goto err_m2m_register; + } platform_set_drvdata(pdev, mdp); - vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); + ret = vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, "Failed to set vb2 dma mag seg size\n"); + goto err_m2m_register; + } pm_runtime_enable(dev); dev_dbg(dev, "mdp-%d registered successfully\n", mdp->id); @@ -212,8 +227,10 @@ err_alloc_wdt_wq: err_alloc_job_wq: err_comp: - for (i = 0; i < ARRAY_SIZE(mdp->comp); i++) - mtk_mdp_comp_deinit(dev, mdp->comp[i]); + list_for_each_entry_safe(comp, comp_temp, &mdp->comp_list, node) { + mtk_mdp_unregister_component(mdp, comp); + mtk_mdp_comp_deinit(dev, comp); + } dev_dbg(dev, "err %d\n", ret); return ret; @@ -222,7 +239,7 @@ err_comp: static int mtk_mdp_remove(struct platform_device *pdev) { struct mtk_mdp_dev *mdp = platform_get_drvdata(pdev); - int i; + struct mtk_mdp_comp *comp, *comp_temp; pm_runtime_disable(&pdev->dev); vb2_dma_contig_clear_max_seg_size(&pdev->dev); @@ -235,8 +252,10 @@ static int mtk_mdp_remove(struct platform_device *pdev) flush_workqueue(mdp->job_wq); destroy_workqueue(mdp->job_wq); - for (i = 0; i < ARRAY_SIZE(mdp->comp); i++) - mtk_mdp_comp_deinit(&pdev->dev, mdp->comp[i]); + list_for_each_entry_safe(comp, comp_temp, &mdp->comp_list, node) { + mtk_mdp_unregister_component(mdp, comp); + mtk_mdp_comp_deinit(&pdev->dev, comp); + } dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name); return 0; diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_core.h b/drivers/media/platform/mtk-mdp/mtk_mdp_core.h index bafcccd71f31..a7da14b97077 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_core.h +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_core.h @@ -28,8 +28,6 @@ #define MTK_MDP_FMT_FLAG_CAPTURE BIT(1) #define MTK_MDP_VPU_INIT BIT(0) -#define MTK_MDP_SRC_FMT BIT(1) -#define MTK_MDP_DST_FMT BIT(2) #define MTK_MDP_CTX_ERROR BIT(5) /** @@ -138,7 +136,7 @@ struct mtk_mdp_variant { * @pdev: pointer to the image processor platform device * @variant: the IP variant information * @id: image processor device index (0..MTK_MDP_MAX_DEVS) - * @comp: MDP function components + * @comp_list: list of MDP function components * @m2m_dev: v4l2 memory-to-memory device data * @ctx_list: list of struct mtk_mdp_ctx * @vdev: video device for image processor driver @@ -156,7 +154,7 @@ struct mtk_mdp_dev { struct platform_device *pdev; struct mtk_mdp_variant *variant; u16 id; - struct mtk_mdp_comp *comp[MTK_MDP_COMP_ID_MAX]; + struct list_head comp_list; struct v4l2_m2m_dev *m2m_dev; struct list_head ctx_list; struct video_device *vdev; @@ -223,6 +221,12 @@ struct mtk_mdp_ctx { extern int mtk_mdp_dbg_level; +void mtk_mdp_register_component(struct mtk_mdp_dev *mdp, + struct mtk_mdp_comp *comp); + +void mtk_mdp_unregister_component(struct mtk_mdp_dev *mdp, + struct mtk_mdp_comp *comp); + #if defined(DEBUG) #define mtk_mdp_dbg(level, fmt, args...) \ diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c index 821f2cf325f0..724c7333b6e5 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c @@ -193,7 +193,7 @@ static const struct mtk_mdp_fmt *mtk_mdp_try_fmt_mplane(struct mtk_mdp_ctx *ctx, pix_mp->field = V4L2_FIELD_NONE; pix_mp->pixelformat = fmt->pixelformat; - if (!V4L2_TYPE_IS_OUTPUT(f->type)) { + if (V4L2_TYPE_IS_CAPTURE(f->type)) { pix_mp->colorspace = ctx->colorspace; pix_mp->xfer_func = ctx->xfer_func; pix_mp->ycbcr_enc = ctx->ycbcr_enc; @@ -327,9 +327,8 @@ static int mtk_mdp_try_crop(struct mtk_mdp_ctx *ctx, u32 type, mtk_mdp_bound_align_image(&new_w, min_w, max_w, align_w, &new_h, min_h, max_h, align_h); - if (!V4L2_TYPE_IS_OUTPUT(type) && - (ctx->ctrls.rotate->val == 90 || - ctx->ctrls.rotate->val == 270)) + if (V4L2_TYPE_IS_CAPTURE(type) && + (ctx->ctrls.rotate->val == 90 || ctx->ctrls.rotate->val == 270)) mtk_mdp_check_crop_change(new_h, new_w, &r->width, &r->height); else @@ -369,13 +368,6 @@ void mtk_mdp_ctx_state_lock_set(struct mtk_mdp_ctx *ctx, u32 state) mutex_unlock(&ctx->slock); } -static void mtk_mdp_ctx_state_lock_clear(struct mtk_mdp_ctx *ctx, u32 state) -{ - mutex_lock(&ctx->slock); - ctx->state &= ~state; - mutex_unlock(&ctx->slock); -} - static bool mtk_mdp_ctx_state_is_set(struct mtk_mdp_ctx *ctx, u32 mask) { bool ret; @@ -726,11 +718,6 @@ static int mtk_mdp_m2m_s_fmt_mplane(struct file *file, void *fh, ctx->quant = pix_mp->quantization; } - if (V4L2_TYPE_IS_OUTPUT(f->type)) - mtk_mdp_ctx_state_lock_set(ctx, MTK_MDP_SRC_FMT); - else - mtk_mdp_ctx_state_lock_set(ctx, MTK_MDP_DST_FMT); - mtk_mdp_dbg(2, "[%d] type:%d, frame:%dx%d", ctx->id, f->type, frame->width, frame->height); @@ -742,13 +729,6 @@ static int mtk_mdp_m2m_reqbufs(struct file *file, void *fh, { struct mtk_mdp_ctx *ctx = fh_to_ctx(fh); - if (reqbufs->count == 0) { - if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) - mtk_mdp_ctx_state_lock_clear(ctx, MTK_MDP_SRC_FMT); - else - mtk_mdp_ctx_state_lock_clear(ctx, MTK_MDP_DST_FMT); - } - return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); } @@ -758,14 +738,6 @@ static int mtk_mdp_m2m_streamon(struct file *file, void *fh, struct mtk_mdp_ctx *ctx = fh_to_ctx(fh); int ret; - /* The source and target color format need to be set */ - if (V4L2_TYPE_IS_OUTPUT(type)) { - if (!mtk_mdp_ctx_state_is_set(ctx, MTK_MDP_SRC_FMT)) - return -EINVAL; - } else if (!mtk_mdp_ctx_state_is_set(ctx, MTK_MDP_DST_FMT)) { - return -EINVAL; - } - if (!mtk_mdp_ctx_state_is_set(ctx, MTK_MDP_VPU_INIT)) { ret = mtk_mdp_vpu_init(&ctx->vpu); if (ret < 0) { @@ -899,24 +871,21 @@ static int mtk_mdp_m2m_s_selection(struct file *file, void *fh, frame = &ctx->d_frame; /* Check to see if scaling ratio is within supported range */ - if (mtk_mdp_ctx_state_is_set(ctx, MTK_MDP_DST_FMT | MTK_MDP_SRC_FMT)) { - if (V4L2_TYPE_IS_OUTPUT(s->type)) { - ret = mtk_mdp_check_scaler_ratio(variant, new_r.width, - new_r.height, ctx->d_frame.crop.width, - ctx->d_frame.crop.height, - ctx->ctrls.rotate->val); - } else { - ret = mtk_mdp_check_scaler_ratio(variant, - ctx->s_frame.crop.width, - ctx->s_frame.crop.height, new_r.width, - new_r.height, ctx->ctrls.rotate->val); - } + if (V4L2_TYPE_IS_OUTPUT(s->type)) + ret = mtk_mdp_check_scaler_ratio(variant, new_r.width, + new_r.height, ctx->d_frame.crop.width, + ctx->d_frame.crop.height, + ctx->ctrls.rotate->val); + else + ret = mtk_mdp_check_scaler_ratio(variant, + ctx->s_frame.crop.width, + ctx->s_frame.crop.height, new_r.width, + new_r.height, ctx->ctrls.rotate->val); - if (ret) { - dev_info(&ctx->mdp_dev->pdev->dev, - "Out of scaler range"); - return -EINVAL; - } + if (ret) { + dev_info(&ctx->mdp_dev->pdev->dev, + "Out of scaler range"); + return -EINVAL; } s->r = new_r; @@ -989,7 +958,6 @@ static int mtk_mdp_s_ctrl(struct v4l2_ctrl *ctrl) struct mtk_mdp_ctx *ctx = ctrl_to_ctx(ctrl); struct mtk_mdp_dev *mdp = ctx->mdp_dev; struct mtk_mdp_variant *variant = mdp->variant; - u32 state = MTK_MDP_DST_FMT | MTK_MDP_SRC_FMT; int ret = 0; if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) @@ -1003,17 +971,15 @@ static int mtk_mdp_s_ctrl(struct v4l2_ctrl *ctrl) ctx->vflip = ctrl->val; break; case V4L2_CID_ROTATE: - if (mtk_mdp_ctx_state_is_set(ctx, state)) { - ret = mtk_mdp_check_scaler_ratio(variant, - ctx->s_frame.crop.width, - ctx->s_frame.crop.height, - ctx->d_frame.crop.width, - ctx->d_frame.crop.height, - ctx->ctrls.rotate->val); - - if (ret) - return -EINVAL; - } + ret = mtk_mdp_check_scaler_ratio(variant, + ctx->s_frame.crop.width, + ctx->s_frame.crop.height, + ctx->d_frame.crop.width, + ctx->d_frame.crop.height, + ctx->ctrls.rotate->val); + + if (ret) + return -EINVAL; ctx->rotation = ctrl->val; break; @@ -1090,6 +1056,7 @@ static int mtk_mdp_m2m_open(struct file *file) struct video_device *vfd = video_devdata(file); struct mtk_mdp_ctx *ctx = NULL; int ret; + struct v4l2_format default_format; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -1144,6 +1111,16 @@ static int mtk_mdp_m2m_open(struct file *file) list_add(&ctx->list, &mdp->ctx_list); mutex_unlock(&mdp->lock); + /* Default format */ + memset(&default_format, 0, sizeof(default_format)); + default_format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + default_format.fmt.pix_mp.width = 32; + default_format.fmt.pix_mp.height = 32; + default_format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_YUV420M; + mtk_mdp_m2m_s_fmt_mplane(file, &ctx->fh, &default_format); + default_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + mtk_mdp_m2m_s_fmt_mplane(file, &ctx->fh, &default_format); + mtk_mdp_dbg(0, "%s [%d]", dev_name(&mdp->pdev->dev), ctx->id); return 0; diff --git a/drivers/media/platform/omap3isp/isppreview.c b/drivers/media/platform/omap3isp/isppreview.c index 4dbdf3180d10..607b7685c982 100644 --- a/drivers/media/platform/omap3isp/isppreview.c +++ b/drivers/media/platform/omap3isp/isppreview.c @@ -2287,7 +2287,7 @@ static int preview_init_entities(struct isp_prev_device *prev) me->ops = &preview_media_ops; ret = media_entity_pads_init(me, PREV_PADS_NUM, pads); if (ret < 0) - return ret; + goto error_handler_free; preview_init_formats(sd, NULL); @@ -2320,6 +2320,8 @@ error_video_out: omap3isp_video_cleanup(&prev->video_in); error_video_in: media_entity_cleanup(&prev->subdev.entity); +error_handler_free: + v4l2_ctrl_handler_free(&prev->ctrls); return ret; } diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c index 3c5fe737d36f..6dce33f35041 100644 --- a/drivers/media/platform/pxa_camera.c +++ b/drivers/media/platform/pxa_camera.c @@ -2504,17 +2504,14 @@ static int pxa_camera_probe(struct platform_device *pdev) if (err) goto exit_notifier_cleanup; - if (pcdev->mclk) { - v4l2_clk_name_i2c(clk_name, sizeof(clk_name), - pcdev->asd.match.i2c.adapter_id, - pcdev->asd.match.i2c.address); - - pcdev->mclk_clk = v4l2_clk_register(&pxa_camera_mclk_ops, - clk_name, NULL); - if (IS_ERR(pcdev->mclk_clk)) { - err = PTR_ERR(pcdev->mclk_clk); - goto exit_notifier_cleanup; - } + v4l2_clk_name_i2c(clk_name, sizeof(clk_name), + pcdev->asd.match.i2c.adapter_id, + pcdev->asd.match.i2c.address); + + pcdev->mclk_clk = v4l2_clk_register(&pxa_camera_mclk_ops, clk_name, NULL); + if (IS_ERR(pcdev->mclk_clk)) { + err = PTR_ERR(pcdev->mclk_clk); + goto exit_notifier_cleanup; } err = v4l2_async_notifier_register(&pcdev->v4l2_dev, &pcdev->notifier); @@ -2588,7 +2585,7 @@ static struct platform_driver pxa_camera_driver = { module_platform_driver(pxa_camera_driver); -MODULE_DESCRIPTION("PXA27x SoC Camera Host driver"); +MODULE_DESCRIPTION("PXA27x Camera Driver"); MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>"); MODULE_LICENSE("GPL"); MODULE_VERSION(PXA_CAM_VERSION); diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index a5ae85674ffb..2ffcda06706b 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -562,8 +562,10 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) u32 hw_version; ret = pm_runtime_get_sync(dev); - if (ret < 0) + if (ret < 0) { + pm_runtime_put_sync(dev); return ret; + } ret = regulator_enable(csid->vdda); if (ret < 0) { @@ -1356,7 +1358,7 @@ int msm_csid_register_entity(struct csid_device *csid, pads[MSM_CSID_PAD_SINK].flags = MEDIA_PAD_FL_SINK; pads[MSM_CSID_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; - sd->entity.function = MEDIA_ENT_F_IO_V4L; + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; sd->entity.ops = &csid_media_ops; ret = media_entity_pads_init(&sd->entity, MSM_CSID_PADS_NUM, pads); if (ret < 0) { diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index 008afb85023b..03ef9c5f4774 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -737,7 +737,7 @@ int msm_csiphy_register_entity(struct csiphy_device *csiphy, pads[MSM_CSIPHY_PAD_SINK].flags = MEDIA_PAD_FL_SINK; pads[MSM_CSIPHY_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; - sd->entity.function = MEDIA_ENT_F_IO_V4L; + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; sd->entity.ops = &csiphy_media_ops; ret = media_entity_pads_init(&sd->entity, MSM_CSIPHY_PADS_NUM, pads); if (ret < 0) { diff --git a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c index 1f33b4eb198c..db94cfd6c508 100644 --- a/drivers/media/platform/qcom/camss/camss-ispif.c +++ b/drivers/media/platform/qcom/camss/camss-ispif.c @@ -344,8 +344,10 @@ static int ispif_set_power(struct v4l2_subdev *sd, int on) } ret = pm_runtime_get_sync(dev); - if (ret < 0) + if (ret < 0) { + pm_runtime_put_sync(dev); goto exit; + } ret = camss_enable_clocks(ispif->nclocks, ispif->clock, dev); if (ret < 0) { @@ -1323,7 +1325,7 @@ int msm_ispif_register_entities(struct ispif_device *ispif, pads[MSM_ISPIF_PAD_SINK].flags = MEDIA_PAD_FL_SINK; pads[MSM_ISPIF_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; - sd->entity.function = MEDIA_ENT_F_IO_V4L; + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; sd->entity.ops = &ispif_media_ops; ret = media_entity_pads_init(&sd->entity, MSM_ISPIF_PADS_NUM, pads); diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index a8c542fa647d..fc31c2c169cd 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -1265,12 +1265,12 @@ static int vfe_get(struct vfe_device *vfe) ret = vfe_set_clock_rates(vfe); if (ret < 0) - goto error_clocks; + goto error_pm_runtime_get; ret = camss_enable_clocks(vfe->nclocks, vfe->clock, vfe->camss->dev); if (ret < 0) - goto error_clocks; + goto error_pm_runtime_get; ret = vfe_reset(vfe); if (ret < 0) @@ -1282,7 +1282,7 @@ static int vfe_get(struct vfe_device *vfe) } else { ret = vfe_check_clock_rates(vfe); if (ret < 0) - goto error_clocks; + goto error_pm_runtime_get; } vfe->power_count++; @@ -1293,10 +1293,8 @@ static int vfe_get(struct vfe_device *vfe) error_reset: camss_disable_clocks(vfe->nclocks, vfe->clock); -error_clocks: - pm_runtime_put_sync(vfe->camss->dev); - error_pm_runtime_get: + pm_runtime_put_sync(vfe->camss->dev); camss_pm_domain_off(vfe->camss, vfe->id); error_pm_domain: diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 3fdc9f964a3c..2483641799df 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -504,7 +504,6 @@ static int camss_of_parse_ports(struct camss *camss) return num_subdevs; err_cleanup: - v4l2_async_notifier_cleanup(&camss->notifier); of_node_put(node); return ret; } @@ -835,29 +834,38 @@ static int camss_probe(struct platform_device *pdev) camss->csid_num = 4; camss->vfe_num = 2; } else { - return -EINVAL; + ret = -EINVAL; + goto err_free; } camss->csiphy = devm_kcalloc(dev, camss->csiphy_num, sizeof(*camss->csiphy), GFP_KERNEL); - if (!camss->csiphy) - return -ENOMEM; + if (!camss->csiphy) { + ret = -ENOMEM; + goto err_free; + } camss->csid = devm_kcalloc(dev, camss->csid_num, sizeof(*camss->csid), GFP_KERNEL); - if (!camss->csid) - return -ENOMEM; + if (!camss->csid) { + ret = -ENOMEM; + goto err_free; + } camss->vfe = devm_kcalloc(dev, camss->vfe_num, sizeof(*camss->vfe), GFP_KERNEL); - if (!camss->vfe) - return -ENOMEM; + if (!camss->vfe) { + ret = -ENOMEM; + goto err_free; + } v4l2_async_notifier_init(&camss->notifier); num_subdevs = camss_of_parse_ports(camss); - if (num_subdevs < 0) - return num_subdevs; + if (num_subdevs < 0) { + ret = num_subdevs; + goto err_cleanup; + } ret = camss_init_subdevices(camss); if (ret < 0) @@ -936,6 +944,8 @@ err_register_entities: v4l2_device_unregister(&camss->v4l2_dev); err_cleanup: v4l2_async_notifier_cleanup(&camss->notifier); +err_free: + kfree(camss); return ret; } diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c index abf93158857b..531e7a41658f 100644 --- a/drivers/media/platform/qcom/venus/pm_helpers.c +++ b/drivers/media/platform/qcom/venus/pm_helpers.c @@ -496,6 +496,10 @@ min_loaded_core(struct venus_inst *inst, u32 *min_coreid, u32 *min_load) list_for_each_entry(inst_pos, &core->instances, list) { if (inst_pos == inst) continue; + + if (inst_pos->state != INST_START) + continue; + vpp_freq = inst_pos->clk_data.codec_freq_data->vpp_freq; coreid = inst_pos->clk_data.core_id; diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index feed648550d1..513bbc07f7bc 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -1177,7 +1177,7 @@ static int venc_open(struct file *file) ret = pm_runtime_get_sync(core->dev_enc); if (ret < 0) - goto err_free_inst; + goto err_put_sync; ret = venc_ctrl_init(inst); if (ret) @@ -1222,7 +1222,6 @@ err_ctrl_deinit: venc_ctrl_deinit(inst); err_put_sync: pm_runtime_put_sync(core->dev_enc); -err_free_inst: kfree(inst); return ret; } diff --git a/drivers/media/platform/rcar-vin/rcar-csi2.c b/drivers/media/platform/rcar-vin/rcar-csi2.c index 151e6a90c5fb..c6cc4f473a07 100644 --- a/drivers/media/platform/rcar-vin/rcar-csi2.c +++ b/drivers/media/platform/rcar-vin/rcar-csi2.c @@ -320,6 +320,7 @@ static const struct rcar_csi2_format rcar_csi2_formats[] = { { .code = MEDIA_BUS_FMT_YUYV8_1X16, .datatype = 0x1e, .bpp = 16 }, { .code = MEDIA_BUS_FMT_UYVY8_2X8, .datatype = 0x1e, .bpp = 16 }, { .code = MEDIA_BUS_FMT_YUYV10_2X10, .datatype = 0x1e, .bpp = 20 }, + { .code = MEDIA_BUS_FMT_SRGGB8_1X8, .datatype = 0x2a, .bpp = 8 }, }; static const struct rcar_csi2_format *rcsi2_code_to_fmt(unsigned int code) @@ -344,7 +345,7 @@ enum rcar_csi2_pads { struct rcar_csi2_info { int (*init_phtw)(struct rcar_csi2 *priv, unsigned int mbps); - int (*confirm_start)(struct rcar_csi2 *priv); + int (*phy_post_init)(struct rcar_csi2 *priv); const struct rcsi2_mbps_reg *hsfreqrange; unsigned int csi0clkfreqrange; unsigned int num_channels; @@ -575,9 +576,9 @@ static int rcsi2_start_receiver(struct rcar_csi2 *priv) if (ret) return ret; - /* Confirm start */ - if (priv->info->confirm_start) { - ret = priv->info->confirm_start(priv); + /* Run post PHY start initialization, if needed. */ + if (priv->info->phy_post_init) { + ret = priv->info->phy_post_init(priv); if (ret) return ret; } @@ -975,7 +976,7 @@ static int rcsi2_init_phtw_v3m_e3(struct rcar_csi2 *priv, unsigned int mbps) return rcsi2_phtw_write_mbps(priv, mbps, phtw_mbps_v3m_e3, 0x44); } -static int rcsi2_confirm_start_v3m_e3(struct rcar_csi2 *priv) +static int rcsi2_phy_post_init_v3m_e3(struct rcar_csi2 *priv) { static const struct phtw_value step1[] = { { .data = 0xee, .code = 0x34 }, @@ -1059,7 +1060,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a77965 = { static const struct rcar_csi2_info rcar_csi2_info_r8a77970 = { .init_phtw = rcsi2_init_phtw_v3m_e3, - .confirm_start = rcsi2_confirm_start_v3m_e3, + .phy_post_init = rcsi2_phy_post_init_v3m_e3, .num_channels = 4, }; @@ -1072,7 +1073,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a77980 = { static const struct rcar_csi2_info rcar_csi2_info_r8a77990 = { .init_phtw = rcsi2_init_phtw_v3m_e3, - .confirm_start = rcsi2_confirm_start_v3m_e3, + .phy_post_init = rcsi2_phy_post_init_v3m_e3, .num_channels = 2, }; diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c index 1a30cd036371..a5dbb90c5210 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -85,6 +85,7 @@ #define VNMC_INF_YUV8_BT601 (1 << 16) #define VNMC_INF_YUV10_BT656 (2 << 16) #define VNMC_INF_YUV10_BT601 (3 << 16) +#define VNMC_INF_RAW8 (4 << 16) #define VNMC_INF_YUV16 (5 << 16) #define VNMC_INF_RGB888 (6 << 16) #define VNMC_VUP (1 << 10) @@ -587,13 +588,19 @@ void rvin_crop_scale_comp(struct rvin_dev *vin) rvin_write(vin, vin->crop.top, VNSLPRC_REG); rvin_write(vin, vin->crop.top + vin->crop.height - 1, VNELPRC_REG); - /* TODO: Add support for the UDS scaler. */ if (vin->info->model != RCAR_GEN3) rvin_crop_scale_comp_gen2(vin); fmt = rvin_format_from_pixel(vin, vin->format.pixelformat); stride = vin->format.bytesperline / fmt->bpp; + + /* For RAW8 format bpp is 1, but the hardware process RAW8 + * format in 2 pixel unit hence configure VNIS_REG as stride / 2. + */ + if (vin->format.pixelformat == V4L2_PIX_FMT_SRGGB8) + stride /= 2; + rvin_write(vin, stride, VNIS_REG); } @@ -676,6 +683,9 @@ static int rvin_setup(struct rvin_dev *vin) input_is_yuv = true; break; + case MEDIA_BUS_FMT_SRGGB8_1X8: + vnmc |= VNMC_INF_RAW8; + break; default: break; } @@ -737,6 +747,9 @@ static int rvin_setup(struct rvin_dev *vin) case V4L2_PIX_FMT_ABGR32: dmr = VNDMR_A8BIT(vin->alpha) | VNDMR_EXRGB | VNDMR_DTMD_ARGB; break; + case V4L2_PIX_FMT_SRGGB8: + dmr = 0; + break; default: vin_err(vin, "Invalid pixelformat (0x%x)\n", vin->format.pixelformat); @@ -1110,11 +1123,15 @@ static int rvin_mc_validate_format(struct rvin_dev *vin, struct v4l2_subdev *sd, case MEDIA_BUS_FMT_UYVY8_2X8: case MEDIA_BUS_FMT_UYVY10_2X10: case MEDIA_BUS_FMT_RGB888_1X24: - vin->mbus_code = fmt.format.code; + break; + case MEDIA_BUS_FMT_SRGGB8_1X8: + if (vin->format.pixelformat != V4L2_PIX_FMT_SRGGB8) + return -EPIPE; break; default: return -EPIPE; } + vin->mbus_code = fmt.format.code; switch (fmt.format.field) { case V4L2_FIELD_TOP: diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index f421e2584875..0e066bba747e 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -66,6 +66,10 @@ static const struct rvin_video_format rvin_formats[] = { .fourcc = V4L2_PIX_FMT_ABGR32, .bpp = 4, }, + { + .fourcc = V4L2_PIX_FMT_SRGGB8, + .bpp = 1, + }, }; const struct rvin_video_format *rvin_format_from_pixel(struct rvin_dev *vin, @@ -350,9 +354,9 @@ static int rvin_enum_fmt_vid_cap(struct file *file, void *priv, * all of the related pixel formats. If mbus_code is not set enumerate * all possible pixelformats. * - * TODO: Once raw capture formats are added to the driver this needs - * to be extended so raw media bus codes only result in raw pixel - * formats. + * TODO: Once raw MEDIA_BUS_FMT_SRGGB12_1X12 format is added to the + * driver this needs to be extended so raw media bus code only result in + * raw pixel format. */ switch (f->mbus_code) { case 0: @@ -362,6 +366,11 @@ static int rvin_enum_fmt_vid_cap(struct file *file, void *priv, case MEDIA_BUS_FMT_UYVY10_2X10: case MEDIA_BUS_FMT_RGB888_1X24: break; + case MEDIA_BUS_FMT_SRGGB8_1X8: + if (f->index) + return -EINVAL; + f->pixelformat = V4L2_PIX_FMT_SRGGB8; + return 0; default: return -EINVAL; } diff --git a/drivers/media/platform/rcar_jpu.c b/drivers/media/platform/rcar_jpu.c index 5250a14324e9..9b99ff368698 100644 --- a/drivers/media/platform/rcar_jpu.c +++ b/drivers/media/platform/rcar_jpu.c @@ -1066,7 +1066,7 @@ static int jpu_buf_prepare(struct vb2_buffer *vb) } /* decoder capture queue */ - if (!ctx->encoder && !V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) + if (!ctx->encoder && V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) vb2_set_plane_payload(vb, i, size); } diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index 4be6dcf292ff..aaa96f256356 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -200,22 +200,25 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) dst_info.data.format = ctx->out.fmt->hw_format; dst_info.data.swap = ctx->out.fmt->color_swap; - if (ctx->in.fmt->hw_format >= RGA_COLOR_FMT_YUV422SP) { - if (ctx->out.fmt->hw_format < RGA_COLOR_FMT_YUV422SP) { - switch (ctx->in.colorspace) { - case V4L2_COLORSPACE_REC709: - src_info.data.csc_mode = - RGA_SRC_CSC_MODE_BT709_R0; - break; - default: - src_info.data.csc_mode = - RGA_SRC_CSC_MODE_BT601_R0; - break; - } + /* + * 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) { + case V4L2_COLORSPACE_REC709: + src_info.data.csc_mode = RGA_SRC_CSC_MODE_BT709_R0; + break; + default: + src_info.data.csc_mode = RGA_SRC_CSC_MODE_BT601_R0; + break; } } - if (ctx->out.fmt->hw_format >= RGA_COLOR_FMT_YUV422SP) { + 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) { case V4L2_COLORSPACE_REC709: dst_info.data.csc_mode = RGA_SRC_CSC_MODE_BT709_R0; diff --git a/drivers/media/platform/rockchip/rga/rga-hw.h b/drivers/media/platform/rockchip/rga/rga-hw.h index 96cb0314dfa7..e8917e5630a4 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.h +++ b/drivers/media/platform/rockchip/rga/rga-hw.h @@ -95,6 +95,11 @@ #define RGA_COLOR_FMT_CP_8BPP 15 #define RGA_COLOR_FMT_MASK 15 +#define RGA_COLOR_FMT_IS_YUV(fmt) \ + (((fmt) >= RGA_COLOR_FMT_YUV422SP) && ((fmt) < RGA_COLOR_FMT_CP_1BPP)) +#define RGA_COLOR_FMT_IS_RGB(fmt) \ + ((fmt) < RGA_COLOR_FMT_YUV422SP) + #define RGA_COLOR_NONE_SWAP 0 #define RGA_COLOR_RB_SWAP 1 #define RGA_COLOR_ALPHA_SWAP 2 diff --git a/drivers/media/platform/s3c-camif/camif-core.c b/drivers/media/platform/s3c-camif/camif-core.c index c6fbcd7036d6..92f43c0cbc0c 100644 --- a/drivers/media/platform/s3c-camif/camif-core.c +++ b/drivers/media/platform/s3c-camif/camif-core.c @@ -304,7 +304,7 @@ static int camif_media_dev_init(struct camif_dev *camif) int ret; memset(md, 0, sizeof(*md)); - snprintf(md->model, sizeof(md->model), "SAMSUNG S3C%s CAMIF", + snprintf(md->model, sizeof(md->model), "Samsung S3C%s CAMIF", ip_rev == S3C6410_CAMIF_IP_REV ? "6410" : "244X"); strscpy(md->bus_info, "platform", sizeof(md->bus_info)); md->hw_revision = ip_rev; diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index 6932fd47071b..15bcb7f6e113 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -695,21 +695,13 @@ static int g2d_probe(struct platform_device *pdev) vfd->lock = &dev->mutex; vfd->v4l2_dev = &dev->v4l2_dev; vfd->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; - ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); - if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); - goto rel_vdev; - } - video_set_drvdata(vfd, dev); - dev->vfd = vfd; - v4l2_info(&dev->v4l2_dev, "device registered as /dev/video%d\n", - vfd->num); + platform_set_drvdata(pdev, dev); dev->m2m_dev = v4l2_m2m_init(&g2d_m2m_ops); if (IS_ERR(dev->m2m_dev)) { v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n"); ret = PTR_ERR(dev->m2m_dev); - goto unreg_video_dev; + goto rel_vdev; } def_frame.stride = (def_frame.width * def_frame.fmt->depth) >> 3; @@ -717,14 +709,24 @@ static int g2d_probe(struct platform_device *pdev) of_id = of_match_node(exynos_g2d_match, pdev->dev.of_node); if (!of_id) { ret = -ENODEV; - goto unreg_video_dev; + goto free_m2m; } dev->variant = (struct g2d_variant *)of_id->data; + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); + goto free_m2m; + } + video_set_drvdata(vfd, dev); + dev->vfd = vfd; + v4l2_info(&dev->v4l2_dev, "device registered as /dev/video%d\n", + vfd->num); + return 0; -unreg_video_dev: - video_unregister_device(dev->vfd); +free_m2m: + v4l2_m2m_release(dev->m2m_dev); rel_vdev: video_device_release(vfd); unreg_v4l2_dev: diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 86bda3947110..9b22dd8e34f4 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -24,6 +24,7 @@ #include <media/v4l2-event.h> #include <media/v4l2-mem2mem.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-rect.h> #include <media/videobuf2-v4l2.h> #include <media/videobuf2-dma-contig.h> @@ -1735,19 +1736,6 @@ static int exynos3250_jpeg_try_downscale(struct s5p_jpeg_ctx *ctx, return 0; } -/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */ -static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b) -{ - if (a->left < b->left || a->top < b->top) - return 0; - if (a->left + a->width > b->left + b->width) - return 0; - if (a->top + a->height > b->top + b->height) - return 0; - - return 1; -} - static int exynos3250_jpeg_try_crop(struct s5p_jpeg_ctx *ctx, struct v4l2_rect *r) { @@ -1780,7 +1768,7 @@ static int exynos3250_jpeg_try_crop(struct s5p_jpeg_ctx *ctx, r->left = round_down(r->left, 2); r->top = round_down(r->top, 2); - if (!enclosed_rectangle(r, &base_rect)) + if (!v4l2_rect_enclosed(r, &base_rect)) return -EINVAL; ctx->crop_rect.left = r->left; diff --git a/drivers/media/platform/sti/hva/hva-v4l2.c b/drivers/media/platform/sti/hva/hva-v4l2.c index 197b99d8fd9c..bb34d6997d99 100644 --- a/drivers/media/platform/sti/hva/hva-v4l2.c +++ b/drivers/media/platform/sti/hva/hva-v4l2.c @@ -1087,7 +1087,7 @@ static void hva_stop_streaming(struct vb2_queue *vq) if ((V4L2_TYPE_IS_OUTPUT(vq->type) && vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q)) || - (!V4L2_TYPE_IS_OUTPUT(vq->type) && + (V4L2_TYPE_IS_CAPTURE(vq->type) && vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q))) { dev_dbg(dev, "%s %s out=%d cap=%d\n", ctx->name, to_type_str(vq->type), diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c index eff34ded6305..5319eb1ab309 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c @@ -191,6 +191,8 @@ static int sun4i_csi_probe(struct platform_device *pdev) strscpy(csi->mdev.model, "Allwinner Video Capture Device", sizeof(csi->mdev.model)); csi->mdev.hw_revision = 0; + snprintf(csi->mdev.bus_info, sizeof(csi->mdev.bus_info), "platform:%s", + dev_name(csi->dev)); media_device_init(&csi->mdev); csi->v4l.mdev = &csi->mdev; diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c index 78fa1c535ac6..3278746246aa 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c @@ -413,7 +413,7 @@ int sun4i_csi_dma_register(struct sun4i_csi *csi, int irq) q->min_buffers_needed = 3; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - q->io_modes = VB2_MMAP; + q->io_modes = VB2_MMAP | VB2_DMABUF; q->lock = &csi->lock; q->drv_priv = csi; q->buf_struct_size = sizeof(struct sun4i_csi_buffer); diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c index 1721e5aee9c6..8f4e254b6a41 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c @@ -242,7 +242,8 @@ static int sun4i_csi_release(struct file *file) mutex_lock(&csi->lock); - v4l2_fh_release(file); + _vb2_fop_release(file, NULL); + v4l2_pipeline_pm_put(&csi->vdev.entity); pm_runtime_put(csi->dev); @@ -256,8 +257,6 @@ static const struct v4l2_file_operations sun4i_csi_fops = { .open = sun4i_csi_open, .release = sun4i_csi_release, .unlocked_ioctl = video_ioctl2, - .read = vb2_fop_read, - .write = vb2_fop_write, .poll = vb2_fop_poll, .mmap = vb2_fop_mmap, }; diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c index 055eb0b8e396..28e89340fed9 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c @@ -733,6 +733,8 @@ static int sun6i_csi_v4l2_init(struct sun6i_csi *csi) strscpy(csi->media_dev.model, "Allwinner Video Capture Device", sizeof(csi->media_dev.model)); csi->media_dev.hw_revision = 0; + snprintf(csi->media_dev.bus_info, sizeof(csi->media_dev.bus_info), + "platform:%s", dev_name(csi->dev)); media_device_init(&csi->media_dev); v4l2_async_notifier_init(&csi->notifier); diff --git a/drivers/media/platform/ti-vpe/Makefile b/drivers/media/platform/ti-vpe/Makefile index 886ac5ec073f..ad624056e039 100644 --- a/drivers/media/platform/ti-vpe/Makefile +++ b/drivers/media/platform/ti-vpe/Makefile @@ -13,4 +13,4 @@ ccflags-$(CONFIG_VIDEO_TI_VPE_DEBUG) += -DDEBUG obj-$(CONFIG_VIDEO_TI_CAL) += ti-cal.o -ti-cal-y := cal.o +ti-cal-y := cal.o cal-camerarx.o cal-video.o diff --git a/drivers/media/platform/ti-vpe/cal-camerarx.c b/drivers/media/platform/ti-vpe/cal-camerarx.c new file mode 100644 index 000000000000..806cbf175d39 --- /dev/null +++ b/drivers/media/platform/ti-vpe/cal-camerarx.c @@ -0,0 +1,649 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * TI Camera Access Layer (CAL) - CAMERARX + * + * Copyright (c) 2015-2020 Texas Instruments Inc. + * + * Authors: + * Benoit Parrot <bparrot@ti.com> + * Laurent Pinchart <laurent.pinchart@ideasonboard.com> + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of_graph.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> + +#include "cal.h" +#include "cal_regs.h" + +/* ------------------------------------------------------------------ + * I/O Register Accessors + * ------------------------------------------------------------------ + */ + +static inline u32 camerarx_read(struct cal_camerarx *phy, u32 offset) +{ + return ioread32(phy->base + offset); +} + +static inline void camerarx_write(struct cal_camerarx *phy, u32 offset, u32 val) +{ + iowrite32(val, phy->base + offset); +} + +/* ------------------------------------------------------------------ + * CAMERARX Management + * ------------------------------------------------------------------ + */ + +static s64 cal_camerarx_get_external_rate(struct cal_camerarx *phy) +{ + struct v4l2_ctrl *ctrl; + s64 rate; + + ctrl = v4l2_ctrl_find(phy->sensor->ctrl_handler, V4L2_CID_PIXEL_RATE); + if (!ctrl) { + phy_err(phy, "no pixel rate control in subdev: %s\n", + phy->sensor->name); + return -EPIPE; + } + + rate = v4l2_ctrl_g_ctrl_int64(ctrl); + phy_dbg(3, phy, "sensor Pixel Rate: %llu\n", rate); + + return rate; +} + +static void cal_camerarx_lane_config(struct cal_camerarx *phy) +{ + u32 val = cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)); + u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK; + u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK; + struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = + &phy->endpoint.bus.mipi_csi2; + int lane; + + cal_set_field(&val, mipi_csi2->clock_lane + 1, lane_mask); + cal_set_field(&val, mipi_csi2->lane_polarities[0], polarity_mask); + for (lane = 0; lane < mipi_csi2->num_data_lanes; lane++) { + /* + * Every lane are one nibble apart starting with the + * clock followed by the data lanes so shift masks by 4. + */ + lane_mask <<= 4; + polarity_mask <<= 4; + cal_set_field(&val, mipi_csi2->data_lanes[lane] + 1, lane_mask); + cal_set_field(&val, mipi_csi2->lane_polarities[lane + 1], + polarity_mask); + } + + cal_write(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), val); + phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n", + phy->instance, val); +} + +static void cal_camerarx_enable(struct cal_camerarx *phy) +{ + u32 num_lanes = phy->cal->data->camerarx[phy->instance].num_lanes; + + regmap_field_write(phy->fields[F_CAMMODE], 0); + /* Always enable all lanes at the phy control level */ + regmap_field_write(phy->fields[F_LANEENABLE], (1 << num_lanes) - 1); + /* F_CSI_MODE is not present on every architecture */ + if (phy->fields[F_CSI_MODE]) + regmap_field_write(phy->fields[F_CSI_MODE], 1); + regmap_field_write(phy->fields[F_CTRLCLKEN], 1); +} + +void cal_camerarx_disable(struct cal_camerarx *phy) +{ + regmap_field_write(phy->fields[F_CTRLCLKEN], 0); +} + +/* + * TCLK values are OK at their reset values + */ +#define TCLK_TERM 0 +#define TCLK_MISS 1 +#define TCLK_SETTLE 14 + +static void cal_camerarx_config(struct cal_camerarx *phy, s64 external_rate, + const struct cal_fmt *fmt) +{ + unsigned int reg0, reg1; + unsigned int ths_term, ths_settle; + unsigned int csi2_ddrclk_khz; + struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = + &phy->endpoint.bus.mipi_csi2; + u32 num_lanes = mipi_csi2->num_data_lanes; + + /* DPHY timing configuration */ + + /* + * CSI-2 is DDR and we only count used lanes. + * + * csi2_ddrclk_khz = external_rate / 1000 + * / (2 * num_lanes) * fmt->bpp; + */ + csi2_ddrclk_khz = div_s64(external_rate * fmt->bpp, + 2 * num_lanes * 1000); + + phy_dbg(1, phy, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz); + + /* THS_TERM: Programmed value = floor(20 ns/DDRClk period) */ + ths_term = 20 * csi2_ddrclk_khz / 1000000; + phy_dbg(1, phy, "ths_term: %d (0x%02x)\n", ths_term, ths_term); + + /* THS_SETTLE: Programmed value = floor(105 ns/DDRClk period) + 4 */ + ths_settle = (105 * csi2_ddrclk_khz / 1000000) + 4; + phy_dbg(1, phy, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle); + + reg0 = camerarx_read(phy, CAL_CSI2_PHY_REG0); + cal_set_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE, + CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK); + cal_set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK); + cal_set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK); + + phy_dbg(1, phy, "CSI2_%d_REG0 = 0x%08x\n", phy->instance, reg0); + camerarx_write(phy, CAL_CSI2_PHY_REG0, reg0); + + reg1 = camerarx_read(phy, CAL_CSI2_PHY_REG1); + cal_set_field(®1, TCLK_TERM, CAL_CSI2_PHY_REG1_TCLK_TERM_MASK); + cal_set_field(®1, 0xb8, CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK); + cal_set_field(®1, TCLK_MISS, + CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK); + cal_set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK); + + phy_dbg(1, phy, "CSI2_%d_REG1 = 0x%08x\n", phy->instance, reg1); + camerarx_write(phy, CAL_CSI2_PHY_REG1, reg1); +} + +static void cal_camerarx_power(struct cal_camerarx *phy, bool enable) +{ + u32 target_state; + unsigned int i; + + target_state = enable ? CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON : + CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_OFF; + + cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), + target_state, CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK); + + for (i = 0; i < 10; i++) { + u32 current_state; + + current_state = cal_read_field(phy->cal, + CAL_CSI2_COMPLEXIO_CFG(phy->instance), + CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK); + + if (current_state == target_state) + break; + + usleep_range(1000, 1100); + } + + if (i == 10) + phy_err(phy, "Failed to power %s complexio\n", + enable ? "up" : "down"); +} + +static void cal_camerarx_wait_reset(struct cal_camerarx *phy) +{ + unsigned long timeout; + + timeout = jiffies + msecs_to_jiffies(750); + while (time_before(jiffies, timeout)) { + if (cal_read_field(phy->cal, + CAL_CSI2_COMPLEXIO_CFG(phy->instance), + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) + break; + usleep_range(500, 5000); + } + + if (cal_read_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) != + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) + phy_err(phy, "Timeout waiting for Complex IO reset done\n"); +} + +static void cal_camerarx_wait_stop_state(struct cal_camerarx *phy) +{ + unsigned long timeout; + + timeout = jiffies + msecs_to_jiffies(750); + while (time_before(jiffies, timeout)) { + if (cal_read_field(phy->cal, + CAL_CSI2_TIMING(phy->instance), + CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) == 0) + break; + usleep_range(500, 5000); + } + + if (cal_read_field(phy->cal, CAL_CSI2_TIMING(phy->instance), + CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) != 0) + phy_err(phy, "Timeout waiting for stop state\n"); +} + +int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt) +{ + s64 external_rate; + u32 sscounter; + u32 val; + int ret; + + external_rate = cal_camerarx_get_external_rate(phy); + if (external_rate < 0) + return external_rate; + + ret = v4l2_subdev_call(phy->sensor, core, s_power, 1); + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) { + phy_err(phy, "power on failed in subdev\n"); + return ret; + } + + /* + * CSI-2 PHY Link Initialization Sequence, according to the DRA74xP / + * DRA75xP / DRA76xP / DRA77xP TRM. The DRA71x / DRA72x and the AM65x / + * DRA80xM TRMs have a a slightly simplified sequence. + */ + + /* + * 1. Configure all CSI-2 low level protocol registers to be ready to + * receive signals/data from the CSI-2 PHY. + * + * i.-v. Configure the lanes position and polarity. + */ + cal_camerarx_lane_config(phy); + + /* + * vi.-vii. Configure D-PHY mode, enable the required lanes and + * enable the CAMERARX clock. + */ + cal_camerarx_enable(phy); + + /* + * 2. CSI PHY and link initialization sequence. + * + * a. Deassert the CSI-2 PHY reset. Do not wait for reset completion + * at this point, as it requires the external sensor to send the + * CSI-2 HS clock. + */ + cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), + CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL, + CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); + phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x De-assert Complex IO Reset\n", + phy->instance, + cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance))); + + /* Dummy read to allow SCP reset to complete. */ + camerarx_read(phy, CAL_CSI2_PHY_REG0); + + /* Program the PHY timing parameters. */ + cal_camerarx_config(phy, external_rate, fmt); + + /* + * b. Assert the FORCERXMODE signal. + * + * The stop-state-counter is based on fclk cycles, and we always use + * the x16 and x4 settings, so stop-state-timeout = + * fclk-cycle * 16 * 4 * counter. + * + * Stop-state-timeout must be more than 100us as per CSI-2 spec, so we + * calculate a timeout that's 100us (rounding up). + */ + sscounter = DIV_ROUND_UP(clk_get_rate(phy->cal->fclk), 10000 * 16 * 4); + + val = cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance)); + cal_set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK); + cal_set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK); + cal_set_field(&val, sscounter, + CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK); + cal_write(phy->cal, CAL_CSI2_TIMING(phy->instance), val); + phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Stop States\n", + phy->instance, + cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance))); + + /* Assert the FORCERXMODE signal. */ + cal_write_field(phy->cal, CAL_CSI2_TIMING(phy->instance), + 1, CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK); + phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Force RXMODE\n", + phy->instance, + cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance))); + + /* + * c. Connect pull-down on CSI-2 PHY link (using pad control). + * + * This is not required on DRA71x, DRA72x, AM65x and DRA80xM. Not + * implemented. + */ + + /* + * d. Power up the CSI-2 PHY. + * e. Check whether the state status reaches the ON state. + */ + cal_camerarx_power(phy, true); + + /* + * Start the sensor to enable the CSI-2 HS clock. We can now wait for + * CSI-2 PHY reset to complete. + */ + ret = v4l2_subdev_call(phy->sensor, video, s_stream, 1); + if (ret) { + v4l2_subdev_call(phy->sensor, core, s_power, 0); + phy_err(phy, "stream on failed in subdev\n"); + return ret; + } + + cal_camerarx_wait_reset(phy); + + /* f. Wait for STOPSTATE=1 for all enabled lane modules. */ + cal_camerarx_wait_stop_state(phy); + + phy_dbg(1, phy, "CSI2_%u_REG1 = 0x%08x (bits 31-28 should be set)\n", + phy->instance, camerarx_read(phy, CAL_CSI2_PHY_REG1)); + + /* + * g. Disable pull-down on CSI-2 PHY link (using pad control). + * + * This is not required on DRA71x, DRA72x, AM65x and DRA80xM. Not + * implemented. + */ + + return 0; +} + +void cal_camerarx_stop(struct cal_camerarx *phy) +{ + unsigned int i; + int ret; + + cal_camerarx_power(phy, false); + + /* Assert Complex IO Reset */ + cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), + CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL, + CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); + + /* Wait for power down completion */ + for (i = 0; i < 10; i++) { + if (cal_read_field(phy->cal, + CAL_CSI2_COMPLEXIO_CFG(phy->instance), + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETONGOING) + break; + usleep_range(1000, 1100); + } + phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Complex IO in Reset (%d) %s\n", + phy->instance, + cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)), i, + (i >= 10) ? "(timeout)" : ""); + + /* Disable the phy */ + cal_camerarx_disable(phy); + + if (v4l2_subdev_call(phy->sensor, video, s_stream, 0)) + phy_err(phy, "stream off failed in subdev\n"); + + ret = v4l2_subdev_call(phy->sensor, core, s_power, 0); + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) + phy_err(phy, "power off failed in subdev\n"); +} + +/* + * Errata i913: CSI2 LDO Needs to be disabled when module is powered on + * + * Enabling CSI2 LDO shorts it to core supply. It is crucial the 2 CSI2 + * LDOs on the device are disabled if CSI-2 module is powered on + * (0x4845 B304 | 0x4845 B384 [28:27] = 0x1) or in ULPS (0x4845 B304 + * | 0x4845 B384 [28:27] = 0x2) mode. Common concerns include: high + * current draw on the module supply in active mode. + * + * Errata does not apply when CSI-2 module is powered off + * (0x4845 B304 | 0x4845 B384 [28:27] = 0x0). + * + * SW Workaround: + * Set the following register bits to disable the LDO, + * which is essentially CSI2 REG10 bit 6: + * + * Core 0: 0x4845 B828 = 0x0000 0040 + * Core 1: 0x4845 B928 = 0x0000 0040 + */ +void cal_camerarx_i913_errata(struct cal_camerarx *phy) +{ + u32 reg10 = camerarx_read(phy, CAL_CSI2_PHY_REG10); + + cal_set_field(®10, 1, CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK); + + phy_dbg(1, phy, "CSI2_%d_REG10 = 0x%08x\n", phy->instance, reg10); + camerarx_write(phy, CAL_CSI2_PHY_REG10, reg10); +} + +/* + * Enable the expected IRQ sources + */ +void cal_camerarx_enable_irqs(struct cal_camerarx *phy) +{ + u32 val; + + const u32 cio_err_mask = + CAL_CSI2_COMPLEXIO_IRQ_LANE_ERRORS_MASK | + CAL_CSI2_COMPLEXIO_IRQ_FIFO_OVR_MASK | + CAL_CSI2_COMPLEXIO_IRQ_SHORT_PACKET_MASK | + CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK; + + /* Enable CIO error irqs */ + cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0), + CAL_HL_IRQ_CIO_MASK(phy->instance)); + cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), + cio_err_mask); + + /* Always enable OCPO error */ + cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK); + + /* Enable IRQ_WDMA_END 0/1 */ + val = 0; + cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + cal_write(phy->cal, CAL_HL_IRQENABLE_SET(1), val); + /* Enable IRQ_WDMA_START 0/1 */ + val = 0; + cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + cal_write(phy->cal, CAL_HL_IRQENABLE_SET(2), val); + /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ + cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0xFF000000); +} + +void cal_camerarx_disable_irqs(struct cal_camerarx *phy) +{ + u32 val; + + /* Disable CIO error irqs */ + cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(0), + CAL_HL_IRQ_CIO_MASK(phy->instance)); + cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), 0); + + /* Disable IRQ_WDMA_END 0/1 */ + val = 0; + cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(1), val); + /* Disable IRQ_WDMA_START 0/1 */ + val = 0; + cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(2), val); + /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ + cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0); +} + +void cal_camerarx_ppi_enable(struct cal_camerarx *phy) +{ + cal_write(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), BIT(3)); + cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), + 1, CAL_CSI2_PPI_CTRL_IF_EN_MASK); +} + +void cal_camerarx_ppi_disable(struct cal_camerarx *phy) +{ + cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), + 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK); +} + +static int cal_camerarx_regmap_init(struct cal_dev *cal, + struct cal_camerarx *phy) +{ + const struct cal_camerarx_data *phy_data; + unsigned int i; + + if (!cal->data) + return -EINVAL; + + phy_data = &cal->data->camerarx[phy->instance]; + + for (i = 0; i < F_MAX_FIELDS; i++) { + struct reg_field field = { + .reg = cal->syscon_camerrx_offset, + .lsb = phy_data->fields[i].lsb, + .msb = phy_data->fields[i].msb, + }; + + /* + * Here we update the reg offset with the + * value found in DT + */ + phy->fields[i] = devm_regmap_field_alloc(cal->dev, + cal->syscon_camerrx, + field); + if (IS_ERR(phy->fields[i])) { + cal_err(cal, "Unable to allocate regmap fields\n"); + return PTR_ERR(phy->fields[i]); + } + } + + return 0; +} + +static int cal_camerarx_parse_dt(struct cal_camerarx *phy) +{ + struct v4l2_fwnode_endpoint *endpoint = &phy->endpoint; + struct device_node *ep_node; + char data_lanes[V4L2_FWNODE_CSI2_MAX_DATA_LANES * 2]; + unsigned int i; + int ret; + + /* + * Find the endpoint node for the port corresponding to the PHY + * instance, and parse its CSI-2-related properties. + */ + ep_node = of_graph_get_endpoint_by_regs(phy->cal->dev->of_node, + phy->instance, 0); + if (!ep_node) { + /* + * The endpoint is not mandatory, not all PHY instances need to + * be connected in DT. + */ + phy_dbg(3, phy, "Port has no endpoint\n"); + return 0; + } + + endpoint->bus_type = V4L2_MBUS_CSI2_DPHY; + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), endpoint); + if (ret < 0) { + phy_err(phy, "Failed to parse endpoint\n"); + goto done; + } + + for (i = 0; i < endpoint->bus.mipi_csi2.num_data_lanes; i++) { + unsigned int lane = endpoint->bus.mipi_csi2.data_lanes[i]; + + if (lane > 4) { + phy_err(phy, "Invalid position %u for data lane %u\n", + lane, i); + ret = -EINVAL; + goto done; + } + + data_lanes[i*2] = '0' + lane; + data_lanes[i*2+1] = ' '; + } + + data_lanes[i*2-1] = '\0'; + + phy_dbg(3, phy, + "CSI-2 bus: clock lane <%u>, data lanes <%s>, flags 0x%08x\n", + endpoint->bus.mipi_csi2.clock_lane, data_lanes, + endpoint->bus.mipi_csi2.flags); + + /* Retrieve the connected device and store it for later use. */ + phy->sensor_node = of_graph_get_remote_port_parent(ep_node); + if (!phy->sensor_node) { + phy_dbg(3, phy, "Can't get remote parent\n"); + ret = -EINVAL; + goto done; + } + + phy_dbg(1, phy, "Found connected device %pOFn\n", phy->sensor_node); + +done: + of_node_put(ep_node); + return ret; +} + +struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, + unsigned int instance) +{ + struct platform_device *pdev = to_platform_device(cal->dev); + struct cal_camerarx *phy; + int ret; + + phy = kzalloc(sizeof(*phy), GFP_KERNEL); + if (!phy) + return ERR_PTR(-ENOMEM); + + phy->cal = cal; + phy->instance = instance; + + phy->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + (instance == 0) ? + "cal_rx_core0" : + "cal_rx_core1"); + phy->base = devm_ioremap_resource(cal->dev, phy->res); + if (IS_ERR(phy->base)) { + cal_err(cal, "failed to ioremap\n"); + ret = PTR_ERR(phy->base); + goto error; + } + + cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", + phy->res->name, &phy->res->start, &phy->res->end); + + ret = cal_camerarx_regmap_init(cal, phy); + if (ret) + goto error; + + ret = cal_camerarx_parse_dt(phy); + if (ret) + goto error; + + return phy; + +error: + kfree(phy); + return ERR_PTR(ret); +} + +void cal_camerarx_destroy(struct cal_camerarx *phy) +{ + if (!phy) + return; + + of_node_put(phy->sensor_node); + kfree(phy); +} diff --git a/drivers/media/platform/ti-vpe/cal-video.c b/drivers/media/platform/ti-vpe/cal-video.c new file mode 100644 index 000000000000..df472a175e83 --- /dev/null +++ b/drivers/media/platform/ti-vpe/cal-video.c @@ -0,0 +1,886 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * TI Camera Access Layer (CAL) - Video Device + * + * Copyright (c) 2015-2020 Texas Instruments Inc. + * + * Authors: + * Benoit Parrot <bparrot@ti.com> + * Laurent Pinchart <laurent.pinchart@ideasonboard.com> + */ + +#include <linux/delay.h> +#include <linux/ioctl.h> +#include <linux/pm_runtime.h> +#include <linux/videodev2.h> + +#include <media/media-device.h> +#include <media/v4l2-common.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-ioctl.h> +#include <media/videobuf2-core.h> +#include <media/videobuf2-dma-contig.h> + +#include "cal.h" + +/* ------------------------------------------------------------------ + * Format Handling + * ------------------------------------------------------------------ + */ + +static const struct cal_fmt cal_formats[] = { + { + .fourcc = V4L2_PIX_FMT_YUYV, + .code = MEDIA_BUS_FMT_YUYV8_2X8, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_UYVY, + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_YVYU, + .code = MEDIA_BUS_FMT_YVYU8_2X8, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_VYUY, + .code = MEDIA_BUS_FMT_VYUY8_2X8, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ + .code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ + .code = MEDIA_BUS_FMT_RGB565_2X8_BE, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */ + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */ + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ + .code = MEDIA_BUS_FMT_RGB888_2X12_LE, + .bpp = 24, + }, { + .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ + .code = MEDIA_BUS_FMT_RGB888_2X12_BE, + .bpp = 24, + }, { + .fourcc = V4L2_PIX_FMT_RGB32, /* argb */ + .code = MEDIA_BUS_FMT_ARGB8888_1X32, + .bpp = 32, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR8, + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .bpp = 8, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG8, + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .bpp = 8, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .bpp = 8, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB8, + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .bpp = 8, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .bpp = 10, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG10, + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .bpp = 10, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG10, + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .bpp = 10, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB10, + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .bpp = 10, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR12, + .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .bpp = 12, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG12, + .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .bpp = 12, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG12, + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .bpp = 12, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB12, + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .bpp = 12, + }, +}; + +/* Print Four-character-code (FOURCC) */ +static char *fourcc_to_str(u32 fmt) +{ + static char code[5]; + + code[0] = (unsigned char)(fmt & 0xff); + code[1] = (unsigned char)((fmt >> 8) & 0xff); + code[2] = (unsigned char)((fmt >> 16) & 0xff); + code[3] = (unsigned char)((fmt >> 24) & 0xff); + code[4] = '\0'; + + return code; +} + +/* ------------------------------------------------------------------ + * V4L2 Video IOCTLs + * ------------------------------------------------------------------ + */ + +static const struct cal_fmt *find_format_by_pix(struct cal_ctx *ctx, + u32 pixelformat) +{ + const struct cal_fmt *fmt; + unsigned int k; + + for (k = 0; k < ctx->num_active_fmt; k++) { + fmt = ctx->active_fmt[k]; + if (fmt->fourcc == pixelformat) + return fmt; + } + + return NULL; +} + +static const struct cal_fmt *find_format_by_code(struct cal_ctx *ctx, + u32 code) +{ + const struct cal_fmt *fmt; + unsigned int k; + + for (k = 0; k < ctx->num_active_fmt; k++) { + fmt = ctx->active_fmt[k]; + if (fmt->code == code) + return fmt; + } + + return NULL; +} + +static int cal_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct cal_ctx *ctx = video_drvdata(file); + + strscpy(cap->driver, CAL_MODULE_NAME, sizeof(cap->driver)); + strscpy(cap->card, CAL_MODULE_NAME, sizeof(cap->card)); + + snprintf(cap->bus_info, sizeof(cap->bus_info), + "platform:%s", dev_name(ctx->cal->dev)); + return 0; +} + +static int cal_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct cal_ctx *ctx = video_drvdata(file); + const struct cal_fmt *fmt; + + if (f->index >= ctx->num_active_fmt) + return -EINVAL; + + fmt = ctx->active_fmt[f->index]; + + f->pixelformat = fmt->fourcc; + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + return 0; +} + +static int __subdev_get_format(struct cal_ctx *ctx, + struct v4l2_mbus_framefmt *fmt) +{ + struct v4l2_subdev_format sd_fmt; + struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; + int ret; + + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + sd_fmt.pad = 0; + + ret = v4l2_subdev_call(ctx->phy->sensor, pad, get_fmt, NULL, &sd_fmt); + if (ret) + return ret; + + *fmt = *mbus_fmt; + + ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__, + fmt->width, fmt->height, fmt->code); + + return 0; +} + +static int __subdev_set_format(struct cal_ctx *ctx, + struct v4l2_mbus_framefmt *fmt) +{ + struct v4l2_subdev_format sd_fmt; + struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; + int ret; + + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + sd_fmt.pad = 0; + *mbus_fmt = *fmt; + + ret = v4l2_subdev_call(ctx->phy->sensor, pad, set_fmt, NULL, &sd_fmt); + if (ret) + return ret; + + ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__, + fmt->width, fmt->height, fmt->code); + + return 0; +} + +static int cal_calc_format_size(struct cal_ctx *ctx, + const struct cal_fmt *fmt, + struct v4l2_format *f) +{ + u32 bpl, max_width; + + if (!fmt) { + ctx_dbg(3, ctx, "No cal_fmt provided!\n"); + return -EINVAL; + } + + /* + * Maximum width is bound by the DMA max width in bytes. + * We need to recalculate the actual maxi width depending on the + * number of bytes per pixels required. + */ + max_width = MAX_WIDTH_BYTES / (ALIGN(fmt->bpp, 8) >> 3); + v4l_bound_align_image(&f->fmt.pix.width, 48, max_width, 2, + &f->fmt.pix.height, 32, MAX_HEIGHT_LINES, 0, 0); + + bpl = (f->fmt.pix.width * ALIGN(fmt->bpp, 8)) >> 3; + f->fmt.pix.bytesperline = ALIGN(bpl, 16); + + f->fmt.pix.sizeimage = f->fmt.pix.height * + f->fmt.pix.bytesperline; + + ctx_dbg(3, ctx, "%s: fourcc: %s size: %dx%d bpl:%d img_size:%d\n", + __func__, fourcc_to_str(f->fmt.pix.pixelformat), + f->fmt.pix.width, f->fmt.pix.height, + f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); + + return 0; +} + +static int cal_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cal_ctx *ctx = video_drvdata(file); + + *f = ctx->v_fmt; + + return 0; +} + +static int cal_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cal_ctx *ctx = video_drvdata(file); + const struct cal_fmt *fmt; + struct v4l2_subdev_frame_size_enum fse; + int ret, found; + + fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat); + if (!fmt) { + ctx_dbg(3, ctx, "Fourcc format (0x%08x) not found.\n", + f->fmt.pix.pixelformat); + + /* Just get the first one enumerated */ + fmt = ctx->active_fmt[0]; + f->fmt.pix.pixelformat = fmt->fourcc; + } + + f->fmt.pix.field = ctx->v_fmt.fmt.pix.field; + + /* check for/find a valid width/height */ + ret = 0; + found = false; + fse.pad = 0; + fse.code = fmt->code; + fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; + for (fse.index = 0; ; fse.index++) { + ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_size, + NULL, &fse); + if (ret) + break; + + if ((f->fmt.pix.width == fse.max_width) && + (f->fmt.pix.height == fse.max_height)) { + found = true; + break; + } else if ((f->fmt.pix.width >= fse.min_width) && + (f->fmt.pix.width <= fse.max_width) && + (f->fmt.pix.height >= fse.min_height) && + (f->fmt.pix.height <= fse.max_height)) { + found = true; + break; + } + } + + if (!found) { + /* use existing values as default */ + f->fmt.pix.width = ctx->v_fmt.fmt.pix.width; + f->fmt.pix.height = ctx->v_fmt.fmt.pix.height; + } + + /* + * Use current colorspace for now, it will get + * updated properly during s_fmt + */ + f->fmt.pix.colorspace = ctx->v_fmt.fmt.pix.colorspace; + return cal_calc_format_size(ctx, fmt, f); +} + +static int cal_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cal_ctx *ctx = video_drvdata(file); + struct vb2_queue *q = &ctx->vb_vidq; + const struct cal_fmt *fmt; + struct v4l2_mbus_framefmt mbus_fmt; + int ret; + + if (vb2_is_busy(q)) { + ctx_dbg(3, ctx, "%s device busy\n", __func__); + return -EBUSY; + } + + ret = cal_try_fmt_vid_cap(file, priv, f); + if (ret < 0) + return ret; + + fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat); + + v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code); + + ret = __subdev_set_format(ctx, &mbus_fmt); + if (ret) + return ret; + + /* Just double check nothing has gone wrong */ + if (mbus_fmt.code != fmt->code) { + ctx_dbg(3, ctx, + "%s subdev changed format on us, this should not happen\n", + __func__); + return -EINVAL; + } + + v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); + ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; + cal_calc_format_size(ctx, fmt, &ctx->v_fmt); + ctx->fmt = fmt; + ctx->m_fmt = mbus_fmt; + *f = ctx->v_fmt; + + return 0; +} + +static int cal_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct cal_ctx *ctx = video_drvdata(file); + const struct cal_fmt *fmt; + struct v4l2_subdev_frame_size_enum fse; + int ret; + + /* check for valid format */ + fmt = find_format_by_pix(ctx, fsize->pixel_format); + if (!fmt) { + ctx_dbg(3, ctx, "Invalid pixel code: %x\n", + fsize->pixel_format); + return -EINVAL; + } + + fse.index = fsize->index; + fse.pad = 0; + fse.code = fmt->code; + fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; + + ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_size, NULL, + &fse); + if (ret) + return ret; + + ctx_dbg(1, ctx, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n", + __func__, fse.index, fse.code, fse.min_width, fse.max_width, + fse.min_height, fse.max_height); + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = fse.max_width; + fsize->discrete.height = fse.max_height; + + return 0; +} + +static int cal_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + if (inp->index > 0) + return -EINVAL; + + inp->type = V4L2_INPUT_TYPE_CAMERA; + sprintf(inp->name, "Camera %u", inp->index); + return 0; +} + +static int cal_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int cal_s_input(struct file *file, void *priv, unsigned int i) +{ + return i > 0 ? -EINVAL : 0; +} + +/* timeperframe is arbitrary and continuous */ +static int cal_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fival) +{ + struct cal_ctx *ctx = video_drvdata(file); + const struct cal_fmt *fmt; + struct v4l2_subdev_frame_interval_enum fie = { + .index = fival->index, + .width = fival->width, + .height = fival->height, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + int ret; + + fmt = find_format_by_pix(ctx, fival->pixel_format); + if (!fmt) + return -EINVAL; + + fie.code = fmt->code; + ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_interval, + NULL, &fie); + if (ret) + return ret; + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete = fie.interval; + + return 0; +} + +static const struct v4l2_file_operations cal_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ + .mmap = vb2_fop_mmap, +}; + +static const struct v4l2_ioctl_ops cal_ioctl_ops = { + .vidioc_querycap = cal_querycap, + .vidioc_enum_fmt_vid_cap = cal_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cal_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cal_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = cal_s_fmt_vid_cap, + .vidioc_enum_framesizes = cal_enum_framesizes, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_enum_input = cal_enum_input, + .vidioc_g_input = cal_g_input, + .vidioc_s_input = cal_s_input, + .vidioc_enum_frameintervals = cal_enum_frameintervals, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/* ------------------------------------------------------------------ + * videobuf2 Operations + * ------------------------------------------------------------------ + */ + +static int cal_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct cal_ctx *ctx = vb2_get_drv_priv(vq); + unsigned int size = ctx->v_fmt.fmt.pix.sizeimage; + + if (vq->num_buffers + *nbuffers < 3) + *nbuffers = 3 - vq->num_buffers; + + if (*nplanes) { + if (sizes[0] < size) + return -EINVAL; + size = sizes[0]; + } + + *nplanes = 1; + sizes[0] = size; + + ctx_dbg(3, ctx, "nbuffers=%d, size=%d\n", *nbuffers, sizes[0]); + + return 0; +} + +static int cal_buffer_prepare(struct vb2_buffer *vb) +{ + struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct cal_buffer *buf = container_of(vb, struct cal_buffer, + vb.vb2_buf); + unsigned long size; + + if (WARN_ON(!ctx->fmt)) + return -EINVAL; + + size = ctx->v_fmt.fmt.pix.sizeimage; + if (vb2_plane_size(vb, 0) < size) { + ctx_err(ctx, + "data will not fit into plane (%lu < %lu)\n", + vb2_plane_size(vb, 0), size); + return -EINVAL; + } + + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); + return 0; +} + +static void cal_buffer_queue(struct vb2_buffer *vb) +{ + struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct cal_buffer *buf = container_of(vb, struct cal_buffer, + vb.vb2_buf); + struct cal_dmaqueue *vidq = &ctx->vidq; + unsigned long flags; + + /* recheck locking */ + spin_lock_irqsave(&ctx->slock, flags); + list_add_tail(&buf->list, &vidq->active); + spin_unlock_irqrestore(&ctx->slock, flags); +} + +static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct cal_ctx *ctx = vb2_get_drv_priv(vq); + struct cal_dmaqueue *dma_q = &ctx->vidq; + struct cal_buffer *buf, *tmp; + unsigned long addr; + unsigned long flags; + int ret; + + spin_lock_irqsave(&ctx->slock, flags); + if (list_empty(&dma_q->active)) { + spin_unlock_irqrestore(&ctx->slock, flags); + ctx_dbg(3, ctx, "buffer queue is empty\n"); + return -EIO; + } + + buf = list_entry(dma_q->active.next, struct cal_buffer, list); + ctx->cur_frm = buf; + ctx->next_frm = buf; + list_del(&buf->list); + spin_unlock_irqrestore(&ctx->slock, flags); + + addr = vb2_dma_contig_plane_dma_addr(&ctx->cur_frm->vb.vb2_buf, 0); + ctx->sequence = 0; + + pm_runtime_get_sync(ctx->cal->dev); + + cal_ctx_csi2_config(ctx); + cal_ctx_pix_proc_config(ctx); + cal_ctx_wr_dma_config(ctx, ctx->v_fmt.fmt.pix.bytesperline, + ctx->v_fmt.fmt.pix.height); + + cal_camerarx_enable_irqs(ctx->phy); + + ret = cal_camerarx_start(ctx->phy, ctx->fmt); + if (ret) + goto err; + + cal_ctx_wr_dma_addr(ctx, addr); + cal_camerarx_ppi_enable(ctx->phy); + + if (cal_debug >= 4) + cal_quickdump_regs(ctx->cal); + + return 0; + +err: + spin_lock_irqsave(&ctx->slock, flags); + vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_QUEUED); + ctx->cur_frm = NULL; + ctx->next_frm = NULL; + list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); + } + spin_unlock_irqrestore(&ctx->slock, flags); + return ret; +} + +static void cal_stop_streaming(struct vb2_queue *vq) +{ + struct cal_ctx *ctx = vb2_get_drv_priv(vq); + struct cal_dmaqueue *dma_q = &ctx->vidq; + struct cal_buffer *buf, *tmp; + unsigned long timeout; + unsigned long flags; + bool dma_act; + + cal_camerarx_ppi_disable(ctx->phy); + + /* wait for stream and dma to finish */ + dma_act = true; + timeout = jiffies + msecs_to_jiffies(500); + while (dma_act && time_before(jiffies, timeout)) { + msleep(50); + + spin_lock_irqsave(&ctx->slock, flags); + dma_act = ctx->dma_act; + spin_unlock_irqrestore(&ctx->slock, flags); + } + + if (dma_act) + ctx_err(ctx, "failed to disable dma cleanly\n"); + + cal_camerarx_disable_irqs(ctx->phy); + cal_camerarx_stop(ctx->phy); + + /* Release all active buffers */ + spin_lock_irqsave(&ctx->slock, flags); + list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } + + if (ctx->cur_frm == ctx->next_frm) { + vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } else { + vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&ctx->next_frm->vb.vb2_buf, + VB2_BUF_STATE_ERROR); + } + ctx->cur_frm = NULL; + ctx->next_frm = NULL; + spin_unlock_irqrestore(&ctx->slock, flags); + + pm_runtime_put_sync(ctx->cal->dev); +} + +static const struct vb2_ops cal_video_qops = { + .queue_setup = cal_queue_setup, + .buf_prepare = cal_buffer_prepare, + .buf_queue = cal_buffer_queue, + .start_streaming = cal_start_streaming, + .stop_streaming = cal_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +/* ------------------------------------------------------------------ + * V4L2 Initialization and Registration + * ------------------------------------------------------------------ + */ + +static const struct video_device cal_videodev = { + .name = CAL_MODULE_NAME, + .fops = &cal_fops, + .ioctl_ops = &cal_ioctl_ops, + .minor = -1, + .release = video_device_release_empty, + .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE, +}; + +static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) +{ + struct v4l2_subdev_mbus_code_enum mbus_code; + struct v4l2_mbus_framefmt mbus_fmt; + const struct cal_fmt *fmt; + unsigned int i, j, k; + int ret = 0; + + /* Enumerate sub device formats and enable all matching local formats */ + ctx->active_fmt = devm_kcalloc(ctx->cal->dev, ARRAY_SIZE(cal_formats), + sizeof(*ctx->active_fmt), GFP_KERNEL); + ctx->num_active_fmt = 0; + + for (j = 0, i = 0; ret != -EINVAL; ++j) { + + memset(&mbus_code, 0, sizeof(mbus_code)); + mbus_code.index = j; + mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_mbus_code, + NULL, &mbus_code); + if (ret) + continue; + + ctx_dbg(2, ctx, + "subdev %s: code: %04x idx: %u\n", + ctx->phy->sensor->name, mbus_code.code, j); + + for (k = 0; k < ARRAY_SIZE(cal_formats); k++) { + const struct cal_fmt *fmt = &cal_formats[k]; + + if (mbus_code.code == fmt->code) { + ctx->active_fmt[i] = fmt; + ctx_dbg(2, ctx, + "matched fourcc: %s: code: %04x idx: %u\n", + fourcc_to_str(fmt->fourcc), + fmt->code, i); + ctx->num_active_fmt = ++i; + } + } + } + + if (i == 0) { + ctx_err(ctx, "No suitable format reported by subdev %s\n", + ctx->phy->sensor->name); + return -EINVAL; + } + + ret = __subdev_get_format(ctx, &mbus_fmt); + if (ret) + return ret; + + fmt = find_format_by_code(ctx, mbus_fmt.code); + if (!fmt) { + ctx_dbg(3, ctx, "mbus code format (0x%08x) not found.\n", + mbus_fmt.code); + return -EINVAL; + } + + /* Save current subdev format */ + v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); + ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; + cal_calc_format_size(ctx, fmt, &ctx->v_fmt); + ctx->fmt = fmt; + ctx->m_fmt = mbus_fmt; + + return 0; +} + +int cal_ctx_v4l2_register(struct cal_ctx *ctx) +{ + struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; + struct video_device *vfd = &ctx->vdev; + int ret; + + ret = cal_ctx_v4l2_init_formats(ctx); + if (ret) + return ret; + + ret = v4l2_ctrl_add_handler(hdl, ctx->phy->sensor->ctrl_handler, NULL, + true); + if (ret < 0) { + ctx_err(ctx, "Failed to add sensor ctrl handler\n"); + return ret; + } + + ret = video_register_device(vfd, VFL_TYPE_VIDEO, cal_video_nr); + if (ret < 0) { + ctx_err(ctx, "Failed to register video device\n"); + return ret; + } + + ctx_info(ctx, "V4L2 device registered as %s\n", + video_device_node_name(vfd)); + + return 0; +} + +void cal_ctx_v4l2_unregister(struct cal_ctx *ctx) +{ + ctx_dbg(1, ctx, "unregistering %s\n", + video_device_node_name(&ctx->vdev)); + + video_unregister_device(&ctx->vdev); +} + +int cal_ctx_v4l2_init(struct cal_ctx *ctx) +{ + struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; + struct video_device *vfd = &ctx->vdev; + struct vb2_queue *q = &ctx->vb_vidq; + int ret; + + INIT_LIST_HEAD(&ctx->vidq.active); + spin_lock_init(&ctx->slock); + mutex_init(&ctx->mutex); + + /* Initialize the vb2 queue. */ + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; + q->drv_priv = ctx; + q->buf_struct_size = sizeof(struct cal_buffer); + q->ops = &cal_video_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &ctx->mutex; + q->min_buffers_needed = 3; + q->dev = ctx->cal->dev; + + ret = vb2_queue_init(q); + if (ret) + return ret; + + /* Initialize the video device and media entity. */ + *vfd = cal_videodev; + vfd->v4l2_dev = &ctx->cal->v4l2_dev; + vfd->queue = q; + snprintf(vfd->name, sizeof(vfd->name), "CAL output %u", ctx->index); + vfd->lock = &ctx->mutex; + video_set_drvdata(vfd, ctx); + + ctx->pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&vfd->entity, 1, &ctx->pad); + if (ret < 0) + return ret; + + /* Initialize the control handler. */ + ret = v4l2_ctrl_handler_init(hdl, 11); + if (ret < 0) { + ctx_err(ctx, "Failed to init ctrl handler\n"); + goto error; + } + + vfd->ctrl_handler = hdl; + + return 0; + +error: + media_entity_cleanup(&vfd->entity); + return ret; +} + +void cal_ctx_v4l2_cleanup(struct cal_ctx *ctx) +{ + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + media_entity_cleanup(&ctx->vdev.entity); +} diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 9b18db7af6c3..59a0266b1f39 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1,976 +1,167 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * TI CAL camera interface driver + * TI Camera Access Layer (CAL) - Driver * - * Copyright (c) 2015 Texas Instruments Inc. - * Benoit Parrot, <bparrot@ti.com> + * Copyright (c) 2015-2020 Texas Instruments Inc. + * + * Authors: + * Benoit Parrot <bparrot@ti.com> + * Laurent Pinchart <laurent.pinchart@ideasonboard.com> */ #include <linux/clk.h> #include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/ioctl.h> +#include <linux/mfd/syscon.h> #include <linux/module.h> +#include <linux/of_device.h> #include <linux/platform_device.h> -#include <linux/delay.h> #include <linux/pm_runtime.h> -#include <linux/slab.h> -#include <linux/mfd/syscon.h> #include <linux/regmap.h> +#include <linux/slab.h> #include <linux/videodev2.h> -#include <linux/of_device.h> -#include <linux/of_graph.h> -#include <media/v4l2-fwnode.h> +#include <media/media-device.h> #include <media/v4l2-async.h> #include <media/v4l2-common.h> -#include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> -#include <media/v4l2-event.h> -#include <media/v4l2-ioctl.h> -#include <media/v4l2-fh.h> #include <media/videobuf2-core.h> #include <media/videobuf2-dma-contig.h> -#include "cal_regs.h" - -#define CAL_MODULE_NAME "cal" - -#define MAX_WIDTH_BYTES (8192 * 8) -#define MAX_HEIGHT_LINES 16383 -#define CAL_VERSION "0.1.0" +#include "cal.h" +#include "cal_regs.h" MODULE_DESCRIPTION("TI CAL driver"); MODULE_AUTHOR("Benoit Parrot, <bparrot@ti.com>"); MODULE_LICENSE("GPL v2"); -MODULE_VERSION(CAL_VERSION); +MODULE_VERSION("0.1.0"); -static unsigned video_nr = -1; -module_param(video_nr, uint, 0644); +int cal_video_nr = -1; +module_param_named(video_nr, cal_video_nr, uint, 0644); MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect"); -static unsigned debug; -module_param(debug, uint, 0644); +unsigned int cal_debug; +module_param_named(debug, cal_debug, uint, 0644); MODULE_PARM_DESC(debug, "activates debug info"); -/* timeperframe: min/max and default */ -static const struct v4l2_fract - tpf_default = {.numerator = 1001, .denominator = 30000}; - -#define cal_dbg(level, caldev, fmt, arg...) \ - v4l2_dbg(level, debug, &caldev->v4l2_dev, fmt, ##arg) -#define cal_info(caldev, fmt, arg...) \ - v4l2_info(&caldev->v4l2_dev, fmt, ##arg) -#define cal_err(caldev, fmt, arg...) \ - v4l2_err(&caldev->v4l2_dev, fmt, ##arg) - -#define ctx_dbg(level, ctx, fmt, arg...) \ - v4l2_dbg(level, debug, &ctx->v4l2_dev, fmt, ##arg) -#define ctx_info(ctx, fmt, arg...) \ - v4l2_info(&ctx->v4l2_dev, fmt, ##arg) -#define ctx_err(ctx, fmt, arg...) \ - v4l2_err(&ctx->v4l2_dev, fmt, ##arg) - -#define CAL_NUM_INPUT 1 -#define CAL_NUM_CONTEXT 2 - -#define reg_read(dev, offset) ioread32(dev->base + offset) -#define reg_write(dev, offset, val) iowrite32(val, dev->base + offset) - -#define reg_read_field(dev, offset, mask) get_field(reg_read(dev, offset), \ - mask) -#define reg_write_field(dev, offset, field, mask) { \ - u32 val = reg_read(dev, offset); \ - set_field(&val, field, mask); \ - reg_write(dev, offset, val); } - /* ------------------------------------------------------------------ - * Basic structures + * Platform Data * ------------------------------------------------------------------ */ -struct cal_fmt { - u32 fourcc; - u32 code; - /* Bits per pixel */ - u8 bpp; -}; - -static struct cal_fmt cal_formats[] = { - { - .fourcc = V4L2_PIX_FMT_YUYV, - .code = MEDIA_BUS_FMT_YUYV8_2X8, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_UYVY, - .code = MEDIA_BUS_FMT_UYVY8_2X8, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_YVYU, - .code = MEDIA_BUS_FMT_YVYU8_2X8, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_VYUY, - .code = MEDIA_BUS_FMT_VYUY8_2X8, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ - .code = MEDIA_BUS_FMT_RGB565_2X8_LE, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ - .code = MEDIA_BUS_FMT_RGB565_2X8_BE, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */ - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */ - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ - .code = MEDIA_BUS_FMT_RGB888_2X12_LE, - .bpp = 24, - }, { - .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ - .code = MEDIA_BUS_FMT_RGB888_2X12_BE, - .bpp = 24, - }, { - .fourcc = V4L2_PIX_FMT_RGB32, /* argb */ - .code = MEDIA_BUS_FMT_ARGB8888_1X32, - .bpp = 32, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR8, - .code = MEDIA_BUS_FMT_SBGGR8_1X8, - .bpp = 8, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG8, - .code = MEDIA_BUS_FMT_SGBRG8_1X8, - .bpp = 8, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG8, - .code = MEDIA_BUS_FMT_SGRBG8_1X8, - .bpp = 8, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB8, - .code = MEDIA_BUS_FMT_SRGGB8_1X8, - .bpp = 8, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .code = MEDIA_BUS_FMT_SBGGR10_1X10, - .bpp = 10, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG10, - .code = MEDIA_BUS_FMT_SGBRG10_1X10, - .bpp = 10, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG10, - .code = MEDIA_BUS_FMT_SGRBG10_1X10, - .bpp = 10, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB10, - .code = MEDIA_BUS_FMT_SRGGB10_1X10, - .bpp = 10, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR12, - .code = MEDIA_BUS_FMT_SBGGR12_1X12, - .bpp = 12, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG12, - .code = MEDIA_BUS_FMT_SGBRG12_1X12, - .bpp = 12, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG12, - .code = MEDIA_BUS_FMT_SGRBG12_1X12, - .bpp = 12, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB12, - .code = MEDIA_BUS_FMT_SRGGB12_1X12, - .bpp = 12, - }, -}; - -/* Print Four-character-code (FOURCC) */ -static char *fourcc_to_str(u32 fmt) -{ - static char code[5]; - - code[0] = (unsigned char)(fmt & 0xff); - code[1] = (unsigned char)((fmt >> 8) & 0xff); - code[2] = (unsigned char)((fmt >> 16) & 0xff); - code[3] = (unsigned char)((fmt >> 24) & 0xff); - code[4] = '\0'; - - return code; -} - -/* buffer for one video frame */ -struct cal_buffer { - /* common v4l buffer stuff -- must be first */ - struct vb2_v4l2_buffer vb; - struct list_head list; - const struct cal_fmt *fmt; -}; - -struct cal_dmaqueue { - struct list_head active; - - /* Counters to control fps rate */ - int frame; - int ini_jiffies; -}; - -struct cc_data { - void __iomem *base; - struct resource *res; - - struct platform_device *pdev; -}; - -/* CTRL_CORE_CAMERRX_CONTROL register field id */ -enum cal_camerarx_field { - F_CTRLCLKEN, - F_CAMMODE, - F_LANEENABLE, - F_CSI_MODE, - - F_MAX_FIELDS, -}; - -struct cal_csi2_phy { - struct regmap_field *fields[F_MAX_FIELDS]; - struct reg_field *base_fields; - const int num_lanes; -}; - -struct cal_data { - const int num_csi2_phy; - struct cal_csi2_phy *csi2_phy_core; - - const unsigned int flags; -}; - -static struct reg_field dra72x_ctrl_core_csi0_reg_fields[F_MAX_FIELDS] = { - [F_CTRLCLKEN] = REG_FIELD(0, 10, 10), - [F_CAMMODE] = REG_FIELD(0, 11, 12), - [F_LANEENABLE] = REG_FIELD(0, 13, 16), - [F_CSI_MODE] = REG_FIELD(0, 17, 17), -}; - -static struct reg_field dra72x_ctrl_core_csi1_reg_fields[F_MAX_FIELDS] = { - [F_CTRLCLKEN] = REG_FIELD(0, 0, 0), - [F_CAMMODE] = REG_FIELD(0, 1, 2), - [F_LANEENABLE] = REG_FIELD(0, 3, 4), - [F_CSI_MODE] = REG_FIELD(0, 5, 5), -}; - -static struct cal_csi2_phy dra72x_cal_csi_phy[] = { +static const struct cal_camerarx_data dra72x_cal_camerarx[] = { { - .base_fields = dra72x_ctrl_core_csi0_reg_fields, + .fields = { + [F_CTRLCLKEN] = { 10, 10 }, + [F_CAMMODE] = { 11, 12 }, + [F_LANEENABLE] = { 13, 16 }, + [F_CSI_MODE] = { 17, 17 }, + }, .num_lanes = 4, }, { - .base_fields = dra72x_ctrl_core_csi1_reg_fields, + .fields = { + [F_CTRLCLKEN] = { 0, 0 }, + [F_CAMMODE] = { 1, 2 }, + [F_LANEENABLE] = { 3, 4 }, + [F_CSI_MODE] = { 5, 5 }, + }, .num_lanes = 2, }, }; static const struct cal_data dra72x_cal_data = { - .csi2_phy_core = dra72x_cal_csi_phy, - .num_csi2_phy = ARRAY_SIZE(dra72x_cal_csi_phy), + .camerarx = dra72x_cal_camerarx, + .num_csi2_phy = ARRAY_SIZE(dra72x_cal_camerarx), }; static const struct cal_data dra72x_es1_cal_data = { - .csi2_phy_core = dra72x_cal_csi_phy, - .num_csi2_phy = ARRAY_SIZE(dra72x_cal_csi_phy), + .camerarx = dra72x_cal_camerarx, + .num_csi2_phy = ARRAY_SIZE(dra72x_cal_camerarx), .flags = DRA72_CAL_PRE_ES2_LDO_DISABLE, }; -static struct reg_field dra76x_ctrl_core_csi0_reg_fields[F_MAX_FIELDS] = { - [F_CTRLCLKEN] = REG_FIELD(0, 8, 8), - [F_CAMMODE] = REG_FIELD(0, 9, 10), - [F_CSI_MODE] = REG_FIELD(0, 11, 11), - [F_LANEENABLE] = REG_FIELD(0, 27, 31), -}; - -static struct reg_field dra76x_ctrl_core_csi1_reg_fields[F_MAX_FIELDS] = { - [F_CTRLCLKEN] = REG_FIELD(0, 0, 0), - [F_CAMMODE] = REG_FIELD(0, 1, 2), - [F_CSI_MODE] = REG_FIELD(0, 3, 3), - [F_LANEENABLE] = REG_FIELD(0, 24, 26), -}; - -static struct cal_csi2_phy dra76x_cal_csi_phy[] = { +static const struct cal_camerarx_data dra76x_cal_csi_phy[] = { { - .base_fields = dra76x_ctrl_core_csi0_reg_fields, + .fields = { + [F_CTRLCLKEN] = { 8, 8 }, + [F_CAMMODE] = { 9, 10 }, + [F_CSI_MODE] = { 11, 11 }, + [F_LANEENABLE] = { 27, 31 }, + }, .num_lanes = 5, }, { - .base_fields = dra76x_ctrl_core_csi1_reg_fields, + .fields = { + [F_CTRLCLKEN] = { 0, 0 }, + [F_CAMMODE] = { 1, 2 }, + [F_CSI_MODE] = { 3, 3 }, + [F_LANEENABLE] = { 24, 26 }, + }, .num_lanes = 3, }, }; static const struct cal_data dra76x_cal_data = { - .csi2_phy_core = dra76x_cal_csi_phy, + .camerarx = dra76x_cal_csi_phy, .num_csi2_phy = ARRAY_SIZE(dra76x_cal_csi_phy), }; -static struct reg_field am654_ctrl_core_csi0_reg_fields[F_MAX_FIELDS] = { - [F_CTRLCLKEN] = REG_FIELD(0, 15, 15), - [F_CAMMODE] = REG_FIELD(0, 24, 25), - [F_LANEENABLE] = REG_FIELD(0, 0, 4), -}; - -static struct cal_csi2_phy am654_cal_csi_phy[] = { +static const struct cal_camerarx_data am654_cal_csi_phy[] = { { - .base_fields = am654_ctrl_core_csi0_reg_fields, + .fields = { + [F_CTRLCLKEN] = { 15, 15 }, + [F_CAMMODE] = { 24, 25 }, + [F_LANEENABLE] = { 0, 4 }, + }, .num_lanes = 5, }, }; static const struct cal_data am654_cal_data = { - .csi2_phy_core = am654_cal_csi_phy, + .camerarx = am654_cal_csi_phy, .num_csi2_phy = ARRAY_SIZE(am654_cal_csi_phy), }; -/* - * there is one cal_dev structure in the driver, it is shared by - * all instances. - */ -struct cal_dev { - struct clk *fclk; - int irq; - void __iomem *base; - struct resource *res; - struct platform_device *pdev; - struct v4l2_device v4l2_dev; - - /* Controller flags for special cases */ - unsigned int flags; - - const struct cal_data *data; - - /* Control Module handle */ - struct regmap *syscon_camerrx; - u32 syscon_camerrx_offset; - - /* Camera Core Module handle */ - struct cc_data *cc[CAL_NUM_CSI2_PORTS]; - - struct cal_ctx *ctx[CAL_NUM_CONTEXT]; -}; - -/* - * There is one cal_ctx structure for each camera core context. - */ -struct cal_ctx { - struct v4l2_device v4l2_dev; - struct v4l2_ctrl_handler ctrl_handler; - struct video_device vdev; - struct v4l2_async_notifier notifier; - struct v4l2_subdev *sensor; - struct v4l2_fwnode_endpoint endpoint; - - struct v4l2_fh fh; - struct cal_dev *dev; - struct cc_data *cc; - - /* v4l2_ioctl mutex */ - struct mutex mutex; - /* v4l2 buffers lock */ - spinlock_t slock; - - /* Several counters */ - unsigned long jiffies; - - struct cal_dmaqueue vidq; - - /* Input Number */ - int input; - - /* video capture */ - const struct cal_fmt *fmt; - /* Used to store current pixel format */ - struct v4l2_format v_fmt; - /* Used to store current mbus frame format */ - struct v4l2_mbus_framefmt m_fmt; - - /* Current subdev enumerated format */ - struct cal_fmt *active_fmt[ARRAY_SIZE(cal_formats)]; - int num_active_fmt; - - struct v4l2_fract timeperframe; - unsigned int sequence; - unsigned int external_rate; - struct vb2_queue vb_vidq; - unsigned int seq_count; - unsigned int csi2_port; - unsigned int virtual_channel; - - /* Pointer pointing to current v4l2_buffer */ - struct cal_buffer *cur_frm; - /* Pointer pointing to next v4l2_buffer */ - struct cal_buffer *next_frm; - - bool dma_act; -}; - -static const struct cal_fmt *find_format_by_pix(struct cal_ctx *ctx, - u32 pixelformat) -{ - const struct cal_fmt *fmt; - unsigned int k; - - for (k = 0; k < ctx->num_active_fmt; k++) { - fmt = ctx->active_fmt[k]; - if (fmt->fourcc == pixelformat) - return fmt; - } - - return NULL; -} - -static const struct cal_fmt *find_format_by_code(struct cal_ctx *ctx, - u32 code) -{ - const struct cal_fmt *fmt; - unsigned int k; - - for (k = 0; k < ctx->num_active_fmt; k++) { - fmt = ctx->active_fmt[k]; - if (fmt->code == code) - return fmt; - } - - return NULL; -} - -static inline struct cal_ctx *notifier_to_ctx(struct v4l2_async_notifier *n) -{ - return container_of(n, struct cal_ctx, notifier); -} - -static inline int get_field(u32 value, u32 mask) -{ - return (value & mask) >> __ffs(mask); -} - -static inline void set_field(u32 *valp, u32 field, u32 mask) -{ - u32 val = *valp; - - val &= ~mask; - val |= (field << __ffs(mask)) & mask; - *valp = val; -} - -static u32 cal_data_get_phy_max_lanes(struct cal_ctx *ctx) -{ - struct cal_dev *dev = ctx->dev; - u32 phy_id = ctx->csi2_port - 1; - - return dev->data->csi2_phy_core[phy_id].num_lanes; -} - -static u32 cal_data_get_num_csi2_phy(struct cal_dev *dev) -{ - return dev->data->num_csi2_phy; -} - -static int cal_camerarx_regmap_init(struct cal_dev *dev) -{ - struct reg_field *field; - struct cal_csi2_phy *phy; - int i, j; - - if (!dev->data) - return -EINVAL; - - for (i = 0; i < cal_data_get_num_csi2_phy(dev); i++) { - phy = &dev->data->csi2_phy_core[i]; - for (j = 0; j < F_MAX_FIELDS; j++) { - field = &phy->base_fields[j]; - /* - * Here we update the reg offset with the - * value found in DT - */ - field->reg = dev->syscon_camerrx_offset; - phy->fields[j] = - devm_regmap_field_alloc(&dev->pdev->dev, - dev->syscon_camerrx, - *field); - if (IS_ERR(phy->fields[j])) { - cal_err(dev, "Unable to allocate regmap fields\n"); - return PTR_ERR(phy->fields[j]); - } - } - } - return 0; -} - -static const struct regmap_config cal_regmap_config = { - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, -}; - -static struct regmap *cal_get_camerarx_regmap(struct cal_dev *dev) -{ - struct platform_device *pdev = dev->pdev; - struct regmap *regmap; - void __iomem *base; - u32 reg_io_width; - struct regmap_config r_config = cal_regmap_config; - struct resource *res; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "camerrx_control"); - base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(base)) { - cal_err(dev, "failed to ioremap\n"); - return ERR_CAST(base); - } - - cal_dbg(1, dev, "ioresource %s at %pa - %pa\n", - res->name, &res->start, &res->end); - - reg_io_width = 4; - r_config.reg_stride = reg_io_width; - r_config.val_bits = reg_io_width * 8; - r_config.max_register = resource_size(res) - reg_io_width; - - regmap = regmap_init_mmio(NULL, base, &r_config); - if (IS_ERR(regmap)) - pr_err("regmap init failed\n"); - - return regmap; -} - -/* - * Control Module CAMERARX block access - */ -static void camerarx_phy_enable(struct cal_ctx *ctx) -{ - struct cal_csi2_phy *phy; - u32 phy_id = ctx->csi2_port - 1; - u32 max_lanes; - - phy = &ctx->dev->data->csi2_phy_core[phy_id]; - regmap_field_write(phy->fields[F_CAMMODE], 0); - /* Always enable all lanes at the phy control level */ - max_lanes = (1 << cal_data_get_phy_max_lanes(ctx)) - 1; - regmap_field_write(phy->fields[F_LANEENABLE], max_lanes); - /* F_CSI_MODE is not present on every architecture */ - if (phy->fields[F_CSI_MODE]) - regmap_field_write(phy->fields[F_CSI_MODE], 1); - regmap_field_write(phy->fields[F_CTRLCLKEN], 1); -} - -static void camerarx_phy_disable(struct cal_ctx *ctx) -{ - struct cal_csi2_phy *phy; - u32 phy_id = ctx->csi2_port - 1; - - phy = &ctx->dev->data->csi2_phy_core[phy_id]; - regmap_field_write(phy->fields[F_CTRLCLKEN], 0); -} - -/* - * Camera Instance access block - */ -static struct cc_data *cc_create(struct cal_dev *dev, unsigned int core) -{ - struct platform_device *pdev = dev->pdev; - struct cc_data *cc; - - cc = devm_kzalloc(&pdev->dev, sizeof(*cc), GFP_KERNEL); - if (!cc) - return ERR_PTR(-ENOMEM); - - cc->res = platform_get_resource_byname(pdev, - IORESOURCE_MEM, - (core == 0) ? - "cal_rx_core0" : - "cal_rx_core1"); - cc->base = devm_ioremap_resource(&pdev->dev, cc->res); - if (IS_ERR(cc->base)) { - cal_err(dev, "failed to ioremap\n"); - return ERR_CAST(cc->base); - } - - cal_dbg(1, dev, "ioresource %s at %pa - %pa\n", - cc->res->name, &cc->res->start, &cc->res->end); - - return cc; -} - -/* - * Get Revision and HW info +/* ------------------------------------------------------------------ + * I/O Register Accessors + * ------------------------------------------------------------------ */ -static void cal_get_hwinfo(struct cal_dev *dev) -{ - u32 revision = 0; - u32 hwinfo = 0; - - revision = reg_read(dev, CAL_HL_REVISION); - cal_dbg(3, dev, "CAL_HL_REVISION = 0x%08x (expecting 0x40000200)\n", - revision); - hwinfo = reg_read(dev, CAL_HL_HWINFO); - cal_dbg(3, dev, "CAL_HL_HWINFO = 0x%08x (expecting 0xA3C90469)\n", - hwinfo); -} - -/* - * Errata i913: CSI2 LDO Needs to be disabled when module is powered on - * - * Enabling CSI2 LDO shorts it to core supply. It is crucial the 2 CSI2 - * LDOs on the device are disabled if CSI-2 module is powered on - * (0x4845 B304 | 0x4845 B384 [28:27] = 0x1) or in ULPS (0x4845 B304 - * | 0x4845 B384 [28:27] = 0x2) mode. Common concerns include: high - * current draw on the module supply in active mode. - * - * Errata does not apply when CSI-2 module is powered off - * (0x4845 B304 | 0x4845 B384 [28:27] = 0x0). - * - * SW Workaround: - * Set the following register bits to disable the LDO, - * which is essentially CSI2 REG10 bit 6: - * - * Core 0: 0x4845 B828 = 0x0000 0040 - * Core 1: 0x4845 B928 = 0x0000 0040 - */ -static void i913_errata(struct cal_dev *dev, unsigned int port) +void cal_quickdump_regs(struct cal_dev *cal) { - u32 reg10 = reg_read(dev->cc[port], CAL_CSI2_PHY_REG10); - - set_field(®10, 1, CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK); - - cal_dbg(1, dev, "CSI2_%d_REG10 = 0x%08x\n", port, reg10); - reg_write(dev->cc[port], CAL_CSI2_PHY_REG10, reg10); -} + unsigned int i; -static void cal_quickdump_regs(struct cal_dev *dev) -{ - cal_info(dev, "CAL Registers @ 0x%pa:\n", &dev->res->start); + cal_info(cal, "CAL Registers @ 0x%pa:\n", &cal->res->start); print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, - (__force const void *)dev->base, - resource_size(dev->res), false); + (__force const void *)cal->base, + resource_size(cal->res), false); - if (dev->ctx[0]) { - cal_info(dev, "CSI2 Core 0 Registers @ %pa:\n", - &dev->ctx[0]->cc->res->start); - print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, - (__force const void *)dev->ctx[0]->cc->base, - resource_size(dev->ctx[0]->cc->res), - false); - } + for (i = 0; i < ARRAY_SIZE(cal->phy); ++i) { + struct cal_camerarx *phy = cal->phy[i]; - if (dev->ctx[1]) { - cal_info(dev, "CSI2 Core 1 Registers @ %pa:\n", - &dev->ctx[1]->cc->res->start); + if (!phy) + continue; + + cal_info(cal, "CSI2 Core %u Registers @ %pa:\n", i, + &phy->res->start); print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, - (__force const void *)dev->ctx[1]->cc->base, - resource_size(dev->ctx[1]->cc->res), + (__force const void *)phy->base, + resource_size(phy->res), false); } } -/* - * Enable the expected IRQ sources +/* ------------------------------------------------------------------ + * Context Management + * ------------------------------------------------------------------ */ -static void enable_irqs(struct cal_ctx *ctx) -{ - u32 val; - - const u32 cio_err_mask = - CAL_CSI2_COMPLEXIO_IRQ_LANE_ERRORS_MASK | - CAL_CSI2_COMPLEXIO_IRQ_FIFO_OVR_MASK | - CAL_CSI2_COMPLEXIO_IRQ_SHORT_PACKET_MASK | - CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK; - - /* Enable CIO error irqs */ - reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(1), - CAL_HL_IRQ_CIO_MASK(ctx->csi2_port)); - reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_IRQENABLE(ctx->csi2_port), - cio_err_mask); - - /* Always enable OCPO error */ - reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(1), CAL_HL_IRQ_OCPO_ERR_MASK); - - /* Enable IRQ_WDMA_END 0/1 */ - val = 0; - set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); - reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(2), val); - /* Enable IRQ_WDMA_START 0/1 */ - val = 0; - set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); - reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(3), val); - /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ - reg_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(1), 0xFF000000); -} - -static void disable_irqs(struct cal_ctx *ctx) -{ - u32 val; - - /* Disable CIO error irqs */ - reg_write(ctx->dev, CAL_HL_IRQENABLE_CLR(1), - CAL_HL_IRQ_CIO_MASK(ctx->csi2_port)); - reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_IRQENABLE(ctx->csi2_port), - 0); - - /* Disable IRQ_WDMA_END 0/1 */ - val = 0; - set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); - reg_write(ctx->dev, CAL_HL_IRQENABLE_CLR(2), val); - /* Disable IRQ_WDMA_START 0/1 */ - val = 0; - set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); - reg_write(ctx->dev, CAL_HL_IRQENABLE_CLR(3), val); - /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ - reg_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(1), 0); -} - -static void csi2_cio_power(struct cal_ctx *ctx, bool enable) -{ - u32 target_state; - unsigned int i; - - target_state = enable ? CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON : - CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_OFF; - - reg_write_field(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), - target_state, CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK); - - for (i = 0; i < 10; i++) { - u32 current_state; - - current_state = reg_read_field(ctx->dev, - CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), - CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK); - - if (current_state == target_state) - break; - - usleep_range(1000, 1100); - } - - if (i == 10) - ctx_err(ctx, "Failed to power %s complexio\n", - enable ? "up" : "down"); -} - -static void csi2_phy_config(struct cal_ctx *ctx); - -static void csi2_phy_init(struct cal_ctx *ctx) -{ - u32 val; - u32 sscounter; - - /* Steps - * 1. Configure D-PHY mode and enable required lanes - * 2. Reset complex IO - Wait for completion of reset - * Note if the external sensor is not sending byte clock, - * the reset will timeout - * 3 Program Stop States - * A. Program THS_TERM, THS_SETTLE, etc... Timings parameters - * in terms of DDR clock periods - * B. Enable stop state transition timeouts - * 4.Force FORCERXMODE - * D. Enable pull down using pad control - * E. Power up PHY - * F. Wait for power up completion - * G. Wait for all enabled lane to reach stop state - * H. Disable pull down using pad control - */ - - /* 1. Configure D-PHY mode and enable required lanes */ - camerarx_phy_enable(ctx); - - /* 2. Reset complex IO - Do not wait for reset completion */ - reg_write_field(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), - CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL, - CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); - ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x De-assert Complex IO Reset\n", - ctx->csi2_port, - reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port))); - - /* Dummy read to allow SCP reset to complete */ - reg_read(ctx->cc, CAL_CSI2_PHY_REG0); - - /* 3.A. Program Phy Timing Parameters */ - csi2_phy_config(ctx); - - /* 3.B. Program Stop States */ - /* - * The stop-state-counter is based on fclk cycles, and we always use - * the x16 and x4 settings, so stop-state-timeout = - * fclk-cycle * 16 * 4 * counter. - * - * Stop-state-timeout must be more than 100us as per CSI2 spec, so we - * calculate a timeout that's 100us (rounding up). - */ - sscounter = DIV_ROUND_UP(clk_get_rate(ctx->dev->fclk), 10000 * 16 * 4); - - val = reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port)); - set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK); - set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK); - set_field(&val, sscounter, CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK); - reg_write(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port), val); - ctx_dbg(3, ctx, "CAL_CSI2_TIMING(%d) = 0x%08x Stop States\n", - ctx->csi2_port, - reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port))); - - /* 4. Force FORCERXMODE */ - reg_write_field(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port), - 1, CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK); - ctx_dbg(3, ctx, "CAL_CSI2_TIMING(%d) = 0x%08x Force RXMODE\n", - ctx->csi2_port, - reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port))); - - /* E. Power up the PHY using the complex IO */ - csi2_cio_power(ctx, true); -} - -static void csi2_wait_complexio_reset(struct cal_ctx *ctx) -{ - unsigned long timeout; - - timeout = jiffies + msecs_to_jiffies(750); - while (time_before(jiffies, timeout)) { - if (reg_read_field(ctx->dev, - CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) - break; - usleep_range(500, 5000); - } - - if (reg_read_field(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) != - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) - ctx_err(ctx, "Timeout waiting for Complex IO reset done\n"); -} - -static void csi2_wait_stop_state(struct cal_ctx *ctx) -{ - unsigned long timeout; - - timeout = jiffies + msecs_to_jiffies(750); - while (time_before(jiffies, timeout)) { - if (reg_read_field(ctx->dev, - CAL_CSI2_TIMING(ctx->csi2_port), - CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) == 0) - break; - usleep_range(500, 5000); - } - - if (reg_read_field(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port), - CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) != 0) - ctx_err(ctx, "Timeout waiting for stop state\n"); -} - -static void csi2_wait_for_phy(struct cal_ctx *ctx) -{ - /* Steps - * 2. Wait for completion of reset - * Note if the external sensor is not sending byte clock, - * the reset will timeout - * 4.Force FORCERXMODE - * G. Wait for all enabled lane to reach stop state - * H. Disable pull down using pad control - */ - - /* 2. Wait for reset completion */ - csi2_wait_complexio_reset(ctx); - - /* 4. G. Wait for all enabled lane to reach stop state */ - csi2_wait_stop_state(ctx); - - ctx_dbg(1, ctx, "CSI2_%d_REG1 = 0x%08x (Bit(31,28) should be set!)\n", - (ctx->csi2_port - 1), reg_read(ctx->cc, CAL_CSI2_PHY_REG1)); -} - -static void csi2_phy_deinit(struct cal_ctx *ctx) -{ - int i; - - csi2_cio_power(ctx, false); - - /* Assert Comple IO Reset */ - reg_write_field(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), - CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL, - CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); - - /* Wait for power down completion */ - for (i = 0; i < 10; i++) { - if (reg_read_field(ctx->dev, - CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETONGOING) - break; - usleep_range(1000, 1100); - } - ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Complex IO in Reset (%d) %s\n", - ctx->csi2_port, - reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)), i, - (i >= 10) ? "(timeout)" : ""); - - /* Disable the phy */ - camerarx_phy_disable(ctx); -} - -static void csi2_lane_config(struct cal_ctx *ctx) -{ - u32 val = reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)); - u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK; - u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK; - struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = - &ctx->endpoint.bus.mipi_csi2; - int lane; - - set_field(&val, mipi_csi2->clock_lane + 1, lane_mask); - set_field(&val, mipi_csi2->lane_polarities[0], polarity_mask); - for (lane = 0; lane < mipi_csi2->num_data_lanes; lane++) { - /* - * Every lane are one nibble apart starting with the - * clock followed by the data lanes so shift masks by 4. - */ - lane_mask <<= 4; - polarity_mask <<= 4; - set_field(&val, mipi_csi2->data_lanes[lane] + 1, lane_mask); - set_field(&val, mipi_csi2->lane_polarities[lane + 1], - polarity_mask); - } - - reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), val); - ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n", - ctx->csi2_port, val); -} - -static void csi2_ppi_enable(struct cal_ctx *ctx) -{ - reg_write(ctx->dev, CAL_CSI2_PPI_CTRL(ctx->csi2_port), BIT(3)); - reg_write_field(ctx->dev, CAL_CSI2_PPI_CTRL(ctx->csi2_port), - 1, CAL_CSI2_PPI_CTRL_IF_EN_MASK); -} - -static void csi2_ppi_disable(struct cal_ctx *ctx) -{ - reg_write_field(ctx->dev, CAL_CSI2_PPI_CTRL(ctx->csi2_port), - 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK); -} -static void csi2_ctx_config(struct cal_ctx *ctx) +void cal_ctx_csi2_config(struct cal_ctx *ctx) { u32 val; - val = reg_read(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port)); - set_field(&val, ctx->csi2_port, CAL_CSI2_CTX_CPORT_MASK); + val = cal_read(ctx->cal, CAL_CSI2_CTX0(ctx->index)); + cal_set_field(&val, ctx->cport, CAL_CSI2_CTX_CPORT_MASK); /* * DT type: MIPI CSI-2 Specs * 0x1: All - DT filter is disabled @@ -979,19 +170,18 @@ static void csi2_ctx_config(struct cal_ctx *ctx) * 0x2A: RAW8 1 pixel = 1 byte * 0x1E: YUV422 2 pixels = 4 bytes */ - set_field(&val, 0x1, CAL_CSI2_CTX_DT_MASK); - /* Virtual Channel from the CSI2 sensor usually 0! */ - set_field(&val, ctx->virtual_channel, CAL_CSI2_CTX_VC_MASK); - set_field(&val, ctx->v_fmt.fmt.pix.height, CAL_CSI2_CTX_LINES_MASK); - set_field(&val, CAL_CSI2_CTX_ATT_PIX, CAL_CSI2_CTX_ATT_MASK); - set_field(&val, CAL_CSI2_CTX_PACK_MODE_LINE, - CAL_CSI2_CTX_PACK_MODE_MASK); - reg_write(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port), val); - ctx_dbg(3, ctx, "CAL_CSI2_CTX0(%d) = 0x%08x\n", ctx->csi2_port, - reg_read(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port))); + cal_set_field(&val, 0x1, CAL_CSI2_CTX_DT_MASK); + cal_set_field(&val, 0, CAL_CSI2_CTX_VC_MASK); + cal_set_field(&val, ctx->v_fmt.fmt.pix.height, CAL_CSI2_CTX_LINES_MASK); + cal_set_field(&val, CAL_CSI2_CTX_ATT_PIX, CAL_CSI2_CTX_ATT_MASK); + cal_set_field(&val, CAL_CSI2_CTX_PACK_MODE_LINE, + CAL_CSI2_CTX_PACK_MODE_MASK); + cal_write(ctx->cal, CAL_CSI2_CTX0(ctx->index), val); + ctx_dbg(3, ctx, "CAL_CSI2_CTX0(%d) = 0x%08x\n", ctx->index, + cal_read(ctx->cal, CAL_CSI2_CTX0(ctx->index))); } -static void pix_proc_config(struct cal_ctx *ctx) +void cal_ctx_pix_proc_config(struct cal_ctx *ctx) { u32 val, extract, pack; @@ -1022,7 +212,7 @@ static void pix_proc_config(struct cal_ctx *ctx) * * Instead of failing here just use 8 bpp as a default. */ - dev_warn_once(&ctx->dev->pdev->dev, + dev_warn_once(ctx->cal->dev, "%s:%d:%s: bpp:%d unsupported! Overwritten with 8.\n", __FILE__, __LINE__, __func__, ctx->fmt->bpp); extract = CAL_PIX_PROC_EXTRACT_B8; @@ -1030,145 +220,82 @@ static void pix_proc_config(struct cal_ctx *ctx) break; } - val = reg_read(ctx->dev, CAL_PIX_PROC(ctx->csi2_port)); - set_field(&val, extract, CAL_PIX_PROC_EXTRACT_MASK); - set_field(&val, CAL_PIX_PROC_DPCMD_BYPASS, CAL_PIX_PROC_DPCMD_MASK); - set_field(&val, CAL_PIX_PROC_DPCME_BYPASS, CAL_PIX_PROC_DPCME_MASK); - set_field(&val, pack, CAL_PIX_PROC_PACK_MASK); - set_field(&val, ctx->csi2_port, CAL_PIX_PROC_CPORT_MASK); - set_field(&val, 1, CAL_PIX_PROC_EN_MASK); - reg_write(ctx->dev, CAL_PIX_PROC(ctx->csi2_port), val); - ctx_dbg(3, ctx, "CAL_PIX_PROC(%d) = 0x%08x\n", ctx->csi2_port, - reg_read(ctx->dev, CAL_PIX_PROC(ctx->csi2_port))); + val = cal_read(ctx->cal, CAL_PIX_PROC(ctx->index)); + cal_set_field(&val, extract, CAL_PIX_PROC_EXTRACT_MASK); + cal_set_field(&val, CAL_PIX_PROC_DPCMD_BYPASS, CAL_PIX_PROC_DPCMD_MASK); + cal_set_field(&val, CAL_PIX_PROC_DPCME_BYPASS, CAL_PIX_PROC_DPCME_MASK); + cal_set_field(&val, pack, CAL_PIX_PROC_PACK_MASK); + cal_set_field(&val, ctx->cport, CAL_PIX_PROC_CPORT_MASK); + cal_set_field(&val, 1, CAL_PIX_PROC_EN_MASK); + cal_write(ctx->cal, CAL_PIX_PROC(ctx->index), val); + ctx_dbg(3, ctx, "CAL_PIX_PROC(%d) = 0x%08x\n", ctx->index, + cal_read(ctx->cal, CAL_PIX_PROC(ctx->index))); } -static void cal_wr_dma_config(struct cal_ctx *ctx, - unsigned int width, unsigned int height) +void cal_ctx_wr_dma_config(struct cal_ctx *ctx, unsigned int width, + unsigned int height) { u32 val; - val = reg_read(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port)); - set_field(&val, ctx->csi2_port, CAL_WR_DMA_CTRL_CPORT_MASK); - set_field(&val, height, CAL_WR_DMA_CTRL_YSIZE_MASK); - set_field(&val, CAL_WR_DMA_CTRL_DTAG_PIX_DAT, - CAL_WR_DMA_CTRL_DTAG_MASK); - set_field(&val, CAL_WR_DMA_CTRL_MODE_CONST, - CAL_WR_DMA_CTRL_MODE_MASK); - set_field(&val, CAL_WR_DMA_CTRL_PATTERN_LINEAR, - CAL_WR_DMA_CTRL_PATTERN_MASK); - set_field(&val, 1, CAL_WR_DMA_CTRL_STALL_RD_MASK); - reg_write(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port), val); - ctx_dbg(3, ctx, "CAL_WR_DMA_CTRL(%d) = 0x%08x\n", ctx->csi2_port, - reg_read(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port))); + val = cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index)); + cal_set_field(&val, ctx->cport, CAL_WR_DMA_CTRL_CPORT_MASK); + cal_set_field(&val, height, CAL_WR_DMA_CTRL_YSIZE_MASK); + cal_set_field(&val, CAL_WR_DMA_CTRL_DTAG_PIX_DAT, + CAL_WR_DMA_CTRL_DTAG_MASK); + cal_set_field(&val, CAL_WR_DMA_CTRL_MODE_CONST, + CAL_WR_DMA_CTRL_MODE_MASK); + cal_set_field(&val, CAL_WR_DMA_CTRL_PATTERN_LINEAR, + CAL_WR_DMA_CTRL_PATTERN_MASK); + cal_set_field(&val, 1, CAL_WR_DMA_CTRL_STALL_RD_MASK); + cal_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->index), val); + ctx_dbg(3, ctx, "CAL_WR_DMA_CTRL(%d) = 0x%08x\n", ctx->index, + cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index))); /* * width/16 not sure but giving it a whirl. * zero does not work right */ - reg_write_field(ctx->dev, - CAL_WR_DMA_OFST(ctx->csi2_port), + cal_write_field(ctx->cal, + CAL_WR_DMA_OFST(ctx->index), (width / 16), CAL_WR_DMA_OFST_MASK); - ctx_dbg(3, ctx, "CAL_WR_DMA_OFST(%d) = 0x%08x\n", ctx->csi2_port, - reg_read(ctx->dev, CAL_WR_DMA_OFST(ctx->csi2_port))); + ctx_dbg(3, ctx, "CAL_WR_DMA_OFST(%d) = 0x%08x\n", ctx->index, + cal_read(ctx->cal, CAL_WR_DMA_OFST(ctx->index))); - val = reg_read(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port)); + val = cal_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index)); /* 64 bit word means no skipping */ - set_field(&val, 0, CAL_WR_DMA_XSIZE_XSKIP_MASK); + cal_set_field(&val, 0, CAL_WR_DMA_XSIZE_XSKIP_MASK); /* * (width*8)/64 this should be size of an entire line * in 64bit word but 0 means all data until the end * is detected automagically */ - set_field(&val, (width / 8), CAL_WR_DMA_XSIZE_MASK); - reg_write(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port), val); - ctx_dbg(3, ctx, "CAL_WR_DMA_XSIZE(%d) = 0x%08x\n", ctx->csi2_port, - reg_read(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port))); - - val = reg_read(ctx->dev, CAL_CTRL); - set_field(&val, CAL_CTRL_BURSTSIZE_BURST128, CAL_CTRL_BURSTSIZE_MASK); - set_field(&val, 0xF, CAL_CTRL_TAGCNT_MASK); - set_field(&val, CAL_CTRL_POSTED_WRITES_NONPOSTED, - CAL_CTRL_POSTED_WRITES_MASK); - set_field(&val, 0xFF, CAL_CTRL_MFLAGL_MASK); - set_field(&val, 0xFF, CAL_CTRL_MFLAGH_MASK); - reg_write(ctx->dev, CAL_CTRL, val); - ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", reg_read(ctx->dev, CAL_CTRL)); -} + cal_set_field(&val, (width / 8), CAL_WR_DMA_XSIZE_MASK); + cal_write(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index), val); + ctx_dbg(3, ctx, "CAL_WR_DMA_XSIZE(%d) = 0x%08x\n", ctx->index, + cal_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index))); -static void cal_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) -{ - reg_write(ctx->dev, CAL_WR_DMA_ADDR(ctx->csi2_port), dmaaddr); + val = cal_read(ctx->cal, CAL_CTRL); + cal_set_field(&val, CAL_CTRL_BURSTSIZE_BURST128, + CAL_CTRL_BURSTSIZE_MASK); + cal_set_field(&val, 0xF, CAL_CTRL_TAGCNT_MASK); + cal_set_field(&val, CAL_CTRL_POSTED_WRITES_NONPOSTED, + CAL_CTRL_POSTED_WRITES_MASK); + cal_set_field(&val, 0xFF, CAL_CTRL_MFLAGL_MASK); + cal_set_field(&val, 0xFF, CAL_CTRL_MFLAGH_MASK); + cal_write(ctx->cal, CAL_CTRL, val); + ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", cal_read(ctx->cal, CAL_CTRL)); } -/* - * TCLK values are OK at their reset values - */ -#define TCLK_TERM 0 -#define TCLK_MISS 1 -#define TCLK_SETTLE 14 - -static void csi2_phy_config(struct cal_ctx *ctx) +void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) { - unsigned int reg0, reg1; - unsigned int ths_term, ths_settle; - unsigned int csi2_ddrclk_khz; - struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = - &ctx->endpoint.bus.mipi_csi2; - u32 num_lanes = mipi_csi2->num_data_lanes; - - /* DPHY timing configuration */ - /* CSI-2 is DDR and we only count used lanes. */ - csi2_ddrclk_khz = ctx->external_rate / 1000 - / (2 * num_lanes) * ctx->fmt->bpp; - ctx_dbg(1, ctx, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz); - - /* THS_TERM: Programmed value = floor(20 ns/DDRClk period) */ - ths_term = 20 * csi2_ddrclk_khz / 1000000; - ctx_dbg(1, ctx, "ths_term: %d (0x%02x)\n", ths_term, ths_term); - - /* THS_SETTLE: Programmed value = floor(105 ns/DDRClk period) + 4 */ - ths_settle = (105 * csi2_ddrclk_khz / 1000000) + 4; - ctx_dbg(1, ctx, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle); - - reg0 = reg_read(ctx->cc, CAL_CSI2_PHY_REG0); - set_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE, - CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK); - set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK); - set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK); - - ctx_dbg(1, ctx, "CSI2_%d_REG0 = 0x%08x\n", (ctx->csi2_port - 1), reg0); - reg_write(ctx->cc, CAL_CSI2_PHY_REG0, reg0); - - reg1 = reg_read(ctx->cc, CAL_CSI2_PHY_REG1); - set_field(®1, TCLK_TERM, CAL_CSI2_PHY_REG1_TCLK_TERM_MASK); - set_field(®1, 0xb8, CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK); - set_field(®1, TCLK_MISS, CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK); - set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK); - - ctx_dbg(1, ctx, "CSI2_%d_REG1 = 0x%08x\n", (ctx->csi2_port - 1), reg1); - reg_write(ctx->cc, CAL_CSI2_PHY_REG1, reg1); + cal_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->index), dmaaddr); } -static int cal_get_external_info(struct cal_ctx *ctx) -{ - struct v4l2_ctrl *ctrl; - - if (!ctx->sensor) - return -ENODEV; - - ctrl = v4l2_ctrl_find(ctx->sensor->ctrl_handler, V4L2_CID_PIXEL_RATE); - if (!ctrl) { - ctx_err(ctx, "no pixel rate control in subdev: %s\n", - ctx->sensor->name); - return -EPIPE; - } - - ctx->external_rate = v4l2_ctrl_g_ctrl_int64(ctrl); - ctx_dbg(3, ctx, "sensor Pixel Rate: %d\n", ctx->external_rate); - - return 0; -} +/* ------------------------------------------------------------------ + * IRQ Handling + * ------------------------------------------------------------------ + */ static inline void cal_schedule_next_buffer(struct cal_ctx *ctx) { @@ -1181,7 +308,7 @@ static inline void cal_schedule_next_buffer(struct cal_ctx *ctx) list_del(&buf->list); addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); - cal_wr_dma_addr(ctx, addr); + cal_ctx_wr_dma_addr(ctx, addr); } static inline void cal_process_buffer_complete(struct cal_ctx *ctx) @@ -1194,52 +321,47 @@ static inline void cal_process_buffer_complete(struct cal_ctx *ctx) ctx->cur_frm = ctx->next_frm; } -#define isvcirqset(irq, vc, ff) (irq & \ - (CAL_CSI2_VC_IRQENABLE_ ##ff ##_IRQ_##vc ##_MASK)) - -#define isportirqset(irq, port) (irq & CAL_HL_IRQ_MASK(port)) - static irqreturn_t cal_irq(int irq_cal, void *data) { - struct cal_dev *dev = (struct cal_dev *)data; + struct cal_dev *cal = data; struct cal_ctx *ctx; struct cal_dmaqueue *dma_q; - u32 irqst1, irqst2, irqst3; + u32 status; - irqst1 = reg_read(dev, CAL_HL_IRQSTATUS(1)); - if (irqst1) { - int i; + status = cal_read(cal, CAL_HL_IRQSTATUS(0)); + if (status) { + unsigned int i; - reg_write(dev, CAL_HL_IRQSTATUS(1), irqst1); + cal_write(cal, CAL_HL_IRQSTATUS(0), status); - if (irqst1 & CAL_HL_IRQ_OCPO_ERR_MASK) - dev_err_ratelimited(&dev->pdev->dev, "OCPO ERROR\n"); + if (status & CAL_HL_IRQ_OCPO_ERR_MASK) + dev_err_ratelimited(cal->dev, "OCPO ERROR\n"); - for (i = 1; i <= 2; ++i) { - if (irqst1 & CAL_HL_IRQ_CIO_MASK(i)) { - u32 cio_stat = reg_read(dev, + for (i = 0; i < CAL_NUM_CSI2_PORTS; ++i) { + if (status & CAL_HL_IRQ_CIO_MASK(i)) { + u32 cio_stat = cal_read(cal, CAL_CSI2_COMPLEXIO_IRQSTATUS(i)); - dev_err_ratelimited(&dev->pdev->dev, - "CIO%d error: %#08x\n", i, cio_stat); + dev_err_ratelimited(cal->dev, + "CIO%u error: %#08x\n", i, cio_stat); - reg_write(dev, CAL_CSI2_COMPLEXIO_IRQSTATUS(i), + cal_write(cal, CAL_CSI2_COMPLEXIO_IRQSTATUS(i), cio_stat); } } } /* Check which DMA just finished */ - irqst2 = reg_read(dev, CAL_HL_IRQSTATUS(2)); - if (irqst2) { - int i; + status = cal_read(cal, CAL_HL_IRQSTATUS(1)); + if (status) { + unsigned int i; /* Clear Interrupt status */ - reg_write(dev, CAL_HL_IRQSTATUS(2), irqst2); + cal_write(cal, CAL_HL_IRQSTATUS(1), status); - for (i = 1; i <= 2; ++i) { - if (isportirqset(irqst2, i)) { - ctx = dev->ctx[i - 1]; + for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { + if (status & CAL_HL_IRQ_MASK(i)) { + ctx = cal->ctx[i]; spin_lock(&ctx->slock); ctx->dma_act = false; @@ -1253,16 +375,16 @@ static irqreturn_t cal_irq(int irq_cal, void *data) } /* Check which DMA just started */ - irqst3 = reg_read(dev, CAL_HL_IRQSTATUS(3)); - if (irqst3) { - int i; + status = cal_read(cal, CAL_HL_IRQSTATUS(2)); + if (status) { + unsigned int i; /* Clear Interrupt status */ - reg_write(dev, CAL_HL_IRQSTATUS(3), irqst3); + cal_write(cal, CAL_HL_IRQSTATUS(2), status); - for (i = 1; i <= 2; ++i) { - if (isportirqset(irqst3, i)) { - ctx = dev->ctx[i - 1]; + for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { + if (status & CAL_HL_IRQ_MASK(i)) { + ctx = cal->ctx[i]; dma_q = &ctx->vidq; spin_lock(&ctx->slock); @@ -1278,963 +400,234 @@ static irqreturn_t cal_irq(int irq_cal, void *data) return IRQ_HANDLED; } -/* - * video ioctls +/* ------------------------------------------------------------------ + * Asynchronous V4L2 subdev binding + * ------------------------------------------------------------------ */ -static int cal_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct cal_ctx *ctx = video_drvdata(file); - - strscpy(cap->driver, CAL_MODULE_NAME, sizeof(cap->driver)); - strscpy(cap->card, CAL_MODULE_NAME, sizeof(cap->card)); - - snprintf(cap->bus_info, sizeof(cap->bus_info), - "platform:%s", ctx->v4l2_dev.name); - return 0; -} - -static int cal_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct cal_ctx *ctx = video_drvdata(file); - const struct cal_fmt *fmt = NULL; - - if (f->index >= ctx->num_active_fmt) - return -EINVAL; - - fmt = ctx->active_fmt[f->index]; - - f->pixelformat = fmt->fourcc; - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - return 0; -} - -static int __subdev_get_format(struct cal_ctx *ctx, - struct v4l2_mbus_framefmt *fmt) -{ - struct v4l2_subdev_format sd_fmt; - struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; - int ret; - - sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; - sd_fmt.pad = 0; - ret = v4l2_subdev_call(ctx->sensor, pad, get_fmt, NULL, &sd_fmt); - if (ret) - return ret; - - *fmt = *mbus_fmt; - - ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__, - fmt->width, fmt->height, fmt->code); - - return 0; -} - -static int __subdev_set_format(struct cal_ctx *ctx, - struct v4l2_mbus_framefmt *fmt) -{ - struct v4l2_subdev_format sd_fmt; - struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; - int ret; - - sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; - sd_fmt.pad = 0; - *mbus_fmt = *fmt; - - ret = v4l2_subdev_call(ctx->sensor, pad, set_fmt, NULL, &sd_fmt); - if (ret) - return ret; - - ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__, - fmt->width, fmt->height, fmt->code); - - return 0; -} - -static int cal_calc_format_size(struct cal_ctx *ctx, - const struct cal_fmt *fmt, - struct v4l2_format *f) -{ - u32 bpl, max_width; - - if (!fmt) { - ctx_dbg(3, ctx, "No cal_fmt provided!\n"); - return -EINVAL; - } - - /* - * Maximum width is bound by the DMA max width in bytes. - * We need to recalculate the actual maxi width depending on the - * number of bytes per pixels required. - */ - max_width = MAX_WIDTH_BYTES / (ALIGN(fmt->bpp, 8) >> 3); - v4l_bound_align_image(&f->fmt.pix.width, 48, max_width, 2, - &f->fmt.pix.height, 32, MAX_HEIGHT_LINES, 0, 0); - - bpl = (f->fmt.pix.width * ALIGN(fmt->bpp, 8)) >> 3; - f->fmt.pix.bytesperline = ALIGN(bpl, 16); - - f->fmt.pix.sizeimage = f->fmt.pix.height * - f->fmt.pix.bytesperline; - - ctx_dbg(3, ctx, "%s: fourcc: %s size: %dx%d bpl:%d img_size:%d\n", - __func__, fourcc_to_str(f->fmt.pix.pixelformat), - f->fmt.pix.width, f->fmt.pix.height, - f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); - - return 0; -} - -static int cal_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cal_ctx *ctx = video_drvdata(file); - - *f = ctx->v_fmt; - - return 0; -} - -static int cal_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cal_ctx *ctx = video_drvdata(file); - const struct cal_fmt *fmt; - struct v4l2_subdev_frame_size_enum fse; - int ret, found; - - fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat); - if (!fmt) { - ctx_dbg(3, ctx, "Fourcc format (0x%08x) not found.\n", - f->fmt.pix.pixelformat); - - /* Just get the first one enumerated */ - fmt = ctx->active_fmt[0]; - f->fmt.pix.pixelformat = fmt->fourcc; - } - - f->fmt.pix.field = ctx->v_fmt.fmt.pix.field; - - /* check for/find a valid width/height */ - ret = 0; - found = false; - fse.pad = 0; - fse.code = fmt->code; - fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; - for (fse.index = 0; ; fse.index++) { - ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_size, - NULL, &fse); - if (ret) - break; - - if ((f->fmt.pix.width == fse.max_width) && - (f->fmt.pix.height == fse.max_height)) { - found = true; - break; - } else if ((f->fmt.pix.width >= fse.min_width) && - (f->fmt.pix.width <= fse.max_width) && - (f->fmt.pix.height >= fse.min_height) && - (f->fmt.pix.height <= fse.max_height)) { - found = true; - break; - } - } - - if (!found) { - /* use existing values as default */ - f->fmt.pix.width = ctx->v_fmt.fmt.pix.width; - f->fmt.pix.height = ctx->v_fmt.fmt.pix.height; - } - - /* - * Use current colorspace for now, it will get - * updated properly during s_fmt - */ - f->fmt.pix.colorspace = ctx->v_fmt.fmt.pix.colorspace; - return cal_calc_format_size(ctx, fmt, f); -} +struct cal_v4l2_async_subdev { + struct v4l2_async_subdev asd; + struct cal_camerarx *phy; +}; -static int cal_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) +static inline struct cal_v4l2_async_subdev * +to_cal_asd(struct v4l2_async_subdev *asd) { - struct cal_ctx *ctx = video_drvdata(file); - struct vb2_queue *q = &ctx->vb_vidq; - const struct cal_fmt *fmt; - struct v4l2_mbus_framefmt mbus_fmt; - int ret; - - if (vb2_is_busy(q)) { - ctx_dbg(3, ctx, "%s device busy\n", __func__); - return -EBUSY; - } - - ret = cal_try_fmt_vid_cap(file, priv, f); - if (ret < 0) - return ret; - - fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat); - - v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code); - - ret = __subdev_set_format(ctx, &mbus_fmt); - if (ret) - return ret; - - /* Just double check nothing has gone wrong */ - if (mbus_fmt.code != fmt->code) { - ctx_dbg(3, ctx, - "%s subdev changed format on us, this should not happen\n", - __func__); - return -EINVAL; - } - - v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); - ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; - cal_calc_format_size(ctx, fmt, &ctx->v_fmt); - ctx->fmt = fmt; - ctx->m_fmt = mbus_fmt; - *f = ctx->v_fmt; - - return 0; + return container_of(asd, struct cal_v4l2_async_subdev, asd); } -static int cal_enum_framesizes(struct file *file, void *fh, - struct v4l2_frmsizeenum *fsize) +static int cal_async_notifier_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) { - struct cal_ctx *ctx = video_drvdata(file); - const struct cal_fmt *fmt; - struct v4l2_subdev_frame_size_enum fse; - int ret; + struct cal_camerarx *phy = to_cal_asd(asd)->phy; - /* check for valid format */ - fmt = find_format_by_pix(ctx, fsize->pixel_format); - if (!fmt) { - ctx_dbg(3, ctx, "Invalid pixel code: %x\n", - fsize->pixel_format); - return -EINVAL; + if (phy->sensor) { + phy_info(phy, "Rejecting subdev %s (Already set!!)", + subdev->name); + return 0; } - fse.index = fsize->index; - fse.pad = 0; - fse.code = fmt->code; - fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; - - ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_size, NULL, &fse); - if (ret) - return ret; - - ctx_dbg(1, ctx, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n", - __func__, fse.index, fse.code, fse.min_width, fse.max_width, - fse.min_height, fse.max_height); - - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - fsize->discrete.width = fse.max_width; - fsize->discrete.height = fse.max_height; - - return 0; -} - -static int cal_enum_input(struct file *file, void *priv, - struct v4l2_input *inp) -{ - if (inp->index >= CAL_NUM_INPUT) - return -EINVAL; - - inp->type = V4L2_INPUT_TYPE_CAMERA; - sprintf(inp->name, "Camera %u", inp->index); - return 0; -} - -static int cal_g_input(struct file *file, void *priv, unsigned int *i) -{ - struct cal_ctx *ctx = video_drvdata(file); - - *i = ctx->input; - return 0; -} - -static int cal_s_input(struct file *file, void *priv, unsigned int i) -{ - struct cal_ctx *ctx = video_drvdata(file); - - if (i >= CAL_NUM_INPUT) - return -EINVAL; - - ctx->input = i; - return 0; -} - -/* timeperframe is arbitrary and continuous */ -static int cal_enum_frameintervals(struct file *file, void *priv, - struct v4l2_frmivalenum *fival) -{ - struct cal_ctx *ctx = video_drvdata(file); - const struct cal_fmt *fmt; - struct v4l2_subdev_frame_interval_enum fie = { - .index = fival->index, - .width = fival->width, - .height = fival->height, - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - int ret; - - fmt = find_format_by_pix(ctx, fival->pixel_format); - if (!fmt) - return -EINVAL; - - fie.code = fmt->code; - ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_interval, - NULL, &fie); - if (ret) - return ret; - fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; - fival->discrete = fie.interval; + phy->sensor = subdev; + phy_dbg(1, phy, "Using sensor %s for capture\n", subdev->name); return 0; } -/* - * Videobuf operations - */ -static int cal_queue_setup(struct vb2_queue *vq, - unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], struct device *alloc_devs[]) +static int cal_async_notifier_complete(struct v4l2_async_notifier *notifier) { - struct cal_ctx *ctx = vb2_get_drv_priv(vq); - unsigned size = ctx->v_fmt.fmt.pix.sizeimage; - - if (vq->num_buffers + *nbuffers < 3) - *nbuffers = 3 - vq->num_buffers; - - if (*nplanes) { - if (sizes[0] < size) - return -EINVAL; - size = sizes[0]; - } - - *nplanes = 1; - sizes[0] = size; - - ctx_dbg(3, ctx, "nbuffers=%d, size=%d\n", *nbuffers, sizes[0]); - - return 0; -} + struct cal_dev *cal = container_of(notifier, struct cal_dev, notifier); + unsigned int i; -static int cal_buffer_prepare(struct vb2_buffer *vb) -{ - struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - struct cal_buffer *buf = container_of(vb, struct cal_buffer, - vb.vb2_buf); - unsigned long size; - - if (WARN_ON(!ctx->fmt)) - return -EINVAL; - - size = ctx->v_fmt.fmt.pix.sizeimage; - if (vb2_plane_size(vb, 0) < size) { - ctx_err(ctx, - "data will not fit into plane (%lu < %lu)\n", - vb2_plane_size(vb, 0), size); - return -EINVAL; + for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { + if (cal->ctx[i]) + cal_ctx_v4l2_register(cal->ctx[i]); } - vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); return 0; } -static void cal_buffer_queue(struct vb2_buffer *vb) -{ - struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - struct cal_buffer *buf = container_of(vb, struct cal_buffer, - vb.vb2_buf); - struct cal_dmaqueue *vidq = &ctx->vidq; - unsigned long flags = 0; - - /* recheck locking */ - spin_lock_irqsave(&ctx->slock, flags); - list_add_tail(&buf->list, &vidq->active); - spin_unlock_irqrestore(&ctx->slock, flags); -} +static const struct v4l2_async_notifier_operations cal_async_notifier_ops = { + .bound = cal_async_notifier_bound, + .complete = cal_async_notifier_complete, +}; -static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) +static int cal_async_notifier_register(struct cal_dev *cal) { - struct cal_ctx *ctx = vb2_get_drv_priv(vq); - struct cal_dmaqueue *dma_q = &ctx->vidq; - struct cal_buffer *buf, *tmp; - unsigned long addr = 0; - unsigned long flags; + unsigned int i; int ret; - spin_lock_irqsave(&ctx->slock, flags); - if (list_empty(&dma_q->active)) { - spin_unlock_irqrestore(&ctx->slock, flags); - ctx_dbg(3, ctx, "buffer queue is empty\n"); - return -EIO; - } + v4l2_async_notifier_init(&cal->notifier); + cal->notifier.ops = &cal_async_notifier_ops; - buf = list_entry(dma_q->active.next, struct cal_buffer, list); - ctx->cur_frm = buf; - ctx->next_frm = buf; - list_del(&buf->list); - spin_unlock_irqrestore(&ctx->slock, flags); + for (i = 0; i < ARRAY_SIZE(cal->phy); ++i) { + struct cal_camerarx *phy = cal->phy[i]; + struct cal_v4l2_async_subdev *casd; + struct v4l2_async_subdev *asd; + struct fwnode_handle *fwnode; - addr = vb2_dma_contig_plane_dma_addr(&ctx->cur_frm->vb.vb2_buf, 0); - ctx->sequence = 0; + if (!phy || !phy->sensor_node) + continue; - ret = cal_get_external_info(ctx); - if (ret < 0) - goto err; + fwnode = of_fwnode_handle(phy->sensor_node); + asd = v4l2_async_notifier_add_fwnode_subdev(&cal->notifier, + fwnode, + sizeof(*asd)); + if (IS_ERR(asd)) { + phy_err(phy, "Failed to add subdev to notifier\n"); + ret = PTR_ERR(asd); + goto error; + } - ret = v4l2_subdev_call(ctx->sensor, core, s_power, 1); - if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) { - ctx_err(ctx, "power on failed in subdev\n"); - goto err; + casd = to_cal_asd(asd); + casd->phy = phy; } - pm_runtime_get_sync(&ctx->dev->pdev->dev); - - csi2_ctx_config(ctx); - pix_proc_config(ctx); - cal_wr_dma_config(ctx, ctx->v_fmt.fmt.pix.bytesperline, - ctx->v_fmt.fmt.pix.height); - csi2_lane_config(ctx); - - enable_irqs(ctx); - csi2_phy_init(ctx); - - ret = v4l2_subdev_call(ctx->sensor, video, s_stream, 1); + ret = v4l2_async_notifier_register(&cal->v4l2_dev, &cal->notifier); if (ret) { - v4l2_subdev_call(ctx->sensor, core, s_power, 0); - ctx_err(ctx, "stream on failed in subdev\n"); - pm_runtime_put_sync(&ctx->dev->pdev->dev); - goto err; + cal_err(cal, "Error registering async notifier\n"); + goto error; } - csi2_wait_for_phy(ctx); - cal_wr_dma_addr(ctx, addr); - csi2_ppi_enable(ctx); - - if (debug >= 4) - cal_quickdump_regs(ctx->dev); - return 0; -err: - spin_lock_irqsave(&ctx->slock, flags); - vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_QUEUED); - ctx->cur_frm = NULL; - ctx->next_frm = NULL; - list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { - list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); - } - spin_unlock_irqrestore(&ctx->slock, flags); +error: + v4l2_async_notifier_cleanup(&cal->notifier); return ret; } -static void cal_stop_streaming(struct vb2_queue *vq) +static void cal_async_notifier_unregister(struct cal_dev *cal) { - struct cal_ctx *ctx = vb2_get_drv_priv(vq); - struct cal_dmaqueue *dma_q = &ctx->vidq; - struct cal_buffer *buf, *tmp; - unsigned long timeout; - unsigned long flags; - int ret; - bool dma_act; - - csi2_ppi_disable(ctx); - - /* wait for stream and dma to finish */ - dma_act = true; - timeout = jiffies + msecs_to_jiffies(500); - while (dma_act && time_before(jiffies, timeout)) { - msleep(50); - - spin_lock_irqsave(&ctx->slock, flags); - dma_act = ctx->dma_act; - spin_unlock_irqrestore(&ctx->slock, flags); - } - - if (dma_act) - ctx_err(ctx, "failed to disable dma cleanly\n"); - - disable_irqs(ctx); - csi2_phy_deinit(ctx); - - if (v4l2_subdev_call(ctx->sensor, video, s_stream, 0)) - ctx_err(ctx, "stream off failed in subdev\n"); - - ret = v4l2_subdev_call(ctx->sensor, core, s_power, 0); - if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) - ctx_err(ctx, "power off failed in subdev\n"); - - /* Release all active buffers */ - spin_lock_irqsave(&ctx->slock, flags); - list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { - list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); - } - - if (ctx->cur_frm == ctx->next_frm) { - vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); - } else { - vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); - vb2_buffer_done(&ctx->next_frm->vb.vb2_buf, - VB2_BUF_STATE_ERROR); - } - ctx->cur_frm = NULL; - ctx->next_frm = NULL; - spin_unlock_irqrestore(&ctx->slock, flags); - - pm_runtime_put_sync(&ctx->dev->pdev->dev); + v4l2_async_notifier_unregister(&cal->notifier); + v4l2_async_notifier_cleanup(&cal->notifier); } -static const struct vb2_ops cal_video_qops = { - .queue_setup = cal_queue_setup, - .buf_prepare = cal_buffer_prepare, - .buf_queue = cal_buffer_queue, - .start_streaming = cal_start_streaming, - .stop_streaming = cal_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -static const struct v4l2_file_operations cal_fops = { - .owner = THIS_MODULE, - .open = v4l2_fh_open, - .release = vb2_fop_release, - .read = vb2_fop_read, - .poll = vb2_fop_poll, - .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ - .mmap = vb2_fop_mmap, -}; - -static const struct v4l2_ioctl_ops cal_ioctl_ops = { - .vidioc_querycap = cal_querycap, - .vidioc_enum_fmt_vid_cap = cal_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = cal_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = cal_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = cal_s_fmt_vid_cap, - .vidioc_enum_framesizes = cal_enum_framesizes, - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_create_bufs = vb2_ioctl_create_bufs, - .vidioc_prepare_buf = vb2_ioctl_prepare_buf, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_expbuf = vb2_ioctl_expbuf, - .vidioc_enum_input = cal_enum_input, - .vidioc_g_input = cal_g_input, - .vidioc_s_input = cal_s_input, - .vidioc_enum_frameintervals = cal_enum_frameintervals, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - .vidioc_log_status = v4l2_ctrl_log_status, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static const struct video_device cal_videodev = { - .name = CAL_MODULE_NAME, - .fops = &cal_fops, - .ioctl_ops = &cal_ioctl_ops, - .minor = -1, - .release = video_device_release_empty, - .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE, -}; - -/* ----------------------------------------------------------------- - * Initialization and module stuff +/* ------------------------------------------------------------------ + * Media and V4L2 device handling * ------------------------------------------------------------------ */ -static int cal_complete_ctx(struct cal_ctx *ctx); -static int cal_async_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) +/* + * Register user-facing devices. To be called at the end of the probe function + * when all resources are initialized and ready. + */ +static int cal_media_register(struct cal_dev *cal) { - struct cal_ctx *ctx = notifier_to_ctx(notifier); - struct v4l2_subdev_mbus_code_enum mbus_code; - int ret = 0; - int i, j, k; - - if (ctx->sensor) { - ctx_info(ctx, "Rejecting subdev %s (Already set!!)", - subdev->name); - return 0; - } - - ctx->sensor = subdev; - ctx_dbg(1, ctx, "Using sensor %s for capture\n", subdev->name); - - /* Enumerate sub device formats and enable all matching local formats */ - ctx->num_active_fmt = 0; - for (j = 0, i = 0; ret != -EINVAL; ++j) { - struct cal_fmt *fmt; - - memset(&mbus_code, 0, sizeof(mbus_code)); - mbus_code.index = j; - mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; - ret = v4l2_subdev_call(subdev, pad, enum_mbus_code, - NULL, &mbus_code); - if (ret) - continue; - - ctx_dbg(2, ctx, - "subdev %s: code: %04x idx: %d\n", - subdev->name, mbus_code.code, j); - - for (k = 0; k < ARRAY_SIZE(cal_formats); k++) { - fmt = &cal_formats[k]; + int ret; - if (mbus_code.code == fmt->code) { - ctx->active_fmt[i] = fmt; - ctx_dbg(2, ctx, - "matched fourcc: %s: code: %04x idx: %d\n", - fourcc_to_str(fmt->fourcc), - fmt->code, i); - ctx->num_active_fmt = ++i; - } - } + ret = media_device_register(&cal->mdev); + if (ret) { + cal_err(cal, "Failed to register media device\n"); + return ret; } - if (i == 0) { - ctx_err(ctx, "No suitable format reported by subdev %s\n", - subdev->name); - return -EINVAL; + /* + * Register the async notifier. This may trigger registration of the + * V4L2 video devices if all subdevs are ready. + */ + ret = cal_async_notifier_register(cal); + if (ret) { + media_device_unregister(&cal->mdev); + return ret; } - cal_complete_ctx(ctx); - return 0; } -static int cal_async_complete(struct v4l2_async_notifier *notifier) +/* + * Unregister the user-facing devices, but don't free memory yet. To be called + * at the beginning of the remove function, to disallow access from userspace. + */ +static void cal_media_unregister(struct cal_dev *cal) { - struct cal_ctx *ctx = notifier_to_ctx(notifier); - const struct cal_fmt *fmt; - struct v4l2_mbus_framefmt mbus_fmt; - int ret; - - ret = __subdev_get_format(ctx, &mbus_fmt); - if (ret) - return ret; + unsigned int i; - fmt = find_format_by_code(ctx, mbus_fmt.code); - if (!fmt) { - ctx_dbg(3, ctx, "mbus code format (0x%08x) not found.\n", - mbus_fmt.code); - return -EINVAL; + /* Unregister all the V4L2 video devices. */ + for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { + if (cal->ctx[i]) + cal_ctx_v4l2_unregister(cal->ctx[i]); } - /* Save current subdev format */ - v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); - ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; - cal_calc_format_size(ctx, fmt, &ctx->v_fmt); - ctx->fmt = fmt; - ctx->m_fmt = mbus_fmt; - - return 0; + cal_async_notifier_unregister(cal); + media_device_unregister(&cal->mdev); } -static const struct v4l2_async_notifier_operations cal_async_ops = { - .bound = cal_async_bound, - .complete = cal_async_complete, -}; - -static int cal_complete_ctx(struct cal_ctx *ctx) +/* + * Initialize the in-kernel objects. To be called at the beginning of the probe + * function, before the V4L2 device is used by the driver. + */ +static int cal_media_init(struct cal_dev *cal) { - struct video_device *vfd; - struct vb2_queue *q; + struct media_device *mdev = &cal->mdev; int ret; - ctx->timeperframe = tpf_default; - ctx->external_rate = 192000000; - - /* initialize locks */ - spin_lock_init(&ctx->slock); - mutex_init(&ctx->mutex); - - /* initialize queue */ - q = &ctx->vb_vidq; - q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; - q->drv_priv = ctx; - q->buf_struct_size = sizeof(struct cal_buffer); - q->ops = &cal_video_qops; - q->mem_ops = &vb2_dma_contig_memops; - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->lock = &ctx->mutex; - q->min_buffers_needed = 3; - q->dev = ctx->v4l2_dev.dev; - - ret = vb2_queue_init(q); - if (ret) - return ret; - - /* init video dma queues */ - INIT_LIST_HEAD(&ctx->vidq.active); - - vfd = &ctx->vdev; - *vfd = cal_videodev; - vfd->v4l2_dev = &ctx->v4l2_dev; - vfd->queue = q; + mdev->dev = cal->dev; + mdev->hw_revision = cal->revision; + strscpy(mdev->model, "CAL", sizeof(mdev->model)); + snprintf(mdev->bus_info, sizeof(mdev->bus_info), "platform:%s", + dev_name(mdev->dev)); + media_device_init(mdev); /* - * Provide a mutex to v4l2 core. It will be used to protect - * all fops and v4l2 ioctls. + * Initialize the V4L2 device (despite the function name, this performs + * initialization, not registration). */ - vfd->lock = &ctx->mutex; - video_set_drvdata(vfd, ctx); - - ret = video_register_device(vfd, VFL_TYPE_VIDEO, video_nr); - if (ret < 0) + cal->v4l2_dev.mdev = mdev; + ret = v4l2_device_register(cal->dev, &cal->v4l2_dev); + if (ret) { + cal_err(cal, "Failed to register V4L2 device\n"); return ret; + } - v4l2_info(&ctx->v4l2_dev, "V4L2 device registered as %s\n", - video_device_node_name(vfd)); + vb2_dma_contig_set_max_seg_size(cal->dev, DMA_BIT_MASK(32)); return 0; } -static struct device_node * -of_get_next_port(const struct device_node *parent, - struct device_node *prev) +/* + * Cleanup the in-kernel objects, freeing memory. To be called at the very end + * of the remove sequence, when nothing (including userspace) can access the + * objects anymore. + */ +static void cal_media_cleanup(struct cal_dev *cal) { - struct device_node *port = NULL; - - if (!parent) - return NULL; + unsigned int i; - if (!prev) { - struct device_node *ports; - /* - * It's the first call, we have to find a port subnode - * within this node or within an optional 'ports' node. - */ - ports = of_get_child_by_name(parent, "ports"); - if (ports) - parent = ports; - - port = of_get_child_by_name(parent, "port"); - - /* release the 'ports' node */ - of_node_put(ports); - } else { - struct device_node *ports; - - ports = of_get_parent(prev); - if (!ports) - return NULL; - - do { - port = of_get_next_child(ports, prev); - if (!port) { - of_node_put(ports); - return NULL; - } - prev = port; - } while (!of_node_name_eq(port, "port")); - of_node_put(ports); + for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { + if (cal->ctx[i]) + cal_ctx_v4l2_cleanup(cal->ctx[i]); } - return port; -} - -static struct device_node * -of_get_next_endpoint(const struct device_node *parent, - struct device_node *prev) -{ - struct device_node *ep = NULL; - - if (!parent) - return NULL; - - do { - ep = of_get_next_child(parent, prev); - if (!ep) - return NULL; - prev = ep; - } while (!of_node_name_eq(ep, "endpoint")); + v4l2_device_unregister(&cal->v4l2_dev); + media_device_cleanup(&cal->mdev); - return ep; + vb2_dma_contig_clear_max_seg_size(cal->dev); } -static int of_cal_create_instance(struct cal_ctx *ctx, int inst) -{ - struct platform_device *pdev = ctx->dev->pdev; - struct device_node *ep_node, *port, *sensor_node, *parent; - struct v4l2_fwnode_endpoint *endpoint; - struct v4l2_async_subdev *asd; - u32 regval = 0; - int ret, index, found_port = 0, lane; - - parent = pdev->dev.of_node; - - endpoint = &ctx->endpoint; - - ep_node = NULL; - port = NULL; - sensor_node = NULL; - ret = -EINVAL; - - ctx_dbg(3, ctx, "Scanning Port node for csi2 port: %d\n", inst); - for (index = 0; index < CAL_NUM_CSI2_PORTS; index++) { - port = of_get_next_port(parent, port); - if (!port) { - ctx_dbg(1, ctx, "No port node found for csi2 port:%d\n", - index); - goto cleanup_exit; - } - - /* Match the slice number with <REG> */ - of_property_read_u32(port, "reg", ®val); - ctx_dbg(3, ctx, "port:%d inst:%d <reg>:%d\n", - index, inst, regval); - if ((regval == inst) && (index == inst)) { - found_port = 1; - break; - } - } - - if (!found_port) { - ctx_dbg(1, ctx, "No port node matches csi2 port:%d\n", - inst); - goto cleanup_exit; - } - - ctx_dbg(3, ctx, "Scanning sub-device for csi2 port: %d\n", - inst); - - ep_node = of_get_next_endpoint(port, ep_node); - if (!ep_node) { - ctx_dbg(3, ctx, "can't get next endpoint\n"); - goto cleanup_exit; - } - - sensor_node = of_graph_get_remote_port_parent(ep_node); - if (!sensor_node) { - ctx_dbg(3, ctx, "can't get remote parent\n"); - goto cleanup_exit; - } - - v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), endpoint); - - if (endpoint->bus_type != V4L2_MBUS_CSI2_DPHY) { - ctx_err(ctx, "Port:%d sub-device %pOFn is not a CSI2 device\n", - inst, sensor_node); - goto cleanup_exit; - } - - /* Store Virtual Channel number */ - ctx->virtual_channel = endpoint->base.id; - - ctx_dbg(3, ctx, "Port:%d v4l2-endpoint: CSI2\n", inst); - ctx_dbg(3, ctx, "Virtual Channel=%d\n", ctx->virtual_channel); - ctx_dbg(3, ctx, "flags=0x%08x\n", endpoint->bus.mipi_csi2.flags); - ctx_dbg(3, ctx, "clock_lane=%d\n", endpoint->bus.mipi_csi2.clock_lane); - ctx_dbg(3, ctx, "num_data_lanes=%d\n", - endpoint->bus.mipi_csi2.num_data_lanes); - ctx_dbg(3, ctx, "data_lanes= <\n"); - for (lane = 0; lane < endpoint->bus.mipi_csi2.num_data_lanes; lane++) - ctx_dbg(3, ctx, "\t%d\n", - endpoint->bus.mipi_csi2.data_lanes[lane]); - ctx_dbg(3, ctx, "\t>\n"); - - ctx_dbg(1, ctx, "Port: %d found sub-device %pOFn\n", - inst, sensor_node); - - v4l2_async_notifier_init(&ctx->notifier); - - asd = kzalloc(sizeof(*asd), GFP_KERNEL); - if (!asd) - goto cleanup_exit; - - asd->match_type = V4L2_ASYNC_MATCH_FWNODE; - asd->match.fwnode = of_fwnode_handle(sensor_node); - - ret = v4l2_async_notifier_add_subdev(&ctx->notifier, asd); - if (ret) { - ctx_err(ctx, "Error adding asd\n"); - kfree(asd); - goto cleanup_exit; - } - - ctx->notifier.ops = &cal_async_ops; - ret = v4l2_async_notifier_register(&ctx->v4l2_dev, - &ctx->notifier); - if (ret) { - ctx_err(ctx, "Error registering async notifier\n"); - v4l2_async_notifier_cleanup(&ctx->notifier); - ret = -EINVAL; - } - - /* - * On success we need to keep reference on sensor_node, or - * if notifier_cleanup was called above, sensor_node was - * already put. - */ - sensor_node = NULL; - -cleanup_exit: - of_node_put(sensor_node); - of_node_put(ep_node); - of_node_put(port); - - return ret; -} +/* ------------------------------------------------------------------ + * Initialization and module stuff + * ------------------------------------------------------------------ + */ -static struct cal_ctx *cal_create_instance(struct cal_dev *dev, int inst) +static struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst) { struct cal_ctx *ctx; - struct v4l2_ctrl_handler *hdl; int ret; - ctx = devm_kzalloc(&dev->pdev->dev, sizeof(*ctx), GFP_KERNEL); + ctx = devm_kzalloc(cal->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return NULL; - /* save the cal_dev * for future ref */ - ctx->dev = dev; + ctx->cal = cal; + ctx->phy = cal->phy[inst]; + ctx->index = inst; + ctx->cport = inst; - snprintf(ctx->v4l2_dev.name, sizeof(ctx->v4l2_dev.name), - "%s-%03d", CAL_MODULE_NAME, inst); - ret = v4l2_device_register(&dev->pdev->dev, &ctx->v4l2_dev); + ret = cal_ctx_v4l2_init(ctx); if (ret) - goto err_exit; - - hdl = &ctx->ctrl_handler; - ret = v4l2_ctrl_handler_init(hdl, 11); - if (ret) { - ctx_err(ctx, "Failed to init ctrl handler\n"); - goto unreg_dev; - } - ctx->v4l2_dev.ctrl_handler = hdl; - - /* Make sure Camera Core H/W register area is available */ - ctx->cc = dev->cc[inst]; - - /* Store the instance id */ - ctx->csi2_port = inst + 1; + return NULL; - ret = of_cal_create_instance(ctx, inst); - if (ret) { - ret = -EINVAL; - goto free_hdl; - } return ctx; - -free_hdl: - v4l2_ctrl_handler_free(hdl); -unreg_dev: - v4l2_device_unregister(&ctx->v4l2_dev); -err_exit: - return NULL; } static const struct of_device_id cal_of_match[] = { @@ -2258,191 +651,253 @@ static const struct of_device_id cal_of_match[] = { }; MODULE_DEVICE_TABLE(of, cal_of_match); +/* Get hardware revision and info. */ + +#define CAL_HL_HWINFO_VALUE 0xa3c90469 + +static void cal_get_hwinfo(struct cal_dev *cal) +{ + u32 hwinfo; + + cal->revision = cal_read(cal, CAL_HL_REVISION); + switch (FIELD_GET(CAL_HL_REVISION_SCHEME_MASK, cal->revision)) { + case CAL_HL_REVISION_SCHEME_H08: + cal_dbg(3, cal, "CAL HW revision %lu.%lu.%lu (0x%08x)\n", + FIELD_GET(CAL_HL_REVISION_MAJOR_MASK, cal->revision), + FIELD_GET(CAL_HL_REVISION_MINOR_MASK, cal->revision), + FIELD_GET(CAL_HL_REVISION_RTL_MASK, cal->revision), + cal->revision); + break; + + case CAL_HL_REVISION_SCHEME_LEGACY: + default: + cal_info(cal, "Unexpected CAL HW revision 0x%08x\n", + cal->revision); + break; + } + + hwinfo = cal_read(cal, CAL_HL_HWINFO); + if (hwinfo != CAL_HL_HWINFO_VALUE) + cal_info(cal, "CAL_HL_HWINFO = 0x%08x, expected 0x%08x\n", + hwinfo, CAL_HL_HWINFO_VALUE); +} + +static int cal_init_camerarx_regmap(struct cal_dev *cal) +{ + struct platform_device *pdev = to_platform_device(cal->dev); + struct device_node *np = cal->dev->of_node; + struct regmap_config config = { }; + struct regmap *syscon; + struct resource *res; + unsigned int offset; + void __iomem *base; + + syscon = syscon_regmap_lookup_by_phandle_args(np, "ti,camerrx-control", + 1, &offset); + if (!IS_ERR(syscon)) { + cal->syscon_camerrx = syscon; + cal->syscon_camerrx_offset = offset; + return 0; + } + + dev_warn(cal->dev, "failed to get ti,camerrx-control: %ld\n", + PTR_ERR(syscon)); + + /* + * Backward DTS compatibility. If syscon entry is not present then + * check if the camerrx_control resource is present. + */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "camerrx_control"); + base = devm_ioremap_resource(cal->dev, res); + if (IS_ERR(base)) { + cal_err(cal, "failed to ioremap camerrx_control\n"); + return PTR_ERR(base); + } + + cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", + res->name, &res->start, &res->end); + + config.reg_bits = 32; + config.reg_stride = 4; + config.val_bits = 32; + config.max_register = resource_size(res) - 4; + + syscon = regmap_init_mmio(NULL, base, &config); + if (IS_ERR(syscon)) { + pr_err("regmap init failed\n"); + return PTR_ERR(syscon); + } + + /* + * In this case the base already point to the direct CM register so no + * need for an offset. + */ + cal->syscon_camerrx = syscon; + cal->syscon_camerrx_offset = 0; + + return 0; +} + static int cal_probe(struct platform_device *pdev) { - struct cal_dev *dev; + struct cal_dev *cal; struct cal_ctx *ctx; - struct device_node *parent = pdev->dev.of_node; - struct regmap *syscon_camerrx = NULL; - u32 syscon_camerrx_offset = 0; + bool connected = false; + unsigned int i; int ret; int irq; - int i; - dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); - if (!dev) + cal = devm_kzalloc(&pdev->dev, sizeof(*cal), GFP_KERNEL); + if (!cal) return -ENOMEM; - dev->data = of_device_get_match_data(&pdev->dev); - if (!dev->data) { + cal->data = of_device_get_match_data(&pdev->dev); + if (!cal->data) { dev_err(&pdev->dev, "Could not get feature data based on compatible version\n"); return -ENODEV; } - dev->flags = dev->data->flags; - - /* set pseudo v4l2 device name so we can use v4l2_printk */ - strscpy(dev->v4l2_dev.name, CAL_MODULE_NAME, - sizeof(dev->v4l2_dev.name)); + cal->dev = &pdev->dev; + platform_set_drvdata(pdev, cal); - /* save pdev pointer */ - dev->pdev = pdev; - - dev->fclk = devm_clk_get(&pdev->dev, "fck"); - if (IS_ERR(dev->fclk)) { + /* Acquire resources: clocks, CAMERARX regmap, I/O memory and IRQ. */ + cal->fclk = devm_clk_get(&pdev->dev, "fck"); + if (IS_ERR(cal->fclk)) { dev_err(&pdev->dev, "cannot get CAL fclk\n"); - return PTR_ERR(dev->fclk); + return PTR_ERR(cal->fclk); } - syscon_camerrx = syscon_regmap_lookup_by_phandle(parent, - "ti,camerrx-control"); - ret = of_property_read_u32_index(parent, "ti,camerrx-control", 1, - &syscon_camerrx_offset); - if (IS_ERR(syscon_camerrx)) - ret = PTR_ERR(syscon_camerrx); - if (ret) { - dev_warn(&pdev->dev, "failed to get ti,camerrx-control: %d\n", - ret); - - /* - * Backward DTS compatibility. - * If syscon entry is not present then check if the - * camerrx_control resource is present. - */ - syscon_camerrx = cal_get_camerarx_regmap(dev); - if (IS_ERR(syscon_camerrx)) { - dev_err(&pdev->dev, "failed to get camerrx_control regmap\n"); - return PTR_ERR(syscon_camerrx); - } - /* In this case the base already point to the direct - * CM register so no need for an offset - */ - syscon_camerrx_offset = 0; - } - - dev->syscon_camerrx = syscon_camerrx; - dev->syscon_camerrx_offset = syscon_camerrx_offset; - ret = cal_camerarx_regmap_init(dev); - if (ret) + ret = cal_init_camerarx_regmap(cal); + if (ret < 0) return ret; - dev->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + cal->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cal_top"); - dev->base = devm_ioremap_resource(&pdev->dev, dev->res); - if (IS_ERR(dev->base)) - return PTR_ERR(dev->base); + cal->base = devm_ioremap_resource(&pdev->dev, cal->res); + if (IS_ERR(cal->base)) + return PTR_ERR(cal->base); - cal_dbg(1, dev, "ioresource %s at %pa - %pa\n", - dev->res->name, &dev->res->start, &dev->res->end); + cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", + cal->res->name, &cal->res->start, &cal->res->end); irq = platform_get_irq(pdev, 0); - cal_dbg(1, dev, "got irq# %d\n", irq); + cal_dbg(1, cal, "got irq# %d\n", irq); ret = devm_request_irq(&pdev->dev, irq, cal_irq, 0, CAL_MODULE_NAME, - dev); + cal); if (ret) return ret; - platform_set_drvdata(pdev, dev); + /* Read the revision and hardware info to verify hardware access. */ + pm_runtime_enable(&pdev->dev); + ret = pm_runtime_get_sync(&pdev->dev); + if (ret) + goto error_pm_runtime; - dev->cc[0] = cc_create(dev, 0); - if (IS_ERR(dev->cc[0])) - return PTR_ERR(dev->cc[0]); + cal_get_hwinfo(cal); + pm_runtime_put_sync(&pdev->dev); - if (cal_data_get_num_csi2_phy(dev) > 1) { - dev->cc[1] = cc_create(dev, 1); - if (IS_ERR(dev->cc[1])) - return PTR_ERR(dev->cc[1]); - } else { - dev->cc[1] = NULL; - } + /* Create CAMERARX PHYs. */ + for (i = 0; i < cal->data->num_csi2_phy; ++i) { + cal->phy[i] = cal_camerarx_create(cal, i); + if (IS_ERR(cal->phy[i])) { + ret = PTR_ERR(cal->phy[i]); + cal->phy[i] = NULL; + goto error_camerarx; + } - dev->ctx[0] = NULL; - dev->ctx[1] = NULL; + if (cal->phy[i]->sensor_node) + connected = true; + } - dev->ctx[0] = cal_create_instance(dev, 0); - if (cal_data_get_num_csi2_phy(dev) > 1) - dev->ctx[1] = cal_create_instance(dev, 1); - if (!dev->ctx[0] && !dev->ctx[1]) { - cal_err(dev, "Neither port is configured, no point in staying up\n"); - return -ENODEV; + if (!connected) { + cal_err(cal, "Neither port is configured, no point in staying up\n"); + ret = -ENODEV; + goto error_camerarx; } - vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); + /* Initialize the media device. */ + ret = cal_media_init(cal); + if (ret < 0) + goto error_camerarx; - pm_runtime_enable(&pdev->dev); + /* Create contexts. */ + for (i = 0; i < cal->data->num_csi2_phy; ++i) { + if (!cal->phy[i]->sensor_node) + continue; - ret = pm_runtime_get_sync(&pdev->dev); + cal->ctx[i] = cal_ctx_create(cal, i); + if (!cal->ctx[i]) { + cal_err(cal, "Failed to create context %u\n", i); + ret = -ENODEV; + goto error_context; + } + } + + /* Register the media device. */ + ret = cal_media_register(cal); if (ret) - goto runtime_disable; + goto error_context; - /* Just check we can actually access the module */ - cal_get_hwinfo(dev); + return 0; - pm_runtime_put_sync(&pdev->dev); +error_context: + for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { + ctx = cal->ctx[i]; + if (ctx) + cal_ctx_v4l2_cleanup(ctx); + } - return 0; + cal_media_cleanup(cal); -runtime_disable: - vb2_dma_contig_clear_max_seg_size(&pdev->dev); +error_camerarx: + for (i = 0; i < ARRAY_SIZE(cal->phy); i++) + cal_camerarx_destroy(cal->phy[i]); +error_pm_runtime: pm_runtime_disable(&pdev->dev); - for (i = 0; i < CAL_NUM_CONTEXT; i++) { - ctx = dev->ctx[i]; - if (ctx) { - v4l2_async_notifier_unregister(&ctx->notifier); - v4l2_async_notifier_cleanup(&ctx->notifier); - v4l2_ctrl_handler_free(&ctx->ctrl_handler); - v4l2_device_unregister(&ctx->v4l2_dev); - } - } return ret; } static int cal_remove(struct platform_device *pdev) { - struct cal_dev *dev = - (struct cal_dev *)platform_get_drvdata(pdev); - struct cal_ctx *ctx; - int i; + struct cal_dev *cal = platform_get_drvdata(pdev); + unsigned int i; - cal_dbg(1, dev, "Removing %s\n", CAL_MODULE_NAME); + cal_dbg(1, cal, "Removing %s\n", CAL_MODULE_NAME); pm_runtime_get_sync(&pdev->dev); - for (i = 0; i < CAL_NUM_CONTEXT; i++) { - ctx = dev->ctx[i]; - if (ctx) { - ctx_dbg(1, ctx, "unregistering %s\n", - video_device_node_name(&ctx->vdev)); - camerarx_phy_disable(ctx); - v4l2_async_notifier_unregister(&ctx->notifier); - v4l2_async_notifier_cleanup(&ctx->notifier); - v4l2_ctrl_handler_free(&ctx->ctrl_handler); - v4l2_device_unregister(&ctx->v4l2_dev); - video_unregister_device(&ctx->vdev); - } + cal_media_unregister(cal); + + for (i = 0; i < ARRAY_SIZE(cal->phy); i++) { + if (cal->phy[i]) + cal_camerarx_disable(cal->phy[i]); } + cal_media_cleanup(cal); + + for (i = 0; i < ARRAY_SIZE(cal->phy); i++) + cal_camerarx_destroy(cal->phy[i]); + pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); - vb2_dma_contig_clear_max_seg_size(&pdev->dev); - return 0; } static int cal_runtime_resume(struct device *dev) { - struct cal_dev *caldev = dev_get_drvdata(dev); + struct cal_dev *cal = dev_get_drvdata(dev); - if (caldev->flags & DRA72_CAL_PRE_ES2_LDO_DISABLE) { + if (cal->data->flags & DRA72_CAL_PRE_ES2_LDO_DISABLE) { /* * Apply errata on both port everytime we (re-)enable * the clock */ - i913_errata(caldev, 0); - i913_errata(caldev, 1); + cal_camerarx_i913_errata(cal->phy[0]); + cal_camerarx_i913_errata(cal->phy[1]); } return 0; diff --git a/drivers/media/platform/ti-vpe/cal.h b/drivers/media/platform/ti-vpe/cal.h new file mode 100644 index 000000000000..e496083715d2 --- /dev/null +++ b/drivers/media/platform/ti-vpe/cal.h @@ -0,0 +1,267 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * TI Camera Access Layer (CAL) + * + * Copyright (c) 2015-2020 Texas Instruments Inc. + * + * Authors: + * Benoit Parrot <bparrot@ti.com> + * Laurent Pinchart <laurent.pinchart@ideasonboard.com> + */ +#ifndef __TI_CAL_H__ +#define __TI_CAL_H__ + +#include <linux/bitfield.h> +#include <linux/io.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/spinlock.h> +#include <linux/videodev2.h> + +#include <media/media-device.h> +#include <media/v4l2-async.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> +#include <media/videobuf2-v4l2.h> + +#define CAL_MODULE_NAME "cal" +#define CAL_NUM_CONTEXT 2 +#define CAL_NUM_CSI2_PORTS 2 + +#define MAX_WIDTH_BYTES (8192 * 8) +#define MAX_HEIGHT_LINES 16383 + +struct device; +struct device_node; +struct resource; +struct regmap; +struct regmap_fied; +struct v4l2_subdev; + +/* CTRL_CORE_CAMERRX_CONTROL register field id */ +enum cal_camerarx_field { + F_CTRLCLKEN, + F_CAMMODE, + F_LANEENABLE, + F_CSI_MODE, + F_MAX_FIELDS, +}; + +struct cal_fmt { + u32 fourcc; + u32 code; + /* Bits per pixel */ + u8 bpp; +}; + +/* buffer for one video frame */ +struct cal_buffer { + /* common v4l buffer stuff -- must be first */ + struct vb2_v4l2_buffer vb; + struct list_head list; +}; + +struct cal_dmaqueue { + struct list_head active; +}; + +struct cal_camerarx_data { + struct { + unsigned int lsb; + unsigned int msb; + } fields[F_MAX_FIELDS]; + unsigned int num_lanes; +}; + +struct cal_data { + const struct cal_camerarx_data *camerarx; + unsigned int num_csi2_phy; + unsigned int flags; +}; + +/* + * The Camera Adaptation Layer (CAL) module is paired with one or more complex + * I/O PHYs (CAMERARX). It contains multiple instances of CSI-2, processing and + * DMA contexts. + * + * The cal_dev structure represents the whole subsystem, including the CAL and + * the CAMERARX instances. Instances of struct cal_dev are named cal through the + * driver. + * + * The cal_camerarx structure represents one CAMERARX instance. Instances of + * cal_camerarx are named phy through the driver. + * + * The cal_ctx structure represents the combination of one CSI-2 context, one + * processing context and one DMA context. Instance of struct cal_ctx are named + * ctx through the driver. + */ + +struct cal_camerarx { + void __iomem *base; + struct resource *res; + struct device *dev; + struct regmap_field *fields[F_MAX_FIELDS]; + + struct cal_dev *cal; + unsigned int instance; + + struct v4l2_fwnode_endpoint endpoint; + struct device_node *sensor_node; + struct v4l2_subdev *sensor; +}; + +struct cal_dev { + struct clk *fclk; + int irq; + void __iomem *base; + struct resource *res; + struct device *dev; + + const struct cal_data *data; + u32 revision; + + /* Control Module handle */ + struct regmap *syscon_camerrx; + u32 syscon_camerrx_offset; + + /* Camera Core Module handle */ + struct cal_camerarx *phy[CAL_NUM_CSI2_PORTS]; + + struct cal_ctx *ctx[CAL_NUM_CONTEXT]; + + struct media_device mdev; + struct v4l2_device v4l2_dev; + struct v4l2_async_notifier notifier; +}; + +/* + * There is one cal_ctx structure for each camera core context. + */ +struct cal_ctx { + struct v4l2_ctrl_handler ctrl_handler; + struct video_device vdev; + struct media_pad pad; + + struct cal_dev *cal; + struct cal_camerarx *phy; + + /* v4l2_ioctl mutex */ + struct mutex mutex; + /* v4l2 buffers lock */ + spinlock_t slock; + + struct cal_dmaqueue vidq; + + /* video capture */ + const struct cal_fmt *fmt; + /* Used to store current pixel format */ + struct v4l2_format v_fmt; + /* Used to store current mbus frame format */ + struct v4l2_mbus_framefmt m_fmt; + + /* Current subdev enumerated format */ + const struct cal_fmt **active_fmt; + unsigned int num_active_fmt; + + unsigned int sequence; + struct vb2_queue vb_vidq; + unsigned int index; + unsigned int cport; + + /* Pointer pointing to current v4l2_buffer */ + struct cal_buffer *cur_frm; + /* Pointer pointing to next v4l2_buffer */ + struct cal_buffer *next_frm; + + bool dma_act; +}; + +extern unsigned int cal_debug; +extern int cal_video_nr; + +#define cal_dbg(level, cal, fmt, arg...) \ + do { \ + if (cal_debug >= (level)) \ + dev_printk(KERN_DEBUG, (cal)->dev, fmt, ##arg); \ + } while (0) +#define cal_info(cal, fmt, arg...) \ + dev_info((cal)->dev, fmt, ##arg) +#define cal_err(cal, fmt, arg...) \ + dev_err((cal)->dev, fmt, ##arg) + +#define ctx_dbg(level, ctx, fmt, arg...) \ + cal_dbg(level, (ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg) +#define ctx_info(ctx, fmt, arg...) \ + cal_info((ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg) +#define ctx_err(ctx, fmt, arg...) \ + cal_err((ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg) + +#define phy_dbg(level, phy, fmt, arg...) \ + cal_dbg(level, (phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) +#define phy_info(phy, fmt, arg...) \ + cal_info((phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) +#define phy_err(phy, fmt, arg...) \ + cal_err((phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) + +static inline u32 cal_read(struct cal_dev *cal, u32 offset) +{ + return ioread32(cal->base + offset); +} + +static inline void cal_write(struct cal_dev *cal, u32 offset, u32 val) +{ + iowrite32(val, cal->base + offset); +} + +static inline u32 cal_read_field(struct cal_dev *cal, u32 offset, u32 mask) +{ + return FIELD_GET(mask, cal_read(cal, offset)); +} + +static inline void cal_write_field(struct cal_dev *cal, u32 offset, u32 value, + u32 mask) +{ + u32 val = cal_read(cal, offset); + + val &= ~mask; + val |= FIELD_PREP(mask, value); + cal_write(cal, offset, val); +} + +static inline void cal_set_field(u32 *valp, u32 field, u32 mask) +{ + u32 val = *valp; + + val &= ~mask; + val |= (field << __ffs(mask)) & mask; + *valp = val; +} + +void cal_quickdump_regs(struct cal_dev *cal); + +void cal_camerarx_disable(struct cal_camerarx *phy); +int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt); +void cal_camerarx_stop(struct cal_camerarx *phy); +void cal_camerarx_enable_irqs(struct cal_camerarx *phy); +void cal_camerarx_disable_irqs(struct cal_camerarx *phy); +void cal_camerarx_ppi_enable(struct cal_camerarx *phy); +void cal_camerarx_ppi_disable(struct cal_camerarx *phy); +void cal_camerarx_i913_errata(struct cal_camerarx *phy); +struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, + unsigned int instance); +void cal_camerarx_destroy(struct cal_camerarx *phy); + +void cal_ctx_csi2_config(struct cal_ctx *ctx); +void cal_ctx_pix_proc_config(struct cal_ctx *ctx); +void cal_ctx_wr_dma_config(struct cal_ctx *ctx, unsigned int width, + unsigned int height); +void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr); + +int cal_ctx_v4l2_register(struct cal_ctx *ctx); +void cal_ctx_v4l2_unregister(struct cal_ctx *ctx); +int cal_ctx_v4l2_init(struct cal_ctx *ctx); +void cal_ctx_v4l2_cleanup(struct cal_ctx *ctx); + +#endif /* __TI_CAL_H__ */ diff --git a/drivers/media/platform/ti-vpe/cal_regs.h b/drivers/media/platform/ti-vpe/cal_regs.h index ac54a2fe7bb6..f752096dcf7f 100644 --- a/drivers/media/platform/ti-vpe/cal_regs.h +++ b/drivers/media/platform/ti-vpe/cal_regs.h @@ -34,19 +34,17 @@ */ #define DRA72_CAL_PRE_ES2_LDO_DISABLE BIT(0) -#define CAL_NUM_CSI2_PORTS 2 - /* CAL register offsets */ #define CAL_HL_REVISION 0x0000 #define CAL_HL_HWINFO 0x0004 #define CAL_HL_SYSCONFIG 0x0010 #define CAL_HL_IRQ_EOI 0x001c -#define CAL_HL_IRQSTATUS_RAW(m) (0x20U + ((m-1) * 0x10U)) -#define CAL_HL_IRQSTATUS(m) (0x24U + ((m-1) * 0x10U)) -#define CAL_HL_IRQENABLE_SET(m) (0x28U + ((m-1) * 0x10U)) -#define CAL_HL_IRQENABLE_CLR(m) (0x2cU + ((m-1) * 0x10U)) -#define CAL_PIX_PROC(m) (0xc0U + ((m-1) * 0x4U)) +#define CAL_HL_IRQSTATUS_RAW(m) (0x20U + (m) * 0x10U) +#define CAL_HL_IRQSTATUS(m) (0x24U + (m) * 0x10U) +#define CAL_HL_IRQENABLE_SET(m) (0x28U + (m) * 0x10U) +#define CAL_HL_IRQENABLE_CLR(m) (0x2cU + (m) * 0x10U) +#define CAL_PIX_PROC(m) (0xc0U + (m) * 0x4U) #define CAL_CTRL 0x100 #define CAL_CTRL1 0x104 #define CAL_LINE_NUMBER_EVT 0x108 @@ -62,34 +60,34 @@ #define CAL_RD_DMA_INIT_ADDR 0x154 #define CAL_RD_DMA_INIT_OFST 0x168 #define CAL_RD_DMA_CTRL2 0x16c -#define CAL_WR_DMA_CTRL(m) (0x200U + ((m-1) * 0x10U)) -#define CAL_WR_DMA_ADDR(m) (0x204U + ((m-1) * 0x10U)) -#define CAL_WR_DMA_OFST(m) (0x208U + ((m-1) * 0x10U)) -#define CAL_WR_DMA_XSIZE(m) (0x20cU + ((m-1) * 0x10U)) -#define CAL_CSI2_PPI_CTRL(m) (0x300U + ((m-1) * 0x80U)) -#define CAL_CSI2_COMPLEXIO_CFG(m) (0x304U + ((m-1) * 0x80U)) -#define CAL_CSI2_COMPLEXIO_IRQSTATUS(m) (0x308U + ((m-1) * 0x80U)) -#define CAL_CSI2_SHORT_PACKET(m) (0x30cU + ((m-1) * 0x80U)) -#define CAL_CSI2_COMPLEXIO_IRQENABLE(m) (0x310U + ((m-1) * 0x80U)) -#define CAL_CSI2_TIMING(m) (0x314U + ((m-1) * 0x80U)) -#define CAL_CSI2_VC_IRQENABLE(m) (0x318U + ((m-1) * 0x80U)) -#define CAL_CSI2_VC_IRQSTATUS(m) (0x328U + ((m-1) * 0x80U)) -#define CAL_CSI2_CTX0(m) (0x330U + ((m-1) * 0x80U)) -#define CAL_CSI2_CTX1(m) (0x334U + ((m-1) * 0x80U)) -#define CAL_CSI2_CTX2(m) (0x338U + ((m-1) * 0x80U)) -#define CAL_CSI2_CTX3(m) (0x33cU + ((m-1) * 0x80U)) -#define CAL_CSI2_CTX4(m) (0x340U + ((m-1) * 0x80U)) -#define CAL_CSI2_CTX5(m) (0x344U + ((m-1) * 0x80U)) -#define CAL_CSI2_CTX6(m) (0x348U + ((m-1) * 0x80U)) -#define CAL_CSI2_CTX7(m) (0x34cU + ((m-1) * 0x80U)) -#define CAL_CSI2_STATUS0(m) (0x350U + ((m-1) * 0x80U)) -#define CAL_CSI2_STATUS1(m) (0x354U + ((m-1) * 0x80U)) -#define CAL_CSI2_STATUS2(m) (0x358U + ((m-1) * 0x80U)) -#define CAL_CSI2_STATUS3(m) (0x35cU + ((m-1) * 0x80U)) -#define CAL_CSI2_STATUS4(m) (0x360U + ((m-1) * 0x80U)) -#define CAL_CSI2_STATUS5(m) (0x364U + ((m-1) * 0x80U)) -#define CAL_CSI2_STATUS6(m) (0x368U + ((m-1) * 0x80U)) -#define CAL_CSI2_STATUS7(m) (0x36cU + ((m-1) * 0x80U)) +#define CAL_WR_DMA_CTRL(m) (0x200U + (m) * 0x10U) +#define CAL_WR_DMA_ADDR(m) (0x204U + (m) * 0x10U) +#define CAL_WR_DMA_OFST(m) (0x208U + (m) * 0x10U) +#define CAL_WR_DMA_XSIZE(m) (0x20cU + (m) * 0x10U) +#define CAL_CSI2_PPI_CTRL(m) (0x300U + (m) * 0x80U) +#define CAL_CSI2_COMPLEXIO_CFG(m) (0x304U + (m) * 0x80U) +#define CAL_CSI2_COMPLEXIO_IRQSTATUS(m) (0x308U + (m) * 0x80U) +#define CAL_CSI2_SHORT_PACKET(m) (0x30cU + (m) * 0x80U) +#define CAL_CSI2_COMPLEXIO_IRQENABLE(m) (0x310U + (m) * 0x80U) +#define CAL_CSI2_TIMING(m) (0x314U + (m) * 0x80U) +#define CAL_CSI2_VC_IRQENABLE(m) (0x318U + (m) * 0x80U) +#define CAL_CSI2_VC_IRQSTATUS(m) (0x328U + (m) * 0x80U) +#define CAL_CSI2_CTX0(m) (0x330U + (m) * 0x80U) +#define CAL_CSI2_CTX1(m) (0x334U + (m) * 0x80U) +#define CAL_CSI2_CTX2(m) (0x338U + (m) * 0x80U) +#define CAL_CSI2_CTX3(m) (0x33cU + (m) * 0x80U) +#define CAL_CSI2_CTX4(m) (0x340U + (m) * 0x80U) +#define CAL_CSI2_CTX5(m) (0x344U + (m) * 0x80U) +#define CAL_CSI2_CTX6(m) (0x348U + (m) * 0x80U) +#define CAL_CSI2_CTX7(m) (0x34cU + (m) * 0x80U) +#define CAL_CSI2_STATUS0(m) (0x350U + (m) * 0x80U) +#define CAL_CSI2_STATUS1(m) (0x354U + (m) * 0x80U) +#define CAL_CSI2_STATUS2(m) (0x358U + (m) * 0x80U) +#define CAL_CSI2_STATUS3(m) (0x35cU + (m) * 0x80U) +#define CAL_CSI2_STATUS4(m) (0x360U + (m) * 0x80U) +#define CAL_CSI2_STATUS5(m) (0x364U + (m) * 0x80U) +#define CAL_CSI2_STATUS6(m) (0x368U + (m) * 0x80U) +#define CAL_CSI2_STATUS7(m) (0x36cU + (m) * 0x80U) /* CAL CSI2 PHY register offsets */ #define CAL_CSI2_PHY_REG0 0x000 @@ -141,12 +139,12 @@ #define CAL_HL_IRQ_EOI_LINE_NUMBER_READ0 0 #define CAL_HL_IRQ_EOI_LINE_NUMBER_EOI0 0 -#define CAL_HL_IRQ_MASK(m) BIT((m) - 1) +#define CAL_HL_IRQ_MASK(m) BIT(m) #define CAL_HL_IRQ_OCPO_ERR_MASK BIT(6) -#define CAL_HL_IRQ_CIO_MASK(i) BIT(16 + ((i) - 1) * 8) -#define CAL_HL_IRQ_VC_MASK(i) BIT(17 + ((i) - 1) * 8) +#define CAL_HL_IRQ_CIO_MASK(i) BIT(16 + (i) * 8) +#define CAL_HL_IRQ_VC_MASK(i) BIT(17 + (i) * 8) #define CAL_PIX_PROC_EN_MASK BIT(0) #define CAL_PIX_PROC_EXTRACT_MASK GENMASK(4, 1) diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index cff2fcd6d812..346f8212791c 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -1576,7 +1576,7 @@ static int vpe_g_fmt(struct file *file, void *priv, struct v4l2_format *f) *f = q_data->format; - if (!V4L2_TYPE_IS_OUTPUT(f->type)) { + if (V4L2_TYPE_IS_CAPTURE(f->type)) { struct vpe_q_data *s_q_data; struct v4l2_pix_format_mplane *spix; diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index d7b43037e500..e07b135613eb 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -431,6 +431,8 @@ vsp1_dl_cmd_pool_create(struct vsp1_device *vsp1, enum vsp1_extcmd_type type, if (!pool) return NULL; + pool->vsp1 = vsp1; + spin_lock_init(&pool->lock); INIT_LIST_HEAD(&pool->free); diff --git a/drivers/media/platform/xilinx/Kconfig b/drivers/media/platform/xilinx/Kconfig index 01c96fb66414..44587dccacf1 100644 --- a/drivers/media/platform/xilinx/Kconfig +++ b/drivers/media/platform/xilinx/Kconfig @@ -12,6 +12,13 @@ config VIDEO_XILINX if VIDEO_XILINX +config VIDEO_XILINX_CSI2RXSS + tristate "Xilinx CSI-2 Rx Subsystem" + help + Driver for Xilinx MIPI CSI-2 Rx Subsystem. This is a V4L sub-device + based driver that takes input from CSI-2 Tx source and converts + it into an AXI4-Stream. + config VIDEO_XILINX_TPG tristate "Xilinx Video Test Pattern Generator" depends on VIDEO_XILINX diff --git a/drivers/media/platform/xilinx/Makefile b/drivers/media/platform/xilinx/Makefile index 4cdc0b1ec7a5..6119a34f3043 100644 --- a/drivers/media/platform/xilinx/Makefile +++ b/drivers/media/platform/xilinx/Makefile @@ -3,5 +3,6 @@ xilinx-video-objs += xilinx-dma.o xilinx-vip.o xilinx-vipp.o obj-$(CONFIG_VIDEO_XILINX) += xilinx-video.o +obj-$(CONFIG_VIDEO_XILINX_CSI2RXSS) += xilinx-csi2rxss.o obj-$(CONFIG_VIDEO_XILINX_TPG) += xilinx-tpg.o obj-$(CONFIG_VIDEO_XILINX_VTC) += xilinx-vtc.o diff --git a/drivers/media/platform/xilinx/xilinx-csi2rxss.c b/drivers/media/platform/xilinx/xilinx-csi2rxss.c new file mode 100644 index 000000000000..fff7ddec6745 --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-csi2rxss.c @@ -0,0 +1,1111 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Xilinx MIPI CSI-2 Rx Subsystem + * + * Copyright (C) 2016 - 2020 Xilinx, Inc. + * + * Contacts: Vishal Sagar <vishal.sagar@xilinx.com> + * + */ +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/platform_device.h> +#include <linux/v4l2-subdev.h> +#include <media/media-entity.h> +#include <media/v4l2-common.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> +#include "xilinx-vip.h" + +/* Register register map */ +#define XCSI_CCR_OFFSET 0x00 +#define XCSI_CCR_SOFTRESET BIT(1) +#define XCSI_CCR_ENABLE BIT(0) + +#define XCSI_PCR_OFFSET 0x04 +#define XCSI_PCR_MAXLANES_MASK GENMASK(4, 3) +#define XCSI_PCR_ACTLANES_MASK GENMASK(1, 0) + +#define XCSI_CSR_OFFSET 0x10 +#define XCSI_CSR_PKTCNT GENMASK(31, 16) +#define XCSI_CSR_SPFIFOFULL BIT(3) +#define XCSI_CSR_SPFIFONE BIT(2) +#define XCSI_CSR_SLBF BIT(1) +#define XCSI_CSR_RIPCD BIT(0) + +#define XCSI_GIER_OFFSET 0x20 +#define XCSI_GIER_GIE BIT(0) + +#define XCSI_ISR_OFFSET 0x24 +#define XCSI_IER_OFFSET 0x28 + +#define XCSI_ISR_FR BIT(31) +#define XCSI_ISR_VCXFE BIT(30) +#define XCSI_ISR_WCC BIT(22) +#define XCSI_ISR_ILC BIT(21) +#define XCSI_ISR_SPFIFOF BIT(20) +#define XCSI_ISR_SPFIFONE BIT(19) +#define XCSI_ISR_SLBF BIT(18) +#define XCSI_ISR_STOP BIT(17) +#define XCSI_ISR_SOTERR BIT(13) +#define XCSI_ISR_SOTSYNCERR BIT(12) +#define XCSI_ISR_ECC2BERR BIT(11) +#define XCSI_ISR_ECC1BERR BIT(10) +#define XCSI_ISR_CRCERR BIT(9) +#define XCSI_ISR_DATAIDERR BIT(8) +#define XCSI_ISR_VC3FSYNCERR BIT(7) +#define XCSI_ISR_VC3FLVLERR BIT(6) +#define XCSI_ISR_VC2FSYNCERR BIT(5) +#define XCSI_ISR_VC2FLVLERR BIT(4) +#define XCSI_ISR_VC1FSYNCERR BIT(3) +#define XCSI_ISR_VC1FLVLERR BIT(2) +#define XCSI_ISR_VC0FSYNCERR BIT(1) +#define XCSI_ISR_VC0FLVLERR BIT(0) + +#define XCSI_ISR_ALLINTR_MASK (0xc07e3fff) + +/* + * Removed VCXFE mask as it doesn't exist in IER + * Removed STOP state irq as this will keep driver in irq handler only + */ +#define XCSI_IER_INTR_MASK (XCSI_ISR_ALLINTR_MASK &\ + ~(XCSI_ISR_STOP | XCSI_ISR_VCXFE)) + +#define XCSI_SPKTR_OFFSET 0x30 +#define XCSI_SPKTR_DATA GENMASK(23, 8) +#define XCSI_SPKTR_VC GENMASK(7, 6) +#define XCSI_SPKTR_DT GENMASK(5, 0) +#define XCSI_SPKT_FIFO_DEPTH 31 + +#define XCSI_VCXR_OFFSET 0x34 +#define XCSI_VCXR_VCERR GENMASK(23, 0) +#define XCSI_VCXR_FSYNCERR BIT(1) +#define XCSI_VCXR_FLVLERR BIT(0) + +#define XCSI_CLKINFR_OFFSET 0x3C +#define XCSI_CLKINFR_STOP BIT(1) + +#define XCSI_DLXINFR_OFFSET 0x40 +#define XCSI_DLXINFR_STOP BIT(5) +#define XCSI_DLXINFR_SOTERR BIT(1) +#define XCSI_DLXINFR_SOTSYNCERR BIT(0) +#define XCSI_MAXDL_COUNT 0x4 + +#define XCSI_VCXINF1R_OFFSET 0x60 +#define XCSI_VCXINF1R_LINECOUNT GENMASK(31, 16) +#define XCSI_VCXINF1R_LINECOUNT_SHIFT 16 +#define XCSI_VCXINF1R_BYTECOUNT GENMASK(15, 0) + +#define XCSI_VCXINF2R_OFFSET 0x64 +#define XCSI_VCXINF2R_DT GENMASK(5, 0) +#define XCSI_MAXVCX_COUNT 16 + +/* + * Sink pad connected to sensor source pad. + * Source pad connected to next module like demosaic. + */ +#define XCSI_MEDIA_PADS 2 +#define XCSI_DEFAULT_WIDTH 1920 +#define XCSI_DEFAULT_HEIGHT 1080 + +/* MIPI CSI-2 Data Types from spec */ +#define XCSI_DT_YUV4228B 0x1e +#define XCSI_DT_YUV42210B 0x1f +#define XCSI_DT_RGB444 0x20 +#define XCSI_DT_RGB555 0x21 +#define XCSI_DT_RGB565 0x22 +#define XCSI_DT_RGB666 0x23 +#define XCSI_DT_RGB888 0x24 +#define XCSI_DT_RAW6 0x28 +#define XCSI_DT_RAW7 0x29 +#define XCSI_DT_RAW8 0x2a +#define XCSI_DT_RAW10 0x2b +#define XCSI_DT_RAW12 0x2c +#define XCSI_DT_RAW14 0x2d +#define XCSI_DT_RAW16 0x2e +#define XCSI_DT_RAW20 0x2f + +#define XCSI_VCX_START 4 +#define XCSI_MAX_VC 4 +#define XCSI_MAX_VCX 16 + +#define XCSI_NEXTREG_OFFSET 4 + +/* There are 2 events frame sync and frame level error per VC */ +#define XCSI_VCX_NUM_EVENTS ((XCSI_MAX_VCX - XCSI_MAX_VC) * 2) + +/** + * struct xcsi2rxss_event - Event log structure + * @mask: Event mask + * @name: Name of the event + */ +struct xcsi2rxss_event { + u32 mask; + const char *name; +}; + +static const struct xcsi2rxss_event xcsi2rxss_events[] = { + { XCSI_ISR_FR, "Frame Received" }, + { XCSI_ISR_VCXFE, "VCX Frame Errors" }, + { XCSI_ISR_WCC, "Word Count Errors" }, + { XCSI_ISR_ILC, "Invalid Lane Count Error" }, + { XCSI_ISR_SPFIFOF, "Short Packet FIFO OverFlow Error" }, + { XCSI_ISR_SPFIFONE, "Short Packet FIFO Not Empty" }, + { XCSI_ISR_SLBF, "Streamline Buffer Full Error" }, + { XCSI_ISR_STOP, "Lane Stop State" }, + { XCSI_ISR_SOTERR, "SOT Error" }, + { XCSI_ISR_SOTSYNCERR, "SOT Sync Error" }, + { XCSI_ISR_ECC2BERR, "2 Bit ECC Unrecoverable Error" }, + { XCSI_ISR_ECC1BERR, "1 Bit ECC Recoverable Error" }, + { XCSI_ISR_CRCERR, "CRC Error" }, + { XCSI_ISR_DATAIDERR, "Data Id Error" }, + { XCSI_ISR_VC3FSYNCERR, "Virtual Channel 3 Frame Sync Error" }, + { XCSI_ISR_VC3FLVLERR, "Virtual Channel 3 Frame Level Error" }, + { XCSI_ISR_VC2FSYNCERR, "Virtual Channel 2 Frame Sync Error" }, + { XCSI_ISR_VC2FLVLERR, "Virtual Channel 2 Frame Level Error" }, + { XCSI_ISR_VC1FSYNCERR, "Virtual Channel 1 Frame Sync Error" }, + { XCSI_ISR_VC1FLVLERR, "Virtual Channel 1 Frame Level Error" }, + { XCSI_ISR_VC0FSYNCERR, "Virtual Channel 0 Frame Sync Error" }, + { XCSI_ISR_VC0FLVLERR, "Virtual Channel 0 Frame Level Error" } +}; + +#define XCSI_NUM_EVENTS ARRAY_SIZE(xcsi2rxss_events) + +/* + * This table provides a mapping between CSI-2 Data type + * and media bus formats + */ +static const u32 xcsi2dt_mbus_lut[][2] = { + { XCSI_DT_YUV4228B, MEDIA_BUS_FMT_UYVY8_1X16 }, + { XCSI_DT_YUV42210B, MEDIA_BUS_FMT_UYVY10_1X20 }, + { XCSI_DT_RGB444, 0 }, + { XCSI_DT_RGB555, 0 }, + { XCSI_DT_RGB565, 0 }, + { XCSI_DT_RGB666, 0 }, + { XCSI_DT_RGB888, MEDIA_BUS_FMT_RBG888_1X24 }, + { XCSI_DT_RAW6, 0 }, + { XCSI_DT_RAW7, 0 }, + { XCSI_DT_RAW8, MEDIA_BUS_FMT_SRGGB8_1X8 }, + { XCSI_DT_RAW8, MEDIA_BUS_FMT_SBGGR8_1X8 }, + { XCSI_DT_RAW8, MEDIA_BUS_FMT_SGBRG8_1X8 }, + { XCSI_DT_RAW8, MEDIA_BUS_FMT_SGRBG8_1X8 }, + { XCSI_DT_RAW10, MEDIA_BUS_FMT_SRGGB10_1X10 }, + { XCSI_DT_RAW10, MEDIA_BUS_FMT_SBGGR10_1X10 }, + { XCSI_DT_RAW10, MEDIA_BUS_FMT_SGBRG10_1X10 }, + { XCSI_DT_RAW10, MEDIA_BUS_FMT_SGRBG10_1X10 }, + { XCSI_DT_RAW12, MEDIA_BUS_FMT_SRGGB12_1X12 }, + { XCSI_DT_RAW12, MEDIA_BUS_FMT_SBGGR12_1X12 }, + { XCSI_DT_RAW12, MEDIA_BUS_FMT_SGBRG12_1X12 }, + { XCSI_DT_RAW12, MEDIA_BUS_FMT_SGRBG12_1X12 }, + { XCSI_DT_RAW16, MEDIA_BUS_FMT_SRGGB16_1X16 }, + { XCSI_DT_RAW16, MEDIA_BUS_FMT_SBGGR16_1X16 }, + { XCSI_DT_RAW16, MEDIA_BUS_FMT_SGBRG16_1X16 }, + { XCSI_DT_RAW16, MEDIA_BUS_FMT_SGRBG16_1X16 }, + { XCSI_DT_RAW20, 0 }, +}; + +/** + * struct xcsi2rxss_state - CSI-2 Rx Subsystem device structure + * @subdev: The v4l2 subdev structure + * @format: Active V4L2 formats on each pad + * @default_format: Default V4L2 format + * @events: counter for events + * @vcx_events: counter for vcx_events + * @dev: Platform structure + * @rsubdev: Remote subdev connected to sink pad + * @rst_gpio: reset to video_aresetn + * @clks: array of clocks + * @iomem: Base address of subsystem + * @max_num_lanes: Maximum number of lanes present + * @datatype: Data type filter + * @lock: mutex for accessing this structure + * @pads: media pads + * @streaming: Flag for storing streaming state + * @enable_active_lanes: If number of active lanes can be modified + * @en_vcx: If more than 4 VC are enabled + * + * This structure contains the device driver related parameters + */ +struct xcsi2rxss_state { + struct v4l2_subdev subdev; + struct v4l2_mbus_framefmt format; + struct v4l2_mbus_framefmt default_format; + u32 events[XCSI_NUM_EVENTS]; + u32 vcx_events[XCSI_VCX_NUM_EVENTS]; + struct device *dev; + struct v4l2_subdev *rsubdev; + struct gpio_desc *rst_gpio; + struct clk_bulk_data *clks; + void __iomem *iomem; + u32 max_num_lanes; + u32 datatype; + /* used to protect access to this struct */ + struct mutex lock; + struct media_pad pads[XCSI_MEDIA_PADS]; + bool streaming; + bool enable_active_lanes; + bool en_vcx; +}; + +static const struct clk_bulk_data xcsi2rxss_clks[] = { + { .id = "lite_aclk" }, + { .id = "video_aclk" }, +}; + +static inline struct xcsi2rxss_state * +to_xcsi2rxssstate(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct xcsi2rxss_state, subdev); +} + +/* + * Register related operations + */ +static inline u32 xcsi2rxss_read(struct xcsi2rxss_state *xcsi2rxss, u32 addr) +{ + return ioread32(xcsi2rxss->iomem + addr); +} + +static inline void xcsi2rxss_write(struct xcsi2rxss_state *xcsi2rxss, u32 addr, + u32 value) +{ + iowrite32(value, xcsi2rxss->iomem + addr); +} + +static inline void xcsi2rxss_clr(struct xcsi2rxss_state *xcsi2rxss, u32 addr, + u32 clr) +{ + xcsi2rxss_write(xcsi2rxss, addr, + xcsi2rxss_read(xcsi2rxss, addr) & ~clr); +} + +static inline void xcsi2rxss_set(struct xcsi2rxss_state *xcsi2rxss, u32 addr, + u32 set) +{ + xcsi2rxss_write(xcsi2rxss, addr, xcsi2rxss_read(xcsi2rxss, addr) | set); +} + +/* + * This function returns the nth mbus for a data type. + * In case of error, mbus code returned is 0. + */ +static u32 xcsi2rxss_get_nth_mbus(u32 dt, u32 n) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(xcsi2dt_mbus_lut); i++) { + if (xcsi2dt_mbus_lut[i][0] == dt) { + if (n-- == 0) + return xcsi2dt_mbus_lut[i][1]; + } + } + + return 0; +} + +/* This returns the data type for a media bus format else 0 */ +static u32 xcsi2rxss_get_dt(u32 mbus) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(xcsi2dt_mbus_lut); i++) { + if (xcsi2dt_mbus_lut[i][1] == mbus) + return xcsi2dt_mbus_lut[i][0]; + } + + return 0; +} + +/** + * xcsi2rxss_soft_reset - Does a soft reset of the MIPI CSI-2 Rx Subsystem + * @state: Xilinx CSI-2 Rx Subsystem structure pointer + * + * Core takes less than 100 video clock cycles to reset. + * So a larger timeout value is chosen for margin. + * + * Return: 0 - on success OR -ETIME if reset times out + */ +static int xcsi2rxss_soft_reset(struct xcsi2rxss_state *state) +{ + u32 timeout = 1000; /* us */ + + xcsi2rxss_set(state, XCSI_CCR_OFFSET, XCSI_CCR_SOFTRESET); + + while (xcsi2rxss_read(state, XCSI_CSR_OFFSET) & XCSI_CSR_RIPCD) { + if (timeout == 0) { + dev_err(state->dev, "soft reset timed out!\n"); + return -ETIME; + } + + timeout--; + udelay(1); + } + + xcsi2rxss_clr(state, XCSI_CCR_OFFSET, XCSI_CCR_SOFTRESET); + return 0; +} + +static void xcsi2rxss_hard_reset(struct xcsi2rxss_state *state) +{ + if (!state->rst_gpio) + return; + + /* minimum of 40 dphy_clk_200M cycles */ + gpiod_set_value_cansleep(state->rst_gpio, 1); + usleep_range(1, 2); + gpiod_set_value_cansleep(state->rst_gpio, 0); +} + +static void xcsi2rxss_reset_event_counters(struct xcsi2rxss_state *state) +{ + unsigned int i; + + for (i = 0; i < XCSI_NUM_EVENTS; i++) + state->events[i] = 0; + + for (i = 0; i < XCSI_VCX_NUM_EVENTS; i++) + state->vcx_events[i] = 0; +} + +/* Print event counters */ +static void xcsi2rxss_log_counters(struct xcsi2rxss_state *state) +{ + struct device *dev = state->dev; + unsigned int i; + + for (i = 0; i < XCSI_NUM_EVENTS; i++) { + if (state->events[i] > 0) { + dev_info(dev, "%s events: %d\n", + xcsi2rxss_events[i].name, + state->events[i]); + } + } + + if (state->en_vcx) { + for (i = 0; i < XCSI_VCX_NUM_EVENTS; i++) { + if (state->vcx_events[i] > 0) { + dev_info(dev, + "VC %d Frame %s err vcx events: %d\n", + (i / 2) + XCSI_VCX_START, + i & 1 ? "Sync" : "Level", + state->vcx_events[i]); + } + } + } +} + +/** + * xcsi2rxss_log_status - Logs the status of the CSI-2 Receiver + * @sd: Pointer to V4L2 subdevice structure + * + * This function prints the current status of Xilinx MIPI CSI-2 + * + * Return: 0 on success + */ +static int xcsi2rxss_log_status(struct v4l2_subdev *sd) +{ + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); + struct device *dev = xcsi2rxss->dev; + u32 reg, data; + unsigned int i, max_vc; + + mutex_lock(&xcsi2rxss->lock); + + xcsi2rxss_log_counters(xcsi2rxss); + + dev_info(dev, "***** Core Status *****\n"); + data = xcsi2rxss_read(xcsi2rxss, XCSI_CSR_OFFSET); + dev_info(dev, "Short Packet FIFO Full = %s\n", + data & XCSI_CSR_SPFIFOFULL ? "true" : "false"); + dev_info(dev, "Short Packet FIFO Not Empty = %s\n", + data & XCSI_CSR_SPFIFONE ? "true" : "false"); + dev_info(dev, "Stream line buffer full = %s\n", + data & XCSI_CSR_SLBF ? "true" : "false"); + dev_info(dev, "Soft reset/Core disable in progress = %s\n", + data & XCSI_CSR_RIPCD ? "true" : "false"); + + /* Clk & Lane Info */ + dev_info(dev, "******** Clock Lane Info *********\n"); + data = xcsi2rxss_read(xcsi2rxss, XCSI_CLKINFR_OFFSET); + dev_info(dev, "Clock Lane in Stop State = %s\n", + data & XCSI_CLKINFR_STOP ? "true" : "false"); + + dev_info(dev, "******** Data Lane Info *********\n"); + dev_info(dev, "Lane\tSoT Error\tSoT Sync Error\tStop State\n"); + reg = XCSI_DLXINFR_OFFSET; + for (i = 0; i < XCSI_MAXDL_COUNT; i++) { + data = xcsi2rxss_read(xcsi2rxss, reg); + + dev_info(dev, "%d\t%s\t\t%s\t\t%s\n", i, + data & XCSI_DLXINFR_SOTERR ? "true" : "false", + data & XCSI_DLXINFR_SOTSYNCERR ? "true" : "false", + data & XCSI_DLXINFR_STOP ? "true" : "false"); + + reg += XCSI_NEXTREG_OFFSET; + } + + /* Virtual Channel Image Information */ + dev_info(dev, "********** Virtual Channel Info ************\n"); + dev_info(dev, "VC\tLine Count\tByte Count\tData Type\n"); + if (xcsi2rxss->en_vcx) + max_vc = XCSI_MAX_VCX; + else + max_vc = XCSI_MAX_VC; + + reg = XCSI_VCXINF1R_OFFSET; + for (i = 0; i < max_vc; i++) { + u32 line_count, byte_count, data_type; + + /* Get line and byte count from VCXINFR1 Register */ + data = xcsi2rxss_read(xcsi2rxss, reg); + byte_count = data & XCSI_VCXINF1R_BYTECOUNT; + line_count = data & XCSI_VCXINF1R_LINECOUNT; + line_count >>= XCSI_VCXINF1R_LINECOUNT_SHIFT; + + /* Get data type from VCXINFR2 Register */ + reg += XCSI_NEXTREG_OFFSET; + data = xcsi2rxss_read(xcsi2rxss, reg); + data_type = data & XCSI_VCXINF2R_DT; + + dev_info(dev, "%d\t%d\t\t%d\t\t0x%x\n", i, line_count, + byte_count, data_type); + + /* Move to next pair of VC Info registers */ + reg += XCSI_NEXTREG_OFFSET; + } + + mutex_unlock(&xcsi2rxss->lock); + + return 0; +} + +static struct v4l2_subdev *xcsi2rxss_get_remote_subdev(struct media_pad *local) +{ + struct media_pad *remote; + + remote = media_entity_remote_pad(local); + if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) + return NULL; + + return media_entity_to_v4l2_subdev(remote->entity); +} + +static int xcsi2rxss_start_stream(struct xcsi2rxss_state *state) +{ + int ret = 0; + + /* enable core */ + xcsi2rxss_set(state, XCSI_CCR_OFFSET, XCSI_CCR_ENABLE); + + ret = xcsi2rxss_soft_reset(state); + if (ret) { + state->streaming = false; + return ret; + } + + /* enable interrupts */ + xcsi2rxss_clr(state, XCSI_GIER_OFFSET, XCSI_GIER_GIE); + xcsi2rxss_write(state, XCSI_IER_OFFSET, XCSI_IER_INTR_MASK); + xcsi2rxss_set(state, XCSI_GIER_OFFSET, XCSI_GIER_GIE); + + state->streaming = true; + + state->rsubdev = + xcsi2rxss_get_remote_subdev(&state->pads[XVIP_PAD_SINK]); + + ret = v4l2_subdev_call(state->rsubdev, video, s_stream, 1); + if (ret) { + /* disable interrupts */ + xcsi2rxss_clr(state, XCSI_IER_OFFSET, XCSI_IER_INTR_MASK); + xcsi2rxss_clr(state, XCSI_GIER_OFFSET, XCSI_GIER_GIE); + + /* disable core */ + xcsi2rxss_clr(state, XCSI_CCR_OFFSET, XCSI_CCR_ENABLE); + state->streaming = false; + } + + return ret; +} + +static void xcsi2rxss_stop_stream(struct xcsi2rxss_state *state) +{ + v4l2_subdev_call(state->rsubdev, video, s_stream, 0); + + /* disable interrupts */ + xcsi2rxss_clr(state, XCSI_IER_OFFSET, XCSI_IER_INTR_MASK); + xcsi2rxss_clr(state, XCSI_GIER_OFFSET, XCSI_GIER_GIE); + + /* disable core */ + xcsi2rxss_clr(state, XCSI_CCR_OFFSET, XCSI_CCR_ENABLE); + state->streaming = false; +} + +/** + * xcsi2rxss_irq_handler - Interrupt handler for CSI-2 + * @irq: IRQ number + * @data: Pointer to device state + * + * In the interrupt handler, a list of event counters are updated for + * corresponding interrupts. This is useful to get status / debug. + * + * Return: IRQ_HANDLED after handling interrupts + */ +static irqreturn_t xcsi2rxss_irq_handler(int irq, void *data) +{ + struct xcsi2rxss_state *state = (struct xcsi2rxss_state *)data; + struct device *dev = state->dev; + u32 status; + + status = xcsi2rxss_read(state, XCSI_ISR_OFFSET) & XCSI_ISR_ALLINTR_MASK; + xcsi2rxss_write(state, XCSI_ISR_OFFSET, status); + + /* Received a short packet */ + if (status & XCSI_ISR_SPFIFONE) { + u32 count = 0; + + /* + * Drain generic short packet FIFO by reading max 31 + * (fifo depth) short packets from fifo or till fifo is empty. + */ + for (count = 0; count < XCSI_SPKT_FIFO_DEPTH; ++count) { + u32 spfifostat, spkt; + + spkt = xcsi2rxss_read(state, XCSI_SPKTR_OFFSET); + dev_dbg(dev, "Short packet = 0x%08x\n", spkt); + spfifostat = xcsi2rxss_read(state, XCSI_ISR_OFFSET); + spfifostat &= XCSI_ISR_SPFIFONE; + if (!spfifostat) + break; + xcsi2rxss_write(state, XCSI_ISR_OFFSET, spfifostat); + } + } + + /* Short packet FIFO overflow */ + if (status & XCSI_ISR_SPFIFOF) + dev_dbg_ratelimited(dev, "Short packet FIFO overflowed\n"); + + /* + * Stream line buffer full + * This means there is a backpressure from downstream IP + */ + if (status & XCSI_ISR_SLBF) { + dev_alert_ratelimited(dev, "Stream Line Buffer Full!\n"); + + /* disable interrupts */ + xcsi2rxss_clr(state, XCSI_IER_OFFSET, XCSI_IER_INTR_MASK); + xcsi2rxss_clr(state, XCSI_GIER_OFFSET, XCSI_GIER_GIE); + + /* disable core */ + xcsi2rxss_clr(state, XCSI_CCR_OFFSET, XCSI_CCR_ENABLE); + + /* + * The IP needs to be hard reset before it can be used now. + * This will be done in streamoff. + */ + + /* + * TODO: Notify the whole pipeline with v4l2_subdev_notify() to + * inform userspace. + */ + } + + /* Increment event counters */ + if (status & XCSI_ISR_ALLINTR_MASK) { + unsigned int i; + + for (i = 0; i < XCSI_NUM_EVENTS; i++) { + if (!(status & xcsi2rxss_events[i].mask)) + continue; + state->events[i]++; + dev_dbg_ratelimited(dev, "%s: %u\n", + xcsi2rxss_events[i].name, + state->events[i]); + } + + if (status & XCSI_ISR_VCXFE && state->en_vcx) { + u32 vcxstatus; + + vcxstatus = xcsi2rxss_read(state, XCSI_VCXR_OFFSET); + vcxstatus &= XCSI_VCXR_VCERR; + for (i = 0; i < XCSI_VCX_NUM_EVENTS; i++) { + if (!(vcxstatus & BIT(i))) + continue; + state->vcx_events[i]++; + } + xcsi2rxss_write(state, XCSI_VCXR_OFFSET, vcxstatus); + } + } + + return IRQ_HANDLED; +} + +/** + * xcsi2rxss_s_stream - It is used to start/stop the streaming. + * @sd: V4L2 Sub device + * @enable: Flag (True / False) + * + * This function controls the start or stop of streaming for the + * Xilinx MIPI CSI-2 Rx Subsystem. + * + * Return: 0 on success, errors otherwise + */ +static int xcsi2rxss_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); + int ret = 0; + + mutex_lock(&xcsi2rxss->lock); + + if (enable == xcsi2rxss->streaming) + goto stream_done; + + if (enable) { + xcsi2rxss_reset_event_counters(xcsi2rxss); + ret = xcsi2rxss_start_stream(xcsi2rxss); + } else { + xcsi2rxss_stop_stream(xcsi2rxss); + xcsi2rxss_hard_reset(xcsi2rxss); + } + +stream_done: + mutex_unlock(&xcsi2rxss->lock); + return ret; +} + +static struct v4l2_mbus_framefmt * +__xcsi2rxss_get_pad_format(struct xcsi2rxss_state *xcsi2rxss, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(&xcsi2rxss->subdev, cfg, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &xcsi2rxss->format; + default: + return NULL; + } +} + +/** + * xcsi2rxss_init_cfg - Initialise the pad format config to default + * @sd: Pointer to V4L2 Sub device structure + * @cfg: Pointer to sub device pad information structure + * + * This function is used to initialize the pad format with the default + * values. + * + * Return: 0 on success + */ +static int xcsi2rxss_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg) +{ + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); + struct v4l2_mbus_framefmt *format; + unsigned int i; + + mutex_lock(&xcsi2rxss->lock); + for (i = 0; i < XCSI_MEDIA_PADS; i++) { + format = v4l2_subdev_get_try_format(sd, cfg, i); + *format = xcsi2rxss->default_format; + } + mutex_unlock(&xcsi2rxss->lock); + + return 0; +} + +/** + * xcsi2rxss_get_format - Get the pad format + * @sd: Pointer to V4L2 Sub device structure + * @cfg: Pointer to sub device pad information 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 xcsi2rxss_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); + + mutex_lock(&xcsi2rxss->lock); + fmt->format = *__xcsi2rxss_get_pad_format(xcsi2rxss, cfg, fmt->pad, + fmt->which); + mutex_unlock(&xcsi2rxss->lock); + + return 0; +} + +/** + * xcsi2rxss_set_format - This is used to set the pad format + * @sd: Pointer to V4L2 Sub device structure + * @cfg: Pointer to sub device pad information structure + * @fmt: Pointer to pad level media bus format + * + * This function is used to set the pad format. Since the pad format is fixed + * in hardware, it can't be modified on run time. So when a format set is + * requested by application, all parameters except the format type is saved + * for the pad and the original pad format is sent back to the application. + * + * Return: 0 on success + */ +static int xcsi2rxss_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); + struct v4l2_mbus_framefmt *__format; + u32 dt; + + mutex_lock(&xcsi2rxss->lock); + + /* + * Only the format->code parameter matters for CSI as the + * CSI format cannot be changed at runtime. + * Ensure that format to set is copied to over to CSI pad format + */ + __format = __xcsi2rxss_get_pad_format(xcsi2rxss, cfg, + fmt->pad, fmt->which); + + /* only sink pad format can be updated */ + if (fmt->pad == XVIP_PAD_SOURCE) { + fmt->format = *__format; + mutex_unlock(&xcsi2rxss->lock); + return 0; + } + + /* + * RAW8 is supported in all datatypes. So if requested media bus format + * is of RAW8 type, then allow to be set. In case core is configured to + * other RAW, YUV422 8/10 or RGB888, set appropriate media bus format. + */ + dt = xcsi2rxss_get_dt(fmt->format.code); + if (dt != xcsi2rxss->datatype && dt != XCSI_DT_RAW8) { + dev_dbg(xcsi2rxss->dev, "Unsupported media bus format"); + /* set the default format for the data type */ + fmt->format.code = xcsi2rxss_get_nth_mbus(xcsi2rxss->datatype, + 0); + } + + *__format = fmt->format; + mutex_unlock(&xcsi2rxss->lock); + + return 0; +} + +/* + * xcsi2rxss_enum_mbus_code - Handle pixel format enumeration + * @sd: pointer to v4l2 subdev structure + * @cfg: V4L2 subdev pad configuration + * @code: pointer to v4l2_subdev_mbus_code_enum structure + * + * Return: -EINVAL or zero on success + */ +static int xcsi2rxss_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct xcsi2rxss_state *state = to_xcsi2rxssstate(sd); + u32 dt, n; + int ret = 0; + + /* RAW8 dt packets are available in all DT configurations */ + if (code->index < 4) { + n = code->index; + dt = XCSI_DT_RAW8; + } else if (state->datatype != XCSI_DT_RAW8) { + n = code->index - 4; + dt = state->datatype; + } else { + return -EINVAL; + } + + code->code = xcsi2rxss_get_nth_mbus(dt, n); + if (!code->code) + ret = -EINVAL; + + return ret; +} + +/* ----------------------------------------------------------------------------- + * Media Operations + */ + +static const struct media_entity_operations xcsi2rxss_media_ops = { + .link_validate = v4l2_subdev_link_validate +}; + +static const struct v4l2_subdev_core_ops xcsi2rxss_core_ops = { + .log_status = xcsi2rxss_log_status, +}; + +static const struct v4l2_subdev_video_ops xcsi2rxss_video_ops = { + .s_stream = xcsi2rxss_s_stream +}; + +static const struct v4l2_subdev_pad_ops xcsi2rxss_pad_ops = { + .init_cfg = xcsi2rxss_init_cfg, + .get_fmt = xcsi2rxss_get_format, + .set_fmt = xcsi2rxss_set_format, + .enum_mbus_code = xcsi2rxss_enum_mbus_code, + .link_validate = v4l2_subdev_link_validate_default, +}; + +static const struct v4l2_subdev_ops xcsi2rxss_ops = { + .core = &xcsi2rxss_core_ops, + .video = &xcsi2rxss_video_ops, + .pad = &xcsi2rxss_pad_ops +}; + +static int xcsi2rxss_parse_of(struct xcsi2rxss_state *xcsi2rxss) +{ + struct device *dev = xcsi2rxss->dev; + struct device_node *node = dev->of_node; + + struct fwnode_handle *ep; + struct v4l2_fwnode_endpoint vep = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + bool en_csi_v20, vfb; + int ret; + + en_csi_v20 = of_property_read_bool(node, "xlnx,en-csi-v2-0"); + if (en_csi_v20) + xcsi2rxss->en_vcx = of_property_read_bool(node, "xlnx,en-vcx"); + + xcsi2rxss->enable_active_lanes = + of_property_read_bool(node, "xlnx,en-active-lanes"); + + ret = of_property_read_u32(node, "xlnx,csi-pxl-format", + &xcsi2rxss->datatype); + if (ret < 0) { + dev_err(dev, "missing xlnx,csi-pxl-format property\n"); + return ret; + } + + switch (xcsi2rxss->datatype) { + case XCSI_DT_YUV4228B: + case XCSI_DT_RGB444: + case XCSI_DT_RGB555: + case XCSI_DT_RGB565: + case XCSI_DT_RGB666: + case XCSI_DT_RGB888: + case XCSI_DT_RAW6: + case XCSI_DT_RAW7: + case XCSI_DT_RAW8: + case XCSI_DT_RAW10: + case XCSI_DT_RAW12: + case XCSI_DT_RAW14: + break; + case XCSI_DT_YUV42210B: + case XCSI_DT_RAW16: + case XCSI_DT_RAW20: + if (!en_csi_v20) { + ret = -EINVAL; + dev_dbg(dev, "enable csi v2 for this pixel format"); + } + break; + default: + ret = -EINVAL; + } + if (ret < 0) { + dev_err(dev, "invalid csi-pxl-format property!\n"); + return ret; + } + + vfb = of_property_read_bool(node, "xlnx,vfb"); + if (!vfb) { + dev_err(dev, "operation without VFB is not supported\n"); + return -EINVAL; + } + + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), + XVIP_PAD_SINK, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!ep) { + dev_err(dev, "no sink port found"); + return -EINVAL; + } + + ret = v4l2_fwnode_endpoint_parse(ep, &vep); + fwnode_handle_put(ep); + if (ret) { + dev_err(dev, "error parsing sink port"); + return ret; + } + + dev_dbg(dev, "mipi number lanes = %d\n", + vep.bus.mipi_csi2.num_data_lanes); + + xcsi2rxss->max_num_lanes = vep.bus.mipi_csi2.num_data_lanes; + + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), + XVIP_PAD_SOURCE, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!ep) { + dev_err(dev, "no source port found"); + return -EINVAL; + } + + fwnode_handle_put(ep); + + dev_dbg(dev, "vcx %s, %u data lanes (%s), data type 0x%02x\n", + xcsi2rxss->en_vcx ? "enabled" : "disabled", + xcsi2rxss->max_num_lanes, + xcsi2rxss->enable_active_lanes ? "dynamic" : "static", + xcsi2rxss->datatype); + + return 0; +} + +static int xcsi2rxss_probe(struct platform_device *pdev) +{ + struct v4l2_subdev *subdev; + struct xcsi2rxss_state *xcsi2rxss; + int num_clks = ARRAY_SIZE(xcsi2rxss_clks); + struct device *dev = &pdev->dev; + int irq, ret; + + xcsi2rxss = devm_kzalloc(dev, sizeof(*xcsi2rxss), GFP_KERNEL); + if (!xcsi2rxss) + return -ENOMEM; + + xcsi2rxss->dev = dev; + + xcsi2rxss->clks = devm_kmemdup(dev, xcsi2rxss_clks, + sizeof(xcsi2rxss_clks), GFP_KERNEL); + if (!xcsi2rxss->clks) + return -ENOMEM; + + /* Reset GPIO */ + xcsi2rxss->rst_gpio = devm_gpiod_get_optional(dev, "video-reset", + GPIOD_OUT_HIGH); + if (IS_ERR(xcsi2rxss->rst_gpio)) { + if (PTR_ERR(xcsi2rxss->rst_gpio) != -EPROBE_DEFER) + dev_err(dev, "Video Reset GPIO not setup in DT"); + return PTR_ERR(xcsi2rxss->rst_gpio); + } + + ret = xcsi2rxss_parse_of(xcsi2rxss); + if (ret < 0) + return ret; + + xcsi2rxss->iomem = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(xcsi2rxss->iomem)) + return PTR_ERR(xcsi2rxss->iomem); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(dev, irq, NULL, + xcsi2rxss_irq_handler, IRQF_ONESHOT, + dev_name(dev), xcsi2rxss); + if (ret) { + dev_err(dev, "Err = %d Interrupt handler reg failed!\n", ret); + return ret; + } + + ret = clk_bulk_get(dev, num_clks, xcsi2rxss->clks); + if (ret) + return ret; + + /* TODO: Enable/disable clocks at stream on/off time. */ + ret = clk_bulk_prepare_enable(num_clks, xcsi2rxss->clks); + if (ret) + goto err_clk_put; + + mutex_init(&xcsi2rxss->lock); + + xcsi2rxss_hard_reset(xcsi2rxss); + xcsi2rxss_soft_reset(xcsi2rxss); + + /* Initialize V4L2 subdevice and media entity */ + xcsi2rxss->pads[XVIP_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + xcsi2rxss->pads[XVIP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + /* Initialize the default format */ + xcsi2rxss->default_format.code = + xcsi2rxss_get_nth_mbus(xcsi2rxss->datatype, 0); + xcsi2rxss->default_format.field = V4L2_FIELD_NONE; + xcsi2rxss->default_format.colorspace = V4L2_COLORSPACE_SRGB; + xcsi2rxss->default_format.width = XCSI_DEFAULT_WIDTH; + xcsi2rxss->default_format.height = XCSI_DEFAULT_HEIGHT; + xcsi2rxss->format = xcsi2rxss->default_format; + + /* Initialize V4L2 subdevice and media entity */ + subdev = &xcsi2rxss->subdev; + v4l2_subdev_init(subdev, &xcsi2rxss_ops); + subdev->dev = dev; + strscpy(subdev->name, dev_name(dev), sizeof(subdev->name)); + subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; + subdev->entity.ops = &xcsi2rxss_media_ops; + v4l2_set_subdevdata(subdev, xcsi2rxss); + + ret = media_entity_pads_init(&subdev->entity, XCSI_MEDIA_PADS, + xcsi2rxss->pads); + if (ret < 0) + goto error; + + platform_set_drvdata(pdev, xcsi2rxss); + + ret = v4l2_async_register_subdev(subdev); + if (ret < 0) { + dev_err(dev, "failed to register subdev\n"); + goto error; + } + + return 0; +error: + media_entity_cleanup(&subdev->entity); + mutex_destroy(&xcsi2rxss->lock); + clk_bulk_disable_unprepare(num_clks, xcsi2rxss->clks); +err_clk_put: + clk_bulk_put(num_clks, xcsi2rxss->clks); + return ret; +} + +static int xcsi2rxss_remove(struct platform_device *pdev) +{ + struct xcsi2rxss_state *xcsi2rxss = platform_get_drvdata(pdev); + struct v4l2_subdev *subdev = &xcsi2rxss->subdev; + int num_clks = ARRAY_SIZE(xcsi2rxss_clks); + + v4l2_async_unregister_subdev(subdev); + media_entity_cleanup(&subdev->entity); + mutex_destroy(&xcsi2rxss->lock); + clk_bulk_disable_unprepare(num_clks, xcsi2rxss->clks); + clk_bulk_put(num_clks, xcsi2rxss->clks); + + return 0; +} + +static const struct of_device_id xcsi2rxss_of_id_table[] = { + { .compatible = "xlnx,mipi-csi2-rx-subsystem-5.0", }, + { } +}; +MODULE_DEVICE_TABLE(of, xcsi2rxss_of_id_table); + +static struct platform_driver xcsi2rxss_driver = { + .driver = { + .name = "xilinx-csi2rxss", + .of_match_table = xcsi2rxss_of_id_table, + }, + .probe = xcsi2rxss_probe, + .remove = xcsi2rxss_remove, +}; + +module_platform_driver(xcsi2rxss_driver); + +MODULE_AUTHOR("Vishal Sagar <vsagar@xilinx.com>"); +MODULE_DESCRIPTION("Xilinx MIPI CSI-2 Rx Subsystem Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/radio/si4713/radio-usb-si4713.c b/drivers/media/radio/si4713/radio-usb-si4713.c index 33274189c83c..2cf36c8abdde 100644 --- a/drivers/media/radio/si4713/radio-usb-si4713.c +++ b/drivers/media/radio/si4713/radio-usb-si4713.c @@ -414,7 +414,7 @@ static int usb_si4713_probe(struct usb_interface *intf, struct si4713_usb_device *radio; struct i2c_adapter *adapter; struct v4l2_subdev *sd; - int retval = -ENOMEM; + int retval; dev_info(&intf->dev, "Si4713 development board discovered: (%04X:%04X)\n", id->idVendor, id->idProduct); diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index c18dee648253..2c0ee2e5b446 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -530,6 +530,17 @@ config IR_ZX To compile this driver as a module, choose M here: the module will be called zx-irdec. +config IR_TOY + tristate "Infrared Toy and IR Droid" + depends on RC_CORE + depends on USB_ARCH_HAS_HCD + help + Say Y here if you want to use the Infrared Toy or IR Droid, USB + versions. + + To compile this driver as a module, choose M here: the module will be + called ir_toy. + endif #RC_DEVICES endif #RC_CORE diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile index 48d23433b3c0..5bb2932ab119 100644 --- a/drivers/media/rc/Makefile +++ b/drivers/media/rc/Makefile @@ -50,3 +50,4 @@ obj-$(CONFIG_IR_MTK) += mtk-cir.o obj-$(CONFIG_IR_ZX) += zx-irdec.o obj-$(CONFIG_IR_TANGO) += tango-ir.o obj-$(CONFIG_RC_XBOX_DVD) += xbox_remote.o +obj-$(CONFIG_IR_TOY) += ir_toy.o diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c index b74bb13161fd..8e3177c5b586 100644 --- a/drivers/media/rc/fintek-cir.c +++ b/drivers/media/rc/fintek-cir.c @@ -51,13 +51,6 @@ static inline void fintek_set_reg_bit(struct fintek_dev *fintek, u8 val, u8 reg) fintek_cr_write(fintek, tmp, reg); } -/* clear config register bit without changing other bits */ -static inline void fintek_clear_reg_bit(struct fintek_dev *fintek, u8 val, u8 reg) -{ - u8 tmp = fintek_cr_read(fintek, reg) & ~val; - fintek_cr_write(fintek, tmp, reg); -} - /* enter config mode */ static inline void fintek_config_mode_enable(struct fintek_dev *fintek) { diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index ed95244da894..a7962ca2ac8e 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -795,7 +795,7 @@ static ssize_t show_associate_remote(struct device *d, else strscpy(buf, "closed\n", PAGE_SIZE); - dev_info(d, "Visit http://www.lirc.org/html/imon-24g.html for instructions on how to associate your iMON 2.4G DT/LT remote\n"); + dev_info(d, "Visit https://www.lirc.org/html/imon-24g.html for instructions on how to associate your iMON 2.4G DT/LT remote\n"); mutex_unlock(&ictx->lock); return strlen(buf); } diff --git a/drivers/media/rc/ir_toy.c b/drivers/media/rc/ir_toy.c new file mode 100644 index 000000000000..5c7a7500a925 --- /dev/null +++ b/drivers/media/rc/ir_toy.c @@ -0,0 +1,509 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/* + * Infrared Toy and IR Droid RC core driver + * + * Copyright (C) 2020 Sean Young <sean@mess.org> + + * This driver is based on the lirc driver which can be found here: + * https://sourceforge.net/p/lirc/git/ci/master/tree/plugins/irtoy.c + * Copyright (C) 2011 Peter Kooiman <pkooiman@gmail.com> + */ + +#include <asm/unaligned.h> +#include <linux/completion.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/slab.h> +#include <linux/usb/input.h> + +#include <media/rc-core.h> + +static const u8 COMMAND_VERSION[] = { 'v' }; +// End transmit and repeat reset command so we exit sump mode +static const u8 COMMAND_RESET[] = { 0xff, 0xff, 0, 0, 0, 0, 0 }; +static const u8 COMMAND_SMODE_ENTER[] = { 's' }; +static const u8 COMMAND_TXSTART[] = { 0x26, 0x24, 0x25, 0x03 }; + +#define REPLY_XMITCOUNT 't' +#define REPLY_XMITSUCCESS 'C' +#define REPLY_VERSION 'V' +#define REPLY_SAMPLEMODEPROTO 'S' + +#define TIMEOUT 500 + +#define LEN_XMITRES 3 +#define LEN_VERSION 4 +#define LEN_SAMPLEMODEPROTO 3 + +#define MIN_FW_VERSION 20 +#define UNIT_NS 21333 +#define MAX_TIMEOUT_NS (UNIT_NS * U16_MAX) + +#define MAX_PACKET 64 + +enum state { + STATE_IRDATA, + STATE_RESET, + STATE_COMMAND, + STATE_TX, +}; + +struct irtoy { + struct device *dev; + struct usb_device *usbdev; + + struct rc_dev *rc; + struct urb *urb_in, *urb_out; + + u8 *in; + u8 *out; + struct completion command_done; + + bool pulse; + enum state state; + + void *tx_buf; + uint tx_len; + + uint emitted; + uint hw_version; + uint sw_version; + uint proto_version; + + char phys[64]; +}; + +static void irtoy_response(struct irtoy *irtoy, u32 len) +{ + switch (irtoy->state) { + case STATE_COMMAND: + if (len == LEN_VERSION && irtoy->in[0] == REPLY_VERSION) { + uint version; + + irtoy->in[LEN_VERSION] = 0; + + if (kstrtouint(irtoy->in + 1, 10, &version)) { + dev_err(irtoy->dev, "invalid version %*phN. Please make sure you are using firmware v20 or higher", + LEN_VERSION, irtoy->in); + break; + } + + dev_dbg(irtoy->dev, "version %s\n", irtoy->in); + + irtoy->hw_version = version / 100; + irtoy->sw_version = version % 100; + + irtoy->state = STATE_IRDATA; + complete(&irtoy->command_done); + } else if (len == LEN_SAMPLEMODEPROTO && + irtoy->in[0] == REPLY_SAMPLEMODEPROTO) { + uint version; + + irtoy->in[LEN_SAMPLEMODEPROTO] = 0; + + if (kstrtouint(irtoy->in + 1, 10, &version)) { + dev_err(irtoy->dev, "invalid sample mode response %*phN", + LEN_SAMPLEMODEPROTO, irtoy->in); + return; + } + + dev_dbg(irtoy->dev, "protocol %s\n", irtoy->in); + + irtoy->proto_version = version; + + irtoy->state = STATE_IRDATA; + complete(&irtoy->command_done); + } else { + dev_err(irtoy->dev, "unexpected response to command: %*phN\n", + len, irtoy->in); + } + break; + case STATE_IRDATA: { + struct ir_raw_event rawir = { .pulse = irtoy->pulse }; + __be16 *in = (__be16 *)irtoy->in; + int i; + + for (i = 0; i < len / sizeof(__be16); i++) { + u16 v = be16_to_cpu(in[i]); + + if (v == 0xffff) { + rawir.pulse = false; + } else { + rawir.duration = v * UNIT_NS; + ir_raw_event_store_with_timeout(irtoy->rc, + &rawir); + } + + rawir.pulse = !rawir.pulse; + } + + irtoy->pulse = rawir.pulse; + + ir_raw_event_handle(irtoy->rc); + break; + } + case STATE_TX: + if (irtoy->tx_len == 0) { + if (len == LEN_XMITRES && + irtoy->in[0] == REPLY_XMITCOUNT) { + u16 emitted = get_unaligned_be16(irtoy->in + 1); + + dev_dbg(irtoy->dev, "emitted:%u\n", emitted); + + irtoy->emitted = emitted; + } else if (len == 1 && + irtoy->in[0] == REPLY_XMITSUCCESS) { + irtoy->state = STATE_IRDATA; + complete(&irtoy->command_done); + } + } else { + // send next part of tx buffer + uint space = irtoy->in[0]; + uint buf_len; + int err; + + if (len != 1 || space > MAX_PACKET || space == 0) { + dev_err(irtoy->dev, "packet length expected: %*phN\n", + len, irtoy->in); + irtoy->state = STATE_IRDATA; + complete(&irtoy->command_done); + break; + } + + buf_len = min(space, irtoy->tx_len); + + dev_dbg(irtoy->dev, "remaining:%u sending:%u\n", + irtoy->tx_len, buf_len); + + memcpy(irtoy->out, irtoy->tx_buf, buf_len); + irtoy->urb_out->transfer_buffer_length = buf_len; + err = usb_submit_urb(irtoy->urb_out, GFP_ATOMIC); + if (err != 0) { + dev_err(irtoy->dev, "fail to submit tx buf urb: %d\n", + err); + irtoy->state = STATE_IRDATA; + complete(&irtoy->command_done); + break; + } + + irtoy->tx_buf += buf_len; + irtoy->tx_len -= buf_len; + } + break; + case STATE_RESET: + dev_err(irtoy->dev, "unexpected response to reset: %*phN\n", + len, irtoy->in); + } +} + +static void irtoy_out_callback(struct urb *urb) +{ + struct irtoy *irtoy = urb->context; + + if (urb->status == 0) { + if (irtoy->state == STATE_RESET) + complete(&irtoy->command_done); + } else { + dev_warn(irtoy->dev, "out urb status: %d\n", urb->status); + } +} + +static void irtoy_in_callback(struct urb *urb) +{ + struct irtoy *irtoy = urb->context; + int ret; + + if (urb->status == 0) + irtoy_response(irtoy, urb->actual_length); + else + dev_dbg(irtoy->dev, "in urb status: %d\n", urb->status); + + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret && ret != -ENODEV) + dev_warn(irtoy->dev, "failed to resubmit urb: %d\n", ret); +} + +static int irtoy_command(struct irtoy *irtoy, const u8 *cmd, int cmd_len, + enum state state) +{ + int err; + + init_completion(&irtoy->command_done); + + irtoy->state = state; + + memcpy(irtoy->out, cmd, cmd_len); + irtoy->urb_out->transfer_buffer_length = cmd_len; + + err = usb_submit_urb(irtoy->urb_out, GFP_KERNEL); + if (err != 0) + return err; + + if (!wait_for_completion_timeout(&irtoy->command_done, + msecs_to_jiffies(TIMEOUT))) { + usb_kill_urb(irtoy->urb_out); + return -ETIMEDOUT; + } + + return 0; +} + +static int irtoy_setup(struct irtoy *irtoy) +{ + int err; + + err = irtoy_command(irtoy, COMMAND_RESET, sizeof(COMMAND_RESET), + STATE_RESET); + if (err != 0) { + dev_err(irtoy->dev, "could not write reset command: %d\n", + err); + return err; + } + + usleep_range(50, 50); + + // get version + err = irtoy_command(irtoy, COMMAND_VERSION, sizeof(COMMAND_VERSION), + STATE_COMMAND); + if (err) { + dev_err(irtoy->dev, "could not write version command: %d\n", + err); + return err; + } + + // enter sample mode + err = irtoy_command(irtoy, COMMAND_SMODE_ENTER, + sizeof(COMMAND_SMODE_ENTER), STATE_COMMAND); + if (err) + dev_err(irtoy->dev, "could not write sample command: %d\n", + err); + + return err; +} + +/* + * When sending IR, it is imperative that we send the IR data as quickly + * as possible to the device, so it does not run out of IR data and + * introduce gaps. Allocate the buffer here, and then feed the data from + * the urb callback handler. + */ +static int irtoy_tx(struct rc_dev *rc, uint *txbuf, uint count) +{ + struct irtoy *irtoy = rc->priv; + unsigned int i, size; + __be16 *buf; + int err; + + size = sizeof(u16) * (count + 1); + buf = kmalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + for (i = 0; i < count; i++) { + u16 v = DIV_ROUND_CLOSEST(US_TO_NS(txbuf[i]), UNIT_NS); + + if (!v) + v = 1; + buf[i] = cpu_to_be16(v); + } + + buf[count] = cpu_to_be16(0xffff); + + irtoy->tx_buf = buf; + irtoy->tx_len = size; + irtoy->emitted = 0; + + err = irtoy_command(irtoy, COMMAND_TXSTART, sizeof(COMMAND_TXSTART), + STATE_TX); + kfree(buf); + + if (err) { + dev_err(irtoy->dev, "failed to send tx start command: %d\n", + err); + // not sure what state the device is in, reset it + irtoy_setup(irtoy); + return err; + } + + if (size != irtoy->emitted) { + dev_err(irtoy->dev, "expected %u emitted, got %u\n", size, + irtoy->emitted); + // not sure what state the device is in, reset it + irtoy_setup(irtoy); + return -EINVAL; + } + + return count; +} + +static int irtoy_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + 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 irtoy *irtoy; + struct rc_dev *rc; + struct urb *urb; + int i, pipe, err = -ENOMEM; + + 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) { + dev_err(&intf->dev, "required endpoints not found\n"); + return -ENODEV; + } + + irtoy = kzalloc(sizeof(*irtoy), GFP_KERNEL); + if (!irtoy) + return -ENOMEM; + + irtoy->in = kmalloc(MAX_PACKET, GFP_KERNEL); + if (!irtoy->in) + goto free_irtoy; + + irtoy->out = kmalloc(MAX_PACKET, GFP_KERNEL); + if (!irtoy->out) + goto free_irtoy; + + rc = rc_allocate_device(RC_DRIVER_IR_RAW); + if (!rc) + goto free_irtoy; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + goto free_rcdev; + + pipe = usb_rcvbulkpipe(usbdev, ep_in->bEndpointAddress); + usb_fill_bulk_urb(urb, usbdev, pipe, irtoy->in, MAX_PACKET, + irtoy_in_callback, irtoy); + irtoy->urb_in = urb; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + goto free_rcdev; + + pipe = usb_sndbulkpipe(usbdev, ep_out->bEndpointAddress); + usb_fill_bulk_urb(urb, usbdev, pipe, irtoy->out, MAX_PACKET, + irtoy_out_callback, irtoy); + + irtoy->dev = &intf->dev; + irtoy->usbdev = usbdev; + irtoy->rc = rc; + irtoy->urb_out = urb; + irtoy->pulse = true; + + err = usb_submit_urb(irtoy->urb_in, GFP_KERNEL); + if (err != 0) { + dev_err(irtoy->dev, "fail to submit in urb: %d\n", err); + return err; + } + + err = irtoy_setup(irtoy); + if (err) + goto free_rcdev; + + dev_info(irtoy->dev, "version: hardware %u, firmware %u, protocol %u", + irtoy->hw_version, irtoy->sw_version, irtoy->proto_version); + + if (irtoy->sw_version < MIN_FW_VERSION) { + dev_err(irtoy->dev, "need firmware V%02u or higher", + MIN_FW_VERSION); + err = -ENODEV; + goto free_rcdev; + } + + usb_make_path(usbdev, irtoy->phys, sizeof(irtoy->phys)); + + rc->device_name = "Infrared Toy"; + rc->driver_name = KBUILD_MODNAME; + rc->input_phys = irtoy->phys; + usb_to_input_id(usbdev, &rc->input_id); + rc->dev.parent = &intf->dev; + rc->priv = irtoy; + rc->tx_ir = irtoy_tx; + rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; + rc->map_name = RC_MAP_RC6_MCE; + rc->rx_resolution = UNIT_NS; + rc->timeout = IR_DEFAULT_TIMEOUT; + + /* + * end of transmission is detected by absence of a usb packet + * with more pulse/spaces. However, each usb packet sent can + * contain 32 pulse/spaces, which can be quite lengthy, so there + * can be a delay between usb packets. For example with nec there is a + * 17ms gap between packets. + * + * So, make timeout a largish minimum which works with most protocols. + */ + rc->min_timeout = MS_TO_NS(40); + rc->max_timeout = MAX_TIMEOUT_NS; + + err = rc_register_device(rc); + if (err) + goto free_rcdev; + + usb_set_intfdata(intf, irtoy); + + return 0; + +free_rcdev: + usb_kill_urb(irtoy->urb_out); + usb_free_urb(irtoy->urb_out); + usb_kill_urb(irtoy->urb_in); + usb_free_urb(irtoy->urb_in); + rc_free_device(rc); +free_irtoy: + kfree(irtoy->in); + kfree(irtoy->out); + kfree(irtoy); + return err; +} + +static void irtoy_disconnect(struct usb_interface *intf) +{ + struct irtoy *ir = usb_get_intfdata(intf); + + rc_unregister_device(ir->rc); + usb_set_intfdata(intf, NULL); + usb_kill_urb(ir->urb_out); + usb_free_urb(ir->urb_out); + usb_kill_urb(ir->urb_in); + usb_free_urb(ir->urb_in); + kfree(ir->in); + kfree(ir->out); + kfree(ir); +} + +static const struct usb_device_id irtoy_table[] = { + { USB_DEVICE_INTERFACE_CLASS(0x04d8, 0xfd08, USB_CLASS_CDC_DATA) }, + { } +}; + +static struct usb_driver irtoy_driver = { + .name = KBUILD_MODNAME, + .probe = irtoy_probe, + .disconnect = irtoy_disconnect, + .id_table = irtoy_table, +}; + +module_usb_driver(irtoy_driver); + +MODULE_AUTHOR("Sean Young <sean@mess.org>"); +MODULE_DESCRIPTION("Infrared Toy and IR Droid driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(usb, irtoy_table); diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c index 48a69bf23236..52d246dc5b3d 100644 --- a/drivers/media/rc/nuvoton-cir.c +++ b/drivers/media/rc/nuvoton-cir.c @@ -74,13 +74,6 @@ static inline void nvt_set_reg_bit(struct nvt_dev *nvt, u8 val, u8 reg) nvt_cr_write(nvt, tmp, reg); } -/* clear config register bit without changing other bits */ -static inline void nvt_clear_reg_bit(struct nvt_dev *nvt, u8 val, u8 reg) -{ - u8 tmp = nvt_cr_read(nvt, reg) & ~val; - nvt_cr_write(nvt, tmp, reg); -} - /* enter extended function mode */ static inline int nvt_efm_enable(struct nvt_dev *nvt) { @@ -631,30 +624,6 @@ static u32 nvt_rx_carrier_detect(struct nvt_dev *nvt) return carrier; } #endif -/* - * set carrier frequency - * - * set carrier on 2 registers: CP & CC - * always set CP as 0x81 - * set CC by SPEC, CC = 3MHz/carrier - 1 - */ -static int nvt_set_tx_carrier(struct rc_dev *dev, u32 carrier) -{ - struct nvt_dev *nvt = dev->priv; - u16 val; - - if (carrier == 0) - return -EINVAL; - - nvt_cir_reg_write(nvt, 1, CIR_CP); - val = 3000000 / (carrier) - 1; - nvt_cir_reg_write(nvt, val & 0xff, CIR_CC); - - nvt_dbg("cp: 0x%x cc: 0x%x\n", - nvt_cir_reg_read(nvt, CIR_CP), nvt_cir_reg_read(nvt, CIR_CC)); - - return 0; -} static int nvt_ir_raw_set_wakeup_filter(struct rc_dev *dev, struct rc_scancode_filter *sc_filter) @@ -1022,7 +991,6 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) rdev->encode_wakeup = true; rdev->open = nvt_open; rdev->close = nvt_close; - rdev->s_tx_carrier = nvt_set_tx_carrier; rdev->s_wakeup_filter = nvt_ir_raw_set_wakeup_filter; rdev->device_name = "Nuvoton w836x7hg Infrared Remote Transceiver"; rdev->input_phys = "nuvoton/cir0"; diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index d7064d664d52..7b53066d9d07 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -2052,7 +2052,7 @@ static int __init rc_core_init(void) if (rc) { pr_err("rc_core: unable to init lirc\n"); class_unregister(&rc_class); - return 0; + return rc; } led_trigger_register_simple("rc-feedback", &led_feedback); diff --git a/drivers/media/test-drivers/vicodec/vicodec-core.c b/drivers/media/test-drivers/vicodec/vicodec-core.c index e879290727ef..8941d73f6611 100644 --- a/drivers/media/test-drivers/vicodec/vicodec-core.c +++ b/drivers/media/test-drivers/vicodec/vicodec-core.c @@ -1442,7 +1442,7 @@ static void vicodec_buf_queue(struct vb2_buffer *vb) .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, }; - if (!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type) && + if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type) && vb2_is_streaming(vb->vb2_queue) && v4l2_m2m_dst_buf_is_last(ctx->fh.m2m_ctx)) { unsigned int i; @@ -1479,7 +1479,7 @@ static void vicodec_buf_queue(struct vb2_buffer *vb) * in the compressed stream */ if (ctx->is_stateless || ctx->is_enc || - !V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { + V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) { v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); return; } @@ -1574,7 +1574,7 @@ static int vicodec_start_streaming(struct vb2_queue *q, state->gop_cnt = 0; if ((V4L2_TYPE_IS_OUTPUT(q->type) && !ctx->is_enc) || - (!V4L2_TYPE_IS_OUTPUT(q->type) && ctx->is_enc)) + (V4L2_TYPE_IS_CAPTURE(q->type) && ctx->is_enc)) return 0; if (info->id == V4L2_PIX_FMT_FWHT || diff --git a/drivers/media/test-drivers/vimc/Kconfig b/drivers/media/test-drivers/vimc/Kconfig index 4068a67585f9..da4b2ad6e40c 100644 --- a/drivers/media/test-drivers/vimc/Kconfig +++ b/drivers/media/test-drivers/vimc/Kconfig @@ -2,6 +2,8 @@ config VIDEO_VIMC tristate "Virtual Media Controller Driver (VIMC)" depends on VIDEO_DEV && VIDEO_V4L2 + select FONT_SUPPORT + select FONT_8x16 select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select VIDEOBUF2_VMALLOC diff --git a/drivers/media/test-drivers/vimc/vimc-common.h b/drivers/media/test-drivers/vimc/vimc-common.h index ae163dec2459..a289434e75ba 100644 --- a/drivers/media/test-drivers/vimc/vimc-common.h +++ b/drivers/media/test-drivers/vimc/vimc-common.h @@ -20,6 +20,7 @@ #define VIMC_CID_VIMC_CLASS (0x00f00000 | 1) #define VIMC_CID_TEST_PATTERN (VIMC_CID_VIMC_BASE + 0) #define VIMC_CID_MEAN_WIN_SIZE (VIMC_CID_VIMC_BASE + 1) +#define VIMC_CID_OSD_TEXT_MODE (VIMC_CID_VIMC_BASE + 2) #define VIMC_FRAME_MAX_WIDTH 4096 #define VIMC_FRAME_MAX_HEIGHT 2160 diff --git a/drivers/media/test-drivers/vimc/vimc-core.c b/drivers/media/test-drivers/vimc/vimc-core.c index 11210aaa2551..4b0ae6f51d76 100644 --- a/drivers/media/test-drivers/vimc/vimc-core.c +++ b/drivers/media/test-drivers/vimc/vimc-core.c @@ -5,10 +5,12 @@ * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com> */ +#include <linux/font.h> #include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> #include <media/media-device.h> +#include <media/tpg/v4l2-tpg.h> #include <media/v4l2-device.h> #include "vimc-common.h" @@ -263,11 +265,19 @@ err_v4l2_unregister: static int vimc_probe(struct platform_device *pdev) { + const struct font_desc *font = find_font("VGA8x16"); struct vimc_device *vimc; int ret; dev_dbg(&pdev->dev, "probe"); + if (!font) { + dev_err(&pdev->dev, "could not find font\n"); + return -ENODEV; + } + + tpg_set_font(font->data); + vimc = kzalloc(sizeof(*vimc), GFP_KERNEL); if (!vimc) return -ENOMEM; diff --git a/drivers/media/test-drivers/vimc/vimc-sensor.c b/drivers/media/test-drivers/vimc/vimc-sensor.c index a2f09ac9a360..ba5db5a150b4 100644 --- a/drivers/media/test-drivers/vimc/vimc-sensor.c +++ b/drivers/media/test-drivers/vimc/vimc-sensor.c @@ -14,11 +14,19 @@ #include "vimc-common.h" +enum vimc_sen_osd_mode { + VIMC_SEN_OSD_SHOW_ALL = 0, + VIMC_SEN_OSD_SHOW_COUNTERS = 1, + VIMC_SEN_OSD_SHOW_NONE = 2 +}; + struct vimc_sen_device { struct vimc_ent_device ved; struct v4l2_subdev sd; struct tpg_data tpg; u8 *frame; + enum vimc_sen_osd_mode osd_value; + u64 start_stream_ts; /* The active format */ struct v4l2_mbus_framefmt mbus_format; struct v4l2_ctrl_handler hdl; @@ -187,8 +195,49 @@ static void *vimc_sen_process_frame(struct vimc_ent_device *ved, { struct vimc_sen_device *vsen = container_of(ved, struct vimc_sen_device, ved); + const unsigned int line_height = 16; + u8 *basep[TPG_MAX_PLANES][2]; + unsigned int line = 1; + char str[100]; tpg_fill_plane_buffer(&vsen->tpg, 0, 0, vsen->frame); + tpg_calc_text_basep(&vsen->tpg, basep, 0, vsen->frame); + switch (vsen->osd_value) { + case VIMC_SEN_OSD_SHOW_ALL: { + const char *order = tpg_g_color_order(&vsen->tpg); + + tpg_gen_text(&vsen->tpg, basep, line++ * line_height, + 16, order); + snprintf(str, sizeof(str), + "brightness %3d, contrast %3d, saturation %3d, hue %d ", + vsen->tpg.brightness, + vsen->tpg.contrast, + vsen->tpg.saturation, + vsen->tpg.hue); + tpg_gen_text(&vsen->tpg, basep, line++ * line_height, 16, str); + snprintf(str, sizeof(str), "sensor size: %dx%d", + vsen->mbus_format.width, + vsen->mbus_format.height); + tpg_gen_text(&vsen->tpg, basep, line++ * line_height, 16, str); + fallthrough; + } + case VIMC_SEN_OSD_SHOW_COUNTERS: { + unsigned int ms; + + ms = div_u64(ktime_get_ns() - vsen->start_stream_ts, 1000000); + snprintf(str, sizeof(str), "%02d:%02d:%02d:%03d", + (ms / (60 * 60 * 1000)) % 24, + (ms / (60 * 1000)) % 60, + (ms / 1000) % 60, + ms % 1000); + tpg_gen_text(&vsen->tpg, basep, line++ * line_height, 16, str); + break; + } + case VIMC_SEN_OSD_SHOW_NONE: + default: + break; + } + return vsen->frame; } @@ -201,6 +250,8 @@ static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable) const struct vimc_pix_map *vpix; unsigned int frame_size; + vsen->start_stream_ts = ktime_get_ns(); + /* Calculate the frame size */ vpix = vimc_pix_map_by_code(vsen->mbus_format.code); frame_size = vsen->mbus_format.width * vpix->bpp * @@ -269,6 +320,9 @@ static int vimc_sen_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_SATURATION: tpg_s_saturation(&vsen->tpg, ctrl->val); break; + case VIMC_CID_OSD_TEXT_MODE: + vsen->osd_value = ctrl->val; + break; default: return -EINVAL; } @@ -307,6 +361,22 @@ static const struct v4l2_ctrl_config vimc_sen_ctrl_test_pattern = { .qmenu = tpg_pattern_strings, }; +static const char * const vimc_ctrl_osd_mode_strings[] = { + "All", + "Counters Only", + "None", + NULL, +}; + +static const struct v4l2_ctrl_config vimc_sen_ctrl_osd_mode = { + .ops = &vimc_sen_ctrl_ops, + .id = VIMC_CID_OSD_TEXT_MODE, + .name = "Show Information", + .type = V4L2_CTRL_TYPE_MENU, + .max = ARRAY_SIZE(vimc_ctrl_osd_mode_strings) - 2, + .qmenu = vimc_ctrl_osd_mode_strings, +}; + static struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc, const char *vcfg_name) { @@ -323,6 +393,7 @@ static struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc, v4l2_ctrl_new_custom(&vsen->hdl, &vimc_sen_ctrl_class, NULL); v4l2_ctrl_new_custom(&vsen->hdl, &vimc_sen_ctrl_test_pattern, NULL); + v4l2_ctrl_new_custom(&vsen->hdl, &vimc_sen_ctrl_osd_mode, NULL); v4l2_ctrl_new_std(&vsen->hdl, &vimc_sen_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); v4l2_ctrl_new_std(&vsen->hdl, &vimc_sen_ctrl_ops, diff --git a/drivers/media/test-drivers/vivid/vivid-core.c b/drivers/media/test-drivers/vivid/vivid-core.c index 6c740e3e6999..f7ee37e9508d 100644 --- a/drivers/media/test-drivers/vivid/vivid-core.c +++ b/drivers/media/test-drivers/vivid/vivid-core.c @@ -169,6 +169,14 @@ MODULE_PARM_DESC(allocators, " memory allocator selection, default is 0.\n" "\t\t 0 == vmalloc\n" "\t\t 1 == dma-contig"); +static unsigned int cache_hints[VIVID_MAX_DEVS] = { + [0 ... (VIVID_MAX_DEVS - 1)] = 0 +}; +module_param_array(cache_hints, uint, NULL, 0444); +MODULE_PARM_DESC(cache_hints, " user-space cache hints, default is 0.\n" + "\t\t 0 == forbid\n" + "\t\t 1 == allow"); + static struct vivid_dev *vivid_devs[VIVID_MAX_DEVS]; const struct v4l2_rect vivid_min_rect = { @@ -819,6 +827,7 @@ static int vivid_create_queue(struct vivid_dev *dev, q->lock = &dev->mutex; q->dev = dev->v4l2_dev.dev; q->supports_requests = true; + q->allow_cache_hints = (cache_hints[dev->inst] == 1); return vb2_queue_init(q); } @@ -1117,7 +1126,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) ret = -ENOMEM; /* initialize the test pattern generator */ tpg_init(&dev->tpg, 640, 360); - if (tpg_alloc(&dev->tpg, MAX_ZOOM * MAX_WIDTH)) + if (tpg_alloc(&dev->tpg, array_size(MAX_WIDTH, MAX_ZOOM))) goto free_dev; dev->scaled_line = vzalloc(array_size(MAX_WIDTH, MAX_ZOOM)); if (!dev->scaled_line) @@ -1127,7 +1136,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) goto free_dev; /* load the edid */ - dev->edid = vmalloc(256 * 128); + dev->edid = vmalloc(array_size(256, 128)); if (!dev->edid) goto free_dev; diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c index b0cd51134654..c5e21785fafe 100644 --- a/drivers/media/usb/cx231xx/cx231xx-417.c +++ b/drivers/media/usb/cx231xx/cx231xx-417.c @@ -1734,7 +1734,7 @@ static void cx231xx_video_dev_init( int cx231xx_417_register(struct cx231xx *dev) { /* FIXME: Port1 hardcoded here */ - int err = -ENODEV; + int err; struct cx231xx_tsport *tsport = &dev->ts1; struct vb2_queue *q; diff --git a/drivers/media/usb/dvb-usb-v2/dvbsky.c b/drivers/media/usb/dvb-usb-v2/dvbsky.c index 356fd8e66834..0d9657f7f29d 100644 --- a/drivers/media/usb/dvb-usb-v2/dvbsky.c +++ b/drivers/media/usb/dvb-usb-v2/dvbsky.c @@ -778,16 +778,16 @@ static const struct usb_device_id dvbsky_id_table[] = { &dvbsky_s960_props, "Terratec Cinergy S2 Rev.4", RC_MAP_DVBSKY) }, { DVB_USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230, - &mygica_t230c_props, "MyGica Mini DVB-T2 USB Stick T230", + &mygica_t230c_props, "MyGica Mini DVB-(T/T2/C) USB Stick T230", RC_MAP_TOTAL_MEDIA_IN_HAND_02) }, { DVB_USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230C, - &mygica_t230c_props, "MyGica Mini DVB-T2 USB Stick T230C", + &mygica_t230c_props, "MyGica Mini DVB-(T/T2/C) USB Stick T230C", RC_MAP_TOTAL_MEDIA_IN_HAND_02) }, { DVB_USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230C_LITE, - &mygica_t230c_props, "MyGica Mini DVB-T2 USB Stick T230C Lite", + &mygica_t230c_props, "MyGica Mini DVB-(T/T2/C) USB Stick T230C Lite", NULL) }, { DVB_USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230C2, - &mygica_t230c_props, "MyGica Mini DVB-T2 USB Stick T230C v2", + &mygica_t230c_props, "MyGica Mini DVB-(T/T2/C) USB Stick T230C v2", RC_MAP_TOTAL_MEDIA_IN_HAND_02) }, { } }; diff --git a/drivers/media/usb/dvb-usb/Kconfig b/drivers/media/usb/dvb-usb/Kconfig index 15d29c91662f..25ba03edcb5c 100644 --- a/drivers/media/usb/dvb-usb/Kconfig +++ b/drivers/media/usb/dvb-usb/Kconfig @@ -151,6 +151,7 @@ config DVB_USB_CXUSB config DVB_USB_CXUSB_ANALOG bool "Analog support for the Conexant USB2.0 hybrid reference design" depends on DVB_USB_CXUSB && VIDEO_V4L2 + depends on VIDEO_V4L2=y || VIDEO_V4L2=DVB_USB_CXUSB select VIDEO_CX25840 select VIDEOBUF2_VMALLOC help diff --git a/drivers/media/usb/go7007/go7007-usb.c b/drivers/media/usb/go7007/go7007-usb.c index f889c9d740cd..dbf0455d5d50 100644 --- a/drivers/media/usb/go7007/go7007-usb.c +++ b/drivers/media/usb/go7007/go7007-usb.c @@ -1132,6 +1132,10 @@ static int go7007_usb_probe(struct usb_interface *intf, go->hpi_ops = &go7007_usb_onboard_hpi_ops; go->hpi_context = usb; + ep = usb->usbdev->ep_in[4]; + if (!ep) + return -ENODEV; + /* Allocate the URB and buffer for receiving incoming interrupts */ usb->intr_urb = usb_alloc_urb(0, GFP_KERNEL); if (usb->intr_urb == NULL) @@ -1141,7 +1145,6 @@ static int go7007_usb_probe(struct usb_interface *intf, if (usb->intr_urb->transfer_buffer == NULL) goto allocfail; - ep = usb->usbdev->ep_in[4]; if (usb_endpoint_type(&ep->desc) == USB_ENDPOINT_XFER_BULK) usb_fill_bulk_urb(usb->intr_urb, usb->usbdev, usb_rcvbulkpipe(usb->usbdev, 4), @@ -1263,9 +1266,13 @@ static int go7007_usb_probe(struct usb_interface *intf, /* Allocate the URBs and buffers for receiving the video stream */ if (board->flags & GO7007_USB_EZUSB) { + if (!usb->usbdev->ep_in[6]) + goto allocfail; v_urb_len = 1024; video_pipe = usb_rcvbulkpipe(usb->usbdev, 6); } else { + if (!usb->usbdev->ep_in[1]) + goto allocfail; v_urb_len = 512; video_pipe = usb_rcvbulkpipe(usb->usbdev, 1); } @@ -1285,6 +1292,8 @@ static int go7007_usb_probe(struct usb_interface *intf, /* Allocate the URBs and buffers for receiving the audio stream */ if ((board->flags & GO7007_USB_EZUSB) && (board->main_info.flags & GO7007_BOARD_HAS_AUDIO)) { + if (!usb->usbdev->ep_in[8]) + goto allocfail; for (i = 0; i < 8; ++i) { usb->audio_urbs[i] = usb_alloc_urb(0, GFP_KERNEL); if (usb->audio_urbs[i] == NULL) diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c index 0566e00d6fea..c295f642d352 100644 --- a/drivers/media/usb/gspca/gspca.c +++ b/drivers/media/usb/gspca/gspca.c @@ -925,7 +925,7 @@ static int wxh_to_nearest_mode(struct gspca_dev *gspca_dev, { int i; - for (i = gspca_dev->cam.nmodes; --i > 0; ) { + for (i = gspca_dev->cam.nmodes; --i >= 0; ) { if (width >= gspca_dev->cam.cam_mode[i].width && height >= gspca_dev->cam.cam_mode[i].height && pixelformat == gspca_dev->cam.cam_mode[i].pixelformat) diff --git a/drivers/media/usb/gspca/jl2005bcd.c b/drivers/media/usb/gspca/jl2005bcd.c index 80ce7448b3dd..ca12f33f3e12 100644 --- a/drivers/media/usb/gspca/jl2005bcd.c +++ b/drivers/media/usb/gspca/jl2005bcd.c @@ -165,7 +165,7 @@ static int jl2005c_get_firmware_id(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *)gspca_dev; int i = 0; - int retval = -1; + int retval; unsigned char regs_to_read[] = {0x57, 0x02, 0x03, 0x5d, 0x5e, 0x5f}; gspca_dbg(gspca_dev, D_PROBE, "Running jl2005c_get_firmware_id\n"); diff --git a/drivers/media/usb/gspca/ov534.c b/drivers/media/usb/gspca/ov534.c index 185c1f10fb30..9a11158f38da 100644 --- a/drivers/media/usb/gspca/ov534.c +++ b/drivers/media/usb/gspca/ov534.c @@ -14,7 +14,7 @@ * PS3 Eye camera - brightness, contrast, awb, agc, aec controls * added by Max Thrun <bear24rw@gmail.com> * PS3 Eye camera - FPS range extended by Joseph Howse - * <josephhowse@nummist.com> http://nummist.com + * <josephhowse@nummist.com> https://nummist.com */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/drivers/media/usb/gspca/sn9c2028.c b/drivers/media/usb/gspca/sn9c2028.c index aff01b753853..dbd1d6da37f1 100644 --- a/drivers/media/usb/gspca/sn9c2028.c +++ b/drivers/media/usb/gspca/sn9c2028.c @@ -215,7 +215,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) { - int status = -1; + int status; sn9c2028_read1(gspca_dev); sn9c2028_read1(gspca_dev); diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 8bde33c21ce4..e3ab003a6c85 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -50,7 +50,8 @@ static int v4l2_async_notifier_call_complete(struct v4l2_async_notifier *n) return n->ops->complete(n); } -static bool match_i2c(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) +static bool match_i2c(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) { #if IS_ENABLED(CONFIG_I2C) struct i2c_client *client = i2c_verify_client(sd->dev); @@ -63,18 +64,83 @@ static bool match_i2c(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) #endif } -static bool match_devname(struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) +static bool match_devname(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) { return !strcmp(asd->match.device_name, dev_name(sd->dev)); } -static bool match_fwnode(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) +static bool match_fwnode(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) { - return sd->fwnode == asd->match.fwnode; + struct fwnode_handle *other_fwnode; + struct fwnode_handle *dev_fwnode; + bool asd_fwnode_is_ep; + bool sd_fwnode_is_ep; + struct device *dev; + + /* + * Both the subdev and the async subdev can provide either an endpoint + * fwnode or a device fwnode. Start with the simple case of direct + * fwnode matching. + */ + if (sd->fwnode == asd->match.fwnode) + return true; + + /* + * Otherwise, check if the sd fwnode and the asd fwnode refer to an + * endpoint or a device. If they're of the same type, there's no match. + * Technically speaking this checks if the nodes refer to a connected + * endpoint, which is the simplest check that works for both OF and + * ACPI. This won't make a difference, as drivers should not try to + * match unconnected endpoints. + */ + sd_fwnode_is_ep = fwnode_graph_is_endpoint(sd->fwnode); + asd_fwnode_is_ep = fwnode_graph_is_endpoint(asd->match.fwnode); + + if (sd_fwnode_is_ep == asd_fwnode_is_ep) + return false; + + /* + * The sd and asd fwnodes are of different types. Get the device fwnode + * parent of the endpoint fwnode, and compare it with the other fwnode. + */ + if (sd_fwnode_is_ep) { + dev_fwnode = fwnode_graph_get_port_parent(sd->fwnode); + other_fwnode = asd->match.fwnode; + } else { + dev_fwnode = fwnode_graph_get_port_parent(asd->match.fwnode); + other_fwnode = sd->fwnode; + } + + fwnode_handle_put(dev_fwnode); + + if (dev_fwnode != other_fwnode) + return false; + + /* + * We have a heterogeneous match. Retrieve the struct device of the side + * that matched on a device fwnode to print its driver name. + */ + if (sd_fwnode_is_ep) + dev = notifier->v4l2_dev ? notifier->v4l2_dev->dev + : notifier->sd->dev; + else + dev = sd->dev; + + if (dev && dev->driver) { + if (sd_fwnode_is_ep) + dev_warn(dev, "Driver %s uses device fwnode, incorrect match may occur\n", + dev->driver->name); + dev_notice(dev, "Consider updating driver %s to match on endpoints\n", + dev->driver->name); + } + + return true; } -static bool match_custom(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) +static bool match_custom(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) { if (!asd->match.custom.match) /* Match always */ @@ -91,7 +157,8 @@ static struct v4l2_async_subdev * v4l2_async_find_match(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd) { - bool (*match)(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd); + bool (*match)(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, struct v4l2_async_subdev *asd); struct v4l2_async_subdev *asd; list_for_each_entry(asd, ¬ifier->waiting, list) { @@ -116,7 +183,7 @@ v4l2_async_find_match(struct v4l2_async_notifier *notifier, } /* match cannot be NULL here */ - if (match(sd, asd)) + if (match(notifier, sd, asd)) return asd; } diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c index a99e82ec9ab6..593bcf6c3735 100644 --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c @@ -246,6 +246,9 @@ struct v4l2_format32 { * @memory: buffer memory type * @format: frame format, for which buffers are requested * @capabilities: capabilities of this buffer type. + * @flags: additional buffer management attributes (ignored unless the + * queue has V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS capability and + * configured for MMAP streaming I/O). * @reserved: future extensions */ struct v4l2_create_buffers32 { @@ -254,7 +257,8 @@ struct v4l2_create_buffers32 { __u32 memory; /* enum v4l2_memory */ struct v4l2_format32 format; __u32 capabilities; - __u32 reserved[7]; + __u32 flags; + __u32 reserved[6]; }; static int __bufsize_v4l2_format(struct v4l2_format32 __user *p32, u32 *size) @@ -355,7 +359,8 @@ static int get_v4l2_create32(struct v4l2_create_buffers __user *p64, { if (!access_ok(p32, sizeof(*p32)) || copy_in_user(p64, p32, - offsetof(struct v4l2_create_buffers32, format))) + offsetof(struct v4l2_create_buffers32, format)) || + assign_in_user(&p64->flags, &p32->flags)) return -EFAULT; return __get_v4l2_format32(&p64->format, &p32->format, aux_buf, aux_space); @@ -417,6 +422,7 @@ static int put_v4l2_create32(struct v4l2_create_buffers __user *p64, copy_in_user(p32, p64, offsetof(struct v4l2_create_buffers32, format)) || assign_in_user(&p32->capabilities, &p64->capabilities) || + assign_in_user(&p32->flags, &p64->flags) || copy_in_user(p32->reserved, p64->reserved, sizeof(p64->reserved))) return -EFAULT; return __put_v4l2_format32(&p64->format, &p32->format); diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 2322f08a98be..a556880f225a 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -582,7 +582,10 @@ static void v4l_print_querymenu(const void *arg, bool write_only) static void v4l_print_control(const void *arg, bool write_only) { const struct v4l2_control *p = arg; + const char *name = v4l2_ctrl_get_name(p->id); + if (name) + pr_cont("name=%s, ", name); pr_cont("id=0x%x, value=%d\n", p->id, p->value); } @@ -594,12 +597,15 @@ static void v4l_print_ext_controls(const void *arg, bool write_only) pr_cont("which=0x%x, count=%d, error_idx=%d, request_fd=%d", p->which, p->count, p->error_idx, p->request_fd); for (i = 0; i < p->count; i++) { + unsigned int id = p->controls[i].id; + const char *name = v4l2_ctrl_get_name(id); + + if (name) + pr_cont(", name=%s", name); if (!p->controls[i].size) - pr_cont(", id/val=0x%x/0x%x", - p->controls[i].id, p->controls[i].value); + pr_cont(", id/val=0x%x/0x%x", id, p->controls[i].value); else - pr_cont(", id/size=0x%x/%u", - p->controls[i].id, p->controls[i].size); + pr_cont(", id/size=0x%x/%u", id, p->controls[i].size); } pr_cont("\n"); } @@ -2038,9 +2044,6 @@ static int v4l_reqbufs(const struct v4l2_ioctl_ops *ops, if (ret) return ret; - - CLEAR_AFTER_FIELD(p, capabilities); - return ops->vidioc_reqbufs(file, fh, p); } @@ -2080,7 +2083,7 @@ static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops, if (ret) return ret; - CLEAR_AFTER_FIELD(create, capabilities); + CLEAR_AFTER_FIELD(create, flags); v4l_sanitize_format(&create->format); diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 62ac9424c92a..95a8f2dc5341 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -556,7 +556,7 @@ int v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, ret = vb2_querybuf(vq, buf); /* Adjust MMAP memory offsets for the CAPTURE queue */ - if (buf->memory == V4L2_MEMORY_MMAP && !V4L2_TYPE_IS_OUTPUT(vq->type)) { + if (buf->memory == V4L2_MEMORY_MMAP && V4L2_TYPE_IS_CAPTURE(vq->type)) { if (V4L2_TYPE_IS_MULTIPLANAR(vq->type)) { for (i = 0; i < buf->length; ++i) buf->m.planes[i].m.mem_offset @@ -712,7 +712,7 @@ int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, int ret; vq = v4l2_m2m_get_vq(m2m_ctx, buf->type); - if (!V4L2_TYPE_IS_OUTPUT(vq->type) && + if (V4L2_TYPE_IS_CAPTURE(vq->type) && (buf->flags & V4L2_BUF_FLAG_REQUEST_FD)) { dprintk("%s: requests cannot be used with capture buffers\n", __func__); @@ -729,7 +729,7 @@ int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, * buffer as DONE with LAST flag since it won't be queued on the * device. */ - if (!V4L2_TYPE_IS_OUTPUT(vq->type) && + if (V4L2_TYPE_IS_CAPTURE(vq->type) && vb2_is_streaming(vq) && !vb2_start_streaming_called(vq) && (v4l2_m2m_has_stopped(m2m_ctx) || v4l2_m2m_dst_buf_is_last(m2m_ctx))) v4l2_m2m_force_last_buf_done(m2m_ctx, vq); diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 4bb1eca6f597..71d077762698 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -42,8 +42,6 @@ source "drivers/staging/media/tegra-video/Kconfig" source "drivers/staging/media/ipu3/Kconfig" -source "drivers/staging/media/soc_camera/Kconfig" - source "drivers/staging/media/phy-rockchip-dphy-rx0/Kconfig" source "drivers/staging/media/rkisp1/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 71a47b61836d..17ececa1e095 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -10,7 +10,6 @@ obj-$(CONFIG_VIDEO_TEGRA) += tegra-video/ obj-$(CONFIG_TEGRA_VDE) += tegra-vde/ obj-$(CONFIG_VIDEO_HANTRO) += hantro/ obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3/ -obj-$(CONFIG_SOC_CAMERA) += soc_camera/ obj-$(CONFIG_PHY_ROCKCHIP_DPHY_RX0) += phy-rockchip-dphy-rx0/ obj-$(CONFIG_VIDEO_ROCKCHIP_ISP1) += rkisp1/ obj-$(CONFIG_VIDEO_USBVISION) += usbvision/ diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c index 70f133a842dd..9f718f43282b 100644 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -105,9 +105,11 @@ struct allegro_buffer { struct list_head head; }; +struct allegro_dev; struct allegro_channel; struct allegro_mbox { + struct allegro_dev *dev; unsigned int head; unsigned int tail; unsigned int data; @@ -128,14 +130,15 @@ struct allegro_dev { struct regmap *regmap; struct regmap *sram; + const struct fw_info *fw_info; struct allegro_buffer firmware; struct allegro_buffer suballocator; struct completion init_complete; /* The mailbox interface */ - struct allegro_mbox mbox_command; - struct allegro_mbox mbox_status; + struct allegro_mbox *mbox_command; + struct allegro_mbox *mbox_status; /* * The downstream driver limits the users to 64 users, thus I can use @@ -204,6 +207,11 @@ struct allegro_channel { unsigned int cpb_size; unsigned int gop_size; + struct allegro_buffer config_blob; + + unsigned int num_ref_idx_l0; + unsigned int num_ref_idx_l1; + struct v4l2_ctrl *mpeg_video_h264_profile; struct v4l2_ctrl *mpeg_video_h264_level; struct v4l2_ctrl *mpeg_video_h264_i_frame_qp; @@ -270,6 +278,7 @@ struct fw_info { unsigned int mailbox_cmd; unsigned int mailbox_status; size_t mailbox_size; + enum mcu_msg_version mailbox_version; size_t suballocator_size; }; @@ -281,7 +290,17 @@ static const struct fw_info supported_firmware[] = { .mailbox_cmd = 0x7800, .mailbox_status = 0x7c00, .mailbox_size = 0x400 - 0x8, + .mailbox_version = MCU_MSG_VERSION_2018_2, .suballocator_size = SZ_16M, + }, { + .id = 14680, + .id_codec = 126572, + .version = "v2019.2", + .mailbox_cmd = 0x7000, + .mailbox_status = 0x7800, + .mailbox_size = 0x800 - 0x8, + .mailbox_version = MCU_MSG_VERSION_2019_2, + .suballocator_size = SZ_32M, }, }; @@ -583,12 +602,20 @@ static void allegro_free_buffer(struct allegro_dev *dev, * Mailbox interface to send messages to the MCU. */ -static int allegro_mbox_init(struct allegro_dev *dev, - struct allegro_mbox *mbox, - unsigned int base, size_t size) +static void allegro_mcu_interrupt(struct allegro_dev *dev); +static void allegro_handle_message(struct allegro_dev *dev, + union mcu_msg_response *msg); + +static struct allegro_mbox *allegro_mbox_init(struct allegro_dev *dev, + unsigned int base, size_t size) { + struct allegro_mbox *mbox; + + mbox = devm_kmalloc(&dev->plat_dev->dev, sizeof(*mbox), GFP_KERNEL); if (!mbox) - return -EINVAL; + return ERR_PTR(-ENOMEM); + + mbox->dev = dev; mbox->head = base; mbox->tail = base + 0x4; @@ -599,52 +626,37 @@ static int allegro_mbox_init(struct allegro_dev *dev, regmap_write(dev->sram, mbox->head, 0); regmap_write(dev->sram, mbox->tail, 0); - return 0; + return mbox; } -static int allegro_mbox_write(struct allegro_dev *dev, - struct allegro_mbox *mbox, void *src, size_t size) +static int allegro_mbox_write(struct allegro_mbox *mbox, + const u32 *src, size_t size) { - struct mcu_msg_header *header = src; + struct regmap *sram = mbox->dev->sram; unsigned int tail; size_t size_no_wrap; int err = 0; + int stride = regmap_get_reg_stride(sram); if (!src) return -EINVAL; - if (size > mbox->size) { - v4l2_err(&dev->v4l2_dev, - "message (%zu bytes) too large for mailbox (%zu bytes)\n", - size, mbox->size); + if (size > mbox->size) return -EINVAL; - } - - if (header->length != size - sizeof(*header)) { - v4l2_err(&dev->v4l2_dev, - "invalid message length: %u bytes (expected %zu bytes)\n", - header->length, size - sizeof(*header)); - return -EINVAL; - } - - v4l2_dbg(2, debug, &dev->v4l2_dev, - "write command message: type %s, body length %d\n", - msg_type_name(header->type), header->length); mutex_lock(&mbox->lock); - regmap_read(dev->sram, mbox->tail, &tail); + regmap_read(sram, mbox->tail, &tail); if (tail > mbox->size) { - v4l2_err(&dev->v4l2_dev, - "invalid tail (0x%x): must be smaller than mailbox size (0x%zx)\n", - tail, mbox->size); err = -EIO; goto out; } size_no_wrap = min(size, mbox->size - (size_t)tail); - regmap_bulk_write(dev->sram, mbox->data + tail, src, size_no_wrap / 4); - regmap_bulk_write(dev->sram, mbox->data, - src + size_no_wrap, (size - size_no_wrap) / 4); - regmap_write(dev->sram, mbox->tail, (tail + size) % mbox->size); + regmap_bulk_write(sram, mbox->data + tail, + src, size_no_wrap / stride); + regmap_bulk_write(sram, mbox->data, + src + (size_no_wrap / sizeof(*src)), + (size - size_no_wrap) / stride); + regmap_write(sram, mbox->tail, (tail + size) % mbox->size); out: mutex_unlock(&mbox->lock); @@ -652,40 +664,32 @@ out: return err; } -static ssize_t allegro_mbox_read(struct allegro_dev *dev, - struct allegro_mbox *mbox, - void *dst, size_t nbyte) +static ssize_t allegro_mbox_read(struct allegro_mbox *mbox, + u32 *dst, size_t nbyte) { - struct mcu_msg_header *header; + struct { + u16 length; + u16 type; + } __attribute__ ((__packed__)) *header; + struct regmap *sram = mbox->dev->sram; unsigned int head; ssize_t size; size_t body_no_wrap; + int stride = regmap_get_reg_stride(sram); - regmap_read(dev->sram, mbox->head, &head); - if (head > mbox->size) { - v4l2_err(&dev->v4l2_dev, - "invalid head (0x%x): must be smaller than mailbox size (0x%zx)\n", - head, mbox->size); + regmap_read(sram, mbox->head, &head); + if (head > mbox->size) return -EIO; - } /* Assume that the header does not wrap. */ - regmap_bulk_read(dev->sram, mbox->data + head, - dst, sizeof(*header) / 4); - header = dst; + regmap_bulk_read(sram, mbox->data + head, + dst, sizeof(*header) / stride); + header = (void *)dst; size = header->length + sizeof(*header); - if (size > mbox->size || size & 0x3) { - v4l2_err(&dev->v4l2_dev, - "invalid message length: %zu bytes (maximum %zu bytes)\n", - header->length + sizeof(*header), mbox->size); + if (size > mbox->size || size & 0x3) return -EIO; - } - if (size > nbyte) { - v4l2_err(&dev->v4l2_dev, - "destination buffer too small: %zu bytes (need %zu bytes)\n", - nbyte, size); + if (size > nbyte) return -EINVAL; - } /* * The message might wrap within the mailbox. If the message does not @@ -698,24 +702,84 @@ static ssize_t allegro_mbox_read(struct allegro_dev *dev, */ body_no_wrap = min((size_t)header->length, (size_t)(mbox->size - (head + sizeof(*header)))); - regmap_bulk_read(dev->sram, mbox->data + head + sizeof(*header), - dst + sizeof(*header), body_no_wrap / 4); - regmap_bulk_read(dev->sram, mbox->data, - dst + sizeof(*header) + body_no_wrap, - (header->length - body_no_wrap) / 4); - - regmap_write(dev->sram, mbox->head, (head + size) % mbox->size); + regmap_bulk_read(sram, mbox->data + head + sizeof(*header), + dst + (sizeof(*header) / sizeof(*dst)), + body_no_wrap / stride); + regmap_bulk_read(sram, mbox->data, + dst + (sizeof(*header) + body_no_wrap) / sizeof(*dst), + (header->length - body_no_wrap) / stride); - v4l2_dbg(2, debug, &dev->v4l2_dev, - "read status message: type %s, body length %d\n", - msg_type_name(header->type), header->length); + regmap_write(sram, mbox->head, (head + size) % mbox->size); return size; } -static void allegro_mcu_interrupt(struct allegro_dev *dev) +/** + * allegro_mbox_send() - Send a message via the mailbox + * @mbox: the mailbox which is used to send the message + * @msg: the message to send + */ +static int allegro_mbox_send(struct allegro_mbox *mbox, void *msg) { - regmap_write(dev->regmap, AL5_MCU_INTERRUPT, BIT(0)); + struct allegro_dev *dev = mbox->dev; + ssize_t size; + int err; + u32 *tmp; + + tmp = kzalloc(mbox->size, GFP_KERNEL); + if (!tmp) { + err = -ENOMEM; + goto out; + } + + size = allegro_encode_mail(tmp, msg); + + err = allegro_mbox_write(mbox, tmp, size); + kfree(tmp); + if (err) + goto out; + + allegro_mcu_interrupt(dev); + +out: + return err; +} + +/** + * allegro_mbox_notify() - Notify the mailbox about a new message + * @mbox: The allegro_mbox to notify + */ +static void allegro_mbox_notify(struct allegro_mbox *mbox) +{ + struct allegro_dev *dev = mbox->dev; + union mcu_msg_response *msg; + ssize_t size; + u32 *tmp; + int err; + + msg = kmalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) + return; + + msg->header.version = dev->fw_info->mailbox_version; + + tmp = kmalloc(mbox->size, GFP_KERNEL); + if (!tmp) + goto out; + + size = allegro_mbox_read(mbox, tmp, mbox->size); + if (size < 0) + goto out; + + err = allegro_decode_mail(msg, tmp); + if (err) + goto out; + + allegro_handle_message(dev, msg); + +out: + kfree(tmp); + kfree(msg); } static void allegro_mcu_send_init(struct allegro_dev *dev, @@ -726,7 +790,7 @@ static void allegro_mcu_send_init(struct allegro_dev *dev, memset(&msg, 0, sizeof(msg)); msg.header.type = MCU_MSG_TYPE_INIT; - msg.header.length = sizeof(msg) - sizeof(msg.header); + msg.header.version = dev->fw_info->mailbox_version; msg.suballoc_dma = to_mcu_addr(dev, suballoc_dma); msg.suballoc_size = to_mcu_size(dev, suballoc_size); @@ -736,8 +800,7 @@ static void allegro_mcu_send_init(struct allegro_dev *dev, msg.l2_cache[1] = -1; msg.l2_cache[2] = -1; - allegro_mbox_write(dev, &dev->mbox_command, &msg, sizeof(msg)); - allegro_mcu_interrupt(dev); + allegro_mbox_send(dev->mbox_command, &msg); } static u32 v4l2_pixelformat_to_mcu_format(u32 pixelformat) @@ -768,15 +831,6 @@ static u32 v4l2_colorspace_to_mcu_colorspace(enum v4l2_colorspace colorspace) } } -static s8 v4l2_pixelformat_to_mcu_codec(u32 pixelformat) -{ - switch (pixelformat) { - case V4L2_PIX_FMT_H264: - default: - return 1; - } -} - static u8 v4l2_profile_to_mcu_profile(enum v4l2_mpeg_video_h264_profile profile) { switch (profile) { @@ -875,13 +929,23 @@ static int fill_create_channel_param(struct allegro_channel *channel, param->src_mode = 0x0; param->profile = v4l2_profile_to_mcu_profile(channel->profile); param->constraint_set_flags = BIT(1); - param->codec = v4l2_pixelformat_to_mcu_codec(channel->codec); + param->codec = channel->codec; param->level = v4l2_level_to_mcu_level(channel->level); param->tier = 0; - param->sps_param = BIT(20) | 0x4a; - param->pps_param = BIT(2); - param->enc_option = AL_OPT_RDO_COST_MODE | AL_OPT_LF_X_TILE | - AL_OPT_LF_X_SLICE | AL_OPT_LF; + + param->log2_max_poc = 10; + param->log2_max_frame_num = 4; + param->temporal_mvp_enable = 1; + + param->dbf_ovr_en = 1; + param->rdo_cost_mode = 1; + param->custom_lda = 1; + param->lf = 1; + param->lf_x_tile = 1; + param->lf_x_slice = 1; + + param->src_bit_depth = 8; + param->beta_offset = -1; param->tc_offset = -1; param->num_slices = 1; @@ -922,13 +986,26 @@ static int fill_create_channel_param(struct allegro_channel *channel, param->golden_ref_frequency = 10; param->rate_control_option = 0x00000000; - param->gop_ctrl_mode = 0x00000000; + param->num_pixel = channel->width + channel->height; + param->max_psnr = 4200; + param->max_pixel_value = 255; + + param->gop_ctrl_mode = 0x00000002; param->freq_idr = channel->gop_size; param->freq_lt = 0; param->gdr_mode = 0x00000000; param->gop_length = channel->gop_size; param->subframe_latency = 0x00000000; + param->lda_factors[0] = 51; + param->lda_factors[1] = 90; + param->lda_factors[2] = 151; + param->lda_factors[3] = 151; + param->lda_factors[4] = 151; + param->lda_factors[5] = 151; + + param->max_num_merge_cand = 5; + return 0; } @@ -936,18 +1013,28 @@ static int allegro_mcu_send_create_channel(struct allegro_dev *dev, struct allegro_channel *channel) { struct mcu_msg_create_channel msg; + struct allegro_buffer *blob = &channel->config_blob; + struct create_channel_param param; + size_t size; + + memset(¶m, 0, sizeof(param)); + fill_create_channel_param(channel, ¶m); + allegro_alloc_buffer(dev, blob, sizeof(struct create_channel_param)); + param.version = dev->fw_info->mailbox_version; + size = allegro_encode_config_blob(blob->vaddr, ¶m); memset(&msg, 0, sizeof(msg)); msg.header.type = MCU_MSG_TYPE_CREATE_CHANNEL; - msg.header.length = sizeof(msg) - sizeof(msg.header); + msg.header.version = dev->fw_info->mailbox_version; msg.user_id = channel->user_id; - fill_create_channel_param(channel, &msg.param); + msg.blob = blob->vaddr; + msg.blob_size = size; + msg.blob_mcu_addr = to_mcu_addr(dev, blob->paddr); - allegro_mbox_write(dev, &dev->mbox_command, &msg, sizeof(msg)); - allegro_mcu_interrupt(dev); + allegro_mbox_send(dev->mbox_command, &msg); return 0; } @@ -960,12 +1047,11 @@ static int allegro_mcu_send_destroy_channel(struct allegro_dev *dev, memset(&msg, 0, sizeof(msg)); msg.header.type = MCU_MSG_TYPE_DESTROY_CHANNEL; - msg.header.length = sizeof(msg) - sizeof(msg.header); + msg.header.version = dev->fw_info->mailbox_version; msg.channel_id = channel->mcu_channel_id; - allegro_mbox_write(dev, &dev->mbox_command, &msg, sizeof(msg)); - allegro_mcu_interrupt(dev); + allegro_mbox_send(dev->mbox_command, &msg); return 0; } @@ -981,7 +1067,7 @@ static int allegro_mcu_send_put_stream_buffer(struct allegro_dev *dev, memset(&msg, 0, sizeof(msg)); msg.header.type = MCU_MSG_TYPE_PUT_STREAM_BUFFER; - msg.header.length = sizeof(msg) - sizeof(msg.header); + msg.header.version = dev->fw_info->mailbox_version; msg.channel_id = channel->mcu_channel_id; msg.dma_addr = to_codec_addr(dev, paddr); @@ -991,8 +1077,7 @@ static int allegro_mcu_send_put_stream_buffer(struct allegro_dev *dev, /* copied to mcu_msg_encode_frame_response */ msg.stream_id = stream_id; - allegro_mbox_write(dev, &dev->mbox_command, &msg, sizeof(msg)); - allegro_mcu_interrupt(dev); + allegro_mbox_send(dev->mbox_command, &msg); return 0; } @@ -1007,7 +1092,7 @@ static int allegro_mcu_send_encode_frame(struct allegro_dev *dev, memset(&msg, 0, sizeof(msg)); msg.header.type = MCU_MSG_TYPE_ENCODE_FRAME; - msg.header.length = sizeof(msg) - sizeof(msg.header); + msg.header.version = dev->fw_info->mailbox_version; msg.channel_id = channel->mcu_channel_id; msg.encoding_options = AL_OPT_FORCE_LOAD; @@ -1021,8 +1106,7 @@ static int allegro_mcu_send_encode_frame(struct allegro_dev *dev, msg.ep2 = 0x0; msg.ep2_v = to_mcu_addr(dev, msg.ep2); - allegro_mbox_write(dev, &dev->mbox_command, &msg, sizeof(msg)); - allegro_mcu_interrupt(dev); + allegro_mbox_send(dev->mbox_command, &msg); return 0; } @@ -1072,9 +1156,11 @@ static int allegro_mcu_push_buffer_internal(struct allegro_channel *channel, if (!msg) return -ENOMEM; - msg->header.length = size - sizeof(msg->header); msg->header.type = type; + msg->header.version = dev->fw_info->mailbox_version; + msg->channel_id = channel->mcu_channel_id; + msg->num_buffers = num_buffers; buffer = msg->buffer; list_for_each_entry(al_buffer, list, head) { @@ -1084,12 +1170,8 @@ static int allegro_mcu_push_buffer_internal(struct allegro_channel *channel, buffer++; } - err = allegro_mbox_write(dev, &dev->mbox_command, msg, size); - if (err) - goto out; - allegro_mcu_interrupt(dev); + err = allegro_mbox_send(dev->mbox_command, msg); -out: kfree(msg); return err; } @@ -1289,8 +1371,8 @@ static ssize_t allegro_h264_write_pps(struct allegro_channel *channel, pps->entropy_coding_mode_flag = 0; pps->bottom_field_pic_order_in_frame_present_flag = 0; pps->num_slice_groups_minus1 = 0; - pps->num_ref_idx_l0_default_active_minus1 = 2; - pps->num_ref_idx_l1_default_active_minus1 = 2; + pps->num_ref_idx_l0_default_active_minus1 = channel->num_ref_idx_l0 - 1; + pps->num_ref_idx_l1_default_active_minus1 = channel->num_ref_idx_l1 - 1; pps->weighted_pred_flag = 0; pps->weighted_bipred_idc = 0; pps->pic_init_qp_minus26 = 0; @@ -1561,12 +1643,7 @@ allegro_handle_create_channel(struct allegro_dev *dev, { struct allegro_channel *channel; int err = 0; - - if (msg->header.length != sizeof(*msg) - sizeof(msg->header)) - v4l2_warn(&dev->v4l2_dev, - "received message has %d bytes, but expected %zu\n", - msg->header.length, - sizeof(*msg) - sizeof(msg->header)); + struct create_channel_param param; channel = allegro_find_channel_by_user_id(dev, msg->user_id); if (IS_ERR(channel)) { @@ -1592,6 +1669,14 @@ allegro_handle_create_channel(struct allegro_dev *dev, "user %d: channel has channel id %d\n", channel->user_id, channel->mcu_channel_id); + err = allegro_decode_config_blob(¶m, msg, channel->config_blob.vaddr); + allegro_free_buffer(channel->dev, &channel->config_blob); + if (err) + goto out; + + channel->num_ref_idx_l0 = param.num_ref_idx_l0; + channel->num_ref_idx_l1 = param.num_ref_idx_l1; + v4l2_dbg(1, debug, &dev->v4l2_dev, "channel %d: intermediate buffers: %d x %d bytes\n", channel->mcu_channel_id, @@ -1661,12 +1746,6 @@ allegro_handle_encode_frame(struct allegro_dev *dev, { struct allegro_channel *channel; - if (msg->header.length != sizeof(*msg) - sizeof(msg->header)) - v4l2_warn(&dev->v4l2_dev, - "received message has %d bytes, but expected %zu\n", - msg->header.length, - sizeof(*msg) - sizeof(msg->header)); - channel = allegro_find_channel_by_channel_id(dev, msg->channel_id); if (IS_ERR(channel)) { v4l2_err(&dev->v4l2_dev, @@ -1681,51 +1760,28 @@ allegro_handle_encode_frame(struct allegro_dev *dev, return 0; } -static int allegro_receive_message(struct allegro_dev *dev) +static void allegro_handle_message(struct allegro_dev *dev, + union mcu_msg_response *msg) { - union mcu_msg_response *msg; - ssize_t size; - int err = 0; - - msg = kmalloc(sizeof(*msg), GFP_KERNEL); - if (!msg) - return -ENOMEM; - - size = allegro_mbox_read(dev, &dev->mbox_status, msg, sizeof(*msg)); - if (size < sizeof(msg->header)) { - v4l2_err(&dev->v4l2_dev, - "invalid mbox message (%zd): must be at least %zu\n", - size, sizeof(msg->header)); - err = -EINVAL; - goto out; - } - switch (msg->header.type) { case MCU_MSG_TYPE_INIT: - err = allegro_handle_init(dev, &msg->init); + allegro_handle_init(dev, &msg->init); break; case MCU_MSG_TYPE_CREATE_CHANNEL: - err = allegro_handle_create_channel(dev, &msg->create_channel); + allegro_handle_create_channel(dev, &msg->create_channel); break; case MCU_MSG_TYPE_DESTROY_CHANNEL: - err = allegro_handle_destroy_channel(dev, - &msg->destroy_channel); + allegro_handle_destroy_channel(dev, &msg->destroy_channel); break; case MCU_MSG_TYPE_ENCODE_FRAME: - err = allegro_handle_encode_frame(dev, &msg->encode_frame); + allegro_handle_encode_frame(dev, &msg->encode_frame); break; default: v4l2_warn(&dev->v4l2_dev, "%s: unknown message %s\n", __func__, msg_type_name(msg->header.type)); - err = -EINVAL; break; } - -out: - kfree(msg); - - return err; } static irqreturn_t allegro_hardirq(int irq, void *data) @@ -1746,7 +1802,7 @@ static irqreturn_t allegro_irq_thread(int irq, void *data) { struct allegro_dev *dev = data; - allegro_receive_message(dev); + allegro_mbox_notify(dev->mbox_status); return IRQ_HANDLED; } @@ -1895,6 +1951,11 @@ static int allegro_mcu_reset(struct allegro_dev *dev) return allegro_mcu_wait_for_sleep(dev); } +static void allegro_mcu_interrupt(struct allegro_dev *dev) +{ + regmap_write(dev->regmap, AL5_MCU_INTERRUPT, BIT(0)); +} + static void allegro_destroy_channel(struct allegro_channel *channel) { struct allegro_dev *dev = channel->dev; @@ -2887,10 +2948,15 @@ static int allegro_mcu_hw_init(struct allegro_dev *dev, { int err; - allegro_mbox_init(dev, &dev->mbox_command, - info->mailbox_cmd, info->mailbox_size); - allegro_mbox_init(dev, &dev->mbox_status, - info->mailbox_status, info->mailbox_size); + dev->mbox_command = allegro_mbox_init(dev, info->mailbox_cmd, + info->mailbox_size); + dev->mbox_status = allegro_mbox_init(dev, info->mailbox_status, + info->mailbox_size); + if (IS_ERR(dev->mbox_command) || IS_ERR(dev->mbox_status)) { + v4l2_err(&dev->v4l2_dev, + "failed to initialize mailboxes\n"); + return -EIO; + } allegro_mcu_enable_interrupts(dev); @@ -2960,7 +3026,6 @@ static void allegro_fw_callback(const struct firmware *fw, void *context) const char *fw_codec_name = "al5e.fw"; const struct firmware *fw_codec; int err; - const struct fw_info *info; if (!fw) return; @@ -2971,14 +3036,14 @@ static void allegro_fw_callback(const struct firmware *fw, void *context) if (err) goto err_release_firmware; - info = allegro_get_firmware_info(dev, fw, fw_codec); - if (!info) { + dev->fw_info = allegro_get_firmware_info(dev, fw, fw_codec); + if (!dev->fw_info) { v4l2_err(&dev->v4l2_dev, "firmware is not supported\n"); goto err_release_firmware_codec; } v4l2_info(&dev->v4l2_dev, - "using mcu firmware version '%s'\n", info->version); + "using mcu firmware version '%s'\n", dev->fw_info->version); /* Ensure that the mcu is sleeping at the reset vector */ err = allegro_mcu_reset(dev); @@ -2990,7 +3055,7 @@ static void allegro_fw_callback(const struct firmware *fw, void *context) allegro_copy_firmware(dev, fw->data, fw->size); allegro_copy_fw_codec(dev, fw_codec->data, fw_codec->size); - err = allegro_mcu_hw_init(dev, info); + err = allegro_mcu_hw_init(dev, dev->fw_info); if (err) { v4l2_err(&dev->v4l2_dev, "failed to initialize mcu\n"); goto err_free_fw_codec; @@ -3065,9 +3130,9 @@ static int allegro_probe(struct platform_device *pdev) return -EINVAL; } regs = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (IS_ERR(regs)) { + if (!regs) { dev_err(&pdev->dev, "failed to map registers\n"); - return PTR_ERR(regs); + return -ENOMEM; } dev->regmap = devm_regmap_init_mmio(&pdev->dev, regs, &allegro_regmap_config); @@ -3085,9 +3150,9 @@ static int allegro_probe(struct platform_device *pdev) sram_regs = devm_ioremap(&pdev->dev, sram_res->start, resource_size(sram_res)); - if (IS_ERR(sram_regs)) { + if (!sram_regs) { dev_err(&pdev->dev, "failed to map sram\n"); - return PTR_ERR(sram_regs); + return -ENOMEM; } dev->sram = devm_regmap_init_mmio(&pdev->dev, sram_regs, &allegro_sram_config); diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.c b/drivers/staging/media/allegro-dvt/allegro-mail.c index df0d8d26a6fb..9286d2162377 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.c +++ b/drivers/staging/media/allegro-dvt/allegro-mail.c @@ -6,7 +6,11 @@ * Allegro VCU firmware. */ +#include <linux/bitfield.h> #include <linux/export.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/videodev2.h> #include "allegro-mail.h" @@ -35,3 +39,505 @@ const char *msg_type_name(enum mcu_msg_type type) } } EXPORT_SYMBOL(msg_type_name); + +static ssize_t +allegro_enc_init(u32 *dst, struct mcu_msg_init_request *msg) +{ + unsigned int i = 0; + enum mcu_msg_version version = msg->header.version; + + dst[i++] = msg->reserved0; + dst[i++] = msg->suballoc_dma; + dst[i++] = msg->suballoc_size; + dst[i++] = msg->l2_cache[0]; + dst[i++] = msg->l2_cache[1]; + dst[i++] = msg->l2_cache[2]; + if (version >= MCU_MSG_VERSION_2019_2) { + dst[i++] = -1; + dst[i++] = 0; + } + + return i * sizeof(*dst); +} + +static inline u32 settings_get_mcu_codec(struct create_channel_param *param) +{ + enum mcu_msg_version version = param->version; + u32 pixelformat = param->codec; + + if (version < MCU_MSG_VERSION_2019_2) { + switch (pixelformat) { + case V4L2_PIX_FMT_H264: + default: + return 1; + } + } else { + switch (pixelformat) { + case V4L2_PIX_FMT_H264: + default: + return 0; + } + } +} + +ssize_t +allegro_encode_config_blob(u32 *dst, struct create_channel_param *param) +{ + enum mcu_msg_version version = param->version; + unsigned int i = 0; + unsigned int j = 0; + u32 val; + unsigned int codec = settings_get_mcu_codec(param); + + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->layer_id; + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->height) | + FIELD_PREP(GENMASK(15, 0), param->width); + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->videomode; + dst[i++] = param->format; + if (version < MCU_MSG_VERSION_2019_2) + dst[i++] = param->colorspace; + dst[i++] = param->src_mode; + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->src_bit_depth; + dst[i++] = FIELD_PREP(GENMASK(31, 24), codec) | + FIELD_PREP(GENMASK(23, 8), param->constraint_set_flags) | + FIELD_PREP(GENMASK(7, 0), param->profile); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->tier) | + FIELD_PREP(GENMASK(15, 0), param->level); + + val = 0; + val |= param->temporal_mvp_enable ? BIT(20) : 0; + val |= FIELD_PREP(GENMASK(7, 4), param->log2_max_frame_num) | + FIELD_PREP(GENMASK(3, 0), param->log2_max_poc); + dst[i++] = val; + + val = 0; + val |= param->dbf_ovr_en ? BIT(2) : 0; + dst[i++] = val; + + if (version >= MCU_MSG_VERSION_2019_2) { + val = 0; + val |= param->custom_lda ? BIT(2) : 0; + val |= param->rdo_cost_mode ? BIT(20) : 0; + dst[i++] = val; + + val = 0; + val |= param->lf ? BIT(2) : 0; + val |= param->lf_x_tile ? BIT(3) : 0; + val |= param->lf_x_slice ? BIT(4) : 0; + dst[i++] = val; + } else { + val = 0; + dst[i++] = val; + } + + dst[i++] = FIELD_PREP(GENMASK(15, 8), param->beta_offset) | + FIELD_PREP(GENMASK(7, 0), param->tc_offset); + dst[i++] = param->unknown11; + dst[i++] = param->unknown12; + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->num_slices; + else + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->prefetch_auto) | + FIELD_PREP(GENMASK(15, 0), param->num_slices); + dst[i++] = param->prefetch_mem_offset; + dst[i++] = param->prefetch_mem_size; + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->clip_vrt_range) | + FIELD_PREP(GENMASK(15, 0), param->clip_hrz_range); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->me_range[1]) | + FIELD_PREP(GENMASK(15, 0), param->me_range[0]); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->me_range[3]) | + FIELD_PREP(GENMASK(15, 0), param->me_range[2]); + dst[i++] = FIELD_PREP(GENMASK(31, 24), param->min_tu_size) | + FIELD_PREP(GENMASK(23, 16), param->max_tu_size) | + FIELD_PREP(GENMASK(15, 8), param->min_cu_size) | + FIELD_PREP(GENMASK(8, 0), param->max_cu_size); + dst[i++] = FIELD_PREP(GENMASK(15, 8), param->max_transfo_depth_intra) | + FIELD_PREP(GENMASK(7, 0), param->max_transfo_depth_inter); + dst[i++] = param->entropy_mode; + dst[i++] = param->wp_mode; + + dst[i++] = param->rate_control_mode; + dst[i++] = param->initial_rem_delay; + dst[i++] = param->cpb_size; + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->clk_ratio) | + FIELD_PREP(GENMASK(15, 0), param->framerate); + dst[i++] = param->target_bitrate; + dst[i++] = param->max_bitrate; + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->min_qp) | + FIELD_PREP(GENMASK(15, 0), param->initial_qp); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->ip_delta) | + FIELD_PREP(GENMASK(15, 0), param->max_qp); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->golden_ref) | + FIELD_PREP(GENMASK(15, 0), param->pb_delta); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->golden_ref_frequency) | + FIELD_PREP(GENMASK(15, 0), param->golden_delta); + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->rate_control_option; + else + dst[i++] = 0; + + if (version >= MCU_MSG_VERSION_2019_2) { + dst[i++] = param->num_pixel; + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->max_pixel_value) | + FIELD_PREP(GENMASK(15, 0), param->max_psnr); + for (j = 0; j < 3; j++) + dst[i++] = param->maxpicturesize[j]; + } + + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->gop_ctrl_mode; + else + dst[i++] = 0; + + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = FIELD_PREP(GENMASK(31, 24), param->freq_golden_ref) | + FIELD_PREP(GENMASK(23, 16), param->num_b) | + FIELD_PREP(GENMASK(15, 0), param->gop_length); + dst[i++] = param->freq_idr; + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->enable_lt; + dst[i++] = param->freq_lt; + dst[i++] = param->gdr_mode; + if (version < MCU_MSG_VERSION_2019_2) + dst[i++] = FIELD_PREP(GENMASK(31, 24), param->freq_golden_ref) | + FIELD_PREP(GENMASK(23, 16), param->num_b) | + FIELD_PREP(GENMASK(15, 0), param->gop_length); + + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->tmpdqp; + + dst[i++] = param->subframe_latency; + dst[i++] = param->lda_control_mode; + if (version < MCU_MSG_VERSION_2019_2) + dst[i++] = param->unknown41; + + if (version >= MCU_MSG_VERSION_2019_2) { + for (j = 0; j < 6; j++) + dst[i++] = param->lda_factors[j]; + dst[i++] = param->max_num_merge_cand; + } + + return i * sizeof(*dst); +} + +static ssize_t +allegro_enc_create_channel(u32 *dst, struct mcu_msg_create_channel *msg) +{ + enum mcu_msg_version version = msg->header.version; + unsigned int i = 0; + + dst[i++] = msg->user_id; + + if (version >= MCU_MSG_VERSION_2019_2) { + dst[i++] = msg->blob_mcu_addr; + } else { + memcpy(&dst[i], msg->blob, msg->blob_size); + i += msg->blob_size / sizeof(*dst); + } + + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = msg->ep1_addr; + + return i * sizeof(*dst); +} + +ssize_t allegro_decode_config_blob(struct create_channel_param *param, + struct mcu_msg_create_channel_response *msg, + u32 *src) +{ + enum mcu_msg_version version = msg->header.version; + + if (version >= MCU_MSG_VERSION_2019_2) { + param->num_ref_idx_l0 = FIELD_GET(GENMASK(7, 4), src[9]); + param->num_ref_idx_l1 = FIELD_GET(GENMASK(11, 8), src[9]); + } else { + param->num_ref_idx_l0 = msg->num_ref_idx_l0; + param->num_ref_idx_l1 = msg->num_ref_idx_l1; + } + + return 0; +} + +static ssize_t +allegro_enc_destroy_channel(u32 *dst, struct mcu_msg_destroy_channel *msg) +{ + unsigned int i = 0; + + dst[i++] = msg->channel_id; + + return i * sizeof(*dst); +} + +static ssize_t +allegro_enc_push_buffers(u32 *dst, struct mcu_msg_push_buffers_internal *msg) +{ + unsigned int i = 0; + struct mcu_msg_push_buffers_internal_buffer *buffer; + unsigned int num_buffers = msg->num_buffers; + unsigned int j; + + dst[i++] = msg->channel_id; + + for (j = 0; j < num_buffers; j++) { + buffer = &msg->buffer[j]; + dst[i++] = buffer->dma_addr; + dst[i++] = buffer->mcu_addr; + dst[i++] = buffer->size; + } + + return i * sizeof(*dst); +} + +static ssize_t +allegro_enc_put_stream_buffer(u32 *dst, + struct mcu_msg_put_stream_buffer *msg) +{ + unsigned int i = 0; + + dst[i++] = msg->channel_id; + dst[i++] = msg->dma_addr; + dst[i++] = msg->mcu_addr; + dst[i++] = msg->size; + dst[i++] = msg->offset; + dst[i++] = lower_32_bits(msg->stream_id); + dst[i++] = upper_32_bits(msg->stream_id); + + return i * sizeof(*dst); +} + +static ssize_t +allegro_enc_encode_frame(u32 *dst, struct mcu_msg_encode_frame *msg) +{ + enum mcu_msg_version version = msg->header.version; + unsigned int i = 0; + + dst[i++] = msg->channel_id; + + dst[i++] = msg->reserved; + dst[i++] = msg->encoding_options; + dst[i++] = FIELD_PREP(GENMASK(31, 16), msg->padding) | + FIELD_PREP(GENMASK(15, 0), msg->pps_qp); + + if (version >= MCU_MSG_VERSION_2019_2) { + dst[i++] = 0; + dst[i++] = 0; + dst[i++] = 0; + dst[i++] = 0; + } + + dst[i++] = lower_32_bits(msg->user_param); + dst[i++] = upper_32_bits(msg->user_param); + dst[i++] = lower_32_bits(msg->src_handle); + dst[i++] = upper_32_bits(msg->src_handle); + dst[i++] = msg->request_options; + dst[i++] = msg->src_y; + dst[i++] = msg->src_uv; + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = msg->is_10_bit; + dst[i++] = msg->stride; + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = msg->format; + dst[i++] = msg->ep2; + dst[i++] = lower_32_bits(msg->ep2_v); + dst[i++] = upper_32_bits(msg->ep2_v); + + return i * sizeof(*dst); +} + +static ssize_t +allegro_dec_init(struct mcu_msg_init_response *msg, u32 *src) +{ + unsigned int i = 0; + + msg->reserved0 = src[i++]; + + return i * sizeof(*src); +} + +static ssize_t +allegro_dec_create_channel(struct mcu_msg_create_channel_response *msg, + u32 *src) +{ + enum mcu_msg_version version = msg->header.version; + unsigned int i = 0; + + msg->channel_id = src[i++]; + msg->user_id = src[i++]; + /* + * Version >= MCU_MSG_VERSION_2019_2 is handled in + * allegro_decode_config_blob(). + */ + if (version < MCU_MSG_VERSION_2019_2) { + msg->options = src[i++]; + msg->num_core = src[i++]; + msg->num_ref_idx_l0 = FIELD_GET(GENMASK(7, 4), src[i]); + msg->num_ref_idx_l1 = FIELD_GET(GENMASK(11, 8), src[i++]); + } + msg->int_buffers_count = src[i++]; + msg->int_buffers_size = src[i++]; + msg->rec_buffers_count = src[i++]; + msg->rec_buffers_size = src[i++]; + msg->reserved = src[i++]; + msg->error_code = src[i++]; + + return i * sizeof(*src); +} + +static ssize_t +allegro_dec_destroy_channel(struct mcu_msg_destroy_channel_response *msg, + u32 *src) +{ + unsigned int i = 0; + + msg->channel_id = src[i++]; + + return i * sizeof(*src); +} + +static ssize_t +allegro_dec_encode_frame(struct mcu_msg_encode_frame_response *msg, u32 *src) +{ + enum mcu_msg_version version = msg->header.version; + unsigned int i = 0; + unsigned int j; + + msg->channel_id = src[i++]; + + msg->stream_id = src[i++]; + msg->stream_id |= (((u64)src[i++]) << 32); + msg->user_param = src[i++]; + msg->user_param |= (((u64)src[i++]) << 32); + msg->src_handle = src[i++]; + msg->src_handle |= (((u64)src[i++]) << 32); + msg->skip = FIELD_GET(GENMASK(31, 16), src[i]); + msg->is_ref = FIELD_GET(GENMASK(15, 0), src[i++]); + msg->initial_removal_delay = src[i++]; + msg->dpb_output_delay = src[i++]; + msg->size = src[i++]; + msg->frame_tag_size = src[i++]; + msg->stuffing = src[i++]; + msg->filler = src[i++]; + msg->num_column = FIELD_GET(GENMASK(31, 16), src[i]); + msg->num_row = FIELD_GET(GENMASK(15, 0), src[i++]); + msg->num_ref_idx_l1 = FIELD_GET(GENMASK(31, 24), src[i]); + msg->num_ref_idx_l0 = FIELD_GET(GENMASK(23, 16), src[i]); + msg->qp = FIELD_GET(GENMASK(15, 0), src[i++]); + msg->partition_table_offset = src[i++]; + msg->partition_table_size = src[i++]; + msg->sum_complex = src[i++]; + for (j = 0; j < 4; j++) + msg->tile_width[j] = src[i++]; + for (j = 0; j < 22; j++) + msg->tile_height[j] = src[i++]; + msg->error_code = src[i++]; + msg->slice_type = src[i++]; + msg->pic_struct = src[i++]; + msg->reserved = FIELD_GET(GENMASK(31, 24), src[i]); + msg->is_last_slice = FIELD_GET(GENMASK(23, 16), src[i]); + msg->is_first_slice = FIELD_GET(GENMASK(15, 8), src[i]); + msg->is_idr = FIELD_GET(GENMASK(7, 0), src[i++]); + + msg->reserved1 = FIELD_GET(GENMASK(31, 16), src[i]); + msg->pps_qp = FIELD_GET(GENMASK(15, 0), src[i++]); + + msg->reserved2 = src[i++]; + if (version >= MCU_MSG_VERSION_2019_2) { + msg->reserved3 = src[i++]; + msg->reserved4 = src[i++]; + msg->reserved5 = src[i++]; + msg->reserved6 = src[i++]; + } + + return i * sizeof(*src); +} + +/** + * allegro_encode_mail() - Encode allegro messages to firmware format + * @dst: Pointer to the memory that will be filled with data + * @msg: The allegro message that will be encoded + */ +ssize_t allegro_encode_mail(u32 *dst, void *msg) +{ + const struct mcu_msg_header *header = msg; + ssize_t size; + + if (!msg || !dst) + return -EINVAL; + + switch (header->type) { + case MCU_MSG_TYPE_INIT: + size = allegro_enc_init(&dst[1], msg); + break; + case MCU_MSG_TYPE_CREATE_CHANNEL: + size = allegro_enc_create_channel(&dst[1], msg); + break; + case MCU_MSG_TYPE_DESTROY_CHANNEL: + size = allegro_enc_destroy_channel(&dst[1], msg); + break; + case MCU_MSG_TYPE_ENCODE_FRAME: + size = allegro_enc_encode_frame(&dst[1], msg); + break; + case MCU_MSG_TYPE_PUT_STREAM_BUFFER: + size = allegro_enc_put_stream_buffer(&dst[1], msg); + break; + case MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE: + case MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE: + size = allegro_enc_push_buffers(&dst[1], msg); + break; + default: + return -EINVAL; + } + + /* + * The encoded messages might have different length depending on + * the firmware version or certain fields. Therefore, we have to + * set the body length after encoding the message. + */ + dst[0] = FIELD_PREP(GENMASK(31, 16), header->type) | + FIELD_PREP(GENMASK(15, 0), size); + + return size + sizeof(*dst); +} + +/** + * allegro_decode_mail() - Parse allegro messages from the firmware. + * @msg: The mcu_msg_response that will be filled with parsed values. + * @src: Pointer to the memory that will be parsed + * + * The message format in the mailbox depends on the firmware. Parse the + * different formats into a uniform message format that can be used without + * taking care of the firmware version. + */ +int allegro_decode_mail(void *msg, u32 *src) +{ + struct mcu_msg_header *header; + + if (!src || !msg) + return -EINVAL; + + header = msg; + header->type = FIELD_GET(GENMASK(31, 16), src[0]); + + src++; + switch (header->type) { + case MCU_MSG_TYPE_INIT: + allegro_dec_init(msg, src); + break; + case MCU_MSG_TYPE_CREATE_CHANNEL: + allegro_dec_create_channel(msg, src); + break; + case MCU_MSG_TYPE_DESTROY_CHANNEL: + allegro_dec_destroy_channel(msg, src); + break; + case MCU_MSG_TYPE_ENCODE_FRAME: + allegro_dec_encode_frame(msg, src); + break; + default: + return -EINVAL; + } + + return 0; +} diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.h b/drivers/staging/media/allegro-dvt/allegro-mail.h index 17db665f8e1e..486ecb12b098 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.h +++ b/drivers/staging/media/allegro-dvt/allegro-mail.h @@ -20,12 +20,17 @@ enum mcu_msg_type { MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE = 0x000f, }; +enum mcu_msg_version { + MCU_MSG_VERSION_2018_2, + MCU_MSG_VERSION_2019_2, +}; + const char *msg_type_name(enum mcu_msg_type type); struct mcu_msg_header { - u16 length; /* length of the body in bytes */ - u16 type; -} __attribute__ ((__packed__)); + enum mcu_msg_type type; + enum mcu_msg_version version; +}; struct mcu_msg_init_request { struct mcu_msg_header header; @@ -33,48 +38,40 @@ struct mcu_msg_init_request { u32 suballoc_dma; u32 suballoc_size; s32 l2_cache[3]; -} __attribute__ ((__packed__)); +}; struct mcu_msg_init_response { struct mcu_msg_header header; u32 reserved0; -} __attribute__ ((__packed__)); +}; struct create_channel_param { + enum mcu_msg_version version; + u32 layer_id; u16 width; u16 height; + u32 videomode; u32 format; u32 colorspace; u32 src_mode; + u32 src_bit_depth; u8 profile; u16 constraint_set_flags; - s8 codec; + u32 codec; u16 level; u16 tier; - u32 sps_param; - u32 pps_param; - - u32 enc_option; -#define AL_OPT_WPP BIT(0) -#define AL_OPT_TILE BIT(1) -#define AL_OPT_LF BIT(2) -#define AL_OPT_LF_X_SLICE BIT(3) -#define AL_OPT_LF_X_TILE BIT(4) -#define AL_OPT_SCL_LST BIT(5) -#define AL_OPT_CONST_INTRA_PRED BIT(6) -#define AL_OPT_QP_TAB_RELATIVE BIT(7) -#define AL_OPT_FIX_PREDICTOR BIT(8) -#define AL_OPT_CUSTOM_LDA BIT(9) -#define AL_OPT_ENABLE_AUTO_QP BIT(10) -#define AL_OPT_ADAPT_AUTO_QP BIT(11) -#define AL_OPT_TRANSFO_SKIP BIT(13) -#define AL_OPT_FORCE_REC BIT(15) -#define AL_OPT_FORCE_MV_OUT BIT(16) -#define AL_OPT_FORCE_MV_CLIP BIT(17) -#define AL_OPT_LOWLAT_SYNC BIT(18) -#define AL_OPT_LOWLAT_INT BIT(19) -#define AL_OPT_RDO_COST_MODE BIT(20) - + u32 log2_max_poc; + u32 log2_max_frame_num; + u32 temporal_mvp_enable; + u32 enable_reordering; + u32 dbf_ovr_en; + u32 num_ref_idx_l0; + u32 num_ref_idx_l1; + u32 custom_lda; + u32 rdo_cost_mode; + u32 lf; + u32 lf_x_tile; + u32 lf_x_slice; s8 beta_offset; s8 tc_offset; u16 reserved10; @@ -114,6 +111,10 @@ struct create_channel_param { u16 golden_delta; u16 golden_ref_frequency; u32 rate_control_option; + u32 num_pixel; + u16 max_psnr; + u16 max_pixel_value; + u32 maxpicturesize[3]; /* gop param */ u32 gop_ctrl_mode; @@ -123,17 +124,26 @@ struct create_channel_param { u16 gop_length; u8 num_b; u8 freq_golden_ref; + u32 enable_lt; + u32 tmpdqp; u32 subframe_latency; u32 lda_control_mode; u32 unknown41; -} __attribute__ ((__packed__)); + + u32 lda_factors[6]; + + u32 max_num_merge_cand; +}; struct mcu_msg_create_channel { struct mcu_msg_header header; u32 user_id; - struct create_channel_param param; -} __attribute__ ((__packed__)); + u32 *blob; + size_t blob_size; + u32 blob_mcu_addr; + u32 ep1_addr; +}; struct mcu_msg_create_channel_response { struct mcu_msg_header header; @@ -141,36 +151,38 @@ struct mcu_msg_create_channel_response { u32 user_id; u32 options; u32 num_core; - u32 pps_param; + u32 num_ref_idx_l0; + u32 num_ref_idx_l1; u32 int_buffers_count; u32 int_buffers_size; u32 rec_buffers_count; u32 rec_buffers_size; u32 reserved; u32 error_code; -} __attribute__ ((__packed__)); +}; struct mcu_msg_destroy_channel { struct mcu_msg_header header; u32 channel_id; -} __attribute__ ((__packed__)); +}; struct mcu_msg_destroy_channel_response { struct mcu_msg_header header; u32 channel_id; -} __attribute__ ((__packed__)); +}; struct mcu_msg_push_buffers_internal_buffer { u32 dma_addr; u32 mcu_addr; u32 size; -} __attribute__ ((__packed__)); +}; struct mcu_msg_push_buffers_internal { struct mcu_msg_header header; u32 channel_id; + size_t num_buffers; struct mcu_msg_push_buffers_internal_buffer buffer[]; -} __attribute__ ((__packed__)); +}; struct mcu_msg_put_stream_buffer { struct mcu_msg_header header; @@ -180,7 +192,7 @@ struct mcu_msg_put_stream_buffer { u32 size; u32 offset; u64 stream_id; -} __attribute__ ((__packed__)); +}; struct mcu_msg_encode_frame { struct mcu_msg_header header; @@ -208,12 +220,15 @@ struct mcu_msg_encode_frame { /* u32 scene_change_delay (optional) */ /* rate control param (optional) */ /* gop param (optional) */ + /* dynamic resolution params (optional) */ u32 src_y; u32 src_uv; + u32 is_10_bit; u32 stride; + u32 format; u32 ep2; u64 ep2_v; -} __attribute__ ((__packed__)); +}; struct mcu_msg_encode_frame_response { struct mcu_msg_header header; @@ -254,7 +269,11 @@ struct mcu_msg_encode_frame_response { u16 pps_qp; u16 reserved1; u32 reserved2; -} __attribute__ ((__packed__)); + u32 reserved3; + u32 reserved4; + u32 reserved5; + u32 reserved6; +}; union mcu_msg_response { struct mcu_msg_header header; @@ -264,4 +283,12 @@ union mcu_msg_response { struct mcu_msg_encode_frame_response encode_frame; }; +ssize_t allegro_encode_config_blob(u32 *dst, struct create_channel_param *param); +ssize_t allegro_decode_config_blob(struct create_channel_param *param, + struct mcu_msg_create_channel_response *msg, + u32 *src); + +int allegro_decode_mail(void *msg, u32 *src); +ssize_t allegro_encode_mail(u32 *dst, void *msg); + #endif diff --git a/drivers/staging/media/hantro/hantro.h b/drivers/staging/media/hantro/hantro.h index 3005207fc6fb..65f9f7ea7dcf 100644 --- a/drivers/staging/media/hantro/hantro.h +++ b/drivers/staging/media/hantro/hantro.h @@ -199,6 +199,7 @@ struct hantro_dev { * * @dev: VPU driver data to which the context belongs. * @fh: V4L2 file handler. + * @is_encoder: Decoder or encoder context? * * @sequence_cap: Sequence counter for capture queue * @sequence_out: Sequence counter for output queue @@ -211,9 +212,6 @@ struct hantro_dev { * @ctrl_handler: Control handler used to register controls. * @jpeg_quality: User-specified JPEG compression quality. * - * @buf_finish: Buffer finish. This depends on encoder or decoder - * context, and it's called right before - * calling v4l2_m2m_job_finish. * @codec_ops: Set of operations related to codec mode. * @postproc: Post-processing context. * @jpeg_enc: JPEG-encoding context. @@ -223,6 +221,7 @@ struct hantro_dev { struct hantro_ctx { struct hantro_dev *dev; struct v4l2_fh fh; + bool is_encoder; u32 sequence_cap; u32 sequence_out; @@ -235,10 +234,6 @@ struct hantro_ctx { struct v4l2_ctrl_handler ctrl_handler; int jpeg_quality; - int (*buf_finish)(struct hantro_ctx *ctx, - struct vb2_buffer *buf, - unsigned int bytesused); - const struct hantro_codec_ops *codec_ops; struct hantro_postproc_ctx postproc; @@ -399,8 +394,6 @@ static inline void hantro_reg_write_s(struct hantro_dev *vpu, vdpu_write(vpu, vdpu_read_mask(vpu, reg, val), reg->base); } -bool hantro_is_encoder_ctx(const struct hantro_ctx *ctx); - void *hantro_get_ctrl(struct hantro_ctx *ctx, u32 id); dma_addr_t hantro_get_ref(struct hantro_ctx *ctx, u64 ts); @@ -420,7 +413,7 @@ static inline bool hantro_needs_postproc(const struct hantro_ctx *ctx, const struct hantro_fmt *fmt) { - return !hantro_is_encoder_ctx(ctx) && fmt->fourcc != V4L2_PIX_FMT_NV12; + return !ctx->is_encoder && fmt->fourcc != V4L2_PIX_FMT_NV12; } static inline dma_addr_t diff --git a/drivers/staging/media/hantro/hantro_drv.c b/drivers/staging/media/hantro/hantro_drv.c index 0db8ad455160..34797507f214 100644 --- a/drivers/staging/media/hantro/hantro_drv.c +++ b/drivers/staging/media/hantro/hantro_drv.c @@ -56,37 +56,11 @@ dma_addr_t hantro_get_ref(struct hantro_ctx *ctx, u64 ts) return hantro_get_dec_buf_addr(ctx, buf); } -static int -hantro_enc_buf_finish(struct hantro_ctx *ctx, struct vb2_buffer *buf, - unsigned int bytesused) -{ - size_t avail_size; - - avail_size = vb2_plane_size(buf, 0) - ctx->vpu_dst_fmt->header_size; - if (bytesused > avail_size) - return -EINVAL; - /* - * The bounce buffer is only for the JPEG encoder. - * TODO: Rework the JPEG encoder to eliminate the need - * for a bounce buffer. - */ - if (ctx->jpeg_enc.bounce_buffer.cpu) { - memcpy(vb2_plane_vaddr(buf, 0) + - ctx->vpu_dst_fmt->header_size, - ctx->jpeg_enc.bounce_buffer.cpu, bytesused); - } - buf->planes[0].bytesused = - ctx->vpu_dst_fmt->header_size + bytesused; - return 0; -} - static void hantro_job_finish(struct hantro_dev *vpu, struct hantro_ctx *ctx, - unsigned int bytesused, enum vb2_buffer_state result) { struct vb2_v4l2_buffer *src, *dst; - int ret; pm_runtime_mark_last_busy(vpu->dev); pm_runtime_put_autosuspend(vpu->dev); @@ -103,17 +77,11 @@ static void hantro_job_finish(struct hantro_dev *vpu, src->sequence = ctx->sequence_out++; dst->sequence = ctx->sequence_cap++; - if (ctx->buf_finish) { - ret = ctx->buf_finish(ctx, &dst->vb2_buf, bytesused); - if (ret) - result = VB2_BUF_STATE_ERROR; - } - v4l2_m2m_buf_done_and_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx, result); } -void hantro_irq_done(struct hantro_dev *vpu, unsigned int bytesused, +void hantro_irq_done(struct hantro_dev *vpu, enum vb2_buffer_state result) { struct hantro_ctx *ctx = @@ -124,8 +92,11 @@ void hantro_irq_done(struct hantro_dev *vpu, unsigned int bytesused, * the timeout expired. The watchdog is running, * and will take care of finishing the job. */ - if (cancel_delayed_work(&vpu->watchdog_work)) - hantro_job_finish(vpu, ctx, bytesused, result); + if (cancel_delayed_work(&vpu->watchdog_work)) { + if (result == VB2_BUF_STATE_DONE && ctx->codec_ops->done) + ctx->codec_ops->done(ctx); + hantro_job_finish(vpu, ctx, result); + } } void hantro_watchdog(struct work_struct *work) @@ -139,7 +110,7 @@ void hantro_watchdog(struct work_struct *work) if (ctx) { vpu_err("frame processing timed out!\n"); ctx->codec_ops->reset(ctx); - hantro_job_finish(vpu, ctx, 0, VB2_BUF_STATE_ERROR); + hantro_job_finish(vpu, ctx, VB2_BUF_STATE_ERROR); } } @@ -151,10 +122,12 @@ void hantro_start_prepare_run(struct hantro_ctx *ctx) v4l2_ctrl_request_setup(src_buf->vb2_buf.req_obj.req, &ctx->ctrl_handler); - if (hantro_needs_postproc(ctx, ctx->vpu_dst_fmt)) - hantro_postproc_enable(ctx); - else - hantro_postproc_disable(ctx); + if (!ctx->is_encoder) { + if (hantro_needs_postproc(ctx, ctx->vpu_dst_fmt)) + hantro_postproc_enable(ctx); + else + hantro_postproc_disable(ctx); + } } void hantro_end_prepare_run(struct hantro_ctx *ctx) @@ -192,12 +165,7 @@ static void device_run(void *priv) return; err_cancel_job: - hantro_job_finish(ctx->dev, ctx, 0, VB2_BUF_STATE_ERROR); -} - -bool hantro_is_encoder_ctx(const struct hantro_ctx *ctx) -{ - return ctx->buf_finish == hantro_enc_buf_finish; + hantro_job_finish(ctx->dev, ctx, VB2_BUF_STATE_ERROR); } static struct v4l2_m2m_ops vpu_m2m_ops = { @@ -240,7 +208,7 @@ queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) * * For the DMA destination buffer, we use a bounce buffer. */ - if (hantro_is_encoder_ctx(ctx)) { + if (ctx->is_encoder) { dst_vq->mem_ops = &vb2_vmalloc_memops; } else { dst_vq->bidirectional = true; @@ -261,7 +229,25 @@ queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) return vb2_queue_init(dst_vq); } -static int hantro_s_ctrl(struct v4l2_ctrl *ctrl) +static int hantro_try_ctrl(struct v4l2_ctrl *ctrl) +{ + if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_SPS) { + const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p_h264_sps; + + if (sps->chroma_format_idc > 1) + /* Only 4:0:0 and 4:2:0 are supported */ + return -EINVAL; + if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8) + /* Luma and chroma bit depth mismatch */ + return -EINVAL; + if (sps->bit_depth_luma_minus8 != 0) + /* Only 8-bit is supported */ + return -EINVAL; + } + return 0; +} + +static int hantro_jpeg_s_ctrl(struct v4l2_ctrl *ctrl) { struct hantro_ctx *ctx; @@ -282,7 +268,11 @@ static int hantro_s_ctrl(struct v4l2_ctrl *ctrl) } static const struct v4l2_ctrl_ops hantro_ctrl_ops = { - .s_ctrl = hantro_s_ctrl, + .try_ctrl = hantro_try_ctrl, +}; + +static const struct v4l2_ctrl_ops hantro_jpeg_ctrl_ops = { + .s_ctrl = hantro_jpeg_s_ctrl, }; static const struct hantro_ctrl controls[] = { @@ -294,7 +284,7 @@ static const struct hantro_ctrl controls[] = { .max = 100, .step = 1, .def = 50, - .ops = &hantro_ctrl_ops, + .ops = &hantro_jpeg_ctrl_ops, }, }, { .codec = HANTRO_MPEG2_DECODER, @@ -325,6 +315,7 @@ static const struct hantro_ctrl controls[] = { .codec = HANTRO_H264_DECODER, .cfg = { .id = V4L2_CID_MPEG_VIDEO_H264_SPS, + .ops = &hantro_ctrl_ops, }, }, { .codec = HANTRO_H264_DECODER, @@ -419,9 +410,10 @@ static int hantro_open(struct file *filp) ctx->dev = vpu; if (func->id == MEDIA_ENT_F_PROC_VIDEO_ENCODER) { allowed_codecs = vpu->variant->codec & HANTRO_ENCODERS; - ctx->buf_finish = hantro_enc_buf_finish; + ctx->is_encoder = true; } else if (func->id == MEDIA_ENT_F_PROC_VIDEO_DECODER) { allowed_codecs = vpu->variant->codec & HANTRO_DECODERS; + ctx->is_encoder = false; } else { ret = -ENODEV; goto err_ctx_free; diff --git a/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c b/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c index b22418436823..b88dc4ed06db 100644 --- a/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c +++ b/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c @@ -137,3 +137,20 @@ void hantro_h1_jpeg_enc_run(struct hantro_ctx *ctx) vepu_write(vpu, reg, H1_REG_ENC_CTRL); } + +void hantro_jpeg_enc_done(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + u32 bytesused = vepu_read(vpu, H1_REG_STR_BUF_LIMIT) / 8; + struct vb2_v4l2_buffer *dst_buf = hantro_get_dst_buf(ctx); + + /* + * TODO: Rework the JPEG encoder to eliminate the need + * for a bounce buffer. + */ + memcpy(vb2_plane_vaddr(&dst_buf->vb2_buf, 0) + + ctx->vpu_dst_fmt->header_size, + ctx->jpeg_enc.bounce_buffer.cpu, bytesused); + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, + ctx->vpu_dst_fmt->header_size + bytesused); +} diff --git a/drivers/staging/media/hantro/hantro_h264.c b/drivers/staging/media/hantro/hantro_h264.c index d561f125085a..194d05848077 100644 --- a/drivers/staging/media/hantro/hantro_h264.c +++ b/drivers/staging/media/hantro/hantro_h264.c @@ -22,8 +22,6 @@ #define POC_BUFFER_SIZE 34 #define SCALING_LIST_SIZE (6 * 16 + 2 * 64) -#define HANTRO_CMP(a, b) ((a) < (b) ? -1 : 1) - /* Data structure describing auxiliary buffer format. */ struct hantro_h264_dec_priv_tbl { u32 cabac_table[CABAC_INIT_BUFFER_SIZE]; @@ -195,7 +193,7 @@ static const u32 h264_cabac_table[] = { }; static void -reorder_scaling_list(struct hantro_ctx *ctx) +assemble_scaling_list(struct hantro_ctx *ctx) { const struct hantro_h264_dec_ctrls *ctrls = &ctx->h264_dec.ctrls; const struct v4l2_ctrl_h264_scaling_matrix *scaling = ctrls->scaling; @@ -237,7 +235,7 @@ static void prepare_table(struct hantro_ctx *ctx) tbl->poc[32] = dec_param->top_field_order_cnt; tbl->poc[33] = dec_param->bottom_field_order_cnt; - reorder_scaling_list(ctx); + assemble_scaling_list(ctx); } static bool dpb_entry_match(const struct v4l2_h264_dpb_entry *a, diff --git a/drivers/staging/media/hantro/hantro_hw.h b/drivers/staging/media/hantro/hantro_hw.h index 4053d8710e04..f066de6b592d 100644 --- a/drivers/staging/media/hantro/hantro_hw.h +++ b/drivers/staging/media/hantro/hantro_hw.h @@ -138,7 +138,7 @@ struct hantro_codec_ops { int (*init)(struct hantro_ctx *ctx); void (*exit)(struct hantro_ctx *ctx); void (*run)(struct hantro_ctx *ctx); - void (*done)(struct hantro_ctx *ctx, enum vb2_buffer_state); + void (*done)(struct hantro_ctx *ctx); void (*reset)(struct hantro_ctx *ctx); }; @@ -163,7 +163,7 @@ 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, unsigned int bytesused, +void hantro_irq_done(struct hantro_dev *vpu, enum vb2_buffer_state result); void hantro_start_prepare_run(struct hantro_ctx *ctx); void hantro_end_prepare_run(struct hantro_ctx *ctx); @@ -172,6 +172,7 @@ void hantro_h1_jpeg_enc_run(struct hantro_ctx *ctx); void rk3399_vpu_jpeg_enc_run(struct hantro_ctx *ctx); int hantro_jpeg_enc_init(struct hantro_ctx *ctx); void hantro_jpeg_enc_exit(struct hantro_ctx *ctx); +void hantro_jpeg_enc_done(struct hantro_ctx *ctx); dma_addr_t hantro_h264_get_ref_buf(struct hantro_ctx *ctx, unsigned int dpb_idx); diff --git a/drivers/staging/media/hantro/hantro_v4l2.c b/drivers/staging/media/hantro/hantro_v4l2.c index f28a94e2fa93..b668a82d40ad 100644 --- a/drivers/staging/media/hantro/hantro_v4l2.c +++ b/drivers/staging/media/hantro/hantro_v4l2.c @@ -40,7 +40,7 @@ hantro_get_formats(const struct hantro_ctx *ctx, unsigned int *num_fmts) { const struct hantro_fmt *formats; - if (hantro_is_encoder_ctx(ctx)) { + if (ctx->is_encoder) { formats = ctx->dev->variant->enc_fmts; *num_fmts = ctx->dev->variant->num_enc_fmts; } else { @@ -55,7 +55,7 @@ static const struct hantro_fmt * hantro_get_postproc_formats(const struct hantro_ctx *ctx, unsigned int *num_fmts) { - if (hantro_is_encoder_ctx(ctx)) { + if (ctx->is_encoder) { *num_fmts = 0; return NULL; } @@ -158,7 +158,7 @@ static int vidioc_enum_fmt(struct file *file, void *priv, * not MODE_NONE. * - on the output side we want to filter out all MODE_NONE formats. */ - skip_mode_none = capture == hantro_is_encoder_ctx(ctx); + skip_mode_none = capture == ctx->is_encoder; formats = hantro_get_formats(ctx, &num_fmts); for (i = 0; i < num_fmts; i++) { @@ -237,10 +237,10 @@ static int hantro_try_fmt(const struct hantro_ctx *ctx, enum v4l2_buf_type type) { const struct hantro_fmt *fmt, *vpu_fmt; - bool capture = !V4L2_TYPE_IS_OUTPUT(type); + bool capture = V4L2_TYPE_IS_CAPTURE(type); bool coded; - coded = capture == hantro_is_encoder_ctx(ctx); + coded = capture == ctx->is_encoder; vpu_debug(4, "trying format %c%c%c%c\n", (pix_mp->pixelformat & 0x7f), @@ -257,7 +257,7 @@ static int hantro_try_fmt(const struct hantro_ctx *ctx, if (coded) { pix_mp->num_planes = 1; vpu_fmt = fmt; - } else if (hantro_is_encoder_ctx(ctx)) { + } else if (ctx->is_encoder) { vpu_fmt = ctx->vpu_dst_fmt; } else { vpu_fmt = ctx->vpu_src_fmt; @@ -330,7 +330,7 @@ hantro_reset_encoded_fmt(struct hantro_ctx *ctx) vpu_fmt = hantro_get_default_fmt(ctx, true); - if (hantro_is_encoder_ctx(ctx)) { + if (ctx->is_encoder) { ctx->vpu_dst_fmt = vpu_fmt; fmt = &ctx->dst_fmt; } else { @@ -341,7 +341,7 @@ hantro_reset_encoded_fmt(struct hantro_ctx *ctx) hantro_reset_fmt(fmt, vpu_fmt); fmt->width = vpu_fmt->frmsize.min_width; fmt->height = vpu_fmt->frmsize.min_height; - if (hantro_is_encoder_ctx(ctx)) + if (ctx->is_encoder) hantro_set_fmt_cap(ctx, fmt); else hantro_set_fmt_out(ctx, fmt); @@ -355,7 +355,7 @@ hantro_reset_raw_fmt(struct hantro_ctx *ctx) raw_vpu_fmt = hantro_get_default_fmt(ctx, false); - if (hantro_is_encoder_ctx(ctx)) { + if (ctx->is_encoder) { ctx->vpu_src_fmt = raw_vpu_fmt; raw_fmt = &ctx->src_fmt; encoded_fmt = &ctx->dst_fmt; @@ -368,7 +368,7 @@ hantro_reset_raw_fmt(struct hantro_ctx *ctx) hantro_reset_fmt(raw_fmt, raw_vpu_fmt); raw_fmt->width = encoded_fmt->width; raw_fmt->width = encoded_fmt->width; - if (hantro_is_encoder_ctx(ctx)) + if (ctx->is_encoder) hantro_set_fmt_out(ctx, raw_fmt); else hantro_set_fmt_cap(ctx, raw_fmt); @@ -409,7 +409,7 @@ static int hantro_set_fmt_out(struct hantro_ctx *ctx, if (ret) return ret; - if (!hantro_is_encoder_ctx(ctx)) { + if (!ctx->is_encoder) { struct vb2_queue *peer_vq; /* @@ -450,7 +450,7 @@ static int hantro_set_fmt_out(struct hantro_ctx *ctx, * Note that hantro_reset_raw_fmt() also propagates size * changes to the raw format. */ - if (!hantro_is_encoder_ctx(ctx)) + if (!ctx->is_encoder) hantro_reset_raw_fmt(ctx); /* Colorimetry information are always propagated. */ @@ -479,7 +479,7 @@ static int hantro_set_fmt_cap(struct hantro_ctx *ctx, if (vb2_is_busy(vq)) return -EBUSY; - if (hantro_is_encoder_ctx(ctx)) { + if (ctx->is_encoder) { struct vb2_queue *peer_vq; /* @@ -512,7 +512,7 @@ static int hantro_set_fmt_cap(struct hantro_ctx *ctx, * Note that hantro_reset_raw_fmt() also propagates size * changes to the raw format. */ - if (hantro_is_encoder_ctx(ctx)) + if (ctx->is_encoder) hantro_reset_raw_fmt(ctx); /* Colorimetry information are always propagated. */ @@ -655,7 +655,7 @@ static bool hantro_vq_is_coded(struct vb2_queue *q) { struct hantro_ctx *ctx = vb2_get_drv_priv(q); - return hantro_is_encoder_ctx(ctx) != V4L2_TYPE_IS_OUTPUT(q->type); + return ctx->is_encoder != V4L2_TYPE_IS_OUTPUT(q->type); } static int hantro_start_streaming(struct vb2_queue *q, unsigned int count) diff --git a/drivers/staging/media/hantro/imx8m_vpu_hw.c b/drivers/staging/media/hantro/imx8m_vpu_hw.c index cb2420c5526e..c222de075ef4 100644 --- a/drivers/staging/media/hantro/imx8m_vpu_hw.c +++ b/drivers/staging/media/hantro/imx8m_vpu_hw.c @@ -143,7 +143,7 @@ static irqreturn_t imx8m_vpu_g1_irq(int irq, void *dev_id) vdpu_write(vpu, 0, G1_REG_INTERRUPT); vdpu_write(vpu, G1_REG_CONFIG_DEC_CLK_GATE_E, G1_REG_CONFIG); - hantro_irq_done(vpu, 0, state); + hantro_irq_done(vpu, state); return IRQ_HANDLED; } diff --git a/drivers/staging/media/hantro/rk3288_vpu_hw.c b/drivers/staging/media/hantro/rk3288_vpu_hw.c index 2f914b37b9e5..7b299ee3e93d 100644 --- a/drivers/staging/media/hantro/rk3288_vpu_hw.c +++ b/drivers/staging/media/hantro/rk3288_vpu_hw.c @@ -113,17 +113,16 @@ static irqreturn_t rk3288_vepu_irq(int irq, void *dev_id) { struct hantro_dev *vpu = dev_id; enum vb2_buffer_state state; - u32 status, bytesused; + u32 status; status = vepu_read(vpu, H1_REG_INTERRUPT); - bytesused = vepu_read(vpu, H1_REG_STR_BUF_LIMIT) / 8; state = (status & H1_REG_INTERRUPT_FRAME_RDY) ? VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR; vepu_write(vpu, 0, H1_REG_INTERRUPT); vepu_write(vpu, 0, H1_REG_AXI_CTRL); - hantro_irq_done(vpu, bytesused, state); + hantro_irq_done(vpu, state); return IRQ_HANDLED; } @@ -141,7 +140,7 @@ static irqreturn_t rk3288_vdpu_irq(int irq, void *dev_id) vdpu_write(vpu, 0, G1_REG_INTERRUPT); vdpu_write(vpu, G1_REG_CONFIG_DEC_CLK_GATE_E, G1_REG_CONFIG); - hantro_irq_done(vpu, 0, state); + hantro_irq_done(vpu, state); return IRQ_HANDLED; } @@ -180,6 +179,7 @@ static const struct hantro_codec_ops rk3288_vpu_codec_ops[] = { .run = hantro_h1_jpeg_enc_run, .reset = rk3288_vpu_enc_reset, .init = hantro_jpeg_enc_init, + .done = hantro_jpeg_enc_done, .exit = hantro_jpeg_enc_exit, }, [HANTRO_MODE_H264_DEC] = { diff --git a/drivers/staging/media/hantro/rk3399_vpu_hw.c b/drivers/staging/media/hantro/rk3399_vpu_hw.c index 9ac1f2cb6a16..7a7962cf771e 100644 --- a/drivers/staging/media/hantro/rk3399_vpu_hw.c +++ b/drivers/staging/media/hantro/rk3399_vpu_hw.c @@ -92,17 +92,16 @@ static irqreturn_t rk3399_vepu_irq(int irq, void *dev_id) { struct hantro_dev *vpu = dev_id; enum vb2_buffer_state state; - u32 status, bytesused; + u32 status; status = vepu_read(vpu, VEPU_REG_INTERRUPT); - bytesused = vepu_read(vpu, VEPU_REG_STR_BUF_LIMIT) / 8; state = (status & VEPU_REG_INTERRUPT_FRAME_READY) ? VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR; vepu_write(vpu, 0, VEPU_REG_INTERRUPT); vepu_write(vpu, 0, VEPU_REG_AXI_CTRL); - hantro_irq_done(vpu, bytesused, state); + hantro_irq_done(vpu, state); return IRQ_HANDLED; } @@ -120,7 +119,7 @@ static irqreturn_t rk3399_vdpu_irq(int irq, void *dev_id) vdpu_write(vpu, 0, VDPU_REG_INTERRUPT); vdpu_write(vpu, 0, VDPU_REG_AXI_CTRL); - hantro_irq_done(vpu, 0, state); + hantro_irq_done(vpu, state); return IRQ_HANDLED; } diff --git a/drivers/staging/media/imx/imx-ic-prp.c b/drivers/staging/media/imx/imx-ic-prp.c index 7658b83466a7..5b09e11b1a0e 100644 --- a/drivers/staging/media/imx/imx-ic-prp.c +++ b/drivers/staging/media/imx/imx-ic-prp.c @@ -26,8 +26,8 @@ /* * Min/Max supported width and heights. */ -#define MIN_W 176 -#define MIN_H 144 +#define MIN_W 32 +#define MIN_H 32 #define MAX_W 4096 #define MAX_H 4096 #define W_ALIGN 4 /* multiple of 16 pixels */ diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index b1f84e0ac486..74f5de466d5d 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -35,8 +35,8 @@ * has not requested a planar format, we should allow 8 pixel * alignment at the source pad. */ -#define MIN_W_SINK 176 -#define MIN_H_SINK 144 +#define MIN_W_SINK 32 +#define MIN_H_SINK 32 #define MAX_W_SINK 4096 #define MAX_H_SINK 4096 #define W_ALIGN_SINK 3 /* multiple of 8 pixels */ diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index d7e5b9ed27b8..d92fd804488e 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -33,8 +33,8 @@ * has not requested planar formats, we should allow 8 pixel * alignment. */ -#define MIN_W 176 -#define MIN_H 144 +#define MIN_W 32 +#define MIN_H 32 #define MAX_W 4096 #define MAX_H 4096 #define W_ALIGN 1 /* multiple of 2 pixels */ diff --git a/drivers/staging/media/imx/imx-media-vdic.c b/drivers/staging/media/imx/imx-media-vdic.c index 303b5407fb64..879329f81f79 100644 --- a/drivers/staging/media/imx/imx-media-vdic.c +++ b/drivers/staging/media/imx/imx-media-vdic.c @@ -49,8 +49,8 @@ struct vdic_pipeline_ops { /* * Min/Max supported width and heights. */ -#define MIN_W 176 -#define MIN_H 144 +#define MIN_W 32 +#define MIN_H 32 #define MAX_W_VDIC 968 #define MAX_H_VDIC 2048 #define W_ALIGN 4 /* multiple of 16 pixels */ diff --git a/drivers/staging/media/rkisp1/TODO b/drivers/staging/media/rkisp1/TODO index c0cbec0a164d..bdb1b8f73556 100644 --- a/drivers/staging/media/rkisp1/TODO +++ b/drivers/staging/media/rkisp1/TODO @@ -1,5 +1,4 @@ * Fix pad format size for statistics and parameters entities. -* Use threaded interrupt for rkisp1_stats_isr(), remove work queue. * Fix checkpatch errors. * Review and comment every lock * Handle quantization diff --git a/drivers/staging/media/rkisp1/rkisp1-capture.c b/drivers/staging/media/rkisp1/rkisp1-capture.c index f69235f82c45..c05280950ea0 100644 --- a/drivers/staging/media/rkisp1/rkisp1-capture.c +++ b/drivers/staging/media/rkisp1/rkisp1-capture.c @@ -283,10 +283,6 @@ static const struct rkisp1_capture_fmt_cfg rkisp1_sp_fmts[] = { .fourcc = V4L2_PIX_FMT_RGB565, .write_format = RKISP1_MI_CTRL_SP_WRITE_PLA, .output_format = RKISP1_MI_CTRL_SP_OUTPUT_RGB565, - }, { - .fourcc = V4L2_PIX_FMT_BGR666, - .write_format = RKISP1_MI_CTRL_SP_WRITE_PLA, - .output_format = RKISP1_MI_CTRL_SP_OUTPUT_RGB666, }, }; @@ -579,12 +575,16 @@ static void rkisp1_dummy_buf_destroy(struct rkisp1_capture *cap) static void rkisp1_set_next_buf(struct rkisp1_capture *cap) { - /* - * Use the dummy space allocated by dma_alloc_coherent to - * throw data if there is no available buffer. - */ - if (cap->buf.next) { - u32 *buff_addr = cap->buf.next->buff_addr; + cap->buf.curr = cap->buf.next; + cap->buf.next = NULL; + + if (!list_empty(&cap->buf.queue)) { + u32 *buff_addr; + + cap->buf.next = list_first_entry(&cap->buf.queue, struct rkisp1_buffer, queue); + list_del(&cap->buf.next->queue); + + buff_addr = cap->buf.next->buff_addr; rkisp1_write(cap->rkisp1, buff_addr[RKISP1_PLANE_Y], @@ -596,6 +596,10 @@ static void rkisp1_set_next_buf(struct rkisp1_capture *cap) buff_addr[RKISP1_PLANE_CR], cap->config->mi.cr_base_ad_init); } else { + /* + * Use the dummy space allocated by dma_alloc_coherent to + * throw data if there is no available buffer. + */ rkisp1_write(cap->rkisp1, cap->buf.dummy.dma_addr, cap->config->mi.y_base_ad_init); @@ -621,10 +625,11 @@ static void rkisp1_set_next_buf(struct rkisp1_capture *cap) static void rkisp1_handle_buffer(struct rkisp1_capture *cap) { struct rkisp1_isp *isp = &cap->rkisp1->isp; - struct rkisp1_buffer *curr_buf = cap->buf.curr; + struct rkisp1_buffer *curr_buf; unsigned long flags; spin_lock_irqsave(&cap->buf.lock, flags); + curr_buf = cap->buf.curr; if (curr_buf) { curr_buf->vb.sequence = atomic_read(&isp->frame_sequence); @@ -635,18 +640,8 @@ static void rkisp1_handle_buffer(struct rkisp1_capture *cap) cap->rkisp1->debug.frame_drop[cap->id]++; } - cap->buf.curr = cap->buf.next; - cap->buf.next = NULL; - - if (!list_empty(&cap->buf.queue)) { - cap->buf.next = list_first_entry(&cap->buf.queue, - struct rkisp1_buffer, - queue); - list_del(&cap->buf.next->queue); - } - spin_unlock_irqrestore(&cap->buf.lock, flags); - rkisp1_set_next_buf(cap); + spin_unlock_irqrestore(&cap->buf.lock, flags); } void rkisp1_capture_isr(struct rkisp1_device *rkisp1) @@ -747,18 +742,7 @@ static void rkisp1_vb2_buf_queue(struct vb2_buffer *vb) ispbuf->buff_addr[RKISP1_PLANE_CB]); spin_lock_irqsave(&cap->buf.lock, flags); - - /* - * If there's no next buffer assigned, queue this buffer directly - * as the next buffer, and update the memory interface. - */ - if (cap->is_streaming && !cap->buf.next && - atomic_read(&cap->rkisp1->isp.frame_sequence) == -1) { - cap->buf.next = ispbuf; - rkisp1_set_next_buf(cap); - } else { - list_add_tail(&ispbuf->queue, &cap->buf.queue); - } + list_add_tail(&ispbuf->queue, &cap->buf.queue); spin_unlock_irqrestore(&cap->buf.lock, flags); } @@ -932,7 +916,7 @@ static void rkisp1_stream_start(struct rkisp1_capture *cap) cap->ops->config(cap); /* Setup a buffer for the next frame */ - rkisp1_handle_buffer(cap); + rkisp1_set_next_buf(cap); cap->ops->enable(cap); /* It's safe to config ACTIVE and SHADOW regs for the * first stream. While when the second is starting, do NOT @@ -947,7 +931,7 @@ static void rkisp1_stream_start(struct rkisp1_capture *cap) /* force cfg update */ rkisp1_write(rkisp1, RKISP1_CIF_MI_INIT_SOFT_UPD, RKISP1_CIF_MI_INIT); - rkisp1_handle_buffer(cap); + rkisp1_set_next_buf(cap); } cap->is_streaming = true; } diff --git a/drivers/staging/media/rkisp1/rkisp1-common.h b/drivers/staging/media/rkisp1/rkisp1-common.h index 0c4fe503adc9..3dc51d703f73 100644 --- a/drivers/staging/media/rkisp1/rkisp1-common.h +++ b/drivers/staging/media/rkisp1/rkisp1-common.h @@ -22,6 +22,9 @@ #include "rkisp1-regs.h" #include "uapi/rkisp1-config.h" +#define RKISP1_ISP_SD_SRC BIT(0) +#define RKISP1_ISP_SD_SINK BIT(1) + #define RKISP1_ISP_MAX_WIDTH 4032 #define RKISP1_ISP_MAX_HEIGHT 3024 #define RKISP1_ISP_MIN_WIDTH 32 @@ -42,9 +45,14 @@ #define RKISP1_MAX_BUS_CLK 8 +#define RKISP1_STATS_MEAS_MASK (RKISP1_CIF_ISP_AWB_DONE | \ + RKISP1_CIF_ISP_AFM_FIN | \ + RKISP1_CIF_ISP_EXP_END | \ + RKISP1_CIF_ISP_HIST_MEASURE_RDY) enum rkisp1_rsz_pad { RKISP1_RSZ_PAD_SINK, RKISP1_RSZ_PAD_SRC, + RKISP1_RSZ_PAD_MAX }; enum rkisp1_stream_id { @@ -174,21 +182,17 @@ struct rkisp1_capture { /* * struct rkisp1_stats - ISP Statistics device * - * @irq_lock: buffer queue lock + * @lock: locks the buffer list 'stat' and 'is_streaming' * @stat: stats buffer list - * @readout_wq: workqueue for statistics information read */ struct rkisp1_stats { struct rkisp1_vdev_node vnode; struct rkisp1_device *rkisp1; - spinlock_t irq_lock; + spinlock_t lock; /* locks 'is_streaming', and 'stats' */ struct list_head stat; struct v4l2_format vdev_fmt; bool is_streaming; - - struct workqueue_struct *readout_wq; - struct mutex wq_lock; }; /* @@ -216,8 +220,8 @@ struct rkisp1_resizer { struct v4l2_subdev sd; enum rkisp1_stream_id id; struct rkisp1_device *rkisp1; - struct media_pad pads[RKISP1_ISP_PAD_MAX]; - struct v4l2_subdev_pad_config pad_cfg[RKISP1_ISP_PAD_MAX]; + struct media_pad pads[RKISP1_RSZ_PAD_MAX]; + struct v4l2_subdev_pad_config pad_cfg[RKISP1_RSZ_PAD_MAX]; const struct rkisp1_rsz_config *config; enum v4l2_pixel_encoding pixel_enc; struct mutex ops_lock; @@ -226,7 +230,9 @@ struct rkisp1_resizer { struct rkisp1_debug { struct dentry *debugfs_dir; unsigned long data_loss; - unsigned long pic_size_error; + unsigned long outform_size_error; + unsigned long img_stabilization_size_error; + unsigned long inform_size_error; unsigned long mipi_error; unsigned long stats_error; unsigned long stop_timeout[2]; @@ -250,7 +256,6 @@ struct rkisp1_device { unsigned int clk_size; struct clk_bulk_data clks[RKISP1_MAX_BUS_CLK]; struct v4l2_device v4l2_dev; - struct v4l2_ctrl_handler ctrl_handler; struct media_device media_dev; struct v4l2_async_notifier notifier; struct rkisp1_sensor_async *active_sensor; @@ -260,7 +265,6 @@ struct rkisp1_device { struct rkisp1_stats stats; struct rkisp1_params params; struct media_pipeline pipe; - struct vb2_alloc_ctx *alloc_ctx; struct mutex stream_lock; struct rkisp1_debug debug; }; diff --git a/drivers/staging/media/rkisp1/rkisp1-dev.c b/drivers/staging/media/rkisp1/rkisp1-dev.c index 9ac38bafb839..a0eb8f08708b 100644 --- a/drivers/staging/media/rkisp1/rkisp1-dev.c +++ b/drivers/staging/media/rkisp1/rkisp1-dev.c @@ -129,7 +129,7 @@ static int rkisp1_create_links(struct rkisp1_device *rkisp1) ret = media_entity_get_fwnode_pad(&sd->entity, sd->fwnode, MEDIA_PAD_FL_SOURCE); if (ret < 0) { - dev_err(sd->dev, "failed to find src pad for %s\n", + dev_err(rkisp1->dev, "failed to find src pad for %s\n", sd->name); return ret; } @@ -438,8 +438,13 @@ static void rkisp1_debug_init(struct rkisp1_device *rkisp1) } debugfs_create_ulong("data_loss", 0444, debug->debugfs_dir, &debug->data_loss); - debugfs_create_ulong("pic_size_error", 0444, debug->debugfs_dir, - &debug->pic_size_error); + debugfs_create_ulong("outform_size_err", 0444, debug->debugfs_dir, + &debug->outform_size_error); + debugfs_create_ulong("img_stabilization_size_error", 0444, + debug->debugfs_dir, + &debug->img_stabilization_size_error); + debugfs_create_ulong("inform_size_error", 0444, debug->debugfs_dir, + &debug->inform_size_error); debugfs_create_ulong("mipi_error", 0444, debug->debugfs_dir, &debug->mipi_error); debugfs_create_ulong("stats_error", 0444, debug->debugfs_dir, diff --git a/drivers/staging/media/rkisp1/rkisp1-isp.c b/drivers/staging/media/rkisp1/rkisp1-isp.c index dc2b59a0160a..6ec1e9816e9f 100644 --- a/drivers/staging/media/rkisp1/rkisp1-isp.c +++ b/drivers/staging/media/rkisp1/rkisp1-isp.c @@ -23,10 +23,6 @@ #define RKISP1_ISP_DEV_NAME RKISP1_DRIVER_NAME "_isp" -#define RKISP1_DIR_SRC BIT(0) -#define RKISP1_DIR_SINK BIT(1) -#define RKISP1_DIR_SINK_SRC (RKISP1_DIR_SINK | RKISP1_DIR_SRC) - /* * NOTE: MIPI controller and input MUX are also configured in this file. * This is because ISP Subdev describes not only ISP submodule (input size, @@ -62,119 +58,119 @@ static const struct rkisp1_isp_mbus_info rkisp1_isp_formats[] = { { .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, .pixel_enc = V4L2_PIXEL_ENC_YUV, - .direction = RKISP1_DIR_SRC, + .direction = RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW10, .bayer_pat = RKISP1_RAW_RGGB, .bus_width = 10, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW10, .bayer_pat = RKISP1_RAW_BGGR, .bus_width = 10, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW10, .bayer_pat = RKISP1_RAW_GBRG, .bus_width = 10, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW10, .bayer_pat = RKISP1_RAW_GRBG, .bus_width = 10, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW12, .bayer_pat = RKISP1_RAW_RGGB, .bus_width = 12, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW12, .bayer_pat = RKISP1_RAW_BGGR, .bus_width = 12, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW12, .bayer_pat = RKISP1_RAW_GBRG, .bus_width = 12, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW12, .bayer_pat = RKISP1_RAW_GRBG, .bus_width = 12, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW8, .bayer_pat = RKISP1_RAW_RGGB, .bus_width = 8, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW8, .bayer_pat = RKISP1_RAW_BGGR, .bus_width = 8, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW8, .bayer_pat = RKISP1_RAW_GBRG, .bus_width = 8, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW8, .bayer_pat = RKISP1_RAW_GRBG, .bus_width = 8, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mipi_dt = RKISP1_CIF_CSI2_DT_YUV422_8b, .yuv_seq = RKISP1_CIF_ISP_ACQ_PROP_YCBYCR, .bus_width = 16, - .direction = RKISP1_DIR_SINK, + .direction = RKISP1_ISP_SD_SINK, }, { .mbus_code = MEDIA_BUS_FMT_YVYU8_1X16, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mipi_dt = RKISP1_CIF_CSI2_DT_YUV422_8b, .yuv_seq = RKISP1_CIF_ISP_ACQ_PROP_YCRYCB, .bus_width = 16, - .direction = RKISP1_DIR_SINK, + .direction = RKISP1_ISP_SD_SINK, }, { .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mipi_dt = RKISP1_CIF_CSI2_DT_YUV422_8b, .yuv_seq = RKISP1_CIF_ISP_ACQ_PROP_CBYCRY, .bus_width = 16, - .direction = RKISP1_DIR_SINK, + .direction = RKISP1_ISP_SD_SINK, }, { .mbus_code = MEDIA_BUS_FMT_VYUY8_1X16, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mipi_dt = RKISP1_CIF_CSI2_DT_YUV422_8b, .yuv_seq = RKISP1_CIF_ISP_ACQ_PROP_CRYCBY, .bus_width = 16, - .direction = RKISP1_DIR_SINK, + .direction = RKISP1_ISP_SD_SINK, }, }; @@ -203,10 +199,8 @@ static struct v4l2_subdev *rkisp1_get_remote_sensor(struct v4l2_subdev *sd) local = &sd->entity.pads[RKISP1_ISP_PAD_SINK_VIDEO]; remote = media_entity_remote_pad(local); - if (!remote) { - dev_warn(sd->dev, "No link between isp and sensor\n"); + if (!remote) return NULL; - } sensor_me = remote->entity; return media_entity_to_v4l2_subdev(sensor_me); @@ -574,9 +568,9 @@ static int rkisp1_isp_enum_mbus_code(struct v4l2_subdev *sd, int pos = 0; if (code->pad == RKISP1_ISP_PAD_SINK_VIDEO) { - dir = RKISP1_DIR_SINK; + dir = RKISP1_ISP_SD_SINK; } else if (code->pad == RKISP1_ISP_PAD_SOURCE_VIDEO) { - dir = RKISP1_DIR_SRC; + dir = RKISP1_ISP_SD_SRC; } else { if (code->index > 0) return -EINVAL; @@ -661,7 +655,7 @@ static void rkisp1_isp_set_src_fmt(struct rkisp1_isp *isp, src_fmt->code = format->code; mbus_info = rkisp1_isp_mbus_info_get(src_fmt->code); - if (!mbus_info || !(mbus_info->direction & RKISP1_DIR_SRC)) { + if (!mbus_info || !(mbus_info->direction & RKISP1_ISP_SD_SRC)) { src_fmt->code = RKISP1_DEF_SRC_PAD_FMT; mbus_info = rkisp1_isp_mbus_info_get(src_fmt->code); } @@ -745,7 +739,7 @@ static void rkisp1_isp_set_sink_fmt(struct rkisp1_isp *isp, which); sink_fmt->code = format->code; mbus_info = rkisp1_isp_mbus_info_get(sink_fmt->code); - if (!mbus_info || !(mbus_info->direction & RKISP1_DIR_SINK)) { + if (!mbus_info || !(mbus_info->direction & RKISP1_ISP_SD_SINK)) { sink_fmt->code = RKISP1_DEF_SINK_PAD_FMT; mbus_info = rkisp1_isp_mbus_info_get(sink_fmt->code); } @@ -889,18 +883,20 @@ static const struct v4l2_subdev_pad_ops rkisp1_isp_pad_ops = { static int rkisp1_mipi_csi2_start(struct rkisp1_isp *isp, struct rkisp1_sensor_async *sensor) { + struct rkisp1_device *rkisp1 = + container_of(isp->sd.v4l2_dev, struct rkisp1_device, v4l2_dev); union phy_configure_opts opts; struct phy_configure_opts_mipi_dphy *cfg = &opts.mipi_dphy; s64 pixel_clock; if (!sensor->pixel_rate_ctrl) { - dev_warn(sensor->sd->dev, "No pixel rate control in subdev\n"); + dev_warn(rkisp1->dev, "No pixel rate control in sensor subdev\n"); return -EPIPE; } pixel_clock = v4l2_ctrl_g_ctrl_int64(sensor->pixel_rate_ctrl); if (!pixel_clock) { - dev_err(sensor->sd->dev, "Invalid pixel rate value\n"); + dev_err(rkisp1->dev, "Invalid pixel rate value\n"); return -EINVAL; } @@ -933,8 +929,11 @@ static int rkisp1_isp_s_stream(struct v4l2_subdev *sd, int enable) } sensor_sd = rkisp1_get_remote_sensor(sd); - if (!sensor_sd) + if (!sensor_sd) { + dev_warn(rkisp1->dev, "No link between isp and sensor\n"); return -ENODEV; + } + rkisp1->active_sensor = container_of(sensor_sd->asd, struct rkisp1_sensor_async, asd); @@ -1021,7 +1020,7 @@ int rkisp1_isp_register(struct rkisp1_device *rkisp1, ret = v4l2_device_register_subdev(v4l2_dev, sd); if (ret) { - dev_err(sd->dev, "Failed to register isp subdev\n"); + dev_err(rkisp1->dev, "Failed to register isp subdev\n"); goto err_cleanup_media_entity; } @@ -1123,8 +1122,13 @@ void rkisp1_isp_isr(struct rkisp1_device *rkisp1) if (status & RKISP1_CIF_ISP_PIC_SIZE_ERROR) { /* Clear pic_size_error */ isp_err = rkisp1_read(rkisp1, RKISP1_CIF_ISP_ERR); + if (isp_err & RKISP1_CIF_ISP_ERR_INFORM_SIZE) + rkisp1->debug.inform_size_error++; + if (isp_err & RKISP1_CIF_ISP_ERR_IS_SIZE) + rkisp1->debug.img_stabilization_size_error++; + if (isp_err & RKISP1_CIF_ISP_ERR_OUTFORM_SIZE) + rkisp1->debug.outform_size_error++; rkisp1_write(rkisp1, isp_err, RKISP1_CIF_ISP_ERR_CLR); - rkisp1->debug.pic_size_error++; } else if (status & RKISP1_CIF_ISP_DATA_LOSS) { /* keep track of data_loss in debugfs */ rkisp1->debug.data_loss++; @@ -1135,10 +1139,7 @@ void rkisp1_isp_isr(struct rkisp1_device *rkisp1) /* New frame from the sensor received */ isp_ris = rkisp1_read(rkisp1, RKISP1_CIF_ISP_RIS); - if (isp_ris & (RKISP1_CIF_ISP_AWB_DONE | - RKISP1_CIF_ISP_AFM_FIN | - RKISP1_CIF_ISP_EXP_END | - RKISP1_CIF_ISP_HIST_MEASURE_RDY)) + if (isp_ris & RKISP1_STATS_MEAS_MASK) rkisp1_stats_isr(&rkisp1->stats, isp_ris); } diff --git a/drivers/staging/media/rkisp1/rkisp1-params.c b/drivers/staging/media/rkisp1/rkisp1-params.c index 44d542caf32b..797e79de659c 100644 --- a/drivers/staging/media/rkisp1/rkisp1-params.c +++ b/drivers/staging/media/rkisp1/rkisp1-params.c @@ -1607,7 +1607,7 @@ int rkisp1_params_register(struct rkisp1_params *params, goto err_release_queue; ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) { - dev_err(&vdev->dev, + dev_err(rkisp1->dev, "failed to register %s, ret=%d\n", vdev->name, ret); goto err_cleanup_media_entity; } diff --git a/drivers/staging/media/rkisp1/rkisp1-regs.h b/drivers/staging/media/rkisp1/rkisp1-regs.h index 46018f435b6f..9b8e616ea24c 100644 --- a/drivers/staging/media/rkisp1/rkisp1-regs.h +++ b/drivers/staging/media/rkisp1/rkisp1-regs.h @@ -398,7 +398,6 @@ #define RKISP1_CIF_ISP_AWB_YMAX_READ(x) (((x) >> 2) & 1) #define RKISP1_CIF_ISP_AWB_MODE_RGB_EN ((1 << 31) | (0x2 << 0)) #define RKISP1_CIF_ISP_AWB_MODE_YCBCR_EN ((0 << 31) | (0x2 << 0)) -#define RKISP1_CIF_ISP_AWB_MODE_YCBCR_EN ((0 << 31) | (0x2 << 0)) #define RKISP1_CIF_ISP_AWB_MODE_MASK_NONE 0xFFFFFFFC #define RKISP1_CIF_ISP_AWB_MODE_READ(x) ((x) & 3) /* ISP_AWB_GAIN_RB, ISP_AWB_GAIN_G */ diff --git a/drivers/staging/media/rkisp1/rkisp1-resizer.c b/drivers/staging/media/rkisp1/rkisp1-resizer.c index d049374413dc..c66d2a52fd71 100644 --- a/drivers/staging/media/rkisp1/rkisp1-resizer.c +++ b/drivers/staging/media/rkisp1/rkisp1-resizer.c @@ -437,8 +437,8 @@ static int rkisp1_rsz_enum_mbus_code(struct v4l2_subdev *sd, u32 pad = code->pad; int ret; - /* supported mbus codes are the same in isp sink pad */ - code->pad = RKISP1_ISP_PAD_SINK_VIDEO; + /* supported mbus codes are the same in isp video src pad */ + code->pad = RKISP1_ISP_PAD_SOURCE_VIDEO; ret = v4l2_subdev_call(&rsz->rkisp1->isp.sd, pad, enum_mbus_code, &dummy_cfg, code); @@ -542,7 +542,7 @@ static void rkisp1_rsz_set_sink_fmt(struct rkisp1_resizer *rsz, which); sink_fmt->code = format->code; mbus_info = rkisp1_isp_mbus_info_get(sink_fmt->code); - if (!mbus_info) { + if (!mbus_info || !(mbus_info->direction & RKISP1_ISP_SD_SRC)) { sink_fmt->code = RKISP1_DEF_FMT; mbus_info = rkisp1_isp_mbus_info_get(sink_fmt->code); } @@ -553,11 +553,11 @@ static void rkisp1_rsz_set_sink_fmt(struct rkisp1_resizer *rsz, src_fmt->code = sink_fmt->code; sink_fmt->width = clamp_t(u32, format->width, - rsz->config->min_rsz_width, - rsz->config->max_rsz_width); + RKISP1_ISP_MIN_WIDTH, + RKISP1_ISP_MAX_WIDTH); sink_fmt->height = clamp_t(u32, format->height, - rsz->config->min_rsz_height, - rsz->config->max_rsz_height); + RKISP1_ISP_MIN_HEIGHT, + RKISP1_ISP_MAX_HEIGHT); *format = *sink_fmt; @@ -639,7 +639,7 @@ static int rkisp1_rsz_set_selection(struct v4l2_subdev *sd, if (sel->target != V4L2_SEL_TGT_CROP || sel->pad == RKISP1_RSZ_PAD_SRC) return -EINVAL; - dev_dbg(sd->dev, "%s: pad: %d sel(%d,%d)/%dx%d\n", __func__, + dev_dbg(rsz->rkisp1->dev, "%s: pad: %d sel(%d,%d)/%dx%d\n", __func__, sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height); mutex_lock(&rsz->ops_lock); @@ -735,7 +735,7 @@ static int rkisp1_rsz_register(struct rkisp1_resizer *rsz) rsz->pixel_enc = RKISP1_DEF_PIXEL_ENC; mutex_init(&rsz->ops_lock); - ret = media_entity_pads_init(&sd->entity, 2, pads); + ret = media_entity_pads_init(&sd->entity, RKISP1_RSZ_PAD_MAX, pads); if (ret) return ret; diff --git a/drivers/staging/media/rkisp1/rkisp1-stats.c b/drivers/staging/media/rkisp1/rkisp1-stats.c index 6dfcbdc3deb8..87e4104d20dd 100644 --- a/drivers/staging/media/rkisp1/rkisp1-stats.c +++ b/drivers/staging/media/rkisp1/rkisp1-stats.c @@ -18,21 +18,6 @@ #define RKISP1_ISP_STATS_REQ_BUFS_MIN 2 #define RKISP1_ISP_STATS_REQ_BUFS_MAX 8 -enum rkisp1_isp_readout_cmd { - RKISP1_ISP_READOUT_MEAS, - RKISP1_ISP_READOUT_META, -}; - -struct rkisp1_isp_readout_work { - struct work_struct work; - struct rkisp1_stats *stats; - - unsigned int frame_id; - unsigned int isp_ris; - enum rkisp1_isp_readout_cmd readout; - struct vb2_buffer *vb; -}; - static int rkisp1_stats_enum_fmt_meta_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { @@ -133,9 +118,9 @@ static void rkisp1_stats_vb2_buf_queue(struct vb2_buffer *vb) stats_buf->vaddr[0] = vb2_plane_vaddr(vb, 0); - mutex_lock(&stats_dev->wq_lock); + spin_lock_irq(&stats_dev->lock); list_add_tail(&stats_buf->queue, &stats_dev->stat); - mutex_unlock(&stats_dev->wq_lock); + spin_unlock_irq(&stats_dev->lock); } static int rkisp1_stats_vb2_buf_prepare(struct vb2_buffer *vb) @@ -152,17 +137,10 @@ static void rkisp1_stats_vb2_stop_streaming(struct vb2_queue *vq) { struct rkisp1_stats *stats = vq->drv_priv; struct rkisp1_buffer *buf; - unsigned long flags; unsigned int i; - /* Make sure no new work queued in isr before draining wq */ - spin_lock_irqsave(&stats->irq_lock, flags); + spin_lock_irq(&stats->lock); stats->is_streaming = false; - spin_unlock_irqrestore(&stats->irq_lock, flags); - - drain_workqueue(stats->readout_wq); - - mutex_lock(&stats->wq_lock); for (i = 0; i < RKISP1_ISP_STATS_REQ_BUFS_MAX; i++) { if (list_empty(&stats->stat)) break; @@ -171,7 +149,7 @@ static void rkisp1_stats_vb2_stop_streaming(struct vb2_queue *vq) list_del(&buf->queue); vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } - mutex_unlock(&stats->wq_lock); + spin_unlock_irq(&stats->lock); } static int @@ -253,7 +231,7 @@ static void rkisp1_stats_get_afc_meas(struct rkisp1_stats *stats, struct rkisp1_device *rkisp1 = stats->rkisp1; struct rkisp1_cif_isp_af_stat *af; - pbuf->meas_type = RKISP1_CIF_ISP_STAT_AFM_FIN; + pbuf->meas_type |= RKISP1_CIF_ISP_STAT_AFM_FIN; af = &pbuf->params.af; af->window[0].sum = rkisp1_read(rkisp1, RKISP1_CIF_ISP_AFM_SUM_A); @@ -325,8 +303,7 @@ static void rkisp1_stats_get_bls_meas(struct rkisp1_stats *stats, } static void -rkisp1_stats_send_measurement(struct rkisp1_stats *stats, - struct rkisp1_isp_readout_work *meas_work) +rkisp1_stats_send_measurement(struct rkisp1_stats *stats, u32 isp_ris) { struct rkisp1_stat_buffer *cur_stat_buf; struct rkisp1_buffer *cur_buf = NULL; @@ -334,21 +311,12 @@ rkisp1_stats_send_measurement(struct rkisp1_stats *stats, atomic_read(&stats->rkisp1->isp.frame_sequence); u64 timestamp = ktime_get_ns(); - if (frame_sequence != meas_work->frame_id) { - dev_warn(stats->rkisp1->dev, - "Measurement late(%d, %d)\n", - frame_sequence, meas_work->frame_id); - frame_sequence = meas_work->frame_id; - } - - mutex_lock(&stats->wq_lock); /* get one empty buffer */ if (!list_empty(&stats->stat)) { cur_buf = list_first_entry(&stats->stat, struct rkisp1_buffer, queue); list_del(&cur_buf->queue); } - mutex_unlock(&stats->wq_lock); if (!cur_buf) return; @@ -356,26 +324,19 @@ rkisp1_stats_send_measurement(struct rkisp1_stats *stats, cur_stat_buf = (struct rkisp1_stat_buffer *)(cur_buf->vaddr[0]); - if (meas_work->isp_ris & RKISP1_CIF_ISP_AWB_DONE) { + if (isp_ris & RKISP1_CIF_ISP_AWB_DONE) rkisp1_stats_get_awb_meas(stats, cur_stat_buf); - cur_stat_buf->meas_type |= RKISP1_CIF_ISP_STAT_AWB; - } - if (meas_work->isp_ris & RKISP1_CIF_ISP_AFM_FIN) { + if (isp_ris & RKISP1_CIF_ISP_AFM_FIN) rkisp1_stats_get_afc_meas(stats, cur_stat_buf); - cur_stat_buf->meas_type |= RKISP1_CIF_ISP_STAT_AFM_FIN; - } - if (meas_work->isp_ris & RKISP1_CIF_ISP_EXP_END) { + if (isp_ris & RKISP1_CIF_ISP_EXP_END) { rkisp1_stats_get_aec_meas(stats, cur_stat_buf); rkisp1_stats_get_bls_meas(stats, cur_stat_buf); - cur_stat_buf->meas_type |= RKISP1_CIF_ISP_STAT_AUTOEXP; } - if (meas_work->isp_ris & RKISP1_CIF_ISP_HIST_MEASURE_RDY) { + if (isp_ris & RKISP1_CIF_ISP_HIST_MEASURE_RDY) rkisp1_stats_get_hst_meas(stats, cur_stat_buf); - cur_stat_buf->meas_type |= RKISP1_CIF_ISP_STAT_HIST; - } vb2_set_plane_payload(&cur_buf->vb.vb2_buf, 0, sizeof(struct rkisp1_stat_buffer)); @@ -384,64 +345,26 @@ rkisp1_stats_send_measurement(struct rkisp1_stats *stats, vb2_buffer_done(&cur_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); } -static void rkisp1_stats_readout_work(struct work_struct *work) -{ - struct rkisp1_isp_readout_work *readout_work = - container_of(work, struct rkisp1_isp_readout_work, work); - struct rkisp1_stats *stats = readout_work->stats; - - if (readout_work->readout == RKISP1_ISP_READOUT_MEAS) - rkisp1_stats_send_measurement(stats, readout_work); - - kfree(readout_work); -} - void rkisp1_stats_isr(struct rkisp1_stats *stats, u32 isp_ris) { - unsigned int frame_sequence = - atomic_read(&stats->rkisp1->isp.frame_sequence); struct rkisp1_device *rkisp1 = stats->rkisp1; - struct rkisp1_isp_readout_work *work; unsigned int isp_mis_tmp = 0; - u32 val; - spin_lock(&stats->irq_lock); + spin_lock(&stats->lock); - val = RKISP1_CIF_ISP_AWB_DONE | RKISP1_CIF_ISP_AFM_FIN | - RKISP1_CIF_ISP_EXP_END | RKISP1_CIF_ISP_HIST_MEASURE_RDY; - rkisp1_write(rkisp1, val, RKISP1_CIF_ISP_ICR); + rkisp1_write(rkisp1, RKISP1_STATS_MEAS_MASK, RKISP1_CIF_ISP_ICR); isp_mis_tmp = rkisp1_read(rkisp1, RKISP1_CIF_ISP_MIS); - if (isp_mis_tmp & - (RKISP1_CIF_ISP_AWB_DONE | RKISP1_CIF_ISP_AFM_FIN | - RKISP1_CIF_ISP_EXP_END | RKISP1_CIF_ISP_HIST_MEASURE_RDY)) + if (isp_mis_tmp & RKISP1_STATS_MEAS_MASK) rkisp1->debug.stats_error++; if (!stats->is_streaming) goto unlock; - if (isp_ris & (RKISP1_CIF_ISP_AWB_DONE | - RKISP1_CIF_ISP_AFM_FIN | - RKISP1_CIF_ISP_EXP_END | - RKISP1_CIF_ISP_HIST_MEASURE_RDY)) { - work = kzalloc(sizeof(*work), GFP_ATOMIC); - if (work) { - INIT_WORK(&work->work, - rkisp1_stats_readout_work); - work->readout = RKISP1_ISP_READOUT_MEAS; - work->stats = stats; - work->frame_id = frame_sequence; - work->isp_ris = isp_ris; - if (!queue_work(stats->readout_wq, - &work->work)) - kfree(work); - } else { - dev_err(stats->rkisp1->dev, - "Could not allocate work\n"); - } - } + if (isp_ris & RKISP1_STATS_MEAS_MASK) + rkisp1_stats_send_measurement(stats, isp_ris); unlock: - spin_unlock(&stats->irq_lock); + spin_unlock(&stats->lock); } static void rkisp1_init_stats(struct rkisp1_stats *stats) @@ -461,10 +384,9 @@ int rkisp1_stats_register(struct rkisp1_stats *stats, int ret; stats->rkisp1 = rkisp1; - mutex_init(&stats->wq_lock); mutex_init(&node->vlock); INIT_LIST_HEAD(&stats->stat); - spin_lock_init(&stats->irq_lock); + spin_lock_init(&stats->lock); strscpy(vdev->name, RKISP1_STATS_DEV_NAME, sizeof(vdev->name)); @@ -493,25 +415,13 @@ int rkisp1_stats_register(struct rkisp1_stats *stats, goto err_cleanup_media_entity; } - stats->readout_wq = alloc_workqueue("measurement_queue", - WQ_UNBOUND | WQ_MEM_RECLAIM, - 1); - - if (!stats->readout_wq) { - ret = -ENOMEM; - goto err_unreg_vdev; - } - return 0; -err_unreg_vdev: - video_unregister_device(vdev); err_cleanup_media_entity: media_entity_cleanup(&vdev->entity); err_release_queue: vb2_queue_release(vdev->queue); mutex_destroy(&node->vlock); - mutex_destroy(&stats->wq_lock); return ret; } @@ -520,10 +430,8 @@ void rkisp1_stats_unregister(struct rkisp1_stats *stats) struct rkisp1_vdev_node *node = &stats->vnode; struct video_device *vdev = &node->vdev; - destroy_workqueue(stats->readout_wq); video_unregister_device(vdev); media_entity_cleanup(&vdev->entity); vb2_queue_release(vdev->queue); mutex_destroy(&node->vlock); - mutex_destroy(&stats->wq_lock); } diff --git a/drivers/staging/media/rkisp1/uapi/rkisp1-config.h b/drivers/staging/media/rkisp1/uapi/rkisp1-config.h index ca0d031b14ac..8f9b061e5b6b 100644 --- a/drivers/staging/media/rkisp1/uapi/rkisp1-config.h +++ b/drivers/staging/media/rkisp1/uapi/rkisp1-config.h @@ -68,7 +68,7 @@ #define RKISP1_CIF_ISP_BLS_FIX_MASK 0x00001fff /* - * Automatic white balance measurments + * Automatic white balance measurements */ #define RKISP1_CIF_ISP_AWB_MAX_GRID 1 #define RKISP1_CIF_ISP_AWB_MAX_FRAMES 7 @@ -408,7 +408,7 @@ struct rkisp1_cif_isp_flt_config { /** * struct rkisp1_cif_isp_bdm_config - Configuration used by Bayer DeMosaic * - * @demosaic_th: threshod for bayer demosaicing texture detection + * @demosaic_th: threshold for bayer demosaicing texture detection */ struct rkisp1_cif_isp_bdm_config { __u8 demosaic_th; diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c index 225eeca73356..c8151328fb70 100644 --- a/drivers/staging/media/rkvdec/rkvdec.c +++ b/drivers/staging/media/rkvdec/rkvdec.c @@ -27,6 +27,32 @@ #include "rkvdec.h" #include "rkvdec-regs.h" +static int rkvdec_try_ctrl(struct v4l2_ctrl *ctrl) +{ + if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_SPS) { + const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p_h264_sps; + /* + * TODO: The hardware supports 10-bit and 4:2:2 profiles, + * but it's currently broken in the driver. + * Reject them for now, until it's fixed. + */ + if (sps->chroma_format_idc > 1) + /* Only 4:0:0 and 4:2:0 are supported */ + return -EINVAL; + if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8) + /* Luma and chroma bit depth mismatch */ + return -EINVAL; + if (sps->bit_depth_luma_minus8 != 0) + /* Only 8-bit is supported */ + return -EINVAL; + } + return 0; +} + +static const struct v4l2_ctrl_ops rkvdec_ctrl_ops = { + .try_ctrl = rkvdec_try_ctrl, +}; + static const struct rkvdec_ctrl_desc rkvdec_h264_ctrl_descs[] = { { .per_request = true, @@ -42,6 +68,7 @@ static const struct rkvdec_ctrl_desc rkvdec_h264_ctrl_descs[] = { .per_request = true, .mandatory = true, .cfg.id = V4L2_CID_MPEG_VIDEO_H264_SPS, + .cfg.ops = &rkvdec_ctrl_ops, }, { .per_request = true, @@ -489,7 +516,7 @@ static int rkvdec_start_streaming(struct vb2_queue *q, unsigned int count) const struct rkvdec_coded_fmt_desc *desc; int ret; - if (!V4L2_TYPE_IS_OUTPUT(q->type)) + if (V4L2_TYPE_IS_CAPTURE(q->type)) return 0; desc = ctx->coded_fmt_desc; diff --git a/drivers/staging/media/soc_camera/Kconfig b/drivers/staging/media/soc_camera/Kconfig deleted file mode 100644 index 4a54db121574..000000000000 --- a/drivers/staging/media/soc_camera/Kconfig +++ /dev/null @@ -1,51 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -config SOC_CAMERA - tristate "SoC camera support" - depends on VIDEO_V4L2 && HAS_DMA && I2C && BROKEN - select VIDEOBUF2_CORE - help - SoC Camera is a common API to several cameras, not connecting - over a bus like PCI or USB. For example some i2c camera connected - directly to the data bus of an SoC. - -comment "soc_camera sensor drivers" - -config SOC_CAMERA_MT9M111 - tristate "legacy soc_camera mt9m111, mt9m112 and mt9m131 support" - depends on SOC_CAMERA && I2C - select VIDEO_MT9M111 - help - This driver supports MT9M111, MT9M112 and MT9M131 cameras from - Micron/Aptina. - This is the legacy configuration which shouldn't be used anymore, - while VIDEO_MT9M111 should be used instead. - -config SOC_CAMERA_MT9V022 - tristate "mt9v022 and mt9v024 support" - depends on SOC_CAMERA && I2C - help - This driver supports MT9V022 cameras from Micron - -config SOC_CAMERA_OV5642 - tristate "ov5642 camera support" - depends on SOC_CAMERA && I2C - help - This is a V4L2 camera driver for the OmniVision OV5642 sensor - -config SOC_CAMERA_OV9740 - tristate "ov9740 camera support" - depends on SOC_CAMERA && I2C - help - This is a ov9740 camera driver - -config SOC_CAMERA_IMX074 - tristate "imx074 support (DEPRECATED)" - depends on SOC_CAMERA && I2C - help - This driver supports IMX074 cameras from Sony - -config SOC_CAMERA_MT9T031 - tristate "mt9t031 support (DEPRECATED)" - depends on SOC_CAMERA && I2C - help - This driver supports MT9T031 cameras from Micron. diff --git a/drivers/staging/media/soc_camera/Makefile b/drivers/staging/media/soc_camera/Makefile deleted file mode 100644 index 3a351bd629f5..000000000000 --- a/drivers/staging/media/soc_camera/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_SOC_CAMERA) += soc_camera.o soc_mediabus.o -obj-$(CONFIG_SOC_CAMERA_MT9V022) += soc_mt9v022.o -obj-$(CONFIG_SOC_CAMERA_OV5642) += soc_ov5642.o -obj-$(CONFIG_SOC_CAMERA_OV9740) += soc_ov9740.o -obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o -obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o diff --git a/drivers/staging/media/soc_camera/TODO b/drivers/staging/media/soc_camera/TODO deleted file mode 100644 index 932af6443b67..000000000000 --- a/drivers/staging/media/soc_camera/TODO +++ /dev/null @@ -1,4 +0,0 @@ -The SoC camera framework is obsolete and scheduled for removal in the near -future. Developers are encouraged to convert the drivers to use the -regular V4L2 API if these drivers are still needed (and if someone has the -hardware). diff --git a/drivers/staging/media/soc_camera/imx074.c b/drivers/staging/media/soc_camera/imx074.c deleted file mode 100644 index 14240b74cdd0..000000000000 --- a/drivers/staging/media/soc_camera/imx074.c +++ /dev/null @@ -1,492 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Driver for IMX074 CMOS Image Sensor from Sony - * - * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de> - * - * Partially inspired by the IMX074 driver from the Android / MSM tree - */ -#include <linux/delay.h> -#include <linux/i2c.h> -#include <linux/v4l2-mediabus.h> -#include <linux/slab.h> -#include <linux/videodev2.h> -#include <linux/module.h> - -#include <media/soc_camera.h> -#include <media/v4l2-async.h> -#include <media/v4l2-clk.h> -#include <media/v4l2-subdev.h> - -/* IMX074 registers */ - -#define MODE_SELECT 0x0100 -#define IMAGE_ORIENTATION 0x0101 -#define GROUPED_PARAMETER_HOLD 0x0104 - -/* Integration Time */ -#define COARSE_INTEGRATION_TIME_HI 0x0202 -#define COARSE_INTEGRATION_TIME_LO 0x0203 -/* Gain */ -#define ANALOGUE_GAIN_CODE_GLOBAL_HI 0x0204 -#define ANALOGUE_GAIN_CODE_GLOBAL_LO 0x0205 - -/* PLL registers */ -#define PRE_PLL_CLK_DIV 0x0305 -#define PLL_MULTIPLIER 0x0307 -#define PLSTATIM 0x302b -#define VNDMY_ABLMGSHLMT 0x300a -#define Y_OPBADDR_START_DI 0x3014 -/* mode setting */ -#define FRAME_LENGTH_LINES_HI 0x0340 -#define FRAME_LENGTH_LINES_LO 0x0341 -#define LINE_LENGTH_PCK_HI 0x0342 -#define LINE_LENGTH_PCK_LO 0x0343 -#define YADDR_START 0x0347 -#define YADDR_END 0x034b -#define X_OUTPUT_SIZE_MSB 0x034c -#define X_OUTPUT_SIZE_LSB 0x034d -#define Y_OUTPUT_SIZE_MSB 0x034e -#define Y_OUTPUT_SIZE_LSB 0x034f -#define X_EVEN_INC 0x0381 -#define X_ODD_INC 0x0383 -#define Y_EVEN_INC 0x0385 -#define Y_ODD_INC 0x0387 - -#define HMODEADD 0x3001 -#define VMODEADD 0x3016 -#define VAPPLINE_START 0x3069 -#define VAPPLINE_END 0x306b -#define SHUTTER 0x3086 -#define HADDAVE 0x30e8 -#define LANESEL 0x3301 - -/* IMX074 supported geometry */ -#define IMX074_WIDTH 1052 -#define IMX074_HEIGHT 780 - -/* IMX074 has only one fixed colorspace per pixelcode */ -struct imx074_datafmt { - u32 code; - enum v4l2_colorspace colorspace; -}; - -struct imx074 { - struct v4l2_subdev subdev; - const struct imx074_datafmt *fmt; - struct v4l2_clk *clk; -}; - -static const struct imx074_datafmt imx074_colour_fmts[] = { - {MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB}, -}; - -static struct imx074 *to_imx074(const struct i2c_client *client) -{ - return container_of(i2c_get_clientdata(client), struct imx074, subdev); -} - -/* Find a data format by a pixel code in an array */ -static const struct imx074_datafmt *imx074_find_datafmt(u32 code) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(imx074_colour_fmts); i++) - if (imx074_colour_fmts[i].code == code) - return imx074_colour_fmts + i; - - return NULL; -} - -static int reg_write(struct i2c_client *client, const u16 addr, const u8 data) -{ - struct i2c_adapter *adap = client->adapter; - struct i2c_msg msg; - unsigned char tx[3]; - int ret; - - msg.addr = client->addr; - msg.buf = tx; - msg.len = 3; - msg.flags = 0; - - tx[0] = addr >> 8; - tx[1] = addr & 0xff; - tx[2] = data; - - ret = i2c_transfer(adap, &msg, 1); - - mdelay(2); - - return ret == 1 ? 0 : -EIO; -} - -static int reg_read(struct i2c_client *client, const u16 addr) -{ - u8 buf[2] = {addr >> 8, addr & 0xff}; - int ret; - struct i2c_msg msgs[] = { - { - .addr = client->addr, - .flags = 0, - .len = 2, - .buf = buf, - }, { - .addr = client->addr, - .flags = I2C_M_RD, - .len = 2, - .buf = buf, - }, - }; - - ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); - if (ret < 0) { - dev_warn(&client->dev, "Reading register %x from %x failed\n", - addr, client->addr); - return ret; - } - - return buf[0] & 0xff; /* no sign-extension */ -} - -static int imx074_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - const struct imx074_datafmt *fmt = imx074_find_datafmt(mf->code); - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct imx074 *priv = to_imx074(client); - - if (format->pad) - return -EINVAL; - - dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code); - - if (!fmt) { - /* MIPI CSI could have changed the format, double-check */ - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - mf->code = imx074_colour_fmts[0].code; - mf->colorspace = imx074_colour_fmts[0].colorspace; - } - - mf->width = IMX074_WIDTH; - mf->height = IMX074_HEIGHT; - mf->field = V4L2_FIELD_NONE; - - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - priv->fmt = fmt; - else - cfg->try_fmt = *mf; - - return 0; -} - -static int imx074_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct imx074 *priv = to_imx074(client); - - const struct imx074_datafmt *fmt = priv->fmt; - - if (format->pad) - return -EINVAL; - - mf->code = fmt->code; - mf->colorspace = fmt->colorspace; - mf->width = IMX074_WIDTH; - mf->height = IMX074_HEIGHT; - mf->field = V4L2_FIELD_NONE; - - return 0; -} - -static int imx074_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - sel->r.left = 0; - sel->r.top = 0; - sel->r.width = IMX074_WIDTH; - sel->r.height = IMX074_HEIGHT; - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP: - return 0; - default: - return -EINVAL; - } -} - -static int imx074_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->pad || - (unsigned int)code->index >= ARRAY_SIZE(imx074_colour_fmts)) - return -EINVAL; - - code->code = imx074_colour_fmts[code->index].code; - return 0; -} - -static int imx074_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - /* MODE_SELECT: stream or standby */ - return reg_write(client, MODE_SELECT, !!enable); -} - -static int imx074_s_power(struct v4l2_subdev *sd, int on) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct imx074 *priv = to_imx074(client); - - return soc_camera_set_power(&client->dev, ssdd, priv->clk, on); -} - -static int imx074_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - cfg->type = V4L2_MBUS_CSI2_DPHY; - cfg->flags = V4L2_MBUS_CSI2_2_LANE | - V4L2_MBUS_CSI2_CHANNEL_0 | - V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; - - return 0; -} - -static const struct v4l2_subdev_video_ops imx074_subdev_video_ops = { - .s_stream = imx074_s_stream, - .g_mbus_config = imx074_g_mbus_config, -}; - -static const struct v4l2_subdev_core_ops imx074_subdev_core_ops = { - .s_power = imx074_s_power, -}; - -static const struct v4l2_subdev_pad_ops imx074_subdev_pad_ops = { - .enum_mbus_code = imx074_enum_mbus_code, - .get_selection = imx074_get_selection, - .get_fmt = imx074_get_fmt, - .set_fmt = imx074_set_fmt, -}; - -static const struct v4l2_subdev_ops imx074_subdev_ops = { - .core = &imx074_subdev_core_ops, - .video = &imx074_subdev_video_ops, - .pad = &imx074_subdev_pad_ops, -}; - -static int imx074_video_probe(struct i2c_client *client) -{ - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - int ret; - u16 id; - - ret = imx074_s_power(subdev, 1); - if (ret < 0) - return ret; - - /* Read sensor Model ID */ - ret = reg_read(client, 0); - if (ret < 0) - goto done; - - id = ret << 8; - - ret = reg_read(client, 1); - if (ret < 0) - goto done; - - id |= ret; - - dev_info(&client->dev, "Chip ID 0x%04x detected\n", id); - - if (id != 0x74) { - ret = -ENODEV; - goto done; - } - - /* PLL Setting EXTCLK=24MHz, 22.5times */ - reg_write(client, PLL_MULTIPLIER, 0x2D); - reg_write(client, PRE_PLL_CLK_DIV, 0x02); - reg_write(client, PLSTATIM, 0x4B); - - /* 2-lane mode */ - reg_write(client, 0x3024, 0x00); - - reg_write(client, IMAGE_ORIENTATION, 0x00); - - /* select RAW mode: - * 0x08+0x08 = top 8 bits - * 0x0a+0x08 = compressed 8-bits - * 0x0a+0x0a = 10 bits - */ - reg_write(client, 0x0112, 0x08); - reg_write(client, 0x0113, 0x08); - - /* Base setting for High frame mode */ - reg_write(client, VNDMY_ABLMGSHLMT, 0x80); - reg_write(client, Y_OPBADDR_START_DI, 0x08); - reg_write(client, 0x3015, 0x37); - reg_write(client, 0x301C, 0x01); - reg_write(client, 0x302C, 0x05); - reg_write(client, 0x3031, 0x26); - reg_write(client, 0x3041, 0x60); - reg_write(client, 0x3051, 0x24); - reg_write(client, 0x3053, 0x34); - reg_write(client, 0x3057, 0xC0); - reg_write(client, 0x305C, 0x09); - reg_write(client, 0x305D, 0x07); - reg_write(client, 0x3060, 0x30); - reg_write(client, 0x3065, 0x00); - reg_write(client, 0x30AA, 0x08); - reg_write(client, 0x30AB, 0x1C); - reg_write(client, 0x30B0, 0x32); - reg_write(client, 0x30B2, 0x83); - reg_write(client, 0x30D3, 0x04); - reg_write(client, 0x3106, 0x78); - reg_write(client, 0x310C, 0x82); - reg_write(client, 0x3304, 0x05); - reg_write(client, 0x3305, 0x04); - reg_write(client, 0x3306, 0x11); - reg_write(client, 0x3307, 0x02); - reg_write(client, 0x3308, 0x0C); - reg_write(client, 0x3309, 0x06); - reg_write(client, 0x330A, 0x08); - reg_write(client, 0x330B, 0x04); - reg_write(client, 0x330C, 0x08); - reg_write(client, 0x330D, 0x06); - reg_write(client, 0x330E, 0x01); - reg_write(client, 0x3381, 0x00); - - /* V : 1/2V-addition (1,3), H : 1/2H-averaging (1,3) -> Full HD */ - /* 1608 = 1560 + 48 (black lines) */ - reg_write(client, FRAME_LENGTH_LINES_HI, 0x06); - reg_write(client, FRAME_LENGTH_LINES_LO, 0x48); - reg_write(client, YADDR_START, 0x00); - reg_write(client, YADDR_END, 0x2F); - /* 0x838 == 2104 */ - reg_write(client, X_OUTPUT_SIZE_MSB, 0x08); - reg_write(client, X_OUTPUT_SIZE_LSB, 0x38); - /* 0x618 == 1560 */ - reg_write(client, Y_OUTPUT_SIZE_MSB, 0x06); - reg_write(client, Y_OUTPUT_SIZE_LSB, 0x18); - reg_write(client, X_EVEN_INC, 0x01); - reg_write(client, X_ODD_INC, 0x03); - reg_write(client, Y_EVEN_INC, 0x01); - reg_write(client, Y_ODD_INC, 0x03); - reg_write(client, HMODEADD, 0x00); - reg_write(client, VMODEADD, 0x16); - reg_write(client, VAPPLINE_START, 0x24); - reg_write(client, VAPPLINE_END, 0x53); - reg_write(client, SHUTTER, 0x00); - reg_write(client, HADDAVE, 0x80); - - reg_write(client, LANESEL, 0x00); - - reg_write(client, GROUPED_PARAMETER_HOLD, 0x00); /* off */ - - ret = 0; - -done: - imx074_s_power(subdev, 0); - return ret; -} - -static int imx074_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct imx074 *priv; - struct i2c_adapter *adapter = client->adapter; - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - int ret; - - if (!ssdd) { - dev_err(&client->dev, "IMX074: missing platform data!\n"); - return -EINVAL; - } - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { - dev_warn(&adapter->dev, - "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n"); - return -EIO; - } - - priv = devm_kzalloc(&client->dev, sizeof(struct imx074), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - v4l2_i2c_subdev_init(&priv->subdev, client, &imx074_subdev_ops); - - priv->fmt = &imx074_colour_fmts[0]; - - priv->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(priv->clk)) { - dev_info(&client->dev, "Error %ld getting clock\n", PTR_ERR(priv->clk)); - return -EPROBE_DEFER; - } - - ret = soc_camera_power_init(&client->dev, ssdd); - if (ret < 0) - goto epwrinit; - - ret = imx074_video_probe(client); - if (ret < 0) - goto eprobe; - - ret = v4l2_async_register_subdev(&priv->subdev); - if (!ret) - return 0; - -epwrinit: -eprobe: - v4l2_clk_put(priv->clk); - return ret; -} - -static int imx074_remove(struct i2c_client *client) -{ - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct imx074 *priv = to_imx074(client); - - v4l2_async_unregister_subdev(&priv->subdev); - v4l2_clk_put(priv->clk); - - if (ssdd->free_bus) - ssdd->free_bus(ssdd); - - return 0; -} - -static const struct i2c_device_id imx074_id[] = { - { "imx074", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, imx074_id); - -static struct i2c_driver imx074_i2c_driver = { - .driver = { - .name = "imx074", - }, - .probe = imx074_probe, - .remove = imx074_remove, - .id_table = imx074_id, -}; - -module_i2c_driver(imx074_i2c_driver); - -MODULE_DESCRIPTION("Sony IMX074 Camera driver"); -MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/soc_camera/mt9t031.c b/drivers/staging/media/soc_camera/mt9t031.c deleted file mode 100644 index c14f23221544..000000000000 --- a/drivers/staging/media/soc_camera/mt9t031.c +++ /dev/null @@ -1,853 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Driver for MT9T031 CMOS Image Sensor from Micron - * - * Copyright (C) 2008, Guennadi Liakhovetski, DENX Software Engineering <lg@denx.de> - */ -#include <linux/device.h> -#include <linux/i2c.h> -#include <linux/log2.h> -#include <linux/pm.h> -#include <linux/slab.h> -#include <linux/v4l2-mediabus.h> -#include <linux/videodev2.h> -#include <linux/module.h> - -#include <media/soc_camera.h> -#include <media/v4l2-clk.h> -#include <media/v4l2-subdev.h> -#include <media/v4l2-ctrls.h> - -/* - * ATTENTION: this driver still cannot be used outside of the soc-camera - * framework because of its PM implementation, using the video_device node. - * If hardware becomes available for testing, alternative PM approaches shall - * be considered and tested. - */ - -/* - * mt9t031 i2c address 0x5d - * The platform has to define struct i2c_board_info objects and link to them - * from struct soc_camera_host_desc - */ - -/* mt9t031 selected register addresses */ -#define MT9T031_CHIP_VERSION 0x00 -#define MT9T031_ROW_START 0x01 -#define MT9T031_COLUMN_START 0x02 -#define MT9T031_WINDOW_HEIGHT 0x03 -#define MT9T031_WINDOW_WIDTH 0x04 -#define MT9T031_HORIZONTAL_BLANKING 0x05 -#define MT9T031_VERTICAL_BLANKING 0x06 -#define MT9T031_OUTPUT_CONTROL 0x07 -#define MT9T031_SHUTTER_WIDTH_UPPER 0x08 -#define MT9T031_SHUTTER_WIDTH 0x09 -#define MT9T031_PIXEL_CLOCK_CONTROL 0x0a -#define MT9T031_FRAME_RESTART 0x0b -#define MT9T031_SHUTTER_DELAY 0x0c -#define MT9T031_RESET 0x0d -#define MT9T031_READ_MODE_1 0x1e -#define MT9T031_READ_MODE_2 0x20 -#define MT9T031_READ_MODE_3 0x21 -#define MT9T031_ROW_ADDRESS_MODE 0x22 -#define MT9T031_COLUMN_ADDRESS_MODE 0x23 -#define MT9T031_GLOBAL_GAIN 0x35 -#define MT9T031_CHIP_ENABLE 0xF8 - -#define MT9T031_MAX_HEIGHT 1536 -#define MT9T031_MAX_WIDTH 2048 -#define MT9T031_MIN_HEIGHT 2 -#define MT9T031_MIN_WIDTH 18 -#define MT9T031_HORIZONTAL_BLANK 142 -#define MT9T031_VERTICAL_BLANK 25 -#define MT9T031_COLUMN_SKIP 32 -#define MT9T031_ROW_SKIP 20 - -struct mt9t031 { - struct v4l2_subdev subdev; - struct v4l2_ctrl_handler hdl; - struct { - /* exposure/auto-exposure cluster */ - struct v4l2_ctrl *autoexposure; - struct v4l2_ctrl *exposure; - }; - struct v4l2_rect rect; /* Sensor window */ - struct v4l2_clk *clk; - u16 xskip; - u16 yskip; - unsigned int total_h; - unsigned short y_skip_top; /* Lines to skip at the top */ -}; - -static struct mt9t031 *to_mt9t031(const struct i2c_client *client) -{ - return container_of(i2c_get_clientdata(client), struct mt9t031, subdev); -} - -static int reg_read(struct i2c_client *client, const u8 reg) -{ - return i2c_smbus_read_word_swapped(client, reg); -} - -static int reg_write(struct i2c_client *client, const u8 reg, - const u16 data) -{ - return i2c_smbus_write_word_swapped(client, reg, data); -} - -static int reg_set(struct i2c_client *client, const u8 reg, - const u16 data) -{ - int ret; - - ret = reg_read(client, reg); - if (ret < 0) - return ret; - return reg_write(client, reg, ret | data); -} - -static int reg_clear(struct i2c_client *client, const u8 reg, - const u16 data) -{ - int ret; - - ret = reg_read(client, reg); - if (ret < 0) - return ret; - return reg_write(client, reg, ret & ~data); -} - -static int set_shutter(struct i2c_client *client, const u32 data) -{ - int ret; - - ret = reg_write(client, MT9T031_SHUTTER_WIDTH_UPPER, data >> 16); - - if (ret >= 0) - ret = reg_write(client, MT9T031_SHUTTER_WIDTH, data & 0xffff); - - return ret; -} - -static int get_shutter(struct i2c_client *client, u32 *data) -{ - int ret; - - ret = reg_read(client, MT9T031_SHUTTER_WIDTH_UPPER); - *data = ret << 16; - - if (ret >= 0) - ret = reg_read(client, MT9T031_SHUTTER_WIDTH); - *data |= ret & 0xffff; - - return ret < 0 ? ret : 0; -} - -static int mt9t031_idle(struct i2c_client *client) -{ - int ret; - - /* Disable chip output, synchronous option update */ - ret = reg_write(client, MT9T031_RESET, 1); - if (ret >= 0) - ret = reg_write(client, MT9T031_RESET, 0); - if (ret >= 0) - ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 2); - - return ret >= 0 ? 0 : -EIO; -} - -static int mt9t031_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - - if (enable) - /* Switch to master "normal" mode */ - ret = reg_set(client, MT9T031_OUTPUT_CONTROL, 2); - else - /* Stop sensor readout */ - ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 2); - - if (ret < 0) - return -EIO; - - return 0; -} - -/* target must be _even_ */ -static u16 mt9t031_skip(s32 *source, s32 target, s32 max) -{ - unsigned int skip; - - if (*source < target + target / 2) { - *source = target; - return 1; - } - - skip = min(max, *source + target / 2) / target; - if (skip > 8) - skip = 8; - *source = target * skip; - - return skip; -} - -/* rect is the sensor rectangle, the caller guarantees parameter validity */ -static int mt9t031_set_params(struct i2c_client *client, - struct v4l2_rect *rect, u16 xskip, u16 yskip) -{ - struct mt9t031 *mt9t031 = to_mt9t031(client); - int ret; - u16 xbin, ybin; - const u16 hblank = MT9T031_HORIZONTAL_BLANK, - vblank = MT9T031_VERTICAL_BLANK; - - xbin = min(xskip, (u16)3); - ybin = min(yskip, (u16)3); - - /* - * Could just do roundup(rect->left, [xy]bin * 2); but this is cheaper. - * There is always a valid suitably aligned value. The worst case is - * xbin = 3, width = 2048. Then we will start at 36, the last read out - * pixel will be 2083, which is < 2085 - first black pixel. - * - * MT9T031 datasheet imposes window left border alignment, depending on - * the selected xskip. Failing to conform to this requirement produces - * dark horizontal stripes in the image. However, even obeying to this - * requirement doesn't eliminate the stripes in all configurations. They - * appear "locally reproducibly," but can differ between tests under - * different lighting conditions. - */ - switch (xbin) { - case 1: - rect->left &= ~1; - break; - case 2: - rect->left &= ~3; - break; - case 3: - rect->left = rect->left > roundup(MT9T031_COLUMN_SKIP, 6) ? - (rect->left / 6) * 6 : roundup(MT9T031_COLUMN_SKIP, 6); - } - - rect->top &= ~1; - - dev_dbg(&client->dev, "skip %u:%u, rect %ux%u@%u:%u\n", - xskip, yskip, rect->width, rect->height, rect->left, rect->top); - - /* Disable register update, reconfigure atomically */ - ret = reg_set(client, MT9T031_OUTPUT_CONTROL, 1); - if (ret < 0) - return ret; - - /* Blanking and start values - default... */ - ret = reg_write(client, MT9T031_HORIZONTAL_BLANKING, hblank); - if (ret >= 0) - ret = reg_write(client, MT9T031_VERTICAL_BLANKING, vblank); - - if (yskip != mt9t031->yskip || xskip != mt9t031->xskip) { - /* Binning, skipping */ - if (ret >= 0) - ret = reg_write(client, MT9T031_COLUMN_ADDRESS_MODE, - ((xbin - 1) << 4) | (xskip - 1)); - if (ret >= 0) - ret = reg_write(client, MT9T031_ROW_ADDRESS_MODE, - ((ybin - 1) << 4) | (yskip - 1)); - } - dev_dbg(&client->dev, "new physical left %u, top %u\n", - rect->left, rect->top); - - /* - * The caller provides a supported format, as guaranteed by - * .set_fmt(FORMAT_TRY), soc_camera_s_selection() and soc_camera_cropcap() - */ - if (ret >= 0) - ret = reg_write(client, MT9T031_COLUMN_START, rect->left); - if (ret >= 0) - ret = reg_write(client, MT9T031_ROW_START, rect->top); - if (ret >= 0) - ret = reg_write(client, MT9T031_WINDOW_WIDTH, rect->width - 1); - if (ret >= 0) - ret = reg_write(client, MT9T031_WINDOW_HEIGHT, - rect->height + mt9t031->y_skip_top - 1); - if (ret >= 0 && v4l2_ctrl_g_ctrl(mt9t031->autoexposure) == V4L2_EXPOSURE_AUTO) { - mt9t031->total_h = rect->height + mt9t031->y_skip_top + vblank; - - ret = set_shutter(client, mt9t031->total_h); - } - - /* Re-enable register update, commit all changes */ - if (ret >= 0) - ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 1); - - if (ret >= 0) { - mt9t031->rect = *rect; - mt9t031->xskip = xskip; - mt9t031->yskip = yskip; - } - - return ret < 0 ? ret : 0; -} - -static int mt9t031_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t031 *mt9t031 = to_mt9t031(client); - struct v4l2_rect rect = sel->r; - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || - sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - rect.width = ALIGN(rect.width, 2); - rect.height = ALIGN(rect.height, 2); - - soc_camera_limit_side(&rect.left, &rect.width, - MT9T031_COLUMN_SKIP, MT9T031_MIN_WIDTH, MT9T031_MAX_WIDTH); - - soc_camera_limit_side(&rect.top, &rect.height, - MT9T031_ROW_SKIP, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT); - - return mt9t031_set_params(client, &rect, mt9t031->xskip, mt9t031->yskip); -} - -static int mt9t031_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t031 *mt9t031 = to_mt9t031(client); - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.left = MT9T031_COLUMN_SKIP; - sel->r.top = MT9T031_ROW_SKIP; - sel->r.width = MT9T031_MAX_WIDTH; - sel->r.height = MT9T031_MAX_HEIGHT; - return 0; - case V4L2_SEL_TGT_CROP: - sel->r = mt9t031->rect; - return 0; - default: - return -EINVAL; - } -} - -static int mt9t031_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t031 *mt9t031 = to_mt9t031(client); - - if (format->pad) - return -EINVAL; - - mf->width = mt9t031->rect.width / mt9t031->xskip; - mf->height = mt9t031->rect.height / mt9t031->yskip; - mf->code = MEDIA_BUS_FMT_SBGGR10_1X10; - mf->colorspace = V4L2_COLORSPACE_SRGB; - mf->field = V4L2_FIELD_NONE; - - return 0; -} - -/* - * If a user window larger than sensor window is requested, we'll increase the - * sensor window. - */ -static int mt9t031_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t031 *mt9t031 = to_mt9t031(client); - u16 xskip, yskip; - struct v4l2_rect rect = mt9t031->rect; - - if (format->pad) - return -EINVAL; - - mf->code = MEDIA_BUS_FMT_SBGGR10_1X10; - mf->colorspace = V4L2_COLORSPACE_SRGB; - v4l_bound_align_image( - &mf->width, MT9T031_MIN_WIDTH, MT9T031_MAX_WIDTH, 1, - &mf->height, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT, 1, 0); - - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - cfg->try_fmt = *mf; - return 0; - } - - /* - * Width and height are within limits. - * S_FMT: use binning and skipping for scaling - */ - xskip = mt9t031_skip(&rect.width, mf->width, MT9T031_MAX_WIDTH); - yskip = mt9t031_skip(&rect.height, mf->height, MT9T031_MAX_HEIGHT); - - mf->code = MEDIA_BUS_FMT_SBGGR10_1X10; - mf->colorspace = V4L2_COLORSPACE_SRGB; - - /* mt9t031_set_params() doesn't change width and height */ - return mt9t031_set_params(client, &rect, xskip, yskip); -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int mt9t031_g_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg > 0xff) - return -EINVAL; - - reg->size = 1; - reg->val = reg_read(client, reg->reg); - - if (reg->val > 0xffff) - return -EIO; - - return 0; -} - -static int mt9t031_s_register(struct v4l2_subdev *sd, - const struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg > 0xff) - return -EINVAL; - - if (reg_write(client, reg->reg, reg->val) < 0) - return -EIO; - - return 0; -} -#endif - -static int mt9t031_g_volatile_ctrl(struct v4l2_ctrl *ctrl) -{ - struct mt9t031 *mt9t031 = container_of(ctrl->handler, - struct mt9t031, hdl); - const u32 shutter_max = MT9T031_MAX_HEIGHT + MT9T031_VERTICAL_BLANK; - s32 min, max; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE_AUTO: - min = mt9t031->exposure->minimum; - max = mt9t031->exposure->maximum; - mt9t031->exposure->val = - (shutter_max / 2 + (mt9t031->total_h - 1) * (max - min)) - / shutter_max + min; - break; - } - return 0; -} - -static int mt9t031_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct mt9t031 *mt9t031 = container_of(ctrl->handler, - struct mt9t031, hdl); - struct v4l2_subdev *sd = &mt9t031->subdev; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct v4l2_ctrl *exp = mt9t031->exposure; - int data; - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - if (ctrl->val) - data = reg_set(client, MT9T031_READ_MODE_2, 0x8000); - else - data = reg_clear(client, MT9T031_READ_MODE_2, 0x8000); - if (data < 0) - return -EIO; - return 0; - case V4L2_CID_HFLIP: - if (ctrl->val) - data = reg_set(client, MT9T031_READ_MODE_2, 0x4000); - else - data = reg_clear(client, MT9T031_READ_MODE_2, 0x4000); - if (data < 0) - return -EIO; - return 0; - case V4L2_CID_GAIN: - /* See Datasheet Table 7, Gain settings. */ - if (ctrl->val <= ctrl->default_value) { - /* Pack it into 0..1 step 0.125, register values 0..8 */ - unsigned long range = ctrl->default_value - ctrl->minimum; - data = ((ctrl->val - (s32)ctrl->minimum) * 8 + range / 2) / range; - - dev_dbg(&client->dev, "Setting gain %d\n", data); - data = reg_write(client, MT9T031_GLOBAL_GAIN, data); - if (data < 0) - return -EIO; - } else { - /* Pack it into 1.125..128 variable step, register values 9..0x7860 */ - /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */ - unsigned long range = ctrl->maximum - ctrl->default_value - 1; - /* calculated gain: map 65..127 to 9..1024 step 0.125 */ - unsigned long gain = ((ctrl->val - (s32)ctrl->default_value - 1) * - 1015 + range / 2) / range + 9; - - if (gain <= 32) /* calculated gain 9..32 -> 9..32 */ - data = gain; - else if (gain <= 64) /* calculated gain 33..64 -> 0x51..0x60 */ - data = ((gain - 32) * 16 + 16) / 32 + 80; - else - /* calculated gain 65..1024 -> (1..120) << 8 + 0x60 */ - data = (((gain - 64 + 7) * 32) & 0xff00) | 0x60; - - dev_dbg(&client->dev, "Set gain from 0x%x to 0x%x\n", - reg_read(client, MT9T031_GLOBAL_GAIN), data); - data = reg_write(client, MT9T031_GLOBAL_GAIN, data); - if (data < 0) - return -EIO; - } - return 0; - - case V4L2_CID_EXPOSURE_AUTO: - if (ctrl->val == V4L2_EXPOSURE_MANUAL) { - unsigned int range = exp->maximum - exp->minimum; - unsigned int shutter = ((exp->val - (s32)exp->minimum) * 1048 + - range / 2) / range + 1; - u32 old; - - get_shutter(client, &old); - dev_dbg(&client->dev, "Set shutter from %u to %u\n", - old, shutter); - if (set_shutter(client, shutter) < 0) - return -EIO; - } else { - const u16 vblank = MT9T031_VERTICAL_BLANK; - mt9t031->total_h = mt9t031->rect.height + - mt9t031->y_skip_top + vblank; - - if (set_shutter(client, mt9t031->total_h) < 0) - return -EIO; - } - return 0; - default: - return -EINVAL; - } - return 0; -} - -/* - * Power Management: - * This function does nothing for now but must be present for pm to work - */ -static int mt9t031_runtime_suspend(struct device *dev) -{ - return 0; -} - -/* - * Power Management: - * COLUMN_ADDRESS_MODE and ROW_ADDRESS_MODE are not rewritten if unchanged - * they are however changed at reset if the platform hook is present - * thus we rewrite them with the values stored by the driver - */ -static int mt9t031_runtime_resume(struct device *dev) -{ - struct video_device *vdev = to_video_device(dev); - struct v4l2_subdev *sd = soc_camera_vdev_to_subdev(vdev); - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t031 *mt9t031 = to_mt9t031(client); - - int ret; - u16 xbin, ybin; - - xbin = min(mt9t031->xskip, (u16)3); - ybin = min(mt9t031->yskip, (u16)3); - - ret = reg_write(client, MT9T031_COLUMN_ADDRESS_MODE, - ((xbin - 1) << 4) | (mt9t031->xskip - 1)); - if (ret < 0) - return ret; - - ret = reg_write(client, MT9T031_ROW_ADDRESS_MODE, - ((ybin - 1) << 4) | (mt9t031->yskip - 1)); - if (ret < 0) - return ret; - - return 0; -} - -static const struct dev_pm_ops mt9t031_dev_pm_ops = { - .runtime_suspend = mt9t031_runtime_suspend, - .runtime_resume = mt9t031_runtime_resume, -}; - -static const struct device_type mt9t031_dev_type = { - .name = "MT9T031", - .pm = &mt9t031_dev_pm_ops, -}; - -static int mt9t031_s_power(struct v4l2_subdev *sd, int on) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct video_device *vdev = soc_camera_i2c_to_vdev(client); - struct mt9t031 *mt9t031 = to_mt9t031(client); - int ret; - - if (on) { - ret = soc_camera_power_on(&client->dev, ssdd, mt9t031->clk); - if (ret < 0) - return ret; - if (vdev) - /* Not needed during probing, when vdev isn't available yet */ - vdev->dev.type = &mt9t031_dev_type; - } else { - if (vdev) - vdev->dev.type = NULL; - soc_camera_power_off(&client->dev, ssdd, mt9t031->clk); - } - - return 0; -} - -/* - * Interface active, can use i2c. If it fails, it can indeed mean, that - * this wasn't our capture interface, so, we wait for the right one - */ -static int mt9t031_video_probe(struct i2c_client *client) -{ - struct mt9t031 *mt9t031 = to_mt9t031(client); - s32 data; - int ret; - - ret = mt9t031_s_power(&mt9t031->subdev, 1); - if (ret < 0) - return ret; - - ret = mt9t031_idle(client); - if (ret < 0) { - dev_err(&client->dev, "Failed to initialise the camera\n"); - goto done; - } - - /* Read out the chip version register */ - data = reg_read(client, MT9T031_CHIP_VERSION); - - switch (data) { - case 0x1621: - break; - default: - dev_err(&client->dev, - "No MT9T031 chip detected, register read %x\n", data); - ret = -ENODEV; - goto done; - } - - dev_info(&client->dev, "Detected a MT9T031 chip ID %x\n", data); - - ret = v4l2_ctrl_handler_setup(&mt9t031->hdl); - -done: - mt9t031_s_power(&mt9t031->subdev, 0); - - return ret; -} - -static int mt9t031_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t031 *mt9t031 = to_mt9t031(client); - - *lines = mt9t031->y_skip_top; - - return 0; -} - -static const struct v4l2_ctrl_ops mt9t031_ctrl_ops = { - .g_volatile_ctrl = mt9t031_g_volatile_ctrl, - .s_ctrl = mt9t031_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops mt9t031_subdev_core_ops = { - .s_power = mt9t031_s_power, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = mt9t031_g_register, - .s_register = mt9t031_s_register, -#endif -}; - -static int mt9t031_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->pad || code->index) - return -EINVAL; - - code->code = MEDIA_BUS_FMT_SBGGR10_1X10; - return 0; -} - -static int mt9t031_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING | - V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_HSYNC_ACTIVE_HIGH | - V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH; - cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); - - return 0; -} - -static int mt9t031_s_mbus_config(struct v4l2_subdev *sd, - const struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - if (soc_camera_apply_board_flags(ssdd, cfg) & - V4L2_MBUS_PCLK_SAMPLE_FALLING) - return reg_clear(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); - else - return reg_set(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); -} - -static const struct v4l2_subdev_video_ops mt9t031_subdev_video_ops = { - .s_stream = mt9t031_s_stream, - .g_mbus_config = mt9t031_g_mbus_config, - .s_mbus_config = mt9t031_s_mbus_config, -}; - -static const struct v4l2_subdev_sensor_ops mt9t031_subdev_sensor_ops = { - .g_skip_top_lines = mt9t031_g_skip_top_lines, -}; - -static const struct v4l2_subdev_pad_ops mt9t031_subdev_pad_ops = { - .enum_mbus_code = mt9t031_enum_mbus_code, - .get_selection = mt9t031_get_selection, - .set_selection = mt9t031_set_selection, - .get_fmt = mt9t031_get_fmt, - .set_fmt = mt9t031_set_fmt, -}; - -static const struct v4l2_subdev_ops mt9t031_subdev_ops = { - .core = &mt9t031_subdev_core_ops, - .video = &mt9t031_subdev_video_ops, - .sensor = &mt9t031_subdev_sensor_ops, - .pad = &mt9t031_subdev_pad_ops, -}; - -static int mt9t031_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct mt9t031 *mt9t031; - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct i2c_adapter *adapter = client->adapter; - int ret; - - if (!ssdd) { - dev_err(&client->dev, "MT9T031 driver needs platform data\n"); - return -EINVAL; - } - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { - dev_warn(&adapter->dev, - "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); - return -EIO; - } - - mt9t031 = devm_kzalloc(&client->dev, sizeof(struct mt9t031), GFP_KERNEL); - if (!mt9t031) - return -ENOMEM; - - v4l2_i2c_subdev_init(&mt9t031->subdev, client, &mt9t031_subdev_ops); - v4l2_ctrl_handler_init(&mt9t031->hdl, 5); - v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops, - V4L2_CID_GAIN, 0, 127, 1, 64); - - /* - * Simulated autoexposure. If enabled, we calculate shutter width - * ourselves in the driver based on vertical blanking and frame width - */ - mt9t031->autoexposure = v4l2_ctrl_new_std_menu(&mt9t031->hdl, - &mt9t031_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0, - V4L2_EXPOSURE_AUTO); - mt9t031->exposure = v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops, - V4L2_CID_EXPOSURE, 1, 255, 1, 255); - - mt9t031->subdev.ctrl_handler = &mt9t031->hdl; - if (mt9t031->hdl.error) - return mt9t031->hdl.error; - - v4l2_ctrl_auto_cluster(2, &mt9t031->autoexposure, - V4L2_EXPOSURE_MANUAL, true); - - mt9t031->y_skip_top = 0; - mt9t031->rect.left = MT9T031_COLUMN_SKIP; - mt9t031->rect.top = MT9T031_ROW_SKIP; - mt9t031->rect.width = MT9T031_MAX_WIDTH; - mt9t031->rect.height = MT9T031_MAX_HEIGHT; - - mt9t031->xskip = 1; - mt9t031->yskip = 1; - - mt9t031->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(mt9t031->clk)) { - ret = PTR_ERR(mt9t031->clk); - goto eclkget; - } - - ret = mt9t031_video_probe(client); - if (ret) { - v4l2_clk_put(mt9t031->clk); -eclkget: - v4l2_ctrl_handler_free(&mt9t031->hdl); - } - - return ret; -} - -static int mt9t031_remove(struct i2c_client *client) -{ - struct mt9t031 *mt9t031 = to_mt9t031(client); - - v4l2_clk_put(mt9t031->clk); - v4l2_device_unregister_subdev(&mt9t031->subdev); - v4l2_ctrl_handler_free(&mt9t031->hdl); - - return 0; -} - -static const struct i2c_device_id mt9t031_id[] = { - { "mt9t031", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, mt9t031_id); - -static struct i2c_driver mt9t031_i2c_driver = { - .driver = { - .name = "mt9t031", - }, - .probe = mt9t031_probe, - .remove = mt9t031_remove, - .id_table = mt9t031_id, -}; - -module_i2c_driver(mt9t031_i2c_driver); - -MODULE_DESCRIPTION("Micron MT9T031 Camera driver"); -MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/soc_camera/soc-camera.rst b/drivers/staging/media/soc_camera/soc-camera.rst deleted file mode 100644 index 7c39711aebf8..000000000000 --- a/drivers/staging/media/soc_camera/soc-camera.rst +++ /dev/null @@ -1,171 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0 - -The Soc-Camera Drivers -====================== - -Author: Guennadi Liakhovetski <g.liakhovetski@gmx.de> - -Terminology ------------ - -The following terms are used in this document: - - camera / camera device / camera sensor - a video-camera sensor chip, capable - of connecting to a variety of systems and interfaces, typically uses i2c for - control and configuration, and a parallel or a serial bus for data. - - camera host - an interface, to which a camera is connected. Typically a - specialised interface, present on many SoCs, e.g. PXA27x and PXA3xx, SuperH, - i.MX27, i.MX31. - - camera host bus - a connection between a camera host and a camera. Can be - parallel or serial, consists of data and control lines, e.g. clock, vertical - and horizontal synchronization signals. - -Purpose of the soc-camera subsystem ------------------------------------ - -The soc-camera subsystem initially provided a unified API between camera host -drivers and camera sensor drivers. Later the soc-camera sensor API has been -replaced with the V4L2 standard subdev API. This also made camera driver re-use -with non-soc-camera hosts possible. The camera host API to the soc-camera core -has been preserved. - -Soc-camera implements a V4L2 interface to the user, currently only the "mmap" -method is supported by host drivers. However, the soc-camera core also provides -support for the "read" method. - -The subsystem has been designed to support multiple camera host interfaces and -multiple cameras per interface, although most applications have only one camera -sensor. - -Existing drivers ----------------- - -As of 3.7 there are seven host drivers in the mainline: atmel-isi.c, -mx1_camera.c (broken, scheduled for removal), mx2_camera.c, mx3_camera.c, -omap1_camera.c, pxa_camera.c, sh_mobile_ceu_camera.c, and multiple sensor -drivers under drivers/media/i2c/soc_camera/. - -Camera host API ---------------- - -A host camera driver is registered using the - -.. code-block:: none - - soc_camera_host_register(struct soc_camera_host *); - -function. The host object can be initialized as follows: - -.. code-block:: none - - struct soc_camera_host *ici; - ici->drv_name = DRV_NAME; - ici->ops = &camera_host_ops; - ici->priv = pcdev; - ici->v4l2_dev.dev = &pdev->dev; - ici->nr = pdev->id; - -All camera host methods are passed in a struct soc_camera_host_ops: - -.. code-block:: none - - static struct soc_camera_host_ops camera_host_ops = { - .owner = THIS_MODULE, - .add = camera_add_device, - .remove = camera_remove_device, - .set_fmt = camera_set_fmt_cap, - .try_fmt = camera_try_fmt_cap, - .init_videobuf2 = camera_init_videobuf2, - .poll = camera_poll, - .querycap = camera_querycap, - .set_bus_param = camera_set_bus_param, - /* The rest of host operations are optional */ - }; - -.add and .remove methods are called when a sensor is attached to or detached -from the host. .set_bus_param is used to configure physical connection -parameters between the host and the sensor. .init_videobuf2 is called by -soc-camera core when a video-device is opened, the host driver would typically -call vb2_queue_init() in this method. Further video-buffer management is -implemented completely by the specific camera host driver. If the host driver -supports non-standard pixel format conversion, it should implement a -.get_formats and, possibly, a .put_formats operations. See below for more -details about format conversion. The rest of the methods are called from -respective V4L2 operations. - -Camera API ----------- - -Sensor drivers can use struct soc_camera_link, typically provided by the -platform, and used to specify to which camera host bus the sensor is connected, -and optionally provide platform .power and .reset methods for the camera. This -struct is provided to the camera driver via the I2C client device platform data -and can be obtained, using the soc_camera_i2c_to_link() macro. Care should be -taken, when using soc_camera_vdev_to_subdev() and when accessing struct -soc_camera_device, using v4l2_get_subdev_hostdata(): both only work, when -running on an soc-camera host. The actual camera driver operation is implemented -using the V4L2 subdev API. Additionally soc-camera camera drivers can use -auxiliary soc-camera helper functions like soc_camera_power_on() and -soc_camera_power_off(), which switch regulators, provided by the platform and call -board-specific power switching methods. soc_camera_apply_board_flags() takes -camera bus configuration capability flags and applies any board transformations, -e.g. signal polarity inversion. soc_mbus_get_fmtdesc() can be used to obtain a -pixel format descriptor, corresponding to a certain media-bus pixel format code. -soc_camera_limit_side() can be used to restrict beginning and length of a frame -side, based on camera capabilities. - -VIDIOC_S_CROP and VIDIOC_S_FMT behaviour ----------------------------------------- - -Above user ioctls modify image geometry as follows: - -VIDIOC_S_CROP: sets location and sizes of the sensor window. Unit is one sensor -pixel. Changing sensor window sizes preserves any scaling factors, therefore -user window sizes change as well. - -VIDIOC_S_FMT: sets user window. Should preserve previously set sensor window as -much as possible by modifying scaling factors. If the sensor window cannot be -preserved precisely, it may be changed too. - -In soc-camera there are two locations, where scaling and cropping can take -place: in the camera driver and in the host driver. User ioctls are first passed -to the host driver, which then generally passes them down to the camera driver. -It is more efficient to perform scaling and cropping in the camera driver to -save camera bus bandwidth and maximise the framerate. However, if the camera -driver failed to set the required parameters with sufficient precision, the host -driver may decide to also use its own scaling and cropping to fulfill the user's -request. - -Camera drivers are interfaced to the soc-camera core and to host drivers over -the v4l2-subdev API, which is completely functional, it doesn't pass any data. -Therefore all camera drivers shall reply to .g_fmt() requests with their current -output geometry. This is necessary to correctly configure the camera bus. -.s_fmt() and .try_fmt() have to be implemented too. Sensor window and scaling -factors have to be maintained by camera drivers internally. According to the -V4L2 API all capture drivers must support the VIDIOC_CROPCAP ioctl, hence we -rely on camera drivers implementing .cropcap(). If the camera driver does not -support cropping, it may choose to not implement .s_crop(), but to enable -cropping support by the camera host driver at least the .g_crop method must be -implemented. - -User window geometry is kept in .user_width and .user_height fields in struct -soc_camera_device and used by the soc-camera core and host drivers. The core -updates these fields upon successful completion of a .s_fmt() call, but if these -fields change elsewhere, e.g. during .s_crop() processing, the host driver is -responsible for updating them. - -Format conversion ------------------ - -V4L2 distinguishes between pixel formats, as they are stored in memory, and as -they are transferred over a media bus. Soc-camera provides support to -conveniently manage these formats. A table of standard transformations is -maintained by soc-camera core, which describes, what FOURCC pixel format will -be obtained, if a media-bus pixel format is stored in memory according to -certain rules. E.g. if MEDIA_BUS_FMT_YUYV8_2X8 data is sampled with 8 bits per -sample and stored in memory in the little-endian order with no gaps between -bytes, data in memory will represent the V4L2_PIX_FMT_YUYV FOURCC format. These -standard transformations will be used by soc-camera or by camera host drivers to -configure camera drivers to produce the FOURCC format, requested by the user, -using the VIDIOC_S_FMT ioctl(). Apart from those standard format conversions, -host drivers can also provide their own conversion rules by implementing a -.get_formats and, if required, a .put_formats methods. diff --git a/drivers/staging/media/soc_camera/soc_camera.c b/drivers/staging/media/soc_camera/soc_camera.c deleted file mode 100644 index 39f513f69b89..000000000000 --- a/drivers/staging/media/soc_camera/soc_camera.c +++ /dev/null @@ -1,2164 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * camera image capture (abstract) bus driver - * - * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> - * - * This driver provides an interface between platform-specific camera - * buses and camera devices. It should be used if the camera is - * connected not over a "proper" bus like PCI or USB, but over a - * special bus, like, for example, the Quick Capture interface on PXA270 - * SoCs. Later it should also be used for i.MX31 SoCs from Freescale. - * It can handle multiple cameras and / or multiple buses, which can - * be used, e.g., in stereo-vision applications. - */ -#include <linux/device.h> -#include <linux/err.h> -#include <linux/i2c.h> -#include <linux/init.h> -#include <linux/list.h> -#include <linux/module.h> -#include <linux/mutex.h> -#include <linux/of_graph.h> -#include <linux/platform_device.h> -#include <linux/pm_runtime.h> -#include <linux/regulator/consumer.h> -#include <linux/slab.h> -#include <linux/vmalloc.h> - -#include <media/soc_camera.h> -#include <media/drv-intf/soc_mediabus.h> -#include <media/v4l2-async.h> -#include <media/v4l2-clk.h> -#include <media/v4l2-common.h> -#include <media/v4l2-ioctl.h> -#include <media/v4l2-dev.h> -#include <media/v4l2-fwnode.h> -#include <media/videobuf2-v4l2.h> - -/* Default to VGA resolution */ -#define DEFAULT_WIDTH 640 -#define DEFAULT_HEIGHT 480 - -#define MAP_MAX_NUM 32 -static DECLARE_BITMAP(device_map, MAP_MAX_NUM); -static LIST_HEAD(hosts); -static LIST_HEAD(devices); -/* - * Protects lists and bitmaps of hosts and devices. - * Lock nesting: Ok to take ->host_lock under list_lock. - */ -static DEFINE_MUTEX(list_lock); - -struct soc_camera_async_client { - struct v4l2_async_subdev *sensor; - struct v4l2_async_notifier notifier; - struct platform_device *pdev; - struct list_head list; /* needed for clean up */ -}; - -static int soc_camera_video_start(struct soc_camera_device *icd); -static int video_dev_create(struct soc_camera_device *icd); - -int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd, - struct v4l2_clk *clk) -{ - int ret; - bool clock_toggle; - - if (clk && (!ssdd->unbalanced_power || - !test_and_set_bit(0, &ssdd->clock_state))) { - ret = v4l2_clk_enable(clk); - if (ret < 0) { - dev_err(dev, "Cannot enable clock: %d\n", ret); - return ret; - } - clock_toggle = true; - } else { - clock_toggle = false; - } - - ret = regulator_bulk_enable(ssdd->sd_pdata.num_regulators, - ssdd->sd_pdata.regulators); - if (ret < 0) { - dev_err(dev, "Cannot enable regulators\n"); - goto eregenable; - } - - if (ssdd->power) { - ret = ssdd->power(dev, 1); - if (ret < 0) { - dev_err(dev, - "Platform failed to power-on the camera.\n"); - goto epwron; - } - } - - return 0; - -epwron: - regulator_bulk_disable(ssdd->sd_pdata.num_regulators, - ssdd->sd_pdata.regulators); -eregenable: - if (clock_toggle) - v4l2_clk_disable(clk); - - return ret; -} -EXPORT_SYMBOL(soc_camera_power_on); - -int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd, - struct v4l2_clk *clk) -{ - int ret = 0; - int err; - - if (ssdd->power) { - err = ssdd->power(dev, 0); - if (err < 0) { - dev_err(dev, - "Platform failed to power-off the camera.\n"); - ret = err; - } - } - - err = regulator_bulk_disable(ssdd->sd_pdata.num_regulators, - ssdd->sd_pdata.regulators); - if (err < 0) { - dev_err(dev, "Cannot disable regulators\n"); - ret = ret ? : err; - } - - if (clk && (!ssdd->unbalanced_power || test_and_clear_bit(0, &ssdd->clock_state))) - v4l2_clk_disable(clk); - - return ret; -} -EXPORT_SYMBOL(soc_camera_power_off); - -int soc_camera_power_init(struct device *dev, struct soc_camera_subdev_desc *ssdd) -{ - /* Should not have any effect in synchronous case */ - return devm_regulator_bulk_get(dev, ssdd->sd_pdata.num_regulators, - ssdd->sd_pdata.regulators); -} -EXPORT_SYMBOL(soc_camera_power_init); - -static int __soc_camera_power_on(struct soc_camera_device *icd) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - int ret; - - ret = v4l2_subdev_call(sd, core, s_power, 1); - if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) - return ret; - - return 0; -} - -static int __soc_camera_power_off(struct soc_camera_device *icd) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - int ret; - - ret = v4l2_subdev_call(sd, core, s_power, 0); - if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) - return ret; - - return 0; -} - -static int soc_camera_clock_start(struct soc_camera_host *ici) -{ - int ret; - - if (!ici->ops->clock_start) - return 0; - - mutex_lock(&ici->clk_lock); - ret = ici->ops->clock_start(ici); - mutex_unlock(&ici->clk_lock); - - return ret; -} - -static void soc_camera_clock_stop(struct soc_camera_host *ici) -{ - if (!ici->ops->clock_stop) - return; - - mutex_lock(&ici->clk_lock); - ici->ops->clock_stop(ici); - mutex_unlock(&ici->clk_lock); -} - -const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc( - struct soc_camera_device *icd, unsigned int fourcc) -{ - unsigned int i; - - for (i = 0; i < icd->num_user_formats; i++) - if (icd->user_formats[i].host_fmt->fourcc == fourcc) - return icd->user_formats + i; - return NULL; -} -EXPORT_SYMBOL(soc_camera_xlate_by_fourcc); - -/** - * soc_camera_apply_board_flags() - apply platform SOCAM_SENSOR_INVERT_* flags - * @ssdd: camera platform parameters - * @cfg: media bus configuration - * @return: resulting flags - */ -unsigned long soc_camera_apply_board_flags(struct soc_camera_subdev_desc *ssdd, - const struct v4l2_mbus_config *cfg) -{ - unsigned long f, flags = cfg->flags; - - /* If only one of the two polarities is supported, switch to the opposite */ - if (ssdd->flags & SOCAM_SENSOR_INVERT_HSYNC) { - f = flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW); - if (f == V4L2_MBUS_HSYNC_ACTIVE_HIGH || f == V4L2_MBUS_HSYNC_ACTIVE_LOW) - flags ^= V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW; - } - - if (ssdd->flags & SOCAM_SENSOR_INVERT_VSYNC) { - f = flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW); - if (f == V4L2_MBUS_VSYNC_ACTIVE_HIGH || f == V4L2_MBUS_VSYNC_ACTIVE_LOW) - flags ^= V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW; - } - - if (ssdd->flags & SOCAM_SENSOR_INVERT_PCLK) { - f = flags & (V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING); - if (f == V4L2_MBUS_PCLK_SAMPLE_RISING || f == V4L2_MBUS_PCLK_SAMPLE_FALLING) - flags ^= V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING; - } - - return flags; -} -EXPORT_SYMBOL(soc_camera_apply_board_flags); - -#define pixfmtstr(x) (x) & 0xff, ((x) >> 8) & 0xff, ((x) >> 16) & 0xff, \ - ((x) >> 24) & 0xff - -static int soc_camera_try_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - const struct soc_camera_format_xlate *xlate; - struct v4l2_pix_format *pix = &f->fmt.pix; - int ret; - - dev_dbg(icd->pdev, "TRY_FMT(%c%c%c%c, %ux%u)\n", - pixfmtstr(pix->pixelformat), pix->width, pix->height); - - if (pix->pixelformat != V4L2_PIX_FMT_JPEG && - !(ici->capabilities & SOCAM_HOST_CAP_STRIDE)) { - pix->bytesperline = 0; - pix->sizeimage = 0; - } - - ret = ici->ops->try_fmt(icd, f); - if (ret < 0) - return ret; - - xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); - if (!xlate) - return -EINVAL; - - ret = soc_mbus_bytes_per_line(pix->width, xlate->host_fmt); - if (ret < 0) - return ret; - - pix->bytesperline = max_t(u32, pix->bytesperline, ret); - - ret = soc_mbus_image_size(xlate->host_fmt, pix->bytesperline, - pix->height); - if (ret < 0) - return ret; - - pix->sizeimage = max_t(u32, pix->sizeimage, ret); - - return 0; -} - -static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct soc_camera_device *icd = file->private_data; - - WARN_ON(priv != file->private_data); - - /* Only single-plane capture is supported so far */ - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - /* limit format to hardware capabilities */ - return soc_camera_try_fmt(icd, f); -} - -static int soc_camera_enum_input(struct file *file, void *priv, - struct v4l2_input *inp) -{ - struct soc_camera_device *icd = file->private_data; - - if (inp->index != 0) - return -EINVAL; - - /* default is camera */ - inp->type = V4L2_INPUT_TYPE_CAMERA; - inp->std = icd->vdev->tvnorms; - strscpy(inp->name, "Camera", sizeof(inp->name)); - - return 0; -} - -static int soc_camera_g_input(struct file *file, void *priv, unsigned int *i) -{ - *i = 0; - - return 0; -} - -static int soc_camera_s_input(struct file *file, void *priv, unsigned int i) -{ - if (i > 0) - return -EINVAL; - - return 0; -} - -static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id a) -{ - struct soc_camera_device *icd = file->private_data; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - - return v4l2_subdev_call(sd, video, s_std, a); -} - -static int soc_camera_g_std(struct file *file, void *priv, v4l2_std_id *a) -{ - struct soc_camera_device *icd = file->private_data; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - - return v4l2_subdev_call(sd, video, g_std, a); -} - -static int soc_camera_enum_framesizes(struct file *file, void *fh, - struct v4l2_frmsizeenum *fsize) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - return ici->ops->enum_framesizes(icd, fsize); -} - -static int soc_camera_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *p) -{ - int ret; - struct soc_camera_device *icd = file->private_data; - - WARN_ON(priv != file->private_data); - - if (icd->streamer && icd->streamer != file) - return -EBUSY; - - ret = vb2_reqbufs(&icd->vb2_vidq, p); - if (!ret) - icd->streamer = p->count ? file : NULL; - return ret; -} - -static int soc_camera_querybuf(struct file *file, void *priv, - struct v4l2_buffer *p) -{ - struct soc_camera_device *icd = file->private_data; - - WARN_ON(priv != file->private_data); - - return vb2_querybuf(&icd->vb2_vidq, p); -} - -static int soc_camera_qbuf(struct file *file, void *priv, - struct v4l2_buffer *p) -{ - struct soc_camera_device *icd = file->private_data; - - WARN_ON(priv != file->private_data); - - if (icd->streamer != file) - return -EBUSY; - - return vb2_qbuf(&icd->vb2_vidq, NULL, p); -} - -static int soc_camera_dqbuf(struct file *file, void *priv, - struct v4l2_buffer *p) -{ - struct soc_camera_device *icd = file->private_data; - - WARN_ON(priv != file->private_data); - - if (icd->streamer != file) - return -EBUSY; - - return vb2_dqbuf(&icd->vb2_vidq, p, file->f_flags & O_NONBLOCK); -} - -static int soc_camera_create_bufs(struct file *file, void *priv, - struct v4l2_create_buffers *create) -{ - struct soc_camera_device *icd = file->private_data; - int ret; - - if (icd->streamer && icd->streamer != file) - return -EBUSY; - - ret = vb2_create_bufs(&icd->vb2_vidq, create); - if (!ret) - icd->streamer = file; - return ret; -} - -static int soc_camera_prepare_buf(struct file *file, void *priv, - struct v4l2_buffer *b) -{ - struct soc_camera_device *icd = file->private_data; - - return vb2_prepare_buf(&icd->vb2_vidq, NULL, b); -} - -static int soc_camera_expbuf(struct file *file, void *priv, - struct v4l2_exportbuffer *p) -{ - struct soc_camera_device *icd = file->private_data; - - if (icd->streamer && icd->streamer != file) - return -EBUSY; - return vb2_expbuf(&icd->vb2_vidq, p); -} - -/* Always entered with .host_lock held */ -static int soc_camera_init_user_formats(struct soc_camera_device *icd) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - unsigned int i, fmts = 0, raw_fmts = 0; - int ret; - struct v4l2_subdev_mbus_code_enum code = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - - while (!v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code)) { - raw_fmts++; - code.index++; - } - - if (!ici->ops->get_formats) - /* - * Fallback mode - the host will have to serve all - * sensor-provided formats one-to-one to the user - */ - fmts = raw_fmts; - else - /* - * First pass - only count formats this host-sensor - * configuration can provide - */ - for (i = 0; i < raw_fmts; i++) { - ret = ici->ops->get_formats(icd, i, NULL); - if (ret < 0) - return ret; - fmts += ret; - } - - if (!fmts) - return -ENXIO; - - icd->user_formats = - vmalloc(array_size(fmts, - sizeof(struct soc_camera_format_xlate))); - if (!icd->user_formats) - return -ENOMEM; - - dev_dbg(icd->pdev, "Found %d supported formats.\n", fmts); - - /* Second pass - actually fill data formats */ - fmts = 0; - for (i = 0; i < raw_fmts; i++) - if (!ici->ops->get_formats) { - code.index = i; - v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code); - icd->user_formats[fmts].host_fmt = - soc_mbus_get_fmtdesc(code.code); - if (icd->user_formats[fmts].host_fmt) - icd->user_formats[fmts++].code = code.code; - } else { - ret = ici->ops->get_formats(icd, i, - &icd->user_formats[fmts]); - if (ret < 0) - goto egfmt; - fmts += ret; - } - - icd->num_user_formats = fmts; - icd->current_fmt = &icd->user_formats[0]; - - return 0; - -egfmt: - vfree(icd->user_formats); - return ret; -} - -/* Always entered with .host_lock held */ -static void soc_camera_free_user_formats(struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - if (ici->ops->put_formats) - ici->ops->put_formats(icd); - icd->current_fmt = NULL; - icd->num_user_formats = 0; - vfree(icd->user_formats); - icd->user_formats = NULL; -} - -/* Called with .vb_lock held, or from the first open(2), see comment there */ -static int soc_camera_set_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct v4l2_pix_format *pix = &f->fmt.pix; - int ret; - - dev_dbg(icd->pdev, "S_FMT(%c%c%c%c, %ux%u)\n", - pixfmtstr(pix->pixelformat), pix->width, pix->height); - - /* We always call try_fmt() before set_fmt() or set_selection() */ - ret = soc_camera_try_fmt(icd, f); - if (ret < 0) - return ret; - - ret = ici->ops->set_fmt(icd, f); - if (ret < 0) { - return ret; - } else if (!icd->current_fmt || - icd->current_fmt->host_fmt->fourcc != pix->pixelformat) { - dev_err(icd->pdev, - "Host driver hasn't set up current format correctly!\n"); - return -EINVAL; - } - - icd->user_width = pix->width; - icd->user_height = pix->height; - icd->bytesperline = pix->bytesperline; - icd->sizeimage = pix->sizeimage; - icd->colorspace = pix->colorspace; - icd->field = pix->field; - - dev_dbg(icd->pdev, "set width: %d height: %d\n", - icd->user_width, icd->user_height); - - /* set physical bus parameters */ - return ici->ops->set_bus_param(icd); -} - -static int soc_camera_add_device(struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - int ret; - - if (ici->icd) - return -EBUSY; - - if (!icd->clk) { - ret = soc_camera_clock_start(ici); - if (ret < 0) - return ret; - } - - if (ici->ops->add) { - ret = ici->ops->add(icd); - if (ret < 0) - goto eadd; - } - - ici->icd = icd; - - return 0; - -eadd: - if (!icd->clk) - soc_camera_clock_stop(ici); - return ret; -} - -static void soc_camera_remove_device(struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - if (WARN_ON(icd != ici->icd)) - return; - - if (ici->ops->remove) - ici->ops->remove(icd); - if (!icd->clk) - soc_camera_clock_stop(ici); - ici->icd = NULL; -} - -static int soc_camera_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct soc_camera_device *icd; - struct soc_camera_host *ici; - int ret; - - /* - * Don't mess with the host during probe: wait until the loop in - * scan_add_host() completes. Also protect against a race with - * soc_camera_host_unregister(). - */ - if (mutex_lock_interruptible(&list_lock)) - return -ERESTARTSYS; - - if (!vdev || !video_is_registered(vdev)) { - mutex_unlock(&list_lock); - return -ENODEV; - } - - icd = video_get_drvdata(vdev); - ici = to_soc_camera_host(icd->parent); - - ret = try_module_get(ici->ops->owner) ? 0 : -ENODEV; - mutex_unlock(&list_lock); - - if (ret < 0) { - dev_err(icd->pdev, "Couldn't lock capture bus driver.\n"); - return ret; - } - - if (!to_soc_camera_control(icd)) { - /* No device driver attached */ - ret = -ENODEV; - goto econtrol; - } - - if (mutex_lock_interruptible(&ici->host_lock)) { - ret = -ERESTARTSYS; - goto elockhost; - } - icd->use_count++; - - /* Now we really have to activate the camera */ - if (icd->use_count == 1) { - struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); - /* Restore parameters before the last close() per V4L2 API */ - struct v4l2_format f = { - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, - .fmt.pix = { - .width = icd->user_width, - .height = icd->user_height, - .field = icd->field, - .colorspace = icd->colorspace, - .pixelformat = - icd->current_fmt->host_fmt->fourcc, - }, - }; - - /* The camera could have been already on, try to reset */ - if (sdesc->subdev_desc.reset) - if (icd->control) - sdesc->subdev_desc.reset(icd->control); - - ret = soc_camera_add_device(icd); - if (ret < 0) { - dev_err(icd->pdev, "Couldn't activate the camera: %d\n", ret); - goto eiciadd; - } - - ret = __soc_camera_power_on(icd); - if (ret < 0) - goto epower; - - pm_runtime_enable(&icd->vdev->dev); - ret = pm_runtime_resume(&icd->vdev->dev); - if (ret < 0 && ret != -ENOSYS) - goto eresume; - - /* - * Try to configure with default parameters. Notice: this is the - * very first open, so, we cannot race against other calls, - * apart from someone else calling open() simultaneously, but - * .host_lock is protecting us against it. - */ - ret = soc_camera_set_fmt(icd, &f); - if (ret < 0) - goto esfmt; - - ret = ici->ops->init_videobuf2(&icd->vb2_vidq, icd); - if (ret < 0) - goto einitvb; - v4l2_ctrl_handler_setup(&icd->ctrl_handler); - } - mutex_unlock(&ici->host_lock); - - file->private_data = icd; - dev_dbg(icd->pdev, "camera device open\n"); - - return 0; - - /* - * All errors are entered with the .host_lock held, first four also - * with use_count == 1 - */ -einitvb: -esfmt: - pm_runtime_disable(&icd->vdev->dev); -eresume: - __soc_camera_power_off(icd); -epower: - soc_camera_remove_device(icd); -eiciadd: - icd->use_count--; - mutex_unlock(&ici->host_lock); -elockhost: -econtrol: - module_put(ici->ops->owner); - - return ret; -} - -static int soc_camera_close(struct file *file) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - mutex_lock(&ici->host_lock); - if (icd->streamer == file) { - if (ici->ops->init_videobuf2) - vb2_queue_release(&icd->vb2_vidq); - icd->streamer = NULL; - } - icd->use_count--; - if (!icd->use_count) { - pm_runtime_suspend(&icd->vdev->dev); - pm_runtime_disable(&icd->vdev->dev); - - __soc_camera_power_off(icd); - - soc_camera_remove_device(icd); - } - - mutex_unlock(&ici->host_lock); - - module_put(ici->ops->owner); - - dev_dbg(icd->pdev, "camera device close\n"); - - return 0; -} - -static ssize_t soc_camera_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - dev_dbg(icd->pdev, "read called, buf %p\n", buf); - - if (ici->ops->init_videobuf2 && icd->vb2_vidq.io_modes & VB2_READ) - return vb2_read(&icd->vb2_vidq, buf, count, ppos, - file->f_flags & O_NONBLOCK); - - dev_err(icd->pdev, "camera device read not implemented\n"); - - return -EINVAL; -} - -static int soc_camera_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - int err; - - dev_dbg(icd->pdev, "mmap called, vma=%p\n", vma); - - if (icd->streamer != file) - return -EBUSY; - - if (mutex_lock_interruptible(&ici->host_lock)) - return -ERESTARTSYS; - err = vb2_mmap(&icd->vb2_vidq, vma); - mutex_unlock(&ici->host_lock); - - dev_dbg(icd->pdev, "vma start=0x%08lx, size=%ld, ret=%d\n", - (unsigned long)vma->vm_start, - (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, - err); - - return err; -} - -static __poll_t soc_camera_poll(struct file *file, poll_table *pt) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - __poll_t res = EPOLLERR; - - if (icd->streamer != file) - return EPOLLERR; - - mutex_lock(&ici->host_lock); - res = ici->ops->poll(file, pt); - mutex_unlock(&ici->host_lock); - return res; -} - -static const struct v4l2_file_operations soc_camera_fops = { - .owner = THIS_MODULE, - .open = soc_camera_open, - .release = soc_camera_close, - .unlocked_ioctl = video_ioctl2, - .read = soc_camera_read, - .mmap = soc_camera_mmap, - .poll = soc_camera_poll, -}; - -static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct soc_camera_device *icd = file->private_data; - int ret; - - WARN_ON(priv != file->private_data); - - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - dev_warn(icd->pdev, "Wrong buf-type %d\n", f->type); - return -EINVAL; - } - - if (icd->streamer && icd->streamer != file) - return -EBUSY; - - if (vb2_is_streaming(&icd->vb2_vidq)) { - dev_err(icd->pdev, "S_FMT denied: queue initialised\n"); - return -EBUSY; - } - - ret = soc_camera_set_fmt(icd, f); - - if (!ret && !icd->streamer) - icd->streamer = file; - - return ret; -} - -static int soc_camera_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct soc_camera_device *icd = file->private_data; - const struct soc_mbus_pixelfmt *format; - - WARN_ON(priv != file->private_data); - - if (f->index >= icd->num_user_formats) - return -EINVAL; - - format = icd->user_formats[f->index].host_fmt; - - f->pixelformat = format->fourcc; - return 0; -} - -static int soc_camera_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct soc_camera_device *icd = file->private_data; - struct v4l2_pix_format *pix = &f->fmt.pix; - - WARN_ON(priv != file->private_data); - - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - pix->width = icd->user_width; - pix->height = icd->user_height; - pix->bytesperline = icd->bytesperline; - pix->sizeimage = icd->sizeimage; - pix->field = icd->field; - pix->pixelformat = icd->current_fmt->host_fmt->fourcc; - pix->colorspace = icd->colorspace; - dev_dbg(icd->pdev, "current_fmt->fourcc: 0x%08x\n", - icd->current_fmt->host_fmt->fourcc); - return 0; -} - -static int soc_camera_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - WARN_ON(priv != file->private_data); - - strscpy(cap->driver, ici->drv_name, sizeof(cap->driver)); - return ici->ops->querycap(ici, cap); -} - -static int soc_camera_streamon(struct file *file, void *priv, - enum v4l2_buf_type i) -{ - struct soc_camera_device *icd = file->private_data; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - int ret; - - WARN_ON(priv != file->private_data); - - if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (icd->streamer != file) - return -EBUSY; - - /* This calls buf_queue from host driver's videobuf2_queue_ops */ - ret = vb2_streamon(&icd->vb2_vidq, i); - if (!ret) - v4l2_subdev_call(sd, video, s_stream, 1); - - return ret; -} - -static int soc_camera_streamoff(struct file *file, void *priv, - enum v4l2_buf_type i) -{ - struct soc_camera_device *icd = file->private_data; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - int ret; - - WARN_ON(priv != file->private_data); - - if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (icd->streamer != file) - return -EBUSY; - - /* - * This calls buf_release from host driver's videobuf2_queue_ops for all - * remaining buffers. When the last buffer is freed, stop capture - */ - ret = vb2_streamoff(&icd->vb2_vidq, i); - - v4l2_subdev_call(sd, video, s_stream, 0); - - return ret; -} - -static int soc_camera_g_selection(struct file *file, void *fh, - struct v4l2_selection *s) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - /* With a wrong type no need to try to fall back to cropping */ - if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - return ici->ops->get_selection(icd, s); -} - -static int soc_camera_s_selection(struct file *file, void *fh, - struct v4l2_selection *s) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - int ret; - - /* In all these cases cropping emulation will not help */ - if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - (s->target != V4L2_SEL_TGT_COMPOSE && - s->target != V4L2_SEL_TGT_CROP)) - return -EINVAL; - - if (s->target == V4L2_SEL_TGT_COMPOSE) { - /* No output size change during a running capture! */ - if (vb2_is_streaming(&icd->vb2_vidq) && - (icd->user_width != s->r.width || - icd->user_height != s->r.height)) - return -EBUSY; - - /* - * Only one user is allowed to change the output format, touch - * buffers, start / stop streaming, poll for data - */ - if (icd->streamer && icd->streamer != file) - return -EBUSY; - } - - if (s->target == V4L2_SEL_TGT_CROP && - vb2_is_streaming(&icd->vb2_vidq) && - ici->ops->set_liveselection) - ret = ici->ops->set_liveselection(icd, s); - else - ret = ici->ops->set_selection(icd, s); - if (!ret && - s->target == V4L2_SEL_TGT_COMPOSE) { - icd->user_width = s->r.width; - icd->user_height = s->r.height; - if (!icd->streamer) - icd->streamer = file; - } - - return ret; -} - -static int soc_camera_g_parm(struct file *file, void *fh, - struct v4l2_streamparm *a) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - if (ici->ops->get_parm) - return ici->ops->get_parm(icd, a); - - return -ENOIOCTLCMD; -} - -static int soc_camera_s_parm(struct file *file, void *fh, - struct v4l2_streamparm *a) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - if (ici->ops->set_parm) - return ici->ops->set_parm(icd, a); - - return -ENOIOCTLCMD; -} - -static int soc_camera_probe(struct soc_camera_host *ici, - struct soc_camera_device *icd); - -/* So far this function cannot fail */ -static void scan_add_host(struct soc_camera_host *ici) -{ - struct soc_camera_device *icd; - - mutex_lock(&list_lock); - - list_for_each_entry(icd, &devices, list) - if (icd->iface == ici->nr) { - struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); - struct soc_camera_subdev_desc *ssdd = &sdesc->subdev_desc; - - /* The camera could have been already on, try to reset */ - if (ssdd->reset) - if (icd->control) - ssdd->reset(icd->control); - - icd->parent = ici->v4l2_dev.dev; - - /* Ignore errors */ - soc_camera_probe(ici, icd); - } - - mutex_unlock(&list_lock); -} - -/* - * It is invalid to call v4l2_clk_enable() after a successful probing - * asynchronously outside of V4L2 operations, i.e. with .host_lock not held. - */ -static int soc_camera_clk_enable(struct v4l2_clk *clk) -{ - struct soc_camera_device *icd = clk->priv; - struct soc_camera_host *ici; - - if (!icd || !icd->parent) - return -ENODEV; - - ici = to_soc_camera_host(icd->parent); - - if (!try_module_get(ici->ops->owner)) - return -ENODEV; - - /* - * If a different client is currently being probed, the host will tell - * you to go - */ - return soc_camera_clock_start(ici); -} - -static void soc_camera_clk_disable(struct v4l2_clk *clk) -{ - struct soc_camera_device *icd = clk->priv; - struct soc_camera_host *ici; - - if (!icd || !icd->parent) - return; - - ici = to_soc_camera_host(icd->parent); - - soc_camera_clock_stop(ici); - - module_put(ici->ops->owner); -} - -/* - * Eventually, it would be more logical to make the respective host the clock - * owner, but then we would have to copy this struct for each ici. Besides, it - * would introduce the circular dependency problem, unless we port all client - * drivers to release the clock, when not in use. - */ -static const struct v4l2_clk_ops soc_camera_clk_ops = { - .owner = THIS_MODULE, - .enable = soc_camera_clk_enable, - .disable = soc_camera_clk_disable, -}; - -static int soc_camera_dyn_pdev(struct soc_camera_desc *sdesc, - struct soc_camera_async_client *sasc) -{ - struct platform_device *pdev; - int ret, i; - - mutex_lock(&list_lock); - i = find_first_zero_bit(device_map, MAP_MAX_NUM); - if (i < MAP_MAX_NUM) - set_bit(i, device_map); - mutex_unlock(&list_lock); - if (i >= MAP_MAX_NUM) - return -ENOMEM; - - pdev = platform_device_alloc("soc-camera-pdrv", i); - if (!pdev) - return -ENOMEM; - - ret = platform_device_add_data(pdev, sdesc, sizeof(*sdesc)); - if (ret < 0) { - platform_device_put(pdev); - return ret; - } - - sasc->pdev = pdev; - - return 0; -} - -static struct soc_camera_device *soc_camera_add_pdev(struct soc_camera_async_client *sasc) -{ - struct platform_device *pdev = sasc->pdev; - int ret; - - ret = platform_device_add(pdev); - if (ret < 0 || !pdev->dev.driver) - return NULL; - - return platform_get_drvdata(pdev); -} - -/* Locking: called with .host_lock held */ -static int soc_camera_probe_finish(struct soc_camera_device *icd) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &fmt.format; - int ret; - - sd->grp_id = soc_camera_grp_id(icd); - v4l2_set_subdev_hostdata(sd, icd); - - v4l2_subdev_call(sd, video, g_tvnorms, &icd->vdev->tvnorms); - - ret = v4l2_ctrl_add_handler(&icd->ctrl_handler, sd->ctrl_handler, - NULL, true); - if (ret < 0) - return ret; - - ret = soc_camera_add_device(icd); - if (ret < 0) { - dev_err(icd->pdev, "Couldn't activate the camera: %d\n", ret); - return ret; - } - - /* At this point client .probe() should have run already */ - ret = soc_camera_init_user_formats(icd); - if (ret < 0) - goto eusrfmt; - - icd->field = V4L2_FIELD_ANY; - - ret = soc_camera_video_start(icd); - if (ret < 0) - goto evidstart; - - /* Try to improve our guess of a reasonable window format */ - if (!v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt)) { - icd->user_width = mf->width; - icd->user_height = mf->height; - icd->colorspace = mf->colorspace; - icd->field = mf->field; - } - soc_camera_remove_device(icd); - - return 0; - -evidstart: - soc_camera_free_user_formats(icd); -eusrfmt: - soc_camera_remove_device(icd); - - return ret; -} - -#ifdef CONFIG_I2C_BOARDINFO -static int soc_camera_i2c_init(struct soc_camera_device *icd, - struct soc_camera_desc *sdesc) -{ - struct soc_camera_subdev_desc *ssdd; - struct i2c_client *client; - struct soc_camera_host *ici; - struct soc_camera_host_desc *shd = &sdesc->host_desc; - struct i2c_adapter *adap; - struct v4l2_subdev *subdev; - char clk_name[V4L2_CLK_NAME_SIZE]; - int ret; - - /* First find out how we link the main client */ - if (icd->sasc) { - /* Async non-OF probing handled by the subdevice list */ - return -EPROBE_DEFER; - } - - ici = to_soc_camera_host(icd->parent); - adap = i2c_get_adapter(shd->i2c_adapter_id); - if (!adap) { - dev_err(icd->pdev, "Cannot get I2C adapter #%d. No driver?\n", - shd->i2c_adapter_id); - return -ENODEV; - } - - ssdd = kmemdup(&sdesc->subdev_desc, sizeof(*ssdd), GFP_KERNEL); - if (!ssdd) { - ret = -ENOMEM; - goto ealloc; - } - /* - * In synchronous case we request regulators ourselves in - * soc_camera_pdrv_probe(), make sure the subdevice driver doesn't try - * to allocate them again. - */ - ssdd->sd_pdata.num_regulators = 0; - ssdd->sd_pdata.regulators = NULL; - shd->board_info->platform_data = ssdd; - - v4l2_clk_name_i2c(clk_name, sizeof(clk_name), - shd->i2c_adapter_id, shd->board_info->addr); - - icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, icd); - if (IS_ERR(icd->clk)) { - ret = PTR_ERR(icd->clk); - goto eclkreg; - } - - subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap, - shd->board_info, NULL); - if (!subdev) { - ret = -ENODEV; - goto ei2cnd; - } - - client = v4l2_get_subdevdata(subdev); - - /* Use to_i2c_client(dev) to recover the i2c client */ - icd->control = &client->dev; - - return 0; -ei2cnd: - v4l2_clk_unregister(icd->clk); - icd->clk = NULL; -eclkreg: - kfree(ssdd); -ealloc: - i2c_put_adapter(adap); - return ret; -} - -static void soc_camera_i2c_free(struct soc_camera_device *icd) -{ - struct i2c_client *client = - to_i2c_client(to_soc_camera_control(icd)); - struct i2c_adapter *adap; - struct soc_camera_subdev_desc *ssdd; - - icd->control = NULL; - if (icd->sasc) - return; - - adap = client->adapter; - ssdd = client->dev.platform_data; - v4l2_device_unregister_subdev(i2c_get_clientdata(client)); - i2c_unregister_device(client); - i2c_put_adapter(adap); - kfree(ssdd); - v4l2_clk_unregister(icd->clk); - icd->clk = NULL; -} - -/* - * V4L2 asynchronous notifier callbacks. They are all called under a v4l2-async - * internal global mutex, therefore cannot race against other asynchronous - * events. Until notifier->complete() (soc_camera_async_complete()) is called, - * the video device node is not registered and no V4L fops can occur. Unloading - * of the host driver also calls a v4l2-async function, so also there we're - * protected. - */ -static int soc_camera_async_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) -{ - struct soc_camera_async_client *sasc = container_of(notifier, - struct soc_camera_async_client, notifier); - struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev); - - if (asd == sasc->sensor && !WARN_ON(icd->control)) { - struct i2c_client *client = v4l2_get_subdevdata(sd); - - /* - * Only now we get subdevice-specific information like - * regulators, flags, callbacks, etc. - */ - if (client) { - struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); - struct soc_camera_subdev_desc *ssdd = - soc_camera_i2c_to_desc(client); - if (ssdd) { - memcpy(&sdesc->subdev_desc, ssdd, - sizeof(sdesc->subdev_desc)); - if (ssdd->reset) - ssdd->reset(&client->dev); - } - - icd->control = &client->dev; - } - } - - return 0; -} - -static void soc_camera_async_unbind(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) -{ - struct soc_camera_async_client *sasc = container_of(notifier, - struct soc_camera_async_client, notifier); - struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev); - - icd->control = NULL; - - if (icd->clk) { - v4l2_clk_unregister(icd->clk); - icd->clk = NULL; - } -} - -static int soc_camera_async_complete(struct v4l2_async_notifier *notifier) -{ - struct soc_camera_async_client *sasc = container_of(notifier, - struct soc_camera_async_client, notifier); - struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev); - - if (to_soc_camera_control(icd)) { - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - int ret; - - mutex_lock(&list_lock); - ret = soc_camera_probe(ici, icd); - mutex_unlock(&list_lock); - if (ret < 0) - return ret; - } - - return 0; -} - -static const struct v4l2_async_notifier_operations soc_camera_async_ops = { - .bound = soc_camera_async_bound, - .unbind = soc_camera_async_unbind, - .complete = soc_camera_async_complete, -}; - -static int scan_async_group(struct soc_camera_host *ici, - struct v4l2_async_subdev **asd, unsigned int size) -{ - struct soc_camera_async_subdev *sasd; - struct soc_camera_async_client *sasc; - struct soc_camera_device *icd; - struct soc_camera_desc sdesc = {.host_desc.bus_id = ici->nr,}; - char clk_name[V4L2_CLK_NAME_SIZE]; - unsigned int i; - int ret; - - /* First look for a sensor */ - for (i = 0; i < size; i++) { - sasd = container_of(asd[i], struct soc_camera_async_subdev, asd); - if (sasd->role == SOCAM_SUBDEV_DATA_SOURCE) - break; - } - - if (i >= size || asd[i]->match_type != V4L2_ASYNC_MATCH_I2C) { - /* All useless */ - dev_err(ici->v4l2_dev.dev, "No I2C data source found!\n"); - return -ENODEV; - } - - /* Or shall this be managed by the soc-camera device? */ - sasc = devm_kzalloc(ici->v4l2_dev.dev, sizeof(*sasc), GFP_KERNEL); - if (!sasc) - return -ENOMEM; - - /* HACK: just need a != NULL */ - sdesc.host_desc.board_info = ERR_PTR(-ENODATA); - - ret = soc_camera_dyn_pdev(&sdesc, sasc); - if (ret < 0) - goto eallocpdev; - - sasc->sensor = &sasd->asd; - - icd = soc_camera_add_pdev(sasc); - if (!icd) { - ret = -ENOMEM; - goto eaddpdev; - } - - v4l2_async_notifier_init(&sasc->notifier); - - for (i = 0; i < size; i++) { - ret = v4l2_async_notifier_add_subdev(&sasc->notifier, asd[i]); - if (ret) - goto eaddasd; - } - - sasc->notifier.ops = &soc_camera_async_ops; - - icd->sasc = sasc; - icd->parent = ici->v4l2_dev.dev; - - v4l2_clk_name_i2c(clk_name, sizeof(clk_name), - sasd->asd.match.i2c.adapter_id, - sasd->asd.match.i2c.address); - - icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, icd); - if (IS_ERR(icd->clk)) { - ret = PTR_ERR(icd->clk); - goto eclkreg; - } - - ret = v4l2_async_notifier_register(&ici->v4l2_dev, &sasc->notifier); - if (!ret) - return 0; - - v4l2_clk_unregister(icd->clk); -eclkreg: - icd->clk = NULL; -eaddasd: - v4l2_async_notifier_cleanup(&sasc->notifier); - platform_device_del(sasc->pdev); -eaddpdev: - platform_device_put(sasc->pdev); -eallocpdev: - devm_kfree(ici->v4l2_dev.dev, sasc); - dev_err(ici->v4l2_dev.dev, "group probe failed: %d\n", ret); - - return ret; -} - -static void scan_async_host(struct soc_camera_host *ici) -{ - struct v4l2_async_subdev **asd; - int j; - - for (j = 0, asd = ici->asd; ici->asd_sizes[j]; j++) { - scan_async_group(ici, asd, ici->asd_sizes[j]); - asd += ici->asd_sizes[j]; - } -} -#else -#define soc_camera_i2c_init(icd, sdesc) (-ENODEV) -#define soc_camera_i2c_free(icd) do {} while (0) -#define scan_async_host(ici) do {} while (0) -#endif - -#ifdef CONFIG_OF - -struct soc_of_info { - struct soc_camera_async_subdev sasd; - struct soc_camera_async_client sasc; - struct v4l2_async_subdev *subdev; -}; - -static int soc_of_bind(struct soc_camera_host *ici, - struct device_node *ep, - struct device_node *remote) -{ - struct soc_camera_device *icd; - struct soc_camera_desc sdesc = {.host_desc.bus_id = ici->nr,}; - struct soc_camera_async_client *sasc; - struct soc_of_info *info; - struct i2c_client *client; - char clk_name[V4L2_CLK_NAME_SIZE]; - int ret; - - /* allocate a new subdev and add match info to it */ - info = devm_kzalloc(ici->v4l2_dev.dev, sizeof(struct soc_of_info), - GFP_KERNEL); - if (!info) - return -ENOMEM; - - info->sasd.asd.match.fwnode = of_fwnode_handle(remote); - info->sasd.asd.match_type = V4L2_ASYNC_MATCH_FWNODE; - info->subdev = &info->sasd.asd; - - /* Or shall this be managed by the soc-camera device? */ - sasc = &info->sasc; - - /* HACK: just need a != NULL */ - sdesc.host_desc.board_info = ERR_PTR(-ENODATA); - - ret = soc_camera_dyn_pdev(&sdesc, sasc); - if (ret < 0) - goto eallocpdev; - - sasc->sensor = &info->sasd.asd; - - icd = soc_camera_add_pdev(sasc); - if (!icd) { - ret = -ENOMEM; - goto eaddpdev; - } - - v4l2_async_notifier_init(&sasc->notifier); - - ret = v4l2_async_notifier_add_subdev(&sasc->notifier, info->subdev); - if (ret) { - of_node_put(remote); - goto eaddasd; - } - - sasc->notifier.ops = &soc_camera_async_ops; - - icd->sasc = sasc; - icd->parent = ici->v4l2_dev.dev; - - client = of_find_i2c_device_by_node(remote); - - if (client) - v4l2_clk_name_i2c(clk_name, sizeof(clk_name), - client->adapter->nr, client->addr); - else - v4l2_clk_name_of(clk_name, sizeof(clk_name), remote); - - icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, icd); - if (IS_ERR(icd->clk)) { - ret = PTR_ERR(icd->clk); - goto eclkreg; - } - - ret = v4l2_async_notifier_register(&ici->v4l2_dev, &sasc->notifier); - if (!ret) - return 0; - - v4l2_clk_unregister(icd->clk); -eclkreg: - icd->clk = NULL; -eaddasd: - v4l2_async_notifier_cleanup(&sasc->notifier); - platform_device_del(sasc->pdev); -eaddpdev: - platform_device_put(sasc->pdev); -eallocpdev: - devm_kfree(ici->v4l2_dev.dev, info); - dev_err(ici->v4l2_dev.dev, "group probe failed: %d\n", ret); - - return ret; -} - -static void scan_of_host(struct soc_camera_host *ici) -{ - struct device *dev = ici->v4l2_dev.dev; - struct device_node *np = dev->of_node; - struct device_node *epn = NULL, *rem; - unsigned int i; - - for (i = 0; ; i++) { - epn = of_graph_get_next_endpoint(np, epn); - if (!epn) - break; - - rem = of_graph_get_remote_port_parent(epn); - if (!rem) { - dev_notice(dev, "no remote for %pOF\n", epn); - continue; - } - - /* so we now have a remote node to connect */ - if (!i) - soc_of_bind(ici, epn, rem); - - if (i) { - dev_err(dev, "multiple subdevices aren't supported yet!\n"); - break; - } - } - - of_node_put(epn); -} - -#else -static inline void scan_of_host(struct soc_camera_host *ici) { } -#endif - -/* Called during host-driver probe */ -static int soc_camera_probe(struct soc_camera_host *ici, - struct soc_camera_device *icd) -{ - struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); - struct soc_camera_host_desc *shd = &sdesc->host_desc; - struct device *control = NULL; - int ret; - - dev_info(icd->pdev, "Probing %s\n", dev_name(icd->pdev)); - - /* - * Currently the subdev with the largest number of controls (13) is - * ov6550. So let's pick 16 as a hint for the control handler. Note - * that this is a hint only: too large and you waste some memory, too - * small and there is a (very) small performance hit when looking up - * controls in the internal hash. - */ - ret = v4l2_ctrl_handler_init(&icd->ctrl_handler, 16); - if (ret < 0) - return ret; - - /* Must have icd->vdev before registering the device */ - ret = video_dev_create(icd); - if (ret < 0) - goto evdc; - - /* - * ..._video_start() will create a device node, video_register_device() - * itself is protected against concurrent open() calls, but we also have - * to protect our data also during client probing. - */ - - /* Non-i2c cameras, e.g., soc_camera_platform, have no board_info */ - if (shd->board_info) { - ret = soc_camera_i2c_init(icd, sdesc); - if (ret < 0 && ret != -EPROBE_DEFER) - goto eadd; - } else if (!shd->add_device || !shd->del_device) { - ret = -EINVAL; - goto eadd; - } else { - ret = soc_camera_clock_start(ici); - if (ret < 0) - goto eadd; - - if (shd->module_name) - ret = request_module(shd->module_name); - - ret = shd->add_device(icd); - if (ret < 0) - goto eadddev; - - /* - * FIXME: this is racy, have to use driver-binding notification, - * when it is available - */ - control = to_soc_camera_control(icd); - if (!control || !control->driver || !dev_get_drvdata(control) || - !try_module_get(control->driver->owner)) { - shd->del_device(icd); - ret = -ENODEV; - goto enodrv; - } - } - - mutex_lock(&ici->host_lock); - ret = soc_camera_probe_finish(icd); - mutex_unlock(&ici->host_lock); - if (ret < 0) - goto efinish; - - return 0; - -efinish: - if (shd->board_info) { - soc_camera_i2c_free(icd); - } else { - shd->del_device(icd); - module_put(control->driver->owner); -enodrv: -eadddev: - soc_camera_clock_stop(ici); - } -eadd: - if (icd->vdev) { - video_device_release(icd->vdev); - icd->vdev = NULL; - } -evdc: - v4l2_ctrl_handler_free(&icd->ctrl_handler); - return ret; -} - -/* - * This is called on device_unregister, which only means we have to disconnect - * from the host, but not remove ourselves from the device list. With - * asynchronous client probing this can also be called without - * soc_camera_probe_finish() having run. Careful with clean up. - */ -static int soc_camera_remove(struct soc_camera_device *icd) -{ - struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); - struct video_device *vdev = icd->vdev; - - v4l2_ctrl_handler_free(&icd->ctrl_handler); - if (vdev) { - video_unregister_device(vdev); - icd->vdev = NULL; - } - - if (sdesc->host_desc.board_info) { - soc_camera_i2c_free(icd); - } else { - struct device *dev = to_soc_camera_control(icd); - struct device_driver *drv = dev ? dev->driver : NULL; - if (drv) { - sdesc->host_desc.del_device(icd); - module_put(drv->owner); - } - } - - if (icd->num_user_formats) - soc_camera_free_user_formats(icd); - - if (icd->clk) { - /* For the synchronous case */ - v4l2_clk_unregister(icd->clk); - icd->clk = NULL; - } - - if (icd->sasc) - platform_device_unregister(icd->sasc->pdev); - - return 0; -} - -static int default_g_selection(struct soc_camera_device *icd, - struct v4l2_selection *sel) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct v4l2_subdev_selection sdsel = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .target = sel->target, - }; - int ret; - - ret = v4l2_subdev_call(sd, pad, get_selection, NULL, &sdsel); - if (ret) - return ret; - sel->r = sdsel.r; - return 0; -} - -static int default_s_selection(struct soc_camera_device *icd, - struct v4l2_selection *sel) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct v4l2_subdev_selection sdsel = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .target = sel->target, - .flags = sel->flags, - .r = sel->r, - }; - int ret; - - ret = v4l2_subdev_call(sd, pad, set_selection, NULL, &sdsel); - if (ret) - return ret; - sel->r = sdsel.r; - return 0; -} - -static int default_g_parm(struct soc_camera_device *icd, - struct v4l2_streamparm *a) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - - return v4l2_g_parm_cap(icd->vdev, sd, a); -} - -static int default_s_parm(struct soc_camera_device *icd, - struct v4l2_streamparm *a) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - - return v4l2_s_parm_cap(icd->vdev, sd, a); -} - -static int default_enum_framesizes(struct soc_camera_device *icd, - struct v4l2_frmsizeenum *fsize) -{ - int ret; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - const struct soc_camera_format_xlate *xlate; - struct v4l2_subdev_frame_size_enum fse = { - .index = fsize->index, - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - - xlate = soc_camera_xlate_by_fourcc(icd, fsize->pixel_format); - if (!xlate) - return -EINVAL; - fse.code = xlate->code; - - ret = v4l2_subdev_call(sd, pad, enum_frame_size, NULL, &fse); - if (ret < 0) - return ret; - - if (fse.min_width == fse.max_width && - fse.min_height == fse.max_height) { - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - fsize->discrete.width = fse.min_width; - fsize->discrete.height = fse.min_height; - return 0; - } - fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; - fsize->stepwise.min_width = fse.min_width; - fsize->stepwise.max_width = fse.max_width; - fsize->stepwise.min_height = fse.min_height; - fsize->stepwise.max_height = fse.max_height; - fsize->stepwise.step_width = 1; - fsize->stepwise.step_height = 1; - return 0; -} - -int soc_camera_host_register(struct soc_camera_host *ici) -{ - struct soc_camera_host *ix; - int ret; - - if (!ici || !ici->ops || - !ici->ops->try_fmt || - !ici->ops->set_fmt || - !ici->ops->set_bus_param || - !ici->ops->querycap || - !ici->ops->init_videobuf2 || - !ici->ops->poll || - !ici->v4l2_dev.dev) - return -EINVAL; - - if (!ici->ops->set_selection) - ici->ops->set_selection = default_s_selection; - if (!ici->ops->get_selection) - ici->ops->get_selection = default_g_selection; - if (!ici->ops->set_parm) - ici->ops->set_parm = default_s_parm; - if (!ici->ops->get_parm) - ici->ops->get_parm = default_g_parm; - if (!ici->ops->enum_framesizes) - ici->ops->enum_framesizes = default_enum_framesizes; - - mutex_lock(&list_lock); - list_for_each_entry(ix, &hosts, list) { - if (ix->nr == ici->nr) { - ret = -EBUSY; - goto edevreg; - } - } - - ret = v4l2_device_register(ici->v4l2_dev.dev, &ici->v4l2_dev); - if (ret < 0) - goto edevreg; - - list_add_tail(&ici->list, &hosts); - mutex_unlock(&list_lock); - - mutex_init(&ici->host_lock); - mutex_init(&ici->clk_lock); - - if (ici->v4l2_dev.dev->of_node) - scan_of_host(ici); - else if (ici->asd_sizes) - /* - * No OF, host with a list of subdevices. Don't try to mix - * modes by initialising some groups statically and some - * dynamically! - */ - scan_async_host(ici); - else - /* Legacy: static platform devices from board data */ - scan_add_host(ici); - - return 0; - -edevreg: - mutex_unlock(&list_lock); - return ret; -} -EXPORT_SYMBOL(soc_camera_host_register); - -/* Unregister all clients! */ -void soc_camera_host_unregister(struct soc_camera_host *ici) -{ - struct soc_camera_device *icd, *tmp; - struct soc_camera_async_client *sasc; - LIST_HEAD(notifiers); - - mutex_lock(&list_lock); - list_del(&ici->list); - list_for_each_entry(icd, &devices, list) - if (icd->iface == ici->nr && icd->sasc) { - /* as long as we hold the device, sasc won't be freed */ - get_device(icd->pdev); - list_add(&icd->sasc->list, ¬ifiers); - } - mutex_unlock(&list_lock); - - list_for_each_entry(sasc, ¬ifiers, list) { - /* Must call unlocked to avoid AB-BA dead-lock */ - v4l2_async_notifier_unregister(&sasc->notifier); - v4l2_async_notifier_cleanup(&sasc->notifier); - put_device(&sasc->pdev->dev); - } - - mutex_lock(&list_lock); - - list_for_each_entry_safe(icd, tmp, &devices, list) - if (icd->iface == ici->nr) - soc_camera_remove(icd); - - mutex_unlock(&list_lock); - - v4l2_device_unregister(&ici->v4l2_dev); -} -EXPORT_SYMBOL(soc_camera_host_unregister); - -/* Image capture device */ -static int soc_camera_device_register(struct soc_camera_device *icd) -{ - struct soc_camera_device *ix; - int num = -1, i; - - mutex_lock(&list_lock); - for (i = 0; i < 256 && num < 0; i++) { - num = i; - /* Check if this index is available on this interface */ - list_for_each_entry(ix, &devices, list) { - if (ix->iface == icd->iface && ix->devnum == i) { - num = -1; - break; - } - } - } - - if (num < 0) { - /* - * ok, we have 256 cameras on this host... - * man, stay reasonable... - */ - mutex_unlock(&list_lock); - return -ENOMEM; - } - - icd->devnum = num; - icd->use_count = 0; - icd->host_priv = NULL; - - /* - * Dynamically allocated devices set the bit earlier, but it doesn't hurt setting - * it again - */ - i = to_platform_device(icd->pdev)->id; - if (i < 0) - /* One static (legacy) soc-camera platform device */ - i = 0; - if (i >= MAP_MAX_NUM) { - mutex_unlock(&list_lock); - return -EBUSY; - } - set_bit(i, device_map); - list_add_tail(&icd->list, &devices); - mutex_unlock(&list_lock); - - return 0; -} - -static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = { - .vidioc_querycap = soc_camera_querycap, - .vidioc_try_fmt_vid_cap = soc_camera_try_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = soc_camera_g_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = soc_camera_s_fmt_vid_cap, - .vidioc_enum_fmt_vid_cap = soc_camera_enum_fmt_vid_cap, - .vidioc_enum_input = soc_camera_enum_input, - .vidioc_g_input = soc_camera_g_input, - .vidioc_s_input = soc_camera_s_input, - .vidioc_s_std = soc_camera_s_std, - .vidioc_g_std = soc_camera_g_std, - .vidioc_enum_framesizes = soc_camera_enum_framesizes, - .vidioc_reqbufs = soc_camera_reqbufs, - .vidioc_querybuf = soc_camera_querybuf, - .vidioc_qbuf = soc_camera_qbuf, - .vidioc_dqbuf = soc_camera_dqbuf, - .vidioc_create_bufs = soc_camera_create_bufs, - .vidioc_prepare_buf = soc_camera_prepare_buf, - .vidioc_expbuf = soc_camera_expbuf, - .vidioc_streamon = soc_camera_streamon, - .vidioc_streamoff = soc_camera_streamoff, - .vidioc_g_selection = soc_camera_g_selection, - .vidioc_s_selection = soc_camera_s_selection, - .vidioc_g_parm = soc_camera_g_parm, - .vidioc_s_parm = soc_camera_s_parm, -}; - -static int video_dev_create(struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct video_device *vdev = video_device_alloc(); - - if (!vdev) - return -ENOMEM; - - strscpy(vdev->name, ici->drv_name, sizeof(vdev->name)); - - vdev->v4l2_dev = &ici->v4l2_dev; - vdev->fops = &soc_camera_fops; - vdev->ioctl_ops = &soc_camera_ioctl_ops; - vdev->release = video_device_release; - vdev->ctrl_handler = &icd->ctrl_handler; - vdev->lock = &ici->host_lock; - - icd->vdev = vdev; - - return 0; -} - -/* - * Called from soc_camera_probe() above with .host_lock held - */ -static int soc_camera_video_start(struct soc_camera_device *icd) -{ - const struct device_type *type = icd->vdev->dev.type; - int ret; - - if (!icd->parent) - return -ENODEV; - - video_set_drvdata(icd->vdev, icd); - if (icd->vdev->tvnorms == 0) { - /* disable the STD API if there are no tvnorms defined */ - v4l2_disable_ioctl(icd->vdev, VIDIOC_G_STD); - v4l2_disable_ioctl(icd->vdev, VIDIOC_S_STD); - v4l2_disable_ioctl(icd->vdev, VIDIOC_ENUMSTD); - } - ret = video_register_device(icd->vdev, VFL_TYPE_VIDEO, -1); - if (ret < 0) { - dev_err(icd->pdev, "video_register_device failed: %d\n", ret); - return ret; - } - - /* Restore device type, possibly set by the subdevice driver */ - icd->vdev->dev.type = type; - - return 0; -} - -static int soc_camera_pdrv_probe(struct platform_device *pdev) -{ - struct soc_camera_desc *sdesc = pdev->dev.platform_data; - struct soc_camera_subdev_desc *ssdd = &sdesc->subdev_desc; - struct soc_camera_device *icd; - int ret; - - if (!sdesc) - return -EINVAL; - - icd = devm_kzalloc(&pdev->dev, sizeof(*icd), GFP_KERNEL); - if (!icd) - return -ENOMEM; - - /* - * In the asynchronous case ssdd->num_regulators == 0 yet, so, the below - * regulator allocation is a dummy. They are actually requested by the - * subdevice driver, using soc_camera_power_init(). Also note, that in - * that case regulators are attached to the I2C device and not to the - * camera platform device. - */ - ret = devm_regulator_bulk_get(&pdev->dev, ssdd->sd_pdata.num_regulators, - ssdd->sd_pdata.regulators); - if (ret < 0) - return ret; - - icd->iface = sdesc->host_desc.bus_id; - icd->sdesc = sdesc; - icd->pdev = &pdev->dev; - platform_set_drvdata(pdev, icd); - - icd->user_width = DEFAULT_WIDTH; - icd->user_height = DEFAULT_HEIGHT; - - return soc_camera_device_register(icd); -} - -/* - * Only called on rmmod for each platform device, since they are not - * hot-pluggable. Now we know, that all our users - hosts and devices have - * been unloaded already - */ -static int soc_camera_pdrv_remove(struct platform_device *pdev) -{ - struct soc_camera_device *icd = platform_get_drvdata(pdev); - int i; - - if (!icd) - return -EINVAL; - - i = pdev->id; - if (i < 0) - i = 0; - - /* - * In synchronous mode with static platform devices this is called in a - * loop from drivers/base/dd.c::driver_detach(), no parallel execution, - * no need to lock. In asynchronous case the caller - - * soc_camera_host_unregister() - already holds the lock - */ - if (test_bit(i, device_map)) { - clear_bit(i, device_map); - list_del(&icd->list); - } - - return 0; -} - -static struct platform_driver __refdata soc_camera_pdrv = { - .probe = soc_camera_pdrv_probe, - .remove = soc_camera_pdrv_remove, - .driver = { - .name = "soc-camera-pdrv", - }, -}; - -module_platform_driver(soc_camera_pdrv); - -MODULE_DESCRIPTION("Image capture bus driver"); -MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:soc-camera-pdrv"); diff --git a/drivers/staging/media/soc_camera/soc_mediabus.c b/drivers/staging/media/soc_camera/soc_mediabus.c deleted file mode 100644 index 2aa646c89c1f..000000000000 --- a/drivers/staging/media/soc_camera/soc_mediabus.c +++ /dev/null @@ -1,529 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * soc-camera media bus helper routines - * - * Copyright (C) 2009, Guennadi Liakhovetski <g.liakhovetski@gmx.de> - */ -#include <linux/kernel.h> -#include <linux/module.h> - -#include <media/v4l2-device.h> -#include <media/v4l2-mediabus.h> -#include <media/drv-intf/soc_mediabus.h> - -static const struct soc_mbus_lookup mbus_fmt[] = { -{ - .code = MEDIA_BUS_FMT_YUYV8_2X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_YUYV, - .name = "YUYV", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_YVYU8_2X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_YVYU, - .name = "YVYU", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_UYVY8_2X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_UYVY, - .name = "UYVY", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_VYUY8_2X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_VYUY, - .name = "VYUY", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB555, - .name = "RGB555", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB555X, - .name = "RGB555X", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB565_2X8_LE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB565, - .name = "RGB565", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB565_2X8_BE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB565X, - .name = "RGB565X", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB666_1X18, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB32, - .name = "RGB666/32bpp", - .bits_per_sample = 18, - .packing = SOC_MBUS_PACKING_EXTEND32, - .order = SOC_MBUS_ORDER_LE, - }, -}, { - .code = MEDIA_BUS_FMT_RGB888_1X24, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB32, - .name = "RGB888/32bpp", - .bits_per_sample = 24, - .packing = SOC_MBUS_PACKING_EXTEND32, - .order = SOC_MBUS_ORDER_LE, - }, -}, { - .code = MEDIA_BUS_FMT_RGB888_2X12_BE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB32, - .name = "RGB888/32bpp", - .bits_per_sample = 12, - .packing = SOC_MBUS_PACKING_EXTEND32, - .order = SOC_MBUS_ORDER_BE, - }, -}, { - .code = MEDIA_BUS_FMT_RGB888_2X12_LE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB32, - .name = "RGB888/32bpp", - .bits_per_sample = 12, - .packing = SOC_MBUS_PACKING_EXTEND32, - .order = SOC_MBUS_ORDER_LE, - }, -}, { - .code = MEDIA_BUS_FMT_SBGGR8_1X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_SBGGR8, - .name = "Bayer 8 BGGR", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_NONE, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SBGGR10_1X10, - .fmt = { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .name = "Bayer 10 BGGR", - .bits_per_sample = 10, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_Y8_1X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_GREY, - .name = "Grey", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_NONE, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_Y10_1X10, - .fmt = { - .fourcc = V4L2_PIX_FMT_Y10, - .name = "Grey 10bit", - .bits_per_sample = 10, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, - .fmt = { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .name = "Bayer 10 BGGR", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE, - .fmt = { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .name = "Bayer 10 BGGR", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADLO, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE, - .fmt = { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .name = "Bayer 10 BGGR", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE, - .fmt = { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .name = "Bayer 10 BGGR", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADLO, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_JPEG_1X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_JPEG, - .name = "JPEG", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_VARIABLE, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB444, - .name = "RGB444", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_YUYV8_1_5X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_YUV420, - .name = "YUYV 4:2:0", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_1_5X8, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_YVYU8_1_5X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_YVU420, - .name = "YVYU 4:2:0", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_1_5X8, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_UYVY8_1X16, - .fmt = { - .fourcc = V4L2_PIX_FMT_UYVY, - .name = "UYVY 16bit", - .bits_per_sample = 16, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_VYUY8_1X16, - .fmt = { - .fourcc = V4L2_PIX_FMT_VYUY, - .name = "VYUY 16bit", - .bits_per_sample = 16, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_YUYV8_1X16, - .fmt = { - .fourcc = V4L2_PIX_FMT_YUYV, - .name = "YUYV 16bit", - .bits_per_sample = 16, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_YVYU8_1X16, - .fmt = { - .fourcc = V4L2_PIX_FMT_YVYU, - .name = "YVYU 16bit", - .bits_per_sample = 16, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SGRBG8_1X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_SGRBG8, - .name = "Bayer 8 GRBG", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_NONE, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_SGRBG10DPCM8, - .name = "Bayer 10 BGGR DPCM 8", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_NONE, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SGBRG10_1X10, - .fmt = { - .fourcc = V4L2_PIX_FMT_SGBRG10, - .name = "Bayer 10 GBRG", - .bits_per_sample = 10, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SGRBG10_1X10, - .fmt = { - .fourcc = V4L2_PIX_FMT_SGRBG10, - .name = "Bayer 10 GRBG", - .bits_per_sample = 10, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SRGGB10_1X10, - .fmt = { - .fourcc = V4L2_PIX_FMT_SRGGB10, - .name = "Bayer 10 RGGB", - .bits_per_sample = 10, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SBGGR12_1X12, - .fmt = { - .fourcc = V4L2_PIX_FMT_SBGGR12, - .name = "Bayer 12 BGGR", - .bits_per_sample = 12, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SGBRG12_1X12, - .fmt = { - .fourcc = V4L2_PIX_FMT_SGBRG12, - .name = "Bayer 12 GBRG", - .bits_per_sample = 12, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SGRBG12_1X12, - .fmt = { - .fourcc = V4L2_PIX_FMT_SGRBG12, - .name = "Bayer 12 GRBG", - .bits_per_sample = 12, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SRGGB12_1X12, - .fmt = { - .fourcc = V4L2_PIX_FMT_SRGGB12, - .name = "Bayer 12 RGGB", - .bits_per_sample = 12, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, -}; - -int soc_mbus_samples_per_pixel(const struct soc_mbus_pixelfmt *mf, - unsigned int *numerator, unsigned int *denominator) -{ - switch (mf->packing) { - case SOC_MBUS_PACKING_NONE: - case SOC_MBUS_PACKING_EXTEND16: - *numerator = 1; - *denominator = 1; - return 0; - case SOC_MBUS_PACKING_EXTEND32: - *numerator = 1; - *denominator = 1; - return 0; - case SOC_MBUS_PACKING_2X8_PADHI: - case SOC_MBUS_PACKING_2X8_PADLO: - *numerator = 2; - *denominator = 1; - return 0; - case SOC_MBUS_PACKING_1_5X8: - *numerator = 3; - *denominator = 2; - return 0; - case SOC_MBUS_PACKING_VARIABLE: - *numerator = 0; - *denominator = 1; - return 0; - } - return -EINVAL; -} -EXPORT_SYMBOL(soc_mbus_samples_per_pixel); - -s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf) -{ - if (mf->layout != SOC_MBUS_LAYOUT_PACKED) - return width * mf->bits_per_sample / 8; - - switch (mf->packing) { - case SOC_MBUS_PACKING_NONE: - return width * mf->bits_per_sample / 8; - case SOC_MBUS_PACKING_2X8_PADHI: - case SOC_MBUS_PACKING_2X8_PADLO: - case SOC_MBUS_PACKING_EXTEND16: - return width * 2; - case SOC_MBUS_PACKING_1_5X8: - return width * 3 / 2; - case SOC_MBUS_PACKING_VARIABLE: - return 0; - case SOC_MBUS_PACKING_EXTEND32: - return width * 4; - } - return -EINVAL; -} -EXPORT_SYMBOL(soc_mbus_bytes_per_line); - -s32 soc_mbus_image_size(const struct soc_mbus_pixelfmt *mf, - u32 bytes_per_line, u32 height) -{ - if (mf->layout == SOC_MBUS_LAYOUT_PACKED) - return bytes_per_line * height; - - switch (mf->packing) { - case SOC_MBUS_PACKING_2X8_PADHI: - case SOC_MBUS_PACKING_2X8_PADLO: - return bytes_per_line * height * 2; - case SOC_MBUS_PACKING_1_5X8: - return bytes_per_line * height * 3 / 2; - default: - return -EINVAL; - } -} -EXPORT_SYMBOL(soc_mbus_image_size); - -const struct soc_mbus_pixelfmt *soc_mbus_find_fmtdesc( - u32 code, - const struct soc_mbus_lookup *lookup, - int n) -{ - int i; - - for (i = 0; i < n; i++) - if (lookup[i].code == code) - return &lookup[i].fmt; - - return NULL; -} -EXPORT_SYMBOL(soc_mbus_find_fmtdesc); - -const struct soc_mbus_pixelfmt *soc_mbus_get_fmtdesc( - u32 code) -{ - return soc_mbus_find_fmtdesc(code, mbus_fmt, ARRAY_SIZE(mbus_fmt)); -} -EXPORT_SYMBOL(soc_mbus_get_fmtdesc); - -unsigned int soc_mbus_config_compatible(const struct v4l2_mbus_config *cfg, - unsigned int flags) -{ - unsigned long common_flags; - bool hsync = true, vsync = true, pclk, data, mode; - bool mipi_lanes, mipi_clock; - - common_flags = cfg->flags & flags; - - switch (cfg->type) { - case V4L2_MBUS_PARALLEL: - hsync = common_flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH | - V4L2_MBUS_HSYNC_ACTIVE_LOW); - vsync = common_flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH | - V4L2_MBUS_VSYNC_ACTIVE_LOW); - /* fall through */ - case V4L2_MBUS_BT656: - pclk = common_flags & (V4L2_MBUS_PCLK_SAMPLE_RISING | - V4L2_MBUS_PCLK_SAMPLE_FALLING); - data = common_flags & (V4L2_MBUS_DATA_ACTIVE_HIGH | - V4L2_MBUS_DATA_ACTIVE_LOW); - mode = common_flags & (V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE); - return (!hsync || !vsync || !pclk || !data || !mode) ? - 0 : common_flags; - case V4L2_MBUS_CSI2_DPHY: - mipi_lanes = common_flags & V4L2_MBUS_CSI2_LANES; - mipi_clock = common_flags & (V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK | - V4L2_MBUS_CSI2_CONTINUOUS_CLOCK); - return (!mipi_lanes || !mipi_clock) ? 0 : common_flags; - default: - WARN_ON(1); - return -EINVAL; - } - return 0; -} -EXPORT_SYMBOL(soc_mbus_config_compatible); - -static int __init soc_mbus_init(void) -{ - return 0; -} - -static void __exit soc_mbus_exit(void) -{ -} - -module_init(soc_mbus_init); -module_exit(soc_mbus_exit); - -MODULE_DESCRIPTION("soc-camera media bus interface"); -MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/soc_camera/soc_mt9v022.c b/drivers/staging/media/soc_camera/soc_mt9v022.c deleted file mode 100644 index 1739a618846d..000000000000 --- a/drivers/staging/media/soc_camera/soc_mt9v022.c +++ /dev/null @@ -1,1008 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Driver for MT9V022 CMOS Image Sensor from Micron - * - * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> - */ -#include <linux/videodev2.h> -#include <linux/slab.h> -#include <linux/i2c.h> -#include <linux/delay.h> -#include <linux/log2.h> -#include <linux/module.h> - -#include <media/i2c/mt9v022.h> -#include <media/soc_camera.h> -#include <media/drv-intf/soc_mediabus.h> -#include <media/v4l2-subdev.h> -#include <media/v4l2-clk.h> -#include <media/v4l2-ctrls.h> - -/* - * mt9v022 i2c address 0x48, 0x4c, 0x58, 0x5c - * The platform has to define struct i2c_board_info objects and link to them - * from struct soc_camera_host_desc - */ - -static char *sensor_type; -module_param(sensor_type, charp, S_IRUGO); -MODULE_PARM_DESC(sensor_type, "Sensor type: \"colour\" or \"monochrome\""); - -/* mt9v022 selected register addresses */ -#define MT9V022_CHIP_VERSION 0x00 -#define MT9V022_COLUMN_START 0x01 -#define MT9V022_ROW_START 0x02 -#define MT9V022_WINDOW_HEIGHT 0x03 -#define MT9V022_WINDOW_WIDTH 0x04 -#define MT9V022_HORIZONTAL_BLANKING 0x05 -#define MT9V022_VERTICAL_BLANKING 0x06 -#define MT9V022_CHIP_CONTROL 0x07 -#define MT9V022_SHUTTER_WIDTH1 0x08 -#define MT9V022_SHUTTER_WIDTH2 0x09 -#define MT9V022_SHUTTER_WIDTH_CTRL 0x0a -#define MT9V022_TOTAL_SHUTTER_WIDTH 0x0b -#define MT9V022_RESET 0x0c -#define MT9V022_READ_MODE 0x0d -#define MT9V022_MONITOR_MODE 0x0e -#define MT9V022_PIXEL_OPERATION_MODE 0x0f -#define MT9V022_LED_OUT_CONTROL 0x1b -#define MT9V022_ADC_MODE_CONTROL 0x1c -#define MT9V022_REG32 0x20 -#define MT9V022_ANALOG_GAIN 0x35 -#define MT9V022_BLACK_LEVEL_CALIB_CTRL 0x47 -#define MT9V022_PIXCLK_FV_LV 0x74 -#define MT9V022_DIGITAL_TEST_PATTERN 0x7f -#define MT9V022_AEC_AGC_ENABLE 0xAF -#define MT9V022_MAX_TOTAL_SHUTTER_WIDTH 0xBD - -/* mt9v024 partial list register addresses changes with respect to mt9v022 */ -#define MT9V024_PIXCLK_FV_LV 0x72 -#define MT9V024_MAX_TOTAL_SHUTTER_WIDTH 0xAD - -/* Progressive scan, master, defaults */ -#define MT9V022_CHIP_CONTROL_DEFAULT 0x188 - -#define MT9V022_MAX_WIDTH 752 -#define MT9V022_MAX_HEIGHT 480 -#define MT9V022_MIN_WIDTH 48 -#define MT9V022_MIN_HEIGHT 32 -#define MT9V022_COLUMN_SKIP 1 -#define MT9V022_ROW_SKIP 4 - -#define MT9V022_HORIZONTAL_BLANKING_MIN 43 -#define MT9V022_HORIZONTAL_BLANKING_MAX 1023 -#define MT9V022_HORIZONTAL_BLANKING_DEF 94 -#define MT9V022_VERTICAL_BLANKING_MIN 2 -#define MT9V022_VERTICAL_BLANKING_MAX 3000 -#define MT9V022_VERTICAL_BLANKING_DEF 45 - -#define is_mt9v022_rev3(id) (id == 0x1313) -#define is_mt9v024(id) (id == 0x1324) - -/* MT9V022 has only one fixed colorspace per pixelcode */ -struct mt9v022_datafmt { - u32 code; - enum v4l2_colorspace colorspace; -}; - -/* Find a data format by a pixel code in an array */ -static const struct mt9v022_datafmt *mt9v022_find_datafmt( - u32 code, const struct mt9v022_datafmt *fmt, - int n) -{ - int i; - for (i = 0; i < n; i++) - if (fmt[i].code == code) - return fmt + i; - - return NULL; -} - -static const struct mt9v022_datafmt mt9v022_colour_fmts[] = { - /* - * Order important: first natively supported, - * second supported with a GPIO extender - */ - {MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB}, - {MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB}, -}; - -static const struct mt9v022_datafmt mt9v022_monochrome_fmts[] = { - /* Order important - see above */ - {MEDIA_BUS_FMT_Y10_1X10, V4L2_COLORSPACE_JPEG}, - {MEDIA_BUS_FMT_Y8_1X8, V4L2_COLORSPACE_JPEG}, -}; - -/* only registers with different addresses on different mt9v02x sensors */ -struct mt9v02x_register { - u8 max_total_shutter_width; - u8 pixclk_fv_lv; -}; - -static const struct mt9v02x_register mt9v022_register = { - .max_total_shutter_width = MT9V022_MAX_TOTAL_SHUTTER_WIDTH, - .pixclk_fv_lv = MT9V022_PIXCLK_FV_LV, -}; - -static const struct mt9v02x_register mt9v024_register = { - .max_total_shutter_width = MT9V024_MAX_TOTAL_SHUTTER_WIDTH, - .pixclk_fv_lv = MT9V024_PIXCLK_FV_LV, -}; - -enum mt9v022_model { - MT9V022IX7ATM, - MT9V022IX7ATC, -}; - -struct mt9v022 { - struct v4l2_subdev subdev; - struct v4l2_ctrl_handler hdl; - struct { - /* exposure/auto-exposure cluster */ - struct v4l2_ctrl *autoexposure; - struct v4l2_ctrl *exposure; - }; - struct { - /* gain/auto-gain cluster */ - struct v4l2_ctrl *autogain; - struct v4l2_ctrl *gain; - }; - struct v4l2_ctrl *hblank; - struct v4l2_ctrl *vblank; - struct v4l2_rect rect; /* Sensor window */ - struct v4l2_clk *clk; - const struct mt9v022_datafmt *fmt; - const struct mt9v022_datafmt *fmts; - const struct mt9v02x_register *reg; - int num_fmts; - enum mt9v022_model model; - u16 chip_control; - u16 chip_version; - unsigned short y_skip_top; /* Lines to skip at the top */ -}; - -static struct mt9v022 *to_mt9v022(const struct i2c_client *client) -{ - return container_of(i2c_get_clientdata(client), struct mt9v022, subdev); -} - -static int reg_read(struct i2c_client *client, const u8 reg) -{ - return i2c_smbus_read_word_swapped(client, reg); -} - -static int reg_write(struct i2c_client *client, const u8 reg, - const u16 data) -{ - return i2c_smbus_write_word_swapped(client, reg, data); -} - -static int reg_set(struct i2c_client *client, const u8 reg, - const u16 data) -{ - int ret; - - ret = reg_read(client, reg); - if (ret < 0) - return ret; - return reg_write(client, reg, ret | data); -} - -static int reg_clear(struct i2c_client *client, const u8 reg, - const u16 data) -{ - int ret; - - ret = reg_read(client, reg); - if (ret < 0) - return ret; - return reg_write(client, reg, ret & ~data); -} - -static int mt9v022_init(struct i2c_client *client) -{ - struct mt9v022 *mt9v022 = to_mt9v022(client); - int ret; - - /* - * Almost the default mode: master, parallel, simultaneous, and an - * undocumented bit 0x200, which is present in table 7, but not in 8, - * plus snapshot mode to disable scan for now - */ - mt9v022->chip_control |= 0x10; - ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control); - if (!ret) - ret = reg_write(client, MT9V022_READ_MODE, 0x300); - - /* All defaults */ - if (!ret) - /* AEC, AGC on */ - ret = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x3); - if (!ret) - ret = reg_write(client, MT9V022_ANALOG_GAIN, 16); - if (!ret) - ret = reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH, 480); - if (!ret) - ret = reg_write(client, mt9v022->reg->max_total_shutter_width, 480); - if (!ret) - /* default - auto */ - ret = reg_clear(client, MT9V022_BLACK_LEVEL_CALIB_CTRL, 1); - if (!ret) - ret = reg_write(client, MT9V022_DIGITAL_TEST_PATTERN, 0); - if (!ret) - return v4l2_ctrl_handler_setup(&mt9v022->hdl); - - return ret; -} - -static int mt9v022_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9v022 *mt9v022 = to_mt9v022(client); - - if (enable) { - /* Switch to master "normal" mode */ - mt9v022->chip_control &= ~0x10; - if (is_mt9v022_rev3(mt9v022->chip_version) || - is_mt9v024(mt9v022->chip_version)) { - /* - * Unset snapshot mode specific settings: clear bit 9 - * and bit 2 in reg. 0x20 when in normal mode. - */ - if (reg_clear(client, MT9V022_REG32, 0x204)) - return -EIO; - } - } else { - /* Switch to snapshot mode */ - mt9v022->chip_control |= 0x10; - if (is_mt9v022_rev3(mt9v022->chip_version) || - is_mt9v024(mt9v022->chip_version)) { - /* - * Required settings for snapshot mode: set bit 9 - * (RST enable) and bit 2 (CR enable) in reg. 0x20 - * See TechNote TN0960 or TN-09-225. - */ - if (reg_set(client, MT9V022_REG32, 0x204)) - return -EIO; - } - } - - if (reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control) < 0) - return -EIO; - return 0; -} - -static int mt9v022_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9v022 *mt9v022 = to_mt9v022(client); - struct v4l2_rect rect = sel->r; - int min_row, min_blank; - int ret; - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || - sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - /* Bayer format - even size lengths */ - if (mt9v022->fmts == mt9v022_colour_fmts) { - rect.width = ALIGN(rect.width, 2); - rect.height = ALIGN(rect.height, 2); - /* Let the user play with the starting pixel */ - } - - soc_camera_limit_side(&rect.left, &rect.width, - MT9V022_COLUMN_SKIP, MT9V022_MIN_WIDTH, MT9V022_MAX_WIDTH); - - soc_camera_limit_side(&rect.top, &rect.height, - MT9V022_ROW_SKIP, MT9V022_MIN_HEIGHT, MT9V022_MAX_HEIGHT); - - /* Like in example app. Contradicts the datasheet though */ - ret = reg_read(client, MT9V022_AEC_AGC_ENABLE); - if (ret >= 0) { - if (ret & 1) /* Autoexposure */ - ret = reg_write(client, mt9v022->reg->max_total_shutter_width, - rect.height + mt9v022->y_skip_top + 43); - /* - * If autoexposure is off, there is no need to set - * MT9V022_TOTAL_SHUTTER_WIDTH here. Autoexposure can be off - * only if the user has set exposure manually, using the - * V4L2_CID_EXPOSURE_AUTO with the value V4L2_EXPOSURE_MANUAL. - * In this case the register MT9V022_TOTAL_SHUTTER_WIDTH - * already contains the correct value. - */ - } - /* Setup frame format: defaults apart from width and height */ - if (!ret) - ret = reg_write(client, MT9V022_COLUMN_START, rect.left); - if (!ret) - ret = reg_write(client, MT9V022_ROW_START, rect.top); - /* - * mt9v022: min total row time is 660 columns, min blanking is 43 - * mt9v024: min total row time is 690 columns, min blanking is 61 - */ - if (is_mt9v024(mt9v022->chip_version)) { - min_row = 690; - min_blank = 61; - } else { - min_row = 660; - min_blank = 43; - } - if (!ret) - ret = v4l2_ctrl_s_ctrl(mt9v022->hblank, - rect.width > min_row - min_blank ? - min_blank : min_row - rect.width); - if (!ret) - ret = v4l2_ctrl_s_ctrl(mt9v022->vblank, 45); - if (!ret) - ret = reg_write(client, MT9V022_WINDOW_WIDTH, rect.width); - if (!ret) - ret = reg_write(client, MT9V022_WINDOW_HEIGHT, - rect.height + mt9v022->y_skip_top); - - if (ret < 0) - return ret; - - dev_dbg(&client->dev, "Frame %dx%d pixel\n", rect.width, rect.height); - - mt9v022->rect = rect; - - return 0; -} - -static int mt9v022_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9v022 *mt9v022 = to_mt9v022(client); - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.left = MT9V022_COLUMN_SKIP; - sel->r.top = MT9V022_ROW_SKIP; - sel->r.width = MT9V022_MAX_WIDTH; - sel->r.height = MT9V022_MAX_HEIGHT; - return 0; - case V4L2_SEL_TGT_CROP: - sel->r = mt9v022->rect; - return 0; - default: - return -EINVAL; - } -} - -static int mt9v022_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9v022 *mt9v022 = to_mt9v022(client); - - if (format->pad) - return -EINVAL; - - mf->width = mt9v022->rect.width; - mf->height = mt9v022->rect.height; - mf->code = mt9v022->fmt->code; - mf->colorspace = mt9v022->fmt->colorspace; - mf->field = V4L2_FIELD_NONE; - - return 0; -} - -static int mt9v022_s_fmt(struct v4l2_subdev *sd, - const struct mt9v022_datafmt *fmt, - struct v4l2_mbus_framefmt *mf) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9v022 *mt9v022 = to_mt9v022(client); - struct v4l2_subdev_selection sel = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .target = V4L2_SEL_TGT_CROP, - .r.left = mt9v022->rect.left, - .r.top = mt9v022->rect.top, - .r.width = mf->width, - .r.height = mf->height, - }; - int ret; - - /* - * The caller provides a supported format, as verified per call to - * .set_fmt(FORMAT_TRY), datawidth is from our supported format list - */ - switch (mf->code) { - case MEDIA_BUS_FMT_Y8_1X8: - case MEDIA_BUS_FMT_Y10_1X10: - if (mt9v022->model != MT9V022IX7ATM) - return -EINVAL; - break; - case MEDIA_BUS_FMT_SBGGR8_1X8: - case MEDIA_BUS_FMT_SBGGR10_1X10: - if (mt9v022->model != MT9V022IX7ATC) - return -EINVAL; - break; - default: - return -EINVAL; - } - - /* No support for scaling on this camera, just crop. */ - ret = mt9v022_set_selection(sd, NULL, &sel); - if (!ret) { - mf->width = mt9v022->rect.width; - mf->height = mt9v022->rect.height; - mt9v022->fmt = fmt; - mf->colorspace = fmt->colorspace; - } - - return ret; -} - -static int mt9v022_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9v022 *mt9v022 = to_mt9v022(client); - const struct mt9v022_datafmt *fmt; - int align = mf->code == MEDIA_BUS_FMT_SBGGR8_1X8 || - mf->code == MEDIA_BUS_FMT_SBGGR10_1X10; - - if (format->pad) - return -EINVAL; - - v4l_bound_align_image(&mf->width, MT9V022_MIN_WIDTH, - MT9V022_MAX_WIDTH, align, - &mf->height, MT9V022_MIN_HEIGHT + mt9v022->y_skip_top, - MT9V022_MAX_HEIGHT + mt9v022->y_skip_top, align, 0); - - fmt = mt9v022_find_datafmt(mf->code, mt9v022->fmts, - mt9v022->num_fmts); - if (!fmt) { - fmt = mt9v022->fmt; - mf->code = fmt->code; - } - - mf->colorspace = fmt->colorspace; - - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return mt9v022_s_fmt(sd, fmt, mf); - cfg->try_fmt = *mf; - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int mt9v022_g_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg > 0xff) - return -EINVAL; - - reg->size = 2; - reg->val = reg_read(client, reg->reg); - - if (reg->val > 0xffff) - return -EIO; - - return 0; -} - -static int mt9v022_s_register(struct v4l2_subdev *sd, - const struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg > 0xff) - return -EINVAL; - - if (reg_write(client, reg->reg, reg->val) < 0) - return -EIO; - - return 0; -} -#endif - -static int mt9v022_s_power(struct v4l2_subdev *sd, int on) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct mt9v022 *mt9v022 = to_mt9v022(client); - - return soc_camera_set_power(&client->dev, ssdd, mt9v022->clk, on); -} - -static int mt9v022_g_volatile_ctrl(struct v4l2_ctrl *ctrl) -{ - struct mt9v022 *mt9v022 = container_of(ctrl->handler, - struct mt9v022, hdl); - struct v4l2_subdev *sd = &mt9v022->subdev; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct v4l2_ctrl *gain = mt9v022->gain; - struct v4l2_ctrl *exp = mt9v022->exposure; - unsigned long range; - int data; - - switch (ctrl->id) { - case V4L2_CID_AUTOGAIN: - data = reg_read(client, MT9V022_ANALOG_GAIN); - if (data < 0) - return -EIO; - - range = gain->maximum - gain->minimum; - gain->val = ((data - 16) * range + 24) / 48 + gain->minimum; - return 0; - case V4L2_CID_EXPOSURE_AUTO: - data = reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH); - if (data < 0) - return -EIO; - - range = exp->maximum - exp->minimum; - exp->val = ((data - 1) * range + 239) / 479 + exp->minimum; - return 0; - case V4L2_CID_HBLANK: - data = reg_read(client, MT9V022_HORIZONTAL_BLANKING); - if (data < 0) - return -EIO; - ctrl->val = data; - return 0; - case V4L2_CID_VBLANK: - data = reg_read(client, MT9V022_VERTICAL_BLANKING); - if (data < 0) - return -EIO; - ctrl->val = data; - return 0; - } - return -EINVAL; -} - -static int mt9v022_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct mt9v022 *mt9v022 = container_of(ctrl->handler, - struct mt9v022, hdl); - struct v4l2_subdev *sd = &mt9v022->subdev; - struct i2c_client *client = v4l2_get_subdevdata(sd); - int data; - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - if (ctrl->val) - data = reg_set(client, MT9V022_READ_MODE, 0x10); - else - data = reg_clear(client, MT9V022_READ_MODE, 0x10); - if (data < 0) - return -EIO; - return 0; - case V4L2_CID_HFLIP: - if (ctrl->val) - data = reg_set(client, MT9V022_READ_MODE, 0x20); - else - data = reg_clear(client, MT9V022_READ_MODE, 0x20); - if (data < 0) - return -EIO; - return 0; - case V4L2_CID_AUTOGAIN: - if (ctrl->val) { - if (reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0) - return -EIO; - } else { - struct v4l2_ctrl *gain = mt9v022->gain; - /* mt9v022 has minimum == default */ - unsigned long range = gain->maximum - gain->minimum; - /* Valid values 16 to 64, 32 to 64 must be even. */ - unsigned long gain_val = ((gain->val - (s32)gain->minimum) * - 48 + range / 2) / range + 16; - - if (gain_val >= 32) - gain_val &= ~1; - - /* - * The user wants to set gain manually, hope, she - * knows, what she's doing... Switch AGC off. - */ - if (reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0) - return -EIO; - - dev_dbg(&client->dev, "Setting gain from %d to %lu\n", - reg_read(client, MT9V022_ANALOG_GAIN), gain_val); - if (reg_write(client, MT9V022_ANALOG_GAIN, gain_val) < 0) - return -EIO; - } - return 0; - case V4L2_CID_EXPOSURE_AUTO: - if (ctrl->val == V4L2_EXPOSURE_AUTO) { - data = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x1); - } else { - struct v4l2_ctrl *exp = mt9v022->exposure; - unsigned long range = exp->maximum - exp->minimum; - unsigned long shutter = ((exp->val - (s32)exp->minimum) * - 479 + range / 2) / range + 1; - - /* - * The user wants to set shutter width manually, hope, - * she knows, what she's doing... Switch AEC off. - */ - data = reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1); - if (data < 0) - return -EIO; - dev_dbg(&client->dev, "Shutter width from %d to %lu\n", - reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH), - shutter); - if (reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH, - shutter) < 0) - return -EIO; - } - return 0; - case V4L2_CID_HBLANK: - if (reg_write(client, MT9V022_HORIZONTAL_BLANKING, - ctrl->val) < 0) - return -EIO; - return 0; - case V4L2_CID_VBLANK: - if (reg_write(client, MT9V022_VERTICAL_BLANKING, - ctrl->val) < 0) - return -EIO; - return 0; - } - return -EINVAL; -} - -/* - * Interface active, can use i2c. If it fails, it can indeed mean, that - * this wasn't our capture interface, so, we wait for the right one - */ -static int mt9v022_video_probe(struct i2c_client *client) -{ - struct mt9v022 *mt9v022 = to_mt9v022(client); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - s32 data; - int ret; - unsigned long flags; - - ret = mt9v022_s_power(&mt9v022->subdev, 1); - if (ret < 0) - return ret; - - /* Read out the chip version register */ - data = reg_read(client, MT9V022_CHIP_VERSION); - - /* must be 0x1311, 0x1313 or 0x1324 */ - if (data != 0x1311 && data != 0x1313 && data != 0x1324) { - ret = -ENODEV; - dev_info(&client->dev, "No MT9V022 found, ID register 0x%x\n", - data); - goto ei2c; - } - - mt9v022->chip_version = data; - - mt9v022->reg = is_mt9v024(data) ? &mt9v024_register : - &mt9v022_register; - - /* Soft reset */ - ret = reg_write(client, MT9V022_RESET, 1); - if (ret < 0) - goto ei2c; - /* 15 clock cycles */ - udelay(200); - if (reg_read(client, MT9V022_RESET)) { - dev_err(&client->dev, "Resetting MT9V022 failed!\n"); - if (ret > 0) - ret = -EIO; - goto ei2c; - } - - /* Set monochrome or colour sensor type */ - if (sensor_type && (!strcmp("colour", sensor_type) || - !strcmp("color", sensor_type))) { - ret = reg_write(client, MT9V022_PIXEL_OPERATION_MODE, 4 | 0x11); - mt9v022->model = MT9V022IX7ATC; - mt9v022->fmts = mt9v022_colour_fmts; - } else { - ret = reg_write(client, MT9V022_PIXEL_OPERATION_MODE, 0x11); - mt9v022->model = MT9V022IX7ATM; - mt9v022->fmts = mt9v022_monochrome_fmts; - } - - if (ret < 0) - goto ei2c; - - mt9v022->num_fmts = 0; - - /* - * This is a 10bit sensor, so by default we only allow 10bit. - * The platform may support different bus widths due to - * different routing of the data lines. - */ - if (ssdd->query_bus_param) - flags = ssdd->query_bus_param(ssdd); - else - flags = SOCAM_DATAWIDTH_10; - - if (flags & SOCAM_DATAWIDTH_10) - mt9v022->num_fmts++; - else - mt9v022->fmts++; - - if (flags & SOCAM_DATAWIDTH_8) - mt9v022->num_fmts++; - - mt9v022->fmt = &mt9v022->fmts[0]; - - dev_info(&client->dev, "Detected a MT9V022 chip ID %x, %s sensor\n", - data, mt9v022->model == MT9V022IX7ATM ? - "monochrome" : "colour"); - - ret = mt9v022_init(client); - if (ret < 0) - dev_err(&client->dev, "Failed to initialise the camera\n"); - -ei2c: - mt9v022_s_power(&mt9v022->subdev, 0); - return ret; -} - -static int mt9v022_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9v022 *mt9v022 = to_mt9v022(client); - - *lines = mt9v022->y_skip_top; - - return 0; -} - -static const struct v4l2_ctrl_ops mt9v022_ctrl_ops = { - .g_volatile_ctrl = mt9v022_g_volatile_ctrl, - .s_ctrl = mt9v022_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops mt9v022_subdev_core_ops = { -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = mt9v022_g_register, - .s_register = mt9v022_s_register, -#endif - .s_power = mt9v022_s_power, -}; - -static int mt9v022_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9v022 *mt9v022 = to_mt9v022(client); - - if (code->pad || code->index >= mt9v022->num_fmts) - return -EINVAL; - - code->code = mt9v022->fmts[code->index].code; - return 0; -} - -static int mt9v022_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE | - V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | - V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW | - V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | - V4L2_MBUS_DATA_ACTIVE_HIGH; - cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); - - return 0; -} - -static int mt9v022_s_mbus_config(struct v4l2_subdev *sd, - const struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct mt9v022 *mt9v022 = to_mt9v022(client); - unsigned long flags = soc_camera_apply_board_flags(ssdd, cfg); - unsigned int bps = soc_mbus_get_fmtdesc(mt9v022->fmt->code)->bits_per_sample; - int ret; - u16 pixclk = 0; - - if (ssdd->set_bus_param) { - ret = ssdd->set_bus_param(ssdd, 1 << (bps - 1)); - if (ret) - return ret; - } else if (bps != 10) { - /* - * Without board specific bus width settings we only support the - * sensors native bus width - */ - return -EINVAL; - } - - if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) - pixclk |= 0x10; - - if (!(flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)) - pixclk |= 0x1; - - if (!(flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)) - pixclk |= 0x2; - - ret = reg_write(client, mt9v022->reg->pixclk_fv_lv, pixclk); - if (ret < 0) - return ret; - - if (!(flags & V4L2_MBUS_MASTER)) - mt9v022->chip_control &= ~0x8; - - ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control); - if (ret < 0) - return ret; - - dev_dbg(&client->dev, "Calculated pixclk 0x%x, chip control 0x%x\n", - pixclk, mt9v022->chip_control); - - return 0; -} - -static const struct v4l2_subdev_video_ops mt9v022_subdev_video_ops = { - .s_stream = mt9v022_s_stream, - .g_mbus_config = mt9v022_g_mbus_config, - .s_mbus_config = mt9v022_s_mbus_config, -}; - -static const struct v4l2_subdev_sensor_ops mt9v022_subdev_sensor_ops = { - .g_skip_top_lines = mt9v022_g_skip_top_lines, -}; - -static const struct v4l2_subdev_pad_ops mt9v022_subdev_pad_ops = { - .enum_mbus_code = mt9v022_enum_mbus_code, - .get_selection = mt9v022_get_selection, - .set_selection = mt9v022_set_selection, - .get_fmt = mt9v022_get_fmt, - .set_fmt = mt9v022_set_fmt, -}; - -static const struct v4l2_subdev_ops mt9v022_subdev_ops = { - .core = &mt9v022_subdev_core_ops, - .video = &mt9v022_subdev_video_ops, - .sensor = &mt9v022_subdev_sensor_ops, - .pad = &mt9v022_subdev_pad_ops, -}; - -static int mt9v022_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct mt9v022 *mt9v022; - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct i2c_adapter *adapter = client->adapter; - struct mt9v022_platform_data *pdata; - int ret; - - if (!ssdd) { - dev_err(&client->dev, "MT9V022 driver needs platform data\n"); - return -EINVAL; - } - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { - dev_warn(&adapter->dev, - "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); - return -EIO; - } - - mt9v022 = devm_kzalloc(&client->dev, sizeof(struct mt9v022), GFP_KERNEL); - if (!mt9v022) - return -ENOMEM; - - pdata = ssdd->drv_priv; - v4l2_i2c_subdev_init(&mt9v022->subdev, client, &mt9v022_subdev_ops); - v4l2_ctrl_handler_init(&mt9v022->hdl, 6); - v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - mt9v022->autogain = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, - V4L2_CID_AUTOGAIN, 0, 1, 1, 1); - mt9v022->gain = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, - V4L2_CID_GAIN, 0, 127, 1, 64); - - /* - * Simulated autoexposure. If enabled, we calculate shutter width - * ourselves in the driver based on vertical blanking and frame width - */ - mt9v022->autoexposure = v4l2_ctrl_new_std_menu(&mt9v022->hdl, - &mt9v022_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0, - V4L2_EXPOSURE_AUTO); - mt9v022->exposure = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, - V4L2_CID_EXPOSURE, 1, 255, 1, 255); - - mt9v022->hblank = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, - V4L2_CID_HBLANK, MT9V022_HORIZONTAL_BLANKING_MIN, - MT9V022_HORIZONTAL_BLANKING_MAX, 1, - MT9V022_HORIZONTAL_BLANKING_DEF); - - mt9v022->vblank = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, - V4L2_CID_VBLANK, MT9V022_VERTICAL_BLANKING_MIN, - MT9V022_VERTICAL_BLANKING_MAX, 1, - MT9V022_VERTICAL_BLANKING_DEF); - - mt9v022->subdev.ctrl_handler = &mt9v022->hdl; - if (mt9v022->hdl.error) { - int err = mt9v022->hdl.error; - - dev_err(&client->dev, "control initialisation err %d\n", err); - return err; - } - v4l2_ctrl_auto_cluster(2, &mt9v022->autoexposure, - V4L2_EXPOSURE_MANUAL, true); - v4l2_ctrl_auto_cluster(2, &mt9v022->autogain, 0, true); - - mt9v022->chip_control = MT9V022_CHIP_CONTROL_DEFAULT; - - /* - * On some platforms the first read out line is corrupted. - * Workaround it by skipping if indicated by platform data. - */ - mt9v022->y_skip_top = pdata ? pdata->y_skip_top : 0; - mt9v022->rect.left = MT9V022_COLUMN_SKIP; - mt9v022->rect.top = MT9V022_ROW_SKIP; - mt9v022->rect.width = MT9V022_MAX_WIDTH; - mt9v022->rect.height = MT9V022_MAX_HEIGHT; - - mt9v022->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(mt9v022->clk)) { - ret = PTR_ERR(mt9v022->clk); - goto eclkget; - } - - ret = mt9v022_video_probe(client); - if (ret) { - v4l2_clk_put(mt9v022->clk); -eclkget: - v4l2_ctrl_handler_free(&mt9v022->hdl); - } - - return ret; -} - -static int mt9v022_remove(struct i2c_client *client) -{ - struct mt9v022 *mt9v022 = to_mt9v022(client); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - v4l2_clk_put(mt9v022->clk); - v4l2_device_unregister_subdev(&mt9v022->subdev); - if (ssdd->free_bus) - ssdd->free_bus(ssdd); - v4l2_ctrl_handler_free(&mt9v022->hdl); - - return 0; -} -static const struct i2c_device_id mt9v022_id[] = { - { "mt9v022", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, mt9v022_id); - -static struct i2c_driver mt9v022_i2c_driver = { - .driver = { - .name = "mt9v022", - }, - .probe = mt9v022_probe, - .remove = mt9v022_remove, - .id_table = mt9v022_id, -}; - -module_i2c_driver(mt9v022_i2c_driver); - -MODULE_DESCRIPTION("Micron MT9V022 Camera driver"); -MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/soc_camera/soc_ov5642.c b/drivers/staging/media/soc_camera/soc_ov5642.c deleted file mode 100644 index 39ae24dca65f..000000000000 --- a/drivers/staging/media/soc_camera/soc_ov5642.c +++ /dev/null @@ -1,1085 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Driver for OV5642 CMOS Image Sensor from Omnivision - * - * Copyright (C) 2011, Bastian Hecht <hechtb@gmail.com> - * - * Based on Sony IMX074 Camera Driver - * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de> - * - * Based on Omnivision OV7670 Camera Driver - * Copyright (C) 2006-7 Jonathan Corbet <corbet@lwn.net> - */ -#include <linux/bitops.h> -#include <linux/delay.h> -#include <linux/i2c.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/videodev2.h> -#include <linux/module.h> -#include <linux/v4l2-mediabus.h> - -#include <media/soc_camera.h> -#include <media/v4l2-clk.h> -#include <media/v4l2-subdev.h> - -/* OV5642 registers */ -#define REG_CHIP_ID_HIGH 0x300a -#define REG_CHIP_ID_LOW 0x300b - -#define REG_WINDOW_START_X_HIGH 0x3800 -#define REG_WINDOW_START_X_LOW 0x3801 -#define REG_WINDOW_START_Y_HIGH 0x3802 -#define REG_WINDOW_START_Y_LOW 0x3803 -#define REG_WINDOW_WIDTH_HIGH 0x3804 -#define REG_WINDOW_WIDTH_LOW 0x3805 -#define REG_WINDOW_HEIGHT_HIGH 0x3806 -#define REG_WINDOW_HEIGHT_LOW 0x3807 -#define REG_OUT_WIDTH_HIGH 0x3808 -#define REG_OUT_WIDTH_LOW 0x3809 -#define REG_OUT_HEIGHT_HIGH 0x380a -#define REG_OUT_HEIGHT_LOW 0x380b -#define REG_OUT_TOTAL_WIDTH_HIGH 0x380c -#define REG_OUT_TOTAL_WIDTH_LOW 0x380d -#define REG_OUT_TOTAL_HEIGHT_HIGH 0x380e -#define REG_OUT_TOTAL_HEIGHT_LOW 0x380f -#define REG_OUTPUT_FORMAT 0x4300 -#define REG_ISP_CTRL_01 0x5001 -#define REG_AVG_WINDOW_END_X_HIGH 0x5682 -#define REG_AVG_WINDOW_END_X_LOW 0x5683 -#define REG_AVG_WINDOW_END_Y_HIGH 0x5686 -#define REG_AVG_WINDOW_END_Y_LOW 0x5687 - -/* active pixel array size */ -#define OV5642_SENSOR_SIZE_X 2592 -#define OV5642_SENSOR_SIZE_Y 1944 - -/* - * About OV5642 resolution, cropping and binning: - * This sensor supports it all, at least in the feature description. - * Unfortunately, no combination of appropriate registers settings could make - * the chip work the intended way. As it works with predefined register lists, - * some undocumented registers are presumably changed there to achieve their - * goals. - * This driver currently only works for resolutions up to 720 lines with a - * 1:1 scale. Hopefully these restrictions will be removed in the future. - */ -#define OV5642_MAX_WIDTH OV5642_SENSOR_SIZE_X -#define OV5642_MAX_HEIGHT 720 - -/* default sizes */ -#define OV5642_DEFAULT_WIDTH 1280 -#define OV5642_DEFAULT_HEIGHT OV5642_MAX_HEIGHT - -/* minimum extra blanking */ -#define BLANKING_EXTRA_WIDTH 500 -#define BLANKING_EXTRA_HEIGHT 20 - -/* - * the sensor's autoexposure is buggy when setting total_height low. - * It tries to expose longer than 1 frame period without taking care of it - * and this leads to weird output. So we set 1000 lines as minimum. - */ -#define BLANKING_MIN_HEIGHT 1000 - -struct regval_list { - u16 reg_num; - u8 value; -}; - -static struct regval_list ov5642_default_regs_init[] = { - { 0x3103, 0x93 }, - { 0x3008, 0x82 }, - { 0x3017, 0x7f }, - { 0x3018, 0xfc }, - { 0x3810, 0xc2 }, - { 0x3615, 0xf0 }, - { 0x3000, 0x0 }, - { 0x3001, 0x0 }, - { 0x3002, 0x0 }, - { 0x3003, 0x0 }, - { 0x3004, 0xff }, - { 0x3030, 0x2b }, - { 0x3011, 0x8 }, - { 0x3010, 0x10 }, - { 0x3604, 0x60 }, - { 0x3622, 0x60 }, - { 0x3621, 0x9 }, - { 0x3709, 0x0 }, - { 0x4000, 0x21 }, - { 0x401d, 0x22 }, - { 0x3600, 0x54 }, - { 0x3605, 0x4 }, - { 0x3606, 0x3f }, - { 0x3c01, 0x80 }, - { 0x300d, 0x22 }, - { 0x3623, 0x22 }, - { 0x5000, 0x4f }, - { 0x5020, 0x4 }, - { 0x5181, 0x79 }, - { 0x5182, 0x0 }, - { 0x5185, 0x22 }, - { 0x5197, 0x1 }, - { 0x5500, 0xa }, - { 0x5504, 0x0 }, - { 0x5505, 0x7f }, - { 0x5080, 0x8 }, - { 0x300e, 0x18 }, - { 0x4610, 0x0 }, - { 0x471d, 0x5 }, - { 0x4708, 0x6 }, - { 0x370c, 0xa0 }, - { 0x5687, 0x94 }, - { 0x501f, 0x0 }, - { 0x5000, 0x4f }, - { 0x5001, 0xcf }, - { 0x4300, 0x30 }, - { 0x4300, 0x30 }, - { 0x460b, 0x35 }, - { 0x471d, 0x0 }, - { 0x3002, 0xc }, - { 0x3002, 0x0 }, - { 0x4713, 0x3 }, - { 0x471c, 0x50 }, - { 0x4721, 0x2 }, - { 0x4402, 0x90 }, - { 0x460c, 0x22 }, - { 0x3815, 0x44 }, - { 0x3503, 0x7 }, - { 0x3501, 0x73 }, - { 0x3502, 0x80 }, - { 0x350b, 0x0 }, - { 0x3818, 0xc8 }, - { 0x3824, 0x11 }, - { 0x3a00, 0x78 }, - { 0x3a1a, 0x4 }, - { 0x3a13, 0x30 }, - { 0x3a18, 0x0 }, - { 0x3a19, 0x7c }, - { 0x3a08, 0x12 }, - { 0x3a09, 0xc0 }, - { 0x3a0a, 0xf }, - { 0x3a0b, 0xa0 }, - { 0x350c, 0x7 }, - { 0x350d, 0xd0 }, - { 0x3a0d, 0x8 }, - { 0x3a0e, 0x6 }, - { 0x3500, 0x0 }, - { 0x3501, 0x0 }, - { 0x3502, 0x0 }, - { 0x350a, 0x0 }, - { 0x350b, 0x0 }, - { 0x3503, 0x0 }, - { 0x3a0f, 0x3c }, - { 0x3a10, 0x32 }, - { 0x3a1b, 0x3c }, - { 0x3a1e, 0x32 }, - { 0x3a11, 0x80 }, - { 0x3a1f, 0x20 }, - { 0x3030, 0x2b }, - { 0x3a02, 0x0 }, - { 0x3a03, 0x7d }, - { 0x3a04, 0x0 }, - { 0x3a14, 0x0 }, - { 0x3a15, 0x7d }, - { 0x3a16, 0x0 }, - { 0x3a00, 0x78 }, - { 0x3a08, 0x9 }, - { 0x3a09, 0x60 }, - { 0x3a0a, 0x7 }, - { 0x3a0b, 0xd0 }, - { 0x3a0d, 0x10 }, - { 0x3a0e, 0xd }, - { 0x4407, 0x4 }, - { 0x5193, 0x70 }, - { 0x589b, 0x0 }, - { 0x589a, 0xc0 }, - { 0x401e, 0x20 }, - { 0x4001, 0x42 }, - { 0x401c, 0x6 }, - { 0x3825, 0xac }, - { 0x3827, 0xc }, - { 0x528a, 0x1 }, - { 0x528b, 0x4 }, - { 0x528c, 0x8 }, - { 0x528d, 0x10 }, - { 0x528e, 0x20 }, - { 0x528f, 0x28 }, - { 0x5290, 0x30 }, - { 0x5292, 0x0 }, - { 0x5293, 0x1 }, - { 0x5294, 0x0 }, - { 0x5295, 0x4 }, - { 0x5296, 0x0 }, - { 0x5297, 0x8 }, - { 0x5298, 0x0 }, - { 0x5299, 0x10 }, - { 0x529a, 0x0 }, - { 0x529b, 0x20 }, - { 0x529c, 0x0 }, - { 0x529d, 0x28 }, - { 0x529e, 0x0 }, - { 0x529f, 0x30 }, - { 0x5282, 0x0 }, - { 0x5300, 0x0 }, - { 0x5301, 0x20 }, - { 0x5302, 0x0 }, - { 0x5303, 0x7c }, - { 0x530c, 0x0 }, - { 0x530d, 0xc }, - { 0x530e, 0x20 }, - { 0x530f, 0x80 }, - { 0x5310, 0x20 }, - { 0x5311, 0x80 }, - { 0x5308, 0x20 }, - { 0x5309, 0x40 }, - { 0x5304, 0x0 }, - { 0x5305, 0x30 }, - { 0x5306, 0x0 }, - { 0x5307, 0x80 }, - { 0x5314, 0x8 }, - { 0x5315, 0x20 }, - { 0x5319, 0x30 }, - { 0x5316, 0x10 }, - { 0x5317, 0x0 }, - { 0x5318, 0x2 }, - { 0x5380, 0x1 }, - { 0x5381, 0x0 }, - { 0x5382, 0x0 }, - { 0x5383, 0x4e }, - { 0x5384, 0x0 }, - { 0x5385, 0xf }, - { 0x5386, 0x0 }, - { 0x5387, 0x0 }, - { 0x5388, 0x1 }, - { 0x5389, 0x15 }, - { 0x538a, 0x0 }, - { 0x538b, 0x31 }, - { 0x538c, 0x0 }, - { 0x538d, 0x0 }, - { 0x538e, 0x0 }, - { 0x538f, 0xf }, - { 0x5390, 0x0 }, - { 0x5391, 0xab }, - { 0x5392, 0x0 }, - { 0x5393, 0xa2 }, - { 0x5394, 0x8 }, - { 0x5480, 0x14 }, - { 0x5481, 0x21 }, - { 0x5482, 0x36 }, - { 0x5483, 0x57 }, - { 0x5484, 0x65 }, - { 0x5485, 0x71 }, - { 0x5486, 0x7d }, - { 0x5487, 0x87 }, - { 0x5488, 0x91 }, - { 0x5489, 0x9a }, - { 0x548a, 0xaa }, - { 0x548b, 0xb8 }, - { 0x548c, 0xcd }, - { 0x548d, 0xdd }, - { 0x548e, 0xea }, - { 0x548f, 0x1d }, - { 0x5490, 0x5 }, - { 0x5491, 0x0 }, - { 0x5492, 0x4 }, - { 0x5493, 0x20 }, - { 0x5494, 0x3 }, - { 0x5495, 0x60 }, - { 0x5496, 0x2 }, - { 0x5497, 0xb8 }, - { 0x5498, 0x2 }, - { 0x5499, 0x86 }, - { 0x549a, 0x2 }, - { 0x549b, 0x5b }, - { 0x549c, 0x2 }, - { 0x549d, 0x3b }, - { 0x549e, 0x2 }, - { 0x549f, 0x1c }, - { 0x54a0, 0x2 }, - { 0x54a1, 0x4 }, - { 0x54a2, 0x1 }, - { 0x54a3, 0xed }, - { 0x54a4, 0x1 }, - { 0x54a5, 0xc5 }, - { 0x54a6, 0x1 }, - { 0x54a7, 0xa5 }, - { 0x54a8, 0x1 }, - { 0x54a9, 0x6c }, - { 0x54aa, 0x1 }, - { 0x54ab, 0x41 }, - { 0x54ac, 0x1 }, - { 0x54ad, 0x20 }, - { 0x54ae, 0x0 }, - { 0x54af, 0x16 }, - { 0x54b0, 0x1 }, - { 0x54b1, 0x20 }, - { 0x54b2, 0x0 }, - { 0x54b3, 0x10 }, - { 0x54b4, 0x0 }, - { 0x54b5, 0xf0 }, - { 0x54b6, 0x0 }, - { 0x54b7, 0xdf }, - { 0x5402, 0x3f }, - { 0x5403, 0x0 }, - { 0x3406, 0x0 }, - { 0x5180, 0xff }, - { 0x5181, 0x52 }, - { 0x5182, 0x11 }, - { 0x5183, 0x14 }, - { 0x5184, 0x25 }, - { 0x5185, 0x24 }, - { 0x5186, 0x6 }, - { 0x5187, 0x8 }, - { 0x5188, 0x8 }, - { 0x5189, 0x7c }, - { 0x518a, 0x60 }, - { 0x518b, 0xb2 }, - { 0x518c, 0xb2 }, - { 0x518d, 0x44 }, - { 0x518e, 0x3d }, - { 0x518f, 0x58 }, - { 0x5190, 0x46 }, - { 0x5191, 0xf8 }, - { 0x5192, 0x4 }, - { 0x5193, 0x70 }, - { 0x5194, 0xf0 }, - { 0x5195, 0xf0 }, - { 0x5196, 0x3 }, - { 0x5197, 0x1 }, - { 0x5198, 0x4 }, - { 0x5199, 0x12 }, - { 0x519a, 0x4 }, - { 0x519b, 0x0 }, - { 0x519c, 0x6 }, - { 0x519d, 0x82 }, - { 0x519e, 0x0 }, - { 0x5025, 0x80 }, - { 0x3a0f, 0x38 }, - { 0x3a10, 0x30 }, - { 0x3a1b, 0x3a }, - { 0x3a1e, 0x2e }, - { 0x3a11, 0x60 }, - { 0x3a1f, 0x10 }, - { 0x5688, 0xa6 }, - { 0x5689, 0x6a }, - { 0x568a, 0xea }, - { 0x568b, 0xae }, - { 0x568c, 0xa6 }, - { 0x568d, 0x6a }, - { 0x568e, 0x62 }, - { 0x568f, 0x26 }, - { 0x5583, 0x40 }, - { 0x5584, 0x40 }, - { 0x5580, 0x2 }, - { 0x5000, 0xcf }, - { 0x5800, 0x27 }, - { 0x5801, 0x19 }, - { 0x5802, 0x12 }, - { 0x5803, 0xf }, - { 0x5804, 0x10 }, - { 0x5805, 0x15 }, - { 0x5806, 0x1e }, - { 0x5807, 0x2f }, - { 0x5808, 0x15 }, - { 0x5809, 0xd }, - { 0x580a, 0xa }, - { 0x580b, 0x9 }, - { 0x580c, 0xa }, - { 0x580d, 0xc }, - { 0x580e, 0x12 }, - { 0x580f, 0x19 }, - { 0x5810, 0xb }, - { 0x5811, 0x7 }, - { 0x5812, 0x4 }, - { 0x5813, 0x3 }, - { 0x5814, 0x3 }, - { 0x5815, 0x6 }, - { 0x5816, 0xa }, - { 0x5817, 0xf }, - { 0x5818, 0xa }, - { 0x5819, 0x5 }, - { 0x581a, 0x1 }, - { 0x581b, 0x0 }, - { 0x581c, 0x0 }, - { 0x581d, 0x3 }, - { 0x581e, 0x8 }, - { 0x581f, 0xc }, - { 0x5820, 0xa }, - { 0x5821, 0x5 }, - { 0x5822, 0x1 }, - { 0x5823, 0x0 }, - { 0x5824, 0x0 }, - { 0x5825, 0x3 }, - { 0x5826, 0x8 }, - { 0x5827, 0xc }, - { 0x5828, 0xe }, - { 0x5829, 0x8 }, - { 0x582a, 0x6 }, - { 0x582b, 0x4 }, - { 0x582c, 0x5 }, - { 0x582d, 0x7 }, - { 0x582e, 0xb }, - { 0x582f, 0x12 }, - { 0x5830, 0x18 }, - { 0x5831, 0x10 }, - { 0x5832, 0xc }, - { 0x5833, 0xa }, - { 0x5834, 0xb }, - { 0x5835, 0xe }, - { 0x5836, 0x15 }, - { 0x5837, 0x19 }, - { 0x5838, 0x32 }, - { 0x5839, 0x1f }, - { 0x583a, 0x18 }, - { 0x583b, 0x16 }, - { 0x583c, 0x17 }, - { 0x583d, 0x1e }, - { 0x583e, 0x26 }, - { 0x583f, 0x53 }, - { 0x5840, 0x10 }, - { 0x5841, 0xf }, - { 0x5842, 0xd }, - { 0x5843, 0xc }, - { 0x5844, 0xe }, - { 0x5845, 0x9 }, - { 0x5846, 0x11 }, - { 0x5847, 0x10 }, - { 0x5848, 0x10 }, - { 0x5849, 0x10 }, - { 0x584a, 0x10 }, - { 0x584b, 0xe }, - { 0x584c, 0x10 }, - { 0x584d, 0x10 }, - { 0x584e, 0x11 }, - { 0x584f, 0x10 }, - { 0x5850, 0xf }, - { 0x5851, 0xc }, - { 0x5852, 0xf }, - { 0x5853, 0x10 }, - { 0x5854, 0x10 }, - { 0x5855, 0xf }, - { 0x5856, 0xe }, - { 0x5857, 0xb }, - { 0x5858, 0x10 }, - { 0x5859, 0xd }, - { 0x585a, 0xd }, - { 0x585b, 0xc }, - { 0x585c, 0xc }, - { 0x585d, 0xc }, - { 0x585e, 0xb }, - { 0x585f, 0xc }, - { 0x5860, 0xc }, - { 0x5861, 0xc }, - { 0x5862, 0xd }, - { 0x5863, 0x8 }, - { 0x5864, 0x11 }, - { 0x5865, 0x18 }, - { 0x5866, 0x18 }, - { 0x5867, 0x19 }, - { 0x5868, 0x17 }, - { 0x5869, 0x19 }, - { 0x586a, 0x16 }, - { 0x586b, 0x13 }, - { 0x586c, 0x13 }, - { 0x586d, 0x12 }, - { 0x586e, 0x13 }, - { 0x586f, 0x16 }, - { 0x5870, 0x14 }, - { 0x5871, 0x12 }, - { 0x5872, 0x10 }, - { 0x5873, 0x11 }, - { 0x5874, 0x11 }, - { 0x5875, 0x16 }, - { 0x5876, 0x14 }, - { 0x5877, 0x11 }, - { 0x5878, 0x10 }, - { 0x5879, 0xf }, - { 0x587a, 0x10 }, - { 0x587b, 0x14 }, - { 0x587c, 0x13 }, - { 0x587d, 0x12 }, - { 0x587e, 0x11 }, - { 0x587f, 0x11 }, - { 0x5880, 0x12 }, - { 0x5881, 0x15 }, - { 0x5882, 0x14 }, - { 0x5883, 0x15 }, - { 0x5884, 0x15 }, - { 0x5885, 0x15 }, - { 0x5886, 0x13 }, - { 0x5887, 0x17 }, - { 0x3710, 0x10 }, - { 0x3632, 0x51 }, - { 0x3702, 0x10 }, - { 0x3703, 0xb2 }, - { 0x3704, 0x18 }, - { 0x370b, 0x40 }, - { 0x370d, 0x3 }, - { 0x3631, 0x1 }, - { 0x3632, 0x52 }, - { 0x3606, 0x24 }, - { 0x3620, 0x96 }, - { 0x5785, 0x7 }, - { 0x3a13, 0x30 }, - { 0x3600, 0x52 }, - { 0x3604, 0x48 }, - { 0x3606, 0x1b }, - { 0x370d, 0xb }, - { 0x370f, 0xc0 }, - { 0x3709, 0x1 }, - { 0x3823, 0x0 }, - { 0x5007, 0x0 }, - { 0x5009, 0x0 }, - { 0x5011, 0x0 }, - { 0x5013, 0x0 }, - { 0x519e, 0x0 }, - { 0x5086, 0x0 }, - { 0x5087, 0x0 }, - { 0x5088, 0x0 }, - { 0x5089, 0x0 }, - { 0x302b, 0x0 }, - { 0x3503, 0x7 }, - { 0x3011, 0x8 }, - { 0x350c, 0x2 }, - { 0x350d, 0xe4 }, - { 0x3621, 0xc9 }, - { 0x370a, 0x81 }, - { 0xffff, 0xff }, -}; - -static struct regval_list ov5642_default_regs_finalise[] = { - { 0x3810, 0xc2 }, - { 0x3818, 0xc9 }, - { 0x381c, 0x10 }, - { 0x381d, 0xa0 }, - { 0x381e, 0x5 }, - { 0x381f, 0xb0 }, - { 0x3820, 0x0 }, - { 0x3821, 0x0 }, - { 0x3824, 0x11 }, - { 0x3a08, 0x1b }, - { 0x3a09, 0xc0 }, - { 0x3a0a, 0x17 }, - { 0x3a0b, 0x20 }, - { 0x3a0d, 0x2 }, - { 0x3a0e, 0x1 }, - { 0x401c, 0x4 }, - { 0x5682, 0x5 }, - { 0x5683, 0x0 }, - { 0x5686, 0x2 }, - { 0x5687, 0xcc }, - { 0x5001, 0x4f }, - { 0x589b, 0x6 }, - { 0x589a, 0xc5 }, - { 0x3503, 0x0 }, - { 0x460c, 0x20 }, - { 0x460b, 0x37 }, - { 0x471c, 0xd0 }, - { 0x471d, 0x5 }, - { 0x3815, 0x1 }, - { 0x3818, 0xc1 }, - { 0x501f, 0x0 }, - { 0x5002, 0xe0 }, - { 0x4300, 0x32 }, /* UYVY */ - { 0x3002, 0x1c }, - { 0x4800, 0x14 }, - { 0x4801, 0xf }, - { 0x3007, 0x3b }, - { 0x300e, 0x4 }, - { 0x4803, 0x50 }, - { 0x3815, 0x1 }, - { 0x4713, 0x2 }, - { 0x4842, 0x1 }, - { 0x300f, 0xe }, - { 0x3003, 0x3 }, - { 0x3003, 0x1 }, - { 0xffff, 0xff }, -}; - -struct ov5642_datafmt { - u32 code; - enum v4l2_colorspace colorspace; -}; - -struct ov5642 { - struct v4l2_subdev subdev; - const struct ov5642_datafmt *fmt; - struct v4l2_rect crop_rect; - struct v4l2_clk *clk; - - /* blanking information */ - int total_width; - int total_height; -}; - -static const struct ov5642_datafmt ov5642_colour_fmts[] = { - {MEDIA_BUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_JPEG}, -}; - -static struct ov5642 *to_ov5642(const struct i2c_client *client) -{ - return container_of(i2c_get_clientdata(client), struct ov5642, subdev); -} - -/* Find a data format by a pixel code in an array */ -static const struct ov5642_datafmt - *ov5642_find_datafmt(u32 code) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(ov5642_colour_fmts); i++) - if (ov5642_colour_fmts[i].code == code) - return ov5642_colour_fmts + i; - - return NULL; -} - -static int reg_read(struct i2c_client *client, u16 reg, u8 *val) -{ - int ret; - /* We have 16-bit i2c addresses - care for endianness */ - unsigned char data[2] = { reg >> 8, reg & 0xff }; - - ret = i2c_master_send(client, data, 2); - if (ret < 2) { - dev_err(&client->dev, "%s: i2c read error, reg: %x\n", - __func__, reg); - return ret < 0 ? ret : -EIO; - } - - ret = i2c_master_recv(client, val, 1); - if (ret < 1) { - dev_err(&client->dev, "%s: i2c read error, reg: %x\n", - __func__, reg); - return ret < 0 ? ret : -EIO; - } - return 0; -} - -static int reg_write(struct i2c_client *client, u16 reg, u8 val) -{ - int ret; - unsigned char data[3] = { reg >> 8, reg & 0xff, val }; - - ret = i2c_master_send(client, data, 3); - if (ret < 3) { - dev_err(&client->dev, "%s: i2c write error, reg: %x\n", - __func__, reg); - return ret < 0 ? ret : -EIO; - } - - return 0; -} - -/* - * convenience function to write 16 bit register values that are split up - * into two consecutive high and low parts - */ -static int reg_write16(struct i2c_client *client, u16 reg, u16 val16) -{ - int ret; - - ret = reg_write(client, reg, val16 >> 8); - if (ret) - return ret; - return reg_write(client, reg + 1, val16 & 0x00ff); -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int ov5642_get_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - u8 val; - - if (reg->reg & ~0xffff) - return -EINVAL; - - reg->size = 1; - - ret = reg_read(client, reg->reg, &val); - if (!ret) - reg->val = (__u64)val; - - return ret; -} - -static int ov5642_set_register(struct v4l2_subdev *sd, - const struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg & ~0xffff || reg->val & ~0xff) - return -EINVAL; - - return reg_write(client, reg->reg, reg->val); -} -#endif - -static int ov5642_write_array(struct i2c_client *client, - struct regval_list *vals) -{ - while (vals->reg_num != 0xffff || vals->value != 0xff) { - int ret = reg_write(client, vals->reg_num, vals->value); - if (ret < 0) - return ret; - vals++; - } - dev_dbg(&client->dev, "Register list loaded\n"); - return 0; -} - -static int ov5642_set_resolution(struct v4l2_subdev *sd) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov5642 *priv = to_ov5642(client); - int width = priv->crop_rect.width; - int height = priv->crop_rect.height; - int total_width = priv->total_width; - int total_height = priv->total_height; - int start_x = (OV5642_SENSOR_SIZE_X - width) / 2; - int start_y = (OV5642_SENSOR_SIZE_Y - height) / 2; - int ret; - - /* - * This should set the starting point for cropping. - * Doesn't work so far. - */ - ret = reg_write16(client, REG_WINDOW_START_X_HIGH, start_x); - if (!ret) - ret = reg_write16(client, REG_WINDOW_START_Y_HIGH, start_y); - if (!ret) { - priv->crop_rect.left = start_x; - priv->crop_rect.top = start_y; - } - - if (!ret) - ret = reg_write16(client, REG_WINDOW_WIDTH_HIGH, width); - if (!ret) - ret = reg_write16(client, REG_WINDOW_HEIGHT_HIGH, height); - if (ret) - return ret; - priv->crop_rect.width = width; - priv->crop_rect.height = height; - - /* Set the output window size. Only 1:1 scale is supported so far. */ - ret = reg_write16(client, REG_OUT_WIDTH_HIGH, width); - if (!ret) - ret = reg_write16(client, REG_OUT_HEIGHT_HIGH, height); - - /* Total width = output size + blanking */ - if (!ret) - ret = reg_write16(client, REG_OUT_TOTAL_WIDTH_HIGH, total_width); - if (!ret) - ret = reg_write16(client, REG_OUT_TOTAL_HEIGHT_HIGH, total_height); - - /* Sets the window for AWB calculations */ - if (!ret) - ret = reg_write16(client, REG_AVG_WINDOW_END_X_HIGH, width); - if (!ret) - ret = reg_write16(client, REG_AVG_WINDOW_END_Y_HIGH, height); - - return ret; -} - -static int ov5642_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov5642 *priv = to_ov5642(client); - const struct ov5642_datafmt *fmt = ov5642_find_datafmt(mf->code); - - if (format->pad) - return -EINVAL; - - mf->width = priv->crop_rect.width; - mf->height = priv->crop_rect.height; - - if (!fmt) { - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - mf->code = ov5642_colour_fmts[0].code; - mf->colorspace = ov5642_colour_fmts[0].colorspace; - } - - mf->field = V4L2_FIELD_NONE; - - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - priv->fmt = fmt; - else - cfg->try_fmt = *mf; - return 0; -} - -static int ov5642_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov5642 *priv = to_ov5642(client); - - const struct ov5642_datafmt *fmt = priv->fmt; - - if (format->pad) - return -EINVAL; - - mf->code = fmt->code; - mf->colorspace = fmt->colorspace; - mf->width = priv->crop_rect.width; - mf->height = priv->crop_rect.height; - mf->field = V4L2_FIELD_NONE; - - return 0; -} - -static int ov5642_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->pad || code->index >= ARRAY_SIZE(ov5642_colour_fmts)) - return -EINVAL; - - code->code = ov5642_colour_fmts[code->index].code; - return 0; -} - -static int ov5642_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov5642 *priv = to_ov5642(client); - struct v4l2_rect rect = sel->r; - int ret; - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || - sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - v4l_bound_align_image(&rect.width, 48, OV5642_MAX_WIDTH, 1, - &rect.height, 32, OV5642_MAX_HEIGHT, 1, 0); - - priv->crop_rect.width = rect.width; - priv->crop_rect.height = rect.height; - priv->total_width = rect.width + BLANKING_EXTRA_WIDTH; - priv->total_height = max_t(int, rect.height + - BLANKING_EXTRA_HEIGHT, - BLANKING_MIN_HEIGHT); - priv->crop_rect.width = rect.width; - priv->crop_rect.height = rect.height; - - ret = ov5642_write_array(client, ov5642_default_regs_init); - if (!ret) - ret = ov5642_set_resolution(sd); - if (!ret) - ret = ov5642_write_array(client, ov5642_default_regs_finalise); - - return ret; -} - -static int ov5642_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov5642 *priv = to_ov5642(client); - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.left = 0; - sel->r.top = 0; - sel->r.width = OV5642_MAX_WIDTH; - sel->r.height = OV5642_MAX_HEIGHT; - return 0; - case V4L2_SEL_TGT_CROP: - sel->r = priv->crop_rect; - return 0; - default: - return -EINVAL; - } -} - -static int ov5642_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - cfg->type = V4L2_MBUS_CSI2_DPHY; - cfg->flags = V4L2_MBUS_CSI2_2_LANE | V4L2_MBUS_CSI2_CHANNEL_0 | - V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; - - return 0; -} - -static int ov5642_s_power(struct v4l2_subdev *sd, int on) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct ov5642 *priv = to_ov5642(client); - int ret; - - if (!on) - return soc_camera_power_off(&client->dev, ssdd, priv->clk); - - ret = soc_camera_power_on(&client->dev, ssdd, priv->clk); - if (ret < 0) - return ret; - - ret = ov5642_write_array(client, ov5642_default_regs_init); - if (!ret) - ret = ov5642_set_resolution(sd); - if (!ret) - ret = ov5642_write_array(client, ov5642_default_regs_finalise); - - return ret; -} - -static const struct v4l2_subdev_video_ops ov5642_subdev_video_ops = { - .g_mbus_config = ov5642_g_mbus_config, -}; - -static const struct v4l2_subdev_pad_ops ov5642_subdev_pad_ops = { - .enum_mbus_code = ov5642_enum_mbus_code, - .get_selection = ov5642_get_selection, - .set_selection = ov5642_set_selection, - .get_fmt = ov5642_get_fmt, - .set_fmt = ov5642_set_fmt, -}; - -static const struct v4l2_subdev_core_ops ov5642_subdev_core_ops = { - .s_power = ov5642_s_power, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = ov5642_get_register, - .s_register = ov5642_set_register, -#endif -}; - -static const struct v4l2_subdev_ops ov5642_subdev_ops = { - .core = &ov5642_subdev_core_ops, - .video = &ov5642_subdev_video_ops, - .pad = &ov5642_subdev_pad_ops, -}; - -static int ov5642_video_probe(struct i2c_client *client) -{ - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - int ret; - u8 id_high, id_low; - u16 id; - - ret = ov5642_s_power(subdev, 1); - if (ret < 0) - return ret; - - /* Read sensor Model ID */ - ret = reg_read(client, REG_CHIP_ID_HIGH, &id_high); - if (ret < 0) - goto done; - - id = id_high << 8; - - ret = reg_read(client, REG_CHIP_ID_LOW, &id_low); - if (ret < 0) - goto done; - - id |= id_low; - - dev_info(&client->dev, "Chip ID 0x%04x detected\n", id); - - if (id != 0x5642) { - ret = -ENODEV; - goto done; - } - - ret = 0; - -done: - ov5642_s_power(subdev, 0); - return ret; -} - -static int ov5642_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct ov5642 *priv; - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - int ret; - - if (!ssdd) { - dev_err(&client->dev, "OV5642: missing platform data!\n"); - return -EINVAL; - } - - priv = devm_kzalloc(&client->dev, sizeof(struct ov5642), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - v4l2_i2c_subdev_init(&priv->subdev, client, &ov5642_subdev_ops); - - priv->fmt = &ov5642_colour_fmts[0]; - - priv->crop_rect.width = OV5642_DEFAULT_WIDTH; - priv->crop_rect.height = OV5642_DEFAULT_HEIGHT; - priv->crop_rect.left = (OV5642_MAX_WIDTH - OV5642_DEFAULT_WIDTH) / 2; - priv->crop_rect.top = (OV5642_MAX_HEIGHT - OV5642_DEFAULT_HEIGHT) / 2; - priv->total_width = OV5642_DEFAULT_WIDTH + BLANKING_EXTRA_WIDTH; - priv->total_height = BLANKING_MIN_HEIGHT; - - priv->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(priv->clk)) - return PTR_ERR(priv->clk); - - ret = ov5642_video_probe(client); - if (ret < 0) - v4l2_clk_put(priv->clk); - - return ret; -} - -static int ov5642_remove(struct i2c_client *client) -{ - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct ov5642 *priv = to_ov5642(client); - - v4l2_clk_put(priv->clk); - if (ssdd->free_bus) - ssdd->free_bus(ssdd); - - return 0; -} - -static const struct i2c_device_id ov5642_id[] = { - { "ov5642", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ov5642_id); - -#if IS_ENABLED(CONFIG_OF) -static const struct of_device_id ov5642_of_match[] = { - { .compatible = "ovti,ov5642" }, - { }, -}; -MODULE_DEVICE_TABLE(of, ov5642_of_match); -#endif - -static struct i2c_driver ov5642_i2c_driver = { - .driver = { - .name = "ov5642", - .of_match_table = of_match_ptr(ov5642_of_match), - }, - .probe = ov5642_probe, - .remove = ov5642_remove, - .id_table = ov5642_id, -}; - -module_i2c_driver(ov5642_i2c_driver); - -MODULE_DESCRIPTION("Omnivision OV5642 Camera driver"); -MODULE_AUTHOR("Bastian Hecht <hechtb@gmail.com>"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/soc_camera/soc_ov9740.c b/drivers/staging/media/soc_camera/soc_ov9740.c deleted file mode 100644 index 7c765595d85f..000000000000 --- a/drivers/staging/media/soc_camera/soc_ov9740.c +++ /dev/null @@ -1,992 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * OmniVision OV9740 Camera Driver - * - * Copyright (C) 2011 NVIDIA Corporation - * - * Based on ov9640 camera driver. - */ -#include <linux/init.h> -#include <linux/module.h> -#include <linux/i2c.h> -#include <linux/slab.h> -#include <linux/v4l2-mediabus.h> - -#include <media/soc_camera.h> -#include <media/v4l2-clk.h> -#include <media/v4l2-ctrls.h> - -#define to_ov9740(sd) container_of(sd, struct ov9740_priv, subdev) - -/* General Status Registers */ -#define OV9740_MODEL_ID_HI 0x0000 -#define OV9740_MODEL_ID_LO 0x0001 -#define OV9740_REVISION_NUMBER 0x0002 -#define OV9740_MANUFACTURER_ID 0x0003 -#define OV9740_SMIA_VERSION 0x0004 - -/* General Setup Registers */ -#define OV9740_MODE_SELECT 0x0100 -#define OV9740_IMAGE_ORT 0x0101 -#define OV9740_SOFTWARE_RESET 0x0103 -#define OV9740_GRP_PARAM_HOLD 0x0104 -#define OV9740_MSK_CORRUP_FM 0x0105 - -/* Timing Setting */ -#define OV9740_FRM_LENGTH_LN_HI 0x0340 /* VTS */ -#define OV9740_FRM_LENGTH_LN_LO 0x0341 /* VTS */ -#define OV9740_LN_LENGTH_PCK_HI 0x0342 /* HTS */ -#define OV9740_LN_LENGTH_PCK_LO 0x0343 /* HTS */ -#define OV9740_X_ADDR_START_HI 0x0344 -#define OV9740_X_ADDR_START_LO 0x0345 -#define OV9740_Y_ADDR_START_HI 0x0346 -#define OV9740_Y_ADDR_START_LO 0x0347 -#define OV9740_X_ADDR_END_HI 0x0348 -#define OV9740_X_ADDR_END_LO 0x0349 -#define OV9740_Y_ADDR_END_HI 0x034a -#define OV9740_Y_ADDR_END_LO 0x034b -#define OV9740_X_OUTPUT_SIZE_HI 0x034c -#define OV9740_X_OUTPUT_SIZE_LO 0x034d -#define OV9740_Y_OUTPUT_SIZE_HI 0x034e -#define OV9740_Y_OUTPUT_SIZE_LO 0x034f - -/* IO Control Registers */ -#define OV9740_IO_CREL00 0x3002 -#define OV9740_IO_CREL01 0x3004 -#define OV9740_IO_CREL02 0x3005 -#define OV9740_IO_OUTPUT_SEL01 0x3026 -#define OV9740_IO_OUTPUT_SEL02 0x3027 - -/* AWB Registers */ -#define OV9740_AWB_MANUAL_CTRL 0x3406 - -/* Analog Control Registers */ -#define OV9740_ANALOG_CTRL01 0x3601 -#define OV9740_ANALOG_CTRL02 0x3602 -#define OV9740_ANALOG_CTRL03 0x3603 -#define OV9740_ANALOG_CTRL04 0x3604 -#define OV9740_ANALOG_CTRL10 0x3610 -#define OV9740_ANALOG_CTRL12 0x3612 -#define OV9740_ANALOG_CTRL15 0x3615 -#define OV9740_ANALOG_CTRL20 0x3620 -#define OV9740_ANALOG_CTRL21 0x3621 -#define OV9740_ANALOG_CTRL22 0x3622 -#define OV9740_ANALOG_CTRL30 0x3630 -#define OV9740_ANALOG_CTRL31 0x3631 -#define OV9740_ANALOG_CTRL32 0x3632 -#define OV9740_ANALOG_CTRL33 0x3633 - -/* Sensor Control */ -#define OV9740_SENSOR_CTRL03 0x3703 -#define OV9740_SENSOR_CTRL04 0x3704 -#define OV9740_SENSOR_CTRL05 0x3705 -#define OV9740_SENSOR_CTRL07 0x3707 - -/* Timing Control */ -#define OV9740_TIMING_CTRL17 0x3817 -#define OV9740_TIMING_CTRL19 0x3819 -#define OV9740_TIMING_CTRL33 0x3833 -#define OV9740_TIMING_CTRL35 0x3835 - -/* Banding Filter */ -#define OV9740_AEC_MAXEXPO_60_H 0x3a02 -#define OV9740_AEC_MAXEXPO_60_L 0x3a03 -#define OV9740_AEC_B50_STEP_HI 0x3a08 -#define OV9740_AEC_B50_STEP_LO 0x3a09 -#define OV9740_AEC_B60_STEP_HI 0x3a0a -#define OV9740_AEC_B60_STEP_LO 0x3a0b -#define OV9740_AEC_CTRL0D 0x3a0d -#define OV9740_AEC_CTRL0E 0x3a0e -#define OV9740_AEC_MAXEXPO_50_H 0x3a14 -#define OV9740_AEC_MAXEXPO_50_L 0x3a15 - -/* AEC/AGC Control */ -#define OV9740_AEC_ENABLE 0x3503 -#define OV9740_GAIN_CEILING_01 0x3a18 -#define OV9740_GAIN_CEILING_02 0x3a19 -#define OV9740_AEC_HI_THRESHOLD 0x3a11 -#define OV9740_AEC_3A1A 0x3a1a -#define OV9740_AEC_CTRL1B_WPT2 0x3a1b -#define OV9740_AEC_CTRL0F_WPT 0x3a0f -#define OV9740_AEC_CTRL10_BPT 0x3a10 -#define OV9740_AEC_CTRL1E_BPT2 0x3a1e -#define OV9740_AEC_LO_THRESHOLD 0x3a1f - -/* BLC Control */ -#define OV9740_BLC_AUTO_ENABLE 0x4002 -#define OV9740_BLC_MODE 0x4005 - -/* VFIFO */ -#define OV9740_VFIFO_READ_START_HI 0x4608 -#define OV9740_VFIFO_READ_START_LO 0x4609 - -/* DVP Control */ -#define OV9740_DVP_VSYNC_CTRL02 0x4702 -#define OV9740_DVP_VSYNC_MODE 0x4704 -#define OV9740_DVP_VSYNC_CTRL06 0x4706 - -/* PLL Setting */ -#define OV9740_PLL_MODE_CTRL01 0x3104 -#define OV9740_PRE_PLL_CLK_DIV 0x0305 -#define OV9740_PLL_MULTIPLIER 0x0307 -#define OV9740_VT_SYS_CLK_DIV 0x0303 -#define OV9740_VT_PIX_CLK_DIV 0x0301 -#define OV9740_PLL_CTRL3010 0x3010 -#define OV9740_VFIFO_CTRL00 0x460e - -/* ISP Control */ -#define OV9740_ISP_CTRL00 0x5000 -#define OV9740_ISP_CTRL01 0x5001 -#define OV9740_ISP_CTRL03 0x5003 -#define OV9740_ISP_CTRL05 0x5005 -#define OV9740_ISP_CTRL12 0x5012 -#define OV9740_ISP_CTRL19 0x5019 -#define OV9740_ISP_CTRL1A 0x501a -#define OV9740_ISP_CTRL1E 0x501e -#define OV9740_ISP_CTRL1F 0x501f -#define OV9740_ISP_CTRL20 0x5020 -#define OV9740_ISP_CTRL21 0x5021 - -/* AWB */ -#define OV9740_AWB_CTRL00 0x5180 -#define OV9740_AWB_CTRL01 0x5181 -#define OV9740_AWB_CTRL02 0x5182 -#define OV9740_AWB_CTRL03 0x5183 -#define OV9740_AWB_ADV_CTRL01 0x5184 -#define OV9740_AWB_ADV_CTRL02 0x5185 -#define OV9740_AWB_ADV_CTRL03 0x5186 -#define OV9740_AWB_ADV_CTRL04 0x5187 -#define OV9740_AWB_ADV_CTRL05 0x5188 -#define OV9740_AWB_ADV_CTRL06 0x5189 -#define OV9740_AWB_ADV_CTRL07 0x518a -#define OV9740_AWB_ADV_CTRL08 0x518b -#define OV9740_AWB_ADV_CTRL09 0x518c -#define OV9740_AWB_ADV_CTRL10 0x518d -#define OV9740_AWB_ADV_CTRL11 0x518e -#define OV9740_AWB_CTRL0F 0x518f -#define OV9740_AWB_CTRL10 0x5190 -#define OV9740_AWB_CTRL11 0x5191 -#define OV9740_AWB_CTRL12 0x5192 -#define OV9740_AWB_CTRL13 0x5193 -#define OV9740_AWB_CTRL14 0x5194 - -/* MIPI Control */ -#define OV9740_MIPI_CTRL00 0x4800 -#define OV9740_MIPI_3837 0x3837 -#define OV9740_MIPI_CTRL01 0x4801 -#define OV9740_MIPI_CTRL03 0x4803 -#define OV9740_MIPI_CTRL05 0x4805 -#define OV9740_VFIFO_RD_CTRL 0x4601 -#define OV9740_MIPI_CTRL_3012 0x3012 -#define OV9740_SC_CMMM_MIPI_CTR 0x3014 - -#define OV9740_MAX_WIDTH 1280 -#define OV9740_MAX_HEIGHT 720 - -/* Misc. structures */ -struct ov9740_reg { - u16 reg; - u8 val; -}; - -struct ov9740_priv { - struct v4l2_subdev subdev; - struct v4l2_ctrl_handler hdl; - struct v4l2_clk *clk; - - u16 model; - u8 revision; - u8 manid; - u8 smiaver; - - bool flag_vflip; - bool flag_hflip; - - /* For suspend/resume. */ - struct v4l2_mbus_framefmt current_mf; - bool current_enable; -}; - -static const struct ov9740_reg ov9740_defaults[] = { - /* Software Reset */ - { OV9740_SOFTWARE_RESET, 0x01 }, - - /* Banding Filter */ - { OV9740_AEC_B50_STEP_HI, 0x00 }, - { OV9740_AEC_B50_STEP_LO, 0xe8 }, - { OV9740_AEC_CTRL0E, 0x03 }, - { OV9740_AEC_MAXEXPO_50_H, 0x15 }, - { OV9740_AEC_MAXEXPO_50_L, 0xc6 }, - { OV9740_AEC_B60_STEP_HI, 0x00 }, - { OV9740_AEC_B60_STEP_LO, 0xc0 }, - { OV9740_AEC_CTRL0D, 0x04 }, - { OV9740_AEC_MAXEXPO_60_H, 0x18 }, - { OV9740_AEC_MAXEXPO_60_L, 0x20 }, - - /* LC */ - { 0x5842, 0x02 }, { 0x5843, 0x5e }, { 0x5844, 0x04 }, { 0x5845, 0x32 }, - { 0x5846, 0x03 }, { 0x5847, 0x29 }, { 0x5848, 0x02 }, { 0x5849, 0xcc }, - - /* Un-documented OV9740 registers */ - { 0x5800, 0x29 }, { 0x5801, 0x25 }, { 0x5802, 0x20 }, { 0x5803, 0x21 }, - { 0x5804, 0x26 }, { 0x5805, 0x2e }, { 0x5806, 0x11 }, { 0x5807, 0x0c }, - { 0x5808, 0x09 }, { 0x5809, 0x0a }, { 0x580a, 0x0e }, { 0x580b, 0x16 }, - { 0x580c, 0x06 }, { 0x580d, 0x02 }, { 0x580e, 0x00 }, { 0x580f, 0x00 }, - { 0x5810, 0x04 }, { 0x5811, 0x0a }, { 0x5812, 0x05 }, { 0x5813, 0x02 }, - { 0x5814, 0x00 }, { 0x5815, 0x00 }, { 0x5816, 0x03 }, { 0x5817, 0x09 }, - { 0x5818, 0x0f }, { 0x5819, 0x0a }, { 0x581a, 0x07 }, { 0x581b, 0x08 }, - { 0x581c, 0x0b }, { 0x581d, 0x14 }, { 0x581e, 0x28 }, { 0x581f, 0x23 }, - { 0x5820, 0x1d }, { 0x5821, 0x1e }, { 0x5822, 0x24 }, { 0x5823, 0x2a }, - { 0x5824, 0x4f }, { 0x5825, 0x6f }, { 0x5826, 0x5f }, { 0x5827, 0x7f }, - { 0x5828, 0x9f }, { 0x5829, 0x5f }, { 0x582a, 0x8f }, { 0x582b, 0x9e }, - { 0x582c, 0x8f }, { 0x582d, 0x9f }, { 0x582e, 0x4f }, { 0x582f, 0x87 }, - { 0x5830, 0x86 }, { 0x5831, 0x97 }, { 0x5832, 0xae }, { 0x5833, 0x3f }, - { 0x5834, 0x8e }, { 0x5835, 0x7c }, { 0x5836, 0x7e }, { 0x5837, 0xaf }, - { 0x5838, 0x8f }, { 0x5839, 0x8f }, { 0x583a, 0x9f }, { 0x583b, 0x7f }, - { 0x583c, 0x5f }, - - /* Y Gamma */ - { 0x5480, 0x07 }, { 0x5481, 0x18 }, { 0x5482, 0x2c }, { 0x5483, 0x4e }, - { 0x5484, 0x5e }, { 0x5485, 0x6b }, { 0x5486, 0x77 }, { 0x5487, 0x82 }, - { 0x5488, 0x8c }, { 0x5489, 0x95 }, { 0x548a, 0xa4 }, { 0x548b, 0xb1 }, - { 0x548c, 0xc6 }, { 0x548d, 0xd8 }, { 0x548e, 0xe9 }, - - /* UV Gamma */ - { 0x5490, 0x0f }, { 0x5491, 0xff }, { 0x5492, 0x0d }, { 0x5493, 0x05 }, - { 0x5494, 0x07 }, { 0x5495, 0x1a }, { 0x5496, 0x04 }, { 0x5497, 0x01 }, - { 0x5498, 0x03 }, { 0x5499, 0x53 }, { 0x549a, 0x02 }, { 0x549b, 0xeb }, - { 0x549c, 0x02 }, { 0x549d, 0xa0 }, { 0x549e, 0x02 }, { 0x549f, 0x67 }, - { 0x54a0, 0x02 }, { 0x54a1, 0x3b }, { 0x54a2, 0x02 }, { 0x54a3, 0x18 }, - { 0x54a4, 0x01 }, { 0x54a5, 0xe7 }, { 0x54a6, 0x01 }, { 0x54a7, 0xc3 }, - { 0x54a8, 0x01 }, { 0x54a9, 0x94 }, { 0x54aa, 0x01 }, { 0x54ab, 0x72 }, - { 0x54ac, 0x01 }, { 0x54ad, 0x57 }, - - /* AWB */ - { OV9740_AWB_CTRL00, 0xf0 }, - { OV9740_AWB_CTRL01, 0x00 }, - { OV9740_AWB_CTRL02, 0x41 }, - { OV9740_AWB_CTRL03, 0x42 }, - { OV9740_AWB_ADV_CTRL01, 0x8a }, - { OV9740_AWB_ADV_CTRL02, 0x61 }, - { OV9740_AWB_ADV_CTRL03, 0xce }, - { OV9740_AWB_ADV_CTRL04, 0xa8 }, - { OV9740_AWB_ADV_CTRL05, 0x17 }, - { OV9740_AWB_ADV_CTRL06, 0x1f }, - { OV9740_AWB_ADV_CTRL07, 0x27 }, - { OV9740_AWB_ADV_CTRL08, 0x41 }, - { OV9740_AWB_ADV_CTRL09, 0x34 }, - { OV9740_AWB_ADV_CTRL10, 0xf0 }, - { OV9740_AWB_ADV_CTRL11, 0x10 }, - { OV9740_AWB_CTRL0F, 0xff }, - { OV9740_AWB_CTRL10, 0x00 }, - { OV9740_AWB_CTRL11, 0xff }, - { OV9740_AWB_CTRL12, 0x00 }, - { OV9740_AWB_CTRL13, 0xff }, - { OV9740_AWB_CTRL14, 0x00 }, - - /* CIP */ - { 0x530d, 0x12 }, - - /* CMX */ - { 0x5380, 0x01 }, { 0x5381, 0x00 }, { 0x5382, 0x00 }, { 0x5383, 0x17 }, - { 0x5384, 0x00 }, { 0x5385, 0x01 }, { 0x5386, 0x00 }, { 0x5387, 0x00 }, - { 0x5388, 0x00 }, { 0x5389, 0xe0 }, { 0x538a, 0x00 }, { 0x538b, 0x20 }, - { 0x538c, 0x00 }, { 0x538d, 0x00 }, { 0x538e, 0x00 }, { 0x538f, 0x16 }, - { 0x5390, 0x00 }, { 0x5391, 0x9c }, { 0x5392, 0x00 }, { 0x5393, 0xa0 }, - { 0x5394, 0x18 }, - - /* 50/60 Detection */ - { 0x3c0a, 0x9c }, { 0x3c0b, 0x3f }, - - /* Output Select */ - { OV9740_IO_OUTPUT_SEL01, 0x00 }, - { OV9740_IO_OUTPUT_SEL02, 0x00 }, - { OV9740_IO_CREL00, 0x00 }, - { OV9740_IO_CREL01, 0x00 }, - { OV9740_IO_CREL02, 0x00 }, - - /* AWB Control */ - { OV9740_AWB_MANUAL_CTRL, 0x00 }, - - /* Analog Control */ - { OV9740_ANALOG_CTRL03, 0xaa }, - { OV9740_ANALOG_CTRL32, 0x2f }, - { OV9740_ANALOG_CTRL20, 0x66 }, - { OV9740_ANALOG_CTRL21, 0xc0 }, - { OV9740_ANALOG_CTRL31, 0x52 }, - { OV9740_ANALOG_CTRL33, 0x50 }, - { OV9740_ANALOG_CTRL30, 0xca }, - { OV9740_ANALOG_CTRL04, 0x0c }, - { OV9740_ANALOG_CTRL01, 0x40 }, - { OV9740_ANALOG_CTRL02, 0x16 }, - { OV9740_ANALOG_CTRL10, 0xa1 }, - { OV9740_ANALOG_CTRL12, 0x24 }, - { OV9740_ANALOG_CTRL22, 0x9f }, - { OV9740_ANALOG_CTRL15, 0xf0 }, - - /* Sensor Control */ - { OV9740_SENSOR_CTRL03, 0x42 }, - { OV9740_SENSOR_CTRL04, 0x10 }, - { OV9740_SENSOR_CTRL05, 0x45 }, - { OV9740_SENSOR_CTRL07, 0x14 }, - - /* Timing Control */ - { OV9740_TIMING_CTRL33, 0x04 }, - { OV9740_TIMING_CTRL35, 0x02 }, - { OV9740_TIMING_CTRL19, 0x6e }, - { OV9740_TIMING_CTRL17, 0x94 }, - - /* AEC/AGC Control */ - { OV9740_AEC_ENABLE, 0x10 }, - { OV9740_GAIN_CEILING_01, 0x00 }, - { OV9740_GAIN_CEILING_02, 0x7f }, - { OV9740_AEC_HI_THRESHOLD, 0xa0 }, - { OV9740_AEC_3A1A, 0x05 }, - { OV9740_AEC_CTRL1B_WPT2, 0x50 }, - { OV9740_AEC_CTRL0F_WPT, 0x50 }, - { OV9740_AEC_CTRL10_BPT, 0x4c }, - { OV9740_AEC_CTRL1E_BPT2, 0x4c }, - { OV9740_AEC_LO_THRESHOLD, 0x26 }, - - /* BLC Control */ - { OV9740_BLC_AUTO_ENABLE, 0x45 }, - { OV9740_BLC_MODE, 0x18 }, - - /* DVP Control */ - { OV9740_DVP_VSYNC_CTRL02, 0x04 }, - { OV9740_DVP_VSYNC_MODE, 0x00 }, - { OV9740_DVP_VSYNC_CTRL06, 0x08 }, - - /* PLL Setting */ - { OV9740_PLL_MODE_CTRL01, 0x20 }, - { OV9740_PRE_PLL_CLK_DIV, 0x03 }, - { OV9740_PLL_MULTIPLIER, 0x4c }, - { OV9740_VT_SYS_CLK_DIV, 0x01 }, - { OV9740_VT_PIX_CLK_DIV, 0x08 }, - { OV9740_PLL_CTRL3010, 0x01 }, - { OV9740_VFIFO_CTRL00, 0x82 }, - - /* Timing Setting */ - /* VTS */ - { OV9740_FRM_LENGTH_LN_HI, 0x03 }, - { OV9740_FRM_LENGTH_LN_LO, 0x07 }, - /* HTS */ - { OV9740_LN_LENGTH_PCK_HI, 0x06 }, - { OV9740_LN_LENGTH_PCK_LO, 0x62 }, - - /* MIPI Control */ - { OV9740_MIPI_CTRL00, 0x44 }, /* 0x64 for discontinuous clk */ - { OV9740_MIPI_3837, 0x01 }, - { OV9740_MIPI_CTRL01, 0x0f }, - { OV9740_MIPI_CTRL03, 0x05 }, - { OV9740_MIPI_CTRL05, 0x10 }, - { OV9740_VFIFO_RD_CTRL, 0x16 }, - { OV9740_MIPI_CTRL_3012, 0x70 }, - { OV9740_SC_CMMM_MIPI_CTR, 0x01 }, - - /* YUYV order */ - { OV9740_ISP_CTRL19, 0x02 }, -}; - -static u32 ov9740_codes[] = { - MEDIA_BUS_FMT_YUYV8_2X8, -}; - -/* read a register */ -static int ov9740_reg_read(struct i2c_client *client, u16 reg, u8 *val) -{ - int ret; - struct i2c_msg msg[] = { - { - .addr = client->addr, - .flags = 0, - .len = 2, - .buf = (u8 *)®, - }, - { - .addr = client->addr, - .flags = I2C_M_RD, - .len = 1, - .buf = val, - }, - }; - - reg = swab16(reg); - - ret = i2c_transfer(client->adapter, msg, 2); - if (ret < 0) { - dev_err(&client->dev, "Failed reading register 0x%04x!\n", reg); - return ret; - } - - return 0; -} - -/* write a register */ -static int ov9740_reg_write(struct i2c_client *client, u16 reg, u8 val) -{ - struct i2c_msg msg; - struct { - u16 reg; - u8 val; - } __packed buf; - int ret; - - reg = swab16(reg); - - buf.reg = reg; - buf.val = val; - - msg.addr = client->addr; - msg.flags = 0; - msg.len = 3; - msg.buf = (u8 *)&buf; - - ret = i2c_transfer(client->adapter, &msg, 1); - if (ret < 0) { - dev_err(&client->dev, "Failed writing register 0x%04x!\n", reg); - return ret; - } - - return 0; -} - - -/* Read a register, alter its bits, write it back */ -static int ov9740_reg_rmw(struct i2c_client *client, u16 reg, u8 set, u8 unset) -{ - u8 val; - int ret; - - ret = ov9740_reg_read(client, reg, &val); - if (ret < 0) { - dev_err(&client->dev, - "[Read]-Modify-Write of register 0x%04x failed!\n", - reg); - return ret; - } - - val |= set; - val &= ~unset; - - ret = ov9740_reg_write(client, reg, val); - if (ret < 0) { - dev_err(&client->dev, - "Read-Modify-[Write] of register 0x%04x failed!\n", - reg); - return ret; - } - - return 0; -} - -static int ov9740_reg_write_array(struct i2c_client *client, - const struct ov9740_reg *regarray, - int regarraylen) -{ - int i; - int ret; - - for (i = 0; i < regarraylen; i++) { - ret = ov9740_reg_write(client, - regarray[i].reg, regarray[i].val); - if (ret < 0) - return ret; - } - - return 0; -} - -/* Start/Stop streaming from the device */ -static int ov9740_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov9740_priv *priv = to_ov9740(sd); - int ret; - - /* Program orientation register. */ - if (priv->flag_vflip) - ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0x2, 0); - else - ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0, 0x2); - if (ret < 0) - return ret; - - if (priv->flag_hflip) - ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0x1, 0); - else - ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0, 0x1); - if (ret < 0) - return ret; - - if (enable) { - dev_dbg(&client->dev, "Enabling Streaming\n"); - /* Start Streaming */ - ret = ov9740_reg_write(client, OV9740_MODE_SELECT, 0x01); - - } else { - dev_dbg(&client->dev, "Disabling Streaming\n"); - /* Software Reset */ - ret = ov9740_reg_write(client, OV9740_SOFTWARE_RESET, 0x01); - if (!ret) - /* Setting Streaming to Standby */ - ret = ov9740_reg_write(client, OV9740_MODE_SELECT, - 0x00); - } - - priv->current_enable = enable; - - return ret; -} - -/* select nearest higher resolution for capture */ -static void ov9740_res_roundup(u32 *width, u32 *height) -{ - /* Width must be a multiple of 4 pixels. */ - *width = ALIGN(*width, 4); - - /* Max resolution is 1280x720 (720p). */ - if (*width > OV9740_MAX_WIDTH) - *width = OV9740_MAX_WIDTH; - - if (*height > OV9740_MAX_HEIGHT) - *height = OV9740_MAX_HEIGHT; -} - -/* Setup registers according to resolution and color encoding */ -static int ov9740_set_res(struct i2c_client *client, u32 width, u32 height) -{ - u32 x_start; - u32 y_start; - u32 x_end; - u32 y_end; - bool scaling = false; - u32 scale_input_x; - u32 scale_input_y; - int ret; - - if ((width != OV9740_MAX_WIDTH) || (height != OV9740_MAX_HEIGHT)) - scaling = true; - - /* - * Try to use as much of the sensor area as possible when supporting - * smaller resolutions. Depending on the aspect ratio of the - * chosen resolution, we can either use the full width of the sensor, - * or the full height of the sensor (or both if the aspect ratio is - * the same as 1280x720. - */ - if ((OV9740_MAX_WIDTH * height) > (OV9740_MAX_HEIGHT * width)) { - scale_input_x = (OV9740_MAX_HEIGHT * width) / height; - scale_input_y = OV9740_MAX_HEIGHT; - } else { - scale_input_x = OV9740_MAX_WIDTH; - scale_input_y = (OV9740_MAX_WIDTH * height) / width; - } - - /* These describe the area of the sensor to use. */ - x_start = (OV9740_MAX_WIDTH - scale_input_x) / 2; - y_start = (OV9740_MAX_HEIGHT - scale_input_y) / 2; - x_end = x_start + scale_input_x - 1; - y_end = y_start + scale_input_y - 1; - - ret = ov9740_reg_write(client, OV9740_X_ADDR_START_HI, x_start >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_X_ADDR_START_LO, x_start & 0xff); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_Y_ADDR_START_HI, y_start >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_Y_ADDR_START_LO, y_start & 0xff); - if (ret) - goto done; - - ret = ov9740_reg_write(client, OV9740_X_ADDR_END_HI, x_end >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_X_ADDR_END_LO, x_end & 0xff); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_Y_ADDR_END_HI, y_end >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_Y_ADDR_END_LO, y_end & 0xff); - if (ret) - goto done; - - ret = ov9740_reg_write(client, OV9740_X_OUTPUT_SIZE_HI, width >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_X_OUTPUT_SIZE_LO, width & 0xff); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_Y_OUTPUT_SIZE_HI, height >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_Y_OUTPUT_SIZE_LO, height & 0xff); - if (ret) - goto done; - - ret = ov9740_reg_write(client, OV9740_ISP_CTRL1E, scale_input_x >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_ISP_CTRL1F, scale_input_x & 0xff); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_ISP_CTRL20, scale_input_y >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_ISP_CTRL21, scale_input_y & 0xff); - if (ret) - goto done; - - ret = ov9740_reg_write(client, OV9740_VFIFO_READ_START_HI, - (scale_input_x - width) >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_VFIFO_READ_START_LO, - (scale_input_x - width) & 0xff); - if (ret) - goto done; - - ret = ov9740_reg_write(client, OV9740_ISP_CTRL00, 0xff); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_ISP_CTRL01, 0xef | - (scaling << 4)); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_ISP_CTRL03, 0xff); - -done: - return ret; -} - -/* set the format we will capture in */ -static int ov9740_s_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov9740_priv *priv = to_ov9740(sd); - int ret; - - ret = ov9740_reg_write_array(client, ov9740_defaults, - ARRAY_SIZE(ov9740_defaults)); - if (ret < 0) - return ret; - - ret = ov9740_set_res(client, mf->width, mf->height); - if (ret < 0) - return ret; - - priv->current_mf = *mf; - return ret; -} - -static int ov9740_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - - if (format->pad) - return -EINVAL; - - ov9740_res_roundup(&mf->width, &mf->height); - - mf->field = V4L2_FIELD_NONE; - mf->code = MEDIA_BUS_FMT_YUYV8_2X8; - mf->colorspace = V4L2_COLORSPACE_SRGB; - - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return ov9740_s_fmt(sd, mf); - cfg->try_fmt = *mf; - return 0; -} - -static int ov9740_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->pad || code->index >= ARRAY_SIZE(ov9740_codes)) - return -EINVAL; - - code->code = ov9740_codes[code->index]; - - return 0; -} - -static int ov9740_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP: - sel->r.left = 0; - sel->r.top = 0; - sel->r.width = OV9740_MAX_WIDTH; - sel->r.height = OV9740_MAX_HEIGHT; - return 0; - default: - return -EINVAL; - } -} - -/* Set status of additional camera capabilities */ -static int ov9740_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct ov9740_priv *priv = - container_of(ctrl->handler, struct ov9740_priv, hdl); - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - priv->flag_vflip = ctrl->val; - break; - case V4L2_CID_HFLIP: - priv->flag_hflip = ctrl->val; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int ov9740_s_power(struct v4l2_subdev *sd, int on) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct ov9740_priv *priv = to_ov9740(sd); - int ret; - - if (on) { - ret = soc_camera_power_on(&client->dev, ssdd, priv->clk); - if (ret < 0) - return ret; - - if (priv->current_enable) { - ov9740_s_fmt(sd, &priv->current_mf); - ov9740_s_stream(sd, 1); - } - } else { - if (priv->current_enable) { - ov9740_s_stream(sd, 0); - priv->current_enable = true; - } - - soc_camera_power_off(&client->dev, ssdd, priv->clk); - } - - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int ov9740_get_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - u8 val; - - if (reg->reg & ~0xffff) - return -EINVAL; - - reg->size = 2; - - ret = ov9740_reg_read(client, reg->reg, &val); - if (ret) - return ret; - - reg->val = (__u64)val; - - return ret; -} - -static int ov9740_set_register(struct v4l2_subdev *sd, - const struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg & ~0xffff || reg->val & ~0xff) - return -EINVAL; - - return ov9740_reg_write(client, reg->reg, reg->val); -} -#endif - -static int ov9740_video_probe(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct ov9740_priv *priv = to_ov9740(sd); - u8 modelhi, modello; - int ret; - - ret = ov9740_s_power(&priv->subdev, 1); - if (ret < 0) - return ret; - - /* - * check and show product ID and manufacturer ID - */ - ret = ov9740_reg_read(client, OV9740_MODEL_ID_HI, &modelhi); - if (ret < 0) - goto done; - - ret = ov9740_reg_read(client, OV9740_MODEL_ID_LO, &modello); - if (ret < 0) - goto done; - - priv->model = (modelhi << 8) | modello; - - ret = ov9740_reg_read(client, OV9740_REVISION_NUMBER, &priv->revision); - if (ret < 0) - goto done; - - ret = ov9740_reg_read(client, OV9740_MANUFACTURER_ID, &priv->manid); - if (ret < 0) - goto done; - - ret = ov9740_reg_read(client, OV9740_SMIA_VERSION, &priv->smiaver); - if (ret < 0) - goto done; - - if (priv->model != 0x9740) { - ret = -ENODEV; - goto done; - } - - dev_info(&client->dev, "ov9740 Model ID 0x%04x, Revision 0x%02x, Manufacturer 0x%02x, SMIA Version 0x%02x\n", - priv->model, priv->revision, priv->manid, priv->smiaver); - - ret = v4l2_ctrl_handler_setup(&priv->hdl); - -done: - ov9740_s_power(&priv->subdev, 0); - return ret; -} - -/* Request bus settings on camera side */ -static int ov9740_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | - V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | - V4L2_MBUS_DATA_ACTIVE_HIGH; - cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); - - return 0; -} - -static const struct v4l2_subdev_video_ops ov9740_video_ops = { - .s_stream = ov9740_s_stream, - .g_mbus_config = ov9740_g_mbus_config, -}; - -static const struct v4l2_subdev_core_ops ov9740_core_ops = { - .s_power = ov9740_s_power, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = ov9740_get_register, - .s_register = ov9740_set_register, -#endif -}; - -static const struct v4l2_subdev_pad_ops ov9740_pad_ops = { - .enum_mbus_code = ov9740_enum_mbus_code, - .get_selection = ov9740_get_selection, - .set_fmt = ov9740_set_fmt, -}; - -static const struct v4l2_subdev_ops ov9740_subdev_ops = { - .core = &ov9740_core_ops, - .video = &ov9740_video_ops, - .pad = &ov9740_pad_ops, -}; - -static const struct v4l2_ctrl_ops ov9740_ctrl_ops = { - .s_ctrl = ov9740_s_ctrl, -}; - -/* - * i2c_driver function - */ -static int ov9740_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct ov9740_priv *priv; - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - int ret; - - if (!ssdd) { - dev_err(&client->dev, "Missing platform_data for driver\n"); - return -EINVAL; - } - - priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - v4l2_i2c_subdev_init(&priv->subdev, client, &ov9740_subdev_ops); - v4l2_ctrl_handler_init(&priv->hdl, 13); - v4l2_ctrl_new_std(&priv->hdl, &ov9740_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&priv->hdl, &ov9740_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - priv->subdev.ctrl_handler = &priv->hdl; - if (priv->hdl.error) - return priv->hdl.error; - - priv->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(priv->clk)) { - ret = PTR_ERR(priv->clk); - goto eclkget; - } - - ret = ov9740_video_probe(client); - if (ret < 0) { - v4l2_clk_put(priv->clk); -eclkget: - v4l2_ctrl_handler_free(&priv->hdl); - } - - return ret; -} - -static int ov9740_remove(struct i2c_client *client) -{ - struct ov9740_priv *priv = i2c_get_clientdata(client); - - v4l2_clk_put(priv->clk); - v4l2_device_unregister_subdev(&priv->subdev); - v4l2_ctrl_handler_free(&priv->hdl); - return 0; -} - -static const struct i2c_device_id ov9740_id[] = { - { "ov9740", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ov9740_id); - -static struct i2c_driver ov9740_i2c_driver = { - .driver = { - .name = "ov9740", - }, - .probe = ov9740_probe, - .remove = ov9740_remove, - .id_table = ov9740_id, -}; - -module_i2c_driver(ov9740_i2c_driver); - -MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV9740"); -MODULE_AUTHOR("Andrew Chew <achew@nvidia.com>"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/tegra-vde/vde.c b/drivers/staging/media/tegra-vde/vde.c index d3e63512a765..a3c24d96d5b9 100644 --- a/drivers/staging/media/tegra-vde/vde.c +++ b/drivers/staging/media/tegra-vde/vde.c @@ -777,7 +777,7 @@ static int tegra_vde_ioctl_decode_h264(struct tegra_vde *vde, ret = pm_runtime_get_sync(dev); if (ret < 0) - goto unlock; + goto put_runtime_pm; /* * We rely on the VDE registers reset value, otherwise VDE @@ -843,8 +843,6 @@ static int tegra_vde_ioctl_decode_h264(struct tegra_vde *vde, put_runtime_pm: pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); - -unlock: mutex_unlock(&vde->lock); release_dpb_frames: @@ -1066,17 +1064,16 @@ static int tegra_vde_probe(struct platform_device *pdev) pm_runtime_use_autosuspend(dev); pm_runtime_set_autosuspend_delay(dev, 300); - if (!pm_runtime_enabled(dev)) { - err = tegra_vde_runtime_resume(dev); - if (err) - goto err_misc_unreg; - } + /* + * VDE partition may be left ON after bootloader, hence let's + * power-cycle it in order to put hardware into a predictable lower + * power state. + */ + pm_runtime_get_sync(dev); + pm_runtime_put(dev); return 0; -err_misc_unreg: - misc_deregister(&vde->miscdev); - err_deinit_iommu: tegra_vde_iommu_deinit(vde); @@ -1091,17 +1088,18 @@ static int tegra_vde_remove(struct platform_device *pdev) { struct tegra_vde *vde = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; - int err; - - if (!pm_runtime_enabled(dev)) { - err = tegra_vde_runtime_suspend(dev); - if (err) - return err; - } + pm_runtime_get_sync(dev); pm_runtime_dont_use_autosuspend(dev); pm_runtime_disable(dev); + /* + * Balance RPM state, the VDE power domain is left ON and hardware + * is clock-gated. It's safe to reboot machine now. + */ + pm_runtime_put_noidle(dev); + clk_disable_unprepare(vde->clk); + misc_deregister(&vde->miscdev); tegra_vde_dmabuf_cache_unmap_all(vde); @@ -1113,6 +1111,16 @@ static int tegra_vde_remove(struct platform_device *pdev) return 0; } +static void tegra_vde_shutdown(struct platform_device *pdev) +{ + /* + * On some devices bootloader isn't ready to a power-gated VDE on + * a warm-reboot, machine will hang in that case. + */ + if (pm_runtime_status_suspended(&pdev->dev)) + tegra_vde_runtime_resume(&pdev->dev); +} + static __maybe_unused int tegra_vde_pm_suspend(struct device *dev) { struct tegra_vde *vde = dev_get_drvdata(dev); @@ -1158,6 +1166,7 @@ MODULE_DEVICE_TABLE(of, tegra_vde_of_match); static struct platform_driver tegra_vde_driver = { .probe = tegra_vde_probe, .remove = tegra_vde_remove, + .shutdown = tegra_vde_shutdown, .driver = { .name = "tegra-vde", .of_match_table = tegra_vde_of_match, diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index d5187b50fc82..991786876dbb 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1892,6 +1892,12 @@ static const struct usb_device_id acm_ids[] = { }, #endif +#if IS_ENABLED(CONFIG_IR_TOY) + { USB_DEVICE(0x04d8, 0xfd08), + .driver_info = IGNORE_DEVICE, + }, +#endif + /*Samsung phone in firmware update mode */ { USB_DEVICE(0x04e8, 0x685d), .driver_info = IGNORE_DEVICE, diff --git a/include/linux/platform_data/media/omap1_camera.h b/include/linux/platform_data/media/omap1_camera.h deleted file mode 100644 index 386439db68de..000000000000 --- a/include/linux/platform_data/media/omap1_camera.h +++ /dev/null @@ -1,32 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Header for V4L2 SoC Camera driver for OMAP1 Camera Interface - * - * Copyright (C) 2010, Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> - */ - -#ifndef __MEDIA_OMAP1_CAMERA_H_ -#define __MEDIA_OMAP1_CAMERA_H_ - -#include <linux/bitops.h> - -#define OMAP1_CAMERA_IOSIZE 0x1c - -enum omap1_cam_vb_mode { - OMAP1_CAM_DMA_CONTIG = 0, - OMAP1_CAM_DMA_SG, -}; - -#define OMAP1_CAMERA_MIN_BUF_COUNT(x) ((x) == OMAP1_CAM_DMA_CONTIG ? 3 : 2) - -struct omap1_cam_platform_data { - unsigned long camexclk_khz; - unsigned long lclk_khz_max; - unsigned long flags; -}; - -#define OMAP1_CAMERA_LCLK_RISING BIT(0) -#define OMAP1_CAMERA_RST_LOW BIT(1) -#define OMAP1_CAMERA_RST_HIGH BIT(2) - -#endif /* __MEDIA_OMAP1_CAMERA_H_ */ diff --git a/include/linux/property.h b/include/linux/property.h index 10d03572f52e..9f805c442819 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -389,6 +389,11 @@ struct fwnode_handle * fwnode_graph_get_remote_node(const struct fwnode_handle *fwnode, u32 port, u32 endpoint); +static inline bool fwnode_graph_is_endpoint(struct fwnode_handle *fwnode) +{ + return fwnode_property_present(fwnode, "remote-endpoint"); +} + /* * Fwnode lookup flags * diff --git a/include/media/cec.h b/include/media/cec.h index 972bc8cd4384..c48b5f2e4b50 100644 --- a/include/media/cec.h +++ b/include/media/cec.h @@ -144,6 +144,60 @@ struct cec_adap_ops { */ #define CEC_MAX_MSG_TX_QUEUE_SZ (18 * 1) +/** + * struct cec_adapter - cec adapter structure + * @owner: module owner + * @name: name of the CEC adapter + * @devnode: device node for the /dev/cecX device + * @lock: mutex controlling access to this structure + * @rc: remote control device + * @transmit_queue: queue of pending transmits + * @transmit_queue_sz: number of pending transmits + * @wait_queue: queue of transmits waiting for a reply + * @transmitting: CEC messages currently being transmitted + * @transmit_in_progress: true if a transmit is in progress + * @kthread_config: kthread used to configure a CEC adapter + * @config_completion: used to signal completion of the config kthread + * @kthread: main CEC processing thread + * @kthread_waitq: main CEC processing wait_queue + * @ops: cec adapter ops + * @priv: cec driver's private data + * @capabilities: cec adapter capabilities + * @available_log_addrs: maximum number of available logical addresses + * @phys_addr: the current physical address + * @needs_hpd: if true, then the HDMI HotPlug Detect pin must be high + * in order to transmit or receive CEC messages. This is usually a HW + * limitation. + * @is_configuring: the CEC adapter is configuring (i.e. claiming LAs) + * @is_configured: the CEC adapter is configured (i.e. has claimed LAs) + * @cec_pin_is_high: if true then the CEC pin is high. Only used with the + * CEC pin framework. + * @adap_controls_phys_addr: if true, then the CEC adapter controls the + * physical address, i.e. the CEC hardware can detect HPD changes and + * read the EDID and is not dependent on an external HDMI driver. + * Drivers that need this can set this field to true after the + * cec_allocate_adapter() call. + * @last_initiator: the initiator of the last transmitted message. + * @monitor_all_cnt: number of filehandles monitoring all msgs + * @monitor_pin_cnt: number of filehandles monitoring pin changes + * @follower_cnt: number of filehandles in follower mode + * @cec_follower: filehandle of the exclusive follower + * @cec_initiator: filehandle of the exclusive initiator + * @passthrough: if true, then the exclusive follower is in + * passthrough mode. + * @log_addrs: current logical addresses + * @conn_info: current connector info + * @tx_timeouts: number of transmit timeouts + * @notifier: CEC notifier + * @pin: CEC pin status struct + * @cec_dir: debugfs cec directory + * @status_file: debugfs cec status file + * @error_inj_file: debugfs cec error injection file + * @sequence: transmit sequence counter + * @input_phys: remote control input_phys name + * + * This structure represents a cec adapter. + */ struct cec_adapter { struct module *owner; char name[32]; @@ -162,7 +216,6 @@ struct cec_adapter { struct task_struct *kthread; wait_queue_head_t kthread_waitq; - wait_queue_head_t waitq; const struct cec_adap_ops *ops; void *priv; @@ -174,6 +227,7 @@ struct cec_adapter { bool is_configuring; bool is_configured; bool cec_pin_is_high; + bool adap_controls_phys_addr; u8 last_initiator; u32 monitor_all_cnt; u32 monitor_pin_cnt; @@ -197,7 +251,6 @@ struct cec_adapter { struct dentry *status_file; struct dentry *error_inj_file; - u16 phys_addrs[15]; u32 sequence; char input_phys[32]; diff --git a/include/media/davinci/vpbe_display.h b/include/media/davinci/vpbe_display.h index 56d05a855140..6d2a93740130 100644 --- a/include/media/davinci/vpbe_display.h +++ b/include/media/davinci/vpbe_display.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2010 Texas Instruments Incorporated - https://www.ti.com/ */ #ifndef VPBE_DISPLAY_H #define VPBE_DISPLAY_H diff --git a/include/media/drv-intf/soc_mediabus.h b/include/media/drv-intf/soc_mediabus.h deleted file mode 100644 index 361f8852c9fc..000000000000 --- a/include/media/drv-intf/soc_mediabus.h +++ /dev/null @@ -1,107 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * SoC-camera Media Bus API extensions - * - * Copyright (C) 2009, Guennadi Liakhovetski <g.liakhovetski@gmx.de> - */ - -#ifndef SOC_MEDIABUS_H -#define SOC_MEDIABUS_H - -#include <linux/videodev2.h> -#include <linux/v4l2-mediabus.h> - -/** - * enum soc_mbus_packing - data packing types on the media-bus - * @SOC_MBUS_PACKING_NONE: no packing, bit-for-bit transfer to RAM, one - * sample represents one pixel - * @SOC_MBUS_PACKING_2X8_PADHI: 16 bits transferred in 2 8-bit samples, in the - * possibly incomplete byte high bits are padding - * @SOC_MBUS_PACKING_2X8_PADLO: as above, but low bits are padding - * @SOC_MBUS_PACKING_EXTEND16: sample width (e.g., 10 bits) has to be extended - * to 16 bits - * @SOC_MBUS_PACKING_VARIABLE: compressed formats with variable packing - * @SOC_MBUS_PACKING_1_5X8: used for packed YUV 4:2:0 formats, where 4 - * pixels occupy 6 bytes in RAM - * @SOC_MBUS_PACKING_EXTEND32: sample width (e.g., 24 bits) has to be extended - * to 32 bits - */ -enum soc_mbus_packing { - SOC_MBUS_PACKING_NONE, - SOC_MBUS_PACKING_2X8_PADHI, - SOC_MBUS_PACKING_2X8_PADLO, - SOC_MBUS_PACKING_EXTEND16, - SOC_MBUS_PACKING_VARIABLE, - SOC_MBUS_PACKING_1_5X8, - SOC_MBUS_PACKING_EXTEND32, -}; - -/** - * enum soc_mbus_order - sample order on the media bus - * @SOC_MBUS_ORDER_LE: least significant sample first - * @SOC_MBUS_ORDER_BE: most significant sample first - */ -enum soc_mbus_order { - SOC_MBUS_ORDER_LE, - SOC_MBUS_ORDER_BE, -}; - -/** - * enum soc_mbus_layout - planes layout in memory - * @SOC_MBUS_LAYOUT_PACKED: color components packed - * @SOC_MBUS_LAYOUT_PLANAR_2Y_U_V: YUV components stored in 3 planes (4:2:2) - * @SOC_MBUS_LAYOUT_PLANAR_2Y_C: YUV components stored in a luma and a - * chroma plane (C plane is half the size - * of Y plane) - * @SOC_MBUS_LAYOUT_PLANAR_Y_C: YUV components stored in a luma and a - * chroma plane (C plane is the same size - * as Y plane) - */ -enum soc_mbus_layout { - SOC_MBUS_LAYOUT_PACKED = 0, - SOC_MBUS_LAYOUT_PLANAR_2Y_U_V, - SOC_MBUS_LAYOUT_PLANAR_2Y_C, - SOC_MBUS_LAYOUT_PLANAR_Y_C, -}; - -/** - * struct soc_mbus_pixelfmt - Data format on the media bus - * @fourcc: Fourcc code, that will be obtained if the data is - * stored in memory in the following way: - * @packing: Type of sample-packing, that has to be used - * @order: Sample order when storing in memory - * @bits_per_sample: How many bits the bridge has to sample - */ -struct soc_mbus_pixelfmt { - u32 fourcc; - enum soc_mbus_packing packing; - enum soc_mbus_order order; - enum soc_mbus_layout layout; - u8 bits_per_sample; -}; - -/** - * struct soc_mbus_lookup - Lookup FOURCC IDs by mediabus codes for pass-through - * @code: mediabus pixel-code - * @fmt: pixel format description - */ -struct soc_mbus_lookup { - u32 code; - struct soc_mbus_pixelfmt fmt; -}; - -const struct soc_mbus_pixelfmt *soc_mbus_find_fmtdesc( - u32 code, - const struct soc_mbus_lookup *lookup, - int n); -const struct soc_mbus_pixelfmt *soc_mbus_get_fmtdesc( - u32 code); -s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf); -s32 soc_mbus_image_size(const struct soc_mbus_pixelfmt *mf, - u32 bytes_per_line, u32 height); -int soc_mbus_samples_per_pixel(const struct soc_mbus_pixelfmt *mf, - unsigned int *numerator, unsigned int *denominator); -unsigned int soc_mbus_config_compatible(const struct v4l2_mbus_config *cfg, - unsigned int flags); - -#endif diff --git a/include/media/dvbdev.h b/include/media/dvbdev.h index 551325858de3..e547cbeee431 100644 --- a/include/media/dvbdev.h +++ b/include/media/dvbdev.h @@ -293,8 +293,8 @@ static inline void dvb_register_media_controller(struct dvb_adapter *adap, * * @adap: pointer to &struct dvb_adapter */ -static inline struct media_device -*dvb_get_media_controller(struct dvb_adapter *adap) +static inline struct media_device * +dvb_get_media_controller(struct dvb_adapter *adap) { return adap->mdev; } @@ -385,7 +385,7 @@ struct i2c_client; * with dvb_module_probe() should use dvb_module_release() to unbind. * * Return: - * On success, return an &struct i2c_client, pointing the the bound + * On success, return an &struct i2c_client, pointing to the bound * I2C device. %NULL otherwise. * * .. note:: diff --git a/include/media/media-device.h b/include/media/media-device.h index fa0895430720..1345e6da688a 100644 --- a/include/media/media-device.h +++ b/include/media/media-device.h @@ -128,7 +128,7 @@ struct media_device_ops { * * Use-case: find tuner entity connected to the decoder * entity and check if it is available, and activate the - * the link between them from @enable_source and deactivate + * link between them from @enable_source and deactivate * from @disable_source. * * .. note:: diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h index 6393842c6b21..d27c1c646c28 100644 --- a/include/media/media-devnode.h +++ b/include/media/media-devnode.h @@ -39,7 +39,7 @@ struct media_device; * @poll: pointer to the function that implements poll() syscall * @ioctl: pointer to the function that implements ioctl() syscall * @compat_ioctl: pointer to the function that will handle 32 bits userspace - * calls to the the ioctl() syscall on a Kernel compiled with 64 bits. + * calls to the ioctl() syscall on a Kernel compiled with 64 bits. * @open: pointer to the function that implements open() syscall * @release: pointer to the function that will release the resources allocated * by the @open function. diff --git a/include/media/media-entity.h b/include/media/media-entity.h index cde80ad029b7..cbdfcb79d0d0 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -803,7 +803,7 @@ int __media_entity_setup_link(struct media_link *link, u32 flags); * @flags: the requested new link flags * * The only configurable property is the %MEDIA_LNK_FL_ENABLED link flag - * flag to enable/disable a link. Links marked with the + * to enable/disable a link. Links marked with the * %MEDIA_LNK_FL_IMMUTABLE link flag can not be enabled or disabled. * * When a link is enabled or disabled, the media framework calls the diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h deleted file mode 100644 index 331c343a5b5a..000000000000 --- a/include/media/soc_camera.h +++ /dev/null @@ -1,397 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * camera image capture (abstract) bus driver header - * - * Copyright (C) 2006, Sascha Hauer, Pengutronix - * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> - */ - -#ifndef SOC_CAMERA_H -#define SOC_CAMERA_H - -#include <linux/bitops.h> -#include <linux/device.h> -#include <linux/mutex.h> -#include <linux/pm.h> -#include <linux/videodev2.h> -#include <media/videobuf2-v4l2.h> -#include <media/v4l2-async.h> -#include <media/v4l2-ctrls.h> -#include <media/v4l2-device.h> - -struct file; -struct soc_camera_desc; -struct soc_camera_async_client; - -struct soc_camera_device { - struct list_head list; /* list of all registered devices */ - struct soc_camera_desc *sdesc; - struct device *pdev; /* Platform device */ - struct device *parent; /* Camera host device */ - struct device *control; /* E.g., the i2c client */ - s32 user_width; - s32 user_height; - u32 bytesperline; /* for padding, zero if unused */ - u32 sizeimage; - enum v4l2_colorspace colorspace; - unsigned char iface; /* Host number */ - unsigned char devnum; /* Device number per host */ - struct soc_camera_sense *sense; /* See comment in struct definition */ - struct video_device *vdev; - struct v4l2_ctrl_handler ctrl_handler; - const struct soc_camera_format_xlate *current_fmt; - struct soc_camera_format_xlate *user_formats; - int num_user_formats; - enum v4l2_field field; /* Preserve field over close() */ - void *host_priv; /* Per-device host private data */ - /* soc_camera.c private count. Only accessed with .host_lock held */ - int use_count; - struct file *streamer; /* stream owner */ - struct v4l2_clk *clk; - /* Asynchronous subdevice management */ - struct soc_camera_async_client *sasc; - /* video buffer queue */ - struct vb2_queue vb2_vidq; -}; - -/* Host supports programmable stride */ -#define SOCAM_HOST_CAP_STRIDE (1 << 0) - -enum soc_camera_subdev_role { - SOCAM_SUBDEV_DATA_SOURCE = 1, - SOCAM_SUBDEV_DATA_SINK, - SOCAM_SUBDEV_DATA_PROCESSOR, -}; - -struct soc_camera_async_subdev { - struct v4l2_async_subdev asd; - enum soc_camera_subdev_role role; -}; - -struct soc_camera_host { - struct v4l2_device v4l2_dev; - struct list_head list; - struct mutex host_lock; /* Main synchronisation lock */ - struct mutex clk_lock; /* Protect pipeline modifications */ - unsigned char nr; /* Host number */ - u32 capabilities; - struct soc_camera_device *icd; /* Currently attached client */ - void *priv; - const char *drv_name; - struct soc_camera_host_ops *ops; - struct v4l2_async_subdev **asd; /* Flat array, arranged in groups */ - unsigned int *asd_sizes; /* 0-terminated array of asd group sizes */ -}; - -struct soc_camera_host_ops { - struct module *owner; - int (*add)(struct soc_camera_device *); - void (*remove)(struct soc_camera_device *); - int (*clock_start)(struct soc_camera_host *); - void (*clock_stop)(struct soc_camera_host *); - /* - * .get_formats() is called for each client device format, but - * .put_formats() is only called once. Further, if any of the calls to - * .get_formats() fail, .put_formats() will not be called at all, the - * failing .get_formats() must then clean up internally. - */ - int (*get_formats)(struct soc_camera_device *, unsigned int, - struct soc_camera_format_xlate *); - void (*put_formats)(struct soc_camera_device *); - int (*get_selection)(struct soc_camera_device *, struct v4l2_selection *); - int (*set_selection)(struct soc_camera_device *, struct v4l2_selection *); - /* - * The difference to .set_selection() is, that .set_liveselection is not allowed - * to change the output sizes - */ - int (*set_liveselection)(struct soc_camera_device *, struct v4l2_selection *); - int (*set_fmt)(struct soc_camera_device *, struct v4l2_format *); - int (*try_fmt)(struct soc_camera_device *, struct v4l2_format *); - int (*init_videobuf2)(struct vb2_queue *, - struct soc_camera_device *); - int (*querycap)(struct soc_camera_host *, struct v4l2_capability *); - int (*set_bus_param)(struct soc_camera_device *); - int (*get_parm)(struct soc_camera_device *, struct v4l2_streamparm *); - int (*set_parm)(struct soc_camera_device *, struct v4l2_streamparm *); - int (*enum_framesizes)(struct soc_camera_device *, struct v4l2_frmsizeenum *); - __poll_t (*poll)(struct file *, poll_table *); -}; - -#define SOCAM_SENSOR_INVERT_PCLK (1 << 0) -#define SOCAM_SENSOR_INVERT_MCLK (1 << 1) -#define SOCAM_SENSOR_INVERT_HSYNC (1 << 2) -#define SOCAM_SENSOR_INVERT_VSYNC (1 << 3) -#define SOCAM_SENSOR_INVERT_DATA (1 << 4) - -struct i2c_board_info; -struct regulator_bulk_data; - -struct soc_camera_subdev_desc { - /* Per camera SOCAM_SENSOR_* bus flags */ - unsigned long flags; - - /* sensor driver private platform data */ - void *drv_priv; - - /* - * Set unbalanced_power to true to deal with legacy drivers, failing to - * balance their calls to subdevice's .s_power() method. clock_state is - * then used internally by helper functions, it shouldn't be touched by - * drivers or the platform code. - */ - bool unbalanced_power; - unsigned long clock_state; - - /* Optional callbacks to power on or off and reset the sensor */ - int (*power)(struct device *, int); - int (*reset)(struct device *); - - /* - * some platforms may support different data widths than the sensors - * native ones due to different data line routing. Let the board code - * overwrite the width flags. - */ - int (*set_bus_param)(struct soc_camera_subdev_desc *, unsigned long flags); - unsigned long (*query_bus_param)(struct soc_camera_subdev_desc *); - void (*free_bus)(struct soc_camera_subdev_desc *); - - /* Optional regulators that have to be managed on power on/off events */ - struct v4l2_subdev_platform_data sd_pdata; -}; - -struct soc_camera_host_desc { - /* Camera bus id, used to match a camera and a bus */ - int bus_id; - int i2c_adapter_id; - struct i2c_board_info *board_info; - const char *module_name; - - /* - * For non-I2C devices platform has to provide methods to add a device - * to the system and to remove it - */ - int (*add_device)(struct soc_camera_device *); - void (*del_device)(struct soc_camera_device *); -}; - -/* - * Platform data for "soc-camera-pdrv" - * This MUST be kept binary-identical to struct soc_camera_link below, until - * it is completely replaced by this one, after which we can split it into its - * two components. - */ -struct soc_camera_desc { - struct soc_camera_subdev_desc subdev_desc; - struct soc_camera_host_desc host_desc; -}; - -/* Prepare to replace this struct: don't change its layout any more! */ -struct soc_camera_link { - /* - * Subdevice part - keep at top and compatible to - * struct soc_camera_subdev_desc - */ - - /* Per camera SOCAM_SENSOR_* bus flags */ - unsigned long flags; - - void *priv; - - /* Set by platforms to handle misbehaving drivers */ - bool unbalanced_power; - /* Used by soc-camera helper functions */ - unsigned long clock_state; - - /* Optional callbacks to power on or off and reset the sensor */ - int (*power)(struct device *, int); - int (*reset)(struct device *); - /* - * some platforms may support different data widths than the sensors - * native ones due to different data line routing. Let the board code - * overwrite the width flags. - */ - int (*set_bus_param)(struct soc_camera_link *, unsigned long flags); - unsigned long (*query_bus_param)(struct soc_camera_link *); - void (*free_bus)(struct soc_camera_link *); - - /* Optional regulators that have to be managed on power on/off events */ - struct regulator_bulk_data *regulators; - int num_regulators; - - void *host_priv; - - /* - * Host part - keep at bottom and compatible to - * struct soc_camera_host_desc - */ - - /* Camera bus id, used to match a camera and a bus */ - int bus_id; - int i2c_adapter_id; - struct i2c_board_info *board_info; - const char *module_name; - - /* - * For non-I2C devices platform has to provide methods to add a device - * to the system and to remove it - */ - int (*add_device)(struct soc_camera_device *); - void (*del_device)(struct soc_camera_device *); -}; - -static inline struct soc_camera_host *to_soc_camera_host( - const struct device *dev) -{ - struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); - - return container_of(v4l2_dev, struct soc_camera_host, v4l2_dev); -} - -static inline struct soc_camera_desc *to_soc_camera_desc( - const struct soc_camera_device *icd) -{ - return icd->sdesc; -} - -static inline struct device *to_soc_camera_control( - const struct soc_camera_device *icd) -{ - return icd->control; -} - -static inline struct v4l2_subdev *soc_camera_to_subdev( - const struct soc_camera_device *icd) -{ - struct device *control = to_soc_camera_control(icd); - return dev_get_drvdata(control); -} - -int soc_camera_host_register(struct soc_camera_host *ici); -void soc_camera_host_unregister(struct soc_camera_host *ici); - -const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc( - struct soc_camera_device *icd, unsigned int fourcc); - -/** - * struct soc_camera_format_xlate - match between host and sensor formats - * @code: code of a sensor provided format - * @host_fmt: host format after host translation from code - * - * Host and sensor translation structure. Used in table of host and sensor - * formats matchings in soc_camera_device. A host can override the generic list - * generation by implementing get_formats(), and use it for format checks and - * format setup. - */ -struct soc_camera_format_xlate { - u32 code; - const struct soc_mbus_pixelfmt *host_fmt; -}; - -#define SOCAM_SENSE_PCLK_CHANGED (1 << 0) - -/** - * This struct can be attached to struct soc_camera_device by the host driver - * to request sense from the camera, for example, when calling .set_fmt(). The - * host then can check which flags are set and verify respective values if any. - * For example, if SOCAM_SENSE_PCLK_CHANGED is set, it means, pixclock has - * changed during this operation. After completion the host should detach sense. - * - * @flags ored SOCAM_SENSE_* flags - * @master_clock if the host wants to be informed about pixel-clock - * change, it better set master_clock. - * @pixel_clock_max maximum pixel clock frequency supported by the host, - * camera is not allowed to exceed this. - * @pixel_clock if the camera driver changed pixel clock during this - * operation, it sets SOCAM_SENSE_PCLK_CHANGED, uses - * master_clock to calculate the new pixel-clock and - * sets this field. - */ -struct soc_camera_sense { - unsigned long flags; - unsigned long master_clock; - unsigned long pixel_clock_max; - unsigned long pixel_clock; -}; - -#define SOCAM_DATAWIDTH(x) BIT((x) - 1) -#define SOCAM_DATAWIDTH_4 SOCAM_DATAWIDTH(4) -#define SOCAM_DATAWIDTH_8 SOCAM_DATAWIDTH(8) -#define SOCAM_DATAWIDTH_9 SOCAM_DATAWIDTH(9) -#define SOCAM_DATAWIDTH_10 SOCAM_DATAWIDTH(10) -#define SOCAM_DATAWIDTH_12 SOCAM_DATAWIDTH(12) -#define SOCAM_DATAWIDTH_15 SOCAM_DATAWIDTH(15) -#define SOCAM_DATAWIDTH_16 SOCAM_DATAWIDTH(16) -#define SOCAM_DATAWIDTH_18 SOCAM_DATAWIDTH(18) -#define SOCAM_DATAWIDTH_24 SOCAM_DATAWIDTH(24) - -#define SOCAM_DATAWIDTH_MASK (SOCAM_DATAWIDTH_4 | SOCAM_DATAWIDTH_8 | \ - SOCAM_DATAWIDTH_9 | SOCAM_DATAWIDTH_10 | \ - SOCAM_DATAWIDTH_12 | SOCAM_DATAWIDTH_15 | \ - SOCAM_DATAWIDTH_16 | SOCAM_DATAWIDTH_18 | \ - SOCAM_DATAWIDTH_24) - -static inline void soc_camera_limit_side(int *start, int *length, - unsigned int start_min, - unsigned int length_min, unsigned int length_max) -{ - if (*length < length_min) - *length = length_min; - else if (*length > length_max) - *length = length_max; - - if (*start < start_min) - *start = start_min; - else if (*start > start_min + length_max - *length) - *start = start_min + length_max - *length; -} - -unsigned long soc_camera_apply_board_flags(struct soc_camera_subdev_desc *ssdd, - const struct v4l2_mbus_config *cfg); - -int soc_camera_power_init(struct device *dev, struct soc_camera_subdev_desc *ssdd); -int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd, - struct v4l2_clk *clk); -int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd, - struct v4l2_clk *clk); - -static inline int soc_camera_set_power(struct device *dev, - struct soc_camera_subdev_desc *ssdd, struct v4l2_clk *clk, bool on) -{ - return on ? soc_camera_power_on(dev, ssdd, clk) - : soc_camera_power_off(dev, ssdd, clk); -} - -/* This is only temporary here - until v4l2-subdev begins to link to video_device */ -#include <linux/i2c.h> -static inline struct video_device *soc_camera_i2c_to_vdev(const struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct soc_camera_device *icd = v4l2_get_subdev_hostdata(sd); - return icd ? icd->vdev : NULL; -} - -static inline struct soc_camera_subdev_desc *soc_camera_i2c_to_desc(const struct i2c_client *client) -{ - return client->dev.platform_data; -} - -static inline struct v4l2_subdev *soc_camera_vdev_to_subdev(struct video_device *vdev) -{ - struct soc_camera_device *icd = video_get_drvdata(vdev); - return soc_camera_to_subdev(icd); -} - -static inline struct soc_camera_device *soc_camera_from_vb2q(const struct vb2_queue *vq) -{ - return container_of(vq, struct soc_camera_device, vb2_vidq); -} - -static inline u32 soc_camera_grp_id(const struct soc_camera_device *icd) -{ - return (icd->iface << 8) | (icd->devnum + 1); -} - -void soc_camera_lock(struct vb2_queue *vq); -void soc_camera_unlock(struct vb2_queue *vq); - -#endif diff --git a/include/media/tpg/v4l2-tpg.h b/include/media/tpg/v4l2-tpg.h index eb191e85d363..0b0ddb87380e 100644 --- a/include/media/tpg/v4l2-tpg.h +++ b/include/media/tpg/v4l2-tpg.h @@ -241,7 +241,7 @@ void tpg_log_status(struct tpg_data *tpg); void tpg_set_font(const u8 *f); void tpg_gen_text(const struct tpg_data *tpg, - u8 *basep[TPG_MAX_PLANES][2], int y, int x, char *text); + u8 *basep[TPG_MAX_PLANES][2], int y, int x, const char *text); void tpg_calc_text_basep(struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], unsigned p, u8 *vbuf); unsigned tpg_g_interleaved_plane(const struct tpg_data *tpg, unsigned buf_line); @@ -252,6 +252,7 @@ void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id std, bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc); void tpg_s_crop_compose(struct tpg_data *tpg, const struct v4l2_rect *crop, const struct v4l2_rect *compose); +const char *tpg_g_color_order(const struct tpg_data *tpg); static inline void tpg_s_pattern(struct tpg_data *tpg, enum tpg_pattern pattern) { diff --git a/include/media/v4l2-mc.h b/include/media/v4l2-mc.h index 246eed398648..bdaa5f2f8ca2 100644 --- a/include/media/v4l2-mc.h +++ b/include/media/v4l2-mc.h @@ -89,8 +89,8 @@ int v4l_vb2q_enable_media_source(struct vb2_queue *q); * v4l2_create_fwnode_links_to_pad - Create fwnode-based links from a * source subdev to a sink subdev pad. * - * @src_sd - pointer to a source subdev - * @sink - pointer to a subdev sink pad + * @src_sd: pointer to a source subdev + * @sink: pointer to a subdev sink pad * * This function searches for fwnode endpoint connections from a source * subdevice to a single sink pad, and if suitable connections are found, @@ -113,8 +113,8 @@ int v4l2_create_fwnode_links_to_pad(struct v4l2_subdev *src_sd, * v4l2_create_fwnode_links - Create fwnode-based links from a source * subdev to a sink subdev. * - * @src_sd - pointer to a source subdevice - * @sink_sd - pointer to a sink subdevice + * @src_sd: pointer to a source subdevice + * @sink_sd: pointer to a sink subdevice * * This function searches for any and all fwnode endpoint connections * between source and sink subdevices, and translates them into media diff --git a/include/media/v4l2-rect.h b/include/media/v4l2-rect.h index 8800a640c224..bd587d0c0dc3 100644 --- a/include/media/v4l2-rect.h +++ b/include/media/v4l2-rect.h @@ -184,4 +184,24 @@ static inline bool v4l2_rect_overlap(const struct v4l2_rect *r1, return true; } +/** + * v4l2_rect_enclosed() - is r1 enclosed in r2? + * @r1: rectangle. + * @r2: rectangle. + * + * Returns true if @r1 is enclosed in @r2. + */ +static inline bool v4l2_rect_enclosed(struct v4l2_rect *r1, + struct v4l2_rect *r2) +{ + if (r1->left < r2->left || r1->top < r2->top) + return false; + if (r1->left + r1->width > r2->left + r2->width) + return false; + if (r1->top + r1->height > r2->top + r2->height) + return false; + + return true; +} + #endif diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index f7fe78a6f65a..d4e3b44cf14c 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -566,9 +566,9 @@ struct v4l2_subdev_ir_parameters { * * @rx_read: Reads received codes or pulse width data. * The semantics are similar to a non-blocking read() call. - * @rx_g_parameters: Get the current operating parameters and state of the + * @rx_g_parameters: Get the current operating parameters and state of * the IR receiver. - * @rx_s_parameters: Set the current operating parameters and state of the + * @rx_s_parameters: Set the current operating parameters and state of * the IR receiver. It is recommended to call * [rt]x_g_parameters first to fill out the current state, and only change * the fields that need to be changed. Upon return, the actual device @@ -582,9 +582,9 @@ struct v4l2_subdev_ir_parameters { * * @tx_write: Writes codes or pulse width data for transmission. * The semantics are similar to a non-blocking write() call. - * @tx_g_parameters: Get the current operating parameters and state of the + * @tx_g_parameters: Get the current operating parameters and state of * the IR transmitter. - * @tx_s_parameters: Set the current operating parameters and state of the + * @tx_s_parameters: Set the current operating parameters and state of * the IR transmitter. It is recommended to call * [rt]x_g_parameters first to fill out the current state, and only change * the fields that need to be changed. Upon return, the actual device @@ -930,10 +930,10 @@ struct v4l2_subdev_fh { * @cfg: pointer to &struct v4l2_subdev_pad_config array. * @pad: index of the pad in the @cfg array. */ -static inline struct v4l2_mbus_framefmt -*v4l2_subdev_get_try_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - unsigned int pad) +static inline struct v4l2_mbus_framefmt * +v4l2_subdev_get_try_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad) { if (WARN_ON(pad >= sd->entity.num_pads)) pad = 0; @@ -948,10 +948,10 @@ static inline struct v4l2_mbus_framefmt * @cfg: pointer to &struct v4l2_subdev_pad_config array. * @pad: index of the pad in the @cfg array. */ -static inline struct v4l2_rect -*v4l2_subdev_get_try_crop(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - unsigned int pad) +static inline struct v4l2_rect * +v4l2_subdev_get_try_crop(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad) { if (WARN_ON(pad >= sd->entity.num_pads)) pad = 0; @@ -959,22 +959,23 @@ static inline struct v4l2_rect } /** - * v4l2_subdev_get_try_crop - ancillary routine to call + * v4l2_subdev_get_try_compose - ancillary routine to call * &struct v4l2_subdev_pad_config->try_compose * * @sd: pointer to &struct v4l2_subdev * @cfg: pointer to &struct v4l2_subdev_pad_config array. * @pad: index of the pad in the @cfg array. */ -static inline struct v4l2_rect -*v4l2_subdev_get_try_compose(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - unsigned int pad) +static inline struct v4l2_rect * +v4l2_subdev_get_try_compose(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad) { if (WARN_ON(pad >= sd->entity.num_pads)) pad = 0; return &cfg[pad].try_compose; } + #endif extern const struct v4l2_file_operations v4l2_subdev_fops; @@ -1031,8 +1032,8 @@ static inline void *v4l2_get_subdev_hostdata(const struct v4l2_subdev *sd) * v4l2_subdev_get_fwnode_pad_1_to_1 - Get pad number from a subdev fwnode * endpoint, assuming 1:1 port:pad * - * @entity - Pointer to the subdev entity - * @endpoint - Pointer to a parsed fwnode endpoint + * @entity: Pointer to the subdev entity + * @endpoint: Pointer to a parsed fwnode endpoint * * This function can be used as the .get_fwnode_pad operation for * subdevices that map port numbers and pad indexes 1:1. If the endpoint diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index f11b96514cf7..52ef92049073 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -263,6 +263,10 @@ struct vb2_buffer { * after the 'buf_finish' op is called. * copied_timestamp: the timestamp of this capture buffer was copied * from an output buffer. + * need_cache_sync_on_prepare: when set buffer's ->prepare() function + * performs cache sync/invalidation. + * need_cache_sync_on_finish: when set buffer's ->finish() function + * performs cache sync/invalidation. * queued_entry: entry on the queued buffers list, which holds * all buffers queued from userspace * done_entry: entry on the list that stores all buffers ready @@ -273,6 +277,8 @@ struct vb2_buffer { unsigned int synced:1; unsigned int prepared:1; unsigned int copied_timestamp:1; + unsigned int need_cache_sync_on_prepare:1; + unsigned int need_cache_sync_on_finish:1; struct vb2_plane planes[VB2_MAX_PLANES]; struct list_head queued_entry; @@ -491,6 +497,9 @@ struct vb2_buf_ops { * @uses_requests: requests are used for this queue. Set to 1 the first time * a request is queued. Set to 0 when the queue is canceled. * If this is 1, then you cannot queue buffers directly. + * @allow_cache_hints: when set user-space can pass cache management hints in + * order to skip cache flush/invalidation on ->prepare() or/and + * ->finish(). * @lock: pointer to a mutex that protects the &struct vb2_queue. The * driver can set this to a mutex to let the v4l2 core serialize * the queuing ioctls. If the driver wants to handle locking @@ -552,21 +561,24 @@ struct vb2_buf_ops { * when a buffer with the %V4L2_BUF_FLAG_LAST is dequeued. * @fileio: file io emulator internal data, used only if emulator is active * @threadio: thread io internal data, used only if thread is active + * @name: queue name, used for logging purpose. Initialized automatically + * if left empty by drivers. */ struct vb2_queue { unsigned int type; unsigned int io_modes; struct device *dev; unsigned long dma_attrs; - unsigned bidirectional:1; - unsigned fileio_read_once:1; - unsigned fileio_write_immediately:1; - unsigned allow_zero_bytesused:1; - unsigned quirk_poll_must_check_waiting_for_buffers:1; - unsigned supports_requests:1; - unsigned requires_requests:1; - unsigned uses_qbuf:1; - unsigned uses_requests:1; + unsigned int bidirectional:1; + unsigned int fileio_read_once:1; + unsigned int fileio_write_immediately:1; + unsigned int allow_zero_bytesused:1; + unsigned int quirk_poll_must_check_waiting_for_buffers:1; + unsigned int supports_requests:1; + unsigned int requires_requests:1; + unsigned int uses_qbuf:1; + unsigned int uses_requests:1; + unsigned int allow_cache_hints:1; struct mutex *lock; void *owner; @@ -612,6 +624,8 @@ struct vb2_queue { struct vb2_fileio_data *fileio; struct vb2_threadio_data *threadio; + char name[32]; + #ifdef CONFIG_VIDEO_ADV_DEBUG /* * Counters for how often these queue-related ops are @@ -626,6 +640,17 @@ struct vb2_queue { }; /** + * vb2_queue_allows_cache_hints() - Return true if the queue allows cache + * and memory consistency hints. + * + * @q: pointer to &struct vb2_queue with videobuf2 queue + */ +static inline bool vb2_queue_allows_cache_hints(struct vb2_queue *q) +{ + return q->allow_cache_hints && q->memory == VB2_MEMORY_MMAP; +} + +/** * vb2_plane_vaddr() - Return a kernel virtual address of a given plane. * @vb: pointer to &struct vb2_buffer to which the plane in * question belongs to. @@ -719,6 +744,8 @@ void vb2_core_querybuf(struct vb2_queue *q, unsigned int index, void *pb); * vb2_core_reqbufs() - Initiate streaming. * @q: pointer to &struct vb2_queue with videobuf2 queue. * @memory: memory type, as defined by &enum vb2_memory. + * @flags: auxiliary queue/buffer management flags. Currently, the only + * used flag is %V4L2_FLAG_MEMORY_NON_CONSISTENT. * @count: requested buffer count. * * Videobuf2 core helper to implement VIDIOC_REQBUF() operation. It is called @@ -743,12 +770,13 @@ void vb2_core_querybuf(struct vb2_queue *q, unsigned int index, void *pb); * Return: returns zero on success; an error code otherwise. */ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, - unsigned int *count); + unsigned int flags, unsigned int *count); /** * vb2_core_create_bufs() - Allocate buffers and any required auxiliary structs * @q: pointer to &struct vb2_queue with videobuf2 queue. * @memory: memory type, as defined by &enum vb2_memory. + * @flags: auxiliary queue/buffer management flags. * @count: requested buffer count. * @requested_planes: number of planes requested. * @requested_sizes: array with the size of the planes. @@ -766,7 +794,8 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, * Return: returns zero on success; an error code otherwise. */ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, - unsigned int *count, unsigned int requested_planes, + unsigned int flags, unsigned int *count, + unsigned int requested_planes, const unsigned int requested_sizes[]); /** diff --git a/include/media/videobuf2-v4l2.h b/include/media/videobuf2-v4l2.h index 59bf33a12648..b7b5a9cb5a28 100644 --- a/include/media/videobuf2-v4l2.h +++ b/include/media/videobuf2-v4l2.h @@ -237,6 +237,19 @@ int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type); int __must_check vb2_queue_init(struct vb2_queue *q); /** + * vb2_queue_init_name() - initialize a videobuf2 queue with a name + * @q: pointer to &struct vb2_queue with videobuf2 queue. + * @name: the queue name + * + * This function initializes the vb2_queue exactly like vb2_queue_init(), + * and additionally sets the queue name. The queue name is used for logging + * purpose, and should uniquely identify the queue within the context of the + * device it belongs to. This is useful to attribute kernel log messages to the + * right queue for m2m devices or other devices that handle multiple queues. + */ +int __must_check vb2_queue_init_name(struct vb2_queue *q, const char *name); + +/** * vb2_queue_release() - stop streaming, release the queue and free memory * @q: pointer to &struct vb2_queue with videobuf2 queue. * diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index c3a1cf1c507f..c7b70ff53bc1 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -171,6 +171,8 @@ enum v4l2_buf_type { || (type) == V4L2_BUF_TYPE_SDR_OUTPUT \ || (type) == V4L2_BUF_TYPE_META_OUTPUT) +#define V4L2_TYPE_IS_CAPTURE(type) (!V4L2_TYPE_IS_OUTPUT(type)) + enum v4l2_tuner_type { V4L2_TUNER_RADIO = 1, V4L2_TUNER_ANALOG_TV = 2, @@ -189,6 +191,8 @@ enum v4l2_memory { V4L2_MEMORY_DMABUF = 4, }; +#define V4L2_FLAG_MEMORY_NON_CONSISTENT (1 << 0) + /* see also http://vektor.theorem.ca/graphics/ycbcr/ */ enum v4l2_colorspace { /* @@ -792,6 +796,7 @@ struct v4l2_fmtdesc { #define V4L2_FMT_FLAG_EMULATED 0x0002 #define V4L2_FMT_FLAG_CONTINUOUS_BYTESTREAM 0x0004 #define V4L2_FMT_FLAG_DYN_RESOLUTION 0x0008 +#define V4L2_FMT_FLAG_ENC_CAP_FRAME_INTERVAL 0x0010 /* Frame Size and frame rate enumeration */ /* @@ -944,7 +949,10 @@ struct v4l2_requestbuffers { __u32 type; /* enum v4l2_buf_type */ __u32 memory; /* enum v4l2_memory */ __u32 capabilities; - __u32 reserved[1]; + union { + __u32 flags; + __u32 reserved[1]; + }; }; /* capabilities for struct v4l2_requestbuffers and v4l2_create_buffers */ @@ -954,6 +962,7 @@ struct v4l2_requestbuffers { #define V4L2_BUF_CAP_SUPPORTS_REQUESTS (1 << 3) #define V4L2_BUF_CAP_SUPPORTS_ORPHANED_BUFS (1 << 4) #define V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF (1 << 5) +#define V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS (1 << 6) /** * struct v4l2_plane - plane info for multi-planar buffers @@ -2447,6 +2456,9 @@ struct v4l2_dbg_chip_info { * @memory: enum v4l2_memory; buffer memory type * @format: frame format, for which buffers are requested * @capabilities: capabilities of this buffer type. + * @flags: additional buffer management attributes (ignored unless the + * queue has V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS capability + * and configured for MMAP streaming I/O). * @reserved: future extensions */ struct v4l2_create_buffers { @@ -2455,7 +2467,8 @@ struct v4l2_create_buffers { __u32 memory; struct v4l2_format format; __u32 capabilities; - __u32 reserved[7]; + __u32 flags; + __u32 reserved[6]; }; /* |