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 ...
Unified · Split
| @@ -2,7 +2,7 @@ · 1 unmodified line | ||
| 2 | 2 | |
| 3 | 3 | .. include:: <isonum.txt> |
| 4 | 4 | |
| 5 | −The Samsung S5P/EXYNOS4 FIMC driver | |
| 5 | +The Samsung S5P/Exynos4 FIMC driver | |
| 6 | 6 | =================================== |
| 7 | 7 | |
| 8 | 8 | Copyright |copy| 2012 - 2013 Samsung Electronics Co., Ltd. |
| @@ -19,7 +19,7 @@ drivers/media/platform/exynos4-is directory. · 10 unmodified lines | ||
| 19 | 19 | Supported SoCs |
| 20 | 20 | -------------- |
| 21 | 21 | |
| 22 | −S5PC100 (mem-to-mem only), S5PV210, EXYNOS4210 | |
| 22 | +S5PC100 (mem-to-mem only), S5PV210, Exynos4210 | |
| 23 | 23 | |
| 24 | 24 | Supported features |
| 25 | 25 | ------------------ |
| @@ -45,7 +45,7 @@ Media device interface · 19 unmodified lines | ||
| 45 | 45 | ~~~~~~~~~~~~~~~~~~~~~~ |
| 46 | 46 | |
| 47 | 47 | The driver supports Media Controller API as defined at :ref:`media_controller`. |
| 48 | −The media device driver name is "SAMSUNG S5P FIMC". | |
| 48 | +The media device driver name is "Samsung S5P FIMC". | |
| 49 | 49 | |
| 50 | 50 | The purpose of this interface is to allow changing assignment of FIMC instances |
| 51 | 51 | to the SoC peripheral camera input at runtime and optionally to control internal |
| @@ -293,6 +293,15 @@ all configurable using the following module options: · 292 unmodified lines | ||
| 293 | 293 | - 0: vmalloc |
| 294 | 294 | - 1: dma-contig |
| 295 | 295 | |
| 296 | +- cache_hints: | |
| 297 | + | |
| 298 | + specifies if the device should set queues' user-space cache and memory | |
| 299 | + consistency hint capability (V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS). | |
| 300 | + The hints are valid only when using MMAP streaming I/O. Default is 0. | |
| 301 | + | |
| 302 | + - 0: forbid hints | |
| 303 | + - 1: allow hints | |
| 304 | + | |
| 296 | 305 | Taken together, all these module options allow you to precisely customize |
| 297 | 306 | the driver behavior and test your application with all sorts of permutations. |
| 298 | 307 | It is also very suitable to emulate hardware that is not yet available, e.g. |
| @@ -0,0 +1,67 @@ | ||
| 1 | +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) | |
| 2 | +%YAML 1.2 | |
| 3 | +--- | |
| 4 | +$id: "http://devicetree.org/schemas/media/i2c/chrontel,ch7322.yaml#" | |
| 5 | +$schema: "http://devicetree.org/meta-schemas/core.yaml#" | |
| 6 | + | |
| 7 | +title: Chrontel HDMI-CEC Controller | |
| 8 | + | |
| 9 | +maintainers: | |
| 10 | + - Jeff Chase <jnchase@google.com> | |
| 11 | + | |
| 12 | +description: | |
| 13 | + The Chrontel CH7322 is a discrete HDMI-CEC controller. It is | |
| 14 | + programmable through I2C and drives a single CEC line. | |
| 15 | + | |
| 16 | +properties: | |
| 17 | + compatible: | |
| 18 | + const: chrontel,ch7322 | |
| 19 | + | |
| 20 | + reg: | |
| 21 | + description: I2C device address | |
| 22 | + maxItems: 1 | |
| 23 | + | |
| 24 | + clocks: | |
| 25 | + maxItems: 1 | |
| 26 | + | |
| 27 | + interrupts: | |
| 28 | + maxItems: 1 | |
| 29 | + | |
| 30 | + reset-gpios: | |
| 31 | + description: | |
| 32 | + Reference to the GPIO connected to the RESET pin, if any. This | |
| 33 | + pin is active-low. | |
| 34 | + maxItems: 1 | |
| 35 | + | |
| 36 | + standby-gpios: | |
| 37 | + description: | |
| 38 | + Reference to the GPIO connected to the OE pin, if any. When low | |
| 39 | + the device will respond to power status requests with "standby" | |
| 40 | + if in auto mode. | |
| 41 | + maxItems: 1 | |
| 42 | + | |
| 43 | + # see ../cec.txt | |
| 44 | + hdmi-phandle: | |
| 45 | + description: phandle to the HDMI controller | |
| 46 | + | |
| 47 | +required: | |
| 48 | + - compatible | |
| 49 | + - reg | |
| 50 | + - interrupts | |
| 51 | + | |
| 52 | +examples: | |
| 53 | + - | | |
| 54 | + #include <dt-bindings/gpio/gpio.h> | |
| 55 | + #include <dt-bindings/interrupt-controller/irq.h> | |
| 56 | + i2c { | |
| 57 | + #address-cells = <1>; | |
| 58 | + #size-cells = <0>; | |
| 59 | + ch7322@75 { | |
| 60 | + compatible = "chrontel,ch7322"; | |
| 61 | + reg = <0x75>; | |
| 62 | + interrupts = <47 IRQ_TYPE_EDGE_RISING>; | |
| 63 | + standby-gpios = <&gpio 16 GPIO_ACTIVE_LOW>; | |
| 64 | + reset-gpios = <&gpio 15 GPIO_ACTIVE_LOW>; | |
| 65 | + hdmi-phandle = <&hdmi>; | |
| 66 | + }; | |
| 67 | + }; | |
| @@ -0,0 +1,100 @@ | ||
| 1 | +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) | |
| 2 | +# Copyright (c) 2020 MediaTek Inc. | |
| 3 | +%YAML 1.2 | |
| 4 | +--- | |
| 5 | +$id: http://devicetree.org/schemas/media/i2c/dongwoon,dw9768.yaml# | |
| 6 | +$schema: http://devicetree.org/meta-schemas/core.yaml# | |
| 7 | + | |
| 8 | +title: Dongwoon Anatech DW9768 Voice Coil Motor (VCM) Lens Device Tree Bindings | |
| 9 | + | |
| 10 | +maintainers: | |
| 11 | + - Dongchun Zhu <dongchun.zhu@mediatek.com> | |
| 12 | + | |
| 13 | +description: |- | |
| 14 | + The Dongwoon DW9768 is a single 10-bit digital-to-analog (DAC) converter | |
| 15 | + with 100 mA output current sink capability. VCM current is controlled with | |
| 16 | + a linear mode driver. The DAC is controlled via a 2-wire (I2C-compatible) | |
| 17 | + serial interface that operates at clock rates up to 1MHz. This chip | |
| 18 | + integrates Advanced Actuator Control (AAC) technology and is intended for | |
| 19 | + driving voice coil lenses in camera modules. | |
| 20 | + | |
| 21 | +properties: | |
| 22 | + compatible: | |
| 23 | + enum: | |
| 24 | + - dongwoon,dw9768 # for DW9768 VCM | |
| 25 | + - giantec,gt9769 # for GT9769 VCM | |
| 26 | + | |
| 27 | + reg: | |
| 28 | + maxItems: 1 | |
| 29 | + | |
| 30 | + vin-supply: | |
| 31 | + description: | |
| 32 | + Definition of the regulator used as Digital I/O voltage supply. | |
| 33 | + | |
| 34 | + vdd-supply: | |
| 35 | + description: | |
| 36 | + Definition of the regulator used as Digital core voltage supply. | |
| 37 | + | |
| 38 | + dongwoon,aac-mode: | |
| 39 | + description: | |
| 40 | + Indication of AAC mode select. | |
| 41 | + allOf: | |
| 42 | + - $ref: "/schemas/types.yaml#/definitions/uint32" | |
| 43 | + - enum: | |
| 44 | + - 1 # AAC2 mode(operation time# 0.48 x Tvib) | |
| 45 | + - 2 # AAC3 mode(operation time# 0.70 x Tvib) | |
| 46 | + - 3 # AAC4 mode(operation time# 0.75 x Tvib) | |
| 47 | + - 5 # AAC8 mode(operation time# 1.13 x Tvib) | |
| 48 | + default: 2 | |
| 49 | + | |
| 50 | + dongwoon,aac-timing: | |
| 51 | + description: | |
| 52 | + Number of AAC Timing count that controlled by one 6-bit period of | |
| 53 | + vibration register AACT[5:0], the unit of which is 100 us. | |
| 54 | + allOf: | |
| 55 | + - $ref: "/schemas/types.yaml#/definitions/uint32" | |
| 56 | + - default: 0x20 | |
| 57 | + minimum: 0x00 | |
| 58 | + maximum: 0x3f | |
| 59 | + | |
| 60 | + dongwoon,clock-presc: | |
| 61 | + description: | |
| 62 | + Indication of VCM internal clock dividing rate select, as one multiple | |
| 63 | + factor to calculate VCM ring periodic time Tvib. | |
| 64 | + allOf: | |
| 65 | + - $ref: "/schemas/types.yaml#/definitions/uint32" | |
| 66 | + - enum: | |
| 67 | + - 0 # Dividing Rate - 2 | |
| 68 | + - 1 # Dividing Rate - 1 | |
| 69 | + - 2 # Dividing Rate - 1/2 | |
| 70 | + - 3 # Dividing Rate - 1/4 | |
| 71 | + - 4 # Dividing Rate - 8 | |
| 72 | + - 5 # Dividing Rate - 4 | |
| 73 | + default: 1 | |
| 74 | + | |
| 75 | +required: | |
| 76 | + - compatible | |
| 77 | + - reg | |
| 78 | + - vin-supply | |
| 79 | + - vdd-supply | |
| 80 | + | |
| 81 | +additionalProperties: false | |
| 82 | + | |
| 83 | +examples: | |
| 84 | + - | | |
| 85 | + | |
| 86 | + i2c { | |
| 87 | + #address-cells = <1>; | |
| 88 | + #size-cells = <0>; | |
| 89 | + | |
| 90 | + dw9768: camera-lens@c { | |
| 91 | + compatible = "dongwoon,dw9768"; | |
| 92 | + reg = <0x0c>; | |
| 93 | + | |
| 94 | + vin-supply = <&mt6358_vcamio_reg>; | |
| 95 | + vdd-supply = <&mt6358_vcama2_reg>; | |
| 96 | + dongwoon,aac-timing = <0x39>; | |
| 97 | + }; | |
| 98 | + }; | |
| 99 | + | |
| 100 | +... | |
| @@ -0,0 +1,159 @@ | ||
| 1 | +# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause | |
| 2 | +# Copyright (C) 2019 Renesas Electronics Corp. | |
| 3 | +%YAML 1.2 | |
| 4 | +--- | |
| 5 | +$id: http://devicetree.org/schemas/media/i2c/imi,rdacm2x-gmsl.yaml# | |
| 6 | +$schema: http://devicetree.org/meta-schemas/core.yaml# | |
| 7 | + | |
| 8 | +title: IMI D&D RDACM20 and RDACM21 Automotive Camera Platforms | |
| 9 | + | |
| 10 | +maintainers: | |
| 11 | + - Jacopo Mondi <jacopo+renesas@jmondi.org> | |
| 12 | + - Kieran Bingham <kieran.bingham+renesas@ideasonboard.com> | |
| 13 | + - Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> | |
| 14 | + - Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se> | |
| 15 | + | |
| 16 | +description: -| | |
| 17 | + The IMI D&D RDACM20 and RDACM21 are GMSL-compatible camera designed for | |
| 18 | + automotive applications. | |
| 19 | + | |
| 20 | + The RDACM20 camera module encloses a Maxim Integrated MAX9271 GMSL serializer, | |
| 21 | + coupled with an OV10635 image sensor and an embedded MCU. Both the MCU and | |
| 22 | + the image sensor are connected to the serializer local I2C bus and are | |
| 23 | + accessible by the host SoC by direct addressing. | |
| 24 | + | |
| 25 | + The RDACM21 camera module encloses the same serializer, coupled with an | |
| 26 | + OV10640 image sensor and an OV490 ISP. Only the OV490 ISP is interfaced to | |
| 27 | + the serializer local I2C bus while the image sensor is not accessible from | |
| 28 | + the host SoC. | |
| 29 | + | |
| 30 | + They both connect to a remote GMSL endpoint through a coaxial cable. | |
| 31 | + | |
| 32 | + IMI RDACM20 | |
| 33 | + +---------------+ +--------------------------------+ | |
| 34 | + | GMSL | <- Video Stream | <- Video--------\ | | |
| 35 | + | |< === GMSL Link ====== >|MAX9271<- I2C bus-> <-->OV10635 | | |
| 36 | + | de-serializer | <- I2C messages -> | \<-->MCU | | |
| 37 | + +---------------+ +--------------------------------+ | |
| 38 | + | |
| 39 | + IMI RDACM21 | |
| 40 | + +---------------+ +--------------------------------+ | |
| 41 | + | GMSL | <- Video Stream | <- Video--------\ | | |
| 42 | + | |< === GMSL Link ====== >|MAX9271<- I2C bus-> <-->OV490 | | |
| 43 | + | | <- I2C messages -> | | | | |
| 44 | + | de-serializer | | OV10640 <-------| | | |
| 45 | + +---------------+ +--------------------------------+ | |
| 46 | + | |
| 47 | + Both camera modules serialize video data generated by the embedded camera | |
| 48 | + sensor on the GMSL serial channel to a remote GMSL de-serializer. They also | |
| 49 | + receive and transmit I2C messages encapsulated and transmitted on the GMSL | |
| 50 | + bidirectional control channel. | |
| 51 | + | |
| 52 | + All I2C traffic received on the GMSL link not directed to the serializer is | |
| 53 | + propagated on the local I2C bus to the remote device there connected. All the | |
| 54 | + I2C traffic generated on the local I2C bus not directed to the serializer is | |
| 55 | + propagated to the remote de-serializer encapsulated in the GMSL control | |
| 56 | + channel. | |
| 57 | + | |
| 58 | + The RDACM20 and RDACM21 DT node should be a direct child of the GMSL | |
| 59 | + deserializer's I2C bus corresponding to the GMSL link that the camera is | |
| 60 | + attached to. | |
| 61 | + | |
| 62 | +properties: | |
| 63 | + '#address-cells': | |
| 64 | + const: 1 | |
| 65 | + | |
| 66 | + '#size-cells': | |
| 67 | + const: 0 | |
| 68 | + | |
| 69 | + compatible: | |
| 70 | + enum: | |
| 71 | + - imi,rdacm20 | |
| 72 | + - imi,rdacm21 | |
| 73 | + | |
| 74 | + reg: | |
| 75 | + description: -| | |
| 76 | + I2C device addresses, the first to be assigned to the serializer, the | |
| 77 | + following ones to be assigned to the remote devices. | |
| 78 | + | |
| 79 | + For RDACM20 the second entry of the property is assigned to the | |
| 80 | + OV10635 image sensor and the optional third one to the embedded MCU. | |
| 81 | + | |
| 82 | + For RDACM21 the second entry is assigned to the OV490 ISP and the optional | |
| 83 | + third one ignored. | |
| 84 | + | |
| 85 | + minItems: 2 | |
| 86 | + maxItems: 3 | |
| 87 | + | |
| 88 | + port: | |
| 89 | + type: object | |
| 90 | + additionalProperties: false | |
| 91 | + description: -| | |
| 92 | + Connection to the remote GMSL endpoint are modelled using the OF graph | |
| 93 | + bindings in accordance with the video interface bindings defined in | |
| 94 | + Documentation/devicetree/bindings/media/video-interfaces.txt. | |
| 95 | + | |
| 96 | + The device node contains a single "port" child node with a single | |
| 97 | + "endpoint" sub-device. | |
| 98 | + | |
| 99 | + properties: | |
| 100 | + endpoint: | |
| 101 | + type: object | |
| 102 | + additionalProperties: false | |
| 103 | + | |
| 104 | + properties: | |
| 105 | + remote-endpoint: | |
| 106 | + description: -| | |
| 107 | + phandle to the remote GMSL endpoint sub-node in the remote node | |
| 108 | + port. | |
| 109 | + maxItems: 1 | |
| 110 | + | |
| 111 | + required: | |
| 112 | + - remote-endpoint | |
| 113 | + | |
| 114 | + required: | |
| 115 | + - endpoint | |
| 116 | + | |
| 117 | +required: | |
| 118 | + - compatible | |
| 119 | + - reg | |
| 120 | + - port | |
| 121 | + | |
| 122 | +examples: | |
| 123 | + - | | |
| 124 | + i2c@e66d8000 { | |
| 125 | + #address-cells = <1>; | |
| 126 | + #size-cells = <0>; | |
| 127 | + | |
| 128 | + reg = <0 0xe66d8000>; | |
| 129 | + | |
| 130 | + camera@31 { | |
| 131 | + compatible = "imi,rdacm20"; | |
| 132 | + reg = <0x31>, <0x41>, <0x51>; | |
| 133 | + | |
| 134 | + port { | |
| 135 | + rdacm20_out0: endpoint { | |
| 136 | + remote-endpoint = <&max9286_in0>; | |
| 137 | + }; | |
| 138 | + }; | |
| 139 | + }; | |
| 140 | + }; | |
| 141 | + | |
| 142 | + - | | |
| 143 | + i2c@e66d8000 { | |
| 144 | + #address-cells = <1>; | |
| 145 | + #size-cells = <0>; | |
| 146 | + | |
| 147 | + reg = <0 0xe66d8000>; | |
| 148 | + | |
| 149 | + camera@31 { | |
| 150 | + compatible = "imi,rdacm21"; | |
| 151 | + reg = <0x31>, <0x41>; | |
| 152 | + | |
| 153 | + port { | |
| 154 | + rdacm21_out0: endpoint { | |
| 155 | + remote-endpoint = <&max9286_in0>; | |
| 156 | + }; | |
| 157 | + }; | |
| 158 | + }; | |
| 159 | + }; | |
| @@ -0,0 +1,366 @@ | ||
| 1 | +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) | |
| 2 | +# Copyright (C) 2019 Renesas Electronics Corp. | |
| 3 | +%YAML 1.2 | |
| 4 | +--- | |
| 5 | +$id: http://devicetree.org/schemas/media/i2c/maxim,max9286.yaml# | |
| 6 | +$schema: http://devicetree.org/meta-schemas/core.yaml# | |
| 7 | + | |
| 8 | +title: Maxim Integrated Quad GMSL Deserializer | |
| 9 | + | |
| 10 | +maintainers: | |
| 11 | + - Jacopo Mondi <jacopo+renesas@jmondi.org> | |
| 12 | + - Kieran Bingham <kieran.bingham+renesas@ideasonboard.com> | |
| 13 | + - Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> | |
| 14 | + - Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se> | |
| 15 | + | |
| 16 | +description: | | |
| 17 | + The MAX9286 deserializer receives video data on up to 4 Gigabit Multimedia | |
| 18 | + Serial Links (GMSL) and outputs them on a CSI-2 D-PHY port using up to 4 data | |
| 19 | + lanes. | |
| 20 | + | |
| 21 | + In addition to video data, the GMSL links carry a bidirectional control | |
| 22 | + channel that encapsulates I2C messages. The MAX9286 forwards all I2C traffic | |
| 23 | + not addressed to itself to the other side of the links, where a GMSL | |
| 24 | + serializer will output it on a local I2C bus. In the other direction all I2C | |
| 25 | + traffic received over GMSL by the MAX9286 is output on the local I2C bus. | |
| 26 | + | |
| 27 | +properties: | |
| 28 | + '#address-cells': | |
| 29 | + const: 1 | |
| 30 | + | |
| 31 | + '#size-cells': | |
| 32 | + const: 0 | |
| 33 | + | |
| 34 | + compatible: | |
| 35 | + const: maxim,max9286 | |
| 36 | + | |
| 37 | + reg: | |
| 38 | + description: I2C device address | |
| 39 | + maxItems: 1 | |
| 40 | + | |
| 41 | + poc-supply: | |
| 42 | + description: Regulator providing Power over Coax to the cameras | |
| 43 | + maxItems: 1 | |
| 44 | + | |
| 45 | + enable-gpios: | |
| 46 | + description: GPIO connected to the \#PWDN pin with inverted polarity | |
| 47 | + maxItems: 1 | |
| 48 | + | |
| 49 | + gpio-controller: true | |
| 50 | + | |
| 51 | + '#gpio-cells': | |
| 52 | + const: 2 | |
| 53 | + | |
| 54 | + ports: | |
| 55 | + type: object | |
| 56 | + description: | | |
| 57 | + The connections to the MAX9286 GMSL and its endpoint nodes are modelled | |
| 58 | + using the OF graph bindings in accordance with the video interface | |
| 59 | + bindings defined in | |
| 60 | + Documentation/devicetree/bindings/media/video-interfaces.txt. | |
| 61 | + | |
| 62 | + The following table lists the port number corresponding to each device | |
| 63 | + port. | |
| 64 | + | |
| 65 | + Port Description | |
| 66 | + ---------------------------------------- | |
| 67 | + Port 0 GMSL Input 0 | |
| 68 | + Port 1 GMSL Input 1 | |
| 69 | + Port 2 GMSL Input 2 | |
| 70 | + Port 3 GMSL Input 3 | |
| 71 | + Port 4 CSI-2 Output | |
| 72 | + | |
| 73 | + properties: | |
| 74 | + '#address-cells': | |
| 75 | + const: 1 | |
| 76 | + | |
| 77 | + '#size-cells': | |
| 78 | + const: 0 | |
| 79 | + | |
| 80 | + port@[0-3]: | |
| 81 | + type: object | |
| 82 | + properties: | |
| 83 | + reg: | |
| 84 | + enum: [ 0, 1, 2, 3 ] | |
| 85 | + | |
| 86 | + endpoint: | |
| 87 | + type: object | |
| 88 | + | |
| 89 | + properties: | |
| 90 | + remote-endpoint: | |
| 91 | + description: | | |
| 92 | + phandle to the remote GMSL source endpoint subnode in the | |
| 93 | + remote node port. | |
| 94 | + | |
| 95 | + required: | |
| 96 | + - remote-endpoint | |
| 97 | + | |
| 98 | + required: | |
| 99 | + - reg | |
| 100 | + - endpoint | |
| 101 | + | |
| 102 | + additionalProperties: false | |
| 103 | + | |
| 104 | + port@4: | |
| 105 | + type: object | |
| 106 | + properties: | |
| 107 | + reg: | |
| 108 | + const: 4 | |
| 109 | + | |
| 110 | + endpoint: | |
| 111 | + type: object | |
| 112 | + | |
| 113 | + properties: | |
| 114 | + remote-endpoint: | |
| 115 | + description: phandle to the remote CSI-2 sink endpoint. | |
| 116 | + | |
| 117 | + data-lanes: | |
| 118 | + description: array of physical CSI-2 data lane indexes. | |
| 119 | + | |
| 120 | + required: | |
| 121 | + - remote-endpoint | |
| 122 | + - data-lanes | |
| 123 | + | |
| 124 | + required: | |
| 125 | + - reg | |
| 126 | + - endpoint | |
| 127 | + | |
| 128 | + additionalProperties: false | |
| 129 | + | |
| 130 | + required: | |
| 131 | + - port@4 | |
| 132 | + | |
| 133 | + i2c-mux: | |
| 134 | + type: object | |
| 135 | + description: | | |
| 136 | + Each GMSL link is modelled as a child bus of an i2c bus | |
| 137 | + multiplexer/switch, in accordance with bindings described in | |
| 138 | + Documentation/devicetree/bindings/i2c/i2c-mux.txt. | |
| 139 | + | |
| 140 | + properties: | |
| 141 | + '#address-cells': | |
| 142 | + const: 1 | |
| 143 | + | |
| 144 | + '#size-cells': | |
| 145 | + const: 0 | |
| 146 | + | |
| 147 | + patternProperties: | |
| 148 | + "^i2c@[0-3]$": | |
| 149 | + type: object | |
| 150 | + description: | | |
| 151 | + Child node of the i2c bus multiplexer which represents a GMSL link. | |
| 152 | + Each serializer device on the GMSL link remote end is represented with | |
| 153 | + an i2c-mux child node. The MAX9286 chip supports up to 4 GMSL | |
| 154 | + channels. | |
| 155 | + | |
| 156 | + properties: | |
| 157 | + '#address-cells': | |
| 158 | + const: 1 | |
| 159 | + | |
| 160 | + '#size-cells': | |
| 161 | + const: 0 | |
| 162 | + | |
| 163 | + reg: | |
| 164 | + description: The index of the GMSL channel. | |
| 165 | + maxItems: 1 | |
| 166 | + | |
| 167 | + patternProperties: | |
| 168 | + "^camera@[a-f0-9]+$": | |
| 169 | + type: object | |
| 170 | + description: | | |
| 171 | + The remote camera device, composed by a GMSL serializer and a | |
| 172 | + connected video source. | |
| 173 | + | |
| 174 | + properties: | |
| 175 | + compatible: | |
| 176 | + description: The remote device compatible string. | |
| 177 | + | |
| 178 | + reg: | |
| 179 | + minItems: 2 | |
| 180 | + maxItems: 3 | |
| 181 | + description: | | |
| 182 | + The I2C addresses to be assigned to the remote devices through | |
| 183 | + address reprogramming. The number of entries depends on the | |
| 184 | + requirements of the currently connected remote device. | |
| 185 | + | |
| 186 | + port: | |
| 187 | + type: object | |
| 188 | + | |
| 189 | + properties: | |
| 190 | + endpoint: | |
| 191 | + type: object | |
| 192 | + | |
| 193 | + properties: | |
| 194 | + remote-endpoint: | |
| 195 | + description: phandle to the MAX9286 sink endpoint. | |
| 196 | + | |
| 197 | + required: | |
| 198 | + - remote-endpoint | |
| 199 | + | |
| 200 | + additionalProperties: false | |
| 201 | + | |
| 202 | + required: | |
| 203 | + - endpoint | |
| 204 | + | |
| 205 | + additionalProperties: false | |
| 206 | + | |
| 207 | + required: | |
| 208 | + - compatible | |
| 209 | + - reg | |
| 210 | + - port | |
| 211 | + | |
| 212 | + additionalProperties: false | |
| 213 | + | |
| 214 | + additionalProperties: false | |
| 215 | + | |
| 216 | + additionalProperties: false | |
| 217 | + | |
| 218 | +required: | |
| 219 | + - compatible | |
| 220 | + - reg | |
| 221 | + - ports | |
| 222 | + - i2c-mux | |
| 223 | + - gpio-controller | |
| 224 | + | |
| 225 | +additionalProperties: false | |
| 226 | + | |
| 227 | +examples: | |
| 228 | + - | | |
| 229 | + #include <dt-bindings/gpio/gpio.h> | |
| 230 | + | |
| 231 | + i2c@e66d8000 { | |
| 232 | + #address-cells = <1>; | |
| 233 | + #size-cells = <0>; | |
| 234 | + | |
| 235 | + reg = <0 0xe66d8000>; | |
| 236 | + | |
| 237 | + gmsl-deserializer@2c { | |
| 238 | + compatible = "maxim,max9286"; | |
| 239 | + reg = <0x2c>; | |
| 240 | + poc-supply = <&camera_poc_12v>; | |
| 241 | + enable-gpios = <&gpio 13 GPIO_ACTIVE_HIGH>; | |
| 242 | + | |
| 243 | + gpio-controller; | |
| 244 | + #gpio-cells = <2>; | |
| 245 | + | |
| 246 | + ports { | |
| 247 | + #address-cells = <1>; | |
| 248 | + #size-cells = <0>; | |
| 249 | + | |
| 250 | + port@0 { | |
| 251 | + reg = <0>; | |
| 252 | + | |
| 253 | + max9286_in0: endpoint { | |
| 254 | + remote-endpoint = <&rdacm20_out0>; | |
| 255 | + }; | |
| 256 | + }; | |
| 257 | + | |
| 258 | + port@1 { | |
| 259 | + reg = <1>; | |
| 260 | + | |
| 261 | + max9286_in1: endpoint { | |
| 262 | + remote-endpoint = <&rdacm20_out1>; | |
| 263 | + }; | |
| 264 | + }; | |
| 265 | + | |
| 266 | + port@2 { | |
| 267 | + reg = <2>; | |
| 268 | + | |
| 269 | + max9286_in2: endpoint { | |
| 270 | + remote-endpoint = <&rdacm20_out2>; | |
| 271 | + }; | |
| 272 | + }; | |
| 273 | + | |
| 274 | + port@3 { | |
| 275 | + reg = <3>; | |
| 276 | + | |
| 277 | + max9286_in3: endpoint { | |
| 278 | + remote-endpoint = <&rdacm20_out3>; | |
| 279 | + }; | |
| 280 | + }; | |
| 281 | + | |
| 282 | + port@4 { | |
| 283 | + reg = <4>; | |
| 284 | + | |
| 285 | + max9286_out: endpoint { | |
| 286 | + data-lanes = <1 2 3 4>; | |
| 287 | + remote-endpoint = <&csi40_in>; | |
| 288 | + }; | |
| 289 | + }; | |
| 290 | + }; | |
| 291 | + | |
| 292 | + i2c-mux { | |
| 293 | + #address-cells = <1>; | |
| 294 | + #size-cells = <0>; | |
| 295 | + | |
| 296 | + i2c@0 { | |
| 297 | + #address-cells = <1>; | |
| 298 | + #size-cells = <0>; | |
| 299 | + reg = <0>; | |
| 300 | + | |
| 301 | + camera@51 { | |
| 302 | + compatible = "imi,rdacm20"; | |
| 303 | + reg = <0x51>, <0x61>; | |
| 304 | + | |
| 305 | + port { | |
| 306 | + rdacm20_out0: endpoint { | |
| 307 | + remote-endpoint = <&max9286_in0>; | |
| 308 | + }; | |
| 309 | + }; | |
| 310 | + | |
| 311 | + }; | |
| 312 | + }; | |
| 313 | + | |
| 314 | + i2c@1 { | |
| 315 | + #address-cells = <1>; | |
| 316 | + #size-cells = <0>; | |
| 317 | + reg = <1>; | |
| 318 | + | |
| 319 | + camera@52 { | |
| 320 | + compatible = "imi,rdacm20"; | |
| 321 | + reg = <0x52>, <0x62>; | |
| 322 | + | |
| 323 | + port { | |
| 324 | + rdacm20_out1: endpoint { | |
| 325 | + remote-endpoint = <&max9286_in1>; | |
| 326 | + }; | |
| 327 | + }; | |
| 328 | + }; | |
| 329 | + }; | |
| 330 | + | |
| 331 | + i2c@2 { | |
| 332 | + #address-cells = <1>; | |
| 333 | + #size-cells = <0>; | |
| 334 | + reg = <2>; | |
| 335 | + | |
| 336 | + camera@53 { | |
| 337 | + compatible = "imi,rdacm20"; | |
| 338 | + reg = <0x53>, <0x63>; | |
| 339 | + | |
| 340 | + port { | |
| 341 | + rdacm20_out2: endpoint { | |
| 342 | + remote-endpoint = <&max9286_in2>; | |
| 343 | + }; | |
| 344 | + }; | |
| 345 | + }; | |
| 346 | + }; | |
| 347 | + | |
| 348 | + i2c@3 { | |
| 349 | + #address-cells = <1>; | |
| 350 | + #size-cells = <0>; | |
| 351 | + reg = <3>; | |
| 352 | + | |
| 353 | + camera@54 { | |
| 354 | + compatible = "imi,rdacm20"; | |
| 355 | + reg = <0x54>, <0x64>; | |
| 356 | + | |
| 357 | + port { | |
| 358 | + rdacm20_out3: endpoint { | |
| 359 | + remote-endpoint = <&max9286_in3>; | |
| 360 | + }; | |
| 361 | + }; | |
| 362 | + }; | |
| 363 | + }; | |
| 364 | + }; | |
| 365 | + }; | |
| 366 | + }; | |
| @@ -1,34 +0,0 @@ | ||
| 1 | −Renesas R-Car Frame Compression Processor (FCP) | |
| 2 | −----------------------------------------------- | |
| 3 | − | |
| 4 | −The FCP is a companion module of video processing modules in the Renesas R-Car | |
| 5 | −Gen3 and RZ/G2 SoCs. It provides data compression and decompression, data | |
| 6 | −caching, and conversion of AXI transactions in order to reduce the memory | |
| 7 | −bandwidth. | |
| 8 | − | |
| 9 | −There are three types of FCP: FCP for Codec (FCPC), FCP for VSP (FCPV) and FCP | |
| 10 | −for FDP (FCPF). Their configuration and behaviour depend on the module they | |
| 11 | −are paired with. These DT bindings currently support the FCPV and FCPF. | |
| 12 | − | |
| 13 | − - compatible: Must be one or more of the following | |
| 14 | − | |
| 15 | − - "renesas,fcpv" for generic compatible 'FCP for VSP' | |
| 16 | − - "renesas,fcpf" for generic compatible 'FCP for FDP' | |
| 17 | − | |
| 18 | − - reg: the register base and size for the device registers | |
| 19 | − - clocks: Reference to the functional clock | |
| 20 | − | |
| 21 | −Optional properties: | |
| 22 | − - power-domains : power-domain property defined with a power domain specifier | |
| 23 | − to respective power domain. | |
| 24 | − | |
| 25 | − | |
| 26 | −Device node example | |
| 27 | −------------------- | |
| 28 | − | |
| 29 | − fcpvd1: fcp@fea2f000 { | |
| 30 | − compatible = "renesas,fcpv"; | |
| 31 | − reg = <0 0xfea2f000 0 0x200>; | |
| 32 | − clocks = <&cpg CPG_MOD 602>; | |
| 33 | − power-domains = <&sysc R8A7795_PD_A3VP>; | |
| 34 | − }; | |
| @@ -0,0 +1,66 @@ | ||
| 1 | +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) | |
| 2 | +%YAML 1.2 | |
| 3 | +--- | |
| 4 | +$id: http://devicetree.org/schemas/media/renesas,fcp.yaml# | |
| 5 | +$schema: http://devicetree.org/meta-schemas/core.yaml# | |
| 6 | + | |
| 7 | +title: Renesas R-Car Frame Compression Processor (FCP) | |
| 8 | + | |
| 9 | +maintainers: | |
| 10 | + - Laurent Pinchart <laurent.pinchart@ideasonboard.com> | |
| 11 | + | |
| 12 | +description: | | |
| 13 | + The FCP is a companion module of video processing modules in the Renesas | |
| 14 | + R-Car Gen3 and RZ/G2 SoCs. It provides data compression and decompression, | |
| 15 | + data caching, and conversion of AXI transactions in order to reduce the | |
| 16 | + memory bandwidth. | |
| 17 | + | |
| 18 | + There are three types of FCP: FCP for Codec (FCPC), FCP for VSP (FCPV) and | |
| 19 | + FCP for FDP (FCPF). Their configuration and behaviour depend on the module | |
| 20 | + they are paired with. These DT bindings currently support the FCPV and FCPF. | |
| 21 | + | |
| 22 | +properties: | |
| 23 | + compatible: | |
| 24 | + enum: | |
| 25 | + - renesas,fcpv # FCP for VSP | |
| 26 | + - renesas,fcpf # FCP for FDP | |
| 27 | + | |
| 28 | + reg: | |
| 29 | + maxItems: 1 | |
| 30 | + | |
| 31 | + clocks: | |
| 32 | + maxItems: 1 | |
| 33 | + | |
| 34 | + iommus: | |
| 35 | + maxItems: 1 | |
| 36 | + | |
| 37 | + power-domains: | |
| 38 | + maxItems: 1 | |
| 39 | + | |
| 40 | + resets: | |
| 41 | + maxItems: 1 | |
| 42 | + | |
| 43 | +required: | |
| 44 | + - compatible | |
| 45 | + - reg | |
| 46 | + - clocks | |
| 47 | + - power-domains | |
| 48 | + - resets | |
| 49 | + | |
| 50 | +additionalProperties: false | |
| 51 | + | |
| 52 | +examples: | |
| 53 | + # R8A7795 (R-Car H3) FCP for VSP-D1 | |
| 54 | + - | | |
| 55 | + #include <dt-bindings/clock/renesas-cpg-mssr.h> | |
| 56 | + #include <dt-bindings/power/r8a7795-sysc.h> | |
| 57 | + | |
| 58 | + fcp@fea2f000 { | |
| 59 | + compatible = "renesas,fcpv"; | |
| 60 | + reg = <0xfea2f000 0x200>; | |
| 61 | + clocks = <&cpg CPG_MOD 602>; | |
| 62 | + power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; | |
| 63 | + resets = <&cpg 602>; | |
| 64 | + iommus = <&ipmmu_vi0 9>; | |
| 65 | + }; | |
| 66 | +... | |
| @@ -1,37 +0,0 @@ | ||
| 1 | −Renesas R-Car Fine Display Processor (FDP1) | |
| 2 | −------------------------------------------- | |
| 3 | − | |
| 4 | −The FDP1 is a de-interlacing module which converts interlaced video to | |
| 5 | −progressive video. It is capable of performing pixel format conversion between | |
| 6 | −YCbCr/YUV formats and RGB formats. Only YCbCr/YUV formats are supported as | |
| 7 | −an input to the module. | |
| 8 | − | |
| 9 | −Required properties: | |
| 10 | − | |
| 11 | − - compatible: must be "renesas,fdp1" | |
| 12 | − - reg: the register base and size for the device registers | |
| 13 | − - interrupts : interrupt specifier for the FDP1 instance | |
| 14 | − - clocks: reference to the functional clock | |
| 15 | − | |
| 16 | −Optional properties: | |
| 17 | − | |
| 18 | − - power-domains: reference to the power domain that the FDP1 belongs to, if | |
| 19 | − any. | |
| 20 | − - renesas,fcp: a phandle referencing the FCP that handles memory accesses | |
| 21 | − for the FDP1. Not needed on Gen2, mandatory on Gen3. | |
| 22 | − | |
| 23 | −Please refer to the binding documentation for the clock and/or power domain | |
| 24 | −providers for more details. | |
| 25 | − | |
| 26 | − | |
| 27 | −Device node example | |
| 28 | −------------------- | |
| 29 | − | |
| 30 | − fdp1@fe940000 { | |
| 31 | − compatible = "renesas,fdp1"; | |
| 32 | − reg = <0 0xfe940000 0 0x2400>; | |
| 33 | − interrupts = <GIC_SPI 262 IRQ_TYPE_LEVEL_HIGH>; | |
| 34 | − clocks = <&cpg CPG_MOD 119>; | |
| 35 | − power-domains = <&sysc R8A7795_PD_A3VP>; | |
| 36 | − renesas,fcp = <&fcpf0>; | |
| 37 | − }; | |
| @@ -0,0 +1,69 @@ | ||
| 1 | +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) | |
| 2 | +%YAML 1.2 | |
| 3 | +--- | |
| 4 | +$id: http://devicetree.org/schemas/media/renesas,fdp1.yaml# | |
| 5 | +$schema: http://devicetree.org/meta-schemas/core.yaml# | |
| 6 | + | |
| 7 | +title: Renesas R-Car Fine Display Processor (FDP1) | |
| 8 | + | |
| 9 | +maintainers: | |
| 10 | + - Laurent Pinchart <laurent.pinchart@ideasonboard.com> | |
| 11 | + | |
| 12 | +description: | |
| 13 | + The FDP1 is a de-interlacing module which converts interlaced video to | |
| 14 | + progressive video. It is capable of performing pixel format conversion | |
| 15 | + between YCbCr/YUV formats and RGB formats. Only YCbCr/YUV formats are | |
| 16 | + supported as an input to the module. | |
| 17 | + | |
| 18 | +properties: | |
| 19 | + compatible: | |
| 20 | + enum: | |
| 21 | + - renesas,fdp1 | |
| 22 | + | |
| 23 | + reg: | |
| 24 | + maxItems: 1 | |
| 25 | + | |
| 26 | + interrupts: | |
| 27 | + maxItems: 1 | |
| 28 | + | |
| 29 | + clocks: | |
| 30 | + maxItems: 1 | |
| 31 | + | |
| 32 | + power-domains: | |
| 33 | + maxItems: 1 | |
| 34 | + | |
| 35 | + resets: | |
| 36 | + maxItems: 1 | |
| 37 | + | |
| 38 | + renesas,fcp: | |
| 39 | + $ref: /schemas/types.yaml#/definitions/phandle | |
| 40 | + description: | |
| 41 | + A phandle referencing the FCP that handles memory accesses for the FDP1. | |
| 42 | + Not allowed on R-Car Gen2, mandatory on R-Car Gen3. | |
| 43 | + | |
| 44 | +required: | |
| 45 | + - compatible | |
| 46 | + - reg | |
| 47 | + - interrupts | |
| 48 | + - clocks | |
| 49 | + - power-domains | |
| 50 | + - resets | |
| 51 | + | |
| 52 | +additionalProperties: false | |
| 53 | + | |
| 54 | +examples: | |
| 55 | + - | | |
| 56 | + #include <dt-bindings/clock/renesas-cpg-mssr.h> | |
| 57 | + #include <dt-bindings/interrupt-controller/arm-gic.h> | |
| 58 | + #include <dt-bindings/power/r8a7795-sysc.h> | |
| 59 | + | |
| 60 | + fdp1@fe940000 { | |
| 61 | + compatible = "renesas,fdp1"; | |
| 62 | + reg = <0xfe940000 0x2400>; | |
| 63 | + interrupts = <GIC_SPI 262 IRQ_TYPE_LEVEL_HIGH>; | |
| 64 | + clocks = <&cpg CPG_MOD 119>; | |
| 65 | + power-domains = <&sysc R8A7795_PD_A3VP>; | |
| 66 | + resets = <&cpg 119>; | |
| 67 | + renesas,fcp = <&fcpf0>; | |
| 68 | + }; | |
| 69 | +... | |
| @@ -1,30 +0,0 @@ | ||
| 1 | −* Renesas VSP Video Processing Engine | |
| 2 | − | |
| 3 | −The VSP is a video processing engine that supports up-/down-scaling, alpha | |
| 4 | −blending, color space conversion and various other image processing features. | |
| 5 | −It can be found in the Renesas R-Car Gen2, R-Car Gen3, RZ/G1, and RZ/G2 SoCs. | |
| 6 | − | |
| 7 | −Required properties: | |
| 8 | − | |
| 9 | − - compatible: Must contain one of the following values | |
| 10 | − - "renesas,vsp1" for the R-Car Gen2 and RZ/G1 VSP1 | |
| 11 | − - "renesas,vsp2" for the R-Car Gen3 and RZ/G2 VSP2 | |
| 12 | − | |
| 13 | − - reg: Base address and length of the registers block for the VSP. | |
| 14 | − - interrupts: VSP interrupt specifier. | |
| 15 | − - clocks: A phandle + clock-specifier pair for the VSP functional clock. | |
| 16 | − | |
| 17 | −Optional properties: | |
| 18 | − | |
| 19 | − - renesas,fcp: A phandle referencing the FCP that handles memory accesses | |
| 20 | − for the VSP. Not needed on Gen2, mandatory on Gen3. | |
| 21 | − | |
| 22 | − | |
| 23 | −Example: R8A7790 (R-Car H2) VSP1-S node | |
| 24 | − | |
| 25 | − vsp@fe928000 { | |
| 26 | − compatible = "renesas,vsp1"; | |
| 27 | − reg = <0 0xfe928000 0 0x8000>; | |
| 28 | − interrupts = <0 267 IRQ_TYPE_LEVEL_HIGH>; | |
| 29 | − clocks = <&mstp1_clks R8A7790_CLK_VSP1_S>; | |
| 30 | − }; | |
| @@ -0,0 +1,97 @@ | ||
| 1 | +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) | |
| 2 | +%YAML 1.2 | |
| 3 | +--- | |
| 4 | +$id: http://devicetree.org/schemas/media/renesas,vsp1.yaml# | |
| 5 | +$schema: http://devicetree.org/meta-schemas/core.yaml# | |
| 6 | + | |
| 7 | +title: Renesas VSP Video Processing Engine | |
| 8 | + | |
| 9 | +maintainers: | |
| 10 | + - Laurent Pinchart <laurent.pinchart@ideasonboard.com> | |
| 11 | + | |
| 12 | +description: | |
| 13 | + The VSP is a video processing engine that supports up-/down-scaling, alpha | |
| 14 | + blending, color space conversion and various other image processing features. | |
| 15 | + It can be found in the Renesas R-Car Gen2, R-Car Gen3, RZ/G1, and RZ/G2 SoCs. | |
| 16 | + | |
| 17 | +properties: | |
| 18 | + compatible: | |
| 19 | + enum: | |
| 20 | + - renesas,vsp1 # R-Car Gen2 and RZ/G1 | |
| 21 | + - renesas,vsp2 # R-Car Gen3 and RZ/G2 | |
| 22 | + | |
| 23 | + reg: | |
| 24 | + maxItems: 1 | |
| 25 | + | |
| 26 | + interrupts: | |
| 27 | + maxItems: 1 | |
| 28 | + | |
| 29 | + clocks: | |
| 30 | + maxItems: 1 | |
| 31 | + | |
| 32 | + power-domains: | |
| 33 | + maxItems: 1 | |
| 34 | + | |
| 35 | + resets: | |
| 36 | + maxItems: 1 | |
| 37 | + | |
| 38 | + renesas,fcp: | |
| 39 | + $ref: /schemas/types.yaml#/definitions/phandle | |
| 40 | + description: | |
| 41 | + A phandle referencing the FCP that handles memory accesses for the VSP. | |
| 42 | + | |
| 43 | +required: | |
| 44 | + - compatible | |
| 45 | + - reg | |
| 46 | + - interrupts | |
| 47 | + - clocks | |
| 48 | + - power-domains | |
| 49 | + - resets | |
| 50 | + | |
| 51 | +additionalProperties: false | |
| 52 | + | |
| 53 | +if: | |
| 54 | + properties: | |
| 55 | + compatible: | |
| 56 | + items: | |
| 57 | + - const: renesas,vsp1 | |
| 58 | +then: | |
| 59 | + properties: | |
| 60 | + renesas,fcp: false | |
| 61 | +else: | |
| 62 | + required: | |
| 63 | + - renesas,fcp | |
| 64 | + | |
| 65 | +examples: | |
| 66 | + # R8A7790 (R-Car H2) VSP1-S | |
| 67 | + - | | |
| 68 | + #include <dt-bindings/clock/renesas-cpg-mssr.h> | |
| 69 | + #include <dt-bindings/interrupt-controller/arm-gic.h> | |
| 70 | + #include <dt-bindings/power/r8a7790-sysc.h> | |
| 71 | + | |
| 72 | + vsp@fe928000 { | |
| 73 | + compatible = "renesas,vsp1"; | |
| 74 | + reg = <0xfe928000 0x8000>; | |
| 75 | + interrupts = <GIC_SPI 267 IRQ_TYPE_LEVEL_HIGH>; | |
| 76 | + clocks = <&cpg CPG_MOD 131>; | |
| 77 | + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; | |
| 78 | + resets = <&cpg 131>; | |
| 79 | + }; | |
| 80 | + | |
| 81 | + # R8A77951 (R-Car H3) VSP2-BC | |
| 82 | + - | | |
| 83 | + #include <dt-bindings/clock/renesas-cpg-mssr.h> | |
| 84 | + #include <dt-bindings/interrupt-controller/arm-gic.h> | |
| 85 | + #include <dt-bindings/power/r8a7795-sysc.h> | |
| 86 | + | |
| 87 | + vsp@fe920000 { | |
| 88 | + compatible = "renesas,vsp2"; | |
| 89 | + reg = <0xfe920000 0x8000>; | |
| 90 | + interrupts = <GIC_SPI 465 IRQ_TYPE_LEVEL_HIGH>; | |
| 91 | + clocks = <&cpg CPG_MOD 624>; | |
| 92 | + power-domains = <&sysc R8A7795_PD_A3VP>; | |
| 93 | + resets = <&cpg 624>; | |
| 94 | + | |
| 95 | + renesas,fcp = <&fcpvb1>; | |
| 96 | + }; | |
| 97 | +... | |
| @@ -0,0 +1,237 @@ | ||
| 1 | +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) | |
| 2 | +%YAML 1.2 | |
| 3 | +--- | |
| 4 | +$id: http://devicetree.org/schemas/media/xilinx/xlnx,csi2rxss.yaml# | |
| 5 | +$schema: http://devicetree.org/meta-schemas/core.yaml# | |
| 6 | + | |
| 7 | +title: Xilinx MIPI CSI-2 Receiver Subsystem | |
| 8 | + | |
| 9 | +maintainers: | |
| 10 | + - Vishal Sagar <vishal.sagar@xilinx.com> | |
| 11 | + | |
| 12 | +description: | | |
| 13 | + The Xilinx MIPI CSI-2 Receiver Subsystem is used to capture MIPI CSI-2 | |
| 14 | + traffic from compliant camera sensors and send the output as AXI4 Stream | |
| 15 | + video data for image processing. | |
| 16 | + The subsystem consists of a MIPI D-PHY in slave mode which captures the | |
| 17 | + data packets. This is passed along the MIPI CSI-2 Rx IP which extracts the | |
| 18 | + packet data. The optional Video Format Bridge (VFB) converts this data to | |
| 19 | + AXI4 Stream video data. | |
| 20 | + For more details, please refer to PG232 Xilinx MIPI CSI-2 Receiver Subsystem. | |
| 21 | + Please note that this bindings includes only the MIPI CSI-2 Rx controller | |
| 22 | + and Video Format Bridge and not D-PHY. | |
| 23 | + | |
| 24 | +properties: | |
| 25 | + compatible: | |
| 26 | + items: | |
| 27 | + - enum: | |
| 28 | + - xlnx,mipi-csi2-rx-subsystem-5.0 | |
| 29 | + | |
| 30 | + reg: | |
| 31 | + maxItems: 1 | |
| 32 | + | |
| 33 | + interrupts: | |
| 34 | + maxItems: 1 | |
| 35 | + | |
| 36 | + clocks: | |
| 37 | + description: List of clock specifiers | |
| 38 | + items: | |
| 39 | + - description: AXI Lite clock | |
| 40 | + - description: Video clock | |
| 41 | + | |
| 42 | + clock-names: | |
| 43 | + items: | |
| 44 | + - const: lite_aclk | |
| 45 | + - const: video_aclk | |
| 46 | + | |
| 47 | + xlnx,csi-pxl-format: | |
| 48 | + description: | | |
| 49 | + This denotes the CSI Data type selected in hw design. | |
| 50 | + Packets other than this data type (except for RAW8 and | |
| 51 | + User defined data types) will be filtered out. | |
| 52 | + Possible values are as below - | |
| 53 | + 0x1e - YUV4228B | |
| 54 | + 0x1f - YUV42210B | |
| 55 | + 0x20 - RGB444 | |
| 56 | + 0x21 - RGB555 | |
| 57 | + 0x22 - RGB565 | |
| 58 | + 0x23 - RGB666 | |
| 59 | + 0x24 - RGB888 | |
| 60 | + 0x28 - RAW6 | |
| 61 | + 0x29 - RAW7 | |
| 62 | + 0x2a - RAW8 | |
| 63 | + 0x2b - RAW10 | |
| 64 | + 0x2c - RAW12 | |
| 65 | + 0x2d - RAW14 | |
| 66 | + 0x2e - RAW16 | |
| 67 | + 0x2f - RAW20 | |
| 68 | + allOf: | |
| 69 | + - $ref: /schemas/types.yaml#/definitions/uint32 | |
| 70 | + - anyOf: | |
| 71 | + - minimum: 0x1e | |
| 72 | + - maximum: 0x24 | |
| 73 | + - minimum: 0x28 | |
| 74 | + - maximum: 0x2f | |
| 75 | + | |
| 76 | + xlnx,vfb: | |
| 77 | + type: boolean | |
| 78 | + description: Present when Video Format Bridge is enabled in IP configuration | |
| 79 | + | |
| 80 | + xlnx,en-csi-v2-0: | |
| 81 | + type: boolean | |
| 82 | + description: Present if CSI v2 is enabled in IP configuration. | |
| 83 | + | |
| 84 | + xlnx,en-vcx: | |
| 85 | + type: boolean | |
| 86 | + description: | | |
| 87 | + When present, there are maximum 16 virtual channels, else only 4. | |
| 88 | + | |
| 89 | + xlnx,en-active-lanes: | |
| 90 | + type: boolean | |
| 91 | + description: | | |
| 92 | + Present if the number of active lanes can be re-configured at | |
| 93 | + runtime in the Protocol Configuration Register. Otherwise all lanes, | |
| 94 | + as set in IP configuration, are always active. | |
| 95 | + | |
| 96 | + video-reset-gpios: | |
| 97 | + description: Optional specifier for a GPIO that asserts video_aresetn. | |
| 98 | + maxItems: 1 | |
| 99 | + | |
| 100 | + ports: | |
| 101 | + type: object | |
| 102 | + | |
| 103 | + properties: | |
| 104 | + port@0: | |
| 105 | + type: object | |
| 106 | + description: | | |
| 107 | + Input / sink port node, single endpoint describing the | |
| 108 | + CSI-2 transmitter. | |
| 109 | + | |
| 110 | + properties: | |
| 111 | + reg: | |
| 112 | + const: 0 | |
| 113 | + | |
| 114 | + endpoint: | |
| 115 | + type: object | |
| 116 | + | |
| 117 | + properties: | |
| 118 | + | |
| 119 | + data-lanes: | |
| 120 | + description: | | |
| 121 | + This is required only in the sink port 0 endpoint which | |
| 122 | + connects to MIPI CSI-2 source like sensor. | |
| 123 | + The possible values are - | |
| 124 | + 1 - For 1 lane enabled in IP. | |
| 125 | + 1 2 - For 2 lanes enabled in IP. | |
| 126 | + 1 2 3 - For 3 lanes enabled in IP. | |
| 127 | + 1 2 3 4 - For 4 lanes enabled in IP. | |
| 128 | + items: | |
| 129 | + - const: 1 | |
| 130 | + - const: 2 | |
| 131 | + - const: 3 | |
| 132 | + - const: 4 | |
| 133 | + | |
| 134 | + remote-endpoint: true | |
| 135 | + | |
| 136 | + required: | |
| 137 | + - data-lanes | |
| 138 | + - remote-endpoint | |
| 139 | + | |
| 140 | + additionalProperties: false | |
| 141 | + | |
| 142 | + additionalProperties: false | |
| 143 | + | |
| 144 | + port@1: | |
| 145 | + type: object | |
| 146 | + description: | | |
| 147 | + Output / source port node, endpoint describing modules | |
| 148 | + connected the CSI-2 receiver. | |
| 149 | + | |
| 150 | + properties: | |
| 151 | + | |
| 152 | + reg: | |
| 153 | + const: 1 | |
| 154 | + | |
| 155 | + endpoint: | |
| 156 | + type: object | |
| 157 | + | |
| 158 | + properties: | |
| 159 | + | |
| 160 | + remote-endpoint: true | |
| 161 | + | |
| 162 | + required: | |
| 163 | + - remote-endpoint | |
| 164 | + | |
| 165 | + additionalProperties: false | |
| 166 | + | |
| 167 | + additionalProperties: false | |
| 168 | + | |
| 169 | +required: | |
| 170 | + - compatible | |
| 171 | + - reg | |
| 172 | + - interrupts | |
| 173 | + - clocks | |
| 174 | + - clock-names | |
| 175 | + - ports | |
| 176 | + | |
| 177 | +allOf: | |
| 178 | + - if: | |
| 179 | + required: | |
| 180 | + - xlnx,vfb | |
| 181 | + then: | |
| 182 | + required: | |
| 183 | + - xlnx,csi-pxl-format | |
| 184 | + else: | |
| 185 | + properties: | |
| 186 | + xlnx,csi-pxl-format: false | |
| 187 | + | |
| 188 | + - if: | |
| 189 | + not: | |
| 190 | + required: | |
| 191 | + - xlnx,en-csi-v2-0 | |
| 192 | + then: | |
| 193 | + properties: | |
| 194 | + xlnx,en-vcx: false | |
| 195 | + | |
| 196 | +additionalProperties: false | |
| 197 | + | |
| 198 | +examples: | |
| 199 | + - | | |
| 200 | + #include <dt-bindings/gpio/gpio.h> | |
| 201 | + xcsi2rxss_1: csi2rx@a0020000 { | |
| 202 | + compatible = "xlnx,mipi-csi2-rx-subsystem-5.0"; | |
| 203 | + reg = <0xa0020000 0x10000>; | |
| 204 | + interrupt-parent = <&gic>; | |
| 205 | + interrupts = <0 95 4>; | |
| 206 | + xlnx,csi-pxl-format = <0x2a>; | |
| 207 | + xlnx,vfb; | |
| 208 | + xlnx,en-active-lanes; | |
| 209 | + xlnx,en-csi-v2-0; | |
| 210 | + xlnx,en-vcx; | |
| 211 | + clock-names = "lite_aclk", "video_aclk"; | |
| 212 | + clocks = <&misc_clk_0>, <&misc_clk_1>; | |
| 213 | + video-reset-gpios = <&gpio 86 GPIO_ACTIVE_LOW>; | |
| 214 | + | |
| 215 | + ports { | |
| 216 | + #address-cells = <1>; | |
| 217 | + #size-cells = <0>; | |
| 218 | + | |
| 219 | + port@0 { | |
| 220 | + /* Sink port */ | |
| 221 | + reg = <0>; | |
| 222 | + csiss_in: endpoint { | |
| 223 | + data-lanes = <1 2 3 4>; | |
| 224 | + /* MIPI CSI-2 Camera handle */ | |
| 225 | + remote-endpoint = <&camera_out>; | |
| 226 | + }; | |
| 227 | + }; | |
| 228 | + port@1 { | |
| 229 | + /* Source port */ | |
| 230 | + reg = <1>; | |
| 231 | + csiss_out: endpoint { | |
| 232 | + remote-endpoint = <&vproc_in>; | |
| 233 | + }; | |
| 234 | + }; | |
| 235 | + }; | |
| 236 | + }; | |
| 237 | +... | |
| @@ -473,6 +473,8 @@ patternProperties: · 472 unmodified lines | ||
| 473 | 473 | description: ILI Technology Corporation (ILITEK) |
| 474 | 474 | "^img,.*": |
| 475 | 475 | description: Imagination Technologies Ltd. |
| 476 | + "^imi,.*": | |
| 477 | + description: Integrated Micro-Electronics Inc. | |
| 476 | 478 | "^incircuit,.*": |
| 477 | 479 | description: In-Circuit GmbH |
| 478 | 480 | "^inet-tek,.*": |
| @@ -20,7 +20,7 @@ last known snapshot and evolved the driver to the state it is in · 19 unmodified lines | ||
| 20 | 20 | here. |
| 21 | 21 | |
| 22 | 22 | More information on this driver can be found at: |
| 23 | −http://www.isely.net/pvrusb2.html | |
| 23 | +https://www.isely.net/pvrusb2.html | |
| 24 | 24 | |
| 25 | 25 | |
| 26 | 26 | This driver has a strong separation of layers. They are very |
| @@ -18,7 +18,7 @@ These differ mainly by the bandswitch byte. · 17 unmodified lines | ||
| 18 | 18 | Tuner Manufacturers |
| 19 | 19 | ------------------- |
| 20 | 20 | |
| 21 | −- SAMSUNG Tuner identification: (e.g. TCPM9091PD27) | |
| 21 | +- Samsung Tuner identification: (e.g. TCPM9091PD27) | |
| 22 | 22 | |
| 23 | 23 | .. code-block:: none |
| 24 | 24 | |
| @@ -57,6 +57,9 @@ returns the information to the application. The ioctl never fails. · 56 unmodified lines | ||
| 57 | 57 | - ``name[32]`` |
| 58 | 58 | - The name of this CEC adapter. The combination ``driver`` and |
| 59 | 59 | ``name`` must be unique. |
| 60 | + * - __u32 | |
| 61 | + - ``available_log_addrs`` | |
| 62 | + - The maximum number of logical addresses that can be configured. | |
| 60 | 63 | * - __u32 |
| 61 | 64 | - ``capabilities`` |
| 62 | 65 | - The capabilities of the CEC adapter, see |
| @@ -34,8 +34,7 @@ Arguments · 33 unmodified lines | ||
| 34 | 34 | File descriptor returned by :ref:`open() <frontend_f_open>`. |
| 35 | 35 | |
| 36 | 36 | ``argp`` |
| 37 | − pointer to struct struct | |
| 38 | − :c:type:`dvb_frontend_info` | |
| 37 | + pointer to struct :c:type:`dvb_frontend_info` | |
| 39 | 38 | |
| 40 | 39 | |
| 41 | 40 | Description |
| @@ -23,8 +23,8 @@ argument to the :ref:`VIDIOC_QUERYBUF`, · 22 unmodified lines | ||
| 23 | 23 | :ref:`VIDIOC_QBUF <VIDIOC_QBUF>` and |
| 24 | 24 | :ref:`VIDIOC_DQBUF <VIDIOC_QBUF>` ioctl. In the multi-planar API, |
| 25 | 25 | some plane-specific members of struct :c:type:`v4l2_buffer`, |
| 26 | −such as pointers and sizes for each plane, are stored in struct | |
| 27 | −struct :c:type:`v4l2_plane` instead. In that case, struct | |
| 26 | +such as pointers and sizes for each plane, are stored in | |
| 27 | +struct :c:type:`v4l2_plane` instead. In that case, | |
| 28 | 28 | struct :c:type:`v4l2_buffer` contains an array of plane structures. |
| 29 | 29 | |
| 30 | 30 | Dequeued video buffers come with timestamps. The driver decides at which |
| @@ -577,7 +577,10 @@ Buffer Flags · 546 unmodified lines | ||
| 577 | 577 | applications shall use this flag if the data captured in the |
| 578 | 578 | buffer is not going to be touched by the CPU, instead the buffer |
| 579 | 579 | will, probably, be passed on to a DMA-capable hardware unit for |
| 580 | − further processing or output. | |
| 580 | + further processing or output. This flag is ignored unless the | |
| 581 | + queue is used for :ref:`memory mapping <mmap>` streaming I/O and | |
| 582 | + reports :ref:`V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS | |
| 583 | + <V4L2-BUF-CAP-SUPPORTS-MMAP-CACHE-HINTS>` capability. | |
| 581 | 584 | * .. _`V4L2-BUF-FLAG-NO-CACHE-CLEAN`: |
| 582 | 585 | |
| 583 | 586 | - ``V4L2_BUF_FLAG_NO_CACHE_CLEAN`` |
| @@ -585,7 +588,10 @@ Buffer Flags · 1 unmodified line | ||
| 585 | 588 | - Caches do not have to be cleaned for this buffer. Typically |
| 586 | 589 | applications shall use this flag for output buffers if the data in |
| 587 | 590 | this buffer has not been created by the CPU but by some |
| 588 | − DMA-capable unit, in which case caches have not been used. | |
| 591 | + DMA-capable unit, in which case caches have not been used. This flag | |
| 592 | + is ignored unless the queue is used for :ref:`memory mapping <mmap>` | |
| 593 | + streaming I/O and reports :ref:`V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS | |
| 594 | + <V4L2-BUF-CAP-SUPPORTS-MMAP-CACHE-HINTS>` capability. | |
| 589 | 595 | * .. _`V4L2-BUF-FLAG-M2M-HOLD-CAPTURE-BUF`: |
| 590 | 596 | |
| 591 | 597 | - ``V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF`` |
| @@ -681,6 +687,36 @@ Buffer Flags · 89 unmodified lines | ||
| 681 | 687 | |
| 682 | 688 | \normalsize |
| 683 | 689 | |
| 690 | +.. _memory-flags: | |
| 691 | + | |
| 692 | +Memory Consistency Flags | |
| 693 | +======================== | |
| 694 | + | |
| 695 | +.. tabularcolumns:: |p{7.0cm}|p{2.2cm}|p{8.3cm}| | |
| 696 | + | |
| 697 | +.. cssclass:: longtable | |
| 698 | + | |
| 699 | +.. flat-table:: | |
| 700 | + :header-rows: 0 | |
| 701 | + :stub-columns: 0 | |
| 702 | + :widths: 3 1 4 | |
| 703 | + | |
| 704 | + * .. _`V4L2-FLAG-MEMORY-NON-CONSISTENT`: | |
| 705 | + | |
| 706 | + - ``V4L2_FLAG_MEMORY_NON_CONSISTENT`` | |
| 707 | + - 0x00000001 | |
| 708 | + - A buffer is allocated either in consistent (it will be automatically | |
| 709 | + coherent between the CPU and the bus) or non-consistent memory. The | |
| 710 | + latter can provide performance gains, for instance the CPU cache | |
| 711 | + sync/flush operations can be avoided if the buffer is accessed by the | |
| 712 | + corresponding device only and the CPU does not read/write to/from that | |
| 713 | + buffer. However, this requires extra care from the driver -- it must | |
| 714 | + guarantee memory consistency by issuing a cache flush/sync when | |
| 715 | + consistency is needed. If this flag is set V4L2 will attempt to | |
| 716 | + allocate the buffer in non-consistent memory. The flag takes effect | |
| 717 | + only if the buffer is used for :ref:`memory mapping <mmap>` I/O and the | |
| 718 | + queue reports the :ref:`V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS | |
| 719 | + <V4L2-BUF-CAP-SUPPORTS-MMAP-CACHE-HINTS>` capability. | |
| 684 | 720 | |
| 685 | 721 | .. c:type:: v4l2_memory |
| 686 | 722 | |
| @@ -767,8 +767,8 @@ scaled to [-128…128] and then clipped to [-128…127]. · 766 unmodified lines | ||
| 767 | 767 | information. So if something other than sRGB is used, then the driver |
| 768 | 768 | will have to set that information explicitly. Effectively |
| 769 | 769 | ``V4L2_COLORSPACE_JPEG`` can be considered to be an abbreviation for |
| 770 | − ``V4L2_COLORSPACE_SRGB``, ``V4L2_YCBCR_ENC_601`` and | |
| 771 | − ``V4L2_QUANTIZATION_FULL_RANGE``. | |
| 770 | + ``V4L2_COLORSPACE_SRGB``, ``V4L2_XFER_FUNC_SRGB``, ``V4L2_YCBCR_ENC_601`` | |
| 771 | + and ``V4L2_QUANTIZATION_FULL_RANGE``. | |
| 772 | 772 | |
| 773 | 773 | *************************************** |
| 774 | 774 | Detailed Transfer Function Descriptions |
| @@ -247,7 +247,7 @@ Querying Capabilities · 246 unmodified lines | ||
| 247 | 247 | Initialization |
| 248 | 248 | ============== |
| 249 | 249 | |
| 250 | −1. Set the coded format on ``OUTPUT`` via :c:func:`VIDIOC_S_FMT` | |
| 250 | +1. Set the coded format on ``OUTPUT`` via :c:func:`VIDIOC_S_FMT`. | |
| 251 | 251 | |
| 252 | 252 | * **Required fields:** |
| 253 | 253 | |
| @@ -803,7 +803,7 @@ it may be affected as per normal decoder operation. · 549 unmodified lines | ||
| 803 | 803 | * The decoder will drop all the pending ``OUTPUT`` buffers and they must be |
| 804 | 804 | treated as returned to the client (following standard semantics). |
| 805 | 805 | |
| 806 | −2. Restart the ``OUTPUT`` queue via :c:func:`VIDIOC_STREAMON` | |
| 806 | +2. Restart the ``OUTPUT`` queue via :c:func:`VIDIOC_STREAMON`. | |
| 807 | 807 | |
| 808 | 808 | * **Required fields:** |
| 809 | 809 | |
| @@ -906,7 +906,9 @@ reflected by corresponding queries): · 96 unmodified lines | ||
| 906 | 906 | |
| 907 | 907 | * visible resolution (selection rectangles), |
| 908 | 908 | |
| 909 | −* the minimum number of buffers needed for decoding. | |
| 909 | +* the minimum number of buffers needed for decoding, | |
| 910 | + | |
| 911 | +* bit-depth of the bitstream has been changed. | |
| 910 | 912 | |
| 911 | 913 | Whenever that happens, the decoder must proceed as follows: |
| 912 | 914 | |
| @@ -1059,7 +1061,7 @@ sequence was started. · 146 unmodified lines | ||
| 1059 | 1061 | ``V4L2_DEC_CMD_STOP`` again while the drain sequence is in progress and they |
| 1060 | 1062 | will fail with -EBUSY error code if attempted. |
| 1061 | 1063 | |
| 1062 | − Although mandatory, the availability of decoder commands may be queried | |
| 1064 | + Although not mandatory, the availability of decoder commands may be queried | |
| 1063 | 1065 | using :c:func:`VIDIOC_TRY_DECODER_CMD`. |
| 1064 | 1066 | |
| 1065 | 1067 | End of Stream |
| @@ -0,0 +1,753 @@ | ||
| 1 | +.. This file is dual-licensed: you can use it either under the terms | |
| 2 | +.. of the GPL 2.0 or the GFDL 1.1+ license, at your option. Note that this | |
| 3 | +.. dual licensing only applies to this file, and not this project as a | |
| 4 | +.. whole. | |
| 5 | +.. | |
| 6 | +.. a) This file is free software; you can redistribute it and/or | |
| 7 | +.. modify it under the terms of the GNU General Public License as | |
| 8 | +.. published by the Free Software Foundation version 2 of | |
| 9 | +.. the License. | |
| 10 | +.. | |
| 11 | +.. This file is distributed in the hope that it will be useful, | |
| 12 | +.. but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 13 | +.. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 14 | +.. GNU General Public License for more details. | |
| 15 | +.. | |
| 16 | +.. Or, alternatively, | |
| 17 | +.. | |
| 18 | +.. b) Permission is granted to copy, distribute and/or modify this | |
| 19 | +.. document under the terms of the GNU Free Documentation License, | |
| 20 | +.. Version 1.1 or any later version published by the Free Software | |
| 21 | +.. Foundation, with no Invariant Sections, no Front-Cover Texts | |
| 22 | +.. and no Back-Cover Texts. A copy of the license is included at | |
| 23 | +.. Documentation/userspace-api/media/fdl-appendix.rst. | |
| 24 | +.. | |
| 25 | +.. TODO: replace it to GPL-2.0 OR GFDL-1.1-or-later WITH no-invariant-sections | |
| 26 | + | |
| 27 | +.. _encoder: | |
| 28 | + | |
| 29 | +************************************************* | |
| 30 | +Memory-to-Memory Stateful Video Encoder Interface | |
| 31 | +************************************************* | |
| 32 | + | |
| 33 | +A stateful video encoder takes raw video frames in display order and encodes | |
| 34 | +them into a bytestream. It generates complete chunks of the bytestream, including | |
| 35 | +all metadata, headers, etc. The resulting bytestream does not require any | |
| 36 | +further post-processing by the client. | |
| 37 | + | |
| 38 | +Performing software stream processing, header generation etc. in the driver | |
| 39 | +in order to support this interface is strongly discouraged. In case such | |
| 40 | +operations are needed, use of the Stateless Video Encoder Interface (in | |
| 41 | +development) is strongly advised. | |
| 42 | + | |
| 43 | +Conventions and Notations Used in This Document | |
| 44 | +=============================================== | |
| 45 | + | |
| 46 | +1. The general V4L2 API rules apply if not specified in this document | |
| 47 | + otherwise. | |
| 48 | + | |
| 49 | +2. The meaning of words "must", "may", "should", etc. is as per `RFC | |
| 50 | + 2119 <https://tools.ietf.org/html/rfc2119>`_. | |
| 51 | + | |
| 52 | +3. All steps not marked "optional" are required. | |
| 53 | + | |
| 54 | +4. :c:func:`VIDIOC_G_EXT_CTRLS` and :c:func:`VIDIOC_S_EXT_CTRLS` may be used | |
| 55 | + interchangeably with :c:func:`VIDIOC_G_CTRL` and :c:func:`VIDIOC_S_CTRL`, | |
| 56 | + unless specified otherwise. | |
| 57 | + | |
| 58 | +5. Single-planar API (see :ref:`planar-apis`) and applicable structures may be | |
| 59 | + used interchangeably with multi-planar API, unless specified otherwise, | |
| 60 | + depending on encoder capabilities and following the general V4L2 guidelines. | |
| 61 | + | |
| 62 | +6. i = [a..b]: sequence of integers from a to b, inclusive, i.e. i = | |
| 63 | + [0..2]: i = 0, 1, 2. | |
| 64 | + | |
| 65 | +7. Given an ``OUTPUT`` buffer A, then A' represents a buffer on the ``CAPTURE`` | |
| 66 | + queue containing data that resulted from processing buffer A. | |
| 67 | + | |
| 68 | +Glossary | |
| 69 | +======== | |
| 70 | + | |
| 71 | +Refer to :ref:`decoder-glossary`. | |
| 72 | + | |
| 73 | +State Machine | |
| 74 | +============= | |
| 75 | + | |
| 76 | +.. kernel-render:: DOT | |
| 77 | + :alt: DOT digraph of encoder state machine | |
| 78 | + :caption: Encoder State Machine | |
| 79 | + | |
| 80 | + digraph encoder_state_machine { | |
| 81 | + node [shape = doublecircle, label="Encoding"] Encoding; | |
| 82 | + | |
| 83 | + node [shape = circle, label="Initialization"] Initialization; | |
| 84 | + node [shape = circle, label="Stopped"] Stopped; | |
| 85 | + node [shape = circle, label="Drain"] Drain; | |
| 86 | + node [shape = circle, label="Reset"] Reset; | |
| 87 | + | |
| 88 | + node [shape = point]; qi | |
| 89 | + qi -> Initialization [ label = "open()" ]; | |
| 90 | + | |
| 91 | + Initialization -> Encoding [ label = "Both queues streaming" ]; | |
| 92 | + | |
| 93 | + Encoding -> Drain [ label = "V4L2_ENC_CMD_STOP" ]; | |
| 94 | + Encoding -> Reset [ label = "VIDIOC_STREAMOFF(CAPTURE)" ]; | |
| 95 | + Encoding -> Stopped [ label = "VIDIOC_STREAMOFF(OUTPUT)" ]; | |
| 96 | + Encoding -> Encoding; | |
| 97 | + | |
| 98 | + Drain -> Stopped [ label = "All CAPTURE\nbuffers dequeued\nor\nVIDIOC_STREAMOFF(OUTPUT)" ]; | |
| 99 | + Drain -> Reset [ label = "VIDIOC_STREAMOFF(CAPTURE)" ]; | |
| 100 | + | |
| 101 | + Reset -> Encoding [ label = "VIDIOC_STREAMON(CAPTURE)" ]; | |
| 102 | + Reset -> Initialization [ label = "VIDIOC_REQBUFS(OUTPUT, 0)" ]; | |
| 103 | + | |
| 104 | + Stopped -> Encoding [ label = "V4L2_ENC_CMD_START\nor\nVIDIOC_STREAMON(OUTPUT)" ]; | |
| 105 | + Stopped -> Reset [ label = "VIDIOC_STREAMOFF(CAPTURE)" ]; | |
| 106 | + } | |
| 107 | + | |
| 108 | +Querying Capabilities | |
| 109 | +===================== | |
| 110 | + | |
| 111 | +1. To enumerate the set of coded formats supported by the encoder, the | |
| 112 | + client may call :c:func:`VIDIOC_ENUM_FMT` on ``CAPTURE``. | |
| 113 | + | |
| 114 | + * The full set of supported formats will be returned, regardless of the | |
| 115 | + format set on ``OUTPUT``. | |
| 116 | + | |
| 117 | +2. To enumerate the set of supported raw formats, the client may call | |
| 118 | + :c:func:`VIDIOC_ENUM_FMT` on ``OUTPUT``. | |
| 119 | + | |
| 120 | + * Only the formats supported for the format currently active on ``CAPTURE`` | |
| 121 | + will be returned. | |
| 122 | + | |
| 123 | + * In order to enumerate raw formats supported by a given coded format, | |
| 124 | + the client must first set that coded format on ``CAPTURE`` and then | |
| 125 | + enumerate the formats on ``OUTPUT``. | |
| 126 | + | |
| 127 | +3. The client may use :c:func:`VIDIOC_ENUM_FRAMESIZES` to detect supported | |
| 128 | + resolutions for a given format, passing the desired pixel format in | |
| 129 | + :c:type:`v4l2_frmsizeenum` ``pixel_format``. | |
| 130 | + | |
| 131 | + * Values returned by :c:func:`VIDIOC_ENUM_FRAMESIZES` for a coded pixel | |
| 132 | + format will include all possible coded resolutions supported by the | |
| 133 | + encoder for the given coded pixel format. | |
| 134 | + | |
| 135 | + * Values returned by :c:func:`VIDIOC_ENUM_FRAMESIZES` for a raw pixel format | |
| 136 | + will include all possible frame buffer resolutions supported by the | |
| 137 | + encoder for the given raw pixel format and coded format currently set on | |
| 138 | + ``CAPTURE``. | |
| 139 | + | |
| 140 | +4. The client may use :c:func:`VIDIOC_ENUM_FRAMEINTERVALS` to detect supported | |
| 141 | + frame intervals for a given format and resolution, passing the desired pixel | |
| 142 | + format in :c:type:`v4l2_frmsizeenum` ``pixel_format`` and the resolution | |
| 143 | + in :c:type:`v4l2_frmsizeenum` ``width`` and :c:type:`v4l2_frmsizeenum` | |
| 144 | + ``height``. | |
| 145 | + | |
| 146 | + * Values returned by :c:func:`VIDIOC_ENUM_FRAMEINTERVALS` for a coded pixel | |
| 147 | + format and coded resolution will include all possible frame intervals | |
| 148 | + supported by the encoder for the given coded pixel format and resolution. | |
| 149 | + | |
| 150 | + * Values returned by :c:func:`VIDIOC_ENUM_FRAMEINTERVALS` for a raw pixel | |
| 151 | + format and resolution will include all possible frame intervals supported | |
| 152 | + by the encoder for the given raw pixel format and resolution and for the | |
| 153 | + coded format, coded resolution and coded frame interval currently set on | |
| 154 | + ``CAPTURE``. | |
| 155 | + | |
| 156 | + * Support for :c:func:`VIDIOC_ENUM_FRAMEINTERVALS` is optional. If it is | |
| 157 | + not implemented, then there are no special restrictions other than the | |
| 158 | + limits of the codec itself. | |
| 159 | + | |
| 160 | +5. Supported profiles and levels for the coded format currently set on | |
| 161 | + ``CAPTURE``, if applicable, may be queried using their respective controls | |
| 162 | + via :c:func:`VIDIOC_QUERYCTRL`. | |
| 163 | + | |
| 164 | +6. Any additional encoder capabilities may be discovered by querying | |
| 165 | + their respective controls. | |
| 166 | + | |
| 167 | +Initialization | |
| 168 | +============== | |
| 169 | + | |
| 170 | +1. Set the coded format on the ``CAPTURE`` queue via :c:func:`VIDIOC_S_FMT`. | |
| 171 | + | |
| 172 | + * **Required fields:** | |
| 173 | + | |
| 174 | + ``type`` | |
| 175 | + a ``V4L2_BUF_TYPE_*`` enum appropriate for ``CAPTURE``. | |
| 176 | + | |
| 177 | + ``pixelformat`` | |
| 178 | + the coded format to be produced. | |
| 179 | + | |
| 180 | + ``sizeimage`` | |
| 181 | + desired size of ``CAPTURE`` buffers; the encoder may adjust it to | |
| 182 | + match hardware requirements. | |
| 183 | + | |
| 184 | + ``width``, ``height`` | |
| 185 | + ignored (read-only). | |
| 186 | + | |
| 187 | + other fields | |
| 188 | + follow standard semantics. | |
| 189 | + | |
| 190 | + * **Return fields:** | |
| 191 | + | |
| 192 | + ``sizeimage`` | |
| 193 | + adjusted size of ``CAPTURE`` buffers. | |
| 194 | + | |
| 195 | + ``width``, ``height`` | |
| 196 | + the coded size selected by the encoder based on current state, e.g. | |
| 197 | + ``OUTPUT`` format, selection rectangles, etc. (read-only). | |
| 198 | + | |
| 199 | + .. important:: | |
| 200 | + | |
| 201 | + Changing the ``CAPTURE`` format may change the currently set ``OUTPUT`` | |
| 202 | + format. How the new ``OUTPUT`` format is determined is up to the encoder | |
| 203 | + and the client must ensure it matches its needs afterwards. | |
| 204 | + | |
| 205 | +2. **Optional.** Enumerate supported ``OUTPUT`` formats (raw formats for | |
| 206 | + source) for the selected coded format via :c:func:`VIDIOC_ENUM_FMT`. | |
| 207 | + | |
| 208 | + * **Required fields:** | |
| 209 | + | |
| 210 | + ``type`` | |
| 211 | + a ``V4L2_BUF_TYPE_*`` enum appropriate for ``OUTPUT``. | |
| 212 | + | |
| 213 | + other fields | |
| 214 | + follow standard semantics. | |
| 215 | + | |
| 216 | + * **Return fields:** | |
| 217 | + | |
| 218 | + ``pixelformat`` | |
| 219 | + raw format supported for the coded format currently selected on | |
| 220 | + the ``CAPTURE`` queue. | |
| 221 | + | |
| 222 | + other fields | |
| 223 | + follow standard semantics. | |
| 224 | + | |
| 225 | +3. Set the raw source format on the ``OUTPUT`` queue via | |
| 226 | + :c:func:`VIDIOC_S_FMT`. | |
| 227 | + | |
| 228 | + * **Required fields:** | |
| 229 | + | |
| 230 | + ``type`` | |
| 231 | + a ``V4L2_BUF_TYPE_*`` enum appropriate for ``OUTPUT``. | |
| 232 | + | |
| 233 | + ``pixelformat`` | |
| 234 | + raw format of the source. | |
| 235 | + | |
| 236 | + ``width``, ``height`` | |
| 237 | + source resolution. | |
| 238 | + | |
| 239 | + other fields | |
| 240 | + follow standard semantics. | |
| 241 | + | |
| 242 | + * **Return fields:** | |
| 243 | + | |
| 244 | + ``width``, ``height`` | |
| 245 | + may be adjusted to match encoder minimums, maximums and alignment | |
| 246 | + requirements, as required by the currently selected formats, as | |
| 247 | + reported by :c:func:`VIDIOC_ENUM_FRAMESIZES`. | |
| 248 | + | |
| 249 | + other fields | |
| 250 | + follow standard semantics. | |
| 251 | + | |
| 252 | + * Setting the ``OUTPUT`` format will reset the selection rectangles to their | |
| 253 | + default values, based on the new resolution, as described in the next | |
| 254 | + step. | |
| 255 | + | |
| 256 | +4. Set the raw frame interval on the ``OUTPUT`` queue via | |
| 257 | + :c:func:`VIDIOC_S_PARM`. This also sets the coded frame interval on the | |
| 258 | + ``CAPTURE`` queue to the same value. | |
| 259 | + | |
| 260 | + * ** Required fields:** | |
| 261 | + | |
| 262 | + ``type`` | |
| 263 | + a ``V4L2_BUF_TYPE_*`` enum appropriate for ``OUTPUT``. | |
| 264 | + | |
| 265 | + ``parm.output`` | |
| 266 | + set all fields except ``parm.output.timeperframe`` to 0. | |
| 267 | + | |
| 268 | + ``parm.output.timeperframe`` | |
| 269 | + the desired frame interval; the encoder may adjust it to | |
| 270 | + match hardware requirements. | |
| 271 | + | |
| 272 | + * **Return fields:** | |
| 273 | + | |
| 274 | + ``parm.output.timeperframe`` | |
| 275 | + the adjusted frame interval. | |
| 276 | + | |
| 277 | + .. important:: | |
| 278 | + | |
| 279 | + Changing the ``OUTPUT`` frame interval *also* sets the framerate that | |
| 280 | + the encoder uses to encode the video. So setting the frame interval | |
| 281 | + to 1/24 (or 24 frames per second) will produce a coded video stream | |
| 282 | + that can be played back at that speed. The frame interval for the | |
| 283 | + ``OUTPUT`` queue is just a hint, the application may provide raw | |
| 284 | + frames at a different rate. It can be used by the driver to help | |
| 285 | + schedule multiple encoders running in parallel. | |
| 286 | + | |
| 287 | + In the next step the ``CAPTURE`` frame interval can optionally be | |
| 288 | + changed to a different value. This is useful for off-line encoding | |
| 289 | + were the coded frame interval can be different from the rate at | |
| 290 | + which raw frames are supplied. | |
| 291 | + | |
| 292 | + .. important:: | |
| 293 | + | |
| 294 | + ``timeperframe`` deals with *frames*, not fields. So for interlaced | |
| 295 | + formats this is the time per two fields, since a frame consists of | |
| 296 | + a top and a bottom field. | |
| 297 | + | |
| 298 | + .. note:: | |
| 299 | + | |
| 300 | + It is due to historical reasons that changing the ``OUTPUT`` frame | |
| 301 | + interval also changes the coded frame interval on the ``CAPTURE`` | |
| 302 | + queue. Ideally these would be independent settings, but that would | |
| 303 | + break the existing API. | |
| 304 | + | |
| 305 | +5. **Optional** Set the coded frame interval on the ``CAPTURE`` queue via | |
| 306 | + :c:func:`VIDIOC_S_PARM`. This is only necessary if the coded frame | |
| 307 | + interval is different from the raw frame interval, which is typically | |
| 308 | + the case for off-line encoding. Support for this feature is signalled | |
| 309 | + by the :ref:`V4L2_FMT_FLAG_ENC_CAP_FRAME_INTERVAL <fmtdesc-flags>` format flag. | |
| 310 | + | |
| 311 | + * ** Required fields:** | |
| 312 | + | |
| 313 | + ``type`` | |
| 314 | + a ``V4L2_BUF_TYPE_*`` enum appropriate for ``CAPTURE``. | |
| 315 | + | |
| 316 | + ``parm.capture`` | |
| 317 | + set all fields except ``parm.capture.timeperframe`` to 0. | |
| 318 | + | |
| 319 | + ``parm.capture.timeperframe`` | |
| 320 | + the desired coded frame interval; the encoder may adjust it to | |
| 321 | + match hardware requirements. | |
| 322 | + | |
| 323 | + * **Return fields:** | |
| 324 | + | |
| 325 | + ``parm.capture.timeperframe`` | |
| 326 | + the adjusted frame interval. | |
| 327 | + | |
| 328 | + .. important:: | |
| 329 | + | |
| 330 | + Changing the ``CAPTURE`` frame interval sets the framerate for the | |
| 331 | + coded video. It does *not* set the rate at which buffers arrive on the | |
| 332 | + ``CAPTURE`` queue, that depends on how fast the encoder is and how | |
| 333 | + fast raw frames are queued on the ``OUTPUT`` queue. | |
| 334 | + | |
| 335 | + .. important:: | |
| 336 | + | |
| 337 | + ``timeperframe`` deals with *frames*, not fields. So for interlaced | |
| 338 | + formats this is the time per two fields, since a frame consists of | |
| 339 | + a top and a bottom field. | |
| 340 | + | |
| 341 | + .. note:: | |
| 342 | + | |
| 343 | + Not all drivers support this functionality, in that case just set | |
| 344 | + the desired coded frame interval for the ``OUTPUT`` queue. | |
| 345 | + | |
| 346 | + However, drivers that can schedule multiple encoders based on the | |
| 347 | + ``OUTPUT`` frame interval must support this optional feature. | |
| 348 | + | |
| 349 | +6. **Optional.** Set the visible resolution for the stream metadata via | |
| 350 | + :c:func:`VIDIOC_S_SELECTION` on the ``OUTPUT`` queue if it is desired | |
| 351 | + to be different than the full OUTPUT resolution. | |
| 352 | + | |
| 353 | + * **Required fields:** | |
| 354 | + | |
| 355 | + ``type`` | |
| 356 | + a ``V4L2_BUF_TYPE_*`` enum appropriate for ``OUTPUT``. | |
| 357 | + | |
| 358 | + ``target`` | |
| 359 | + set to ``V4L2_SEL_TGT_CROP``. | |
| 360 | + | |
| 361 | + ``r.left``, ``r.top``, ``r.width``, ``r.height`` | |
| 362 | + visible rectangle; this must fit within the `V4L2_SEL_TGT_CROP_BOUNDS` | |
| 363 | + rectangle and may be subject to adjustment to match codec and | |
| 364 | + hardware constraints. | |
| 365 | + | |
| 366 | + * **Return fields:** | |
| 367 | + | |
| 368 | + ``r.left``, ``r.top``, ``r.width``, ``r.height`` | |
| 369 | + visible rectangle adjusted by the encoder. | |
| 370 | + | |
| 371 | + * The following selection targets are supported on ``OUTPUT``: | |
| 372 | + | |
| 373 | + ``V4L2_SEL_TGT_CROP_BOUNDS`` | |
| 374 | + equal to the full source frame, matching the active ``OUTPUT`` | |
| 375 | + format. | |
| 376 | + | |
| 377 | + ``V4L2_SEL_TGT_CROP_DEFAULT`` | |
| 378 | + equal to ``V4L2_SEL_TGT_CROP_BOUNDS``. | |
| 379 | + | |
| 380 | + ``V4L2_SEL_TGT_CROP`` | |
| 381 | + rectangle within the source buffer to be encoded into the | |
| 382 | + ``CAPTURE`` stream; defaults to ``V4L2_SEL_TGT_CROP_DEFAULT``. | |
| 383 | + | |
| 384 | + .. note:: | |
| 385 | + | |
| 386 | + A common use case for this selection target is encoding a source | |
| 387 | + video with a resolution that is not a multiple of a macroblock, | |
| 388 | + e.g. the common 1920x1080 resolution may require the source | |
| 389 | + buffers to be aligned to 1920x1088 for codecs with 16x16 macroblock | |
| 390 | + size. To avoid encoding the padding, the client needs to explicitly | |
| 391 | + configure this selection target to 1920x1080. | |
| 392 | + | |
| 393 | + .. warning:: | |
| 394 | + | |
| 395 | + The encoder may adjust the crop/compose rectangles to the nearest | |
| 396 | + supported ones to meet codec and hardware requirements. The client needs | |
| 397 | + to check the adjusted rectangle returned by :c:func:`VIDIOC_S_SELECTION`. | |
| 398 | + | |
| 399 | +7. Allocate buffers for both ``OUTPUT`` and ``CAPTURE`` via | |
| 400 | + :c:func:`VIDIOC_REQBUFS`. This may be performed in any order. | |
| 401 | + | |
| 402 | + * **Required fields:** | |
| 403 | + | |
| 404 | + ``count`` | |
| 405 | + requested number of buffers to allocate; greater than zero. | |
| 406 | + | |
| 407 | + ``type`` | |
| 408 | + a ``V4L2_BUF_TYPE_*`` enum appropriate for ``OUTPUT`` or | |
| 409 | + ``CAPTURE``. | |
| 410 | + | |
| 411 | + other fields | |
| 412 | + follow standard semantics. | |
| 413 | + | |
| 414 | + * **Return fields:** | |
| 415 | + | |
| 416 | + ``count`` | |
| 417 | + actual number of buffers allocated. | |
| 418 | + | |
| 419 | + .. warning:: | |
| 420 | + | |
| 421 | + The actual number of allocated buffers may differ from the ``count`` | |
| 422 | + given. The client must check the updated value of ``count`` after the | |
| 423 | + call returns. | |
| 424 | + | |
| 425 | + .. note:: | |
| 426 | + | |
| 427 | + To allocate more than the minimum number of OUTPUT buffers (for pipeline | |
| 428 | + depth), the client may query the ``V4L2_CID_MIN_BUFFERS_FOR_OUTPUT`` | |
| 429 | + control to get the minimum number of buffers required, and pass the | |
| 430 | + obtained value plus the number of additional buffers needed in the | |
| 431 | + ``count`` field to :c:func:`VIDIOC_REQBUFS`. | |
| 432 | + | |
| 433 | + Alternatively, :c:func:`VIDIOC_CREATE_BUFS` can be used to have more | |
| 434 | + control over buffer allocation. | |
| 435 | + | |
| 436 | + * **Required fields:** | |
| 437 | + | |
| 438 | + ``count`` | |
| 439 | + requested number of buffers to allocate; greater than zero. | |
| 440 | + | |
| 441 | + ``type`` | |
| 442 | + a ``V4L2_BUF_TYPE_*`` enum appropriate for ``OUTPUT``. | |
| 443 | + | |
| 444 | + other fields | |
| 445 | + follow standard semantics. | |
| 446 | + | |
| 447 | + * **Return fields:** | |
| 448 | + | |
| 449 | + ``count`` | |
| 450 | + adjusted to the number of allocated buffers. | |
| 451 | + | |
| 452 | +8. Begin streaming on both ``OUTPUT`` and ``CAPTURE`` queues via | |
| 453 | + :c:func:`VIDIOC_STREAMON`. This may be performed in any order. The actual | |
| 454 | + encoding process starts when both queues start streaming. | |
| 455 | + | |
| 456 | +.. note:: | |
| 457 | + | |
| 458 | + If the client stops the ``CAPTURE`` queue during the encode process and then | |
| 459 | + restarts it again, the encoder will begin generating a stream independent | |
| 460 | + from the stream generated before the stop. The exact constraints depend | |
| 461 | + on the coded format, but may include the following implications: | |
| 462 | + | |
| 463 | + * encoded frames produced after the restart must not reference any | |
| 464 | + frames produced before the stop, e.g. no long term references for | |
| 465 | + H.264/HEVC, | |
| 466 | + | |
| 467 | + * any headers that must be included in a standalone stream must be | |
| 468 | + produced again, e.g. SPS and PPS for H.264/HEVC. | |
| 469 | + | |
| 470 | +Encoding | |
| 471 | +======== | |
| 472 | + | |
| 473 | +This state is reached after the `Initialization` sequence finishes | |
| 474 | +successfully. In this state, the client queues and dequeues buffers to both | |
| 475 | +queues via :c:func:`VIDIOC_QBUF` and :c:func:`VIDIOC_DQBUF`, following the | |
| 476 | +standard semantics. | |
| 477 | + | |
| 478 | +The content of encoded ``CAPTURE`` buffers depends on the active coded pixel | |
| 479 | +format and may be affected by codec-specific extended controls, as stated | |
| 480 | +in the documentation of each format. | |
| 481 | + | |
| 482 | +Both queues operate independently, following standard behavior of V4L2 buffer | |
| 483 | +queues and memory-to-memory devices. In addition, the order of encoded frames | |
| 484 | +dequeued from the ``CAPTURE`` queue may differ from the order of queuing raw | |
| 485 | +frames to the ``OUTPUT`` queue, due to properties of the selected coded format, | |
| 486 | +e.g. frame reordering. | |
| 487 | + | |
| 488 | +The client must not assume any direct relationship between ``CAPTURE`` and | |
| 489 | +``OUTPUT`` buffers and any specific timing of buffers becoming | |
| 490 | +available to dequeue. Specifically: | |
| 491 | + | |
| 492 | +* a buffer queued to ``OUTPUT`` may result in more than one buffer produced on | |
| 493 | + ``CAPTURE`` (for example, if returning an encoded frame allowed the encoder | |
| 494 | + to return a frame that preceded it in display, but succeeded it in the decode | |
| 495 | + order; however, there may be other reasons for this as well), | |
| 496 | + | |
| 497 | +* a buffer queued to ``OUTPUT`` may result in a buffer being produced on | |
| 498 | + ``CAPTURE`` later into encode process, and/or after processing further | |
| 499 | + ``OUTPUT`` buffers, or be returned out of order, e.g. if display | |
| 500 | + reordering is used, | |
| 501 | + | |
| 502 | +* buffers may become available on the ``CAPTURE`` queue without additional | |
| 503 | + buffers queued to ``OUTPUT`` (e.g. during drain or ``EOS``), because of the | |
| 504 | + ``OUTPUT`` buffers queued in the past whose encoding results are only | |
| 505 | + available at later time, due to specifics of the encoding process, | |
| 506 | + | |
| 507 | +* buffers queued to ``OUTPUT`` may not become available to dequeue instantly | |
| 508 | + after being encoded into a corresponding ``CAPTURE`` buffer, e.g. if the | |
| 509 | + encoder needs to use the frame as a reference for encoding further frames. | |
| 510 | + | |
| 511 | +.. note:: | |
| 512 | + | |
| 513 | + To allow matching encoded ``CAPTURE`` buffers with ``OUTPUT`` buffers they | |
| 514 | + originated from, the client can set the ``timestamp`` field of the | |
| 515 | + :c:type:`v4l2_buffer` struct when queuing an ``OUTPUT`` buffer. The | |
| 516 | + ``CAPTURE`` buffer(s), which resulted from encoding that ``OUTPUT`` buffer | |
| 517 | + will have their ``timestamp`` field set to the same value when dequeued. | |
| 518 | + | |
| 519 | + In addition to the straightforward case of one ``OUTPUT`` buffer producing | |
| 520 | + one ``CAPTURE`` buffer, the following cases are defined: | |
| 521 | + | |
| 522 | + * one ``OUTPUT`` buffer generates multiple ``CAPTURE`` buffers: the same | |
| 523 | + ``OUTPUT`` timestamp will be copied to multiple ``CAPTURE`` buffers, | |
| 524 | + | |
| 525 | + * the encoding order differs from the presentation order (i.e. the | |
| 526 | + ``CAPTURE`` buffers are out-of-order compared to the ``OUTPUT`` buffers): | |
| 527 | + ``CAPTURE`` timestamps will not retain the order of ``OUTPUT`` timestamps. | |
| 528 | + | |
| 529 | +.. note:: | |
| 530 | + | |
| 531 | + To let the client distinguish between frame types (keyframes, intermediate | |
| 532 | + frames; the exact list of types depends on the coded format), the | |
| 533 | + ``CAPTURE`` buffers will have corresponding flag bits set in their | |
| 534 | + :c:type:`v4l2_buffer` struct when dequeued. See the documentation of | |
| 535 | + :c:type:`v4l2_buffer` and each coded pixel format for exact list of flags | |
| 536 | + and their meanings. | |
| 537 | + | |
| 538 | +Should an encoding error occur, it will be reported to the client with the level | |
| 539 | +of details depending on the encoder capabilities. Specifically: | |
| 540 | + | |
| 541 | +* the ``CAPTURE`` buffer (if any) that contains the results of the failed encode | |
| 542 | + operation will be returned with the ``V4L2_BUF_FLAG_ERROR`` flag set, | |
| 543 | + | |
| 544 | +* if the encoder is able to precisely report the ``OUTPUT`` buffer(s) that triggered | |
| 545 | + the error, such buffer(s) will be returned with the ``V4L2_BUF_FLAG_ERROR`` flag | |
| 546 | + set. | |
| 547 | + | |
| 548 | +.. note:: | |
| 549 | + | |
| 550 | + If a ``CAPTURE`` buffer is too small then it is just returned with the | |
| 551 | + ``V4L2_BUF_FLAG_ERROR`` flag set. More work is needed to detect that this | |
| 552 | + error occurred because the buffer was too small, and to provide support to | |
| 553 | + free existing buffers that were too small. | |
| 554 | + | |
| 555 | +In case of a fatal failure that does not allow the encoding to continue, any | |
| 556 | +further operations on corresponding encoder file handle will return the -EIO | |
| 557 | +error code. The client may close the file handle and open a new one, or | |
| 558 | +alternatively reinitialize the instance by stopping streaming on both queues, | |
| 559 | +releasing all buffers and performing the Initialization sequence again. | |
| 560 | + | |
| 561 | +Encoding Parameter Changes | |
| 562 | +========================== | |
| 563 | + | |
| 564 | +The client is allowed to use :c:func:`VIDIOC_S_CTRL` to change encoder | |
| 565 | +parameters at any time. The availability of parameters is encoder-specific | |
| 566 | +and the client must query the encoder to find the set of available controls. | |
| 567 | + | |
| 568 | +The ability to change each parameter during encoding is encoder-specific, as | |
| 569 | +per the standard semantics of the V4L2 control interface. The client may | |
| 570 | +attempt to set a control during encoding and if the operation fails with the | |
| 571 | +-EBUSY error code, the ``CAPTURE`` queue needs to be stopped for the | |
| 572 | +configuration change to be allowed. To do this, it may follow the `Drain` | |
| 573 | +sequence to avoid losing the already queued/encoded frames. | |
| 574 | + | |
| 575 | +The timing of parameter updates is encoder-specific, as per the standard | |
| 576 | +semantics of the V4L2 control interface. If the client needs to apply the | |
| 577 | +parameters exactly at specific frame, using the Request API | |
| 578 | +(:ref:`media-request-api`) should be considered, if supported by the encoder. | |
| 579 | + | |
| 580 | +Drain | |
| 581 | +===== | |
| 582 | + | |
| 583 | +To ensure that all the queued ``OUTPUT`` buffers have been processed and the | |
| 584 | +related ``CAPTURE`` buffers are given to the client, the client must follow the | |
| 585 | +drain sequence described below. After the drain sequence ends, the client has | |
| 586 | +received all encoded frames for all ``OUTPUT`` buffers queued before the | |
| 587 | +sequence was started. | |
| 588 | + | |
| 589 | +1. Begin the drain sequence by issuing :c:func:`VIDIOC_ENCODER_CMD`. | |
| 590 | + | |
| 591 | + * **Required fields:** | |
| 592 | + | |
| 593 | + ``cmd`` | |
| 594 | + set to ``V4L2_ENC_CMD_STOP``. | |
| 595 | + | |
| 596 | + ``flags`` | |
| 597 | + set to 0. | |
| 598 | + | |
| 599 | + ``pts`` | |
| 600 | + set to 0. | |
| 601 | + | |
| 602 | + .. warning:: | |
| 603 | + | |
| 604 | + The sequence can be only initiated if both ``OUTPUT`` and ``CAPTURE`` | |
| 605 | + queues are streaming. For compatibility reasons, the call to | |
| 606 | + :c:func:`VIDIOC_ENCODER_CMD` will not fail even if any of the queues is | |
| 607 | + not streaming, but at the same time it will not initiate the `Drain` | |
| 608 | + sequence and so the steps described below would not be applicable. | |
| 609 | + | |
| 610 | +2. Any ``OUTPUT`` buffers queued by the client before the | |
| 611 | + :c:func:`VIDIOC_ENCODER_CMD` was issued will be processed and encoded as | |
| 612 | + normal. The client must continue to handle both queues independently, | |
| 613 | + similarly to normal encode operation. This includes: | |
| 614 | + | |
| 615 | + * queuing and dequeuing ``CAPTURE`` buffers, until a buffer marked with the | |
| 616 | + ``V4L2_BUF_FLAG_LAST`` flag is dequeued, | |
| 617 | + | |
| 618 | + .. warning:: | |
| 619 | + | |
| 620 | + The last buffer may be empty (with :c:type:`v4l2_buffer` | |
| 621 | + ``bytesused`` = 0) and in that case it must be ignored by the client, | |
| 622 | + as it does not contain an encoded frame. | |
| 623 | + | |
| 624 | + .. note:: | |
| 625 | + | |
| 626 | + Any attempt to dequeue more ``CAPTURE`` buffers beyond the buffer | |
| 627 | + marked with ``V4L2_BUF_FLAG_LAST`` will result in a -EPIPE error from | |
| 628 | + :c:func:`VIDIOC_DQBUF`. | |
| 629 | + | |
| 630 | + * dequeuing processed ``OUTPUT`` buffers, until all the buffers queued | |
| 631 | + before the ``V4L2_ENC_CMD_STOP`` command are dequeued, | |
| 632 | + | |
| 633 | + * dequeuing the ``V4L2_EVENT_EOS`` event, if the client subscribes to it. | |
| 634 | + | |
| 635 | + .. note:: | |
| 636 | + | |
| 637 | + For backwards compatibility, the encoder will signal a ``V4L2_EVENT_EOS`` | |
| 638 | + event when the last frame has been encoded and all frames are ready to be | |
| 639 | + dequeued. It is deprecated behavior and the client must not rely on it. | |
| 640 | + The ``V4L2_BUF_FLAG_LAST`` buffer flag should be used instead. | |
| 641 | + | |
| 642 | +3. Once all ``OUTPUT`` buffers queued before the ``V4L2_ENC_CMD_STOP`` call are | |
| 643 | + dequeued and the last ``CAPTURE`` buffer is dequeued, the encoder is stopped | |
| 644 | + and it will accept, but not process any newly queued ``OUTPUT`` buffers | |
| 645 | + until the client issues any of the following operations: | |
| 646 | + | |
| 647 | + * ``V4L2_ENC_CMD_START`` - the encoder will not be reset and will resume | |
| 648 | + operation normally, with all the state from before the drain, | |
| 649 | + | |
| 650 | + * a pair of :c:func:`VIDIOC_STREAMOFF` and :c:func:`VIDIOC_STREAMON` on the | |
| 651 | + ``CAPTURE`` queue - the encoder will be reset (see the `Reset` sequence) | |
| 652 | + and then resume encoding, | |
| 653 | + | |
| 654 | + * a pair of :c:func:`VIDIOC_STREAMOFF` and :c:func:`VIDIOC_STREAMON` on the | |
| 655 | + ``OUTPUT`` queue - the encoder will resume operation normally, however any | |
| 656 | + source frames queued to the ``OUTPUT`` queue between ``V4L2_ENC_CMD_STOP`` | |
| 657 | + and :c:func:`VIDIOC_STREAMOFF` will be discarded. | |
| 658 | + | |
| 659 | +.. note:: | |
| 660 | + | |
| 661 | + Once the drain sequence is initiated, the client needs to drive it to | |
| 662 | + completion, as described by the steps above, unless it aborts the process by | |
| 663 | + issuing :c:func:`VIDIOC_STREAMOFF` on any of the ``OUTPUT`` or ``CAPTURE`` | |
| 664 | + queues. The client is not allowed to issue ``V4L2_ENC_CMD_START`` or | |
| 665 | + ``V4L2_ENC_CMD_STOP`` again while the drain sequence is in progress and they | |
| 666 | + will fail with -EBUSY error code if attempted. | |
| 667 | + | |
| 668 | + For reference, handling of various corner cases is described below: | |
| 669 | + | |
| 670 | + * In case of no buffer in the ``OUTPUT`` queue at the time the | |
| 671 | + ``V4L2_ENC_CMD_STOP`` command was issued, the drain sequence completes | |
| 672 | + immediately and the encoder returns an empty ``CAPTURE`` buffer with the | |
| 673 | + ``V4L2_BUF_FLAG_LAST`` flag set. | |
| 674 | + | |
| 675 | + * In case of no buffer in the ``CAPTURE`` queue at the time the drain | |
| 676 | + sequence completes, the next time the client queues a ``CAPTURE`` buffer | |
| 677 | + it is returned at once as an empty buffer with the ``V4L2_BUF_FLAG_LAST`` | |
| 678 | + flag set. | |
| 679 | + | |
| 680 | + * If :c:func:`VIDIOC_STREAMOFF` is called on the ``CAPTURE`` queue in the | |
| 681 | + middle of the drain sequence, the drain sequence is canceled and all | |
| 682 | + ``CAPTURE`` buffers are implicitly returned to the client. | |
| 683 | + | |
| 684 | + * If :c:func:`VIDIOC_STREAMOFF` is called on the ``OUTPUT`` queue in the | |
| 685 | + middle of the drain sequence, the drain sequence completes immediately and | |
| 686 | + next ``CAPTURE`` buffer will be returned empty with the | |
| 687 | + ``V4L2_BUF_FLAG_LAST`` flag set. | |
| 688 | + | |
| 689 | + Although not mandatory, the availability of encoder commands may be queried | |
| 690 | + using :c:func:`VIDIOC_TRY_ENCODER_CMD`. | |
| 691 | + | |
| 692 | +Reset | |
| 693 | +===== | |
| 694 | + | |
| 695 | +The client may want to request the encoder to reinitialize the encoding, so | |
| 696 | +that the following stream data becomes independent from the stream data | |
| 697 | +generated before. Depending on the coded format, that may imply that: | |
| 698 | + | |
| 699 | +* encoded frames produced after the restart must not reference any frames | |
| 700 | + produced before the stop, e.g. no long term references for H.264/HEVC, | |
| 701 | + | |
| 702 | +* any headers that must be included in a standalone stream must be produced | |
| 703 | + again, e.g. SPS and PPS for H.264/HEVC. | |
| 704 | + | |
| 705 | +This can be achieved by performing the reset sequence. | |
| 706 | + | |
| 707 | +1. Perform the `Drain` sequence to ensure all the in-flight encoding finishes | |
| 708 | + and respective buffers are dequeued. | |
| 709 | + | |
| 710 | +2. Stop streaming on the ``CAPTURE`` queue via :c:func:`VIDIOC_STREAMOFF`. This | |
| 711 | + will return all currently queued ``CAPTURE`` buffers to the client, without | |
| 712 | + valid frame data. | |
| 713 | + | |
| 714 | +3. Start streaming on the ``CAPTURE`` queue via :c:func:`VIDIOC_STREAMON` and | |
| 715 | + continue with regular encoding sequence. The encoded frames produced into | |
| 716 | + ``CAPTURE`` buffers from now on will contain a standalone stream that can be | |
| 717 | + decoded without the need for frames encoded before the reset sequence, | |
| 718 | + starting at the first ``OUTPUT`` buffer queued after issuing the | |
| 719 | + `V4L2_ENC_CMD_STOP` of the `Drain` sequence. | |
| 720 | + | |
| 721 | +This sequence may be also used to change encoding parameters for encoders | |
| 722 | +without the ability to change the parameters on the fly. | |
| 723 | + | |
| 724 | +Commit Points | |
| 725 | +============= | |
| 726 | + | |
| 727 | +Setting formats and allocating buffers triggers changes in the behavior of the | |
| 728 | +encoder. | |
| 729 | + | |
| 730 | +1. Setting the format on the ``CAPTURE`` queue may change the set of formats | |
| 731 | + supported/advertised on the ``OUTPUT`` queue. In particular, it also means | |
| 732 | + that the ``OUTPUT`` format may be reset and the client must not rely on the | |
| 733 | + previously set format being preserved. | |
| 734 | + | |
| 735 | +2. Enumerating formats on the ``OUTPUT`` queue always returns only formats | |
| 736 | + supported for the current ``CAPTURE`` format. | |
| 737 | + | |
| 738 | +3. Setting the format on the ``OUTPUT`` queue does not change the list of | |
| 739 | + formats available on the ``CAPTURE`` queue. An attempt to set the ``OUTPUT`` | |
| 740 | + format that is not supported for the currently selected ``CAPTURE`` format | |
| 741 | + will result in the encoder adjusting the requested ``OUTPUT`` format to a | |
| 742 | + supported one. | |
| 743 | + | |
| 744 | +4. Enumerating formats on the ``CAPTURE`` queue always returns the full set of | |
| 745 | + supported coded formats, irrespective of the current ``OUTPUT`` format. | |
| 746 | + | |
| 747 | +5. While buffers are allocated on any of the ``OUTPUT`` or ``CAPTURE`` queues, | |
| 748 | + the client must not change the format on the ``CAPTURE`` queue. Drivers will | |
| 749 | + return the -EBUSY error code for any such format change attempt. | |
| 750 | + | |
| 751 | +To summarize, setting formats and allocation must always start with the | |
| 752 | +``CAPTURE`` queue and the ``CAPTURE`` queue is the master that governs the | |
| 753 | +set of supported formats for the ``OUTPUT`` queue. | |
| @@ -46,4 +46,5 @@ devices are given in the following sections. · 45 unmodified lines | ||
| 46 | 46 | :maxdepth: 1 |
| 47 | 47 | |
| 48 | 48 | dev-decoder |
| 49 | + dev-encoder | |
| 49 | 50 | dev-stateless-decoder |
| @@ -51,7 +51,7 @@ other information, the physical address of the framebuffer in the · 50 unmodified lines | ||
| 51 | 51 | ``base`` field of struct :c:type:`v4l2_framebuffer`. |
| 52 | 52 | The framebuffer device ioctl ``FBIOGET_FSCREENINFO`` returns the same |
| 53 | 53 | address in the ``smem_start`` field of struct |
| 54 | −struct :c:type:`fb_fix_screeninfo`. The ``FBIOGET_FSCREENINFO`` | |
| 54 | +:c:type:`fb_fix_screeninfo`. The ``FBIOGET_FSCREENINFO`` | |
| 55 | 55 | ioctl and struct :c:type:`fb_fix_screeninfo` are defined in |
| 56 | 56 | the ``linux/fb.h`` header file. |
| 57 | 57 | |
| @@ -78,7 +78,7 @@ field of a struct :c:type:`v4l2_format` to · 77 unmodified lines | ||
| 78 | 78 | ``V4L2_BUF_TYPE_SDR_CAPTURE`` or ``V4L2_BUF_TYPE_SDR_OUTPUT`` and use |
| 79 | 79 | the struct :c:type:`v4l2_sdr_format` ``sdr`` member |
| 80 | 80 | of the ``fmt`` union as needed per the desired operation. Currently |
| 81 | −there is two fields, ``pixelformat`` and ``buffersize``, of struct | |
| 81 | +there are two fields, ``pixelformat`` and ``buffersize``, of | |
| 82 | 82 | struct :c:type:`v4l2_sdr_format` which are used. |
| 83 | 83 | Content of the ``pixelformat`` is V4L2 FourCC code of the data format. |
| 84 | 84 | The ``buffersize`` field is maximum buffer size in bytes required for |
| @@ -43,7 +43,7 @@ transmission arguments. · 42 unmodified lines | ||
| 43 | 43 | 1998-09-28: Revamped video standard. Made video controls individually |
| 44 | 44 | enumerable. |
| 45 | 45 | |
| 46 | −1998-10-02: The ``id`` field was removed from struct | |
| 46 | +1998-10-02: The ``id`` field was removed from | |
| 47 | 47 | struct ``video_standard`` and the color subcarrier fields were |
| 48 | 48 | renamed. The :ref:`VIDIOC_QUERYSTD` ioctl was |
| 49 | 49 | renamed to :ref:`VIDIOC_ENUMSTD`, |
| @@ -260,7 +260,7 @@ multiple tuners into account.) · 210 unmodified lines | ||
| 260 | 260 | |
| 261 | 261 | 2000-09-18: ``V4L2_BUF_TYPE_VBI`` was added. This may *break |
| 262 | 262 | compatibility* as the :ref:`VIDIOC_G_FMT <VIDIOC_G_FMT>` and |
| 263 | −:ref:`VIDIOC_S_FMT <VIDIOC_G_FMT>` ioctls may fail now if the struct | |
| 263 | +:ref:`VIDIOC_S_FMT <VIDIOC_G_FMT>` ioctls may fail now if the | |
| 264 | 264 | struct ``v4l2_fmt`` ``type`` field does not contain |
| 265 | 265 | ``V4L2_BUF_TYPE_VBI``. In the documentation of the struct |
| 266 | 266 | :c:type:`v4l2_vbi_format` ``offset`` field the |
| @@ -69,37 +69,37 @@ Each cell is one byte. · 68 unmodified lines | ||
| 69 | 69 | |
| 70 | 70 | B\ :sub:`00low bits 5--0`\ (bits 5--0) |
| 71 | 71 | |
| 72 | − - R\ :sub:`02low bits 3--0`\ (bits 7--4) | |
| 72 | + - B\ :sub:`02low bits 3--0`\ (bits 7--4) | |
| 73 | 73 | |
| 74 | 74 | G\ :sub:`01low bits 5--2`\ (bits 3--0) |
| 75 | 75 | |
| 76 | 76 | - G\ :sub:`03low bits 5--0`\ (bits 7--2) |
| 77 | 77 | |
| 78 | − R\ :sub:`02low bits 5--4`\ (bits 1--0) | |
| 78 | + B\ :sub:`02low bits 5--4`\ (bits 1--0) | |
| 79 | 79 | |
| 80 | 80 | - .. row 2 |
| 81 | 81 | |
| 82 | 82 | - start + 7 |
| 83 | 83 | |
| 84 | − - G\ :sub:`00high` | |
| 84 | + - G\ :sub:`10high` | |
| 85 | 85 | |
| 86 | − - R\ :sub:`01high` | |
| 86 | + - R\ :sub:`11high` | |
| 87 | 87 | |
| 88 | − - G\ :sub:`02high` | |
| 88 | + - G\ :sub:`12high` | |
| 89 | 89 | |
| 90 | − - R\ :sub:`03high` | |
| 90 | + - R\ :sub:`13high` | |
| 91 | 91 | |
| 92 | − - R\ :sub:`01low bits 1--0`\ (bits 7--6) | |
| 92 | + - R\ :sub:`11low bits 1--0`\ (bits 7--6) | |
| 93 | 93 | |
| 94 | − G\ :sub:`00low bits 5--0`\ (bits 5--0) | |
| 94 | + G\ :sub:`10low bits 5--0`\ (bits 5--0) | |
| 95 | 95 | |
| 96 | − - G\ :sub:`02low bits 3--0`\ (bits 7--4) | |
| 96 | + - G\ :sub:`12low bits 3--0`\ (bits 7--4) | |
| 97 | 97 | |
| 98 | − R\ :sub:`01low bits 5--2`\ (bits 3--0) | |
| 98 | + R\ :sub:`11low bits 5--2`\ (bits 3--0) | |
| 99 | 99 | |
| 100 | − - R\ :sub:`03low bits 5--0`\ (bits 7--2) | |
| 100 | + - R\ :sub:`13low bits 5--0`\ (bits 7--2) | |
| 101 | 101 | |
| 102 | − G\ :sub:`02low bits 5--4`\ (bits 1--0) | |
| 102 | + G\ :sub:`12low bits 5--4`\ (bits 1--0) | |
| 103 | 103 | |
| 104 | 104 | - .. row 3 |
| 105 | 105 | |
| @@ -117,13 +117,13 @@ Each cell is one byte. · 11 unmodified lines | ||
| 117 | 117 | |
| 118 | 118 | B\ :sub:`20low bits 5--0`\ (bits 5--0) |
| 119 | 119 | |
| 120 | − - R\ :sub:`22low bits 3--0`\ (bits 7--4) | |
| 120 | + - B\ :sub:`22low bits 3--0`\ (bits 7--4) | |
| 121 | 121 | |
| 122 | 122 | G\ :sub:`21low bits 5--2`\ (bits 3--0) |
| 123 | 123 | |
| 124 | 124 | - G\ :sub:`23low bits 5--0`\ (bits 7--2) |
| 125 | 125 | |
| 126 | − R\ :sub:`22low bits 5--4`\ (bits 1--0) | |
| 126 | + B\ :sub:`22low bits 5--4`\ (bits 1--0) | |
| 127 | 127 | |
| 128 | 128 | - .. row 4 |
| 129 | 129 | |
| @@ -44,6 +44,11 @@ Single-planar format structure · 43 unmodified lines | ||
| 44 | 44 | inside the stream, when fed to a stateful mem2mem decoder, the fields |
| 45 | 45 | may be zero to rely on the decoder to detect the right values. For more |
| 46 | 46 | details see :ref:`decoder` and format descriptions. |
| 47 | + | |
| 48 | + For compressed formats on the CAPTURE side of a stateful mem2mem | |
| 49 | + encoder, the fields must be zero, since the coded size is expected to | |
| 50 | + be calculated internally by the encoder itself, based on the OUTPUT | |
| 51 | + side. For more details see :ref:`encoder` and format descriptions. | |
| 47 | 52 | * - __u32 |
| 48 | 53 | - ``pixelformat`` |
| 49 | 54 | - The pixel format or type of compression, set by the application. |
| @@ -63,6 +63,7 @@ Authors, in alphabetical order: · 62 unmodified lines | ||
| 63 | 63 | - Figa, Tomasz <tfiga@chromium.org> |
| 64 | 64 | |
| 65 | 65 | - Documented the memory-to-memory decoder interface. |
| 66 | + - Documented the memory-to-memory encoder interface. | |
| 66 | 67 | |
| 67 | 68 | - H Schimek, Michael <mschimek@gmx.at> |
| 68 | 69 | |
| @@ -75,6 +76,7 @@ Authors, in alphabetical order: · 6 unmodified lines | ||
| 75 | 76 | - Osciak, Pawel <posciak@chromium.org> |
| 76 | 77 | |
| 77 | 78 | - Documented the memory-to-memory decoder interface. |
| 79 | + - Documented the memory-to-memory encoder interface. | |
| 78 | 80 | |
| 79 | 81 | - Osciak, Pawel <pawel@osciak.com> |
| 80 | 82 | |
| @@ -121,7 +121,12 @@ than the number requested. · 120 unmodified lines | ||
| 121 | 121 | other changes, then set ``count`` to 0, ``memory`` to |
| 122 | 122 | ``V4L2_MEMORY_MMAP`` and ``format.type`` to the buffer type. |
| 123 | 123 | * - __u32 |
| 124 | − - ``reserved``\ [7] | |
| 124 | + - ``flags`` | |
| 125 | + - Specifies additional buffer management attributes. | |
| 126 | + See :ref:`memory-flags`. | |
| 127 | + | |
| 128 | + * - __u32 | |
| 129 | + - ``reserved``\ [6] | |
| 125 | 130 | - A place holder for future extensions. Drivers and applications |
| 126 | 131 | must set the array to zero. |
| 127 | 132 | |
| @@ -260,7 +260,7 @@ call. · 259 unmodified lines | ||
| 260 | 260 | :ref:`v4l2_queryctrl <v4l2-queryctrl>`. |
| 261 | 261 | * - __s32 |
| 262 | 262 | - ``default_value`` |
| 263 | − - The default value value of the control. See struct | |
| 263 | + - The default value of the control. See struct | |
| 264 | 264 | :ref:`v4l2_queryctrl <v4l2-queryctrl>`. |
| 265 | 265 | |
| 266 | 266 | |
| @@ -51,25 +51,26 @@ To send a command applications must initialize all fields of a struct · 50 unmodified lines | ||
| 51 | 51 | ``VIDIOC_ENCODER_CMD`` or ``VIDIOC_TRY_ENCODER_CMD`` with a pointer to |
| 52 | 52 | this structure. |
| 53 | 53 | |
| 54 | −The ``cmd`` field must contain the command code. The ``flags`` field is | |
| 55 | −currently only used by the STOP command and contains one bit: If the | |
| 56 | −``V4L2_ENC_CMD_STOP_AT_GOP_END`` flag is set, encoding will continue | |
| 57 | −until the end of the current *Group Of Pictures*, otherwise it will stop | |
| 58 | −immediately. | |
| 54 | +The ``cmd`` field must contain the command code. Some commands use the | |
| 55 | +``flags`` field for additional information. | |
| 59 | 56 | |
| 60 | −A :ref:`read() <func-read>` or :ref:`VIDIOC_STREAMON <VIDIOC_STREAMON>` | |
| 61 | −call sends an implicit START command to the encoder if it has not been | |
| 62 | −started yet. After a STOP command, :ref:`read() <func-read>` calls will read | |
| 57 | +After a STOP command, :ref:`read() <func-read>` calls will read | |
| 63 | 58 | the remaining data buffered by the driver. When the buffer is empty, |
| 64 | 59 | :ref:`read() <func-read>` will return zero and the next :ref:`read() <func-read>` |
| 65 | 60 | call will restart the encoder. |
| 66 | 61 | |
| 62 | +A :ref:`read() <func-read>` or :ref:`VIDIOC_STREAMON <VIDIOC_STREAMON>` | |
| 63 | +call sends an implicit START command to the encoder if it has not been | |
| 64 | +started yet. Applies to both queues of mem2mem encoders. | |
| 65 | + | |
| 67 | 66 | A :ref:`close() <func-close>` or :ref:`VIDIOC_STREAMOFF <VIDIOC_STREAMON>` |
| 68 | 67 | call of a streaming file descriptor sends an implicit immediate STOP to |
| 69 | −the encoder, and all buffered data is discarded. | |
| 68 | +the encoder, and all buffered data is discarded. Applies to both queues of | |
| 69 | +mem2mem encoders. | |
| 70 | 70 | |
| 71 | 71 | These ioctls are optional, not all drivers may support them. They were |
| 72 | −introduced in Linux 2.6.21. | |
| 72 | +introduced in Linux 2.6.21. They are, however, mandatory for stateful mem2mem | |
| 73 | +encoders (as further documented in :ref:`encoder`). | |
| 73 | 74 | |
| 74 | 75 | |
| 75 | 76 | .. tabularcolumns:: |p{4.4cm}|p{4.4cm}|p{8.7cm}| |
| @@ -109,21 +110,24 @@ introduced in Linux 2.6.21. · 33 unmodified lines | ||
| 109 | 110 | - 0 |
| 110 | 111 | - Start the encoder. When the encoder is already running or paused, |
| 111 | 112 | this command does nothing. No flags are defined for this command. |
| 113 | + | |
| 114 | + For a device implementing the :ref:`encoder`, once the drain sequence | |
| 115 | + is initiated with the ``V4L2_ENC_CMD_STOP`` command, it must be driven | |
| 116 | + to completion before this command can be invoked. Any attempt to | |
| 117 | + invoke the command while the drain sequence is in progress will trigger | |
| 118 | + an ``EBUSY`` error code. See :ref:`encoder` for more details. | |
| 112 | 119 | * - ``V4L2_ENC_CMD_STOP`` |
| 113 | 120 | - 1 |
| 114 | 121 | - Stop the encoder. When the ``V4L2_ENC_CMD_STOP_AT_GOP_END`` flag |
| 115 | 122 | is set, encoding will continue until the end of the current *Group |
| 116 | 123 | Of Pictures*, otherwise encoding will stop immediately. When the |
| 117 | − encoder is already stopped, this command does nothing. mem2mem | |
| 118 | − encoders will send a ``V4L2_EVENT_EOS`` event when the last frame | |
| 119 | − has been encoded and all frames are ready to be dequeued and will | |
| 120 | − set the ``V4L2_BUF_FLAG_LAST`` buffer flag on the last buffer of | |
| 121 | − the capture queue to indicate there will be no new buffers | |
| 122 | − produced to dequeue. This buffer may be empty, indicated by the | |
| 123 | − driver setting the ``bytesused`` field to 0. Once the | |
| 124 | − ``V4L2_BUF_FLAG_LAST`` flag was set, the | |
| 125 | − :ref:`VIDIOC_DQBUF <VIDIOC_QBUF>` ioctl will not block anymore, | |
| 126 | − but return an ``EPIPE`` error code. | |
| 124 | + encoder is already stopped, this command does nothing. | |
| 125 | + | |
| 126 | + For a device implementing the :ref:`encoder`, the command will initiate | |
| 127 | + the drain sequence as documented in :ref:`encoder`. No flags or other | |
| 128 | + arguments are accepted in this case. Any attempt to invoke the command | |
| 129 | + again before the sequence completes will trigger an ``EBUSY`` error | |
| 130 | + code. | |
| 127 | 131 | * - ``V4L2_ENC_CMD_PAUSE`` |
| 128 | 132 | - 2 |
| 129 | 133 | - Pause the encoder. When the encoder has not been started yet, the |
| @@ -152,6 +156,8 @@ introduced in Linux 2.6.21. · 22 unmodified lines | ||
| 152 | 156 | - Stop encoding at the end of the current *Group Of Pictures*, |
| 153 | 157 | rather than immediately. |
| 154 | 158 | |
| 159 | + Does not apply to :ref:`encoder`. | |
| 160 | + | |
| 155 | 161 | |
| 156 | 162 | Return Value |
| 157 | 163 | ============ |
| @@ -160,6 +166,11 @@ On success 0 is returned, on error -1 and the ``errno`` variable is set · 2 unmodified lines | ||
| 160 | 166 | appropriately. The generic error codes are described at the |
| 161 | 167 | :ref:`Generic Error Codes <gen-errors>` chapter. |
| 162 | 168 | |
| 169 | +EBUSY | |
| 170 | + A drain sequence of a device implementing the :ref:`encoder` is still in | |
| 171 | + progress. It is not allowed to issue another encoder command until it | |
| 172 | + completes. | |
| 173 | + | |
| 163 | 174 | EINVAL |
| 164 | 175 | The ``cmd`` field is invalid. |
| 165 | 176 | |
| @@ -167,17 +167,37 @@ the ``mbus_code`` field is handled differently: · 166 unmodified lines | ||
| 167 | 167 | - The hardware decoder for this compressed bytestream format (aka coded |
| 168 | 168 | format) is capable of parsing a continuous bytestream. Applications do |
| 169 | 169 | not need to parse the bytestream themselves to find the boundaries |
| 170 | − between frames/fields. This flag can only be used in combination with | |
| 171 | − the ``V4L2_FMT_FLAG_COMPRESSED`` flag, since this applies to compressed | |
| 170 | + between frames/fields. | |
| 171 | + | |
| 172 | + This flag can only be used in combination with the | |
| 173 | + ``V4L2_FMT_FLAG_COMPRESSED`` flag, since this applies to compressed | |
| 172 | 174 | formats only. This flag is valid for stateful decoders only. |
| 173 | 175 | * - ``V4L2_FMT_FLAG_DYN_RESOLUTION`` |
| 174 | 176 | - 0x0008 |
| 175 | 177 | - Dynamic resolution switching is supported by the device for this |
| 176 | 178 | compressed bytestream format (aka coded format). It will notify the user |
| 177 | 179 | via the event ``V4L2_EVENT_SOURCE_CHANGE`` when changes in the video |
| 178 | − parameters are detected. This flag can only be used in combination | |
| 179 | − with the ``V4L2_FMT_FLAG_COMPRESSED`` flag, since this applies to | |
| 180 | − compressed formats only. It is also only applies to stateful codecs. | |
| 180 | + parameters are detected. | |
| 181 | + | |
| 182 | + This flag can only be used in combination with the | |
| 183 | + ``V4L2_FMT_FLAG_COMPRESSED`` flag, since this applies to | |
| 184 | + compressed formats only. This flag is valid for stateful codecs only. | |
| 185 | + * - ``V4L2_FMT_FLAG_ENC_CAP_FRAME_INTERVAL`` | |
| 186 | + - 0x0010 | |
| 187 | + - The hardware encoder supports setting the ``CAPTURE`` coded frame | |
| 188 | + interval separately from the ``OUTPUT`` raw frame interval. | |
| 189 | + Setting the ``OUTPUT`` raw frame interval with :ref:`VIDIOC_S_PARM <VIDIOC_G_PARM>` | |
| 190 | + also sets the ``CAPTURE`` coded frame interval to the same value. | |
| 191 | + If this flag is set, then the ``CAPTURE`` coded frame interval can be | |
| 192 | + set to a different value afterwards. This is typically used for | |
| 193 | + offline encoding where the ``OUTPUT`` raw frame interval is used as | |
| 194 | + a hint for reserving hardware encoder resources and the ``CAPTURE`` coded | |
| 195 | + frame interval is the actual frame rate embedded in the encoded video | |
| 196 | + stream. | |
| 197 | + | |
| 198 | + This flag can only be used in combination with the | |
| 199 | + ``V4L2_FMT_FLAG_COMPRESSED`` flag, since this applies to | |
| 200 | + compressed formats only. This flag is valid for stateful encoders only. | |
| 181 | 201 | |
| 182 | 202 | |
| 183 | 203 | Return Value |
| @@ -42,12 +42,13 @@ Arguments · 41 unmodified lines | ||
| 42 | 42 | Description |
| 43 | 43 | =========== |
| 44 | 44 | |
| 45 | −The current video standard determines a nominal number of frames per | |
| 46 | −second. If less than this number of frames is to be captured or output, | |
| 47 | −applications can request frame skipping or duplicating on the driver | |
| 48 | −side. This is especially useful when using the :ref:`read() <func-read>` or | |
| 49 | −:ref:`write() <func-write>`, which are not augmented by timestamps or sequence | |
| 50 | −counters, and to avoid unnecessary data copying. | |
| 45 | +Applications can request a different frame interval. The capture or | |
| 46 | +output device will be reconfigured to support the requested frame | |
| 47 | +interval if possible. Optionally drivers may choose to skip or | |
| 48 | +repeat frames to achieve the requested frame interval. | |
| 49 | + | |
| 50 | +For stateful encoders (see :ref:`encoder`) this represents the | |
| 51 | +frame interval that is typically embedded in the encoded video stream. | |
| 51 | 52 | |
| 52 | 53 | Changing the frame interval shall never change the format. Changing the |
| 53 | 54 | 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 · 3 unmodified lines | ||
| 57 | 58 | section discussing the :ref:`read() <func-read>` function. |
| 58 | 59 | |
| 59 | 60 | To get and set the streaming parameters applications call the |
| 60 | −:ref:`VIDIOC_G_PARM <VIDIOC_G_PARM>` and :ref:`VIDIOC_S_PARM <VIDIOC_G_PARM>` ioctl, respectively. They take a | |
| 61 | +:ref:`VIDIOC_G_PARM <VIDIOC_G_PARM>` and | |
| 62 | +:ref:`VIDIOC_S_PARM <VIDIOC_G_PARM>` ioctl, respectively. They take a | |
| 61 | 63 | pointer to a struct :c:type:`v4l2_streamparm` which contains a |
| 62 | 64 | union holding separate parameters for input and output devices. |
| 63 | 65 | |
| @@ -113,14 +115,21 @@ union holding separate parameters for input and output devices. · 49 unmodified lines | ||
| 113 | 115 | * - struct :c:type:`v4l2_fract` |
| 114 | 116 | - ``timeperframe`` |
| 115 | 117 | - This is the desired period between successive frames captured by |
| 116 | − the driver, in seconds. The field is intended to skip frames on | |
| 117 | − the driver side, saving I/O bandwidth. | |
| 118 | + the driver, in seconds. | |
| 119 | + * - :cspan:`2` | |
| 120 | + | |
| 121 | + This will configure the speed at which the video source (e.g. a sensor) | |
| 122 | + generates video frames. If the speed is fixed, then the driver may | |
| 123 | + choose to skip or repeat frames in order to achieve the requested | |
| 124 | + frame rate. | |
| 125 | + | |
| 126 | + For stateful encoders (see :ref:`encoder`) this represents the | |
| 127 | + frame interval that is typically embedded in the encoded video stream. | |
| 118 | 128 | |
| 119 | 129 | Applications store here the desired frame period, drivers return |
| 120 | − the actual frame period, which must be greater or equal to the | |
| 121 | − nominal frame period determined by the current video standard | |
| 122 | − (struct :c:type:`v4l2_standard` ``frameperiod`` | |
| 123 | − field). Changing the video standard (also implicitly by switching | |
| 130 | + the actual frame period. | |
| 131 | + | |
| 132 | + Changing the video standard (also implicitly by switching | |
| 124 | 133 | the video input) may reset this parameter to the nominal frame |
| 125 | 134 | period. To reset manually applications can just set this field to |
| 126 | 135 | zero. |
| @@ -173,11 +182,15 @@ union holding separate parameters for input and output devices. · 46 unmodified lines | ||
| 173 | 182 | :ref:`write() <func-write>` mode (in streaming mode timestamps |
| 174 | 183 | can be used to throttle the output), saving I/O bandwidth. |
| 175 | 184 | |
| 185 | + For stateful encoders (see :ref:`encoder`) this represents the | |
| 186 | + frame interval that is typically embedded in the encoded video stream | |
| 187 | + and it provides a hint to the encoder of the speed at which raw | |
| 188 | + frames are queued up to the encoder. | |
| 189 | + | |
| 176 | 190 | Applications store here the desired frame period, drivers return |
| 177 | − the actual frame period, which must be greater or equal to the | |
| 178 | − nominal frame period determined by the current video standard | |
| 179 | − (struct :c:type:`v4l2_standard` ``frameperiod`` | |
| 180 | − field). Changing the video standard (also implicitly by switching | |
| 191 | + the actual frame period. | |
| 192 | + | |
| 193 | + Changing the video standard (also implicitly by switching | |
| 181 | 194 | the video output) may reset this parameter to the nominal frame |
| 182 | 195 | period. To reset manually applications can just set this field to |
| 183 | 196 | zero. |
| @@ -216,8 +229,8 @@ union holding separate parameters for input and output devices. · 32 unmodified lines | ||
| 216 | 229 | |
| 217 | 230 | * - ``V4L2_CAP_TIMEPERFRAME`` |
| 218 | 231 | - 0x1000 |
| 219 | − - The frame skipping/repeating controlled by the ``timeperframe`` | |
| 220 | − field is supported. | |
| 232 | + - The frame period can be modified by setting the ``timeperframe`` | |
| 233 | + field. | |
| 221 | 234 | |
| 222 | 235 | |
| 223 | 236 | |
| @@ -168,11 +168,11 @@ specification the ioctl returns an ``EINVAL`` error code. · 167 unmodified lines | ||
| 168 | 168 | - The device supports the :ref:`multi-planar API <planar-apis>` |
| 169 | 169 | through the :ref:`Video Output <output>` interface. |
| 170 | 170 | * - ``V4L2_CAP_VIDEO_M2M`` |
| 171 | − - 0x00004000 | |
| 171 | + - 0x00008000 | |
| 172 | 172 | - The device supports the single-planar API through the Video |
| 173 | 173 | Memory-To-Memory interface. |
| 174 | 174 | * - ``V4L2_CAP_VIDEO_M2M_MPLANE`` |
| 175 | − - 0x00008000 | |
| 175 | + - 0x00004000 | |
| 176 | 176 | - The device supports the :ref:`multi-planar API <planar-apis>` |
| 177 | 177 | through the Video Memory-To-Memory interface. |
| 178 | 178 | * - ``V4L2_CAP_VIDEO_OVERLAY`` |
| @@ -112,10 +112,17 @@ aborting or finishing any DMA in progress, an implicit · 111 unmodified lines | ||
| 112 | 112 | ``V4L2_MEMORY_MMAP`` and ``type`` set to the buffer type. This will |
| 113 | 113 | free any previously allocated buffers, so this is typically something |
| 114 | 114 | that will be done at the start of the application. |
| 115 | + * - union { | |
| 116 | + - (anonymous) | |
| 117 | + * - __u32 | |
| 118 | + - ``flags`` | |
| 119 | + - Specifies additional buffer management attributes. | |
| 120 | + See :ref:`memory-flags`. | |
| 115 | 121 | * - __u32 |
| 116 | 122 | - ``reserved``\ [1] |
| 117 | − - A place holder for future extensions. Drivers and applications | |
| 118 | − must set the array to zero. | |
| 123 | + - Kept for backwards compatibility. Use ``flags`` instead. | |
| 124 | + * - } | |
| 125 | + - | |
| 119 | 126 | |
| 120 | 127 | .. tabularcolumns:: |p{6.1cm}|p{2.2cm}|p{8.7cm}| |
| 121 | 128 | |
| @@ -126,6 +133,7 @@ aborting or finishing any DMA in progress, an implicit · 4 unmodified lines | ||
| 126 | 133 | .. _V4L2-BUF-CAP-SUPPORTS-REQUESTS: |
| 127 | 134 | .. _V4L2-BUF-CAP-SUPPORTS-ORPHANED-BUFS: |
| 128 | 135 | .. _V4L2-BUF-CAP-SUPPORTS-M2M-HOLD-CAPTURE-BUF: |
| 136 | +.. _V4L2-BUF-CAP-SUPPORTS-MMAP-CACHE-HINTS: | |
| 129 | 137 | |
| 130 | 138 | .. cssclass:: longtable |
| 131 | 139 | |
| @@ -156,6 +164,15 @@ aborting or finishing any DMA in progress, an implicit · 24 unmodified lines | ||
| 156 | 164 | - Only valid for stateless decoders. If set, then userspace can set the |
| 157 | 165 | ``V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF`` flag to hold off on returning the |
| 158 | 166 | capture buffer until the OUTPUT timestamp changes. |
| 167 | + * - ``V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS`` | |
| 168 | + - 0x00000040 | |
| 169 | + - This capability is set by the driver to indicate that the queue supports | |
| 170 | + cache and memory management hints. However, it's only valid when the | |
| 171 | + queue is used for :ref:`memory mapping <mmap>` streaming I/O. See | |
| 172 | + :ref:`V4L2_FLAG_MEMORY_NON_CONSISTENT <V4L2-FLAG-MEMORY-NON-CONSISTENT>`, | |
| 173 | + :ref:`V4L2_BUF_FLAG_NO_CACHE_INVALIDATE <V4L2-BUF-FLAG-NO-CACHE-INVALIDATE>` and | |
| 174 | + :ref:`V4L2_BUF_FLAG_NO_CACHE_CLEAN <V4L2-BUF-FLAG-NO-CACHE-CLEAN>`. | |
| 175 | + | |
| 159 | 176 | |
| 160 | 177 | Return Value |
| 161 | 178 | ============ |
| @@ -187,6 +187,7 @@ replace define V4L2_FMT_FLAG_COMPRESSED fmtdesc-flags · 186 unmodified lines | ||
| 187 | 187 | replace define V4L2_FMT_FLAG_EMULATED fmtdesc-flags |
| 188 | 188 | replace define V4L2_FMT_FLAG_CONTINUOUS_BYTESTREAM fmtdesc-flags |
| 189 | 189 | replace define V4L2_FMT_FLAG_DYN_RESOLUTION fmtdesc-flags |
| 190 | +replace define V4L2_FMT_FLAG_ENC_CAP_FRAME_INTERVAL fmtdesc-flags | |
| 190 | 191 | |
| 191 | 192 | # V4L2 timecode types |
| 192 | 193 | replace define V4L2_TC_TYPE_24FPS timecode-type |
| @@ -4141,6 +4141,14 @@ F: drivers/power/supply/cros_usbpd-charger.c · 4140 unmodified lines | ||
| 4141 | 4141 | N: cros_ec |
| 4142 | 4142 | N: cros-ec |
| 4143 | 4143 | |
| 4144 | +CHRONTEL CH7322 CEC DRIVER | |
| 4145 | +M: Jeff Chase <jnchase@google.com> | |
| 4146 | +L: linux-media@vger.kernel.org | |
| 4147 | +S: Maintained | |
| 4148 | +T: git git://linuxtv.org/media_tree.git | |
| 4149 | +F: Documentation/devicetree/bindings/media/i2c/chrontel,ch7322.yaml | |
| 4150 | +F: drivers/media/cec/i2c/ch7322.c | |
| 4151 | + | |
| 4144 | 4152 | CIRRUS LOGIC AUDIO CODEC DRIVERS |
| 4145 | 4153 | M: James Schulman <james.schulman@cirrus.com> |
| 4146 | 4154 | M: David Rhodes <david.rhodes@cirrus.com> |
| @@ -5267,6 +5275,14 @@ T: git git://linuxtv.org/media_tree.git · 1120 unmodified lines | ||
| 5267 | 5275 | F: Documentation/devicetree/bindings/media/i2c/dongwoon,dw9714.txt |
| 5268 | 5276 | F: drivers/media/i2c/dw9714.c |
| 5269 | 5277 | |
| 5278 | +DONGWOON DW9768 LENS VOICE COIL DRIVER | |
| 5279 | +M: Dongchun Zhu <dongchun.zhu@mediatek.com> | |
| 5280 | +L: linux-media@vger.kernel.org | |
| 5281 | +S: Maintained | |
| 5282 | +T: git git://linuxtv.org/media_tree.git | |
| 5283 | +F: Documentation/devicetree/bindings/media/i2c/dongwoon,dw9768.yaml | |
| 5284 | +F: drivers/media/i2c/dw9768.c | |
| 5285 | + | |
| 5270 | 5286 | DONGWOON DW9807 LENS VOICE COIL DRIVER |
| 5271 | 5287 | M: Sakari Ailus <sakari.ailus@linux.intel.com> |
| 5272 | 5288 | L: linux-media@vger.kernel.org |
| @@ -10507,6 +10523,16 @@ F: Documentation/hwmon/max6697.rst · 5234 unmodified lines | ||
| 10507 | 10523 | F: drivers/hwmon/max6697.c |
| 10508 | 10524 | F: include/linux/platform_data/max6697.h |
| 10509 | 10525 | |
| 10526 | +MAX9286 QUAD GMSL DESERIALIZER DRIVER | |
| 10527 | +M: Jacopo Mondi <jacopo+renesas@jmondi.org> | |
| 10528 | +M: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com> | |
| 10529 | +M: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> | |
| 10530 | +M: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se> | |
| 10531 | +L: linux-media@vger.kernel.org | |
| 10532 | +S: Maintained | |
| 10533 | +F: Documentation/devicetree/bindings/media/i2c/maxim,max9286.yaml | |
| 10534 | +F: drivers/media/i2c/max9286.c | |
| 10535 | + | |
| 10510 | 10536 | MAX9860 MONO AUDIO VOICE CODEC DRIVER |
| 10511 | 10537 | M: Peter Rosin <peda@axentia.se> |
| 10512 | 10538 | L: alsa-devel@alsa-project.org (moderated for non-subscribers) |
| @@ -10785,7 +10811,7 @@ L: linux-media@vger.kernel.org · 272 unmodified lines | ||
| 10785 | 10811 | L: linux-renesas-soc@vger.kernel.org |
| 10786 | 10812 | S: Supported |
| 10787 | 10813 | T: git git://linuxtv.org/media_tree.git |
| 10788 | −F: Documentation/devicetree/bindings/media/renesas,fcp.txt | |
| 10814 | +F: Documentation/devicetree/bindings/media/renesas,fcp.yaml | |
| 10789 | 10815 | F: drivers/media/platform/rcar-fcp.c |
| 10790 | 10816 | F: include/media/rcar-fcp.h |
| 10791 | 10817 | |
| @@ -10795,7 +10821,7 @@ L: linux-media@vger.kernel.org · 3 unmodified lines | ||
| 10795 | 10821 | L: linux-renesas-soc@vger.kernel.org |
| 10796 | 10822 | S: Supported |
| 10797 | 10823 | T: git git://linuxtv.org/media_tree.git |
| 10798 | −F: Documentation/devicetree/bindings/media/renesas,fdp1.txt | |
| 10824 | +F: Documentation/devicetree/bindings/media/renesas,fdp1.yaml | |
| 10799 | 10825 | F: drivers/media/platform/rcar_fdp1.c |
| 10800 | 10826 | |
| 10801 | 10827 | MEDIA DRIVERS FOR RENESAS - VIN |
| @@ -10815,7 +10841,7 @@ L: linux-media@vger.kernel.org · 13 unmodified lines | ||
| 10815 | 10841 | L: linux-renesas-soc@vger.kernel.org |
| 10816 | 10842 | S: Supported |
| 10817 | 10843 | T: git git://linuxtv.org/media_tree.git |
| 10818 | −F: Documentation/devicetree/bindings/media/renesas,vsp1.txt | |
| 10844 | +F: Documentation/devicetree/bindings/media/renesas,vsp1.yaml | |
| 10819 | 10845 | F: drivers/media/platform/vsp1/ |
| 10820 | 10846 | |
| 10821 | 10847 | MEDIA DRIVERS FOR ST STV0910 DEMODULATOR ICs |
| @@ -14483,6 +14509,19 @@ L: linux-wireless@vger.kernel.org · 3661 unmodified lines | ||
| 14483 | 14509 | S: Orphan |
| 14484 | 14510 | F: drivers/net/wireless/ray* |
| 14485 | 14511 | |
| 14512 | +RC-CORE / LIRC FRAMEWORK | |
| 14513 | +M: Sean Young <sean@mess.org> | |
| 14514 | +L: linux-media@vger.kernel.org | |
| 14515 | +S: Maintained | |
| 14516 | +W: http://linuxtv.org | |
| 14517 | +T: git git://linuxtv.org/media_tree.git | |
| 14518 | +F: Documentation/driver-api/media/rc-core.rst | |
| 14519 | +F: Documentation/userspace-api/media/rc/ | |
| 14520 | +F: drivers/media/rc/ | |
| 14521 | +F: include/media/rc-map.h | |
| 14522 | +F: include/media/rc-core.h | |
| 14523 | +F: include/uapi/linux/lirc.h | |
| 14524 | + | |
| 14486 | 14525 | RCMM REMOTE CONTROLS DECODER |
| 14487 | 14526 | M: Patrick Lerda <patrick9876@free.fr> |
| 14488 | 14527 | S: Maintained |
| @@ -14499,6 +14538,18 @@ S: Supported · 10 unmodified lines | ||
| 14499 | 14538 | T: git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git dev |
| 14500 | 14539 | F: tools/testing/selftests/rcutorture |
| 14501 | 14540 | |
| 14541 | +RDACM20 Camera Sensor | |
| 14542 | +M: Jacopo Mondi <jacopo+renesas@jmondi.org> | |
| 14543 | +M: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com> | |
| 14544 | +M: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> | |
| 14545 | +M: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se> | |
| 14546 | +L: linux-media@vger.kernel.org | |
| 14547 | +S: Maintained | |
| 14548 | +F: Documentation/devicetree/bindings/media/i2c/imi,rdacm2x-gmsl.yaml | |
| 14549 | +F: drivers/media/i2c/rdacm20.c | |
| 14550 | +F: drivers/media/i2c/max9271.c | |
| 14551 | +F: drivers/media/i2c/max9271.h | |
| 14552 | + | |
| 14502 | 14553 | RDC R-321X SoC |
| 14503 | 14554 | M: Florian Fainelli <florian@openwrt.org> |
| 14504 | 14555 | S: Maintained |
| @@ -15887,13 +15938,6 @@ L: netdev@vger.kernel.org · 1382 unmodified lines | ||
| 15887 | 15938 | S: Maintained |
| 15888 | 15939 | F: drivers/net/ethernet/smsc/smsc9420.* |
| 15889 | 15940 | |
| 15890 | −SOC-CAMERA V4L2 SUBSYSTEM | |
| 15891 | −L: linux-media@vger.kernel.org | |
| 15892 | −S: Orphan | |
| 15893 | −T: git git://linuxtv.org/media_tree.git | |
| 15894 | −F: drivers/staging/media/soc_camera/ | |
| 15895 | −F: include/media/soc_camera.h | |
| 15896 | − | |
| 15897 | 15941 | SOCIONEXT (SNI) AVE NETWORK DRIVER |
| 15898 | 15942 | M: Kunihiko Hayashi <hayashi.kunihiko@socionext.com> |
| 15899 | 15943 | L: netdev@vger.kernel.org |
| @@ -236,8 +236,6 @@ CONFIG_MEDIA_TUNER_CUSTOMISE=y · 235 unmodified lines | ||
| 236 | 236 | # CONFIG_MEDIA_TUNER_MXL5007T is not set |
| 237 | 237 | # CONFIG_MEDIA_TUNER_MC44S803 is not set |
| 238 | 238 | # CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set |
| 239 | −CONFIG_SOC_CAMERA=y | |
| 240 | −CONFIG_SOC_CAMERA_MT9M111=y | |
| 241 | 239 | CONFIG_VIDEO_PXA27x=y |
| 242 | 240 | # CONFIG_V4L_USB_DRIVERS is not set |
| 243 | 241 | CONFIG_RADIO_TEA5764=y |
| @@ -217,8 +217,6 @@ CONFIG_MEDIA_TUNER_CUSTOMISE=y · 216 unmodified lines | ||
| 217 | 217 | # CONFIG_MEDIA_TUNER_MXL5007T is not set |
| 218 | 218 | # CONFIG_MEDIA_TUNER_MC44S803 is not set |
| 219 | 219 | # CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set |
| 220 | −CONFIG_SOC_CAMERA=y | |
| 221 | −CONFIG_SOC_CAMERA_MT9M111=y | |
| 222 | 220 | CONFIG_VIDEO_PXA27x=y |
| 223 | 221 | # CONFIG_V4L_USB_DRIVERS is not set |
| 224 | 222 | # CONFIG_RADIO_ADAPTERS is not set |
| @@ -110,7 +110,6 @@ CONFIG_REGULATOR_MC13892=y · 109 unmodified lines | ||
| 110 | 110 | CONFIG_MEDIA_SUPPORT=y |
| 111 | 111 | CONFIG_MEDIA_CAMERA_SUPPORT=y |
| 112 | 112 | CONFIG_V4L_PLATFORM_DRIVERS=y |
| 113 | −CONFIG_SOC_CAMERA=y | |
| 114 | 113 | CONFIG_V4L_MEM2MEM_DRIVERS=y |
| 115 | 114 | CONFIG_VIDEO_CODA=y |
| 116 | 115 | CONFIG_FB=y |
| @@ -272,7 +272,6 @@ CONFIG_MEDIA_USB_SUPPORT=y · 271 unmodified lines | ||
| 272 | 272 | CONFIG_USB_VIDEO_CLASS=m |
| 273 | 273 | CONFIG_V4L_PLATFORM_DRIVERS=y |
| 274 | 274 | CONFIG_VIDEO_MUX=y |
| 275 | −CONFIG_SOC_CAMERA=y | |
| 276 | 275 | CONFIG_V4L_MEM2MEM_DRIVERS=y |
| 277 | 276 | CONFIG_VIDEO_CODA=m |
| 278 | 277 | CONFIG_VIDEO_IMX_PXP=y |
| @@ -447,11 +447,8 @@ CONFIG_VIDEO_V4L2_SUBDEV_API=y · 446 unmodified lines | ||
| 447 | 447 | CONFIG_MEDIA_USB_SUPPORT=y |
| 448 | 448 | CONFIG_USB_VIDEO_CLASS=m |
| 449 | 449 | CONFIG_V4L_PLATFORM_DRIVERS=y |
| 450 | −CONFIG_SOC_CAMERA=m | |
| 451 | −CONFIG_SOC_CAMERA_PLATFORM=m | |
| 452 | 450 | CONFIG_VIDEO_PXA27x=m |
| 453 | 451 | CONFIG_V4L_MEM2MEM_DRIVERS=y |
| 454 | −CONFIG_SOC_CAMERA_MT9M111=m | |
| 455 | 452 | CONFIG_DRM=m |
| 456 | 453 | CONFIG_FIRMWARE_EDID=y |
| 457 | 454 | CONFIG_FB_TILEBLITTING=y |
| @@ -155,9 +155,7 @@ CONFIG_REGULATOR_PWM=m · 154 unmodified lines | ||
| 155 | 155 | CONFIG_MEDIA_SUPPORT=y |
| 156 | 156 | CONFIG_MEDIA_CAMERA_SUPPORT=y |
| 157 | 157 | CONFIG_V4L_PLATFORM_DRIVERS=y |
| 158 | −CONFIG_SOC_CAMERA=y | |
| 159 | 158 | CONFIG_VIDEO_ATMEL_ISI=y |
| 160 | −CONFIG_SOC_CAMERA_OV2640=m | |
| 161 | 159 | CONFIG_DRM=y |
| 162 | 160 | CONFIG_DRM_ATMEL_HLCDC=y |
| 163 | 161 | CONFIG_DRM_PANEL_SIMPLE=y |
| @@ -19,7 +19,6 @@ · 18 unmodified lines | ||
| 19 | 19 | #include <linux/dma-mapping.h> |
| 20 | 20 | #include <linux/leds.h> |
| 21 | 21 | #include <linux/platform_data/asoc-mx27vis.h> |
| 22 | −#include <media/soc_camera.h> | |
| 23 | 22 | #include <sound/tlv320aic32x4.h> |
| 24 | 23 | #include <asm/mach-types.h> |
| 25 | 24 | #include <asm/mach/arch.h> |
| @@ -191,34 +190,6 @@ static const struct gpio visstrim_m10_gpios[] __initconst = { · 165 unmodified lines | ||
| 191 | 190 | }; |
| 192 | 191 | |
| 193 | 192 | /* Camera */ |
| 194 | −static int visstrim_camera_power(struct device *dev, int on) | |
| 195 | −{ | |
| 196 | − gpio_set_value(TVP5150_PWDN, on); | |
| 197 | − | |
| 198 | − return 0; | |
| 199 | −}; | |
| 200 | − | |
| 201 | −static int visstrim_camera_reset(struct device *dev) | |
| 202 | −{ | |
| 203 | − gpio_set_value(TVP5150_RSTN, 0); | |
| 204 | − ndelay(500); | |
| 205 | − gpio_set_value(TVP5150_RSTN, 1); | |
| 206 | − | |
| 207 | − return 0; | |
| 208 | −}; | |
| 209 | − | |
| 210 | −static struct i2c_board_info visstrim_i2c_camera = { | |
| 211 | − I2C_BOARD_INFO("tvp5150", 0x5d), | |
| 212 | −}; | |
| 213 | − | |
| 214 | −static struct soc_camera_link iclink_tvp5150 = { | |
| 215 | − .bus_id = 0, | |
| 216 | − .board_info = &visstrim_i2c_camera, | |
| 217 | − .i2c_adapter_id = 0, | |
| 218 | − .power = visstrim_camera_power, | |
| 219 | − .reset = visstrim_camera_reset, | |
| 220 | −}; | |
| 221 | − | |
| 222 | 193 | static struct mx2_camera_platform_data visstrim_camera = { |
| 223 | 194 | .flags = MX2_CAMERA_CCIR | MX2_CAMERA_CCIR_INTERLACE | |
| 224 | 195 | MX2_CAMERA_PCLK_SAMPLE_RISING, |
| @@ -549,8 +520,6 @@ static void __init visstrim_m10_late_init(void) · 324 unmodified lines | ||
| 549 | 520 | |
| 550 | 521 | imx_add_platform_device("mx27vis", 0, NULL, 0, &snd_mx27vis_pdata, |
| 551 | 522 | sizeof(snd_mx27vis_pdata)); |
| 552 | − platform_device_register_resndata(NULL, "soc-camera-pdrv", 0, NULL, 0, | |
| 553 | − &iclink_tvp5150, sizeof(iclink_tvp5150)); | |
| 554 | 523 | |
| 555 | 524 | gpio_led_register_device(0, &visstrim_m10_led_data); |
| 556 | 525 | |
| @@ -29,8 +29,6 @@ · 28 unmodified lines | ||
| 29 | 29 | #include <linux/io.h> |
| 30 | 30 | #include <linux/platform_data/gpio-omap.h> |
| 31 | 31 | |
| 32 | −#include <media/soc_camera.h> | |
| 33 | − | |
| 34 | 32 | #include <asm/serial.h> |
| 35 | 33 | #include <asm/mach-types.h> |
| 36 | 34 | #include <asm/mach/arch.h> |
| @@ -40,7 +38,6 @@ · 3 unmodified lines | ||
| 40 | 38 | #include <mach/mux.h> |
| 41 | 39 | |
| 42 | 40 | #include <mach/hardware.h> |
| 43 | −#include "camera.h" | |
| 44 | 41 | #include <mach/usb.h> |
| 45 | 42 | |
| 46 | 43 | #include "ams-delta-fiq.h" |
| @@ -459,12 +456,6 @@ static struct gpiod_lookup_table leds_gpio_table = { · 412 unmodified lines | ||
| 459 | 456 | }, |
| 460 | 457 | }; |
| 461 | 458 | |
| 462 | −static struct i2c_board_info ams_delta_camera_board_info[] = { | |
| 463 | − { | |
| 464 | − I2C_BOARD_INFO("ov6650", 0x60), | |
| 465 | − }, | |
| 466 | −}; | |
| 467 | − | |
| 468 | 459 | #ifdef CONFIG_LEDS_TRIGGERS |
| 469 | 460 | DEFINE_LED_TRIGGER(ams_delta_camera_led_trigger); |
| 470 | 461 | |
| @@ -483,27 +474,6 @@ static int ams_delta_camera_power(struct device *dev, int power) · 12 unmodified lines | ||
| 483 | 474 | #define ams_delta_camera_power NULL |
| 484 | 475 | #endif |
| 485 | 476 | |
| 486 | −static struct soc_camera_link ams_delta_iclink = { | |
| 487 | − .bus_id = 0, /* OMAP1 SoC camera bus */ | |
| 488 | − .i2c_adapter_id = 1, | |
| 489 | − .board_info = &ams_delta_camera_board_info[0], | |
| 490 | − .module_name = "ov6650", | |
| 491 | − .power = ams_delta_camera_power, | |
| 492 | −}; | |
| 493 | − | |
| 494 | −static struct platform_device ams_delta_camera_device = { | |
| 495 | − .name = "soc-camera-pdrv", | |
| 496 | − .id = 0, | |
| 497 | − .dev = { | |
| 498 | − .platform_data = &ams_delta_iclink, | |
| 499 | − }, | |
| 500 | −}; | |
| 501 | − | |
| 502 | −static struct omap1_cam_platform_data ams_delta_camera_platform_data = { | |
| 503 | − .camexclk_khz = 12000, /* default 12MHz clock, no extra DPLL */ | |
| 504 | − .lclk_khz_max = 1334, /* results in 5fps CIF, 10fps QCIF */ | |
| 505 | −}; | |
| 506 | − | |
| 507 | 477 | static struct platform_device ams_delta_audio_device = { |
| 508 | 478 | .name = "ams-delta-audio", |
| 509 | 479 | .id = -1, |
| @@ -598,7 +568,6 @@ static struct platform_device *ams_delta_devices[] __initdata = { · 88 unmodified lines | ||
| 598 | 568 | &latch1_gpio_device, |
| 599 | 569 | &latch2_gpio_device, |
| 600 | 570 | &ams_delta_kp_device, |
| 601 | − &ams_delta_camera_device, | |
| 602 | 571 | &ams_delta_audio_device, |
| 603 | 572 | &ams_delta_serio_device, |
| 604 | 573 | &ams_delta_nand_device, |
| @@ -750,7 +719,6 @@ static void __init ams_delta_init(void) · 145 unmodified lines | ||
| 750 | 719 | omap_register_i2c_bus(1, 100, NULL, 0); |
| 751 | 720 | |
| 752 | 721 | omap1_usb_init(&ams_delta_usb_config); |
| 753 | − omap1_set_camera_info(&ams_delta_camera_platform_data); | |
| 754 | 722 | #ifdef CONFIG_LEDS_TRIGGERS |
| 755 | 723 | led_trigger_register_simple("ams_delta_camera", |
| 756 | 724 | &ams_delta_camera_led_trigger); |
| @@ -1,14 +0,0 @@ | ||
| 1 | −/* SPDX-License-Identifier: GPL-2.0 */ | |
| 2 | −#ifndef __ASM_ARCH_CAMERA_H_ | |
| 3 | −#define __ASM_ARCH_CAMERA_H_ | |
| 4 | − | |
| 5 | −#include <linux/platform_data/media/omap1_camera.h> | |
| 6 | − | |
| 7 | −void omap1_camera_init(void *); | |
| 8 | − | |
| 9 | −static inline void omap1_set_camera_info(struct omap1_cam_platform_data *info) | |
| 10 | −{ | |
| 11 | − omap1_camera_init(info); | |
| 12 | −} | |
| 13 | − | |
| 14 | −#endif /* __ASM_ARCH_CAMERA_H_ */ | |
| @@ -21,7 +21,6 @@ · 20 unmodified lines | ||
| 21 | 21 | #include <mach/mux.h> |
| 22 | 22 | |
| 23 | 23 | #include <mach/omap7xx.h> |
| 24 | −#include "camera.h" | |
| 25 | 24 | #include <mach/hardware.h> |
| 26 | 25 | |
| 27 | 26 | #include "common.h" |
| @@ -258,48 +257,6 @@ static inline void omap_init_spi100k(void) · 230 unmodified lines | ||
| 258 | 257 | } |
| 259 | 258 | #endif |
| 260 | 259 | |
| 261 | − | |
| 262 | −#define OMAP1_CAMERA_BASE 0xfffb6800 | |
| 263 | −#define OMAP1_CAMERA_IOSIZE 0x1c | |
| 264 | − | |
| 265 | −static struct resource omap1_camera_resources[] = { | |
| 266 | − [0] = { | |
| 267 | − .start = OMAP1_CAMERA_BASE, | |
| 268 | − .end = OMAP1_CAMERA_BASE + OMAP1_CAMERA_IOSIZE - 1, | |
| 269 | − .flags = IORESOURCE_MEM, | |
| 270 | − }, | |
| 271 | − [1] = { | |
| 272 | − .start = INT_CAMERA, | |
| 273 | − .flags = IORESOURCE_IRQ, | |
| 274 | − }, | |
| 275 | −}; | |
| 276 | − | |
| 277 | −static u64 omap1_camera_dma_mask = DMA_BIT_MASK(32); | |
| 278 | − | |
| 279 | −static struct platform_device omap1_camera_device = { | |
| 280 | − .name = "omap1-camera", | |
| 281 | − .id = 0, /* This is used to put cameras on this interface */ | |
| 282 | − .dev = { | |
| 283 | − .dma_mask = &omap1_camera_dma_mask, | |
| 284 | − .coherent_dma_mask = DMA_BIT_MASK(32), | |
| 285 | − }, | |
| 286 | − .num_resources = ARRAY_SIZE(omap1_camera_resources), | |
| 287 | − .resource = omap1_camera_resources, | |
| 288 | −}; | |
| 289 | − | |
| 290 | −void __init omap1_camera_init(void *info) | |
| 291 | −{ | |
| 292 | − struct platform_device *dev = &omap1_camera_device; | |
| 293 | − int ret; | |
| 294 | − | |
| 295 | − dev->dev.platform_data = info; | |
| 296 | − | |
| 297 | − ret = platform_device_register(dev); | |
| 298 | − if (ret) | |
| 299 | − dev_err(&dev->dev, "unable to register device: %d\n", ret); | |
| 300 | −} | |
| 301 | − | |
| 302 | − | |
| 303 | 260 | /*-------------------------------------------------------------------------*/ |
| 304 | 261 | |
| 305 | 262 | static inline void omap_init_sti(void) {} |
| @@ -47,8 +47,6 @@ · 46 unmodified lines | ||
| 47 | 47 | #include "pm.h" |
| 48 | 48 | #include <linux/platform_data/media/camera-pxa.h> |
| 49 | 49 | |
| 50 | −#include <media/soc_camera.h> | |
| 51 | − | |
| 52 | 50 | #include "generic.h" |
| 53 | 51 | #include "devices.h" |
| 54 | 52 | |
| @@ -272,115 +270,6 @@ static int __init palmz72_pm_init(void) · 217 unmodified lines | ||
| 272 | 270 | device_initcall(palmz72_pm_init); |
| 273 | 271 | #endif |
| 274 | 272 | |
| 275 | −/****************************************************************************** | |
| 276 | − * SoC Camera | |
| 277 | − ******************************************************************************/ | |
| 278 | −#if defined(CONFIG_SOC_CAMERA_OV9640) || \ | |
| 279 | − defined(CONFIG_SOC_CAMERA_OV9640_MODULE) | |
| 280 | −static struct pxacamera_platform_data palmz72_pxacamera_platform_data = { | |
| 281 | − .flags = PXA_CAMERA_MASTER | PXA_CAMERA_DATAWIDTH_8 | | |
| 282 | − PXA_CAMERA_PCLK_EN | PXA_CAMERA_MCLK_EN, | |
| 283 | − .mclk_10khz = 2600, | |
| 284 | −}; | |
| 285 | − | |
| 286 | −/* Board I2C devices. */ | |
| 287 | −static struct i2c_board_info palmz72_i2c_device[] = { | |
| 288 | − { | |
| 289 | − I2C_BOARD_INFO("ov9640", 0x30), | |
| 290 | − } | |
| 291 | −}; | |
| 292 | − | |
| 293 | −static int palmz72_camera_power(struct device *dev, int power) | |
| 294 | −{ | |
| 295 | − gpio_set_value(GPIO_NR_PALMZ72_CAM_PWDN, !power); | |
| 296 | − mdelay(50); | |
| 297 | − return 0; | |
| 298 | −} | |
| 299 | − | |
| 300 | −static int palmz72_camera_reset(struct device *dev) | |
| 301 | −{ | |
| 302 | − gpio_set_value(GPIO_NR_PALMZ72_CAM_RESET, 1); | |
| 303 | − mdelay(50); | |
| 304 | − gpio_set_value(GPIO_NR_PALMZ72_CAM_RESET, 0); | |
| 305 | − mdelay(50); | |
| 306 | − return 0; | |
| 307 | −} | |
| 308 | − | |
| 309 | −static struct soc_camera_link palmz72_iclink = { | |
| 310 | − .bus_id = 0, /* Match id in pxa27x_device_camera in device.c */ | |
| 311 | − .board_info = &palmz72_i2c_device[0], | |
| 312 | − .i2c_adapter_id = 0, | |
| 313 | − .module_name = "ov96xx", | |
| 314 | − .power = &palmz72_camera_power, | |
| 315 | − .reset = &palmz72_camera_reset, | |
| 316 | − .flags = SOCAM_DATAWIDTH_8, | |
| 317 | −}; | |
| 318 | − | |
| 319 | −static struct gpiod_lookup_table palmz72_i2c_gpiod_table = { | |
| 320 | − .dev_id = "i2c-gpio.0", | |
| 321 | − .table = { | |
| 322 | − GPIO_LOOKUP_IDX("gpio-pxa", 118, NULL, 0, | |
| 323 | − GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN), | |
| 324 | − GPIO_LOOKUP_IDX("gpio-pxa", 117, NULL, 1, | |
| 325 | − GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN), | |
| 326 | − }, | |
| 327 | −}; | |
| 328 | − | |
| 329 | −static struct i2c_gpio_platform_data palmz72_i2c_bus_data = { | |
| 330 | − .udelay = 10, | |
| 331 | − .timeout = 100, | |
| 332 | −}; | |
| 333 | − | |
| 334 | −static struct platform_device palmz72_i2c_bus_device = { | |
| 335 | − .name = "i2c-gpio", | |
| 336 | − .id = 0, /* we use this as a replacement for i2c-pxa */ | |
| 337 | − .dev = { | |
| 338 | − .platform_data = &palmz72_i2c_bus_data, | |
| 339 | − } | |
| 340 | −}; | |
| 341 | − | |
| 342 | −static struct platform_device palmz72_camera = { | |
| 343 | − .name = "soc-camera-pdrv", | |
| 344 | − .id = -1, | |
| 345 | − .dev = { | |
| 346 | − .platform_data = &palmz72_iclink, | |
| 347 | − }, | |
| 348 | −}; | |
| 349 | − | |
| 350 | −/* Here we request the camera GPIOs and configure them. We power up the camera | |
| 351 | − * module, deassert the reset pin, but put it into powerdown (low to no power | |
| 352 | − * consumption) mode. This allows us to later bring the module up fast. */ | |
| 353 | −static struct gpio palmz72_camera_gpios[] = { | |
| 354 | − { GPIO_NR_PALMZ72_CAM_POWER, GPIOF_INIT_HIGH,"Camera DVDD" }, | |
| 355 | − { GPIO_NR_PALMZ72_CAM_RESET, GPIOF_INIT_LOW, "Camera RESET" }, | |
| 356 | − { GPIO_NR_PALMZ72_CAM_PWDN, GPIOF_INIT_LOW, "Camera PWDN" }, | |
| 357 | −}; | |
| 358 | − | |
| 359 | −static inline void __init palmz72_cam_gpio_init(void) | |
| 360 | −{ | |
| 361 | − int ret; | |
| 362 | − | |
| 363 | − ret = gpio_request_array(ARRAY_AND_SIZE(palmz72_camera_gpios)); | |
| 364 | − if (!ret) | |
| 365 | − gpio_free_array(ARRAY_AND_SIZE(palmz72_camera_gpios)); | |
| 366 | − else | |
| 367 | − printk(KERN_ERR "Camera GPIO init failed!\n"); | |
| 368 | − | |
| 369 | − return; | |
| 370 | −} | |
| 371 | − | |
| 372 | −static void __init palmz72_camera_init(void) | |
| 373 | −{ | |
| 374 | − palmz72_cam_gpio_init(); | |
| 375 | − pxa_set_camera_info(&palmz72_pxacamera_platform_data); | |
| 376 | − gpiod_add_lookup_table(&palmz72_i2c_gpiod_table); | |
| 377 | − platform_device_register(&palmz72_i2c_bus_device); | |
| 378 | − platform_device_register(&palmz72_camera); | |
| 379 | −} | |
| 380 | −#else | |
| 381 | −static inline void palmz72_camera_init(void) {} | |
| 382 | −#endif | |
| 383 | − | |
| 384 | 273 | static struct gpiod_lookup_table palmz72_mci_gpio_table = { |
| 385 | 274 | .dev_id = "pxa2xx-mci.0", |
| 386 | 275 | .table = { |
| @@ -416,7 +305,6 @@ static void __init palmz72_init(void) · 29 unmodified lines | ||
| 416 | 305 | palm27x_pmic_init(); |
| 417 | 306 | palmz72_kpc_init(); |
| 418 | 307 | palmz72_leds_init(); |
| 419 | − palmz72_camera_init(); | |
| 420 | 308 | } |
| 421 | 309 | |
| 422 | 310 | MACHINE_START(PALMZ72, "Palm Zire72") |
| @@ -24,10 +24,6 @@ · 23 unmodified lines | ||
| 24 | 24 | #include <linux/pwm.h> |
| 25 | 25 | #include <linux/pwm_backlight.h> |
| 26 | 26 | |
| 27 | −#include <media/i2c/mt9v022.h> | |
| 28 | −#include <media/soc_camera.h> | |
| 29 | − | |
| 30 | −#include <linux/platform_data/media/camera-pxa.h> | |
| 31 | 27 | #include <asm/mach/map.h> |
| 32 | 28 | #include "pxa27x.h" |
| 33 | 29 | #include <mach/audio.h> |
| @@ -374,149 +370,6 @@ static struct pxaohci_platform_data pcm990_ohci_platform_data = { · 340 unmodified lines | ||
| 374 | 370 | .power_on_delay = 10, |
| 375 | 371 | }; |
| 376 | 372 | |
| 377 | −/* | |
| 378 | − * PXA27x Camera specific stuff | |
| 379 | − */ | |
| 380 | −#if defined(CONFIG_VIDEO_PXA27x) || defined(CONFIG_VIDEO_PXA27x_MODULE) | |
| 381 | −static unsigned long pcm990_camera_pin_config[] = { | |
| 382 | − /* CIF */ | |
| 383 | − GPIO98_CIF_DD_0, | |
| 384 | − GPIO105_CIF_DD_1, | |
| 385 | − GPIO104_CIF_DD_2, | |
| 386 | − GPIO103_CIF_DD_3, | |
| 387 | − GPIO95_CIF_DD_4, | |
| 388 | − GPIO94_CIF_DD_5, | |
| 389 | − GPIO93_CIF_DD_6, | |
| 390 | − GPIO108_CIF_DD_7, | |
| 391 | − GPIO107_CIF_DD_8, | |
| 392 | − GPIO106_CIF_DD_9, | |
| 393 | − GPIO42_CIF_MCLK, | |
| 394 | − GPIO45_CIF_PCLK, | |
| 395 | − GPIO43_CIF_FV, | |
| 396 | − GPIO44_CIF_LV, | |
| 397 | −}; | |
| 398 | − | |
| 399 | −/* | |
| 400 | − * CICR4: PCLK_EN: Pixel clock is supplied by the sensor | |
| 401 | − * MCLK_EN: Master clock is generated by PXA | |
| 402 | − * PCP: Data sampled on the falling edge of pixel clock | |
| 403 | − */ | |
| 404 | −struct pxacamera_platform_data pcm990_pxacamera_platform_data = { | |
| 405 | − .flags = PXA_CAMERA_MASTER | PXA_CAMERA_DATAWIDTH_8 | PXA_CAMERA_DATAWIDTH_10 | | |
| 406 | − PXA_CAMERA_PCLK_EN | PXA_CAMERA_MCLK_EN/* | PXA_CAMERA_PCP*/, | |
| 407 | − .mclk_10khz = 1000, | |
| 408 | −}; | |
| 409 | − | |
| 410 | −#include <linux/platform_data/pca953x.h> | |
| 411 | − | |
| 412 | −static struct pca953x_platform_data pca9536_data = { | |
| 413 | − .gpio_base = PXA_NR_BUILTIN_GPIO, | |
| 414 | −}; | |
| 415 | − | |
| 416 | −static int gpio_bus_switch = -EINVAL; | |
| 417 | − | |
| 418 | −static int pcm990_camera_set_bus_param(struct soc_camera_link *link, | |
| 419 | − unsigned long flags) | |
| 420 | −{ | |
| 421 | − if (gpio_bus_switch < 0) { | |
| 422 | − if (flags == SOCAM_DATAWIDTH_10) | |
| 423 | − return 0; | |
| 424 | − else | |
| 425 | − return -EINVAL; | |
| 426 | − } | |
| 427 | − | |
| 428 | − if (flags & SOCAM_DATAWIDTH_8) | |
| 429 | − gpio_set_value_cansleep(gpio_bus_switch, 1); | |
| 430 | − else | |
| 431 | − gpio_set_value_cansleep(gpio_bus_switch, 0); | |
| 432 | − | |
| 433 | − return 0; | |
| 434 | −} | |
| 435 | − | |
| 436 | −static unsigned long pcm990_camera_query_bus_param(struct soc_camera_link *link) | |
| 437 | −{ | |
| 438 | − int ret; | |
| 439 | − | |
| 440 | − if (gpio_bus_switch < 0) { | |
| 441 | − ret = gpio_request(PXA_NR_BUILTIN_GPIO, "camera"); | |
| 442 | − if (!ret) { | |
| 443 | − gpio_bus_switch = PXA_NR_BUILTIN_GPIO; | |
| 444 | − gpio_direction_output(gpio_bus_switch, 0); | |
| 445 | − } | |
| 446 | − } | |
| 447 | − | |
| 448 | − if (gpio_bus_switch >= 0) | |
| 449 | − return SOCAM_DATAWIDTH_8 | SOCAM_DATAWIDTH_10; | |
| 450 | − else | |
| 451 | − return SOCAM_DATAWIDTH_10; | |
| 452 | −} | |
| 453 | − | |
| 454 | −static void pcm990_camera_free_bus(struct soc_camera_link *link) | |
| 455 | −{ | |
| 456 | − if (gpio_bus_switch < 0) | |
| 457 | − return; | |
| 458 | − | |
| 459 | − gpio_free(gpio_bus_switch); | |
| 460 | − gpio_bus_switch = -EINVAL; | |
| 461 | −} | |
| 462 | − | |
| 463 | −/* Board I2C devices. */ | |
| 464 | −static struct i2c_board_info __initdata pcm990_i2c_devices[] = { | |
| 465 | − { | |
| 466 | − /* Must initialize before the camera(s) */ | |
| 467 | − I2C_BOARD_INFO("pca9536", 0x41), | |
| 468 | − .platform_data = &pca9536_data, | |
| 469 | − }, | |
| 470 | −}; | |
| 471 | − | |
| 472 | −static struct mt9v022_platform_data mt9v022_pdata = { | |
| 473 | − .y_skip_top = 1, | |
| 474 | −}; | |
| 475 | − | |
| 476 | −static struct i2c_board_info pcm990_camera_i2c[] = { | |
| 477 | − { | |
| 478 | − I2C_BOARD_INFO("mt9v022", 0x48), | |
| 479 | − }, { | |
| 480 | − I2C_BOARD_INFO("mt9m001", 0x5d), | |
| 481 | − }, | |
| 482 | −}; | |
| 483 | − | |
| 484 | −static struct soc_camera_link iclink[] = { | |
| 485 | − { | |
| 486 | − .bus_id = 0, /* Must match with the camera ID */ | |
| 487 | − .board_info = &pcm990_camera_i2c[0], | |
| 488 | − .priv = &mt9v022_pdata, | |
| 489 | − .i2c_adapter_id = 0, | |
| 490 | − .query_bus_param = pcm990_camera_query_bus_param, | |
| 491 | − .set_bus_param = pcm990_camera_set_bus_param, | |
| 492 | − .free_bus = pcm990_camera_free_bus, | |
| 493 | − }, { | |
| 494 | − .bus_id = 0, /* Must match with the camera ID */ | |
| 495 | − .board_info = &pcm990_camera_i2c[1], | |
| 496 | − .i2c_adapter_id = 0, | |
| 497 | − .query_bus_param = pcm990_camera_query_bus_param, | |
| 498 | − .set_bus_param = pcm990_camera_set_bus_param, | |
| 499 | − .free_bus = pcm990_camera_free_bus, | |
| 500 | − }, | |
| 501 | −}; | |
| 502 | − | |
| 503 | −static struct platform_device pcm990_camera[] = { | |
| 504 | − { | |
| 505 | − .name = "soc-camera-pdrv", | |
| 506 | − .id = 0, | |
| 507 | − .dev = { | |
| 508 | − .platform_data = &iclink[0], | |
| 509 | − }, | |
| 510 | − }, { | |
| 511 | − .name = "soc-camera-pdrv", | |
| 512 | − .id = 1, | |
| 513 | − .dev = { | |
| 514 | − .platform_data = &iclink[1], | |
| 515 | − }, | |
| 516 | − }, | |
| 517 | −}; | |
| 518 | −#endif /* CONFIG_VIDEO_PXA27x ||CONFIG_VIDEO_PXA27x_MODULE */ | |
| 519 | − | |
| 520 | 373 | /* |
| 521 | 374 | * system init for baseboard usage. Will be called by pcm027 init. |
| 522 | 375 | * |
| @@ -551,15 +404,5 @@ void __init pcm990_baseboard_init(void) · 28 unmodified lines | ||
| 551 | 404 | pxa_set_i2c_info(NULL); |
| 552 | 405 | pxa_set_ac97_info(NULL); |
| 553 | 406 | |
| 554 | −#if defined(CONFIG_VIDEO_PXA27x) || defined(CONFIG_VIDEO_PXA27x_MODULE) | |
| 555 | − pxa2xx_mfp_config(ARRAY_AND_SIZE(pcm990_camera_pin_config)); | |
| 556 | − pxa_set_camera_info(&pcm990_pxacamera_platform_data); | |
| 557 | − | |
| 558 | − i2c_register_board_info(0, ARRAY_AND_SIZE(pcm990_i2c_devices)); | |
| 559 | − | |
| 560 | − platform_device_register(&pcm990_camera[0]); | |
| 561 | − platform_device_register(&pcm990_camera[1]); | |
| 562 | −#endif | |
| 563 | − | |
| 564 | 407 | printk(KERN_INFO "PCM-990 Evaluation baseboard initialized\n"); |
| 565 | 408 | } |
| @@ -65,9 +65,6 @@ CONFIG_VIDEO_DEV=y · 64 unmodified lines | ||
| 65 | 65 | # CONFIG_VIDEO_ALLOW_V4L1 is not set |
| 66 | 66 | # CONFIG_MEDIA_TUNER_CUSTOMISE is not set |
| 67 | 67 | CONFIG_VIDEO_HELPER_CHIPS_AUTO=y |
| 68 | −CONFIG_SOC_CAMERA=y | |
| 69 | −CONFIG_SOC_CAMERA_PLATFORM=y | |
| 70 | −CONFIG_SOC_CAMERA_OV772X=y | |
| 71 | 68 | CONFIG_VIDEO_SH_MOBILE_CEU=y |
| 72 | 69 | # CONFIG_RADIO_ADAPTERS is not set |
| 73 | 70 | CONFIG_FB=y |
| @@ -72,9 +72,6 @@ CONFIG_MEDIA_SUPPORT=y · 71 unmodified lines | ||
| 72 | 72 | CONFIG_VIDEO_DEV=y |
| 73 | 73 | # CONFIG_MEDIA_TUNER_CUSTOMISE is not set |
| 74 | 74 | CONFIG_VIDEO_HELPER_CHIPS_AUTO=y |
| 75 | −CONFIG_SOC_CAMERA=y | |
| 76 | −CONFIG_SOC_CAMERA_MT9T112=y | |
| 77 | −CONFIG_SOC_CAMERA_TW9910=y | |
| 78 | 75 | CONFIG_VIDEO_SH_MOBILE_CEU=y |
| 79 | 76 | # CONFIG_V4L_USB_DRIVERS is not set |
| 80 | 77 | CONFIG_FB=y |
| @@ -62,9 +62,6 @@ CONFIG_VIDEO_DEV=y · 61 unmodified lines | ||
| 62 | 62 | # CONFIG_VIDEO_ALLOW_V4L1 is not set |
| 63 | 63 | # CONFIG_MEDIA_TUNER_CUSTOMISE is not set |
| 64 | 64 | CONFIG_VIDEO_HELPER_CHIPS_AUTO=y |
| 65 | −CONFIG_SOC_CAMERA=y | |
| 66 | −CONFIG_SOC_CAMERA_TW9910=y | |
| 67 | −CONFIG_SOC_CAMERA_OV772X=y | |
| 68 | 65 | CONFIG_VIDEO_SH_MOBILE_CEU=y |
| 69 | 66 | # CONFIG_RADIO_ADAPTERS is not set |
| 70 | 67 | CONFIG_FB=y |
| @@ -70,8 +70,6 @@ CONFIG_VIDEO_DEV=y · 69 unmodified lines | ||
| 70 | 70 | CONFIG_DVB_CORE=m |
| 71 | 71 | # CONFIG_MEDIA_TUNER_CUSTOMISE is not set |
| 72 | 72 | CONFIG_VIDEO_HELPER_CHIPS_AUTO=y |
| 73 | −CONFIG_SOC_CAMERA=y | |
| 74 | −CONFIG_SOC_CAMERA_OV772X=y | |
| 75 | 73 | CONFIG_VIDEO_SH_MOBILE_CEU=y |
| 76 | 74 | # CONFIG_RADIO_ADAPTERS is not set |
| 77 | 75 | # CONFIG_DVB_FE_CUSTOMISE is not set |
| @@ -33,6 +33,7 @@ menuconfig MEDIA_CEC_SUPPORT · 32 unmodified lines | ||
| 33 | 33 | adapter that supports HDMI CEC. |
| 34 | 34 | |
| 35 | 35 | if MEDIA_CEC_SUPPORT |
| 36 | +source "drivers/media/cec/i2c/Kconfig" | |
| 36 | 37 | source "drivers/media/cec/platform/Kconfig" |
| 37 | 38 | source "drivers/media/cec/usb/Kconfig" |
| 38 | 39 | endif |
| @@ -1,2 +1,2 @@ | ||
| 1 | 1 | # SPDX-License-Identifier: GPL-2.0 |
| 2 | −obj-y += core/ platform/ usb/ | |
| 2 | +obj-y += core/ i2c/ platform/ usb/ | |
| @@ -1306,7 +1306,6 @@ static int cec_config_log_addr(struct cec_adapter *adap, · 1305 unmodified lines | ||
| 1306 | 1306 | |
| 1307 | 1307 | las->log_addr[idx] = log_addr; |
| 1308 | 1308 | las->log_addr_mask |= 1 << log_addr; |
| 1309 | − adap->phys_addrs[log_addr] = adap->phys_addr; | |
| 1310 | 1309 | return 1; |
| 1311 | 1310 | } |
| 1312 | 1311 | |
| @@ -1324,7 +1323,6 @@ static void cec_adap_unconfigure(struct cec_adapter *adap) · 11 unmodified lines | ||
| 1324 | 1323 | adap->log_addrs.log_addr_mask = 0; |
| 1325 | 1324 | adap->is_configuring = false; |
| 1326 | 1325 | adap->is_configured = false; |
| 1327 | − memset(adap->phys_addrs, 0xff, sizeof(adap->phys_addrs)); | |
| 1328 | 1326 | cec_flush(adap); |
| 1329 | 1327 | wake_up_interruptible(&adap->kthread_waitq); |
| 1330 | 1328 | cec_post_state_event(adap); |
| @@ -1974,8 +1972,6 @@ static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg, · 643 unmodified lines | ||
| 1974 | 1972 | case CEC_MSG_REPORT_PHYSICAL_ADDR: { |
| 1975 | 1973 | u16 pa = (msg->msg[2] << 8) | msg->msg[3]; |
| 1976 | 1974 | |
| 1977 | − if (!from_unregistered) | |
| 1978 | − adap->phys_addrs[init_laddr] = pa; | |
| 1979 | 1975 | dprintk(1, "reported physical address %x.%x.%x.%x for logical address %d\n", |
| 1980 | 1976 | cec_phys_addr_exp(pa), init_laddr); |
| 1981 | 1977 | break; |
| @@ -147,7 +147,13 @@ static long cec_adap_g_log_addrs(struct cec_adapter *adap, · 146 unmodified lines | ||
| 147 | 147 | struct cec_log_addrs log_addrs; |
| 148 | 148 | |
| 149 | 149 | mutex_lock(&adap->lock); |
| 150 | − log_addrs = adap->log_addrs; | |
| 150 | + /* | |
| 151 | + * We use memcpy here instead of assignment since there is a | |
| 152 | + * hole at the end of struct cec_log_addrs that an assignment | |
| 153 | + * might ignore. So when we do copy_to_user() we could leak | |
| 154 | + * one byte of memory. | |
| 155 | + */ | |
| 156 | + memcpy(&log_addrs, &adap->log_addrs, sizeof(log_addrs)); | |
| 151 | 157 | if (!adap->is_configured) |
| 152 | 158 | memset(log_addrs.log_addr, CEC_LOG_ADDR_INVALID, |
| 153 | 159 | sizeof(log_addrs.log_addr)); |
| @@ -265,7 +265,6 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, · 264 unmodified lines | ||
| 265 | 265 | adap->sequence = 0; |
| 266 | 266 | adap->ops = ops; |
| 267 | 267 | adap->priv = priv; |
| 268 | − memset(adap->phys_addrs, 0xff, sizeof(adap->phys_addrs)); | |
| 269 | 268 | mutex_init(&adap->lock); |
| 270 | 269 | INIT_LIST_HEAD(&adap->transmit_queue); |
| 271 | 270 | INIT_LIST_HEAD(&adap->wait_queue); |
| @@ -116,7 +116,8 @@ cec_notifier_conn_register(struct device *hdmi_dev, const char *port_name, · 115 unmodified lines | ||
| 116 | 116 | else |
| 117 | 117 | memset(&n->conn_info, 0, sizeof(n->conn_info)); |
| 118 | 118 | if (n->cec_adap) { |
| 119 | − cec_phys_addr_invalidate(n->cec_adap); | |
| 119 | + if (!n->cec_adap->adap_controls_phys_addr) | |
| 120 | + cec_phys_addr_invalidate(n->cec_adap); | |
| 120 | 121 | cec_s_conn_info(n->cec_adap, conn_info); |
| 121 | 122 | } |
| 122 | 123 | mutex_unlock(&n->lock); |
| @@ -133,7 +134,8 @@ void cec_notifier_conn_unregister(struct cec_notifier *n) · 10 unmodified lines | ||
| 133 | 134 | memset(&n->conn_info, 0, sizeof(n->conn_info)); |
| 134 | 135 | n->phys_addr = CEC_PHYS_ADDR_INVALID; |
| 135 | 136 | if (n->cec_adap) { |
| 136 | − cec_phys_addr_invalidate(n->cec_adap); | |
| 137 | + if (!n->cec_adap->adap_controls_phys_addr) | |
| 138 | + cec_phys_addr_invalidate(n->cec_adap); | |
| 137 | 139 | cec_s_conn_info(n->cec_adap, NULL); |
| 138 | 140 | } |
| 139 | 141 | mutex_unlock(&n->lock); |
| @@ -158,7 +160,8 @@ cec_notifier_cec_adap_register(struct device *hdmi_dev, const char *port_name, · 18 unmodified lines | ||
| 158 | 160 | n->cec_adap = adap; |
| 159 | 161 | adap->conn_info = n->conn_info; |
| 160 | 162 | adap->notifier = n; |
| 161 | − cec_s_phys_addr(adap, n->phys_addr, false); | |
| 163 | + if (!adap->adap_controls_phys_addr) | |
| 164 | + cec_s_phys_addr(adap, n->phys_addr, false); | |
| 162 | 165 | mutex_unlock(&n->lock); |
| 163 | 166 | return n; |
| 164 | 167 | } |
| @@ -185,7 +188,7 @@ void cec_notifier_set_phys_addr(struct cec_notifier *n, u16 pa) · 20 unmodified lines | ||
| 185 | 188 | |
| 186 | 189 | mutex_lock(&n->lock); |
| 187 | 190 | n->phys_addr = pa; |
| 188 | − if (n->cec_adap) | |
| 191 | + if (n->cec_adap && !n->cec_adap->adap_controls_phys_addr) | |
| 189 | 192 | cec_s_phys_addr(n->cec_adap, n->phys_addr, false); |
| 190 | 193 | mutex_unlock(&n->lock); |
| 191 | 194 | } |
| @@ -0,0 +1,14 @@ | ||
| 1 | +# SPDX-License-Identifier: GPL-2.0 | |
| 2 | +# | |
| 3 | +# I2C drivers | |
| 4 | + | |
| 5 | +config CEC_CH7322 | |
| 6 | + tristate "Chrontel CH7322 CEC controller" | |
| 7 | + depends on I2C | |
| 8 | + select REGMAP_I2C | |
| 9 | + select CEC_CORE | |
| 10 | + help | |
| 11 | + This is a driver for the Chrontel CH7322 CEC controller. It uses the | |
| 12 | + generic CEC framework interface. | |
| 13 | + CEC bus is present in the HDMI connector and enables communication | |
| 14 | + between compatible devices. | |
| @@ -0,0 +1,5 @@ | ||
| 1 | +# SPDX-License-Identifier: GPL-2.0 | |
| 2 | +# | |
| 3 | +# Makefile for the CEC I2C device drivers. | |
| 4 | +# | |
| 5 | +obj-$(CONFIG_CEC_CH7322) += ch7322.o | |
| @@ -0,0 +1,604 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +/* | |
| 3 | + * Driver for the Chrontel CH7322 CEC Controller | |
| 4 | + * | |
| 5 | + * Copyright 2020 Google LLC. | |
| 6 | + */ | |
| 7 | + | |
| 8 | +/* | |
| 9 | + * Notes | |
| 10 | + * | |
| 11 | + * - This device powers on in Auto Mode which has limited functionality. This | |
| 12 | + * driver disables Auto Mode when it attaches. | |
| 13 | + * | |
| 14 | + */ | |
| 15 | + | |
| 16 | +#include <linux/cec.h> | |
| 17 | +#include <linux/dmi.h> | |
| 18 | +#include <linux/i2c.h> | |
| 19 | +#include <linux/interrupt.h> | |
| 20 | +#include <linux/module.h> | |
| 21 | +#include <linux/mutex.h> | |
| 22 | +#include <linux/pci.h> | |
| 23 | +#include <linux/regmap.h> | |
| 24 | +#include <media/cec.h> | |
| 25 | +#include <media/cec-notifier.h> | |
| 26 | + | |
| 27 | +#define CH7322_WRITE 0x00 | |
| 28 | +#define CH7322_WRITE_MSENT 0x80 | |
| 29 | +#define CH7322_WRITE_BOK 0x40 | |
| 30 | +#define CH7322_WRITE_NMASK 0x0f | |
| 31 | + | |
| 32 | +/* Write buffer is 0x01-0x10 */ | |
| 33 | +#define CH7322_WRBUF 0x01 | |
| 34 | +#define CH7322_WRBUF_LEN 0x10 | |
| 35 | + | |
| 36 | +#define CH7322_READ 0x40 | |
| 37 | +#define CH7322_READ_NRDT 0x80 | |
| 38 | +#define CH7322_READ_MSENT 0x20 | |
| 39 | +#define CH7322_READ_NMASK 0x0f | |
| 40 | + | |
| 41 | +/* Read buffer is 0x41-0x50 */ | |
| 42 | +#define CH7322_RDBUF 0x41 | |
| 43 | +#define CH7322_RDBUF_LEN 0x10 | |
| 44 | + | |
| 45 | +#define CH7322_MODE 0x11 | |
| 46 | +#define CH7322_MODE_AUTO 0x78 | |
| 47 | +#define CH7322_MODE_SW 0xb5 | |
| 48 | + | |
| 49 | +#define CH7322_RESET 0x12 | |
| 50 | +#define CH7322_RESET_RST 0x00 | |
| 51 | + | |
| 52 | +#define CH7322_POWER 0x13 | |
| 53 | +#define CH7322_POWER_FPD 0x04 | |
| 54 | + | |
| 55 | +#define CH7322_CFG0 0x17 | |
| 56 | +#define CH7322_CFG0_EOBEN 0x40 | |
| 57 | +#define CH7322_CFG0_PEOB 0x20 | |
| 58 | +#define CH7322_CFG0_CLRSPP 0x10 | |
| 59 | +#define CH7322_CFG0_FLOW 0x08 | |
| 60 | + | |
| 61 | +#define CH7322_CFG1 0x1a | |
| 62 | +#define CH7322_CFG1_STDBYO 0x04 | |
| 63 | +#define CH7322_CFG1_HPBP 0x02 | |
| 64 | +#define CH7322_CFG1_PIO 0x01 | |
| 65 | + | |
| 66 | +#define CH7322_INTCTL 0x1b | |
| 67 | +#define CH7322_INTCTL_INTPB 0x80 | |
| 68 | +#define CH7322_INTCTL_STDBY 0x40 | |
| 69 | +#define CH7322_INTCTL_HPDFALL 0x20 | |
| 70 | +#define CH7322_INTCTL_HPDRISE 0x10 | |
| 71 | +#define CH7322_INTCTL_RXMSG 0x08 | |
| 72 | +#define CH7322_INTCTL_TXMSG 0x04 | |
| 73 | +#define CH7322_INTCTL_NEWPHA 0x02 | |
| 74 | +#define CH7322_INTCTL_ERROR 0x01 | |
| 75 | + | |
| 76 | +#define CH7322_DVCLKFNH 0x1d | |
| 77 | +#define CH7322_DVCLKFNL 0x1e | |
| 78 | + | |
| 79 | +#define CH7322_CTL 0x31 | |
| 80 | +#define CH7322_CTL_FSTDBY 0x80 | |
| 81 | +#define CH7322_CTL_PLSEN 0x40 | |
| 82 | +#define CH7322_CTL_PLSPB 0x20 | |
| 83 | +#define CH7322_CTL_SPADL 0x10 | |
| 84 | +#define CH7322_CTL_HINIT 0x08 | |
| 85 | +#define CH7322_CTL_WPHYA 0x04 | |
| 86 | +#define CH7322_CTL_H1T 0x02 | |
| 87 | +#define CH7322_CTL_S1T 0x01 | |
| 88 | + | |
| 89 | +#define CH7322_PAWH 0x32 | |
| 90 | +#define CH7322_PAWL 0x33 | |
| 91 | + | |
| 92 | +#define CH7322_ADDLW 0x34 | |
| 93 | +#define CH7322_ADDLW_MASK 0xf0 | |
| 94 | + | |
| 95 | +#define CH7322_ADDLR 0x3d | |
| 96 | +#define CH7322_ADDLR_HPD 0x80 | |
| 97 | +#define CH7322_ADDLR_MASK 0x0f | |
| 98 | + | |
| 99 | +#define CH7322_INTDATA 0x3e | |
| 100 | +#define CH7322_INTDATA_MODE 0x80 | |
| 101 | +#define CH7322_INTDATA_STDBY 0x40 | |
| 102 | +#define CH7322_INTDATA_HPDFALL 0x20 | |
| 103 | +#define CH7322_INTDATA_HPDRISE 0x10 | |
| 104 | +#define CH7322_INTDATA_RXMSG 0x08 | |
| 105 | +#define CH7322_INTDATA_TXMSG 0x04 | |
| 106 | +#define CH7322_INTDATA_NEWPHA 0x02 | |
| 107 | +#define CH7322_INTDATA_ERROR 0x01 | |
| 108 | + | |
| 109 | +#define CH7322_EVENT 0x3f | |
| 110 | +#define CH7322_EVENT_TXERR 0x80 | |
| 111 | +#define CH7322_EVENT_HRST 0x40 | |
| 112 | +#define CH7322_EVENT_HFST 0x20 | |
| 113 | +#define CH7322_EVENT_PHACHG 0x10 | |
| 114 | +#define CH7322_EVENT_ACTST 0x08 | |
| 115 | +#define CH7322_EVENT_PHARDY 0x04 | |
| 116 | +#define CH7322_EVENT_BSOK 0x02 | |
| 117 | +#define CH7322_EVENT_ERRADCF 0x01 | |
| 118 | + | |
| 119 | +#define CH7322_DID 0x51 | |
| 120 | +#define CH7322_DID_CH7322 0x5b | |
| 121 | +#define CH7322_DID_CH7323 0x5f | |
| 122 | + | |
| 123 | +#define CH7322_REVISIONID 0x52 | |
| 124 | + | |
| 125 | +#define CH7322_PARH 0x53 | |
| 126 | +#define CH7322_PARL 0x54 | |
| 127 | + | |
| 128 | +#define CH7322_IOCFG2 0x75 | |
| 129 | +#define CH7322_IOCFG_CIO 0x80 | |
| 130 | +#define CH7322_IOCFG_IOCFGMASK 0x78 | |
| 131 | +#define CH7322_IOCFG_AUDIO 0x04 | |
| 132 | +#define CH7322_IOCFG_SPAMST 0x02 | |
| 133 | +#define CH7322_IOCFG_SPAMSP 0x01 | |
| 134 | + | |
| 135 | +#define CH7322_CTL3 0x7b | |
| 136 | +#define CH7322_CTL3_SWENA 0x80 | |
| 137 | +#define CH7322_CTL3_FC_INIT 0x40 | |
| 138 | +#define CH7322_CTL3_SML_FL 0x20 | |
| 139 | +#define CH7322_CTL3_SM_RDST 0x10 | |
| 140 | +#define CH7322_CTL3_SPP_CIAH 0x08 | |
| 141 | +#define CH7322_CTL3_SPP_CIAL 0x04 | |
| 142 | +#define CH7322_CTL3_SPP_ACTH 0x02 | |
| 143 | +#define CH7322_CTL3_SPP_ACTL 0x01 | |
| 144 | + | |
| 145 | +/* BOK status means NACK */ | |
| 146 | +#define CH7322_TX_FLAG_NACK BIT(0) | |
| 147 | +/* Device will retry automatically */ | |
| 148 | +#define CH7322_TX_FLAG_RETRY BIT(1) | |
| 149 | + | |
| 150 | +struct ch7322 { | |
| 151 | + struct i2c_client *i2c; | |
| 152 | + struct regmap *regmap; | |
| 153 | + struct cec_adapter *cec; | |
| 154 | + struct mutex mutex; /* device access mutex */ | |
| 155 | + u8 tx_flags; | |
| 156 | +}; | |
| 157 | + | |
| 158 | +static const struct regmap_config ch7322_regmap = { | |
| 159 | + .reg_bits = 8, | |
| 160 | + .val_bits = 8, | |
| 161 | + .max_register = 0x7f, | |
| 162 | + .disable_locking = true, | |
| 163 | +}; | |
| 164 | + | |
| 165 | +static int ch7322_send_message(struct ch7322 *ch7322, const struct cec_msg *msg) | |
| 166 | +{ | |
| 167 | + unsigned int val; | |
| 168 | + unsigned int len = msg->len; | |
| 169 | + int ret; | |
| 170 | + int i; | |
| 171 | + | |
| 172 | + WARN_ON(!mutex_is_locked(&ch7322->mutex)); | |
| 173 | + | |
| 174 | + if (len > CH7322_WRBUF_LEN || len < 1) | |
| 175 | + return -EINVAL; | |
| 176 | + | |
| 177 | + ret = regmap_read(ch7322->regmap, CH7322_WRITE, &val); | |
| 178 | + if (ret) | |
| 179 | + return ret; | |
| 180 | + | |
| 181 | + /* Buffer not ready */ | |
| 182 | + if (!(val & CH7322_WRITE_MSENT)) | |
| 183 | + return -EBUSY; | |
| 184 | + | |
| 185 | + if (cec_msg_opcode(msg) == -1 && | |
| 186 | + cec_msg_initiator(msg) == cec_msg_destination(msg)) { | |
| 187 | + ch7322->tx_flags = CH7322_TX_FLAG_NACK | CH7322_TX_FLAG_RETRY; | |
| 188 | + } else if (cec_msg_is_broadcast(msg)) { | |
| 189 | + ch7322->tx_flags = CH7322_TX_FLAG_NACK; | |
| 190 | + } else { | |
| 191 | + ch7322->tx_flags = CH7322_TX_FLAG_RETRY; | |
| 192 | + } | |
| 193 | + | |
| 194 | + ret = regmap_write(ch7322->regmap, CH7322_WRITE, len - 1); | |
| 195 | + if (ret) | |
| 196 | + return ret; | |
| 197 | + | |
| 198 | + for (i = 0; i < len; i++) { | |
| 199 | + ret = regmap_write(ch7322->regmap, | |
| 200 | + CH7322_WRBUF + i, msg->msg[i]); | |
| 201 | + if (ret) | |
| 202 | + return ret; | |
| 203 | + } | |
| 204 | + | |
| 205 | + return 0; | |
| 206 | +} | |
| 207 | + | |
| 208 | +static int ch7322_receive_message(struct ch7322 *ch7322, struct cec_msg *msg) | |
| 209 | +{ | |
| 210 | + unsigned int val; | |
| 211 | + int ret = 0; | |
| 212 | + int i; | |
| 213 | + | |
| 214 | + WARN_ON(!mutex_is_locked(&ch7322->mutex)); | |
| 215 | + | |
| 216 | + ret = regmap_read(ch7322->regmap, CH7322_READ, &val); | |
| 217 | + if (ret) | |
| 218 | + return ret; | |
| 219 | + | |
| 220 | + /* Message not ready */ | |
| 221 | + if (!(val & CH7322_READ_NRDT)) | |
| 222 | + return -EIO; | |
| 223 | + | |
| 224 | + msg->len = (val & CH7322_READ_NMASK) + 1; | |
| 225 | + | |
| 226 | + /* Read entire RDBUF to clear state */ | |
| 227 | + for (i = 0; i < CH7322_RDBUF_LEN; i++) { | |
| 228 | + ret = regmap_read(ch7322->regmap, CH7322_RDBUF + i, &val); | |
| 229 | + if (ret) | |
| 230 | + return ret; | |
| 231 | + msg->msg[i] = (u8)val; | |
| 232 | + } | |
| 233 | + | |
| 234 | + return 0; | |
| 235 | +} | |
| 236 | + | |
| 237 | +static void ch7322_tx_done(struct ch7322 *ch7322) | |
| 238 | +{ | |
| 239 | + int ret; | |
| 240 | + unsigned int val; | |
| 241 | + u8 status, flags; | |
| 242 | + | |
| 243 | + mutex_lock(&ch7322->mutex); | |
| 244 | + ret = regmap_read(ch7322->regmap, CH7322_WRITE, &val); | |
| 245 | + flags = ch7322->tx_flags; | |
| 246 | + mutex_unlock(&ch7322->mutex); | |
| 247 | + | |
| 248 | + /* | |
| 249 | + * The device returns a one-bit OK status which usually means ACK but | |
| 250 | + * actually means NACK when sending a logical address query or a | |
| 251 | + * broadcast. | |
| 252 | + */ | |
| 253 | + if (ret) | |
| 254 | + status = CEC_TX_STATUS_ERROR; | |
| 255 | + else if ((val & CH7322_WRITE_BOK) && (flags & CH7322_TX_FLAG_NACK)) | |
| 256 | + status = CEC_TX_STATUS_NACK; | |
| 257 | + else if (val & CH7322_WRITE_BOK) | |
| 258 | + status = CEC_TX_STATUS_OK; | |
| 259 | + else if (flags & CH7322_TX_FLAG_NACK) | |
| 260 | + status = CEC_TX_STATUS_OK; | |
| 261 | + else | |
| 262 | + status = CEC_TX_STATUS_NACK; | |
| 263 | + | |
| 264 | + if (status == CEC_TX_STATUS_NACK && (flags & CH7322_TX_FLAG_RETRY)) | |
| 265 | + status |= CEC_TX_STATUS_MAX_RETRIES; | |
| 266 | + | |
| 267 | + cec_transmit_attempt_done(ch7322->cec, status); | |
| 268 | +} | |
| 269 | + | |
| 270 | +static void ch7322_rx_done(struct ch7322 *ch7322) | |
| 271 | +{ | |
| 272 | + struct cec_msg msg; | |
| 273 | + int ret; | |
| 274 | + | |
| 275 | + mutex_lock(&ch7322->mutex); | |
| 276 | + ret = ch7322_receive_message(ch7322, &msg); | |
| 277 | + mutex_unlock(&ch7322->mutex); | |
| 278 | + | |
| 279 | + if (ret) | |
| 280 | + dev_err(&ch7322->i2c->dev, "cec receive error: %d\n", ret); | |
| 281 | + else | |
| 282 | + cec_received_msg(ch7322->cec, &msg); | |
| 283 | +} | |
| 284 | + | |
| 285 | +/* | |
| 286 | + * This device can either monitor the DDC lines to obtain the physical address | |
| 287 | + * or it can allow the host to program it. This driver lets the device obtain | |
| 288 | + * it. | |
| 289 | + */ | |
| 290 | +static void ch7322_phys_addr(struct ch7322 *ch7322) | |
| 291 | +{ | |
| 292 | + unsigned int pah, pal; | |
| 293 | + int ret = 0; | |
| 294 | + | |
| 295 | + mutex_lock(&ch7322->mutex); | |
| 296 | + ret |= regmap_read(ch7322->regmap, CH7322_PARH, &pah); | |
| 297 | + ret |= regmap_read(ch7322->regmap, CH7322_PARL, &pal); | |
| 298 | + mutex_unlock(&ch7322->mutex); | |
| 299 | + | |
| 300 | + if (ret) | |
| 301 | + dev_err(&ch7322->i2c->dev, "phys addr error\n"); | |
| 302 | + else | |
| 303 | + cec_s_phys_addr(ch7322->cec, pal | (pah << 8), false); | |
| 304 | +} | |
| 305 | + | |
| 306 | +static irqreturn_t ch7322_irq(int irq, void *dev) | |
| 307 | +{ | |
| 308 | + struct ch7322 *ch7322 = dev; | |
| 309 | + unsigned int data = 0; | |
| 310 | + | |
| 311 | + mutex_lock(&ch7322->mutex); | |
| 312 | + regmap_read(ch7322->regmap, CH7322_INTDATA, &data); | |
| 313 | + regmap_write(ch7322->regmap, CH7322_INTDATA, data); | |
| 314 | + mutex_unlock(&ch7322->mutex); | |
| 315 | + | |
| 316 | + if (data & CH7322_INTDATA_HPDFALL) | |
| 317 | + cec_phys_addr_invalidate(ch7322->cec); | |
| 318 | + | |
| 319 | + if (data & CH7322_INTDATA_TXMSG) | |
| 320 | + ch7322_tx_done(ch7322); | |
| 321 | + | |
| 322 | + if (data & CH7322_INTDATA_RXMSG) | |
| 323 | + ch7322_rx_done(ch7322); | |
| 324 | + | |
| 325 | + if (data & CH7322_INTDATA_NEWPHA) | |
| 326 | + ch7322_phys_addr(ch7322); | |
| 327 | + | |
| 328 | + if (data & CH7322_INTDATA_ERROR) | |
| 329 | + dev_dbg(&ch7322->i2c->dev, "unknown error\n"); | |
| 330 | + | |
| 331 | + return IRQ_HANDLED; | |
| 332 | +} | |
| 333 | + | |
| 334 | +/* This device is always enabled */ | |
| 335 | +static int ch7322_cec_adap_enable(struct cec_adapter *adap, bool enable) | |
| 336 | +{ | |
| 337 | + return 0; | |
| 338 | +} | |
| 339 | + | |
| 340 | +static int ch7322_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr) | |
| 341 | +{ | |
| 342 | + struct ch7322 *ch7322 = cec_get_drvdata(adap); | |
| 343 | + int ret; | |
| 344 | + | |
| 345 | + mutex_lock(&ch7322->mutex); | |
| 346 | + ret = regmap_update_bits(ch7322->regmap, CH7322_ADDLW, | |
| 347 | + CH7322_ADDLW_MASK, log_addr << 4); | |
| 348 | + mutex_unlock(&ch7322->mutex); | |
| 349 | + | |
| 350 | + return ret; | |
| 351 | +} | |
| 352 | + | |
| 353 | +static int ch7322_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, | |
| 354 | + u32 signal_free_time, struct cec_msg *msg) | |
| 355 | +{ | |
| 356 | + struct ch7322 *ch7322 = cec_get_drvdata(adap); | |
| 357 | + int ret; | |
| 358 | + | |
| 359 | + mutex_lock(&ch7322->mutex); | |
| 360 | + ret = ch7322_send_message(ch7322, msg); | |
| 361 | + mutex_unlock(&ch7322->mutex); | |
| 362 | + | |
| 363 | + return ret; | |
| 364 | +} | |
| 365 | + | |
| 366 | +static const struct cec_adap_ops ch7322_cec_adap_ops = { | |
| 367 | + .adap_enable = ch7322_cec_adap_enable, | |
| 368 | + .adap_log_addr = ch7322_cec_adap_log_addr, | |
| 369 | + .adap_transmit = ch7322_cec_adap_transmit, | |
| 370 | +}; | |
| 371 | + | |
| 372 | +#if IS_ENABLED(CONFIG_PCI) && IS_ENABLED(CONFIG_DMI) | |
| 373 | + | |
| 374 | +struct ch7322_conn_match { | |
| 375 | + const char *dev_name; | |
| 376 | + const char *pci_name; | |
| 377 | + const char *port_name; | |
| 378 | +}; | |
| 379 | + | |
| 380 | +static struct ch7322_conn_match google_endeavour[] = { | |
| 381 | + { "i2c-PRP0001:00", "0000:00:02.0", "Port B" }, | |
| 382 | + { "i2c-PRP0001:01", "0000:00:02.0", "Port C" }, | |
| 383 | + { }, | |
| 384 | +}; | |
| 385 | + | |
| 386 | +static const struct dmi_system_id ch7322_dmi_table[] = { | |
| 387 | + { | |
| 388 | + .matches = { | |
| 389 | + DMI_MATCH(DMI_BOARD_VENDOR, "Google"), | |
| 390 | + DMI_MATCH(DMI_BOARD_NAME, "Endeavour"), | |
| 391 | + }, | |
| 392 | + .driver_data = google_endeavour, | |
| 393 | + }, | |
| 394 | + { }, | |
| 395 | +}; | |
| 396 | + | |
| 397 | +/* Make a best-effort attempt to locate a matching HDMI port */ | |
| 398 | +static int ch7322_get_port(struct i2c_client *client, | |
| 399 | + struct device **dev, | |
| 400 | + const char **port) | |
| 401 | +{ | |
| 402 | + const struct dmi_system_id *system; | |
| 403 | + const struct ch7322_conn_match *conn; | |
| 404 | + | |
| 405 | + *dev = NULL; | |
| 406 | + *port = NULL; | |
| 407 | + | |
| 408 | + system = dmi_first_match(ch7322_dmi_table); | |
| 409 | + if (!system) | |
| 410 | + return 0; | |
| 411 | + | |
| 412 | + for (conn = system->driver_data; conn->dev_name; conn++) { | |
| 413 | + if (!strcmp(dev_name(&client->dev), conn->dev_name)) { | |
| 414 | + struct device *d; | |
| 415 | + | |
| 416 | + d = bus_find_device_by_name(&pci_bus_type, NULL, | |
| 417 | + conn->pci_name); | |
| 418 | + if (!d) | |
| 419 | + return -EPROBE_DEFER; | |
| 420 | + | |
| 421 | + put_device(d); | |
| 422 | + | |
| 423 | + *dev = d; | |
| 424 | + *port = conn->port_name; | |
| 425 | + | |
| 426 | + return 0; | |
| 427 | + } | |
| 428 | + } | |
| 429 | + | |
| 430 | + return 0; | |
| 431 | +} | |
| 432 | + | |
| 433 | +#else | |
| 434 | + | |
| 435 | +static int ch7322_get_port(struct i2c_client *client, | |
| 436 | + struct device **dev, | |
| 437 | + const char **port) | |
| 438 | +{ | |
| 439 | + *dev = NULL; | |
| 440 | + *port = NULL; | |
| 441 | + | |
| 442 | + return 0; | |
| 443 | +} | |
| 444 | + | |
| 445 | +#endif | |
| 446 | + | |
| 447 | +static int ch7322_probe(struct i2c_client *client) | |
| 448 | +{ | |
| 449 | + struct device *hdmi_dev; | |
| 450 | + const char *port_name; | |
| 451 | + struct ch7322 *ch7322; | |
| 452 | + struct cec_notifier *notifier = NULL; | |
| 453 | + u32 caps = CEC_CAP_DEFAULTS; | |
| 454 | + int ret; | |
| 455 | + unsigned int val; | |
| 456 | + | |
| 457 | + ret = ch7322_get_port(client, &hdmi_dev, &port_name); | |
| 458 | + if (ret) | |
| 459 | + return ret; | |
| 460 | + | |
| 461 | + if (hdmi_dev) | |
| 462 | + caps |= CEC_CAP_CONNECTOR_INFO; | |
| 463 | + | |
| 464 | + ch7322 = devm_kzalloc(&client->dev, sizeof(*ch7322), GFP_KERNEL); | |
| 465 | + if (!ch7322) | |
| 466 | + return -ENOMEM; | |
| 467 | + | |
| 468 | + ch7322->regmap = devm_regmap_init_i2c(client, &ch7322_regmap); | |
| 469 | + if (IS_ERR(ch7322->regmap)) | |
| 470 | + return PTR_ERR(ch7322->regmap); | |
| 471 | + | |
| 472 | + ret = regmap_read(ch7322->regmap, CH7322_DID, &val); | |
| 473 | + if (ret) | |
| 474 | + return ret; | |
| 475 | + | |
| 476 | + if (val != CH7322_DID_CH7322) | |
| 477 | + return -EOPNOTSUPP; | |
| 478 | + | |
| 479 | + mutex_init(&ch7322->mutex); | |
| 480 | + ch7322->i2c = client; | |
| 481 | + ch7322->tx_flags = 0; | |
| 482 | + | |
| 483 | + i2c_set_clientdata(client, ch7322); | |
| 484 | + | |
| 485 | + /* Disable auto mode */ | |
| 486 | + ret = regmap_write(ch7322->regmap, CH7322_MODE, CH7322_MODE_SW); | |
| 487 | + if (ret) | |
| 488 | + goto err_mutex; | |
| 489 | + | |
| 490 | + /* Enable logical address register */ | |
| 491 | + ret = regmap_update_bits(ch7322->regmap, CH7322_CTL, | |
| 492 | + CH7322_CTL_SPADL, CH7322_CTL_SPADL); | |
| 493 | + if (ret) | |
| 494 | + goto err_mutex; | |
| 495 | + | |
| 496 | + ch7322->cec = cec_allocate_adapter(&ch7322_cec_adap_ops, ch7322, | |
| 497 | + dev_name(&client->dev), | |
| 498 | + caps, 1); | |
| 499 | + | |
| 500 | + if (IS_ERR(ch7322->cec)) { | |
| 501 | + ret = PTR_ERR(ch7322->cec); | |
| 502 | + goto err_mutex; | |
| 503 | + } | |
| 504 | + | |
| 505 | + ch7322->cec->adap_controls_phys_addr = true; | |
| 506 | + | |
| 507 | + if (hdmi_dev) { | |
| 508 | + notifier = cec_notifier_cec_adap_register(hdmi_dev, | |
| 509 | + port_name, | |
| 510 | + ch7322->cec); | |
| 511 | + if (!notifier) { | |
| 512 | + ret = -ENOMEM; | |
| 513 | + goto err_cec; | |
| 514 | + } | |
| 515 | + } | |
| 516 | + | |
| 517 | + /* Configure, mask, and clear interrupt */ | |
| 518 | + ret = regmap_write(ch7322->regmap, CH7322_CFG1, 0); | |
| 519 | + if (ret) | |
| 520 | + goto err_notifier; | |
| 521 | + ret = regmap_write(ch7322->regmap, CH7322_INTCTL, CH7322_INTCTL_INTPB); | |
| 522 | + if (ret) | |
| 523 | + goto err_notifier; | |
| 524 | + ret = regmap_write(ch7322->regmap, CH7322_INTDATA, 0xff); | |
| 525 | + if (ret) | |
| 526 | + goto err_notifier; | |
| 527 | + | |
| 528 | + /* If HPD is up read physical address */ | |
| 529 | + ret = regmap_read(ch7322->regmap, CH7322_ADDLR, &val); | |
| 530 | + if (ret) | |
| 531 | + goto err_notifier; | |
| 532 | + if (val & CH7322_ADDLR_HPD) | |
| 533 | + ch7322_phys_addr(ch7322); | |
| 534 | + | |
| 535 | + ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, | |
| 536 | + ch7322_irq, | |
| 537 | + IRQF_ONESHOT | IRQF_TRIGGER_RISING, | |
| 538 | + client->name, ch7322); | |
| 539 | + if (ret) | |
| 540 | + goto err_notifier; | |
| 541 | + | |
| 542 | + /* Unmask interrupt */ | |
| 543 | + mutex_lock(&ch7322->mutex); | |
| 544 | + ret = regmap_write(ch7322->regmap, CH7322_INTCTL, 0xff); | |
| 545 | + mutex_unlock(&ch7322->mutex); | |
| 546 | + | |
| 547 | + if (ret) | |
| 548 | + goto err_notifier; | |
| 549 | + | |
| 550 | + ret = cec_register_adapter(ch7322->cec, &client->dev); | |
| 551 | + if (ret) | |
| 552 | + goto err_notifier; | |
| 553 | + | |
| 554 | + dev_info(&client->dev, "device registered\n"); | |
| 555 | + | |
| 556 | + return 0; | |
| 557 | + | |
| 558 | +err_notifier: | |
| 559 | + if (notifier) | |
| 560 | + cec_notifier_cec_adap_unregister(notifier, ch7322->cec); | |
| 561 | +err_cec: | |
| 562 | + cec_delete_adapter(ch7322->cec); | |
| 563 | +err_mutex: | |
| 564 | + mutex_destroy(&ch7322->mutex); | |
| 565 | + return ret; | |
| 566 | +} | |
| 567 | + | |
| 568 | +static int ch7322_remove(struct i2c_client *client) | |
| 569 | +{ | |
| 570 | + struct ch7322 *ch7322 = i2c_get_clientdata(client); | |
| 571 | + | |
| 572 | + /* Mask interrupt */ | |
| 573 | + mutex_lock(&ch7322->mutex); | |
| 574 | + regmap_write(ch7322->regmap, CH7322_INTCTL, CH7322_INTCTL_INTPB); | |
| 575 | + mutex_unlock(&ch7322->mutex); | |
| 576 | + | |
| 577 | + cec_unregister_adapter(ch7322->cec); | |
| 578 | + mutex_destroy(&ch7322->mutex); | |
| 579 | + | |
| 580 | + dev_info(&client->dev, "device unregistered\n"); | |
| 581 | + | |
| 582 | + return 0; | |
| 583 | +} | |
| 584 | + | |
| 585 | +static const struct of_device_id ch7322_of_match[] = { | |
| 586 | + { .compatible = "chrontel,ch7322", }, | |
| 587 | + {}, | |
| 588 | +}; | |
| 589 | +MODULE_DEVICE_TABLE(of, ch7322_of_match); | |
| 590 | + | |
| 591 | +static struct i2c_driver ch7322_i2c_driver = { | |
| 592 | + .driver = { | |
| 593 | + .name = "ch7322", | |
| 594 | + .of_match_table = of_match_ptr(ch7322_of_match), | |
| 595 | + }, | |
| 596 | + .probe_new = ch7322_probe, | |
| 597 | + .remove = ch7322_remove, | |
| 598 | +}; | |
| 599 | + | |
| 600 | +module_i2c_driver(ch7322_i2c_driver); | |
| 601 | + | |
| 602 | +MODULE_DESCRIPTION("Chrontel CH7322 CEC Controller Driver"); | |
| 603 | +MODULE_AUTHOR("Jeff Chase <jnchase@google.com>"); | |
| 604 | +MODULE_LICENSE("GPL"); | |
| @@ -277,11 +277,7 @@ static int cros_ec_cec_probe(struct platform_device *pdev) · 276 unmodified lines | ||
| 277 | 277 | platform_set_drvdata(pdev, cros_ec_cec); |
| 278 | 278 | cros_ec_cec->cros_ec = cros_ec; |
| 279 | 279 | |
| 280 | − ret = device_init_wakeup(&pdev->dev, 1); | |
| 281 | − if (ret) { | |
| 282 | − dev_err(&pdev->dev, "failed to initialize wakeup\n"); | |
| 283 | − return ret; | |
| 284 | − } | |
| 280 | + device_init_wakeup(&pdev->dev, 1); | |
| 285 | 281 | |
| 286 | 282 | cros_ec_cec->adap = cec_allocate_adapter(&cros_ec_cec_ops, cros_ec_cec, |
| 287 | 283 | DRV_NAME, |
| @@ -1927,44 +1927,46 @@ typedef struct { u16 __; u8 _; } __packed x24; · 1926 unmodified lines | ||
| 1927 | 1927 | |
| 1928 | 1928 | static noinline void tpg_print_str_2(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], |
| 1929 | 1929 | unsigned p, unsigned first, unsigned div, unsigned step, |
| 1930 | − int y, int x, char *text, unsigned len) | |
| 1930 | + int y, int x, const char *text, unsigned len) | |
| 1931 | 1931 | { |
| 1932 | 1932 | PRINTSTR(u8); |
| 1933 | 1933 | } |
| 1934 | 1934 | |
| 1935 | 1935 | static noinline void tpg_print_str_4(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], |
| 1936 | 1936 | unsigned p, unsigned first, unsigned div, unsigned step, |
| 1937 | − int y, int x, char *text, unsigned len) | |
| 1937 | + int y, int x, const char *text, unsigned len) | |
| 1938 | 1938 | { |
| 1939 | 1939 | PRINTSTR(u16); |
| 1940 | 1940 | } |
| 1941 | 1941 | |
| 1942 | 1942 | static noinline void tpg_print_str_6(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], |
| 1943 | 1943 | unsigned p, unsigned first, unsigned div, unsigned step, |
| 1944 | − int y, int x, char *text, unsigned len) | |
| 1944 | + int y, int x, const char *text, unsigned len) | |
| 1945 | 1945 | { |
| 1946 | 1946 | PRINTSTR(x24); |
| 1947 | 1947 | } |
| 1948 | 1948 | |
| 1949 | 1949 | static noinline void tpg_print_str_8(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], |
| 1950 | 1950 | unsigned p, unsigned first, unsigned div, unsigned step, |
| 1951 | − int y, int x, char *text, unsigned len) | |
| 1951 | + int y, int x, const char *text, unsigned len) | |
| 1952 | 1952 | { |
| 1953 | 1953 | PRINTSTR(u32); |
| 1954 | 1954 | } |
| 1955 | 1955 | |
| 1956 | 1956 | void tpg_gen_text(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], |
| 1957 | − int y, int x, char *text) | |
| 1957 | + int y, int x, const char *text) | |
| 1958 | 1958 | { |
| 1959 | 1959 | unsigned step = V4L2_FIELD_HAS_T_OR_B(tpg->field) ? 2 : 1; |
| 1960 | 1960 | unsigned div = step; |
| 1961 | 1961 | unsigned first = 0; |
| 1962 | − unsigned len = strlen(text); | |
| 1962 | + unsigned len; | |
| 1963 | 1963 | unsigned p; |
| 1964 | 1964 | |
| 1965 | − if (font8x16 == NULL || basep == NULL) | |
| 1965 | + if (font8x16 == NULL || basep == NULL || text == NULL) | |
| 1966 | 1966 | return; |
| 1967 | 1967 | |
| 1968 | + len = strlen(text); | |
| 1969 | + | |
| 1968 | 1970 | /* Checks if it is possible to show string */ |
| 1969 | 1971 | if (y + 16 >= tpg->compose.height || x + 8 >= tpg->compose.width) |
| 1970 | 1972 | return; |
| @@ -2006,6 +2008,30 @@ void tpg_gen_text(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], · 35 unmodified lines | ||
| 2006 | 2008 | } |
| 2007 | 2009 | EXPORT_SYMBOL_GPL(tpg_gen_text); |
| 2008 | 2010 | |
| 2011 | +const char *tpg_g_color_order(const struct tpg_data *tpg) | |
| 2012 | +{ | |
| 2013 | + switch (tpg->pattern) { | |
| 2014 | + case TPG_PAT_75_COLORBAR: | |
| 2015 | + case TPG_PAT_100_COLORBAR: | |
| 2016 | + case TPG_PAT_CSC_COLORBAR: | |
| 2017 | + case TPG_PAT_100_HCOLORBAR: | |
| 2018 | + return "White, yellow, cyan, green, magenta, red, blue, black"; | |
| 2019 | + case TPG_PAT_BLACK: | |
| 2020 | + return "Black"; | |
| 2021 | + case TPG_PAT_WHITE: | |
| 2022 | + return "White"; | |
| 2023 | + case TPG_PAT_RED: | |
| 2024 | + return "Red"; | |
| 2025 | + case TPG_PAT_GREEN: | |
| 2026 | + return "Green"; | |
| 2027 | + case TPG_PAT_BLUE: | |
| 2028 | + return "Blue"; | |
| 2029 | + default: | |
| 2030 | + return NULL; | |
| 2031 | + } | |
| 2032 | +} | |
| 2033 | +EXPORT_SYMBOL_GPL(tpg_g_color_order); | |
| 2034 | + | |
| 2009 | 2035 | void tpg_update_mv_step(struct tpg_data *tpg) |
| 2010 | 2036 | { |
| 2011 | 2037 | int factor = tpg->mv_hor_mode > TPG_MOVE_NONE ? -1 : 1; |
| @@ -34,10 +34,11 @@ · 33 unmodified lines | ||
| 34 | 34 | static int debug; |
| 35 | 35 | module_param(debug, int, 0644); |
| 36 | 36 | |
| 37 | −#define dprintk(level, fmt, arg...) \ | |
| 38 | − do { \ | |
| 39 | − if (debug >= level) \ | |
| 40 | − pr_info("%s: " fmt, __func__, ## arg); \ | |
| 37 | +#define dprintk(q, level, fmt, arg...) \ | |
| 38 | + do { \ | |
| 39 | + if (debug >= level) \ | |
| 40 | + pr_info("[%s] %s: " fmt, (q)->name, __func__, \ | |
| 41 | + ## arg); \ | |
| 41 | 42 | } while (0) |
| 42 | 43 | |
| 43 | 44 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
| @@ -51,8 +52,8 @@ module_param(debug, int, 0644); · 7 unmodified lines | ||
| 51 | 52 | */ |
| 52 | 53 | |
| 53 | 54 | #define log_memop(vb, op) \ |
| 54 | − dprintk(2, "call_memop(%p, %d, %s)%s\n", \ | |
| 55 | − (vb)->vb2_queue, (vb)->index, #op, \ | |
| 55 | + dprintk((vb)->vb2_queue, 2, "call_memop(%d, %s)%s\n", \ | |
| 56 | + (vb)->index, #op, \ | |
| 56 | 57 | (vb)->vb2_queue->mem_ops->op ? "" : " (nop)") |
| 57 | 58 | |
| 58 | 59 | #define call_memop(vb, op, args...) \ |
| @@ -90,7 +91,7 @@ module_param(debug, int, 0644); · 31 unmodified lines | ||
| 90 | 91 | }) |
| 91 | 92 | |
| 92 | 93 | #define log_qop(q, op) \ |
| 93 | − dprintk(2, "call_qop(%p, %s)%s\n", q, #op, \ | |
| 94 | + dprintk(q, 2, "call_qop(%s)%s\n", #op, \ | |
| 94 | 95 | (q)->ops->op ? "" : " (nop)") |
| 95 | 96 | |
| 96 | 97 | #define call_qop(q, op, args...) \ |
| @@ -113,8 +114,8 @@ module_param(debug, int, 0644); · 16 unmodified lines | ||
| 113 | 114 | }) |
| 114 | 115 | |
| 115 | 116 | #define log_vb_qop(vb, op, args...) \ |
| 116 | − dprintk(2, "call_vb_qop(%p, %d, %s)%s\n", \ | |
| 117 | − (vb)->vb2_queue, (vb)->index, #op, \ | |
| 117 | + dprintk((vb)->vb2_queue, 2, "call_vb_qop(%d, %s)%s\n", \ | |
| 118 | + (vb)->index, #op, \ | |
| 118 | 119 | (vb)->vb2_queue->ops->op ? "" : " (nop)") |
| 119 | 120 | |
| 120 | 121 | #define call_vb_qop(vb, op, args...) \ |
| @@ -190,6 +191,23 @@ module_param(debug, int, 0644); · 69 unmodified lines | ||
| 190 | 191 | static void __vb2_queue_cancel(struct vb2_queue *q); |
| 191 | 192 | static void __enqueue_in_driver(struct vb2_buffer *vb); |
| 192 | 193 | |
| 194 | +static const char *vb2_state_name(enum vb2_buffer_state s) | |
| 195 | +{ | |
| 196 | + static const char * const state_names[] = { | |
| 197 | + [VB2_BUF_STATE_DEQUEUED] = "dequeued", | |
| 198 | + [VB2_BUF_STATE_IN_REQUEST] = "in request", | |
| 199 | + [VB2_BUF_STATE_PREPARING] = "preparing", | |
| 200 | + [VB2_BUF_STATE_QUEUED] = "queued", | |
| 201 | + [VB2_BUF_STATE_ACTIVE] = "active", | |
| 202 | + [VB2_BUF_STATE_DONE] = "done", | |
| 203 | + [VB2_BUF_STATE_ERROR] = "error", | |
| 204 | + }; | |
| 205 | + | |
| 206 | + if ((unsigned int)(s) < ARRAY_SIZE(state_names)) | |
| 207 | + return state_names[s]; | |
| 208 | + return "unknown"; | |
| 209 | +} | |
| 210 | + | |
| 193 | 211 | /* |
| 194 | 212 | * __vb2_buf_mem_alloc() - allocate video memory for the given buffer |
| 195 | 213 | */ |
| @@ -246,7 +264,8 @@ static void __vb2_buf_mem_free(struct vb2_buffer *vb) · 50 unmodified lines | ||
| 246 | 264 | for (plane = 0; plane < vb->num_planes; ++plane) { |
| 247 | 265 | call_void_memop(vb, put, vb->planes[plane].mem_priv); |
| 248 | 266 | vb->planes[plane].mem_priv = NULL; |
| 249 | − dprintk(3, "freed plane %d of buffer %d\n", plane, vb->index); | |
| 267 | + dprintk(vb->vb2_queue, 3, "freed plane %d of buffer %d\n", | |
| 268 | + plane, vb->index); | |
| 250 | 269 | } |
| 251 | 270 | } |
| 252 | 271 | |
| @@ -296,6 +315,44 @@ static void __vb2_buf_dmabuf_put(struct vb2_buffer *vb) · 43 unmodified lines | ||
| 296 | 315 | __vb2_plane_dmabuf_put(vb, &vb->planes[plane]); |
| 297 | 316 | } |
| 298 | 317 | |
| 318 | +/* | |
| 319 | + * __vb2_buf_mem_prepare() - call ->prepare() on buffer's private memory | |
| 320 | + * to sync caches | |
| 321 | + */ | |
| 322 | +static void __vb2_buf_mem_prepare(struct vb2_buffer *vb) | |
| 323 | +{ | |
| 324 | + unsigned int plane; | |
| 325 | + | |
| 326 | + if (vb->synced) | |
| 327 | + return; | |
| 328 | + | |
| 329 | + if (vb->need_cache_sync_on_prepare) { | |
| 330 | + for (plane = 0; plane < vb->num_planes; ++plane) | |
| 331 | + call_void_memop(vb, prepare, | |
| 332 | + vb->planes[plane].mem_priv); | |
| 333 | + } | |
| 334 | + vb->synced = 1; | |
| 335 | +} | |
| 336 | + | |
| 337 | +/* | |
| 338 | + * __vb2_buf_mem_finish() - call ->finish on buffer's private memory | |
| 339 | + * to sync caches | |
| 340 | + */ | |
| 341 | +static void __vb2_buf_mem_finish(struct vb2_buffer *vb) | |
| 342 | +{ | |
| 343 | + unsigned int plane; | |
| 344 | + | |
| 345 | + if (!vb->synced) | |
| 346 | + return; | |
| 347 | + | |
| 348 | + if (vb->need_cache_sync_on_finish) { | |
| 349 | + for (plane = 0; plane < vb->num_planes; ++plane) | |
| 350 | + call_void_memop(vb, finish, | |
| 351 | + vb->planes[plane].mem_priv); | |
| 352 | + } | |
| 353 | + vb->synced = 0; | |
| 354 | +} | |
| 355 | + | |
| 299 | 356 | /* |
| 300 | 357 | * __setup_offsets() - setup unique offsets ("cookies") for every plane in |
| 301 | 358 | * the buffer. |
| @@ -316,7 +373,7 @@ static void __setup_offsets(struct vb2_buffer *vb) · 14 unmodified lines | ||
| 316 | 373 | for (plane = 0; plane < vb->num_planes; ++plane) { |
| 317 | 374 | vb->planes[plane].m.offset = off; |
| 318 | 375 | |
| 319 | − dprintk(3, "buffer %d, plane %d offset 0x%08lx\n", | |
| 376 | + dprintk(q, 3, "buffer %d, plane %d offset 0x%08lx\n", | |
| 320 | 377 | vb->index, plane, off); |
| 321 | 378 | |
| 322 | 379 | off += vb->planes[plane].length; |
| @@ -347,7 +404,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, · 24 unmodified lines | ||
| 347 | 404 | /* Allocate videobuf buffer structures */ |
| 348 | 405 | vb = kzalloc(q->buf_struct_size, GFP_KERNEL); |
| 349 | 406 | if (!vb) { |
| 350 | − dprintk(1, "memory alloc for buffer struct failed\n"); | |
| 407 | + dprintk(q, 1, "memory alloc for buffer struct failed\n"); | |
| 351 | 408 | break; |
| 352 | 409 | } |
| 353 | 410 | |
| @@ -369,7 +426,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, · 15 unmodified lines | ||
| 369 | 426 | if (memory == VB2_MEMORY_MMAP) { |
| 370 | 427 | ret = __vb2_buf_mem_alloc(vb); |
| 371 | 428 | if (ret) { |
| 372 | − dprintk(1, "failed allocating memory for buffer %d\n", | |
| 429 | + dprintk(q, 1, "failed allocating memory for buffer %d\n", | |
| 373 | 430 | buffer); |
| 374 | 431 | q->bufs[vb->index] = NULL; |
| 375 | 432 | kfree(vb); |
| @@ -383,7 +440,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, · 7 unmodified lines | ||
| 383 | 440 | */ |
| 384 | 441 | ret = call_vb_qop(vb, buf_init, vb); |
| 385 | 442 | if (ret) { |
| 386 | − dprintk(1, "buffer %d %p initialization failed\n", | |
| 443 | + dprintk(q, 1, "buffer %d %p initialization failed\n", | |
| 387 | 444 | buffer, vb); |
| 388 | 445 | __vb2_buf_mem_free(vb); |
| 389 | 446 | q->bufs[vb->index] = NULL; |
| @@ -393,7 +450,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, · 3 unmodified lines | ||
| 393 | 450 | } |
| 394 | 451 | } |
| 395 | 452 | |
| 396 | − dprintk(3, "allocated %d buffers, %d plane(s) each\n", | |
| 453 | + dprintk(q, 3, "allocated %d buffers, %d plane(s) each\n", | |
| 397 | 454 | buffer, num_planes); |
| 398 | 455 | |
| 399 | 456 | return buffer; |
| @@ -445,7 +502,7 @@ static int __vb2_queue_free(struct vb2_queue *q, unsigned int buffers) · 45 unmodified lines | ||
| 445 | 502 | if (q->bufs[buffer] == NULL) |
| 446 | 503 | continue; |
| 447 | 504 | if (q->bufs[buffer]->state == VB2_BUF_STATE_PREPARING) { |
| 448 | − dprintk(1, "preparing buffers, cannot free\n"); | |
| 505 | + dprintk(q, 1, "preparing buffers, cannot free\n"); | |
| 449 | 506 | return -EAGAIN; |
| 450 | 507 | } |
| 451 | 508 | } |
| @@ -623,12 +680,12 @@ int vb2_verify_memory_type(struct vb2_queue *q, · 171 unmodified lines | ||
| 623 | 680 | { |
| 624 | 681 | if (memory != VB2_MEMORY_MMAP && memory != VB2_MEMORY_USERPTR && |
| 625 | 682 | memory != VB2_MEMORY_DMABUF) { |
| 626 | − dprintk(1, "unsupported memory type\n"); | |
| 683 | + dprintk(q, 1, "unsupported memory type\n"); | |
| 627 | 684 | return -EINVAL; |
| 628 | 685 | } |
| 629 | 686 | |
| 630 | 687 | if (type != q->type) { |
| 631 | − dprintk(1, "requested type is incorrect\n"); | |
| 688 | + dprintk(q, 1, "requested type is incorrect\n"); | |
| 632 | 689 | return -EINVAL; |
| 633 | 690 | } |
| 634 | 691 | |
| @@ -637,17 +694,17 @@ int vb2_verify_memory_type(struct vb2_queue *q, · 2 unmodified lines | ||
| 637 | 694 | * are available. |
| 638 | 695 | */ |
| 639 | 696 | if (memory == VB2_MEMORY_MMAP && __verify_mmap_ops(q)) { |
| 640 | − dprintk(1, "MMAP for current setup unsupported\n"); | |
| 697 | + dprintk(q, 1, "MMAP for current setup unsupported\n"); | |
| 641 | 698 | return -EINVAL; |
| 642 | 699 | } |
| 643 | 700 | |
| 644 | 701 | if (memory == VB2_MEMORY_USERPTR && __verify_userptr_ops(q)) { |
| 645 | − dprintk(1, "USERPTR for current setup unsupported\n"); | |
| 702 | + dprintk(q, 1, "USERPTR for current setup unsupported\n"); | |
| 646 | 703 | return -EINVAL; |
| 647 | 704 | } |
| 648 | 705 | |
| 649 | 706 | if (memory == VB2_MEMORY_DMABUF && __verify_dmabuf_ops(q)) { |
| 650 | − dprintk(1, "DMABUF for current setup unsupported\n"); | |
| 707 | + dprintk(q, 1, "DMABUF for current setup unsupported\n"); | |
| 651 | 708 | return -EINVAL; |
| 652 | 709 | } |
| 653 | 710 | |
| @@ -657,33 +714,59 @@ int vb2_verify_memory_type(struct vb2_queue *q, · 3 unmodified lines | ||
| 657 | 714 | * do the memory and type validation. |
| 658 | 715 | */ |
| 659 | 716 | if (vb2_fileio_is_active(q)) { |
| 660 | − dprintk(1, "file io in progress\n"); | |
| 717 | + dprintk(q, 1, "file io in progress\n"); | |
| 661 | 718 | return -EBUSY; |
| 662 | 719 | } |
| 663 | 720 | return 0; |
| 664 | 721 | } |
| 665 | 722 | EXPORT_SYMBOL(vb2_verify_memory_type); |
| 666 | 723 | |
| 724 | +static void set_queue_consistency(struct vb2_queue *q, bool consistent_mem) | |
| 725 | +{ | |
| 726 | + q->dma_attrs &= ~DMA_ATTR_NON_CONSISTENT; | |
| 727 | + | |
| 728 | + if (!vb2_queue_allows_cache_hints(q)) | |
| 729 | + return; | |
| 730 | + if (!consistent_mem) | |
| 731 | + q->dma_attrs |= DMA_ATTR_NON_CONSISTENT; | |
| 732 | +} | |
| 733 | + | |
| 734 | +static bool verify_consistency_attr(struct vb2_queue *q, bool consistent_mem) | |
| 735 | +{ | |
| 736 | + bool queue_is_consistent = !(q->dma_attrs & DMA_ATTR_NON_CONSISTENT); | |
| 737 | + | |
| 738 | + if (consistent_mem != queue_is_consistent) { | |
| 739 | + dprintk(q, 1, "memory consistency model mismatch\n"); | |
| 740 | + return false; | |
| 741 | + } | |
| 742 | + return true; | |
| 743 | +} | |
| 744 | + | |
| 667 | 745 | int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, |
| 668 | − unsigned int *count) | |
| 746 | + unsigned int flags, unsigned int *count) | |
| 669 | 747 | { |
| 670 | 748 | unsigned int num_buffers, allocated_buffers, num_planes = 0; |
| 671 | 749 | unsigned plane_sizes[VB2_MAX_PLANES] = { }; |
| 750 | + bool consistent_mem = true; | |
| 672 | 751 | unsigned int i; |
| 673 | 752 | int ret; |
| 674 | 753 | |
| 754 | + if (flags & V4L2_FLAG_MEMORY_NON_CONSISTENT) | |
| 755 | + consistent_mem = false; | |
| 756 | + | |
| 675 | 757 | if (q->streaming) { |
| 676 | − dprintk(1, "streaming active\n"); | |
| 758 | + dprintk(q, 1, "streaming active\n"); | |
| 677 | 759 | return -EBUSY; |
| 678 | 760 | } |
| 679 | 761 | |
| 680 | 762 | if (q->waiting_in_dqbuf && *count) { |
| 681 | − dprintk(1, "another dup()ped fd is waiting for a buffer\n"); | |
| 763 | + dprintk(q, 1, "another dup()ped fd is waiting for a buffer\n"); | |
| 682 | 764 | return -EBUSY; |
| 683 | 765 | } |
| 684 | 766 | |
| 685 | 767 | if (*count == 0 || q->num_buffers != 0 || |
| 686 | − (q->memory != VB2_MEMORY_UNKNOWN && q->memory != memory)) { | |
| 768 | + (q->memory != VB2_MEMORY_UNKNOWN && q->memory != memory) || | |
| 769 | + !verify_consistency_attr(q, consistent_mem)) { | |
| 687 | 770 | /* |
| 688 | 771 | * We already have buffers allocated, so first check if they |
| 689 | 772 | * are not in use and can be freed. |
| @@ -691,7 +774,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, · 1 unmodified line | ||
| 691 | 774 | mutex_lock(&q->mmap_lock); |
| 692 | 775 | if (debug && q->memory == VB2_MEMORY_MMAP && |
| 693 | 776 | __buffers_in_use(q)) |
| 694 | − dprintk(1, "memory in use, orphaning buffers\n"); | |
| 777 | + dprintk(q, 1, "memory in use, orphaning buffers\n"); | |
| 695 | 778 | |
| 696 | 779 | /* |
| 697 | 780 | * 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, · 22 unmodified lines | ||
| 720 | 803 | num_buffers = min_t(unsigned int, num_buffers, VB2_MAX_FRAME); |
| 721 | 804 | memset(q->alloc_devs, 0, sizeof(q->alloc_devs)); |
| 722 | 805 | q->memory = memory; |
| 806 | + set_queue_consistency(q, consistent_mem); | |
| 723 | 807 | |
| 724 | 808 | /* |
| 725 | 809 | * 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, · 16 unmodified lines | ||
| 742 | 826 | allocated_buffers = |
| 743 | 827 | __vb2_queue_alloc(q, memory, num_buffers, num_planes, plane_sizes); |
| 744 | 828 | if (allocated_buffers == 0) { |
| 745 | − dprintk(1, "memory allocation failed\n"); | |
| 829 | + dprintk(q, 1, "memory allocation failed\n"); | |
| 746 | 830 | return -ENOMEM; |
| 747 | 831 | } |
| 748 | 832 | |
| @@ -804,29 +888,39 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, · 55 unmodified lines | ||
| 804 | 888 | EXPORT_SYMBOL_GPL(vb2_core_reqbufs); |
| 805 | 889 | |
| 806 | 890 | int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, |
| 807 | − unsigned int *count, unsigned requested_planes, | |
| 808 | − const unsigned requested_sizes[]) | |
| 891 | + unsigned int flags, unsigned int *count, | |
| 892 | + unsigned int requested_planes, | |
| 893 | + const unsigned int requested_sizes[]) | |
| 809 | 894 | { |
| 810 | 895 | unsigned int num_planes = 0, num_buffers, allocated_buffers; |
| 811 | 896 | unsigned plane_sizes[VB2_MAX_PLANES] = { }; |
| 897 | + bool consistent_mem = true; | |
| 812 | 898 | int ret; |
| 813 | 899 | |
| 900 | + if (flags & V4L2_FLAG_MEMORY_NON_CONSISTENT) | |
| 901 | + consistent_mem = false; | |
| 902 | + | |
| 814 | 903 | if (q->num_buffers == VB2_MAX_FRAME) { |
| 815 | − dprintk(1, "maximum number of buffers already allocated\n"); | |
| 904 | + dprintk(q, 1, "maximum number of buffers already allocated\n"); | |
| 816 | 905 | return -ENOBUFS; |
| 817 | 906 | } |
| 818 | 907 | |
| 819 | 908 | if (!q->num_buffers) { |
| 820 | 909 | if (q->waiting_in_dqbuf && *count) { |
| 821 | − dprintk(1, "another dup()ped fd is waiting for a buffer\n"); | |
| 910 | + dprintk(q, 1, "another dup()ped fd is waiting for a buffer\n"); | |
| 822 | 911 | return -EBUSY; |
| 823 | 912 | } |
| 824 | 913 | memset(q->alloc_devs, 0, sizeof(q->alloc_devs)); |
| 825 | 914 | q->memory = memory; |
| 915 | + set_queue_consistency(q, consistent_mem); | |
| 826 | 916 | q->waiting_for_buffers = !q->is_output; |
| 827 | − } else if (q->memory != memory) { | |
| 828 | − dprintk(1, "memory model mismatch\n"); | |
| 829 | − return -EINVAL; | |
| 917 | + } else { | |
| 918 | + if (q->memory != memory) { | |
| 919 | + dprintk(q, 1, "memory model mismatch\n"); | |
| 920 | + return -EINVAL; | |
| 921 | + } | |
| 922 | + if (!verify_consistency_attr(q, consistent_mem)) | |
| 923 | + return -EINVAL; | |
| 830 | 924 | } |
| 831 | 925 | |
| 832 | 926 | 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, · 16 unmodified lines | ||
| 849 | 943 | allocated_buffers = __vb2_queue_alloc(q, memory, num_buffers, |
| 850 | 944 | num_planes, plane_sizes); |
| 851 | 945 | if (allocated_buffers == 0) { |
| 852 | − dprintk(1, "memory allocation failed\n"); | |
| 946 | + dprintk(q, 1, "memory allocation failed\n"); | |
| 853 | 947 | return -ENOMEM; |
| 854 | 948 | } |
| 855 | 949 | |
| @@ -922,7 +1016,6 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) · 66 unmodified lines | ||
| 922 | 1016 | { |
| 923 | 1017 | struct vb2_queue *q = vb->vb2_queue; |
| 924 | 1018 | unsigned long flags; |
| 925 | − unsigned int plane; | |
| 926 | 1019 | |
| 927 | 1020 | if (WARN_ON(vb->state != VB2_BUF_STATE_ACTIVE)) |
| 928 | 1021 | return; |
| @@ -939,15 +1032,11 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) · 10 unmodified lines | ||
| 939 | 1032 | */ |
| 940 | 1033 | vb->cnt_buf_done++; |
| 941 | 1034 | #endif |
| 942 | − dprintk(4, "done processing on buffer %d, state: %d\n", | |
| 943 | − vb->index, state); | |
| 1035 | + dprintk(q, 4, "done processing on buffer %d, state: %s\n", | |
| 1036 | + vb->index, vb2_state_name(state)); | |
| 944 | 1037 | |
| 945 | − if (state != VB2_BUF_STATE_QUEUED) { | |
| 946 | − /* sync buffers */ | |
| 947 | − for (plane = 0; plane < vb->num_planes; ++plane) | |
| 948 | − call_void_memop(vb, finish, vb->planes[plane].mem_priv); | |
| 949 | − vb->synced = 0; | |
| 950 | − } | |
| 1038 | + if (state != VB2_BUF_STATE_QUEUED) | |
| 1039 | + __vb2_buf_mem_finish(vb); | |
| 951 | 1040 | |
| 952 | 1041 | spin_lock_irqsave(&q->done_lock, flags); |
| 953 | 1042 | if (state == VB2_BUF_STATE_QUEUED) { |
| @@ -1029,12 +1118,12 @@ static int __prepare_userptr(struct vb2_buffer *vb) · 75 unmodified lines | ||
| 1029 | 1118 | && vb->planes[plane].length == planes[plane].length) |
| 1030 | 1119 | continue; |
| 1031 | 1120 | |
| 1032 | − dprintk(3, "userspace address for plane %d changed, reacquiring memory\n", | |
| 1121 | + dprintk(q, 3, "userspace address for plane %d changed, reacquiring memory\n", | |
| 1033 | 1122 | plane); |
| 1034 | 1123 | |
| 1035 | 1124 | /* Check if the provided plane buffer is large enough */ |
| 1036 | 1125 | if (planes[plane].length < vb->planes[plane].min_length) { |
| 1037 | − dprintk(1, "provided buffer size %u is less than setup size %u for plane %d\n", | |
| 1126 | + dprintk(q, 1, "provided buffer size %u is less than setup size %u for plane %d\n", | |
| 1038 | 1127 | planes[plane].length, |
| 1039 | 1128 | vb->planes[plane].min_length, |
| 1040 | 1129 | plane); |
| @@ -1064,7 +1153,7 @@ static int __prepare_userptr(struct vb2_buffer *vb) · 23 unmodified lines | ||
| 1064 | 1153 | planes[plane].m.userptr, |
| 1065 | 1154 | planes[plane].length, q->dma_dir); |
| 1066 | 1155 | if (IS_ERR(mem_priv)) { |
| 1067 | − dprintk(1, "failed acquiring userspace memory for plane %d\n", | |
| 1156 | + dprintk(q, 1, "failed acquiring userspace memory for plane %d\n", | |
| 1068 | 1157 | plane); |
| 1069 | 1158 | ret = PTR_ERR(mem_priv); |
| 1070 | 1159 | goto err; |
| @@ -1091,14 +1180,14 @@ static int __prepare_userptr(struct vb2_buffer *vb) · 20 unmodified lines | ||
| 1091 | 1180 | */ |
| 1092 | 1181 | ret = call_vb_qop(vb, buf_init, vb); |
| 1093 | 1182 | if (ret) { |
| 1094 | − dprintk(1, "buffer initialization failed\n"); | |
| 1183 | + dprintk(q, 1, "buffer initialization failed\n"); | |
| 1095 | 1184 | goto err; |
| 1096 | 1185 | } |
| 1097 | 1186 | } |
| 1098 | 1187 | |
| 1099 | 1188 | ret = call_vb_qop(vb, buf_prepare, vb); |
| 1100 | 1189 | if (ret) { |
| 1101 | − dprintk(1, "buffer preparation failed\n"); | |
| 1190 | + dprintk(q, 1, "buffer preparation failed\n"); | |
| 1102 | 1191 | call_void_vb_qop(vb, buf_cleanup, vb); |
| 1103 | 1192 | goto err; |
| 1104 | 1193 | } |
| @@ -1141,7 +1230,7 @@ static int __prepare_dmabuf(struct vb2_buffer *vb) · 36 unmodified lines | ||
| 1141 | 1230 | struct dma_buf *dbuf = dma_buf_get(planes[plane].m.fd); |
| 1142 | 1231 | |
| 1143 | 1232 | if (IS_ERR_OR_NULL(dbuf)) { |
| 1144 | − dprintk(1, "invalid dmabuf fd for plane %d\n", | |
| 1233 | + dprintk(q, 1, "invalid dmabuf fd for plane %d\n", | |
| 1145 | 1234 | plane); |
| 1146 | 1235 | ret = -EINVAL; |
| 1147 | 1236 | goto err; |
| @@ -1152,7 +1241,7 @@ static int __prepare_dmabuf(struct vb2_buffer *vb) · 4 unmodified lines | ||
| 1152 | 1241 | planes[plane].length = dbuf->size; |
| 1153 | 1242 | |
| 1154 | 1243 | if (planes[plane].length < vb->planes[plane].min_length) { |
| 1155 | − dprintk(1, "invalid dmabuf length %u for plane %d, minimum length %u\n", | |
| 1244 | + dprintk(q, 1, "invalid dmabuf length %u for plane %d, minimum length %u\n", | |
| 1156 | 1245 | planes[plane].length, plane, |
| 1157 | 1246 | vb->planes[plane].min_length); |
| 1158 | 1247 | dma_buf_put(dbuf); |
| @@ -1167,7 +1256,7 @@ static int __prepare_dmabuf(struct vb2_buffer *vb) · 8 unmodified lines | ||
| 1167 | 1256 | continue; |
| 1168 | 1257 | } |
| 1169 | 1258 | |
| 1170 | − dprintk(3, "buffer for plane %d changed\n", plane); | |
| 1259 | + dprintk(q, 3, "buffer for plane %d changed\n", plane); | |
| 1171 | 1260 | |
| 1172 | 1261 | if (!reacquired) { |
| 1173 | 1262 | reacquired = true; |
| @@ -1187,7 +1276,7 @@ static int __prepare_dmabuf(struct vb2_buffer *vb) · 13 unmodified lines | ||
| 1187 | 1276 | q->alloc_devs[plane] ? : q->dev, |
| 1188 | 1277 | dbuf, planes[plane].length, q->dma_dir); |
| 1189 | 1278 | if (IS_ERR(mem_priv)) { |
| 1190 | − dprintk(1, "failed to attach dmabuf\n"); | |
| 1279 | + dprintk(q, 1, "failed to attach dmabuf\n"); | |
| 1191 | 1280 | ret = PTR_ERR(mem_priv); |
| 1192 | 1281 | dma_buf_put(dbuf); |
| 1193 | 1282 | goto err; |
| @@ -1208,7 +1297,7 @@ static int __prepare_dmabuf(struct vb2_buffer *vb) · 14 unmodified lines | ||
| 1208 | 1297 | |
| 1209 | 1298 | ret = call_memop(vb, map_dmabuf, vb->planes[plane].mem_priv); |
| 1210 | 1299 | if (ret) { |
| 1211 | − dprintk(1, "failed to map dmabuf for plane %d\n", | |
| 1300 | + dprintk(q, 1, "failed to map dmabuf for plane %d\n", | |
| 1212 | 1301 | plane); |
| 1213 | 1302 | goto err; |
| 1214 | 1303 | } |
| @@ -1233,14 +1322,14 @@ static int __prepare_dmabuf(struct vb2_buffer *vb) · 18 unmodified lines | ||
| 1233 | 1322 | */ |
| 1234 | 1323 | ret = call_vb_qop(vb, buf_init, vb); |
| 1235 | 1324 | if (ret) { |
| 1236 | − dprintk(1, "buffer initialization failed\n"); | |
| 1325 | + dprintk(q, 1, "buffer initialization failed\n"); | |
| 1237 | 1326 | goto err; |
| 1238 | 1327 | } |
| 1239 | 1328 | } |
| 1240 | 1329 | |
| 1241 | 1330 | ret = call_vb_qop(vb, buf_prepare, vb); |
| 1242 | 1331 | if (ret) { |
| 1243 | − dprintk(1, "buffer preparation failed\n"); | |
| 1332 | + dprintk(q, 1, "buffer preparation failed\n"); | |
| 1244 | 1333 | call_void_vb_qop(vb, buf_cleanup, vb); |
| 1245 | 1334 | goto err; |
| 1246 | 1335 | } |
| @@ -1272,11 +1361,10 @@ static int __buf_prepare(struct vb2_buffer *vb) · 25 unmodified lines | ||
| 1272 | 1361 | { |
| 1273 | 1362 | struct vb2_queue *q = vb->vb2_queue; |
| 1274 | 1363 | enum vb2_buffer_state orig_state = vb->state; |
| 1275 | − unsigned int plane; | |
| 1276 | 1364 | int ret; |
| 1277 | 1365 | |
| 1278 | 1366 | if (q->error) { |
| 1279 | − dprintk(1, "fatal error occurred on queue\n"); | |
| 1367 | + dprintk(q, 1, "fatal error occurred on queue\n"); | |
| 1280 | 1368 | return -EIO; |
| 1281 | 1369 | } |
| 1282 | 1370 | |
| @@ -1287,7 +1375,7 @@ static int __buf_prepare(struct vb2_buffer *vb) · 4 unmodified lines | ||
| 1287 | 1375 | if (q->is_output) { |
| 1288 | 1376 | ret = call_vb_qop(vb, buf_out_validate, vb); |
| 1289 | 1377 | if (ret) { |
| 1290 | − dprintk(1, "buffer validation failed\n"); | |
| 1378 | + dprintk(q, 1, "buffer validation failed\n"); | |
| 1291 | 1379 | return ret; |
| 1292 | 1380 | } |
| 1293 | 1381 | } |
| @@ -1311,16 +1399,12 @@ static int __buf_prepare(struct vb2_buffer *vb) · 17 unmodified lines | ||
| 1311 | 1399 | } |
| 1312 | 1400 | |
| 1313 | 1401 | if (ret) { |
| 1314 | − dprintk(1, "buffer preparation failed: %d\n", ret); | |
| 1402 | + dprintk(q, 1, "buffer preparation failed: %d\n", ret); | |
| 1315 | 1403 | vb->state = orig_state; |
| 1316 | 1404 | return ret; |
| 1317 | 1405 | } |
| 1318 | 1406 | |
| 1319 | − /* sync buffers */ | |
| 1320 | − for (plane = 0; plane < vb->num_planes; ++plane) | |
| 1321 | − call_void_memop(vb, prepare, vb->planes[plane].mem_priv); | |
| 1322 | − | |
| 1323 | − vb->synced = 1; | |
| 1407 | + __vb2_buf_mem_prepare(vb); | |
| 1324 | 1408 | vb->prepared = 1; |
| 1325 | 1409 | vb->state = orig_state; |
| 1326 | 1410 | |
| @@ -1423,12 +1507,12 @@ int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb) · 96 unmodified lines | ||
| 1423 | 1507 | |
| 1424 | 1508 | vb = q->bufs[index]; |
| 1425 | 1509 | if (vb->state != VB2_BUF_STATE_DEQUEUED) { |
| 1426 | − dprintk(1, "invalid buffer state %d\n", | |
| 1427 | − vb->state); | |
| 1510 | + dprintk(q, 1, "invalid buffer state %s\n", | |
| 1511 | + vb2_state_name(vb->state)); | |
| 1428 | 1512 | return -EINVAL; |
| 1429 | 1513 | } |
| 1430 | 1514 | if (vb->prepared) { |
| 1431 | − dprintk(1, "buffer already prepared\n"); | |
| 1515 | + dprintk(q, 1, "buffer already prepared\n"); | |
| 1432 | 1516 | return -EINVAL; |
| 1433 | 1517 | } |
| 1434 | 1518 | |
| @@ -1439,7 +1523,7 @@ int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb) · 4 unmodified lines | ||
| 1439 | 1523 | /* Fill buffer information for the userspace */ |
| 1440 | 1524 | call_void_bufop(q, fill_user_buffer, vb, pb); |
| 1441 | 1525 | |
| 1442 | − dprintk(2, "prepare of buffer %d succeeded\n", vb->index); | |
| 1526 | + dprintk(q, 2, "prepare of buffer %d succeeded\n", vb->index); | |
| 1443 | 1527 | |
| 1444 | 1528 | return 0; |
| 1445 | 1529 | } |
| @@ -1477,7 +1561,7 @@ static int vb2_start_streaming(struct vb2_queue *q) · 31 unmodified lines | ||
| 1477 | 1561 | |
| 1478 | 1562 | q->start_streaming_called = 0; |
| 1479 | 1563 | |
| 1480 | − dprintk(1, "driver refused to start streaming\n"); | |
| 1564 | + dprintk(q, 1, "driver refused to start streaming\n"); | |
| 1481 | 1565 | /* |
| 1482 | 1566 | * If you see this warning, then the driver isn't cleaning up properly |
| 1483 | 1567 | * 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, · 31 unmodified lines | ||
| 1515 | 1599 | int ret; |
| 1516 | 1600 | |
| 1517 | 1601 | if (q->error) { |
| 1518 | − dprintk(1, "fatal error occurred on queue\n"); | |
| 1602 | + dprintk(q, 1, "fatal error occurred on queue\n"); | |
| 1519 | 1603 | return -EIO; |
| 1520 | 1604 | } |
| 1521 | 1605 | |
| @@ -1523,14 +1607,14 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, · 1 unmodified line | ||
| 1523 | 1607 | |
| 1524 | 1608 | if (!req && vb->state != VB2_BUF_STATE_IN_REQUEST && |
| 1525 | 1609 | q->requires_requests) { |
| 1526 | − dprintk(1, "qbuf requires a request\n"); | |
| 1610 | + dprintk(q, 1, "qbuf requires a request\n"); | |
| 1527 | 1611 | return -EBADR; |
| 1528 | 1612 | } |
| 1529 | 1613 | |
| 1530 | 1614 | if ((req && q->uses_qbuf) || |
| 1531 | 1615 | (!req && vb->state != VB2_BUF_STATE_IN_REQUEST && |
| 1532 | 1616 | q->uses_requests)) { |
| 1533 | − dprintk(1, "queue in wrong mode (qbuf vs requests)\n"); | |
| 1617 | + dprintk(q, 1, "queue in wrong mode (qbuf vs requests)\n"); | |
| 1534 | 1618 | return -EBUSY; |
| 1535 | 1619 | } |
| 1536 | 1620 | |
| @@ -1539,7 +1623,7 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, · 2 unmodified lines | ||
| 1539 | 1623 | |
| 1540 | 1624 | q->uses_requests = 1; |
| 1541 | 1625 | if (vb->state != VB2_BUF_STATE_DEQUEUED) { |
| 1542 | − dprintk(1, "buffer %d not in dequeued state\n", | |
| 1626 | + dprintk(q, 1, "buffer %d not in dequeued state\n", | |
| 1543 | 1627 | vb->index); |
| 1544 | 1628 | return -EINVAL; |
| 1545 | 1629 | } |
| @@ -1547,7 +1631,7 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, · 1 unmodified line | ||
| 1547 | 1631 | if (q->is_output && !vb->prepared) { |
| 1548 | 1632 | ret = call_vb_qop(vb, buf_out_validate, vb); |
| 1549 | 1633 | if (ret) { |
| 1550 | − dprintk(1, "buffer validation failed\n"); | |
| 1634 | + dprintk(q, 1, "buffer validation failed\n"); | |
| 1551 | 1635 | return ret; |
| 1552 | 1636 | } |
| 1553 | 1637 | } |
| @@ -1583,7 +1667,7 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, · 29 unmodified lines | ||
| 1583 | 1667 | call_void_bufop(q, fill_user_buffer, vb, pb); |
| 1584 | 1668 | } |
| 1585 | 1669 | |
| 1586 | − dprintk(2, "qbuf of buffer %d succeeded\n", vb->index); | |
| 1670 | + dprintk(q, 2, "qbuf of buffer %d succeeded\n", vb->index); | |
| 1587 | 1671 | return 0; |
| 1588 | 1672 | } |
| 1589 | 1673 | |
| @@ -1600,10 +1684,11 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, · 10 unmodified lines | ||
| 1600 | 1684 | } |
| 1601 | 1685 | break; |
| 1602 | 1686 | case VB2_BUF_STATE_PREPARING: |
| 1603 | − dprintk(1, "buffer still being prepared\n"); | |
| 1687 | + dprintk(q, 1, "buffer still being prepared\n"); | |
| 1604 | 1688 | return -EINVAL; |
| 1605 | 1689 | default: |
| 1606 | − dprintk(1, "invalid buffer state %d\n", vb->state); | |
| 1690 | + dprintk(q, 1, "invalid buffer state %s\n", | |
| 1691 | + vb2_state_name(vb->state)); | |
| 1607 | 1692 | return -EINVAL; |
| 1608 | 1693 | } |
| 1609 | 1694 | |
| @@ -1645,7 +1730,7 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, · 35 unmodified lines | ||
| 1645 | 1730 | return ret; |
| 1646 | 1731 | } |
| 1647 | 1732 | |
| 1648 | − dprintk(2, "qbuf of buffer %d succeeded\n", vb->index); | |
| 1733 | + dprintk(q, 2, "qbuf of buffer %d succeeded\n", vb->index); | |
| 1649 | 1734 | return 0; |
| 1650 | 1735 | } |
| 1651 | 1736 | EXPORT_SYMBOL_GPL(vb2_core_qbuf); |
| @@ -1671,22 +1756,22 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) · 19 unmodified lines | ||
| 1671 | 1756 | int ret; |
| 1672 | 1757 | |
| 1673 | 1758 | if (q->waiting_in_dqbuf) { |
| 1674 | − dprintk(1, "another dup()ped fd is waiting for a buffer\n"); | |
| 1759 | + dprintk(q, 1, "another dup()ped fd is waiting for a buffer\n"); | |
| 1675 | 1760 | return -EBUSY; |
| 1676 | 1761 | } |
| 1677 | 1762 | |
| 1678 | 1763 | if (!q->streaming) { |
| 1679 | − dprintk(1, "streaming off, will not wait for buffers\n"); | |
| 1764 | + dprintk(q, 1, "streaming off, will not wait for buffers\n"); | |
| 1680 | 1765 | return -EINVAL; |
| 1681 | 1766 | } |
| 1682 | 1767 | |
| 1683 | 1768 | if (q->error) { |
| 1684 | − dprintk(1, "Queue in error state, will not wait for buffers\n"); | |
| 1769 | + dprintk(q, 1, "Queue in error state, will not wait for buffers\n"); | |
| 1685 | 1770 | return -EIO; |
| 1686 | 1771 | } |
| 1687 | 1772 | |
| 1688 | 1773 | if (q->last_buffer_dequeued) { |
| 1689 | − dprintk(3, "last buffer dequeued already, will not wait for buffers\n"); | |
| 1774 | + dprintk(q, 3, "last buffer dequeued already, will not wait for buffers\n"); | |
| 1690 | 1775 | return -EPIPE; |
| 1691 | 1776 | } |
| 1692 | 1777 | |
| @@ -1698,7 +1783,7 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) · 5 unmodified lines | ||
| 1698 | 1783 | } |
| 1699 | 1784 | |
| 1700 | 1785 | if (nonblocking) { |
| 1701 | − dprintk(3, "nonblocking and no buffers to dequeue, will not wait\n"); | |
| 1786 | + dprintk(q, 3, "nonblocking and no buffers to dequeue, will not wait\n"); | |
| 1702 | 1787 | return -EAGAIN; |
| 1703 | 1788 | } |
| 1704 | 1789 | |
| @@ -1713,7 +1798,7 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) · 8 unmodified lines | ||
| 1713 | 1798 | /* |
| 1714 | 1799 | * All locks have been released, it is safe to sleep now. |
| 1715 | 1800 | */ |
| 1716 | − dprintk(3, "will sleep waiting for buffers\n"); | |
| 1801 | + dprintk(q, 3, "will sleep waiting for buffers\n"); | |
| 1717 | 1802 | ret = wait_event_interruptible(q->done_wq, |
| 1718 | 1803 | !list_empty(&q->done_list) || !q->streaming || |
| 1719 | 1804 | q->error); |
| @@ -1725,7 +1810,7 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) · 5 unmodified lines | ||
| 1725 | 1810 | call_void_qop(q, wait_finish, q); |
| 1726 | 1811 | q->waiting_in_dqbuf = 0; |
| 1727 | 1812 | if (ret) { |
| 1728 | − dprintk(1, "sleep was interrupted\n"); | |
| 1813 | + dprintk(q, 1, "sleep was interrupted\n"); | |
| 1729 | 1814 | return ret; |
| 1730 | 1815 | } |
| 1731 | 1816 | } |
| @@ -1773,7 +1858,7 @@ static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb, · 41 unmodified lines | ||
| 1773 | 1858 | int vb2_wait_for_all_buffers(struct vb2_queue *q) |
| 1774 | 1859 | { |
| 1775 | 1860 | if (!q->streaming) { |
| 1776 | − dprintk(1, "streaming off, will not wait for buffers\n"); | |
| 1861 | + dprintk(q, 1, "streaming off, will not wait for buffers\n"); | |
| 1777 | 1862 | return -EINVAL; |
| 1778 | 1863 | } |
| 1779 | 1864 | |
| @@ -1811,13 +1896,14 @@ int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb, · 31 unmodified lines | ||
| 1811 | 1896 | |
| 1812 | 1897 | switch (vb->state) { |
| 1813 | 1898 | case VB2_BUF_STATE_DONE: |
| 1814 | − dprintk(3, "returning done buffer\n"); | |
| 1899 | + dprintk(q, 3, "returning done buffer\n"); | |
| 1815 | 1900 | break; |
| 1816 | 1901 | case VB2_BUF_STATE_ERROR: |
| 1817 | − dprintk(3, "returning done buffer with errors\n"); | |
| 1902 | + dprintk(q, 3, "returning done buffer with errors\n"); | |
| 1818 | 1903 | break; |
| 1819 | 1904 | default: |
| 1820 | − dprintk(1, "invalid buffer state\n"); | |
| 1905 | + dprintk(q, 1, "invalid buffer state %s\n", | |
| 1906 | + vb2_state_name(vb->state)); | |
| 1821 | 1907 | return -EINVAL; |
| 1822 | 1908 | } |
| 1823 | 1909 | |
| @@ -1848,8 +1934,8 @@ int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb, · 24 unmodified lines | ||
| 1848 | 1934 | media_request_put(vb->request); |
| 1849 | 1935 | vb->request = NULL; |
| 1850 | 1936 | |
| 1851 | − dprintk(2, "dqbuf of buffer %d, with state %d\n", | |
| 1852 | − vb->index, vb->state); | |
| 1937 | + dprintk(q, 2, "dqbuf of buffer %d, state: %s\n", | |
| 1938 | + vb->index, vb2_state_name(vb->state)); | |
| 1853 | 1939 | |
| 1854 | 1940 | return 0; |
| 1855 | 1941 | |
| @@ -1940,14 +2026,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q) · 84 unmodified lines | ||
| 1940 | 2026 | call_void_vb_qop(vb, buf_request_complete, vb); |
| 1941 | 2027 | } |
| 1942 | 2028 | |
| 1943 | − if (vb->synced) { | |
| 1944 | − unsigned int plane; | |
| 1945 | − | |
| 1946 | − for (plane = 0; plane < vb->num_planes; ++plane) | |
| 1947 | − call_void_memop(vb, finish, | |
| 1948 | − vb->planes[plane].mem_priv); | |
| 1949 | − vb->synced = 0; | |
| 1950 | − } | |
| 2029 | + __vb2_buf_mem_finish(vb); | |
| 1951 | 2030 | |
| 1952 | 2031 | if (vb->prepared) { |
| 1953 | 2032 | call_void_vb_qop(vb, buf_finish, vb); |
| @@ -1971,22 +2050,22 @@ int vb2_core_streamon(struct vb2_queue *q, unsigned int type) · 17 unmodified lines | ||
| 1971 | 2050 | int ret; |
| 1972 | 2051 | |
| 1973 | 2052 | if (type != q->type) { |
| 1974 | − dprintk(1, "invalid stream type\n"); | |
| 2053 | + dprintk(q, 1, "invalid stream type\n"); | |
| 1975 | 2054 | return -EINVAL; |
| 1976 | 2055 | } |
| 1977 | 2056 | |
| 1978 | 2057 | if (q->streaming) { |
| 1979 | − dprintk(3, "already streaming\n"); | |
| 2058 | + dprintk(q, 3, "already streaming\n"); | |
| 1980 | 2059 | return 0; |
| 1981 | 2060 | } |
| 1982 | 2061 | |
| 1983 | 2062 | if (!q->num_buffers) { |
| 1984 | − dprintk(1, "no buffers have been allocated\n"); | |
| 2063 | + dprintk(q, 1, "no buffers have been allocated\n"); | |
| 1985 | 2064 | return -EINVAL; |
| 1986 | 2065 | } |
| 1987 | 2066 | |
| 1988 | 2067 | if (q->num_buffers < q->min_buffers_needed) { |
| 1989 | − dprintk(1, "need at least %u allocated buffers\n", | |
| 2068 | + dprintk(q, 1, "need at least %u allocated buffers\n", | |
| 1990 | 2069 | q->min_buffers_needed); |
| 1991 | 2070 | return -EINVAL; |
| 1992 | 2071 | } |
| @@ -2006,7 +2085,7 @@ int vb2_core_streamon(struct vb2_queue *q, unsigned int type) · 13 unmodified lines | ||
| 2006 | 2085 | |
| 2007 | 2086 | q->streaming = 1; |
| 2008 | 2087 | |
| 2009 | − dprintk(3, "successful\n"); | |
| 2088 | + dprintk(q, 3, "successful\n"); | |
| 2010 | 2089 | return 0; |
| 2011 | 2090 | } |
| 2012 | 2091 | EXPORT_SYMBOL_GPL(vb2_core_streamon); |
| @@ -2022,7 +2101,7 @@ EXPORT_SYMBOL_GPL(vb2_queue_error); · 9 unmodified lines | ||
| 2022 | 2101 | int vb2_core_streamoff(struct vb2_queue *q, unsigned int type) |
| 2023 | 2102 | { |
| 2024 | 2103 | if (type != q->type) { |
| 2025 | − dprintk(1, "invalid stream type\n"); | |
| 2104 | + dprintk(q, 1, "invalid stream type\n"); | |
| 2026 | 2105 | return -EINVAL; |
| 2027 | 2106 | } |
| 2028 | 2107 | |
| @@ -2039,7 +2118,7 @@ int vb2_core_streamoff(struct vb2_queue *q, unsigned int type) · 10 unmodified lines | ||
| 2039 | 2118 | q->waiting_for_buffers = !q->is_output; |
| 2040 | 2119 | q->last_buffer_dequeued = false; |
| 2041 | 2120 | |
| 2042 | − dprintk(3, "successful\n"); | |
| 2121 | + dprintk(q, 3, "successful\n"); | |
| 2043 | 2122 | return 0; |
| 2044 | 2123 | } |
| 2045 | 2124 | EXPORT_SYMBOL_GPL(vb2_core_streamoff); |
| @@ -2082,39 +2161,39 @@ int vb2_core_expbuf(struct vb2_queue *q, int *fd, unsigned int type, · 36 unmodified lines | ||
| 2082 | 2161 | struct dma_buf *dbuf; |
| 2083 | 2162 | |
| 2084 | 2163 | if (q->memory != VB2_MEMORY_MMAP) { |
| 2085 | − dprintk(1, "queue is not currently set up for mmap\n"); | |
| 2164 | + dprintk(q, 1, "queue is not currently set up for mmap\n"); | |
| 2086 | 2165 | return -EINVAL; |
| 2087 | 2166 | } |
| 2088 | 2167 | |
| 2089 | 2168 | if (!q->mem_ops->get_dmabuf) { |
| 2090 | − dprintk(1, "queue does not support DMA buffer exporting\n"); | |
| 2169 | + dprintk(q, 1, "queue does not support DMA buffer exporting\n"); | |
| 2091 | 2170 | return -EINVAL; |
| 2092 | 2171 | } |
| 2093 | 2172 | |
| 2094 | 2173 | if (flags & ~(O_CLOEXEC | O_ACCMODE)) { |
| 2095 | − dprintk(1, "queue does support only O_CLOEXEC and access mode flags\n"); | |
| 2174 | + dprintk(q, 1, "queue does support only O_CLOEXEC and access mode flags\n"); | |
| 2096 | 2175 | return -EINVAL; |
| 2097 | 2176 | } |
| 2098 | 2177 | |
| 2099 | 2178 | if (type != q->type) { |
| 2100 | − dprintk(1, "invalid buffer type\n"); | |
| 2179 | + dprintk(q, 1, "invalid buffer type\n"); | |
| 2101 | 2180 | return -EINVAL; |
| 2102 | 2181 | } |
| 2103 | 2182 | |
| 2104 | 2183 | if (index >= q->num_buffers) { |
| 2105 | − dprintk(1, "buffer index out of range\n"); | |
| 2184 | + dprintk(q, 1, "buffer index out of range\n"); | |
| 2106 | 2185 | return -EINVAL; |
| 2107 | 2186 | } |
| 2108 | 2187 | |
| 2109 | 2188 | vb = q->bufs[index]; |
| 2110 | 2189 | |
| 2111 | 2190 | if (plane >= vb->num_planes) { |
| 2112 | − dprintk(1, "buffer plane out of range\n"); | |
| 2191 | + dprintk(q, 1, "buffer plane out of range\n"); | |
| 2113 | 2192 | return -EINVAL; |
| 2114 | 2193 | } |
| 2115 | 2194 | |
| 2116 | 2195 | if (vb2_fileio_is_active(q)) { |
| 2117 | − dprintk(1, "expbuf: file io in progress\n"); | |
| 2196 | + dprintk(q, 1, "expbuf: file io in progress\n"); | |
| 2118 | 2197 | return -EBUSY; |
| 2119 | 2198 | } |
| 2120 | 2199 | |
| @@ -2123,20 +2202,20 @@ int vb2_core_expbuf(struct vb2_queue *q, int *fd, unsigned int type, · 2 unmodified lines | ||
| 2123 | 2202 | dbuf = call_ptr_memop(vb, get_dmabuf, vb_plane->mem_priv, |
| 2124 | 2203 | flags & O_ACCMODE); |
| 2125 | 2204 | if (IS_ERR_OR_NULL(dbuf)) { |
| 2126 | − dprintk(1, "failed to export buffer %d, plane %d\n", | |
| 2205 | + dprintk(q, 1, "failed to export buffer %d, plane %d\n", | |
| 2127 | 2206 | index, plane); |
| 2128 | 2207 | return -EINVAL; |
| 2129 | 2208 | } |
| 2130 | 2209 | |
| 2131 | 2210 | ret = dma_buf_fd(dbuf, flags & ~O_ACCMODE); |
| 2132 | 2211 | if (ret < 0) { |
| 2133 | − dprintk(3, "buffer %d, plane %d failed to export (%d)\n", | |
| 2212 | + dprintk(q, 3, "buffer %d, plane %d failed to export (%d)\n", | |
| 2134 | 2213 | index, plane, ret); |
| 2135 | 2214 | dma_buf_put(dbuf); |
| 2136 | 2215 | return ret; |
| 2137 | 2216 | } |
| 2138 | 2217 | |
| 2139 | − dprintk(3, "buffer %d, plane %d exported as %d descriptor\n", | |
| 2218 | + dprintk(q, 3, "buffer %d, plane %d exported as %d descriptor\n", | |
| 2140 | 2219 | index, plane, ret); |
| 2141 | 2220 | *fd = ret; |
| 2142 | 2221 | |
| @@ -2153,7 +2232,7 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) · 10 unmodified lines | ||
| 2153 | 2232 | unsigned long length; |
| 2154 | 2233 | |
| 2155 | 2234 | if (q->memory != VB2_MEMORY_MMAP) { |
| 2156 | − dprintk(1, "queue is not currently set up for mmap\n"); | |
| 2235 | + dprintk(q, 1, "queue is not currently set up for mmap\n"); | |
| 2157 | 2236 | return -EINVAL; |
| 2158 | 2237 | } |
| 2159 | 2238 | |
| @@ -2161,17 +2240,17 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) · 1 unmodified line | ||
| 2161 | 2240 | * Check memory area access mode. |
| 2162 | 2241 | */ |
| 2163 | 2242 | if (!(vma->vm_flags & VM_SHARED)) { |
| 2164 | − dprintk(1, "invalid vma flags, VM_SHARED needed\n"); | |
| 2243 | + dprintk(q, 1, "invalid vma flags, VM_SHARED needed\n"); | |
| 2165 | 2244 | return -EINVAL; |
| 2166 | 2245 | } |
| 2167 | 2246 | if (q->is_output) { |
| 2168 | 2247 | if (!(vma->vm_flags & VM_WRITE)) { |
| 2169 | − dprintk(1, "invalid vma flags, VM_WRITE needed\n"); | |
| 2248 | + dprintk(q, 1, "invalid vma flags, VM_WRITE needed\n"); | |
| 2170 | 2249 | return -EINVAL; |
| 2171 | 2250 | } |
| 2172 | 2251 | } else { |
| 2173 | 2252 | if (!(vma->vm_flags & VM_READ)) { |
| 2174 | − dprintk(1, "invalid vma flags, VM_READ needed\n"); | |
| 2253 | + dprintk(q, 1, "invalid vma flags, VM_READ needed\n"); | |
| 2175 | 2254 | return -EINVAL; |
| 2176 | 2255 | } |
| 2177 | 2256 | } |
| @@ -2179,7 +2258,7 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) · 1 unmodified line | ||
| 2179 | 2258 | mutex_lock(&q->mmap_lock); |
| 2180 | 2259 | |
| 2181 | 2260 | if (vb2_fileio_is_active(q)) { |
| 2182 | − dprintk(1, "mmap: file io in progress\n"); | |
| 2261 | + dprintk(q, 1, "mmap: file io in progress\n"); | |
| 2183 | 2262 | ret = -EBUSY; |
| 2184 | 2263 | goto unlock; |
| 2185 | 2264 | } |
| @@ -2200,7 +2279,7 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) · 14 unmodified lines | ||
| 2200 | 2279 | */ |
| 2201 | 2280 | length = PAGE_ALIGN(vb->planes[plane].length); |
| 2202 | 2281 | if (length < (vma->vm_end - vma->vm_start)) { |
| 2203 | − dprintk(1, | |
| 2282 | + dprintk(q, 1, | |
| 2204 | 2283 | "MMAP invalid, as it would overflow buffer length\n"); |
| 2205 | 2284 | ret = -EINVAL; |
| 2206 | 2285 | goto unlock; |
| @@ -2220,7 +2299,7 @@ unlock: · 13 unmodified lines | ||
| 2220 | 2299 | if (ret) |
| 2221 | 2300 | return ret; |
| 2222 | 2301 | |
| 2223 | − dprintk(3, "buffer %d, plane %d successfully mapped\n", buffer, plane); | |
| 2302 | + dprintk(q, 3, "buffer %d, plane %d successfully mapped\n", buffer, plane); | |
| 2224 | 2303 | return 0; |
| 2225 | 2304 | } |
| 2226 | 2305 | EXPORT_SYMBOL_GPL(vb2_mmap); |
| @@ -2239,7 +2318,7 @@ unsigned long vb2_get_unmapped_area(struct vb2_queue *q, · 12 unmodified lines | ||
| 2239 | 2318 | int ret; |
| 2240 | 2319 | |
| 2241 | 2320 | if (q->memory != VB2_MEMORY_MMAP) { |
| 2242 | − dprintk(1, "queue is not currently set up for mmap\n"); | |
| 2321 | + dprintk(q, 1, "queue is not currently set up for mmap\n"); | |
| 2243 | 2322 | return -EINVAL; |
| 2244 | 2323 | } |
| 2245 | 2324 | |
| @@ -2291,6 +2370,10 @@ int vb2_core_queue_init(struct vb2_queue *q) · 45 unmodified lines | ||
| 2291 | 2370 | else |
| 2292 | 2371 | q->dma_dir = q->is_output ? DMA_TO_DEVICE : DMA_FROM_DEVICE; |
| 2293 | 2372 | |
| 2373 | + if (q->name[0] == '\0') | |
| 2374 | + snprintf(q->name, sizeof(q->name), "%s-%p", | |
| 2375 | + q->is_output ? "out" : "cap", q); | |
| 2376 | + | |
| 2294 | 2377 | return 0; |
| 2295 | 2378 | } |
| 2296 | 2379 | EXPORT_SYMBOL_GPL(vb2_core_queue_init); |
| @@ -2479,7 +2562,7 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) · 182 unmodified lines | ||
| 2479 | 2562 | */ |
| 2480 | 2563 | count = 1; |
| 2481 | 2564 | |
| 2482 | − dprintk(3, "setting up file io: mode %s, count %d, read_once %d, write_immediately %d\n", | |
| 2565 | + dprintk(q, 3, "setting up file io: mode %s, count %d, read_once %d, write_immediately %d\n", | |
| 2483 | 2566 | (read) ? "read" : "write", count, q->fileio_read_once, |
| 2484 | 2567 | q->fileio_write_immediately); |
| 2485 | 2568 | |
| @@ -2498,7 +2581,7 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) · 12 unmodified lines | ||
| 2498 | 2581 | fileio->memory = VB2_MEMORY_MMAP; |
| 2499 | 2582 | fileio->type = q->type; |
| 2500 | 2583 | q->fileio = fileio; |
| 2501 | − ret = vb2_core_reqbufs(q, fileio->memory, &fileio->count); | |
| 2584 | + ret = vb2_core_reqbufs(q, fileio->memory, 0, &fileio->count); | |
| 2502 | 2585 | if (ret) |
| 2503 | 2586 | goto err_kfree; |
| 2504 | 2587 | |
| @@ -2555,7 +2638,7 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) · 50 unmodified lines | ||
| 2555 | 2638 | |
| 2556 | 2639 | err_reqbufs: |
| 2557 | 2640 | fileio->count = 0; |
| 2558 | − vb2_core_reqbufs(q, fileio->memory, &fileio->count); | |
| 2641 | + vb2_core_reqbufs(q, fileio->memory, 0, &fileio->count); | |
| 2559 | 2642 | |
| 2560 | 2643 | err_kfree: |
| 2561 | 2644 | q->fileio = NULL; |
| @@ -2575,9 +2658,9 @@ static int __vb2_cleanup_fileio(struct vb2_queue *q) · 13 unmodified lines | ||
| 2575 | 2658 | vb2_core_streamoff(q, q->type); |
| 2576 | 2659 | q->fileio = NULL; |
| 2577 | 2660 | fileio->count = 0; |
| 2578 | − vb2_core_reqbufs(q, fileio->memory, &fileio->count); | |
| 2661 | + vb2_core_reqbufs(q, fileio->memory, 0, &fileio->count); | |
| 2579 | 2662 | kfree(fileio); |
| 2580 | − dprintk(3, "file io emulator closed\n"); | |
| 2663 | + dprintk(q, 3, "file io emulator closed\n"); | |
| 2581 | 2664 | } |
| 2582 | 2665 | return 0; |
| 2583 | 2666 | } |
| @@ -2606,7 +2689,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ · 22 unmodified lines | ||
| 2606 | 2689 | unsigned index; |
| 2607 | 2690 | int ret; |
| 2608 | 2691 | |
| 2609 | − dprintk(3, "mode %s, offset %ld, count %zd, %sblocking\n", | |
| 2692 | + dprintk(q, 3, "mode %s, offset %ld, count %zd, %sblocking\n", | |
| 2610 | 2693 | read ? "read" : "write", (long)*ppos, count, |
| 2611 | 2694 | nonblock ? "non" : ""); |
| 2612 | 2695 | |
| @@ -2614,7 +2697,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ · 1 unmodified line | ||
| 2614 | 2697 | return -EINVAL; |
| 2615 | 2698 | |
| 2616 | 2699 | if (q->waiting_in_dqbuf) { |
| 2617 | − dprintk(3, "another dup()ped fd is %s\n", | |
| 2700 | + dprintk(q, 3, "another dup()ped fd is %s\n", | |
| 2618 | 2701 | read ? "reading" : "writing"); |
| 2619 | 2702 | return -EBUSY; |
| 2620 | 2703 | } |
| @@ -2624,7 +2707,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ · 3 unmodified lines | ||
| 2624 | 2707 | */ |
| 2625 | 2708 | if (!vb2_fileio_is_active(q)) { |
| 2626 | 2709 | ret = __vb2_init_fileio(q, read); |
| 2627 | − dprintk(3, "vb2_init_fileio result: %d\n", ret); | |
| 2710 | + dprintk(q, 3, "vb2_init_fileio result: %d\n", ret); | |
| 2628 | 2711 | if (ret) |
| 2629 | 2712 | return ret; |
| 2630 | 2713 | } |
| @@ -2641,7 +2724,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ · 10 unmodified lines | ||
| 2641 | 2724 | * Call vb2_dqbuf to get buffer back. |
| 2642 | 2725 | */ |
| 2643 | 2726 | ret = vb2_core_dqbuf(q, &index, NULL, nonblock); |
| 2644 | − dprintk(5, "vb2_dqbuf result: %d\n", ret); | |
| 2727 | + dprintk(q, 5, "vb2_dqbuf result: %d\n", ret); | |
| 2645 | 2728 | if (ret) |
| 2646 | 2729 | return ret; |
| 2647 | 2730 | fileio->dq_count += 1; |
| @@ -2672,20 +2755,20 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ · 24 unmodified lines | ||
| 2672 | 2755 | */ |
| 2673 | 2756 | if (buf->pos + count > buf->size) { |
| 2674 | 2757 | count = buf->size - buf->pos; |
| 2675 | − dprintk(5, "reducing read count: %zd\n", count); | |
| 2758 | + dprintk(q, 5, "reducing read count: %zd\n", count); | |
| 2676 | 2759 | } |
| 2677 | 2760 | |
| 2678 | 2761 | /* |
| 2679 | 2762 | * Transfer data to userspace. |
| 2680 | 2763 | */ |
| 2681 | − dprintk(3, "copying %zd bytes - buffer %d, offset %u\n", | |
| 2764 | + dprintk(q, 3, "copying %zd bytes - buffer %d, offset %u\n", | |
| 2682 | 2765 | count, index, buf->pos); |
| 2683 | 2766 | if (read) |
| 2684 | 2767 | ret = copy_to_user(data, buf->vaddr + buf->pos, count); |
| 2685 | 2768 | else |
| 2686 | 2769 | ret = copy_from_user(buf->vaddr + buf->pos, data, count); |
| 2687 | 2770 | if (ret) { |
| 2688 | − dprintk(3, "error copying data\n"); | |
| 2771 | + dprintk(q, 3, "error copying data\n"); | |
| 2689 | 2772 | return -EFAULT; |
| 2690 | 2773 | } |
| 2691 | 2774 | |
| @@ -2705,7 +2788,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ · 13 unmodified lines | ||
| 2705 | 2788 | * Check if this is the last buffer to read. |
| 2706 | 2789 | */ |
| 2707 | 2790 | if (read && fileio->read_once && fileio->dq_count == 1) { |
| 2708 | − dprintk(3, "read limit reached\n"); | |
| 2791 | + dprintk(q, 3, "read limit reached\n"); | |
| 2709 | 2792 | return __vb2_cleanup_fileio(q); |
| 2710 | 2793 | } |
| 2711 | 2794 | |
| @@ -2717,7 +2800,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ · 5 unmodified lines | ||
| 2717 | 2800 | if (copy_timestamp) |
| 2718 | 2801 | b->timestamp = ktime_get_ns(); |
| 2719 | 2802 | ret = vb2_core_qbuf(q, index, NULL, NULL); |
| 2720 | − dprintk(5, "vb2_dbuf result: %d\n", ret); | |
| 2803 | + dprintk(q, 5, "vb2_dbuf result: %d\n", ret); | |
| 2721 | 2804 | if (ret) |
| 2722 | 2805 | return ret; |
| 2723 | 2806 | |
| @@ -2804,7 +2887,7 @@ static int vb2_thread(void *data) · 80 unmodified lines | ||
| 2804 | 2887 | if (!threadio->stop) |
| 2805 | 2888 | ret = vb2_core_dqbuf(q, &index, NULL, 0); |
| 2806 | 2889 | call_void_qop(q, wait_prepare, q); |
| 2807 | − dprintk(5, "file io: vb2_dqbuf result: %d\n", ret); | |
| 2890 | + dprintk(q, 5, "file io: vb2_dqbuf result: %d\n", ret); | |
| 2808 | 2891 | if (!ret) |
| 2809 | 2892 | vb = q->bufs[index]; |
| 2810 | 2893 | } |
| @@ -2858,7 +2941,7 @@ int vb2_thread_start(struct vb2_queue *q, vb2_thread_fnc fnc, void *priv, · 47 unmodified lines | ||
| 2858 | 2941 | threadio->priv = priv; |
| 2859 | 2942 | |
| 2860 | 2943 | ret = __vb2_init_fileio(q, !q->is_output); |
| 2861 | − dprintk(3, "file io: vb2_init_fileio result: %d\n", ret); | |
| 2944 | + dprintk(q, 3, "file io: vb2_init_fileio result: %d\n", ret); | |
| 2862 | 2945 | if (ret) |
| 2863 | 2946 | goto nomem; |
| 2864 | 2947 | q->threadio = threadio; |
| @@ -42,6 +42,11 @@ struct vb2_dc_buf { · 41 unmodified lines | ||
| 42 | 42 | struct dma_buf_attachment *db_attach; |
| 43 | 43 | }; |
| 44 | 44 | |
| 45 | +static inline bool vb2_dc_buffer_consistent(unsigned long attr) | |
| 46 | +{ | |
| 47 | + return !(attr & DMA_ATTR_NON_CONSISTENT); | |
| 48 | +} | |
| 49 | + | |
| 45 | 50 | /*********************************************/ |
| 46 | 51 | /* scatterlist table functions */ |
| 47 | 52 | /*********************************************/ |
| @@ -95,8 +100,7 @@ static void vb2_dc_prepare(void *buf_priv) · 47 unmodified lines | ||
| 95 | 100 | struct vb2_dc_buf *buf = buf_priv; |
| 96 | 101 | struct sg_table *sgt = buf->dma_sgt; |
| 97 | 102 | |
| 98 | − /* DMABUF exporter will flush the cache for us */ | |
| 99 | − if (!sgt || buf->db_attach) | |
| 103 | + if (!sgt) | |
| 100 | 104 | return; |
| 101 | 105 | |
| 102 | 106 | dma_sync_sg_for_device(buf->dev, sgt->sgl, sgt->orig_nents, |
| @@ -108,8 +112,7 @@ static void vb2_dc_finish(void *buf_priv) · 5 unmodified lines | ||
| 108 | 112 | struct vb2_dc_buf *buf = buf_priv; |
| 109 | 113 | struct sg_table *sgt = buf->dma_sgt; |
| 110 | 114 | |
| 111 | − /* DMABUF exporter will flush the cache for us */ | |
| 112 | − if (!sgt || buf->db_attach) | |
| 115 | + if (!sgt) | |
| 113 | 116 | return; |
| 114 | 117 | |
| 115 | 118 | 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, · 33 unmodified lines | ||
| 149 | 152 | if (!buf) |
| 150 | 153 | return ERR_PTR(-ENOMEM); |
| 151 | 154 | |
| 152 | − if (attrs) | |
| 153 | − buf->attrs = attrs; | |
| 155 | + buf->attrs = attrs; | |
| 154 | 156 | buf->cookie = dma_alloc_attrs(dev, size, &buf->dma_addr, |
| 155 | 157 | GFP_KERNEL | gfp_flags, buf->attrs); |
| 156 | 158 | if (!buf->cookie) { |
| @@ -335,6 +337,34 @@ static void vb2_dc_dmabuf_ops_release(struct dma_buf *dbuf) · 178 unmodified lines | ||
| 335 | 337 | vb2_dc_put(dbuf->priv); |
| 336 | 338 | } |
| 337 | 339 | |
| 340 | +static int | |
| 341 | +vb2_dc_dmabuf_ops_begin_cpu_access(struct dma_buf *dbuf, | |
| 342 | + enum dma_data_direction direction) | |
| 343 | +{ | |
| 344 | + struct vb2_dc_buf *buf = dbuf->priv; | |
| 345 | + struct sg_table *sgt = buf->dma_sgt; | |
| 346 | + | |
| 347 | + if (vb2_dc_buffer_consistent(buf->attrs)) | |
| 348 | + return 0; | |
| 349 | + | |
| 350 | + dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); | |
| 351 | + return 0; | |
| 352 | +} | |
| 353 | + | |
| 354 | +static int | |
| 355 | +vb2_dc_dmabuf_ops_end_cpu_access(struct dma_buf *dbuf, | |
| 356 | + enum dma_data_direction direction) | |
| 357 | +{ | |
| 358 | + struct vb2_dc_buf *buf = dbuf->priv; | |
| 359 | + struct sg_table *sgt = buf->dma_sgt; | |
| 360 | + | |
| 361 | + if (vb2_dc_buffer_consistent(buf->attrs)) | |
| 362 | + return 0; | |
| 363 | + | |
| 364 | + dma_sync_sg_for_device(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); | |
| 365 | + return 0; | |
| 366 | +} | |
| 367 | + | |
| 338 | 368 | static void *vb2_dc_dmabuf_ops_vmap(struct dma_buf *dbuf) |
| 339 | 369 | { |
| 340 | 370 | struct vb2_dc_buf *buf = dbuf->priv; |
| @@ -353,6 +383,8 @@ static const struct dma_buf_ops vb2_dc_dmabuf_ops = { · 12 unmodified lines | ||
| 353 | 383 | .detach = vb2_dc_dmabuf_ops_detach, |
| 354 | 384 | .map_dma_buf = vb2_dc_dmabuf_ops_map, |
| 355 | 385 | .unmap_dma_buf = vb2_dc_dmabuf_ops_unmap, |
| 386 | + .begin_cpu_access = vb2_dc_dmabuf_ops_begin_cpu_access, | |
| 387 | + .end_cpu_access = vb2_dc_dmabuf_ops_end_cpu_access, | |
| 356 | 388 | .vmap = vb2_dc_dmabuf_ops_vmap, |
| 357 | 389 | .mmap = vb2_dc_dmabuf_ops_mmap, |
| 358 | 390 | .release = vb2_dc_dmabuf_ops_release, |
| @@ -120,6 +120,12 @@ static void *vb2_dma_sg_alloc(struct device *dev, unsigned long dma_attrs, · 119 unmodified lines | ||
| 120 | 120 | buf->num_pages = size >> PAGE_SHIFT; |
| 121 | 121 | buf->dma_sgt = &buf->sg_table; |
| 122 | 122 | |
| 123 | + /* | |
| 124 | + * NOTE: dma-sg allocates memory using the page allocator directly, so | |
| 125 | + * there is no memory consistency guarantee, hence dma-sg ignores DMA | |
| 126 | + * attributes passed from the upper layer. That means that | |
| 127 | + * V4L2_FLAG_MEMORY_NON_CONSISTENT has no effect on dma-sg buffers. | |
| 128 | + */ | |
| 123 | 129 | buf->pages = kvmalloc_array(buf->num_pages, sizeof(struct page *), |
| 124 | 130 | GFP_KERNEL | __GFP_ZERO); |
| 125 | 131 | if (!buf->pages) |
| @@ -198,10 +204,6 @@ static void vb2_dma_sg_prepare(void *buf_priv) · 72 unmodified lines | ||
| 198 | 204 | struct vb2_dma_sg_buf *buf = buf_priv; |
| 199 | 205 | struct sg_table *sgt = buf->dma_sgt; |
| 200 | 206 | |
| 201 | − /* DMABUF exporter will flush the cache for us */ | |
| 202 | − if (buf->db_attach) | |
| 203 | − return; | |
| 204 | − | |
| 205 | 207 | dma_sync_sg_for_device(buf->dev, sgt->sgl, sgt->orig_nents, |
| 206 | 208 | buf->dma_dir); |
| 207 | 209 | } |
| @@ -211,10 +213,6 @@ static void vb2_dma_sg_finish(void *buf_priv) · 3 unmodified lines | ||
| 211 | 213 | struct vb2_dma_sg_buf *buf = buf_priv; |
| 212 | 214 | struct sg_table *sgt = buf->dma_sgt; |
| 213 | 215 | |
| 214 | − /* DMABUF exporter will flush the cache for us */ | |
| 215 | − if (buf->db_attach) | |
| 216 | − return; | |
| 217 | − | |
| 218 | 216 | dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir); |
| 219 | 217 | } |
| 220 | 218 | |
| @@ -469,6 +467,28 @@ static void vb2_dma_sg_dmabuf_ops_release(struct dma_buf *dbuf) · 248 unmodified lines | ||
| 469 | 467 | vb2_dma_sg_put(dbuf->priv); |
| 470 | 468 | } |
| 471 | 469 | |
| 470 | +static int | |
| 471 | +vb2_dma_sg_dmabuf_ops_begin_cpu_access(struct dma_buf *dbuf, | |
| 472 | + enum dma_data_direction direction) | |
| 473 | +{ | |
| 474 | + struct vb2_dma_sg_buf *buf = dbuf->priv; | |
| 475 | + struct sg_table *sgt = buf->dma_sgt; | |
| 476 | + | |
| 477 | + dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); | |
| 478 | + return 0; | |
| 479 | +} | |
| 480 | + | |
| 481 | +static int | |
| 482 | +vb2_dma_sg_dmabuf_ops_end_cpu_access(struct dma_buf *dbuf, | |
| 483 | + enum dma_data_direction direction) | |
| 484 | +{ | |
| 485 | + struct vb2_dma_sg_buf *buf = dbuf->priv; | |
| 486 | + struct sg_table *sgt = buf->dma_sgt; | |
| 487 | + | |
| 488 | + dma_sync_sg_for_device(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); | |
| 489 | + return 0; | |
| 490 | +} | |
| 491 | + | |
| 472 | 492 | static void *vb2_dma_sg_dmabuf_ops_vmap(struct dma_buf *dbuf) |
| 473 | 493 | { |
| 474 | 494 | struct vb2_dma_sg_buf *buf = dbuf->priv; |
| @@ -487,6 +507,8 @@ static const struct dma_buf_ops vb2_dma_sg_dmabuf_ops = { · 12 unmodified lines | ||
| 487 | 507 | .detach = vb2_dma_sg_dmabuf_ops_detach, |
| 488 | 508 | .map_dma_buf = vb2_dma_sg_dmabuf_ops_map, |
| 489 | 509 | .unmap_dma_buf = vb2_dma_sg_dmabuf_ops_unmap, |
| 510 | + .begin_cpu_access = vb2_dma_sg_dmabuf_ops_begin_cpu_access, | |
| 511 | + .end_cpu_access = vb2_dma_sg_dmabuf_ops_end_cpu_access, | |
| 490 | 512 | .vmap = vb2_dma_sg_dmabuf_ops_vmap, |
| 491 | 513 | .mmap = vb2_dma_sg_dmabuf_ops_mmap, |
| 492 | 514 | .release = vb2_dma_sg_dmabuf_ops_release, |
| @@ -35,10 +35,11 @@ · 34 unmodified lines | ||
| 35 | 35 | static int debug; |
| 36 | 36 | module_param(debug, int, 0644); |
| 37 | 37 | |
| 38 | −#define dprintk(level, fmt, arg...) \ | |
| 38 | +#define dprintk(q, level, fmt, arg...) \ | |
| 39 | 39 | do { \ |
| 40 | 40 | if (debug >= level) \ |
| 41 | − pr_info("vb2-v4l2: %s: " fmt, __func__, ## arg); \ | |
| 41 | + pr_info("vb2-v4l2: [%p] %s: " fmt, \ | |
| 42 | + (q)->name, __func__, ## arg); \ | |
| 42 | 43 | } while (0) |
| 43 | 44 | |
| 44 | 45 | /* Flags that are set by us */ |
| @@ -66,12 +67,14 @@ static int __verify_planes_array(struct vb2_buffer *vb, const struct v4l2_buffer · 21 unmodified lines | ||
| 66 | 67 | |
| 67 | 68 | /* Is memory for copying plane information present? */ |
| 68 | 69 | if (b->m.planes == NULL) { |
| 69 | − dprintk(1, "multi-planar buffer passed but planes array not provided\n"); | |
| 70 | + dprintk(vb->vb2_queue, 1, | |
| 71 | + "multi-planar buffer passed but planes array not provided\n"); | |
| 70 | 72 | return -EINVAL; |
| 71 | 73 | } |
| 72 | 74 | |
| 73 | 75 | if (b->length < vb->num_planes || b->length > VB2_MAX_PLANES) { |
| 74 | − dprintk(1, "incorrect planes array length, expected %d, got %d\n", | |
| 76 | + dprintk(vb->vb2_queue, 1, | |
| 77 | + "incorrect planes array length, expected %d, got %d\n", | |
| 75 | 78 | vb->num_planes, b->length); |
| 76 | 79 | return -EINVAL; |
| 77 | 80 | } |
| @@ -94,7 +97,7 @@ static int __verify_length(struct vb2_buffer *vb, const struct v4l2_buffer *b) · 16 unmodified lines | ||
| 94 | 97 | unsigned int bytesused; |
| 95 | 98 | unsigned int plane; |
| 96 | 99 | |
| 97 | − if (!V4L2_TYPE_IS_OUTPUT(b->type)) | |
| 100 | + if (V4L2_TYPE_IS_CAPTURE(b->type)) | |
| 98 | 101 | return 0; |
| 99 | 102 | |
| 100 | 103 | if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) { |
| @@ -114,7 +117,8 @@ static int __verify_length(struct vb2_buffer *vb, const struct v4l2_buffer *b) · 13 unmodified lines | ||
| 114 | 117 | return -EINVAL; |
| 115 | 118 | } |
| 116 | 119 | } else { |
| 117 | − length = (b->memory == VB2_MEMORY_USERPTR) | |
| 120 | + length = (b->memory == VB2_MEMORY_USERPTR || | |
| 121 | + b->memory == VB2_MEMORY_DMABUF) | |
| 118 | 122 | ? b->length : vb->planes[0].length; |
| 119 | 123 | |
| 120 | 124 | if (b->bytesused > length) |
| @@ -179,7 +183,7 @@ static int vb2_fill_vb2_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b · 58 unmodified lines | ||
| 179 | 183 | |
| 180 | 184 | ret = __verify_length(vb, b); |
| 181 | 185 | if (ret < 0) { |
| 182 | − dprintk(1, "plane parameters verification failed: %d\n", ret); | |
| 186 | + dprintk(q, 1, "plane parameters verification failed: %d\n", ret); | |
| 183 | 187 | return ret; |
| 184 | 188 | } |
| 185 | 189 | 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 · 6 unmodified lines | ||
| 192 | 196 | * that just says that it is either a top or a bottom field, |
| 193 | 197 | * but not which of the two it is. |
| 194 | 198 | */ |
| 195 | − dprintk(1, "the field is incorrectly set to ALTERNATE for an output buffer\n"); | |
| 199 | + dprintk(q, 1, "the field is incorrectly set to ALTERNATE for an output buffer\n"); | |
| 196 | 200 | return -EINVAL; |
| 197 | 201 | } |
| 198 | 202 | vbuf->sequence = 0; |
| @@ -307,7 +311,7 @@ static int vb2_fill_vb2_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b · 108 unmodified lines | ||
| 307 | 311 | |
| 308 | 312 | /* Zero flags that we handle */ |
| 309 | 313 | vbuf->flags = b->flags & ~V4L2_BUFFER_MASK_FLAGS; |
| 310 | − if (!vb->vb2_queue->copy_timestamp || !V4L2_TYPE_IS_OUTPUT(b->type)) { | |
| 314 | + if (!vb->vb2_queue->copy_timestamp || V4L2_TYPE_IS_CAPTURE(b->type)) { | |
| 311 | 315 | /* |
| 312 | 316 | * Non-COPY timestamps and non-OUTPUT queues will get |
| 313 | 317 | * 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 · 23 unmodified lines | ||
| 337 | 341 | return 0; |
| 338 | 342 | } |
| 339 | 343 | |
| 344 | +static void set_buffer_cache_hints(struct vb2_queue *q, | |
| 345 | + struct vb2_buffer *vb, | |
| 346 | + struct v4l2_buffer *b) | |
| 347 | +{ | |
| 348 | + /* | |
| 349 | + * DMA exporter should take care of cache syncs, so we can avoid | |
| 350 | + * explicit ->prepare()/->finish() syncs. For other ->memory types | |
| 351 | + * we always need ->prepare() or/and ->finish() cache sync. | |
| 352 | + */ | |
| 353 | + if (q->memory == VB2_MEMORY_DMABUF) { | |
| 354 | + vb->need_cache_sync_on_finish = 0; | |
| 355 | + vb->need_cache_sync_on_prepare = 0; | |
| 356 | + return; | |
| 357 | + } | |
| 358 | + | |
| 359 | + /* | |
| 360 | + * Cache sync/invalidation flags are set by default in order to | |
| 361 | + * preserve existing behaviour for old apps/drivers. | |
| 362 | + */ | |
| 363 | + vb->need_cache_sync_on_prepare = 1; | |
| 364 | + vb->need_cache_sync_on_finish = 1; | |
| 365 | + | |
| 366 | + if (!vb2_queue_allows_cache_hints(q)) { | |
| 367 | + /* | |
| 368 | + * Clear buffer cache flags if queue does not support user | |
| 369 | + * space hints. That's to indicate to userspace that these | |
| 370 | + * flags won't work. | |
| 371 | + */ | |
| 372 | + b->flags &= ~V4L2_BUF_FLAG_NO_CACHE_INVALIDATE; | |
| 373 | + b->flags &= ~V4L2_BUF_FLAG_NO_CACHE_CLEAN; | |
| 374 | + return; | |
| 375 | + } | |
| 376 | + | |
| 377 | + /* | |
| 378 | + * ->finish() cache sync can be avoided when queue direction is | |
| 379 | + * TO_DEVICE. | |
| 380 | + */ | |
| 381 | + if (q->dma_dir == DMA_TO_DEVICE) | |
| 382 | + vb->need_cache_sync_on_finish = 0; | |
| 383 | + | |
| 384 | + if (b->flags & V4L2_BUF_FLAG_NO_CACHE_INVALIDATE) | |
| 385 | + vb->need_cache_sync_on_finish = 0; | |
| 386 | + | |
| 387 | + if (b->flags & V4L2_BUF_FLAG_NO_CACHE_CLEAN) | |
| 388 | + vb->need_cache_sync_on_prepare = 0; | |
| 389 | +} | |
| 390 | + | |
| 340 | 391 | static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *mdev, |
| 341 | 392 | struct v4l2_buffer *b, bool is_prepare, |
| 342 | 393 | struct media_request **p_req) |
| @@ -348,23 +399,23 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md · 5 unmodified lines | ||
| 348 | 399 | int ret; |
| 349 | 400 | |
| 350 | 401 | if (b->type != q->type) { |
| 351 | − dprintk(1, "%s: invalid buffer type\n", opname); | |
| 402 | + dprintk(q, 1, "%s: invalid buffer type\n", opname); | |
| 352 | 403 | return -EINVAL; |
| 353 | 404 | } |
| 354 | 405 | |
| 355 | 406 | if (b->index >= q->num_buffers) { |
| 356 | − dprintk(1, "%s: buffer index out of range\n", opname); | |
| 407 | + dprintk(q, 1, "%s: buffer index out of range\n", opname); | |
| 357 | 408 | return -EINVAL; |
| 358 | 409 | } |
| 359 | 410 | |
| 360 | 411 | if (q->bufs[b->index] == NULL) { |
| 361 | 412 | /* Should never happen */ |
| 362 | − dprintk(1, "%s: buffer is NULL\n", opname); | |
| 413 | + dprintk(q, 1, "%s: buffer is NULL\n", opname); | |
| 363 | 414 | return -EINVAL; |
| 364 | 415 | } |
| 365 | 416 | |
| 366 | 417 | if (b->memory != q->memory) { |
| 367 | − dprintk(1, "%s: invalid memory type\n", opname); | |
| 418 | + dprintk(q, 1, "%s: invalid memory type\n", opname); | |
| 368 | 419 | return -EINVAL; |
| 369 | 420 | } |
| 370 | 421 | |
| @@ -376,11 +427,12 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md · 5 unmodified lines | ||
| 376 | 427 | |
| 377 | 428 | if (!is_prepare && (b->flags & V4L2_BUF_FLAG_REQUEST_FD) && |
| 378 | 429 | vb->state != VB2_BUF_STATE_DEQUEUED) { |
| 379 | − dprintk(1, "%s: buffer is not in dequeued state\n", opname); | |
| 430 | + dprintk(q, 1, "%s: buffer is not in dequeued state\n", opname); | |
| 380 | 431 | return -EINVAL; |
| 381 | 432 | } |
| 382 | 433 | |
| 383 | 434 | if (!vb->prepared) { |
| 435 | + set_buffer_cache_hints(q, vb, b); | |
| 384 | 436 | /* Copy relevant information provided by the userspace */ |
| 385 | 437 | memset(vbuf->planes, 0, |
| 386 | 438 | 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 · 7 unmodified lines | ||
| 394 | 446 | |
| 395 | 447 | if (!(b->flags & V4L2_BUF_FLAG_REQUEST_FD)) { |
| 396 | 448 | if (q->requires_requests) { |
| 397 | − dprintk(1, "%s: queue requires requests\n", opname); | |
| 449 | + dprintk(q, 1, "%s: queue requires requests\n", opname); | |
| 398 | 450 | return -EBADR; |
| 399 | 451 | } |
| 400 | 452 | if (q->uses_requests) { |
| 401 | − dprintk(1, "%s: queue uses requests\n", opname); | |
| 453 | + dprintk(q, 1, "%s: queue uses requests\n", opname); | |
| 402 | 454 | return -EBUSY; |
| 403 | 455 | } |
| 404 | 456 | return 0; |
| 405 | 457 | } else if (!q->supports_requests) { |
| 406 | − dprintk(1, "%s: queue does not support requests\n", opname); | |
| 458 | + dprintk(q, 1, "%s: queue does not support requests\n", opname); | |
| 407 | 459 | return -EBADR; |
| 408 | 460 | } else if (q->uses_qbuf) { |
| 409 | − dprintk(1, "%s: queue does not use requests\n", opname); | |
| 461 | + dprintk(q, 1, "%s: queue does not use requests\n", opname); | |
| 410 | 462 | return -EBUSY; |
| 411 | 463 | } |
| 412 | 464 | |
| @@ -436,13 +488,13 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md · 23 unmodified lines | ||
| 436 | 488 | return -EINVAL; |
| 437 | 489 | |
| 438 | 490 | if (b->request_fd < 0) { |
| 439 | − dprintk(1, "%s: request_fd < 0\n", opname); | |
| 491 | + dprintk(q, 1, "%s: request_fd < 0\n", opname); | |
| 440 | 492 | return -EINVAL; |
| 441 | 493 | } |
| 442 | 494 | |
| 443 | 495 | req = media_request_get_by_fd(mdev, b->request_fd); |
| 444 | 496 | if (IS_ERR(req)) { |
| 445 | − dprintk(1, "%s: invalid request_fd\n", opname); | |
| 497 | + dprintk(q, 1, "%s: invalid request_fd\n", opname); | |
| 446 | 498 | return PTR_ERR(req); |
| 447 | 499 | } |
| 448 | 500 | |
| @@ -452,7 +504,7 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md · 3 unmodified lines | ||
| 452 | 504 | */ |
| 453 | 505 | if (req->state != MEDIA_REQUEST_STATE_IDLE && |
| 454 | 506 | req->state != MEDIA_REQUEST_STATE_UPDATING) { |
| 455 | − dprintk(1, "%s: request is not idle\n", opname); | |
| 507 | + dprintk(q, 1, "%s: request is not idle\n", opname); | |
| 456 | 508 | media_request_put(req); |
| 457 | 509 | return -EBUSY; |
| 458 | 510 | } |
| @@ -635,12 +687,12 @@ int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b) · 176 unmodified lines | ||
| 635 | 687 | int ret; |
| 636 | 688 | |
| 637 | 689 | if (b->type != q->type) { |
| 638 | − dprintk(1, "wrong buffer type\n"); | |
| 690 | + dprintk(q, 1, "wrong buffer type\n"); | |
| 639 | 691 | return -EINVAL; |
| 640 | 692 | } |
| 641 | 693 | |
| 642 | 694 | if (b->index >= q->num_buffers) { |
| 643 | − dprintk(1, "buffer index out of range\n"); | |
| 695 | + dprintk(q, 1, "buffer index out of range\n"); | |
| 644 | 696 | return -EINVAL; |
| 645 | 697 | } |
| 646 | 698 | vb = q->bufs[b->index]; |
| @@ -662,18 +714,30 @@ static void fill_buf_caps(struct vb2_queue *q, u32 *caps) · 15 unmodified lines | ||
| 662 | 714 | *caps |= V4L2_BUF_CAP_SUPPORTS_DMABUF; |
| 663 | 715 | if (q->subsystem_flags & VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF) |
| 664 | 716 | *caps |= V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF; |
| 717 | + if (q->allow_cache_hints && q->io_modes & VB2_MMAP) | |
| 718 | + *caps |= V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS; | |
| 665 | 719 | #ifdef CONFIG_MEDIA_CONTROLLER_REQUEST_API |
| 666 | 720 | if (q->supports_requests) |
| 667 | 721 | *caps |= V4L2_BUF_CAP_SUPPORTS_REQUESTS; |
| 668 | 722 | #endif |
| 669 | 723 | } |
| 670 | 724 | |
| 725 | +static void clear_consistency_attr(struct vb2_queue *q, | |
| 726 | + int memory, | |
| 727 | + unsigned int *flags) | |
| 728 | +{ | |
| 729 | + if (!q->allow_cache_hints || memory != V4L2_MEMORY_MMAP) | |
| 730 | + *flags &= ~V4L2_FLAG_MEMORY_NON_CONSISTENT; | |
| 731 | +} | |
| 732 | + | |
| 671 | 733 | int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) |
| 672 | 734 | { |
| 673 | 735 | int ret = vb2_verify_memory_type(q, req->memory, req->type); |
| 674 | 736 | |
| 675 | 737 | fill_buf_caps(q, &req->capabilities); |
| 676 | − return ret ? ret : vb2_core_reqbufs(q, req->memory, &req->count); | |
| 738 | + clear_consistency_attr(q, req->memory, &req->flags); | |
| 739 | + return ret ? ret : vb2_core_reqbufs(q, req->memory, | |
| 740 | + req->flags, &req->count); | |
| 677 | 741 | } |
| 678 | 742 | EXPORT_SYMBOL_GPL(vb2_reqbufs); |
| 679 | 743 | |
| @@ -683,7 +747,7 @@ int vb2_prepare_buf(struct vb2_queue *q, struct media_device *mdev, · 3 unmodified lines | ||
| 683 | 747 | int ret; |
| 684 | 748 | |
| 685 | 749 | if (vb2_fileio_is_active(q)) { |
| 686 | − dprintk(1, "file io in progress\n"); | |
| 750 | + dprintk(q, 1, "file io in progress\n"); | |
| 687 | 751 | return -EBUSY; |
| 688 | 752 | } |
| 689 | 753 | |
| @@ -705,6 +769,7 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create) · 15 unmodified lines | ||
| 705 | 769 | unsigned i; |
| 706 | 770 | |
| 707 | 771 | fill_buf_caps(q, &create->capabilities); |
| 772 | + clear_consistency_attr(q, create->memory, &create->flags); | |
| 708 | 773 | create->index = q->num_buffers; |
| 709 | 774 | if (create->count == 0) |
| 710 | 775 | return ret != -EBUSY ? ret : 0; |
| @@ -748,7 +813,10 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create) · 37 unmodified lines | ||
| 748 | 813 | if (requested_sizes[i] == 0) |
| 749 | 814 | return -EINVAL; |
| 750 | 815 | return ret ? ret : vb2_core_create_bufs(q, create->memory, |
| 751 | − &create->count, requested_planes, requested_sizes); | |
| 816 | + create->flags, | |
| 817 | + &create->count, | |
| 818 | + requested_planes, | |
| 819 | + requested_sizes); | |
| 752 | 820 | } |
| 753 | 821 | EXPORT_SYMBOL_GPL(vb2_create_bufs); |
| 754 | 822 | |
| @@ -759,7 +827,7 @@ int vb2_qbuf(struct vb2_queue *q, struct media_device *mdev, · 4 unmodified lines | ||
| 759 | 827 | int ret; |
| 760 | 828 | |
| 761 | 829 | if (vb2_fileio_is_active(q)) { |
| 762 | − dprintk(1, "file io in progress\n"); | |
| 830 | + dprintk(q, 1, "file io in progress\n"); | |
| 763 | 831 | return -EBUSY; |
| 764 | 832 | } |
| 765 | 833 | |
| @@ -778,12 +846,12 @@ int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking) · 12 unmodified lines | ||
| 778 | 846 | int ret; |
| 779 | 847 | |
| 780 | 848 | if (vb2_fileio_is_active(q)) { |
| 781 | − dprintk(1, "file io in progress\n"); | |
| 849 | + dprintk(q, 1, "file io in progress\n"); | |
| 782 | 850 | return -EBUSY; |
| 783 | 851 | } |
| 784 | 852 | |
| 785 | 853 | if (b->type != q->type) { |
| 786 | − dprintk(1, "invalid buffer type\n"); | |
| 854 | + dprintk(q, 1, "invalid buffer type\n"); | |
| 787 | 855 | return -EINVAL; |
| 788 | 856 | } |
| 789 | 857 | |
| @@ -807,7 +875,7 @@ EXPORT_SYMBOL_GPL(vb2_dqbuf); · 17 unmodified lines | ||
| 807 | 875 | int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type) |
| 808 | 876 | { |
| 809 | 877 | if (vb2_fileio_is_active(q)) { |
| 810 | − dprintk(1, "file io in progress\n"); | |
| 878 | + dprintk(q, 1, "file io in progress\n"); | |
| 811 | 879 | return -EBUSY; |
| 812 | 880 | } |
| 813 | 881 | return vb2_core_streamon(q, type); |
| @@ -817,7 +885,7 @@ EXPORT_SYMBOL_GPL(vb2_streamon); · 3 unmodified lines | ||
| 817 | 885 | int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type) |
| 818 | 886 | { |
| 819 | 887 | if (vb2_fileio_is_active(q)) { |
| 820 | − dprintk(1, "file io in progress\n"); | |
| 888 | + dprintk(q, 1, "file io in progress\n"); | |
| 821 | 889 | return -EBUSY; |
| 822 | 890 | } |
| 823 | 891 | return vb2_core_streamoff(q, type); |
| @@ -831,7 +899,7 @@ int vb2_expbuf(struct vb2_queue *q, struct v4l2_exportbuffer *eb) · 7 unmodified lines | ||
| 831 | 899 | } |
| 832 | 900 | EXPORT_SYMBOL_GPL(vb2_expbuf); |
| 833 | 901 | |
| 834 | −int vb2_queue_init(struct vb2_queue *q) | |
| 902 | +int vb2_queue_init_name(struct vb2_queue *q, const char *name) | |
| 835 | 903 | { |
| 836 | 904 | /* |
| 837 | 905 | * Sanity check |
| @@ -867,8 +935,19 @@ int vb2_queue_init(struct vb2_queue *q) · 29 unmodified lines | ||
| 867 | 935 | */ |
| 868 | 936 | q->quirk_poll_must_check_waiting_for_buffers = true; |
| 869 | 937 | |
| 938 | + if (name) | |
| 939 | + strscpy(q->name, name, sizeof(q->name)); | |
| 940 | + else | |
| 941 | + q->name[0] = '\0'; | |
| 942 | + | |
| 870 | 943 | return vb2_core_queue_init(q); |
| 871 | 944 | } |
| 945 | +EXPORT_SYMBOL_GPL(vb2_queue_init_name); | |
| 946 | + | |
| 947 | +int vb2_queue_init(struct vb2_queue *q) | |
| 948 | +{ | |
| 949 | + return vb2_queue_init_name(q, NULL); | |
| 950 | +} | |
| 872 | 951 | EXPORT_SYMBOL_GPL(vb2_queue_init); |
| 873 | 952 | |
| 874 | 953 | void vb2_queue_release(struct vb2_queue *q) |
| @@ -919,11 +998,12 @@ int vb2_ioctl_reqbufs(struct file *file, void *priv, · 44 unmodified lines | ||
| 919 | 998 | int res = vb2_verify_memory_type(vdev->queue, p->memory, p->type); |
| 920 | 999 | |
| 921 | 1000 | fill_buf_caps(vdev->queue, &p->capabilities); |
| 1001 | + clear_consistency_attr(vdev->queue, p->memory, &p->flags); | |
| 922 | 1002 | if (res) |
| 923 | 1003 | return res; |
| 924 | 1004 | if (vb2_queue_is_busy(vdev, file)) |
| 925 | 1005 | return -EBUSY; |
| 926 | − res = vb2_core_reqbufs(vdev->queue, p->memory, &p->count); | |
| 1006 | + res = vb2_core_reqbufs(vdev->queue, p->memory, p->flags, &p->count); | |
| 927 | 1007 | /* If count == 0, then the owner has released all buffers and he |
| 928 | 1008 | is no longer owner of the queue. Otherwise we have a new owner. */ |
| 929 | 1009 | if (res == 0) |
| @@ -941,6 +1021,7 @@ int vb2_ioctl_create_bufs(struct file *file, void *priv, · 11 unmodified lines | ||
| 941 | 1021 | |
| 942 | 1022 | p->index = vdev->queue->num_buffers; |
| 943 | 1023 | fill_buf_caps(vdev->queue, &p->capabilities); |
| 1024 | + clear_consistency_attr(vdev->queue, p->memory, &p->flags); | |
| 944 | 1025 | /* |
| 945 | 1026 | * If count == 0, then just check if memory and type are valid. |
| 946 | 1027 | * Any -EBUSY result from vb2_verify_memory_type can be mapped to 0. |
| @@ -342,7 +342,7 @@ int dvb_vb2_reqbufs(struct dvb_vb2_ctx *ctx, struct dmx_requestbuffers *req) · 341 unmodified lines | ||
| 342 | 342 | |
| 343 | 343 | ctx->buf_siz = req->size; |
| 344 | 344 | ctx->buf_cnt = req->count; |
| 345 | − ret = vb2_core_reqbufs(&ctx->vb_q, VB2_MEMORY_MMAP, &req->count); | |
| 345 | + ret = vb2_core_reqbufs(&ctx->vb_q, VB2_MEMORY_MMAP, 0, &req->count); | |
| 346 | 346 | if (ret) { |
| 347 | 347 | ctx->state = DVB_VB2_STATE_NONE; |
| 348 | 348 | dprintk(1, "[%s] count=%d size=%d errno=%d\n", ctx->name, |
| @@ -155,17 +155,6 @@ if (debug >= level) \ · 154 unmodified lines | ||
| 155 | 155 | printk(KERN_DEBUG KBUILD_MODNAME ": %s " fmt, __func__, ##arg); \ |
| 156 | 156 | } while (0) |
| 157 | 157 | |
| 158 | − | |
| 159 | −static inline u32 MulDiv32(u32 a, u32 b, u32 c) | |
| 160 | −{ | |
| 161 | − u64 tmp64; | |
| 162 | − | |
| 163 | − tmp64 = (u64) a * (u64) b; | |
| 164 | − do_div(tmp64, c); | |
| 165 | − | |
| 166 | − return (u32) tmp64; | |
| 167 | −} | |
| 168 | − | |
| 169 | 158 | static inline u32 Frac28a(u32 a, u32 c) |
| 170 | 159 | { |
| 171 | 160 | int i = 0; |
| @@ -5,7 +5,7 @@ · 4 unmodified lines | ||
| 5 | 5 | * Copyright (C) 2013 Antti Palosaari <crope@iki.fi> |
| 6 | 6 | * |
| 7 | 7 | * GNU Radio plugin "gr-kernel" for device usage will be on: |
| 8 | − * http://git.linuxtv.org/anttip/gr-kernel.git | |
| 8 | + * https://git.linuxtv.org/anttip/gr-kernel.git | |
| 9 | 9 | */ |
| 10 | 10 | |
| 11 | 11 | #include "rtl2832_sdr.h" |
| @@ -272,6 +272,8 @@ static int node_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) · 271 unmodified lines | ||
| 272 | 272 | |
| 273 | 273 | name_len = fw_csr_string(unit->directory, CSR_MODEL, |
| 274 | 274 | name, sizeof(name)); |
| 275 | + if (name_len < 0) | |
| 276 | + return name_len; | |
| 275 | 277 | for (i = ARRAY_SIZE(model_names); --i; ) |
| 276 | 278 | if (strlen(model_names[i]) <= name_len && |
| 277 | 279 | strncmp(name, model_names[i], name_len) == 0) |
| @@ -464,6 +464,19 @@ config VIDEO_VPX3220 · 463 unmodified lines | ||
| 464 | 464 | To compile this driver as a module, choose M here: the |
| 465 | 465 | module will be called vpx3220. |
| 466 | 466 | |
| 467 | +config VIDEO_MAX9286 | |
| 468 | + tristate "Maxim MAX9286 GMSL deserializer support" | |
| 469 | + depends on I2C && I2C_MUX | |
| 470 | + depends on OF | |
| 471 | + select V4L2_FWNODE | |
| 472 | + select VIDEO_V4L2_SUBDEV_API | |
| 473 | + select MEDIA_CONTROLLER | |
| 474 | + help | |
| 475 | + This driver supports the Maxim MAX9286 GMSL deserializer. | |
| 476 | + | |
| 477 | + To compile this driver as a module, choose M here: the | |
| 478 | + module will be called max9286. | |
| 479 | + | |
| 467 | 480 | comment "Video and audio decoders" |
| 468 | 481 | |
| 469 | 482 | config VIDEO_SAA717X |
| @@ -860,6 +873,7 @@ config VIDEO_OV2685 · 390 unmodified lines | ||
| 860 | 873 | config VIDEO_OV2740 |
| 861 | 874 | tristate "OmniVision OV2740 sensor support" |
| 862 | 875 | depends on VIDEO_V4L2 && I2C |
| 876 | + depends on ACPI || COMPILE_TEST | |
| 863 | 877 | select MEDIA_CONTROLLER |
| 864 | 878 | select VIDEO_V4L2_SUBDEV_API |
| 865 | 879 | select V4L2_FWNODE |
| @@ -1157,6 +1171,19 @@ config VIDEO_NOON010PC30 · 291 unmodified lines | ||
| 1157 | 1171 | |
| 1158 | 1172 | source "drivers/media/i2c/m5mols/Kconfig" |
| 1159 | 1173 | |
| 1174 | +config VIDEO_RDACM20 | |
| 1175 | + tristate "IMI RDACM20 camera support" | |
| 1176 | + depends on I2C | |
| 1177 | + select V4L2_FWNODE | |
| 1178 | + select VIDEO_V4L2_SUBDEV_API | |
| 1179 | + select MEDIA_CONTROLLER | |
| 1180 | + help | |
| 1181 | + This driver supports the IMI RDACM20 GMSL camera, used in | |
| 1182 | + ADAS systems. | |
| 1183 | + | |
| 1184 | + This camera should be used in conjunction with a GMSL | |
| 1185 | + deserialiser such as the MAX9286. | |
| 1186 | + | |
| 1160 | 1187 | config VIDEO_RJ54N1 |
| 1161 | 1188 | tristate "Sharp RJ54N1CB0C sensor support" |
| 1162 | 1189 | depends on I2C && VIDEO_V4L2 |
| @@ -1253,6 +1280,18 @@ config VIDEO_DW9714 · 90 unmodified lines | ||
| 1253 | 1280 | capability. This is designed for linear control of |
| 1254 | 1281 | voice coil motors, controlled via I2C serial interface. |
| 1255 | 1282 | |
| 1283 | +config VIDEO_DW9768 | |
| 1284 | + tristate "DW9768 lens voice coil support" | |
| 1285 | + depends on I2C && VIDEO_V4L2 | |
| 1286 | + select MEDIA_CONTROLLER | |
| 1287 | + select VIDEO_V4L2_SUBDEV_API | |
| 1288 | + select V4L2_FWNODE | |
| 1289 | + help | |
| 1290 | + This is a driver for the DW9768 camera lens voice coil. | |
| 1291 | + DW9768 is a 10 bit DAC with 100mA output current sink | |
| 1292 | + capability. This is designed for linear control of | |
| 1293 | + voice coil motors, controlled via I2C serial interface. | |
| 1294 | + | |
| 1256 | 1295 | config VIDEO_DW9807_VCM |
| 1257 | 1296 | tristate "DW9807 lens voice coil support" |
| 1258 | 1297 | depends on I2C && VIDEO_V4L2 |
| @@ -24,6 +24,7 @@ obj-$(CONFIG_VIDEO_SAA6752HS) += saa6752hs.o · 23 unmodified lines | ||
| 24 | 24 | obj-$(CONFIG_VIDEO_AD5820) += ad5820.o |
| 25 | 25 | obj-$(CONFIG_VIDEO_AK7375) += ak7375.o |
| 26 | 26 | obj-$(CONFIG_VIDEO_DW9714) += dw9714.o |
| 27 | +obj-$(CONFIG_VIDEO_DW9768) += dw9768.o | |
| 27 | 28 | obj-$(CONFIG_VIDEO_DW9807_VCM) += dw9807-vcm.o |
| 28 | 29 | obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o |
| 29 | 30 | obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o |
| @@ -118,6 +119,9 @@ obj-$(CONFIG_VIDEO_IMX274) += imx274.o · 88 unmodified lines | ||
| 118 | 119 | obj-$(CONFIG_VIDEO_IMX290) += imx290.o |
| 119 | 120 | obj-$(CONFIG_VIDEO_IMX319) += imx319.o |
| 120 | 121 | obj-$(CONFIG_VIDEO_IMX355) += imx355.o |
| 122 | +obj-$(CONFIG_VIDEO_MAX9286) += max9286.o | |
| 123 | +rdacm20-camera_module-objs := rdacm20.o max9271.o | |
| 124 | +obj-$(CONFIG_VIDEO_RDACM20) += rdacm20-camera_module.o | |
| 121 | 125 | obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o |
| 122 | 126 | |
| 123 | 127 | obj-$(CONFIG_SDR_MAX2175) += max2175.o |
| @@ -0,0 +1,554 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +// Copyright (c) 2020 MediaTek Inc. | |
| 3 | + | |
| 4 | +#include <linux/delay.h> | |
| 5 | +#include <linux/i2c.h> | |
| 6 | +#include <linux/module.h> | |
| 7 | +#include <linux/pm_runtime.h> | |
| 8 | +#include <linux/regulator/consumer.h> | |
| 9 | +#include <media/v4l2-async.h> | |
| 10 | +#include <media/v4l2-ctrls.h> | |
| 11 | +#include <media/v4l2-device.h> | |
| 12 | +#include <media/v4l2-fwnode.h> | |
| 13 | +#include <media/v4l2-subdev.h> | |
| 14 | + | |
| 15 | +#define DW9768_NAME "dw9768" | |
| 16 | +#define DW9768_MAX_FOCUS_POS (1024 - 1) | |
| 17 | +/* | |
| 18 | + * This sets the minimum granularity for the focus positions. | |
| 19 | + * A value of 1 gives maximum accuracy for a desired focus position | |
| 20 | + */ | |
| 21 | +#define DW9768_FOCUS_STEPS 1 | |
| 22 | + | |
| 23 | +/* | |
| 24 | + * Ring control and Power control register | |
| 25 | + * Bit[1] RING_EN | |
| 26 | + * 0: Direct mode | |
| 27 | + * 1: AAC mode (ringing control mode) | |
| 28 | + * Bit[0] PD | |
| 29 | + * 0: Normal operation mode | |
| 30 | + * 1: Power down mode | |
| 31 | + * DW9768 requires waiting time of Topr after PD reset takes place. | |
| 32 | + */ | |
| 33 | +#define DW9768_RING_PD_CONTROL_REG 0x02 | |
| 34 | +#define DW9768_PD_MODE_OFF 0x00 | |
| 35 | +#define DW9768_PD_MODE_EN BIT(0) | |
| 36 | +#define DW9768_AAC_MODE_EN BIT(1) | |
| 37 | + | |
| 38 | +/* | |
| 39 | + * DW9768 separates two registers to control the VCM position. | |
| 40 | + * One for MSB value, another is LSB value. | |
| 41 | + * DAC_MSB: D[9:8] (ADD: 0x03) | |
| 42 | + * DAC_LSB: D[7:0] (ADD: 0x04) | |
| 43 | + * D[9:0] DAC data input: positive output current = D[9:0] / 1023 * 100[mA] | |
| 44 | + */ | |
| 45 | +#define DW9768_MSB_ADDR 0x03 | |
| 46 | +#define DW9768_LSB_ADDR 0x04 | |
| 47 | +#define DW9768_STATUS_ADDR 0x05 | |
| 48 | + | |
| 49 | +/* | |
| 50 | + * AAC mode control & prescale register | |
| 51 | + * Bit[7:5] Namely AC[2:0], decide the VCM mode and operation time. | |
| 52 | + * 001 AAC2 0.48 x Tvib | |
| 53 | + * 010 AAC3 0.70 x Tvib | |
| 54 | + * 011 AAC4 0.75 x Tvib | |
| 55 | + * 101 AAC8 1.13 x Tvib | |
| 56 | + * Bit[2:0] Namely PRESC[2:0], set the internal clock dividing rate as follow. | |
| 57 | + * 000 2 | |
| 58 | + * 001 1 | |
| 59 | + * 010 1/2 | |
| 60 | + * 011 1/4 | |
| 61 | + * 100 8 | |
| 62 | + * 101 4 | |
| 63 | + */ | |
| 64 | +#define DW9768_AAC_PRESC_REG 0x06 | |
| 65 | +#define DW9768_AAC_MODE_SEL_MASK GENMASK(7, 5) | |
| 66 | +#define DW9768_CLOCK_PRE_SCALE_SEL_MASK GENMASK(2, 0) | |
| 67 | + | |
| 68 | +/* | |
| 69 | + * VCM period of vibration register | |
| 70 | + * Bit[5:0] Defined as VCM rising periodic time (Tvib) together with PRESC[2:0] | |
| 71 | + * Tvib = (6.3ms + AACT[5:0] * 0.1ms) * Dividing Rate | |
| 72 | + * Dividing Rate is the internal clock dividing rate that is defined at | |
| 73 | + * PRESCALE register (ADD: 0x06) | |
| 74 | + */ | |
| 75 | +#define DW9768_AAC_TIME_REG 0x07 | |
| 76 | + | |
| 77 | +/* | |
| 78 | + * DW9768 requires waiting time (delay time) of t_OPR after power-up, | |
| 79 | + * or in the case of PD reset taking place. | |
| 80 | + */ | |
| 81 | +#define DW9768_T_OPR_US 1000 | |
| 82 | +#define DW9768_TVIB_MS_BASE10 (64 - 1) | |
| 83 | +#define DW9768_AAC_MODE_DEFAULT 2 | |
| 84 | +#define DW9768_AAC_TIME_DEFAULT 0x20 | |
| 85 | +#define DW9768_CLOCK_PRE_SCALE_DEFAULT 1 | |
| 86 | + | |
| 87 | +/* | |
| 88 | + * This acts as the minimum granularity of lens movement. | |
| 89 | + * Keep this value power of 2, so the control steps can be | |
| 90 | + * uniformly adjusted for gradual lens movement, with desired | |
| 91 | + * number of control steps. | |
| 92 | + */ | |
| 93 | +#define DW9768_MOVE_STEPS 16 | |
| 94 | + | |
| 95 | +static const char * const dw9768_supply_names[] = { | |
| 96 | + "vin", /* Digital I/O power */ | |
| 97 | + "vdd", /* Digital core power */ | |
| 98 | +}; | |
| 99 | + | |
| 100 | +/* dw9768 device structure */ | |
| 101 | +struct dw9768 { | |
| 102 | + struct regulator_bulk_data supplies[ARRAY_SIZE(dw9768_supply_names)]; | |
| 103 | + struct v4l2_ctrl_handler ctrls; | |
| 104 | + struct v4l2_ctrl *focus; | |
| 105 | + struct v4l2_subdev sd; | |
| 106 | + | |
| 107 | + u32 aac_mode; | |
| 108 | + u32 aac_timing; | |
| 109 | + u32 clock_presc; | |
| 110 | + u32 move_delay_us; | |
| 111 | +}; | |
| 112 | + | |
| 113 | +static inline struct dw9768 *sd_to_dw9768(struct v4l2_subdev *subdev) | |
| 114 | +{ | |
| 115 | + return container_of(subdev, struct dw9768, sd); | |
| 116 | +} | |
| 117 | + | |
| 118 | +struct regval_list { | |
| 119 | + u8 reg_num; | |
| 120 | + u8 value; | |
| 121 | +}; | |
| 122 | + | |
| 123 | +struct dw9768_aac_mode_ot_multi { | |
| 124 | + u32 aac_mode_enum; | |
| 125 | + u32 ot_multi_base100; | |
| 126 | +}; | |
| 127 | + | |
| 128 | +struct dw9768_clk_presc_dividing_rate { | |
| 129 | + u32 clk_presc_enum; | |
| 130 | + u32 dividing_rate_base100; | |
| 131 | +}; | |
| 132 | + | |
| 133 | +static const struct dw9768_aac_mode_ot_multi aac_mode_ot_multi[] = { | |
| 134 | + {1, 48}, | |
| 135 | + {2, 70}, | |
| 136 | + {3, 75}, | |
| 137 | + {5, 113}, | |
| 138 | +}; | |
| 139 | + | |
| 140 | +static const struct dw9768_clk_presc_dividing_rate presc_dividing_rate[] = { | |
| 141 | + {0, 200}, | |
| 142 | + {1, 100}, | |
| 143 | + {2, 50}, | |
| 144 | + {3, 25}, | |
| 145 | + {4, 800}, | |
| 146 | + {5, 400}, | |
| 147 | +}; | |
| 148 | + | |
| 149 | +static u32 dw9768_find_ot_multi(u32 aac_mode_param) | |
| 150 | +{ | |
| 151 | + u32 cur_ot_multi_base100 = 70; | |
| 152 | + unsigned int i; | |
| 153 | + | |
| 154 | + for (i = 0; i < ARRAY_SIZE(aac_mode_ot_multi); i++) { | |
| 155 | + if (aac_mode_ot_multi[i].aac_mode_enum == aac_mode_param) { | |
| 156 | + cur_ot_multi_base100 = | |
| 157 | + aac_mode_ot_multi[i].ot_multi_base100; | |
| 158 | + } | |
| 159 | + } | |
| 160 | + | |
| 161 | + return cur_ot_multi_base100; | |
| 162 | +} | |
| 163 | + | |
| 164 | +static u32 dw9768_find_dividing_rate(u32 presc_param) | |
| 165 | +{ | |
| 166 | + u32 cur_clk_dividing_rate_base100 = 100; | |
| 167 | + unsigned int i; | |
| 168 | + | |
| 169 | + for (i = 0; i < ARRAY_SIZE(presc_dividing_rate); i++) { | |
| 170 | + if (presc_dividing_rate[i].clk_presc_enum == presc_param) { | |
| 171 | + cur_clk_dividing_rate_base100 = | |
| 172 | + presc_dividing_rate[i].dividing_rate_base100; | |
| 173 | + } | |
| 174 | + } | |
| 175 | + | |
| 176 | + return cur_clk_dividing_rate_base100; | |
| 177 | +} | |
| 178 | + | |
| 179 | +/* | |
| 180 | + * DW9768_AAC_PRESC_REG & DW9768_AAC_TIME_REG determine VCM operation time. | |
| 181 | + * For current VCM mode: AAC3, Operation Time would be 0.70 x Tvib. | |
| 182 | + * Tvib = (6.3ms + AACT[5:0] * 0.1MS) * Dividing Rate. | |
| 183 | + * Below is calculation of the operation delay for each step. | |
| 184 | + */ | |
| 185 | +static inline u32 dw9768_cal_move_delay(u32 aac_mode_param, u32 presc_param, | |
| 186 | + u32 aac_timing_param) | |
| 187 | +{ | |
| 188 | + u32 Tvib_us; | |
| 189 | + u32 ot_multi_base100; | |
| 190 | + u32 clk_dividing_rate_base100; | |
| 191 | + | |
| 192 | + ot_multi_base100 = dw9768_find_ot_multi(aac_mode_param); | |
| 193 | + | |
| 194 | + clk_dividing_rate_base100 = dw9768_find_dividing_rate(presc_param); | |
| 195 | + | |
| 196 | + Tvib_us = (DW9768_TVIB_MS_BASE10 + aac_timing_param) * | |
| 197 | + clk_dividing_rate_base100; | |
| 198 | + | |
| 199 | + return Tvib_us * ot_multi_base100 / 100; | |
| 200 | +} | |
| 201 | + | |
| 202 | +static int dw9768_mod_reg(struct dw9768 *dw9768, u8 reg, u8 mask, u8 val) | |
| 203 | +{ | |
| 204 | + struct i2c_client *client = v4l2_get_subdevdata(&dw9768->sd); | |
| 205 | + int ret; | |
| 206 | + | |
| 207 | + ret = i2c_smbus_read_byte_data(client, reg); | |
| 208 | + if (ret < 0) | |
| 209 | + return ret; | |
| 210 | + | |
| 211 | + val = ((unsigned char)ret & ~mask) | (val & mask); | |
| 212 | + | |
| 213 | + return i2c_smbus_write_byte_data(client, reg, val); | |
| 214 | +} | |
| 215 | + | |
| 216 | +static int dw9768_set_dac(struct dw9768 *dw9768, u16 val) | |
| 217 | +{ | |
| 218 | + struct i2c_client *client = v4l2_get_subdevdata(&dw9768->sd); | |
| 219 | + | |
| 220 | + /* Write VCM position to registers */ | |
| 221 | + return i2c_smbus_write_word_swapped(client, DW9768_MSB_ADDR, val); | |
| 222 | +} | |
| 223 | + | |
| 224 | +static int dw9768_init(struct dw9768 *dw9768) | |
| 225 | +{ | |
| 226 | + struct i2c_client *client = v4l2_get_subdevdata(&dw9768->sd); | |
| 227 | + int ret, val; | |
| 228 | + | |
| 229 | + /* Reset DW9768_RING_PD_CONTROL_REG to default status 0x00 */ | |
| 230 | + ret = i2c_smbus_write_byte_data(client, DW9768_RING_PD_CONTROL_REG, | |
| 231 | + DW9768_PD_MODE_OFF); | |
| 232 | + if (ret < 0) | |
| 233 | + return ret; | |
| 234 | + | |
| 235 | + /* | |
| 236 | + * DW9769 requires waiting delay time of t_OPR | |
| 237 | + * after PD reset takes place. | |
| 238 | + */ | |
| 239 | + usleep_range(DW9768_T_OPR_US, DW9768_T_OPR_US + 100); | |
| 240 | + | |
| 241 | + /* Set DW9768_RING_PD_CONTROL_REG to DW9768_AAC_MODE_EN(0x01) */ | |
| 242 | + ret = i2c_smbus_write_byte_data(client, DW9768_RING_PD_CONTROL_REG, | |
| 243 | + DW9768_AAC_MODE_EN); | |
| 244 | + if (ret < 0) | |
| 245 | + return ret; | |
| 246 | + | |
| 247 | + /* Set AAC mode */ | |
| 248 | + ret = dw9768_mod_reg(dw9768, DW9768_AAC_PRESC_REG, | |
| 249 | + DW9768_AAC_MODE_SEL_MASK, | |
| 250 | + dw9768->aac_mode << 5); | |
| 251 | + if (ret < 0) | |
| 252 | + return ret; | |
| 253 | + | |
| 254 | + /* Set clock presc */ | |
| 255 | + if (dw9768->clock_presc != DW9768_CLOCK_PRE_SCALE_DEFAULT) { | |
| 256 | + ret = dw9768_mod_reg(dw9768, DW9768_AAC_PRESC_REG, | |
| 257 | + DW9768_CLOCK_PRE_SCALE_SEL_MASK, | |
| 258 | + dw9768->clock_presc); | |
| 259 | + if (ret < 0) | |
| 260 | + return ret; | |
| 261 | + } | |
| 262 | + | |
| 263 | + /* Set AAC Timing */ | |
| 264 | + if (dw9768->aac_timing != DW9768_AAC_TIME_DEFAULT) { | |
| 265 | + ret = i2c_smbus_write_byte_data(client, DW9768_AAC_TIME_REG, | |
| 266 | + dw9768->aac_timing); | |
| 267 | + if (ret < 0) | |
| 268 | + return ret; | |
| 269 | + } | |
| 270 | + | |
| 271 | + for (val = dw9768->focus->val % DW9768_MOVE_STEPS; | |
| 272 | + val <= dw9768->focus->val; | |
| 273 | + val += DW9768_MOVE_STEPS) { | |
| 274 | + ret = dw9768_set_dac(dw9768, val); | |
| 275 | + if (ret) { | |
| 276 | + dev_err(&client->dev, "I2C failure: %d", ret); | |
| 277 | + return ret; | |
| 278 | + } | |
| 279 | + usleep_range(dw9768->move_delay_us, | |
| 280 | + dw9768->move_delay_us + 1000); | |
| 281 | + } | |
| 282 | + | |
| 283 | + return 0; | |
| 284 | +} | |
| 285 | + | |
| 286 | +static int dw9768_release(struct dw9768 *dw9768) | |
| 287 | +{ | |
| 288 | + struct i2c_client *client = v4l2_get_subdevdata(&dw9768->sd); | |
| 289 | + int ret, val; | |
| 290 | + | |
| 291 | + val = round_down(dw9768->focus->val, DW9768_MOVE_STEPS); | |
| 292 | + for ( ; val >= 0; val -= DW9768_MOVE_STEPS) { | |
| 293 | + ret = dw9768_set_dac(dw9768, val); | |
| 294 | + if (ret) { | |
| 295 | + dev_err(&client->dev, "I2C write fail: %d", ret); | |
| 296 | + return ret; | |
| 297 | + } | |
| 298 | + usleep_range(dw9768->move_delay_us, | |
| 299 | + dw9768->move_delay_us + 1000); | |
| 300 | + } | |
| 301 | + | |
| 302 | + ret = i2c_smbus_write_byte_data(client, DW9768_RING_PD_CONTROL_REG, | |
| 303 | + DW9768_PD_MODE_EN); | |
| 304 | + if (ret < 0) | |
| 305 | + return ret; | |
| 306 | + | |
| 307 | + /* | |
| 308 | + * DW9769 requires waiting delay time of t_OPR | |
| 309 | + * after PD reset takes place. | |
| 310 | + */ | |
| 311 | + usleep_range(DW9768_T_OPR_US, DW9768_T_OPR_US + 100); | |
| 312 | + | |
| 313 | + return 0; | |
| 314 | +} | |
| 315 | + | |
| 316 | +static int dw9768_runtime_suspend(struct device *dev) | |
| 317 | +{ | |
| 318 | + struct i2c_client *client = to_i2c_client(dev); | |
| 319 | + struct v4l2_subdev *sd = i2c_get_clientdata(client); | |
| 320 | + struct dw9768 *dw9768 = sd_to_dw9768(sd); | |
| 321 | + | |
| 322 | + dw9768_release(dw9768); | |
| 323 | + regulator_bulk_disable(ARRAY_SIZE(dw9768_supply_names), | |
| 324 | + dw9768->supplies); | |
| 325 | + | |
| 326 | + return 0; | |
| 327 | +} | |
| 328 | + | |
| 329 | +static int dw9768_runtime_resume(struct device *dev) | |
| 330 | +{ | |
| 331 | + struct i2c_client *client = to_i2c_client(dev); | |
| 332 | + struct v4l2_subdev *sd = i2c_get_clientdata(client); | |
| 333 | + struct dw9768 *dw9768 = sd_to_dw9768(sd); | |
| 334 | + int ret; | |
| 335 | + | |
| 336 | + ret = regulator_bulk_enable(ARRAY_SIZE(dw9768_supply_names), | |
| 337 | + dw9768->supplies); | |
| 338 | + if (ret < 0) { | |
| 339 | + dev_err(dev, "failed to enable regulators\n"); | |
| 340 | + return ret; | |
| 341 | + } | |
| 342 | + | |
| 343 | + /* | |
| 344 | + * The datasheet refers to t_OPR that needs to be waited before sending | |
| 345 | + * I2C commands after power-up. | |
| 346 | + */ | |
| 347 | + usleep_range(DW9768_T_OPR_US, DW9768_T_OPR_US + 100); | |
| 348 | + | |
| 349 | + ret = dw9768_init(dw9768); | |
| 350 | + if (ret < 0) | |
| 351 | + goto disable_regulator; | |
| 352 | + | |
| 353 | + return 0; | |
| 354 | + | |
| 355 | +disable_regulator: | |
| 356 | + regulator_bulk_disable(ARRAY_SIZE(dw9768_supply_names), | |
| 357 | + dw9768->supplies); | |
| 358 | + | |
| 359 | + return ret; | |
| 360 | +} | |
| 361 | + | |
| 362 | +static int dw9768_set_ctrl(struct v4l2_ctrl *ctrl) | |
| 363 | +{ | |
| 364 | + struct dw9768 *dw9768 = container_of(ctrl->handler, | |
| 365 | + struct dw9768, ctrls); | |
| 366 | + | |
| 367 | + if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE) | |
| 368 | + return dw9768_set_dac(dw9768, ctrl->val); | |
| 369 | + | |
| 370 | + return 0; | |
| 371 | +} | |
| 372 | + | |
| 373 | +static const struct v4l2_ctrl_ops dw9768_ctrl_ops = { | |
| 374 | + .s_ctrl = dw9768_set_ctrl, | |
| 375 | +}; | |
| 376 | + | |
| 377 | +static int dw9768_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) | |
| 378 | +{ | |
| 379 | + int ret; | |
| 380 | + | |
| 381 | + ret = pm_runtime_get_sync(sd->dev); | |
| 382 | + if (ret < 0) { | |
| 383 | + pm_runtime_put_noidle(sd->dev); | |
| 384 | + return ret; | |
| 385 | + } | |
| 386 | + | |
| 387 | + return 0; | |
| 388 | +} | |
| 389 | + | |
| 390 | +static int dw9768_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) | |
| 391 | +{ | |
| 392 | + pm_runtime_put(sd->dev); | |
| 393 | + | |
| 394 | + return 0; | |
| 395 | +} | |
| 396 | + | |
| 397 | +static const struct v4l2_subdev_internal_ops dw9768_int_ops = { | |
| 398 | + .open = dw9768_open, | |
| 399 | + .close = dw9768_close, | |
| 400 | +}; | |
| 401 | + | |
| 402 | +static const struct v4l2_subdev_ops dw9768_ops = { }; | |
| 403 | + | |
| 404 | +static int dw9768_init_controls(struct dw9768 *dw9768) | |
| 405 | +{ | |
| 406 | + struct v4l2_ctrl_handler *hdl = &dw9768->ctrls; | |
| 407 | + const struct v4l2_ctrl_ops *ops = &dw9768_ctrl_ops; | |
| 408 | + | |
| 409 | + v4l2_ctrl_handler_init(hdl, 1); | |
| 410 | + | |
| 411 | + dw9768->focus = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FOCUS_ABSOLUTE, 0, | |
| 412 | + DW9768_MAX_FOCUS_POS, | |
| 413 | + DW9768_FOCUS_STEPS, 0); | |
| 414 | + | |
| 415 | + if (hdl->error) | |
| 416 | + return hdl->error; | |
| 417 | + | |
| 418 | + dw9768->sd.ctrl_handler = hdl; | |
| 419 | + | |
| 420 | + return 0; | |
| 421 | +} | |
| 422 | + | |
| 423 | +static int dw9768_probe(struct i2c_client *client) | |
| 424 | +{ | |
| 425 | + struct device *dev = &client->dev; | |
| 426 | + struct dw9768 *dw9768; | |
| 427 | + unsigned int i; | |
| 428 | + int ret; | |
| 429 | + | |
| 430 | + dw9768 = devm_kzalloc(dev, sizeof(*dw9768), GFP_KERNEL); | |
| 431 | + if (!dw9768) | |
| 432 | + return -ENOMEM; | |
| 433 | + | |
| 434 | + /* Initialize subdev */ | |
| 435 | + v4l2_i2c_subdev_init(&dw9768->sd, client, &dw9768_ops); | |
| 436 | + | |
| 437 | + dw9768->aac_mode = DW9768_AAC_MODE_DEFAULT; | |
| 438 | + dw9768->aac_timing = DW9768_AAC_TIME_DEFAULT; | |
| 439 | + dw9768->clock_presc = DW9768_CLOCK_PRE_SCALE_DEFAULT; | |
| 440 | + | |
| 441 | + /* Optional indication of AAC mode select */ | |
| 442 | + fwnode_property_read_u32(dev_fwnode(dev), "dongwoon,aac-mode", | |
| 443 | + &dw9768->aac_mode); | |
| 444 | + | |
| 445 | + /* Optional indication of clock pre-scale select */ | |
| 446 | + fwnode_property_read_u32(dev_fwnode(dev), "dongwoon,clock-presc", | |
| 447 | + &dw9768->clock_presc); | |
| 448 | + | |
| 449 | + /* Optional indication of AAC Timing */ | |
| 450 | + fwnode_property_read_u32(dev_fwnode(dev), "dongwoon,aac-timing", | |
| 451 | + &dw9768->aac_timing); | |
| 452 | + | |
| 453 | + dw9768->move_delay_us = dw9768_cal_move_delay(dw9768->aac_mode, | |
| 454 | + dw9768->clock_presc, | |
| 455 | + dw9768->aac_timing); | |
| 456 | + | |
| 457 | + for (i = 0; i < ARRAY_SIZE(dw9768_supply_names); i++) | |
| 458 | + dw9768->supplies[i].supply = dw9768_supply_names[i]; | |
| 459 | + | |
| 460 | + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(dw9768_supply_names), | |
| 461 | + dw9768->supplies); | |
| 462 | + if (ret) { | |
| 463 | + dev_err(dev, "failed to get regulators\n"); | |
| 464 | + return ret; | |
| 465 | + } | |
| 466 | + | |
| 467 | + /* Initialize controls */ | |
| 468 | + ret = dw9768_init_controls(dw9768); | |
| 469 | + if (ret) | |
| 470 | + goto err_free_handler; | |
| 471 | + | |
| 472 | + /* Initialize subdev */ | |
| 473 | + dw9768->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | |
| 474 | + dw9768->sd.internal_ops = &dw9768_int_ops; | |
| 475 | + | |
| 476 | + ret = media_entity_pads_init(&dw9768->sd.entity, 0, NULL); | |
| 477 | + if (ret < 0) | |
| 478 | + goto err_free_handler; | |
| 479 | + | |
| 480 | + dw9768->sd.entity.function = MEDIA_ENT_F_LENS; | |
| 481 | + | |
| 482 | + pm_runtime_enable(dev); | |
| 483 | + if (!pm_runtime_enabled(dev)) { | |
| 484 | + ret = dw9768_runtime_resume(dev); | |
| 485 | + if (ret < 0) { | |
| 486 | + dev_err(dev, "failed to power on: %d\n", ret); | |
| 487 | + goto err_clean_entity; | |
| 488 | + } | |
| 489 | + } | |
| 490 | + | |
| 491 | + ret = v4l2_async_register_subdev(&dw9768->sd); | |
| 492 | + if (ret < 0) { | |
| 493 | + dev_err(dev, "failed to register V4L2 subdev: %d", ret); | |
| 494 | + goto err_power_off; | |
| 495 | + } | |
| 496 | + | |
| 497 | + return 0; | |
| 498 | + | |
| 499 | +err_power_off: | |
| 500 | + if (pm_runtime_enabled(dev)) | |
| 501 | + pm_runtime_disable(dev); | |
| 502 | + else | |
| 503 | + dw9768_runtime_suspend(dev); | |
| 504 | +err_clean_entity: | |
| 505 | + media_entity_cleanup(&dw9768->sd.entity); | |
| 506 | +err_free_handler: | |
| 507 | + v4l2_ctrl_handler_free(&dw9768->ctrls); | |
| 508 | + | |
| 509 | + return ret; | |
| 510 | +} | |
| 511 | + | |
| 512 | +static int dw9768_remove(struct i2c_client *client) | |
| 513 | +{ | |
| 514 | + struct v4l2_subdev *sd = i2c_get_clientdata(client); | |
| 515 | + struct dw9768 *dw9768 = sd_to_dw9768(sd); | |
| 516 | + | |
| 517 | + v4l2_async_unregister_subdev(&dw9768->sd); | |
| 518 | + v4l2_ctrl_handler_free(&dw9768->ctrls); | |
| 519 | + media_entity_cleanup(&dw9768->sd.entity); | |
| 520 | + pm_runtime_disable(&client->dev); | |
| 521 | + if (!pm_runtime_status_suspended(&client->dev)) | |
| 522 | + dw9768_runtime_suspend(&client->dev); | |
| 523 | + pm_runtime_set_suspended(&client->dev); | |
| 524 | + | |
| 525 | + return 0; | |
| 526 | +} | |
| 527 | + | |
| 528 | +static const struct of_device_id dw9768_of_table[] = { | |
| 529 | + { .compatible = "dongwoon,dw9768" }, | |
| 530 | + { .compatible = "giantec,gt9769" }, | |
| 531 | + {} | |
| 532 | +}; | |
| 533 | +MODULE_DEVICE_TABLE(of, dw9768_of_table); | |
| 534 | + | |
| 535 | +static const struct dev_pm_ops dw9768_pm_ops = { | |
| 536 | + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, | |
| 537 | + pm_runtime_force_resume) | |
| 538 | + SET_RUNTIME_PM_OPS(dw9768_runtime_suspend, dw9768_runtime_resume, NULL) | |
| 539 | +}; | |
| 540 | + | |
| 541 | +static struct i2c_driver dw9768_i2c_driver = { | |
| 542 | + .driver = { | |
| 543 | + .name = DW9768_NAME, | |
| 544 | + .pm = &dw9768_pm_ops, | |
| 545 | + .of_match_table = dw9768_of_table, | |
| 546 | + }, | |
| 547 | + .probe_new = dw9768_probe, | |
| 548 | + .remove = dw9768_remove, | |
| 549 | +}; | |
| 550 | +module_i2c_driver(dw9768_i2c_driver); | |
| 551 | + | |
| 552 | +MODULE_AUTHOR("Dongchun Zhu <dongchun.zhu@mediatek.com>"); | |
| 553 | +MODULE_DESCRIPTION("DW9768 VCM driver"); | |
| 554 | +MODULE_LICENSE("GPL v2"); | |
| @@ -25,9 +25,19 @@ · 24 unmodified lines | ||
| 25 | 25 | #define IMX290_STANDBY 0x3000 |
| 26 | 26 | #define IMX290_REGHOLD 0x3001 |
| 27 | 27 | #define IMX290_XMSTA 0x3002 |
| 28 | +#define IMX290_FR_FDG_SEL 0x3009 | |
| 29 | +#define IMX290_BLKLEVEL_LOW 0x300a | |
| 30 | +#define IMX290_BLKLEVEL_HIGH 0x300b | |
| 28 | 31 | #define IMX290_GAIN 0x3014 |
| 32 | +#define IMX290_HMAX_LOW 0x301c | |
| 33 | +#define IMX290_HMAX_HIGH 0x301d | |
| 34 | +#define IMX290_PGCTRL 0x308c | |
| 35 | +#define IMX290_PHY_LANE_NUM 0x3407 | |
| 36 | +#define IMX290_CSI_LANE_MODE 0x3443 | |
| 29 | 37 | |
| 30 | −#define IMX290_DEFAULT_LINK_FREQ 445500000 | |
| 38 | +#define IMX290_PGCTRL_REGEN BIT(0) | |
| 39 | +#define IMX290_PGCTRL_THRU BIT(1) | |
| 40 | +#define IMX290_PGCTRL_MODE(n) ((n) << 4) | |
| 31 | 41 | |
| 32 | 42 | static const char * const imx290_supply_name[] = { |
| 33 | 43 | "vdda", |
| @@ -45,8 +55,8 @@ struct imx290_regval { · 11 unmodified lines | ||
| 45 | 55 | struct imx290_mode { |
| 46 | 56 | u32 width; |
| 47 | 57 | u32 height; |
| 48 | − u32 pixel_rate; | |
| 49 | − u32 link_freq_index; | |
| 58 | + u32 hmax; | |
| 59 | + u8 link_freq_index; | |
| 50 | 60 | |
| 51 | 61 | const struct imx290_regval *data; |
| 52 | 62 | u32 data_size; |
| @@ -56,9 +66,10 @@ struct imx290 { · 3 unmodified lines | ||
| 56 | 66 | struct device *dev; |
| 57 | 67 | struct clk *xclk; |
| 58 | 68 | struct regmap *regmap; |
| 69 | + u8 nlanes; | |
| 70 | + u8 bpp; | |
| 59 | 71 | |
| 60 | 72 | struct v4l2_subdev sd; |
| 61 | − struct v4l2_fwnode_endpoint ep; | |
| 62 | 73 | struct media_pad pad; |
| 63 | 74 | struct v4l2_mbus_framefmt current_format; |
| 64 | 75 | const struct imx290_mode *current_mode; |
| @@ -75,10 +86,12 @@ struct imx290 { · 10 unmodified lines | ||
| 75 | 86 | |
| 76 | 87 | struct imx290_pixfmt { |
| 77 | 88 | u32 code; |
| 89 | + u8 bpp; | |
| 78 | 90 | }; |
| 79 | 91 | |
| 80 | 92 | static const struct imx290_pixfmt imx290_formats[] = { |
| 81 | − { MEDIA_BUS_FMT_SRGGB10_1X10 }, | |
| 93 | + { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, | |
| 94 | + { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, | |
| 82 | 95 | }; |
| 83 | 96 | |
| 84 | 97 | static const struct regmap_config imx290_regmap_config = { |
| @@ -87,16 +100,24 @@ static const struct regmap_config imx290_regmap_config = { · 2 unmodified lines | ||
| 87 | 100 | .cache_type = REGCACHE_RBTREE, |
| 88 | 101 | }; |
| 89 | 102 | |
| 103 | +static const char * const imx290_test_pattern_menu[] = { | |
| 104 | + "Disabled", | |
| 105 | + "Sequence Pattern 1", | |
| 106 | + "Horizontal Color-bar Chart", | |
| 107 | + "Vertical Color-bar Chart", | |
| 108 | + "Sequence Pattern 2", | |
| 109 | + "Gradation Pattern 1", | |
| 110 | + "Gradation Pattern 2", | |
| 111 | + "000/555h Toggle Pattern", | |
| 112 | +}; | |
| 113 | + | |
| 90 | 114 | static const struct imx290_regval imx290_global_init_settings[] = { |
| 91 | 115 | { 0x3007, 0x00 }, |
| 92 | − { 0x3009, 0x00 }, | |
| 93 | 116 | { 0x3018, 0x65 }, |
| 94 | 117 | { 0x3019, 0x04 }, |
| 95 | 118 | { 0x301a, 0x00 }, |
| 96 | − { 0x3443, 0x03 }, | |
| 97 | 119 | { 0x3444, 0x20 }, |
| 98 | 120 | { 0x3445, 0x25 }, |
| 99 | − { 0x3407, 0x03 }, | |
| 100 | 121 | { 0x303a, 0x0c }, |
| 101 | 122 | { 0x3040, 0x00 }, |
| 102 | 123 | { 0x3041, 0x00 }, |
| @@ -169,7 +190,6 @@ static const struct imx290_regval imx290_1080p_settings[] = { · 66 unmodified lines | ||
| 169 | 190 | { 0x3164, 0x1a }, |
| 170 | 191 | { 0x3480, 0x49 }, |
| 171 | 192 | /* data rate settings */ |
| 172 | − { 0x3009, 0x01 }, | |
| 173 | 193 | { 0x3405, 0x10 }, |
| 174 | 194 | { 0x3446, 0x57 }, |
| 175 | 195 | { 0x3447, 0x00 }, |
| @@ -187,8 +207,6 @@ static const struct imx290_regval imx290_1080p_settings[] = { · 11 unmodified lines | ||
| 187 | 207 | { 0x3453, 0x00 }, |
| 188 | 208 | { 0x3454, 0x17 }, |
| 189 | 209 | { 0x3455, 0x00 }, |
| 190 | − { 0x301c, 0x98 }, | |
| 191 | − { 0x301d, 0x08 }, | |
| 192 | 210 | }; |
| 193 | 211 | |
| 194 | 212 | static const struct imx290_regval imx290_720p_settings[] = { |
| @@ -210,7 +228,6 @@ static const struct imx290_regval imx290_720p_settings[] = { · 15 unmodified lines | ||
| 210 | 228 | { 0x3164, 0x1a }, |
| 211 | 229 | { 0x3480, 0x49 }, |
| 212 | 230 | /* data rate settings */ |
| 213 | − { 0x3009, 0x01 }, | |
| 214 | 231 | { 0x3405, 0x10 }, |
| 215 | 232 | { 0x3446, 0x4f }, |
| 216 | 233 | { 0x3447, 0x00 }, |
| @@ -228,8 +245,6 @@ static const struct imx290_regval imx290_720p_settings[] = { · 11 unmodified lines | ||
| 228 | 245 | { 0x3453, 0x00 }, |
| 229 | 246 | { 0x3454, 0x17 }, |
| 230 | 247 | { 0x3455, 0x00 }, |
| 231 | − { 0x301c, 0xe4 }, | |
| 232 | − { 0x301d, 0x0c }, | |
| 233 | 248 | }; |
| 234 | 249 | |
| 235 | 250 | static const struct imx290_regval imx290_10bit_settings[] = { |
| @@ -244,31 +259,105 @@ static const struct imx290_regval imx290_10bit_settings[] = { · 8 unmodified lines | ||
| 244 | 259 | { 0x300b, 0x00}, |
| 245 | 260 | }; |
| 246 | 261 | |
| 262 | +static const struct imx290_regval imx290_12bit_settings[] = { | |
| 263 | + { 0x3005, 0x01 }, | |
| 264 | + { 0x3046, 0x01 }, | |
| 265 | + { 0x3129, 0x00 }, | |
| 266 | + { 0x317c, 0x00 }, | |
| 267 | + { 0x31ec, 0x0e }, | |
| 268 | + { 0x3441, 0x0c }, | |
| 269 | + { 0x3442, 0x0c }, | |
| 270 | + { 0x300a, 0xf0 }, | |
| 271 | + { 0x300b, 0x00 }, | |
| 272 | +}; | |
| 273 | + | |
| 247 | 274 | /* supported link frequencies */ |
| 248 | −static const s64 imx290_link_freq[] = { | |
| 249 | − IMX290_DEFAULT_LINK_FREQ, | |
| 275 | +#define FREQ_INDEX_1080P 0 | |
| 276 | +#define FREQ_INDEX_720P 1 | |
| 277 | +static const s64 imx290_link_freq_2lanes[] = { | |
| 278 | + [FREQ_INDEX_1080P] = 445500000, | |
| 279 | + [FREQ_INDEX_720P] = 297000000, | |
| 250 | 280 | }; |
| 281 | +static const s64 imx290_link_freq_4lanes[] = { | |
| 282 | + [FREQ_INDEX_1080P] = 222750000, | |
| 283 | + [FREQ_INDEX_720P] = 148500000, | |
| 284 | +}; | |
| 285 | + | |
| 286 | +/* | |
| 287 | + * In this function and in the similar ones below We rely on imx290_probe() | |
| 288 | + * to ensure that nlanes is either 2 or 4. | |
| 289 | + */ | |
| 290 | +static inline const s64 *imx290_link_freqs_ptr(const struct imx290 *imx290) | |
| 291 | +{ | |
| 292 | + if (imx290->nlanes == 2) | |
| 293 | + return imx290_link_freq_2lanes; | |
| 294 | + else | |
| 295 | + return imx290_link_freq_4lanes; | |
| 296 | +} | |
| 297 | + | |
| 298 | +static inline int imx290_link_freqs_num(const struct imx290 *imx290) | |
| 299 | +{ | |
| 300 | + if (imx290->nlanes == 2) | |
| 301 | + return ARRAY_SIZE(imx290_link_freq_2lanes); | |
| 302 | + else | |
| 303 | + return ARRAY_SIZE(imx290_link_freq_4lanes); | |
| 304 | +} | |
| 251 | 305 | |
| 252 | 306 | /* Mode configs */ |
| 253 | −static const struct imx290_mode imx290_modes[] = { | |
| 307 | +static const struct imx290_mode imx290_modes_2lanes[] = { | |
| 308 | + { | |
| 309 | + .width = 1920, | |
| 310 | + .height = 1080, | |
| 311 | + .hmax = 0x1130, | |
| 312 | + .link_freq_index = FREQ_INDEX_1080P, | |
| 313 | + .data = imx290_1080p_settings, | |
| 314 | + .data_size = ARRAY_SIZE(imx290_1080p_settings), | |
| 315 | + }, | |
| 316 | + { | |
| 317 | + .width = 1280, | |
| 318 | + .height = 720, | |
| 319 | + .hmax = 0x19c8, | |
| 320 | + .link_freq_index = FREQ_INDEX_720P, | |
| 321 | + .data = imx290_720p_settings, | |
| 322 | + .data_size = ARRAY_SIZE(imx290_720p_settings), | |
| 323 | + }, | |
| 324 | +}; | |
| 325 | + | |
| 326 | +static const struct imx290_mode imx290_modes_4lanes[] = { | |
| 254 | 327 | { |
| 255 | 328 | .width = 1920, |
| 256 | 329 | .height = 1080, |
| 330 | + .hmax = 0x0898, | |
| 331 | + .link_freq_index = FREQ_INDEX_1080P, | |
| 257 | 332 | .data = imx290_1080p_settings, |
| 258 | 333 | .data_size = ARRAY_SIZE(imx290_1080p_settings), |
| 259 | − .pixel_rate = 178200000, | |
| 260 | − .link_freq_index = 0, | |
| 261 | 334 | }, |
| 262 | 335 | { |
| 263 | 336 | .width = 1280, |
| 264 | 337 | .height = 720, |
| 338 | + .hmax = 0x0ce4, | |
| 339 | + .link_freq_index = FREQ_INDEX_720P, | |
| 265 | 340 | .data = imx290_720p_settings, |
| 266 | 341 | .data_size = ARRAY_SIZE(imx290_720p_settings), |
| 267 | − .pixel_rate = 178200000, | |
| 268 | − .link_freq_index = 0, | |
| 269 | 342 | }, |
| 270 | 343 | }; |
| 271 | 344 | |
| 345 | +static inline const struct imx290_mode *imx290_modes_ptr(const struct imx290 *imx290) | |
| 346 | +{ | |
| 347 | + if (imx290->nlanes == 2) | |
| 348 | + return imx290_modes_2lanes; | |
| 349 | + else | |
| 350 | + return imx290_modes_4lanes; | |
| 351 | +} | |
| 352 | + | |
| 353 | +static inline int imx290_modes_num(const struct imx290 *imx290) | |
| 354 | +{ | |
| 355 | + if (imx290->nlanes == 2) | |
| 356 | + return ARRAY_SIZE(imx290_modes_2lanes); | |
| 357 | + else | |
| 358 | + return ARRAY_SIZE(imx290_modes_4lanes); | |
| 359 | +} | |
| 360 | + | |
| 272 | 361 | static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd) |
| 273 | 362 | { |
| 274 | 363 | return container_of(_sd, struct imx290, sd); |
| @@ -314,11 +403,11 @@ static int imx290_set_register_array(struct imx290 *imx290, · 39 unmodified lines | ||
| 314 | 403 | ret = imx290_write_reg(imx290, settings->reg, settings->val); |
| 315 | 404 | if (ret < 0) |
| 316 | 405 | return ret; |
| 317 | − | |
| 318 | − /* Settle time is 10ms for all registers */ | |
| 319 | − msleep(10); | |
| 320 | 406 | } |
| 321 | 407 | |
| 408 | + /* Provide 10ms settle time */ | |
| 409 | + usleep_range(10000, 11000); | |
| 410 | + | |
| 322 | 411 | return 0; |
| 323 | 412 | } |
| 324 | 413 | |
| @@ -391,6 +480,27 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) · 66 unmodified lines | ||
| 391 | 480 | case V4L2_CID_GAIN: |
| 392 | 481 | ret = imx290_set_gain(imx290, ctrl->val); |
| 393 | 482 | break; |
| 483 | + case V4L2_CID_TEST_PATTERN: | |
| 484 | + if (ctrl->val) { | |
| 485 | + imx290_write_reg(imx290, IMX290_BLKLEVEL_LOW, 0x00); | |
| 486 | + imx290_write_reg(imx290, IMX290_BLKLEVEL_HIGH, 0x00); | |
| 487 | + usleep_range(10000, 11000); | |
| 488 | + imx290_write_reg(imx290, IMX290_PGCTRL, | |
| 489 | + (u8)(IMX290_PGCTRL_REGEN | | |
| 490 | + IMX290_PGCTRL_THRU | | |
| 491 | + IMX290_PGCTRL_MODE(ctrl->val))); | |
| 492 | + } else { | |
| 493 | + imx290_write_reg(imx290, IMX290_PGCTRL, 0x00); | |
| 494 | + usleep_range(10000, 11000); | |
| 495 | + if (imx290->bpp == 10) | |
| 496 | + imx290_write_reg(imx290, IMX290_BLKLEVEL_LOW, | |
| 497 | + 0x3c); | |
| 498 | + else /* 12 bits per pixel */ | |
| 499 | + imx290_write_reg(imx290, IMX290_BLKLEVEL_LOW, | |
| 500 | + 0xf0); | |
| 501 | + imx290_write_reg(imx290, IMX290_BLKLEVEL_HIGH, 0x00); | |
| 502 | + } | |
| 503 | + break; | |
| 394 | 504 | default: |
| 395 | 505 | ret = -EINVAL; |
| 396 | 506 | break; |
| @@ -417,6 +527,28 @@ static int imx290_enum_mbus_code(struct v4l2_subdev *sd, · 20 unmodified lines | ||
| 417 | 527 | return 0; |
| 418 | 528 | } |
| 419 | 529 | |
| 530 | +static int imx290_enum_frame_size(struct v4l2_subdev *sd, | |
| 531 | + struct v4l2_subdev_pad_config *cfg, | |
| 532 | + struct v4l2_subdev_frame_size_enum *fse) | |
| 533 | +{ | |
| 534 | + const struct imx290 *imx290 = to_imx290(sd); | |
| 535 | + const struct imx290_mode *imx290_modes = imx290_modes_ptr(imx290); | |
| 536 | + | |
| 537 | + if ((fse->code != imx290_formats[0].code) && | |
| 538 | + (fse->code != imx290_formats[1].code)) | |
| 539 | + return -EINVAL; | |
| 540 | + | |
| 541 | + if (fse->index >= imx290_modes_num(imx290)) | |
| 542 | + return -EINVAL; | |
| 543 | + | |
| 544 | + fse->min_width = imx290_modes[fse->index].width; | |
| 545 | + fse->max_width = imx290_modes[fse->index].width; | |
| 546 | + fse->min_height = imx290_modes[fse->index].height; | |
| 547 | + fse->max_height = imx290_modes[fse->index].height; | |
| 548 | + | |
| 549 | + return 0; | |
| 550 | +} | |
| 551 | + | |
| 420 | 552 | static int imx290_get_fmt(struct v4l2_subdev *sd, |
| 421 | 553 | struct v4l2_subdev_pad_config *cfg, |
| 422 | 554 | struct v4l2_subdev_format *fmt) |
| @@ -439,6 +571,30 @@ static int imx290_get_fmt(struct v4l2_subdev *sd, · 16 unmodified lines | ||
| 439 | 571 | return 0; |
| 440 | 572 | } |
| 441 | 573 | |
| 574 | +static inline u8 imx290_get_link_freq_index(struct imx290 *imx290) | |
| 575 | +{ | |
| 576 | + return imx290->current_mode->link_freq_index; | |
| 577 | +} | |
| 578 | + | |
| 579 | +static s64 imx290_get_link_freq(struct imx290 *imx290) | |
| 580 | +{ | |
| 581 | + u8 index = imx290_get_link_freq_index(imx290); | |
| 582 | + | |
| 583 | + return *(imx290_link_freqs_ptr(imx290) + index); | |
| 584 | +} | |
| 585 | + | |
| 586 | +static u64 imx290_calc_pixel_rate(struct imx290 *imx290) | |
| 587 | +{ | |
| 588 | + s64 link_freq = imx290_get_link_freq(imx290); | |
| 589 | + u8 nlanes = imx290->nlanes; | |
| 590 | + u64 pixel_rate; | |
| 591 | + | |
| 592 | + /* pixel rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ | |
| 593 | + pixel_rate = link_freq * 2 * nlanes; | |
| 594 | + do_div(pixel_rate, imx290->bpp); | |
| 595 | + return pixel_rate; | |
| 596 | +} | |
| 597 | + | |
| 442 | 598 | static int imx290_set_fmt(struct v4l2_subdev *sd, |
| 443 | 599 | struct v4l2_subdev_pad_config *cfg, |
| 444 | 600 | struct v4l2_subdev_format *fmt) |
| @@ -450,9 +606,8 @@ static int imx290_set_fmt(struct v4l2_subdev *sd, · 5 unmodified lines | ||
| 450 | 606 | |
| 451 | 607 | mutex_lock(&imx290->lock); |
| 452 | 608 | |
| 453 | − mode = v4l2_find_nearest_size(imx290_modes, | |
| 454 | − ARRAY_SIZE(imx290_modes), | |
| 455 | − width, height, | |
| 609 | + mode = v4l2_find_nearest_size(imx290_modes_ptr(imx290), | |
| 610 | + imx290_modes_num(imx290), width, height, | |
| 456 | 611 | fmt->format.width, fmt->format.height); |
| 457 | 612 | |
| 458 | 613 | fmt->format.width = mode->width; |
| @@ -472,10 +627,15 @@ static int imx290_set_fmt(struct v4l2_subdev *sd, · 13 unmodified lines | ||
| 472 | 627 | format = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); |
| 473 | 628 | } else { |
| 474 | 629 | format = &imx290->current_format; |
| 475 | − __v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index); | |
| 476 | − __v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate, mode->pixel_rate); | |
| 477 | − | |
| 478 | 630 | imx290->current_mode = mode; |
| 631 | + imx290->bpp = imx290_formats[i].bpp; | |
| 632 | + | |
| 633 | + if (imx290->link_freq) | |
| 634 | + __v4l2_ctrl_s_ctrl(imx290->link_freq, | |
| 635 | + imx290_get_link_freq_index(imx290)); | |
| 636 | + if (imx290->pixel_rate) | |
| 637 | + __v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate, | |
| 638 | + imx290_calc_pixel_rate(imx290)); | |
| 479 | 639 | } |
| 480 | 640 | |
| 481 | 641 | *format = fmt->format; |
| @@ -499,12 +659,11 @@ static int imx290_entity_init_cfg(struct v4l2_subdev *subdev, · 17 unmodified lines | ||
| 499 | 659 | return 0; |
| 500 | 660 | } |
| 501 | 661 | |
| 502 | −static int imx290_write_current_format(struct imx290 *imx290, | |
| 503 | − struct v4l2_mbus_framefmt *format) | |
| 662 | +static int imx290_write_current_format(struct imx290 *imx290) | |
| 504 | 663 | { |
| 505 | 664 | int ret; |
| 506 | 665 | |
| 507 | − switch (format->code) { | |
| 666 | + switch (imx290->current_format.code) { | |
| 508 | 667 | case MEDIA_BUS_FMT_SRGGB10_1X10: |
| 509 | 668 | ret = imx290_set_register_array(imx290, imx290_10bit_settings, |
| 510 | 669 | ARRAY_SIZE( |
| @@ -514,6 +673,15 @@ static int imx290_write_current_format(struct imx290 *imx290, · 3 unmodified lines | ||
| 514 | 673 | return ret; |
| 515 | 674 | } |
| 516 | 675 | break; |
| 676 | + case MEDIA_BUS_FMT_SRGGB12_1X12: | |
| 677 | + ret = imx290_set_register_array(imx290, imx290_12bit_settings, | |
| 678 | + ARRAY_SIZE( | |
| 679 | + imx290_12bit_settings)); | |
| 680 | + if (ret < 0) { | |
| 681 | + dev_err(imx290->dev, "Could not set format registers\n"); | |
| 682 | + return ret; | |
| 683 | + } | |
| 684 | + break; | |
| 517 | 685 | default: |
| 518 | 686 | dev_err(imx290->dev, "Unknown pixel format\n"); |
| 519 | 687 | return -EINVAL; |
| @@ -522,6 +690,25 @@ static int imx290_write_current_format(struct imx290 *imx290, · 2 unmodified lines | ||
| 522 | 690 | return 0; |
| 523 | 691 | } |
| 524 | 692 | |
| 693 | +static int imx290_set_hmax(struct imx290 *imx290, u32 val) | |
| 694 | +{ | |
| 695 | + int ret; | |
| 696 | + | |
| 697 | + ret = imx290_write_reg(imx290, IMX290_HMAX_LOW, (val & 0xff)); | |
| 698 | + if (ret) { | |
| 699 | + dev_err(imx290->dev, "Error setting HMAX register\n"); | |
| 700 | + return ret; | |
| 701 | + } | |
| 702 | + | |
| 703 | + ret = imx290_write_reg(imx290, IMX290_HMAX_HIGH, ((val >> 8) & 0xff)); | |
| 704 | + if (ret) { | |
| 705 | + dev_err(imx290->dev, "Error setting HMAX register\n"); | |
| 706 | + return ret; | |
| 707 | + } | |
| 708 | + | |
| 709 | + return 0; | |
| 710 | +} | |
| 711 | + | |
| 525 | 712 | /* Start streaming */ |
| 526 | 713 | static int imx290_start_streaming(struct imx290 *imx290) |
| 527 | 714 | { |
| @@ -536,8 +723,8 @@ static int imx290_start_streaming(struct imx290 *imx290) · 8 unmodified lines | ||
| 536 | 723 | return ret; |
| 537 | 724 | } |
| 538 | 725 | |
| 539 | − /* Set current frame format */ | |
| 540 | − ret = imx290_write_current_format(imx290, &imx290->current_format); | |
| 726 | + /* Apply the register values related to current frame format */ | |
| 727 | + ret = imx290_write_current_format(imx290); | |
| 541 | 728 | if (ret < 0) { |
| 542 | 729 | dev_err(imx290->dev, "Could not set frame format\n"); |
| 543 | 730 | return ret; |
| @@ -550,6 +737,9 @@ static int imx290_start_streaming(struct imx290 *imx290) · 6 unmodified lines | ||
| 550 | 737 | dev_err(imx290->dev, "Could not set current mode\n"); |
| 551 | 738 | return ret; |
| 552 | 739 | } |
| 740 | + ret = imx290_set_hmax(imx290, imx290->current_mode->hmax); | |
| 741 | + if (ret < 0) | |
| 742 | + return ret; | |
| 553 | 743 | |
| 554 | 744 | /* Apply customized values from user */ |
| 555 | 745 | ret = v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler); |
| @@ -607,6 +797,49 @@ static int imx290_get_regulators(struct device *dev, struct imx290 *imx290) · 51 unmodified lines | ||
| 607 | 797 | imx290->supplies); |
| 608 | 798 | } |
| 609 | 799 | |
| 800 | +static int imx290_set_data_lanes(struct imx290 *imx290) | |
| 801 | +{ | |
| 802 | + int ret = 0, laneval, frsel; | |
| 803 | + | |
| 804 | + switch (imx290->nlanes) { | |
| 805 | + case 2: | |
| 806 | + laneval = 0x01; | |
| 807 | + frsel = 0x02; | |
| 808 | + break; | |
| 809 | + case 4: | |
| 810 | + laneval = 0x03; | |
| 811 | + frsel = 0x01; | |
| 812 | + break; | |
| 813 | + default: | |
| 814 | + /* | |
| 815 | + * We should never hit this since the data lane count is | |
| 816 | + * validated in probe itself | |
| 817 | + */ | |
| 818 | + dev_err(imx290->dev, "Lane configuration not supported\n"); | |
| 819 | + ret = -EINVAL; | |
| 820 | + goto exit; | |
| 821 | + } | |
| 822 | + | |
| 823 | + ret = imx290_write_reg(imx290, IMX290_PHY_LANE_NUM, laneval); | |
| 824 | + if (ret) { | |
| 825 | + dev_err(imx290->dev, "Error setting Physical Lane number register\n"); | |
| 826 | + goto exit; | |
| 827 | + } | |
| 828 | + | |
| 829 | + ret = imx290_write_reg(imx290, IMX290_CSI_LANE_MODE, laneval); | |
| 830 | + if (ret) { | |
| 831 | + dev_err(imx290->dev, "Error setting CSI Lane mode register\n"); | |
| 832 | + goto exit; | |
| 833 | + } | |
| 834 | + | |
| 835 | + ret = imx290_write_reg(imx290, IMX290_FR_FDG_SEL, frsel); | |
| 836 | + if (ret) | |
| 837 | + dev_err(imx290->dev, "Error setting FR/FDG SEL register\n"); | |
| 838 | + | |
| 839 | +exit: | |
| 840 | + return ret; | |
| 841 | +} | |
| 842 | + | |
| 610 | 843 | static int imx290_power_on(struct device *dev) |
| 611 | 844 | { |
| 612 | 845 | struct i2c_client *client = to_i2c_client(dev); |
| @@ -628,9 +861,12 @@ static int imx290_power_on(struct device *dev) · 15 unmodified lines | ||
| 628 | 861 | } |
| 629 | 862 | |
| 630 | 863 | usleep_range(1, 2); |
| 631 | − gpiod_set_value_cansleep(imx290->rst_gpio, 1); | |
| 864 | + gpiod_set_value_cansleep(imx290->rst_gpio, 0); | |
| 632 | 865 | usleep_range(30000, 31000); |
| 633 | 866 | |
| 867 | + /* Set data lane count */ | |
| 868 | + imx290_set_data_lanes(imx290); | |
| 869 | + | |
| 634 | 870 | return 0; |
| 635 | 871 | } |
| 636 | 872 | |
| @@ -641,14 +877,14 @@ static int imx290_power_off(struct device *dev) · 4 unmodified lines | ||
| 641 | 877 | struct imx290 *imx290 = to_imx290(sd); |
| 642 | 878 | |
| 643 | 879 | clk_disable_unprepare(imx290->xclk); |
| 644 | − gpiod_set_value_cansleep(imx290->rst_gpio, 0); | |
| 880 | + gpiod_set_value_cansleep(imx290->rst_gpio, 1); | |
| 645 | 881 | regulator_bulk_disable(IMX290_NUM_SUPPLIES, imx290->supplies); |
| 646 | 882 | |
| 647 | 883 | return 0; |
| 648 | 884 | } |
| 649 | 885 | |
| 650 | 886 | static const struct dev_pm_ops imx290_pm_ops = { |
| 651 | − SET_RUNTIME_PM_OPS(imx290_power_on, imx290_power_off, NULL) | |
| 887 | + SET_RUNTIME_PM_OPS(imx290_power_off, imx290_power_on, NULL) | |
| 652 | 888 | }; |
| 653 | 889 | |
| 654 | 890 | static const struct v4l2_subdev_video_ops imx290_video_ops = { |
| @@ -658,6 +894,7 @@ static const struct v4l2_subdev_video_ops imx290_video_ops = { · 3 unmodified lines | ||
| 658 | 894 | static const struct v4l2_subdev_pad_ops imx290_pad_ops = { |
| 659 | 895 | .init_cfg = imx290_entity_init_cfg, |
| 660 | 896 | .enum_mbus_code = imx290_enum_mbus_code, |
| 897 | + .enum_frame_size = imx290_enum_frame_size, | |
| 661 | 898 | .get_fmt = imx290_get_fmt, |
| 662 | 899 | .set_fmt = imx290_set_fmt, |
| 663 | 900 | }; |
| @@ -671,12 +908,39 @@ static const struct media_entity_operations imx290_subdev_entity_ops = { · 7 unmodified lines | ||
| 671 | 908 | .link_validate = v4l2_subdev_link_validate, |
| 672 | 909 | }; |
| 673 | 910 | |
| 911 | +/* | |
| 912 | + * Returns 0 if all link frequencies used by the driver for the given number | |
| 913 | + * of MIPI data lanes are mentioned in the device tree, or the value of the | |
| 914 | + * first missing frequency otherwise. | |
| 915 | + */ | |
| 916 | +static s64 imx290_check_link_freqs(const struct imx290 *imx290, | |
| 917 | + const struct v4l2_fwnode_endpoint *ep) | |
| 918 | +{ | |
| 919 | + int i, j; | |
| 920 | + const s64 *freqs = imx290_link_freqs_ptr(imx290); | |
| 921 | + int freqs_count = imx290_link_freqs_num(imx290); | |
| 922 | + | |
| 923 | + for (i = 0; i < freqs_count; i++) { | |
| 924 | + for (j = 0; j < ep->nr_of_link_frequencies; j++) | |
| 925 | + if (freqs[i] == ep->link_frequencies[j]) | |
| 926 | + break; | |
| 927 | + if (j == ep->nr_of_link_frequencies) | |
| 928 | + return freqs[i]; | |
| 929 | + } | |
| 930 | + return 0; | |
| 931 | +} | |
| 932 | + | |
| 674 | 933 | static int imx290_probe(struct i2c_client *client) |
| 675 | 934 | { |
| 676 | 935 | struct device *dev = &client->dev; |
| 677 | 936 | struct fwnode_handle *endpoint; |
| 937 | + /* Only CSI2 is supported for now: */ | |
| 938 | + struct v4l2_fwnode_endpoint ep = { | |
| 939 | + .bus_type = V4L2_MBUS_CSI2_DPHY | |
| 940 | + }; | |
| 678 | 941 | struct imx290 *imx290; |
| 679 | 942 | u32 xclk_freq; |
| 943 | + s64 fq; | |
| 680 | 944 | int ret; |
| 681 | 945 | |
| 682 | 946 | imx290 = devm_kzalloc(dev, sizeof(*imx290), GFP_KERNEL); |
| @@ -696,35 +960,40 @@ static int imx290_probe(struct i2c_client *client) · 13 unmodified lines | ||
| 696 | 960 | return -EINVAL; |
| 697 | 961 | } |
| 698 | 962 | |
| 699 | − ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &imx290->ep); | |
| 963 | + ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep); | |
| 700 | 964 | fwnode_handle_put(endpoint); |
| 701 | − if (ret) { | |
| 965 | + if (ret == -ENXIO) { | |
| 966 | + dev_err(dev, "Unsupported bus type, should be CSI2\n"); | |
| 967 | + goto free_err; | |
| 968 | + } else if (ret) { | |
| 702 | 969 | dev_err(dev, "Parsing endpoint node failed\n"); |
| 703 | 970 | goto free_err; |
| 704 | 971 | } |
| 705 | 972 | |
| 706 | − if (!imx290->ep.nr_of_link_frequencies) { | |
| 707 | − dev_err(dev, "link-frequency property not found in DT\n"); | |
| 973 | + /* Get number of data lanes */ | |
| 974 | + imx290->nlanes = ep.bus.mipi_csi2.num_data_lanes; | |
| 975 | + if (imx290->nlanes != 2 && imx290->nlanes != 4) { | |
| 976 | + dev_err(dev, "Invalid data lanes: %d\n", imx290->nlanes); | |
| 708 | 977 | ret = -EINVAL; |
| 709 | 978 | goto free_err; |
| 710 | 979 | } |
| 711 | 980 | |
| 712 | − if (imx290->ep.link_frequencies[0] != IMX290_DEFAULT_LINK_FREQ) { | |
| 713 | − dev_err(dev, "Unsupported link frequency\n"); | |
| 981 | + dev_dbg(dev, "Using %u data lanes\n", imx290->nlanes); | |
| 982 | + | |
| 983 | + if (!ep.nr_of_link_frequencies) { | |
| 984 | + dev_err(dev, "link-frequency property not found in DT\n"); | |
| 714 | 985 | ret = -EINVAL; |
| 715 | 986 | goto free_err; |
| 716 | 987 | } |
| 717 | 988 | |
| 718 | − /* Only CSI2 is supported for now */ | |
| 719 | − if (imx290->ep.bus_type != V4L2_MBUS_CSI2_DPHY) { | |
| 720 | − dev_err(dev, "Unsupported bus type, should be CSI2\n"); | |
| 989 | + /* Check that link frequences for all the modes are in device tree */ | |
| 990 | + fq = imx290_check_link_freqs(imx290, &ep); | |
| 991 | + if (fq) { | |
| 992 | + dev_err(dev, "Link frequency of %lld is not supported\n", fq); | |
| 721 | 993 | ret = -EINVAL; |
| 722 | 994 | goto free_err; |
| 723 | 995 | } |
| 724 | 996 | |
| 725 | − /* Set default mode to max resolution */ | |
| 726 | − imx290->current_mode = &imx290_modes[0]; | |
| 727 | − | |
| 728 | 997 | /* get system clock (xclk) */ |
| 729 | 998 | imx290->xclk = devm_clk_get(dev, "xclk"); |
| 730 | 999 | if (IS_ERR(imx290->xclk)) { |
| @@ -760,7 +1029,8 @@ static int imx290_probe(struct i2c_client *client) · 29 unmodified lines | ||
| 760 | 1029 | goto free_err; |
| 761 | 1030 | } |
| 762 | 1031 | |
| 763 | − imx290->rst_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS); | |
| 1032 | + imx290->rst_gpio = devm_gpiod_get_optional(dev, "reset", | |
| 1033 | + GPIOD_OUT_HIGH); | |
| 764 | 1034 | if (IS_ERR(imx290->rst_gpio)) { |
| 765 | 1035 | dev_err(dev, "Cannot get reset gpio\n"); |
| 766 | 1036 | ret = PTR_ERR(imx290->rst_gpio); |
| @@ -769,23 +1039,35 @@ static int imx290_probe(struct i2c_client *client) · 2 unmodified lines | ||
| 769 | 1039 | |
| 770 | 1040 | mutex_init(&imx290->lock); |
| 771 | 1041 | |
| 772 | − v4l2_ctrl_handler_init(&imx290->ctrls, 3); | |
| 1042 | + /* | |
| 1043 | + * Initialize the frame format. In particular, imx290->current_mode | |
| 1044 | + * and imx290->bpp are set to defaults: imx290_calc_pixel_rate() call | |
| 1045 | + * below relies on these fields. | |
| 1046 | + */ | |
| 1047 | + imx290_entity_init_cfg(&imx290->sd, NULL); | |
| 1048 | + | |
| 1049 | + v4l2_ctrl_handler_init(&imx290->ctrls, 4); | |
| 773 | 1050 | |
| 774 | 1051 | v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, |
| 775 | 1052 | V4L2_CID_GAIN, 0, 72, 1, 0); |
| 1053 | + | |
| 776 | 1054 | imx290->link_freq = |
| 777 | − v4l2_ctrl_new_int_menu(&imx290->ctrls, | |
| 778 | − &imx290_ctrl_ops, | |
| 1055 | + v4l2_ctrl_new_int_menu(&imx290->ctrls, &imx290_ctrl_ops, | |
| 779 | 1056 | V4L2_CID_LINK_FREQ, |
| 780 | − ARRAY_SIZE(imx290_link_freq) - 1, | |
| 781 | − 0, imx290_link_freq); | |
| 1057 | + imx290_link_freqs_num(imx290) - 1, 0, | |
| 1058 | + imx290_link_freqs_ptr(imx290)); | |
| 782 | 1059 | if (imx290->link_freq) |
| 783 | 1060 | imx290->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; |
| 784 | 1061 | |
| 785 | 1062 | imx290->pixel_rate = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, |
| 786 | − V4L2_CID_PIXEL_RATE, 1, | |
| 787 | − INT_MAX, 1, | |
| 788 | − imx290_modes[0].pixel_rate); | |
| 1063 | + V4L2_CID_PIXEL_RATE, | |
| 1064 | + 1, INT_MAX, 1, | |
| 1065 | + imx290_calc_pixel_rate(imx290)); | |
| 1066 | + | |
| 1067 | + v4l2_ctrl_new_std_menu_items(&imx290->ctrls, &imx290_ctrl_ops, | |
| 1068 | + V4L2_CID_TEST_PATTERN, | |
| 1069 | + ARRAY_SIZE(imx290_test_pattern_menu) - 1, | |
| 1070 | + 0, 0, imx290_test_pattern_menu); | |
| 789 | 1071 | |
| 790 | 1072 | imx290->sd.ctrl_handler = &imx290->ctrls; |
| 791 | 1073 | |
| @@ -826,7 +1108,7 @@ static int imx290_probe(struct i2c_client *client) · 34 unmodified lines | ||
| 826 | 1108 | pm_runtime_enable(dev); |
| 827 | 1109 | pm_runtime_idle(dev); |
| 828 | 1110 | |
| 829 | − v4l2_fwnode_endpoint_free(&imx290->ep); | |
| 1111 | + v4l2_fwnode_endpoint_free(&ep); | |
| 830 | 1112 | |
| 831 | 1113 | return 0; |
| 832 | 1114 | |
| @@ -836,7 +1118,7 @@ free_ctrl: · 3 unmodified lines | ||
| 836 | 1118 | v4l2_ctrl_handler_free(&imx290->ctrls); |
| 837 | 1119 | mutex_destroy(&imx290->lock); |
| 838 | 1120 | free_err: |
| 839 | − v4l2_fwnode_endpoint_free(&imx290->ep); | |
| 1121 | + v4l2_fwnode_endpoint_free(&ep); | |
| 840 | 1122 | |
| 841 | 1123 | return ret; |
| 842 | 1124 | } |
| @@ -0,0 +1,341 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0+ | |
| 2 | +/* | |
| 3 | + * Copyright (C) 2017-2020 Jacopo Mondi | |
| 4 | + * Copyright (C) 2017-2020 Kieran Bingham | |
| 5 | + * Copyright (C) 2017-2020 Laurent Pinchart | |
| 6 | + * Copyright (C) 2017-2020 Niklas Söderlund | |
| 7 | + * Copyright (C) 2016 Renesas Electronics Corporation | |
| 8 | + * Copyright (C) 2015 Cogent Embedded, Inc. | |
| 9 | + * | |
| 10 | + * This file exports functions to control the Maxim MAX9271 GMSL serializer | |
| 11 | + * chip. This is not a self-contained driver, as MAX9271 is usually embedded in | |
| 12 | + * camera modules with at least one image sensor and optional additional | |
| 13 | + * components, such as uController units or ISPs/DSPs. | |
| 14 | + * | |
| 15 | + * Drivers for the camera modules (i.e. rdacm20/21) are expected to use | |
| 16 | + * functions exported from this library driver to maximize code re-use. | |
| 17 | + */ | |
| 18 | + | |
| 19 | +#include <linux/delay.h> | |
| 20 | +#include <linux/i2c.h> | |
| 21 | + | |
| 22 | +#include "max9271.h" | |
| 23 | + | |
| 24 | +static int max9271_read(struct max9271_device *dev, u8 reg) | |
| 25 | +{ | |
| 26 | + int ret; | |
| 27 | + | |
| 28 | + dev_dbg(&dev->client->dev, "%s(0x%02x)\n", __func__, reg); | |
| 29 | + | |
| 30 | + ret = i2c_smbus_read_byte_data(dev->client, reg); | |
| 31 | + if (ret < 0) | |
| 32 | + dev_dbg(&dev->client->dev, | |
| 33 | + "%s: register 0x%02x read failed (%d)\n", | |
| 34 | + __func__, reg, ret); | |
| 35 | + | |
| 36 | + return ret; | |
| 37 | +} | |
| 38 | + | |
| 39 | +static int max9271_write(struct max9271_device *dev, u8 reg, u8 val) | |
| 40 | +{ | |
| 41 | + int ret; | |
| 42 | + | |
| 43 | + dev_dbg(&dev->client->dev, "%s(0x%02x, 0x%02x)\n", __func__, reg, val); | |
| 44 | + | |
| 45 | + ret = i2c_smbus_write_byte_data(dev->client, reg, val); | |
| 46 | + if (ret < 0) | |
| 47 | + dev_err(&dev->client->dev, | |
| 48 | + "%s: register 0x%02x write failed (%d)\n", | |
| 49 | + __func__, reg, ret); | |
| 50 | + | |
| 51 | + return ret; | |
| 52 | +} | |
| 53 | + | |
| 54 | +/* | |
| 55 | + * max9271_pclk_detect() - Detect valid pixel clock from image sensor | |
| 56 | + * | |
| 57 | + * Wait up to 10ms for a valid pixel clock. | |
| 58 | + * | |
| 59 | + * Returns 0 for success, < 0 for pixel clock not properly detected | |
| 60 | + */ | |
| 61 | +static int max9271_pclk_detect(struct max9271_device *dev) | |
| 62 | +{ | |
| 63 | + unsigned int i; | |
| 64 | + int ret; | |
| 65 | + | |
| 66 | + for (i = 0; i < 100; i++) { | |
| 67 | + ret = max9271_read(dev, 0x15); | |
| 68 | + if (ret < 0) | |
| 69 | + return ret; | |
| 70 | + | |
| 71 | + if (ret & MAX9271_PCLKDET) | |
| 72 | + return 0; | |
| 73 | + | |
| 74 | + usleep_range(50, 100); | |
| 75 | + } | |
| 76 | + | |
| 77 | + dev_err(&dev->client->dev, "Unable to detect valid pixel clock\n"); | |
| 78 | + | |
| 79 | + return -EIO; | |
| 80 | +} | |
| 81 | + | |
| 82 | +int max9271_set_serial_link(struct max9271_device *dev, bool enable) | |
| 83 | +{ | |
| 84 | + int ret; | |
| 85 | + u8 val = MAX9271_REVCCEN | MAX9271_FWDCCEN; | |
| 86 | + | |
| 87 | + if (enable) { | |
| 88 | + ret = max9271_pclk_detect(dev); | |
| 89 | + if (ret) | |
| 90 | + return ret; | |
| 91 | + | |
| 92 | + val |= MAX9271_SEREN; | |
| 93 | + } else { | |
| 94 | + val |= MAX9271_CLINKEN; | |
| 95 | + } | |
| 96 | + | |
| 97 | + /* | |
| 98 | + * The serializer temporarily disables the reverse control channel for | |
| 99 | + * 350µs after starting/stopping the forward serial link, but the | |
| 100 | + * deserializer synchronization time isn't clearly documented. | |
| 101 | + * | |
| 102 | + * According to the serializer datasheet we should wait 3ms, while | |
| 103 | + * according to the deserializer datasheet we should wait 5ms. | |
| 104 | + * | |
| 105 | + * Short delays here appear to show bit-errors in the writes following. | |
| 106 | + * Therefore a conservative delay seems best here. | |
| 107 | + */ | |
| 108 | + max9271_write(dev, 0x04, val); | |
| 109 | + usleep_range(5000, 8000); | |
| 110 | + | |
| 111 | + return 0; | |
| 112 | +} | |
| 113 | +EXPORT_SYMBOL_GPL(max9271_set_serial_link); | |
| 114 | + | |
| 115 | +int max9271_configure_i2c(struct max9271_device *dev, u8 i2c_config) | |
| 116 | +{ | |
| 117 | + int ret; | |
| 118 | + | |
| 119 | + ret = max9271_write(dev, 0x0d, i2c_config); | |
| 120 | + if (ret) | |
| 121 | + return ret; | |
| 122 | + | |
| 123 | + /* The delay required after an I2C bus configuration change is not | |
| 124 | + * characterized in the serializer manual. Sleep up to 5msec to | |
| 125 | + * stay safe. | |
| 126 | + */ | |
| 127 | + usleep_range(3500, 5000); | |
| 128 | + | |
| 129 | + return 0; | |
| 130 | +} | |
| 131 | +EXPORT_SYMBOL_GPL(max9271_configure_i2c); | |
| 132 | + | |
| 133 | +int max9271_set_high_threshold(struct max9271_device *dev, bool enable) | |
| 134 | +{ | |
| 135 | + int ret; | |
| 136 | + | |
| 137 | + ret = max9271_read(dev, 0x08); | |
| 138 | + if (ret < 0) | |
| 139 | + return ret; | |
| 140 | + | |
| 141 | + /* | |
| 142 | + * Enable or disable reverse channel high threshold to increase | |
| 143 | + * immunity to power supply noise. | |
| 144 | + */ | |
| 145 | + max9271_write(dev, 0x08, enable ? ret | BIT(0) : ret & ~BIT(0)); | |
| 146 | + usleep_range(2000, 2500); | |
| 147 | + | |
| 148 | + return 0; | |
| 149 | +} | |
| 150 | +EXPORT_SYMBOL_GPL(max9271_set_high_threshold); | |
| 151 | + | |
| 152 | +int max9271_configure_gmsl_link(struct max9271_device *dev) | |
| 153 | +{ | |
| 154 | + /* | |
| 155 | + * Configure the GMSL link: | |
| 156 | + * | |
| 157 | + * - Double input mode, high data rate, 24-bit mode | |
| 158 | + * - Latch input data on PCLKIN rising edge | |
| 159 | + * - Enable HS/VS encoding | |
| 160 | + * - 1-bit parity error detection | |
| 161 | + * | |
| 162 | + * TODO: Make the GMSL link configuration parametric. | |
| 163 | + */ | |
| 164 | + max9271_write(dev, 0x07, MAX9271_DBL | MAX9271_HVEN | | |
| 165 | + MAX9271_EDC_1BIT_PARITY); | |
| 166 | + usleep_range(5000, 8000); | |
| 167 | + | |
| 168 | + /* | |
| 169 | + * Adjust spread spectrum to +4% and auto-detect pixel clock | |
| 170 | + * and serial link rate. | |
| 171 | + */ | |
| 172 | + max9271_write(dev, 0x02, MAX9271_SPREAD_SPECT_4 | MAX9271_R02_RES | | |
| 173 | + MAX9271_PCLK_AUTODETECT | MAX9271_SERIAL_AUTODETECT); | |
| 174 | + usleep_range(5000, 8000); | |
| 175 | + | |
| 176 | + return 0; | |
| 177 | +} | |
| 178 | +EXPORT_SYMBOL_GPL(max9271_configure_gmsl_link); | |
| 179 | + | |
| 180 | +int max9271_set_gpios(struct max9271_device *dev, u8 gpio_mask) | |
| 181 | +{ | |
| 182 | + int ret; | |
| 183 | + | |
| 184 | + ret = max9271_read(dev, 0x0f); | |
| 185 | + if (ret < 0) | |
| 186 | + return 0; | |
| 187 | + | |
| 188 | + ret |= gpio_mask; | |
| 189 | + ret = max9271_write(dev, 0x0f, ret); | |
| 190 | + if (ret < 0) { | |
| 191 | + dev_err(&dev->client->dev, "Failed to set gpio (%d)\n", ret); | |
| 192 | + return ret; | |
| 193 | + } | |
| 194 | + | |
| 195 | + usleep_range(3500, 5000); | |
| 196 | + | |
| 197 | + return 0; | |
| 198 | +} | |
| 199 | +EXPORT_SYMBOL_GPL(max9271_set_gpios); | |
| 200 | + | |
| 201 | +int max9271_clear_gpios(struct max9271_device *dev, u8 gpio_mask) | |
| 202 | +{ | |
| 203 | + int ret; | |
| 204 | + | |
| 205 | + ret = max9271_read(dev, 0x0f); | |
| 206 | + if (ret < 0) | |
| 207 | + return 0; | |
| 208 | + | |
| 209 | + ret &= ~gpio_mask; | |
| 210 | + ret = max9271_write(dev, 0x0f, ret); | |
| 211 | + if (ret < 0) { | |
| 212 | + dev_err(&dev->client->dev, "Failed to clear gpio (%d)\n", ret); | |
| 213 | + return ret; | |
| 214 | + } | |
| 215 | + | |
| 216 | + usleep_range(3500, 5000); | |
| 217 | + | |
| 218 | + return 0; | |
| 219 | +} | |
| 220 | +EXPORT_SYMBOL_GPL(max9271_clear_gpios); | |
| 221 | + | |
| 222 | +int max9271_enable_gpios(struct max9271_device *dev, u8 gpio_mask) | |
| 223 | +{ | |
| 224 | + int ret; | |
| 225 | + | |
| 226 | + ret = max9271_read(dev, 0x0f); | |
| 227 | + if (ret < 0) | |
| 228 | + return 0; | |
| 229 | + | |
| 230 | + /* BIT(0) reserved: GPO is always enabled. */ | |
| 231 | + ret |= gpio_mask | BIT(0); | |
| 232 | + ret = max9271_write(dev, 0x0e, ret); | |
| 233 | + if (ret < 0) { | |
| 234 | + dev_err(&dev->client->dev, "Failed to enable gpio (%d)\n", ret); | |
| 235 | + return ret; | |
| 236 | + } | |
| 237 | + | |
| 238 | + usleep_range(3500, 5000); | |
| 239 | + | |
| 240 | + return 0; | |
| 241 | +} | |
| 242 | +EXPORT_SYMBOL_GPL(max9271_enable_gpios); | |
| 243 | + | |
| 244 | +int max9271_disable_gpios(struct max9271_device *dev, u8 gpio_mask) | |
| 245 | +{ | |
| 246 | + int ret; | |
| 247 | + | |
| 248 | + ret = max9271_read(dev, 0x0f); | |
| 249 | + if (ret < 0) | |
| 250 | + return 0; | |
| 251 | + | |
| 252 | + /* BIT(0) reserved: GPO cannot be disabled */ | |
| 253 | + ret &= (~gpio_mask | BIT(0)); | |
| 254 | + ret = max9271_write(dev, 0x0e, ret); | |
| 255 | + if (ret < 0) { | |
| 256 | + dev_err(&dev->client->dev, "Failed to disable gpio (%d)\n", ret); | |
| 257 | + return ret; | |
| 258 | + } | |
| 259 | + | |
| 260 | + usleep_range(3500, 5000); | |
| 261 | + | |
| 262 | + return 0; | |
| 263 | +} | |
| 264 | +EXPORT_SYMBOL_GPL(max9271_disable_gpios); | |
| 265 | + | |
| 266 | +int max9271_verify_id(struct max9271_device *dev) | |
| 267 | +{ | |
| 268 | + int ret; | |
| 269 | + | |
| 270 | + ret = max9271_read(dev, 0x1e); | |
| 271 | + if (ret < 0) { | |
| 272 | + dev_err(&dev->client->dev, "MAX9271 ID read failed (%d)\n", | |
| 273 | + ret); | |
| 274 | + return ret; | |
| 275 | + } | |
| 276 | + | |
| 277 | + if (ret != MAX9271_ID) { | |
| 278 | + dev_err(&dev->client->dev, "MAX9271 ID mismatch (0x%02x)\n", | |
| 279 | + ret); | |
| 280 | + return -ENXIO; | |
| 281 | + } | |
| 282 | + | |
| 283 | + return 0; | |
| 284 | +} | |
| 285 | +EXPORT_SYMBOL_GPL(max9271_verify_id); | |
| 286 | + | |
| 287 | +int max9271_set_address(struct max9271_device *dev, u8 addr) | |
| 288 | +{ | |
| 289 | + int ret; | |
| 290 | + | |
| 291 | + ret = max9271_write(dev, 0x00, addr << 1); | |
| 292 | + if (ret < 0) { | |
| 293 | + dev_err(&dev->client->dev, | |
| 294 | + "MAX9271 I2C address change failed (%d)\n", ret); | |
| 295 | + return ret; | |
| 296 | + } | |
| 297 | + usleep_range(3500, 5000); | |
| 298 | + | |
| 299 | + return 0; | |
| 300 | +} | |
| 301 | +EXPORT_SYMBOL_GPL(max9271_set_address); | |
| 302 | + | |
| 303 | +int max9271_set_deserializer_address(struct max9271_device *dev, u8 addr) | |
| 304 | +{ | |
| 305 | + int ret; | |
| 306 | + | |
| 307 | + ret = max9271_write(dev, 0x01, addr << 1); | |
| 308 | + if (ret < 0) { | |
| 309 | + dev_err(&dev->client->dev, | |
| 310 | + "MAX9271 deserializer address set failed (%d)\n", ret); | |
| 311 | + return ret; | |
| 312 | + } | |
| 313 | + usleep_range(3500, 5000); | |
| 314 | + | |
| 315 | + return 0; | |
| 316 | +} | |
| 317 | +EXPORT_SYMBOL_GPL(max9271_set_deserializer_address); | |
| 318 | + | |
| 319 | +int max9271_set_translation(struct max9271_device *dev, u8 source, u8 dest) | |
| 320 | +{ | |
| 321 | + int ret; | |
| 322 | + | |
| 323 | + ret = max9271_write(dev, 0x09, source << 1); | |
| 324 | + if (ret < 0) { | |
| 325 | + dev_err(&dev->client->dev, | |
| 326 | + "MAX9271 I2C translation setup failed (%d)\n", ret); | |
| 327 | + return ret; | |
| 328 | + } | |
| 329 | + usleep_range(3500, 5000); | |
| 330 | + | |
| 331 | + ret = max9271_write(dev, 0x0a, dest << 1); | |
| 332 | + if (ret < 0) { | |
| 333 | + dev_err(&dev->client->dev, | |
| 334 | + "MAX9271 I2C translation setup failed (%d)\n", ret); | |
| 335 | + return ret; | |
| 336 | + } | |
| 337 | + usleep_range(3500, 5000); | |
| 338 | + | |
| 339 | + return 0; | |
| 340 | +} | |
| 341 | +EXPORT_SYMBOL_GPL(max9271_set_translation); | |
| @@ -0,0 +1,224 @@ | ||
| 1 | +/* SPDX-License-Identifier: GPL-2.0+ */ | |
| 2 | +/* | |
| 3 | + * Copyright (C) 2017-2020 Jacopo Mondi | |
| 4 | + * Copyright (C) 2017-2020 Kieran Bingham | |
| 5 | + * Copyright (C) 2017-2020 Laurent Pinchart | |
| 6 | + * Copyright (C) 2017-2020 Niklas Söderlund | |
| 7 | + * Copyright (C) 2016 Renesas Electronics Corporation | |
| 8 | + * Copyright (C) 2015 Cogent Embedded, Inc. | |
| 9 | + */ | |
| 10 | + | |
| 11 | +#include <linux/i2c.h> | |
| 12 | + | |
| 13 | +#define MAX9271_DEFAULT_ADDR 0x40 | |
| 14 | + | |
| 15 | +/* Register 0x02 */ | |
| 16 | +#define MAX9271_SPREAD_SPECT_0 (0 << 5) | |
| 17 | +#define MAX9271_SPREAD_SPECT_05 (1 << 5) | |
| 18 | +#define MAX9271_SPREAD_SPECT_15 (2 << 5) | |
| 19 | +#define MAX9271_SPREAD_SPECT_1 (5 << 5) | |
| 20 | +#define MAX9271_SPREAD_SPECT_2 (3 << 5) | |
| 21 | +#define MAX9271_SPREAD_SPECT_3 (6 << 5) | |
| 22 | +#define MAX9271_SPREAD_SPECT_4 (7 << 5) | |
| 23 | +#define MAX9271_R02_RES BIT(4) | |
| 24 | +#define MAX9271_PCLK_AUTODETECT (3 << 2) | |
| 25 | +#define MAX9271_SERIAL_AUTODETECT (0x03) | |
| 26 | +/* Register 0x04 */ | |
| 27 | +#define MAX9271_SEREN BIT(7) | |
| 28 | +#define MAX9271_CLINKEN BIT(6) | |
| 29 | +#define MAX9271_PRBSEN BIT(5) | |
| 30 | +#define MAX9271_SLEEP BIT(4) | |
| 31 | +#define MAX9271_INTTYPE_I2C (0 << 2) | |
| 32 | +#define MAX9271_INTTYPE_UART (1 << 2) | |
| 33 | +#define MAX9271_INTTYPE_NONE (2 << 2) | |
| 34 | +#define MAX9271_REVCCEN BIT(1) | |
| 35 | +#define MAX9271_FWDCCEN BIT(0) | |
| 36 | +/* Register 0x07 */ | |
| 37 | +#define MAX9271_DBL BIT(7) | |
| 38 | +#define MAX9271_DRS BIT(6) | |
| 39 | +#define MAX9271_BWS BIT(5) | |
| 40 | +#define MAX9271_ES BIT(4) | |
| 41 | +#define MAX9271_HVEN BIT(2) | |
| 42 | +#define MAX9271_EDC_1BIT_PARITY (0 << 0) | |
| 43 | +#define MAX9271_EDC_6BIT_CRC (1 << 0) | |
| 44 | +#define MAX9271_EDC_6BIT_HAMMING (2 << 0) | |
| 45 | +/* Register 0x08 */ | |
| 46 | +#define MAX9271_INVVS BIT(7) | |
| 47 | +#define MAX9271_INVHS BIT(6) | |
| 48 | +#define MAX9271_REV_LOGAIN BIT(3) | |
| 49 | +#define MAX9271_REV_HIVTH BIT(0) | |
| 50 | +/* Register 0x09 */ | |
| 51 | +#define MAX9271_ID 0x09 | |
| 52 | +/* Register 0x0d */ | |
| 53 | +#define MAX9271_I2CLOCACK BIT(7) | |
| 54 | +#define MAX9271_I2CSLVSH_1046NS_469NS (3 << 5) | |
| 55 | +#define MAX9271_I2CSLVSH_938NS_352NS (2 << 5) | |
| 56 | +#define MAX9271_I2CSLVSH_469NS_234NS (1 << 5) | |
| 57 | +#define MAX9271_I2CSLVSH_352NS_117NS (0 << 5) | |
| 58 | +#define MAX9271_I2CMSTBT_837KBPS (7 << 2) | |
| 59 | +#define MAX9271_I2CMSTBT_533KBPS (6 << 2) | |
| 60 | +#define MAX9271_I2CMSTBT_339KBPS (5 << 2) | |
| 61 | +#define MAX9271_I2CMSTBT_173KBPS (4 << 2) | |
| 62 | +#define MAX9271_I2CMSTBT_105KBPS (3 << 2) | |
| 63 | +#define MAX9271_I2CMSTBT_84KBPS (2 << 2) | |
| 64 | +#define MAX9271_I2CMSTBT_28KBPS (1 << 2) | |
| 65 | +#define MAX9271_I2CMSTBT_8KBPS (0 << 2) | |
| 66 | +#define MAX9271_I2CSLVTO_NONE (3 << 0) | |
| 67 | +#define MAX9271_I2CSLVTO_1024US (2 << 0) | |
| 68 | +#define MAX9271_I2CSLVTO_256US (1 << 0) | |
| 69 | +#define MAX9271_I2CSLVTO_64US (0 << 0) | |
| 70 | +/* Register 0x0f */ | |
| 71 | +#define MAX9271_GPIO5OUT BIT(5) | |
| 72 | +#define MAX9271_GPIO4OUT BIT(4) | |
| 73 | +#define MAX9271_GPIO3OUT BIT(3) | |
| 74 | +#define MAX9271_GPIO2OUT BIT(2) | |
| 75 | +#define MAX9271_GPIO1OUT BIT(1) | |
| 76 | +#define MAX9271_GPO BIT(0) | |
| 77 | +/* Register 0x15 */ | |
| 78 | +#define MAX9271_PCLKDET BIT(0) | |
| 79 | + | |
| 80 | +/** | |
| 81 | + * struct max9271_device - max9271 device | |
| 82 | + * @client: The i2c client for the max9271 instance | |
| 83 | + */ | |
| 84 | +struct max9271_device { | |
| 85 | + struct i2c_client *client; | |
| 86 | +}; | |
| 87 | + | |
| 88 | +/** | |
| 89 | + * max9271_set_serial_link() - Enable/disable serial link | |
| 90 | + * @dev: The max9271 device | |
| 91 | + * @enable: Serial link enable/disable flag | |
| 92 | + * | |
| 93 | + * Return 0 on success or a negative error code on failure | |
| 94 | + */ | |
| 95 | +int max9271_set_serial_link(struct max9271_device *dev, bool enable); | |
| 96 | + | |
| 97 | +/** | |
| 98 | + * max9271_configure_i2c() - Configure I2C bus parameters | |
| 99 | + * @dev: The max9271 device | |
| 100 | + * @i2c_config: The I2C bus configuration bit mask | |
| 101 | + * | |
| 102 | + * Configure MAX9271 I2C interface. The bus configuration provided in the | |
| 103 | + * @i2c_config parameter shall be assembled using bit values defined by the | |
| 104 | + * MAX9271_I2C* macros. | |
| 105 | + * | |
| 106 | + * Return 0 on success or a negative error code on failure | |
| 107 | + */ | |
| 108 | +int max9271_configure_i2c(struct max9271_device *dev, u8 i2c_config); | |
| 109 | + | |
| 110 | +/** | |
| 111 | + * max9271_set_high_threshold() - Enable or disable reverse channel high | |
| 112 | + * threshold | |
| 113 | + * @dev: The max9271 device | |
| 114 | + * @enable: High threshold enable/disable flag | |
| 115 | + * | |
| 116 | + * Return 0 on success or a negative error code on failure | |
| 117 | + */ | |
| 118 | +int max9271_set_high_threshold(struct max9271_device *dev, bool enable); | |
| 119 | + | |
| 120 | +/** | |
| 121 | + * max9271_configure_gmsl_link() - Configure the GMSL link | |
| 122 | + * @dev: The max9271 device | |
| 123 | + * | |
| 124 | + * FIXME: the GMSL link configuration is currently hardcoded and performed | |
| 125 | + * by programming registers 0x04, 0x07 and 0x02. | |
| 126 | + * | |
| 127 | + * Return 0 on success or a negative error code on failure | |
| 128 | + */ | |
| 129 | +int max9271_configure_gmsl_link(struct max9271_device *dev); | |
| 130 | + | |
| 131 | +/** | |
| 132 | + * max9271_set_gpios() - Set gpio lines to physical high value | |
| 133 | + * @dev: The max9271 device | |
| 134 | + * @gpio_mask: The mask of gpio lines to set to high value | |
| 135 | + * | |
| 136 | + * The @gpio_mask parameter shall be assembled using the MAX9271_GP[IO|O]* | |
| 137 | + * bit values. | |
| 138 | + * | |
| 139 | + * Return 0 on success or a negative error code on failure | |
| 140 | + */ | |
| 141 | +int max9271_set_gpios(struct max9271_device *dev, u8 gpio_mask); | |
| 142 | + | |
| 143 | +/** | |
| 144 | + * max9271_clear_gpios() - Set gpio lines to physical low value | |
| 145 | + * @dev: The max9271 device | |
| 146 | + * @gpio_mask: The mask of gpio lines to set to low value | |
| 147 | + * | |
| 148 | + * The @gpio_mask parameter shall be assembled using the MAX9271_GP[IO|O]* | |
| 149 | + * bit values. | |
| 150 | + * | |
| 151 | + * Return 0 on success or a negative error code on failure | |
| 152 | + */ | |
| 153 | +int max9271_clear_gpios(struct max9271_device *dev, u8 gpio_mask); | |
| 154 | + | |
| 155 | +/** | |
| 156 | + * max9271_enable_gpios() - Enable gpio lines | |
| 157 | + * @dev: The max9271 device | |
| 158 | + * @gpio_mask: The mask of gpio lines to enable | |
| 159 | + * | |
| 160 | + * The @gpio_mask parameter shall be assembled using the MAX9271_GPIO* | |
| 161 | + * bit values. GPO line is always enabled by default. | |
| 162 | + * | |
| 163 | + * Return 0 on success or a negative error code on failure | |
| 164 | + */ | |
| 165 | +int max9271_enable_gpios(struct max9271_device *dev, u8 gpio_mask); | |
| 166 | + | |
| 167 | +/** | |
| 168 | + * max9271_disable_gpios() - Disable gpio lines | |
| 169 | + * @dev: The max9271 device | |
| 170 | + * @gpio_mask: The mask of gpio lines to disable | |
| 171 | + * | |
| 172 | + * The @gpio_mask parameter shall be assembled using the MAX9271_GPIO* | |
| 173 | + * bit values. GPO line is always enabled by default and cannot be disabled. | |
| 174 | + * | |
| 175 | + * Return 0 on success or a negative error code on failure | |
| 176 | + */ | |
| 177 | +int max9271_disable_gpios(struct max9271_device *dev, u8 gpio_mask); | |
| 178 | + | |
| 179 | +/** | |
| 180 | + * max9271_verify_id() - Read and verify MAX9271 id | |
| 181 | + * @dev: The max9271 device | |
| 182 | + * | |
| 183 | + * Return 0 on success or a negative error code on failure | |
| 184 | + */ | |
| 185 | +int max9271_verify_id(struct max9271_device *dev); | |
| 186 | + | |
| 187 | +/** | |
| 188 | + * max9271_set_address() - Program a new I2C address | |
| 189 | + * @dev: The max9271 device | |
| 190 | + * @addr: The new I2C address in 7-bit format | |
| 191 | + * | |
| 192 | + * This function only takes care of programming the new I2C address @addr to | |
| 193 | + * in the MAX9271 chip registers, it is responsiblity of the caller to set | |
| 194 | + * the i2c address client to the @addr value to be able to communicate with | |
| 195 | + * the MAX9271 chip using the I2C framework APIs after this function returns. | |
| 196 | + * | |
| 197 | + * Return 0 on success or a negative error code on failure | |
| 198 | + */ | |
| 199 | +int max9271_set_address(struct max9271_device *dev, u8 addr); | |
| 200 | + | |
| 201 | +/** | |
| 202 | + * max9271_set_deserializer_address() - Program the remote deserializer address | |
| 203 | + * @dev: The max9271 device | |
| 204 | + * @addr: The deserializer I2C address in 7-bit format | |
| 205 | + * | |
| 206 | + * Return 0 on success or a negative error code on failure | |
| 207 | + */ | |
| 208 | +int max9271_set_deserializer_address(struct max9271_device *dev, u8 addr); | |
| 209 | + | |
| 210 | +/** | |
| 211 | + * max9271_set_translation() - Program I2C address translation | |
| 212 | + * @dev: The max9271 device | |
| 213 | + * @source: The I2C source address | |
| 214 | + * @dest: The I2C destination address | |
| 215 | + * | |
| 216 | + * Program address translation from @source to @dest. This is required to | |
| 217 | + * communicate with local devices that do not support address reprogramming. | |
| 218 | + * | |
| 219 | + * TODO: The device supports translation of two address, this function currently | |
| 220 | + * supports a single one. | |
| 221 | + * | |
| 222 | + * Return 0 on success or a negative error code on failure | |
| 223 | + */ | |
| 224 | +int max9271_set_translation(struct max9271_device *dev, u8 source, u8 dest); | |
| @@ -0,0 +1,1320 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0+ | |
| 2 | +/* | |
| 3 | + * Maxim MAX9286 GMSL Deserializer Driver | |
| 4 | + * | |
| 5 | + * Copyright (C) 2017-2019 Jacopo Mondi | |
| 6 | + * Copyright (C) 2017-2019 Kieran Bingham | |
| 7 | + * Copyright (C) 2017-2019 Laurent Pinchart | |
| 8 | + * Copyright (C) 2017-2019 Niklas Söderlund | |
| 9 | + * Copyright (C) 2016 Renesas Electronics Corporation | |
| 10 | + * Copyright (C) 2015 Cogent Embedded, Inc. | |
| 11 | + */ | |
| 12 | + | |
| 13 | +#include <linux/delay.h> | |
| 14 | +#include <linux/device.h> | |
| 15 | +#include <linux/fwnode.h> | |
| 16 | +#include <linux/gpio/consumer.h> | |
| 17 | +#include <linux/gpio/driver.h> | |
| 18 | +#include <linux/i2c.h> | |
| 19 | +#include <linux/i2c-mux.h> | |
| 20 | +#include <linux/module.h> | |
| 21 | +#include <linux/mutex.h> | |
| 22 | +#include <linux/of_graph.h> | |
| 23 | +#include <linux/regulator/consumer.h> | |
| 24 | +#include <linux/slab.h> | |
| 25 | + | |
| 26 | +#include <media/v4l2-async.h> | |
| 27 | +#include <media/v4l2-ctrls.h> | |
| 28 | +#include <media/v4l2-device.h> | |
| 29 | +#include <media/v4l2-fwnode.h> | |
| 30 | +#include <media/v4l2-subdev.h> | |
| 31 | + | |
| 32 | +/* Register 0x00 */ | |
| 33 | +#define MAX9286_MSTLINKSEL_AUTO (7 << 5) | |
| 34 | +#define MAX9286_MSTLINKSEL(n) ((n) << 5) | |
| 35 | +#define MAX9286_EN_VS_GEN BIT(4) | |
| 36 | +#define MAX9286_LINKEN(n) (1 << (n)) | |
| 37 | +/* Register 0x01 */ | |
| 38 | +#define MAX9286_FSYNCMODE_ECU (3 << 6) | |
| 39 | +#define MAX9286_FSYNCMODE_EXT (2 << 6) | |
| 40 | +#define MAX9286_FSYNCMODE_INT_OUT (1 << 6) | |
| 41 | +#define MAX9286_FSYNCMODE_INT_HIZ (0 << 6) | |
| 42 | +#define MAX9286_GPIEN BIT(5) | |
| 43 | +#define MAX9286_ENLMO_RSTFSYNC BIT(2) | |
| 44 | +#define MAX9286_FSYNCMETH_AUTO (2 << 0) | |
| 45 | +#define MAX9286_FSYNCMETH_SEMI_AUTO (1 << 0) | |
| 46 | +#define MAX9286_FSYNCMETH_MANUAL (0 << 0) | |
| 47 | +#define MAX9286_REG_FSYNC_PERIOD_L 0x06 | |
| 48 | +#define MAX9286_REG_FSYNC_PERIOD_M 0x07 | |
| 49 | +#define MAX9286_REG_FSYNC_PERIOD_H 0x08 | |
| 50 | +/* Register 0x0a */ | |
| 51 | +#define MAX9286_FWDCCEN(n) (1 << ((n) + 4)) | |
| 52 | +#define MAX9286_REVCCEN(n) (1 << (n)) | |
| 53 | +/* Register 0x0c */ | |
| 54 | +#define MAX9286_HVEN BIT(7) | |
| 55 | +#define MAX9286_EDC_6BIT_HAMMING (2 << 5) | |
| 56 | +#define MAX9286_EDC_6BIT_CRC (1 << 5) | |
| 57 | +#define MAX9286_EDC_1BIT_PARITY (0 << 5) | |
| 58 | +#define MAX9286_DESEL BIT(4) | |
| 59 | +#define MAX9286_INVVS BIT(3) | |
| 60 | +#define MAX9286_INVHS BIT(2) | |
| 61 | +#define MAX9286_HVSRC_D0 (2 << 0) | |
| 62 | +#define MAX9286_HVSRC_D14 (1 << 0) | |
| 63 | +#define MAX9286_HVSRC_D18 (0 << 0) | |
| 64 | +/* Register 0x0f */ | |
| 65 | +#define MAX9286_0X0F_RESERVED BIT(3) | |
| 66 | +/* Register 0x12 */ | |
| 67 | +#define MAX9286_CSILANECNT(n) (((n) - 1) << 6) | |
| 68 | +#define MAX9286_CSIDBL BIT(5) | |
| 69 | +#define MAX9286_DBL BIT(4) | |
| 70 | +#define MAX9286_DATATYPE_USER_8BIT (11 << 0) | |
| 71 | +#define MAX9286_DATATYPE_USER_YUV_12BIT (10 << 0) | |
| 72 | +#define MAX9286_DATATYPE_USER_24BIT (9 << 0) | |
| 73 | +#define MAX9286_DATATYPE_RAW14 (8 << 0) | |
| 74 | +#define MAX9286_DATATYPE_RAW11 (7 << 0) | |
| 75 | +#define MAX9286_DATATYPE_RAW10 (6 << 0) | |
| 76 | +#define MAX9286_DATATYPE_RAW8 (5 << 0) | |
| 77 | +#define MAX9286_DATATYPE_YUV422_10BIT (4 << 0) | |
| 78 | +#define MAX9286_DATATYPE_YUV422_8BIT (3 << 0) | |
| 79 | +#define MAX9286_DATATYPE_RGB555 (2 << 0) | |
| 80 | +#define MAX9286_DATATYPE_RGB565 (1 << 0) | |
| 81 | +#define MAX9286_DATATYPE_RGB888 (0 << 0) | |
| 82 | +/* Register 0x15 */ | |
| 83 | +#define MAX9286_VC(n) ((n) << 5) | |
| 84 | +#define MAX9286_VCTYPE BIT(4) | |
| 85 | +#define MAX9286_CSIOUTEN BIT(3) | |
| 86 | +#define MAX9286_0X15_RESV (3 << 0) | |
| 87 | +/* Register 0x1b */ | |
| 88 | +#define MAX9286_SWITCHIN(n) (1 << ((n) + 4)) | |
| 89 | +#define MAX9286_ENEQ(n) (1 << (n)) | |
| 90 | +/* Register 0x27 */ | |
| 91 | +#define MAX9286_LOCKED BIT(7) | |
| 92 | +/* Register 0x31 */ | |
| 93 | +#define MAX9286_FSYNC_LOCKED BIT(6) | |
| 94 | +/* Register 0x34 */ | |
| 95 | +#define MAX9286_I2CLOCACK BIT(7) | |
| 96 | +#define MAX9286_I2CSLVSH_1046NS_469NS (3 << 5) | |
| 97 | +#define MAX9286_I2CSLVSH_938NS_352NS (2 << 5) | |
| 98 | +#define MAX9286_I2CSLVSH_469NS_234NS (1 << 5) | |
| 99 | +#define MAX9286_I2CSLVSH_352NS_117NS (0 << 5) | |
| 100 | +#define MAX9286_I2CMSTBT_837KBPS (7 << 2) | |
| 101 | +#define MAX9286_I2CMSTBT_533KBPS (6 << 2) | |
| 102 | +#define MAX9286_I2CMSTBT_339KBPS (5 << 2) | |
| 103 | +#define MAX9286_I2CMSTBT_173KBPS (4 << 2) | |
| 104 | +#define MAX9286_I2CMSTBT_105KBPS (3 << 2) | |
| 105 | +#define MAX9286_I2CMSTBT_84KBPS (2 << 2) | |
| 106 | +#define MAX9286_I2CMSTBT_28KBPS (1 << 2) | |
| 107 | +#define MAX9286_I2CMSTBT_8KBPS (0 << 2) | |
| 108 | +#define MAX9286_I2CSLVTO_NONE (3 << 0) | |
| 109 | +#define MAX9286_I2CSLVTO_1024US (2 << 0) | |
| 110 | +#define MAX9286_I2CSLVTO_256US (1 << 0) | |
| 111 | +#define MAX9286_I2CSLVTO_64US (0 << 0) | |
| 112 | +/* Register 0x3b */ | |
| 113 | +#define MAX9286_REV_TRF(n) ((n) << 4) | |
| 114 | +#define MAX9286_REV_AMP(n) ((((n) - 30) / 10) << 1) /* in mV */ | |
| 115 | +#define MAX9286_REV_AMP_X BIT(0) | |
| 116 | +/* Register 0x3f */ | |
| 117 | +#define MAX9286_EN_REV_CFG BIT(6) | |
| 118 | +#define MAX9286_REV_FLEN(n) ((n) - 20) | |
| 119 | +/* Register 0x49 */ | |
| 120 | +#define MAX9286_VIDEO_DETECT_MASK 0x0f | |
| 121 | +/* Register 0x69 */ | |
| 122 | +#define MAX9286_LFLTBMONMASKED BIT(7) | |
| 123 | +#define MAX9286_LOCKMONMASKED BIT(6) | |
| 124 | +#define MAX9286_AUTOCOMBACKEN BIT(5) | |
| 125 | +#define MAX9286_AUTOMASKEN BIT(4) | |
| 126 | +#define MAX9286_MASKLINK(n) ((n) << 0) | |
| 127 | + | |
| 128 | +/* | |
| 129 | + * The sink and source pads are created to match the OF graph port numbers so | |
| 130 | + * that their indexes can be used interchangeably. | |
| 131 | + */ | |
| 132 | +#define MAX9286_NUM_GMSL 4 | |
| 133 | +#define MAX9286_N_SINKS 4 | |
| 134 | +#define MAX9286_N_PADS 5 | |
| 135 | +#define MAX9286_SRC_PAD 4 | |
| 136 | + | |
| 137 | +struct max9286_source { | |
| 138 | + struct v4l2_async_subdev asd; | |
| 139 | + struct v4l2_subdev *sd; | |
| 140 | + struct fwnode_handle *fwnode; | |
| 141 | +}; | |
| 142 | + | |
| 143 | +#define asd_to_max9286_source(_asd) \ | |
| 144 | + container_of(_asd, struct max9286_source, asd) | |
| 145 | + | |
| 146 | +struct max9286_priv { | |
| 147 | + struct i2c_client *client; | |
| 148 | + struct gpio_desc *gpiod_pwdn; | |
| 149 | + struct v4l2_subdev sd; | |
| 150 | + struct media_pad pads[MAX9286_N_PADS]; | |
| 151 | + struct regulator *regulator; | |
| 152 | + | |
| 153 | + struct gpio_chip gpio; | |
| 154 | + u8 gpio_state; | |
| 155 | + | |
| 156 | + struct i2c_mux_core *mux; | |
| 157 | + unsigned int mux_channel; | |
| 158 | + bool mux_open; | |
| 159 | + | |
| 160 | + struct v4l2_ctrl_handler ctrls; | |
| 161 | + struct v4l2_ctrl *pixelrate; | |
| 162 | + | |
| 163 | + struct v4l2_mbus_framefmt fmt[MAX9286_N_SINKS]; | |
| 164 | + | |
| 165 | + /* Protects controls and fmt structures */ | |
| 166 | + struct mutex mutex; | |
| 167 | + | |
| 168 | + unsigned int nsources; | |
| 169 | + unsigned int source_mask; | |
| 170 | + unsigned int route_mask; | |
| 171 | + unsigned int bound_sources; | |
| 172 | + unsigned int csi2_data_lanes; | |
| 173 | + struct max9286_source sources[MAX9286_NUM_GMSL]; | |
| 174 | + struct v4l2_async_notifier notifier; | |
| 175 | +}; | |
| 176 | + | |
| 177 | +static struct max9286_source *next_source(struct max9286_priv *priv, | |
| 178 | + struct max9286_source *source) | |
| 179 | +{ | |
| 180 | + if (!source) | |
| 181 | + source = &priv->sources[0]; | |
| 182 | + else | |
| 183 | + source++; | |
| 184 | + | |
| 185 | + for (; source < &priv->sources[MAX9286_NUM_GMSL]; source++) { | |
| 186 | + if (source->fwnode) | |
| 187 | + return source; | |
| 188 | + } | |
| 189 | + | |
| 190 | + return NULL; | |
| 191 | +} | |
| 192 | + | |
| 193 | +#define for_each_source(priv, source) \ | |
| 194 | + for ((source) = NULL; ((source) = next_source((priv), (source))); ) | |
| 195 | + | |
| 196 | +#define to_index(priv, source) ((source) - &(priv)->sources[0]) | |
| 197 | + | |
| 198 | +static inline struct max9286_priv *sd_to_max9286(struct v4l2_subdev *sd) | |
| 199 | +{ | |
| 200 | + return container_of(sd, struct max9286_priv, sd); | |
| 201 | +} | |
| 202 | + | |
| 203 | +/* ----------------------------------------------------------------------------- | |
| 204 | + * I2C IO | |
| 205 | + */ | |
| 206 | + | |
| 207 | +static int max9286_read(struct max9286_priv *priv, u8 reg) | |
| 208 | +{ | |
| 209 | + int ret; | |
| 210 | + | |
| 211 | + ret = i2c_smbus_read_byte_data(priv->client, reg); | |
| 212 | + if (ret < 0) | |
| 213 | + dev_err(&priv->client->dev, | |
| 214 | + "%s: register 0x%02x read failed (%d)\n", | |
| 215 | + __func__, reg, ret); | |
| 216 | + | |
| 217 | + return ret; | |
| 218 | +} | |
| 219 | + | |
| 220 | +static int max9286_write(struct max9286_priv *priv, u8 reg, u8 val) | |
| 221 | +{ | |
| 222 | + int ret; | |
| 223 | + | |
| 224 | + ret = i2c_smbus_write_byte_data(priv->client, reg, val); | |
| 225 | + if (ret < 0) | |
| 226 | + dev_err(&priv->client->dev, | |
| 227 | + "%s: register 0x%02x write failed (%d)\n", | |
| 228 | + __func__, reg, ret); | |
| 229 | + | |
| 230 | + return ret; | |
| 231 | +} | |
| 232 | + | |
| 233 | +/* ----------------------------------------------------------------------------- | |
| 234 | + * I2C Multiplexer | |
| 235 | + */ | |
| 236 | + | |
| 237 | +static void max9286_i2c_mux_configure(struct max9286_priv *priv, u8 conf) | |
| 238 | +{ | |
| 239 | + max9286_write(priv, 0x0a, conf); | |
| 240 | + | |
| 241 | + /* | |
| 242 | + * We must sleep after any change to the forward or reverse channel | |
| 243 | + * configuration. | |
| 244 | + */ | |
| 245 | + usleep_range(3000, 5000); | |
| 246 | +} | |
| 247 | + | |
| 248 | +static void max9286_i2c_mux_open(struct max9286_priv *priv) | |
| 249 | +{ | |
| 250 | + /* Open all channels on the MAX9286 */ | |
| 251 | + max9286_i2c_mux_configure(priv, 0xff); | |
| 252 | + | |
| 253 | + priv->mux_open = true; | |
| 254 | +} | |
| 255 | + | |
| 256 | +static void max9286_i2c_mux_close(struct max9286_priv *priv) | |
| 257 | +{ | |
| 258 | + /* | |
| 259 | + * Ensure that both the forward and reverse channel are disabled on the | |
| 260 | + * mux, and that the channel ID is invalidated to ensure we reconfigure | |
| 261 | + * on the next max9286_i2c_mux_select() call. | |
| 262 | + */ | |
| 263 | + max9286_i2c_mux_configure(priv, 0x00); | |
| 264 | + | |
| 265 | + priv->mux_open = false; | |
| 266 | + priv->mux_channel = -1; | |
| 267 | +} | |
| 268 | + | |
| 269 | +static int max9286_i2c_mux_select(struct i2c_mux_core *muxc, u32 chan) | |
| 270 | +{ | |
| 271 | + struct max9286_priv *priv = i2c_mux_priv(muxc); | |
| 272 | + | |
| 273 | + /* Channel select is disabled when configured in the opened state. */ | |
| 274 | + if (priv->mux_open) | |
| 275 | + return 0; | |
| 276 | + | |
| 277 | + if (priv->mux_channel == chan) | |
| 278 | + return 0; | |
| 279 | + | |
| 280 | + priv->mux_channel = chan; | |
| 281 | + | |
| 282 | + max9286_i2c_mux_configure(priv, | |
| 283 | + MAX9286_FWDCCEN(chan) | | |
| 284 | + MAX9286_REVCCEN(chan)); | |
| 285 | + | |
| 286 | + return 0; | |
| 287 | +} | |
| 288 | + | |
| 289 | +static int max9286_i2c_mux_init(struct max9286_priv *priv) | |
| 290 | +{ | |
| 291 | + struct max9286_source *source; | |
| 292 | + int ret; | |
| 293 | + | |
| 294 | + if (!i2c_check_functionality(priv->client->adapter, | |
| 295 | + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) | |
| 296 | + return -ENODEV; | |
| 297 | + | |
| 298 | + priv->mux = i2c_mux_alloc(priv->client->adapter, &priv->client->dev, | |
| 299 | + priv->nsources, 0, I2C_MUX_LOCKED, | |
| 300 | + max9286_i2c_mux_select, NULL); | |
| 301 | + if (!priv->mux) | |
| 302 | + return -ENOMEM; | |
| 303 | + | |
| 304 | + priv->mux->priv = priv; | |
| 305 | + | |
| 306 | + for_each_source(priv, source) { | |
| 307 | + unsigned int index = to_index(priv, source); | |
| 308 | + | |
| 309 | + ret = i2c_mux_add_adapter(priv->mux, 0, index, 0); | |
| 310 | + if (ret < 0) | |
| 311 | + goto error; | |
| 312 | + } | |
| 313 | + | |
| 314 | + return 0; | |
| 315 | + | |
| 316 | +error: | |
| 317 | + i2c_mux_del_adapters(priv->mux); | |
| 318 | + return ret; | |
| 319 | +} | |
| 320 | + | |
| 321 | +static void max9286_configure_i2c(struct max9286_priv *priv, bool localack) | |
| 322 | +{ | |
| 323 | + u8 config = MAX9286_I2CSLVSH_469NS_234NS | MAX9286_I2CSLVTO_1024US | | |
| 324 | + MAX9286_I2CMSTBT_105KBPS; | |
| 325 | + | |
| 326 | + if (localack) | |
| 327 | + config |= MAX9286_I2CLOCACK; | |
| 328 | + | |
| 329 | + max9286_write(priv, 0x34, config); | |
| 330 | + usleep_range(3000, 5000); | |
| 331 | +} | |
| 332 | + | |
| 333 | +/* | |
| 334 | + * max9286_check_video_links() - Make sure video links are detected and locked | |
| 335 | + * | |
| 336 | + * Performs safety checks on video link status. Make sure they are detected | |
| 337 | + * and all enabled links are locked. | |
| 338 | + * | |
| 339 | + * Returns 0 for success, -EIO for errors. | |
| 340 | + */ | |
| 341 | +static int max9286_check_video_links(struct max9286_priv *priv) | |
| 342 | +{ | |
| 343 | + unsigned int i; | |
| 344 | + int ret; | |
| 345 | + | |
| 346 | + /* | |
| 347 | + * Make sure valid video links are detected. | |
| 348 | + * The delay is not characterized in de-serializer manual, wait up | |
| 349 | + * to 5 ms. | |
| 350 | + */ | |
| 351 | + for (i = 0; i < 10; i++) { | |
| 352 | + ret = max9286_read(priv, 0x49); | |
| 353 | + if (ret < 0) | |
| 354 | + return -EIO; | |
| 355 | + | |
| 356 | + if ((ret & MAX9286_VIDEO_DETECT_MASK) == priv->source_mask) | |
| 357 | + break; | |
| 358 | + | |
| 359 | + usleep_range(350, 500); | |
| 360 | + } | |
| 361 | + | |
| 362 | + if (i == 10) { | |
| 363 | + dev_err(&priv->client->dev, | |
| 364 | + "Unable to detect video links: 0x%02x\n", ret); | |
| 365 | + return -EIO; | |
| 366 | + } | |
| 367 | + | |
| 368 | + /* Make sure all enabled links are locked (4ms max). */ | |
| 369 | + for (i = 0; i < 10; i++) { | |
| 370 | + ret = max9286_read(priv, 0x27); | |
| 371 | + if (ret < 0) | |
| 372 | + return -EIO; | |
| 373 | + | |
| 374 | + if (ret & MAX9286_LOCKED) | |
| 375 | + break; | |
| 376 | + | |
| 377 | + usleep_range(350, 450); | |
| 378 | + } | |
| 379 | + | |
| 380 | + if (i == 10) { | |
| 381 | + dev_err(&priv->client->dev, "Not all enabled links locked\n"); | |
| 382 | + return -EIO; | |
| 383 | + } | |
| 384 | + | |
| 385 | + return 0; | |
| 386 | +} | |
| 387 | + | |
| 388 | +/* | |
| 389 | + * max9286_check_config_link() - Detect and wait for configuration links | |
| 390 | + * | |
| 391 | + * Determine if the configuration channel is up and settled for a link. | |
| 392 | + * | |
| 393 | + * Returns 0 for success, -EIO for errors. | |
| 394 | + */ | |
| 395 | +static int max9286_check_config_link(struct max9286_priv *priv, | |
| 396 | + unsigned int source_mask) | |
| 397 | +{ | |
| 398 | + unsigned int conflink_mask = (source_mask & 0x0f) << 4; | |
| 399 | + unsigned int i; | |
| 400 | + int ret; | |
| 401 | + | |
| 402 | + /* | |
| 403 | + * Make sure requested configuration links are detected. | |
| 404 | + * The delay is not characterized in the chip manual: wait up | |
| 405 | + * to 5 milliseconds. | |
| 406 | + */ | |
| 407 | + for (i = 0; i < 10; i++) { | |
| 408 | + ret = max9286_read(priv, 0x49) & 0xf0; | |
| 409 | + if (ret < 0) | |
| 410 | + return -EIO; | |
| 411 | + | |
| 412 | + if (ret == conflink_mask) | |
| 413 | + break; | |
| 414 | + | |
| 415 | + usleep_range(350, 500); | |
| 416 | + } | |
| 417 | + | |
| 418 | + if (ret != conflink_mask) { | |
| 419 | + dev_err(&priv->client->dev, | |
| 420 | + "Unable to detect configuration links: 0x%02x expected 0x%02x\n", | |
| 421 | + ret, conflink_mask); | |
| 422 | + return -EIO; | |
| 423 | + } | |
| 424 | + | |
| 425 | + dev_info(&priv->client->dev, | |
| 426 | + "Successfully detected configuration links after %u loops: 0x%02x\n", | |
| 427 | + i, conflink_mask); | |
| 428 | + | |
| 429 | + return 0; | |
| 430 | +} | |
| 431 | + | |
| 432 | +/* ----------------------------------------------------------------------------- | |
| 433 | + * V4L2 Subdev | |
| 434 | + */ | |
| 435 | + | |
| 436 | +static int max9286_set_pixelrate(struct max9286_priv *priv) | |
| 437 | +{ | |
| 438 | + struct max9286_source *source = NULL; | |
| 439 | + u64 pixelrate = 0; | |
| 440 | + | |
| 441 | + for_each_source(priv, source) { | |
| 442 | + struct v4l2_ctrl *ctrl; | |
| 443 | + u64 source_rate = 0; | |
| 444 | + | |
| 445 | + /* Pixel rate is mandatory to be reported by sources. */ | |
| 446 | + ctrl = v4l2_ctrl_find(source->sd->ctrl_handler, | |
| 447 | + V4L2_CID_PIXEL_RATE); | |
| 448 | + if (!ctrl) { | |
| 449 | + pixelrate = 0; | |
| 450 | + break; | |
| 451 | + } | |
| 452 | + | |
| 453 | + /* All source must report the same pixel rate. */ | |
| 454 | + source_rate = v4l2_ctrl_g_ctrl_int64(ctrl); | |
| 455 | + if (!pixelrate) { | |
| 456 | + pixelrate = source_rate; | |
| 457 | + } else if (pixelrate != source_rate) { | |
| 458 | + dev_err(&priv->client->dev, | |
| 459 | + "Unable to calculate pixel rate\n"); | |
| 460 | + return -EINVAL; | |
| 461 | + } | |
| 462 | + } | |
| 463 | + | |
| 464 | + if (!pixelrate) { | |
| 465 | + dev_err(&priv->client->dev, | |
| 466 | + "No pixel rate control available in sources\n"); | |
| 467 | + return -EINVAL; | |
| 468 | + } | |
| 469 | + | |
| 470 | + /* | |
| 471 | + * The CSI-2 transmitter pixel rate is the single source rate multiplied | |
| 472 | + * by the number of available sources. | |
| 473 | + */ | |
| 474 | + return v4l2_ctrl_s_ctrl_int64(priv->pixelrate, | |
| 475 | + pixelrate * priv->nsources); | |
| 476 | +} | |
| 477 | + | |
| 478 | +static int max9286_notify_bound(struct v4l2_async_notifier *notifier, | |
| 479 | + struct v4l2_subdev *subdev, | |
| 480 | + struct v4l2_async_subdev *asd) | |
| 481 | +{ | |
| 482 | + struct max9286_priv *priv = sd_to_max9286(notifier->sd); | |
| 483 | + struct max9286_source *source = asd_to_max9286_source(asd); | |
| 484 | + unsigned int index = to_index(priv, source); | |
| 485 | + unsigned int src_pad; | |
| 486 | + int ret; | |
| 487 | + | |
| 488 | + ret = media_entity_get_fwnode_pad(&subdev->entity, | |
| 489 | + source->fwnode, | |
| 490 | + MEDIA_PAD_FL_SOURCE); | |
| 491 | + if (ret < 0) { | |
| 492 | + dev_err(&priv->client->dev, | |
| 493 | + "Failed to find pad for %s\n", subdev->name); | |
| 494 | + return ret; | |
| 495 | + } | |
| 496 | + | |
| 497 | + priv->bound_sources |= BIT(index); | |
| 498 | + source->sd = subdev; | |
| 499 | + src_pad = ret; | |
| 500 | + | |
| 501 | + ret = media_create_pad_link(&source->sd->entity, src_pad, | |
| 502 | + &priv->sd.entity, index, | |
| 503 | + MEDIA_LNK_FL_ENABLED | | |
| 504 | + MEDIA_LNK_FL_IMMUTABLE); | |
| 505 | + if (ret) { | |
| 506 | + dev_err(&priv->client->dev, | |
| 507 | + "Unable to link %s:%u -> %s:%u\n", | |
| 508 | + source->sd->name, src_pad, priv->sd.name, index); | |
| 509 | + return ret; | |
| 510 | + } | |
| 511 | + | |
| 512 | + dev_dbg(&priv->client->dev, "Bound %s pad: %u on index %u\n", | |
| 513 | + subdev->name, src_pad, index); | |
| 514 | + | |
| 515 | + /* | |
| 516 | + * We can only register v4l2_async_notifiers, which do not provide a | |
| 517 | + * means to register a complete callback. bound_sources allows us to | |
| 518 | + * identify when all remote serializers have completed their probe. | |
| 519 | + */ | |
| 520 | + if (priv->bound_sources != priv->source_mask) | |
| 521 | + return 0; | |
| 522 | + | |
| 523 | + /* | |
| 524 | + * All enabled sources have probed and enabled their reverse control | |
| 525 | + * channels: | |
| 526 | + * | |
| 527 | + * - Verify all configuration links are properly detected | |
| 528 | + * - Disable auto-ack as communication on the control channel are now | |
| 529 | + * stable. | |
| 530 | + */ | |
| 531 | + max9286_check_config_link(priv, priv->source_mask); | |
| 532 | + | |
| 533 | + /* | |
| 534 | + * Re-configure I2C with local acknowledge disabled after cameras have | |
| 535 | + * probed. | |
| 536 | + */ | |
| 537 | + max9286_configure_i2c(priv, false); | |
| 538 | + | |
| 539 | + return max9286_set_pixelrate(priv); | |
| 540 | +} | |
| 541 | + | |
| 542 | +static void max9286_notify_unbind(struct v4l2_async_notifier *notifier, | |
| 543 | + struct v4l2_subdev *subdev, | |
| 544 | + struct v4l2_async_subdev *asd) | |
| 545 | +{ | |
| 546 | + struct max9286_priv *priv = sd_to_max9286(notifier->sd); | |
| 547 | + struct max9286_source *source = asd_to_max9286_source(asd); | |
| 548 | + unsigned int index = to_index(priv, source); | |
| 549 | + | |
| 550 | + source->sd = NULL; | |
| 551 | + priv->bound_sources &= ~BIT(index); | |
| 552 | +} | |
| 553 | + | |
| 554 | +static const struct v4l2_async_notifier_operations max9286_notify_ops = { | |
| 555 | + .bound = max9286_notify_bound, | |
| 556 | + .unbind = max9286_notify_unbind, | |
| 557 | +}; | |
| 558 | + | |
| 559 | +static int max9286_v4l2_notifier_register(struct max9286_priv *priv) | |
| 560 | +{ | |
| 561 | + struct device *dev = &priv->client->dev; | |
| 562 | + struct max9286_source *source = NULL; | |
| 563 | + int ret; | |
| 564 | + | |
| 565 | + if (!priv->nsources) | |
| 566 | + return 0; | |
| 567 | + | |
| 568 | + v4l2_async_notifier_init(&priv->notifier); | |
| 569 | + | |
| 570 | + for_each_source(priv, source) { | |
| 571 | + unsigned int i = to_index(priv, source); | |
| 572 | + | |
| 573 | + source->asd.match_type = V4L2_ASYNC_MATCH_FWNODE; | |
| 574 | + source->asd.match.fwnode = source->fwnode; | |
| 575 | + | |
| 576 | + ret = v4l2_async_notifier_add_subdev(&priv->notifier, | |
| 577 | + &source->asd); | |
| 578 | + if (ret) { | |
| 579 | + dev_err(dev, "Failed to add subdev for source %d", i); | |
| 580 | + v4l2_async_notifier_cleanup(&priv->notifier); | |
| 581 | + return ret; | |
| 582 | + } | |
| 583 | + | |
| 584 | + /* | |
| 585 | + * Balance the reference counting handled through | |
| 586 | + * v4l2_async_notifier_cleanup() | |
| 587 | + */ | |
| 588 | + fwnode_handle_get(source->fwnode); | |
| 589 | + } | |
| 590 | + | |
| 591 | + priv->notifier.ops = &max9286_notify_ops; | |
| 592 | + | |
| 593 | + ret = v4l2_async_subdev_notifier_register(&priv->sd, &priv->notifier); | |
| 594 | + if (ret) { | |
| 595 | + dev_err(dev, "Failed to register subdev_notifier"); | |
| 596 | + v4l2_async_notifier_cleanup(&priv->notifier); | |
| 597 | + return ret; | |
| 598 | + } | |
| 599 | + | |
| 600 | + return 0; | |
| 601 | +} | |
| 602 | + | |
| 603 | +static void max9286_v4l2_notifier_unregister(struct max9286_priv *priv) | |
| 604 | +{ | |
| 605 | + if (!priv->nsources) | |
| 606 | + return; | |
| 607 | + | |
| 608 | + v4l2_async_notifier_unregister(&priv->notifier); | |
| 609 | + v4l2_async_notifier_cleanup(&priv->notifier); | |
| 610 | +} | |
| 611 | + | |
| 612 | +static int max9286_s_stream(struct v4l2_subdev *sd, int enable) | |
| 613 | +{ | |
| 614 | + struct max9286_priv *priv = sd_to_max9286(sd); | |
| 615 | + struct max9286_source *source; | |
| 616 | + unsigned int i; | |
| 617 | + bool sync = false; | |
| 618 | + int ret; | |
| 619 | + | |
| 620 | + if (enable) { | |
| 621 | + /* | |
| 622 | + * The frame sync between cameras is transmitted across the | |
| 623 | + * reverse channel as GPIO. We must open all channels while | |
| 624 | + * streaming to allow this synchronisation signal to be shared. | |
| 625 | + */ | |
| 626 | + max9286_i2c_mux_open(priv); | |
| 627 | + | |
| 628 | + /* Start all cameras. */ | |
| 629 | + for_each_source(priv, source) { | |
| 630 | + ret = v4l2_subdev_call(source->sd, video, s_stream, 1); | |
| 631 | + if (ret) | |
| 632 | + return ret; | |
| 633 | + } | |
| 634 | + | |
| 635 | + ret = max9286_check_video_links(priv); | |
| 636 | + if (ret) | |
| 637 | + return ret; | |
| 638 | + | |
| 639 | + /* | |
| 640 | + * Wait until frame synchronization is locked. | |
| 641 | + * | |
| 642 | + * Manual says frame sync locking should take ~6 VTS. | |
| 643 | + * From practical experience at least 8 are required. Give | |
| 644 | + * 12 complete frames time (~400ms at 30 fps) to achieve frame | |
| 645 | + * locking before returning error. | |
| 646 | + */ | |
| 647 | + for (i = 0; i < 40; i++) { | |
| 648 | + if (max9286_read(priv, 0x31) & MAX9286_FSYNC_LOCKED) { | |
| 649 | + sync = true; | |
| 650 | + break; | |
| 651 | + } | |
| 652 | + usleep_range(9000, 11000); | |
| 653 | + } | |
| 654 | + | |
| 655 | + if (!sync) { | |
| 656 | + dev_err(&priv->client->dev, | |
| 657 | + "Failed to get frame synchronization\n"); | |
| 658 | + return -EXDEV; /* Invalid cross-device link */ | |
| 659 | + } | |
| 660 | + | |
| 661 | + /* | |
| 662 | + * Enable CSI output, VC set according to link number. | |
| 663 | + * Bit 7 must be set (chip manual says it's 0 and reserved). | |
| 664 | + */ | |
| 665 | + max9286_write(priv, 0x15, 0x80 | MAX9286_VCTYPE | | |
| 666 | + MAX9286_CSIOUTEN | MAX9286_0X15_RESV); | |
| 667 | + } else { | |
| 668 | + max9286_write(priv, 0x15, MAX9286_VCTYPE | MAX9286_0X15_RESV); | |
| 669 | + | |
| 670 | + /* Stop all cameras. */ | |
| 671 | + for_each_source(priv, source) | |
| 672 | + v4l2_subdev_call(source->sd, video, s_stream, 0); | |
| 673 | + | |
| 674 | + max9286_i2c_mux_close(priv); | |
| 675 | + } | |
| 676 | + | |
| 677 | + return 0; | |
| 678 | +} | |
| 679 | + | |
| 680 | +static int max9286_enum_mbus_code(struct v4l2_subdev *sd, | |
| 681 | + struct v4l2_subdev_pad_config *cfg, | |
| 682 | + struct v4l2_subdev_mbus_code_enum *code) | |
| 683 | +{ | |
| 684 | + if (code->pad || code->index > 0) | |
| 685 | + return -EINVAL; | |
| 686 | + | |
| 687 | + code->code = MEDIA_BUS_FMT_UYVY8_1X16; | |
| 688 | + | |
| 689 | + return 0; | |
| 690 | +} | |
| 691 | + | |
| 692 | +static struct v4l2_mbus_framefmt * | |
| 693 | +max9286_get_pad_format(struct max9286_priv *priv, | |
| 694 | + struct v4l2_subdev_pad_config *cfg, | |
| 695 | + unsigned int pad, u32 which) | |
| 696 | +{ | |
| 697 | + switch (which) { | |
| 698 | + case V4L2_SUBDEV_FORMAT_TRY: | |
| 699 | + return v4l2_subdev_get_try_format(&priv->sd, cfg, pad); | |
| 700 | + case V4L2_SUBDEV_FORMAT_ACTIVE: | |
| 701 | + return &priv->fmt[pad]; | |
| 702 | + default: | |
| 703 | + return NULL; | |
| 704 | + } | |
| 705 | +} | |
| 706 | + | |
| 707 | +static int max9286_set_fmt(struct v4l2_subdev *sd, | |
| 708 | + struct v4l2_subdev_pad_config *cfg, | |
| 709 | + struct v4l2_subdev_format *format) | |
| 710 | +{ | |
| 711 | + struct max9286_priv *priv = sd_to_max9286(sd); | |
| 712 | + struct v4l2_mbus_framefmt *cfg_fmt; | |
| 713 | + | |
| 714 | + if (format->pad == MAX9286_SRC_PAD) | |
| 715 | + return -EINVAL; | |
| 716 | + | |
| 717 | + /* Refuse non YUV422 formats as we hardcode DT to 8 bit YUV422 */ | |
| 718 | + switch (format->format.code) { | |
| 719 | + case MEDIA_BUS_FMT_UYVY8_1X16: | |
| 720 | + case MEDIA_BUS_FMT_VYUY8_1X16: | |
| 721 | + case MEDIA_BUS_FMT_YUYV8_1X16: | |
| 722 | + case MEDIA_BUS_FMT_YVYU8_1X16: | |
| 723 | + break; | |
| 724 | + default: | |
| 725 | + format->format.code = MEDIA_BUS_FMT_UYVY8_1X16; | |
| 726 | + break; | |
| 727 | + } | |
| 728 | + | |
| 729 | + cfg_fmt = max9286_get_pad_format(priv, cfg, format->pad, format->which); | |
| 730 | + if (!cfg_fmt) | |
| 731 | + return -EINVAL; | |
| 732 | + | |
| 733 | + mutex_lock(&priv->mutex); | |
| 734 | + *cfg_fmt = format->format; | |
| 735 | + mutex_unlock(&priv->mutex); | |
| 736 | + | |
| 737 | + return 0; | |
| 738 | +} | |
| 739 | + | |
| 740 | +static int max9286_get_fmt(struct v4l2_subdev *sd, | |
| 741 | + struct v4l2_subdev_pad_config *cfg, | |
| 742 | + struct v4l2_subdev_format *format) | |
| 743 | +{ | |
| 744 | + struct max9286_priv *priv = sd_to_max9286(sd); | |
| 745 | + struct v4l2_mbus_framefmt *cfg_fmt; | |
| 746 | + unsigned int pad = format->pad; | |
| 747 | + | |
| 748 | + /* | |
| 749 | + * Multiplexed Stream Support: Support link validation by returning the | |
| 750 | + * format of the first bound link. All links must have the same format, | |
| 751 | + * as we do not support mixing and matching of cameras connected to the | |
| 752 | + * max9286. | |
| 753 | + */ | |
| 754 | + if (pad == MAX9286_SRC_PAD) | |
| 755 | + pad = __ffs(priv->bound_sources); | |
| 756 | + | |
| 757 | + cfg_fmt = max9286_get_pad_format(priv, cfg, pad, format->which); | |
| 758 | + if (!cfg_fmt) | |
| 759 | + return -EINVAL; | |
| 760 | + | |
| 761 | + mutex_lock(&priv->mutex); | |
| 762 | + format->format = *cfg_fmt; | |
| 763 | + mutex_unlock(&priv->mutex); | |
| 764 | + | |
| 765 | + return 0; | |
| 766 | +} | |
| 767 | + | |
| 768 | +static const struct v4l2_subdev_video_ops max9286_video_ops = { | |
| 769 | + .s_stream = max9286_s_stream, | |
| 770 | +}; | |
| 771 | + | |
| 772 | +static const struct v4l2_subdev_pad_ops max9286_pad_ops = { | |
| 773 | + .enum_mbus_code = max9286_enum_mbus_code, | |
| 774 | + .get_fmt = max9286_get_fmt, | |
| 775 | + .set_fmt = max9286_set_fmt, | |
| 776 | +}; | |
| 777 | + | |
| 778 | +static const struct v4l2_subdev_ops max9286_subdev_ops = { | |
| 779 | + .video = &max9286_video_ops, | |
| 780 | + .pad = &max9286_pad_ops, | |
| 781 | +}; | |
| 782 | + | |
| 783 | +static void max9286_init_format(struct v4l2_mbus_framefmt *fmt) | |
| 784 | +{ | |
| 785 | + fmt->width = 1280; | |
| 786 | + fmt->height = 800; | |
| 787 | + fmt->code = MEDIA_BUS_FMT_UYVY8_1X16; | |
| 788 | + fmt->colorspace = V4L2_COLORSPACE_SRGB; | |
| 789 | + fmt->field = V4L2_FIELD_NONE; | |
| 790 | + fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; | |
| 791 | + fmt->quantization = V4L2_QUANTIZATION_DEFAULT; | |
| 792 | + fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; | |
| 793 | +} | |
| 794 | + | |
| 795 | +static int max9286_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) | |
| 796 | +{ | |
| 797 | + struct v4l2_mbus_framefmt *format; | |
| 798 | + unsigned int i; | |
| 799 | + | |
| 800 | + for (i = 0; i < MAX9286_N_SINKS; i++) { | |
| 801 | + format = v4l2_subdev_get_try_format(subdev, fh->pad, i); | |
| 802 | + max9286_init_format(format); | |
| 803 | + } | |
| 804 | + | |
| 805 | + return 0; | |
| 806 | +} | |
| 807 | + | |
| 808 | +static const struct v4l2_subdev_internal_ops max9286_subdev_internal_ops = { | |
| 809 | + .open = max9286_open, | |
| 810 | +}; | |
| 811 | + | |
| 812 | +static int max9286_s_ctrl(struct v4l2_ctrl *ctrl) | |
| 813 | +{ | |
| 814 | + switch (ctrl->id) { | |
| 815 | + case V4L2_CID_PIXEL_RATE: | |
| 816 | + return 0; | |
| 817 | + default: | |
| 818 | + return -EINVAL; | |
| 819 | + } | |
| 820 | +} | |
| 821 | + | |
| 822 | +static const struct v4l2_ctrl_ops max9286_ctrl_ops = { | |
| 823 | + .s_ctrl = max9286_s_ctrl, | |
| 824 | +}; | |
| 825 | + | |
| 826 | +static int max9286_v4l2_register(struct max9286_priv *priv) | |
| 827 | +{ | |
| 828 | + struct device *dev = &priv->client->dev; | |
| 829 | + struct fwnode_handle *ep; | |
| 830 | + int ret; | |
| 831 | + int i; | |
| 832 | + | |
| 833 | + /* Register v4l2 async notifiers for connected Camera subdevices */ | |
| 834 | + ret = max9286_v4l2_notifier_register(priv); | |
| 835 | + if (ret) { | |
| 836 | + dev_err(dev, "Unable to register V4L2 async notifiers\n"); | |
| 837 | + return ret; | |
| 838 | + } | |
| 839 | + | |
| 840 | + /* Configure V4L2 for the MAX9286 itself */ | |
| 841 | + | |
| 842 | + for (i = 0; i < MAX9286_N_SINKS; i++) | |
| 843 | + max9286_init_format(&priv->fmt[i]); | |
| 844 | + | |
| 845 | + v4l2_i2c_subdev_init(&priv->sd, priv->client, &max9286_subdev_ops); | |
| 846 | + priv->sd.internal_ops = &max9286_subdev_internal_ops; | |
| 847 | + priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | |
| 848 | + | |
| 849 | + v4l2_ctrl_handler_init(&priv->ctrls, 1); | |
| 850 | + priv->pixelrate = v4l2_ctrl_new_std(&priv->ctrls, | |
| 851 | + &max9286_ctrl_ops, | |
| 852 | + V4L2_CID_PIXEL_RATE, | |
| 853 | + 1, INT_MAX, 1, 50000000); | |
| 854 | + | |
| 855 | + priv->sd.ctrl_handler = &priv->ctrls; | |
| 856 | + ret = priv->ctrls.error; | |
| 857 | + if (ret) | |
| 858 | + goto err_async; | |
| 859 | + | |
| 860 | + priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; | |
| 861 | + | |
| 862 | + priv->pads[MAX9286_SRC_PAD].flags = MEDIA_PAD_FL_SOURCE; | |
| 863 | + for (i = 0; i < MAX9286_SRC_PAD; i++) | |
| 864 | + priv->pads[i].flags = MEDIA_PAD_FL_SINK; | |
| 865 | + ret = media_entity_pads_init(&priv->sd.entity, MAX9286_N_PADS, | |
| 866 | + priv->pads); | |
| 867 | + if (ret) | |
| 868 | + goto err_async; | |
| 869 | + | |
| 870 | + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), MAX9286_SRC_PAD, | |
| 871 | + 0, 0); | |
| 872 | + if (!ep) { | |
| 873 | + dev_err(dev, "Unable to retrieve endpoint on \"port@4\"\n"); | |
| 874 | + ret = -ENOENT; | |
| 875 | + goto err_async; | |
| 876 | + } | |
| 877 | + priv->sd.fwnode = ep; | |
| 878 | + | |
| 879 | + ret = v4l2_async_register_subdev(&priv->sd); | |
| 880 | + if (ret < 0) { | |
| 881 | + dev_err(dev, "Unable to register subdevice\n"); | |
| 882 | + goto err_put_node; | |
| 883 | + } | |
| 884 | + | |
| 885 | + return 0; | |
| 886 | + | |
| 887 | +err_put_node: | |
| 888 | + fwnode_handle_put(ep); | |
| 889 | +err_async: | |
| 890 | + max9286_v4l2_notifier_unregister(priv); | |
| 891 | + | |
| 892 | + return ret; | |
| 893 | +} | |
| 894 | + | |
| 895 | +static void max9286_v4l2_unregister(struct max9286_priv *priv) | |
| 896 | +{ | |
| 897 | + fwnode_handle_put(priv->sd.fwnode); | |
| 898 | + v4l2_async_unregister_subdev(&priv->sd); | |
| 899 | + max9286_v4l2_notifier_unregister(priv); | |
| 900 | +} | |
| 901 | + | |
| 902 | +/* ----------------------------------------------------------------------------- | |
| 903 | + * Probe/Remove | |
| 904 | + */ | |
| 905 | + | |
| 906 | +static int max9286_setup(struct max9286_priv *priv) | |
| 907 | +{ | |
| 908 | + /* | |
| 909 | + * Link ordering values for all enabled links combinations. Orders must | |
| 910 | + * be assigned sequentially from 0 to the number of enabled links | |
| 911 | + * without leaving any hole for disabled links. We thus assign orders to | |
| 912 | + * enabled links first, and use the remaining order values for disabled | |
| 913 | + * links are all links must have a different order value; | |
| 914 | + */ | |
| 915 | + static const u8 link_order[] = { | |
| 916 | + (3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xxxx */ | |
| 917 | + (3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xxx0 */ | |
| 918 | + (3 << 6) | (2 << 4) | (0 << 2) | (1 << 0), /* xx0x */ | |
| 919 | + (3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xx10 */ | |
| 920 | + (3 << 6) | (0 << 4) | (2 << 2) | (1 << 0), /* x0xx */ | |
| 921 | + (3 << 6) | (1 << 4) | (2 << 2) | (0 << 0), /* x1x0 */ | |
| 922 | + (3 << 6) | (1 << 4) | (0 << 2) | (2 << 0), /* x10x */ | |
| 923 | + (3 << 6) | (1 << 4) | (1 << 2) | (0 << 0), /* x210 */ | |
| 924 | + (0 << 6) | (3 << 4) | (2 << 2) | (1 << 0), /* 0xxx */ | |
| 925 | + (1 << 6) | (3 << 4) | (2 << 2) | (0 << 0), /* 1xx0 */ | |
| 926 | + (1 << 6) | (3 << 4) | (0 << 2) | (2 << 0), /* 1x0x */ | |
| 927 | + (2 << 6) | (3 << 4) | (1 << 2) | (0 << 0), /* 2x10 */ | |
| 928 | + (1 << 6) | (0 << 4) | (3 << 2) | (2 << 0), /* 10xx */ | |
| 929 | + (2 << 6) | (1 << 4) | (3 << 2) | (0 << 0), /* 21x0 */ | |
| 930 | + (2 << 6) | (1 << 4) | (0 << 2) | (3 << 0), /* 210x */ | |
| 931 | + (3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* 3210 */ | |
| 932 | + }; | |
| 933 | + | |
| 934 | + /* | |
| 935 | + * Set the I2C bus speed. | |
| 936 | + * | |
| 937 | + * Enable I2C Local Acknowledge during the probe sequences of the camera | |
| 938 | + * only. This should be disabled after the mux is initialised. | |
| 939 | + */ | |
| 940 | + max9286_configure_i2c(priv, true); | |
| 941 | + | |
| 942 | + /* | |
| 943 | + * Reverse channel setup. | |
| 944 | + * | |
| 945 | + * - Enable custom reverse channel configuration (through register 0x3f) | |
| 946 | + * and set the first pulse length to 35 clock cycles. | |
| 947 | + * - Increase the reverse channel amplitude to 170mV to accommodate the | |
| 948 | + * high threshold enabled by the serializer driver. | |
| 949 | + */ | |
| 950 | + max9286_write(priv, 0x3f, MAX9286_EN_REV_CFG | MAX9286_REV_FLEN(35)); | |
| 951 | + max9286_write(priv, 0x3b, MAX9286_REV_TRF(1) | MAX9286_REV_AMP(70) | | |
| 952 | + MAX9286_REV_AMP_X); | |
| 953 | + usleep_range(2000, 2500); | |
| 954 | + | |
| 955 | + /* | |
| 956 | + * Enable GMSL links, mask unused ones and autodetect link | |
| 957 | + * used as CSI clock source. | |
| 958 | + */ | |
| 959 | + max9286_write(priv, 0x00, MAX9286_MSTLINKSEL_AUTO | priv->route_mask); | |
| 960 | + max9286_write(priv, 0x0b, link_order[priv->route_mask]); | |
| 961 | + max9286_write(priv, 0x69, (0xf & ~priv->route_mask)); | |
| 962 | + | |
| 963 | + /* | |
| 964 | + * Video format setup: | |
| 965 | + * Disable CSI output, VC is set according to Link number. | |
| 966 | + */ | |
| 967 | + max9286_write(priv, 0x15, MAX9286_VCTYPE | MAX9286_0X15_RESV); | |
| 968 | + | |
| 969 | + /* Enable CSI-2 Lane D0-D3 only, DBL mode, YUV422 8-bit. */ | |
| 970 | + max9286_write(priv, 0x12, MAX9286_CSIDBL | MAX9286_DBL | | |
| 971 | + MAX9286_CSILANECNT(priv->csi2_data_lanes) | | |
| 972 | + MAX9286_DATATYPE_YUV422_8BIT); | |
| 973 | + | |
| 974 | + /* Automatic: FRAMESYNC taken from the slowest Link. */ | |
| 975 | + max9286_write(priv, 0x01, MAX9286_FSYNCMODE_INT_HIZ | | |
| 976 | + MAX9286_FSYNCMETH_AUTO); | |
| 977 | + | |
| 978 | + /* Enable HS/VS encoding, use D14/15 for HS/VS, invert VS. */ | |
| 979 | + max9286_write(priv, 0x0c, MAX9286_HVEN | MAX9286_INVVS | | |
| 980 | + MAX9286_HVSRC_D14); | |
| 981 | + | |
| 982 | + /* | |
| 983 | + * The overlap window seems to provide additional validation by tracking | |
| 984 | + * the delay between vsync and frame sync, generating an error if the | |
| 985 | + * delay is bigger than the programmed window, though it's not yet clear | |
| 986 | + * what value should be set. | |
| 987 | + * | |
| 988 | + * As it's an optional value and can be disabled, we do so by setting | |
| 989 | + * a 0 overlap value. | |
| 990 | + */ | |
| 991 | + max9286_write(priv, 0x63, 0); | |
| 992 | + max9286_write(priv, 0x64, 0); | |
| 993 | + | |
| 994 | + /* | |
| 995 | + * Wait for 2ms to allow the link to resynchronize after the | |
| 996 | + * configuration change. | |
| 997 | + */ | |
| 998 | + usleep_range(2000, 5000); | |
| 999 | + | |
| 1000 | + return 0; | |
| 1001 | +} | |
| 1002 | + | |
| 1003 | +static void max9286_gpio_set(struct gpio_chip *chip, | |
| 1004 | + unsigned int offset, int value) | |
| 1005 | +{ | |
| 1006 | + struct max9286_priv *priv = gpiochip_get_data(chip); | |
| 1007 | + | |
| 1008 | + if (value) | |
| 1009 | + priv->gpio_state |= BIT(offset); | |
| 1010 | + else | |
| 1011 | + priv->gpio_state &= ~BIT(offset); | |
| 1012 | + | |
| 1013 | + max9286_write(priv, 0x0f, MAX9286_0X0F_RESERVED | priv->gpio_state); | |
| 1014 | +} | |
| 1015 | + | |
| 1016 | +static int max9286_gpio_get(struct gpio_chip *chip, unsigned int offset) | |
| 1017 | +{ | |
| 1018 | + struct max9286_priv *priv = gpiochip_get_data(chip); | |
| 1019 | + | |
| 1020 | + return priv->gpio_state & BIT(offset); | |
| 1021 | +} | |
| 1022 | + | |
| 1023 | +static int max9286_register_gpio(struct max9286_priv *priv) | |
| 1024 | +{ | |
| 1025 | + struct device *dev = &priv->client->dev; | |
| 1026 | + struct gpio_chip *gpio = &priv->gpio; | |
| 1027 | + int ret; | |
| 1028 | + | |
| 1029 | + /* Configure the GPIO */ | |
| 1030 | + gpio->label = dev_name(dev); | |
| 1031 | + gpio->parent = dev; | |
| 1032 | + gpio->owner = THIS_MODULE; | |
| 1033 | + gpio->of_node = dev->of_node; | |
| 1034 | + gpio->ngpio = 2; | |
| 1035 | + gpio->base = -1; | |
| 1036 | + gpio->set = max9286_gpio_set; | |
| 1037 | + gpio->get = max9286_gpio_get; | |
| 1038 | + gpio->can_sleep = true; | |
| 1039 | + | |
| 1040 | + /* GPIO values default to high */ | |
| 1041 | + priv->gpio_state = BIT(0) | BIT(1); | |
| 1042 | + | |
| 1043 | + ret = devm_gpiochip_add_data(dev, gpio, priv); | |
| 1044 | + if (ret) | |
| 1045 | + dev_err(dev, "Unable to create gpio_chip\n"); | |
| 1046 | + | |
| 1047 | + return ret; | |
| 1048 | +} | |
| 1049 | + | |
| 1050 | +static int max9286_init(struct device *dev) | |
| 1051 | +{ | |
| 1052 | + struct max9286_priv *priv; | |
| 1053 | + struct i2c_client *client; | |
| 1054 | + int ret; | |
| 1055 | + | |
| 1056 | + client = to_i2c_client(dev); | |
| 1057 | + priv = i2c_get_clientdata(client); | |
| 1058 | + | |
| 1059 | + /* Enable the bus power. */ | |
| 1060 | + ret = regulator_enable(priv->regulator); | |
| 1061 | + if (ret < 0) { | |
| 1062 | + dev_err(&client->dev, "Unable to turn PoC on\n"); | |
| 1063 | + return ret; | |
| 1064 | + } | |
| 1065 | + | |
| 1066 | + ret = max9286_setup(priv); | |
| 1067 | + if (ret) { | |
| 1068 | + dev_err(dev, "Unable to setup max9286\n"); | |
| 1069 | + goto err_regulator; | |
| 1070 | + } | |
| 1071 | + | |
| 1072 | + /* | |
| 1073 | + * Register all V4L2 interactions for the MAX9286 and notifiers for | |
| 1074 | + * any subdevices connected. | |
| 1075 | + */ | |
| 1076 | + ret = max9286_v4l2_register(priv); | |
| 1077 | + if (ret) { | |
| 1078 | + dev_err(dev, "Failed to register with V4L2\n"); | |
| 1079 | + goto err_regulator; | |
| 1080 | + } | |
| 1081 | + | |
| 1082 | + ret = max9286_i2c_mux_init(priv); | |
| 1083 | + if (ret) { | |
| 1084 | + dev_err(dev, "Unable to initialize I2C multiplexer\n"); | |
| 1085 | + goto err_v4l2_register; | |
| 1086 | + } | |
| 1087 | + | |
| 1088 | + /* Leave the mux channels disabled until they are selected. */ | |
| 1089 | + max9286_i2c_mux_close(priv); | |
| 1090 | + | |
| 1091 | + return 0; | |
| 1092 | + | |
| 1093 | +err_v4l2_register: | |
| 1094 | + max9286_v4l2_unregister(priv); | |
| 1095 | +err_regulator: | |
| 1096 | + regulator_disable(priv->regulator); | |
| 1097 | + | |
| 1098 | + return ret; | |
| 1099 | +} | |
| 1100 | + | |
| 1101 | +static void max9286_cleanup_dt(struct max9286_priv *priv) | |
| 1102 | +{ | |
| 1103 | + struct max9286_source *source; | |
| 1104 | + | |
| 1105 | + for_each_source(priv, source) { | |
| 1106 | + fwnode_handle_put(source->fwnode); | |
| 1107 | + source->fwnode = NULL; | |
| 1108 | + } | |
| 1109 | +} | |
| 1110 | + | |
| 1111 | +static int max9286_parse_dt(struct max9286_priv *priv) | |
| 1112 | +{ | |
| 1113 | + struct device *dev = &priv->client->dev; | |
| 1114 | + struct device_node *i2c_mux; | |
| 1115 | + struct device_node *node = NULL; | |
| 1116 | + unsigned int i2c_mux_mask = 0; | |
| 1117 | + | |
| 1118 | + /* Balance the of_node_put() performed by of_find_node_by_name(). */ | |
| 1119 | + of_node_get(dev->of_node); | |
| 1120 | + i2c_mux = of_find_node_by_name(dev->of_node, "i2c-mux"); | |
| 1121 | + if (!i2c_mux) { | |
| 1122 | + dev_err(dev, "Failed to find i2c-mux node\n"); | |
| 1123 | + return -EINVAL; | |
| 1124 | + } | |
| 1125 | + | |
| 1126 | + /* Identify which i2c-mux channels are enabled */ | |
| 1127 | + for_each_child_of_node(i2c_mux, node) { | |
| 1128 | + u32 id = 0; | |
| 1129 | + | |
| 1130 | + of_property_read_u32(node, "reg", &id); | |
| 1131 | + if (id >= MAX9286_NUM_GMSL) | |
| 1132 | + continue; | |
| 1133 | + | |
| 1134 | + if (!of_device_is_available(node)) { | |
| 1135 | + dev_dbg(dev, "Skipping disabled I2C bus port %u\n", id); | |
| 1136 | + continue; | |
| 1137 | + } | |
| 1138 | + | |
| 1139 | + i2c_mux_mask |= BIT(id); | |
| 1140 | + } | |
| 1141 | + of_node_put(node); | |
| 1142 | + of_node_put(i2c_mux); | |
| 1143 | + | |
| 1144 | + /* Parse the endpoints */ | |
| 1145 | + for_each_endpoint_of_node(dev->of_node, node) { | |
| 1146 | + struct max9286_source *source; | |
| 1147 | + struct of_endpoint ep; | |
| 1148 | + | |
| 1149 | + of_graph_parse_endpoint(node, &ep); | |
| 1150 | + dev_dbg(dev, "Endpoint %pOF on port %d", | |
| 1151 | + ep.local_node, ep.port); | |
| 1152 | + | |
| 1153 | + if (ep.port > MAX9286_NUM_GMSL) { | |
| 1154 | + dev_err(dev, "Invalid endpoint %s on port %d", | |
| 1155 | + of_node_full_name(ep.local_node), ep.port); | |
| 1156 | + continue; | |
| 1157 | + } | |
| 1158 | + | |
| 1159 | + /* For the source endpoint just parse the bus configuration. */ | |
| 1160 | + if (ep.port == MAX9286_SRC_PAD) { | |
| 1161 | + struct v4l2_fwnode_endpoint vep = { | |
| 1162 | + .bus_type = V4L2_MBUS_CSI2_DPHY | |
| 1163 | + }; | |
| 1164 | + int ret; | |
| 1165 | + | |
| 1166 | + ret = v4l2_fwnode_endpoint_parse( | |
| 1167 | + of_fwnode_handle(node), &vep); | |
| 1168 | + if (ret) { | |
| 1169 | + of_node_put(node); | |
| 1170 | + return ret; | |
| 1171 | + } | |
| 1172 | + | |
| 1173 | + priv->csi2_data_lanes = | |
| 1174 | + vep.bus.mipi_csi2.num_data_lanes; | |
| 1175 | + | |
| 1176 | + continue; | |
| 1177 | + } | |
| 1178 | + | |
| 1179 | + /* Skip if the corresponding GMSL link is unavailable. */ | |
| 1180 | + if (!(i2c_mux_mask & BIT(ep.port))) | |
| 1181 | + continue; | |
| 1182 | + | |
| 1183 | + if (priv->sources[ep.port].fwnode) { | |
| 1184 | + dev_err(dev, | |
| 1185 | + "Multiple port endpoints are not supported: %d", | |
| 1186 | + ep.port); | |
| 1187 | + | |
| 1188 | + continue; | |
| 1189 | + } | |
| 1190 | + | |
| 1191 | + source = &priv->sources[ep.port]; | |
| 1192 | + source->fwnode = fwnode_graph_get_remote_endpoint( | |
| 1193 | + of_fwnode_handle(node)); | |
| 1194 | + if (!source->fwnode) { | |
| 1195 | + dev_err(dev, | |
| 1196 | + "Endpoint %pOF has no remote endpoint connection\n", | |
| 1197 | + ep.local_node); | |
| 1198 | + | |
| 1199 | + continue; | |
| 1200 | + } | |
| 1201 | + | |
| 1202 | + priv->source_mask |= BIT(ep.port); | |
| 1203 | + priv->nsources++; | |
| 1204 | + } | |
| 1205 | + of_node_put(node); | |
| 1206 | + | |
| 1207 | + priv->route_mask = priv->source_mask; | |
| 1208 | + | |
| 1209 | + return 0; | |
| 1210 | +} | |
| 1211 | + | |
| 1212 | +static int max9286_probe(struct i2c_client *client) | |
| 1213 | +{ | |
| 1214 | + struct max9286_priv *priv; | |
| 1215 | + int ret; | |
| 1216 | + | |
| 1217 | + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); | |
| 1218 | + if (!priv) | |
| 1219 | + return -ENOMEM; | |
| 1220 | + | |
| 1221 | + mutex_init(&priv->mutex); | |
| 1222 | + | |
| 1223 | + priv->client = client; | |
| 1224 | + i2c_set_clientdata(client, priv); | |
| 1225 | + | |
| 1226 | + priv->gpiod_pwdn = devm_gpiod_get_optional(&client->dev, "enable", | |
| 1227 | + GPIOD_OUT_HIGH); | |
| 1228 | + if (IS_ERR(priv->gpiod_pwdn)) | |
| 1229 | + return PTR_ERR(priv->gpiod_pwdn); | |
| 1230 | + | |
| 1231 | + gpiod_set_consumer_name(priv->gpiod_pwdn, "max9286-pwdn"); | |
| 1232 | + gpiod_set_value_cansleep(priv->gpiod_pwdn, 1); | |
| 1233 | + | |
| 1234 | + /* Wait at least 4ms before the I2C lines latch to the address */ | |
| 1235 | + if (priv->gpiod_pwdn) | |
| 1236 | + usleep_range(4000, 5000); | |
| 1237 | + | |
| 1238 | + /* | |
| 1239 | + * The MAX9286 starts by default with all ports enabled, we disable all | |
| 1240 | + * ports early to ensure that all channels are disabled if we error out | |
| 1241 | + * and keep the bus consistent. | |
| 1242 | + */ | |
| 1243 | + max9286_i2c_mux_close(priv); | |
| 1244 | + | |
| 1245 | + /* | |
| 1246 | + * The MAX9286 initialises with auto-acknowledge enabled by default. | |
| 1247 | + * This can be invasive to other transactions on the same bus, so | |
| 1248 | + * disable it early. It will be enabled only as and when needed. | |
| 1249 | + */ | |
| 1250 | + max9286_configure_i2c(priv, false); | |
| 1251 | + | |
| 1252 | + ret = max9286_register_gpio(priv); | |
| 1253 | + if (ret) | |
| 1254 | + goto err_powerdown; | |
| 1255 | + | |
| 1256 | + priv->regulator = devm_regulator_get(&client->dev, "poc"); | |
| 1257 | + if (IS_ERR(priv->regulator)) { | |
| 1258 | + if (PTR_ERR(priv->regulator) != -EPROBE_DEFER) | |
| 1259 | + dev_err(&client->dev, | |
| 1260 | + "Unable to get PoC regulator (%ld)\n", | |
| 1261 | + PTR_ERR(priv->regulator)); | |
| 1262 | + ret = PTR_ERR(priv->regulator); | |
| 1263 | + goto err_powerdown; | |
| 1264 | + } | |
| 1265 | + | |
| 1266 | + ret = max9286_parse_dt(priv); | |
| 1267 | + if (ret) | |
| 1268 | + goto err_powerdown; | |
| 1269 | + | |
| 1270 | + ret = max9286_init(&client->dev); | |
| 1271 | + if (ret < 0) | |
| 1272 | + goto err_cleanup_dt; | |
| 1273 | + | |
| 1274 | + return 0; | |
| 1275 | + | |
| 1276 | +err_cleanup_dt: | |
| 1277 | + max9286_cleanup_dt(priv); | |
| 1278 | +err_powerdown: | |
| 1279 | + gpiod_set_value_cansleep(priv->gpiod_pwdn, 0); | |
| 1280 | + | |
| 1281 | + return ret; | |
| 1282 | +} | |
| 1283 | + | |
| 1284 | +static int max9286_remove(struct i2c_client *client) | |
| 1285 | +{ | |
| 1286 | + struct max9286_priv *priv = i2c_get_clientdata(client); | |
| 1287 | + | |
| 1288 | + i2c_mux_del_adapters(priv->mux); | |
| 1289 | + | |
| 1290 | + max9286_v4l2_unregister(priv); | |
| 1291 | + | |
| 1292 | + regulator_disable(priv->regulator); | |
| 1293 | + | |
| 1294 | + gpiod_set_value_cansleep(priv->gpiod_pwdn, 0); | |
| 1295 | + | |
| 1296 | + max9286_cleanup_dt(priv); | |
| 1297 | + | |
| 1298 | + return 0; | |
| 1299 | +} | |
| 1300 | + | |
| 1301 | +static const struct of_device_id max9286_dt_ids[] = { | |
| 1302 | + { .compatible = "maxim,max9286" }, | |
| 1303 | + {}, | |
| 1304 | +}; | |
| 1305 | +MODULE_DEVICE_TABLE(of, max9286_dt_ids); | |
| 1306 | + | |
| 1307 | +static struct i2c_driver max9286_i2c_driver = { | |
| 1308 | + .driver = { | |
| 1309 | + .name = "max9286", | |
| 1310 | + .of_match_table = of_match_ptr(max9286_dt_ids), | |
| 1311 | + }, | |
| 1312 | + .probe_new = max9286_probe, | |
| 1313 | + .remove = max9286_remove, | |
| 1314 | +}; | |
| 1315 | + | |
| 1316 | +module_i2c_driver(max9286_i2c_driver); | |
| 1317 | + | |
| 1318 | +MODULE_DESCRIPTION("Maxim MAX9286 GMSL Deserializer Driver"); | |
| 1319 | +MODULE_AUTHOR("Jacopo Mondi, Kieran Bingham, Laurent Pinchart, Niklas Söderlund, Vladimir Barinov"); | |
| 1320 | +MODULE_LICENSE("GPL"); | |
| @@ -7,6 +7,8 @@ · 6 unmodified lines | ||
| 7 | 7 | #include <linux/i2c.h> |
| 8 | 8 | #include <linux/module.h> |
| 9 | 9 | #include <linux/pm_runtime.h> |
| 10 | +#include <linux/nvmem-provider.h> | |
| 11 | +#include <linux/regmap.h> | |
| 10 | 12 | #include <media/v4l2-ctrls.h> |
| 11 | 13 | #include <media/v4l2-device.h> |
| 12 | 14 | #include <media/v4l2-fwnode.h> |
| @@ -59,6 +61,21 @@ · 46 unmodified lines | ||
| 59 | 61 | #define OV2740_TEST_PATTERN_ENABLE BIT(7) |
| 60 | 62 | #define OV2740_TEST_PATTERN_BAR_SHIFT 2 |
| 61 | 63 | |
| 64 | +/* ISP CTRL00 */ | |
| 65 | +#define OV2740_REG_ISP_CTRL00 0x5000 | |
| 66 | +/* ISP CTRL01 */ | |
| 67 | +#define OV2740_REG_ISP_CTRL01 0x5001 | |
| 68 | +/* Customer Addresses: 0x7010 - 0x710F */ | |
| 69 | +#define CUSTOMER_USE_OTP_SIZE 0x100 | |
| 70 | +/* OTP registers from sensor */ | |
| 71 | +#define OV2740_REG_OTP_CUSTOMER 0x7010 | |
| 72 | + | |
| 73 | +struct nvm_data { | |
| 74 | + char *nvm_buffer; | |
| 75 | + struct nvmem_device *nvmem; | |
| 76 | + struct regmap *regmap; | |
| 77 | +}; | |
| 78 | + | |
| 62 | 79 | enum { |
| 63 | 80 | OV2740_LINK_FREQ_360MHZ_INDEX, |
| 64 | 81 | }; |
| @@ -915,6 +932,130 @@ static int ov2740_remove(struct i2c_client *client) · 850 unmodified lines | ||
| 915 | 932 | return 0; |
| 916 | 933 | } |
| 917 | 934 | |
| 935 | +static int ov2740_load_otp_data(struct i2c_client *client, struct nvm_data *nvm) | |
| 936 | +{ | |
| 937 | + struct ov2740 *ov2740 = to_ov2740(i2c_get_clientdata(client)); | |
| 938 | + u32 isp_ctrl00 = 0; | |
| 939 | + u32 isp_ctrl01 = 0; | |
| 940 | + int ret; | |
| 941 | + | |
| 942 | + ret = ov2740_read_reg(ov2740, OV2740_REG_ISP_CTRL00, 1, &isp_ctrl00); | |
| 943 | + if (ret) { | |
| 944 | + dev_err(&client->dev, "failed to read ISP CTRL00\n"); | |
| 945 | + goto exit; | |
| 946 | + } | |
| 947 | + ret = ov2740_read_reg(ov2740, OV2740_REG_ISP_CTRL01, 1, &isp_ctrl01); | |
| 948 | + if (ret) { | |
| 949 | + dev_err(&client->dev, "failed to read ISP CTRL01\n"); | |
| 950 | + goto exit; | |
| 951 | + } | |
| 952 | + | |
| 953 | + /* Clear bit 5 of ISP CTRL00 */ | |
| 954 | + ret = ov2740_write_reg(ov2740, OV2740_REG_ISP_CTRL00, 1, | |
| 955 | + isp_ctrl00 & ~BIT(5)); | |
| 956 | + if (ret) { | |
| 957 | + dev_err(&client->dev, "failed to write ISP CTRL00\n"); | |
| 958 | + goto exit; | |
| 959 | + } | |
| 960 | + | |
| 961 | + /* Clear bit 7 of ISP CTRL01 */ | |
| 962 | + ret = ov2740_write_reg(ov2740, OV2740_REG_ISP_CTRL01, 1, | |
| 963 | + isp_ctrl01 & ~BIT(7)); | |
| 964 | + if (ret) { | |
| 965 | + dev_err(&client->dev, "failed to write ISP CTRL01\n"); | |
| 966 | + goto exit; | |
| 967 | + } | |
| 968 | + | |
| 969 | + ret = ov2740_write_reg(ov2740, OV2740_REG_MODE_SELECT, 1, | |
| 970 | + OV2740_MODE_STREAMING); | |
| 971 | + if (ret) { | |
| 972 | + dev_err(&client->dev, "failed to start streaming\n"); | |
| 973 | + goto exit; | |
| 974 | + } | |
| 975 | + | |
| 976 | + /* | |
| 977 | + * Users are not allowed to access OTP-related registers and memory | |
| 978 | + * during the 20 ms period after streaming starts (0x100 = 0x01). | |
| 979 | + */ | |
| 980 | + msleep(20); | |
| 981 | + | |
| 982 | + ret = regmap_bulk_read(nvm->regmap, OV2740_REG_OTP_CUSTOMER, | |
| 983 | + nvm->nvm_buffer, CUSTOMER_USE_OTP_SIZE); | |
| 984 | + if (ret) { | |
| 985 | + dev_err(&client->dev, "failed to read OTP data, ret %d\n", ret); | |
| 986 | + goto exit; | |
| 987 | + } | |
| 988 | + | |
| 989 | + ov2740_write_reg(ov2740, OV2740_REG_MODE_SELECT, 1, | |
| 990 | + OV2740_MODE_STANDBY); | |
| 991 | + ov2740_write_reg(ov2740, OV2740_REG_ISP_CTRL01, 1, isp_ctrl01); | |
| 992 | + ov2740_write_reg(ov2740, OV2740_REG_ISP_CTRL00, 1, isp_ctrl00); | |
| 993 | + | |
| 994 | +exit: | |
| 995 | + return ret; | |
| 996 | +} | |
| 997 | + | |
| 998 | +static int ov2740_nvmem_read(void *priv, unsigned int off, void *val, | |
| 999 | + size_t count) | |
| 1000 | +{ | |
| 1001 | + struct nvm_data *nvm = priv; | |
| 1002 | + | |
| 1003 | + memcpy(val, nvm->nvm_buffer + off, count); | |
| 1004 | + | |
| 1005 | + return 0; | |
| 1006 | +} | |
| 1007 | + | |
| 1008 | +static int ov2740_register_nvmem(struct i2c_client *client) | |
| 1009 | +{ | |
| 1010 | + struct nvm_data *nvm; | |
| 1011 | + struct regmap_config regmap_config = { }; | |
| 1012 | + struct nvmem_config nvmem_config = { }; | |
| 1013 | + struct regmap *regmap; | |
| 1014 | + struct device *dev = &client->dev; | |
| 1015 | + int ret = 0; | |
| 1016 | + | |
| 1017 | + nvm = devm_kzalloc(dev, sizeof(*nvm), GFP_KERNEL); | |
| 1018 | + if (!nvm) | |
| 1019 | + return -ENOMEM; | |
| 1020 | + | |
| 1021 | + regmap_config.val_bits = 8; | |
| 1022 | + regmap_config.reg_bits = 16; | |
| 1023 | + regmap_config.disable_locking = true; | |
| 1024 | + regmap = devm_regmap_init_i2c(client, ®map_config); | |
| 1025 | + if (IS_ERR(regmap)) | |
| 1026 | + return PTR_ERR(regmap); | |
| 1027 | + | |
| 1028 | + nvm->regmap = regmap; | |
| 1029 | + | |
| 1030 | + nvmem_config.name = dev_name(dev); | |
| 1031 | + nvmem_config.dev = dev; | |
| 1032 | + nvmem_config.read_only = true; | |
| 1033 | + nvmem_config.root_only = true; | |
| 1034 | + nvmem_config.owner = THIS_MODULE; | |
| 1035 | + nvmem_config.compat = true; | |
| 1036 | + nvmem_config.base_dev = dev; | |
| 1037 | + nvmem_config.reg_read = ov2740_nvmem_read; | |
| 1038 | + nvmem_config.reg_write = NULL; | |
| 1039 | + nvmem_config.priv = nvm; | |
| 1040 | + nvmem_config.stride = 1; | |
| 1041 | + nvmem_config.word_size = 1; | |
| 1042 | + nvmem_config.size = CUSTOMER_USE_OTP_SIZE; | |
| 1043 | + | |
| 1044 | + nvm->nvmem = devm_nvmem_register(dev, &nvmem_config); | |
| 1045 | + if (IS_ERR(nvm->nvmem)) | |
| 1046 | + return PTR_ERR(nvm->nvmem); | |
| 1047 | + | |
| 1048 | + nvm->nvm_buffer = devm_kzalloc(dev, CUSTOMER_USE_OTP_SIZE, GFP_KERNEL); | |
| 1049 | + if (!nvm->nvm_buffer) | |
| 1050 | + return -ENOMEM; | |
| 1051 | + | |
| 1052 | + ret = ov2740_load_otp_data(client, nvm); | |
| 1053 | + if (ret) | |
| 1054 | + dev_err(dev, "failed to load OTP data, ret %d\n", ret); | |
| 1055 | + | |
| 1056 | + return ret; | |
| 1057 | +} | |
| 1058 | + | |
| 918 | 1059 | static int ov2740_probe(struct i2c_client *client) |
| 919 | 1060 | { |
| 920 | 1061 | struct ov2740 *ov2740; |
| @@ -964,6 +1105,10 @@ static int ov2740_probe(struct i2c_client *client) · 43 unmodified lines | ||
| 964 | 1105 | goto probe_error_media_entity_cleanup; |
| 965 | 1106 | } |
| 966 | 1107 | |
| 1108 | + ret = ov2740_register_nvmem(client); | |
| 1109 | + if (ret) | |
| 1110 | + dev_err(&client->dev, "register nvmem failed, ret %d\n", ret); | |
| 1111 | + | |
| 967 | 1112 | /* |
| 968 | 1113 | * Device is already turned on by i2c-core with ACPI domain PM. |
| 969 | 1114 | * Enable runtime PM and turn off the device. |
| @@ -988,20 +1133,18 @@ static const struct dev_pm_ops ov2740_pm_ops = { · 18 unmodified lines | ||
| 988 | 1133 | SET_SYSTEM_SLEEP_PM_OPS(ov2740_suspend, ov2740_resume) |
| 989 | 1134 | }; |
| 990 | 1135 | |
| 991 | −#ifdef CONFIG_ACPI | |
| 992 | 1136 | static const struct acpi_device_id ov2740_acpi_ids[] = { |
| 993 | 1137 | {"INT3474"}, |
| 994 | 1138 | {} |
| 995 | 1139 | }; |
| 996 | 1140 | |
| 997 | 1141 | MODULE_DEVICE_TABLE(acpi, ov2740_acpi_ids); |
| 998 | −#endif | |
| 999 | 1142 | |
| 1000 | 1143 | static struct i2c_driver ov2740_i2c_driver = { |
| 1001 | 1144 | .driver = { |
| 1002 | 1145 | .name = "ov2740", |
| 1003 | 1146 | .pm = &ov2740_pm_ops, |
| 1004 | − .acpi_match_table = ACPI_PTR(ov2740_acpi_ids), | |
| 1147 | + .acpi_match_table = ov2740_acpi_ids, | |
| 1005 | 1148 | }, |
| 1006 | 1149 | .probe_new = ov2740_probe, |
| 1007 | 1150 | .remove = ov2740_remove, |
| @@ -772,6 +772,6 @@ static struct i2c_driver ov9640_i2c_driver = { · 771 unmodified lines | ||
| 772 | 772 | |
| 773 | 773 | module_i2c_driver(ov9640_i2c_driver); |
| 774 | 774 | |
| 775 | −MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV96xx"); | |
| 775 | +MODULE_DESCRIPTION("OmniVision OV96xx CMOS Image Sensor driver"); | |
| 776 | 776 | MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); |
| 777 | 777 | MODULE_LICENSE("GPL v2"); |
| @@ -0,0 +1,667 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0+ | |
| 2 | +/* | |
| 3 | + * IMI RDACM20 GMSL Camera Driver | |
| 4 | + * | |
| 5 | + * Copyright (C) 2017-2020 Jacopo Mondi | |
| 6 | + * Copyright (C) 2017-2020 Kieran Bingham | |
| 7 | + * Copyright (C) 2017-2019 Laurent Pinchart | |
| 8 | + * Copyright (C) 2017-2019 Niklas Söderlund | |
| 9 | + * Copyright (C) 2016 Renesas Electronics Corporation | |
| 10 | + * Copyright (C) 2015 Cogent Embedded, Inc. | |
| 11 | + */ | |
| 12 | + | |
| 13 | +/* | |
| 14 | + * The camera is made of an Omnivision OV10635 sensor connected to a Maxim | |
| 15 | + * MAX9271 GMSL serializer. | |
| 16 | + */ | |
| 17 | + | |
| 18 | +#include <linux/delay.h> | |
| 19 | +#include <linux/fwnode.h> | |
| 20 | +#include <linux/init.h> | |
| 21 | +#include <linux/i2c.h> | |
| 22 | +#include <linux/module.h> | |
| 23 | +#include <linux/slab.h> | |
| 24 | +#include <linux/videodev2.h> | |
| 25 | + | |
| 26 | +#include <media/v4l2-async.h> | |
| 27 | +#include <media/v4l2-ctrls.h> | |
| 28 | +#include <media/v4l2-subdev.h> | |
| 29 | + | |
| 30 | +#include "max9271.h" | |
| 31 | + | |
| 32 | +#define OV10635_I2C_ADDRESS 0x30 | |
| 33 | + | |
| 34 | +#define OV10635_SOFTWARE_RESET 0x0103 | |
| 35 | +#define OV10635_PID 0x300a | |
| 36 | +#define OV10635_VER 0x300b | |
| 37 | +#define OV10635_SC_CMMN_SCCB_ID 0x300c | |
| 38 | +#define OV10635_SC_CMMN_SCCB_ID_SELECT BIT(0) | |
| 39 | +#define OV10635_VERSION 0xa635 | |
| 40 | + | |
| 41 | +#define OV10635_WIDTH 1280 | |
| 42 | +#define OV10635_HEIGHT 800 | |
| 43 | + | |
| 44 | +/* VTS = PCLK / FPS / HTS / 2 (= 88MHz / 1572 / 30 / 2) */ | |
| 45 | +#define OV10635_HTS 1572 | |
| 46 | +/* FPS = 29,9998 */ | |
| 47 | +#define OV10635_VTS 933 | |
| 48 | + | |
| 49 | +/* | |
| 50 | + * As the drivers supports a single MEDIA_BUS_FMT_UYVY8_2X8 format we | |
| 51 | + * can harcode the pixel rate. | |
| 52 | + * | |
| 53 | + * PCLK is fed through the system clock, programmed @88MHz. | |
| 54 | + * MEDIA_BUS_FMT_UYVY8_2X8 format = 2 samples per pixel. | |
| 55 | + * | |
| 56 | + * Pixelrate = PCLK / 2 | |
| 57 | + * FPS = (OV10635_VTS * OV10635_HTS) / PixelRate | |
| 58 | + * = 29,9998 | |
| 59 | + */ | |
| 60 | +#define OV10635_PIXEL_RATE (44000000) | |
| 61 | + | |
| 62 | +static const struct ov10635_reg { | |
| 63 | + u16 reg; | |
| 64 | + u8 val; | |
| 65 | +} ov10635_regs_wizard[] = { | |
| 66 | + { 0x301b, 0xff }, { 0x301c, 0xff }, { 0x301a, 0xff }, { 0x3011, 0x42 }, | |
| 67 | + { 0x6900, 0x0c }, { 0x6901, 0x19 }, { 0x3503, 0x10 }, { 0x3025, 0x03 }, | |
| 68 | + { 0x3003, 0x16 }, { 0x3004, 0x30 }, { 0x3005, 0x40 }, { 0x3006, 0x91 }, | |
| 69 | + { 0x3600, 0x74 }, { 0x3601, 0x2b }, { 0x3612, 0x00 }, { 0x3611, 0x67 }, | |
| 70 | + { 0x3633, 0xca }, { 0x3602, 0xaf }, { 0x3603, 0x04 }, { 0x3630, 0x28 }, | |
| 71 | + { 0x3631, 0x16 }, { 0x3714, 0x10 }, { 0x371d, 0x01 }, { 0x4300, 0x3a }, | |
| 72 | + { 0x3007, 0x01 }, { 0x3024, 0x03 }, { 0x3020, 0x0a }, { 0x3702, 0x0d }, | |
| 73 | + { 0x3703, 0x20 }, { 0x3704, 0x15 }, { 0x3709, 0xa8 }, { 0x370c, 0xc7 }, | |
| 74 | + { 0x370d, 0x80 }, { 0x3712, 0x00 }, { 0x3713, 0x20 }, { 0x3715, 0x04 }, | |
| 75 | + { 0x381d, 0x40 }, { 0x381c, 0x00 }, { 0x3822, 0x50 }, { 0x3824, 0x10 }, | |
| 76 | + { 0x3815, 0x8c }, { 0x3804, 0x05 }, { 0x3805, 0x1f }, { 0x3800, 0x00 }, | |
| 77 | + { 0x3801, 0x00 }, { 0x3806, 0x03 }, { 0x3807, 0x28 }, { 0x3802, 0x00 }, | |
| 78 | + { 0x3803, 0x07 }, { 0x3808, 0x05 }, { 0x3809, 0x00 }, { 0x380a, 0x03 }, | |
| 79 | + { 0x380b, 0x20 }, { 0x380c, OV10635_HTS >> 8 }, | |
| 80 | + { 0x380d, OV10635_HTS & 0xff }, { 0x380e, OV10635_VTS >> 8 }, | |
| 81 | + { 0x380f, OV10635_VTS & 0xff }, { 0x3813, 0x02 }, { 0x3811, 0x08 }, | |
| 82 | + { 0x381f, 0x0c }, { 0x3819, 0x04 }, { 0x3804, 0x01 }, { 0x3805, 0x00 }, | |
| 83 | + { 0x3828, 0x03 }, { 0x3829, 0x10 }, { 0x382a, 0x10 }, { 0x3621, 0x63 }, | |
| 84 | + { 0x5005, 0x08 }, { 0x56d5, 0x00 }, { 0x56d6, 0x80 }, { 0x56d7, 0x00 }, | |
| 85 | + { 0x56d8, 0x00 }, { 0x56d9, 0x00 }, { 0x56da, 0x80 }, { 0x56db, 0x00 }, | |
| 86 | + { 0x56dc, 0x00 }, { 0x56e8, 0x00 }, { 0x56e9, 0x7f }, { 0x56ea, 0x00 }, | |
| 87 | + { 0x56eb, 0x7f }, { 0x5100, 0x00 }, { 0x5101, 0x80 }, { 0x5102, 0x00 }, | |
| 88 | + { 0x5103, 0x80 }, { 0x5104, 0x00 }, { 0x5105, 0x80 }, { 0x5106, 0x00 }, | |
| 89 | + { 0x5107, 0x80 }, { 0x5108, 0x00 }, { 0x5109, 0x00 }, { 0x510a, 0x00 }, | |
| 90 | + { 0x510b, 0x00 }, { 0x510c, 0x00 }, { 0x510d, 0x00 }, { 0x510e, 0x00 }, | |
| 91 | + { 0x510f, 0x00 }, { 0x5110, 0x00 }, { 0x5111, 0x80 }, { 0x5112, 0x00 }, | |
| 92 | + { 0x5113, 0x80 }, { 0x5114, 0x00 }, { 0x5115, 0x80 }, { 0x5116, 0x00 }, | |
| 93 | + { 0x5117, 0x80 }, { 0x5118, 0x00 }, { 0x5119, 0x00 }, { 0x511a, 0x00 }, | |
| 94 | + { 0x511b, 0x00 }, { 0x511c, 0x00 }, { 0x511d, 0x00 }, { 0x511e, 0x00 }, | |
| 95 | + { 0x511f, 0x00 }, { 0x56d0, 0x00 }, { 0x5006, 0x04 }, { 0x5608, 0x05 }, | |
| 96 | + { 0x52d7, 0x06 }, { 0x528d, 0x08 }, { 0x5293, 0x12 }, { 0x52d3, 0x12 }, | |
| 97 | + { 0x5288, 0x06 }, { 0x5289, 0x20 }, { 0x52c8, 0x06 }, { 0x52c9, 0x20 }, | |
| 98 | + { 0x52cd, 0x04 }, { 0x5381, 0x00 }, { 0x5382, 0xff }, { 0x5589, 0x76 }, | |
| 99 | + { 0x558a, 0x47 }, { 0x558b, 0xef }, { 0x558c, 0xc9 }, { 0x558d, 0x49 }, | |
| 100 | + { 0x558e, 0x30 }, { 0x558f, 0x67 }, { 0x5590, 0x3f }, { 0x5591, 0xf0 }, | |
| 101 | + { 0x5592, 0x10 }, { 0x55a2, 0x6d }, { 0x55a3, 0x55 }, { 0x55a4, 0xc3 }, | |
| 102 | + { 0x55a5, 0xb5 }, { 0x55a6, 0x43 }, { 0x55a7, 0x38 }, { 0x55a8, 0x5f }, | |
| 103 | + { 0x55a9, 0x4b }, { 0x55aa, 0xf0 }, { 0x55ab, 0x10 }, { 0x5581, 0x52 }, | |
| 104 | + { 0x5300, 0x01 }, { 0x5301, 0x00 }, { 0x5302, 0x00 }, { 0x5303, 0x0e }, | |
| 105 | + { 0x5304, 0x00 }, { 0x5305, 0x0e }, { 0x5306, 0x00 }, { 0x5307, 0x36 }, | |
| 106 | + { 0x5308, 0x00 }, { 0x5309, 0xd9 }, { 0x530a, 0x00 }, { 0x530b, 0x0f }, | |
| 107 | + { 0x530c, 0x00 }, { 0x530d, 0x2c }, { 0x530e, 0x00 }, { 0x530f, 0x59 }, | |
| 108 | + { 0x5310, 0x00 }, { 0x5311, 0x7b }, { 0x5312, 0x00 }, { 0x5313, 0x22 }, | |
| 109 | + { 0x5314, 0x00 }, { 0x5315, 0xd5 }, { 0x5316, 0x00 }, { 0x5317, 0x13 }, | |
| 110 | + { 0x5318, 0x00 }, { 0x5319, 0x18 }, { 0x531a, 0x00 }, { 0x531b, 0x26 }, | |
| 111 | + { 0x531c, 0x00 }, { 0x531d, 0xdc }, { 0x531e, 0x00 }, { 0x531f, 0x02 }, | |
| 112 | + { 0x5320, 0x00 }, { 0x5321, 0x24 }, { 0x5322, 0x00 }, { 0x5323, 0x56 }, | |
| 113 | + { 0x5324, 0x00 }, { 0x5325, 0x85 }, { 0x5326, 0x00 }, { 0x5327, 0x20 }, | |
| 114 | + { 0x5609, 0x01 }, { 0x560a, 0x40 }, { 0x560b, 0x01 }, { 0x560c, 0x40 }, | |
| 115 | + { 0x560d, 0x00 }, { 0x560e, 0xfa }, { 0x560f, 0x00 }, { 0x5610, 0xfa }, | |
| 116 | + { 0x5611, 0x02 }, { 0x5612, 0x80 }, { 0x5613, 0x02 }, { 0x5614, 0x80 }, | |
| 117 | + { 0x5615, 0x01 }, { 0x5616, 0x2c }, { 0x5617, 0x01 }, { 0x5618, 0x2c }, | |
| 118 | + { 0x563b, 0x01 }, { 0x563c, 0x01 }, { 0x563d, 0x01 }, { 0x563e, 0x01 }, | |
| 119 | + { 0x563f, 0x03 }, { 0x5640, 0x03 }, { 0x5641, 0x03 }, { 0x5642, 0x05 }, | |
| 120 | + { 0x5643, 0x09 }, { 0x5644, 0x05 }, { 0x5645, 0x05 }, { 0x5646, 0x05 }, | |
| 121 | + { 0x5647, 0x05 }, { 0x5651, 0x00 }, { 0x5652, 0x80 }, { 0x521a, 0x01 }, | |
| 122 | + { 0x521b, 0x03 }, { 0x521c, 0x06 }, { 0x521d, 0x0a }, { 0x521e, 0x0e }, | |
| 123 | + { 0x521f, 0x12 }, { 0x5220, 0x16 }, { 0x5223, 0x02 }, { 0x5225, 0x04 }, | |
| 124 | + { 0x5227, 0x08 }, { 0x5229, 0x0c }, { 0x522b, 0x12 }, { 0x522d, 0x18 }, | |
| 125 | + { 0x522f, 0x1e }, { 0x5241, 0x04 }, { 0x5242, 0x01 }, { 0x5243, 0x03 }, | |
| 126 | + { 0x5244, 0x06 }, { 0x5245, 0x0a }, { 0x5246, 0x0e }, { 0x5247, 0x12 }, | |
| 127 | + { 0x5248, 0x16 }, { 0x524a, 0x03 }, { 0x524c, 0x04 }, { 0x524e, 0x08 }, | |
| 128 | + { 0x5250, 0x0c }, { 0x5252, 0x12 }, { 0x5254, 0x18 }, { 0x5256, 0x1e }, | |
| 129 | + /* fifo_line_length = 2*hts */ | |
| 130 | + { 0x4606, (2 * OV10635_HTS) >> 8 }, { 0x4607, (2 * OV10635_HTS) & 0xff }, | |
| 131 | + /* fifo_hsync_start = 2*(hts - xres) */ | |
| 132 | + { 0x460a, (2 * (OV10635_HTS - OV10635_WIDTH)) >> 8 }, | |
| 133 | + { 0x460b, (2 * (OV10635_HTS - OV10635_WIDTH)) & 0xff }, | |
| 134 | + { 0x460c, 0x00 }, { 0x4620, 0x0e }, | |
| 135 | + /* BT601: 0x08 is also acceptable as HS/VS mode */ | |
| 136 | + { 0x4700, 0x04 }, { 0x4701, 0x00 }, { 0x4702, 0x01 }, { 0x4004, 0x04 }, | |
| 137 | + { 0x4005, 0x18 }, { 0x4001, 0x06 }, { 0x4050, 0x22 }, { 0x4051, 0x24 }, | |
| 138 | + { 0x4052, 0x02 }, { 0x4057, 0x9c }, { 0x405a, 0x00 }, { 0x4202, 0x02 }, | |
| 139 | + { 0x3023, 0x10 }, { 0x0100, 0x01 }, { 0x0100, 0x01 }, { 0x6f10, 0x07 }, | |
| 140 | + { 0x6f11, 0x82 }, { 0x6f12, 0x04 }, { 0x6f13, 0x00 }, { 0xd000, 0x19 }, | |
| 141 | + { 0xd001, 0xa0 }, { 0xd002, 0x00 }, { 0xd003, 0x01 }, { 0xd004, 0xa9 }, | |
| 142 | + { 0xd005, 0xad }, { 0xd006, 0x10 }, { 0xd007, 0x40 }, { 0xd008, 0x44 }, | |
| 143 | + { 0xd009, 0x00 }, { 0xd00a, 0x68 }, { 0xd00b, 0x00 }, { 0xd00c, 0x15 }, | |
| 144 | + { 0xd00d, 0x00 }, { 0xd00e, 0x00 }, { 0xd00f, 0x00 }, { 0xd040, 0x9c }, | |
| 145 | + { 0xd041, 0x21 }, { 0xd042, 0xff }, { 0xd043, 0xf8 }, { 0xd044, 0xd4 }, | |
| 146 | + { 0xd045, 0x01 }, { 0xd046, 0x48 }, { 0xd047, 0x00 }, { 0xd048, 0xd4 }, | |
| 147 | + { 0xd049, 0x01 }, { 0xd04a, 0x50 }, { 0xd04b, 0x04 }, { 0xd04c, 0x18 }, | |
| 148 | + { 0xd04d, 0x60 }, { 0xd04e, 0x00 }, { 0xd04f, 0x01 }, { 0xd050, 0xa8 }, | |
| 149 | + { 0xd051, 0x63 }, { 0xd052, 0x02 }, { 0xd053, 0xa4 }, { 0xd054, 0x85 }, | |
| 150 | + { 0xd055, 0x43 }, { 0xd056, 0x00 }, { 0xd057, 0x00 }, { 0xd058, 0x18 }, | |
| 151 | + { 0xd059, 0x60 }, { 0xd05a, 0x00 }, { 0xd05b, 0x01 }, { 0xd05c, 0xa8 }, | |
| 152 | + { 0xd05d, 0x63 }, { 0xd05e, 0x03 }, { 0xd05f, 0xf0 }, { 0xd060, 0x98 }, | |
| 153 | + { 0xd061, 0xa3 }, { 0xd062, 0x00 }, { 0xd063, 0x00 }, { 0xd064, 0x8c }, | |
| 154 | + { 0xd065, 0x6a }, { 0xd066, 0x00 }, { 0xd067, 0x6e }, { 0xd068, 0xe5 }, | |
| 155 | + { 0xd069, 0x85 }, { 0xd06a, 0x18 }, { 0xd06b, 0x00 }, { 0xd06c, 0x10 }, | |
| 156 | + { 0xd06d, 0x00 }, { 0xd06e, 0x00 }, { 0xd06f, 0x10 }, { 0xd070, 0x9c }, | |
| 157 | + { 0xd071, 0x80 }, { 0xd072, 0x00 }, { 0xd073, 0x03 }, { 0xd074, 0x18 }, | |
| 158 | + { 0xd075, 0x60 }, { 0xd076, 0x00 }, { 0xd077, 0x01 }, { 0xd078, 0xa8 }, | |
| 159 | + { 0xd079, 0x63 }, { 0xd07a, 0x07 }, { 0xd07b, 0x80 }, { 0xd07c, 0x07 }, | |
| 160 | + { 0xd07d, 0xff }, { 0xd07e, 0xf9 }, { 0xd07f, 0x03 }, { 0xd080, 0x8c }, | |
| 161 | + { 0xd081, 0x63 }, { 0xd082, 0x00 }, { 0xd083, 0x00 }, { 0xd084, 0xa5 }, | |
| 162 | + { 0xd085, 0x6b }, { 0xd086, 0x00 }, { 0xd087, 0xff }, { 0xd088, 0x18 }, | |
| 163 | + { 0xd089, 0x80 }, { 0xd08a, 0x00 }, { 0xd08b, 0x01 }, { 0xd08c, 0xa8 }, | |
| 164 | + { 0xd08d, 0x84 }, { 0xd08e, 0x01 }, { 0xd08f, 0x04 }, { 0xd090, 0xe1 }, | |
| 165 | + { 0xd091, 0x6b }, { 0xd092, 0x58 }, { 0xd093, 0x00 }, { 0xd094, 0x94 }, | |
| 166 | + { 0xd095, 0x6a }, { 0xd096, 0x00 }, { 0xd097, 0x70 }, { 0xd098, 0xe1 }, | |
| 167 | + { 0xd099, 0x6b }, { 0xd09a, 0x20 }, { 0xd09b, 0x00 }, { 0xd09c, 0x95 }, | |
| 168 | + { 0xd09d, 0x6b }, { 0xd09e, 0x00 }, { 0xd09f, 0x00 }, { 0xd0a0, 0xe4 }, | |
| 169 | + { 0xd0a1, 0x8b }, { 0xd0a2, 0x18 }, { 0xd0a3, 0x00 }, { 0xd0a4, 0x0c }, | |
| 170 | + { 0xd0a5, 0x00 }, { 0xd0a6, 0x00 }, { 0xd0a7, 0x23 }, { 0xd0a8, 0x15 }, | |
| 171 | + { 0xd0a9, 0x00 }, { 0xd0aa, 0x00 }, { 0xd0ab, 0x00 }, { 0xd0ac, 0x18 }, | |
| 172 | + { 0xd0ad, 0x60 }, { 0xd0ae, 0x80 }, { 0xd0af, 0x06 }, { 0xd0b0, 0xa8 }, | |
| 173 | + { 0xd0b1, 0x83 }, { 0xd0b2, 0x40 }, { 0xd0b3, 0x08 }, { 0xd0b4, 0xa8 }, | |
| 174 | + { 0xd0b5, 0xe3 }, { 0xd0b6, 0x38 }, { 0xd0b7, 0x2a }, { 0xd0b8, 0xa8 }, | |
| 175 | + { 0xd0b9, 0xc3 }, { 0xd0ba, 0x40 }, { 0xd0bb, 0x09 }, { 0xd0bc, 0xa8 }, | |
| 176 | + { 0xd0bd, 0xa3 }, { 0xd0be, 0x38 }, { 0xd0bf, 0x29 }, { 0xd0c0, 0x8c }, | |
| 177 | + { 0xd0c1, 0x65 }, { 0xd0c2, 0x00 }, { 0xd0c3, 0x00 }, { 0xd0c4, 0xd8 }, | |
| 178 | + { 0xd0c5, 0x04 }, { 0xd0c6, 0x18 }, { 0xd0c7, 0x00 }, { 0xd0c8, 0x8c }, | |
| 179 | + { 0xd0c9, 0x67 }, { 0xd0ca, 0x00 }, { 0xd0cb, 0x00 }, { 0xd0cc, 0xd8 }, | |
| 180 | + { 0xd0cd, 0x06 }, { 0xd0ce, 0x18 }, { 0xd0cf, 0x00 }, { 0xd0d0, 0x18 }, | |
| 181 | + { 0xd0d1, 0x60 }, { 0xd0d2, 0x80 }, { 0xd0d3, 0x06 }, { 0xd0d4, 0xa8 }, | |
| 182 | + { 0xd0d5, 0xe3 }, { 0xd0d6, 0x67 }, { 0xd0d7, 0x02 }, { 0xd0d8, 0xa9 }, | |
| 183 | + { 0xd0d9, 0x03 }, { 0xd0da, 0x67 }, { 0xd0db, 0x03 }, { 0xd0dc, 0xa8 }, | |
| 184 | + { 0xd0dd, 0xc3 }, { 0xd0de, 0x3d }, { 0xd0df, 0x05 }, { 0xd0e0, 0x8c }, | |
| 185 | + { 0xd0e1, 0x66 }, { 0xd0e2, 0x00 }, { 0xd0e3, 0x00 }, { 0xd0e4, 0xb8 }, | |
| 186 | + { 0xd0e5, 0x63 }, { 0xd0e6, 0x00 }, { 0xd0e7, 0x18 }, { 0xd0e8, 0xb8 }, | |
| 187 | + { 0xd0e9, 0x63 }, { 0xd0ea, 0x00 }, { 0xd0eb, 0x98 }, { 0xd0ec, 0xbc }, | |
| 188 | + { 0xd0ed, 0x03 }, { 0xd0ee, 0x00 }, { 0xd0ef, 0x00 }, { 0xd0f0, 0x10 }, | |
| 189 | + { 0xd0f1, 0x00 }, { 0xd0f2, 0x00 }, { 0xd0f3, 0x16 }, { 0xd0f4, 0xb8 }, | |
| 190 | + { 0xd0f5, 0x83 }, { 0xd0f6, 0x00 }, { 0xd0f7, 0x19 }, { 0xd0f8, 0x8c }, | |
| 191 | + { 0xd0f9, 0x67 }, { 0xd0fa, 0x00 }, { 0xd0fb, 0x00 }, { 0xd0fc, 0xb8 }, | |
| 192 | + { 0xd0fd, 0xa4 }, { 0xd0fe, 0x00 }, { 0xd0ff, 0x98 }, { 0xd100, 0xb8 }, | |
| 193 | + { 0xd101, 0x83 }, { 0xd102, 0x00 }, { 0xd103, 0x08 }, { 0xd104, 0x8c }, | |
| 194 | + { 0xd105, 0x68 }, { 0xd106, 0x00 }, { 0xd107, 0x00 }, { 0xd108, 0xe0 }, | |
| 195 | + { 0xd109, 0x63 }, { 0xd10a, 0x20 }, { 0xd10b, 0x04 }, { 0xd10c, 0xe0 }, | |
| 196 | + { 0xd10d, 0x65 }, { 0xd10e, 0x18 }, { 0xd10f, 0x00 }, { 0xd110, 0xa4 }, | |
| 197 | + { 0xd111, 0x83 }, { 0xd112, 0xff }, { 0xd113, 0xff }, { 0xd114, 0xb8 }, | |
| 198 | + { 0xd115, 0x64 }, { 0xd116, 0x00 }, { 0xd117, 0x48 }, { 0xd118, 0xd8 }, | |
| 199 | + { 0xd119, 0x07 }, { 0xd11a, 0x18 }, { 0xd11b, 0x00 }, { 0xd11c, 0xd8 }, | |
| 200 | + { 0xd11d, 0x08 }, { 0xd11e, 0x20 }, { 0xd11f, 0x00 }, { 0xd120, 0x9c }, | |
| 201 | + { 0xd121, 0x60 }, { 0xd122, 0x00 }, { 0xd123, 0x00 }, { 0xd124, 0xd8 }, | |
| 202 | + { 0xd125, 0x06 }, { 0xd126, 0x18 }, { 0xd127, 0x00 }, { 0xd128, 0x00 }, | |
| 203 | + { 0xd129, 0x00 }, { 0xd12a, 0x00 }, { 0xd12b, 0x08 }, { 0xd12c, 0x15 }, | |
| 204 | + { 0xd12d, 0x00 }, { 0xd12e, 0x00 }, { 0xd12f, 0x00 }, { 0xd130, 0x8c }, | |
| 205 | + { 0xd131, 0x6a }, { 0xd132, 0x00 }, { 0xd133, 0x76 }, { 0xd134, 0xbc }, | |
| 206 | + { 0xd135, 0x23 }, { 0xd136, 0x00 }, { 0xd137, 0x00 }, { 0xd138, 0x13 }, | |
| 207 | + { 0xd139, 0xff }, { 0xd13a, 0xff }, { 0xd13b, 0xe6 }, { 0xd13c, 0x18 }, | |
| 208 | + { 0xd13d, 0x60 }, { 0xd13e, 0x80 }, { 0xd13f, 0x06 }, { 0xd140, 0x03 }, | |
| 209 | + { 0xd141, 0xff }, { 0xd142, 0xff }, { 0xd143, 0xdd }, { 0xd144, 0xa8 }, | |
| 210 | + { 0xd145, 0x83 }, { 0xd146, 0x40 }, { 0xd147, 0x08 }, { 0xd148, 0x85 }, | |
| 211 | + { 0xd149, 0x21 }, { 0xd14a, 0x00 }, { 0xd14b, 0x00 }, { 0xd14c, 0x85 }, | |
| 212 | + { 0xd14d, 0x41 }, { 0xd14e, 0x00 }, { 0xd14f, 0x04 }, { 0xd150, 0x44 }, | |
| 213 | + { 0xd151, 0x00 }, { 0xd152, 0x48 }, { 0xd153, 0x00 }, { 0xd154, 0x9c }, | |
| 214 | + { 0xd155, 0x21 }, { 0xd156, 0x00 }, { 0xd157, 0x08 }, { 0x6f0e, 0x03 }, | |
| 215 | + { 0x6f0f, 0x00 }, { 0x460e, 0x08 }, { 0x460f, 0x01 }, { 0x4610, 0x00 }, | |
| 216 | + { 0x4611, 0x01 }, { 0x4612, 0x00 }, { 0x4613, 0x01 }, | |
| 217 | + /* 8 bits */ | |
| 218 | + { 0x4605, 0x08 }, | |
| 219 | + /* Swap data bits order [9:0] -> [0:9] */ | |
| 220 | + { 0x4709, 0x10 }, { 0x4608, 0x00 }, { 0x4609, 0x08 }, { 0x6804, 0x00 }, | |
| 221 | + { 0x6805, 0x06 }, { 0x6806, 0x00 }, { 0x5120, 0x00 }, { 0x3510, 0x00 }, | |
| 222 | + { 0x3504, 0x00 }, { 0x6800, 0x00 }, { 0x6f0d, 0x01 }, | |
| 223 | + /* PCLK falling edge */ | |
| 224 | + { 0x4708, 0x01 }, { 0x5000, 0xff }, { 0x5001, 0xbf }, { 0x5002, 0x7e }, | |
| 225 | + { 0x503d, 0x00 }, { 0xc450, 0x01 }, { 0xc452, 0x04 }, { 0xc453, 0x00 }, | |
| 226 | + { 0xc454, 0x00 }, { 0xc455, 0x01 }, { 0xc456, 0x01 }, { 0xc457, 0x00 }, | |
| 227 | + { 0xc458, 0x00 }, { 0xc459, 0x00 }, { 0xc45b, 0x00 }, { 0xc45c, 0x01 }, | |
| 228 | + { 0xc45d, 0x00 }, { 0xc45e, 0x00 }, { 0xc45f, 0x00 }, { 0xc460, 0x00 }, | |
| 229 | + { 0xc461, 0x01 }, { 0xc462, 0x01 }, { 0xc464, 0x03 }, { 0xc465, 0x00 }, | |
| 230 | + { 0xc466, 0x8a }, { 0xc467, 0x00 }, { 0xc468, 0x86 }, { 0xc469, 0x00 }, | |
| 231 | + { 0xc46a, 0x40 }, { 0xc46b, 0x50 }, { 0xc46c, 0x30 }, { 0xc46d, 0x28 }, | |
| 232 | + { 0xc46e, 0x60 }, { 0xc46f, 0x40 }, { 0xc47c, 0x01 }, { 0xc47d, 0x38 }, | |
| 233 | + { 0xc47e, 0x00 }, { 0xc47f, 0x00 }, { 0xc480, 0x00 }, { 0xc481, 0xff }, | |
| 234 | + { 0xc482, 0x00 }, { 0xc483, 0x40 }, { 0xc484, 0x00 }, { 0xc485, 0x18 }, | |
| 235 | + { 0xc486, 0x00 }, { 0xc487, 0x18 }, | |
| 236 | + { 0xc488, (OV10635_VTS - 8) * 16 >> 8}, | |
| 237 | + { 0xc489, (OV10635_VTS - 8) * 16 & 0xff}, | |
| 238 | + { 0xc48a, (OV10635_VTS - 8) * 16 >> 8}, | |
| 239 | + { 0xc48b, (OV10635_VTS - 8) * 16 & 0xff}, { 0xc48c, 0x00 }, | |
| 240 | + { 0xc48d, 0x04 }, { 0xc48e, 0x00 }, { 0xc48f, 0x04 }, { 0xc490, 0x03 }, | |
| 241 | + { 0xc492, 0x20 }, { 0xc493, 0x08 }, { 0xc498, 0x02 }, { 0xc499, 0x00 }, | |
| 242 | + { 0xc49a, 0x02 }, { 0xc49b, 0x00 }, { 0xc49c, 0x02 }, { 0xc49d, 0x00 }, | |
| 243 | + { 0xc49e, 0x02 }, { 0xc49f, 0x60 }, { 0xc4a0, 0x03 }, { 0xc4a1, 0x00 }, | |
| 244 | + { 0xc4a2, 0x04 }, { 0xc4a3, 0x00 }, { 0xc4a4, 0x00 }, { 0xc4a5, 0x10 }, | |
| 245 | + { 0xc4a6, 0x00 }, { 0xc4a7, 0x40 }, { 0xc4a8, 0x00 }, { 0xc4a9, 0x80 }, | |
| 246 | + { 0xc4aa, 0x0d }, { 0xc4ab, 0x00 }, { 0xc4ac, 0x0f }, { 0xc4ad, 0xc0 }, | |
| 247 | + { 0xc4b4, 0x01 }, { 0xc4b5, 0x01 }, { 0xc4b6, 0x00 }, { 0xc4b7, 0x01 }, | |
| 248 | + { 0xc4b8, 0x00 }, { 0xc4b9, 0x01 }, { 0xc4ba, 0x01 }, { 0xc4bb, 0x00 }, | |
| 249 | + { 0xc4bc, 0x01 }, { 0xc4bd, 0x60 }, { 0xc4be, 0x02 }, { 0xc4bf, 0x33 }, | |
| 250 | + { 0xc4c8, 0x03 }, { 0xc4c9, 0xd0 }, { 0xc4ca, 0x0e }, { 0xc4cb, 0x00 }, | |
| 251 | + { 0xc4cc, 0x0e }, { 0xc4cd, 0x51 }, { 0xc4ce, 0x0e }, { 0xc4cf, 0x51 }, | |
| 252 | + { 0xc4d0, 0x04 }, { 0xc4d1, 0x80 }, { 0xc4e0, 0x04 }, { 0xc4e1, 0x02 }, | |
| 253 | + { 0xc4e2, 0x01 }, { 0xc4e4, 0x10 }, { 0xc4e5, 0x20 }, { 0xc4e6, 0x30 }, | |
| 254 | + { 0xc4e7, 0x40 }, { 0xc4e8, 0x50 }, { 0xc4e9, 0x60 }, { 0xc4ea, 0x70 }, | |
| 255 | + { 0xc4eb, 0x80 }, { 0xc4ec, 0x90 }, { 0xc4ed, 0xa0 }, { 0xc4ee, 0xb0 }, | |
| 256 | + { 0xc4ef, 0xc0 }, { 0xc4f0, 0xd0 }, { 0xc4f1, 0xe0 }, { 0xc4f2, 0xf0 }, | |
| 257 | + { 0xc4f3, 0x80 }, { 0xc4f4, 0x00 }, { 0xc4f5, 0x20 }, { 0xc4f6, 0x02 }, | |
| 258 | + { 0xc4f7, 0x00 }, { 0xc4f8, 0x00 }, { 0xc4f9, 0x00 }, { 0xc4fa, 0x00 }, | |
| 259 | + { 0xc4fb, 0x01 }, { 0xc4fc, 0x01 }, { 0xc4fd, 0x00 }, { 0xc4fe, 0x04 }, | |
| 260 | + { 0xc4ff, 0x02 }, { 0xc500, 0x48 }, { 0xc501, 0x74 }, { 0xc502, 0x58 }, | |
| 261 | + { 0xc503, 0x80 }, { 0xc504, 0x05 }, { 0xc505, 0x80 }, { 0xc506, 0x03 }, | |
| 262 | + { 0xc507, 0x80 }, { 0xc508, 0x01 }, { 0xc509, 0xc0 }, { 0xc50a, 0x01 }, | |
| 263 | + { 0xc50b, 0xa0 }, { 0xc50c, 0x01 }, { 0xc50d, 0x2c }, { 0xc50e, 0x01 }, | |
| 264 | + { 0xc50f, 0x0a }, { 0xc510, 0x00 }, { 0xc511, 0x00 }, { 0xc512, 0xe5 }, | |
| 265 | + { 0xc513, 0x14 }, { 0xc514, 0x04 }, { 0xc515, 0x00 }, { 0xc518, OV10635_VTS >> 8}, | |
| 266 | + { 0xc519, OV10635_VTS & 0xff}, { 0xc51a, OV10635_HTS >> 8}, | |
| 267 | + { 0xc51b, OV10635_HTS & 0xff}, { 0xc2e0, 0x00 }, { 0xc2e1, 0x51 }, | |
| 268 | + { 0xc2e2, 0x00 }, { 0xc2e3, 0xd6 }, { 0xc2e4, 0x01 }, { 0xc2e5, 0x5e }, | |
| 269 | + { 0xc2e9, 0x01 }, { 0xc2ea, 0x7a }, { 0xc2eb, 0x90 }, { 0xc2ed, 0x00 }, | |
| 270 | + { 0xc2ee, 0x7a }, { 0xc2ef, 0x64 }, { 0xc308, 0x00 }, { 0xc309, 0x00 }, | |
| 271 | + { 0xc30a, 0x00 }, { 0xc30c, 0x00 }, { 0xc30d, 0x01 }, { 0xc30e, 0x00 }, | |
| 272 | + { 0xc30f, 0x00 }, { 0xc310, 0x01 }, { 0xc311, 0x60 }, { 0xc312, 0xff }, | |
| 273 | + { 0xc313, 0x08 }, { 0xc314, 0x01 }, { 0xc315, 0x00 }, { 0xc316, 0xff }, | |
| 274 | + { 0xc317, 0x0b }, { 0xc318, 0x00 }, { 0xc319, 0x0c }, { 0xc31a, 0x00 }, | |
| 275 | + { 0xc31b, 0xe0 }, { 0xc31c, 0x00 }, { 0xc31d, 0x14 }, { 0xc31e, 0x00 }, | |
| 276 | + { 0xc31f, 0xc5 }, { 0xc320, 0xff }, { 0xc321, 0x4b }, { 0xc322, 0xff }, | |
| 277 | + { 0xc323, 0xf0 }, { 0xc324, 0xff }, { 0xc325, 0xe8 }, { 0xc326, 0x00 }, | |
| 278 | + { 0xc327, 0x46 }, { 0xc328, 0xff }, { 0xc329, 0xd2 }, { 0xc32a, 0xff }, | |
| 279 | + { 0xc32b, 0xe4 }, { 0xc32c, 0xff }, { 0xc32d, 0xbb }, { 0xc32e, 0x00 }, | |
| 280 | + { 0xc32f, 0x61 }, { 0xc330, 0xff }, { 0xc331, 0xf9 }, { 0xc332, 0x00 }, | |
| 281 | + { 0xc333, 0xd9 }, { 0xc334, 0x00 }, { 0xc335, 0x2e }, { 0xc336, 0x00 }, | |
| 282 | + { 0xc337, 0xb1 }, { 0xc338, 0xff }, { 0xc339, 0x64 }, { 0xc33a, 0xff }, | |
| 283 | + { 0xc33b, 0xeb }, { 0xc33c, 0xff }, { 0xc33d, 0xe8 }, { 0xc33e, 0x00 }, | |
| 284 | + { 0xc33f, 0x48 }, { 0xc340, 0xff }, { 0xc341, 0xd0 }, { 0xc342, 0xff }, | |
| 285 | + { 0xc343, 0xed }, { 0xc344, 0xff }, { 0xc345, 0xad }, { 0xc346, 0x00 }, | |
| 286 | + { 0xc347, 0x66 }, { 0xc348, 0x01 }, { 0xc349, 0x00 }, { 0x6700, 0x04 }, | |
| 287 | + { 0x6701, 0x7b }, { 0x6702, 0xfd }, { 0x6703, 0xf9 }, { 0x6704, 0x3d }, | |
| 288 | + { 0x6705, 0x71 }, { 0x6706, 0x78 }, { 0x6708, 0x05 }, { 0x6f06, 0x6f }, | |
| 289 | + { 0x6f07, 0x00 }, { 0x6f0a, 0x6f }, { 0x6f0b, 0x00 }, { 0x6f00, 0x03 }, | |
| 290 | + { 0xc34c, 0x01 }, { 0xc34d, 0x00 }, { 0xc34e, 0x46 }, { 0xc34f, 0x55 }, | |
| 291 | + { 0xc350, 0x00 }, { 0xc351, 0x40 }, { 0xc352, 0x00 }, { 0xc353, 0xff }, | |
| 292 | + { 0xc354, 0x04 }, { 0xc355, 0x08 }, { 0xc356, 0x01 }, { 0xc357, 0xef }, | |
| 293 | + { 0xc358, 0x30 }, { 0xc359, 0x01 }, { 0xc35a, 0x64 }, { 0xc35b, 0x46 }, | |
| 294 | + { 0xc35c, 0x00 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, | |
| 295 | + { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, | |
| 296 | + { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, | |
| 297 | + { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, | |
| 298 | + { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, | |
| 299 | + { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, | |
| 300 | + { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0xc261, 0x01 }, | |
| 301 | + { 0x301b, 0xf0 }, { 0x301c, 0xf0 }, { 0x301a, 0xf0 }, { 0x6f00, 0xc3 }, | |
| 302 | + { 0xc46a, 0x30 }, { 0xc46d, 0x20 }, { 0xc464, 0x84 }, { 0xc465, 0x00 }, | |
| 303 | + { 0x6f00, 0x03 }, { 0x6f00, 0x43 }, { 0x381c, 0x00 }, { 0x381d, 0x40 }, | |
| 304 | + { 0xc454, 0x01 }, { 0x6f00, 0xc3 }, { 0xc454, 0x00 }, { 0xc4b1, 0x02 }, | |
| 305 | + { 0xc4b2, 0x01 }, { 0xc4b3, 0x03 }, { 0x6f00, 0x03 }, { 0x6f00, 0x43 }, | |
| 306 | + /* enable FSIN (FRAMESYNC input) functionality */ | |
| 307 | + { 0x3832, (0x0d + 2 * 0x20 + 0x15 + 38) >> 8 }, | |
| 308 | + { 0x3833, (0x0d + 2 * 0x20 + 0x15 + 38) & 0xff }, | |
| 309 | + { 0x3834, OV10635_VTS >> 8 }, { 0x3835, OV10635_VTS & 0xff }, | |
| 310 | + { 0x302e, 0x01 }, | |
| 311 | +}; | |
| 312 | + | |
| 313 | +struct rdacm20_device { | |
| 314 | + struct device *dev; | |
| 315 | + struct max9271_device *serializer; | |
| 316 | + struct i2c_client *sensor; | |
| 317 | + struct v4l2_subdev sd; | |
| 318 | + struct media_pad pad; | |
| 319 | + struct v4l2_ctrl_handler ctrls; | |
| 320 | + u32 addrs[2]; | |
| 321 | +}; | |
| 322 | + | |
| 323 | +static inline struct rdacm20_device *sd_to_rdacm20(struct v4l2_subdev *sd) | |
| 324 | +{ | |
| 325 | + return container_of(sd, struct rdacm20_device, sd); | |
| 326 | +} | |
| 327 | + | |
| 328 | +static inline struct rdacm20_device *i2c_to_rdacm20(struct i2c_client *client) | |
| 329 | +{ | |
| 330 | + return sd_to_rdacm20(i2c_get_clientdata(client)); | |
| 331 | +} | |
| 332 | + | |
| 333 | +static int ov10635_read16(struct rdacm20_device *dev, u16 reg) | |
| 334 | +{ | |
| 335 | + u8 buf[2] = { reg >> 8, reg & 0xff }; | |
| 336 | + int ret; | |
| 337 | + | |
| 338 | + ret = i2c_master_send(dev->sensor, buf, 2); | |
| 339 | + if (ret != 2) { | |
| 340 | + dev_dbg(dev->dev, "%s: register 0x%04x write failed (%d)\n", | |
| 341 | + __func__, reg, ret); | |
| 342 | + return ret; | |
| 343 | + } | |
| 344 | + | |
| 345 | + ret = i2c_master_recv(dev->sensor, buf, 2); | |
| 346 | + if (ret < 0) { | |
| 347 | + dev_dbg(dev->dev, "%s: register 0x%04x read failed (%d)\n", | |
| 348 | + __func__, reg, ret); | |
| 349 | + return ret; | |
| 350 | + } | |
| 351 | + | |
| 352 | + return (buf[0] << 8) | buf[1]; | |
| 353 | +} | |
| 354 | + | |
| 355 | +static int __ov10635_write(struct rdacm20_device *dev, u16 reg, u8 val) | |
| 356 | +{ | |
| 357 | + u8 buf[3] = { reg >> 8, reg & 0xff, val }; | |
| 358 | + int ret; | |
| 359 | + | |
| 360 | + dev_dbg(dev->dev, "%s(0x%04x, 0x%02x)\n", __func__, reg, val); | |
| 361 | + | |
| 362 | + ret = i2c_master_send(dev->sensor, buf, 3); | |
| 363 | + return ret < 0 ? ret : 0; | |
| 364 | +} | |
| 365 | + | |
| 366 | +static int ov10635_write(struct rdacm20_device *dev, u16 reg, u8 val) | |
| 367 | +{ | |
| 368 | + int ret; | |
| 369 | + | |
| 370 | + ret = __ov10635_write(dev, reg, val); | |
| 371 | + if (ret < 0) | |
| 372 | + dev_err(dev->dev, "%s: register 0x%04x write failed (%d)\n", | |
| 373 | + __func__, reg, ret); | |
| 374 | + | |
| 375 | + return ret; | |
| 376 | +} | |
| 377 | + | |
| 378 | +static int ov10635_set_regs(struct rdacm20_device *dev, | |
| 379 | + const struct ov10635_reg *regs, | |
| 380 | + unsigned int nr_regs) | |
| 381 | +{ | |
| 382 | + unsigned int i; | |
| 383 | + int ret; | |
| 384 | + | |
| 385 | + for (i = 0; i < nr_regs; i++) { | |
| 386 | + ret = __ov10635_write(dev, regs[i].reg, regs[i].val); | |
| 387 | + if (ret) { | |
| 388 | + dev_err(dev->dev, | |
| 389 | + "%s: register %u (0x%04x) write failed (%d)\n", | |
| 390 | + __func__, i, regs[i].reg, ret); | |
| 391 | + return ret; | |
| 392 | + } | |
| 393 | + } | |
| 394 | + | |
| 395 | + return 0; | |
| 396 | +} | |
| 397 | + | |
| 398 | +static int rdacm20_s_stream(struct v4l2_subdev *sd, int enable) | |
| 399 | +{ | |
| 400 | + struct rdacm20_device *dev = sd_to_rdacm20(sd); | |
| 401 | + | |
| 402 | + return max9271_set_serial_link(dev->serializer, enable); | |
| 403 | +} | |
| 404 | + | |
| 405 | +static int rdacm20_enum_mbus_code(struct v4l2_subdev *sd, | |
| 406 | + struct v4l2_subdev_pad_config *cfg, | |
| 407 | + struct v4l2_subdev_mbus_code_enum *code) | |
| 408 | +{ | |
| 409 | + if (code->pad || code->index > 0) | |
| 410 | + return -EINVAL; | |
| 411 | + | |
| 412 | + code->code = MEDIA_BUS_FMT_UYVY8_2X8; | |
| 413 | + | |
| 414 | + return 0; | |
| 415 | +} | |
| 416 | + | |
| 417 | +static int rdacm20_get_fmt(struct v4l2_subdev *sd, | |
| 418 | + struct v4l2_subdev_pad_config *cfg, | |
| 419 | + struct v4l2_subdev_format *format) | |
| 420 | +{ | |
| 421 | + struct v4l2_mbus_framefmt *mf = &format->format; | |
| 422 | + | |
| 423 | + if (format->pad) | |
| 424 | + return -EINVAL; | |
| 425 | + | |
| 426 | + mf->width = OV10635_WIDTH; | |
| 427 | + mf->height = OV10635_HEIGHT; | |
| 428 | + mf->code = MEDIA_BUS_FMT_UYVY8_2X8; | |
| 429 | + mf->colorspace = V4L2_COLORSPACE_RAW; | |
| 430 | + mf->field = V4L2_FIELD_NONE; | |
| 431 | + mf->ycbcr_enc = V4L2_YCBCR_ENC_601; | |
| 432 | + mf->quantization = V4L2_QUANTIZATION_FULL_RANGE; | |
| 433 | + mf->xfer_func = V4L2_XFER_FUNC_NONE; | |
| 434 | + | |
| 435 | + return 0; | |
| 436 | +} | |
| 437 | + | |
| 438 | +static struct v4l2_subdev_video_ops rdacm20_video_ops = { | |
| 439 | + .s_stream = rdacm20_s_stream, | |
| 440 | +}; | |
| 441 | + | |
| 442 | +static const struct v4l2_subdev_pad_ops rdacm20_subdev_pad_ops = { | |
| 443 | + .enum_mbus_code = rdacm20_enum_mbus_code, | |
| 444 | + .get_fmt = rdacm20_get_fmt, | |
| 445 | + .set_fmt = rdacm20_get_fmt, | |
| 446 | +}; | |
| 447 | + | |
| 448 | +static struct v4l2_subdev_ops rdacm20_subdev_ops = { | |
| 449 | + .video = &rdacm20_video_ops, | |
| 450 | + .pad = &rdacm20_subdev_pad_ops, | |
| 451 | +}; | |
| 452 | + | |
| 453 | +static int rdacm20_initialize(struct rdacm20_device *dev) | |
| 454 | +{ | |
| 455 | + unsigned int retry = 3; | |
| 456 | + int ret; | |
| 457 | + | |
| 458 | + /* Verify communication with the MAX9271: ping to wakeup. */ | |
| 459 | + dev->serializer->client->addr = MAX9271_DEFAULT_ADDR; | |
| 460 | + i2c_smbus_read_byte(dev->serializer->client); | |
| 461 | + | |
| 462 | + /* Serial link disabled during config as it needs a valid pixel clock. */ | |
| 463 | + ret = max9271_set_serial_link(dev->serializer, false); | |
| 464 | + if (ret) | |
| 465 | + return ret; | |
| 466 | + | |
| 467 | + /* | |
| 468 | + * Ensure that we have a good link configuration before attempting to | |
| 469 | + * identify the device. | |
| 470 | + */ | |
| 471 | + max9271_configure_i2c(dev->serializer, MAX9271_I2CSLVSH_469NS_234NS | | |
| 472 | + MAX9271_I2CSLVTO_1024US | | |
| 473 | + MAX9271_I2CMSTBT_105KBPS); | |
| 474 | + | |
| 475 | + max9271_configure_gmsl_link(dev->serializer); | |
| 476 | + | |
| 477 | + ret = max9271_verify_id(dev->serializer); | |
| 478 | + if (ret < 0) | |
| 479 | + return ret; | |
| 480 | + | |
| 481 | + ret = max9271_set_address(dev->serializer, dev->addrs[0]); | |
| 482 | + if (ret < 0) | |
| 483 | + return ret; | |
| 484 | + dev->serializer->client->addr = dev->addrs[0]; | |
| 485 | + | |
| 486 | + /* | |
| 487 | + * Reset the sensor by cycling the OV10635 reset signal connected to the | |
| 488 | + * MAX9271 GPIO1 and verify communication with the OV10635. | |
| 489 | + */ | |
| 490 | + max9271_clear_gpios(dev->serializer, MAX9271_GPIO1OUT); | |
| 491 | + usleep_range(10000, 15000); | |
| 492 | + max9271_set_gpios(dev->serializer, MAX9271_GPIO1OUT); | |
| 493 | + usleep_range(10000, 15000); | |
| 494 | + | |
| 495 | +again: | |
| 496 | + ret = ov10635_read16(dev, OV10635_PID); | |
| 497 | + if (ret < 0) { | |
| 498 | + if (retry--) | |
| 499 | + goto again; | |
| 500 | + | |
| 501 | + dev_err(dev->dev, "OV10635 ID read failed (%d)\n", | |
| 502 | + ret); | |
| 503 | + return -ENXIO; | |
| 504 | + } | |
| 505 | + | |
| 506 | + if (ret != OV10635_VERSION) { | |
| 507 | + if (retry--) | |
| 508 | + goto again; | |
| 509 | + | |
| 510 | + dev_err(dev->dev, "OV10635 ID mismatch (0x%04x)\n", | |
| 511 | + ret); | |
| 512 | + return -ENXIO; | |
| 513 | + } | |
| 514 | + | |
| 515 | + /* Change the sensor I2C address. */ | |
| 516 | + ret = ov10635_write(dev, OV10635_SC_CMMN_SCCB_ID, | |
| 517 | + (dev->addrs[1] << 1) | | |
| 518 | + OV10635_SC_CMMN_SCCB_ID_SELECT); | |
| 519 | + if (ret < 0) { | |
| 520 | + dev_err(dev->dev, | |
| 521 | + "OV10635 I2C address change failed (%d)\n", ret); | |
| 522 | + return ret; | |
| 523 | + } | |
| 524 | + dev->sensor->addr = dev->addrs[1]; | |
| 525 | + usleep_range(3500, 5000); | |
| 526 | + | |
| 527 | + /* Program the 0V10635 initial configuration. */ | |
| 528 | + ret = ov10635_set_regs(dev, ov10635_regs_wizard, | |
| 529 | + ARRAY_SIZE(ov10635_regs_wizard)); | |
| 530 | + if (ret) | |
| 531 | + return ret; | |
| 532 | + | |
| 533 | + dev_info(dev->dev, "Identified MAX9271 + OV10635 device\n"); | |
| 534 | + | |
| 535 | + return 0; | |
| 536 | +} | |
| 537 | + | |
| 538 | +static int rdacm20_probe(struct i2c_client *client) | |
| 539 | +{ | |
| 540 | + struct rdacm20_device *dev; | |
| 541 | + struct fwnode_handle *ep; | |
| 542 | + int ret; | |
| 543 | + | |
| 544 | + dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL); | |
| 545 | + if (!dev) | |
| 546 | + return -ENOMEM; | |
| 547 | + dev->dev = &client->dev; | |
| 548 | + | |
| 549 | + dev->serializer = devm_kzalloc(&client->dev, sizeof(*dev->serializer), | |
| 550 | + GFP_KERNEL); | |
| 551 | + if (!dev->serializer) | |
| 552 | + return -ENOMEM; | |
| 553 | + | |
| 554 | + dev->serializer->client = client; | |
| 555 | + | |
| 556 | + ret = of_property_read_u32_array(client->dev.of_node, "reg", | |
| 557 | + dev->addrs, 2); | |
| 558 | + if (ret < 0) { | |
| 559 | + dev_err(dev->dev, "Invalid DT reg property: %d\n", ret); | |
| 560 | + return -EINVAL; | |
| 561 | + } | |
| 562 | + | |
| 563 | + /* Create the dummy I2C client for the sensor. */ | |
| 564 | + dev->sensor = i2c_new_dummy_device(client->adapter, | |
| 565 | + OV10635_I2C_ADDRESS); | |
| 566 | + if (IS_ERR(dev->sensor)) { | |
| 567 | + ret = PTR_ERR(dev->sensor); | |
| 568 | + goto error; | |
| 569 | + } | |
| 570 | + | |
| 571 | + /* Initialize the hardware. */ | |
| 572 | + ret = rdacm20_initialize(dev); | |
| 573 | + if (ret < 0) | |
| 574 | + goto error; | |
| 575 | + | |
| 576 | + /* Initialize and register the subdevice. */ | |
| 577 | + v4l2_i2c_subdev_init(&dev->sd, client, &rdacm20_subdev_ops); | |
| 578 | + dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | |
| 579 | + | |
| 580 | + v4l2_ctrl_handler_init(&dev->ctrls, 1); | |
| 581 | + v4l2_ctrl_new_std(&dev->ctrls, NULL, V4L2_CID_PIXEL_RATE, | |
| 582 | + OV10635_PIXEL_RATE, OV10635_PIXEL_RATE, 1, | |
| 583 | + OV10635_PIXEL_RATE); | |
| 584 | + dev->sd.ctrl_handler = &dev->ctrls; | |
| 585 | + | |
| 586 | + ret = dev->ctrls.error; | |
| 587 | + if (ret) | |
| 588 | + goto error_free_ctrls; | |
| 589 | + | |
| 590 | + dev->pad.flags = MEDIA_PAD_FL_SOURCE; | |
| 591 | + dev->sd.entity.flags |= MEDIA_ENT_F_CAM_SENSOR; | |
| 592 | + ret = media_entity_pads_init(&dev->sd.entity, 1, &dev->pad); | |
| 593 | + if (ret < 0) | |
| 594 | + goto error_free_ctrls; | |
| 595 | + | |
| 596 | + ep = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL); | |
| 597 | + if (!ep) { | |
| 598 | + dev_err(&client->dev, | |
| 599 | + "Unable to get endpoint in node %pOF\n", | |
| 600 | + client->dev.of_node); | |
| 601 | + ret = -ENOENT; | |
| 602 | + goto error_free_ctrls; | |
| 603 | + } | |
| 604 | + dev->sd.fwnode = ep; | |
| 605 | + | |
| 606 | + ret = v4l2_async_register_subdev(&dev->sd); | |
| 607 | + if (ret) | |
| 608 | + goto error_put_node; | |
| 609 | + | |
| 610 | + return 0; | |
| 611 | + | |
| 612 | +error_put_node: | |
| 613 | + fwnode_handle_put(ep); | |
| 614 | +error_free_ctrls: | |
| 615 | + v4l2_ctrl_handler_free(&dev->ctrls); | |
| 616 | +error: | |
| 617 | + media_entity_cleanup(&dev->sd.entity); | |
| 618 | + if (dev->sensor) | |
| 619 | + i2c_unregister_device(dev->sensor); | |
| 620 | + | |
| 621 | + dev_err(&client->dev, "probe failed\n"); | |
| 622 | + | |
| 623 | + return ret; | |
| 624 | +} | |
| 625 | + | |
| 626 | +static int rdacm20_remove(struct i2c_client *client) | |
| 627 | +{ | |
| 628 | + struct rdacm20_device *dev = i2c_to_rdacm20(client); | |
| 629 | + | |
| 630 | + fwnode_handle_put(dev->sd.fwnode); | |
| 631 | + v4l2_async_unregister_subdev(&dev->sd); | |
| 632 | + v4l2_ctrl_handler_free(&dev->ctrls); | |
| 633 | + media_entity_cleanup(&dev->sd.entity); | |
| 634 | + i2c_unregister_device(dev->sensor); | |
| 635 | + | |
| 636 | + return 0; | |
| 637 | +} | |
| 638 | + | |
| 639 | +static void rdacm20_shutdown(struct i2c_client *client) | |
| 640 | +{ | |
| 641 | + struct rdacm20_device *dev = i2c_to_rdacm20(client); | |
| 642 | + | |
| 643 | + /* make sure stream off during shutdown (reset/reboot) */ | |
| 644 | + rdacm20_s_stream(&dev->sd, 0); | |
| 645 | +} | |
| 646 | + | |
| 647 | +static const struct of_device_id rdacm20_of_ids[] = { | |
| 648 | + { .compatible = "imi,rdacm20", }, | |
| 649 | + { } | |
| 650 | +}; | |
| 651 | +MODULE_DEVICE_TABLE(of, rdacm20_of_ids); | |
| 652 | + | |
| 653 | +static struct i2c_driver rdacm20_i2c_driver = { | |
| 654 | + .driver = { | |
| 655 | + .name = "rdacm20", | |
| 656 | + .of_match_table = rdacm20_of_ids, | |
| 657 | + }, | |
| 658 | + .probe_new = rdacm20_probe, | |
| 659 | + .remove = rdacm20_remove, | |
| 660 | + .shutdown = rdacm20_shutdown, | |
| 661 | +}; | |
| 662 | + | |
| 663 | +module_i2c_driver(rdacm20_i2c_driver); | |
| 664 | + | |
| 665 | +MODULE_DESCRIPTION("GMSL Camera driver for RDACM20"); | |
| 666 | +MODULE_AUTHOR("Vladimir Barinov"); | |
| 667 | +MODULE_LICENSE("GPL"); | |
| @@ -197,7 +197,7 @@ static int __s5k6a3_power_on(struct s5k6a3 *sensor) · 196 unmodified lines | ||
| 197 | 197 | |
| 198 | 198 | ret = pm_runtime_get(sensor->dev); |
| 199 | 199 | if (ret < 0) |
| 200 | − return ret; | |
| 200 | + goto error_rpm_put; | |
| 201 | 201 | |
| 202 | 202 | ret = regulator_enable(sensor->supplies[i].consumer); |
| 203 | 203 | if (ret < 0) |
| @@ -508,9 +508,7 @@ static int smiapp_set_ctrl(struct v4l2_ctrl *ctrl) · 507 unmodified lines | ||
| 508 | 508 | break; |
| 509 | 509 | } |
| 510 | 510 | |
| 511 | − pm_runtime_get_noresume(&client->dev); | |
| 512 | − pm_status = pm_runtime_get_if_in_use(&client->dev); | |
| 513 | − pm_runtime_put_noidle(&client->dev); | |
| 511 | + pm_status = pm_runtime_get_if_active(&client->dev, true); | |
| 514 | 512 | if (!pm_status) |
| 515 | 513 | return 0; |
| 516 | 514 | |
| @@ -3103,6 +3101,7 @@ static int smiapp_probe(struct i2c_client *client) · 2586 unmodified lines | ||
| 3103 | 3101 | return 0; |
| 3104 | 3102 | |
| 3105 | 3103 | out_disable_runtime_pm: |
| 3104 | + pm_runtime_put_noidle(&client->dev); | |
| 3106 | 3105 | pm_runtime_disable(&client->dev); |
| 3107 | 3106 | |
| 3108 | 3107 | out_media_entity_cleanup: |
| @@ -1664,8 +1664,10 @@ static int tvp5150_registered(struct v4l2_subdev *sd) · 1663 unmodified lines | ||
| 1664 | 1664 | return 0; |
| 1665 | 1665 | |
| 1666 | 1666 | err: |
| 1667 | − for (i = 0; i < decoder->connectors_num; i++) | |
| 1667 | + for (i = 0; i < decoder->connectors_num; i++) { | |
| 1668 | 1668 | media_device_unregister_entity(&decoder->connectors[i].ent); |
| 1669 | + media_entity_cleanup(&decoder->connectors[i].ent); | |
| 1670 | + } | |
| 1669 | 1671 | return ret; |
| 1670 | 1672 | #endif |
| 1671 | 1673 | |
| @@ -2248,8 +2250,10 @@ static int tvp5150_remove(struct i2c_client *c) · 576 unmodified lines | ||
| 2248 | 2250 | |
| 2249 | 2251 | for (i = 0; i < decoder->connectors_num; i++) |
| 2250 | 2252 | v4l2_fwnode_connector_free(&decoder->connectors[i].base); |
| 2251 | − for (i = 0; i < decoder->connectors_num; i++) | |
| 2253 | + for (i = 0; i < decoder->connectors_num; i++) { | |
| 2252 | 2254 | media_device_unregister_entity(&decoder->connectors[i].ent); |
| 2255 | + media_entity_cleanup(&decoder->connectors[i].ent); | |
| 2256 | + } | |
| 2253 | 2257 | v4l2_async_unregister_subdev(sd); |
| 2254 | 2258 | v4l2_ctrl_handler_free(&decoder->hdl); |
| 2255 | 2259 | pm_runtime_disable(&c->dev); |
| @@ -296,9 +296,18 @@ int media_request_alloc(struct media_device *mdev, int *alloc_fd) · 295 unmodified lines | ||
| 296 | 296 | if (WARN_ON(!mdev->ops->req_alloc ^ !mdev->ops->req_free)) |
| 297 | 297 | return -ENOMEM; |
| 298 | 298 | |
| 299 | + if (mdev->ops->req_alloc) | |
| 300 | + req = mdev->ops->req_alloc(mdev); | |
| 301 | + else | |
| 302 | + req = kzalloc(sizeof(*req), GFP_KERNEL); | |
| 303 | + if (!req) | |
| 304 | + return -ENOMEM; | |
| 305 | + | |
| 299 | 306 | fd = get_unused_fd_flags(O_CLOEXEC); |
| 300 | − if (fd < 0) | |
| 301 | − return fd; | |
| 307 | + if (fd < 0) { | |
| 308 | + ret = fd; | |
| 309 | + goto err_free_req; | |
| 310 | + } | |
| 302 | 311 | |
| 303 | 312 | filp = anon_inode_getfile("request", &request_fops, NULL, O_CLOEXEC); |
| 304 | 313 | if (IS_ERR(filp)) { |
| @@ -306,15 +315,6 @@ int media_request_alloc(struct media_device *mdev, int *alloc_fd) · 1 unmodified line | ||
| 306 | 315 | goto err_put_fd; |
| 307 | 316 | } |
| 308 | 317 | |
| 309 | − if (mdev->ops->req_alloc) | |
| 310 | − req = mdev->ops->req_alloc(mdev); | |
| 311 | − else | |
| 312 | − req = kzalloc(sizeof(*req), GFP_KERNEL); | |
| 313 | − if (!req) { | |
| 314 | − ret = -ENOMEM; | |
| 315 | − goto err_fput; | |
| 316 | − } | |
| 317 | − | |
| 318 | 318 | filp->private_data = req; |
| 319 | 319 | req->mdev = mdev; |
| 320 | 320 | req->state = MEDIA_REQUEST_STATE_IDLE; |
| @@ -336,12 +336,15 @@ int media_request_alloc(struct media_device *mdev, int *alloc_fd) · 15 unmodified lines | ||
| 336 | 336 | |
| 337 | 337 | return 0; |
| 338 | 338 | |
| 339 | −err_fput: | |
| 340 | − fput(filp); | |
| 341 | − | |
| 342 | 339 | err_put_fd: |
| 343 | 340 | put_unused_fd(fd); |
| 344 | 341 | |
| 342 | +err_free_req: | |
| 343 | + if (mdev->ops->req_free) | |
| 344 | + mdev->ops->req_free(req); | |
| 345 | + else | |
| 346 | + kfree(req); | |
| 347 | + | |
| 345 | 348 | return ret; |
| 346 | 349 | } |
| 347 | 350 | |
| @@ -35,7 +35,7 @@ static struct cx18_card_tuner_i2c cx18_i2c_nxp = { · 34 unmodified lines | ||
| 35 | 35 | .tv = { 0x61, 0x60, I2C_CLIENT_END }, |
| 36 | 36 | }; |
| 37 | 37 | |
| 38 | −/* Please add new PCI IDs to: http://pci-ids.ucw.cz/ | |
| 38 | +/* Please add new PCI IDs to: https://pci-ids.ucw.cz/ | |
| 39 | 39 | This keeps the PCI ID database up to date. Note that the entries |
| 40 | 40 | must be added under vendor 0x4444 (Conexant) as subsystem IDs. |
| 41 | 41 | New vendor IDs should still be added to the vendor ID list. */ |
| @@ -2235,9 +2235,6 @@ static struct pci_driver cx23885_pci_driver = { · 2234 unmodified lines | ||
| 2235 | 2235 | .id_table = cx23885_pci_tbl, |
| 2236 | 2236 | .probe = cx23885_initdev, |
| 2237 | 2237 | .remove = cx23885_finidev, |
| 2238 | − /* TODO */ | |
| 2239 | − .suspend = NULL, | |
| 2240 | − .resume = NULL, | |
| 2241 | 2238 | }; |
| 2242 | 2239 | |
| 2243 | 2240 | static int __init cx23885_init(void) |
| @@ -175,19 +175,6 @@ static inline u16 count_to_clock_divider(unsigned int d) · 174 unmodified lines | ||
| 175 | 175 | return (u16) d; |
| 176 | 176 | } |
| 177 | 177 | |
| 178 | −static inline u16 ns_to_clock_divider(unsigned int ns) | |
| 179 | −{ | |
| 180 | − return count_to_clock_divider( | |
| 181 | − DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ / 1000000 * ns, 1000)); | |
| 182 | −} | |
| 183 | − | |
| 184 | −static inline unsigned int clock_divider_to_ns(unsigned int divider) | |
| 185 | −{ | |
| 186 | − /* Period of the Rx or Tx clock in ns */ | |
| 187 | − return DIV_ROUND_CLOSEST((divider + 1) * 1000, | |
| 188 | − CX23888_IR_REFCLK_FREQ / 1000000); | |
| 189 | −} | |
| 190 | − | |
| 191 | 178 | static inline u16 carrier_freq_to_clock_divider(unsigned int freq) |
| 192 | 179 | { |
| 193 | 180 | return count_to_clock_divider( |
| @@ -199,13 +186,6 @@ static inline unsigned int clock_divider_to_carrier_freq(unsigned int divider) · 5 unmodified lines | ||
| 199 | 186 | return DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, (divider + 1) * 16); |
| 200 | 187 | } |
| 201 | 188 | |
| 202 | −static inline u16 freq_to_clock_divider(unsigned int freq, | |
| 203 | − unsigned int rollovers) | |
| 204 | −{ | |
| 205 | − return count_to_clock_divider( | |
| 206 | − DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, freq * rollovers)); | |
| 207 | −} | |
| 208 | − | |
| 209 | 189 | static inline unsigned int clock_divider_to_freq(unsigned int divider, |
| 210 | 190 | unsigned int rollovers) |
| 211 | 191 | { |
| @@ -1374,9 +1374,6 @@ static struct pci_driver cx25821_pci_driver = { · 1373 unmodified lines | ||
| 1374 | 1374 | .id_table = cx25821_pci_tbl, |
| 1375 | 1375 | .probe = cx25821_initdev, |
| 1376 | 1376 | .remove = cx25821_finidev, |
| 1377 | − /* TODO */ | |
| 1378 | − .suspend = NULL, | |
| 1379 | − .resume = NULL, | |
| 1380 | 1377 | }; |
| 1381 | 1378 | |
| 1382 | 1379 | static int __init cx25821_init(void) |
| @@ -385,8 +385,7 @@ static int start_video_dma(struct cx8800_dev *dev, · 384 unmodified lines | ||
| 385 | 385 | return 0; |
| 386 | 386 | } |
| 387 | 387 | |
| 388 | −#ifdef CONFIG_PM | |
| 389 | −static int stop_video_dma(struct cx8800_dev *dev) | |
| 388 | +static int __maybe_unused stop_video_dma(struct cx8800_dev *dev) | |
| 390 | 389 | { |
| 391 | 390 | struct cx88_core *core = dev->core; |
| 392 | 391 | |
| @@ -402,8 +401,8 @@ static int stop_video_dma(struct cx8800_dev *dev) · 9 unmodified lines | ||
| 402 | 401 | return 0; |
| 403 | 402 | } |
| 404 | 403 | |
| 405 | −static int restart_video_queue(struct cx8800_dev *dev, | |
| 406 | − struct cx88_dmaqueue *q) | |
| 404 | +static int __maybe_unused restart_video_queue(struct cx8800_dev *dev, | |
| 405 | + struct cx88_dmaqueue *q) | |
| 407 | 406 | { |
| 408 | 407 | struct cx88_buffer *buf; |
| 409 | 408 | |
| @@ -415,7 +414,6 @@ static int restart_video_queue(struct cx8800_dev *dev, · 5 unmodified lines | ||
| 415 | 414 | } |
| 416 | 415 | return 0; |
| 417 | 416 | } |
| 418 | −#endif | |
| 419 | 417 | |
| 420 | 418 | /* ------------------------------------------------------------------ */ |
| 421 | 419 | |
| @@ -1551,10 +1549,9 @@ static void cx8800_finidev(struct pci_dev *pci_dev) · 1129 unmodified lines | ||
| 1551 | 1549 | kfree(dev); |
| 1552 | 1550 | } |
| 1553 | 1551 | |
| 1554 | −#ifdef CONFIG_PM | |
| 1555 | −static int cx8800_suspend(struct pci_dev *pci_dev, pm_message_t state) | |
| 1552 | +static int __maybe_unused cx8800_suspend(struct device *dev_d) | |
| 1556 | 1553 | { |
| 1557 | − struct cx8800_dev *dev = pci_get_drvdata(pci_dev); | |
| 1554 | + struct cx8800_dev *dev = dev_get_drvdata(dev_d); | |
| 1558 | 1555 | struct cx88_core *core = dev->core; |
| 1559 | 1556 | unsigned long flags; |
| 1560 | 1557 | |
| @@ -1575,40 +1572,17 @@ static int cx8800_suspend(struct pci_dev *pci_dev, pm_message_t state) · 14 unmodified lines | ||
| 1575 | 1572 | /* FIXME -- shutdown device */ |
| 1576 | 1573 | cx88_shutdown(core); |
| 1577 | 1574 | |
| 1578 | − pci_save_state(pci_dev); | |
| 1579 | − if (pci_set_power_state(pci_dev, | |
| 1580 | − pci_choose_state(pci_dev, state)) != 0) { | |
| 1581 | − pci_disable_device(pci_dev); | |
| 1582 | − dev->state.disabled = 1; | |
| 1583 | − } | |
| 1575 | + dev->state.disabled = 1; | |
| 1584 | 1576 | return 0; |
| 1585 | 1577 | } |
| 1586 | 1578 | |
| 1587 | −static int cx8800_resume(struct pci_dev *pci_dev) | |
| 1579 | +static int __maybe_unused cx8800_resume(struct device *dev_d) | |
| 1588 | 1580 | { |
| 1589 | − struct cx8800_dev *dev = pci_get_drvdata(pci_dev); | |
| 1581 | + struct cx8800_dev *dev = dev_get_drvdata(dev_d); | |
| 1590 | 1582 | struct cx88_core *core = dev->core; |
| 1591 | 1583 | unsigned long flags; |
| 1592 | − int err; | |
| 1593 | 1584 | |
| 1594 | − if (dev->state.disabled) { | |
| 1595 | − err = pci_enable_device(pci_dev); | |
| 1596 | − if (err) { | |
| 1597 | − pr_err("can't enable device\n"); | |
| 1598 | − return err; | |
| 1599 | − } | |
| 1600 | − | |
| 1601 | − dev->state.disabled = 0; | |
| 1602 | − } | |
| 1603 | − err = pci_set_power_state(pci_dev, PCI_D0); | |
| 1604 | − if (err) { | |
| 1605 | − pr_err("can't set power state\n"); | |
| 1606 | − pci_disable_device(pci_dev); | |
| 1607 | − dev->state.disabled = 1; | |
| 1608 | − | |
| 1609 | − return err; | |
| 1610 | − } | |
| 1611 | − pci_restore_state(pci_dev); | |
| 1585 | + dev->state.disabled = 0; | |
| 1612 | 1586 | |
| 1613 | 1587 | /* FIXME: re-initialize hardware */ |
| 1614 | 1588 | cx88_reset(core); |
| @@ -1631,7 +1605,6 @@ static int cx8800_resume(struct pci_dev *pci_dev) · 16 unmodified lines | ||
| 1631 | 1605 | |
| 1632 | 1606 | return 0; |
| 1633 | 1607 | } |
| 1634 | −#endif | |
| 1635 | 1608 | |
| 1636 | 1609 | /* ----------------------------------------------------------- */ |
| 1637 | 1610 | |
| @@ -1647,15 +1620,14 @@ static const struct pci_device_id cx8800_pci_tbl[] = { · 9 unmodified lines | ||
| 1647 | 1620 | }; |
| 1648 | 1621 | MODULE_DEVICE_TABLE(pci, cx8800_pci_tbl); |
| 1649 | 1622 | |
| 1623 | +static SIMPLE_DEV_PM_OPS(cx8800_pm_ops, cx8800_suspend, cx8800_resume); | |
| 1624 | + | |
| 1650 | 1625 | static struct pci_driver cx8800_pci_driver = { |
| 1651 | − .name = "cx8800", | |
| 1652 | − .id_table = cx8800_pci_tbl, | |
| 1653 | − .probe = cx8800_initdev, | |
| 1654 | − .remove = cx8800_finidev, | |
| 1655 | −#ifdef CONFIG_PM | |
| 1656 | − .suspend = cx8800_suspend, | |
| 1657 | − .resume = cx8800_resume, | |
| 1658 | −#endif | |
| 1626 | + .name = "cx8800", | |
| 1627 | + .id_table = cx8800_pci_tbl, | |
| 1628 | + .probe = cx8800_initdev, | |
| 1629 | + .remove = cx8800_finidev, | |
| 1630 | + .driver.pm = &cx8800_pm_ops, | |
| 1659 | 1631 | }; |
| 1660 | 1632 | |
| 1661 | 1633 | module_pci_driver(cx8800_pci_driver); |
| @@ -426,7 +426,7 @@ static int dt3155_init_board(struct dt3155_priv *pd) · 425 unmodified lines | ||
| 426 | 426 | iowrite32(FIFO_EN | SRST, pd->regs + CSR1); |
| 427 | 427 | iowrite32(0xEEEEEE01, pd->regs + EVEN_PIXEL_FMT); |
| 428 | 428 | iowrite32(0xEEEEEE01, pd->regs + ODD_PIXEL_FMT); |
| 429 | − iowrite32(0x00000020, pd->regs + FIFO_TRIGER); | |
| 429 | + iowrite32(0x00000020, pd->regs + FIFO_TRIGGER); | |
| 430 | 430 | iowrite32(0x00000103, pd->regs + XFER_MODE); |
| 431 | 431 | iowrite32(0, pd->regs + RETRY_WAIT_CNT); |
| 432 | 432 | iowrite32(0, pd->regs + INT_CSR); |
| @@ -31,7 +31,7 @@ · 30 unmodified lines | ||
| 31 | 31 | #define ODD_DMA_STRIDE 0x24 |
| 32 | 32 | #define EVEN_PIXEL_FMT 0x30 |
| 33 | 33 | #define ODD_PIXEL_FMT 0x34 |
| 34 | −#define FIFO_TRIGER 0x38 | |
| 34 | +#define FIFO_TRIGGER 0x38 | |
| 35 | 35 | #define XFER_MODE 0x3C |
| 36 | 36 | #define CSR1 0x40 |
| 37 | 37 | #define RETRY_WAIT_CNT 0x44 |
| @@ -53,7 +53,7 @@ static struct ivtv_card_tuner_i2c ivtv_i2c_tda8290 = { · 52 unmodified lines | ||
| 53 | 53 | |
| 54 | 54 | /********************** card configuration *******************************/ |
| 55 | 55 | |
| 56 | −/* Please add new PCI IDs to: http://pci-ids.ucw.cz/ | |
| 56 | +/* Please add new PCI IDs to: https://pci-ids.ucw.cz/ | |
| 57 | 57 | This keeps the PCI ID database up to date. Note that the entries |
| 58 | 58 | must be added under vendor 0x4444 (Conexant) as subsystem IDs. |
| 59 | 59 | New vendor IDs should still be added to the vendor ID list. */ |
| @@ -1528,19 +1528,16 @@ static const struct v4l2_ctrl_ops meye_ctrl_ops = { · 1527 unmodified lines | ||
| 1528 | 1528 | .s_ctrl = meye_s_ctrl, |
| 1529 | 1529 | }; |
| 1530 | 1530 | |
| 1531 | −#ifdef CONFIG_PM | |
| 1532 | −static int meye_suspend(struct pci_dev *pdev, pm_message_t state) | |
| 1531 | +static int __maybe_unused meye_suspend(struct device *dev) | |
| 1533 | 1532 | { |
| 1534 | − pci_save_state(pdev); | |
| 1535 | 1533 | meye.pm_mchip_mode = meye.mchip_mode; |
| 1536 | 1534 | mchip_hic_stop(); |
| 1537 | 1535 | mchip_set(MCHIP_MM_INTA, 0x0); |
| 1538 | 1536 | return 0; |
| 1539 | 1537 | } |
| 1540 | 1538 | |
| 1541 | −static int meye_resume(struct pci_dev *pdev) | |
| 1539 | +static int __maybe_unused meye_resume(struct device *dev) | |
| 1542 | 1540 | { |
| 1543 | − pci_restore_state(pdev); | |
| 1544 | 1541 | pci_write_config_word(meye.mchip_dev, MCHIP_PCI_SOFTRESET_SET, 1); |
| 1545 | 1542 | |
| 1546 | 1543 | mchip_delay(MCHIP_HIC_CMD, 0); |
| @@ -1562,7 +1559,6 @@ static int meye_resume(struct pci_dev *pdev) · 15 unmodified lines | ||
| 1562 | 1559 | } |
| 1563 | 1560 | return 0; |
| 1564 | 1561 | } |
| 1565 | −#endif | |
| 1566 | 1562 | |
| 1567 | 1563 | static int meye_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) |
| 1568 | 1564 | { |
| @@ -1788,15 +1784,14 @@ static const struct pci_device_id meye_pci_tbl[] = { · 219 unmodified lines | ||
| 1788 | 1784 | |
| 1789 | 1785 | MODULE_DEVICE_TABLE(pci, meye_pci_tbl); |
| 1790 | 1786 | |
| 1787 | +static SIMPLE_DEV_PM_OPS(meye_pm_ops, meye_suspend, meye_resume); | |
| 1788 | + | |
| 1791 | 1789 | static struct pci_driver meye_driver = { |
| 1792 | 1790 | .name = "meye", |
| 1793 | 1791 | .id_table = meye_pci_tbl, |
| 1794 | 1792 | .probe = meye_probe, |
| 1795 | 1793 | .remove = meye_remove, |
| 1796 | −#ifdef CONFIG_PM | |
| 1797 | − .suspend = meye_suspend, | |
| 1798 | − .resume = meye_resume, | |
| 1799 | −#endif | |
| 1794 | + .driver.pm = &meye_pm_ops, | |
| 1800 | 1795 | }; |
| 1801 | 1796 | |
| 1802 | 1797 | static int __init meye_init(void) |
| @@ -305,9 +305,7 @@ struct meye { · 304 unmodified lines | ||
| 305 | 305 | u16 colour; |
| 306 | 306 | struct meye_params params; /* additional parameters */ |
| 307 | 307 | unsigned long in_use; /* set to 1 if the device is in use */ |
| 308 | −#ifdef CONFIG_PM | |
| 309 | 308 | u8 pm_mchip_mode; /* old mchip mode */ |
| 310 | −#endif | |
| 311 | 309 | }; |
| 312 | 310 | |
| 313 | 311 | #endif |
| @@ -1539,9 +1539,6 @@ static struct pci_driver saa7164_pci_driver = { · 1538 unmodified lines | ||
| 1539 | 1539 | .id_table = saa7164_pci_tbl, |
| 1540 | 1540 | .probe = saa7164_initdev, |
| 1541 | 1541 | .remove = saa7164_finidev, |
| 1542 | − /* TODO */ | |
| 1543 | − .suspend = NULL, | |
| 1544 | − .resume = NULL, | |
| 1545 | 1542 | }; |
| 1546 | 1543 | |
| 1547 | 1544 | static int __init saa7164_init(void) |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 2 | 2 | /* |
| 3 | − * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> | |
| 3 | + * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> | |
| 4 | 4 | * |
| 5 | 5 | * Original author: |
| 6 | 6 | * Ben Collins <bcollins@ubuntu.com> |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 2 | 2 | /* |
| 3 | − * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> | |
| 3 | + * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> | |
| 4 | 4 | * |
| 5 | 5 | * Original author: |
| 6 | 6 | * Ben Collins <bcollins@ubuntu.com> |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 2 | 2 | /* |
| 3 | − * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> | |
| 3 | + * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> | |
| 4 | 4 | * |
| 5 | 5 | * Original author: |
| 6 | 6 | * Ben Collins <bcollins@ubuntu.com> |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 2 | 2 | /* |
| 3 | − * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> | |
| 3 | + * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> | |
| 4 | 4 | * |
| 5 | 5 | * Original author: |
| 6 | 6 | * Ben Collins <bcollins@ubuntu.com> |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 2 | 2 | /* |
| 3 | − * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> | |
| 3 | + * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> | |
| 4 | 4 | * |
| 5 | 5 | * Original author: |
| 6 | 6 | * Ben Collins <bcollins@ubuntu.com> |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 2 | 2 | /* |
| 3 | − * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> | |
| 3 | + * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> | |
| 4 | 4 | * |
| 5 | 5 | * Original author: |
| 6 | 6 | * Ben Collins <bcollins@ubuntu.com> |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 2 | 2 | /* |
| 3 | − * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> | |
| 3 | + * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> | |
| 4 | 4 | * |
| 5 | 5 | * Original author: |
| 6 | 6 | * Ben Collins <bcollins@ubuntu.com> |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ |
| 2 | 2 | /* |
| 3 | − * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> | |
| 3 | + * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> | |
| 4 | 4 | * |
| 5 | 5 | * Original author: |
| 6 | 6 | * Ben Collins <bcollins@ubuntu.com> |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ |
| 2 | 2 | /* |
| 3 | − * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> | |
| 3 | + * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> | |
| 4 | 4 | * |
| 5 | 5 | * Original author: |
| 6 | 6 | * Ben Collins <bcollins@ubuntu.com> |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 2 | 2 | /* |
| 3 | − * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> | |
| 3 | + * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> | |
| 4 | 4 | * |
| 5 | 5 | * Original author: |
| 6 | 6 | * Ben Collins <bcollins@ubuntu.com> |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ |
| 2 | 2 | /* |
| 3 | − * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> | |
| 3 | + * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> | |
| 4 | 4 | * |
| 5 | 5 | * Original author: |
| 6 | 6 | * Ben Collins <bcollins@ubuntu.com> |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 2 | 2 | /* |
| 3 | − * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> | |
| 3 | + * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> | |
| 4 | 4 | * |
| 5 | 5 | * Original author: |
| 6 | 6 | * Ben Collins <bcollins@ubuntu.com> |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ |
| 2 | 2 | /* |
| 3 | − * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> | |
| 3 | + * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> | |
| 4 | 4 | * |
| 5 | 5 | * Original author: |
| 6 | 6 | * Ben Collins <bcollins@ubuntu.com> |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 2 | 2 | /* |
| 3 | − * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> | |
| 3 | + * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> | |
| 4 | 4 | * |
| 5 | 5 | * Original author: |
| 6 | 6 | * Ben Collins <bcollins@ubuntu.com> |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 2 | 2 | /* |
| 3 | − * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> | |
| 3 | + * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> | |
| 4 | 4 | * |
| 5 | 5 | * Original author: |
| 6 | 6 | * Ben Collins <bcollins@ubuntu.com> |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ |
| 2 | 2 | /* |
| 3 | − * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> | |
| 3 | + * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> | |
| 4 | 4 | * |
| 5 | 5 | * Original author: |
| 6 | 6 | * Ben Collins <bcollins@ubuntu.com> |
| @@ -1167,21 +1167,18 @@ static void sta2x11_vip_remove_one(struct pci_dev *pdev) · 1166 unmodified lines | ||
| 1167 | 1167 | */ |
| 1168 | 1168 | } |
| 1169 | 1169 | |
| 1170 | −#ifdef CONFIG_PM | |
| 1171 | − | |
| 1172 | 1170 | /** |
| 1173 | 1171 | * sta2x11_vip_suspend - set device into power save mode |
| 1174 | − * @pdev: PCI device | |
| 1175 | − * @state: new state of device | |
| 1172 | + * @dev_d: PCI device | |
| 1176 | 1173 | * |
| 1177 | 1174 | * all relevant registers are saved and an attempt to set a new state is made. |
| 1178 | 1175 | * |
| 1179 | 1176 | * return value: 0 always indicate success, |
| 1180 | 1177 | * even if device could not be disabled. (workaround for hardware problem) |
| 1181 | 1178 | */ |
| 1182 | −static int sta2x11_vip_suspend(struct pci_dev *pdev, pm_message_t state) | |
| 1179 | +static int __maybe_unused sta2x11_vip_suspend(struct device *dev_d) | |
| 1183 | 1180 | { |
| 1184 | − struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); | |
| 1181 | + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev_d); | |
| 1185 | 1182 | struct sta2x11_vip *vip = |
| 1186 | 1183 | container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev); |
| 1187 | 1184 | unsigned long flags; |
| @@ -1198,15 +1195,8 @@ static int sta2x11_vip_suspend(struct pci_dev *pdev, pm_message_t state) · 10 unmodified lines | ||
| 1198 | 1195 | vip->register_save_area[SAVE_COUNT + IRQ_COUNT + i] = |
| 1199 | 1196 | reg_read(vip, registers_to_save[i]); |
| 1200 | 1197 | spin_unlock_irqrestore(&vip->slock, flags); |
| 1201 | − /* save pci state */ | |
| 1202 | − pci_save_state(pdev); | |
| 1203 | − if (pci_set_power_state(pdev, pci_choose_state(pdev, state))) { | |
| 1204 | − /* | |
| 1205 | − * do not call pci_disable_device on sta2x11 because it | |
| 1206 | − * break all other Bus masters on this EP | |
| 1207 | − */ | |
| 1208 | − vip->disabled = 1; | |
| 1209 | − } | |
| 1198 | + | |
| 1199 | + vip->disabled = 1; | |
| 1210 | 1200 | |
| 1211 | 1201 | pr_info("VIP: suspend\n"); |
| 1212 | 1202 | return 0; |
| @@ -1214,45 +1204,23 @@ static int sta2x11_vip_suspend(struct pci_dev *pdev, pm_message_t state) · 1 unmodified line | ||
| 1214 | 1204 | |
| 1215 | 1205 | /** |
| 1216 | 1206 | * sta2x11_vip_resume - resume device operation |
| 1217 | − * @pdev : PCI device | |
| 1218 | − * | |
| 1219 | − * re-enable device, set PCI state to powered and restore registers. | |
| 1220 | − * resume normal device operation afterwards. | |
| 1207 | + * @dev_d : PCI device | |
| 1221 | 1208 | * |
| 1222 | 1209 | * return value: 0, no error. |
| 1223 | 1210 | * |
| 1224 | 1211 | * other, could not set device to power on state. |
| 1225 | 1212 | */ |
| 1226 | −static int sta2x11_vip_resume(struct pci_dev *pdev) | |
| 1213 | +static int __maybe_unused sta2x11_vip_resume(struct device *dev_d) | |
| 1227 | 1214 | { |
| 1228 | − struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); | |
| 1215 | + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev_d); | |
| 1229 | 1216 | struct sta2x11_vip *vip = |
| 1230 | 1217 | container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev); |
| 1231 | 1218 | unsigned long flags; |
| 1232 | − int ret, i; | |
| 1219 | + int i; | |
| 1233 | 1220 | |
| 1234 | 1221 | pr_info("VIP: resume\n"); |
| 1235 | − /* restore pci state */ | |
| 1236 | − if (vip->disabled) { | |
| 1237 | − ret = pci_enable_device(pdev); | |
| 1238 | − if (ret) { | |
| 1239 | − pr_warn("VIP: Can't enable device.\n"); | |
| 1240 | − return ret; | |
| 1241 | − } | |
| 1242 | − vip->disabled = 0; | |
| 1243 | − } | |
| 1244 | − ret = pci_set_power_state(pdev, PCI_D0); | |
| 1245 | − if (ret) { | |
| 1246 | − /* | |
| 1247 | − * do not call pci_disable_device on sta2x11 because it | |
| 1248 | − * break all other Bus masters on this EP | |
| 1249 | − */ | |
| 1250 | − pr_warn("VIP: Can't enable device.\n"); | |
| 1251 | − vip->disabled = 1; | |
| 1252 | − return ret; | |
| 1253 | − } | |
| 1254 | 1222 | |
| 1255 | − pci_restore_state(pdev); | |
| 1223 | + vip->disabled = 0; | |
| 1256 | 1224 | |
| 1257 | 1225 | spin_lock_irqsave(&vip->slock, flags); |
| 1258 | 1226 | for (i = 1; i < SAVE_COUNT; i++) |
| @@ -1266,22 +1234,21 @@ static int sta2x11_vip_resume(struct pci_dev *pdev) · 7 unmodified lines | ||
| 1266 | 1234 | return 0; |
| 1267 | 1235 | } |
| 1268 | 1236 | |
| 1269 | −#endif | |
| 1270 | − | |
| 1271 | 1237 | static const struct pci_device_id sta2x11_vip_pci_tbl[] = { |
| 1272 | 1238 | {PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_VIP)}, |
| 1273 | 1239 | {0,} |
| 1274 | 1240 | }; |
| 1275 | 1241 | |
| 1242 | +static SIMPLE_DEV_PM_OPS(sta2x11_vip_pm_ops, | |
| 1243 | + sta2x11_vip_suspend, | |
| 1244 | + sta2x11_vip_resume); | |
| 1245 | + | |
| 1276 | 1246 | static struct pci_driver sta2x11_vip_driver = { |
| 1277 | 1247 | .name = KBUILD_MODNAME, |
| 1278 | 1248 | .probe = sta2x11_vip_init_one, |
| 1279 | 1249 | .remove = sta2x11_vip_remove_one, |
| 1280 | 1250 | .id_table = sta2x11_vip_pci_tbl, |
| 1281 | −#ifdef CONFIG_PM | |
| 1282 | − .suspend = sta2x11_vip_suspend, | |
| 1283 | − .resume = sta2x11_vip_resume, | |
| 1284 | −#endif | |
| 1251 | + .driver.pm = &sta2x11_vip_pm_ops, | |
| 1285 | 1252 | }; |
| 1286 | 1253 | |
| 1287 | 1254 | static int __init sta2x11_vip_init_module(void) |
| @@ -406,14 +406,15 @@ static void debiirq(unsigned long cookie) · 405 unmodified lines | ||
| 406 | 406 | case DATA_CI_GET: |
| 407 | 407 | { |
| 408 | 408 | u8 *data = av7110->debi_virt; |
| 409 | + u8 data_0 = data[0]; | |
| 409 | 410 | |
| 410 | − if ((data[0] < 2) && data[2] == 0xff) { | |
| 411 | + if (data_0 < 2 && data[2] == 0xff) { | |
| 411 | 412 | int flags = 0; |
| 412 | 413 | if (data[5] > 0) |
| 413 | 414 | flags |= CA_CI_MODULE_PRESENT; |
| 414 | 415 | if (data[5] > 5) |
| 415 | 416 | flags |= CA_CI_MODULE_READY; |
| 416 | − av7110->ci_slot[data[0]].flags = flags; | |
| 417 | + av7110->ci_slot[data_0].flags = flags; | |
| 417 | 418 | } else |
| 418 | 419 | ci_get_data(&av7110->ci_rbuffer, |
| 419 | 420 | av7110->debi_virt, |
| @@ -369,20 +369,25 @@ static int budget_register(struct budget *budget) · 368 unmodified lines | ||
| 369 | 369 | ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &budget->hw_frontend); |
| 370 | 370 | |
| 371 | 371 | if (ret < 0) |
| 372 | − return ret; | |
| 372 | + goto err_release_dmx; | |
| 373 | 373 | |
| 374 | 374 | budget->mem_frontend.source = DMX_MEMORY_FE; |
| 375 | 375 | ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &budget->mem_frontend); |
| 376 | 376 | if (ret < 0) |
| 377 | − return ret; | |
| 377 | + goto err_release_dmx; | |
| 378 | 378 | |
| 379 | 379 | ret = dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, &budget->hw_frontend); |
| 380 | 380 | if (ret < 0) |
| 381 | − return ret; | |
| 381 | + goto err_release_dmx; | |
| 382 | 382 | |
| 383 | 383 | dvb_net_init(&budget->dvb_adapter, &budget->dvb_net, &dvbdemux->dmx); |
| 384 | 384 | |
| 385 | 385 | return 0; |
| 386 | + | |
| 387 | +err_release_dmx: | |
| 388 | + dvb_dmxdev_release(&budget->dmxdev); | |
| 389 | + dvb_dmx_release(&budget->demux); | |
| 390 | + return ret; | |
| 386 | 391 | } |
| 387 | 392 | |
| 388 | 393 | static void budget_unregister(struct budget *budget) |
| @@ -65,7 +65,7 @@ module_param_array(video_nr, int, NULL, 0444); · 64 unmodified lines | ||
| 65 | 65 | MODULE_PARM_DESC(video_nr, "video devices numbers array"); |
| 66 | 66 | |
| 67 | 67 | /* |
| 68 | − * Please add any new PCI IDs to: http://pci-ids.ucw.cz. This keeps | |
| 68 | + * Please add any new PCI IDs to: https://pci-ids.ucw.cz. This keeps | |
| 69 | 69 | * the PCI ID database up to date. Note that the entries must be |
| 70 | 70 | * added under vendor 0x1797 (Techwell Inc.) as subsystem IDs. |
| 71 | 71 | */ |
| @@ -57,7 +57,7 @@ static atomic_t tw68_instance = ATOMIC_INIT(0); · 56 unmodified lines | ||
| 57 | 57 | /* ------------------------------------------------------------------ */ |
| 58 | 58 | |
| 59 | 59 | /* |
| 60 | − * Please add any new PCI IDs to: http://pci-ids.ucw.cz. This keeps | |
| 60 | + * Please add any new PCI IDs to: https://pci-ids.ucw.cz. This keeps | |
| 61 | 61 | * the PCI ID database up to date. Note that the entries must be |
| 62 | 62 | * added under vendor 0x1797 (Techwell Inc.) as subsystem IDs. |
| 63 | 63 | */ |
| @@ -359,10 +359,9 @@ static void tw68_finidev(struct pci_dev *pci_dev) · 295 unmodified lines | ||
| 359 | 359 | v4l2_device_unregister(&dev->v4l2_dev); |
| 360 | 360 | } |
| 361 | 361 | |
| 362 | −#ifdef CONFIG_PM | |
| 363 | − | |
| 364 | −static int tw68_suspend(struct pci_dev *pci_dev , pm_message_t state) | |
| 362 | +static int __maybe_unused tw68_suspend(struct device *dev_d) | |
| 365 | 363 | { |
| 364 | + struct pci_dev *pci_dev = to_pci_dev(dev_d); | |
| 366 | 365 | struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); |
| 367 | 366 | struct tw68_dev *dev = container_of(v4l2_dev, |
| 368 | 367 | struct tw68_dev, v4l2_dev); |
| @@ -373,24 +372,19 @@ static int tw68_suspend(struct pci_dev *pci_dev , pm_message_t state) · 4 unmodified lines | ||
| 373 | 372 | |
| 374 | 373 | synchronize_irq(pci_dev->irq); |
| 375 | 374 | |
| 376 | − pci_save_state(pci_dev); | |
| 377 | − pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state)); | |
| 378 | 375 | vb2_discard_done(&dev->vidq); |
| 379 | 376 | |
| 380 | 377 | return 0; |
| 381 | 378 | } |
| 382 | 379 | |
| 383 | −static int tw68_resume(struct pci_dev *pci_dev) | |
| 380 | +static int __maybe_unused tw68_resume(struct device *dev_d) | |
| 384 | 381 | { |
| 385 | − struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); | |
| 382 | + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev_d); | |
| 386 | 383 | struct tw68_dev *dev = container_of(v4l2_dev, |
| 387 | 384 | struct tw68_dev, v4l2_dev); |
| 388 | 385 | struct tw68_buf *buf; |
| 389 | 386 | unsigned long flags; |
| 390 | 387 | |
| 391 | − pci_set_power_state(pci_dev, PCI_D0); | |
| 392 | − pci_restore_state(pci_dev); | |
| 393 | − | |
| 394 | 388 | /* Do things that are done in tw68_initdev , |
| 395 | 389 | except of initializing memory structures.*/ |
| 396 | 390 | |
| @@ -408,19 +402,17 @@ static int tw68_resume(struct pci_dev *pci_dev) · 11 unmodified lines | ||
| 408 | 402 | |
| 409 | 403 | return 0; |
| 410 | 404 | } |
| 411 | −#endif | |
| 412 | 405 | |
| 413 | 406 | /* ----------------------------------------------------------- */ |
| 414 | 407 | |
| 408 | +static SIMPLE_DEV_PM_OPS(tw68_pm_ops, tw68_suspend, tw68_resume); | |
| 409 | + | |
| 415 | 410 | static struct pci_driver tw68_pci_driver = { |
| 416 | − .name = "tw68", | |
| 417 | − .id_table = tw68_pci_tbl, | |
| 418 | − .probe = tw68_initdev, | |
| 419 | − .remove = tw68_finidev, | |
| 420 | −#ifdef CONFIG_PM | |
| 421 | − .suspend = tw68_suspend, | |
| 422 | − .resume = tw68_resume | |
| 423 | −#endif | |
| 411 | + .name = "tw68", | |
| 412 | + .id_table = tw68_pci_tbl, | |
| 413 | + .probe = tw68_initdev, | |
| 414 | + .remove = tw68_finidev, | |
| 415 | + .driver.pm = &tw68_pm_ops, | |
| 424 | 416 | }; |
| 425 | 417 | |
| 426 | 418 | module_pci_driver(tw68_pci_driver); |
| @@ -26,6 +26,7 @@ · 25 unmodified lines | ||
| 26 | 26 | #include <media/v4l2-ctrls.h> |
| 27 | 27 | #include <media/v4l2-event.h> |
| 28 | 28 | #include <media/v4l2-fwnode.h> |
| 29 | +#include <media/v4l2-rect.h> | |
| 29 | 30 | |
| 30 | 31 | #include "am437x-vpfe.h" |
| 31 | 32 | |
| @@ -1987,20 +1988,6 @@ vpfe_g_selection(struct file *file, void *fh, struct v4l2_selection *s) · 1955 unmodified lines | ||
| 1987 | 1988 | return 0; |
| 1988 | 1989 | } |
| 1989 | 1990 | |
| 1990 | −static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b) | |
| 1991 | −{ | |
| 1992 | − if (a->left < b->left || a->top < b->top) | |
| 1993 | − return 0; | |
| 1994 | − | |
| 1995 | − if (a->left + a->width > b->left + b->width) | |
| 1996 | − return 0; | |
| 1997 | − | |
| 1998 | − if (a->top + a->height > b->top + b->height) | |
| 1999 | − return 0; | |
| 2000 | − | |
| 2001 | − return 1; | |
| 2002 | −} | |
| 2003 | − | |
| 2004 | 1991 | static int |
| 2005 | 1992 | vpfe_s_selection(struct file *file, void *fh, struct v4l2_selection *s) |
| 2006 | 1993 | { |
| @@ -2025,10 +2012,10 @@ vpfe_s_selection(struct file *file, void *fh, struct v4l2_selection *s) · 18 unmodified lines | ||
| 2025 | 2012 | r.left = clamp_t(unsigned int, r.left, 0, cr.width - r.width); |
| 2026 | 2013 | r.top = clamp_t(unsigned int, r.top, 0, cr.height - r.height); |
| 2027 | 2014 | |
| 2028 | − if (s->flags & V4L2_SEL_FLAG_LE && !enclosed_rectangle(&r, &s->r)) | |
| 2015 | + if (s->flags & V4L2_SEL_FLAG_LE && !v4l2_rect_enclosed(&r, &s->r)) | |
| 2029 | 2016 | return -ERANGE; |
| 2030 | 2017 | |
| 2031 | − if (s->flags & V4L2_SEL_FLAG_GE && !enclosed_rectangle(&s->r, &r)) | |
| 2018 | + if (s->flags & V4L2_SEL_FLAG_GE && !v4l2_rect_enclosed(&s->r, &r)) | |
| 2032 | 2019 | return -ERANGE; |
| 2033 | 2020 | |
| 2034 | 2021 | s->r = vpfe->crop = r; |
| @@ -225,9 +225,6 @@ const u32 isc_gamma_table[GAMMA_MAX + 1][GAMMA_ENTRIES] = { · 224 unmodified lines | ||
| 225 | 225 | (((mbus_code) == MEDIA_BUS_FMT_Y10_1X10) | \ |
| 226 | 226 | (((mbus_code) == MEDIA_BUS_FMT_Y8_1X8))) |
| 227 | 227 | |
| 228 | −#define ISC_CTRL_ISC_TO_V4L2(x) ((x) == ISC_WB_O_ZERO_VAL ? 0 : (x)) | |
| 229 | −#define ISC_CTRL_V4L2_TO_ISC(x) ((x) ? (x) : ISC_WB_O_ZERO_VAL) | |
| 230 | − | |
| 231 | 228 | static inline void isc_update_v4l2_ctrls(struct isc_device *isc) |
| 232 | 229 | { |
| 233 | 230 | struct isc_ctrls *ctrls = &isc->ctrls; |
| @@ -238,14 +235,10 @@ static inline void isc_update_v4l2_ctrls(struct isc_device *isc) · 4 unmodified lines | ||
| 238 | 235 | v4l2_ctrl_s_ctrl(isc->gr_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GR]); |
| 239 | 236 | v4l2_ctrl_s_ctrl(isc->gb_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GB]); |
| 240 | 237 | |
| 241 | − v4l2_ctrl_s_ctrl(isc->r_off_ctrl, | |
| 242 | − ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_R])); | |
| 243 | − v4l2_ctrl_s_ctrl(isc->b_off_ctrl, | |
| 244 | − ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_B])); | |
| 245 | − v4l2_ctrl_s_ctrl(isc->gr_off_ctrl, | |
| 246 | − ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_GR])); | |
| 247 | − v4l2_ctrl_s_ctrl(isc->gb_off_ctrl, | |
| 248 | − ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_GB])); | |
| 238 | + v4l2_ctrl_s_ctrl(isc->r_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_R]); | |
| 239 | + v4l2_ctrl_s_ctrl(isc->b_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_B]); | |
| 240 | + v4l2_ctrl_s_ctrl(isc->gr_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_GR]); | |
| 241 | + v4l2_ctrl_s_ctrl(isc->gb_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_GB]); | |
| 249 | 242 | } |
| 250 | 243 | |
| 251 | 244 | 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) · 3 unmodified lines | ||
| 255 | 248 | /* In here we set our actual hw pipeline config */ |
| 256 | 249 | |
| 257 | 250 | regmap_write(isc->regmap, ISC_WB_O_RGR, |
| 258 | − (ISC_WB_O_ZERO_VAL - (ctrls->offset[ISC_HIS_CFG_MODE_R])) | | |
| 259 | − ((ISC_WB_O_ZERO_VAL - ctrls->offset[ISC_HIS_CFG_MODE_GR]) << 16)); | |
| 251 | + ((ctrls->offset[ISC_HIS_CFG_MODE_R])) | | |
| 252 | + ((ctrls->offset[ISC_HIS_CFG_MODE_GR]) << 16)); | |
| 260 | 253 | regmap_write(isc->regmap, ISC_WB_O_BGB, |
| 261 | − (ISC_WB_O_ZERO_VAL - (ctrls->offset[ISC_HIS_CFG_MODE_B])) | | |
| 262 | − ((ISC_WB_O_ZERO_VAL - ctrls->offset[ISC_HIS_CFG_MODE_GB]) << 16)); | |
| 254 | + ((ctrls->offset[ISC_HIS_CFG_MODE_B])) | | |
| 255 | + ((ctrls->offset[ISC_HIS_CFG_MODE_GB]) << 16)); | |
| 263 | 256 | regmap_write(isc->regmap, ISC_WB_G_RGR, |
| 264 | 257 | ctrls->gain[ISC_HIS_CFG_MODE_R] | |
| 265 | 258 | (ctrls->gain[ISC_HIS_CFG_MODE_GR] << 16)); |
| @@ -275,12 +268,8 @@ static inline void isc_reset_awb_ctrls(struct isc_device *isc) · 9 unmodified lines | ||
| 275 | 268 | for (c = ISC_HIS_CFG_MODE_GR; c <= ISC_HIS_CFG_MODE_B; c++) { |
| 276 | 269 | /* gains have a fixed point at 9 decimals */ |
| 277 | 270 | isc->ctrls.gain[c] = 1 << 9; |
| 278 | − /* offsets are in 2's complements, the value | |
| 279 | − * will be substracted from ISC_WB_O_ZERO_VAL to obtain | |
| 280 | − * 2's complement of a value between 0 and | |
| 281 | − * ISC_WB_O_ZERO_VAL >> 1 | |
| 282 | − */ | |
| 283 | − isc->ctrls.offset[c] = ISC_WB_O_ZERO_VAL; | |
| 271 | + /* offsets are in 2's complements */ | |
| 272 | + isc->ctrls.offset[c] = 0; | |
| 284 | 273 | } |
| 285 | 274 | } |
| 286 | 275 | |
| @@ -1774,9 +1763,12 @@ static void isc_wb_update(struct isc_ctrls *ctrls) · 1487 unmodified lines | ||
| 1774 | 1763 | */ |
| 1775 | 1764 | ctrls->offset[c] = (offset[c] - 1) << 3; |
| 1776 | 1765 | |
| 1777 | − /* the offset is then taken and converted to 2's complements */ | |
| 1778 | − if (!ctrls->offset[c]) | |
| 1779 | − ctrls->offset[c] = ISC_WB_O_ZERO_VAL; | |
| 1766 | + /* | |
| 1767 | + * the offset is then taken and converted to 2's complements, | |
| 1768 | + * and must be negative, as we subtract this value from the | |
| 1769 | + * color components | |
| 1770 | + */ | |
| 1771 | + ctrls->offset[c] = -ctrls->offset[c]; | |
| 1780 | 1772 | |
| 1781 | 1773 | /* |
| 1782 | 1774 | * the stretch gain is the total number of histogram bins |
| @@ -1938,17 +1930,13 @@ static int isc_s_awb_ctrl(struct v4l2_ctrl *ctrl) · 155 unmodified lines | ||
| 1938 | 1930 | ctrls->gain[ISC_HIS_CFG_MODE_GB] = isc->gb_gain_ctrl->val; |
| 1939 | 1931 | |
| 1940 | 1932 | if (ctrl->cluster[ISC_CTRL_R_OFF]->is_new) |
| 1941 | − ctrls->offset[ISC_HIS_CFG_MODE_R] = | |
| 1942 | − ISC_CTRL_V4L2_TO_ISC(isc->r_off_ctrl->val); | |
| 1933 | + ctrls->offset[ISC_HIS_CFG_MODE_R] = isc->r_off_ctrl->val; | |
| 1943 | 1934 | if (ctrl->cluster[ISC_CTRL_B_OFF]->is_new) |
| 1944 | − ctrls->offset[ISC_HIS_CFG_MODE_B] = | |
| 1945 | − ISC_CTRL_V4L2_TO_ISC(isc->b_off_ctrl->val); | |
| 1935 | + ctrls->offset[ISC_HIS_CFG_MODE_B] = isc->b_off_ctrl->val; | |
| 1946 | 1936 | if (ctrl->cluster[ISC_CTRL_GR_OFF]->is_new) |
| 1947 | − ctrls->offset[ISC_HIS_CFG_MODE_GR] = | |
| 1948 | − ISC_CTRL_V4L2_TO_ISC(isc->gr_off_ctrl->val); | |
| 1937 | + ctrls->offset[ISC_HIS_CFG_MODE_GR] = isc->gr_off_ctrl->val; | |
| 1949 | 1938 | if (ctrl->cluster[ISC_CTRL_GB_OFF]->is_new) |
| 1950 | − ctrls->offset[ISC_HIS_CFG_MODE_GB] = | |
| 1951 | − ISC_CTRL_V4L2_TO_ISC(isc->gb_off_ctrl->val); | |
| 1939 | + ctrls->offset[ISC_HIS_CFG_MODE_GB] = isc->gb_off_ctrl->val; | |
| 1952 | 1940 | |
| 1953 | 1941 | isc_update_awb_ctrls(isc); |
| 1954 | 1942 | |
| @@ -2010,13 +1998,13 @@ static int isc_g_volatile_awb_ctrl(struct v4l2_ctrl *ctrl) · 55 unmodified lines | ||
| 2010 | 1998 | ctrls->gain[ISC_HIS_CFG_MODE_GB]; |
| 2011 | 1999 | |
| 2012 | 2000 | ctrl->cluster[ISC_CTRL_R_OFF]->val = |
| 2013 | − ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_R]); | |
| 2001 | + ctrls->offset[ISC_HIS_CFG_MODE_R]; | |
| 2014 | 2002 | ctrl->cluster[ISC_CTRL_B_OFF]->val = |
| 2015 | − ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_B]); | |
| 2003 | + ctrls->offset[ISC_HIS_CFG_MODE_B]; | |
| 2016 | 2004 | ctrl->cluster[ISC_CTRL_GR_OFF]->val = |
| 2017 | − ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_GR]); | |
| 2005 | + ctrls->offset[ISC_HIS_CFG_MODE_GR]; | |
| 2018 | 2006 | ctrl->cluster[ISC_CTRL_GB_OFF]->val = |
| 2019 | − ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_GB]); | |
| 2007 | + ctrls->offset[ISC_HIS_CFG_MODE_GB]; | |
| 2020 | 2008 | break; |
| 2021 | 2009 | } |
| 2022 | 2010 | return 0; |
| @@ -108,8 +108,6 @@ · 107 unmodified lines | ||
| 108 | 108 | /* ISC White Balance Gain for B, GB Register */ |
| 109 | 109 | #define ISC_WB_G_BGB 0x0000006c |
| 110 | 110 | |
| 111 | −#define ISC_WB_O_ZERO_VAL (1 << 13) | |
| 112 | − | |
| 113 | 111 | /* ISC Color Filter Array Control Register */ |
| 114 | 112 | #define ISC_CFA_CTRL 0x00000070 |
| 115 | 113 | |
| @@ -133,7 +133,7 @@ struct isc_ctrls { · 132 unmodified lines | ||
| 133 | 133 | |
| 134 | 134 | /* one for each component : GR, R, GB, B */ |
| 135 | 135 | u32 gain[HIST_BAYER]; |
| 136 | − u32 offset[HIST_BAYER]; | |
| 136 | + s32 offset[HIST_BAYER]; | |
| 137 | 137 | |
| 138 | 138 | u32 hist_entry[HIST_ENTRIES]; |
| 139 | 139 | u32 hist_count[HIST_BAYER]; |
| @@ -321,11 +321,13 @@ static const struct dev_pm_ops atmel_isc_dev_pm_ops = { · 320 unmodified lines | ||
| 321 | 321 | SET_RUNTIME_PM_OPS(isc_runtime_suspend, isc_runtime_resume, NULL) |
| 322 | 322 | }; |
| 323 | 323 | |
| 324 | +#if IS_ENABLED(CONFIG_OF) | |
| 324 | 325 | static const struct of_device_id atmel_isc_of_match[] = { |
| 325 | 326 | { .compatible = "atmel,sama5d2-isc" }, |
| 326 | 327 | { } |
| 327 | 328 | }; |
| 328 | 329 | MODULE_DEVICE_TABLE(of, atmel_isc_of_match); |
| 330 | +#endif | |
| 329 | 331 | |
| 330 | 332 | static struct platform_driver atmel_isc_driver = { |
| 331 | 333 | .probe = atmel_isc_probe, |
| @@ -2653,7 +2653,7 @@ static int coda_open(struct file *file) · 2652 unmodified lines | ||
| 2653 | 2653 | |
| 2654 | 2654 | ret = clk_prepare_enable(dev->clk_per); |
| 2655 | 2655 | if (ret) |
| 2656 | − goto err_clk_per; | |
| 2656 | + goto err_pm_get; | |
| 2657 | 2657 | |
| 2658 | 2658 | ret = clk_prepare_enable(dev->clk_ahb); |
| 2659 | 2659 | if (ret) |
| @@ -2692,9 +2692,8 @@ err_ctx_init: · 32 unmodified lines | ||
| 2692 | 2692 | clk_disable_unprepare(dev->clk_ahb); |
| 2693 | 2693 | err_clk_ahb: |
| 2694 | 2694 | clk_disable_unprepare(dev->clk_per); |
| 2695 | −err_clk_per: | |
| 2696 | − pm_runtime_put_sync(dev->dev); | |
| 2697 | 2695 | err_pm_get: |
| 2696 | + pm_runtime_put_sync(dev->dev); | |
| 2698 | 2697 | v4l2_fh_del(&ctx->fh); |
| 2699 | 2698 | v4l2_fh_exit(&ctx->fh); |
| 2700 | 2699 | err_coda_name_init: |
| @@ -327,8 +327,11 @@ int coda_jpeg_decode_header(struct coda_ctx *ctx, struct vb2_buffer *vb) · 326 unmodified lines | ||
| 327 | 327 | "only 8-bit quantization tables supported\n"); |
| 328 | 328 | continue; |
| 329 | 329 | } |
| 330 | − if (!ctx->params.jpeg_qmat_tab[i]) | |
| 330 | + if (!ctx->params.jpeg_qmat_tab[i]) { | |
| 331 | 331 | ctx->params.jpeg_qmat_tab[i] = kmalloc(64, GFP_KERNEL); |
| 332 | + if (!ctx->params.jpeg_qmat_tab[i]) | |
| 333 | + return -ENOMEM; | |
| 334 | + } | |
| 332 | 335 | memcpy(ctx->params.jpeg_qmat_tab[i], |
| 333 | 336 | quantization_tables[i].start, 64); |
| 334 | 337 | } |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
| 2 | 2 | /* |
| 3 | − * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ | |
| 3 | + * Copyright (C) 2010 Texas Instruments Incorporated - https://www.ti.com/ | |
| 4 | 4 | */ |
| 5 | 5 | #include <linux/kernel.h> |
| 6 | 6 | #include <linux/init.h> |
| @@ -5,7 +5,7 @@ · 4 unmodified lines | ||
| 5 | 5 | * The hardware supports SDTV, HDTV formats, raw data capture. |
| 6 | 6 | * Currently, the driver supports NTSC and PAL standards. |
| 7 | 7 | * |
| 8 | − * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ | |
| 8 | + * Copyright (C) 2009 Texas Instruments Incorporated - https://www.ti.com/ | |
| 9 | 9 | * |
| 10 | 10 | * This program is free software; you can redistribute it and/or |
| 11 | 11 | * modify it under the terms of the GNU General Public License as |
| @@ -458,6 +458,7 @@ static int vpif_probe(struct platform_device *pdev) · 446 unmodified lines | ||
| 458 | 458 | res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); |
| 459 | 459 | if (!res_irq) { |
| 460 | 460 | dev_warn(&pdev->dev, "Missing IRQ resource.\n"); |
| 461 | + pm_runtime_put(&pdev->dev); | |
| 461 | 462 | return -EINVAL; |
| 462 | 463 | } |
| 463 | 464 | |
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | /* |
| 2 | 2 | * VPIF header file |
| 3 | 3 | * |
| 4 | − * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ | |
| 4 | + * Copyright (C) 2009 Texas Instruments Incorporated - https://www.ti.com/ | |
| 5 | 5 | * |
| 6 | 6 | * This program is free software; you can redistribute it and/or |
| 7 | 7 | * modify it under the terms of the GNU General Public License as |
| @@ -1482,8 +1482,6 @@ probe_out: · 1481 unmodified lines | ||
| 1482 | 1482 | /* Unregister video device */ |
| 1483 | 1483 | video_unregister_device(&ch->video_dev); |
| 1484 | 1484 | } |
| 1485 | − kfree(vpif_obj.sd); | |
| 1486 | − v4l2_device_unregister(&vpif_obj.v4l2_dev); | |
| 1487 | 1485 | |
| 1488 | 1486 | return err; |
| 1489 | 1487 | } |
| @@ -2,7 +2,7 @@ · 1 unmodified line | ||
| 2 | 2 | * vpif-display - VPIF display driver |
| 3 | 3 | * Display driver for TI DaVinci VPIF |
| 4 | 4 | * |
| 5 | − * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ | |
| 5 | + * Copyright (C) 2009 Texas Instruments Incorporated - https://www.ti.com/ | |
| 6 | 6 | * Copyright (C) 2014 Lad, Prabhakar <prabhakar.csengg@gmail.com> |
| 7 | 7 | * |
| 8 | 8 | * This program is free software; you can redistribute it and/or |
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | /* |
| 2 | 2 | * VPIF display header file |
| 3 | 3 | * |
| 4 | − * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ | |
| 4 | + * Copyright (C) 2009 Texas Instruments Incorporated - https://www.ti.com/ | |
| 5 | 5 | * |
| 6 | 6 | * This program is free software; you can redistribute it and/or |
| 7 | 7 | * modify it under the terms of the GNU General Public License as |
| @@ -505,19 +505,31 @@ static void vpss_exit(void) · 504 unmodified lines | ||
| 505 | 505 | |
| 506 | 506 | static int __init vpss_init(void) |
| 507 | 507 | { |
| 508 | + int ret; | |
| 509 | + | |
| 508 | 510 | if (!request_mem_region(VPSS_CLK_CTRL, 4, "vpss_clock_control")) |
| 509 | 511 | return -EBUSY; |
| 510 | 512 | |
| 511 | 513 | oper_cfg.vpss_regs_base2 = ioremap(VPSS_CLK_CTRL, 4); |
| 512 | 514 | if (unlikely(!oper_cfg.vpss_regs_base2)) { |
| 513 | − release_mem_region(VPSS_CLK_CTRL, 4); | |
| 514 | − return -ENOMEM; | |
| 515 | + ret = -ENOMEM; | |
| 516 | + goto err_ioremap; | |
| 515 | 517 | } |
| 516 | 518 | |
| 517 | 519 | writel(VPSS_CLK_CTRL_VENCCLKEN | |
| 518 | − VPSS_CLK_CTRL_DACCLKEN, oper_cfg.vpss_regs_base2); | |
| 520 | + VPSS_CLK_CTRL_DACCLKEN, oper_cfg.vpss_regs_base2); | |
| 521 | + | |
| 522 | + ret = platform_driver_register(&vpss_driver); | |
| 523 | + if (ret) | |
| 524 | + goto err_pd_register; | |
| 525 | + | |
| 526 | + return 0; | |
| 519 | 527 | |
| 520 | − return platform_driver_register(&vpss_driver); | |
| 528 | +err_pd_register: | |
| 529 | + iounmap(oper_cfg.vpss_regs_base2); | |
| 530 | +err_ioremap: | |
| 531 | + release_mem_region(VPSS_CLK_CTRL, 4); | |
| 532 | + return ret; | |
| 521 | 533 | } |
| 522 | 534 | subsys_initcall(vpss_init); |
| 523 | 535 | module_exit(vpss_exit); |
| @@ -577,7 +577,7 @@ int gsc_try_selection(struct gsc_ctx *ctx, struct v4l2_selection *s) · 576 unmodified lines | ||
| 577 | 577 | v4l_bound_align_image(&tmp_w, min_w, max_w, mod_x, |
| 578 | 578 | &tmp_h, min_h, max_h, mod_y, 0); |
| 579 | 579 | |
| 580 | − if (!V4L2_TYPE_IS_OUTPUT(s->type) && | |
| 580 | + if (V4L2_TYPE_IS_CAPTURE(s->type) && | |
| 581 | 581 | (ctx->gsc_ctrls.rotate->val == 90 || |
| 582 | 582 | ctx->gsc_ctrls.rotate->val == 270)) |
| 583 | 583 | gsc_check_crop_change(tmp_h, tmp_w, |
| @@ -255,7 +255,7 @@ static int gsc_m2m_buf_prepare(struct vb2_buffer *vb) · 254 unmodified lines | ||
| 255 | 255 | if (IS_ERR(frame)) |
| 256 | 256 | return PTR_ERR(frame); |
| 257 | 257 | |
| 258 | − if (!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { | |
| 258 | + if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) { | |
| 259 | 259 | for (i = 0; i < frame->fmt->num_planes; i++) |
| 260 | 260 | vb2_set_plane_payload(vb, i, frame->payload[i]); |
| 261 | 261 | } |
| @@ -21,6 +21,7 @@ · 20 unmodified lines | ||
| 21 | 21 | #include <media/v4l2-device.h> |
| 22 | 22 | #include <media/v4l2-ioctl.h> |
| 23 | 23 | #include <media/v4l2-mem2mem.h> |
| 24 | +#include <media/v4l2-rect.h> | |
| 24 | 25 | #include <media/videobuf2-v4l2.h> |
| 25 | 26 | #include <media/videobuf2-dma-contig.h> |
| 26 | 27 | |
| @@ -478,8 +479,10 @@ static int fimc_capture_open(struct file *file) · 451 unmodified lines | ||
| 478 | 479 | |
| 479 | 480 | set_bit(ST_CAPT_BUSY, &fimc->state); |
| 480 | 481 | ret = pm_runtime_get_sync(&fimc->pdev->dev); |
| 481 | − if (ret < 0) | |
| 482 | + if (ret < 0) { | |
| 483 | + pm_runtime_put_sync(&fimc->pdev->dev); | |
| 482 | 484 | goto unlock; |
| 485 | + } | |
| 483 | 486 | |
| 484 | 487 | ret = v4l2_fh_open(file); |
| 485 | 488 | if (ret) { |
| @@ -1299,19 +1302,6 @@ static int fimc_cap_g_selection(struct file *file, void *fh, · 813 unmodified lines | ||
| 1299 | 1302 | return -EINVAL; |
| 1300 | 1303 | } |
| 1301 | 1304 | |
| 1302 | −/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */ | |
| 1303 | −static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b) | |
| 1304 | −{ | |
| 1305 | − if (a->left < b->left || a->top < b->top) | |
| 1306 | − return 0; | |
| 1307 | − if (a->left + a->width > b->left + b->width) | |
| 1308 | − return 0; | |
| 1309 | − if (a->top + a->height > b->top + b->height) | |
| 1310 | − return 0; | |
| 1311 | − | |
| 1312 | − return 1; | |
| 1313 | −} | |
| 1314 | − | |
| 1315 | 1305 | static int fimc_cap_s_selection(struct file *file, void *fh, |
| 1316 | 1306 | struct v4l2_selection *s) |
| 1317 | 1307 | { |
| @@ -1334,11 +1324,11 @@ static int fimc_cap_s_selection(struct file *file, void *fh, · 16 unmodified lines | ||
| 1334 | 1324 | fimc_capture_try_selection(ctx, &rect, s->target); |
| 1335 | 1325 | |
| 1336 | 1326 | if (s->flags & V4L2_SEL_FLAG_LE && |
| 1337 | − !enclosed_rectangle(&rect, &s->r)) | |
| 1327 | + !v4l2_rect_enclosed(&rect, &s->r)) | |
| 1338 | 1328 | return -ERANGE; |
| 1339 | 1329 | |
| 1340 | 1330 | if (s->flags & V4L2_SEL_FLAG_GE && |
| 1341 | − !enclosed_rectangle(&s->r, &rect)) | |
| 1331 | + !v4l2_rect_enclosed(&s->r, &rect)) | |
| 1342 | 1332 | return -ERANGE; |
| 1343 | 1333 | |
| 1344 | 1334 | s->r = rect; |
| @@ -871,6 +871,7 @@ err_dfs: · 870 unmodified lines | ||
| 871 | 871 | err_sd: |
| 872 | 872 | fimc_is_unregister_subdevs(is); |
| 873 | 873 | err_pm: |
| 874 | + pm_runtime_put_noidle(dev); | |
| 874 | 875 | if (!pm_runtime_enabled(dev)) |
| 875 | 876 | fimc_is_runtime_suspend(dev); |
| 876 | 877 | err_irq: |
| @@ -293,6 +293,7 @@ static int isp_video_open(struct file *file) · 292 unmodified lines | ||
| 293 | 293 | if (!ret) |
| 294 | 294 | goto unlock; |
| 295 | 295 | rel_fh: |
| 296 | + pm_runtime_put_noidle(&isp->pdev->dev); | |
| 296 | 297 | v4l2_fh_release(file); |
| 297 | 298 | unlock: |
| 298 | 299 | mutex_unlock(&isp->video_lock); |
| @@ -25,6 +25,7 @@ · 24 unmodified lines | ||
| 25 | 25 | #include <media/v4l2-device.h> |
| 26 | 26 | #include <media/v4l2-ioctl.h> |
| 27 | 27 | #include <media/v4l2-mem2mem.h> |
| 28 | +#include <media/v4l2-rect.h> | |
| 28 | 29 | #include <media/videobuf2-v4l2.h> |
| 29 | 30 | #include <media/videobuf2-dma-contig.h> |
| 30 | 31 | #include <media/drv-intf/exynos-fimc.h> |
| @@ -868,19 +869,6 @@ static int fimc_lite_reqbufs(struct file *file, void *priv, · 837 unmodified lines | ||
| 868 | 869 | return ret; |
| 869 | 870 | } |
| 870 | 871 | |
| 871 | −/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */ | |
| 872 | −static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b) | |
| 873 | −{ | |
| 874 | − if (a->left < b->left || a->top < b->top) | |
| 875 | − return 0; | |
| 876 | − if (a->left + a->width > b->left + b->width) | |
| 877 | − return 0; | |
| 878 | − if (a->top + a->height > b->top + b->height) | |
| 879 | − return 0; | |
| 880 | − | |
| 881 | − return 1; | |
| 882 | −} | |
| 883 | − | |
| 884 | 872 | static int fimc_lite_g_selection(struct file *file, void *fh, |
| 885 | 873 | struct v4l2_selection *sel) |
| 886 | 874 | { |
| @@ -922,11 +910,11 @@ static int fimc_lite_s_selection(struct file *file, void *fh, · 35 unmodified lines | ||
| 922 | 910 | fimc_lite_try_compose(fimc, &rect); |
| 923 | 911 | |
| 924 | 912 | if ((sel->flags & V4L2_SEL_FLAG_LE) && |
| 925 | − !enclosed_rectangle(&rect, &sel->r)) | |
| 913 | + !v4l2_rect_enclosed(&rect, &sel->r)) | |
| 926 | 914 | return -ERANGE; |
| 927 | 915 | |
| 928 | 916 | if ((sel->flags & V4L2_SEL_FLAG_GE) && |
| 929 | − !enclosed_rectangle(&sel->r, &rect)) | |
| 917 | + !v4l2_rect_enclosed(&sel->r, &rect)) | |
| 930 | 918 | return -ERANGE; |
| 931 | 919 | |
| 932 | 920 | sel->r = rect; |
| @@ -1270,6 +1270,9 @@ static int fimc_md_get_pinctrl(struct fimc_md *fmd) · 1269 unmodified lines | ||
| 1270 | 1270 | |
| 1271 | 1271 | pctl->state_idle = pinctrl_lookup_state(pctl->pinctrl, |
| 1272 | 1272 | PINCTRL_STATE_IDLE); |
| 1273 | + if (IS_ERR(pctl->state_idle)) | |
| 1274 | + return PTR_ERR(pctl->state_idle); | |
| 1275 | + | |
| 1273 | 1276 | return 0; |
| 1274 | 1277 | } |
| 1275 | 1278 | |
| @@ -1439,7 +1442,7 @@ static int fimc_md_probe(struct platform_device *pdev) · 163 unmodified lines | ||
| 1439 | 1442 | INIT_LIST_HEAD(&fmd->pipelines); |
| 1440 | 1443 | fmd->pdev = pdev; |
| 1441 | 1444 | |
| 1442 | − strscpy(fmd->media_dev.model, "SAMSUNG S5P FIMC", | |
| 1445 | + strscpy(fmd->media_dev.model, "Samsung S5P FIMC", | |
| 1443 | 1446 | sizeof(fmd->media_dev.model)); |
| 1444 | 1447 | fmd->media_dev.ops = &fimc_md_ops; |
| 1445 | 1448 | fmd->media_dev.dev = dev; |
| @@ -604,44 +604,28 @@ static void cafe_pci_remove(struct pci_dev *pdev) · 603 unmodified lines | ||
| 604 | 604 | } |
| 605 | 605 | |
| 606 | 606 | |
| 607 | −#ifdef CONFIG_PM | |
| 608 | 607 | /* |
| 609 | 608 | * Basic power management. |
| 610 | 609 | */ |
| 611 | −static int cafe_pci_suspend(struct pci_dev *pdev, pm_message_t state) | |
| 610 | +static int __maybe_unused cafe_pci_suspend(struct device *dev) | |
| 612 | 611 | { |
| 613 | − struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); | |
| 612 | + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); | |
| 614 | 613 | struct cafe_camera *cam = to_cam(v4l2_dev); |
| 615 | − int ret; | |
| 616 | 614 | |
| 617 | − ret = pci_save_state(pdev); | |
| 618 | − if (ret) | |
| 619 | − return ret; | |
| 620 | 615 | mccic_suspend(&cam->mcam); |
| 621 | − pci_disable_device(pdev); | |
| 622 | 616 | return 0; |
| 623 | 617 | } |
| 624 | 618 | |
| 625 | 619 | |
| 626 | −static int cafe_pci_resume(struct pci_dev *pdev) | |
| 620 | +static int __maybe_unused cafe_pci_resume(struct device *dev) | |
| 627 | 621 | { |
| 628 | − struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); | |
| 622 | + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); | |
| 629 | 623 | struct cafe_camera *cam = to_cam(v4l2_dev); |
| 630 | − int ret = 0; | |
| 631 | 624 | |
| 632 | − pci_restore_state(pdev); | |
| 633 | − ret = pci_enable_device(pdev); | |
| 634 | − | |
| 635 | − if (ret) { | |
| 636 | − cam_warn(cam, "Unable to re-enable device on resume!\n"); | |
| 637 | − return ret; | |
| 638 | − } | |
| 639 | 625 | cafe_ctlr_init(&cam->mcam); |
| 640 | 626 | return mccic_resume(&cam->mcam); |
| 641 | 627 | } |
| 642 | 628 | |
| 643 | −#endif /* CONFIG_PM */ | |
| 644 | − | |
| 645 | 629 | static const struct pci_device_id cafe_ids[] = { |
| 646 | 630 | { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, |
| 647 | 631 | PCI_DEVICE_ID_MARVELL_88ALP01_CCIC) }, |
| @@ -650,15 +634,14 @@ static const struct pci_device_id cafe_ids[] = { · 2 unmodified lines | ||
| 650 | 634 | |
| 651 | 635 | MODULE_DEVICE_TABLE(pci, cafe_ids); |
| 652 | 636 | |
| 637 | +static SIMPLE_DEV_PM_OPS(cafe_pci_pm_ops, cafe_pci_suspend, cafe_pci_resume); | |
| 638 | + | |
| 653 | 639 | static struct pci_driver cafe_pci_driver = { |
| 654 | 640 | .name = "cafe1000-ccic", |
| 655 | 641 | .id_table = cafe_ids, |
| 656 | 642 | .probe = cafe_pci_probe, |
| 657 | 643 | .remove = cafe_pci_remove, |
| 658 | −#ifdef CONFIG_PM | |
| 659 | − .suspend = cafe_pci_suspend, | |
| 660 | − .resume = cafe_pci_resume, | |
| 661 | −#endif | |
| 644 | + .driver.pm = &cafe_pci_pm_ops, | |
| 662 | 645 | }; |
| 663 | 646 | |
| 664 | 647 | |
| @@ -1940,6 +1940,7 @@ int mccic_register(struct mcam_camera *cam) · 1939 unmodified lines | ||
| 1940 | 1940 | out: |
| 1941 | 1941 | v4l2_async_notifier_unregister(&cam->notifier); |
| 1942 | 1942 | v4l2_device_unregister(&cam->v4l2_dev); |
| 1943 | + v4l2_async_notifier_cleanup(&cam->notifier); | |
| 1943 | 1944 | return ret; |
| 1944 | 1945 | } |
| 1945 | 1946 | EXPORT_SYMBOL_GPL(mccic_register); |
| @@ -1961,14 +1962,13 @@ void mccic_shutdown(struct mcam_camera *cam) · 15 unmodified lines | ||
| 1961 | 1962 | v4l2_ctrl_handler_free(&cam->ctrl_handler); |
| 1962 | 1963 | v4l2_async_notifier_unregister(&cam->notifier); |
| 1963 | 1964 | v4l2_device_unregister(&cam->v4l2_dev); |
| 1965 | + v4l2_async_notifier_cleanup(&cam->notifier); | |
| 1964 | 1966 | } |
| 1965 | 1967 | EXPORT_SYMBOL_GPL(mccic_shutdown); |
| 1966 | 1968 | |
| 1967 | 1969 | /* |
| 1968 | 1970 | * Power management |
| 1969 | 1971 | */ |
| 1970 | −#ifdef CONFIG_PM | |
| 1971 | − | |
| 1972 | 1972 | void mccic_suspend(struct mcam_camera *cam) |
| 1973 | 1973 | { |
| 1974 | 1974 | mutex_lock(&cam->s_mutex); |
| @@ -2015,7 +2015,6 @@ int mccic_resume(struct mcam_camera *cam) · 40 unmodified lines | ||
| 2015 | 2015 | return ret; |
| 2016 | 2016 | } |
| 2017 | 2017 | EXPORT_SYMBOL_GPL(mccic_resume); |
| 2018 | −#endif /* CONFIG_PM */ | |
| 2019 | 2018 | |
| 2020 | 2019 | MODULE_LICENSE("GPL v2"); |
| 2021 | 2020 | MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>"); |
| @@ -236,10 +236,8 @@ static inline void mcam_reg_set_bit(struct mcam_camera *cam, · 235 unmodified lines | ||
| 236 | 236 | int mccic_register(struct mcam_camera *cam); |
| 237 | 237 | int mccic_irq(struct mcam_camera *cam, unsigned int irqs); |
| 238 | 238 | void mccic_shutdown(struct mcam_camera *cam); |
| 239 | −#ifdef CONFIG_PM | |
| 240 | 239 | void mccic_suspend(struct mcam_camera *cam); |
| 241 | 240 | int mccic_resume(struct mcam_camera *cam); |
| 242 | −#endif | |
| 243 | 241 | |
| 244 | 242 | /* |
| 245 | 243 | * Register definitions for the m88alp01 camera interface. Offsets in bytes |
| @@ -731,7 +731,7 @@ static void mtk_jpeg_stop_streaming(struct vb2_queue *q) · 730 unmodified lines | ||
| 731 | 731 | * subsampling. Update capture queue when the stream is off. |
| 732 | 732 | */ |
| 733 | 733 | if (ctx->state == MTK_JPEG_SOURCE_CHANGE && |
| 734 | − !V4L2_TYPE_IS_OUTPUT(q->type)) { | |
| 734 | + V4L2_TYPE_IS_CAPTURE(q->type)) { | |
| 735 | 735 | struct mtk_jpeg_src_buf *src_buf; |
| 736 | 736 | |
| 737 | 737 | vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); |
| @@ -14,46 +14,6 @@ · 13 unmodified lines | ||
| 14 | 14 | #include "mtk_mdp_comp.h" |
| 15 | 15 | |
| 16 | 16 | |
| 17 | −static const char * const mtk_mdp_comp_stem[MTK_MDP_COMP_TYPE_MAX] = { | |
| 18 | − "mdp-rdma", | |
| 19 | − "mdp-rsz", | |
| 20 | − "mdp-wdma", | |
| 21 | − "mdp-wrot", | |
| 22 | −}; | |
| 23 | − | |
| 24 | −struct mtk_mdp_comp_match { | |
| 25 | − enum mtk_mdp_comp_type type; | |
| 26 | − int alias_id; | |
| 27 | −}; | |
| 28 | − | |
| 29 | −static const struct mtk_mdp_comp_match mtk_mdp_matches[MTK_MDP_COMP_ID_MAX] = { | |
| 30 | − { MTK_MDP_RDMA, 0 }, | |
| 31 | − { MTK_MDP_RDMA, 1 }, | |
| 32 | − { MTK_MDP_RSZ, 0 }, | |
| 33 | − { MTK_MDP_RSZ, 1 }, | |
| 34 | − { MTK_MDP_RSZ, 2 }, | |
| 35 | − { MTK_MDP_WDMA, 0 }, | |
| 36 | − { MTK_MDP_WROT, 0 }, | |
| 37 | − { MTK_MDP_WROT, 1 }, | |
| 38 | −}; | |
| 39 | − | |
| 40 | −int mtk_mdp_comp_get_id(struct device *dev, struct device_node *node, | |
| 41 | − enum mtk_mdp_comp_type comp_type) | |
| 42 | −{ | |
| 43 | − int id = of_alias_get_id(node, mtk_mdp_comp_stem[comp_type]); | |
| 44 | − int i; | |
| 45 | − | |
| 46 | − for (i = 0; i < ARRAY_SIZE(mtk_mdp_matches); i++) { | |
| 47 | − if (comp_type == mtk_mdp_matches[i].type && | |
| 48 | − id == mtk_mdp_matches[i].alias_id) | |
| 49 | − return i; | |
| 50 | − } | |
| 51 | − | |
| 52 | − dev_err(dev, "Failed to get id. type: %d, id: %d\n", comp_type, id); | |
| 53 | − | |
| 54 | − return -EINVAL; | |
| 55 | −} | |
| 56 | − | |
| 57 | 17 | void mtk_mdp_comp_clock_on(struct device *dev, struct mtk_mdp_comp *comp) |
| 58 | 18 | { |
| 59 | 19 | int i, err; |
| @@ -62,8 +22,8 @@ void mtk_mdp_comp_clock_on(struct device *dev, struct mtk_mdp_comp *comp) · 2 unmodified lines | ||
| 62 | 22 | err = mtk_smi_larb_get(comp->larb_dev); |
| 63 | 23 | if (err) |
| 64 | 24 | dev_err(dev, |
| 65 | − "failed to get larb, err %d. type:%d id:%d\n", | |
| 66 | − err, comp->type, comp->id); | |
| 25 | + "failed to get larb, err %d. type:%d\n", | |
| 26 | + err, comp->type); | |
| 67 | 27 | } |
| 68 | 28 | |
| 69 | 29 | 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) · 2 unmodified lines | ||
| 72 | 32 | err = clk_prepare_enable(comp->clk[i]); |
| 73 | 33 | if (err) |
| 74 | 34 | dev_err(dev, |
| 75 | − "failed to enable clock, err %d. type:%d id:%d i:%d\n", | |
| 76 | − err, comp->type, comp->id, i); | |
| 35 | + "failed to enable clock, err %d. type:%d i:%d\n", | |
| 36 | + err, comp->type, i); | |
| 77 | 37 | } |
| 78 | 38 | } |
| 79 | 39 | |
| @@ -92,29 +52,24 @@ void mtk_mdp_comp_clock_off(struct device *dev, struct mtk_mdp_comp *comp) · 12 unmodified lines | ||
| 92 | 52 | } |
| 93 | 53 | |
| 94 | 54 | int mtk_mdp_comp_init(struct device *dev, struct device_node *node, |
| 95 | − struct mtk_mdp_comp *comp, enum mtk_mdp_comp_id comp_id) | |
| 55 | + struct mtk_mdp_comp *comp, | |
| 56 | + enum mtk_mdp_comp_type comp_type) | |
| 96 | 57 | { |
| 97 | 58 | struct device_node *larb_node; |
| 98 | 59 | struct platform_device *larb_pdev; |
| 60 | + int ret; | |
| 99 | 61 | int i; |
| 100 | 62 | |
| 101 | − if (comp_id < 0 || comp_id >= MTK_MDP_COMP_ID_MAX) { | |
| 102 | − dev_err(dev, "Invalid comp_id %d\n", comp_id); | |
| 103 | − return -EINVAL; | |
| 104 | − } | |
| 105 | − | |
| 106 | 63 | comp->dev_node = of_node_get(node); |
| 107 | − comp->id = comp_id; | |
| 108 | − comp->type = mtk_mdp_matches[comp_id].type; | |
| 109 | − comp->regs = of_iomap(node, 0); | |
| 64 | + comp->type = comp_type; | |
| 110 | 65 | |
| 111 | 66 | for (i = 0; i < ARRAY_SIZE(comp->clk); i++) { |
| 112 | 67 | comp->clk[i] = of_clk_get(node, i); |
| 113 | 68 | if (IS_ERR(comp->clk[i])) { |
| 114 | 69 | if (PTR_ERR(comp->clk[i]) != -EPROBE_DEFER) |
| 115 | 70 | dev_err(dev, "Failed to get clock\n"); |
| 116 | − | |
| 117 | − return PTR_ERR(comp->clk[i]); | |
| 71 | + ret = PTR_ERR(comp->clk[i]); | |
| 72 | + goto put_dev; | |
| 118 | 73 | } |
| 119 | 74 | |
| 120 | 75 | /* Only RDMA needs two clocks */ |
| @@ -133,20 +88,27 @@ int mtk_mdp_comp_init(struct device *dev, struct device_node *node, · 12 unmodified lines | ||
| 133 | 88 | if (!larb_node) { |
| 134 | 89 | dev_err(dev, |
| 135 | 90 | "Missing mediadek,larb phandle in %pOF node\n", node); |
| 136 | − return -EINVAL; | |
| 91 | + ret = -EINVAL; | |
| 92 | + goto put_dev; | |
| 137 | 93 | } |
| 138 | 94 | |
| 139 | 95 | larb_pdev = of_find_device_by_node(larb_node); |
| 140 | 96 | if (!larb_pdev) { |
| 141 | 97 | dev_warn(dev, "Waiting for larb device %pOF\n", larb_node); |
| 142 | 98 | of_node_put(larb_node); |
| 143 | − return -EPROBE_DEFER; | |
| 99 | + ret = -EPROBE_DEFER; | |
| 100 | + goto put_dev; | |
| 144 | 101 | } |
| 145 | 102 | of_node_put(larb_node); |
| 146 | 103 | |
| 147 | 104 | comp->larb_dev = &larb_pdev->dev; |
| 148 | 105 | |
| 149 | 106 | return 0; |
| 107 | + | |
| 108 | +put_dev: | |
| 109 | + of_node_put(comp->dev_node); | |
| 110 | + | |
| 111 | + return ret; | |
| 150 | 112 | } |
| 151 | 113 | |
| 152 | 114 | void mtk_mdp_comp_deinit(struct device *dev, struct mtk_mdp_comp *comp) |
| @@ -22,41 +22,26 @@ enum mtk_mdp_comp_type { · 21 unmodified lines | ||
| 22 | 22 | MTK_MDP_COMP_TYPE_MAX, |
| 23 | 23 | }; |
| 24 | 24 | |
| 25 | −enum mtk_mdp_comp_id { | |
| 26 | − MTK_MDP_COMP_RDMA0, | |
| 27 | − MTK_MDP_COMP_RDMA1, | |
| 28 | − MTK_MDP_COMP_RSZ0, | |
| 29 | − MTK_MDP_COMP_RSZ1, | |
| 30 | − MTK_MDP_COMP_RSZ2, | |
| 31 | − MTK_MDP_COMP_WDMA, | |
| 32 | − MTK_MDP_COMP_WROT0, | |
| 33 | − MTK_MDP_COMP_WROT1, | |
| 34 | − MTK_MDP_COMP_ID_MAX, | |
| 35 | −}; | |
| 36 | − | |
| 37 | 25 | /** |
| 38 | 26 | * struct mtk_mdp_comp - the MDP's function component data |
| 27 | + * @node: list node to track sibing MDP components | |
| 39 | 28 | * @dev_node: component device node |
| 40 | 29 | * @clk: clocks required for component |
| 41 | − * @regs: Mapped address of component registers. | |
| 42 | 30 | * @larb_dev: SMI device required for component |
| 43 | 31 | * @type: component type |
| 44 | − * @id: component ID | |
| 45 | 32 | */ |
| 46 | 33 | struct mtk_mdp_comp { |
| 34 | + struct list_head node; | |
| 47 | 35 | struct device_node *dev_node; |
| 48 | 36 | struct clk *clk[2]; |
| 49 | − void __iomem *regs; | |
| 50 | 37 | struct device *larb_dev; |
| 51 | 38 | enum mtk_mdp_comp_type type; |
| 52 | − enum mtk_mdp_comp_id id; | |
| 53 | 39 | }; |
| 54 | 40 | |
| 55 | 41 | int mtk_mdp_comp_init(struct device *dev, struct device_node *node, |
| 56 | − struct mtk_mdp_comp *comp, enum mtk_mdp_comp_id comp_id); | |
| 42 | + struct mtk_mdp_comp *comp, | |
| 43 | + enum mtk_mdp_comp_type comp_type); | |
| 57 | 44 | void mtk_mdp_comp_deinit(struct device *dev, struct mtk_mdp_comp *comp); |
| 58 | −int mtk_mdp_comp_get_id(struct device *dev, struct device_node *node, | |
| 59 | − enum mtk_mdp_comp_type comp_type); | |
| 60 | 45 | void mtk_mdp_comp_clock_on(struct device *dev, struct mtk_mdp_comp *comp); |
| 61 | 46 | void mtk_mdp_comp_clock_off(struct device *dev, struct mtk_mdp_comp *comp); |
| 62 | 47 | |
| @@ -55,19 +55,19 @@ MODULE_DEVICE_TABLE(of, mtk_mdp_of_ids); · 54 unmodified lines | ||
| 55 | 55 | static void mtk_mdp_clock_on(struct mtk_mdp_dev *mdp) |
| 56 | 56 | { |
| 57 | 57 | struct device *dev = &mdp->pdev->dev; |
| 58 | − int i; | |
| 58 | + struct mtk_mdp_comp *comp_node; | |
| 59 | 59 | |
| 60 | − for (i = 0; i < ARRAY_SIZE(mdp->comp); i++) | |
| 61 | − mtk_mdp_comp_clock_on(dev, mdp->comp[i]); | |
| 60 | + list_for_each_entry(comp_node, &mdp->comp_list, node) | |
| 61 | + mtk_mdp_comp_clock_on(dev, comp_node); | |
| 62 | 62 | } |
| 63 | 63 | |
| 64 | 64 | static void mtk_mdp_clock_off(struct mtk_mdp_dev *mdp) |
| 65 | 65 | { |
| 66 | 66 | struct device *dev = &mdp->pdev->dev; |
| 67 | − int i; | |
| 67 | + struct mtk_mdp_comp *comp_node; | |
| 68 | 68 | |
| 69 | − for (i = 0; i < ARRAY_SIZE(mdp->comp); i++) | |
| 70 | − mtk_mdp_comp_clock_off(dev, mdp->comp[i]); | |
| 69 | + list_for_each_entry(comp_node, &mdp->comp_list, node) | |
| 70 | + mtk_mdp_comp_clock_off(dev, comp_node); | |
| 71 | 71 | } |
| 72 | 72 | |
| 73 | 73 | static void mtk_mdp_wdt_worker(struct work_struct *work) |
| @@ -91,12 +91,25 @@ static void mtk_mdp_reset_handler(void *priv) · 17 unmodified lines | ||
| 91 | 91 | queue_work(mdp->wdt_wq, &mdp->wdt_work); |
| 92 | 92 | } |
| 93 | 93 | |
| 94 | +void mtk_mdp_register_component(struct mtk_mdp_dev *mdp, | |
| 95 | + struct mtk_mdp_comp *comp) | |
| 96 | +{ | |
| 97 | + list_add(&mdp->comp_list, &comp->node); | |
| 98 | +} | |
| 99 | + | |
| 100 | +void mtk_mdp_unregister_component(struct mtk_mdp_dev *mdp, | |
| 101 | + struct mtk_mdp_comp *comp) | |
| 102 | +{ | |
| 103 | + list_del(&comp->node); | |
| 104 | +} | |
| 105 | + | |
| 94 | 106 | static int mtk_mdp_probe(struct platform_device *pdev) |
| 95 | 107 | { |
| 96 | 108 | struct mtk_mdp_dev *mdp; |
| 97 | 109 | struct device *dev = &pdev->dev; |
| 98 | 110 | struct device_node *node, *parent; |
| 99 | − int i, ret = 0; | |
| 111 | + struct mtk_mdp_comp *comp, *comp_temp; | |
| 112 | + int ret = 0; | |
| 100 | 113 | |
| 101 | 114 | mdp = devm_kzalloc(dev, sizeof(*mdp), GFP_KERNEL); |
| 102 | 115 | if (!mdp) |
| @@ -104,6 +117,7 @@ static int mtk_mdp_probe(struct platform_device *pdev) · 1 unmodified line | ||
| 104 | 117 | |
| 105 | 118 | mdp->id = pdev->id; |
| 106 | 119 | mdp->pdev = pdev; |
| 120 | + INIT_LIST_HEAD(&mdp->comp_list); | |
| 107 | 121 | INIT_LIST_HEAD(&mdp->ctx_list); |
| 108 | 122 | |
| 109 | 123 | mutex_init(&mdp->lock); |
| @@ -123,8 +137,6 @@ static int mtk_mdp_probe(struct platform_device *pdev) · 13 unmodified lines | ||
| 123 | 137 | for_each_child_of_node(parent, node) { |
| 124 | 138 | const struct of_device_id *of_id; |
| 125 | 139 | enum mtk_mdp_comp_type comp_type; |
| 126 | − int comp_id; | |
| 127 | − struct mtk_mdp_comp *comp; | |
| 128 | 140 | |
| 129 | 141 | of_id = of_match_node(mtk_mdp_comp_dt_ids, node); |
| 130 | 142 | if (!of_id) |
| @@ -137,12 +149,6 @@ static int mtk_mdp_probe(struct platform_device *pdev) · 6 unmodified lines | ||
| 137 | 149 | } |
| 138 | 150 | |
| 139 | 151 | comp_type = (enum mtk_mdp_comp_type)of_id->data; |
| 140 | − comp_id = mtk_mdp_comp_get_id(dev, node, comp_type); | |
| 141 | − if (comp_id < 0) { | |
| 142 | − dev_warn(dev, "Skipping unknown component %pOF\n", | |
| 143 | − node); | |
| 144 | − continue; | |
| 145 | − } | |
| 146 | 152 | |
| 147 | 153 | comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL); |
| 148 | 154 | if (!comp) { |
| @@ -150,13 +156,14 @@ static int mtk_mdp_probe(struct platform_device *pdev) · 1 unmodified line | ||
| 150 | 156 | of_node_put(node); |
| 151 | 157 | goto err_comp; |
| 152 | 158 | } |
| 153 | − mdp->comp[comp_id] = comp; | |
| 154 | 159 | |
| 155 | − ret = mtk_mdp_comp_init(dev, node, comp, comp_id); | |
| 160 | + ret = mtk_mdp_comp_init(dev, node, comp, comp_type); | |
| 156 | 161 | if (ret) { |
| 157 | 162 | of_node_put(node); |
| 158 | 163 | goto err_comp; |
| 159 | 164 | } |
| 165 | + | |
| 166 | + mtk_mdp_register_component(mdp, comp); | |
| 160 | 167 | } |
| 161 | 168 | |
| 162 | 169 | mdp->job_wq = create_singlethread_workqueue(MTK_MDP_MODULE_NAME); |
| @@ -188,12 +195,20 @@ static int mtk_mdp_probe(struct platform_device *pdev) · 25 unmodified lines | ||
| 188 | 195 | } |
| 189 | 196 | |
| 190 | 197 | mdp->vpu_dev = vpu_get_plat_device(pdev); |
| 191 | − vpu_wdt_reg_handler(mdp->vpu_dev, mtk_mdp_reset_handler, mdp, | |
| 192 | − VPU_RST_MDP); | |
| 198 | + ret = vpu_wdt_reg_handler(mdp->vpu_dev, mtk_mdp_reset_handler, mdp, | |
| 199 | + VPU_RST_MDP); | |
| 200 | + if (ret) { | |
| 201 | + dev_err(&pdev->dev, "Failed to register reset handler\n"); | |
| 202 | + goto err_m2m_register; | |
| 203 | + } | |
| 193 | 204 | |
| 194 | 205 | platform_set_drvdata(pdev, mdp); |
| 195 | 206 | |
| 196 | − vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); | |
| 207 | + ret = vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); | |
| 208 | + if (ret) { | |
| 209 | + dev_err(&pdev->dev, "Failed to set vb2 dma mag seg size\n"); | |
| 210 | + goto err_m2m_register; | |
| 211 | + } | |
| 197 | 212 | |
| 198 | 213 | pm_runtime_enable(dev); |
| 199 | 214 | dev_dbg(dev, "mdp-%d registered successfully\n", mdp->id); |
| @@ -212,8 +227,10 @@ err_alloc_wdt_wq: · 12 unmodified lines | ||
| 212 | 227 | err_alloc_job_wq: |
| 213 | 228 | |
| 214 | 229 | err_comp: |
| 215 | − for (i = 0; i < ARRAY_SIZE(mdp->comp); i++) | |
| 216 | − mtk_mdp_comp_deinit(dev, mdp->comp[i]); | |
| 230 | + list_for_each_entry_safe(comp, comp_temp, &mdp->comp_list, node) { | |
| 231 | + mtk_mdp_unregister_component(mdp, comp); | |
| 232 | + mtk_mdp_comp_deinit(dev, comp); | |
| 233 | + } | |
| 217 | 234 | |
| 218 | 235 | dev_dbg(dev, "err %d\n", ret); |
| 219 | 236 | return ret; |
| @@ -222,7 +239,7 @@ err_comp: · 2 unmodified lines | ||
| 222 | 239 | static int mtk_mdp_remove(struct platform_device *pdev) |
| 223 | 240 | { |
| 224 | 241 | struct mtk_mdp_dev *mdp = platform_get_drvdata(pdev); |
| 225 | − int i; | |
| 242 | + struct mtk_mdp_comp *comp, *comp_temp; | |
| 226 | 243 | |
| 227 | 244 | pm_runtime_disable(&pdev->dev); |
| 228 | 245 | vb2_dma_contig_clear_max_seg_size(&pdev->dev); |
| @@ -235,8 +252,10 @@ static int mtk_mdp_remove(struct platform_device *pdev) · 6 unmodified lines | ||
| 235 | 252 | flush_workqueue(mdp->job_wq); |
| 236 | 253 | destroy_workqueue(mdp->job_wq); |
| 237 | 254 | |
| 238 | − for (i = 0; i < ARRAY_SIZE(mdp->comp); i++) | |
| 239 | − mtk_mdp_comp_deinit(&pdev->dev, mdp->comp[i]); | |
| 255 | + list_for_each_entry_safe(comp, comp_temp, &mdp->comp_list, node) { | |
| 256 | + mtk_mdp_unregister_component(mdp, comp); | |
| 257 | + mtk_mdp_comp_deinit(&pdev->dev, comp); | |
| 258 | + } | |
| 240 | 259 | |
| 241 | 260 | dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name); |
| 242 | 261 | return 0; |
| @@ -28,8 +28,6 @@ · 27 unmodified lines | ||
| 28 | 28 | #define MTK_MDP_FMT_FLAG_CAPTURE BIT(1) |
| 29 | 29 | |
| 30 | 30 | #define MTK_MDP_VPU_INIT BIT(0) |
| 31 | −#define MTK_MDP_SRC_FMT BIT(1) | |
| 32 | −#define MTK_MDP_DST_FMT BIT(2) | |
| 33 | 31 | #define MTK_MDP_CTX_ERROR BIT(5) |
| 34 | 32 | |
| 35 | 33 | /** |
| @@ -138,7 +136,7 @@ struct mtk_mdp_variant { · 102 unmodified lines | ||
| 138 | 136 | * @pdev: pointer to the image processor platform device |
| 139 | 137 | * @variant: the IP variant information |
| 140 | 138 | * @id: image processor device index (0..MTK_MDP_MAX_DEVS) |
| 141 | − * @comp: MDP function components | |
| 139 | + * @comp_list: list of MDP function components | |
| 142 | 140 | * @m2m_dev: v4l2 memory-to-memory device data |
| 143 | 141 | * @ctx_list: list of struct mtk_mdp_ctx |
| 144 | 142 | * @vdev: video device for image processor driver |
| @@ -156,7 +154,7 @@ struct mtk_mdp_dev { · 11 unmodified lines | ||
| 156 | 154 | struct platform_device *pdev; |
| 157 | 155 | struct mtk_mdp_variant *variant; |
| 158 | 156 | u16 id; |
| 159 | − struct mtk_mdp_comp *comp[MTK_MDP_COMP_ID_MAX]; | |
| 157 | + struct list_head comp_list; | |
| 160 | 158 | struct v4l2_m2m_dev *m2m_dev; |
| 161 | 159 | struct list_head ctx_list; |
| 162 | 160 | struct video_device *vdev; |
| @@ -223,6 +221,12 @@ struct mtk_mdp_ctx { · 60 unmodified lines | ||
| 223 | 221 | |
| 224 | 222 | extern int mtk_mdp_dbg_level; |
| 225 | 223 | |
| 224 | +void mtk_mdp_register_component(struct mtk_mdp_dev *mdp, | |
| 225 | + struct mtk_mdp_comp *comp); | |
| 226 | + | |
| 227 | +void mtk_mdp_unregister_component(struct mtk_mdp_dev *mdp, | |
| 228 | + struct mtk_mdp_comp *comp); | |
| 229 | + | |
| 226 | 230 | #if defined(DEBUG) |
| 227 | 231 | |
| 228 | 232 | #define mtk_mdp_dbg(level, fmt, args...) \ |
| @@ -193,7 +193,7 @@ static const struct mtk_mdp_fmt *mtk_mdp_try_fmt_mplane(struct mtk_mdp_ctx *ctx, · 192 unmodified lines | ||
| 193 | 193 | |
| 194 | 194 | pix_mp->field = V4L2_FIELD_NONE; |
| 195 | 195 | pix_mp->pixelformat = fmt->pixelformat; |
| 196 | − if (!V4L2_TYPE_IS_OUTPUT(f->type)) { | |
| 196 | + if (V4L2_TYPE_IS_CAPTURE(f->type)) { | |
| 197 | 197 | pix_mp->colorspace = ctx->colorspace; |
| 198 | 198 | pix_mp->xfer_func = ctx->xfer_func; |
| 199 | 199 | pix_mp->ycbcr_enc = ctx->ycbcr_enc; |
| @@ -327,9 +327,8 @@ static int mtk_mdp_try_crop(struct mtk_mdp_ctx *ctx, u32 type, · 127 unmodified lines | ||
| 327 | 327 | mtk_mdp_bound_align_image(&new_w, min_w, max_w, align_w, |
| 328 | 328 | &new_h, min_h, max_h, align_h); |
| 329 | 329 | |
| 330 | − if (!V4L2_TYPE_IS_OUTPUT(type) && | |
| 331 | − (ctx->ctrls.rotate->val == 90 || | |
| 332 | − ctx->ctrls.rotate->val == 270)) | |
| 330 | + if (V4L2_TYPE_IS_CAPTURE(type) && | |
| 331 | + (ctx->ctrls.rotate->val == 90 || ctx->ctrls.rotate->val == 270)) | |
| 333 | 332 | mtk_mdp_check_crop_change(new_h, new_w, |
| 334 | 333 | &r->width, &r->height); |
| 335 | 334 | else |
| @@ -369,13 +368,6 @@ void mtk_mdp_ctx_state_lock_set(struct mtk_mdp_ctx *ctx, u32 state) · 33 unmodified lines | ||
| 369 | 368 | mutex_unlock(&ctx->slock); |
| 370 | 369 | } |
| 371 | 370 | |
| 372 | −static void mtk_mdp_ctx_state_lock_clear(struct mtk_mdp_ctx *ctx, u32 state) | |
| 373 | −{ | |
| 374 | − mutex_lock(&ctx->slock); | |
| 375 | − ctx->state &= ~state; | |
| 376 | − mutex_unlock(&ctx->slock); | |
| 377 | −} | |
| 378 | − | |
| 379 | 371 | static bool mtk_mdp_ctx_state_is_set(struct mtk_mdp_ctx *ctx, u32 mask) |
| 380 | 372 | { |
| 381 | 373 | bool ret; |
| @@ -726,11 +718,6 @@ static int mtk_mdp_m2m_s_fmt_mplane(struct file *file, void *fh, · 344 unmodified lines | ||
| 726 | 718 | ctx->quant = pix_mp->quantization; |
| 727 | 719 | } |
| 728 | 720 | |
| 729 | − if (V4L2_TYPE_IS_OUTPUT(f->type)) | |
| 730 | − mtk_mdp_ctx_state_lock_set(ctx, MTK_MDP_SRC_FMT); | |
| 731 | − else | |
| 732 | − mtk_mdp_ctx_state_lock_set(ctx, MTK_MDP_DST_FMT); | |
| 733 | − | |
| 734 | 721 | mtk_mdp_dbg(2, "[%d] type:%d, frame:%dx%d", ctx->id, f->type, |
| 735 | 722 | frame->width, frame->height); |
| 736 | 723 | |
| @@ -742,13 +729,6 @@ static int mtk_mdp_m2m_reqbufs(struct file *file, void *fh, · 5 unmodified lines | ||
| 742 | 729 | { |
| 743 | 730 | struct mtk_mdp_ctx *ctx = fh_to_ctx(fh); |
| 744 | 731 | |
| 745 | − if (reqbufs->count == 0) { | |
| 746 | − if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) | |
| 747 | − mtk_mdp_ctx_state_lock_clear(ctx, MTK_MDP_SRC_FMT); | |
| 748 | − else | |
| 749 | − mtk_mdp_ctx_state_lock_clear(ctx, MTK_MDP_DST_FMT); | |
| 750 | − } | |
| 751 | − | |
| 752 | 732 | return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); |
| 753 | 733 | } |
| 754 | 734 | |
| @@ -758,14 +738,6 @@ static int mtk_mdp_m2m_streamon(struct file *file, void *fh, · 3 unmodified lines | ||
| 758 | 738 | struct mtk_mdp_ctx *ctx = fh_to_ctx(fh); |
| 759 | 739 | int ret; |
| 760 | 740 | |
| 761 | − /* The source and target color format need to be set */ | |
| 762 | − if (V4L2_TYPE_IS_OUTPUT(type)) { | |
| 763 | − if (!mtk_mdp_ctx_state_is_set(ctx, MTK_MDP_SRC_FMT)) | |
| 764 | − return -EINVAL; | |
| 765 | − } else if (!mtk_mdp_ctx_state_is_set(ctx, MTK_MDP_DST_FMT)) { | |
| 766 | − return -EINVAL; | |
| 767 | − } | |
| 768 | − | |
| 769 | 741 | if (!mtk_mdp_ctx_state_is_set(ctx, MTK_MDP_VPU_INIT)) { |
| 770 | 742 | ret = mtk_mdp_vpu_init(&ctx->vpu); |
| 771 | 743 | if (ret < 0) { |
| @@ -899,24 +871,21 @@ static int mtk_mdp_m2m_s_selection(struct file *file, void *fh, · 127 unmodified lines | ||
| 899 | 871 | frame = &ctx->d_frame; |
| 900 | 872 | |
| 901 | 873 | /* Check to see if scaling ratio is within supported range */ |
| 902 | − if (mtk_mdp_ctx_state_is_set(ctx, MTK_MDP_DST_FMT | MTK_MDP_SRC_FMT)) { | |
| 903 | − if (V4L2_TYPE_IS_OUTPUT(s->type)) { | |
| 904 | − ret = mtk_mdp_check_scaler_ratio(variant, new_r.width, | |
| 905 | − new_r.height, ctx->d_frame.crop.width, | |
| 906 | − ctx->d_frame.crop.height, | |
| 907 | − ctx->ctrls.rotate->val); | |
| 908 | − } else { | |
| 909 | − ret = mtk_mdp_check_scaler_ratio(variant, | |
| 910 | − ctx->s_frame.crop.width, | |
| 911 | − ctx->s_frame.crop.height, new_r.width, | |
| 912 | − new_r.height, ctx->ctrls.rotate->val); | |
| 913 | − } | |
| 874 | + if (V4L2_TYPE_IS_OUTPUT(s->type)) | |
| 875 | + ret = mtk_mdp_check_scaler_ratio(variant, new_r.width, | |
| 876 | + new_r.height, ctx->d_frame.crop.width, | |
| 877 | + ctx->d_frame.crop.height, | |
| 878 | + ctx->ctrls.rotate->val); | |
| 879 | + else | |
| 880 | + ret = mtk_mdp_check_scaler_ratio(variant, | |
| 881 | + ctx->s_frame.crop.width, | |
| 882 | + ctx->s_frame.crop.height, new_r.width, | |
| 883 | + new_r.height, ctx->ctrls.rotate->val); | |
| 914 | 884 | |
| 915 | − if (ret) { | |
| 916 | − dev_info(&ctx->mdp_dev->pdev->dev, | |
| 917 | − "Out of scaler range"); | |
| 918 | − return -EINVAL; | |
| 919 | − } | |
| 885 | + if (ret) { | |
| 886 | + dev_info(&ctx->mdp_dev->pdev->dev, | |
| 887 | + "Out of scaler range"); | |
| 888 | + return -EINVAL; | |
| 920 | 889 | } |
| 921 | 890 | |
| 922 | 891 | s->r = new_r; |
| @@ -989,7 +958,6 @@ static int mtk_mdp_s_ctrl(struct v4l2_ctrl *ctrl) · 66 unmodified lines | ||
| 989 | 958 | struct mtk_mdp_ctx *ctx = ctrl_to_ctx(ctrl); |
| 990 | 959 | struct mtk_mdp_dev *mdp = ctx->mdp_dev; |
| 991 | 960 | struct mtk_mdp_variant *variant = mdp->variant; |
| 992 | − u32 state = MTK_MDP_DST_FMT | MTK_MDP_SRC_FMT; | |
| 993 | 961 | int ret = 0; |
| 994 | 962 | |
| 995 | 963 | if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) |
| @@ -1003,17 +971,15 @@ static int mtk_mdp_s_ctrl(struct v4l2_ctrl *ctrl) · 7 unmodified lines | ||
| 1003 | 971 | ctx->vflip = ctrl->val; |
| 1004 | 972 | break; |
| 1005 | 973 | case V4L2_CID_ROTATE: |
| 1006 | − if (mtk_mdp_ctx_state_is_set(ctx, state)) { | |
| 1007 | − ret = mtk_mdp_check_scaler_ratio(variant, | |
| 1008 | − ctx->s_frame.crop.width, | |
| 1009 | − ctx->s_frame.crop.height, | |
| 1010 | − ctx->d_frame.crop.width, | |
| 1011 | − ctx->d_frame.crop.height, | |
| 1012 | − ctx->ctrls.rotate->val); | |
| 1013 | − | |
| 1014 | − if (ret) | |
| 1015 | − return -EINVAL; | |
| 1016 | − } | |
| 974 | + ret = mtk_mdp_check_scaler_ratio(variant, | |
| 975 | + ctx->s_frame.crop.width, | |
| 976 | + ctx->s_frame.crop.height, | |
| 977 | + ctx->d_frame.crop.width, | |
| 978 | + ctx->d_frame.crop.height, | |
| 979 | + ctx->ctrls.rotate->val); | |
| 980 | + | |
| 981 | + if (ret) | |
| 982 | + return -EINVAL; | |
| 1017 | 983 | |
| 1018 | 984 | ctx->rotation = ctrl->val; |
| 1019 | 985 | break; |
| @@ -1090,6 +1056,7 @@ static int mtk_mdp_m2m_open(struct file *file) · 70 unmodified lines | ||
| 1090 | 1056 | struct video_device *vfd = video_devdata(file); |
| 1091 | 1057 | struct mtk_mdp_ctx *ctx = NULL; |
| 1092 | 1058 | int ret; |
| 1059 | + struct v4l2_format default_format; | |
| 1093 | 1060 | |
| 1094 | 1061 | ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); |
| 1095 | 1062 | if (!ctx) |
| @@ -1144,6 +1111,16 @@ static int mtk_mdp_m2m_open(struct file *file) · 48 unmodified lines | ||
| 1144 | 1111 | list_add(&ctx->list, &mdp->ctx_list); |
| 1145 | 1112 | mutex_unlock(&mdp->lock); |
| 1146 | 1113 | |
| 1114 | + /* Default format */ | |
| 1115 | + memset(&default_format, 0, sizeof(default_format)); | |
| 1116 | + default_format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
| 1117 | + default_format.fmt.pix_mp.width = 32; | |
| 1118 | + default_format.fmt.pix_mp.height = 32; | |
| 1119 | + default_format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_YUV420M; | |
| 1120 | + mtk_mdp_m2m_s_fmt_mplane(file, &ctx->fh, &default_format); | |
| 1121 | + default_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
| 1122 | + mtk_mdp_m2m_s_fmt_mplane(file, &ctx->fh, &default_format); | |
| 1123 | + | |
| 1147 | 1124 | mtk_mdp_dbg(0, "%s [%d]", dev_name(&mdp->pdev->dev), ctx->id); |
| 1148 | 1125 | |
| 1149 | 1126 | return 0; |
| @@ -2287,7 +2287,7 @@ static int preview_init_entities(struct isp_prev_device *prev) · 2286 unmodified lines | ||
| 2287 | 2287 | me->ops = &preview_media_ops; |
| 2288 | 2288 | ret = media_entity_pads_init(me, PREV_PADS_NUM, pads); |
| 2289 | 2289 | if (ret < 0) |
| 2290 | − return ret; | |
| 2290 | + goto error_handler_free; | |
| 2291 | 2291 | |
| 2292 | 2292 | preview_init_formats(sd, NULL); |
| 2293 | 2293 | |
| @@ -2320,6 +2320,8 @@ error_video_out: · 26 unmodified lines | ||
| 2320 | 2320 | omap3isp_video_cleanup(&prev->video_in); |
| 2321 | 2321 | error_video_in: |
| 2322 | 2322 | media_entity_cleanup(&prev->subdev.entity); |
| 2323 | +error_handler_free: | |
| 2324 | + v4l2_ctrl_handler_free(&prev->ctrls); | |
| 2323 | 2325 | return ret; |
| 2324 | 2326 | } |
| 2325 | 2327 | |
| @@ -2504,17 +2504,14 @@ static int pxa_camera_probe(struct platform_device *pdev) · 2503 unmodified lines | ||
| 2504 | 2504 | if (err) |
| 2505 | 2505 | goto exit_notifier_cleanup; |
| 2506 | 2506 | |
| 2507 | − if (pcdev->mclk) { | |
| 2508 | − v4l2_clk_name_i2c(clk_name, sizeof(clk_name), | |
| 2509 | − pcdev->asd.match.i2c.adapter_id, | |
| 2510 | − pcdev->asd.match.i2c.address); | |
| 2511 | − | |
| 2512 | − pcdev->mclk_clk = v4l2_clk_register(&pxa_camera_mclk_ops, | |
| 2513 | − clk_name, NULL); | |
| 2514 | − if (IS_ERR(pcdev->mclk_clk)) { | |
| 2515 | − err = PTR_ERR(pcdev->mclk_clk); | |
| 2516 | − goto exit_notifier_cleanup; | |
| 2517 | − } | |
| 2507 | + v4l2_clk_name_i2c(clk_name, sizeof(clk_name), | |
| 2508 | + pcdev->asd.match.i2c.adapter_id, | |
| 2509 | + pcdev->asd.match.i2c.address); | |
| 2510 | + | |
| 2511 | + pcdev->mclk_clk = v4l2_clk_register(&pxa_camera_mclk_ops, clk_name, NULL); | |
| 2512 | + if (IS_ERR(pcdev->mclk_clk)) { | |
| 2513 | + err = PTR_ERR(pcdev->mclk_clk); | |
| 2514 | + goto exit_notifier_cleanup; | |
| 2518 | 2515 | } |
| 2519 | 2516 | |
| 2520 | 2517 | err = v4l2_async_notifier_register(&pcdev->v4l2_dev, &pcdev->notifier); |
| @@ -2588,7 +2585,7 @@ static struct platform_driver pxa_camera_driver = { · 67 unmodified lines | ||
| 2588 | 2585 | |
| 2589 | 2586 | module_platform_driver(pxa_camera_driver); |
| 2590 | 2587 | |
| 2591 | −MODULE_DESCRIPTION("PXA27x SoC Camera Host driver"); | |
| 2588 | +MODULE_DESCRIPTION("PXA27x Camera Driver"); | |
| 2592 | 2589 | MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>"); |
| 2593 | 2590 | MODULE_LICENSE("GPL"); |
| 2594 | 2591 | MODULE_VERSION(PXA_CAM_VERSION); |
| @@ -562,8 +562,10 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) · 561 unmodified lines | ||
| 562 | 562 | u32 hw_version; |
| 563 | 563 | |
| 564 | 564 | ret = pm_runtime_get_sync(dev); |
| 565 | − if (ret < 0) | |
| 565 | + if (ret < 0) { | |
| 566 | + pm_runtime_put_sync(dev); | |
| 566 | 567 | return ret; |
| 568 | + } | |
| 567 | 569 | |
| 568 | 570 | ret = regulator_enable(csid->vdda); |
| 569 | 571 | if (ret < 0) { |
| @@ -1356,7 +1358,7 @@ int msm_csid_register_entity(struct csid_device *csid, · 786 unmodified lines | ||
| 1356 | 1358 | pads[MSM_CSID_PAD_SINK].flags = MEDIA_PAD_FL_SINK; |
| 1357 | 1359 | pads[MSM_CSID_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; |
| 1358 | 1360 | |
| 1359 | − sd->entity.function = MEDIA_ENT_F_IO_V4L; | |
| 1361 | + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; | |
| 1360 | 1362 | sd->entity.ops = &csid_media_ops; |
| 1361 | 1363 | ret = media_entity_pads_init(&sd->entity, MSM_CSID_PADS_NUM, pads); |
| 1362 | 1364 | if (ret < 0) { |
| @@ -737,7 +737,7 @@ int msm_csiphy_register_entity(struct csiphy_device *csiphy, · 736 unmodified lines | ||
| 737 | 737 | pads[MSM_CSIPHY_PAD_SINK].flags = MEDIA_PAD_FL_SINK; |
| 738 | 738 | pads[MSM_CSIPHY_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; |
| 739 | 739 | |
| 740 | − sd->entity.function = MEDIA_ENT_F_IO_V4L; | |
| 740 | + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; | |
| 741 | 741 | sd->entity.ops = &csiphy_media_ops; |
| 742 | 742 | ret = media_entity_pads_init(&sd->entity, MSM_CSIPHY_PADS_NUM, pads); |
| 743 | 743 | if (ret < 0) { |
| @@ -344,8 +344,10 @@ static int ispif_set_power(struct v4l2_subdev *sd, int on) · 343 unmodified lines | ||
| 344 | 344 | } |
| 345 | 345 | |
| 346 | 346 | ret = pm_runtime_get_sync(dev); |
| 347 | − if (ret < 0) | |
| 347 | + if (ret < 0) { | |
| 348 | + pm_runtime_put_sync(dev); | |
| 348 | 349 | goto exit; |
| 350 | + } | |
| 349 | 351 | |
| 350 | 352 | ret = camss_enable_clocks(ispif->nclocks, ispif->clock, dev); |
| 351 | 353 | if (ret < 0) { |
| @@ -1323,7 +1325,7 @@ int msm_ispif_register_entities(struct ispif_device *ispif, · 971 unmodified lines | ||
| 1323 | 1325 | pads[MSM_ISPIF_PAD_SINK].flags = MEDIA_PAD_FL_SINK; |
| 1324 | 1326 | pads[MSM_ISPIF_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; |
| 1325 | 1327 | |
| 1326 | − sd->entity.function = MEDIA_ENT_F_IO_V4L; | |
| 1328 | + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; | |
| 1327 | 1329 | sd->entity.ops = &ispif_media_ops; |
| 1328 | 1330 | ret = media_entity_pads_init(&sd->entity, MSM_ISPIF_PADS_NUM, |
| 1329 | 1331 | pads); |
| @@ -1265,12 +1265,12 @@ static int vfe_get(struct vfe_device *vfe) · 1264 unmodified lines | ||
| 1265 | 1265 | |
| 1266 | 1266 | ret = vfe_set_clock_rates(vfe); |
| 1267 | 1267 | if (ret < 0) |
| 1268 | − goto error_clocks; | |
| 1268 | + goto error_pm_runtime_get; | |
| 1269 | 1269 | |
| 1270 | 1270 | ret = camss_enable_clocks(vfe->nclocks, vfe->clock, |
| 1271 | 1271 | vfe->camss->dev); |
| 1272 | 1272 | if (ret < 0) |
| 1273 | − goto error_clocks; | |
| 1273 | + goto error_pm_runtime_get; | |
| 1274 | 1274 | |
| 1275 | 1275 | ret = vfe_reset(vfe); |
| 1276 | 1276 | if (ret < 0) |
| @@ -1282,7 +1282,7 @@ static int vfe_get(struct vfe_device *vfe) · 5 unmodified lines | ||
| 1282 | 1282 | } else { |
| 1283 | 1283 | ret = vfe_check_clock_rates(vfe); |
| 1284 | 1284 | if (ret < 0) |
| 1285 | − goto error_clocks; | |
| 1285 | + goto error_pm_runtime_get; | |
| 1286 | 1286 | } |
| 1287 | 1287 | vfe->power_count++; |
| 1288 | 1288 | |
| @@ -1293,10 +1293,8 @@ static int vfe_get(struct vfe_device *vfe) · 4 unmodified lines | ||
| 1293 | 1293 | error_reset: |
| 1294 | 1294 | camss_disable_clocks(vfe->nclocks, vfe->clock); |
| 1295 | 1295 | |
| 1296 | −error_clocks: | |
| 1297 | − pm_runtime_put_sync(vfe->camss->dev); | |
| 1298 | − | |
| 1299 | 1296 | error_pm_runtime_get: |
| 1297 | + pm_runtime_put_sync(vfe->camss->dev); | |
| 1300 | 1298 | camss_pm_domain_off(vfe->camss, vfe->id); |
| 1301 | 1299 | |
| 1302 | 1300 | error_pm_domain: |
| @@ -504,7 +504,6 @@ static int camss_of_parse_ports(struct camss *camss) · 503 unmodified lines | ||
| 504 | 504 | return num_subdevs; |
| 505 | 505 | |
| 506 | 506 | err_cleanup: |
| 507 | − v4l2_async_notifier_cleanup(&camss->notifier); | |
| 508 | 507 | of_node_put(node); |
| 509 | 508 | return ret; |
| 510 | 509 | } |
| @@ -835,29 +834,38 @@ static int camss_probe(struct platform_device *pdev) · 324 unmodified lines | ||
| 835 | 834 | camss->csid_num = 4; |
| 836 | 835 | camss->vfe_num = 2; |
| 837 | 836 | } else { |
| 838 | − return -EINVAL; | |
| 837 | + ret = -EINVAL; | |
| 838 | + goto err_free; | |
| 839 | 839 | } |
| 840 | 840 | |
| 841 | 841 | camss->csiphy = devm_kcalloc(dev, camss->csiphy_num, |
| 842 | 842 | sizeof(*camss->csiphy), GFP_KERNEL); |
| 843 | − if (!camss->csiphy) | |
| 844 | − return -ENOMEM; | |
| 843 | + if (!camss->csiphy) { | |
| 844 | + ret = -ENOMEM; | |
| 845 | + goto err_free; | |
| 846 | + } | |
| 845 | 847 | |
| 846 | 848 | camss->csid = devm_kcalloc(dev, camss->csid_num, sizeof(*camss->csid), |
| 847 | 849 | GFP_KERNEL); |
| 848 | − if (!camss->csid) | |
| 849 | − return -ENOMEM; | |
| 850 | + if (!camss->csid) { | |
| 851 | + ret = -ENOMEM; | |
| 852 | + goto err_free; | |
| 853 | + } | |
| 850 | 854 | |
| 851 | 855 | camss->vfe = devm_kcalloc(dev, camss->vfe_num, sizeof(*camss->vfe), |
| 852 | 856 | GFP_KERNEL); |
| 853 | − if (!camss->vfe) | |
| 854 | − return -ENOMEM; | |
| 857 | + if (!camss->vfe) { | |
| 858 | + ret = -ENOMEM; | |
| 859 | + goto err_free; | |
| 860 | + } | |
| 855 | 861 | |
| 856 | 862 | v4l2_async_notifier_init(&camss->notifier); |
| 857 | 863 | |
| 858 | 864 | num_subdevs = camss_of_parse_ports(camss); |
| 859 | − if (num_subdevs < 0) | |
| 860 | − return num_subdevs; | |
| 865 | + if (num_subdevs < 0) { | |
| 866 | + ret = num_subdevs; | |
| 867 | + goto err_cleanup; | |
| 868 | + } | |
| 861 | 869 | |
| 862 | 870 | ret = camss_init_subdevices(camss); |
| 863 | 871 | if (ret < 0) |
| @@ -936,6 +944,8 @@ err_register_entities: · 72 unmodified lines | ||
| 936 | 944 | v4l2_device_unregister(&camss->v4l2_dev); |
| 937 | 945 | err_cleanup: |
| 938 | 946 | v4l2_async_notifier_cleanup(&camss->notifier); |
| 947 | +err_free: | |
| 948 | + kfree(camss); | |
| 939 | 949 | |
| 940 | 950 | return ret; |
| 941 | 951 | } |
| @@ -496,6 +496,10 @@ min_loaded_core(struct venus_inst *inst, u32 *min_coreid, u32 *min_load) · 495 unmodified lines | ||
| 496 | 496 | list_for_each_entry(inst_pos, &core->instances, list) { |
| 497 | 497 | if (inst_pos == inst) |
| 498 | 498 | continue; |
| 499 | + | |
| 500 | + if (inst_pos->state != INST_START) | |
| 501 | + continue; | |
| 502 | + | |
| 499 | 503 | vpp_freq = inst_pos->clk_data.codec_freq_data->vpp_freq; |
| 500 | 504 | coreid = inst_pos->clk_data.core_id; |
| 501 | 505 | |
| @@ -1177,7 +1177,7 @@ static int venc_open(struct file *file) · 1176 unmodified lines | ||
| 1177 | 1177 | |
| 1178 | 1178 | ret = pm_runtime_get_sync(core->dev_enc); |
| 1179 | 1179 | if (ret < 0) |
| 1180 | − goto err_free_inst; | |
| 1180 | + goto err_put_sync; | |
| 1181 | 1181 | |
| 1182 | 1182 | ret = venc_ctrl_init(inst); |
| 1183 | 1183 | if (ret) |
| @@ -1222,7 +1222,6 @@ err_ctrl_deinit: · 38 unmodified lines | ||
| 1222 | 1222 | venc_ctrl_deinit(inst); |
| 1223 | 1223 | err_put_sync: |
| 1224 | 1224 | pm_runtime_put_sync(core->dev_enc); |
| 1225 | −err_free_inst: | |
| 1226 | 1225 | kfree(inst); |
| 1227 | 1226 | return ret; |
| 1228 | 1227 | } |
| @@ -320,6 +320,7 @@ static const struct rcar_csi2_format rcar_csi2_formats[] = { · 319 unmodified lines | ||
| 320 | 320 | { .code = MEDIA_BUS_FMT_YUYV8_1X16, .datatype = 0x1e, .bpp = 16 }, |
| 321 | 321 | { .code = MEDIA_BUS_FMT_UYVY8_2X8, .datatype = 0x1e, .bpp = 16 }, |
| 322 | 322 | { .code = MEDIA_BUS_FMT_YUYV10_2X10, .datatype = 0x1e, .bpp = 20 }, |
| 323 | + { .code = MEDIA_BUS_FMT_SRGGB8_1X8, .datatype = 0x2a, .bpp = 8 }, | |
| 323 | 324 | }; |
| 324 | 325 | |
| 325 | 326 | static const struct rcar_csi2_format *rcsi2_code_to_fmt(unsigned int code) |
| @@ -344,7 +345,7 @@ enum rcar_csi2_pads { · 18 unmodified lines | ||
| 344 | 345 | |
| 345 | 346 | struct rcar_csi2_info { |
| 346 | 347 | int (*init_phtw)(struct rcar_csi2 *priv, unsigned int mbps); |
| 347 | − int (*confirm_start)(struct rcar_csi2 *priv); | |
| 348 | + int (*phy_post_init)(struct rcar_csi2 *priv); | |
| 348 | 349 | const struct rcsi2_mbps_reg *hsfreqrange; |
| 349 | 350 | unsigned int csi0clkfreqrange; |
| 350 | 351 | unsigned int num_channels; |
| @@ -575,9 +576,9 @@ static int rcsi2_start_receiver(struct rcar_csi2 *priv) · 224 unmodified lines | ||
| 575 | 576 | if (ret) |
| 576 | 577 | return ret; |
| 577 | 578 | |
| 578 | − /* Confirm start */ | |
| 579 | − if (priv->info->confirm_start) { | |
| 580 | − ret = priv->info->confirm_start(priv); | |
| 579 | + /* Run post PHY start initialization, if needed. */ | |
| 580 | + if (priv->info->phy_post_init) { | |
| 581 | + ret = priv->info->phy_post_init(priv); | |
| 581 | 582 | if (ret) |
| 582 | 583 | return ret; |
| 583 | 584 | } |
| @@ -975,7 +976,7 @@ static int rcsi2_init_phtw_v3m_e3(struct rcar_csi2 *priv, unsigned int mbps) · 391 unmodified lines | ||
| 975 | 976 | return rcsi2_phtw_write_mbps(priv, mbps, phtw_mbps_v3m_e3, 0x44); |
| 976 | 977 | } |
| 977 | 978 | |
| 978 | −static int rcsi2_confirm_start_v3m_e3(struct rcar_csi2 *priv) | |
| 979 | +static int rcsi2_phy_post_init_v3m_e3(struct rcar_csi2 *priv) | |
| 979 | 980 | { |
| 980 | 981 | static const struct phtw_value step1[] = { |
| 981 | 982 | { .data = 0xee, .code = 0x34 }, |
| @@ -1059,7 +1060,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a77965 = { · 77 unmodified lines | ||
| 1059 | 1060 | |
| 1060 | 1061 | static const struct rcar_csi2_info rcar_csi2_info_r8a77970 = { |
| 1061 | 1062 | .init_phtw = rcsi2_init_phtw_v3m_e3, |
| 1062 | − .confirm_start = rcsi2_confirm_start_v3m_e3, | |
| 1063 | + .phy_post_init = rcsi2_phy_post_init_v3m_e3, | |
| 1063 | 1064 | .num_channels = 4, |
| 1064 | 1065 | }; |
| 1065 | 1066 | |
| @@ -1072,7 +1073,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a77980 = { · 6 unmodified lines | ||
| 1072 | 1073 | |
| 1073 | 1074 | static const struct rcar_csi2_info rcar_csi2_info_r8a77990 = { |
| 1074 | 1075 | .init_phtw = rcsi2_init_phtw_v3m_e3, |
| 1075 | − .confirm_start = rcsi2_confirm_start_v3m_e3, | |
| 1076 | + .phy_post_init = rcsi2_phy_post_init_v3m_e3, | |
| 1076 | 1077 | .num_channels = 2, |
| 1077 | 1078 | }; |
| 1078 | 1079 | |
| @@ -85,6 +85,7 @@ · 84 unmodified lines | ||
| 85 | 85 | #define VNMC_INF_YUV8_BT601 (1 << 16) |
| 86 | 86 | #define VNMC_INF_YUV10_BT656 (2 << 16) |
| 87 | 87 | #define VNMC_INF_YUV10_BT601 (3 << 16) |
| 88 | +#define VNMC_INF_RAW8 (4 << 16) | |
| 88 | 89 | #define VNMC_INF_YUV16 (5 << 16) |
| 89 | 90 | #define VNMC_INF_RGB888 (6 << 16) |
| 90 | 91 | #define VNMC_VUP (1 << 10) |
| @@ -587,13 +588,19 @@ void rvin_crop_scale_comp(struct rvin_dev *vin) · 496 unmodified lines | ||
| 587 | 588 | rvin_write(vin, vin->crop.top, VNSLPRC_REG); |
| 588 | 589 | rvin_write(vin, vin->crop.top + vin->crop.height - 1, VNELPRC_REG); |
| 589 | 590 | |
| 590 | − | |
| 591 | 591 | /* TODO: Add support for the UDS scaler. */ |
| 592 | 592 | if (vin->info->model != RCAR_GEN3) |
| 593 | 593 | rvin_crop_scale_comp_gen2(vin); |
| 594 | 594 | |
| 595 | 595 | fmt = rvin_format_from_pixel(vin, vin->format.pixelformat); |
| 596 | 596 | stride = vin->format.bytesperline / fmt->bpp; |
| 597 | + | |
| 598 | + /* For RAW8 format bpp is 1, but the hardware process RAW8 | |
| 599 | + * format in 2 pixel unit hence configure VNIS_REG as stride / 2. | |
| 600 | + */ | |
| 601 | + if (vin->format.pixelformat == V4L2_PIX_FMT_SRGGB8) | |
| 602 | + stride /= 2; | |
| 603 | + | |
| 597 | 604 | rvin_write(vin, stride, VNIS_REG); |
| 598 | 605 | } |
| 599 | 606 | |
| @@ -676,6 +683,9 @@ static int rvin_setup(struct rvin_dev *vin) · 76 unmodified lines | ||
| 676 | 683 | |
| 677 | 684 | input_is_yuv = true; |
| 678 | 685 | break; |
| 686 | + case MEDIA_BUS_FMT_SRGGB8_1X8: | |
| 687 | + vnmc |= VNMC_INF_RAW8; | |
| 688 | + break; | |
| 679 | 689 | default: |
| 680 | 690 | break; |
| 681 | 691 | } |
| @@ -737,6 +747,9 @@ static int rvin_setup(struct rvin_dev *vin) · 55 unmodified lines | ||
| 737 | 747 | case V4L2_PIX_FMT_ABGR32: |
| 738 | 748 | dmr = VNDMR_A8BIT(vin->alpha) | VNDMR_EXRGB | VNDMR_DTMD_ARGB; |
| 739 | 749 | break; |
| 750 | + case V4L2_PIX_FMT_SRGGB8: | |
| 751 | + dmr = 0; | |
| 752 | + break; | |
| 740 | 753 | default: |
| 741 | 754 | vin_err(vin, "Invalid pixelformat (0x%x)\n", |
| 742 | 755 | vin->format.pixelformat); |
| @@ -1110,11 +1123,15 @@ static int rvin_mc_validate_format(struct rvin_dev *vin, struct v4l2_subdev *sd, · 367 unmodified lines | ||
| 1110 | 1123 | case MEDIA_BUS_FMT_UYVY8_2X8: |
| 1111 | 1124 | case MEDIA_BUS_FMT_UYVY10_2X10: |
| 1112 | 1125 | case MEDIA_BUS_FMT_RGB888_1X24: |
| 1113 | − vin->mbus_code = fmt.format.code; | |
| 1126 | + break; | |
| 1127 | + case MEDIA_BUS_FMT_SRGGB8_1X8: | |
| 1128 | + if (vin->format.pixelformat != V4L2_PIX_FMT_SRGGB8) | |
| 1129 | + return -EPIPE; | |
| 1114 | 1130 | break; |
| 1115 | 1131 | default: |
| 1116 | 1132 | return -EPIPE; |
| 1117 | 1133 | } |
| 1134 | + vin->mbus_code = fmt.format.code; | |
| 1118 | 1135 | |
| 1119 | 1136 | switch (fmt.format.field) { |
| 1120 | 1137 | case V4L2_FIELD_TOP: |
| @@ -66,6 +66,10 @@ static const struct rvin_video_format rvin_formats[] = { · 65 unmodified lines | ||
| 66 | 66 | .fourcc = V4L2_PIX_FMT_ABGR32, |
| 67 | 67 | .bpp = 4, |
| 68 | 68 | }, |
| 69 | + { | |
| 70 | + .fourcc = V4L2_PIX_FMT_SRGGB8, | |
| 71 | + .bpp = 1, | |
| 72 | + }, | |
| 69 | 73 | }; |
| 70 | 74 | |
| 71 | 75 | 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, · 278 unmodified lines | ||
| 350 | 354 | * all of the related pixel formats. If mbus_code is not set enumerate |
| 351 | 355 | * all possible pixelformats. |
| 352 | 356 | * |
| 353 | − * TODO: Once raw capture formats are added to the driver this needs | |
| 354 | − * to be extended so raw media bus codes only result in raw pixel | |
| 355 | − * formats. | |
| 357 | + * TODO: Once raw MEDIA_BUS_FMT_SRGGB12_1X12 format is added to the | |
| 358 | + * driver this needs to be extended so raw media bus code only result in | |
| 359 | + * raw pixel format. | |
| 356 | 360 | */ |
| 357 | 361 | switch (f->mbus_code) { |
| 358 | 362 | case 0: |
| @@ -362,6 +366,11 @@ static int rvin_enum_fmt_vid_cap(struct file *file, void *priv, · 3 unmodified lines | ||
| 362 | 366 | case MEDIA_BUS_FMT_UYVY10_2X10: |
| 363 | 367 | case MEDIA_BUS_FMT_RGB888_1X24: |
| 364 | 368 | break; |
| 369 | + case MEDIA_BUS_FMT_SRGGB8_1X8: | |
| 370 | + if (f->index) | |
| 371 | + return -EINVAL; | |
| 372 | + f->pixelformat = V4L2_PIX_FMT_SRGGB8; | |
| 373 | + return 0; | |
| 365 | 374 | default: |
| 366 | 375 | return -EINVAL; |
| 367 | 376 | } |
| @@ -1066,7 +1066,7 @@ static int jpu_buf_prepare(struct vb2_buffer *vb) · 1065 unmodified lines | ||
| 1066 | 1066 | } |
| 1067 | 1067 | |
| 1068 | 1068 | /* decoder capture queue */ |
| 1069 | − if (!ctx->encoder && !V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) | |
| 1069 | + if (!ctx->encoder && V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) | |
| 1070 | 1070 | vb2_set_plane_payload(vb, i, size); |
| 1071 | 1071 | } |
| 1072 | 1072 | |
| @@ -200,22 +200,25 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) · 199 unmodified lines | ||
| 200 | 200 | dst_info.data.format = ctx->out.fmt->hw_format; |
| 201 | 201 | dst_info.data.swap = ctx->out.fmt->color_swap; |
| 202 | 202 | |
| 203 | − if (ctx->in.fmt->hw_format >= RGA_COLOR_FMT_YUV422SP) { | |
| 204 | − if (ctx->out.fmt->hw_format < RGA_COLOR_FMT_YUV422SP) { | |
| 205 | − switch (ctx->in.colorspace) { | |
| 206 | − case V4L2_COLORSPACE_REC709: | |
| 207 | − src_info.data.csc_mode = | |
| 208 | − RGA_SRC_CSC_MODE_BT709_R0; | |
| 209 | − break; | |
| 210 | − default: | |
| 211 | − src_info.data.csc_mode = | |
| 212 | − RGA_SRC_CSC_MODE_BT601_R0; | |
| 213 | − break; | |
| 214 | − } | |
| 203 | + /* | |
| 204 | + * CSC mode must only be set when the colorspace families differ between | |
| 205 | + * input and output. It must remain unset (zeroed) if both are the same. | |
| 206 | + */ | |
| 207 | + | |
| 208 | + if (RGA_COLOR_FMT_IS_YUV(ctx->in.fmt->hw_format) && | |
| 209 | + RGA_COLOR_FMT_IS_RGB(ctx->out.fmt->hw_format)) { | |
| 210 | + switch (ctx->in.colorspace) { | |
| 211 | + case V4L2_COLORSPACE_REC709: | |
| 212 | + src_info.data.csc_mode = RGA_SRC_CSC_MODE_BT709_R0; | |
| 213 | + break; | |
| 214 | + default: | |
| 215 | + src_info.data.csc_mode = RGA_SRC_CSC_MODE_BT601_R0; | |
| 216 | + break; | |
| 215 | 217 | } |
| 216 | 218 | } |
| 217 | 219 | |
| 218 | − if (ctx->out.fmt->hw_format >= RGA_COLOR_FMT_YUV422SP) { | |
| 220 | + if (RGA_COLOR_FMT_IS_RGB(ctx->in.fmt->hw_format) && | |
| 221 | + RGA_COLOR_FMT_IS_YUV(ctx->out.fmt->hw_format)) { | |
| 219 | 222 | switch (ctx->out.colorspace) { |
| 220 | 223 | case V4L2_COLORSPACE_REC709: |
| 221 | 224 | dst_info.data.csc_mode = RGA_SRC_CSC_MODE_BT709_R0; |
| @@ -95,6 +95,11 @@ · 94 unmodified lines | ||
| 95 | 95 | #define RGA_COLOR_FMT_CP_8BPP 15 |
| 96 | 96 | #define RGA_COLOR_FMT_MASK 15 |
| 97 | 97 | |
| 98 | +#define RGA_COLOR_FMT_IS_YUV(fmt) \ | |
| 99 | + (((fmt) >= RGA_COLOR_FMT_YUV422SP) && ((fmt) < RGA_COLOR_FMT_CP_1BPP)) | |
| 100 | +#define RGA_COLOR_FMT_IS_RGB(fmt) \ | |
| 101 | + ((fmt) < RGA_COLOR_FMT_YUV422SP) | |
| 102 | + | |
| 98 | 103 | #define RGA_COLOR_NONE_SWAP 0 |
| 99 | 104 | #define RGA_COLOR_RB_SWAP 1 |
| 100 | 105 | #define RGA_COLOR_ALPHA_SWAP 2 |
| @@ -304,7 +304,7 @@ static int camif_media_dev_init(struct camif_dev *camif) · 303 unmodified lines | ||
| 304 | 304 | int ret; |
| 305 | 305 | |
| 306 | 306 | memset(md, 0, sizeof(*md)); |
| 307 | − snprintf(md->model, sizeof(md->model), "SAMSUNG S3C%s CAMIF", | |
| 307 | + snprintf(md->model, sizeof(md->model), "Samsung S3C%s CAMIF", | |
| 308 | 308 | ip_rev == S3C6410_CAMIF_IP_REV ? "6410" : "244X"); |
| 309 | 309 | strscpy(md->bus_info, "platform", sizeof(md->bus_info)); |
| 310 | 310 | md->hw_revision = ip_rev; |
| @@ -695,21 +695,13 @@ static int g2d_probe(struct platform_device *pdev) · 694 unmodified lines | ||
| 695 | 695 | vfd->lock = &dev->mutex; |
| 696 | 696 | vfd->v4l2_dev = &dev->v4l2_dev; |
| 697 | 697 | vfd->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; |
| 698 | − ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); | |
| 699 | − if (ret) { | |
| 700 | − v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); | |
| 701 | − goto rel_vdev; | |
| 702 | − } | |
| 703 | − video_set_drvdata(vfd, dev); | |
| 704 | − dev->vfd = vfd; | |
| 705 | − v4l2_info(&dev->v4l2_dev, "device registered as /dev/video%d\n", | |
| 706 | − vfd->num); | |
| 698 | + | |
| 707 | 699 | platform_set_drvdata(pdev, dev); |
| 708 | 700 | dev->m2m_dev = v4l2_m2m_init(&g2d_m2m_ops); |
| 709 | 701 | if (IS_ERR(dev->m2m_dev)) { |
| 710 | 702 | v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n"); |
| 711 | 703 | ret = PTR_ERR(dev->m2m_dev); |
| 712 | − goto unreg_video_dev; | |
| 704 | + goto rel_vdev; | |
| 713 | 705 | } |
| 714 | 706 | |
| 715 | 707 | def_frame.stride = (def_frame.width * def_frame.fmt->depth) >> 3; |
| @@ -717,14 +709,24 @@ static int g2d_probe(struct platform_device *pdev) · 1 unmodified line | ||
| 717 | 709 | of_id = of_match_node(exynos_g2d_match, pdev->dev.of_node); |
| 718 | 710 | if (!of_id) { |
| 719 | 711 | ret = -ENODEV; |
| 720 | − goto unreg_video_dev; | |
| 712 | + goto free_m2m; | |
| 721 | 713 | } |
| 722 | 714 | dev->variant = (struct g2d_variant *)of_id->data; |
| 723 | 715 | |
| 716 | + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); | |
| 717 | + if (ret) { | |
| 718 | + v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); | |
| 719 | + goto free_m2m; | |
| 720 | + } | |
| 721 | + video_set_drvdata(vfd, dev); | |
| 722 | + dev->vfd = vfd; | |
| 723 | + v4l2_info(&dev->v4l2_dev, "device registered as /dev/video%d\n", | |
| 724 | + vfd->num); | |
| 725 | + | |
| 724 | 726 | return 0; |
| 725 | 727 | |
| 726 | −unreg_video_dev: | |
| 727 | − video_unregister_device(dev->vfd); | |
| 728 | +free_m2m: | |
| 729 | + v4l2_m2m_release(dev->m2m_dev); | |
| 728 | 730 | rel_vdev: |
| 729 | 731 | video_device_release(vfd); |
| 730 | 732 | unreg_v4l2_dev: |
| @@ -24,6 +24,7 @@ · 23 unmodified lines | ||
| 24 | 24 | #include <media/v4l2-event.h> |
| 25 | 25 | #include <media/v4l2-mem2mem.h> |
| 26 | 26 | #include <media/v4l2-ioctl.h> |
| 27 | +#include <media/v4l2-rect.h> | |
| 27 | 28 | #include <media/videobuf2-v4l2.h> |
| 28 | 29 | #include <media/videobuf2-dma-contig.h> |
| 29 | 30 | |
| @@ -1735,19 +1736,6 @@ static int exynos3250_jpeg_try_downscale(struct s5p_jpeg_ctx *ctx, · 1705 unmodified lines | ||
| 1735 | 1736 | return 0; |
| 1736 | 1737 | } |
| 1737 | 1738 | |
| 1738 | −/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */ | |
| 1739 | −static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b) | |
| 1740 | −{ | |
| 1741 | − if (a->left < b->left || a->top < b->top) | |
| 1742 | − return 0; | |
| 1743 | − if (a->left + a->width > b->left + b->width) | |
| 1744 | − return 0; | |
| 1745 | − if (a->top + a->height > b->top + b->height) | |
| 1746 | − return 0; | |
| 1747 | − | |
| 1748 | − return 1; | |
| 1749 | −} | |
| 1750 | − | |
| 1751 | 1739 | static int exynos3250_jpeg_try_crop(struct s5p_jpeg_ctx *ctx, |
| 1752 | 1740 | struct v4l2_rect *r) |
| 1753 | 1741 | { |
| @@ -1780,7 +1768,7 @@ static int exynos3250_jpeg_try_crop(struct s5p_jpeg_ctx *ctx, · 26 unmodified lines | ||
| 1780 | 1768 | r->left = round_down(r->left, 2); |
| 1781 | 1769 | r->top = round_down(r->top, 2); |
| 1782 | 1770 | |
| 1783 | − if (!enclosed_rectangle(r, &base_rect)) | |
| 1771 | + if (!v4l2_rect_enclosed(r, &base_rect)) | |
| 1784 | 1772 | return -EINVAL; |
| 1785 | 1773 | |
| 1786 | 1774 | ctx->crop_rect.left = r->left; |
| @@ -1087,7 +1087,7 @@ static void hva_stop_streaming(struct vb2_queue *vq) · 1086 unmodified lines | ||
| 1087 | 1087 | |
| 1088 | 1088 | if ((V4L2_TYPE_IS_OUTPUT(vq->type) && |
| 1089 | 1089 | vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q)) || |
| 1090 | − (!V4L2_TYPE_IS_OUTPUT(vq->type) && | |
| 1090 | + (V4L2_TYPE_IS_CAPTURE(vq->type) && | |
| 1091 | 1091 | vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q))) { |
| 1092 | 1092 | dev_dbg(dev, "%s %s out=%d cap=%d\n", |
| 1093 | 1093 | ctx->name, to_type_str(vq->type), |
| @@ -191,6 +191,8 @@ static int sun4i_csi_probe(struct platform_device *pdev) · 190 unmodified lines | ||
| 191 | 191 | strscpy(csi->mdev.model, "Allwinner Video Capture Device", |
| 192 | 192 | sizeof(csi->mdev.model)); |
| 193 | 193 | csi->mdev.hw_revision = 0; |
| 194 | + snprintf(csi->mdev.bus_info, sizeof(csi->mdev.bus_info), "platform:%s", | |
| 195 | + dev_name(csi->dev)); | |
| 194 | 196 | media_device_init(&csi->mdev); |
| 195 | 197 | csi->v4l.mdev = &csi->mdev; |
| 196 | 198 | |
| @@ -413,7 +413,7 @@ int sun4i_csi_dma_register(struct sun4i_csi *csi, int irq) · 412 unmodified lines | ||
| 413 | 413 | |
| 414 | 414 | q->min_buffers_needed = 3; |
| 415 | 415 | q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; |
| 416 | − q->io_modes = VB2_MMAP; | |
| 416 | + q->io_modes = VB2_MMAP | VB2_DMABUF; | |
| 417 | 417 | q->lock = &csi->lock; |
| 418 | 418 | q->drv_priv = csi; |
| 419 | 419 | q->buf_struct_size = sizeof(struct sun4i_csi_buffer); |
| @@ -242,7 +242,8 @@ static int sun4i_csi_release(struct file *file) · 241 unmodified lines | ||
| 242 | 242 | |
| 243 | 243 | mutex_lock(&csi->lock); |
| 244 | 244 | |
| 245 | − v4l2_fh_release(file); | |
| 245 | + _vb2_fop_release(file, NULL); | |
| 246 | + | |
| 246 | 247 | v4l2_pipeline_pm_put(&csi->vdev.entity); |
| 247 | 248 | pm_runtime_put(csi->dev); |
| 248 | 249 | |
| @@ -256,8 +257,6 @@ static const struct v4l2_file_operations sun4i_csi_fops = { · 7 unmodified lines | ||
| 256 | 257 | .open = sun4i_csi_open, |
| 257 | 258 | .release = sun4i_csi_release, |
| 258 | 259 | .unlocked_ioctl = video_ioctl2, |
| 259 | − .read = vb2_fop_read, | |
| 260 | − .write = vb2_fop_write, | |
| 261 | 260 | .poll = vb2_fop_poll, |
| 262 | 261 | .mmap = vb2_fop_mmap, |
| 263 | 262 | }; |
| @@ -733,6 +733,8 @@ static int sun6i_csi_v4l2_init(struct sun6i_csi *csi) · 732 unmodified lines | ||
| 733 | 733 | strscpy(csi->media_dev.model, "Allwinner Video Capture Device", |
| 734 | 734 | sizeof(csi->media_dev.model)); |
| 735 | 735 | csi->media_dev.hw_revision = 0; |
| 736 | + snprintf(csi->media_dev.bus_info, sizeof(csi->media_dev.bus_info), | |
| 737 | + "platform:%s", dev_name(csi->dev)); | |
| 736 | 738 | |
| 737 | 739 | media_device_init(&csi->media_dev); |
| 738 | 740 | v4l2_async_notifier_init(&csi->notifier); |
| @@ -13,4 +13,4 @@ ccflags-$(CONFIG_VIDEO_TI_VPE_DEBUG) += -DDEBUG · 12 unmodified lines | ||
| 13 | 13 | |
| 14 | 14 | obj-$(CONFIG_VIDEO_TI_CAL) += ti-cal.o |
| 15 | 15 | |
| 16 | −ti-cal-y := cal.o | |
| 16 | +ti-cal-y := cal.o cal-camerarx.o cal-video.o | |
| @@ -0,0 +1,649 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | +/* | |
| 3 | + * TI Camera Access Layer (CAL) - CAMERARX | |
| 4 | + * | |
| 5 | + * Copyright (c) 2015-2020 Texas Instruments Inc. | |
| 6 | + * | |
| 7 | + * Authors: | |
| 8 | + * Benoit Parrot <bparrot@ti.com> | |
| 9 | + * Laurent Pinchart <laurent.pinchart@ideasonboard.com> | |
| 10 | + */ | |
| 11 | + | |
| 12 | +#include <linux/clk.h> | |
| 13 | +#include <linux/delay.h> | |
| 14 | +#include <linux/mfd/syscon.h> | |
| 15 | +#include <linux/module.h> | |
| 16 | +#include <linux/of_graph.h> | |
| 17 | +#include <linux/platform_device.h> | |
| 18 | +#include <linux/regmap.h> | |
| 19 | +#include <linux/slab.h> | |
| 20 | + | |
| 21 | +#include <media/v4l2-ctrls.h> | |
| 22 | +#include <media/v4l2-fwnode.h> | |
| 23 | +#include <media/v4l2-subdev.h> | |
| 24 | + | |
| 25 | +#include "cal.h" | |
| 26 | +#include "cal_regs.h" | |
| 27 | + | |
| 28 | +/* ------------------------------------------------------------------ | |
| 29 | + * I/O Register Accessors | |
| 30 | + * ------------------------------------------------------------------ | |
| 31 | + */ | |
| 32 | + | |
| 33 | +static inline u32 camerarx_read(struct cal_camerarx *phy, u32 offset) | |
| 34 | +{ | |
| 35 | + return ioread32(phy->base + offset); | |
| 36 | +} | |
| 37 | + | |
| 38 | +static inline void camerarx_write(struct cal_camerarx *phy, u32 offset, u32 val) | |
| 39 | +{ | |
| 40 | + iowrite32(val, phy->base + offset); | |
| 41 | +} | |
| 42 | + | |
| 43 | +/* ------------------------------------------------------------------ | |
| 44 | + * CAMERARX Management | |
| 45 | + * ------------------------------------------------------------------ | |
| 46 | + */ | |
| 47 | + | |
| 48 | +static s64 cal_camerarx_get_external_rate(struct cal_camerarx *phy) | |
| 49 | +{ | |
| 50 | + struct v4l2_ctrl *ctrl; | |
| 51 | + s64 rate; | |
| 52 | + | |
| 53 | + ctrl = v4l2_ctrl_find(phy->sensor->ctrl_handler, V4L2_CID_PIXEL_RATE); | |
| 54 | + if (!ctrl) { | |
| 55 | + phy_err(phy, "no pixel rate control in subdev: %s\n", | |
| 56 | + phy->sensor->name); | |
| 57 | + return -EPIPE; | |
| 58 | + } | |
| 59 | + | |
| 60 | + rate = v4l2_ctrl_g_ctrl_int64(ctrl); | |
| 61 | + phy_dbg(3, phy, "sensor Pixel Rate: %llu\n", rate); | |
| 62 | + | |
| 63 | + return rate; | |
| 64 | +} | |
| 65 | + | |
| 66 | +static void cal_camerarx_lane_config(struct cal_camerarx *phy) | |
| 67 | +{ | |
| 68 | + u32 val = cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)); | |
| 69 | + u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK; | |
| 70 | + u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK; | |
| 71 | + struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = | |
| 72 | + &phy->endpoint.bus.mipi_csi2; | |
| 73 | + int lane; | |
| 74 | + | |
| 75 | + cal_set_field(&val, mipi_csi2->clock_lane + 1, lane_mask); | |
| 76 | + cal_set_field(&val, mipi_csi2->lane_polarities[0], polarity_mask); | |
| 77 | + for (lane = 0; lane < mipi_csi2->num_data_lanes; lane++) { | |
| 78 | + /* | |
| 79 | + * Every lane are one nibble apart starting with the | |
| 80 | + * clock followed by the data lanes so shift masks by 4. | |
| 81 | + */ | |
| 82 | + lane_mask <<= 4; | |
| 83 | + polarity_mask <<= 4; | |
| 84 | + cal_set_field(&val, mipi_csi2->data_lanes[lane] + 1, lane_mask); | |
| 85 | + cal_set_field(&val, mipi_csi2->lane_polarities[lane + 1], | |
| 86 | + polarity_mask); | |
| 87 | + } | |
| 88 | + | |
| 89 | + cal_write(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), val); | |
| 90 | + phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n", | |
| 91 | + phy->instance, val); | |
| 92 | +} | |
| 93 | + | |
| 94 | +static void cal_camerarx_enable(struct cal_camerarx *phy) | |
| 95 | +{ | |
| 96 | + u32 num_lanes = phy->cal->data->camerarx[phy->instance].num_lanes; | |
| 97 | + | |
| 98 | + regmap_field_write(phy->fields[F_CAMMODE], 0); | |
| 99 | + /* Always enable all lanes at the phy control level */ | |
| 100 | + regmap_field_write(phy->fields[F_LANEENABLE], (1 << num_lanes) - 1); | |
| 101 | + /* F_CSI_MODE is not present on every architecture */ | |
| 102 | + if (phy->fields[F_CSI_MODE]) | |
| 103 | + regmap_field_write(phy->fields[F_CSI_MODE], 1); | |
| 104 | + regmap_field_write(phy->fields[F_CTRLCLKEN], 1); | |
| 105 | +} | |
| 106 | + | |
| 107 | +void cal_camerarx_disable(struct cal_camerarx *phy) | |
| 108 | +{ | |
| 109 | + regmap_field_write(phy->fields[F_CTRLCLKEN], 0); | |
| 110 | +} | |
| 111 | + | |
| 112 | +/* | |
| 113 | + * TCLK values are OK at their reset values | |
| 114 | + */ | |
| 115 | +#define TCLK_TERM 0 | |
| 116 | +#define TCLK_MISS 1 | |
| 117 | +#define TCLK_SETTLE 14 | |
| 118 | + | |
| 119 | +static void cal_camerarx_config(struct cal_camerarx *phy, s64 external_rate, | |
| 120 | + const struct cal_fmt *fmt) | |
| 121 | +{ | |
| 122 | + unsigned int reg0, reg1; | |
| 123 | + unsigned int ths_term, ths_settle; | |
| 124 | + unsigned int csi2_ddrclk_khz; | |
| 125 | + struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = | |
| 126 | + &phy->endpoint.bus.mipi_csi2; | |
| 127 | + u32 num_lanes = mipi_csi2->num_data_lanes; | |
| 128 | + | |
| 129 | + /* DPHY timing configuration */ | |
| 130 | + | |
| 131 | + /* | |
| 132 | + * CSI-2 is DDR and we only count used lanes. | |
| 133 | + * | |
| 134 | + * csi2_ddrclk_khz = external_rate / 1000 | |
| 135 | + * / (2 * num_lanes) * fmt->bpp; | |
| 136 | + */ | |
| 137 | + csi2_ddrclk_khz = div_s64(external_rate * fmt->bpp, | |
| 138 | + 2 * num_lanes * 1000); | |
| 139 | + | |
| 140 | + phy_dbg(1, phy, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz); | |
| 141 | + | |
| 142 | + /* THS_TERM: Programmed value = floor(20 ns/DDRClk period) */ | |
| 143 | + ths_term = 20 * csi2_ddrclk_khz / 1000000; | |
| 144 | + phy_dbg(1, phy, "ths_term: %d (0x%02x)\n", ths_term, ths_term); | |
| 145 | + | |
| 146 | + /* THS_SETTLE: Programmed value = floor(105 ns/DDRClk period) + 4 */ | |
| 147 | + ths_settle = (105 * csi2_ddrclk_khz / 1000000) + 4; | |
| 148 | + phy_dbg(1, phy, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle); | |
| 149 | + | |
| 150 | + reg0 = camerarx_read(phy, CAL_CSI2_PHY_REG0); | |
| 151 | + cal_set_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE, | |
| 152 | + CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK); | |
| 153 | + cal_set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK); | |
| 154 | + cal_set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK); | |
| 155 | + | |
| 156 | + phy_dbg(1, phy, "CSI2_%d_REG0 = 0x%08x\n", phy->instance, reg0); | |
| 157 | + camerarx_write(phy, CAL_CSI2_PHY_REG0, reg0); | |
| 158 | + | |
| 159 | + reg1 = camerarx_read(phy, CAL_CSI2_PHY_REG1); | |
| 160 | + cal_set_field(®1, TCLK_TERM, CAL_CSI2_PHY_REG1_TCLK_TERM_MASK); | |
| 161 | + cal_set_field(®1, 0xb8, CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK); | |
| 162 | + cal_set_field(®1, TCLK_MISS, | |
| 163 | + CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK); | |
| 164 | + cal_set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK); | |
| 165 | + | |
| 166 | + phy_dbg(1, phy, "CSI2_%d_REG1 = 0x%08x\n", phy->instance, reg1); | |
| 167 | + camerarx_write(phy, CAL_CSI2_PHY_REG1, reg1); | |
| 168 | +} | |
| 169 | + | |
| 170 | +static void cal_camerarx_power(struct cal_camerarx *phy, bool enable) | |
| 171 | +{ | |
| 172 | + u32 target_state; | |
| 173 | + unsigned int i; | |
| 174 | + | |
| 175 | + target_state = enable ? CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON : | |
| 176 | + CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_OFF; | |
| 177 | + | |
| 178 | + cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), | |
| 179 | + target_state, CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK); | |
| 180 | + | |
| 181 | + for (i = 0; i < 10; i++) { | |
| 182 | + u32 current_state; | |
| 183 | + | |
| 184 | + current_state = cal_read_field(phy->cal, | |
| 185 | + CAL_CSI2_COMPLEXIO_CFG(phy->instance), | |
| 186 | + CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK); | |
| 187 | + | |
| 188 | + if (current_state == target_state) | |
| 189 | + break; | |
| 190 | + | |
| 191 | + usleep_range(1000, 1100); | |
| 192 | + } | |
| 193 | + | |
| 194 | + if (i == 10) | |
| 195 | + phy_err(phy, "Failed to power %s complexio\n", | |
| 196 | + enable ? "up" : "down"); | |
| 197 | +} | |
| 198 | + | |
| 199 | +static void cal_camerarx_wait_reset(struct cal_camerarx *phy) | |
| 200 | +{ | |
| 201 | + unsigned long timeout; | |
| 202 | + | |
| 203 | + timeout = jiffies + msecs_to_jiffies(750); | |
| 204 | + while (time_before(jiffies, timeout)) { | |
| 205 | + if (cal_read_field(phy->cal, | |
| 206 | + CAL_CSI2_COMPLEXIO_CFG(phy->instance), | |
| 207 | + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == | |
| 208 | + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) | |
| 209 | + break; | |
| 210 | + usleep_range(500, 5000); | |
| 211 | + } | |
| 212 | + | |
| 213 | + if (cal_read_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), | |
| 214 | + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) != | |
| 215 | + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) | |
| 216 | + phy_err(phy, "Timeout waiting for Complex IO reset done\n"); | |
| 217 | +} | |
| 218 | + | |
| 219 | +static void cal_camerarx_wait_stop_state(struct cal_camerarx *phy) | |
| 220 | +{ | |
| 221 | + unsigned long timeout; | |
| 222 | + | |
| 223 | + timeout = jiffies + msecs_to_jiffies(750); | |
| 224 | + while (time_before(jiffies, timeout)) { | |
| 225 | + if (cal_read_field(phy->cal, | |
| 226 | + CAL_CSI2_TIMING(phy->instance), | |
| 227 | + CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) == 0) | |
| 228 | + break; | |
| 229 | + usleep_range(500, 5000); | |
| 230 | + } | |
| 231 | + | |
| 232 | + if (cal_read_field(phy->cal, CAL_CSI2_TIMING(phy->instance), | |
| 233 | + CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) != 0) | |
| 234 | + phy_err(phy, "Timeout waiting for stop state\n"); | |
| 235 | +} | |
| 236 | + | |
| 237 | +int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt) | |
| 238 | +{ | |
| 239 | + s64 external_rate; | |
| 240 | + u32 sscounter; | |
| 241 | + u32 val; | |
| 242 | + int ret; | |
| 243 | + | |
| 244 | + external_rate = cal_camerarx_get_external_rate(phy); | |
| 245 | + if (external_rate < 0) | |
| 246 | + return external_rate; | |
| 247 | + | |
| 248 | + ret = v4l2_subdev_call(phy->sensor, core, s_power, 1); | |
| 249 | + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) { | |
| 250 | + phy_err(phy, "power on failed in subdev\n"); | |
| 251 | + return ret; | |
| 252 | + } | |
| 253 | + | |
| 254 | + /* | |
| 255 | + * CSI-2 PHY Link Initialization Sequence, according to the DRA74xP / | |
| 256 | + * DRA75xP / DRA76xP / DRA77xP TRM. The DRA71x / DRA72x and the AM65x / | |
| 257 | + * DRA80xM TRMs have a a slightly simplified sequence. | |
| 258 | + */ | |
| 259 | + | |
| 260 | + /* | |
| 261 | + * 1. Configure all CSI-2 low level protocol registers to be ready to | |
| 262 | + * receive signals/data from the CSI-2 PHY. | |
| 263 | + * | |
| 264 | + * i.-v. Configure the lanes position and polarity. | |
| 265 | + */ | |
| 266 | + cal_camerarx_lane_config(phy); | |
| 267 | + | |
| 268 | + /* | |
| 269 | + * vi.-vii. Configure D-PHY mode, enable the required lanes and | |
| 270 | + * enable the CAMERARX clock. | |
| 271 | + */ | |
| 272 | + cal_camerarx_enable(phy); | |
| 273 | + | |
| 274 | + /* | |
| 275 | + * 2. CSI PHY and link initialization sequence. | |
| 276 | + * | |
| 277 | + * a. Deassert the CSI-2 PHY reset. Do not wait for reset completion | |
| 278 | + * at this point, as it requires the external sensor to send the | |
| 279 | + * CSI-2 HS clock. | |
| 280 | + */ | |
| 281 | + cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), | |
| 282 | + CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL, | |
| 283 | + CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); | |
| 284 | + phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x De-assert Complex IO Reset\n", | |
| 285 | + phy->instance, | |
| 286 | + cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance))); | |
| 287 | + | |
| 288 | + /* Dummy read to allow SCP reset to complete. */ | |
| 289 | + camerarx_read(phy, CAL_CSI2_PHY_REG0); | |
| 290 | + | |
| 291 | + /* Program the PHY timing parameters. */ | |
| 292 | + cal_camerarx_config(phy, external_rate, fmt); | |
| 293 | + | |
| 294 | + /* | |
| 295 | + * b. Assert the FORCERXMODE signal. | |
| 296 | + * | |
| 297 | + * The stop-state-counter is based on fclk cycles, and we always use | |
| 298 | + * the x16 and x4 settings, so stop-state-timeout = | |
| 299 | + * fclk-cycle * 16 * 4 * counter. | |
| 300 | + * | |
| 301 | + * Stop-state-timeout must be more than 100us as per CSI-2 spec, so we | |
| 302 | + * calculate a timeout that's 100us (rounding up). | |
| 303 | + */ | |
| 304 | + sscounter = DIV_ROUND_UP(clk_get_rate(phy->cal->fclk), 10000 * 16 * 4); | |
| 305 | + | |
| 306 | + val = cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance)); | |
| 307 | + cal_set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK); | |
| 308 | + cal_set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK); | |
| 309 | + cal_set_field(&val, sscounter, | |
| 310 | + CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK); | |
| 311 | + cal_write(phy->cal, CAL_CSI2_TIMING(phy->instance), val); | |
| 312 | + phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Stop States\n", | |
| 313 | + phy->instance, | |
| 314 | + cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance))); | |
| 315 | + | |
| 316 | + /* Assert the FORCERXMODE signal. */ | |
| 317 | + cal_write_field(phy->cal, CAL_CSI2_TIMING(phy->instance), | |
| 318 | + 1, CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK); | |
| 319 | + phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Force RXMODE\n", | |
| 320 | + phy->instance, | |
| 321 | + cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance))); | |
| 322 | + | |
| 323 | + /* | |
| 324 | + * c. Connect pull-down on CSI-2 PHY link (using pad control). | |
| 325 | + * | |
| 326 | + * This is not required on DRA71x, DRA72x, AM65x and DRA80xM. Not | |
| 327 | + * implemented. | |
| 328 | + */ | |
| 329 | + | |
| 330 | + /* | |
| 331 | + * d. Power up the CSI-2 PHY. | |
| 332 | + * e. Check whether the state status reaches the ON state. | |
| 333 | + */ | |
| 334 | + cal_camerarx_power(phy, true); | |
| 335 | + | |
| 336 | + /* | |
| 337 | + * Start the sensor to enable the CSI-2 HS clock. We can now wait for | |
| 338 | + * CSI-2 PHY reset to complete. | |
| 339 | + */ | |
| 340 | + ret = v4l2_subdev_call(phy->sensor, video, s_stream, 1); | |
| 341 | + if (ret) { | |
| 342 | + v4l2_subdev_call(phy->sensor, core, s_power, 0); | |
| 343 | + phy_err(phy, "stream on failed in subdev\n"); | |
| 344 | + return ret; | |
| 345 | + } | |
| 346 | + | |
| 347 | + cal_camerarx_wait_reset(phy); | |
| 348 | + | |
| 349 | + /* f. Wait for STOPSTATE=1 for all enabled lane modules. */ | |
| 350 | + cal_camerarx_wait_stop_state(phy); | |
| 351 | + | |
| 352 | + phy_dbg(1, phy, "CSI2_%u_REG1 = 0x%08x (bits 31-28 should be set)\n", | |
| 353 | + phy->instance, camerarx_read(phy, CAL_CSI2_PHY_REG1)); | |
| 354 | + | |
| 355 | + /* | |
| 356 | + * g. Disable pull-down on CSI-2 PHY link (using pad control). | |
| 357 | + * | |
| 358 | + * This is not required on DRA71x, DRA72x, AM65x and DRA80xM. Not | |
| 359 | + * implemented. | |
| 360 | + */ | |
| 361 | + | |
| 362 | + return 0; | |
| 363 | +} | |
| 364 | + | |
| 365 | +void cal_camerarx_stop(struct cal_camerarx *phy) | |
| 366 | +{ | |
| 367 | + unsigned int i; | |
| 368 | + int ret; | |
| 369 | + | |
| 370 | + cal_camerarx_power(phy, false); | |
| 371 | + | |
| 372 | + /* Assert Complex IO Reset */ | |
| 373 | + cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), | |
| 374 | + CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL, | |
| 375 | + CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); | |
| 376 | + | |
| 377 | + /* Wait for power down completion */ | |
| 378 | + for (i = 0; i < 10; i++) { | |
| 379 | + if (cal_read_field(phy->cal, | |
| 380 | + CAL_CSI2_COMPLEXIO_CFG(phy->instance), | |
| 381 | + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == | |
| 382 | + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETONGOING) | |
| 383 | + break; | |
| 384 | + usleep_range(1000, 1100); | |
| 385 | + } | |
| 386 | + phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Complex IO in Reset (%d) %s\n", | |
| 387 | + phy->instance, | |
| 388 | + cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)), i, | |
| 389 | + (i >= 10) ? "(timeout)" : ""); | |
| 390 | + | |
| 391 | + /* Disable the phy */ | |
| 392 | + cal_camerarx_disable(phy); | |
| 393 | + | |
| 394 | + if (v4l2_subdev_call(phy->sensor, video, s_stream, 0)) | |
| 395 | + phy_err(phy, "stream off failed in subdev\n"); | |
| 396 | + | |
| 397 | + ret = v4l2_subdev_call(phy->sensor, core, s_power, 0); | |
| 398 | + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) | |
| 399 | + phy_err(phy, "power off failed in subdev\n"); | |
| 400 | +} | |
| 401 | + | |
| 402 | +/* | |
| 403 | + * Errata i913: CSI2 LDO Needs to be disabled when module is powered on | |
| 404 | + * | |
| 405 | + * Enabling CSI2 LDO shorts it to core supply. It is crucial the 2 CSI2 | |
| 406 | + * LDOs on the device are disabled if CSI-2 module is powered on | |
| 407 | + * (0x4845 B304 | 0x4845 B384 [28:27] = 0x1) or in ULPS (0x4845 B304 | |
| 408 | + * | 0x4845 B384 [28:27] = 0x2) mode. Common concerns include: high | |
| 409 | + * current draw on the module supply in active mode. | |
| 410 | + * | |
| 411 | + * Errata does not apply when CSI-2 module is powered off | |
| 412 | + * (0x4845 B304 | 0x4845 B384 [28:27] = 0x0). | |
| 413 | + * | |
| 414 | + * SW Workaround: | |
| 415 | + * Set the following register bits to disable the LDO, | |
| 416 | + * which is essentially CSI2 REG10 bit 6: | |
| 417 | + * | |
| 418 | + * Core 0: 0x4845 B828 = 0x0000 0040 | |
| 419 | + * Core 1: 0x4845 B928 = 0x0000 0040 | |
| 420 | + */ | |
| 421 | +void cal_camerarx_i913_errata(struct cal_camerarx *phy) | |
| 422 | +{ | |
| 423 | + u32 reg10 = camerarx_read(phy, CAL_CSI2_PHY_REG10); | |
| 424 | + | |
| 425 | + cal_set_field(®10, 1, CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK); | |
| 426 | + | |
| 427 | + phy_dbg(1, phy, "CSI2_%d_REG10 = 0x%08x\n", phy->instance, reg10); | |
| 428 | + camerarx_write(phy, CAL_CSI2_PHY_REG10, reg10); | |
| 429 | +} | |
| 430 | + | |
| 431 | +/* | |
| 432 | + * Enable the expected IRQ sources | |
| 433 | + */ | |
| 434 | +void cal_camerarx_enable_irqs(struct cal_camerarx *phy) | |
| 435 | +{ | |
| 436 | + u32 val; | |
| 437 | + | |
| 438 | + const u32 cio_err_mask = | |
| 439 | + CAL_CSI2_COMPLEXIO_IRQ_LANE_ERRORS_MASK | | |
| 440 | + CAL_CSI2_COMPLEXIO_IRQ_FIFO_OVR_MASK | | |
| 441 | + CAL_CSI2_COMPLEXIO_IRQ_SHORT_PACKET_MASK | | |
| 442 | + CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK; | |
| 443 | + | |
| 444 | + /* Enable CIO error irqs */ | |
| 445 | + cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0), | |
| 446 | + CAL_HL_IRQ_CIO_MASK(phy->instance)); | |
| 447 | + cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), | |
| 448 | + cio_err_mask); | |
| 449 | + | |
| 450 | + /* Always enable OCPO error */ | |
| 451 | + cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK); | |
| 452 | + | |
| 453 | + /* Enable IRQ_WDMA_END 0/1 */ | |
| 454 | + val = 0; | |
| 455 | + cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); | |
| 456 | + cal_write(phy->cal, CAL_HL_IRQENABLE_SET(1), val); | |
| 457 | + /* Enable IRQ_WDMA_START 0/1 */ | |
| 458 | + val = 0; | |
| 459 | + cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); | |
| 460 | + cal_write(phy->cal, CAL_HL_IRQENABLE_SET(2), val); | |
| 461 | + /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ | |
| 462 | + cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0xFF000000); | |
| 463 | +} | |
| 464 | + | |
| 465 | +void cal_camerarx_disable_irqs(struct cal_camerarx *phy) | |
| 466 | +{ | |
| 467 | + u32 val; | |
| 468 | + | |
| 469 | + /* Disable CIO error irqs */ | |
| 470 | + cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(0), | |
| 471 | + CAL_HL_IRQ_CIO_MASK(phy->instance)); | |
| 472 | + cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), 0); | |
| 473 | + | |
| 474 | + /* Disable IRQ_WDMA_END 0/1 */ | |
| 475 | + val = 0; | |
| 476 | + cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); | |
| 477 | + cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(1), val); | |
| 478 | + /* Disable IRQ_WDMA_START 0/1 */ | |
| 479 | + val = 0; | |
| 480 | + cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); | |
| 481 | + cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(2), val); | |
| 482 | + /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ | |
| 483 | + cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0); | |
| 484 | +} | |
| 485 | + | |
| 486 | +void cal_camerarx_ppi_enable(struct cal_camerarx *phy) | |
| 487 | +{ | |
| 488 | + cal_write(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), BIT(3)); | |
| 489 | + cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), | |
| 490 | + 1, CAL_CSI2_PPI_CTRL_IF_EN_MASK); | |
| 491 | +} | |
| 492 | + | |
| 493 | +void cal_camerarx_ppi_disable(struct cal_camerarx *phy) | |
| 494 | +{ | |
| 495 | + cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), | |
| 496 | + 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK); | |
| 497 | +} | |
| 498 | + | |
| 499 | +static int cal_camerarx_regmap_init(struct cal_dev *cal, | |
| 500 | + struct cal_camerarx *phy) | |
| 501 | +{ | |
| 502 | + const struct cal_camerarx_data *phy_data; | |
| 503 | + unsigned int i; | |
| 504 | + | |
| 505 | + if (!cal->data) | |
| 506 | + return -EINVAL; | |
| 507 | + | |
| 508 | + phy_data = &cal->data->camerarx[phy->instance]; | |
| 509 | + | |
| 510 | + for (i = 0; i < F_MAX_FIELDS; i++) { | |
| 511 | + struct reg_field field = { | |
| 512 | + .reg = cal->syscon_camerrx_offset, | |
| 513 | + .lsb = phy_data->fields[i].lsb, | |
| 514 | + .msb = phy_data->fields[i].msb, | |
| 515 | + }; | |
| 516 | + | |
| 517 | + /* | |
| 518 | + * Here we update the reg offset with the | |
| 519 | + * value found in DT | |
| 520 | + */ | |
| 521 | + phy->fields[i] = devm_regmap_field_alloc(cal->dev, | |
| 522 | + cal->syscon_camerrx, | |
| 523 | + field); | |
| 524 | + if (IS_ERR(phy->fields[i])) { | |
| 525 | + cal_err(cal, "Unable to allocate regmap fields\n"); | |
| 526 | + return PTR_ERR(phy->fields[i]); | |
| 527 | + } | |
| 528 | + } | |
| 529 | + | |
| 530 | + return 0; | |
| 531 | +} | |
| 532 | + | |
| 533 | +static int cal_camerarx_parse_dt(struct cal_camerarx *phy) | |
| 534 | +{ | |
| 535 | + struct v4l2_fwnode_endpoint *endpoint = &phy->endpoint; | |
| 536 | + struct device_node *ep_node; | |
| 537 | + char data_lanes[V4L2_FWNODE_CSI2_MAX_DATA_LANES * 2]; | |
| 538 | + unsigned int i; | |
| 539 | + int ret; | |
| 540 | + | |
| 541 | + /* | |
| 542 | + * Find the endpoint node for the port corresponding to the PHY | |
| 543 | + * instance, and parse its CSI-2-related properties. | |
| 544 | + */ | |
| 545 | + ep_node = of_graph_get_endpoint_by_regs(phy->cal->dev->of_node, | |
| 546 | + phy->instance, 0); | |
| 547 | + if (!ep_node) { | |
| 548 | + /* | |
| 549 | + * The endpoint is not mandatory, not all PHY instances need to | |
| 550 | + * be connected in DT. | |
| 551 | + */ | |
| 552 | + phy_dbg(3, phy, "Port has no endpoint\n"); | |
| 553 | + return 0; | |
| 554 | + } | |
| 555 | + | |
| 556 | + endpoint->bus_type = V4L2_MBUS_CSI2_DPHY; | |
| 557 | + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), endpoint); | |
| 558 | + if (ret < 0) { | |
| 559 | + phy_err(phy, "Failed to parse endpoint\n"); | |
| 560 | + goto done; | |
| 561 | + } | |
| 562 | + | |
| 563 | + for (i = 0; i < endpoint->bus.mipi_csi2.num_data_lanes; i++) { | |
| 564 | + unsigned int lane = endpoint->bus.mipi_csi2.data_lanes[i]; | |
| 565 | + | |
| 566 | + if (lane > 4) { | |
| 567 | + phy_err(phy, "Invalid position %u for data lane %u\n", | |
| 568 | + lane, i); | |
| 569 | + ret = -EINVAL; | |
| 570 | + goto done; | |
| 571 | + } | |
| 572 | + | |
| 573 | + data_lanes[i*2] = '0' + lane; | |
| 574 | + data_lanes[i*2+1] = ' '; | |
| 575 | + } | |
| 576 | + | |
| 577 | + data_lanes[i*2-1] = '\0'; | |
| 578 | + | |
| 579 | + phy_dbg(3, phy, | |
| 580 | + "CSI-2 bus: clock lane <%u>, data lanes <%s>, flags 0x%08x\n", | |
| 581 | + endpoint->bus.mipi_csi2.clock_lane, data_lanes, | |
| 582 | + endpoint->bus.mipi_csi2.flags); | |
| 583 | + | |
| 584 | + /* Retrieve the connected device and store it for later use. */ | |
| 585 | + phy->sensor_node = of_graph_get_remote_port_parent(ep_node); | |
| 586 | + if (!phy->sensor_node) { | |
| 587 | + phy_dbg(3, phy, "Can't get remote parent\n"); | |
| 588 | + ret = -EINVAL; | |
| 589 | + goto done; | |
| 590 | + } | |
| 591 | + | |
| 592 | + phy_dbg(1, phy, "Found connected device %pOFn\n", phy->sensor_node); | |
| 593 | + | |
| 594 | +done: | |
| 595 | + of_node_put(ep_node); | |
| 596 | + return ret; | |
| 597 | +} | |
| 598 | + | |
| 599 | +struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, | |
| 600 | + unsigned int instance) | |
| 601 | +{ | |
| 602 | + struct platform_device *pdev = to_platform_device(cal->dev); | |
| 603 | + struct cal_camerarx *phy; | |
| 604 | + int ret; | |
| 605 | + | |
| 606 | + phy = kzalloc(sizeof(*phy), GFP_KERNEL); | |
| 607 | + if (!phy) | |
| 608 | + return ERR_PTR(-ENOMEM); | |
| 609 | + | |
| 610 | + phy->cal = cal; | |
| 611 | + phy->instance = instance; | |
| 612 | + | |
| 613 | + phy->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, | |
| 614 | + (instance == 0) ? | |
| 615 | + "cal_rx_core0" : | |
| 616 | + "cal_rx_core1"); | |
| 617 | + phy->base = devm_ioremap_resource(cal->dev, phy->res); | |
| 618 | + if (IS_ERR(phy->base)) { | |
| 619 | + cal_err(cal, "failed to ioremap\n"); | |
| 620 | + ret = PTR_ERR(phy->base); | |
| 621 | + goto error; | |
| 622 | + } | |
| 623 | + | |
| 624 | + cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", | |
| 625 | + phy->res->name, &phy->res->start, &phy->res->end); | |
| 626 | + | |
| 627 | + ret = cal_camerarx_regmap_init(cal, phy); | |
| 628 | + if (ret) | |
| 629 | + goto error; | |
| 630 | + | |
| 631 | + ret = cal_camerarx_parse_dt(phy); | |
| 632 | + if (ret) | |
| 633 | + goto error; | |
| 634 | + | |
| 635 | + return phy; | |
| 636 | + | |
| 637 | +error: | |
| 638 | + kfree(phy); | |
| 639 | + return ERR_PTR(ret); | |
| 640 | +} | |
| 641 | + | |
| 642 | +void cal_camerarx_destroy(struct cal_camerarx *phy) | |
| 643 | +{ | |
| 644 | + if (!phy) | |
| 645 | + return; | |
| 646 | + | |
| 647 | + of_node_put(phy->sensor_node); | |
| 648 | + kfree(phy); | |
| 649 | +} | |
| @@ -0,0 +1,886 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | +/* | |
| 3 | + * TI Camera Access Layer (CAL) - Video Device | |
| 4 | + * | |
| 5 | + * Copyright (c) 2015-2020 Texas Instruments Inc. | |
| 6 | + * | |
| 7 | + * Authors: | |
| 8 | + * Benoit Parrot <bparrot@ti.com> | |
| 9 | + * Laurent Pinchart <laurent.pinchart@ideasonboard.com> | |
| 10 | + */ | |
| 11 | + | |
| 12 | +#include <linux/delay.h> | |
| 13 | +#include <linux/ioctl.h> | |
| 14 | +#include <linux/pm_runtime.h> | |
| 15 | +#include <linux/videodev2.h> | |
| 16 | + | |
| 17 | +#include <media/media-device.h> | |
| 18 | +#include <media/v4l2-common.h> | |
| 19 | +#include <media/v4l2-ctrls.h> | |
| 20 | +#include <media/v4l2-device.h> | |
| 21 | +#include <media/v4l2-event.h> | |
| 22 | +#include <media/v4l2-fh.h> | |
| 23 | +#include <media/v4l2-ioctl.h> | |
| 24 | +#include <media/videobuf2-core.h> | |
| 25 | +#include <media/videobuf2-dma-contig.h> | |
| 26 | + | |
| 27 | +#include "cal.h" | |
| 28 | + | |
| 29 | +/* ------------------------------------------------------------------ | |
| 30 | + * Format Handling | |
| 31 | + * ------------------------------------------------------------------ | |
| 32 | + */ | |
| 33 | + | |
| 34 | +static const struct cal_fmt cal_formats[] = { | |
| 35 | + { | |
| 36 | + .fourcc = V4L2_PIX_FMT_YUYV, | |
| 37 | + .code = MEDIA_BUS_FMT_YUYV8_2X8, | |
| 38 | + .bpp = 16, | |
| 39 | + }, { | |
| 40 | + .fourcc = V4L2_PIX_FMT_UYVY, | |
| 41 | + .code = MEDIA_BUS_FMT_UYVY8_2X8, | |
| 42 | + .bpp = 16, | |
| 43 | + }, { | |
| 44 | + .fourcc = V4L2_PIX_FMT_YVYU, | |
| 45 | + .code = MEDIA_BUS_FMT_YVYU8_2X8, | |
| 46 | + .bpp = 16, | |
| 47 | + }, { | |
| 48 | + .fourcc = V4L2_PIX_FMT_VYUY, | |
| 49 | + .code = MEDIA_BUS_FMT_VYUY8_2X8, | |
| 50 | + .bpp = 16, | |
| 51 | + }, { | |
| 52 | + .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ | |
| 53 | + .code = MEDIA_BUS_FMT_RGB565_2X8_LE, | |
| 54 | + .bpp = 16, | |
| 55 | + }, { | |
| 56 | + .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ | |
| 57 | + .code = MEDIA_BUS_FMT_RGB565_2X8_BE, | |
| 58 | + .bpp = 16, | |
| 59 | + }, { | |
| 60 | + .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */ | |
| 61 | + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, | |
| 62 | + .bpp = 16, | |
| 63 | + }, { | |
| 64 | + .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */ | |
| 65 | + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, | |
| 66 | + .bpp = 16, | |
| 67 | + }, { | |
| 68 | + .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ | |
| 69 | + .code = MEDIA_BUS_FMT_RGB888_2X12_LE, | |
| 70 | + .bpp = 24, | |
| 71 | + }, { | |
| 72 | + .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ | |
| 73 | + .code = MEDIA_BUS_FMT_RGB888_2X12_BE, | |
| 74 | + .bpp = 24, | |
| 75 | + }, { | |
| 76 | + .fourcc = V4L2_PIX_FMT_RGB32, /* argb */ | |
| 77 | + .code = MEDIA_BUS_FMT_ARGB8888_1X32, | |
| 78 | + .bpp = 32, | |
| 79 | + }, { | |
| 80 | + .fourcc = V4L2_PIX_FMT_SBGGR8, | |
| 81 | + .code = MEDIA_BUS_FMT_SBGGR8_1X8, | |
| 82 | + .bpp = 8, | |
| 83 | + }, { | |
| 84 | + .fourcc = V4L2_PIX_FMT_SGBRG8, | |
| 85 | + .code = MEDIA_BUS_FMT_SGBRG8_1X8, | |
| 86 | + .bpp = 8, | |
| 87 | + }, { | |
| 88 | + .fourcc = V4L2_PIX_FMT_SGRBG8, | |
| 89 | + .code = MEDIA_BUS_FMT_SGRBG8_1X8, | |
| 90 | + .bpp = 8, | |
| 91 | + }, { | |
| 92 | + .fourcc = V4L2_PIX_FMT_SRGGB8, | |
| 93 | + .code = MEDIA_BUS_FMT_SRGGB8_1X8, | |
| 94 | + .bpp = 8, | |
| 95 | + }, { | |
| 96 | + .fourcc = V4L2_PIX_FMT_SBGGR10, | |
| 97 | + .code = MEDIA_BUS_FMT_SBGGR10_1X10, | |
| 98 | + .bpp = 10, | |
| 99 | + }, { | |
| 100 | + .fourcc = V4L2_PIX_FMT_SGBRG10, | |
| 101 | + .code = MEDIA_BUS_FMT_SGBRG10_1X10, | |
| 102 | + .bpp = 10, | |
| 103 | + }, { | |
| 104 | + .fourcc = V4L2_PIX_FMT_SGRBG10, | |
| 105 | + .code = MEDIA_BUS_FMT_SGRBG10_1X10, | |
| 106 | + .bpp = 10, | |
| 107 | + }, { | |
| 108 | + .fourcc = V4L2_PIX_FMT_SRGGB10, | |
| 109 | + .code = MEDIA_BUS_FMT_SRGGB10_1X10, | |
| 110 | + .bpp = 10, | |
| 111 | + }, { | |
| 112 | + .fourcc = V4L2_PIX_FMT_SBGGR12, | |
| 113 | + .code = MEDIA_BUS_FMT_SBGGR12_1X12, | |
| 114 | + .bpp = 12, | |
| 115 | + }, { | |
| 116 | + .fourcc = V4L2_PIX_FMT_SGBRG12, | |
| 117 | + .code = MEDIA_BUS_FMT_SGBRG12_1X12, | |
| 118 | + .bpp = 12, | |
| 119 | + }, { | |
| 120 | + .fourcc = V4L2_PIX_FMT_SGRBG12, | |
| 121 | + .code = MEDIA_BUS_FMT_SGRBG12_1X12, | |
| 122 | + .bpp = 12, | |
| 123 | + }, { | |
| 124 | + .fourcc = V4L2_PIX_FMT_SRGGB12, | |
| 125 | + .code = MEDIA_BUS_FMT_SRGGB12_1X12, | |
| 126 | + .bpp = 12, | |
| 127 | + }, | |
| 128 | +}; | |
| 129 | + | |
| 130 | +/* Print Four-character-code (FOURCC) */ | |
| 131 | +static char *fourcc_to_str(u32 fmt) | |
| 132 | +{ | |
| 133 | + static char code[5]; | |
| 134 | + | |
| 135 | + code[0] = (unsigned char)(fmt & 0xff); | |
| 136 | + code[1] = (unsigned char)((fmt >> 8) & 0xff); | |
| 137 | + code[2] = (unsigned char)((fmt >> 16) & 0xff); | |
| 138 | + code[3] = (unsigned char)((fmt >> 24) & 0xff); | |
| 139 | + code[4] = '\0'; | |
| 140 | + | |
| 141 | + return code; | |
| 142 | +} | |
| 143 | + | |
| 144 | +/* ------------------------------------------------------------------ | |
| 145 | + * V4L2 Video IOCTLs | |
| 146 | + * ------------------------------------------------------------------ | |
| 147 | + */ | |
| 148 | + | |
| 149 | +static const struct cal_fmt *find_format_by_pix(struct cal_ctx *ctx, | |
| 150 | + u32 pixelformat) | |
| 151 | +{ | |
| 152 | + const struct cal_fmt *fmt; | |
| 153 | + unsigned int k; | |
| 154 | + | |
| 155 | + for (k = 0; k < ctx->num_active_fmt; k++) { | |
| 156 | + fmt = ctx->active_fmt[k]; | |
| 157 | + if (fmt->fourcc == pixelformat) | |
| 158 | + return fmt; | |
| 159 | + } | |
| 160 | + | |
| 161 | + return NULL; | |
| 162 | +} | |
| 163 | + | |
| 164 | +static const struct cal_fmt *find_format_by_code(struct cal_ctx *ctx, | |
| 165 | + u32 code) | |
| 166 | +{ | |
| 167 | + const struct cal_fmt *fmt; | |
| 168 | + unsigned int k; | |
| 169 | + | |
| 170 | + for (k = 0; k < ctx->num_active_fmt; k++) { | |
| 171 | + fmt = ctx->active_fmt[k]; | |
| 172 | + if (fmt->code == code) | |
| 173 | + return fmt; | |
| 174 | + } | |
| 175 | + | |
| 176 | + return NULL; | |
| 177 | +} | |
| 178 | + | |
| 179 | +static int cal_querycap(struct file *file, void *priv, | |
| 180 | + struct v4l2_capability *cap) | |
| 181 | +{ | |
| 182 | + struct cal_ctx *ctx = video_drvdata(file); | |
| 183 | + | |
| 184 | + strscpy(cap->driver, CAL_MODULE_NAME, sizeof(cap->driver)); | |
| 185 | + strscpy(cap->card, CAL_MODULE_NAME, sizeof(cap->card)); | |
| 186 | + | |
| 187 | + snprintf(cap->bus_info, sizeof(cap->bus_info), | |
| 188 | + "platform:%s", dev_name(ctx->cal->dev)); | |
| 189 | + return 0; | |
| 190 | +} | |
| 191 | + | |
| 192 | +static int cal_enum_fmt_vid_cap(struct file *file, void *priv, | |
| 193 | + struct v4l2_fmtdesc *f) | |
| 194 | +{ | |
| 195 | + struct cal_ctx *ctx = video_drvdata(file); | |
| 196 | + const struct cal_fmt *fmt; | |
| 197 | + | |
| 198 | + if (f->index >= ctx->num_active_fmt) | |
| 199 | + return -EINVAL; | |
| 200 | + | |
| 201 | + fmt = ctx->active_fmt[f->index]; | |
| 202 | + | |
| 203 | + f->pixelformat = fmt->fourcc; | |
| 204 | + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | |
| 205 | + return 0; | |
| 206 | +} | |
| 207 | + | |
| 208 | +static int __subdev_get_format(struct cal_ctx *ctx, | |
| 209 | + struct v4l2_mbus_framefmt *fmt) | |
| 210 | +{ | |
| 211 | + struct v4l2_subdev_format sd_fmt; | |
| 212 | + struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; | |
| 213 | + int ret; | |
| 214 | + | |
| 215 | + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; | |
| 216 | + sd_fmt.pad = 0; | |
| 217 | + | |
| 218 | + ret = v4l2_subdev_call(ctx->phy->sensor, pad, get_fmt, NULL, &sd_fmt); | |
| 219 | + if (ret) | |
| 220 | + return ret; | |
| 221 | + | |
| 222 | + *fmt = *mbus_fmt; | |
| 223 | + | |
| 224 | + ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__, | |
| 225 | + fmt->width, fmt->height, fmt->code); | |
| 226 | + | |
| 227 | + return 0; | |
| 228 | +} | |
| 229 | + | |
| 230 | +static int __subdev_set_format(struct cal_ctx *ctx, | |
| 231 | + struct v4l2_mbus_framefmt *fmt) | |
| 232 | +{ | |
| 233 | + struct v4l2_subdev_format sd_fmt; | |
| 234 | + struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; | |
| 235 | + int ret; | |
| 236 | + | |
| 237 | + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; | |
| 238 | + sd_fmt.pad = 0; | |
| 239 | + *mbus_fmt = *fmt; | |
| 240 | + | |
| 241 | + ret = v4l2_subdev_call(ctx->phy->sensor, pad, set_fmt, NULL, &sd_fmt); | |
| 242 | + if (ret) | |
| 243 | + return ret; | |
| 244 | + | |
| 245 | + ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__, | |
| 246 | + fmt->width, fmt->height, fmt->code); | |
| 247 | + | |
| 248 | + return 0; | |
| 249 | +} | |
| 250 | + | |
| 251 | +static int cal_calc_format_size(struct cal_ctx *ctx, | |
| 252 | + const struct cal_fmt *fmt, | |
| 253 | + struct v4l2_format *f) | |
| 254 | +{ | |
| 255 | + u32 bpl, max_width; | |
| 256 | + | |
| 257 | + if (!fmt) { | |
| 258 | + ctx_dbg(3, ctx, "No cal_fmt provided!\n"); | |
| 259 | + return -EINVAL; | |
| 260 | + } | |
| 261 | + | |
| 262 | + /* | |
| 263 | + * Maximum width is bound by the DMA max width in bytes. | |
| 264 | + * We need to recalculate the actual maxi width depending on the | |
| 265 | + * number of bytes per pixels required. | |
| 266 | + */ | |
| 267 | + max_width = MAX_WIDTH_BYTES / (ALIGN(fmt->bpp, 8) >> 3); | |
| 268 | + v4l_bound_align_image(&f->fmt.pix.width, 48, max_width, 2, | |
| 269 | + &f->fmt.pix.height, 32, MAX_HEIGHT_LINES, 0, 0); | |
| 270 | + | |
| 271 | + bpl = (f->fmt.pix.width * ALIGN(fmt->bpp, 8)) >> 3; | |
| 272 | + f->fmt.pix.bytesperline = ALIGN(bpl, 16); | |
| 273 | + | |
| 274 | + f->fmt.pix.sizeimage = f->fmt.pix.height * | |
| 275 | + f->fmt.pix.bytesperline; | |
| 276 | + | |
| 277 | + ctx_dbg(3, ctx, "%s: fourcc: %s size: %dx%d bpl:%d img_size:%d\n", | |
| 278 | + __func__, fourcc_to_str(f->fmt.pix.pixelformat), | |
| 279 | + f->fmt.pix.width, f->fmt.pix.height, | |
| 280 | + f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); | |
| 281 | + | |
| 282 | + return 0; | |
| 283 | +} | |
| 284 | + | |
| 285 | +static int cal_g_fmt_vid_cap(struct file *file, void *priv, | |
| 286 | + struct v4l2_format *f) | |
| 287 | +{ | |
| 288 | + struct cal_ctx *ctx = video_drvdata(file); | |
| 289 | + | |
| 290 | + *f = ctx->v_fmt; | |
| 291 | + | |
| 292 | + return 0; | |
| 293 | +} | |
| 294 | + | |
| 295 | +static int cal_try_fmt_vid_cap(struct file *file, void *priv, | |
| 296 | + struct v4l2_format *f) | |
| 297 | +{ | |
| 298 | + struct cal_ctx *ctx = video_drvdata(file); | |
| 299 | + const struct cal_fmt *fmt; | |
| 300 | + struct v4l2_subdev_frame_size_enum fse; | |
| 301 | + int ret, found; | |
| 302 | + | |
| 303 | + fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat); | |
| 304 | + if (!fmt) { | |
| 305 | + ctx_dbg(3, ctx, "Fourcc format (0x%08x) not found.\n", | |
| 306 | + f->fmt.pix.pixelformat); | |
| 307 | + | |
| 308 | + /* Just get the first one enumerated */ | |
| 309 | + fmt = ctx->active_fmt[0]; | |
| 310 | + f->fmt.pix.pixelformat = fmt->fourcc; | |
| 311 | + } | |
| 312 | + | |
| 313 | + f->fmt.pix.field = ctx->v_fmt.fmt.pix.field; | |
| 314 | + | |
| 315 | + /* check for/find a valid width/height */ | |
| 316 | + ret = 0; | |
| 317 | + found = false; | |
| 318 | + fse.pad = 0; | |
| 319 | + fse.code = fmt->code; | |
| 320 | + fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; | |
| 321 | + for (fse.index = 0; ; fse.index++) { | |
| 322 | + ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_size, | |
| 323 | + NULL, &fse); | |
| 324 | + if (ret) | |
| 325 | + break; | |
| 326 | + | |
| 327 | + if ((f->fmt.pix.width == fse.max_width) && | |
| 328 | + (f->fmt.pix.height == fse.max_height)) { | |
| 329 | + found = true; | |
| 330 | + break; | |
| 331 | + } else if ((f->fmt.pix.width >= fse.min_width) && | |
| 332 | + (f->fmt.pix.width <= fse.max_width) && | |
| 333 | + (f->fmt.pix.height >= fse.min_height) && | |
| 334 | + (f->fmt.pix.height <= fse.max_height)) { | |
| 335 | + found = true; | |
| 336 | + break; | |
| 337 | + } | |
| 338 | + } | |
| 339 | + | |
| 340 | + if (!found) { | |
| 341 | + /* use existing values as default */ | |
| 342 | + f->fmt.pix.width = ctx->v_fmt.fmt.pix.width; | |
| 343 | + f->fmt.pix.height = ctx->v_fmt.fmt.pix.height; | |
| 344 | + } | |
| 345 | + | |
| 346 | + /* | |
| 347 | + * Use current colorspace for now, it will get | |
| 348 | + * updated properly during s_fmt | |
| 349 | + */ | |
| 350 | + f->fmt.pix.colorspace = ctx->v_fmt.fmt.pix.colorspace; | |
| 351 | + return cal_calc_format_size(ctx, fmt, f); | |
| 352 | +} | |
| 353 | + | |
| 354 | +static int cal_s_fmt_vid_cap(struct file *file, void *priv, | |
| 355 | + struct v4l2_format *f) | |
| 356 | +{ | |
| 357 | + struct cal_ctx *ctx = video_drvdata(file); | |
| 358 | + struct vb2_queue *q = &ctx->vb_vidq; | |
| 359 | + const struct cal_fmt *fmt; | |
| 360 | + struct v4l2_mbus_framefmt mbus_fmt; | |
| 361 | + int ret; | |
| 362 | + | |
| 363 | + if (vb2_is_busy(q)) { | |
| 364 | + ctx_dbg(3, ctx, "%s device busy\n", __func__); | |
| 365 | + return -EBUSY; | |
| 366 | + } | |
| 367 | + | |
| 368 | + ret = cal_try_fmt_vid_cap(file, priv, f); | |
| 369 | + if (ret < 0) | |
| 370 | + return ret; | |
| 371 | + | |
| 372 | + fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat); | |
| 373 | + | |
| 374 | + v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code); | |
| 375 | + | |
| 376 | + ret = __subdev_set_format(ctx, &mbus_fmt); | |
| 377 | + if (ret) | |
| 378 | + return ret; | |
| 379 | + | |
| 380 | + /* Just double check nothing has gone wrong */ | |
| 381 | + if (mbus_fmt.code != fmt->code) { | |
| 382 | + ctx_dbg(3, ctx, | |
| 383 | + "%s subdev changed format on us, this should not happen\n", | |
| 384 | + __func__); | |
| 385 | + return -EINVAL; | |
| 386 | + } | |
| 387 | + | |
| 388 | + v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); | |
| 389 | + ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | |
| 390 | + ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; | |
| 391 | + cal_calc_format_size(ctx, fmt, &ctx->v_fmt); | |
| 392 | + ctx->fmt = fmt; | |
| 393 | + ctx->m_fmt = mbus_fmt; | |
| 394 | + *f = ctx->v_fmt; | |
| 395 | + | |
| 396 | + return 0; | |
| 397 | +} | |
| 398 | + | |
| 399 | +static int cal_enum_framesizes(struct file *file, void *fh, | |
| 400 | + struct v4l2_frmsizeenum *fsize) | |
| 401 | +{ | |
| 402 | + struct cal_ctx *ctx = video_drvdata(file); | |
| 403 | + const struct cal_fmt *fmt; | |
| 404 | + struct v4l2_subdev_frame_size_enum fse; | |
| 405 | + int ret; | |
| 406 | + | |
| 407 | + /* check for valid format */ | |
| 408 | + fmt = find_format_by_pix(ctx, fsize->pixel_format); | |
| 409 | + if (!fmt) { | |
| 410 | + ctx_dbg(3, ctx, "Invalid pixel code: %x\n", | |
| 411 | + fsize->pixel_format); | |
| 412 | + return -EINVAL; | |
| 413 | + } | |
| 414 | + | |
| 415 | + fse.index = fsize->index; | |
| 416 | + fse.pad = 0; | |
| 417 | + fse.code = fmt->code; | |
| 418 | + fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; | |
| 419 | + | |
| 420 | + ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_size, NULL, | |
| 421 | + &fse); | |
| 422 | + if (ret) | |
| 423 | + return ret; | |
| 424 | + | |
| 425 | + ctx_dbg(1, ctx, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n", | |
| 426 | + __func__, fse.index, fse.code, fse.min_width, fse.max_width, | |
| 427 | + fse.min_height, fse.max_height); | |
| 428 | + | |
| 429 | + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; | |
| 430 | + fsize->discrete.width = fse.max_width; | |
| 431 | + fsize->discrete.height = fse.max_height; | |
| 432 | + | |
| 433 | + return 0; | |
| 434 | +} | |
| 435 | + | |
| 436 | +static int cal_enum_input(struct file *file, void *priv, | |
| 437 | + struct v4l2_input *inp) | |
| 438 | +{ | |
| 439 | + if (inp->index > 0) | |
| 440 | + return -EINVAL; | |
| 441 | + | |
| 442 | + inp->type = V4L2_INPUT_TYPE_CAMERA; | |
| 443 | + sprintf(inp->name, "Camera %u", inp->index); | |
| 444 | + return 0; | |
| 445 | +} | |
| 446 | + | |
| 447 | +static int cal_g_input(struct file *file, void *priv, unsigned int *i) | |
| 448 | +{ | |
| 449 | + *i = 0; | |
| 450 | + return 0; | |
| 451 | +} | |
| 452 | + | |
| 453 | +static int cal_s_input(struct file *file, void *priv, unsigned int i) | |
| 454 | +{ | |
| 455 | + return i > 0 ? -EINVAL : 0; | |
| 456 | +} | |
| 457 | + | |
| 458 | +/* timeperframe is arbitrary and continuous */ | |
| 459 | +static int cal_enum_frameintervals(struct file *file, void *priv, | |
| 460 | + struct v4l2_frmivalenum *fival) | |
| 461 | +{ | |
| 462 | + struct cal_ctx *ctx = video_drvdata(file); | |
| 463 | + const struct cal_fmt *fmt; | |
| 464 | + struct v4l2_subdev_frame_interval_enum fie = { | |
| 465 | + .index = fival->index, | |
| 466 | + .width = fival->width, | |
| 467 | + .height = fival->height, | |
| 468 | + .which = V4L2_SUBDEV_FORMAT_ACTIVE, | |
| 469 | + }; | |
| 470 | + int ret; | |
| 471 | + | |
| 472 | + fmt = find_format_by_pix(ctx, fival->pixel_format); | |
| 473 | + if (!fmt) | |
| 474 | + return -EINVAL; | |
| 475 | + | |
| 476 | + fie.code = fmt->code; | |
| 477 | + ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_interval, | |
| 478 | + NULL, &fie); | |
| 479 | + if (ret) | |
| 480 | + return ret; | |
| 481 | + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; | |
| 482 | + fival->discrete = fie.interval; | |
| 483 | + | |
| 484 | + return 0; | |
| 485 | +} | |
| 486 | + | |
| 487 | +static const struct v4l2_file_operations cal_fops = { | |
| 488 | + .owner = THIS_MODULE, | |
| 489 | + .open = v4l2_fh_open, | |
| 490 | + .release = vb2_fop_release, | |
| 491 | + .read = vb2_fop_read, | |
| 492 | + .poll = vb2_fop_poll, | |
| 493 | + .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ | |
| 494 | + .mmap = vb2_fop_mmap, | |
| 495 | +}; | |
| 496 | + | |
| 497 | +static const struct v4l2_ioctl_ops cal_ioctl_ops = { | |
| 498 | + .vidioc_querycap = cal_querycap, | |
| 499 | + .vidioc_enum_fmt_vid_cap = cal_enum_fmt_vid_cap, | |
| 500 | + .vidioc_g_fmt_vid_cap = cal_g_fmt_vid_cap, | |
| 501 | + .vidioc_try_fmt_vid_cap = cal_try_fmt_vid_cap, | |
| 502 | + .vidioc_s_fmt_vid_cap = cal_s_fmt_vid_cap, | |
| 503 | + .vidioc_enum_framesizes = cal_enum_framesizes, | |
| 504 | + .vidioc_reqbufs = vb2_ioctl_reqbufs, | |
| 505 | + .vidioc_create_bufs = vb2_ioctl_create_bufs, | |
| 506 | + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, | |
| 507 | + .vidioc_querybuf = vb2_ioctl_querybuf, | |
| 508 | + .vidioc_qbuf = vb2_ioctl_qbuf, | |
| 509 | + .vidioc_dqbuf = vb2_ioctl_dqbuf, | |
| 510 | + .vidioc_expbuf = vb2_ioctl_expbuf, | |
| 511 | + .vidioc_enum_input = cal_enum_input, | |
| 512 | + .vidioc_g_input = cal_g_input, | |
| 513 | + .vidioc_s_input = cal_s_input, | |
| 514 | + .vidioc_enum_frameintervals = cal_enum_frameintervals, | |
| 515 | + .vidioc_streamon = vb2_ioctl_streamon, | |
| 516 | + .vidioc_streamoff = vb2_ioctl_streamoff, | |
| 517 | + .vidioc_log_status = v4l2_ctrl_log_status, | |
| 518 | + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, | |
| 519 | + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, | |
| 520 | +}; | |
| 521 | + | |
| 522 | +/* ------------------------------------------------------------------ | |
| 523 | + * videobuf2 Operations | |
| 524 | + * ------------------------------------------------------------------ | |
| 525 | + */ | |
| 526 | + | |
| 527 | +static int cal_queue_setup(struct vb2_queue *vq, | |
| 528 | + unsigned int *nbuffers, unsigned int *nplanes, | |
| 529 | + unsigned int sizes[], struct device *alloc_devs[]) | |
| 530 | +{ | |
| 531 | + struct cal_ctx *ctx = vb2_get_drv_priv(vq); | |
| 532 | + unsigned int size = ctx->v_fmt.fmt.pix.sizeimage; | |
| 533 | + | |
| 534 | + if (vq->num_buffers + *nbuffers < 3) | |
| 535 | + *nbuffers = 3 - vq->num_buffers; | |
| 536 | + | |
| 537 | + if (*nplanes) { | |
| 538 | + if (sizes[0] < size) | |
| 539 | + return -EINVAL; | |
| 540 | + size = sizes[0]; | |
| 541 | + } | |
| 542 | + | |
| 543 | + *nplanes = 1; | |
| 544 | + sizes[0] = size; | |
| 545 | + | |
| 546 | + ctx_dbg(3, ctx, "nbuffers=%d, size=%d\n", *nbuffers, sizes[0]); | |
| 547 | + | |
| 548 | + return 0; | |
| 549 | +} | |
| 550 | + | |
| 551 | +static int cal_buffer_prepare(struct vb2_buffer *vb) | |
| 552 | +{ | |
| 553 | + struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); | |
| 554 | + struct cal_buffer *buf = container_of(vb, struct cal_buffer, | |
| 555 | + vb.vb2_buf); | |
| 556 | + unsigned long size; | |
| 557 | + | |
| 558 | + if (WARN_ON(!ctx->fmt)) | |
| 559 | + return -EINVAL; | |
| 560 | + | |
| 561 | + size = ctx->v_fmt.fmt.pix.sizeimage; | |
| 562 | + if (vb2_plane_size(vb, 0) < size) { | |
| 563 | + ctx_err(ctx, | |
| 564 | + "data will not fit into plane (%lu < %lu)\n", | |
| 565 | + vb2_plane_size(vb, 0), size); | |
| 566 | + return -EINVAL; | |
| 567 | + } | |
| 568 | + | |
| 569 | + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); | |
| 570 | + return 0; | |
| 571 | +} | |
| 572 | + | |
| 573 | +static void cal_buffer_queue(struct vb2_buffer *vb) | |
| 574 | +{ | |
| 575 | + struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); | |
| 576 | + struct cal_buffer *buf = container_of(vb, struct cal_buffer, | |
| 577 | + vb.vb2_buf); | |
| 578 | + struct cal_dmaqueue *vidq = &ctx->vidq; | |
| 579 | + unsigned long flags; | |
| 580 | + | |
| 581 | + /* recheck locking */ | |
| 582 | + spin_lock_irqsave(&ctx->slock, flags); | |
| 583 | + list_add_tail(&buf->list, &vidq->active); | |
| 584 | + spin_unlock_irqrestore(&ctx->slock, flags); | |
| 585 | +} | |
| 586 | + | |
| 587 | +static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) | |
| 588 | +{ | |
| 589 | + struct cal_ctx *ctx = vb2_get_drv_priv(vq); | |
| 590 | + struct cal_dmaqueue *dma_q = &ctx->vidq; | |
| 591 | + struct cal_buffer *buf, *tmp; | |
| 592 | + unsigned long addr; | |
| 593 | + unsigned long flags; | |
| 594 | + int ret; | |
| 595 | + | |
| 596 | + spin_lock_irqsave(&ctx->slock, flags); | |
| 597 | + if (list_empty(&dma_q->active)) { | |
| 598 | + spin_unlock_irqrestore(&ctx->slock, flags); | |
| 599 | + ctx_dbg(3, ctx, "buffer queue is empty\n"); | |
| 600 | + return -EIO; | |
| 601 | + } | |
| 602 | + | |
| 603 | + buf = list_entry(dma_q->active.next, struct cal_buffer, list); | |
| 604 | + ctx->cur_frm = buf; | |
| 605 | + ctx->next_frm = buf; | |
| 606 | + list_del(&buf->list); | |
| 607 | + spin_unlock_irqrestore(&ctx->slock, flags); | |
| 608 | + | |
| 609 | + addr = vb2_dma_contig_plane_dma_addr(&ctx->cur_frm->vb.vb2_buf, 0); | |
| 610 | + ctx->sequence = 0; | |
| 611 | + | |
| 612 | + pm_runtime_get_sync(ctx->cal->dev); | |
| 613 | + | |
| 614 | + cal_ctx_csi2_config(ctx); | |
| 615 | + cal_ctx_pix_proc_config(ctx); | |
| 616 | + cal_ctx_wr_dma_config(ctx, ctx->v_fmt.fmt.pix.bytesperline, | |
| 617 | + ctx->v_fmt.fmt.pix.height); | |
| 618 | + | |
| 619 | + cal_camerarx_enable_irqs(ctx->phy); | |
| 620 | + | |
| 621 | + ret = cal_camerarx_start(ctx->phy, ctx->fmt); | |
| 622 | + if (ret) | |
| 623 | + goto err; | |
| 624 | + | |
| 625 | + cal_ctx_wr_dma_addr(ctx, addr); | |
| 626 | + cal_camerarx_ppi_enable(ctx->phy); | |
| 627 | + | |
| 628 | + if (cal_debug >= 4) | |
| 629 | + cal_quickdump_regs(ctx->cal); | |
| 630 | + | |
| 631 | + return 0; | |
| 632 | + | |
| 633 | +err: | |
| 634 | + spin_lock_irqsave(&ctx->slock, flags); | |
| 635 | + vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_QUEUED); | |
| 636 | + ctx->cur_frm = NULL; | |
| 637 | + ctx->next_frm = NULL; | |
| 638 | + list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { | |
| 639 | + list_del(&buf->list); | |
| 640 | + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); | |
| 641 | + } | |
| 642 | + spin_unlock_irqrestore(&ctx->slock, flags); | |
| 643 | + return ret; | |
| 644 | +} | |
| 645 | + | |
| 646 | +static void cal_stop_streaming(struct vb2_queue *vq) | |
| 647 | +{ | |
| 648 | + struct cal_ctx *ctx = vb2_get_drv_priv(vq); | |
| 649 | + struct cal_dmaqueue *dma_q = &ctx->vidq; | |
| 650 | + struct cal_buffer *buf, *tmp; | |
| 651 | + unsigned long timeout; | |
| 652 | + unsigned long flags; | |
| 653 | + bool dma_act; | |
| 654 | + | |
| 655 | + cal_camerarx_ppi_disable(ctx->phy); | |
| 656 | + | |
| 657 | + /* wait for stream and dma to finish */ | |
| 658 | + dma_act = true; | |
| 659 | + timeout = jiffies + msecs_to_jiffies(500); | |
| 660 | + while (dma_act && time_before(jiffies, timeout)) { | |
| 661 | + msleep(50); | |
| 662 | + | |
| 663 | + spin_lock_irqsave(&ctx->slock, flags); | |
| 664 | + dma_act = ctx->dma_act; | |
| 665 | + spin_unlock_irqrestore(&ctx->slock, flags); | |
| 666 | + } | |
| 667 | + | |
| 668 | + if (dma_act) | |
| 669 | + ctx_err(ctx, "failed to disable dma cleanly\n"); | |
| 670 | + | |
| 671 | + cal_camerarx_disable_irqs(ctx->phy); | |
| 672 | + cal_camerarx_stop(ctx->phy); | |
| 673 | + | |
| 674 | + /* Release all active buffers */ | |
| 675 | + spin_lock_irqsave(&ctx->slock, flags); | |
| 676 | + list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { | |
| 677 | + list_del(&buf->list); | |
| 678 | + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); | |
| 679 | + } | |
| 680 | + | |
| 681 | + if (ctx->cur_frm == ctx->next_frm) { | |
| 682 | + vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); | |
| 683 | + } else { | |
| 684 | + vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); | |
| 685 | + vb2_buffer_done(&ctx->next_frm->vb.vb2_buf, | |
| 686 | + VB2_BUF_STATE_ERROR); | |
| 687 | + } | |
| 688 | + ctx->cur_frm = NULL; | |
| 689 | + ctx->next_frm = NULL; | |
| 690 | + spin_unlock_irqrestore(&ctx->slock, flags); | |
| 691 | + | |
| 692 | + pm_runtime_put_sync(ctx->cal->dev); | |
| 693 | +} | |
| 694 | + | |
| 695 | +static const struct vb2_ops cal_video_qops = { | |
| 696 | + .queue_setup = cal_queue_setup, | |
| 697 | + .buf_prepare = cal_buffer_prepare, | |
| 698 | + .buf_queue = cal_buffer_queue, | |
| 699 | + .start_streaming = cal_start_streaming, | |
| 700 | + .stop_streaming = cal_stop_streaming, | |
| 701 | + .wait_prepare = vb2_ops_wait_prepare, | |
| 702 | + .wait_finish = vb2_ops_wait_finish, | |
| 703 | +}; | |
| 704 | + | |
| 705 | +/* ------------------------------------------------------------------ | |
| 706 | + * V4L2 Initialization and Registration | |
| 707 | + * ------------------------------------------------------------------ | |
| 708 | + */ | |
| 709 | + | |
| 710 | +static const struct video_device cal_videodev = { | |
| 711 | + .name = CAL_MODULE_NAME, | |
| 712 | + .fops = &cal_fops, | |
| 713 | + .ioctl_ops = &cal_ioctl_ops, | |
| 714 | + .minor = -1, | |
| 715 | + .release = video_device_release_empty, | |
| 716 | + .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | | |
| 717 | + V4L2_CAP_READWRITE, | |
| 718 | +}; | |
| 719 | + | |
| 720 | +static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) | |
| 721 | +{ | |
| 722 | + struct v4l2_subdev_mbus_code_enum mbus_code; | |
| 723 | + struct v4l2_mbus_framefmt mbus_fmt; | |
| 724 | + const struct cal_fmt *fmt; | |
| 725 | + unsigned int i, j, k; | |
| 726 | + int ret = 0; | |
| 727 | + | |
| 728 | + /* Enumerate sub device formats and enable all matching local formats */ | |
| 729 | + ctx->active_fmt = devm_kcalloc(ctx->cal->dev, ARRAY_SIZE(cal_formats), | |
| 730 | + sizeof(*ctx->active_fmt), GFP_KERNEL); | |
| 731 | + ctx->num_active_fmt = 0; | |
| 732 | + | |
| 733 | + for (j = 0, i = 0; ret != -EINVAL; ++j) { | |
| 734 | + | |
| 735 | + memset(&mbus_code, 0, sizeof(mbus_code)); | |
| 736 | + mbus_code.index = j; | |
| 737 | + mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; | |
| 738 | + ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_mbus_code, | |
| 739 | + NULL, &mbus_code); | |
| 740 | + if (ret) | |
| 741 | + continue; | |
| 742 | + | |
| 743 | + ctx_dbg(2, ctx, | |
| 744 | + "subdev %s: code: %04x idx: %u\n", | |
| 745 | + ctx->phy->sensor->name, mbus_code.code, j); | |
| 746 | + | |
| 747 | + for (k = 0; k < ARRAY_SIZE(cal_formats); k++) { | |
| 748 | + const struct cal_fmt *fmt = &cal_formats[k]; | |
| 749 | + | |
| 750 | + if (mbus_code.code == fmt->code) { | |
| 751 | + ctx->active_fmt[i] = fmt; | |
| 752 | + ctx_dbg(2, ctx, | |
| 753 | + "matched fourcc: %s: code: %04x idx: %u\n", | |
| 754 | + fourcc_to_str(fmt->fourcc), | |
| 755 | + fmt->code, i); | |
| 756 | + ctx->num_active_fmt = ++i; | |
| 757 | + } | |
| 758 | + } | |
| 759 | + } | |
| 760 | + | |
| 761 | + if (i == 0) { | |
| 762 | + ctx_err(ctx, "No suitable format reported by subdev %s\n", | |
| 763 | + ctx->phy->sensor->name); | |
| 764 | + return -EINVAL; | |
| 765 | + } | |
| 766 | + | |
| 767 | + ret = __subdev_get_format(ctx, &mbus_fmt); | |
| 768 | + if (ret) | |
| 769 | + return ret; | |
| 770 | + | |
| 771 | + fmt = find_format_by_code(ctx, mbus_fmt.code); | |
| 772 | + if (!fmt) { | |
| 773 | + ctx_dbg(3, ctx, "mbus code format (0x%08x) not found.\n", | |
| 774 | + mbus_fmt.code); | |
| 775 | + return -EINVAL; | |
| 776 | + } | |
| 777 | + | |
| 778 | + /* Save current subdev format */ | |
| 779 | + v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); | |
| 780 | + ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | |
| 781 | + ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; | |
| 782 | + cal_calc_format_size(ctx, fmt, &ctx->v_fmt); | |
| 783 | + ctx->fmt = fmt; | |
| 784 | + ctx->m_fmt = mbus_fmt; | |
| 785 | + | |
| 786 | + return 0; | |
| 787 | +} | |
| 788 | + | |
| 789 | +int cal_ctx_v4l2_register(struct cal_ctx *ctx) | |
| 790 | +{ | |
| 791 | + struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; | |
| 792 | + struct video_device *vfd = &ctx->vdev; | |
| 793 | + int ret; | |
| 794 | + | |
| 795 | + ret = cal_ctx_v4l2_init_formats(ctx); | |
| 796 | + if (ret) | |
| 797 | + return ret; | |
| 798 | + | |
| 799 | + ret = v4l2_ctrl_add_handler(hdl, ctx->phy->sensor->ctrl_handler, NULL, | |
| 800 | + true); | |
| 801 | + if (ret < 0) { | |
| 802 | + ctx_err(ctx, "Failed to add sensor ctrl handler\n"); | |
| 803 | + return ret; | |
| 804 | + } | |
| 805 | + | |
| 806 | + ret = video_register_device(vfd, VFL_TYPE_VIDEO, cal_video_nr); | |
| 807 | + if (ret < 0) { | |
| 808 | + ctx_err(ctx, "Failed to register video device\n"); | |
| 809 | + return ret; | |
| 810 | + } | |
| 811 | + | |
| 812 | + ctx_info(ctx, "V4L2 device registered as %s\n", | |
| 813 | + video_device_node_name(vfd)); | |
| 814 | + | |
| 815 | + return 0; | |
| 816 | +} | |
| 817 | + | |
| 818 | +void cal_ctx_v4l2_unregister(struct cal_ctx *ctx) | |
| 819 | +{ | |
| 820 | + ctx_dbg(1, ctx, "unregistering %s\n", | |
| 821 | + video_device_node_name(&ctx->vdev)); | |
| 822 | + | |
| 823 | + video_unregister_device(&ctx->vdev); | |
| 824 | +} | |
| 825 | + | |
| 826 | +int cal_ctx_v4l2_init(struct cal_ctx *ctx) | |
| 827 | +{ | |
| 828 | + struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; | |
| 829 | + struct video_device *vfd = &ctx->vdev; | |
| 830 | + struct vb2_queue *q = &ctx->vb_vidq; | |
| 831 | + int ret; | |
| 832 | + | |
| 833 | + INIT_LIST_HEAD(&ctx->vidq.active); | |
| 834 | + spin_lock_init(&ctx->slock); | |
| 835 | + mutex_init(&ctx->mutex); | |
| 836 | + | |
| 837 | + /* Initialize the vb2 queue. */ | |
| 838 | + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | |
| 839 | + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; | |
| 840 | + q->drv_priv = ctx; | |
| 841 | + q->buf_struct_size = sizeof(struct cal_buffer); | |
| 842 | + q->ops = &cal_video_qops; | |
| 843 | + q->mem_ops = &vb2_dma_contig_memops; | |
| 844 | + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; | |
| 845 | + q->lock = &ctx->mutex; | |
| 846 | + q->min_buffers_needed = 3; | |
| 847 | + q->dev = ctx->cal->dev; | |
| 848 | + | |
| 849 | + ret = vb2_queue_init(q); | |
| 850 | + if (ret) | |
| 851 | + return ret; | |
| 852 | + | |
| 853 | + /* Initialize the video device and media entity. */ | |
| 854 | + *vfd = cal_videodev; | |
| 855 | + vfd->v4l2_dev = &ctx->cal->v4l2_dev; | |
| 856 | + vfd->queue = q; | |
| 857 | + snprintf(vfd->name, sizeof(vfd->name), "CAL output %u", ctx->index); | |
| 858 | + vfd->lock = &ctx->mutex; | |
| 859 | + video_set_drvdata(vfd, ctx); | |
| 860 | + | |
| 861 | + ctx->pad.flags = MEDIA_PAD_FL_SINK; | |
| 862 | + ret = media_entity_pads_init(&vfd->entity, 1, &ctx->pad); | |
| 863 | + if (ret < 0) | |
| 864 | + return ret; | |
| 865 | + | |
| 866 | + /* Initialize the control handler. */ | |
| 867 | + ret = v4l2_ctrl_handler_init(hdl, 11); | |
| 868 | + if (ret < 0) { | |
| 869 | + ctx_err(ctx, "Failed to init ctrl handler\n"); | |
| 870 | + goto error; | |
| 871 | + } | |
| 872 | + | |
| 873 | + vfd->ctrl_handler = hdl; | |
| 874 | + | |
| 875 | + return 0; | |
| 876 | + | |
| 877 | +error: | |
| 878 | + media_entity_cleanup(&vfd->entity); | |
| 879 | + return ret; | |
| 880 | +} | |
| 881 | + | |
| 882 | +void cal_ctx_v4l2_cleanup(struct cal_ctx *ctx) | |
| 883 | +{ | |
| 884 | + v4l2_ctrl_handler_free(&ctx->ctrl_handler); | |
| 885 | + media_entity_cleanup(&ctx->vdev.entity); | |
| 886 | +} | |
| @@ -1,976 +1,167 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
| 2 | 2 | /* |
| 3 | − * TI CAL camera interface driver | |
| 3 | + * TI Camera Access Layer (CAL) - Driver | |
| 4 | 4 | * |
| 5 | − * Copyright (c) 2015 Texas Instruments Inc. | |
| 6 | − * Benoit Parrot, <bparrot@ti.com> | |
| 5 | + * Copyright (c) 2015-2020 Texas Instruments Inc. | |
| 6 | + * | |
| 7 | + * Authors: | |
| 8 | + * Benoit Parrot <bparrot@ti.com> | |
| 9 | + * Laurent Pinchart <laurent.pinchart@ideasonboard.com> | |
| 7 | 10 | */ |
| 8 | 11 | |
| 9 | 12 | #include <linux/clk.h> |
| 10 | 13 | #include <linux/interrupt.h> |
| 11 | −#include <linux/io.h> | |
| 12 | −#include <linux/ioctl.h> | |
| 14 | +#include <linux/mfd/syscon.h> | |
| 13 | 15 | #include <linux/module.h> |
| 16 | +#include <linux/of_device.h> | |
| 14 | 17 | #include <linux/platform_device.h> |
| 15 | −#include <linux/delay.h> | |
| 16 | 18 | #include <linux/pm_runtime.h> |
| 17 | −#include <linux/slab.h> | |
| 18 | −#include <linux/mfd/syscon.h> | |
| 19 | 19 | #include <linux/regmap.h> |
| 20 | +#include <linux/slab.h> | |
| 20 | 21 | #include <linux/videodev2.h> |
| 21 | −#include <linux/of_device.h> | |
| 22 | −#include <linux/of_graph.h> | |
| 23 | 22 | |
| 24 | −#include <media/v4l2-fwnode.h> | |
| 23 | +#include <media/media-device.h> | |
| 25 | 24 | #include <media/v4l2-async.h> |
| 26 | 25 | #include <media/v4l2-common.h> |
| 27 | −#include <media/v4l2-ctrls.h> | |
| 28 | 26 | #include <media/v4l2-device.h> |
| 29 | −#include <media/v4l2-event.h> | |
| 30 | −#include <media/v4l2-ioctl.h> | |
| 31 | −#include <media/v4l2-fh.h> | |
| 32 | 27 | #include <media/videobuf2-core.h> |
| 33 | 28 | #include <media/videobuf2-dma-contig.h> |
| 34 | −#include "cal_regs.h" | |
| 35 | 29 | |
| 36 | −#define CAL_MODULE_NAME "cal" | |
| 37 | − | |
| 38 | −#define MAX_WIDTH_BYTES (8192 * 8) | |
| 39 | −#define MAX_HEIGHT_LINES 16383 | |
| 40 | − | |
| 41 | −#define CAL_VERSION "0.1.0" | |
| 30 | +#include "cal.h" | |
| 31 | +#include "cal_regs.h" | |
| 42 | 32 | |
| 43 | 33 | MODULE_DESCRIPTION("TI CAL driver"); |
| 44 | 34 | MODULE_AUTHOR("Benoit Parrot, <bparrot@ti.com>"); |
| 45 | 35 | MODULE_LICENSE("GPL v2"); |
| 46 | −MODULE_VERSION(CAL_VERSION); | |
| 36 | +MODULE_VERSION("0.1.0"); | |
| 47 | 37 | |
| 48 | −static unsigned video_nr = -1; | |
| 49 | −module_param(video_nr, uint, 0644); | |
| 38 | +int cal_video_nr = -1; | |
| 39 | +module_param_named(video_nr, cal_video_nr, uint, 0644); | |
| 50 | 40 | MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect"); |
| 51 | 41 | |
| 52 | −static unsigned debug; | |
| 53 | −module_param(debug, uint, 0644); | |
| 42 | +unsigned int cal_debug; | |
| 43 | +module_param_named(debug, cal_debug, uint, 0644); | |
| 54 | 44 | MODULE_PARM_DESC(debug, "activates debug info"); |
| 55 | 45 | |
| 56 | −/* timeperframe: min/max and default */ | |
| 57 | −static const struct v4l2_fract | |
| 58 | − tpf_default = {.numerator = 1001, .denominator = 30000}; | |
| 59 | − | |
| 60 | −#define cal_dbg(level, caldev, fmt, arg...) \ | |
| 61 | − v4l2_dbg(level, debug, &caldev->v4l2_dev, fmt, ##arg) | |
| 62 | −#define cal_info(caldev, fmt, arg...) \ | |
| 63 | − v4l2_info(&caldev->v4l2_dev, fmt, ##arg) | |
| 64 | −#define cal_err(caldev, fmt, arg...) \ | |
| 65 | − v4l2_err(&caldev->v4l2_dev, fmt, ##arg) | |
| 66 | − | |
| 67 | −#define ctx_dbg(level, ctx, fmt, arg...) \ | |
| 68 | − v4l2_dbg(level, debug, &ctx->v4l2_dev, fmt, ##arg) | |
| 69 | −#define ctx_info(ctx, fmt, arg...) \ | |
| 70 | − v4l2_info(&ctx->v4l2_dev, fmt, ##arg) | |
| 71 | −#define ctx_err(ctx, fmt, arg...) \ | |
| 72 | − v4l2_err(&ctx->v4l2_dev, fmt, ##arg) | |
| 73 | − | |
| 74 | −#define CAL_NUM_INPUT 1 | |
| 75 | −#define CAL_NUM_CONTEXT 2 | |
| 76 | − | |
| 77 | −#define reg_read(dev, offset) ioread32(dev->base + offset) | |
| 78 | −#define reg_write(dev, offset, val) iowrite32(val, dev->base + offset) | |
| 79 | − | |
| 80 | −#define reg_read_field(dev, offset, mask) get_field(reg_read(dev, offset), \ | |
| 81 | − mask) | |
| 82 | −#define reg_write_field(dev, offset, field, mask) { \ | |
| 83 | − u32 val = reg_read(dev, offset); \ | |
| 84 | − set_field(&val, field, mask); \ | |
| 85 | − reg_write(dev, offset, val); } | |
| 86 | − | |
| 87 | 46 | /* ------------------------------------------------------------------ |
| 88 | − * Basic structures | |
| 47 | + * Platform Data | |
| 89 | 48 | * ------------------------------------------------------------------ |
| 90 | 49 | */ |
| 91 | 50 | |
| 92 | −struct cal_fmt { | |
| 93 | − u32 fourcc; | |
| 94 | − u32 code; | |
| 95 | − /* Bits per pixel */ | |
| 96 | − u8 bpp; | |
| 97 | −}; | |
| 98 | − | |
| 99 | −static struct cal_fmt cal_formats[] = { | |
| 100 | − { | |
| 101 | − .fourcc = V4L2_PIX_FMT_YUYV, | |
| 102 | − .code = MEDIA_BUS_FMT_YUYV8_2X8, | |
| 103 | − .bpp = 16, | |
| 104 | − }, { | |
| 105 | − .fourcc = V4L2_PIX_FMT_UYVY, | |
| 106 | − .code = MEDIA_BUS_FMT_UYVY8_2X8, | |
| 107 | − .bpp = 16, | |
| 108 | − }, { | |
| 109 | − .fourcc = V4L2_PIX_FMT_YVYU, | |
| 110 | − .code = MEDIA_BUS_FMT_YVYU8_2X8, | |
| 111 | − .bpp = 16, | |
| 112 | − }, { | |
| 113 | − .fourcc = V4L2_PIX_FMT_VYUY, | |
| 114 | − .code = MEDIA_BUS_FMT_VYUY8_2X8, | |
| 115 | − .bpp = 16, | |
| 116 | − }, { | |
| 117 | − .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ | |
| 118 | − .code = MEDIA_BUS_FMT_RGB565_2X8_LE, | |
| 119 | − .bpp = 16, | |
| 120 | − }, { | |
| 121 | − .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ | |
| 122 | − .code = MEDIA_BUS_FMT_RGB565_2X8_BE, | |
| 123 | − .bpp = 16, | |
| 124 | − }, { | |
| 125 | − .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */ | |
| 126 | − .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, | |
| 127 | − .bpp = 16, | |
| 128 | − }, { | |
| 129 | − .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */ | |
| 130 | − .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, | |
| 131 | − .bpp = 16, | |
| 132 | − }, { | |
| 133 | − .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ | |
| 134 | − .code = MEDIA_BUS_FMT_RGB888_2X12_LE, | |
| 135 | − .bpp = 24, | |
| 136 | − }, { | |
| 137 | − .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ | |
| 138 | − .code = MEDIA_BUS_FMT_RGB888_2X12_BE, | |
| 139 | − .bpp = 24, | |
| 140 | − }, { | |
| 141 | − .fourcc = V4L2_PIX_FMT_RGB32, /* argb */ | |
| 142 | − .code = MEDIA_BUS_FMT_ARGB8888_1X32, | |
| 143 | − .bpp = 32, | |
| 144 | − }, { | |
| 145 | − .fourcc = V4L2_PIX_FMT_SBGGR8, | |
| 146 | − .code = MEDIA_BUS_FMT_SBGGR8_1X8, | |
| 147 | − .bpp = 8, | |
| 148 | − }, { | |
| 149 | − .fourcc = V4L2_PIX_FMT_SGBRG8, | |
| 150 | − .code = MEDIA_BUS_FMT_SGBRG8_1X8, | |
| 151 | − .bpp = 8, | |
| 152 | − }, { | |
| 153 | − .fourcc = V4L2_PIX_FMT_SGRBG8, | |
| 154 | − .code = MEDIA_BUS_FMT_SGRBG8_1X8, | |
| 155 | − .bpp = 8, | |
| 156 | − }, { | |
| 157 | − .fourcc = V4L2_PIX_FMT_SRGGB8, | |
| 158 | − .code = MEDIA_BUS_FMT_SRGGB8_1X8, | |
| 159 | − .bpp = 8, | |
| 160 | − }, { | |
| 161 | − .fourcc = V4L2_PIX_FMT_SBGGR10, | |
| 162 | − .code = MEDIA_BUS_FMT_SBGGR10_1X10, | |
| 163 | − .bpp = 10, | |
| 164 | − }, { | |
| 165 | − .fourcc = V4L2_PIX_FMT_SGBRG10, | |
| 166 | − .code = MEDIA_BUS_FMT_SGBRG10_1X10, | |
| 167 | − .bpp = 10, | |
| 168 | − }, { | |
| 169 | − .fourcc = V4L2_PIX_FMT_SGRBG10, | |
| 170 | − .code = MEDIA_BUS_FMT_SGRBG10_1X10, | |
| 171 | − .bpp = 10, | |
| 172 | − }, { | |
| 173 | − .fourcc = V4L2_PIX_FMT_SRGGB10, | |
| 174 | − .code = MEDIA_BUS_FMT_SRGGB10_1X10, | |
| 175 | − .bpp = 10, | |
| 176 | − }, { | |
| 177 | − .fourcc = V4L2_PIX_FMT_SBGGR12, | |
| 178 | − .code = MEDIA_BUS_FMT_SBGGR12_1X12, | |
| 179 | − .bpp = 12, | |
| 180 | − }, { | |
| 181 | − .fourcc = V4L2_PIX_FMT_SGBRG12, | |
| 182 | − .code = MEDIA_BUS_FMT_SGBRG12_1X12, | |
| 183 | − .bpp = 12, | |
| 184 | − }, { | |
| 185 | − .fourcc = V4L2_PIX_FMT_SGRBG12, | |
| 186 | − .code = MEDIA_BUS_FMT_SGRBG12_1X12, | |
| 187 | − .bpp = 12, | |
| 188 | − }, { | |
| 189 | − .fourcc = V4L2_PIX_FMT_SRGGB12, | |
| 190 | − .code = MEDIA_BUS_FMT_SRGGB12_1X12, | |
| 191 | − .bpp = 12, | |
| 192 | − }, | |
| 193 | −}; | |
| 194 | − | |
| 195 | −/* Print Four-character-code (FOURCC) */ | |
| 196 | −static char *fourcc_to_str(u32 fmt) | |
| 197 | −{ | |
| 198 | − static char code[5]; | |
| 199 | − | |
| 200 | − code[0] = (unsigned char)(fmt & 0xff); | |
| 201 | − code[1] = (unsigned char)((fmt >> 8) & 0xff); | |
| 202 | − code[2] = (unsigned char)((fmt >> 16) & 0xff); | |
| 203 | − code[3] = (unsigned char)((fmt >> 24) & 0xff); | |
| 204 | − code[4] = '\0'; | |
| 205 | − | |
| 206 | − return code; | |
| 207 | −} | |
| 208 | − | |
| 209 | −/* buffer for one video frame */ | |
| 210 | −struct cal_buffer { | |
| 211 | − /* common v4l buffer stuff -- must be first */ | |
| 212 | − struct vb2_v4l2_buffer vb; | |
| 213 | − struct list_head list; | |
| 214 | − const struct cal_fmt *fmt; | |
| 215 | −}; | |
| 216 | − | |
| 217 | −struct cal_dmaqueue { | |
| 218 | − struct list_head active; | |
| 219 | − | |
| 220 | − /* Counters to control fps rate */ | |
| 221 | − int frame; | |
| 222 | − int ini_jiffies; | |
| 223 | −}; | |
| 224 | − | |
| 225 | −struct cc_data { | |
| 226 | − void __iomem *base; | |
| 227 | − struct resource *res; | |
| 228 | − | |
| 229 | − struct platform_device *pdev; | |
| 230 | −}; | |
| 231 | − | |
| 232 | −/* CTRL_CORE_CAMERRX_CONTROL register field id */ | |
| 233 | −enum cal_camerarx_field { | |
| 234 | − F_CTRLCLKEN, | |
| 235 | − F_CAMMODE, | |
| 236 | − F_LANEENABLE, | |
| 237 | − F_CSI_MODE, | |
| 238 | − | |
| 239 | − F_MAX_FIELDS, | |
| 240 | −}; | |
| 241 | − | |
| 242 | −struct cal_csi2_phy { | |
| 243 | − struct regmap_field *fields[F_MAX_FIELDS]; | |
| 244 | − struct reg_field *base_fields; | |
| 245 | − const int num_lanes; | |
| 246 | −}; | |
| 247 | − | |
| 248 | −struct cal_data { | |
| 249 | − const int num_csi2_phy; | |
| 250 | − struct cal_csi2_phy *csi2_phy_core; | |
| 251 | − | |
| 252 | − const unsigned int flags; | |
| 253 | −}; | |
| 254 | − | |
| 255 | −static struct reg_field dra72x_ctrl_core_csi0_reg_fields[F_MAX_FIELDS] = { | |
| 256 | − [F_CTRLCLKEN] = REG_FIELD(0, 10, 10), | |
| 257 | − [F_CAMMODE] = REG_FIELD(0, 11, 12), | |
| 258 | − [F_LANEENABLE] = REG_FIELD(0, 13, 16), | |
| 259 | − [F_CSI_MODE] = REG_FIELD(0, 17, 17), | |
| 260 | −}; | |
| 261 | − | |
| 262 | −static struct reg_field dra72x_ctrl_core_csi1_reg_fields[F_MAX_FIELDS] = { | |
| 263 | − [F_CTRLCLKEN] = REG_FIELD(0, 0, 0), | |
| 264 | − [F_CAMMODE] = REG_FIELD(0, 1, 2), | |
| 265 | − [F_LANEENABLE] = REG_FIELD(0, 3, 4), | |
| 266 | − [F_CSI_MODE] = REG_FIELD(0, 5, 5), | |
| 267 | −}; | |
| 268 | − | |
| 269 | −static struct cal_csi2_phy dra72x_cal_csi_phy[] = { | |
| 51 | +static const struct cal_camerarx_data dra72x_cal_camerarx[] = { | |
| 270 | 52 | { |
| 271 | − .base_fields = dra72x_ctrl_core_csi0_reg_fields, | |
| 53 | + .fields = { | |
| 54 | + [F_CTRLCLKEN] = { 10, 10 }, | |
| 55 | + [F_CAMMODE] = { 11, 12 }, | |
| 56 | + [F_LANEENABLE] = { 13, 16 }, | |
| 57 | + [F_CSI_MODE] = { 17, 17 }, | |
| 58 | + }, | |
| 272 | 59 | .num_lanes = 4, |
| 273 | 60 | }, |
| 274 | 61 | { |
| 275 | − .base_fields = dra72x_ctrl_core_csi1_reg_fields, | |
| 62 | + .fields = { | |
| 63 | + [F_CTRLCLKEN] = { 0, 0 }, | |
| 64 | + [F_CAMMODE] = { 1, 2 }, | |
| 65 | + [F_LANEENABLE] = { 3, 4 }, | |
| 66 | + [F_CSI_MODE] = { 5, 5 }, | |
| 67 | + }, | |
| 276 | 68 | .num_lanes = 2, |
| 277 | 69 | }, |
| 278 | 70 | }; |
| 279 | 71 | |
| 280 | 72 | static const struct cal_data dra72x_cal_data = { |
| 281 | − .csi2_phy_core = dra72x_cal_csi_phy, | |
| 282 | − .num_csi2_phy = ARRAY_SIZE(dra72x_cal_csi_phy), | |
| 73 | + .camerarx = dra72x_cal_camerarx, | |
| 74 | + .num_csi2_phy = ARRAY_SIZE(dra72x_cal_camerarx), | |
| 283 | 75 | }; |
| 284 | 76 | |
| 285 | 77 | static const struct cal_data dra72x_es1_cal_data = { |
| 286 | − .csi2_phy_core = dra72x_cal_csi_phy, | |
| 287 | − .num_csi2_phy = ARRAY_SIZE(dra72x_cal_csi_phy), | |
| 78 | + .camerarx = dra72x_cal_camerarx, | |
| 79 | + .num_csi2_phy = ARRAY_SIZE(dra72x_cal_camerarx), | |
| 288 | 80 | .flags = DRA72_CAL_PRE_ES2_LDO_DISABLE, |
| 289 | 81 | }; |
| 290 | 82 | |
| 291 | −static struct reg_field dra76x_ctrl_core_csi0_reg_fields[F_MAX_FIELDS] = { | |
| 292 | − [F_CTRLCLKEN] = REG_FIELD(0, 8, 8), | |
| 293 | − [F_CAMMODE] = REG_FIELD(0, 9, 10), | |
| 294 | − [F_CSI_MODE] = REG_FIELD(0, 11, 11), | |
| 295 | − [F_LANEENABLE] = REG_FIELD(0, 27, 31), | |
| 296 | −}; | |
| 297 | − | |
| 298 | −static struct reg_field dra76x_ctrl_core_csi1_reg_fields[F_MAX_FIELDS] = { | |
| 299 | − [F_CTRLCLKEN] = REG_FIELD(0, 0, 0), | |
| 300 | − [F_CAMMODE] = REG_FIELD(0, 1, 2), | |
| 301 | − [F_CSI_MODE] = REG_FIELD(0, 3, 3), | |
| 302 | − [F_LANEENABLE] = REG_FIELD(0, 24, 26), | |
| 303 | −}; | |
| 304 | − | |
| 305 | −static struct cal_csi2_phy dra76x_cal_csi_phy[] = { | |
| 83 | +static const struct cal_camerarx_data dra76x_cal_csi_phy[] = { | |
| 306 | 84 | { |
| 307 | − .base_fields = dra76x_ctrl_core_csi0_reg_fields, | |
| 85 | + .fields = { | |
| 86 | + [F_CTRLCLKEN] = { 8, 8 }, | |
| 87 | + [F_CAMMODE] = { 9, 10 }, | |
| 88 | + [F_CSI_MODE] = { 11, 11 }, | |
| 89 | + [F_LANEENABLE] = { 27, 31 }, | |
| 90 | + }, | |
| 308 | 91 | .num_lanes = 5, |
| 309 | 92 | }, |
| 310 | 93 | { |
| 311 | − .base_fields = dra76x_ctrl_core_csi1_reg_fields, | |
| 94 | + .fields = { | |
| 95 | + [F_CTRLCLKEN] = { 0, 0 }, | |
| 96 | + [F_CAMMODE] = { 1, 2 }, | |
| 97 | + [F_CSI_MODE] = { 3, 3 }, | |
| 98 | + [F_LANEENABLE] = { 24, 26 }, | |
| 99 | + }, | |
| 312 | 100 | .num_lanes = 3, |
| 313 | 101 | }, |
| 314 | 102 | }; |
| 315 | 103 | |
| 316 | 104 | static const struct cal_data dra76x_cal_data = { |
| 317 | − .csi2_phy_core = dra76x_cal_csi_phy, | |
| 105 | + .camerarx = dra76x_cal_csi_phy, | |
| 318 | 106 | .num_csi2_phy = ARRAY_SIZE(dra76x_cal_csi_phy), |
| 319 | 107 | }; |
| 320 | 108 | |
| 321 | −static struct reg_field am654_ctrl_core_csi0_reg_fields[F_MAX_FIELDS] = { | |
| 322 | − [F_CTRLCLKEN] = REG_FIELD(0, 15, 15), | |
| 323 | − [F_CAMMODE] = REG_FIELD(0, 24, 25), | |
| 324 | − [F_LANEENABLE] = REG_FIELD(0, 0, 4), | |
| 325 | −}; | |
| 326 | − | |
| 327 | −static struct cal_csi2_phy am654_cal_csi_phy[] = { | |
| 109 | +static const struct cal_camerarx_data am654_cal_csi_phy[] = { | |
| 328 | 110 | { |
| 329 | − .base_fields = am654_ctrl_core_csi0_reg_fields, | |
| 111 | + .fields = { | |
| 112 | + [F_CTRLCLKEN] = { 15, 15 }, | |
| 113 | + [F_CAMMODE] = { 24, 25 }, | |
| 114 | + [F_LANEENABLE] = { 0, 4 }, | |
| 115 | + }, | |
| 330 | 116 | .num_lanes = 5, |
| 331 | 117 | }, |
| 332 | 118 | }; |
| 333 | 119 | |
| 334 | 120 | static const struct cal_data am654_cal_data = { |
| 335 | − .csi2_phy_core = am654_cal_csi_phy, | |
| 121 | + .camerarx = am654_cal_csi_phy, | |
| 336 | 122 | .num_csi2_phy = ARRAY_SIZE(am654_cal_csi_phy), |
| 337 | 123 | }; |
| 338 | 124 | |
| 339 | −/* | |
| 340 | − * there is one cal_dev structure in the driver, it is shared by | |
| 341 | − * all instances. | |
| 342 | − */ | |
| 343 | −struct cal_dev { | |
| 344 | − struct clk *fclk; | |
| 345 | − int irq; | |
| 346 | − void __iomem *base; | |
| 347 | − struct resource *res; | |
| 348 | − struct platform_device *pdev; | |
| 349 | − struct v4l2_device v4l2_dev; | |
| 350 | − | |
| 351 | − /* Controller flags for special cases */ | |
| 352 | − unsigned int flags; | |
| 353 | − | |
| 354 | − const struct cal_data *data; | |
| 355 | − | |
| 356 | − /* Control Module handle */ | |
| 357 | − struct regmap *syscon_camerrx; | |
| 358 | − u32 syscon_camerrx_offset; | |
| 359 | − | |
| 360 | − /* Camera Core Module handle */ | |
| 361 | − struct cc_data *cc[CAL_NUM_CSI2_PORTS]; | |
| 362 | − | |
| 363 | − struct cal_ctx *ctx[CAL_NUM_CONTEXT]; | |
| 364 | −}; | |
| 365 | − | |
| 366 | −/* | |
| 367 | − * There is one cal_ctx structure for each camera core context. | |
| 368 | − */ | |
| 369 | −struct cal_ctx { | |
| 370 | − struct v4l2_device v4l2_dev; | |
| 371 | − struct v4l2_ctrl_handler ctrl_handler; | |
| 372 | − struct video_device vdev; | |
| 373 | − struct v4l2_async_notifier notifier; | |
| 374 | − struct v4l2_subdev *sensor; | |
| 375 | − struct v4l2_fwnode_endpoint endpoint; | |
| 376 | − | |
| 377 | − struct v4l2_fh fh; | |
| 378 | − struct cal_dev *dev; | |
| 379 | − struct cc_data *cc; | |
| 380 | − | |
| 381 | − /* v4l2_ioctl mutex */ | |
| 382 | − struct mutex mutex; | |
| 383 | − /* v4l2 buffers lock */ | |
| 384 | − spinlock_t slock; | |
| 385 | − | |
| 386 | − /* Several counters */ | |
| 387 | − unsigned long jiffies; | |
| 388 | − | |
| 389 | − struct cal_dmaqueue vidq; | |
| 390 | − | |
| 391 | − /* Input Number */ | |
| 392 | − int input; | |
| 393 | − | |
| 394 | − /* video capture */ | |
| 395 | − const struct cal_fmt *fmt; | |
| 396 | − /* Used to store current pixel format */ | |
| 397 | − struct v4l2_format v_fmt; | |
| 398 | − /* Used to store current mbus frame format */ | |
| 399 | − struct v4l2_mbus_framefmt m_fmt; | |
| 400 | − | |
| 401 | − /* Current subdev enumerated format */ | |
| 402 | − struct cal_fmt *active_fmt[ARRAY_SIZE(cal_formats)]; | |
| 403 | − int num_active_fmt; | |
| 404 | − | |
| 405 | − struct v4l2_fract timeperframe; | |
| 406 | − unsigned int sequence; | |
| 407 | − unsigned int external_rate; | |
| 408 | − struct vb2_queue vb_vidq; | |
| 409 | − unsigned int seq_count; | |
| 410 | − unsigned int csi2_port; | |
| 411 | − unsigned int virtual_channel; | |
| 412 | − | |
| 413 | − /* Pointer pointing to current v4l2_buffer */ | |
| 414 | − struct cal_buffer *cur_frm; | |
| 415 | − /* Pointer pointing to next v4l2_buffer */ | |
| 416 | − struct cal_buffer *next_frm; | |
| 417 | − | |
| 418 | − bool dma_act; | |
| 419 | −}; | |
| 420 | − | |
| 421 | −static const struct cal_fmt *find_format_by_pix(struct cal_ctx *ctx, | |
| 422 | − u32 pixelformat) | |
| 423 | −{ | |
| 424 | − const struct cal_fmt *fmt; | |
| 425 | − unsigned int k; | |
| 426 | − | |
| 427 | − for (k = 0; k < ctx->num_active_fmt; k++) { | |
| 428 | − fmt = ctx->active_fmt[k]; | |
| 429 | − if (fmt->fourcc == pixelformat) | |
| 430 | − return fmt; | |
| 431 | − } | |
| 432 | − | |
| 433 | − return NULL; | |
| 434 | −} | |
| 435 | − | |
| 436 | −static const struct cal_fmt *find_format_by_code(struct cal_ctx *ctx, | |
| 437 | − u32 code) | |
| 438 | −{ | |
| 439 | − const struct cal_fmt *fmt; | |
| 440 | − unsigned int k; | |
| 441 | − | |
| 442 | − for (k = 0; k < ctx->num_active_fmt; k++) { | |
| 443 | − fmt = ctx->active_fmt[k]; | |
| 444 | − if (fmt->code == code) | |
| 445 | − return fmt; | |
| 446 | − } | |
| 447 | − | |
| 448 | − return NULL; | |
| 449 | −} | |
| 450 | − | |
| 451 | −static inline struct cal_ctx *notifier_to_ctx(struct v4l2_async_notifier *n) | |
| 452 | −{ | |
| 453 | − return container_of(n, struct cal_ctx, notifier); | |
| 454 | −} | |
| 455 | − | |
| 456 | −static inline int get_field(u32 value, u32 mask) | |
| 457 | −{ | |
| 458 | − return (value & mask) >> __ffs(mask); | |
| 459 | −} | |
| 460 | − | |
| 461 | −static inline void set_field(u32 *valp, u32 field, u32 mask) | |
| 462 | −{ | |
| 463 | − u32 val = *valp; | |
| 464 | − | |
| 465 | − val &= ~mask; | |
| 466 | − val |= (field << __ffs(mask)) & mask; | |
| 467 | − *valp = val; | |
| 468 | −} | |
| 469 | − | |
| 470 | −static u32 cal_data_get_phy_max_lanes(struct cal_ctx *ctx) | |
| 471 | −{ | |
| 472 | − struct cal_dev *dev = ctx->dev; | |
| 473 | − u32 phy_id = ctx->csi2_port - 1; | |
| 474 | − | |
| 475 | − return dev->data->csi2_phy_core[phy_id].num_lanes; | |
| 476 | −} | |
| 477 | − | |
| 478 | −static u32 cal_data_get_num_csi2_phy(struct cal_dev *dev) | |
| 479 | −{ | |
| 480 | − return dev->data->num_csi2_phy; | |
| 481 | −} | |
| 482 | − | |
| 483 | −static int cal_camerarx_regmap_init(struct cal_dev *dev) | |
| 484 | −{ | |
| 485 | − struct reg_field *field; | |
| 486 | − struct cal_csi2_phy *phy; | |
| 487 | − int i, j; | |
| 488 | − | |
| 489 | − if (!dev->data) | |
| 490 | − return -EINVAL; | |
| 491 | − | |
| 492 | − for (i = 0; i < cal_data_get_num_csi2_phy(dev); i++) { | |
| 493 | − phy = &dev->data->csi2_phy_core[i]; | |
| 494 | − for (j = 0; j < F_MAX_FIELDS; j++) { | |
| 495 | − field = &phy->base_fields[j]; | |
| 496 | − /* | |
| 497 | − * Here we update the reg offset with the | |
| 498 | − * value found in DT | |
| 499 | − */ | |
| 500 | − field->reg = dev->syscon_camerrx_offset; | |
| 501 | − phy->fields[j] = | |
| 502 | − devm_regmap_field_alloc(&dev->pdev->dev, | |
| 503 | − dev->syscon_camerrx, | |
| 504 | − *field); | |
| 505 | − if (IS_ERR(phy->fields[j])) { | |
| 506 | − cal_err(dev, "Unable to allocate regmap fields\n"); | |
| 507 | − return PTR_ERR(phy->fields[j]); | |
| 508 | − } | |
| 509 | − } | |
| 510 | − } | |
| 511 | − return 0; | |
| 512 | −} | |
| 513 | − | |
| 514 | −static const struct regmap_config cal_regmap_config = { | |
| 515 | − .reg_bits = 32, | |
| 516 | − .val_bits = 32, | |
| 517 | − .reg_stride = 4, | |
| 518 | −}; | |
| 519 | − | |
| 520 | −static struct regmap *cal_get_camerarx_regmap(struct cal_dev *dev) | |
| 521 | −{ | |
| 522 | − struct platform_device *pdev = dev->pdev; | |
| 523 | − struct regmap *regmap; | |
| 524 | − void __iomem *base; | |
| 525 | − u32 reg_io_width; | |
| 526 | − struct regmap_config r_config = cal_regmap_config; | |
| 527 | − struct resource *res; | |
| 528 | − | |
| 529 | − res = platform_get_resource_byname(pdev, IORESOURCE_MEM, | |
| 530 | − "camerrx_control"); | |
| 531 | − base = devm_ioremap_resource(&pdev->dev, res); | |
| 532 | − if (IS_ERR(base)) { | |
| 533 | − cal_err(dev, "failed to ioremap\n"); | |
| 534 | − return ERR_CAST(base); | |
| 535 | − } | |
| 536 | − | |
| 537 | − cal_dbg(1, dev, "ioresource %s at %pa - %pa\n", | |
| 538 | − res->name, &res->start, &res->end); | |
| 539 | − | |
| 540 | − reg_io_width = 4; | |
| 541 | − r_config.reg_stride = reg_io_width; | |
| 542 | − r_config.val_bits = reg_io_width * 8; | |
| 543 | − r_config.max_register = resource_size(res) - reg_io_width; | |
| 544 | − | |
| 545 | − regmap = regmap_init_mmio(NULL, base, &r_config); | |
| 546 | − if (IS_ERR(regmap)) | |
| 547 | − pr_err("regmap init failed\n"); | |
| 548 | − | |
| 549 | − return regmap; | |
| 550 | −} | |
| 551 | − | |
| 552 | −/* | |
| 553 | − * Control Module CAMERARX block access | |
| 554 | − */ | |
| 555 | −static void camerarx_phy_enable(struct cal_ctx *ctx) | |
| 556 | −{ | |
| 557 | − struct cal_csi2_phy *phy; | |
| 558 | − u32 phy_id = ctx->csi2_port - 1; | |
| 559 | − u32 max_lanes; | |
| 560 | − | |
| 561 | − phy = &ctx->dev->data->csi2_phy_core[phy_id]; | |
| 562 | − regmap_field_write(phy->fields[F_CAMMODE], 0); | |
| 563 | − /* Always enable all lanes at the phy control level */ | |
| 564 | − max_lanes = (1 << cal_data_get_phy_max_lanes(ctx)) - 1; | |
| 565 | − regmap_field_write(phy->fields[F_LANEENABLE], max_lanes); | |
| 566 | − /* F_CSI_MODE is not present on every architecture */ | |
| 567 | − if (phy->fields[F_CSI_MODE]) | |
| 568 | − regmap_field_write(phy->fields[F_CSI_MODE], 1); | |
| 569 | − regmap_field_write(phy->fields[F_CTRLCLKEN], 1); | |
| 570 | −} | |
| 571 | − | |
| 572 | −static void camerarx_phy_disable(struct cal_ctx *ctx) | |
| 573 | −{ | |
| 574 | − struct cal_csi2_phy *phy; | |
| 575 | − u32 phy_id = ctx->csi2_port - 1; | |
| 576 | − | |
| 577 | − phy = &ctx->dev->data->csi2_phy_core[phy_id]; | |
| 578 | − regmap_field_write(phy->fields[F_CTRLCLKEN], 0); | |
| 579 | −} | |
| 580 | − | |
| 581 | −/* | |
| 582 | − * Camera Instance access block | |
| 583 | − */ | |
| 584 | −static struct cc_data *cc_create(struct cal_dev *dev, unsigned int core) | |
| 585 | −{ | |
| 586 | − struct platform_device *pdev = dev->pdev; | |
| 587 | − struct cc_data *cc; | |
| 588 | − | |
| 589 | − cc = devm_kzalloc(&pdev->dev, sizeof(*cc), GFP_KERNEL); | |
| 590 | − if (!cc) | |
| 591 | − return ERR_PTR(-ENOMEM); | |
| 592 | − | |
| 593 | − cc->res = platform_get_resource_byname(pdev, | |
| 594 | − IORESOURCE_MEM, | |
| 595 | − (core == 0) ? | |
| 596 | − "cal_rx_core0" : | |
| 597 | − "cal_rx_core1"); | |
| 598 | − cc->base = devm_ioremap_resource(&pdev->dev, cc->res); | |
| 599 | − if (IS_ERR(cc->base)) { | |
| 600 | − cal_err(dev, "failed to ioremap\n"); | |
| 601 | − return ERR_CAST(cc->base); | |
| 602 | − } | |
| 603 | − | |
| 604 | − cal_dbg(1, dev, "ioresource %s at %pa - %pa\n", | |
| 605 | − cc->res->name, &cc->res->start, &cc->res->end); | |
| 606 | − | |
| 607 | − return cc; | |
| 608 | −} | |
| 609 | − | |
| 610 | −/* | |
| 611 | − * Get Revision and HW info | |
| 125 | +/* ------------------------------------------------------------------ | |
| 126 | + * I/O Register Accessors | |
| 127 | + * ------------------------------------------------------------------ | |
| 612 | 128 | */ |
| 613 | −static void cal_get_hwinfo(struct cal_dev *dev) | |
| 614 | −{ | |
| 615 | − u32 revision = 0; | |
| 616 | − u32 hwinfo = 0; | |
| 617 | − | |
| 618 | − revision = reg_read(dev, CAL_HL_REVISION); | |
| 619 | − cal_dbg(3, dev, "CAL_HL_REVISION = 0x%08x (expecting 0x40000200)\n", | |
| 620 | − revision); | |
| 621 | − | |
| 622 | − hwinfo = reg_read(dev, CAL_HL_HWINFO); | |
| 623 | − cal_dbg(3, dev, "CAL_HL_HWINFO = 0x%08x (expecting 0xA3C90469)\n", | |
| 624 | − hwinfo); | |
| 625 | −} | |
| 626 | 129 | |
| 627 | −/* | |
| 628 | − * Errata i913: CSI2 LDO Needs to be disabled when module is powered on | |
| 629 | − * | |
| 630 | − * Enabling CSI2 LDO shorts it to core supply. It is crucial the 2 CSI2 | |
| 631 | − * LDOs on the device are disabled if CSI-2 module is powered on | |
| 632 | − * (0x4845 B304 | 0x4845 B384 [28:27] = 0x1) or in ULPS (0x4845 B304 | |
| 633 | − * | 0x4845 B384 [28:27] = 0x2) mode. Common concerns include: high | |
| 634 | − * current draw on the module supply in active mode. | |
| 635 | − * | |
| 636 | − * Errata does not apply when CSI-2 module is powered off | |
| 637 | − * (0x4845 B304 | 0x4845 B384 [28:27] = 0x0). | |
| 638 | − * | |
| 639 | − * SW Workaround: | |
| 640 | − * Set the following register bits to disable the LDO, | |
| 641 | − * which is essentially CSI2 REG10 bit 6: | |
| 642 | − * | |
| 643 | − * Core 0: 0x4845 B828 = 0x0000 0040 | |
| 644 | − * Core 1: 0x4845 B928 = 0x0000 0040 | |
| 645 | − */ | |
| 646 | −static void i913_errata(struct cal_dev *dev, unsigned int port) | |
| 130 | +void cal_quickdump_regs(struct cal_dev *cal) | |
| 647 | 131 | { |
| 648 | − u32 reg10 = reg_read(dev->cc[port], CAL_CSI2_PHY_REG10); | |
| 649 | − | |
| 650 | − set_field(®10, 1, CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK); | |
| 651 | − | |
| 652 | − cal_dbg(1, dev, "CSI2_%d_REG10 = 0x%08x\n", port, reg10); | |
| 653 | − reg_write(dev->cc[port], CAL_CSI2_PHY_REG10, reg10); | |
| 654 | −} | |
| 132 | + unsigned int i; | |
| 655 | 133 | |
| 656 | −static void cal_quickdump_regs(struct cal_dev *dev) | |
| 657 | −{ | |
| 658 | − cal_info(dev, "CAL Registers @ 0x%pa:\n", &dev->res->start); | |
| 134 | + cal_info(cal, "CAL Registers @ 0x%pa:\n", &cal->res->start); | |
| 659 | 135 | print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, |
| 660 | − (__force const void *)dev->base, | |
| 661 | − resource_size(dev->res), false); | |
| 136 | + (__force const void *)cal->base, | |
| 137 | + resource_size(cal->res), false); | |
| 662 | 138 | |
| 663 | − if (dev->ctx[0]) { | |
| 664 | − cal_info(dev, "CSI2 Core 0 Registers @ %pa:\n", | |
| 665 | − &dev->ctx[0]->cc->res->start); | |
| 666 | − print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, | |
| 667 | − (__force const void *)dev->ctx[0]->cc->base, | |
| 668 | − resource_size(dev->ctx[0]->cc->res), | |
| 669 | − false); | |
| 670 | − } | |
| 139 | + for (i = 0; i < ARRAY_SIZE(cal->phy); ++i) { | |
| 140 | + struct cal_camerarx *phy = cal->phy[i]; | |
| 141 | + | |
| 142 | + if (!phy) | |
| 143 | + continue; | |
| 671 | 144 | |
| 672 | − if (dev->ctx[1]) { | |
| 673 | − cal_info(dev, "CSI2 Core 1 Registers @ %pa:\n", | |
| 674 | − &dev->ctx[1]->cc->res->start); | |
| 145 | + cal_info(cal, "CSI2 Core %u Registers @ %pa:\n", i, | |
| 146 | + &phy->res->start); | |
| 675 | 147 | print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, |
| 676 | − (__force const void *)dev->ctx[1]->cc->base, | |
| 677 | − resource_size(dev->ctx[1]->cc->res), | |
| 148 | + (__force const void *)phy->base, | |
| 149 | + resource_size(phy->res), | |
| 678 | 150 | false); |
| 679 | 151 | } |
| 680 | 152 | } |
| 681 | 153 | |
| 682 | −/* | |
| 683 | − * Enable the expected IRQ sources | |
| 154 | +/* ------------------------------------------------------------------ | |
| 155 | + * Context Management | |
| 156 | + * ------------------------------------------------------------------ | |
| 684 | 157 | */ |
| 685 | −static void enable_irqs(struct cal_ctx *ctx) | |
| 686 | −{ | |
| 687 | − u32 val; | |
| 688 | − | |
| 689 | − const u32 cio_err_mask = | |
| 690 | − CAL_CSI2_COMPLEXIO_IRQ_LANE_ERRORS_MASK | | |
| 691 | − CAL_CSI2_COMPLEXIO_IRQ_FIFO_OVR_MASK | | |
| 692 | − CAL_CSI2_COMPLEXIO_IRQ_SHORT_PACKET_MASK | | |
| 693 | − CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK; | |
| 694 | − | |
| 695 | − /* Enable CIO error irqs */ | |
| 696 | − reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(1), | |
| 697 | − CAL_HL_IRQ_CIO_MASK(ctx->csi2_port)); | |
| 698 | − reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_IRQENABLE(ctx->csi2_port), | |
| 699 | − cio_err_mask); | |
| 700 | − | |
| 701 | − /* Always enable OCPO error */ | |
| 702 | − reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(1), CAL_HL_IRQ_OCPO_ERR_MASK); | |
| 703 | − | |
| 704 | − /* Enable IRQ_WDMA_END 0/1 */ | |
| 705 | − val = 0; | |
| 706 | − set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); | |
| 707 | − reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(2), val); | |
| 708 | − /* Enable IRQ_WDMA_START 0/1 */ | |
| 709 | − val = 0; | |
| 710 | − set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); | |
| 711 | − reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(3), val); | |
| 712 | − /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ | |
| 713 | − reg_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(1), 0xFF000000); | |
| 714 | −} | |
| 715 | − | |
| 716 | −static void disable_irqs(struct cal_ctx *ctx) | |
| 717 | −{ | |
| 718 | − u32 val; | |
| 719 | − | |
| 720 | − /* Disable CIO error irqs */ | |
| 721 | − reg_write(ctx->dev, CAL_HL_IRQENABLE_CLR(1), | |
| 722 | − CAL_HL_IRQ_CIO_MASK(ctx->csi2_port)); | |
| 723 | − reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_IRQENABLE(ctx->csi2_port), | |
| 724 | − 0); | |
| 725 | − | |
| 726 | − /* Disable IRQ_WDMA_END 0/1 */ | |
| 727 | − val = 0; | |
| 728 | − set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); | |
| 729 | − reg_write(ctx->dev, CAL_HL_IRQENABLE_CLR(2), val); | |
| 730 | − /* Disable IRQ_WDMA_START 0/1 */ | |
| 731 | − val = 0; | |
| 732 | − set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); | |
| 733 | − reg_write(ctx->dev, CAL_HL_IRQENABLE_CLR(3), val); | |
| 734 | − /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ | |
| 735 | − reg_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(1), 0); | |
| 736 | −} | |
| 737 | − | |
| 738 | −static void csi2_cio_power(struct cal_ctx *ctx, bool enable) | |
| 739 | −{ | |
| 740 | − u32 target_state; | |
| 741 | − unsigned int i; | |
| 742 | − | |
| 743 | − target_state = enable ? CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON : | |
| 744 | − CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_OFF; | |
| 745 | − | |
| 746 | − reg_write_field(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), | |
| 747 | − target_state, CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK); | |
| 748 | − | |
| 749 | − for (i = 0; i < 10; i++) { | |
| 750 | − u32 current_state; | |
| 751 | − | |
| 752 | − current_state = reg_read_field(ctx->dev, | |
| 753 | − CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), | |
| 754 | − CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK); | |
| 755 | − | |
| 756 | − if (current_state == target_state) | |
| 757 | − break; | |
| 758 | − | |
| 759 | − usleep_range(1000, 1100); | |
| 760 | − } | |
| 761 | − | |
| 762 | − if (i == 10) | |
| 763 | − ctx_err(ctx, "Failed to power %s complexio\n", | |
| 764 | − enable ? "up" : "down"); | |
| 765 | −} | |
| 766 | − | |
| 767 | −static void csi2_phy_config(struct cal_ctx *ctx); | |
| 768 | − | |
| 769 | −static void csi2_phy_init(struct cal_ctx *ctx) | |
| 770 | −{ | |
| 771 | − u32 val; | |
| 772 | − u32 sscounter; | |
| 773 | − | |
| 774 | − /* Steps | |
| 775 | − * 1. Configure D-PHY mode and enable required lanes | |
| 776 | − * 2. Reset complex IO - Wait for completion of reset | |
| 777 | − * Note if the external sensor is not sending byte clock, | |
| 778 | − * the reset will timeout | |
| 779 | − * 3 Program Stop States | |
| 780 | − * A. Program THS_TERM, THS_SETTLE, etc... Timings parameters | |
| 781 | − * in terms of DDR clock periods | |
| 782 | − * B. Enable stop state transition timeouts | |
| 783 | − * 4.Force FORCERXMODE | |
| 784 | − * D. Enable pull down using pad control | |
| 785 | − * E. Power up PHY | |
| 786 | − * F. Wait for power up completion | |
| 787 | − * G. Wait for all enabled lane to reach stop state | |
| 788 | − * H. Disable pull down using pad control | |
| 789 | − */ | |
| 790 | − | |
| 791 | − /* 1. Configure D-PHY mode and enable required lanes */ | |
| 792 | − camerarx_phy_enable(ctx); | |
| 793 | − | |
| 794 | − /* 2. Reset complex IO - Do not wait for reset completion */ | |
| 795 | − reg_write_field(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), | |
| 796 | − CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL, | |
| 797 | − CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); | |
| 798 | − ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x De-assert Complex IO Reset\n", | |
| 799 | − ctx->csi2_port, | |
| 800 | − reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port))); | |
| 801 | − | |
| 802 | − /* Dummy read to allow SCP reset to complete */ | |
| 803 | − reg_read(ctx->cc, CAL_CSI2_PHY_REG0); | |
| 804 | − | |
| 805 | − /* 3.A. Program Phy Timing Parameters */ | |
| 806 | − csi2_phy_config(ctx); | |
| 807 | − | |
| 808 | − /* 3.B. Program Stop States */ | |
| 809 | − /* | |
| 810 | − * The stop-state-counter is based on fclk cycles, and we always use | |
| 811 | − * the x16 and x4 settings, so stop-state-timeout = | |
| 812 | − * fclk-cycle * 16 * 4 * counter. | |
| 813 | − * | |
| 814 | − * Stop-state-timeout must be more than 100us as per CSI2 spec, so we | |
| 815 | − * calculate a timeout that's 100us (rounding up). | |
| 816 | − */ | |
| 817 | − sscounter = DIV_ROUND_UP(clk_get_rate(ctx->dev->fclk), 10000 * 16 * 4); | |
| 818 | − | |
| 819 | − val = reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port)); | |
| 820 | − set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK); | |
| 821 | − set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK); | |
| 822 | − set_field(&val, sscounter, CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK); | |
| 823 | − reg_write(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port), val); | |
| 824 | − ctx_dbg(3, ctx, "CAL_CSI2_TIMING(%d) = 0x%08x Stop States\n", | |
| 825 | − ctx->csi2_port, | |
| 826 | − reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port))); | |
| 827 | − | |
| 828 | − /* 4. Force FORCERXMODE */ | |
| 829 | − reg_write_field(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port), | |
| 830 | − 1, CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK); | |
| 831 | − ctx_dbg(3, ctx, "CAL_CSI2_TIMING(%d) = 0x%08x Force RXMODE\n", | |
| 832 | − ctx->csi2_port, | |
| 833 | − reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port))); | |
| 834 | − | |
| 835 | − /* E. Power up the PHY using the complex IO */ | |
| 836 | − csi2_cio_power(ctx, true); | |
| 837 | −} | |
| 838 | − | |
| 839 | −static void csi2_wait_complexio_reset(struct cal_ctx *ctx) | |
| 840 | −{ | |
| 841 | − unsigned long timeout; | |
| 842 | − | |
| 843 | − timeout = jiffies + msecs_to_jiffies(750); | |
| 844 | − while (time_before(jiffies, timeout)) { | |
| 845 | − if (reg_read_field(ctx->dev, | |
| 846 | − CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), | |
| 847 | − CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == | |
| 848 | − CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) | |
| 849 | − break; | |
| 850 | − usleep_range(500, 5000); | |
| 851 | − } | |
| 852 | − | |
| 853 | − if (reg_read_field(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), | |
| 854 | − CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) != | |
| 855 | − CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) | |
| 856 | − ctx_err(ctx, "Timeout waiting for Complex IO reset done\n"); | |
| 857 | −} | |
| 858 | − | |
| 859 | −static void csi2_wait_stop_state(struct cal_ctx *ctx) | |
| 860 | −{ | |
| 861 | − unsigned long timeout; | |
| 862 | − | |
| 863 | − timeout = jiffies + msecs_to_jiffies(750); | |
| 864 | − while (time_before(jiffies, timeout)) { | |
| 865 | − if (reg_read_field(ctx->dev, | |
| 866 | − CAL_CSI2_TIMING(ctx->csi2_port), | |
| 867 | − CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) == 0) | |
| 868 | − break; | |
| 869 | − usleep_range(500, 5000); | |
| 870 | − } | |
| 871 | − | |
| 872 | − if (reg_read_field(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port), | |
| 873 | − CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) != 0) | |
| 874 | − ctx_err(ctx, "Timeout waiting for stop state\n"); | |
| 875 | −} | |
| 876 | − | |
| 877 | −static void csi2_wait_for_phy(struct cal_ctx *ctx) | |
| 878 | −{ | |
| 879 | − /* Steps | |
| 880 | − * 2. Wait for completion of reset | |
| 881 | − * Note if the external sensor is not sending byte clock, | |
| 882 | − * the reset will timeout | |
| 883 | − * 4.Force FORCERXMODE | |
| 884 | − * G. Wait for all enabled lane to reach stop state | |
| 885 | − * H. Disable pull down using pad control | |
| 886 | − */ | |
| 887 | − | |
| 888 | − /* 2. Wait for reset completion */ | |
| 889 | − csi2_wait_complexio_reset(ctx); | |
| 890 | − | |
| 891 | − /* 4. G. Wait for all enabled lane to reach stop state */ | |
| 892 | − csi2_wait_stop_state(ctx); | |
| 893 | − | |
| 894 | − ctx_dbg(1, ctx, "CSI2_%d_REG1 = 0x%08x (Bit(31,28) should be set!)\n", | |
| 895 | − (ctx->csi2_port - 1), reg_read(ctx->cc, CAL_CSI2_PHY_REG1)); | |
| 896 | −} | |
| 897 | − | |
| 898 | −static void csi2_phy_deinit(struct cal_ctx *ctx) | |
| 899 | −{ | |
| 900 | − int i; | |
| 901 | − | |
| 902 | − csi2_cio_power(ctx, false); | |
| 903 | − | |
| 904 | − /* Assert Comple IO Reset */ | |
| 905 | − reg_write_field(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), | |
| 906 | − CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL, | |
| 907 | − CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); | |
| 908 | − | |
| 909 | − /* Wait for power down completion */ | |
| 910 | − for (i = 0; i < 10; i++) { | |
| 911 | − if (reg_read_field(ctx->dev, | |
| 912 | − CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), | |
| 913 | − CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == | |
| 914 | − CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETONGOING) | |
| 915 | − break; | |
| 916 | − usleep_range(1000, 1100); | |
| 917 | − } | |
| 918 | − ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Complex IO in Reset (%d) %s\n", | |
| 919 | − ctx->csi2_port, | |
| 920 | − reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)), i, | |
| 921 | − (i >= 10) ? "(timeout)" : ""); | |
| 922 | − | |
| 923 | − /* Disable the phy */ | |
| 924 | − camerarx_phy_disable(ctx); | |
| 925 | −} | |
| 926 | − | |
| 927 | −static void csi2_lane_config(struct cal_ctx *ctx) | |
| 928 | −{ | |
| 929 | − u32 val = reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)); | |
| 930 | − u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK; | |
| 931 | − u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK; | |
| 932 | − struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = | |
| 933 | − &ctx->endpoint.bus.mipi_csi2; | |
| 934 | − int lane; | |
| 935 | − | |
| 936 | − set_field(&val, mipi_csi2->clock_lane + 1, lane_mask); | |
| 937 | − set_field(&val, mipi_csi2->lane_polarities[0], polarity_mask); | |
| 938 | − for (lane = 0; lane < mipi_csi2->num_data_lanes; lane++) { | |
| 939 | − /* | |
| 940 | − * Every lane are one nibble apart starting with the | |
| 941 | − * clock followed by the data lanes so shift masks by 4. | |
| 942 | − */ | |
| 943 | − lane_mask <<= 4; | |
| 944 | − polarity_mask <<= 4; | |
| 945 | − set_field(&val, mipi_csi2->data_lanes[lane] + 1, lane_mask); | |
| 946 | − set_field(&val, mipi_csi2->lane_polarities[lane + 1], | |
| 947 | − polarity_mask); | |
| 948 | − } | |
| 949 | − | |
| 950 | − reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), val); | |
| 951 | − ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n", | |
| 952 | − ctx->csi2_port, val); | |
| 953 | −} | |
| 954 | − | |
| 955 | −static void csi2_ppi_enable(struct cal_ctx *ctx) | |
| 956 | −{ | |
| 957 | − reg_write(ctx->dev, CAL_CSI2_PPI_CTRL(ctx->csi2_port), BIT(3)); | |
| 958 | − reg_write_field(ctx->dev, CAL_CSI2_PPI_CTRL(ctx->csi2_port), | |
| 959 | − 1, CAL_CSI2_PPI_CTRL_IF_EN_MASK); | |
| 960 | −} | |
| 961 | − | |
| 962 | −static void csi2_ppi_disable(struct cal_ctx *ctx) | |
| 963 | −{ | |
| 964 | − reg_write_field(ctx->dev, CAL_CSI2_PPI_CTRL(ctx->csi2_port), | |
| 965 | − 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK); | |
| 966 | −} | |
| 967 | 158 | |
| 968 | −static void csi2_ctx_config(struct cal_ctx *ctx) | |
| 159 | +void cal_ctx_csi2_config(struct cal_ctx *ctx) | |
| 969 | 160 | { |
| 970 | 161 | u32 val; |
| 971 | 162 | |
| 972 | − val = reg_read(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port)); | |
| 973 | − set_field(&val, ctx->csi2_port, CAL_CSI2_CTX_CPORT_MASK); | |
| 163 | + val = cal_read(ctx->cal, CAL_CSI2_CTX0(ctx->index)); | |
| 164 | + cal_set_field(&val, ctx->cport, CAL_CSI2_CTX_CPORT_MASK); | |
| 974 | 165 | /* |
| 975 | 166 | * DT type: MIPI CSI-2 Specs |
| 976 | 167 | * 0x1: All - DT filter is disabled |
| @@ -979,19 +170,18 @@ static void csi2_ctx_config(struct cal_ctx *ctx) · 2 unmodified lines | ||
| 979 | 170 | * 0x2A: RAW8 1 pixel = 1 byte |
| 980 | 171 | * 0x1E: YUV422 2 pixels = 4 bytes |
| 981 | 172 | */ |
| 982 | − set_field(&val, 0x1, CAL_CSI2_CTX_DT_MASK); | |
| 983 | − /* Virtual Channel from the CSI2 sensor usually 0! */ | |
| 984 | − set_field(&val, ctx->virtual_channel, CAL_CSI2_CTX_VC_MASK); | |
| 985 | − set_field(&val, ctx->v_fmt.fmt.pix.height, CAL_CSI2_CTX_LINES_MASK); | |
| 986 | − set_field(&val, CAL_CSI2_CTX_ATT_PIX, CAL_CSI2_CTX_ATT_MASK); | |
| 987 | − set_field(&val, CAL_CSI2_CTX_PACK_MODE_LINE, | |
| 988 | − CAL_CSI2_CTX_PACK_MODE_MASK); | |
| 989 | − reg_write(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port), val); | |
| 990 | − ctx_dbg(3, ctx, "CAL_CSI2_CTX0(%d) = 0x%08x\n", ctx->csi2_port, | |
| 991 | − reg_read(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port))); | |
| 173 | + cal_set_field(&val, 0x1, CAL_CSI2_CTX_DT_MASK); | |
| 174 | + cal_set_field(&val, 0, CAL_CSI2_CTX_VC_MASK); | |
| 175 | + cal_set_field(&val, ctx->v_fmt.fmt.pix.height, CAL_CSI2_CTX_LINES_MASK); | |
| 176 | + cal_set_field(&val, CAL_CSI2_CTX_ATT_PIX, CAL_CSI2_CTX_ATT_MASK); | |
| 177 | + cal_set_field(&val, CAL_CSI2_CTX_PACK_MODE_LINE, | |
| 178 | + CAL_CSI2_CTX_PACK_MODE_MASK); | |
| 179 | + cal_write(ctx->cal, CAL_CSI2_CTX0(ctx->index), val); | |
| 180 | + ctx_dbg(3, ctx, "CAL_CSI2_CTX0(%d) = 0x%08x\n", ctx->index, | |
| 181 | + cal_read(ctx->cal, CAL_CSI2_CTX0(ctx->index))); | |
| 992 | 182 | } |
| 993 | 183 | |
| 994 | −static void pix_proc_config(struct cal_ctx *ctx) | |
| 184 | +void cal_ctx_pix_proc_config(struct cal_ctx *ctx) | |
| 995 | 185 | { |
| 996 | 186 | u32 val, extract, pack; |
| 997 | 187 | |
| @@ -1022,7 +212,7 @@ static void pix_proc_config(struct cal_ctx *ctx) · 24 unmodified lines | ||
| 1022 | 212 | * |
| 1023 | 213 | * Instead of failing here just use 8 bpp as a default. |
| 1024 | 214 | */ |
| 1025 | − dev_warn_once(&ctx->dev->pdev->dev, | |
| 215 | + dev_warn_once(ctx->cal->dev, | |
| 1026 | 216 | "%s:%d:%s: bpp:%d unsupported! Overwritten with 8.\n", |
| 1027 | 217 | __FILE__, __LINE__, __func__, ctx->fmt->bpp); |
| 1028 | 218 | extract = CAL_PIX_PROC_EXTRACT_B8; |
| @@ -1030,145 +220,82 @@ static void pix_proc_config(struct cal_ctx *ctx) · 1 unmodified line | ||
| 1030 | 220 | break; |
| 1031 | 221 | } |
| 1032 | 222 | |
| 1033 | − val = reg_read(ctx->dev, CAL_PIX_PROC(ctx->csi2_port)); | |
| 1034 | − set_field(&val, extract, CAL_PIX_PROC_EXTRACT_MASK); | |
| 1035 | − set_field(&val, CAL_PIX_PROC_DPCMD_BYPASS, CAL_PIX_PROC_DPCMD_MASK); | |
| 1036 | − set_field(&val, CAL_PIX_PROC_DPCME_BYPASS, CAL_PIX_PROC_DPCME_MASK); | |
| 1037 | − set_field(&val, pack, CAL_PIX_PROC_PACK_MASK); | |
| 1038 | − set_field(&val, ctx->csi2_port, CAL_PIX_PROC_CPORT_MASK); | |
| 1039 | − set_field(&val, 1, CAL_PIX_PROC_EN_MASK); | |
| 1040 | − reg_write(ctx->dev, CAL_PIX_PROC(ctx->csi2_port), val); | |
| 1041 | − ctx_dbg(3, ctx, "CAL_PIX_PROC(%d) = 0x%08x\n", ctx->csi2_port, | |
| 1042 | − reg_read(ctx->dev, CAL_PIX_PROC(ctx->csi2_port))); | |
| 223 | + val = cal_read(ctx->cal, CAL_PIX_PROC(ctx->index)); | |
| 224 | + cal_set_field(&val, extract, CAL_PIX_PROC_EXTRACT_MASK); | |
| 225 | + cal_set_field(&val, CAL_PIX_PROC_DPCMD_BYPASS, CAL_PIX_PROC_DPCMD_MASK); | |
| 226 | + cal_set_field(&val, CAL_PIX_PROC_DPCME_BYPASS, CAL_PIX_PROC_DPCME_MASK); | |
| 227 | + cal_set_field(&val, pack, CAL_PIX_PROC_PACK_MASK); | |
| 228 | + cal_set_field(&val, ctx->cport, CAL_PIX_PROC_CPORT_MASK); | |
| 229 | + cal_set_field(&val, 1, CAL_PIX_PROC_EN_MASK); | |
| 230 | + cal_write(ctx->cal, CAL_PIX_PROC(ctx->index), val); | |
| 231 | + ctx_dbg(3, ctx, "CAL_PIX_PROC(%d) = 0x%08x\n", ctx->index, | |
| 232 | + cal_read(ctx->cal, CAL_PIX_PROC(ctx->index))); | |
| 1043 | 233 | } |
| 1044 | 234 | |
| 1045 | −static void cal_wr_dma_config(struct cal_ctx *ctx, | |
| 1046 | − unsigned int width, unsigned int height) | |
| 235 | +void cal_ctx_wr_dma_config(struct cal_ctx *ctx, unsigned int width, | |
| 236 | + unsigned int height) | |
| 1047 | 237 | { |
| 1048 | 238 | u32 val; |
| 1049 | 239 | |
| 1050 | − val = reg_read(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port)); | |
| 1051 | − set_field(&val, ctx->csi2_port, CAL_WR_DMA_CTRL_CPORT_MASK); | |
| 1052 | − set_field(&val, height, CAL_WR_DMA_CTRL_YSIZE_MASK); | |
| 1053 | − set_field(&val, CAL_WR_DMA_CTRL_DTAG_PIX_DAT, | |
| 1054 | − CAL_WR_DMA_CTRL_DTAG_MASK); | |
| 1055 | − set_field(&val, CAL_WR_DMA_CTRL_MODE_CONST, | |
| 1056 | − CAL_WR_DMA_CTRL_MODE_MASK); | |
| 1057 | − set_field(&val, CAL_WR_DMA_CTRL_PATTERN_LINEAR, | |
| 1058 | − CAL_WR_DMA_CTRL_PATTERN_MASK); | |
| 1059 | − set_field(&val, 1, CAL_WR_DMA_CTRL_STALL_RD_MASK); | |
| 1060 | − reg_write(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port), val); | |
| 1061 | − ctx_dbg(3, ctx, "CAL_WR_DMA_CTRL(%d) = 0x%08x\n", ctx->csi2_port, | |
| 1062 | − reg_read(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port))); | |
| 240 | + val = cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index)); | |
| 241 | + cal_set_field(&val, ctx->cport, CAL_WR_DMA_CTRL_CPORT_MASK); | |
| 242 | + cal_set_field(&val, height, CAL_WR_DMA_CTRL_YSIZE_MASK); | |
| 243 | + cal_set_field(&val, CAL_WR_DMA_CTRL_DTAG_PIX_DAT, | |
| 244 | + CAL_WR_DMA_CTRL_DTAG_MASK); | |
| 245 | + cal_set_field(&val, CAL_WR_DMA_CTRL_MODE_CONST, | |
| 246 | + CAL_WR_DMA_CTRL_MODE_MASK); | |
| 247 | + cal_set_field(&val, CAL_WR_DMA_CTRL_PATTERN_LINEAR, | |
| 248 | + CAL_WR_DMA_CTRL_PATTERN_MASK); | |
| 249 | + cal_set_field(&val, 1, CAL_WR_DMA_CTRL_STALL_RD_MASK); | |
| 250 | + cal_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->index), val); | |
| 251 | + ctx_dbg(3, ctx, "CAL_WR_DMA_CTRL(%d) = 0x%08x\n", ctx->index, | |
| 252 | + cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index))); | |
| 1063 | 253 | |
| 1064 | 254 | /* |
| 1065 | 255 | * width/16 not sure but giving it a whirl. |
| 1066 | 256 | * zero does not work right |
| 1067 | 257 | */ |
| 1068 | − reg_write_field(ctx->dev, | |
| 1069 | − CAL_WR_DMA_OFST(ctx->csi2_port), | |
| 258 | + cal_write_field(ctx->cal, | |
| 259 | + CAL_WR_DMA_OFST(ctx->index), | |
| 1070 | 260 | (width / 16), |
| 1071 | 261 | CAL_WR_DMA_OFST_MASK); |
| 1072 | − ctx_dbg(3, ctx, "CAL_WR_DMA_OFST(%d) = 0x%08x\n", ctx->csi2_port, | |
| 1073 | − reg_read(ctx->dev, CAL_WR_DMA_OFST(ctx->csi2_port))); | |
| 262 | + ctx_dbg(3, ctx, "CAL_WR_DMA_OFST(%d) = 0x%08x\n", ctx->index, | |
| 263 | + cal_read(ctx->cal, CAL_WR_DMA_OFST(ctx->index))); | |
| 1074 | 264 | |
| 1075 | − val = reg_read(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port)); | |
| 265 | + val = cal_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index)); | |
| 1076 | 266 | /* 64 bit word means no skipping */ |
| 1077 | − set_field(&val, 0, CAL_WR_DMA_XSIZE_XSKIP_MASK); | |
| 267 | + cal_set_field(&val, 0, CAL_WR_DMA_XSIZE_XSKIP_MASK); | |
| 1078 | 268 | /* |
| 1079 | 269 | * (width*8)/64 this should be size of an entire line |
| 1080 | 270 | * in 64bit word but 0 means all data until the end |
| 1081 | 271 | * is detected automagically |
| 1082 | 272 | */ |
| 1083 | − set_field(&val, (width / 8), CAL_WR_DMA_XSIZE_MASK); | |
| 1084 | − reg_write(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port), val); | |
| 1085 | − ctx_dbg(3, ctx, "CAL_WR_DMA_XSIZE(%d) = 0x%08x\n", ctx->csi2_port, | |
| 1086 | − reg_read(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port))); | |
| 1087 | − | |
| 1088 | − val = reg_read(ctx->dev, CAL_CTRL); | |
| 1089 | − set_field(&val, CAL_CTRL_BURSTSIZE_BURST128, CAL_CTRL_BURSTSIZE_MASK); | |
| 1090 | − set_field(&val, 0xF, CAL_CTRL_TAGCNT_MASK); | |
| 1091 | − set_field(&val, CAL_CTRL_POSTED_WRITES_NONPOSTED, | |
| 1092 | − CAL_CTRL_POSTED_WRITES_MASK); | |
| 1093 | − set_field(&val, 0xFF, CAL_CTRL_MFLAGL_MASK); | |
| 1094 | − set_field(&val, 0xFF, CAL_CTRL_MFLAGH_MASK); | |
| 1095 | − reg_write(ctx->dev, CAL_CTRL, val); | |
| 1096 | − ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", reg_read(ctx->dev, CAL_CTRL)); | |
| 1097 | −} | |
| 273 | + cal_set_field(&val, (width / 8), CAL_WR_DMA_XSIZE_MASK); | |
| 274 | + cal_write(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index), val); | |
| 275 | + ctx_dbg(3, ctx, "CAL_WR_DMA_XSIZE(%d) = 0x%08x\n", ctx->index, | |
| 276 | + cal_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index))); | |
| 1098 | 277 | |
| 1099 | −static void cal_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) | |
| 1100 | −{ | |
| 1101 | − reg_write(ctx->dev, CAL_WR_DMA_ADDR(ctx->csi2_port), dmaaddr); | |
| 278 | + val = cal_read(ctx->cal, CAL_CTRL); | |
| 279 | + cal_set_field(&val, CAL_CTRL_BURSTSIZE_BURST128, | |
| 280 | + CAL_CTRL_BURSTSIZE_MASK); | |
| 281 | + cal_set_field(&val, 0xF, CAL_CTRL_TAGCNT_MASK); | |
| 282 | + cal_set_field(&val, CAL_CTRL_POSTED_WRITES_NONPOSTED, | |
| 283 | + CAL_CTRL_POSTED_WRITES_MASK); | |
| 284 | + cal_set_field(&val, 0xFF, CAL_CTRL_MFLAGL_MASK); | |
| 285 | + cal_set_field(&val, 0xFF, CAL_CTRL_MFLAGH_MASK); | |
| 286 | + cal_write(ctx->cal, CAL_CTRL, val); | |
| 287 | + ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", cal_read(ctx->cal, CAL_CTRL)); | |
| 1102 | 288 | } |
| 1103 | 289 | |
| 1104 | −/* | |
| 1105 | − * TCLK values are OK at their reset values | |
| 1106 | − */ | |
| 1107 | −#define TCLK_TERM 0 | |
| 1108 | −#define TCLK_MISS 1 | |
| 1109 | −#define TCLK_SETTLE 14 | |
| 1110 | − | |
| 1111 | −static void csi2_phy_config(struct cal_ctx *ctx) | |
| 290 | +void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) | |
| 1112 | 291 | { |
| 1113 | − unsigned int reg0, reg1; | |
| 1114 | − unsigned int ths_term, ths_settle; | |
| 1115 | − unsigned int csi2_ddrclk_khz; | |
| 1116 | − struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = | |
| 1117 | − &ctx->endpoint.bus.mipi_csi2; | |
| 1118 | − u32 num_lanes = mipi_csi2->num_data_lanes; | |
| 1119 | − | |
| 1120 | − /* DPHY timing configuration */ | |
| 1121 | − /* CSI-2 is DDR and we only count used lanes. */ | |
| 1122 | − csi2_ddrclk_khz = ctx->external_rate / 1000 | |
| 1123 | − / (2 * num_lanes) * ctx->fmt->bpp; | |
| 1124 | − ctx_dbg(1, ctx, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz); | |
| 1125 | − | |
| 1126 | − /* THS_TERM: Programmed value = floor(20 ns/DDRClk period) */ | |
| 1127 | − ths_term = 20 * csi2_ddrclk_khz / 1000000; | |
| 1128 | − ctx_dbg(1, ctx, "ths_term: %d (0x%02x)\n", ths_term, ths_term); | |
| 1129 | − | |
| 1130 | − /* THS_SETTLE: Programmed value = floor(105 ns/DDRClk period) + 4 */ | |
| 1131 | − ths_settle = (105 * csi2_ddrclk_khz / 1000000) + 4; | |
| 1132 | − ctx_dbg(1, ctx, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle); | |
| 1133 | − | |
| 1134 | − reg0 = reg_read(ctx->cc, CAL_CSI2_PHY_REG0); | |
| 1135 | − set_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE, | |
| 1136 | − CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK); | |
| 1137 | − set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK); | |
| 1138 | − set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK); | |
| 1139 | − | |
| 1140 | − ctx_dbg(1, ctx, "CSI2_%d_REG0 = 0x%08x\n", (ctx->csi2_port - 1), reg0); | |
| 1141 | − reg_write(ctx->cc, CAL_CSI2_PHY_REG0, reg0); | |
| 1142 | − | |
| 1143 | − reg1 = reg_read(ctx->cc, CAL_CSI2_PHY_REG1); | |
| 1144 | − set_field(®1, TCLK_TERM, CAL_CSI2_PHY_REG1_TCLK_TERM_MASK); | |
| 1145 | − set_field(®1, 0xb8, CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK); | |
| 1146 | − set_field(®1, TCLK_MISS, CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK); | |
| 1147 | − set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK); | |
| 1148 | − | |
| 1149 | − ctx_dbg(1, ctx, "CSI2_%d_REG1 = 0x%08x\n", (ctx->csi2_port - 1), reg1); | |
| 1150 | − reg_write(ctx->cc, CAL_CSI2_PHY_REG1, reg1); | |
| 292 | + cal_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->index), dmaaddr); | |
| 1151 | 293 | } |
| 1152 | 294 | |
| 1153 | −static int cal_get_external_info(struct cal_ctx *ctx) | |
| 1154 | −{ | |
| 1155 | − struct v4l2_ctrl *ctrl; | |
| 1156 | − | |
| 1157 | − if (!ctx->sensor) | |
| 1158 | − return -ENODEV; | |
| 1159 | − | |
| 1160 | − ctrl = v4l2_ctrl_find(ctx->sensor->ctrl_handler, V4L2_CID_PIXEL_RATE); | |
| 1161 | − if (!ctrl) { | |
| 1162 | − ctx_err(ctx, "no pixel rate control in subdev: %s\n", | |
| 1163 | − ctx->sensor->name); | |
| 1164 | − return -EPIPE; | |
| 1165 | − } | |
| 1166 | − | |
| 1167 | − ctx->external_rate = v4l2_ctrl_g_ctrl_int64(ctrl); | |
| 1168 | − ctx_dbg(3, ctx, "sensor Pixel Rate: %d\n", ctx->external_rate); | |
| 1169 | − | |
| 1170 | − return 0; | |
| 1171 | −} | |
| 295 | +/* ------------------------------------------------------------------ | |
| 296 | + * IRQ Handling | |
| 297 | + * ------------------------------------------------------------------ | |
| 298 | + */ | |
| 1172 | 299 | |
| 1173 | 300 | static inline void cal_schedule_next_buffer(struct cal_ctx *ctx) |
| 1174 | 301 | { |
| @@ -1181,7 +308,7 @@ static inline void cal_schedule_next_buffer(struct cal_ctx *ctx) · 6 unmodified lines | ||
| 1181 | 308 | list_del(&buf->list); |
| 1182 | 309 | |
| 1183 | 310 | addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); |
| 1184 | − cal_wr_dma_addr(ctx, addr); | |
| 311 | + cal_ctx_wr_dma_addr(ctx, addr); | |
| 1185 | 312 | } |
| 1186 | 313 | |
| 1187 | 314 | 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) · 6 unmodified lines | ||
| 1194 | 321 | ctx->cur_frm = ctx->next_frm; |
| 1195 | 322 | } |
| 1196 | 323 | |
| 1197 | −#define isvcirqset(irq, vc, ff) (irq & \ | |
| 1198 | − (CAL_CSI2_VC_IRQENABLE_ ##ff ##_IRQ_##vc ##_MASK)) | |
| 1199 | − | |
| 1200 | −#define isportirqset(irq, port) (irq & CAL_HL_IRQ_MASK(port)) | |
| 1201 | − | |
| 1202 | 324 | static irqreturn_t cal_irq(int irq_cal, void *data) |
| 1203 | 325 | { |
| 1204 | − struct cal_dev *dev = (struct cal_dev *)data; | |
| 326 | + struct cal_dev *cal = data; | |
| 1205 | 327 | struct cal_ctx *ctx; |
| 1206 | 328 | struct cal_dmaqueue *dma_q; |
| 1207 | − u32 irqst1, irqst2, irqst3; | |
| 329 | + u32 status; | |
| 1208 | 330 | |
| 1209 | − irqst1 = reg_read(dev, CAL_HL_IRQSTATUS(1)); | |
| 1210 | − if (irqst1) { | |
| 1211 | − int i; | |
| 331 | + status = cal_read(cal, CAL_HL_IRQSTATUS(0)); | |
| 332 | + if (status) { | |
| 333 | + unsigned int i; | |
| 1212 | 334 | |
| 1213 | − reg_write(dev, CAL_HL_IRQSTATUS(1), irqst1); | |
| 335 | + cal_write(cal, CAL_HL_IRQSTATUS(0), status); | |
| 1214 | 336 | |
| 1215 | − if (irqst1 & CAL_HL_IRQ_OCPO_ERR_MASK) | |
| 1216 | − dev_err_ratelimited(&dev->pdev->dev, "OCPO ERROR\n"); | |
| 337 | + if (status & CAL_HL_IRQ_OCPO_ERR_MASK) | |
| 338 | + dev_err_ratelimited(cal->dev, "OCPO ERROR\n"); | |
| 1217 | 339 | |
| 1218 | − for (i = 1; i <= 2; ++i) { | |
| 1219 | − if (irqst1 & CAL_HL_IRQ_CIO_MASK(i)) { | |
| 1220 | − u32 cio_stat = reg_read(dev, | |
| 340 | + for (i = 0; i < CAL_NUM_CSI2_PORTS; ++i) { | |
| 341 | + if (status & CAL_HL_IRQ_CIO_MASK(i)) { | |
| 342 | + u32 cio_stat = cal_read(cal, | |
| 1221 | 343 | CAL_CSI2_COMPLEXIO_IRQSTATUS(i)); |
| 1222 | 344 | |
| 1223 | − dev_err_ratelimited(&dev->pdev->dev, | |
| 1224 | − "CIO%d error: %#08x\n", i, cio_stat); | |
| 345 | + dev_err_ratelimited(cal->dev, | |
| 346 | + "CIO%u error: %#08x\n", i, cio_stat); | |
| 1225 | 347 | |
| 1226 | − reg_write(dev, CAL_CSI2_COMPLEXIO_IRQSTATUS(i), | |
| 348 | + cal_write(cal, CAL_CSI2_COMPLEXIO_IRQSTATUS(i), | |
| 1227 | 349 | cio_stat); |
| 1228 | 350 | } |
| 1229 | 351 | } |
| 1230 | 352 | } |
| 1231 | 353 | |
| 1232 | 354 | /* Check which DMA just finished */ |
| 1233 | − irqst2 = reg_read(dev, CAL_HL_IRQSTATUS(2)); | |
| 1234 | − if (irqst2) { | |
| 1235 | − int i; | |
| 355 | + status = cal_read(cal, CAL_HL_IRQSTATUS(1)); | |
| 356 | + if (status) { | |
| 357 | + unsigned int i; | |
| 1236 | 358 | |
| 1237 | 359 | /* Clear Interrupt status */ |
| 1238 | − reg_write(dev, CAL_HL_IRQSTATUS(2), irqst2); | |
| 360 | + cal_write(cal, CAL_HL_IRQSTATUS(1), status); | |
| 1239 | 361 | |
| 1240 | − for (i = 1; i <= 2; ++i) { | |
| 1241 | − if (isportirqset(irqst2, i)) { | |
| 1242 | − ctx = dev->ctx[i - 1]; | |
| 362 | + for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { | |
| 363 | + if (status & CAL_HL_IRQ_MASK(i)) { | |
| 364 | + ctx = cal->ctx[i]; | |
| 1243 | 365 | |
| 1244 | 366 | spin_lock(&ctx->slock); |
| 1245 | 367 | ctx->dma_act = false; |
| @@ -1253,16 +375,16 @@ static irqreturn_t cal_irq(int irq_cal, void *data) · 7 unmodified lines | ||
| 1253 | 375 | } |
| 1254 | 376 | |
| 1255 | 377 | /* Check which DMA just started */ |
| 1256 | − irqst3 = reg_read(dev, CAL_HL_IRQSTATUS(3)); | |
| 1257 | − if (irqst3) { | |
| 1258 | − int i; | |
| 378 | + status = cal_read(cal, CAL_HL_IRQSTATUS(2)); | |
| 379 | + if (status) { | |
| 380 | + unsigned int i; | |
| 1259 | 381 | |
| 1260 | 382 | /* Clear Interrupt status */ |
| 1261 | − reg_write(dev, CAL_HL_IRQSTATUS(3), irqst3); | |
| 383 | + cal_write(cal, CAL_HL_IRQSTATUS(2), status); | |
| 1262 | 384 | |
| 1263 | − for (i = 1; i <= 2; ++i) { | |
| 1264 | − if (isportirqset(irqst3, i)) { | |
| 1265 | − ctx = dev->ctx[i - 1]; | |
| 385 | + for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { | |
| 386 | + if (status & CAL_HL_IRQ_MASK(i)) { | |
| 387 | + ctx = cal->ctx[i]; | |
| 1266 | 388 | dma_q = &ctx->vidq; |
| 1267 | 389 | |
| 1268 | 390 | spin_lock(&ctx->slock); |
| @@ -1278,963 +400,234 @@ static irqreturn_t cal_irq(int irq_cal, void *data) · 9 unmodified lines | ||
| 1278 | 400 | return IRQ_HANDLED; |
| 1279 | 401 | } |
| 1280 | 402 | |
| 1281 | −/* | |
| 1282 | − * video ioctls | |
| 403 | +/* ------------------------------------------------------------------ | |
| 404 | + * Asynchronous V4L2 subdev binding | |
| 405 | + * ------------------------------------------------------------------ | |
| 1283 | 406 | */ |
| 1284 | −static int cal_querycap(struct file *file, void *priv, | |
| 1285 | − struct v4l2_capability *cap) | |
| 1286 | −{ | |
| 1287 | − struct cal_ctx *ctx = video_drvdata(file); | |
| 1288 | 407 | |
| 1289 | − strscpy(cap->driver, CAL_MODULE_NAME, sizeof(cap->driver)); | |
| 1290 | − strscpy(cap->card, CAL_MODULE_NAME, sizeof(cap->card)); | |
| 408 | +struct cal_v4l2_async_subdev { | |
| 409 | + struct v4l2_async_subdev asd; | |
| 410 | + struct cal_camerarx *phy; | |
| 411 | +}; | |
| 1291 | 412 | |
| 1292 | − snprintf(cap->bus_info, sizeof(cap->bus_info), | |
| 1293 | − "platform:%s", ctx->v4l2_dev.name); | |
| 1294 | − return 0; | |
| 413 | +static inline struct cal_v4l2_async_subdev * | |
| 414 | +to_cal_asd(struct v4l2_async_subdev *asd) | |
| 415 | +{ | |
| 416 | + return container_of(asd, struct cal_v4l2_async_subdev, asd); | |
| 1295 | 417 | } |
| 1296 | 418 | |
| 1297 | −static int cal_enum_fmt_vid_cap(struct file *file, void *priv, | |
| 1298 | − struct v4l2_fmtdesc *f) | |
| 419 | +static int cal_async_notifier_bound(struct v4l2_async_notifier *notifier, | |
| 420 | + struct v4l2_subdev *subdev, | |
| 421 | + struct v4l2_async_subdev *asd) | |
| 1299 | 422 | { |
| 1300 | − struct cal_ctx *ctx = video_drvdata(file); | |
| 1301 | − const struct cal_fmt *fmt = NULL; | |
| 1302 | − | |
| 1303 | − if (f->index >= ctx->num_active_fmt) | |
| 1304 | − return -EINVAL; | |
| 1305 | − | |
| 1306 | − fmt = ctx->active_fmt[f->index]; | |
| 423 | + struct cal_camerarx *phy = to_cal_asd(asd)->phy; | |
| 1307 | 424 | |
| 1308 | − f->pixelformat = fmt->fourcc; | |
| 1309 | − f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | |
| 1310 | − return 0; | |
| 1311 | −} | |
| 1312 | − | |
| 1313 | −static int __subdev_get_format(struct cal_ctx *ctx, | |
| 1314 | − struct v4l2_mbus_framefmt *fmt) | |
| 1315 | −{ | |
| 1316 | − struct v4l2_subdev_format sd_fmt; | |
| 1317 | − struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; | |
| 1318 | − int ret; | |
| 1319 | − | |
| 1320 | − sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; | |
| 1321 | − sd_fmt.pad = 0; | |
| 1322 | − | |
| 1323 | − ret = v4l2_subdev_call(ctx->sensor, pad, get_fmt, NULL, &sd_fmt); | |
| 1324 | − if (ret) | |
| 1325 | − return ret; | |
| 1326 | − | |
| 1327 | − *fmt = *mbus_fmt; | |
| 1328 | − | |
| 1329 | − ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__, | |
| 1330 | − fmt->width, fmt->height, fmt->code); | |
| 1331 | − | |
| 1332 | − return 0; | |
| 1333 | −} | |
| 1334 | − | |
| 1335 | −static int __subdev_set_format(struct cal_ctx *ctx, | |
| 1336 | − struct v4l2_mbus_framefmt *fmt) | |
| 1337 | −{ | |
| 1338 | − struct v4l2_subdev_format sd_fmt; | |
| 1339 | − struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; | |
| 1340 | − int ret; | |
| 1341 | − | |
| 1342 | − sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; | |
| 1343 | − sd_fmt.pad = 0; | |
| 1344 | − *mbus_fmt = *fmt; | |
| 1345 | − | |
| 1346 | − ret = v4l2_subdev_call(ctx->sensor, pad, set_fmt, NULL, &sd_fmt); | |
| 1347 | − if (ret) | |
| 1348 | − return ret; | |
| 1349 | − | |
| 1350 | − ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__, | |
| 1351 | − fmt->width, fmt->height, fmt->code); | |
| 1352 | − | |
| 1353 | − return 0; | |
| 1354 | −} | |
| 1355 | − | |
| 1356 | −static int cal_calc_format_size(struct cal_ctx *ctx, | |
| 1357 | − const struct cal_fmt *fmt, | |
| 1358 | − struct v4l2_format *f) | |
| 1359 | −{ | |
| 1360 | − u32 bpl, max_width; | |
| 1361 | − | |
| 1362 | − if (!fmt) { | |
| 1363 | − ctx_dbg(3, ctx, "No cal_fmt provided!\n"); | |
| 1364 | − return -EINVAL; | |
| 1365 | − } | |
| 1366 | − | |
| 1367 | − /* | |
| 1368 | − * Maximum width is bound by the DMA max width in bytes. | |
| 1369 | − * We need to recalculate the actual maxi width depending on the | |
| 1370 | − * number of bytes per pixels required. | |
| 1371 | − */ | |
| 1372 | − max_width = MAX_WIDTH_BYTES / (ALIGN(fmt->bpp, 8) >> 3); | |
| 1373 | − v4l_bound_align_image(&f->fmt.pix.width, 48, max_width, 2, | |
| 1374 | − &f->fmt.pix.height, 32, MAX_HEIGHT_LINES, 0, 0); | |
| 1375 | − | |
| 1376 | − bpl = (f->fmt.pix.width * ALIGN(fmt->bpp, 8)) >> 3; | |
| 1377 | − f->fmt.pix.bytesperline = ALIGN(bpl, 16); | |
| 1378 | − | |
| 1379 | − f->fmt.pix.sizeimage = f->fmt.pix.height * | |
| 1380 | − f->fmt.pix.bytesperline; | |
| 1381 | − | |
| 1382 | − ctx_dbg(3, ctx, "%s: fourcc: %s size: %dx%d bpl:%d img_size:%d\n", | |
| 1383 | − __func__, fourcc_to_str(f->fmt.pix.pixelformat), | |
| 1384 | − f->fmt.pix.width, f->fmt.pix.height, | |
| 1385 | − f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); | |
| 1386 | − | |
| 1387 | − return 0; | |
| 1388 | −} | |
| 1389 | − | |
| 1390 | −static int cal_g_fmt_vid_cap(struct file *file, void *priv, | |
| 1391 | − struct v4l2_format *f) | |
| 1392 | −{ | |
| 1393 | − struct cal_ctx *ctx = video_drvdata(file); | |
| 1394 | − | |
| 1395 | − *f = ctx->v_fmt; | |
| 1396 | − | |
| 1397 | − return 0; | |
| 1398 | −} | |
| 1399 | − | |
| 1400 | −static int cal_try_fmt_vid_cap(struct file *file, void *priv, | |
| 1401 | − struct v4l2_format *f) | |
| 1402 | −{ | |
| 1403 | − struct cal_ctx *ctx = video_drvdata(file); | |
| 1404 | − const struct cal_fmt *fmt; | |
| 1405 | − struct v4l2_subdev_frame_size_enum fse; | |
| 1406 | − int ret, found; | |
| 1407 | − | |
| 1408 | − fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat); | |
| 1409 | − if (!fmt) { | |
| 1410 | − ctx_dbg(3, ctx, "Fourcc format (0x%08x) not found.\n", | |
| 1411 | − f->fmt.pix.pixelformat); | |
| 1412 | − | |
| 1413 | − /* Just get the first one enumerated */ | |
| 1414 | − fmt = ctx->active_fmt[0]; | |
| 1415 | − f->fmt.pix.pixelformat = fmt->fourcc; | |
| 1416 | − } | |
| 1417 | − | |
| 1418 | − f->fmt.pix.field = ctx->v_fmt.fmt.pix.field; | |
| 1419 | − | |
| 1420 | − /* check for/find a valid width/height */ | |
| 1421 | − ret = 0; | |
| 1422 | − found = false; | |
| 1423 | − fse.pad = 0; | |
| 1424 | − fse.code = fmt->code; | |
| 1425 | − fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; | |
| 1426 | − for (fse.index = 0; ; fse.index++) { | |
| 1427 | − ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_size, | |
| 1428 | − NULL, &fse); | |
| 1429 | − if (ret) | |
| 1430 | − break; | |
| 1431 | − | |
| 1432 | − if ((f->fmt.pix.width == fse.max_width) && | |
| 1433 | − (f->fmt.pix.height == fse.max_height)) { | |
| 1434 | − found = true; | |
| 1435 | − break; | |
| 1436 | − } else if ((f->fmt.pix.width >= fse.min_width) && | |
| 1437 | − (f->fmt.pix.width <= fse.max_width) && | |
| 1438 | − (f->fmt.pix.height >= fse.min_height) && | |
| 1439 | − (f->fmt.pix.height <= fse.max_height)) { | |
| 1440 | − found = true; | |
| 1441 | − break; | |
| 1442 | − } | |
| 1443 | − } | |
| 1444 | − | |
| 1445 | − if (!found) { | |
| 1446 | − /* use existing values as default */ | |
| 1447 | − f->fmt.pix.width = ctx->v_fmt.fmt.pix.width; | |
| 1448 | − f->fmt.pix.height = ctx->v_fmt.fmt.pix.height; | |
| 1449 | − } | |
| 1450 | − | |
| 1451 | − /* | |
| 1452 | − * Use current colorspace for now, it will get | |
| 1453 | − * updated properly during s_fmt | |
| 1454 | − */ | |
| 1455 | − f->fmt.pix.colorspace = ctx->v_fmt.fmt.pix.colorspace; | |
| 1456 | − return cal_calc_format_size(ctx, fmt, f); | |
| 1457 | −} | |
| 1458 | − | |
| 1459 | −static int cal_s_fmt_vid_cap(struct file *file, void *priv, | |
| 1460 | − struct v4l2_format *f) | |
| 1461 | −{ | |
| 1462 | − struct cal_ctx *ctx = video_drvdata(file); | |
| 1463 | − struct vb2_queue *q = &ctx->vb_vidq; | |
| 1464 | − const struct cal_fmt *fmt; | |
| 1465 | − struct v4l2_mbus_framefmt mbus_fmt; | |
| 1466 | − int ret; | |
| 1467 | − | |
| 1468 | − if (vb2_is_busy(q)) { | |
| 1469 | − ctx_dbg(3, ctx, "%s device busy\n", __func__); | |
| 1470 | − return -EBUSY; | |
| 1471 | − } | |
| 1472 | − | |
| 1473 | − ret = cal_try_fmt_vid_cap(file, priv, f); | |
| 1474 | − if (ret < 0) | |
| 1475 | − return ret; | |
| 1476 | − | |
| 1477 | − fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat); | |
| 1478 | − | |
| 1479 | − v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code); | |
| 1480 | − | |
| 1481 | − ret = __subdev_set_format(ctx, &mbus_fmt); | |
| 1482 | − if (ret) | |
| 1483 | − return ret; | |
| 1484 | − | |
| 1485 | − /* Just double check nothing has gone wrong */ | |
| 1486 | − if (mbus_fmt.code != fmt->code) { | |
| 1487 | − ctx_dbg(3, ctx, | |
| 1488 | − "%s subdev changed format on us, this should not happen\n", | |
| 1489 | − __func__); | |
| 1490 | − return -EINVAL; | |
| 1491 | − } | |
| 1492 | − | |
| 1493 | − v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); | |
| 1494 | − ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | |
| 1495 | − ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; | |
| 1496 | − cal_calc_format_size(ctx, fmt, &ctx->v_fmt); | |
| 1497 | − ctx->fmt = fmt; | |
| 1498 | − ctx->m_fmt = mbus_fmt; | |
| 1499 | − *f = ctx->v_fmt; | |
| 1500 | − | |
| 1501 | − return 0; | |
| 1502 | −} | |
| 1503 | − | |
| 1504 | −static int cal_enum_framesizes(struct file *file, void *fh, | |
| 1505 | − struct v4l2_frmsizeenum *fsize) | |
| 1506 | −{ | |
| 1507 | − struct cal_ctx *ctx = video_drvdata(file); | |
| 1508 | − const struct cal_fmt *fmt; | |
| 1509 | − struct v4l2_subdev_frame_size_enum fse; | |
| 1510 | − int ret; | |
| 1511 | − | |
| 1512 | − /* check for valid format */ | |
| 1513 | − fmt = find_format_by_pix(ctx, fsize->pixel_format); | |
| 1514 | − if (!fmt) { | |
| 1515 | − ctx_dbg(3, ctx, "Invalid pixel code: %x\n", | |
| 1516 | − fsize->pixel_format); | |
| 1517 | − return -EINVAL; | |
| 425 | + if (phy->sensor) { | |
| 426 | + phy_info(phy, "Rejecting subdev %s (Already set!!)", | |
| 427 | + subdev->name); | |
| 428 | + return 0; | |
| 1518 | 429 | } |
| 1519 | 430 | |
| 1520 | − fse.index = fsize->index; | |
| 1521 | − fse.pad = 0; | |
| 1522 | − fse.code = fmt->code; | |
| 1523 | − fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; | |
| 1524 | − | |
| 1525 | − ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_size, NULL, &fse); | |
| 1526 | − if (ret) | |
| 1527 | − return ret; | |
| 1528 | − | |
| 1529 | − ctx_dbg(1, ctx, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n", | |
| 1530 | − __func__, fse.index, fse.code, fse.min_width, fse.max_width, | |
| 1531 | − fse.min_height, fse.max_height); | |
| 1532 | − | |
| 1533 | − fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; | |
| 1534 | − fsize->discrete.width = fse.max_width; | |
| 1535 | − fsize->discrete.height = fse.max_height; | |
| 1536 | − | |
| 1537 | − return 0; | |
| 1538 | −} | |
| 1539 | − | |
| 1540 | −static int cal_enum_input(struct file *file, void *priv, | |
| 1541 | − struct v4l2_input *inp) | |
| 1542 | −{ | |
| 1543 | − if (inp->index >= CAL_NUM_INPUT) | |
| 1544 | − return -EINVAL; | |
| 1545 | − | |
| 1546 | − inp->type = V4L2_INPUT_TYPE_CAMERA; | |
| 1547 | − sprintf(inp->name, "Camera %u", inp->index); | |
| 1548 | − return 0; | |
| 1549 | −} | |
| 1550 | − | |
| 1551 | −static int cal_g_input(struct file *file, void *priv, unsigned int *i) | |
| 1552 | −{ | |
| 1553 | − struct cal_ctx *ctx = video_drvdata(file); | |
| 1554 | − | |
| 1555 | − *i = ctx->input; | |
| 1556 | − return 0; | |
| 1557 | −} | |
| 1558 | − | |
| 1559 | −static int cal_s_input(struct file *file, void *priv, unsigned int i) | |
| 1560 | −{ | |
| 1561 | − struct cal_ctx *ctx = video_drvdata(file); | |
| 1562 | − | |
| 1563 | − if (i >= CAL_NUM_INPUT) | |
| 1564 | − return -EINVAL; | |
| 1565 | − | |
| 1566 | − ctx->input = i; | |
| 1567 | − return 0; | |
| 1568 | −} | |
| 1569 | − | |
| 1570 | −/* timeperframe is arbitrary and continuous */ | |
| 1571 | −static int cal_enum_frameintervals(struct file *file, void *priv, | |
| 1572 | − struct v4l2_frmivalenum *fival) | |
| 1573 | −{ | |
| 1574 | − struct cal_ctx *ctx = video_drvdata(file); | |
| 1575 | − const struct cal_fmt *fmt; | |
| 1576 | − struct v4l2_subdev_frame_interval_enum fie = { | |
| 1577 | − .index = fival->index, | |
| 1578 | − .width = fival->width, | |
| 1579 | − .height = fival->height, | |
| 1580 | − .which = V4L2_SUBDEV_FORMAT_ACTIVE, | |
| 1581 | − }; | |
| 1582 | − int ret; | |
| 1583 | − | |
| 1584 | − fmt = find_format_by_pix(ctx, fival->pixel_format); | |
| 1585 | − if (!fmt) | |
| 1586 | − return -EINVAL; | |
| 1587 | − | |
| 1588 | − fie.code = fmt->code; | |
| 1589 | − ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_interval, | |
| 1590 | − NULL, &fie); | |
| 1591 | − if (ret) | |
| 1592 | − return ret; | |
| 1593 | − fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; | |
| 1594 | − fival->discrete = fie.interval; | |
| 431 | + phy->sensor = subdev; | |
| 432 | + phy_dbg(1, phy, "Using sensor %s for capture\n", subdev->name); | |
| 1595 | 433 | |
| 1596 | 434 | return 0; |
| 1597 | 435 | } |
| 1598 | 436 | |
| 1599 | −/* | |
| 1600 | − * Videobuf operations | |
| 1601 | − */ | |
| 1602 | −static int cal_queue_setup(struct vb2_queue *vq, | |
| 1603 | − unsigned int *nbuffers, unsigned int *nplanes, | |
| 1604 | − unsigned int sizes[], struct device *alloc_devs[]) | |
| 437 | +static int cal_async_notifier_complete(struct v4l2_async_notifier *notifier) | |
| 1605 | 438 | { |
| 1606 | − struct cal_ctx *ctx = vb2_get_drv_priv(vq); | |
| 1607 | − unsigned size = ctx->v_fmt.fmt.pix.sizeimage; | |
| 1608 | − | |
| 1609 | − if (vq->num_buffers + *nbuffers < 3) | |
| 1610 | − *nbuffers = 3 - vq->num_buffers; | |
| 1611 | − | |
| 1612 | − if (*nplanes) { | |
| 1613 | − if (sizes[0] < size) | |
| 1614 | − return -EINVAL; | |
| 1615 | − size = sizes[0]; | |
| 1616 | − } | |
| 1617 | − | |
| 1618 | − *nplanes = 1; | |
| 1619 | − sizes[0] = size; | |
| 1620 | − | |
| 1621 | − ctx_dbg(3, ctx, "nbuffers=%d, size=%d\n", *nbuffers, sizes[0]); | |
| 1622 | − | |
| 1623 | − return 0; | |
| 1624 | −} | |
| 439 | + struct cal_dev *cal = container_of(notifier, struct cal_dev, notifier); | |
| 440 | + unsigned int i; | |
| 1625 | 441 | |
| 1626 | −static int cal_buffer_prepare(struct vb2_buffer *vb) | |
| 1627 | −{ | |
| 1628 | − struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); | |
| 1629 | − struct cal_buffer *buf = container_of(vb, struct cal_buffer, | |
| 1630 | − vb.vb2_buf); | |
| 1631 | − unsigned long size; | |
| 1632 | − | |
| 1633 | − if (WARN_ON(!ctx->fmt)) | |
| 1634 | − return -EINVAL; | |
| 1635 | − | |
| 1636 | − size = ctx->v_fmt.fmt.pix.sizeimage; | |
| 1637 | − if (vb2_plane_size(vb, 0) < size) { | |
| 1638 | − ctx_err(ctx, | |
| 1639 | − "data will not fit into plane (%lu < %lu)\n", | |
| 1640 | − vb2_plane_size(vb, 0), size); | |
| 1641 | − return -EINVAL; | |
| 442 | + for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { | |
| 443 | + if (cal->ctx[i]) | |
| 444 | + cal_ctx_v4l2_register(cal->ctx[i]); | |
| 1642 | 445 | } |
| 1643 | 446 | |
| 1644 | − vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); | |
| 1645 | 447 | return 0; |
| 1646 | 448 | } |
| 1647 | 449 | |
| 1648 | −static void cal_buffer_queue(struct vb2_buffer *vb) | |
| 1649 | −{ | |
| 1650 | − struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); | |
| 1651 | − struct cal_buffer *buf = container_of(vb, struct cal_buffer, | |
| 1652 | − vb.vb2_buf); | |
| 1653 | − struct cal_dmaqueue *vidq = &ctx->vidq; | |
| 1654 | − unsigned long flags = 0; | |
| 1655 | − | |
| 1656 | − /* recheck locking */ | |
| 1657 | − spin_lock_irqsave(&ctx->slock, flags); | |
| 1658 | − list_add_tail(&buf->list, &vidq->active); | |
| 1659 | − spin_unlock_irqrestore(&ctx->slock, flags); | |
| 1660 | −} | |
| 450 | +static const struct v4l2_async_notifier_operations cal_async_notifier_ops = { | |
| 451 | + .bound = cal_async_notifier_bound, | |
| 452 | + .complete = cal_async_notifier_complete, | |
| 453 | +}; | |
| 1661 | 454 | |
| 1662 | −static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) | |
| 455 | +static int cal_async_notifier_register(struct cal_dev *cal) | |
| 1663 | 456 | { |
| 1664 | − struct cal_ctx *ctx = vb2_get_drv_priv(vq); | |
| 1665 | − struct cal_dmaqueue *dma_q = &ctx->vidq; | |
| 1666 | − struct cal_buffer *buf, *tmp; | |
| 1667 | − unsigned long addr = 0; | |
| 1668 | − unsigned long flags; | |
| 457 | + unsigned int i; | |
| 1669 | 458 | int ret; |
| 1670 | 459 | |
| 1671 | − spin_lock_irqsave(&ctx->slock, flags); | |
| 1672 | − if (list_empty(&dma_q->active)) { | |
| 1673 | − spin_unlock_irqrestore(&ctx->slock, flags); | |
| 1674 | − ctx_dbg(3, ctx, "buffer queue is empty\n"); | |
| 1675 | − return -EIO; | |
| 1676 | − } | |
| 460 | + v4l2_async_notifier_init(&cal->notifier); | |
| 461 | + cal->notifier.ops = &cal_async_notifier_ops; | |
| 1677 | 462 | |
| 1678 | − buf = list_entry(dma_q->active.next, struct cal_buffer, list); | |
| 1679 | − ctx->cur_frm = buf; | |
| 1680 | − ctx->next_frm = buf; | |
| 1681 | − list_del(&buf->list); | |
| 1682 | − spin_unlock_irqrestore(&ctx->slock, flags); | |
| 463 | + for (i = 0; i < ARRAY_SIZE(cal->phy); ++i) { | |
| 464 | + struct cal_camerarx *phy = cal->phy[i]; | |
| 465 | + struct cal_v4l2_async_subdev *casd; | |
| 466 | + struct v4l2_async_subdev *asd; | |
| 467 | + struct fwnode_handle *fwnode; | |
| 1683 | 468 | |
| 1684 | − addr = vb2_dma_contig_plane_dma_addr(&ctx->cur_frm->vb.vb2_buf, 0); | |
| 1685 | − ctx->sequence = 0; | |
| 469 | + if (!phy || !phy->sensor_node) | |
| 470 | + continue; | |
| 1686 | 471 | |
| 1687 | − ret = cal_get_external_info(ctx); | |
| 1688 | − if (ret < 0) | |
| 1689 | − goto err; | |
| 472 | + fwnode = of_fwnode_handle(phy->sensor_node); | |
| 473 | + asd = v4l2_async_notifier_add_fwnode_subdev(&cal->notifier, | |
| 474 | + fwnode, | |
| 475 | + sizeof(*asd)); | |
| 476 | + if (IS_ERR(asd)) { | |
| 477 | + phy_err(phy, "Failed to add subdev to notifier\n"); | |
| 478 | + ret = PTR_ERR(asd); | |
| 479 | + goto error; | |
| 480 | + } | |
| 1690 | 481 | |
| 1691 | − ret = v4l2_subdev_call(ctx->sensor, core, s_power, 1); | |
| 1692 | − if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) { | |
| 1693 | − ctx_err(ctx, "power on failed in subdev\n"); | |
| 1694 | − goto err; | |
| 482 | + casd = to_cal_asd(asd); | |
| 483 | + casd->phy = phy; | |
| 1695 | 484 | } |
| 1696 | 485 | |
| 1697 | − pm_runtime_get_sync(&ctx->dev->pdev->dev); | |
| 1698 | − | |
| 1699 | − csi2_ctx_config(ctx); | |
| 1700 | − pix_proc_config(ctx); | |
| 1701 | − cal_wr_dma_config(ctx, ctx->v_fmt.fmt.pix.bytesperline, | |
| 1702 | − ctx->v_fmt.fmt.pix.height); | |
| 1703 | − csi2_lane_config(ctx); | |
| 1704 | − | |
| 1705 | − enable_irqs(ctx); | |
| 1706 | − csi2_phy_init(ctx); | |
| 1707 | − | |
| 1708 | − ret = v4l2_subdev_call(ctx->sensor, video, s_stream, 1); | |
| 486 | + ret = v4l2_async_notifier_register(&cal->v4l2_dev, &cal->notifier); | |
| 1709 | 487 | if (ret) { |
| 1710 | − v4l2_subdev_call(ctx->sensor, core, s_power, 0); | |
| 1711 | − ctx_err(ctx, "stream on failed in subdev\n"); | |
| 1712 | − pm_runtime_put_sync(&ctx->dev->pdev->dev); | |
| 1713 | − goto err; | |
| 488 | + cal_err(cal, "Error registering async notifier\n"); | |
| 489 | + goto error; | |
| 1714 | 490 | } |
| 1715 | 491 | |
| 1716 | − csi2_wait_for_phy(ctx); | |
| 1717 | − cal_wr_dma_addr(ctx, addr); | |
| 1718 | − csi2_ppi_enable(ctx); | |
| 1719 | − | |
| 1720 | − if (debug >= 4) | |
| 1721 | − cal_quickdump_regs(ctx->dev); | |
| 1722 | − | |
| 1723 | 492 | return 0; |
| 1724 | 493 | |
| 1725 | −err: | |
| 1726 | − spin_lock_irqsave(&ctx->slock, flags); | |
| 1727 | − vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_QUEUED); | |
| 1728 | − ctx->cur_frm = NULL; | |
| 1729 | − ctx->next_frm = NULL; | |
| 1730 | − list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { | |
| 1731 | − list_del(&buf->list); | |
| 1732 | − vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); | |
| 1733 | − } | |
| 1734 | − spin_unlock_irqrestore(&ctx->slock, flags); | |
| 494 | +error: | |
| 495 | + v4l2_async_notifier_cleanup(&cal->notifier); | |
| 1735 | 496 | return ret; |
| 1736 | 497 | } |
| 1737 | 498 | |
| 1738 | −static void cal_stop_streaming(struct vb2_queue *vq) | |
| 499 | +static void cal_async_notifier_unregister(struct cal_dev *cal) | |
| 1739 | 500 | { |
| 1740 | − struct cal_ctx *ctx = vb2_get_drv_priv(vq); | |
| 1741 | − struct cal_dmaqueue *dma_q = &ctx->vidq; | |
| 1742 | − struct cal_buffer *buf, *tmp; | |
| 1743 | − unsigned long timeout; | |
| 1744 | − unsigned long flags; | |
| 1745 | − int ret; | |
| 1746 | − bool dma_act; | |
| 1747 | − | |
| 1748 | − csi2_ppi_disable(ctx); | |
| 1749 | − | |
| 1750 | − /* wait for stream and dma to finish */ | |
| 1751 | − dma_act = true; | |
| 1752 | − timeout = jiffies + msecs_to_jiffies(500); | |
| 1753 | − while (dma_act && time_before(jiffies, timeout)) { | |
| 1754 | − msleep(50); | |
| 1755 | − | |
| 1756 | − spin_lock_irqsave(&ctx->slock, flags); | |
| 1757 | − dma_act = ctx->dma_act; | |
| 1758 | − spin_unlock_irqrestore(&ctx->slock, flags); | |
| 1759 | − } | |
| 1760 | − | |
| 1761 | − if (dma_act) | |
| 1762 | − ctx_err(ctx, "failed to disable dma cleanly\n"); | |
| 1763 | − | |
| 1764 | − disable_irqs(ctx); | |
| 1765 | − csi2_phy_deinit(ctx); | |
| 1766 | − | |
| 1767 | − if (v4l2_subdev_call(ctx->sensor, video, s_stream, 0)) | |
| 1768 | − ctx_err(ctx, "stream off failed in subdev\n"); | |
| 1769 | − | |
| 1770 | − ret = v4l2_subdev_call(ctx->sensor, core, s_power, 0); | |
| 1771 | − if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) | |
| 1772 | − ctx_err(ctx, "power off failed in subdev\n"); | |
| 1773 | − | |
| 1774 | − /* Release all active buffers */ | |
| 1775 | − spin_lock_irqsave(&ctx->slock, flags); | |
| 1776 | − list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { | |
| 1777 | − list_del(&buf->list); | |
| 1778 | − vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); | |
| 1779 | − } | |
| 1780 | − | |
| 1781 | − if (ctx->cur_frm == ctx->next_frm) { | |
| 1782 | − vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); | |
| 1783 | − } else { | |
| 1784 | − vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); | |
| 1785 | − vb2_buffer_done(&ctx->next_frm->vb.vb2_buf, | |
| 1786 | − VB2_BUF_STATE_ERROR); | |
| 1787 | − } | |
| 1788 | − ctx->cur_frm = NULL; | |
| 1789 | − ctx->next_frm = NULL; | |
| 1790 | − spin_unlock_irqrestore(&ctx->slock, flags); | |
| 1791 | − | |
| 1792 | − pm_runtime_put_sync(&ctx->dev->pdev->dev); | |
| 501 | + v4l2_async_notifier_unregister(&cal->notifier); | |
| 502 | + v4l2_async_notifier_cleanup(&cal->notifier); | |
| 1793 | 503 | } |
| 1794 | 504 | |
| 1795 | −static const struct vb2_ops cal_video_qops = { | |
| 1796 | − .queue_setup = cal_queue_setup, | |
| 1797 | − .buf_prepare = cal_buffer_prepare, | |
| 1798 | − .buf_queue = cal_buffer_queue, | |
| 1799 | − .start_streaming = cal_start_streaming, | |
| 1800 | − .stop_streaming = cal_stop_streaming, | |
| 1801 | − .wait_prepare = vb2_ops_wait_prepare, | |
| 1802 | − .wait_finish = vb2_ops_wait_finish, | |
| 1803 | −}; | |
| 1804 | − | |
| 1805 | −static const struct v4l2_file_operations cal_fops = { | |
| 1806 | − .owner = THIS_MODULE, | |
| 1807 | − .open = v4l2_fh_open, | |
| 1808 | − .release = vb2_fop_release, | |
| 1809 | − .read = vb2_fop_read, | |
| 1810 | − .poll = vb2_fop_poll, | |
| 1811 | − .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ | |
| 1812 | − .mmap = vb2_fop_mmap, | |
| 1813 | −}; | |
| 1814 | − | |
| 1815 | −static const struct v4l2_ioctl_ops cal_ioctl_ops = { | |
| 1816 | − .vidioc_querycap = cal_querycap, | |
| 1817 | − .vidioc_enum_fmt_vid_cap = cal_enum_fmt_vid_cap, | |
| 1818 | − .vidioc_g_fmt_vid_cap = cal_g_fmt_vid_cap, | |
| 1819 | − .vidioc_try_fmt_vid_cap = cal_try_fmt_vid_cap, | |
| 1820 | − .vidioc_s_fmt_vid_cap = cal_s_fmt_vid_cap, | |
| 1821 | − .vidioc_enum_framesizes = cal_enum_framesizes, | |
| 1822 | − .vidioc_reqbufs = vb2_ioctl_reqbufs, | |
| 1823 | − .vidioc_create_bufs = vb2_ioctl_create_bufs, | |
| 1824 | − .vidioc_prepare_buf = vb2_ioctl_prepare_buf, | |
| 1825 | − .vidioc_querybuf = vb2_ioctl_querybuf, | |
| 1826 | − .vidioc_qbuf = vb2_ioctl_qbuf, | |
| 1827 | − .vidioc_dqbuf = vb2_ioctl_dqbuf, | |
| 1828 | − .vidioc_expbuf = vb2_ioctl_expbuf, | |
| 1829 | − .vidioc_enum_input = cal_enum_input, | |
| 1830 | − .vidioc_g_input = cal_g_input, | |
| 1831 | − .vidioc_s_input = cal_s_input, | |
| 1832 | − .vidioc_enum_frameintervals = cal_enum_frameintervals, | |
| 1833 | − .vidioc_streamon = vb2_ioctl_streamon, | |
| 1834 | − .vidioc_streamoff = vb2_ioctl_streamoff, | |
| 1835 | − .vidioc_log_status = v4l2_ctrl_log_status, | |
| 1836 | − .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, | |
| 1837 | − .vidioc_unsubscribe_event = v4l2_event_unsubscribe, | |
| 1838 | −}; | |
| 1839 | − | |
| 1840 | −static const struct video_device cal_videodev = { | |
| 1841 | − .name = CAL_MODULE_NAME, | |
| 1842 | − .fops = &cal_fops, | |
| 1843 | − .ioctl_ops = &cal_ioctl_ops, | |
| 1844 | − .minor = -1, | |
| 1845 | − .release = video_device_release_empty, | |
| 1846 | − .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | | |
| 1847 | − V4L2_CAP_READWRITE, | |
| 1848 | −}; | |
| 1849 | − | |
| 1850 | −/* ----------------------------------------------------------------- | |
| 1851 | − * Initialization and module stuff | |
| 505 | +/* ------------------------------------------------------------------ | |
| 506 | + * Media and V4L2 device handling | |
| 1852 | 507 | * ------------------------------------------------------------------ |
| 1853 | 508 | */ |
| 1854 | −static int cal_complete_ctx(struct cal_ctx *ctx); | |
| 1855 | 509 | |
| 1856 | −static int cal_async_bound(struct v4l2_async_notifier *notifier, | |
| 1857 | − struct v4l2_subdev *subdev, | |
| 1858 | − struct v4l2_async_subdev *asd) | |
| 510 | +/* | |
| 511 | + * Register user-facing devices. To be called at the end of the probe function | |
| 512 | + * when all resources are initialized and ready. | |
| 513 | + */ | |
| 514 | +static int cal_media_register(struct cal_dev *cal) | |
| 1859 | 515 | { |
| 1860 | − struct cal_ctx *ctx = notifier_to_ctx(notifier); | |
| 1861 | − struct v4l2_subdev_mbus_code_enum mbus_code; | |
| 1862 | − int ret = 0; | |
| 1863 | − int i, j, k; | |
| 1864 | − | |
| 1865 | − if (ctx->sensor) { | |
| 1866 | − ctx_info(ctx, "Rejecting subdev %s (Already set!!)", | |
| 1867 | − subdev->name); | |
| 1868 | − return 0; | |
| 1869 | − } | |
| 1870 | − | |
| 1871 | − ctx->sensor = subdev; | |
| 1872 | − ctx_dbg(1, ctx, "Using sensor %s for capture\n", subdev->name); | |
| 1873 | − | |
| 1874 | − /* Enumerate sub device formats and enable all matching local formats */ | |
| 1875 | − ctx->num_active_fmt = 0; | |
| 1876 | − for (j = 0, i = 0; ret != -EINVAL; ++j) { | |
| 1877 | − struct cal_fmt *fmt; | |
| 1878 | − | |
| 1879 | − memset(&mbus_code, 0, sizeof(mbus_code)); | |
| 1880 | − mbus_code.index = j; | |
| 1881 | − mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; | |
| 1882 | − ret = v4l2_subdev_call(subdev, pad, enum_mbus_code, | |
| 1883 | − NULL, &mbus_code); | |
| 1884 | − if (ret) | |
| 1885 | − continue; | |
| 1886 | − | |
| 1887 | − ctx_dbg(2, ctx, | |
| 1888 | − "subdev %s: code: %04x idx: %d\n", | |
| 1889 | − subdev->name, mbus_code.code, j); | |
| 1890 | − | |
| 1891 | − for (k = 0; k < ARRAY_SIZE(cal_formats); k++) { | |
| 1892 | − fmt = &cal_formats[k]; | |
| 516 | + int ret; | |
| 1893 | 517 | |
| 1894 | − if (mbus_code.code == fmt->code) { | |
| 1895 | − ctx->active_fmt[i] = fmt; | |
| 1896 | − ctx_dbg(2, ctx, | |
| 1897 | − "matched fourcc: %s: code: %04x idx: %d\n", | |
| 1898 | − fourcc_to_str(fmt->fourcc), | |
| 1899 | − fmt->code, i); | |
| 1900 | − ctx->num_active_fmt = ++i; | |
| 1901 | − } | |
| 1902 | − } | |
| 518 | + ret = media_device_register(&cal->mdev); | |
| 519 | + if (ret) { | |
| 520 | + cal_err(cal, "Failed to register media device\n"); | |
| 521 | + return ret; | |
| 1903 | 522 | } |
| 1904 | 523 | |
| 1905 | − if (i == 0) { | |
| 1906 | − ctx_err(ctx, "No suitable format reported by subdev %s\n", | |
| 1907 | − subdev->name); | |
| 1908 | − return -EINVAL; | |
| 524 | + /* | |
| 525 | + * Register the async notifier. This may trigger registration of the | |
| 526 | + * V4L2 video devices if all subdevs are ready. | |
| 527 | + */ | |
| 528 | + ret = cal_async_notifier_register(cal); | |
| 529 | + if (ret) { | |
| 530 | + media_device_unregister(&cal->mdev); | |
| 531 | + return ret; | |
| 1909 | 532 | } |
| 1910 | 533 | |
| 1911 | − cal_complete_ctx(ctx); | |
| 1912 | − | |
| 1913 | 534 | return 0; |
| 1914 | 535 | } |
| 1915 | 536 | |
| 1916 | −static int cal_async_complete(struct v4l2_async_notifier *notifier) | |
| 537 | +/* | |
| 538 | + * Unregister the user-facing devices, but don't free memory yet. To be called | |
| 539 | + * at the beginning of the remove function, to disallow access from userspace. | |
| 540 | + */ | |
| 541 | +static void cal_media_unregister(struct cal_dev *cal) | |
| 1917 | 542 | { |
| 1918 | − struct cal_ctx *ctx = notifier_to_ctx(notifier); | |
| 1919 | − const struct cal_fmt *fmt; | |
| 1920 | − struct v4l2_mbus_framefmt mbus_fmt; | |
| 1921 | − int ret; | |
| 1922 | − | |
| 1923 | − ret = __subdev_get_format(ctx, &mbus_fmt); | |
| 1924 | − if (ret) | |
| 1925 | − return ret; | |
| 543 | + unsigned int i; | |
| 1926 | 544 | |
| 1927 | − fmt = find_format_by_code(ctx, mbus_fmt.code); | |
| 1928 | − if (!fmt) { | |
| 1929 | − ctx_dbg(3, ctx, "mbus code format (0x%08x) not found.\n", | |
| 1930 | − mbus_fmt.code); | |
| 1931 | − return -EINVAL; | |
| 545 | + /* Unregister all the V4L2 video devices. */ | |
| 546 | + for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { | |
| 547 | + if (cal->ctx[i]) | |
| 548 | + cal_ctx_v4l2_unregister(cal->ctx[i]); | |
| 1932 | 549 | } |
| 1933 | 550 | |
| 1934 | − /* Save current subdev format */ | |
| 1935 | − v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); | |
| 1936 | − ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | |
| 1937 | − ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; | |
| 1938 | − cal_calc_format_size(ctx, fmt, &ctx->v_fmt); | |
| 1939 | − ctx->fmt = fmt; | |
| 1940 | − ctx->m_fmt = mbus_fmt; | |
| 1941 | − | |
| 1942 | − return 0; | |
| 551 | + cal_async_notifier_unregister(cal); | |
| 552 | + media_device_unregister(&cal->mdev); | |
| 1943 | 553 | } |
| 1944 | 554 | |
| 1945 | −static const struct v4l2_async_notifier_operations cal_async_ops = { | |
| 1946 | − .bound = cal_async_bound, | |
| 1947 | − .complete = cal_async_complete, | |
| 1948 | −}; | |
| 1949 | − | |
| 1950 | −static int cal_complete_ctx(struct cal_ctx *ctx) | |
| 555 | +/* | |
| 556 | + * Initialize the in-kernel objects. To be called at the beginning of the probe | |
| 557 | + * function, before the V4L2 device is used by the driver. | |
| 558 | + */ | |
| 559 | +static int cal_media_init(struct cal_dev *cal) | |
| 1951 | 560 | { |
| 1952 | − struct video_device *vfd; | |
| 1953 | − struct vb2_queue *q; | |
| 561 | + struct media_device *mdev = &cal->mdev; | |
| 1954 | 562 | int ret; |
| 1955 | 563 | |
| 1956 | − ctx->timeperframe = tpf_default; | |
| 1957 | − ctx->external_rate = 192000000; | |
| 1958 | − | |
| 1959 | − /* initialize locks */ | |
| 1960 | − spin_lock_init(&ctx->slock); | |
| 1961 | − mutex_init(&ctx->mutex); | |
| 1962 | − | |
| 1963 | − /* initialize queue */ | |
| 1964 | − q = &ctx->vb_vidq; | |
| 1965 | − q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | |
| 1966 | − q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; | |
| 1967 | − q->drv_priv = ctx; | |
| 1968 | − q->buf_struct_size = sizeof(struct cal_buffer); | |
| 1969 | − q->ops = &cal_video_qops; | |
| 1970 | − q->mem_ops = &vb2_dma_contig_memops; | |
| 1971 | − q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; | |
| 1972 | − q->lock = &ctx->mutex; | |
| 1973 | − q->min_buffers_needed = 3; | |
| 1974 | − q->dev = ctx->v4l2_dev.dev; | |
| 1975 | − | |
| 1976 | − ret = vb2_queue_init(q); | |
| 1977 | − if (ret) | |
| 1978 | − return ret; | |
| 1979 | − | |
| 1980 | − /* init video dma queues */ | |
| 1981 | − INIT_LIST_HEAD(&ctx->vidq.active); | |
| 1982 | − | |
| 1983 | − vfd = &ctx->vdev; | |
| 1984 | − *vfd = cal_videodev; | |
| 1985 | − vfd->v4l2_dev = &ctx->v4l2_dev; | |
| 1986 | − vfd->queue = q; | |
| 564 | + mdev->dev = cal->dev; | |
| 565 | + mdev->hw_revision = cal->revision; | |
| 566 | + strscpy(mdev->model, "CAL", sizeof(mdev->model)); | |
| 567 | + snprintf(mdev->bus_info, sizeof(mdev->bus_info), "platform:%s", | |
| 568 | + dev_name(mdev->dev)); | |
| 569 | + media_device_init(mdev); | |
| 1987 | 570 | |
| 1988 | 571 | /* |
| 1989 | − * Provide a mutex to v4l2 core. It will be used to protect | |
| 1990 | − * all fops and v4l2 ioctls. | |
| 572 | + * Initialize the V4L2 device (despite the function name, this performs | |
| 573 | + * initialization, not registration). | |
| 1991 | 574 | */ |
| 1992 | − vfd->lock = &ctx->mutex; | |
| 1993 | − video_set_drvdata(vfd, ctx); | |
| 1994 | − | |
| 1995 | − ret = video_register_device(vfd, VFL_TYPE_VIDEO, video_nr); | |
| 1996 | − if (ret < 0) | |
| 575 | + cal->v4l2_dev.mdev = mdev; | |
| 576 | + ret = v4l2_device_register(cal->dev, &cal->v4l2_dev); | |
| 577 | + if (ret) { | |
| 578 | + cal_err(cal, "Failed to register V4L2 device\n"); | |
| 1997 | 579 | return ret; |
| 580 | + } | |
| 1998 | 581 | |
| 1999 | − v4l2_info(&ctx->v4l2_dev, "V4L2 device registered as %s\n", | |
| 2000 | − video_device_node_name(vfd)); | |
| 582 | + vb2_dma_contig_set_max_seg_size(cal->dev, DMA_BIT_MASK(32)); | |
| 2001 | 583 | |
| 2002 | 584 | return 0; |
| 2003 | 585 | } |
| 2004 | 586 | |
| 2005 | −static struct device_node * | |
| 2006 | −of_get_next_port(const struct device_node *parent, | |
| 2007 | − struct device_node *prev) | |
| 587 | +/* | |
| 588 | + * Cleanup the in-kernel objects, freeing memory. To be called at the very end | |
| 589 | + * of the remove sequence, when nothing (including userspace) can access the | |
| 590 | + * objects anymore. | |
| 591 | + */ | |
| 592 | +static void cal_media_cleanup(struct cal_dev *cal) | |
| 2008 | 593 | { |
| 2009 | − struct device_node *port = NULL; | |
| 2010 | − | |
| 2011 | − if (!parent) | |
| 2012 | − return NULL; | |
| 594 | + unsigned int i; | |
| 2013 | 595 | |
| 2014 | − if (!prev) { | |
| 2015 | − struct device_node *ports; | |
| 2016 | − /* | |
| 2017 | − * It's the first call, we have to find a port subnode | |
| 2018 | − * within this node or within an optional 'ports' node. | |
| 2019 | − */ | |
| 2020 | − ports = of_get_child_by_name(parent, "ports"); | |
| 2021 | − if (ports) | |
| 2022 | − parent = ports; | |
| 2023 | − | |
| 2024 | − port = of_get_child_by_name(parent, "port"); | |
| 2025 | − | |
| 2026 | − /* release the 'ports' node */ | |
| 2027 | − of_node_put(ports); | |
| 2028 | − } else { | |
| 2029 | − struct device_node *ports; | |
| 2030 | − | |
| 2031 | − ports = of_get_parent(prev); | |
| 2032 | − if (!ports) | |
| 2033 | − return NULL; | |
| 2034 | − | |
| 2035 | − do { | |
| 2036 | − port = of_get_next_child(ports, prev); | |
| 2037 | − if (!port) { | |
| 2038 | − of_node_put(ports); | |
| 2039 | − return NULL; | |
| 2040 | − } | |
| 2041 | − prev = port; | |
| 2042 | − } while (!of_node_name_eq(port, "port")); | |
| 2043 | − of_node_put(ports); | |
| 596 | + for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { | |
| 597 | + if (cal->ctx[i]) | |
| 598 | + cal_ctx_v4l2_cleanup(cal->ctx[i]); | |
| 2044 | 599 | } |
| 2045 | 600 | |
| 2046 | − return port; | |
| 2047 | −} | |
| 2048 | − | |
| 2049 | −static struct device_node * | |
| 2050 | −of_get_next_endpoint(const struct device_node *parent, | |
| 2051 | − struct device_node *prev) | |
| 2052 | −{ | |
| 2053 | − struct device_node *ep = NULL; | |
| 2054 | − | |
| 2055 | − if (!parent) | |
| 2056 | − return NULL; | |
| 601 | + v4l2_device_unregister(&cal->v4l2_dev); | |
| 602 | + media_device_cleanup(&cal->mdev); | |
| 2057 | 603 | |
| 2058 | − do { | |
| 2059 | − ep = of_get_next_child(parent, prev); | |
| 2060 | − if (!ep) | |
| 2061 | − return NULL; | |
| 2062 | − prev = ep; | |
| 2063 | − } while (!of_node_name_eq(ep, "endpoint")); | |
| 2064 | − | |
| 2065 | − return ep; | |
| 604 | + vb2_dma_contig_clear_max_seg_size(cal->dev); | |
| 2066 | 605 | } |
| 2067 | 606 | |
| 2068 | −static int of_cal_create_instance(struct cal_ctx *ctx, int inst) | |
| 2069 | −{ | |
| 2070 | − struct platform_device *pdev = ctx->dev->pdev; | |
| 2071 | − struct device_node *ep_node, *port, *sensor_node, *parent; | |
| 2072 | − struct v4l2_fwnode_endpoint *endpoint; | |
| 2073 | − struct v4l2_async_subdev *asd; | |
| 2074 | − u32 regval = 0; | |
| 2075 | − int ret, index, found_port = 0, lane; | |
| 2076 | − | |
| 2077 | − parent = pdev->dev.of_node; | |
| 2078 | − | |
| 2079 | − endpoint = &ctx->endpoint; | |
| 2080 | − | |
| 2081 | − ep_node = NULL; | |
| 2082 | − port = NULL; | |
| 2083 | − sensor_node = NULL; | |
| 2084 | − ret = -EINVAL; | |
| 2085 | − | |
| 2086 | − ctx_dbg(3, ctx, "Scanning Port node for csi2 port: %d\n", inst); | |
| 2087 | − for (index = 0; index < CAL_NUM_CSI2_PORTS; index++) { | |
| 2088 | − port = of_get_next_port(parent, port); | |
| 2089 | − if (!port) { | |
| 2090 | − ctx_dbg(1, ctx, "No port node found for csi2 port:%d\n", | |
| 2091 | − index); | |
| 2092 | − goto cleanup_exit; | |
| 2093 | − } | |
| 2094 | − | |
| 2095 | − /* Match the slice number with <REG> */ | |
| 2096 | − of_property_read_u32(port, "reg", ®val); | |
| 2097 | − ctx_dbg(3, ctx, "port:%d inst:%d <reg>:%d\n", | |
| 2098 | − index, inst, regval); | |
| 2099 | − if ((regval == inst) && (index == inst)) { | |
| 2100 | − found_port = 1; | |
| 2101 | − break; | |
| 2102 | − } | |
| 2103 | − } | |
| 2104 | − | |
| 2105 | − if (!found_port) { | |
| 2106 | − ctx_dbg(1, ctx, "No port node matches csi2 port:%d\n", | |
| 2107 | − inst); | |
| 2108 | − goto cleanup_exit; | |
| 2109 | − } | |
| 2110 | − | |
| 2111 | − ctx_dbg(3, ctx, "Scanning sub-device for csi2 port: %d\n", | |
| 2112 | − inst); | |
| 2113 | − | |
| 2114 | − ep_node = of_get_next_endpoint(port, ep_node); | |
| 2115 | − if (!ep_node) { | |
| 2116 | − ctx_dbg(3, ctx, "can't get next endpoint\n"); | |
| 2117 | − goto cleanup_exit; | |
| 2118 | − } | |
| 2119 | − | |
| 2120 | − sensor_node = of_graph_get_remote_port_parent(ep_node); | |
| 2121 | − if (!sensor_node) { | |
| 2122 | − ctx_dbg(3, ctx, "can't get remote parent\n"); | |
| 2123 | − goto cleanup_exit; | |
| 2124 | − } | |
| 2125 | − | |
| 2126 | − v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), endpoint); | |
| 2127 | − | |
| 2128 | − if (endpoint->bus_type != V4L2_MBUS_CSI2_DPHY) { | |
| 2129 | − ctx_err(ctx, "Port:%d sub-device %pOFn is not a CSI2 device\n", | |
| 2130 | − inst, sensor_node); | |
| 2131 | − goto cleanup_exit; | |
| 2132 | − } | |
| 2133 | − | |
| 2134 | − /* Store Virtual Channel number */ | |
| 2135 | − ctx->virtual_channel = endpoint->base.id; | |
| 2136 | − | |
| 2137 | − ctx_dbg(3, ctx, "Port:%d v4l2-endpoint: CSI2\n", inst); | |
| 2138 | − ctx_dbg(3, ctx, "Virtual Channel=%d\n", ctx->virtual_channel); | |
| 2139 | − ctx_dbg(3, ctx, "flags=0x%08x\n", endpoint->bus.mipi_csi2.flags); | |
| 2140 | − ctx_dbg(3, ctx, "clock_lane=%d\n", endpoint->bus.mipi_csi2.clock_lane); | |
| 2141 | − ctx_dbg(3, ctx, "num_data_lanes=%d\n", | |
| 2142 | − endpoint->bus.mipi_csi2.num_data_lanes); | |
| 2143 | − ctx_dbg(3, ctx, "data_lanes= <\n"); | |
| 2144 | − for (lane = 0; lane < endpoint->bus.mipi_csi2.num_data_lanes; lane++) | |
| 2145 | − ctx_dbg(3, ctx, "\t%d\n", | |
| 2146 | − endpoint->bus.mipi_csi2.data_lanes[lane]); | |
| 2147 | − ctx_dbg(3, ctx, "\t>\n"); | |
| 2148 | − | |
| 2149 | − ctx_dbg(1, ctx, "Port: %d found sub-device %pOFn\n", | |
| 2150 | − inst, sensor_node); | |
| 2151 | − | |
| 2152 | − v4l2_async_notifier_init(&ctx->notifier); | |
| 2153 | − | |
| 2154 | − asd = kzalloc(sizeof(*asd), GFP_KERNEL); | |
| 2155 | − if (!asd) | |
| 2156 | − goto cleanup_exit; | |
| 2157 | − | |
| 2158 | − asd->match_type = V4L2_ASYNC_MATCH_FWNODE; | |
| 2159 | − asd->match.fwnode = of_fwnode_handle(sensor_node); | |
| 2160 | − | |
| 2161 | − ret = v4l2_async_notifier_add_subdev(&ctx->notifier, asd); | |
| 2162 | − if (ret) { | |
| 2163 | − ctx_err(ctx, "Error adding asd\n"); | |
| 2164 | − kfree(asd); | |
| 2165 | − goto cleanup_exit; | |
| 2166 | − } | |
| 2167 | − | |
| 2168 | − ctx->notifier.ops = &cal_async_ops; | |
| 2169 | − ret = v4l2_async_notifier_register(&ctx->v4l2_dev, | |
| 2170 | − &ctx->notifier); | |
| 2171 | − if (ret) { | |
| 2172 | − ctx_err(ctx, "Error registering async notifier\n"); | |
| 2173 | − v4l2_async_notifier_cleanup(&ctx->notifier); | |
| 2174 | − ret = -EINVAL; | |
| 2175 | − } | |
| 2176 | − | |
| 2177 | − /* | |
| 2178 | − * On success we need to keep reference on sensor_node, or | |
| 2179 | − * if notifier_cleanup was called above, sensor_node was | |
| 2180 | − * already put. | |
| 2181 | − */ | |
| 2182 | − sensor_node = NULL; | |
| 2183 | − | |
| 2184 | −cleanup_exit: | |
| 2185 | − of_node_put(sensor_node); | |
| 2186 | − of_node_put(ep_node); | |
| 2187 | − of_node_put(port); | |
| 2188 | − | |
| 2189 | − return ret; | |
| 2190 | −} | |
| 607 | +/* ------------------------------------------------------------------ | |
| 608 | + * Initialization and module stuff | |
| 609 | + * ------------------------------------------------------------------ | |
| 610 | + */ | |
| 2191 | 611 | |
| 2192 | −static struct cal_ctx *cal_create_instance(struct cal_dev *dev, int inst) | |
| 612 | +static struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst) | |
| 2193 | 613 | { |
| 2194 | 614 | struct cal_ctx *ctx; |
| 2195 | − struct v4l2_ctrl_handler *hdl; | |
| 2196 | 615 | int ret; |
| 2197 | 616 | |
| 2198 | − ctx = devm_kzalloc(&dev->pdev->dev, sizeof(*ctx), GFP_KERNEL); | |
| 617 | + ctx = devm_kzalloc(cal->dev, sizeof(*ctx), GFP_KERNEL); | |
| 2199 | 618 | if (!ctx) |
| 2200 | 619 | return NULL; |
| 2201 | 620 | |
| 2202 | − /* save the cal_dev * for future ref */ | |
| 2203 | − ctx->dev = dev; | |
| 621 | + ctx->cal = cal; | |
| 622 | + ctx->phy = cal->phy[inst]; | |
| 623 | + ctx->index = inst; | |
| 624 | + ctx->cport = inst; | |
| 2204 | 625 | |
| 2205 | − snprintf(ctx->v4l2_dev.name, sizeof(ctx->v4l2_dev.name), | |
| 2206 | − "%s-%03d", CAL_MODULE_NAME, inst); | |
| 2207 | − ret = v4l2_device_register(&dev->pdev->dev, &ctx->v4l2_dev); | |
| 626 | + ret = cal_ctx_v4l2_init(ctx); | |
| 2208 | 627 | if (ret) |
| 2209 | − goto err_exit; | |
| 2210 | − | |
| 2211 | − hdl = &ctx->ctrl_handler; | |
| 2212 | − ret = v4l2_ctrl_handler_init(hdl, 11); | |
| 2213 | − if (ret) { | |
| 2214 | − ctx_err(ctx, "Failed to init ctrl handler\n"); | |
| 2215 | − goto unreg_dev; | |
| 2216 | − } | |
| 2217 | − ctx->v4l2_dev.ctrl_handler = hdl; | |
| 2218 | − | |
| 2219 | − /* Make sure Camera Core H/W register area is available */ | |
| 2220 | − ctx->cc = dev->cc[inst]; | |
| 2221 | − | |
| 2222 | − /* Store the instance id */ | |
| 2223 | − ctx->csi2_port = inst + 1; | |
| 628 | + return NULL; | |
| 2224 | 629 | |
| 2225 | − ret = of_cal_create_instance(ctx, inst); | |
| 2226 | − if (ret) { | |
| 2227 | − ret = -EINVAL; | |
| 2228 | − goto free_hdl; | |
| 2229 | − } | |
| 2230 | 630 | return ctx; |
| 2231 | − | |
| 2232 | −free_hdl: | |
| 2233 | − v4l2_ctrl_handler_free(hdl); | |
| 2234 | −unreg_dev: | |
| 2235 | − v4l2_device_unregister(&ctx->v4l2_dev); | |
| 2236 | −err_exit: | |
| 2237 | − return NULL; | |
| 2238 | 631 | } |
| 2239 | 632 | |
| 2240 | 633 | static const struct of_device_id cal_of_match[] = { |
| @@ -2258,191 +651,253 @@ static const struct of_device_id cal_of_match[] = { · 17 unmodified lines | ||
| 2258 | 651 | }; |
| 2259 | 652 | MODULE_DEVICE_TABLE(of, cal_of_match); |
| 2260 | 653 | |
| 654 | +/* Get hardware revision and info. */ | |
| 655 | + | |
| 656 | +#define CAL_HL_HWINFO_VALUE 0xa3c90469 | |
| 657 | + | |
| 658 | +static void cal_get_hwinfo(struct cal_dev *cal) | |
| 659 | +{ | |
| 660 | + u32 hwinfo; | |
| 661 | + | |
| 662 | + cal->revision = cal_read(cal, CAL_HL_REVISION); | |
| 663 | + switch (FIELD_GET(CAL_HL_REVISION_SCHEME_MASK, cal->revision)) { | |
| 664 | + case CAL_HL_REVISION_SCHEME_H08: | |
| 665 | + cal_dbg(3, cal, "CAL HW revision %lu.%lu.%lu (0x%08x)\n", | |
| 666 | + FIELD_GET(CAL_HL_REVISION_MAJOR_MASK, cal->revision), | |
| 667 | + FIELD_GET(CAL_HL_REVISION_MINOR_MASK, cal->revision), | |
| 668 | + FIELD_GET(CAL_HL_REVISION_RTL_MASK, cal->revision), | |
| 669 | + cal->revision); | |
| 670 | + break; | |
| 671 | + | |
| 672 | + case CAL_HL_REVISION_SCHEME_LEGACY: | |
| 673 | + default: | |
| 674 | + cal_info(cal, "Unexpected CAL HW revision 0x%08x\n", | |
| 675 | + cal->revision); | |
| 676 | + break; | |
| 677 | + } | |
| 678 | + | |
| 679 | + hwinfo = cal_read(cal, CAL_HL_HWINFO); | |
| 680 | + if (hwinfo != CAL_HL_HWINFO_VALUE) | |
| 681 | + cal_info(cal, "CAL_HL_HWINFO = 0x%08x, expected 0x%08x\n", | |
| 682 | + hwinfo, CAL_HL_HWINFO_VALUE); | |
| 683 | +} | |
| 684 | + | |
| 685 | +static int cal_init_camerarx_regmap(struct cal_dev *cal) | |
| 686 | +{ | |
| 687 | + struct platform_device *pdev = to_platform_device(cal->dev); | |
| 688 | + struct device_node *np = cal->dev->of_node; | |
| 689 | + struct regmap_config config = { }; | |
| 690 | + struct regmap *syscon; | |
| 691 | + struct resource *res; | |
| 692 | + unsigned int offset; | |
| 693 | + void __iomem *base; | |
| 694 | + | |
| 695 | + syscon = syscon_regmap_lookup_by_phandle_args(np, "ti,camerrx-control", | |
| 696 | + 1, &offset); | |
| 697 | + if (!IS_ERR(syscon)) { | |
| 698 | + cal->syscon_camerrx = syscon; | |
| 699 | + cal->syscon_camerrx_offset = offset; | |
| 700 | + return 0; | |
| 701 | + } | |
| 702 | + | |
| 703 | + dev_warn(cal->dev, "failed to get ti,camerrx-control: %ld\n", | |
| 704 | + PTR_ERR(syscon)); | |
| 705 | + | |
| 706 | + /* | |
| 707 | + * Backward DTS compatibility. If syscon entry is not present then | |
| 708 | + * check if the camerrx_control resource is present. | |
| 709 | + */ | |
| 710 | + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, | |
| 711 | + "camerrx_control"); | |
| 712 | + base = devm_ioremap_resource(cal->dev, res); | |
| 713 | + if (IS_ERR(base)) { | |
| 714 | + cal_err(cal, "failed to ioremap camerrx_control\n"); | |
| 715 | + return PTR_ERR(base); | |
| 716 | + } | |
| 717 | + | |
| 718 | + cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", | |
| 719 | + res->name, &res->start, &res->end); | |
| 720 | + | |
| 721 | + config.reg_bits = 32; | |
| 722 | + config.reg_stride = 4; | |
| 723 | + config.val_bits = 32; | |
| 724 | + config.max_register = resource_size(res) - 4; | |
| 725 | + | |
| 726 | + syscon = regmap_init_mmio(NULL, base, &config); | |
| 727 | + if (IS_ERR(syscon)) { | |
| 728 | + pr_err("regmap init failed\n"); | |
| 729 | + return PTR_ERR(syscon); | |
| 730 | + } | |
| 731 | + | |
| 732 | + /* | |
| 733 | + * In this case the base already point to the direct CM register so no | |
| 734 | + * need for an offset. | |
| 735 | + */ | |
| 736 | + cal->syscon_camerrx = syscon; | |
| 737 | + cal->syscon_camerrx_offset = 0; | |
| 738 | + | |
| 739 | + return 0; | |
| 740 | +} | |
| 741 | + | |
| 2261 | 742 | static int cal_probe(struct platform_device *pdev) |
| 2262 | 743 | { |
| 2263 | − struct cal_dev *dev; | |
| 744 | + struct cal_dev *cal; | |
| 2264 | 745 | struct cal_ctx *ctx; |
| 2265 | − struct device_node *parent = pdev->dev.of_node; | |
| 2266 | − struct regmap *syscon_camerrx = NULL; | |
| 2267 | − u32 syscon_camerrx_offset = 0; | |
| 746 | + bool connected = false; | |
| 747 | + unsigned int i; | |
| 2268 | 748 | int ret; |
| 2269 | 749 | int irq; |
| 2270 | − int i; | |
| 2271 | 750 | |
| 2272 | − dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); | |
| 2273 | − if (!dev) | |
| 751 | + cal = devm_kzalloc(&pdev->dev, sizeof(*cal), GFP_KERNEL); | |
| 752 | + if (!cal) | |
| 2274 | 753 | return -ENOMEM; |
| 2275 | 754 | |
| 2276 | − dev->data = of_device_get_match_data(&pdev->dev); | |
| 2277 | − if (!dev->data) { | |
| 755 | + cal->data = of_device_get_match_data(&pdev->dev); | |
| 756 | + if (!cal->data) { | |
| 2278 | 757 | dev_err(&pdev->dev, "Could not get feature data based on compatible version\n"); |
| 2279 | 758 | return -ENODEV; |
| 2280 | 759 | } |
| 2281 | 760 | |
| 2282 | − dev->flags = dev->data->flags; | |
| 761 | + cal->dev = &pdev->dev; | |
| 762 | + platform_set_drvdata(pdev, cal); | |
| 2283 | 763 | |
| 2284 | − /* set pseudo v4l2 device name so we can use v4l2_printk */ | |
| 2285 | − strscpy(dev->v4l2_dev.name, CAL_MODULE_NAME, | |
| 2286 | − sizeof(dev->v4l2_dev.name)); | |
| 2287 | − | |
| 2288 | − /* save pdev pointer */ | |
| 2289 | − dev->pdev = pdev; | |
| 2290 | − | |
| 2291 | − dev->fclk = devm_clk_get(&pdev->dev, "fck"); | |
| 2292 | − if (IS_ERR(dev->fclk)) { | |
| 764 | + /* Acquire resources: clocks, CAMERARX regmap, I/O memory and IRQ. */ | |
| 765 | + cal->fclk = devm_clk_get(&pdev->dev, "fck"); | |
| 766 | + if (IS_ERR(cal->fclk)) { | |
| 2293 | 767 | dev_err(&pdev->dev, "cannot get CAL fclk\n"); |
| 2294 | − return PTR_ERR(dev->fclk); | |
| 2295 | − } | |
| 2296 | − | |
| 2297 | − syscon_camerrx = syscon_regmap_lookup_by_phandle(parent, | |
| 2298 | − "ti,camerrx-control"); | |
| 2299 | − ret = of_property_read_u32_index(parent, "ti,camerrx-control", 1, | |
| 2300 | − &syscon_camerrx_offset); | |
| 2301 | − if (IS_ERR(syscon_camerrx)) | |
| 2302 | − ret = PTR_ERR(syscon_camerrx); | |
| 2303 | − if (ret) { | |
| 2304 | − dev_warn(&pdev->dev, "failed to get ti,camerrx-control: %d\n", | |
| 2305 | − ret); | |
| 2306 | − | |
| 2307 | − /* | |
| 2308 | − * Backward DTS compatibility. | |
| 2309 | − * If syscon entry is not present then check if the | |
| 2310 | − * camerrx_control resource is present. | |
| 2311 | − */ | |
| 2312 | − syscon_camerrx = cal_get_camerarx_regmap(dev); | |
| 2313 | − if (IS_ERR(syscon_camerrx)) { | |
| 2314 | − dev_err(&pdev->dev, "failed to get camerrx_control regmap\n"); | |
| 2315 | − return PTR_ERR(syscon_camerrx); | |
| 2316 | − } | |
| 2317 | − /* In this case the base already point to the direct | |
| 2318 | − * CM register so no need for an offset | |
| 2319 | − */ | |
| 2320 | − syscon_camerrx_offset = 0; | |
| 768 | + return PTR_ERR(cal->fclk); | |
| 2321 | 769 | } |
| 2322 | 770 | |
| 2323 | − dev->syscon_camerrx = syscon_camerrx; | |
| 2324 | − dev->syscon_camerrx_offset = syscon_camerrx_offset; | |
| 2325 | − ret = cal_camerarx_regmap_init(dev); | |
| 2326 | − if (ret) | |
| 771 | + ret = cal_init_camerarx_regmap(cal); | |
| 772 | + if (ret < 0) | |
| 2327 | 773 | return ret; |
| 2328 | 774 | |
| 2329 | − dev->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, | |
| 775 | + cal->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, | |
| 2330 | 776 | "cal_top"); |
| 2331 | − dev->base = devm_ioremap_resource(&pdev->dev, dev->res); | |
| 2332 | − if (IS_ERR(dev->base)) | |
| 2333 | − return PTR_ERR(dev->base); | |
| 777 | + cal->base = devm_ioremap_resource(&pdev->dev, cal->res); | |
| 778 | + if (IS_ERR(cal->base)) | |
| 779 | + return PTR_ERR(cal->base); | |
| 2334 | 780 | |
| 2335 | − cal_dbg(1, dev, "ioresource %s at %pa - %pa\n", | |
| 2336 | − dev->res->name, &dev->res->start, &dev->res->end); | |
| 781 | + cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", | |
| 782 | + cal->res->name, &cal->res->start, &cal->res->end); | |
| 2337 | 783 | |
| 2338 | 784 | irq = platform_get_irq(pdev, 0); |
| 2339 | − cal_dbg(1, dev, "got irq# %d\n", irq); | |
| 785 | + cal_dbg(1, cal, "got irq# %d\n", irq); | |
| 2340 | 786 | ret = devm_request_irq(&pdev->dev, irq, cal_irq, 0, CAL_MODULE_NAME, |
| 2341 | − dev); | |
| 787 | + cal); | |
| 2342 | 788 | if (ret) |
| 2343 | 789 | return ret; |
| 2344 | 790 | |
| 2345 | − platform_set_drvdata(pdev, dev); | |
| 791 | + /* Read the revision and hardware info to verify hardware access. */ | |
| 792 | + pm_runtime_enable(&pdev->dev); | |
| 793 | + ret = pm_runtime_get_sync(&pdev->dev); | |
| 794 | + if (ret) | |
| 795 | + goto error_pm_runtime; | |
| 2346 | 796 | |
| 2347 | − dev->cc[0] = cc_create(dev, 0); | |
| 2348 | − if (IS_ERR(dev->cc[0])) | |
| 2349 | − return PTR_ERR(dev->cc[0]); | |
| 797 | + cal_get_hwinfo(cal); | |
| 798 | + pm_runtime_put_sync(&pdev->dev); | |
| 2350 | 799 | |
| 2351 | − if (cal_data_get_num_csi2_phy(dev) > 1) { | |
| 2352 | − dev->cc[1] = cc_create(dev, 1); | |
| 2353 | − if (IS_ERR(dev->cc[1])) | |
| 2354 | − return PTR_ERR(dev->cc[1]); | |
| 2355 | − } else { | |
| 2356 | − dev->cc[1] = NULL; | |
| 2357 | − } | |
| 800 | + /* Create CAMERARX PHYs. */ | |
| 801 | + for (i = 0; i < cal->data->num_csi2_phy; ++i) { | |
| 802 | + cal->phy[i] = cal_camerarx_create(cal, i); | |
| 803 | + if (IS_ERR(cal->phy[i])) { | |
| 804 | + ret = PTR_ERR(cal->phy[i]); | |
| 805 | + cal->phy[i] = NULL; | |
| 806 | + goto error_camerarx; | |
| 807 | + } | |
| 2358 | 808 | |
| 2359 | − dev->ctx[0] = NULL; | |
| 2360 | − dev->ctx[1] = NULL; | |
| 809 | + if (cal->phy[i]->sensor_node) | |
| 810 | + connected = true; | |
| 811 | + } | |
| 2361 | 812 | |
| 2362 | − dev->ctx[0] = cal_create_instance(dev, 0); | |
| 2363 | − if (cal_data_get_num_csi2_phy(dev) > 1) | |
| 2364 | − dev->ctx[1] = cal_create_instance(dev, 1); | |
| 2365 | − if (!dev->ctx[0] && !dev->ctx[1]) { | |
| 2366 | − cal_err(dev, "Neither port is configured, no point in staying up\n"); | |
| 2367 | − return -ENODEV; | |
| 813 | + if (!connected) { | |
| 814 | + cal_err(cal, "Neither port is configured, no point in staying up\n"); | |
| 815 | + ret = -ENODEV; | |
| 816 | + goto error_camerarx; | |
| 2368 | 817 | } |
| 2369 | 818 | |
| 2370 | − vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); | |
| 819 | + /* Initialize the media device. */ | |
| 820 | + ret = cal_media_init(cal); | |
| 821 | + if (ret < 0) | |
| 822 | + goto error_camerarx; | |
| 2371 | 823 | |
| 2372 | − pm_runtime_enable(&pdev->dev); | |
| 824 | + /* Create contexts. */ | |
| 825 | + for (i = 0; i < cal->data->num_csi2_phy; ++i) { | |
| 826 | + if (!cal->phy[i]->sensor_node) | |
| 827 | + continue; | |
| 2373 | 828 | |
| 2374 | − ret = pm_runtime_get_sync(&pdev->dev); | |
| 829 | + cal->ctx[i] = cal_ctx_create(cal, i); | |
| 830 | + if (!cal->ctx[i]) { | |
| 831 | + cal_err(cal, "Failed to create context %u\n", i); | |
| 832 | + ret = -ENODEV; | |
| 833 | + goto error_context; | |
| 834 | + } | |
| 835 | + } | |
| 836 | + | |
| 837 | + /* Register the media device. */ | |
| 838 | + ret = cal_media_register(cal); | |
| 2375 | 839 | if (ret) |
| 2376 | − goto runtime_disable; | |
| 840 | + goto error_context; | |
| 2377 | 841 | |
| 2378 | − /* Just check we can actually access the module */ | |
| 2379 | − cal_get_hwinfo(dev); | |
| 842 | + return 0; | |
| 2380 | 843 | |
| 2381 | − pm_runtime_put_sync(&pdev->dev); | |
| 844 | +error_context: | |
| 845 | + for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { | |
| 846 | + ctx = cal->ctx[i]; | |
| 847 | + if (ctx) | |
| 848 | + cal_ctx_v4l2_cleanup(ctx); | |
| 849 | + } | |
| 2382 | 850 | |
| 2383 | − return 0; | |
| 851 | + cal_media_cleanup(cal); | |
| 2384 | 852 | |
| 2385 | −runtime_disable: | |
| 2386 | − vb2_dma_contig_clear_max_seg_size(&pdev->dev); | |
| 853 | +error_camerarx: | |
| 854 | + for (i = 0; i < ARRAY_SIZE(cal->phy); i++) | |
| 855 | + cal_camerarx_destroy(cal->phy[i]); | |
| 2387 | 856 | |
| 857 | +error_pm_runtime: | |
| 2388 | 858 | pm_runtime_disable(&pdev->dev); |
| 2389 | − for (i = 0; i < CAL_NUM_CONTEXT; i++) { | |
| 2390 | − ctx = dev->ctx[i]; | |
| 2391 | − if (ctx) { | |
| 2392 | − v4l2_async_notifier_unregister(&ctx->notifier); | |
| 2393 | − v4l2_async_notifier_cleanup(&ctx->notifier); | |
| 2394 | − v4l2_ctrl_handler_free(&ctx->ctrl_handler); | |
| 2395 | − v4l2_device_unregister(&ctx->v4l2_dev); | |
| 2396 | − } | |
| 2397 | − } | |
| 2398 | 859 | |
| 2399 | 860 | return ret; |
| 2400 | 861 | } |
| 2401 | 862 | |
| 2402 | 863 | static int cal_remove(struct platform_device *pdev) |
| 2403 | 864 | { |
| 2404 | − struct cal_dev *dev = | |
| 2405 | − (struct cal_dev *)platform_get_drvdata(pdev); | |
| 2406 | − struct cal_ctx *ctx; | |
| 2407 | − int i; | |
| 865 | + struct cal_dev *cal = platform_get_drvdata(pdev); | |
| 866 | + unsigned int i; | |
| 2408 | 867 | |
| 2409 | − cal_dbg(1, dev, "Removing %s\n", CAL_MODULE_NAME); | |
| 868 | + cal_dbg(1, cal, "Removing %s\n", CAL_MODULE_NAME); | |
| 2410 | 869 | |
| 2411 | 870 | pm_runtime_get_sync(&pdev->dev); |
| 2412 | 871 | |
| 2413 | − for (i = 0; i < CAL_NUM_CONTEXT; i++) { | |
| 2414 | − ctx = dev->ctx[i]; | |
| 2415 | − if (ctx) { | |
| 2416 | − ctx_dbg(1, ctx, "unregistering %s\n", | |
| 2417 | − video_device_node_name(&ctx->vdev)); | |
| 2418 | − camerarx_phy_disable(ctx); | |
| 2419 | − v4l2_async_notifier_unregister(&ctx->notifier); | |
| 2420 | − v4l2_async_notifier_cleanup(&ctx->notifier); | |
| 2421 | − v4l2_ctrl_handler_free(&ctx->ctrl_handler); | |
| 2422 | − v4l2_device_unregister(&ctx->v4l2_dev); | |
| 2423 | − video_unregister_device(&ctx->vdev); | |
| 2424 | − } | |
| 872 | + cal_media_unregister(cal); | |
| 873 | + | |
| 874 | + for (i = 0; i < ARRAY_SIZE(cal->phy); i++) { | |
| 875 | + if (cal->phy[i]) | |
| 876 | + cal_camerarx_disable(cal->phy[i]); | |
| 2425 | 877 | } |
| 2426 | 878 | |
| 879 | + cal_media_cleanup(cal); | |
| 880 | + | |
| 881 | + for (i = 0; i < ARRAY_SIZE(cal->phy); i++) | |
| 882 | + cal_camerarx_destroy(cal->phy[i]); | |
| 883 | + | |
| 2427 | 884 | pm_runtime_put_sync(&pdev->dev); |
| 2428 | 885 | pm_runtime_disable(&pdev->dev); |
| 2429 | 886 | |
| 2430 | − vb2_dma_contig_clear_max_seg_size(&pdev->dev); | |
| 2431 | − | |
| 2432 | 887 | return 0; |
| 2433 | 888 | } |
| 2434 | 889 | |
| 2435 | 890 | static int cal_runtime_resume(struct device *dev) |
| 2436 | 891 | { |
| 2437 | − struct cal_dev *caldev = dev_get_drvdata(dev); | |
| 892 | + struct cal_dev *cal = dev_get_drvdata(dev); | |
| 2438 | 893 | |
| 2439 | − if (caldev->flags & DRA72_CAL_PRE_ES2_LDO_DISABLE) { | |
| 894 | + if (cal->data->flags & DRA72_CAL_PRE_ES2_LDO_DISABLE) { | |
| 2440 | 895 | /* |
| 2441 | 896 | * Apply errata on both port everytime we (re-)enable |
| 2442 | 897 | * the clock |
| 2443 | 898 | */ |
| 2444 | − i913_errata(caldev, 0); | |
| 2445 | − i913_errata(caldev, 1); | |
| 899 | + cal_camerarx_i913_errata(cal->phy[0]); | |
| 900 | + cal_camerarx_i913_errata(cal->phy[1]); | |
| 2446 | 901 | } |
| 2447 | 902 | |
| 2448 | 903 | return 0; |
| @@ -0,0 +1,267 @@ | ||
| 1 | +/* SPDX-License-Identifier: GPL-2.0-only */ | |
| 2 | +/* | |
| 3 | + * TI Camera Access Layer (CAL) | |
| 4 | + * | |
| 5 | + * Copyright (c) 2015-2020 Texas Instruments Inc. | |
| 6 | + * | |
| 7 | + * Authors: | |
| 8 | + * Benoit Parrot <bparrot@ti.com> | |
| 9 | + * Laurent Pinchart <laurent.pinchart@ideasonboard.com> | |
| 10 | + */ | |
| 11 | +#ifndef __TI_CAL_H__ | |
| 12 | +#define __TI_CAL_H__ | |
| 13 | + | |
| 14 | +#include <linux/bitfield.h> | |
| 15 | +#include <linux/io.h> | |
| 16 | +#include <linux/list.h> | |
| 17 | +#include <linux/mutex.h> | |
| 18 | +#include <linux/spinlock.h> | |
| 19 | +#include <linux/videodev2.h> | |
| 20 | + | |
| 21 | +#include <media/media-device.h> | |
| 22 | +#include <media/v4l2-async.h> | |
| 23 | +#include <media/v4l2-ctrls.h> | |
| 24 | +#include <media/v4l2-dev.h> | |
| 25 | +#include <media/v4l2-device.h> | |
| 26 | +#include <media/v4l2-fwnode.h> | |
| 27 | +#include <media/videobuf2-v4l2.h> | |
| 28 | + | |
| 29 | +#define CAL_MODULE_NAME "cal" | |
| 30 | +#define CAL_NUM_CONTEXT 2 | |
| 31 | +#define CAL_NUM_CSI2_PORTS 2 | |
| 32 | + | |
| 33 | +#define MAX_WIDTH_BYTES (8192 * 8) | |
| 34 | +#define MAX_HEIGHT_LINES 16383 | |
| 35 | + | |
| 36 | +struct device; | |
| 37 | +struct device_node; | |
| 38 | +struct resource; | |
| 39 | +struct regmap; | |
| 40 | +struct regmap_fied; | |
| 41 | +struct v4l2_subdev; | |
| 42 | + | |
| 43 | +/* CTRL_CORE_CAMERRX_CONTROL register field id */ | |
| 44 | +enum cal_camerarx_field { | |
| 45 | + F_CTRLCLKEN, | |
| 46 | + F_CAMMODE, | |
| 47 | + F_LANEENABLE, | |
| 48 | + F_CSI_MODE, | |
| 49 | + F_MAX_FIELDS, | |
| 50 | +}; | |
| 51 | + | |
| 52 | +struct cal_fmt { | |
| 53 | + u32 fourcc; | |
| 54 | + u32 code; | |
| 55 | + /* Bits per pixel */ | |
| 56 | + u8 bpp; | |
| 57 | +}; | |
| 58 | + | |
| 59 | +/* buffer for one video frame */ | |
| 60 | +struct cal_buffer { | |
| 61 | + /* common v4l buffer stuff -- must be first */ | |
| 62 | + struct vb2_v4l2_buffer vb; | |
| 63 | + struct list_head list; | |
| 64 | +}; | |
| 65 | + | |
| 66 | +struct cal_dmaqueue { | |
| 67 | + struct list_head active; | |
| 68 | +}; | |
| 69 | + | |
| 70 | +struct cal_camerarx_data { | |
| 71 | + struct { | |
| 72 | + unsigned int lsb; | |
| 73 | + unsigned int msb; | |
| 74 | + } fields[F_MAX_FIELDS]; | |
| 75 | + unsigned int num_lanes; | |
| 76 | +}; | |
| 77 | + | |
| 78 | +struct cal_data { | |
| 79 | + const struct cal_camerarx_data *camerarx; | |
| 80 | + unsigned int num_csi2_phy; | |
| 81 | + unsigned int flags; | |
| 82 | +}; | |
| 83 | + | |
| 84 | +/* | |
| 85 | + * The Camera Adaptation Layer (CAL) module is paired with one or more complex | |
| 86 | + * I/O PHYs (CAMERARX). It contains multiple instances of CSI-2, processing and | |
| 87 | + * DMA contexts. | |
| 88 | + * | |
| 89 | + * The cal_dev structure represents the whole subsystem, including the CAL and | |
| 90 | + * the CAMERARX instances. Instances of struct cal_dev are named cal through the | |
| 91 | + * driver. | |
| 92 | + * | |
| 93 | + * The cal_camerarx structure represents one CAMERARX instance. Instances of | |
| 94 | + * cal_camerarx are named phy through the driver. | |
| 95 | + * | |
| 96 | + * The cal_ctx structure represents the combination of one CSI-2 context, one | |
| 97 | + * processing context and one DMA context. Instance of struct cal_ctx are named | |
| 98 | + * ctx through the driver. | |
| 99 | + */ | |
| 100 | + | |
| 101 | +struct cal_camerarx { | |
| 102 | + void __iomem *base; | |
| 103 | + struct resource *res; | |
| 104 | + struct device *dev; | |
| 105 | + struct regmap_field *fields[F_MAX_FIELDS]; | |
| 106 | + | |
| 107 | + struct cal_dev *cal; | |
| 108 | + unsigned int instance; | |
| 109 | + | |
| 110 | + struct v4l2_fwnode_endpoint endpoint; | |
| 111 | + struct device_node *sensor_node; | |
| 112 | + struct v4l2_subdev *sensor; | |
| 113 | +}; | |
| 114 | + | |
| 115 | +struct cal_dev { | |
| 116 | + struct clk *fclk; | |
| 117 | + int irq; | |
| 118 | + void __iomem *base; | |
| 119 | + struct resource *res; | |
| 120 | + struct device *dev; | |
| 121 | + | |
| 122 | + const struct cal_data *data; | |
| 123 | + u32 revision; | |
| 124 | + | |
| 125 | + /* Control Module handle */ | |
| 126 | + struct regmap *syscon_camerrx; | |
| 127 | + u32 syscon_camerrx_offset; | |
| 128 | + | |
| 129 | + /* Camera Core Module handle */ | |
| 130 | + struct cal_camerarx *phy[CAL_NUM_CSI2_PORTS]; | |
| 131 | + | |
| 132 | + struct cal_ctx *ctx[CAL_NUM_CONTEXT]; | |
| 133 | + | |
| 134 | + struct media_device mdev; | |
| 135 | + struct v4l2_device v4l2_dev; | |
| 136 | + struct v4l2_async_notifier notifier; | |
| 137 | +}; | |
| 138 | + | |
| 139 | +/* | |
| 140 | + * There is one cal_ctx structure for each camera core context. | |
| 141 | + */ | |
| 142 | +struct cal_ctx { | |
| 143 | + struct v4l2_ctrl_handler ctrl_handler; | |
| 144 | + struct video_device vdev; | |
| 145 | + struct media_pad pad; | |
| 146 | + | |
| 147 | + struct cal_dev *cal; | |
| 148 | + struct cal_camerarx *phy; | |
| 149 | + | |
| 150 | + /* v4l2_ioctl mutex */ | |
| 151 | + struct mutex mutex; | |
| 152 | + /* v4l2 buffers lock */ | |
| 153 | + spinlock_t slock; | |
| 154 | + | |
| 155 | + struct cal_dmaqueue vidq; | |
| 156 | + | |
| 157 | + /* video capture */ | |
| 158 | + const struct cal_fmt *fmt; | |
| 159 | + /* Used to store current pixel format */ | |
| 160 | + struct v4l2_format v_fmt; | |
| 161 | + /* Used to store current mbus frame format */ | |
| 162 | + struct v4l2_mbus_framefmt m_fmt; | |
| 163 | + | |
| 164 | + /* Current subdev enumerated format */ | |
| 165 | + const struct cal_fmt **active_fmt; | |
| 166 | + unsigned int num_active_fmt; | |
| 167 | + | |
| 168 | + unsigned int sequence; | |
| 169 | + struct vb2_queue vb_vidq; | |
| 170 | + unsigned int index; | |
| 171 | + unsigned int cport; | |
| 172 | + | |
| 173 | + /* Pointer pointing to current v4l2_buffer */ | |
| 174 | + struct cal_buffer *cur_frm; | |
| 175 | + /* Pointer pointing to next v4l2_buffer */ | |
| 176 | + struct cal_buffer *next_frm; | |
| 177 | + | |
| 178 | + bool dma_act; | |
| 179 | +}; | |
| 180 | + | |
| 181 | +extern unsigned int cal_debug; | |
| 182 | +extern int cal_video_nr; | |
| 183 | + | |
| 184 | +#define cal_dbg(level, cal, fmt, arg...) \ | |
| 185 | + do { \ | |
| 186 | + if (cal_debug >= (level)) \ | |
| 187 | + dev_printk(KERN_DEBUG, (cal)->dev, fmt, ##arg); \ | |
| 188 | + } while (0) | |
| 189 | +#define cal_info(cal, fmt, arg...) \ | |
| 190 | + dev_info((cal)->dev, fmt, ##arg) | |
| 191 | +#define cal_err(cal, fmt, arg...) \ | |
| 192 | + dev_err((cal)->dev, fmt, ##arg) | |
| 193 | + | |
| 194 | +#define ctx_dbg(level, ctx, fmt, arg...) \ | |
| 195 | + cal_dbg(level, (ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg) | |
| 196 | +#define ctx_info(ctx, fmt, arg...) \ | |
| 197 | + cal_info((ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg) | |
| 198 | +#define ctx_err(ctx, fmt, arg...) \ | |
| 199 | + cal_err((ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg) | |
| 200 | + | |
| 201 | +#define phy_dbg(level, phy, fmt, arg...) \ | |
| 202 | + cal_dbg(level, (phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) | |
| 203 | +#define phy_info(phy, fmt, arg...) \ | |
| 204 | + cal_info((phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) | |
| 205 | +#define phy_err(phy, fmt, arg...) \ | |
| 206 | + cal_err((phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) | |
| 207 | + | |
| 208 | +static inline u32 cal_read(struct cal_dev *cal, u32 offset) | |
| 209 | +{ | |
| 210 | + return ioread32(cal->base + offset); | |
| 211 | +} | |
| 212 | + | |
| 213 | +static inline void cal_write(struct cal_dev *cal, u32 offset, u32 val) | |
| 214 | +{ | |
| 215 | + iowrite32(val, cal->base + offset); | |
| 216 | +} | |
| 217 | + | |
| 218 | +static inline u32 cal_read_field(struct cal_dev *cal, u32 offset, u32 mask) | |
| 219 | +{ | |
| 220 | + return FIELD_GET(mask, cal_read(cal, offset)); | |
| 221 | +} | |
| 222 | + | |
| 223 | +static inline void cal_write_field(struct cal_dev *cal, u32 offset, u32 value, | |
| 224 | + u32 mask) | |
| 225 | +{ | |
| 226 | + u32 val = cal_read(cal, offset); | |
| 227 | + | |
| 228 | + val &= ~mask; | |
| 229 | + val |= FIELD_PREP(mask, value); | |
| 230 | + cal_write(cal, offset, val); | |
| 231 | +} | |
| 232 | + | |
| 233 | +static inline void cal_set_field(u32 *valp, u32 field, u32 mask) | |
| 234 | +{ | |
| 235 | + u32 val = *valp; | |
| 236 | + | |
| 237 | + val &= ~mask; | |
| 238 | + val |= (field << __ffs(mask)) & mask; | |
| 239 | + *valp = val; | |
| 240 | +} | |
| 241 | + | |
| 242 | +void cal_quickdump_regs(struct cal_dev *cal); | |
| 243 | + | |
| 244 | +void cal_camerarx_disable(struct cal_camerarx *phy); | |
| 245 | +int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt); | |
| 246 | +void cal_camerarx_stop(struct cal_camerarx *phy); | |
| 247 | +void cal_camerarx_enable_irqs(struct cal_camerarx *phy); | |
| 248 | +void cal_camerarx_disable_irqs(struct cal_camerarx *phy); | |
| 249 | +void cal_camerarx_ppi_enable(struct cal_camerarx *phy); | |
| 250 | +void cal_camerarx_ppi_disable(struct cal_camerarx *phy); | |
| 251 | +void cal_camerarx_i913_errata(struct cal_camerarx *phy); | |
| 252 | +struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, | |
| 253 | + unsigned int instance); | |
| 254 | +void cal_camerarx_destroy(struct cal_camerarx *phy); | |
| 255 | + | |
| 256 | +void cal_ctx_csi2_config(struct cal_ctx *ctx); | |
| 257 | +void cal_ctx_pix_proc_config(struct cal_ctx *ctx); | |
| 258 | +void cal_ctx_wr_dma_config(struct cal_ctx *ctx, unsigned int width, | |
| 259 | + unsigned int height); | |
| 260 | +void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr); | |
| 261 | + | |
| 262 | +int cal_ctx_v4l2_register(struct cal_ctx *ctx); | |
| 263 | +void cal_ctx_v4l2_unregister(struct cal_ctx *ctx); | |
| 264 | +int cal_ctx_v4l2_init(struct cal_ctx *ctx); | |
| 265 | +void cal_ctx_v4l2_cleanup(struct cal_ctx *ctx); | |
| 266 | + | |
| 267 | +#endif /* __TI_CAL_H__ */ | |
| @@ -34,19 +34,17 @@ · 33 unmodified lines | ||
| 34 | 34 | */ |
| 35 | 35 | #define DRA72_CAL_PRE_ES2_LDO_DISABLE BIT(0) |
| 36 | 36 | |
| 37 | −#define CAL_NUM_CSI2_PORTS 2 | |
| 38 | − | |
| 39 | 37 | /* CAL register offsets */ |
| 40 | 38 | |
| 41 | 39 | #define CAL_HL_REVISION 0x0000 |
| 42 | 40 | #define CAL_HL_HWINFO 0x0004 |
| 43 | 41 | #define CAL_HL_SYSCONFIG 0x0010 |
| 44 | 42 | #define CAL_HL_IRQ_EOI 0x001c |
| 45 | −#define CAL_HL_IRQSTATUS_RAW(m) (0x20U + ((m-1) * 0x10U)) | |
| 46 | −#define CAL_HL_IRQSTATUS(m) (0x24U + ((m-1) * 0x10U)) | |
| 47 | −#define CAL_HL_IRQENABLE_SET(m) (0x28U + ((m-1) * 0x10U)) | |
| 48 | −#define CAL_HL_IRQENABLE_CLR(m) (0x2cU + ((m-1) * 0x10U)) | |
| 49 | −#define CAL_PIX_PROC(m) (0xc0U + ((m-1) * 0x4U)) | |
| 43 | +#define CAL_HL_IRQSTATUS_RAW(m) (0x20U + (m) * 0x10U) | |
| 44 | +#define CAL_HL_IRQSTATUS(m) (0x24U + (m) * 0x10U) | |
| 45 | +#define CAL_HL_IRQENABLE_SET(m) (0x28U + (m) * 0x10U) | |
| 46 | +#define CAL_HL_IRQENABLE_CLR(m) (0x2cU + (m) * 0x10U) | |
| 47 | +#define CAL_PIX_PROC(m) (0xc0U + (m) * 0x4U) | |
| 50 | 48 | #define CAL_CTRL 0x100 |
| 51 | 49 | #define CAL_CTRL1 0x104 |
| 52 | 50 | #define CAL_LINE_NUMBER_EVT 0x108 |
| @@ -62,34 +60,34 @@ · 9 unmodified lines | ||
| 62 | 60 | #define CAL_RD_DMA_INIT_ADDR 0x154 |
| 63 | 61 | #define CAL_RD_DMA_INIT_OFST 0x168 |
| 64 | 62 | #define CAL_RD_DMA_CTRL2 0x16c |
| 65 | −#define CAL_WR_DMA_CTRL(m) (0x200U + ((m-1) * 0x10U)) | |
| 66 | −#define CAL_WR_DMA_ADDR(m) (0x204U + ((m-1) * 0x10U)) | |
| 67 | −#define CAL_WR_DMA_OFST(m) (0x208U + ((m-1) * 0x10U)) | |
| 68 | −#define CAL_WR_DMA_XSIZE(m) (0x20cU + ((m-1) * 0x10U)) | |
| 69 | −#define CAL_CSI2_PPI_CTRL(m) (0x300U + ((m-1) * 0x80U)) | |
| 70 | −#define CAL_CSI2_COMPLEXIO_CFG(m) (0x304U + ((m-1) * 0x80U)) | |
| 71 | −#define CAL_CSI2_COMPLEXIO_IRQSTATUS(m) (0x308U + ((m-1) * 0x80U)) | |
| 72 | −#define CAL_CSI2_SHORT_PACKET(m) (0x30cU + ((m-1) * 0x80U)) | |
| 73 | −#define CAL_CSI2_COMPLEXIO_IRQENABLE(m) (0x310U + ((m-1) * 0x80U)) | |
| 74 | −#define CAL_CSI2_TIMING(m) (0x314U + ((m-1) * 0x80U)) | |
| 75 | −#define CAL_CSI2_VC_IRQENABLE(m) (0x318U + ((m-1) * 0x80U)) | |
| 76 | −#define CAL_CSI2_VC_IRQSTATUS(m) (0x328U + ((m-1) * 0x80U)) | |
| 77 | −#define CAL_CSI2_CTX0(m) (0x330U + ((m-1) * 0x80U)) | |
| 78 | −#define CAL_CSI2_CTX1(m) (0x334U + ((m-1) * 0x80U)) | |
| 79 | −#define CAL_CSI2_CTX2(m) (0x338U + ((m-1) * 0x80U)) | |
| 80 | −#define CAL_CSI2_CTX3(m) (0x33cU + ((m-1) * 0x80U)) | |
| 81 | −#define CAL_CSI2_CTX4(m) (0x340U + ((m-1) * 0x80U)) | |
| 82 | −#define CAL_CSI2_CTX5(m) (0x344U + ((m-1) * 0x80U)) | |
| 83 | −#define CAL_CSI2_CTX6(m) (0x348U + ((m-1) * 0x80U)) | |
| 84 | −#define CAL_CSI2_CTX7(m) (0x34cU + ((m-1) * 0x80U)) | |
| 85 | −#define CAL_CSI2_STATUS0(m) (0x350U + ((m-1) * 0x80U)) | |
| 86 | −#define CAL_CSI2_STATUS1(m) (0x354U + ((m-1) * 0x80U)) | |
| 87 | −#define CAL_CSI2_STATUS2(m) (0x358U + ((m-1) * 0x80U)) | |
| 88 | −#define CAL_CSI2_STATUS3(m) (0x35cU + ((m-1) * 0x80U)) | |
| 89 | −#define CAL_CSI2_STATUS4(m) (0x360U + ((m-1) * 0x80U)) | |
| 90 | −#define CAL_CSI2_STATUS5(m) (0x364U + ((m-1) * 0x80U)) | |
| 91 | −#define CAL_CSI2_STATUS6(m) (0x368U + ((m-1) * 0x80U)) | |
| 92 | −#define CAL_CSI2_STATUS7(m) (0x36cU + ((m-1) * 0x80U)) | |
| 63 | +#define CAL_WR_DMA_CTRL(m) (0x200U + (m) * 0x10U) | |
| 64 | +#define CAL_WR_DMA_ADDR(m) (0x204U + (m) * 0x10U) | |
| 65 | +#define CAL_WR_DMA_OFST(m) (0x208U + (m) * 0x10U) | |
| 66 | +#define CAL_WR_DMA_XSIZE(m) (0x20cU + (m) * 0x10U) | |
| 67 | +#define CAL_CSI2_PPI_CTRL(m) (0x300U + (m) * 0x80U) | |
| 68 | +#define CAL_CSI2_COMPLEXIO_CFG(m) (0x304U + (m) * 0x80U) | |
| 69 | +#define CAL_CSI2_COMPLEXIO_IRQSTATUS(m) (0x308U + (m) * 0x80U) | |
| 70 | +#define CAL_CSI2_SHORT_PACKET(m) (0x30cU + (m) * 0x80U) | |
| 71 | +#define CAL_CSI2_COMPLEXIO_IRQENABLE(m) (0x310U + (m) * 0x80U) | |
| 72 | +#define CAL_CSI2_TIMING(m) (0x314U + (m) * 0x80U) | |
| 73 | +#define CAL_CSI2_VC_IRQENABLE(m) (0x318U + (m) * 0x80U) | |
| 74 | +#define CAL_CSI2_VC_IRQSTATUS(m) (0x328U + (m) * 0x80U) | |
| 75 | +#define CAL_CSI2_CTX0(m) (0x330U + (m) * 0x80U) | |
| 76 | +#define CAL_CSI2_CTX1(m) (0x334U + (m) * 0x80U) | |
| 77 | +#define CAL_CSI2_CTX2(m) (0x338U + (m) * 0x80U) | |
| 78 | +#define CAL_CSI2_CTX3(m) (0x33cU + (m) * 0x80U) | |
| 79 | +#define CAL_CSI2_CTX4(m) (0x340U + (m) * 0x80U) | |
| 80 | +#define CAL_CSI2_CTX5(m) (0x344U + (m) * 0x80U) | |
| 81 | +#define CAL_CSI2_CTX6(m) (0x348U + (m) * 0x80U) | |
| 82 | +#define CAL_CSI2_CTX7(m) (0x34cU + (m) * 0x80U) | |
| 83 | +#define CAL_CSI2_STATUS0(m) (0x350U + (m) * 0x80U) | |
| 84 | +#define CAL_CSI2_STATUS1(m) (0x354U + (m) * 0x80U) | |
| 85 | +#define CAL_CSI2_STATUS2(m) (0x358U + (m) * 0x80U) | |
| 86 | +#define CAL_CSI2_STATUS3(m) (0x35cU + (m) * 0x80U) | |
| 87 | +#define CAL_CSI2_STATUS4(m) (0x360U + (m) * 0x80U) | |
| 88 | +#define CAL_CSI2_STATUS5(m) (0x364U + (m) * 0x80U) | |
| 89 | +#define CAL_CSI2_STATUS6(m) (0x368U + (m) * 0x80U) | |
| 90 | +#define CAL_CSI2_STATUS7(m) (0x36cU + (m) * 0x80U) | |
| 93 | 91 | |
| 94 | 92 | /* CAL CSI2 PHY register offsets */ |
| 95 | 93 | #define CAL_CSI2_PHY_REG0 0x000 |
| @@ -141,12 +139,12 @@ · 45 unmodified lines | ||
| 141 | 139 | #define CAL_HL_IRQ_EOI_LINE_NUMBER_READ0 0 |
| 142 | 140 | #define CAL_HL_IRQ_EOI_LINE_NUMBER_EOI0 0 |
| 143 | 141 | |
| 144 | −#define CAL_HL_IRQ_MASK(m) BIT((m) - 1) | |
| 142 | +#define CAL_HL_IRQ_MASK(m) BIT(m) | |
| 145 | 143 | |
| 146 | 144 | #define CAL_HL_IRQ_OCPO_ERR_MASK BIT(6) |
| 147 | 145 | |
| 148 | −#define CAL_HL_IRQ_CIO_MASK(i) BIT(16 + ((i) - 1) * 8) | |
| 149 | −#define CAL_HL_IRQ_VC_MASK(i) BIT(17 + ((i) - 1) * 8) | |
| 146 | +#define CAL_HL_IRQ_CIO_MASK(i) BIT(16 + (i) * 8) | |
| 147 | +#define CAL_HL_IRQ_VC_MASK(i) BIT(17 + (i) * 8) | |
| 150 | 148 | |
| 151 | 149 | #define CAL_PIX_PROC_EN_MASK BIT(0) |
| 152 | 150 | #define CAL_PIX_PROC_EXTRACT_MASK GENMASK(4, 1) |
| @@ -1576,7 +1576,7 @@ static int vpe_g_fmt(struct file *file, void *priv, struct v4l2_format *f) · 1575 unmodified lines | ||
| 1576 | 1576 | |
| 1577 | 1577 | *f = q_data->format; |
| 1578 | 1578 | |
| 1579 | − if (!V4L2_TYPE_IS_OUTPUT(f->type)) { | |
| 1579 | + if (V4L2_TYPE_IS_CAPTURE(f->type)) { | |
| 1580 | 1580 | struct vpe_q_data *s_q_data; |
| 1581 | 1581 | struct v4l2_pix_format_mplane *spix; |
| 1582 | 1582 | |
| @@ -431,6 +431,8 @@ vsp1_dl_cmd_pool_create(struct vsp1_device *vsp1, enum vsp1_extcmd_type type, · 430 unmodified lines | ||
| 431 | 431 | if (!pool) |
| 432 | 432 | return NULL; |
| 433 | 433 | |
| 434 | + pool->vsp1 = vsp1; | |
| 435 | + | |
| 434 | 436 | spin_lock_init(&pool->lock); |
| 435 | 437 | INIT_LIST_HEAD(&pool->free); |
| 436 | 438 | |
| @@ -12,6 +12,13 @@ config VIDEO_XILINX · 11 unmodified lines | ||
| 12 | 12 | |
| 13 | 13 | if VIDEO_XILINX |
| 14 | 14 | |
| 15 | +config VIDEO_XILINX_CSI2RXSS | |
| 16 | + tristate "Xilinx CSI-2 Rx Subsystem" | |
| 17 | + help | |
| 18 | + Driver for Xilinx MIPI CSI-2 Rx Subsystem. This is a V4L sub-device | |
| 19 | + based driver that takes input from CSI-2 Tx source and converts | |
| 20 | + it into an AXI4-Stream. | |
| 21 | + | |
| 15 | 22 | config VIDEO_XILINX_TPG |
| 16 | 23 | tristate "Xilinx Video Test Pattern Generator" |
| 17 | 24 | depends on VIDEO_XILINX |
| @@ -3,5 +3,6 @@ · 2 unmodified lines | ||
| 3 | 3 | xilinx-video-objs += xilinx-dma.o xilinx-vip.o xilinx-vipp.o |
| 4 | 4 | |
| 5 | 5 | obj-$(CONFIG_VIDEO_XILINX) += xilinx-video.o |
| 6 | +obj-$(CONFIG_VIDEO_XILINX_CSI2RXSS) += xilinx-csi2rxss.o | |
| 6 | 7 | obj-$(CONFIG_VIDEO_XILINX_TPG) += xilinx-tpg.o |
| 7 | 8 | obj-$(CONFIG_VIDEO_XILINX_VTC) += xilinx-vtc.o |
| @@ -0,0 +1,1111 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +/* | |
| 3 | + * Driver for Xilinx MIPI CSI-2 Rx Subsystem | |
| 4 | + * | |
| 5 | + * Copyright (C) 2016 - 2020 Xilinx, Inc. | |
| 6 | + * | |
| 7 | + * Contacts: Vishal Sagar <vishal.sagar@xilinx.com> | |
| 8 | + * | |
| 9 | + */ | |
| 10 | +#include <linux/clk.h> | |
| 11 | +#include <linux/delay.h> | |
| 12 | +#include <linux/gpio/consumer.h> | |
| 13 | +#include <linux/interrupt.h> | |
| 14 | +#include <linux/module.h> | |
| 15 | +#include <linux/mutex.h> | |
| 16 | +#include <linux/of.h> | |
| 17 | +#include <linux/of_irq.h> | |
| 18 | +#include <linux/platform_device.h> | |
| 19 | +#include <linux/v4l2-subdev.h> | |
| 20 | +#include <media/media-entity.h> | |
| 21 | +#include <media/v4l2-common.h> | |
| 22 | +#include <media/v4l2-ctrls.h> | |
| 23 | +#include <media/v4l2-fwnode.h> | |
| 24 | +#include <media/v4l2-subdev.h> | |
| 25 | +#include "xilinx-vip.h" | |
| 26 | + | |
| 27 | +/* Register register map */ | |
| 28 | +#define XCSI_CCR_OFFSET 0x00 | |
| 29 | +#define XCSI_CCR_SOFTRESET BIT(1) | |
| 30 | +#define XCSI_CCR_ENABLE BIT(0) | |
| 31 | + | |
| 32 | +#define XCSI_PCR_OFFSET 0x04 | |
| 33 | +#define XCSI_PCR_MAXLANES_MASK GENMASK(4, 3) | |
| 34 | +#define XCSI_PCR_ACTLANES_MASK GENMASK(1, 0) | |
| 35 | + | |
| 36 | +#define XCSI_CSR_OFFSET 0x10 | |
| 37 | +#define XCSI_CSR_PKTCNT GENMASK(31, 16) | |
| 38 | +#define XCSI_CSR_SPFIFOFULL BIT(3) | |
| 39 | +#define XCSI_CSR_SPFIFONE BIT(2) | |
| 40 | +#define XCSI_CSR_SLBF BIT(1) | |
| 41 | +#define XCSI_CSR_RIPCD BIT(0) | |
| 42 | + | |
| 43 | +#define XCSI_GIER_OFFSET 0x20 | |
| 44 | +#define XCSI_GIER_GIE BIT(0) | |
| 45 | + | |
| 46 | +#define XCSI_ISR_OFFSET 0x24 | |
| 47 | +#define XCSI_IER_OFFSET 0x28 | |
| 48 | + | |
| 49 | +#define XCSI_ISR_FR BIT(31) | |
| 50 | +#define XCSI_ISR_VCXFE BIT(30) | |
| 51 | +#define XCSI_ISR_WCC BIT(22) | |
| 52 | +#define XCSI_ISR_ILC BIT(21) | |
| 53 | +#define XCSI_ISR_SPFIFOF BIT(20) | |
| 54 | +#define XCSI_ISR_SPFIFONE BIT(19) | |
| 55 | +#define XCSI_ISR_SLBF BIT(18) | |
| 56 | +#define XCSI_ISR_STOP BIT(17) | |
| 57 | +#define XCSI_ISR_SOTERR BIT(13) | |
| 58 | +#define XCSI_ISR_SOTSYNCERR BIT(12) | |
| 59 | +#define XCSI_ISR_ECC2BERR BIT(11) | |
| 60 | +#define XCSI_ISR_ECC1BERR BIT(10) | |
| 61 | +#define XCSI_ISR_CRCERR BIT(9) | |
| 62 | +#define XCSI_ISR_DATAIDERR BIT(8) | |
| 63 | +#define XCSI_ISR_VC3FSYNCERR BIT(7) | |
| 64 | +#define XCSI_ISR_VC3FLVLERR BIT(6) | |
| 65 | +#define XCSI_ISR_VC2FSYNCERR BIT(5) | |
| 66 | +#define XCSI_ISR_VC2FLVLERR BIT(4) | |
| 67 | +#define XCSI_ISR_VC1FSYNCERR BIT(3) | |
| 68 | +#define XCSI_ISR_VC1FLVLERR BIT(2) | |
| 69 | +#define XCSI_ISR_VC0FSYNCERR BIT(1) | |
| 70 | +#define XCSI_ISR_VC0FLVLERR BIT(0) | |
| 71 | + | |
| 72 | +#define XCSI_ISR_ALLINTR_MASK (0xc07e3fff) | |
| 73 | + | |
| 74 | +/* | |
| 75 | + * Removed VCXFE mask as it doesn't exist in IER | |
| 76 | + * Removed STOP state irq as this will keep driver in irq handler only | |
| 77 | + */ | |
| 78 | +#define XCSI_IER_INTR_MASK (XCSI_ISR_ALLINTR_MASK &\ | |
| 79 | + ~(XCSI_ISR_STOP | XCSI_ISR_VCXFE)) | |
| 80 | + | |
| 81 | +#define XCSI_SPKTR_OFFSET 0x30 | |
| 82 | +#define XCSI_SPKTR_DATA GENMASK(23, 8) | |
| 83 | +#define XCSI_SPKTR_VC GENMASK(7, 6) | |
| 84 | +#define XCSI_SPKTR_DT GENMASK(5, 0) | |
| 85 | +#define XCSI_SPKT_FIFO_DEPTH 31 | |
| 86 | + | |
| 87 | +#define XCSI_VCXR_OFFSET 0x34 | |
| 88 | +#define XCSI_VCXR_VCERR GENMASK(23, 0) | |
| 89 | +#define XCSI_VCXR_FSYNCERR BIT(1) | |
| 90 | +#define XCSI_VCXR_FLVLERR BIT(0) | |
| 91 | + | |
| 92 | +#define XCSI_CLKINFR_OFFSET 0x3C | |
| 93 | +#define XCSI_CLKINFR_STOP BIT(1) | |
| 94 | + | |
| 95 | +#define XCSI_DLXINFR_OFFSET 0x40 | |
| 96 | +#define XCSI_DLXINFR_STOP BIT(5) | |
| 97 | +#define XCSI_DLXINFR_SOTERR BIT(1) | |
| 98 | +#define XCSI_DLXINFR_SOTSYNCERR BIT(0) | |
| 99 | +#define XCSI_MAXDL_COUNT 0x4 | |
| 100 | + | |
| 101 | +#define XCSI_VCXINF1R_OFFSET 0x60 | |
| 102 | +#define XCSI_VCXINF1R_LINECOUNT GENMASK(31, 16) | |
| 103 | +#define XCSI_VCXINF1R_LINECOUNT_SHIFT 16 | |
| 104 | +#define XCSI_VCXINF1R_BYTECOUNT GENMASK(15, 0) | |
| 105 | + | |
| 106 | +#define XCSI_VCXINF2R_OFFSET 0x64 | |
| 107 | +#define XCSI_VCXINF2R_DT GENMASK(5, 0) | |
| 108 | +#define XCSI_MAXVCX_COUNT 16 | |
| 109 | + | |
| 110 | +/* | |
| 111 | + * Sink pad connected to sensor source pad. | |
| 112 | + * Source pad connected to next module like demosaic. | |
| 113 | + */ | |
| 114 | +#define XCSI_MEDIA_PADS 2 | |
| 115 | +#define XCSI_DEFAULT_WIDTH 1920 | |
| 116 | +#define XCSI_DEFAULT_HEIGHT 1080 | |
| 117 | + | |
| 118 | +/* MIPI CSI-2 Data Types from spec */ | |
| 119 | +#define XCSI_DT_YUV4228B 0x1e | |
| 120 | +#define XCSI_DT_YUV42210B 0x1f | |
| 121 | +#define XCSI_DT_RGB444 0x20 | |
| 122 | +#define XCSI_DT_RGB555 0x21 | |
| 123 | +#define XCSI_DT_RGB565 0x22 | |
| 124 | +#define XCSI_DT_RGB666 0x23 | |
| 125 | +#define XCSI_DT_RGB888 0x24 | |
| 126 | +#define XCSI_DT_RAW6 0x28 | |
| 127 | +#define XCSI_DT_RAW7 0x29 | |
| 128 | +#define XCSI_DT_RAW8 0x2a | |
| 129 | +#define XCSI_DT_RAW10 0x2b | |
| 130 | +#define XCSI_DT_RAW12 0x2c | |
| 131 | +#define XCSI_DT_RAW14 0x2d | |
| 132 | +#define XCSI_DT_RAW16 0x2e | |
| 133 | +#define XCSI_DT_RAW20 0x2f | |
| 134 | + | |
| 135 | +#define XCSI_VCX_START 4 | |
| 136 | +#define XCSI_MAX_VC 4 | |
| 137 | +#define XCSI_MAX_VCX 16 | |
| 138 | + | |
| 139 | +#define XCSI_NEXTREG_OFFSET 4 | |
| 140 | + | |
| 141 | +/* There are 2 events frame sync and frame level error per VC */ | |
| 142 | +#define XCSI_VCX_NUM_EVENTS ((XCSI_MAX_VCX - XCSI_MAX_VC) * 2) | |
| 143 | + | |
| 144 | +/** | |
| 145 | + * struct xcsi2rxss_event - Event log structure | |
| 146 | + * @mask: Event mask | |
| 147 | + * @name: Name of the event | |
| 148 | + */ | |
| 149 | +struct xcsi2rxss_event { | |
| 150 | + u32 mask; | |
| 151 | + const char *name; | |
| 152 | +}; | |
| 153 | + | |
| 154 | +static const struct xcsi2rxss_event xcsi2rxss_events[] = { | |
| 155 | + { XCSI_ISR_FR, "Frame Received" }, | |
| 156 | + { XCSI_ISR_VCXFE, "VCX Frame Errors" }, | |
| 157 | + { XCSI_ISR_WCC, "Word Count Errors" }, | |
| 158 | + { XCSI_ISR_ILC, "Invalid Lane Count Error" }, | |
| 159 | + { XCSI_ISR_SPFIFOF, "Short Packet FIFO OverFlow Error" }, | |
| 160 | + { XCSI_ISR_SPFIFONE, "Short Packet FIFO Not Empty" }, | |
| 161 | + { XCSI_ISR_SLBF, "Streamline Buffer Full Error" }, | |
| 162 | + { XCSI_ISR_STOP, "Lane Stop State" }, | |
| 163 | + { XCSI_ISR_SOTERR, "SOT Error" }, | |
| 164 | + { XCSI_ISR_SOTSYNCERR, "SOT Sync Error" }, | |
| 165 | + { XCSI_ISR_ECC2BERR, "2 Bit ECC Unrecoverable Error" }, | |
| 166 | + { XCSI_ISR_ECC1BERR, "1 Bit ECC Recoverable Error" }, | |
| 167 | + { XCSI_ISR_CRCERR, "CRC Error" }, | |
| 168 | + { XCSI_ISR_DATAIDERR, "Data Id Error" }, | |
| 169 | + { XCSI_ISR_VC3FSYNCERR, "Virtual Channel 3 Frame Sync Error" }, | |
| 170 | + { XCSI_ISR_VC3FLVLERR, "Virtual Channel 3 Frame Level Error" }, | |
| 171 | + { XCSI_ISR_VC2FSYNCERR, "Virtual Channel 2 Frame Sync Error" }, | |
| 172 | + { XCSI_ISR_VC2FLVLERR, "Virtual Channel 2 Frame Level Error" }, | |
| 173 | + { XCSI_ISR_VC1FSYNCERR, "Virtual Channel 1 Frame Sync Error" }, | |
| 174 | + { XCSI_ISR_VC1FLVLERR, "Virtual Channel 1 Frame Level Error" }, | |
| 175 | + { XCSI_ISR_VC0FSYNCERR, "Virtual Channel 0 Frame Sync Error" }, | |
| 176 | + { XCSI_ISR_VC0FLVLERR, "Virtual Channel 0 Frame Level Error" } | |
| 177 | +}; | |
| 178 | + | |
| 179 | +#define XCSI_NUM_EVENTS ARRAY_SIZE(xcsi2rxss_events) | |
| 180 | + | |
| 181 | +/* | |
| 182 | + * This table provides a mapping between CSI-2 Data type | |
| 183 | + * and media bus formats | |
| 184 | + */ | |
| 185 | +static const u32 xcsi2dt_mbus_lut[][2] = { | |
| 186 | + { XCSI_DT_YUV4228B, MEDIA_BUS_FMT_UYVY8_1X16 }, | |
| 187 | + { XCSI_DT_YUV42210B, MEDIA_BUS_FMT_UYVY10_1X20 }, | |
| 188 | + { XCSI_DT_RGB444, 0 }, | |
| 189 | + { XCSI_DT_RGB555, 0 }, | |
| 190 | + { XCSI_DT_RGB565, 0 }, | |
| 191 | + { XCSI_DT_RGB666, 0 }, | |
| 192 | + { XCSI_DT_RGB888, MEDIA_BUS_FMT_RBG888_1X24 }, | |
| 193 | + { XCSI_DT_RAW6, 0 }, | |
| 194 | + { XCSI_DT_RAW7, 0 }, | |
| 195 | + { XCSI_DT_RAW8, MEDIA_BUS_FMT_SRGGB8_1X8 }, | |
| 196 | + { XCSI_DT_RAW8, MEDIA_BUS_FMT_SBGGR8_1X8 }, | |
| 197 | + { XCSI_DT_RAW8, MEDIA_BUS_FMT_SGBRG8_1X8 }, | |
| 198 | + { XCSI_DT_RAW8, MEDIA_BUS_FMT_SGRBG8_1X8 }, | |
| 199 | + { XCSI_DT_RAW10, MEDIA_BUS_FMT_SRGGB10_1X10 }, | |
| 200 | + { XCSI_DT_RAW10, MEDIA_BUS_FMT_SBGGR10_1X10 }, | |
| 201 | + { XCSI_DT_RAW10, MEDIA_BUS_FMT_SGBRG10_1X10 }, | |
| 202 | + { XCSI_DT_RAW10, MEDIA_BUS_FMT_SGRBG10_1X10 }, | |
| 203 | + { XCSI_DT_RAW12, MEDIA_BUS_FMT_SRGGB12_1X12 }, | |
| 204 | + { XCSI_DT_RAW12, MEDIA_BUS_FMT_SBGGR12_1X12 }, | |
| 205 | + { XCSI_DT_RAW12, MEDIA_BUS_FMT_SGBRG12_1X12 }, | |
| 206 | + { XCSI_DT_RAW12, MEDIA_BUS_FMT_SGRBG12_1X12 }, | |
| 207 | + { XCSI_DT_RAW16, MEDIA_BUS_FMT_SRGGB16_1X16 }, | |
| 208 | + { XCSI_DT_RAW16, MEDIA_BUS_FMT_SBGGR16_1X16 }, | |
| 209 | + { XCSI_DT_RAW16, MEDIA_BUS_FMT_SGBRG16_1X16 }, | |
| 210 | + { XCSI_DT_RAW16, MEDIA_BUS_FMT_SGRBG16_1X16 }, | |
| 211 | + { XCSI_DT_RAW20, 0 }, | |
| 212 | +}; | |
| 213 | + | |
| 214 | +/** | |
| 215 | + * struct xcsi2rxss_state - CSI-2 Rx Subsystem device structure | |
| 216 | + * @subdev: The v4l2 subdev structure | |
| 217 | + * @format: Active V4L2 formats on each pad | |
| 218 | + * @default_format: Default V4L2 format | |
| 219 | + * @events: counter for events | |
| 220 | + * @vcx_events: counter for vcx_events | |
| 221 | + * @dev: Platform structure | |
| 222 | + * @rsubdev: Remote subdev connected to sink pad | |
| 223 | + * @rst_gpio: reset to video_aresetn | |
| 224 | + * @clks: array of clocks | |
| 225 | + * @iomem: Base address of subsystem | |
| 226 | + * @max_num_lanes: Maximum number of lanes present | |
| 227 | + * @datatype: Data type filter | |
| 228 | + * @lock: mutex for accessing this structure | |
| 229 | + * @pads: media pads | |
| 230 | + * @streaming: Flag for storing streaming state | |
| 231 | + * @enable_active_lanes: If number of active lanes can be modified | |
| 232 | + * @en_vcx: If more than 4 VC are enabled | |
| 233 | + * | |
| 234 | + * This structure contains the device driver related parameters | |
| 235 | + */ | |
| 236 | +struct xcsi2rxss_state { | |
| 237 | + struct v4l2_subdev subdev; | |
| 238 | + struct v4l2_mbus_framefmt format; | |
| 239 | + struct v4l2_mbus_framefmt default_format; | |
| 240 | + u32 events[XCSI_NUM_EVENTS]; | |
| 241 | + u32 vcx_events[XCSI_VCX_NUM_EVENTS]; | |
| 242 | + struct device *dev; | |
| 243 | + struct v4l2_subdev *rsubdev; | |
| 244 | + struct gpio_desc *rst_gpio; | |
| 245 | + struct clk_bulk_data *clks; | |
| 246 | + void __iomem *iomem; | |
| 247 | + u32 max_num_lanes; | |
| 248 | + u32 datatype; | |
| 249 | + /* used to protect access to this struct */ | |
| 250 | + struct mutex lock; | |
| 251 | + struct media_pad pads[XCSI_MEDIA_PADS]; | |
| 252 | + bool streaming; | |
| 253 | + bool enable_active_lanes; | |
| 254 | + bool en_vcx; | |
| 255 | +}; | |
| 256 | + | |
| 257 | +static const struct clk_bulk_data xcsi2rxss_clks[] = { | |
| 258 | + { .id = "lite_aclk" }, | |
| 259 | + { .id = "video_aclk" }, | |
| 260 | +}; | |
| 261 | + | |
| 262 | +static inline struct xcsi2rxss_state * | |
| 263 | +to_xcsi2rxssstate(struct v4l2_subdev *subdev) | |
| 264 | +{ | |
| 265 | + return container_of(subdev, struct xcsi2rxss_state, subdev); | |
| 266 | +} | |
| 267 | + | |
| 268 | +/* | |
| 269 | + * Register related operations | |
| 270 | + */ | |
| 271 | +static inline u32 xcsi2rxss_read(struct xcsi2rxss_state *xcsi2rxss, u32 addr) | |
| 272 | +{ | |
| 273 | + return ioread32(xcsi2rxss->iomem + addr); | |
| 274 | +} | |
| 275 | + | |
| 276 | +static inline void xcsi2rxss_write(struct xcsi2rxss_state *xcsi2rxss, u32 addr, | |
| 277 | + u32 value) | |
| 278 | +{ | |
| 279 | + iowrite32(value, xcsi2rxss->iomem + addr); | |
| 280 | +} | |
| 281 | + | |
| 282 | +static inline void xcsi2rxss_clr(struct xcsi2rxss_state *xcsi2rxss, u32 addr, | |
| 283 | + u32 clr) | |
| 284 | +{ | |
| 285 | + xcsi2rxss_write(xcsi2rxss, addr, | |
| 286 | + xcsi2rxss_read(xcsi2rxss, addr) & ~clr); | |
| 287 | +} | |
| 288 | + | |
| 289 | +static inline void xcsi2rxss_set(struct xcsi2rxss_state *xcsi2rxss, u32 addr, | |
| 290 | + u32 set) | |
| 291 | +{ | |
| 292 | + xcsi2rxss_write(xcsi2rxss, addr, xcsi2rxss_read(xcsi2rxss, addr) | set); | |
| 293 | +} | |
| 294 | + | |
| 295 | +/* | |
| 296 | + * This function returns the nth mbus for a data type. | |
| 297 | + * In case of error, mbus code returned is 0. | |
| 298 | + */ | |
| 299 | +static u32 xcsi2rxss_get_nth_mbus(u32 dt, u32 n) | |
| 300 | +{ | |
| 301 | + unsigned int i; | |
| 302 | + | |
| 303 | + for (i = 0; i < ARRAY_SIZE(xcsi2dt_mbus_lut); i++) { | |
| 304 | + if (xcsi2dt_mbus_lut[i][0] == dt) { | |
| 305 | + if (n-- == 0) | |
| 306 | + return xcsi2dt_mbus_lut[i][1]; | |
| 307 | + } | |
| 308 | + } | |
| 309 | + | |
| 310 | + return 0; | |
| 311 | +} | |
| 312 | + | |
| 313 | +/* This returns the data type for a media bus format else 0 */ | |
| 314 | +static u32 xcsi2rxss_get_dt(u32 mbus) | |
| 315 | +{ | |
| 316 | + unsigned int i; | |
| 317 | + | |
| 318 | + for (i = 0; i < ARRAY_SIZE(xcsi2dt_mbus_lut); i++) { | |
| 319 | + if (xcsi2dt_mbus_lut[i][1] == mbus) | |
| 320 | + return xcsi2dt_mbus_lut[i][0]; | |
| 321 | + } | |
| 322 | + | |
| 323 | + return 0; | |
| 324 | +} | |
| 325 | + | |
| 326 | +/** | |
| 327 | + * xcsi2rxss_soft_reset - Does a soft reset of the MIPI CSI-2 Rx Subsystem | |
| 328 | + * @state: Xilinx CSI-2 Rx Subsystem structure pointer | |
| 329 | + * | |
| 330 | + * Core takes less than 100 video clock cycles to reset. | |
| 331 | + * So a larger timeout value is chosen for margin. | |
| 332 | + * | |
| 333 | + * Return: 0 - on success OR -ETIME if reset times out | |
| 334 | + */ | |
| 335 | +static int xcsi2rxss_soft_reset(struct xcsi2rxss_state *state) | |
| 336 | +{ | |
| 337 | + u32 timeout = 1000; /* us */ | |
| 338 | + | |
| 339 | + xcsi2rxss_set(state, XCSI_CCR_OFFSET, XCSI_CCR_SOFTRESET); | |
| 340 | + | |
| 341 | + while (xcsi2rxss_read(state, XCSI_CSR_OFFSET) & XCSI_CSR_RIPCD) { | |
| 342 | + if (timeout == 0) { | |
| 343 | + dev_err(state->dev, "soft reset timed out!\n"); | |
| 344 | + return -ETIME; | |
| 345 | + } | |
| 346 | + | |
| 347 | + timeout--; | |
| 348 | + udelay(1); | |
| 349 | + } | |
| 350 | + | |
| 351 | + xcsi2rxss_clr(state, XCSI_CCR_OFFSET, XCSI_CCR_SOFTRESET); | |
| 352 | + return 0; | |
| 353 | +} | |
| 354 | + | |
| 355 | +static void xcsi2rxss_hard_reset(struct xcsi2rxss_state *state) | |
| 356 | +{ | |
| 357 | + if (!state->rst_gpio) | |
| 358 | + return; | |
| 359 | + | |
| 360 | + /* minimum of 40 dphy_clk_200M cycles */ | |
| 361 | + gpiod_set_value_cansleep(state->rst_gpio, 1); | |
| 362 | + usleep_range(1, 2); | |
| 363 | + gpiod_set_value_cansleep(state->rst_gpio, 0); | |
| 364 | +} | |
| 365 | + | |
| 366 | +static void xcsi2rxss_reset_event_counters(struct xcsi2rxss_state *state) | |
| 367 | +{ | |
| 368 | + unsigned int i; | |
| 369 | + | |
| 370 | + for (i = 0; i < XCSI_NUM_EVENTS; i++) | |
| 371 | + state->events[i] = 0; | |
| 372 | + | |
| 373 | + for (i = 0; i < XCSI_VCX_NUM_EVENTS; i++) | |
| 374 | + state->vcx_events[i] = 0; | |
| 375 | +} | |
| 376 | + | |
| 377 | +/* Print event counters */ | |
| 378 | +static void xcsi2rxss_log_counters(struct xcsi2rxss_state *state) | |
| 379 | +{ | |
| 380 | + struct device *dev = state->dev; | |
| 381 | + unsigned int i; | |
| 382 | + | |
| 383 | + for (i = 0; i < XCSI_NUM_EVENTS; i++) { | |
| 384 | + if (state->events[i] > 0) { | |
| 385 | + dev_info(dev, "%s events: %d\n", | |
| 386 | + xcsi2rxss_events[i].name, | |
| 387 | + state->events[i]); | |
| 388 | + } | |
| 389 | + } | |
| 390 | + | |
| 391 | + if (state->en_vcx) { | |
| 392 | + for (i = 0; i < XCSI_VCX_NUM_EVENTS; i++) { | |
| 393 | + if (state->vcx_events[i] > 0) { | |
| 394 | + dev_info(dev, | |
| 395 | + "VC %d Frame %s err vcx events: %d\n", | |
| 396 | + (i / 2) + XCSI_VCX_START, | |
| 397 | + i & 1 ? "Sync" : "Level", | |
| 398 | + state->vcx_events[i]); | |
| 399 | + } | |
| 400 | + } | |
| 401 | + } | |
| 402 | +} | |
| 403 | + | |
| 404 | +/** | |
| 405 | + * xcsi2rxss_log_status - Logs the status of the CSI-2 Receiver | |
| 406 | + * @sd: Pointer to V4L2 subdevice structure | |
| 407 | + * | |
| 408 | + * This function prints the current status of Xilinx MIPI CSI-2 | |
| 409 | + * | |
| 410 | + * Return: 0 on success | |
| 411 | + */ | |
| 412 | +static int xcsi2rxss_log_status(struct v4l2_subdev *sd) | |
| 413 | +{ | |
| 414 | + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); | |
| 415 | + struct device *dev = xcsi2rxss->dev; | |
| 416 | + u32 reg, data; | |
| 417 | + unsigned int i, max_vc; | |
| 418 | + | |
| 419 | + mutex_lock(&xcsi2rxss->lock); | |
| 420 | + | |
| 421 | + xcsi2rxss_log_counters(xcsi2rxss); | |
| 422 | + | |
| 423 | + dev_info(dev, "***** Core Status *****\n"); | |
| 424 | + data = xcsi2rxss_read(xcsi2rxss, XCSI_CSR_OFFSET); | |
| 425 | + dev_info(dev, "Short Packet FIFO Full = %s\n", | |
| 426 | + data & XCSI_CSR_SPFIFOFULL ? "true" : "false"); | |
| 427 | + dev_info(dev, "Short Packet FIFO Not Empty = %s\n", | |
| 428 | + data & XCSI_CSR_SPFIFONE ? "true" : "false"); | |
| 429 | + dev_info(dev, "Stream line buffer full = %s\n", | |
| 430 | + data & XCSI_CSR_SLBF ? "true" : "false"); | |
| 431 | + dev_info(dev, "Soft reset/Core disable in progress = %s\n", | |
| 432 | + data & XCSI_CSR_RIPCD ? "true" : "false"); | |
| 433 | + | |
| 434 | + /* Clk & Lane Info */ | |
| 435 | + dev_info(dev, "******** Clock Lane Info *********\n"); | |
| 436 | + data = xcsi2rxss_read(xcsi2rxss, XCSI_CLKINFR_OFFSET); | |
| 437 | + dev_info(dev, "Clock Lane in Stop State = %s\n", | |
| 438 | + data & XCSI_CLKINFR_STOP ? "true" : "false"); | |
| 439 | + | |
| 440 | + dev_info(dev, "******** Data Lane Info *********\n"); | |
| 441 | + dev_info(dev, "Lane\tSoT Error\tSoT Sync Error\tStop State\n"); | |
| 442 | + reg = XCSI_DLXINFR_OFFSET; | |
| 443 | + for (i = 0; i < XCSI_MAXDL_COUNT; i++) { | |
| 444 | + data = xcsi2rxss_read(xcsi2rxss, reg); | |
| 445 | + | |
| 446 | + dev_info(dev, "%d\t%s\t\t%s\t\t%s\n", i, | |
| 447 | + data & XCSI_DLXINFR_SOTERR ? "true" : "false", | |
| 448 | + data & XCSI_DLXINFR_SOTSYNCERR ? "true" : "false", | |
| 449 | + data & XCSI_DLXINFR_STOP ? "true" : "false"); | |
| 450 | + | |
| 451 | + reg += XCSI_NEXTREG_OFFSET; | |
| 452 | + } | |
| 453 | + | |
| 454 | + /* Virtual Channel Image Information */ | |
| 455 | + dev_info(dev, "********** Virtual Channel Info ************\n"); | |
| 456 | + dev_info(dev, "VC\tLine Count\tByte Count\tData Type\n"); | |
| 457 | + if (xcsi2rxss->en_vcx) | |
| 458 | + max_vc = XCSI_MAX_VCX; | |
| 459 | + else | |
| 460 | + max_vc = XCSI_MAX_VC; | |
| 461 | + | |
| 462 | + reg = XCSI_VCXINF1R_OFFSET; | |
| 463 | + for (i = 0; i < max_vc; i++) { | |
| 464 | + u32 line_count, byte_count, data_type; | |
| 465 | + | |
| 466 | + /* Get line and byte count from VCXINFR1 Register */ | |
| 467 | + data = xcsi2rxss_read(xcsi2rxss, reg); | |
| 468 | + byte_count = data & XCSI_VCXINF1R_BYTECOUNT; | |
| 469 | + line_count = data & XCSI_VCXINF1R_LINECOUNT; | |
| 470 | + line_count >>= XCSI_VCXINF1R_LINECOUNT_SHIFT; | |
| 471 | + | |
| 472 | + /* Get data type from VCXINFR2 Register */ | |
| 473 | + reg += XCSI_NEXTREG_OFFSET; | |
| 474 | + data = xcsi2rxss_read(xcsi2rxss, reg); | |
| 475 | + data_type = data & XCSI_VCXINF2R_DT; | |
| 476 | + | |
| 477 | + dev_info(dev, "%d\t%d\t\t%d\t\t0x%x\n", i, line_count, | |
| 478 | + byte_count, data_type); | |
| 479 | + | |
| 480 | + /* Move to next pair of VC Info registers */ | |
| 481 | + reg += XCSI_NEXTREG_OFFSET; | |
| 482 | + } | |
| 483 | + | |
| 484 | + mutex_unlock(&xcsi2rxss->lock); | |
| 485 | + | |
| 486 | + return 0; | |
| 487 | +} | |
| 488 | + | |
| 489 | +static struct v4l2_subdev *xcsi2rxss_get_remote_subdev(struct media_pad *local) | |
| 490 | +{ | |
| 491 | + struct media_pad *remote; | |
| 492 | + | |
| 493 | + remote = media_entity_remote_pad(local); | |
| 494 | + if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) | |
| 495 | + return NULL; | |
| 496 | + | |
| 497 | + return media_entity_to_v4l2_subdev(remote->entity); | |
| 498 | +} | |
| 499 | + | |
| 500 | +static int xcsi2rxss_start_stream(struct xcsi2rxss_state *state) | |
| 501 | +{ | |
| 502 | + int ret = 0; | |
| 503 | + | |
| 504 | + /* enable core */ | |
| 505 | + xcsi2rxss_set(state, XCSI_CCR_OFFSET, XCSI_CCR_ENABLE); | |
| 506 | + | |
| 507 | + ret = xcsi2rxss_soft_reset(state); | |
| 508 | + if (ret) { | |
| 509 | + state->streaming = false; | |
| 510 | + return ret; | |
| 511 | + } | |
| 512 | + | |
| 513 | + /* enable interrupts */ | |
| 514 | + xcsi2rxss_clr(state, XCSI_GIER_OFFSET, XCSI_GIER_GIE); | |
| 515 | + xcsi2rxss_write(state, XCSI_IER_OFFSET, XCSI_IER_INTR_MASK); | |
| 516 | + xcsi2rxss_set(state, XCSI_GIER_OFFSET, XCSI_GIER_GIE); | |
| 517 | + | |
| 518 | + state->streaming = true; | |
| 519 | + | |
| 520 | + state->rsubdev = | |
| 521 | + xcsi2rxss_get_remote_subdev(&state->pads[XVIP_PAD_SINK]); | |
| 522 | + | |
| 523 | + ret = v4l2_subdev_call(state->rsubdev, video, s_stream, 1); | |
| 524 | + if (ret) { | |
| 525 | + /* disable interrupts */ | |
| 526 | + xcsi2rxss_clr(state, XCSI_IER_OFFSET, XCSI_IER_INTR_MASK); | |
| 527 | + xcsi2rxss_clr(state, XCSI_GIER_OFFSET, XCSI_GIER_GIE); | |
| 528 | + | |
| 529 | + /* disable core */ | |
| 530 | + xcsi2rxss_clr(state, XCSI_CCR_OFFSET, XCSI_CCR_ENABLE); | |
| 531 | + state->streaming = false; | |
| 532 | + } | |
| 533 | + | |
| 534 | + return ret; | |
| 535 | +} | |
| 536 | + | |
| 537 | +static void xcsi2rxss_stop_stream(struct xcsi2rxss_state *state) | |
| 538 | +{ | |
| 539 | + v4l2_subdev_call(state->rsubdev, video, s_stream, 0); | |
| 540 | + | |
| 541 | + /* disable interrupts */ | |
| 542 | + xcsi2rxss_clr(state, XCSI_IER_OFFSET, XCSI_IER_INTR_MASK); | |
| 543 | + xcsi2rxss_clr(state, XCSI_GIER_OFFSET, XCSI_GIER_GIE); | |
| 544 | + | |
| 545 | + /* disable core */ | |
| 546 | + xcsi2rxss_clr(state, XCSI_CCR_OFFSET, XCSI_CCR_ENABLE); | |
| 547 | + state->streaming = false; | |
| 548 | +} | |
| 549 | + | |
| 550 | +/** | |
| 551 | + * xcsi2rxss_irq_handler - Interrupt handler for CSI-2 | |
| 552 | + * @irq: IRQ number | |
| 553 | + * @data: Pointer to device state | |
| 554 | + * | |
| 555 | + * In the interrupt handler, a list of event counters are updated for | |
| 556 | + * corresponding interrupts. This is useful to get status / debug. | |
| 557 | + * | |
| 558 | + * Return: IRQ_HANDLED after handling interrupts | |
| 559 | + */ | |
| 560 | +static irqreturn_t xcsi2rxss_irq_handler(int irq, void *data) | |
| 561 | +{ | |
| 562 | + struct xcsi2rxss_state *state = (struct xcsi2rxss_state *)data; | |
| 563 | + struct device *dev = state->dev; | |
| 564 | + u32 status; | |
| 565 | + | |
| 566 | + status = xcsi2rxss_read(state, XCSI_ISR_OFFSET) & XCSI_ISR_ALLINTR_MASK; | |
| 567 | + xcsi2rxss_write(state, XCSI_ISR_OFFSET, status); | |
| 568 | + | |
| 569 | + /* Received a short packet */ | |
| 570 | + if (status & XCSI_ISR_SPFIFONE) { | |
| 571 | + u32 count = 0; | |
| 572 | + | |
| 573 | + /* | |
| 574 | + * Drain generic short packet FIFO by reading max 31 | |
| 575 | + * (fifo depth) short packets from fifo or till fifo is empty. | |
| 576 | + */ | |
| 577 | + for (count = 0; count < XCSI_SPKT_FIFO_DEPTH; ++count) { | |
| 578 | + u32 spfifostat, spkt; | |
| 579 | + | |
| 580 | + spkt = xcsi2rxss_read(state, XCSI_SPKTR_OFFSET); | |
| 581 | + dev_dbg(dev, "Short packet = 0x%08x\n", spkt); | |
| 582 | + spfifostat = xcsi2rxss_read(state, XCSI_ISR_OFFSET); | |
| 583 | + spfifostat &= XCSI_ISR_SPFIFONE; | |
| 584 | + if (!spfifostat) | |
| 585 | + break; | |
| 586 | + xcsi2rxss_write(state, XCSI_ISR_OFFSET, spfifostat); | |
| 587 | + } | |
| 588 | + } | |
| 589 | + | |
| 590 | + /* Short packet FIFO overflow */ | |
| 591 | + if (status & XCSI_ISR_SPFIFOF) | |
| 592 | + dev_dbg_ratelimited(dev, "Short packet FIFO overflowed\n"); | |
| 593 | + | |
| 594 | + /* | |
| 595 | + * Stream line buffer full | |
| 596 | + * This means there is a backpressure from downstream IP | |
| 597 | + */ | |
| 598 | + if (status & XCSI_ISR_SLBF) { | |
| 599 | + dev_alert_ratelimited(dev, "Stream Line Buffer Full!\n"); | |
| 600 | + | |
| 601 | + /* disable interrupts */ | |
| 602 | + xcsi2rxss_clr(state, XCSI_IER_OFFSET, XCSI_IER_INTR_MASK); | |
| 603 | + xcsi2rxss_clr(state, XCSI_GIER_OFFSET, XCSI_GIER_GIE); | |
| 604 | + | |
| 605 | + /* disable core */ | |
| 606 | + xcsi2rxss_clr(state, XCSI_CCR_OFFSET, XCSI_CCR_ENABLE); | |
| 607 | + | |
| 608 | + /* | |
| 609 | + * The IP needs to be hard reset before it can be used now. | |
| 610 | + * This will be done in streamoff. | |
| 611 | + */ | |
| 612 | + | |
| 613 | + /* | |
| 614 | + * TODO: Notify the whole pipeline with v4l2_subdev_notify() to | |
| 615 | + * inform userspace. | |
| 616 | + */ | |
| 617 | + } | |
| 618 | + | |
| 619 | + /* Increment event counters */ | |
| 620 | + if (status & XCSI_ISR_ALLINTR_MASK) { | |
| 621 | + unsigned int i; | |
| 622 | + | |
| 623 | + for (i = 0; i < XCSI_NUM_EVENTS; i++) { | |
| 624 | + if (!(status & xcsi2rxss_events[i].mask)) | |
| 625 | + continue; | |
| 626 | + state->events[i]++; | |
| 627 | + dev_dbg_ratelimited(dev, "%s: %u\n", | |
| 628 | + xcsi2rxss_events[i].name, | |
| 629 | + state->events[i]); | |
| 630 | + } | |
| 631 | + | |
| 632 | + if (status & XCSI_ISR_VCXFE && state->en_vcx) { | |
| 633 | + u32 vcxstatus; | |
| 634 | + | |
| 635 | + vcxstatus = xcsi2rxss_read(state, XCSI_VCXR_OFFSET); | |
| 636 | + vcxstatus &= XCSI_VCXR_VCERR; | |
| 637 | + for (i = 0; i < XCSI_VCX_NUM_EVENTS; i++) { | |
| 638 | + if (!(vcxstatus & BIT(i))) | |
| 639 | + continue; | |
| 640 | + state->vcx_events[i]++; | |
| 641 | + } | |
| 642 | + xcsi2rxss_write(state, XCSI_VCXR_OFFSET, vcxstatus); | |
| 643 | + } | |
| 644 | + } | |
| 645 | + | |
| 646 | + return IRQ_HANDLED; | |
| 647 | +} | |
| 648 | + | |
| 649 | +/** | |
| 650 | + * xcsi2rxss_s_stream - It is used to start/stop the streaming. | |
| 651 | + * @sd: V4L2 Sub device | |
| 652 | + * @enable: Flag (True / False) | |
| 653 | + * | |
| 654 | + * This function controls the start or stop of streaming for the | |
| 655 | + * Xilinx MIPI CSI-2 Rx Subsystem. | |
| 656 | + * | |
| 657 | + * Return: 0 on success, errors otherwise | |
| 658 | + */ | |
| 659 | +static int xcsi2rxss_s_stream(struct v4l2_subdev *sd, int enable) | |
| 660 | +{ | |
| 661 | + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); | |
| 662 | + int ret = 0; | |
| 663 | + | |
| 664 | + mutex_lock(&xcsi2rxss->lock); | |
| 665 | + | |
| 666 | + if (enable == xcsi2rxss->streaming) | |
| 667 | + goto stream_done; | |
| 668 | + | |
| 669 | + if (enable) { | |
| 670 | + xcsi2rxss_reset_event_counters(xcsi2rxss); | |
| 671 | + ret = xcsi2rxss_start_stream(xcsi2rxss); | |
| 672 | + } else { | |
| 673 | + xcsi2rxss_stop_stream(xcsi2rxss); | |
| 674 | + xcsi2rxss_hard_reset(xcsi2rxss); | |
| 675 | + } | |
| 676 | + | |
| 677 | +stream_done: | |
| 678 | + mutex_unlock(&xcsi2rxss->lock); | |
| 679 | + return ret; | |
| 680 | +} | |
| 681 | + | |
| 682 | +static struct v4l2_mbus_framefmt * | |
| 683 | +__xcsi2rxss_get_pad_format(struct xcsi2rxss_state *xcsi2rxss, | |
| 684 | + struct v4l2_subdev_pad_config *cfg, | |
| 685 | + unsigned int pad, u32 which) | |
| 686 | +{ | |
| 687 | + switch (which) { | |
| 688 | + case V4L2_SUBDEV_FORMAT_TRY: | |
| 689 | + return v4l2_subdev_get_try_format(&xcsi2rxss->subdev, cfg, pad); | |
| 690 | + case V4L2_SUBDEV_FORMAT_ACTIVE: | |
| 691 | + return &xcsi2rxss->format; | |
| 692 | + default: | |
| 693 | + return NULL; | |
| 694 | + } | |
| 695 | +} | |
| 696 | + | |
| 697 | +/** | |
| 698 | + * xcsi2rxss_init_cfg - Initialise the pad format config to default | |
| 699 | + * @sd: Pointer to V4L2 Sub device structure | |
| 700 | + * @cfg: Pointer to sub device pad information structure | |
| 701 | + * | |
| 702 | + * This function is used to initialize the pad format with the default | |
| 703 | + * values. | |
| 704 | + * | |
| 705 | + * Return: 0 on success | |
| 706 | + */ | |
| 707 | +static int xcsi2rxss_init_cfg(struct v4l2_subdev *sd, | |
| 708 | + struct v4l2_subdev_pad_config *cfg) | |
| 709 | +{ | |
| 710 | + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); | |
| 711 | + struct v4l2_mbus_framefmt *format; | |
| 712 | + unsigned int i; | |
| 713 | + | |
| 714 | + mutex_lock(&xcsi2rxss->lock); | |
| 715 | + for (i = 0; i < XCSI_MEDIA_PADS; i++) { | |
| 716 | + format = v4l2_subdev_get_try_format(sd, cfg, i); | |
| 717 | + *format = xcsi2rxss->default_format; | |
| 718 | + } | |
| 719 | + mutex_unlock(&xcsi2rxss->lock); | |
| 720 | + | |
| 721 | + return 0; | |
| 722 | +} | |
| 723 | + | |
| 724 | +/** | |
| 725 | + * xcsi2rxss_get_format - Get the pad format | |
| 726 | + * @sd: Pointer to V4L2 Sub device structure | |
| 727 | + * @cfg: Pointer to sub device pad information structure | |
| 728 | + * @fmt: Pointer to pad level media bus format | |
| 729 | + * | |
| 730 | + * This function is used to get the pad format information. | |
| 731 | + * | |
| 732 | + * Return: 0 on success | |
| 733 | + */ | |
| 734 | +static int xcsi2rxss_get_format(struct v4l2_subdev *sd, | |
| 735 | + struct v4l2_subdev_pad_config *cfg, | |
| 736 | + struct v4l2_subdev_format *fmt) | |
| 737 | +{ | |
| 738 | + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); | |
| 739 | + | |
| 740 | + mutex_lock(&xcsi2rxss->lock); | |
| 741 | + fmt->format = *__xcsi2rxss_get_pad_format(xcsi2rxss, cfg, fmt->pad, | |
| 742 | + fmt->which); | |
| 743 | + mutex_unlock(&xcsi2rxss->lock); | |
| 744 | + | |
| 745 | + return 0; | |
| 746 | +} | |
| 747 | + | |
| 748 | +/** | |
| 749 | + * xcsi2rxss_set_format - This is used to set the pad format | |
| 750 | + * @sd: Pointer to V4L2 Sub device structure | |
| 751 | + * @cfg: Pointer to sub device pad information structure | |
| 752 | + * @fmt: Pointer to pad level media bus format | |
| 753 | + * | |
| 754 | + * This function is used to set the pad format. Since the pad format is fixed | |
| 755 | + * in hardware, it can't be modified on run time. So when a format set is | |
| 756 | + * requested by application, all parameters except the format type is saved | |
| 757 | + * for the pad and the original pad format is sent back to the application. | |
| 758 | + * | |
| 759 | + * Return: 0 on success | |
| 760 | + */ | |
| 761 | +static int xcsi2rxss_set_format(struct v4l2_subdev *sd, | |
| 762 | + struct v4l2_subdev_pad_config *cfg, | |
| 763 | + struct v4l2_subdev_format *fmt) | |
| 764 | +{ | |
| 765 | + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); | |
| 766 | + struct v4l2_mbus_framefmt *__format; | |
| 767 | + u32 dt; | |
| 768 | + | |
| 769 | + mutex_lock(&xcsi2rxss->lock); | |
| 770 | + | |
| 771 | + /* | |
| 772 | + * Only the format->code parameter matters for CSI as the | |
| 773 | + * CSI format cannot be changed at runtime. | |
| 774 | + * Ensure that format to set is copied to over to CSI pad format | |
| 775 | + */ | |
| 776 | + __format = __xcsi2rxss_get_pad_format(xcsi2rxss, cfg, | |
| 777 | + fmt->pad, fmt->which); | |
| 778 | + | |
| 779 | + /* only sink pad format can be updated */ | |
| 780 | + if (fmt->pad == XVIP_PAD_SOURCE) { | |
| 781 | + fmt->format = *__format; | |
| 782 | + mutex_unlock(&xcsi2rxss->lock); | |
| 783 | + return 0; | |
| 784 | + } | |
| 785 | + | |
| 786 | + /* | |
| 787 | + * RAW8 is supported in all datatypes. So if requested media bus format | |
| 788 | + * is of RAW8 type, then allow to be set. In case core is configured to | |
| 789 | + * other RAW, YUV422 8/10 or RGB888, set appropriate media bus format. | |
| 790 | + */ | |
| 791 | + dt = xcsi2rxss_get_dt(fmt->format.code); | |
| 792 | + if (dt != xcsi2rxss->datatype && dt != XCSI_DT_RAW8) { | |
| 793 | + dev_dbg(xcsi2rxss->dev, "Unsupported media bus format"); | |
| 794 | + /* set the default format for the data type */ | |
| 795 | + fmt->format.code = xcsi2rxss_get_nth_mbus(xcsi2rxss->datatype, | |
| 796 | + 0); | |
| 797 | + } | |
| 798 | + | |
| 799 | + *__format = fmt->format; | |
| 800 | + mutex_unlock(&xcsi2rxss->lock); | |
| 801 | + | |
| 802 | + return 0; | |
| 803 | +} | |
| 804 | + | |
| 805 | +/* | |
| 806 | + * xcsi2rxss_enum_mbus_code - Handle pixel format enumeration | |
| 807 | + * @sd: pointer to v4l2 subdev structure | |
| 808 | + * @cfg: V4L2 subdev pad configuration | |
| 809 | + * @code: pointer to v4l2_subdev_mbus_code_enum structure | |
| 810 | + * | |
| 811 | + * Return: -EINVAL or zero on success | |
| 812 | + */ | |
| 813 | +static int xcsi2rxss_enum_mbus_code(struct v4l2_subdev *sd, | |
| 814 | + struct v4l2_subdev_pad_config *cfg, | |
| 815 | + struct v4l2_subdev_mbus_code_enum *code) | |
| 816 | +{ | |
| 817 | + struct xcsi2rxss_state *state = to_xcsi2rxssstate(sd); | |
| 818 | + u32 dt, n; | |
| 819 | + int ret = 0; | |
| 820 | + | |
| 821 | + /* RAW8 dt packets are available in all DT configurations */ | |
| 822 | + if (code->index < 4) { | |
| 823 | + n = code->index; | |
| 824 | + dt = XCSI_DT_RAW8; | |
| 825 | + } else if (state->datatype != XCSI_DT_RAW8) { | |
| 826 | + n = code->index - 4; | |
| 827 | + dt = state->datatype; | |
| 828 | + } else { | |
| 829 | + return -EINVAL; | |
| 830 | + } | |
| 831 | + | |
| 832 | + code->code = xcsi2rxss_get_nth_mbus(dt, n); | |
| 833 | + if (!code->code) | |
| 834 | + ret = -EINVAL; | |
| 835 | + | |
| 836 | + return ret; | |
| 837 | +} | |
| 838 | + | |
| 839 | +/* ----------------------------------------------------------------------------- | |
| 840 | + * Media Operations | |
| 841 | + */ | |
| 842 | + | |
| 843 | +static const struct media_entity_operations xcsi2rxss_media_ops = { | |
| 844 | + .link_validate = v4l2_subdev_link_validate | |
| 845 | +}; | |
| 846 | + | |
| 847 | +static const struct v4l2_subdev_core_ops xcsi2rxss_core_ops = { | |
| 848 | + .log_status = xcsi2rxss_log_status, | |
| 849 | +}; | |
| 850 | + | |
| 851 | +static const struct v4l2_subdev_video_ops xcsi2rxss_video_ops = { | |
| 852 | + .s_stream = xcsi2rxss_s_stream | |
| 853 | +}; | |
| 854 | + | |
| 855 | +static const struct v4l2_subdev_pad_ops xcsi2rxss_pad_ops = { | |
| 856 | + .init_cfg = xcsi2rxss_init_cfg, | |
| 857 | + .get_fmt = xcsi2rxss_get_format, | |
| 858 | + .set_fmt = xcsi2rxss_set_format, | |
| 859 | + .enum_mbus_code = xcsi2rxss_enum_mbus_code, | |
| 860 | + .link_validate = v4l2_subdev_link_validate_default, | |
| 861 | +}; | |
| 862 | + | |
| 863 | +static const struct v4l2_subdev_ops xcsi2rxss_ops = { | |
| 864 | + .core = &xcsi2rxss_core_ops, | |
| 865 | + .video = &xcsi2rxss_video_ops, | |
| 866 | + .pad = &xcsi2rxss_pad_ops | |
| 867 | +}; | |
| 868 | + | |
| 869 | +static int xcsi2rxss_parse_of(struct xcsi2rxss_state *xcsi2rxss) | |
| 870 | +{ | |
| 871 | + struct device *dev = xcsi2rxss->dev; | |
| 872 | + struct device_node *node = dev->of_node; | |
| 873 | + | |
| 874 | + struct fwnode_handle *ep; | |
| 875 | + struct v4l2_fwnode_endpoint vep = { | |
| 876 | + .bus_type = V4L2_MBUS_CSI2_DPHY | |
| 877 | + }; | |
| 878 | + bool en_csi_v20, vfb; | |
| 879 | + int ret; | |
| 880 | + | |
| 881 | + en_csi_v20 = of_property_read_bool(node, "xlnx,en-csi-v2-0"); | |
| 882 | + if (en_csi_v20) | |
| 883 | + xcsi2rxss->en_vcx = of_property_read_bool(node, "xlnx,en-vcx"); | |
| 884 | + | |
| 885 | + xcsi2rxss->enable_active_lanes = | |
| 886 | + of_property_read_bool(node, "xlnx,en-active-lanes"); | |
| 887 | + | |
| 888 | + ret = of_property_read_u32(node, "xlnx,csi-pxl-format", | |
| 889 | + &xcsi2rxss->datatype); | |
| 890 | + if (ret < 0) { | |
| 891 | + dev_err(dev, "missing xlnx,csi-pxl-format property\n"); | |
| 892 | + return ret; | |
| 893 | + } | |
| 894 | + | |
| 895 | + switch (xcsi2rxss->datatype) { | |
| 896 | + case XCSI_DT_YUV4228B: | |
| 897 | + case XCSI_DT_RGB444: | |
| 898 | + case XCSI_DT_RGB555: | |
| 899 | + case XCSI_DT_RGB565: | |
| 900 | + case XCSI_DT_RGB666: | |
| 901 | + case XCSI_DT_RGB888: | |
| 902 | + case XCSI_DT_RAW6: | |
| 903 | + case XCSI_DT_RAW7: | |
| 904 | + case XCSI_DT_RAW8: | |
| 905 | + case XCSI_DT_RAW10: | |
| 906 | + case XCSI_DT_RAW12: | |
| 907 | + case XCSI_DT_RAW14: | |
| 908 | + break; | |
| 909 | + case XCSI_DT_YUV42210B: | |
| 910 | + case XCSI_DT_RAW16: | |
| 911 | + case XCSI_DT_RAW20: | |
| 912 | + if (!en_csi_v20) { | |
| 913 | + ret = -EINVAL; | |
| 914 | + dev_dbg(dev, "enable csi v2 for this pixel format"); | |
| 915 | + } | |
| 916 | + break; | |
| 917 | + default: | |
| 918 | + ret = -EINVAL; | |
| 919 | + } | |
| 920 | + if (ret < 0) { | |
| 921 | + dev_err(dev, "invalid csi-pxl-format property!\n"); | |
| 922 | + return ret; | |
| 923 | + } | |
| 924 | + | |
| 925 | + vfb = of_property_read_bool(node, "xlnx,vfb"); | |
| 926 | + if (!vfb) { | |
| 927 | + dev_err(dev, "operation without VFB is not supported\n"); | |
| 928 | + return -EINVAL; | |
| 929 | + } | |
| 930 | + | |
| 931 | + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), | |
| 932 | + XVIP_PAD_SINK, 0, | |
| 933 | + FWNODE_GRAPH_ENDPOINT_NEXT); | |
| 934 | + if (!ep) { | |
| 935 | + dev_err(dev, "no sink port found"); | |
| 936 | + return -EINVAL; | |
| 937 | + } | |
| 938 | + | |
| 939 | + ret = v4l2_fwnode_endpoint_parse(ep, &vep); | |
| 940 | + fwnode_handle_put(ep); | |
| 941 | + if (ret) { | |
| 942 | + dev_err(dev, "error parsing sink port"); | |
| 943 | + return ret; | |
| 944 | + } | |
| 945 | + | |
| 946 | + dev_dbg(dev, "mipi number lanes = %d\n", | |
| 947 | + vep.bus.mipi_csi2.num_data_lanes); | |
| 948 | + | |
| 949 | + xcsi2rxss->max_num_lanes = vep.bus.mipi_csi2.num_data_lanes; | |
| 950 | + | |
| 951 | + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), | |
| 952 | + XVIP_PAD_SOURCE, 0, | |
| 953 | + FWNODE_GRAPH_ENDPOINT_NEXT); | |
| 954 | + if (!ep) { | |
| 955 | + dev_err(dev, "no source port found"); | |
| 956 | + return -EINVAL; | |
| 957 | + } | |
| 958 | + | |
| 959 | + fwnode_handle_put(ep); | |
| 960 | + | |
| 961 | + dev_dbg(dev, "vcx %s, %u data lanes (%s), data type 0x%02x\n", | |
| 962 | + xcsi2rxss->en_vcx ? "enabled" : "disabled", | |
| 963 | + xcsi2rxss->max_num_lanes, | |
| 964 | + xcsi2rxss->enable_active_lanes ? "dynamic" : "static", | |
| 965 | + xcsi2rxss->datatype); | |
| 966 | + | |
| 967 | + return 0; | |
| 968 | +} | |
| 969 | + | |
| 970 | +static int xcsi2rxss_probe(struct platform_device *pdev) | |
| 971 | +{ | |
| 972 | + struct v4l2_subdev *subdev; | |
| 973 | + struct xcsi2rxss_state *xcsi2rxss; | |
| 974 | + int num_clks = ARRAY_SIZE(xcsi2rxss_clks); | |
| 975 | + struct device *dev = &pdev->dev; | |
| 976 | + int irq, ret; | |
| 977 | + | |
| 978 | + xcsi2rxss = devm_kzalloc(dev, sizeof(*xcsi2rxss), GFP_KERNEL); | |
| 979 | + if (!xcsi2rxss) | |
| 980 | + return -ENOMEM; | |
| 981 | + | |
| 982 | + xcsi2rxss->dev = dev; | |
| 983 | + | |
| 984 | + xcsi2rxss->clks = devm_kmemdup(dev, xcsi2rxss_clks, | |
| 985 | + sizeof(xcsi2rxss_clks), GFP_KERNEL); | |
| 986 | + if (!xcsi2rxss->clks) | |
| 987 | + return -ENOMEM; | |
| 988 | + | |
| 989 | + /* Reset GPIO */ | |
| 990 | + xcsi2rxss->rst_gpio = devm_gpiod_get_optional(dev, "video-reset", | |
| 991 | + GPIOD_OUT_HIGH); | |
| 992 | + if (IS_ERR(xcsi2rxss->rst_gpio)) { | |
| 993 | + if (PTR_ERR(xcsi2rxss->rst_gpio) != -EPROBE_DEFER) | |
| 994 | + dev_err(dev, "Video Reset GPIO not setup in DT"); | |
| 995 | + return PTR_ERR(xcsi2rxss->rst_gpio); | |
| 996 | + } | |
| 997 | + | |
| 998 | + ret = xcsi2rxss_parse_of(xcsi2rxss); | |
| 999 | + if (ret < 0) | |
| 1000 | + return ret; | |
| 1001 | + | |
| 1002 | + xcsi2rxss->iomem = devm_platform_ioremap_resource(pdev, 0); | |
| 1003 | + if (IS_ERR(xcsi2rxss->iomem)) | |
| 1004 | + return PTR_ERR(xcsi2rxss->iomem); | |
| 1005 | + | |
| 1006 | + irq = platform_get_irq(pdev, 0); | |
| 1007 | + if (irq < 0) | |
| 1008 | + return irq; | |
| 1009 | + | |
| 1010 | + ret = devm_request_threaded_irq(dev, irq, NULL, | |
| 1011 | + xcsi2rxss_irq_handler, IRQF_ONESHOT, | |
| 1012 | + dev_name(dev), xcsi2rxss); | |
| 1013 | + if (ret) { | |
| 1014 | + dev_err(dev, "Err = %d Interrupt handler reg failed!\n", ret); | |
| 1015 | + return ret; | |
| 1016 | + } | |
| 1017 | + | |
| 1018 | + ret = clk_bulk_get(dev, num_clks, xcsi2rxss->clks); | |
| 1019 | + if (ret) | |
| 1020 | + return ret; | |
| 1021 | + | |
| 1022 | + /* TODO: Enable/disable clocks at stream on/off time. */ | |
| 1023 | + ret = clk_bulk_prepare_enable(num_clks, xcsi2rxss->clks); | |
| 1024 | + if (ret) | |
| 1025 | + goto err_clk_put; | |
| 1026 | + | |
| 1027 | + mutex_init(&xcsi2rxss->lock); | |
| 1028 | + | |
| 1029 | + xcsi2rxss_hard_reset(xcsi2rxss); | |
| 1030 | + xcsi2rxss_soft_reset(xcsi2rxss); | |
| 1031 | + | |
| 1032 | + /* Initialize V4L2 subdevice and media entity */ | |
| 1033 | + xcsi2rxss->pads[XVIP_PAD_SINK].flags = MEDIA_PAD_FL_SINK; | |
| 1034 | + xcsi2rxss->pads[XVIP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; | |
| 1035 | + | |
| 1036 | + /* Initialize the default format */ | |
| 1037 | + xcsi2rxss->default_format.code = | |
| 1038 | + xcsi2rxss_get_nth_mbus(xcsi2rxss->datatype, 0); | |
| 1039 | + xcsi2rxss->default_format.field = V4L2_FIELD_NONE; | |
| 1040 | + xcsi2rxss->default_format.colorspace = V4L2_COLORSPACE_SRGB; | |
| 1041 | + xcsi2rxss->default_format.width = XCSI_DEFAULT_WIDTH; | |
| 1042 | + xcsi2rxss->default_format.height = XCSI_DEFAULT_HEIGHT; | |
| 1043 | + xcsi2rxss->format = xcsi2rxss->default_format; | |
| 1044 | + | |
| 1045 | + /* Initialize V4L2 subdevice and media entity */ | |
| 1046 | + subdev = &xcsi2rxss->subdev; | |
| 1047 | + v4l2_subdev_init(subdev, &xcsi2rxss_ops); | |
| 1048 | + subdev->dev = dev; | |
| 1049 | + strscpy(subdev->name, dev_name(dev), sizeof(subdev->name)); | |
| 1050 | + subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; | |
| 1051 | + subdev->entity.ops = &xcsi2rxss_media_ops; | |
| 1052 | + v4l2_set_subdevdata(subdev, xcsi2rxss); | |
| 1053 | + | |
| 1054 | + ret = media_entity_pads_init(&subdev->entity, XCSI_MEDIA_PADS, | |
| 1055 | + xcsi2rxss->pads); | |
| 1056 | + if (ret < 0) | |
| 1057 | + goto error; | |
| 1058 | + | |
| 1059 | + platform_set_drvdata(pdev, xcsi2rxss); | |
| 1060 | + | |
| 1061 | + ret = v4l2_async_register_subdev(subdev); | |
| 1062 | + if (ret < 0) { | |
| 1063 | + dev_err(dev, "failed to register subdev\n"); | |
| 1064 | + goto error; | |
| 1065 | + } | |
| 1066 | + | |
| 1067 | + return 0; | |
| 1068 | +error: | |
| 1069 | + media_entity_cleanup(&subdev->entity); | |
| 1070 | + mutex_destroy(&xcsi2rxss->lock); | |
| 1071 | + clk_bulk_disable_unprepare(num_clks, xcsi2rxss->clks); | |
| 1072 | +err_clk_put: | |
| 1073 | + clk_bulk_put(num_clks, xcsi2rxss->clks); | |
| 1074 | + return ret; | |
| 1075 | +} | |
| 1076 | + | |
| 1077 | +static int xcsi2rxss_remove(struct platform_device *pdev) | |
| 1078 | +{ | |
| 1079 | + struct xcsi2rxss_state *xcsi2rxss = platform_get_drvdata(pdev); | |
| 1080 | + struct v4l2_subdev *subdev = &xcsi2rxss->subdev; | |
| 1081 | + int num_clks = ARRAY_SIZE(xcsi2rxss_clks); | |
| 1082 | + | |
| 1083 | + v4l2_async_unregister_subdev(subdev); | |
| 1084 | + media_entity_cleanup(&subdev->entity); | |
| 1085 | + mutex_destroy(&xcsi2rxss->lock); | |
| 1086 | + clk_bulk_disable_unprepare(num_clks, xcsi2rxss->clks); | |
| 1087 | + clk_bulk_put(num_clks, xcsi2rxss->clks); | |
| 1088 | + | |
| 1089 | + return 0; | |
| 1090 | +} | |
| 1091 | + | |
| 1092 | +static const struct of_device_id xcsi2rxss_of_id_table[] = { | |
| 1093 | + { .compatible = "xlnx,mipi-csi2-rx-subsystem-5.0", }, | |
| 1094 | + { } | |
| 1095 | +}; | |
| 1096 | +MODULE_DEVICE_TABLE(of, xcsi2rxss_of_id_table); | |
| 1097 | + | |
| 1098 | +static struct platform_driver xcsi2rxss_driver = { | |
| 1099 | + .driver = { | |
| 1100 | + .name = "xilinx-csi2rxss", | |
| 1101 | + .of_match_table = xcsi2rxss_of_id_table, | |
| 1102 | + }, | |
| 1103 | + .probe = xcsi2rxss_probe, | |
| 1104 | + .remove = xcsi2rxss_remove, | |
| 1105 | +}; | |
| 1106 | + | |
| 1107 | +module_platform_driver(xcsi2rxss_driver); | |
| 1108 | + | |
| 1109 | +MODULE_AUTHOR("Vishal Sagar <vsagar@xilinx.com>"); | |
| 1110 | +MODULE_DESCRIPTION("Xilinx MIPI CSI-2 Rx Subsystem Driver"); | |
| 1111 | +MODULE_LICENSE("GPL v2"); | |
| @@ -414,7 +414,7 @@ static int usb_si4713_probe(struct usb_interface *intf, · 413 unmodified lines | ||
| 414 | 414 | struct si4713_usb_device *radio; |
| 415 | 415 | struct i2c_adapter *adapter; |
| 416 | 416 | struct v4l2_subdev *sd; |
| 417 | − int retval = -ENOMEM; | |
| 417 | + int retval; | |
| 418 | 418 | |
| 419 | 419 | dev_info(&intf->dev, "Si4713 development board discovered: (%04X:%04X)\n", |
| 420 | 420 | id->idVendor, id->idProduct); |
| @@ -530,6 +530,17 @@ config IR_ZX · 529 unmodified lines | ||
| 530 | 530 | To compile this driver as a module, choose M here: the |
| 531 | 531 | module will be called zx-irdec. |
| 532 | 532 | |
| 533 | +config IR_TOY | |
| 534 | + tristate "Infrared Toy and IR Droid" | |
| 535 | + depends on RC_CORE | |
| 536 | + depends on USB_ARCH_HAS_HCD | |
| 537 | + help | |
| 538 | + Say Y here if you want to use the Infrared Toy or IR Droid, USB | |
| 539 | + versions. | |
| 540 | + | |
| 541 | + To compile this driver as a module, choose M here: the module will be | |
| 542 | + called ir_toy. | |
| 543 | + | |
| 533 | 544 | endif #RC_DEVICES |
| 534 | 545 | |
| 535 | 546 | endif #RC_CORE |
| @@ -50,3 +50,4 @@ obj-$(CONFIG_IR_MTK) += mtk-cir.o · 49 unmodified lines | ||
| 50 | 50 | obj-$(CONFIG_IR_ZX) += zx-irdec.o |
| 51 | 51 | obj-$(CONFIG_IR_TANGO) += tango-ir.o |
| 52 | 52 | obj-$(CONFIG_RC_XBOX_DVD) += xbox_remote.o |
| 53 | +obj-$(CONFIG_IR_TOY) += ir_toy.o | |
| @@ -51,13 +51,6 @@ static inline void fintek_set_reg_bit(struct fintek_dev *fintek, u8 val, u8 reg) · 50 unmodified lines | ||
| 51 | 51 | fintek_cr_write(fintek, tmp, reg); |
| 52 | 52 | } |
| 53 | 53 | |
| 54 | −/* clear config register bit without changing other bits */ | |
| 55 | −static inline void fintek_clear_reg_bit(struct fintek_dev *fintek, u8 val, u8 reg) | |
| 56 | −{ | |
| 57 | − u8 tmp = fintek_cr_read(fintek, reg) & ~val; | |
| 58 | − fintek_cr_write(fintek, tmp, reg); | |
| 59 | −} | |
| 60 | − | |
| 61 | 54 | /* enter config mode */ |
| 62 | 55 | static inline void fintek_config_mode_enable(struct fintek_dev *fintek) |
| 63 | 56 | { |
| @@ -795,7 +795,7 @@ static ssize_t show_associate_remote(struct device *d, · 794 unmodified lines | ||
| 795 | 795 | else |
| 796 | 796 | strscpy(buf, "closed\n", PAGE_SIZE); |
| 797 | 797 | |
| 798 | − 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"); | |
| 798 | + 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"); | |
| 799 | 799 | mutex_unlock(&ictx->lock); |
| 800 | 800 | return strlen(buf); |
| 801 | 801 | } |
| @@ -0,0 +1,509 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later | |
| 2 | + | |
| 3 | +/* | |
| 4 | + * Infrared Toy and IR Droid RC core driver | |
| 5 | + * | |
| 6 | + * Copyright (C) 2020 Sean Young <sean@mess.org> | |
| 7 | + | |
| 8 | + * This driver is based on the lirc driver which can be found here: | |
| 9 | + * https://sourceforge.net/p/lirc/git/ci/master/tree/plugins/irtoy.c | |
| 10 | + * Copyright (C) 2011 Peter Kooiman <pkooiman@gmail.com> | |
| 11 | + */ | |
| 12 | + | |
| 13 | +#include <asm/unaligned.h> | |
| 14 | +#include <linux/completion.h> | |
| 15 | +#include <linux/kernel.h> | |
| 16 | +#include <linux/module.h> | |
| 17 | +#include <linux/usb.h> | |
| 18 | +#include <linux/slab.h> | |
| 19 | +#include <linux/usb/input.h> | |
| 20 | + | |
| 21 | +#include <media/rc-core.h> | |
| 22 | + | |
| 23 | +static const u8 COMMAND_VERSION[] = { 'v' }; | |
| 24 | +// End transmit and repeat reset command so we exit sump mode | |
| 25 | +static const u8 COMMAND_RESET[] = { 0xff, 0xff, 0, 0, 0, 0, 0 }; | |
| 26 | +static const u8 COMMAND_SMODE_ENTER[] = { 's' }; | |
| 27 | +static const u8 COMMAND_TXSTART[] = { 0x26, 0x24, 0x25, 0x03 }; | |
| 28 | + | |
| 29 | +#define REPLY_XMITCOUNT 't' | |
| 30 | +#define REPLY_XMITSUCCESS 'C' | |
| 31 | +#define REPLY_VERSION 'V' | |
| 32 | +#define REPLY_SAMPLEMODEPROTO 'S' | |
| 33 | + | |
| 34 | +#define TIMEOUT 500 | |
| 35 | + | |
| 36 | +#define LEN_XMITRES 3 | |
| 37 | +#define LEN_VERSION 4 | |
| 38 | +#define LEN_SAMPLEMODEPROTO 3 | |
| 39 | + | |
| 40 | +#define MIN_FW_VERSION 20 | |
| 41 | +#define UNIT_NS 21333 | |
| 42 | +#define MAX_TIMEOUT_NS (UNIT_NS * U16_MAX) | |
| 43 | + | |
| 44 | +#define MAX_PACKET 64 | |
| 45 | + | |
| 46 | +enum state { | |
| 47 | + STATE_IRDATA, | |
| 48 | + STATE_RESET, | |
| 49 | + STATE_COMMAND, | |
| 50 | + STATE_TX, | |
| 51 | +}; | |
| 52 | + | |
| 53 | +struct irtoy { | |
| 54 | + struct device *dev; | |
| 55 | + struct usb_device *usbdev; | |
| 56 | + | |
| 57 | + struct rc_dev *rc; | |
| 58 | + struct urb *urb_in, *urb_out; | |
| 59 | + | |
| 60 | + u8 *in; | |
| 61 | + u8 *out; | |
| 62 | + struct completion command_done; | |
| 63 | + | |
| 64 | + bool pulse; | |
| 65 | + enum state state; | |
| 66 | + | |
| 67 | + void *tx_buf; | |
| 68 | + uint tx_len; | |
| 69 | + | |
| 70 | + uint emitted; | |
| 71 | + uint hw_version; | |
| 72 | + uint sw_version; | |
| 73 | + uint proto_version; | |
| 74 | + | |
| 75 | + char phys[64]; | |
| 76 | +}; | |
| 77 | + | |
| 78 | +static void irtoy_response(struct irtoy *irtoy, u32 len) | |
| 79 | +{ | |
| 80 | + switch (irtoy->state) { | |
| 81 | + case STATE_COMMAND: | |
| 82 | + if (len == LEN_VERSION && irtoy->in[0] == REPLY_VERSION) { | |
| 83 | + uint version; | |
| 84 | + | |
| 85 | + irtoy->in[LEN_VERSION] = 0; | |
| 86 | + | |
| 87 | + if (kstrtouint(irtoy->in + 1, 10, &version)) { | |
| 88 | + dev_err(irtoy->dev, "invalid version %*phN. Please make sure you are using firmware v20 or higher", | |
| 89 | + LEN_VERSION, irtoy->in); | |
| 90 | + break; | |
| 91 | + } | |
| 92 | + | |
| 93 | + dev_dbg(irtoy->dev, "version %s\n", irtoy->in); | |
| 94 | + | |
| 95 | + irtoy->hw_version = version / 100; | |
| 96 | + irtoy->sw_version = version % 100; | |
| 97 | + | |
| 98 | + irtoy->state = STATE_IRDATA; | |
| 99 | + complete(&irtoy->command_done); | |
| 100 | + } else if (len == LEN_SAMPLEMODEPROTO && | |
| 101 | + irtoy->in[0] == REPLY_SAMPLEMODEPROTO) { | |
| 102 | + uint version; | |
| 103 | + | |
| 104 | + irtoy->in[LEN_SAMPLEMODEPROTO] = 0; | |
| 105 | + | |
| 106 | + if (kstrtouint(irtoy->in + 1, 10, &version)) { | |
| 107 | + dev_err(irtoy->dev, "invalid sample mode response %*phN", | |
| 108 | + LEN_SAMPLEMODEPROTO, irtoy->in); | |
| 109 | + return; | |
| 110 | + } | |
| 111 | + | |
| 112 | + dev_dbg(irtoy->dev, "protocol %s\n", irtoy->in); | |
| 113 | + | |
| 114 | + irtoy->proto_version = version; | |
| 115 | + | |
| 116 | + irtoy->state = STATE_IRDATA; | |
| 117 | + complete(&irtoy->command_done); | |
| 118 | + } else { | |
| 119 | + dev_err(irtoy->dev, "unexpected response to command: %*phN\n", | |
| 120 | + len, irtoy->in); | |
| 121 | + } | |
| 122 | + break; | |
| 123 | + case STATE_IRDATA: { | |
| 124 | + struct ir_raw_event rawir = { .pulse = irtoy->pulse }; | |
| 125 | + __be16 *in = (__be16 *)irtoy->in; | |
| 126 | + int i; | |
| 127 | + | |
| 128 | + for (i = 0; i < len / sizeof(__be16); i++) { | |
| 129 | + u16 v = be16_to_cpu(in[i]); | |
| 130 | + | |
| 131 | + if (v == 0xffff) { | |
| 132 | + rawir.pulse = false; | |
| 133 | + } else { | |
| 134 | + rawir.duration = v * UNIT_NS; | |
| 135 | + ir_raw_event_store_with_timeout(irtoy->rc, | |
| 136 | + &rawir); | |
| 137 | + } | |
| 138 | + | |
| 139 | + rawir.pulse = !rawir.pulse; | |
| 140 | + } | |
| 141 | + | |
| 142 | + irtoy->pulse = rawir.pulse; | |
| 143 | + | |
| 144 | + ir_raw_event_handle(irtoy->rc); | |
| 145 | + break; | |
| 146 | + } | |
| 147 | + case STATE_TX: | |
| 148 | + if (irtoy->tx_len == 0) { | |
| 149 | + if (len == LEN_XMITRES && | |
| 150 | + irtoy->in[0] == REPLY_XMITCOUNT) { | |
| 151 | + u16 emitted = get_unaligned_be16(irtoy->in + 1); | |
| 152 | + | |
| 153 | + dev_dbg(irtoy->dev, "emitted:%u\n", emitted); | |
| 154 | + | |
| 155 | + irtoy->emitted = emitted; | |
| 156 | + } else if (len == 1 && | |
| 157 | + irtoy->in[0] == REPLY_XMITSUCCESS) { | |
| 158 | + irtoy->state = STATE_IRDATA; | |
| 159 | + complete(&irtoy->command_done); | |
| 160 | + } | |
| 161 | + } else { | |
| 162 | + // send next part of tx buffer | |
| 163 | + uint space = irtoy->in[0]; | |
| 164 | + uint buf_len; | |
| 165 | + int err; | |
| 166 | + | |
| 167 | + if (len != 1 || space > MAX_PACKET || space == 0) { | |
| 168 | + dev_err(irtoy->dev, "packet length expected: %*phN\n", | |
| 169 | + len, irtoy->in); | |
| 170 | + irtoy->state = STATE_IRDATA; | |
| 171 | + complete(&irtoy->command_done); | |
| 172 | + break; | |
| 173 | + } | |
| 174 | + | |
| 175 | + buf_len = min(space, irtoy->tx_len); | |
| 176 | + | |
| 177 | + dev_dbg(irtoy->dev, "remaining:%u sending:%u\n", | |
| 178 | + irtoy->tx_len, buf_len); | |
| 179 | + | |
| 180 | + memcpy(irtoy->out, irtoy->tx_buf, buf_len); | |
| 181 | + irtoy->urb_out->transfer_buffer_length = buf_len; | |
| 182 | + err = usb_submit_urb(irtoy->urb_out, GFP_ATOMIC); | |
| 183 | + if (err != 0) { | |
| 184 | + dev_err(irtoy->dev, "fail to submit tx buf urb: %d\n", | |
| 185 | + err); | |
| 186 | + irtoy->state = STATE_IRDATA; | |
| 187 | + complete(&irtoy->command_done); | |
| 188 | + break; | |
| 189 | + } | |
| 190 | + | |
| 191 | + irtoy->tx_buf += buf_len; | |
| 192 | + irtoy->tx_len -= buf_len; | |
| 193 | + } | |
| 194 | + break; | |
| 195 | + case STATE_RESET: | |
| 196 | + dev_err(irtoy->dev, "unexpected response to reset: %*phN\n", | |
| 197 | + len, irtoy->in); | |
| 198 | + } | |
| 199 | +} | |
| 200 | + | |
| 201 | +static void irtoy_out_callback(struct urb *urb) | |
| 202 | +{ | |
| 203 | + struct irtoy *irtoy = urb->context; | |
| 204 | + | |
| 205 | + if (urb->status == 0) { | |
| 206 | + if (irtoy->state == STATE_RESET) | |
| 207 | + complete(&irtoy->command_done); | |
| 208 | + } else { | |
| 209 | + dev_warn(irtoy->dev, "out urb status: %d\n", urb->status); | |
| 210 | + } | |
| 211 | +} | |
| 212 | + | |
| 213 | +static void irtoy_in_callback(struct urb *urb) | |
| 214 | +{ | |
| 215 | + struct irtoy *irtoy = urb->context; | |
| 216 | + int ret; | |
| 217 | + | |
| 218 | + if (urb->status == 0) | |
| 219 | + irtoy_response(irtoy, urb->actual_length); | |
| 220 | + else | |
| 221 | + dev_dbg(irtoy->dev, "in urb status: %d\n", urb->status); | |
| 222 | + | |
| 223 | + ret = usb_submit_urb(urb, GFP_ATOMIC); | |
| 224 | + if (ret && ret != -ENODEV) | |
| 225 | + dev_warn(irtoy->dev, "failed to resubmit urb: %d\n", ret); | |
| 226 | +} | |
| 227 | + | |
| 228 | +static int irtoy_command(struct irtoy *irtoy, const u8 *cmd, int cmd_len, | |
| 229 | + enum state state) | |
| 230 | +{ | |
| 231 | + int err; | |
| 232 | + | |
| 233 | + init_completion(&irtoy->command_done); | |
| 234 | + | |
| 235 | + irtoy->state = state; | |
| 236 | + | |
| 237 | + memcpy(irtoy->out, cmd, cmd_len); | |
| 238 | + irtoy->urb_out->transfer_buffer_length = cmd_len; | |
| 239 | + | |
| 240 | + err = usb_submit_urb(irtoy->urb_out, GFP_KERNEL); | |
| 241 | + if (err != 0) | |
| 242 | + return err; | |
| 243 | + | |
| 244 | + if (!wait_for_completion_timeout(&irtoy->command_done, | |
| 245 | + msecs_to_jiffies(TIMEOUT))) { | |
| 246 | + usb_kill_urb(irtoy->urb_out); | |
| 247 | + return -ETIMEDOUT; | |
| 248 | + } | |
| 249 | + | |
| 250 | + return 0; | |
| 251 | +} | |
| 252 | + | |
| 253 | +static int irtoy_setup(struct irtoy *irtoy) | |
| 254 | +{ | |
| 255 | + int err; | |
| 256 | + | |
| 257 | + err = irtoy_command(irtoy, COMMAND_RESET, sizeof(COMMAND_RESET), | |
| 258 | + STATE_RESET); | |
| 259 | + if (err != 0) { | |
| 260 | + dev_err(irtoy->dev, "could not write reset command: %d\n", | |
| 261 | + err); | |
| 262 | + return err; | |
| 263 | + } | |
| 264 | + | |
| 265 | + usleep_range(50, 50); | |
| 266 | + | |
| 267 | + // get version | |
| 268 | + err = irtoy_command(irtoy, COMMAND_VERSION, sizeof(COMMAND_VERSION), | |
| 269 | + STATE_COMMAND); | |
| 270 | + if (err) { | |
| 271 | + dev_err(irtoy->dev, "could not write version command: %d\n", | |
| 272 | + err); | |
| 273 | + return err; | |
| 274 | + } | |
| 275 | + | |
| 276 | + // enter sample mode | |
| 277 | + err = irtoy_command(irtoy, COMMAND_SMODE_ENTER, | |
| 278 | + sizeof(COMMAND_SMODE_ENTER), STATE_COMMAND); | |
| 279 | + if (err) | |
| 280 | + dev_err(irtoy->dev, "could not write sample command: %d\n", | |
| 281 | + err); | |
| 282 | + | |
| 283 | + return err; | |
| 284 | +} | |
| 285 | + | |
| 286 | +/* | |
| 287 | + * When sending IR, it is imperative that we send the IR data as quickly | |
| 288 | + * as possible to the device, so it does not run out of IR data and | |
| 289 | + * introduce gaps. Allocate the buffer here, and then feed the data from | |
| 290 | + * the urb callback handler. | |
| 291 | + */ | |
| 292 | +static int irtoy_tx(struct rc_dev *rc, uint *txbuf, uint count) | |
| 293 | +{ | |
| 294 | + struct irtoy *irtoy = rc->priv; | |
| 295 | + unsigned int i, size; | |
| 296 | + __be16 *buf; | |
| 297 | + int err; | |
| 298 | + | |
| 299 | + size = sizeof(u16) * (count + 1); | |
| 300 | + buf = kmalloc(size, GFP_KERNEL); | |
| 301 | + if (!buf) | |
| 302 | + return -ENOMEM; | |
| 303 | + | |
| 304 | + for (i = 0; i < count; i++) { | |
| 305 | + u16 v = DIV_ROUND_CLOSEST(US_TO_NS(txbuf[i]), UNIT_NS); | |
| 306 | + | |
| 307 | + if (!v) | |
| 308 | + v = 1; | |
| 309 | + buf[i] = cpu_to_be16(v); | |
| 310 | + } | |
| 311 | + | |
| 312 | + buf[count] = cpu_to_be16(0xffff); | |
| 313 | + | |
| 314 | + irtoy->tx_buf = buf; | |
| 315 | + irtoy->tx_len = size; | |
| 316 | + irtoy->emitted = 0; | |
| 317 | + | |
| 318 | + err = irtoy_command(irtoy, COMMAND_TXSTART, sizeof(COMMAND_TXSTART), | |
| 319 | + STATE_TX); | |
| 320 | + kfree(buf); | |
| 321 | + | |
| 322 | + if (err) { | |
| 323 | + dev_err(irtoy->dev, "failed to send tx start command: %d\n", | |
| 324 | + err); | |
| 325 | + // not sure what state the device is in, reset it | |
| 326 | + irtoy_setup(irtoy); | |
| 327 | + return err; | |
| 328 | + } | |
| 329 | + | |
| 330 | + if (size != irtoy->emitted) { | |
| 331 | + dev_err(irtoy->dev, "expected %u emitted, got %u\n", size, | |
| 332 | + irtoy->emitted); | |
| 333 | + // not sure what state the device is in, reset it | |
| 334 | + irtoy_setup(irtoy); | |
| 335 | + return -EINVAL; | |
| 336 | + } | |
| 337 | + | |
| 338 | + return count; | |
| 339 | +} | |
| 340 | + | |
| 341 | +static int irtoy_probe(struct usb_interface *intf, | |
| 342 | + const struct usb_device_id *id) | |
| 343 | +{ | |
| 344 | + struct usb_host_interface *idesc = intf->cur_altsetting; | |
| 345 | + struct usb_device *usbdev = interface_to_usbdev(intf); | |
| 346 | + struct usb_endpoint_descriptor *ep_in = NULL; | |
| 347 | + struct usb_endpoint_descriptor *ep_out = NULL; | |
| 348 | + struct usb_endpoint_descriptor *ep = NULL; | |
| 349 | + struct irtoy *irtoy; | |
| 350 | + struct rc_dev *rc; | |
| 351 | + struct urb *urb; | |
| 352 | + int i, pipe, err = -ENOMEM; | |
| 353 | + | |
| 354 | + for (i = 0; i < idesc->desc.bNumEndpoints; i++) { | |
| 355 | + ep = &idesc->endpoint[i].desc; | |
| 356 | + | |
| 357 | + if (!ep_in && usb_endpoint_is_bulk_in(ep) && | |
| 358 | + usb_endpoint_maxp(ep) == MAX_PACKET) | |
| 359 | + ep_in = ep; | |
| 360 | + | |
| 361 | + if (!ep_out && usb_endpoint_is_bulk_out(ep) && | |
| 362 | + usb_endpoint_maxp(ep) == MAX_PACKET) | |
| 363 | + ep_out = ep; | |
| 364 | + } | |
| 365 | + | |
| 366 | + if (!ep_in || !ep_out) { | |
| 367 | + dev_err(&intf->dev, "required endpoints not found\n"); | |
| 368 | + return -ENODEV; | |
| 369 | + } | |
| 370 | + | |
| 371 | + irtoy = kzalloc(sizeof(*irtoy), GFP_KERNEL); | |
| 372 | + if (!irtoy) | |
| 373 | + return -ENOMEM; | |
| 374 | + | |
| 375 | + irtoy->in = kmalloc(MAX_PACKET, GFP_KERNEL); | |
| 376 | + if (!irtoy->in) | |
| 377 | + goto free_irtoy; | |
| 378 | + | |
| 379 | + irtoy->out = kmalloc(MAX_PACKET, GFP_KERNEL); | |
| 380 | + if (!irtoy->out) | |
| 381 | + goto free_irtoy; | |
| 382 | + | |
| 383 | + rc = rc_allocate_device(RC_DRIVER_IR_RAW); | |
| 384 | + if (!rc) | |
| 385 | + goto free_irtoy; | |
| 386 | + | |
| 387 | + urb = usb_alloc_urb(0, GFP_KERNEL); | |
| 388 | + if (!urb) | |
| 389 | + goto free_rcdev; | |
| 390 | + | |
| 391 | + pipe = usb_rcvbulkpipe(usbdev, ep_in->bEndpointAddress); | |
| 392 | + usb_fill_bulk_urb(urb, usbdev, pipe, irtoy->in, MAX_PACKET, | |
| 393 | + irtoy_in_callback, irtoy); | |
| 394 | + irtoy->urb_in = urb; | |
| 395 | + | |
| 396 | + urb = usb_alloc_urb(0, GFP_KERNEL); | |
| 397 | + if (!urb) | |
| 398 | + goto free_rcdev; | |
| 399 | + | |
| 400 | + pipe = usb_sndbulkpipe(usbdev, ep_out->bEndpointAddress); | |
| 401 | + usb_fill_bulk_urb(urb, usbdev, pipe, irtoy->out, MAX_PACKET, | |
| 402 | + irtoy_out_callback, irtoy); | |
| 403 | + | |
| 404 | + irtoy->dev = &intf->dev; | |
| 405 | + irtoy->usbdev = usbdev; | |
| 406 | + irtoy->rc = rc; | |
| 407 | + irtoy->urb_out = urb; | |
| 408 | + irtoy->pulse = true; | |
| 409 | + | |
| 410 | + err = usb_submit_urb(irtoy->urb_in, GFP_KERNEL); | |
| 411 | + if (err != 0) { | |
| 412 | + dev_err(irtoy->dev, "fail to submit in urb: %d\n", err); | |
| 413 | + return err; | |
| 414 | + } | |
| 415 | + | |
| 416 | + err = irtoy_setup(irtoy); | |
| 417 | + if (err) | |
| 418 | + goto free_rcdev; | |
| 419 | + | |
| 420 | + dev_info(irtoy->dev, "version: hardware %u, firmware %u, protocol %u", | |
| 421 | + irtoy->hw_version, irtoy->sw_version, irtoy->proto_version); | |
| 422 | + | |
| 423 | + if (irtoy->sw_version < MIN_FW_VERSION) { | |
| 424 | + dev_err(irtoy->dev, "need firmware V%02u or higher", | |
| 425 | + MIN_FW_VERSION); | |
| 426 | + err = -ENODEV; | |
| 427 | + goto free_rcdev; | |
| 428 | + } | |
| 429 | + | |
| 430 | + usb_make_path(usbdev, irtoy->phys, sizeof(irtoy->phys)); | |
| 431 | + | |
| 432 | + rc->device_name = "Infrared Toy"; | |
| 433 | + rc->driver_name = KBUILD_MODNAME; | |
| 434 | + rc->input_phys = irtoy->phys; | |
| 435 | + usb_to_input_id(usbdev, &rc->input_id); | |
| 436 | + rc->dev.parent = &intf->dev; | |
| 437 | + rc->priv = irtoy; | |
| 438 | + rc->tx_ir = irtoy_tx; | |
| 439 | + rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; | |
| 440 | + rc->map_name = RC_MAP_RC6_MCE; | |
| 441 | + rc->rx_resolution = UNIT_NS; | |
| 442 | + rc->timeout = IR_DEFAULT_TIMEOUT; | |
| 443 | + | |
| 444 | + /* | |
| 445 | + * end of transmission is detected by absence of a usb packet | |
| 446 | + * with more pulse/spaces. However, each usb packet sent can | |
| 447 | + * contain 32 pulse/spaces, which can be quite lengthy, so there | |
| 448 | + * can be a delay between usb packets. For example with nec there is a | |
| 449 | + * 17ms gap between packets. | |
| 450 | + * | |
| 451 | + * So, make timeout a largish minimum which works with most protocols. | |
| 452 | + */ | |
| 453 | + rc->min_timeout = MS_TO_NS(40); | |
| 454 | + rc->max_timeout = MAX_TIMEOUT_NS; | |
| 455 | + | |
| 456 | + err = rc_register_device(rc); | |
| 457 | + if (err) | |
| 458 | + goto free_rcdev; | |
| 459 | + | |
| 460 | + usb_set_intfdata(intf, irtoy); | |
| 461 | + | |
| 462 | + return 0; | |
| 463 | + | |
| 464 | +free_rcdev: | |
| 465 | + usb_kill_urb(irtoy->urb_out); | |
| 466 | + usb_free_urb(irtoy->urb_out); | |
| 467 | + usb_kill_urb(irtoy->urb_in); | |
| 468 | + usb_free_urb(irtoy->urb_in); | |
| 469 | + rc_free_device(rc); | |
| 470 | +free_irtoy: | |
| 471 | + kfree(irtoy->in); | |
| 472 | + kfree(irtoy->out); | |
| 473 | + kfree(irtoy); | |
| 474 | + return err; | |
| 475 | +} | |
| 476 | + | |
| 477 | +static void irtoy_disconnect(struct usb_interface *intf) | |
| 478 | +{ | |
| 479 | + struct irtoy *ir = usb_get_intfdata(intf); | |
| 480 | + | |
| 481 | + rc_unregister_device(ir->rc); | |
| 482 | + usb_set_intfdata(intf, NULL); | |
| 483 | + usb_kill_urb(ir->urb_out); | |
| 484 | + usb_free_urb(ir->urb_out); | |
| 485 | + usb_kill_urb(ir->urb_in); | |
| 486 | + usb_free_urb(ir->urb_in); | |
| 487 | + kfree(ir->in); | |
| 488 | + kfree(ir->out); | |
| 489 | + kfree(ir); | |
| 490 | +} | |
| 491 | + | |
| 492 | +static const struct usb_device_id irtoy_table[] = { | |
| 493 | + { USB_DEVICE_INTERFACE_CLASS(0x04d8, 0xfd08, USB_CLASS_CDC_DATA) }, | |
| 494 | + { } | |
| 495 | +}; | |
| 496 | + | |
| 497 | +static struct usb_driver irtoy_driver = { | |
| 498 | + .name = KBUILD_MODNAME, | |
| 499 | + .probe = irtoy_probe, | |
| 500 | + .disconnect = irtoy_disconnect, | |
| 501 | + .id_table = irtoy_table, | |
| 502 | +}; | |
| 503 | + | |
| 504 | +module_usb_driver(irtoy_driver); | |
| 505 | + | |
| 506 | +MODULE_AUTHOR("Sean Young <sean@mess.org>"); | |
| 507 | +MODULE_DESCRIPTION("Infrared Toy and IR Droid driver"); | |
| 508 | +MODULE_LICENSE("GPL"); | |
| 509 | +MODULE_DEVICE_TABLE(usb, irtoy_table); | |
| @@ -74,13 +74,6 @@ static inline void nvt_set_reg_bit(struct nvt_dev *nvt, u8 val, u8 reg) · 73 unmodified lines | ||
| 74 | 74 | nvt_cr_write(nvt, tmp, reg); |
| 75 | 75 | } |
| 76 | 76 | |
| 77 | −/* clear config register bit without changing other bits */ | |
| 78 | −static inline void nvt_clear_reg_bit(struct nvt_dev *nvt, u8 val, u8 reg) | |
| 79 | −{ | |
| 80 | − u8 tmp = nvt_cr_read(nvt, reg) & ~val; | |
| 81 | − nvt_cr_write(nvt, tmp, reg); | |
| 82 | −} | |
| 83 | − | |
| 84 | 77 | /* enter extended function mode */ |
| 85 | 78 | static inline int nvt_efm_enable(struct nvt_dev *nvt) |
| 86 | 79 | { |
| @@ -631,30 +624,6 @@ static u32 nvt_rx_carrier_detect(struct nvt_dev *nvt) · 544 unmodified lines | ||
| 631 | 624 | return carrier; |
| 632 | 625 | } |
| 633 | 626 | #endif |
| 634 | −/* | |
| 635 | − * set carrier frequency | |
| 636 | − * | |
| 637 | − * set carrier on 2 registers: CP & CC | |
| 638 | − * always set CP as 0x81 | |
| 639 | − * set CC by SPEC, CC = 3MHz/carrier - 1 | |
| 640 | − */ | |
| 641 | −static int nvt_set_tx_carrier(struct rc_dev *dev, u32 carrier) | |
| 642 | −{ | |
| 643 | − struct nvt_dev *nvt = dev->priv; | |
| 644 | − u16 val; | |
| 645 | − | |
| 646 | − if (carrier == 0) | |
| 647 | − return -EINVAL; | |
| 648 | − | |
| 649 | − nvt_cir_reg_write(nvt, 1, CIR_CP); | |
| 650 | − val = 3000000 / (carrier) - 1; | |
| 651 | − nvt_cir_reg_write(nvt, val & 0xff, CIR_CC); | |
| 652 | − | |
| 653 | − nvt_dbg("cp: 0x%x cc: 0x%x\n", | |
| 654 | − nvt_cir_reg_read(nvt, CIR_CP), nvt_cir_reg_read(nvt, CIR_CC)); | |
| 655 | − | |
| 656 | − return 0; | |
| 657 | −} | |
| 658 | 627 | |
| 659 | 628 | static int nvt_ir_raw_set_wakeup_filter(struct rc_dev *dev, |
| 660 | 629 | 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) · 361 unmodified lines | ||
| 1022 | 991 | rdev->encode_wakeup = true; |
| 1023 | 992 | rdev->open = nvt_open; |
| 1024 | 993 | rdev->close = nvt_close; |
| 1025 | − rdev->s_tx_carrier = nvt_set_tx_carrier; | |
| 1026 | 994 | rdev->s_wakeup_filter = nvt_ir_raw_set_wakeup_filter; |
| 1027 | 995 | rdev->device_name = "Nuvoton w836x7hg Infrared Remote Transceiver"; |
| 1028 | 996 | rdev->input_phys = "nuvoton/cir0"; |
| @@ -2052,7 +2052,7 @@ static int __init rc_core_init(void) · 2051 unmodified lines | ||
| 2052 | 2052 | if (rc) { |
| 2053 | 2053 | pr_err("rc_core: unable to init lirc\n"); |
| 2054 | 2054 | class_unregister(&rc_class); |
| 2055 | − return 0; | |
| 2055 | + return rc; | |
| 2056 | 2056 | } |
| 2057 | 2057 | |
| 2058 | 2058 | led_trigger_register_simple("rc-feedback", &led_feedback); |
| @@ -1442,7 +1442,7 @@ static void vicodec_buf_queue(struct vb2_buffer *vb) · 1441 unmodified lines | ||
| 1442 | 1442 | .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, |
| 1443 | 1443 | }; |
| 1444 | 1444 | |
| 1445 | − if (!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type) && | |
| 1445 | + if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type) && | |
| 1446 | 1446 | vb2_is_streaming(vb->vb2_queue) && |
| 1447 | 1447 | v4l2_m2m_dst_buf_is_last(ctx->fh.m2m_ctx)) { |
| 1448 | 1448 | unsigned int i; |
| @@ -1479,7 +1479,7 @@ static void vicodec_buf_queue(struct vb2_buffer *vb) · 30 unmodified lines | ||
| 1479 | 1479 | * in the compressed stream |
| 1480 | 1480 | */ |
| 1481 | 1481 | if (ctx->is_stateless || ctx->is_enc || |
| 1482 | − !V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { | |
| 1482 | + V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) { | |
| 1483 | 1483 | v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); |
| 1484 | 1484 | return; |
| 1485 | 1485 | } |
| @@ -1574,7 +1574,7 @@ static int vicodec_start_streaming(struct vb2_queue *q, · 88 unmodified lines | ||
| 1574 | 1574 | state->gop_cnt = 0; |
| 1575 | 1575 | |
| 1576 | 1576 | if ((V4L2_TYPE_IS_OUTPUT(q->type) && !ctx->is_enc) || |
| 1577 | − (!V4L2_TYPE_IS_OUTPUT(q->type) && ctx->is_enc)) | |
| 1577 | + (V4L2_TYPE_IS_CAPTURE(q->type) && ctx->is_enc)) | |
| 1578 | 1578 | return 0; |
| 1579 | 1579 | |
| 1580 | 1580 | if (info->id == V4L2_PIX_FMT_FWHT || |
| @@ -2,6 +2,8 @@ · 1 unmodified line | ||
| 2 | 2 | config VIDEO_VIMC |
| 3 | 3 | tristate "Virtual Media Controller Driver (VIMC)" |
| 4 | 4 | depends on VIDEO_DEV && VIDEO_V4L2 |
| 5 | + select FONT_SUPPORT | |
| 6 | + select FONT_8x16 | |
| 5 | 7 | select MEDIA_CONTROLLER |
| 6 | 8 | select VIDEO_V4L2_SUBDEV_API |
| 7 | 9 | select VIDEOBUF2_VMALLOC |
| @@ -20,6 +20,7 @@ · 19 unmodified lines | ||
| 20 | 20 | #define VIMC_CID_VIMC_CLASS (0x00f00000 | 1) |
| 21 | 21 | #define VIMC_CID_TEST_PATTERN (VIMC_CID_VIMC_BASE + 0) |
| 22 | 22 | #define VIMC_CID_MEAN_WIN_SIZE (VIMC_CID_VIMC_BASE + 1) |
| 23 | +#define VIMC_CID_OSD_TEXT_MODE (VIMC_CID_VIMC_BASE + 2) | |
| 23 | 24 | |
| 24 | 25 | #define VIMC_FRAME_MAX_WIDTH 4096 |
| 25 | 26 | #define VIMC_FRAME_MAX_HEIGHT 2160 |
| @@ -5,10 +5,12 @@ · 4 unmodified lines | ||
| 5 | 5 | * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com> |
| 6 | 6 | */ |
| 7 | 7 | |
| 8 | +#include <linux/font.h> | |
| 8 | 9 | #include <linux/init.h> |
| 9 | 10 | #include <linux/module.h> |
| 10 | 11 | #include <linux/platform_device.h> |
| 11 | 12 | #include <media/media-device.h> |
| 13 | +#include <media/tpg/v4l2-tpg.h> | |
| 12 | 14 | #include <media/v4l2-device.h> |
| 13 | 15 | |
| 14 | 16 | #include "vimc-common.h" |
| @@ -263,11 +265,19 @@ err_v4l2_unregister: · 248 unmodified lines | ||
| 263 | 265 | |
| 264 | 266 | static int vimc_probe(struct platform_device *pdev) |
| 265 | 267 | { |
| 268 | + const struct font_desc *font = find_font("VGA8x16"); | |
| 266 | 269 | struct vimc_device *vimc; |
| 267 | 270 | int ret; |
| 268 | 271 | |
| 269 | 272 | dev_dbg(&pdev->dev, "probe"); |
| 270 | 273 | |
| 274 | + if (!font) { | |
| 275 | + dev_err(&pdev->dev, "could not find font\n"); | |
| 276 | + return -ENODEV; | |
| 277 | + } | |
| 278 | + | |
| 279 | + tpg_set_font(font->data); | |
| 280 | + | |
| 271 | 281 | vimc = kzalloc(sizeof(*vimc), GFP_KERNEL); |
| 272 | 282 | if (!vimc) |
| 273 | 283 | return -ENOMEM; |
| @@ -14,11 +14,19 @@ · 13 unmodified lines | ||
| 14 | 14 | |
| 15 | 15 | #include "vimc-common.h" |
| 16 | 16 | |
| 17 | +enum vimc_sen_osd_mode { | |
| 18 | + VIMC_SEN_OSD_SHOW_ALL = 0, | |
| 19 | + VIMC_SEN_OSD_SHOW_COUNTERS = 1, | |
| 20 | + VIMC_SEN_OSD_SHOW_NONE = 2 | |
| 21 | +}; | |
| 22 | + | |
| 17 | 23 | struct vimc_sen_device { |
| 18 | 24 | struct vimc_ent_device ved; |
| 19 | 25 | struct v4l2_subdev sd; |
| 20 | 26 | struct tpg_data tpg; |
| 21 | 27 | u8 *frame; |
| 28 | + enum vimc_sen_osd_mode osd_value; | |
| 29 | + u64 start_stream_ts; | |
| 22 | 30 | /* The active format */ |
| 23 | 31 | struct v4l2_mbus_framefmt mbus_format; |
| 24 | 32 | struct v4l2_ctrl_handler hdl; |
| @@ -187,8 +195,49 @@ static void *vimc_sen_process_frame(struct vimc_ent_device *ved, · 162 unmodified lines | ||
| 187 | 195 | { |
| 188 | 196 | struct vimc_sen_device *vsen = container_of(ved, struct vimc_sen_device, |
| 189 | 197 | ved); |
| 198 | + const unsigned int line_height = 16; | |
| 199 | + u8 *basep[TPG_MAX_PLANES][2]; | |
| 200 | + unsigned int line = 1; | |
| 201 | + char str[100]; | |
| 190 | 202 | |
| 191 | 203 | tpg_fill_plane_buffer(&vsen->tpg, 0, 0, vsen->frame); |
| 204 | + tpg_calc_text_basep(&vsen->tpg, basep, 0, vsen->frame); | |
| 205 | + switch (vsen->osd_value) { | |
| 206 | + case VIMC_SEN_OSD_SHOW_ALL: { | |
| 207 | + const char *order = tpg_g_color_order(&vsen->tpg); | |
| 208 | + | |
| 209 | + tpg_gen_text(&vsen->tpg, basep, line++ * line_height, | |
| 210 | + 16, order); | |
| 211 | + snprintf(str, sizeof(str), | |
| 212 | + "brightness %3d, contrast %3d, saturation %3d, hue %d ", | |
| 213 | + vsen->tpg.brightness, | |
| 214 | + vsen->tpg.contrast, | |
| 215 | + vsen->tpg.saturation, | |
| 216 | + vsen->tpg.hue); | |
| 217 | + tpg_gen_text(&vsen->tpg, basep, line++ * line_height, 16, str); | |
| 218 | + snprintf(str, sizeof(str), "sensor size: %dx%d", | |
| 219 | + vsen->mbus_format.width, | |
| 220 | + vsen->mbus_format.height); | |
| 221 | + tpg_gen_text(&vsen->tpg, basep, line++ * line_height, 16, str); | |
| 222 | + fallthrough; | |
| 223 | + } | |
| 224 | + case VIMC_SEN_OSD_SHOW_COUNTERS: { | |
| 225 | + unsigned int ms; | |
| 226 | + | |
| 227 | + ms = div_u64(ktime_get_ns() - vsen->start_stream_ts, 1000000); | |
| 228 | + snprintf(str, sizeof(str), "%02d:%02d:%02d:%03d", | |
| 229 | + (ms / (60 * 60 * 1000)) % 24, | |
| 230 | + (ms / (60 * 1000)) % 60, | |
| 231 | + (ms / 1000) % 60, | |
| 232 | + ms % 1000); | |
| 233 | + tpg_gen_text(&vsen->tpg, basep, line++ * line_height, 16, str); | |
| 234 | + break; | |
| 235 | + } | |
| 236 | + case VIMC_SEN_OSD_SHOW_NONE: | |
| 237 | + default: | |
| 238 | + break; | |
| 239 | + } | |
| 240 | + | |
| 192 | 241 | return vsen->frame; |
| 193 | 242 | } |
| 194 | 243 | |
| @@ -201,6 +250,8 @@ static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable) · 6 unmodified lines | ||
| 201 | 250 | const struct vimc_pix_map *vpix; |
| 202 | 251 | unsigned int frame_size; |
| 203 | 252 | |
| 253 | + vsen->start_stream_ts = ktime_get_ns(); | |
| 254 | + | |
| 204 | 255 | /* Calculate the frame size */ |
| 205 | 256 | vpix = vimc_pix_map_by_code(vsen->mbus_format.code); |
| 206 | 257 | frame_size = vsen->mbus_format.width * vpix->bpp * |
| @@ -269,6 +320,9 @@ static int vimc_sen_s_ctrl(struct v4l2_ctrl *ctrl) · 62 unmodified lines | ||
| 269 | 320 | case V4L2_CID_SATURATION: |
| 270 | 321 | tpg_s_saturation(&vsen->tpg, ctrl->val); |
| 271 | 322 | break; |
| 323 | + case VIMC_CID_OSD_TEXT_MODE: | |
| 324 | + vsen->osd_value = ctrl->val; | |
| 325 | + break; | |
| 272 | 326 | default: |
| 273 | 327 | return -EINVAL; |
| 274 | 328 | } |
| @@ -307,6 +361,22 @@ static const struct v4l2_ctrl_config vimc_sen_ctrl_test_pattern = { · 32 unmodified lines | ||
| 307 | 361 | .qmenu = tpg_pattern_strings, |
| 308 | 362 | }; |
| 309 | 363 | |
| 364 | +static const char * const vimc_ctrl_osd_mode_strings[] = { | |
| 365 | + "All", | |
| 366 | + "Counters Only", | |
| 367 | + "None", | |
| 368 | + NULL, | |
| 369 | +}; | |
| 370 | + | |
| 371 | +static const struct v4l2_ctrl_config vimc_sen_ctrl_osd_mode = { | |
| 372 | + .ops = &vimc_sen_ctrl_ops, | |
| 373 | + .id = VIMC_CID_OSD_TEXT_MODE, | |
| 374 | + .name = "Show Information", | |
| 375 | + .type = V4L2_CTRL_TYPE_MENU, | |
| 376 | + .max = ARRAY_SIZE(vimc_ctrl_osd_mode_strings) - 2, | |
| 377 | + .qmenu = vimc_ctrl_osd_mode_strings, | |
| 378 | +}; | |
| 379 | + | |
| 310 | 380 | static struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc, |
| 311 | 381 | const char *vcfg_name) |
| 312 | 382 | { |
| @@ -323,6 +393,7 @@ static struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc, · 10 unmodified lines | ||
| 323 | 393 | |
| 324 | 394 | v4l2_ctrl_new_custom(&vsen->hdl, &vimc_sen_ctrl_class, NULL); |
| 325 | 395 | v4l2_ctrl_new_custom(&vsen->hdl, &vimc_sen_ctrl_test_pattern, NULL); |
| 396 | + v4l2_ctrl_new_custom(&vsen->hdl, &vimc_sen_ctrl_osd_mode, NULL); | |
| 326 | 397 | v4l2_ctrl_new_std(&vsen->hdl, &vimc_sen_ctrl_ops, |
| 327 | 398 | V4L2_CID_VFLIP, 0, 1, 1, 0); |
| 328 | 399 | v4l2_ctrl_new_std(&vsen->hdl, &vimc_sen_ctrl_ops, |
| @@ -169,6 +169,14 @@ MODULE_PARM_DESC(allocators, " memory allocator selection, default is 0.\n" · 168 unmodified lines | ||
| 169 | 169 | "\t\t 0 == vmalloc\n" |
| 170 | 170 | "\t\t 1 == dma-contig"); |
| 171 | 171 | |
| 172 | +static unsigned int cache_hints[VIVID_MAX_DEVS] = { | |
| 173 | + [0 ... (VIVID_MAX_DEVS - 1)] = 0 | |
| 174 | +}; | |
| 175 | +module_param_array(cache_hints, uint, NULL, 0444); | |
| 176 | +MODULE_PARM_DESC(cache_hints, " user-space cache hints, default is 0.\n" | |
| 177 | + "\t\t 0 == forbid\n" | |
| 178 | + "\t\t 1 == allow"); | |
| 179 | + | |
| 172 | 180 | static struct vivid_dev *vivid_devs[VIVID_MAX_DEVS]; |
| 173 | 181 | |
| 174 | 182 | const struct v4l2_rect vivid_min_rect = { |
| @@ -819,6 +827,7 @@ static int vivid_create_queue(struct vivid_dev *dev, · 644 unmodified lines | ||
| 819 | 827 | q->lock = &dev->mutex; |
| 820 | 828 | q->dev = dev->v4l2_dev.dev; |
| 821 | 829 | q->supports_requests = true; |
| 830 | + q->allow_cache_hints = (cache_hints[dev->inst] == 1); | |
| 822 | 831 | |
| 823 | 832 | return vb2_queue_init(q); |
| 824 | 833 | } |
| @@ -1117,7 +1126,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) · 292 unmodified lines | ||
| 1117 | 1126 | ret = -ENOMEM; |
| 1118 | 1127 | /* initialize the test pattern generator */ |
| 1119 | 1128 | tpg_init(&dev->tpg, 640, 360); |
| 1120 | − if (tpg_alloc(&dev->tpg, MAX_ZOOM * MAX_WIDTH)) | |
| 1129 | + if (tpg_alloc(&dev->tpg, array_size(MAX_WIDTH, MAX_ZOOM))) | |
| 1121 | 1130 | goto free_dev; |
| 1122 | 1131 | dev->scaled_line = vzalloc(array_size(MAX_WIDTH, MAX_ZOOM)); |
| 1123 | 1132 | if (!dev->scaled_line) |
| @@ -1127,7 +1136,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) · 3 unmodified lines | ||
| 1127 | 1136 | goto free_dev; |
| 1128 | 1137 | |
| 1129 | 1138 | /* load the edid */ |
| 1130 | − dev->edid = vmalloc(256 * 128); | |
| 1139 | + dev->edid = vmalloc(array_size(256, 128)); | |
| 1131 | 1140 | if (!dev->edid) |
| 1132 | 1141 | goto free_dev; |
| 1133 | 1142 | |
| @@ -1734,7 +1734,7 @@ static void cx231xx_video_dev_init( · 1733 unmodified lines | ||
| 1734 | 1734 | int cx231xx_417_register(struct cx231xx *dev) |
| 1735 | 1735 | { |
| 1736 | 1736 | /* FIXME: Port1 hardcoded here */ |
| 1737 | − int err = -ENODEV; | |
| 1737 | + int err; | |
| 1738 | 1738 | struct cx231xx_tsport *tsport = &dev->ts1; |
| 1739 | 1739 | struct vb2_queue *q; |
| 1740 | 1740 | |
| @@ -778,16 +778,16 @@ static const struct usb_device_id dvbsky_id_table[] = { · 777 unmodified lines | ||
| 778 | 778 | &dvbsky_s960_props, "Terratec Cinergy S2 Rev.4", |
| 779 | 779 | RC_MAP_DVBSKY) }, |
| 780 | 780 | { DVB_USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230, |
| 781 | − &mygica_t230c_props, "MyGica Mini DVB-T2 USB Stick T230", | |
| 781 | + &mygica_t230c_props, "MyGica Mini DVB-(T/T2/C) USB Stick T230", | |
| 782 | 782 | RC_MAP_TOTAL_MEDIA_IN_HAND_02) }, |
| 783 | 783 | { DVB_USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230C, |
| 784 | − &mygica_t230c_props, "MyGica Mini DVB-T2 USB Stick T230C", | |
| 784 | + &mygica_t230c_props, "MyGica Mini DVB-(T/T2/C) USB Stick T230C", | |
| 785 | 785 | RC_MAP_TOTAL_MEDIA_IN_HAND_02) }, |
| 786 | 786 | { DVB_USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230C_LITE, |
| 787 | − &mygica_t230c_props, "MyGica Mini DVB-T2 USB Stick T230C Lite", | |
| 787 | + &mygica_t230c_props, "MyGica Mini DVB-(T/T2/C) USB Stick T230C Lite", | |
| 788 | 788 | NULL) }, |
| 789 | 789 | { DVB_USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230C2, |
| 790 | − &mygica_t230c_props, "MyGica Mini DVB-T2 USB Stick T230C v2", | |
| 790 | + &mygica_t230c_props, "MyGica Mini DVB-(T/T2/C) USB Stick T230C v2", | |
| 791 | 791 | RC_MAP_TOTAL_MEDIA_IN_HAND_02) }, |
| 792 | 792 | { } |
| 793 | 793 | }; |
| @@ -151,6 +151,7 @@ config DVB_USB_CXUSB · 150 unmodified lines | ||
| 151 | 151 | config DVB_USB_CXUSB_ANALOG |
| 152 | 152 | bool "Analog support for the Conexant USB2.0 hybrid reference design" |
| 153 | 153 | depends on DVB_USB_CXUSB && VIDEO_V4L2 |
| 154 | + depends on VIDEO_V4L2=y || VIDEO_V4L2=DVB_USB_CXUSB | |
| 154 | 155 | select VIDEO_CX25840 |
| 155 | 156 | select VIDEOBUF2_VMALLOC |
| 156 | 157 | help |
| @@ -1132,6 +1132,10 @@ static int go7007_usb_probe(struct usb_interface *intf, · 1131 unmodified lines | ||
| 1132 | 1132 | go->hpi_ops = &go7007_usb_onboard_hpi_ops; |
| 1133 | 1133 | go->hpi_context = usb; |
| 1134 | 1134 | |
| 1135 | + ep = usb->usbdev->ep_in[4]; | |
| 1136 | + if (!ep) | |
| 1137 | + return -ENODEV; | |
| 1138 | + | |
| 1135 | 1139 | /* Allocate the URB and buffer for receiving incoming interrupts */ |
| 1136 | 1140 | usb->intr_urb = usb_alloc_urb(0, GFP_KERNEL); |
| 1137 | 1141 | if (usb->intr_urb == NULL) |
| @@ -1141,7 +1145,6 @@ static int go7007_usb_probe(struct usb_interface *intf, · 3 unmodified lines | ||
| 1141 | 1145 | if (usb->intr_urb->transfer_buffer == NULL) |
| 1142 | 1146 | goto allocfail; |
| 1143 | 1147 | |
| 1144 | − ep = usb->usbdev->ep_in[4]; | |
| 1145 | 1148 | if (usb_endpoint_type(&ep->desc) == USB_ENDPOINT_XFER_BULK) |
| 1146 | 1149 | usb_fill_bulk_urb(usb->intr_urb, usb->usbdev, |
| 1147 | 1150 | usb_rcvbulkpipe(usb->usbdev, 4), |
| @@ -1263,9 +1266,13 @@ static int go7007_usb_probe(struct usb_interface *intf, · 115 unmodified lines | ||
| 1263 | 1266 | |
| 1264 | 1267 | /* Allocate the URBs and buffers for receiving the video stream */ |
| 1265 | 1268 | if (board->flags & GO7007_USB_EZUSB) { |
| 1269 | + if (!usb->usbdev->ep_in[6]) | |
| 1270 | + goto allocfail; | |
| 1266 | 1271 | v_urb_len = 1024; |
| 1267 | 1272 | video_pipe = usb_rcvbulkpipe(usb->usbdev, 6); |
| 1268 | 1273 | } else { |
| 1274 | + if (!usb->usbdev->ep_in[1]) | |
| 1275 | + goto allocfail; | |
| 1269 | 1276 | v_urb_len = 512; |
| 1270 | 1277 | video_pipe = usb_rcvbulkpipe(usb->usbdev, 1); |
| 1271 | 1278 | } |
| @@ -1285,6 +1292,8 @@ static int go7007_usb_probe(struct usb_interface *intf, · 13 unmodified lines | ||
| 1285 | 1292 | /* Allocate the URBs and buffers for receiving the audio stream */ |
| 1286 | 1293 | if ((board->flags & GO7007_USB_EZUSB) && |
| 1287 | 1294 | (board->main_info.flags & GO7007_BOARD_HAS_AUDIO)) { |
| 1295 | + if (!usb->usbdev->ep_in[8]) | |
| 1296 | + goto allocfail; | |
| 1288 | 1297 | for (i = 0; i < 8; ++i) { |
| 1289 | 1298 | usb->audio_urbs[i] = usb_alloc_urb(0, GFP_KERNEL); |
| 1290 | 1299 | if (usb->audio_urbs[i] == NULL) |
| @@ -925,7 +925,7 @@ static int wxh_to_nearest_mode(struct gspca_dev *gspca_dev, · 924 unmodified lines | ||
| 925 | 925 | { |
| 926 | 926 | int i; |
| 927 | 927 | |
| 928 | − for (i = gspca_dev->cam.nmodes; --i > 0; ) { | |
| 928 | + for (i = gspca_dev->cam.nmodes; --i >= 0; ) { | |
| 929 | 929 | if (width >= gspca_dev->cam.cam_mode[i].width |
| 930 | 930 | && height >= gspca_dev->cam.cam_mode[i].height |
| 931 | 931 | && pixelformat == gspca_dev->cam.cam_mode[i].pixelformat) |
| @@ -165,7 +165,7 @@ static int jl2005c_get_firmware_id(struct gspca_dev *gspca_dev) · 164 unmodified lines | ||
| 165 | 165 | { |
| 166 | 166 | struct sd *sd = (struct sd *)gspca_dev; |
| 167 | 167 | int i = 0; |
| 168 | − int retval = -1; | |
| 168 | + int retval; | |
| 169 | 169 | unsigned char regs_to_read[] = {0x57, 0x02, 0x03, 0x5d, 0x5e, 0x5f}; |
| 170 | 170 | |
| 171 | 171 | gspca_dbg(gspca_dev, D_PROBE, "Running jl2005c_get_firmware_id\n"); |
| @@ -14,7 +14,7 @@ · 13 unmodified lines | ||
| 14 | 14 | * PS3 Eye camera - brightness, contrast, awb, agc, aec controls |
| 15 | 15 | * added by Max Thrun <bear24rw@gmail.com> |
| 16 | 16 | * PS3 Eye camera - FPS range extended by Joseph Howse |
| 17 | − * <josephhowse@nummist.com> http://nummist.com | |
| 17 | + * <josephhowse@nummist.com> https://nummist.com | |
| 18 | 18 | */ |
| 19 | 19 | |
| 20 | 20 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
| @@ -215,7 +215,7 @@ static int sd_config(struct gspca_dev *gspca_dev, · 214 unmodified lines | ||
| 215 | 215 | /* this function is called at probe and resume time */ |
| 216 | 216 | static int sd_init(struct gspca_dev *gspca_dev) |
| 217 | 217 | { |
| 218 | − int status = -1; | |
| 218 | + int status; | |
| 219 | 219 | |
| 220 | 220 | sn9c2028_read1(gspca_dev); |
| 221 | 221 | sn9c2028_read1(gspca_dev); |
| @@ -50,7 +50,8 @@ static int v4l2_async_notifier_call_complete(struct v4l2_async_notifier *n) · 49 unmodified lines | ||
| 50 | 50 | return n->ops->complete(n); |
| 51 | 51 | } |
| 52 | 52 | |
| 53 | −static bool match_i2c(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) | |
| 53 | +static bool match_i2c(struct v4l2_async_notifier *notifier, | |
| 54 | + struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) | |
| 54 | 55 | { |
| 55 | 56 | #if IS_ENABLED(CONFIG_I2C) |
| 56 | 57 | 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) · 6 unmodified lines | ||
| 63 | 64 | #endif |
| 64 | 65 | } |
| 65 | 66 | |
| 66 | −static bool match_devname(struct v4l2_subdev *sd, | |
| 67 | − struct v4l2_async_subdev *asd) | |
| 67 | +static bool match_devname(struct v4l2_async_notifier *notifier, | |
| 68 | + struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) | |
| 68 | 69 | { |
| 69 | 70 | return !strcmp(asd->match.device_name, dev_name(sd->dev)); |
| 70 | 71 | } |
| 71 | 72 | |
| 72 | −static bool match_fwnode(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) | |
| 73 | +static bool match_fwnode(struct v4l2_async_notifier *notifier, | |
| 74 | + struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) | |
| 73 | 75 | { |
| 74 | − return sd->fwnode == asd->match.fwnode; | |
| 76 | + struct fwnode_handle *other_fwnode; | |
| 77 | + struct fwnode_handle *dev_fwnode; | |
| 78 | + bool asd_fwnode_is_ep; | |
| 79 | + bool sd_fwnode_is_ep; | |
| 80 | + struct device *dev; | |
| 81 | + | |
| 82 | + /* | |
| 83 | + * Both the subdev and the async subdev can provide either an endpoint | |
| 84 | + * fwnode or a device fwnode. Start with the simple case of direct | |
| 85 | + * fwnode matching. | |
| 86 | + */ | |
| 87 | + if (sd->fwnode == asd->match.fwnode) | |
| 88 | + return true; | |
| 89 | + | |
| 90 | + /* | |
| 91 | + * Otherwise, check if the sd fwnode and the asd fwnode refer to an | |
| 92 | + * endpoint or a device. If they're of the same type, there's no match. | |
| 93 | + * Technically speaking this checks if the nodes refer to a connected | |
| 94 | + * endpoint, which is the simplest check that works for both OF and | |
| 95 | + * ACPI. This won't make a difference, as drivers should not try to | |
| 96 | + * match unconnected endpoints. | |
| 97 | + */ | |
| 98 | + sd_fwnode_is_ep = fwnode_graph_is_endpoint(sd->fwnode); | |
| 99 | + asd_fwnode_is_ep = fwnode_graph_is_endpoint(asd->match.fwnode); | |
| 100 | + | |
| 101 | + if (sd_fwnode_is_ep == asd_fwnode_is_ep) | |
| 102 | + return false; | |
| 103 | + | |
| 104 | + /* | |
| 105 | + * The sd and asd fwnodes are of different types. Get the device fwnode | |
| 106 | + * parent of the endpoint fwnode, and compare it with the other fwnode. | |
| 107 | + */ | |
| 108 | + if (sd_fwnode_is_ep) { | |
| 109 | + dev_fwnode = fwnode_graph_get_port_parent(sd->fwnode); | |
| 110 | + other_fwnode = asd->match.fwnode; | |
| 111 | + } else { | |
| 112 | + dev_fwnode = fwnode_graph_get_port_parent(asd->match.fwnode); | |
| 113 | + other_fwnode = sd->fwnode; | |
| 114 | + } | |
| 115 | + | |
| 116 | + fwnode_handle_put(dev_fwnode); | |
| 117 | + | |
| 118 | + if (dev_fwnode != other_fwnode) | |
| 119 | + return false; | |
| 120 | + | |
| 121 | + /* | |
| 122 | + * We have a heterogeneous match. Retrieve the struct device of the side | |
| 123 | + * that matched on a device fwnode to print its driver name. | |
| 124 | + */ | |
| 125 | + if (sd_fwnode_is_ep) | |
| 126 | + dev = notifier->v4l2_dev ? notifier->v4l2_dev->dev | |
| 127 | + : notifier->sd->dev; | |
| 128 | + else | |
| 129 | + dev = sd->dev; | |
| 130 | + | |
| 131 | + if (dev && dev->driver) { | |
| 132 | + if (sd_fwnode_is_ep) | |
| 133 | + dev_warn(dev, "Driver %s uses device fwnode, incorrect match may occur\n", | |
| 134 | + dev->driver->name); | |
| 135 | + dev_notice(dev, "Consider updating driver %s to match on endpoints\n", | |
| 136 | + dev->driver->name); | |
| 137 | + } | |
| 138 | + | |
| 139 | + return true; | |
| 75 | 140 | } |
| 76 | 141 | |
| 77 | −static bool match_custom(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) | |
| 142 | +static bool match_custom(struct v4l2_async_notifier *notifier, | |
| 143 | + struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) | |
| 78 | 144 | { |
| 79 | 145 | if (!asd->match.custom.match) |
| 80 | 146 | /* Match always */ |
| @@ -91,7 +157,8 @@ static struct v4l2_async_subdev * · 10 unmodified lines | ||
| 91 | 157 | v4l2_async_find_match(struct v4l2_async_notifier *notifier, |
| 92 | 158 | struct v4l2_subdev *sd) |
| 93 | 159 | { |
| 94 | − bool (*match)(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd); | |
| 160 | + bool (*match)(struct v4l2_async_notifier *notifier, | |
| 161 | + struct v4l2_subdev *sd, struct v4l2_async_subdev *asd); | |
| 95 | 162 | struct v4l2_async_subdev *asd; |
| 96 | 163 | |
| 97 | 164 | list_for_each_entry(asd, ¬ifier->waiting, list) { |
| @@ -116,7 +183,7 @@ v4l2_async_find_match(struct v4l2_async_notifier *notifier, · 18 unmodified lines | ||
| 116 | 183 | } |
| 117 | 184 | |
| 118 | 185 | /* match cannot be NULL here */ |
| 119 | − if (match(sd, asd)) | |
| 186 | + if (match(notifier, sd, asd)) | |
| 120 | 187 | return asd; |
| 121 | 188 | } |
| 122 | 189 | |
| @@ -246,6 +246,9 @@ struct v4l2_format32 { · 245 unmodified lines | ||
| 246 | 246 | * @memory: buffer memory type |
| 247 | 247 | * @format: frame format, for which buffers are requested |
| 248 | 248 | * @capabilities: capabilities of this buffer type. |
| 249 | + * @flags: additional buffer management attributes (ignored unless the | |
| 250 | + * queue has V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS capability and | |
| 251 | + * configured for MMAP streaming I/O). | |
| 249 | 252 | * @reserved: future extensions |
| 250 | 253 | */ |
| 251 | 254 | struct v4l2_create_buffers32 { |
| @@ -254,7 +257,8 @@ struct v4l2_create_buffers32 { · 2 unmodified lines | ||
| 254 | 257 | __u32 memory; /* enum v4l2_memory */ |
| 255 | 258 | struct v4l2_format32 format; |
| 256 | 259 | __u32 capabilities; |
| 257 | − __u32 reserved[7]; | |
| 260 | + __u32 flags; | |
| 261 | + __u32 reserved[6]; | |
| 258 | 262 | }; |
| 259 | 263 | |
| 260 | 264 | 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, · 94 unmodified lines | ||
| 355 | 359 | { |
| 356 | 360 | if (!access_ok(p32, sizeof(*p32)) || |
| 357 | 361 | copy_in_user(p64, p32, |
| 358 | − offsetof(struct v4l2_create_buffers32, format))) | |
| 362 | + offsetof(struct v4l2_create_buffers32, format)) || | |
| 363 | + assign_in_user(&p64->flags, &p32->flags)) | |
| 359 | 364 | return -EFAULT; |
| 360 | 365 | return __get_v4l2_format32(&p64->format, &p32->format, |
| 361 | 366 | aux_buf, aux_space); |
| @@ -417,6 +422,7 @@ static int put_v4l2_create32(struct v4l2_create_buffers __user *p64, · 55 unmodified lines | ||
| 417 | 422 | copy_in_user(p32, p64, |
| 418 | 423 | offsetof(struct v4l2_create_buffers32, format)) || |
| 419 | 424 | assign_in_user(&p32->capabilities, &p64->capabilities) || |
| 425 | + assign_in_user(&p32->flags, &p64->flags) || | |
| 420 | 426 | copy_in_user(p32->reserved, p64->reserved, sizeof(p64->reserved))) |
| 421 | 427 | return -EFAULT; |
| 422 | 428 | return __put_v4l2_format32(&p64->format, &p32->format); |
| @@ -582,7 +582,10 @@ static void v4l_print_querymenu(const void *arg, bool write_only) · 581 unmodified lines | ||
| 582 | 582 | static void v4l_print_control(const void *arg, bool write_only) |
| 583 | 583 | { |
| 584 | 584 | const struct v4l2_control *p = arg; |
| 585 | + const char *name = v4l2_ctrl_get_name(p->id); | |
| 585 | 586 | |
| 587 | + if (name) | |
| 588 | + pr_cont("name=%s, ", name); | |
| 586 | 589 | pr_cont("id=0x%x, value=%d\n", p->id, p->value); |
| 587 | 590 | } |
| 588 | 591 | |
| @@ -594,12 +597,15 @@ static void v4l_print_ext_controls(const void *arg, bool write_only) · 5 unmodified lines | ||
| 594 | 597 | pr_cont("which=0x%x, count=%d, error_idx=%d, request_fd=%d", |
| 595 | 598 | p->which, p->count, p->error_idx, p->request_fd); |
| 596 | 599 | for (i = 0; i < p->count; i++) { |
| 600 | + unsigned int id = p->controls[i].id; | |
| 601 | + const char *name = v4l2_ctrl_get_name(id); | |
| 602 | + | |
| 603 | + if (name) | |
| 604 | + pr_cont(", name=%s", name); | |
| 597 | 605 | if (!p->controls[i].size) |
| 598 | − pr_cont(", id/val=0x%x/0x%x", | |
| 599 | − p->controls[i].id, p->controls[i].value); | |
| 606 | + pr_cont(", id/val=0x%x/0x%x", id, p->controls[i].value); | |
| 600 | 607 | else |
| 601 | − pr_cont(", id/size=0x%x/%u", | |
| 602 | − p->controls[i].id, p->controls[i].size); | |
| 608 | + pr_cont(", id/size=0x%x/%u", id, p->controls[i].size); | |
| 603 | 609 | } |
| 604 | 610 | pr_cont("\n"); |
| 605 | 611 | } |
| @@ -2038,9 +2044,6 @@ static int v4l_reqbufs(const struct v4l2_ioctl_ops *ops, · 1432 unmodified lines | ||
| 2038 | 2044 | |
| 2039 | 2045 | if (ret) |
| 2040 | 2046 | return ret; |
| 2041 | − | |
| 2042 | − CLEAR_AFTER_FIELD(p, capabilities); | |
| 2043 | − | |
| 2044 | 2047 | return ops->vidioc_reqbufs(file, fh, p); |
| 2045 | 2048 | } |
| 2046 | 2049 | |
| @@ -2080,7 +2083,7 @@ static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops, · 33 unmodified lines | ||
| 2080 | 2083 | if (ret) |
| 2081 | 2084 | return ret; |
| 2082 | 2085 | |
| 2083 | − CLEAR_AFTER_FIELD(create, capabilities); | |
| 2086 | + CLEAR_AFTER_FIELD(create, flags); | |
| 2084 | 2087 | |
| 2085 | 2088 | v4l_sanitize_format(&create->format); |
| 2086 | 2089 | |
| @@ -556,7 +556,7 @@ int v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, · 555 unmodified lines | ||
| 556 | 556 | ret = vb2_querybuf(vq, buf); |
| 557 | 557 | |
| 558 | 558 | /* Adjust MMAP memory offsets for the CAPTURE queue */ |
| 559 | − if (buf->memory == V4L2_MEMORY_MMAP && !V4L2_TYPE_IS_OUTPUT(vq->type)) { | |
| 559 | + if (buf->memory == V4L2_MEMORY_MMAP && V4L2_TYPE_IS_CAPTURE(vq->type)) { | |
| 560 | 560 | if (V4L2_TYPE_IS_MULTIPLANAR(vq->type)) { |
| 561 | 561 | for (i = 0; i < buf->length; ++i) |
| 562 | 562 | buf->m.planes[i].m.mem_offset |
| @@ -712,7 +712,7 @@ int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, · 149 unmodified lines | ||
| 712 | 712 | int ret; |
| 713 | 713 | |
| 714 | 714 | vq = v4l2_m2m_get_vq(m2m_ctx, buf->type); |
| 715 | − if (!V4L2_TYPE_IS_OUTPUT(vq->type) && | |
| 715 | + if (V4L2_TYPE_IS_CAPTURE(vq->type) && | |
| 716 | 716 | (buf->flags & V4L2_BUF_FLAG_REQUEST_FD)) { |
| 717 | 717 | dprintk("%s: requests cannot be used with capture buffers\n", |
| 718 | 718 | __func__); |
| @@ -729,7 +729,7 @@ int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, · 10 unmodified lines | ||
| 729 | 729 | * buffer as DONE with LAST flag since it won't be queued on the |
| 730 | 730 | * device. |
| 731 | 731 | */ |
| 732 | − if (!V4L2_TYPE_IS_OUTPUT(vq->type) && | |
| 732 | + if (V4L2_TYPE_IS_CAPTURE(vq->type) && | |
| 733 | 733 | vb2_is_streaming(vq) && !vb2_start_streaming_called(vq) && |
| 734 | 734 | (v4l2_m2m_has_stopped(m2m_ctx) || v4l2_m2m_dst_buf_is_last(m2m_ctx))) |
| 735 | 735 | v4l2_m2m_force_last_buf_done(m2m_ctx, vq); |
| @@ -42,8 +42,6 @@ source "drivers/staging/media/tegra-video/Kconfig" · 41 unmodified lines | ||
| 42 | 42 | |
| 43 | 43 | source "drivers/staging/media/ipu3/Kconfig" |
| 44 | 44 | |
| 45 | −source "drivers/staging/media/soc_camera/Kconfig" | |
| 46 | − | |
| 47 | 45 | source "drivers/staging/media/phy-rockchip-dphy-rx0/Kconfig" |
| 48 | 46 | |
| 49 | 47 | source "drivers/staging/media/rkisp1/Kconfig" |
| @@ -10,7 +10,6 @@ obj-$(CONFIG_VIDEO_TEGRA) += tegra-video/ · 9 unmodified lines | ||
| 10 | 10 | obj-$(CONFIG_TEGRA_VDE) += tegra-vde/ |
| 11 | 11 | obj-$(CONFIG_VIDEO_HANTRO) += hantro/ |
| 12 | 12 | obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3/ |
| 13 | −obj-$(CONFIG_SOC_CAMERA) += soc_camera/ | |
| 14 | 13 | obj-$(CONFIG_PHY_ROCKCHIP_DPHY_RX0) += phy-rockchip-dphy-rx0/ |
| 15 | 14 | obj-$(CONFIG_VIDEO_ROCKCHIP_ISP1) += rkisp1/ |
| 16 | 15 | obj-$(CONFIG_VIDEO_USBVISION) += usbvision/ |
| @@ -105,9 +105,11 @@ struct allegro_buffer { · 104 unmodified lines | ||
| 105 | 105 | struct list_head head; |
| 106 | 106 | }; |
| 107 | 107 | |
| 108 | +struct allegro_dev; | |
| 108 | 109 | struct allegro_channel; |
| 109 | 110 | |
| 110 | 111 | struct allegro_mbox { |
| 112 | + struct allegro_dev *dev; | |
| 111 | 113 | unsigned int head; |
| 112 | 114 | unsigned int tail; |
| 113 | 115 | unsigned int data; |
| @@ -128,14 +130,15 @@ struct allegro_dev { · 14 unmodified lines | ||
| 128 | 130 | struct regmap *regmap; |
| 129 | 131 | struct regmap *sram; |
| 130 | 132 | |
| 133 | + const struct fw_info *fw_info; | |
| 131 | 134 | struct allegro_buffer firmware; |
| 132 | 135 | struct allegro_buffer suballocator; |
| 133 | 136 | |
| 134 | 137 | struct completion init_complete; |
| 135 | 138 | |
| 136 | 139 | /* The mailbox interface */ |
| 137 | − struct allegro_mbox mbox_command; | |
| 138 | − struct allegro_mbox mbox_status; | |
| 140 | + struct allegro_mbox *mbox_command; | |
| 141 | + struct allegro_mbox *mbox_status; | |
| 139 | 142 | |
| 140 | 143 | /* |
| 141 | 144 | * The downstream driver limits the users to 64 users, thus I can use |
| @@ -204,6 +207,11 @@ struct allegro_channel { · 62 unmodified lines | ||
| 204 | 207 | unsigned int cpb_size; |
| 205 | 208 | unsigned int gop_size; |
| 206 | 209 | |
| 210 | + struct allegro_buffer config_blob; | |
| 211 | + | |
| 212 | + unsigned int num_ref_idx_l0; | |
| 213 | + unsigned int num_ref_idx_l1; | |
| 214 | + | |
| 207 | 215 | struct v4l2_ctrl *mpeg_video_h264_profile; |
| 208 | 216 | struct v4l2_ctrl *mpeg_video_h264_level; |
| 209 | 217 | struct v4l2_ctrl *mpeg_video_h264_i_frame_qp; |
| @@ -270,6 +278,7 @@ struct fw_info { · 60 unmodified lines | ||
| 270 | 278 | unsigned int mailbox_cmd; |
| 271 | 279 | unsigned int mailbox_status; |
| 272 | 280 | size_t mailbox_size; |
| 281 | + enum mcu_msg_version mailbox_version; | |
| 273 | 282 | size_t suballocator_size; |
| 274 | 283 | }; |
| 275 | 284 | |
| @@ -281,7 +290,17 @@ static const struct fw_info supported_firmware[] = { · 5 unmodified lines | ||
| 281 | 290 | .mailbox_cmd = 0x7800, |
| 282 | 291 | .mailbox_status = 0x7c00, |
| 283 | 292 | .mailbox_size = 0x400 - 0x8, |
| 293 | + .mailbox_version = MCU_MSG_VERSION_2018_2, | |
| 284 | 294 | .suballocator_size = SZ_16M, |
| 295 | + }, { | |
| 296 | + .id = 14680, | |
| 297 | + .id_codec = 126572, | |
| 298 | + .version = "v2019.2", | |
| 299 | + .mailbox_cmd = 0x7000, | |
| 300 | + .mailbox_status = 0x7800, | |
| 301 | + .mailbox_size = 0x800 - 0x8, | |
| 302 | + .mailbox_version = MCU_MSG_VERSION_2019_2, | |
| 303 | + .suballocator_size = SZ_32M, | |
| 285 | 304 | }, |
| 286 | 305 | }; |
| 287 | 306 | |
| @@ -583,12 +602,20 @@ static void allegro_free_buffer(struct allegro_dev *dev, · 295 unmodified lines | ||
| 583 | 602 | * Mailbox interface to send messages to the MCU. |
| 584 | 603 | */ |
| 585 | 604 | |
| 586 | −static int allegro_mbox_init(struct allegro_dev *dev, | |
| 587 | − struct allegro_mbox *mbox, | |
| 588 | − unsigned int base, size_t size) | |
| 605 | +static void allegro_mcu_interrupt(struct allegro_dev *dev); | |
| 606 | +static void allegro_handle_message(struct allegro_dev *dev, | |
| 607 | + union mcu_msg_response *msg); | |
| 608 | + | |
| 609 | +static struct allegro_mbox *allegro_mbox_init(struct allegro_dev *dev, | |
| 610 | + unsigned int base, size_t size) | |
| 589 | 611 | { |
| 612 | + struct allegro_mbox *mbox; | |
| 613 | + | |
| 614 | + mbox = devm_kmalloc(&dev->plat_dev->dev, sizeof(*mbox), GFP_KERNEL); | |
| 590 | 615 | if (!mbox) |
| 591 | − return -EINVAL; | |
| 616 | + return ERR_PTR(-ENOMEM); | |
| 617 | + | |
| 618 | + mbox->dev = dev; | |
| 592 | 619 | |
| 593 | 620 | mbox->head = base; |
| 594 | 621 | mbox->tail = base + 0x4; |
| @@ -599,52 +626,37 @@ static int allegro_mbox_init(struct allegro_dev *dev, · 4 unmodified lines | ||
| 599 | 626 | regmap_write(dev->sram, mbox->head, 0); |
| 600 | 627 | regmap_write(dev->sram, mbox->tail, 0); |
| 601 | 628 | |
| 602 | − return 0; | |
| 629 | + return mbox; | |
| 603 | 630 | } |
| 604 | 631 | |
| 605 | −static int allegro_mbox_write(struct allegro_dev *dev, | |
| 606 | − struct allegro_mbox *mbox, void *src, size_t size) | |
| 632 | +static int allegro_mbox_write(struct allegro_mbox *mbox, | |
| 633 | + const u32 *src, size_t size) | |
| 607 | 634 | { |
| 608 | − struct mcu_msg_header *header = src; | |
| 635 | + struct regmap *sram = mbox->dev->sram; | |
| 609 | 636 | unsigned int tail; |
| 610 | 637 | size_t size_no_wrap; |
| 611 | 638 | int err = 0; |
| 639 | + int stride = regmap_get_reg_stride(sram); | |
| 612 | 640 | |
| 613 | 641 | if (!src) |
| 614 | 642 | return -EINVAL; |
| 615 | 643 | |
| 616 | − if (size > mbox->size) { | |
| 617 | − v4l2_err(&dev->v4l2_dev, | |
| 618 | − "message (%zu bytes) too large for mailbox (%zu bytes)\n", | |
| 619 | − size, mbox->size); | |
| 644 | + if (size > mbox->size) | |
| 620 | 645 | return -EINVAL; |
| 621 | − } | |
| 622 | − | |
| 623 | − if (header->length != size - sizeof(*header)) { | |
| 624 | − v4l2_err(&dev->v4l2_dev, | |
| 625 | − "invalid message length: %u bytes (expected %zu bytes)\n", | |
| 626 | − header->length, size - sizeof(*header)); | |
| 627 | − return -EINVAL; | |
| 628 | − } | |
| 629 | − | |
| 630 | − v4l2_dbg(2, debug, &dev->v4l2_dev, | |
| 631 | − "write command message: type %s, body length %d\n", | |
| 632 | − msg_type_name(header->type), header->length); | |
| 633 | 646 | |
| 634 | 647 | mutex_lock(&mbox->lock); |
| 635 | − regmap_read(dev->sram, mbox->tail, &tail); | |
| 648 | + regmap_read(sram, mbox->tail, &tail); | |
| 636 | 649 | if (tail > mbox->size) { |
| 637 | − v4l2_err(&dev->v4l2_dev, | |
| 638 | − "invalid tail (0x%x): must be smaller than mailbox size (0x%zx)\n", | |
| 639 | − tail, mbox->size); | |
| 640 | 650 | err = -EIO; |
| 641 | 651 | goto out; |
| 642 | 652 | } |
| 643 | 653 | size_no_wrap = min(size, mbox->size - (size_t)tail); |
| 644 | − regmap_bulk_write(dev->sram, mbox->data + tail, src, size_no_wrap / 4); | |
| 645 | − regmap_bulk_write(dev->sram, mbox->data, | |
| 646 | − src + size_no_wrap, (size - size_no_wrap) / 4); | |
| 647 | − regmap_write(dev->sram, mbox->tail, (tail + size) % mbox->size); | |
| 654 | + regmap_bulk_write(sram, mbox->data + tail, | |
| 655 | + src, size_no_wrap / stride); | |
| 656 | + regmap_bulk_write(sram, mbox->data, | |
| 657 | + src + (size_no_wrap / sizeof(*src)), | |
| 658 | + (size - size_no_wrap) / stride); | |
| 659 | + regmap_write(sram, mbox->tail, (tail + size) % mbox->size); | |
| 648 | 660 | |
| 649 | 661 | out: |
| 650 | 662 | mutex_unlock(&mbox->lock); |
| @@ -652,40 +664,32 @@ out: · 1 unmodified line | ||
| 652 | 664 | return err; |
| 653 | 665 | } |
| 654 | 666 | |
| 655 | −static ssize_t allegro_mbox_read(struct allegro_dev *dev, | |
| 656 | − struct allegro_mbox *mbox, | |
| 657 | − void *dst, size_t nbyte) | |
| 667 | +static ssize_t allegro_mbox_read(struct allegro_mbox *mbox, | |
| 668 | + u32 *dst, size_t nbyte) | |
| 658 | 669 | { |
| 659 | − struct mcu_msg_header *header; | |
| 670 | + struct { | |
| 671 | + u16 length; | |
| 672 | + u16 type; | |
| 673 | + } __attribute__ ((__packed__)) *header; | |
| 674 | + struct regmap *sram = mbox->dev->sram; | |
| 660 | 675 | unsigned int head; |
| 661 | 676 | ssize_t size; |
| 662 | 677 | size_t body_no_wrap; |
| 678 | + int stride = regmap_get_reg_stride(sram); | |
| 663 | 679 | |
| 664 | − regmap_read(dev->sram, mbox->head, &head); | |
| 665 | − if (head > mbox->size) { | |
| 666 | − v4l2_err(&dev->v4l2_dev, | |
| 667 | − "invalid head (0x%x): must be smaller than mailbox size (0x%zx)\n", | |
| 668 | − head, mbox->size); | |
| 680 | + regmap_read(sram, mbox->head, &head); | |
| 681 | + if (head > mbox->size) | |
| 669 | 682 | return -EIO; |
| 670 | − } | |
| 671 | 683 | |
| 672 | 684 | /* Assume that the header does not wrap. */ |
| 673 | − regmap_bulk_read(dev->sram, mbox->data + head, | |
| 674 | − dst, sizeof(*header) / 4); | |
| 675 | − header = dst; | |
| 685 | + regmap_bulk_read(sram, mbox->data + head, | |
| 686 | + dst, sizeof(*header) / stride); | |
| 687 | + header = (void *)dst; | |
| 676 | 688 | size = header->length + sizeof(*header); |
| 677 | − if (size > mbox->size || size & 0x3) { | |
| 678 | − v4l2_err(&dev->v4l2_dev, | |
| 679 | − "invalid message length: %zu bytes (maximum %zu bytes)\n", | |
| 680 | − header->length + sizeof(*header), mbox->size); | |
| 689 | + if (size > mbox->size || size & 0x3) | |
| 681 | 690 | return -EIO; |
| 682 | − } | |
| 683 | − if (size > nbyte) { | |
| 684 | − v4l2_err(&dev->v4l2_dev, | |
| 685 | − "destination buffer too small: %zu bytes (need %zu bytes)\n", | |
| 686 | − nbyte, size); | |
| 691 | + if (size > nbyte) | |
| 687 | 692 | return -EINVAL; |
| 688 | − } | |
| 689 | 693 | |
| 690 | 694 | /* |
| 691 | 695 | * 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, · 6 unmodified lines | ||
| 698 | 702 | */ |
| 699 | 703 | body_no_wrap = min((size_t)header->length, |
| 700 | 704 | (size_t)(mbox->size - (head + sizeof(*header)))); |
| 701 | − regmap_bulk_read(dev->sram, mbox->data + head + sizeof(*header), | |
| 702 | − dst + sizeof(*header), body_no_wrap / 4); | |
| 703 | − regmap_bulk_read(dev->sram, mbox->data, | |
| 704 | − dst + sizeof(*header) + body_no_wrap, | |
| 705 | − (header->length - body_no_wrap) / 4); | |
| 706 | − | |
| 707 | − regmap_write(dev->sram, mbox->head, (head + size) % mbox->size); | |
| 705 | + regmap_bulk_read(sram, mbox->data + head + sizeof(*header), | |
| 706 | + dst + (sizeof(*header) / sizeof(*dst)), | |
| 707 | + body_no_wrap / stride); | |
| 708 | + regmap_bulk_read(sram, mbox->data, | |
| 709 | + dst + (sizeof(*header) + body_no_wrap) / sizeof(*dst), | |
| 710 | + (header->length - body_no_wrap) / stride); | |
| 708 | 711 | |
| 709 | − v4l2_dbg(2, debug, &dev->v4l2_dev, | |
| 710 | − "read status message: type %s, body length %d\n", | |
| 711 | − msg_type_name(header->type), header->length); | |
| 712 | + regmap_write(sram, mbox->head, (head + size) % mbox->size); | |
| 712 | 713 | |
| 713 | 714 | return size; |
| 714 | 715 | } |
| 715 | 716 | |
| 716 | −static void allegro_mcu_interrupt(struct allegro_dev *dev) | |
| 717 | +/** | |
| 718 | + * allegro_mbox_send() - Send a message via the mailbox | |
| 719 | + * @mbox: the mailbox which is used to send the message | |
| 720 | + * @msg: the message to send | |
| 721 | + */ | |
| 722 | +static int allegro_mbox_send(struct allegro_mbox *mbox, void *msg) | |
| 717 | 723 | { |
| 718 | − regmap_write(dev->regmap, AL5_MCU_INTERRUPT, BIT(0)); | |
| 724 | + struct allegro_dev *dev = mbox->dev; | |
| 725 | + ssize_t size; | |
| 726 | + int err; | |
| 727 | + u32 *tmp; | |
| 728 | + | |
| 729 | + tmp = kzalloc(mbox->size, GFP_KERNEL); | |
| 730 | + if (!tmp) { | |
| 731 | + err = -ENOMEM; | |
| 732 | + goto out; | |
| 733 | + } | |
| 734 | + | |
| 735 | + size = allegro_encode_mail(tmp, msg); | |
| 736 | + | |
| 737 | + err = allegro_mbox_write(mbox, tmp, size); | |
| 738 | + kfree(tmp); | |
| 739 | + if (err) | |
| 740 | + goto out; | |
| 741 | + | |
| 742 | + allegro_mcu_interrupt(dev); | |
| 743 | + | |
| 744 | +out: | |
| 745 | + return err; | |
| 746 | +} | |
| 747 | + | |
| 748 | +/** | |
| 749 | + * allegro_mbox_notify() - Notify the mailbox about a new message | |
| 750 | + * @mbox: The allegro_mbox to notify | |
| 751 | + */ | |
| 752 | +static void allegro_mbox_notify(struct allegro_mbox *mbox) | |
| 753 | +{ | |
| 754 | + struct allegro_dev *dev = mbox->dev; | |
| 755 | + union mcu_msg_response *msg; | |
| 756 | + ssize_t size; | |
| 757 | + u32 *tmp; | |
| 758 | + int err; | |
| 759 | + | |
| 760 | + msg = kmalloc(sizeof(*msg), GFP_KERNEL); | |
| 761 | + if (!msg) | |
| 762 | + return; | |
439 more lines aren’t shown here.
This diff is large: its 518 lines aren’t shown here.
This diff is large: its 227 lines aren’t shown here.
This diff is large: its 55 lines aren’t shown here.
This diff is large: its 189 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 26 lines aren’t shown here.
This diff is large: its 130 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 37 lines aren’t shown here.
This diff is large: its 29 lines aren’t shown here.
This diff is large: its 11 lines aren’t shown here.
This diff is large: its 11 lines aren’t shown here.
This diff is large: its 11 lines aren’t shown here.
This diff is large: its 11 lines aren’t shown here.
This diff is large: its 6 lines aren’t shown here.
This diff is large: its 116 lines aren’t shown here.
This diff is large: its 87 lines aren’t shown here.
This diff is large: its 25 lines aren’t shown here.
This diff is large: its 262 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 54 lines aren’t shown here.
This diff is large: its 254 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 50 lines aren’t shown here.
This diff is large: its 52 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 5 lines aren’t shown here.
This diff is large: its 493 lines aren’t shown here.
This diff is large: its 854 lines aren’t shown here.
This diff is large: its 172 lines aren’t shown here.
This diff is large: its 2165 lines aren’t shown here.
This diff is large: its 530 lines aren’t shown here.
This diff is large: its 1009 lines aren’t shown here.
This diff is large: its 1086 lines aren’t shown here.
This diff is large: its 993 lines aren’t shown here.
This diff is large: its 94 lines aren’t shown here.
This diff is large: its 13 lines aren’t shown here.
This diff is large: its 33 lines aren’t shown here.
This diff is large: its 12 lines aren’t shown here.
This diff is large: its 85 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 108 lines aren’t shown here.
This diff is large: its 20 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 398 lines aren’t shown here.
This diff is large: its 17 lines aren’t shown here.
This diff is large: its 22 lines aren’t shown here.
This diff is large: its 25 lines aren’t shown here.
This diff is large: its 94 lines aren’t shown here.
This diff is large: its 125 lines aren’t shown here.
This diff is large: its 20 lines aren’t shown here.
This diff is large: its 66 lines aren’t shown here.