diff --git a/Documentation/admin-guide/media/raspberrypi-rp1-cfe.dot b/Documentation/admin-guide/media/raspberrypi-rp1-cfe.dot new file mode 100644 index 00000000000000..7717f229104963 --- /dev/null +++ b/Documentation/admin-guide/media/raspberrypi-rp1-cfe.dot @@ -0,0 +1,27 @@ +digraph board { + rankdir=TB + n00000001 [label="{{ 0} | csi2\n/dev/v4l-subdev0 | { 1 | 2 | 3 | 4}}", shape=Mrecord, style=filled, fillcolor=green] + n00000001:port1 -> n00000011 [style=dashed] + n00000001:port1 -> n00000007:port0 + n00000001:port2 -> n00000015 + n00000001:port2 -> n00000007:port0 [style=dashed] + n00000001:port3 -> n00000019 [style=dashed] + n00000001:port3 -> n00000007:port0 [style=dashed] + n00000001:port4 -> n0000001d [style=dashed] + n00000001:port4 -> n00000007:port0 [style=dashed] + n00000007 [label="{{ 0 | 1} | pisp-fe\n/dev/v4l-subdev1 | { 2 | 3 | 4}}", shape=Mrecord, style=filled, fillcolor=green] + n00000007:port2 -> n00000021 + n00000007:port3 -> n00000025 [style=dashed] + n00000007:port4 -> n00000029 + n0000000d [label="{imx219 6-0010\n/dev/v4l-subdev2 | { 0}}", shape=Mrecord, style=filled, fillcolor=green] + n0000000d:port0 -> n00000001:port0 [style=bold] + n00000011 [label="rp1-cfe-csi2-ch0\n/dev/video0", shape=box, style=filled, fillcolor=yellow] + n00000015 [label="rp1-cfe-csi2-ch1\n/dev/video1", shape=box, style=filled, fillcolor=yellow] + n00000019 [label="rp1-cfe-csi2-ch2\n/dev/video2", shape=box, style=filled, fillcolor=yellow] + n0000001d [label="rp1-cfe-csi2-ch3\n/dev/video3", shape=box, style=filled, fillcolor=yellow] + n00000021 [label="rp1-cfe-fe-image0\n/dev/video4", shape=box, style=filled, fillcolor=yellow] + n00000025 [label="rp1-cfe-fe-image1\n/dev/video5", shape=box, style=filled, fillcolor=yellow] + n00000029 [label="rp1-cfe-fe-stats\n/dev/video6", shape=box, style=filled, fillcolor=yellow] + n0000002d [label="rp1-cfe-fe-config\n/dev/video7", shape=box, style=filled, fillcolor=yellow] + n0000002d -> n00000007:port1 +} diff --git a/Documentation/admin-guide/media/raspberrypi-rp1-cfe.rst b/Documentation/admin-guide/media/raspberrypi-rp1-cfe.rst new file mode 100644 index 00000000000000..668d978a9875b7 --- /dev/null +++ b/Documentation/admin-guide/media/raspberrypi-rp1-cfe.rst @@ -0,0 +1,78 @@ +.. SPDX-License-Identifier: GPL-2.0 + +============================================ +Raspberry Pi PiSP Camera Front End (rp1-cfe) +============================================ + +The PiSP Camera Front End +========================= + +The PiSP Camera Front End (CFE) is a module which combines a CSI-2 receiver with +a simple ISP, called the Front End (FE). + +The CFE has four DMA engines and can write frames from four separate streams +received from the CSI-2 to the memory. One of those streams can also be routed +directly to the FE, which can do minimal image processing, write two versions +(e.g. non-scaled and downscaled versions) of the received frames to memory and +provide statistics of the received frames. + +The FE registers are documented in the `Raspberry Pi Image Signal Processor +(ISP) Specification document +`_, +and example code for FE can be found in `libpisp +`_. + +The rp1-cfe driver +================== + +The Raspberry Pi PiSP Camera Front End (rp1-cfe) driver is located under +drivers/media/platform/raspberrypi/rp1-cfe. It uses the `V4L2 API` to register +a number of video capture and output devices, the `V4L2 subdev API` to register +subdevices for the CSI-2 received and the FE that connects the video devices in +a single media graph realized using the `Media Controller (MC) API`. + +The media topology registered by the `rp1-cfe` driver, in this particular +example connected to an imx219 sensor, is the following one: + +.. _rp1-cfe-topology: + +.. kernel-figure:: raspberrypi-rp1-cfe.dot + :alt: Diagram of an example media pipeline topology + :align: center + +The media graph contains the following video device nodes: + +- rp1-cfe-csi2-ch0: capture device for the first CSI-2 stream +- rp1-cfe-csi2-ch1: capture device for the second CSI-2 stream +- rp1-cfe-csi2-ch2: capture device for the third CSI-2 stream +- rp1-cfe-csi2-ch3: capture device for the fourth CSI-2 stream +- rp1-cfe-fe-image0: capture device for the first FE output +- rp1-cfe-fe-image1: capture device for the second FE output +- rp1-cfe-fe-stats: capture device for the FE statistics +- rp1-cfe-fe-config: output device for FE configuration + +rp1-cfe-csi2-chX +---------------- + +The rp1-cfe-csi2-chX capture devices are normal V4L2 capture devices which +can be used to capture video frames or metadata received from the CSI-2. + +rp1-cfe-fe-image0, rp1-cfe-fe-image1 +------------------------------------ + +The rp1-cfe-fe-image0 and rp1-cfe-fe-image1 capture devices are used to write +the processed frames to memory. + +rp1-cfe-fe-stats +---------------- + +The format of the FE statistics buffer is defined by +:c:type:`pisp_statistics` C structure and the meaning of each parameter is +described in the `PiSP specification` document. + +rp1-cfe-fe-config +----------------- + +The format of the FE configuration buffer is defined by +:c:type:`pisp_fe_config` C structure and the meaning of each parameter is +described in the `PiSP specification` document. diff --git a/Documentation/admin-guide/media/v4l-drivers.rst b/Documentation/admin-guide/media/v4l-drivers.rst index 53e075cbdf7061..9a4f9ac0e6c214 100644 --- a/Documentation/admin-guide/media/v4l-drivers.rst +++ b/Documentation/admin-guide/media/v4l-drivers.rst @@ -24,6 +24,7 @@ Video4Linux (V4L) driver-specific documentation raspberrypi-pisp-be rcar-fdp1 rkisp1 + raspberrypi-rp1-cfe saa7134 si470x si4713 diff --git a/Documentation/devicetree/bindings/media/raspberrypi,rp1-cfe.yaml b/Documentation/devicetree/bindings/media/raspberrypi,rp1-cfe.yaml new file mode 100644 index 00000000000000..eba5394719b906 --- /dev/null +++ b/Documentation/devicetree/bindings/media/raspberrypi,rp1-cfe.yaml @@ -0,0 +1,93 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/raspberrypi,rp1-cfe.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Raspberry Pi PiSP Camera Front End + +maintainers: + - Tomi Valkeinen + - Raspberry Pi Kernel Maintenance + +description: | + The Raspberry Pi PiSP Camera Front End is a module in Raspberrypi 5's RP1 I/O + controller, that contains: + - MIPI D-PHY + - MIPI CSI-2 receiver + - Simple image processor (called PiSP Front End, or FE) + + The FE documentation is available at: + https://datasheets.raspberrypi.com/camera/raspberry-pi-image-signal-processor-specification.pdf + + The PHY and CSI-2 receiver part have no public documentation. + +properties: + compatible: + items: + - const: raspberrypi,rp1-cfe + + reg: + items: + - description: CSI-2 registers + - description: D-PHY registers + - description: MIPI CFG (a simple top-level mux) registers + - description: FE registers + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + port: + $ref: /schemas/graph.yaml#/$defs/port-base + additionalProperties: false + description: CSI-2 RX Port + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + minItems: 1 + maxItems: 4 + + required: + - data-lanes + +required: + - compatible + - reg + - interrupts + - clocks + +additionalProperties: false + +examples: + - | + rp1 { + #address-cells = <2>; + #size-cells = <2>; + + csi@110000 { + compatible = "raspberrypi,rp1-cfe"; + reg = <0xc0 0x40110000 0x0 0x100>, + <0xc0 0x40114000 0x0 0x100>, + <0xc0 0x40120000 0x0 0x100>, + <0xc0 0x40124000 0x0 0x1000>; + + interrupts = <42>; + + clocks = <&rp1_clocks>; + + port { + csi_ep: endpoint { + remote-endpoint = <&cam_endpoint>; + data-lanes = <1 2>; + }; + }; + }; + }; diff --git a/Documentation/driver-api/media/camera-sensor.rst b/Documentation/driver-api/media/camera-sensor.rst index 93f4f2536c250d..6456145f96ed00 100644 --- a/Documentation/driver-api/media/camera-sensor.rst +++ b/Documentation/driver-api/media/camera-sensor.rst @@ -1,8 +1,14 @@ .. SPDX-License-Identifier: GPL-2.0 +.. _media_writing_camera_sensor_drivers: + Writing camera sensor drivers ============================= +This document covers the in-kernel APIs only. For the best practices on +userspace API implementation in camera sensor drivers, please see +:ref:`media_using_camera_sensor_drivers`. + CSI-2 and parallel (BT.601 and BT.656) busses --------------------------------------------- @@ -13,7 +19,7 @@ Handling clocks Camera sensors have an internal clock tree including a PLL and a number of divisors. The clock tree is generally configured by the driver based on a few -input parameters that are specific to the hardware:: the external clock frequency +input parameters that are specific to the hardware: the external clock frequency and the link frequency. The two parameters generally are obtained from system firmware. **No other frequencies should be used in any circumstances.** @@ -32,110 +38,61 @@ can rely on this frequency being used. Devicetree ~~~~~~~~~~ -The currently preferred way to achieve this is using ``assigned-clocks``, -``assigned-clock-parents`` and ``assigned-clock-rates`` properties. See -``Documentation/devicetree/bindings/clock/clock-bindings.txt`` for more -information. The driver then gets the frequency using ``clk_get_rate()``. +The preferred way to achieve this is using ``assigned-clocks``, +``assigned-clock-parents`` and ``assigned-clock-rates`` properties. See the +`clock device tree bindings +`_ +for more information. The driver then gets the frequency using +``clk_get_rate()``. This approach has the drawback that there's no guarantee that the frequency hasn't been modified directly or indirectly by another driver, or supported by the board's clock tree to begin with. Changes to the Common Clock Framework API are required to ensure reliability. -Frame size ----------- - -There are two distinct ways to configure the frame size produced by camera -sensors. - -Freely configurable camera sensor drivers -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Freely configurable camera sensor drivers expose the device's internal -processing pipeline as one or more sub-devices with different cropping and -scaling configurations. The output size of the device is the result of a series -of cropping and scaling operations from the device's pixel array's size. - -An example of such a driver is the CCS driver (see ``drivers/media/i2c/ccs``). - -Register list based drivers -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Register list based drivers generally, instead of able to configure the device -they control based on user requests, are limited to a number of preset -configurations that combine a number of different parameters that on hardware -level are independent. How a driver picks such configuration is based on the -format set on a source pad at the end of the device's internal pipeline. - -Most sensor drivers are implemented this way, see e.g. -``drivers/media/i2c/imx319.c`` for an example. - -Frame interval configuration ----------------------------- - -There are two different methods for obtaining possibilities for different frame -intervals as well as configuring the frame interval. Which one to implement -depends on the type of the device. - -Raw camera sensors -~~~~~~~~~~~~~~~~~~ - -Instead of a high level parameter such as frame interval, the frame interval is -a result of the configuration of a number of camera sensor implementation -specific parameters. Luckily, these parameters tend to be the same for more or -less all modern raw camera sensors. - -The frame interval is calculated using the following equation:: - - frame interval = (analogue crop width + horizontal blanking) * - (analogue crop height + vertical blanking) / pixel rate - -The formula is bus independent and is applicable for raw timing parameters on -large variety of devices beyond camera sensors. Devices that have no analogue -crop, use the full source image size, i.e. pixel array size. - -Horizontal and vertical blanking are specified by ``V4L2_CID_HBLANK`` and -``V4L2_CID_VBLANK``, respectively. The unit of the ``V4L2_CID_HBLANK`` control -is pixels and the unit of the ``V4L2_CID_VBLANK`` is lines. The pixel rate in -the sensor's **pixel array** is specified by ``V4L2_CID_PIXEL_RATE`` in the same -sub-device. The unit of that control is pixels per second. - -Register list based drivers need to implement read-only sub-device nodes for the -purpose. Devices that are not register list based need these to configure the -device's internal processing pipeline. - -The first entity in the linear pipeline is the pixel array. The pixel array may -be followed by other entities that are there to allow configuring binning, -skipping, scaling or digital crop :ref:`v4l2-subdev-selections`. - -USB cameras etc. devices -~~~~~~~~~~~~~~~~~~~~~~~~ - -USB video class hardware, as well as many cameras offering a similar higher -level interface natively, generally use the concept of frame interval (or frame -rate) on device level in firmware or hardware. This means lower level controls -implemented by raw cameras may not be used on uAPI (or even kAPI) to control the -frame interval on these devices. - Power management ---------------- -Always use runtime PM to manage the power states of your device. Camera sensor -drivers are in no way special in this respect: they are responsible for -controlling the power state of the device they otherwise control as well. In -general, the device must be powered on at least when its registers are being -accessed and when it is streaming. - -Existing camera sensor drivers may rely on the old -struct v4l2_subdev_core_ops->s_power() callback for bridge or ISP drivers to -manage their power state. This is however **deprecated**. If you feel you need -to begin calling an s_power from an ISP or a bridge driver, instead please add -runtime PM support to the sensor driver you are using. Likewise, new drivers -should not use s_power. - -Please see examples in e.g. ``drivers/media/i2c/ov8856.c`` and -``drivers/media/i2c/ccs/ccs-core.c``. The two drivers work in both ACPI -and DT based systems. +Camera sensors are used in conjunction with other devices to form a camera +pipeline. They must obey the rules listed herein to ensure coherent power +management over the pipeline. + +Camera sensor drivers are responsible for controlling the power state of the +device they otherwise control as well. They shall use runtime PM to manage +power states. Runtime PM shall be enabled at probe time and disabled at remove +time. Drivers should enable runtime PM autosuspend. + +The runtime PM handlers shall handle clocks, regulators, GPIOs, and other +system resources required to power the sensor up and down. For drivers that +don't use any of those resources (such as drivers that support ACPI systems +only), the runtime PM handlers may be left unimplemented. + +In general, the device shall be powered on at least when its registers are +being accessed and when it is streaming. Drivers should use +``pm_runtime_resume_and_get()`` when starting streaming and +``pm_runtime_put()`` or ``pm_runtime_put_autosuspend()`` when stopping +streaming. They may power the device up at probe time (for example to read +identification registers), but should not keep it powered unconditionally after +probe. + +At system suspend time, the whole camera pipeline must stop streaming, and +restart when the system is resumed. This requires coordination between the +camera sensor and the rest of the camera pipeline. Bridge drivers are +responsible for this coordination, and instruct camera sensors to stop and +restart streaming by calling the appropriate subdev operations +(``.s_stream()``, ``.enable_streams()`` or ``.disable_streams()``). Camera +sensor drivers shall therefore **not** keep track of the streaming state to +stop streaming in the PM suspend handler and restart it in the resume handler. +Drivers should in general not implement the system PM handlers. + +Camera sensor drivers shall **not** implement the subdev ``.s_power()`` +operation, as it is deprecated. While this operation is implemented in some +existing drivers as they predate the deprecation, new drivers shall use runtime +PM instead. If you feel you need to begin calling ``.s_power()`` from an ISP or +a bridge driver, instead add runtime PM support to the sensor driver you are +using and drop its ``.s_power()`` handler. + +Please also see :ref:`examples `. Control framework ~~~~~~~~~~~~~~~~~ @@ -155,21 +112,36 @@ access the device. Rotation, orientation and flipping ---------------------------------- -Some systems have the camera sensor mounted upside down compared to its natural -mounting rotation. In such cases, drivers shall expose the information to -userspace with the :ref:`V4L2_CID_CAMERA_SENSOR_ROTATION -` control. - -Sensor drivers shall also report the sensor's mounting orientation with the -:ref:`V4L2_CID_CAMERA_SENSOR_ORIENTATION `. - Use ``v4l2_fwnode_device_parse()`` to obtain rotation and orientation information from system firmware and ``v4l2_ctrl_new_fwnode_properties()`` to register the appropriate controls. -Sensor drivers that have any vertical or horizontal flips embedded in the -register programming sequences shall initialize the V4L2_CID_HFLIP and -V4L2_CID_VFLIP controls with the values programmed by the register sequences. -The default values of these controls shall be 0 (disabled). Especially these -controls shall not be inverted, independently of the sensor's mounting -rotation. +.. _media-camera-sensor-examples: + +Example drivers +--------------- + +Features implemented by sensor drivers vary, and depending on the set of +supported features and other qualities, particular sensor drivers better serve +the purpose of an example. The following drivers are known to be good examples: + +.. flat-table:: Example sensor drivers + :header-rows: 0 + :widths: 1 1 1 2 + + * - Driver name + - File(s) + - Driver type + - Example topic + * - CCS + - ``drivers/media/i2c/ccs/`` + - Freely configurable + - Power management (ACPI and DT), UAPI + * - imx219 + - ``drivers/media/i2c/imx219.c`` + - Register list based + - Power management (DT), UAPI, mode selection + * - imx319 + - ``drivers/media/i2c/imx319.c`` + - Register list based + - Power management (ACPI and DT) diff --git a/Documentation/driver-api/media/v4l2-subdev.rst b/Documentation/driver-api/media/v4l2-subdev.rst index e56b50b3f203ee..a62e4aff31be70 100644 --- a/Documentation/driver-api/media/v4l2-subdev.rst +++ b/Documentation/driver-api/media/v4l2-subdev.rst @@ -562,8 +562,8 @@ device configuration. This is often implemented as e.g. an array of struct v4l2_mbus_framefmt, one entry for each pad, and similarly for crop and compose rectangles. -In addition to the active configuration, each subdev file handle has an array of -struct v4l2_subdev_pad_config, managed by the V4L2 core, which contains the try +In addition to the active configuration, each subdev file handle has a struct +v4l2_subdev_state, managed by the V4L2 core, which contains the try configuration. To simplify the subdev drivers the V4L2 subdev API now optionally supports a diff --git a/Documentation/userspace-api/media/drivers/camera-sensor.rst b/Documentation/userspace-api/media/drivers/camera-sensor.rst new file mode 100644 index 00000000000000..919a50e8b9d9c5 --- /dev/null +++ b/Documentation/userspace-api/media/drivers/camera-sensor.rst @@ -0,0 +1,104 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. _media_using_camera_sensor_drivers: + +Using camera sensor drivers +=========================== + +This section describes common practices for how the V4L2 sub-device interface is +used to control the camera sensor drivers. + +You may also find :ref:`media_writing_camera_sensor_drivers` useful. + +Frame size +---------- + +There are two distinct ways to configure the frame size produced by camera +sensors. + +Freely configurable camera sensor drivers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Freely configurable camera sensor drivers expose the device's internal +processing pipeline as one or more sub-devices with different cropping and +scaling configurations. The output size of the device is the result of a series +of cropping and scaling operations from the device's pixel array's size. + +An example of such a driver is the CCS driver. + +Register list based drivers +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Register list based drivers generally, instead of able to configure the device +they control based on user requests, are limited to a number of preset +configurations that combine a number of different parameters that on hardware +level are independent. How a driver picks such configuration is based on the +format set on a source pad at the end of the device's internal pipeline. + +Most sensor drivers are implemented this way. + +Frame interval configuration +---------------------------- + +There are two different methods for obtaining possibilities for different frame +intervals as well as configuring the frame interval. Which one to implement +depends on the type of the device. + +Raw camera sensors +~~~~~~~~~~~~~~~~~~ + +Instead of a high level parameter such as frame interval, the frame interval is +a result of the configuration of a number of camera sensor implementation +specific parameters. Luckily, these parameters tend to be the same for more or +less all modern raw camera sensors. + +The frame interval is calculated using the following equation:: + + frame interval = (analogue crop width + horizontal blanking) * + (analogue crop height + vertical blanking) / pixel rate + +The formula is bus independent and is applicable for raw timing parameters on +large variety of devices beyond camera sensors. Devices that have no analogue +crop, use the full source image size, i.e. pixel array size. + +Horizontal and vertical blanking are specified by ``V4L2_CID_HBLANK`` and +``V4L2_CID_VBLANK``, respectively. The unit of the ``V4L2_CID_HBLANK`` control +is pixels and the unit of the ``V4L2_CID_VBLANK`` is lines. The pixel rate in +the sensor's **pixel array** is specified by ``V4L2_CID_PIXEL_RATE`` in the same +sub-device. The unit of that control is pixels per second. + +Register list based drivers need to implement read-only sub-device nodes for the +purpose. Devices that are not register list based need these to configure the +device's internal processing pipeline. + +The first entity in the linear pipeline is the pixel array. The pixel array may +be followed by other entities that are there to allow configuring binning, +skipping, scaling or digital crop, see :ref:`VIDIOC_SUBDEV_G_SELECTION +`. + +USB cameras etc. devices +~~~~~~~~~~~~~~~~~~~~~~~~ + +USB video class hardware, as well as many cameras offering a similar higher +level interface natively, generally use the concept of frame interval (or frame +rate) on device level in firmware or hardware. This means lower level controls +implemented by raw cameras may not be used on uAPI (or even kAPI) to control the +frame interval on these devices. + +Rotation, orientation and flipping +---------------------------------- + +Some systems have the camera sensor mounted upside down compared to its natural +mounting rotation. In such cases, drivers shall expose the information to +userspace with the :ref:`V4L2_CID_CAMERA_SENSOR_ROTATION +` control. + +Sensor drivers shall also report the sensor's mounting orientation with the +:ref:`V4L2_CID_CAMERA_SENSOR_ORIENTATION `. + +Sensor drivers that have any vertical or horizontal flips embedded in the +register programming sequences shall initialize the :ref:`V4L2_CID_HFLIP +` and :ref:`V4L2_CID_VFLIP ` controls with the +values programmed by the register sequences. The default values of these +controls shall be 0 (disabled). Especially these controls shall not be inverted, +independently of the sensor's mounting rotation. diff --git a/Documentation/userspace-api/media/drivers/ccs.rst b/Documentation/userspace-api/media/drivers/ccs.rst index 161cb65f4d98a6..02a0de9c087762 100644 --- a/Documentation/userspace-api/media/drivers/ccs.rst +++ b/Documentation/userspace-api/media/drivers/ccs.rst @@ -107,4 +107,45 @@ than in the centre. Shading correction needs to be enabled for luminance correction level to have an effect. -**Copyright** |copy| 2020 Intel Corporation +.. _media-ccs-routes: + +Routes +------ + +The CCS driver implements one or two :ref:`routes ` in +its source sub-device (scaler sub-device if it exists for the device, otherwise +binner) depending on whether the sensor supports embedded data. (All CCS +compliant sensors do but the CCS driver supports preceding standards that did +not require embedded data support, too.) + +The first route of the CCS source sub-device is for pixel data (sink pad +0/stream 0 -> source pad 1/stream 0) and the second one is for embedded data +(internal sink pad 2/stream 0 -> source pad 1/stream 1). + +Embedded data +~~~~~~~~~~~~~ + +MIPI CCS supports generation of camera sensor embedded data. The media bus code +used for this format on the internal sink pad is +:ref:`MEDIA_BUS_FMT_CCS_EMBEDDDED `. + +The bit depth of the CCS pixel data affects how the sensor will output the +embedded data, adding padding to align with CSI-2 bus :term:`Data Unit` for that +particular bit depth. This is indicated by the generic metadata format on the +source pad of the sensor's source sub-device. + +Devices supporting embedded data for bit depths greater than or equal to 16 may +support more dense packing or legacy single metadata byte per data unit, or both +of these. The supported embedded data formats can be enumerated and configured +on stream 1 of the source pad (1) of the CCS source sub-device. + +The use of the denser packing results in embedded data lines being longer than +the pixel data in data units since the data units are smaller. In bytes the +embedded data lines are still not longer than the image data lines. + +The embedded data format is determined by the sub-device image data format +configured on the source sub-device. The embedded data mbus code is only changed +when the bit depth of the image data changes in the source pad of the source +sub-device. + +**Copyright** |copy| 2020, 2024 Intel Corporation diff --git a/Documentation/userspace-api/media/drivers/index.rst b/Documentation/userspace-api/media/drivers/index.rst index 65de8ab99c58cb..e911740daec8cf 100644 --- a/Documentation/userspace-api/media/drivers/index.rst +++ b/Documentation/userspace-api/media/drivers/index.rst @@ -32,6 +32,7 @@ For more details see the file COPYING in the source distribution of Linux. :numbered: aspeed-video + camera-sensor ccs cx2341x-uapi dw100 diff --git a/Documentation/userspace-api/media/glossary.rst b/Documentation/userspace-api/media/glossary.rst index 96a360edbf3b1f..f4e95246fa127e 100644 --- a/Documentation/userspace-api/media/glossary.rst +++ b/Documentation/userspace-api/media/glossary.rst @@ -25,6 +25,13 @@ Glossary See :ref:`cec`. + Data Unit + + Unit of data transported by a bus. On parallel buses, the data unit + consists of one or more related samples while on serial buses the data + unit is logical. If the data unit is image data, it may also be called a + pixel. + Device Driver Part of the Linux Kernel that implements support for a hardware component. diff --git a/Documentation/userspace-api/media/mediactl/media-types.rst b/Documentation/userspace-api/media/mediactl/media-types.rst index 6332e8395263b0..200c37a1da2685 100644 --- a/Documentation/userspace-api/media/mediactl/media-types.rst +++ b/Documentation/userspace-api/media/mediactl/media-types.rst @@ -361,6 +361,7 @@ Types and flags used to represent the media graph elements .. _MEDIA-PAD-FL-SINK: .. _MEDIA-PAD-FL-SOURCE: .. _MEDIA-PAD-FL-MUST-CONNECT: +.. _MEDIA-PAD-FL-INTERNAL: .. flat-table:: Media pad flags :header-rows: 0 @@ -381,6 +382,14 @@ Types and flags used to represent the media graph elements enabled links even when this flag isn't set; the absence of the flag doesn't imply there is none. + * - ``MEDIA_PAD_FL_INTERNAL`` + - The internal flag indicates an internal pad that has no external + connections. As they are internal to entities, internal pads shall not + be connected with links. + + The internal flag may currently be present only in a sink pad where it + indicates that the :ref:``stream `` originates + from within the entity. One and only one of ``MEDIA_PAD_FL_SINK`` and ``MEDIA_PAD_FL_SOURCE`` must be set for every pad. diff --git a/Documentation/userspace-api/media/v4l/control.rst b/Documentation/userspace-api/media/v4l/control.rst index 4463fce694b08d..57893814a1e5d9 100644 --- a/Documentation/userspace-api/media/v4l/control.rst +++ b/Documentation/userspace-api/media/v4l/control.rst @@ -143,9 +143,13 @@ Control IDs recognise the difference between digital and analogue gain use controls ``V4L2_CID_DIGITAL_GAIN`` and ``V4L2_CID_ANALOGUE_GAIN``. +.. _v4l2-cid-hflip: + ``V4L2_CID_HFLIP`` ``(boolean)`` Mirror the picture horizontally. +.. _v4l2-cid-vflip: + ``V4L2_CID_VFLIP`` ``(boolean)`` Mirror the picture vertically. diff --git a/Documentation/userspace-api/media/v4l/dev-meta.rst b/Documentation/userspace-api/media/v4l/dev-meta.rst index 0e7e1ee1471a44..5eee9ab60395e8 100644 --- a/Documentation/userspace-api/media/v4l/dev-meta.rst +++ b/Documentation/userspace-api/media/v4l/dev-meta.rst @@ -47,6 +47,12 @@ member of the ``fmt`` union as needed per the desired operation. Both drivers and applications must set the remainder of the :c:type:`v4l2_format` structure to 0. +Devices that capture metadata by line have the struct v4l2_fmtdesc +``V4L2_FMT_FLAG_META_LINE_BASED`` flag set for :c:func:`VIDIOC_ENUM_FMT`. Such +devices can typically also :ref:`capture image data `. This primarily +involves devices that receive the data from a different devices such as a camera +sensor. + .. c:type:: v4l2_meta_format .. tabularcolumns:: |p{1.4cm}|p{2.4cm}|p{13.5cm}| @@ -65,3 +71,18 @@ to 0. - ``buffersize`` - Maximum buffer size in bytes required for data. The value is set by the driver. + * - __u32 + - ``width`` + - Width of a line of metadata in Data Units. Valid when + :c:type`v4l2_fmtdesc` flag ``V4L2_FMT_FLAG_META_LINE_BASED`` is set, + otherwise zero. See :c:func:`VIDIOC_ENUM_FMT`. + * - __u32 + - ``height`` + - Number of rows of metadata. Valid when :c:type`v4l2_fmtdesc` flag + ``V4L2_FMT_FLAG_META_LINE_BASED`` is set, otherwise zero. See + :c:func:`VIDIOC_ENUM_FMT`. + * - __u32 + - ``bytesperline`` + - Offset in bytes between the beginning of two consecutive lines. Valid + when :c:type`v4l2_fmtdesc` flag ``V4L2_FMT_FLAG_META_LINE_BASED`` is + set, otherwise zero. See :c:func:`VIDIOC_ENUM_FMT`. diff --git a/Documentation/userspace-api/media/v4l/dev-subdev.rst b/Documentation/userspace-api/media/v4l/dev-subdev.rst index a4f1df7093e8e7..5c63c8c24108e2 100644 --- a/Documentation/userspace-api/media/v4l/dev-subdev.rst +++ b/Documentation/userspace-api/media/v4l/dev-subdev.rst @@ -506,6 +506,8 @@ source pads. subdev-formats +.. _subdev-routing: + Streams, multiplexed media pads and internal routing ---------------------------------------------------- @@ -527,9 +529,9 @@ the its sink pad and allows to route them individually to one of its source pads. Subdevice drivers that support multiplexed streams are compatible with -non-multiplexed subdev drivers, but, of course, require a routing configuration -where the link between those two types of drivers contains only a single -stream. +non-multiplexed subdev drivers. However, if the driver at the sink end of a link +does not support streams, then only stream 0 of source end may be captured. +There may be additional limitations specific to the sink device. Understanding streams ^^^^^^^^^^^^^^^^^^^^^ @@ -551,6 +553,29 @@ A stream at a specific point in the media pipeline is identified by the sub-device and a (pad, stream) pair. For sub-devices that do not support multiplexed streams the 'stream' field is always 0. +.. _v4l2-subdev-internal-source-pads: + +Internal sink pads and routing +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Cases where a single sub-device source pad is traversed by multiple streams, one +or more of which originate from within the sub-device itself, are special as +there is no external sink pad for such routes. In those cases, the sources of +the internally generated streams are represented by internal sink pads, which +are sink pads that have the :ref:`MEDIA_PAD_FL_INTERNAL ` +pad flag set. + +Internal pads have all the properties of an external pad, including formats and +selections. The format in this case is the source format of the stream. An +internal pad always has a single stream only (0). + +Routes from an internal sink pad to an external source pad are typically not +modifiable but they can be activated and deactivated using the +:ref:`V4L2_SUBDEV_ROUTE_FL_ACTIVE ` flag, depending +on driver capabilities. The :ref:`V4L2_SUBDEV_ROUTE_FL_IMMUTABLE +` flag indicates that the +``V4L2_SUBDEV_ROUTE_FLAG_ACTIVE`` of the route may not be unset. + Interaction between routes, streams, formats and selections ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -570,6 +595,30 @@ Any configurations of a stream within a pad, such as format or selections, are independent of similar configurations on other streams. This is subject to change in the future. +Device types and routing setup +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Different kinds of sub-devices have differing behaviour for route activation, +depending on the hardware. In all cases, however, only routes that have the +``V4L2_SUBDEV_STREAM_FL_ACTIVE`` flag set are active. + +Devices generating the streams may allow enabling and disabling some of the +routes or the configuration is fixed. If the routes can be disabled, not +declaring the routes (or declaring them without +``VIDIOC_SUBDEV_STREAM_FL_ACTIVE`` flag set) in ``VIDIOC_SUBDEV_S_ROUTING`` will +disable the routes while the sub-device driver retains the streams and their +format and selection configuration. The ``VIDIOC_SUBDEV_S_ROUTING`` will still +return such routes back to the user in the routes array, with the +``V4L2_SUBDEV_STREAM_FL_ACTIVE`` flag unset. + +Devices transporting the streams almost always have more configurability with +respect to routing. Typically any route between the sub-device's sink and source +pads is possible, and multiple routes (usually up to certain limited number) may +be active simultaneously. For such devices, no routes are created by the driver +and user-created routes are fully replaced when ``VIDIOC_SUBDEV_S_ROUTING`` is +called on the sub-device. Such newly created routes have the device's default +configuration for format and selection rectangles. + Configuring streams ^^^^^^^^^^^^^^^^^^^ @@ -579,20 +628,19 @@ is started. There are three steps in configuring the streams: -1) Set up links. Connect the pads between sub-devices using the :ref:`Media -Controller API ` +1. Set up links. Connect the pads between sub-devices using the + :ref:`Media Controller API ` -2) Streams. Streams are declared and their routing is configured by -setting the routing table for the sub-device using -:ref:`VIDIOC_SUBDEV_S_ROUTING ` ioctl. Note that -setting the routing table will reset formats and selections in the -sub-device to default values. +2. Streams. Streams are declared and their routing is configured by setting the + routing table for the sub-device using :ref:`VIDIOC_SUBDEV_S_ROUTING + ` ioctl. Note that setting the routing table will + reset formats and selections in the sub-device to default values. -3) Configure formats and selections. Formats and selections of each stream -are configured separately as documented for plain sub-devices in -:ref:`format-propagation`. The stream ID is set to the same stream ID -associated with either sink or source pads of routes configured using the -:ref:`VIDIOC_SUBDEV_S_ROUTING ` ioctl. +3. Configure formats and selections. Formats and selections of each stream are + configured separately as documented for plain sub-devices in + :ref:`format-propagation`. The stream ID is set to the same stream ID + associated with either sink or source pads of routes configured using the + :ref:`VIDIOC_SUBDEV_S_ROUTING ` ioctl. Multiplexed streams setup example ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -618,11 +666,11 @@ modeled as V4L2 devices, exposed to userspace via /dev/videoX nodes. To configure this pipeline, the userspace must take the following steps: -1) Set up media links between entities: connect the sensors to the bridge, -bridge to the receiver, and the receiver to the DMA engines. This step does -not differ from normal non-multiplexed media controller setup. +1. Set up media links between entities: connect the sensors to the bridge, + bridge to the receiver, and the receiver to the DMA engines. This step does + not differ from normal non-multiplexed media controller setup. -2) Configure routing +2. Configure routing .. flat-table:: Bridge routing table :header-rows: 1 @@ -656,14 +704,138 @@ not differ from normal non-multiplexed media controller setup. - V4L2_SUBDEV_ROUTE_FL_ACTIVE - Pixel data stream from Sensor B -3) Configure formats and selections +3. Configure formats and selections + + After configuring routing, the next step is configuring the formats and + selections for the streams. This is similar to performing this step without + streams, with just one exception: the ``stream`` field needs to be assigned + to the value of the stream ID. + + A common way to accomplish this is to start from the sensors and propagate + the configurations along the stream towards the receiver, using + :ref:`VIDIOC_SUBDEV_S_FMT ` ioctls to configure each + stream endpoint in each sub-device. + +Internal pads setup example +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A simple example of a multiplexed stream setup might be as follows: + +- An IMX219 camera sensor source sub-device, with one source pad (0), one + internal sink pad (1) as the source of the image data and an internal sink + pad (2) as the source of the embedded data. There are two routes, one from the + internal sink pad 1 to the source 0 (image data) and another from the internal + sink pad 2 to the source pad 0 (embedded data). Both streams are always + active, i.e. there is no need to separately enable the embedded data + stream. The sensor uses the CSI-2 interface. + +- A CSI-2 receiver in the SoC. The receiver has a single sink pad (pad 0), + connected to the sensor, and two source pads (pads 1 and 2), to the DMA + engine. The receiver demultiplexes the incoming streams to the source pads. + +- DMA engines in the SoC, one for each stream. Each DMA engine is connected to a + single source pad of the receiver. + +The sensor and the receiver are modelled as V4L2 sub-devices, exposed to +userspace via /dev/v4l-subdevX device nodes. The DMA engines are modelled as +V4L2 devices, exposed to userspace via /dev/videoX nodes. + +To configure this pipeline, the userspace must take the following steps: + +1) Set up media links between entities: enable the links from the sensor to the + receiver and from the receiver to the DMA engines. This step does not differ + from normal non-multiplexed media controller setup. + +2) Configure routing + +.. flat-table:: Camera sensor. There are no configurable routes. + :header-rows: 1 + + * - Sink Pad/Stream + - Source Pad/Stream + - Routing Flags + - Comments + * - 1/0 + - 0/0 + - V4L2_SUBDEV_ROUTE_FL_ACTIVE + - Pixel data stream from the sink pad + * - 2/0 + - 0/1 + - V4L2_SUBDEV_ROUTE_FL_ACTIVE + - Metadata stream from the internal sink pad + +.. flat-table:: Receiver routing table. Typically both routes need to be + explicitly set. + :header-rows: 1 + + * - Sink Pad/Stream + - Source Pad/Stream + - Routing Flags + - Comments + * - 0/0 + - 1/0 + - V4L2_SUBDEV_ROUTE_FL_ACTIVE + - Pixel data stream from camera sensor + * - 0/1 + - 2/0 + - V4L2_SUBDEV_ROUTE_FL_ACTIVE + - Metadata stream from camera sensor -After configuring routing, the next step is configuring the formats and -selections for the streams. This is similar to performing this step without -streams, with just one exception: the ``stream`` field needs to be assigned -to the value of the stream ID. +The options available in sensor's routing configuration are dictated by hardware +capabilities: typically camera sensors always produce an image data stream while +it may be possible to enable and disable the embedded data stream. -A common way to accomplish this is to start from the sensors and propagate the -configurations along the stream towards the receiver, -using :ref:`VIDIOC_SUBDEV_S_FMT ` ioctls to configure each -stream endpoint in each sub-device. +3) Configure formats and selections + + This example assumes that the formats are propagated from sink pad to the + source pad as-is. The tables contain fields of both struct v4l2_subdev_format + and struct v4l2_mbus_framefmt. + +.. flat-table:: Formats set on the sub-devices. Bold values are set, others are + static or propagated. The order is aligned with configured + routes. + :header-rows: 1 + :fill-cells: + + * - Sub-device + - Pad/Stream + - Width + - Height + - Code + * - :rspan:`3` IMX219 + - 1/0 + - 3296 + - 2480 + - MEDIA_BUS_FMT_SRGGB10 + * - 0/0 + - **3296** + - **2480** + - **MEDIA_BUS_FMT_SRGGB10** + * - 2/0 + - 3296 + - 2 + - MEDIA_BUS_FMT_IMX219_EMBEDDED + * - 1/1 + - 3296 + - 2 + - MEDIA_BUS_FMT_META_10 + * - :rspan:`3` CSI-2 receiver + - 0/0 + - **3296** + - **2480** + - **MEDIA_BUS_FMT_SRGGB10** + * - 1/0 + - 3296 + - 2480 + - MEDIA_BUS_FMT_SRGGB10 + * - 0/1 + - **3296** + - **2** + - **MEDIA_BUS_FMT_META_10** + * - 2/0 + - 3296 + - 2 + - MEDIA_BUS_FMT_META_10 + +The embedded data format does not need to be configured as the format is +dictated by the pixel data format in this case. diff --git a/Documentation/userspace-api/media/v4l/meta-formats.rst b/Documentation/userspace-api/media/v4l/meta-formats.rst index 178653874e44b3..3e976816dcafeb 100644 --- a/Documentation/userspace-api/media/v4l/meta-formats.rst +++ b/Documentation/userspace-api/media/v4l/meta-formats.rst @@ -14,11 +14,13 @@ These formats are used for the :ref:`metadata` interface only. metafmt-bcm2835-isp-stats metafmt-d4xx + metafmt-generic metafmt-intel-ipu3 metafmt-pisp-be + metafmt-pisp-fe metafmt-rkisp1 metafmt-sensor-data metafmt-uvc + metafmt-vivid metafmt-vsp1-hgo metafmt-vsp1-hgt - metafmt-vivid diff --git a/Documentation/userspace-api/media/v4l/metafmt-generic.rst b/Documentation/userspace-api/media/v4l/metafmt-generic.rst new file mode 100644 index 00000000000000..78ab56b21682d2 --- /dev/null +++ b/Documentation/userspace-api/media/v4l/metafmt-generic.rst @@ -0,0 +1,340 @@ +.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later + +******************************************************************************************************************************************************************************************************************************************************************************** +V4L2_META_FMT_GENERIC_8 ('MET8'), V4L2_META_FMT_GENERIC_CSI2_10 ('MC1A'), V4L2_META_FMT_GENERIC_CSI2_12 ('MC1C'), V4L2_META_FMT_GENERIC_CSI2_14 ('MC1E'), V4L2_META_FMT_GENERIC_CSI2_16 ('MC1G'), V4L2_META_FMT_GENERIC_CSI2_20 ('MC1K'), V4L2_META_FMT_GENERIC_CSI2_24 ('MC1O') +******************************************************************************************************************************************************************************************************************************************************************************** + + +Generic line-based metadata formats + + +Description +=========== + +These generic line-based metadata formats define the memory layout of the data +without defining the format or meaning of the metadata itself. + +.. _v4l2-meta-fmt-generic-8: + +V4L2_META_FMT_GENERIC_8 +----------------------- + +The V4L2_META_FMT_GENERIC_8 format is a plain 8-bit metadata format. This format +is used on CSI-2 for 8 bits per :term:`Data Unit`. + +Additionally it is used for 16 bits per Data Unit when two bytes of metadata are +packed into one 16-bit Data Unit. Otherwise the 16 bits per pixel dataformat is +:ref:`V4L2_META_FMT_GENERIC_CSI2_16 `. + +**Byte Order Of V4L2_META_FMT_GENERIC_8.** +Each cell is one byte. "M" denotes a byte of metadata. + +.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}| + +.. flat-table:: Sample 4x2 Metadata Frame + :header-rows: 0 + :stub-columns: 0 + :widths: 12 8 8 8 8 + + * - start + 0: + - M\ :sub:`00` + - M\ :sub:`10` + - M\ :sub:`20` + - M\ :sub:`30` + * - start + 4: + - M\ :sub:`01` + - M\ :sub:`11` + - M\ :sub:`21` + - M\ :sub:`31` + +.. _v4l2-meta-fmt-generic-csi2-10: + +V4L2_META_FMT_GENERIC_CSI2_10 +----------------------------- + +V4L2_META_FMT_GENERIC_CSI2_10 contains 8-bit generic metadata packed in 10-bit +Data Units, with one padding byte after every four bytes of metadata. This +format is typically used by CSI-2 receivers with a source that transmits +MEDIA_BUS_FMT_META_10 and the CSI-2 receiver writes the received data to memory +as-is. + +The packing of the data follows the MIPI CSI-2 specification and the padding of +the data is defined in the MIPI CCS specification. + +This format is also used in conjunction with 20 bits per :term:`Data Unit` +formats that pack two bytes of metadata into one Data Unit. Otherwise the +20 bits per pixel dataformat is :ref:`V4L2_META_FMT_GENERIC_CSI2_20 +`. + +This format is little endian. + +**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_10.** +Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding. + +.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{.8cm}| + +.. flat-table:: Sample 4x2 Metadata Frame + :header-rows: 0 + :stub-columns: 0 + :widths: 12 8 8 8 8 8 + + * - start + 0: + - M\ :sub:`00` + - M\ :sub:`10` + - M\ :sub:`20` + - M\ :sub:`30` + - x + * - start + 5: + - M\ :sub:`01` + - M\ :sub:`11` + - M\ :sub:`21` + - M\ :sub:`31` + - x + +.. _v4l2-meta-fmt-generic-csi2-12: + +V4L2_META_FMT_GENERIC_CSI2_12 +----------------------------- + +V4L2_META_FMT_GENERIC_CSI2_12 contains 8-bit generic metadata packed in 12-bit +Data Units, with one padding byte after every two bytes of metadata. This format +is typically used by CSI-2 receivers with a source that transmits +MEDIA_BUS_FMT_META_12 and the CSI-2 receiver writes the received data to memory +as-is. + +The packing of the data follows the MIPI CSI-2 specification and the padding of +the data is defined in the MIPI CCS specification. + +This format is also used in conjunction with 24 bits per :term:`Data Unit` +formats that pack two bytes of metadata into one Data Unit. Otherwise the +24 bits per pixel dataformat is :ref:`V4L2_META_FMT_GENERIC_CSI2_24 +`. + +This format is little endian. + +**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_12.** +Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding. + +.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{.8cm}|p{.8cm}| + +.. flat-table:: Sample 4x2 Metadata Frame + :header-rows: 0 + :stub-columns: 0 + :widths: 12 8 8 8 8 8 8 + + * - start + 0: + - M\ :sub:`00` + - M\ :sub:`10` + - x + - M\ :sub:`20` + - M\ :sub:`30` + - x + * - start + 6: + - M\ :sub:`01` + - M\ :sub:`11` + - x + - M\ :sub:`21` + - M\ :sub:`31` + - x + +.. _v4l2-meta-fmt-generic-csi2-14: + +V4L2_META_FMT_GENERIC_CSI2_14 +----------------------------- + +V4L2_META_FMT_GENERIC_CSI2_14 contains 8-bit generic metadata packed in 14-bit +Data Units, with three padding bytes after every four bytes of metadata. This +format is typically used by CSI-2 receivers with a source that transmits +MEDIA_BUS_FMT_META_14 and the CSI-2 receiver writes the received data to memory +as-is. + +The packing of the data follows the MIPI CSI-2 specification and the padding of +the data is defined in the MIPI CCS specification. + +This format is little endian. + +**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_14.** +Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding. + +.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{.8cm}| + +.. flat-table:: Sample 4x2 Metadata Frame + :header-rows: 0 + :stub-columns: 0 + :widths: 12 8 8 8 8 8 8 8 + + * - start + 0: + - M\ :sub:`00` + - M\ :sub:`10` + - M\ :sub:`20` + - M\ :sub:`30` + - x + - x + - x + * - start + 7: + - M\ :sub:`01` + - M\ :sub:`11` + - M\ :sub:`21` + - M\ :sub:`31` + - x + - x + - x + +.. _v4l2-meta-fmt-generic-csi2-16: + +V4L2_META_FMT_GENERIC_CSI2_16 +----------------------------- + +V4L2_META_FMT_GENERIC_CSI2_16 contains 8-bit generic metadata packed in 16-bit +Data Units, with one padding byte after every byte of metadata. This format is +typically used by CSI-2 receivers with a source that transmits +MEDIA_BUS_FMT_META_16 and the CSI-2 receiver writes the received data to memory +as-is. + +The packing of the data follows the MIPI CSI-2 specification and the padding of +the data is defined in the MIPI CCS specification. + +Some devices support more efficient packing of metadata in conjunction with +16-bit image data. In that case the dataformat is +:ref:`V4L2_META_FMT_GENERIC_8 `. + +This format is little endian. + +**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_16.** +Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding. + +.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}| + +.. flat-table:: Sample 4x2 Metadata Frame + :header-rows: 0 + :stub-columns: 0 + :widths: 12 8 8 8 8 8 8 8 8 + + * - start + 0: + - M\ :sub:`00` + - x + - M\ :sub:`10` + - x + - M\ :sub:`20` + - x + - M\ :sub:`30` + - x + * - start + 8: + - M\ :sub:`01` + - x + - M\ :sub:`11` + - x + - M\ :sub:`21` + - x + - M\ :sub:`31` + - x + +.. _v4l2-meta-fmt-generic-csi2-20: + +V4L2_META_FMT_GENERIC_CSI2_20 +----------------------------- + +V4L2_META_FMT_GENERIC_CSI2_20 contains 8-bit generic metadata packed in 20-bit +Data Units, with alternating one or two padding bytes after every byte of +metadata. This format is typically used by CSI-2 receivers with a source that +transmits MEDIA_BUS_FMT_META_20 and the CSI-2 receiver writes the received data +to memory as-is. + +The packing of the data follows the MIPI CSI-2 specification and the padding of +the data is defined in the MIPI CCS specification. + +Some devices support more efficient packing of metadata in conjunction with +16-bit image data. In that case the dataformat is +:ref:`V4L2_META_FMT_GENERIC_CSI2_10 `. + +This format is little endian. + +**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_20.** +Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding. + +.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}| + +.. flat-table:: Sample 4x2 Metadata Frame + :header-rows: 0 + :stub-columns: 0 + :widths: 12 8 8 8 8 8 8 8 8 8 8 + + * - start + 0: + - M\ :sub:`00` + - x + - M\ :sub:`10` + - x + - x + - M\ :sub:`20` + - x + - M\ :sub:`30` + - x + - x + * - start + 10: + - M\ :sub:`01` + - x + - M\ :sub:`11` + - x + - x + - M\ :sub:`21` + - x + - M\ :sub:`31` + - x + - x + +.. _v4l2-meta-fmt-generic-csi2-24: + +V4L2_META_FMT_GENERIC_CSI2_24 +----------------------------- + +V4L2_META_FMT_GENERIC_CSI2_24 contains 8-bit generic metadata packed in 24-bit +Data Units, with two padding bytes after every byte of metadata. This format is +typically used by CSI-2 receivers with a source that transmits +MEDIA_BUS_FMT_META_24 and the CSI-2 receiver writes the received data to memory +as-is. + +The packing of the data follows the MIPI CSI-2 specification and the padding of +the data is defined in the MIPI CCS specification. + +Some devices support more efficient packing of metadata in conjunction with +16-bit image data. In that case the dataformat is +:ref:`V4L2_META_FMT_GENERIC_CSI2_12 `. + +This format is little endian. + +**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_24.** +Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding. + +.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}| + +.. flat-table:: Sample 4x2 Metadata Frame + :header-rows: 0 + :stub-columns: 0 + :widths: 12 8 8 8 8 8 8 8 8 8 8 8 8 + + * - start + 0: + - M\ :sub:`00` + - x + - x + - M\ :sub:`10` + - x + - x + - M\ :sub:`20` + - x + - x + - M\ :sub:`30` + - x + - x + * - start + 12: + - M\ :sub:`01` + - x + - x + - M\ :sub:`11` + - x + - x + - M\ :sub:`21` + - x + - x + - M\ :sub:`31` + - x + - x diff --git a/Documentation/userspace-api/media/v4l/metafmt-pisp-fe.rst b/Documentation/userspace-api/media/v4l/metafmt-pisp-fe.rst new file mode 100644 index 00000000000000..fddeada83e4afb --- /dev/null +++ b/Documentation/userspace-api/media/v4l/metafmt-pisp-fe.rst @@ -0,0 +1,39 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. _v4l2-meta-fmt-rpi-fe-cfg: + +************************ +V4L2_META_FMT_RPI_FE_CFG +************************ + +Raspberry Pi PiSP Front End configuration format +================================================ + +The Raspberry Pi PiSP Front End image signal processor is configured by +userspace by providing a buffer of configuration parameters to the +`rp1-cfe-fe-config` output video device node using the +:c:type:`v4l2_meta_format` interface. + +The `Raspberry Pi PiSP technical specification +`_ +provide detailed description of the Front End configuration and programming +model. + +.. _v4l2-meta-fmt-rpi-fe-stats: + +************************** +V4L2_META_FMT_RPI_FE_STATS +************************** + +Raspberry Pi PiSP Front End statistics format +============================================= + +The Raspberry Pi PiSP Front End image signal processor provides statistics data +by writing to a buffer provided via the `rp1-cfe-fe-stats` capture video device +node using the +:c:type:`v4l2_meta_format` interface. + +The `Raspberry Pi PiSP technical specification +`_ +provide detailed description of the Front End configuration and programming +model. diff --git a/Documentation/userspace-api/media/v4l/subdev-formats.rst b/Documentation/userspace-api/media/v4l/subdev-formats.rst index d7250b6a169548..23c7aeaaf9711d 100644 --- a/Documentation/userspace-api/media/v4l/subdev-formats.rst +++ b/Documentation/userspace-api/media/v4l/subdev-formats.rst @@ -33,7 +33,7 @@ Media Bus Formats * - __u32 - ``field`` - Field order, from enum :c:type:`v4l2_field`. See - :ref:`field-order` for details. + :ref:`field-order` for details. Zero for metadata mbus codes. * - __u32 - ``colorspace`` - Image colorspace, from enum :c:type:`v4l2_colorspace`. @@ -45,7 +45,7 @@ Media Bus Formats conversion is supported by setting the flag V4L2_SUBDEV_MBUS_CODE_CSC_COLORSPACE in the corresponding struct :c:type:`v4l2_subdev_mbus_code_enum` during enumeration. - See :ref:`v4l2-subdev-mbus-code-flags`. + See :ref:`v4l2-subdev-mbus-code-flags`. Zero for metadata mbus codes. * - union { - (anonymous) * - __u16 @@ -61,7 +61,7 @@ Media Bus Formats that ycbcr_enc conversion is supported by setting the flag V4L2_SUBDEV_MBUS_CODE_CSC_YCBCR_ENC in the corresponding struct :c:type:`v4l2_subdev_mbus_code_enum` during enumeration. - See :ref:`v4l2-subdev-mbus-code-flags`. + See :ref:`v4l2-subdev-mbus-code-flags`. Zero for metadata mbus codes. * - __u16 - ``hsv_enc`` - HSV encoding, from enum :c:type:`v4l2_hsv_encoding`. @@ -75,7 +75,7 @@ Media Bus Formats that hsv_enc conversion is supported by setting the flag V4L2_SUBDEV_MBUS_CODE_CSC_HSV_ENC in the corresponding struct :c:type:`v4l2_subdev_mbus_code_enum` during enumeration. - See :ref:`v4l2-subdev-mbus-code-flags` + See :ref:`v4l2-subdev-mbus-code-flags`. Zero for metadata mbus codes. * - } - * - __u16 @@ -90,8 +90,8 @@ Media Bus Formats The driver indicates that quantization conversion is supported by setting the flag V4L2_SUBDEV_MBUS_CODE_CSC_QUANTIZATION in the corresponding struct :c:type:`v4l2_subdev_mbus_code_enum` - during enumeration. See :ref:`v4l2-subdev-mbus-code-flags`. - + during enumeration. See :ref:`v4l2-subdev-mbus-code-flags`. Zero for + metadata mbus codes. * - __u16 - ``xfer_func`` - Transfer function, from enum :c:type:`v4l2_xfer_func`. @@ -104,7 +104,8 @@ Media Bus Formats The driver indicates that the transfer function conversion is supported by setting the flag V4L2_SUBDEV_MBUS_CODE_CSC_XFER_FUNC in the corresponding struct :c:type:`v4l2_subdev_mbus_code_enum` - during enumeration. See :ref:`v4l2-subdev-mbus-code-flags`. + during enumeration. See :ref:`v4l2-subdev-mbus-code-flags`. Zero for + metadata mbus codes. * - __u16 - ``flags`` - flags See: :ref:v4l2-mbus-framefmt-flags @@ -8377,3 +8378,360 @@ The following table lists the existing sensor ancillary metadata formats: CSI-2/SMIA embedded data format as described in the `CSI-2 specification. `_ +Generic Serial Metadata Formats +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Generic serial metadata formats are used on serial buses where the actual data +content is more or less device specific but the data is transmitted and received +by multiple devices that do not process the data in any way, simply writing +it to system memory for processing in software at the end of the pipeline. + +The exact format of the data generated by the device is reported on the internal +source pad of the originating sub-device, using one of the more specific +metadata formats such as MEDIA_BUS_FMT_CCS_EMBEDDED. + +"b" in an array cell signifies a byte of data, followed by the number of the bit +and finally the bit number in subscript. "x" indicates a padding bit. + +.. _media-bus-format-generic-meta: + +.. cssclass: longtable + +.. flat-table:: Generic Serial Metadata Formats + :header-rows: 2 + :stub-columns: 0 + + * - Identifier + - Code + - + - :cspan:`23` Data organization within bus :term:`Data Unit` + * - + - + - Bit + - 23 + - 22 + - 21 + - 20 + - 19 + - 18 + - 17 + - 16 + - 15 + - 14 + - 13 + - 12 + - 11 + - 10 + - 9 + - 8 + - 7 + - 6 + - 5 + - 4 + - 3 + - 2 + - 1 + - 0 + * .. _MEDIA-BUS-FMT-META-8: + + - MEDIA_BUS_FMT_META_8 + - 0x8001 + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - b0\ :sub:`7` + - b0\ :sub:`6` + - b0\ :sub:`5` + - b0\ :sub:`4` + - b0\ :sub:`3` + - b0\ :sub:`2` + - b0\ :sub:`1` + - b0\ :sub:`0` + * .. _MEDIA-BUS-FMT-META-10: + + - MEDIA_BUS_FMT_META_10 + - 0x8002 + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - b0\ :sub:`7` + - b0\ :sub:`6` + - b0\ :sub:`5` + - b0\ :sub:`4` + - b0\ :sub:`3` + - b0\ :sub:`2` + - b0\ :sub:`1` + - b0\ :sub:`0` + - x + - x + * .. _MEDIA-BUS-FMT-META-12: + + - MEDIA_BUS_FMT_META_12 + - 0x8003 + - + - + - + - + - + - + - + - + - + - + - + - + - + - b0\ :sub:`7` + - b0\ :sub:`6` + - b0\ :sub:`5` + - b0\ :sub:`4` + - b0\ :sub:`3` + - b0\ :sub:`2` + - b0\ :sub:`1` + - b0\ :sub:`0` + - x + - x + - x + - x + * .. _MEDIA-BUS-FMT-META-14: + + - MEDIA_BUS_FMT_META_14 + - 0x8004 + - + - + - + - + - + - + - + - + - + - + - + - b0\ :sub:`7` + - b0\ :sub:`6` + - b0\ :sub:`5` + - b0\ :sub:`4` + - b0\ :sub:`3` + - b0\ :sub:`2` + - b0\ :sub:`1` + - b0\ :sub:`0` + - x + - x + - x + - x + - x + - x + * .. _MEDIA-BUS-FMT-META-16: + + - MEDIA_BUS_FMT_META_16 + - 0x8005 + - + - + - + - + - + - + - + - + - + - b0\ :sub:`7` + - b0\ :sub:`6` + - b0\ :sub:`5` + - b0\ :sub:`4` + - b0\ :sub:`3` + - b0\ :sub:`2` + - b0\ :sub:`1` + - b0\ :sub:`0` + - x + - x + - x + - x + - x + - x + - x + - x + * .. _MEDIA-BUS-FMT-META-20: + + - MEDIA_BUS_FMT_META_20 + - 0x8006 + - + - + - + - + - + - b0\ :sub:`7` + - b0\ :sub:`6` + - b0\ :sub:`5` + - b0\ :sub:`4` + - b0\ :sub:`3` + - b0\ :sub:`2` + - b0\ :sub:`1` + - b0\ :sub:`0` + - x + - x + - x + - x + - x + - x + - x + - x + - x + - x + - x + - x + * .. _MEDIA-BUS-FMT-META-24: + + - MEDIA_BUS_FMT_META_24 + - 0x8007 + - + - b0\ :sub:`7` + - b0\ :sub:`6` + - b0\ :sub:`5` + - b0\ :sub:`4` + - b0\ :sub:`3` + - b0\ :sub:`2` + - b0\ :sub:`1` + - b0\ :sub:`0` + - x + - x + - x + - x + - x + - x + - x + - x + - x + - x + - x + - x + - x + - x + - x + - x + +.. _MEDIA-BUS-FMT-CCS-EMBEDDED: + +MIPI CCS Embedded Data Formats +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +`MIPI CCS `_ defines a +metadata format for sensor embedded data, which is used to store the register +configuration used for capturing a given frame. The format is defined in the CCS +specification. The media bus code for this format is +``MEDIA_BUS_FMT_CCS_EMBEDDED``. + +The CCS embedded data format definition includes three levels: + +1. Padding within CSI-2 bus :ref:`Data Unit ` as + documented in the MIPI CCS specification. + +2. The tagged data format as documented in the MIPI CCS specification. + +3. Register addresses and register documentation as documented in the MIPI CCS + specification. + +The format definition shall be used only by devices that fulfill all three +levels above. + +This mbus code are only used for "2-byte simplified tagged data format" (code +0xa) but their use may be extended further in the future, to cover other CCS +embedded data format codes. + +Also see :ref:`CCS driver documentation `. + +Omnivision OV2740 Embedded Data Format +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The Omnivision OV2740 camera sensor produces the following embedded data format, +indicated by mbus code MEDIA_BUS_FMT_OV2740_EMBEDDED. The format conforms to +:ref:`CCS embedded data format ` up to level 1. + +.. flat-table:: Omnivision OV2740 Embedded Data Format. Octets at indices marked + reserved or unused have been omitted from the table. The values + are big endian byte order. + :header-rows: 1 + + * - Offset + - Size in bits (active bits if not the same as size) + - Content description + * - 4 + - 16 (10--0) + - Analogue gain + * - 6 + - 16 + - Coarse integration time + * - 10 + - 8 + - Dpc correction threshold + * - 15 + - 16 + - Output image width + * - 17 + - 16 + - Output image height + * - 23 + - 8 + - MIPI header revision number (2) + * - 31 + - 8 + - Vertical (bit 1) and horizontal flip (bit 0) + * - 32 + - 8 + - Frame duration A + * - 33 + - 8 + - Frame duration B + * - 34 + - 8 + - Context count (2) + * - 35 + - 8 + - Context select + * - 54 + - 8 + - Data pedestal bits 9--2 + * - 63 + - 8 + - Frame average bits 9--2 + * - 64 + - 16 + - Digital gain red + * - 66 + - 16 + - Digital gain greenr + * - 68 + - 16 + - Digital gain blue + * - 70 + - 16 + - Digital gain greenb + * - 89 + - 8 + - Frame counter (starts at 1, wraps to 0 after 255) diff --git a/Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst b/Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst index 000c154b0f989d..3adb3d205531b4 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst @@ -227,6 +227,13 @@ the ``mbus_code`` field is handled differently: The application can ask to configure the quantization of the capture device when calling the :ref:`VIDIOC_S_FMT ` ioctl with :ref:`V4L2_PIX_FMT_FLAG_SET_CSC ` set. + * - ``V4L2_FMT_FLAG_META_LINE_BASED`` + - 0x0200 + - The metadata format is line-based. In this case the ``width``, + ``height`` and ``bytesperline`` fields of :c:type:`v4l2_meta_format` are + valid. The buffer consists of ``height`` lines, each having ``width`` + Data Units of data and the offset (in bytes) between the beginning of + each two consecutive lines is ``bytesperline``. Return Value ============ diff --git a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst index 72677a280cd647..6599e5c5c184f1 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst @@ -43,23 +43,39 @@ The routing configuration determines the flows of data inside an entity. Drivers report their current routing tables using the ``VIDIOC_SUBDEV_G_ROUTING`` ioctl and application may enable or disable routes with the ``VIDIOC_SUBDEV_S_ROUTING`` ioctl, by adding or removing routes and -setting or clearing flags of the ``flags`` field of a -struct :c:type:`v4l2_subdev_route`. +setting or clearing flags of the ``flags`` field of a struct +:c:type:`v4l2_subdev_route`. Similarly to ``VIDIOC_SUBDEV_G_ROUTING``, also +``VIDIOC_SUBDEV_S_ROUTING`` returns the routes back to the user. -All stream configurations are reset when ``VIDIOC_SUBDEV_S_ROUTING`` is called. This -means that the userspace must reconfigure all streams after calling the ioctl -with e.g. ``VIDIOC_SUBDEV_S_FMT``. +All stream configurations are reset when ``VIDIOC_SUBDEV_S_ROUTING`` is called. +This means that the userspace must reconfigure all stream formats and selections +after calling the ioctl with e.g. ``VIDIOC_SUBDEV_S_FMT``. Only subdevices which have both sink and source pads can support routing. -When inspecting routes through ``VIDIOC_SUBDEV_G_ROUTING`` and the application -provided ``num_routes`` is not big enough to contain all the available routes -the subdevice exposes, drivers return the ENOSPC error code and adjust the -value of the ``num_routes`` field. Application should then reserve enough memory -for all the route entries and call ``VIDIOC_SUBDEV_G_ROUTING`` again. - -On a successful ``VIDIOC_SUBDEV_G_ROUTING`` call the driver updates the -``num_routes`` field to reflect the actual number of routes returned. +The ``len_routes`` field indicates the number of routes that can fit in the +``routes`` array allocated by userspace. It is set by applications for both +ioctls to indicate how many routes the kernel can return, and is never modified +by the kernel. + +The ``num_routes`` field indicates the number of routes in the routing +table. For ``VIDIOC_SUBDEV_S_ROUTING``, it is set by userspace to the number of +routes that the application stored in the ``routes`` array. For both ioctls, it +is returned by the kernel and indicates how many routes are stored in the +subdevice routing table. This may be smaller or larger than the value of +``num_routes`` set by the application for ``VIDIOC_SUBDEV_S_ROUTING``, as +drivers may adjust the requested routing table. + +The kernel can return a ``num_routes`` value larger than ``len_routes`` from +both ioctls. This indicates thare are more routes in the routing table than fits +the ``routes`` array. In this case, the ``routes`` array is filled by the kernel +with the first ``len_routes`` entries of the subdevice routing table. This is +not considered to be an error, and the ioctl call succeeds. If the applications +wants to retrieve the missing routes, it can issue a new +``VIDIOC_SUBDEV_G_ROUTING`` call with a large enough ``routes`` array. + +``VIDIOC_SUBDEV_S_ROUTING`` may return more routes than the user provided in +``num_routes`` field due to e.g. hardware properties. .. tabularcolumns:: |p{4.4cm}|p{4.4cm}|p{8.7cm}| @@ -74,6 +90,9 @@ On a successful ``VIDIOC_SUBDEV_G_ROUTING`` call the driver updates the - ``which`` - Format to modified, from enum :ref:`v4l2_subdev_format_whence `. + * - __u32 + - ``len_routes`` + - The length of the array (as in memory reserved for the array) * - struct :c:type:`v4l2_subdev_route` - ``routes[]`` - Array of struct :c:type:`v4l2_subdev_route` entries @@ -81,7 +100,7 @@ On a successful ``VIDIOC_SUBDEV_G_ROUTING`` call the driver updates the - ``num_routes`` - Number of entries of the routes array * - __u32 - - ``reserved``\ [5] + - ``reserved``\ [11] - Reserved for future extensions. Applications and drivers must set the array to zero. @@ -127,6 +146,11 @@ On a successful ``VIDIOC_SUBDEV_G_ROUTING`` call the driver updates the * - V4L2_SUBDEV_ROUTE_FL_ACTIVE - 0x0001 - The route is enabled. Set by applications. + * - V4L2_SUBDEV_ROUTE_FL_IMMUTABLE + - 0x0002 + - The route is immutable. Set by the driver. Indicates that the + ``V4L2_SUBDEV_ROUTE_FL_ACTIVE`` flag of an immutable route may not be + unset. Return Value ============ @@ -135,10 +159,6 @@ On success 0 is returned, on error -1 and the ``errno`` variable is set appropriately. The generic error codes are described at the :ref:`Generic Error Codes ` chapter. -ENOSPC - The application provided ``num_routes`` is not big enough to contain - all the available routes the subdevice exposes. - EINVAL The sink or source pad identifiers reference a non-existing pad, or reference pads of different types (ie. the sink_pad identifiers refers to a source pad). diff --git a/Documentation/userspace-api/media/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions index 3e58aac4ef0b6e..bdc628e8c1d694 100644 --- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions +++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions @@ -215,6 +215,7 @@ replace define V4L2_FMT_FLAG_CSC_XFER_FUNC fmtdesc-flags replace define V4L2_FMT_FLAG_CSC_YCBCR_ENC fmtdesc-flags replace define V4L2_FMT_FLAG_CSC_HSV_ENC fmtdesc-flags replace define V4L2_FMT_FLAG_CSC_QUANTIZATION fmtdesc-flags +replace define V4L2_FMT_FLAG_META_LINE_BASED fmtdesc-flags # V4L2 timecode types replace define V4L2_TC_TYPE_24FPS timecode-type diff --git a/MAINTAINERS b/MAINTAINERS index 6a6e2941c4976a..ac1416a5dccb01 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3943,7 +3943,7 @@ BROADCOM BCM2835 CAMERA DRIVER M: Raspberry Pi Kernel Maintenance L: linux-media@vger.kernel.org S: Maintained -F: drivers/media/platform/bcm2835/ +F: drivers/media/platform/broadcom/bcm2835* F: Documentation/devicetree/bindings/media/brcm,bcm2835-unicam.yaml BROADCOM BCM2835 ISP DRIVER @@ -18044,6 +18044,13 @@ S: Maintained F: Documentation/devicetree/bindings/media/raspberrypi,pispbe.yaml F: include/uapi/linux/media/raspberrypi/ +RASPBERRY PI PISP CAMERA FRONT END +M: Tomi Valkeinen +M: Raspberry Pi Kernel Maintenance +S: Maintained +F: Documentation/devicetree/bindings/media/raspberrypi,rp1-cfe.yaml +F: drivers/media/platform/raspberrypi/rp1-cfe/ + RC-CORE / LIRC FRAMEWORK M: Sean Young L: linux-media@vger.kernel.org diff --git a/arch/arm/boot/dts/broadcom/bcm270x.dtsi b/arch/arm/boot/dts/broadcom/bcm270x.dtsi index e077bd2711230c..049136ba201663 100644 --- a/arch/arm/boot/dts/broadcom/bcm270x.dtsi +++ b/arch/arm/boot/dts/broadcom/bcm270x.dtsi @@ -89,6 +89,8 @@ compatible = "brcm,bcm2835-unicam"; reg = <0x7e800000 0x800>, <0x7e802000 0x4>; + reg-names = "unicam", "cmi"; + brcm,num-data-lanes = <2>; interrupts = <2 6>; clocks = <&clocks BCM2835_CLOCK_CAM0>, <&firmware_clocks 4>; @@ -104,6 +106,8 @@ compatible = "brcm,bcm2835-unicam"; reg = <0x7e801000 0x800>, <0x7e802004 0x4>; + reg-names = "unicam", "cmi"; + brcm,num-data-lanes = <2>; interrupts = <2 7>; clocks = <&clocks BCM2835_CLOCK_CAM1>, <&firmware_clocks 4>; diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index 413021887f9615..3a62a72488200b 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -826,7 +826,7 @@ static int adv7180_get_pad_format(struct v4l2_subdev *sd, struct adv7180_state *state = to_state(sd); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - format->format = *v4l2_subdev_get_try_format(sd, sd_state, 0); + format->format = *v4l2_subdev_state_get_format(sd_state, 0); } else { adv7180_mbus_fmt(sd, &format->format); format->format.field = state->field; @@ -867,15 +867,15 @@ static int adv7180_set_pad_format(struct v4l2_subdev *sd, I2P_IDX : INTERLACED_IDX); } } else { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, 0); + framefmt = v4l2_subdev_state_get_format(sd_state, 0); *framefmt = format->format; } return ret; } -static int adv7180_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int adv7180_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY @@ -992,7 +992,6 @@ static const struct v4l2_subdev_core_ops adv7180_core_ops = { }; static const struct v4l2_subdev_pad_ops adv7180_pad_ops = { - .init_cfg = adv7180_init_cfg, .enum_mbus_code = adv7180_enum_mbus_code, .set_fmt = adv7180_set_pad_format, .get_fmt = adv7180_get_pad_format, @@ -1010,6 +1009,10 @@ static const struct v4l2_subdev_ops adv7180_ops = { .sensor = &adv7180_sensor_ops, }; +static const struct v4l2_subdev_internal_ops adv7180_internal_ops = { + .init_state = adv7180_init_state, +}; + static irqreturn_t adv7180_irq(int irq, void *devid) { struct adv7180_state *state = devid; @@ -1536,6 +1539,7 @@ static int adv7180_probe(struct i2c_client *client) state->input = 0; sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &adv7180_ops); + sd->internal_ops = &adv7180_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; ret = adv7180_init_controls(state); diff --git a/drivers/media/i2c/adv7183.c b/drivers/media/i2c/adv7183.c index 3659feafac69c8..2a2cace4a1538c 100644 --- a/drivers/media/i2c/adv7183.c +++ b/drivers/media/i2c/adv7183.c @@ -442,8 +442,6 @@ static int adv7183_set_fmt(struct v4l2_subdev *sd, } if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) decoder->fmt = *fmt; - else - sd_state->pads->try_fmt = *fmt; return 0; } diff --git a/drivers/media/i2c/adv748x/adv748x-afe.c b/drivers/media/i2c/adv748x/adv748x-afe.c index 00095c7762c24d..50d9fbadbe3822 100644 --- a/drivers/media/i2c/adv748x/adv748x-afe.c +++ b/drivers/media/i2c/adv748x/adv748x-afe.c @@ -354,8 +354,8 @@ static int adv748x_afe_get_format(struct v4l2_subdev *sd, return -EINVAL; if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY) { - mbusformat = v4l2_subdev_get_try_format(sd, sd_state, - sdformat->pad); + mbusformat = v4l2_subdev_state_get_format(sd_state, + sdformat->pad); sdformat->format = *mbusformat; } else { adv748x_afe_fill_format(afe, &sdformat->format); @@ -378,7 +378,7 @@ static int adv748x_afe_set_format(struct v4l2_subdev *sd, if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE) return adv748x_afe_get_format(sd, sd_state, sdformat); - mbusformat = v4l2_subdev_get_try_format(sd, sd_state, sdformat->pad); + mbusformat = v4l2_subdev_state_get_format(sd_state, sdformat->pad); *mbusformat = sdformat->format; return 0; diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c index a5a7cb228896b0..5b265b722394a2 100644 --- a/drivers/media/i2c/adv748x/adv748x-csi2.c +++ b/drivers/media/i2c/adv748x/adv748x-csi2.c @@ -147,7 +147,7 @@ adv748x_csi2_get_pad_format(struct v4l2_subdev *sd, struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); return &tx->format; } diff --git a/drivers/media/i2c/adv748x/adv748x-hdmi.c b/drivers/media/i2c/adv748x/adv748x-hdmi.c index 400d71c2745cde..ec151dc69c239f 100644 --- a/drivers/media/i2c/adv748x/adv748x-hdmi.c +++ b/drivers/media/i2c/adv748x/adv748x-hdmi.c @@ -441,8 +441,8 @@ static int adv748x_hdmi_get_format(struct v4l2_subdev *sd, return -EINVAL; if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY) { - mbusformat = v4l2_subdev_get_try_format(sd, sd_state, - sdformat->pad); + mbusformat = v4l2_subdev_state_get_format(sd_state, + sdformat->pad); sdformat->format = *mbusformat; } else { adv748x_hdmi_fill_format(hdmi, &sdformat->format); @@ -464,7 +464,7 @@ static int adv748x_hdmi_set_format(struct v4l2_subdev *sd, if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE) return adv748x_hdmi_get_format(sd, sd_state, sdformat); - mbusformat = v4l2_subdev_get_try_format(sd, sd_state, sdformat->pad); + mbusformat = v4l2_subdev_state_get_format(sd_state, sdformat->pad); *mbusformat = sdformat->format; return 0; diff --git a/drivers/media/i2c/adv7511-v4l2.c b/drivers/media/i2c/adv7511-v4l2.c index a9183d9282fd24..0f780eb6ef6335 100644 --- a/drivers/media/i2c/adv7511-v4l2.c +++ b/drivers/media/i2c/adv7511-v4l2.c @@ -1238,7 +1238,7 @@ static int adv7511_get_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); format->format.code = fmt->code; format->format.colorspace = fmt->colorspace; format->format.ycbcr_enc = fmt->ycbcr_enc; @@ -1293,7 +1293,7 @@ static int adv7511_set_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); fmt->code = format->format.code; fmt->colorspace = format->format.colorspace; fmt->ycbcr_enc = format->format.ycbcr_enc; diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index d1609bd8f0485a..f12e758c4ea022 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -1929,7 +1929,7 @@ static int adv76xx_get_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); format->format.code = fmt->code; } else { format->format.code = state->format->code; @@ -1978,7 +1978,7 @@ static int adv76xx_set_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); fmt->code = format->format.code; } else { state->format = info; diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index c1664a3620c8ed..2ad0f9f5503db8 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -2087,7 +2087,7 @@ static int adv7842_get_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); format->format.code = fmt->code; } else { format->format.code = state->format->code; @@ -2119,7 +2119,7 @@ static int adv7842_set_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); fmt->code = format->format.code; } else { state->format = info; diff --git a/drivers/media/i2c/ar0521.c b/drivers/media/i2c/ar0521.c index 60ab433cafb114..a2ad22807d9dec 100644 --- a/drivers/media/i2c/ar0521.c +++ b/drivers/media/i2c/ar0521.c @@ -448,8 +448,7 @@ static int ar0521_get_fmt(struct v4l2_subdev *sd, mutex_lock(&sensor->lock); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - fmt = v4l2_subdev_get_try_format(&sensor->sd, sd_state, 0 - /* pad */); + fmt = v4l2_subdev_state_get_format(sd_state, 0); else fmt = &sensor->fmt; @@ -474,7 +473,7 @@ static int ar0521_set_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, sd_state, 0 /* pad */); + fmt = v4l2_subdev_state_get_format(sd_state, 0); *fmt = format->format; mutex_unlock(&sensor->lock); diff --git a/drivers/media/i2c/arducam-pivariety.c b/drivers/media/i2c/arducam-pivariety.c index 6bb9e9c48e5c2c..f64d4d46ad6bcb 100644 --- a/drivers/media/i2c/arducam-pivariety.c +++ b/drivers/media/i2c/arducam-pivariety.c @@ -394,7 +394,7 @@ static int pivariety_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct pivariety *pivariety = to_pivariety(sd); struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); struct arducam_format *def_fmt = &pivariety->supported_formats[0]; /* Initialize try_fmt */ @@ -771,7 +771,7 @@ __pivariety_get_pad_crop(struct pivariety *pivariety, switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&pivariety->sd, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: ret = pivariety_read_sel(pivariety, &pivariety->crop); if (ret) diff --git a/drivers/media/i2c/arducam_64mp.c b/drivers/media/i2c/arducam_64mp.c index 0d6a2c463ee4ff..651d2b04c9c1ad 100644 --- a/drivers/media/i2c/arducam_64mp.c +++ b/drivers/media/i2c/arducam_64mp.c @@ -94,16 +94,6 @@ #define ARDUCAM_64MP_TEST_PATTERN_B_DEFAULT 0 #define ARDUCAM_64MP_TEST_PATTERN_GB_DEFAULT 0 -/* Embedded metadata stream structure */ -#define ARDUCAM_64MP_EMBEDDED_LINE_WIDTH (11560 * 3) -#define ARDUCAM_64MP_NUM_EMBEDDED_LINES 1 - -enum pad_types { - IMAGE_PAD, - METADATA_PAD, - NUM_PADS -}; - /* ARDUCAM_64MP native and active pixel array size. */ #define ARDUCAM_64MP_NATIVE_WIDTH 9344U #define ARDUCAM_64MP_NATIVE_HEIGHT 7032U @@ -1422,7 +1412,7 @@ static const char * const arducam_64mp_supply_name[] = { struct arducam_64mp { struct v4l2_subdev sd; - struct media_pad pad[NUM_PADS]; + struct media_pad pad; unsigned int fmt_code; @@ -1555,9 +1545,7 @@ static int arducam_64mp_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct arducam_64mp *arducam_64mp = to_arducam_64mp(sd); struct v4l2_mbus_framefmt *try_fmt_img = - v4l2_subdev_get_try_format(sd, fh->state, IMAGE_PAD); - struct v4l2_mbus_framefmt *try_fmt_meta = - v4l2_subdev_get_try_format(sd, fh->state, METADATA_PAD); + v4l2_subdev_state_get_format(fh->state, 0); struct v4l2_rect *try_crop; mutex_lock(&arducam_64mp->mutex); @@ -1568,14 +1556,8 @@ static int arducam_64mp_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) try_fmt_img->code = arducam_64mp_get_format_code(arducam_64mp); try_fmt_img->field = V4L2_FIELD_NONE; - /* Initialize try_fmt for the embedded metadata pad */ - try_fmt_meta->width = ARDUCAM_64MP_EMBEDDED_LINE_WIDTH; - try_fmt_meta->height = ARDUCAM_64MP_NUM_EMBEDDED_LINES; - try_fmt_meta->code = MEDIA_BUS_FMT_SENSOR_DATA; - try_fmt_meta->field = V4L2_FIELD_NONE; - /* Initialize try_crop */ - try_crop = v4l2_subdev_get_try_crop(sd, fh->state, IMAGE_PAD); + try_crop = v4l2_subdev_state_get_crop(fh->state, 0); try_crop->left = ARDUCAM_64MP_PIXEL_ARRAY_LEFT; try_crop->top = ARDUCAM_64MP_PIXEL_ARRAY_TOP; try_crop->width = ARDUCAM_64MP_PIXEL_ARRAY_WIDTH; @@ -1731,20 +1713,13 @@ static int arducam_64mp_enum_mbus_code(struct v4l2_subdev *sd, { struct arducam_64mp *arducam_64mp = to_arducam_64mp(sd); - if (code->pad >= NUM_PADS) + if (code->pad >= 1) return -EINVAL; - if (code->pad == IMAGE_PAD) { - if (code->index > 0) - return -EINVAL; + if (code->index > 0) + return -EINVAL; - code->code = arducam_64mp_get_format_code(arducam_64mp); - } else { - if (code->index > 0) - return -EINVAL; - - code->code = MEDIA_BUS_FMT_SENSOR_DATA; - } + code->code = arducam_64mp_get_format_code(arducam_64mp); return 0; } @@ -1755,29 +1730,19 @@ static int arducam_64mp_enum_frame_size(struct v4l2_subdev *sd, { struct arducam_64mp *arducam_64mp = to_arducam_64mp(sd); - if (fse->pad >= NUM_PADS) + if (fse->pad >= 1) return -EINVAL; - if (fse->pad == IMAGE_PAD) { - if (fse->index >= ARRAY_SIZE(supported_modes)) - return -EINVAL; - - if (fse->code != arducam_64mp_get_format_code(arducam_64mp)) - return -EINVAL; + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; - fse->min_width = supported_modes[fse->index].width; - fse->max_width = fse->min_width; - fse->min_height = supported_modes[fse->index].height; - fse->max_height = fse->min_height; - } else { - if (fse->code != MEDIA_BUS_FMT_SENSOR_DATA || fse->index > 0) - return -EINVAL; + if (fse->code != arducam_64mp_get_format_code(arducam_64mp)) + return -EINVAL; - fse->min_width = ARDUCAM_64MP_EMBEDDED_LINE_WIDTH; - fse->max_width = fse->min_width; - fse->min_height = ARDUCAM_64MP_NUM_EMBEDDED_LINES; - fse->max_height = fse->min_height; - } + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; return 0; } @@ -1803,45 +1768,28 @@ arducam_64mp_update_image_pad_format(struct arducam_64mp *arducam_64mp, arducam_64mp_reset_colorspace(&fmt->format); } -static void -arducam_64mp_update_metadata_pad_format(struct v4l2_subdev_format *fmt) -{ - fmt->format.width = ARDUCAM_64MP_EMBEDDED_LINE_WIDTH; - fmt->format.height = ARDUCAM_64MP_NUM_EMBEDDED_LINES; - fmt->format.code = MEDIA_BUS_FMT_SENSOR_DATA; - fmt->format.field = V4L2_FIELD_NONE; -} - static int arducam_64mp_get_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct arducam_64mp *arducam_64mp = to_arducam_64mp(sd); - if (fmt->pad >= NUM_PADS) + if (fmt->pad >= 1) return -EINVAL; mutex_lock(&arducam_64mp->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(&arducam_64mp->sd, sd_state, - fmt->pad); + v4l2_subdev_state_get_format(sd_state, fmt->pad); /* update the code which could change due to vflip or hflip: */ - try_fmt->code = fmt->pad == IMAGE_PAD ? - arducam_64mp_get_format_code(arducam_64mp) : - MEDIA_BUS_FMT_SENSOR_DATA; + try_fmt->code = arducam_64mp_get_format_code(arducam_64mp); fmt->format = *try_fmt; } else { - if (fmt->pad == IMAGE_PAD) { - arducam_64mp_update_image_pad_format(arducam_64mp, - arducam_64mp->mode, - fmt); - fmt->format.code = - arducam_64mp_get_format_code(arducam_64mp); - } else { - arducam_64mp_update_metadata_pad_format(fmt); - } + arducam_64mp_update_image_pad_format(arducam_64mp, + arducam_64mp->mode, fmt); + fmt->format.code = + arducam_64mp_get_format_code(arducam_64mp); } mutex_unlock(&arducam_64mp->mutex); @@ -1909,39 +1857,26 @@ static int arducam_64mp_set_pad_format(struct v4l2_subdev *sd, const struct arducam_64mp_mode *mode; struct arducam_64mp *arducam_64mp = to_arducam_64mp(sd); - if (fmt->pad >= NUM_PADS) + if (fmt->pad >= 1) return -EINVAL; mutex_lock(&arducam_64mp->mutex); - if (fmt->pad == IMAGE_PAD) { - /* Bayer order varies with flips */ - fmt->format.code = arducam_64mp_get_format_code(arducam_64mp); - - mode = v4l2_find_nearest_size(supported_modes, - ARRAY_SIZE(supported_modes), - width, height, - fmt->format.width, - fmt->format.height); - arducam_64mp_update_image_pad_format(arducam_64mp, mode, fmt); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); - *framefmt = fmt->format; - } else { - arducam_64mp->mode = mode; - arducam_64mp->fmt_code = fmt->format.code; - arducam_64mp_set_framing_limits(arducam_64mp); - } + /* Bayer order varies with flips */ + fmt->format.code = arducam_64mp_get_format_code(arducam_64mp); + + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), width, + height, fmt->format.width, + fmt->format.height); + arducam_64mp_update_image_pad_format(arducam_64mp, mode, fmt); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); + *framefmt = fmt->format; } else { - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); - *framefmt = fmt->format; - } else { - /* Only one embedded data mode is supported */ - arducam_64mp_update_metadata_pad_format(fmt); - } + arducam_64mp->mode = mode; + arducam_64mp->fmt_code = fmt->format.code; + arducam_64mp_set_framing_limits(arducam_64mp); } mutex_unlock(&arducam_64mp->mutex); @@ -1957,8 +1892,7 @@ __arducam_64mp_get_pad_crop(struct arducam_64mp *arducam_64mp, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&arducam_64mp->sd, sd_state, - pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &arducam_64mp->mode->crop; } @@ -2547,11 +2481,10 @@ static int arducam_64mp_probe(struct i2c_client *client) arducam_64mp->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; /* Initialize source pads */ - arducam_64mp->pad[IMAGE_PAD].flags = MEDIA_PAD_FL_SOURCE; - arducam_64mp->pad[METADATA_PAD].flags = MEDIA_PAD_FL_SOURCE; + arducam_64mp->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_pads_init(&arducam_64mp->sd.entity, NUM_PADS, - arducam_64mp->pad); + ret = media_entity_pads_init(&arducam_64mp->sd.entity, 1, + &arducam_64mp->pad); if (ret) { dev_err(dev, "failed to init entity pads: %d\n", ret); goto error_handler_free; diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index 6f8fbd82e21c8f..e27cd8b296b7ab 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -508,9 +508,8 @@ static void __ccs_update_exposure_limits(struct ccs_sensor *sensor) struct v4l2_ctrl *ctrl = sensor->exposure; int max; - max = sensor->pixel_array->crop[CCS_PA_PAD_SRC].height - + sensor->vblank->val - - CCS_LIM(sensor, COARSE_INTEGRATION_TIME_MAX_MARGIN); + max = sensor->pa_src.height + sensor->vblank->val - + CCS_LIM(sensor, COARSE_INTEGRATION_TIME_MAX_MARGIN); __v4l2_ctrl_modify_range(ctrl, ctrl->minimum, max, ctrl->step, max); } @@ -728,15 +727,12 @@ static int ccs_set_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_VBLANK: rval = ccs_write(sensor, FRAME_LENGTH_LINES, - sensor->pixel_array->crop[ - CCS_PA_PAD_SRC].height - + ctrl->val); + sensor->pa_src.height + ctrl->val); break; case V4L2_CID_HBLANK: rval = ccs_write(sensor, LINE_LENGTH_PCK, - sensor->pixel_array->crop[CCS_PA_PAD_SRC].width - + ctrl->val); + sensor->pa_src.width + ctrl->val); break; case V4L2_CID_TEST_PATTERN: @@ -1214,15 +1210,13 @@ static void ccs_update_blanking(struct ccs_sensor *sensor) min = max_t(int, CCS_LIM(sensor, MIN_FRAME_BLANKING_LINES), - min_fll - sensor->pixel_array->crop[CCS_PA_PAD_SRC].height); - max = max_fll - sensor->pixel_array->crop[CCS_PA_PAD_SRC].height; + min_fll - sensor->pa_src.height); + max = max_fll - sensor->pa_src.height; __v4l2_ctrl_modify_range(vblank, min, max, vblank->step, min); - min = max_t(int, - min_llp - sensor->pixel_array->crop[CCS_PA_PAD_SRC].width, - min_lbp); - max = max_llp - sensor->pixel_array->crop[CCS_PA_PAD_SRC].width; + min = max_t(int, min_llp - sensor->pa_src.width, min_lbp); + max = max_llp - sensor->pa_src.width; __v4l2_ctrl_modify_range(hblank, min, max, hblank->step, min); @@ -1246,10 +1240,8 @@ static int ccs_pll_blanking_update(struct ccs_sensor *sensor) dev_dbg(&client->dev, "real timeperframe\t100/%d\n", sensor->pll.pixel_rate_pixel_array / - ((sensor->pixel_array->crop[CCS_PA_PAD_SRC].width - + sensor->hblank->val) * - (sensor->pixel_array->crop[CCS_PA_PAD_SRC].height - + sensor->vblank->val) / 100)); + ((sensor->pa_src.width + sensor->hblank->val) * + (sensor->pa_src.height + sensor->vblank->val) / 100)); return 0; } @@ -1756,28 +1748,22 @@ static int ccs_start_streaming(struct ccs_sensor *sensor) goto out; /* Analog crop start coordinates */ - rval = ccs_write(sensor, X_ADDR_START, - sensor->pixel_array->crop[CCS_PA_PAD_SRC].left); + rval = ccs_write(sensor, X_ADDR_START, sensor->pa_src.left); if (rval < 0) goto out; - rval = ccs_write(sensor, Y_ADDR_START, - sensor->pixel_array->crop[CCS_PA_PAD_SRC].top); + rval = ccs_write(sensor, Y_ADDR_START, sensor->pa_src.top); if (rval < 0) goto out; /* Analog crop end coordinates */ - rval = ccs_write( - sensor, X_ADDR_END, - sensor->pixel_array->crop[CCS_PA_PAD_SRC].left - + sensor->pixel_array->crop[CCS_PA_PAD_SRC].width - 1); + rval = ccs_write(sensor, X_ADDR_END, + sensor->pa_src.left + sensor->pa_src.width - 1); if (rval < 0) goto out; - rval = ccs_write( - sensor, Y_ADDR_END, - sensor->pixel_array->crop[CCS_PA_PAD_SRC].top - + sensor->pixel_array->crop[CCS_PA_PAD_SRC].height - 1); + rval = ccs_write(sensor, Y_ADDR_END, + sensor->pa_src.top + sensor->pa_src.height - 1); if (rval < 0) goto out; @@ -1789,27 +1775,23 @@ static int ccs_start_streaming(struct ccs_sensor *sensor) /* Digital crop */ if (CCS_LIM(sensor, DIGITAL_CROP_CAPABILITY) == CCS_DIGITAL_CROP_CAPABILITY_INPUT_CROP) { - rval = ccs_write( - sensor, DIGITAL_CROP_X_OFFSET, - sensor->scaler->crop[CCS_PAD_SINK].left); + rval = ccs_write(sensor, DIGITAL_CROP_X_OFFSET, + sensor->scaler_sink.left); if (rval < 0) goto out; - rval = ccs_write( - sensor, DIGITAL_CROP_Y_OFFSET, - sensor->scaler->crop[CCS_PAD_SINK].top); + rval = ccs_write(sensor, DIGITAL_CROP_Y_OFFSET, + sensor->scaler_sink.top); if (rval < 0) goto out; - rval = ccs_write( - sensor, DIGITAL_CROP_IMAGE_WIDTH, - sensor->scaler->crop[CCS_PAD_SINK].width); + rval = ccs_write(sensor, DIGITAL_CROP_IMAGE_WIDTH, + sensor->scaler_sink.width); if (rval < 0) goto out; - rval = ccs_write( - sensor, DIGITAL_CROP_IMAGE_HEIGHT, - sensor->scaler->crop[CCS_PAD_SINK].height); + rval = ccs_write(sensor, DIGITAL_CROP_IMAGE_HEIGHT, + sensor->scaler_sink.height); if (rval < 0) goto out; } @@ -1827,12 +1809,10 @@ static int ccs_start_streaming(struct ccs_sensor *sensor) } /* Output size from sensor */ - rval = ccs_write(sensor, X_OUTPUT_SIZE, - sensor->src->crop[CCS_PAD_SRC].width); + rval = ccs_write(sensor, X_OUTPUT_SIZE, sensor->src_src.width); if (rval < 0) goto out; - rval = ccs_write(sensor, Y_OUTPUT_SIZE, - sensor->src->crop[CCS_PAD_SRC].height); + rval = ccs_write(sensor, Y_OUTPUT_SIZE, sensor->src_src.height); if (rval < 0) goto out; @@ -1898,9 +1878,11 @@ static int ccs_pm_get_init(struct ccs_sensor *sensor) goto error; /* Device was already active, so don't set controls */ - if (rval == 1) + if (rval == 1 && !sensor->handler_setup_needed) return 0; + sensor->handler_setup_needed = false; + /* Restore V4L2 controls to the previously suspended device */ rval = v4l2_ctrl_handler_setup(&sensor->pixel_array->ctrl_handler); if (rval) @@ -1923,9 +1905,6 @@ static int ccs_set_stream(struct v4l2_subdev *subdev, int enable) struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); int rval; - if (sensor->streaming == enable) - return 0; - if (!enable) { ccs_stop_streaming(sensor); sensor->streaming = false; @@ -2053,24 +2032,8 @@ static int __ccs_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { - struct ccs_subdev *ssd = to_ccs_subdev(subdev); - - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - fmt->format = *v4l2_subdev_get_try_format(subdev, sd_state, - fmt->pad); - } else { - struct v4l2_rect *r; - - if (fmt->pad == ssd->source_pad) - r = &ssd->crop[ssd->source_pad]; - else - r = &ssd->sink_fmt; - - fmt->format.code = __ccs_get_mbus_code(subdev, fmt->pad); - fmt->format.width = r->width; - fmt->format.height = r->height; - fmt->format.field = V4L2_FIELD_NONE; - } + fmt->format = *v4l2_subdev_state_get_format(sd_state, fmt->pad); + fmt->format.code = __ccs_get_mbus_code(subdev, fmt->pad); return 0; } @@ -2092,28 +2055,18 @@ static int ccs_get_format(struct v4l2_subdev *subdev, static void ccs_get_crop_compose(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_rect **crops, - struct v4l2_rect **comps, int which) + struct v4l2_rect **comps) { struct ccs_subdev *ssd = to_ccs_subdev(subdev); unsigned int i; - if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { - if (crops) - for (i = 0; i < subdev->entity.num_pads; i++) - crops[i] = &ssd->crop[i]; - if (comps) - *comps = &ssd->compose; - } else { - if (crops) { - for (i = 0; i < subdev->entity.num_pads; i++) - crops[i] = v4l2_subdev_get_try_crop(subdev, - sd_state, - i); - } - if (comps) - *comps = v4l2_subdev_get_try_compose(subdev, sd_state, - CCS_PAD_SINK); - } + if (crops) + for (i = 0; i < subdev->entity.num_pads; i++) + crops[i] = + v4l2_subdev_state_get_crop(sd_state, i); + if (comps) + *comps = v4l2_subdev_state_get_compose(sd_state, + ssd->sink_pad); } /* Changes require propagation only on sink pad. */ @@ -2124,8 +2077,9 @@ static void ccs_propagate(struct v4l2_subdev *subdev, struct ccs_sensor *sensor = to_ccs_sensor(subdev); struct ccs_subdev *ssd = to_ccs_subdev(subdev); struct v4l2_rect *comp, *crops[CCS_PADS]; + struct v4l2_mbus_framefmt *fmt; - ccs_get_crop_compose(subdev, sd_state, crops, &comp, which); + ccs_get_crop_compose(subdev, sd_state, crops, &comp); switch (target) { case V4L2_SEL_TGT_CROP: @@ -2136,6 +2090,7 @@ static void ccs_propagate(struct v4l2_subdev *subdev, sensor->scale_m = CCS_LIM(sensor, SCALER_N_MIN); sensor->scaling_mode = CCS_SCALING_MODE_NO_SCALING; + sensor->scaler_sink = *comp; } else if (ssd == sensor->binner) { sensor->binning_horizontal = 1; sensor->binning_vertical = 1; @@ -2144,6 +2099,11 @@ static void ccs_propagate(struct v4l2_subdev *subdev, fallthrough; case V4L2_SEL_TGT_COMPOSE: *crops[CCS_PAD_SRC] = *comp; + fmt = v4l2_subdev_state_get_format(sd_state, CCS_PAD_SRC); + fmt->width = comp->width; + fmt->height = comp->height; + if (which == V4L2_SUBDEV_FORMAT_ACTIVE && ssd == sensor->src) + sensor->src_src = *crops[CCS_PAD_SRC]; break; default: WARN_ON_ONCE(1); @@ -2252,14 +2212,12 @@ static int ccs_set_format(struct v4l2_subdev *subdev, CCS_LIM(sensor, MIN_Y_OUTPUT_SIZE), CCS_LIM(sensor, MAX_Y_OUTPUT_SIZE)); - ccs_get_crop_compose(subdev, sd_state, crops, NULL, fmt->which); + ccs_get_crop_compose(subdev, sd_state, crops, NULL); crops[ssd->sink_pad]->left = 0; crops[ssd->sink_pad]->top = 0; crops[ssd->sink_pad]->width = fmt->format.width; crops[ssd->sink_pad]->height = fmt->format.height; - if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) - ssd->sink_fmt = *crops[ssd->sink_pad]; ccs_propagate(subdev, sd_state, fmt->which, V4L2_SEL_TGT_CROP); mutex_unlock(&sensor->mutex); @@ -2482,7 +2440,7 @@ static int ccs_set_compose(struct v4l2_subdev *subdev, struct ccs_subdev *ssd = to_ccs_subdev(subdev); struct v4l2_rect *comp, *crops[CCS_PADS]; - ccs_get_crop_compose(subdev, sd_state, crops, &comp, sel->which); + ccs_get_crop_compose(subdev, sd_state, crops, &comp); sel->r.top = 0; sel->r.left = 0; @@ -2501,8 +2459,8 @@ static int ccs_set_compose(struct v4l2_subdev *subdev, return 0; } -static int __ccs_sel_supported(struct v4l2_subdev *subdev, - struct v4l2_subdev_selection *sel) +static int ccs_sel_supported(struct v4l2_subdev *subdev, + struct v4l2_subdev_selection *sel) { struct ccs_sensor *sensor = to_ccs_sensor(subdev); struct ccs_subdev *ssd = to_ccs_subdev(subdev); @@ -2545,33 +2503,18 @@ static int ccs_set_crop(struct v4l2_subdev *subdev, { struct ccs_sensor *sensor = to_ccs_sensor(subdev); struct ccs_subdev *ssd = to_ccs_subdev(subdev); - struct v4l2_rect *src_size, *crops[CCS_PADS]; - struct v4l2_rect _r; + struct v4l2_rect src_size = { 0 }, *crops[CCS_PADS], *comp; - ccs_get_crop_compose(subdev, sd_state, crops, NULL, sel->which); + ccs_get_crop_compose(subdev, sd_state, crops, &comp); - if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - if (sel->pad == ssd->sink_pad) - src_size = &ssd->sink_fmt; - else - src_size = &ssd->compose; + if (sel->pad == ssd->sink_pad) { + struct v4l2_mbus_framefmt *mfmt = + v4l2_subdev_state_get_format(sd_state, sel->pad); + + src_size.width = mfmt->width; + src_size.height = mfmt->height; } else { - if (sel->pad == ssd->sink_pad) { - _r.left = 0; - _r.top = 0; - _r.width = v4l2_subdev_get_try_format(subdev, - sd_state, - sel->pad) - ->width; - _r.height = v4l2_subdev_get_try_format(subdev, - sd_state, - sel->pad) - ->height; - src_size = &_r; - } else { - src_size = v4l2_subdev_get_try_compose( - subdev, sd_state, ssd->sink_pad); - } + src_size = *comp; } if (ssd == sensor->src && sel->pad == CCS_PAD_SRC) { @@ -2579,16 +2522,19 @@ static int ccs_set_crop(struct v4l2_subdev *subdev, sel->r.top = 0; } - sel->r.width = min(sel->r.width, src_size->width); - sel->r.height = min(sel->r.height, src_size->height); + sel->r.width = min(sel->r.width, src_size.width); + sel->r.height = min(sel->r.height, src_size.height); - sel->r.left = min_t(int, sel->r.left, src_size->width - sel->r.width); - sel->r.top = min_t(int, sel->r.top, src_size->height - sel->r.height); + sel->r.left = min_t(int, sel->r.left, src_size.width - sel->r.width); + sel->r.top = min_t(int, sel->r.top, src_size.height - sel->r.height); *crops[sel->pad] = sel->r; if (ssd != sensor->pixel_array && sel->pad == CCS_PAD_SINK) ccs_propagate(subdev, sd_state, sel->which, V4L2_SEL_TGT_CROP); + else if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE && + ssd == sensor->pixel_array) + sensor->pa_src = sel->r; return 0; } @@ -2601,44 +2547,36 @@ static void ccs_get_native_size(struct ccs_subdev *ssd, struct v4l2_rect *r) r->height = CCS_LIM(ssd->sensor, Y_ADDR_MAX) + 1; } -static int __ccs_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_selection *sel) +static int ccs_get_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) { struct ccs_sensor *sensor = to_ccs_sensor(subdev); struct ccs_subdev *ssd = to_ccs_subdev(subdev); struct v4l2_rect *comp, *crops[CCS_PADS]; - struct v4l2_rect sink_fmt; int ret; - ret = __ccs_sel_supported(subdev, sel); + ret = ccs_sel_supported(subdev, sel); if (ret) return ret; - ccs_get_crop_compose(subdev, sd_state, crops, &comp, sel->which); - - if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - sink_fmt = ssd->sink_fmt; - } else { - struct v4l2_mbus_framefmt *fmt = - v4l2_subdev_get_try_format(subdev, sd_state, - ssd->sink_pad); - - sink_fmt.left = 0; - sink_fmt.top = 0; - sink_fmt.width = fmt->width; - sink_fmt.height = fmt->height; - } + ccs_get_crop_compose(subdev, sd_state, crops, &comp); switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: case V4L2_SEL_TGT_NATIVE_SIZE: - if (ssd == sensor->pixel_array) + if (ssd == sensor->pixel_array) { ccs_get_native_size(ssd, &sel->r); - else if (sel->pad == ssd->sink_pad) - sel->r = sink_fmt; - else + } else if (sel->pad == ssd->sink_pad) { + struct v4l2_mbus_framefmt *sink_fmt = + v4l2_subdev_state_get_format(sd_state, + ssd->sink_pad); + sel->r.top = sel->r.left = 0; + sel->r.width = sink_fmt->width; + sel->r.height = sink_fmt->height; + } else { sel->r = *comp; + } break; case V4L2_SEL_TGT_CROP: case V4L2_SEL_TGT_COMPOSE_BOUNDS: @@ -2652,20 +2590,6 @@ static int __ccs_get_selection(struct v4l2_subdev *subdev, return 0; } -static int ccs_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_selection *sel) -{ - struct ccs_sensor *sensor = to_ccs_sensor(subdev); - int rval; - - mutex_lock(&sensor->mutex); - rval = __ccs_get_selection(subdev, sd_state, sel); - mutex_unlock(&sensor->mutex); - - return rval; -} - static int ccs_set_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) @@ -2673,7 +2597,7 @@ static int ccs_set_selection(struct v4l2_subdev *subdev, struct ccs_sensor *sensor = to_ccs_sensor(subdev); int ret; - ret = __ccs_sel_supported(subdev, sel); + ret = ccs_sel_supported(subdev, sel); if (ret) return ret; @@ -2945,7 +2869,6 @@ static int ccs_identify_module(struct ccs_sensor *sensor) } static const struct v4l2_subdev_ops ccs_ops; -static const struct v4l2_subdev_internal_ops ccs_internal_ops; static const struct media_entity_operations ccs_entity_ops; static int ccs_register_subdev(struct ccs_sensor *sensor, @@ -2959,12 +2882,6 @@ static int ccs_register_subdev(struct ccs_sensor *sensor, if (!sink_ssd) return 0; - rval = media_entity_pads_init(&ssd->sd.entity, ssd->npads, ssd->pads); - if (rval) { - dev_err(&client->dev, "media_entity_pads_init failed\n"); - return rval; - } - rval = v4l2_device_register_subdev(sensor->src->sd.v4l2_dev, &ssd->sd); if (rval) { dev_err(&client->dev, "v4l2_device_register_subdev failed\n"); @@ -3025,6 +2942,12 @@ static int ccs_registered(struct v4l2_subdev *subdev) static void ccs_cleanup(struct ccs_sensor *sensor) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int i; + + for (i = 0; i < sensor->ssds_used; i++) { + v4l2_subdev_cleanup(&sensor->ssds[2].sd); + media_entity_cleanup(&sensor->ssds[i].sd.entity); + } device_remove_file(&client->dev, &dev_attr_nvm); device_remove_file(&client->dev, &dev_attr_ident); @@ -3032,14 +2955,17 @@ static void ccs_cleanup(struct ccs_sensor *sensor) ccs_free_controls(sensor); } -static void ccs_create_subdev(struct ccs_sensor *sensor, - struct ccs_subdev *ssd, const char *name, - unsigned short num_pads, u32 function) +static int ccs_init_subdev(struct ccs_sensor *sensor, + struct ccs_subdev *ssd, const char *name, + unsigned short num_pads, u32 function, + const char *lock_name, + struct lock_class_key *lock_key) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; if (!ssd) - return; + return 0; if (ssd != sensor->src) v4l2_subdev_init(&ssd->sd, &ccs_ops); @@ -3053,57 +2979,70 @@ static void ccs_create_subdev(struct ccs_sensor *sensor, v4l2_i2c_subdev_set_name(&ssd->sd, client, sensor->minfo.name, name); - ccs_get_native_size(ssd, &ssd->sink_fmt); - - ssd->compose.width = ssd->sink_fmt.width; - ssd->compose.height = ssd->sink_fmt.height; - ssd->crop[ssd->source_pad] = ssd->compose; ssd->pads[ssd->source_pad].flags = MEDIA_PAD_FL_SOURCE; - if (ssd != sensor->pixel_array) { - ssd->crop[ssd->sink_pad] = ssd->compose; + if (ssd != sensor->pixel_array) ssd->pads[ssd->sink_pad].flags = MEDIA_PAD_FL_SINK; - } ssd->sd.entity.ops = &ccs_entity_ops; - if (ssd == sensor->src) - return; + if (ssd != sensor->src) { + ssd->sd.owner = THIS_MODULE; + ssd->sd.dev = &client->dev; + v4l2_set_subdevdata(&ssd->sd, client); + } + + rval = media_entity_pads_init(&ssd->sd.entity, ssd->npads, ssd->pads); + if (rval) { + dev_err(&client->dev, "media_entity_pads_init failed\n"); + return rval; + } - ssd->sd.internal_ops = &ccs_internal_ops; - ssd->sd.owner = THIS_MODULE; - ssd->sd.dev = &client->dev; - v4l2_set_subdevdata(&ssd->sd, client); + rval = __v4l2_subdev_init_finalize(&ssd->sd, lock_name, lock_key); + if (rval) { + media_entity_cleanup(&ssd->sd.entity); + return rval; + } + + return 0; } -static int ccs_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +static int ccs_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct ccs_subdev *ssd = to_ccs_subdev(sd); struct ccs_sensor *sensor = ssd->sensor; - unsigned int i; + unsigned int pad = ssd == sensor->pixel_array ? + CCS_PA_PAD_SRC : CCS_PAD_SINK; + struct v4l2_mbus_framefmt *fmt = + v4l2_subdev_state_get_format(sd_state, pad); + struct v4l2_rect *crop = + v4l2_subdev_state_get_crop(sd_state, pad); + bool is_active = !sd->active_state || sd->active_state == sd_state; mutex_lock(&sensor->mutex); - for (i = 0; i < ssd->npads; i++) { - struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->state, i); - struct v4l2_rect *try_crop = - v4l2_subdev_get_try_crop(sd, fh->state, i); - struct v4l2_rect *try_comp; + ccs_get_native_size(ssd, crop); - ccs_get_native_size(ssd, try_crop); + fmt->width = crop->width; + fmt->height = crop->height; + fmt->code = sensor->internal_csi_format->code; + fmt->field = V4L2_FIELD_NONE; - try_fmt->width = try_crop->width; - try_fmt->height = try_crop->height; - try_fmt->code = sensor->internal_csi_format->code; - try_fmt->field = V4L2_FIELD_NONE; + if (ssd == sensor->pixel_array) { + if (is_active) + sensor->pa_src = *crop; - if (ssd == sensor->pixel_array) - continue; - - try_comp = v4l2_subdev_get_try_compose(sd, fh->state, i); - *try_comp = *try_crop; + mutex_unlock(&sensor->mutex); + return 0; } + fmt = v4l2_subdev_state_get_format(sd_state, CCS_PAD_SRC); + fmt->code = ssd == sensor->src ? + sensor->csi_format->code : sensor->internal_csi_format->code; + fmt->field = V4L2_FIELD_NONE; + + ccs_propagate(sd, sd_state, is_active, V4L2_SEL_TGT_CROP); + mutex_unlock(&sensor->mutex); return 0; @@ -3139,13 +3078,9 @@ static const struct media_entity_operations ccs_entity_ops = { }; static const struct v4l2_subdev_internal_ops ccs_internal_src_ops = { + .init_state = ccs_init_state, .registered = ccs_registered, .unregistered = ccs_unregistered, - .open = ccs_open, -}; - -static const struct v4l2_subdev_internal_ops ccs_internal_ops = { - .open = ccs_open, }; /* ----------------------------------------------------------------------------- @@ -3311,6 +3246,8 @@ static int ccs_firmware_name(struct i2c_client *client, static int ccs_probe(struct i2c_client *client) { + static struct lock_class_key pixel_array_lock_key, binner_lock_key, + scaler_lock_key; const struct ccs_device *ccsdev = device_get_match_data(&client->dev); struct ccs_sensor *sensor; const struct firmware *fw; @@ -3587,12 +3524,27 @@ static int ccs_probe(struct i2c_client *client) sensor->pll.ext_clk_freq_hz = sensor->hwcfg.ext_clk; sensor->pll.scale_n = CCS_LIM(sensor, SCALER_N_MIN); - ccs_create_subdev(sensor, sensor->scaler, " scaler", 2, - MEDIA_ENT_F_PROC_VIDEO_SCALER); - ccs_create_subdev(sensor, sensor->binner, " binner", 2, - MEDIA_ENT_F_PROC_VIDEO_SCALER); - ccs_create_subdev(sensor, sensor->pixel_array, " pixel_array", 1, - MEDIA_ENT_F_CAM_SENSOR); + rval = ccs_get_mbus_formats(sensor); + if (rval) { + rval = -ENODEV; + goto out_cleanup; + } + + rval = ccs_init_subdev(sensor, sensor->scaler, " scaler", 2, + MEDIA_ENT_F_PROC_VIDEO_SCALER, + "ccs scaler mutex", &scaler_lock_key); + if (rval) + goto out_cleanup; + rval = ccs_init_subdev(sensor, sensor->binner, " binner", 2, + MEDIA_ENT_F_PROC_VIDEO_SCALER, + "ccs binner mutex", &binner_lock_key); + if (rval) + goto out_cleanup; + rval = ccs_init_subdev(sensor, sensor->pixel_array, " pixel_array", 1, + MEDIA_ENT_F_CAM_SENSOR, "ccs pixel array mutex", + &pixel_array_lock_key); + if (rval) + goto out_cleanup; rval = ccs_init_controls(sensor); if (rval < 0) @@ -3602,12 +3554,6 @@ static int ccs_probe(struct i2c_client *client) if (rval) goto out_cleanup; - rval = ccs_get_mbus_formats(sensor); - if (rval) { - rval = -ENODEV; - goto out_cleanup; - } - rval = ccs_init_late_controls(sensor); if (rval) { rval = -ENODEV; @@ -3624,15 +3570,11 @@ static int ccs_probe(struct i2c_client *client) sensor->streaming = false; sensor->dev_init_done = true; - - rval = media_entity_pads_init(&sensor->src->sd.entity, 2, - sensor->src->pads); - if (rval < 0) - goto out_media_entity_cleanup; + sensor->handler_setup_needed = true; rval = ccs_write_msr_regs(sensor); if (rval) - goto out_media_entity_cleanup; + goto out_cleanup; pm_runtime_set_active(&client->dev); pm_runtime_get_noresume(&client->dev); @@ -3652,9 +3594,6 @@ static int ccs_probe(struct i2c_client *client) pm_runtime_put_noidle(&client->dev); pm_runtime_disable(&client->dev); -out_media_entity_cleanup: - media_entity_cleanup(&sensor->src->sd.entity); - out_cleanup: ccs_cleanup(sensor); @@ -3687,10 +3626,8 @@ static void ccs_remove(struct i2c_client *client) ccs_power_off(&client->dev); pm_runtime_set_suspended(&client->dev); - for (i = 0; i < sensor->ssds_used; i++) { + for (i = 0; i < sensor->ssds_used; i++) v4l2_device_unregister_subdev(&sensor->ssds[i].sd); - media_entity_cleanup(&sensor->ssds[i].sd.entity); - } ccs_cleanup(sensor); mutex_destroy(&sensor->mutex); kfree(sensor->ccs_limits); diff --git a/drivers/media/i2c/ccs/ccs.h b/drivers/media/i2c/ccs/ccs.h index a94c796cea4871..2c013d96adcc2b 100644 --- a/drivers/media/i2c/ccs/ccs.h +++ b/drivers/media/i2c/ccs/ccs.h @@ -182,9 +182,6 @@ struct ccs_binning_subtype { struct ccs_subdev { struct v4l2_subdev sd; struct media_pad pads[CCS_PADS]; - struct v4l2_rect sink_fmt; - struct v4l2_rect crop[CCS_PADS]; - struct v4l2_rect compose; /* compose on sink */ unsigned short sink_pad; unsigned short source_pad; int npads; @@ -220,6 +217,7 @@ struct ccs_sensor { u32 mbus_frame_fmts; const struct ccs_csi_data_format *csi_format; const struct ccs_csi_data_format *internal_csi_format; + struct v4l2_rect pa_src, scaler_sink, src_src; u32 default_mbus_frame_fmts; int default_pixel_order; struct ccs_data_container sdata, mdata; @@ -238,6 +236,7 @@ struct ccs_sensor { bool streaming; bool dev_init_done; + bool handler_setup_needed; u8 compressed_min_bpp; struct ccs_module_info minfo; diff --git a/drivers/media/i2c/ds90ub913.c b/drivers/media/i2c/ds90ub913.c index 4bfa3b3cf619b3..ca9bb29dab890c 100644 --- a/drivers/media/i2c/ds90ub913.c +++ b/drivers/media/i2c/ds90ub913.c @@ -362,8 +362,6 @@ static int ub913_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, if (ret) return ret; - memset(fd, 0, sizeof(*fd)); - fd->type = V4L2_MBUS_FRAME_DESC_TYPE_PARALLEL; state = v4l2_subdev_lock_and_get_active_state(sd); @@ -426,8 +424,7 @@ static int ub913_set_fmt(struct v4l2_subdev *sd, } /* Set sink format */ - fmt = v4l2_subdev_state_get_stream_format(state, format->pad, - format->stream); + fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); if (!fmt) return -EINVAL; @@ -446,8 +443,8 @@ static int ub913_set_fmt(struct v4l2_subdev *sd, return 0; } -static int ub913_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int ub913_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct v4l2_subdev_route routes[] = { { @@ -506,7 +503,6 @@ static const struct v4l2_subdev_pad_ops ub913_pad_ops = { .get_frame_desc = ub913_get_frame_desc, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = ub913_set_fmt, - .init_cfg = ub913_init_cfg, }; static const struct v4l2_subdev_ops ub913_subdev_ops = { @@ -514,6 +510,10 @@ static const struct v4l2_subdev_ops ub913_subdev_ops = { .pad = &ub913_pad_ops, }; +static const struct v4l2_subdev_internal_ops ub913_internal_ops = { + .init_state = ub913_init_state, +}; + static const struct media_entity_operations ub913_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -747,6 +747,7 @@ static int ub913_subdev_init(struct ub913_data *priv) int ret; v4l2_i2c_subdev_init(&priv->sd, priv->client, &ub913_subdev_ops); + priv->sd.internal_ops = &ub913_internal_ops; priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; priv->sd.entity.ops = &ub913_entity_ops; diff --git a/drivers/media/i2c/ds90ub953.c b/drivers/media/i2c/ds90ub953.c index dc394e22a42c43..16f88db1498162 100644 --- a/drivers/media/i2c/ds90ub953.c +++ b/drivers/media/i2c/ds90ub953.c @@ -499,8 +499,6 @@ static int ub953_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, if (ret) return ret; - memset(fd, 0, sizeof(*fd)); - fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; state = v4l2_subdev_lock_and_get_active_state(sd); @@ -560,8 +558,7 @@ static int ub953_set_fmt(struct v4l2_subdev *sd, return v4l2_subdev_get_fmt(sd, state, format); /* Set sink format */ - fmt = v4l2_subdev_state_get_stream_format(state, format->pad, - format->stream); + fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); if (!fmt) return -EINVAL; @@ -578,8 +575,8 @@ static int ub953_set_fmt(struct v4l2_subdev *sd, return 0; } -static int ub953_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int ub953_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct v4l2_subdev_route routes[] = { { @@ -716,7 +713,6 @@ static const struct v4l2_subdev_pad_ops ub953_pad_ops = { .get_frame_desc = ub953_get_frame_desc, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = ub953_set_fmt, - .init_cfg = ub953_init_cfg, }; static const struct v4l2_subdev_core_ops ub953_subdev_core_ops = { @@ -730,6 +726,10 @@ static const struct v4l2_subdev_ops ub953_subdev_ops = { .pad = &ub953_pad_ops, }; +static const struct v4l2_subdev_internal_ops ub953_internal_ops = { + .init_state = ub953_init_state, +}; + static const struct media_entity_operations ub953_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -1243,6 +1243,7 @@ static int ub953_subdev_init(struct ub953_data *priv) int ret; v4l2_i2c_subdev_init(&priv->sd, priv->client, &ub953_subdev_ops); + priv->sd.internal_ops = &ub953_internal_ops; priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_STREAMS; diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c index 8ba5750f5a2319..ffe5f25f864762 100644 --- a/drivers/media/i2c/ds90ub960.c +++ b/drivers/media/i2c/ds90ub960.c @@ -2451,9 +2451,8 @@ static int ub960_configure_ports_for_streaming(struct ub960_data *priv, if (rx_data[nport].num_streams > 2) return -EPIPE; - fmt = v4l2_subdev_state_get_stream_format(state, - route->sink_pad, - route->sink_stream); + fmt = v4l2_subdev_state_get_format(state, route->sink_pad, + route->sink_stream); if (!fmt) return -EPIPE; @@ -2786,8 +2785,6 @@ static int ub960_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, if (!ub960_pad_is_source(priv, pad)) return -EINVAL; - memset(fd, 0, sizeof(*fd)); - fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; state = v4l2_subdev_lock_and_get_active_state(&priv->sd); @@ -2844,8 +2841,8 @@ static int ub960_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, const struct ub960_format_info *ub960_fmt; struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_state_get_stream_format(state, pad, - route->source_stream); + fmt = v4l2_subdev_state_get_format(state, pad, + route->source_stream); if (!fmt) { ret = -EINVAL; @@ -2893,8 +2890,7 @@ static int ub960_set_fmt(struct v4l2_subdev *sd, if (!ub960_find_format(format->format.code)) format->format.code = ub960_formats[0].code; - fmt = v4l2_subdev_state_get_stream_format(state, format->pad, - format->stream); + fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); if (!fmt) return -EINVAL; @@ -2910,8 +2906,8 @@ static int ub960_set_fmt(struct v4l2_subdev *sd, return 0; } -static int ub960_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int ub960_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct ub960_data *priv = sd_to_ub960(sd); @@ -2942,8 +2938,6 @@ static const struct v4l2_subdev_pad_ops ub960_pad_ops = { .get_fmt = v4l2_subdev_get_fmt, .set_fmt = ub960_set_fmt, - - .init_cfg = ub960_init_cfg, }; static int ub960_log_status(struct v4l2_subdev *sd) @@ -3095,6 +3089,10 @@ static const struct v4l2_subdev_core_ops ub960_subdev_core_ops = { .unsubscribe_event = v4l2_event_subdev_unsubscribe, }; +static const struct v4l2_subdev_internal_ops ub960_internal_ops = { + .init_state = ub960_init_state, +}; + static const struct v4l2_subdev_ops ub960_subdev_ops = { .core = &ub960_subdev_core_ops, .pad = &ub960_pad_ops, @@ -3654,6 +3652,7 @@ static int ub960_create_subdev(struct ub960_data *priv) int ret; v4l2_i2c_subdev_init(&priv->sd, priv->client, &ub960_subdev_ops); + priv->sd.internal_ops = &ub960_internal_ops; v4l2_ctrl_handler_init(&priv->ctrl_handler, 1); priv->sd.ctrl_handler = &priv->ctrl_handler; diff --git a/drivers/media/i2c/et8ek8/et8ek8_driver.c b/drivers/media/i2c/et8ek8/et8ek8_driver.c index 0d6f0f8506f76f..b13b3db7af5d51 100644 --- a/drivers/media/i2c/et8ek8/et8ek8_driver.c +++ b/drivers/media/i2c/et8ek8/et8ek8_driver.c @@ -995,8 +995,7 @@ __et8ek8_get_pad_format(struct et8ek8_sensor *sensor, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&sensor->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &sensor->format; default: diff --git a/drivers/media/i2c/gc2145.c b/drivers/media/i2c/gc2145.c new file mode 100644 index 00000000000000..bef7b0e056a8d9 --- /dev/null +++ b/drivers/media/i2c/gc2145.c @@ -0,0 +1,1450 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * A V4L2 driver for Galaxycore GC2145 camera. + * Copyright (C) 2023, STMicroelectronics SA + * + * Inspired by the imx219.c driver + * + * Datasheet v1.0 available at http://files.pine64.org/doc/datasheet/PinebookPro/GC2145%20CSP%20DataSheet%20release%20V1.0_20131201.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* Chip ID */ +#define GC2145_CHIP_ID 0x2145 + +/* Page 0 */ +#define GC2145_REG_EXPOSURE CCI_REG16(0x03) +#define GC2145_REG_HBLANK CCI_REG16(0x05) +#define GC2145_REG_VBLANK CCI_REG16(0x07) +#define GC2145_REG_ROW_START CCI_REG16(0x09) +#define GC2145_REG_COL_START CCI_REG16(0x0b) +#define GC2145_REG_WIN_HEIGHT CCI_REG16(0x0d) +#define GC2145_REG_WIN_WIDTH CCI_REG16(0x0f) +#define GC2145_REG_ANALOG_MODE1 CCI_REG8(0x17) +#define GC2145_REG_OUTPUT_FMT CCI_REG8(0x84) +#define GC2145_REG_SYNC_MODE CCI_REG8(0x86) +#define GC2145_SYNC_MODE_COL_SWITCH BIT(4) +#define GC2145_SYNC_MODE_ROW_SWITCH BIT(5) +#define GC2145_REG_BYPASS_MODE CCI_REG8(0x89) +#define GC2145_BYPASS_MODE_SWITCH BIT(5) +#define GC2145_REG_DEBUG_MODE2 CCI_REG8(0x8c) +#define GC2145_REG_DEBUG_MODE3 CCI_REG8(0x8d) +#define GC2145_REG_CROP_ENABLE CCI_REG8(0x90) +#define GC2145_REG_CROP_Y CCI_REG16(0x91) +#define GC2145_REG_CROP_X CCI_REG16(0x93) +#define GC2145_REG_CROP_HEIGHT CCI_REG16(0x95) +#define GC2145_REG_CROP_WIDTH CCI_REG16(0x97) +#define GC2145_REG_GLOBAL_GAIN CCI_REG8(0xb0) +#define GC2145_REG_CHIP_ID CCI_REG16(0xf0) +#define GC2145_REG_PAD_IO CCI_REG8(0xf2) +#define GC2145_REG_PAGE_SELECT CCI_REG8(0xfe) +/* Page 3 */ +#define GC2145_REG_DPHY_ANALOG_MODE1 CCI_REG8(0x01) +#define GC2145_DPHY_MODE_PHY_CLK_EN BIT(0) +#define GC2145_DPHY_MODE_PHY_LANE0_EN BIT(1) +#define GC2145_DPHY_MODE_PHY_LANE1_EN BIT(2) +#define GC2145_DPHY_MODE_PHY_CLK_LANE_P2S_SEL BIT(7) +#define GC2145_REG_DPHY_ANALOG_MODE2 CCI_REG8(0x02) +#define GC2145_DPHY_CLK_DIFF(a) ((a) & 0x07) +#define GC2145_DPHY_LANE0_DIFF(a) (((a) & 0x07) << 4) +#define GC2145_REG_DPHY_ANALOG_MODE3 CCI_REG8(0x03) +#define GC2145_DPHY_LANE1_DIFF(a) ((a) & 0x07) +#define GC2145_DPHY_CLK_DELAY BIT(4) +#define GC2145_DPHY_LANE0_DELAY BIT(5) +#define GC2145_DPHY_LANE1_DELAY BIT(6) +#define GC2145_REG_FIFO_FULL_LVL_LOW CCI_REG8(0x04) +#define GC2145_REG_FIFO_FULL_LVL_HIGH CCI_REG8(0x05) +#define GC2145_REG_FIFO_MODE CCI_REG8(0x06) +#define GC2145_FIFO_MODE_READ_GATE BIT(3) +#define GC2145_FIFO_MODE_MIPI_CLK_MODULE BIT(7) +#define GC2145_REG_BUF_CSI2_MODE CCI_REG8(0x10) +#define GC2145_CSI2_MODE_DOUBLE BIT(0) +#define GC2145_CSI2_MODE_RAW8 BIT(2) +#define GC2145_CSI2_MODE_MIPI_EN BIT(4) +#define GC2145_CSI2_MODE_EN BIT(7) +#define GC2145_REG_MIPI_DT CCI_REG8(0x11) +#define GC2145_REG_LWC_LOW CCI_REG8(0x12) +#define GC2145_REG_LWC_HIGH CCI_REG8(0x13) +#define GC2145_REG_DPHY_MODE CCI_REG8(0x15) +#define GC2145_DPHY_MODE_TRIGGER_PROG BIT(4) +#define GC2145_REG_FIFO_GATE_MODE CCI_REG8(0x17) +#define GC2145_REG_T_LPX CCI_REG8(0x21) +#define GC2145_REG_T_CLK_HS_PREPARE CCI_REG8(0x22) +#define GC2145_REG_T_CLK_ZERO CCI_REG8(0x23) +#define GC2145_REG_T_CLK_PRE CCI_REG8(0x24) +#define GC2145_REG_T_CLK_POST CCI_REG8(0x25) +#define GC2145_REG_T_CLK_TRAIL CCI_REG8(0x26) +#define GC2145_REG_T_HS_EXIT CCI_REG8(0x27) +#define GC2145_REG_T_WAKEUP CCI_REG8(0x28) +#define GC2145_REG_T_HS_PREPARE CCI_REG8(0x29) +#define GC2145_REG_T_HS_ZERO CCI_REG8(0x2a) +#define GC2145_REG_T_HS_TRAIL CCI_REG8(0x2b) + +/* External clock frequency is 24.0MHz */ +#define GC2145_XCLK_FREQ (24 * HZ_PER_MHZ) + +#define GC2145_NATIVE_WIDTH 1616U +#define GC2145_NATIVE_HEIGHT 1232U + +/** + * struct gc2145_mode - GC2145 mode description + * @width: frame width (in pixels) + * @height: frame height (in pixels) + * @reg_seq: registers config sequence to enter into the mode + * @reg_seq_size: size of the sequence + * @pixel_rate: pixel rate associated with the mode + * @crop: window area captured + * @hblank: default horizontal blanking + * @vblank: default vertical blanking + * @link_freq_index: index within the link frequency menu + */ +struct gc2145_mode { + unsigned int width; + unsigned int height; + const struct cci_reg_sequence *reg_seq; + size_t reg_seq_size; + unsigned long pixel_rate; + struct v4l2_rect crop; + unsigned int hblank; + unsigned int vblank; + unsigned int link_freq_index; +}; + +#define GC2145_DEFAULT_EXPOSURE 0x04e2 +#define GC2145_DEFAULT_GLOBAL_GAIN 0x55 +static const struct cci_reg_sequence gc2145_common_regs[] = { + {GC2145_REG_PAGE_SELECT, 0x00}, + /* SH Delay */ + {CCI_REG8(0x12), 0x2e}, + /* Flip */ + {GC2145_REG_ANALOG_MODE1, 0x14}, + /* Analog Conf */ + {CCI_REG8(0x18), 0x22}, {CCI_REG8(0x19), 0x0e}, {CCI_REG8(0x1a), 0x01}, + {CCI_REG8(0x1b), 0x4b}, {CCI_REG8(0x1c), 0x07}, {CCI_REG8(0x1d), 0x10}, + {CCI_REG8(0x1e), 0x88}, {CCI_REG8(0x1f), 0x78}, {CCI_REG8(0x20), 0x03}, + {CCI_REG8(0x21), 0x40}, {CCI_REG8(0x22), 0xa0}, {CCI_REG8(0x24), 0x16}, + {CCI_REG8(0x25), 0x01}, {CCI_REG8(0x26), 0x10}, {CCI_REG8(0x2d), 0x60}, + {CCI_REG8(0x30), 0x01}, {CCI_REG8(0x31), 0x90}, {CCI_REG8(0x33), 0x06}, + {CCI_REG8(0x34), 0x01}, + /* ISP related */ + {CCI_REG8(0x80), 0x7f}, {CCI_REG8(0x81), 0x26}, {CCI_REG8(0x82), 0xfa}, + {CCI_REG8(0x83), 0x00}, {CCI_REG8(0x84), 0x02}, {CCI_REG8(0x86), 0x02}, + {CCI_REG8(0x88), 0x03}, + {GC2145_REG_BYPASS_MODE, 0x03}, + {CCI_REG8(0x85), 0x08}, {CCI_REG8(0x8a), 0x00}, {CCI_REG8(0x8b), 0x00}, + {GC2145_REG_GLOBAL_GAIN, GC2145_DEFAULT_GLOBAL_GAIN}, + {CCI_REG8(0xc3), 0x00}, {CCI_REG8(0xc4), 0x80}, {CCI_REG8(0xc5), 0x90}, + {CCI_REG8(0xc6), 0x3b}, {CCI_REG8(0xc7), 0x46}, + /* BLK */ + {GC2145_REG_PAGE_SELECT, 0x00}, + {CCI_REG8(0x40), 0x42}, {CCI_REG8(0x41), 0x00}, {CCI_REG8(0x43), 0x5b}, + {CCI_REG8(0x5e), 0x00}, {CCI_REG8(0x5f), 0x00}, {CCI_REG8(0x60), 0x00}, + {CCI_REG8(0x61), 0x00}, {CCI_REG8(0x62), 0x00}, {CCI_REG8(0x63), 0x00}, + {CCI_REG8(0x64), 0x00}, {CCI_REG8(0x65), 0x00}, {CCI_REG8(0x66), 0x20}, + {CCI_REG8(0x67), 0x20}, {CCI_REG8(0x68), 0x20}, {CCI_REG8(0x69), 0x20}, + {CCI_REG8(0x76), 0x00}, {CCI_REG8(0x6a), 0x08}, {CCI_REG8(0x6b), 0x08}, + {CCI_REG8(0x6c), 0x08}, {CCI_REG8(0x6d), 0x08}, {CCI_REG8(0x6e), 0x08}, + {CCI_REG8(0x6f), 0x08}, {CCI_REG8(0x70), 0x08}, {CCI_REG8(0x71), 0x08}, + {CCI_REG8(0x76), 0x00}, {CCI_REG8(0x72), 0xf0}, {CCI_REG8(0x7e), 0x3c}, + {CCI_REG8(0x7f), 0x00}, + {GC2145_REG_PAGE_SELECT, 0x02}, + {CCI_REG8(0x48), 0x15}, {CCI_REG8(0x49), 0x00}, {CCI_REG8(0x4b), 0x0b}, + /* AEC */ + {GC2145_REG_PAGE_SELECT, 0x00}, + {GC2145_REG_EXPOSURE, GC2145_DEFAULT_EXPOSURE}, + {GC2145_REG_PAGE_SELECT, 0x01}, + {CCI_REG8(0x01), 0x04}, {CCI_REG8(0x02), 0xc0}, {CCI_REG8(0x03), 0x04}, + {CCI_REG8(0x04), 0x90}, {CCI_REG8(0x05), 0x30}, {CCI_REG8(0x06), 0x90}, + {CCI_REG8(0x07), 0x30}, {CCI_REG8(0x08), 0x80}, {CCI_REG8(0x09), 0x00}, + {CCI_REG8(0x0a), 0x82}, {CCI_REG8(0x0b), 0x11}, {CCI_REG8(0x0c), 0x10}, + {CCI_REG8(0x11), 0x10}, {CCI_REG8(0x13), 0x7b}, {CCI_REG8(0x17), 0x00}, + {CCI_REG8(0x1c), 0x11}, {CCI_REG8(0x1e), 0x61}, {CCI_REG8(0x1f), 0x35}, + {CCI_REG8(0x20), 0x40}, {CCI_REG8(0x22), 0x40}, {CCI_REG8(0x23), 0x20}, + {GC2145_REG_PAGE_SELECT, 0x02}, + {CCI_REG8(0x0f), 0x04}, + {GC2145_REG_PAGE_SELECT, 0x01}, + {CCI_REG8(0x12), 0x35}, {CCI_REG8(0x15), 0xb0}, {CCI_REG8(0x10), 0x31}, + {CCI_REG8(0x3e), 0x28}, {CCI_REG8(0x3f), 0xb0}, {CCI_REG8(0x40), 0x90}, + {CCI_REG8(0x41), 0x0f}, + /* INTPEE */ + {GC2145_REG_PAGE_SELECT, 0x02}, + {CCI_REG8(0x90), 0x6c}, {CCI_REG8(0x91), 0x03}, {CCI_REG8(0x92), 0xcb}, + {CCI_REG8(0x94), 0x33}, {CCI_REG8(0x95), 0x84}, {CCI_REG8(0x97), 0x65}, + {CCI_REG8(0xa2), 0x11}, + /* DNDD */ + {GC2145_REG_PAGE_SELECT, 0x02}, + {CCI_REG8(0x80), 0xc1}, {CCI_REG8(0x81), 0x08}, {CCI_REG8(0x82), 0x05}, + {CCI_REG8(0x83), 0x08}, {CCI_REG8(0x84), 0x0a}, {CCI_REG8(0x86), 0xf0}, + {CCI_REG8(0x87), 0x50}, {CCI_REG8(0x88), 0x15}, {CCI_REG8(0x89), 0xb0}, + {CCI_REG8(0x8a), 0x30}, {CCI_REG8(0x8b), 0x10}, + /* ASDE */ + {GC2145_REG_PAGE_SELECT, 0x01}, + {CCI_REG8(0x21), 0x04}, + {GC2145_REG_PAGE_SELECT, 0x02}, + {CCI_REG8(0xa3), 0x50}, {CCI_REG8(0xa4), 0x20}, {CCI_REG8(0xa5), 0x40}, + {CCI_REG8(0xa6), 0x80}, {CCI_REG8(0xab), 0x40}, {CCI_REG8(0xae), 0x0c}, + {CCI_REG8(0xb3), 0x46}, {CCI_REG8(0xb4), 0x64}, {CCI_REG8(0xb6), 0x38}, + {CCI_REG8(0xb7), 0x01}, {CCI_REG8(0xb9), 0x2b}, {CCI_REG8(0x3c), 0x04}, + {CCI_REG8(0x3d), 0x15}, {CCI_REG8(0x4b), 0x06}, {CCI_REG8(0x4c), 0x20}, + /* Gamma */ + {GC2145_REG_PAGE_SELECT, 0x02}, + {CCI_REG8(0x10), 0x09}, {CCI_REG8(0x11), 0x0d}, {CCI_REG8(0x12), 0x13}, + {CCI_REG8(0x13), 0x19}, {CCI_REG8(0x14), 0x27}, {CCI_REG8(0x15), 0x37}, + {CCI_REG8(0x16), 0x45}, {CCI_REG8(0x17), 0x53}, {CCI_REG8(0x18), 0x69}, + {CCI_REG8(0x19), 0x7d}, {CCI_REG8(0x1a), 0x8f}, {CCI_REG8(0x1b), 0x9d}, + {CCI_REG8(0x1c), 0xa9}, {CCI_REG8(0x1d), 0xbd}, {CCI_REG8(0x1e), 0xcd}, + {CCI_REG8(0x1f), 0xd9}, {CCI_REG8(0x20), 0xe3}, {CCI_REG8(0x21), 0xea}, + {CCI_REG8(0x22), 0xef}, {CCI_REG8(0x23), 0xf5}, {CCI_REG8(0x24), 0xf9}, + {CCI_REG8(0x25), 0xff}, + {GC2145_REG_PAGE_SELECT, 0x00}, + {CCI_REG8(0xc6), 0x20}, {CCI_REG8(0xc7), 0x2b}, + /* Gamma 2 */ + {GC2145_REG_PAGE_SELECT, 0x02}, + {CCI_REG8(0x26), 0x0f}, {CCI_REG8(0x27), 0x14}, {CCI_REG8(0x28), 0x19}, + {CCI_REG8(0x29), 0x1e}, {CCI_REG8(0x2a), 0x27}, {CCI_REG8(0x2b), 0x33}, + {CCI_REG8(0x2c), 0x3b}, {CCI_REG8(0x2d), 0x45}, {CCI_REG8(0x2e), 0x59}, + {CCI_REG8(0x2f), 0x69}, {CCI_REG8(0x30), 0x7c}, {CCI_REG8(0x31), 0x89}, + {CCI_REG8(0x32), 0x98}, {CCI_REG8(0x33), 0xae}, {CCI_REG8(0x34), 0xc0}, + {CCI_REG8(0x35), 0xcf}, {CCI_REG8(0x36), 0xda}, {CCI_REG8(0x37), 0xe2}, + {CCI_REG8(0x38), 0xe9}, {CCI_REG8(0x39), 0xf3}, {CCI_REG8(0x3a), 0xf9}, + {CCI_REG8(0x3b), 0xff}, + /* YCP */ + {GC2145_REG_PAGE_SELECT, 0x02}, + {CCI_REG8(0xd1), 0x32}, {CCI_REG8(0xd2), 0x32}, {CCI_REG8(0xd3), 0x40}, + {CCI_REG8(0xd6), 0xf0}, {CCI_REG8(0xd7), 0x10}, {CCI_REG8(0xd8), 0xda}, + {CCI_REG8(0xdd), 0x14}, {CCI_REG8(0xde), 0x86}, {CCI_REG8(0xed), 0x80}, + {CCI_REG8(0xee), 0x00}, {CCI_REG8(0xef), 0x3f}, {CCI_REG8(0xd8), 0xd8}, + /* ABS */ + {GC2145_REG_PAGE_SELECT, 0x01}, + {CCI_REG8(0x9f), 0x40}, + /* LSC */ + {GC2145_REG_PAGE_SELECT, 0x01}, + {CCI_REG8(0xc2), 0x14}, {CCI_REG8(0xc3), 0x0d}, {CCI_REG8(0xc4), 0x0c}, + {CCI_REG8(0xc8), 0x15}, {CCI_REG8(0xc9), 0x0d}, {CCI_REG8(0xca), 0x0a}, + {CCI_REG8(0xbc), 0x24}, {CCI_REG8(0xbd), 0x10}, {CCI_REG8(0xbe), 0x0b}, + {CCI_REG8(0xb6), 0x25}, {CCI_REG8(0xb7), 0x16}, {CCI_REG8(0xb8), 0x15}, + {CCI_REG8(0xc5), 0x00}, {CCI_REG8(0xc6), 0x00}, {CCI_REG8(0xc7), 0x00}, + {CCI_REG8(0xcb), 0x00}, {CCI_REG8(0xcc), 0x00}, {CCI_REG8(0xcd), 0x00}, + {CCI_REG8(0xbf), 0x07}, {CCI_REG8(0xc0), 0x00}, {CCI_REG8(0xc1), 0x00}, + {CCI_REG8(0xb9), 0x00}, {CCI_REG8(0xba), 0x00}, {CCI_REG8(0xbb), 0x00}, + {CCI_REG8(0xaa), 0x01}, {CCI_REG8(0xab), 0x01}, {CCI_REG8(0xac), 0x00}, + {CCI_REG8(0xad), 0x05}, {CCI_REG8(0xae), 0x06}, {CCI_REG8(0xaf), 0x0e}, + {CCI_REG8(0xb0), 0x0b}, {CCI_REG8(0xb1), 0x07}, {CCI_REG8(0xb2), 0x06}, + {CCI_REG8(0xb3), 0x17}, {CCI_REG8(0xb4), 0x0e}, {CCI_REG8(0xb5), 0x0e}, + {CCI_REG8(0xd0), 0x09}, {CCI_REG8(0xd1), 0x00}, {CCI_REG8(0xd2), 0x00}, + {CCI_REG8(0xd6), 0x08}, {CCI_REG8(0xd7), 0x00}, {CCI_REG8(0xd8), 0x00}, + {CCI_REG8(0xd9), 0x00}, {CCI_REG8(0xda), 0x00}, {CCI_REG8(0xdb), 0x00}, + {CCI_REG8(0xd3), 0x0a}, {CCI_REG8(0xd4), 0x00}, {CCI_REG8(0xd5), 0x00}, + {CCI_REG8(0xa4), 0x00}, {CCI_REG8(0xa5), 0x00}, {CCI_REG8(0xa6), 0x77}, + {CCI_REG8(0xa7), 0x77}, {CCI_REG8(0xa8), 0x77}, {CCI_REG8(0xa9), 0x77}, + {CCI_REG8(0xa1), 0x80}, {CCI_REG8(0xa2), 0x80}, + {GC2145_REG_PAGE_SELECT, 0x01}, + {CCI_REG8(0xdf), 0x0d}, {CCI_REG8(0xdc), 0x25}, {CCI_REG8(0xdd), 0x30}, + {CCI_REG8(0xe0), 0x77}, {CCI_REG8(0xe1), 0x80}, {CCI_REG8(0xe2), 0x77}, + {CCI_REG8(0xe3), 0x90}, {CCI_REG8(0xe6), 0x90}, {CCI_REG8(0xe7), 0xa0}, + {CCI_REG8(0xe8), 0x90}, {CCI_REG8(0xe9), 0xa0}, + /* AWB */ + /* measure window */ + {GC2145_REG_PAGE_SELECT, 0x00}, + {CCI_REG8(0xec), 0x06}, {CCI_REG8(0xed), 0x04}, {CCI_REG8(0xee), 0x60}, + {CCI_REG8(0xef), 0x90}, {CCI_REG8(0xb6), 0x01}, + {GC2145_REG_PAGE_SELECT, 0x01}, + {CCI_REG8(0x4f), 0x00}, {CCI_REG8(0x4f), 0x00}, {CCI_REG8(0x4b), 0x01}, + {CCI_REG8(0x4f), 0x00}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x71}, {CCI_REG8(0x4e), 0x01}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x91}, {CCI_REG8(0x4e), 0x01}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x70}, {CCI_REG8(0x4e), 0x01}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x90}, {CCI_REG8(0x4e), 0x02}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xb0}, {CCI_REG8(0x4e), 0x02}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x8f}, {CCI_REG8(0x4e), 0x02}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x6f}, {CCI_REG8(0x4e), 0x02}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xaf}, {CCI_REG8(0x4e), 0x02}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xd0}, {CCI_REG8(0x4e), 0x02}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xf0}, {CCI_REG8(0x4e), 0x02}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xcf}, {CCI_REG8(0x4e), 0x02}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xef}, {CCI_REG8(0x4e), 0x02}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x6e}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x8e}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xae}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xce}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x4d}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x6d}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x8d}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xad}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xcd}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x4c}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x6c}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x8c}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xac}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xcc}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xcb}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x4b}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x6b}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x8b}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xab}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x8a}, {CCI_REG8(0x4e), 0x04}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xaa}, {CCI_REG8(0x4e), 0x04}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xca}, {CCI_REG8(0x4e), 0x04}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xca}, {CCI_REG8(0x4e), 0x04}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xc9}, {CCI_REG8(0x4e), 0x04}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x8a}, {CCI_REG8(0x4e), 0x04}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x89}, {CCI_REG8(0x4e), 0x04}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xa9}, {CCI_REG8(0x4e), 0x04}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x0b}, {CCI_REG8(0x4e), 0x05}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x0a}, {CCI_REG8(0x4e), 0x05}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xeb}, {CCI_REG8(0x4e), 0x05}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xea}, {CCI_REG8(0x4e), 0x05}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x09}, {CCI_REG8(0x4e), 0x05}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x29}, {CCI_REG8(0x4e), 0x05}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x2a}, {CCI_REG8(0x4e), 0x05}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x4a}, {CCI_REG8(0x4e), 0x05}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x8a}, {CCI_REG8(0x4e), 0x06}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x49}, {CCI_REG8(0x4e), 0x06}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x69}, {CCI_REG8(0x4e), 0x06}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x89}, {CCI_REG8(0x4e), 0x06}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xa9}, {CCI_REG8(0x4e), 0x06}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x48}, {CCI_REG8(0x4e), 0x06}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x68}, {CCI_REG8(0x4e), 0x06}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x69}, {CCI_REG8(0x4e), 0x06}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xca}, {CCI_REG8(0x4e), 0x07}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xc9}, {CCI_REG8(0x4e), 0x07}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xe9}, {CCI_REG8(0x4e), 0x07}, + {CCI_REG8(0x4c), 0x03}, {CCI_REG8(0x4d), 0x09}, {CCI_REG8(0x4e), 0x07}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xc8}, {CCI_REG8(0x4e), 0x07}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xe8}, {CCI_REG8(0x4e), 0x07}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xa7}, {CCI_REG8(0x4e), 0x07}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xc7}, {CCI_REG8(0x4e), 0x07}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xe7}, {CCI_REG8(0x4e), 0x07}, + {CCI_REG8(0x4c), 0x03}, {CCI_REG8(0x4d), 0x07}, {CCI_REG8(0x4e), 0x07}, + {CCI_REG8(0x4f), 0x01}, + {CCI_REG8(0x50), 0x80}, {CCI_REG8(0x51), 0xa8}, {CCI_REG8(0x52), 0x47}, + {CCI_REG8(0x53), 0x38}, {CCI_REG8(0x54), 0xc7}, {CCI_REG8(0x56), 0x0e}, + {CCI_REG8(0x58), 0x08}, {CCI_REG8(0x5b), 0x00}, {CCI_REG8(0x5c), 0x74}, + {CCI_REG8(0x5d), 0x8b}, {CCI_REG8(0x61), 0xdb}, {CCI_REG8(0x62), 0xb8}, + {CCI_REG8(0x63), 0x86}, {CCI_REG8(0x64), 0xc0}, {CCI_REG8(0x65), 0x04}, + {CCI_REG8(0x67), 0xa8}, {CCI_REG8(0x68), 0xb0}, {CCI_REG8(0x69), 0x00}, + {CCI_REG8(0x6a), 0xa8}, {CCI_REG8(0x6b), 0xb0}, {CCI_REG8(0x6c), 0xaf}, + {CCI_REG8(0x6d), 0x8b}, {CCI_REG8(0x6e), 0x50}, {CCI_REG8(0x6f), 0x18}, + {CCI_REG8(0x73), 0xf0}, {CCI_REG8(0x70), 0x0d}, {CCI_REG8(0x71), 0x60}, + {CCI_REG8(0x72), 0x80}, {CCI_REG8(0x74), 0x01}, {CCI_REG8(0x75), 0x01}, + {CCI_REG8(0x7f), 0x0c}, {CCI_REG8(0x76), 0x70}, {CCI_REG8(0x77), 0x58}, + {CCI_REG8(0x78), 0xa0}, {CCI_REG8(0x79), 0x5e}, {CCI_REG8(0x7a), 0x54}, + {CCI_REG8(0x7b), 0x58}, + /* CC */ + {GC2145_REG_PAGE_SELECT, 0x02}, + {CCI_REG8(0xc0), 0x01}, {CCI_REG8(0xc1), 0x44}, {CCI_REG8(0xc2), 0xfd}, + {CCI_REG8(0xc3), 0x04}, {CCI_REG8(0xc4), 0xf0}, {CCI_REG8(0xc5), 0x48}, + {CCI_REG8(0xc6), 0xfd}, {CCI_REG8(0xc7), 0x46}, {CCI_REG8(0xc8), 0xfd}, + {CCI_REG8(0xc9), 0x02}, {CCI_REG8(0xca), 0xe0}, {CCI_REG8(0xcb), 0x45}, + {CCI_REG8(0xcc), 0xec}, {CCI_REG8(0xcd), 0x48}, {CCI_REG8(0xce), 0xf0}, + {CCI_REG8(0xcf), 0xf0}, {CCI_REG8(0xe3), 0x0c}, {CCI_REG8(0xe4), 0x4b}, + {CCI_REG8(0xe5), 0xe0}, + /* ABS */ + {GC2145_REG_PAGE_SELECT, 0x01}, + {CCI_REG8(0x9f), 0x40}, + /* Dark sun */ + {GC2145_REG_PAGE_SELECT, 0x02}, + {CCI_REG8(0x40), 0xbf}, {CCI_REG8(0x46), 0xcf}, +}; + +#define GC2145_640_480_PIXELRATE 30000000 +#define GC2145_640_480_LINKFREQ 120000000 +#define GC2145_640_480_HBLANK 0x0130 +#define GC2145_640_480_VBLANK 0x000c +static const struct cci_reg_sequence gc2145_mode_640_480_regs[] = { + {GC2145_REG_PAGE_SELECT, 0xf0}, {GC2145_REG_PAGE_SELECT, 0xf0}, + {GC2145_REG_PAGE_SELECT, 0xf0}, {CCI_REG8(0xfc), 0x06}, + {CCI_REG8(0xf6), 0x00}, {CCI_REG8(0xf7), 0x1d}, {CCI_REG8(0xf8), 0x86}, + {CCI_REG8(0xfa), 0x00}, {CCI_REG8(0xf9), 0x8e}, + /* Disable PAD IO */ + {GC2145_REG_PAD_IO, 0x00}, + {GC2145_REG_PAGE_SELECT, 0x00}, + /* Row/Col start - 0/0 */ + {GC2145_REG_ROW_START, 0x0000}, + {GC2145_REG_COL_START, 0x0000}, + /* Window size 1216/1618 */ + {GC2145_REG_WIN_HEIGHT, 0x04c0}, + {GC2145_REG_WIN_WIDTH, 0x0652}, + /* Scalar more */ + {CCI_REG8(0xfd), 0x01}, {CCI_REG8(0xfa), 0x00}, + /* Crop 640-480@0-0 */ + {GC2145_REG_CROP_ENABLE, 0x01}, + {GC2145_REG_CROP_Y, 0x0000}, + {GC2145_REG_CROP_X, 0x0000}, + {GC2145_REG_CROP_HEIGHT, 0x01e0}, + {GC2145_REG_CROP_WIDTH, 0x0280}, + /* Subsampling configuration */ + {CCI_REG8(0x99), 0x55}, {CCI_REG8(0x9a), 0x06}, {CCI_REG8(0x9b), 0x01}, + {CCI_REG8(0x9c), 0x23}, {CCI_REG8(0x9d), 0x00}, {CCI_REG8(0x9e), 0x00}, + {CCI_REG8(0x9f), 0x01}, {CCI_REG8(0xa0), 0x23}, {CCI_REG8(0xa1), 0x00}, + {CCI_REG8(0xa2), 0x00}, + {GC2145_REG_PAGE_SELECT, 0x01}, + /* AEC anti-flicker */ + {CCI_REG16(0x25), 0x0175}, + /* AEC exposure level 1-5 */ + {CCI_REG16(0x27), 0x045f}, {CCI_REG16(0x29), 0x045f}, + {CCI_REG16(0x2b), 0x045f}, {CCI_REG16(0x2d), 0x045f}, +}; + +#define GC2145_1280_720_PIXELRATE 48000000 +#define GC2145_1280_720_LINKFREQ 192000000 +#define GC2145_1280_720_HBLANK 0x0156 +#define GC2145_1280_720_VBLANK 0x0011 +static const struct cci_reg_sequence gc2145_mode_1280_720_regs[] = { + {GC2145_REG_PAGE_SELECT, 0xf0}, {GC2145_REG_PAGE_SELECT, 0xf0}, + {GC2145_REG_PAGE_SELECT, 0xf0}, {CCI_REG8(0xfc), 0x06}, + {CCI_REG8(0xf6), 0x00}, {CCI_REG8(0xf7), 0x1d}, {CCI_REG8(0xf8), 0x83}, + {CCI_REG8(0xfa), 0x00}, {CCI_REG8(0xf9), 0x8e}, + /* Disable PAD IO */ + {GC2145_REG_PAD_IO, 0x00}, + {GC2145_REG_PAGE_SELECT, 0x00}, + /* Row/Col start - 240/160 */ + {GC2145_REG_ROW_START, 0x00f0}, + {GC2145_REG_COL_START, 0x00a0}, + /* Window size 736/1296 */ + {GC2145_REG_WIN_HEIGHT, 0x02e0}, + {GC2145_REG_WIN_WIDTH, 0x0510}, + /* Crop 1280-720@0-0 */ + {GC2145_REG_CROP_ENABLE, 0x01}, + {GC2145_REG_CROP_Y, 0x0000}, + {GC2145_REG_CROP_X, 0x0000}, + {GC2145_REG_CROP_HEIGHT, 0x02d0}, + {GC2145_REG_CROP_WIDTH, 0x0500}, + {GC2145_REG_PAGE_SELECT, 0x01}, + /* AEC anti-flicker */ + {CCI_REG16(0x25), 0x00e6}, + /* AEC exposure level 1-5 */ + {CCI_REG16(0x27), 0x02b2}, {CCI_REG16(0x29), 0x02b2}, + {CCI_REG16(0x2b), 0x02b2}, {CCI_REG16(0x2d), 0x02b2}, +}; + +#define GC2145_1600_1200_PIXELRATE 60000000 +#define GC2145_1600_1200_LINKFREQ 240000000 +#define GC2145_1600_1200_HBLANK 0x0156 +#define GC2145_1600_1200_VBLANK 0x0010 +static const struct cci_reg_sequence gc2145_mode_1600_1200_regs[] = { + {GC2145_REG_PAGE_SELECT, 0xf0}, {GC2145_REG_PAGE_SELECT, 0xf0}, + {GC2145_REG_PAGE_SELECT, 0xf0}, {CCI_REG8(0xfc), 0x06}, + {CCI_REG8(0xf6), 0x00}, {CCI_REG8(0xf7), 0x1d}, {CCI_REG8(0xf8), 0x84}, + {CCI_REG8(0xfa), 0x00}, {CCI_REG8(0xf9), 0x8e}, + /* Disable PAD IO */ + {GC2145_REG_PAD_IO, 0x00}, + {GC2145_REG_PAGE_SELECT, 0x00}, + /* Row/Col start - 0/0 */ + {GC2145_REG_ROW_START, 0x0000}, + {GC2145_REG_COL_START, 0x0000}, + /* Window size: 1216/1618 */ + {GC2145_REG_WIN_HEIGHT, 0x04c0}, + {GC2145_REG_WIN_WIDTH, 0x0652}, + /* Crop 1600-1200@0-0 */ + {GC2145_REG_CROP_ENABLE, 0x01}, + {GC2145_REG_CROP_Y, 0x0000}, + {GC2145_REG_CROP_X, 0x0000}, + {GC2145_REG_CROP_HEIGHT, 0x04b0}, + {GC2145_REG_CROP_WIDTH, 0x0640}, + {GC2145_REG_PAGE_SELECT, 0x01}, + /* AEC anti-flicker */ + {CCI_REG16(0x25), 0x00fa}, + /* AEC exposure level 1-5 */ + {CCI_REG16(0x27), 0x04e2}, {CCI_REG16(0x29), 0x04e2}, + {CCI_REG16(0x2b), 0x04e2}, {CCI_REG16(0x2d), 0x04e2}, +}; + +static const s64 gc2145_link_freq_menu[] = { + GC2145_640_480_LINKFREQ, + GC2145_1280_720_LINKFREQ, + GC2145_1600_1200_LINKFREQ, +}; + +/* Regulators supplies */ +static const char * const gc2145_supply_name[] = { + "iovdd", /* Digital I/O (1.7-3V) suppply */ + "avdd", /* Analog (2.7-3V) supply */ + "dvdd", /* Digital Core (1.7-1.9V) supply */ +}; + +#define GC2145_NUM_SUPPLIES ARRAY_SIZE(gc2145_supply_name) + +/* Mode configs */ +#define GC2145_MODE_640X480 0 +#define GC2145_MODE_1280X720 1 +#define GC2145_MODE_1600X1200 2 +static const struct gc2145_mode supported_modes[] = { + { + /* 640x480 30fps mode */ + .width = 640, + .height = 480, + .reg_seq = gc2145_mode_640_480_regs, + .reg_seq_size = ARRAY_SIZE(gc2145_mode_640_480_regs), + .pixel_rate = GC2145_640_480_PIXELRATE, + .crop = { + .top = 0, + .left = 0, + .width = 640, + .height = 480, + }, + .hblank = GC2145_640_480_HBLANK, + .vblank = GC2145_640_480_VBLANK, + .link_freq_index = GC2145_MODE_640X480, + }, + { + /* 1280x720 30fps mode */ + .width = 1280, + .height = 720, + .reg_seq = gc2145_mode_1280_720_regs, + .reg_seq_size = ARRAY_SIZE(gc2145_mode_1280_720_regs), + .pixel_rate = GC2145_1280_720_PIXELRATE, + .crop = { + .top = 160, + .left = 240, + .width = 1280, + .height = 720, + }, + .hblank = GC2145_1280_720_HBLANK, + .vblank = GC2145_1280_720_VBLANK, + .link_freq_index = GC2145_MODE_1280X720, + }, + { + /* 1600x1200 20fps mode */ + .width = 1600, + .height = 1200, + .reg_seq = gc2145_mode_1600_1200_regs, + .reg_seq_size = ARRAY_SIZE(gc2145_mode_1600_1200_regs), + .pixel_rate = GC2145_1600_1200_PIXELRATE, + .crop = { + .top = 0, + .left = 0, + .width = 1600, + .height = 1200, + }, + .hblank = GC2145_1600_1200_HBLANK, + .vblank = GC2145_1600_1200_VBLANK, + .link_freq_index = GC2145_MODE_1600X1200, + }, +}; + +/** + * struct gc2145_format - GC2145 pixel format description + * @code: media bus (MBUS) associated code + * @datatype: MIPI CSI2 data type + * @output_fmt: GC2145 output format + * @switch_bit: GC2145 first/second switch + */ +struct gc2145_format { + unsigned int code; + unsigned char datatype; + unsigned char output_fmt; + bool switch_bit; +}; + +/* All supported formats */ +static const struct gc2145_format supported_formats[] = { + { + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .datatype = MIPI_CSI2_DT_YUV422_8B, + .output_fmt = 0x00, + }, + { + .code = MEDIA_BUS_FMT_VYUY8_1X16, + .datatype = MIPI_CSI2_DT_YUV422_8B, + .output_fmt = 0x01, + }, + { + .code = MEDIA_BUS_FMT_YUYV8_1X16, + .datatype = MIPI_CSI2_DT_YUV422_8B, + .output_fmt = 0x02, + }, + { + .code = MEDIA_BUS_FMT_YVYU8_1X16, + .datatype = MIPI_CSI2_DT_YUV422_8B, + .output_fmt = 0x03, + }, + { + .code = MEDIA_BUS_FMT_RGB565_1X16, + .datatype = MIPI_CSI2_DT_RGB565, + .output_fmt = 0x06, + .switch_bit = true, + }, +}; + +struct gc2145_ctrls { + struct v4l2_ctrl_handler handler; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *test_pattern; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; +}; + +struct gc2145 { + struct v4l2_subdev sd; + struct media_pad pad; + + struct regmap *regmap; + struct clk *xclk; + + struct gpio_desc *reset_gpio; + struct gpio_desc *powerdown_gpio; + struct regulator_bulk_data supplies[GC2145_NUM_SUPPLIES]; + + /* V4L2 controls */ + struct gc2145_ctrls ctrls; + + /* Current mode */ + const struct gc2145_mode *mode; +}; + +static inline struct gc2145 *to_gc2145(struct v4l2_subdev *_sd) +{ + return container_of(_sd, struct gc2145, sd); +} + +static inline struct v4l2_subdev *gc2145_ctrl_to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct gc2145, + ctrls.handler)->sd; +} + +static const struct gc2145_format * +gc2145_get_format_code(struct gc2145 *gc2145, u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(supported_formats); i++) { + if (supported_formats[i].code == code) + break; + } + + if (i >= ARRAY_SIZE(supported_formats)) + i = 0; + + return &supported_formats[i]; +} + +static void gc2145_update_pad_format(struct gc2145 *gc2145, + const struct gc2145_mode *mode, + struct v4l2_mbus_framefmt *fmt, u32 code) +{ + fmt->code = code; + fmt->width = mode->width; + fmt->height = mode->height; + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + fmt->quantization = V4L2_QUANTIZATION_DEFAULT; + fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; +} + +static int gc2145_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct gc2145 *gc2145 = to_gc2145(sd); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + + /* Initialize pad format */ + format = v4l2_subdev_state_get_format(state, 0); + gc2145_update_pad_format(gc2145, &supported_modes[0], format, + MEDIA_BUS_FMT_RGB565_1X16); + + /* Initialize crop rectangle. */ + crop = v4l2_subdev_state_get_crop(state, 0); + *crop = supported_modes[0].crop; + + return 0; +} + +static int gc2145_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(sd_state, 0); + return 0; + + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = GC2145_NATIVE_WIDTH; + sel->r.height = GC2145_NATIVE_HEIGHT; + + return 0; + + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = 1600; + sel->r.height = 1200; + + return 0; + } + + return -EINVAL; +} + +static int gc2145_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(supported_formats)) + return -EINVAL; + + code->code = supported_formats[code->index].code; + return 0; +} + +static int gc2145_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct gc2145 *gc2145 = to_gc2145(sd); + const struct gc2145_format *gc2145_format; + u32 code; + + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + gc2145_format = gc2145_get_format_code(gc2145, fse->code); + code = gc2145_format->code; + if (fse->code != code) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static int gc2145_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct gc2145 *gc2145 = to_gc2145(sd); + const struct gc2145_mode *mode; + const struct gc2145_format *gc2145_fmt; + struct v4l2_mbus_framefmt *framefmt; + struct gc2145_ctrls *ctrls = &gc2145->ctrls; + struct v4l2_rect *crop; + + gc2145_fmt = gc2145_get_format_code(gc2145, fmt->format.code); + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), + width, height, + fmt->format.width, fmt->format.height); + + gc2145_update_pad_format(gc2145, mode, &fmt->format, gc2145_fmt->code); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + gc2145->mode = mode; + /* Update pixel_rate based on the mode */ + __v4l2_ctrl_s_ctrl_int64(ctrls->pixel_rate, mode->pixel_rate); + /* Update link_freq based on the mode */ + __v4l2_ctrl_s_ctrl(ctrls->link_freq, mode->link_freq_index); + /* Update hblank/vblank based on the mode */ + __v4l2_ctrl_s_ctrl(ctrls->hblank, mode->hblank); + __v4l2_ctrl_s_ctrl(ctrls->vblank, mode->vblank); + } + *framefmt = fmt->format; + crop = v4l2_subdev_state_get_crop(sd_state, fmt->pad); + *crop = mode->crop; + + return 0; +} + +static const struct cci_reg_sequence gc2145_common_mipi_regs[] = { + {GC2145_REG_PAGE_SELECT, 0x03}, + {GC2145_REG_DPHY_ANALOG_MODE1, GC2145_DPHY_MODE_PHY_CLK_EN | + GC2145_DPHY_MODE_PHY_LANE0_EN | + GC2145_DPHY_MODE_PHY_LANE1_EN | + GC2145_DPHY_MODE_PHY_CLK_LANE_P2S_SEL}, + {GC2145_REG_DPHY_ANALOG_MODE2, GC2145_DPHY_CLK_DIFF(2) | + GC2145_DPHY_LANE0_DIFF(2)}, + {GC2145_REG_DPHY_ANALOG_MODE3, GC2145_DPHY_LANE1_DIFF(0) | + GC2145_DPHY_CLK_DELAY}, + {GC2145_REG_FIFO_MODE, GC2145_FIFO_MODE_READ_GATE | + GC2145_FIFO_MODE_MIPI_CLK_MODULE}, + {GC2145_REG_DPHY_MODE, GC2145_DPHY_MODE_TRIGGER_PROG}, + /* Clock & Data lanes timing */ + {GC2145_REG_T_LPX, 0x10}, + {GC2145_REG_T_CLK_HS_PREPARE, 0x04}, {GC2145_REG_T_CLK_ZERO, 0x10}, + {GC2145_REG_T_CLK_PRE, 0x10}, {GC2145_REG_T_CLK_POST, 0x10}, + {GC2145_REG_T_CLK_TRAIL, 0x05}, + {GC2145_REG_T_HS_PREPARE, 0x03}, {GC2145_REG_T_HS_ZERO, 0x0a}, + {GC2145_REG_T_HS_TRAIL, 0x06}, +}; + +static int gc2145_config_mipi_mode(struct gc2145 *gc2145, + const struct gc2145_format *gc2145_format) +{ + u16 lwc, fifo_full_lvl; + int ret = 0; + + /* Common MIPI settings */ + cci_multi_reg_write(gc2145->regmap, gc2145_common_mipi_regs, + ARRAY_SIZE(gc2145_common_mipi_regs), &ret); + + /* + * Adjust the MIPI buffer settings. + * For YUV/RGB, LWC = image width * 2 + * For RAW8, LWC = image width + * For RAW10, LWC = image width * 1.25 + */ + lwc = gc2145->mode->width * 2; + cci_write(gc2145->regmap, GC2145_REG_LWC_HIGH, lwc >> 8, &ret); + cci_write(gc2145->regmap, GC2145_REG_LWC_LOW, lwc & 0xff, &ret); + + /* + * Adjust the MIPI FIFO Full Level + * 640x480 RGB: 0x0190 + * 1280x720 / 1600x1200 (aka no scaler) non RAW: 0x0001 + * 1600x1200 RAW: 0x0190 + */ + if (gc2145->mode->width == 1280 || gc2145->mode->width == 1600) + fifo_full_lvl = 0x0001; + else + fifo_full_lvl = 0x0190; + + cci_write(gc2145->regmap, GC2145_REG_FIFO_FULL_LVL_HIGH, + fifo_full_lvl >> 8, &ret); + cci_write(gc2145->regmap, GC2145_REG_FIFO_FULL_LVL_LOW, + fifo_full_lvl & 0xff, &ret); + + /* + * Set the FIFO gate mode / MIPI wdiv set: + * 0xf1 in case of RAW mode and 0xf0 otherwise + */ + cci_write(gc2145->regmap, GC2145_REG_FIFO_GATE_MODE, 0xf0, &ret); + + /* Set the MIPI data type */ + cci_write(gc2145->regmap, GC2145_REG_MIPI_DT, + gc2145_format->datatype, &ret); + + /* Configure mode and enable CSI */ + cci_write(gc2145->regmap, GC2145_REG_BUF_CSI2_MODE, + GC2145_CSI2_MODE_RAW8 | GC2145_CSI2_MODE_DOUBLE | + GC2145_CSI2_MODE_EN | GC2145_CSI2_MODE_MIPI_EN, &ret); + + return ret; +} + +static int gc2145_start_streaming(struct gc2145 *gc2145, + struct v4l2_subdev_state *state) +{ + struct i2c_client *client = v4l2_get_subdevdata(&gc2145->sd); + const struct gc2145_format *gc2145_format; + struct v4l2_mbus_framefmt *fmt; + int ret; + + ret = pm_runtime_resume_and_get(&client->dev); + if (ret < 0) + return ret; + + /* Apply default values of current mode */ + cci_multi_reg_write(gc2145->regmap, gc2145->mode->reg_seq, + gc2145->mode->reg_seq_size, &ret); + cci_multi_reg_write(gc2145->regmap, gc2145_common_regs, + ARRAY_SIZE(gc2145_common_regs), &ret); + if (ret) { + dev_err(&client->dev, "%s failed to write regs\n", __func__); + goto err_rpm_put; + } + + fmt = v4l2_subdev_state_get_format(state, 0); + gc2145_format = gc2145_get_format_code(gc2145, fmt->code); + + /* Set the output format */ + cci_write(gc2145->regmap, GC2145_REG_PAGE_SELECT, 0x00, &ret); + + cci_write(gc2145->regmap, GC2145_REG_OUTPUT_FMT, + gc2145_format->output_fmt, &ret); + cci_update_bits(gc2145->regmap, GC2145_REG_BYPASS_MODE, + GC2145_BYPASS_MODE_SWITCH, + gc2145_format->switch_bit ? GC2145_BYPASS_MODE_SWITCH + : 0, &ret); + if (ret) { + dev_err(&client->dev, "%s failed to write regs\n", __func__); + goto err_rpm_put; + } + + /* Apply customized values from user */ + ret = __v4l2_ctrl_handler_setup(&gc2145->ctrls.handler); + if (ret) { + dev_err(&client->dev, "%s failed to apply ctrls\n", __func__); + goto err_rpm_put; + } + + /* Perform MIPI specific configuration */ + ret = gc2145_config_mipi_mode(gc2145, gc2145_format); + if (ret) { + dev_err(&client->dev, "%s failed to write mipi conf\n", + __func__); + goto err_rpm_put; + } + + cci_write(gc2145->regmap, GC2145_REG_PAGE_SELECT, 0x00, &ret); + + return 0; + +err_rpm_put: + pm_runtime_mark_last_busy(&client->dev); + pm_runtime_put_autosuspend(&client->dev); + return ret; +} + +static void gc2145_stop_streaming(struct gc2145 *gc2145) +{ + struct i2c_client *client = v4l2_get_subdevdata(&gc2145->sd); + int ret = 0; + + /* Disable lanes & mipi streaming */ + cci_write(gc2145->regmap, GC2145_REG_PAGE_SELECT, 0x03, &ret); + cci_update_bits(gc2145->regmap, GC2145_REG_BUF_CSI2_MODE, + GC2145_CSI2_MODE_EN | GC2145_CSI2_MODE_MIPI_EN, 0, + &ret); + cci_write(gc2145->regmap, GC2145_REG_PAGE_SELECT, 0x00, &ret); + if (ret) + dev_err(&client->dev, "%s failed to write regs\n", __func__); + + pm_runtime_mark_last_busy(&client->dev); + pm_runtime_put_autosuspend(&client->dev); +} + +static int gc2145_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct gc2145 *gc2145 = to_gc2145(sd); + struct v4l2_subdev_state *state; + int ret = 0; + + state = v4l2_subdev_lock_and_get_active_state(sd); + + if (enable) + ret = gc2145_start_streaming(gc2145, state); + else + gc2145_stop_streaming(gc2145); + + v4l2_subdev_unlock_state(state); + + return ret; +} + +/* Power/clock management functions */ +static int gc2145_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct gc2145 *gc2145 = to_gc2145(sd); + int ret; + + ret = regulator_bulk_enable(GC2145_NUM_SUPPLIES, gc2145->supplies); + if (ret) { + dev_err(dev, "failed to enable regulators\n"); + return ret; + } + + ret = clk_prepare_enable(gc2145->xclk); + if (ret) { + dev_err(dev, "failed to enable clock\n"); + goto reg_off; + } + + gpiod_set_value_cansleep(gc2145->powerdown_gpio, 0); + gpiod_set_value_cansleep(gc2145->reset_gpio, 0); + + /* + * Datasheet doesn't mention timing between PWDN/RESETB control and + * i2c access however, experimentation shows that a rather big delay is + * needed. + */ + msleep(41); + + return 0; + +reg_off: + regulator_bulk_disable(GC2145_NUM_SUPPLIES, gc2145->supplies); + + return ret; +} + +static int gc2145_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct gc2145 *gc2145 = to_gc2145(sd); + + gpiod_set_value_cansleep(gc2145->powerdown_gpio, 1); + gpiod_set_value_cansleep(gc2145->reset_gpio, 1); + clk_disable_unprepare(gc2145->xclk); + regulator_bulk_disable(GC2145_NUM_SUPPLIES, gc2145->supplies); + + return 0; +} + +static int gc2145_get_regulators(struct gc2145 *gc2145) +{ + struct i2c_client *client = v4l2_get_subdevdata(&gc2145->sd); + unsigned int i; + + for (i = 0; i < GC2145_NUM_SUPPLIES; i++) + gc2145->supplies[i].supply = gc2145_supply_name[i]; + + return devm_regulator_bulk_get(&client->dev, GC2145_NUM_SUPPLIES, + gc2145->supplies); +} + +/* Verify chip ID */ +static int gc2145_identify_module(struct gc2145 *gc2145) +{ + struct i2c_client *client = v4l2_get_subdevdata(&gc2145->sd); + int ret; + u64 chip_id; + + ret = cci_read(gc2145->regmap, GC2145_REG_CHIP_ID, &chip_id, NULL); + if (ret) { + dev_err(&client->dev, "failed to read chip id (%d)\n", ret); + return ret; + } + + if (chip_id != GC2145_CHIP_ID) { + dev_err(&client->dev, "chip id mismatch: %x!=%llx\n", + GC2145_CHIP_ID, chip_id); + return -EIO; + } + + return 0; +} + +static const char * const test_pattern_menu[] = { + "Disabled", + "Colored patterns", + "Uniform white", + "Uniform yellow", + "Uniform cyan", + "Uniform green", + "Uniform magenta", + "Uniform red", + "Uniform black", +}; + +#define GC2145_TEST_PATTERN_ENABLE BIT(0) +#define GC2145_TEST_PATTERN_UXGA BIT(3) + +#define GC2145_TEST_UNIFORM BIT(3) +#define GC2145_TEST_WHITE (4 << 4) +#define GC2145_TEST_YELLOW (8 << 4) +#define GC2145_TEST_CYAN (9 << 4) +#define GC2145_TEST_GREEN (6 << 4) +#define GC2145_TEST_MAGENTA (10 << 4) +#define GC2145_TEST_RED (5 << 4) +#define GC2145_TEST_BLACK (0) + +static const u8 test_pattern_val[] = { + 0, + GC2145_TEST_PATTERN_ENABLE, + GC2145_TEST_UNIFORM | GC2145_TEST_WHITE, + GC2145_TEST_UNIFORM | GC2145_TEST_YELLOW, + GC2145_TEST_UNIFORM | GC2145_TEST_CYAN, + GC2145_TEST_UNIFORM | GC2145_TEST_GREEN, + GC2145_TEST_UNIFORM | GC2145_TEST_MAGENTA, + GC2145_TEST_UNIFORM | GC2145_TEST_RED, + GC2145_TEST_UNIFORM | GC2145_TEST_BLACK, +}; + +static const struct v4l2_subdev_core_ops gc2145_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_video_ops gc2145_video_ops = { + .s_stream = gc2145_set_stream, +}; + +static const struct v4l2_subdev_pad_ops gc2145_pad_ops = { + .enum_mbus_code = gc2145_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = gc2145_set_pad_format, + .get_selection = gc2145_get_selection, + .enum_frame_size = gc2145_enum_frame_size, +}; + +static const struct v4l2_subdev_ops gc2145_subdev_ops = { + .core = &gc2145_core_ops, + .video = &gc2145_video_ops, + .pad = &gc2145_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops gc2145_subdev_internal_ops = { + .init_state = gc2145_init_state, +}; + +static int gc2145_set_ctrl_test_pattern(struct gc2145 *gc2145, int value) +{ + int ret = 0; + + if (!value) { + /* Disable test pattern */ + cci_write(gc2145->regmap, GC2145_REG_DEBUG_MODE2, 0, &ret); + return cci_write(gc2145->regmap, GC2145_REG_DEBUG_MODE3, 0, + &ret); + } + + /* Enable test pattern, colored or uniform */ + cci_write(gc2145->regmap, GC2145_REG_DEBUG_MODE2, + GC2145_TEST_PATTERN_ENABLE | GC2145_TEST_PATTERN_UXGA, &ret); + + if (!(test_pattern_val[value] & GC2145_TEST_UNIFORM)) + return cci_write(gc2145->regmap, GC2145_REG_DEBUG_MODE3, 0, + &ret); + + /* Uniform */ + return cci_write(gc2145->regmap, GC2145_REG_DEBUG_MODE3, + test_pattern_val[value], &ret); +} + +static int gc2145_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = gc2145_ctrl_to_sd(ctrl); + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct gc2145 *gc2145 = to_gc2145(sd); + int ret; + + if (pm_runtime_get_if_in_use(&client->dev) == 0) + return 0; + + switch (ctrl->id) { + case V4L2_CID_HBLANK: + ret = cci_write(gc2145->regmap, GC2145_REG_HBLANK, ctrl->val, + NULL); + break; + case V4L2_CID_VBLANK: + ret = cci_write(gc2145->regmap, GC2145_REG_VBLANK, ctrl->val, + NULL); + break; + case V4L2_CID_TEST_PATTERN: + ret = gc2145_set_ctrl_test_pattern(gc2145, ctrl->val); + break; + case V4L2_CID_HFLIP: + ret = cci_update_bits(gc2145->regmap, GC2145_REG_ANALOG_MODE1, + BIT(0), (ctrl->val ? BIT(0) : 0), NULL); + break; + case V4L2_CID_VFLIP: + ret = cci_update_bits(gc2145->regmap, GC2145_REG_ANALOG_MODE1, + BIT(1), (ctrl->val ? BIT(1) : 0), NULL); + break; + default: + ret = -EINVAL; + break; + } + + pm_runtime_mark_last_busy(&client->dev); + pm_runtime_put_autosuspend(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops gc2145_ctrl_ops = { + .s_ctrl = gc2145_s_ctrl, +}; + +/* Initialize control handlers */ +static int gc2145_init_controls(struct gc2145 *gc2145) +{ + struct i2c_client *client = v4l2_get_subdevdata(&gc2145->sd); + const struct v4l2_ctrl_ops *ops = &gc2145_ctrl_ops; + struct gc2145_ctrls *ctrls = &gc2145->ctrls; + struct v4l2_ctrl_handler *hdl = &ctrls->handler; + struct v4l2_fwnode_device_properties props; + int ret; + + ret = v4l2_ctrl_handler_init(hdl, 12); + if (ret) + return ret; + + ctrls->pixel_rate = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_PIXEL_RATE, + GC2145_640_480_PIXELRATE, + GC2145_1600_1200_PIXELRATE, 1, + supported_modes[0].pixel_rate); + + ctrls->link_freq = v4l2_ctrl_new_int_menu(hdl, ops, V4L2_CID_LINK_FREQ, + ARRAY_SIZE(gc2145_link_freq_menu) - 1, + 0, gc2145_link_freq_menu); + if (ctrls->link_freq) + ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + ctrls->hblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK, + 0, 0xfff, 1, GC2145_640_480_HBLANK); + + ctrls->vblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK, + 0, 0x1fff, 1, GC2145_640_480_VBLANK); + + ctrls->test_pattern = + v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(test_pattern_menu) - 1, + 0, 0, test_pattern_menu); + ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, + 0, 1, 1, 0); + ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, + 0, 1, 1, 0); + + if (hdl->error) { + ret = hdl->error; + dev_err(&client->dev, "control init failed (%d)\n", ret); + goto error; + } + + ret = v4l2_fwnode_device_parse(&client->dev, &props); + if (ret) + goto error; + + ret = v4l2_ctrl_new_fwnode_properties(hdl, &gc2145_ctrl_ops, + &props); + if (ret) + goto error; + + gc2145->sd.ctrl_handler = hdl; + + return 0; + +error: + v4l2_ctrl_handler_free(hdl); + + return ret; +} + +static int gc2145_check_hwcfg(struct device *dev) +{ + struct fwnode_handle *endpoint; + struct v4l2_fwnode_endpoint ep_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + int ret; + + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); + if (!endpoint) { + dev_err(dev, "endpoint node not found\n"); + return -EINVAL; + } + + ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg); + fwnode_handle_put(endpoint); + if (ret) + return ret; + + /* Check the number of MIPI CSI2 data lanes */ + if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2) { + dev_err(dev, "only 2 data lanes are currently supported\n"); + ret = -EINVAL; + goto out; + } + + /* Check the link frequency set in device tree */ + if (!ep_cfg.nr_of_link_frequencies) { + dev_err(dev, "link-frequency property not found in DT\n"); + ret = -EINVAL; + goto out; + } + + if (ep_cfg.nr_of_link_frequencies != 3 || + ep_cfg.link_frequencies[0] != GC2145_640_480_LINKFREQ || + ep_cfg.link_frequencies[1] != GC2145_1280_720_LINKFREQ || + ep_cfg.link_frequencies[2] != GC2145_1600_1200_LINKFREQ) { + dev_err(dev, "Invalid link-frequencies provided\n"); + ret = -EINVAL; + } + +out: + v4l2_fwnode_endpoint_free(&ep_cfg); + + return ret; +} + +static int gc2145_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + unsigned int xclk_freq; + struct gc2145 *gc2145; + int ret; + + gc2145 = devm_kzalloc(&client->dev, sizeof(*gc2145), GFP_KERNEL); + if (!gc2145) + return -ENOMEM; + + v4l2_i2c_subdev_init(&gc2145->sd, client, &gc2145_subdev_ops); + gc2145->sd.internal_ops = &gc2145_subdev_internal_ops; + + /* Check the hardware configuration in device tree */ + if (gc2145_check_hwcfg(dev)) + return -EINVAL; + + /* Get system clock (xclk) */ + gc2145->xclk = devm_clk_get(dev, NULL); + if (IS_ERR(gc2145->xclk)) + return dev_err_probe(dev, PTR_ERR(gc2145->xclk), + "failed to get xclk\n"); + + xclk_freq = clk_get_rate(gc2145->xclk); + if (xclk_freq != GC2145_XCLK_FREQ) { + dev_err(dev, "xclk frequency not supported: %d Hz\n", + xclk_freq); + return -EINVAL; + } + + ret = gc2145_get_regulators(gc2145); + if (ret) + return dev_err_probe(dev, ret, + "failed to get regulators\n"); + + /* Request optional reset pin */ + gc2145->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(gc2145->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(gc2145->reset_gpio), + "failed to get reset_gpio\n"); + + /* Request optional powerdown pin */ + gc2145->powerdown_gpio = devm_gpiod_get_optional(dev, "powerdown", + GPIOD_OUT_HIGH); + if (IS_ERR(gc2145->powerdown_gpio)) + return dev_err_probe(dev, PTR_ERR(gc2145->powerdown_gpio), + "failed to get powerdown_gpio\n"); + + /* Initialise the regmap for further cci access */ + gc2145->regmap = devm_cci_regmap_init_i2c(client, 8); + if (IS_ERR(gc2145->regmap)) + return dev_err_probe(dev, PTR_ERR(gc2145->regmap), + "failed to get cci regmap\n"); + + /* + * The sensor must be powered for gc2145_identify_module() + * to be able to read the CHIP_ID register + */ + ret = gc2145_power_on(dev); + if (ret) + return ret; + + ret = gc2145_identify_module(gc2145); + if (ret) + goto error_power_off; + + /* Set default mode */ + gc2145->mode = &supported_modes[0]; + + ret = gc2145_init_controls(gc2145); + if (ret) + goto error_power_off; + + /* Initialize subdev */ + gc2145->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; + gc2145->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + /* Initialize source pad */ + gc2145->pad.flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&gc2145->sd.entity, 1, &gc2145->pad); + if (ret) { + dev_err(dev, "failed to init entity pads: %d\n", ret); + goto error_handler_free; + } + + gc2145->sd.state_lock = gc2145->ctrls.handler.lock; + ret = v4l2_subdev_init_finalize(&gc2145->sd); + if (ret < 0) { + dev_err(dev, "subdev init error: %d\n", ret); + goto error_media_entity; + } + + /* Enable runtime PM and turn off the device */ + pm_runtime_set_active(dev); + pm_runtime_get_noresume(&client->dev); + pm_runtime_enable(dev); + + pm_runtime_set_autosuspend_delay(&client->dev, 1000); + pm_runtime_use_autosuspend(&client->dev); + pm_runtime_put_autosuspend(&client->dev); + + ret = v4l2_async_register_subdev_sensor(&gc2145->sd); + if (ret < 0) { + dev_err(dev, "failed to register sensor sub-device: %d\n", ret); + goto error_subdev_cleanup; + } + + return 0; + +error_subdev_cleanup: + v4l2_subdev_cleanup(&gc2145->sd); + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + +error_media_entity: + media_entity_cleanup(&gc2145->sd.entity); + +error_handler_free: + v4l2_ctrl_handler_free(&gc2145->ctrls.handler); + +error_power_off: + gc2145_power_off(dev); + + return ret; +} + +static void gc2145_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc2145 *gc2145 = to_gc2145(sd); + + v4l2_subdev_cleanup(sd); + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(&gc2145->ctrls.handler); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + gc2145_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); +} + +static const struct of_device_id gc2145_dt_ids[] = { + { .compatible = "galaxycore,gc2145" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, gc2145_dt_ids); + +static const struct dev_pm_ops gc2145_pm_ops = { + RUNTIME_PM_OPS(gc2145_power_off, gc2145_power_on, NULL) +}; + +static struct i2c_driver gc2145_i2c_driver = { + .driver = { + .name = "gc2145", + .of_match_table = gc2145_dt_ids, + .pm = pm_ptr(&gc2145_pm_ops), + }, + .probe = gc2145_probe, + .remove = gc2145_remove, +}; + +module_i2c_driver(gc2145_i2c_driver); + +MODULE_AUTHOR("Alain Volmat "); +MODULE_DESCRIPTION("GalaxyCore GC2145 sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/hi556.c b/drivers/media/i2c/hi556.c index fd56ba13873915..7b71e6def40c41 100644 --- a/drivers/media/i2c/hi556.c +++ b/drivers/media/i2c/hi556.c @@ -876,7 +876,7 @@ __hi556_get_pad_crop(struct hi556 *hi556, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&hi556->sd, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &hi556->cur_mode->crop; } @@ -1058,7 +1058,7 @@ static int hi556_set_format(struct v4l2_subdev *sd, mutex_lock(&hi556->mutex); hi556_assign_pad_format(mode, &fmt->format); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; } else { hi556->cur_mode = mode; __v4l2_ctrl_s_ctrl(hi556->link_freq, mode->link_freq_index); @@ -1092,9 +1092,8 @@ static int hi556_get_format(struct v4l2_subdev *sd, mutex_lock(&hi556->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&hi556->sd, - sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); else hi556_assign_pad_format(hi556->cur_mode, &fmt->format); @@ -1140,10 +1139,10 @@ static int hi556_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) mutex_lock(&hi556->mutex); hi556_assign_pad_format(&supported_modes[0], - v4l2_subdev_get_try_format(sd, fh->state, 0)); + v4l2_subdev_state_get_format(fh->state, 0)); /* Initialize try_crop rectangle. */ - try_crop = v4l2_subdev_get_try_crop(sd, fh->state, 0); + try_crop = v4l2_subdev_state_get_crop(fh->state, 0); try_crop->top = HI556_PIXEL_ARRAY_TOP; try_crop->left = HI556_PIXEL_ARRAY_LEFT; try_crop->width = HI556_PIXEL_ARRAY_WIDTH; diff --git a/drivers/media/i2c/hi846.c b/drivers/media/i2c/hi846.c index fa0038749a3b98..678b30556b0a18 100644 --- a/drivers/media/i2c/hi846.c +++ b/drivers/media/i2c/hi846.c @@ -1731,7 +1731,7 @@ static int hi846_set_format(struct v4l2_subdev *sd, } if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, format->pad) = *mf; + *v4l2_subdev_state_get_format(sd_state, format->pad) = *mf; return 0; } @@ -1809,9 +1809,8 @@ static int hi846_get_format(struct v4l2_subdev *sd, struct i2c_client *client = v4l2_get_subdevdata(sd); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - format->format = *v4l2_subdev_get_try_format(&hi846->sd, - sd_state, - format->pad); + format->format = *v4l2_subdev_state_get_format(sd_state, + format->pad); return 0; } @@ -1878,7 +1877,7 @@ static int hi846_get_selection(struct v4l2_subdev *sd, mutex_lock(&hi846->mutex); switch (sel->which) { case V4L2_SUBDEV_FORMAT_TRY: - v4l2_subdev_get_try_crop(sd, sd_state, sel->pad); + v4l2_subdev_state_get_crop(sd_state, sel->pad); break; case V4L2_SUBDEV_FORMAT_ACTIVE: sel->r = hi846->cur_mode->crop; @@ -1898,13 +1897,13 @@ static int hi846_get_selection(struct v4l2_subdev *sd, } } -static int hi846_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int hi846_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct hi846 *hi846 = to_hi846(sd); struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_get_try_format(sd, sd_state, 0); + mf = v4l2_subdev_state_get_format(sd_state, 0); mutex_lock(&hi846->mutex); mf->code = HI846_MEDIA_BUS_FORMAT; @@ -1922,7 +1921,6 @@ static const struct v4l2_subdev_video_ops hi846_video_ops = { }; static const struct v4l2_subdev_pad_ops hi846_pad_ops = { - .init_cfg = hi846_init_cfg, .enum_frame_size = hi846_enum_frame_size, .enum_mbus_code = hi846_enum_mbus_code, .set_fmt = hi846_set_format, @@ -1935,6 +1933,10 @@ static const struct v4l2_subdev_ops hi846_subdev_ops = { .pad = &hi846_pad_ops, }; +static const struct v4l2_subdev_internal_ops hi846_internal_ops = { + .init_state = hi846_init_state, +}; + static const struct media_entity_operations hi846_subdev_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -2098,6 +2100,7 @@ static int hi846_probe(struct i2c_client *client) return ret; v4l2_i2c_subdev_init(&hi846->sd, client, &hi846_subdev_ops); + hi846->sd.internal_ops = &hi846_internal_ops; mutex_init(&hi846->mutex); diff --git a/drivers/media/i2c/hi847.c b/drivers/media/i2c/hi847.c index 32547d7a2659fa..cda9fce0ee1b6b 100644 --- a/drivers/media/i2c/hi847.c +++ b/drivers/media/i2c/hi847.c @@ -2703,7 +2703,7 @@ static int hi847_set_format(struct v4l2_subdev *sd, mutex_lock(&hi847->mutex); hi847_assign_pad_format(mode, &fmt->format); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; } else { hi847->cur_mode = mode; @@ -2738,9 +2738,8 @@ static int hi847_get_format(struct v4l2_subdev *sd, mutex_lock(&hi847->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&hi847->sd, - sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); else hi847_assign_pad_format(hi847->cur_mode, &fmt->format); @@ -2785,7 +2784,7 @@ static int hi847_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) mutex_lock(&hi847->mutex); hi847_assign_pad_format(&supported_modes[0], - v4l2_subdev_get_try_format(sd, fh->state, 0)); + v4l2_subdev_state_get_format(fh->state, 0)); mutex_unlock(&hi847->mutex); return 0; diff --git a/drivers/media/i2c/imx208.c b/drivers/media/i2c/imx208.c index ee5a2867538844..865799880b0bad 100644 --- a/drivers/media/i2c/imx208.c +++ b/drivers/media/i2c/imx208.c @@ -398,7 +398,7 @@ static int imx208_write_regs(struct imx208 *imx208, static int imx208_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); /* Initialize try_fmt */ try_fmt->width = supported_modes[0].width; @@ -551,9 +551,8 @@ static int __imx208_get_pad_format(struct imx208 *imx208, struct v4l2_subdev_format *fmt) { if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&imx208->sd, - sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); else imx208_mode_to_pad_format(imx208, imx208->cur_mode, fmt); @@ -594,7 +593,7 @@ static int imx208_set_pad_format(struct v4l2_subdev *sd, fmt->format.width, fmt->format.height); imx208_mode_to_pad_format(imx208, mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; } else { imx208->cur_mode = mode; __v4l2_ctrl_s_ctrl(imx208->link_freq, mode->link_freq_index); diff --git a/drivers/media/i2c/imx214.c b/drivers/media/i2c/imx214.c index 2f9c8582f9401a..ea3fb81fdbea96 100644 --- a/drivers/media/i2c/imx214.c +++ b/drivers/media/i2c/imx214.c @@ -540,7 +540,7 @@ __imx214_get_pad_format(struct imx214 *imx214, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&imx214->sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &imx214->fmt; default: @@ -570,7 +570,7 @@ __imx214_get_pad_crop(struct imx214 *imx214, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&imx214->sd, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &imx214->crop; default: @@ -635,8 +635,8 @@ static int imx214_get_selection(struct v4l2_subdev *sd, return 0; } -static int imx214_entity_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state) +static int imx214_entity_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { }; @@ -845,7 +845,6 @@ static const struct v4l2_subdev_pad_ops imx214_subdev_pad_ops = { .get_fmt = imx214_get_format, .set_fmt = imx214_set_format, .get_selection = imx214_get_selection, - .init_cfg = imx214_entity_init_cfg, }; static const struct v4l2_subdev_ops imx214_subdev_ops = { @@ -854,6 +853,10 @@ static const struct v4l2_subdev_ops imx214_subdev_ops = { .pad = &imx214_subdev_pad_ops, }; +static const struct v4l2_subdev_internal_ops imx214_internal_ops = { + .init_state = imx214_entity_init_state, +}; + static const struct regmap_config sensor_regmap_config = { .reg_bits = 16, .val_bits = 8, @@ -996,6 +999,7 @@ static int imx214_probe(struct i2c_client *client) } v4l2_i2c_subdev_init(&imx214->sd, client, &imx214_subdev_ops); + imx214->sd.internal_ops = &imx214_internal_ops; /* * Enable power initially, to avoid warnings @@ -1060,7 +1064,7 @@ static int imx214_probe(struct i2c_client *client) goto free_ctrl; } - imx214_entity_init_cfg(&imx214->sd, NULL); + imx214_entity_init_state(&imx214->sd, NULL); ret = v4l2_async_register_subdev_sensor(&imx214->sd); if (ret < 0) { diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 91854f16c5b96c..2d23ccece489ad 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -18,10 +18,12 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -71,20 +73,10 @@ /* V_TIMING internal */ #define IMX219_REG_VTS CCI_REG16(0x0160) -#define IMX219_VTS_15FPS 0x0dc6 -#define IMX219_VTS_30FPS_1080P 0x06e3 -#define IMX219_VTS_30FPS_BINNED 0x06e3 -#define IMX219_VTS_30FPS_640x480 0x06e3 #define IMX219_VTS_MAX 0xffff #define IMX219_VBLANK_MIN 32 -/*Frame Length Line*/ -#define IMX219_FLL_MIN 0x08a6 -#define IMX219_FLL_MAX 0xffff -#define IMX219_FLL_STEP 1 -#define IMX219_FLL_DEFAULT 0x0c98 - /* HBLANK control range */ #define IMX219_PPL_MIN 3448 #define IMX219_PPL_MAX 0x7ff0 @@ -102,10 +94,11 @@ #define IMX219_REG_ORIENTATION CCI_REG8(0x0172) /* Binning Mode */ -#define IMX219_REG_BINNING_MODE CCI_REG16(0x0174) -#define IMX219_BINNING_NONE 0x0000 -#define IMX219_BINNING_2X2_NORMAL 0x0101 -#define IMX219_BINNING_2X2_SPECIAL 0x0303 +#define IMX219_REG_BINNING_MODE_H CCI_REG8(0x0174) +#define IMX219_REG_BINNING_MODE_V CCI_REG8(0x0175) +#define IMX219_BINNING_NONE 0x00 +#define IMX219_BINNING_X2 0x01 +#define IMX219_BINNING_X2_ANALOG 0x03 #define IMX219_REG_CSI_DATA_FORMAT_A CCI_REG16(0x018c) @@ -135,10 +128,6 @@ #define IMX219_TESTP_COLOUR_MIN 0 #define IMX219_TESTP_COLOUR_MAX 0x03ff #define IMX219_TESTP_COLOUR_STEP 1 -#define IMX219_TESTP_RED_DEFAULT IMX219_TESTP_COLOUR_MAX -#define IMX219_TESTP_GREENR_DEFAULT 0 -#define IMX219_TESTP_BLUE_DEFAULT 0 -#define IMX219_TESTP_GREENB_DEFAULT 0 #define IMX219_REG_TP_WINDOW_WIDTH CCI_REG16(0x0624) #define IMX219_REG_TP_WINDOW_HEIGHT CCI_REG16(0x0626) @@ -154,6 +143,7 @@ #define IMX219_DEFAULT_LINK_FREQ_4LANE 363000000 /* IMX219 native and active pixel array size. */ +#define IMX219_NATIVE_FORMAT MEDIA_BUS_FMT_SRGGB10_1X10 #define IMX219_NATIVE_WIDTH 3296U #define IMX219_NATIVE_HEIGHT 2480U #define IMX219_PIXEL_ARRAY_LEFT 8U @@ -161,20 +151,13 @@ #define IMX219_PIXEL_ARRAY_WIDTH 3280U #define IMX219_PIXEL_ARRAY_HEIGHT 2464U -/* Embedded metadata stream structure */ -#define IMX219_EMBEDDED_LINE_WIDTH 16384 -#define IMX219_NUM_EMBEDDED_LINES 1 - -enum pad_types { - IMAGE_PAD, - METADATA_PAD, - NUM_PADS -}; +/* Embedded metadata stream height */ +#define IMX219_EMBEDDED_DATA_HEIGHT 2U enum binning_mode { BINNING_NONE, - BINNING_NORMAL_2x2, - BINNING_SPECIAL_2x2, + BINNING_X2, + BINNING_ANALOG_X2, }; enum binning_bit_depths { @@ -183,16 +166,6 @@ enum binning_bit_depths { BINNING_IDX_MAX }; -struct imx219_reg { - u16 address; - u8 val; -}; - -struct imx219_reg_list { - unsigned int num_of_regs; - const struct cci_reg_sequence *regs; -}; - /* Mode : resolution and related config&values */ struct imx219_mode { /* Frame width */ @@ -200,15 +173,9 @@ struct imx219_mode { /* Frame height */ unsigned int height; - /* Analog crop rectangle. */ - struct v4l2_rect crop; - /* V-timing */ unsigned int vts_def; - /* Default register values */ - struct imx219_reg_list reg_list; - /* binning mode based on format code */ enum binning_mode binning[BINNING_IDX_MAX]; }; @@ -257,65 +224,6 @@ static const struct cci_reg_sequence imx219_common_regs[] = { { IMX219_REG_EXCK_FREQ, IMX219_EXCK_FREQ(IMX219_XCLK_FREQ / 1000000) }, }; -/* - * Register sets lifted off the i2C interface from the Raspberry Pi firmware - * driver. - * 3280x2464 = mode 2, 1920x1080 = mode 1, 1640x1232 = mode 4, 640x480 = mode 7. - */ -static const struct cci_reg_sequence mode_3280x2464_regs[] = { - { IMX219_REG_X_ADD_STA_A, 0 }, - { IMX219_REG_X_ADD_END_A, 3279 }, - { IMX219_REG_Y_ADD_STA_A, 0 }, - { IMX219_REG_Y_ADD_END_A, 2463 }, - { IMX219_REG_X_OUTPUT_SIZE, 3280 }, - { IMX219_REG_Y_OUTPUT_SIZE, 2464 }, - { IMX219_REG_TP_WINDOW_WIDTH, 3280 }, - { IMX219_REG_TP_WINDOW_HEIGHT, 2464 }, -}; - -static const struct cci_reg_sequence mode_1920_1080_regs[] = { - { IMX219_REG_X_ADD_STA_A, 680 }, - { IMX219_REG_X_ADD_END_A, 2599 }, - { IMX219_REG_Y_ADD_STA_A, 692 }, - { IMX219_REG_Y_ADD_END_A, 1771 }, - { IMX219_REG_X_OUTPUT_SIZE, 1920 }, - { IMX219_REG_Y_OUTPUT_SIZE, 1080 }, - { IMX219_REG_TP_WINDOW_WIDTH, 1920 }, - { IMX219_REG_TP_WINDOW_HEIGHT, 1080 }, -}; - -static const struct cci_reg_sequence mode_1640_1232_regs[] = { - { IMX219_REG_X_ADD_STA_A, 0 }, - { IMX219_REG_X_ADD_END_A, 3279 }, - { IMX219_REG_Y_ADD_STA_A, 0 }, - { IMX219_REG_Y_ADD_END_A, 2463 }, - { IMX219_REG_X_OUTPUT_SIZE, 1640 }, - { IMX219_REG_Y_OUTPUT_SIZE, 1232 }, - { IMX219_REG_TP_WINDOW_WIDTH, 1640 }, - { IMX219_REG_TP_WINDOW_HEIGHT, 1232 }, -}; - -static const struct cci_reg_sequence mode_640_480_regs[] = { - { IMX219_REG_X_ADD_STA_A, 1000 }, - { IMX219_REG_X_ADD_END_A, 2279 }, - { IMX219_REG_Y_ADD_STA_A, 752 }, - { IMX219_REG_Y_ADD_END_A, 1711 }, - { IMX219_REG_X_OUTPUT_SIZE, 640 }, - { IMX219_REG_Y_OUTPUT_SIZE, 480 }, - { IMX219_REG_TP_WINDOW_WIDTH, 1640 }, - { IMX219_REG_TP_WINDOW_HEIGHT, 1232 }, -}; - -static const struct cci_reg_sequence raw8_framefmt_regs[] = { - { IMX219_REG_CSI_DATA_FORMAT_A, 0x0808 }, - { IMX219_REG_OPPXCK_DIV, 8 }, -}; - -static const struct cci_reg_sequence raw10_framefmt_regs[] = { - { IMX219_REG_CSI_DATA_FORMAT_A, 0x0a0a }, - { IMX219_REG_OPPXCK_DIV, 10 }, -}; - static const s64 imx219_link_freq_menu[] = { IMX219_DEFAULT_LINK_FREQ, }; @@ -404,17 +312,7 @@ static const struct imx219_mode supported_modes[] = { /* 8MPix 15fps mode */ .width = 3280, .height = 2464, - .crop = { - .left = IMX219_PIXEL_ARRAY_LEFT, - .top = IMX219_PIXEL_ARRAY_TOP, - .width = 3280, - .height = 2464 - }, - .vts_def = IMX219_VTS_15FPS, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_3280x2464_regs), - .regs = mode_3280x2464_regs, - }, + .vts_def = 3526, .binning = { [BINNING_IDX_8_BIT] = BINNING_NONE, [BINNING_IDX_10_BIT] = BINNING_NONE, @@ -424,17 +322,7 @@ static const struct imx219_mode supported_modes[] = { /* 1080P 30fps cropped */ .width = 1920, .height = 1080, - .crop = { - .left = 688, - .top = 700, - .width = 1920, - .height = 1080 - }, - .vts_def = IMX219_VTS_30FPS_1080P, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_1920_1080_regs), - .regs = mode_1920_1080_regs, - }, + .vts_def = 1763, .binning = { [BINNING_IDX_8_BIT] = BINNING_NONE, [BINNING_IDX_10_BIT] = BINNING_NONE, @@ -444,47 +332,39 @@ static const struct imx219_mode supported_modes[] = { /* 2x2 binned 30fps mode */ .width = 1640, .height = 1232, - .crop = { - .left = IMX219_PIXEL_ARRAY_LEFT, - .top = IMX219_PIXEL_ARRAY_TOP, - .width = 3280, - .height = 2464 - }, - .vts_def = IMX219_VTS_30FPS_BINNED, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_1640_1232_regs), - .regs = mode_1640_1232_regs, - }, + .vts_def = 1763, .binning = { - [BINNING_IDX_8_BIT] = BINNING_SPECIAL_2x2, - [BINNING_IDX_10_BIT] = BINNING_NORMAL_2x2, + [BINNING_IDX_8_BIT] = BINNING_ANALOG_X2, + [BINNING_IDX_10_BIT] = BINNING_X2, }, }, { /* 640x480 30fps mode */ .width = 640, .height = 480, - .crop = { - .left = 1008, - .top = 760, - .width = 1280, - .height = 960 - }, - .vts_def = IMX219_VTS_30FPS_640x480, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_640_480_regs), - .regs = mode_640_480_regs, - }, + .vts_def = 1763, .binning = { - [BINNING_IDX_8_BIT] = BINNING_SPECIAL_2x2, - [BINNING_IDX_10_BIT] = BINNING_SPECIAL_2x2, + [BINNING_IDX_8_BIT] = BINNING_ANALOG_X2, + [BINNING_IDX_10_BIT] = BINNING_ANALOG_X2, }, }, }; +enum imx219_pad_ids { + IMX219_PAD_SOURCE = 0, + IMX219_PAD_IMAGE, + IMX219_PAD_EDATA, + IMX219_NUM_PADS, +}; + +enum imx219_stream_ids { + IMX219_STREAM_IMAGE, + IMX219_STREAM_EDATA, +}; + struct imx219 { struct v4l2_subdev sd; - struct media_pad pad[NUM_PADS]; + struct media_pad pads[IMX219_NUM_PADS]; struct regmap *regmap; struct clk *xclk; /* system clock to IMX219 */ @@ -503,14 +383,11 @@ struct imx219 { struct v4l2_ctrl *vblank; struct v4l2_ctrl *hblank; - /* Current mode */ - const struct imx219_mode *mode; - - /* Streaming on/off */ - bool streaming; - /* Two or Four lanes */ u8 lanes; + + /* Binning mode */ + enum binning_mode binning; }; static inline struct imx219 *to_imx219(struct v4l2_subdev *_sd) @@ -536,70 +413,42 @@ static u32 imx219_get_format_code(struct imx219 *imx219, u32 code) return imx219_mbus_formats[i]; } -static int imx219_resolve_binning(struct imx219 *imx219, - const struct v4l2_mbus_framefmt *format, - enum binning_mode *binning) -{ - u32 fmt; - - if (format) - fmt = format->code; - else - fmt = MEDIA_BUS_FMT_SRGGB10_1X10; - - switch (fmt) { - case MEDIA_BUS_FMT_SRGGB8_1X8: - case MEDIA_BUS_FMT_SGRBG8_1X8: - case MEDIA_BUS_FMT_SGBRG8_1X8: - case MEDIA_BUS_FMT_SBGGR8_1X8: - *binning = imx219->mode->binning[BINNING_IDX_8_BIT]; - return 0; - - case MEDIA_BUS_FMT_SRGGB10_1X10: - case MEDIA_BUS_FMT_SGRBG10_1X10: - case MEDIA_BUS_FMT_SGBRG10_1X10: - case MEDIA_BUS_FMT_SBGGR10_1X10: - *binning = imx219->mode->binning[BINNING_IDX_10_BIT]; - return 0; - } - return -EINVAL; -} - -static int imx219_get_rate_factor(struct imx219 *imx219, - const struct v4l2_mbus_framefmt *format) +static int imx219_get_rate_factor(struct imx219 *imx219) { - enum binning_mode binning = BINNING_NONE; - int ret; - - ret = imx219_resolve_binning(imx219, format, &binning); - if (ret < 0) - return ret; - - switch (binning) { + switch (imx219->binning) { case BINNING_NONE: - case BINNING_NORMAL_2x2: + case BINNING_X2: return 1; - case BINNING_SPECIAL_2x2: + case BINNING_ANALOG_X2: return 2; } return -EINVAL; } +/* ----------------------------------------------------------------------------- + * Controls + */ + static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) { struct imx219 *imx219 = container_of(ctrl->handler, struct imx219, ctrl_handler); struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); - int ret = 0; const struct v4l2_mbus_framefmt *format; struct v4l2_subdev_state *state; int rate_factor; + int ret = 0; + + state = v4l2_subdev_get_locked_active_state(&imx219->sd); + format = v4l2_subdev_state_get_format(state, IMX219_PAD_SOURCE, + IMX219_STREAM_IMAGE); + rate_factor = imx219_get_rate_factor(imx219); if (ctrl->id == V4L2_CID_VBLANK) { int exposure_max, exposure_def; /* Update max exposure while meeting expected vblanking */ - exposure_max = imx219->mode->height + ctrl->val - 4; + exposure_max = format->height + ctrl->val - 4; exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ? exposure_max : IMX219_EXPOSURE_DEFAULT; __v4l2_ctrl_modify_range(imx219->exposure, @@ -615,10 +464,6 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) if (pm_runtime_get_if_in_use(&client->dev) == 0) return 0; - state = v4l2_subdev_get_locked_active_state(&imx219->sd); - format = v4l2_subdev_get_pad_format(&imx219->sd, state, 0); - rate_factor = imx219_get_rate_factor(imx219, format); - switch (ctrl->id) { case V4L2_CID_ANALOGUE_GAIN: cci_write(imx219->regmap, IMX219_REG_ANALOG_GAIN, @@ -643,11 +488,11 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_VBLANK: cci_write(imx219->regmap, IMX219_REG_VTS, - (imx219->mode->height + ctrl->val) / rate_factor, &ret); + (format->height + ctrl->val) / rate_factor, &ret); break; case V4L2_CID_HBLANK: cci_write(imx219->regmap, IMX219_REG_HTS, - imx219->mode->width + ctrl->val, &ret); + format->width + ctrl->val, &ret); break; case V4L2_CID_TEST_PATTERN_RED: cci_write(imx219->regmap, IMX219_REG_TESTP_RED, @@ -682,261 +527,219 @@ static const struct v4l2_ctrl_ops imx219_ctrl_ops = { .s_ctrl = imx219_set_ctrl, }; -static void imx219_update_pad_format(struct imx219 *imx219, - const struct imx219_mode *mode, - struct v4l2_mbus_framefmt *fmt, u32 code) +static unsigned long imx219_get_pixel_rate(struct imx219 *imx219) { - /* Bayer order varies with flips */ - fmt->code = imx219_get_format_code(imx219, code); - fmt->width = mode->width; - fmt->height = mode->height; - fmt->field = V4L2_FIELD_NONE; - fmt->colorspace = V4L2_COLORSPACE_RAW; - fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; - fmt->xfer_func = V4L2_XFER_FUNC_NONE; + return ((imx219->lanes == 2) ? IMX219_PIXEL_RATE : + IMX219_PIXEL_RATE_4LANE) * imx219_get_rate_factor(imx219); } -static int imx219_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +/* Initialize control handlers */ +static int imx219_init_controls(struct imx219 *imx219) { - struct imx219 *imx219 = to_imx219(sd); - struct v4l2_mbus_framefmt *format; - struct v4l2_rect *crop; - - /* Initialize the format. */ - format = v4l2_subdev_get_pad_format(sd, state, 0); - imx219_update_pad_format(imx219, &supported_modes[0], format, - MEDIA_BUS_FMT_SRGGB10_1X10); - - /* Initialize the crop rectangle. */ - crop = v4l2_subdev_get_pad_crop(sd, state, 0); - crop->top = IMX219_PIXEL_ARRAY_TOP; - crop->left = IMX219_PIXEL_ARRAY_LEFT; - crop->width = IMX219_PIXEL_ARRAY_WIDTH; - crop->height = IMX219_PIXEL_ARRAY_HEIGHT; - - /* Initialize try_fmt for the embedded metadata pad */ - format = v4l2_subdev_get_pad_format(sd, state, 1); - format->code = MEDIA_BUS_FMT_SENSOR_DATA; - format->width = IMX219_EMBEDDED_LINE_WIDTH; - format->height = IMX219_NUM_EMBEDDED_LINES; - format->field = V4L2_FIELD_NONE; + struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); + const struct imx219_mode *mode = &supported_modes[0]; + struct v4l2_ctrl_handler *ctrl_hdlr; + struct v4l2_fwnode_device_properties props; + int exposure_max, exposure_def, hblank, pixel_rate; + int i, ret; - return 0; -} + ctrl_hdlr = &imx219->ctrl_handler; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 12); + if (ret) + return ret; -static int imx219_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_mbus_code_enum *code) -{ - struct imx219 *imx219 = to_imx219(sd); + /* By default, PIXEL_RATE is read only */ + pixel_rate = imx219_get_pixel_rate(imx219); + imx219->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, + V4L2_CID_PIXEL_RATE, + pixel_rate, pixel_rate, 1, + pixel_rate); - if (code->pad >= NUM_PADS) - return -EINVAL; + imx219->link_freq = + v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx219_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(imx219_link_freq_menu) - 1, 0, + (imx219->lanes == 2) ? imx219_link_freq_menu : + imx219_link_freq_4lane_menu); + if (imx219->link_freq) + imx219->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; - if (code->pad == IMAGE_PAD) { - if (code->index >= (ARRAY_SIZE(imx219_mbus_formats) / 4)) - return -EINVAL; + /* Initial vblank/hblank/exposure parameters based on current mode */ + imx219->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, + V4L2_CID_VBLANK, IMX219_VBLANK_MIN, + IMX219_VTS_MAX - mode->height, 1, + mode->vts_def - mode->height); + hblank = IMX219_PPL_MIN - mode->width; + imx219->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, + V4L2_CID_HBLANK, hblank, + IMX219_PPL_MIN - mode->width, + 1, hblank); + exposure_max = mode->vts_def - 4; + exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ? + exposure_max : IMX219_EXPOSURE_DEFAULT; + imx219->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, + V4L2_CID_EXPOSURE, + IMX219_EXPOSURE_MIN, exposure_max, + IMX219_EXPOSURE_STEP, + exposure_def); - code->code = imx219_get_format_code(imx219, imx219_mbus_formats[code->index * 4]); - } else { - if (code->index > 0) - return -EINVAL; + v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + IMX219_ANA_GAIN_MIN, IMX219_ANA_GAIN_MAX, + IMX219_ANA_GAIN_STEP, IMX219_ANA_GAIN_DEFAULT); - code->code = MEDIA_BUS_FMT_SENSOR_DATA; - } + v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + IMX219_DGTL_GAIN_MIN, IMX219_DGTL_GAIN_MAX, + IMX219_DGTL_GAIN_STEP, IMX219_DGTL_GAIN_DEFAULT); - return 0; -} + imx219->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + if (imx219->hflip) + imx219->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; -static int imx219_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_frame_size_enum *fse) -{ - struct imx219 *imx219 = to_imx219(sd); - u32 code; + imx219->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + if (imx219->vflip) + imx219->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; - if (fse->pad >= NUM_PADS) - return -EINVAL; + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx219_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(imx219_test_pattern_menu) - 1, + 0, 0, imx219_test_pattern_menu); + for (i = 0; i < 4; i++) { + /* + * The assumption is that + * V4L2_CID_TEST_PATTERN_GREENR == V4L2_CID_TEST_PATTERN_RED + 1 + * V4L2_CID_TEST_PATTERN_BLUE == V4L2_CID_TEST_PATTERN_RED + 2 + * V4L2_CID_TEST_PATTERN_GREENB == V4L2_CID_TEST_PATTERN_RED + 3 + */ + v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, + V4L2_CID_TEST_PATTERN_RED + i, + IMX219_TESTP_COLOUR_MIN, + IMX219_TESTP_COLOUR_MAX, + IMX219_TESTP_COLOUR_STEP, + IMX219_TESTP_COLOUR_MAX); + /* The "Solid color" pattern is white by default */ + } - if (fse->pad == IMAGE_PAD) { - if (fse->index >= ARRAY_SIZE(supported_modes)) - return -EINVAL; + if (ctrl_hdlr->error) { + ret = ctrl_hdlr->error; + dev_err(&client->dev, "%s control init failed (%d)\n", + __func__, ret); + goto error; + } - code = imx219_get_format_code(imx219, fse->code); - if (fse->code != code) - return -EINVAL; + ret = v4l2_fwnode_device_parse(&client->dev, &props); + if (ret) + goto error; - fse->min_width = supported_modes[fse->index].width; - fse->max_width = fse->min_width; - fse->min_height = supported_modes[fse->index].height; - fse->max_height = fse->min_height; - } else { - if (fse->code != MEDIA_BUS_FMT_SENSOR_DATA || fse->index > 0) - return -EINVAL; + ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx219_ctrl_ops, + &props); + if (ret) + goto error; - fse->min_width = IMX219_EMBEDDED_LINE_WIDTH; - fse->max_width = fse->min_width; - fse->min_height = IMX219_NUM_EMBEDDED_LINES; - fse->max_height = fse->min_height; - } + imx219->sd.ctrl_handler = ctrl_hdlr; return 0; -} -static unsigned long imx219_get_pixel_rate(struct imx219 *imx219, - const struct v4l2_mbus_framefmt *format) -{ - return ((imx219->lanes == 2) ? IMX219_PIXEL_RATE : - IMX219_PIXEL_RATE_4LANE) * imx219_get_rate_factor(imx219, format); +error: + v4l2_ctrl_handler_free(ctrl_hdlr); + + return ret; } -static int imx219_set_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) +static void imx219_free_controls(struct imx219 *imx219) { - struct imx219 *imx219 = to_imx219(sd); - const struct imx219_mode *mode; - int exposure_max, exposure_def, hblank, pixel_rate; - struct v4l2_mbus_framefmt *format; - struct v4l2_rect *crop; - - if (fmt->pad >= NUM_PADS) - return -EINVAL; - - if (fmt->pad == IMAGE_PAD) { - mode = v4l2_find_nearest_size(supported_modes, - ARRAY_SIZE(supported_modes), - width, height, - fmt->format.width, fmt->format.height); - - imx219_update_pad_format(imx219, mode, &fmt->format, fmt->format.code); - - format = v4l2_subdev_get_pad_format(sd, sd_state, 0); - crop = v4l2_subdev_get_pad_crop(sd, sd_state, 0); - - *format = fmt->format; - *crop = mode->crop; - - if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - u32 prev_hts = imx219->mode->width + imx219->hblank->val; - - imx219->mode = mode; - /* Update limits and set FPS to default */ - __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN, - IMX219_VTS_MAX - mode->height, 1, - mode->vts_def - mode->height); - __v4l2_ctrl_s_ctrl(imx219->vblank, - mode->vts_def - mode->height); - /* Update max exposure while meeting expected vblanking */ - exposure_max = mode->vts_def - 4; - exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ? - exposure_max : IMX219_EXPOSURE_DEFAULT; - __v4l2_ctrl_modify_range(imx219->exposure, - imx219->exposure->minimum, - exposure_max, imx219->exposure->step, - exposure_def); - /* - * Retain PPL setting from previous mode so that the - * line time does not change on a mode change. - * Limits have to be recomputed as the controls define - * the blanking only, so PPL values need to have the - * mode width subtracted. - */ - hblank = prev_hts - mode->width; - __v4l2_ctrl_modify_range(imx219->hblank, - IMX219_PPL_MIN - mode->width, - IMX219_PPL_MAX - mode->width, - 1, IMX219_PPL_MIN - mode->width); - __v4l2_ctrl_s_ctrl(imx219->hblank, hblank); - - /* Scale the pixel rate based on the mode specific factor */ - pixel_rate = imx219_get_pixel_rate(imx219, &fmt->format); - __v4l2_ctrl_modify_range(imx219->pixel_rate, pixel_rate, - pixel_rate, 1, pixel_rate); - } - } else { - format = v4l2_subdev_get_pad_format(sd, sd_state, 1); - /* Don't allow the embedded data format to be changed */ - fmt->format = *format; - } - - return 0; + v4l2_ctrl_handler_free(imx219->sd.ctrl_handler); } -static int imx219_set_framefmt(struct imx219 *imx219, - const struct v4l2_mbus_framefmt *format) +/* ----------------------------------------------------------------------------- + * Subdev operations + */ + +static unsigned int imx219_format_bpp(u32 code) { - switch (format->code) { + switch (code) { case MEDIA_BUS_FMT_SRGGB8_1X8: case MEDIA_BUS_FMT_SGRBG8_1X8: case MEDIA_BUS_FMT_SGBRG8_1X8: case MEDIA_BUS_FMT_SBGGR8_1X8: - return cci_multi_reg_write(imx219->regmap, raw8_framefmt_regs, - ARRAY_SIZE(raw8_framefmt_regs), NULL); + return 8; case MEDIA_BUS_FMT_SRGGB10_1X10: case MEDIA_BUS_FMT_SGRBG10_1X10: case MEDIA_BUS_FMT_SGBRG10_1X10: case MEDIA_BUS_FMT_SBGGR10_1X10: - return cci_multi_reg_write(imx219->regmap, raw10_framefmt_regs, - ARRAY_SIZE(raw10_framefmt_regs), NULL); + default: + return 10; } - - return -EINVAL; } -static int imx219_set_binning(struct imx219 *imx219, - const struct v4l2_mbus_framefmt *format) +/* Return the embedded data format corresponding to an image format. */ +static u32 imx219_format_edata(u32 code) { - enum binning_mode binning = BINNING_NONE; - - imx219_resolve_binning(imx219, format, &binning); + switch (imx219_format_bpp(code)) { + case 8: + return MEDIA_BUS_FMT_META_8; - switch (binning) { - case BINNING_NONE: - return cci_write(imx219->regmap, IMX219_REG_BINNING_MODE, - IMX219_BINNING_NONE, NULL); - case BINNING_NORMAL_2x2: - return cci_write(imx219->regmap, IMX219_REG_BINNING_MODE, - IMX219_BINNING_2X2_NORMAL, NULL); - case BINNING_SPECIAL_2x2: - return cci_write(imx219->regmap, IMX219_REG_BINNING_MODE, - IMX219_BINNING_2X2_SPECIAL, NULL); + case 10: + default: + return MEDIA_BUS_FMT_META_10; } - - return -EINVAL; } -static int imx219_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_selection *sel) +static int imx219_set_framefmt(struct imx219 *imx219, + struct v4l2_subdev_state *state) { - switch (sel->target) { - case V4L2_SEL_TGT_CROP: { - sel->r = *v4l2_subdev_get_pad_crop(sd, sd_state, 0); - return 0; + const struct v4l2_mbus_framefmt *format; + const struct v4l2_rect *crop; + u64 binning; + u32 bpp; + int ret = 0; + + format = v4l2_subdev_state_get_format(state, IMX219_PAD_SOURCE, + IMX219_STREAM_IMAGE); + crop = v4l2_subdev_state_get_crop(state, IMX219_PAD_IMAGE); + bpp = imx219_format_bpp(format->code); + + cci_write(imx219->regmap, IMX219_REG_X_ADD_STA_A, + crop->left - IMX219_PIXEL_ARRAY_LEFT, &ret); + cci_write(imx219->regmap, IMX219_REG_X_ADD_END_A, + crop->left - IMX219_PIXEL_ARRAY_LEFT + crop->width - 1, &ret); + cci_write(imx219->regmap, IMX219_REG_Y_ADD_STA_A, + crop->top - IMX219_PIXEL_ARRAY_TOP, &ret); + cci_write(imx219->regmap, IMX219_REG_Y_ADD_END_A, + crop->top - IMX219_PIXEL_ARRAY_TOP + crop->height - 1, &ret); + + switch (imx219->binning) { + case BINNING_NONE: + binning = IMX219_BINNING_NONE; + break; + case BINNING_X2: + binning = IMX219_BINNING_X2; + break; + case BINNING_ANALOG_X2: + binning = IMX219_BINNING_X2_ANALOG; + break; } - case V4L2_SEL_TGT_NATIVE_SIZE: - sel->r.top = 0; - sel->r.left = 0; - sel->r.width = IMX219_NATIVE_WIDTH; - sel->r.height = IMX219_NATIVE_HEIGHT; + cci_write(imx219->regmap, IMX219_REG_BINNING_MODE_H, binning, &ret); + cci_write(imx219->regmap, IMX219_REG_BINNING_MODE_V, binning, &ret); - return 0; + cci_write(imx219->regmap, IMX219_REG_X_OUTPUT_SIZE, + format->width, &ret); + cci_write(imx219->regmap, IMX219_REG_Y_OUTPUT_SIZE, + format->height, &ret); - case V4L2_SEL_TGT_CROP_DEFAULT: - case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.top = IMX219_PIXEL_ARRAY_TOP; - sel->r.left = IMX219_PIXEL_ARRAY_LEFT; - sel->r.width = IMX219_PIXEL_ARRAY_WIDTH; - sel->r.height = IMX219_PIXEL_ARRAY_HEIGHT; + cci_write(imx219->regmap, IMX219_REG_TP_WINDOW_WIDTH, + format->width, &ret); + cci_write(imx219->regmap, IMX219_REG_TP_WINDOW_HEIGHT, + format->height, &ret); - return 0; - } + cci_write(imx219->regmap, IMX219_REG_CSI_DATA_FORMAT_A, + (bpp << 8) | bpp, &ret); + cci_write(imx219->regmap, IMX219_REG_OPPXCK_DIV, bpp, &ret); - return -EINVAL; + return ret; } static int imx219_configure_lanes(struct imx219 *imx219) @@ -950,8 +753,6 @@ static int imx219_start_streaming(struct imx219 *imx219, struct v4l2_subdev_state *state) { struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); - const struct v4l2_mbus_framefmt *format; - const struct imx219_reg_list *reg_list; int ret; ret = pm_runtime_resume_and_get(&client->dev); @@ -973,30 +774,14 @@ static int imx219_start_streaming(struct imx219 *imx219, goto err_rpm_put; } - /* Apply default values of current mode */ - reg_list = &imx219->mode->reg_list; - ret = cci_multi_reg_write(imx219->regmap, reg_list->regs, - reg_list->num_of_regs, NULL); - if (ret) { - dev_err(&client->dev, "%s failed to set mode\n", __func__); - goto err_rpm_put; - } - - format = v4l2_subdev_get_pad_format(&imx219->sd, state, 0); - ret = imx219_set_framefmt(imx219, format); + /* Apply format and crop settings. */ + ret = imx219_set_framefmt(imx219, state); if (ret) { dev_err(&client->dev, "%s failed to set frame format: %d\n", __func__, ret); goto err_rpm_put; } - ret = imx219_set_binning(imx219, format); - if (ret) { - dev_err(&client->dev, "%s failed to set binning: %d\n", - __func__, ret); - goto err_rpm_put; - } - /* Apply customized values from user */ ret = __v4l2_ctrl_handler_setup(imx219->sd.ctrl_handler); if (ret) @@ -1036,149 +821,454 @@ static void imx219_stop_streaming(struct imx219 *imx219) pm_runtime_put(&client->dev); } -static int imx219_set_stream(struct v4l2_subdev *sd, int enable) +static int imx219_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) { struct imx219 *imx219 = to_imx219(sd); - struct v4l2_subdev_state *state; - int ret = 0; - state = v4l2_subdev_lock_and_get_active_state(sd); + switch (code->pad) { + case IMX219_PAD_IMAGE: + /* The internal image pad is hardwired to the native format. */ + if (code->index > 0) + return -EINVAL; + + code->code = IMX219_NATIVE_FORMAT; + return 0; - if (imx219->streaming == enable) - goto unlock; + case IMX219_PAD_EDATA: + if (code->index > 0) + return -EINVAL; - if (enable) { - /* - * Apply default & customized values - * and then start streaming. - */ - ret = imx219_start_streaming(imx219, state); - if (ret) - goto unlock; - } else { - imx219_stop_streaming(imx219); + code->code = MEDIA_BUS_FMT_CCS_EMBEDDED; + return 0; + + case IMX219_PAD_SOURCE: + default: + break; } - imx219->streaming = enable; + /* + * On the source pad, the sensor supports multiple image raw formats + * with different bit depths. The embedded data format bit depth + * follows the image stream. + */ + if (code->stream == IMX219_STREAM_IMAGE) { + u32 format; + + if (code->index >= ARRAY_SIZE(imx219_mbus_formats) / 4) + return -EINVAL; -unlock: - v4l2_subdev_unlock_state(state); - return ret; + format = imx219_mbus_formats[code->index * 4]; + code->code = imx219_get_format_code(imx219, format); + } else { + struct v4l2_mbus_framefmt *fmt; + + if (code->index > 0) + return -EINVAL; + + fmt = v4l2_subdev_state_get_format(state, IMX219_PAD_SOURCE, + IMX219_STREAM_EDATA); + code->code = fmt->code; + } + + return 0; } -/* Power/clock management functions */ -static int imx219_power_on(struct device *dev) +static int imx219_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_size_enum *fse) { - struct v4l2_subdev *sd = dev_get_drvdata(dev); struct imx219 *imx219 = to_imx219(sd); - int ret; - ret = regulator_bulk_enable(IMX219_NUM_SUPPLIES, - imx219->supplies); - if (ret) { - dev_err(dev, "%s: failed to enable regulators\n", - __func__); - return ret; - } + switch (fse->pad) { + case IMX219_PAD_IMAGE: + if (fse->code != IMX219_NATIVE_FORMAT || fse->index > 0) + return -EINVAL; - ret = clk_prepare_enable(imx219->xclk); - if (ret) { - dev_err(dev, "%s: failed to enable clock\n", - __func__); - goto reg_off; + fse->min_width = IMX219_NATIVE_WIDTH; + fse->max_width = IMX219_NATIVE_WIDTH; + fse->min_height = IMX219_NATIVE_HEIGHT; + fse->max_height = IMX219_NATIVE_HEIGHT; + return 0; + + case IMX219_PAD_EDATA: + if (fse->code != MEDIA_BUS_FMT_CCS_EMBEDDED || fse->index > 0) + return -EINVAL; + + fse->min_width = IMX219_NATIVE_WIDTH; + fse->max_width = IMX219_NATIVE_WIDTH; + fse->min_height = IMX219_EMBEDDED_DATA_HEIGHT; + fse->max_height = IMX219_EMBEDDED_DATA_HEIGHT; + return 0; + + case IMX219_PAD_SOURCE: + default: + break; } - gpiod_set_value_cansleep(imx219->reset_gpio, 1); - usleep_range(IMX219_XCLR_MIN_DELAY_US, - IMX219_XCLR_MIN_DELAY_US + IMX219_XCLR_DELAY_RANGE_US); + if (fse->stream == IMX219_STREAM_IMAGE) { + if (fse->code != imx219_get_format_code(imx219, fse->code) || + fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; - return 0; + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; + } else { + struct v4l2_mbus_framefmt *fmt; -reg_off: - regulator_bulk_disable(IMX219_NUM_SUPPLIES, imx219->supplies); + fmt = v4l2_subdev_state_get_format(state, IMX219_PAD_SOURCE, + IMX219_STREAM_EDATA); + if (fse->code != fmt->code) + return -EINVAL; - return ret; + if (fse->index > 0) + return -EINVAL; + + fse->min_width = fmt->width; + fse->max_width = fmt->width; + fse->min_height = IMX219_EMBEDDED_DATA_HEIGHT; + fse->max_height = IMX219_EMBEDDED_DATA_HEIGHT; + } + + return 0; } -static int imx219_power_off(struct device *dev) +static int imx219_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *fmt) { - struct v4l2_subdev *sd = dev_get_drvdata(dev); struct imx219 *imx219 = to_imx219(sd); + const struct imx219_mode *mode; + struct v4l2_mbus_framefmt *ed_format; + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *compose; + struct v4l2_rect *crop; + unsigned int bin_h, bin_v; - gpiod_set_value_cansleep(imx219->reset_gpio, 0); - regulator_bulk_disable(IMX219_NUM_SUPPLIES, imx219->supplies); - clk_disable_unprepare(imx219->xclk); + /* + * The driver is mode-based, the format can be set on the source pad + * only, and only for the image streeam. + */ + if (fmt->pad != IMX219_PAD_SOURCE || fmt->stream != IMX219_STREAM_IMAGE) + return v4l2_subdev_get_fmt(sd, state, fmt); + + /* + * Adjust the requested format to match the closest mode. The Bayer + * order varies with flips. + */ + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), + width, height, + fmt->format.width, fmt->format.height); + + fmt->format.code = imx219_get_format_code(imx219, fmt->format.code); + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + fmt->format.colorspace = V4L2_COLORSPACE_RAW; + fmt->format.ycbcr_enc = V4L2_YCBCR_ENC_601; + fmt->format.quantization = V4L2_QUANTIZATION_FULL_RANGE; + fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; + + /* Propagate the format through the sensor. */ + + /* The image pad models the pixel array, and thus has a fixed format. */ + format = v4l2_subdev_state_get_format(state, IMX219_PAD_IMAGE); + *format = fmt->format; + format->code = IMX219_NATIVE_FORMAT; + format->width = IMX219_NATIVE_WIDTH; + format->height = IMX219_NATIVE_HEIGHT; + + /* + * Use binning to maximize the crop rectangle size, and centre it in the + * sensor. + */ + bin_h = min(IMX219_PIXEL_ARRAY_WIDTH / fmt->format.width, 2U); + bin_v = min(IMX219_PIXEL_ARRAY_HEIGHT / fmt->format.height, 2U); + + crop = v4l2_subdev_state_get_crop(state, IMX219_PAD_IMAGE); + crop->width = fmt->format.width * bin_h; + crop->height = fmt->format.height * bin_v; + crop->left = (IMX219_NATIVE_WIDTH - crop->width) / 2; + crop->top = (IMX219_NATIVE_HEIGHT - crop->height) / 2; + + /* + * The compose rectangle models binning, its size is the sensor output + * size. + */ + compose = v4l2_subdev_state_get_compose(state, IMX219_PAD_IMAGE); + compose->left = 0; + compose->top = 0; + compose->width = fmt->format.width; + compose->height = fmt->format.height; + + /* + * No mode use digital crop, the source pad crop rectangle size and + * format are thus identical to the image pad compose rectangle. + */ + crop = v4l2_subdev_state_get_crop(state, IMX219_PAD_SOURCE, + IMX219_STREAM_IMAGE); + crop->left = 0; + crop->top = 0; + crop->width = fmt->format.width; + crop->height = fmt->format.height; + + format = v4l2_subdev_state_get_format(state, IMX219_PAD_SOURCE, + IMX219_STREAM_IMAGE); + *format = fmt->format; + + /* + * Finally, update the formats on the sink and source sides of the + * embedded data stream. + */ + ed_format = v4l2_subdev_state_get_format(state, IMX219_PAD_EDATA); + ed_format->code = MEDIA_BUS_FMT_CCS_EMBEDDED; + ed_format->width = format->width; + ed_format->height = IMX219_EMBEDDED_DATA_HEIGHT; + ed_format->field = V4L2_FIELD_NONE; + + format = v4l2_subdev_state_get_format(state, IMX219_PAD_SOURCE, + IMX219_STREAM_EDATA); + *format = *ed_format; + format->code = imx219_format_edata(format->code); + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + u32 prev_hts = format->width + imx219->hblank->val; + int exposure_max; + int exposure_def; + int hblank; + int pixel_rate; + + /* Update binning mode based on format */ + switch (imx219_format_bpp(fmt->format.code)) { + case 8: + imx219->binning = mode->binning[BINNING_IDX_8_BIT]; + break; + + case 10: + imx219->binning = mode->binning[BINNING_IDX_10_BIT]; + break; + + default: + imx219->binning = BINNING_NONE; + } + + /* Update limits and set FPS to default */ + __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN, + IMX219_VTS_MAX - mode->height, 1, + mode->vts_def - mode->height); + __v4l2_ctrl_s_ctrl(imx219->vblank, + mode->vts_def - mode->height); + /* Update max exposure while meeting expected vblanking */ + exposure_max = mode->vts_def - 4; + exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ? + exposure_max : IMX219_EXPOSURE_DEFAULT; + __v4l2_ctrl_modify_range(imx219->exposure, + imx219->exposure->minimum, + exposure_max, imx219->exposure->step, + exposure_def); + /* + * Retain PPL setting from previous mode so that the + * line time does not change on a mode change. + * Limits have to be recomputed as the controls define + * the blanking only, so PPL values need to have the + * mode width subtracted. + */ + hblank = prev_hts - mode->width; + __v4l2_ctrl_modify_range(imx219->hblank, + IMX219_PPL_MIN - mode->width, + IMX219_PPL_MAX - mode->width, + 1, IMX219_PPL_MIN - mode->width); + __v4l2_ctrl_s_ctrl(imx219->hblank, hblank); + + /* Scale the pixel rate based on the mode specific factor */ + pixel_rate = imx219_get_pixel_rate(imx219); + __v4l2_ctrl_modify_range(imx219->pixel_rate, pixel_rate, + pixel_rate, 1, pixel_rate); + } return 0; } -static int __maybe_unused imx219_suspend(struct device *dev) +static int imx219_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) { - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct imx219 *imx219 = to_imx219(sd); + struct v4l2_rect *compose; + + /* + * The embedded data stream doesn't support selection rectangles, + * neither on the embedded data pad nor on the source pad. + */ + if (sel->pad == IMX219_PAD_EDATA || sel->stream != 0) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_NATIVE_SIZE: + if (sel->pad != IMX219_PAD_IMAGE) + return -EINVAL; - if (imx219->streaming) - imx219_stop_streaming(imx219); + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = IMX219_NATIVE_WIDTH; + sel->r.height = IMX219_NATIVE_HEIGHT; - return 0; + return 0; + + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + switch (sel->pad) { + case IMX219_PAD_IMAGE: + sel->r.top = IMX219_PIXEL_ARRAY_TOP; + sel->r.left = IMX219_PIXEL_ARRAY_LEFT; + sel->r.width = IMX219_PIXEL_ARRAY_WIDTH; + sel->r.height = IMX219_PIXEL_ARRAY_HEIGHT; + return 0; + + case IMX219_PAD_SOURCE: + compose = v4l2_subdev_state_get_compose(state, + IMX219_PAD_IMAGE); + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = compose->width; + sel->r.height = compose->height; + return 0; + } + + break; + + case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(state, sel->pad); + return 0; + + case V4L2_SEL_TGT_COMPOSE: + if (sel->pad != IMX219_PAD_IMAGE) + return -EINVAL; + + sel->r = *v4l2_subdev_state_get_compose(state, sel->pad); + return 0; + } + + return -EINVAL; } -static int __maybe_unused imx219_resume(struct device *dev) +static int imx219_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) { - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct imx219 *imx219 = to_imx219(sd); struct v4l2_subdev_state *state; - int ret; + u32 img_code; + u32 ed_code; - if (imx219->streaming) { - state = v4l2_subdev_lock_and_get_active_state(sd); - ret = imx219_start_streaming(imx219, state); - v4l2_subdev_unlock_state(state); - if (ret) - goto error; - } + if (pad != IMX219_PAD_SOURCE) + return -EINVAL; + + state = v4l2_subdev_lock_and_get_active_state(sd); + img_code = v4l2_subdev_state_get_format(state, IMX219_PAD_SOURCE, + IMX219_STREAM_IMAGE)->code; + ed_code = v4l2_subdev_state_get_format(state, IMX219_PAD_SOURCE, + IMX219_STREAM_EDATA)->code; + v4l2_subdev_unlock_state(state); + + fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; + fd->num_entries = 2; + + fd->entry[0].pixelcode = img_code; + fd->entry[0].stream = IMX219_STREAM_IMAGE; + fd->entry[0].bus.csi2.vc = 0; + fd->entry[0].bus.csi2.dt = imx219_format_bpp(img_code) == 8 + ? MIPI_CSI2_DT_RAW8 : MIPI_CSI2_DT_RAW10; + + fd->entry[1].pixelcode = ed_code; + fd->entry[1].stream = IMX219_STREAM_EDATA; + fd->entry[1].bus.csi2.vc = 0; + fd->entry[1].bus.csi2.dt = MIPI_CSI2_DT_EMBEDDED_8B; return 0; +} -error: - imx219_stop_streaming(imx219); - imx219->streaming = false; +static int imx219_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct imx219 *imx219 = to_imx219(sd); - return ret; + /* + * The image stream controls sensor streaming, as embedded data isn't + * controllable independently. + */ + if (!(streams_mask & BIT(IMX219_STREAM_IMAGE))) + return 0; + + return imx219_start_streaming(imx219, state); } -static int imx219_get_regulators(struct imx219 *imx219) +static int imx219_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) { - struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); - unsigned int i; + struct imx219 *imx219 = to_imx219(sd); - for (i = 0; i < IMX219_NUM_SUPPLIES; i++) - imx219->supplies[i].supply = imx219_supply_name[i]; + /* + * The image stream controls sensor streaming, as embedded data isn't + * controllable independently. + */ + if (!(streams_mask & BIT(IMX219_STREAM_IMAGE))) + return 0; - return devm_regulator_bulk_get(&client->dev, - IMX219_NUM_SUPPLIES, - imx219->supplies); + imx219_stop_streaming(imx219); + + return 0; } -/* Verify chip ID */ -static int imx219_identify_module(struct imx219 *imx219) +static int imx219_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { - struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); + struct v4l2_subdev_route routes[] = { + { + .sink_pad = IMX219_PAD_IMAGE, + .sink_stream = 0, + .source_pad = IMX219_PAD_SOURCE, + .source_stream = IMX219_STREAM_IMAGE, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE | + V4L2_SUBDEV_ROUTE_FL_IMMUTABLE, + }, { + .sink_pad = IMX219_PAD_EDATA, + .sink_stream = 0, + .source_pad = IMX219_PAD_SOURCE, + .source_stream = IMX219_STREAM_EDATA, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE | + V4L2_SUBDEV_ROUTE_FL_IMMUTABLE, + }, + }; + struct v4l2_subdev_krouting routing = { + .len_routes = ARRAY_SIZE(routes), + .num_routes = ARRAY_SIZE(routes), + .routes = routes, + }; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .pad = IMX219_PAD_SOURCE, + .stream = IMX219_STREAM_IMAGE, + .format = { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .width = supported_modes[0].width, + .height = supported_modes[0].height, + }, + }; int ret; - u64 val; - ret = cci_read(imx219->regmap, IMX219_REG_CHIP_ID, &val, NULL); - if (ret) { - dev_err(&client->dev, "failed to read chip id %x\n", - IMX219_CHIP_ID); + ret = v4l2_subdev_set_routing(sd, state, &routing); + if (ret) return ret; - } - if (val != IMX219_CHIP_ID) { - dev_err(&client->dev, "chip id mismatch: %x!=%llx\n", - IMX219_CHIP_ID, val); - return -EIO; - } + /* + * Set the image stream format on the source pad. This will be + * propagated to all formats and selection rectangles internally. + */ + imx219_set_pad_format(sd, state, &fmt); return 0; } @@ -1188,143 +1278,113 @@ static const struct v4l2_subdev_core_ops imx219_core_ops = { .unsubscribe_event = v4l2_event_subdev_unsubscribe, }; -static const struct v4l2_subdev_video_ops imx219_video_ops = { - .s_stream = imx219_set_stream, -}; - static const struct v4l2_subdev_pad_ops imx219_pad_ops = { - .init_cfg = imx219_init_cfg, .enum_mbus_code = imx219_enum_mbus_code, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = imx219_set_pad_format, .get_selection = imx219_get_selection, .enum_frame_size = imx219_enum_frame_size, + .get_frame_desc = imx219_get_frame_desc, + .enable_streams = imx219_enable_streams, + .disable_streams = imx219_disable_streams, }; static const struct v4l2_subdev_ops imx219_subdev_ops = { .core = &imx219_core_ops, - .video = &imx219_video_ops, .pad = &imx219_pad_ops, }; +static const struct v4l2_subdev_internal_ops imx219_internal_ops = { + .init_state = imx219_init_state, +}; + +/* ----------------------------------------------------------------------------- + * Power management + */ -/* Initialize control handlers */ -static int imx219_init_controls(struct imx219 *imx219) +static int imx219_power_on(struct device *dev) { - struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); - struct v4l2_ctrl_handler *ctrl_hdlr; - unsigned int height = imx219->mode->height; - struct v4l2_fwnode_device_properties props; - int exposure_max, exposure_def, hblank, pixel_rate; - int i, ret; + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct imx219 *imx219 = to_imx219(sd); + int ret; - ctrl_hdlr = &imx219->ctrl_handler; - ret = v4l2_ctrl_handler_init(ctrl_hdlr, 12); - if (ret) + ret = regulator_bulk_enable(IMX219_NUM_SUPPLIES, + imx219->supplies); + if (ret) { + dev_err(dev, "%s: failed to enable regulators\n", + __func__); return ret; + } - /* By default, PIXEL_RATE is read only */ - pixel_rate = imx219_get_pixel_rate(imx219, NULL); - imx219->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, - V4L2_CID_PIXEL_RATE, - pixel_rate, pixel_rate, 1, - pixel_rate); + ret = clk_prepare_enable(imx219->xclk); + if (ret) { + dev_err(dev, "%s: failed to enable clock\n", + __func__); + goto reg_off; + } - imx219->link_freq = - v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx219_ctrl_ops, - V4L2_CID_LINK_FREQ, - ARRAY_SIZE(imx219_link_freq_menu) - 1, 0, - (imx219->lanes == 2) ? imx219_link_freq_menu : - imx219_link_freq_4lane_menu); - if (imx219->link_freq) - imx219->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + gpiod_set_value_cansleep(imx219->reset_gpio, 1); + usleep_range(IMX219_XCLR_MIN_DELAY_US, + IMX219_XCLR_MIN_DELAY_US + IMX219_XCLR_DELAY_RANGE_US); - /* Initial vblank/hblank/exposure parameters based on current mode */ - imx219->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, - V4L2_CID_VBLANK, IMX219_VBLANK_MIN, - IMX219_VTS_MAX - height, 1, - imx219->mode->vts_def - height); - hblank = IMX219_PPL_MIN - imx219->mode->width; - imx219->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, - V4L2_CID_HBLANK, hblank, - IMX219_PPL_MIN - imx219->mode->width, - 1, hblank); - exposure_max = imx219->mode->vts_def - 4; - exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ? - exposure_max : IMX219_EXPOSURE_DEFAULT; - imx219->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, - V4L2_CID_EXPOSURE, - IMX219_EXPOSURE_MIN, exposure_max, - IMX219_EXPOSURE_STEP, - exposure_def); + return 0; - v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, - IMX219_ANA_GAIN_MIN, IMX219_ANA_GAIN_MAX, - IMX219_ANA_GAIN_STEP, IMX219_ANA_GAIN_DEFAULT); +reg_off: + regulator_bulk_disable(IMX219_NUM_SUPPLIES, imx219->supplies); - v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_DIGITAL_GAIN, - IMX219_DGTL_GAIN_MIN, IMX219_DGTL_GAIN_MAX, - IMX219_DGTL_GAIN_STEP, IMX219_DGTL_GAIN_DEFAULT); + return ret; +} - imx219->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - if (imx219->hflip) - imx219->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; +static int imx219_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct imx219 *imx219 = to_imx219(sd); - imx219->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - if (imx219->vflip) - imx219->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + gpiod_set_value_cansleep(imx219->reset_gpio, 0); + regulator_bulk_disable(IMX219_NUM_SUPPLIES, imx219->supplies); + clk_disable_unprepare(imx219->xclk); - v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx219_ctrl_ops, - V4L2_CID_TEST_PATTERN, - ARRAY_SIZE(imx219_test_pattern_menu) - 1, - 0, 0, imx219_test_pattern_menu); - for (i = 0; i < 4; i++) { - /* - * The assumption is that - * V4L2_CID_TEST_PATTERN_GREENR == V4L2_CID_TEST_PATTERN_RED + 1 - * V4L2_CID_TEST_PATTERN_BLUE == V4L2_CID_TEST_PATTERN_RED + 2 - * V4L2_CID_TEST_PATTERN_GREENB == V4L2_CID_TEST_PATTERN_RED + 3 - */ - v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, - V4L2_CID_TEST_PATTERN_RED + i, - IMX219_TESTP_COLOUR_MIN, - IMX219_TESTP_COLOUR_MAX, - IMX219_TESTP_COLOUR_STEP, - IMX219_TESTP_COLOUR_MAX); - /* The "Solid color" pattern is white by default */ - } + return 0; +} - if (ctrl_hdlr->error) { - ret = ctrl_hdlr->error; - dev_err(&client->dev, "%s control init failed (%d)\n", - __func__, ret); - goto error; - } +/* ----------------------------------------------------------------------------- + * Probe & remove + */ - ret = v4l2_fwnode_device_parse(&client->dev, &props); - if (ret) - goto error; +static int imx219_get_regulators(struct imx219 *imx219) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); + unsigned int i; - ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx219_ctrl_ops, - &props); - if (ret) - goto error; + for (i = 0; i < IMX219_NUM_SUPPLIES; i++) + imx219->supplies[i].supply = imx219_supply_name[i]; - imx219->sd.ctrl_handler = ctrl_hdlr; + return devm_regulator_bulk_get(&client->dev, + IMX219_NUM_SUPPLIES, + imx219->supplies); +} - return 0; +/* Verify chip ID */ +static int imx219_identify_module(struct imx219 *imx219) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); + int ret; + u64 val; -error: - v4l2_ctrl_handler_free(ctrl_hdlr); + ret = cci_read(imx219->regmap, IMX219_REG_CHIP_ID, &val, NULL); + if (ret) { + dev_err(&client->dev, "failed to read chip id %x\n", + IMX219_CHIP_ID); + return ret; + } - return ret; -} + if (val != IMX219_CHIP_ID) { + dev_err(&client->dev, "chip id mismatch: %x!=%llx\n", + IMX219_CHIP_ID, val); + return -EIO; + } -static void imx219_free_controls(struct imx219 *imx219) -{ - v4l2_ctrl_handler_free(imx219->sd.ctrl_handler); + return 0; } static int imx219_check_hwcfg(struct device *dev, struct imx219 *imx219) @@ -1388,6 +1448,7 @@ static int imx219_probe(struct i2c_client *client) return -ENOMEM; v4l2_i2c_subdev_init(&imx219->sd, client, &imx219_subdev_ops); + imx219->sd.internal_ops = &imx219_internal_ops; /* Check the hardware configuration in device tree */ if (imx219_check_hwcfg(dev, imx219)) @@ -1436,10 +1497,8 @@ static int imx219_probe(struct i2c_client *client) if (ret) goto error_power_off; - /* Set default mode to max resolution */ - imx219->mode = &supported_modes[0]; - - /* sensor doesn't enter LP-11 state upon power up until and unless + /* + * Sensor doesn't enter LP-11 state upon power up until and unless * streaming is started, so upon power up switch the modes to: * streaming -> standby */ @@ -1464,14 +1523,24 @@ static int imx219_probe(struct i2c_client *client) /* Initialize subdev */ imx219->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | - V4L2_SUBDEV_FL_HAS_EVENTS; + V4L2_SUBDEV_FL_HAS_EVENTS | + V4L2_SUBDEV_FL_STREAMS; imx219->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; - /* Initialize source pad */ - imx219->pad[IMAGE_PAD].flags = MEDIA_PAD_FL_SOURCE; - imx219->pad[METADATA_PAD].flags = MEDIA_PAD_FL_SOURCE; - - ret = media_entity_pads_init(&imx219->sd.entity, NUM_PADS, imx219->pad); + /* + * Initialize the pads. To preserve backward compatibility with + * userspace that used the sensor before the introduction of the + * internal pads, the external source pad is numbered 0 and the internal + * image and embedded data pads numbered 1 and 2 respectively. + */ + imx219->pads[IMX219_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + imx219->pads[IMX219_PAD_IMAGE].flags = MEDIA_PAD_FL_SINK + | MEDIA_PAD_FL_INTERNAL; + imx219->pads[IMX219_PAD_EDATA].flags = MEDIA_PAD_FL_SINK + | MEDIA_PAD_FL_INTERNAL; + + ret = media_entity_pads_init(&imx219->sd.entity, + ARRAY_SIZE(imx219->pads), imx219->pads); if (ret) { dev_err(dev, "failed to init entity pads: %d\n", ret); goto error_handler_free; @@ -1535,7 +1604,6 @@ static const struct of_device_id imx219_dt_ids[] = { MODULE_DEVICE_TABLE(of, imx219_dt_ids); static const struct dev_pm_ops imx219_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(imx219_suspend, imx219_resume) SET_RUNTIME_PM_OPS(imx219_power_off, imx219_power_on, NULL) }; diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index e30e14d63823aa..fdb38b948a20f7 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -1005,7 +1005,7 @@ static int imx258_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct imx258 *imx258 = to_imx258(sd); struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); struct v4l2_rect *try_crop; /* Initialize try_fmt */ @@ -1015,7 +1015,7 @@ static int imx258_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) try_fmt->field = V4L2_FIELD_NONE; /* Initialize try_crop */ - try_crop = v4l2_subdev_get_try_crop(sd, fh->state, 0); + try_crop = v4l2_subdev_state_get_crop(fh->state, 0); try_crop->left = IMX258_PIXEL_ARRAY_LEFT; try_crop->top = IMX258_PIXEL_ARRAY_TOP; try_crop->width = IMX258_PIXEL_ARRAY_WIDTH; @@ -1220,9 +1220,8 @@ static int __imx258_get_pad_format(struct imx258 *imx258, struct v4l2_subdev_format *fmt) { if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&imx258->sd, - sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); else imx258_update_pad_format(imx258, imx258->cur_mode, fmt); @@ -1267,7 +1266,7 @@ static int imx258_set_pad_format(struct v4l2_subdev *sd, fmt->format.width, fmt->format.height); imx258_update_pad_format(imx258, mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); *framefmt = fmt->format; } else { imx258->cur_mode = mode; @@ -1311,7 +1310,7 @@ __imx258_get_pad_crop(struct imx258 *imx258, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&imx258->sd, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &imx258->cur_mode->crop; } diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index f33b692e6951ef..8dc11c9ec1ee1c 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -1018,8 +1018,8 @@ static int __imx274_change_compose(struct stimx274 *imx274, int best_goodness = INT_MIN; if (which == V4L2_SUBDEV_FORMAT_TRY) { - cur_crop = &sd_state->pads->try_crop; - tgt_fmt = &sd_state->pads->try_fmt; + cur_crop = v4l2_subdev_state_get_crop(sd_state, 0); + tgt_fmt = v4l2_subdev_state_get_format(sd_state, 0); } else { cur_crop = &imx274->crop; tgt_fmt = &imx274->format; @@ -1112,7 +1112,7 @@ static int imx274_set_fmt(struct v4l2_subdev *sd, */ fmt->field = V4L2_FIELD_NONE; if (format->which == V4L2_SUBDEV_FORMAT_TRY) - sd_state->pads->try_fmt = *fmt; + *v4l2_subdev_state_get_format(sd_state, 0) = *fmt; else imx274->format = *fmt; @@ -1143,8 +1143,8 @@ static int imx274_get_selection(struct v4l2_subdev *sd, } if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - src_crop = &sd_state->pads->try_crop; - src_fmt = &sd_state->pads->try_fmt; + src_crop = v4l2_subdev_state_get_crop(sd_state, 0); + src_fmt = v4l2_subdev_state_get_format(sd_state, 0); } else { src_crop = &imx274->crop; src_fmt = &imx274->format; @@ -1215,7 +1215,7 @@ static int imx274_set_selection_crop(struct stimx274 *imx274, sel->r = new_crop; if (sel->which == V4L2_SUBDEV_FORMAT_TRY) - tgt_crop = &sd_state->pads->try_crop; + tgt_crop = v4l2_subdev_state_get_crop(sd_state, 0); else tgt_crop = &imx274->crop; diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index 6dacd38ae947a1..4150e6e4b9a635 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -758,7 +758,7 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) return 0; state = v4l2_subdev_get_locked_active_state(&imx290->sd); - format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0); + format = v4l2_subdev_state_get_format(state, 0); switch (ctrl->id) { case V4L2_CID_ANALOGUE_GAIN: @@ -994,7 +994,7 @@ static int imx290_start_streaming(struct imx290 *imx290, } /* Apply the register values related to current frame format */ - format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0); + format = v4l2_subdev_state_get_format(state, 0); ret = imx290_setup_format(imx290, format); if (ret < 0) { dev_err(imx290->dev, "Could not set frame format - %d\n", ret); @@ -1132,7 +1132,7 @@ static int imx290_set_fmt(struct v4l2_subdev *sd, fmt->format.quantization = V4L2_QUANTIZATION_FULL_RANGE; fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; - format = v4l2_subdev_get_pad_format(sd, sd_state, 0); + format = v4l2_subdev_state_get_format(sd_state, 0); if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { imx290->current_mode = mode; @@ -1155,7 +1155,7 @@ static int imx290_get_selection(struct v4l2_subdev *sd, switch (sel->target) { case V4L2_SEL_TGT_CROP: { - format = v4l2_subdev_get_pad_format(sd, sd_state, 0); + format = v4l2_subdev_state_get_format(sd_state, 0); /* * The sensor moves the readout by 1 pixel based on flips to @@ -1195,8 +1195,8 @@ static int imx290_get_selection(struct v4l2_subdev *sd, } } -static int imx290_entity_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state) +static int imx290_entity_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { .which = V4L2_SUBDEV_FORMAT_TRY, @@ -1221,7 +1221,6 @@ static const struct v4l2_subdev_video_ops imx290_video_ops = { }; static const struct v4l2_subdev_pad_ops imx290_pad_ops = { - .init_cfg = imx290_entity_init_cfg, .enum_mbus_code = imx290_enum_mbus_code, .enum_frame_size = imx290_enum_frame_size, .get_fmt = v4l2_subdev_get_fmt, @@ -1235,6 +1234,10 @@ static const struct v4l2_subdev_ops imx290_subdev_ops = { .pad = &imx290_pad_ops, }; +static const struct v4l2_subdev_internal_ops imx290_internal_ops = { + .init_state = imx290_entity_init_state, +}; + static const struct media_entity_operations imx290_subdev_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -1248,6 +1251,7 @@ static int imx290_subdev_init(struct imx290 *imx290) imx290->current_mode = &imx290_modes_ptr(imx290)[0]; v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops); + imx290->sd.internal_ops = &imx290_internal_ops; imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; imx290->sd.dev = imx290->dev; diff --git a/drivers/media/i2c/imx296.c b/drivers/media/i2c/imx296.c index e87cc1dc038e0d..341b4975727b1e 100644 --- a/drivers/media/i2c/imx296.c +++ b/drivers/media/i2c/imx296.c @@ -362,7 +362,7 @@ static int imx296_s_ctrl(struct v4l2_ctrl *ctrl) return 0; state = v4l2_subdev_get_locked_active_state(&sensor->subdev); - format = v4l2_subdev_get_pad_format(&sensor->subdev, state, 0); + format = v4l2_subdev_state_get_format(state, 0); switch (ctrl->id) { case V4L2_CID_EXPOSURE: @@ -579,8 +579,8 @@ static int imx296_setup(struct imx296 *sensor, struct v4l2_subdev_state *state) unsigned int i; int ret = 0; - format = v4l2_subdev_get_pad_format(&sensor->subdev, state, 0); - crop = v4l2_subdev_get_pad_crop(&sensor->subdev, state, 0); + format = v4l2_subdev_state_get_format(state, 0); + crop = v4l2_subdev_state_get_crop(state, 0); for (i = 0; i < ARRAY_SIZE(imx296_init_table); ++i) imx296_write(sensor, imx296_init_table[i].reg, @@ -757,7 +757,7 @@ static int imx296_enum_frame_size(struct v4l2_subdev *sd, const struct v4l2_mbus_framefmt *format; struct imx296 *sensor = to_imx296(sd); - format = v4l2_subdev_get_pad_format(sd, state, fse->pad); + format = v4l2_subdev_state_get_format(state, fse->pad); /* * Binning does not seem to work on either mono or colour sensor @@ -782,8 +782,8 @@ static int imx296_set_format(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - crop = v4l2_subdev_get_pad_crop(sd, state, fmt->pad); - format = v4l2_subdev_get_pad_format(sd, state, fmt->pad); + crop = v4l2_subdev_state_get_crop(state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); format->width = crop->width; format->height = crop->height; @@ -808,7 +808,7 @@ static int imx296_get_selection(struct v4l2_subdev *sd, { switch (sel->target) { case V4L2_SEL_TGT_CROP: - sel->r = *v4l2_subdev_get_pad_crop(sd, state, sel->pad); + sel->r = *v4l2_subdev_state_get_crop(state, sel->pad); break; case V4L2_SEL_TGT_CROP_DEFAULT: @@ -856,14 +856,14 @@ static int imx296_set_selection(struct v4l2_subdev *sd, rect.height = min_t(unsigned int, rect.height, IMX296_PIXEL_ARRAY_HEIGHT - rect.top); - crop = v4l2_subdev_get_pad_crop(sd, state, sel->pad); + crop = v4l2_subdev_state_get_crop(state, sel->pad); if (rect.width != crop->width || rect.height != crop->height) { /* * Reset the output image size if the crop rectangle size has * been modified. */ - format = v4l2_subdev_get_pad_format(sd, state, sel->pad); + format = v4l2_subdev_state_get_format(state, sel->pad); format->width = rect.width; format->height = rect.height; } @@ -874,8 +874,8 @@ static int imx296_set_selection(struct v4l2_subdev *sd, return 0; } -static int imx296_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int imx296_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct v4l2_subdev_selection sel = { .target = V4L2_SEL_TGT_CROP, @@ -906,7 +906,6 @@ static const struct v4l2_subdev_pad_ops imx296_subdev_pad_ops = { .set_fmt = imx296_set_format, .get_selection = imx296_get_selection, .set_selection = imx296_set_selection, - .init_cfg = imx296_init_cfg, }; static const struct v4l2_subdev_ops imx296_subdev_ops = { @@ -914,12 +913,17 @@ static const struct v4l2_subdev_ops imx296_subdev_ops = { .pad = &imx296_subdev_pad_ops, }; +static const struct v4l2_subdev_internal_ops imx296_internal_ops = { + .init_state = imx296_init_state, +}; + static int imx296_subdev_init(struct imx296 *sensor) { struct i2c_client *client = to_i2c_client(sensor->dev); int ret; v4l2_i2c_subdev_init(&sensor->subdev, client, &imx296_subdev_ops); + sensor->subdev.internal_ops = &imx296_internal_ops; ret = imx296_ctrls_init(sensor); if (ret < 0) diff --git a/drivers/media/i2c/imx319.c b/drivers/media/i2c/imx319.c index 52ebb096e10750..90018d5c1da280 100644 --- a/drivers/media/i2c/imx319.c +++ b/drivers/media/i2c/imx319.c @@ -1862,7 +1862,7 @@ static int imx319_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct imx319 *imx319 = to_imx319(sd); struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); mutex_lock(&imx319->mutex); @@ -2003,10 +2003,9 @@ static int imx319_do_get_pad_format(struct imx319 *imx319, struct v4l2_subdev_format *fmt) { struct v4l2_mbus_framefmt *framefmt; - struct v4l2_subdev *sd = &imx319->sd; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *framefmt; } else { imx319_update_pad_format(imx319, imx319->cur_mode, fmt); @@ -2057,7 +2056,7 @@ imx319_set_pad_format(struct v4l2_subdev *sd, fmt->format.width, fmt->format.height); imx319_update_pad_format(imx319, mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); *framefmt = fmt->format; } else { imx319->cur_mode = mode; diff --git a/drivers/media/i2c/imx334.c b/drivers/media/i2c/imx334.c index d722c9b7cd31d0..61f873ff646abf 100644 --- a/drivers/media/i2c/imx334.c +++ b/drivers/media/i2c/imx334.c @@ -831,7 +831,7 @@ static int imx334_get_pad_format(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *framefmt; - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *framefmt; } else { fmt->format.code = imx334->cur_code; @@ -872,7 +872,7 @@ static int imx334_set_pad_format(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *framefmt; - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); *framefmt = fmt->format; } else if (imx334->cur_mode != mode || imx334->cur_code != fmt->format.code) { imx334->cur_code = fmt->format.code; @@ -887,14 +887,14 @@ static int imx334_set_pad_format(struct v4l2_subdev *sd, } /** - * imx334_init_pad_cfg() - Initialize sub-device pad configuration + * imx334_init_state() - Initialize sub-device state * @sd: pointer to imx334 V4L2 sub-device structure * @sd_state: V4L2 sub-device state * * Return: 0 if successful, error code otherwise. */ -static int imx334_init_pad_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int imx334_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct imx334 *imx334 = to_imx334(sd); struct v4l2_subdev_format fmt = { 0 }; @@ -1149,7 +1149,6 @@ static const struct v4l2_subdev_video_ops imx334_video_ops = { }; static const struct v4l2_subdev_pad_ops imx334_pad_ops = { - .init_cfg = imx334_init_pad_cfg, .enum_mbus_code = imx334_enum_mbus_code, .enum_frame_size = imx334_enum_frame_size, .get_fmt = imx334_get_pad_format, @@ -1161,6 +1160,10 @@ static const struct v4l2_subdev_ops imx334_subdev_ops = { .pad = &imx334_pad_ops, }; +static const struct v4l2_subdev_internal_ops imx334_internal_ops = { + .init_state = imx334_init_state, +}; + /** * imx334_power_on() - Sensor power on sequence * @dev: pointer to i2c device @@ -1313,6 +1316,7 @@ static int imx334_probe(struct i2c_client *client) /* Initialize subdev */ v4l2_i2c_subdev_init(&imx334->sd, client, &imx334_subdev_ops); + imx334->sd.internal_ops = &imx334_internal_ops; ret = imx334_parse_hw_config(imx334); if (ret) { diff --git a/drivers/media/i2c/imx335.c b/drivers/media/i2c/imx335.c index cb3f4fc66a1740..0e332d4922e21c 100644 --- a/drivers/media/i2c/imx335.c +++ b/drivers/media/i2c/imx335.c @@ -580,7 +580,7 @@ static int imx335_get_pad_format(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *framefmt; - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *framefmt; } else { imx335_fill_pad_format(imx335, imx335->cur_mode, fmt); @@ -615,7 +615,7 @@ static int imx335_set_pad_format(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *framefmt; - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); *framefmt = fmt->format; } else { ret = imx335_update_controls(imx335, mode); @@ -629,14 +629,14 @@ static int imx335_set_pad_format(struct v4l2_subdev *sd, } /** - * imx335_init_pad_cfg() - Initialize sub-device pad configuration + * imx335_init_state() - Initialize sub-device state * @sd: pointer to imx335 V4L2 sub-device structure * @sd_state: V4L2 sub-device configuration * * Return: 0 if successful, error code otherwise. */ -static int imx335_init_pad_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int imx335_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct imx335 *imx335 = to_imx335(sd); struct v4l2_subdev_format fmt = { 0 }; @@ -864,7 +864,6 @@ static const struct v4l2_subdev_video_ops imx335_video_ops = { }; static const struct v4l2_subdev_pad_ops imx335_pad_ops = { - .init_cfg = imx335_init_pad_cfg, .enum_mbus_code = imx335_enum_mbus_code, .enum_frame_size = imx335_enum_frame_size, .get_fmt = imx335_get_pad_format, @@ -876,6 +875,10 @@ static const struct v4l2_subdev_ops imx335_subdev_ops = { .pad = &imx335_pad_ops, }; +static const struct v4l2_subdev_internal_ops imx335_internal_ops = { + .init_state = imx335_init_state, +}; + /** * imx335_power_on() - Sensor power on sequence * @dev: pointer to i2c device @@ -1039,6 +1042,7 @@ static int imx335_probe(struct i2c_client *client) /* Initialize subdev */ v4l2_i2c_subdev_init(&imx335->sd, client, &imx335_subdev_ops); + imx335->sd.internal_ops = &imx335_internal_ops; ret = imx335_parse_hw_config(imx335); if (ret) { diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 059a41b7eefc49..5c68fd948ff65d 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -1161,7 +1161,7 @@ static int imx355_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct imx355 *imx355 = to_imx355(sd); struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); mutex_lock(&imx355->mutex); @@ -1302,10 +1302,9 @@ static int imx355_do_get_pad_format(struct imx355 *imx355, struct v4l2_subdev_format *fmt) { struct v4l2_mbus_framefmt *framefmt; - struct v4l2_subdev *sd = &imx355->sd; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *framefmt; } else { imx355_update_pad_format(imx355, imx355->cur_mode, fmt); @@ -1356,7 +1355,7 @@ imx355_set_pad_format(struct v4l2_subdev *sd, fmt->format.width, fmt->format.height); imx355_update_pad_format(imx355, mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); *framefmt = fmt->format; } else { imx355->cur_mode = mode; diff --git a/drivers/media/i2c/imx412.c b/drivers/media/i2c/imx412.c index 8597f98a8dcf80..6ce9b92972c690 100644 --- a/drivers/media/i2c/imx412.c +++ b/drivers/media/i2c/imx412.c @@ -722,7 +722,7 @@ static int imx412_get_pad_format(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *framefmt; - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *framefmt; } else { imx412_fill_pad_format(imx412, imx412->cur_mode, fmt); @@ -757,7 +757,7 @@ static int imx412_set_pad_format(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *framefmt; - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); *framefmt = fmt->format; } else { ret = imx412_update_controls(imx412, mode); @@ -771,14 +771,14 @@ static int imx412_set_pad_format(struct v4l2_subdev *sd, } /** - * imx412_init_pad_cfg() - Initialize sub-device pad configuration + * imx412_init_state() - Initialize sub-device state * @sd: pointer to imx412 V4L2 sub-device structure * @sd_state: V4L2 sub-device configuration * * Return: 0 if successful, error code otherwise. */ -static int imx412_init_pad_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int imx412_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct imx412 *imx412 = to_imx412(sd); struct v4l2_subdev_format fmt = { 0 }; @@ -1005,7 +1005,6 @@ static const struct v4l2_subdev_video_ops imx412_video_ops = { }; static const struct v4l2_subdev_pad_ops imx412_pad_ops = { - .init_cfg = imx412_init_pad_cfg, .enum_mbus_code = imx412_enum_mbus_code, .enum_frame_size = imx412_enum_frame_size, .get_fmt = imx412_get_pad_format, @@ -1017,6 +1016,10 @@ static const struct v4l2_subdev_ops imx412_subdev_ops = { .pad = &imx412_pad_ops, }; +static const struct v4l2_subdev_internal_ops imx412_internal_ops = { + .init_state = imx412_init_state, +}; + /** * imx412_power_on() - Sensor power on sequence * @dev: pointer to i2c device @@ -1185,6 +1188,7 @@ static int imx412_probe(struct i2c_client *client) /* Initialize subdev */ v4l2_i2c_subdev_init(&imx412->sd, client, &imx412_subdev_ops); + imx412->sd.internal_ops = &imx412_internal_ops; ret = imx412_parse_hw_config(imx412); if (ret) { diff --git a/drivers/media/i2c/imx415.c b/drivers/media/i2c/imx415.c index 3f00172df3cc38..1f6e9f138c0531 100644 --- a/drivers/media/i2c/imx415.c +++ b/drivers/media/i2c/imx415.c @@ -547,7 +547,7 @@ static int imx415_s_ctrl(struct v4l2_ctrl *ctrl) return 0; state = v4l2_subdev_get_locked_active_state(&sensor->subdev); - format = v4l2_subdev_get_pad_format(&sensor->subdev, state, 0); + format = v4l2_subdev_state_get_format(state, 0); switch (ctrl->id) { case V4L2_CID_EXPOSURE: @@ -830,7 +830,7 @@ static int imx415_enum_frame_size(struct v4l2_subdev *sd, { const struct v4l2_mbus_framefmt *format; - format = v4l2_subdev_get_pad_format(sd, state, fse->pad); + format = v4l2_subdev_state_get_format(state, fse->pad); if (fse->index > 0 || fse->code != format->code) return -EINVAL; @@ -846,7 +846,7 @@ static int imx415_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_subdev_format *fmt) { - fmt->format = *v4l2_subdev_get_pad_format(sd, state, fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(state, fmt->pad); return 0; } @@ -857,7 +857,7 @@ static int imx415_set_format(struct v4l2_subdev *sd, { struct v4l2_mbus_framefmt *format; - format = v4l2_subdev_get_pad_format(sd, state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); format->width = fmt->format.width; format->height = fmt->format.height; @@ -891,8 +891,8 @@ static int imx415_get_selection(struct v4l2_subdev *sd, return -EINVAL; } -static int imx415_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int imx415_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct v4l2_subdev_format format = { .format = { @@ -916,7 +916,6 @@ static const struct v4l2_subdev_pad_ops imx415_subdev_pad_ops = { .get_fmt = imx415_get_format, .set_fmt = imx415_set_format, .get_selection = imx415_get_selection, - .init_cfg = imx415_init_cfg, }; static const struct v4l2_subdev_ops imx415_subdev_ops = { @@ -924,12 +923,17 @@ static const struct v4l2_subdev_ops imx415_subdev_ops = { .pad = &imx415_subdev_pad_ops, }; +static const struct v4l2_subdev_internal_ops imx415_internal_ops = { + .init_state = imx415_init_state, +}; + static int imx415_subdev_init(struct imx415 *sensor) { struct i2c_client *client = to_i2c_client(sensor->dev); int ret; v4l2_i2c_subdev_init(&sensor->subdev, client, &imx415_subdev_ops); + sensor->subdev.internal_ops = &imx415_internal_ops; ret = imx415_ctrls_init(sensor); if (ret) diff --git a/drivers/media/i2c/imx477.c b/drivers/media/i2c/imx477.c index 8e9d89e88de3c9..df4a66c7dc07ac 100644 --- a/drivers/media/i2c/imx477.c +++ b/drivers/media/i2c/imx477.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -112,17 +113,8 @@ MODULE_PARM_DESC(trigger_mode, "Set vsync trigger mode: 1=source, 2=sink"); #define IMX477_REG_XVS_IO_CTRL 0x3040 #define IMX477_REG_EXTOUT_EN 0x4b81 -/* Embedded metadata stream structure */ -#define IMX477_EMBEDDED_LINE_WIDTH 16384 -#define IMX477_NUM_EMBEDDED_LINES 1 - -enum pad_types { - IMAGE_PAD, - METADATA_PAD, - NUM_PADS -}; - /* IMX477 native and active pixel array size. */ +#define IMX477_NATIVE_FORMAT MEDIA_BUS_FMT_SRGGB12_1X12 #define IMX477_NATIVE_WIDTH 4072U #define IMX477_NATIVE_HEIGHT 3176U #define IMX477_PIXEL_ARRAY_LEFT 8U @@ -130,6 +122,9 @@ enum pad_types { #define IMX477_PIXEL_ARRAY_WIDTH 4056U #define IMX477_PIXEL_ARRAY_HEIGHT 3040U +/* Embedded metadata stream height */ +#define IMX477_EMBEDDED_DATA_HEIGHT 2U + struct imx477_reg { u16 address; u8 val; @@ -1076,7 +1071,7 @@ static const struct imx477_mode supported_modes_10bit[] = { * - v flip * - h&v flips */ -static const u32 codes[] = { +static const u32 imx477_codes[] = { /* 12-bit modes. */ MEDIA_BUS_FMT_SRGGB12_1X12, MEDIA_BUS_FMT_SGRBG12_1X12, @@ -1126,6 +1121,18 @@ static const char * const imx477_supply_name[] = { #define IMX477_XCLR_MIN_DELAY_US 8000 #define IMX477_XCLR_DELAY_RANGE_US 1000 +enum imx477_pad_ids { + IMX477_PAD_SOURCE, + IMX477_PAD_IMAGE, + IMX477_PAD_EDATA, + IMX477_NUM_PADS, +}; + +enum imx477_stream_ids { + IMX477_STREAM_IMAGE, + IMX477_STREAM_EDATA, +}; + struct imx477_compatible_data { unsigned int chip_id; struct imx477_reg_list extra_regs; @@ -1133,9 +1140,7 @@ struct imx477_compatible_data { struct imx477 { struct v4l2_subdev sd; - struct media_pad pad[NUM_PADS]; - - unsigned int fmt_code; + struct media_pad pads[IMX477_NUM_PADS]; struct clk *xclk; u32 xclk_freq; @@ -1161,15 +1166,6 @@ struct imx477 { /* Trigger mode */ int trigger_mode_of; - /* - * Mutex for serialized access: - * Protect sensor module set pad format and start/stop streaming safely. - */ - struct mutex mutex; - - /* Streaming on/off */ - bool streaming; - /* Rewrite common registers on stream on? */ bool common_regs_written; @@ -1289,62 +1285,17 @@ static u32 imx477_get_format_code(struct imx477 *imx477, u32 code) { unsigned int i; - lockdep_assert_held(&imx477->mutex); - - for (i = 0; i < ARRAY_SIZE(codes); i++) - if (codes[i] == code) + for (i = 0; i < ARRAY_SIZE(imx477_codes); i++) + if (imx477_codes[i] == code) break; - if (i >= ARRAY_SIZE(codes)) + if (i >= ARRAY_SIZE(imx477_codes)) i = 0; i = (i & ~3) | (imx477->vflip->val ? 2 : 0) | (imx477->hflip->val ? 1 : 0); - return codes[i]; -} - -static void imx477_set_default_format(struct imx477 *imx477) -{ - /* Set default mode to max resolution */ - imx477->mode = &supported_modes_12bit[0]; - imx477->fmt_code = MEDIA_BUS_FMT_SRGGB12_1X12; -} - -static int imx477_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - struct imx477 *imx477 = to_imx477(sd); - struct v4l2_mbus_framefmt *try_fmt_img = - v4l2_subdev_get_try_format(sd, fh->state, IMAGE_PAD); - struct v4l2_mbus_framefmt *try_fmt_meta = - v4l2_subdev_get_try_format(sd, fh->state, METADATA_PAD); - struct v4l2_rect *try_crop; - - mutex_lock(&imx477->mutex); - - /* Initialize try_fmt for the image pad */ - try_fmt_img->width = supported_modes_12bit[0].width; - try_fmt_img->height = supported_modes_12bit[0].height; - try_fmt_img->code = imx477_get_format_code(imx477, - MEDIA_BUS_FMT_SRGGB12_1X12); - try_fmt_img->field = V4L2_FIELD_NONE; - - /* Initialize try_fmt for the embedded metadata pad */ - try_fmt_meta->width = IMX477_EMBEDDED_LINE_WIDTH; - try_fmt_meta->height = IMX477_NUM_EMBEDDED_LINES; - try_fmt_meta->code = MEDIA_BUS_FMT_SENSOR_DATA; - try_fmt_meta->field = V4L2_FIELD_NONE; - - /* Initialize try_crop */ - try_crop = v4l2_subdev_get_try_crop(sd, fh->state, IMAGE_PAD); - try_crop->left = IMX477_PIXEL_ARRAY_LEFT; - try_crop->top = IMX477_PIXEL_ARRAY_TOP; - try_crop->width = IMX477_PIXEL_ARRAY_WIDTH; - try_crop->height = IMX477_PIXEL_ARRAY_HEIGHT; - - mutex_unlock(&imx477->mutex); - - return 0; + return imx477_codes[i]; } static void imx477_adjust_exposure_range(struct imx477 *imx477) @@ -1385,8 +1336,11 @@ static int imx477_set_ctrl(struct v4l2_ctrl *ctrl) struct imx477 *imx477 = container_of(ctrl->handler, struct imx477, ctrl_handler); struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd); + struct v4l2_subdev_state *state; int ret = 0; + state = v4l2_subdev_get_locked_active_state(&imx477->sd); + /* * The VBLANK control may change the limits of usable exposure, so check * and adjust if necessary. @@ -1473,20 +1427,52 @@ static int imx477_enum_mbus_code(struct v4l2_subdev *sd, { struct imx477 *imx477 = to_imx477(sd); - if (code->pad >= NUM_PADS) + if (code->pad >= IMX477_NUM_PADS) return -EINVAL; - if (code->pad == IMAGE_PAD) { - if (code->index >= (ARRAY_SIZE(codes) / 4)) + switch (code->pad) { + case IMX477_PAD_IMAGE: + /* The internal image pad is hardwired to the native format. */ + if (code->index > 0) + return -EINVAL; + + code->code = IMX477_NATIVE_FORMAT; + return 0; + + case IMX477_PAD_EDATA: + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_CCS_EMBEDDED; + return 0; + + case IMX477_PAD_SOURCE: + default: + break; + } + + /* + * On the source pad, the sensor supports multiple image raw formats + * with different bit depths. The embedded data format bit depth + * follows the image stream. + */ + if (code->stream == IMX477_STREAM_IMAGE) { + u32 format; + + if (code->index >= (ARRAY_SIZE(imx477_codes) / 4)) return -EINVAL; - code->code = imx477_get_format_code(imx477, - codes[code->index * 4]); + format = imx477_codes[code->index * 4]; + code->code = imx477_get_format_code(imx477, format); } else { + struct v4l2_mbus_framefmt *fmt; + if (code->index > 0) return -EINVAL; - code->code = MEDIA_BUS_FMT_SENSOR_DATA; + fmt = v4l2_subdev_state_get_format(sd_state, IMX477_PAD_SOURCE, + IMX477_STREAM_EDATA); + code->code = fmt->code; } return 0; @@ -1498,19 +1484,39 @@ static int imx477_enum_frame_size(struct v4l2_subdev *sd, { struct imx477 *imx477 = to_imx477(sd); - if (fse->pad >= NUM_PADS) - return -EINVAL; + switch (fse->pad) { + case IMX477_PAD_IMAGE: + if (fse->code != IMX477_NATIVE_FORMAT || fse->index > 0) + return -EINVAL; + + fse->min_width = IMX477_NATIVE_WIDTH; + fse->max_width = IMX477_NATIVE_WIDTH; + fse->min_height = IMX477_NATIVE_HEIGHT; + fse->max_height = IMX477_NATIVE_HEIGHT; + return 0; + + case IMX477_PAD_EDATA: + if (fse->code != MEDIA_BUS_FMT_CCS_EMBEDDED || fse->index > 0) + return -EINVAL; - if (fse->pad == IMAGE_PAD) { + fse->min_width = IMX477_NATIVE_WIDTH; + fse->max_width = IMX477_NATIVE_WIDTH; + fse->min_height = IMX477_EMBEDDED_DATA_HEIGHT; + fse->max_height = IMX477_EMBEDDED_DATA_HEIGHT; + return 0; + + case IMX477_PAD_SOURCE: + default: + break; + } + + if (fse->stream == IMX477_STREAM_IMAGE) { const struct imx477_mode *mode_list; unsigned int num_modes; get_mode_table(fse->code, &mode_list, &num_modes); - - if (fse->index >= num_modes) - return -EINVAL; - - if (fse->code != imx477_get_format_code(imx477, fse->code)) + if (fse->code != imx477_get_format_code(imx477, fse->code) || + fse->index >= num_modes) return -EINVAL; fse->min_width = mode_list[fse->index].width; @@ -1518,78 +1524,22 @@ static int imx477_enum_frame_size(struct v4l2_subdev *sd, fse->min_height = mode_list[fse->index].height; fse->max_height = fse->min_height; } else { - if (fse->code != MEDIA_BUS_FMT_SENSOR_DATA || fse->index > 0) - return -EINVAL; - - fse->min_width = IMX477_EMBEDDED_LINE_WIDTH; - fse->max_width = fse->min_width; - fse->min_height = IMX477_NUM_EMBEDDED_LINES; - fse->max_height = fse->min_height; - } - - return 0; -} - -static void imx477_reset_colorspace(struct v4l2_mbus_framefmt *fmt) -{ - fmt->colorspace = V4L2_COLORSPACE_RAW; - fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); - fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, - fmt->colorspace, - fmt->ycbcr_enc); - fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); -} - -static void imx477_update_image_pad_format(struct imx477 *imx477, - const struct imx477_mode *mode, - struct v4l2_subdev_format *fmt) -{ - fmt->format.width = mode->width; - fmt->format.height = mode->height; - fmt->format.field = V4L2_FIELD_NONE; - imx477_reset_colorspace(&fmt->format); -} - -static void imx477_update_metadata_pad_format(struct v4l2_subdev_format *fmt) -{ - fmt->format.width = IMX477_EMBEDDED_LINE_WIDTH; - fmt->format.height = IMX477_NUM_EMBEDDED_LINES; - fmt->format.code = MEDIA_BUS_FMT_SENSOR_DATA; - fmt->format.field = V4L2_FIELD_NONE; -} + struct v4l2_mbus_framefmt *fmt; -static int imx477_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct imx477 *imx477 = to_imx477(sd); + fmt = v4l2_subdev_state_get_format(sd_state, IMX477_PAD_SOURCE, + IMX477_STREAM_EDATA); + if (fse->code != fmt->code) + return -EINVAL; - if (fmt->pad >= NUM_PADS) - return -EINVAL; + if (fse->index) + return -EINVAL; - mutex_lock(&imx477->mutex); - - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(&imx477->sd, sd_state, - fmt->pad); - /* update the code which could change due to vflip or hflip: */ - try_fmt->code = fmt->pad == IMAGE_PAD ? - imx477_get_format_code(imx477, try_fmt->code) : - MEDIA_BUS_FMT_SENSOR_DATA; - fmt->format = *try_fmt; - } else { - if (fmt->pad == IMAGE_PAD) { - imx477_update_image_pad_format(imx477, imx477->mode, - fmt); - fmt->format.code = - imx477_get_format_code(imx477, imx477->fmt_code); - } else { - imx477_update_metadata_pad_format(fmt); - } + fse->min_width = fmt->width; + fse->max_width = fmt->width; + fse->min_height = IMX477_EMBEDDED_DATA_HEIGHT; + fse->max_height = IMX477_EMBEDDED_DATA_HEIGHT; } - mutex_unlock(&imx477->mutex); return 0; } @@ -1636,106 +1586,271 @@ static void imx477_set_framing_limits(struct imx477 *imx477) __v4l2_ctrl_s_ctrl(imx477->hblank, hblank_min); } +static int imx477_format_bpp(u32 code) +{ + switch (code) { + case MEDIA_BUS_FMT_SRGGB12_1X12: + case MEDIA_BUS_FMT_SGRBG12_1X12: + case MEDIA_BUS_FMT_SGBRG12_1X12: + case MEDIA_BUS_FMT_SBGGR12_1X12: + return 12; + + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SBGGR10_1X10: + default: + return 10; + } +} + +static int imx477_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) +{ + const struct v4l2_mbus_framefmt *fmt; + struct v4l2_subdev_state *state; + u32 code; + + if (pad != IMX477_PAD_SOURCE) + return -EINVAL; + + state = v4l2_subdev_lock_and_get_active_state(sd); + fmt = v4l2_subdev_state_get_format(state, IMX477_PAD_SOURCE, + IMX477_STREAM_IMAGE); + code = fmt->code; + v4l2_subdev_unlock_state(state); + + fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; + fd->num_entries = 2; + + memset(fd->entry, 0, sizeof(fd->entry)); + + fd->entry[0].pixelcode = code; + fd->entry[0].stream = IMX477_STREAM_IMAGE; + fd->entry[0].bus.csi2.vc = 0; + fd->entry[0].bus.csi2.dt = imx477_format_bpp(code) == 10 + ? MIPI_CSI2_DT_RAW10 : MIPI_CSI2_DT_RAW12; + + fd->entry[1].pixelcode = code; + fd->entry[1].stream = IMX477_STREAM_EDATA; + fd->entry[1].bus.csi2.vc = 0; + fd->entry[1].bus.csi2.dt = MIPI_CSI2_DT_EMBEDDED_8B; + + return 0; +} + static int imx477_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { - struct v4l2_mbus_framefmt *framefmt; - const struct imx477_mode *mode; struct imx477 *imx477 = to_imx477(sd); + struct v4l2_mbus_framefmt *format, *ed_format; + const struct imx477_mode *mode_list; + const struct imx477_mode *mode; + struct v4l2_rect *compose; + struct v4l2_rect *crop; + unsigned int num_modes; - if (fmt->pad >= NUM_PADS) - return -EINVAL; + /* + * The driver is mode-based, the format can be set on the source pad + * only, and only for the image stream. + */ + if (fmt->pad != IMX477_PAD_SOURCE || fmt->stream != IMX477_STREAM_IMAGE) + return v4l2_subdev_get_fmt(sd, sd_state, fmt); - mutex_lock(&imx477->mutex); + get_mode_table(fmt->format.code, &mode_list, &num_modes); - if (fmt->pad == IMAGE_PAD) { - const struct imx477_mode *mode_list; - unsigned int num_modes; + /* + * Adjust the requested format to match the closest mode. The Bayer + * order varies with flips. + */ + mode = v4l2_find_nearest_size(mode_list, num_modes, width, height, + fmt->format.width, fmt->format.height); - /* Bayer order varies with flips */ - fmt->format.code = imx477_get_format_code(imx477, - fmt->format.code); - - get_mode_table(fmt->format.code, &mode_list, &num_modes); - - mode = v4l2_find_nearest_size(mode_list, - num_modes, - width, height, - fmt->format.width, - fmt->format.height); - imx477_update_image_pad_format(imx477, mode, fmt); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); - *framefmt = fmt->format; - } else if (imx477->mode != mode) { - imx477->mode = mode; - imx477->fmt_code = fmt->format.code; - imx477_set_framing_limits(imx477); - } - } else { - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); - *framefmt = fmt->format; - } else { - /* Only one embedded data mode is supported */ - imx477_update_metadata_pad_format(fmt); - } - } + fmt->format.code = imx477_get_format_code(imx477, fmt->format.code); + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + fmt->format.colorspace = V4L2_COLORSPACE_RAW; + fmt->format.ycbcr_enc = + V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->format.colorspace); + fmt->format.xfer_func = + V4L2_MAP_XFER_FUNC_DEFAULT(fmt->format.colorspace); + fmt->format.quantization = + V4L2_MAP_QUANTIZATION_DEFAULT(true, fmt->format.colorspace, + fmt->format.ycbcr_enc); + + /* Propagate the format through the sensor. */ + + /* The image pad models the pixel array, and thus has a fixed size. */ + format = v4l2_subdev_state_get_format(sd_state, IMX477_PAD_IMAGE); + *format = fmt->format; + format->code = IMX477_NATIVE_FORMAT; + format->width = IMX477_NATIVE_WIDTH; + format->height = IMX477_NATIVE_HEIGHT; + + /* Get the crop rectangle from the mode list */ + crop = v4l2_subdev_state_get_crop(sd_state, IMX477_PAD_IMAGE); + *crop = mode->crop; + + /* The compose rectangle size is the sensor output size. */ + compose = v4l2_subdev_state_get_compose(sd_state, IMX477_PAD_IMAGE); + compose->left = 0; + compose->top = 0; + compose->width = fmt->format.width; + compose->height = fmt->format.height; + + /* + * No mode use digital crop, the source pad crop rectangle size and + * format are thus identical to the image pad compose rectangle. + */ + crop = v4l2_subdev_state_get_crop(sd_state, IMX477_PAD_SOURCE, + IMX477_STREAM_IMAGE); + crop->left = 0; + crop->top = 0; + crop->width = fmt->format.width; + crop->height = fmt->format.height; + + format = v4l2_subdev_state_get_format(sd_state, IMX477_PAD_SOURCE, + IMX477_STREAM_IMAGE); + *format = fmt->format; - mutex_unlock(&imx477->mutex); + /* + * Finally, update the formats on the sink and source sides of the + * embedded data stream. + */ + ed_format = v4l2_subdev_state_get_format(sd_state, IMX477_PAD_EDATA); + ed_format->code = MEDIA_BUS_FMT_CCS_EMBEDDED; + ed_format->width = format->width; + ed_format->height = IMX477_EMBEDDED_DATA_HEIGHT; + ed_format->field = V4L2_FIELD_NONE; + + format = v4l2_subdev_state_get_format(sd_state, IMX477_PAD_SOURCE, + IMX477_STREAM_EDATA); + *format = *ed_format; + format->code = imx477_format_bpp(format->code) == 10 + ? MEDIA_BUS_FMT_META_10 : MEDIA_BUS_FMT_META_12; + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + imx477->mode = mode; + imx477_set_framing_limits(imx477); + } return 0; } -static const struct v4l2_rect * -__imx477_get_pad_crop(struct imx477 *imx477, - struct v4l2_subdev_state *sd_state, - unsigned int pad, enum v4l2_subdev_format_whence which) +static int imx477_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&imx477->sd, sd_state, pad); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &imx477->mode->crop; - } + struct v4l2_subdev_route routes[2] = { + { + .sink_pad = IMX477_PAD_IMAGE, + .sink_stream = 0, + .source_pad = IMX477_PAD_SOURCE, + .source_stream = IMX477_STREAM_IMAGE, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }, { + .sink_pad = IMX477_PAD_EDATA, + .sink_stream = 0, + .source_pad = IMX477_PAD_SOURCE, + .source_stream = IMX477_STREAM_EDATA, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }, + }; + struct v4l2_subdev_krouting routing = { + .len_routes = ARRAY_SIZE(routes), + .num_routes = ARRAY_SIZE(routes), + .routes = routes, + }; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .pad = IMX477_PAD_SOURCE, + .stream = IMX477_STREAM_IMAGE, + .format = { + .code = IMX477_NATIVE_FORMAT, + .width = supported_modes_12bit[0].width, + .height = supported_modes_12bit[0].height, + .colorspace = V4L2_COLORSPACE_RAW, + .ycbcr_enc = + V4L2_MAP_YCBCR_ENC_DEFAULT(V4L2_COLORSPACE_RAW), + .xfer_func = + V4L2_MAP_XFER_FUNC_DEFAULT(V4L2_COLORSPACE_RAW), + .quantization = + V4L2_MAP_QUANTIZATION_DEFAULT + (true, V4L2_COLORSPACE_RAW, + V4L2_MAP_YCBCR_ENC_DEFAULT(V4L2_COLORSPACE_RAW)) + }, + }; + int ret; - return NULL; + ret = v4l2_subdev_set_routing(sd, state, &routing); + if (ret) + return ret; + + /* + * Set the image stream format on the source pad. This will be + * propagated to all formats and selection rectangles internally. + */ + imx477_set_pad_format(sd, state, &fmt); + + return 0; } static int imx477_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { - switch (sel->target) { - case V4L2_SEL_TGT_CROP: { - struct imx477 *imx477 = to_imx477(sd); + struct v4l2_rect *compose; - mutex_lock(&imx477->mutex); - sel->r = *__imx477_get_pad_crop(imx477, sd_state, sel->pad, - sel->which); - mutex_unlock(&imx477->mutex); + /* + * The embedded data stream doesn't support selection rectangles, + * neither on the embedded data pad nor on the source pad. + */ + if (sel->pad == IMX477_PAD_EDATA || sel->stream != 0) + return -EINVAL; + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(sd_state, sel->pad); return 0; - } case V4L2_SEL_TGT_NATIVE_SIZE: + if (sel->pad != IMX477_PAD_IMAGE) + return -EINVAL; + sel->r.left = 0; sel->r.top = 0; sel->r.width = IMX477_NATIVE_WIDTH; sel->r.height = IMX477_NATIVE_HEIGHT; - return 0; case V4L2_SEL_TGT_CROP_DEFAULT: case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.left = IMX477_PIXEL_ARRAY_LEFT; - sel->r.top = IMX477_PIXEL_ARRAY_TOP; - sel->r.width = IMX477_PIXEL_ARRAY_WIDTH; - sel->r.height = IMX477_PIXEL_ARRAY_HEIGHT; + switch (sel->pad) { + case IMX477_PAD_IMAGE: + sel->r.top = IMX477_PIXEL_ARRAY_TOP; + sel->r.left = IMX477_PIXEL_ARRAY_LEFT; + sel->r.width = IMX477_PIXEL_ARRAY_WIDTH; + sel->r.height = IMX477_PIXEL_ARRAY_HEIGHT; + return 0; + + case IMX477_PAD_SOURCE: + compose = v4l2_subdev_state_get_compose(sd_state, + IMX477_PAD_IMAGE); + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = compose->width; + sel->r.height = compose->height; + return 0; + } + + break; + + case V4L2_SEL_TGT_COMPOSE: + if (sel->pad != IMX477_PAD_IMAGE) + return -EINVAL; + sel->r = *v4l2_subdev_state_get_compose(sd_state, sel->pad); return 0; } @@ -1750,6 +1865,10 @@ static int imx477_start_streaming(struct imx477 *imx477) const struct imx477_reg_list *extra_regs; int ret, tm; + ret = pm_runtime_resume_and_get(&client->dev); + if (ret < 0) + return ret; + if (!imx477->common_regs_written) { ret = imx477_write_regs(imx477, mode_common_regs, ARRAY_SIZE(mode_common_regs)); @@ -1769,7 +1888,7 @@ static int imx477_start_streaming(struct imx477 *imx477) if (ret) { dev_err(&client->dev, "%s failed to set common settings\n", __func__); - return ret; + goto error_pm_put; } imx477->common_regs_written = true; } @@ -1779,7 +1898,7 @@ static int imx477_start_streaming(struct imx477 *imx477) ret = imx477_write_regs(imx477, reg_list->regs, reg_list->num_of_regs); if (ret) { dev_err(&client->dev, "%s failed to set mode\n", __func__); - return ret; + goto error_pm_put; } /* Set on-sensor DPC. */ @@ -1789,7 +1908,7 @@ static int imx477_start_streaming(struct imx477 *imx477) /* Apply customized values from user */ ret = __v4l2_ctrl_handler_setup(imx477->sd.ctrl_handler); if (ret) - return ret; + goto error_pm_put; /* Set vsync trigger mode: 0=standalone, 1=source, 2=sink */ tm = (imx477->trigger_mode_of >= 0) ? imx477->trigger_mode_of : trigger_mode; @@ -1803,8 +1922,16 @@ static int imx477_start_streaming(struct imx477 *imx477) IMX477_REG_VALUE_08BIT, (tm == 1) ? 1 : 0); /* set stream on register */ - return imx477_write_reg(imx477, IMX477_REG_MODE_SELECT, - IMX477_REG_VALUE_08BIT, IMX477_MODE_STREAMING); + ret = imx477_write_reg(imx477, IMX477_REG_MODE_SELECT, + IMX477_REG_VALUE_08BIT, IMX477_MODE_STREAMING); + if (ret) + goto error_pm_put; + + return 0; + +error_pm_put: + pm_runtime_put(&client->dev); + return ret; } /* Stop streaming */ @@ -1822,55 +1949,49 @@ static void imx477_stop_streaming(struct imx477 *imx477) /* Stop driving XVS out (there is still a weak pull-up) */ imx477_write_reg(imx477, IMX477_REG_EXTOUT_EN, IMX477_REG_VALUE_08BIT, 0); + + pm_runtime_put(&client->dev); } -static int imx477_set_stream(struct v4l2_subdev *sd, int enable) +static int imx477_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) { struct imx477 *imx477 = to_imx477(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret = 0; - mutex_lock(&imx477->mutex); - if (imx477->streaming == enable) { - mutex_unlock(&imx477->mutex); + /* + * The image stream controls sensor streaming, as embedded data isn't + * controllable independently. + */ + if (!(streams_mask & BIT(IMX477_STREAM_IMAGE))) return 0; - } - - if (enable) { - ret = pm_runtime_get_sync(&client->dev); - if (ret < 0) { - pm_runtime_put_noidle(&client->dev); - goto err_unlock; - } - /* - * Apply default & customized values - * and then start streaming. - */ - ret = imx477_start_streaming(imx477); - if (ret) - goto err_rpm_put; - } else { - imx477_stop_streaming(imx477); - pm_runtime_put(&client->dev); - } + /* vflip and hflip cannot change during streaming */ + __v4l2_ctrl_grab(imx477->vflip, true); + __v4l2_ctrl_grab(imx477->hflip, true); - imx477->streaming = enable; + return imx477_start_streaming(imx477); +} - /* vflip and hflip cannot change during streaming */ - __v4l2_ctrl_grab(imx477->vflip, enable); - __v4l2_ctrl_grab(imx477->hflip, enable); +static int imx477_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct imx477 *imx477 = to_imx477(sd); - mutex_unlock(&imx477->mutex); + /* + * The image stream controls sensor streaming, as embedded data isn't + * controllable independently. + */ + if (!(streams_mask & BIT(IMX477_STREAM_IMAGE))) + return 0; - return ret; + __v4l2_ctrl_grab(imx477->vflip, false); + __v4l2_ctrl_grab(imx477->hflip, false); -err_rpm_put: - pm_runtime_put(&client->dev); -err_unlock: - mutex_unlock(&imx477->mutex); + imx477_stop_streaming(imx477); - return ret; + return 0; } /* Power/clock management functions */ @@ -1923,39 +2044,6 @@ static int imx477_power_off(struct device *dev) return 0; } -static int __maybe_unused imx477_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct imx477 *imx477 = to_imx477(sd); - - if (imx477->streaming) - imx477_stop_streaming(imx477); - - return 0; -} - -static int __maybe_unused imx477_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct imx477 *imx477 = to_imx477(sd); - int ret; - - if (imx477->streaming) { - ret = imx477_start_streaming(imx477); - if (ret) - goto error; - } - - return 0; - -error: - imx477_stop_streaming(imx477); - imx477->streaming = 0; - return ret; -} - static int imx477_get_regulators(struct imx477 *imx477) { struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd); @@ -2000,26 +2088,24 @@ static const struct v4l2_subdev_core_ops imx477_core_ops = { .unsubscribe_event = v4l2_event_subdev_unsubscribe, }; -static const struct v4l2_subdev_video_ops imx477_video_ops = { - .s_stream = imx477_set_stream, -}; - static const struct v4l2_subdev_pad_ops imx477_pad_ops = { .enum_mbus_code = imx477_enum_mbus_code, - .get_fmt = imx477_get_pad_format, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = imx477_set_pad_format, .get_selection = imx477_get_selection, .enum_frame_size = imx477_enum_frame_size, + .get_frame_desc = imx477_get_frame_desc, + .enable_streams = imx477_enable_streams, + .disable_streams = imx477_disable_streams, }; static const struct v4l2_subdev_ops imx477_subdev_ops = { .core = &imx477_core_ops, - .video = &imx477_video_ops, .pad = &imx477_pad_ops, }; static const struct v4l2_subdev_internal_ops imx477_internal_ops = { - .open = imx477_open, + .init_state = imx477_init_state, }; /* Initialize control handlers */ @@ -2036,9 +2122,6 @@ static int imx477_init_controls(struct imx477 *imx477) if (ret) return ret; - mutex_init(&imx477->mutex); - ctrl_hdlr->lock = &imx477->mutex; - /* By default, PIXEL_RATE is read only */ imx477->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops, V4L2_CID_PIXEL_RATE, @@ -2058,7 +2141,7 @@ static int imx477_init_controls(struct imx477 *imx477) /* * Create the controls here, but mode specific limits are setup - * in the imx477_set_framing_limits() call below. + * in the imx477_set_framing_limits() call. */ imx477->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops, V4L2_CID_VBLANK, 0, 0xffff, 1, 0); @@ -2128,18 +2211,10 @@ static int imx477_init_controls(struct imx477 *imx477) imx477->sd.ctrl_handler = ctrl_hdlr; - mutex_lock(&imx477->mutex); - - /* Setup exposure and frame/line length limits. */ - imx477_set_framing_limits(imx477); - - mutex_unlock(&imx477->mutex); - return 0; error: v4l2_ctrl_handler_free(ctrl_hdlr); - mutex_destroy(&imx477->mutex); return ret; } @@ -2147,7 +2222,6 @@ static int imx477_init_controls(struct imx477 *imx477) static void imx477_free_controls(struct imx477 *imx477) { v4l2_ctrl_handler_free(imx477->sd.ctrl_handler); - mutex_destroy(&imx477->mutex); } static int imx477_check_hwcfg(struct device *dev, struct imx477 *imx477) @@ -2246,6 +2320,7 @@ static int imx477_probe(struct i2c_client *client) return -ENOMEM; v4l2_i2c_subdev_init(&imx477->sd, client, &imx477_subdev_ops); + imx477->sd.internal_ops = &imx477_internal_ops; match = of_match_device(imx477_dt_ids, dev); if (!match) @@ -2297,43 +2372,58 @@ static int imx477_probe(struct i2c_client *client) if (ret) goto error_power_off; - /* Initialize default format */ - imx477_set_default_format(imx477); - - /* Enable runtime PM and turn off the device */ - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - pm_runtime_idle(dev); - /* This needs the pm runtime to be registered. */ ret = imx477_init_controls(imx477); if (ret) goto error_power_off; /* Initialize subdev */ - imx477->sd.internal_ops = &imx477_internal_ops; imx477->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | - V4L2_SUBDEV_FL_HAS_EVENTS; + V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_STREAMS; imx477->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; - /* Initialize source pads */ - imx477->pad[IMAGE_PAD].flags = MEDIA_PAD_FL_SOURCE; - imx477->pad[METADATA_PAD].flags = MEDIA_PAD_FL_SOURCE; - - ret = media_entity_pads_init(&imx477->sd.entity, NUM_PADS, imx477->pad); + /* + * Initialize the pads. To preserve backward compatibility with + * userspace that used the sensor before the introduction of the + * internal pads, the external source pad is numbered 0 and the internal + * image and embedded data pads numbered 1 and 2 respectively. + */ + imx477->pads[IMX477_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + imx477->pads[IMX477_PAD_IMAGE].flags = MEDIA_PAD_FL_SINK | + MEDIA_PAD_FL_INTERNAL; + imx477->pads[IMX477_PAD_EDATA].flags = MEDIA_PAD_FL_SINK | + MEDIA_PAD_FL_INTERNAL; + + ret = media_entity_pads_init(&imx477->sd.entity, + ARRAY_SIZE(imx477->pads), imx477->pads); if (ret) { dev_err(dev, "failed to init entity pads: %d\n", ret); goto error_handler_free; } + imx477->sd.state_lock = imx477->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&imx477->sd); + if (ret < 0) { + dev_err(dev, "subdev init error: %d\n", ret); + goto error_media_entity; + } + ret = v4l2_async_register_subdev_sensor(&imx477->sd); if (ret < 0) { dev_err(dev, "failed to register sensor sub-device: %d\n", ret); - goto error_media_entity; + goto error_subdev_cleanup; } + /* Enable runtime PM and turn off the device */ + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + return 0; +error_subdev_cleanup: + v4l2_subdev_cleanup(&imx477->sd); + error_media_entity: media_entity_cleanup(&imx477->sd.entity); @@ -2341,8 +2431,6 @@ static int imx477_probe(struct i2c_client *client) imx477_free_controls(imx477); error_power_off: - pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); imx477_power_off(&client->dev); return ret; @@ -2366,7 +2454,6 @@ static void imx477_remove(struct i2c_client *client) MODULE_DEVICE_TABLE(of, imx477_dt_ids); static const struct dev_pm_ops imx477_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(imx477_suspend, imx477_resume) SET_RUNTIME_PM_OPS(imx477_power_off, imx477_power_on, NULL) }; diff --git a/drivers/media/i2c/imx500.c b/drivers/media/i2c/imx500.c index 15e7d3f7dccdc4..c03e51ea90435f 100644 --- a/drivers/media/i2c/imx500.c +++ b/drivers/media/i2c/imx500.c @@ -2105,8 +2105,8 @@ static int imx500_get_pad_format(struct v4l2_subdev *sd, mutex_lock(&imx500->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_get_try_format( - &imx500->sd, sd_state, fmt->pad); + struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_state_get_format(sd_state, + fmt->pad); /* update the code which could change due to vflip or hflip */ try_fmt->code = fmt->pad == IMAGE_PAD ? imx500_get_format_code(imx500) : @@ -2174,8 +2174,8 @@ static int imx500_set_pad_format(struct v4l2_subdev *sd, fmt->format.height); imx500_update_image_pad_format(imx500, mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, + fmt->pad); *framefmt = fmt->format; } else if (imx500->mode != mode) { imx500->mode = mode; @@ -2184,8 +2184,8 @@ static int imx500_set_pad_format(struct v4l2_subdev *sd, } } else { if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, + fmt->pad); *framefmt = fmt->format; } else { /* Only one embedded data mode is supported */ @@ -2204,7 +2204,7 @@ __imx500_get_pad_crop(struct imx500 *imx500, struct v4l2_subdev_state *sd_state, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&imx500->sd, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &imx500->mode->crop; } diff --git a/drivers/media/i2c/imx519.c b/drivers/media/i2c/imx519.c index 7cb63e0187641f..5e93c26575184d 100644 --- a/drivers/media/i2c/imx519.c +++ b/drivers/media/i2c/imx519.c @@ -2,8 +2,9 @@ /* * A V4L2 driver for Sony IMX519 cameras. * Copyright (C) 2021 Arducam Technology co., Ltd. + * Copyright (C) 2024 Raspberry Pi Ltd * - * Based on Sony IMX477 camera driver + * Based on Sony IMX519 camera driver * Copyright (C) 2020 Raspberry Pi (Trading) Ltd */ #include @@ -15,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -66,7 +68,7 @@ #define IMX519_ANA_GAIN_DEFAULT 0x0 /* Digital gain control */ -#define IMX519_REG_DIGITAL_GAIN 0x020e +#define IMX519_REG_DIGITAL_GAIN 0x020ef #define IMX519_DGTL_GAIN_MIN 0x0100 #define IMX519_DGTL_GAIN_MAX 0xffff #define IMX519_DGTL_GAIN_DEFAULT 0x0100 @@ -93,17 +95,11 @@ #define IMX519_TEST_PATTERN_B_DEFAULT 0 #define IMX519_TEST_PATTERN_GB_DEFAULT 0 -/* Embedded metadata stream structure */ -#define IMX519_EMBEDDED_LINE_WIDTH (5820 * 3) -#define IMX519_NUM_EMBEDDED_LINES 1 - -enum pad_types { - IMAGE_PAD, - METADATA_PAD, - NUM_PADS -}; +/* Embedded metadata stream height */ +#define IMX519_EMBEDDED_DATA_HEIGHT 3U /* IMX519 native and active pixel array size. */ +#define IMX519_NATIVE_FORMAT MEDIA_BUS_FMT_SRGGB10_1X10 #define IMX519_NATIVE_WIDTH 4672U #define IMX519_NATIVE_HEIGHT 3648U #define IMX519_PIXEL_ARRAY_LEFT 8U @@ -1024,11 +1020,22 @@ static const char * const imx519_supply_name[] = { #define IMX519_XCLR_MIN_DELAY_US 8000 #define IMX519_XCLR_DELAY_RANGE_US 1000 + +enum imx519_pad_ids { + IMX519_PAD_SOURCE, + IMX519_PAD_IMAGE, + IMX519_PAD_EDATA, + IMX519_NUM_PADS, +}; + +enum imx519_stream_ids { + IMX519_STREAM_IMAGE, + IMX519_STREAM_EDATA, +}; + struct imx519 { struct v4l2_subdev sd; - struct media_pad pad[NUM_PADS]; - - unsigned int fmt_code; + struct media_pad pads[IMX519_NUM_PADS]; struct clk *xclk; @@ -1047,15 +1054,6 @@ struct imx519 { /* Current mode */ const struct imx519_mode *mode; - /* - * Mutex for serialized access: - * Protect sensor module set pad format and start/stop streaming safely. - */ - struct mutex mutex; - - /* Streaming on/off */ - bool streaming; - /* Rewrite common registers on stream on? */ bool common_regs_written; @@ -1145,49 +1143,12 @@ static u32 imx519_get_format_code(struct imx519 *imx519) { unsigned int i; - lockdep_assert_held(&imx519->mutex); - i = (imx519->vflip->val ? 2 : 0) | (imx519->hflip->val ? 1 : 0); return codes[i]; } -static int imx519_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - struct imx519 *imx519 = to_imx519(sd); - struct v4l2_mbus_framefmt *try_fmt_img = - v4l2_subdev_get_try_format(sd, fh->state, IMAGE_PAD); - struct v4l2_mbus_framefmt *try_fmt_meta = - v4l2_subdev_get_try_format(sd, fh->state, METADATA_PAD); - struct v4l2_rect *try_crop; - - mutex_lock(&imx519->mutex); - - /* Initialize try_fmt for the image pad */ - try_fmt_img->width = supported_modes_10bit[0].width; - try_fmt_img->height = supported_modes_10bit[0].height; - try_fmt_img->code = imx519_get_format_code(imx519); - try_fmt_img->field = V4L2_FIELD_NONE; - - /* Initialize try_fmt for the embedded metadata pad */ - try_fmt_meta->width = IMX519_EMBEDDED_LINE_WIDTH; - try_fmt_meta->height = IMX519_NUM_EMBEDDED_LINES; - try_fmt_meta->code = MEDIA_BUS_FMT_SENSOR_DATA; - try_fmt_meta->field = V4L2_FIELD_NONE; - - /* Initialize try_crop */ - try_crop = v4l2_subdev_get_try_crop(sd, fh->state, IMAGE_PAD); - try_crop->left = IMX519_PIXEL_ARRAY_LEFT; - try_crop->top = IMX519_PIXEL_ARRAY_TOP; - try_crop->width = IMX519_PIXEL_ARRAY_WIDTH; - try_crop->height = IMX519_PIXEL_ARRAY_HEIGHT; - - mutex_unlock(&imx519->mutex); - - return 0; -} - static void imx519_adjust_exposure_range(struct imx519 *imx519) { int exposure_max, exposure_def; @@ -1226,8 +1187,11 @@ static int imx519_set_ctrl(struct v4l2_ctrl *ctrl) struct imx519 *imx519 = container_of(ctrl->handler, struct imx519, ctrl_handler); struct i2c_client *client = v4l2_get_subdevdata(&imx519->sd); + struct v4l2_subdev_state *state; int ret = 0; + state = v4l2_subdev_get_locked_active_state(&imx519->sd); + /* * The VBLANK control may change the limits of usable exposure, so check * and adjust if necessary. @@ -1304,25 +1268,59 @@ static const struct v4l2_ctrl_ops imx519_ctrl_ops = { .s_ctrl = imx519_set_ctrl, }; + static int imx519_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct imx519 *imx519 = to_imx519(sd); - if (code->pad >= NUM_PADS) + if (code->pad >= IMX519_NUM_PADS) return -EINVAL; - if (code->pad == IMAGE_PAD) { + switch (code->pad) { + case IMX519_PAD_IMAGE: + /* The internal image pad is hardwired to the native format. */ if (code->index > 0) return -EINVAL; + code->code = IMX519_NATIVE_FORMAT; + return 0; + + case IMX519_PAD_EDATA: + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_CCS_EMBEDDED; + return 0; + + case IMX519_PAD_SOURCE: + default: + break; + } + + /* + * On the source pad, the sensor supports multiple image raw formats + * with different bit depths. The embedded data format bit depth + * follows the image stream. + */ + if (code->stream == IMX519_STREAM_IMAGE) { + u32 format; + + if (code->index >= (ARRAY_SIZE(codes) / 4)) + return -EINVAL; + + format = codes[code->index * 4]; code->code = imx519_get_format_code(imx519); } else { + struct v4l2_mbus_framefmt *fmt; + if (code->index > 0) return -EINVAL; - code->code = MEDIA_BUS_FMT_SENSOR_DATA; + fmt = v4l2_subdev_state_get_format(sd_state, IMX519_PAD_SOURCE, + IMX519_STREAM_EDATA); + code->code = fmt->code; } return 0; @@ -1334,14 +1332,35 @@ static int imx519_enum_frame_size(struct v4l2_subdev *sd, { struct imx519 *imx519 = to_imx519(sd); - if (fse->pad >= NUM_PADS) - return -EINVAL; + switch (fse->pad) { + case IMX519_PAD_IMAGE: + if (fse->code != IMX519_NATIVE_FORMAT || fse->index > 0) + return -EINVAL; - if (fse->pad == IMAGE_PAD) { - if (fse->index >= ARRAY_SIZE(supported_modes_10bit)) + fse->min_width = IMX519_NATIVE_WIDTH; + fse->max_width = IMX519_NATIVE_WIDTH; + fse->min_height = IMX519_NATIVE_HEIGHT; + fse->max_height = IMX519_NATIVE_HEIGHT; + return 0; + + case IMX519_PAD_EDATA: + if (fse->code != MEDIA_BUS_FMT_CCS_EMBEDDED || fse->index > 0) return -EINVAL; - if (fse->code != imx519_get_format_code(imx519)) + fse->min_width = IMX519_NATIVE_WIDTH; + fse->max_width = IMX519_NATIVE_WIDTH; + fse->min_height = IMX519_EMBEDDED_DATA_HEIGHT; + fse->max_height = IMX519_EMBEDDED_DATA_HEIGHT; + return 0; + + case IMX519_PAD_SOURCE: + default: + break; + } + + if (fse->stream == IMX519_STREAM_IMAGE) { + if (fse->code != imx519_get_format_code(imx519) || + fse->index >= ARRAY_SIZE(supported_modes_10bit)) return -EINVAL; fse->min_width = supported_modes_10bit[fse->index].width; @@ -1349,78 +1368,56 @@ static int imx519_enum_frame_size(struct v4l2_subdev *sd, fse->min_height = supported_modes_10bit[fse->index].height; fse->max_height = fse->min_height; } else { - if (fse->code != MEDIA_BUS_FMT_SENSOR_DATA || fse->index > 0) + struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_state_get_format(sd_state, IMX519_PAD_SOURCE, + IMX519_STREAM_EDATA); + if (fse->code != fmt->code) return -EINVAL; - fse->min_width = IMX519_EMBEDDED_LINE_WIDTH; - fse->max_width = fse->min_width; - fse->min_height = IMX519_NUM_EMBEDDED_LINES; - fse->max_height = fse->min_height; + if (fse->index) + return -EINVAL; + + fse->min_width = fmt->width; + fse->max_width = fmt->width; + fse->min_height = IMX519_EMBEDDED_DATA_HEIGHT; + fse->max_height = IMX519_EMBEDDED_DATA_HEIGHT; } return 0; } -static void imx519_reset_colorspace(struct v4l2_mbus_framefmt *fmt) +static int imx519_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) { - fmt->colorspace = V4L2_COLORSPACE_RAW; - fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); - fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, - fmt->colorspace, - fmt->ycbcr_enc); - fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); -} + const struct v4l2_mbus_framefmt *fmt; + struct v4l2_subdev_state *state; + u32 code; -static void imx519_update_image_pad_format(struct imx519 *imx519, - const struct imx519_mode *mode, - struct v4l2_subdev_format *fmt) -{ - fmt->format.width = mode->width; - fmt->format.height = mode->height; - fmt->format.field = V4L2_FIELD_NONE; - imx519_reset_colorspace(&fmt->format); -} + if (pad != IMX519_PAD_SOURCE) + return -EINVAL; -static void imx519_update_metadata_pad_format(struct v4l2_subdev_format *fmt) -{ - fmt->format.width = IMX519_EMBEDDED_LINE_WIDTH; - fmt->format.height = IMX519_NUM_EMBEDDED_LINES; - fmt->format.code = MEDIA_BUS_FMT_SENSOR_DATA; - fmt->format.field = V4L2_FIELD_NONE; -} + state = v4l2_subdev_lock_and_get_active_state(sd); + fmt = v4l2_subdev_state_get_format(state, IMX519_PAD_SOURCE, + IMX519_STREAM_IMAGE); + code = fmt->code; + v4l2_subdev_unlock_state(state); -static int imx519_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct imx519 *imx519 = to_imx519(sd); + fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; + fd->num_entries = 2; - if (fmt->pad >= NUM_PADS) - return -EINVAL; + memset(fd->entry, 0, sizeof(fd->entry)); - mutex_lock(&imx519->mutex); - - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(&imx519->sd, sd_state, - fmt->pad); - /* update the code which could change due to vflip or hflip: */ - try_fmt->code = fmt->pad == IMAGE_PAD ? - imx519_get_format_code(imx519) : - MEDIA_BUS_FMT_SENSOR_DATA; - fmt->format = *try_fmt; - } else { - if (fmt->pad == IMAGE_PAD) { - imx519_update_image_pad_format(imx519, imx519->mode, - fmt); - fmt->format.code = - imx519_get_format_code(imx519); - } else { - imx519_update_metadata_pad_format(fmt); - } - } + fd->entry[0].pixelcode = code; + fd->entry[0].stream = IMX519_STREAM_IMAGE; + fd->entry[0].bus.csi2.vc = 0; + fd->entry[0].bus.csi2.dt = MIPI_CSI2_DT_RAW10; + + fd->entry[1].pixelcode = code; + fd->entry[1].stream = IMX519_STREAM_EDATA; + fd->entry[1].bus.csi2.vc = 0; + fd->entry[1].bus.csi2.dt = MIPI_CSI2_DT_EMBEDDED_8B; - mutex_unlock(&imx519->mutex); return 0; } @@ -1470,99 +1467,217 @@ static void imx519_set_framing_limits(struct imx519 *imx519) __v4l2_ctrl_modify_range(imx519->hblank, hblank, hblank, 1, hblank); } + static int imx519_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { - struct v4l2_mbus_framefmt *framefmt; - const struct imx519_mode *mode; struct imx519 *imx519 = to_imx519(sd); + struct v4l2_mbus_framefmt *format, *ed_format; + const struct imx519_mode *mode; + struct v4l2_rect *compose; + struct v4l2_rect *crop; - if (fmt->pad >= NUM_PADS) - return -EINVAL; + /* + * The driver is mode-based, the format can be set on the source pad + * only, and only for the image stream. + */ + if (fmt->pad != IMX519_PAD_SOURCE || fmt->stream != IMX519_STREAM_IMAGE) + return v4l2_subdev_get_fmt(sd, sd_state, fmt); - mutex_lock(&imx519->mutex); - - if (fmt->pad == IMAGE_PAD) { - /* Bayer order varies with flips */ - fmt->format.code = imx519_get_format_code(imx519); - - mode = v4l2_find_nearest_size(supported_modes_10bit, - ARRAY_SIZE(supported_modes_10bit), - width, height, - fmt->format.width, - fmt->format.height); - imx519_update_image_pad_format(imx519, mode, fmt); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); - *framefmt = fmt->format; - } else { - imx519->mode = mode; - imx519->fmt_code = fmt->format.code; - imx519_set_framing_limits(imx519); - } - } else { - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); - *framefmt = fmt->format; - } else { - /* Only one embedded data mode is supported */ - imx519_update_metadata_pad_format(fmt); - } - } - mutex_unlock(&imx519->mutex); + /* + * Adjust the requested format to match the closest mode. The Bayer + * order varies with flips. + */ + mode = v4l2_find_nearest_size(supported_modes_10bit, + ARRAY_SIZE(supported_modes_10bit), + width, height, fmt->format.width, + fmt->format.height); + + fmt->format.code = imx519_get_format_code(imx519); + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + fmt->format.colorspace = V4L2_COLORSPACE_RAW; + fmt->format.ycbcr_enc = + V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->format.colorspace); + fmt->format.xfer_func = + V4L2_MAP_XFER_FUNC_DEFAULT(fmt->format.colorspace); + fmt->format.quantization = + V4L2_MAP_QUANTIZATION_DEFAULT(true, fmt->format.colorspace, + fmt->format.ycbcr_enc); + + /* Propagate the format through the sensor. */ + + /* The image pad models the pixel array, and thus has a fixed size. */ + format = v4l2_subdev_state_get_format(sd_state, IMX519_PAD_IMAGE); + *format = fmt->format; + format->code = IMX519_NATIVE_FORMAT; + format->width = IMX519_NATIVE_WIDTH; + format->height = IMX519_NATIVE_HEIGHT; + + /* Get the crop rectangle from the mode list */ + crop = v4l2_subdev_state_get_crop(sd_state, IMX519_PAD_IMAGE); + *crop = mode->crop; + + /* The compose rectangle size is the sensor output size. */ + compose = v4l2_subdev_state_get_compose(sd_state, IMX519_PAD_IMAGE); + compose->left = 0; + compose->top = 0; + compose->width = fmt->format.width; + compose->height = fmt->format.height; + + /* + * No mode use digital crop, the source pad crop rectangle size and + * format are thus identical to the image pad compose rectangle. + */ + crop = v4l2_subdev_state_get_crop(sd_state, IMX519_PAD_SOURCE, + IMX519_STREAM_IMAGE); + crop->left = 0; + crop->top = 0; + crop->width = fmt->format.width; + crop->height = fmt->format.height; + + format = v4l2_subdev_state_get_format(sd_state, IMX519_PAD_SOURCE, + IMX519_STREAM_IMAGE); + *format = fmt->format; + + /* + * Finally, update the formats on the sink and source sides of the + * embedded data stream. + */ + ed_format = v4l2_subdev_state_get_format(sd_state, IMX519_PAD_EDATA); + ed_format->code = MEDIA_BUS_FMT_CCS_EMBEDDED; + ed_format->width = format->width; + ed_format->height = IMX519_EMBEDDED_DATA_HEIGHT; + ed_format->field = V4L2_FIELD_NONE; + + format = v4l2_subdev_state_get_format(sd_state, IMX519_PAD_SOURCE, + IMX519_STREAM_EDATA); + *format = *ed_format; + format->code = MEDIA_BUS_FMT_META_10; + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + imx519->mode = mode; + imx519_set_framing_limits(imx519); + } return 0; } -static const struct v4l2_rect * -__imx519_get_pad_crop(struct imx519 *imx519, struct v4l2_subdev_state *sd_state, - unsigned int pad, enum v4l2_subdev_format_whence which) +static int imx519_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&imx519->sd, sd_state, pad); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &imx519->mode->crop; - } + struct v4l2_subdev_route routes[2] = { + { + .sink_pad = IMX519_PAD_IMAGE, + .sink_stream = 0, + .source_pad = IMX519_PAD_SOURCE, + .source_stream = IMX519_STREAM_IMAGE, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }, { + .sink_pad = IMX519_PAD_EDATA, + .sink_stream = 0, + .source_pad = IMX519_PAD_SOURCE, + .source_stream = IMX519_STREAM_EDATA, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }, + }; + struct v4l2_subdev_krouting routing = { + .len_routes = ARRAY_SIZE(routes), + .num_routes = ARRAY_SIZE(routes), + .routes = routes, + }; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .pad = IMX519_PAD_SOURCE, + .stream = IMX519_STREAM_IMAGE, + .format = { + .code = IMX519_NATIVE_FORMAT, + .width = supported_modes_10bit[0].width, + .height = supported_modes_10bit[0].height, + .colorspace = V4L2_COLORSPACE_RAW, + .ycbcr_enc = + V4L2_MAP_YCBCR_ENC_DEFAULT(V4L2_COLORSPACE_RAW), + .xfer_func = + V4L2_MAP_XFER_FUNC_DEFAULT(V4L2_COLORSPACE_RAW), + .quantization = + V4L2_MAP_QUANTIZATION_DEFAULT + (true, V4L2_COLORSPACE_RAW, + V4L2_MAP_YCBCR_ENC_DEFAULT(V4L2_COLORSPACE_RAW)) + }, + }; + int ret; + + ret = v4l2_subdev_set_routing(sd, state, &routing); + if (ret) + return ret; + + /* + * Set the image stream format on the source pad. This will be + * propagated to all formats and selection rectangles internally. + */ + imx519_set_pad_format(sd, state, &fmt); - return NULL; + return 0; } static int imx519_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { - switch (sel->target) { - case V4L2_SEL_TGT_CROP: { - struct imx519 *imx519 = to_imx519(sd); + struct v4l2_rect *compose; - mutex_lock(&imx519->mutex); - sel->r = *__imx519_get_pad_crop(imx519, sd_state, sel->pad, - sel->which); - mutex_unlock(&imx519->mutex); + /* + * The embedded data stream doesn't support selection rectangles, + * neither on the embedded data pad nor on the source pad. + */ + if (sel->pad == IMX519_PAD_EDATA || sel->stream != 0) + return -EINVAL; + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(sd_state, sel->pad); return 0; - } case V4L2_SEL_TGT_NATIVE_SIZE: + if (sel->pad != IMX519_PAD_IMAGE) + return -EINVAL; + sel->r.left = 0; sel->r.top = 0; sel->r.width = IMX519_NATIVE_WIDTH; sel->r.height = IMX519_NATIVE_HEIGHT; - return 0; case V4L2_SEL_TGT_CROP_DEFAULT: case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.left = IMX519_PIXEL_ARRAY_LEFT; - sel->r.top = IMX519_PIXEL_ARRAY_TOP; - sel->r.width = IMX519_PIXEL_ARRAY_WIDTH; - sel->r.height = IMX519_PIXEL_ARRAY_HEIGHT; + switch (sel->pad) { + case IMX519_PAD_IMAGE: + sel->r.top = IMX519_PIXEL_ARRAY_TOP; + sel->r.left = IMX519_PIXEL_ARRAY_LEFT; + sel->r.width = IMX519_PIXEL_ARRAY_WIDTH; + sel->r.height = IMX519_PIXEL_ARRAY_HEIGHT; + return 0; + + case IMX519_PAD_SOURCE: + compose = v4l2_subdev_state_get_compose(sd_state, + IMX519_PAD_IMAGE); + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = compose->width; + sel->r.height = compose->height; + return 0; + } + + break; + case V4L2_SEL_TGT_COMPOSE: + if (sel->pad != IMX519_PAD_IMAGE) + return -EINVAL; + + sel->r = *v4l2_subdev_state_get_compose(sd_state, sel->pad); return 0; } @@ -1576,6 +1691,10 @@ static int imx519_start_streaming(struct imx519 *imx519) const struct imx519_reg_list *reg_list; int ret; + ret = pm_runtime_resume_and_get(&client->dev); + if (ret < 0) + goto error_pm_put; + if (!imx519->common_regs_written) { ret = imx519_write_regs(imx519, mode_common_regs, ARRAY_SIZE(mode_common_regs)); @@ -1583,7 +1702,7 @@ static int imx519_start_streaming(struct imx519 *imx519) if (ret) { dev_err(&client->dev, "%s failed to set common settings\n", __func__); - return ret; + goto error_pm_put; } imx519->common_regs_written = true; } @@ -1593,17 +1712,25 @@ static int imx519_start_streaming(struct imx519 *imx519) ret = imx519_write_regs(imx519, reg_list->regs, reg_list->num_of_regs); if (ret) { dev_err(&client->dev, "%s failed to set mode\n", __func__); - return ret; + goto error_pm_put; } /* Apply customized values from user */ ret = __v4l2_ctrl_handler_setup(imx519->sd.ctrl_handler); if (ret) - return ret; + goto error_pm_put; /* set stream on register */ - return imx519_write_reg(imx519, IMX519_REG_MODE_SELECT, - IMX519_REG_VALUE_08BIT, IMX519_MODE_STREAMING); + ret = imx519_write_reg(imx519, IMX519_REG_MODE_SELECT, + IMX519_REG_VALUE_08BIT, IMX519_MODE_STREAMING); + if (ret) + goto error_pm_put; + + return 0; + +error_pm_put: + pm_runtime_put(&client->dev); + return ret; } /* Stop streaming */ @@ -1617,55 +1744,49 @@ static void imx519_stop_streaming(struct imx519 *imx519) IMX519_REG_VALUE_08BIT, IMX519_MODE_STANDBY); if (ret) dev_err(&client->dev, "%s failed to set stream\n", __func__); + + pm_runtime_put(&client->dev); } -static int imx519_set_stream(struct v4l2_subdev *sd, int enable) +static int imx519_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) { struct imx519 *imx519 = to_imx519(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret = 0; - mutex_lock(&imx519->mutex); - if (imx519->streaming == enable) { - mutex_unlock(&imx519->mutex); + /* + * The image stream controls sensor streaming, as embedded data isn't + * controllable independently. + */ + if (!(streams_mask & BIT(IMX519_STREAM_IMAGE))) return 0; - } - - if (enable) { - ret = pm_runtime_get_sync(&client->dev); - if (ret < 0) { - pm_runtime_put_noidle(&client->dev); - goto err_unlock; - } - /* - * Apply default & customized values - * and then start streaming. - */ - ret = imx519_start_streaming(imx519); - if (ret) - goto err_rpm_put; - } else { - imx519_stop_streaming(imx519); - pm_runtime_put(&client->dev); - } + /* vflip and hflip cannot change during streaming */ + __v4l2_ctrl_grab(imx519->vflip, true); + __v4l2_ctrl_grab(imx519->hflip, true); - imx519->streaming = enable; + return imx519_start_streaming(imx519); +} - /* vflip and hflip cannot change during streaming */ - __v4l2_ctrl_grab(imx519->vflip, enable); - __v4l2_ctrl_grab(imx519->hflip, enable); +static int imx519_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct imx519 *imx519 = to_imx519(sd); - mutex_unlock(&imx519->mutex); + /* + * The image stream controls sensor streaming, as embedded data isn't + * controllable independently. + */ + if (!(streams_mask & BIT(IMX519_STREAM_IMAGE))) + return 0; - return ret; + __v4l2_ctrl_grab(imx519->vflip, false); + __v4l2_ctrl_grab(imx519->hflip, false); -err_rpm_put: - pm_runtime_put(&client->dev); -err_unlock: - mutex_unlock(&imx519->mutex); + imx519_stop_streaming(imx519); - return ret; + return 0; } /* Power/clock management functions */ @@ -1718,39 +1839,6 @@ static int imx519_power_off(struct device *dev) return 0; } -static int __maybe_unused imx519_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct imx519 *imx519 = to_imx519(sd); - - if (imx519->streaming) - imx519_stop_streaming(imx519); - - return 0; -} - -static int __maybe_unused imx519_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct imx519 *imx519 = to_imx519(sd); - int ret; - - if (imx519->streaming) { - ret = imx519_start_streaming(imx519); - if (ret) - goto error; - } - - return 0; - -error: - imx519_stop_streaming(imx519); - imx519->streaming = 0; - return ret; -} - static int imx519_get_regulators(struct imx519 *imx519) { struct i2c_client *client = v4l2_get_subdevdata(&imx519->sd); @@ -1795,26 +1883,24 @@ static const struct v4l2_subdev_core_ops imx519_core_ops = { .unsubscribe_event = v4l2_event_subdev_unsubscribe, }; -static const struct v4l2_subdev_video_ops imx519_video_ops = { - .s_stream = imx519_set_stream, -}; - static const struct v4l2_subdev_pad_ops imx519_pad_ops = { .enum_mbus_code = imx519_enum_mbus_code, - .get_fmt = imx519_get_pad_format, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = imx519_set_pad_format, .get_selection = imx519_get_selection, .enum_frame_size = imx519_enum_frame_size, + .get_frame_desc = imx519_get_frame_desc, + .enable_streams = imx519_enable_streams, + .disable_streams = imx519_disable_streams, }; static const struct v4l2_subdev_ops imx519_subdev_ops = { .core = &imx519_core_ops, - .video = &imx519_video_ops, .pad = &imx519_pad_ops, }; static const struct v4l2_subdev_internal_ops imx519_internal_ops = { - .open = imx519_open, + .init_state = imx519_init_state, }; /* Initialize control handlers */ @@ -1832,9 +1918,6 @@ static int imx519_init_controls(struct imx519 *imx519) if (ret) return ret; - mutex_init(&imx519->mutex); - ctrl_hdlr->lock = &imx519->mutex; - /* By default, PIXEL_RATE is read only */ imx519->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx519_ctrl_ops, V4L2_CID_PIXEL_RATE, @@ -1853,7 +1936,7 @@ static int imx519_init_controls(struct imx519 *imx519) /* * Create the controls here, but mode specific limits are setup - * in the imx519_set_framing_limits() call below. + * in the imx519_set_framing_limits() call. */ imx519->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx519_ctrl_ops, V4L2_CID_VBLANK, 0, 0xffff, 1, 0); @@ -1927,14 +2010,10 @@ static int imx519_init_controls(struct imx519 *imx519) imx519->sd.ctrl_handler = ctrl_hdlr; - /* Setup exposure and frame/line length limits. */ - imx519_set_framing_limits(imx519); - return 0; error: v4l2_ctrl_handler_free(ctrl_hdlr); - mutex_destroy(&imx519->mutex); return ret; } @@ -1942,7 +2021,6 @@ static int imx519_init_controls(struct imx519 *imx519) static void imx519_free_controls(struct imx519 *imx519) { v4l2_ctrl_handler_free(imx519->sd.ctrl_handler); - mutex_destroy(&imx519->mutex); } static int imx519_check_hwcfg(struct device *dev) @@ -2010,6 +2088,7 @@ static int imx519_probe(struct i2c_client *client) return -ENOMEM; v4l2_i2c_subdev_init(&imx519->sd, client, &imx519_subdev_ops); + imx519->sd.internal_ops = &imx519_internal_ops; match = of_match_device(imx519_dt_ids, dev); if (!match) @@ -2055,14 +2134,6 @@ static int imx519_probe(struct i2c_client *client) if (ret) goto error_power_off; - /* Set default mode to max resolution */ - imx519->mode = &supported_modes_10bit[0]; - imx519->fmt_code = MEDIA_BUS_FMT_SRGGB10_1X10; - - /* Enable runtime PM and turn off the device */ - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - pm_runtime_idle(dev); /* This needs the pm runtime to be registered. */ ret = imx519_init_controls(imx519); @@ -2072,27 +2143,51 @@ static int imx519_probe(struct i2c_client *client) /* Initialize subdev */ imx519->sd.internal_ops = &imx519_internal_ops; imx519->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | - V4L2_SUBDEV_FL_HAS_EVENTS; + V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_STREAMS; imx519->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; - /* Initialize source pads */ - imx519->pad[IMAGE_PAD].flags = MEDIA_PAD_FL_SOURCE; - imx519->pad[METADATA_PAD].flags = MEDIA_PAD_FL_SOURCE; - - ret = media_entity_pads_init(&imx519->sd.entity, NUM_PADS, imx519->pad); + /* + * Initialize the pads. To preserve backward compatibility with + * userspace that used the sensor before the introduction of the + * internal pads, the external source pad is numbered 0 and the internal + * image and embedded data pads numbered 1 and 2 respectively. + */ + imx519->pads[IMX519_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + imx519->pads[IMX519_PAD_IMAGE].flags = MEDIA_PAD_FL_SINK | + MEDIA_PAD_FL_INTERNAL; + imx519->pads[IMX519_PAD_EDATA].flags = MEDIA_PAD_FL_SINK | + MEDIA_PAD_FL_INTERNAL; + + ret = media_entity_pads_init(&imx519->sd.entity, + ARRAY_SIZE(imx519->pads), imx519->pads); if (ret) { dev_err(dev, "failed to init entity pads: %d\n", ret); goto error_handler_free; } + imx519->sd.state_lock = imx519->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&imx519->sd); + if (ret < 0) { + dev_err(dev, "subdev init error: %d\n", ret); + goto error_media_entity; + } + ret = v4l2_async_register_subdev_sensor(&imx519->sd); if (ret < 0) { dev_err(dev, "failed to register sensor sub-device: %d\n", ret); - goto error_media_entity; + goto error_subdev_cleanup; } + /* Enable runtime PM and turn off the device */ + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + return 0; +error_subdev_cleanup: + v4l2_subdev_cleanup(&imx519->sd); + error_media_entity: media_entity_cleanup(&imx519->sd.entity); @@ -2100,8 +2195,6 @@ static int imx519_probe(struct i2c_client *client) imx519_free_controls(imx519); error_power_off: - pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); imx519_power_off(&client->dev); return ret; @@ -2125,7 +2218,6 @@ static void imx519_remove(struct i2c_client *client) MODULE_DEVICE_TABLE(of, imx519_dt_ids); static const struct dev_pm_ops imx519_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(imx519_suspend, imx519_resume) SET_RUNTIME_PM_OPS(imx519_power_off, imx519_power_on, NULL) }; diff --git a/drivers/media/i2c/imx708.c b/drivers/media/i2c/imx708.c index 29b1d4479c508a..5cdc84dbdfa527 100644 --- a/drivers/media/i2c/imx708.c +++ b/drivers/media/i2c/imx708.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -118,22 +119,8 @@ MODULE_PARM_DESC(qbc_adjust, "Quad Bayer broken line correction strength [0,2-5] #define IMX708_LPF_INTENSITY_DISABLED 0x01 #define IMX708_LPF_INTENSITY 0xC429 -/* - * Metadata buffer holds a variety of data, all sent with the same VC/DT (0x12). - * It comprises two scanlines (of up to 5760 bytes each, for 4608 pixels) - * of embedded data, one line of PDAF data, and two lines of AE-HIST data - * (AE histograms are valid for HDR mode and empty in non-HDR modes). - */ -#define IMX708_EMBEDDED_LINE_WIDTH (5 * 5760) -#define IMX708_NUM_EMBEDDED_LINES 1 - -enum pad_types { - IMAGE_PAD, - METADATA_PAD, - NUM_PADS -}; - /* IMX708 native and active pixel array size. */ +#define IMX708_NATIVE_FORMAT MEDIA_BUS_FMT_SRGGB10_1X10 #define IMX708_NATIVE_WIDTH 4640U #define IMX708_NATIVE_HEIGHT 2658U #define IMX708_PIXEL_ARRAY_LEFT 16U @@ -141,6 +128,15 @@ enum pad_types { #define IMX708_PIXEL_ARRAY_WIDTH 4608U #define IMX708_PIXEL_ARRAY_HEIGHT 2592U +/* + * Metadata buffer holds a variety of data, all sent with the same VC/DT (0x12). + * It comprises two scanlines (of up to 5760 bytes each, for 4608 pixels) + * of embedded data, one line of PDAF data, and two lines of AE-HIST data + * (AE histograms are valid for HDR mode and empty in non-HDR modes). + */ +#define IMX708_EMBEDDED_DATA_WIDTH IMX708_PIXEL_ARRAY_WIDTH +#define IMX708_EMBEDDED_DATA_HEIGHT 5U + struct imx708_reg { u16 address; u8 val; @@ -822,11 +818,21 @@ static const char * const imx708_supply_name[] = { #define IMX708_XCLR_MIN_DELAY_US 8000 #define IMX708_XCLR_DELAY_RANGE_US 1000 +enum imx708_pad_ids { + IMX708_PAD_SOURCE, + IMX708_PAD_IMAGE, + IMX708_PAD_EDATA, + IMX708_NUM_PADS, +}; + +enum imx708_stream_ids { + IMX708_STREAM_IMAGE, + IMX708_STREAM_EDATA, +}; + struct imx708 { struct v4l2_subdev sd; - struct media_pad pad[NUM_PADS]; - - struct v4l2_mbus_framefmt fmt; + struct media_pad pads[IMX708_NUM_PADS]; struct clk *inclk; u32 inclk_freq; @@ -850,15 +856,6 @@ struct imx708 { /* Current mode */ const struct imx708_mode *mode; - /* - * Mutex for serialized access: - * Protect sensor module set pad format and start/stop streaming safely. - */ - struct mutex mutex; - - /* Streaming on/off */ - bool streaming; - /* Rewrite common registers on stream on? */ bool common_regs_written; @@ -976,73 +973,12 @@ static u32 imx708_get_format_code(struct imx708 *imx708) { unsigned int i; - lockdep_assert_held(&imx708->mutex); - i = (imx708->vflip->val ? 2 : 0) | (imx708->hflip->val ? 1 : 0); return codes[i]; } -static void imx708_set_default_format(struct imx708 *imx708) -{ - struct v4l2_mbus_framefmt *fmt = &imx708->fmt; - - /* Set default mode to max resolution */ - imx708->mode = &supported_modes_10bit_no_hdr[0]; - - /* fmt->code not set as it will always be computed based on flips */ - fmt->colorspace = V4L2_COLORSPACE_RAW; - fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); - fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, - fmt->colorspace, - fmt->ycbcr_enc); - fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); - fmt->width = imx708->mode->width; - fmt->height = imx708->mode->height; - fmt->field = V4L2_FIELD_NONE; -} - -static int imx708_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - struct imx708 *imx708 = to_imx708(sd); - struct v4l2_mbus_framefmt *try_fmt_img = - v4l2_subdev_get_try_format(sd, fh->state, IMAGE_PAD); - struct v4l2_mbus_framefmt *try_fmt_meta = - v4l2_subdev_get_try_format(sd, fh->state, METADATA_PAD); - struct v4l2_rect *try_crop; - - mutex_lock(&imx708->mutex); - - /* Initialize try_fmt for the image pad */ - if (imx708->hdr_mode->val) { - try_fmt_img->width = supported_modes_10bit_hdr[0].width; - try_fmt_img->height = supported_modes_10bit_hdr[0].height; - } else { - try_fmt_img->width = supported_modes_10bit_no_hdr[0].width; - try_fmt_img->height = supported_modes_10bit_no_hdr[0].height; - } - try_fmt_img->code = imx708_get_format_code(imx708); - try_fmt_img->field = V4L2_FIELD_NONE; - - /* Initialize try_fmt for the embedded metadata pad */ - try_fmt_meta->width = IMX708_EMBEDDED_LINE_WIDTH; - try_fmt_meta->height = IMX708_NUM_EMBEDDED_LINES; - try_fmt_meta->code = MEDIA_BUS_FMT_SENSOR_DATA; - try_fmt_meta->field = V4L2_FIELD_NONE; - - /* Initialize try_crop */ - try_crop = v4l2_subdev_get_try_crop(sd, fh->state, IMAGE_PAD); - try_crop->left = IMX708_PIXEL_ARRAY_LEFT; - try_crop->top = IMX708_PIXEL_ARRAY_TOP; - try_crop->width = IMX708_PIXEL_ARRAY_WIDTH; - try_crop->height = IMX708_PIXEL_ARRAY_HEIGHT; - - mutex_unlock(&imx708->mutex); - - return 0; -} - static int imx708_set_exposure(struct imx708 *imx708, unsigned int val) { val = max(val, imx708->mode->exposure_lines_min); @@ -1135,9 +1071,12 @@ static int imx708_set_ctrl(struct v4l2_ctrl *ctrl) container_of(ctrl->handler, struct imx708, ctrl_handler); struct i2c_client *client = v4l2_get_subdevdata(&imx708->sd); const struct imx708_mode *mode_list; + struct v4l2_subdev_state *state; unsigned int code, num_modes; int ret = 0; + state = v4l2_subdev_get_locked_active_state(&imx708->sd); + switch (ctrl->id) { case V4L2_CID_VBLANK: /* @@ -1251,19 +1190,49 @@ static int imx708_enum_mbus_code(struct v4l2_subdev *sd, { struct imx708 *imx708 = to_imx708(sd); - if (code->pad >= NUM_PADS) + if (code->pad >= IMX708_NUM_PADS) return -EINVAL; - if (code->pad == IMAGE_PAD) { + switch (code->pad) { + case IMX708_PAD_IMAGE: + /* The internal image pad is hardwired to the native format. */ + if (code->index > 0) + return -EINVAL; + + code->code = IMX708_NATIVE_FORMAT; + return 0; + + case IMX708_PAD_EDATA: + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_CCS_EMBEDDED; + return 0; + + case IMX708_PAD_SOURCE: + default: + break; + } + + /* + * On the source pad, the sensor supports multiple image raw formats + * with different bit depths. The embedded data format bit depth + * follows the image stream. + */ + if (code->stream == IMX708_STREAM_IMAGE) { if (code->index >= (ARRAY_SIZE(codes) / 4)) return -EINVAL; code->code = imx708_get_format_code(imx708); } else { + struct v4l2_mbus_framefmt *fmt; + if (code->index > 0) return -EINVAL; - code->code = MEDIA_BUS_FMT_SENSOR_DATA; + fmt = v4l2_subdev_state_get_format(sd_state, IMX708_PAD_SOURCE, + IMX708_STREAM_EDATA); + code->code = fmt->code; } return 0; @@ -1275,20 +1244,41 @@ static int imx708_enum_frame_size(struct v4l2_subdev *sd, { struct imx708 *imx708 = to_imx708(sd); - if (fse->pad >= NUM_PADS) - return -EINVAL; + switch (fse->pad) { + case IMX708_PAD_IMAGE: + if (fse->code != IMX708_NATIVE_FORMAT || fse->index > 0) + return -EINVAL; + + fse->min_width = IMX708_NATIVE_WIDTH; + fse->max_width = IMX708_NATIVE_WIDTH; + fse->min_height = IMX708_NATIVE_HEIGHT; + fse->max_height = IMX708_NATIVE_HEIGHT; + return 0; - if (fse->pad == IMAGE_PAD) { + case IMX708_PAD_EDATA: + if (fse->code != MEDIA_BUS_FMT_CCS_EMBEDDED || fse->index > 0) + return -EINVAL; + + fse->min_width = IMX708_EMBEDDED_DATA_WIDTH; + fse->max_width = IMX708_EMBEDDED_DATA_WIDTH; + fse->min_height = IMX708_EMBEDDED_DATA_HEIGHT; + fse->max_height = IMX708_EMBEDDED_DATA_HEIGHT; + return 0; + + case IMX708_PAD_SOURCE: + default: + break; + } + + if (fse->stream == IMX708_STREAM_IMAGE) { const struct imx708_mode *mode_list; unsigned int num_modes; get_mode_table(fse->code, &mode_list, &num_modes, imx708->hdr_mode->val); - if (fse->index >= num_modes) - return -EINVAL; - - if (fse->code != imx708_get_format_code(imx708)) + if (fse->code != imx708_get_format_code(imx708) || + fse->index >= num_modes) return -EINVAL; fse->min_width = mode_list[fse->index].width; @@ -1296,77 +1286,56 @@ static int imx708_enum_frame_size(struct v4l2_subdev *sd, fse->min_height = mode_list[fse->index].height; fse->max_height = fse->min_height; } else { - if (fse->code != MEDIA_BUS_FMT_SENSOR_DATA || fse->index > 0) + struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_state_get_format(sd_state, IMX708_PAD_SOURCE, + IMX708_STREAM_EDATA); + if (fse->code != fmt->code) return -EINVAL; - fse->min_width = IMX708_EMBEDDED_LINE_WIDTH; - fse->max_width = fse->min_width; - fse->min_height = IMX708_NUM_EMBEDDED_LINES; - fse->max_height = fse->min_height; + if (fse->index) + return -EINVAL; + + fse->min_width = IMX708_EMBEDDED_DATA_WIDTH; + fse->max_width = IMX708_EMBEDDED_DATA_WIDTH; + fse->min_height = IMX708_EMBEDDED_DATA_HEIGHT; + fse->max_height = IMX708_EMBEDDED_DATA_HEIGHT; } return 0; } -static void imx708_reset_colorspace(struct v4l2_mbus_framefmt *fmt) +static int imx708_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) { - fmt->colorspace = V4L2_COLORSPACE_RAW; - fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); - fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, - fmt->colorspace, - fmt->ycbcr_enc); - fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); -} + const struct v4l2_mbus_framefmt *fmt; + struct v4l2_subdev_state *state; + u32 code; -static void imx708_update_image_pad_format(struct imx708 *imx708, - const struct imx708_mode *mode, - struct v4l2_subdev_format *fmt) -{ - fmt->format.width = mode->width; - fmt->format.height = mode->height; - fmt->format.field = V4L2_FIELD_NONE; - imx708_reset_colorspace(&fmt->format); -} + if (pad != IMX708_PAD_SOURCE) + return -EINVAL; -static void imx708_update_metadata_pad_format(struct v4l2_subdev_format *fmt) -{ - fmt->format.width = IMX708_EMBEDDED_LINE_WIDTH; - fmt->format.height = IMX708_NUM_EMBEDDED_LINES; - fmt->format.code = MEDIA_BUS_FMT_SENSOR_DATA; - fmt->format.field = V4L2_FIELD_NONE; -} + state = v4l2_subdev_lock_and_get_active_state(sd); + fmt = v4l2_subdev_state_get_format(state, IMX708_PAD_SOURCE, + IMX708_STREAM_IMAGE); + code = fmt->code; + v4l2_subdev_unlock_state(state); -static int imx708_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct imx708 *imx708 = to_imx708(sd); + fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; + fd->num_entries = 2; - if (fmt->pad >= NUM_PADS) - return -EINVAL; + memset(fd->entry, 0, sizeof(fd->entry)); - mutex_lock(&imx708->mutex); - - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(&imx708->sd, sd_state, - fmt->pad); - /* update the code which could change due to vflip or hflip */ - try_fmt->code = fmt->pad == IMAGE_PAD ? - imx708_get_format_code(imx708) : - MEDIA_BUS_FMT_SENSOR_DATA; - fmt->format = *try_fmt; - } else { - if (fmt->pad == IMAGE_PAD) { - imx708_update_image_pad_format(imx708, imx708->mode, - fmt); - fmt->format.code = imx708_get_format_code(imx708); - } else { - imx708_update_metadata_pad_format(fmt); - } - } + fd->entry[0].pixelcode = code; + fd->entry[0].stream = IMX708_STREAM_IMAGE; + fd->entry[0].bus.csi2.vc = 0; + fd->entry[0].bus.csi2.dt = MIPI_CSI2_DT_RAW10; + + fd->entry[1].pixelcode = code; + fd->entry[1].stream = IMX708_STREAM_EDATA; + fd->entry[1].bus.csi2.vc = 0; + fd->entry[1].bus.csi2.dt = MIPI_CSI2_DT_EMBEDDED_8B; - mutex_unlock(&imx708->mutex); return 0; } @@ -1374,100 +1343,214 @@ static int imx708_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { - struct v4l2_mbus_framefmt *framefmt; - const struct imx708_mode *mode; struct imx708 *imx708 = to_imx708(sd); + struct v4l2_mbus_framefmt *format, *ed_format; + const struct imx708_mode *mode_list; + const struct imx708_mode *mode; + struct v4l2_rect *compose; + struct v4l2_rect *crop; + unsigned int num_modes; - if (fmt->pad >= NUM_PADS) - return -EINVAL; + /* + * The driver is mode-based, the format can be set on the source pad + * only, and only for the image stream. + */ + if (fmt->pad != IMX708_PAD_SOURCE || fmt->stream != IMX708_STREAM_IMAGE) + return v4l2_subdev_get_fmt(sd, sd_state, fmt); - mutex_lock(&imx708->mutex); + get_mode_table(fmt->format.code, &mode_list, &num_modes, + imx708->hdr_mode->val); - if (fmt->pad == IMAGE_PAD) { - const struct imx708_mode *mode_list; - unsigned int num_modes; + /* + * Adjust the requested format to match the closest mode. The Bayer + * order varies with flips. + */ + mode = v4l2_find_nearest_size(mode_list, num_modes, width, height, + fmt->format.width, fmt->format.height); + + fmt->format.code = imx708_get_format_code(imx708); + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + fmt->format.colorspace = V4L2_COLORSPACE_RAW; + fmt->format.ycbcr_enc = + V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->format.colorspace); + fmt->format.xfer_func = + V4L2_MAP_XFER_FUNC_DEFAULT(fmt->format.colorspace); + fmt->format.quantization = + V4L2_MAP_QUANTIZATION_DEFAULT(true, fmt->format.colorspace, + fmt->format.ycbcr_enc); + + /* Propagate the format through the sensor. */ + + /* The image pad models the pixel array, and thus has a fixed size. */ + format = v4l2_subdev_state_get_format(sd_state, IMX708_PAD_IMAGE); + *format = fmt->format; + format->code = IMX708_NATIVE_FORMAT; + format->width = IMX708_NATIVE_WIDTH; + format->height = IMX708_NATIVE_HEIGHT; + + /* Get the crop rectangle from the mode list */ + crop = v4l2_subdev_state_get_crop(sd_state, IMX708_PAD_IMAGE); + *crop = mode->crop; + + /* The compose rectangle size is the sensor output size. */ + compose = v4l2_subdev_state_get_compose(sd_state, IMX708_PAD_IMAGE); + compose->left = 0; + compose->top = 0; + compose->width = fmt->format.width; + compose->height = fmt->format.height; - /* Bayer order varies with flips */ - fmt->format.code = imx708_get_format_code(imx708); + /* + * No mode use digital crop, the source pad crop rectangle size and + * format are thus identical to the image pad compose rectangle. + */ + crop = v4l2_subdev_state_get_crop(sd_state, IMX708_PAD_SOURCE, + IMX708_STREAM_IMAGE); + crop->left = 0; + crop->top = 0; + crop->width = fmt->format.width; + crop->height = fmt->format.height; - get_mode_table(fmt->format.code, &mode_list, &num_modes, - imx708->hdr_mode->val); + format = v4l2_subdev_state_get_format(sd_state, IMX708_PAD_SOURCE, + IMX708_STREAM_IMAGE); + *format = fmt->format; - mode = v4l2_find_nearest_size(mode_list, - num_modes, - width, height, - fmt->format.width, - fmt->format.height); - imx708_update_image_pad_format(imx708, mode, fmt); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); - *framefmt = fmt->format; - } else { - imx708->mode = mode; - imx708_set_framing_limits(imx708); - } - } else { - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); - *framefmt = fmt->format; - } else { - /* Only one embedded data mode is supported */ - imx708_update_metadata_pad_format(fmt); - } + /* + * Finally, update the formats on the sink and source sides of the + * embedded data stream. + */ + ed_format = v4l2_subdev_state_get_format(sd_state, IMX708_PAD_EDATA); + ed_format->code = MEDIA_BUS_FMT_CCS_EMBEDDED; + ed_format->width = IMX708_EMBEDDED_DATA_WIDTH; + ed_format->height = IMX708_EMBEDDED_DATA_HEIGHT; + ed_format->field = V4L2_FIELD_NONE; + + format = v4l2_subdev_state_get_format(sd_state, IMX708_PAD_SOURCE, + IMX708_STREAM_EDATA); + *format = *ed_format; + format->code = MEDIA_BUS_FMT_META_10; + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + imx708->mode = mode; + imx708_set_framing_limits(imx708); } - mutex_unlock(&imx708->mutex); - return 0; } -static const struct v4l2_rect * -__imx708_get_pad_crop(struct imx708 *imx708, struct v4l2_subdev_state *sd_state, - unsigned int pad, enum v4l2_subdev_format_whence which) +static int imx708_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&imx708->sd, sd_state, pad); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &imx708->mode->crop; - } + struct v4l2_subdev_route routes[2] = { + { + .sink_pad = IMX708_PAD_IMAGE, + .sink_stream = 0, + .source_pad = IMX708_PAD_SOURCE, + .source_stream = IMX708_STREAM_IMAGE, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }, { + .sink_pad = IMX708_PAD_EDATA, + .sink_stream = 0, + .source_pad = IMX708_PAD_SOURCE, + .source_stream = IMX708_STREAM_EDATA, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }, + }; + struct v4l2_subdev_krouting routing = { + .len_routes = ARRAY_SIZE(routes), + .num_routes = ARRAY_SIZE(routes), + .routes = routes, + }; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .pad = IMX708_PAD_SOURCE, + .stream = IMX708_STREAM_IMAGE, + .format = { + .code = IMX708_NATIVE_FORMAT, + .width = supported_modes_10bit_no_hdr[0].width, + .height = supported_modes_10bit_no_hdr[0].height, + .colorspace = V4L2_COLORSPACE_RAW, + .ycbcr_enc = + V4L2_MAP_YCBCR_ENC_DEFAULT(V4L2_COLORSPACE_RAW), + .xfer_func = + V4L2_MAP_XFER_FUNC_DEFAULT(V4L2_COLORSPACE_RAW), + .quantization = + V4L2_MAP_QUANTIZATION_DEFAULT + (true, V4L2_COLORSPACE_RAW, + V4L2_MAP_YCBCR_ENC_DEFAULT(V4L2_COLORSPACE_RAW)) + }, + }; + int ret; + + ret = v4l2_subdev_set_routing(sd, state, &routing); + if (ret) + return ret; + + /* + * Set the image stream format on the source pad. This will be + * propagated to all formats and selection rectangles internally. + */ + imx708_set_pad_format(sd, state, &fmt); - return NULL; + return 0; } static int imx708_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { - switch (sel->target) { - case V4L2_SEL_TGT_CROP: { - struct imx708 *imx708 = to_imx708(sd); + struct v4l2_rect *compose; - mutex_lock(&imx708->mutex); - sel->r = *__imx708_get_pad_crop(imx708, sd_state, sel->pad, - sel->which); - mutex_unlock(&imx708->mutex); + /* + * The embedded data stream doesn't support selection rectangles, + * neither on the embedded data pad nor on the source pad. + */ + if (sel->pad == IMX708_PAD_EDATA || sel->stream != 0) + return -EINVAL; + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(sd_state, sel->pad); return 0; - } case V4L2_SEL_TGT_NATIVE_SIZE: + if (sel->pad != IMX708_PAD_IMAGE) + return -EINVAL; + sel->r.left = 0; sel->r.top = 0; sel->r.width = IMX708_NATIVE_WIDTH; sel->r.height = IMX708_NATIVE_HEIGHT; - return 0; case V4L2_SEL_TGT_CROP_DEFAULT: case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.left = IMX708_PIXEL_ARRAY_LEFT; - sel->r.top = IMX708_PIXEL_ARRAY_TOP; - sel->r.width = IMX708_PIXEL_ARRAY_WIDTH; - sel->r.height = IMX708_PIXEL_ARRAY_HEIGHT; + switch (sel->pad) { + case IMX708_PAD_IMAGE: + sel->r.top = IMX708_PIXEL_ARRAY_TOP; + sel->r.left = IMX708_PIXEL_ARRAY_LEFT; + sel->r.width = IMX708_PIXEL_ARRAY_WIDTH; + sel->r.height = IMX708_PIXEL_ARRAY_HEIGHT; + return 0; + + case IMX708_PAD_SOURCE: + compose = v4l2_subdev_state_get_compose(sd_state, + IMX708_PAD_IMAGE); + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = compose->width; + sel->r.height = compose->height; + return 0; + } + break; + + case V4L2_SEL_TGT_COMPOSE: + if (sel->pad != IMX708_PAD_IMAGE) + return -EINVAL; + + sel->r = *v4l2_subdev_state_get_compose(sd_state, sel->pad); return 0; } @@ -1482,13 +1565,17 @@ static int imx708_start_streaming(struct imx708 *imx708) int i, ret; u32 val; + ret = pm_runtime_resume_and_get(&client->dev); + if (ret < 0) + return ret; + if (!imx708->common_regs_written) { ret = imx708_write_regs(imx708, mode_common_regs, ARRAY_SIZE(mode_common_regs)); if (ret) { dev_err(&client->dev, "%s failed to set common settings\n", __func__); - return ret; + goto error_pm_put; } ret = imx708_read_reg(imx708, IMX708_REG_BASE_SPC_GAINS_L, @@ -1510,9 +1597,8 @@ static int imx708_start_streaming(struct imx708 *imx708) if (ret) { dev_err(&client->dev, "%s failed to set PDAF gains\n", __func__); - return ret; + goto error_pm_put; } - imx708->common_regs_written = true; } @@ -1521,9 +1607,8 @@ static int imx708_start_streaming(struct imx708 *imx708) ret = imx708_write_regs(imx708, reg_list->regs, reg_list->num_of_regs); if (ret) { dev_err(&client->dev, "%s failed to set mode\n", __func__); - return ret; + goto error_pm_put; } - /* Update the link frequency registers */ freq_regs = &link_freq_regs[imx708->link_freq_idx]; ret = imx708_write_regs(imx708, freq_regs->regs, @@ -1531,7 +1616,7 @@ static int imx708_start_streaming(struct imx708 *imx708) if (ret) { dev_err(&client->dev, "%s failed to set link frequency registers\n", __func__); - return ret; + goto error_pm_put; } /* Quad Bayer re-mosaic adjustments (for full-resolution mode only) */ @@ -1548,15 +1633,21 @@ static int imx708_start_streaming(struct imx708 *imx708) IMX708_REG_VALUE_08BIT, IMX708_LPF_INTENSITY_DISABLED); } - /* Apply customized values from user */ ret = __v4l2_ctrl_handler_setup(imx708->sd.ctrl_handler); if (ret) - return ret; - + goto error_pm_put; /* set stream on register */ - return imx708_write_reg(imx708, IMX708_REG_MODE_SELECT, - IMX708_REG_VALUE_08BIT, IMX708_MODE_STREAMING); + ret = imx708_write_reg(imx708, IMX708_REG_MODE_SELECT, + IMX708_REG_VALUE_08BIT, IMX708_MODE_STREAMING); + if (ret) + goto error_pm_put; + + return 0; + +error_pm_put: + pm_runtime_put(&client->dev); + return ret; } /* Stop streaming */ @@ -1570,56 +1661,51 @@ static void imx708_stop_streaming(struct imx708 *imx708) IMX708_REG_VALUE_08BIT, IMX708_MODE_STANDBY); if (ret) dev_err(&client->dev, "%s failed to set stream\n", __func__); + + pm_runtime_put(&client->dev); } -static int imx708_set_stream(struct v4l2_subdev *sd, int enable) +static int imx708_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) { struct imx708 *imx708 = to_imx708(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret = 0; - mutex_lock(&imx708->mutex); - if (imx708->streaming == enable) { - mutex_unlock(&imx708->mutex); + /* + * The image stream controls sensor streaming, as embedded data isn't + * controllable independently. + */ + if (!(streams_mask & BIT(IMX708_STREAM_IMAGE))) return 0; - } - if (enable) { - ret = pm_runtime_get_sync(&client->dev); - if (ret < 0) { - pm_runtime_put_noidle(&client->dev); - goto err_unlock; - } + /* vflip and hflip cannot change during streaming */ + __v4l2_ctrl_grab(imx708->vflip, true); + __v4l2_ctrl_grab(imx708->hflip, true); + __v4l2_ctrl_grab(imx708->hdr_mode, true); - /* - * Apply default & customized values - * and then start streaming. - */ - ret = imx708_start_streaming(imx708); - if (ret) - goto err_rpm_put; - } else { - imx708_stop_streaming(imx708); - pm_runtime_put(&client->dev); - } - - imx708->streaming = enable; + return imx708_start_streaming(imx708); +} - /* vflip/hflip and hdr mode cannot change during streaming */ - __v4l2_ctrl_grab(imx708->vflip, enable); - __v4l2_ctrl_grab(imx708->hflip, enable); - __v4l2_ctrl_grab(imx708->hdr_mode, enable); +static int imx708_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct imx708 *imx708 = to_imx708(sd); - mutex_unlock(&imx708->mutex); + /* + * The image stream controls sensor streaming, as embedded data isn't + * controllable independently. + */ + if (!(streams_mask & BIT(IMX708_STREAM_IMAGE))) + return 0; - return ret; + __v4l2_ctrl_grab(imx708->vflip, false); + __v4l2_ctrl_grab(imx708->hflip, false); + __v4l2_ctrl_grab(imx708->hdr_mode, false); -err_rpm_put: - pm_runtime_put(&client->dev); -err_unlock: - mutex_unlock(&imx708->mutex); + imx708_stop_streaming(imx708); - return ret; + return 0; } /* Power/clock management functions */ @@ -1674,39 +1760,6 @@ static int imx708_power_off(struct device *dev) return 0; } -static int __maybe_unused imx708_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct imx708 *imx708 = to_imx708(sd); - - if (imx708->streaming) - imx708_stop_streaming(imx708); - - return 0; -} - -static int __maybe_unused imx708_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct imx708 *imx708 = to_imx708(sd); - int ret; - - if (imx708->streaming) { - ret = imx708_start_streaming(imx708); - if (ret) - goto error; - } - - return 0; - -error: - imx708_stop_streaming(imx708); - imx708->streaming = 0; - return ret; -} - static int imx708_get_regulators(struct imx708 *imx708) { struct i2c_client *client = v4l2_get_subdevdata(&imx708->sd); @@ -1757,26 +1810,24 @@ static const struct v4l2_subdev_core_ops imx708_core_ops = { .unsubscribe_event = v4l2_event_subdev_unsubscribe, }; -static const struct v4l2_subdev_video_ops imx708_video_ops = { - .s_stream = imx708_set_stream, -}; - static const struct v4l2_subdev_pad_ops imx708_pad_ops = { .enum_mbus_code = imx708_enum_mbus_code, - .get_fmt = imx708_get_pad_format, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = imx708_set_pad_format, .get_selection = imx708_get_selection, .enum_frame_size = imx708_enum_frame_size, + .get_frame_desc = imx708_get_frame_desc, + .enable_streams = imx708_enable_streams, + .disable_streams = imx708_disable_streams, }; static const struct v4l2_subdev_ops imx708_subdev_ops = { .core = &imx708_core_ops, - .video = &imx708_video_ops, .pad = &imx708_pad_ops, }; static const struct v4l2_subdev_internal_ops imx708_internal_ops = { - .open = imx708_open, + .init_state = imx708_init_state, }; static const struct v4l2_ctrl_config imx708_notify_gains_ctrl = { @@ -1806,9 +1857,6 @@ static int imx708_init_controls(struct imx708 *imx708) if (ret) return ret; - mutex_init(&imx708->mutex); - ctrl_hdlr->lock = &imx708->mutex; - /* By default, PIXEL_RATE is read only */ imx708->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops, V4L2_CID_PIXEL_RATE, @@ -1824,7 +1872,7 @@ static int imx708_init_controls(struct imx708 *imx708) /* * Create the controls here, but mode specific limits are setup - * in the imx708_set_framing_limits() call below. + * in the imx708_set_framing_limits() call. */ imx708->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops, V4L2_CID_VBLANK, 0, 0xffff, 1, 0); @@ -1899,14 +1947,10 @@ static int imx708_init_controls(struct imx708 *imx708) imx708->sd.ctrl_handler = ctrl_hdlr; - /* Setup exposure and frame/line length limits. */ - imx708_set_framing_limits(imx708); - return 0; error: v4l2_ctrl_handler_free(ctrl_hdlr); - mutex_destroy(&imx708->mutex); return ret; } @@ -1914,7 +1958,6 @@ static int imx708_init_controls(struct imx708 *imx708) static void imx708_free_controls(struct imx708 *imx708) { v4l2_ctrl_handler_free(imx708->sd.ctrl_handler); - mutex_destroy(&imx708->mutex); } static int imx708_check_hwcfg(struct device *dev, struct imx708 *imx708) @@ -1983,6 +2026,7 @@ static int imx708_probe(struct i2c_client *client) return -ENOMEM; v4l2_i2c_subdev_init(&imx708->sd, client, &imx708_subdev_ops); + imx708->sd.internal_ops = &imx708_internal_ops; /* Check the hardware configuration in device tree */ if (imx708_check_hwcfg(dev, imx708)) @@ -2020,53 +2064,61 @@ static int imx708_probe(struct i2c_client *client) if (ret) goto error_power_off; - /* Initialize default format */ - imx708_set_default_format(imx708); - - /* Enable runtime PM and turn off the device */ - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - pm_runtime_idle(dev); - /* This needs the pm runtime to be registered. */ ret = imx708_init_controls(imx708); if (ret) - goto error_pm_runtime; + goto error_power_off; /* Initialize subdev */ imx708->sd.internal_ops = &imx708_internal_ops; imx708->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | - V4L2_SUBDEV_FL_HAS_EVENTS; + V4L2_SUBDEV_FL_HAS_EVENTS | + V4L2_SUBDEV_FL_STREAMS; imx708->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; /* Initialize source pads */ - imx708->pad[IMAGE_PAD].flags = MEDIA_PAD_FL_SOURCE; - imx708->pad[METADATA_PAD].flags = MEDIA_PAD_FL_SOURCE; - - ret = media_entity_pads_init(&imx708->sd.entity, NUM_PADS, imx708->pad); + imx708->pads[IMX708_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + imx708->pads[IMX708_PAD_IMAGE].flags = MEDIA_PAD_FL_SINK | + MEDIA_PAD_FL_INTERNAL; + imx708->pads[IMX708_PAD_EDATA].flags = MEDIA_PAD_FL_SINK | + MEDIA_PAD_FL_INTERNAL; + + ret = media_entity_pads_init(&imx708->sd.entity, + ARRAY_SIZE(imx708->pads), imx708->pads); if (ret) { dev_err(dev, "failed to init entity pads: %d\n", ret); goto error_handler_free; } + imx708->sd.state_lock = imx708->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&imx708->sd); + if (ret < 0) { + dev_err(dev, "subdev init error: %d\n", ret); + goto error_media_entity; + } + ret = v4l2_async_register_subdev_sensor(&imx708->sd); if (ret < 0) { dev_err(dev, "failed to register sensor sub-device: %d\n", ret); - goto error_media_entity; + goto error_subdev_cleanup; } + /* Enable runtime PM and turn off the device */ + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + return 0; +error_subdev_cleanup: + v4l2_subdev_cleanup(&imx708->sd); + error_media_entity: media_entity_cleanup(&imx708->sd.entity); error_handler_free: imx708_free_controls(imx708); -error_pm_runtime: - pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); - error_power_off: imx708_power_off(&client->dev); @@ -2095,7 +2147,6 @@ static const struct of_device_id imx708_dt_ids[] = { MODULE_DEVICE_TABLE(of, imx708_dt_ids); static const struct dev_pm_ops imx708_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(imx708_suspend, imx708_resume) SET_RUNTIME_PM_OPS(imx708_power_off, imx708_power_on, NULL) }; diff --git a/drivers/media/i2c/irs1125.c b/drivers/media/i2c/irs1125.c index eac03e13aeb54e..f69b4c5a98279f 100644 --- a/drivers/media/i2c/irs1125.c +++ b/drivers/media/i2c/irs1125.c @@ -930,7 +930,7 @@ static int irs1125_detect(struct v4l2_subdev *sd) static int irs1125_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *format = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); format->code = MEDIA_BUS_FMT_Y12_1X12; format->width = IRS1125_WINDOW_WIDTH_DEF; diff --git a/drivers/media/i2c/isl7998x.c b/drivers/media/i2c/isl7998x.c index 73460688c356d2..89e13ebbce0c21 100644 --- a/drivers/media/i2c/isl7998x.c +++ b/drivers/media/i2c/isl7998x.c @@ -1007,8 +1007,8 @@ static int isl7998x_get_fmt(struct v4l2_subdev *sd, mutex_lock(&isl7998x->lock); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - format->format = *v4l2_subdev_get_try_format(sd, sd_state, - format->pad); + format->format = *v4l2_subdev_state_get_format(sd_state, + format->pad); goto out; } @@ -1044,7 +1044,7 @@ static int isl7998x_set_fmt(struct v4l2_subdev *sd, mf->field = mode->field; if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *v4l2_subdev_get_try_format(sd, sd_state, format->pad) = format->format; + *v4l2_subdev_state_get_format(sd_state, format->pad) = format->format; mutex_unlock(&isl7998x->lock); diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c index fc1cf196ef0151..ee11ae682d8de6 100644 --- a/drivers/media/i2c/max9286.c +++ b/drivers/media/i2c/max9286.c @@ -913,7 +913,7 @@ max9286_get_pad_format(struct max9286_priv *priv, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&priv->sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &priv->fmt[pad]; default: @@ -1020,7 +1020,7 @@ static int max9286_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) unsigned int i; for (i = 0; i < MAX9286_N_SINKS; i++) { - format = v4l2_subdev_get_try_format(subdev, fh->state, i); + format = v4l2_subdev_state_get_format(fh->state, i); max9286_init_format(format); } diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index ce9568e8391cdd..c0cefd3fb80b92 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -331,7 +331,7 @@ static int mt9m001_get_fmt(struct v4l2_subdev *sd, return -EINVAL; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, 0); + mf = v4l2_subdev_state_get_format(sd_state, 0); format->format = *mf; return 0; } @@ -411,7 +411,7 @@ static int mt9m001_set_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) return mt9m001_s_fmt(sd, fmt, mf); - sd_state->pads->try_fmt = *mf; + *v4l2_subdev_state_get_format(sd_state, 0) = *mf; return 0; } @@ -656,13 +656,13 @@ static const struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = { #endif }; -static int mt9m001_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int mt9m001_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9m001 *mt9m001 = to_mt9m001(client); struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, sd_state, 0); + v4l2_subdev_state_get_format(sd_state, 0); try_fmt->width = MT9M001_MAX_WIDTH; try_fmt->height = MT9M001_MAX_HEIGHT; @@ -714,7 +714,6 @@ static const struct v4l2_subdev_sensor_ops mt9m001_subdev_sensor_ops = { }; static const struct v4l2_subdev_pad_ops mt9m001_subdev_pad_ops = { - .init_cfg = mt9m001_init_cfg, .enum_mbus_code = mt9m001_enum_mbus_code, .get_selection = mt9m001_get_selection, .set_selection = mt9m001_set_selection, @@ -730,6 +729,10 @@ static const struct v4l2_subdev_ops mt9m001_subdev_ops = { .pad = &mt9m001_subdev_pad_ops, }; +static const struct v4l2_subdev_internal_ops mt9m001_internal_ops = { + .init_state = mt9m001_init_state, +}; + static int mt9m001_probe(struct i2c_client *client) { struct mt9m001 *mt9m001; @@ -761,6 +764,7 @@ static int mt9m001_probe(struct i2c_client *client) return PTR_ERR(mt9m001->reset_gpio); v4l2_i2c_subdev_init(&mt9m001->subdev, client, &mt9m001_subdev_ops); + mt9m001->subdev.internal_ops = &mt9m001_internal_ops; mt9m001->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; v4l2_ctrl_handler_init(&mt9m001->hdl, 4); diff --git a/drivers/media/i2c/mt9m111.c b/drivers/media/i2c/mt9m111.c index df8d9c9e6a96cf..54a7a4c623ea37 100644 --- a/drivers/media/i2c/mt9m111.c +++ b/drivers/media/i2c/mt9m111.c @@ -244,9 +244,7 @@ struct mt9m111 { bool is_streaming; /* user point of view - 0: falling 1: rising edge */ unsigned int pclk_sample:1; -#ifdef CONFIG_MEDIA_CONTROLLER struct media_pad pad; -#endif }; static const struct mt9m111_mode_info mt9m111_mode_data[MT9M111_NUM_MODES] = { @@ -527,13 +525,9 @@ static int mt9m111_get_fmt(struct v4l2_subdev *sd, return -EINVAL; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - mf = v4l2_subdev_get_try_format(sd, sd_state, format->pad); + mf = v4l2_subdev_state_get_format(sd_state, format->pad); format->format = *mf; return 0; -#else - return -EINVAL; -#endif } mf->width = mt9m111->width; @@ -677,7 +671,7 @@ static int mt9m111_set_fmt(struct v4l2_subdev *sd, mf->xfer_func = V4L2_XFER_FUNC_DEFAULT; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - sd_state->pads->try_fmt = *mf; + *v4l2_subdev_state_get_format(sd_state, 0) = *mf; return 0; } @@ -1117,12 +1111,11 @@ static int mt9m111_s_stream(struct v4l2_subdev *sd, int enable) return 0; } -static int mt9m111_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int mt9m111_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API struct v4l2_mbus_framefmt *format = - v4l2_subdev_get_try_format(sd, sd_state, 0); + v4l2_subdev_state_get_format(sd_state, 0); format->width = MT9M111_MAX_WIDTH; format->height = MT9M111_MAX_HEIGHT; @@ -1132,7 +1125,7 @@ static int mt9m111_init_cfg(struct v4l2_subdev *sd, format->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; format->quantization = V4L2_QUANTIZATION_DEFAULT; format->xfer_func = V4L2_XFER_FUNC_DEFAULT; -#endif + return 0; } @@ -1163,7 +1156,6 @@ static const struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = { }; static const struct v4l2_subdev_pad_ops mt9m111_subdev_pad_ops = { - .init_cfg = mt9m111_init_cfg, .enum_mbus_code = mt9m111_enum_mbus_code, .get_selection = mt9m111_get_selection, .set_selection = mt9m111_set_selection, @@ -1178,6 +1170,10 @@ static const struct v4l2_subdev_ops mt9m111_subdev_ops = { .pad = &mt9m111_subdev_pad_ops, }; +static const struct v4l2_subdev_internal_ops mt9m111_internal_ops = { + .init_state = mt9m111_init_state, +}; + /* * Interface active, can use i2c. If it fails, it can indeed mean, that * this wasn't our capture interface, so, we wait for the right one @@ -1282,6 +1278,7 @@ static int mt9m111_probe(struct i2c_client *client) mt9m111->ctx = &context_b; v4l2_i2c_subdev_init(&mt9m111->subdev, client, &mt9m111_subdev_ops); + mt9m111->subdev.internal_ops = &mt9m111_internal_ops; mt9m111->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; @@ -1315,13 +1312,11 @@ static int mt9m111_probe(struct i2c_client *client) return ret; } -#ifdef CONFIG_MEDIA_CONTROLLER mt9m111->pad.flags = MEDIA_PAD_FL_SOURCE; mt9m111->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; ret = media_entity_pads_init(&mt9m111->subdev.entity, 1, &mt9m111->pad); if (ret < 0) goto out_hdlfree; -#endif mt9m111->current_mode = &mt9m111_mode_data[MT9M111_MODE_SXGA_15FPS]; mt9m111->frame_interval.numerator = 1; @@ -1350,10 +1345,8 @@ static int mt9m111_probe(struct i2c_client *client) return 0; out_entityclean: -#ifdef CONFIG_MEDIA_CONTROLLER media_entity_cleanup(&mt9m111->subdev.entity); out_hdlfree: -#endif v4l2_ctrl_handler_free(&mt9m111->hdl); return ret; diff --git a/drivers/media/i2c/mt9m114.c b/drivers/media/i2c/mt9m114.c new file mode 100644 index 00000000000000..0a22f328981d0d --- /dev/null +++ b/drivers/media/i2c/mt9m114.c @@ -0,0 +1,2485 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * mt9m114.c onsemi MT9M114 sensor driver + * + * Copyright (c) 2020-2023 Laurent Pinchart + * Copyright (c) 2012 Analog Devices Inc. + * + * Almost complete rewrite of work by Scott Jiang + * itself based on work from Andrew Chew . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* Sysctl registers */ +#define MT9M114_CHIP_ID CCI_REG16(0x0000) +#define MT9M114_COMMAND_REGISTER CCI_REG16(0x0080) +#define MT9M114_COMMAND_REGISTER_APPLY_PATCH BIT(0) +#define MT9M114_COMMAND_REGISTER_SET_STATE BIT(1) +#define MT9M114_COMMAND_REGISTER_REFRESH BIT(2) +#define MT9M114_COMMAND_REGISTER_WAIT_FOR_EVENT BIT(3) +#define MT9M114_COMMAND_REGISTER_OK BIT(15) +#define MT9M114_RESET_AND_MISC_CONTROL CCI_REG16(0x001a) +#define MT9M114_RESET_SOC BIT(0) +#define MT9M114_PAD_SLEW CCI_REG16(0x001e) +#define MT9M114_PAD_CONTROL CCI_REG16(0x0032) + +/* XDMA registers */ +#define MT9M114_ACCESS_CTL_STAT CCI_REG16(0x0982) +#define MT9M114_PHYSICAL_ADDRESS_ACCESS CCI_REG16(0x098a) +#define MT9M114_LOGICAL_ADDRESS_ACCESS CCI_REG16(0x098e) + +/* Sensor Core registers */ +#define MT9M114_COARSE_INTEGRATION_TIME CCI_REG16(0x3012) +#define MT9M114_FINE_INTEGRATION_TIME CCI_REG16(0x3014) +#define MT9M114_RESET_REGISTER CCI_REG16(0x301a) +#define MT9M114_RESET_REGISTER_LOCK_REG BIT(3) +#define MT9M114_RESET_REGISTER_MASK_BAD BIT(9) +#define MT9M114_FLASH CCI_REG16(0x3046) +#define MT9M114_GREEN1_GAIN CCI_REG16(0x3056) +#define MT9M114_BLUE_GAIN CCI_REG16(0x3058) +#define MT9M114_RED_GAIN CCI_REG16(0x305a) +#define MT9M114_GREEN2_GAIN CCI_REG16(0x305c) +#define MT9M114_GLOBAL_GAIN CCI_REG16(0x305e) +#define MT9M114_GAIN_DIGITAL_GAIN(n) ((n) << 12) +#define MT9M114_GAIN_DIGITAL_GAIN_MASK (0xf << 12) +#define MT9M114_GAIN_ANALOG_GAIN(n) ((n) << 0) +#define MT9M114_GAIN_ANALOG_GAIN_MASK (0xff << 0) +#define MT9M114_CUSTOMER_REV CCI_REG16(0x31fe) + +/* Monitor registers */ +#define MT9M114_MON_MAJOR_VERSION CCI_REG16(0x8000) +#define MT9M114_MON_MINOR_VERSION CCI_REG16(0x8002) +#define MT9M114_MON_RELEASE_VERSION CCI_REG16(0x8004) + +/* Auto-Exposure Track registers */ +#define MT9M114_AE_TRACK_ALGO CCI_REG16(0xa804) +#define MT9M114_AE_TRACK_EXEC_AUTOMATIC_EXPOSURE BIT(0) +#define MT9M114_AE_TRACK_AE_TRACKING_DAMPENING_SPEED CCI_REG8(0xa80a) + +/* Color Correction Matrix registers */ +#define MT9M114_CCM_ALGO CCI_REG16(0xb404) +#define MT9M114_CCM_EXEC_CALC_CCM_MATRIX BIT(4) +#define MT9M114_CCM_DELTA_GAIN CCI_REG8(0xb42a) + +/* Camera Control registers */ +#define MT9M114_CAM_SENSOR_CFG_Y_ADDR_START CCI_REG16(0xc800) +#define MT9M114_CAM_SENSOR_CFG_X_ADDR_START CCI_REG16(0xc802) +#define MT9M114_CAM_SENSOR_CFG_Y_ADDR_END CCI_REG16(0xc804) +#define MT9M114_CAM_SENSOR_CFG_X_ADDR_END CCI_REG16(0xc806) +#define MT9M114_CAM_SENSOR_CFG_PIXCLK CCI_REG32(0xc808) +#define MT9M114_CAM_SENSOR_CFG_ROW_SPEED CCI_REG16(0xc80c) +#define MT9M114_CAM_SENSOR_CFG_FINE_INTEG_TIME_MIN CCI_REG16(0xc80e) +#define MT9M114_CAM_SENSOR_CFG_FINE_INTEG_TIME_MAX CCI_REG16(0xc810) +#define MT9M114_CAM_SENSOR_CFG_FRAME_LENGTH_LINES CCI_REG16(0xc812) +#define MT9M114_CAM_SENSOR_CFG_FRAME_LENGTH_LINES_MAX 65535 +#define MT9M114_CAM_SENSOR_CFG_LINE_LENGTH_PCK CCI_REG16(0xc814) +#define MT9M114_CAM_SENSOR_CFG_LINE_LENGTH_PCK_MAX 8191 +#define MT9M114_CAM_SENSOR_CFG_FINE_CORRECTION CCI_REG16(0xc816) +#define MT9M114_CAM_SENSOR_CFG_CPIPE_LAST_ROW CCI_REG16(0xc818) +#define MT9M114_CAM_SENSOR_CFG_REG_0_DATA CCI_REG16(0xc826) +#define MT9M114_CAM_SENSOR_CONTROL_READ_MODE CCI_REG16(0xc834) +#define MT9M114_CAM_SENSOR_CONTROL_HORZ_MIRROR_EN BIT(0) +#define MT9M114_CAM_SENSOR_CONTROL_VERT_FLIP_EN BIT(1) +#define MT9M114_CAM_SENSOR_CONTROL_X_READ_OUT_NORMAL (0 << 4) +#define MT9M114_CAM_SENSOR_CONTROL_X_READ_OUT_SKIPPING (1 << 4) +#define MT9M114_CAM_SENSOR_CONTROL_X_READ_OUT_AVERAGE (2 << 4) +#define MT9M114_CAM_SENSOR_CONTROL_X_READ_OUT_SUMMING (3 << 4) +#define MT9M114_CAM_SENSOR_CONTROL_X_READ_OUT_MASK (3 << 4) +#define MT9M114_CAM_SENSOR_CONTROL_Y_READ_OUT_NORMAL (0 << 8) +#define MT9M114_CAM_SENSOR_CONTROL_Y_READ_OUT_SKIPPING (1 << 8) +#define MT9M114_CAM_SENSOR_CONTROL_Y_READ_OUT_SUMMING (3 << 8) +#define MT9M114_CAM_SENSOR_CONTROL_Y_READ_OUT_MASK (3 << 8) +#define MT9M114_CAM_SENSOR_CONTROL_ANALOG_GAIN CCI_REG16(0xc836) +#define MT9M114_CAM_SENSOR_CONTROL_COARSE_INTEGRATION_TIME CCI_REG16(0xc83c) +#define MT9M114_CAM_SENSOR_CONTROL_FINE_INTEGRATION_TIME CCI_REG16(0xc83e) +#define MT9M114_CAM_MODE_SELECT CCI_REG8(0xc84c) +#define MT9M114_CAM_MODE_SELECT_NORMAL (0 << 0) +#define MT9M114_CAM_MODE_SELECT_LENS_CALIBRATION (1 << 0) +#define MT9M114_CAM_MODE_SELECT_TEST_PATTERN (2 << 0) +#define MT9M114_CAM_MODE_TEST_PATTERN_SELECT CCI_REG8(0xc84d) +#define MT9M114_CAM_MODE_TEST_PATTERN_SELECT_SOLID (1 << 0) +#define MT9M114_CAM_MODE_TEST_PATTERN_SELECT_SOLID_BARS (4 << 0) +#define MT9M114_CAM_MODE_TEST_PATTERN_SELECT_RANDOM (5 << 0) +#define MT9M114_CAM_MODE_TEST_PATTERN_SELECT_FADING_BARS (8 << 0) +#define MT9M114_CAM_MODE_TEST_PATTERN_SELECT_WALKING_1S_10B (10 << 0) +#define MT9M114_CAM_MODE_TEST_PATTERN_SELECT_WALKING_1S_8B (11 << 0) +#define MT9M114_CAM_MODE_TEST_PATTERN_RED CCI_REG16(0xc84e) +#define MT9M114_CAM_MODE_TEST_PATTERN_GREEN CCI_REG16(0xc850) +#define MT9M114_CAM_MODE_TEST_PATTERN_BLUE CCI_REG16(0xc852) +#define MT9M114_CAM_CROP_WINDOW_XOFFSET CCI_REG16(0xc854) +#define MT9M114_CAM_CROP_WINDOW_YOFFSET CCI_REG16(0xc856) +#define MT9M114_CAM_CROP_WINDOW_WIDTH CCI_REG16(0xc858) +#define MT9M114_CAM_CROP_WINDOW_HEIGHT CCI_REG16(0xc85a) +#define MT9M114_CAM_CROP_CROPMODE CCI_REG8(0xc85c) +#define MT9M114_CAM_CROP_MODE_AE_AUTO_CROP_EN BIT(0) +#define MT9M114_CAM_CROP_MODE_AWB_AUTO_CROP_EN BIT(1) +#define MT9M114_CAM_OUTPUT_WIDTH CCI_REG16(0xc868) +#define MT9M114_CAM_OUTPUT_HEIGHT CCI_REG16(0xc86a) +#define MT9M114_CAM_OUTPUT_FORMAT CCI_REG16(0xc86c) +#define MT9M114_CAM_OUTPUT_FORMAT_SWAP_RED_BLUE BIT(0) +#define MT9M114_CAM_OUTPUT_FORMAT_SWAP_BYTES BIT(1) +#define MT9M114_CAM_OUTPUT_FORMAT_MONO_ENABLE BIT(2) +#define MT9M114_CAM_OUTPUT_FORMAT_BT656_ENABLE BIT(3) +#define MT9M114_CAM_OUTPUT_FORMAT_BT656_CROP_SCALE_DISABLE BIT(4) +#define MT9M114_CAM_OUTPUT_FORMAT_FVLV_DISABLE BIT(5) +#define MT9M114_CAM_OUTPUT_FORMAT_FORMAT_YUV (0 << 8) +#define MT9M114_CAM_OUTPUT_FORMAT_FORMAT_RGB (1 << 8) +#define MT9M114_CAM_OUTPUT_FORMAT_FORMAT_BAYER (2 << 8) +#define MT9M114_CAM_OUTPUT_FORMAT_FORMAT_NONE (3 << 8) +#define MT9M114_CAM_OUTPUT_FORMAT_FORMAT_MASK (3 << 8) +#define MT9M114_CAM_OUTPUT_FORMAT_BAYER_FORMAT_RAWR10 (0 << 10) +#define MT9M114_CAM_OUTPUT_FORMAT_BAYER_FORMAT_PRELSC_8_2 (1 << 10) +#define MT9M114_CAM_OUTPUT_FORMAT_BAYER_FORMAT_POSTLSC_8_2 (2 << 10) +#define MT9M114_CAM_OUTPUT_FORMAT_BAYER_FORMAT_PROCESSED8 (3 << 10) +#define MT9M114_CAM_OUTPUT_FORMAT_BAYER_FORMAT_MASK (3 << 10) +#define MT9M114_CAM_OUTPUT_FORMAT_RGB_FORMAT_565RGB (0 << 12) +#define MT9M114_CAM_OUTPUT_FORMAT_RGB_FORMAT_555RGB (1 << 12) +#define MT9M114_CAM_OUTPUT_FORMAT_RGB_FORMAT_444xRGB (2 << 12) +#define MT9M114_CAM_OUTPUT_FORMAT_RGB_FORMAT_444RGBx (3 << 12) +#define MT9M114_CAM_OUTPUT_FORMAT_RGB_FORMAT_MASK (3 << 12) +#define MT9M114_CAM_OUTPUT_FORMAT_YUV CCI_REG16(0xc86e) +#define MT9M114_CAM_OUTPUT_FORMAT_YUV_CLIP BIT(5) +#define MT9M114_CAM_OUTPUT_FORMAT_YUV_AUV_OFFSET BIT(4) +#define MT9M114_CAM_OUTPUT_FORMAT_YUV_SELECT_601 BIT(3) +#define MT9M114_CAM_OUTPUT_FORMAT_YUV_NORMALISE BIT(2) +#define MT9M114_CAM_OUTPUT_FORMAT_YUV_SAMPLING_EVEN_UV (0 << 0) +#define MT9M114_CAM_OUTPUT_FORMAT_YUV_SAMPLING_ODD_UV (1 << 0) +#define MT9M114_CAM_OUTPUT_FORMAT_YUV_SAMPLING_EVENU_ODDV (2 << 0) +#define MT9M114_CAM_OUTPUT_Y_OFFSET CCI_REG8(0xc870) +#define MT9M114_CAM_AET_AEMODE CCI_REG8(0xc878) +#define MT9M114_CAM_AET_EXEC_SET_INDOOR BIT(0) +#define MT9M114_CAM_AET_DISCRETE_FRAMERATE BIT(1) +#define MT9M114_CAM_AET_ADAPTATIVE_TARGET_LUMA BIT(2) +#define MT9M114_CAM_AET_ADAPTATIVE_SKIP_FRAMES BIT(3) +#define MT9M114_CAM_AET_SKIP_FRAMES CCI_REG8(0xc879) +#define MT9M114_CAM_AET_TARGET_AVERAGE_LUMA CCI_REG8(0xc87a) +#define MT9M114_CAM_AET_TARGET_AVERAGE_LUMA_DARK CCI_REG8(0xc87b) +#define MT9M114_CAM_AET_BLACK_CLIPPING_TARGET CCI_REG16(0xc87c) +#define MT9M114_CAM_AET_AE_MIN_VIRT_INT_TIME_PCLK CCI_REG16(0xc87e) +#define MT9M114_CAM_AET_AE_MIN_VIRT_DGAIN CCI_REG16(0xc880) +#define MT9M114_CAM_AET_AE_MAX_VIRT_DGAIN CCI_REG16(0xc882) +#define MT9M114_CAM_AET_AE_MIN_VIRT_AGAIN CCI_REG16(0xc884) +#define MT9M114_CAM_AET_AE_MAX_VIRT_AGAIN CCI_REG16(0xc886) +#define MT9M114_CAM_AET_AE_VIRT_GAIN_TH_EG CCI_REG16(0xc888) +#define MT9M114_CAM_AET_AE_EG_GATE_PERCENTAGE CCI_REG8(0xc88a) +#define MT9M114_CAM_AET_FLICKER_FREQ_HZ CCI_REG8(0xc88b) +#define MT9M114_CAM_AET_MAX_FRAME_RATE CCI_REG16(0xc88c) +#define MT9M114_CAM_AET_MIN_FRAME_RATE CCI_REG16(0xc88e) +#define MT9M114_CAM_AET_TARGET_GAIN CCI_REG16(0xc890) +#define MT9M114_CAM_AWB_CCM_L(n) CCI_REG16(0xc892 + (n) * 2) +#define MT9M114_CAM_AWB_CCM_M(n) CCI_REG16(0xc8a4 + (n) * 2) +#define MT9M114_CAM_AWB_CCM_R(n) CCI_REG16(0xc8b6 + (n) * 2) +#define MT9M114_CAM_AWB_CCM_L_RG_GAIN CCI_REG16(0xc8c8) +#define MT9M114_CAM_AWB_CCM_L_BG_GAIN CCI_REG16(0xc8ca) +#define MT9M114_CAM_AWB_CCM_M_RG_GAIN CCI_REG16(0xc8cc) +#define MT9M114_CAM_AWB_CCM_M_BG_GAIN CCI_REG16(0xc8ce) +#define MT9M114_CAM_AWB_CCM_R_RG_GAIN CCI_REG16(0xc8d0) +#define MT9M114_CAM_AWB_CCM_R_BG_GAIN CCI_REG16(0xc8d2) +#define MT9M114_CAM_AWB_CCM_L_CTEMP CCI_REG16(0xc8d4) +#define MT9M114_CAM_AWB_CCM_M_CTEMP CCI_REG16(0xc8d6) +#define MT9M114_CAM_AWB_CCM_R_CTEMP CCI_REG16(0xc8d8) +#define MT9M114_CAM_AWB_AWB_XSCALE CCI_REG8(0xc8f2) +#define MT9M114_CAM_AWB_AWB_YSCALE CCI_REG8(0xc8f3) +#define MT9M114_CAM_AWB_AWB_WEIGHTS(n) CCI_REG16(0xc8f4 + (n) * 2) +#define MT9M114_CAM_AWB_AWB_XSHIFT_PRE_ADJ CCI_REG16(0xc904) +#define MT9M114_CAM_AWB_AWB_YSHIFT_PRE_ADJ CCI_REG16(0xc906) +#define MT9M114_CAM_AWB_AWBMODE CCI_REG8(0xc909) +#define MT9M114_CAM_AWB_MODE_AUTO BIT(1) +#define MT9M114_CAM_AWB_MODE_EXCLUSIVE_AE BIT(0) +#define MT9M114_CAM_AWB_K_R_L CCI_REG8(0xc90c) +#define MT9M114_CAM_AWB_K_G_L CCI_REG8(0xc90d) +#define MT9M114_CAM_AWB_K_B_L CCI_REG8(0xc90e) +#define MT9M114_CAM_AWB_K_R_R CCI_REG8(0xc90f) +#define MT9M114_CAM_AWB_K_G_R CCI_REG8(0xc910) +#define MT9M114_CAM_AWB_K_B_R CCI_REG8(0xc911) +#define MT9M114_CAM_STAT_AWB_CLIP_WINDOW_XSTART CCI_REG16(0xc914) +#define MT9M114_CAM_STAT_AWB_CLIP_WINDOW_YSTART CCI_REG16(0xc916) +#define MT9M114_CAM_STAT_AWB_CLIP_WINDOW_XEND CCI_REG16(0xc918) +#define MT9M114_CAM_STAT_AWB_CLIP_WINDOW_YEND CCI_REG16(0xc91a) +#define MT9M114_CAM_STAT_AE_INITIAL_WINDOW_XSTART CCI_REG16(0xc91c) +#define MT9M114_CAM_STAT_AE_INITIAL_WINDOW_YSTART CCI_REG16(0xc91e) +#define MT9M114_CAM_STAT_AE_INITIAL_WINDOW_XEND CCI_REG16(0xc920) +#define MT9M114_CAM_STAT_AE_INITIAL_WINDOW_YEND CCI_REG16(0xc922) +#define MT9M114_CAM_LL_LLMODE CCI_REG16(0xc924) +#define MT9M114_CAM_LL_START_BRIGHTNESS CCI_REG16(0xc926) +#define MT9M114_CAM_LL_STOP_BRIGHTNESS CCI_REG16(0xc928) +#define MT9M114_CAM_LL_START_SATURATION CCI_REG8(0xc92a) +#define MT9M114_CAM_LL_END_SATURATION CCI_REG8(0xc92b) +#define MT9M114_CAM_LL_START_DESATURATION CCI_REG8(0xc92c) +#define MT9M114_CAM_LL_END_DESATURATION CCI_REG8(0xc92d) +#define MT9M114_CAM_LL_START_DEMOSAICING CCI_REG8(0xc92e) +#define MT9M114_CAM_LL_START_AP_GAIN CCI_REG8(0xc92f) +#define MT9M114_CAM_LL_START_AP_THRESH CCI_REG8(0xc930) +#define MT9M114_CAM_LL_STOP_DEMOSAICING CCI_REG8(0xc931) +#define MT9M114_CAM_LL_STOP_AP_GAIN CCI_REG8(0xc932) +#define MT9M114_CAM_LL_STOP_AP_THRESH CCI_REG8(0xc933) +#define MT9M114_CAM_LL_START_NR_RED CCI_REG8(0xc934) +#define MT9M114_CAM_LL_START_NR_GREEN CCI_REG8(0xc935) +#define MT9M114_CAM_LL_START_NR_BLUE CCI_REG8(0xc936) +#define MT9M114_CAM_LL_START_NR_THRESH CCI_REG8(0xc937) +#define MT9M114_CAM_LL_STOP_NR_RED CCI_REG8(0xc938) +#define MT9M114_CAM_LL_STOP_NR_GREEN CCI_REG8(0xc939) +#define MT9M114_CAM_LL_STOP_NR_BLUE CCI_REG8(0xc93a) +#define MT9M114_CAM_LL_STOP_NR_THRESH CCI_REG8(0xc93b) +#define MT9M114_CAM_LL_START_CONTRAST_BM CCI_REG16(0xc93c) +#define MT9M114_CAM_LL_STOP_CONTRAST_BM CCI_REG16(0xc93e) +#define MT9M114_CAM_LL_GAMMA CCI_REG16(0xc940) +#define MT9M114_CAM_LL_START_CONTRAST_GRADIENT CCI_REG8(0xc942) +#define MT9M114_CAM_LL_STOP_CONTRAST_GRADIENT CCI_REG8(0xc943) +#define MT9M114_CAM_LL_START_CONTRAST_LUMA_PERCENTAGE CCI_REG8(0xc944) +#define MT9M114_CAM_LL_STOP_CONTRAST_LUMA_PERCENTAGE CCI_REG8(0xc945) +#define MT9M114_CAM_LL_START_GAIN_METRIC CCI_REG16(0xc946) +#define MT9M114_CAM_LL_STOP_GAIN_METRIC CCI_REG16(0xc948) +#define MT9M114_CAM_LL_START_FADE_TO_BLACK_LUMA CCI_REG16(0xc94a) +#define MT9M114_CAM_LL_STOP_FADE_TO_BLACK_LUMA CCI_REG16(0xc94c) +#define MT9M114_CAM_LL_CLUSTER_DC_TH_BM CCI_REG16(0xc94e) +#define MT9M114_CAM_LL_CLUSTER_DC_GATE_PERCENTAGE CCI_REG8(0xc950) +#define MT9M114_CAM_LL_SUMMING_SENSITIVITY_FACTOR CCI_REG8(0xc951) +#define MT9M114_CAM_LL_START_TARGET_LUMA_BM CCI_REG16(0xc952) +#define MT9M114_CAM_LL_STOP_TARGET_LUMA_BM CCI_REG16(0xc954) +#define MT9M114_CAM_PGA_PGA_CONTROL CCI_REG16(0xc95e) +#define MT9M114_CAM_SYSCTL_PLL_ENABLE CCI_REG8(0xc97e) +#define MT9M114_CAM_SYSCTL_PLL_ENABLE_VALUE BIT(0) +#define MT9M114_CAM_SYSCTL_PLL_DIVIDER_M_N CCI_REG16(0xc980) +#define MT9M114_CAM_SYSCTL_PLL_DIVIDER_VALUE(m, n) (((n) << 8) | (m)) +#define MT9M114_CAM_SYSCTL_PLL_DIVIDER_P CCI_REG16(0xc982) +#define MT9M114_CAM_SYSCTL_PLL_DIVIDER_P_VALUE(p) ((p) << 8) +#define MT9M114_CAM_PORT_OUTPUT_CONTROL CCI_REG16(0xc984) +#define MT9M114_CAM_PORT_PORT_SELECT_PARALLEL (0 << 0) +#define MT9M114_CAM_PORT_PORT_SELECT_MIPI (1 << 0) +#define MT9M114_CAM_PORT_CLOCK_SLOWDOWN BIT(3) +#define MT9M114_CAM_PORT_TRUNCATE_RAW_BAYER BIT(4) +#define MT9M114_CAM_PORT_PIXCLK_GATE BIT(5) +#define MT9M114_CAM_PORT_CONT_MIPI_CLK BIT(6) +#define MT9M114_CAM_PORT_CHAN_NUM(vc) ((vc) << 8) +#define MT9M114_CAM_PORT_MIPI_TIMING_T_HS_ZERO CCI_REG16(0xc988) +#define MT9M114_CAM_PORT_MIPI_TIMING_T_HS_ZERO_VALUE(n) ((n) << 8) +#define MT9M114_CAM_PORT_MIPI_TIMING_T_HS_EXIT_TRAIL CCI_REG16(0xc98a) +#define MT9M114_CAM_PORT_MIPI_TIMING_T_HS_EXIT_VALUE(n) ((n) << 8) +#define MT9M114_CAM_PORT_MIPI_TIMING_T_HS_TRAIL_VALUE(n) ((n) << 0) +#define MT9M114_CAM_PORT_MIPI_TIMING_T_CLK_POST_PRE CCI_REG16(0xc98c) +#define MT9M114_CAM_PORT_MIPI_TIMING_T_CLK_POST_VALUE(n) ((n) << 8) +#define MT9M114_CAM_PORT_MIPI_TIMING_T_CLK_PRE_VALUE(n) ((n) << 0) +#define MT9M114_CAM_PORT_MIPI_TIMING_T_CLK_TRAIL_ZERO CCI_REG16(0xc98e) +#define MT9M114_CAM_PORT_MIPI_TIMING_T_CLK_TRAIL_VALUE(n) ((n) << 8) +#define MT9M114_CAM_PORT_MIPI_TIMING_T_CLK_ZERO_VALUE(n) ((n) << 0) + +/* System Manager registers */ +#define MT9M114_SYSMGR_NEXT_STATE CCI_REG8(0xdc00) +#define MT9M114_SYSMGR_CURRENT_STATE CCI_REG8(0xdc01) +#define MT9M114_SYSMGR_CMD_STATUS CCI_REG8(0xdc02) + +/* Patch Loader registers */ +#define MT9M114_PATCHLDR_LOADER_ADDRESS CCI_REG16(0xe000) +#define MT9M114_PATCHLDR_PATCH_ID CCI_REG16(0xe002) +#define MT9M114_PATCHLDR_FIRMWARE_ID CCI_REG32(0xe004) +#define MT9M114_PATCHLDR_APPLY_STATUS CCI_REG8(0xe008) +#define MT9M114_PATCHLDR_NUM_PATCHES CCI_REG8(0xe009) +#define MT9M114_PATCHLDR_PATCH_ID_0 CCI_REG16(0xe00a) +#define MT9M114_PATCHLDR_PATCH_ID_1 CCI_REG16(0xe00c) +#define MT9M114_PATCHLDR_PATCH_ID_2 CCI_REG16(0xe00e) +#define MT9M114_PATCHLDR_PATCH_ID_3 CCI_REG16(0xe010) +#define MT9M114_PATCHLDR_PATCH_ID_4 CCI_REG16(0xe012) +#define MT9M114_PATCHLDR_PATCH_ID_5 CCI_REG16(0xe014) +#define MT9M114_PATCHLDR_PATCH_ID_6 CCI_REG16(0xe016) +#define MT9M114_PATCHLDR_PATCH_ID_7 CCI_REG16(0xe018) + +/* SYS_STATE values (for SYSMGR_NEXT_STATE and SYSMGR_CURRENT_STATE) */ +#define MT9M114_SYS_STATE_ENTER_CONFIG_CHANGE 0x28 +#define MT9M114_SYS_STATE_STREAMING 0x31 +#define MT9M114_SYS_STATE_START_STREAMING 0x34 +#define MT9M114_SYS_STATE_ENTER_SUSPEND 0x40 +#define MT9M114_SYS_STATE_SUSPENDED 0x41 +#define MT9M114_SYS_STATE_ENTER_STANDBY 0x50 +#define MT9M114_SYS_STATE_STANDBY 0x52 +#define MT9M114_SYS_STATE_LEAVE_STANDBY 0x54 + +/* Result status of last SET_STATE comamnd */ +#define MT9M114_SET_STATE_RESULT_ENOERR 0x00 +#define MT9M114_SET_STATE_RESULT_EINVAL 0x0c +#define MT9M114_SET_STATE_RESULT_ENOSPC 0x0d + +/* + * The minimum amount of horizontal and vertical blanking is undocumented. The + * minimum values that have been seen in register lists are 303 and 38, use + * them. + * + * Set the default to achieve 1280x960 at 30fps. + */ +#define MT9M114_MIN_HBLANK 303 +#define MT9M114_MIN_VBLANK 38 +#define MT9M114_DEF_HBLANK 323 +#define MT9M114_DEF_VBLANK 39 + +#define MT9M114_DEF_FRAME_RATE 30 +#define MT9M114_MAX_FRAME_RATE 120 + +#define MT9M114_PIXEL_ARRAY_WIDTH 1296U +#define MT9M114_PIXEL_ARRAY_HEIGHT 976U + +/* + * These values are not well documented and are semi-arbitrary. The pixel array + * minimum output size is 8 pixels larger than the minimum scaler cropped input + * width to account for the demosaicing. + */ +#define MT9M114_PIXEL_ARRAY_MIN_OUTPUT_WIDTH (32U + 8U) +#define MT9M114_PIXEL_ARRAY_MIN_OUTPUT_HEIGHT (32U + 8U) +#define MT9M114_SCALER_CROPPED_INPUT_WIDTH 32U +#define MT9M114_SCALER_CROPPED_INPUT_HEIGHT 32U + +/* Indices into the mt9m114.ifp.tpg array. */ +#define MT9M114_TPG_PATTERN 0 +#define MT9M114_TPG_RED 1 +#define MT9M114_TPG_GREEN 2 +#define MT9M114_TPG_BLUE 3 + +/* ----------------------------------------------------------------------------- + * Data Structures + */ + +enum mt9m114_format_flag { + MT9M114_FMT_FLAG_PARALLEL = BIT(0), + MT9M114_FMT_FLAG_CSI2 = BIT(1), +}; + +struct mt9m114_format_info { + u32 code; + u32 output_format; + u32 flags; +}; + +struct mt9m114 { + struct i2c_client *client; + struct regmap *regmap; + + struct clk *clk; + struct gpio_desc *reset; + struct regulator_bulk_data supplies[3]; + struct v4l2_fwnode_endpoint bus_cfg; + + struct { + unsigned int m; + unsigned int n; + unsigned int p; + } pll; + + unsigned int pixrate; + bool streaming; + + /* Pixel Array */ + struct { + struct v4l2_subdev sd; + struct media_pad pad; + + struct v4l2_ctrl_handler hdl; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + } pa; + + /* Image Flow Processor */ + struct { + struct v4l2_subdev sd; + struct media_pad pads[2]; + + struct v4l2_ctrl_handler hdl; + unsigned int frame_rate; + + struct v4l2_ctrl *tpg[4]; + } ifp; +}; + +/* ----------------------------------------------------------------------------- + * Formats + */ + +static const struct mt9m114_format_info mt9m114_format_infos[] = { + { + /* + * The first two entries are used as defaults, for parallel and + * CSI-2 buses respectively. Keep them in that order. + */ + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .flags = MT9M114_FMT_FLAG_PARALLEL, + .output_format = MT9M114_CAM_OUTPUT_FORMAT_FORMAT_YUV, + }, { + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .flags = MT9M114_FMT_FLAG_CSI2, + .output_format = MT9M114_CAM_OUTPUT_FORMAT_FORMAT_YUV, + }, { + .code = MEDIA_BUS_FMT_YUYV8_2X8, + .flags = MT9M114_FMT_FLAG_PARALLEL, + .output_format = MT9M114_CAM_OUTPUT_FORMAT_FORMAT_YUV + | MT9M114_CAM_OUTPUT_FORMAT_SWAP_BYTES, + }, { + .code = MEDIA_BUS_FMT_YUYV8_1X16, + .flags = MT9M114_FMT_FLAG_CSI2, + .output_format = MT9M114_CAM_OUTPUT_FORMAT_FORMAT_YUV + | MT9M114_CAM_OUTPUT_FORMAT_SWAP_BYTES, + }, { + .code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .flags = MT9M114_FMT_FLAG_PARALLEL, + .output_format = MT9M114_CAM_OUTPUT_FORMAT_RGB_FORMAT_565RGB + | MT9M114_CAM_OUTPUT_FORMAT_FORMAT_RGB + | MT9M114_CAM_OUTPUT_FORMAT_SWAP_BYTES, + }, { + .code = MEDIA_BUS_FMT_RGB565_2X8_BE, + .flags = MT9M114_FMT_FLAG_PARALLEL, + .output_format = MT9M114_CAM_OUTPUT_FORMAT_RGB_FORMAT_565RGB + | MT9M114_CAM_OUTPUT_FORMAT_FORMAT_RGB, + }, { + .code = MEDIA_BUS_FMT_RGB565_1X16, + .flags = MT9M114_FMT_FLAG_CSI2, + .output_format = MT9M114_CAM_OUTPUT_FORMAT_RGB_FORMAT_565RGB + | MT9M114_CAM_OUTPUT_FORMAT_FORMAT_RGB, + }, { + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .output_format = MT9M114_CAM_OUTPUT_FORMAT_BAYER_FORMAT_PROCESSED8 + | MT9M114_CAM_OUTPUT_FORMAT_FORMAT_BAYER, + .flags = MT9M114_FMT_FLAG_PARALLEL | MT9M114_FMT_FLAG_CSI2, + }, { + /* Keep the format compatible with the IFP sink pad last. */ + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .output_format = MT9M114_CAM_OUTPUT_FORMAT_BAYER_FORMAT_RAWR10 + | MT9M114_CAM_OUTPUT_FORMAT_FORMAT_BAYER, + .flags = MT9M114_FMT_FLAG_PARALLEL | MT9M114_FMT_FLAG_CSI2, + } +}; + +static const struct mt9m114_format_info * +mt9m114_default_format_info(struct mt9m114 *sensor) +{ + if (sensor->bus_cfg.bus_type == V4L2_MBUS_CSI2_DPHY) + return &mt9m114_format_infos[1]; + else + return &mt9m114_format_infos[0]; +} + +static const struct mt9m114_format_info * +mt9m114_format_info(struct mt9m114 *sensor, unsigned int pad, u32 code) +{ + const unsigned int num_formats = ARRAY_SIZE(mt9m114_format_infos); + unsigned int flag; + unsigned int i; + + switch (pad) { + case 0: + return &mt9m114_format_infos[num_formats - 1]; + + case 1: + if (sensor->bus_cfg.bus_type == V4L2_MBUS_CSI2_DPHY) + flag = MT9M114_FMT_FLAG_CSI2; + else + flag = MT9M114_FMT_FLAG_PARALLEL; + + for (i = 0; i < num_formats; ++i) { + const struct mt9m114_format_info *info = + &mt9m114_format_infos[i]; + + if (info->code == code && info->flags & flag) + return info; + } + + return mt9m114_default_format_info(sensor); + + default: + return NULL; + } +} + +/* ----------------------------------------------------------------------------- + * Initialization + */ + +static const struct cci_reg_sequence mt9m114_init[] = { + { MT9M114_RESET_REGISTER, MT9M114_RESET_REGISTER_MASK_BAD | + MT9M114_RESET_REGISTER_LOCK_REG | + 0x0010 }, + + /* Sensor optimization */ + { CCI_REG16(0x316a), 0x8270 }, + { CCI_REG16(0x316c), 0x8270 }, + { CCI_REG16(0x3ed0), 0x2305 }, + { CCI_REG16(0x3ed2), 0x77cf }, + { CCI_REG16(0x316e), 0x8202 }, + { CCI_REG16(0x3180), 0x87ff }, + { CCI_REG16(0x30d4), 0x6080 }, + { CCI_REG16(0xa802), 0x0008 }, + + { CCI_REG16(0x3e14), 0xff39 }, + + /* APGA */ + { MT9M114_CAM_PGA_PGA_CONTROL, 0x0000 }, + + /* Automatic White balance */ + { MT9M114_CAM_AWB_CCM_L(0), 0x0267 }, + { MT9M114_CAM_AWB_CCM_L(1), 0xff1a }, + { MT9M114_CAM_AWB_CCM_L(2), 0xffb3 }, + { MT9M114_CAM_AWB_CCM_L(3), 0xff80 }, + { MT9M114_CAM_AWB_CCM_L(4), 0x0166 }, + { MT9M114_CAM_AWB_CCM_L(5), 0x0003 }, + { MT9M114_CAM_AWB_CCM_L(6), 0xff9a }, + { MT9M114_CAM_AWB_CCM_L(7), 0xfeb4 }, + { MT9M114_CAM_AWB_CCM_L(8), 0x024d }, + { MT9M114_CAM_AWB_CCM_M(0), 0x01bf }, + { MT9M114_CAM_AWB_CCM_M(1), 0xff01 }, + { MT9M114_CAM_AWB_CCM_M(2), 0xfff3 }, + { MT9M114_CAM_AWB_CCM_M(3), 0xff75 }, + { MT9M114_CAM_AWB_CCM_M(4), 0x0198 }, + { MT9M114_CAM_AWB_CCM_M(5), 0xfffd }, + { MT9M114_CAM_AWB_CCM_M(6), 0xff9a }, + { MT9M114_CAM_AWB_CCM_M(7), 0xfee7 }, + { MT9M114_CAM_AWB_CCM_M(8), 0x02a8 }, + { MT9M114_CAM_AWB_CCM_R(0), 0x01d9 }, + { MT9M114_CAM_AWB_CCM_R(1), 0xff26 }, + { MT9M114_CAM_AWB_CCM_R(2), 0xfff3 }, + { MT9M114_CAM_AWB_CCM_R(3), 0xffb3 }, + { MT9M114_CAM_AWB_CCM_R(4), 0x0132 }, + { MT9M114_CAM_AWB_CCM_R(5), 0xffe8 }, + { MT9M114_CAM_AWB_CCM_R(6), 0xffda }, + { MT9M114_CAM_AWB_CCM_R(7), 0xfecd }, + { MT9M114_CAM_AWB_CCM_R(8), 0x02c2 }, + { MT9M114_CAM_AWB_CCM_L_RG_GAIN, 0x0075 }, + { MT9M114_CAM_AWB_CCM_L_BG_GAIN, 0x011c }, + { MT9M114_CAM_AWB_CCM_M_RG_GAIN, 0x009a }, + { MT9M114_CAM_AWB_CCM_M_BG_GAIN, 0x0105 }, + { MT9M114_CAM_AWB_CCM_R_RG_GAIN, 0x00a4 }, + { MT9M114_CAM_AWB_CCM_R_BG_GAIN, 0x00ac }, + { MT9M114_CAM_AWB_CCM_L_CTEMP, 0x0a8c }, + { MT9M114_CAM_AWB_CCM_M_CTEMP, 0x0f0a }, + { MT9M114_CAM_AWB_CCM_R_CTEMP, 0x1964 }, + { MT9M114_CAM_AWB_AWB_XSHIFT_PRE_ADJ, 51 }, + { MT9M114_CAM_AWB_AWB_YSHIFT_PRE_ADJ, 60 }, + { MT9M114_CAM_AWB_AWB_XSCALE, 3 }, + { MT9M114_CAM_AWB_AWB_YSCALE, 2 }, + { MT9M114_CAM_AWB_AWB_WEIGHTS(0), 0x0000 }, + { MT9M114_CAM_AWB_AWB_WEIGHTS(1), 0x0000 }, + { MT9M114_CAM_AWB_AWB_WEIGHTS(2), 0x0000 }, + { MT9M114_CAM_AWB_AWB_WEIGHTS(3), 0xe724 }, + { MT9M114_CAM_AWB_AWB_WEIGHTS(4), 0x1583 }, + { MT9M114_CAM_AWB_AWB_WEIGHTS(5), 0x2045 }, + { MT9M114_CAM_AWB_AWB_WEIGHTS(6), 0x03ff }, + { MT9M114_CAM_AWB_AWB_WEIGHTS(7), 0x007c }, + { MT9M114_CAM_AWB_K_R_L, 0x80 }, + { MT9M114_CAM_AWB_K_G_L, 0x80 }, + { MT9M114_CAM_AWB_K_B_L, 0x80 }, + { MT9M114_CAM_AWB_K_R_R, 0x88 }, + { MT9M114_CAM_AWB_K_G_R, 0x80 }, + { MT9M114_CAM_AWB_K_B_R, 0x80 }, + + /* Low-Light Image Enhancements */ + { MT9M114_CAM_LL_START_BRIGHTNESS, 0x0020 }, + { MT9M114_CAM_LL_STOP_BRIGHTNESS, 0x009a }, + { MT9M114_CAM_LL_START_GAIN_METRIC, 0x0070 }, + { MT9M114_CAM_LL_STOP_GAIN_METRIC, 0x00f3 }, + { MT9M114_CAM_LL_START_CONTRAST_LUMA_PERCENTAGE, 0x20 }, + { MT9M114_CAM_LL_STOP_CONTRAST_LUMA_PERCENTAGE, 0x9a }, + { MT9M114_CAM_LL_START_SATURATION, 0x80 }, + { MT9M114_CAM_LL_END_SATURATION, 0x4b }, + { MT9M114_CAM_LL_START_DESATURATION, 0x00 }, + { MT9M114_CAM_LL_END_DESATURATION, 0xff }, + { MT9M114_CAM_LL_START_DEMOSAICING, 0x3c }, + { MT9M114_CAM_LL_START_AP_GAIN, 0x02 }, + { MT9M114_CAM_LL_START_AP_THRESH, 0x06 }, + { MT9M114_CAM_LL_STOP_DEMOSAICING, 0x64 }, + { MT9M114_CAM_LL_STOP_AP_GAIN, 0x01 }, + { MT9M114_CAM_LL_STOP_AP_THRESH, 0x0c }, + { MT9M114_CAM_LL_START_NR_RED, 0x3c }, + { MT9M114_CAM_LL_START_NR_GREEN, 0x3c }, + { MT9M114_CAM_LL_START_NR_BLUE, 0x3c }, + { MT9M114_CAM_LL_START_NR_THRESH, 0x0f }, + { MT9M114_CAM_LL_STOP_NR_RED, 0x64 }, + { MT9M114_CAM_LL_STOP_NR_GREEN, 0x64 }, + { MT9M114_CAM_LL_STOP_NR_BLUE, 0x64 }, + { MT9M114_CAM_LL_STOP_NR_THRESH, 0x32 }, + { MT9M114_CAM_LL_START_CONTRAST_BM, 0x0020 }, + { MT9M114_CAM_LL_STOP_CONTRAST_BM, 0x009a }, + { MT9M114_CAM_LL_GAMMA, 0x00dc }, + { MT9M114_CAM_LL_START_CONTRAST_GRADIENT, 0x38 }, + { MT9M114_CAM_LL_STOP_CONTRAST_GRADIENT, 0x30 }, + { MT9M114_CAM_LL_START_CONTRAST_LUMA_PERCENTAGE, 0x50 }, + { MT9M114_CAM_LL_STOP_CONTRAST_LUMA_PERCENTAGE, 0x19 }, + { MT9M114_CAM_LL_START_FADE_TO_BLACK_LUMA, 0x0230 }, + { MT9M114_CAM_LL_STOP_FADE_TO_BLACK_LUMA, 0x0010 }, + { MT9M114_CAM_LL_CLUSTER_DC_TH_BM, 0x01cd }, + { MT9M114_CAM_LL_CLUSTER_DC_GATE_PERCENTAGE, 0x05 }, + { MT9M114_CAM_LL_SUMMING_SENSITIVITY_FACTOR, 0x40 }, + + /* Auto-Exposure */ + { MT9M114_CAM_AET_TARGET_AVERAGE_LUMA_DARK, 0x1b }, + { MT9M114_CAM_AET_AEMODE, 0x00 }, + { MT9M114_CAM_AET_TARGET_GAIN, 0x0080 }, + { MT9M114_CAM_AET_AE_MAX_VIRT_AGAIN, 0x0100 }, + { MT9M114_CAM_AET_BLACK_CLIPPING_TARGET, 0x005a }, + + { MT9M114_CCM_DELTA_GAIN, 0x05 }, + { MT9M114_AE_TRACK_AE_TRACKING_DAMPENING_SPEED, 0x20 }, + + /* Pixel array timings and integration time */ + { MT9M114_CAM_SENSOR_CFG_ROW_SPEED, 1 }, + { MT9M114_CAM_SENSOR_CFG_FINE_INTEG_TIME_MIN, 219 }, + { MT9M114_CAM_SENSOR_CFG_FINE_INTEG_TIME_MAX, 1459 }, + { MT9M114_CAM_SENSOR_CFG_FINE_CORRECTION, 96 }, + { MT9M114_CAM_SENSOR_CFG_REG_0_DATA, 32 }, + + /* Miscellaneous settings */ + { MT9M114_PAD_SLEW, 0x0777 }, +}; + +/* ----------------------------------------------------------------------------- + * Hardware Configuration + */ + +/* Wait for a command to complete. */ +static int mt9m114_poll_command(struct mt9m114 *sensor, u32 command) +{ + unsigned int i; + u64 value; + int ret; + + for (i = 0; i < 100; ++i) { + ret = cci_read(sensor->regmap, MT9M114_COMMAND_REGISTER, &value, + NULL); + if (ret < 0) + return ret; + + if (!(value & command)) + break; + + usleep_range(5000, 6000); + } + + if (value & command) { + dev_err(&sensor->client->dev, "Command %u completion timeout\n", + command); + return -ETIMEDOUT; + } + + if (!(value & MT9M114_COMMAND_REGISTER_OK)) { + dev_err(&sensor->client->dev, "Command %u failed\n", command); + return -EIO; + } + + return 0; +} + +/* Wait for a state to be entered. */ +static int mt9m114_poll_state(struct mt9m114 *sensor, u32 state) +{ + unsigned int i; + u64 value; + int ret; + + for (i = 0; i < 100; ++i) { + ret = cci_read(sensor->regmap, MT9M114_SYSMGR_CURRENT_STATE, + &value, NULL); + if (ret < 0) + return ret; + + if (value == state) + return 0; + + usleep_range(1000, 1500); + } + + dev_err(&sensor->client->dev, "Timeout waiting for state 0x%02x\n", + state); + return -ETIMEDOUT; +} + +static int mt9m114_set_state(struct mt9m114 *sensor, u8 next_state) +{ + int ret = 0; + + /* Set the next desired state and start the state transition. */ + cci_write(sensor->regmap, MT9M114_SYSMGR_NEXT_STATE, next_state, &ret); + cci_write(sensor->regmap, MT9M114_COMMAND_REGISTER, + MT9M114_COMMAND_REGISTER_OK | + MT9M114_COMMAND_REGISTER_SET_STATE, &ret); + if (ret < 0) + return ret; + + /* Wait for the state transition to complete. */ + ret = mt9m114_poll_command(sensor, MT9M114_COMMAND_REGISTER_SET_STATE); + if (ret < 0) + return ret; + + return 0; +} + +static int mt9m114_initialize(struct mt9m114 *sensor) +{ + u32 value; + int ret; + + ret = cci_multi_reg_write(sensor->regmap, mt9m114_init, + ARRAY_SIZE(mt9m114_init), NULL); + if (ret < 0) { + dev_err(&sensor->client->dev, + "Failed to initialize the sensor\n"); + return ret; + } + + /* Configure the PLL. */ + cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_ENABLE, + MT9M114_CAM_SYSCTL_PLL_ENABLE_VALUE, &ret); + cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_DIVIDER_M_N, + MT9M114_CAM_SYSCTL_PLL_DIVIDER_VALUE(sensor->pll.m, + sensor->pll.n), + &ret); + cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_DIVIDER_P, + MT9M114_CAM_SYSCTL_PLL_DIVIDER_P_VALUE(sensor->pll.p), &ret); + cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_PIXCLK, + sensor->pixrate, &ret); + + /* Configure the output mode. */ + if (sensor->bus_cfg.bus_type == V4L2_MBUS_CSI2_DPHY) { + value = MT9M114_CAM_PORT_PORT_SELECT_MIPI + | MT9M114_CAM_PORT_CHAN_NUM(0) + | 0x8000; + if (!(sensor->bus_cfg.bus.mipi_csi2.flags & + V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK)) + value |= MT9M114_CAM_PORT_CONT_MIPI_CLK; + } else { + value = MT9M114_CAM_PORT_PORT_SELECT_PARALLEL + | 0x8000; + } + cci_write(sensor->regmap, MT9M114_CAM_PORT_OUTPUT_CONTROL, value, &ret); + if (ret < 0) + return ret; + + ret = mt9m114_set_state(sensor, MT9M114_SYS_STATE_ENTER_CONFIG_CHANGE); + if (ret < 0) + return ret; + + ret = mt9m114_set_state(sensor, MT9M114_SYS_STATE_ENTER_SUSPEND); + if (ret < 0) + return ret; + + return 0; +} + +static int mt9m114_configure(struct mt9m114 *sensor, + struct v4l2_subdev_state *pa_state, + struct v4l2_subdev_state *ifp_state) +{ + const struct v4l2_mbus_framefmt *pa_format; + const struct v4l2_rect *pa_crop; + const struct mt9m114_format_info *ifp_info; + const struct v4l2_mbus_framefmt *ifp_format; + const struct v4l2_rect *ifp_crop; + const struct v4l2_rect *ifp_compose; + unsigned int hratio, vratio; + u64 output_format; + u64 read_mode; + int ret = 0; + + pa_format = v4l2_subdev_state_get_format(pa_state, 0); + pa_crop = v4l2_subdev_state_get_crop(pa_state, 0); + + ifp_format = v4l2_subdev_state_get_format(ifp_state, 1); + ifp_info = mt9m114_format_info(sensor, 1, ifp_format->code); + ifp_crop = v4l2_subdev_state_get_crop(ifp_state, 0); + ifp_compose = v4l2_subdev_state_get_compose(ifp_state, 0); + + ret = cci_read(sensor->regmap, MT9M114_CAM_SENSOR_CONTROL_READ_MODE, + &read_mode, NULL); + if (ret < 0) + return ret; + + ret = cci_read(sensor->regmap, MT9M114_CAM_OUTPUT_FORMAT, + &output_format, NULL); + if (ret < 0) + return ret; + + hratio = pa_crop->width / pa_format->width; + vratio = pa_crop->height / pa_format->height; + + /* + * Pixel array crop and binning. The CAM_SENSOR_CFG_CPIPE_LAST_ROW + * register isn't clearly documented, but is always set to the number + * of active rows minus 4 divided by the vertical binning factor in all + * example sensor modes. + */ + cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_X_ADDR_START, + pa_crop->left, &ret); + cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_Y_ADDR_START, + pa_crop->top, &ret); + cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_X_ADDR_END, + pa_crop->width + pa_crop->left - 1, &ret); + cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_Y_ADDR_END, + pa_crop->height + pa_crop->top - 1, &ret); + cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_CPIPE_LAST_ROW, + (pa_crop->height - 4) / vratio - 1, &ret); + + read_mode &= ~(MT9M114_CAM_SENSOR_CONTROL_X_READ_OUT_MASK | + MT9M114_CAM_SENSOR_CONTROL_Y_READ_OUT_MASK); + + if (hratio > 1) + read_mode |= MT9M114_CAM_SENSOR_CONTROL_X_READ_OUT_SUMMING; + if (vratio > 1) + read_mode |= MT9M114_CAM_SENSOR_CONTROL_Y_READ_OUT_SUMMING; + + cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CONTROL_READ_MODE, + read_mode, &ret); + + /* + * Color pipeline (IFP) cropping and scaling. Subtract 4 from the left + * and top coordinates to compensate for the lines and columns removed + * by demosaicing that are taken into account in the crop rectangle but + * not in the hardware. + */ + cci_write(sensor->regmap, MT9M114_CAM_CROP_WINDOW_XOFFSET, + ifp_crop->left - 4, &ret); + cci_write(sensor->regmap, MT9M114_CAM_CROP_WINDOW_YOFFSET, + ifp_crop->top - 4, &ret); + cci_write(sensor->regmap, MT9M114_CAM_CROP_WINDOW_WIDTH, + ifp_crop->width, &ret); + cci_write(sensor->regmap, MT9M114_CAM_CROP_WINDOW_HEIGHT, + ifp_crop->height, &ret); + + cci_write(sensor->regmap, MT9M114_CAM_OUTPUT_WIDTH, + ifp_compose->width, &ret); + cci_write(sensor->regmap, MT9M114_CAM_OUTPUT_HEIGHT, + ifp_compose->height, &ret); + + /* AWB and AE windows, use the full frame. */ + cci_write(sensor->regmap, MT9M114_CAM_STAT_AWB_CLIP_WINDOW_XSTART, + 0, &ret); + cci_write(sensor->regmap, MT9M114_CAM_STAT_AWB_CLIP_WINDOW_YSTART, + 0, &ret); + cci_write(sensor->regmap, MT9M114_CAM_STAT_AWB_CLIP_WINDOW_XEND, + ifp_compose->width - 1, &ret); + cci_write(sensor->regmap, MT9M114_CAM_STAT_AWB_CLIP_WINDOW_YEND, + ifp_compose->height - 1, &ret); + + cci_write(sensor->regmap, MT9M114_CAM_STAT_AE_INITIAL_WINDOW_XSTART, + 0, &ret); + cci_write(sensor->regmap, MT9M114_CAM_STAT_AE_INITIAL_WINDOW_YSTART, + 0, &ret); + cci_write(sensor->regmap, MT9M114_CAM_STAT_AE_INITIAL_WINDOW_XEND, + ifp_compose->width / 5 - 1, &ret); + cci_write(sensor->regmap, MT9M114_CAM_STAT_AE_INITIAL_WINDOW_YEND, + ifp_compose->height / 5 - 1, &ret); + + cci_write(sensor->regmap, MT9M114_CAM_CROP_CROPMODE, + MT9M114_CAM_CROP_MODE_AWB_AUTO_CROP_EN | + MT9M114_CAM_CROP_MODE_AE_AUTO_CROP_EN, &ret); + + /* Set the media bus code. */ + output_format &= ~(MT9M114_CAM_OUTPUT_FORMAT_RGB_FORMAT_MASK | + MT9M114_CAM_OUTPUT_FORMAT_BAYER_FORMAT_MASK | + MT9M114_CAM_OUTPUT_FORMAT_FORMAT_MASK | + MT9M114_CAM_OUTPUT_FORMAT_SWAP_BYTES | + MT9M114_CAM_OUTPUT_FORMAT_SWAP_RED_BLUE); + output_format |= ifp_info->output_format; + + cci_write(sensor->regmap, MT9M114_CAM_OUTPUT_FORMAT, + output_format, &ret); + + return ret; +} + +static int mt9m114_set_frame_rate(struct mt9m114 *sensor) +{ + u16 frame_rate = sensor->ifp.frame_rate << 8; + int ret = 0; + + cci_write(sensor->regmap, MT9M114_CAM_AET_MIN_FRAME_RATE, + frame_rate, &ret); + cci_write(sensor->regmap, MT9M114_CAM_AET_MAX_FRAME_RATE, + frame_rate, &ret); + + return ret; +} + +static int mt9m114_start_streaming(struct mt9m114 *sensor, + struct v4l2_subdev_state *pa_state, + struct v4l2_subdev_state *ifp_state) +{ + int ret; + + ret = pm_runtime_resume_and_get(&sensor->client->dev); + if (ret) + return ret; + + ret = mt9m114_configure(sensor, pa_state, ifp_state); + if (ret) + goto error; + + ret = mt9m114_set_frame_rate(sensor); + if (ret) + goto error; + + ret = __v4l2_ctrl_handler_setup(&sensor->pa.hdl); + if (ret) + goto error; + + ret = __v4l2_ctrl_handler_setup(&sensor->ifp.hdl); + if (ret) + goto error; + + /* + * The Change-Config state is transient and moves to the streaming + * state automatically. + */ + ret = mt9m114_set_state(sensor, MT9M114_SYS_STATE_ENTER_CONFIG_CHANGE); + if (ret) + goto error; + + sensor->streaming = true; + + return 0; + +error: + pm_runtime_mark_last_busy(&sensor->client->dev); + pm_runtime_put_autosuspend(&sensor->client->dev); + + return ret; +} + +static int mt9m114_stop_streaming(struct mt9m114 *sensor) +{ + int ret; + + sensor->streaming = false; + + ret = mt9m114_set_state(sensor, MT9M114_SYS_STATE_ENTER_SUSPEND); + + pm_runtime_mark_last_busy(&sensor->client->dev); + pm_runtime_put_autosuspend(&sensor->client->dev); + + return ret; +} + +/* ----------------------------------------------------------------------------- + * Common Subdev Operations + */ + +static const struct media_entity_operations mt9m114_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +/* ----------------------------------------------------------------------------- + * Pixel Array Control Operations + */ + +static inline struct mt9m114 *pa_ctrl_to_mt9m114(struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct mt9m114, pa.hdl); +} + +static int mt9m114_pa_g_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mt9m114 *sensor = pa_ctrl_to_mt9m114(ctrl); + u64 value; + int ret; + + if (!pm_runtime_get_if_in_use(&sensor->client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + ret = cci_read(sensor->regmap, + MT9M114_CAM_SENSOR_CONTROL_COARSE_INTEGRATION_TIME, + &value, NULL); + if (ret) + break; + + ctrl->val = value; + break; + + case V4L2_CID_ANALOGUE_GAIN: + ret = cci_read(sensor->regmap, + MT9M114_CAM_SENSOR_CONTROL_ANALOG_GAIN, + &value, NULL); + if (ret) + break; + + ctrl->val = value; + break; + + default: + ret = -EINVAL; + break; + } + + pm_runtime_mark_last_busy(&sensor->client->dev); + pm_runtime_put_autosuspend(&sensor->client->dev); + + return ret; +} + +static int mt9m114_pa_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mt9m114 *sensor = pa_ctrl_to_mt9m114(ctrl); + const struct v4l2_mbus_framefmt *format; + struct v4l2_subdev_state *state; + int ret = 0; + u64 mask; + + /* V4L2 controls values are applied only when power is up. */ + if (!pm_runtime_get_if_in_use(&sensor->client->dev)) + return 0; + + state = v4l2_subdev_get_locked_active_state(&sensor->pa.sd); + format = v4l2_subdev_state_get_format(state, 0); + + switch (ctrl->id) { + case V4L2_CID_HBLANK: + cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_LINE_LENGTH_PCK, + ctrl->val + format->width, &ret); + break; + + case V4L2_CID_VBLANK: + cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_FRAME_LENGTH_LINES, + ctrl->val + format->height, &ret); + break; + + case V4L2_CID_EXPOSURE: + cci_write(sensor->regmap, + MT9M114_CAM_SENSOR_CONTROL_COARSE_INTEGRATION_TIME, + ctrl->val, &ret); + break; + + case V4L2_CID_ANALOGUE_GAIN: + /* + * The CAM_SENSOR_CONTROL_ANALOG_GAIN contains linear analog + * gain values that are mapped to the GLOBAL_GAIN register + * values by the sensor firmware. + */ + cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CONTROL_ANALOG_GAIN, + ctrl->val, &ret); + break; + + case V4L2_CID_HFLIP: + mask = MT9M114_CAM_SENSOR_CONTROL_HORZ_MIRROR_EN; + ret = cci_update_bits(sensor->regmap, + MT9M114_CAM_SENSOR_CONTROL_READ_MODE, + mask, ctrl->val ? mask : 0, NULL); + break; + + case V4L2_CID_VFLIP: + mask = MT9M114_CAM_SENSOR_CONTROL_VERT_FLIP_EN; + ret = cci_update_bits(sensor->regmap, + MT9M114_CAM_SENSOR_CONTROL_READ_MODE, + mask, ctrl->val ? mask : 0, NULL); + break; + + default: + ret = -EINVAL; + break; + } + + pm_runtime_mark_last_busy(&sensor->client->dev); + pm_runtime_put_autosuspend(&sensor->client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops mt9m114_pa_ctrl_ops = { + .g_volatile_ctrl = mt9m114_pa_g_ctrl, + .s_ctrl = mt9m114_pa_s_ctrl, +}; + +static void mt9m114_pa_ctrl_update_exposure(struct mt9m114 *sensor, bool manual) +{ + /* + * Update the volatile flag on the manual exposure and gain controls. + * If the controls have switched to manual, read their current value + * from the hardware to ensure that control read and write operations + * will behave correctly + */ + if (manual) { + mt9m114_pa_g_ctrl(sensor->pa.exposure); + sensor->pa.exposure->cur.val = sensor->pa.exposure->val; + sensor->pa.exposure->flags &= ~V4L2_CTRL_FLAG_VOLATILE; + + mt9m114_pa_g_ctrl(sensor->pa.gain); + sensor->pa.gain->cur.val = sensor->pa.gain->val; + sensor->pa.gain->flags &= ~V4L2_CTRL_FLAG_VOLATILE; + } else { + sensor->pa.exposure->flags |= V4L2_CTRL_FLAG_VOLATILE; + sensor->pa.gain->flags |= V4L2_CTRL_FLAG_VOLATILE; + } +} + +static void mt9m114_pa_ctrl_update_blanking(struct mt9m114 *sensor, + const struct v4l2_mbus_framefmt *format) +{ + unsigned int max_blank; + + /* Update the blanking controls ranges based on the output size. */ + max_blank = MT9M114_CAM_SENSOR_CFG_LINE_LENGTH_PCK_MAX + - format->width; + __v4l2_ctrl_modify_range(sensor->pa.hblank, MT9M114_MIN_HBLANK, + max_blank, 1, MT9M114_DEF_HBLANK); + + max_blank = MT9M114_CAM_SENSOR_CFG_FRAME_LENGTH_LINES_MAX + - format->height; + __v4l2_ctrl_modify_range(sensor->pa.vblank, MT9M114_MIN_VBLANK, + max_blank, 1, MT9M114_DEF_VBLANK); +} + +/* ----------------------------------------------------------------------------- + * Pixel Array Subdev Operations + */ + +static inline struct mt9m114 *pa_to_mt9m114(struct v4l2_subdev *sd) +{ + return container_of(sd, struct mt9m114, pa.sd); +} + +static int mt9m114_pa_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + + crop = v4l2_subdev_state_get_crop(state, 0); + + crop->left = 0; + crop->top = 0; + crop->width = MT9M114_PIXEL_ARRAY_WIDTH; + crop->height = MT9M114_PIXEL_ARRAY_HEIGHT; + + format = v4l2_subdev_state_get_format(state, 0); + + format->width = MT9M114_PIXEL_ARRAY_WIDTH; + format->height = MT9M114_PIXEL_ARRAY_HEIGHT; + format->code = MEDIA_BUS_FMT_SGRBG10_1X10; + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_RAW; + format->ycbcr_enc = V4L2_YCBCR_ENC_601; + format->quantization = V4L2_QUANTIZATION_FULL_RANGE; + format->xfer_func = V4L2_XFER_FUNC_NONE; + + return 0; +} + +static int mt9m114_pa_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_SGRBG10_1X10; + + return 0; +} + +static int mt9m114_pa_enum_framesizes(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index > 1) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) + return -EINVAL; + + /* Report binning capability through frame size enumeration. */ + fse->min_width = MT9M114_PIXEL_ARRAY_WIDTH / (fse->index + 1); + fse->max_width = MT9M114_PIXEL_ARRAY_WIDTH / (fse->index + 1); + fse->min_height = MT9M114_PIXEL_ARRAY_HEIGHT / (fse->index + 1); + fse->max_height = MT9M114_PIXEL_ARRAY_HEIGHT / (fse->index + 1); + + return 0; +} + +static int mt9m114_pa_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *fmt) +{ + struct mt9m114 *sensor = pa_to_mt9m114(sd); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + unsigned int hscale; + unsigned int vscale; + + crop = v4l2_subdev_state_get_crop(state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); + + /* The sensor can bin horizontally and vertically. */ + hscale = DIV_ROUND_CLOSEST(crop->width, fmt->format.width ? : 1); + vscale = DIV_ROUND_CLOSEST(crop->height, fmt->format.height ? : 1); + format->width = crop->width / clamp(hscale, 1U, 2U); + format->height = crop->height / clamp(vscale, 1U, 2U); + + fmt->format = *format; + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + mt9m114_pa_ctrl_update_blanking(sensor, format); + + return 0; +} + +static int mt9m114_pa_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(state, sel->pad); + return 0; + + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = MT9M114_PIXEL_ARRAY_WIDTH; + sel->r.height = MT9M114_PIXEL_ARRAY_HEIGHT; + return 0; + + default: + return -EINVAL; + } +} + +static int mt9m114_pa_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + struct mt9m114 *sensor = pa_to_mt9m114(sd); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + crop = v4l2_subdev_state_get_crop(state, sel->pad); + format = v4l2_subdev_state_get_format(state, sel->pad); + + /* + * Clamp the crop rectangle. The vertical coordinates must be even, and + * the horizontal coordinates must be a multiple of 4. + * + * FIXME: The horizontal coordinates must be a multiple of 8 when + * binning, but binning is configured after setting the selection, so + * we can't know tell here if it will be used. + */ + crop->left = ALIGN(sel->r.left, 4); + crop->top = ALIGN(sel->r.top, 2); + crop->width = clamp_t(unsigned int, ALIGN(sel->r.width, 4), + MT9M114_PIXEL_ARRAY_MIN_OUTPUT_WIDTH, + MT9M114_PIXEL_ARRAY_WIDTH - crop->left); + crop->height = clamp_t(unsigned int, ALIGN(sel->r.height, 2), + MT9M114_PIXEL_ARRAY_MIN_OUTPUT_HEIGHT, + MT9M114_PIXEL_ARRAY_HEIGHT - crop->top); + + sel->r = *crop; + + /* Reset the format. */ + format->width = crop->width; + format->height = crop->height; + + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) + mt9m114_pa_ctrl_update_blanking(sensor, format); + + return 0; +} + +static const struct v4l2_subdev_pad_ops mt9m114_pa_pad_ops = { + .enum_mbus_code = mt9m114_pa_enum_mbus_code, + .enum_frame_size = mt9m114_pa_enum_framesizes, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = mt9m114_pa_set_fmt, + .get_selection = mt9m114_pa_get_selection, + .set_selection = mt9m114_pa_set_selection, +}; + +static const struct v4l2_subdev_ops mt9m114_pa_ops = { + .pad = &mt9m114_pa_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops mt9m114_pa_internal_ops = { + .init_state = mt9m114_pa_init_state, +}; + +static int mt9m114_pa_init(struct mt9m114 *sensor) +{ + struct v4l2_ctrl_handler *hdl = &sensor->pa.hdl; + struct v4l2_subdev *sd = &sensor->pa.sd; + struct media_pad *pads = &sensor->pa.pad; + const struct v4l2_mbus_framefmt *format; + struct v4l2_subdev_state *state; + unsigned int max_exposure; + int ret; + + /* Initialize the subdev. */ + v4l2_subdev_init(sd, &mt9m114_pa_ops); + sd->internal_ops = &mt9m114_pa_internal_ops; + v4l2_i2c_subdev_set_name(sd, sensor->client, NULL, " pixel array"); + + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->owner = THIS_MODULE; + sd->dev = &sensor->client->dev; + v4l2_set_subdevdata(sd, sensor->client); + + /* Initialize the media entity. */ + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + sd->entity.ops = &mt9m114_entity_ops; + pads[0].flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&sd->entity, 1, pads); + if (ret < 0) + return ret; + + /* Initialize the control handler. */ + v4l2_ctrl_handler_init(hdl, 7); + + /* The range of the HBLANK and VBLANK controls will be updated below. */ + sensor->pa.hblank = v4l2_ctrl_new_std(hdl, &mt9m114_pa_ctrl_ops, + V4L2_CID_HBLANK, + MT9M114_DEF_HBLANK, + MT9M114_DEF_HBLANK, 1, + MT9M114_DEF_HBLANK); + sensor->pa.vblank = v4l2_ctrl_new_std(hdl, &mt9m114_pa_ctrl_ops, + V4L2_CID_VBLANK, + MT9M114_DEF_VBLANK, + MT9M114_DEF_VBLANK, 1, + MT9M114_DEF_VBLANK); + + /* + * The maximum coarse integration time is the frame length in lines + * minus two. The default is taken directly from the datasheet, but + * makes little sense as auto-exposure is enabled by default. + */ + max_exposure = MT9M114_PIXEL_ARRAY_HEIGHT + MT9M114_MIN_VBLANK - 2; + sensor->pa.exposure = v4l2_ctrl_new_std(hdl, &mt9m114_pa_ctrl_ops, + V4L2_CID_EXPOSURE, 1, + max_exposure, 1, 16); + if (sensor->pa.exposure) + sensor->pa.exposure->flags |= V4L2_CTRL_FLAG_VOLATILE; + + sensor->pa.gain = v4l2_ctrl_new_std(hdl, &mt9m114_pa_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, 1, + 511, 1, 32); + if (sensor->pa.gain) + sensor->pa.gain->flags |= V4L2_CTRL_FLAG_VOLATILE; + + v4l2_ctrl_new_std(hdl, &mt9m114_pa_ctrl_ops, + V4L2_CID_PIXEL_RATE, + sensor->pixrate, sensor->pixrate, 1, + sensor->pixrate); + + v4l2_ctrl_new_std(hdl, &mt9m114_pa_ctrl_ops, + V4L2_CID_HFLIP, + 0, 1, 1, 0); + v4l2_ctrl_new_std(hdl, &mt9m114_pa_ctrl_ops, + V4L2_CID_VFLIP, + 0, 1, 1, 0); + + if (hdl->error) { + ret = hdl->error; + goto error; + } + + sd->state_lock = hdl->lock; + + ret = v4l2_subdev_init_finalize(sd); + if (ret) + goto error; + + /* Update the range of the blanking controls based on the format. */ + state = v4l2_subdev_lock_and_get_active_state(sd); + format = v4l2_subdev_state_get_format(state, 0); + mt9m114_pa_ctrl_update_blanking(sensor, format); + v4l2_subdev_unlock_state(state); + + sd->ctrl_handler = hdl; + + return 0; + +error: + v4l2_ctrl_handler_free(&sensor->pa.hdl); + media_entity_cleanup(&sensor->pa.sd.entity); + return ret; +} + +static void mt9m114_pa_cleanup(struct mt9m114 *sensor) +{ + v4l2_ctrl_handler_free(&sensor->pa.hdl); + media_entity_cleanup(&sensor->pa.sd.entity); +} + +/* ----------------------------------------------------------------------------- + * Image Flow Processor Control Operations + */ + +static const char * const mt9m114_test_pattern_menu[] = { + "Disabled", + "Solid Color", + "100% Color Bars", + "Pseudo-Random", + "Fade-to-Gray Color Bars", + "Walking Ones 10-bit", + "Walking Ones 8-bit", +}; + +/* Keep in sync with mt9m114_test_pattern_menu */ +static const unsigned int mt9m114_test_pattern_value[] = { + MT9M114_CAM_MODE_TEST_PATTERN_SELECT_SOLID, + MT9M114_CAM_MODE_TEST_PATTERN_SELECT_SOLID_BARS, + MT9M114_CAM_MODE_TEST_PATTERN_SELECT_RANDOM, + MT9M114_CAM_MODE_TEST_PATTERN_SELECT_FADING_BARS, + MT9M114_CAM_MODE_TEST_PATTERN_SELECT_WALKING_1S_10B, + MT9M114_CAM_MODE_TEST_PATTERN_SELECT_WALKING_1S_8B, +}; + +static inline struct mt9m114 *ifp_ctrl_to_mt9m114(struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct mt9m114, ifp.hdl); +} + +static int mt9m114_ifp_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mt9m114 *sensor = ifp_ctrl_to_mt9m114(ctrl); + u32 value; + int ret = 0; + + if (ctrl->id == V4L2_CID_EXPOSURE_AUTO) + mt9m114_pa_ctrl_update_exposure(sensor, + ctrl->val != V4L2_EXPOSURE_AUTO); + + /* V4L2 controls values are applied only when power is up. */ + if (!pm_runtime_get_if_in_use(&sensor->client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_AUTO_WHITE_BALANCE: + /* Control both the AWB mode and the CCM algorithm. */ + if (ctrl->val) + value = MT9M114_CAM_AWB_MODE_AUTO + | MT9M114_CAM_AWB_MODE_EXCLUSIVE_AE; + else + value = 0; + + cci_write(sensor->regmap, MT9M114_CAM_AWB_AWBMODE, value, &ret); + + if (ctrl->val) + value = MT9M114_CCM_EXEC_CALC_CCM_MATRIX | 0x22; + else + value = 0; + + cci_write(sensor->regmap, MT9M114_CCM_ALGO, value, &ret); + break; + + case V4L2_CID_EXPOSURE_AUTO: + if (ctrl->val == V4L2_EXPOSURE_AUTO) + value = MT9M114_AE_TRACK_EXEC_AUTOMATIC_EXPOSURE + | 0x00fe; + else + value = 0; + + cci_write(sensor->regmap, MT9M114_AE_TRACK_ALGO, value, &ret); + if (ret) + break; + + break; + + case V4L2_CID_TEST_PATTERN: + case V4L2_CID_TEST_PATTERN_RED: + case V4L2_CID_TEST_PATTERN_GREENR: + case V4L2_CID_TEST_PATTERN_BLUE: { + unsigned int pattern = sensor->ifp.tpg[MT9M114_TPG_PATTERN]->val; + + if (pattern) { + cci_write(sensor->regmap, MT9M114_CAM_MODE_SELECT, + MT9M114_CAM_MODE_SELECT_TEST_PATTERN, &ret); + cci_write(sensor->regmap, + MT9M114_CAM_MODE_TEST_PATTERN_SELECT, + mt9m114_test_pattern_value[pattern - 1], &ret); + cci_write(sensor->regmap, + MT9M114_CAM_MODE_TEST_PATTERN_RED, + sensor->ifp.tpg[MT9M114_TPG_RED]->val, &ret); + cci_write(sensor->regmap, + MT9M114_CAM_MODE_TEST_PATTERN_GREEN, + sensor->ifp.tpg[MT9M114_TPG_GREEN]->val, &ret); + cci_write(sensor->regmap, + MT9M114_CAM_MODE_TEST_PATTERN_BLUE, + sensor->ifp.tpg[MT9M114_TPG_BLUE]->val, &ret); + } else { + cci_write(sensor->regmap, MT9M114_CAM_MODE_SELECT, + MT9M114_CAM_MODE_SELECT_NORMAL, &ret); + } + + /* + * A Config-Change needs to be issued for the change to take + * effect. If we're not streaming ignore this, the change will + * be applied when the stream is started. + */ + if (ret || !sensor->streaming) + break; + + ret = mt9m114_set_state(sensor, + MT9M114_SYS_STATE_ENTER_CONFIG_CHANGE); + break; + } + + default: + ret = -EINVAL; + break; + } + + pm_runtime_mark_last_busy(&sensor->client->dev); + pm_runtime_put_autosuspend(&sensor->client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops mt9m114_ifp_ctrl_ops = { + .s_ctrl = mt9m114_ifp_s_ctrl, +}; + +/* ----------------------------------------------------------------------------- + * Image Flow Processor Subdev Operations + */ + +static inline struct mt9m114 *ifp_to_mt9m114(struct v4l2_subdev *sd) +{ + return container_of(sd, struct mt9m114, ifp.sd); +} + +static int mt9m114_ifp_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct mt9m114 *sensor = ifp_to_mt9m114(sd); + struct v4l2_subdev_state *pa_state; + struct v4l2_subdev_state *ifp_state; + int ret; + + if (!enable) + return mt9m114_stop_streaming(sensor); + + ifp_state = v4l2_subdev_lock_and_get_active_state(&sensor->ifp.sd); + pa_state = v4l2_subdev_lock_and_get_active_state(&sensor->pa.sd); + + ret = mt9m114_start_streaming(sensor, pa_state, ifp_state); + + v4l2_subdev_unlock_state(pa_state); + v4l2_subdev_unlock_state(ifp_state); + + return ret; +} + +static int mt9m114_ifp_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *interval) +{ + struct v4l2_fract *ival = &interval->interval; + struct mt9m114 *sensor = ifp_to_mt9m114(sd); + + mutex_lock(sensor->ifp.hdl.lock); + + ival->numerator = 1; + ival->denominator = sensor->ifp.frame_rate; + + mutex_unlock(sensor->ifp.hdl.lock); + + return 0; +} + +static int mt9m114_ifp_s_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *interval) +{ + struct v4l2_fract *ival = &interval->interval; + struct mt9m114 *sensor = ifp_to_mt9m114(sd); + int ret = 0; + + mutex_lock(sensor->ifp.hdl.lock); + + if (ival->numerator != 0 && ival->denominator != 0) + sensor->ifp.frame_rate = min_t(unsigned int, + ival->denominator / ival->numerator, + MT9M114_MAX_FRAME_RATE); + else + sensor->ifp.frame_rate = MT9M114_MAX_FRAME_RATE; + + ival->numerator = 1; + ival->denominator = sensor->ifp.frame_rate; + + if (sensor->streaming) + ret = mt9m114_set_frame_rate(sensor); + + mutex_unlock(sensor->ifp.hdl.lock); + + return ret; +} + +static int mt9m114_ifp_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct mt9m114 *sensor = ifp_to_mt9m114(sd); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + struct v4l2_rect *compose; + + format = v4l2_subdev_state_get_format(state, 0); + + format->width = MT9M114_PIXEL_ARRAY_WIDTH; + format->height = MT9M114_PIXEL_ARRAY_HEIGHT; + format->code = MEDIA_BUS_FMT_SGRBG10_1X10; + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_RAW; + format->ycbcr_enc = V4L2_YCBCR_ENC_601; + format->quantization = V4L2_QUANTIZATION_FULL_RANGE; + format->xfer_func = V4L2_XFER_FUNC_NONE; + + crop = v4l2_subdev_state_get_crop(state, 0); + + crop->left = 4; + crop->top = 4; + crop->width = format->width - 8; + crop->height = format->height - 8; + + compose = v4l2_subdev_state_get_compose(state, 0); + + compose->left = 0; + compose->top = 0; + compose->width = crop->width; + compose->height = crop->height; + + format = v4l2_subdev_state_get_format(state, 1); + + format->width = compose->width; + format->height = compose->height; + format->code = mt9m114_default_format_info(sensor)->code; + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_SRGB; + format->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + format->quantization = V4L2_QUANTIZATION_DEFAULT; + format->xfer_func = V4L2_XFER_FUNC_DEFAULT; + + return 0; +} + +static int mt9m114_ifp_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) +{ + const unsigned int num_formats = ARRAY_SIZE(mt9m114_format_infos); + struct mt9m114 *sensor = ifp_to_mt9m114(sd); + unsigned int index = 0; + unsigned int flag; + unsigned int i; + + switch (code->pad) { + case 0: + if (code->index != 0) + return -EINVAL; + + code->code = mt9m114_format_infos[num_formats - 1].code; + return 0; + + case 1: + if (sensor->bus_cfg.bus_type == V4L2_MBUS_CSI2_DPHY) + flag = MT9M114_FMT_FLAG_CSI2; + else + flag = MT9M114_FMT_FLAG_PARALLEL; + + for (i = 0; i < num_formats; ++i) { + const struct mt9m114_format_info *info = + &mt9m114_format_infos[i]; + + if (info->flags & flag) { + if (index == code->index) { + code->code = info->code; + return 0; + } + + index++; + } + } + + return -EINVAL; + + default: + return -EINVAL; + } +} + +static int mt9m114_ifp_enum_framesizes(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct mt9m114 *sensor = ifp_to_mt9m114(sd); + const struct mt9m114_format_info *info; + + if (fse->index > 0) + return -EINVAL; + + info = mt9m114_format_info(sensor, fse->pad, fse->code); + if (!info || info->code != fse->code) + return -EINVAL; + + if (fse->pad == 0) { + fse->min_width = MT9M114_PIXEL_ARRAY_MIN_OUTPUT_WIDTH; + fse->max_width = MT9M114_PIXEL_ARRAY_WIDTH; + fse->min_height = MT9M114_PIXEL_ARRAY_MIN_OUTPUT_HEIGHT; + fse->max_height = MT9M114_PIXEL_ARRAY_HEIGHT; + } else { + const struct v4l2_rect *crop; + + crop = v4l2_subdev_state_get_crop(state, 0); + + fse->max_width = crop->width; + fse->max_height = crop->height; + + fse->min_width = fse->max_width / 4; + fse->min_height = fse->max_height / 4; + } + + return 0; +} + +static int mt9m114_ifp_enum_frameintervals(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct mt9m114 *sensor = ifp_to_mt9m114(sd); + const struct mt9m114_format_info *info; + + if (fie->index > 0) + return -EINVAL; + + info = mt9m114_format_info(sensor, fie->pad, fie->code); + if (!info || info->code != fie->code) + return -EINVAL; + + fie->interval.numerator = 1; + fie->interval.denominator = MT9M114_MAX_FRAME_RATE; + + return 0; +} + +static int mt9m114_ifp_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *fmt) +{ + struct mt9m114 *sensor = ifp_to_mt9m114(sd); + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_state_get_format(state, fmt->pad); + + if (fmt->pad == 0) { + /* Only the size can be changed on the sink pad. */ + format->width = clamp(ALIGN(fmt->format.width, 8), + MT9M114_PIXEL_ARRAY_MIN_OUTPUT_WIDTH, + MT9M114_PIXEL_ARRAY_WIDTH); + format->height = clamp(ALIGN(fmt->format.height, 8), + MT9M114_PIXEL_ARRAY_MIN_OUTPUT_HEIGHT, + MT9M114_PIXEL_ARRAY_HEIGHT); + } else { + const struct mt9m114_format_info *info; + + /* Only the media bus code can be changed on the source pad. */ + info = mt9m114_format_info(sensor, 1, fmt->format.code); + + format->code = info->code; + + /* If the output format is RAW10, bypass the scaler. */ + if (format->code == MEDIA_BUS_FMT_SGRBG10_1X10) + *format = *v4l2_subdev_state_get_format(state, 0); + } + + fmt->format = *format; + + return 0; +} + +static int mt9m114_ifp_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + const struct v4l2_mbus_framefmt *format; + const struct v4l2_rect *crop; + int ret = 0; + + /* Crop and compose are only supported on the sink pad. */ + if (sel->pad != 0) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(state, 0); + break; + + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + /* + * The crop default and bounds are equal to the sink + * format size minus 4 pixels on each side for demosaicing. + */ + format = v4l2_subdev_state_get_format(state, 0); + + sel->r.left = 4; + sel->r.top = 4; + sel->r.width = format->width - 8; + sel->r.height = format->height - 8; + break; + + case V4L2_SEL_TGT_COMPOSE: + sel->r = *v4l2_subdev_state_get_compose(state, 0); + break; + + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + /* + * The compose default and bounds sizes are equal to the sink + * crop rectangle size. + */ + crop = v4l2_subdev_state_get_crop(state, 0); + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = crop->width; + sel->r.height = crop->height; + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int mt9m114_ifp_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + struct v4l2_rect *compose; + + if (sel->target != V4L2_SEL_TGT_CROP && + sel->target != V4L2_SEL_TGT_COMPOSE) + return -EINVAL; + + /* Crop and compose are only supported on the sink pad. */ + if (sel->pad != 0) + return -EINVAL; + + format = v4l2_subdev_state_get_format(state, 0); + crop = v4l2_subdev_state_get_crop(state, 0); + compose = v4l2_subdev_state_get_compose(state, 0); + + if (sel->target == V4L2_SEL_TGT_CROP) { + /* + * Clamp the crop rectangle. Demosaicing removes 4 pixels on + * each side of the image. + */ + crop->left = clamp_t(unsigned int, ALIGN(sel->r.left, 2), 4, + format->width - 4 - + MT9M114_SCALER_CROPPED_INPUT_WIDTH); + crop->top = clamp_t(unsigned int, ALIGN(sel->r.top, 2), 4, + format->height - 4 - + MT9M114_SCALER_CROPPED_INPUT_HEIGHT); + crop->width = clamp_t(unsigned int, ALIGN(sel->r.width, 2), + MT9M114_SCALER_CROPPED_INPUT_WIDTH, + format->width - 4 - crop->left); + crop->height = clamp_t(unsigned int, ALIGN(sel->r.height, 2), + MT9M114_SCALER_CROPPED_INPUT_HEIGHT, + format->height - 4 - crop->top); + + sel->r = *crop; + + /* Propagate to the compose rectangle. */ + compose->width = crop->width; + compose->height = crop->height; + } else { + /* + * Clamp the compose rectangle. The scaler can only downscale. + */ + compose->left = 0; + compose->top = 0; + compose->width = clamp_t(unsigned int, ALIGN(sel->r.width, 2), + MT9M114_SCALER_CROPPED_INPUT_WIDTH, + crop->width); + compose->height = clamp_t(unsigned int, ALIGN(sel->r.height, 2), + MT9M114_SCALER_CROPPED_INPUT_HEIGHT, + crop->height); + + sel->r = *compose; + } + + /* Propagate the compose rectangle to the source format. */ + format = v4l2_subdev_state_get_format(state, 1); + format->width = compose->width; + format->height = compose->height; + + return 0; +} + +static void mt9m114_ifp_unregistered(struct v4l2_subdev *sd) +{ + struct mt9m114 *sensor = ifp_to_mt9m114(sd); + + v4l2_device_unregister_subdev(&sensor->pa.sd); +} + +static int mt9m114_ifp_registered(struct v4l2_subdev *sd) +{ + struct mt9m114 *sensor = ifp_to_mt9m114(sd); + int ret; + + ret = v4l2_device_register_subdev(sd->v4l2_dev, &sensor->pa.sd); + if (ret < 0) { + dev_err(&sensor->client->dev, + "Failed to register pixel array subdev\n"); + return ret; + } + + ret = media_create_pad_link(&sensor->pa.sd.entity, 0, + &sensor->ifp.sd.entity, 0, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret < 0) { + dev_err(&sensor->client->dev, + "Failed to link pixel array to ifp\n"); + v4l2_device_unregister_subdev(&sensor->pa.sd); + return ret; + } + + return 0; +} + +static const struct v4l2_subdev_video_ops mt9m114_ifp_video_ops = { + .s_stream = mt9m114_ifp_s_stream, + .g_frame_interval = mt9m114_ifp_g_frame_interval, + .s_frame_interval = mt9m114_ifp_s_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops mt9m114_ifp_pad_ops = { + .enum_mbus_code = mt9m114_ifp_enum_mbus_code, + .enum_frame_size = mt9m114_ifp_enum_framesizes, + .enum_frame_interval = mt9m114_ifp_enum_frameintervals, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = mt9m114_ifp_set_fmt, + .get_selection = mt9m114_ifp_get_selection, + .set_selection = mt9m114_ifp_set_selection, +}; + +static const struct v4l2_subdev_ops mt9m114_ifp_ops = { + .video = &mt9m114_ifp_video_ops, + .pad = &mt9m114_ifp_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops mt9m114_ifp_internal_ops = { + .init_state = mt9m114_ifp_init_state, + .registered = mt9m114_ifp_registered, + .unregistered = mt9m114_ifp_unregistered, +}; + +static int mt9m114_ifp_init(struct mt9m114 *sensor) +{ + struct v4l2_subdev *sd = &sensor->ifp.sd; + struct media_pad *pads = sensor->ifp.pads; + struct v4l2_ctrl_handler *hdl = &sensor->ifp.hdl; + struct v4l2_ctrl *link_freq; + int ret; + + /* Initialize the subdev. */ + v4l2_i2c_subdev_init(sd, sensor->client, &mt9m114_ifp_ops); + v4l2_i2c_subdev_set_name(sd, sensor->client, NULL, " ifp"); + + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->internal_ops = &mt9m114_ifp_internal_ops; + + /* Initialize the media entity. */ + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_ISP; + sd->entity.ops = &mt9m114_entity_ops; + pads[0].flags = MEDIA_PAD_FL_SINK; + pads[1].flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&sd->entity, 2, pads); + if (ret < 0) + return ret; + + sensor->ifp.frame_rate = MT9M114_DEF_FRAME_RATE; + + /* Initialize the control handler. */ + v4l2_ctrl_handler_init(hdl, 8); + v4l2_ctrl_new_std(hdl, &mt9m114_ifp_ctrl_ops, + V4L2_CID_AUTO_WHITE_BALANCE, + 0, 1, 1, 1); + v4l2_ctrl_new_std_menu(hdl, &mt9m114_ifp_ctrl_ops, + V4L2_CID_EXPOSURE_AUTO, + V4L2_EXPOSURE_MANUAL, 0, + V4L2_EXPOSURE_AUTO); + + link_freq = v4l2_ctrl_new_int_menu(hdl, &mt9m114_ifp_ctrl_ops, + V4L2_CID_LINK_FREQ, + sensor->bus_cfg.nr_of_link_frequencies - 1, + 0, sensor->bus_cfg.link_frequencies); + if (link_freq) + link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(hdl, &mt9m114_ifp_ctrl_ops, + V4L2_CID_PIXEL_RATE, + sensor->pixrate, sensor->pixrate, 1, + sensor->pixrate); + + sensor->ifp.tpg[MT9M114_TPG_PATTERN] = + v4l2_ctrl_new_std_menu_items(hdl, &mt9m114_ifp_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(mt9m114_test_pattern_menu) - 1, + 0, 0, mt9m114_test_pattern_menu); + sensor->ifp.tpg[MT9M114_TPG_RED] = + v4l2_ctrl_new_std(hdl, &mt9m114_ifp_ctrl_ops, + V4L2_CID_TEST_PATTERN_RED, + 0, 1023, 1, 1023); + sensor->ifp.tpg[MT9M114_TPG_GREEN] = + v4l2_ctrl_new_std(hdl, &mt9m114_ifp_ctrl_ops, + V4L2_CID_TEST_PATTERN_GREENR, + 0, 1023, 1, 1023); + sensor->ifp.tpg[MT9M114_TPG_BLUE] = + v4l2_ctrl_new_std(hdl, &mt9m114_ifp_ctrl_ops, + V4L2_CID_TEST_PATTERN_BLUE, + 0, 1023, 1, 1023); + + v4l2_ctrl_cluster(ARRAY_SIZE(sensor->ifp.tpg), sensor->ifp.tpg); + + if (hdl->error) { + ret = hdl->error; + goto error; + } + + sd->ctrl_handler = hdl; + sd->state_lock = hdl->lock; + + ret = v4l2_subdev_init_finalize(sd); + if (ret) + goto error; + + return 0; + +error: + v4l2_ctrl_handler_free(&sensor->ifp.hdl); + media_entity_cleanup(&sensor->ifp.sd.entity); + return ret; +} + +static void mt9m114_ifp_cleanup(struct mt9m114 *sensor) +{ + v4l2_ctrl_handler_free(&sensor->ifp.hdl); + media_entity_cleanup(&sensor->ifp.sd.entity); +} + +/* ----------------------------------------------------------------------------- + * Power Management + */ + +static int mt9m114_power_on(struct mt9m114 *sensor) +{ + int ret; + + /* Enable power and clocks. */ + ret = regulator_bulk_enable(ARRAY_SIZE(sensor->supplies), + sensor->supplies); + if (ret < 0) + return ret; + + ret = clk_prepare_enable(sensor->clk); + if (ret < 0) + goto error_regulator; + + /* Perform a hard reset if available, or a soft reset otherwise. */ + if (sensor->reset) { + long freq = clk_get_rate(sensor->clk); + unsigned int duration; + + /* + * The minimum duration is 50 clock cycles, thus typically + * around 2µs. Double it to be safe. + */ + duration = DIV_ROUND_UP(2 * 50 * 1000000, freq); + + gpiod_set_value(sensor->reset, 1); + udelay(duration); + gpiod_set_value(sensor->reset, 0); + } else { + /* + * The power may have just been turned on, we need to wait for + * the sensor to be ready to accept I2C commands. + */ + usleep_range(44500, 50000); + + cci_write(sensor->regmap, MT9M114_RESET_AND_MISC_CONTROL, + MT9M114_RESET_SOC, &ret); + cci_write(sensor->regmap, MT9M114_RESET_AND_MISC_CONTROL, 0, + &ret); + + if (ret < 0) { + dev_err(&sensor->client->dev, "Soft reset failed\n"); + goto error_clock; + } + } + + /* + * Wait for the sensor to be ready to accept I2C commands by polling the + * command register to wait for initialization to complete. + */ + usleep_range(44500, 50000); + + ret = mt9m114_poll_command(sensor, MT9M114_COMMAND_REGISTER_SET_STATE); + if (ret < 0) + goto error_clock; + + if (sensor->bus_cfg.bus_type == V4L2_MBUS_PARALLEL) { + /* + * In parallel mode (OE set to low), the sensor will enter the + * streaming state after initialization. Enter the standby + * manually to stop streaming. + */ + ret = mt9m114_set_state(sensor, + MT9M114_SYS_STATE_ENTER_STANDBY); + if (ret < 0) + goto error_clock; + } + + /* + * Before issuing any Set-State command, we must ensure that the sensor + * reaches the standby mode (either initiated manually above in + * parallel mode, or automatically after reset in MIPI mode). + */ + ret = mt9m114_poll_state(sensor, MT9M114_SYS_STATE_STANDBY); + if (ret < 0) + goto error_clock; + + return 0; + +error_clock: + clk_disable_unprepare(sensor->clk); +error_regulator: + regulator_bulk_disable(ARRAY_SIZE(sensor->supplies), sensor->supplies); + return ret; +} + +static void mt9m114_power_off(struct mt9m114 *sensor) +{ + clk_disable_unprepare(sensor->clk); + regulator_bulk_disable(ARRAY_SIZE(sensor->supplies), sensor->supplies); +} + +static int __maybe_unused mt9m114_runtime_resume(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct mt9m114 *sensor = ifp_to_mt9m114(sd); + int ret; + + ret = mt9m114_power_on(sensor); + if (ret) + return ret; + + ret = mt9m114_initialize(sensor); + if (ret) { + mt9m114_power_off(sensor); + return ret; + } + + return 0; +} + +static int __maybe_unused mt9m114_runtime_suspend(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct mt9m114 *sensor = ifp_to_mt9m114(sd); + + mt9m114_power_off(sensor); + + return 0; +} + +static const struct dev_pm_ops mt9m114_pm_ops = { + SET_RUNTIME_PM_OPS(mt9m114_runtime_suspend, mt9m114_runtime_resume, NULL) +}; + +/* ----------------------------------------------------------------------------- + * Probe & Remove + */ + +static int mt9m114_clk_init(struct mt9m114 *sensor) +{ + unsigned int link_freq; + + /* Hardcode the PLL multiplier and dividers to default settings. */ + sensor->pll.m = 32; + sensor->pll.n = 1; + sensor->pll.p = 7; + + /* + * Calculate the pixel rate and link frequency. The CSI-2 bus is clocked + * for 16-bit per pixel, transmitted in DDR over a single lane. For + * parallel mode, the sensor ouputs one pixel in two PIXCLK cycles. + */ + sensor->pixrate = clk_get_rate(sensor->clk) * sensor->pll.m + / ((sensor->pll.n + 1) * (sensor->pll.p + 1)); + + link_freq = sensor->bus_cfg.bus_type == V4L2_MBUS_CSI2_DPHY + ? sensor->pixrate * 8 : sensor->pixrate * 2; + + if (sensor->bus_cfg.nr_of_link_frequencies != 1 || + sensor->bus_cfg.link_frequencies[0] != link_freq) { + dev_err(&sensor->client->dev, "Unsupported DT link-frequencies\n"); + return -EINVAL; + } + + return 0; +} + +static int mt9m114_identify(struct mt9m114 *sensor) +{ + u64 major, minor, release, customer; + u64 value; + int ret; + + ret = cci_read(sensor->regmap, MT9M114_CHIP_ID, &value, NULL); + if (ret) { + dev_err(&sensor->client->dev, "Failed to read chip ID\n"); + return -ENXIO; + } + + if (value != 0x2481) { + dev_err(&sensor->client->dev, "Invalid chip ID 0x%04llx\n", + value); + return -ENXIO; + } + + cci_read(sensor->regmap, MT9M114_MON_MAJOR_VERSION, &major, &ret); + cci_read(sensor->regmap, MT9M114_MON_MINOR_VERSION, &minor, &ret); + cci_read(sensor->regmap, MT9M114_MON_RELEASE_VERSION, &release, &ret); + cci_read(sensor->regmap, MT9M114_CUSTOMER_REV, &customer, &ret); + if (ret) { + dev_err(&sensor->client->dev, "Failed to read version\n"); + return -ENXIO; + } + + dev_dbg(&sensor->client->dev, + "monitor v%llu.%llu.%04llx customer rev 0x%04llx\n", + major, minor, release, customer); + + return 0; +} + +static int mt9m114_parse_dt(struct mt9m114 *sensor) +{ + struct fwnode_handle *fwnode = dev_fwnode(&sensor->client->dev); + struct fwnode_handle *ep; + int ret; + + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) { + dev_err(&sensor->client->dev, "No endpoint found\n"); + return -EINVAL; + } + + sensor->bus_cfg.bus_type = V4L2_MBUS_UNKNOWN; + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &sensor->bus_cfg); + fwnode_handle_put(ep); + if (ret < 0) { + dev_err(&sensor->client->dev, "Failed to parse endpoint\n"); + goto error; + } + + switch (sensor->bus_cfg.bus_type) { + case V4L2_MBUS_CSI2_DPHY: + case V4L2_MBUS_PARALLEL: + break; + + default: + dev_err(&sensor->client->dev, "unsupported bus type %u\n", + sensor->bus_cfg.bus_type); + ret = -EINVAL; + goto error; + } + + return 0; + +error: + v4l2_fwnode_endpoint_free(&sensor->bus_cfg); + return ret; +} + +static int mt9m114_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct mt9m114 *sensor; + int ret; + + sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) + return -ENOMEM; + + sensor->client = client; + + sensor->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(sensor->regmap)) { + dev_err(dev, "Unable to initialize I2C\n"); + return -ENODEV; + } + + ret = mt9m114_parse_dt(sensor); + if (ret < 0) + return ret; + + /* Acquire clocks, GPIOs and regulators. */ + sensor->clk = devm_clk_get(dev, NULL); + if (IS_ERR(sensor->clk)) { + ret = PTR_ERR(sensor->clk); + dev_err_probe(dev, ret, "Failed to get clock\n"); + goto error_ep_free; + } + + sensor->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(sensor->reset)) { + ret = PTR_ERR(sensor->reset); + dev_err_probe(dev, ret, "Failed to get reset GPIO\n"); + goto error_ep_free; + } + + sensor->supplies[0].supply = "vddio"; + sensor->supplies[1].supply = "vdd"; + sensor->supplies[2].supply = "vaa"; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(sensor->supplies), + sensor->supplies); + if (ret < 0) { + dev_err_probe(dev, ret, "Failed to get regulators\n"); + goto error_ep_free; + } + + ret = mt9m114_clk_init(sensor); + if (ret) + goto error_ep_free; + + /* + * Identify the sensor. The driver supports runtime PM, but needs to + * work when runtime PM is disabled in the kernel. To that end, power + * the sensor on manually here, and initialize it after identification + * to reach the same state as if resumed through runtime PM. + */ + ret = mt9m114_power_on(sensor); + if (ret < 0) { + dev_err_probe(dev, ret, "Could not power on the device\n"); + goto error_ep_free; + } + + ret = mt9m114_identify(sensor); + if (ret < 0) + goto error_power_off; + + ret = mt9m114_initialize(sensor); + if (ret < 0) + goto error_power_off; + + /* + * Enable runtime PM with autosuspend. As the device has been powered + * manually, mark it as active, and increase the usage count without + * resuming the device. + */ + pm_runtime_set_active(dev); + pm_runtime_get_noresume(dev); + pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_use_autosuspend(dev); + + /* Initialize the subdevices. */ + ret = mt9m114_pa_init(sensor); + if (ret < 0) + goto error_pm_cleanup; + + ret = mt9m114_ifp_init(sensor); + if (ret < 0) + goto error_pa_cleanup; + + ret = v4l2_async_register_subdev(&sensor->ifp.sd); + if (ret < 0) + goto error_ifp_cleanup; + + /* + * Decrease the PM usage count. The device will get suspended after the + * autosuspend delay, turning the power off. + */ + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return 0; + +error_ifp_cleanup: + mt9m114_ifp_cleanup(sensor); +error_pa_cleanup: + mt9m114_pa_cleanup(sensor); +error_pm_cleanup: + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); +error_power_off: + mt9m114_power_off(sensor); +error_ep_free: + v4l2_fwnode_endpoint_free(&sensor->bus_cfg); + return ret; +} + +static void mt9m114_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct mt9m114 *sensor = ifp_to_mt9m114(sd); + struct device *dev = &client->dev; + + v4l2_async_unregister_subdev(&sensor->ifp.sd); + + mt9m114_ifp_cleanup(sensor); + mt9m114_pa_cleanup(sensor); + v4l2_fwnode_endpoint_free(&sensor->bus_cfg); + + /* + * Disable runtime PM. In case runtime PM is disabled in the kernel, + * make sure to turn power off manually. + */ + pm_runtime_disable(dev); + if (!pm_runtime_status_suspended(dev)) + mt9m114_power_off(sensor); + pm_runtime_set_suspended(dev); +} + +static const struct of_device_id mt9m114_of_ids[] = { + { .compatible = "onnn,mt9m114" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, mt9m114_of_ids); + +static struct i2c_driver mt9m114_driver = { + .driver = { + .name = "mt9m114", + .pm = &mt9m114_pm_ops, + .of_match_table = mt9m114_of_ids, + }, + .probe = mt9m114_probe, + .remove = mt9m114_remove, +}; + +module_i2c_driver(mt9m114_driver); + +MODULE_DESCRIPTION("onsemi MT9M114 Sensor Driver"); +MODULE_AUTHOR("Laurent Pinchart "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c index 348f1e1098fb87..596200d0248cf0 100644 --- a/drivers/media/i2c/mt9p031.c +++ b/drivers/media/i2c/mt9p031.c @@ -549,8 +549,7 @@ __mt9p031_get_pad_format(struct mt9p031 *mt9p031, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&mt9p031->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &mt9p031->format; default: @@ -565,8 +564,7 @@ __mt9p031_get_pad_crop(struct mt9p031 *mt9p031, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&mt9p031->subdev, sd_state, - pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &mt9p031->crop; default: @@ -698,8 +696,8 @@ static int mt9p031_set_selection(struct v4l2_subdev *subdev, return 0; } -static int mt9p031_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state) +static int mt9p031_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state) { struct mt9p031 *mt9p031 = to_mt9p031(subdev); struct v4l2_mbus_framefmt *format; @@ -1043,7 +1041,6 @@ static const struct v4l2_subdev_video_ops mt9p031_subdev_video_ops = { }; static const struct v4l2_subdev_pad_ops mt9p031_subdev_pad_ops = { - .init_cfg = mt9p031_init_cfg, .enum_mbus_code = mt9p031_enum_mbus_code, .enum_frame_size = mt9p031_enum_frame_size, .get_fmt = mt9p031_get_format, @@ -1059,6 +1056,7 @@ static const struct v4l2_subdev_ops mt9p031_subdev_ops = { }; static const struct v4l2_subdev_internal_ops mt9p031_subdev_internal_ops = { + .init_state = mt9p031_init_state, .registered = mt9p031_registered, .open = mt9p031_open, .close = mt9p031_close, @@ -1191,7 +1189,7 @@ static int mt9p031_probe(struct i2c_client *client) mt9p031->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - ret = mt9p031_init_cfg(&mt9p031->subdev, NULL); + ret = mt9p031_init_state(&mt9p031->subdev, NULL); if (ret) goto done; diff --git a/drivers/media/i2c/mt9t112.c b/drivers/media/i2c/mt9t112.c index 93f34b767027af..fb1588c57cc806 100644 --- a/drivers/media/i2c/mt9t112.c +++ b/drivers/media/i2c/mt9t112.c @@ -982,7 +982,6 @@ static int mt9t112_set_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) return mt9t112_s_fmt(sd, mf); - sd_state->pads->try_fmt = *mf; return 0; } diff --git a/drivers/media/i2c/mt9v011.c b/drivers/media/i2c/mt9v011.c index 774861ba7747d5..d0924b4ac6fb05 100644 --- a/drivers/media/i2c/mt9v011.c +++ b/drivers/media/i2c/mt9v011.c @@ -49,9 +49,7 @@ MODULE_PARM_DESC(debug, "Debug level (0-2)"); struct mt9v011 { struct v4l2_subdev sd; -#ifdef CONFIG_MEDIA_CONTROLLER struct media_pad pad; -#endif struct v4l2_ctrl_handler ctrls; unsigned width, height; unsigned xtal; @@ -358,7 +356,7 @@ static int mt9v011_set_fmt(struct v4l2_subdev *sd, set_res(sd); } else { - sd_state->pads->try_fmt = *fmt; + *v4l2_subdev_state_get_format(sd_state, 0) = *fmt; } return 0; @@ -483,9 +481,7 @@ static int mt9v011_probe(struct i2c_client *c) u16 version; struct mt9v011 *core; struct v4l2_subdev *sd; -#ifdef CONFIG_MEDIA_CONTROLLER int ret; -#endif /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(c->adapter, @@ -499,14 +495,12 @@ static int mt9v011_probe(struct i2c_client *c) sd = &core->sd; v4l2_i2c_subdev_init(sd, c, &mt9v011_ops); -#ifdef CONFIG_MEDIA_CONTROLLER core->pad.flags = MEDIA_PAD_FL_SOURCE; sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; ret = media_entity_pads_init(&sd->entity, 1, &core->pad); if (ret < 0) return ret; -#endif /* Check if the sensor is really a MT9V011 */ version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION); diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index 00e7bc6e3235c6..c5614f535d1a4b 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -355,8 +355,7 @@ __mt9v032_get_pad_format(struct mt9v032 *mt9v032, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&mt9v032->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &mt9v032->format; default: @@ -371,8 +370,7 @@ __mt9v032_get_pad_crop(struct mt9v032 *mt9v032, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&mt9v032->subdev, sd_state, - pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &mt9v032->crop; default: @@ -930,13 +928,13 @@ static int mt9v032_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - crop = v4l2_subdev_get_try_crop(subdev, fh->state, 0); + crop = v4l2_subdev_state_get_crop(fh->state, 0); crop->left = MT9V032_COLUMN_START_DEF; crop->top = MT9V032_ROW_START_DEF; crop->width = MT9V032_WINDOW_WIDTH_DEF; crop->height = MT9V032_WINDOW_HEIGHT_DEF; - format = v4l2_subdev_get_try_format(subdev, fh->state, 0); + format = v4l2_subdev_state_get_format(fh->state, 0); if (mt9v032->model->color) format->code = MEDIA_BUS_FMT_SGRBG10_1X10; diff --git a/drivers/media/i2c/mt9v111.c b/drivers/media/i2c/mt9v111.c index 1f7edc0f5b1abe..b186e9160d94b3 100644 --- a/drivers/media/i2c/mt9v111.c +++ b/drivers/media/i2c/mt9v111.c @@ -121,9 +121,7 @@ struct mt9v111_dev { u8 addr_space; struct v4l2_subdev sd; -#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER) struct media_pad pad; -#endif struct v4l2_ctrl *auto_awb; struct v4l2_ctrl *auto_exp; @@ -797,11 +795,7 @@ static struct v4l2_mbus_framefmt *__mt9v111_get_pad_format( { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: -#if IS_ENABLED(CONFIG_VIDEO_V4L2_SUBDEV_API) - return v4l2_subdev_get_try_format(&mt9v111->sd, sd_state, pad); -#else - return &sd_state->pads->try_fmt; -#endif + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &mt9v111->fmt; default: @@ -954,10 +948,10 @@ static int mt9v111_set_format(struct v4l2_subdev *subdev, return 0; } -static int mt9v111_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state) +static int mt9v111_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state) { - sd_state->pads->try_fmt = mt9v111_def_fmt; + *v4l2_subdev_state_get_format(sd_state, 0) = mt9v111_def_fmt; return 0; } @@ -973,7 +967,6 @@ static const struct v4l2_subdev_video_ops mt9v111_video_ops = { }; static const struct v4l2_subdev_pad_ops mt9v111_pad_ops = { - .init_cfg = mt9v111_init_cfg, .enum_mbus_code = mt9v111_enum_mbus_code, .enum_frame_size = mt9v111_enum_frame_size, .enum_frame_interval = mt9v111_enum_frame_interval, @@ -987,11 +980,13 @@ static const struct v4l2_subdev_ops mt9v111_ops = { .pad = &mt9v111_pad_ops, }; -#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER) +static const struct v4l2_subdev_internal_ops mt9v111_internal_ops = { + .init_state = mt9v111_init_state, +}; + static const struct media_entity_operations mt9v111_subdev_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; -#endif /* --- V4L2 ctrl --- */ static int mt9v111_s_ctrl(struct v4l2_ctrl *ctrl) @@ -1202,8 +1197,8 @@ static int mt9v111_probe(struct i2c_client *client) mt9v111->pending = true; v4l2_i2c_subdev_init(&mt9v111->sd, client, &mt9v111_ops); + mt9v111->sd.internal_ops = &mt9v111_internal_ops; -#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER) mt9v111->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; mt9v111->sd.entity.ops = &mt9v111_subdev_entity_ops; mt9v111->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; @@ -1212,7 +1207,6 @@ static int mt9v111_probe(struct i2c_client *client) ret = media_entity_pads_init(&mt9v111->sd.entity, 1, &mt9v111->pad); if (ret) goto error_free_entity; -#endif ret = mt9v111_chip_probe(mt9v111); if (ret) @@ -1225,9 +1219,7 @@ static int mt9v111_probe(struct i2c_client *client) return 0; error_free_entity: -#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER) media_entity_cleanup(&mt9v111->sd.entity); -#endif error_free_ctrls: v4l2_ctrl_handler_free(&mt9v111->ctrls); @@ -1245,9 +1237,7 @@ static void mt9v111_remove(struct i2c_client *client) v4l2_async_unregister_subdev(sd); -#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER) media_entity_cleanup(&sd->entity); -#endif v4l2_ctrl_handler_free(&mt9v111->ctrls); diff --git a/drivers/media/i2c/og01a1b.c b/drivers/media/i2c/og01a1b.c index 365ce568458360..1051b59affa39e 100644 --- a/drivers/media/i2c/og01a1b.c +++ b/drivers/media/i2c/og01a1b.c @@ -815,8 +815,7 @@ static int og01a1b_set_format(struct v4l2_subdev *sd, mutex_lock(&og01a1b->mutex); og01a1b_update_pad_format(mode, &fmt->format); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad) = fmt->format; + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; } else { og01a1b->cur_mode = mode; __v4l2_ctrl_s_ctrl(og01a1b->link_freq, mode->link_freq_index); @@ -849,9 +848,8 @@ static int og01a1b_get_format(struct v4l2_subdev *sd, mutex_lock(&og01a1b->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&og01a1b->sd, - sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); else og01a1b_update_pad_format(og01a1b->cur_mode, &fmt->format); @@ -896,7 +894,7 @@ static int og01a1b_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) mutex_lock(&og01a1b->mutex); og01a1b_update_pad_format(&supported_modes[0], - v4l2_subdev_get_try_format(sd, fh->state, 0)); + v4l2_subdev_state_get_format(fh->state, 0)); mutex_unlock(&og01a1b->mutex); return 0; diff --git a/drivers/media/i2c/ov01a10.c b/drivers/media/i2c/ov01a10.c index 9afe9bf50334a9..478a39e0d5c8a2 100644 --- a/drivers/media/i2c/ov01a10.c +++ b/drivers/media/i2c/ov01a10.c @@ -771,14 +771,14 @@ static int ov01a10_set_format(struct v4l2_subdev *sd, h_blank); } - format = v4l2_subdev_get_pad_format(sd, sd_state, fmt->stream); + format = v4l2_subdev_state_get_format(sd_state, fmt->stream); *format = fmt->format; return 0; } -static int ov01a10_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int ov01a10_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct v4l2_subdev_format fmt = { .which = V4L2_SUBDEV_FORMAT_TRY, @@ -861,7 +861,6 @@ static const struct v4l2_subdev_video_ops ov01a10_video_ops = { }; static const struct v4l2_subdev_pad_ops ov01a10_pad_ops = { - .init_cfg = ov01a10_init_cfg, .set_fmt = ov01a10_set_format, .get_fmt = v4l2_subdev_get_fmt, .get_selection = ov01a10_get_selection, @@ -875,6 +874,10 @@ static const struct v4l2_subdev_ops ov01a10_subdev_ops = { .pad = &ov01a10_pad_ops, }; +static const struct v4l2_subdev_internal_ops ov01a10_internal_ops = { + .init_state = ov01a10_init_state, +}; + static const struct media_entity_operations ov01a10_subdev_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -921,6 +924,7 @@ static int ov01a10_probe(struct i2c_client *client) return -ENOMEM; v4l2_i2c_subdev_init(&ov01a10->sd, client, &ov01a10_subdev_ops); + ov01a10->sd.internal_ops = &ov01a10_internal_ops; ret = ov01a10_identify_module(ov01a10); if (ret) diff --git a/drivers/media/i2c/ov02a10.c b/drivers/media/i2c/ov02a10.c index 741d977a76f32f..ccce5ff7228002 100644 --- a/drivers/media/i2c/ov02a10.c +++ b/drivers/media/i2c/ov02a10.c @@ -315,7 +315,7 @@ static int ov02a10_set_fmt(struct v4l2_subdev *sd, ov02a10_fill_fmt(ov02a10->cur_mode, mbus_fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - frame_fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); + frame_fmt = v4l2_subdev_state_get_format(sd_state, 0); else frame_fmt = &ov02a10->fmt; @@ -336,8 +336,8 @@ static int ov02a10_get_fmt(struct v4l2_subdev *sd, mutex_lock(&ov02a10->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - fmt->format = *v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); } else { fmt->format = ov02a10->fmt; mbus_fmt->code = ov02a10->fmt.code; @@ -511,8 +511,8 @@ static int __ov02a10_stop_stream(struct ov02a10 *ov02a10) SC_CTRL_MODE_STANDBY); } -static int ov02a10_entity_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int ov02a10_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { .which = V4L2_SUBDEV_FORMAT_TRY, @@ -711,7 +711,6 @@ static const struct v4l2_subdev_video_ops ov02a10_video_ops = { }; static const struct v4l2_subdev_pad_ops ov02a10_pad_ops = { - .init_cfg = ov02a10_entity_init_cfg, .enum_mbus_code = ov02a10_enum_mbus_code, .enum_frame_size = ov02a10_enum_frame_sizes, .get_fmt = ov02a10_get_fmt, @@ -723,6 +722,10 @@ static const struct v4l2_subdev_ops ov02a10_subdev_ops = { .pad = &ov02a10_pad_ops, }; +static const struct v4l2_subdev_internal_ops ov02a10_internal_ops = { + .init_state = ov02a10_init_state, +}; + static const struct media_entity_operations ov02a10_subdev_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -871,6 +874,7 @@ static int ov02a10_probe(struct i2c_client *client) "failed to check HW configuration\n"); v4l2_i2c_subdev_init(&ov02a10->subdev, client, &ov02a10_subdev_ops); + ov02a10->subdev.internal_ops = &ov02a10_internal_ops; ov02a10->mipi_clock_voltage = OV02A10_MIPI_TX_SPEED_DEFAULT; ov02a10->fmt.code = MEDIA_BUS_FMT_SBGGR10_1X10; diff --git a/drivers/media/i2c/ov08d10.c b/drivers/media/i2c/ov08d10.c index 7d55d4ca24de18..d790f7d2652656 100644 --- a/drivers/media/i2c/ov08d10.c +++ b/drivers/media/i2c/ov08d10.c @@ -1192,7 +1192,7 @@ static int ov08d10_set_format(struct v4l2_subdev *sd, mutex_lock(&ov08d10->mutex); ov08d10_update_pad_format(ov08d10, mode, &fmt->format); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; } else { ov08d10->cur_mode = mode; @@ -1231,9 +1231,8 @@ static int ov08d10_get_format(struct v4l2_subdev *sd, mutex_lock(&ov08d10->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&ov08d10->sd, - sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); else ov08d10_update_pad_format(ov08d10, ov08d10->cur_mode, &fmt->format); @@ -1289,7 +1288,7 @@ static int ov08d10_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) mutex_lock(&ov08d10->mutex); ov08d10_update_pad_format(ov08d10, &ov08d10->priv_lane->sp_modes[0], - v4l2_subdev_get_try_format(sd, fh->state, 0)); + v4l2_subdev_state_get_format(fh->state, 0)); mutex_unlock(&ov08d10->mutex); return 0; diff --git a/drivers/media/i2c/ov08x40.c b/drivers/media/i2c/ov08x40.c index 637da4df69011d..ab63d06a68cb4f 100644 --- a/drivers/media/i2c/ov08x40.c +++ b/drivers/media/i2c/ov08x40.c @@ -2539,7 +2539,7 @@ static int ov08x40_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) const struct ov08x40_mode *default_mode = &supported_modes[0]; struct ov08x40 *ov08x = to_ov08x40(sd); struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); mutex_lock(&ov08x->mutex); @@ -2777,10 +2777,9 @@ static int ov08x40_do_get_pad_format(struct ov08x40 *ov08x, struct v4l2_subdev_format *fmt) { struct v4l2_mbus_framefmt *framefmt; - struct v4l2_subdev *sd = &ov08x->sd; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *framefmt; } else { ov08x40_update_pad_format(ov08x->cur_mode, fmt); @@ -2829,7 +2828,7 @@ ov08x40_set_pad_format(struct v4l2_subdev *sd, fmt->format.width, fmt->format.height); ov08x40_update_pad_format(mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); *framefmt = fmt->format; } else { ov08x->cur_mode = mode; diff --git a/drivers/media/i2c/ov13858.c b/drivers/media/i2c/ov13858.c index 35652b36234726..1ccd1f8c67afe8 100644 --- a/drivers/media/i2c/ov13858.c +++ b/drivers/media/i2c/ov13858.c @@ -1150,9 +1150,8 @@ static int ov13858_write_reg_list(struct ov13858 *ov13858, static int ov13858_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct ov13858 *ov13858 = to_ov13858(sd); - struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_get_try_format(sd, - fh->state, - 0); + struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_state_get_format(fh->state, + 0); mutex_lock(&ov13858->mutex); @@ -1320,10 +1319,9 @@ static int ov13858_do_get_pad_format(struct ov13858 *ov13858, struct v4l2_subdev_format *fmt) { struct v4l2_mbus_framefmt *framefmt; - struct v4l2_subdev *sd = &ov13858->sd; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *framefmt; } else { ov13858_update_pad_format(ov13858->cur_mode, fmt); @@ -1372,7 +1370,7 @@ ov13858_set_pad_format(struct v4l2_subdev *sd, fmt->format.width, fmt->format.height); ov13858_update_pad_format(mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); *framefmt = fmt->format; } else { ov13858->cur_mode = mode; diff --git a/drivers/media/i2c/ov13b10.c b/drivers/media/i2c/ov13b10.c index 124eaa4f9e2ca8..1ae6cc4e38abd0 100644 --- a/drivers/media/i2c/ov13b10.c +++ b/drivers/media/i2c/ov13b10.c @@ -702,9 +702,8 @@ static int ov13b10_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { const struct ov13b10_mode *default_mode = &supported_modes[0]; struct ov13b10 *ov13b = to_ov13b10(sd); - struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_get_try_format(sd, - fh->state, - 0); + struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_state_get_format(fh->state, + 0); mutex_lock(&ov13b->mutex); @@ -949,10 +948,9 @@ static int ov13b10_do_get_pad_format(struct ov13b10 *ov13b, struct v4l2_subdev_format *fmt) { struct v4l2_mbus_framefmt *framefmt; - struct v4l2_subdev *sd = &ov13b->sd; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *framefmt; } else { ov13b10_update_pad_format(ov13b->cur_mode, fmt); @@ -1001,7 +999,7 @@ ov13b10_set_pad_format(struct v4l2_subdev *sd, fmt->format.width, fmt->format.height); ov13b10_update_pad_format(mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); *framefmt = fmt->format; } else { ov13b->cur_mode = mode; diff --git a/drivers/media/i2c/ov2311.c b/drivers/media/i2c/ov2311.c index fa5bb797d3d67d..3680561ad779b7 100644 --- a/drivers/media/i2c/ov2311.c +++ b/drivers/media/i2c/ov2311.c @@ -484,7 +484,7 @@ static int ov2311_set_fmt(struct v4l2_subdev *sd, V4L2_MAP_XFER_FUNC_DEFAULT(fmt->format.colorspace); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; } else { ov2311->cur_mode = mode; @@ -519,7 +519,7 @@ static int ov2311_get_fmt(struct v4l2_subdev *sd, mutex_lock(&ov2311->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - fmt->format = *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, fmt->pad); } else { fmt->format.width = mode->width; fmt->format.height = mode->height; @@ -596,7 +596,7 @@ __ov2311_get_pad_crop(struct ov2311 *ov2311, struct v4l2_subdev_state *sd_state, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&ov2311->subdev, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &ov2311->cur_mode->crop; } @@ -802,7 +802,7 @@ static int ov2311_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct ov2311 *ov2311 = to_ov2311(sd); struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); const struct ov2311_mode *def_mode = &supported_modes[0]; mutex_lock(&ov2311->mutex); diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c index bb6c9863a5460f..db9dcf732d21b6 100644 --- a/drivers/media/i2c/ov2640.c +++ b/drivers/media/i2c/ov2640.c @@ -293,9 +293,7 @@ struct ov2640_win_size { struct ov2640_priv { struct v4l2_subdev subdev; -#if defined(CONFIG_MEDIA_CONTROLLER) struct media_pad pad; -#endif struct v4l2_ctrl_handler hdl; u32 cfmt_code; struct clk *clk; @@ -922,13 +920,9 @@ static int ov2640_get_fmt(struct v4l2_subdev *sd, return -EINVAL; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - mf = v4l2_subdev_get_try_format(sd, sd_state, 0); + mf = v4l2_subdev_state_get_format(sd_state, 0); format->format = *mf; return 0; -#else - return -EINVAL; -#endif } mf->width = priv->win->width; @@ -994,7 +988,7 @@ static int ov2640_set_fmt(struct v4l2_subdev *sd, /* select format */ priv->cfmt_code = mf->code; } else { - sd_state->pads->try_fmt = *mf; + *v4l2_subdev_state_get_format(sd_state, 0) = *mf; } out: mutex_unlock(&priv->lock); @@ -1002,12 +996,11 @@ static int ov2640_set_fmt(struct v4l2_subdev *sd, return ret; } -static int ov2640_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int ov2640_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, sd_state, 0); + v4l2_subdev_state_get_format(sd_state, 0); const struct ov2640_win_size *win = ov2640_select_win(SVGA_WIDTH, SVGA_HEIGHT); @@ -1019,7 +1012,7 @@ static int ov2640_init_cfg(struct v4l2_subdev *sd, try_fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; try_fmt->quantization = V4L2_QUANTIZATION_DEFAULT; try_fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; -#endif + return 0; } @@ -1132,7 +1125,6 @@ static const struct v4l2_subdev_core_ops ov2640_subdev_core_ops = { }; static const struct v4l2_subdev_pad_ops ov2640_subdev_pad_ops = { - .init_cfg = ov2640_init_cfg, .enum_mbus_code = ov2640_enum_mbus_code, .get_selection = ov2640_get_selection, .get_fmt = ov2640_get_fmt, @@ -1149,6 +1141,10 @@ static const struct v4l2_subdev_ops ov2640_subdev_ops = { .video = &ov2640_subdev_video_ops, }; +static const struct v4l2_subdev_internal_ops ov2640_internal_ops = { + .init_state = ov2640_init_state, +}; + static int ov2640_probe_dt(struct i2c_client *client, struct ov2640_priv *priv) { @@ -1221,6 +1217,7 @@ static int ov2640_probe(struct i2c_client *client) priv->cfmt_code = MEDIA_BUS_FMT_UYVY8_2X8; v4l2_i2c_subdev_init(&priv->subdev, client, &ov2640_subdev_ops); + priv->subdev.internal_ops = &ov2640_internal_ops; priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; mutex_init(&priv->lock); @@ -1239,13 +1236,11 @@ static int ov2640_probe(struct i2c_client *client) ret = priv->hdl.error; goto err_hdl; } -#if defined(CONFIG_MEDIA_CONTROLLER) priv->pad.flags = MEDIA_PAD_FL_SOURCE; priv->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; ret = media_entity_pads_init(&priv->subdev.entity, 1, &priv->pad); if (ret < 0) goto err_hdl; -#endif ret = ov2640_video_probe(client); if (ret < 0) diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c index 5429bd2eb05318..1d0ef72a64036b 100644 --- a/drivers/media/i2c/ov2659.c +++ b/drivers/media/i2c/ov2659.c @@ -1031,17 +1031,13 @@ static int ov2659_get_fmt(struct v4l2_subdev *sd, dev_dbg(&client->dev, "ov2659_get_fmt\n"); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_get_try_format(sd, sd_state, 0); + mf = v4l2_subdev_state_get_format(sd_state, 0); mutex_lock(&ov2659->lock); fmt->format = *mf; mutex_unlock(&ov2659->lock); return 0; -#else - return -EINVAL; -#endif } mutex_lock(&ov2659->lock); @@ -1113,10 +1109,8 @@ static int ov2659_set_fmt(struct v4l2_subdev *sd, mutex_lock(&ov2659->lock); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); *mf = fmt->format; -#endif } else { s64 val; @@ -1306,12 +1300,11 @@ static int ov2659_power_on(struct device *dev) * V4L2 subdev internal operations */ -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API static int ov2659_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); dev_dbg(&client->dev, "%s:\n", __func__); @@ -1319,7 +1312,6 @@ static int ov2659_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) return 0; } -#endif static const struct v4l2_subdev_core_ops ov2659_subdev_core_ops = { .log_status = v4l2_ctrl_subdev_log_status, @@ -1338,7 +1330,6 @@ static const struct v4l2_subdev_pad_ops ov2659_subdev_pad_ops = { .set_fmt = ov2659_set_fmt, }; -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API static const struct v4l2_subdev_ops ov2659_subdev_ops = { .core = &ov2659_subdev_core_ops, .video = &ov2659_subdev_video_ops, @@ -1348,7 +1339,6 @@ static const struct v4l2_subdev_ops ov2659_subdev_ops = { static const struct v4l2_subdev_internal_ops ov2659_subdev_internal_ops = { .open = ov2659_open, }; -#endif static int ov2659_detect(struct v4l2_subdev *sd) { @@ -1489,15 +1479,12 @@ static int ov2659_probe(struct i2c_client *client) sd = &ov2659->sd; client->flags |= I2C_CLIENT_SCCB; -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - v4l2_i2c_subdev_init(sd, client, &ov2659_subdev_ops); + v4l2_i2c_subdev_init(sd, client, &ov2659_subdev_ops); sd->internal_ops = &ov2659_subdev_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; -#endif -#if defined(CONFIG_MEDIA_CONTROLLER) ov2659->pad.flags = MEDIA_PAD_FL_SOURCE; sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; ret = media_entity_pads_init(&sd->entity, 1, &ov2659->pad); @@ -1505,7 +1492,6 @@ static int ov2659_probe(struct i2c_client *client) v4l2_ctrl_handler_free(&ov2659->ctrls); return ret; } -#endif mutex_init(&ov2659->lock); diff --git a/drivers/media/i2c/ov2680.c b/drivers/media/i2c/ov2680.c index 6436879f95c015..20cf7caea5f5ae 100644 --- a/drivers/media/i2c/ov2680.c +++ b/drivers/media/i2c/ov2680.c @@ -309,7 +309,7 @@ __ov2680_get_pad_format(struct ov2680_dev *sensor, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&sensor->sd, state, pad); + return v4l2_subdev_state_get_format(state, pad); return &sensor->mode.fmt; } @@ -321,7 +321,7 @@ __ov2680_get_pad_crop(struct ov2680_dev *sensor, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&sensor->sd, state, pad); + return v4l2_subdev_state_get_crop(state, pad); return &sensor->mode.crop; } @@ -650,7 +650,7 @@ static int ov2680_set_fmt(struct v4l2_subdev *sd, ov2680_fill_format(sensor, &format->format, width, height); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - try_fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); + try_fmt = v4l2_subdev_state_get_format(sd_state, 0); *try_fmt = format->format; return 0; } @@ -755,14 +755,14 @@ static int ov2680_set_selection(struct v4l2_subdev *sd, return 0; } -static int ov2680_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int ov2680_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct ov2680_dev *sensor = to_ov2680_dev(sd); - sd_state->pads[0].try_crop = ov2680_default_crop; + *v4l2_subdev_state_get_crop(sd_state, 0) = ov2680_default_crop; - ov2680_fill_format(sensor, &sd_state->pads[0].try_fmt, + ov2680_fill_format(sensor, v4l2_subdev_state_get_format(sd_state, 0), OV2680_DEFAULT_WIDTH, OV2680_DEFAULT_HEIGHT); return 0; } @@ -876,7 +876,6 @@ static const struct v4l2_subdev_video_ops ov2680_video_ops = { }; static const struct v4l2_subdev_pad_ops ov2680_pad_ops = { - .init_cfg = ov2680_init_cfg, .enum_mbus_code = ov2680_enum_mbus_code, .enum_frame_size = ov2680_enum_frame_size, .enum_frame_interval = ov2680_enum_frame_interval, @@ -891,6 +890,10 @@ static const struct v4l2_subdev_ops ov2680_subdev_ops = { .pad = &ov2680_pad_ops, }; +static const struct v4l2_subdev_internal_ops ov2680_internal_ops = { + .init_state = ov2680_init_state, +}; + static int ov2680_mode_init(struct ov2680_dev *sensor) { /* set initial mode */ @@ -915,6 +918,7 @@ static int ov2680_v4l2_register(struct ov2680_dev *sensor) int ret = 0; v4l2_i2c_subdev_init(&sensor->sd, client, &ov2680_subdev_ops); + sensor->sd.internal_ops = &ov2680_internal_ops; sensor->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; sensor->pad.flags = MEDIA_PAD_FL_SOURCE; diff --git a/drivers/media/i2c/ov2685.c b/drivers/media/i2c/ov2685.c index 303793e1f97d64..8c935202ac9bfc 100644 --- a/drivers/media/i2c/ov2685.c +++ b/drivers/media/i2c/ov2685.c @@ -405,7 +405,7 @@ __ov2685_get_pad_crop(struct ov2685 *ov2685, switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&ov2685->subdev, state, pad); + return v4l2_subdev_state_get_crop(state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return mode->analog_crop; } @@ -547,7 +547,6 @@ static int ov2685_s_stream(struct v4l2_subdev *sd, int on) return ret; } -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API static int ov2685_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct ov2685 *ov2685 = to_ov2685(sd); @@ -555,7 +554,7 @@ static int ov2685_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) mutex_lock(&ov2685->mutex); - try_fmt = v4l2_subdev_get_try_format(sd, fh->state, 0); + try_fmt = v4l2_subdev_state_get_format(fh->state, 0); /* Initialize try_fmt */ ov2685_fill_fmt(&supported_modes[0], try_fmt); @@ -563,7 +562,6 @@ static int ov2685_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) return 0; } -#endif static int __maybe_unused ov2685_runtime_resume(struct device *dev) { @@ -660,11 +658,9 @@ static const struct v4l2_subdev_ops ov2685_subdev_ops = { .pad = &ov2685_pad_ops, }; -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API static const struct v4l2_subdev_internal_ops ov2685_internal_ops = { .open = ov2685_open, }; -#endif static const struct v4l2_ctrl_ops ov2685_ctrl_ops = { .s_ctrl = ov2685_set_ctrl, @@ -833,17 +829,13 @@ static int ov2685_probe(struct i2c_client *client) if (ret) goto err_power_off; -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API ov2685->subdev.internal_ops = &ov2685_internal_ops; ov2685->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; -#endif -#if defined(CONFIG_MEDIA_CONTROLLER) ov2685->pad.flags = MEDIA_PAD_FL_SOURCE; ov2685->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; ret = media_entity_pads_init(&ov2685->subdev.entity, 1, &ov2685->pad); if (ret < 0) goto err_power_off; -#endif ret = v4l2_async_register_subdev(&ov2685->subdev); if (ret) { @@ -858,9 +850,7 @@ static int ov2685_probe(struct i2c_client *client) return 0; err_clean_entity: -#if defined(CONFIG_MEDIA_CONTROLLER) media_entity_cleanup(&ov2685->subdev.entity); -#endif err_power_off: __ov2685_power_off(ov2685); err_free_handler: @@ -877,9 +867,7 @@ static void ov2685_remove(struct i2c_client *client) struct ov2685 *ov2685 = to_ov2685(sd); v4l2_async_unregister_subdev(sd); -#if defined(CONFIG_MEDIA_CONTROLLER) media_entity_cleanup(&sd->entity); -#endif v4l2_ctrl_handler_free(&ov2685->ctrl_handler); mutex_destroy(&ov2685->mutex); diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c index 41d4f85470fd21..036b62d33648f8 100644 --- a/drivers/media/i2c/ov2740.c +++ b/drivers/media/i2c/ov2740.c @@ -336,12 +336,6 @@ struct ov2740 { /* Current mode */ const struct ov2740_mode *cur_mode; - /* To serialize asynchronus callbacks */ - struct mutex mutex; - - /* Streaming on/off */ - bool streaming; - /* NVM data inforamtion */ struct nvm_data *nvm; @@ -582,7 +576,6 @@ static int ov2740_init_controls(struct ov2740 *ov2740) if (ret) return ret; - ctrl_hdlr->lock = &ov2740->mutex; cur_mode = ov2740->cur_mode; size = ARRAY_SIZE(link_freq_menu_items); @@ -792,18 +785,15 @@ static int ov2740_set_stream(struct v4l2_subdev *sd, int enable) { struct ov2740 *ov2740 = to_ov2740(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); + struct v4l2_subdev_state *sd_state; int ret = 0; - if (ov2740->streaming == enable) - return 0; + sd_state = v4l2_subdev_lock_and_get_active_state(&ov2740->sd); - mutex_lock(&ov2740->mutex); if (enable) { ret = pm_runtime_resume_and_get(&client->dev); - if (ret < 0) { - mutex_unlock(&ov2740->mutex); - return ret; - } + if (ret < 0) + goto out_unlock; ret = ov2740_start_streaming(ov2740); if (ret) { @@ -816,47 +806,12 @@ static int ov2740_set_stream(struct v4l2_subdev *sd, int enable) pm_runtime_put(&client->dev); } - ov2740->streaming = enable; - mutex_unlock(&ov2740->mutex); +out_unlock: + v4l2_subdev_unlock_state(sd_state); return ret; } -static int ov2740_suspend(struct device *dev) -{ - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct ov2740 *ov2740 = to_ov2740(sd); - - mutex_lock(&ov2740->mutex); - if (ov2740->streaming) - ov2740_stop_streaming(ov2740); - - mutex_unlock(&ov2740->mutex); - - return 0; -} - -static int ov2740_resume(struct device *dev) -{ - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct ov2740 *ov2740 = to_ov2740(sd); - int ret = 0; - - mutex_lock(&ov2740->mutex); - if (!ov2740->streaming) - goto exit; - - ret = ov2740_start_streaming(ov2740); - if (ret) { - ov2740->streaming = false; - ov2740_stop_streaming(ov2740); - } - -exit: - mutex_unlock(&ov2740->mutex); - return ret; -} - static int ov2740_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -870,48 +825,26 @@ static int ov2740_set_format(struct v4l2_subdev *sd, height, fmt->format.width, fmt->format.height); - mutex_lock(&ov2740->mutex); ov2740_update_pad_format(mode, &fmt->format); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; - } else { - ov2740->cur_mode = mode; - __v4l2_ctrl_s_ctrl(ov2740->link_freq, mode->link_freq_index); - __v4l2_ctrl_s_ctrl_int64(ov2740->pixel_rate, - to_pixel_rate(mode->link_freq_index)); - - /* Update limits and set FPS to default */ - vblank_def = mode->vts_def - mode->height; - __v4l2_ctrl_modify_range(ov2740->vblank, - mode->vts_min - mode->height, - OV2740_VTS_MAX - mode->height, 1, - vblank_def); - __v4l2_ctrl_s_ctrl(ov2740->vblank, vblank_def); - h_blank = to_pixels_per_line(mode->hts, mode->link_freq_index) - - mode->width; - __v4l2_ctrl_modify_range(ov2740->hblank, h_blank, h_blank, 1, - h_blank); - } - mutex_unlock(&ov2740->mutex); - - return 0; -} - -static int ov2740_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct ov2740 *ov2740 = to_ov2740(sd); + *v4l2_subdev_get_pad_format(sd, sd_state, fmt->pad) = fmt->format; - mutex_lock(&ov2740->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&ov2740->sd, - sd_state, - fmt->pad); - else - ov2740_update_pad_format(ov2740->cur_mode, &fmt->format); + return 0; - mutex_unlock(&ov2740->mutex); + ov2740->cur_mode = mode; + __v4l2_ctrl_s_ctrl(ov2740->link_freq, mode->link_freq_index); + __v4l2_ctrl_s_ctrl_int64(ov2740->pixel_rate, + to_pixel_rate(mode->link_freq_index)); + + /* Update limits and set FPS to default */ + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(ov2740->vblank, + mode->vts_min - mode->height, + OV2740_VTS_MAX - mode->height, 1, vblank_def); + __v4l2_ctrl_s_ctrl(ov2740->vblank, vblank_def); + h_blank = to_pixels_per_line(mode->hts, mode->link_freq_index) - + mode->width; + __v4l2_ctrl_modify_range(ov2740->hblank, h_blank, h_blank, 1, h_blank); return 0; } @@ -946,14 +879,11 @@ static int ov2740_enum_frame_size(struct v4l2_subdev *sd, return 0; } -static int ov2740_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +static int ov2740_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { - struct ov2740 *ov2740 = to_ov2740(sd); - - mutex_lock(&ov2740->mutex); ov2740_update_pad_format(&supported_modes[0], - v4l2_subdev_get_try_format(sd, fh->state, 0)); - mutex_unlock(&ov2740->mutex); + v4l2_subdev_get_pad_format(sd, sd_state, 0)); return 0; } @@ -963,8 +893,8 @@ static const struct v4l2_subdev_video_ops ov2740_video_ops = { }; static const struct v4l2_subdev_pad_ops ov2740_pad_ops = { + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = ov2740_set_format, - .get_fmt = ov2740_get_format, .enum_mbus_code = ov2740_enum_mbus_code, .enum_frame_size = ov2740_enum_frame_size, }; @@ -974,12 +904,12 @@ static const struct v4l2_subdev_ops ov2740_subdev_ops = { .pad = &ov2740_pad_ops, }; -static const struct media_entity_operations ov2740_subdev_entity_ops = { - .link_validate = v4l2_subdev_link_validate, +static const struct v4l2_subdev_internal_ops ov2740_internal_ops = { + .init_state = ov2740_init_state, }; -static const struct v4l2_subdev_internal_ops ov2740_internal_ops = { - .open = ov2740_open, +static const struct media_entity_operations ov2740_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, }; static int ov2740_check_hwcfg(struct device *dev) @@ -1004,7 +934,7 @@ static int ov2740_check_hwcfg(struct device *dev) ep = fwnode_graph_get_next_endpoint(fwnode, NULL); if (!ep) - return -ENXIO; + return -EPROBE_DEFER; ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); fwnode_handle_put(ep); @@ -1047,13 +977,12 @@ static int ov2740_check_hwcfg(struct device *dev) static void ov2740_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct ov2740 *ov2740 = to_ov2740(sd); v4l2_async_unregister_subdev(sd); media_entity_cleanup(&sd->entity); + v4l2_subdev_cleanup(sd); v4l2_ctrl_handler_free(sd->ctrl_handler); pm_runtime_disable(&client->dev); - mutex_destroy(&ov2740->mutex); } static int ov2740_nvmem_read(void *priv, unsigned int off, void *val, @@ -1062,9 +991,11 @@ static int ov2740_nvmem_read(void *priv, unsigned int off, void *val, struct nvm_data *nvm = priv; struct device *dev = regmap_get_device(nvm->regmap); struct ov2740 *ov2740 = to_ov2740(dev_get_drvdata(dev)); + struct v4l2_subdev_state *sd_state; int ret = 0; - mutex_lock(&ov2740->mutex); + /* Serialise sensor access */ + sd_state = v4l2_subdev_lock_and_get_active_state(&ov2740->sd); if (nvm->nvm_buffer) { memcpy(val, nvm->nvm_buffer + off, count); @@ -1082,7 +1013,7 @@ static int ov2740_nvmem_read(void *priv, unsigned int off, void *val, pm_runtime_put(dev); exit: - mutex_unlock(&ov2740->mutex); + v4l2_subdev_unlock_state(sd_state); return ret; } @@ -1146,6 +1077,7 @@ static int ov2740_probe(struct i2c_client *client) return -ENOMEM; v4l2_i2c_subdev_init(&ov2740->sd, client, &ov2740_subdev_ops); + ov2740->sd.internal_ops = &ov2740_internal_ops; full_power = acpi_dev_state_d0(&client->dev); if (full_power) { ret = ov2740_identify_module(ov2740); @@ -1153,7 +1085,6 @@ static int ov2740_probe(struct i2c_client *client) return dev_err_probe(dev, ret, "failed to find sensor\n"); } - mutex_init(&ov2740->mutex); ov2740->cur_mode = &supported_modes[0]; ret = ov2740_init_controls(ov2740); if (ret) { @@ -1161,7 +1092,7 @@ static int ov2740_probe(struct i2c_client *client) goto probe_error_v4l2_ctrl_handler_free; } - ov2740->sd.internal_ops = &ov2740_internal_ops; + ov2740->sd.state_lock = ov2740->ctrl_handler.lock; ov2740->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ov2740->sd.entity.ops = &ov2740_subdev_entity_ops; ov2740->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; @@ -1172,36 +1103,42 @@ static int ov2740_probe(struct i2c_client *client) goto probe_error_v4l2_ctrl_handler_free; } + ret = v4l2_subdev_init_finalize(&ov2740->sd); + if (ret) + goto probe_error_media_entity_cleanup; + + /* Set the device's state to active if it's in D0 state. */ + if (full_power) + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_idle(&client->dev); + ret = v4l2_async_register_subdev_sensor(&ov2740->sd); if (ret < 0) { dev_err_probe(dev, ret, "failed to register V4L2 subdev\n"); - goto probe_error_media_entity_cleanup; + goto probe_error_v4l2_subdev_cleanup; } ret = ov2740_register_nvmem(client, ov2740); if (ret) dev_warn(&client->dev, "register nvmem failed, ret %d\n", ret); - /* Set the device's state to active if it's in D0 state. */ - if (full_power) - pm_runtime_set_active(&client->dev); - pm_runtime_enable(&client->dev); - pm_runtime_idle(&client->dev); - return 0; +probe_error_v4l2_subdev_cleanup: + v4l2_subdev_cleanup(&ov2740->sd); + probe_error_media_entity_cleanup: media_entity_cleanup(&ov2740->sd.entity); + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); probe_error_v4l2_ctrl_handler_free: v4l2_ctrl_handler_free(ov2740->sd.ctrl_handler); - mutex_destroy(&ov2740->mutex); return ret; } -static DEFINE_SIMPLE_DEV_PM_OPS(ov2740_pm_ops, ov2740_suspend, ov2740_resume); - static const struct acpi_device_id ov2740_acpi_ids[] = { {"INT3474"}, {} @@ -1212,7 +1149,6 @@ MODULE_DEVICE_TABLE(acpi, ov2740_acpi_ids); static struct i2c_driver ov2740_i2c_driver = { .driver = { .name = "ov2740", - .pm = pm_sleep_ptr(&ov2740_pm_ops), .acpi_match_table = ov2740_acpi_ids, }, .probe = ov2740_probe, diff --git a/drivers/media/i2c/ov4689.c b/drivers/media/i2c/ov4689.c index fda217d2cb10af..b271ff3e254418 100644 --- a/drivers/media/i2c/ov4689.c +++ b/drivers/media/i2c/ov4689.c @@ -577,7 +577,7 @@ static int ov4689_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) mutex_lock(&ov4689->mutex); - try_fmt = v4l2_subdev_get_try_format(sd, fh->state, 0); + try_fmt = v4l2_subdev_state_get_format(fh->state, 0); /* Initialize try_fmt */ ov4689_fill_fmt(&supported_modes[OV4689_MODE_2688_1520], try_fmt); diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 40532f7bcabea8..6fd98b8cb18124 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -2797,8 +2797,7 @@ static int ov5640_get_fmt(struct v4l2_subdev *sd, mutex_lock(&sensor->lock); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - fmt = v4l2_subdev_get_try_format(&sensor->sd, sd_state, - format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); else fmt = &sensor->fmt; @@ -2971,7 +2970,7 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd, goto out; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, 0) = *mbus_fmt; + *v4l2_subdev_state_get_format(sd_state, 0) = *mbus_fmt; goto out; } @@ -3745,13 +3744,13 @@ static int ov5640_s_stream(struct v4l2_subdev *sd, int enable) return ret; } -static int ov5640_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int ov5640_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct ov5640_dev *sensor = to_ov5640_dev(sd); struct v4l2_mbus_framefmt *fmt = - v4l2_subdev_get_try_format(sd, state, 0); - struct v4l2_rect *crop = v4l2_subdev_get_try_crop(sd, state, 0); + v4l2_subdev_state_get_format(state, 0); + struct v4l2_rect *crop = v4l2_subdev_state_get_crop(state, 0); *fmt = ov5640_is_csi2(sensor) ? ov5640_csi2_default_fmt : ov5640_dvp_default_fmt; @@ -3777,7 +3776,6 @@ static const struct v4l2_subdev_video_ops ov5640_video_ops = { }; static const struct v4l2_subdev_pad_ops ov5640_pad_ops = { - .init_cfg = ov5640_init_cfg, .enum_mbus_code = ov5640_enum_mbus_code, .get_fmt = ov5640_get_fmt, .set_fmt = ov5640_set_fmt, @@ -3792,6 +3790,10 @@ static const struct v4l2_subdev_ops ov5640_subdev_ops = { .pad = &ov5640_pad_ops, }; +static const struct v4l2_subdev_internal_ops ov5640_internal_ops = { + .init_state = ov5640_init_state, +}; + static int ov5640_get_regulators(struct ov5640_dev *sensor) { int i; @@ -3906,6 +3908,7 @@ static int ov5640_probe(struct i2c_client *client) return PTR_ERR(sensor->reset_gpio); v4l2_i2c_subdev_init(&sensor->sd, client, &ov5640_subdev_ops); + sensor->sd.internal_ops = &ov5640_internal_ops; sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; diff --git a/drivers/media/i2c/ov5645.c b/drivers/media/i2c/ov5645.c index a70db7e601a495..a26ac11c989d78 100644 --- a/drivers/media/i2c/ov5645.c +++ b/drivers/media/i2c/ov5645.c @@ -851,7 +851,7 @@ __ov5645_get_pad_format(struct ov5645 *ov5645, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&ov5645->sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &ov5645->fmt; default: @@ -878,7 +878,7 @@ __ov5645_get_pad_crop(struct ov5645 *ov5645, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&ov5645->sd, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &ov5645->crop; default: @@ -934,8 +934,8 @@ static int ov5645_set_format(struct v4l2_subdev *sd, return 0; } -static int ov5645_entity_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state) +static int ov5645_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { 0 }; @@ -1023,7 +1023,6 @@ static const struct v4l2_subdev_video_ops ov5645_video_ops = { }; static const struct v4l2_subdev_pad_ops ov5645_subdev_pad_ops = { - .init_cfg = ov5645_entity_init_cfg, .enum_mbus_code = ov5645_enum_mbus_code, .enum_frame_size = ov5645_enum_frame_size, .get_fmt = ov5645_get_format, @@ -1036,6 +1035,10 @@ static const struct v4l2_subdev_ops ov5645_subdev_ops = { .pad = &ov5645_subdev_pad_ops, }; +static const struct v4l2_subdev_internal_ops ov5645_internal_ops = { + .init_state = ov5645_init_state, +}; + static int ov5645_probe(struct i2c_client *client) { struct device *dev = &client->dev; @@ -1162,6 +1165,7 @@ static int ov5645_probe(struct i2c_client *client) } v4l2_i2c_subdev_init(&ov5645->sd, client, &ov5645_subdev_ops); + ov5645->sd.internal_ops = &ov5645_internal_ops; ov5645->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ov5645->pad.flags = MEDIA_PAD_FL_SOURCE; ov5645->sd.dev = &client->dev; @@ -1220,7 +1224,7 @@ static int ov5645_probe(struct i2c_client *client) pm_runtime_get_noresume(dev); pm_runtime_enable(dev); - ov5645_entity_init_cfg(&ov5645->sd, NULL); + ov5645_init_state(&ov5645->sd, NULL); ret = v4l2_async_register_subdev(&ov5645->sd); if (ret < 0) { diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index f474d22dbded94..164f16da70239b 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -782,7 +782,7 @@ __ov5647_get_pad_crop(struct ov5647 *ov5647, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&ov5647->sd, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &ov5647->mode->crop; } @@ -899,8 +899,8 @@ static int ov5647_get_pad_fmt(struct v4l2_subdev *sd, mutex_lock(&sensor->lock); switch (format->which) { case V4L2_SUBDEV_FORMAT_TRY: - sensor_format = v4l2_subdev_get_try_format(sd, sd_state, - format->pad); + sensor_format = v4l2_subdev_state_get_format(sd_state, + format->pad); break; default: sensor_format = &sensor->mode->format; @@ -930,7 +930,7 @@ static int ov5647_set_pad_fmt(struct v4l2_subdev *sd, /* Update the sensor mode and apply at it at streamon time. */ mutex_lock(&sensor->lock); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, format->pad) = mode->format; + *v4l2_subdev_state_get_format(sd_state, format->pad) = mode->format; } else { int exposure_max, exposure_def; int hblank, vblank; @@ -1052,8 +1052,8 @@ static int ov5647_detect(struct v4l2_subdev *sd) static int ov5647_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *format = - v4l2_subdev_get_try_format(sd, fh->state, 0); - struct v4l2_rect *crop = v4l2_subdev_get_try_crop(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); + struct v4l2_rect *crop = v4l2_subdev_state_get_crop(fh->state, 0); crop->left = OV5647_PIXEL_ARRAY_LEFT; crop->top = OV5647_PIXEL_ARRAY_TOP; diff --git a/drivers/media/i2c/ov5648.c b/drivers/media/i2c/ov5648.c index aa10eb4e3991c5..13e6060d15d4d8 100644 --- a/drivers/media/i2c/ov5648.c +++ b/drivers/media/i2c/ov5648.c @@ -2232,8 +2232,8 @@ static int ov5648_get_fmt(struct v4l2_subdev *subdev, mutex_lock(&sensor->mutex); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *mbus_format = *v4l2_subdev_get_try_format(subdev, sd_state, - format->pad); + *mbus_format = *v4l2_subdev_state_get_format(sd_state, + format->pad); else ov5648_mbus_format_fill(mbus_format, sensor->state.mbus_code, sensor->state.mode); @@ -2285,7 +2285,7 @@ static int ov5648_set_fmt(struct v4l2_subdev *subdev, ov5648_mbus_format_fill(mbus_format, mbus_code, mode); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *v4l2_subdev_get_try_format(subdev, sd_state, format->pad) = + *v4l2_subdev_state_get_format(sd_state, format->pad) = *mbus_format; else if (sensor->state.mode != mode || sensor->state.mbus_code != mbus_code) diff --git a/drivers/media/i2c/ov5670.c b/drivers/media/i2c/ov5670.c index 29e773a997dd4d..57e171ee05d577 100644 --- a/drivers/media/i2c/ov5670.c +++ b/drivers/media/i2c/ov5670.c @@ -2198,13 +2198,13 @@ static int ov5670_init_controls(struct ov5670 *ov5670) return ret; } -static int ov5670_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int ov5670_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct v4l2_mbus_framefmt *fmt = - v4l2_subdev_get_try_format(sd, state, 0); + v4l2_subdev_state_get_format(state, 0); const struct ov5670_mode *default_mode = &supported_modes[0]; - struct v4l2_rect *crop = v4l2_subdev_get_try_crop(sd, state, 0); + struct v4l2_rect *crop = v4l2_subdev_state_get_crop(state, 0); fmt->width = default_mode->width; fmt->height = default_mode->height; @@ -2265,9 +2265,8 @@ static int ov5670_do_get_pad_format(struct ov5670 *ov5670, struct v4l2_subdev_format *fmt) { if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&ov5670->sd, - sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); else ov5670_update_pad_format(ov5670->cur_mode, fmt); @@ -2312,7 +2311,7 @@ static int ov5670_set_pad_format(struct v4l2_subdev *sd, fmt->format.width, fmt->format.height); ov5670_update_pad_format(mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; } else { ov5670->cur_mode = mode; __v4l2_ctrl_s_ctrl(ov5670->link_freq, mode->link_freq_index); @@ -2583,7 +2582,7 @@ __ov5670_get_pad_crop(struct ov5670 *sensor, struct v4l2_subdev_state *state, switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&sensor->sd, state, pad); + return v4l2_subdev_state_get_crop(state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return mode->analog_crop; } @@ -2626,7 +2625,6 @@ static const struct v4l2_subdev_video_ops ov5670_video_ops = { }; static const struct v4l2_subdev_pad_ops ov5670_pad_ops = { - .init_cfg = ov5670_init_cfg, .enum_mbus_code = ov5670_enum_mbus_code, .get_fmt = ov5670_get_pad_format, .set_fmt = ov5670_set_pad_format, @@ -2646,6 +2644,10 @@ static const struct v4l2_subdev_ops ov5670_subdev_ops = { .sensor = &ov5670_sensor_ops, }; +static const struct v4l2_subdev_internal_ops ov5670_internal_ops = { + .init_state = ov5670_init_state, +}; + static const struct media_entity_operations ov5670_subdev_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -2709,6 +2711,7 @@ static int ov5670_probe(struct i2c_client *client) /* Initialize subdev */ v4l2_i2c_subdev_init(&ov5670->sd, client, &ov5670_subdev_ops); + ov5670->sd.internal_ops = &ov5670_internal_ops; ret = ov5670_regulators_probe(ov5670); if (ret) diff --git a/drivers/media/i2c/ov5675.c b/drivers/media/i2c/ov5675.c index c499e7e93c54b9..3590c8de7ef0cf 100644 --- a/drivers/media/i2c/ov5675.c +++ b/drivers/media/i2c/ov5675.c @@ -1079,7 +1079,7 @@ static int ov5675_set_format(struct v4l2_subdev *sd, mutex_lock(&ov5675->mutex); ov5675_update_pad_format(mode, &fmt->format); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; } else { ov5675->cur_mode = mode; __v4l2_ctrl_s_ctrl(ov5675->link_freq, mode->link_freq_index); @@ -1112,9 +1112,8 @@ static int ov5675_get_format(struct v4l2_subdev *sd, mutex_lock(&ov5675->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&ov5675->sd, - sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); else ov5675_update_pad_format(ov5675->cur_mode, &fmt->format); @@ -1184,7 +1183,7 @@ static int ov5675_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) mutex_lock(&ov5675->mutex); ov5675_update_pad_format(&supported_modes[0], - v4l2_subdev_get_try_format(sd, fh->state, 0)); + v4l2_subdev_state_get_format(fh->state, 0)); mutex_unlock(&ov5675->mutex); return 0; diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c index 488ee6d9d30101..3211f21614e84b 100644 --- a/drivers/media/i2c/ov5693.c +++ b/drivers/media/i2c/ov5693.c @@ -776,7 +776,7 @@ __ov5693_get_pad_format(struct ov5693_device *ov5693, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&ov5693->sd, state, pad); + return v4l2_subdev_state_get_format(state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &ov5693->mode.format; default: @@ -791,7 +791,7 @@ __ov5693_get_pad_crop(struct ov5693_device *ov5693, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&ov5693->sd, state, pad); + return v4l2_subdev_state_get_crop(state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &ov5693->mode.crop; } diff --git a/drivers/media/i2c/ov5695.c b/drivers/media/i2c/ov5695.c index 3023b72541677d..c2f0063206392a 100644 --- a/drivers/media/i2c/ov5695.c +++ b/drivers/media/i2c/ov5695.c @@ -821,9 +821,7 @@ static int ov5695_set_fmt(struct v4l2_subdev *sd, fmt->format.height = mode->height; fmt->format.field = V4L2_FIELD_NONE; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; -#endif + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; } else { ov5695->cur_mode = mode; h_blank = mode->hts_def - mode->width; @@ -849,13 +847,8 @@ static int ov5695_get_fmt(struct v4l2_subdev *sd, mutex_lock(&ov5695->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - fmt->format = *v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); -#else - mutex_unlock(&ov5695->mutex); - return -EINVAL; -#endif + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); } else { fmt->format.width = mode->width; fmt->format.height = mode->height; @@ -1048,12 +1041,11 @@ static int __maybe_unused ov5695_runtime_suspend(struct device *dev) return 0; } -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API static int ov5695_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct ov5695 *ov5695 = to_ov5695(sd); struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); const struct ov5695_mode *def_mode = &supported_modes[0]; mutex_lock(&ov5695->mutex); @@ -1068,18 +1060,15 @@ static int ov5695_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) return 0; } -#endif static const struct dev_pm_ops ov5695_pm_ops = { SET_RUNTIME_PM_OPS(ov5695_runtime_suspend, ov5695_runtime_resume, NULL) }; -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API static const struct v4l2_subdev_internal_ops ov5695_internal_ops = { .open = ov5695_open, }; -#endif static const struct v4l2_subdev_video_ops ov5695_video_ops = { .s_stream = ov5695_s_stream, @@ -1322,17 +1311,13 @@ static int ov5695_probe(struct i2c_client *client) if (ret) goto err_power_off; -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API sd->internal_ops = &ov5695_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; -#endif -#if defined(CONFIG_MEDIA_CONTROLLER) ov5695->pad.flags = MEDIA_PAD_FL_SOURCE; sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; ret = media_entity_pads_init(&sd->entity, 1, &ov5695->pad); if (ret < 0) goto err_power_off; -#endif ret = v4l2_async_register_subdev_sensor(sd); if (ret) { @@ -1347,9 +1332,7 @@ static int ov5695_probe(struct i2c_client *client) return 0; err_clean_entity: -#if defined(CONFIG_MEDIA_CONTROLLER) media_entity_cleanup(&sd->entity); -#endif err_power_off: __ov5695_power_off(ov5695); err_free_handler: @@ -1366,9 +1349,7 @@ static void ov5695_remove(struct i2c_client *client) struct ov5695 *ov5695 = to_ov5695(sd); v4l2_async_unregister_subdev(sd); -#if defined(CONFIG_MEDIA_CONTROLLER) media_entity_cleanup(&sd->entity); -#endif v4l2_ctrl_handler_free(&ov5695->ctrl_handler); mutex_destroy(&ov5695->mutex); diff --git a/drivers/media/i2c/ov64a40.c b/drivers/media/i2c/ov64a40.c index ee75bbf8d8bfd6..d48a9ab3408843 100644 --- a/drivers/media/i2c/ov64a40.c +++ b/drivers/media/i2c/ov64a40.c @@ -3052,17 +3052,17 @@ static void ov64a40_update_pad_fmt(struct ov64a40 *ov64a40, fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; } -static int ov64a40_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int ov64a40_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct ov64a40 *ov64a40 = sd_to_ov64a40(sd); struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - format = v4l2_subdev_get_pad_format(sd, state, 0); + format = v4l2_subdev_state_get_format(state, 0); ov64a40_update_pad_fmt(ov64a40, &ov64a40_modes[0], format); - crop = v4l2_subdev_get_pad_crop(sd, state, 0); + crop = v4l2_subdev_state_get_crop(state, 0); crop->top = OV64A40_PIXEL_ARRAY_TOP; crop->left = OV64A40_PIXEL_ARRAY_LEFT; crop->width = OV64A40_PIXEL_ARRAY_WIDTH; @@ -3115,7 +3115,7 @@ static int ov64a40_get_selection(struct v4l2_subdev *sd, { switch (sel->target) { case V4L2_SEL_TGT_CROP: - sel->r = *v4l2_subdev_get_pad_crop(sd, state, 0); + sel->r = *v4l2_subdev_state_get_crop(state, 0); return 0; @@ -3155,7 +3155,7 @@ static int ov64a40_set_format(struct v4l2_subdev *sd, ov64a40_update_pad_fmt(ov64a40, mode, &fmt->format); - format = v4l2_subdev_get_pad_format(sd, state, 0); + format = v4l2_subdev_state_get_format(state, 0); if (ov64a40->mode == mode && format->code == fmt->format.code) return 0; @@ -3166,7 +3166,7 @@ static int ov64a40_set_format(struct v4l2_subdev *sd, int exp_max; ov64a40->mode = mode; - *v4l2_subdev_get_pad_crop(sd, state, 0) = mode->analogue_crop; + *v4l2_subdev_state_get_crop(state, 0) = mode->analogue_crop; /* Update control limits according to the new mode. */ timings = ov64a40_get_timings(ov64a40, @@ -3193,7 +3193,6 @@ static int ov64a40_set_format(struct v4l2_subdev *sd, } static const struct v4l2_subdev_pad_ops ov64a40_pad_ops = { - .init_cfg = ov64a40_init_cfg, .enum_mbus_code = ov64a40_enum_mbus_code, .enum_frame_size = ov64a40_enum_frame_size, .get_fmt = v4l2_subdev_get_fmt, @@ -3212,6 +3211,10 @@ static const struct v4l2_subdev_ops ov64a40_subdev_ops = { .pad = &ov64a40_pad_ops, }; +static const struct v4l2_subdev_internal_ops ov64a40_internal_ops = { + .init_state = ov64a40_init_state, +}; + static int ov64a40_power_on(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); @@ -3547,6 +3550,7 @@ static int ov64a40_probe(struct i2c_client *client) ov64a40->dev = &client->dev; v4l2_i2c_subdev_init(&ov64a40->sd, client, &ov64a40_subdev_ops); + ov64a40->sd.internal_ops = &ov64a40_internal_ops; ov64a40->cci = devm_cci_regmap_init_i2c(client, 16); if (IS_ERR(ov64a40->cci)) { diff --git a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c index 1ad07935f0465a..bf1e7617ee08b8 100644 --- a/drivers/media/i2c/ov6650.c +++ b/drivers/media/i2c/ov6650.c @@ -476,7 +476,7 @@ static int ov6650_get_selection(struct v4l2_subdev *sd, if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { /* pre-select try crop rectangle */ - rect = &sd_state->pads->try_crop; + rect = v4l2_subdev_state_get_crop(sd_state, 0); } else { /* pre-select active crop rectangle */ @@ -531,8 +531,10 @@ static int ov6650_set_selection(struct v4l2_subdev *sd, ov6650_bind_align_crop_rectangle(&sel->r); if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - struct v4l2_rect *crop = &sd_state->pads->try_crop; - struct v4l2_mbus_framefmt *mf = &sd_state->pads->try_fmt; + struct v4l2_rect *crop = + v4l2_subdev_state_get_crop(sd_state, 0); + struct v4l2_mbus_framefmt *mf = + v4l2_subdev_state_get_format(sd_state, 0); /* detect current pad config scaling factor */ bool half_scale = !is_unscaled_ok(mf->width, mf->height, crop); @@ -588,9 +590,12 @@ static int ov6650_get_fmt(struct v4l2_subdev *sd, /* update media bus format code and frame size */ if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - mf->width = sd_state->pads->try_fmt.width; - mf->height = sd_state->pads->try_fmt.height; - mf->code = sd_state->pads->try_fmt.code; + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_state_get_format(sd_state, 0); + + mf->width = try_fmt->width; + mf->height = try_fmt->height; + mf->code = try_fmt->code; } else { mf->width = priv->rect.width >> priv->half_scale; @@ -717,23 +722,26 @@ static int ov6650_set_fmt(struct v4l2_subdev *sd, } if (format->which == V4L2_SUBDEV_FORMAT_TRY) - crop = &sd_state->pads->try_crop; + crop = v4l2_subdev_state_get_crop(sd_state, 0); else crop = &priv->rect; half_scale = !is_unscaled_ok(mf->width, mf->height, crop); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_state_get_format(sd_state, 0); + /* store new mbus frame format code and size in pad config */ - sd_state->pads->try_fmt.width = crop->width >> half_scale; - sd_state->pads->try_fmt.height = crop->height >> half_scale; - sd_state->pads->try_fmt.code = mf->code; + try_fmt->width = crop->width >> half_scale; + try_fmt->height = crop->height >> half_scale; + try_fmt->code = mf->code; /* return default mbus frame format updated with pad config */ *mf = ov6650_def_fmt; - mf->width = sd_state->pads->try_fmt.width; - mf->height = sd_state->pads->try_fmt.height; - mf->code = sd_state->pads->try_fmt.code; + mf->width = try_fmt->width; + mf->height = try_fmt->height; + mf->code = try_fmt->code; } else { int ret = 0; diff --git a/drivers/media/i2c/ov7251.c b/drivers/media/i2c/ov7251.c index fc26f2b6a60492..ed5e458918fcd0 100644 --- a/drivers/media/i2c/ov7251.c +++ b/drivers/media/i2c/ov7251.c @@ -1151,7 +1151,7 @@ __ov7251_get_pad_format(struct ov7251 *ov7251, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&ov7251->sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &ov7251->fmt; default: @@ -1181,7 +1181,7 @@ __ov7251_get_pad_crop(struct ov7251 *ov7251, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&ov7251->sd, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &ov7251->crop; default: @@ -1294,8 +1294,8 @@ static int ov7251_set_format(struct v4l2_subdev *sd, return ret; } -static int ov7251_entity_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state) +static int ov7251_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY @@ -1476,7 +1476,6 @@ static const struct v4l2_subdev_video_ops ov7251_video_ops = { }; static const struct v4l2_subdev_pad_ops ov7251_subdev_pad_ops = { - .init_cfg = ov7251_entity_init_cfg, .enum_mbus_code = ov7251_enum_mbus_code, .enum_frame_size = ov7251_enum_frame_size, .enum_frame_interval = ov7251_enum_frame_ival, @@ -1490,6 +1489,10 @@ static const struct v4l2_subdev_ops ov7251_subdev_ops = { .pad = &ov7251_subdev_pad_ops, }; +static const struct v4l2_subdev_internal_ops ov7251_internal_ops = { + .init_state = ov7251_init_state, +}; + static int ov7251_check_hwcfg(struct ov7251 *ov7251) { struct fwnode_handle *fwnode = dev_fwnode(ov7251->dev); @@ -1739,6 +1742,7 @@ static int ov7251_probe(struct i2c_client *client) goto free_ctrl; v4l2_i2c_subdev_init(&ov7251->sd, client, &ov7251_subdev_ops); + ov7251->sd.internal_ops = &ov7251_internal_ops; ov7251->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ov7251->pad.flags = MEDIA_PAD_FL_SOURCE; ov7251->sd.dev = &client->dev; @@ -1796,7 +1800,7 @@ static int ov7251_probe(struct i2c_client *client) goto free_entity; } - ov7251_entity_init_cfg(&ov7251->sd, NULL); + ov7251_init_state(&ov7251->sd, NULL); return 0; diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 2f55491ef571f0..1557ebbb7f9c0f 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -217,9 +217,7 @@ struct ov7670_devtype { struct ov7670_format_struct; /* coming later */ struct ov7670_info { struct v4l2_subdev sd; -#if defined(CONFIG_MEDIA_CONTROLLER) struct media_pad pad; -#endif struct v4l2_ctrl_handler hdl; struct { /* gain cluster */ @@ -1108,9 +1106,7 @@ static int ov7670_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_format *format) { struct ov7670_info *info = to_state(sd); -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API struct v4l2_mbus_framefmt *mbus_fmt; -#endif int ret; if (format->pad) @@ -1120,11 +1116,8 @@ static int ov7670_set_fmt(struct v4l2_subdev *sd, ret = ov7670_try_fmt_internal(sd, &format->format, NULL, NULL); if (ret) return ret; -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - mbus_fmt = v4l2_subdev_get_try_format(sd, sd_state, - format->pad); + mbus_fmt = v4l2_subdev_state_get_format(sd_state, format->pad); *mbus_fmt = format->format; -#endif return 0; } @@ -1148,18 +1141,12 @@ static int ov7670_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_format *format) { struct ov7670_info *info = to_state(sd); -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API struct v4l2_mbus_framefmt *mbus_fmt; -#endif if (format->which == V4L2_SUBDEV_FORMAT_TRY) { -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - mbus_fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); + mbus_fmt = v4l2_subdev_state_get_format(sd_state, 0); format->format = *mbus_fmt; return 0; -#else - return -EINVAL; -#endif } else { format->format = info->format; } @@ -1720,17 +1707,15 @@ static void ov7670_get_default_format(struct v4l2_subdev *sd, format->field = V4L2_FIELD_NONE; } -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API static int ov7670_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *format = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); ov7670_get_default_format(sd, format); return 0; } -#endif /* ----------------------------------------------------------------------- */ @@ -1766,11 +1751,9 @@ static const struct v4l2_subdev_ops ov7670_ops = { .pad = &ov7670_pad_ops, }; -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API static const struct v4l2_subdev_internal_ops ov7670_subdev_internal_ops = { .open = ov7670_open, }; -#endif /* ----------------------------------------------------------------------- */ @@ -1861,10 +1844,8 @@ static int ov7670_probe(struct i2c_client *client) sd = &info->sd; v4l2_i2c_subdev_init(sd, client, &ov7670_ops); -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API sd->internal_ops = &ov7670_subdev_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; -#endif info->clock_speed = 30; /* default: a guess */ @@ -1977,13 +1958,11 @@ static int ov7670_probe(struct i2c_client *client) V4L2_EXPOSURE_MANUAL, false); v4l2_ctrl_cluster(2, &info->saturation); -#if defined(CONFIG_MEDIA_CONTROLLER) info->pad.flags = MEDIA_PAD_FL_SOURCE; info->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; ret = media_entity_pads_init(&info->sd.entity, 1, &info->pad); if (ret < 0) goto hdl_free; -#endif v4l2_ctrl_handler_setup(&info->hdl); diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c index 386d69c8e07465..e397f7531e1dd0 100644 --- a/drivers/media/i2c/ov772x.c +++ b/drivers/media/i2c/ov772x.c @@ -433,9 +433,7 @@ struct ov772x_priv { struct mutex lock; int power_count; int streaming; -#ifdef CONFIG_MEDIA_CONTROLLER struct media_pad pad; -#endif enum v4l2_mbus_type bus_type; }; @@ -1222,7 +1220,7 @@ static int ov772x_set_fmt(struct v4l2_subdev *sd, mf->xfer_func = V4L2_XFER_FUNC_DEFAULT; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - sd_state->pads->try_fmt = *mf; + *v4l2_subdev_state_get_format(sd_state, 0) = *mf; return 0; } @@ -1488,13 +1486,11 @@ static int ov772x_probe(struct i2c_client *client) if (ret < 0) goto error_gpio_put; -#ifdef CONFIG_MEDIA_CONTROLLER priv->pad.flags = MEDIA_PAD_FL_SOURCE; priv->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; ret = media_entity_pads_init(&priv->subdev.entity, 1, &priv->pad); if (ret < 0) goto error_gpio_put; -#endif priv->cfmt = &ov772x_cfmts[0]; priv->win = &ov772x_win_sizes[0]; diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c index dffdb475e43391..6c642693ca307f 100644 --- a/drivers/media/i2c/ov7740.c +++ b/drivers/media/i2c/ov7740.c @@ -83,9 +83,7 @@ struct ov7740 { struct v4l2_subdev subdev; -#if defined(CONFIG_MEDIA_CONTROLLER) struct media_pad pad; -#endif struct v4l2_mbus_framefmt format; const struct ov7740_pixfmt *fmt; /* Current format */ const struct ov7740_framesize *frmsize; @@ -807,9 +805,7 @@ static int ov7740_set_fmt(struct v4l2_subdev *sd, struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); const struct ov7740_pixfmt *ovfmt; const struct ov7740_framesize *fsize; -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API struct v4l2_mbus_framefmt *mbus_fmt; -#endif int ret; mutex_lock(&ov7740->mutex); @@ -822,11 +818,9 @@ static int ov7740_set_fmt(struct v4l2_subdev *sd, ret = ov7740_try_fmt_internal(sd, &format->format, NULL, NULL); if (ret) goto error; -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - mbus_fmt = v4l2_subdev_get_try_format(sd, sd_state, - format->pad); + + mbus_fmt = v4l2_subdev_state_get_format(sd_state, format->pad); *mbus_fmt = format->format; -#endif mutex_unlock(&ov7740->mutex); return 0; } @@ -851,26 +845,18 @@ static int ov7740_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_format *format) { struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API struct v4l2_mbus_framefmt *mbus_fmt; -#endif - int ret = 0; mutex_lock(&ov7740->mutex); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - mbus_fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); + mbus_fmt = v4l2_subdev_state_get_format(sd_state, 0); format->format = *mbus_fmt; - ret = 0; -#else - ret = -EINVAL; -#endif } else { format->format = ov7740->format; } mutex_unlock(&ov7740->mutex); - return ret; + return 0; } static const struct v4l2_subdev_pad_ops ov7740_subdev_pad_ops = { @@ -899,12 +885,11 @@ static void ov7740_get_default_format(struct v4l2_subdev *sd, format->field = V4L2_FIELD_NONE; } -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API static int ov7740_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); struct v4l2_mbus_framefmt *format = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); mutex_lock(&ov7740->mutex); ov7740_get_default_format(sd, format); @@ -916,7 +901,6 @@ static int ov7740_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) static const struct v4l2_subdev_internal_ops ov7740_subdev_internal_ops = { .open = ov7740_open, }; -#endif static int ov7740_probe_dt(struct i2c_client *client, struct ov7740 *ov7740) @@ -1094,18 +1078,14 @@ static int ov7740_probe(struct i2c_client *client) sd = &ov7740->subdev; v4l2_i2c_subdev_init(sd, client, &ov7740_subdev_ops); -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API sd->internal_ops = &ov7740_subdev_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; -#endif -#if defined(CONFIG_MEDIA_CONTROLLER) ov7740->pad.flags = MEDIA_PAD_FL_SOURCE; sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; ret = media_entity_pads_init(&sd->entity, 1, &ov7740->pad); if (ret) return ret; -#endif ret = ov7740_set_power(ov7740, 1); if (ret) diff --git a/drivers/media/i2c/ov8856.c b/drivers/media/i2c/ov8856.c index f053c3a7676a09..50a3c575bf78e6 100644 --- a/drivers/media/i2c/ov8856.c +++ b/drivers/media/i2c/ov8856.c @@ -2180,7 +2180,7 @@ static int ov8856_set_format(struct v4l2_subdev *sd, mutex_lock(&ov8856->mutex); ov8856_update_pad_format(ov8856, mode, &fmt->format); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; } else { ov8856->cur_mode = mode; __v4l2_ctrl_s_ctrl(ov8856->link_freq, mode->link_freq_index); @@ -2218,9 +2218,8 @@ static int ov8856_get_format(struct v4l2_subdev *sd, mutex_lock(&ov8856->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&ov8856->sd, - sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); else ov8856_update_pad_format(ov8856, ov8856->cur_mode, &fmt->format); @@ -2271,7 +2270,7 @@ static int ov8856_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) mutex_lock(&ov8856->mutex); ov8856_update_pad_format(ov8856, &ov8856->priv_lane->supported_modes[0], - v4l2_subdev_get_try_format(sd, fh->state, 0)); + v4l2_subdev_state_get_format(fh->state, 0)); mutex_unlock(&ov8856->mutex); return 0; diff --git a/drivers/media/i2c/ov8858.c b/drivers/media/i2c/ov8858.c index 4d9fd76e2f60f3..174c65f768860e 100644 --- a/drivers/media/i2c/ov8858.c +++ b/drivers/media/i2c/ov8858.c @@ -1333,7 +1333,7 @@ static int ov8858_start_stream(struct ov8858 *ov8858, if (ret) return ret; - format = v4l2_subdev_get_pad_format(&ov8858->subdev, state, 0); + format = v4l2_subdev_state_get_format(state, 0); mode = v4l2_find_nearest_size(ov8858_modes, ARRAY_SIZE(ov8858_modes), width, height, format->width, format->height); @@ -1428,7 +1428,7 @@ static int ov8858_set_fmt(struct v4l2_subdev *sd, fmt->format.field = V4L2_FIELD_NONE; /* Store the format in the current subdev state. */ - *v4l2_subdev_get_pad_format(sd, state, 0) = fmt->format; + *v4l2_subdev_state_get_format(state, 0) = fmt->format; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) return 0; @@ -1476,8 +1476,8 @@ static int ov8858_enum_mbus_code(struct v4l2_subdev *sd, return 0; } -static int ov8858_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int ov8858_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { const struct ov8858_mode *def_mode = &ov8858_modes[0]; struct v4l2_subdev_format fmt = { @@ -1494,7 +1494,6 @@ static int ov8858_init_cfg(struct v4l2_subdev *sd, } static const struct v4l2_subdev_pad_ops ov8858_pad_ops = { - .init_cfg = ov8858_init_cfg, .enum_mbus_code = ov8858_enum_mbus_code, .enum_frame_size = ov8858_enum_frame_sizes, .get_fmt = v4l2_subdev_get_fmt, @@ -1512,6 +1511,10 @@ static const struct v4l2_subdev_ops ov8858_subdev_ops = { .pad = &ov8858_pad_ops, }; +static const struct v4l2_subdev_internal_ops ov8858_internal_ops = { + .init_state = ov8858_init_state, +}; + /* ---------------------------------------------------------------------------- * Controls handling */ @@ -1547,7 +1550,7 @@ static int ov8858_set_ctrl(struct v4l2_ctrl *ctrl) * - by the driver when s_ctrl is called in the s_stream(1) call path */ state = v4l2_subdev_get_locked_active_state(&ov8858->subdev); - format = v4l2_subdev_get_pad_format(&ov8858->subdev, state, 0); + format = v4l2_subdev_state_get_format(state, 0); /* Propagate change of current control to all related controls */ switch (ctrl->id) { @@ -1899,6 +1902,7 @@ static int ov8858_probe(struct i2c_client *client) "Failed to get powerdown gpio\n"); v4l2_i2c_subdev_init(&ov8858->subdev, client, &ov8858_subdev_ops); + ov8858->subdev.internal_ops = &ov8858_internal_ops; ret = ov8858_configure_regulators(ov8858); if (ret) diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c index f2213c6158d3e4..fb19ab0c2a9d7b 100644 --- a/drivers/media/i2c/ov8865.c +++ b/drivers/media/i2c/ov8865.c @@ -2710,8 +2710,8 @@ static int ov8865_get_fmt(struct v4l2_subdev *subdev, mutex_lock(&sensor->mutex); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *mbus_format = *v4l2_subdev_get_try_format(subdev, sd_state, - format->pad); + *mbus_format = *v4l2_subdev_state_get_format(sd_state, + format->pad); else ov8865_mbus_format_fill(mbus_format, sensor->state.mbus_code, sensor->state.mode); @@ -2765,7 +2765,7 @@ static int ov8865_set_fmt(struct v4l2_subdev *subdev, ov8865_mbus_format_fill(mbus_format, mbus_code, mode); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *v4l2_subdev_get_try_format(subdev, sd_state, format->pad) = + *v4l2_subdev_state_get_format(sd_state, format->pad) = *mbus_format; else if (sensor->state.mode != mode || sensor->state.mbus_code != mbus_code) @@ -2818,7 +2818,7 @@ __ov8865_get_pad_crop(struct ov8865_sensor *sensor, switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - *r = *v4l2_subdev_get_try_crop(&sensor->subdev, state, pad); + *r = *v4l2_subdev_state_get_crop(state, pad); break; case V4L2_SUBDEV_FORMAT_ACTIVE: r->height = mode->output_size_y; diff --git a/drivers/media/i2c/ov9282.c b/drivers/media/i2c/ov9282.c index 0b092a425cfc8c..c4cc33c5354b87 100644 --- a/drivers/media/i2c/ov9282.c +++ b/drivers/media/i2c/ov9282.c @@ -165,7 +165,6 @@ struct ov9282_mode { * @cur_mode: Pointer to current selected sensor mode * @code: Mbus code currently selected * @mutex: Mutex for serializing sensor controls - * @streaming: Flag indicating streaming state */ struct ov9282 { struct device *dev; @@ -188,7 +187,6 @@ struct ov9282 { const struct ov9282_mode *cur_mode; u32 code; struct mutex mutex; - bool streaming; }; static const s64 link_freq[] = { @@ -816,7 +814,7 @@ static int ov9282_get_pad_format(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *framefmt; - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *framefmt; } else { ov9282_fill_pad_format(ov9282, ov9282->cur_mode, ov9282->code, @@ -862,7 +860,7 @@ static int ov9282_set_pad_format(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *framefmt; - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); *framefmt = fmt->format; } else { ret = ov9282_update_controls(ov9282, mode, fmt); @@ -878,14 +876,14 @@ static int ov9282_set_pad_format(struct v4l2_subdev *sd, } /** - * ov9282_init_pad_cfg() - Initialize sub-device pad configuration + * ov9282_init_state() - Initialize sub-device state * @sd: pointer to ov9282 V4L2 sub-device structure * @sd_state: V4L2 sub-device configuration * * Return: 0 if successful, error code otherwise. */ -static int ov9282_init_pad_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int ov9282_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct ov9282 *ov9282 = to_ov9282(sd); struct v4l2_subdev_format fmt = { 0 }; @@ -904,7 +902,7 @@ __ov9282_get_pad_crop(struct ov9282 *ov9282, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&ov9282->sd, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &ov9282->cur_mode->crop; } @@ -1037,11 +1035,6 @@ static int ov9282_set_stream(struct v4l2_subdev *sd, int enable) mutex_lock(&ov9282->mutex); - if (ov9282->streaming == enable) { - mutex_unlock(&ov9282->mutex); - return 0; - } - if (enable) { ret = pm_runtime_resume_and_get(ov9282->dev); if (ret) @@ -1055,8 +1048,6 @@ static int ov9282_set_stream(struct v4l2_subdev *sd, int enable) pm_runtime_put(ov9282->dev); } - ov9282->streaming = enable; - mutex_unlock(&ov9282->mutex); return 0; @@ -1205,7 +1196,6 @@ static const struct v4l2_subdev_video_ops ov9282_video_ops = { }; static const struct v4l2_subdev_pad_ops ov9282_pad_ops = { - .init_cfg = ov9282_init_pad_cfg, .enum_mbus_code = ov9282_enum_mbus_code, .enum_frame_size = ov9282_enum_frame_size, .get_fmt = ov9282_get_pad_format, @@ -1219,6 +1209,10 @@ static const struct v4l2_subdev_ops ov9282_subdev_ops = { .pad = &ov9282_pad_ops, }; +static const struct v4l2_subdev_internal_ops ov9282_internal_ops = { + .init_state = ov9282_init_state, +}; + /** * ov9282_power_on() - Sensor power on sequence * @dev: pointer to i2c device @@ -1407,6 +1401,7 @@ static int ov9282_probe(struct i2c_client *client) /* Initialize subdev */ v4l2_i2c_subdev_init(&ov9282->sd, client, &ov9282_subdev_ops); + ov9282->sd.internal_ops = &ov9282_internal_ops; v4l2_i2c_subdev_set_name(&ov9282->sd, client, device_get_match_data(ov9282->dev), NULL); diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c index cbaea049531d24..e9a52a8a9dc0af 100644 --- a/drivers/media/i2c/ov9640.c +++ b/drivers/media/i2c/ov9640.c @@ -547,8 +547,6 @@ static int ov9640_set_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) return ov9640_s_fmt(sd, mf); - sd_state->pads->try_fmt = *mf; - return 0; } diff --git a/drivers/media/i2c/ov9650.c b/drivers/media/i2c/ov9650.c index da1ab5135eaa1c..753f6222102a3a 100644 --- a/drivers/media/i2c/ov9650.c +++ b/drivers/media/i2c/ov9650.c @@ -1172,7 +1172,7 @@ static int ov965x_get_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, 0); + mf = v4l2_subdev_state_get_format(sd_state, 0); fmt->format = *mf; return 0; } @@ -1233,8 +1233,7 @@ static int ov965x_set_fmt(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { if (sd_state) { - mf = v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); *mf = fmt->format; } } else { @@ -1363,7 +1362,7 @@ static int ov965x_s_stream(struct v4l2_subdev *sd, int on) static int ov965x_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *mf = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); ov965x_get_default_format(mf); return 0; diff --git a/drivers/media/i2c/ov9734.c b/drivers/media/i2c/ov9734.c index b36fc0fedad481..a3ba0f06f017a9 100644 --- a/drivers/media/i2c/ov9734.c +++ b/drivers/media/i2c/ov9734.c @@ -742,7 +742,7 @@ static int ov9734_set_format(struct v4l2_subdev *sd, mutex_lock(&ov9734->mutex); ov9734_update_pad_format(mode, &fmt->format); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; } else { ov9734->cur_mode = mode; __v4l2_ctrl_s_ctrl(ov9734->link_freq, mode->link_freq_index); @@ -775,9 +775,8 @@ static int ov9734_get_format(struct v4l2_subdev *sd, mutex_lock(&ov9734->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&ov9734->sd, - sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); else ov9734_update_pad_format(ov9734->cur_mode, &fmt->format); @@ -822,7 +821,7 @@ static int ov9734_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) mutex_lock(&ov9734->mutex); ov9734_update_pad_format(&supported_modes[0], - v4l2_subdev_get_try_format(sd, fh->state, 0)); + v4l2_subdev_state_get_format(fh->state, 0)); mutex_unlock(&ov9734->mutex); return 0; diff --git a/drivers/media/i2c/rj54n1cb0c.c b/drivers/media/i2c/rj54n1cb0c.c index b430046f9e2a9f..a59db10153cdcb 100644 --- a/drivers/media/i2c/rj54n1cb0c.c +++ b/drivers/media/i2c/rj54n1cb0c.c @@ -1008,10 +1008,8 @@ static int rj54n1_set_fmt(struct v4l2_subdev *sd, v4l_bound_align_image(&mf->width, 112, RJ54N1_MAX_WIDTH, align, &mf->height, 84, RJ54N1_MAX_HEIGHT, align, 0); - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - sd_state->pads->try_fmt = *mf; + if (format->which == V4L2_SUBDEV_FORMAT_TRY) return 0; - } /* * Verify if the sensor has just been powered on. TODO: replace this diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c index ed5b10731a14fd..8f9b5713daf7a4 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -819,7 +819,6 @@ static void s5c73m3_oif_try_format(struct s5c73m3 *state, struct v4l2_subdev_format *fmt, const struct s5c73m3_frame_size **fs) { - struct v4l2_subdev *sd = &state->sensor_sd; u32 code; switch (fmt->pad) { @@ -841,10 +840,8 @@ static void s5c73m3_oif_try_format(struct s5c73m3 *state, if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) *fs = state->oif_pix_size[RES_ISP]; else - *fs = s5c73m3_find_frame_size( - v4l2_subdev_get_try_format(sd, sd_state, - OIF_ISP_PAD), - RES_ISP); + *fs = s5c73m3_find_frame_size(v4l2_subdev_state_get_format(sd_state, OIF_ISP_PAD), + RES_ISP); break; } @@ -990,8 +987,8 @@ static int s5c73m3_get_fmt(struct v4l2_subdev *sd, u32 code; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - fmt->format = *v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); return 0; } @@ -1025,8 +1022,8 @@ static int s5c73m3_oif_get_fmt(struct v4l2_subdev *sd, u32 code; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - fmt->format = *v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); return 0; } @@ -1069,7 +1066,7 @@ static int s5c73m3_set_fmt(struct v4l2_subdev *sd, s5c73m3_try_format(state, sd_state, fmt, &frame_size); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); *mf = fmt->format; } else { switch (fmt->pad) { @@ -1108,11 +1105,11 @@ static int s5c73m3_oif_set_fmt(struct v4l2_subdev *sd, s5c73m3_oif_try_format(state, sd_state, fmt, &frame_size); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); *mf = fmt->format; if (fmt->pad == OIF_ISP_PAD) { - mf = v4l2_subdev_get_try_format(sd, sd_state, - OIF_SOURCE_PAD); + mf = v4l2_subdev_state_get_format(sd_state, + OIF_SOURCE_PAD); mf->width = fmt->format.width; mf->height = fmt->format.height; } @@ -1260,8 +1257,8 @@ static int s5c73m3_oif_enum_frame_size(struct v4l2_subdev *sd, if (fse->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_get_try_format(sd, sd_state, - OIF_ISP_PAD); + mf = v4l2_subdev_state_get_format(sd_state, + OIF_ISP_PAD); w = mf->width; h = mf->height; @@ -1316,11 +1313,11 @@ static int s5c73m3_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_get_try_format(sd, fh->state, S5C73M3_ISP_PAD); + mf = v4l2_subdev_state_get_format(fh->state, S5C73M3_ISP_PAD); s5c73m3_fill_mbus_fmt(mf, &s5c73m3_isp_resolutions[1], S5C73M3_ISP_FMT); - mf = v4l2_subdev_get_try_format(sd, fh->state, S5C73M3_JPEG_PAD); + mf = v4l2_subdev_state_get_format(fh->state, S5C73M3_JPEG_PAD); s5c73m3_fill_mbus_fmt(mf, &s5c73m3_jpeg_resolutions[1], S5C73M3_JPEG_FMT); @@ -1331,15 +1328,15 @@ static int s5c73m3_oif_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_get_try_format(sd, fh->state, OIF_ISP_PAD); + mf = v4l2_subdev_state_get_format(fh->state, OIF_ISP_PAD); s5c73m3_fill_mbus_fmt(mf, &s5c73m3_isp_resolutions[1], S5C73M3_ISP_FMT); - mf = v4l2_subdev_get_try_format(sd, fh->state, OIF_JPEG_PAD); + mf = v4l2_subdev_state_get_format(fh->state, OIF_JPEG_PAD); s5c73m3_fill_mbus_fmt(mf, &s5c73m3_jpeg_resolutions[1], S5C73M3_JPEG_FMT); - mf = v4l2_subdev_get_try_format(sd, fh->state, OIF_SOURCE_PAD); + mf = v4l2_subdev_state_get_format(fh->state, OIF_SOURCE_PAD); s5c73m3_fill_mbus_fmt(mf, &s5c73m3_isp_resolutions[1], S5C73M3_ISP_FMT); return 0; diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c index 67da2045f5431f..03ccfb0e1e11db 100644 --- a/drivers/media/i2c/s5k5baf.c +++ b/drivers/media/i2c/s5k5baf.c @@ -1273,7 +1273,7 @@ static int s5k5baf_get_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *mf; return 0; } @@ -1307,7 +1307,7 @@ static int s5k5baf_set_fmt(struct v4l2_subdev *sd, mf->field = V4L2_FIELD_NONE; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = *mf; + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = *mf; return 0; } @@ -1379,11 +1379,11 @@ static int s5k5baf_get_selection(struct v4l2_subdev *sd, if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { if (rtype == R_COMPOSE) - sel->r = *v4l2_subdev_get_try_compose(sd, sd_state, - sel->pad); + sel->r = *v4l2_subdev_state_get_compose(sd_state, + sel->pad); else - sel->r = *v4l2_subdev_get_try_crop(sd, sd_state, - sel->pad); + sel->r = *v4l2_subdev_state_get_crop(sd_state, + sel->pad); return 0; } @@ -1472,14 +1472,11 @@ static int s5k5baf_set_selection(struct v4l2_subdev *sd, if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { rects = (struct v4l2_rect * []) { - &s5k5baf_cis_rect, - v4l2_subdev_get_try_crop(sd, sd_state, - PAD_CIS), - v4l2_subdev_get_try_compose(sd, sd_state, - PAD_CIS), - v4l2_subdev_get_try_crop(sd, sd_state, - PAD_OUT) - }; + &s5k5baf_cis_rect, + v4l2_subdev_state_get_crop(sd_state, PAD_CIS), + v4l2_subdev_state_get_compose(sd_state, PAD_CIS), + v4l2_subdev_state_get_crop(sd_state, PAD_OUT) + }; s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r); return 0; } @@ -1696,22 +1693,22 @@ static int s5k5baf_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_get_try_format(sd, fh->state, PAD_CIS); + mf = v4l2_subdev_state_get_format(fh->state, PAD_CIS); s5k5baf_try_cis_format(mf); if (s5k5baf_is_cis_subdev(sd)) return 0; - mf = v4l2_subdev_get_try_format(sd, fh->state, PAD_OUT); + mf = v4l2_subdev_state_get_format(fh->state, PAD_OUT); mf->colorspace = s5k5baf_formats[0].colorspace; mf->code = s5k5baf_formats[0].code; mf->width = s5k5baf_cis_rect.width; mf->height = s5k5baf_cis_rect.height; mf->field = V4L2_FIELD_NONE; - *v4l2_subdev_get_try_crop(sd, fh->state, PAD_CIS) = s5k5baf_cis_rect; - *v4l2_subdev_get_try_compose(sd, fh->state, PAD_CIS) = s5k5baf_cis_rect; - *v4l2_subdev_get_try_crop(sd, fh->state, PAD_OUT) = s5k5baf_cis_rect; + *v4l2_subdev_state_get_crop(fh->state, PAD_CIS) = s5k5baf_cis_rect; + *v4l2_subdev_state_get_compose(fh->state, PAD_CIS) = s5k5baf_cis_rect; + *v4l2_subdev_state_get_crop(fh->state, PAD_OUT) = s5k5baf_cis_rect; return 0; } diff --git a/drivers/media/i2c/s5k6a3.c b/drivers/media/i2c/s5k6a3.c index b3560c8f8b4116..0c2674115b7bd3 100644 --- a/drivers/media/i2c/s5k6a3.c +++ b/drivers/media/i2c/s5k6a3.c @@ -127,8 +127,7 @@ static struct v4l2_mbus_framefmt *__s5k6a3_get_format( u32 pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return sd_state ? v4l2_subdev_get_try_format(&sensor->subdev, - sd_state, pad) : NULL; + return sd_state ? v4l2_subdev_state_get_format(sd_state, pad) : NULL; return &sensor->format; } @@ -174,9 +173,8 @@ static const struct v4l2_subdev_pad_ops s5k6a3_pad_ops = { static int s5k6a3_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { - struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(sd, - fh->state, - 0); + struct v4l2_mbus_framefmt *format = v4l2_subdev_state_get_format(fh->state, + 0); *format = s5k6a3_formats[0]; format->width = S5K6A3_DEFAULT_WIDTH; diff --git a/drivers/media/i2c/saa6752hs.c b/drivers/media/i2c/saa6752hs.c index c106e7a7d1f4ed..897eaa669b8622 100644 --- a/drivers/media/i2c/saa6752hs.c +++ b/drivers/media/i2c/saa6752hs.c @@ -594,10 +594,8 @@ static int saa6752hs_set_fmt(struct v4l2_subdev *sd, f->field = V4L2_FIELD_INTERLACED; f->colorspace = V4L2_COLORSPACE_SMPTE170M; - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - sd_state->pads->try_fmt = *f; + if (format->which == V4L2_SUBDEV_FORMAT_TRY) return 0; - } /* FIXME: translate and round width/height into EMPRESS diff --git a/drivers/media/i2c/st-mipid02.c b/drivers/media/i2c/st-mipid02.c index dab14787116b6d..914f915749a80f 100644 --- a/drivers/media/i2c/st-mipid02.c +++ b/drivers/media/i2c/st-mipid02.c @@ -724,8 +724,7 @@ static int mipid02_get_fmt(struct v4l2_subdev *sd, return -EINVAL; if (format->which == V4L2_SUBDEV_FORMAT_TRY) - fmt = v4l2_subdev_get_try_format(&bridge->sd, sd_state, - format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); else fmt = &bridge->fmt; @@ -751,8 +750,8 @@ static void mipid02_set_fmt_source(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) format->format = bridge->fmt; else - format->format = *v4l2_subdev_get_try_format(sd, sd_state, - MIPID02_SINK_0); + format->format = *v4l2_subdev_state_get_format(sd_state, + MIPID02_SINK_0); /* but code may need to be converted */ format->format.code = serial_to_parallel_code(format->format.code); @@ -761,7 +760,7 @@ static void mipid02_set_fmt_source(struct v4l2_subdev *sd, if (format->which != V4L2_SUBDEV_FORMAT_TRY) return; - *v4l2_subdev_get_try_format(sd, sd_state, MIPID02_SOURCE) = + *v4l2_subdev_state_get_format(sd_state, MIPID02_SOURCE) = format->format; } @@ -776,7 +775,7 @@ static void mipid02_set_fmt_sink(struct v4l2_subdev *sd, format->format.code = get_fmt_code(format->format.code); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); else fmt = &bridge->fmt; diff --git a/drivers/media/i2c/st-vgxy61.c b/drivers/media/i2c/st-vgxy61.c index 30f82ca344c446..e4357d997db341 100644 --- a/drivers/media/i2c/st-vgxy61.c +++ b/drivers/media/i2c/st-vgxy61.c @@ -780,8 +780,7 @@ static int vgxy61_get_fmt(struct v4l2_subdev *sd, mutex_lock(&sensor->lock); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - fmt = v4l2_subdev_get_try_format(&sensor->sd, sd_state, - format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); else fmt = &sensor->fmt; @@ -1294,7 +1293,7 @@ static int vgxy61_set_fmt(struct v4l2_subdev *sd, goto out; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); + fmt = v4l2_subdev_state_get_format(sd_state, 0); *fmt = format->format; } else if (sensor->current_mode != new_mode || sensor->fmt.code != format->format.code) { @@ -1328,8 +1327,8 @@ static int vgxy61_set_fmt(struct v4l2_subdev *sd, return ret; } -static int vgxy61_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int vgxy61_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct vgxy61_dev *sensor = to_vgxy61_dev(sd); struct v4l2_subdev_format fmt = { 0 }; @@ -1475,7 +1474,6 @@ static const struct v4l2_subdev_video_ops vgxy61_video_ops = { }; static const struct v4l2_subdev_pad_ops vgxy61_pad_ops = { - .init_cfg = vgxy61_init_cfg, .enum_mbus_code = vgxy61_enum_mbus_code, .get_fmt = vgxy61_get_fmt, .set_fmt = vgxy61_set_fmt, @@ -1488,6 +1486,10 @@ static const struct v4l2_subdev_ops vgxy61_subdev_ops = { .pad = &vgxy61_pad_ops, }; +static const struct v4l2_subdev_internal_ops vgxy61_internal_ops = { + .init_state = vgxy61_init_state, +}; + static const struct media_entity_operations vgxy61_subdev_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -1848,6 +1850,7 @@ static int vgxy61_probe(struct i2c_client *client) device_property_read_bool(dev, "st,strobe-gpios-polarity"); v4l2_i2c_subdev_init(&sensor->sd, client, &vgxy61_subdev_ops); + sensor->sd.internal_ops = &vgxy61_internal_ops; sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; sensor->pad.flags = MEDIA_PAD_FL_SOURCE; sensor->sd.entity.ops = &vgxy61_subdev_entity_ops; diff --git a/drivers/media/i2c/tc358746.c b/drivers/media/i2c/tc358746.c index 566f5eaddd572e..af0305383702b6 100644 --- a/drivers/media/i2c/tc358746.c +++ b/drivers/media/i2c/tc358746.c @@ -427,7 +427,7 @@ static int tc358746_apply_misc_config(struct tc358746 *tc358746) sink_state = v4l2_subdev_lock_and_get_active_state(sd); - mbusfmt = v4l2_subdev_get_pad_format(sd, sink_state, TC358746_SINK); + mbusfmt = v4l2_subdev_state_get_format(sink_state, TC358746_SINK); fmt = tc358746_get_format_by_code(TC358746_SINK, mbusfmt->code); /* Self defined CSI user data type id's are not supported yet */ @@ -740,15 +740,15 @@ static int tc358746_s_stream(struct v4l2_subdev *sd, int enable) return v4l2_subdev_call(src, video, s_stream, 0); } -static int tc358746_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int tc358746_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_pad_format(sd, state, TC358746_SINK); + fmt = v4l2_subdev_state_get_format(state, TC358746_SINK); *fmt = tc358746_def_fmt; - fmt = v4l2_subdev_get_pad_format(sd, state, TC358746_SOURCE); + fmt = v4l2_subdev_state_get_format(state, TC358746_SOURCE); *fmt = tc358746_def_fmt; fmt->code = tc358746_src_mbus_code(tc358746_def_fmt.code); @@ -781,7 +781,7 @@ static int tc358746_set_fmt(struct v4l2_subdev *sd, if (format->pad == TC358746_SOURCE) return v4l2_subdev_get_fmt(sd, sd_state, format); - sink_fmt = v4l2_subdev_get_pad_format(sd, sd_state, TC358746_SINK); + sink_fmt = v4l2_subdev_state_get_format(sd_state, TC358746_SINK); fmt = tc358746_get_format_by_code(format->pad, format->format.code); if (IS_ERR(fmt)) @@ -796,7 +796,7 @@ static int tc358746_set_fmt(struct v4l2_subdev *sd, *sink_fmt = format->format; - src_fmt = v4l2_subdev_get_pad_format(sd, sd_state, TC358746_SOURCE); + src_fmt = v4l2_subdev_state_get_format(sd_state, TC358746_SOURCE); *src_fmt = *sink_fmt; src_fmt->code = tc358746_src_mbus_code(sink_fmt->code); @@ -901,7 +901,7 @@ tc358746_link_validate(struct v4l2_subdev *sd, struct media_link *link, return err; sink_state = v4l2_subdev_lock_and_get_active_state(sd); - mbusfmt = v4l2_subdev_get_pad_format(sd, sink_state, TC358746_SINK); + mbusfmt = v4l2_subdev_state_get_format(sink_state, TC358746_SINK); /* Check the FIFO settings */ fmt = tc358746_get_format_by_code(TC358746_SINK, mbusfmt->code); @@ -1034,7 +1034,6 @@ static const struct v4l2_subdev_video_ops tc358746_video_ops = { }; static const struct v4l2_subdev_pad_ops tc358746_pad_ops = { - .init_cfg = tc358746_init_cfg, .enum_mbus_code = tc358746_enum_mbus_code, .set_fmt = tc358746_set_fmt, .get_fmt = v4l2_subdev_get_fmt, @@ -1048,6 +1047,10 @@ static const struct v4l2_subdev_ops tc358746_ops = { .pad = &tc358746_pad_ops, }; +static const struct v4l2_subdev_internal_ops tc358746_internal_ops = { + .init_state = tc358746_init_state, +}; + static const struct media_entity_operations tc358746_entity_ops = { .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1, .link_validate = v4l2_subdev_link_validate, @@ -1278,6 +1281,7 @@ tc358746_init_subdev(struct tc358746 *tc358746, struct i2c_client *client) int err; v4l2_i2c_subdev_init(sd, client, &tc358746_ops); + sd->internal_ops = &tc358746_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; sd->entity.ops = &tc358746_entity_ops; diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c index 325e9912594164..1ea703a9909f54 100644 --- a/drivers/media/i2c/tda1997x.c +++ b/drivers/media/i2c/tda1997x.c @@ -1734,13 +1734,13 @@ static const struct v4l2_subdev_video_ops tda1997x_video_ops = { * v4l2_subdev_pad_ops */ -static int tda1997x_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int tda1997x_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct tda1997x_state *state = to_state(sd); struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_get_try_format(sd, sd_state, 0); + mf = v4l2_subdev_state_get_format(sd_state, 0); mf->code = state->mbus_codes[0]; return 0; @@ -1792,7 +1792,7 @@ static int tda1997x_get_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); format->format.code = fmt->code; } else format->format.code = state->mbus_code; @@ -1826,7 +1826,7 @@ static int tda1997x_set_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); *fmt = format->format; } else { int ret = tda1997x_setup_format(state, format->format.code); @@ -1925,7 +1925,6 @@ static int tda1997x_enum_dv_timings(struct v4l2_subdev *sd, } static const struct v4l2_subdev_pad_ops tda1997x_pad_ops = { - .init_cfg = tda1997x_init_cfg, .enum_mbus_code = tda1997x_enum_mbus_code, .get_fmt = tda1997x_get_format, .set_fmt = tda1997x_set_format, @@ -2047,6 +2046,10 @@ static const struct v4l2_subdev_ops tda1997x_subdev_ops = { .pad = &tda1997x_pad_ops, }; +static const struct v4l2_subdev_internal_ops tda1997x_internal_ops = { + .init_state = tda1997x_init_state, +}; + /* ----------------------------------------------------------------------------- * v4l2_controls */ @@ -2588,6 +2591,7 @@ static int tda1997x_probe(struct i2c_client *client) /* initialize subdev */ sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &tda1997x_subdev_ops); + sd->internal_ops = &tda1997x_internal_ops; snprintf(sd->name, sizeof(sd->name), "%s %d-%04x", id->name, i2c_adapter_id(client->adapter), client->addr); diff --git a/drivers/media/i2c/thp7312.c b/drivers/media/i2c/thp7312.c new file mode 100644 index 00000000000000..3d46e428e0ac08 --- /dev/null +++ b/drivers/media/i2c/thp7312.c @@ -0,0 +1,2244 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2021 THine Electronics, Inc. + * Copyright (C) 2023 Ideas on Board Oy + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +/* ISP registers */ + +#define THP7312_REG_FIRMWARE_VERSION_1 CCI_REG8(0xf000) +#define THP7312_REG_CAMERA_STATUS CCI_REG8(0xf001) +#define THP7312_REG_FIRMWARE_VERSION_2 CCI_REG8(0xf005) +#define THP7312_REG_SET_OUTPUT_ENABLE CCI_REG8(0xf008) +#define THP7312_OUTPUT_ENABLE 0x01 +#define THP7312_OUTPUT_DISABLE 0x00 +#define THP7312_REG_SET_OUTPUT_COLOR_COMPRESSION CCI_REG8(0xf009) +#define THP7312_REG_SET_OUTPUT_COLOR_UYVY 0x00 +#define THP7312_REG_SET_OUTPUT_COLOR_YUY2 0x04 +#define THP7312_REG_FLIP_MIRROR CCI_REG8(0xf00c) +#define THP7312_REG_FLIP_MIRROR_FLIP BIT(0) +#define THP7312_REG_FLIP_MIRROR_MIRROR BIT(1) +#define THP7312_REG_VIDEO_IMAGE_SIZE CCI_REG8(0xf00d) +#define THP7312_VIDEO_IMAGE_SIZE_640x360 0x52 +#define THP7312_VIDEO_IMAGE_SIZE_640x460 0x03 +#define THP7312_VIDEO_IMAGE_SIZE_1280x720 0x0a +#define THP7312_VIDEO_IMAGE_SIZE_1920x1080 0x0b +#define THP7312_VIDEO_IMAGE_SIZE_3840x2160 0x0d +#define THP7312_VIDEO_IMAGE_SIZE_4160x3120 0x14 +#define THP7312_VIDEO_IMAGE_SIZE_2016x1512 0x20 +#define THP7312_VIDEO_IMAGE_SIZE_2048x1536 0x21 +#define THP7312_REG_VIDEO_FRAME_RATE_MODE CCI_REG8(0xf00f) +#define THP7312_VIDEO_FRAME_RATE_MODE1 0x80 +#define THP7312_VIDEO_FRAME_RATE_MODE2 0x81 +#define THP7312_VIDEO_FRAME_RATE_MODE3 0x82 +#define THP7312_REG_SET_DRIVING_MODE CCI_REG8(0xf010) +#define THP7312_REG_DRIVING_MODE_STATUS CCI_REG8(0xf011) +#define THP7312_REG_JPEG_COMPRESSION_FACTOR CCI_REG8(0xf01b) +#define THP7312_REG_AE_EXPOSURE_COMPENSATION CCI_REG8(0xf022) +#define THP7312_REG_AE_FLICKER_MODE CCI_REG8(0xf023) +#define THP7312_AE_FLICKER_MODE_50 0x00 +#define THP7312_AE_FLICKER_MODE_60 0x01 +#define THP7312_AE_FLICKER_MODE_DISABLE 0x80 +#define THP7312_REG_AE_FIX_FRAME_RATE CCI_REG8(0xf02e) +#define THP7312_REG_MANUAL_WB_RED_GAIN CCI_REG8(0xf036) +#define THP7312_REG_MANUAL_WB_BLUE_GAIN CCI_REG8(0xf037) +#define THP7312_REG_WB_MODE CCI_REG8(0xf039) +#define THP7312_WB_MODE_AUTO 0x00 +#define THP7312_WB_MODE_MANUAL 0x11 +#define THP7312_REG_MANUAL_FOCUS_POSITION CCI_REG16(0xf03c) +#define THP7312_REG_AF_CONTROL CCI_REG8(0xf040) +#define THP7312_REG_AF_CONTROL_AF 0x01 +#define THP7312_REG_AF_CONTROL_MANUAL 0x10 +#define THP7312_REG_AF_CONTROL_LOCK 0x80 +#define THP7312_REG_AF_SETTING CCI_REG8(0xf041) +#define THP7312_REG_AF_SETTING_ONESHOT_CONTRAST 0x00 +#define THP7312_REG_AF_SETTING_ONESHOT_PDAF 0x40 +#define THP7312_REG_AF_SETTING_ONESHOT_HYBRID 0x80 +#define THP7312_REG_AF_SETTING_CONTINUOUS_CONTRAST 0x30 +#define THP7312_REG_AF_SETTING_CONTINUOUS_PDAF 0x70 +#define THP7312_REG_AF_SETTING_CONTINUOUS_HYBRID 0xf0 +#define THP7312_REG_AF_SUPPORT CCI_REG8(0xf043) +#define THP7312_AF_SUPPORT_PDAF BIT(1) +#define THP7312_AF_SUPPORT_CONTRAST BIT(0) +#define THP7312_REG_SATURATION CCI_REG8(0xf052) +#define THP7312_REG_SHARPNESS CCI_REG8(0xf053) +#define THP7312_REG_BRIGHTNESS CCI_REG8(0xf056) +#define THP7312_REG_CONTRAST CCI_REG8(0xf057) +#define THP7312_REG_NOISE_REDUCTION CCI_REG8(0xf059) +#define THP7312_REG_NOISE_REDUCTION_FIXED BIT(7) + +#define TH7312_REG_CUSTOM_MIPI_SET CCI_REG8(0xf0f6) +#define TH7312_REG_CUSTOM_MIPI_STATUS CCI_REG8(0xf0f7) +#define TH7312_REG_CUSTOM_MIPI_RD CCI_REG8(0xf0f8) +#define TH7312_REG_CUSTOM_MIPI_TD CCI_REG8(0xf0f9) + +/* + * Firmware update registers. Those use a different address space than the + * normal operation ISP registers. + */ + +#define THP7312_REG_FW_DRIVABILITY CCI_REG32(0xd65c) +#define THP7312_REG_FW_DEST_BANK_ADDR CCI_REG32(0xff08) +#define THP7312_REG_FW_VERIFY_RESULT CCI_REG8(0xff60) +#define THP7312_REG_FW_RESET_FLASH CCI_REG8(0xff61) +#define THP7312_REG_FW_MEMORY_IO_SETTING CCI_REG8(0xff62) +#define THP7312_FW_MEMORY_IO_GPIO0 1 +#define THP7312_FW_MEMORY_IO_GPIO1 0 +#define THP7312_REG_FW_CRC_RESULT CCI_REG32(0xff64) +#define THP7312_REG_FW_STATUS CCI_REG8(0xfffc) + +#define THP7312_FW_VERSION(major, minor) (((major) << 8) | (minor)) +#define THP7312_FW_VERSION_MAJOR(v) ((v) >> 8) +#define THP7312_FW_VERSION_MINOR(v) ((v) & 0xff) + +enum thp7312_focus_method { + THP7312_FOCUS_METHOD_CONTRAST, + THP7312_FOCUS_METHOD_PDAF, + THP7312_FOCUS_METHOD_HYBRID, +}; + +/* + * enum thp7312_focus_state - State of the focus handler + * + * @THP7312_FOCUS_STATE_MANUAL: Manual focus, controlled through the + * V4L2_CID_FOCUS_ABSOLUTE control + * @THP7312_FOCUS_STATE_AUTO: Continuous auto-focus + * @THP7312_FOCUS_STATE_LOCKED: Lock the focus to a fixed position. This state + * is entered when switching from auto to manual mode. + * @THP7312_FOCUS_STATE_ONESHOT: One-shot auto-focus + * + * Valid transitions are as follow: + * + * digraph fsm { + * node [shape=circle]; + * + * manual [label="MANUAL"]; + * auto [label="AUTO"]; + * locked [label="LOCKED"]; + * oneshot [label="ONESHOT"]; + * + * manual -> auto [label="FOCUS_AUTO <- true"] + * locked -> auto [label="FOCUS_AUTO <- true"] + * oneshot -> auto [label="FOCUS_AUTO <- true"] + * auto -> locked [label="FOCUS_AUTO <- false"] + * + * locked -> manual [label="FOCUS_ABSOLUTE <- *"] + * oneshot -> manual [label="FOCUS_ABSOLUTE <- *"] + * + * manual -> oneshot [label="FOCUS_START <- *"] + * locked -> oneshot [label="FOCUS_START <- *"] + * } + */ +enum thp7312_focus_state { + THP7312_FOCUS_STATE_MANUAL, + THP7312_FOCUS_STATE_AUTO, + THP7312_FOCUS_STATE_LOCKED, + THP7312_FOCUS_STATE_ONESHOT, +}; + +enum thp7312_boot_mode { + THP7312_BOOT_MODE_2WIRE_SLAVE = 0, + THP7312_BOOT_MODE_SPI_MASTER = 1, +}; + +struct thp7312_frame_rate { + u32 fps; + u32 link_freq; + u8 reg_frame_rate_mode; +}; + +struct thp7312_mode_info { + u32 width; + u32 height; + u8 reg_image_size; + const struct thp7312_frame_rate *rates; +}; + +static const u32 thp7312_colour_fmts[] = { + MEDIA_BUS_FMT_YUYV8_1X16, +}; + +/* regulator supplies */ +static const char * const thp7312_supply_name[] = { + "vddcore", + "vhtermrx", + "vddtx", + "vddhost", + "vddcmos", + "vddgpio-0", + "vddgpio-1", +}; + +static const struct thp7312_mode_info thp7312_mode_info_data[] = { + { + .width = 1920, + .height = 1080, + .reg_image_size = THP7312_VIDEO_IMAGE_SIZE_1920x1080, + .rates = (const struct thp7312_frame_rate[]) { + { 30, 300000000, 0x81 }, + { 60, 387500000, 0x82 }, + { 0 } + }, + }, { + .width = 2048, + .height = 1536, + .reg_image_size = THP7312_VIDEO_IMAGE_SIZE_2048x1536, + .rates = (const struct thp7312_frame_rate[]) { + { 30, 300000000, 0x81 }, + { 0 } + } + }, { + .width = 3840, + .height = 2160, + .reg_image_size = THP7312_VIDEO_IMAGE_SIZE_3840x2160, + .rates = (const struct thp7312_frame_rate[]) { + { 30, 600000000, 0x81 }, + { 0 } + }, + }, { + .width = 4160, + .height = 3120, + .reg_image_size = THP7312_VIDEO_IMAGE_SIZE_4160x3120, + .rates = (const struct thp7312_frame_rate[]) { + { 20, 600000000, 0x81 }, + { 0 } + }, + }, +}; + +struct thp7312_device; + +struct thp7312_sensor_info { + const char *model; +}; + +struct thp7312_sensor { + const struct thp7312_sensor_info *info; + u8 lane_remap; +}; + +struct thp7312_device { + struct device *dev; + struct regmap *regmap; + + struct v4l2_subdev sd; + struct media_pad pad; + + struct gpio_desc *reset_gpio; + struct regulator_bulk_data supplies[ARRAY_SIZE(thp7312_supply_name)]; + struct clk *iclk; + + u8 lane_remap; + + struct thp7312_sensor sensors[1]; + + enum thp7312_boot_mode boot_mode; + + struct v4l2_ctrl_handler ctrl_handler; + bool ctrls_applied; + + /* These are protected by v4l2 active state */ + const struct thp7312_mode_info *current_mode; + const struct thp7312_frame_rate *current_rate; + s64 link_freq; + + struct { + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + }; + + struct { + struct v4l2_ctrl *focus_auto; + struct v4l2_ctrl *focus_absolute; + struct v4l2_ctrl *focus_start; + struct v4l2_ctrl *focus_method; + }; + + enum thp7312_focus_state focus_state; + + struct { + struct v4l2_ctrl *noise_reduction_auto; + struct v4l2_ctrl *noise_reduction_absolute; + }; + + /* Lock to protect fw_cancel */ + struct mutex fw_lock; + struct fw_upload *fwl; + u8 *fw_write_buf; + bool fw_cancel; + + u16 fw_version; +}; + +static const struct thp7312_sensor_info thp7312_sensor_info[] = { + { + .model = "sony,imx258", + }, +}; + +static inline struct thp7312_device *to_thp7312_dev(struct v4l2_subdev *sd) +{ + return container_of(sd, struct thp7312_device, sd); +} + +/* ----------------------------------------------------------------------------- + * Device Access & Configuration + */ + +#define thp7312_read_poll_timeout(dev, addr, val, cond, sleep_us, timeout_us) \ +({ \ + int __ret, __err; \ + __ret = read_poll_timeout(cci_read, __err, __err || (cond), sleep_us, \ + timeout_us, false, (dev)->regmap, addr, \ + &(val), NULL); \ + __ret ? : __err; \ +}) + +static int thp7312_map_data_lanes(u8 *lane_remap, const u8 *lanes, u8 num_lanes) +{ + u8 used_lanes = 0; + u8 val = 0; + unsigned int i; + + /* + * The value that we write to the register is the index in the + * data-lanes array, so we need to do a conversion. Do this in the same + * pass as validating data-lanes. + */ + for (i = 0; i < num_lanes; i++) { + if (lanes[i] < 1 || lanes[i] > 4) + return -EINVAL; + + if (used_lanes & (BIT(lanes[i]))) + return -EINVAL; + + used_lanes |= BIT(lanes[i]); + + /* + * data-lanes is 1-indexed while the field position in the + * register is 0-indexed. + */ + val |= i << ((lanes[i] - 1) * 2); + } + + *lane_remap = val; + + return 0; +} + +static int thp7312_set_mipi_lanes(struct thp7312_device *thp7312) +{ + struct device *dev = thp7312->dev; + int ret = 0; + u64 val; + + cci_write(thp7312->regmap, TH7312_REG_CUSTOM_MIPI_RD, + thp7312->sensors[0].lane_remap, &ret); + cci_write(thp7312->regmap, TH7312_REG_CUSTOM_MIPI_TD, + thp7312->lane_remap, &ret); + cci_write(thp7312->regmap, TH7312_REG_CUSTOM_MIPI_SET, 1, &ret); + + if (ret) + return ret; + + ret = thp7312_read_poll_timeout(thp7312, TH7312_REG_CUSTOM_MIPI_STATUS, + val, val == 0x00, 100000, 2000000); + if (ret) { + dev_err(dev, "Failed to poll MIPI lane status: %d\n", ret); + return ret; + } + + return 0; +} + +static int thp7312_change_mode(struct thp7312_device *thp7312, + const struct thp7312_mode_info *mode, + const struct thp7312_frame_rate *rate) +{ + struct device *dev = thp7312->dev; + u64 val = 0; + int ret; + + ret = thp7312_read_poll_timeout(thp7312, THP7312_REG_CAMERA_STATUS, val, + val == 0x80, 20000, 200000); + if (ret < 0) { + dev_err(dev, "%s(): failed to poll ISP: %d\n", __func__, ret); + return ret; + } + + cci_write(thp7312->regmap, THP7312_REG_VIDEO_IMAGE_SIZE, + mode->reg_image_size, &ret); + cci_write(thp7312->regmap, THP7312_REG_VIDEO_FRAME_RATE_MODE, + rate->reg_frame_rate_mode, &ret); + cci_write(thp7312->regmap, THP7312_REG_JPEG_COMPRESSION_FACTOR, 0x5e, + &ret); + cci_write(thp7312->regmap, THP7312_REG_SET_DRIVING_MODE, 0x01, &ret); + + if (ret) + return ret; + + ret = thp7312_read_poll_timeout(thp7312, THP7312_REG_DRIVING_MODE_STATUS, + val, val == 0x01, 20000, 100000); + if (ret < 0) { + dev_err(dev, "%s(): failed\n", __func__); + return ret; + } + + return 0; +} + +static int thp7312_set_framefmt(struct thp7312_device *thp7312, + struct v4l2_mbus_framefmt *format) +{ + u8 val; + + switch (format->code) { + case MEDIA_BUS_FMT_UYVY8_1X16: + /* YUV422, UYVY */ + val = THP7312_REG_SET_OUTPUT_COLOR_UYVY; + break; + case MEDIA_BUS_FMT_YUYV8_1X16: + /* YUV422, YUYV */ + val = THP7312_REG_SET_OUTPUT_COLOR_YUY2; + break; + default: + /* Should never happen */ + return -EINVAL; + } + + return cci_write(thp7312->regmap, + THP7312_REG_SET_OUTPUT_COLOR_COMPRESSION, val, NULL); +} + +static int thp7312_init_mode(struct thp7312_device *thp7312, + struct v4l2_subdev_state *sd_state) +{ + struct v4l2_mbus_framefmt *fmt; + int ret; + + fmt = v4l2_subdev_state_get_format(sd_state, 0); + + ret = thp7312_set_framefmt(thp7312, fmt); + if (ret) + return ret; + + return thp7312_change_mode(thp7312, thp7312->current_mode, + thp7312->current_rate); +} + +static int thp7312_stream_enable(struct thp7312_device *thp7312, bool enable) +{ + return cci_write(thp7312->regmap, THP7312_REG_SET_OUTPUT_ENABLE, + enable ? THP7312_OUTPUT_ENABLE : THP7312_OUTPUT_DISABLE, + NULL); +} + +static int thp7312_check_status_stream_mode(struct thp7312_device *thp7312) +{ + struct device *dev = thp7312->dev; + u64 status = 0; + int ret; + + while (status != 0x80) { + ret = cci_read(thp7312->regmap, THP7312_REG_CAMERA_STATUS, + &status, NULL); + if (ret) + return ret; + + if (status == 0x80) { + dev_dbg(dev, "Camera initialization done\n"); + return 0; + } + + if (status != 0x00) { + dev_err(dev, "Invalid camera status %llx\n", status); + return -EINVAL; + } + + dev_dbg(dev, "Camera initializing...\n"); + usleep_range(70000, 80000); + } + + return 0; +} + +static void thp7312_reset(struct thp7312_device *thp7312) +{ + unsigned long rate; + + gpiod_set_value_cansleep(thp7312->reset_gpio, 1); + + /* + * The minimum reset duration is 8 clock cycles, make it 10 to provide + * a safety margin. + */ + rate = clk_get_rate(thp7312->iclk); + fsleep(DIV_ROUND_UP(10 * USEC_PER_SEC, rate)); + + gpiod_set_value_cansleep(thp7312->reset_gpio, 0); + + /* + * TODO: The documentation states that the device needs 2ms to + * initialize after reset is deasserted. It then proceeds to load the + * firmware from the flash memory, which takes an unspecified amount of + * time. Check if this delay could be reduced. + */ + fsleep(300000); +} + +/* ----------------------------------------------------------------------------- + * Power Management + */ + +static void __thp7312_power_off(struct thp7312_device *thp7312) +{ + regulator_bulk_disable(ARRAY_SIZE(thp7312->supplies), thp7312->supplies); + clk_disable_unprepare(thp7312->iclk); +} + +static void thp7312_power_off(struct thp7312_device *thp7312) +{ + __thp7312_power_off(thp7312); +} + +static int __thp7312_power_on(struct thp7312_device *thp7312) +{ + struct device *dev = thp7312->dev; + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(thp7312->supplies), + thp7312->supplies); + if (ret < 0) + return ret; + + ret = clk_prepare_enable(thp7312->iclk); + if (ret < 0) { + dev_err(dev, "clk prepare enable failed\n"); + regulator_bulk_disable(ARRAY_SIZE(thp7312->supplies), + thp7312->supplies); + return ret; + } + + /* + * We cannot assume that turning off and on again will reset, so do a + * software reset on power up. + */ + thp7312_reset(thp7312); + + return 0; +} + +static int thp7312_power_on(struct thp7312_device *thp7312) +{ + int ret; + + ret = __thp7312_power_on(thp7312); + if (ret < 0) + return ret; + + ret = thp7312_check_status_stream_mode(thp7312); + if (ret < 0) + goto error; + + ret = thp7312_set_mipi_lanes(thp7312); + if (ret) + goto error; + + return 0; + +error: + thp7312_power_off(thp7312); + return ret; +} + +static int __maybe_unused thp7312_pm_runtime_suspend(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct thp7312_device *thp7312 = to_thp7312_dev(sd); + + thp7312_power_off(thp7312); + + thp7312->ctrls_applied = false; + + return 0; +} + +static int __maybe_unused thp7312_pm_runtime_resume(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct thp7312_device *thp7312 = to_thp7312_dev(sd); + + return thp7312_power_on(thp7312); +} + +static const struct dev_pm_ops thp7312_pm_ops = { + SET_RUNTIME_PM_OPS(thp7312_pm_runtime_suspend, + thp7312_pm_runtime_resume, NULL) +}; + +/* ----------------------------------------------------------------------------- + * V4L2 Subdev Operations + */ + +static bool thp7312_find_bus_code(u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(thp7312_colour_fmts); ++i) { + if (thp7312_colour_fmts[i] == code) + return true; + } + + return false; +} + +static const struct thp7312_mode_info * +thp7312_find_mode(unsigned int width, unsigned int height, bool nearest) +{ + const struct thp7312_mode_info *mode; + + mode = v4l2_find_nearest_size(thp7312_mode_info_data, + ARRAY_SIZE(thp7312_mode_info_data), + width, height, width, height); + + if (!nearest && (mode->width != width || mode->height != height)) + return NULL; + + return mode; +} + +static void thp7312_set_frame_rate(struct thp7312_device *thp7312, + const struct thp7312_frame_rate *rate) +{ + thp7312->link_freq = rate->link_freq; + thp7312->current_rate = rate; +} + +static int thp7312_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(thp7312_colour_fmts)) + return -EINVAL; + + code->code = thp7312_colour_fmts[code->index]; + + return 0; +} + +static int thp7312_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (!thp7312_find_bus_code(fse->code)) + return -EINVAL; + + if (fse->index >= ARRAY_SIZE(thp7312_mode_info_data)) + return -EINVAL; + + fse->min_width = thp7312_mode_info_data[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = thp7312_mode_info_data[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static int thp7312_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval_enum *fie) +{ + const struct thp7312_frame_rate *rate; + const struct thp7312_mode_info *mode; + unsigned int index = fie->index; + + if (!thp7312_find_bus_code(fie->code)) + return -EINVAL; + + mode = thp7312_find_mode(fie->width, fie->height, false); + if (!mode) + return -EINVAL; + + for (rate = mode->rates; rate->fps; ++rate, --index) { + if (!index) { + fie->interval.numerator = 1; + fie->interval.denominator = rate->fps; + + return 0; + } + } + + return -EINVAL; +} + +static int thp7312_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *format) +{ + struct thp7312_device *thp7312 = to_thp7312_dev(sd); + struct v4l2_mbus_framefmt *mbus_fmt = &format->format; + struct v4l2_mbus_framefmt *fmt; + const struct thp7312_mode_info *mode; + + if (!thp7312_find_bus_code(mbus_fmt->code)) + mbus_fmt->code = thp7312_colour_fmts[0]; + + mode = thp7312_find_mode(mbus_fmt->width, mbus_fmt->height, true); + + fmt = v4l2_subdev_state_get_format(sd_state, 0); + + fmt->code = mbus_fmt->code; + fmt->width = mode->width; + fmt->height = mode->height; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); + fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; + fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); + + *mbus_fmt = *fmt; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + thp7312->current_mode = mode; + thp7312_set_frame_rate(thp7312, &mode->rates[0]); + } + + return 0; +} + +static int thp7312_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct thp7312_device *thp7312 = to_thp7312_dev(sd); + struct v4l2_subdev_state *sd_state; + + sd_state = v4l2_subdev_lock_and_get_active_state(sd); + fi->interval.numerator = 1; + fi->interval.denominator = thp7312->current_rate->fps; + v4l2_subdev_unlock_state(sd_state); + + return 0; +} + +static int thp7312_s_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct thp7312_device *thp7312 = to_thp7312_dev(sd); + const struct thp7312_mode_info *mode; + const struct thp7312_frame_rate *best_rate = NULL; + const struct thp7312_frame_rate *rate; + struct v4l2_subdev_state *sd_state; + unsigned int best_delta = UINT_MAX; + unsigned int fps; + + /* Avoid divisions by 0, pick the highest frame if the interval is 0. */ + fps = fi->interval.numerator + ? DIV_ROUND_CLOSEST(fi->interval.denominator, fi->interval.numerator) + : UINT_MAX; + + sd_state = v4l2_subdev_lock_and_get_active_state(sd); + + mode = thp7312->current_mode; + + for (rate = mode->rates; rate->fps && best_delta; ++rate) { + unsigned int delta = abs(rate->fps - fps); + + if (delta <= best_delta) { + best_delta = delta; + best_rate = rate; + } + } + + thp7312_set_frame_rate(thp7312, best_rate); + + v4l2_subdev_unlock_state(sd_state); + + fi->interval.numerator = 1; + fi->interval.denominator = best_rate->fps; + + return 0; +} + +static int thp7312_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct thp7312_device *thp7312 = to_thp7312_dev(sd); + struct v4l2_subdev_state *sd_state; + int ret; + + sd_state = v4l2_subdev_lock_and_get_active_state(sd); + + if (!enable) { + thp7312_stream_enable(thp7312, false); + + pm_runtime_mark_last_busy(thp7312->dev); + pm_runtime_put_autosuspend(thp7312->dev); + + v4l2_subdev_unlock_state(sd_state); + + return 0; + } + + ret = pm_runtime_resume_and_get(thp7312->dev); + if (ret) + goto finish_unlock; + + ret = thp7312_init_mode(thp7312, sd_state); + if (ret) + goto finish_pm; + + if (!thp7312->ctrls_applied) { + ret = __v4l2_ctrl_handler_setup(&thp7312->ctrl_handler); + if (ret) + goto finish_pm; + + thp7312->ctrls_applied = true; + } + + ret = thp7312_stream_enable(thp7312, true); + if (ret) + goto finish_pm; + + goto finish_unlock; + +finish_pm: + pm_runtime_mark_last_busy(thp7312->dev); + pm_runtime_put_autosuspend(thp7312->dev); +finish_unlock: + v4l2_subdev_unlock_state(sd_state); + + return ret; +} + +static int thp7312_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + const struct thp7312_mode_info *default_mode = &thp7312_mode_info_data[0]; + struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_state_get_format(sd_state, 0); + + /* + * default init sequence initialize thp7312 to + * YUV422 YUYV VGA@30fps + */ + fmt->code = MEDIA_BUS_FMT_YUYV8_1X16; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); + fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; + fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); + fmt->width = default_mode->width; + fmt->height = default_mode->height; + fmt->field = V4L2_FIELD_NONE; + + return 0; +} + +static const struct v4l2_subdev_core_ops thp7312_core_ops = { + .log_status = v4l2_ctrl_subdev_log_status, + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_video_ops thp7312_video_ops = { + .g_frame_interval = thp7312_g_frame_interval, + .s_frame_interval = thp7312_s_frame_interval, + .s_stream = thp7312_s_stream, +}; + +static const struct v4l2_subdev_pad_ops thp7312_pad_ops = { + .enum_mbus_code = thp7312_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = thp7312_set_fmt, + .enum_frame_size = thp7312_enum_frame_size, + .enum_frame_interval = thp7312_enum_frame_interval, +}; + +static const struct v4l2_subdev_ops thp7312_subdev_ops = { + .core = &thp7312_core_ops, + .video = &thp7312_video_ops, + .pad = &thp7312_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops thp7312_internal_ops = { + .init_state = thp7312_init_state, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 Control Operations + */ + +static inline struct thp7312_device *to_thp7312_from_ctrl(struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct thp7312_device, ctrl_handler); +} + +/* 0: 3000cm, 18: 8cm */ +static const u16 thp7312_focus_values[] = { + 3000, 1000, 600, 450, 350, + 290, 240, 200, 170, 150, + 140, 130, 120, 110, 100, + 93, 87, 83, 80, +}; + +static int thp7312_set_focus(struct thp7312_device *thp7312) +{ + enum thp7312_focus_state new_state = thp7312->focus_state; + bool continuous; + u8 af_control; + u8 af_setting; + int ret = 0; + + /* Start by programming the manual focus position if it has changed. */ + if (thp7312->focus_absolute->is_new) { + unsigned int value; + + value = thp7312_focus_values[thp7312->focus_absolute->val]; + + ret = cci_write(thp7312->regmap, + THP7312_REG_MANUAL_FOCUS_POSITION, value, NULL); + if (ret) + return ret; + } + + /* Calculate the new focus state. */ + switch (thp7312->focus_state) { + case THP7312_FOCUS_STATE_MANUAL: + default: + if (thp7312->focus_auto->val) + new_state = THP7312_FOCUS_STATE_AUTO; + else if (thp7312->focus_start->is_new) + new_state = THP7312_FOCUS_STATE_ONESHOT; + break; + + case THP7312_FOCUS_STATE_AUTO: + if (!thp7312->focus_auto->val) + new_state = THP7312_FOCUS_STATE_LOCKED; + break; + + case THP7312_FOCUS_STATE_LOCKED: + if (thp7312->focus_auto->val) + new_state = THP7312_FOCUS_STATE_AUTO; + else if (thp7312->focus_start->is_new) + new_state = THP7312_FOCUS_STATE_ONESHOT; + else if (thp7312->focus_absolute->is_new) + new_state = THP7312_FOCUS_STATE_MANUAL; + break; + + case THP7312_FOCUS_STATE_ONESHOT: + if (thp7312->focus_auto->val) + new_state = THP7312_FOCUS_STATE_AUTO; + else if (thp7312->focus_start->is_new) + new_state = THP7312_FOCUS_STATE_ONESHOT; + else if (thp7312->focus_absolute->is_new) + new_state = THP7312_FOCUS_STATE_MANUAL; + break; + } + + /* + * If neither the state nor the focus method has changed, and no new + * one-shot focus is requested, there's nothing new to program to the + * hardware. + */ + if (thp7312->focus_state == new_state && + !thp7312->focus_method->is_new && !thp7312->focus_start->is_new) + return 0; + + continuous = new_state == THP7312_FOCUS_STATE_MANUAL || + new_state == THP7312_FOCUS_STATE_ONESHOT; + + switch (thp7312->focus_method->val) { + case THP7312_FOCUS_METHOD_CONTRAST: + default: + af_setting = continuous + ? THP7312_REG_AF_SETTING_CONTINUOUS_CONTRAST + : THP7312_REG_AF_SETTING_ONESHOT_CONTRAST; + break; + case THP7312_FOCUS_METHOD_PDAF: + af_setting = continuous + ? THP7312_REG_AF_SETTING_CONTINUOUS_PDAF + : THP7312_REG_AF_SETTING_ONESHOT_PDAF; + break; + case THP7312_FOCUS_METHOD_HYBRID: + af_setting = continuous + ? THP7312_REG_AF_SETTING_CONTINUOUS_HYBRID + : THP7312_REG_AF_SETTING_ONESHOT_HYBRID; + break; + } + + switch (new_state) { + case THP7312_FOCUS_STATE_MANUAL: + default: + af_control = THP7312_REG_AF_CONTROL_MANUAL; + break; + case THP7312_FOCUS_STATE_AUTO: + case THP7312_FOCUS_STATE_ONESHOT: + af_control = THP7312_REG_AF_CONTROL_AF; + break; + case THP7312_FOCUS_STATE_LOCKED: + af_control = THP7312_REG_AF_CONTROL_LOCK; + break; + } + + cci_write(thp7312->regmap, THP7312_REG_AF_SETTING, af_setting, &ret); + + if (new_state == THP7312_FOCUS_STATE_MANUAL && + (thp7312->focus_state == THP7312_FOCUS_STATE_AUTO || + thp7312->focus_state == THP7312_FOCUS_STATE_ONESHOT)) { + /* When switching to manual state, lock AF first. */ + cci_write(thp7312->regmap, THP7312_REG_AF_CONTROL, + THP7312_REG_AF_CONTROL_LOCK, &ret); + } + + cci_write(thp7312->regmap, THP7312_REG_AF_CONTROL, af_control, &ret); + + if (ret) + return ret; + + thp7312->focus_state = new_state; + + return 0; +} + +static int thp7312_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct thp7312_device *thp7312 = to_thp7312_from_ctrl(ctrl); + int ret = 0; + u8 value; + + if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) + return -EINVAL; + + if (!pm_runtime_get_if_active(thp7312->dev, true)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + cci_write(thp7312->regmap, THP7312_REG_BRIGHTNESS, + ctrl->val + 10, &ret); + break; + + case V4L2_CID_THP7312_LOW_LIGHT_COMPENSATION: + /* 0 = Auto adjust frame rate, 1 = Fix frame rate */ + cci_write(thp7312->regmap, THP7312_REG_AE_FIX_FRAME_RATE, + ctrl->val ? 0 : 1, &ret); + break; + + case V4L2_CID_FOCUS_AUTO: + case V4L2_CID_FOCUS_ABSOLUTE: + case V4L2_CID_AUTO_FOCUS_START: + case V4L2_CID_THP7312_AUTO_FOCUS_METHOD: + ret = thp7312_set_focus(thp7312); + break; + + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + value = (thp7312->hflip->val ? THP7312_REG_FLIP_MIRROR_MIRROR : 0) + | (thp7312->vflip->val ? THP7312_REG_FLIP_MIRROR_FLIP : 0); + + cci_write(thp7312->regmap, THP7312_REG_FLIP_MIRROR, value, &ret); + break; + + case V4L2_CID_THP7312_NOISE_REDUCTION_AUTO: + case V4L2_CID_THP7312_NOISE_REDUCTION_ABSOLUTE: + value = thp7312->noise_reduction_auto->val ? 0 + : THP7312_REG_NOISE_REDUCTION_FIXED | + thp7312->noise_reduction_absolute->val; + + cci_write(thp7312->regmap, THP7312_REG_NOISE_REDUCTION, value, + &ret); + break; + + case V4L2_CID_AUTO_WHITE_BALANCE: + value = ctrl->val ? THP7312_WB_MODE_AUTO : THP7312_WB_MODE_MANUAL; + + cci_write(thp7312->regmap, THP7312_REG_WB_MODE, value, &ret); + break; + + case V4L2_CID_RED_BALANCE: + cci_write(thp7312->regmap, THP7312_REG_MANUAL_WB_RED_GAIN, + ctrl->val, &ret); + break; + + case V4L2_CID_BLUE_BALANCE: + cci_write(thp7312->regmap, THP7312_REG_MANUAL_WB_BLUE_GAIN, + ctrl->val, &ret); + break; + + case V4L2_CID_AUTO_EXPOSURE_BIAS: + cci_write(thp7312->regmap, THP7312_REG_AE_EXPOSURE_COMPENSATION, + ctrl->val, &ret); + break; + + case V4L2_CID_POWER_LINE_FREQUENCY: + if (ctrl->val == V4L2_CID_POWER_LINE_FREQUENCY_60HZ) { + value = THP7312_AE_FLICKER_MODE_60; + } else if (ctrl->val == V4L2_CID_POWER_LINE_FREQUENCY_50HZ) { + value = THP7312_AE_FLICKER_MODE_50; + } else { + if (thp7312->fw_version == THP7312_FW_VERSION(40, 3)) { + /* THP7312_AE_FLICKER_MODE_DISABLE is not supported */ + value = THP7312_AE_FLICKER_MODE_50; + } else { + value = THP7312_AE_FLICKER_MODE_DISABLE; + } + } + + cci_write(thp7312->regmap, THP7312_REG_AE_FLICKER_MODE, + value, &ret); + break; + + case V4L2_CID_SATURATION: + cci_write(thp7312->regmap, THP7312_REG_SATURATION, + ctrl->val, &ret); + break; + + case V4L2_CID_CONTRAST: + cci_write(thp7312->regmap, THP7312_REG_CONTRAST, + ctrl->val, &ret); + break; + + case V4L2_CID_SHARPNESS: + cci_write(thp7312->regmap, THP7312_REG_SHARPNESS, + ctrl->val, &ret); + break; + + default: + break; + } + + pm_runtime_mark_last_busy(thp7312->dev); + pm_runtime_put_autosuspend(thp7312->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops thp7312_ctrl_ops = { + .s_ctrl = thp7312_s_ctrl, +}; + +/* + * Refer to Documentation/userspace-api/media/drivers/thp7312.rst for details. + */ +static const struct v4l2_ctrl_config thp7312_ctrl_focus_method_cdaf = { + .ops = &thp7312_ctrl_ops, + .id = V4L2_CID_THP7312_AUTO_FOCUS_METHOD, + .name = "Auto-Focus Method", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = THP7312_FOCUS_METHOD_CONTRAST, + .def = THP7312_FOCUS_METHOD_CONTRAST, + .max = THP7312_FOCUS_METHOD_CONTRAST, + .step = 1, +}; + +static const struct v4l2_ctrl_config thp7312_ctrl_focus_method_pdaf = { + .ops = &thp7312_ctrl_ops, + .id = V4L2_CID_THP7312_AUTO_FOCUS_METHOD, + .name = "Auto-Focus Method", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = THP7312_FOCUS_METHOD_CONTRAST, + .def = THP7312_FOCUS_METHOD_HYBRID, + .max = THP7312_FOCUS_METHOD_HYBRID, + .step = 1, +}; + +static const struct v4l2_ctrl_config thp7312_v4l2_ctrls_custom[] = { + { + .ops = &thp7312_ctrl_ops, + .id = V4L2_CID_THP7312_LOW_LIGHT_COMPENSATION, + .name = "Low Light Compensation", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .def = 1, + .max = 1, + .step = 1, + }, { + .ops = &thp7312_ctrl_ops, + .id = V4L2_CID_THP7312_NOISE_REDUCTION_AUTO, + .name = "Noise Reduction Auto", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .def = 1, + .max = 1, + .step = 1, + }, { + .ops = &thp7312_ctrl_ops, + .id = V4L2_CID_THP7312_NOISE_REDUCTION_ABSOLUTE, + .name = "Noise Reduction Level", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .def = 0, + .max = 10, + .step = 1, + }, +}; + +static const s64 exp_bias_qmenu[] = { + -2000, -1667, -1333, -1000, -667, -333, 0, 333, 667, 1000, 1333, 1667, 2000 +}; + +static int thp7312_init_controls(struct thp7312_device *thp7312) +{ + struct v4l2_ctrl_handler *hdl = &thp7312->ctrl_handler; + struct device *dev = thp7312->dev; + struct v4l2_fwnode_device_properties props; + struct v4l2_ctrl *link_freq; + unsigned int num_controls; + unsigned int i; + u8 af_support; + int ret; + + /* + * Check what auto-focus methods the connected sensor supports, if any. + * Firmwares before v90.03 didn't expose the AF_SUPPORT register, + * consider both CDAF and PDAF as supported in that case. + */ + if (thp7312->fw_version >= THP7312_FW_VERSION(90, 3)) { + u64 val; + + ret = cci_read(thp7312->regmap, THP7312_REG_AF_SUPPORT, &val, + NULL); + if (ret) + return ret; + + af_support = val & (THP7312_AF_SUPPORT_PDAF | + THP7312_AF_SUPPORT_CONTRAST); + } else { + af_support = THP7312_AF_SUPPORT_PDAF + | THP7312_AF_SUPPORT_CONTRAST; + } + + num_controls = 14 + ARRAY_SIZE(thp7312_v4l2_ctrls_custom) + + (af_support ? 4 : 0); + + v4l2_ctrl_handler_init(hdl, num_controls); + + if (af_support) { + const struct v4l2_ctrl_config *af_method; + + af_method = af_support & THP7312_AF_SUPPORT_PDAF + ? &thp7312_ctrl_focus_method_pdaf + : &thp7312_ctrl_focus_method_cdaf; + + thp7312->focus_state = THP7312_FOCUS_STATE_MANUAL; + + thp7312->focus_auto = + v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, + V4L2_CID_FOCUS_AUTO, + 0, 1, 1, 1); + thp7312->focus_absolute = + v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, + V4L2_CID_FOCUS_ABSOLUTE, + 0, ARRAY_SIZE(thp7312_focus_values), + 1, 0); + thp7312->focus_method = + v4l2_ctrl_new_custom(hdl, af_method, NULL); + thp7312->focus_start = + v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, + V4L2_CID_AUTO_FOCUS_START, + 1, 1, 1, 1); + + v4l2_ctrl_cluster(4, &thp7312->focus_auto); + } + + v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, V4L2_CID_AUTO_WHITE_BALANCE, + 0, 1, 1, 1); + /* 32: 1x, 255: 7.95x */ + v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, V4L2_CID_RED_BALANCE, + 32, 255, 1, 64); + /* 32: 1x, 255: 7.95x */ + v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, V4L2_CID_BLUE_BALANCE, + 32, 255, 1, 50); + + v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, V4L2_CID_BRIGHTNESS, + -10, 10, 1, 0); + v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, V4L2_CID_SATURATION, + 0, 31, 1, 10); + v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, V4L2_CID_CONTRAST, + 0, 20, 1, 10); + v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, V4L2_CID_SHARPNESS, + 0, 31, 1, 8); + + thp7312->hflip = v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + thp7312->vflip = v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + v4l2_ctrl_cluster(2, &thp7312->hflip); + + v4l2_ctrl_new_int_menu(hdl, &thp7312_ctrl_ops, + V4L2_CID_AUTO_EXPOSURE_BIAS, + ARRAY_SIZE(exp_bias_qmenu) - 1, + ARRAY_SIZE(exp_bias_qmenu) / 2, exp_bias_qmenu); + + v4l2_ctrl_new_std_menu(hdl, &thp7312_ctrl_ops, + V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, + V4L2_CID_POWER_LINE_FREQUENCY_50HZ); + + link_freq = v4l2_ctrl_new_int_menu(hdl, &thp7312_ctrl_ops, + V4L2_CID_LINK_FREQ, 0, 0, + &thp7312->link_freq); + + /* Set properties from fwnode (e.g. rotation, orientation). */ + ret = v4l2_fwnode_device_parse(dev, &props); + if (ret) { + dev_err(dev, "Failed to parse fwnode: %d\n", ret); + goto error; + } + + ret = v4l2_ctrl_new_fwnode_properties(hdl, &thp7312_ctrl_ops, &props); + if (ret) { + dev_err(dev, "Failed to create new v4l2 ctrl for fwnode properties: %d\n", ret); + goto error; + } + + for (i = 0; i < ARRAY_SIZE(thp7312_v4l2_ctrls_custom); i++) { + const struct v4l2_ctrl_config *ctrl_cfg = + &thp7312_v4l2_ctrls_custom[i]; + struct v4l2_ctrl *ctrl; + + ctrl = v4l2_ctrl_new_custom(hdl, ctrl_cfg, NULL); + + if (ctrl_cfg->id == V4L2_CID_THP7312_NOISE_REDUCTION_AUTO) + thp7312->noise_reduction_auto = ctrl; + else if (ctrl_cfg->id == V4L2_CID_THP7312_NOISE_REDUCTION_ABSOLUTE) + thp7312->noise_reduction_absolute = ctrl; + } + + v4l2_ctrl_cluster(2, &thp7312->noise_reduction_auto); + + if (hdl->error) { + dev_err(dev, "v4l2_ctrl_handler error\n"); + ret = hdl->error; + goto error; + } + + link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + return ret; + +error: + v4l2_ctrl_handler_free(hdl); + return ret; +} + +/* ----------------------------------------------------------------------------- + * Firmware Update + */ + +/* + * The firmware data is made of 128kB of RAM firmware, followed by a + * variable-size "header". Both are stored in flash memory. + */ +#define THP7312_FW_RAM_SIZE (128 * 1024) +#define THP7312_FW_MIN_SIZE (THP7312_FW_RAM_SIZE + 4) +#define THP7312_FW_MAX_SIZE (THP7312_FW_RAM_SIZE + 64 * 1024) + +/* + * Data is first uploaded to the THP7312 128kB SRAM, and then written to flash. + * The SRAM is exposed over I2C as 32kB banks, and up to 4kB of data can be + * transferred in a single I2C write. + */ +#define THP7312_RAM_BANK_SIZE (32 * 1024) +#define THP7312_FW_DOWNLOAD_UNIT (4 * 1024) + +#define THP7312_FLASH_MEMORY_ERASE_TIMEOUT 40 + +#define THP7312_FLASH_MAX_REG_READ_SIZE 10 +#define THP7312_FLASH_MAX_REG_DATA_SIZE 10 + +static const u8 thp7312_cmd_config_flash_mem_if[] = { + 0xd5, 0x18, 0x00, 0x00, 0x00, 0x80 +}; + +static const u8 thp7312_cmd_write_to_reg[] = { + 0xd5, 0x0c, 0x80, 0x00, 0x00, 0x00 +}; + +static const u8 thp7312_cmd_read_reg[] = { + 0xd5, 0x04 +}; + +/* + * THP7312 Write data from RAM to Flash Memory + * Command ID FF700F + * Format: FF700F AA AA AA BB BB BB + * AA AA AA: destination start address + * BB BB BB: (write size - 1) + * Source address always starts from 0 + */ +static const u8 thp7312_cmd_write_ram_to_flash[] = { 0xff, 0x70, 0x0f }; + +/* + * THP7312 Calculate CRC command + * Command ID: FF70 09 + * Format: FF70 09 AA AA AA BB BB BB + * AA AA AA: Start address of calculation + * BB BB BB: (calculate size - 1) + */ +static const u8 thp7312_cmd_calc_crc[] = { 0xff, 0x70, 0x09 }; + +static const u8 thp7312_jedec_rdid[] = { SPINOR_OP_RDID, 0x00, 0x00, 0x00 }; +static const u8 thp7312_jedec_rdsr[] = { SPINOR_OP_RDSR, 0x00, 0x00, 0x00 }; +static const u8 thp7312_jedec_wen[] = { SPINOR_OP_WREN }; + +static int thp7312_read_firmware_version(struct thp7312_device *thp7312) +{ + u64 val = 0; + int ret = 0; + u8 major; + u8 minor; + + cci_read(thp7312->regmap, THP7312_REG_FIRMWARE_VERSION_1, &val, &ret); + major = val; + + cci_read(thp7312->regmap, THP7312_REG_FIRMWARE_VERSION_2, &val, &ret); + minor = val; + + thp7312->fw_version = THP7312_FW_VERSION(major, minor); + return ret; +} + +static int thp7312_write_buf(struct thp7312_device *thp7312, + const u8 *write_buf, u16 write_size) +{ + struct i2c_client *client = to_i2c_client(thp7312->dev); + int ret; + + ret = i2c_master_send(client, write_buf, write_size); + return ret >= 0 ? 0 : ret; +} + +static int __thp7312_flash_reg_write(struct thp7312_device *thp7312, + const u8 *write_buf, u16 write_size) +{ + struct device *dev = thp7312->dev; + u8 temp_write_buf[THP7312_FLASH_MAX_REG_DATA_SIZE + 2]; + int ret; + + if (write_size > THP7312_FLASH_MAX_REG_DATA_SIZE) { + dev_err(dev, "%s: Write size error size = %d\n", + __func__, write_size); + return -EINVAL; + } + + ret = thp7312_write_buf(thp7312, thp7312_cmd_config_flash_mem_if, + sizeof(thp7312_cmd_config_flash_mem_if)); + if (ret < 0) { + dev_err(dev, "%s: Failed to config flash memory IF: %d\n", + __func__, ret); + return ret; + } + + temp_write_buf[0] = 0xd5; + temp_write_buf[1] = 0x00; + memcpy((temp_write_buf + 2), write_buf, write_size); + ret = thp7312_write_buf(thp7312, temp_write_buf, write_size + 2); + if (ret < 0) + return ret; + + thp7312_write_buf(thp7312, thp7312_cmd_write_to_reg, + sizeof(thp7312_cmd_write_to_reg)); + + return 0; +} + +static int __thp7312_flash_reg_read(struct thp7312_device *thp7312, + const u8 *write_buf, u16 write_size, + u8 *read_buf, u16 read_size) +{ + struct i2c_client *client = to_i2c_client(thp7312->dev); + struct i2c_msg msgs[2]; + int ret; + + ret = __thp7312_flash_reg_write(thp7312, write_buf, write_size); + if (ret) + return ret; + + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(thp7312_cmd_read_reg), + msgs[0].buf = (u8 *)thp7312_cmd_read_reg; + + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = read_size; + msgs[1].buf = read_buf; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + return ret >= 0 ? 0 : ret; +} + +#define thp7312_flash_reg_write(thp7312, wrbuf) \ + __thp7312_flash_reg_write(thp7312, wrbuf, sizeof(wrbuf)) + +#define thp7312_flash_reg_read(thp7312, wrbuf, rdbuf) \ + __thp7312_flash_reg_read(thp7312, wrbuf, sizeof(wrbuf), \ + rdbuf, sizeof(rdbuf)) + +static enum fw_upload_err thp7312_fw_prepare_config(struct thp7312_device *thp7312) +{ + struct device *dev = thp7312->dev; + int ret; + + ret = cci_write(thp7312->regmap, THP7312_REG_FW_MEMORY_IO_SETTING, + THP7312_FW_MEMORY_IO_GPIO0, NULL); + if (ret) { + dev_err(dev, "Failed to set flash memory I/O\n"); + return FW_UPLOAD_ERR_HW_ERROR; + } + + /* Set max drivability. */ + ret = cci_write(thp7312->regmap, THP7312_REG_FW_DRIVABILITY, 0x00777777, + NULL); + if (ret) { + dev_err(dev, "Failed to set drivability: %d\n", ret); + return FW_UPLOAD_ERR_HW_ERROR; + } + + return FW_UPLOAD_ERR_NONE; +} + +static enum fw_upload_err thp7312_fw_prepare_check(struct thp7312_device *thp7312) +{ + struct device *dev = thp7312->dev; + u8 read_buf[3] = { 0 }; + int ret; + + /* Get JEDEC ID */ + ret = thp7312_flash_reg_read(thp7312, thp7312_jedec_rdid, read_buf); + if (ret) { + dev_err(dev, "Failed to get JEDEC ID: %d\n", ret); + return FW_UPLOAD_ERR_HW_ERROR; + } + + dev_dbg(dev, "Flash Memory: JEDEC ID = 0x%x 0x%x 0x%x\n", + read_buf[0], read_buf[1], read_buf[2]); + + return FW_UPLOAD_ERR_NONE; +} + +static enum fw_upload_err thp7312_fw_prepare_reset(struct thp7312_device *thp7312) +{ + struct device *dev = thp7312->dev; + int ret; + + ret = cci_write(thp7312->regmap, THP7312_REG_FW_RESET_FLASH, 0x81, NULL); + if (ret) { + dev_err(dev, "Failed to reset flash memory: %d\n", ret); + return FW_UPLOAD_ERR_HW_ERROR; + } + + return FW_UPLOAD_ERR_NONE; +} + +/* TODO: Erase only the amount of blocks necessary */ +static enum fw_upload_err thp7312_flash_erase(struct thp7312_device *thp7312) +{ + struct device *dev = thp7312->dev; + u8 read_buf[1] = { 0 }; + unsigned int i; + u8 block; + int ret; + + for (block = 0; block < 3; block++) { + const u8 jedec_se[] = { SPINOR_OP_SE, block, 0x00, 0x00 }; + + ret = thp7312_flash_reg_write(thp7312, thp7312_jedec_wen); + if (ret < 0) { + dev_err(dev, "Failed to enable flash for writing\n"); + return FW_UPLOAD_ERR_RW_ERROR; + } + + ret = thp7312_flash_reg_write(thp7312, jedec_se); + if (ret < 0) { + dev_err(dev, "Failed to erase flash sector\n"); + return FW_UPLOAD_ERR_RW_ERROR; + } + + for (i = 0; i < THP7312_FLASH_MEMORY_ERASE_TIMEOUT; i++) { + usleep_range(100000, 101000); + thp7312_flash_reg_read(thp7312, thp7312_jedec_rdsr, + read_buf); + + /* Check Busy bit. Busy == 0x0 means erase complete. */ + if (!(read_buf[0] & SR_WIP)) + break; + } + + if (i == THP7312_FLASH_MEMORY_ERASE_TIMEOUT) + return FW_UPLOAD_ERR_TIMEOUT; + } + + thp7312_flash_reg_read(thp7312, thp7312_jedec_rdsr, read_buf); + + /* Check WEL bit. */ + if (read_buf[0] & SR_WEL) + return FW_UPLOAD_ERR_HW_ERROR; + + return FW_UPLOAD_ERR_NONE; +} + +static enum fw_upload_err +thp7312_write_download_data_by_unit(struct thp7312_device *thp7312, + unsigned int addr, const u8 *data, + unsigned int size) +{ + struct device *dev = thp7312->dev; + u8 *write_buf = thp7312->fw_write_buf; + int ret; + + dev_dbg(dev, "%s: addr = 0x%04x, data = 0x%p, size = %u\n", + __func__, addr, data, size); + + write_buf[0] = (addr >> 8) & 0xff; + write_buf[1] = (addr >> 0) & 0xff; + memcpy(&write_buf[2], data, size); + + /* + * THP7312 Firmware download to RAM + * Command ID (address to download): 0x0000 - 0x7fff + * Format:: 0000 XX XX XX ........ XX + */ + ret = thp7312_write_buf(thp7312, write_buf, size + 2); + if (ret < 0) + dev_err(dev, "Unit transfer ERROR %s(): ret = %d\n", __func__, ret); + + return ret >= 0 ? FW_UPLOAD_ERR_NONE : FW_UPLOAD_ERR_RW_ERROR; +} + +static enum fw_upload_err thp7312_fw_load_to_ram(struct thp7312_device *thp7312, + const u8 *data, u32 size) +{ + struct device *dev = thp7312->dev; + enum fw_upload_err ret; + unsigned int num_banks; + unsigned int i, j; + + num_banks = DIV_ROUND_UP(size, THP7312_RAM_BANK_SIZE); + + dev_dbg(dev, "%s: loading %u bytes in SRAM (%u banks)\n", __func__, + size, num_banks); + + for (i = 0; i < num_banks; i++) { + const u32 bank_addr = 0x10000000 | (i * THP7312_RAM_BANK_SIZE); + unsigned int bank_size; + unsigned int num_chunks; + + ret = cci_write(thp7312->regmap, THP7312_REG_FW_DEST_BANK_ADDR, + bank_addr, NULL); + if (ret) + return FW_UPLOAD_ERR_HW_ERROR; + + bank_size = min_t(u32, size, THP7312_RAM_BANK_SIZE); + num_chunks = DIV_ROUND_UP(bank_size, THP7312_FW_DOWNLOAD_UNIT); + + dev_dbg(dev, "%s: loading %u bytes in SRAM bank %u (%u chunks)\n", + __func__, bank_size, i, num_chunks); + + for (j = 0 ; j < num_chunks; j++) { + unsigned int chunk_addr; + unsigned int chunk_size; + + chunk_addr = j * THP7312_FW_DOWNLOAD_UNIT; + chunk_size = min_t(u32, size, THP7312_FW_DOWNLOAD_UNIT); + + ret = thp7312_write_download_data_by_unit(thp7312, chunk_addr, + data, chunk_size); + if (ret != FW_UPLOAD_ERR_NONE) { + dev_err(dev, "Unit transfer ERROR at bank transfer %s(): %d\n", + __func__, j); + return ret; + } + + data += chunk_size; + size -= chunk_size; + } + } + + return FW_UPLOAD_ERR_NONE; +} + +static enum fw_upload_err thp7312_fw_write_to_flash(struct thp7312_device *thp7312, + u32 dest, u32 write_size) +{ + u8 command[sizeof(thp7312_cmd_write_ram_to_flash) + 6]; + static const u32 cmd_size = sizeof(thp7312_cmd_write_ram_to_flash); + u64 val; + int ret; + + memcpy(command, thp7312_cmd_write_ram_to_flash, cmd_size); + + command[cmd_size] = (dest & 0xff0000) >> 16; + command[cmd_size + 1] = (dest & 0x00ff00) >> 8; + command[cmd_size + 2] = (dest & 0x0000ff); + command[cmd_size + 3] = ((write_size - 1) & 0xff0000) >> 16; + command[cmd_size + 4] = ((write_size - 1) & 0x00ff00) >> 8; + command[cmd_size + 5] = ((write_size - 1) & 0x0000ff); + + ret = thp7312_write_buf(thp7312, command, sizeof(command)); + if (ret < 0) + return FW_UPLOAD_ERR_RW_ERROR; + + usleep_range(8000000, 8100000); + + ret = cci_read(thp7312->regmap, THP7312_REG_FW_VERIFY_RESULT, &val, + NULL); + if (ret < 0) + return FW_UPLOAD_ERR_RW_ERROR; + + return val ? FW_UPLOAD_ERR_HW_ERROR : FW_UPLOAD_ERR_NONE; +} + +static enum fw_upload_err thp7312_fw_check_crc(struct thp7312_device *thp7312, + const u8 *fw_data, u32 fw_size) +{ + struct device *dev = thp7312->dev; + u16 header_size = fw_size - THP7312_FW_RAM_SIZE; + u8 command[sizeof(thp7312_cmd_calc_crc) + 6]; + static const u32 cmd_size = sizeof(thp7312_cmd_calc_crc); + u32 size = THP7312_FW_RAM_SIZE - 4; + u32 fw_crc; + u64 crc; + int ret; + + memcpy(command, thp7312_cmd_calc_crc, cmd_size); + + command[cmd_size] = 0; + command[cmd_size + 1] = (header_size >> 8) & 0xff; + command[cmd_size + 2] = header_size & 0xff; + + command[cmd_size + 3] = (size >> 16) & 0xff; + command[cmd_size + 4] = (size >> 8) & 0xff; + command[cmd_size + 5] = size & 0xff; + + ret = thp7312_write_buf(thp7312, command, sizeof(command)); + if (ret < 0) + return FW_UPLOAD_ERR_RW_ERROR; + + usleep_range(2000000, 2100000); + + fw_crc = get_unaligned_be32(&fw_data[fw_size - 4]); + + ret = cci_read(thp7312->regmap, THP7312_REG_FW_CRC_RESULT, &crc, NULL); + if (ret < 0) + return FW_UPLOAD_ERR_RW_ERROR; + + if (fw_crc != crc) { + dev_err(dev, "CRC mismatch: firmware 0x%08x, flash 0x%08llx\n", + fw_crc, crc); + return FW_UPLOAD_ERR_HW_ERROR; + } + + return FW_UPLOAD_ERR_NONE; +} + +static enum fw_upload_err thp7312_fw_prepare(struct fw_upload *fw_upload, + const u8 *data, u32 size) +{ + struct thp7312_device *thp7312 = fw_upload->dd_handle; + struct device *dev = thp7312->dev; + enum fw_upload_err ret; + + mutex_lock(&thp7312->fw_lock); + thp7312->fw_cancel = false; + mutex_unlock(&thp7312->fw_lock); + + if (size < THP7312_FW_MIN_SIZE || size > THP7312_FW_MAX_SIZE) { + dev_err(dev, "%s: Invalid firmware size %d; must be between %d and %d\n", + __func__, size, THP7312_FW_MIN_SIZE, THP7312_FW_MAX_SIZE); + return FW_UPLOAD_ERR_INVALID_SIZE; + } + + ret = thp7312_fw_prepare_config(thp7312); + if (ret != FW_UPLOAD_ERR_NONE) + return ret; + + ret = thp7312_fw_prepare_check(thp7312); + if (ret != FW_UPLOAD_ERR_NONE) + return ret; + + ret = thp7312_fw_prepare_reset(thp7312); + if (ret != FW_UPLOAD_ERR_NONE) + return ret; + + mutex_lock(&thp7312->fw_lock); + ret = thp7312->fw_cancel ? FW_UPLOAD_ERR_CANCELED : FW_UPLOAD_ERR_NONE; + mutex_unlock(&thp7312->fw_lock); + + return ret; +} + +static enum fw_upload_err thp7312_fw_write(struct fw_upload *fw_upload, + const u8 *data, u32 offset, + u32 size, u32 *written) +{ + struct thp7312_device *thp7312 = fw_upload->dd_handle; + struct device *dev = thp7312->dev; + u16 header_size = size - THP7312_FW_RAM_SIZE; + enum fw_upload_err ret; + bool cancel; + + mutex_lock(&thp7312->fw_lock); + cancel = thp7312->fw_cancel; + mutex_unlock(&thp7312->fw_lock); + + if (cancel) + return FW_UPLOAD_ERR_CANCELED; + + ret = thp7312_flash_erase(thp7312); + if (ret != FW_UPLOAD_ERR_NONE) + return ret; + + ret = thp7312_fw_load_to_ram(thp7312, data, THP7312_FW_RAM_SIZE); + if (ret != FW_UPLOAD_ERR_NONE) + return ret; + + ret = thp7312_fw_write_to_flash(thp7312, 0, 0x1ffff); + if (ret != FW_UPLOAD_ERR_NONE) + return ret; + + ret = thp7312_fw_load_to_ram(thp7312, data + THP7312_FW_RAM_SIZE, header_size); + if (ret != FW_UPLOAD_ERR_NONE) + return ret; + + ret = thp7312_fw_write_to_flash(thp7312, 0x20000, header_size - 1); + if (ret != FW_UPLOAD_ERR_NONE) + return ret; + + ret = thp7312_fw_check_crc(thp7312, data, size); + if (ret != FW_UPLOAD_ERR_NONE) + return ret; + + dev_info(dev, "Successfully wrote firmware\n"); + + *written = size; + return FW_UPLOAD_ERR_NONE; +} + +static enum fw_upload_err thp7312_fw_poll_complete(struct fw_upload *fw_upload) +{ + return FW_UPLOAD_ERR_NONE; +} + +/* + * This may be called asynchronously with an on-going update. All other + * functions are called sequentially in a single thread. To avoid contention on + * register accesses, only update the cancel_request flag. Other functions will + * check this flag and handle the cancel request synchronously. + */ +static void thp7312_fw_cancel(struct fw_upload *fw_upload) +{ + struct thp7312_device *thp7312 = fw_upload->dd_handle; + + mutex_lock(&thp7312->fw_lock); + thp7312->fw_cancel = true; + mutex_unlock(&thp7312->fw_lock); +} + +static const struct fw_upload_ops thp7312_fw_upload_ops = { + .prepare = thp7312_fw_prepare, + .write = thp7312_fw_write, + .poll_complete = thp7312_fw_poll_complete, + .cancel = thp7312_fw_cancel, +}; + +static int thp7312_register_flash_mode(struct thp7312_device *thp7312) +{ + struct device *dev = thp7312->dev; + struct fw_upload *fwl; + u64 val; + int ret; + + dev_info(dev, "booted in flash mode\n"); + + mutex_init(&thp7312->fw_lock); + + thp7312->fw_write_buf = devm_kzalloc(dev, THP7312_FW_DOWNLOAD_UNIT + 2, + GFP_KERNEL); + if (!thp7312->fw_write_buf) + return -ENOMEM; + + ret = __thp7312_power_on(thp7312); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to power on\n"); + + ret = cci_read(thp7312->regmap, THP7312_REG_FW_STATUS, &val, NULL); + if (ret) { + dev_err_probe(dev, ret, "Camera status read failed\n"); + goto error; + } + + fwl = firmware_upload_register(THIS_MODULE, dev, "thp7312-firmware", + &thp7312_fw_upload_ops, thp7312); + if (IS_ERR(fwl)) { + ret = PTR_ERR(fwl); + dev_err_probe(dev, ret, "Failed to register firmware upload\n"); + goto error; + } + + thp7312->fwl = fwl; + return 0; + +error: + __thp7312_power_off(thp7312); + return ret; +} + +/* ----------------------------------------------------------------------------- + * Probe & Remove + */ + +static int thp7312_get_regulators(struct thp7312_device *thp7312) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(thp7312->supplies); i++) + thp7312->supplies[i].supply = thp7312_supply_name[i]; + + return devm_regulator_bulk_get(thp7312->dev, + ARRAY_SIZE(thp7312->supplies), + thp7312->supplies); +} + +static int thp7312_sensor_parse_dt(struct thp7312_device *thp7312, + struct fwnode_handle *node) +{ + struct device *dev = thp7312->dev; + struct thp7312_sensor *sensor; + const char *model; + u8 data_lanes[4]; + u32 values[4]; + unsigned int i; + u32 reg; + int ret; + + /* Retrieve the sensor index from the reg property. */ + ret = fwnode_property_read_u32(node, "reg", ®); + if (ret < 0) { + dev_err(dev, "'reg' property missing in sensor node\n"); + return -EINVAL; + } + + if (reg >= ARRAY_SIZE(thp7312->sensors)) { + dev_err(dev, "Out-of-bounds 'reg' value %u\n", reg); + return -EINVAL; + } + + sensor = &thp7312->sensors[reg]; + if (sensor->info) { + dev_err(dev, "Duplicate entry for sensor %u\n", reg); + return -EINVAL; + } + + ret = fwnode_property_read_string(node, "thine,model", &model); + if (ret < 0) { + dev_err(dev, "'thine,model' property missing in sensor node\n"); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(thp7312_sensor_info); i++) { + const struct thp7312_sensor_info *info = + &thp7312_sensor_info[i]; + + if (!strcmp(info->model, model)) { + sensor->info = info; + break; + } + } + + if (!sensor->info) { + dev_err(dev, "Unsupported sensor model %s\n", model); + return -EINVAL; + } + + ret = fwnode_property_read_u32_array(node, "data-lanes", values, + ARRAY_SIZE(values)); + if (ret < 0) { + dev_err(dev, "Failed to read property data-lanes: %d\n", ret); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(data_lanes); ++i) + data_lanes[i] = values[i]; + + ret = thp7312_map_data_lanes(&sensor->lane_remap, data_lanes, + ARRAY_SIZE(data_lanes)); + if (ret) { + dev_err(dev, "Invalid sensor@%u data-lanes value\n", reg); + return ret; + } + + return 0; +} + +static int thp7312_parse_dt(struct thp7312_device *thp7312) +{ + struct v4l2_fwnode_endpoint ep = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + struct device *dev = thp7312->dev; + struct fwnode_handle *endpoint; + struct fwnode_handle *sensors; + unsigned int num_sensors = 0; + struct fwnode_handle *node; + int ret; + + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); + if (!endpoint) + return dev_err_probe(dev, -EINVAL, "Endpoint node not found\n"); + + ret = v4l2_fwnode_endpoint_parse(endpoint, &ep); + fwnode_handle_put(endpoint); + if (ret) + return dev_err_probe(dev, ret, "Could not parse endpoint\n"); + + ret = thp7312_map_data_lanes(&thp7312->lane_remap, + ep.bus.mipi_csi2.data_lanes, + ep.bus.mipi_csi2.num_data_lanes); + if (ret) { + dev_err(dev, "Invalid data-lanes value\n"); + return ret; + } + + /* + * The thine,boot-mode property is optional and default to + * THP7312_BOOT_MODE_SPI_MASTER (1). + */ + thp7312->boot_mode = THP7312_BOOT_MODE_SPI_MASTER; + ret = device_property_read_u32(dev, "thine,boot-mode", + &thp7312->boot_mode); + if (ret && ret != -EINVAL) + return dev_err_probe(dev, ret, "Property '%s' is invalid\n", + "thine,boot-mode"); + + if (thp7312->boot_mode != THP7312_BOOT_MODE_2WIRE_SLAVE && + thp7312->boot_mode != THP7312_BOOT_MODE_SPI_MASTER) + return dev_err_probe(dev, -EINVAL, "Invalid '%s' value %u\n", + "thine,boot-mode", thp7312->boot_mode); + + /* Sensors */ + sensors = device_get_named_child_node(dev, "sensors"); + if (!sensors) { + dev_err(dev, "'sensors' child node not found\n"); + return -EINVAL; + } + + fwnode_for_each_available_child_node(sensors, node) { + if (fwnode_name_eq(node, "sensor")) { + if (!thp7312_sensor_parse_dt(thp7312, node)) + num_sensors++; + } + } + + fwnode_handle_put(sensors); + + if (!num_sensors) { + dev_err(dev, "No sensor found\n"); + return -EINVAL; + } + + return 0; +} + +static int thp7312_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct v4l2_subdev_state *sd_state; + struct thp7312_device *thp7312; + int ret; + + thp7312 = devm_kzalloc(dev, sizeof(*thp7312), GFP_KERNEL); + if (!thp7312) + return -ENOMEM; + + thp7312->dev = dev; + + thp7312->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(thp7312->regmap)) + return dev_err_probe(dev, PTR_ERR(thp7312->regmap), + "Unable to initialize I2C\n"); + + ret = thp7312_parse_dt(thp7312); + if (ret < 0) + return ret; + + ret = thp7312_get_regulators(thp7312); + if (ret) + return dev_err_probe(dev, ret, "Failed to get regulators\n"); + + thp7312->iclk = devm_clk_get(dev, NULL); + if (IS_ERR(thp7312->iclk)) + return dev_err_probe(dev, PTR_ERR(thp7312->iclk), + "Failed to get iclk\n"); + + thp7312->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(thp7312->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(thp7312->reset_gpio), + "Failed to get reset gpio\n"); + + if (thp7312->boot_mode == THP7312_BOOT_MODE_2WIRE_SLAVE) + return thp7312_register_flash_mode(thp7312); + + v4l2_i2c_subdev_init(&thp7312->sd, client, &thp7312_subdev_ops); + thp7312->sd.internal_ops = &thp7312_internal_ops; + thp7312->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; + thp7312->pad.flags = MEDIA_PAD_FL_SOURCE; + thp7312->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + ret = media_entity_pads_init(&thp7312->sd.entity, 1, &thp7312->pad); + if (ret) + return ret; + + /* + * Enable power management. The driver supports runtime PM, but needs to + * work when runtime PM is disabled in the kernel. To that end, power + * the device manually here. + */ + ret = thp7312_power_on(thp7312); + if (ret) + goto err_entity_cleanup; + + ret = thp7312_read_firmware_version(thp7312); + if (ret < 0) { + dev_err(dev, "Camera is not found\n"); + goto err_power_off; + } + + ret = thp7312_init_controls(thp7312); + if (ret) { + dev_err(dev, "Failed to initialize controls\n"); + goto err_power_off; + } + + thp7312->sd.ctrl_handler = &thp7312->ctrl_handler; + thp7312->sd.state_lock = thp7312->ctrl_handler.lock; + + ret = v4l2_subdev_init_finalize(&thp7312->sd); + if (ret < 0) { + dev_err(dev, "Subdev active state initialization failed\n"); + goto err_free_ctrls; + } + + sd_state = v4l2_subdev_lock_and_get_active_state(&thp7312->sd); + thp7312->current_mode = &thp7312_mode_info_data[0]; + thp7312_set_frame_rate(thp7312, &thp7312->current_mode->rates[0]); + v4l2_subdev_unlock_state(sd_state); + + /* + * Enable runtime PM with autosuspend. As the device has been powered + * manually, mark it as active, and increase the usage count without + * resuming the device. + */ + pm_runtime_set_active(dev); + pm_runtime_get_noresume(dev); + pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_use_autosuspend(dev); + + ret = v4l2_async_register_subdev(&thp7312->sd); + if (ret < 0) { + dev_err(dev, "Subdev registration failed\n"); + goto err_pm; + } + + /* + * Decrease the PM usage count. The device will get suspended after the + * autosuspend delay, turning the power off. + */ + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + dev_info(dev, "THP7312 firmware version %02u.%02u\n", + THP7312_FW_VERSION_MAJOR(thp7312->fw_version), + THP7312_FW_VERSION_MINOR(thp7312->fw_version)); + + return 0; + +err_pm: + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); + v4l2_subdev_cleanup(&thp7312->sd); +err_free_ctrls: + v4l2_ctrl_handler_free(&thp7312->ctrl_handler); +err_power_off: + thp7312_power_off(thp7312); +err_entity_cleanup: + media_entity_cleanup(&thp7312->sd.entity); + return ret; +} + +static void thp7312_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct thp7312_device *thp7312 = to_thp7312_dev(sd); + + if (thp7312->boot_mode == THP7312_BOOT_MODE_2WIRE_SLAVE) { + firmware_upload_unregister(thp7312->fwl); + __thp7312_power_off(thp7312); + return; + } + + v4l2_async_unregister_subdev(&thp7312->sd); + v4l2_subdev_cleanup(&thp7312->sd); + media_entity_cleanup(&thp7312->sd.entity); + v4l2_ctrl_handler_free(&thp7312->ctrl_handler); + + /* + * Disable runtime PM. In case runtime PM is disabled in the kernel, + * make sure to turn power off manually. + */ + pm_runtime_disable(thp7312->dev); + if (!pm_runtime_status_suspended(thp7312->dev)) + thp7312_power_off(thp7312); + pm_runtime_set_suspended(thp7312->dev); +} + +static const struct of_device_id thp7312_dt_ids[] = { + { .compatible = "thine,thp7312" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, thp7312_dt_ids); + +static struct i2c_driver thp7312_i2c_driver = { + .driver = { + .name = "thp7312", + .pm = &thp7312_pm_ops, + .of_match_table = thp7312_dt_ids, + }, + .probe = thp7312_probe, + .remove = thp7312_remove, +}; + +module_i2c_driver(thp7312_i2c_driver); + +MODULE_DESCRIPTION("THP7312 MIPI Camera Subdev Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index e543b3f7a4d89b..9fc586cfdcd874 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -1035,7 +1035,7 @@ tvp5150_get_pad_crop(struct tvp5150 *decoder, return &decoder->rect; case V4L2_SUBDEV_FORMAT_TRY: #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) - return v4l2_subdev_get_try_crop(&decoder->sd, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); #else return ERR_PTR(-EINVAL); #endif @@ -1209,8 +1209,8 @@ static int tvp5150_get_mbus_config(struct v4l2_subdev *sd, /**************************************************************************** V4L2 subdev pad ops ****************************************************************************/ -static int tvp5150_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int tvp5150_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct tvp5150 *decoder = to_tvp5150(sd); v4l2_std_id std; @@ -1722,7 +1722,6 @@ static const struct v4l2_subdev_vbi_ops tvp5150_vbi_ops = { }; static const struct v4l2_subdev_pad_ops tvp5150_pad_ops = { - .init_cfg = tvp5150_init_cfg, .enum_mbus_code = tvp5150_enum_mbus_code, .enum_frame_size = tvp5150_enum_frame_size, .set_fmt = tvp5150_fill_fmt, @@ -1741,6 +1740,7 @@ static const struct v4l2_subdev_ops tvp5150_ops = { }; static const struct v4l2_subdev_internal_ops tvp5150_internal_ops = { + .init_state = tvp5150_init_state, .registered = tvp5150_registered, .open = tvp5150_open, .close = tvp5150_close, diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c index a2d7bc79984930..30831b4b56d6b1 100644 --- a/drivers/media/i2c/tvp7002.c +++ b/drivers/media/i2c/tvp7002.c @@ -791,7 +791,7 @@ static const struct v4l2_ctrl_ops tvp7002_ctrl_ops = { /* * tvp7002_enum_mbus_code() - Enum supported digital video format on pad * @sd: pointer to standard V4L2 sub-device structure - * @cfg: pad configuration + * @sd_state: V4L2 subdev state * @code: pointer to subdev enum mbus code struct * * Enumerate supported digital video formats for pad. @@ -813,7 +813,7 @@ tvp7002_enum_mbus_code(struct v4l2_subdev *sd, /* * tvp7002_get_pad_format() - get video format on pad * @sd: pointer to standard V4L2 sub-device structure - * @cfg: pad configuration + * @sd_state: V4L2 subdev state * @fmt: pointer to subdev format struct * * get video format for pad. @@ -837,7 +837,7 @@ tvp7002_get_pad_format(struct v4l2_subdev *sd, /* * tvp7002_set_pad_format() - set video format on pad * @sd: pointer to standard V4L2 sub-device structure - * @cfg: pad configuration + * @sd_state: V4L2 subdev state * @fmt: pointer to subdev format struct * * set video format for pad. diff --git a/drivers/media/i2c/tw9910.c b/drivers/media/i2c/tw9910.c index 477a64d8f8ab6f..905af98c7d53cd 100644 --- a/drivers/media/i2c/tw9910.c +++ b/drivers/media/i2c/tw9910.c @@ -829,8 +829,6 @@ static int tw9910_set_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) return tw9910_s_fmt(sd, mf); - sd_state->pads->try_fmt = *mf; - return 0; } diff --git a/drivers/media/mc/mc-entity.c b/drivers/media/mc/mc-entity.c index 951b79ca125cdd..db048423a33636 100644 --- a/drivers/media/mc/mc-entity.c +++ b/drivers/media/mc/mc-entity.c @@ -197,6 +197,7 @@ int media_entity_pads_init(struct media_entity *entity, u16 num_pads, struct media_device *mdev = entity->graph_obj.mdev; struct media_pad *iter; unsigned int i = 0; + int ret = 0; if (num_pads >= MEDIA_ENTITY_MAX_PADS) return -E2BIG; @@ -208,17 +209,34 @@ int media_entity_pads_init(struct media_entity *entity, u16 num_pads, mutex_lock(&mdev->graph_mutex); media_entity_for_each_pad(entity, iter) { + const u32 pad_flags = iter->flags & (MEDIA_PAD_FL_SINK | + MEDIA_PAD_FL_SOURCE | + MEDIA_PAD_FL_INTERNAL); + iter->entity = entity; iter->index = i++; + + if (pad_flags != MEDIA_PAD_FL_SINK && + pad_flags != (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_INTERNAL) && + pad_flags != MEDIA_PAD_FL_SOURCE) { + ret = -EINVAL; + break; + } + if (mdev) media_gobj_create(mdev, MEDIA_GRAPH_PAD, &iter->graph_obj); } + if (ret && mdev) { + media_entity_for_each_pad(entity, iter) + media_gobj_destroy(&iter->graph_obj); + } + if (mdev) mutex_unlock(&mdev->graph_mutex); - return 0; + return ret; } EXPORT_SYMBOL_GPL(media_entity_pads_init); @@ -1105,7 +1123,8 @@ int media_get_pad_index(struct media_entity *entity, u32 pad_type, for (i = 0; i < entity->num_pads; i++) { if ((entity->pads[i].flags & - (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_SOURCE)) != pad_type) + (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_SOURCE | + MEDIA_PAD_FL_INTERNAL)) != pad_type) continue; if (entity->pads[i].sig_type == sig_type) @@ -1135,6 +1154,9 @@ media_create_pad_link(struct media_entity *source, u16 source_pad, return -EINVAL; if (WARN_ON(!(sink->pads[sink_pad].flags & MEDIA_PAD_FL_SINK))) return -EINVAL; + if (WARN_ON(source->pads[source_pad].flags & MEDIA_PAD_FL_INTERNAL) || + WARN_ON(sink->pads[sink_pad].flags & MEDIA_PAD_FL_INTERNAL)) + return -EINVAL; link = media_add_link(&source->links); if (link == NULL) diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c index 423842d2a5b2b2..0bc508ad38dfc7 100644 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c @@ -1205,11 +1205,11 @@ static int cio2_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) }; /* Initialize try_fmt */ - format = v4l2_subdev_get_try_format(sd, fh->state, CIO2_PAD_SINK); + format = v4l2_subdev_state_get_format(fh->state, CIO2_PAD_SINK); *format = fmt_default; /* same as sink */ - format = v4l2_subdev_get_try_format(sd, fh->state, CIO2_PAD_SOURCE); + format = v4l2_subdev_state_get_format(fh->state, CIO2_PAD_SOURCE); *format = fmt_default; return 0; @@ -1231,8 +1231,8 @@ static int cio2_subdev_get_fmt(struct v4l2_subdev *sd, mutex_lock(&q->subdev_lock); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); else fmt->format = q->subdev_fmt; @@ -1265,7 +1265,7 @@ static int cio2_subdev_set_fmt(struct v4l2_subdev *sd, return cio2_subdev_get_fmt(sd, sd_state, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - mbus = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mbus = v4l2_subdev_state_get_format(sd_state, fmt->pad); else mbus = &q->subdev_fmt; diff --git a/drivers/media/pci/intel/ivsc/mei_csi.c b/drivers/media/pci/intel/ivsc/mei_csi.c index 685b2ec96071a4..8ce21bc8831f0f 100644 --- a/drivers/media/pci/intel/ivsc/mei_csi.c +++ b/drivers/media/pci/intel/ivsc/mei_csi.c @@ -345,7 +345,7 @@ mei_csi_get_pad_format(struct v4l2_subdev *sd, switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &csi->format_mbus[pad]; default: @@ -353,8 +353,8 @@ mei_csi_get_pad_format(struct v4l2_subdev *sd, } } -static int mei_csi_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int mei_csi_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *mbusformat; struct mei_csi *csi = sd_to_csi(sd); @@ -363,7 +363,7 @@ static int mei_csi_init_cfg(struct v4l2_subdev *sd, mutex_lock(&csi->lock); for (i = 0; i < sd->entity.num_pads; i++) { - mbusformat = v4l2_subdev_get_try_format(sd, sd_state, i); + mbusformat = v4l2_subdev_state_get_format(sd_state, i); *mbusformat = mei_csi_format_mbus_default; } @@ -561,7 +561,6 @@ static const struct v4l2_subdev_video_ops mei_csi_video_ops = { }; static const struct v4l2_subdev_pad_ops mei_csi_pad_ops = { - .init_cfg = mei_csi_init_cfg, .get_fmt = mei_csi_get_fmt, .set_fmt = mei_csi_set_fmt, }; @@ -571,6 +570,10 @@ static const struct v4l2_subdev_ops mei_csi_subdev_ops = { .pad = &mei_csi_pad_ops, }; +static const struct v4l2_subdev_internal_ops mei_csi_internal_ops = { + .init_state = mei_csi_init_state, +}; + static const struct media_entity_operations mei_csi_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -737,6 +740,7 @@ static int mei_csi_probe(struct mei_cl_device *cldev, csi->subdev.dev = &cldev->dev; v4l2_subdev_init(&csi->subdev, &mei_csi_subdev_ops); + csi->subdev.internal_ops = &mei_csi_internal_ops; v4l2_set_subdevdata(&csi->subdev, csi); csi->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 8f9acc325cc8a2..31152a89cafac9 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -67,7 +67,7 @@ source "drivers/media/platform/amlogic/Kconfig" source "drivers/media/platform/amphion/Kconfig" source "drivers/media/platform/aspeed/Kconfig" source "drivers/media/platform/atmel/Kconfig" -source "drivers/media/platform/bcm2835/Kconfig" +source "drivers/media/platform/broadcom/Kconfig" source "drivers/media/platform/cadence/Kconfig" source "drivers/media/platform/chips-media/Kconfig" source "drivers/media/platform/intel/Kconfig" diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index eab239b4466a9b..48dba05e890f68 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -10,7 +10,7 @@ obj-y += amlogic/ obj-y += amphion/ obj-y += aspeed/ obj-y += atmel/ -obj-y += bcm2835/ +obj-y += broadcom/ obj-y += cadence/ obj-y += chips-media/ obj-y += intel/ diff --git a/drivers/media/platform/atmel/atmel-isi.c b/drivers/media/platform/atmel/atmel-isi.c index 4046212d48b415..da58f33b6b0af4 100644 --- a/drivers/media/platform/atmel/atmel-isi.c +++ b/drivers/media/platform/atmel/atmel-isi.c @@ -559,11 +559,13 @@ static const struct isi_format *find_format_by_fourcc(struct atmel_isi *isi, static void isi_try_fse(struct atmel_isi *isi, const struct isi_format *isi_fmt, struct v4l2_subdev_state *sd_state) { - int ret; + struct v4l2_rect *try_crop = + v4l2_subdev_state_get_crop(sd_state, 0); struct v4l2_subdev_frame_size_enum fse = { .code = isi_fmt->mbus_code, .which = V4L2_SUBDEV_FORMAT_TRY, }; + int ret; ret = v4l2_subdev_call(isi->entity.subdev, pad, enum_frame_size, sd_state, &fse); @@ -572,11 +574,11 @@ static void isi_try_fse(struct atmel_isi *isi, const struct isi_format *isi_fmt, * just use the maximum ISI can receive. */ if (ret) { - sd_state->pads->try_crop.width = MAX_SUPPORT_WIDTH; - sd_state->pads->try_crop.height = MAX_SUPPORT_HEIGHT; + try_crop->width = MAX_SUPPORT_WIDTH; + try_crop->height = MAX_SUPPORT_HEIGHT; } else { - sd_state->pads->try_crop.width = fse.max_width; - sd_state->pads->try_crop.height = fse.max_height; + try_crop->width = fse.max_width; + try_crop->height = fse.max_height; } } diff --git a/drivers/media/platform/bcm2835/bcm2835-unicam.c b/drivers/media/platform/bcm2835/bcm2835-unicam.c deleted file mode 100644 index a6ad0c0fbef64a..00000000000000 --- a/drivers/media/platform/bcm2835/bcm2835-unicam.c +++ /dev/null @@ -1,3528 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * BCM283x / BCM271x Unicam Capture Driver - * - * Copyright (C) 2017-2020 - Raspberry Pi (Trading) Ltd. - * - * Dave Stevenson - * - * Based on TI am437x driver by - * Benoit Parrot - * Lad, Prabhakar - * - * and TI CAL camera interface driver by - * Benoit Parrot - * - * - * There are two camera drivers in the kernel for BCM283x - this one - * and bcm2835-camera (currently in staging). - * - * This driver directly controls the Unicam peripheral - there is no - * involvement with the VideoCore firmware. Unicam receives CSI-2 or - * CCP2 data and writes it into SDRAM. - * The only potential processing options are to repack Bayer data into an - * alternate format, and applying windowing. - * The repacking does not shift the data, so can repack V4L2_PIX_FMT_Sxxxx10P - * to V4L2_PIX_FMT_Sxxxx10, or V4L2_PIX_FMT_Sxxxx12P to V4L2_PIX_FMT_Sxxxx12, - * but not generically up to V4L2_PIX_FMT_Sxxxx16. The driver will add both - * formats where the relevant formats are defined, and will automatically - * configure the repacking as required. - * Support for windowing may be added later. - * - * It should be possible to connect this driver to any sensor with a - * suitable output interface and V4L2 subdevice driver. - * - * bcm2835-camera uses the VideoCore firmware to control the sensor, - * Unicam, ISP, and all tuner control loops. Fully processed frames are - * delivered to the driver by the firmware. It only has sensor drivers - * for Omnivision OV5647, and Sony IMX219 sensors. - * - * The two drivers are mutually exclusive for the same Unicam instance. - * The VideoCore firmware checks the device tree configuration during boot. - * If it finds device tree nodes called csi0 or csi1 it will block the - * firmware from accessing the peripheral, and bcm2835-camera will - * not be able to stream data. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "vc4-regs-unicam.h" - -#define UNICAM_MODULE_NAME "unicam" -#define UNICAM_VERSION "0.1.0" - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Debug level 0-3"); - -static int media_controller; -module_param(media_controller, int, 0644); -MODULE_PARM_DESC(media_controller, "Use media controller API"); - -#define unicam_dbg(level, dev, fmt, arg...) \ - v4l2_dbg(level, debug, &(dev)->v4l2_dev, fmt, ##arg) -#define unicam_info(dev, fmt, arg...) \ - v4l2_info(&(dev)->v4l2_dev, fmt, ##arg) -#define unicam_err(dev, fmt, arg...) \ - v4l2_err(&(dev)->v4l2_dev, fmt, ##arg) - -/* - * Unicam must request a minimum of 250Mhz from the VPU clock. - * Otherwise the input FIFOs overrun and cause image corruption. - */ -#define MIN_VPU_CLOCK_RATE (250 * 1000 * 1000) -/* - * To protect against a dodgy sensor driver never returning an error from - * enum_mbus_code, set a maximum index value to be used. - */ -#define MAX_ENUM_MBUS_CODE 128 - -/* - * Stride is a 16 bit register, but also has to be a multiple of 32. - */ -#define BPL_ALIGNMENT 32 -#define MAX_BYTESPERLINE ((1 << 16) - BPL_ALIGNMENT) -/* - * Max width is therefore determined by the max stride divided by - * the number of bits per pixel. Take 32bpp as a - * worst case. - * No imposed limit on the height, so adopt a square image for want - * of anything better. - */ -#define MAX_WIDTH (MAX_BYTESPERLINE / 4) -#define MAX_HEIGHT MAX_WIDTH -/* Define a nominal minimum image size */ -#define MIN_WIDTH 16 -#define MIN_HEIGHT 16 -/* Default size of the embedded buffer */ -#define UNICAM_EMBEDDED_SIZE 16384 - -/* - * Size of the dummy buffer allocation. - * - * Due to a HW bug causing buffer overruns in circular buffer mode under certain - * (not yet fully known) conditions, the dummy buffer allocation is set to a - * a single page size, but the hardware gets programmed with a buffer size of 0. - */ -#define DUMMY_BUF_SIZE (PAGE_SIZE) - -enum pad_types { - IMAGE_PAD, - METADATA_PAD, - MAX_NODES -}; - -#define MASK_CS_DEFAULT BIT(V4L2_COLORSPACE_DEFAULT) -#define MASK_CS_SMPTE170M BIT(V4L2_COLORSPACE_SMPTE170M) -#define MASK_CS_SMPTE240M BIT(V4L2_COLORSPACE_SMPTE240M) -#define MASK_CS_REC709 BIT(V4L2_COLORSPACE_REC709) -#define MASK_CS_BT878 BIT(V4L2_COLORSPACE_BT878) -#define MASK_CS_470_M BIT(V4L2_COLORSPACE_470_SYSTEM_M) -#define MASK_CS_470_BG BIT(V4L2_COLORSPACE_470_SYSTEM_BG) -#define MASK_CS_JPEG BIT(V4L2_COLORSPACE_JPEG) -#define MASK_CS_SRGB BIT(V4L2_COLORSPACE_SRGB) -#define MASK_CS_OPRGB BIT(V4L2_COLORSPACE_OPRGB) -#define MASK_CS_BT2020 BIT(V4L2_COLORSPACE_BT2020) -#define MASK_CS_RAW BIT(V4L2_COLORSPACE_RAW) -#define MASK_CS_DCI_P3 BIT(V4L2_COLORSPACE_DCI_P3) - -#define MAX_COLORSPACE 32 - -/* - * struct unicam_fmt - Unicam media bus format information - * @pixelformat: V4L2 pixel format FCC identifier. 0 if n/a. - * @repacked_fourcc: V4L2 pixel format FCC identifier if the data is expanded - * out to 16bpp. 0 if n/a. - * @code: V4L2 media bus format code. - * @depth: Bits per pixel as delivered from the source. - * @csi_dt: CSI data type. - * @valid_colorspaces: Bitmask of valid colorspaces so that the Media Controller - * centric try_fmt can validate the colorspace and pass - * v4l2-compliance. - * @check_variants: Flag to denote that there are multiple mediabus formats - * still in the list that could match this V4L2 format. - * @mc_skip: Media Controller shouldn't list this format via ENUM_FMT as it is - * a duplicate of an earlier format. - * @metadata_fmt: This format only applies to the metadata pad. - */ -struct unicam_fmt { - u32 fourcc; - u32 repacked_fourcc; - u32 code; - u8 depth; - u8 csi_dt; - u32 valid_colorspaces; - u8 check_variants:1; - u8 mc_skip:1; - u8 metadata_fmt:1; -}; - -static const struct unicam_fmt formats[] = { - /* YUV Formats */ - { - .fourcc = V4L2_PIX_FMT_YUYV, - .code = MEDIA_BUS_FMT_YUYV8_2X8, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_YUV422_8B, - .check_variants = 1, - .valid_colorspaces = MASK_CS_SMPTE170M | MASK_CS_REC709 | - MASK_CS_JPEG, - }, { - .fourcc = V4L2_PIX_FMT_UYVY, - .code = MEDIA_BUS_FMT_UYVY8_2X8, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_YUV422_8B, - .check_variants = 1, - .valid_colorspaces = MASK_CS_SMPTE170M | MASK_CS_REC709 | - MASK_CS_JPEG, - }, { - .fourcc = V4L2_PIX_FMT_YVYU, - .code = MEDIA_BUS_FMT_YVYU8_2X8, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_YUV422_8B, - .check_variants = 1, - .valid_colorspaces = MASK_CS_SMPTE170M | MASK_CS_REC709 | - MASK_CS_JPEG, - }, { - .fourcc = V4L2_PIX_FMT_VYUY, - .code = MEDIA_BUS_FMT_VYUY8_2X8, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_YUV422_8B, - .check_variants = 1, - .valid_colorspaces = MASK_CS_SMPTE170M | MASK_CS_REC709 | - MASK_CS_JPEG, - }, { - .fourcc = V4L2_PIX_FMT_YUYV, - .code = MEDIA_BUS_FMT_YUYV8_1X16, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_YUV422_8B, - .mc_skip = 1, - .valid_colorspaces = MASK_CS_SMPTE170M | MASK_CS_REC709 | - MASK_CS_JPEG, - }, { - .fourcc = V4L2_PIX_FMT_UYVY, - .code = MEDIA_BUS_FMT_UYVY8_1X16, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_YUV422_8B, - .mc_skip = 1, - .valid_colorspaces = MASK_CS_SMPTE170M | MASK_CS_REC709 | - MASK_CS_JPEG, - }, { - .fourcc = V4L2_PIX_FMT_YVYU, - .code = MEDIA_BUS_FMT_YVYU8_1X16, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_YUV422_8B, - .mc_skip = 1, - .valid_colorspaces = MASK_CS_SMPTE170M | MASK_CS_REC709 | - MASK_CS_JPEG, - }, { - .fourcc = V4L2_PIX_FMT_VYUY, - .code = MEDIA_BUS_FMT_VYUY8_1X16, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_YUV422_8B, - .mc_skip = 1, - .valid_colorspaces = MASK_CS_SMPTE170M | MASK_CS_REC709 | - MASK_CS_JPEG, - }, { - /* RGB Formats */ - .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ - .code = MEDIA_BUS_FMT_RGB565_2X8_LE, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_RGB565, - .valid_colorspaces = MASK_CS_SRGB, - }, { - .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ - .code = MEDIA_BUS_FMT_RGB565_2X8_BE, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_RGB565, - .valid_colorspaces = MASK_CS_SRGB, - }, { - .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */ - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_RGB555, - .valid_colorspaces = MASK_CS_SRGB, - }, { - .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */ - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_RGB555, - .valid_colorspaces = MASK_CS_SRGB, - }, { - .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ - .code = MEDIA_BUS_FMT_RGB888_1X24, - .depth = 24, - .csi_dt = MIPI_CSI2_DT_RGB888, - .valid_colorspaces = MASK_CS_SRGB, - }, { - .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ - .code = MEDIA_BUS_FMT_BGR888_1X24, - .depth = 24, - .csi_dt = MIPI_CSI2_DT_RGB888, - .valid_colorspaces = MASK_CS_SRGB, - }, { - .fourcc = V4L2_PIX_FMT_RGB32, /* argb */ - .code = MEDIA_BUS_FMT_ARGB8888_1X32, - .depth = 32, - .csi_dt = 0x0, - .valid_colorspaces = MASK_CS_SRGB, - }, { - /* Bayer Formats */ - .fourcc = V4L2_PIX_FMT_SBGGR8, - .code = MEDIA_BUS_FMT_SBGGR8_1X8, - .depth = 8, - .csi_dt = MIPI_CSI2_DT_RAW8, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG8, - .code = MEDIA_BUS_FMT_SGBRG8_1X8, - .depth = 8, - .csi_dt = MIPI_CSI2_DT_RAW8, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG8, - .code = MEDIA_BUS_FMT_SGRBG8_1X8, - .depth = 8, - .csi_dt = MIPI_CSI2_DT_RAW8, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB8, - .code = MEDIA_BUS_FMT_SRGGB8_1X8, - .depth = 8, - .csi_dt = MIPI_CSI2_DT_RAW8, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR10P, - .repacked_fourcc = V4L2_PIX_FMT_SBGGR10, - .code = MEDIA_BUS_FMT_SBGGR10_1X10, - .depth = 10, - .csi_dt = MIPI_CSI2_DT_RAW10, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG10P, - .repacked_fourcc = V4L2_PIX_FMT_SGBRG10, - .code = MEDIA_BUS_FMT_SGBRG10_1X10, - .depth = 10, - .csi_dt = MIPI_CSI2_DT_RAW10, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG10P, - .repacked_fourcc = V4L2_PIX_FMT_SGRBG10, - .code = MEDIA_BUS_FMT_SGRBG10_1X10, - .depth = 10, - .csi_dt = MIPI_CSI2_DT_RAW10, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB10P, - .repacked_fourcc = V4L2_PIX_FMT_SRGGB10, - .code = MEDIA_BUS_FMT_SRGGB10_1X10, - .depth = 10, - .csi_dt = MIPI_CSI2_DT_RAW10, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR12P, - .repacked_fourcc = V4L2_PIX_FMT_SBGGR12, - .code = MEDIA_BUS_FMT_SBGGR12_1X12, - .depth = 12, - .csi_dt = MIPI_CSI2_DT_RAW12, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG12P, - .repacked_fourcc = V4L2_PIX_FMT_SGBRG12, - .code = MEDIA_BUS_FMT_SGBRG12_1X12, - .depth = 12, - .csi_dt = MIPI_CSI2_DT_RAW12, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG12P, - .repacked_fourcc = V4L2_PIX_FMT_SGRBG12, - .code = MEDIA_BUS_FMT_SGRBG12_1X12, - .depth = 12, - .csi_dt = MIPI_CSI2_DT_RAW12, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB12P, - .repacked_fourcc = V4L2_PIX_FMT_SRGGB12, - .code = MEDIA_BUS_FMT_SRGGB12_1X12, - .depth = 12, - .csi_dt = MIPI_CSI2_DT_RAW12, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR14P, - .repacked_fourcc = V4L2_PIX_FMT_SBGGR14, - .code = MEDIA_BUS_FMT_SBGGR14_1X14, - .depth = 14, - .csi_dt = MIPI_CSI2_DT_RAW14, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG14P, - .repacked_fourcc = V4L2_PIX_FMT_SGBRG14, - .code = MEDIA_BUS_FMT_SGBRG14_1X14, - .depth = 14, - .csi_dt = MIPI_CSI2_DT_RAW14, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG14P, - .repacked_fourcc = V4L2_PIX_FMT_SGRBG14, - .code = MEDIA_BUS_FMT_SGRBG14_1X14, - .depth = 14, - .csi_dt = MIPI_CSI2_DT_RAW14, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB14P, - .repacked_fourcc = V4L2_PIX_FMT_SRGGB14, - .code = MEDIA_BUS_FMT_SRGGB14_1X14, - .depth = 14, - .csi_dt = MIPI_CSI2_DT_RAW14, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR16, - .code = MEDIA_BUS_FMT_SBGGR16_1X16, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_RAW16, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG16, - .code = MEDIA_BUS_FMT_SGBRG16_1X16, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_RAW16, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG16, - .code = MEDIA_BUS_FMT_SGRBG16_1X16, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_RAW16, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB16, - .code = MEDIA_BUS_FMT_SRGGB16_1X16, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_RAW16, - .valid_colorspaces = MASK_CS_RAW, - }, { - - /* Greyscale formats */ - .fourcc = V4L2_PIX_FMT_GREY, - .code = MEDIA_BUS_FMT_Y8_1X8, - .depth = 8, - .csi_dt = MIPI_CSI2_DT_RAW8, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_Y10P, - .repacked_fourcc = V4L2_PIX_FMT_Y10, - .code = MEDIA_BUS_FMT_Y10_1X10, - .depth = 10, - .csi_dt = MIPI_CSI2_DT_RAW10, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_Y12P, - .repacked_fourcc = V4L2_PIX_FMT_Y12, - .code = MEDIA_BUS_FMT_Y12_1X12, - .depth = 12, - .csi_dt = MIPI_CSI2_DT_RAW12, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_Y14P, - .repacked_fourcc = V4L2_PIX_FMT_Y14, - .code = MEDIA_BUS_FMT_Y14_1X14, - .depth = 14, - .csi_dt = MIPI_CSI2_DT_RAW14, - .valid_colorspaces = MASK_CS_RAW, - }, { - .fourcc = V4L2_PIX_FMT_Y16, - .code = MEDIA_BUS_FMT_Y16_1X16, - .depth = 16, - .csi_dt = MIPI_CSI2_DT_RAW16, - .valid_colorspaces = MASK_CS_RAW, - }, - /* Embedded data format */ - { - .fourcc = V4L2_META_FMT_SENSOR_DATA, - .code = MEDIA_BUS_FMT_SENSOR_DATA, - .depth = 8, - .metadata_fmt = 1, - } -}; - -struct unicam_buffer { - struct vb2_v4l2_buffer vb; - struct list_head list; -}; - -static inline struct unicam_buffer *to_unicam_buffer(struct vb2_buffer *vb) -{ - return container_of(vb, struct unicam_buffer, vb.vb2_buf); -} - -struct unicam_node { - bool registered; - int open; - bool streaming; - unsigned int pad_id; - /* Source pad id on the sensor for this node */ - unsigned int src_pad_id; - /* Pointer pointing to current v4l2_buffer */ - struct unicam_buffer *cur_frm; - /* Pointer pointing to next v4l2_buffer */ - struct unicam_buffer *next_frm; - /* video capture */ - const struct unicam_fmt *fmt; - /* Used to store current pixel format */ - struct v4l2_format v_fmt; - /* Used to store current mbus frame format */ - struct v4l2_mbus_framefmt m_fmt; - /* Buffer queue used in video-buf */ - struct vb2_queue buffer_queue; - /* Queue of filled frames */ - struct list_head dma_queue; - /* IRQ lock for DMA queue */ - spinlock_t dma_queue_lock; - /* lock used to access this structure */ - struct mutex lock; - /* Identifies video device for this channel */ - struct video_device video_dev; - /* Pointer to the parent handle */ - struct unicam_device *dev; - struct media_pad pad; - unsigned int embedded_lines; - struct media_pipeline pipe; - /* - * Dummy buffer intended to be used by unicam - * if we have no other queued buffers to swap to. - */ - void *dummy_buf_cpu_addr; - dma_addr_t dummy_buf_dma_addr; -}; - -struct unicam_device { - struct kref kref; - - /* V4l2 specific parameters */ - struct v4l2_async_connection *asd; - - /* peripheral base address */ - void __iomem *base; - /* clock gating base address */ - void __iomem *clk_gate_base; - /* lp clock handle */ - struct clk *clock; - /* vpu clock handle */ - struct clk *vpu_clock; - /* clock status for error handling */ - bool clocks_enabled; - /* V4l2 device */ - struct v4l2_device v4l2_dev; - struct media_device mdev; - - struct gpio_desc *sync_gpio; - - /* parent device */ - struct platform_device *pdev; - /* subdevice async Notifier */ - struct v4l2_async_notifier notifier; - unsigned int sequence; - bool frame_started; - - /* ptr to sub device */ - struct v4l2_subdev *sensor; - /* Pad config for the sensor */ - struct v4l2_subdev_state *sensor_state; - - enum v4l2_mbus_type bus_type; - /* - * Stores bus.mipi_csi2.flags for CSI2 sensors, or - * bus.mipi_csi1.strobe for CCP2. - */ - unsigned int bus_flags; - unsigned int max_data_lanes; - unsigned int active_data_lanes; - bool sensor_embedded_data; - - struct unicam_node node[MAX_NODES]; - struct v4l2_ctrl_handler ctrl_handler; - - bool mc_api; -}; - -static inline struct unicam_device * -to_unicam_device(struct v4l2_device *v4l2_dev) -{ - return container_of(v4l2_dev, struct unicam_device, v4l2_dev); -} - -/* Hardware access */ -static inline void clk_write(struct unicam_device *dev, u32 val) -{ - writel(val | 0x5a000000, dev->clk_gate_base); -} - -static inline u32 reg_read(struct unicam_device *dev, u32 offset) -{ - return readl(dev->base + offset); -} - -static inline void reg_write(struct unicam_device *dev, u32 offset, u32 val) -{ - writel(val, dev->base + offset); -} - -static inline int get_field(u32 value, u32 mask) -{ - return (value & mask) >> __ffs(mask); -} - -static inline void set_field(u32 *valp, u32 field, u32 mask) -{ - u32 val = *valp; - - val &= ~mask; - val |= (field << __ffs(mask)) & mask; - *valp = val; -} - -static inline u32 reg_read_field(struct unicam_device *dev, u32 offset, - u32 mask) -{ - return get_field(reg_read(dev, offset), mask); -} - -static inline void reg_write_field(struct unicam_device *dev, u32 offset, - u32 field, u32 mask) -{ - u32 val = reg_read(dev, offset); - - set_field(&val, field, mask); - reg_write(dev, offset, val); -} - -/* Power management functions */ -static inline int unicam_runtime_get(struct unicam_device *dev) -{ - return pm_runtime_get_sync(&dev->pdev->dev); -} - -static inline void unicam_runtime_put(struct unicam_device *dev) -{ - pm_runtime_put_sync(&dev->pdev->dev); -} - -/* Format setup functions */ -static const struct unicam_fmt *find_format_by_code(u32 code) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(formats); i++) { - if (formats[i].code == code) - return &formats[i]; - } - - return NULL; -} - -static int check_mbus_format(struct unicam_device *dev, - const struct unicam_fmt *format) -{ - unsigned int i; - int ret = 0; - - for (i = 0; !ret && i < MAX_ENUM_MBUS_CODE; i++) { - struct v4l2_subdev_mbus_code_enum mbus_code = { - .index = i, - .pad = IMAGE_PAD, - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - - ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code, - NULL, &mbus_code); - - if (!ret && mbus_code.code == format->code) - return 1; - } - - return 0; -} - -static const struct unicam_fmt *find_format_by_pix(struct unicam_device *dev, - u32 pixelformat) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(formats); i++) { - if (formats[i].fourcc == pixelformat || - formats[i].repacked_fourcc == pixelformat) { - if (formats[i].check_variants && - !check_mbus_format(dev, &formats[i])) - continue; - return &formats[i]; - } - } - - return NULL; -} - -static unsigned int bytes_per_line(u32 width, const struct unicam_fmt *fmt, - u32 v4l2_fourcc) -{ - if (v4l2_fourcc == fmt->repacked_fourcc) - /* Repacking always goes to 16bpp */ - return ALIGN(width << 1, BPL_ALIGNMENT); - else - return ALIGN((width * fmt->depth) >> 3, BPL_ALIGNMENT); -} - -static int __subdev_get_format(struct unicam_device *dev, - struct v4l2_mbus_framefmt *fmt, int pad_id) -{ - struct v4l2_subdev_format sd_fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .pad = dev->node[pad_id].src_pad_id, - }; - int ret; - - ret = v4l2_subdev_call(dev->sensor, pad, get_fmt, dev->sensor_state, - &sd_fmt); - if (ret < 0) - return ret; - - *fmt = sd_fmt.format; - - unicam_dbg(1, dev, "%s %dx%d code:%04x\n", __func__, - fmt->width, fmt->height, fmt->code); - - return 0; -} - -static int __subdev_set_format(struct unicam_device *dev, - struct v4l2_mbus_framefmt *fmt, int pad_id) -{ - struct v4l2_subdev_format sd_fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .pad = dev->node[pad_id].src_pad_id, - }; - int ret; - - sd_fmt.format = *fmt; - - ret = v4l2_subdev_call(dev->sensor, pad, set_fmt, dev->sensor_state, - &sd_fmt); - if (ret < 0) - return ret; - - *fmt = sd_fmt.format; - - if (pad_id == IMAGE_PAD) - unicam_dbg(1, dev, "%s %dx%d code:%04x\n", __func__, fmt->width, - fmt->height, fmt->code); - else - unicam_dbg(1, dev, "%s Embedded data code:%04x\n", __func__, - sd_fmt.format.code); - - return 0; -} - -static int unicam_calc_format_size_bpl(struct unicam_device *dev, - const struct unicam_fmt *fmt, - struct v4l2_format *f) -{ - unsigned int min_bytesperline; - - v4l_bound_align_image(&f->fmt.pix.width, MIN_WIDTH, MAX_WIDTH, 2, - &f->fmt.pix.height, MIN_HEIGHT, MAX_HEIGHT, 0, - 0); - - min_bytesperline = bytes_per_line(f->fmt.pix.width, fmt, - f->fmt.pix.pixelformat); - - if (f->fmt.pix.bytesperline > min_bytesperline && - f->fmt.pix.bytesperline <= MAX_BYTESPERLINE) - f->fmt.pix.bytesperline = ALIGN(f->fmt.pix.bytesperline, - BPL_ALIGNMENT); - else - f->fmt.pix.bytesperline = min_bytesperline; - - f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; - - unicam_dbg(3, dev, "%s: fourcc: %08X size: %dx%d bpl:%d img_size:%d\n", - __func__, - f->fmt.pix.pixelformat, - f->fmt.pix.width, f->fmt.pix.height, - f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); - - return 0; -} - -static int unicam_reset_format(struct unicam_node *node) -{ - struct unicam_device *dev = node->dev; - struct v4l2_mbus_framefmt mbus_fmt; - int ret; - - if (dev->sensor_embedded_data || node->pad_id != METADATA_PAD) { - ret = __subdev_get_format(dev, &mbus_fmt, node->pad_id); - if (ret) { - unicam_err(dev, "Failed to get_format - ret %d\n", ret); - return ret; - } - - if (mbus_fmt.code != node->fmt->code) { - unicam_err(dev, "code mismatch - fmt->code %08x, mbus_fmt.code %08x\n", - node->fmt->code, mbus_fmt.code); - return ret; - } - } - - if (node->pad_id == IMAGE_PAD) { - v4l2_fill_pix_format(&node->v_fmt.fmt.pix, &mbus_fmt); - node->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - unicam_calc_format_size_bpl(dev, node->fmt, &node->v_fmt); - } else { - node->v_fmt.type = V4L2_BUF_TYPE_META_CAPTURE; - node->v_fmt.fmt.meta.dataformat = V4L2_META_FMT_SENSOR_DATA; - if (dev->sensor_embedded_data) { - node->v_fmt.fmt.meta.buffersize = - mbus_fmt.width * mbus_fmt.height; - node->embedded_lines = mbus_fmt.height; - } else { - node->v_fmt.fmt.meta.buffersize = UNICAM_EMBEDDED_SIZE; - node->embedded_lines = 1; - } - } - - node->m_fmt = mbus_fmt; - return 0; -} - -static void unicam_wr_dma_addr(struct unicam_device *dev, dma_addr_t dmaaddr, - unsigned int buffer_size, int pad_id) -{ - dma_addr_t endaddr = dmaaddr + buffer_size; - - if (pad_id == IMAGE_PAD) { - reg_write(dev, UNICAM_IBSA0, dmaaddr); - reg_write(dev, UNICAM_IBEA0, endaddr); - } else { - reg_write(dev, UNICAM_DBSA0, dmaaddr); - reg_write(dev, UNICAM_DBEA0, endaddr); - } -} - -static unsigned int unicam_get_lines_done(struct unicam_device *dev) -{ - dma_addr_t start_addr, cur_addr; - unsigned int stride = dev->node[IMAGE_PAD].v_fmt.fmt.pix.bytesperline; - struct unicam_buffer *frm = dev->node[IMAGE_PAD].cur_frm; - - if (!frm) - return 0; - - start_addr = vb2_dma_contig_plane_dma_addr(&frm->vb.vb2_buf, 0); - cur_addr = reg_read(dev, UNICAM_IBWP); - return (unsigned int)(cur_addr - start_addr) / stride; -} - -static void unicam_schedule_next_buffer(struct unicam_node *node) -{ - struct unicam_device *dev = node->dev; - struct unicam_buffer *buf; - unsigned int size; - dma_addr_t addr; - - buf = list_first_entry(&node->dma_queue, struct unicam_buffer, list); - node->next_frm = buf; - list_del(&buf->list); - - addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); - size = (node->pad_id == IMAGE_PAD) ? - node->v_fmt.fmt.pix.sizeimage : - node->v_fmt.fmt.meta.buffersize; - - unicam_wr_dma_addr(dev, addr, size, node->pad_id); -} - -static void unicam_schedule_dummy_buffer(struct unicam_node *node) -{ - struct unicam_device *dev = node->dev; - - unicam_dbg(3, dev, "Scheduling dummy buffer for node %d\n", - node->pad_id); - - unicam_wr_dma_addr(dev, node->dummy_buf_dma_addr, 0, node->pad_id); - node->next_frm = NULL; -} - -static void unicam_process_buffer_complete(struct unicam_node *node, - unsigned int sequence) -{ - node->cur_frm->vb.field = node->m_fmt.field; - node->cur_frm->vb.sequence = sequence; - - vb2_buffer_done(&node->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE); -} - -static void unicam_queue_event_sof(struct unicam_device *unicam) -{ - struct v4l2_event event = { - .type = V4L2_EVENT_FRAME_SYNC, - .u.frame_sync.frame_sequence = unicam->sequence, - }; - - v4l2_event_queue(&unicam->node[IMAGE_PAD].video_dev, &event); -} - -/* - * unicam_isr : ISR handler for unicam capture - * @irq: irq number - * @dev_id: dev_id ptr - * - * It changes status of the captured buffer, takes next buffer from the queue - * and sets its address in unicam registers - */ -static irqreturn_t unicam_isr(int irq, void *dev) -{ - struct unicam_device *unicam = dev; - unsigned int lines_done = unicam_get_lines_done(dev); - unsigned int sequence = unicam->sequence; - unsigned int i; - u32 ista, sta; - bool fe; - u64 ts; - - sta = reg_read(unicam, UNICAM_STA); - /* Write value back to clear the interrupts */ - reg_write(unicam, UNICAM_STA, sta); - - ista = reg_read(unicam, UNICAM_ISTA); - /* Write value back to clear the interrupts */ - reg_write(unicam, UNICAM_ISTA, ista); - - unicam_dbg(3, unicam, "ISR: ISTA: 0x%X, STA: 0x%X, sequence %d, lines done %d", - ista, sta, sequence, lines_done); - - if (!(sta & (UNICAM_IS | UNICAM_PI0))) - return IRQ_HANDLED; - - /* - * Look for either the Frame End interrupt or the Packet Capture status - * to signal a frame end. - */ - fe = (ista & UNICAM_FEI || sta & UNICAM_PI0); - - /* - * We must run the frame end handler first. If we have a valid next_frm - * and we get a simultaneout FE + FS interrupt, running the FS handler - * first would null out the next_frm ptr and we would have lost the - * buffer forever. - */ - if (fe) { - bool inc_seq = unicam->frame_started; - - if (unicam->sync_gpio) - gpiod_set_value(unicam->sync_gpio, 0); - /* - * Ensure we have swapped buffers already as we can't - * stop the peripheral. If no buffer is available, use a - * dummy buffer to dump out frames until we get a new buffer - * to use. - */ - for (i = 0; i < ARRAY_SIZE(unicam->node); i++) { - struct unicam_node *node = &unicam->node[i]; - - if (!node->streaming) - continue; - - /* - * If cur_frm == next_frm, it means we have not had - * a chance to swap buffers, likely due to having - * multiple interrupts occurring simultaneously (like FE - * + FS + LS). In this case, we cannot signal the buffer - * as complete, as the HW will reuse that buffer. - */ - if (node->cur_frm && node->cur_frm != node->next_frm) { - /* - * This condition checks if FE + FS for the same - * frame has occurred. In such cases, we cannot - * return out the frame, as no buffer handling - * or timestamping has yet been done as part of - * the FS handler. - */ - if (!node->cur_frm->vb.vb2_buf.timestamp) { - unicam_dbg(2, unicam, "ISR: FE without FS, dropping frame\n"); - continue; - } - - unicam_process_buffer_complete(node, sequence); - node->cur_frm = node->next_frm; - node->next_frm = NULL; - inc_seq = true; - } else { - node->cur_frm = node->next_frm; - } - } - - /* - * Increment the sequence number conditionally on either a FS - * having already occurred, or in the FE + FS condition as - * caught in the FE handler above. This ensures the sequence - * number corresponds to the frames generated by the sensor, not - * the frames dequeued to userland. - */ - if (inc_seq) { - unicam->sequence++; - unicam->frame_started = false; - } - } - - if (ista & UNICAM_FSI) { - /* - * Timestamp is to be when the first data byte was captured, - * aka frame start. - */ - ts = ktime_get_ns(); - - if (unicam->sync_gpio) - gpiod_set_value(unicam->sync_gpio, 1); - - for (i = 0; i < ARRAY_SIZE(unicam->node); i++) { - if (!unicam->node[i].streaming) - continue; - - if (unicam->node[i].cur_frm) - unicam->node[i].cur_frm->vb.vb2_buf.timestamp = - ts; - else - unicam_dbg(2, unicam, "ISR: [%d] Dropping frame, buffer not available at FS\n", - i); - /* - * Set the next frame output to go to a dummy frame - * if no buffer currently queued. - */ - if (!unicam->node[i].next_frm || - unicam->node[i].next_frm == unicam->node[i].cur_frm) { - unicam_schedule_dummy_buffer(&unicam->node[i]); - } else if (unicam->node[i].cur_frm) { - /* - * Repeated FS without FE. Hardware will have - * swapped buffers, but the cur_frm doesn't - * contain valid data. Return cur_frm to the - * queue. - */ - spin_lock(&unicam->node[i].dma_queue_lock); - list_add_tail(&unicam->node[i].cur_frm->list, - &unicam->node[i].dma_queue); - spin_unlock(&unicam->node[i].dma_queue_lock); - unicam->node[i].cur_frm = unicam->node[i].next_frm; - unicam->node[i].next_frm = NULL; - } - } - - unicam_queue_event_sof(unicam); - unicam->frame_started = true; - } - - /* - * Cannot swap buffer at frame end, there may be a race condition - * where the HW does not actually swap it if the new frame has - * already started. - */ - if (ista & (UNICAM_FSI | UNICAM_LCI) && !fe) { - for (i = 0; i < ARRAY_SIZE(unicam->node); i++) { - if (!unicam->node[i].streaming) - continue; - - spin_lock(&unicam->node[i].dma_queue_lock); - if (!list_empty(&unicam->node[i].dma_queue) && - !unicam->node[i].next_frm) - unicam_schedule_next_buffer(&unicam->node[i]); - spin_unlock(&unicam->node[i].dma_queue_lock); - } - } - - return IRQ_HANDLED; -} - -/* V4L2 Common IOCTLs */ -static int unicam_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - - strscpy(cap->driver, UNICAM_MODULE_NAME, sizeof(cap->driver)); - strscpy(cap->card, UNICAM_MODULE_NAME, sizeof(cap->card)); - - snprintf(cap->bus_info, sizeof(cap->bus_info), - "platform:%s", dev_name(&dev->pdev->dev)); - - cap->capabilities |= V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE; - - return 0; -} - -static int unicam_log_status(struct file *file, void *fh) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - u32 reg; - - /* status for sub devices */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, log_status); - - unicam_info(dev, "-----Receiver status-----\n"); - unicam_info(dev, "V4L2 width/height: %ux%u\n", - node->v_fmt.fmt.pix.width, node->v_fmt.fmt.pix.height); - unicam_info(dev, "Mediabus format: %08x\n", node->fmt->code); - unicam_info(dev, "V4L2 format: %08x\n", - node->v_fmt.fmt.pix.pixelformat); - reg = reg_read(dev, UNICAM_IPIPE); - unicam_info(dev, "Unpacking/packing: %u / %u\n", - get_field(reg, UNICAM_PUM_MASK), - get_field(reg, UNICAM_PPM_MASK)); - unicam_info(dev, "----Live data----\n"); - unicam_info(dev, "Programmed stride: %4u\n", - reg_read(dev, UNICAM_IBLS)); - unicam_info(dev, "Detected resolution: %ux%u\n", - reg_read(dev, UNICAM_IHSTA), - reg_read(dev, UNICAM_IVSTA)); - unicam_info(dev, "Write pointer: %08x\n", - reg_read(dev, UNICAM_IBWP)); - - return 0; -} - -/* V4L2 Video Centric IOCTLs */ -static int unicam_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - unsigned int index = 0; - unsigned int i; - int ret = 0; - - if (node->pad_id != IMAGE_PAD) - return -EINVAL; - - for (i = 0; !ret && i < MAX_ENUM_MBUS_CODE; i++) { - struct v4l2_subdev_mbus_code_enum mbus_code = { - .index = i, - .pad = IMAGE_PAD, - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - const struct unicam_fmt *fmt; - - ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code, - NULL, &mbus_code); - if (ret < 0) { - unicam_dbg(2, dev, - "subdev->enum_mbus_code idx %d returned %d - index invalid\n", - i, ret); - return -EINVAL; - } - - fmt = find_format_by_code(mbus_code.code); - if (fmt) { - if (fmt->fourcc) { - if (index == f->index) { - f->pixelformat = fmt->fourcc; - break; - } - index++; - } - if (fmt->repacked_fourcc) { - if (index == f->index) { - f->pixelformat = fmt->repacked_fourcc; - break; - } - index++; - } - } - } - - return 0; -} - -static int unicam_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct v4l2_mbus_framefmt mbus_fmt = {0}; - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - const struct unicam_fmt *fmt = NULL; - int ret; - - if (node->pad_id != IMAGE_PAD) - return -EINVAL; - - /* - * If a flip has occurred in the sensor, the fmt code might have - * changed. So we will need to re-fetch the format from the subdevice. - */ - ret = __subdev_get_format(dev, &mbus_fmt, node->pad_id); - if (ret) - return -EINVAL; - - /* Find the V4L2 format from mbus code. We must match a known format. */ - fmt = find_format_by_code(mbus_fmt.code); - if (!fmt) - return -EINVAL; - - if (node->fmt != fmt) { - /* - * The sensor format has changed so the pixelformat needs to - * be updated. Try and retain the packed/unpacked choice if - * at all possible. - */ - if (node->fmt->repacked_fourcc == - node->v_fmt.fmt.pix.pixelformat) - /* Using the repacked format */ - node->v_fmt.fmt.pix.pixelformat = fmt->repacked_fourcc; - else - /* Using the native format */ - node->v_fmt.fmt.pix.pixelformat = fmt->fourcc; - - node->fmt = fmt; - } - - *f = node->v_fmt; - - return 0; -} - -static const struct unicam_fmt * -get_first_supported_format(struct unicam_device *dev) -{ - struct v4l2_subdev_mbus_code_enum mbus_code; - const struct unicam_fmt *fmt = NULL; - unsigned int i; - int ret = 0; - - for (i = 0; ret != -EINVAL && ret != -ENOIOCTLCMD; ++i) { - memset(&mbus_code, 0, sizeof(mbus_code)); - mbus_code.index = i; - mbus_code.pad = IMAGE_PAD; - mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; - - ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code, NULL, - &mbus_code); - if (ret < 0) { - unicam_dbg(2, dev, - "subdev->enum_mbus_code idx %u returned %d - continue\n", - i, ret); - continue; - } - - unicam_dbg(2, dev, "subdev %s: code: 0x%08x idx: %u\n", - dev->sensor->name, mbus_code.code, i); - - fmt = find_format_by_code(mbus_code.code); - unicam_dbg(2, dev, "fmt 0x%08x returned as %p, V4L2 FOURCC 0x%08x, csi_dt 0x%02x\n", - mbus_code.code, fmt, fmt ? fmt->fourcc : 0, - fmt ? fmt->csi_dt : 0); - if (fmt) - return fmt; - } - - return NULL; -} - -static int unicam_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - struct v4l2_subdev_format sd_fmt = { - .which = V4L2_SUBDEV_FORMAT_TRY, - .pad = IMAGE_PAD - }; - struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; - const struct unicam_fmt *fmt; - int ret; - - if (node->pad_id != IMAGE_PAD) - return -EINVAL; - - fmt = find_format_by_pix(dev, f->fmt.pix.pixelformat); - if (!fmt) { - /* - * Pixel format not supported by unicam. Choose the first - * supported format, and let the sensor choose something else. - */ - unicam_dbg(3, dev, "Fourcc format (0x%08x) not found. Use first format.\n", - f->fmt.pix.pixelformat); - - fmt = &formats[0]; - f->fmt.pix.pixelformat = fmt->fourcc; - } - - v4l2_fill_mbus_format(mbus_fmt, &f->fmt.pix, fmt->code); - /* - * No support for receiving interlaced video, so never - * request it from the sensor subdev. - */ - mbus_fmt->field = V4L2_FIELD_NONE; - - ret = v4l2_subdev_call(dev->sensor, pad, set_fmt, dev->sensor_state, - &sd_fmt); - if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) - return ret; - - if (mbus_fmt->field != V4L2_FIELD_NONE) - unicam_info(dev, "Sensor trying to send interlaced video - results may be unpredictable\n"); - - v4l2_fill_pix_format(&f->fmt.pix, &sd_fmt.format); - if (mbus_fmt->code != fmt->code) { - /* Sensor has returned an alternate format */ - fmt = find_format_by_code(mbus_fmt->code); - if (!fmt) { - /* - * The alternate format is one unicam can't support. - * Find the first format that is supported by both, and - * then set that. - */ - fmt = get_first_supported_format(dev); - mbus_fmt->code = fmt->code; - - ret = v4l2_subdev_call(dev->sensor, pad, set_fmt, - dev->sensor_state, &sd_fmt); - if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) - return ret; - - if (mbus_fmt->field != V4L2_FIELD_NONE) - unicam_info(dev, "Sensor trying to send interlaced video - results may be unpredictable\n"); - - v4l2_fill_pix_format(&f->fmt.pix, &sd_fmt.format); - - if (mbus_fmt->code != fmt->code) { - /* - * We've set a format that the sensor reports - * as being supported, but it refuses to set it. - * Not much else we can do. - * Assume that the sensor driver may accept the - * format when it is set (rather than tried). - */ - unicam_err(dev, "Sensor won't accept default format, and Unicam can't support sensor default\n"); - } - } - - if (fmt->fourcc) - f->fmt.pix.pixelformat = fmt->fourcc; - else - f->fmt.pix.pixelformat = fmt->repacked_fourcc; - } - - return unicam_calc_format_size_bpl(dev, fmt, f); -} - -static int unicam_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - struct vb2_queue *q = &node->buffer_queue; - struct v4l2_mbus_framefmt mbus_fmt = {0}; - const struct unicam_fmt *fmt; - int ret; - - if (vb2_is_busy(q)) - return -EBUSY; - - ret = unicam_try_fmt_vid_cap(file, priv, f); - if (ret < 0) - return ret; - - fmt = find_format_by_pix(dev, f->fmt.pix.pixelformat); - if (!fmt) { - /* - * Unknown pixel format - adopt a default. - * This shouldn't happen as try_fmt should have resolved any - * issues first. - */ - fmt = get_first_supported_format(dev); - if (!fmt) - /* - * It shouldn't be possible to get here with no - * supported formats - */ - return -EINVAL; - f->fmt.pix.pixelformat = fmt->fourcc; - return -EINVAL; - } - - v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code); - - ret = __subdev_set_format(dev, &mbus_fmt, node->pad_id); - if (ret) { - unicam_dbg(3, dev, "%s __subdev_set_format failed %d\n", - __func__, ret); - return ret; - } - - /* Just double check nothing has gone wrong */ - if (mbus_fmt.code != fmt->code) { - unicam_dbg(3, dev, - "%s subdev changed format on us, this should not happen\n", - __func__); - return -EINVAL; - } - - node->fmt = fmt; - node->v_fmt.fmt.pix.pixelformat = f->fmt.pix.pixelformat; - node->v_fmt.fmt.pix.bytesperline = f->fmt.pix.bytesperline; - unicam_reset_format(node); - - unicam_dbg(3, dev, - "%s %dx%d, mbus_fmt 0x%08X, V4L2 pix 0x%08X.\n", - __func__, node->v_fmt.fmt.pix.width, - node->v_fmt.fmt.pix.height, mbus_fmt.code, - node->v_fmt.fmt.pix.pixelformat); - - *f = node->v_fmt; - - return 0; -} - -static int unicam_enum_fmt_meta_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - const struct unicam_fmt *fmt; - u32 code; - int ret = 0; - - if (node->pad_id != METADATA_PAD || f->index != 0) - return -EINVAL; - - if (dev->sensor_embedded_data) { - struct v4l2_subdev_mbus_code_enum mbus_code = { - .index = f->index, - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .pad = METADATA_PAD, - }; - - ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code, NULL, - &mbus_code); - if (ret < 0) { - unicam_dbg(2, dev, - "subdev->enum_mbus_code idx 0 returned %d - index invalid\n", - ret); - return -EINVAL; - } - - code = mbus_code.code; - } else { - code = MEDIA_BUS_FMT_SENSOR_DATA; - } - - fmt = find_format_by_code(code); - if (fmt) - f->pixelformat = fmt->fourcc; - - return 0; -} - -static int unicam_g_fmt_meta_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct unicam_node *node = video_drvdata(file); - - if (node->pad_id != METADATA_PAD) - return -EINVAL; - - *f = node->v_fmt; - - return 0; -} - -static int unicam_enum_input(struct file *file, void *priv, - struct v4l2_input *inp) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - int ret; - - if (inp->index != 0) - return -EINVAL; - - inp->type = V4L2_INPUT_TYPE_CAMERA; - if (v4l2_subdev_has_op(dev->sensor, video, s_dv_timings)) { - inp->capabilities = V4L2_IN_CAP_DV_TIMINGS; - inp->std = 0; - } else if (v4l2_subdev_has_op(dev->sensor, video, s_std)) { - inp->capabilities = V4L2_IN_CAP_STD; - if (v4l2_subdev_call(dev->sensor, video, g_tvnorms, &inp->std) < 0) - inp->std = V4L2_STD_ALL; - } else { - inp->capabilities = 0; - inp->std = 0; - } - - if (v4l2_subdev_has_op(dev->sensor, video, g_input_status)) { - ret = v4l2_subdev_call(dev->sensor, video, g_input_status, - &inp->status); - if (ret < 0) - return ret; - } - - snprintf(inp->name, sizeof(inp->name), "Camera 0"); - return 0; -} - -static int unicam_g_input(struct file *file, void *priv, unsigned int *i) -{ - *i = 0; - - return 0; -} - -static int unicam_s_input(struct file *file, void *priv, unsigned int i) -{ - /* - * FIXME: Ideally we would like to be able to query the source - * subdevice for information over the input connectors it supports, - * and map that through in to a call to video_ops->s_routing. - * There is no infrastructure support for defining that within - * devicetree at present. Until that is implemented we can't - * map a user physical connector number to s_routing input number. - */ - if (i > 0) - return -EINVAL; - - return 0; -} - -static int unicam_querystd(struct file *file, void *priv, - v4l2_std_id *std) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - - return v4l2_subdev_call(dev->sensor, video, querystd, std); -} - -static int unicam_g_std(struct file *file, void *priv, v4l2_std_id *std) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - - return v4l2_subdev_call(dev->sensor, video, g_std, std); -} - -static int unicam_s_std(struct file *file, void *priv, v4l2_std_id std) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - int ret; - v4l2_std_id current_std; - - ret = v4l2_subdev_call(dev->sensor, video, g_std, ¤t_std); - if (ret) - return ret; - - if (std == current_std) - return 0; - - if (vb2_is_busy(&node->buffer_queue)) - return -EBUSY; - - ret = v4l2_subdev_call(dev->sensor, video, s_std, std); - - /* Force recomputation of bytesperline */ - node->v_fmt.fmt.pix.bytesperline = 0; - - unicam_reset_format(node); - - return ret; -} - -static int unicam_s_edid(struct file *file, void *priv, struct v4l2_edid *edid) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - - return v4l2_subdev_call(dev->sensor, pad, set_edid, edid); -} - -static int unicam_g_edid(struct file *file, void *priv, struct v4l2_edid *edid) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - - return v4l2_subdev_call(dev->sensor, pad, get_edid, edid); -} - -static int unicam_s_selection(struct file *file, void *priv, - struct v4l2_selection *sel) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - struct v4l2_subdev_selection sdsel = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .target = sel->target, - .flags = sel->flags, - .r = sel->r, - }; - - if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - return v4l2_subdev_call(dev->sensor, pad, set_selection, NULL, &sdsel); -} - -static int unicam_g_selection(struct file *file, void *priv, - struct v4l2_selection *sel) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - struct v4l2_subdev_selection sdsel = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .target = sel->target, - }; - int ret; - - if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - ret = v4l2_subdev_call(dev->sensor, pad, get_selection, NULL, &sdsel); - if (!ret) - sel->r = sdsel.r; - - return ret; -} - -static int unicam_enum_framesizes(struct file *file, void *priv, - struct v4l2_frmsizeenum *fsize) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - const struct unicam_fmt *fmt; - struct v4l2_subdev_frame_size_enum fse; - int ret; - - /* check for valid format */ - fmt = find_format_by_pix(dev, fsize->pixel_format); - if (!fmt) { - unicam_dbg(3, dev, "Invalid pixel code: %x\n", - fsize->pixel_format); - return -EINVAL; - } - fse.code = fmt->code; - - fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; - fse.index = fsize->index; - fse.pad = node->src_pad_id; - - ret = v4l2_subdev_call(dev->sensor, pad, enum_frame_size, NULL, &fse); - if (ret) - return ret; - - unicam_dbg(1, dev, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n", - __func__, fse.index, fse.code, fse.min_width, fse.max_width, - fse.min_height, fse.max_height); - - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - fsize->discrete.width = fse.max_width; - fsize->discrete.height = fse.max_height; - - return 0; -} - -static int unicam_enum_frameintervals(struct file *file, void *priv, - struct v4l2_frmivalenum *fival) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - const struct unicam_fmt *fmt; - struct v4l2_subdev_frame_interval_enum fie = { - .index = fival->index, - .pad = node->src_pad_id, - .width = fival->width, - .height = fival->height, - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - int ret; - - fmt = find_format_by_pix(dev, fival->pixel_format); - if (!fmt) - return -EINVAL; - - fie.code = fmt->code; - ret = v4l2_subdev_call(dev->sensor, pad, enum_frame_interval, - NULL, &fie); - if (ret) - return ret; - - fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; - fival->discrete = fie.interval; - - return 0; -} - -static int unicam_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - - return v4l2_g_parm_cap(video_devdata(file), dev->sensor, a); -} - -static int unicam_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - - return v4l2_s_parm_cap(video_devdata(file), dev->sensor, a); -} - -static int unicam_g_dv_timings(struct file *file, void *priv, - struct v4l2_dv_timings *timings) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - - return v4l2_subdev_call(dev->sensor, video, g_dv_timings, timings); -} - -static int unicam_s_dv_timings(struct file *file, void *priv, - struct v4l2_dv_timings *timings) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - struct v4l2_dv_timings current_timings; - int ret; - - ret = v4l2_subdev_call(dev->sensor, video, g_dv_timings, - ¤t_timings); - - if (ret < 0) - return ret; - - if (v4l2_match_dv_timings(timings, ¤t_timings, 0, false)) - return 0; - - if (vb2_is_busy(&node->buffer_queue)) - return -EBUSY; - - ret = v4l2_subdev_call(dev->sensor, video, s_dv_timings, timings); - - /* Force recomputation of bytesperline */ - node->v_fmt.fmt.pix.bytesperline = 0; - - unicam_reset_format(node); - - return ret; -} - -static int unicam_query_dv_timings(struct file *file, void *priv, - struct v4l2_dv_timings *timings) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - - return v4l2_subdev_call(dev->sensor, video, query_dv_timings, timings); -} - -static int unicam_enum_dv_timings(struct file *file, void *priv, - struct v4l2_enum_dv_timings *timings) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - int ret; - - timings->pad = node->src_pad_id; - ret = v4l2_subdev_call(dev->sensor, pad, enum_dv_timings, timings); - timings->pad = node->pad_id; - - return ret; -} - -static int unicam_dv_timings_cap(struct file *file, void *priv, - struct v4l2_dv_timings_cap *cap) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - int ret; - - cap->pad = node->src_pad_id; - ret = v4l2_subdev_call(dev->sensor, pad, dv_timings_cap, cap); - cap->pad = node->pad_id; - - return ret; -} - -static int unicam_subscribe_event(struct v4l2_fh *fh, - const struct v4l2_event_subscription *sub) -{ - switch (sub->type) { - case V4L2_EVENT_FRAME_SYNC: - return v4l2_event_subscribe(fh, sub, 2, NULL); - case V4L2_EVENT_SOURCE_CHANGE: - return v4l2_event_subscribe(fh, sub, 4, NULL); - } - - return v4l2_ctrl_subscribe_event(fh, sub); -} - -static void unicam_notify(struct v4l2_subdev *sd, - unsigned int notification, void *arg) -{ - struct unicam_device *dev = to_unicam_device(sd->v4l2_dev); - - switch (notification) { - case V4L2_DEVICE_NOTIFY_EVENT: - v4l2_event_queue(&dev->node[IMAGE_PAD].video_dev, arg); - break; - default: - break; - } -} - -/* unicam capture ioctl operations */ -static const struct v4l2_ioctl_ops unicam_ioctl_ops = { - .vidioc_querycap = unicam_querycap, - .vidioc_enum_fmt_vid_cap = unicam_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = unicam_g_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = unicam_s_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = unicam_try_fmt_vid_cap, - - .vidioc_enum_fmt_meta_cap = unicam_enum_fmt_meta_cap, - .vidioc_g_fmt_meta_cap = unicam_g_fmt_meta_cap, - .vidioc_s_fmt_meta_cap = unicam_g_fmt_meta_cap, - .vidioc_try_fmt_meta_cap = unicam_g_fmt_meta_cap, - - .vidioc_enum_input = unicam_enum_input, - .vidioc_g_input = unicam_g_input, - .vidioc_s_input = unicam_s_input, - - .vidioc_querystd = unicam_querystd, - .vidioc_s_std = unicam_s_std, - .vidioc_g_std = unicam_g_std, - - .vidioc_g_edid = unicam_g_edid, - .vidioc_s_edid = unicam_s_edid, - - .vidioc_enum_framesizes = unicam_enum_framesizes, - .vidioc_enum_frameintervals = unicam_enum_frameintervals, - - .vidioc_g_selection = unicam_g_selection, - .vidioc_s_selection = unicam_s_selection, - - .vidioc_g_parm = unicam_g_parm, - .vidioc_s_parm = unicam_s_parm, - - .vidioc_s_dv_timings = unicam_s_dv_timings, - .vidioc_g_dv_timings = unicam_g_dv_timings, - .vidioc_query_dv_timings = unicam_query_dv_timings, - .vidioc_enum_dv_timings = unicam_enum_dv_timings, - .vidioc_dv_timings_cap = unicam_dv_timings_cap, - - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_create_bufs = vb2_ioctl_create_bufs, - .vidioc_prepare_buf = vb2_ioctl_prepare_buf, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_expbuf = vb2_ioctl_expbuf, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - - .vidioc_log_status = unicam_log_status, - .vidioc_subscribe_event = unicam_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -/* V4L2 Media Controller Centric IOCTLs */ - -static int unicam_mc_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - int i, j; - - for (i = 0, j = 0; i < ARRAY_SIZE(formats); i++) { - if (f->mbus_code && formats[i].code != f->mbus_code) - continue; - if (formats[i].mc_skip || formats[i].metadata_fmt) - continue; - - if (formats[i].fourcc) { - if (j == f->index) { - f->pixelformat = formats[i].fourcc; - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - return 0; - } - j++; - } - if (formats[i].repacked_fourcc) { - if (j == f->index) { - f->pixelformat = formats[i].repacked_fourcc; - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - return 0; - } - j++; - } - } - - return -EINVAL; -} - -static int unicam_mc_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct unicam_node *node = video_drvdata(file); - - if (node->pad_id != IMAGE_PAD) - return -EINVAL; - - *f = node->v_fmt; - - return 0; -} - -static void unicam_mc_try_fmt(struct unicam_node *node, struct v4l2_format *f, - const struct unicam_fmt **ret_fmt) -{ - struct v4l2_pix_format *v4l2_format = &f->fmt.pix; - struct unicam_device *dev = node->dev; - const struct unicam_fmt *fmt; - int is_rgb; - - /* - * Default to the first format if the requested pixel format code isn't - * supported. - */ - fmt = find_format_by_pix(dev, v4l2_format->pixelformat); - if (!fmt) { - fmt = &formats[0]; - v4l2_format->pixelformat = fmt->fourcc; - } - - unicam_calc_format_size_bpl(dev, fmt, f); - - if (v4l2_format->field == V4L2_FIELD_ANY) - v4l2_format->field = V4L2_FIELD_NONE; - - if (ret_fmt) - *ret_fmt = fmt; - - if (v4l2_format->colorspace >= MAX_COLORSPACE || - !(fmt->valid_colorspaces & (1 << v4l2_format->colorspace))) { - v4l2_format->colorspace = __ffs(fmt->valid_colorspaces); - - v4l2_format->xfer_func = - V4L2_MAP_XFER_FUNC_DEFAULT(v4l2_format->colorspace); - v4l2_format->ycbcr_enc = - V4L2_MAP_YCBCR_ENC_DEFAULT(v4l2_format->colorspace); - is_rgb = v4l2_format->colorspace == V4L2_COLORSPACE_SRGB; - v4l2_format->quantization = - V4L2_MAP_QUANTIZATION_DEFAULT(is_rgb, - v4l2_format->colorspace, - v4l2_format->ycbcr_enc); - } - - unicam_dbg(3, dev, "%s: %08x %ux%u (bytesperline %u sizeimage %u)\n", - __func__, v4l2_format->pixelformat, - v4l2_format->width, v4l2_format->height, - v4l2_format->bytesperline, v4l2_format->sizeimage); -} - -static int unicam_mc_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct unicam_node *node = video_drvdata(file); - - unicam_mc_try_fmt(node, f, NULL); - return 0; -} - -static int unicam_mc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - const struct unicam_fmt *fmt; - - if (vb2_is_busy(&node->buffer_queue)) { - unicam_dbg(3, dev, "%s device busy\n", __func__); - return -EBUSY; - } - - unicam_mc_try_fmt(node, f, &fmt); - - node->v_fmt = *f; - node->fmt = fmt; - - return 0; -} - -static int unicam_mc_enum_framesizes(struct file *file, void *fh, - struct v4l2_frmsizeenum *fsize) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - - if (fsize->index > 0) - return -EINVAL; - - if (!find_format_by_pix(dev, fsize->pixel_format)) { - unicam_dbg(3, dev, "Invalid pixel format 0x%08x\n", - fsize->pixel_format); - return -EINVAL; - } - - fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; - fsize->stepwise.min_width = MIN_WIDTH; - fsize->stepwise.max_width = MAX_WIDTH; - fsize->stepwise.step_width = 1; - fsize->stepwise.min_height = MIN_HEIGHT; - fsize->stepwise.max_height = MAX_HEIGHT; - fsize->stepwise.step_height = 1; - - return 0; -} - -static int unicam_mc_enum_fmt_meta_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - int i, j; - - for (i = 0, j = 0; i < ARRAY_SIZE(formats); i++) { - if (f->mbus_code && formats[i].code != f->mbus_code) - continue; - if (!formats[i].metadata_fmt) - continue; - - if (formats[i].fourcc) { - if (j == f->index) { - f->pixelformat = formats[i].fourcc; - f->type = V4L2_BUF_TYPE_META_CAPTURE; - return 0; - } - j++; - } - } - - return -EINVAL; -} - -static int unicam_mc_g_fmt_meta_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct unicam_node *node = video_drvdata(file); - - if (node->pad_id != METADATA_PAD) - return -EINVAL; - - *f = node->v_fmt; - - return 0; -} - -static int unicam_mc_try_fmt_meta_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct unicam_node *node = video_drvdata(file); - - if (node->pad_id != METADATA_PAD) - return -EINVAL; - - f->fmt.meta.dataformat = V4L2_META_FMT_SENSOR_DATA; - - return 0; -} - -static int unicam_mc_s_fmt_meta_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct unicam_node *node = video_drvdata(file); - - if (node->pad_id != METADATA_PAD) - return -EINVAL; - - unicam_mc_try_fmt_meta_cap(file, priv, f); - - node->v_fmt = *f; - - return 0; -} - -static const struct v4l2_ioctl_ops unicam_mc_ioctl_ops = { - .vidioc_querycap = unicam_querycap, - .vidioc_enum_fmt_vid_cap = unicam_mc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = unicam_mc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = unicam_mc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = unicam_mc_s_fmt_vid_cap, - - .vidioc_enum_fmt_meta_cap = unicam_mc_enum_fmt_meta_cap, - .vidioc_g_fmt_meta_cap = unicam_mc_g_fmt_meta_cap, - .vidioc_try_fmt_meta_cap = unicam_mc_try_fmt_meta_cap, - .vidioc_s_fmt_meta_cap = unicam_mc_s_fmt_meta_cap, - - .vidioc_enum_framesizes = unicam_mc_enum_framesizes, - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_create_bufs = vb2_ioctl_create_bufs, - .vidioc_prepare_buf = vb2_ioctl_prepare_buf, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_expbuf = vb2_ioctl_expbuf, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - - .vidioc_log_status = unicam_log_status, - .vidioc_subscribe_event = unicam_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static int -unicam_mc_subdev_link_validate_get_format(struct media_pad *pad, - struct v4l2_subdev_format *fmt) -{ - if (is_media_entity_v4l2_subdev(pad->entity)) { - struct v4l2_subdev *sd = - media_entity_to_v4l2_subdev(pad->entity); - - fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; - fmt->pad = pad->index; - return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt); - } - - return -EINVAL; -} - -static int unicam_mc_video_link_validate(struct media_link *link) -{ - struct video_device *vd = container_of(link->sink->entity, - struct video_device, entity); - struct unicam_node *node = container_of(vd, struct unicam_node, - video_dev); - struct unicam_device *unicam = node->dev; - struct v4l2_subdev_format source_fmt; - int ret; - - if (!media_entity_remote_source_pad_unique(link->sink->entity)) { - unicam_dbg(1, unicam, - "video node %s pad not connected\n", vd->name); - return -ENOTCONN; - } - - ret = unicam_mc_subdev_link_validate_get_format(link->source, - &source_fmt); - if (ret < 0) - return 0; - - if (node->pad_id == IMAGE_PAD) { - struct v4l2_pix_format *pix_fmt = &node->v_fmt.fmt.pix; - const struct unicam_fmt *fmt; - - if (source_fmt.format.width != pix_fmt->width || - source_fmt.format.height != pix_fmt->height) { - unicam_err(unicam, - "Wrong width or height %ux%u (remote pad set to %ux%u)\n", - pix_fmt->width, pix_fmt->height, - source_fmt.format.width, - source_fmt.format.height); - return -EINVAL; - } - - fmt = find_format_by_code(source_fmt.format.code); - - if (!fmt || (fmt->fourcc != pix_fmt->pixelformat && - fmt->repacked_fourcc != pix_fmt->pixelformat)) - return -EINVAL; - } else { - struct v4l2_meta_format *meta_fmt = &node->v_fmt.fmt.meta; - - if (source_fmt.format.width != meta_fmt->buffersize || - source_fmt.format.height != 1 || - source_fmt.format.code != MEDIA_BUS_FMT_SENSOR_DATA) { - unicam_err(unicam, - "Wrong metadata width/height/code %ux%u %08x (remote pad set to %ux%u %08x)\n", - meta_fmt->buffersize, 1, - MEDIA_BUS_FMT_SENSOR_DATA, - source_fmt.format.width, - source_fmt.format.height, - source_fmt.format.code); - return -EINVAL; - } - } - - return 0; -} - -static const struct media_entity_operations unicam_mc_entity_ops = { - .link_validate = unicam_mc_video_link_validate, -}; - -/* videobuf2 Operations */ - -static int unicam_queue_setup(struct vb2_queue *vq, - unsigned int *nbuffers, - unsigned int *nplanes, - unsigned int sizes[], - struct device *alloc_devs[]) -{ - struct unicam_node *node = vb2_get_drv_priv(vq); - struct unicam_device *dev = node->dev; - unsigned int size = node->pad_id == IMAGE_PAD ? - node->v_fmt.fmt.pix.sizeimage : - node->v_fmt.fmt.meta.buffersize; - - if (vq->num_buffers + *nbuffers < 3) - *nbuffers = 3 - vq->num_buffers; - - if (*nplanes) { - if (sizes[0] < size) { - unicam_err(dev, "sizes[0] %i < size %u\n", sizes[0], - size); - return -EINVAL; - } - size = sizes[0]; - } - - *nplanes = 1; - sizes[0] = size; - - return 0; -} - -static int unicam_buffer_prepare(struct vb2_buffer *vb) -{ - struct unicam_node *node = vb2_get_drv_priv(vb->vb2_queue); - struct unicam_device *dev = node->dev; - struct unicam_buffer *buf = to_unicam_buffer(vb); - unsigned long size; - - if (WARN_ON(!node->fmt)) - return -EINVAL; - - size = node->pad_id == IMAGE_PAD ? node->v_fmt.fmt.pix.sizeimage : - node->v_fmt.fmt.meta.buffersize; - if (vb2_plane_size(vb, 0) < size) { - unicam_err(dev, "data will not fit into plane (%lu < %lu)\n", - vb2_plane_size(vb, 0), size); - return -EINVAL; - } - - vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); - return 0; -} - -static void unicam_buffer_queue(struct vb2_buffer *vb) -{ - struct unicam_node *node = vb2_get_drv_priv(vb->vb2_queue); - struct unicam_buffer *buf = to_unicam_buffer(vb); - unsigned long flags; - - spin_lock_irqsave(&node->dma_queue_lock, flags); - list_add_tail(&buf->list, &node->dma_queue); - spin_unlock_irqrestore(&node->dma_queue_lock, flags); -} - -static void unicam_set_packing_config(struct unicam_device *dev) -{ - u32 pack, unpack; - u32 val; - - if (dev->node[IMAGE_PAD].v_fmt.fmt.pix.pixelformat == - dev->node[IMAGE_PAD].fmt->fourcc) { - unpack = UNICAM_PUM_NONE; - pack = UNICAM_PPM_NONE; - } else { - switch (dev->node[IMAGE_PAD].fmt->depth) { - case 8: - unpack = UNICAM_PUM_UNPACK8; - break; - case 10: - unpack = UNICAM_PUM_UNPACK10; - break; - case 12: - unpack = UNICAM_PUM_UNPACK12; - break; - case 14: - unpack = UNICAM_PUM_UNPACK14; - break; - case 16: - unpack = UNICAM_PUM_UNPACK16; - break; - default: - unpack = UNICAM_PUM_NONE; - break; - } - - /* Repacking is always to 16bpp */ - pack = UNICAM_PPM_PACK16; - } - - val = 0; - set_field(&val, unpack, UNICAM_PUM_MASK); - set_field(&val, pack, UNICAM_PPM_MASK); - reg_write(dev, UNICAM_IPIPE, val); -} - -static void unicam_cfg_image_id(struct unicam_device *dev) -{ - if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) { - /* CSI2 mode, hardcode VC 0 for now. */ - reg_write(dev, UNICAM_IDI0, - (0 << 6) | dev->node[IMAGE_PAD].fmt->csi_dt); - } else { - /* CCP2 mode */ - reg_write(dev, UNICAM_IDI0, - 0x80 | dev->node[IMAGE_PAD].fmt->csi_dt); - } -} - -static void unicam_enable_ed(struct unicam_device *dev) -{ - u32 val = reg_read(dev, UNICAM_DCS); - - set_field(&val, 2, UNICAM_EDL_MASK); - /* Do not wrap at the end of the embedded data buffer */ - set_field(&val, 0, UNICAM_DBOB); - - reg_write(dev, UNICAM_DCS, val); -} - -static void unicam_start_rx(struct unicam_device *dev, dma_addr_t *addr) -{ - int line_int_freq = dev->node[IMAGE_PAD].v_fmt.fmt.pix.height >> 2; - unsigned int size, i; - u32 val; - - if (line_int_freq < 128) - line_int_freq = 128; - - /* Enable lane clocks */ - val = 1; - for (i = 0; i < dev->active_data_lanes; i++) - val = val << 2 | 1; - clk_write(dev, val); - - /* Basic init */ - reg_write(dev, UNICAM_CTRL, UNICAM_MEM); - - /* Enable analogue control, and leave in reset. */ - val = UNICAM_AR; - set_field(&val, 7, UNICAM_CTATADJ_MASK); - set_field(&val, 7, UNICAM_PTATADJ_MASK); - reg_write(dev, UNICAM_ANA, val); - usleep_range(1000, 2000); - - /* Come out of reset */ - reg_write_field(dev, UNICAM_ANA, 0, UNICAM_AR); - - /* Peripheral reset */ - reg_write_field(dev, UNICAM_CTRL, 1, UNICAM_CPR); - reg_write_field(dev, UNICAM_CTRL, 0, UNICAM_CPR); - - reg_write_field(dev, UNICAM_CTRL, 0, UNICAM_CPE); - - /* Enable Rx control. */ - val = reg_read(dev, UNICAM_CTRL); - if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) { - set_field(&val, UNICAM_CPM_CSI2, UNICAM_CPM_MASK); - set_field(&val, UNICAM_DCM_STROBE, UNICAM_DCM_MASK); - } else { - set_field(&val, UNICAM_CPM_CCP2, UNICAM_CPM_MASK); - set_field(&val, dev->bus_flags, UNICAM_DCM_MASK); - } - /* Packet framer timeout */ - set_field(&val, 0xf, UNICAM_PFT_MASK); - set_field(&val, 128, UNICAM_OET_MASK); - reg_write(dev, UNICAM_CTRL, val); - - reg_write(dev, UNICAM_IHWIN, 0); - reg_write(dev, UNICAM_IVWIN, 0); - - /* AXI bus access QoS setup */ - val = reg_read(dev, UNICAM_PRI); - set_field(&val, 0, UNICAM_BL_MASK); - set_field(&val, 0, UNICAM_BS_MASK); - set_field(&val, 0xe, UNICAM_PP_MASK); - set_field(&val, 8, UNICAM_NP_MASK); - set_field(&val, 2, UNICAM_PT_MASK); - set_field(&val, 1, UNICAM_PE); - reg_write(dev, UNICAM_PRI, val); - - reg_write_field(dev, UNICAM_ANA, 0, UNICAM_DDL); - - val = UNICAM_FSIE | UNICAM_FEIE | UNICAM_IBOB; - set_field(&val, line_int_freq, UNICAM_LCIE_MASK); - reg_write(dev, UNICAM_ICTL, val); - reg_write(dev, UNICAM_STA, UNICAM_STA_MASK_ALL); - reg_write(dev, UNICAM_ISTA, UNICAM_ISTA_MASK_ALL); - - /* tclk_term_en */ - reg_write_field(dev, UNICAM_CLT, 2, UNICAM_CLT1_MASK); - /* tclk_settle */ - reg_write_field(dev, UNICAM_CLT, 6, UNICAM_CLT2_MASK); - /* td_term_en */ - reg_write_field(dev, UNICAM_DLT, 2, UNICAM_DLT1_MASK); - /* ths_settle */ - reg_write_field(dev, UNICAM_DLT, 6, UNICAM_DLT2_MASK); - /* trx_enable */ - reg_write_field(dev, UNICAM_DLT, 0, UNICAM_DLT3_MASK); - - reg_write_field(dev, UNICAM_CTRL, 0, UNICAM_SOE); - - /* Packet compare setup - required to avoid missing frame ends */ - val = 0; - set_field(&val, 1, UNICAM_PCE); - set_field(&val, 1, UNICAM_GI); - set_field(&val, 1, UNICAM_CPH); - set_field(&val, 0, UNICAM_PCVC_MASK); - set_field(&val, 1, UNICAM_PCDT_MASK); - reg_write(dev, UNICAM_CMP0, val); - - /* Enable clock lane and set up terminations */ - val = 0; - if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) { - /* CSI2 */ - set_field(&val, 1, UNICAM_CLE); - set_field(&val, 1, UNICAM_CLLPE); - if (!(dev->bus_flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK)) { - set_field(&val, 1, UNICAM_CLTRE); - set_field(&val, 1, UNICAM_CLHSE); - } - } else { - /* CCP2 */ - set_field(&val, 1, UNICAM_CLE); - set_field(&val, 1, UNICAM_CLHSE); - set_field(&val, 1, UNICAM_CLTRE); - } - reg_write(dev, UNICAM_CLK, val); - - /* - * Enable required data lanes with appropriate terminations. - * The same value needs to be written to UNICAM_DATn registers for - * the active lanes, and 0 for inactive ones. - */ - val = 0; - if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) { - /* CSI2 */ - set_field(&val, 1, UNICAM_DLE); - set_field(&val, 1, UNICAM_DLLPE); - if (!(dev->bus_flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK)) { - set_field(&val, 1, UNICAM_DLTRE); - set_field(&val, 1, UNICAM_DLHSE); - } - } else { - /* CCP2 */ - set_field(&val, 1, UNICAM_DLE); - set_field(&val, 1, UNICAM_DLHSE); - set_field(&val, 1, UNICAM_DLTRE); - } - reg_write(dev, UNICAM_DAT0, val); - - if (dev->active_data_lanes == 1) - val = 0; - reg_write(dev, UNICAM_DAT1, val); - - if (dev->max_data_lanes > 2) { - /* - * Registers UNICAM_DAT2 and UNICAM_DAT3 only valid if the - * instance supports more than 2 data lanes. - */ - if (dev->active_data_lanes == 2) - val = 0; - reg_write(dev, UNICAM_DAT2, val); - - if (dev->active_data_lanes == 3) - val = 0; - reg_write(dev, UNICAM_DAT3, val); - } - - reg_write(dev, UNICAM_IBLS, - dev->node[IMAGE_PAD].v_fmt.fmt.pix.bytesperline); - size = dev->node[IMAGE_PAD].v_fmt.fmt.pix.sizeimage; - unicam_wr_dma_addr(dev, addr[IMAGE_PAD], size, IMAGE_PAD); - unicam_set_packing_config(dev); - unicam_cfg_image_id(dev); - - val = reg_read(dev, UNICAM_MISC); - set_field(&val, 1, UNICAM_FL0); - set_field(&val, 1, UNICAM_FL1); - reg_write(dev, UNICAM_MISC, val); - - if (dev->node[METADATA_PAD].streaming && dev->sensor_embedded_data) { - size = dev->node[METADATA_PAD].v_fmt.fmt.meta.buffersize; - unicam_enable_ed(dev); - unicam_wr_dma_addr(dev, addr[METADATA_PAD], size, METADATA_PAD); - } - - /* Enable peripheral */ - reg_write_field(dev, UNICAM_CTRL, 1, UNICAM_CPE); - - /* Load image pointers */ - reg_write_field(dev, UNICAM_ICTL, 1, UNICAM_LIP_MASK); - - /* Load embedded data buffer pointers if needed */ - if (dev->node[METADATA_PAD].streaming && dev->sensor_embedded_data) - reg_write_field(dev, UNICAM_DCS, 1, UNICAM_LDP); -} - -static void unicam_disable(struct unicam_device *dev) -{ - /* Analogue lane control disable */ - reg_write_field(dev, UNICAM_ANA, 1, UNICAM_DDL); - - /* Stop the output engine */ - reg_write_field(dev, UNICAM_CTRL, 1, UNICAM_SOE); - - /* Disable the data lanes. */ - reg_write(dev, UNICAM_DAT0, 0); - reg_write(dev, UNICAM_DAT1, 0); - - if (dev->max_data_lanes > 2) { - reg_write(dev, UNICAM_DAT2, 0); - reg_write(dev, UNICAM_DAT3, 0); - } - - /* Peripheral reset */ - reg_write_field(dev, UNICAM_CTRL, 1, UNICAM_CPR); - usleep_range(50, 100); - reg_write_field(dev, UNICAM_CTRL, 0, UNICAM_CPR); - - /* Disable peripheral */ - reg_write_field(dev, UNICAM_CTRL, 0, UNICAM_CPE); - - /* Clear ED setup */ - reg_write(dev, UNICAM_DCS, 0); - - /* Disable all lane clocks */ - clk_write(dev, 0); -} - -static void unicam_return_buffers(struct unicam_node *node, - enum vb2_buffer_state state) -{ - struct unicam_buffer *buf, *tmp; - unsigned long flags; - - spin_lock_irqsave(&node->dma_queue_lock, flags); - list_for_each_entry_safe(buf, tmp, &node->dma_queue, list) { - list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, state); - } - - if (node->cur_frm) - vb2_buffer_done(&node->cur_frm->vb.vb2_buf, - state); - if (node->next_frm && node->cur_frm != node->next_frm) - vb2_buffer_done(&node->next_frm->vb.vb2_buf, - state); - - node->cur_frm = NULL; - node->next_frm = NULL; - spin_unlock_irqrestore(&node->dma_queue_lock, flags); -} - -static int unicam_start_streaming(struct vb2_queue *vq, unsigned int count) -{ - struct unicam_node *node = vb2_get_drv_priv(vq); - struct unicam_device *dev = node->dev; - dma_addr_t buffer_addr[MAX_NODES] = { 0 }; - unsigned long flags; - unsigned int i; - int ret; - - node->streaming = true; - if (!(dev->node[IMAGE_PAD].open && dev->node[IMAGE_PAD].streaming && - (!dev->node[METADATA_PAD].open || - dev->node[METADATA_PAD].streaming))) { - /* - * Metadata pad must be enabled before image pad if it is - * wanted. - */ - unicam_dbg(3, dev, "Not all nodes are streaming yet."); - return 0; - } - - dev->sequence = 0; - ret = unicam_runtime_get(dev); - if (ret < 0) { - unicam_dbg(3, dev, "unicam_runtime_get failed\n"); - goto err_streaming; - } - - ret = media_pipeline_start(dev->node[IMAGE_PAD].video_dev.entity.pads, - &dev->node[IMAGE_PAD].pipe); - if (ret < 0) { - unicam_err(dev, "Failed to start media pipeline: %d\n", ret); - goto err_pm_put; - } - - dev->active_data_lanes = dev->max_data_lanes; - - if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) { - struct v4l2_mbus_config mbus_config = { 0 }; - - ret = v4l2_subdev_call(dev->sensor, pad, get_mbus_config, - 0, &mbus_config); - if (ret < 0 && ret != -ENOIOCTLCMD) { - unicam_dbg(3, dev, "g_mbus_config failed\n"); - goto error_pipeline; - } - - dev->active_data_lanes = mbus_config.bus.mipi_csi2.num_data_lanes; - if (!dev->active_data_lanes) - dev->active_data_lanes = dev->max_data_lanes; - if (dev->active_data_lanes > dev->max_data_lanes) { - unicam_err(dev, "Device has requested %u data lanes, which is >%u configured in DT\n", - dev->active_data_lanes, - dev->max_data_lanes); - ret = -EINVAL; - goto error_pipeline; - } - } - - unicam_dbg(1, dev, "Running with %u data lanes\n", - dev->active_data_lanes); - - ret = clk_set_min_rate(dev->vpu_clock, MIN_VPU_CLOCK_RATE); - if (ret) { - unicam_err(dev, "failed to set up VPU clock\n"); - goto error_pipeline; - } - - ret = clk_prepare_enable(dev->vpu_clock); - if (ret) { - unicam_err(dev, "Failed to enable VPU clock: %d\n", ret); - goto error_pipeline; - } - - ret = clk_set_rate(dev->clock, 100 * 1000 * 1000); - if (ret) { - unicam_err(dev, "failed to set up CSI clock\n"); - goto err_vpu_clock; - } - - ret = clk_prepare_enable(dev->clock); - if (ret) { - unicam_err(dev, "Failed to enable CSI clock: %d\n", ret); - goto err_vpu_clock; - } - - for (i = 0; i < ARRAY_SIZE(dev->node); i++) { - struct unicam_buffer *buf; - - if (!dev->node[i].streaming) - continue; - - spin_lock_irqsave(&dev->node[i].dma_queue_lock, flags); - buf = list_first_entry(&dev->node[i].dma_queue, - struct unicam_buffer, list); - dev->node[i].cur_frm = buf; - dev->node[i].next_frm = buf; - list_del(&buf->list); - spin_unlock_irqrestore(&dev->node[i].dma_queue_lock, flags); - - buffer_addr[i] = - vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); - } - - dev->frame_started = false; - unicam_start_rx(dev, buffer_addr); - - ret = v4l2_subdev_call(dev->sensor, video, s_stream, 1); - if (ret < 0) { - unicam_err(dev, "stream on failed in subdev\n"); - goto err_disable_unicam; - } - - dev->clocks_enabled = true; - return 0; - -err_disable_unicam: - unicam_disable(dev); - clk_disable_unprepare(dev->clock); -err_vpu_clock: - if (clk_set_min_rate(dev->vpu_clock, 0)) - unicam_err(dev, "failed to reset the VPU clock\n"); - clk_disable_unprepare(dev->vpu_clock); -error_pipeline: - if (node->pad_id == IMAGE_PAD) - media_pipeline_stop(dev->node[IMAGE_PAD].video_dev.entity.pads); -err_pm_put: - unicam_runtime_put(dev); -err_streaming: - unicam_return_buffers(node, VB2_BUF_STATE_QUEUED); - node->streaming = false; - - return ret; -} - -static void unicam_stop_streaming(struct vb2_queue *vq) -{ - struct unicam_node *node = vb2_get_drv_priv(vq); - struct unicam_device *dev = node->dev; - - node->streaming = false; - - if (node->pad_id == IMAGE_PAD) { - /* - * Stop streaming the sensor and disable the peripheral. - * We cannot continue streaming embedded data with the - * image pad disabled. - */ - if (v4l2_subdev_call(dev->sensor, video, s_stream, 0) < 0) - unicam_err(dev, "stream off failed in subdev\n"); - - unicam_disable(dev); - - media_pipeline_stop(node->video_dev.entity.pads); - - if (dev->clocks_enabled) { - if (clk_set_min_rate(dev->vpu_clock, 0)) - unicam_err(dev, "failed to reset the min VPU clock\n"); - - clk_disable_unprepare(dev->vpu_clock); - clk_disable_unprepare(dev->clock); - dev->clocks_enabled = false; - } - unicam_runtime_put(dev); - - } else if (node->pad_id == METADATA_PAD) { - /* - * Allow the hardware to spin in the dummy buffer. - * This is only really needed if the embedded data pad is - * disabled before the image pad. - */ - unicam_wr_dma_addr(dev, node->dummy_buf_dma_addr, 0, - METADATA_PAD); - } - - /* Clear all queued buffers for the node */ - unicam_return_buffers(node, VB2_BUF_STATE_ERROR); -} - - -static const struct vb2_ops unicam_video_qops = { - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, - .queue_setup = unicam_queue_setup, - .buf_prepare = unicam_buffer_prepare, - .buf_queue = unicam_buffer_queue, - .start_streaming = unicam_start_streaming, - .stop_streaming = unicam_stop_streaming, -}; - -/* - * unicam_v4l2_open : This function is based on the v4l2_fh_open helper - * function. It has been augmented to handle sensor subdevice power management, - */ -static int unicam_v4l2_open(struct file *file) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - int ret; - - mutex_lock(&node->lock); - - ret = v4l2_fh_open(file); - if (ret) { - unicam_err(dev, "v4l2_fh_open failed\n"); - goto unlock; - } - - node->open++; - - if (!v4l2_fh_is_singular_file(file)) - goto unlock; - - ret = v4l2_subdev_call(dev->sensor, core, s_power, 1); - if (ret < 0 && ret != -ENOIOCTLCMD) { - v4l2_fh_release(file); - node->open--; - goto unlock; - } - - ret = 0; - -unlock: - mutex_unlock(&node->lock); - return ret; -} - -static int unicam_v4l2_release(struct file *file) -{ - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - struct v4l2_subdev *sd = dev->sensor; - bool fh_singular; - int ret; - - mutex_lock(&node->lock); - - fh_singular = v4l2_fh_is_singular_file(file); - - ret = _vb2_fop_release(file, NULL); - - if (fh_singular) - v4l2_subdev_call(sd, core, s_power, 0); - - node->open--; - mutex_unlock(&node->lock); - - return ret; -} - -/* unicam capture driver file operations */ -static const struct v4l2_file_operations unicam_fops = { - .owner = THIS_MODULE, - .open = unicam_v4l2_open, - .release = unicam_v4l2_release, - .read = vb2_fop_read, - .poll = vb2_fop_poll, - .unlocked_ioctl = video_ioctl2, - .mmap = vb2_fop_mmap, -}; - -static int -unicam_async_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *subdev, - struct v4l2_async_connection *asd) -{ - struct unicam_device *unicam = to_unicam_device(notifier->v4l2_dev); - - if (unicam->sensor) { - unicam_info(unicam, "Rejecting subdev %s (Already set!!)", - subdev->name); - return 0; - } - - unicam->sensor = subdev; - unicam_dbg(1, unicam, "Using sensor %s for capture\n", subdev->name); - - return 0; -} - -static void unicam_release(struct kref *kref) -{ - struct unicam_device *unicam = - container_of(kref, struct unicam_device, kref); - - v4l2_ctrl_handler_free(&unicam->ctrl_handler); - media_device_cleanup(&unicam->mdev); - - if (unicam->sensor_state) - __v4l2_subdev_state_free(unicam->sensor_state); - - kfree(unicam); -} - -static void unicam_put(struct unicam_device *unicam) -{ - kref_put(&unicam->kref, unicam_release); -} - -static void unicam_get(struct unicam_device *unicam) -{ - kref_get(&unicam->kref); -} - -static void unicam_node_release(struct video_device *vdev) -{ - struct unicam_node *node = video_get_drvdata(vdev); - - unicam_put(node->dev); -} - -static int unicam_set_default_format(struct unicam_device *unicam, - struct unicam_node *node, - int pad_id, - const struct unicam_fmt **ret_fmt) -{ - struct v4l2_mbus_framefmt mbus_fmt = {0}; - const struct unicam_fmt *fmt; - int ret; - - if (pad_id == IMAGE_PAD) { - ret = __subdev_get_format(unicam, &mbus_fmt, pad_id); - if (ret) { - unicam_err(unicam, "Failed to get_format - ret %d\n", - ret); - return ret; - } - - fmt = find_format_by_code(mbus_fmt.code); - if (!fmt) { - /* - * Find the first format that the sensor and unicam both - * support - */ - fmt = get_first_supported_format(unicam); - - if (fmt) { - mbus_fmt.code = fmt->code; - ret = __subdev_set_format(unicam, &mbus_fmt, pad_id); - if (ret) - return -EINVAL; - } - } - if (mbus_fmt.field != V4L2_FIELD_NONE) { - /* Interlaced not supported - disable it now. */ - mbus_fmt.field = V4L2_FIELD_NONE; - ret = __subdev_set_format(unicam, &mbus_fmt, pad_id); - if (ret) - return -EINVAL; - } - - if (fmt) - node->v_fmt.fmt.pix.pixelformat = fmt->fourcc ? fmt->fourcc - : fmt->repacked_fourcc; - } else { - /* Fix this node format as embedded data. */ - fmt = find_format_by_code(MEDIA_BUS_FMT_SENSOR_DATA); - node->v_fmt.fmt.meta.dataformat = fmt->fourcc; - } - - *ret_fmt = fmt; - - return 0; -} - -static void unicam_mc_set_default_format(struct unicam_node *node, int pad_id) -{ - if (pad_id == IMAGE_PAD) { - struct v4l2_pix_format *pix_fmt = &node->v_fmt.fmt.pix; - - pix_fmt->width = 640; - pix_fmt->height = 480; - pix_fmt->field = V4L2_FIELD_NONE; - pix_fmt->colorspace = V4L2_COLORSPACE_SRGB; - pix_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; - pix_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE; - pix_fmt->xfer_func = V4L2_XFER_FUNC_SRGB; - pix_fmt->pixelformat = formats[0].fourcc; - unicam_calc_format_size_bpl(node->dev, &formats[0], - &node->v_fmt); - node->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - node->fmt = &formats[0]; - } else { - const struct unicam_fmt *fmt; - - /* Fix this node format as embedded data. */ - fmt = find_format_by_code(MEDIA_BUS_FMT_SENSOR_DATA); - node->v_fmt.fmt.meta.dataformat = fmt->fourcc; - node->fmt = fmt; - - node->v_fmt.fmt.meta.buffersize = UNICAM_EMBEDDED_SIZE; - node->embedded_lines = 1; - node->v_fmt.type = V4L2_BUF_TYPE_META_CAPTURE; - } -} - -static int register_node(struct unicam_device *unicam, struct unicam_node *node, - enum v4l2_buf_type type, int pad_id) -{ - struct video_device *vdev; - struct vb2_queue *q; - int ret; - - node->dev = unicam; - node->pad_id = pad_id; - - if (!unicam->mc_api) { - const struct unicam_fmt *fmt; - - ret = unicam_set_default_format(unicam, node, pad_id, &fmt); - if (ret) - return ret; - node->fmt = fmt; - /* Read current subdev format */ - if (fmt) - unicam_reset_format(node); - } else { - unicam_mc_set_default_format(node, pad_id); - } - - if (!unicam->mc_api && - v4l2_subdev_has_op(unicam->sensor, video, s_std)) { - v4l2_std_id tvnorms; - - if (WARN_ON(!v4l2_subdev_has_op(unicam->sensor, video, - g_tvnorms))) - /* - * Subdevice should not advertise s_std but not - * g_tvnorms - */ - return -EINVAL; - - ret = v4l2_subdev_call(unicam->sensor, video, - g_tvnorms, &tvnorms); - if (WARN_ON(ret)) - return -EINVAL; - node->video_dev.tvnorms |= tvnorms; - } - - spin_lock_init(&node->dma_queue_lock); - mutex_init(&node->lock); - - vdev = &node->video_dev; - if (pad_id == IMAGE_PAD) { - if (!unicam->mc_api) { - /* Add controls from the subdevice */ - ret = v4l2_ctrl_add_handler(&unicam->ctrl_handler, - unicam->sensor->ctrl_handler, - NULL, - true); - if (ret < 0) - return ret; - } - - /* - * If the sensor subdevice has any controls, associate the node - * with the ctrl handler to allow access from userland. - */ - if (!list_empty(&unicam->ctrl_handler.ctrls)) - vdev->ctrl_handler = &unicam->ctrl_handler; - } - - q = &node->buffer_queue; - q->type = type; - q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; - q->drv_priv = node; - q->ops = &unicam_video_qops; - q->mem_ops = &vb2_dma_contig_memops; - q->buf_struct_size = sizeof(struct unicam_buffer); - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->lock = &node->lock; - q->min_buffers_needed = 1; - q->dev = &unicam->pdev->dev; - - ret = vb2_queue_init(q); - if (ret) { - unicam_err(unicam, "vb2_queue_init() failed\n"); - return ret; - } - - INIT_LIST_HEAD(&node->dma_queue); - - vdev->release = unicam_node_release; - vdev->fops = &unicam_fops; - vdev->ioctl_ops = unicam->mc_api ? &unicam_mc_ioctl_ops : - &unicam_ioctl_ops; - vdev->v4l2_dev = &unicam->v4l2_dev; - vdev->vfl_dir = VFL_DIR_RX; - vdev->queue = q; - vdev->lock = &node->lock; - vdev->device_caps = (pad_id == IMAGE_PAD) ? - V4L2_CAP_VIDEO_CAPTURE : V4L2_CAP_META_CAPTURE; - vdev->device_caps |= V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; - if (unicam->mc_api) { - vdev->device_caps |= V4L2_CAP_IO_MC; - vdev->entity.ops = &unicam_mc_entity_ops; - } - - /* Define the device names */ - snprintf(vdev->name, sizeof(vdev->name), "%s-%s", UNICAM_MODULE_NAME, - pad_id == IMAGE_PAD ? "image" : "embedded"); - - video_set_drvdata(vdev, node); - if (pad_id == IMAGE_PAD) - vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT; - node->pad.flags = MEDIA_PAD_FL_SINK; - media_entity_pads_init(&vdev->entity, 1, &node->pad); - - node->dummy_buf_cpu_addr = dma_alloc_coherent(&unicam->pdev->dev, - DUMMY_BUF_SIZE, - &node->dummy_buf_dma_addr, - GFP_KERNEL); - if (!node->dummy_buf_cpu_addr) { - unicam_err(unicam, "Unable to allocate dummy buffer.\n"); - return -ENOMEM; - } - if (!unicam->mc_api) { - if (pad_id == METADATA_PAD || - !v4l2_subdev_has_op(unicam->sensor, video, s_std)) { - v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_STD); - v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_STD); - v4l2_disable_ioctl(&node->video_dev, VIDIOC_ENUMSTD); - } - if (pad_id == METADATA_PAD || - !v4l2_subdev_has_op(unicam->sensor, video, querystd)) - v4l2_disable_ioctl(&node->video_dev, VIDIOC_QUERYSTD); - if (pad_id == METADATA_PAD || - !v4l2_subdev_has_op(unicam->sensor, video, s_dv_timings)) { - v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_EDID); - v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_EDID); - v4l2_disable_ioctl(&node->video_dev, - VIDIOC_DV_TIMINGS_CAP); - v4l2_disable_ioctl(&node->video_dev, - VIDIOC_G_DV_TIMINGS); - v4l2_disable_ioctl(&node->video_dev, - VIDIOC_S_DV_TIMINGS); - v4l2_disable_ioctl(&node->video_dev, - VIDIOC_ENUM_DV_TIMINGS); - v4l2_disable_ioctl(&node->video_dev, - VIDIOC_QUERY_DV_TIMINGS); - } - if (pad_id == METADATA_PAD || - !v4l2_subdev_has_op(unicam->sensor, pad, - enum_frame_interval)) - v4l2_disable_ioctl(&node->video_dev, - VIDIOC_ENUM_FRAMEINTERVALS); - if (pad_id == METADATA_PAD || - !v4l2_subdev_has_op(unicam->sensor, video, - g_frame_interval)) - v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_PARM); - if (pad_id == METADATA_PAD || - !v4l2_subdev_has_op(unicam->sensor, video, - s_frame_interval)) - v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_PARM); - - if (pad_id == METADATA_PAD || - !v4l2_subdev_has_op(unicam->sensor, pad, - enum_frame_size)) - v4l2_disable_ioctl(&node->video_dev, - VIDIOC_ENUM_FRAMESIZES); - - if (node->pad_id == METADATA_PAD || - !v4l2_subdev_has_op(unicam->sensor, pad, set_selection)) - v4l2_disable_ioctl(&node->video_dev, - VIDIOC_S_SELECTION); - - if (node->pad_id == METADATA_PAD || - !v4l2_subdev_has_op(unicam->sensor, pad, get_selection)) - v4l2_disable_ioctl(&node->video_dev, - VIDIOC_G_SELECTION); - } - - ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); - if (ret) { - unicam_err(unicam, "Unable to register video device %s\n", - vdev->name); - return ret; - } - - /* - * Acquire a reference to unicam, which will be released when the video - * device will be unregistered and userspace will have closed all open - * file handles. - */ - unicam_get(unicam); - node->registered = true; - - if (pad_id != METADATA_PAD || unicam->sensor_embedded_data) { - ret = media_create_pad_link(&unicam->sensor->entity, - node->src_pad_id, - &node->video_dev.entity, 0, - MEDIA_LNK_FL_ENABLED | - MEDIA_LNK_FL_IMMUTABLE); - if (ret) - unicam_err(unicam, "Unable to create pad link for %s\n", - vdev->name); - } - - return ret; -} - -static void unregister_nodes(struct unicam_device *unicam) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(unicam->node); i++) { - struct unicam_node *node = &unicam->node[i]; - - if (node->dummy_buf_cpu_addr) { - dma_free_coherent(&unicam->pdev->dev, DUMMY_BUF_SIZE, - node->dummy_buf_cpu_addr, - node->dummy_buf_dma_addr); - } - - if (node->registered) { - node->registered = false; - video_unregister_device(&node->video_dev); - } - } -} - -static int unicam_async_complete(struct v4l2_async_notifier *notifier) -{ - static struct lock_class_key key; - struct unicam_device *unicam = to_unicam_device(notifier->v4l2_dev); - unsigned int i, source_pads = 0; - int ret; - - unicam->v4l2_dev.notify = unicam_notify; - - unicam->sensor_state = __v4l2_subdev_state_alloc(unicam->sensor, - "unicam:async->lock", &key); - if (!unicam->sensor_state) - return -ENOMEM; - - for (i = 0; i < unicam->sensor->entity.num_pads; i++) { - if (unicam->sensor->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE) { - if (source_pads < MAX_NODES) { - unicam->node[source_pads].src_pad_id = i; - unicam_dbg(3, unicam, "source pad %u is index %u\n", - source_pads, i); - } - source_pads++; - } - } - if (!source_pads) { - unicam_err(unicam, "No source pads on sensor.\n"); - ret = -ENODEV; - goto unregister; - } - - ret = register_node(unicam, &unicam->node[IMAGE_PAD], - V4L2_BUF_TYPE_VIDEO_CAPTURE, IMAGE_PAD); - if (ret) { - unicam_err(unicam, "Unable to register image video device.\n"); - goto unregister; - } - - if (source_pads >= 2) { - unicam->sensor_embedded_data = true; - - ret = register_node(unicam, &unicam->node[METADATA_PAD], - V4L2_BUF_TYPE_META_CAPTURE, METADATA_PAD); - if (ret) { - unicam_err(unicam, "Unable to register metadata video device.\n"); - goto unregister; - } - } - - if (unicam->mc_api) - ret = v4l2_device_register_subdev_nodes(&unicam->v4l2_dev); - else - ret = v4l2_device_register_ro_subdev_nodes(&unicam->v4l2_dev); - if (ret) { - unicam_err(unicam, "Unable to register subdev nodes.\n"); - goto unregister; - } - - /* - * Release the initial reference, all references are now owned by the - * video devices. - */ - unicam_put(unicam); - return 0; - -unregister: - unregister_nodes(unicam); - unicam_put(unicam); - - return ret; -} - -static const struct v4l2_async_notifier_operations unicam_async_ops = { - .bound = unicam_async_bound, - .complete = unicam_async_complete, -}; - -static int of_unicam_connect_subdevs(struct unicam_device *dev) -{ - struct platform_device *pdev = dev->pdev; - struct v4l2_fwnode_endpoint ep = { }; - struct device_node *ep_node; - struct device_node *sensor_node; - unsigned int lane; - int ret = -EINVAL; - - if (of_property_read_u32(pdev->dev.of_node, "brcm,num-data-lanes", - &dev->max_data_lanes) < 0) { - unicam_err(dev, "number of data lanes not set\n"); - return -EINVAL; - } - - /* Get the local endpoint and remote device. */ - ep_node = of_graph_get_next_endpoint(pdev->dev.of_node, NULL); - if (!ep_node) { - unicam_dbg(3, dev, "can't get next endpoint\n"); - return -EINVAL; - } - - unicam_dbg(3, dev, "ep_node is %pOF\n", ep_node); - - sensor_node = of_graph_get_remote_port_parent(ep_node); - if (!sensor_node) { - unicam_dbg(3, dev, "can't get remote parent\n"); - goto cleanup_exit; - } - - unicam_dbg(1, dev, "found subdevice %pOF\n", sensor_node); - - /* Parse the local endpoint and validate its configuration. */ - v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), &ep); - - unicam_dbg(3, dev, "parsed local endpoint, bus_type %u\n", - ep.bus_type); - - dev->bus_type = ep.bus_type; - - switch (ep.bus_type) { - case V4L2_MBUS_CSI2_DPHY: - switch (ep.bus.mipi_csi2.num_data_lanes) { - case 1: - case 2: - case 4: - break; - - default: - unicam_err(dev, "subdevice %pOF: %u data lanes not supported\n", - sensor_node, - ep.bus.mipi_csi2.num_data_lanes); - goto cleanup_exit; - } - - for (lane = 0; lane < ep.bus.mipi_csi2.num_data_lanes; lane++) { - if (ep.bus.mipi_csi2.data_lanes[lane] != lane + 1) { - unicam_err(dev, "subdevice %pOF: data lanes reordering not supported\n", - sensor_node); - goto cleanup_exit; - } - } - - if (ep.bus.mipi_csi2.num_data_lanes > dev->max_data_lanes) { - unicam_err(dev, "subdevice requires %u data lanes when %u are supported\n", - ep.bus.mipi_csi2.num_data_lanes, - dev->max_data_lanes); - } - - dev->max_data_lanes = ep.bus.mipi_csi2.num_data_lanes; - dev->bus_flags = ep.bus.mipi_csi2.flags; - - break; - - case V4L2_MBUS_CCP2: - if (ep.bus.mipi_csi1.clock_lane != 0 || - ep.bus.mipi_csi1.data_lane != 1) { - unicam_err(dev, "subdevice %pOF: unsupported lanes configuration\n", - sensor_node); - goto cleanup_exit; - } - - dev->max_data_lanes = 1; - dev->bus_flags = ep.bus.mipi_csi1.strobe; - break; - - default: - /* Unsupported bus type */ - unicam_err(dev, "subdevice %pOF: unsupported bus type %u\n", - sensor_node, ep.bus_type); - goto cleanup_exit; - } - - unicam_dbg(3, dev, "subdevice %pOF: %s bus, %u data lanes, flags=0x%08x\n", - sensor_node, - dev->bus_type == V4L2_MBUS_CSI2_DPHY ? "CSI-2" : "CCP2", - dev->max_data_lanes, dev->bus_flags); - - /* Initialize and register the async notifier. */ - v4l2_async_nf_init(&dev->notifier, &dev->v4l2_dev); - dev->notifier.ops = &unicam_async_ops; - - dev->asd = v4l2_async_nf_add_fwnode(&dev->notifier, - of_fwnode_handle(sensor_node), - struct v4l2_async_connection); - if (IS_ERR(dev->asd)) { - unicam_err(dev, "Error adding subdevice: %d\n", ret); - goto cleanup_exit; - } - - ret = v4l2_async_nf_register(&dev->notifier); - if (ret) { - unicam_err(dev, "Error registering async notifier: %d\n", ret); - ret = -EINVAL; - } - -cleanup_exit: - of_node_put(sensor_node); - of_node_put(ep_node); - - return ret; -} - -static int unicam_probe(struct platform_device *pdev) -{ - struct unicam_device *unicam; - int ret; - - unicam = kzalloc(sizeof(*unicam), GFP_KERNEL); - if (!unicam) - return -ENOMEM; - - kref_init(&unicam->kref); - unicam->pdev = pdev; - - /* - * Adopt the current setting of the module parameter, and check if - * device tree requests it. - */ - unicam->mc_api = media_controller; - if (of_property_read_bool(pdev->dev.of_node, "brcm,media-controller")) - unicam->mc_api = true; - - unicam->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(unicam->base)) { - unicam_err(unicam, "Failed to get main io block\n"); - ret = PTR_ERR(unicam->base); - goto err_unicam_put; - } - - unicam->clk_gate_base = devm_platform_ioremap_resource(pdev, 1); - if (IS_ERR(unicam->clk_gate_base)) { - unicam_err(unicam, "Failed to get 2nd io block\n"); - ret = PTR_ERR(unicam->clk_gate_base); - goto err_unicam_put; - } - - unicam->clock = devm_clk_get(&pdev->dev, "lp"); - if (IS_ERR(unicam->clock)) { - unicam_err(unicam, "Failed to get lp clock\n"); - ret = PTR_ERR(unicam->clock); - goto err_unicam_put; - } - - unicam->vpu_clock = devm_clk_get(&pdev->dev, "vpu"); - if (IS_ERR(unicam->vpu_clock)) { - unicam_err(unicam, "Failed to get vpu clock\n"); - ret = PTR_ERR(unicam->vpu_clock); - goto err_unicam_put; - } - - unicam->sync_gpio = devm_gpiod_get_optional(&pdev->dev, "sync", - GPIOD_OUT_LOW); - - ret = platform_get_irq(pdev, 0); - if (ret <= 0) { - dev_err(&pdev->dev, "No IRQ resource\n"); - ret = -EINVAL; - goto err_unicam_put; - } - - ret = devm_request_irq(&pdev->dev, ret, unicam_isr, 0, - "unicam_capture0", unicam); - if (ret) { - dev_err(&pdev->dev, "Unable to request interrupt\n"); - ret = -EINVAL; - goto err_unicam_put; - } - - unicam->mdev.dev = &pdev->dev; - strscpy(unicam->mdev.model, UNICAM_MODULE_NAME, - sizeof(unicam->mdev.model)); - strscpy(unicam->mdev.serial, "", sizeof(unicam->mdev.serial)); - snprintf(unicam->mdev.bus_info, sizeof(unicam->mdev.bus_info), - "platform:%s", dev_name(&pdev->dev)); - unicam->mdev.hw_revision = 0; - - media_device_init(&unicam->mdev); - - unicam->v4l2_dev.mdev = &unicam->mdev; - - ret = v4l2_device_register(&pdev->dev, &unicam->v4l2_dev); - if (ret) { - unicam_err(unicam, - "Unable to register v4l2 device.\n"); - goto err_unicam_put; - } - - ret = media_device_register(&unicam->mdev); - if (ret < 0) { - unicam_err(unicam, - "Unable to register media-controller device.\n"); - goto err_v4l2_unregister; - } - - /* Reserve space for the controls */ - ret = v4l2_ctrl_handler_init(&unicam->ctrl_handler, 16); - if (ret < 0) - goto err_media_unregister; - - /* set the driver data in platform device */ - platform_set_drvdata(pdev, unicam); - - ret = of_unicam_connect_subdevs(unicam); - if (ret) { - dev_err(&pdev->dev, "Failed to connect subdevs\n"); - goto err_media_unregister; - } - - /* Enable the block power domain */ - pm_runtime_enable(&pdev->dev); - - return 0; - -err_media_unregister: - media_device_unregister(&unicam->mdev); -err_v4l2_unregister: - v4l2_device_unregister(&unicam->v4l2_dev); -err_unicam_put: - unicam_put(unicam); - - return ret; -} - -static int unicam_remove(struct platform_device *pdev) -{ - struct unicam_device *unicam = platform_get_drvdata(pdev); - - unicam_dbg(2, unicam, "%s\n", __func__); - - v4l2_async_nf_unregister(&unicam->notifier); - v4l2_device_unregister(&unicam->v4l2_dev); - media_device_unregister(&unicam->mdev); - unregister_nodes(unicam); - - pm_runtime_disable(&pdev->dev); - - return 0; -} - -static const struct of_device_id unicam_of_match[] = { - { .compatible = "brcm,bcm2835-unicam", }, - { /* sentinel */ }, -}; -MODULE_DEVICE_TABLE(of, unicam_of_match); - -static struct platform_driver unicam_driver = { - .probe = unicam_probe, - .remove = unicam_remove, - .driver = { - .name = UNICAM_MODULE_NAME, - .of_match_table = of_match_ptr(unicam_of_match), - }, -}; - -module_platform_driver(unicam_driver); - -MODULE_AUTHOR("Dave Stevenson "); -MODULE_DESCRIPTION("BCM2835 Unicam driver"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(UNICAM_VERSION); diff --git a/drivers/media/platform/bcm2835/Kconfig b/drivers/media/platform/broadcom/Kconfig similarity index 92% rename from drivers/media/platform/bcm2835/Kconfig rename to drivers/media/platform/broadcom/Kconfig index 8fa00f0b2ceab7..32b76ebfcd9a19 100644 --- a/drivers/media/platform/bcm2835/Kconfig +++ b/drivers/media/platform/broadcom/Kconfig @@ -1,17 +1,19 @@ -# Broadcom VideoCore4 V4L2 camera support +# SPDX-License-Identifier: GPL-2.0 config VIDEO_BCM2835_UNICAM tristate "Broadcom BCM283x/BCM271x Unicam video capture driver" - depends on VIDEO_DEV depends on ARCH_BCM2835 || COMPILE_TEST - select VIDEO_V4L2_SUBDEV_API + depends on COMMON_CLK && PM + depends on VIDEO_DEV select MEDIA_CONTROLLER - select VIDEOBUF2_DMA_CONTIG select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + select VIDEOBUF2_DMA_CONTIG help Say Y here to enable support for the BCM283x/BCM271x CSI-2 receiver. This is a V4L2 driver that controls the CSI-2 receiver directly, independently from the VC4 firmware. + This driver is mutually exclusive with the use of bcm2835-camera. The firmware will disable all access to the peripheral from within the firmware if it finds a DT node using it, and bcm2835-camera will diff --git a/drivers/media/platform/bcm2835/Makefile b/drivers/media/platform/broadcom/Makefile similarity index 60% rename from drivers/media/platform/bcm2835/Makefile rename to drivers/media/platform/broadcom/Makefile index a98aba03598abf..03d2045aba2e4d 100644 --- a/drivers/media/platform/bcm2835/Makefile +++ b/drivers/media/platform/broadcom/Makefile @@ -1,3 +1,3 @@ -# Makefile for BCM2835 Unicam driver +# SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_VIDEO_BCM2835_UNICAM) += bcm2835-unicam.o diff --git a/drivers/media/platform/bcm2835/vc4-regs-unicam.h b/drivers/media/platform/broadcom/bcm2835-unicam-regs.h similarity index 71% rename from drivers/media/platform/bcm2835/vc4-regs-unicam.h rename to drivers/media/platform/broadcom/bcm2835-unicam-regs.h index ae059a171d0fe0..fce6fa92707cac 100644 --- a/drivers/media/platform/bcm2835/vc4-regs-unicam.h +++ b/drivers/media/platform/broadcom/bcm2835-unicam-regs.h @@ -8,57 +8,59 @@ #ifndef VC4_REGS_UNICAM_H #define VC4_REGS_UNICAM_H +#include + /* * The following values are taken from files found within the code drop * made by Broadcom for the BCM21553 Graphics Driver, predominantly in * brcm_usrlib/dag/vmcsx/vcinclude/hardware_vc4.h. * They have been modified to be only the register offset. */ -#define UNICAM_CTRL 0x000 -#define UNICAM_STA 0x004 -#define UNICAM_ANA 0x008 -#define UNICAM_PRI 0x00c -#define UNICAM_CLK 0x010 -#define UNICAM_CLT 0x014 -#define UNICAM_DAT0 0x018 -#define UNICAM_DAT1 0x01c -#define UNICAM_DAT2 0x020 -#define UNICAM_DAT3 0x024 -#define UNICAM_DLT 0x028 -#define UNICAM_CMP0 0x02c -#define UNICAM_CMP1 0x030 -#define UNICAM_CAP0 0x034 -#define UNICAM_CAP1 0x038 -#define UNICAM_ICTL 0x100 -#define UNICAM_ISTA 0x104 -#define UNICAM_IDI0 0x108 -#define UNICAM_IPIPE 0x10c -#define UNICAM_IBSA0 0x110 -#define UNICAM_IBEA0 0x114 -#define UNICAM_IBLS 0x118 -#define UNICAM_IBWP 0x11c -#define UNICAM_IHWIN 0x120 -#define UNICAM_IHSTA 0x124 -#define UNICAM_IVWIN 0x128 -#define UNICAM_IVSTA 0x12c -#define UNICAM_ICC 0x130 -#define UNICAM_ICS 0x134 -#define UNICAM_IDC 0x138 -#define UNICAM_IDPO 0x13c -#define UNICAM_IDCA 0x140 -#define UNICAM_IDCD 0x144 -#define UNICAM_IDS 0x148 -#define UNICAM_DCS 0x200 -#define UNICAM_DBSA0 0x204 -#define UNICAM_DBEA0 0x208 -#define UNICAM_DBWP 0x20c -#define UNICAM_DBCTL 0x300 -#define UNICAM_IBSA1 0x304 -#define UNICAM_IBEA1 0x308 -#define UNICAM_IDI1 0x30c -#define UNICAM_DBSA1 0x310 -#define UNICAM_DBEA1 0x314 -#define UNICAM_MISC 0x400 +#define UNICAM_CTRL 0x000 +#define UNICAM_STA 0x004 +#define UNICAM_ANA 0x008 +#define UNICAM_PRI 0x00c +#define UNICAM_CLK 0x010 +#define UNICAM_CLT 0x014 +#define UNICAM_DAT0 0x018 +#define UNICAM_DAT1 0x01c +#define UNICAM_DAT2 0x020 +#define UNICAM_DAT3 0x024 +#define UNICAM_DLT 0x028 +#define UNICAM_CMP0 0x02c +#define UNICAM_CMP1 0x030 +#define UNICAM_CAP0 0x034 +#define UNICAM_CAP1 0x038 +#define UNICAM_ICTL 0x100 +#define UNICAM_ISTA 0x104 +#define UNICAM_IDI0 0x108 +#define UNICAM_IPIPE 0x10c +#define UNICAM_IBSA0 0x110 +#define UNICAM_IBEA0 0x114 +#define UNICAM_IBLS 0x118 +#define UNICAM_IBWP 0x11c +#define UNICAM_IHWIN 0x120 +#define UNICAM_IHSTA 0x124 +#define UNICAM_IVWIN 0x128 +#define UNICAM_IVSTA 0x12c +#define UNICAM_ICC 0x130 +#define UNICAM_ICS 0x134 +#define UNICAM_IDC 0x138 +#define UNICAM_IDPO 0x13c +#define UNICAM_IDCA 0x140 +#define UNICAM_IDCD 0x144 +#define UNICAM_IDS 0x148 +#define UNICAM_DCS 0x200 +#define UNICAM_DBSA0 0x204 +#define UNICAM_DBEA0 0x208 +#define UNICAM_DBWP 0x20c +#define UNICAM_DBCTL 0x300 +#define UNICAM_IBSA1 0x304 +#define UNICAM_IBEA1 0x308 +#define UNICAM_IDI1 0x30c +#define UNICAM_DBSA1 0x310 +#define UNICAM_DBEA1 0x314 +#define UNICAM_MISC 0x400 /* * The following bitmasks are from the kernel released by Broadcom @@ -113,18 +115,9 @@ #define UNICAM_DI BIT(24) #define UNICAM_STA_MASK_ALL \ - (UNICAM_DL + \ - UNICAM_SBE + \ - UNICAM_PBE + \ - UNICAM_HOE + \ - UNICAM_PLE + \ - UNICAM_SSC + \ - UNICAM_CRCE + \ - UNICAM_IFO + \ - UNICAM_OFO + \ - UNICAM_PS + \ - UNICAM_PI0 + \ - UNICAM_PI1) + (UNICAM_SBE | UNICAM_PBE | UNICAM_HOE | UNICAM_PLE | UNICAM_SSC | \ + UNICAM_CRCE | UNICAM_IFO | UNICAM_OFO | UNICAM_DL | UNICAM_PS | \ + UNICAM_PI0 | UNICAM_PI1) /* UNICAM_ANA Register */ #define UNICAM_APD BIT(0) @@ -165,7 +158,7 @@ #define UNICAM_DLFO BIT(28) #define UNICAM_DLSTE BIT(29) -#define UNICAM_DAT_MASK_ALL (UNICAM_DLSTE + UNICAM_DLFO) +#define UNICAM_DAT_MASK_ALL (UNICAM_DLSTE | UNICAM_DLFO) /* UNICAM_DLT Register */ #define UNICAM_DLT1_MASK GENMASK(7, 0) @@ -192,28 +185,28 @@ #define UNICAM_FEI BIT(1) #define UNICAM_LCI BIT(2) -#define UNICAM_ISTA_MASK_ALL (UNICAM_FSI + UNICAM_FEI + UNICAM_LCI) +#define UNICAM_ISTA_MASK_ALL (UNICAM_FSI | UNICAM_FEI | UNICAM_LCI) /* UNICAM_IPIPE Register */ #define UNICAM_PUM_MASK GENMASK(2, 0) - /* Unpacking modes */ - #define UNICAM_PUM_NONE 0 - #define UNICAM_PUM_UNPACK6 1 - #define UNICAM_PUM_UNPACK7 2 - #define UNICAM_PUM_UNPACK8 3 - #define UNICAM_PUM_UNPACK10 4 - #define UNICAM_PUM_UNPACK12 5 - #define UNICAM_PUM_UNPACK14 6 - #define UNICAM_PUM_UNPACK16 7 +/* Unpacking modes */ +#define UNICAM_PUM_NONE 0 +#define UNICAM_PUM_UNPACK6 1 +#define UNICAM_PUM_UNPACK7 2 +#define UNICAM_PUM_UNPACK8 3 +#define UNICAM_PUM_UNPACK10 4 +#define UNICAM_PUM_UNPACK12 5 +#define UNICAM_PUM_UNPACK14 6 +#define UNICAM_PUM_UNPACK16 7 #define UNICAM_DDM_MASK GENMASK(6, 3) #define UNICAM_PPM_MASK GENMASK(9, 7) - /* Packing modes */ - #define UNICAM_PPM_NONE 0 - #define UNICAM_PPM_PACK8 1 - #define UNICAM_PPM_PACK10 2 - #define UNICAM_PPM_PACK12 3 - #define UNICAM_PPM_PACK14 4 - #define UNICAM_PPM_PACK16 5 +/* Packing modes */ +#define UNICAM_PPM_NONE 0 +#define UNICAM_PPM_PACK8 1 +#define UNICAM_PPM_PACK10 2 +#define UNICAM_PPM_PACK12 3 +#define UNICAM_PPM_PACK14 4 +#define UNICAM_PPM_PACK16 5 #define UNICAM_DEM_MASK GENMASK(11, 10) #define UNICAM_DEBL_MASK GENMASK(14, 12) #define UNICAM_ICM_MASK GENMASK(16, 15) diff --git a/drivers/media/platform/broadcom/bcm2835-unicam.c b/drivers/media/platform/broadcom/bcm2835-unicam.c new file mode 100644 index 00000000000000..390e3318dd64a6 --- /dev/null +++ b/drivers/media/platform/broadcom/bcm2835-unicam.c @@ -0,0 +1,2802 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * BCM283x / BCM271x Unicam Capture Driver + * + * Copyright (C) 2017-2020 - Raspberry Pi (Trading) Ltd. + * Copyright (C) 2024 - Ideas on Board + * + * Dave Stevenson + * + * Based on TI am437x driver by + * Benoit Parrot + * Lad, Prabhakar + * + * and TI CAL camera interface driver by + * Benoit Parrot + * + * + * There are two camera drivers in the kernel for BCM283x - this one and + * bcm2835-camera (currently in staging). + * + * This driver directly controls the Unicam peripheral - there is no + * involvement with the VideoCore firmware. Unicam receives CSI-2 or CCP2 data + * and writes it into SDRAM. The only potential processing options are to + * repack Bayer data into an alternate format, and applying windowing. The + * repacking does not shift the data, so can repack V4L2_PIX_FMT_Sxxxx10P to + * V4L2_PIX_FMT_Sxxxx10, or V4L2_PIX_FMT_Sxxxx12P to V4L2_PIX_FMT_Sxxxx12, but + * not generically up to V4L2_PIX_FMT_Sxxxx16. Support for windowing may be + * added later. + * + * It should be possible to connect this driver to any sensor with a suitable + * output interface and V4L2 subdevice driver. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bcm2835-unicam-regs.h" + +#define UNICAM_MODULE_NAME "unicam" + +/* + * Unicam must request a minimum of 250Mhz from the VPU clock. + * Otherwise the input FIFOs overrun and cause image corruption. + */ +#define UNICAM_MIN_VPU_CLOCK_RATE (250 * 1000 * 1000) + +/* Unicam has an internal DMA alignment constraint of 16 bytes for each line. */ +#define UNICAM_DMA_BPL_ALIGNMENT 16 + +/* + * The image stride is stored in a 16 bit register, and needs to be aligned to + * the DMA constraint. As the ISP in the same SoC has a 32 bytes alignment + * constraint on its input, set the image stride alignment to 32 bytes here as + * well to avoid incompatible configurations. + */ +#define UNICAM_IMAGE_BPL_ALIGNMENT 32 +#define UNICAM_IMAGE_MAX_BPL ((1U << 16) - UNICAM_IMAGE_BPL_ALIGNMENT) + +/* + * Max width is therefore determined by the max stride divided by the number of + * bits per pixel. Take 32bpp as a worst case. No imposed limit on the height, + * so adopt a square image for want of anything better. + */ +#define UNICAM_IMAGE_MIN_WIDTH 16 +#define UNICAM_IMAGE_MIN_HEIGHT 16 +#define UNICAM_IMAGE_MAX_WIDTH (UNICAM_IMAGE_MAX_BPL / 4) +#define UNICAM_IMAGE_MAX_HEIGHT UNICAM_IMAGE_MAX_WIDTH + +/* + * There's no intrinsic limits on the width and height for embedded data. Use + * the same maximum values as for the image, to avoid overflows in the image + * size computation. + */ +#define UNICAM_META_MIN_WIDTH 1 +#define UNICAM_META_MIN_HEIGHT 1 +#define UNICAM_META_MAX_WIDTH UNICAM_IMAGE_MAX_WIDTH +#define UNICAM_META_MAX_HEIGHT UNICAM_IMAGE_MAX_HEIGHT + +/* + * Size of the dummy buffer. Can be any size really, but the DMA + * allocation works in units of page sizes. + */ +#define UNICAM_DUMMY_BUF_SIZE PAGE_SIZE + +enum unicam_pad { + UNICAM_SD_PAD_SINK, + UNICAM_SD_PAD_SOURCE_IMAGE, + UNICAM_SD_PAD_SOURCE_METADATA, + UNICAM_SD_NUM_PADS +}; + +enum unicam_node_type { + UNICAM_IMAGE_NODE, + UNICAM_METADATA_NODE, + UNICAM_MAX_NODES +}; + +/* + * struct unicam_format_info - Unicam media bus format information + * @fourcc: V4L2 pixel format FCC identifier. 0 if n/a. + * @unpacked_fourcc: V4L2 pixel format FCC identifier if the data is expanded + * out to 16bpp. 0 if n/a. + * @code: V4L2 media bus format code. + * @depth: Bits per pixel as delivered from the source. + * @csi_dt: CSI data type. + * @unpack: PUM value when unpacking to @unpacked_fourcc + */ +struct unicam_format_info { + u32 fourcc; + u32 unpacked_fourcc; + u32 code; + u8 depth; + u8 csi_dt; + u8 unpack; +}; + +struct unicam_buffer { + struct vb2_v4l2_buffer vb; + struct list_head list; + dma_addr_t dma_addr; + unsigned int size; +}; + +static inline struct unicam_buffer *to_unicam_buffer(struct vb2_buffer *vb) +{ + return container_of(vb, struct unicam_buffer, vb.vb2_buf); +} + +struct unicam_node { + bool registered; + unsigned int id; + + /* Pointer to the current v4l2_buffer */ + struct unicam_buffer *cur_frm; + /* Pointer to the next v4l2_buffer */ + struct unicam_buffer *next_frm; + /* Used to store current pixel format */ + struct v4l2_format fmt; + /* Buffer queue used in video-buf */ + struct vb2_queue buffer_queue; + /* Queue of filled frames */ + struct list_head dma_queue; + /* IRQ lock for DMA queue */ + spinlock_t dma_queue_lock; + /* Identifies video device for this channel */ + struct video_device video_dev; + /* Pointer to the parent handle */ + struct unicam_device *dev; + struct media_pad pad; + /* + * Dummy buffer intended to be used by unicam + * if we have no other queued buffers to swap to. + */ + struct unicam_buffer dummy_buf; + void *dummy_buf_cpu_addr; +}; + +struct unicam_device { + struct kref kref; + + /* peripheral base address */ + void __iomem *base; + /* clock gating base address */ + void __iomem *clk_gate_base; + /* lp clock handle */ + struct clk *clock; + /* vpu clock handle */ + struct clk *vpu_clock; + /* V4l2 device */ + struct v4l2_device v4l2_dev; + struct media_device mdev; + + struct gpio_desc *sync_gpio; + + /* parent device */ + struct device *dev; + /* subdevice async notifier */ + struct v4l2_async_notifier notifier; + unsigned int sequence; + bool frame_started; + + /* Sensor node */ + struct { + struct v4l2_subdev *subdev; + struct media_pad *pad; + } sensor; + + /* Internal subdev */ + struct { + struct v4l2_subdev sd; + struct media_pad pads[UNICAM_SD_NUM_PADS]; + unsigned int enabled_streams; + } subdev; + + enum v4l2_mbus_type bus_type; + /* + * Stores bus.mipi_csi2.flags for CSI2 sensors, or + * bus.mipi_csi1.strobe for CCP2. + */ + unsigned int bus_flags; + unsigned int max_data_lanes; + + struct { + struct media_pipeline pipe; + unsigned int num_data_lanes; + unsigned int nodes; + } pipe; + + /* Lock used for the video devices of both nodes */ + struct mutex lock; + struct unicam_node node[UNICAM_MAX_NODES]; +}; + +static inline struct unicam_device * +notifier_to_unicam_device(struct v4l2_async_notifier *notifier) +{ + return container_of(notifier, struct unicam_device, notifier); +} + +static inline struct unicam_device * +sd_to_unicam_device(struct v4l2_subdev *sd) +{ + return container_of(sd, struct unicam_device, subdev.sd); +} + +static void unicam_release(struct kref *kref) +{ + struct unicam_device *unicam = + container_of(kref, struct unicam_device, kref); + + if (unicam->mdev.dev) + media_device_cleanup(&unicam->mdev); + + mutex_destroy(&unicam->lock); + kfree(unicam); +} + +static struct unicam_device *unicam_get(struct unicam_device *unicam) +{ + kref_get(&unicam->kref); + + return unicam; +} + +static void unicam_put(struct unicam_device *unicam) +{ + kref_put(&unicam->kref, unicam_release); +} + +/* ----------------------------------------------------------------------------- + * Misc helper functions + */ + +static inline bool unicam_sd_pad_is_source(u32 pad) +{ + /* Camera RX has 1 sink pad, and N source pads */ + return pad != UNICAM_SD_PAD_SINK; +} + +static inline bool is_metadata_node(struct unicam_node *node) +{ + return node->video_dev.device_caps & V4L2_CAP_META_CAPTURE; +} + +static inline bool is_image_node(struct unicam_node *node) +{ + return node->video_dev.device_caps & V4L2_CAP_VIDEO_CAPTURE; +} + +/* ----------------------------------------------------------------------------- + * Format data table and helper functions + */ + +static const struct v4l2_mbus_framefmt unicam_default_image_format = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_LIM_RANGE, + .xfer_func = V4L2_XFER_FUNC_SRGB, + .flags = 0, +}; + +static const struct v4l2_mbus_framefmt unicam_default_meta_format = { + .width = 640, + .height = 2, + .code = MEDIA_BUS_FMT_META_8, + .field = V4L2_FIELD_NONE, +}; + +static const struct unicam_format_info unicam_image_formats[] = { + /* YUV Formats */ + { + .fourcc = V4L2_PIX_FMT_YUYV, + .code = MEDIA_BUS_FMT_YUYV8_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_YUV422_8B, + }, { + .fourcc = V4L2_PIX_FMT_UYVY, + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_YUV422_8B, + }, { + .fourcc = V4L2_PIX_FMT_YVYU, + .code = MEDIA_BUS_FMT_YVYU8_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_YUV422_8B, + }, { + .fourcc = V4L2_PIX_FMT_VYUY, + .code = MEDIA_BUS_FMT_VYUY8_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_YUV422_8B, + }, { + /* RGB Formats */ + .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ + .code = MEDIA_BUS_FMT_RGB565_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_RGB565, + }, { + .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ + .code = MEDIA_BUS_FMT_RGB888_1X24, + .depth = 24, + .csi_dt = MIPI_CSI2_DT_RGB888, + }, { + .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ + .code = MEDIA_BUS_FMT_BGR888_1X24, + .depth = 24, + .csi_dt = MIPI_CSI2_DT_RGB888, + }, { + /* Bayer Formats */ + .fourcc = V4L2_PIX_FMT_SBGGR8, + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .depth = 8, + .csi_dt = MIPI_CSI2_DT_RAW8, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG8, + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .depth = 8, + .csi_dt = MIPI_CSI2_DT_RAW8, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .depth = 8, + .csi_dt = MIPI_CSI2_DT_RAW8, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB8, + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .depth = 8, + .csi_dt = MIPI_CSI2_DT_RAW8, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR10P, + .unpacked_fourcc = V4L2_PIX_FMT_SBGGR10, + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .depth = 10, + .csi_dt = MIPI_CSI2_DT_RAW10, + .unpack = UNICAM_PUM_UNPACK10, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG10P, + .unpacked_fourcc = V4L2_PIX_FMT_SGBRG10, + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .depth = 10, + .csi_dt = MIPI_CSI2_DT_RAW10, + .unpack = UNICAM_PUM_UNPACK10, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG10P, + .unpacked_fourcc = V4L2_PIX_FMT_SGRBG10, + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .depth = 10, + .csi_dt = MIPI_CSI2_DT_RAW10, + .unpack = UNICAM_PUM_UNPACK10, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB10P, + .unpacked_fourcc = V4L2_PIX_FMT_SRGGB10, + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .depth = 10, + .csi_dt = MIPI_CSI2_DT_RAW10, + .unpack = UNICAM_PUM_UNPACK10, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR12P, + .unpacked_fourcc = V4L2_PIX_FMT_SBGGR12, + .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .depth = 12, + .csi_dt = MIPI_CSI2_DT_RAW12, + .unpack = UNICAM_PUM_UNPACK12, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG12P, + .unpacked_fourcc = V4L2_PIX_FMT_SGBRG12, + .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .depth = 12, + .csi_dt = MIPI_CSI2_DT_RAW12, + .unpack = UNICAM_PUM_UNPACK12, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG12P, + .unpacked_fourcc = V4L2_PIX_FMT_SGRBG12, + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .depth = 12, + .csi_dt = MIPI_CSI2_DT_RAW12, + .unpack = UNICAM_PUM_UNPACK12, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB12P, + .unpacked_fourcc = V4L2_PIX_FMT_SRGGB12, + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .depth = 12, + .csi_dt = MIPI_CSI2_DT_RAW12, + .unpack = UNICAM_PUM_UNPACK12, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR14P, + .unpacked_fourcc = V4L2_PIX_FMT_SBGGR14, + .code = MEDIA_BUS_FMT_SBGGR14_1X14, + .depth = 14, + .csi_dt = MIPI_CSI2_DT_RAW14, + .unpack = UNICAM_PUM_UNPACK14, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG14P, + .unpacked_fourcc = V4L2_PIX_FMT_SGBRG14, + .code = MEDIA_BUS_FMT_SGBRG14_1X14, + .depth = 14, + .csi_dt = MIPI_CSI2_DT_RAW14, + .unpack = UNICAM_PUM_UNPACK14, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG14P, + .unpacked_fourcc = V4L2_PIX_FMT_SGRBG14, + .code = MEDIA_BUS_FMT_SGRBG14_1X14, + .depth = 14, + .csi_dt = MIPI_CSI2_DT_RAW14, + .unpack = UNICAM_PUM_UNPACK14, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB14P, + .unpacked_fourcc = V4L2_PIX_FMT_SRGGB14, + .code = MEDIA_BUS_FMT_SRGGB14_1X14, + .depth = 14, + .csi_dt = MIPI_CSI2_DT_RAW14, + .unpack = UNICAM_PUM_UNPACK14, + }, { + /* 16 bit Bayer formats could be supported. */ + + /* Greyscale formats */ + .fourcc = V4L2_PIX_FMT_GREY, + .code = MEDIA_BUS_FMT_Y8_1X8, + .depth = 8, + .csi_dt = MIPI_CSI2_DT_RAW8, + }, { + .fourcc = V4L2_PIX_FMT_Y10P, + .unpacked_fourcc = V4L2_PIX_FMT_Y10, + .code = MEDIA_BUS_FMT_Y10_1X10, + .depth = 10, + .csi_dt = MIPI_CSI2_DT_RAW10, + .unpack = UNICAM_PUM_UNPACK10, + }, { + .fourcc = V4L2_PIX_FMT_Y12P, + .unpacked_fourcc = V4L2_PIX_FMT_Y12, + .code = MEDIA_BUS_FMT_Y12_1X12, + .depth = 12, + .csi_dt = MIPI_CSI2_DT_RAW12, + .unpack = UNICAM_PUM_UNPACK12, + }, { + .fourcc = V4L2_PIX_FMT_Y14P, + .unpacked_fourcc = V4L2_PIX_FMT_Y14, + .code = MEDIA_BUS_FMT_Y14_1X14, + .depth = 14, + .csi_dt = MIPI_CSI2_DT_RAW14, + .unpack = UNICAM_PUM_UNPACK14, + }, +}; + +static const struct unicam_format_info unicam_meta_formats[] = { + { + .fourcc = V4L2_META_FMT_GENERIC_8, + .code = MEDIA_BUS_FMT_META_8, + .depth = 8, + }, { + .fourcc = V4L2_META_FMT_GENERIC_CSI2_10, + .code = MEDIA_BUS_FMT_META_10, + .depth = 10, + }, { + .fourcc = V4L2_META_FMT_GENERIC_CSI2_12, + .code = MEDIA_BUS_FMT_META_12, + .depth = 12, + }, { + .fourcc = V4L2_META_FMT_GENERIC_CSI2_14, + .code = MEDIA_BUS_FMT_META_14, + .depth = 14, + }, +}; + +/* Format setup functions */ +static const struct unicam_format_info * +unicam_find_format_by_code(u32 code, u32 pad) +{ + const struct unicam_format_info *formats; + unsigned int num_formats; + unsigned int i; + + if (pad == UNICAM_SD_PAD_SOURCE_IMAGE) { + formats = unicam_image_formats; + num_formats = ARRAY_SIZE(unicam_image_formats); + } else { + formats = unicam_meta_formats; + num_formats = ARRAY_SIZE(unicam_meta_formats); + } + + for (i = 0; i < num_formats; i++) { + if (formats[i].code == code) + return &formats[i]; + } + + return NULL; +} + +static const struct unicam_format_info * +unicam_find_format_by_fourcc(u32 fourcc, u32 pad) +{ + const struct unicam_format_info *formats; + unsigned int num_formats; + unsigned int i; + + if (pad == UNICAM_SD_PAD_SOURCE_IMAGE) { + formats = unicam_image_formats; + num_formats = ARRAY_SIZE(unicam_image_formats); + } else { + formats = unicam_meta_formats; + num_formats = ARRAY_SIZE(unicam_meta_formats); + } + + for (i = 0; i < num_formats; ++i) { + if (formats[i].fourcc == fourcc || + formats[i].unpacked_fourcc == fourcc) + return &formats[i]; + } + + return NULL; +} + +static void unicam_calc_image_size_bpl(struct unicam_device *unicam, + const struct unicam_format_info *fmtinfo, + struct v4l2_pix_format *pix) +{ + u32 min_bpl; + + v4l_bound_align_image(&pix->width, UNICAM_IMAGE_MIN_WIDTH, + UNICAM_IMAGE_MAX_WIDTH, 2, + &pix->height, UNICAM_IMAGE_MIN_HEIGHT, + UNICAM_IMAGE_MAX_HEIGHT, 0, 0); + + /* Unpacking always goes to 16bpp */ + if (pix->pixelformat == fmtinfo->unpacked_fourcc) + min_bpl = pix->width * 2; + else + min_bpl = pix->width * fmtinfo->depth / 8; + min_bpl = ALIGN(min_bpl, UNICAM_IMAGE_BPL_ALIGNMENT); + + pix->bytesperline = ALIGN(pix->bytesperline, UNICAM_IMAGE_BPL_ALIGNMENT); + pix->bytesperline = clamp_t(unsigned int, pix->bytesperline, min_bpl, + UNICAM_IMAGE_MAX_BPL); + + pix->sizeimage = pix->height * pix->bytesperline; +} + +static void unicam_calc_meta_size_bpl(struct unicam_device *unicam, + const struct unicam_format_info *fmtinfo, + struct v4l2_meta_format *meta) +{ + v4l_bound_align_image(&meta->width, UNICAM_META_MIN_WIDTH, + UNICAM_META_MAX_WIDTH, 0, + &meta->height, UNICAM_META_MIN_HEIGHT, + UNICAM_META_MAX_HEIGHT, 0, 0); + + meta->bytesperline = ALIGN(meta->width * fmtinfo->depth / 8, + UNICAM_DMA_BPL_ALIGNMENT); + meta->buffersize = meta->height * meta->bytesperline; +} + +/* ----------------------------------------------------------------------------- + * Hardware handling + */ + +static inline void unicam_clk_write(struct unicam_device *unicam, u32 val) +{ + /* Pass the CM_PASSWORD along with the value. */ + writel(val | 0x5a000000, unicam->clk_gate_base); +} + +static inline u32 unicam_reg_read(struct unicam_device *unicam, u32 offset) +{ + return readl(unicam->base + offset); +} + +static inline void unicam_reg_write(struct unicam_device *unicam, u32 offset, u32 val) +{ + writel(val, unicam->base + offset); +} + +static inline int unicam_get_field(u32 value, u32 mask) +{ + return (value & mask) >> __ffs(mask); +} + +static inline void unicam_set_field(u32 *valp, u32 field, u32 mask) +{ + u32 val = *valp; + + val &= ~mask; + val |= (field << __ffs(mask)) & mask; + *valp = val; +} + +static inline void unicam_reg_write_field(struct unicam_device *unicam, u32 offset, + u32 field, u32 mask) +{ + u32 val = unicam_reg_read(unicam, offset); + + unicam_set_field(&val, field, mask); + unicam_reg_write(unicam, offset, val); +} + +static void unicam_wr_dma_addr(struct unicam_node *node, + struct unicam_buffer *buf, unsigned int size) +{ + dma_addr_t endaddr = buf->dma_addr + size; + + if (node->id == UNICAM_IMAGE_NODE) { + unicam_reg_write(node->dev, UNICAM_IBSA0, buf->dma_addr); + unicam_reg_write(node->dev, UNICAM_IBEA0, endaddr); + } else { + unicam_reg_write(node->dev, UNICAM_DBSA0, buf->dma_addr); + unicam_reg_write(node->dev, UNICAM_DBEA0, endaddr); + } +} + +static unsigned int unicam_get_lines_done(struct unicam_device *unicam) +{ + struct unicam_node *node = &unicam->node[UNICAM_IMAGE_NODE]; + unsigned int stride = node->fmt.fmt.pix.bytesperline; + struct unicam_buffer *frm = node->cur_frm; + dma_addr_t cur_addr; + + if (!frm) + return 0; + + cur_addr = unicam_reg_read(unicam, UNICAM_IBWP); + return (unsigned int)(cur_addr - frm->dma_addr) / stride; +} + +static void unicam_schedule_next_buffer(struct unicam_node *node) +{ + struct unicam_buffer *buf; + + buf = list_first_entry(&node->dma_queue, struct unicam_buffer, list); + node->next_frm = buf; + list_del(&buf->list); + + unicam_wr_dma_addr(node, buf, buf->size); +} + +static void unicam_schedule_dummy_buffer(struct unicam_node *node) +{ + int node_id = is_image_node(node) ? UNICAM_IMAGE_NODE : UNICAM_METADATA_NODE; + + dev_dbg(node->dev->dev, "Scheduling dummy buffer for node %d\n", node_id); + /* + * Due to a HW bug causing buffer overruns in circular buffer mode under + * certain (not yet fully known) conditions, the dummy buffer allocation + * is set to a a single page size, but the hardware gets programmed with + * a buffer size of 0. + */ + unicam_wr_dma_addr(node, &node->dummy_buf, 0); + + node->next_frm = NULL; +} + +static void unicam_process_buffer_complete(struct unicam_node *node, + unsigned int sequence) +{ + node->cur_frm->vb.field = node->fmt.fmt.pix.field; + node->cur_frm->vb.sequence = sequence; + + vb2_buffer_done(&node->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE); +} + +static void unicam_queue_event_sof(struct unicam_device *unicam) +{ + struct unicam_node *node = &unicam->node[UNICAM_IMAGE_NODE]; + struct v4l2_event event = { + .type = V4L2_EVENT_FRAME_SYNC, + .u.frame_sync.frame_sequence = unicam->sequence, + }; + + v4l2_event_queue(&node->video_dev, &event); +} + +static irqreturn_t unicam_isr(int irq, void *dev) +{ + struct unicam_device *unicam = dev; + unsigned int lines_done = unicam_get_lines_done(dev); + unsigned int sequence = unicam->sequence; + unsigned int i; + u32 ista, sta; + bool fe; + u64 ts; + + sta = unicam_reg_read(unicam, UNICAM_STA); + /* Write value back to clear the interrupts */ + unicam_reg_write(unicam, UNICAM_STA, sta); + + ista = unicam_reg_read(unicam, UNICAM_ISTA); + /* Write value back to clear the interrupts */ + unicam_reg_write(unicam, UNICAM_ISTA, ista); + + dev_dbg(unicam->dev, "ISR: ISTA: 0x%X, STA: 0x%X, sequence %d, lines done %d\n", + ista, sta, sequence, lines_done); + + if (!(sta & (UNICAM_IS | UNICAM_PI0))) + return IRQ_HANDLED; + + /* + * Look for either the Frame End interrupt or the Packet Capture status + * to signal a frame end. + */ + fe = ista & UNICAM_FEI || sta & UNICAM_PI0; + + /* + * We must run the frame end handler first. If we have a valid next_frm + * and we get a simultaneout FE + FS interrupt, running the FS handler + * first would null out the next_frm ptr and we would have lost the + * buffer forever. + */ + if (fe) { + bool inc_seq = unicam->frame_started; + + if (unicam->sync_gpio) + gpiod_set_value(unicam->sync_gpio, 0); + + /* + * Ensure we have swapped buffers already as we can't + * stop the peripheral. If no buffer is available, use a + * dummy buffer to dump out frames until we get a new buffer + * to use. + */ + for (i = 0; i < ARRAY_SIZE(unicam->node); i++) { + struct unicam_node *node = &unicam->node[i]; + + if (!vb2_start_streaming_called(&node->buffer_queue)) + continue; + + /* + * If cur_frm == next_frm, it means we have not had + * a chance to swap buffers, likely due to having + * multiple interrupts occurring simultaneously (like FE + * + FS + LS). In this case, we cannot signal the buffer + * as complete, as the HW will reuse that buffer. + */ + if (node->cur_frm && node->cur_frm != node->next_frm) { + /* + * This condition checks if FE + FS for the same + * frame has occurred. In such cases, we cannot + * return out the frame, as no buffer handling + * or timestamping has yet been done as part of + * the FS handler. + */ + if (!node->cur_frm->vb.vb2_buf.timestamp) { + dev_dbg(unicam->v4l2_dev.dev, + "ISR: FE without FS, dropping frame\n"); + continue; + } + + unicam_process_buffer_complete(node, sequence); + node->cur_frm = node->next_frm; + node->next_frm = NULL; + inc_seq = true; + } else + node->cur_frm = node->next_frm; + } + + /* + * Increment the sequence number conditionally on either a FS + * having already occurred, or in the FE + FS condition as + * caught in the FE handler above. This ensures the sequence + * number corresponds to the frames generated by the sensor, not + * the frames dequeued to userland. + */ + if (inc_seq) { + unicam->sequence++; + unicam->frame_started = false; + } + } + + if (ista & UNICAM_FSI) { + /* + * Timestamp is to be when the first data byte was captured, + * aka frame start. + */ + ts = ktime_get_ns(); + + if (unicam->sync_gpio) + gpiod_set_value(unicam->sync_gpio, 1); + + for (i = 0; i < ARRAY_SIZE(unicam->node); i++) { + struct unicam_node *node = &unicam->node[i]; + + if (!vb2_start_streaming_called(&node->buffer_queue)) + continue; + + if (node->cur_frm) + node->cur_frm->vb.vb2_buf.timestamp = ts; + else + dev_dbg(unicam->v4l2_dev.dev, + "ISR: [%d] Dropping frame, buffer not available at FS\n", + i); + /* + * Set the next frame output to go to a dummy frame + * if no buffer currently queued. + */ + if (!node->next_frm || + node->next_frm == node->cur_frm) { + unicam_schedule_dummy_buffer(node); + } else if (unicam->node[i].cur_frm) { + /* + * Repeated FS without FE. Hardware will have + * swapped buffers, but the cur_frm doesn't + * contain valid data. Return cur_frm to the + * queue. + */ + spin_lock(&node->dma_queue_lock); + list_add_tail(&node->cur_frm->list, + &node->dma_queue); + spin_unlock(&node->dma_queue_lock); + node->cur_frm = node->next_frm; + node->next_frm = NULL; + } + } + + unicam_queue_event_sof(unicam); + unicam->frame_started = true; + } + + /* + * Cannot swap buffer at frame end, there may be a race condition + * where the HW does not actually swap it if the new frame has + * already started. + */ + if (ista & (UNICAM_FSI | UNICAM_LCI) && !fe) { + for (i = 0; i < ARRAY_SIZE(unicam->node); i++) { + struct unicam_node *node = &unicam->node[i]; + + if (!vb2_start_streaming_called(&node->buffer_queue)) + continue; + + spin_lock(&node->dma_queue_lock); + if (!list_empty(&node->dma_queue) && !node->next_frm) + unicam_schedule_next_buffer(node); + spin_unlock(&node->dma_queue_lock); + } + } + + return IRQ_HANDLED; +} + +static void unicam_set_packing_config(struct unicam_device *unicam, + const struct unicam_format_info *fmtinfo) +{ + struct unicam_node *node = &unicam->node[UNICAM_IMAGE_NODE]; + u32 pack, unpack; + u32 val; + + if (node->fmt.fmt.pix.pixelformat == fmtinfo->fourcc) { + unpack = UNICAM_PUM_NONE; + pack = UNICAM_PPM_NONE; + } else { + unpack = fmtinfo->unpack; + /* Repacking is always to 16bpp */ + pack = UNICAM_PPM_PACK16; + } + + val = 0; + unicam_set_field(&val, unpack, UNICAM_PUM_MASK); + unicam_set_field(&val, pack, UNICAM_PPM_MASK); + unicam_reg_write(unicam, UNICAM_IPIPE, val); +} + +static void unicam_cfg_image_id(struct unicam_device *unicam, u8 vc, u8 dt) +{ + if (unicam->bus_type == V4L2_MBUS_CSI2_DPHY) { + /* CSI2 mode */ + unicam_reg_write(unicam, UNICAM_IDI0, (vc << 6) | dt); + } else { + /* CCP2 mode */ + unicam_reg_write(unicam, UNICAM_IDI0, 0x80 | dt); + } +} + +static void unicam_enable_ed(struct unicam_device *unicam) +{ + u32 val = unicam_reg_read(unicam, UNICAM_DCS); + + unicam_set_field(&val, 2, UNICAM_EDL_MASK); + /* Do not wrap at the end of the embedded data buffer */ + unicam_set_field(&val, 0, UNICAM_DBOB); + + unicam_reg_write(unicam, UNICAM_DCS, val); +} + +static int unicam_get_image_vc_dt(struct unicam_device *unicam, + struct v4l2_subdev_state *state, + u8 *vc, u8 *dt) +{ + struct v4l2_mbus_frame_desc fd; + u32 stream; + int ret; + + ret = v4l2_subdev_routing_find_opposite_end(&state->routing, + UNICAM_SD_PAD_SOURCE_IMAGE, + 0, NULL, &stream); + if (ret) + return ret; + + ret = v4l2_subdev_call(unicam->sensor.subdev, pad, get_frame_desc, + unicam->sensor.pad->index, &fd); + if (ret) + return ret; + + /* Only CSI-2 supports DTs. */ + if (fd.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) + return -EINVAL; + + for (unsigned int i = 0; i < fd.num_entries; ++i) { + const struct v4l2_mbus_frame_desc_entry *fde = &fd.entry[i]; + + if (fde->stream == stream) { + *vc = fde->bus.csi2.vc; + *dt = fde->bus.csi2.dt; + return 0; + } + } + + return -EINVAL; +} + +static void unicam_start_rx(struct unicam_device *unicam, + struct v4l2_subdev_state *state) +{ + struct unicam_node *node = &unicam->node[UNICAM_IMAGE_NODE]; + const struct unicam_format_info *fmtinfo; + const struct v4l2_mbus_framefmt *fmt; + unsigned int line_int_freq; + u8 vc, dt; + u32 val; + int ret; + + fmt = v4l2_subdev_state_get_format(state, UNICAM_SD_PAD_SOURCE_IMAGE, 0); + fmtinfo = unicam_find_format_by_code(fmt->code, + UNICAM_SD_PAD_SOURCE_IMAGE); + if (WARN_ON(!fmtinfo)) + return; + + /* + * Enable lane clocks. The register is structured as follows: + * + * [9:8] - DAT3 + * [7:6] - DAT2 + * [5:4] - DAT1 + * [3:2] - DAT0 + * [1:0] - CLK + * + * Enabled lane must be set to b01, and disabled lanes to b00. The clock + * lane is always enabled. + */ + val = 0x155 & GENMASK(unicam->pipe.num_data_lanes * 2 + 1, 0); + unicam_clk_write(unicam, val); + + /* Basic init */ + unicam_reg_write(unicam, UNICAM_CTRL, UNICAM_MEM); + + /* Enable analogue control, and leave in reset. */ + val = UNICAM_AR; + unicam_set_field(&val, 7, UNICAM_CTATADJ_MASK); + unicam_set_field(&val, 7, UNICAM_PTATADJ_MASK); + unicam_reg_write(unicam, UNICAM_ANA, val); + usleep_range(1000, 2000); + + /* Come out of reset */ + unicam_reg_write_field(unicam, UNICAM_ANA, 0, UNICAM_AR); + + /* Peripheral reset */ + unicam_reg_write_field(unicam, UNICAM_CTRL, 1, UNICAM_CPR); + unicam_reg_write_field(unicam, UNICAM_CTRL, 0, UNICAM_CPR); + + unicam_reg_write_field(unicam, UNICAM_CTRL, 0, UNICAM_CPE); + + /* Enable Rx control. */ + val = unicam_reg_read(unicam, UNICAM_CTRL); + if (unicam->bus_type == V4L2_MBUS_CSI2_DPHY) { + unicam_set_field(&val, UNICAM_CPM_CSI2, UNICAM_CPM_MASK); + unicam_set_field(&val, UNICAM_DCM_STROBE, UNICAM_DCM_MASK); + } else { + unicam_set_field(&val, UNICAM_CPM_CCP2, UNICAM_CPM_MASK); + unicam_set_field(&val, unicam->bus_flags, UNICAM_DCM_MASK); + } + /* Packet framer timeout */ + unicam_set_field(&val, 0xf, UNICAM_PFT_MASK); + unicam_set_field(&val, 128, UNICAM_OET_MASK); + unicam_reg_write(unicam, UNICAM_CTRL, val); + + unicam_reg_write(unicam, UNICAM_IHWIN, 0); + unicam_reg_write(unicam, UNICAM_IVWIN, 0); + + /* AXI bus access QoS setup */ + val = unicam_reg_read(unicam, UNICAM_PRI); + unicam_set_field(&val, 0, UNICAM_BL_MASK); + unicam_set_field(&val, 0, UNICAM_BS_MASK); + unicam_set_field(&val, 0xe, UNICAM_PP_MASK); + unicam_set_field(&val, 8, UNICAM_NP_MASK); + unicam_set_field(&val, 2, UNICAM_PT_MASK); + unicam_set_field(&val, 1, UNICAM_PE); + unicam_reg_write(unicam, UNICAM_PRI, val); + + unicam_reg_write_field(unicam, UNICAM_ANA, 0, UNICAM_DDL); + + val = UNICAM_FSIE | UNICAM_FEIE | UNICAM_IBOB; + line_int_freq = max(fmt->height >> 2, 128); + unicam_set_field(&val, line_int_freq, UNICAM_LCIE_MASK); + unicam_reg_write(unicam, UNICAM_ICTL, val); + unicam_reg_write(unicam, UNICAM_STA, UNICAM_STA_MASK_ALL); + unicam_reg_write(unicam, UNICAM_ISTA, UNICAM_ISTA_MASK_ALL); + + /* tclk_term_en */ + unicam_reg_write_field(unicam, UNICAM_CLT, 2, UNICAM_CLT1_MASK); + /* tclk_settle */ + unicam_reg_write_field(unicam, UNICAM_CLT, 6, UNICAM_CLT2_MASK); + /* td_term_en */ + unicam_reg_write_field(unicam, UNICAM_DLT, 2, UNICAM_DLT1_MASK); + /* ths_settle */ + unicam_reg_write_field(unicam, UNICAM_DLT, 6, UNICAM_DLT2_MASK); + /* trx_enable */ + unicam_reg_write_field(unicam, UNICAM_DLT, 0, UNICAM_DLT3_MASK); + + unicam_reg_write_field(unicam, UNICAM_CTRL, 0, UNICAM_SOE); + + /* Packet compare setup - required to avoid missing frame ends */ + val = 0; + unicam_set_field(&val, 1, UNICAM_PCE); + unicam_set_field(&val, 1, UNICAM_GI); + unicam_set_field(&val, 1, UNICAM_CPH); + unicam_set_field(&val, 0, UNICAM_PCVC_MASK); + unicam_set_field(&val, 1, UNICAM_PCDT_MASK); + unicam_reg_write(unicam, UNICAM_CMP0, val); + + /* Enable clock lane and set up terminations */ + val = 0; + if (unicam->bus_type == V4L2_MBUS_CSI2_DPHY) { + /* CSI2 */ + unicam_set_field(&val, 1, UNICAM_CLE); + unicam_set_field(&val, 1, UNICAM_CLLPE); + if (!(unicam->bus_flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK)) { + unicam_set_field(&val, 1, UNICAM_CLTRE); + unicam_set_field(&val, 1, UNICAM_CLHSE); + } + } else { + /* CCP2 */ + unicam_set_field(&val, 1, UNICAM_CLE); + unicam_set_field(&val, 1, UNICAM_CLHSE); + unicam_set_field(&val, 1, UNICAM_CLTRE); + } + unicam_reg_write(unicam, UNICAM_CLK, val); + + /* + * Enable required data lanes with appropriate terminations. + * The same value needs to be written to UNICAM_DATn registers for + * the active lanes, and 0 for inactive ones. + */ + val = 0; + if (unicam->bus_type == V4L2_MBUS_CSI2_DPHY) { + /* CSI2 */ + unicam_set_field(&val, 1, UNICAM_DLE); + unicam_set_field(&val, 1, UNICAM_DLLPE); + if (!(unicam->bus_flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK)) { + unicam_set_field(&val, 1, UNICAM_DLTRE); + unicam_set_field(&val, 1, UNICAM_DLHSE); + } + } else { + /* CCP2 */ + unicam_set_field(&val, 1, UNICAM_DLE); + unicam_set_field(&val, 1, UNICAM_DLHSE); + unicam_set_field(&val, 1, UNICAM_DLTRE); + } + unicam_reg_write(unicam, UNICAM_DAT0, val); + + if (unicam->pipe.num_data_lanes == 1) + val = 0; + unicam_reg_write(unicam, UNICAM_DAT1, val); + + if (unicam->max_data_lanes > 2) { + /* + * Registers UNICAM_DAT2 and UNICAM_DAT3 only valid if the + * instance supports more than 2 data lanes. + */ + if (unicam->pipe.num_data_lanes == 2) + val = 0; + unicam_reg_write(unicam, UNICAM_DAT2, val); + + if (unicam->pipe.num_data_lanes == 3) + val = 0; + unicam_reg_write(unicam, UNICAM_DAT3, val); + } + + unicam_reg_write(unicam, UNICAM_IBLS, + node->fmt.fmt.pix.bytesperline); + unicam_wr_dma_addr(node, node->cur_frm, node->cur_frm->size); + unicam_set_packing_config(unicam, fmtinfo); + + ret = unicam_get_image_vc_dt(unicam, state, &vc, &dt); + if (ret) { + /* + * If the source doesn't support frame descriptors, default to + * VC 0 and use the DT corresponding to the format. + */ + vc = 0; + dt = fmtinfo->csi_dt; + } + + unicam_cfg_image_id(unicam, vc, dt); + + val = unicam_reg_read(unicam, UNICAM_MISC); + unicam_set_field(&val, 1, UNICAM_FL0); + unicam_set_field(&val, 1, UNICAM_FL1); + unicam_reg_write(unicam, UNICAM_MISC, val); + + /* Enable peripheral */ + unicam_reg_write_field(unicam, UNICAM_CTRL, 1, UNICAM_CPE); + + /* Load image pointers */ + unicam_reg_write_field(unicam, UNICAM_ICTL, 1, UNICAM_LIP_MASK); + + /* + * Enable trigger only for the first frame to + * sync correctly to the FS from the source. + */ + unicam_reg_write_field(unicam, UNICAM_ICTL, 1, UNICAM_TFC); +} + +static void unicam_start_metadata(struct unicam_device *unicam) +{ + struct unicam_node *node = &unicam->node[UNICAM_METADATA_NODE]; + + unicam_enable_ed(unicam); + unicam_wr_dma_addr(node, node->cur_frm, node->cur_frm->size); + unicam_reg_write_field(unicam, UNICAM_DCS, 1, UNICAM_LDP); +} + +static void unicam_disable(struct unicam_device *unicam) +{ + /* Analogue lane control disable */ + unicam_reg_write_field(unicam, UNICAM_ANA, 1, UNICAM_DDL); + + /* Stop the output engine */ + unicam_reg_write_field(unicam, UNICAM_CTRL, 1, UNICAM_SOE); + + /* Disable the data lanes. */ + unicam_reg_write(unicam, UNICAM_DAT0, 0); + unicam_reg_write(unicam, UNICAM_DAT1, 0); + + if (unicam->max_data_lanes > 2) { + unicam_reg_write(unicam, UNICAM_DAT2, 0); + unicam_reg_write(unicam, UNICAM_DAT3, 0); + } + + /* Peripheral reset */ + unicam_reg_write_field(unicam, UNICAM_CTRL, 1, UNICAM_CPR); + usleep_range(50, 100); + unicam_reg_write_field(unicam, UNICAM_CTRL, 0, UNICAM_CPR); + + /* Disable peripheral */ + unicam_reg_write_field(unicam, UNICAM_CTRL, 0, UNICAM_CPE); + + /* Clear ED setup */ + unicam_reg_write(unicam, UNICAM_DCS, 0); + + /* Disable all lane clocks */ + unicam_clk_write(unicam, 0); +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev operations + */ + +static int __unicam_subdev_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing) +{ + struct v4l2_subdev_route *route; + int ret; + + ret = v4l2_subdev_routing_validate(sd, routing, + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); + if (ret) + return ret; + + ret = v4l2_subdev_set_routing(sd, state, routing); + if (ret) + return ret; + + for_each_active_route(&state->routing, route) { + const struct v4l2_mbus_framefmt *def_fmt; + struct v4l2_mbus_framefmt *fmt; + + if (route->source_pad == UNICAM_SD_PAD_SOURCE_IMAGE) + def_fmt = &unicam_default_image_format; + else + def_fmt = &unicam_default_meta_format; + + fmt = v4l2_subdev_state_get_format(state, route->sink_pad, + route->sink_stream); + *fmt = *def_fmt; + fmt = v4l2_subdev_state_get_format(state, route->source_pad, + route->source_stream); + *fmt = *def_fmt; + } + + return 0; +} + +static int unicam_subdev_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_route routes[] = { + { + .sink_pad = UNICAM_SD_PAD_SINK, + .sink_stream = 0, + .source_pad = UNICAM_SD_PAD_SOURCE_IMAGE, + .source_stream = 0, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }, + }; + + struct v4l2_subdev_krouting routing = { + .len_routes = ARRAY_SIZE(routes), + .num_routes = ARRAY_SIZE(routes), + .routes = routes, + }; + + /* Initialize routing to single route to the fist source pad. */ + return __unicam_subdev_set_routing(sd, state, &routing); +} + +static int unicam_subdev_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) +{ + u32 pad, stream; + int ret; + + ret = v4l2_subdev_routing_find_opposite_end(&state->routing, + code->pad, code->stream, + &pad, &stream); + if (ret) + return ret; + + if (unicam_sd_pad_is_source(code->pad)) { + /* No transcoding, source and sink codes must match. */ + const struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_state_get_format(state, pad, stream); + if (!fmt) + return -EINVAL; + + if (code->index > 0) + return -EINVAL; + + code->code = fmt->code; + } else { + const struct unicam_format_info *formats; + unsigned int num_formats; + + if (pad == UNICAM_SD_PAD_SOURCE_IMAGE) { + formats = unicam_image_formats; + num_formats = ARRAY_SIZE(unicam_image_formats); + } else { + formats = unicam_meta_formats; + num_formats = ARRAY_SIZE(unicam_meta_formats); + } + + if (code->index >= num_formats) + return -EINVAL; + + code->code = formats[code->index].code; + } + + return 0; +} + +static int unicam_subdev_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_size_enum *fse) +{ + u32 pad, stream; + int ret; + + if (fse->index > 0) + return -EINVAL; + + ret = v4l2_subdev_routing_find_opposite_end(&state->routing, fse->pad, + fse->stream, &pad, + &stream); + if (ret) + return ret; + + if (unicam_sd_pad_is_source(fse->pad)) { + /* No transcoding, source and sink formats must match. */ + const struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_state_get_format(state, pad, stream); + if (!fmt) + return -EINVAL; + + if (fse->code != fmt->code) + return -EINVAL; + + fse->min_width = fmt->width; + fse->max_width = fmt->width; + fse->min_height = fmt->height; + fse->max_height = fmt->height; + } else { + const struct unicam_format_info *fmtinfo; + + fmtinfo = unicam_find_format_by_code(fse->code, pad); + if (!fmtinfo) + return -EINVAL; + + if (pad == UNICAM_SD_PAD_SOURCE_IMAGE) { + fse->min_width = UNICAM_IMAGE_MIN_WIDTH; + fse->max_width = UNICAM_IMAGE_MAX_WIDTH; + fse->min_height = UNICAM_IMAGE_MIN_HEIGHT; + fse->max_height = UNICAM_IMAGE_MAX_HEIGHT; + } else { + fse->min_width = UNICAM_META_MIN_WIDTH; + fse->max_width = UNICAM_META_MAX_WIDTH; + fse->min_height = UNICAM_META_MIN_HEIGHT; + fse->max_height = UNICAM_META_MAX_HEIGHT; + } + } + + return 0; +} + +static int unicam_subdev_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct unicam_device *unicam = sd_to_unicam_device(sd); + struct v4l2_mbus_framefmt *sink_format, *source_format; + const struct unicam_format_info *fmtinfo; + u32 source_pad, source_stream; + int ret; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && + unicam->subdev.enabled_streams) + return -EBUSY; + + /* No transcoding, source and sink formats must match. */ + if (unicam_sd_pad_is_source(format->pad)) + return v4l2_subdev_get_fmt(sd, state, format); + + /* + * Allowed formats for the stream on the sink pad depend on what source + * pad the stream is routed to. Find the corresponding source pad and + * use it to validate the media bus code. + */ + ret = v4l2_subdev_routing_find_opposite_end(&state->routing, + format->pad, format->stream, + &source_pad, &source_stream); + if (ret) + return ret; + + fmtinfo = unicam_find_format_by_code(format->format.code, source_pad); + if (!fmtinfo) { + fmtinfo = source_pad == UNICAM_SD_PAD_SOURCE_IMAGE + ? &unicam_image_formats[0] : &unicam_meta_formats[0]; + format->format.code = fmtinfo->code; + } + + if (source_pad == UNICAM_SD_PAD_SOURCE_IMAGE) { + format->format.width = clamp_t(unsigned int, + format->format.width, + UNICAM_IMAGE_MIN_WIDTH, + UNICAM_IMAGE_MAX_WIDTH); + format->format.height = clamp_t(unsigned int, + format->format.height, + UNICAM_IMAGE_MIN_HEIGHT, + UNICAM_IMAGE_MAX_HEIGHT); + format->format.field = V4L2_FIELD_NONE; + } else { + format->format.width = clamp_t(unsigned int, + format->format.width, + UNICAM_META_MIN_WIDTH, + UNICAM_META_MAX_WIDTH); + format->format.height = clamp_t(unsigned int, + format->format.height, + UNICAM_META_MIN_HEIGHT, + UNICAM_META_MAX_HEIGHT); + format->format.field = V4L2_FIELD_NONE; + + /* Colorspace don't apply to metadata. */ + format->format.colorspace = 0; + format->format.ycbcr_enc = 0; + format->format.quantization = 0; + format->format.xfer_func = 0; + } + + sink_format = v4l2_subdev_state_get_format(state, format->pad, + format->stream); + source_format = v4l2_subdev_state_get_format(state, source_pad, + source_stream); + *sink_format = format->format; + *source_format = format->format; + + return 0; +} + +static int unicam_subdev_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + struct unicam_device *unicam = sd_to_unicam_device(sd); + + if (which == V4L2_SUBDEV_FORMAT_ACTIVE && unicam->subdev.enabled_streams) + return -EBUSY; + + return __unicam_subdev_set_routing(sd, state, routing); +} + +static int unicam_sd_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct unicam_device *unicam = sd_to_unicam_device(sd); + u32 other_pad, other_stream; + int ret; + + if (!unicam->subdev.enabled_streams) { + /* Configure and start Unicam. */ + unicam->sequence = 0; + + if (unicam->pipe.nodes & BIT(UNICAM_METADATA_NODE)) + unicam_start_metadata(unicam); + + unicam->frame_started = false; + unicam_start_rx(unicam, state); + } + + ret = v4l2_subdev_routing_find_opposite_end(&state->routing, pad, 0, + &other_pad, &other_stream); + if (ret) + return ret; + + ret = v4l2_subdev_enable_streams(unicam->sensor.subdev, + unicam->sensor.pad->index, + BIT(other_stream)); + if (ret) { + dev_err(unicam->dev, "stream on failed in subdev\n"); + return ret; + } + + unicam->subdev.enabled_streams |= BIT(other_stream); + + return 0; +} + +static int unicam_sd_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct unicam_device *unicam = sd_to_unicam_device(sd); + u32 other_pad, other_stream; + int ret; + + ret = v4l2_subdev_routing_find_opposite_end(&state->routing, pad, 0, + &other_pad, &other_stream); + if (ret) + return ret; + + v4l2_subdev_disable_streams(unicam->sensor.subdev, + unicam->sensor.pad->index, + BIT(other_stream)); + + unicam->subdev.enabled_streams &= ~BIT(other_stream); + + if (!unicam->subdev.enabled_streams) + unicam_disable(unicam); + + return 0; +} + +static const struct v4l2_subdev_pad_ops unicam_subdev_pad_ops = { + .enum_mbus_code = unicam_subdev_enum_mbus_code, + .enum_frame_size = unicam_subdev_enum_frame_size, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = unicam_subdev_set_format, + .set_routing = unicam_subdev_set_routing, + .enable_streams = unicam_sd_enable_streams, + .disable_streams = unicam_sd_disable_streams, +}; + +static const struct v4l2_subdev_ops unicam_subdev_ops = { + .pad = &unicam_subdev_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops unicam_subdev_internal_ops = { + .init_state = unicam_subdev_init_state, +}; + +static const struct media_entity_operations unicam_subdev_media_ops = { + .link_validate = v4l2_subdev_link_validate, + .has_pad_interdep = v4l2_subdev_has_pad_interdep, +}; + +static int unicam_subdev_init(struct unicam_device *unicam) +{ + struct v4l2_subdev *sd = &unicam->subdev.sd; + int ret; + + v4l2_subdev_init(sd, &unicam_subdev_ops); + sd->internal_ops = &unicam_subdev_internal_ops; + v4l2_set_subdevdata(sd, unicam); + + sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + sd->entity.ops = &unicam_subdev_media_ops; + sd->dev = unicam->dev; + sd->owner = THIS_MODULE; + sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; + + strscpy(sd->name, "unicam", sizeof(sd->name)); + + unicam->subdev.pads[UNICAM_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + unicam->subdev.pads[UNICAM_SD_PAD_SOURCE_IMAGE].flags = MEDIA_PAD_FL_SOURCE; + unicam->subdev.pads[UNICAM_SD_PAD_SOURCE_METADATA].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(unicam->subdev.pads), + unicam->subdev.pads); + if (ret) { + dev_err(unicam->dev, "Failed to initialize media entity: %d\n", + ret); + return ret; + } + + ret = v4l2_subdev_init_finalize(sd); + if (ret) { + dev_err(unicam->dev, "Failed to initialize subdev: %d\n", ret); + goto err_entity; + } + + ret = v4l2_device_register_subdev(&unicam->v4l2_dev, sd); + if (ret) { + dev_err(unicam->dev, "Failed to register subdev: %d\n", ret); + goto err_subdev; + } + + return 0; + +err_subdev: + v4l2_subdev_cleanup(sd); +err_entity: + media_entity_cleanup(&sd->entity); + return ret; +} + +static void unicam_subdev_cleanup(struct unicam_device *unicam) +{ + v4l2_subdev_cleanup(&unicam->subdev.sd); + media_entity_cleanup(&unicam->subdev.sd.entity); +} + +/* ----------------------------------------------------------------------------- + * Videobuf2 queue operations + */ + +static int unicam_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct unicam_node *node = vb2_get_drv_priv(vq); + u32 size = is_image_node(node) ? node->fmt.fmt.pix.sizeimage + : node->fmt.fmt.meta.buffersize; + + if (*nplanes) { + if (sizes[0] < size) { + dev_dbg(node->dev->dev, "sizes[0] %i < size %u\n", + sizes[0], size); + return -EINVAL; + } + size = sizes[0]; + } + + *nplanes = 1; + sizes[0] = size; + + return 0; +} + +static int unicam_buffer_prepare(struct vb2_buffer *vb) +{ + struct unicam_node *node = vb2_get_drv_priv(vb->vb2_queue); + struct unicam_buffer *buf = to_unicam_buffer(vb); + u32 size = is_image_node(node) ? node->fmt.fmt.pix.sizeimage + : node->fmt.fmt.meta.buffersize; + + if (vb2_plane_size(vb, 0) < size) { + dev_dbg(node->dev->dev, + "data will not fit into plane (%lu < %u)\n", + vb2_plane_size(vb, 0), size); + return -EINVAL; + } + + buf->dma_addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); + buf->size = size; + + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); + + return 0; +} + +static void unicam_return_buffers(struct unicam_node *node, + enum vb2_buffer_state state) +{ + struct unicam_buffer *buf, *tmp; + + list_for_each_entry_safe(buf, tmp, &node->dma_queue, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, state); + } + + if (node->cur_frm) + vb2_buffer_done(&node->cur_frm->vb.vb2_buf, + state); + if (node->next_frm && node->cur_frm != node->next_frm) + vb2_buffer_done(&node->next_frm->vb.vb2_buf, + state); + + node->cur_frm = NULL; + node->next_frm = NULL; +} + +static int unicam_num_data_lanes(struct unicam_device *unicam) +{ + struct v4l2_mbus_config mbus_config = { 0 }; + unsigned int num_data_lanes; + int ret; + + if (unicam->bus_type != V4L2_MBUS_CSI2_DPHY) + return unicam->max_data_lanes; + + ret = v4l2_subdev_call(unicam->sensor.subdev, pad, get_mbus_config, + unicam->sensor.pad->index, &mbus_config); + if (ret == -ENOIOCTLCMD) + return unicam->max_data_lanes; + + if (ret < 0) { + dev_err(unicam->dev, "Failed to get mbus config: %d\n", ret); + return ret; + } + + num_data_lanes = mbus_config.bus.mipi_csi2.num_data_lanes; + + if (num_data_lanes != 1 && num_data_lanes != 2 && num_data_lanes != 4) { + dev_err(unicam->dev, + "Device %s has requested %u data lanes, invalid\n", + unicam->sensor.subdev->name, num_data_lanes); + return -EINVAL; + } + + if (num_data_lanes > unicam->max_data_lanes) { + dev_err(unicam->dev, + "Device %s has requested %u data lanes, >%u configured in DT\n", + unicam->sensor.subdev->name, num_data_lanes, + unicam->max_data_lanes); + return -EINVAL; + } + + return num_data_lanes; +} + +static int unicam_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct unicam_node *node = vb2_get_drv_priv(vq); + struct unicam_device *unicam = node->dev; + struct unicam_buffer *buf; + struct media_pipeline_pad_iter iter; + struct media_pad *pad; + unsigned long flags; + int ret; + + dev_dbg(unicam->dev, "Starting stream on %s device\n", + is_metadata_node(node) ? "metadata" : "image"); + + /* + * Start the pipeline. This validates all links, and populates the + * pipeline structure. + */ + ret = video_device_pipeline_start(&node->video_dev, &unicam->pipe.pipe); + if (ret < 0) { + dev_dbg(unicam->dev, "Failed to start media pipeline: %d\n", ret); + goto err_buffers; + } + + /* + * Determine which video nodes are included in the pipeline, and get the + * number of data lanes. + */ + if (unicam->pipe.pipe.start_count == 1) { + unicam->pipe.nodes = 0; + + media_pipeline_for_each_pad(&unicam->pipe.pipe, &iter, pad) { + if (pad->entity != &unicam->subdev.sd.entity) + continue; + + if (pad->index == UNICAM_SD_PAD_SOURCE_IMAGE) + unicam->pipe.nodes |= BIT(UNICAM_IMAGE_NODE); + else if (pad->index == UNICAM_SD_PAD_SOURCE_METADATA) + unicam->pipe.nodes |= BIT(UNICAM_METADATA_NODE); + } + + if (!(unicam->pipe.nodes & BIT(UNICAM_IMAGE_NODE))) { + dev_dbg(unicam->dev, + "Pipeline does not include image node\n"); + ret = -EPIPE; + goto err_pipeline; + } + + ret = unicam_num_data_lanes(unicam); + if (ret < 0) + goto err_pipeline; + + unicam->pipe.num_data_lanes = ret; + + dev_dbg(unicam->dev, "Running with %u data lanes, nodes %u\n", + unicam->pipe.num_data_lanes, unicam->pipe.nodes); + } + + /* Arm the node with the first buffer from the DMA queue. */ + spin_lock_irqsave(&node->dma_queue_lock, flags); + buf = list_first_entry(&node->dma_queue, struct unicam_buffer, list); + node->cur_frm = buf; + node->next_frm = buf; + list_del(&buf->list); + spin_unlock_irqrestore(&node->dma_queue_lock, flags); + + /* + * Wait for all the video devices in the pipeline to have been started + * before starting the hardware. In the general case, this would + * prevent capturing multiple streams independently. However, the + * Unicam DMA engines are not generic, they have been designed to + * capture image data and embedded data from the same camera sensor. + * Not only does the main use case not benefit from independent + * capture, it requires proper synchronization of the streams at start + * time. + */ + if (unicam->pipe.pipe.start_count < hweight32(unicam->pipe.nodes)) + return 0; + + ret = pm_runtime_resume_and_get(unicam->dev); + if (ret < 0) { + dev_err(unicam->dev, "PM runtime resume failed: %d\n", ret); + goto err_pipeline; + } + + /* Enable the streams on the source. */ + ret = v4l2_subdev_enable_streams(&unicam->subdev.sd, + UNICAM_SD_PAD_SOURCE_IMAGE, + BIT(0)); + if (ret < 0) { + dev_err(unicam->dev, "stream on failed in subdev\n"); + goto err_pm_put; + } + + if (unicam->pipe.nodes & BIT(UNICAM_METADATA_NODE)) { + ret = v4l2_subdev_enable_streams(&unicam->subdev.sd, + UNICAM_SD_PAD_SOURCE_METADATA, + BIT(0)); + if (ret < 0) { + dev_err(unicam->dev, "stream on failed in subdev\n"); + goto err_disable_streams; + } + } + + return 0; + +err_disable_streams: + v4l2_subdev_disable_streams(&unicam->subdev.sd, + UNICAM_SD_PAD_SOURCE_IMAGE, BIT(0)); +err_pm_put: + pm_runtime_put_sync(unicam->dev); +err_pipeline: + video_device_pipeline_stop(&node->video_dev); +err_buffers: + unicam_return_buffers(node, VB2_BUF_STATE_QUEUED); + return ret; +} + +static void unicam_stop_streaming(struct vb2_queue *vq) +{ + struct unicam_node *node = vb2_get_drv_priv(vq); + struct unicam_device *unicam = node->dev; + + /* Stop the hardware when the first video device gets stopped. */ + if (unicam->pipe.pipe.start_count == hweight32(unicam->pipe.nodes)) { + if (unicam->pipe.nodes & BIT(UNICAM_METADATA_NODE)) + v4l2_subdev_disable_streams(&unicam->subdev.sd, + UNICAM_SD_PAD_SOURCE_METADATA, + BIT(0)); + + v4l2_subdev_disable_streams(&unicam->subdev.sd, + UNICAM_SD_PAD_SOURCE_IMAGE, + BIT(0)); + + pm_runtime_put(unicam->dev); + } + + video_device_pipeline_stop(&node->video_dev); + + /* Clear all queued buffers for the node */ + unicam_return_buffers(node, VB2_BUF_STATE_ERROR); +} + +static void unicam_buffer_queue(struct vb2_buffer *vb) +{ + struct unicam_node *node = vb2_get_drv_priv(vb->vb2_queue); + struct unicam_buffer *buf = to_unicam_buffer(vb); + + spin_lock_irq(&node->dma_queue_lock); + list_add_tail(&buf->list, &node->dma_queue); + spin_unlock_irq(&node->dma_queue_lock); +} + +static const struct vb2_ops unicam_video_qops = { + .queue_setup = unicam_queue_setup, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .buf_prepare = unicam_buffer_prepare, + .start_streaming = unicam_start_streaming, + .stop_streaming = unicam_stop_streaming, + .buf_queue = unicam_buffer_queue, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 video device operations + */ + +static int unicam_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, UNICAM_MODULE_NAME, sizeof(cap->driver)); + strscpy(cap->card, UNICAM_MODULE_NAME, sizeof(cap->card)); + + cap->capabilities |= V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE; + + return 0; +} + +static int unicam_enum_fmt_vid(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + unsigned int index; + unsigned int i; + + for (i = 0, index = 0; i < ARRAY_SIZE(unicam_image_formats); i++) { + if (f->mbus_code && unicam_image_formats[i].code != f->mbus_code) + continue; + + if (index == f->index) { + f->pixelformat = unicam_image_formats[i].fourcc; + return 0; + } + + index++; + + if (!unicam_image_formats[i].unpacked_fourcc) + continue; + + if (index == f->index) { + f->pixelformat = unicam_image_formats[i].unpacked_fourcc; + return 0; + } + + index++; + } + + return -EINVAL; +} + +static int unicam_g_fmt_vid(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct unicam_node *node = video_drvdata(file); + + *f = node->fmt; + + return 0; +} + +static void __unicam_try_fmt_vid(struct unicam_node *node, + struct v4l2_pix_format *pix) +{ + const struct unicam_format_info *fmtinfo; + + /* + * Default to the first format if the requested pixel format code isn't + * supported. + */ + fmtinfo = unicam_find_format_by_fourcc(pix->pixelformat, + UNICAM_SD_PAD_SOURCE_IMAGE); + if (!fmtinfo) { + fmtinfo = &unicam_image_formats[0]; + pix->pixelformat = fmtinfo->fourcc; + } + + unicam_calc_image_size_bpl(node->dev, fmtinfo, pix); + + if (pix->field == V4L2_FIELD_ANY) + pix->field = V4L2_FIELD_NONE; +} + +static int unicam_try_fmt_vid(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct unicam_node *node = video_drvdata(file); + + __unicam_try_fmt_vid(node, &f->fmt.pix); + return 0; +} + +static int unicam_s_fmt_vid(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct unicam_node *node = video_drvdata(file); + + if (vb2_is_busy(&node->buffer_queue)) + return -EBUSY; + + __unicam_try_fmt_vid(node, &f->fmt.pix); + node->fmt = *f; + + return 0; +} + +static int unicam_enum_fmt_meta(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + unsigned int i, index; + + for (i = 0, index = 0; i < ARRAY_SIZE(unicam_meta_formats); i++) { + if (f->mbus_code && unicam_meta_formats[i].code != f->mbus_code) + continue; + + if (index == f->index) { + f->pixelformat = unicam_meta_formats[i].fourcc; + f->type = V4L2_BUF_TYPE_META_CAPTURE; + f->flags = V4L2_FMT_FLAG_META_LINE_BASED; + return 0; + } + + index++; + } + + return -EINVAL; +} + +static int unicam_g_fmt_meta(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct unicam_node *node = video_drvdata(file); + + f->fmt.meta = node->fmt.fmt.meta; + + return 0; +} + +static const struct unicam_format_info * +__unicam_try_fmt_meta(struct unicam_node *node, struct v4l2_meta_format *meta) +{ + const struct unicam_format_info *fmtinfo; + + /* + * Default to the first format if the requested pixel format code isn't + * supported. + */ + fmtinfo = unicam_find_format_by_fourcc(meta->dataformat, + UNICAM_SD_PAD_SOURCE_METADATA); + if (!fmtinfo) { + fmtinfo = &unicam_meta_formats[0]; + meta->dataformat = fmtinfo->fourcc; + } + + unicam_calc_meta_size_bpl(node->dev, fmtinfo, meta); + + return fmtinfo; +} + +static int unicam_try_fmt_meta(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct unicam_node *node = video_drvdata(file); + + __unicam_try_fmt_meta(node, &f->fmt.meta); + return 0; +} + +static int unicam_s_fmt_meta(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct unicam_node *node = video_drvdata(file); + + if (vb2_is_busy(&node->buffer_queue)) + return -EBUSY; + + __unicam_try_fmt_meta(node, &f->fmt.meta); + node->fmt = *f; + + return 0; +} + +static int unicam_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct unicam_node *node = video_drvdata(file); + int ret = -EINVAL; + + if (fsize->index > 0) + return ret; + + if (is_image_node(node)) { + if (!unicam_find_format_by_fourcc(fsize->pixel_format, + UNICAM_SD_PAD_SOURCE_IMAGE)) + return ret; + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise.min_width = UNICAM_IMAGE_MIN_WIDTH; + fsize->stepwise.max_width = UNICAM_IMAGE_MAX_WIDTH; + fsize->stepwise.step_width = 1; + fsize->stepwise.min_height = UNICAM_IMAGE_MIN_HEIGHT; + fsize->stepwise.max_height = UNICAM_IMAGE_MAX_HEIGHT; + fsize->stepwise.step_height = 1; + } else { + if (!unicam_find_format_by_fourcc(fsize->pixel_format, + UNICAM_SD_PAD_SOURCE_METADATA)) + return ret; + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise.min_width = UNICAM_META_MIN_WIDTH; + fsize->stepwise.max_width = UNICAM_META_MAX_WIDTH; + fsize->stepwise.step_width = 1; + fsize->stepwise.min_height = UNICAM_META_MIN_HEIGHT; + fsize->stepwise.max_height = UNICAM_META_MAX_HEIGHT; + fsize->stepwise.step_height = 1; + } + + return 0; +} + +static int unicam_log_status(struct file *file, void *fh) +{ + struct unicam_node *node = video_drvdata(file); + struct unicam_device *unicam = node->dev; + u32 reg; + + /* status for sub devices */ + v4l2_device_call_all(&unicam->v4l2_dev, 0, core, log_status); + + dev_info(unicam->dev, "-----Receiver status-----\n"); + dev_info(unicam->dev, "V4L2 width/height: %ux%u\n", + node->fmt.fmt.pix.width, node->fmt.fmt.pix.height); + dev_info(unicam->dev, "V4L2 format: %08x\n", + node->fmt.fmt.pix.pixelformat); + reg = unicam_reg_read(unicam, UNICAM_IPIPE); + dev_info(unicam->dev, "Unpacking/packing: %u / %u\n", + unicam_get_field(reg, UNICAM_PUM_MASK), + unicam_get_field(reg, UNICAM_PPM_MASK)); + dev_info(unicam->dev, "----Live data----\n"); + dev_info(unicam->dev, "Programmed stride: %4u\n", + unicam_reg_read(unicam, UNICAM_IBLS)); + dev_info(unicam->dev, "Detected resolution: %ux%u\n", + unicam_reg_read(unicam, UNICAM_IHSTA), + unicam_reg_read(unicam, UNICAM_IVSTA)); + dev_info(unicam->dev, "Write pointer: %08x\n", + unicam_reg_read(unicam, UNICAM_IBWP)); + + return 0; +} + +static int unicam_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_FRAME_SYNC: + return v4l2_event_subscribe(fh, sub, 2, NULL); + default: + return -EINVAL; + } +} + +static const struct v4l2_ioctl_ops unicam_ioctl_ops = { + .vidioc_querycap = unicam_querycap, + + .vidioc_enum_fmt_vid_cap = unicam_enum_fmt_vid, + .vidioc_g_fmt_vid_cap = unicam_g_fmt_vid, + .vidioc_try_fmt_vid_cap = unicam_try_fmt_vid, + .vidioc_s_fmt_vid_cap = unicam_s_fmt_vid, + + .vidioc_enum_fmt_meta_cap = unicam_enum_fmt_meta, + .vidioc_g_fmt_meta_cap = unicam_g_fmt_meta, + .vidioc_try_fmt_meta_cap = unicam_try_fmt_meta, + .vidioc_s_fmt_meta_cap = unicam_s_fmt_meta, + + .vidioc_enum_framesizes = unicam_enum_framesizes, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_log_status = unicam_log_status, + .vidioc_subscribe_event = unicam_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/* unicam capture driver file operations */ +static const struct v4l2_file_operations unicam_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, +}; + +static int unicam_video_link_validate(struct media_link *link) +{ + struct video_device *vdev = + media_entity_to_video_device(link->sink->entity); + struct v4l2_subdev *sd = + media_entity_to_v4l2_subdev(link->source->entity); + struct unicam_node *node = video_get_drvdata(vdev); + const u32 pad = is_image_node(node) ? UNICAM_SD_PAD_SOURCE_IMAGE + : UNICAM_SD_PAD_SOURCE_METADATA; + const struct v4l2_mbus_framefmt *format; + struct v4l2_subdev_state *state; + int ret = 0; + + state = v4l2_subdev_lock_and_get_active_state(sd); + + format = v4l2_subdev_state_get_format(state, pad, 0); + if (!format) { + ret = -EINVAL; + goto out; + } + + if (is_image_node(node)) { + const struct v4l2_pix_format *fmt = &node->fmt.fmt.pix; + const struct unicam_format_info *fmtinfo; + + fmtinfo = unicam_find_format_by_fourcc(fmt->pixelformat, + UNICAM_SD_PAD_SOURCE_IMAGE); + if (WARN_ON(!fmtinfo)) { + ret = -EPIPE; + goto out; + } + + if (fmtinfo->code != format->code || + fmt->height != format->height || + fmt->width != format->width || + fmt->field != format->field) { + dev_dbg(node->dev->dev, + "image: (%u x %u) 0x%08x %s != (%u x %u) 0x%08x %s\n", + fmt->width, fmt->height, fmtinfo->code, + v4l2_field_names[fmt->field], + format->width, format->height, format->code, + v4l2_field_names[format->field]); + ret = -EPIPE; + } + } else { + const struct v4l2_meta_format *fmt = &node->fmt.fmt.meta; + + const struct unicam_format_info *fmtinfo; + + fmtinfo = unicam_find_format_by_fourcc(fmt->dataformat, + UNICAM_SD_PAD_SOURCE_METADATA); + if (WARN_ON(!fmtinfo)) { + ret = -EPIPE; + goto out; + } + + if (fmtinfo->code != format->code || + fmt->height != format->height || + fmt->width != format->width) { + dev_dbg(node->dev->dev, + "meta: (%u x %u) 0x%04x != (%u x %u) 0x%04x\n", + fmt->width, fmt->height, fmtinfo->code, + format->width, format->height, format->code); + ret = -EPIPE; + } + } + +out: + v4l2_subdev_unlock_state(state); + return ret; +} + +static const struct media_entity_operations unicam_video_media_ops = { + .link_validate = unicam_video_link_validate, +}; + +static void unicam_node_release(struct video_device *vdev) +{ + struct unicam_node *node = video_get_drvdata(vdev); + + unicam_put(node->dev); +} + +static void unicam_set_default_format(struct unicam_node *node) +{ + if (is_image_node(node)) { + struct v4l2_pix_format *fmt = &node->fmt.fmt.pix; + const struct unicam_format_info *fmtinfo = + &unicam_image_formats[0]; + + node->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + v4l2_fill_pix_format(fmt, &unicam_default_image_format); + fmt->pixelformat = fmtinfo->fourcc; + unicam_calc_image_size_bpl(node->dev, fmtinfo, fmt); + } else { + struct v4l2_meta_format *fmt = &node->fmt.fmt.meta; + const struct unicam_format_info *fmtinfo = + &unicam_meta_formats[0]; + + node->fmt.type = V4L2_BUF_TYPE_META_CAPTURE; + + fmt->dataformat = fmtinfo->fourcc; + fmt->width = unicam_default_meta_format.width; + fmt->height = unicam_default_meta_format.height; + unicam_calc_meta_size_bpl(node->dev, fmtinfo, fmt); + } +} + +static int unicam_register_node(struct unicam_device *unicam, + enum unicam_node_type type) +{ + const u32 pad_index = type == UNICAM_IMAGE_NODE + ? UNICAM_SD_PAD_SOURCE_IMAGE + : UNICAM_SD_PAD_SOURCE_METADATA; + struct unicam_node *node = &unicam->node[type]; + struct video_device *vdev = &node->video_dev; + struct vb2_queue *q = &node->buffer_queue; + int ret; + + node->dev = unicam_get(unicam); + node->id = type; + + spin_lock_init(&node->dma_queue_lock); + + INIT_LIST_HEAD(&node->dma_queue); + + /* Initialize the videobuf2 queue. */ + q->type = type == UNICAM_IMAGE_NODE ? V4L2_BUF_TYPE_VIDEO_CAPTURE + : V4L2_BUF_TYPE_META_CAPTURE; + q->io_modes = VB2_MMAP | VB2_DMABUF; + q->drv_priv = node; + q->ops = &unicam_video_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->buf_struct_size = sizeof(struct unicam_buffer); + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &unicam->lock; + q->min_buffers_needed = 1; + q->dev = unicam->dev; + + ret = vb2_queue_init(q); + if (ret) { + dev_err(unicam->dev, "vb2_queue_init() failed\n"); + goto err_unicam_put; + } + + /* Initialize the video device. */ + vdev->release = unicam_node_release; + vdev->fops = &unicam_fops; + vdev->ioctl_ops = &unicam_ioctl_ops; + vdev->v4l2_dev = &unicam->v4l2_dev; + vdev->vfl_dir = VFL_DIR_RX; + vdev->queue = q; + vdev->lock = &unicam->lock; + vdev->device_caps = type == UNICAM_IMAGE_NODE + ? V4L2_CAP_VIDEO_CAPTURE : V4L2_CAP_META_CAPTURE; + vdev->device_caps |= V4L2_CAP_STREAMING | V4L2_CAP_IO_MC; + vdev->entity.ops = &unicam_video_media_ops; + + snprintf(vdev->name, sizeof(vdev->name), "%s-%s", UNICAM_MODULE_NAME, + type == UNICAM_IMAGE_NODE ? "image" : "embedded"); + + video_set_drvdata(vdev, node); + + if (type == UNICAM_IMAGE_NODE) + vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT; + + node->pad.flags = MEDIA_PAD_FL_SINK; + + ret = media_entity_pads_init(&vdev->entity, 1, &node->pad); + if (ret) + goto err_unicam_put; + + node->dummy_buf.size = UNICAM_DUMMY_BUF_SIZE; + node->dummy_buf_cpu_addr = dma_alloc_coherent(unicam->dev, + node->dummy_buf.size, + &node->dummy_buf.dma_addr, + GFP_KERNEL); + if (!node->dummy_buf_cpu_addr) { + dev_err(unicam->dev, "Unable to allocate dummy buffer.\n"); + ret = -ENOMEM; + goto err_entity_cleanup; + } + + unicam_set_default_format(node); + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) { + dev_err(unicam->dev, "Unable to register video device %s\n", + vdev->name); + goto err_dma_free; + } + + node->registered = true; + + ret = media_create_pad_link(&unicam->subdev.sd.entity, + pad_index, + &node->video_dev.entity, + 0, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) { + /* + * No need for cleanup, the caller will unregister the + * video device, which will drop the reference on the + * device and trigger the cleanup. + */ + dev_err(unicam->dev, "Unable to create pad link for %s\n", + unicam->sensor.subdev->name); + return ret; + } + + return 0; + +err_dma_free: + dma_free_coherent(unicam->dev, node->dummy_buf.size, + node->dummy_buf_cpu_addr, + node->dummy_buf.dma_addr); +err_entity_cleanup: + media_entity_cleanup(&vdev->entity); +err_unicam_put: + unicam_put(unicam); + return ret; +} + +static void unicam_unregister_nodes(struct unicam_device *unicam) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(unicam->node); i++) { + struct unicam_node *node = &unicam->node[i]; + + if (node->registered) { + vb2_video_unregister_device(&node->video_dev); + node->registered = false; + } + + if (node->dummy_buf_cpu_addr) + dma_free_coherent(unicam->dev, node->dummy_buf.size, + node->dummy_buf_cpu_addr, + node->dummy_buf.dma_addr); + } +} + +/* ----------------------------------------------------------------------------- + * Power management + */ + +static int unicam_runtime_resume(struct device *dev) +{ + struct unicam_device *unicam = dev_get_drvdata(dev); + int ret; + + ret = clk_set_min_rate(unicam->vpu_clock, UNICAM_MIN_VPU_CLOCK_RATE); + if (ret) { + dev_err(unicam->dev, "failed to set up VPU clock\n"); + return ret; + } + + ret = clk_prepare_enable(unicam->vpu_clock); + if (ret) { + dev_err(unicam->dev, "Failed to enable VPU clock: %d\n", ret); + goto err_vpu_clock; + } + + ret = clk_set_rate(unicam->clock, 100 * 1000 * 1000); + if (ret) { + dev_err(unicam->dev, "failed to set up CSI clock\n"); + goto err_vpu_prepare; + } + + ret = clk_prepare_enable(unicam->clock); + if (ret) { + dev_err(unicam->dev, "Failed to enable CSI clock: %d\n", ret); + goto err_vpu_prepare; + } + + return 0; + +err_vpu_prepare: + clk_disable_unprepare(unicam->vpu_clock); +err_vpu_clock: + if (clk_set_min_rate(unicam->vpu_clock, 0)) + dev_err(unicam->dev, "failed to reset the VPU clock\n"); + + return ret; +} + +static int unicam_runtime_suspend(struct device *dev) +{ + struct unicam_device *unicam = dev_get_drvdata(dev); + + clk_disable_unprepare(unicam->clock); + + if (clk_set_min_rate(unicam->vpu_clock, 0)) + dev_err(unicam->dev, "failed to reset the VPU clock\n"); + + clk_disable_unprepare(unicam->vpu_clock); + + return 0; +} + +static const struct dev_pm_ops unicam_pm_ops = { + RUNTIME_PM_OPS(unicam_runtime_suspend, unicam_runtime_resume, NULL) +}; + +/* ----------------------------------------------------------------------------- + * V4L2 async notifier + */ + +static int unicam_async_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_connection *asc) +{ + struct unicam_device *unicam = notifier_to_unicam_device(notifier); + struct media_pad *sink = &unicam->subdev.pads[UNICAM_SD_PAD_SINK]; + struct media_pad *source; + int ret; + + dev_dbg(unicam->dev, "Using sensor %s for capture\n", + subdev->name); + + ret = v4l2_create_fwnode_links_to_pad(subdev, sink, MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) + return ret; + + source = media_pad_remote_pad_unique(sink); + if (IS_ERR(source)) { + dev_err(unicam->dev, "No connected sensor pad\n"); + return PTR_ERR(source); + } + + unicam->sensor.subdev = subdev; + unicam->sensor.pad = source; + + return 0; +} + +static int unicam_async_complete(struct v4l2_async_notifier *notifier) +{ + struct unicam_device *unicam = notifier_to_unicam_device(notifier); + int ret; + + ret = unicam_register_node(unicam, UNICAM_IMAGE_NODE); + if (ret) { + dev_err(unicam->dev, "Unable to register image video device.\n"); + goto unregister; + } + + ret = unicam_register_node(unicam, UNICAM_METADATA_NODE); + if (ret) { + dev_err(unicam->dev, "Unable to register metadata video device.\n"); + goto unregister; + } + + ret = v4l2_device_register_subdev_nodes(&unicam->v4l2_dev); + if (ret) { + dev_err(unicam->dev, "Unable to register subdev nodes.\n"); + goto unregister; + } + + return 0; + +unregister: + unicam_unregister_nodes(unicam); + unicam_put(unicam); + + return ret; +} + +static const struct v4l2_async_notifier_operations unicam_async_ops = { + .bound = unicam_async_bound, + .complete = unicam_async_complete, +}; + +static int unicam_async_nf_init(struct unicam_device *unicam) +{ + struct v4l2_fwnode_endpoint ep = { }; + struct fwnode_handle *ep_handle; + struct v4l2_async_connection *asc; + int ret; + + ret = of_property_read_u32(unicam->dev->of_node, "brcm,num-data-lanes", + &unicam->max_data_lanes); + if (ret < 0) { + dev_err(unicam->dev, "Missing %s DT property\n", + "brcm,num-data-lanes"); + return -EINVAL; + } + + /* Get and parse the local endpoint. */ + ep_handle = fwnode_graph_get_endpoint_by_id(dev_fwnode(unicam->dev), 0, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!ep_handle) { + dev_err(unicam->dev, "No endpoint found\n"); + return -ENODEV; + } + + ret = v4l2_fwnode_endpoint_parse(ep_handle, &ep); + if (ret) { + dev_err(unicam->dev, "Failed to parse endpoint: %d\n", ret); + goto error; + } + + unicam->bus_type = ep.bus_type; + + switch (ep.bus_type) { + case V4L2_MBUS_CSI2_DPHY: { + unsigned int num_data_lanes = ep.bus.mipi_csi2.num_data_lanes; + + if (num_data_lanes != 1 && num_data_lanes != 2 && + num_data_lanes != 4) { + dev_err(unicam->dev, "%u data lanes not supported\n", + num_data_lanes); + ret = -EINVAL; + goto error; + } + + if (num_data_lanes > unicam->max_data_lanes) { + dev_err(unicam->dev, + "Endpoint uses %u data lanes when %u are supported\n", + num_data_lanes, unicam->max_data_lanes); + ret = -EINVAL; + goto error; + } + + unicam->max_data_lanes = num_data_lanes; + unicam->bus_flags = ep.bus.mipi_csi2.flags; + break; + } + + case V4L2_MBUS_CCP2: + unicam->max_data_lanes = 1; + unicam->bus_flags = ep.bus.mipi_csi1.strobe; + break; + + default: + /* Unsupported bus type */ + dev_err(unicam->dev, "Unsupported bus type %u\n", ep.bus_type); + ret = -EINVAL; + goto error; + } + + /* Initialize and register the async notifier. */ + v4l2_async_nf_init(&unicam->notifier, &unicam->v4l2_dev); + + asc = v4l2_async_nf_add_fwnode_remote(&unicam->notifier, ep_handle, + struct v4l2_async_connection); + fwnode_handle_put(ep_handle); + ep_handle = NULL; + + if (IS_ERR(asc)) { + ret = PTR_ERR(asc); + dev_err(unicam->dev, "Failed to add entry to notifier: %d\n", + ret); + goto error; + } + + unicam->notifier.ops = &unicam_async_ops; + + ret = v4l2_async_nf_register(&unicam->notifier); + if (ret) { + dev_err(unicam->dev, "Error registering device notifier: %d\n", + ret); + goto error; + } + + return 0; + +error: + fwnode_handle_put(ep_handle); + return ret; +} + +/* ----------------------------------------------------------------------------- + * Probe & remove + */ + +static int unicam_media_init(struct unicam_device *unicam) +{ + int ret; + + unicam->mdev.dev = unicam->dev; + strscpy(unicam->mdev.model, UNICAM_MODULE_NAME, + sizeof(unicam->mdev.model)); + unicam->mdev.hw_revision = 0; + + media_device_init(&unicam->mdev); + + unicam->v4l2_dev.mdev = &unicam->mdev; + + ret = v4l2_device_register(unicam->dev, &unicam->v4l2_dev); + if (ret < 0) { + dev_err(unicam->dev, "Unable to register v4l2 device\n"); + goto err_media_cleanup; + } + + ret = media_device_register(&unicam->mdev); + if (ret < 0) { + dev_err(unicam->dev, + "Unable to register media-controller device\n"); + goto err_v4l2_unregister; + } + + return 0; + +err_v4l2_unregister: + v4l2_device_unregister(&unicam->v4l2_dev); +err_media_cleanup: + media_device_cleanup(&unicam->mdev); + return ret; +} + +static int unicam_probe(struct platform_device *pdev) +{ + struct unicam_device *unicam; + int ret; + + unicam = kzalloc(sizeof(*unicam), GFP_KERNEL); + if (!unicam) + return -ENOMEM; + + kref_init(&unicam->kref); + mutex_init(&unicam->lock); + + unicam->dev = &pdev->dev; + platform_set_drvdata(pdev, unicam); + + unicam->base = devm_platform_ioremap_resource_byname(pdev, "unicam"); + if (IS_ERR(unicam->base)) { + ret = PTR_ERR(unicam->base); + goto err_unicam_put; + } + + unicam->clk_gate_base = devm_platform_ioremap_resource_byname(pdev, "cmi"); + if (IS_ERR(unicam->clk_gate_base)) { + ret = PTR_ERR(unicam->clk_gate_base); + goto err_unicam_put; + } + + unicam->clock = devm_clk_get(&pdev->dev, "lp"); + if (IS_ERR(unicam->clock)) { + dev_err(unicam->dev, "Failed to get lp clock\n"); + ret = PTR_ERR(unicam->clock); + goto err_unicam_put; + } + + unicam->vpu_clock = devm_clk_get(&pdev->dev, "vpu"); + if (IS_ERR(unicam->vpu_clock)) { + dev_err(unicam->dev, "Failed to get vpu clock\n"); + ret = PTR_ERR(unicam->vpu_clock); + goto err_unicam_put; + } + + unicam->sync_gpio = devm_gpiod_get_optional(&pdev->dev, "sync", + GPIOD_OUT_LOW); + + ret = platform_get_irq(pdev, 0); + if (ret < 0) + goto err_unicam_put; + + ret = devm_request_irq(&pdev->dev, ret, unicam_isr, 0, + "unicam_capture0", unicam); + if (ret) { + dev_err(&pdev->dev, "Unable to request interrupt\n"); + goto err_unicam_put; + } + + /* Enable the block power domain. */ + pm_runtime_enable(&pdev->dev); + + ret = unicam_media_init(unicam); + if (ret) + goto err_pm_runtime; + + ret = unicam_subdev_init(unicam); + if (ret) + goto err_media_unregister; + + ret = unicam_async_nf_init(unicam); + if (ret) + goto err_subdev_unregister; + + return 0; + +err_subdev_unregister: + unicam_subdev_cleanup(unicam); +err_media_unregister: + media_device_unregister(&unicam->mdev); +err_pm_runtime: + pm_runtime_disable(&pdev->dev); +err_unicam_put: + unicam_put(unicam); + + return ret; +} + +static int unicam_remove(struct platform_device *pdev) +{ + struct unicam_device *unicam = platform_get_drvdata(pdev); + + unicam_unregister_nodes(unicam); + v4l2_device_unregister(&unicam->v4l2_dev); + media_device_unregister(&unicam->mdev); + v4l2_async_nf_unregister(&unicam->notifier); + + unicam_subdev_cleanup(unicam); + + unicam_put(unicam); + + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct of_device_id unicam_of_match[] = { + { .compatible = "brcm,bcm2835-unicam", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, unicam_of_match); + +static struct platform_driver unicam_driver = { + .probe = unicam_probe, + .remove = unicam_remove, + .driver = { + .name = UNICAM_MODULE_NAME, + .pm = pm_ptr(&unicam_pm_ops), + .of_match_table = unicam_of_match, + }, +}; + +module_platform_driver(unicam_driver); + +MODULE_AUTHOR("Dave Stevenson "); +MODULE_DESCRIPTION("BCM2835 Unicam driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c index 2d803cf31e9d1c..d8510707bb974f 100644 --- a/drivers/media/platform/cadence/cdns-csi2rx.c +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -61,6 +61,11 @@ enum csi2rx_pads { CSI2RX_PAD_MAX, }; +struct csi2rx_fmt { + u32 code; + u8 bpp; +}; + struct csi2rx_priv { struct device *dev; unsigned int count; @@ -95,6 +100,32 @@ struct csi2rx_priv { int source_pad; }; +static const struct csi2rx_fmt formats[] = { + { .code = MEDIA_BUS_FMT_YUYV8_1X16, .bpp = 16, }, + { .code = MEDIA_BUS_FMT_UYVY8_1X16, .bpp = 16, }, + { .code = MEDIA_BUS_FMT_YVYU8_1X16, .bpp = 16, }, + { .code = MEDIA_BUS_FMT_VYUY8_1X16, .bpp = 16, }, + { .code = MEDIA_BUS_FMT_SBGGR8_1X8, .bpp = 8, }, + { .code = MEDIA_BUS_FMT_SGBRG8_1X8, .bpp = 8, }, + { .code = MEDIA_BUS_FMT_SGRBG8_1X8, .bpp = 8, }, + { .code = MEDIA_BUS_FMT_SRGGB8_1X8, .bpp = 8, }, + { .code = MEDIA_BUS_FMT_SBGGR10_1X10, .bpp = 10, }, + { .code = MEDIA_BUS_FMT_SGBRG10_1X10, .bpp = 10, }, + { .code = MEDIA_BUS_FMT_SGRBG10_1X10, .bpp = 10, }, + { .code = MEDIA_BUS_FMT_SRGGB10_1X10, .bpp = 10, }, +}; + +static const struct csi2rx_fmt *csi2rx_get_fmt_by_code(u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(formats); i++) + if (formats[i].code == code) + return &formats[i]; + + return NULL; +} + static inline struct csi2rx_priv *v4l2_subdev_to_csi2rx(struct v4l2_subdev *subdev) { @@ -305,12 +336,71 @@ static int csi2rx_s_stream(struct v4l2_subdev *subdev, int enable) return ret; } +static int csi2rx_set_fmt(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *fmt; + unsigned int i; + + /* No transcoding, source and sink formats must match. */ + if (format->pad != CSI2RX_PAD_SINK) + return v4l2_subdev_get_fmt(subdev, state, format); + + if (!csi2rx_get_fmt_by_code(format->format.code)) + format->format.code = formats[0].code; + + format->format.field = V4L2_FIELD_NONE; + + /* Set sink format */ + fmt = v4l2_subdev_state_get_format(state, format->pad); + *fmt = format->format; + + /* Propagate to source formats */ + for (i = CSI2RX_PAD_SOURCE_STREAM0; i < CSI2RX_PAD_MAX; i++) { + fmt = v4l2_subdev_state_get_format(state, i); + *fmt = format->format; + } + + return 0; +} + +static int csi2rx_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_format format = { + .pad = CSI2RX_PAD_SINK, + .format = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_LIM_RANGE, + .xfer_func = V4L2_XFER_FUNC_SRGB, + }, + }; + + return csi2rx_set_fmt(subdev, state, &format); +} + +static const struct v4l2_subdev_pad_ops csi2rx_pad_ops = { + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = csi2rx_set_fmt, +}; + static const struct v4l2_subdev_video_ops csi2rx_video_ops = { .s_stream = csi2rx_s_stream, }; static const struct v4l2_subdev_ops csi2rx_subdev_ops = { .video = &csi2rx_video_ops, + .pad = &csi2rx_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops csi2rx_internal_ops = { + .init_state = csi2rx_init_state, }; static int csi2rx_async_bound(struct v4l2_async_notifier *notifier, @@ -519,6 +609,7 @@ static int csi2rx_probe(struct platform_device *pdev) csi2rx->subdev.owner = THIS_MODULE; csi2rx->subdev.dev = &pdev->dev; v4l2_subdev_init(&csi2rx->subdev, &csi2rx_subdev_ops); + csi2rx->subdev.internal_ops = &csi2rx_internal_ops; v4l2_set_subdevdata(&csi2rx->subdev, &pdev->dev); snprintf(csi2rx->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s.%s", KBUILD_MODNAME, dev_name(&pdev->dev)); @@ -534,9 +625,13 @@ static int csi2rx_probe(struct platform_device *pdev) if (ret) goto err_cleanup; + ret = v4l2_subdev_init_finalize(&csi2rx->subdev); + if (ret) + goto err_cleanup; + ret = v4l2_async_register_subdev(&csi2rx->subdev); if (ret < 0) - goto err_cleanup; + goto err_free_state; dev_info(&pdev->dev, "Probed CSI2RX with %u/%u lanes, %u streams, %s D-PHY\n", @@ -546,9 +641,12 @@ static int csi2rx_probe(struct platform_device *pdev) return 0; +err_free_state: + v4l2_subdev_cleanup(&csi2rx->subdev); err_cleanup: v4l2_async_nf_unregister(&csi2rx->notifier); v4l2_async_nf_cleanup(&csi2rx->notifier); + media_entity_cleanup(&csi2rx->subdev.entity); err_free_priv: kfree(csi2rx); return ret; @@ -561,6 +659,8 @@ static void csi2rx_remove(struct platform_device *pdev) v4l2_async_nf_unregister(&csi2rx->notifier); v4l2_async_nf_cleanup(&csi2rx->notifier); v4l2_async_unregister_subdev(&csi2rx->subdev); + v4l2_subdev_cleanup(&csi2rx->subdev); + media_entity_cleanup(&csi2rx->subdev.entity); kfree(csi2rx); } diff --git a/drivers/media/platform/cadence/cdns-csi2tx.c b/drivers/media/platform/cadence/cdns-csi2tx.c index 1e0400b7803e93..1107e34cd8d380 100644 --- a/drivers/media/platform/cadence/cdns-csi2tx.c +++ b/drivers/media/platform/cadence/cdns-csi2tx.c @@ -176,8 +176,7 @@ __csi2tx_get_pad_format(struct v4l2_subdev *subdev, struct csi2tx_priv *csi2tx = v4l2_subdev_to_csi2tx(subdev); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(subdev, sd_state, - fmt->pad); + return v4l2_subdev_state_get_format(sd_state, fmt->pad); return &csi2tx->pad_fmts[fmt->pad]; } diff --git a/drivers/media/platform/microchip/microchip-csi2dc.c b/drivers/media/platform/microchip/microchip-csi2dc.c index 988c1cc1d8b6b9..fee73260bb1e11 100644 --- a/drivers/media/platform/microchip/microchip-csi2dc.c +++ b/drivers/media/platform/microchip/microchip-csi2dc.c @@ -232,8 +232,8 @@ static int csi2dc_get_fmt(struct v4l2_subdev *csi2dc_sd, struct v4l2_mbus_framefmt *v4l2_try_fmt; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - v4l2_try_fmt = v4l2_subdev_get_try_format(csi2dc_sd, sd_state, - format->pad); + v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state, + format->pad); format->format = *v4l2_try_fmt; return 0; @@ -281,13 +281,12 @@ static int csi2dc_set_fmt(struct v4l2_subdev *csi2dc_sd, req_fmt->format.field = V4L2_FIELD_NONE; if (req_fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - v4l2_try_fmt = v4l2_subdev_get_try_format(csi2dc_sd, sd_state, - req_fmt->pad); + v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state, + req_fmt->pad); *v4l2_try_fmt = req_fmt->format; /* Trying on the sink pad makes the source pad change too */ - v4l2_try_fmt = v4l2_subdev_get_try_format(csi2dc_sd, - sd_state, - CSI2DC_PAD_SOURCE); + v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state, + CSI2DC_PAD_SOURCE); *v4l2_try_fmt = req_fmt->format; /* if we are just trying, we are done */ @@ -436,11 +435,11 @@ static int csi2dc_s_stream(struct v4l2_subdev *csi2dc_sd, int enable) return ret; } -static int csi2dc_init_cfg(struct v4l2_subdev *csi2dc_sd, - struct v4l2_subdev_state *sd_state) +static int csi2dc_init_state(struct v4l2_subdev *csi2dc_sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *v4l2_try_fmt = - v4l2_subdev_get_try_format(csi2dc_sd, sd_state, 0); + v4l2_subdev_state_get_format(sd_state, 0); v4l2_try_fmt->height = 480; v4l2_try_fmt->width = 640; @@ -462,7 +461,6 @@ static const struct v4l2_subdev_pad_ops csi2dc_pad_ops = { .enum_mbus_code = csi2dc_enum_mbus_code, .set_fmt = csi2dc_set_fmt, .get_fmt = csi2dc_get_fmt, - .init_cfg = csi2dc_init_cfg, }; static const struct v4l2_subdev_video_ops csi2dc_video_ops = { @@ -474,6 +472,10 @@ static const struct v4l2_subdev_ops csi2dc_subdev_ops = { .video = &csi2dc_video_ops, }; +static const struct v4l2_subdev_internal_ops csi2dc_internal_ops = { + .init_state = csi2dc_init_state, +}; + static int csi2dc_async_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, struct v4l2_async_connection *asd) @@ -678,6 +680,7 @@ static int csi2dc_probe(struct platform_device *pdev) } v4l2_subdev_init(&csi2dc->csi2dc_sd, &csi2dc_subdev_ops); + csi2dc->csi2dc_sd.internal_ops = &csi2dc_internal_ops; csi2dc->csi2dc_sd.owner = THIS_MODULE; csi2dc->csi2dc_sd.dev = dev; diff --git a/drivers/media/platform/microchip/microchip-isc-base.c b/drivers/media/platform/microchip/microchip-isc-base.c index 8dbf7bc1e863ba..1d310814ae1eed 100644 --- a/drivers/media/platform/microchip/microchip-isc-base.c +++ b/drivers/media/platform/microchip/microchip-isc-base.c @@ -858,6 +858,8 @@ static int isc_try_configure_pipeline(struct isc_device *isc) static void isc_try_fse(struct isc_device *isc, struct v4l2_subdev_state *sd_state) { + struct v4l2_rect *try_crop = + v4l2_subdev_state_get_crop(sd_state, 0); struct v4l2_subdev_frame_size_enum fse = { .which = V4L2_SUBDEV_FORMAT_TRY, }; @@ -879,11 +881,11 @@ static void isc_try_fse(struct isc_device *isc, * just use the maximum ISC can receive. */ if (ret) { - sd_state->pads->try_crop.width = isc->max_width; - sd_state->pads->try_crop.height = isc->max_height; + try_crop->width = isc->max_width; + try_crop->height = isc->max_height; } else { - sd_state->pads->try_crop.width = fse.max_width; - sd_state->pads->try_crop.height = fse.max_height; + try_crop->width = fse.max_width; + try_crop->height = fse.max_height; } } diff --git a/drivers/media/platform/microchip/microchip-isc-scaler.c b/drivers/media/platform/microchip/microchip-isc-scaler.c index 0f29a32d15ce5d..e83463543e2100 100644 --- a/drivers/media/platform/microchip/microchip-isc-scaler.c +++ b/drivers/media/platform/microchip/microchip-isc-scaler.c @@ -33,8 +33,8 @@ static int isc_scaler_get_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *v4l2_try_fmt; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - v4l2_try_fmt = v4l2_subdev_get_try_format(sd, sd_state, - format->pad); + v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state, + format->pad); format->format = *v4l2_try_fmt; return 0; @@ -74,12 +74,12 @@ static int isc_scaler_set_fmt(struct v4l2_subdev *sd, req_fmt->format.code = fmt->mbus_code; if (req_fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - v4l2_try_fmt = v4l2_subdev_get_try_format(sd, sd_state, - req_fmt->pad); + v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state, + req_fmt->pad); *v4l2_try_fmt = req_fmt->format; /* Trying on the sink pad makes the source pad change too */ - v4l2_try_fmt = v4l2_subdev_get_try_format(sd, sd_state, - ISC_SCALER_PAD_SOURCE); + v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state, + ISC_SCALER_PAD_SOURCE); *v4l2_try_fmt = req_fmt->format; v4l_bound_align_image(&v4l2_try_fmt->width, @@ -145,17 +145,17 @@ static int isc_scaler_g_sel(struct v4l2_subdev *sd, return 0; } -static int isc_scaler_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int isc_scaler_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *v4l2_try_fmt = - v4l2_subdev_get_try_format(sd, sd_state, 0); + v4l2_subdev_state_get_format(sd_state, 0); struct v4l2_rect *try_crop; struct isc_device *isc = container_of(sd, struct isc_device, scaler_sd); *v4l2_try_fmt = isc->scaler_format[ISC_SCALER_PAD_SOURCE]; - try_crop = v4l2_subdev_get_try_crop(sd, sd_state, 0); + try_crop = v4l2_subdev_state_get_crop(sd_state, 0); try_crop->top = 0; try_crop->left = 0; @@ -170,7 +170,6 @@ static const struct v4l2_subdev_pad_ops isc_scaler_pad_ops = { .set_fmt = isc_scaler_set_fmt, .get_fmt = isc_scaler_get_fmt, .get_selection = isc_scaler_g_sel, - .init_cfg = isc_scaler_init_cfg, }; static const struct media_entity_operations isc_scaler_entity_ops = { @@ -181,11 +180,16 @@ static const struct v4l2_subdev_ops xisc_scaler_subdev_ops = { .pad = &isc_scaler_pad_ops, }; +static const struct v4l2_subdev_internal_ops isc_scaler_internal_ops = { + .init_state = isc_scaler_init_state, +}; + int isc_scaler_init(struct isc_device *isc) { int ret; v4l2_subdev_init(&isc->scaler_sd, &xisc_scaler_subdev_ops); + isc->scaler_sd.internal_ops = &isc_scaler_internal_ops; isc->scaler_sd.owner = THIS_MODULE; isc->scaler_sd.dev = isc->dev; diff --git a/drivers/media/platform/nxp/imx-mipi-csis.c b/drivers/media/platform/nxp/imx-mipi-csis.c index 142ac7b73e14a0..db8ff5f5c4d3cd 100644 --- a/drivers/media/platform/nxp/imx-mipi-csis.c +++ b/drivers/media/platform/nxp/imx-mipi-csis.c @@ -954,7 +954,7 @@ static int mipi_csis_s_stream(struct v4l2_subdev *sd, int enable) state = v4l2_subdev_lock_and_get_active_state(sd); - format = v4l2_subdev_get_pad_format(sd, state, CSIS_PAD_SINK); + format = v4l2_subdev_state_get_format(state, CSIS_PAD_SINK); csis_fmt = find_csis_format(format->code); ret = mipi_csis_calculate_params(csis, csis_fmt); @@ -1002,7 +1002,7 @@ static int mipi_csis_enum_mbus_code(struct v4l2_subdev *sd, if (code->index > 0) return -EINVAL; - fmt = v4l2_subdev_get_pad_format(sd, sd_state, code->pad); + fmt = v4l2_subdev_state_get_format(sd_state, code->pad); code->code = fmt->code; return 0; } @@ -1069,7 +1069,7 @@ static int mipi_csis_set_fmt(struct v4l2_subdev *sd, &sdformat->format.height, 1, CSIS_MAX_PIX_HEIGHT, 0, 0); - fmt = v4l2_subdev_get_pad_format(sd, sd_state, sdformat->pad); + fmt = v4l2_subdev_state_get_format(sd_state, sdformat->pad); fmt->code = csis_fmt->code; fmt->width = sdformat->format.width; @@ -1083,7 +1083,7 @@ static int mipi_csis_set_fmt(struct v4l2_subdev *sd, sdformat->format = *fmt; /* Propagate the format from sink to source. */ - fmt = v4l2_subdev_get_pad_format(sd, sd_state, CSIS_PAD_SOURCE); + fmt = v4l2_subdev_state_get_format(sd_state, CSIS_PAD_SOURCE); *fmt = sdformat->format; /* The format on the source pad might change due to unpacking. */ @@ -1104,7 +1104,7 @@ static int mipi_csis_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, return -EINVAL; state = v4l2_subdev_lock_and_get_active_state(sd); - fmt = v4l2_subdev_get_pad_format(sd, state, CSIS_PAD_SOURCE); + fmt = v4l2_subdev_state_get_format(state, CSIS_PAD_SOURCE); csis_fmt = find_csis_format(fmt->code); v4l2_subdev_unlock_state(state); @@ -1114,8 +1114,6 @@ static int mipi_csis_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, fd->type = V4L2_MBUS_FRAME_DESC_TYPE_PARALLEL; fd->num_entries = 1; - memset(entry, 0, sizeof(*entry)); - entry->flags = 0; entry->pixelcode = csis_fmt->code; entry->bus.csi2.vc = 0; @@ -1124,8 +1122,8 @@ static int mipi_csis_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, return 0; } -static int mipi_csis_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int mipi_csis_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { .pad = CSIS_PAD_SINK, @@ -1165,7 +1163,6 @@ static const struct v4l2_subdev_video_ops mipi_csis_video_ops = { }; static const struct v4l2_subdev_pad_ops mipi_csis_pad_ops = { - .init_cfg = mipi_csis_init_cfg, .enum_mbus_code = mipi_csis_enum_mbus_code, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = mipi_csis_set_fmt, @@ -1178,6 +1175,10 @@ static const struct v4l2_subdev_ops mipi_csis_subdev_ops = { .pad = &mipi_csis_pad_ops, }; +static const struct v4l2_subdev_internal_ops mipi_csis_internal_ops = { + .init_state = mipi_csis_init_state, +}; + /* ----------------------------------------------------------------------------- * Media entity operations */ @@ -1352,6 +1353,7 @@ static int mipi_csis_subdev_init(struct mipi_csis_device *csis) int ret; v4l2_subdev_init(sd, &mipi_csis_subdev_ops); + sd->internal_ops = &mipi_csis_internal_ops; sd->owner = THIS_MODULE; snprintf(sd->name, sizeof(sd->name), "csis-%s", dev_name(csis->dev)); diff --git a/drivers/media/platform/nxp/imx7-media-csi.c b/drivers/media/platform/nxp/imx7-media-csi.c index 15049c6aab3724..1d6248211c76e0 100644 --- a/drivers/media/platform/nxp/imx7-media-csi.c +++ b/drivers/media/platform/nxp/imx7-media-csi.c @@ -542,8 +542,8 @@ static void imx7_csi_configure(struct imx7_csi *csi, } else { const struct v4l2_mbus_framefmt *sink_fmt; - sink_fmt = v4l2_subdev_get_pad_format(&csi->sd, sd_state, - IMX7_CSI_PAD_SINK); + sink_fmt = v4l2_subdev_state_get_format(sd_state, + IMX7_CSI_PAD_SINK); cr1 = BIT_SOF_POL | BIT_REDGE | BIT_HSYNC_POL | BIT_FCC | BIT_MCLKDIV(1) | BIT_MCLKEN; @@ -1728,8 +1728,8 @@ static int imx7_csi_s_stream(struct v4l2_subdev *sd, int enable) return ret; } -static int imx7_csi_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int imx7_csi_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { const struct imx7_csi_pixfmt *cc; int i; @@ -1738,7 +1738,7 @@ static int imx7_csi_init_cfg(struct v4l2_subdev *sd, for (i = 0; i < IMX7_CSI_PADS_NUM; i++) { struct v4l2_mbus_framefmt *mf = - v4l2_subdev_get_pad_format(sd, sd_state, i); + v4l2_subdev_state_get_format(sd_state, i); mf->code = IMX7_CSI_DEF_MBUS_CODE; mf->width = IMX7_CSI_DEF_PIX_WIDTH; @@ -1762,7 +1762,7 @@ static int imx7_csi_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *in_fmt; int ret = 0; - in_fmt = v4l2_subdev_get_pad_format(sd, sd_state, IMX7_CSI_PAD_SINK); + in_fmt = v4l2_subdev_state_get_format(sd_state, IMX7_CSI_PAD_SINK); switch (code->pad) { case IMX7_CSI_PAD_SINK: @@ -1841,7 +1841,7 @@ static void imx7_csi_try_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *in_fmt; u32 code; - in_fmt = v4l2_subdev_get_pad_format(sd, sd_state, IMX7_CSI_PAD_SINK); + in_fmt = v4l2_subdev_state_get_format(sd_state, IMX7_CSI_PAD_SINK); switch (sdformat->pad) { case IMX7_CSI_PAD_SRC: @@ -1891,7 +1891,7 @@ static int imx7_csi_set_fmt(struct v4l2_subdev *sd, imx7_csi_try_fmt(sd, sd_state, sdformat, &cc); - fmt = v4l2_subdev_get_pad_format(sd, sd_state, sdformat->pad); + fmt = v4l2_subdev_state_get_format(sd_state, sdformat->pad); *fmt = sdformat->format; @@ -1902,8 +1902,8 @@ static int imx7_csi_set_fmt(struct v4l2_subdev *sd, format.format = sdformat->format; imx7_csi_try_fmt(sd, sd_state, &format, &outcc); - outfmt = v4l2_subdev_get_pad_format(sd, sd_state, - IMX7_CSI_PAD_SRC); + outfmt = v4l2_subdev_state_get_format(sd_state, + IMX7_CSI_PAD_SRC); *outfmt = format.format; } @@ -2005,7 +2005,6 @@ static const struct v4l2_subdev_video_ops imx7_csi_video_ops = { }; static const struct v4l2_subdev_pad_ops imx7_csi_pad_ops = { - .init_cfg = imx7_csi_init_cfg, .enum_mbus_code = imx7_csi_enum_mbus_code, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = imx7_csi_set_fmt, @@ -2018,6 +2017,7 @@ static const struct v4l2_subdev_ops imx7_csi_subdev_ops = { }; static const struct v4l2_subdev_internal_ops imx7_csi_internal_ops = { + .init_state = imx7_csi_init_state, .registered = imx7_csi_registered, .unregistered = imx7_csi_unregistered, }; diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c index c9a4d091b57074..93a55c97cd1738 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c @@ -58,7 +58,7 @@ static int mxc_isi_crossbar_gasket_enable(struct mxc_isi_crossbar *xbar, return -EINVAL; } - fmt = v4l2_subdev_state_get_stream_format(state, port, 0); + fmt = v4l2_subdev_state_get_format(state, port, 0); if (!fmt) return -EINVAL; @@ -175,8 +175,8 @@ mxc_isi_crossbar_xlate_streams(struct mxc_isi_crossbar *xbar, return sd; } -static int mxc_isi_crossbar_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int mxc_isi_crossbar_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct mxc_isi_crossbar *xbar = to_isi_crossbar(sd); struct v4l2_subdev_krouting routing = { }; @@ -280,8 +280,7 @@ static int mxc_isi_crossbar_set_fmt(struct v4l2_subdev *sd, * Set the format on the sink stream and propagate it to the source * streams. */ - sink_fmt = v4l2_subdev_state_get_stream_format(state, fmt->pad, - fmt->stream); + sink_fmt = v4l2_subdev_state_get_format(state, fmt->pad, fmt->stream); if (!sink_fmt) return -EINVAL; @@ -295,8 +294,9 @@ static int mxc_isi_crossbar_set_fmt(struct v4l2_subdev *sd, route->sink_stream != fmt->stream) continue; - source_fmt = v4l2_subdev_state_get_stream_format(state, route->source_pad, - route->source_stream); + source_fmt = v4l2_subdev_state_get_format(state, + route->source_pad, + route->source_stream); if (!source_fmt) return -EINVAL; @@ -403,7 +403,6 @@ static int mxc_isi_crossbar_disable_streams(struct v4l2_subdev *sd, } static const struct v4l2_subdev_pad_ops mxc_isi_crossbar_subdev_pad_ops = { - .init_cfg = mxc_isi_crossbar_init_cfg, .enum_mbus_code = mxc_isi_crossbar_enum_mbus_code, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = mxc_isi_crossbar_set_fmt, @@ -416,6 +415,10 @@ static const struct v4l2_subdev_ops mxc_isi_crossbar_subdev_ops = { .pad = &mxc_isi_crossbar_subdev_pad_ops, }; +static const struct v4l2_subdev_internal_ops mxc_isi_crossbar_internal_ops = { + .init_state = mxc_isi_crossbar_init_state, +}; + static const struct media_entity_operations mxc_isi_cross_entity_ops = { .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1, .link_validate = v4l2_subdev_link_validate, @@ -437,6 +440,7 @@ int mxc_isi_crossbar_init(struct mxc_isi_dev *isi) xbar->isi = isi; v4l2_subdev_init(sd, &mxc_isi_crossbar_subdev_ops); + sd->internal_ops = &mxc_isi_crossbar_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; strscpy(sd->name, "crossbar", sizeof(sd->name)); sd->dev = isi->dev; diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c index 65d20e9bae69db..d76eb58deb096b 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c @@ -263,10 +263,10 @@ int mxc_isi_pipe_enable(struct mxc_isi_pipe *pipe) /* Configure the pipeline. */ state = v4l2_subdev_lock_and_get_active_state(sd); - sink_fmt = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SINK); - src_fmt = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SOURCE); - compose = v4l2_subdev_get_try_compose(sd, state, MXC_ISI_PIPE_PAD_SINK); - crop = *v4l2_subdev_get_try_crop(sd, state, MXC_ISI_PIPE_PAD_SOURCE); + sink_fmt = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SINK); + src_fmt = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SOURCE); + compose = v4l2_subdev_state_get_compose(state, MXC_ISI_PIPE_PAD_SINK); + crop = *v4l2_subdev_state_get_crop(state, MXC_ISI_PIPE_PAD_SOURCE); sink_info = mxc_isi_bus_format_by_code(sink_fmt->code, MXC_ISI_PIPE_PAD_SINK); @@ -322,7 +322,7 @@ mxc_isi_pipe_get_pad_format(struct mxc_isi_pipe *pipe, struct v4l2_subdev_state *state, unsigned int pad) { - return v4l2_subdev_get_try_format(&pipe->sd, state, pad); + return v4l2_subdev_state_get_format(state, pad); } static struct v4l2_rect * @@ -330,7 +330,7 @@ mxc_isi_pipe_get_pad_crop(struct mxc_isi_pipe *pipe, struct v4l2_subdev_state *state, unsigned int pad) { - return v4l2_subdev_get_try_crop(&pipe->sd, state, pad); + return v4l2_subdev_state_get_crop(state, pad); } static struct v4l2_rect * @@ -338,11 +338,11 @@ mxc_isi_pipe_get_pad_compose(struct mxc_isi_pipe *pipe, struct v4l2_subdev_state *state, unsigned int pad) { - return v4l2_subdev_get_try_compose(&pipe->sd, state, pad); + return v4l2_subdev_state_get_compose(state, pad); } -static int mxc_isi_pipe_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int mxc_isi_pipe_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct mxc_isi_pipe *pipe = to_isi_pipe(sd); struct v4l2_mbus_framefmt *fmt_source; @@ -682,7 +682,6 @@ static int mxc_isi_pipe_set_selection(struct v4l2_subdev *sd, } static const struct v4l2_subdev_pad_ops mxc_isi_pipe_subdev_pad_ops = { - .init_cfg = mxc_isi_pipe_init_cfg, .enum_mbus_code = mxc_isi_pipe_enum_mbus_code, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = mxc_isi_pipe_set_fmt, @@ -694,6 +693,10 @@ static const struct v4l2_subdev_ops mxc_isi_pipe_subdev_ops = { .pad = &mxc_isi_pipe_subdev_pad_ops, }; +static const struct v4l2_subdev_internal_ops mxc_isi_pipe_internal_ops = { + .init_state = mxc_isi_pipe_init_state, +}; + /* ----------------------------------------------------------------------------- * IRQ handling */ @@ -767,6 +770,7 @@ int mxc_isi_pipe_init(struct mxc_isi_dev *isi, unsigned int id) sd = &pipe->sd; v4l2_subdev_init(sd, &mxc_isi_pipe_subdev_ops); + sd->internal_ops = &mxc_isi_pipe_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; snprintf(sd->name, sizeof(sd->name), "mxc_isi.%d", pipe->id); sd->dev = isi->dev; @@ -832,8 +836,8 @@ int mxc_isi_pipe_acquire(struct mxc_isi_pipe *pipe, int ret; state = v4l2_subdev_lock_and_get_active_state(sd); - sink_fmt = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SINK); - src_fmt = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SOURCE); + sink_fmt = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SINK); + src_fmt = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SOURCE); v4l2_subdev_unlock_state(state); sink_info = mxc_isi_bus_format_by_code(sink_fmt->code, diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c index 10840c9a0912b1..49bca2b01cc61d 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c @@ -713,7 +713,7 @@ static int mxc_isi_video_validate_format(struct mxc_isi_video *video) info = mxc_isi_format_by_fourcc(video->pix.pixelformat, MXC_ISI_VIDEO_CAP); - format = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SOURCE); if (format->code != info->mbus_code || format->width != video->pix.width || diff --git a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c index ed048f73c982b4..ba2e81f24965d8 100644 --- a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c +++ b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c @@ -296,7 +296,7 @@ static int imx8mq_mipi_csi_calc_hs_settle(struct csi_state *state, /* Calculate the line rate from the pixel rate. */ - fmt = v4l2_subdev_get_pad_format(&state->sd, sd_state, MIPI_CSI2_PAD_SINK); + fmt = v4l2_subdev_state_get_format(sd_state, MIPI_CSI2_PAD_SINK); csi2_fmt = find_csi2_format(fmt->code); link_freq = v4l2_get_link_freq(state->src_sd->ctrl_handler, @@ -437,14 +437,15 @@ static int imx8mq_mipi_csi_s_stream(struct v4l2_subdev *sd, int enable) return ret; } -static int imx8mq_mipi_csi_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int imx8mq_mipi_csi_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *fmt_sink; struct v4l2_mbus_framefmt *fmt_source; - fmt_sink = v4l2_subdev_get_pad_format(sd, sd_state, MIPI_CSI2_PAD_SINK); - fmt_source = v4l2_subdev_get_pad_format(sd, sd_state, MIPI_CSI2_PAD_SOURCE); + fmt_sink = v4l2_subdev_state_get_format(sd_state, MIPI_CSI2_PAD_SINK); + fmt_source = v4l2_subdev_state_get_format(sd_state, + MIPI_CSI2_PAD_SOURCE); fmt_sink->code = MEDIA_BUS_FMT_SGBRG10_1X10; fmt_sink->width = MIPI_CSI2_DEF_PIX_WIDTH; @@ -477,7 +478,7 @@ static int imx8mq_mipi_csi_enum_mbus_code(struct v4l2_subdev *sd, if (code->index > 0) return -EINVAL; - fmt = v4l2_subdev_get_pad_format(sd, sd_state, code->pad); + fmt = v4l2_subdev_state_get_format(sd_state, code->pad); code->code = fmt->code; return 0; } @@ -514,7 +515,7 @@ static int imx8mq_mipi_csi_set_fmt(struct v4l2_subdev *sd, if (!csi2_fmt) csi2_fmt = &imx8mq_mipi_csi_formats[0]; - fmt = v4l2_subdev_get_pad_format(sd, sd_state, sdformat->pad); + fmt = v4l2_subdev_state_get_format(sd_state, sdformat->pad); fmt->code = csi2_fmt->code; fmt->width = sdformat->format.width; @@ -523,7 +524,7 @@ static int imx8mq_mipi_csi_set_fmt(struct v4l2_subdev *sd, sdformat->format = *fmt; /* Propagate the format from sink to source. */ - fmt = v4l2_subdev_get_pad_format(sd, sd_state, MIPI_CSI2_PAD_SOURCE); + fmt = v4l2_subdev_state_get_format(sd_state, MIPI_CSI2_PAD_SOURCE); *fmt = sdformat->format; return 0; @@ -534,7 +535,6 @@ static const struct v4l2_subdev_video_ops imx8mq_mipi_csi_video_ops = { }; static const struct v4l2_subdev_pad_ops imx8mq_mipi_csi_pad_ops = { - .init_cfg = imx8mq_mipi_csi_init_cfg, .enum_mbus_code = imx8mq_mipi_csi_enum_mbus_code, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = imx8mq_mipi_csi_set_fmt, @@ -545,6 +545,10 @@ static const struct v4l2_subdev_ops imx8mq_mipi_csi_subdev_ops = { .pad = &imx8mq_mipi_csi_pad_ops, }; +static const struct v4l2_subdev_internal_ops imx8mq_mipi_csi_internal_ops = { + .init_state = imx8mq_mipi_csi_init_state, +}; + /* ----------------------------------------------------------------------------- * Media entity operations */ @@ -759,6 +763,7 @@ static int imx8mq_mipi_csi_subdev_init(struct csi_state *state) int ret; v4l2_subdev_init(sd, &imx8mq_mipi_csi_subdev_ops); + sd->internal_ops = &imx8mq_mipi_csi_internal_ops; sd->owner = THIS_MODULE; snprintf(sd->name, sizeof(sd->name), "%s %s", MIPI_CSI2_SUBDEV_NAME, dev_name(state->dev)); diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index 6360314f04a636..3afbadbd7f23f9 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -275,8 +275,7 @@ __csid_get_format(struct csid_device *csid, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&csid->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); return &csid->fmt[pad]; } diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index 3f726a7237f566..340ccc0e827884 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -325,8 +325,7 @@ __csiphy_get_format(struct csiphy_device *csiphy, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&csiphy->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); return &csiphy->fmt[pad]; } diff --git a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c index b713f5b86aba69..79d91ec110236b 100644 --- a/drivers/media/platform/qcom/camss/camss-ispif.c +++ b/drivers/media/platform/qcom/camss/camss-ispif.c @@ -879,8 +879,7 @@ __ispif_get_format(struct ispif_line *line, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&line->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); return &line->fmt[pad]; } diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index 965500b83d073b..94197355075ceb 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -772,8 +772,7 @@ __vfe_get_format(struct vfe_line *line, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&line->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); return &line->fmt[pad]; } @@ -792,8 +791,8 @@ __vfe_get_compose(struct vfe_line *line, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_compose(&line->subdev, sd_state, - MSM_VFE_PAD_SINK); + return v4l2_subdev_state_get_compose(sd_state, + MSM_VFE_PAD_SINK); return &line->compose; } @@ -812,8 +811,7 @@ __vfe_get_crop(struct vfe_line *line, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&line->subdev, sd_state, - MSM_VFE_PAD_SRC); + return v4l2_subdev_state_get_crop(sd_state, MSM_VFE_PAD_SRC); return &line->crop; } diff --git a/drivers/media/platform/raspberrypi/Kconfig b/drivers/media/platform/raspberrypi/Kconfig index a1850c559dbbcc..bd5101ffefb5bc 100644 --- a/drivers/media/platform/raspberrypi/Kconfig +++ b/drivers/media/platform/raspberrypi/Kconfig @@ -3,4 +3,4 @@ comment "Raspberry Pi media platform drivers" source "drivers/media/platform/raspberrypi/pisp_be/Kconfig" -source "drivers/media/platform/raspberrypi/rp1_cfe/Kconfig" +source "drivers/media/platform/raspberrypi/rp1-cfe/Kconfig" diff --git a/drivers/media/platform/raspberrypi/Makefile b/drivers/media/platform/raspberrypi/Makefile index 4ce6c998927c17..af7fde84eefe23 100644 --- a/drivers/media/platform/raspberrypi/Makefile +++ b/drivers/media/platform/raspberrypi/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 obj-y += pisp_be/ -obj-y += rp1_cfe/ +obj-y += rp1-cfe/ diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/Kconfig b/drivers/media/platform/raspberrypi/rp1-cfe/Kconfig similarity index 62% rename from drivers/media/platform/raspberrypi/rp1_cfe/Kconfig rename to drivers/media/platform/raspberrypi/rp1-cfe/Kconfig index 8cb1255319fb7d..327b61f1134bc9 100644 --- a/drivers/media/platform/raspberrypi/rp1_cfe/Kconfig +++ b/drivers/media/platform/raspberrypi/rp1-cfe/Kconfig @@ -1,14 +1,15 @@ # RP1 V4L2 camera support config VIDEO_RP1_CFE - tristate "RP1 Camera Frond End (CFE) video capture driver" + tristate "Raspberry Pi RP1 Camera Front End (CFE) video capture driver" depends on VIDEO_DEV + depends on PM select VIDEO_V4L2_SUBDEV_API select MEDIA_CONTROLLER select VIDEOBUF2_DMA_CONTIG select V4L2_FWNODE help - Say Y here to enable support for the RP1 Camera Front End. + Say Y here to enable support for the Raspberry Pi RP1 Camera Front End. To compile this driver as a module, choose M here. The module will be called rp1-cfe. diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/Makefile b/drivers/media/platform/raspberrypi/rp1-cfe/Makefile similarity index 72% rename from drivers/media/platform/raspberrypi/rp1_cfe/Makefile rename to drivers/media/platform/raspberrypi/rp1-cfe/Makefile index 9709d6f603e995..3f0d0fc8570ee7 100644 --- a/drivers/media/platform/raspberrypi/rp1_cfe/Makefile +++ b/drivers/media/platform/raspberrypi/rp1-cfe/Makefile @@ -2,5 +2,5 @@ # # Makefile for RP1 Camera Front End driver # -rp1-cfe-objs := cfe.o csi2.o pisp_fe.o dphy.o +rp1-cfe-objs := cfe.o csi2.o pisp-fe.o dphy.o obj-$(CONFIG_VIDEO_RP1_CFE) += rp1-cfe.o diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h b/drivers/media/platform/raspberrypi/rp1-cfe/cfe-fmts.h similarity index 94% rename from drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h rename to drivers/media/platform/raspberrypi/rp1-cfe/cfe-fmts.h index 29c807253e6428..7aecf7f8373344 100644 --- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h +++ b/drivers/media/platform/raspberrypi/rp1-cfe/cfe-fmts.h @@ -2,8 +2,7 @@ /* * RP1 Camera Front End formats definition * - * Copyright (C) 2021 - Raspberry Pi Ltd. - * + * Copyright (C) 2021-2024 - Raspberry Pi Ltd. */ #ifndef _CFE_FMTS_H_ #define _CFE_FMTS_H_ @@ -293,14 +292,29 @@ static const struct cfe_fmt formats[] = { .depth = 8, .flags = CFE_FORMAT_FLAG_FE_OUT, }, - /* Embedded data format */ + + /* Embedded data formats */ { - .fourcc = V4L2_META_FMT_SENSOR_DATA, - .code = MEDIA_BUS_FMT_SENSOR_DATA, + .fourcc = V4L2_META_FMT_GENERIC_8, + .code = MEDIA_BUS_FMT_META_8, .depth = 8, .csi_dt = MIPI_CSI2_DT_EMBEDDED_8B, .flags = CFE_FORMAT_FLAG_META_CAP, }, + { + .fourcc = V4L2_META_FMT_GENERIC_CSI2_10, + .code = MEDIA_BUS_FMT_META_10, + .depth = 10, + .csi_dt = MIPI_CSI2_DT_EMBEDDED_8B, + .flags = CFE_FORMAT_FLAG_META_CAP, + }, + { + .fourcc = V4L2_META_FMT_GENERIC_CSI2_12, + .code = MEDIA_BUS_FMT_META_12, + .depth = 12, + .csi_dt = MIPI_CSI2_DT_EMBEDDED_8B, + .flags = CFE_FORMAT_FLAG_META_CAP, + }, /* Frontend formats */ { diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/cfe-trace.h b/drivers/media/platform/raspberrypi/rp1-cfe/cfe-trace.h new file mode 100644 index 00000000000000..1a36259f51b7bd --- /dev/null +++ b/drivers/media/platform/raspberrypi/rp1-cfe/cfe-trace.h @@ -0,0 +1,202 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2024 Raspberry Pi Ltd. + * Copyright (c) 2024 Ideas on Board Oy + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM cfe + +#if !defined(_CFE_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _CFE_TRACE_H + +#include +#include + +TRACE_EVENT(cfe_return_buffer, + TP_PROTO(u32 node_id, u32 buf_idx, u32 queue_id), + TP_ARGS(node_id, buf_idx, queue_id), + TP_STRUCT__entry( + __field(u32, node_id) + __field(u32, buf_idx) + __field(u32, queue_id) + ), + TP_fast_assign( + __entry->node_id = node_id; + __entry->buf_idx = buf_idx; + __entry->queue_id = queue_id; + ), + TP_printk("node=%u buf=%u, queue=%u", __entry->node_id, + __entry->buf_idx, __entry->queue_id) +); + +DECLARE_EVENT_CLASS(cfe_buffer_template, + TP_PROTO(u32 node_id, struct vb2_buffer *buf), + TP_ARGS(node_id, buf), + TP_STRUCT__entry( + __field(u32, node_id) + __field(u32, buf_idx) + ), + TP_fast_assign( + __entry->node_id = node_id; + __entry->buf_idx = buf->index; + ), + TP_printk("node=%u buf=%u", __entry->node_id, __entry->buf_idx) +); + +DEFINE_EVENT(cfe_buffer_template, cfe_buffer_prepare, + TP_PROTO(u32 node_id, struct vb2_buffer *buf), + TP_ARGS(node_id, buf)); + +TRACE_EVENT(cfe_buffer_queue, + TP_PROTO(u32 node_id, struct vb2_buffer *buf, bool schedule_now), + TP_ARGS(node_id, buf, schedule_now), + TP_STRUCT__entry( + __field(u32, node_id) + __field(u32, buf_idx) + __field(bool, schedule_now) + ), + TP_fast_assign( + __entry->node_id = node_id; + __entry->buf_idx = buf->index; + __entry->schedule_now = schedule_now; + ), + TP_printk("node=%u buf=%u%s", __entry->node_id, __entry->buf_idx, + __entry->schedule_now ? " schedule immediately" : "") +); + +DEFINE_EVENT(cfe_buffer_template, cfe_csi2_schedule, + TP_PROTO(u32 node_id, struct vb2_buffer *buf), + TP_ARGS(node_id, buf)); + +DEFINE_EVENT(cfe_buffer_template, cfe_fe_schedule, + TP_PROTO(u32 node_id, struct vb2_buffer *buf), + TP_ARGS(node_id, buf)); + +TRACE_EVENT(cfe_buffer_complete, + TP_PROTO(u32 node_id, struct vb2_v4l2_buffer *buf), + TP_ARGS(node_id, buf), + TP_STRUCT__entry( + __field(u32, node_id) + __field(u32, buf_idx) + __field(u32, seq) + __field(u64, ts) + ), + TP_fast_assign( + __entry->node_id = node_id; + __entry->buf_idx = buf->vb2_buf.index; + __entry->seq = buf->sequence; + __entry->ts = buf->vb2_buf.timestamp; + ), + TP_printk("node=%u buf=%u seq=%u ts=%llu", __entry->node_id, + __entry->buf_idx, __entry->seq, __entry->ts) +); + +TRACE_EVENT(cfe_frame_start, + TP_PROTO(u32 node_id, u32 fs_count), + TP_ARGS(node_id, fs_count), + TP_STRUCT__entry( + __field(u32, node_id) + __field(u32, fs_count) + ), + TP_fast_assign( + __entry->node_id = node_id; + __entry->fs_count = fs_count; + ), + TP_printk("node=%u fs_count=%u", __entry->node_id, __entry->fs_count) +); + +TRACE_EVENT(cfe_frame_end, + TP_PROTO(u32 node_id, u32 fs_count), + TP_ARGS(node_id, fs_count), + TP_STRUCT__entry( + __field(u32, node_id) + __field(u32, fs_count) + ), + TP_fast_assign( + __entry->node_id = node_id; + __entry->fs_count = fs_count; + ), + TP_printk("node=%u fs_count=%u", __entry->node_id, __entry->fs_count) +); + +TRACE_EVENT(cfe_prepare_next_job, + TP_PROTO(bool fe_enabled), + TP_ARGS(fe_enabled), + TP_STRUCT__entry( + __field(bool, fe_enabled) + ), + TP_fast_assign( + __entry->fe_enabled = fe_enabled; + ), + TP_printk("fe_enabled=%u", __entry->fe_enabled) +); + +/* These are copied from csi2.c */ +#define CSI2_STATUS_IRQ_FS(x) (BIT(0) << (x)) +#define CSI2_STATUS_IRQ_FE(x) (BIT(4) << (x)) +#define CSI2_STATUS_IRQ_FE_ACK(x) (BIT(8) << (x)) +#define CSI2_STATUS_IRQ_LE(x) (BIT(12) << (x)) +#define CSI2_STATUS_IRQ_LE_ACK(x) (BIT(16) << (x)) + +TRACE_EVENT(csi2_irq, + TP_PROTO(u32 channel, u32 status, u32 dbg), + TP_ARGS(channel, status, dbg), + TP_STRUCT__entry( + __field(u32, channel) + __field(u32, status) + __field(u32, dbg) + ), + TP_fast_assign( + __entry->channel = channel; + __entry->status = status; + __entry->dbg = dbg; + ), + TP_printk("ch=%u flags=[ %s%s%s%s%s] frame=%u line=%u\n", + __entry->channel, + (__entry->status & CSI2_STATUS_IRQ_FS(__entry->channel)) ? + "FS " : "", + (__entry->status & CSI2_STATUS_IRQ_FE(__entry->channel)) ? + "FE " : "", + (__entry->status & CSI2_STATUS_IRQ_FE_ACK(__entry->channel)) ? + "FE_ACK " : "", + (__entry->status & CSI2_STATUS_IRQ_LE(__entry->channel)) ? + "LE " : "", + (__entry->status & CSI2_STATUS_IRQ_LE_ACK(__entry->channel)) ? + "LE_ACK " : "", + __entry->dbg >> 16, __entry->dbg & 0xffff) +); + +TRACE_EVENT(fe_irq, + TP_PROTO(u32 status, u32 output_status, u32 frame_status, + u32 error_status, u32 int_status), + TP_ARGS(status, output_status, frame_status, error_status, int_status), + TP_STRUCT__entry( + __field(u32, status) + __field(u32, output_status) + __field(u32, frame_status) + __field(u32, error_status) + __field(u32, int_status) + ), + TP_fast_assign( + __entry->status = status; + __entry->output_status = output_status; + __entry->frame_status = frame_status; + __entry->error_status = error_status; + __entry->int_status = int_status; + ), + TP_printk("status 0x%x out_status 0x%x frame_status 0x%x error_status 0x%x int_status 0x%x", + __entry->status, + __entry->output_status, + __entry->frame_status, + __entry->error_status, + __entry->int_status) +); + +#endif /* _CFE_TRACE_H */ + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#define TRACE_INCLUDE_FILE ../../drivers/media/platform/raspberrypi/rp1-cfe/cfe-trace +#include diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c b/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c similarity index 71% rename from drivers/media/platform/raspberrypi/rp1_cfe/cfe.c rename to drivers/media/platform/raspberrypi/rp1-cfe/cfe.c index 45bd8abbddbcc1..484fc7006bc789 100644 --- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c +++ b/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c @@ -2,27 +2,26 @@ /* * RP1 Camera Front End Driver * - * Copyright (C) 2021-2022 - Raspberry Pi Ltd. - * + * Copyright (c) 2021-2024 Raspberry Pi Ltd. + * Copyright (c) 2023-2024 Ideas on Board Oy */ -#include #include #include #include #include #include #include +#include #include #include #include +#include +#include #include -#include -#include -#include -#include #include #include +#include #include #include #include @@ -33,34 +32,29 @@ #include #include #include -#include #include #include #include +#include #include +#include +#include + +#include "cfe-fmts.h" #include "cfe.h" -#include "cfe_fmts.h" #include "csi2.h" -#include "pisp_fe.h" -#include "pisp_fe_config.h" -#include "pisp_statistics.h" +#include "pisp-fe.h" + +#define CREATE_TRACE_POINTS +#include "cfe-trace.h" #define CFE_MODULE_NAME "rp1-cfe" #define CFE_VERSION "1.0" -bool cfe_debug_verbose; -module_param_named(verbose_debug, cfe_debug_verbose, bool, 0644); -MODULE_PARM_DESC(verbose_debug, "verbose debugging messages"); - -#define cfe_dbg_verbose(fmt, arg...) \ - do { \ - if (cfe_debug_verbose) \ - dev_dbg(&cfe->pdev->dev, fmt, ##arg); \ - } while (0) -#define cfe_dbg(fmt, arg...) dev_dbg(&cfe->pdev->dev, fmt, ##arg) -#define cfe_info(fmt, arg...) dev_info(&cfe->pdev->dev, fmt, ##arg) -#define cfe_err(fmt, arg...) dev_err(&cfe->pdev->dev, fmt, ##arg) +#define cfe_dbg(cfe, fmt, arg...) dev_dbg(&(cfe)->pdev->dev, fmt, ##arg) +#define cfe_info(cfe, fmt, arg...) dev_info(&(cfe)->pdev->dev, fmt, ##arg) +#define cfe_err(cfe, fmt, arg...) dev_err(&(cfe)->pdev->dev, fmt, ##arg) /* MIPICFG registers */ #define MIPICFG_CFG 0x004 @@ -92,8 +86,9 @@ MODULE_PARM_DESC(verbose_debug, "verbose debugging messages"); /* Define a nominal minimum image size */ #define MIN_WIDTH 16 #define MIN_HEIGHT 16 -/* Default size of the embedded buffer */ -#define DEFAULT_EMBEDDED_SIZE 16384 + +#define MIN_META_WIDTH 4 +#define MIN_META_HEIGHT 1 const struct v4l2_mbus_framefmt cfe_default_format = { .width = 640, @@ -106,13 +101,6 @@ const struct v4l2_mbus_framefmt cfe_default_format = { .xfer_func = V4L2_XFER_FUNC_NONE, }; -const struct v4l2_mbus_framefmt cfe_default_meta_format = { - .width = DEFAULT_EMBEDDED_SIZE, - .height = 1, - .code = MEDIA_BUS_FMT_SENSOR_DATA, - .field = V4L2_FIELD_NONE, -}; - enum node_ids { /* CSI2 HW output nodes first. */ CSI2_CH0, @@ -128,7 +116,7 @@ enum node_ids { }; struct node_description { - unsigned int id; + enum node_ids id; const char *name; unsigned int caps; unsigned int pad_flags; @@ -138,55 +126,55 @@ struct node_description { /* Must match the ordering of enum ids */ static const struct node_description node_desc[NUM_NODES] = { [CSI2_CH0] = { - .name = "csi2_ch0", + .name = "csi2-ch0", .caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE, .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, - .link_pad = CSI2_NUM_CHANNELS + 0 + .link_pad = CSI2_PAD_FIRST_SOURCE + 0 }, /* - * TODO: This node should be named "csi2_ch1" and the caps should be set - * to both video and meta capture. However, to keep compatibility with - * the current libcamera, keep the name as "embedded" and support - * only meta capture. + * At the moment the main userspace component (libcamera) doesn't + * support metadata with video nodes that support both video and + * metadata. So for the time being this node is set to only support + * V4L2_CAP_META_CAPTURE. */ [CSI2_CH1] = { - .name = "embedded", + .name = "csi2-ch1", .caps = V4L2_CAP_META_CAPTURE, .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, - .link_pad = CSI2_NUM_CHANNELS + 1 + .link_pad = CSI2_PAD_FIRST_SOURCE + 1 }, [CSI2_CH2] = { - .name = "csi2_ch2", + .name = "csi2-ch2", .caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE, .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, - .link_pad = CSI2_NUM_CHANNELS + 2 + .link_pad = CSI2_PAD_FIRST_SOURCE + 2 }, [CSI2_CH3] = { - .name = "csi2_ch3", + .name = "csi2-ch3", .caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE, .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, - .link_pad = CSI2_NUM_CHANNELS + 3 + .link_pad = CSI2_PAD_FIRST_SOURCE + 3 }, [FE_OUT0] = { - .name = "fe_image0", + .name = "fe-image0", .caps = V4L2_CAP_VIDEO_CAPTURE, .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, .link_pad = FE_OUTPUT0_PAD }, [FE_OUT1] = { - .name = "fe_image1", + .name = "fe-image1", .caps = V4L2_CAP_VIDEO_CAPTURE, .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, .link_pad = FE_OUTPUT1_PAD }, [FE_STATS] = { - .name = "fe_stats", + .name = "fe-stats", .caps = V4L2_CAP_META_CAPTURE, .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, .link_pad = FE_STATS_PAD }, [FE_CONFIG] = { - .name = "fe_config", + .name = "fe-config", .caps = V4L2_CAP_META_OUTPUT, .pad_flags = MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT, .link_pad = FE_CONFIG_PAD @@ -197,13 +185,13 @@ static const struct node_description node_desc[NUM_NODES] = { #define is_csi2_node(node) (!is_fe_node(node)) #define node_supports_image_output(node) \ - (!!(node_desc[(node)->id].caps & V4L2_CAP_VIDEO_CAPTURE)) + (node_desc[(node)->id].caps & V4L2_CAP_VIDEO_CAPTURE) #define node_supports_meta_output(node) \ - (!!(node_desc[(node)->id].caps & V4L2_CAP_META_CAPTURE)) + (node_desc[(node)->id].caps & V4L2_CAP_META_CAPTURE) #define node_supports_image_input(node) \ - (!!(node_desc[(node)->id].caps & V4L2_CAP_VIDEO_OUTPUT)) + (node_desc[(node)->id].caps & V4L2_CAP_VIDEO_OUTPUT) #define node_supports_meta_input(node) \ - (!!(node_desc[(node)->id].caps & V4L2_CAP_META_OUTPUT)) + (node_desc[(node)->id].caps & V4L2_CAP_META_OUTPUT) #define node_supports_image(node) \ (node_supports_image_output(node) || node_supports_image_input(node)) #define node_supports_meta(node) \ @@ -223,12 +211,12 @@ static const struct node_description node_desc[NUM_NODES] = { (is_meta_output_node(node) || is_meta_input_node(node)) /* To track state across all nodes. */ -#define NUM_STATES 5 #define NODE_REGISTERED BIT(0) #define NODE_ENABLED BIT(1) #define NODE_STREAMING BIT(2) #define FS_INT BIT(3) #define FE_INT BIT(4) +#define NUM_STATES 5 struct cfe_buffer { struct vb2_v4l2_buffer vb; @@ -252,7 +240,8 @@ struct cfe_config_buffer *to_cfe_config_buffer(struct cfe_buffer *buf) } struct cfe_node { - unsigned int id; + /* Node id */ + enum node_ids id; /* Pointer pointing to current v4l2_buffer */ struct cfe_buffer *cur_frm; /* Pointer pointing to next v4l2_buffer */ @@ -271,8 +260,11 @@ struct cfe_node { struct video_device video_dev; /* Pointer to the parent handle */ struct cfe_device *cfe; + /* Media pad for this node */ struct media_pad pad; + /* Frame-start counter */ unsigned int fs_count; + /* Timestamp of the current buffer */ u64 ts; }; @@ -280,9 +272,6 @@ struct cfe_device { struct dentry *debugfs; struct kref kref; - /* V4l2 specific parameters */ - struct v4l2_async_connection *asd; - /* peripheral base address */ void __iomem *mipi_cfg_base; @@ -303,8 +292,10 @@ struct cfe_device { /* subdevice async Notifier */ struct v4l2_async_notifier notifier; - /* ptr to sub device */ - struct v4l2_subdev *sensor; + /* Source sub device */ + struct v4l2_subdev *source_sd; + /* Source subdev's pad */ + u32 source_pad; struct cfe_node node[NUM_NODES]; DECLARE_BITMAP(node_flags, NUM_STATES * NUM_NODES); @@ -313,6 +304,9 @@ struct cfe_device { struct pisp_fe_device fe; int fe_csi2_channel; + + /* Mask of enabled streams */ + u64 streams_mask; }; static inline bool is_fe_enabled(struct cfe_device *cfe) @@ -344,6 +338,7 @@ static bool check_state(struct cfe_device *cfe, unsigned long state, if (!test_bit(bit + (node_id * NUM_STATES), cfe->node_flags)) return false; } + return true; } @@ -367,9 +362,7 @@ static void clear_state(struct cfe_device *cfe, unsigned long state, static bool test_any_node(struct cfe_device *cfe, unsigned long cond) { - unsigned int i; - - for (i = 0; i < NUM_NODES; i++) { + for (unsigned int i = 0; i < NUM_NODES; i++) { if (check_state(cfe, cond, i)) return true; } @@ -380,9 +373,7 @@ static bool test_any_node(struct cfe_device *cfe, unsigned long cond) static bool test_all_nodes(struct cfe_device *cfe, unsigned long precond, unsigned long cond) { - unsigned int i; - - for (i = 0; i < NUM_NODES; i++) { + for (unsigned int i = 0; i < NUM_NODES; i++) { if (check_state(cfe, precond, i)) { if (!check_state(cfe, cond, i)) return false; @@ -414,52 +405,12 @@ static int mipi_cfg_regs_show(struct seq_file *s, void *data) return 0; } -static int format_show(struct seq_file *s, void *data) -{ - struct cfe_device *cfe = s->private; - unsigned int i; - - for (i = 0; i < NUM_NODES; i++) { - struct cfe_node *node = &cfe->node[i]; - unsigned long sb, state = 0; - - for (sb = 0; sb < NUM_STATES; sb++) { - if (check_state(cfe, BIT(sb), i)) - state |= BIT(sb); - } - - seq_printf(s, "\nNode %u (%s) state: 0x%lx\n", i, - node_desc[i].name, state); - - if (node_supports_image(node)) - seq_printf(s, "format: " V4L2_FOURCC_CONV " 0x%x\n" - "resolution: %ux%u\nbpl: %u\nsize: %u\n", - V4L2_FOURCC_CONV_ARGS(node->vid_fmt.fmt.pix.pixelformat), - node->vid_fmt.fmt.pix.pixelformat, - node->vid_fmt.fmt.pix.width, - node->vid_fmt.fmt.pix.height, - node->vid_fmt.fmt.pix.bytesperline, - node->vid_fmt.fmt.pix.sizeimage); - - if (node_supports_meta(node)) - seq_printf(s, "format: " V4L2_FOURCC_CONV " 0x%x\nsize: %u\n", - V4L2_FOURCC_CONV_ARGS(node->meta_fmt.fmt.meta.dataformat), - node->meta_fmt.fmt.meta.dataformat, - node->meta_fmt.fmt.meta.buffersize); - } - - return 0; -} - DEFINE_SHOW_ATTRIBUTE(mipi_cfg_regs); -DEFINE_SHOW_ATTRIBUTE(format); /* Format setup functions */ const struct cfe_fmt *find_format_by_code(u32 code) { - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(formats); i++) { + for (unsigned int i = 0; i < ARRAY_SIZE(formats); i++) { if (formats[i].code == code) return &formats[i]; } @@ -469,9 +420,7 @@ const struct cfe_fmt *find_format_by_code(u32 code) const struct cfe_fmt *find_format_by_pix(u32 pixelformat) { - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(formats); i++) { + for (unsigned int i = 0; i < ARRAY_SIZE(formats); i++) { if (formats[i].fourcc == pixelformat) return &formats[i]; } @@ -479,6 +428,17 @@ const struct cfe_fmt *find_format_by_pix(u32 pixelformat) return NULL; } +static const struct cfe_fmt *find_format_by_code_and_fourcc(u32 code, + u32 fourcc) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(formats); i++) { + if (formats[i].code == code && formats[i].fourcc == fourcc) + return &formats[i]; + } + + return NULL; +} + /* * Given the mbus code, find the 16 bit remapped code. Returns 0 if no remap * possible. @@ -519,9 +479,9 @@ u32 cfe_find_compressed_code(u32 code) return cfe_fmt->code; } -static int cfe_calc_format_size_bpl(struct cfe_device *cfe, - const struct cfe_fmt *fmt, - struct v4l2_format *f) +static void cfe_calc_vid_format_size_bpl(struct cfe_device *cfe, + const struct cfe_fmt *fmt, + struct v4l2_format *f) { unsigned int min_bytesperline; @@ -540,21 +500,33 @@ static int cfe_calc_format_size_bpl(struct cfe_device *cfe, f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; - cfe_dbg("%s: " V4L2_FOURCC_CONV " size: %ux%u bpl:%u img_size:%u\n", - __func__, V4L2_FOURCC_CONV_ARGS(f->fmt.pix.pixelformat), - f->fmt.pix.width, f->fmt.pix.height, + cfe_dbg(cfe, "%s: %p4cc size: %ux%u bpl:%u img_size:%u\n", __func__, + &f->fmt.pix.pixelformat, f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); +} - return 0; +static void cfe_calc_meta_format_size_bpl(struct cfe_device *cfe, + const struct cfe_fmt *fmt, + struct v4l2_format *f) +{ + v4l_bound_align_image(&f->fmt.meta.width, MIN_META_WIDTH, MAX_WIDTH, 2, + &f->fmt.meta.height, MIN_META_HEIGHT, MAX_HEIGHT, + 0, 0); + + f->fmt.meta.bytesperline = (f->fmt.meta.width * fmt->depth) >> 3; + f->fmt.meta.buffersize = f->fmt.meta.height * f->fmt.pix.bytesperline; + + cfe_dbg(cfe, "%s: %p4cc size: %ux%u bpl:%u buf_size:%u\n", __func__, + &f->fmt.meta.dataformat, f->fmt.meta.width, f->fmt.meta.height, + f->fmt.meta.bytesperline, f->fmt.meta.buffersize); } static void cfe_schedule_next_csi2_job(struct cfe_device *cfe) { struct cfe_buffer *buf; - unsigned int i; dma_addr_t addr; - for (i = 0; i < CSI2_NUM_CHANNELS; i++) { + for (unsigned int i = 0; i < CSI2_NUM_CHANNELS; i++) { struct cfe_node *node = &cfe->node[i]; unsigned int stride, size; @@ -566,11 +538,11 @@ static void cfe_schedule_next_csi2_job(struct cfe_device *cfe) node->next_frm = buf; list_del(&buf->list); - cfe_dbg_verbose("%s: [%s] buffer:%p\n", __func__, - node_desc[node->id].name, &buf->vb.vb2_buf); + trace_cfe_csi2_schedule(node->id, &buf->vb.vb2_buf); if (is_meta_node(node)) { size = node->meta_fmt.fmt.meta.buffersize; + /* We use CSI2_CH_CTRL_PACK_BYTES, so stride == 0 */ stride = 0; } else { size = node->vid_fmt.fmt.pix.sizeimage; @@ -587,9 +559,8 @@ static void cfe_schedule_next_pisp_job(struct cfe_device *cfe) struct vb2_buffer *vb2_bufs[FE_NUM_PADS] = { 0 }; struct cfe_config_buffer *config_buf; struct cfe_buffer *buf; - unsigned int i; - for (i = CSI2_NUM_CHANNELS; i < NUM_NODES; i++) { + for (unsigned int i = CSI2_NUM_CHANNELS; i < NUM_NODES; i++) { struct cfe_node *node = &cfe->node[i]; if (!check_state(cfe, NODE_STREAMING, i)) @@ -598,8 +569,7 @@ static void cfe_schedule_next_pisp_job(struct cfe_device *cfe) buf = list_first_entry(&node->dma_queue, struct cfe_buffer, list); - cfe_dbg_verbose("%s: [%s] buffer:%p\n", __func__, - node_desc[node->id].name, &buf->vb.vb2_buf); + trace_cfe_fe_schedule(node->id, &buf->vb.vb2_buf); node->next_frm = buf; vb2_bufs[node_desc[i].link_pad] = &buf->vb.vb2_buf; @@ -612,19 +582,14 @@ static void cfe_schedule_next_pisp_job(struct cfe_device *cfe) static bool cfe_check_job_ready(struct cfe_device *cfe) { - unsigned int i; - - for (i = 0; i < NUM_NODES; i++) { + for (unsigned int i = 0; i < NUM_NODES; i++) { struct cfe_node *node = &cfe->node[i]; if (!check_state(cfe, NODE_ENABLED, i)) continue; - if (list_empty(&node->dma_queue)) { - cfe_dbg_verbose("%s: [%s] has no buffer, unable to schedule job\n", - __func__, node_desc[i].name); + if (list_empty(&node->dma_queue)) return false; - } } return true; @@ -632,6 +597,8 @@ static bool cfe_check_job_ready(struct cfe_device *cfe) static void cfe_prepare_next_job(struct cfe_device *cfe) { + trace_cfe_prepare_next_job(is_fe_enabled(cfe)); + cfe->job_queued = true; cfe_schedule_next_csi2_job(cfe); if (is_fe_enabled(cfe)) @@ -639,17 +606,12 @@ static void cfe_prepare_next_job(struct cfe_device *cfe) /* Flag if another job is ready after this. */ cfe->job_ready = cfe_check_job_ready(cfe); - - cfe_dbg_verbose("%s: end with scheduled job\n", __func__); } static void cfe_process_buffer_complete(struct cfe_node *node, enum vb2_buffer_state state) { - struct cfe_device *cfe = node->cfe; - - cfe_dbg_verbose("%s: [%s] buffer:%p\n", __func__, - node_desc[node->id].name, &node->cur_frm->vb.vb2_buf); + trace_cfe_buffer_complete(node->id, &node->cur_frm->vb); node->cur_frm->vb.sequence = node->fs_count - 1; vb2_buffer_done(&node->cur_frm->vb.vb2_buf, state); @@ -665,14 +627,12 @@ static void cfe_queue_event_sof(struct cfe_node *node) v4l2_event_queue(&node->video_dev, &event); } -static void cfe_sof_isr_handler(struct cfe_node *node) +static void cfe_sof_isr(struct cfe_node *node) { struct cfe_device *cfe = node->cfe; bool matching_fs = true; - unsigned int i; - cfe_dbg_verbose("%s: [%s] seq %u\n", __func__, node_desc[node->id].name, - node->fs_count); + trace_cfe_frame_start(node->id, node->fs_count); /* * If the sensor is producing unexpected frame event ordering over a @@ -690,7 +650,7 @@ static void cfe_sof_isr_handler(struct cfe_node *node) node->fs_count++; node->ts = ktime_get_ns(); - for (i = 0; i < NUM_NODES; i++) { + for (unsigned int i = 0; i < NUM_NODES; i++) { if (!check_state(cfe, NODE_STREAMING, i) || i == node->id) continue; /* @@ -720,12 +680,11 @@ static void cfe_sof_isr_handler(struct cfe_node *node) cfe_queue_event_sof(node); } -static void cfe_eof_isr_handler(struct cfe_node *node) +static void cfe_eof_isr(struct cfe_node *node) { struct cfe_device *cfe = node->cfe; - cfe_dbg_verbose("%s: [%s] seq %u\n", __func__, node_desc[node->id].name, - node->fs_count - 1); + trace_cfe_frame_end(node->id, node->fs_count - 1); if (node->cur_frm) cfe_process_buffer_complete(node, VB2_BUF_STATE_DONE); @@ -738,8 +697,7 @@ static void cfe_eof_isr_handler(struct cfe_node *node) static irqreturn_t cfe_isr(int irq, void *dev) { struct cfe_device *cfe = dev; - unsigned int i; - bool sof[NUM_NODES] = {0}, eof[NUM_NODES] = {0}; + bool sof[NUM_NODES] = { 0 }, eof[NUM_NODES] = { 0 }; u32 sts; sts = cfg_reg_read(cfe, MIPICFG_INTS); @@ -753,7 +711,7 @@ static irqreturn_t cfe_isr(int irq, void *dev) spin_lock(&cfe->state_lock); - for (i = 0; i < NUM_NODES; i++) { + for (unsigned int i = 0; i < NUM_NODES; i++) { struct cfe_node *node = &cfe->node[i]; /* @@ -761,8 +719,7 @@ static irqreturn_t cfe_isr(int irq, void *dev) * over the CSI2_CHx nodes when the FE is active since they * generate interrupts even though the node is not streaming. */ - if (!check_state(cfe, NODE_STREAMING, i) || - !(sof[i] || eof[i])) + if (!check_state(cfe, NODE_STREAMING, i) || !(sof[i] || eof[i])) continue; /* @@ -783,11 +740,11 @@ static irqreturn_t cfe_isr(int irq, void *dev) * frame. */ if (sof[i] && !check_state(cfe, FS_INT, i)) { - cfe_sof_isr_handler(node); + cfe_sof_isr(node); sof[i] = false; } - cfe_eof_isr_handler(node); + cfe_eof_isr(node); } if (sof[i]) { @@ -795,18 +752,18 @@ static irqreturn_t cfe_isr(int irq, void *dev) * The condition below tests for (3). In such cases, we * come in here with FS flag set in the node state from * the previous frame since it only gets cleared in - * eof_isr_handler(). Handle the FE for the previous + * cfe_eof_isr(). Handle the FE for the previous * frame first before the FS handler for the current * frame. */ if (check_state(cfe, FS_INT, node->id) && !check_state(cfe, FE_INT, node->id)) { - cfe_dbg("%s: [%s] Handling missing previous FE interrupt\n", + cfe_dbg(cfe, "%s: [%s] Handling missing previous FE interrupt\n", __func__, node_desc[node->id].name); - cfe_eof_isr_handler(node); + cfe_eof_isr(node); } - cfe_sof_isr_handler(node); + cfe_sof_isr(node); } if (!cfe->job_queued && cfe->job_ready) @@ -822,30 +779,106 @@ static irqreturn_t cfe_isr(int irq, void *dev) * Stream helpers */ -static void cfe_start_channel(struct cfe_node *node) +static int cfe_get_vc_dt_fallback(struct cfe_device *cfe, u8 *vc, u8 *dt) +{ + struct v4l2_subdev_state *state; + struct v4l2_mbus_framefmt *fmt; + const struct cfe_fmt *cfe_fmt; + + state = v4l2_subdev_get_locked_active_state(&cfe->csi2.sd); + + fmt = v4l2_subdev_state_get_format(state, CSI2_PAD_SINK, 0); + if (!fmt) + return -EINVAL; + + cfe_fmt = find_format_by_code(fmt->code); + if (!cfe_fmt) + return -EINVAL; + + *vc = 0; + *dt = cfe_fmt->csi_dt; + + return 0; +} + +static int cfe_get_vc_dt(struct cfe_device *cfe, unsigned int channel, u8 *vc, + u8 *dt) +{ + struct v4l2_mbus_frame_desc remote_desc; + struct v4l2_subdev_state *state; + u32 sink_stream; + unsigned int i; + int ret; + + state = v4l2_subdev_get_locked_active_state(&cfe->csi2.sd); + + ret = v4l2_subdev_routing_find_opposite_end(&state->routing, + CSI2_PAD_FIRST_SOURCE + channel, 0, NULL, &sink_stream); + if (ret) + return ret; + + ret = v4l2_subdev_call(cfe->source_sd, pad, get_frame_desc, + cfe->source_pad, &remote_desc); + if (ret == -ENOIOCTLCMD) { + cfe_dbg(cfe, "source does not support get_frame_desc, use fallback\n"); + return cfe_get_vc_dt_fallback(cfe, vc, dt); + } else if (ret) { + cfe_err(cfe, "Failed to get frame descriptor\n"); + return ret; + } + + if (remote_desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { + cfe_err(cfe, "Frame descriptor does not describe CSI-2 link"); + return -EINVAL; + } + + for (i = 0; i < remote_desc.num_entries; i++) { + if (remote_desc.entry[i].stream == sink_stream) + break; + } + + if (i == remote_desc.num_entries) { + cfe_err(cfe, "Stream %u not found in remote frame desc\n", + sink_stream); + return -EINVAL; + } + + *vc = remote_desc.entry[i].bus.csi2.vc; + *dt = remote_desc.entry[i].bus.csi2.dt; + + return 0; +} + +static int cfe_start_channel(struct cfe_node *node) { struct cfe_device *cfe = node->cfe; struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *source_fmt; const struct cfe_fmt *fmt; unsigned long flags; - bool start_fe = is_fe_enabled(cfe) && - test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING); + bool start_fe; + int ret; - cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name); + cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); - state = v4l2_subdev_lock_and_get_active_state(&cfe->csi2.sd); + start_fe = is_fe_enabled(cfe) && + test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING); + + state = v4l2_subdev_get_locked_active_state(&cfe->csi2.sd); if (start_fe) { unsigned int width, height; + u8 vc, dt; - WARN_ON(!is_fe_enabled(cfe)); - cfe_dbg("%s: %s using csi2 channel %d\n", - __func__, node_desc[FE_OUT0].name, - cfe->fe_csi2_channel); + cfe_dbg(cfe, "%s: %s using csi2 channel %d\n", __func__, + node_desc[FE_OUT0].name, cfe->fe_csi2_channel); + + ret = cfe_get_vc_dt(cfe, cfe->fe_csi2_channel, &vc, &dt); + if (ret) + return ret; - source_fmt = v4l2_subdev_get_pad_format(&cfe->csi2.sd, state, - cfe->fe_csi2_channel); + source_fmt = v4l2_subdev_state_get_format(state, + node_desc[cfe->fe_csi2_channel].link_pad); fmt = find_format_by_code(source_fmt->code); width = source_fmt->width; @@ -863,32 +896,46 @@ static void cfe_start_channel(struct cfe_node *node) */ csi2_start_channel(&cfe->csi2, cfe->fe_csi2_channel, CSI2_MODE_FE_STREAMING, - true, false, width, height); + true, false, width, height, vc, dt); csi2_set_buffer(&cfe->csi2, cfe->fe_csi2_channel, 0, 0, -1); pisp_fe_start(&cfe->fe); } if (is_csi2_node(node)) { unsigned int width = 0, height = 0; + u8 vc, dt; + + ret = cfe_get_vc_dt(cfe, node->id, &vc, &dt); + if (ret) { + if (start_fe) { + csi2_stop_channel(&cfe->csi2, + cfe->fe_csi2_channel); + pisp_fe_stop(&cfe->fe); + } + + return ret; + } u32 mode = CSI2_MODE_NORMAL; - source_fmt = v4l2_subdev_get_pad_format(&cfe->csi2.sd, state, - node_desc[node->id].link_pad - CSI2_NUM_CHANNELS); + source_fmt = v4l2_subdev_state_get_format(state, + node_desc[node->id].link_pad); fmt = find_format_by_code(source_fmt->code); /* Must have a valid CSI2 datatype. */ WARN_ON(!fmt->csi_dt); if (is_image_output_node(node)) { + u32 pixfmt; + width = source_fmt->width; height = source_fmt->height; - if (node->vid_fmt.fmt.pix.pixelformat == - fmt->remap[CFE_REMAP_16BIT]) + pixfmt = node->vid_fmt.fmt.pix.pixelformat; + + if (pixfmt == fmt->remap[CFE_REMAP_16BIT]) { mode = CSI2_MODE_REMAP; - else if (node->vid_fmt.fmt.pix.pixelformat == - fmt->remap[CFE_REMAP_COMPRESSED]) { + } else if (pixfmt == fmt->remap[CFE_REMAP_COMPRESSED]) { mode = CSI2_MODE_COMPRESSED; csi2_set_compression(&cfe->csi2, node->id, CSI2_COMPRESSION_DELTA, 0, @@ -902,22 +949,22 @@ static void cfe_start_channel(struct cfe_node *node) false, /* Pack bytes */ is_meta_node(node) ? true : false, - width, height); + width, height, vc, dt); } - v4l2_subdev_unlock_state(state); - spin_lock_irqsave(&cfe->state_lock, flags); if (cfe->job_ready && test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING)) cfe_prepare_next_job(cfe); spin_unlock_irqrestore(&cfe->state_lock, flags); + + return 0; } static void cfe_stop_channel(struct cfe_node *node, bool fe_stop) { struct cfe_device *cfe = node->cfe; - cfe_dbg("%s: [%s] fe_stop %u\n", __func__, + cfe_dbg(cfe, "%s: [%s] fe_stop %u\n", __func__, node_desc[node->id].name, fe_stop); if (fe_stop) { @@ -936,18 +983,25 @@ static void cfe_return_buffers(struct cfe_node *node, struct cfe_buffer *buf, *tmp; unsigned long flags; - cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name); + cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); spin_lock_irqsave(&cfe->state_lock, flags); list_for_each_entry_safe(buf, tmp, &node->dma_queue, list) { list_del(&buf->list); + trace_cfe_return_buffer(node->id, buf->vb.vb2_buf.index, 2); vb2_buffer_done(&buf->vb.vb2_buf, state); } - if (node->cur_frm) + if (node->cur_frm) { + trace_cfe_return_buffer(node->id, + node->cur_frm->vb.vb2_buf.index, 0); vb2_buffer_done(&node->cur_frm->vb.vb2_buf, state); - if (node->next_frm && node->cur_frm != node->next_frm) + } + if (node->next_frm && node->cur_frm != node->next_frm) { + trace_cfe_return_buffer(node->id, + node->next_frm->vb.vb2_buf.index, 1); vb2_buffer_done(&node->next_frm->vb.vb2_buf, state); + } node->cur_frm = NULL; node->next_frm = NULL; @@ -964,10 +1018,11 @@ static int cfe_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, { struct cfe_node *node = vb2_get_drv_priv(vq); struct cfe_device *cfe = node->cfe; - unsigned int size = is_image_node(node) ? node->vid_fmt.fmt.pix.sizeimage : - node->meta_fmt.fmt.meta.buffersize; + unsigned int size = is_image_node(node) ? + node->vid_fmt.fmt.pix.sizeimage : + node->meta_fmt.fmt.meta.buffersize; - cfe_dbg("%s: [%s] type:%u\n", __func__, node_desc[node->id].name, + cfe_dbg(cfe, "%s: [%s] type:%u\n", __func__, node_desc[node->id].name, node->buffer_queue.type); if (vq->num_buffers + *nbuffers < 3) @@ -975,7 +1030,7 @@ static int cfe_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, if (*nplanes) { if (sizes[0] < size) { - cfe_err("sizes[0] %i < size %u\n", sizes[0], size); + cfe_err(cfe, "sizes[0] %i < size %u\n", sizes[0], size); return -EINVAL; } size = sizes[0]; @@ -994,13 +1049,12 @@ static int cfe_buffer_prepare(struct vb2_buffer *vb) struct cfe_buffer *buf = to_cfe_buffer(vb); unsigned long size; - cfe_dbg_verbose("%s: [%s] buffer:%p\n", __func__, - node_desc[node->id].name, vb); + trace_cfe_buffer_prepare(node->id, vb); size = is_image_node(node) ? node->vid_fmt.fmt.pix.sizeimage : node->meta_fmt.fmt.meta.buffersize; if (vb2_plane_size(vb, 0) < size) { - cfe_err("data will not fit into plane (%lu < %lu)\n", + cfe_err(cfe, "data will not fit into plane (%lu < %lu)\n", vb2_plane_size(vb, 0), size); return -EINVAL; } @@ -1026,9 +1080,7 @@ static void cfe_buffer_queue(struct vb2_buffer *vb) struct cfe_device *cfe = node->cfe; struct cfe_buffer *buf = to_cfe_buffer(vb); unsigned long flags; - - cfe_dbg_verbose("%s: [%s] buffer:%p\n", __func__, - node_desc[node->id].name, vb); + bool schedule_now; spin_lock_irqsave(&cfe->state_lock, flags); @@ -1037,67 +1089,60 @@ static void cfe_buffer_queue(struct vb2_buffer *vb) if (!cfe->job_ready) cfe->job_ready = cfe_check_job_ready(cfe); - if (!cfe->job_queued && cfe->job_ready && - test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING)) { - cfe_dbg("Preparing job immediately for channel %u\n", - node->id); + schedule_now = !cfe->job_queued && cfe->job_ready && + test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING); + + trace_cfe_buffer_queue(node->id, vb, schedule_now); + + if (schedule_now) cfe_prepare_next_job(cfe); - } spin_unlock_irqrestore(&cfe->state_lock, flags); } -static u64 sensor_link_rate(struct cfe_device *cfe) +static s64 cfe_get_source_link_freq(struct cfe_device *cfe) { - struct v4l2_mbus_framefmt *source_fmt; struct v4l2_subdev_state *state; - struct media_entity *entity; - struct v4l2_subdev *subdev; - const struct cfe_fmt *fmt; - struct media_pad *pad; s64 link_freq; + u32 bpp; - state = v4l2_subdev_lock_and_get_active_state(&cfe->csi2.sd); - source_fmt = v4l2_subdev_get_pad_format(&cfe->csi2.sd, state, 0); - fmt = find_format_by_code(source_fmt->code); - v4l2_subdev_unlock_state(state); + state = v4l2_subdev_get_locked_active_state(&cfe->csi2.sd); /* - * Walk up the media graph to find either the sensor entity, or another - * entity that advertises the V4L2_CID_LINK_FREQ or V4L2_CID_PIXEL_RATE - * control through the subdev. + * v4l2_get_link_freq() uses V4L2_CID_LINK_FREQ first, and falls back + * to V4L2_CID_PIXEL_RATE if V4L2_CID_LINK_FREQ is not available. + * + * With multistream input there is no single pixel rate, and thus we + * cannot use V4L2_CID_PIXEL_RATE, so we pass 0 as the bpp which + * causes v4l2_get_link_freq() to return an error if it falls back to + * V4L2_CID_PIXEL_RATE. */ - entity = &cfe->csi2.sd.entity; - while (1) { - pad = &entity->pads[0]; - if (!(pad->flags & MEDIA_PAD_FL_SINK)) - goto err; - - pad = media_pad_remote_pad_first(pad); - if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) - goto err; - - entity = pad->entity; - subdev = media_entity_to_v4l2_subdev(entity); - if (entity->function == MEDIA_ENT_F_CAM_SENSOR || - v4l2_ctrl_find(subdev->ctrl_handler, V4L2_CID_LINK_FREQ) || - v4l2_ctrl_find(subdev->ctrl_handler, V4L2_CID_PIXEL_RATE)) - break; + + if (state->routing.num_routes == 1) { + struct v4l2_subdev_route *route = &state->routing.routes[0]; + struct v4l2_mbus_framefmt *source_fmt; + const struct cfe_fmt *fmt; + + source_fmt = v4l2_subdev_state_get_format(state, + route->sink_pad, + route->sink_stream); + + fmt = find_format_by_code(source_fmt->code); + if (!fmt) + return -EINVAL; + + bpp = fmt->depth; + } else { + bpp = 0; } - link_freq = v4l2_get_link_freq(subdev->ctrl_handler, fmt->depth, - cfe->csi2.dphy.active_lanes * 2); + link_freq = v4l2_get_link_freq(cfe->source_sd->ctrl_handler, bpp, + 2 * cfe->csi2.dphy.active_lanes); if (link_freq < 0) - goto err; + cfe_err(cfe, "failed to get link freq for subdev '%s'\n", + cfe->source_sd->name); - /* x2 for DDR. */ - link_freq *= 2; - cfe_info("Using a link rate of %lld Mbps\n", link_freq / (1000 * 1000)); return link_freq; - -err: - cfe_err("Unable to determine sensor link rate, using 999 Mbps\n"); - return 999 * 1000000UL; } static int cfe_start_streaming(struct vb2_queue *vq, unsigned int count) @@ -1105,12 +1150,15 @@ static int cfe_start_streaming(struct vb2_queue *vq, unsigned int count) struct v4l2_mbus_config mbus_config = { 0 }; struct cfe_node *node = vb2_get_drv_priv(vq); struct cfe_device *cfe = node->cfe; + struct v4l2_subdev_state *state; + struct v4l2_subdev_route *route; + s64 link_freq; int ret; - cfe_dbg("%s: [%s] begin.\n", __func__, node_desc[node->id].name); + cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); if (!check_state(cfe, NODE_ENABLED, node->id)) { - cfe_err("%s node link is not enabled.\n", + cfe_err(cfe, "%s node link is not enabled.\n", node_desc[node->id].name); ret = -EINVAL; goto err_streaming; @@ -1118,72 +1166,96 @@ static int cfe_start_streaming(struct vb2_queue *vq, unsigned int count) ret = pm_runtime_resume_and_get(&cfe->pdev->dev); if (ret < 0) { - cfe_err("pm_runtime_resume_and_get failed\n"); + cfe_err(cfe, "pm_runtime_resume_and_get failed\n"); goto err_streaming; } /* When using the Frontend, we must enable the FE_CONFIG node. */ if (is_fe_enabled(cfe) && !check_state(cfe, NODE_ENABLED, cfe->node[FE_CONFIG].id)) { - cfe_err("FE enabled, but FE_CONFIG node is not\n"); + cfe_err(cfe, "FE enabled, but FE_CONFIG node is not\n"); ret = -EINVAL; goto err_pm_put; } ret = media_pipeline_start(&node->pad, &cfe->pipe); if (ret < 0) { - cfe_err("Failed to start media pipeline: %d\n", ret); + cfe_err(cfe, "Failed to start media pipeline: %d\n", ret); goto err_pm_put; } + state = v4l2_subdev_lock_and_get_active_state(&cfe->csi2.sd); + clear_state(cfe, FS_INT | FE_INT, node->id); set_state(cfe, NODE_STREAMING, node->id); node->fs_count = 0; - cfe_start_channel(node); + + ret = cfe_start_channel(node); + if (ret) + goto err_unlock_state; if (!test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING)) { - cfe_dbg("Not all nodes are set to streaming yet!\n"); + cfe_dbg(cfe, "Streaming on hold, as all nodes are not set to streaming yet\n"); + v4l2_subdev_unlock_state(state); return 0; } cfg_reg_write(cfe, MIPICFG_CFG, MIPICFG_CFG_SEL_CSI); - cfg_reg_write(cfe, MIPICFG_INTE, MIPICFG_INT_CSI_DMA | MIPICFG_INT_PISP_FE); + cfg_reg_write(cfe, MIPICFG_INTE, + MIPICFG_INT_CSI_DMA | MIPICFG_INT_PISP_FE); - ret = v4l2_subdev_call(cfe->sensor, pad, get_mbus_config, 0, + ret = v4l2_subdev_call(cfe->source_sd, pad, get_mbus_config, 0, &mbus_config); if (ret < 0 && ret != -ENOIOCTLCMD) { - cfe_err("g_mbus_config failed\n"); - goto err_pm_put; + cfe_err(cfe, "g_mbus_config failed\n"); + goto err_clear_inte; } cfe->csi2.dphy.active_lanes = mbus_config.bus.mipi_csi2.num_data_lanes; if (!cfe->csi2.dphy.active_lanes) cfe->csi2.dphy.active_lanes = cfe->csi2.dphy.max_lanes; if (cfe->csi2.dphy.active_lanes > cfe->csi2.dphy.max_lanes) { - cfe_err("Device has requested %u data lanes, which is >%u configured in DT\n", + cfe_err(cfe, "Device has requested %u data lanes, which is >%u configured in DT\n", cfe->csi2.dphy.active_lanes, cfe->csi2.dphy.max_lanes); ret = -EINVAL; - goto err_disable_cfe; + goto err_clear_inte; } - cfe_dbg("Configuring CSI-2 block - %u data lanes\n", cfe->csi2.dphy.active_lanes); - cfe->csi2.dphy.dphy_rate = sensor_link_rate(cfe) / 1000000UL; + link_freq = cfe_get_source_link_freq(cfe); + if (link_freq < 0) + goto err_clear_inte; + + cfe->csi2.dphy.dphy_rate = div_s64(link_freq * 2, 1000000); csi2_open_rx(&cfe->csi2); - cfe_dbg("Starting sensor streaming\n"); - ret = v4l2_subdev_call(cfe->sensor, video, s_stream, 1); - if (ret < 0) { - cfe_err("stream on failed in subdev\n"); + cfe->streams_mask = 0; + + for_each_active_route(&state->routing, route) + cfe->streams_mask |= BIT_ULL(route->sink_stream); + + ret = v4l2_subdev_enable_streams(cfe->source_sd, cfe->source_pad, + cfe->streams_mask); + if (ret) { + cfe_err(cfe, "stream on failed in subdev\n"); goto err_disable_cfe; } - cfe_dbg("%s: [%s] end.\n", __func__, node_desc[node->id].name); + cfe_dbg(cfe, "Streaming enabled\n"); + + v4l2_subdev_unlock_state(state); return 0; err_disable_cfe: csi2_close_rx(&cfe->csi2); - cfe_stop_channel(node, true); +err_clear_inte: + cfg_reg_write(cfe, MIPICFG_INTE, 0); + + cfe_stop_channel(node, + is_fe_enabled(cfe) && test_all_nodes(cfe, NODE_ENABLED, + NODE_STREAMING)); +err_unlock_state: + v4l2_subdev_unlock_state(state); media_pipeline_stop(&node->pad); err_pm_put: pm_runtime_put(&cfe->pdev->dev); @@ -1201,7 +1273,7 @@ static void cfe_stop_streaming(struct vb2_queue *vq) unsigned long flags; bool fe_stop; - cfe_dbg("%s: [%s] begin.\n", __func__, node_desc[node->id].name); + cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); spin_lock_irqsave(&cfe->state_lock, flags); fe_stop = is_fe_enabled(cfe) && @@ -1214,13 +1286,24 @@ static void cfe_stop_streaming(struct vb2_queue *vq) cfe_stop_channel(node, fe_stop); if (!test_any_node(cfe, NODE_STREAMING)) { - /* Stop streaming the sensor and disable the peripheral. */ - if (v4l2_subdev_call(cfe->sensor, video, s_stream, 0) < 0) - cfe_err("stream off failed in subdev\n"); + struct v4l2_subdev_state *state; + int ret; + + state = v4l2_subdev_lock_and_get_active_state(&cfe->csi2.sd); + + ret = v4l2_subdev_disable_streams(cfe->source_sd, + cfe->source_pad, + cfe->streams_mask); + if (ret) + cfe_err(cfe, "stream disable failed in subdev\n"); + + v4l2_subdev_unlock_state(state); csi2_close_rx(&cfe->csi2); cfg_reg_write(cfe, MIPICFG_INTE, 0); + + cfe_dbg(cfe, "%s: Streaming disabled\n", __func__); } media_pipeline_stop(&node->pad); @@ -1229,8 +1312,6 @@ static void cfe_stop_streaming(struct vb2_queue *vq) cfe_return_buffers(node, VB2_BUF_STATE_ERROR); pm_runtime_put(&cfe->pdev->dev); - - cfe_dbg("%s: [%s] end.\n", __func__, node_desc[node->id].name); } static const struct vb2_ops cfe_video_qops = { @@ -1250,15 +1331,9 @@ static const struct vb2_ops cfe_video_qops = { static int cfe_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct cfe_node *node = video_drvdata(file); - struct cfe_device *cfe = node->cfe; - strscpy(cap->driver, CFE_MODULE_NAME, sizeof(cap->driver)); strscpy(cap->card, CFE_MODULE_NAME, sizeof(cap->card)); - snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", - dev_name(&cfe->pdev->dev)); - cap->capabilities |= V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE | V4L2_CAP_META_OUTPUT; @@ -1275,7 +1350,7 @@ static int cfe_enum_fmt_vid_cap(struct file *file, void *priv, if (!node_supports_image_output(node)) return -EINVAL; - cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name); + cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); for (i = 0, j = 0; i < ARRAY_SIZE(formats); i++) { if (f->mbus_code && formats[i].code != f->mbus_code) @@ -1300,13 +1375,9 @@ static int cfe_enum_fmt_vid_cap(struct file *file, void *priv, return -EINVAL; } -static int cfe_g_fmt(struct file *file, void *priv, - struct v4l2_format *f) +static int cfe_g_fmt(struct file *file, void *priv, struct v4l2_format *f) { struct cfe_node *node = video_drvdata(file); - struct cfe_device *cfe = node->cfe; - - cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name); if (!node_supports_image(node)) return -EINVAL; @@ -1316,15 +1387,15 @@ static int cfe_g_fmt(struct file *file, void *priv, return 0; } -static int try_fmt_vid_cap(struct cfe_node *node, struct v4l2_format *f) +static int cfe_validate_fmt_vid_cap(struct cfe_node *node, + struct v4l2_format *f) { struct cfe_device *cfe = node->cfe; const struct cfe_fmt *fmt; - cfe_dbg("%s: [%s] %ux%u, V4L2 pix " V4L2_FOURCC_CONV "\n", - __func__, node_desc[node->id].name, - f->fmt.pix.width, f->fmt.pix.height, - V4L2_FOURCC_CONV_ARGS(f->fmt.pix.pixelformat)); + cfe_dbg(cfe, "%s: [%s] %ux%u, V4L2 pix %p4cc\n", __func__, + node_desc[node->id].name, f->fmt.pix.width, f->fmt.pix.height, + &f->fmt.pix.pixelformat); if (!node_supports_image_output(node)) return -EINVAL; @@ -1345,7 +1416,7 @@ static int try_fmt_vid_cap(struct cfe_node *node, struct v4l2_format *f) f->fmt.pix.field = V4L2_FIELD_NONE; - cfe_calc_format_size_bpl(cfe, fmt, f); + cfe_calc_vid_format_size_bpl(cfe, fmt, f); return 0; } @@ -1358,20 +1429,18 @@ static int cfe_s_fmt_vid_cap(struct file *file, void *priv, struct vb2_queue *q = &node->buffer_queue; int ret; - cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name); - if (vb2_is_busy(q)) return -EBUSY; - ret = try_fmt_vid_cap(node, f); + ret = cfe_validate_fmt_vid_cap(node, f); if (ret) return ret; node->vid_fmt = *f; - cfe_dbg("%s: Set %ux%u, V4L2 pix " V4L2_FOURCC_CONV "\n", __func__, + cfe_dbg(cfe, "%s: Set %ux%u, V4L2 pix %p4cc\n", __func__, node->vid_fmt.fmt.pix.width, node->vid_fmt.fmt.pix.height, - V4L2_FOURCC_CONV_ARGS(node->vid_fmt.fmt.pix.pixelformat)); + &node->vid_fmt.fmt.pix.pixelformat); return 0; } @@ -1382,9 +1451,9 @@ static int cfe_try_fmt_vid_cap(struct file *file, void *priv, struct cfe_node *node = video_drvdata(file); struct cfe_device *cfe = node->cfe; - cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name); + cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); - return try_fmt_vid_cap(node, f); + return cfe_validate_fmt_vid_cap(node, f); } static int cfe_enum_fmt_meta(struct file *file, void *priv, @@ -1393,40 +1462,81 @@ static int cfe_enum_fmt_meta(struct file *file, void *priv, struct cfe_node *node = video_drvdata(file); struct cfe_device *cfe = node->cfe; - cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name); + cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); - if (!node_supports_meta(node) || f->index != 0) + if (!node_supports_meta(node)) return -EINVAL; switch (node->id) { case CSI2_CH0...CSI2_CH3: - f->pixelformat = V4L2_META_FMT_SENSOR_DATA; - return 0; + f->flags = V4L2_FMT_FLAG_META_LINE_BASED; + + switch (f->index) { + case 0: + f->pixelformat = V4L2_META_FMT_GENERIC_8; + return 0; + case 1: + f->pixelformat = V4L2_META_FMT_GENERIC_CSI2_10; + return 0; + case 2: + f->pixelformat = V4L2_META_FMT_GENERIC_CSI2_12; + return 0; + default: + return -EINVAL; + } + default: + break; + } + + if (f->index != 0) + return -EINVAL; + + switch (node->id) { case FE_STATS: f->pixelformat = V4L2_META_FMT_RPI_FE_STATS; return 0; case FE_CONFIG: f->pixelformat = V4L2_META_FMT_RPI_FE_CFG; return 0; + default: + return -EINVAL; } - - return -EINVAL; } -static int try_fmt_meta(struct cfe_node *node, struct v4l2_format *f) +static int cfe_validate_fmt_meta(struct cfe_node *node, struct v4l2_format *f) { + struct cfe_device *cfe = node->cfe; + const struct cfe_fmt *fmt; + + switch (node->id) { + case CSI2_CH0...CSI2_CH3: + cfe_dbg(cfe, "%s: [%s] %ux%u, V4L2 meta %p4cc\n", __func__, + node_desc[node->id].name, f->fmt.meta.width, + f->fmt.meta.height, &f->fmt.meta.dataformat); + break; + case FE_STATS: + case FE_CONFIG: + cfe_dbg(cfe, "%s: [%s] %u bytes, V4L2 meta %p4cc\n", __func__, + node_desc[node->id].name, f->fmt.meta.buffersize, + &f->fmt.meta.dataformat); + break; + default: + return -EINVAL; + } + if (!node_supports_meta(node)) return -EINVAL; switch (node->id) { case CSI2_CH0...CSI2_CH3: - f->fmt.meta.dataformat = V4L2_META_FMT_SENSOR_DATA; - if (!f->fmt.meta.buffersize) - f->fmt.meta.buffersize = DEFAULT_EMBEDDED_SIZE; - f->fmt.meta.buffersize = - min_t(u32, f->fmt.meta.buffersize, MAX_BUFFER_SIZE); - f->fmt.meta.buffersize = - ALIGN(f->fmt.meta.buffersize, BPL_ALIGNMENT); + fmt = find_format_by_pix(f->fmt.meta.dataformat); + if (!fmt || !(fmt->flags & CFE_FORMAT_FLAG_META_CAP)) + fmt = find_format_by_pix(V4L2_META_FMT_GENERIC_CSI2_10); + + f->fmt.meta.dataformat = fmt->fourcc; + + cfe_calc_meta_format_size_bpl(cfe, fmt, f); + return 0; case FE_STATS: f->fmt.meta.dataformat = V4L2_META_FMT_RPI_FE_STATS; @@ -1436,9 +1546,9 @@ static int try_fmt_meta(struct cfe_node *node, struct v4l2_format *f) f->fmt.meta.dataformat = V4L2_META_FMT_RPI_FE_CFG; f->fmt.meta.buffersize = sizeof(struct pisp_fe_config); return 0; + default: + return -EINVAL; } - - return -EINVAL; } static int cfe_g_fmt_meta(struct file *file, void *priv, struct v4l2_format *f) @@ -1446,7 +1556,7 @@ static int cfe_g_fmt_meta(struct file *file, void *priv, struct v4l2_format *f) struct cfe_node *node = video_drvdata(file); struct cfe_device *cfe = node->cfe; - cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name); + cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); if (!node_supports_meta(node)) return -EINVAL; @@ -1463,7 +1573,7 @@ static int cfe_s_fmt_meta(struct file *file, void *priv, struct v4l2_format *f) struct vb2_queue *q = &node->buffer_queue; int ret; - cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name); + cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); if (vb2_is_busy(q)) return -EBUSY; @@ -1471,14 +1581,14 @@ static int cfe_s_fmt_meta(struct file *file, void *priv, struct v4l2_format *f) if (!node_supports_meta(node)) return -EINVAL; - ret = try_fmt_meta(node, f); + ret = cfe_validate_fmt_meta(node, f); if (ret) return ret; node->meta_fmt = *f; - cfe_dbg("%s: Set " V4L2_FOURCC_CONV "\n", __func__, - V4L2_FOURCC_CONV_ARGS(node->meta_fmt.fmt.meta.dataformat)); + cfe_dbg(cfe, "%s: Set %p4cc\n", __func__, + &node->meta_fmt.fmt.meta.dataformat); return 0; } @@ -1489,8 +1599,8 @@ static int cfe_try_fmt_meta(struct file *file, void *priv, struct cfe_node *node = video_drvdata(file); struct cfe_device *cfe = node->cfe; - cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name); - return try_fmt_meta(node, f); + cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); + return cfe_validate_fmt_meta(node, f); } static int cfe_enum_framesizes(struct file *file, void *priv, @@ -1500,7 +1610,7 @@ static int cfe_enum_framesizes(struct file *file, void *priv, struct cfe_device *cfe = node->cfe; const struct cfe_fmt *fmt; - cfe_dbg("%s [%s]\n", __func__, node_desc[node->id].name); + cfe_dbg(cfe, "%s [%s]\n", __func__, node_desc[node->id].name); if (fsize->index > 0) return -EINVAL; @@ -1508,7 +1618,7 @@ static int cfe_enum_framesizes(struct file *file, void *priv, /* check for valid format */ fmt = find_format_by_pix(fsize->pixel_format); if (!fmt) { - cfe_dbg("Invalid pixel code: %x\n", fsize->pixel_format); + cfe_dbg(cfe, "Invalid pixel code: %x\n", fsize->pixel_format); return -EINVAL; } @@ -1533,7 +1643,7 @@ static int cfe_vb2_ioctl_reqbufs(struct file *file, void *priv, struct cfe_device *cfe = node->cfe; int ret; - cfe_dbg("%s: [%s] type:%u\n", __func__, node_desc[node->id].name, + cfe_dbg(cfe, "%s: [%s] type:%u\n", __func__, node_desc[node->id].name, p->type); if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && @@ -1556,7 +1666,7 @@ static int cfe_vb2_ioctl_create_bufs(struct file *file, void *priv, struct cfe_device *cfe = node->cfe; int ret; - cfe_dbg("%s: [%s] type:%u\n", __func__, node_desc[node->id].name, + cfe_dbg(cfe, "%s: [%s] type:%u\n", __func__, node_desc[node->id].name, p->format.type); if (p->format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE && @@ -1630,11 +1740,10 @@ static void cfe_notify(struct v4l2_subdev *sd, unsigned int notification, void *arg) { struct cfe_device *cfe = to_cfe_device(sd->v4l2_dev); - unsigned int i; switch (notification) { case V4L2_DEVICE_NOTIFY_EVENT: - for (i = 0; i < NUM_NODES; i++) { + for (unsigned int i = 0; i < NUM_NODES; i++) { struct cfe_node *node = &cfe->node[i]; if (check_state(cfe, NODE_REGISTERED, i)) @@ -1669,13 +1778,13 @@ static int cfe_video_link_validate(struct media_link *link) struct v4l2_subdev *source_sd; int ret = 0; - cfe_dbg("%s: [%s] link \"%s\":%u -> \"%s\":%u\n", __func__, + cfe_dbg(cfe, "%s: [%s] link \"%s\":%u -> \"%s\":%u\n", __func__, node_desc[node->id].name, link->source->entity->name, link->source->index, link->sink->entity->name, link->sink->index); if (!media_entity_remote_source_pad_unique(link->sink->entity)) { - cfe_err("video node %s pad not connected\n", vd->name); + cfe_err(cfe, "video node %s pad not connected\n", vd->name); return -ENOTCONN; } @@ -1683,8 +1792,7 @@ static int cfe_video_link_validate(struct media_link *link) state = v4l2_subdev_lock_and_get_active_state(source_sd); - source_fmt = v4l2_subdev_get_pad_format(source_sd, state, - link->source->index); + source_fmt = v4l2_subdev_state_get_format(state, link->source->index); if (!source_fmt) { ret = -EINVAL; goto out; @@ -1692,54 +1800,41 @@ static int cfe_video_link_validate(struct media_link *link) if (is_image_output_node(node)) { struct v4l2_pix_format *pix_fmt = &node->vid_fmt.fmt.pix; - const struct cfe_fmt *fmt = NULL; - unsigned int i; + const struct cfe_fmt *fmt; if (source_fmt->width != pix_fmt->width || source_fmt->height != pix_fmt->height) { - cfe_err("Wrong width or height %ux%u (remote pad set to %ux%u)\n", + cfe_err(cfe, "Wrong width or height %ux%u (remote pad set to %ux%u)\n", pix_fmt->width, pix_fmt->height, - source_fmt->width, - source_fmt->height); + source_fmt->width, source_fmt->height); ret = -EINVAL; goto out; } - for (i = 0; i < ARRAY_SIZE(formats); i++) { - if (formats[i].code == source_fmt->code && - formats[i].fourcc == pix_fmt->pixelformat) { - fmt = &formats[i]; - break; - } - } + fmt = find_format_by_code_and_fourcc(source_fmt->code, + pix_fmt->pixelformat); if (!fmt) { - cfe_err("Format mismatch!\n"); + cfe_err(cfe, "Format mismatch!\n"); ret = -EINVAL; goto out; } } else if (is_csi2_node(node) && is_meta_output_node(node)) { struct v4l2_meta_format *meta_fmt = &node->meta_fmt.fmt.meta; const struct cfe_fmt *fmt; - u32 source_size; - - fmt = find_format_by_code(source_fmt->code); - if (!fmt || fmt->fourcc != meta_fmt->dataformat) { - cfe_err("Metadata format mismatch!\n"); - ret = -EINVAL; - goto out; - } - source_size = DIV_ROUND_UP(source_fmt->width * source_fmt->height * fmt->depth, 8); - - if (source_fmt->code != MEDIA_BUS_FMT_SENSOR_DATA) { - cfe_err("Bad metadata mbus format\n"); + if (source_fmt->width != meta_fmt->width || + source_fmt->height != meta_fmt->height) { + cfe_err(cfe, "Wrong width or height %ux%u (remote pad set to %ux%u)\n", + meta_fmt->width, meta_fmt->height, + source_fmt->width, source_fmt->height); ret = -EINVAL; goto out; } - if (source_size > meta_fmt->buffersize) { - cfe_err("Metadata buffer too small: %u < %u\n", - meta_fmt->buffersize, source_size); + fmt = find_format_by_code_and_fourcc(source_fmt->code, + meta_fmt->dataformat); + if (!fmt) { + cfe_err(cfe, "Format mismatch!\n"); ret = -EINVAL; goto out; } @@ -1763,18 +1858,17 @@ static int cfe_video_link_notify(struct media_link *link, u32 flags, struct media_entity *fe = &cfe->fe.sd.entity; struct media_entity *csi2 = &cfe->csi2.sd.entity; unsigned long lock_flags; - unsigned int i; if (notification != MEDIA_DEV_NOTIFY_POST_LINK_CH) return 0; - cfe_dbg("%s: %s[%u] -> %s[%u] 0x%x", __func__, + cfe_dbg(cfe, "%s: %s[%u] -> %s[%u] 0x%x", __func__, link->source->entity->name, link->source->index, link->sink->entity->name, link->sink->index, flags); spin_lock_irqsave(&cfe->state_lock, lock_flags); - for (i = 0; i < NUM_NODES; i++) { + for (unsigned int i = 0; i < NUM_NODES; i++) { if (link->sink->entity != &cfe->node[i].video_dev.entity && link->source->entity != &cfe->node[i].video_dev.entity) continue; @@ -1809,10 +1903,11 @@ static int cfe_video_link_notify(struct media_link *link, u32 flags, } if (is_fe_enabled(cfe)) - cfe_dbg("%s: Found CSI2:%d -> FE:0 link\n", __func__, + cfe_dbg(cfe, "%s: Found CSI2:%d -> FE:0 link\n", __func__, cfe->fe_csi2_channel); else - cfe_dbg("%s: Unable to find CSI2:x -> FE:0 link\n", __func__); + cfe_dbg(cfe, "%s: Unable to find CSI2:x -> FE:0 link\n", + __func__); return 0; } @@ -1866,14 +1961,15 @@ static int cfe_register_node(struct cfe_device *cfe, int id) fmt = find_format_by_code(cfe_default_format.code); if (!fmt) { - cfe_err("Failed to find format code\n"); + cfe_err(cfe, "Failed to find format code\n"); return -EINVAL; } node->vid_fmt.fmt.pix.pixelformat = fmt->fourcc; - v4l2_fill_pix_format(&node->vid_fmt.fmt.pix, &cfe_default_format); + v4l2_fill_pix_format(&node->vid_fmt.fmt.pix, + &cfe_default_format); - ret = try_fmt_vid_cap(node, &node->vid_fmt); + ret = cfe_validate_fmt_vid_cap(node, &node->vid_fmt); if (ret) return ret; } @@ -1884,7 +1980,7 @@ static int cfe_register_node(struct cfe_device *cfe, int id) else node->meta_fmt.type = V4L2_BUF_TYPE_META_OUTPUT; - ret = try_fmt_meta(node, &node->meta_fmt); + ret = cfe_validate_fmt_meta(node, &node->meta_fmt); if (ret) return ret; } @@ -1907,7 +2003,7 @@ static int cfe_register_node(struct cfe_device *cfe, int id) ret = vb2_queue_init(q); if (ret) { - cfe_err("vb2_queue_init() failed\n"); + cfe_err(cfe, "vb2_queue_init() failed\n"); return ret; } @@ -1933,25 +2029,23 @@ static int cfe_register_node(struct cfe_device *cfe, int id) node_desc[id].name); video_set_drvdata(vdev, node); - if (node->id == FE_OUT0) - vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT; node->pad.flags = node_desc[id].pad_flags; media_entity_pads_init(&vdev->entity, 1, &node->pad); if (!node_supports_image(node)) { v4l2_disable_ioctl(&node->video_dev, VIDIOC_ENUM_FRAMEINTERVALS); - v4l2_disable_ioctl(&node->video_dev, - VIDIOC_ENUM_FRAMESIZES); + v4l2_disable_ioctl(&node->video_dev, VIDIOC_ENUM_FRAMESIZES); } ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) { - cfe_err("Unable to register video device %s\n", vdev->name); + cfe_err(cfe, "Unable to register video device %s\n", + vdev->name); return ret; } - cfe_info("Registered [%s] node id %d successfully as /dev/video%u\n", + cfe_info(cfe, "Registered [%s] node id %d as /dev/video%u\n", vdev->name, id, vdev->num); /* @@ -1967,9 +2061,7 @@ static int cfe_register_node(struct cfe_device *cfe, int id) static void cfe_unregister_nodes(struct cfe_device *cfe) { - unsigned int i; - - for (i = 0; i < NUM_NODES; i++) { + for (unsigned int i = 0; i < NUM_NODES; i++) { struct cfe_node *node = &cfe->node[i]; if (check_state(cfe, NODE_REGISTERED, i)) { @@ -1981,34 +2073,36 @@ static void cfe_unregister_nodes(struct cfe_device *cfe) static int cfe_link_node_pads(struct cfe_device *cfe) { - unsigned int i, source_pad = 0; + struct media_pad *remote_pad; int ret; - for (i = 0; i < CSI2_NUM_CHANNELS; i++) { + /* Source -> CSI2 */ + + ret = v4l2_create_fwnode_links_to_pad(cfe->source_sd, + &cfe->csi2.pad[CSI2_PAD_SINK], + MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); + + if (ret) { + cfe_err(cfe, "Failed to create links to the source: %d\n", ret); + return ret; + } + + remote_pad = media_pad_remote_pad_unique(&cfe->csi2.pad[CSI2_PAD_SINK]); + if (IS_ERR(remote_pad)) { + ret = PTR_ERR(remote_pad); + cfe_err(cfe, "Failed to get unique remote source pad: %d\n", + ret); + return ret; + } + + cfe->source_pad = remote_pad->index; + + for (unsigned int i = 0; i < CSI2_NUM_CHANNELS; i++) { struct cfe_node *node = &cfe->node[i]; if (!check_state(cfe, NODE_REGISTERED, i)) continue; - /* Find next source pad */ - while (source_pad < cfe->sensor->entity.num_pads && - !(cfe->sensor->entity.pads[source_pad].flags & - MEDIA_PAD_FL_SOURCE)) - source_pad++; - - if (source_pad < cfe->sensor->entity.num_pads) { - /* Sensor -> CSI2 */ - ret = media_create_pad_link(&cfe->sensor->entity, source_pad, - &cfe->csi2.sd.entity, i, - MEDIA_LNK_FL_IMMUTABLE | - MEDIA_LNK_FL_ENABLED); - if (ret) - return ret; - - /* Dealt with that source_pad, look at the next one next time */ - source_pad++; - } - /* CSI2 channel # -> /dev/video# */ ret = media_create_pad_link(&cfe->csi2.sd.entity, node_desc[i].link_pad, @@ -2027,7 +2121,7 @@ static int cfe_link_node_pads(struct cfe_device *cfe) } } - for (; i < NUM_NODES; i++) { + for (unsigned int i = CSI2_NUM_CHANNELS; i < NUM_NODES; i++) { struct cfe_node *node = &cfe->node[i]; struct media_entity *src, *dst; unsigned int src_pad, dst_pad; @@ -2056,28 +2150,27 @@ static int cfe_link_node_pads(struct cfe_device *cfe) static int cfe_probe_complete(struct cfe_device *cfe) { - unsigned int i; int ret; cfe->v4l2_dev.notify = cfe_notify; - for (i = 0; i < NUM_NODES; i++) { + for (unsigned int i = 0; i < NUM_NODES; i++) { ret = cfe_register_node(cfe, i); if (ret) { - cfe_err("Unable to register video node %u.\n", i); + cfe_err(cfe, "Unable to register video node %u.\n", i); goto unregister; } } ret = cfe_link_node_pads(cfe); if (ret) { - cfe_err("Unable to link node pads.\n"); + cfe_err(cfe, "Unable to link node pads.\n"); goto unregister; } ret = v4l2_device_register_subdev_nodes(&cfe->v4l2_dev); if (ret) { - cfe_err("Unable to register subdev nodes.\n"); + cfe_err(cfe, "Unable to register subdev nodes.\n"); goto unregister; } @@ -2094,13 +2187,15 @@ static int cfe_async_bound(struct v4l2_async_notifier *notifier, { struct cfe_device *cfe = to_cfe_device(notifier->v4l2_dev); - if (cfe->sensor) { - cfe_info("Rejecting subdev %s (Already set!!)", subdev->name); + if (cfe->source_sd) { + cfe_err(cfe, "Rejecting subdev %s (Already set!!)", + subdev->name); return 0; } - cfe->sensor = subdev; - cfe_info("Using sensor %s for capture\n", subdev->name); + cfe->source_sd = subdev; + + cfe_dbg(cfe, "Using source %s for capture\n", subdev->name); return 0; } @@ -2117,81 +2212,66 @@ static const struct v4l2_async_notifier_operations cfe_async_ops = { .complete = cfe_async_complete, }; -static int of_cfe_connect_subdevs(struct cfe_device *cfe) +static int cfe_register_async_nf(struct cfe_device *cfe) { struct platform_device *pdev = cfe->pdev; struct v4l2_fwnode_endpoint ep = { .bus_type = V4L2_MBUS_CSI2_DPHY }; - struct device_node *node = pdev->dev.of_node; - struct device_node *ep_node; - struct device_node *sensor_node; - unsigned int lane; - int ret = -EINVAL; - - /* Get the local endpoint and remote device. */ - ep_node = of_graph_get_next_endpoint(node, NULL); - if (!ep_node) { - cfe_err("can't get next endpoint\n"); - return -EINVAL; - } - - cfe_dbg("ep_node is %pOF\n", ep_node); + struct fwnode_handle *local_ep_fwnode; + struct v4l2_async_connection *asd; + int ret; - sensor_node = of_graph_get_remote_port_parent(ep_node); - if (!sensor_node) { - cfe_err("can't get remote parent\n"); - goto cleanup_exit; + local_ep_fwnode = fwnode_graph_get_endpoint_by_id(pdev->dev.fwnode, 0, + 0, 0); + if (!local_ep_fwnode) { + cfe_err(cfe, "Failed to find local endpoint fwnode\n"); + return -ENODEV; } - cfe_info("found subdevice %pOF\n", sensor_node); - /* Parse the local endpoint and validate its configuration. */ - v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), &ep); - - cfe->csi2.multipacket_line = - fwnode_property_present(of_fwnode_handle(ep_node), - "multipacket-line"); - - if (ep.bus_type != V4L2_MBUS_CSI2_DPHY) { - cfe_err("endpoint node type != CSI2\n"); - return -EINVAL; + ret = v4l2_fwnode_endpoint_parse(local_ep_fwnode, &ep); + if (ret) { + cfe_err(cfe, "Failed to find remote endpoint fwnode\n"); + goto err_put_local_fwnode; } - for (lane = 0; lane < ep.bus.mipi_csi2.num_data_lanes; lane++) { + for (unsigned int lane = 0; lane < ep.bus.mipi_csi2.num_data_lanes; + lane++) { if (ep.bus.mipi_csi2.data_lanes[lane] != lane + 1) { - cfe_err("subdevice %pOF: data lanes reordering not supported\n", - sensor_node); - goto cleanup_exit; + cfe_err(cfe, "Data lanes reordering not supported\n"); + ret = -EINVAL; + goto err_put_local_fwnode; } } cfe->csi2.dphy.max_lanes = ep.bus.mipi_csi2.num_data_lanes; cfe->csi2.bus_flags = ep.bus.mipi_csi2.flags; - cfe_dbg("subdevice %pOF: %u data lanes, flags=0x%08x, multipacket_line=%u\n", - sensor_node, cfe->csi2.dphy.max_lanes, cfe->csi2.bus_flags, - cfe->csi2.multipacket_line); - /* Initialize and register the async notifier. */ v4l2_async_nf_init(&cfe->notifier, &cfe->v4l2_dev); cfe->notifier.ops = &cfe_async_ops; - cfe->asd = v4l2_async_nf_add_fwnode(&cfe->notifier, - of_fwnode_handle(sensor_node), - struct v4l2_async_connection); - if (IS_ERR(cfe->asd)) { - cfe_err("Error adding subdevice: %d\n", ret); - goto cleanup_exit; + asd = v4l2_async_nf_add_fwnode_remote(&cfe->notifier, local_ep_fwnode, + struct v4l2_async_connection); + if (IS_ERR(asd)) { + ret = PTR_ERR(asd); + cfe_err(cfe, "Error adding subdevice: %d\n", ret); + goto err_put_local_fwnode; } ret = v4l2_async_nf_register(&cfe->notifier); if (ret) { - cfe_err("Error registering async notifier: %d\n", ret); - ret = -EINVAL; + cfe_err(cfe, "Error registering async notifier: %d\n", ret); + goto err_nf_cleanup; } -cleanup_exit: - of_node_put(sensor_node); - of_node_put(ep_node); + fwnode_handle_put(local_ep_fwnode); + + return 0; + +err_nf_cleanup: + v4l2_async_nf_cleanup(&cfe->notifier); +err_put_local_fwnode: + fwnode_handle_put(local_ep_fwnode); return ret; } @@ -2280,15 +2360,14 @@ static int cfe_probe(struct platform_device *pdev) ret = v4l2_device_register(&pdev->dev, &cfe->v4l2_dev); if (ret) { - cfe_err("Unable to register v4l2 device.\n"); + cfe_err(cfe, "Unable to register v4l2 device.\n"); goto err_cfe_put; } snprintf(debugfs_name, sizeof(debugfs_name), "rp1-cfe:%s", dev_name(&pdev->dev)); cfe->debugfs = debugfs_create_dir(debugfs_name, NULL); - debugfs_create_file("format", 0444, cfe->debugfs, cfe, &format_fops); - debugfs_create_file("regs", 0444, cfe->debugfs, cfe, + debugfs_create_file("regs", 0440, cfe->debugfs, cfe, &mipi_cfg_regs_fops); /* Enable the block power domain */ @@ -2301,27 +2380,27 @@ static int cfe_probe(struct platform_device *pdev) cfe->csi2.v4l2_dev = &cfe->v4l2_dev; ret = csi2_init(&cfe->csi2, cfe->debugfs); if (ret) { - cfe_err("Failed to init csi2 (%d)\n", ret); + cfe_err(cfe, "Failed to init csi2 (%d)\n", ret); goto err_runtime_put; } cfe->fe.v4l2_dev = &cfe->v4l2_dev; ret = pisp_fe_init(&cfe->fe, cfe->debugfs); if (ret) { - cfe_err("Failed to init pisp fe (%d)\n", ret); + cfe_err(cfe, "Failed to init pisp fe (%d)\n", ret); goto err_csi2_uninit; } cfe->mdev.hw_revision = cfe->fe.hw_revision; ret = media_device_register(&cfe->mdev); if (ret < 0) { - cfe_err("Unable to register media-controller device.\n"); + cfe_err(cfe, "Unable to register media-controller device.\n"); goto err_pisp_fe_uninit; } - ret = of_cfe_connect_subdevs(cfe); + ret = cfe_register_async_nf(cfe); if (ret) { - cfe_err("Failed to connect subdevs\n"); + cfe_err(cfe, "Failed to connect subdevs\n"); goto err_media_unregister; } @@ -2354,6 +2433,8 @@ static int cfe_remove(struct platform_device *pdev) debugfs_remove(cfe->debugfs); v4l2_async_nf_unregister(&cfe->notifier); + v4l2_async_nf_cleanup(&cfe->notifier); + media_device_unregister(&cfe->mdev); cfe_unregister_nodes(cfe); @@ -2396,7 +2477,8 @@ static int cfe_runtime_resume(struct device *dev) static const struct dev_pm_ops cfe_pm_ops = { SET_RUNTIME_PM_OPS(cfe_runtime_suspend, cfe_runtime_resume, NULL) - SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) + SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) }; static const struct of_device_id cfe_of_match[] = { @@ -2418,6 +2500,7 @@ static struct platform_driver cfe_driver = { module_platform_driver(cfe_driver); MODULE_AUTHOR("Naushir Patuck "); -MODULE_DESCRIPTION("RP1 Camera Front End driver"); +MODULE_AUTHOR("Tomi Valkeinen "); +MODULE_DESCRIPTION("Raspberry Pi RP1 Camera Front End driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(CFE_VERSION); diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.h b/drivers/media/platform/raspberrypi/rp1-cfe/cfe.h similarity index 86% rename from drivers/media/platform/raspberrypi/rp1_cfe/cfe.h rename to drivers/media/platform/raspberrypi/rp1-cfe/cfe.h index 637b63a838c407..c63cc314be3caa 100644 --- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.h +++ b/drivers/media/platform/raspberrypi/rp1-cfe/cfe.h @@ -1,14 +1,15 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * RP1 CFE driver. - * Copyright (c) 2021 Raspberry Pi Ltd. + * RP1 CFE Driver * + * Copyright (c) 2021-2024 Raspberry Pi Ltd. + * Copyright (c) 2023-2024 Ideas on Board Oy */ #ifndef _RP1_CFE_ #define _RP1_CFE_ -#include #include +#include #include extern bool cfe_debug_verbose; @@ -33,7 +34,6 @@ struct cfe_fmt { }; extern const struct v4l2_mbus_framefmt cfe_default_format; -extern const struct v4l2_mbus_framefmt cfe_default_meta_format; const struct cfe_fmt *find_format_by_code(u32 code); const struct cfe_fmt *find_format_by_pix(u32 pixelformat); diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c b/drivers/media/platform/raspberrypi/rp1-cfe/csi2.c similarity index 61% rename from drivers/media/platform/raspberrypi/rp1_cfe/csi2.c rename to drivers/media/platform/raspberrypi/rp1-cfe/csi2.c index 015bd3feac8d14..35c2ab1e2cd4d5 100644 --- a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c +++ b/drivers/media/platform/raspberrypi/rp1-cfe/csi2.c @@ -2,8 +2,8 @@ /* * RP1 CSI-2 Driver * - * Copyright (C) 2021 - Raspberry Pi Ltd. - * + * Copyright (c) 2021-2024 Raspberry Pi Ltd. + * Copyright (c) 2023-2024 Ideas on Board Oy */ #include @@ -13,21 +13,17 @@ #include -#include "csi2.h" #include "cfe.h" +#include "csi2.h" + +#include "cfe-trace.h" static bool csi2_track_errors; module_param_named(track_csi2_errors, csi2_track_errors, bool, 0); MODULE_PARM_DESC(track_csi2_errors, "track csi-2 errors"); -#define csi2_dbg_verbose(fmt, arg...) \ - do { \ - if (cfe_debug_verbose) \ - dev_dbg(csi2->v4l2_dev->dev, fmt, ##arg); \ - } while (0) -#define csi2_dbg(fmt, arg...) dev_dbg(csi2->v4l2_dev->dev, fmt, ##arg) -#define csi2_info(fmt, arg...) dev_info(csi2->v4l2_dev->dev, fmt, ##arg) -#define csi2_err(fmt, arg...) dev_err(csi2->v4l2_dev->dev, fmt, ##arg) +#define csi2_dbg(csi2, fmt, arg...) dev_dbg((csi2)->v4l2_dev->dev, fmt, ##arg) +#define csi2_err(csi2, fmt, arg...) dev_err((csi2)->v4l2_dev->dev, fmt, ##arg) /* CSI2-DMA registers */ #define CSI2_STATUS 0x000 @@ -70,42 +66,45 @@ MODULE_PARM_DESC(track_csi2_errors, "track csi-2 errors"); #define CSI2_CH_FE_FRAME_ID(x) ((x) * 0x40 + 0x48) /* CSI2_STATUS */ -#define IRQ_FS(x) (BIT(0) << (x)) -#define IRQ_FE(x) (BIT(4) << (x)) -#define IRQ_FE_ACK(x) (BIT(8) << (x)) -#define IRQ_LE(x) (BIT(12) << (x)) -#define IRQ_LE_ACK(x) (BIT(16) << (x)) -#define IRQ_CH_MASK(x) (IRQ_FS(x) | IRQ_FE(x) | IRQ_FE_ACK(x) | IRQ_LE(x) | IRQ_LE_ACK(x)) -#define IRQ_OVERFLOW BIT(20) -#define IRQ_DISCARD_OVERFLOW BIT(21) -#define IRQ_DISCARD_LEN_LIMIT BIT(22) -#define IRQ_DISCARD_UNMATCHED BIT(23) -#define IRQ_DISCARD_INACTIVE BIT(24) +#define CSI2_STATUS_IRQ_FS(x) (BIT(0) << (x)) +#define CSI2_STATUS_IRQ_FE(x) (BIT(4) << (x)) +#define CSI2_STATUS_IRQ_FE_ACK(x) (BIT(8) << (x)) +#define CSI2_STATUS_IRQ_LE(x) (BIT(12) << (x)) +#define CSI2_STATUS_IRQ_LE_ACK(x) (BIT(16) << (x)) +#define CSI2_STATUS_IRQ_CH_MASK(x) \ + (CSI2_STATUS_IRQ_FS(x) | CSI2_STATUS_IRQ_FE(x) | \ + CSI2_STATUS_IRQ_FE_ACK(x) | CSI2_STATUS_IRQ_LE(x) | \ + CSI2_STATUS_IRQ_LE_ACK(x)) +#define CSI2_STATUS_IRQ_OVERFLOW BIT(20) +#define CSI2_STATUS_IRQ_DISCARD_OVERFLOW BIT(21) +#define CSI2_STATUS_IRQ_DISCARD_LEN_LIMIT BIT(22) +#define CSI2_STATUS_IRQ_DISCARD_UNMATCHED BIT(23) +#define CSI2_STATUS_IRQ_DISCARD_INACTIVE BIT(24) /* CSI2_CTRL */ -#define EOP_IS_EOL BIT(0) +#define CSI2_CTRL_EOP_IS_EOL BIT(0) /* CSI2_CH_CTRL */ -#define DMA_EN BIT(0) -#define FORCE BIT(3) -#define AUTO_ARM BIT(4) -#define IRQ_EN_FS BIT(13) -#define IRQ_EN_FE BIT(14) -#define IRQ_EN_FE_ACK BIT(15) -#define IRQ_EN_LE BIT(16) -#define IRQ_EN_LE_ACK BIT(17) -#define FLUSH_FE BIT(28) -#define PACK_LINE BIT(29) -#define PACK_BYTES BIT(30) -#define CH_MODE_MASK GENMASK(2, 1) -#define VC_MASK GENMASK(6, 5) -#define DT_MASK GENMASK(12, 7) -#define LC_MASK GENMASK(27, 18) +#define CSI2_CH_CTRL_DMA_EN BIT(0) +#define CSI2_CH_CTRL_FORCE BIT(3) +#define CSI2_CH_CTRL_AUTO_ARM BIT(4) +#define CSI2_CH_CTRL_IRQ_EN_FS BIT(13) +#define CSI2_CH_CTRL_IRQ_EN_FE BIT(14) +#define CSI2_CH_CTRL_IRQ_EN_FE_ACK BIT(15) +#define CSI2_CH_CTRL_IRQ_EN_LE BIT(16) +#define CSI2_CH_CTRL_IRQ_EN_LE_ACK BIT(17) +#define CSI2_CH_CTRL_FLUSH_FE BIT(28) +#define CSI2_CH_CTRL_PACK_LINE BIT(29) +#define CSI2_CH_CTRL_PACK_BYTES BIT(30) +#define CSI2_CH_CTRL_CH_MODE_MASK GENMASK(2, 1) +#define CSI2_CH_CTRL_VC_MASK GENMASK(6, 5) +#define CSI2_CH_CTRL_DT_MASK GENMASK(12, 7) +#define CSI2_CH_CTRL_LC_MASK GENMASK(27, 18) /* CHx_COMPRESSION_CONTROL */ -#define COMP_OFFSET_MASK GENMASK(15, 0) -#define COMP_SHIFT_MASK GENMASK(19, 16) -#define COMP_MODE_MASK GENMASK(25, 24) +#define CSI2_CH_COMP_CTRL_OFFSET_MASK GENMASK(15, 0) +#define CSI2_CH_COMP_CTRL_SHIFT_MASK GENMASK(19, 16) +#define CSI2_CH_COMP_CTRL_MODE_MASK GENMASK(25, 24) static inline u32 csi2_reg_read(struct csi2_device *csi2, u32 offset) { @@ -115,7 +114,6 @@ static inline u32 csi2_reg_read(struct csi2_device *csi2, u32 offset) static inline void csi2_reg_write(struct csi2_device *csi2, u32 offset, u32 val) { writel(val, csi2->base + offset); - csi2_dbg_verbose("csi2: write 0x%04x -> 0x%03x\n", val, offset); } static inline void set_field(u32 *valp, u32 field, u32 mask) @@ -130,7 +128,6 @@ static inline void set_field(u32 *valp, u32 field, u32 mask) static int csi2_regs_show(struct seq_file *s, void *data) { struct csi2_device *csi2 = s->private; - unsigned int i; int ret; ret = pm_runtime_resume_and_get(csi2->v4l2_dev->dev); @@ -138,7 +135,8 @@ static int csi2_regs_show(struct seq_file *s, void *data) return ret; #define DUMP(reg) seq_printf(s, #reg " \t0x%08x\n", csi2_reg_read(csi2, reg)) -#define DUMP_CH(idx, reg) seq_printf(s, #reg "(%u) \t0x%08x\n", idx, csi2_reg_read(csi2, reg(idx))) +#define DUMP_CH(idx, reg) seq_printf(s, #reg "(%u) \t0x%08x\n", idx, \ + csi2_reg_read(csi2, reg(idx))) DUMP(CSI2_STATUS); DUMP(CSI2_DISCARDS_OVERFLOW); @@ -150,7 +148,7 @@ static int csi2_regs_show(struct seq_file *s, void *data) DUMP(CSI2_IRQ_MASK); DUMP(CSI2_CTRL); - for (i = 0; i < CSI2_NUM_CHANNELS; ++i) { + for (unsigned int i = 0; i < CSI2_NUM_CHANNELS; ++i) { DUMP_CH(i, CSI2_CH_CTRL); DUMP_CH(i, CSI2_CH_ADDR0); DUMP_CH(i, CSI2_CH_ADDR1); @@ -220,15 +218,15 @@ static void csi2_isr_handle_errors(struct csi2_device *csi2, u32 status) { spin_lock(&csi2->errors_lock); - if (status & IRQ_OVERFLOW) + if (status & CSI2_STATUS_IRQ_OVERFLOW) csi2->overflows++; for (unsigned int i = 0; i < DISCARDS_TABLE_NUM_ENTRIES; ++i) { static const u32 discard_bits[] = { - IRQ_DISCARD_OVERFLOW, - IRQ_DISCARD_LEN_LIMIT, - IRQ_DISCARD_UNMATCHED, - IRQ_DISCARD_INACTIVE, + CSI2_STATUS_IRQ_DISCARD_OVERFLOW, + CSI2_STATUS_IRQ_DISCARD_LEN_LIMIT, + CSI2_STATUS_IRQ_DISCARD_UNMATCHED, + CSI2_STATUS_IRQ_DISCARD_INACTIVE, }; static const u8 discard_regs[] = { CSI2_DISCARDS_OVERFLOW, @@ -260,36 +258,25 @@ static void csi2_isr_handle_errors(struct csi2_device *csi2, u32 status) void csi2_isr(struct csi2_device *csi2, bool *sof, bool *eof) { - unsigned int i; u32 status; status = csi2_reg_read(csi2, CSI2_STATUS); - csi2_dbg_verbose("ISR: STA: 0x%x\n", status); /* Write value back to clear the interrupts */ csi2_reg_write(csi2, CSI2_STATUS, status); - for (i = 0; i < CSI2_NUM_CHANNELS; i++) { + for (unsigned int i = 0; i < CSI2_NUM_CHANNELS; i++) { u32 dbg; - if ((status & IRQ_CH_MASK(i)) == 0) + if ((status & CSI2_STATUS_IRQ_CH_MASK(i)) == 0) continue; dbg = csi2_reg_read(csi2, CSI2_CH_DEBUG(i)); - csi2_dbg_verbose("ISR: [%u], %s%s%s%s%s frame: %u line: %u\n", - i, (status & IRQ_FS(i)) ? "FS " : "", - (status & IRQ_FE(i)) ? "FE " : "", - (status & IRQ_FE_ACK(i)) ? "FE_ACK " : "", - (status & IRQ_LE(i)) ? "LE " : "", - (status & IRQ_LE_ACK(i)) ? "LE_ACK " : "", - dbg >> 16, - csi2->num_lines[i] ? - ((dbg & 0xffff) % csi2->num_lines[i]) : - 0); - - sof[i] = !!(status & IRQ_FS(i)); - eof[i] = !!(status & IRQ_FE_ACK(i)); + trace_csi2_irq(i, status, dbg); + + sof[i] = !!(status & CSI2_STATUS_IRQ_FS(i)); + eof[i] = !!(status & CSI2_STATUS_IRQ_FE_ACK(i)); } if (csi2_track_errors) @@ -317,127 +304,56 @@ void csi2_set_compression(struct csi2_device *csi2, unsigned int channel, { u32 compression = 0; - set_field(&compression, COMP_OFFSET_MASK, offset); - set_field(&compression, COMP_SHIFT_MASK, shift); - set_field(&compression, COMP_MODE_MASK, mode); + set_field(&compression, CSI2_CH_COMP_CTRL_OFFSET_MASK, offset); + set_field(&compression, CSI2_CH_COMP_CTRL_SHIFT_MASK, shift); + set_field(&compression, CSI2_CH_COMP_CTRL_MODE_MASK, mode); csi2_reg_write(csi2, CSI2_CH_COMP_CTRL(channel), compression); } -static int csi2_get_vc_dt_fallback(struct csi2_device *csi2, - unsigned int channel, u8 *vc, u8 *dt) -{ - struct v4l2_subdev *sd = &csi2->sd; - struct v4l2_subdev_state *state; - struct v4l2_mbus_framefmt *fmt; - const struct cfe_fmt *cfe_fmt; - - state = v4l2_subdev_get_locked_active_state(sd); - - /* Without Streams API, the channel number matches the sink pad */ - fmt = v4l2_subdev_get_pad_format(sd, state, channel); - if (!fmt) - return -EINVAL; - - cfe_fmt = find_format_by_code(fmt->code); - if (!cfe_fmt) - return -EINVAL; - - *vc = 0; - *dt = cfe_fmt->csi_dt; - - return 0; -} - -static int csi2_get_vc_dt(struct csi2_device *csi2, unsigned int channel, - u8 *vc, u8 *dt) -{ - struct v4l2_mbus_frame_desc remote_desc; - const struct media_pad *remote_pad; - struct v4l2_subdev *source_sd; - int ret; - - /* Without Streams API, the channel number matches the sink pad */ - remote_pad = media_pad_remote_pad_first(&csi2->pad[channel]); - if (!remote_pad) - return -EPIPE; - - source_sd = media_entity_to_v4l2_subdev(remote_pad->entity); - - ret = v4l2_subdev_call(source_sd, pad, get_frame_desc, - remote_pad->index, &remote_desc); - if (ret == -ENOIOCTLCMD) { - csi2_dbg("source does not support get_frame_desc, use fallback\n"); - return csi2_get_vc_dt_fallback(csi2, channel, vc, dt); - } else if (ret) { - csi2_err("Failed to get frame descriptor\n"); - return ret; - } - - if (remote_desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { - csi2_err("Frame descriptor does not describe CSI-2 link"); - return -EINVAL; - } - - if (remote_desc.num_entries != 1) { - csi2_err("Frame descriptor does not have a single entry"); - return -EINVAL; - } - - *vc = remote_desc.entry[0].bus.csi2.vc; - *dt = remote_desc.entry[0].bus.csi2.dt; - - return 0; -} - void csi2_start_channel(struct csi2_device *csi2, unsigned int channel, - enum csi2_mode mode, bool auto_arm, - bool pack_bytes, unsigned int width, - unsigned int height) + enum csi2_mode mode, bool auto_arm, bool pack_bytes, + unsigned int width, unsigned int height, + u8 vc, u8 dt) { u32 ctrl; - int ret; - u8 vc, dt; - - ret = csi2_get_vc_dt(csi2, channel, &vc, &dt); - if (ret) - return; - csi2_dbg("%s [%u]\n", __func__, channel); + csi2_dbg(csi2, "%s [%u]\n", __func__, channel); csi2_reg_write(csi2, CSI2_CH_CTRL(channel), 0); csi2_reg_write(csi2, CSI2_CH_DEBUG(channel), 0); - csi2_reg_write(csi2, CSI2_STATUS, IRQ_CH_MASK(channel)); + csi2_reg_write(csi2, CSI2_STATUS, CSI2_STATUS_IRQ_CH_MASK(channel)); /* Enable channel and FS/FE interrupts. */ - ctrl = DMA_EN | IRQ_EN_FS | IRQ_EN_FE_ACK | PACK_LINE; + ctrl = CSI2_CH_CTRL_DMA_EN | CSI2_CH_CTRL_IRQ_EN_FS | + CSI2_CH_CTRL_IRQ_EN_FE_ACK | CSI2_CH_CTRL_PACK_LINE; /* PACK_BYTES ensures no striding for embedded data. */ if (pack_bytes) - ctrl |= PACK_BYTES; + ctrl |= CSI2_CH_CTRL_PACK_BYTES; if (auto_arm) - ctrl |= AUTO_ARM; + ctrl |= CSI2_CH_CTRL_AUTO_ARM; if (width && height) { - set_field(&ctrl, mode, CH_MODE_MASK); + set_field(&ctrl, mode, CSI2_CH_CTRL_CH_MODE_MASK); csi2_reg_write(csi2, CSI2_CH_FRAME_SIZE(channel), (height << 16) | width); } else { - set_field(&ctrl, 0x0, CH_MODE_MASK); + set_field(&ctrl, 0x0, CSI2_CH_CTRL_CH_MODE_MASK); csi2_reg_write(csi2, CSI2_CH_FRAME_SIZE(channel), 0); } - set_field(&ctrl, vc, VC_MASK); - set_field(&ctrl, dt, DT_MASK); + set_field(&ctrl, vc, CSI2_CH_CTRL_VC_MASK); + set_field(&ctrl, dt, CSI2_CH_CTRL_DT_MASK); csi2_reg_write(csi2, CSI2_CH_CTRL(channel), ctrl); csi2->num_lines[channel] = height; } void csi2_stop_channel(struct csi2_device *csi2, unsigned int channel) { - csi2_dbg("%s [%u]\n", __func__, channel); + csi2_dbg(csi2, "%s [%u]\n", __func__, channel); /* Channel disable. Use FORCE to allow stopping mid-frame. */ - csi2_reg_write(csi2, CSI2_CH_CTRL(channel), FORCE); + csi2_reg_write(csi2, CSI2_CH_CTRL(channel), CSI2_CH_CTRL_FORCE); /* Latch the above change by writing to the ADDR0 register. */ csi2_reg_write(csi2, CSI2_CH_ADDR0(channel), 0); /* Write this again, the HW needs it! */ @@ -451,8 +367,7 @@ void csi2_open_rx(struct csi2_device *csi2) dphy_start(&csi2->dphy); - csi2_reg_write(csi2, CSI2_CTRL, - csi2->multipacket_line ? 0 : EOP_IS_EOL); + csi2_reg_write(csi2, CSI2_CTRL, CSI2_CTRL_EOP_IS_EOL); } void csi2_close_rx(struct csi2_device *csi2) @@ -462,26 +377,28 @@ void csi2_close_rx(struct csi2_device *csi2) csi2_reg_write(csi2, CSI2_IRQ_MASK, 0); } -static int csi2_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int csi2_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { - struct v4l2_mbus_framefmt *fmt; + struct v4l2_subdev_route routes[] = { { + .sink_pad = CSI2_PAD_SINK, + .sink_stream = 0, + .source_pad = CSI2_PAD_FIRST_SOURCE, + .source_stream = 0, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + } }; + + struct v4l2_subdev_krouting routing = { + .num_routes = ARRAY_SIZE(routes), + .routes = routes, + }; - for (unsigned int i = 0; i < CSI2_NUM_CHANNELS; ++i) { - const struct v4l2_mbus_framefmt *def_fmt; - - /* CSI2_CH1_EMBEDDED */ - if (i == 1) - def_fmt = &cfe_default_meta_format; - else - def_fmt = &cfe_default_format; - - fmt = v4l2_subdev_get_pad_format(sd, state, i); - *fmt = *def_fmt; + int ret; - fmt = v4l2_subdev_get_pad_format(sd, state, i + CSI2_NUM_CHANNELS); - *fmt = *def_fmt; - } + ret = v4l2_subdev_set_routing_with_fmt(sd, state, &routing, + &cfe_default_format); + if (ret) + return ret; return 0; } @@ -490,21 +407,29 @@ static int csi2_pad_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_subdev_format *format) { - if (format->pad < CSI2_NUM_CHANNELS) { - /* - * Store the sink pad format and propagate it to the source pad. - */ + if (format->pad == CSI2_PAD_SINK) { + /* Store the sink format and propagate it to the source. */ + + const struct cfe_fmt *cfe_fmt; + + cfe_fmt = find_format_by_code(format->format.code); + if (!cfe_fmt) { + cfe_fmt = find_format_by_code(MEDIA_BUS_FMT_SRGGB10_1X10); + format->format.code = cfe_fmt->code; + } struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_pad_format(sd, state, format->pad); + fmt = v4l2_subdev_state_get_format(state, format->pad, + format->stream); if (!fmt) return -EINVAL; *fmt = format->format; - fmt = v4l2_subdev_get_pad_format(sd, state, - format->pad + CSI2_NUM_CHANNELS); + fmt = v4l2_subdev_state_get_opposite_stream_format(state, + format->pad, + format->stream); if (!fmt) return -EINVAL; @@ -512,20 +437,20 @@ static int csi2_pad_set_fmt(struct v4l2_subdev *sd, *fmt = format->format; } else { - /* - * Only allow changing the source pad mbus code. - */ + /* Only allow changing the source pad mbus code. */ struct v4l2_mbus_framefmt *sink_fmt, *source_fmt; u32 sink_code; u32 code; - sink_fmt = v4l2_subdev_get_pad_format(sd, state, - format->pad - CSI2_NUM_CHANNELS); + sink_fmt = v4l2_subdev_state_get_opposite_stream_format(state, + format->pad, + format->stream); if (!sink_fmt) return -EINVAL; - source_fmt = v4l2_subdev_get_pad_format(sd, state, format->pad); + source_fmt = v4l2_subdev_state_get_format(state, format->pad, + format->stream); if (!source_fmt) return -EINVAL; @@ -533,14 +458,14 @@ static int csi2_pad_set_fmt(struct v4l2_subdev *sd, code = format->format.code; /* - * If the source code from the user does not match the code in - * the sink pad, check that the source code matches either the - * 16-bit version or the compressed version of the sink code. + * Only allow changing the mbus code to: + * - The sink's mbus code + * - The 16-bit version of the sink's mbus code + * - The compressed version of the sink's mbus code */ - - if (code != sink_code && - (code == cfe_find_16bit_code(sink_code) || - code == cfe_find_compressed_code(sink_code))) + if (code == sink_code || + code == cfe_find_16bit_code(sink_code) || + code == cfe_find_compressed_code(sink_code)) source_fmt->code = code; format->format.code = source_fmt->code; @@ -549,39 +474,75 @@ static int csi2_pad_set_fmt(struct v4l2_subdev *sd, return 0; } +static int csi2_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + int ret; + + ret = v4l2_subdev_routing_validate(sd, routing, + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 | + V4L2_SUBDEV_ROUTING_NO_SOURCE_MULTIPLEXING); + if (ret) + return ret; + + /* Only stream ID 0 allowed on source pads */ + for (unsigned int i = 0; i < routing->num_routes; ++i) { + const struct v4l2_subdev_route *route = &routing->routes[i]; + + if (route->source_stream != 0) + return -EINVAL; + } + + ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing, + &cfe_default_format); + if (ret) + return ret; + + return 0; +} + static const struct v4l2_subdev_pad_ops csi2_subdev_pad_ops = { - .init_cfg = csi2_init_cfg, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = csi2_pad_set_fmt, + .set_routing = csi2_set_routing, .link_validate = v4l2_subdev_link_validate_default, }; static const struct media_entity_operations csi2_entity_ops = { .link_validate = v4l2_subdev_link_validate, + .has_pad_interdep = v4l2_subdev_has_pad_interdep, }; static const struct v4l2_subdev_ops csi2_subdev_ops = { .pad = &csi2_subdev_pad_ops, }; +static const struct v4l2_subdev_internal_ops csi2_internal_ops = { + .init_state = csi2_init_state, +}; + int csi2_init(struct csi2_device *csi2, struct dentry *debugfs) { - unsigned int i, ret; + unsigned int ret; spin_lock_init(&csi2->errors_lock); csi2->dphy.dev = csi2->v4l2_dev->dev; dphy_probe(&csi2->dphy); - debugfs_create_file("csi2_regs", 0444, debugfs, csi2, &csi2_regs_fops); + debugfs_create_file("csi2_regs", 0440, debugfs, csi2, &csi2_regs_fops); if (csi2_track_errors) - debugfs_create_file("csi2_errors", 0444, debugfs, csi2, + debugfs_create_file("csi2_errors", 0440, debugfs, csi2, &csi2_errors_fops); - for (i = 0; i < CSI2_NUM_CHANNELS * 2; i++) - csi2->pad[i].flags = i < CSI2_NUM_CHANNELS ? - MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; + csi2->pad[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + + for (unsigned int i = CSI2_PAD_FIRST_SOURCE; + i < CSI2_PAD_FIRST_SOURCE + CSI2_PAD_NUM_SOURCES; i++) + csi2->pad[i].flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&csi2->sd.entity, ARRAY_SIZE(csi2->pad), csi2->pad); @@ -590,9 +551,10 @@ int csi2_init(struct csi2_device *csi2, struct dentry *debugfs) /* Initialize subdev */ v4l2_subdev_init(&csi2->sd, &csi2_subdev_ops); + csi2->sd.internal_ops = &csi2_internal_ops; csi2->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; csi2->sd.entity.ops = &csi2_entity_ops; - csi2->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; + csi2->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; csi2->sd.owner = THIS_MODULE; snprintf(csi2->sd.name, sizeof(csi2->sd.name), "csi2"); @@ -602,7 +564,7 @@ int csi2_init(struct csi2_device *csi2, struct dentry *debugfs) ret = v4l2_device_register_subdev(csi2->v4l2_dev, &csi2->sd); if (ret) { - csi2_err("Failed register csi2 subdev (%d)\n", ret); + csi2_err(csi2, "Failed register csi2 subdev (%d)\n", ret); goto err_subdev_cleanup; } diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.h b/drivers/media/platform/raspberrypi/rp1-cfe/csi2.h similarity index 86% rename from drivers/media/platform/raspberrypi/rp1_cfe/csi2.h rename to drivers/media/platform/raspberrypi/rp1-cfe/csi2.h index 4fff16ee940788..a8ee5de565fbd6 100644 --- a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.h +++ b/drivers/media/platform/raspberrypi/rp1-cfe/csi2.h @@ -1,9 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * RP1 CSI-2 driver. - * Copyright (c) 2021 Raspberry Pi Ltd. + * RP1 CSI-2 Driver * + * Copyright (c) 2021-2024 Raspberry Pi Ltd. + * Copyright (c) 2023-2024 Ideas on Board Oy */ + #ifndef _RP1_CSI2_ #define _RP1_CSI2_ @@ -17,6 +19,11 @@ #define CSI2_NUM_CHANNELS 4 +#define CSI2_PAD_SINK 0 +#define CSI2_PAD_FIRST_SOURCE 1 +#define CSI2_PAD_NUM_SOURCES 4 +#define CSI2_NUM_PADS 5 + #define DISCARDS_TABLE_NUM_VCS 4 enum csi2_mode { @@ -32,13 +39,6 @@ enum csi2_compression_mode { CSI2_COMPRESSION_COMBINED = 3, }; -struct csi2_cfg { - u16 width; - u16 height; - u32 stride; - u32 buffer_size; -}; - enum discards_table_index { DISCARDS_TABLE_OVERFLOW = 0, DISCARDS_TABLE_LENGTH_LIMIT, @@ -57,10 +57,9 @@ struct csi2_device { enum v4l2_mbus_type bus_type; unsigned int bus_flags; - bool multipacket_line; unsigned int num_lines[CSI2_NUM_CHANNELS]; - struct media_pad pad[CSI2_NUM_CHANNELS * 2]; + struct media_pad pad[CSI2_NUM_PADS]; struct v4l2_subdev sd; /* lock for csi2 errors counters */ @@ -80,7 +79,7 @@ void csi2_set_compression(struct csi2_device *csi2, unsigned int channel, void csi2_start_channel(struct csi2_device *csi2, unsigned int channel, enum csi2_mode mode, bool auto_arm, bool pack_bytes, unsigned int width, - unsigned int height); + unsigned int height, u8 vc, u8 dt); void csi2_stop_channel(struct csi2_device *csi2, unsigned int channel); void csi2_open_rx(struct csi2_device *csi2); void csi2_close_rx(struct csi2_device *csi2); diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/dphy.c b/drivers/media/platform/raspberrypi/rp1-cfe/dphy.c similarity index 63% rename from drivers/media/platform/raspberrypi/rp1_cfe/dphy.c rename to drivers/media/platform/raspberrypi/rp1-cfe/dphy.c index 28ea3215fff5c1..b443f0f56ddc82 100644 --- a/drivers/media/platform/raspberrypi/rp1_cfe/dphy.c +++ b/drivers/media/platform/raspberrypi/rp1-cfe/dphy.c @@ -2,32 +2,30 @@ /* * RP1 CSI-2 Driver * - * Copyright (C) 2021 - Raspberry Pi Ltd. - * + * Copyright (c) 2021-2024 Raspberry Pi Ltd. + * Copyright (c) 2023-2024 Ideas on Board Oy */ #include -#include #include #include "dphy.h" -#define dphy_dbg(fmt, arg...) dev_dbg(dphy->dev, fmt, ##arg) -#define dphy_info(fmt, arg...) dev_info(dphy->dev, fmt, ##arg) -#define dphy_err(fmt, arg...) dev_err(dphy->dev, fmt, ##arg) +#define dphy_dbg(dphy, fmt, arg...) dev_dbg((dphy)->dev, fmt, ##arg) +#define dphy_err(dphy, fmt, arg...) dev_err((dphy)->dev, fmt, ##arg) /* DW dphy Host registers */ -#define VERSION 0x000 -#define N_LANES 0x004 -#define RESETN 0x008 -#define PHY_SHUTDOWNZ 0x040 -#define PHY_RSTZ 0x044 -#define PHY_RX 0x048 -#define PHY_STOPSTATE 0x04c -#define PHY_TST_CTRL0 0x050 -#define PHY_TST_CTRL1 0x054 -#define PHY2_TST_CTRL0 0x058 -#define PHY2_TST_CTRL1 0x05c +#define DPHY_VERSION 0x000 +#define DPHY_N_LANES 0x004 +#define DPHY_RESETN 0x008 +#define DPHY_PHY_SHUTDOWNZ 0x040 +#define DPHY_PHY_RSTZ 0x044 +#define DPHY_PHY_RX 0x048 +#define DPHY_PHY_STOPSTATE 0x04c +#define DPHY_PHY_TST_CTRL0 0x050 +#define DPHY_PHY_TST_CTRL1 0x054 +#define DPHY_PHY2_TST_CTRL0 0x058 +#define DPHY_PHY2_TST_CTRL1 0x05c /* DW dphy Host Transactions */ #define DPHY_HS_RX_CTRL_LANE0_OFFSET 0x44 @@ -47,38 +45,38 @@ static void dw_csi2_host_write(struct dphy_data *dphy, u32 offset, u32 data) static void set_tstclr(struct dphy_data *dphy, u32 val) { - u32 ctrl0 = dw_csi2_host_read(dphy, PHY_TST_CTRL0); + u32 ctrl0 = dw_csi2_host_read(dphy, DPHY_PHY_TST_CTRL0); - dw_csi2_host_write(dphy, PHY_TST_CTRL0, (ctrl0 & ~1) | val); + dw_csi2_host_write(dphy, DPHY_PHY_TST_CTRL0, (ctrl0 & ~1) | val); } static void set_tstclk(struct dphy_data *dphy, u32 val) { - u32 ctrl0 = dw_csi2_host_read(dphy, PHY_TST_CTRL0); + u32 ctrl0 = dw_csi2_host_read(dphy, DPHY_PHY_TST_CTRL0); - dw_csi2_host_write(dphy, PHY_TST_CTRL0, (ctrl0 & ~2) | (val << 1)); + dw_csi2_host_write(dphy, DPHY_PHY_TST_CTRL0, (ctrl0 & ~2) | (val << 1)); } static uint8_t get_tstdout(struct dphy_data *dphy) { - u32 ctrl1 = dw_csi2_host_read(dphy, PHY_TST_CTRL1); + u32 ctrl1 = dw_csi2_host_read(dphy, DPHY_PHY_TST_CTRL1); return ((ctrl1 >> 8) & 0xff); } static void set_testen(struct dphy_data *dphy, u32 val) { - u32 ctrl1 = dw_csi2_host_read(dphy, PHY_TST_CTRL1); + u32 ctrl1 = dw_csi2_host_read(dphy, DPHY_PHY_TST_CTRL1); - dw_csi2_host_write(dphy, PHY_TST_CTRL1, + dw_csi2_host_write(dphy, DPHY_PHY_TST_CTRL1, (ctrl1 & ~(1 << 16)) | (val << 16)); } static void set_testdin(struct dphy_data *dphy, u32 val) { - u32 ctrl1 = dw_csi2_host_read(dphy, PHY_TST_CTRL1); + u32 ctrl1 = dw_csi2_host_read(dphy, DPHY_PHY_TST_CTRL1); - dw_csi2_host_write(dphy, PHY_TST_CTRL1, (ctrl1 & ~0xff) | val); + dw_csi2_host_write(dphy, DPHY_PHY_TST_CTRL1, (ctrl1 & ~0xff) | val); } static uint8_t dphy_transaction(struct dphy_data *dphy, u8 test_code, @@ -117,7 +115,7 @@ static void dphy_set_hsfreqrange(struct dphy_data *dphy, uint32_t mbps) unsigned int i; if (mbps < 80 || mbps > 1500) - dphy_err("DPHY: Datarate %u Mbps out of range\n", mbps); + dphy_err(dphy, "DPHY: Datarate %u Mbps out of range\n", mbps); for (i = 0; i < ARRAY_SIZE(hsfreqrange_table) - 1; i++) { if (mbps <= hsfreqrange_table[i][0]) @@ -130,8 +128,8 @@ static void dphy_set_hsfreqrange(struct dphy_data *dphy, uint32_t mbps) static void dphy_init(struct dphy_data *dphy) { - dw_csi2_host_write(dphy, PHY_RSTZ, 0); - dw_csi2_host_write(dphy, PHY_SHUTDOWNZ, 0); + dw_csi2_host_write(dphy, DPHY_PHY_RSTZ, 0); + dw_csi2_host_write(dphy, DPHY_PHY_SHUTDOWNZ, 0); set_tstclk(dphy, 1); set_testen(dphy, 0); set_tstclr(dphy, 1); @@ -142,24 +140,29 @@ static void dphy_init(struct dphy_data *dphy) dphy_set_hsfreqrange(dphy, dphy->dphy_rate); usleep_range(5, 10); - dw_csi2_host_write(dphy, PHY_SHUTDOWNZ, 1); + dw_csi2_host_write(dphy, DPHY_PHY_SHUTDOWNZ, 1); usleep_range(5, 10); - dw_csi2_host_write(dphy, PHY_RSTZ, 1); + dw_csi2_host_write(dphy, DPHY_PHY_RSTZ, 1); } void dphy_start(struct dphy_data *dphy) { - dw_csi2_host_write(dphy, N_LANES, (dphy->active_lanes - 1)); + dphy_dbg(dphy, "%s: Link rate %u Mbps, %u data lanes\n", __func__, + dphy->dphy_rate, dphy->active_lanes); + + dw_csi2_host_write(dphy, DPHY_N_LANES, (dphy->active_lanes - 1)); dphy_init(dphy); - dw_csi2_host_write(dphy, RESETN, 0xffffffff); + dw_csi2_host_write(dphy, DPHY_RESETN, 0xffffffff); usleep_range(10, 50); } void dphy_stop(struct dphy_data *dphy) { + dphy_dbg(dphy, "%s\n", __func__); + /* Set only one lane (lane 0) as active (ON) */ - dw_csi2_host_write(dphy, N_LANES, 0); - dw_csi2_host_write(dphy, RESETN, 0); + dw_csi2_host_write(dphy, DPHY_N_LANES, 0); + dw_csi2_host_write(dphy, DPHY_RESETN, 0); } void dphy_probe(struct dphy_data *dphy) @@ -167,11 +170,12 @@ void dphy_probe(struct dphy_data *dphy) u32 host_ver; u8 host_ver_major, host_ver_minor; - host_ver = dw_csi2_host_read(dphy, VERSION); + host_ver = dw_csi2_host_read(dphy, DPHY_VERSION); host_ver_major = (u8)((host_ver >> 24) - '0'); host_ver_minor = (u8)((host_ver >> 16) - '0'); host_ver_minor = host_ver_minor * 10; host_ver_minor += (u8)((host_ver >> 8) - '0'); - dphy_info("DW dphy Host HW v%u.%u\n", host_ver_major, host_ver_minor); + dphy_dbg(dphy, "DW dphy Host HW v%u.%u\n", host_ver_major, + host_ver_minor); } diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/dphy.h b/drivers/media/platform/raspberrypi/rp1-cfe/dphy.h similarity index 80% rename from drivers/media/platform/raspberrypi/rp1_cfe/dphy.h rename to drivers/media/platform/raspberrypi/rp1-cfe/dphy.h index 9d7a80b3f68402..84fa370957cc3c 100644 --- a/drivers/media/platform/raspberrypi/rp1_cfe/dphy.h +++ b/drivers/media/platform/raspberrypi/rp1-cfe/dphy.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2021 Raspberry Pi Ltd. - * + * Copyright (c) 2021-2024 Raspberry Pi Ltd. + * Copyright (c) 2023-2024 Ideas on Board Oy */ #ifndef _RP1_DPHY_ diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c b/drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.c similarity index 65% rename from drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c rename to drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.c index 93dc4ec4e6891d..05762b1be2bc75 100644 --- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c +++ b/drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 /* - * PiSP Front End driver. - * Copyright (c) 2021 Raspberry Pi Ltd. + * PiSP Front End Driver * + * Copyright (c) 2021-2024 Raspberry Pi Ltd. */ #include @@ -13,8 +13,10 @@ #include -#include "pisp_fe.h" #include "cfe.h" +#include "pisp-fe.h" + +#include "cfe-trace.h" #define FE_VERSION 0x000 #define FE_CONTROL 0x004 @@ -65,63 +67,81 @@ struct pisp_fe_config_param { static const struct pisp_fe_config_param pisp_fe_config_map[] = { /* *_dirty_flag_extra types */ - { 0, PISP_FE_DIRTY_GLOBAL, offsetof(struct pisp_fe_config, global), - sizeof(struct pisp_fe_global_config) }, - { 0, PISP_FE_DIRTY_FLOATING, offsetof(struct pisp_fe_config, floating_stats), - sizeof(struct pisp_fe_floating_stats_config) }, - { 0, PISP_FE_DIRTY_OUTPUT_AXI, offsetof(struct pisp_fe_config, output_axi), - sizeof(struct pisp_fe_output_axi_config) }, + { 0, PISP_FE_DIRTY_GLOBAL, + offsetof(struct pisp_fe_config, global), + sizeof(struct pisp_fe_global_config) }, + { 0, PISP_FE_DIRTY_FLOATING, + offsetof(struct pisp_fe_config, floating_stats), + sizeof(struct pisp_fe_floating_stats_config) }, + { 0, PISP_FE_DIRTY_OUTPUT_AXI, + offsetof(struct pisp_fe_config, output_axi), + sizeof(struct pisp_fe_output_axi_config) }, /* *_dirty_flag types */ - { PISP_FE_ENABLE_INPUT, 0, offsetof(struct pisp_fe_config, input), - sizeof(struct pisp_fe_input_config) }, - { PISP_FE_ENABLE_DECOMPRESS, 0, offsetof(struct pisp_fe_config, decompress), - sizeof(struct pisp_decompress_config) }, - { PISP_FE_ENABLE_DECOMPAND, 0, offsetof(struct pisp_fe_config, decompand), - sizeof(struct pisp_fe_decompand_config) }, - { PISP_FE_ENABLE_BLA, 0, offsetof(struct pisp_fe_config, bla), - sizeof(struct pisp_bla_config) }, - { PISP_FE_ENABLE_DPC, 0, offsetof(struct pisp_fe_config, dpc), - sizeof(struct pisp_fe_dpc_config) }, - { PISP_FE_ENABLE_STATS_CROP, 0, offsetof(struct pisp_fe_config, stats_crop), - sizeof(struct pisp_fe_crop_config) }, - { PISP_FE_ENABLE_BLC, 0, offsetof(struct pisp_fe_config, blc), - sizeof(struct pisp_bla_config) }, - { PISP_FE_ENABLE_CDAF_STATS, 0, offsetof(struct pisp_fe_config, cdaf_stats), - sizeof(struct pisp_fe_cdaf_stats_config) }, - { PISP_FE_ENABLE_AWB_STATS, 0, offsetof(struct pisp_fe_config, awb_stats), - sizeof(struct pisp_fe_awb_stats_config) }, - { PISP_FE_ENABLE_RGBY, 0, offsetof(struct pisp_fe_config, rgby), - sizeof(struct pisp_fe_rgby_config) }, - { PISP_FE_ENABLE_LSC, 0, offsetof(struct pisp_fe_config, lsc), - sizeof(struct pisp_fe_lsc_config) }, - { PISP_FE_ENABLE_AGC_STATS, 0, offsetof(struct pisp_fe_config, agc_stats), - sizeof(struct pisp_agc_statistics) }, - { PISP_FE_ENABLE_CROP0, 0, offsetof(struct pisp_fe_config, ch[0].crop), - sizeof(struct pisp_fe_crop_config) }, - { PISP_FE_ENABLE_DOWNSCALE0, 0, offsetof(struct pisp_fe_config, ch[0].downscale), - sizeof(struct pisp_fe_downscale_config) }, - { PISP_FE_ENABLE_COMPRESS0, 0, offsetof(struct pisp_fe_config, ch[0].compress), - sizeof(struct pisp_compress_config) }, - { PISP_FE_ENABLE_OUTPUT0, 0, offsetof(struct pisp_fe_config, ch[0].output), - sizeof(struct pisp_fe_output_config) }, - { PISP_FE_ENABLE_CROP1, 0, offsetof(struct pisp_fe_config, ch[1].crop), - sizeof(struct pisp_fe_crop_config) }, - { PISP_FE_ENABLE_DOWNSCALE1, 0, offsetof(struct pisp_fe_config, ch[1].downscale), - sizeof(struct pisp_fe_downscale_config) }, - { PISP_FE_ENABLE_COMPRESS1, 0, offsetof(struct pisp_fe_config, ch[1].compress), - sizeof(struct pisp_compress_config) }, - { PISP_FE_ENABLE_OUTPUT1, 0, offsetof(struct pisp_fe_config, ch[1].output), - sizeof(struct pisp_fe_output_config) }, + { PISP_FE_ENABLE_INPUT, 0, + offsetof(struct pisp_fe_config, input), + sizeof(struct pisp_fe_input_config) }, + { PISP_FE_ENABLE_DECOMPRESS, 0, + offsetof(struct pisp_fe_config, decompress), + sizeof(struct pisp_decompress_config) }, + { PISP_FE_ENABLE_DECOMPAND, 0, + offsetof(struct pisp_fe_config, decompand), + sizeof(struct pisp_fe_decompand_config) }, + { PISP_FE_ENABLE_BLA, 0, + offsetof(struct pisp_fe_config, bla), + sizeof(struct pisp_bla_config) }, + { PISP_FE_ENABLE_DPC, 0, + offsetof(struct pisp_fe_config, dpc), + sizeof(struct pisp_fe_dpc_config) }, + { PISP_FE_ENABLE_STATS_CROP, 0, + offsetof(struct pisp_fe_config, stats_crop), + sizeof(struct pisp_fe_crop_config) }, + { PISP_FE_ENABLE_BLC, 0, + offsetof(struct pisp_fe_config, blc), + sizeof(struct pisp_bla_config) }, + { PISP_FE_ENABLE_CDAF_STATS, 0, + offsetof(struct pisp_fe_config, cdaf_stats), + sizeof(struct pisp_fe_cdaf_stats_config) }, + { PISP_FE_ENABLE_AWB_STATS, 0, + offsetof(struct pisp_fe_config, awb_stats), + sizeof(struct pisp_fe_awb_stats_config) }, + { PISP_FE_ENABLE_RGBY, 0, + offsetof(struct pisp_fe_config, rgby), + sizeof(struct pisp_fe_rgby_config) }, + { PISP_FE_ENABLE_LSC, 0, + offsetof(struct pisp_fe_config, lsc), + sizeof(struct pisp_fe_lsc_config) }, + { PISP_FE_ENABLE_AGC_STATS, 0, + offsetof(struct pisp_fe_config, agc_stats), + sizeof(struct pisp_agc_statistics) }, + { PISP_FE_ENABLE_CROP0, 0, + offsetof(struct pisp_fe_config, ch[0].crop), + sizeof(struct pisp_fe_crop_config) }, + { PISP_FE_ENABLE_DOWNSCALE0, 0, + offsetof(struct pisp_fe_config, ch[0].downscale), + sizeof(struct pisp_fe_downscale_config) }, + { PISP_FE_ENABLE_COMPRESS0, 0, + offsetof(struct pisp_fe_config, ch[0].compress), + sizeof(struct pisp_compress_config) }, + { PISP_FE_ENABLE_OUTPUT0, 0, + offsetof(struct pisp_fe_config, ch[0].output), + sizeof(struct pisp_fe_output_config) }, + { PISP_FE_ENABLE_CROP1, 0, + offsetof(struct pisp_fe_config, ch[1].crop), + sizeof(struct pisp_fe_crop_config) }, + { PISP_FE_ENABLE_DOWNSCALE1, 0, + offsetof(struct pisp_fe_config, ch[1].downscale), + sizeof(struct pisp_fe_downscale_config) }, + { PISP_FE_ENABLE_COMPRESS1, 0, + offsetof(struct pisp_fe_config, ch[1].compress), + sizeof(struct pisp_compress_config) }, + { PISP_FE_ENABLE_OUTPUT1, 0, + offsetof(struct pisp_fe_config, ch[1].output), + sizeof(struct pisp_fe_output_config) }, }; -#define pisp_fe_dbg_verbose(fmt, arg...) \ - do { \ - if (cfe_debug_verbose) \ - dev_dbg(fe->v4l2_dev->dev, fmt, ##arg); \ - } while (0) -#define pisp_fe_dbg(fmt, arg...) dev_dbg(fe->v4l2_dev->dev, fmt, ##arg) -#define pisp_fe_info(fmt, arg...) dev_info(fe->v4l2_dev->dev, fmt, ##arg) -#define pisp_fe_err(fmt, arg...) dev_err(fe->v4l2_dev->dev, fmt, ##arg) +#define pisp_fe_dbg(fe, fmt, arg...) dev_dbg((fe)->v4l2_dev->dev, fmt, ##arg) +#define pisp_fe_info(fe, fmt, arg...) dev_info((fe)->v4l2_dev->dev, fmt, ##arg) +#define pisp_fe_err(fe, fmt, arg...) dev_err((fe)->v4l2_dev->dev, fmt, ##arg) static inline u32 pisp_fe_reg_read(struct pisp_fe_device *fe, u32 offset) { @@ -132,17 +152,15 @@ static inline void pisp_fe_reg_write(struct pisp_fe_device *fe, u32 offset, u32 val) { writel(val, fe->base + offset); - pisp_fe_dbg_verbose("fe: write 0x%04x -> 0x%03x\n", val, offset); } -static inline void pisp_fe_reg_write_relaxed(struct pisp_fe_device *fe, u32 offset, - u32 val) +static inline void pisp_fe_reg_write_relaxed(struct pisp_fe_device *fe, + u32 offset, u32 val) { writel_relaxed(val, fe->base + offset); - pisp_fe_dbg_verbose("fe: write 0x%04x -> 0x%03x\n", val, offset); } -static int pisp_regs_show(struct seq_file *s, void *data) +static int pisp_fe_regs_show(struct seq_file *s, void *data) { struct pisp_fe_device *fe = s->private; int ret; @@ -169,23 +187,22 @@ static int pisp_regs_show(struct seq_file *s, void *data) return 0; } -DEFINE_SHOW_ATTRIBUTE(pisp_regs); +DEFINE_SHOW_ATTRIBUTE(pisp_fe_regs); -static void pisp_config_write(struct pisp_fe_device *fe, - struct pisp_fe_config *config, - unsigned int start_offset, - unsigned int size) +static void pisp_fe_config_write(struct pisp_fe_device *fe, + struct pisp_fe_config *config, + unsigned int start_offset, unsigned int size) { const unsigned int max_offset = offsetof(struct pisp_fe_config, ch[PISP_FE_NUM_OUTPUTS]); - unsigned int i, end_offset; + unsigned int end_offset; u32 *cfg = (u32 *)config; start_offset = min(start_offset, max_offset); end_offset = min(start_offset + size, max_offset); cfg += start_offset >> 2; - for (i = start_offset; i < end_offset; i += 4, cfg++) + for (unsigned int i = start_offset; i < end_offset; i += 4, cfg++) pisp_fe_reg_write_relaxed(fe, PISP_FE_CONFIG_BASE_OFFSET + i, *cfg); } @@ -193,7 +210,6 @@ static void pisp_config_write(struct pisp_fe_device *fe, void pisp_fe_isr(struct pisp_fe_device *fe, bool *sof, bool *eof) { u32 status, int_status, out_status, frame_status, error_status; - unsigned int i; pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_LATCH_REGS); status = pisp_fe_reg_read(fe, FE_STATUS); @@ -204,12 +220,11 @@ void pisp_fe_isr(struct pisp_fe_device *fe, bool *sof, bool *eof) int_status = pisp_fe_reg_read(fe, FE_INT_STATUS); pisp_fe_reg_write(fe, FE_INT_STATUS, int_status); - pisp_fe_dbg_verbose("%s: status 0x%x out 0x%x frame 0x%x error 0x%x int 0x%x\n", - __func__, status, out_status, frame_status, error_status, - int_status); + trace_fe_irq(status, out_status, frame_status, error_status, + int_status); /* We do not report interrupts for the input/stream pad. */ - for (i = 0; i < FE_NUM_PADS - 1; i++) { + for (unsigned int i = 0; i < FE_NUM_PADS - 1; i++) { sof[i] = !!(int_status & FE_INT_SOF); eof[i] = !!(int_status & FE_INT_EOF); } @@ -236,8 +251,7 @@ static bool pisp_fe_validate_output(struct pisp_fe_config const *cfg, if ((cfg->global.enables & PISP_FE_ENABLE_CROP(c)) && ((cfg->ch[c].crop.offset_x >= (cfg->input.format.width & ~1) || cfg->ch[c].crop.offset_y >= cfg->input.format.height || - cfg->ch[c].crop.width < 2 || - cfg->ch[c].crop.height < 2))) + cfg->ch[c].crop.width < 2 || cfg->ch[c].crop.height < 2))) return false; if ((cfg->global.enables & PISP_FE_ENABLE_DOWNSCALE(c)) && @@ -254,8 +268,7 @@ static bool pisp_fe_validate_stats(struct pisp_fe_config const *cfg) return (!(cfg->global.enables & PISP_FE_ENABLE_STATS_CROP) || (cfg->stats_crop.offset_x < (cfg->input.format.width & ~1) && cfg->stats_crop.offset_y < cfg->input.format.height && - cfg->stats_crop.width >= 2 && - cfg->stats_crop.height >= 2)); + cfg->stats_crop.width >= 2 && cfg->stats_crop.height >= 2)); } int pisp_fe_validate_config(struct pisp_fe_device *fe, @@ -263,8 +276,6 @@ int pisp_fe_validate_config(struct pisp_fe_device *fe, struct v4l2_format const *f0, struct v4l2_format const *f1) { - unsigned int i; - /* * Check the input is enabled, streaming and has nonzero size; * to avoid cases where the hardware might lock up or try to @@ -273,15 +284,15 @@ int pisp_fe_validate_config(struct pisp_fe_device *fe, if (!(cfg->global.enables & PISP_FE_ENABLE_INPUT) || cfg->input.streaming != 1 || cfg->input.format.width < 2 || cfg->input.format.height < 2) { - pisp_fe_err("%s: Input config not valid", __func__); + pisp_fe_err(fe, "%s: Input config not valid", __func__); return -EINVAL; } - for (i = 0; i < PISP_FE_NUM_OUTPUTS; i++) { + for (unsigned int i = 0; i < PISP_FE_NUM_OUTPUTS; i++) { if (!(cfg->global.enables & PISP_FE_ENABLE_OUTPUT(i))) { if (cfg->global.enables & PISP_FE_ENABLE_OUTPUT_CLUSTER(i)) { - pisp_fe_err("%s: Output %u not valid", + pisp_fe_err(fe, "%s: Output %u not valid", __func__, i); return -EINVAL; } @@ -294,7 +305,7 @@ int pisp_fe_validate_config(struct pisp_fe_device *fe, if ((cfg->global.enables & PISP_FE_ENABLE_STATS_CLUSTER) && !pisp_fe_validate_stats(cfg)) { - pisp_fe_err("%s: Stats config not valid", __func__); + pisp_fe_err(fe, "%s: Stats config not valid", __func__); return -EINVAL; } @@ -304,7 +315,6 @@ int pisp_fe_validate_config(struct pisp_fe_device *fe, void pisp_fe_submit_job(struct pisp_fe_device *fe, struct vb2_buffer **vb2_bufs, struct pisp_fe_config *cfg) { - unsigned int i; u64 addr; u32 status; @@ -312,7 +322,7 @@ void pisp_fe_submit_job(struct pisp_fe_device *fe, struct vb2_buffer **vb2_bufs, * Check output buffers exist and outputs are correctly configured. * If valid, set the buffer's DMA address; otherwise disable. */ - for (i = 0; i < PISP_FE_NUM_OUTPUTS; i++) { + for (unsigned int i = 0; i < PISP_FE_NUM_OUTPUTS; i++) { struct vb2_buffer *buf = vb2_bufs[FE_OUTPUT0_PAD + i]; if (!(cfg->global.enables & PISP_FE_ENABLE_OUTPUT(i))) @@ -341,7 +351,6 @@ void pisp_fe_submit_job(struct pisp_fe_device *fe, struct vb2_buffer **vb2_bufs, * sequence of relaxed writes which follow. */ status = pisp_fe_reg_read(fe, FE_STATUS); - pisp_fe_dbg_verbose("%s: status = 0x%x\n", __func__, status); if (WARN_ON(status & FE_STATUS_QUEUED)) return; @@ -351,20 +360,20 @@ void pisp_fe_submit_job(struct pisp_fe_device *fe, struct vb2_buffer **vb2_bufs, * Selectively write other parameters that have been marked as * changed through the dirty flags. */ - pisp_config_write(fe, cfg, 0, - offsetof(struct pisp_fe_config, decompress)); + pisp_fe_config_write(fe, cfg, 0, + offsetof(struct pisp_fe_config, decompress)); cfg->dirty_flags_extra &= ~PISP_FE_DIRTY_GLOBAL; cfg->dirty_flags &= ~PISP_FE_ENABLE_INPUT; cfg->dirty_flags |= (cfg->global.enables & (PISP_FE_ENABLE_STATS_CROP | PISP_FE_ENABLE_OUTPUT_CLUSTER(0) | PISP_FE_ENABLE_OUTPUT_CLUSTER(1))); - for (i = 0; i < ARRAY_SIZE(pisp_fe_config_map); i++) { + for (unsigned int i = 0; i < ARRAY_SIZE(pisp_fe_config_map); i++) { const struct pisp_fe_config_param *p = &pisp_fe_config_map[i]; if (cfg->dirty_flags & p->dirty_flags || cfg->dirty_flags_extra & p->dirty_flags_extra) - pisp_config_write(fe, cfg, p->offset, p->size); + pisp_fe_config_write(fe, cfg, p->offset, p->size); } /* This final non-relaxed write serves as a memory barrier */ @@ -375,7 +384,8 @@ void pisp_fe_start(struct pisp_fe_device *fe) { pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_RESET); pisp_fe_reg_write(fe, FE_INT_STATUS, ~0); - pisp_fe_reg_write(fe, FE_INT_EN, FE_INT_EOF | FE_INT_SOF | FE_INT_LINES0 | FE_INT_LINES1); + pisp_fe_reg_write(fe, FE_INT_EN, FE_INT_EOF | FE_INT_SOF | + FE_INT_LINES0 | FE_INT_LINES1); fe->inframe_count = 0; } @@ -388,30 +398,32 @@ void pisp_fe_stop(struct pisp_fe_device *fe) pisp_fe_reg_write(fe, FE_INT_STATUS, ~0); } -static int pisp_fe_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int pisp_fe_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_pad_format(sd, state, FE_STREAM_PAD); + fmt = v4l2_subdev_state_get_format(state, FE_STREAM_PAD); *fmt = cfe_default_format; fmt->code = MEDIA_BUS_FMT_SRGGB16_1X16; - fmt = v4l2_subdev_get_pad_format(sd, state, FE_CONFIG_PAD); - *fmt = cfe_default_meta_format; + fmt = v4l2_subdev_state_get_format(state, FE_CONFIG_PAD); fmt->code = MEDIA_BUS_FMT_FIXED; + fmt->width = sizeof(struct pisp_fe_config); + fmt->height = 1; - fmt = v4l2_subdev_get_pad_format(sd, state, FE_OUTPUT0_PAD); + fmt = v4l2_subdev_state_get_format(state, FE_OUTPUT0_PAD); *fmt = cfe_default_format; fmt->code = MEDIA_BUS_FMT_SRGGB16_1X16; - fmt = v4l2_subdev_get_pad_format(sd, state, FE_OUTPUT1_PAD); + fmt = v4l2_subdev_state_get_format(state, FE_OUTPUT1_PAD); *fmt = cfe_default_format; fmt->code = MEDIA_BUS_FMT_SRGGB16_1X16; - fmt = v4l2_subdev_get_pad_format(sd, state, FE_STATS_PAD); - *fmt = cfe_default_meta_format; + fmt = v4l2_subdev_state_get_format(state, FE_STATS_PAD); fmt->code = MEDIA_BUS_FMT_FIXED; + fmt->width = sizeof(struct pisp_statistics); + fmt->height = 1; return 0; } @@ -435,13 +447,13 @@ static int pisp_fe_pad_set_fmt(struct v4l2_subdev *sd, format->format.code = cfe_fmt->code; format->format.field = V4L2_FIELD_NONE; - fmt = v4l2_subdev_get_pad_format(sd, state, FE_STREAM_PAD); + fmt = v4l2_subdev_state_get_format(state, FE_STREAM_PAD); *fmt = format->format; - fmt = v4l2_subdev_get_pad_format(sd, state, FE_OUTPUT0_PAD); + fmt = v4l2_subdev_state_get_format(state, FE_OUTPUT0_PAD); *fmt = format->format; - fmt = v4l2_subdev_get_pad_format(sd, state, FE_OUTPUT1_PAD); + fmt = v4l2_subdev_state_get_format(state, FE_OUTPUT1_PAD); *fmt = format->format; return 0; @@ -456,11 +468,17 @@ static int pisp_fe_pad_set_fmt(struct v4l2_subdev *sd, u32 sink_code; u32 code; - sink_fmt = v4l2_subdev_get_pad_format(sd, state, FE_STREAM_PAD); + cfe_fmt = find_format_by_code(format->format.code); + if (!cfe_fmt || !(cfe_fmt->flags & CFE_FORMAT_FLAG_FE_OUT)) + cfe_fmt = find_format_by_code(MEDIA_BUS_FMT_SRGGB16_1X16); + + format->format.code = cfe_fmt->code; + + sink_fmt = v4l2_subdev_state_get_format(state, FE_STREAM_PAD); if (!sink_fmt) return -EINVAL; - source_fmt = v4l2_subdev_get_pad_format(sd, state, format->pad); + source_fmt = v4l2_subdev_state_get_format(state, format->pad); if (!source_fmt) return -EINVAL; @@ -488,28 +506,49 @@ static int pisp_fe_pad_set_fmt(struct v4l2_subdev *sd, } static const struct v4l2_subdev_pad_ops pisp_fe_subdev_pad_ops = { - .init_cfg = pisp_fe_init_cfg, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = pisp_fe_pad_set_fmt, .link_validate = v4l2_subdev_link_validate_default, }; +static int pisp_fe_link_validate(struct media_link *link) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(link->sink->entity); + struct pisp_fe_device *fe = container_of(sd, struct pisp_fe_device, sd); + + pisp_fe_dbg(fe, "%s: link \"%s\":%u -> \"%s\":%u\n", __func__, + link->source->entity->name, link->source->index, + link->sink->entity->name, link->sink->index); + + if (link->sink->index == FE_STREAM_PAD) + return v4l2_subdev_link_validate(link); + + if (link->sink->index == FE_CONFIG_PAD) + return 0; + + return -EINVAL; +} + static const struct media_entity_operations pisp_fe_entity_ops = { - .link_validate = v4l2_subdev_link_validate, + .link_validate = pisp_fe_link_validate, }; static const struct v4l2_subdev_ops pisp_fe_subdev_ops = { .pad = &pisp_fe_subdev_pad_ops, }; +static const struct v4l2_subdev_internal_ops pisp_fe_internal_ops = { + .init_state = pisp_fe_init_state, +}; + int pisp_fe_init(struct pisp_fe_device *fe, struct dentry *debugfs) { int ret; - debugfs_create_file("pisp_regs", 0444, debugfs, fe, &pisp_regs_fops); + debugfs_create_file("fe_regs", 0440, debugfs, fe, &pisp_fe_regs_fops); fe->hw_revision = pisp_fe_reg_read(fe, FE_VERSION); - pisp_fe_info("PiSP FE HW v%u.%u\n", + pisp_fe_info(fe, "PiSP FE HW v%u.%u\n", (fe->hw_revision >> 24) & 0xff, (fe->hw_revision >> 20) & 0x0f); @@ -527,6 +566,7 @@ int pisp_fe_init(struct pisp_fe_device *fe, struct dentry *debugfs) /* Initialize subdev */ v4l2_subdev_init(&fe->sd, &pisp_fe_subdev_ops); + fe->sd.internal_ops = &pisp_fe_internal_ops; fe->sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER; fe->sd.entity.ops = &pisp_fe_entity_ops; fe->sd.entity.name = "pisp-fe"; @@ -540,7 +580,7 @@ int pisp_fe_init(struct pisp_fe_device *fe, struct dentry *debugfs) ret = v4l2_device_register_subdev(fe->v4l2_dev, &fe->sd); if (ret) { - pisp_fe_err("Failed register pisp fe subdev (%d)\n", ret); + pisp_fe_err(fe, "Failed register pisp fe subdev (%d)\n", ret); goto err_subdev_cleanup; } diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.h b/drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.h similarity index 90% rename from drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.h rename to drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.h index 12760cb55af422..54d506e19cf214 100644 --- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.h +++ b/drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.h @@ -1,8 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * PiSP Front End driver. - * Copyright (c) 2021 Raspberry Pi Ltd. + * PiSP Front End Driver * + * Copyright (c) 2021-2024 Raspberry Pi Ltd. */ #ifndef _PISP_FE_H_ #define _PISP_FE_H_ @@ -16,7 +16,7 @@ #include #include -#include "pisp_fe_config.h" +#include enum pisp_fe_pads { FE_STREAM_PAD, diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_common.h b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_common.h deleted file mode 100644 index e91aa2ed365919..00000000000000 --- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_common.h +++ /dev/null @@ -1,69 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ -/* - * RP1 PiSP common definitions. - * - * Copyright (C) 2021 - Raspberry Pi Ltd. - * - */ -#ifndef _PISP_COMMON_H_ -#define _PISP_COMMON_H_ - -#include "pisp_types.h" - -struct pisp_bla_config { - u16 black_level_r; - u16 black_level_gr; - u16 black_level_gb; - u16 black_level_b; - u16 output_black_level; - u8 pad[2]; -}; - -struct pisp_wbg_config { - u16 gain_r; - u16 gain_g; - u16 gain_b; - u8 pad[2]; -}; - -struct pisp_compress_config { - /* value subtracted from incoming data */ - u16 offset; - u8 pad; - /* 1 => Companding; 2 => Delta (recommended); 3 => Combined (for HDR) */ - u8 mode; -}; - -struct pisp_decompress_config { - /* value added to reconstructed data */ - u16 offset; - u8 pad; - /* 1 => Companding; 2 => Delta (recommended); 3 => Combined (for HDR) */ - u8 mode; -}; - -enum pisp_axi_flags { - /* - * round down bursts to end at a 32-byte boundary, to align following - * bursts - */ - PISP_AXI_FLAG_ALIGN = 128, - /* for FE writer: force WSTRB high, to pad output to 16-byte boundary */ - PISP_AXI_FLAG_PAD = 64, - /* for FE writer: Use Output FIFO level to trigger "panic" */ - PISP_AXI_FLAG_PANIC = 32, -}; - -struct pisp_axi_config { - /* - * burst length minus one, which must be in the range 0:15; OR'd with - * flags - */ - u8 maxlen_flags; - /* { prot[2:0], cache[3:0] } fields, echoed on AXI bus */ - u8 cache_prot; - /* QoS field(s) (4x4 bits for FE writer; 4 bits for other masters) */ - u16 qos; -}; - -#endif /* _PISP_COMMON_H_ */ diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_types.h b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_types.h deleted file mode 100644 index 93d2d3c27a5626..00000000000000 --- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_types.h +++ /dev/null @@ -1,144 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ -/* - * RP1 PiSP Front End image definitions. - * - * Copyright (C) 2021 - Raspberry Pi Ltd. - * - */ -#ifndef _PISP_FE_TYPES_H_ -#define _PISP_FE_TYPES_H_ - -/* This definition must match the format description in the hardware exactly! */ -struct pisp_image_format_config { - /* size in pixels */ - u16 width, height; - /* must match struct pisp_image_format below */ - u32 format; - s32 stride; - /* some planar image formats will need a second stride */ - s32 stride2; -}; - -static_assert(sizeof(struct pisp_image_format_config) == 16); - -enum pisp_bayer_order { - /* - * Note how bayer_order&1 tells you if G is on the even pixels of the - * checkerboard or not, and bayer_order&2 tells you if R is on the even - * rows or is swapped with B. Note that if the top (of the 8) bits is - * set, this denotes a monochrome or greyscale image, and the lower bits - * should all be ignored. - */ - PISP_BAYER_ORDER_RGGB = 0, - PISP_BAYER_ORDER_GBRG = 1, - PISP_BAYER_ORDER_BGGR = 2, - PISP_BAYER_ORDER_GRBG = 3, - PISP_BAYER_ORDER_GREYSCALE = 128 -}; - -enum pisp_image_format { - /* - * Precise values are mostly tbd. Generally these will be portmanteau - * values comprising bit fields and flags. This format must be shared - * throughout the PiSP. - */ - PISP_IMAGE_FORMAT_BPS_8 = 0x00000000, - PISP_IMAGE_FORMAT_BPS_10 = 0x00000001, - PISP_IMAGE_FORMAT_BPS_12 = 0x00000002, - PISP_IMAGE_FORMAT_BPS_16 = 0x00000003, - PISP_IMAGE_FORMAT_BPS_MASK = 0x00000003, - - PISP_IMAGE_FORMAT_PLANARITY_INTERLEAVED = 0x00000000, - PISP_IMAGE_FORMAT_PLANARITY_SEMI_PLANAR = 0x00000010, - PISP_IMAGE_FORMAT_PLANARITY_PLANAR = 0x00000020, - PISP_IMAGE_FORMAT_PLANARITY_MASK = 0x00000030, - - PISP_IMAGE_FORMAT_SAMPLING_444 = 0x00000000, - PISP_IMAGE_FORMAT_SAMPLING_422 = 0x00000100, - PISP_IMAGE_FORMAT_SAMPLING_420 = 0x00000200, - PISP_IMAGE_FORMAT_SAMPLING_MASK = 0x00000300, - - PISP_IMAGE_FORMAT_ORDER_NORMAL = 0x00000000, - PISP_IMAGE_FORMAT_ORDER_SWAPPED = 0x00001000, - - PISP_IMAGE_FORMAT_SHIFT_0 = 0x00000000, - PISP_IMAGE_FORMAT_SHIFT_1 = 0x00010000, - PISP_IMAGE_FORMAT_SHIFT_2 = 0x00020000, - PISP_IMAGE_FORMAT_SHIFT_3 = 0x00030000, - PISP_IMAGE_FORMAT_SHIFT_4 = 0x00040000, - PISP_IMAGE_FORMAT_SHIFT_5 = 0x00050000, - PISP_IMAGE_FORMAT_SHIFT_6 = 0x00060000, - PISP_IMAGE_FORMAT_SHIFT_7 = 0x00070000, - PISP_IMAGE_FORMAT_SHIFT_8 = 0x00080000, - PISP_IMAGE_FORMAT_SHIFT_MASK = 0x000f0000, - - PISP_IMAGE_FORMAT_UNCOMPRESSED = 0x00000000, - PISP_IMAGE_FORMAT_COMPRESSION_MODE_1 = 0x01000000, - PISP_IMAGE_FORMAT_COMPRESSION_MODE_2 = 0x02000000, - PISP_IMAGE_FORMAT_COMPRESSION_MODE_3 = 0x03000000, - PISP_IMAGE_FORMAT_COMPRESSION_MASK = 0x03000000, - - PISP_IMAGE_FORMAT_HOG_SIGNED = 0x04000000, - PISP_IMAGE_FORMAT_HOG_UNSIGNED = 0x08000000, - PISP_IMAGE_FORMAT_INTEGRAL_IMAGE = 0x10000000, - PISP_IMAGE_FORMAT_WALLPAPER_ROLL = 0x20000000, - PISP_IMAGE_FORMAT_THREE_CHANNEL = 0x40000000, - - /* Lastly a few specific instantiations of the above. */ - PISP_IMAGE_FORMAT_SINGLE_16 = PISP_IMAGE_FORMAT_BPS_16, - PISP_IMAGE_FORMAT_THREE_16 = - PISP_IMAGE_FORMAT_BPS_16 | PISP_IMAGE_FORMAT_THREE_CHANNEL -}; - -#define PISP_IMAGE_FORMAT_bps_8(fmt) \ - (((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) == PISP_IMAGE_FORMAT_BPS_8) -#define PISP_IMAGE_FORMAT_bps_10(fmt) \ - (((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) == PISP_IMAGE_FORMAT_BPS_10) -#define PISP_IMAGE_FORMAT_bps_12(fmt) \ - (((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) == PISP_IMAGE_FORMAT_BPS_12) -#define PISP_IMAGE_FORMAT_bps_16(fmt) \ - (((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) == PISP_IMAGE_FORMAT_BPS_16) -#define PISP_IMAGE_FORMAT_bps(fmt) \ - (((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) ? \ - 8 + (2 << (((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) - 1)) : \ - 8) -#define PISP_IMAGE_FORMAT_shift(fmt) \ - (((fmt) & PISP_IMAGE_FORMAT_SHIFT_MASK) / PISP_IMAGE_FORMAT_SHIFT_1) -#define PISP_IMAGE_FORMAT_three_channel(fmt) \ - ((fmt) & PISP_IMAGE_FORMAT_THREE_CHANNEL) -#define PISP_IMAGE_FORMAT_single_channel(fmt) \ - (!((fmt) & PISP_IMAGE_FORMAT_THREE_CHANNEL)) -#define PISP_IMAGE_FORMAT_compressed(fmt) \ - (((fmt) & PISP_IMAGE_FORMAT_COMPRESSION_MASK) != \ - PISP_IMAGE_FORMAT_UNCOMPRESSED) -#define PISP_IMAGE_FORMAT_sampling_444(fmt) \ - (((fmt) & PISP_IMAGE_FORMAT_SAMPLING_MASK) == \ - PISP_IMAGE_FORMAT_SAMPLING_444) -#define PISP_IMAGE_FORMAT_sampling_422(fmt) \ - (((fmt) & PISP_IMAGE_FORMAT_SAMPLING_MASK) == \ - PISP_IMAGE_FORMAT_SAMPLING_422) -#define PISP_IMAGE_FORMAT_sampling_420(fmt) \ - (((fmt) & PISP_IMAGE_FORMAT_SAMPLING_MASK) == \ - PISP_IMAGE_FORMAT_SAMPLING_420) -#define PISP_IMAGE_FORMAT_order_normal(fmt) \ - (!((fmt) & PISP_IMAGE_FORMAT_ORDER_SWAPPED)) -#define PISP_IMAGE_FORMAT_order_swapped(fmt) \ - ((fmt) & PISP_IMAGE_FORMAT_ORDER_SWAPPED) -#define PISP_IMAGE_FORMAT_interleaved(fmt) \ - (((fmt) & PISP_IMAGE_FORMAT_PLANARITY_MASK) == \ - PISP_IMAGE_FORMAT_PLANARITY_INTERLEAVED) -#define PISP_IMAGE_FORMAT_semiplanar(fmt) \ - (((fmt) & PISP_IMAGE_FORMAT_PLANARITY_MASK) == \ - PISP_IMAGE_FORMAT_PLANARITY_SEMI_PLANAR) -#define PISP_IMAGE_FORMAT_planar(fmt) \ - (((fmt) & PISP_IMAGE_FORMAT_PLANARITY_MASK) == \ - PISP_IMAGE_FORMAT_PLANARITY_PLANAR) -#define PISP_IMAGE_FORMAT_wallpaper(fmt) \ - ((fmt) & PISP_IMAGE_FORMAT_WALLPAPER_ROLL) -#define PISP_IMAGE_FORMAT_HOG(fmt) \ - ((fmt) & \ - (PISP_IMAGE_FORMAT_HOG_SIGNED | PISP_IMAGE_FORMAT_HOG_UNSIGNED)) - -#define PISP_WALLPAPER_WIDTH 128 // in bytes - -#endif /* _PISP_FE_TYPES_H_ */ diff --git a/drivers/media/platform/renesas/rcar-isp.c b/drivers/media/platform/renesas/rcar-isp.c index 7360cf3863f2c8..aa974f7b5c0479 100644 --- a/drivers/media/platform/renesas/rcar-isp.c +++ b/drivers/media/platform/renesas/rcar-isp.c @@ -282,7 +282,7 @@ static int risp_set_pad_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { isp->mf = format->format; } else { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, 0); + framefmt = v4l2_subdev_state_get_format(sd_state, 0); *framefmt = format->format; } @@ -302,7 +302,7 @@ static int risp_get_pad_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) format->format = isp->mf; else - format->format = *v4l2_subdev_get_try_format(sd, sd_state, 0); + format->format = *v4l2_subdev_state_get_format(sd_state, 0); mutex_unlock(&isp->lock); diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-csi2.c b/drivers/media/platform/renesas/rcar-vin/rcar-csi2.c index 109cca91f733a0..eaf26f29be245c 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-csi2.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-csi2.c @@ -1185,7 +1185,7 @@ static int rcsi2_set_pad_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { priv->mf = format->format; } else { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, 0); + framefmt = v4l2_subdev_state_get_format(sd_state, 0); *framefmt = format->format; } @@ -1205,7 +1205,7 @@ static int rcsi2_get_pad_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) format->format = priv->mf; else - format->format = *v4l2_subdev_get_try_format(sd, sd_state, 0); + format->format = *v4l2_subdev_state_get_format(sd_state, 0); mutex_unlock(&priv->lock); diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c index 246eec259c5d7c..93746e939b8499 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c @@ -250,7 +250,7 @@ static int rzg2l_csi2_calc_mbps(struct rzg2l_csi2 *csi2) } state = v4l2_subdev_lock_and_get_active_state(&csi2->subdev); - fmt = v4l2_subdev_get_pad_format(&csi2->subdev, state, RZG2L_CSI2_SINK); + fmt = v4l2_subdev_state_get_format(state, RZG2L_CSI2_SINK); format = rzg2l_csi2_code_to_fmt(fmt->code); v4l2_subdev_unlock_state(state); @@ -500,13 +500,13 @@ static int rzg2l_csi2_set_format(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *src_format; struct v4l2_mbus_framefmt *sink_format; - src_format = v4l2_subdev_get_pad_format(sd, state, RZG2L_CSI2_SOURCE); + src_format = v4l2_subdev_state_get_format(state, RZG2L_CSI2_SOURCE); if (fmt->pad == RZG2L_CSI2_SOURCE) { fmt->format = *src_format; return 0; } - sink_format = v4l2_subdev_get_pad_format(sd, state, RZG2L_CSI2_SINK); + sink_format = v4l2_subdev_state_get_format(state, RZG2L_CSI2_SINK); if (!rzg2l_csi2_code_to_fmt(fmt->format.code)) sink_format->code = rzg2l_csi2_formats[0].code; @@ -530,8 +530,8 @@ static int rzg2l_csi2_set_format(struct v4l2_subdev *sd, return 0; } -static int rzg2l_csi2_init_config(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int rzg2l_csi2_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { .pad = RZG2L_CSI2_SINK, }; @@ -582,7 +582,6 @@ static const struct v4l2_subdev_video_ops rzg2l_csi2_video_ops = { static const struct v4l2_subdev_pad_ops rzg2l_csi2_pad_ops = { .enum_mbus_code = rzg2l_csi2_enum_mbus_code, - .init_cfg = rzg2l_csi2_init_config, .enum_frame_size = rzg2l_csi2_enum_frame_size, .set_fmt = rzg2l_csi2_set_format, .get_fmt = v4l2_subdev_get_fmt, @@ -593,6 +592,10 @@ static const struct v4l2_subdev_ops rzg2l_csi2_subdev_ops = { .pad = &rzg2l_csi2_pad_ops, }; +static const struct v4l2_subdev_internal_ops rzg2l_csi2_internal_ops = { + .init_state = rzg2l_csi2_init_state, +}; + /* ----------------------------------------------------------------------------- * Async handling and registration of subdevices and links. */ @@ -777,6 +780,7 @@ static int rzg2l_csi2_probe(struct platform_device *pdev) csi2->subdev.dev = &pdev->dev; v4l2_subdev_init(&csi2->subdev, &rzg2l_csi2_subdev_ops); + csi2->subdev.internal_ops = &rzg2l_csi2_internal_ops; v4l2_set_subdevdata(&csi2->subdev, &pdev->dev); snprintf(csi2->subdev.name, sizeof(csi2->subdev.name), "csi-%s", dev_name(&pdev->dev)); diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c index 4dcd2faff5bbca..9f351a05893e6c 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c @@ -39,7 +39,7 @@ struct v4l2_mbus_framefmt *rzg2l_cru_ip_get_src_fmt(struct rzg2l_cru_dev *cru) struct v4l2_mbus_framefmt *fmt; state = v4l2_subdev_lock_and_get_active_state(&cru->ip.subdev); - fmt = v4l2_subdev_get_pad_format(&cru->ip.subdev, state, 1); + fmt = v4l2_subdev_state_get_format(state, 1); v4l2_subdev_unlock_state(state); return fmt; @@ -108,13 +108,13 @@ static int rzg2l_cru_ip_set_format(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *src_format; struct v4l2_mbus_framefmt *sink_format; - src_format = v4l2_subdev_get_pad_format(sd, state, RZG2L_CRU_IP_SOURCE); + src_format = v4l2_subdev_state_get_format(state, RZG2L_CRU_IP_SOURCE); if (fmt->pad == RZG2L_CRU_IP_SOURCE) { fmt->format = *src_format; return 0; } - sink_format = v4l2_subdev_get_pad_format(sd, state, fmt->pad); + sink_format = v4l2_subdev_state_get_format(state, fmt->pad); if (!rzg2l_cru_ip_code_to_fmt(fmt->format.code)) sink_format->code = rzg2l_cru_ip_formats[0].code; @@ -168,8 +168,8 @@ static int rzg2l_cru_ip_enum_frame_size(struct v4l2_subdev *sd, return 0; } -static int rzg2l_cru_ip_init_config(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int rzg2l_cru_ip_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { .pad = RZG2L_CRU_IP_SINK, }; @@ -192,7 +192,6 @@ static const struct v4l2_subdev_video_ops rzg2l_cru_ip_video_ops = { static const struct v4l2_subdev_pad_ops rzg2l_cru_ip_pad_ops = { .enum_mbus_code = rzg2l_cru_ip_enum_mbus_code, .enum_frame_size = rzg2l_cru_ip_enum_frame_size, - .init_cfg = rzg2l_cru_ip_init_config, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = rzg2l_cru_ip_set_format, }; @@ -202,6 +201,10 @@ static const struct v4l2_subdev_ops rzg2l_cru_ip_subdev_ops = { .pad = &rzg2l_cru_ip_pad_ops, }; +static const struct v4l2_subdev_internal_ops rzg2l_cru_ip_internal_ops = { + .init_state = rzg2l_cru_ip_init_state, +}; + static const struct media_entity_operations rzg2l_cru_ip_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -213,6 +216,7 @@ int rzg2l_cru_ip_subdev_register(struct rzg2l_cru_dev *cru) ip->subdev.dev = cru->dev; v4l2_subdev_init(&ip->subdev, &rzg2l_cru_ip_subdev_ops); + ip->subdev.internal_ops = &rzg2l_cru_ip_internal_ops; v4l2_set_subdevdata(&ip->subdev, cru); snprintf(ip->subdev.name, sizeof(ip->subdev.name), "cru-ip-%s", dev_name(cru->dev)); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_brx.c b/drivers/media/platform/renesas/vsp1/vsp1_brx.c index 89385b4cabe572..c48ebd67187f0d 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_brx.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_brx.c @@ -100,7 +100,7 @@ static struct v4l2_rect *brx_get_compose(struct vsp1_brx *brx, struct v4l2_subdev_state *sd_state, unsigned int pad) { - return v4l2_subdev_get_try_compose(&brx->entity.subdev, sd_state, pad); + return v4l2_subdev_state_get_compose(sd_state, pad); } static void brx_try_format(struct vsp1_brx *brx, @@ -266,7 +266,6 @@ static int brx_set_selection(struct v4l2_subdev *subdev, } static const struct v4l2_subdev_pad_ops brx_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = brx_enum_mbus_code, .enum_frame_size = brx_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_clu.c b/drivers/media/platform/renesas/vsp1/vsp1_clu.c index c5217fee24f139..8fe84c6072f254 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_clu.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_clu.c @@ -155,7 +155,6 @@ static int clu_set_format(struct v4l2_subdev *subdev, */ static const struct v4l2_subdev_pad_ops clu_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = clu_enum_mbus_code, .enum_frame_size = clu_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c index c31f05a80bb567..a84f2c13fb9c0b 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c @@ -128,6 +128,35 @@ vsp1_entity_get_pad_config(struct vsp1_entity *entity, } } +/* + * vsp1_entity_init_state - Initialize formats on all pads + * @subdev: V4L2 subdevice + * @cfg: V4L2 subdev pad configuration + * + * Initialize all pad formats with default values in the given pad config. This + * function can be used as a handler for the subdev internal_ops::init_state + * operation. + */ +static int vsp1_entity_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state) +{ + unsigned int pad; + + /* Initialize all pad formats with default values. */ + for (pad = 0; pad < subdev->entity.num_pads - 1; ++pad) { + struct v4l2_subdev_format format = { + .pad = pad, + .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY + : V4L2_SUBDEV_FORMAT_ACTIVE, + }; + + v4l2_subdev_call(subdev, pad, set_fmt, sd_state, &format); + } + + return 0; +} + + /** * vsp1_entity_get_pad_format - Get a pad format from storage for an entity * @entity: the entity @@ -142,7 +171,7 @@ vsp1_entity_get_pad_format(struct vsp1_entity *entity, struct v4l2_subdev_state *sd_state, unsigned int pad) { - return v4l2_subdev_get_try_format(&entity->subdev, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); } /** @@ -163,42 +192,14 @@ vsp1_entity_get_pad_selection(struct vsp1_entity *entity, { switch (target) { case V4L2_SEL_TGT_COMPOSE: - return v4l2_subdev_get_try_compose(&entity->subdev, sd_state, - pad); + return v4l2_subdev_state_get_compose(sd_state, pad); case V4L2_SEL_TGT_CROP: - return v4l2_subdev_get_try_crop(&entity->subdev, sd_state, - pad); + return v4l2_subdev_state_get_crop(sd_state, pad); default: return NULL; } } -/* - * vsp1_entity_init_cfg - Initialize formats on all pads - * @subdev: V4L2 subdevice - * @cfg: V4L2 subdev pad configuration - * - * Initialize all pad formats with default values in the given pad config. This - * function can be used as a handler for the subdev pad::init_cfg operation. - */ -int vsp1_entity_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state) -{ - unsigned int pad; - - for (pad = 0; pad < subdev->entity.num_pads - 1; ++pad) { - struct v4l2_subdev_format format = { - .pad = pad, - .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY - : V4L2_SUBDEV_FORMAT_ACTIVE, - }; - - v4l2_subdev_call(subdev, pad, set_fmt, sd_state, &format); - } - - return 0; -} - /* * vsp1_subdev_get_pad_format - Subdev pad get_fmt handler * @subdev: V4L2 subdevice @@ -427,6 +428,10 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, return ret; } +static const struct v4l2_subdev_internal_ops vsp1_entity_internal_ops = { + .init_state = vsp1_entity_init_state, +}; + /* ----------------------------------------------------------------------------- * Media Operations */ @@ -661,6 +666,7 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, /* Initialize the V4L2 subdev. */ subdev = &entity->subdev; v4l2_subdev_init(subdev, ops); + subdev->internal_ops = &vsp1_entity_internal_ops; subdev->entity.function = function; subdev->entity.ops = &vsp1->media_ops; @@ -669,7 +675,7 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, snprintf(subdev->name, sizeof(subdev->name), "%s %s", dev_name(vsp1->dev), name); - vsp1_entity_init_cfg(subdev, NULL); + vsp1_entity_init_state(subdev, NULL); /* * Allocate the pad configuration to store formats and selection diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.h b/drivers/media/platform/renesas/vsp1/vsp1_entity.h index 17f98a6a972e3c..eb83875d1c5536 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.h @@ -146,8 +146,6 @@ struct v4l2_rect * vsp1_entity_get_pad_selection(struct vsp1_entity *entity, struct v4l2_subdev_state *sd_state, unsigned int pad, unsigned int target); -int vsp1_entity_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state); void vsp1_entity_route_setup(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c index 361a870380c202..b547ee683644cf 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c @@ -114,7 +114,6 @@ static int hsit_set_format(struct v4l2_subdev *subdev, } static const struct v4l2_subdev_pad_ops hsit_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = hsit_enum_mbus_code, .enum_frame_size = hsit_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_lif.c b/drivers/media/platform/renesas/vsp1/vsp1_lif.c index 0ab2e0c7047416..18b1036677c1dc 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_lif.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_lif.c @@ -68,7 +68,6 @@ static int lif_set_format(struct v4l2_subdev *subdev, } static const struct v4l2_subdev_pad_ops lif_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = lif_enum_mbus_code, .enum_frame_size = lif_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_lut.c b/drivers/media/platform/renesas/vsp1/vsp1_lut.c index ac6802a325f5aa..451d24ab0b56fb 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_lut.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_lut.c @@ -131,7 +131,6 @@ static int lut_set_format(struct v4l2_subdev *subdev, */ static const struct v4l2_subdev_pad_ops lut_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = lut_enum_mbus_code, .enum_frame_size = lut_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c index e0f87c8103ca56..36bac0a9f1262d 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c @@ -19,8 +19,7 @@ struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, struct v4l2_subdev_state *sd_state) { - return v4l2_subdev_get_try_crop(&rwpf->entity.subdev, sd_state, - RWPF_PAD_SINK); + return v4l2_subdev_state_get_crop(sd_state, RWPF_PAD_SINK); } /* ----------------------------------------------------------------------------- @@ -244,7 +243,6 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, } static const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = vsp1_rwpf_enum_mbus_code, .enum_frame_size = vsp1_rwpf_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_sru.c b/drivers/media/platform/renesas/vsp1/vsp1_sru.c index b614a2aea46118..c48857cd99d0f5 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_sru.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_sru.c @@ -254,7 +254,6 @@ static int sru_set_format(struct v4l2_subdev *subdev, } static const struct v4l2_subdev_pad_ops sru_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = sru_enum_mbus_code, .enum_frame_size = sru_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uds.c b/drivers/media/platform/renesas/vsp1/vsp1_uds.c index 1c290cda005a3e..00d5e39d48de32 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_uds.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uds.c @@ -242,7 +242,6 @@ static int uds_set_format(struct v4l2_subdev *subdev, */ static const struct v4l2_subdev_pad_ops uds_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = uds_enum_mbus_code, .enum_frame_size = uds_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uif.c b/drivers/media/platform/renesas/vsp1/vsp1_uif.c index 83d7f17df80e7d..75a80bbe48efa7 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_uif.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uif.c @@ -176,7 +176,6 @@ static int uif_set_selection(struct v4l2_subdev *subdev, */ static const struct v4l2_subdev_pad_ops uif_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = uif_enum_mbus_code, .enum_frame_size = uif_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h index e9bc6c155d2fcf..a4e272adc1ad06 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h @@ -175,9 +175,6 @@ struct rkisp1_sensor_async { * @is_dphy_errctrl_disabled: if dphy errctrl is disabled (avoid endless interrupt) * @sd: v4l2_subdev variable * @pads: media pads - * @pad_cfg: configurations for the pads - * @sink_fmt: input format - * @lock: protects pad_cfg and sink_fmt * @source: source in-use, set when starting streaming */ struct rkisp1_csi { @@ -186,9 +183,6 @@ struct rkisp1_csi { bool is_dphy_errctrl_disabled; struct v4l2_subdev sd; struct media_pad pads[RKISP1_CSI_PAD_NUM]; - struct v4l2_subdev_pad_config pad_cfg[RKISP1_CSI_PAD_NUM]; - const struct rkisp1_mbus_info *sink_fmt; - struct mutex lock; struct v4l2_subdev *source; }; @@ -198,20 +192,14 @@ struct rkisp1_csi { * @sd: v4l2_subdev variable * @rkisp1: pointer to rkisp1_device * @pads: media pads - * @pad_cfg: pads configurations * @sink_fmt: input format - * @src_fmt: output format - * @ops_lock: ops serialization * @frame_sequence: used to synchronize frame_id between video devices. */ struct rkisp1_isp { struct v4l2_subdev sd; struct rkisp1_device *rkisp1; struct media_pad pads[RKISP1_ISP_PAD_MAX]; - struct v4l2_subdev_pad_config pad_cfg[RKISP1_ISP_PAD_MAX]; const struct rkisp1_mbus_info *sink_fmt; - const struct rkisp1_mbus_info *src_fmt; - struct mutex ops_lock; /* serialize the subdevice ops */ __u32 frame_sequence; }; @@ -398,10 +386,7 @@ struct rkisp1_params { * @id: id of the resizer, one of RKISP1_SELFPATH, RKISP1_MAINPATH * @rkisp1: pointer to the rkisp1 device * @pads: media pads - * @pad_cfg: configurations for the pads * @config: the set of registers to configure the resizer - * @pixel_enc: pixel encoding of the resizer - * @ops_lock: a lock for the subdev ops */ struct rkisp1_resizer { struct v4l2_subdev sd; @@ -409,10 +394,7 @@ struct rkisp1_resizer { enum rkisp1_stream_id id; struct rkisp1_device *rkisp1; struct media_pad pads[RKISP1_RSZ_PAD_MAX]; - struct v4l2_subdev_pad_config pad_cfg[RKISP1_RSZ_PAD_MAX]; const struct rkisp1_rsz_config *config; - enum v4l2_pixel_encoding pixel_enc; - struct mutex ops_lock; /* serialize the subdevice ops */ }; /* diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c index 1537dccbd2e28f..4202642e052392 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c @@ -30,23 +30,6 @@ static inline struct rkisp1_csi *to_rkisp1_csi(struct v4l2_subdev *sd) return container_of(sd, struct rkisp1_csi, sd); } -static struct v4l2_mbus_framefmt * -rkisp1_csi_get_pad_fmt(struct rkisp1_csi *csi, - struct v4l2_subdev_state *sd_state, - unsigned int pad, u32 which) -{ - struct v4l2_subdev_state state = { - .pads = csi->pad_cfg - }; - - lockdep_assert_held(&csi->lock); - - if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&csi->sd, sd_state, pad); - else - return v4l2_subdev_get_try_format(&csi->sd, &state, pad); -} - int rkisp1_csi_link_sensor(struct rkisp1_device *rkisp1, struct v4l2_subdev *sd, struct rkisp1_sensor_async *s_asd, unsigned int source_pad) @@ -76,7 +59,8 @@ int rkisp1_csi_link_sensor(struct rkisp1_device *rkisp1, struct v4l2_subdev *sd, } static int rkisp1_csi_config(struct rkisp1_csi *csi, - const struct rkisp1_sensor_async *sensor) + const struct rkisp1_sensor_async *sensor, + const struct rkisp1_mbus_info *format) { struct rkisp1_device *rkisp1 = csi->rkisp1; unsigned int lanes = sensor->lanes; @@ -98,7 +82,7 @@ static int rkisp1_csi_config(struct rkisp1_csi *csi, /* Configure Data Type and Virtual Channel */ rkisp1_write(rkisp1, RKISP1_CIF_MIPI_IMG_DATA_SEL, - RKISP1_CIF_MIPI_DATA_SEL_DT(csi->sink_fmt->mipi_dt) | + RKISP1_CIF_MIPI_DATA_SEL_DT(format->mipi_dt) | RKISP1_CIF_MIPI_DATA_SEL_VC(0)); /* Clear MIPI interrupts */ @@ -163,7 +147,8 @@ static void rkisp1_csi_disable(struct rkisp1_csi *csi) } static int rkisp1_csi_start(struct rkisp1_csi *csi, - const struct rkisp1_sensor_async *sensor) + const struct rkisp1_sensor_async *sensor, + const struct rkisp1_mbus_info *format) { struct rkisp1_device *rkisp1 = csi->rkisp1; union phy_configure_opts opts; @@ -171,7 +156,7 @@ static int rkisp1_csi_start(struct rkisp1_csi *csi, s64 pixel_clock; int ret; - ret = rkisp1_csi_config(csi, sensor); + ret = rkisp1_csi_config(csi, sensor, format); if (ret) return ret; @@ -181,7 +166,7 @@ static int rkisp1_csi_start(struct rkisp1_csi *csi, return -EINVAL; } - phy_mipi_dphy_get_default_config(pixel_clock, csi->sink_fmt->bus_width, + phy_mipi_dphy_get_default_config(pixel_clock, format->bus_width, sensor->lanes, cfg); phy_set_mode(csi->dphy, PHY_MODE_MIPI_DPHY); phy_configure(csi->dphy, &opts); @@ -263,7 +248,6 @@ static int rkisp1_csi_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { - struct rkisp1_csi *csi = to_rkisp1_csi(sd); unsigned int i; int pos = 0; @@ -273,15 +257,10 @@ static int rkisp1_csi_enum_mbus_code(struct v4l2_subdev *sd, if (code->index) return -EINVAL; - mutex_lock(&csi->lock); - - sink_fmt = rkisp1_csi_get_pad_fmt(csi, sd_state, - RKISP1_CSI_PAD_SINK, - code->which); + sink_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_CSI_PAD_SINK); code->code = sink_fmt->code; - mutex_unlock(&csi->lock); - return 0; } @@ -306,15 +285,13 @@ static int rkisp1_csi_enum_mbus_code(struct v4l2_subdev *sd, return -EINVAL; } -static int rkisp1_csi_init_config(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int rkisp1_csi_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; - sink_fmt = v4l2_subdev_get_try_format(sd, sd_state, - RKISP1_CSI_PAD_SINK); - src_fmt = v4l2_subdev_get_try_format(sd, sd_state, - RKISP1_CSI_PAD_SRC); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_CSI_PAD_SINK); + src_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_CSI_PAD_SRC); sink_fmt->width = RKISP1_DEFAULT_WIDTH; sink_fmt->height = RKISP1_DEFAULT_HEIGHT; @@ -326,36 +303,18 @@ static int rkisp1_csi_init_config(struct v4l2_subdev *sd, return 0; } -static int rkisp1_csi_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct rkisp1_csi *csi = to_rkisp1_csi(sd); - - mutex_lock(&csi->lock); - fmt->format = *rkisp1_csi_get_pad_fmt(csi, sd_state, fmt->pad, - fmt->which); - mutex_unlock(&csi->lock); - - return 0; -} - static int rkisp1_csi_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { - struct rkisp1_csi *csi = to_rkisp1_csi(sd); const struct rkisp1_mbus_info *mbus_info; struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; /* The format on the source pad always matches the sink pad. */ if (fmt->pad == RKISP1_CSI_PAD_SRC) - return rkisp1_csi_get_fmt(sd, sd_state, fmt); - - mutex_lock(&csi->lock); + return v4l2_subdev_get_fmt(sd, sd_state, fmt); - sink_fmt = rkisp1_csi_get_pad_fmt(csi, sd_state, RKISP1_CSI_PAD_SINK, - fmt->which); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_CSI_PAD_SINK); sink_fmt->code = fmt->format.code; @@ -374,16 +333,10 @@ static int rkisp1_csi_set_fmt(struct v4l2_subdev *sd, fmt->format = *sink_fmt; - if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) - csi->sink_fmt = mbus_info; - /* Propagate the format to the source pad. */ - src_fmt = rkisp1_csi_get_pad_fmt(csi, sd_state, RKISP1_CSI_PAD_SRC, - fmt->which); + src_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_CSI_PAD_SRC); *src_fmt = *sink_fmt; - mutex_unlock(&csi->lock); - return 0; } @@ -395,8 +348,11 @@ static int rkisp1_csi_s_stream(struct v4l2_subdev *sd, int enable) { struct rkisp1_csi *csi = to_rkisp1_csi(sd); struct rkisp1_device *rkisp1 = csi->rkisp1; + const struct v4l2_mbus_framefmt *sink_fmt; + const struct rkisp1_mbus_info *format; struct rkisp1_sensor_async *source_asd; struct v4l2_async_connection *asc; + struct v4l2_subdev_state *sd_state; struct media_pad *source_pad; struct v4l2_subdev *source; int ret; @@ -430,9 +386,12 @@ static int rkisp1_csi_s_stream(struct v4l2_subdev *sd, int enable) if (source_asd->mbus_type != V4L2_MBUS_CSI2_DPHY) return -EINVAL; - mutex_lock(&csi->lock); - ret = rkisp1_csi_start(csi, source_asd); - mutex_unlock(&csi->lock); + sd_state = v4l2_subdev_lock_and_get_active_state(sd); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_CSI_PAD_SINK); + format = rkisp1_mbus_info_get_by_code(sink_fmt->code); + v4l2_subdev_unlock_state(sd_state); + + ret = rkisp1_csi_start(csi, source_asd, format); if (ret) return ret; @@ -461,8 +420,7 @@ static const struct v4l2_subdev_video_ops rkisp1_csi_video_ops = { static const struct v4l2_subdev_pad_ops rkisp1_csi_pad_ops = { .enum_mbus_code = rkisp1_csi_enum_mbus_code, - .init_cfg = rkisp1_csi_init_config, - .get_fmt = rkisp1_csi_get_fmt, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = rkisp1_csi_set_fmt, }; @@ -471,19 +429,22 @@ static const struct v4l2_subdev_ops rkisp1_csi_ops = { .pad = &rkisp1_csi_pad_ops, }; +static const struct v4l2_subdev_internal_ops rkisp1_csi_internal_ops = { + .init_state = rkisp1_csi_init_state, +}; + int rkisp1_csi_register(struct rkisp1_device *rkisp1) { struct rkisp1_csi *csi = &rkisp1->csi; - struct v4l2_subdev_state state = {}; struct media_pad *pads; struct v4l2_subdev *sd; int ret; csi->rkisp1 = rkisp1; - mutex_init(&csi->lock); sd = &csi->sd; v4l2_subdev_init(sd, &rkisp1_csi_ops); + sd->internal_ops = &rkisp1_csi_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; sd->entity.ops = &rkisp1_csi_media_ops; sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; @@ -496,26 +457,26 @@ int rkisp1_csi_register(struct rkisp1_device *rkisp1) pads[RKISP1_CSI_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT; - csi->sink_fmt = rkisp1_mbus_info_get_by_code(RKISP1_CSI_DEF_FMT); - ret = media_entity_pads_init(&sd->entity, RKISP1_CSI_PAD_NUM, pads); if (ret) - goto error; + goto err_entity_cleanup; - state.pads = csi->pad_cfg; - rkisp1_csi_init_config(sd, &state); + ret = v4l2_subdev_init_finalize(sd); + if (ret) + goto err_entity_cleanup; ret = v4l2_device_register_subdev(&csi->rkisp1->v4l2_dev, sd); if (ret) { dev_err(sd->dev, "Failed to register csi receiver subdev\n"); - goto error; + goto err_subdev_cleanup; } return 0; -error: +err_subdev_cleanup: + v4l2_subdev_cleanup(sd); +err_entity_cleanup: media_entity_cleanup(&sd->entity); - mutex_destroy(&csi->lock); csi->rkisp1 = NULL; return ret; } @@ -528,8 +489,8 @@ void rkisp1_csi_unregister(struct rkisp1_device *rkisp1) return; v4l2_device_unregister_subdev(&csi->sd); + v4l2_subdev_cleanup(&csi->sd); media_entity_cleanup(&csi->sd.entity); - mutex_destroy(&csi->lock); } int rkisp1_csi_init(struct rkisp1_device *rkisp1) diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c index 8fc9c1c116f1dc..6cd1244c30de56 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c @@ -53,40 +53,6 @@ * +---------------------------------------------------------+ */ -/* ---------------------------------------------------------------------------- - * Helpers - */ - -static struct v4l2_mbus_framefmt * -rkisp1_isp_get_pad_fmt(struct rkisp1_isp *isp, - struct v4l2_subdev_state *sd_state, - unsigned int pad, u32 which) -{ - struct v4l2_subdev_state state = { - .pads = isp->pad_cfg - }; - - if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&isp->sd, sd_state, pad); - else - return v4l2_subdev_get_try_format(&isp->sd, &state, pad); -} - -static struct v4l2_rect * -rkisp1_isp_get_pad_crop(struct rkisp1_isp *isp, - struct v4l2_subdev_state *sd_state, - unsigned int pad, u32 which) -{ - struct v4l2_subdev_state state = { - .pads = isp->pad_cfg - }; - - if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&isp->sd, sd_state, pad); - else - return v4l2_subdev_get_try_crop(&isp->sd, &state, pad); -} - /* ---------------------------------------------------------------------------- * Camera Interface registers configurations */ @@ -96,12 +62,12 @@ rkisp1_isp_get_pad_crop(struct rkisp1_isp *isp, * This should only be called when configuring CIF * or at the frame end interrupt */ -static void rkisp1_config_ism(struct rkisp1_isp *isp) +static void rkisp1_config_ism(struct rkisp1_isp *isp, + struct v4l2_subdev_state *sd_state) { const struct v4l2_rect *src_crop = - rkisp1_isp_get_pad_crop(isp, NULL, - RKISP1_ISP_PAD_SOURCE_VIDEO, - V4L2_SUBDEV_FORMAT_ACTIVE); + v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); struct rkisp1_device *rkisp1 = isp->rkisp1; u32 val; @@ -125,21 +91,26 @@ static void rkisp1_config_ism(struct rkisp1_isp *isp) * configure ISP blocks with input format, size...... */ static int rkisp1_config_isp(struct rkisp1_isp *isp, + struct v4l2_subdev_state *sd_state, enum v4l2_mbus_type mbus_type, u32 mbus_flags) { struct rkisp1_device *rkisp1 = isp->rkisp1; u32 isp_ctrl = 0, irq_mask = 0, acq_mult = 0, acq_prop = 0; - const struct rkisp1_mbus_info *sink_fmt = isp->sink_fmt; - const struct rkisp1_mbus_info *src_fmt = isp->src_fmt; + const struct rkisp1_mbus_info *sink_fmt; + const struct rkisp1_mbus_info *src_fmt; + const struct v4l2_mbus_framefmt *src_frm; const struct v4l2_mbus_framefmt *sink_frm; const struct v4l2_rect *sink_crop; - sink_frm = rkisp1_isp_get_pad_fmt(isp, NULL, - RKISP1_ISP_PAD_SINK_VIDEO, - V4L2_SUBDEV_FORMAT_ACTIVE); - sink_crop = rkisp1_isp_get_pad_crop(isp, NULL, - RKISP1_ISP_PAD_SINK_VIDEO, - V4L2_SUBDEV_FORMAT_ACTIVE); + sink_frm = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); + sink_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); + src_frm = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); + + sink_fmt = rkisp1_mbus_info_get_by_code(sink_frm->code); + src_fmt = rkisp1_mbus_info_get_by_code(src_frm->code); if (sink_fmt->pixel_enc == V4L2_PIXEL_ENC_BAYER) { acq_mult = 1; @@ -230,14 +201,15 @@ static int rkisp1_config_isp(struct rkisp1_isp *isp, } else { struct v4l2_mbus_framefmt *src_frm; - src_frm = rkisp1_isp_get_pad_fmt(isp, NULL, - RKISP1_ISP_PAD_SOURCE_VIDEO, - V4L2_SUBDEV_FORMAT_ACTIVE); + src_frm = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); rkisp1_params_pre_configure(&rkisp1->params, sink_fmt->bayer_pat, src_frm->quantization, src_frm->ycbcr_enc); } + isp->sink_fmt = sink_fmt; + return 0; } @@ -258,16 +230,17 @@ static void rkisp1_config_path(struct rkisp1_isp *isp, /* Hardware configure Entry */ static int rkisp1_config_cif(struct rkisp1_isp *isp, + struct v4l2_subdev_state *sd_state, enum v4l2_mbus_type mbus_type, u32 mbus_flags) { int ret; - ret = rkisp1_config_isp(isp, mbus_type, mbus_flags); + ret = rkisp1_config_isp(isp, sd_state, mbus_type, mbus_flags); if (ret) return ret; rkisp1_config_path(isp, mbus_type); - rkisp1_config_ism(isp); + rkisp1_config_ism(isp, sd_state); return 0; } @@ -342,9 +315,12 @@ static void rkisp1_config_clk(struct rkisp1_isp *isp) } } -static void rkisp1_isp_start(struct rkisp1_isp *isp) +static void rkisp1_isp_start(struct rkisp1_isp *isp, + struct v4l2_subdev_state *sd_state) { struct rkisp1_device *rkisp1 = isp->rkisp1; + const struct v4l2_mbus_framefmt *src_fmt; + const struct rkisp1_mbus_info *src_info; u32 val; rkisp1_config_clk(isp); @@ -356,7 +332,11 @@ static void rkisp1_isp_start(struct rkisp1_isp *isp) RKISP1_CIF_ISP_CTRL_ISP_INFORM_ENABLE; rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL, val); - if (isp->src_fmt->pixel_enc != V4L2_PIXEL_ENC_BAYER) + src_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); + src_info = rkisp1_mbus_info_get_by_code(src_fmt->code); + + if (src_info->pixel_enc != V4L2_PIXEL_ENC_BAYER) rkisp1_params_post_configure(&rkisp1->params); } @@ -443,15 +423,15 @@ static int rkisp1_isp_enum_frame_size(struct v4l2_subdev *sd, return 0; } -static int rkisp1_isp_init_config(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int rkisp1_isp_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; struct v4l2_rect *sink_crop, *src_crop; /* Video. */ - sink_fmt = v4l2_subdev_get_try_format(sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); + sink_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); sink_fmt->width = RKISP1_DEFAULT_WIDTH; sink_fmt->height = RKISP1_DEFAULT_HEIGHT; sink_fmt->field = V4L2_FIELD_NONE; @@ -461,15 +441,15 @@ static int rkisp1_isp_init_config(struct v4l2_subdev *sd, sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; sink_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; - sink_crop = v4l2_subdev_get_try_crop(sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); + sink_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); sink_crop->width = RKISP1_DEFAULT_WIDTH; sink_crop->height = RKISP1_DEFAULT_HEIGHT; sink_crop->left = 0; sink_crop->top = 0; - src_fmt = v4l2_subdev_get_try_format(sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); + src_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); *src_fmt = *sink_fmt; src_fmt->code = RKISP1_DEF_SRC_PAD_FMT; src_fmt->colorspace = V4L2_COLORSPACE_SRGB; @@ -477,15 +457,15 @@ static int rkisp1_isp_init_config(struct v4l2_subdev *sd, src_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; src_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE; - src_crop = v4l2_subdev_get_try_crop(sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); + src_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); *src_crop = *sink_crop; /* Parameters and statistics. */ - sink_fmt = v4l2_subdev_get_try_format(sd, sd_state, - RKISP1_ISP_PAD_SINK_PARAMS); - src_fmt = v4l2_subdev_get_try_format(sd, sd_state, - RKISP1_ISP_PAD_SOURCE_STATS); + sink_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SINK_PARAMS); + src_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SOURCE_STATS); sink_fmt->width = 0; sink_fmt->height = 0; sink_fmt->field = V4L2_FIELD_NONE; @@ -497,8 +477,7 @@ static int rkisp1_isp_init_config(struct v4l2_subdev *sd, static void rkisp1_isp_set_src_fmt(struct rkisp1_isp *isp, struct v4l2_subdev_state *sd_state, - struct v4l2_mbus_framefmt *format, - unsigned int which) + struct v4l2_mbus_framefmt *format) { const struct rkisp1_mbus_info *sink_info; const struct rkisp1_mbus_info *src_info; @@ -507,12 +486,12 @@ static void rkisp1_isp_set_src_fmt(struct rkisp1_isp *isp, const struct v4l2_rect *src_crop; bool set_csc; - sink_fmt = rkisp1_isp_get_pad_fmt(isp, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO, which); - src_fmt = rkisp1_isp_get_pad_fmt(isp, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO, which); - src_crop = rkisp1_isp_get_pad_crop(isp, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO, which); + sink_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); + src_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); + src_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); /* * Media bus code. The ISP can operate in pass-through mode (Bayer in, @@ -595,26 +574,20 @@ static void rkisp1_isp_set_src_fmt(struct rkisp1_isp *isp, */ if (set_csc) format->flags |= V4L2_MBUS_FRAMEFMT_SET_CSC; - - /* Store the source format info when setting the active format. */ - if (which == V4L2_SUBDEV_FORMAT_ACTIVE) - isp->src_fmt = src_info; } static void rkisp1_isp_set_src_crop(struct rkisp1_isp *isp, struct v4l2_subdev_state *sd_state, - struct v4l2_rect *r, unsigned int which) + struct v4l2_rect *r) { struct v4l2_mbus_framefmt *src_fmt; const struct v4l2_rect *sink_crop; struct v4l2_rect *src_crop; - src_crop = rkisp1_isp_get_pad_crop(isp, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO, - which); - sink_crop = rkisp1_isp_get_pad_crop(isp, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO, - which); + src_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); + sink_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); src_crop->left = ALIGN(r->left, 2); src_crop->width = ALIGN(r->width, 2); @@ -625,24 +598,22 @@ static void rkisp1_isp_set_src_crop(struct rkisp1_isp *isp, *r = *src_crop; /* Propagate to out format */ - src_fmt = rkisp1_isp_get_pad_fmt(isp, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO, which); - rkisp1_isp_set_src_fmt(isp, sd_state, src_fmt, which); + src_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); + rkisp1_isp_set_src_fmt(isp, sd_state, src_fmt); } static void rkisp1_isp_set_sink_crop(struct rkisp1_isp *isp, struct v4l2_subdev_state *sd_state, - struct v4l2_rect *r, unsigned int which) + struct v4l2_rect *r) { struct v4l2_rect *sink_crop, *src_crop; const struct v4l2_mbus_framefmt *sink_fmt; - sink_crop = rkisp1_isp_get_pad_crop(isp, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO, - which); - sink_fmt = rkisp1_isp_get_pad_fmt(isp, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO, - which); + sink_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); + sink_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); sink_crop->left = ALIGN(r->left, 2); sink_crop->width = ALIGN(r->width, 2); @@ -653,32 +624,28 @@ static void rkisp1_isp_set_sink_crop(struct rkisp1_isp *isp, *r = *sink_crop; /* Propagate to out crop */ - src_crop = rkisp1_isp_get_pad_crop(isp, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO, which); - rkisp1_isp_set_src_crop(isp, sd_state, src_crop, which); + src_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); + rkisp1_isp_set_src_crop(isp, sd_state, src_crop); } static void rkisp1_isp_set_sink_fmt(struct rkisp1_isp *isp, struct v4l2_subdev_state *sd_state, - struct v4l2_mbus_framefmt *format, - unsigned int which) + struct v4l2_mbus_framefmt *format) { const struct rkisp1_mbus_info *mbus_info; struct v4l2_mbus_framefmt *sink_fmt; struct v4l2_rect *sink_crop; bool is_yuv; - sink_fmt = rkisp1_isp_get_pad_fmt(isp, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO, - which); + sink_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); sink_fmt->code = format->code; mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code); if (!mbus_info || !(mbus_info->direction & RKISP1_ISP_SD_SINK)) { sink_fmt->code = RKISP1_DEF_SINK_PAD_FMT; mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code); } - if (which == V4L2_SUBDEV_FORMAT_ACTIVE) - isp->sink_fmt = mbus_info; sink_fmt->width = clamp_t(u32, format->width, RKISP1_ISP_MIN_WIDTH, @@ -720,23 +687,9 @@ static void rkisp1_isp_set_sink_fmt(struct rkisp1_isp *isp, *format = *sink_fmt; /* Propagate to in crop */ - sink_crop = rkisp1_isp_get_pad_crop(isp, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO, - which); - rkisp1_isp_set_sink_crop(isp, sd_state, sink_crop, which); -} - -static int rkisp1_isp_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct rkisp1_isp *isp = to_rkisp1_isp(sd); - - mutex_lock(&isp->ops_lock); - fmt->format = *rkisp1_isp_get_pad_fmt(isp, sd_state, fmt->pad, - fmt->which); - mutex_unlock(&isp->ops_lock); - return 0; + sink_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); + rkisp1_isp_set_sink_crop(isp, sd_state, sink_crop); } static int rkisp1_isp_set_fmt(struct v4l2_subdev *sd, @@ -745,18 +698,14 @@ static int rkisp1_isp_set_fmt(struct v4l2_subdev *sd, { struct rkisp1_isp *isp = to_rkisp1_isp(sd); - mutex_lock(&isp->ops_lock); if (fmt->pad == RKISP1_ISP_PAD_SINK_VIDEO) - rkisp1_isp_set_sink_fmt(isp, sd_state, &fmt->format, - fmt->which); + rkisp1_isp_set_sink_fmt(isp, sd_state, &fmt->format); else if (fmt->pad == RKISP1_ISP_PAD_SOURCE_VIDEO) - rkisp1_isp_set_src_fmt(isp, sd_state, &fmt->format, - fmt->which); + rkisp1_isp_set_src_fmt(isp, sd_state, &fmt->format); else - fmt->format = *rkisp1_isp_get_pad_fmt(isp, sd_state, fmt->pad, - fmt->which); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); - mutex_unlock(&isp->ops_lock); return 0; } @@ -764,39 +713,37 @@ static int rkisp1_isp_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { - struct rkisp1_isp *isp = to_rkisp1_isp(sd); int ret = 0; if (sel->pad != RKISP1_ISP_PAD_SOURCE_VIDEO && sel->pad != RKISP1_ISP_PAD_SINK_VIDEO) return -EINVAL; - mutex_lock(&isp->ops_lock); switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: if (sel->pad == RKISP1_ISP_PAD_SINK_VIDEO) { struct v4l2_mbus_framefmt *fmt; - fmt = rkisp1_isp_get_pad_fmt(isp, sd_state, sel->pad, - sel->which); + fmt = v4l2_subdev_state_get_format(sd_state, sel->pad); sel->r.height = fmt->height; sel->r.width = fmt->width; sel->r.left = 0; sel->r.top = 0; } else { - sel->r = *rkisp1_isp_get_pad_crop(isp, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO, - sel->which); + sel->r = *v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); } break; + case V4L2_SEL_TGT_CROP: - sel->r = *rkisp1_isp_get_pad_crop(isp, sd_state, sel->pad, - sel->which); + sel->r = *v4l2_subdev_state_get_crop(sd_state, sel->pad); break; + default: ret = -EINVAL; + break; } - mutex_unlock(&isp->ops_lock); + return ret; } @@ -812,15 +759,14 @@ static int rkisp1_isp_set_selection(struct v4l2_subdev *sd, dev_dbg(isp->rkisp1->dev, "%s: pad: %d sel(%d,%d)/%dx%d\n", __func__, sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height); - mutex_lock(&isp->ops_lock); + if (sel->pad == RKISP1_ISP_PAD_SINK_VIDEO) - rkisp1_isp_set_sink_crop(isp, sd_state, &sel->r, sel->which); + rkisp1_isp_set_sink_crop(isp, sd_state, &sel->r); else if (sel->pad == RKISP1_ISP_PAD_SOURCE_VIDEO) - rkisp1_isp_set_src_crop(isp, sd_state, &sel->r, sel->which); + rkisp1_isp_set_src_crop(isp, sd_state, &sel->r); else ret = -EINVAL; - mutex_unlock(&isp->ops_lock); return ret; } @@ -837,8 +783,7 @@ static const struct v4l2_subdev_pad_ops rkisp1_isp_pad_ops = { .enum_frame_size = rkisp1_isp_enum_frame_size, .get_selection = rkisp1_isp_get_selection, .set_selection = rkisp1_isp_set_selection, - .init_cfg = rkisp1_isp_init_config, - .get_fmt = rkisp1_isp_get_fmt, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = rkisp1_isp_set_fmt, .link_validate = v4l2_subdev_link_validate_default, }; @@ -851,6 +796,7 @@ static int rkisp1_isp_s_stream(struct v4l2_subdev *sd, int enable) { struct rkisp1_isp *isp = to_rkisp1_isp(sd); struct rkisp1_device *rkisp1 = isp->rkisp1; + struct v4l2_subdev_state *sd_state; struct media_pad *source_pad; struct media_pad *sink_pad; enum v4l2_mbus_type mbus_type; @@ -895,21 +841,23 @@ static int rkisp1_isp_s_stream(struct v4l2_subdev *sd, int enable) } isp->frame_sequence = -1; - mutex_lock(&isp->ops_lock); - ret = rkisp1_config_cif(isp, mbus_type, mbus_flags); + + sd_state = v4l2_subdev_lock_and_get_active_state(sd); + + ret = rkisp1_config_cif(isp, sd_state, mbus_type, mbus_flags); if (ret) - goto mutex_unlock; + goto out_unlock; - rkisp1_isp_start(isp); + rkisp1_isp_start(isp, sd_state); ret = v4l2_subdev_call(rkisp1->source, video, s_stream, true); if (ret) { rkisp1_isp_stop(isp); - goto mutex_unlock; + goto out_unlock; } -mutex_unlock: - mutex_unlock(&isp->ops_lock); +out_unlock: + v4l2_subdev_unlock_state(sd_state); return ret; } @@ -945,11 +893,12 @@ static const struct v4l2_subdev_ops rkisp1_isp_ops = { .pad = &rkisp1_isp_pad_ops, }; +static const struct v4l2_subdev_internal_ops rkisp1_isp_internal_ops = { + .init_state = rkisp1_isp_init_state, +}; + int rkisp1_isp_register(struct rkisp1_device *rkisp1) { - struct v4l2_subdev_state state = { - .pads = rkisp1->isp.pad_cfg - }; struct rkisp1_isp *isp = &rkisp1->isp; struct media_pad *pads = isp->pads; struct v4l2_subdev *sd = &isp->sd; @@ -958,6 +907,7 @@ int rkisp1_isp_register(struct rkisp1_device *rkisp1) isp->rkisp1 = rkisp1; v4l2_subdev_init(sd, &rkisp1_isp_ops); + sd->internal_ops = &rkisp1_isp_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; sd->entity.ops = &rkisp1_isp_media_ops; sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; @@ -970,27 +920,26 @@ int rkisp1_isp_register(struct rkisp1_device *rkisp1) pads[RKISP1_ISP_PAD_SOURCE_VIDEO].flags = MEDIA_PAD_FL_SOURCE; pads[RKISP1_ISP_PAD_SOURCE_STATS].flags = MEDIA_PAD_FL_SOURCE; - isp->sink_fmt = rkisp1_mbus_info_get_by_code(RKISP1_DEF_SINK_PAD_FMT); - isp->src_fmt = rkisp1_mbus_info_get_by_code(RKISP1_DEF_SRC_PAD_FMT); - - mutex_init(&isp->ops_lock); ret = media_entity_pads_init(&sd->entity, RKISP1_ISP_PAD_MAX, pads); if (ret) - goto error; + goto err_entity_cleanup; + + ret = v4l2_subdev_init_finalize(sd); + if (ret) + goto err_subdev_cleanup; ret = v4l2_device_register_subdev(&rkisp1->v4l2_dev, sd); if (ret) { dev_err(rkisp1->dev, "Failed to register isp subdev\n"); - goto error; + goto err_subdev_cleanup; } - rkisp1_isp_init_config(sd, &state); - return 0; -error: +err_subdev_cleanup: + v4l2_subdev_cleanup(sd); +err_entity_cleanup: media_entity_cleanup(&sd->entity); - mutex_destroy(&isp->ops_lock); isp->sd.v4l2_dev = NULL; return ret; } @@ -1004,7 +953,6 @@ void rkisp1_isp_unregister(struct rkisp1_device *rkisp1) v4l2_device_unregister_subdev(&isp->sd); media_entity_cleanup(&isp->sd.entity); - mutex_destroy(&isp->ops_lock); } /* ---------------------------------------------------------------------------- diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c index eb0aae56d2c7f8..128c80fe2c62d2 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c @@ -117,34 +117,6 @@ static inline void rkisp1_rsz_write(struct rkisp1_resizer *rsz, u32 offset, rkisp1_write(rsz->rkisp1, rsz->regs_base + offset, value); } -static struct v4l2_mbus_framefmt * -rkisp1_rsz_get_pad_fmt(struct rkisp1_resizer *rsz, - struct v4l2_subdev_state *sd_state, - unsigned int pad, u32 which) -{ - struct v4l2_subdev_state state = { - .pads = rsz->pad_cfg, - }; - if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&rsz->sd, sd_state, pad); - else - return v4l2_subdev_get_try_format(&rsz->sd, &state, pad); -} - -static struct v4l2_rect * -rkisp1_rsz_get_pad_crop(struct rkisp1_resizer *rsz, - struct v4l2_subdev_state *sd_state, - unsigned int pad, u32 which) -{ - struct v4l2_subdev_state state = { - .pads = rsz->pad_cfg, - }; - if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&rsz->sd, sd_state, pad); - else - return v4l2_subdev_get_try_crop(&rsz->sd, &state, pad); -} - /* ---------------------------------------------------------------------------- * Dual crop hw configs */ @@ -165,17 +137,16 @@ static void rkisp1_dcrop_disable(struct rkisp1_resizer *rsz, } /* configure dual-crop unit */ -static void rkisp1_dcrop_config(struct rkisp1_resizer *rsz) +static void rkisp1_dcrop_config(struct rkisp1_resizer *rsz, + struct v4l2_subdev_state *sd_state) { struct rkisp1_device *rkisp1 = rsz->rkisp1; struct v4l2_mbus_framefmt *sink_fmt; struct v4l2_rect *sink_crop; u32 dc_ctrl; - sink_crop = rkisp1_rsz_get_pad_crop(rsz, NULL, RKISP1_RSZ_PAD_SINK, - V4L2_SUBDEV_FORMAT_ACTIVE); - sink_fmt = rkisp1_rsz_get_pad_fmt(rsz, NULL, RKISP1_RSZ_PAD_SINK, - V4L2_SUBDEV_FORMAT_ACTIVE); + sink_crop = v4l2_subdev_state_get_crop(sd_state, RKISP1_RSZ_PAD_SINK); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SINK); if (sink_crop->width == sink_fmt->width && sink_crop->height == sink_fmt->height && @@ -236,10 +207,10 @@ static void rkisp1_rsz_disable(struct rkisp1_resizer *rsz, } static void rkisp1_rsz_config_regs(struct rkisp1_resizer *rsz, - struct v4l2_rect *sink_y, - struct v4l2_rect *sink_c, - struct v4l2_rect *src_y, - struct v4l2_rect *src_c, + const struct v4l2_rect *sink_y, + const struct v4l2_rect *sink_c, + const struct v4l2_area *src_y, + const struct v4l2_area *src_c, enum rkisp1_shadow_regs_when when) { u32 ratio, rsz_ctrl = 0; @@ -296,39 +267,37 @@ static void rkisp1_rsz_config_regs(struct rkisp1_resizer *rsz, } static void rkisp1_rsz_config(struct rkisp1_resizer *rsz, + struct v4l2_subdev_state *sd_state, enum rkisp1_shadow_regs_when when) { const struct rkisp1_rsz_yuv_mbus_info *sink_yuv_info, *src_yuv_info; - struct v4l2_rect sink_y, sink_c, src_y, src_c; - struct v4l2_mbus_framefmt *src_fmt, *sink_fmt; - struct v4l2_rect *sink_crop; + const struct v4l2_mbus_framefmt *src_fmt, *sink_fmt; + const struct v4l2_rect *sink_y; + struct v4l2_area src_y, src_c; + struct v4l2_rect sink_c; + + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SINK); + src_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SRC); - sink_crop = rkisp1_rsz_get_pad_crop(rsz, NULL, RKISP1_RSZ_PAD_SINK, - V4L2_SUBDEV_FORMAT_ACTIVE); - src_fmt = rkisp1_rsz_get_pad_fmt(rsz, NULL, RKISP1_RSZ_PAD_SRC, - V4L2_SUBDEV_FORMAT_ACTIVE); - src_yuv_info = rkisp1_rsz_get_yuv_mbus_info(src_fmt->code); - sink_fmt = rkisp1_rsz_get_pad_fmt(rsz, NULL, RKISP1_RSZ_PAD_SINK, - V4L2_SUBDEV_FORMAT_ACTIVE); sink_yuv_info = rkisp1_rsz_get_yuv_mbus_info(sink_fmt->code); + src_yuv_info = rkisp1_rsz_get_yuv_mbus_info(src_fmt->code); /* * The resizer only works on yuv formats, * so return if it is bayer format. */ - if (rsz->pixel_enc == V4L2_PIXEL_ENC_BAYER) { + if (!sink_yuv_info) { rkisp1_rsz_disable(rsz, when); return; } - sink_y.width = sink_crop->width; - sink_y.height = sink_crop->height; + sink_y = v4l2_subdev_state_get_crop(sd_state, RKISP1_RSZ_PAD_SINK); + sink_c.width = sink_y->width / sink_yuv_info->hdiv; + sink_c.height = sink_y->height / sink_yuv_info->vdiv; + src_y.width = src_fmt->width; src_y.height = src_fmt->height; - sink_c.width = sink_y.width / sink_yuv_info->hdiv; - sink_c.height = sink_y.height / sink_yuv_info->vdiv; - /* * The resizer is used not only to change the dimensions of the frame * but also to change the scale for YUV formats, @@ -344,13 +313,13 @@ static void rkisp1_rsz_config(struct rkisp1_resizer *rsz, } dev_dbg(rsz->rkisp1->dev, "stream %d rsz/scale: %dx%d -> %dx%d\n", - rsz->id, sink_crop->width, sink_crop->height, + rsz->id, sink_y->width, sink_y->height, src_fmt->width, src_fmt->height); dev_dbg(rsz->rkisp1->dev, "chroma scaling %dx%d -> %dx%d\n", sink_c.width, sink_c.height, src_c.width, src_c.height); /* set values in the hw */ - rkisp1_rsz_config_regs(rsz, &sink_y, &sink_c, &src_y, &src_c, when); + rkisp1_rsz_config_regs(rsz, sink_y, &sink_c, &src_y, &src_c, when); } /* ---------------------------------------------------------------------------- @@ -409,14 +378,13 @@ static int rkisp1_rsz_enum_mbus_code(struct v4l2_subdev *sd, return -EINVAL; } -static int rkisp1_rsz_init_config(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int rkisp1_rsz_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; struct v4l2_rect *sink_crop; - sink_fmt = v4l2_subdev_get_try_format(sd, sd_state, - RKISP1_RSZ_PAD_SRC); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SRC); sink_fmt->width = RKISP1_DEFAULT_WIDTH; sink_fmt->height = RKISP1_DEFAULT_HEIGHT; sink_fmt->field = V4L2_FIELD_NONE; @@ -426,15 +394,13 @@ static int rkisp1_rsz_init_config(struct v4l2_subdev *sd, sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; sink_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE; - sink_crop = v4l2_subdev_get_try_crop(sd, sd_state, - RKISP1_RSZ_PAD_SINK); + sink_crop = v4l2_subdev_state_get_crop(sd_state, RKISP1_RSZ_PAD_SINK); sink_crop->width = RKISP1_DEFAULT_WIDTH; sink_crop->height = RKISP1_DEFAULT_HEIGHT; sink_crop->left = 0; sink_crop->top = 0; - src_fmt = v4l2_subdev_get_try_format(sd, sd_state, - RKISP1_RSZ_PAD_SINK); + src_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SINK); *src_fmt = *sink_fmt; /* NOTE: there is no crop in the source pad, only in the sink */ @@ -444,16 +410,14 @@ static int rkisp1_rsz_init_config(struct v4l2_subdev *sd, static void rkisp1_rsz_set_src_fmt(struct rkisp1_resizer *rsz, struct v4l2_subdev_state *sd_state, - struct v4l2_mbus_framefmt *format, - unsigned int which) + struct v4l2_mbus_framefmt *format) { const struct rkisp1_mbus_info *sink_mbus_info; struct v4l2_mbus_framefmt *src_fmt, *sink_fmt; - sink_fmt = rkisp1_rsz_get_pad_fmt(rsz, sd_state, RKISP1_RSZ_PAD_SINK, - which); - src_fmt = rkisp1_rsz_get_pad_fmt(rsz, sd_state, RKISP1_RSZ_PAD_SRC, - which); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SINK); + src_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SRC); + sink_mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code); /* for YUV formats, userspace can change the mbus code on the src pad if it is supported */ @@ -473,18 +437,14 @@ static void rkisp1_rsz_set_src_fmt(struct rkisp1_resizer *rsz, static void rkisp1_rsz_set_sink_crop(struct rkisp1_resizer *rsz, struct v4l2_subdev_state *sd_state, - struct v4l2_rect *r, - unsigned int which) + struct v4l2_rect *r) { const struct rkisp1_mbus_info *mbus_info; struct v4l2_mbus_framefmt *sink_fmt; struct v4l2_rect *sink_crop; - sink_fmt = rkisp1_rsz_get_pad_fmt(rsz, sd_state, RKISP1_RSZ_PAD_SINK, - which); - sink_crop = rkisp1_rsz_get_pad_crop(rsz, sd_state, - RKISP1_RSZ_PAD_SINK, - which); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SINK); + sink_crop = v4l2_subdev_state_get_crop(sd_state, RKISP1_RSZ_PAD_SINK); /* Not crop for MP bayer raw data */ mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code); @@ -511,21 +471,17 @@ static void rkisp1_rsz_set_sink_crop(struct rkisp1_resizer *rsz, static void rkisp1_rsz_set_sink_fmt(struct rkisp1_resizer *rsz, struct v4l2_subdev_state *sd_state, - struct v4l2_mbus_framefmt *format, - unsigned int which) + struct v4l2_mbus_framefmt *format) { const struct rkisp1_mbus_info *mbus_info; struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; struct v4l2_rect *sink_crop; bool is_yuv; - sink_fmt = rkisp1_rsz_get_pad_fmt(rsz, sd_state, RKISP1_RSZ_PAD_SINK, - which); - src_fmt = rkisp1_rsz_get_pad_fmt(rsz, sd_state, RKISP1_RSZ_PAD_SRC, - which); - sink_crop = rkisp1_rsz_get_pad_crop(rsz, sd_state, - RKISP1_RSZ_PAD_SINK, - which); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SINK); + src_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SRC); + sink_crop = v4l2_subdev_state_get_crop(sd_state, RKISP1_RSZ_PAD_SINK); + if (rsz->id == RKISP1_SELFPATH) sink_fmt->code = MEDIA_BUS_FMT_YUYV8_2X8; else @@ -536,8 +492,6 @@ static void rkisp1_rsz_set_sink_fmt(struct rkisp1_resizer *rsz, sink_fmt->code = RKISP1_DEF_FMT; mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code); } - if (which == V4L2_SUBDEV_FORMAT_ACTIVE) - rsz->pixel_enc = mbus_info->pixel_enc; sink_fmt->width = clamp_t(u32, format->width, RKISP1_ISP_MIN_WIDTH, @@ -586,21 +540,7 @@ static void rkisp1_rsz_set_sink_fmt(struct rkisp1_resizer *rsz, src_fmt->quantization = sink_fmt->quantization; /* Update sink crop */ - rkisp1_rsz_set_sink_crop(rsz, sd_state, sink_crop, which); -} - -static int rkisp1_rsz_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct rkisp1_resizer *rsz = - container_of(sd, struct rkisp1_resizer, sd); - - mutex_lock(&rsz->ops_lock); - fmt->format = *rkisp1_rsz_get_pad_fmt(rsz, sd_state, fmt->pad, - fmt->which); - mutex_unlock(&rsz->ops_lock); - return 0; + rkisp1_rsz_set_sink_crop(rsz, sd_state, sink_crop); } static int rkisp1_rsz_set_fmt(struct v4l2_subdev *sd, @@ -610,15 +550,11 @@ static int rkisp1_rsz_set_fmt(struct v4l2_subdev *sd, struct rkisp1_resizer *rsz = container_of(sd, struct rkisp1_resizer, sd); - mutex_lock(&rsz->ops_lock); if (fmt->pad == RKISP1_RSZ_PAD_SINK) - rkisp1_rsz_set_sink_fmt(rsz, sd_state, &fmt->format, - fmt->which); + rkisp1_rsz_set_sink_fmt(rsz, sd_state, &fmt->format); else - rkisp1_rsz_set_src_fmt(rsz, sd_state, &fmt->format, - fmt->which); + rkisp1_rsz_set_src_fmt(rsz, sd_state, &fmt->format); - mutex_unlock(&rsz->ops_lock); return 0; } @@ -626,35 +562,32 @@ static int rkisp1_rsz_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { - struct rkisp1_resizer *rsz = - container_of(sd, struct rkisp1_resizer, sd); struct v4l2_mbus_framefmt *mf_sink; int ret = 0; if (sel->pad == RKISP1_RSZ_PAD_SRC) return -EINVAL; - mutex_lock(&rsz->ops_lock); switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: - mf_sink = rkisp1_rsz_get_pad_fmt(rsz, sd_state, - RKISP1_RSZ_PAD_SINK, - sel->which); + mf_sink = v4l2_subdev_state_get_format(sd_state, + RKISP1_RSZ_PAD_SINK); sel->r.height = mf_sink->height; sel->r.width = mf_sink->width; sel->r.left = 0; sel->r.top = 0; break; + case V4L2_SEL_TGT_CROP: - sel->r = *rkisp1_rsz_get_pad_crop(rsz, sd_state, - RKISP1_RSZ_PAD_SINK, - sel->which); + sel->r = *v4l2_subdev_state_get_crop(sd_state, + RKISP1_RSZ_PAD_SINK); break; + default: ret = -EINVAL; + break; } - mutex_unlock(&rsz->ops_lock); return ret; } @@ -671,9 +604,7 @@ static int rkisp1_rsz_set_selection(struct v4l2_subdev *sd, dev_dbg(rsz->rkisp1->dev, "%s: pad: %d sel(%d,%d)/%dx%d\n", __func__, sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height); - mutex_lock(&rsz->ops_lock); - rkisp1_rsz_set_sink_crop(rsz, sd_state, &sel->r, sel->which); - mutex_unlock(&rsz->ops_lock); + rkisp1_rsz_set_sink_crop(rsz, sd_state, &sel->r); return 0; } @@ -686,8 +617,7 @@ static const struct v4l2_subdev_pad_ops rkisp1_rsz_pad_ops = { .enum_mbus_code = rkisp1_rsz_enum_mbus_code, .get_selection = rkisp1_rsz_get_selection, .set_selection = rkisp1_rsz_set_selection, - .init_cfg = rkisp1_rsz_init_config, - .get_fmt = rkisp1_rsz_get_fmt, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = rkisp1_rsz_set_fmt, .link_validate = v4l2_subdev_link_validate_default, }; @@ -703,6 +633,7 @@ static int rkisp1_rsz_s_stream(struct v4l2_subdev *sd, int enable) struct rkisp1_device *rkisp1 = rsz->rkisp1; struct rkisp1_capture *other = &rkisp1->capture_devs[rsz->id ^ 1]; enum rkisp1_shadow_regs_when when = RKISP1_SHADOW_REGS_SYNC; + struct v4l2_subdev_state *sd_state; if (!enable) { rkisp1_dcrop_disable(rsz, RKISP1_SHADOW_REGS_ASYNC); @@ -713,11 +644,13 @@ static int rkisp1_rsz_s_stream(struct v4l2_subdev *sd, int enable) if (other->is_streaming) when = RKISP1_SHADOW_REGS_ASYNC; - mutex_lock(&rsz->ops_lock); - rkisp1_rsz_config(rsz, when); - rkisp1_dcrop_config(rsz); + sd_state = v4l2_subdev_lock_and_get_active_state(sd); + + rkisp1_rsz_config(rsz, sd_state, when); + rkisp1_dcrop_config(rsz, sd_state); + + v4l2_subdev_unlock_state(sd_state); - mutex_unlock(&rsz->ops_lock); return 0; } @@ -730,21 +663,22 @@ static const struct v4l2_subdev_ops rkisp1_rsz_ops = { .pad = &rkisp1_rsz_pad_ops, }; +static const struct v4l2_subdev_internal_ops rkisp1_rsz_internal_ops = { + .init_state = rkisp1_rsz_init_state, +}; + static void rkisp1_rsz_unregister(struct rkisp1_resizer *rsz) { if (!rsz->rkisp1) return; v4l2_device_unregister_subdev(&rsz->sd); + v4l2_subdev_cleanup(&rsz->sd); media_entity_cleanup(&rsz->sd.entity); - mutex_destroy(&rsz->ops_lock); } static int rkisp1_rsz_register(struct rkisp1_resizer *rsz) { - struct v4l2_subdev_state state = { - .pads = rsz->pad_cfg, - }; static const char * const dev_names[] = { RKISP1_RSZ_MP_DEV_NAME, RKISP1_RSZ_SP_DEV_NAME @@ -762,6 +696,7 @@ static int rkisp1_rsz_register(struct rkisp1_resizer *rsz) } v4l2_subdev_init(sd, &rkisp1_rsz_ops); + sd->internal_ops = &rkisp1_rsz_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; sd->entity.ops = &rkisp1_rsz_media_ops; sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER; @@ -773,25 +708,26 @@ static int rkisp1_rsz_register(struct rkisp1_resizer *rsz) pads[RKISP1_RSZ_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT; - rsz->pixel_enc = RKISP1_DEF_PIXEL_ENC; - - mutex_init(&rsz->ops_lock); ret = media_entity_pads_init(&sd->entity, RKISP1_RSZ_PAD_MAX, pads); if (ret) - goto error; + goto err_entity_cleanup; + + ret = v4l2_subdev_init_finalize(sd); + if (ret) + goto err_entity_cleanup; ret = v4l2_device_register_subdev(&rsz->rkisp1->v4l2_dev, sd); if (ret) { dev_err(sd->dev, "Failed to register resizer subdev\n"); - goto error; + goto err_subdev_cleanup; } - rkisp1_rsz_init_config(sd, &state); return 0; -error: +err_subdev_cleanup: + v4l2_subdev_cleanup(sd); +err_entity_cleanup: media_entity_cleanup(&sd->entity); - mutex_destroy(&rsz->ops_lock); return ret; } diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-capture.c b/drivers/media/platform/samsung/exynos4-is/fimc-capture.c index a0d43bf892e677..05cafba1c7287a 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-capture.c @@ -1479,7 +1479,7 @@ static int fimc_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *mf; return 0; } @@ -1534,7 +1534,7 @@ static int fimc_subdev_set_fmt(struct v4l2_subdev *sd, mf->colorspace = V4L2_COLORSPACE_JPEG; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); *mf = fmt->format; return 0; } @@ -1604,10 +1604,10 @@ static int fimc_subdev_get_selection(struct v4l2_subdev *sd, return 0; case V4L2_SEL_TGT_CROP: - try_sel = v4l2_subdev_get_try_crop(sd, sd_state, sel->pad); + try_sel = v4l2_subdev_state_get_crop(sd_state, sel->pad); break; case V4L2_SEL_TGT_COMPOSE: - try_sel = v4l2_subdev_get_try_compose(sd, sd_state, sel->pad); + try_sel = v4l2_subdev_state_get_compose(sd_state, sel->pad); f = &ctx->d_frame; break; default: @@ -1651,10 +1651,10 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd, switch (sel->target) { case V4L2_SEL_TGT_CROP: - try_sel = v4l2_subdev_get_try_crop(sd, sd_state, sel->pad); + try_sel = v4l2_subdev_state_get_crop(sd_state, sel->pad); break; case V4L2_SEL_TGT_COMPOSE: - try_sel = v4l2_subdev_get_try_compose(sd, sd_state, sel->pad); + try_sel = v4l2_subdev_state_get_compose(sd_state, sel->pad); f = &ctx->d_frame; break; default: diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-isp.c b/drivers/media/platform/samsung/exynos4-is/fimc-isp.c index b85986e50f4689..3c5d7bee2655b5 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-isp.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-isp.c @@ -126,7 +126,7 @@ static int fimc_isp_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf = &fmt->format; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *mf = *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + *mf = *v4l2_subdev_state_get_format(sd_state, fmt->pad); return 0; } @@ -172,9 +172,8 @@ static void __isp_subdev_try_format(struct fimc_isp *isp, mf->code = MEDIA_BUS_FMT_SGRBG10_1X10; } else { if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - format = v4l2_subdev_get_try_format(&isp->subdev, - sd_state, - FIMC_ISP_SD_PAD_SINK); + format = v4l2_subdev_state_get_format(sd_state, + FIMC_ISP_SD_PAD_SINK); else format = &isp->sink_fmt; @@ -207,7 +206,7 @@ static int fimc_isp_subdev_set_fmt(struct v4l2_subdev *sd, __isp_subdev_try_format(isp, sd_state, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); *mf = fmt->format; /* Propagate format to the source pads */ @@ -220,8 +219,8 @@ static int fimc_isp_subdev_set_fmt(struct v4l2_subdev *sd, format.pad = pad; __isp_subdev_try_format(isp, sd_state, &format); - mf = v4l2_subdev_get_try_format(sd, sd_state, - pad); + mf = v4l2_subdev_state_get_format(sd_state, + pad); *mf = format.format; } } @@ -374,18 +373,17 @@ static int fimc_isp_subdev_open(struct v4l2_subdev *sd, .field = V4L2_FIELD_NONE, }; - format = v4l2_subdev_get_try_format(sd, fh->state, - FIMC_ISP_SD_PAD_SINK); + format = v4l2_subdev_state_get_format(fh->state, FIMC_ISP_SD_PAD_SINK); *format = fmt; - format = v4l2_subdev_get_try_format(sd, fh->state, - FIMC_ISP_SD_PAD_SRC_FIFO); + format = v4l2_subdev_state_get_format(fh->state, + FIMC_ISP_SD_PAD_SRC_FIFO); fmt.width = DEFAULT_PREVIEW_STILL_WIDTH; fmt.height = DEFAULT_PREVIEW_STILL_HEIGHT; *format = fmt; - format = v4l2_subdev_get_try_format(sd, fh->state, - FIMC_ISP_SD_PAD_SRC_DMA); + format = v4l2_subdev_state_get_format(fh->state, + FIMC_ISP_SD_PAD_SRC_DMA); *format = fmt; return 0; diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-lite.c b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c index 9396b10b5b1cc7..7898c9bebb0467 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c @@ -574,16 +574,14 @@ static const struct fimc_fmt *fimc_lite_subdev_try_fmt(struct fimc_lite *fimc, struct v4l2_rect *rect; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - sink_fmt = v4l2_subdev_get_try_format(&fimc->subdev, - sd_state, - FLITE_SD_PAD_SINK); + sink_fmt = v4l2_subdev_state_get_format(sd_state, + FLITE_SD_PAD_SINK); mf->code = sink_fmt->code; mf->colorspace = sink_fmt->colorspace; - rect = v4l2_subdev_get_try_crop(&fimc->subdev, - sd_state, - FLITE_SD_PAD_SINK); + rect = v4l2_subdev_state_get_crop(sd_state, + FLITE_SD_PAD_SINK); } else { mf->code = sink->fmt->mbus_code; mf->colorspace = sink->fmt->colorspace; @@ -1021,7 +1019,7 @@ static struct v4l2_mbus_framefmt *__fimc_lite_subdev_get_try_fmt( if (pad != FLITE_SD_PAD_SINK) pad = FLITE_SD_PAD_SOURCE_DMA; - return v4l2_subdev_get_try_format(sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); } static int fimc_lite_subdev_get_fmt(struct v4l2_subdev *sd, @@ -1129,7 +1127,7 @@ static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd, return -EINVAL; if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - sel->r = *v4l2_subdev_get_try_crop(sd, sd_state, sel->pad); + sel->r = *v4l2_subdev_state_get_crop(sd_state, sel->pad); return 0; } @@ -1166,7 +1164,7 @@ static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd, fimc_lite_try_crop(fimc, &sel->r); if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_crop(sd, sd_state, sel->pad) = sel->r; + *v4l2_subdev_state_get_crop(sd_state, sel->pad) = sel->r; } else { unsigned long flags; spin_lock_irqsave(&fimc->slock, flags); diff --git a/drivers/media/platform/samsung/exynos4-is/mipi-csis.c b/drivers/media/platform/samsung/exynos4-is/mipi-csis.c index 686ca8753ba22a..aae8a8b2c0f4c0 100644 --- a/drivers/media/platform/samsung/exynos4-is/mipi-csis.c +++ b/drivers/media/platform/samsung/exynos4-is/mipi-csis.c @@ -569,8 +569,7 @@ static struct v4l2_mbus_framefmt *__s5pcsis_get_format( enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return sd_state ? v4l2_subdev_get_try_format(&state->sd, - sd_state, 0) : NULL; + return sd_state ? v4l2_subdev_state_get_format(sd_state, 0) : NULL; return &state->format; } diff --git a/drivers/media/platform/samsung/s3c-camif/camif-capture.c b/drivers/media/platform/samsung/s3c-camif/camif-capture.c index 0f5b3845d7b94f..be58260ea67e79 100644 --- a/drivers/media/platform/samsung/s3c-camif/camif-capture.c +++ b/drivers/media/platform/samsung/s3c-camif/camif-capture.c @@ -1216,7 +1216,7 @@ static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf = &fmt->format; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *mf; return 0; } @@ -1305,7 +1305,7 @@ static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd, __camif_subdev_try_format(camif, mf, fmt->pad); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); *mf = fmt->format; mutex_unlock(&camif->lock); return 0; @@ -1357,7 +1357,7 @@ static int s3c_camif_subdev_get_selection(struct v4l2_subdev *sd, return -EINVAL; if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - sel->r = *v4l2_subdev_get_try_crop(sd, sd_state, sel->pad); + sel->r = *v4l2_subdev_state_get_crop(sd_state, sel->pad); return 0; } @@ -1445,7 +1445,7 @@ static int s3c_camif_subdev_set_selection(struct v4l2_subdev *sd, __camif_try_crop(camif, &sel->r); if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_crop(sd, sd_state, sel->pad) = sel->r; + *v4l2_subdev_state_get_crop(sd_state, sel->pad) = sel->r; } else { unsigned long flags; unsigned int i; diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c index aa1df66d5ac62d..dbb26c7b2f8d7a 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c @@ -215,6 +215,7 @@ static int sun4i_csi_probe(struct platform_device *pdev) /* Initialize subdev */ v4l2_subdev_init(subdev, &sun4i_csi_subdev_ops); + subdev->internal_ops = &sun4i_csi_subdev_internal_ops; subdev->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; subdev->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; subdev->entity.ops = &sun4i_csi_subdev_entity_ops; diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h index 8eeed87bfb13be..4e0c2df45d4d2d 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h @@ -89,6 +89,7 @@ enum csi_subdev_pads { }; extern const struct v4l2_subdev_ops sun4i_csi_subdev_ops; +extern const struct v4l2_subdev_internal_ops sun4i_csi_subdev_internal_ops; struct sun4i_csi_format { u32 mbus; diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c index 48702134ccc55e..744197b0fccb03 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c @@ -266,12 +266,12 @@ static const struct v4l2_mbus_framefmt sun4i_csi_pad_fmt_default = { .xfer_func = V4L2_XFER_FUNC_DEFAULT, }; -static int sun4i_csi_subdev_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state) +static int sun4i_csi_subdev_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(subdev, sd_state, CSI_SUBDEV_SINK); + fmt = v4l2_subdev_state_get_format(sd_state, CSI_SUBDEV_SINK); *fmt = sun4i_csi_pad_fmt_default; return 0; @@ -285,8 +285,7 @@ static int sun4i_csi_subdev_get_fmt(struct v4l2_subdev *subdev, struct v4l2_mbus_framefmt *subdev_fmt; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - subdev_fmt = v4l2_subdev_get_try_format(subdev, sd_state, - fmt->pad); + subdev_fmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); else subdev_fmt = &csi->subdev_fmt; @@ -303,8 +302,7 @@ static int sun4i_csi_subdev_set_fmt(struct v4l2_subdev *subdev, struct v4l2_mbus_framefmt *subdev_fmt; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - subdev_fmt = v4l2_subdev_get_try_format(subdev, sd_state, - fmt->pad); + subdev_fmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); else subdev_fmt = &csi->subdev_fmt; @@ -336,7 +334,6 @@ sun4i_csi_subdev_enum_mbus_code(struct v4l2_subdev *subdev, static const struct v4l2_subdev_pad_ops sun4i_csi_subdev_pad_ops = { .link_validate = v4l2_subdev_link_validate_default, - .init_cfg = sun4i_csi_subdev_init_cfg, .get_fmt = sun4i_csi_subdev_get_fmt, .set_fmt = sun4i_csi_subdev_set_fmt, .enum_mbus_code = sun4i_csi_subdev_enum_mbus_code, @@ -346,6 +343,10 @@ const struct v4l2_subdev_ops sun4i_csi_subdev_ops = { .pad = &sun4i_csi_subdev_pad_ops, }; +const struct v4l2_subdev_internal_ops sun4i_csi_subdev_internal_ops = { + .init_state = sun4i_csi_subdev_init_state, +}; + int sun4i_csi_v4l2_register(struct sun4i_csi *csi) { struct video_device *vdev = &csi->vdev; diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c index e573413123b955..d006d9dd017081 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c @@ -501,13 +501,13 @@ sun6i_csi_bridge_mbus_format_prepare(struct v4l2_mbus_framefmt *mbus_format) mbus_format->xfer_func = V4L2_XFER_FUNC_DEFAULT; } -static int sun6i_csi_bridge_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *state) +static int sun6i_csi_bridge_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state) { struct sun6i_csi_device *csi_dev = v4l2_get_subdevdata(subdev); unsigned int pad = SUN6I_CSI_BRIDGE_PAD_SINK; struct v4l2_mbus_framefmt *mbus_format = - v4l2_subdev_get_try_format(subdev, state, pad); + v4l2_subdev_state_get_format(state, pad); struct mutex *lock = &csi_dev->bridge.lock; mutex_lock(lock); @@ -547,8 +547,8 @@ static int sun6i_csi_bridge_get_fmt(struct v4l2_subdev *subdev, mutex_lock(lock); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *mbus_format = *v4l2_subdev_get_try_format(subdev, state, - format->pad); + *mbus_format = *v4l2_subdev_state_get_format(state, + format->pad); else *mbus_format = csi_dev->bridge.mbus_format; @@ -570,7 +570,7 @@ static int sun6i_csi_bridge_set_fmt(struct v4l2_subdev *subdev, sun6i_csi_bridge_mbus_format_prepare(mbus_format); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *v4l2_subdev_get_try_format(subdev, state, format->pad) = + *v4l2_subdev_state_get_format(state, format->pad) = *mbus_format; else csi_dev->bridge.mbus_format = *mbus_format; @@ -581,7 +581,6 @@ static int sun6i_csi_bridge_set_fmt(struct v4l2_subdev *subdev, } static const struct v4l2_subdev_pad_ops sun6i_csi_bridge_pad_ops = { - .init_cfg = sun6i_csi_bridge_init_cfg, .enum_mbus_code = sun6i_csi_bridge_enum_mbus_code, .get_fmt = sun6i_csi_bridge_get_fmt, .set_fmt = sun6i_csi_bridge_set_fmt, @@ -592,6 +591,10 @@ static const struct v4l2_subdev_ops sun6i_csi_bridge_subdev_ops = { .pad = &sun6i_csi_bridge_pad_ops, }; +static const struct v4l2_subdev_internal_ops sun6i_csi_bridge_internal_ops = { + .init_state = sun6i_csi_bridge_init_state, +}; + /* Media Entity */ static const struct media_entity_operations sun6i_csi_bridge_entity_ops = { @@ -782,6 +785,7 @@ int sun6i_csi_bridge_setup(struct sun6i_csi_device *csi_dev) /* V4L2 Subdev */ v4l2_subdev_init(subdev, &sun6i_csi_bridge_subdev_ops); + subdev->internal_ops = &sun6i_csi_bridge_internal_ops; strscpy(subdev->name, SUN6I_CSI_BRIDGE_NAME, sizeof(subdev->name)); subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; subdev->owner = THIS_MODULE; diff --git a/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c b/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c index 08d86c17b284a5..f9d4dc45b49077 100644 --- a/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c +++ b/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c @@ -305,13 +305,13 @@ sun6i_mipi_csi2_mbus_format_prepare(struct v4l2_mbus_framefmt *mbus_format) mbus_format->xfer_func = V4L2_XFER_FUNC_DEFAULT; } -static int sun6i_mipi_csi2_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *state) +static int sun6i_mipi_csi2_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state) { struct sun6i_mipi_csi2_device *csi2_dev = v4l2_get_subdevdata(subdev); unsigned int pad = SUN6I_MIPI_CSI2_PAD_SINK; struct v4l2_mbus_framefmt *mbus_format = - v4l2_subdev_get_try_format(subdev, state, pad); + v4l2_subdev_state_get_format(state, pad); struct mutex *lock = &csi2_dev->bridge.lock; mutex_lock(lock); @@ -351,8 +351,8 @@ static int sun6i_mipi_csi2_get_fmt(struct v4l2_subdev *subdev, mutex_lock(lock); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *mbus_format = *v4l2_subdev_get_try_format(subdev, state, - format->pad); + *mbus_format = *v4l2_subdev_state_get_format(state, + format->pad); else *mbus_format = csi2_dev->bridge.mbus_format; @@ -374,7 +374,7 @@ static int sun6i_mipi_csi2_set_fmt(struct v4l2_subdev *subdev, sun6i_mipi_csi2_mbus_format_prepare(mbus_format); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *v4l2_subdev_get_try_format(subdev, state, format->pad) = + *v4l2_subdev_state_get_format(state, format->pad) = *mbus_format; else csi2_dev->bridge.mbus_format = *mbus_format; @@ -385,7 +385,6 @@ static int sun6i_mipi_csi2_set_fmt(struct v4l2_subdev *subdev, } static const struct v4l2_subdev_pad_ops sun6i_mipi_csi2_pad_ops = { - .init_cfg = sun6i_mipi_csi2_init_cfg, .enum_mbus_code = sun6i_mipi_csi2_enum_mbus_code, .get_fmt = sun6i_mipi_csi2_get_fmt, .set_fmt = sun6i_mipi_csi2_set_fmt, @@ -396,6 +395,10 @@ static const struct v4l2_subdev_ops sun6i_mipi_csi2_subdev_ops = { .pad = &sun6i_mipi_csi2_pad_ops, }; +static const struct v4l2_subdev_internal_ops sun6i_mipi_csi2_internal_ops = { + .init_state = sun6i_mipi_csi2_init_state, +}; + /* Media Entity */ static const struct media_entity_operations sun6i_mipi_csi2_entity_ops = { @@ -504,6 +507,7 @@ static int sun6i_mipi_csi2_bridge_setup(struct sun6i_mipi_csi2_device *csi2_dev) /* V4L2 Subdev */ v4l2_subdev_init(subdev, &sun6i_mipi_csi2_subdev_ops); + subdev->internal_ops = &sun6i_mipi_csi2_internal_ops; strscpy(subdev->name, SUN6I_MIPI_CSI2_NAME, sizeof(subdev->name)); subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; subdev->owner = THIS_MODULE; diff --git a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c index 14a1844812c0e2..4a5698eb12b77c 100644 --- a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c +++ b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c @@ -338,14 +338,14 @@ sun8i_a83t_mipi_csi2_mbus_format_prepare(struct v4l2_mbus_framefmt *mbus_format) mbus_format->xfer_func = V4L2_XFER_FUNC_DEFAULT; } -static int sun8i_a83t_mipi_csi2_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *state) +static int sun8i_a83t_mipi_csi2_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state) { struct sun8i_a83t_mipi_csi2_device *csi2_dev = v4l2_get_subdevdata(subdev); unsigned int pad = SUN8I_A83T_MIPI_CSI2_PAD_SINK; struct v4l2_mbus_framefmt *mbus_format = - v4l2_subdev_get_try_format(subdev, state, pad); + v4l2_subdev_state_get_format(state, pad); struct mutex *lock = &csi2_dev->bridge.lock; mutex_lock(lock); @@ -387,8 +387,8 @@ static int sun8i_a83t_mipi_csi2_get_fmt(struct v4l2_subdev *subdev, mutex_lock(lock); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *mbus_format = *v4l2_subdev_get_try_format(subdev, state, - format->pad); + *mbus_format = *v4l2_subdev_state_get_format(state, + format->pad); else *mbus_format = csi2_dev->bridge.mbus_format; @@ -411,7 +411,7 @@ static int sun8i_a83t_mipi_csi2_set_fmt(struct v4l2_subdev *subdev, sun8i_a83t_mipi_csi2_mbus_format_prepare(mbus_format); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *v4l2_subdev_get_try_format(subdev, state, format->pad) = + *v4l2_subdev_state_get_format(state, format->pad) = *mbus_format; else csi2_dev->bridge.mbus_format = *mbus_format; @@ -422,7 +422,6 @@ static int sun8i_a83t_mipi_csi2_set_fmt(struct v4l2_subdev *subdev, } static const struct v4l2_subdev_pad_ops sun8i_a83t_mipi_csi2_pad_ops = { - .init_cfg = sun8i_a83t_mipi_csi2_init_cfg, .enum_mbus_code = sun8i_a83t_mipi_csi2_enum_mbus_code, .get_fmt = sun8i_a83t_mipi_csi2_get_fmt, .set_fmt = sun8i_a83t_mipi_csi2_set_fmt, @@ -433,6 +432,10 @@ static const struct v4l2_subdev_ops sun8i_a83t_mipi_csi2_subdev_ops = { .pad = &sun8i_a83t_mipi_csi2_pad_ops, }; +static const struct v4l2_subdev_internal_ops sun8i_a83t_mipi_csi2_internal_ops = { + .init_state = sun8i_a83t_mipi_csi2_init_state, +}; + /* Media Entity */ static const struct media_entity_operations sun8i_a83t_mipi_csi2_entity_ops = { @@ -542,6 +545,7 @@ sun8i_a83t_mipi_csi2_bridge_setup(struct sun8i_a83t_mipi_csi2_device *csi2_dev) /* V4L2 Subdev */ v4l2_subdev_init(subdev, &sun8i_a83t_mipi_csi2_subdev_ops); + subdev->internal_ops = &sun8i_a83t_mipi_csi2_internal_ops; strscpy(subdev->name, SUN8I_A83T_MIPI_CSI2_NAME, sizeof(subdev->name)); subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; subdev->owner = THIS_MODULE; diff --git a/drivers/media/platform/ti/cal/cal-camerarx.c b/drivers/media/platform/ti/cal/cal-camerarx.c index 1a4273bbe75255..4afc2ad0033039 100644 --- a/drivers/media/platform/ti/cal/cal-camerarx.c +++ b/drivers/media/platform/ti/cal/cal-camerarx.c @@ -57,7 +57,7 @@ static s64 cal_camerarx_get_ext_link_freq(struct cal_camerarx *phy) state = v4l2_subdev_get_locked_active_state(&phy->subdev); - fmt = v4l2_subdev_get_pad_format(&phy->subdev, state, CAL_CAMERARX_PAD_SINK); + fmt = v4l2_subdev_state_get_format(state, CAL_CAMERARX_PAD_SINK); fmtinfo = cal_format_by_code(fmt->code); if (!fmtinfo) @@ -621,8 +621,6 @@ static int cal_camerarx_sd_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_subdev_mbus_code_enum *code) { - struct cal_camerarx *phy = to_cal_camerarx(sd); - /* No transcoding, source and sink codes must match. */ if (cal_rx_pad_is_source(code->pad)) { struct v4l2_mbus_framefmt *fmt; @@ -630,8 +628,8 @@ static int cal_camerarx_sd_enum_mbus_code(struct v4l2_subdev *sd, if (code->index > 0) return -EINVAL; - fmt = v4l2_subdev_get_pad_format(&phy->subdev, state, - CAL_CAMERARX_PAD_SINK); + fmt = v4l2_subdev_state_get_format(state, + CAL_CAMERARX_PAD_SINK); code->code = fmt->code; } else { if (code->index >= cal_num_formats) @@ -656,8 +654,8 @@ static int cal_camerarx_sd_enum_frame_size(struct v4l2_subdev *sd, if (cal_rx_pad_is_source(fse->pad)) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_pad_format(sd, state, - CAL_CAMERARX_PAD_SINK); + fmt = v4l2_subdev_state_get_format(state, + CAL_CAMERARX_PAD_SINK); if (fse->code != fmt->code) return -EINVAL; @@ -713,18 +711,18 @@ static int cal_camerarx_sd_set_fmt(struct v4l2_subdev *sd, /* Store the format and propagate it to the source pad. */ - fmt = v4l2_subdev_get_pad_format(sd, state, CAL_CAMERARX_PAD_SINK); + fmt = v4l2_subdev_state_get_format(state, CAL_CAMERARX_PAD_SINK); *fmt = format->format; - fmt = v4l2_subdev_get_pad_format(sd, state, - CAL_CAMERARX_PAD_FIRST_SOURCE); + fmt = v4l2_subdev_state_get_format(state, + CAL_CAMERARX_PAD_FIRST_SOURCE); *fmt = format->format; return 0; } -static int cal_camerarx_sd_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int cal_camerarx_sd_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct v4l2_subdev_format format = { .which = state ? V4L2_SUBDEV_FORMAT_TRY @@ -784,7 +782,6 @@ static const struct v4l2_subdev_video_ops cal_camerarx_video_ops = { }; static const struct v4l2_subdev_pad_ops cal_camerarx_pad_ops = { - .init_cfg = cal_camerarx_sd_init_cfg, .enum_mbus_code = cal_camerarx_sd_enum_mbus_code, .enum_frame_size = cal_camerarx_sd_enum_frame_size, .get_fmt = v4l2_subdev_get_fmt, @@ -797,6 +794,10 @@ static const struct v4l2_subdev_ops cal_camerarx_subdev_ops = { .pad = &cal_camerarx_pad_ops, }; +static const struct v4l2_subdev_internal_ops cal_camerarx_internal_ops = { + .init_state = cal_camerarx_sd_init_state, +}; + static struct media_entity_operations cal_camerarx_media_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -848,6 +849,7 @@ struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, /* Initialize the V4L2 subdev and media entity. */ sd = &phy->subdev; v4l2_subdev_init(sd, &cal_camerarx_subdev_ops); + sd->internal_ops = &cal_camerarx_internal_ops; sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; snprintf(sd->name, sizeof(sd->name), "CAMERARX%u", instance); diff --git a/drivers/media/platform/ti/cal/cal-video.c b/drivers/media/platform/ti/cal/cal-video.c index a8abcd0fee17e5..da4dcfc829b7cc 100644 --- a/drivers/media/platform/ti/cal/cal-video.c +++ b/drivers/media/platform/ti/cal/cal-video.c @@ -697,7 +697,7 @@ static int cal_video_check_format(struct cal_ctx *ctx) state = v4l2_subdev_lock_and_get_active_state(&ctx->phy->subdev); - format = v4l2_subdev_get_pad_format(&ctx->phy->subdev, state, remote_pad->index); + format = v4l2_subdev_state_get_format(state, remote_pad->index); if (!format) { ret = -EINVAL; goto out; diff --git a/drivers/media/platform/ti/omap3isp/ispccdc.c b/drivers/media/platform/ti/omap3isp/ispccdc.c index 2fe42aa9180049..7f46d0cdde6e77 100644 --- a/drivers/media/platform/ti/omap3isp/ispccdc.c +++ b/drivers/media/platform/ti/omap3isp/ispccdc.c @@ -1948,8 +1948,7 @@ __ccdc_get_format(struct isp_ccdc_device *ccdc, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&ccdc->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &ccdc->formats[pad]; } @@ -1960,8 +1959,8 @@ __ccdc_get_crop(struct isp_ccdc_device *ccdc, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&ccdc->subdev, sd_state, - CCDC_PAD_SOURCE_OF); + return v4l2_subdev_state_get_crop(sd_state, + CCDC_PAD_SOURCE_OF); else return &ccdc->crop; } diff --git a/drivers/media/platform/ti/omap3isp/ispccp2.c b/drivers/media/platform/ti/omap3isp/ispccp2.c index da5f0176ec7890..0517d410b22cfa 100644 --- a/drivers/media/platform/ti/omap3isp/ispccp2.c +++ b/drivers/media/platform/ti/omap3isp/ispccp2.c @@ -625,8 +625,7 @@ __ccp2_get_format(struct isp_ccp2_device *ccp2, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&ccp2->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &ccp2->formats[pad]; } diff --git a/drivers/media/platform/ti/omap3isp/ispcsi2.c b/drivers/media/platform/ti/omap3isp/ispcsi2.c index 0f9a54b11f983e..21270fc4e62365 100644 --- a/drivers/media/platform/ti/omap3isp/ispcsi2.c +++ b/drivers/media/platform/ti/omap3isp/ispcsi2.c @@ -834,8 +834,7 @@ __csi2_get_format(struct isp_csi2_device *csi2, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&csi2->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &csi2->formats[pad]; } diff --git a/drivers/media/platform/ti/omap3isp/isppreview.c b/drivers/media/platform/ti/omap3isp/isppreview.c index 53aedec7990da1..fe550021d930af 100644 --- a/drivers/media/platform/ti/omap3isp/isppreview.c +++ b/drivers/media/platform/ti/omap3isp/isppreview.c @@ -1684,8 +1684,7 @@ __preview_get_format(struct isp_prev_device *prev, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&prev->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &prev->formats[pad]; } @@ -1696,8 +1695,7 @@ __preview_get_crop(struct isp_prev_device *prev, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&prev->subdev, sd_state, - PREV_PAD_SINK); + return v4l2_subdev_state_get_crop(sd_state, PREV_PAD_SINK); else return &prev->crop; } diff --git a/drivers/media/platform/ti/omap3isp/ispresizer.c b/drivers/media/platform/ti/omap3isp/ispresizer.c index ed2fb0c7a57e50..bbcb27508fab2a 100644 --- a/drivers/media/platform/ti/omap3isp/ispresizer.c +++ b/drivers/media/platform/ti/omap3isp/ispresizer.c @@ -119,7 +119,7 @@ __resizer_get_format(struct isp_res_device *res, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&res->subdev, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &res->formats[pad]; } @@ -136,8 +136,7 @@ __resizer_get_crop(struct isp_res_device *res, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&res->subdev, sd_state, - RESZ_PAD_SINK); + return v4l2_subdev_state_get_crop(sd_state, RESZ_PAD_SINK); else return &res->crop.request; } diff --git a/drivers/media/platform/video-mux.c b/drivers/media/platform/video-mux.c index ea6d094e0844eb..f33b4548b88491 100644 --- a/drivers/media/platform/video-mux.c +++ b/drivers/media/platform/video-mux.c @@ -107,9 +107,9 @@ static int video_mux_link_setup(struct media_entity *entity, vmux->active = local->index; /* Propagate the active format to the source */ - source_mbusformat = v4l2_subdev_get_pad_format(sd, sd_state, + source_mbusformat = v4l2_subdev_state_get_format(sd_state, source_pad); - *source_mbusformat = *v4l2_subdev_get_pad_format(sd, sd_state, + *source_mbusformat = *v4l2_subdev_state_get_format(sd_state, vmux->active); source_sd = media_entity_to_v4l2_subdev(remote->entity); @@ -178,11 +178,11 @@ static int video_mux_set_format(struct v4l2_subdev *sd, struct media_pad *pad = &vmux->pads[sdformat->pad]; u16 source_pad = sd->entity.num_pads - 1; - mbusformat = v4l2_subdev_get_pad_format(sd, sd_state, sdformat->pad); + mbusformat = v4l2_subdev_state_get_format(sd_state, sdformat->pad); if (!mbusformat) return -EINVAL; - source_mbusformat = v4l2_subdev_get_pad_format(sd, sd_state, source_pad); + source_mbusformat = v4l2_subdev_state_get_format(sd_state, source_pad); if (!source_mbusformat) return -EINVAL; @@ -292,7 +292,7 @@ static int video_mux_set_format(struct v4l2_subdev *sd, /* Source pad mirrors active sink pad, no limitations on sink pads */ if ((pad->flags & MEDIA_PAD_FL_SOURCE) && vmux->active >= 0) - sdformat->format = *v4l2_subdev_get_pad_format(sd, sd_state, + sdformat->format = *v4l2_subdev_state_get_format(sd_state, vmux->active); *mbusformat = sdformat->format; @@ -306,8 +306,8 @@ static int video_mux_set_format(struct v4l2_subdev *sd, return 0; } -static int video_mux_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int video_mux_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct video_mux *vmux = v4l2_subdev_to_video_mux(sd); struct v4l2_mbus_framefmt *mbusformat; @@ -316,7 +316,7 @@ static int video_mux_init_cfg(struct v4l2_subdev *sd, mutex_lock(&vmux->lock); for (i = 0; i < sd->entity.num_pads; i++) { - mbusformat = v4l2_subdev_get_pad_format(sd, sd_state, i); + mbusformat = v4l2_subdev_state_get_format(sd_state, i); *mbusformat = video_mux_format_mbus_default; } @@ -349,7 +349,6 @@ static int video_mux_get_mbus_config(struct v4l2_subdev *sd, }; static const struct v4l2_subdev_pad_ops video_mux_pad_ops = { - .init_cfg = video_mux_init_cfg, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = video_mux_set_format, .get_mbus_config = video_mux_get_mbus_config, @@ -360,6 +359,10 @@ static const struct v4l2_subdev_ops video_mux_subdev_ops = { .video = &video_mux_subdev_video_ops, }; +static const struct v4l2_subdev_internal_ops video_mux_internal_ops = { + .init_state = video_mux_init_state, +}; + static int video_mux_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd, struct v4l2_async_connection *asd) @@ -455,6 +458,7 @@ static int video_mux_probe(struct platform_device *pdev) platform_set_drvdata(pdev, vmux); v4l2_subdev_init(&vmux->subdev, &video_mux_subdev_ops); + vmux->subdev.internal_ops = &video_mux_internal_ops; snprintf(vmux->subdev.name, sizeof(vmux->subdev.name), "%pOFn", np); vmux->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; vmux->subdev.dev = dev; diff --git a/drivers/media/platform/xilinx/xilinx-csi2rxss.c b/drivers/media/platform/xilinx/xilinx-csi2rxss.c index 5b53745fe44eb8..1ced0c1b5de3eb 100644 --- a/drivers/media/platform/xilinx/xilinx-csi2rxss.c +++ b/drivers/media/platform/xilinx/xilinx-csi2rxss.c @@ -671,8 +671,7 @@ __xcsi2rxss_get_pad_format(struct xcsi2rxss_state *xcsi2rxss, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&xcsi2rxss->subdev, - sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &xcsi2rxss->format; default: @@ -680,18 +679,8 @@ __xcsi2rxss_get_pad_format(struct xcsi2rxss_state *xcsi2rxss, } } -/** - * xcsi2rxss_init_cfg - Initialise the pad format config to default - * @sd: Pointer to V4L2 Sub device structure - * @sd_state: Pointer to sub device state structure - * - * This function is used to initialize the pad format with the default - * values. - * - * Return: 0 on success - */ -static int xcsi2rxss_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int xcsi2rxss_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); struct v4l2_mbus_framefmt *format; @@ -699,7 +688,7 @@ static int xcsi2rxss_init_cfg(struct v4l2_subdev *sd, mutex_lock(&xcsi2rxss->lock); for (i = 0; i < XCSI_MEDIA_PADS; i++) { - format = v4l2_subdev_get_try_format(sd, sd_state, i); + format = v4l2_subdev_state_get_format(sd_state, i); *format = xcsi2rxss->default_format; } mutex_unlock(&xcsi2rxss->lock); @@ -840,7 +829,6 @@ static const struct v4l2_subdev_video_ops xcsi2rxss_video_ops = { }; static const struct v4l2_subdev_pad_ops xcsi2rxss_pad_ops = { - .init_cfg = xcsi2rxss_init_cfg, .get_fmt = xcsi2rxss_get_format, .set_fmt = xcsi2rxss_set_format, .enum_mbus_code = xcsi2rxss_enum_mbus_code, @@ -853,6 +841,10 @@ static const struct v4l2_subdev_ops xcsi2rxss_ops = { .pad = &xcsi2rxss_pad_ops }; +static const struct v4l2_subdev_internal_ops xcsi2rxss_internal_ops = { + .init_state = xcsi2rxss_init_state, +}; + static int xcsi2rxss_parse_of(struct xcsi2rxss_state *xcsi2rxss) { struct device *dev = xcsi2rxss->dev; @@ -1030,6 +1022,7 @@ static int xcsi2rxss_probe(struct platform_device *pdev) /* Initialize V4L2 subdevice and media entity */ subdev = &xcsi2rxss->subdev; v4l2_subdev_init(subdev, &xcsi2rxss_ops); + subdev->internal_ops = &xcsi2rxss_internal_ops; subdev->dev = dev; strscpy(subdev->name, dev_name(dev), sizeof(subdev->name)); subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; diff --git a/drivers/media/platform/xilinx/xilinx-tpg.c b/drivers/media/platform/xilinx/xilinx-tpg.c index 80353ca44402a5..e05e528ffc6f72 100644 --- a/drivers/media/platform/xilinx/xilinx-tpg.c +++ b/drivers/media/platform/xilinx/xilinx-tpg.c @@ -256,8 +256,7 @@ __xtpg_get_pad_format(struct xtpg_device *xtpg, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&xtpg->xvip.subdev, - sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &xtpg->formats[pad]; default: @@ -326,7 +325,7 @@ static int xtpg_enum_frame_size(struct v4l2_subdev *subdev, { struct v4l2_mbus_framefmt *format; - format = v4l2_subdev_get_try_format(subdev, sd_state, fse->pad); + format = v4l2_subdev_state_get_format(sd_state, fse->pad); if (fse->index || fse->code != format->code) return -EINVAL; @@ -354,11 +353,11 @@ static int xtpg_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) struct xtpg_device *xtpg = to_tpg(subdev); struct v4l2_mbus_framefmt *format; - format = v4l2_subdev_get_try_format(subdev, fh->state, 0); + format = v4l2_subdev_state_get_format(fh->state, 0); *format = xtpg->default_format; if (xtpg->npads == 2) { - format = v4l2_subdev_get_try_format(subdev, fh->state, 1); + format = v4l2_subdev_state_get_format(fh->state, 1); *format = xtpg->default_format; } diff --git a/drivers/media/platform/xilinx/xilinx-vip.c b/drivers/media/platform/xilinx/xilinx-vip.c index 5b214bf7f93a47..f1574edd2b43cd 100644 --- a/drivers/media/platform/xilinx/xilinx-vip.c +++ b/drivers/media/platform/xilinx/xilinx-vip.c @@ -260,7 +260,7 @@ int xvip_enum_mbus_code(struct v4l2_subdev *subdev, if (code->index) return -EINVAL; - format = v4l2_subdev_get_try_format(subdev, sd_state, code->pad); + format = v4l2_subdev_state_get_format(sd_state, code->pad); code->code = format->code; @@ -295,7 +295,7 @@ int xvip_enum_frame_size(struct v4l2_subdev *subdev, if (fse->which == V4L2_SUBDEV_FORMAT_ACTIVE) return -EINVAL; - format = v4l2_subdev_get_try_format(subdev, sd_state, fse->pad); + format = v4l2_subdev_state_get_format(sd_state, fse->pad); if (fse->index || fse->code != format->code) return -EINVAL; diff --git a/drivers/media/test-drivers/vimc/vimc-debayer.c b/drivers/media/test-drivers/vimc/vimc-debayer.c index f671251fdf0e4a..d72ed086e00bb4 100644 --- a/drivers/media/test-drivers/vimc/vimc-debayer.c +++ b/drivers/media/test-drivers/vimc/vimc-debayer.c @@ -150,18 +150,18 @@ static bool vimc_debayer_src_code_is_valid(u32 code) return false; } -static int vimc_debayer_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int vimc_debayer_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct vimc_debayer_device *vdebayer = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *mf; unsigned int i; - mf = v4l2_subdev_get_try_format(sd, sd_state, 0); + mf = v4l2_subdev_state_get_format(sd_state, 0); *mf = sink_fmt_default; for (i = 1; i < sd->entity.num_pads; i++) { - mf = v4l2_subdev_get_try_format(sd, sd_state, i); + mf = v4l2_subdev_state_get_format(sd_state, i); *mf = sink_fmt_default; mf->code = vdebayer->src_code; } @@ -221,7 +221,7 @@ static int vimc_debayer_get_fmt(struct v4l2_subdev *sd, /* Get the current sink format */ fmt->format = fmt->which == V4L2_SUBDEV_FORMAT_TRY ? - *v4l2_subdev_get_try_format(sd, sd_state, 0) : + *v4l2_subdev_state_get_format(sd_state, 0) : vdebayer->sink_fmt; /* Set the right code for the source pad */ @@ -267,8 +267,8 @@ static int vimc_debayer_set_fmt(struct v4l2_subdev *sd, sink_fmt = &vdebayer->sink_fmt; src_code = &vdebayer->src_code; } else { - sink_fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); - src_code = &v4l2_subdev_get_try_format(sd, sd_state, 1)->code; + sink_fmt = v4l2_subdev_state_get_format(sd_state, 0); + src_code = &v4l2_subdev_state_get_format(sd_state, 1)->code; } /* @@ -307,7 +307,6 @@ static int vimc_debayer_set_fmt(struct v4l2_subdev *sd, } static const struct v4l2_subdev_pad_ops vimc_debayer_pad_ops = { - .init_cfg = vimc_debayer_init_cfg, .enum_mbus_code = vimc_debayer_enum_mbus_code, .enum_frame_size = vimc_debayer_enum_frame_size, .get_fmt = vimc_debayer_get_fmt, @@ -395,6 +394,10 @@ static const struct v4l2_subdev_ops vimc_debayer_ops = { .video = &vimc_debayer_video_ops, }; +static const struct v4l2_subdev_internal_ops vimc_debayer_internal_ops = { + .init_state = vimc_debayer_init_state, +}; + static unsigned int vimc_debayer_get_val(const u8 *bytes, const unsigned int n_bytes) { @@ -595,6 +598,8 @@ static struct vimc_ent_device *vimc_debayer_add(struct vimc_device *vimc, if (ret) goto err_free_hdl; + vdebayer->sd.internal_ops = &vimc_debayer_internal_ops; + vdebayer->ved.process_frame = vimc_debayer_process_frame; vdebayer->ved.dev = vimc->mdev.dev; vdebayer->mean_win_size = vimc_debayer_ctrl_mean_win_size.def; diff --git a/drivers/media/test-drivers/vimc/vimc-scaler.c b/drivers/media/test-drivers/vimc/vimc-scaler.c index b671774e2784ab..afe13d6af321b3 100644 --- a/drivers/media/test-drivers/vimc/vimc-scaler.c +++ b/drivers/media/test-drivers/vimc/vimc-scaler.c @@ -70,19 +70,19 @@ vimc_scaler_get_crop_bound_sink(const struct v4l2_mbus_framefmt *sink_fmt) return r; } -static int vimc_scaler_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int vimc_scaler_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *mf; struct v4l2_rect *r; unsigned int i; for (i = 0; i < sd->entity.num_pads; i++) { - mf = v4l2_subdev_get_try_format(sd, sd_state, i); + mf = v4l2_subdev_state_get_format(sd_state, i); *mf = fmt_default; } - r = v4l2_subdev_get_try_crop(sd, sd_state, VIMC_SCALER_SINK); + r = v4l2_subdev_state_get_crop(sd_state, VIMC_SCALER_SINK); *r = crop_rect_default; return 0; @@ -138,7 +138,7 @@ vimc_scaler_pad_format(struct vimc_scaler_device *vscaler, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&vscaler->sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &vscaler->fmt[pad]; } @@ -149,8 +149,7 @@ vimc_scaler_pad_crop(struct vimc_scaler_device *vscaler, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&vscaler->sd, sd_state, - VIMC_SCALER_SINK); + return v4l2_subdev_state_get_crop(sd_state, VIMC_SCALER_SINK); else return &vscaler->crop_rect; } @@ -293,7 +292,6 @@ static int vimc_scaler_set_selection(struct v4l2_subdev *sd, } static const struct v4l2_subdev_pad_ops vimc_scaler_pad_ops = { - .init_cfg = vimc_scaler_init_cfg, .enum_mbus_code = vimc_scaler_enum_mbus_code, .enum_frame_size = vimc_scaler_enum_frame_size, .get_fmt = vimc_scaler_get_fmt, @@ -348,6 +346,10 @@ static const struct v4l2_subdev_ops vimc_scaler_ops = { .video = &vimc_scaler_video_ops, }; +static const struct v4l2_subdev_internal_ops vimc_scaler_internal_ops = { + .init_state = vimc_scaler_init_state, +}; + static void vimc_scaler_fill_src_frame(const struct vimc_scaler_device *const vscaler, const u8 *const sink_frame) { @@ -425,6 +427,8 @@ static struct vimc_ent_device *vimc_scaler_add(struct vimc_device *vimc, return ERR_PTR(ret); } + vscaler->sd.internal_ops = &vimc_scaler_internal_ops; + vscaler->ved.process_frame = vimc_scaler_process_frame; vscaler->ved.dev = vimc->mdev.dev; diff --git a/drivers/media/test-drivers/vimc/vimc-sensor.c b/drivers/media/test-drivers/vimc/vimc-sensor.c index 41a3dce2d71471..5e34b1aed95e86 100644 --- a/drivers/media/test-drivers/vimc/vimc-sensor.c +++ b/drivers/media/test-drivers/vimc/vimc-sensor.c @@ -41,15 +41,15 @@ static const struct v4l2_mbus_framefmt fmt_default = { .colorspace = V4L2_COLORSPACE_SRGB, }; -static int vimc_sensor_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int vimc_sensor_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { unsigned int i; for (i = 0; i < sd->entity.num_pads; i++) { struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_get_try_format(sd, sd_state, i); + mf = v4l2_subdev_state_get_format(sd_state, i); *mf = fmt_default; } @@ -100,7 +100,7 @@ static int vimc_sensor_get_fmt(struct v4l2_subdev *sd, container_of(sd, struct vimc_sensor_device, sd); fmt->format = fmt->which == V4L2_SUBDEV_FORMAT_TRY ? - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) : + *v4l2_subdev_state_get_format(sd_state, fmt->pad) : vsensor->mbus_format; return 0; @@ -159,7 +159,7 @@ static int vimc_sensor_set_fmt(struct v4l2_subdev *sd, mf = &vsensor->mbus_format; } else { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); } /* Set the new format */ @@ -183,7 +183,6 @@ static int vimc_sensor_set_fmt(struct v4l2_subdev *sd, } static const struct v4l2_subdev_pad_ops vimc_sensor_pad_ops = { - .init_cfg = vimc_sensor_init_cfg, .enum_mbus_code = vimc_sensor_enum_mbus_code, .enum_frame_size = vimc_sensor_enum_frame_size, .get_fmt = vimc_sensor_get_fmt, @@ -294,6 +293,10 @@ static const struct v4l2_subdev_ops vimc_sensor_ops = { .video = &vimc_sensor_video_ops, }; +static const struct v4l2_subdev_internal_ops vimc_sensor_internal_ops = { + .init_state = vimc_sensor_init_state, +}; + static int vimc_sensor_s_ctrl(struct v4l2_ctrl *ctrl) { struct vimc_sensor_device *vsensor = @@ -429,6 +432,8 @@ static struct vimc_ent_device *vimc_sensor_add(struct vimc_device *vimc, if (ret) goto err_free_tpg; + vsensor->sd.internal_ops = &vimc_sensor_internal_ops; + vsensor->ved.process_frame = vimc_sensor_process_frame; vsensor->ved.dev = vimc->mdev.dev; diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 0ba33c03bb21dd..4be767c129af84 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -343,8 +343,9 @@ static void v4l_print_format(const void *arg, bool write_only) case V4L2_BUF_TYPE_META_OUTPUT: meta = &p->fmt.meta; pixelformat = meta->dataformat; - pr_cont(", dataformat=%p4cc, buffersize=%u\n", - &pixelformat, meta->buffersize); + pr_cont(", dataformat=%p4cc, buffersize=%u, width=%u, height=%u, bytesperline=%u\n", + &pixelformat, meta->buffersize, meta->width, + meta->height, meta->bytesperline); break; } } @@ -1463,6 +1464,13 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_META_FMT_RPI_BE_CFG: descr = "PiSP BE Config format"; break; case V4L2_META_FMT_RPI_FE_CFG: descr = "PiSP FE Config format"; break; case V4L2_META_FMT_RPI_FE_STATS: descr = "PiSP FE Statistics format"; break; + case V4L2_META_FMT_GENERIC_8: descr = "8-bit Generic Metadata"; break; + case V4L2_META_FMT_GENERIC_CSI2_10: descr = "8-bit Generic Meta, 10b CSI-2"; break; + case V4L2_META_FMT_GENERIC_CSI2_12: descr = "8-bit Generic Meta, 12b CSI-2"; break; + case V4L2_META_FMT_GENERIC_CSI2_14: descr = "8-bit Generic Meta, 14b CSI-2"; break; + case V4L2_META_FMT_GENERIC_CSI2_16: descr = "8-bit Generic Meta, 16b CSI-2"; break; + case V4L2_META_FMT_GENERIC_CSI2_20: descr = "8-bit Generic Meta, 20b CSI-2"; break; + case V4L2_META_FMT_GENERIC_CSI2_24: descr = "8-bit Generic Meta, 24b CSI-2"; break; default: /* Compressed formats */ @@ -1525,7 +1533,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_PIX_FMT_PISP_COMP1_RGGB: case V4L2_PIX_FMT_PISP_COMP1_GRBG: case V4L2_PIX_FMT_PISP_COMP1_GBRG: - case V4L2_PIX_FMT_PISP_COMP1_BGGR: + case V4L2_PIX_FMT_PISP_COMP1_BGGR: case V4L2_PIX_FMT_PISP_COMP1_MONO: descr = "PiSP Bayer Compressed Format"; break; case V4L2_PIX_FMT_PISP_COMP2_RGGB: case V4L2_PIX_FMT_PISP_COMP2_GRBG: @@ -1543,6 +1551,22 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) } } + if (fmt->type == V4L2_BUF_TYPE_META_CAPTURE) { + switch (fmt->pixelformat) { + case V4L2_META_FMT_GENERIC_8: + case V4L2_META_FMT_GENERIC_CSI2_10: + case V4L2_META_FMT_GENERIC_CSI2_12: + case V4L2_META_FMT_GENERIC_CSI2_14: + case V4L2_META_FMT_GENERIC_CSI2_16: + case V4L2_META_FMT_GENERIC_CSI2_20: + case V4L2_META_FMT_GENERIC_CSI2_24: + fmt->flags |= V4L2_FMT_FLAG_META_LINE_BASED; + break; + default: + fmt->flags &= ~V4L2_FMT_FLAG_META_LINE_BASED; + } + } + if (descr) WARN_ON(strscpy(fmt->description, descr, sz) < 0); fmt->flags |= flags; @@ -3168,13 +3192,13 @@ static int check_array_args(unsigned int cmd, void *parg, size_t *array_size, case VIDIOC_SUBDEV_S_ROUTING: { struct v4l2_subdev_routing *routing = parg; - if (routing->num_routes > 256) + if (routing->len_routes > 256) return -E2BIG; *user_ptr = u64_to_user_ptr(routing->routes); *kernel_ptr = (void **)&routing->routes; *array_size = sizeof(struct v4l2_subdev_route) - * routing->num_routes; + * routing->len_routes; ret = 1; break; } @@ -3428,11 +3452,14 @@ video_usercopy(struct file *file, unsigned int orig_cmd, unsigned long arg, * FIXME: subdev IOCTLS are partially handled here and partially in * v4l2-subdev.c and the 'always_copy' flag can only be set for IOCTLS * defined here as part of the 'v4l2_ioctls' array. As - * VIDIOC_SUBDEV_G_ROUTING needs to return results to applications even - * in case of failure, but it is not defined here as part of the + * VIDIOC_SUBDEV_[GS]_ROUTING needs to return results to applications + * even in case of failure, but it is not defined here as part of the * 'v4l2_ioctls' array, insert an ad-hoc check to address that. */ - if (err < 0 && !always_copy && cmd != VIDIOC_SUBDEV_G_ROUTING) + if (cmd == VIDIOC_SUBDEV_G_ROUTING || cmd == VIDIOC_SUBDEV_S_ROUTING) + always_copy = true; + + if (err < 0 && !always_copy) goto out; if (has_array_args) { diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index a32ef739eb4490..06adf9f82e38db 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -25,15 +26,6 @@ #include #include -#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) -/* - * The Streams API is an experimental feature. To use the Streams API, set - * 'v4l2_subdev_enable_streams_api' to 1 below. - */ - -static bool v4l2_subdev_enable_streams_api; -#endif - /* * Maximum stream ID is 63 for now, as we use u64 bitmask to represent a set * of streams. @@ -147,6 +139,23 @@ static int subdev_close(struct file *file) } #endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */ +static void v4l2_subdev_enable_privacy_led(struct v4l2_subdev *sd) +{ +#if IS_REACHABLE(CONFIG_LEDS_CLASS) + if (!IS_ERR_OR_NULL(sd->privacy_led)) + led_set_brightness(sd->privacy_led, + sd->privacy_led->max_brightness); +#endif +} + +static void v4l2_subdev_disable_privacy_led(struct v4l2_subdev *sd) +{ +#if IS_REACHABLE(CONFIG_LEDS_CLASS) + if (!IS_ERR_OR_NULL(sd->privacy_led)) + led_set_brightness(sd->privacy_led, 0); +#endif +} + static inline int check_which(u32 which) { if (which != V4L2_SUBDEV_FORMAT_TRY && @@ -176,7 +185,7 @@ static int check_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, { if (sd->flags & V4L2_SUBDEV_FL_STREAMS) { #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) - if (!v4l2_subdev_state_get_stream_format(state, pad, stream)) + if (!v4l2_subdev_state_get_format(state, pad, stream)) return -EINVAL; return 0; #else @@ -306,6 +315,42 @@ static int call_set_selection(struct v4l2_subdev *sd, sd->ops->pad->set_selection(sd, state, sel); } +static int call_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) +{ + unsigned int i; + int ret; + + memset(fd, 0, sizeof(*fd)); + + ret = sd->ops->pad->get_frame_desc(sd, pad, fd); + if (ret) + return ret; + + dev_dbg(sd->dev, "Frame descriptor on pad %u, type %s\n", pad, + fd->type == V4L2_MBUS_FRAME_DESC_TYPE_PARALLEL ? "parallel" : + fd->type == V4L2_MBUS_FRAME_DESC_TYPE_CSI2 ? "CSI-2" : + "unknown"); + + for (i = 0; i < fd->num_entries; i++) { + struct v4l2_mbus_frame_desc_entry *entry = &fd->entry[i]; + char buf[20] = ""; + + if (fd->type == V4L2_MBUS_FRAME_DESC_TYPE_CSI2) + WARN_ON(snprintf(buf, sizeof(buf), + ", vc %u, dt 0x%02x", + entry->bus.csi2.vc, + entry->bus.csi2.dt) >= sizeof(buf)); + + dev_dbg(sd->dev, + "\tstream %u, code 0x%04x, length %u, flags 0x%04x%s\n", + entry->stream, entry->pixelcode, entry->length, + entry->flags, buf); + } + + return 0; +} + static inline int check_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid) { @@ -363,12 +408,8 @@ static int call_s_stream(struct v4l2_subdev *sd, int enable) * The .s_stream() operation must never be called to start or stop an * already started or stopped subdev. Catch offenders but don't return * an error yet to avoid regressions. - * - * As .s_stream() is mutually exclusive with the .enable_streams() and - * .disable_streams() operation, we can use the enabled_streams field - * to store the subdev streaming state. */ - if (WARN_ON(!!sd->enabled_streams == !!enable)) + if (WARN_ON(sd->s_stream_enabled == !!enable)) return 0; ret = sd->ops->video->s_stream(sd, enable); @@ -379,17 +420,12 @@ static int call_s_stream(struct v4l2_subdev *sd, int enable) } if (!ret) { - sd->enabled_streams = enable ? BIT(0) : 0; + sd->s_stream_enabled = enable; -#if IS_REACHABLE(CONFIG_LEDS_CLASS) - if (!IS_ERR_OR_NULL(sd->privacy_led)) { - if (enable) - led_set_brightness(sd->privacy_led, - sd->privacy_led->max_brightness); - else - led_set_brightness(sd->privacy_led, 0); - } -#endif + if (enable) + v4l2_subdev_enable_privacy_led(sd); + else + v4l2_subdev_disable_privacy_led(sd); } return ret; @@ -448,6 +484,7 @@ static const struct v4l2_subdev_pad_ops v4l2_subdev_call_pad_wrappers = { .set_edid = call_set_edid, .dv_timings_cap = call_dv_timings_cap, .enum_dv_timings = call_enum_dv_timings, + .get_frame_desc = call_get_frame_desc, .get_mbus_config = call_get_mbus_config, }; @@ -519,13 +556,6 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, V4L2_SUBDEV_CLIENT_CAP_STREAMS; int rval; - /* - * If the streams API is not enabled, remove V4L2_SUBDEV_CAP_STREAMS. - * Remove this when the API is no longer experimental. - */ - if (!v4l2_subdev_enable_streams_api) - streams_subdev = false; - switch (cmd) { case VIDIOC_SUBDEV_QUERYCAP: { struct v4l2_subdev_capability *cap = arg; @@ -860,33 +890,6 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, case VIDIOC_SUBDEV_QUERYSTD: return v4l2_subdev_call(sd, video, querystd, arg); - case VIDIOC_SUBDEV_G_ROUTING: { - struct v4l2_subdev_routing *routing = arg; - struct v4l2_subdev_krouting *krouting; - - if (!v4l2_subdev_enable_streams_api) - return -ENOIOCTLCMD; - - if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) - return -ENOIOCTLCMD; - - memset(routing->reserved, 0, sizeof(routing->reserved)); - - krouting = &state->routing; - - if (routing->num_routes < krouting->num_routes) { - routing->num_routes = krouting->num_routes; - return -ENOSPC; - } - - memcpy((struct v4l2_subdev_route *)(uintptr_t)routing->routes, - krouting->routes, - krouting->num_routes * sizeof(*krouting->routes)); - routing->num_routes = krouting->num_routes; - - return 0; - } - case VIDIOC_SUBDEV_S_ROUTING: { struct v4l2_subdev_routing *routing = arg; struct v4l2_subdev_route *routes = @@ -894,15 +897,15 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, struct v4l2_subdev_krouting krouting = {}; unsigned int i; - if (!v4l2_subdev_enable_streams_api) - return -ENOIOCTLCMD; - if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) return -ENOIOCTLCMD; if (routing->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev) return -EPERM; + if (routing->num_routes > routing->len_routes) + return -EINVAL; + memset(routing->reserved, 0, sizeof(routing->reserved)); for (i = 0; i < routing->num_routes; ++i) { @@ -928,11 +931,56 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, return -EINVAL; } + /* + * If the driver doesn't support setting routing, just return + * the routing table. + */ + if (!v4l2_subdev_has_op(sd, pad, set_routing)) { + memcpy((struct v4l2_subdev_route *)(uintptr_t)routing->routes, + state->routing.routes, + min(state->routing.num_routes, routing->len_routes) * + sizeof(*state->routing.routes)); + routing->num_routes = state->routing.num_routes; + + return 0; + } + krouting.num_routes = routing->num_routes; + krouting.len_routes = routing->len_routes; krouting.routes = routes; - return v4l2_subdev_call(sd, pad, set_routing, state, + rval = v4l2_subdev_call(sd, pad, set_routing, state, routing->which, &krouting); + if (rval < 0) + return rval; + + memcpy((struct v4l2_subdev_route *)(uintptr_t)routing->routes, + state->routing.routes, + min(state->routing.num_routes, routing->len_routes) * + sizeof(*state->routing.routes)); + routing->num_routes = state->routing.num_routes; + + return 0; + } + + case VIDIOC_SUBDEV_G_ROUTING: { + struct v4l2_subdev_routing *routing = arg; + struct v4l2_subdev_krouting *krouting; + + if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) + return -ENOIOCTLCMD; + + memset(routing->reserved, 0, sizeof(routing->reserved)); + + krouting = &state->routing; + + memcpy((struct v4l2_subdev_route *)(uintptr_t)routing->routes, + krouting->routes, + min(krouting->num_routes, routing->len_routes) * + sizeof(*krouting->routes)); + routing->num_routes = krouting->num_routes; + + return 0; } case VIDIOC_SUBDEV_G_CLIENT_CAP: { @@ -946,14 +994,6 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, case VIDIOC_SUBDEV_S_CLIENT_CAP: { struct v4l2_subdev_client_capability *client_cap = arg; - /* - * Clear V4L2_SUBDEV_CLIENT_CAP_STREAMS if streams API is not - * enabled. Remove this when streams API is no longer - * experimental. - */ - if (!v4l2_subdev_enable_streams_api) - client_cap->capabilities &= ~V4L2_SUBDEV_CLIENT_CAP_STREAMS; - /* Filter out unsupported capabilities */ client_cap->capabilities &= V4L2_SUBDEV_CLIENT_CAP_STREAMS; @@ -1150,14 +1190,6 @@ v4l2_subdev_link_validate_get_format(struct media_pad *pad, u32 stream, struct v4l2_subdev *sd; int ret; - if (!is_media_entity_v4l2_subdev(pad->entity)) { - WARN(pad->entity->function != MEDIA_ENT_F_IO_V4L, - "Driver bug! Wrong media entity type 0x%08x, entity %s\n", - pad->entity->function, pad->entity->name); - - return -EINVAL; - } - sd = media_entity_to_v4l2_subdev(pad->entity); fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; @@ -1332,16 +1364,53 @@ int v4l2_subdev_link_validate(struct media_link *link) bool states_locked; int ret; - if (!is_media_entity_v4l2_subdev(link->sink->entity) || - !is_media_entity_v4l2_subdev(link->source->entity)) { - pr_warn_once("%s of link '%s':%u->'%s':%u is not a V4L2 sub-device, driver bug!\n", - !is_media_entity_v4l2_subdev(link->sink->entity) ? - "sink" : "source", - link->source->entity->name, link->source->index, - link->sink->entity->name, link->sink->index); - return 0; + /* + * Links are validated in the context of the sink entity. Usage of this + * helper on a sink that is not a subdev is a clear driver bug. + */ + if (WARN_ON_ONCE(!is_media_entity_v4l2_subdev(link->sink->entity))) + return -EINVAL; + + /* + * If the source is a video device, delegate link validation to it. This + * allows usage of this helper for subdev connected to a video output + * device, provided that the driver implement the video output device's + * .link_validate() operation. + */ + if (is_media_entity_v4l2_video_device(link->source->entity)) { + struct media_entity *source = link->source->entity; + + if (!source->ops || !source->ops->link_validate) { + /* + * Many existing drivers do not implement the required + * .link_validate() operation for their video devices. + * Print a warning to get the drivers fixed, and return + * 0 to avoid breaking userspace. This should + * eventually be turned into a WARN_ON() when all + * drivers will have been fixed. + */ + pr_warn_once("video device '%s' does not implement .link_validate(), driver bug!\n", + source->name); + return 0; + } + + /* + * Avoid infinite loops in case a video device incorrectly uses + * this helper function as its .link_validate() handler. + */ + if (WARN_ON(source->ops->link_validate == v4l2_subdev_link_validate)) + return -EINVAL; + + return source->ops->link_validate(link); } + /* + * If the source is still not a subdev, usage of this helper is a clear + * driver bug. + */ + if (WARN_ON(!is_media_entity_v4l2_subdev(link->source->entity))) + return -EINVAL; + sink_sd = media_entity_to_v4l2_subdev(link->sink->entity); source_sd = media_entity_to_v4l2_subdev(link->source->entity); @@ -1350,17 +1419,13 @@ int v4l2_subdev_link_validate(struct media_link *link) states_locked = sink_state && source_state; - if (states_locked) { - v4l2_subdev_lock_state(sink_state); - v4l2_subdev_lock_state(source_state); - } + if (states_locked) + v4l2_subdev_lock_states(sink_state, source_state); ret = v4l2_subdev_link_validate_locked(link, states_locked); - if (states_locked) { - v4l2_subdev_unlock_state(sink_state); - v4l2_subdev_unlock_state(source_state); - } + if (states_locked) + v4l2_subdev_unlock_states(sink_state, source_state); return ret; } @@ -1414,6 +1479,8 @@ __v4l2_subdev_state_alloc(struct v4l2_subdev *sd, const char *lock_name, else state->lock = &state->_lock; + state->sd = sd; + /* Drivers that support streams do not need the legacy pad config */ if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS) && sd->entity.num_pads) { state->pads = kvcalloc(sd->entity.num_pads, @@ -1424,16 +1491,18 @@ __v4l2_subdev_state_alloc(struct v4l2_subdev *sd, const char *lock_name, } } - /* - * There can be no race at this point, but we lock the state anyway to - * satisfy lockdep checks. - */ - v4l2_subdev_lock_state(state); - ret = v4l2_subdev_call(sd, pad, init_cfg, state); - v4l2_subdev_unlock_state(state); + if (sd->internal_ops && sd->internal_ops->init_state) { + /* + * There can be no race at this point, but we lock the state + * anyway to satisfy lockdep checks. + */ + v4l2_subdev_lock_state(state); + ret = sd->internal_ops->init_state(sd, state); + v4l2_subdev_unlock_state(state); - if (ret < 0 && ret != -ENOIOCTLCMD) - goto err; + if (ret) + goto err; + } return state; @@ -1465,6 +1534,33 @@ int __v4l2_subdev_init_finalize(struct v4l2_subdev *sd, const char *name, struct lock_class_key *key) { struct v4l2_subdev_state *state; + struct device *dev = sd->dev; + bool has_disable_streams; + bool has_enable_streams; + bool has_s_stream; + + /* Check that the subdevice implements the required features */ + + has_s_stream = v4l2_subdev_has_op(sd, video, s_stream); + has_enable_streams = v4l2_subdev_has_op(sd, pad, enable_streams); + has_disable_streams = v4l2_subdev_has_op(sd, pad, disable_streams); + + if (has_enable_streams != has_disable_streams) { + dev_err(dev, + "subdev '%s' must implement both or neither of .enable_streams() and .disable_streams()\n", + sd->name); + return -EINVAL; + } + + if (sd->flags & V4L2_SUBDEV_FL_STREAMS) { + if (has_s_stream && !has_enable_streams) { + dev_err(dev, + "subdev '%s' must implement .enable/disable_streams()\n", + sd->name); + + return -EINVAL; + } + } state = __v4l2_subdev_state_alloc(sd, name, key); if (IS_ERR(state)) @@ -1483,7 +1579,8 @@ void v4l2_subdev_cleanup(struct v4l2_subdev *sd) __v4l2_subdev_state_free(sd->active_state); sd->active_state = NULL; - if (list_empty(&sd->async_subdev_endpoint_list)) + /* Uninitialised sub-device, bail out here. */ + if (!sd->async_subdev_endpoint_list.next) return; list_for_each_entry_safe(ase, ase_tmp, &sd->async_subdev_endpoint_list, @@ -1495,6 +1592,108 @@ void v4l2_subdev_cleanup(struct v4l2_subdev *sd) } EXPORT_SYMBOL_GPL(v4l2_subdev_cleanup); +struct v4l2_mbus_framefmt * +__v4l2_subdev_state_get_format(struct v4l2_subdev_state *state, + unsigned int pad, u32 stream) +{ + struct v4l2_subdev_stream_configs *stream_configs; + unsigned int i; + + if (WARN_ON_ONCE(!state)) + return NULL; + + if (state->pads) { + if (stream) + return NULL; + + if (pad >= state->sd->entity.num_pads) + return NULL; + + return &state->pads[pad].format; + } + + lockdep_assert_held(state->lock); + + stream_configs = &state->stream_configs; + + for (i = 0; i < stream_configs->num_configs; ++i) { + if (stream_configs->configs[i].pad == pad && + stream_configs->configs[i].stream == stream) + return &stream_configs->configs[i].fmt; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(__v4l2_subdev_state_get_format); + +struct v4l2_rect * +__v4l2_subdev_state_get_crop(struct v4l2_subdev_state *state, unsigned int pad, + u32 stream) +{ + struct v4l2_subdev_stream_configs *stream_configs; + unsigned int i; + + if (WARN_ON_ONCE(!state)) + return NULL; + + if (state->pads) { + if (stream) + return NULL; + + if (pad >= state->sd->entity.num_pads) + return NULL; + + return &state->pads[pad].crop; + } + + lockdep_assert_held(state->lock); + + stream_configs = &state->stream_configs; + + for (i = 0; i < stream_configs->num_configs; ++i) { + if (stream_configs->configs[i].pad == pad && + stream_configs->configs[i].stream == stream) + return &stream_configs->configs[i].crop; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(__v4l2_subdev_state_get_crop); + +struct v4l2_rect * +__v4l2_subdev_state_get_compose(struct v4l2_subdev_state *state, + unsigned int pad, u32 stream) +{ + struct v4l2_subdev_stream_configs *stream_configs; + unsigned int i; + + if (WARN_ON_ONCE(!state)) + return NULL; + + if (state->pads) { + if (stream) + return NULL; + + if (pad >= state->sd->entity.num_pads) + return NULL; + + return &state->pads[pad].compose; + } + + lockdep_assert_held(state->lock); + + stream_configs = &state->stream_configs; + + for (i = 0; i < stream_configs->num_configs; ++i) { + if (stream_configs->configs[i].pad == pad && + stream_configs->configs[i].stream == stream) + return &stream_configs->configs[i].compose; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(__v4l2_subdev_state_get_compose); + #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) static int @@ -1551,14 +1750,7 @@ int v4l2_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, { struct v4l2_mbus_framefmt *fmt; - if (sd->flags & V4L2_SUBDEV_FL_STREAMS) - fmt = v4l2_subdev_state_get_stream_format(state, format->pad, - format->stream); - else if (format->pad < sd->entity.num_pads && format->stream == 0) - fmt = v4l2_subdev_get_pad_format(sd, state, format->pad); - else - fmt = NULL; - + fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); if (!fmt) return -EINVAL; @@ -1648,69 +1840,6 @@ int v4l2_subdev_set_routing_with_fmt(struct v4l2_subdev *sd, } EXPORT_SYMBOL_GPL(v4l2_subdev_set_routing_with_fmt); -struct v4l2_mbus_framefmt * -v4l2_subdev_state_get_stream_format(struct v4l2_subdev_state *state, - unsigned int pad, u32 stream) -{ - struct v4l2_subdev_stream_configs *stream_configs; - unsigned int i; - - lockdep_assert_held(state->lock); - - stream_configs = &state->stream_configs; - - for (i = 0; i < stream_configs->num_configs; ++i) { - if (stream_configs->configs[i].pad == pad && - stream_configs->configs[i].stream == stream) - return &stream_configs->configs[i].fmt; - } - - return NULL; -} -EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_stream_format); - -struct v4l2_rect * -v4l2_subdev_state_get_stream_crop(struct v4l2_subdev_state *state, - unsigned int pad, u32 stream) -{ - struct v4l2_subdev_stream_configs *stream_configs; - unsigned int i; - - lockdep_assert_held(state->lock); - - stream_configs = &state->stream_configs; - - for (i = 0; i < stream_configs->num_configs; ++i) { - if (stream_configs->configs[i].pad == pad && - stream_configs->configs[i].stream == stream) - return &stream_configs->configs[i].crop; - } - - return NULL; -} -EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_stream_crop); - -struct v4l2_rect * -v4l2_subdev_state_get_stream_compose(struct v4l2_subdev_state *state, - unsigned int pad, u32 stream) -{ - struct v4l2_subdev_stream_configs *stream_configs; - unsigned int i; - - lockdep_assert_held(state->lock); - - stream_configs = &state->stream_configs; - - for (i = 0; i < stream_configs->num_configs; ++i) { - if (stream_configs->configs[i].pad == pad && - stream_configs->configs[i].stream == stream) - return &stream_configs->configs[i].compose; - } - - return NULL; -} -EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_stream_compose); - int v4l2_subdev_routing_find_opposite_end(const struct v4l2_subdev_krouting *routing, u32 pad, u32 stream, u32 *other_pad, u32 *other_stream) @@ -1755,8 +1884,7 @@ v4l2_subdev_state_get_opposite_stream_format(struct v4l2_subdev_state *state, if (ret) return NULL; - return v4l2_subdev_state_get_stream_format(state, other_pad, - other_stream); + return v4l2_subdev_state_get_format(state, other_pad, other_stream); } EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_opposite_stream_format); @@ -1925,43 +2053,55 @@ int v4l2_subdev_routing_validate(struct v4l2_subdev *sd, } EXPORT_SYMBOL_GPL(v4l2_subdev_routing_validate); -static int v4l2_subdev_enable_streams_fallback(struct v4l2_subdev *sd, u32 pad, - u64 streams_mask) +static void v4l2_subdev_collect_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask, + u64 *found_streams, + u64 *enabled_streams) { - struct device *dev = sd->entity.graph_obj.mdev->dev; - unsigned int i; - int ret; + if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) { + *found_streams = BIT_ULL(0); + *enabled_streams = + (sd->enabled_pads & BIT_ULL(pad)) ? BIT_ULL(0) : 0; + return; + } - /* - * The subdev doesn't implement pad-based stream enable, fall back - * on the .s_stream() operation. This can only be done for subdevs that - * have a single source pad, as sd->enabled_streams is global to the - * subdev. - */ - if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE)) - return -EOPNOTSUPP; + *found_streams = 0; + *enabled_streams = 0; - for (i = 0; i < sd->entity.num_pads; ++i) { - if (i != pad && sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE) - return -EOPNOTSUPP; - } + for (unsigned int i = 0; i < state->stream_configs.num_configs; ++i) { + const struct v4l2_subdev_stream_config *cfg = + &state->stream_configs.configs[i]; + + if (cfg->pad != pad || !(streams_mask & BIT_ULL(cfg->stream))) + continue; - if (sd->enabled_streams & streams_mask) { - dev_dbg(dev, "set of streams %#llx already enabled on %s:%u\n", - streams_mask, sd->entity.name, pad); - return -EALREADY; + *found_streams |= BIT_ULL(cfg->stream); + if (cfg->enabled) + *enabled_streams |= BIT_ULL(cfg->stream); } +} - /* Start streaming when the first streams are enabled. */ - if (!sd->enabled_streams) { - ret = v4l2_subdev_call(sd, video, s_stream, 1); - if (ret) - return ret; +static void v4l2_subdev_set_streams_enabled(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask, + bool enabled) +{ + if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) { + if (enabled) + sd->enabled_pads |= BIT_ULL(pad); + else + sd->enabled_pads &= ~BIT_ULL(pad); + return; } - sd->enabled_streams |= streams_mask; + for (unsigned int i = 0; i < state->stream_configs.num_configs; ++i) { + struct v4l2_subdev_stream_config *cfg = + &state->stream_configs.configs[i]; - return 0; + if (cfg->pad == pad && (streams_mask & BIT_ULL(cfg->stream))) + cfg->enabled = enabled; + } } int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, @@ -1969,44 +2109,44 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, { struct device *dev = sd->entity.graph_obj.mdev->dev; struct v4l2_subdev_state *state; - u64 found_streams = 0; - unsigned int i; + bool already_streaming; + u64 enabled_streams; + u64 found_streams; + bool use_s_stream; int ret; /* A few basic sanity checks first. */ if (pad >= sd->entity.num_pads) return -EINVAL; + if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE)) + return -EOPNOTSUPP; + + /* + * We use a 64-bit bitmask for tracking enabled pads, so only subdevices + * with 64 pads or less can be supported. + */ + if (pad >= sizeof(sd->enabled_pads) * BITS_PER_BYTE) + return -EOPNOTSUPP; + if (!streams_mask) return 0; /* Fallback on .s_stream() if .enable_streams() isn't available. */ - if (!sd->ops->pad || !sd->ops->pad->enable_streams) - return v4l2_subdev_enable_streams_fallback(sd, pad, - streams_mask); + use_s_stream = !v4l2_subdev_has_op(sd, pad, enable_streams); - state = v4l2_subdev_lock_and_get_active_state(sd); + if (!use_s_stream) + state = v4l2_subdev_lock_and_get_active_state(sd); + else + state = NULL; /* * Verify that the requested streams exist and that they are not * already enabled. */ - for (i = 0; i < state->stream_configs.num_configs; ++i) { - struct v4l2_subdev_stream_config *cfg = - &state->stream_configs.configs[i]; - - if (cfg->pad != pad || !(streams_mask & BIT_ULL(cfg->stream))) - continue; - found_streams |= BIT_ULL(cfg->stream); - - if (cfg->enabled) { - dev_dbg(dev, "stream %u already enabled on %s:%u\n", - cfg->stream, sd->entity.name, pad); - ret = -EALREADY; - goto done; - } - } + v4l2_subdev_collect_streams(sd, state, pad, streams_mask, + &found_streams, &enabled_streams); if (found_streams != streams_mask) { dev_dbg(dev, "streams 0x%llx not found on %s:%u\n", @@ -2015,11 +2155,29 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, goto done; } + if (enabled_streams) { + dev_dbg(dev, "streams 0x%llx already enabled on %s:%u\n", + enabled_streams, sd->entity.name, pad); + ret = -EALREADY; + goto done; + } + dev_dbg(dev, "enable streams %u:%#llx\n", pad, streams_mask); - /* Call the .enable_streams() operation. */ - ret = v4l2_subdev_call(sd, pad, enable_streams, state, pad, - streams_mask); + already_streaming = v4l2_subdev_is_streaming(sd); + + if (!use_s_stream) { + /* Call the .enable_streams() operation. */ + ret = v4l2_subdev_call(sd, pad, enable_streams, state, pad, + streams_mask); + } else { + /* Start streaming when the first pad is enabled. */ + if (!already_streaming) + ret = v4l2_subdev_call(sd, video, s_stream, 1); + else + ret = 0; + } + if (ret) { dev_dbg(dev, "enable streams %u:%#llx failed: %d\n", pad, streams_mask, ret); @@ -2027,103 +2185,68 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, } /* Mark the streams as enabled. */ - for (i = 0; i < state->stream_configs.num_configs; ++i) { - struct v4l2_subdev_stream_config *cfg = - &state->stream_configs.configs[i]; + v4l2_subdev_set_streams_enabled(sd, state, pad, streams_mask, true); - if (cfg->pad == pad && (streams_mask & BIT_ULL(cfg->stream))) - cfg->enabled = true; - } + /* + * TODO: When all the drivers have been changed to use + * v4l2_subdev_enable_streams() and v4l2_subdev_disable_streams(), + * instead of calling .s_stream() operation directly, we can remove + * the privacy LED handling from call_s_stream() and do it here + * for all cases. + */ + if (!use_s_stream && !already_streaming) + v4l2_subdev_enable_privacy_led(sd); done: - v4l2_subdev_unlock_state(state); + if (!use_s_stream) + v4l2_subdev_unlock_state(state); return ret; } EXPORT_SYMBOL_GPL(v4l2_subdev_enable_streams); -static int v4l2_subdev_disable_streams_fallback(struct v4l2_subdev *sd, u32 pad, - u64 streams_mask) -{ - struct device *dev = sd->entity.graph_obj.mdev->dev; - unsigned int i; - int ret; - - /* - * If the subdev doesn't implement pad-based stream enable, fall back - * on the .s_stream() operation. This can only be done for subdevs that - * have a single source pad, as sd->enabled_streams is global to the - * subdev. - */ - if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE)) - return -EOPNOTSUPP; - - for (i = 0; i < sd->entity.num_pads; ++i) { - if (i != pad && sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE) - return -EOPNOTSUPP; - } - - if ((sd->enabled_streams & streams_mask) != streams_mask) { - dev_dbg(dev, "set of streams %#llx already disabled on %s:%u\n", - streams_mask, sd->entity.name, pad); - return -EALREADY; - } - - /* Stop streaming when the last streams are disabled. */ - if (!(sd->enabled_streams & ~streams_mask)) { - ret = v4l2_subdev_call(sd, video, s_stream, 0); - if (ret) - return ret; - } - - sd->enabled_streams &= ~streams_mask; - - return 0; -} - int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad, u64 streams_mask) { struct device *dev = sd->entity.graph_obj.mdev->dev; struct v4l2_subdev_state *state; - u64 found_streams = 0; - unsigned int i; + u64 enabled_streams; + u64 found_streams; + bool use_s_stream; int ret; /* A few basic sanity checks first. */ if (pad >= sd->entity.num_pads) return -EINVAL; + if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE)) + return -EOPNOTSUPP; + + /* + * We use a 64-bit bitmask for tracking enabled pads, so only subdevices + * with 64 pads or less can be supported. + */ + if (pad >= sizeof(sd->enabled_pads) * BITS_PER_BYTE) + return -EOPNOTSUPP; + if (!streams_mask) return 0; /* Fallback on .s_stream() if .disable_streams() isn't available. */ - if (!sd->ops->pad || !sd->ops->pad->disable_streams) - return v4l2_subdev_disable_streams_fallback(sd, pad, - streams_mask); + use_s_stream = !v4l2_subdev_has_op(sd, pad, disable_streams); - state = v4l2_subdev_lock_and_get_active_state(sd); + if (!use_s_stream) + state = v4l2_subdev_lock_and_get_active_state(sd); + else + state = NULL; /* * Verify that the requested streams exist and that they are not * already disabled. */ - for (i = 0; i < state->stream_configs.num_configs; ++i) { - struct v4l2_subdev_stream_config *cfg = - &state->stream_configs.configs[i]; - - if (cfg->pad != pad || !(streams_mask & BIT_ULL(cfg->stream))) - continue; - found_streams |= BIT_ULL(cfg->stream); - - if (!cfg->enabled) { - dev_dbg(dev, "stream %u already disabled on %s:%u\n", - cfg->stream, sd->entity.name, pad); - ret = -EALREADY; - goto done; - } - } + v4l2_subdev_collect_streams(sd, state, pad, streams_mask, + &found_streams, &enabled_streams); if (found_streams != streams_mask) { dev_dbg(dev, "streams 0x%llx not found on %s:%u\n", @@ -2132,28 +2255,43 @@ int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad, goto done; } + if (enabled_streams != streams_mask) { + dev_dbg(dev, "streams 0x%llx already disabled on %s:%u\n", + streams_mask & ~enabled_streams, sd->entity.name, pad); + ret = -EALREADY; + goto done; + } + dev_dbg(dev, "disable streams %u:%#llx\n", pad, streams_mask); - /* Call the .disable_streams() operation. */ - ret = v4l2_subdev_call(sd, pad, disable_streams, state, pad, - streams_mask); + if (!use_s_stream) { + /* Call the .disable_streams() operation. */ + ret = v4l2_subdev_call(sd, pad, disable_streams, state, pad, + streams_mask); + } else { + /* Stop streaming when the last streams are disabled. */ + + if (!(sd->enabled_pads & ~BIT_ULL(pad))) + ret = v4l2_subdev_call(sd, video, s_stream, 0); + else + ret = 0; + } + if (ret) { dev_dbg(dev, "disable streams %u:%#llx failed: %d\n", pad, streams_mask, ret); goto done; } - /* Mark the streams as disabled. */ - for (i = 0; i < state->stream_configs.num_configs; ++i) { - struct v4l2_subdev_stream_config *cfg = - &state->stream_configs.configs[i]; - - if (cfg->pad == pad && (streams_mask & BIT_ULL(cfg->stream))) - cfg->enabled = false; - } + v4l2_subdev_set_streams_enabled(sd, state, pad, streams_mask, false); done: - v4l2_subdev_unlock_state(state); + if (!use_s_stream) { + if (!v4l2_subdev_is_streaming(sd)) + v4l2_subdev_disable_privacy_led(sd); + + v4l2_subdev_unlock_state(state); + } return ret; } @@ -2182,15 +2320,24 @@ int v4l2_subdev_s_stream_helper(struct v4l2_subdev *sd, int enable) if (WARN_ON(pad_index == -1)) return -EINVAL; - /* - * As there's a single source pad, just collect all the source streams. - */ - state = v4l2_subdev_lock_and_get_active_state(sd); + if (sd->flags & V4L2_SUBDEV_FL_STREAMS) { + /* + * As there's a single source pad, just collect all the source + * streams. + */ + state = v4l2_subdev_lock_and_get_active_state(sd); - for_each_active_route(&state->routing, route) - source_mask |= BIT_ULL(route->source_stream); + for_each_active_route(&state->routing, route) + source_mask |= BIT_ULL(route->source_stream); - v4l2_subdev_unlock_state(state); + v4l2_subdev_unlock_state(state); + } else { + /* + * For non-streams subdevices, there's a single implicit stream + * per pad. + */ + source_mask = BIT_ULL(0); + } if (enable) return v4l2_subdev_enable_streams(sd, pad_index, source_mask); @@ -2232,6 +2379,31 @@ void v4l2_subdev_notify_event(struct v4l2_subdev *sd, } EXPORT_SYMBOL_GPL(v4l2_subdev_notify_event); +bool v4l2_subdev_is_streaming(struct v4l2_subdev *sd) +{ + struct v4l2_subdev_state *state; + + if (!v4l2_subdev_has_op(sd, pad, enable_streams)) + return sd->s_stream_enabled; + + if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) + return !!sd->enabled_pads; + + state = v4l2_subdev_get_locked_active_state(sd); + + for (unsigned int i = 0; i < state->stream_configs.num_configs; ++i) { + const struct v4l2_subdev_stream_config *cfg; + + cfg = &state->stream_configs.configs[i]; + + if (cfg->enabled) + return true; + } + + return false; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_is_streaming); + int v4l2_subdev_get_privacy_led(struct v4l2_subdev *sd) { #if IS_REACHABLE(CONFIG_LEDS_CLASS) diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c index 9a11793f34f74b..de20ea26f838fa 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c @@ -362,7 +362,7 @@ gc0310_get_pad_format(struct gc0310_device *dev, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&dev->sd, state, pad); + return v4l2_subdev_state_get_format(state, pad); return &dev->mode.fmt; } diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c index 9fa390fbc5f31e..9c20fe91523813 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c @@ -561,7 +561,7 @@ static int gc2235_set_fmt(struct v4l2_subdev *sd, fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - sd_state->pads->try_fmt = *fmt; + *v4l2_subdev_state_get_format(sd_state, 0) = *fmt; mutex_unlock(&dev->input_lock); return 0; } diff --git a/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c b/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c index 1c6643c442ef36..8105365fbb2a50 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c @@ -666,7 +666,7 @@ static int mt9m114_set_fmt(struct v4l2_subdev *sd, fmt->height = res->height; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - sd_state->pads->try_fmt = *fmt; + *v4l2_subdev_state_get_format(sd_state, 0) = *fmt; return 0; } diff --git a/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c b/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c index 6a72691ed5b74d..1de63c82cce15d 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c @@ -671,7 +671,7 @@ static int ov2722_set_fmt(struct v4l2_subdev *sd, fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - sd_state->pads->try_fmt = *fmt; + *v4l2_subdev_state_get_format(sd_state, 0) = *fmt; return 0; } diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2.c b/drivers/staging/media/atomisp/pci/atomisp_csi2.c index abf55a86f79578..89118438a3b679 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_csi2.c +++ b/drivers/staging/media/atomisp/pci/atomisp_csi2.c @@ -29,8 +29,7 @@ v4l2_mbus_framefmt *__csi2_get_format(struct atomisp_mipi_csi2_device *csi2, unsigned int pad) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&csi2->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &csi2->formats[pad]; } diff --git a/drivers/staging/media/atomisp/pci/atomisp_subdev.c b/drivers/staging/media/atomisp/pci/atomisp_subdev.c index 45073e401bac48..7ad9e48424537f 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_subdev.c +++ b/drivers/staging/media/atomisp/pci/atomisp_subdev.c @@ -240,9 +240,9 @@ struct v4l2_rect *atomisp_subdev_get_rect(struct v4l2_subdev *sd, if (which == V4L2_SUBDEV_FORMAT_TRY) { switch (target) { case V4L2_SEL_TGT_CROP: - return v4l2_subdev_get_try_crop(sd, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SEL_TGT_COMPOSE: - return v4l2_subdev_get_try_compose(sd, sd_state, pad); + return v4l2_subdev_state_get_compose(sd_state, pad); } } @@ -264,7 +264,7 @@ struct v4l2_mbus_framefmt struct atomisp_sub_device *isp_sd = v4l2_get_subdevdata(sd); if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); return &isp_sd->fmt[pad].fmt; } diff --git a/drivers/staging/media/atomisp/pci/atomisp_tpg.c b/drivers/staging/media/atomisp/pci/atomisp_tpg.c index 074826a5b70643..92e61ee9099346 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_tpg.c +++ b/drivers/staging/media/atomisp/pci/atomisp_tpg.c @@ -47,7 +47,7 @@ static int tpg_set_fmt(struct v4l2_subdev *sd, /* only raw8 grbg is supported by TPG */ fmt->code = MEDIA_BUS_FMT_SGRBG8_1X8; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - sd_state->pads->try_fmt = *fmt; + *v4l2_subdev_state_get_format(sd_state, 0) = *fmt; return 0; } return 0; diff --git a/drivers/staging/media/deprecated/atmel/atmel-isc-base.c b/drivers/staging/media/deprecated/atmel/atmel-isc-base.c index f5d963904201fa..df9e446b453088 100644 --- a/drivers/staging/media/deprecated/atmel/atmel-isc-base.c +++ b/drivers/staging/media/deprecated/atmel/atmel-isc-base.c @@ -824,6 +824,7 @@ static int isc_try_configure_pipeline(struct isc_device *isc) static void isc_try_fse(struct isc_device *isc, struct v4l2_subdev_state *sd_state) { + struct v4l2_rect *try_crop = v4l2_subdev_state_get_crop(sd_state, 0); struct v4l2_subdev_frame_size_enum fse = { .which = V4L2_SUBDEV_FORMAT_TRY, }; @@ -845,11 +846,11 @@ static void isc_try_fse(struct isc_device *isc, * just use the maximum ISC can receive. */ if (ret) { - sd_state->pads->try_crop.width = isc->max_width; - sd_state->pads->try_crop.height = isc->max_height; + try_crop->width = isc->max_width; + try_crop->height = isc->max_height; } else { - sd_state->pads->try_crop.width = fse.max_width; - sd_state->pads->try_crop.height = fse.max_height; + try_crop->width = fse.max_width; + try_crop->height = fse.max_height; } } diff --git a/drivers/staging/media/imx/imx-ic-prp.c b/drivers/staging/media/imx/imx-ic-prp.c index ac5fb332088ea3..8bd9be49cc0839 100644 --- a/drivers/staging/media/imx/imx-ic-prp.c +++ b/drivers/staging/media/imx/imx-ic-prp.c @@ -82,10 +82,8 @@ static struct v4l2_mbus_framefmt * __prp_get_fmt(struct prp_priv *priv, struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { - struct imx_ic_priv *ic_priv = priv->ic_priv; - if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&ic_priv->sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &priv->format_mbus; } @@ -450,7 +448,6 @@ static int prp_registered(struct v4l2_subdev *sd) } static const struct v4l2_subdev_pad_ops prp_pad_ops = { - .init_cfg = imx_media_init_cfg, .enum_mbus_code = prp_enum_mbus_code, .get_fmt = prp_get_fmt, .set_fmt = prp_set_fmt, @@ -474,6 +471,7 @@ static const struct v4l2_subdev_ops prp_subdev_ops = { }; static const struct v4l2_subdev_internal_ops prp_internal_ops = { + .init_state = imx_media_init_state, .registered = prp_registered, }; diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 9b81cfbcd77790..04878f07eeba6b 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -790,10 +790,8 @@ static struct v4l2_mbus_framefmt * __prp_get_fmt(struct prp_priv *priv, struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { - struct imx_ic_priv *ic_priv = priv->ic_priv; - if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&ic_priv->sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &priv->format_mbus[pad]; } @@ -1298,7 +1296,6 @@ static void prp_unregistered(struct v4l2_subdev *sd) } static const struct v4l2_subdev_pad_ops prp_pad_ops = { - .init_cfg = imx_media_init_cfg, .enum_mbus_code = prp_enum_mbus_code, .enum_frame_size = prp_enum_frame_size, .get_fmt = prp_get_fmt, @@ -1322,6 +1319,7 @@ static const struct v4l2_subdev_ops prp_subdev_ops = { }; static const struct v4l2_subdev_internal_ops prp_internal_ops = { + .init_state = imx_media_init_state, .registered = prp_registered, .unregistered = prp_unregistered, }; diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index dda1ebc34692aa..2fc94011fe4deb 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -1148,7 +1148,7 @@ __csi_get_fmt(struct csi_priv *priv, struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&priv->sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &priv->format_mbus[pad]; } @@ -1158,8 +1158,7 @@ __csi_get_crop(struct csi_priv *priv, struct v4l2_subdev_state *sd_state, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&priv->sd, sd_state, - CSI_SINK_PAD); + return v4l2_subdev_state_get_crop(sd_state, CSI_SINK_PAD); else return &priv->crop; } @@ -1169,8 +1168,7 @@ __csi_get_compose(struct csi_priv *priv, struct v4l2_subdev_state *sd_state, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_compose(&priv->sd, sd_state, - CSI_SINK_PAD); + return v4l2_subdev_state_get_compose(sd_state, CSI_SINK_PAD); else return &priv->compose; } @@ -1868,7 +1866,6 @@ static const struct v4l2_subdev_video_ops csi_video_ops = { }; static const struct v4l2_subdev_pad_ops csi_pad_ops = { - .init_cfg = imx_media_init_cfg, .enum_mbus_code = csi_enum_mbus_code, .enum_frame_size = csi_enum_frame_size, .enum_frame_interval = csi_enum_frame_interval, @@ -1886,6 +1883,7 @@ static const struct v4l2_subdev_ops csi_subdev_ops = { }; static const struct v4l2_subdev_internal_ops csi_internal_ops = { + .init_state = imx_media_init_state, .registered = csi_registered, .unregistered = csi_unregistered, }; diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c index 064dc562bc96a0..1b5af8945e6b8b 100644 --- a/drivers/staging/media/imx/imx-media-utils.c +++ b/drivers/staging/media/imx/imx-media-utils.c @@ -426,10 +426,10 @@ EXPORT_SYMBOL_GPL(imx_media_init_mbus_fmt); /* * Initializes the TRY format to the ACTIVE format on all pads - * of a subdev. Can be used as the .init_cfg pad operation. + * of a subdev. Can be used as the .init_state internal operation. */ -int imx_media_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +int imx_media_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *mf_try; unsigned int pad; @@ -445,13 +445,13 @@ int imx_media_init_cfg(struct v4l2_subdev *sd, if (ret) continue; - mf_try = v4l2_subdev_get_try_format(sd, sd_state, pad); + mf_try = v4l2_subdev_state_get_format(sd_state, pad); *mf_try = format.format; } return 0; } -EXPORT_SYMBOL_GPL(imx_media_init_cfg); +EXPORT_SYMBOL_GPL(imx_media_init_state); /* * Default the colorspace in tryfmt to SRGB if set to an unsupported diff --git a/drivers/staging/media/imx/imx-media-vdic.c b/drivers/staging/media/imx/imx-media-vdic.c index 3c2093c520bab2..810b38ea3ab904 100644 --- a/drivers/staging/media/imx/imx-media-vdic.c +++ b/drivers/staging/media/imx/imx-media-vdic.c @@ -536,7 +536,7 @@ __vdic_get_fmt(struct vdic_priv *priv, struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&priv->sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &priv->format_mbus[pad]; } @@ -882,7 +882,6 @@ static void vdic_unregistered(struct v4l2_subdev *sd) } static const struct v4l2_subdev_pad_ops vdic_pad_ops = { - .init_cfg = imx_media_init_cfg, .enum_mbus_code = vdic_enum_mbus_code, .get_fmt = vdic_get_fmt, .set_fmt = vdic_set_fmt, @@ -906,6 +905,7 @@ static const struct v4l2_subdev_ops vdic_subdev_ops = { }; static const struct v4l2_subdev_internal_ops vdic_internal_ops = { + .init_state = imx_media_init_state, .registered = vdic_registered, .unregistered = vdic_unregistered, }; diff --git a/drivers/staging/media/imx/imx-media.h b/drivers/staging/media/imx/imx-media.h index 2640cd34dce264..f095d9134fee4a 100644 --- a/drivers/staging/media/imx/imx-media.h +++ b/drivers/staging/media/imx/imx-media.h @@ -192,8 +192,8 @@ static inline int imx_media_enum_ipu_formats(u32 *code, u32 index, int imx_media_init_mbus_fmt(struct v4l2_mbus_framefmt *mbus, u32 width, u32 height, u32 code, u32 field, const struct imx_media_pixfmt **cc); -int imx_media_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state); +int imx_media_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state); void imx_media_try_colorimetry(struct v4l2_mbus_framefmt *tryfmt, bool ic_route); int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix, diff --git a/drivers/staging/media/imx/imx6-mipi-csi2.c b/drivers/staging/media/imx/imx6-mipi-csi2.c index b2d8476d83a0af..0d8b420616235d 100644 --- a/drivers/staging/media/imx/imx6-mipi-csi2.c +++ b/drivers/staging/media/imx/imx6-mipi-csi2.c @@ -501,7 +501,7 @@ __csi2_get_fmt(struct csi2_dev *csi2, struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&csi2->sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &csi2->format_mbus; } @@ -619,7 +619,6 @@ static const struct v4l2_subdev_video_ops csi2_video_ops = { }; static const struct v4l2_subdev_pad_ops csi2_pad_ops = { - .init_cfg = imx_media_init_cfg, .get_fmt = csi2_get_fmt, .set_fmt = csi2_set_fmt, }; @@ -631,6 +630,7 @@ static const struct v4l2_subdev_ops csi2_subdev_ops = { }; static const struct v4l2_subdev_internal_ops csi2_internal_ops = { + .init_state = imx_media_init_state, .registered = csi2_registered, }; diff --git a/drivers/staging/media/ipu3/ipu3-v4l2.c b/drivers/staging/media/ipu3/ipu3-v4l2.c index 55cc44a401bc43..9bb4fab2839636 100644 --- a/drivers/staging/media/ipu3/ipu3-v4l2.c +++ b/drivers/staging/media/ipu3/ipu3-v4l2.c @@ -36,7 +36,7 @@ static int imgu_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) /* Initialize try_fmt */ for (i = 0; i < IMGU_NODE_NUM; i++) { struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->state, i); + v4l2_subdev_state_get_format(fh->state, i); try_fmt->width = try_crop.width; try_fmt->height = try_crop.height; @@ -44,8 +44,8 @@ static int imgu_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) try_fmt->field = V4L2_FIELD_NONE; } - *v4l2_subdev_get_try_crop(sd, fh->state, IMGU_NODE_IN) = try_crop; - *v4l2_subdev_get_try_compose(sd, fh->state, IMGU_NODE_IN) = try_crop; + *v4l2_subdev_state_get_crop(fh->state, IMGU_NODE_IN) = try_crop; + *v4l2_subdev_state_get_compose(fh->state, IMGU_NODE_IN) = try_crop; return 0; } @@ -136,7 +136,7 @@ static int imgu_subdev_get_fmt(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { fmt->format = imgu_pipe->nodes[pad].pad_fmt; } else { - mf = v4l2_subdev_get_try_format(sd, sd_state, pad); + mf = v4l2_subdev_state_get_format(sd_state, pad); fmt->format = *mf; } @@ -161,7 +161,7 @@ static int imgu_subdev_set_fmt(struct v4l2_subdev *sd, imgu_pipe = &imgu->imgu_pipe[pipe]; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - mf = v4l2_subdev_get_try_format(sd, sd_state, pad); + mf = v4l2_subdev_state_get_format(sd_state, pad); else mf = &imgu_pipe->nodes[pad].pad_fmt; @@ -194,7 +194,7 @@ imgu_subdev_get_crop(struct imgu_v4l2_subdev *sd, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&sd->subdev, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); else return &sd->rect.eff; } @@ -205,7 +205,7 @@ imgu_subdev_get_compose(struct imgu_v4l2_subdev *sd, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_compose(&sd->subdev, sd_state, pad); + return v4l2_subdev_state_get_compose(sd_state, pad); else return &sd->rect.bds; } diff --git a/drivers/staging/media/omap4iss/iss_csi2.c b/drivers/staging/media/omap4iss/iss_csi2.c index 04ce0e7eb55784..c6a8381082c389 100644 --- a/drivers/staging/media/omap4iss/iss_csi2.c +++ b/drivers/staging/media/omap4iss/iss_csi2.c @@ -830,8 +830,7 @@ __csi2_get_format(struct iss_csi2_device *csi2, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&csi2->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); return &csi2->formats[pad]; } diff --git a/drivers/staging/media/omap4iss/iss_ipipe.c b/drivers/staging/media/omap4iss/iss_ipipe.c index 23f707cb336f53..ad013ed923e7be 100644 --- a/drivers/staging/media/omap4iss/iss_ipipe.c +++ b/drivers/staging/media/omap4iss/iss_ipipe.c @@ -180,8 +180,7 @@ __ipipe_get_format(struct iss_ipipe_device *ipipe, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&ipipe->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); return &ipipe->formats[pad]; } diff --git a/drivers/staging/media/omap4iss/iss_ipipeif.c b/drivers/staging/media/omap4iss/iss_ipipeif.c index 5e7f25cd53acfd..5cafcd38438a48 100644 --- a/drivers/staging/media/omap4iss/iss_ipipeif.c +++ b/drivers/staging/media/omap4iss/iss_ipipeif.c @@ -361,8 +361,7 @@ __ipipeif_get_format(struct iss_ipipeif_device *ipipeif, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&ipipeif->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); return &ipipeif->formats[pad]; } diff --git a/drivers/staging/media/omap4iss/iss_resizer.c b/drivers/staging/media/omap4iss/iss_resizer.c index a5f8f9f1ab16d2..be26467ad65374 100644 --- a/drivers/staging/media/omap4iss/iss_resizer.c +++ b/drivers/staging/media/omap4iss/iss_resizer.c @@ -420,8 +420,7 @@ __resizer_get_format(struct iss_resizer_device *resizer, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&resizer->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); return &resizer->formats[pad]; } diff --git a/drivers/staging/media/starfive/camss/stf-isp.c b/drivers/staging/media/starfive/camss/stf-isp.c new file mode 100644 index 00000000000000..6bffffb2b20c5f --- /dev/null +++ b/drivers/staging/media/starfive/camss/stf-isp.c @@ -0,0 +1,385 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * stf_isp.c + * + * StarFive Camera Subsystem - ISP Module + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + */ +#include + +#include "stf-camss.h" + +#define SINK_FORMATS_INDEX 0 +#define SOURCE_FORMATS_INDEX 1 + +static int isp_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel); + +static const struct stf_isp_format isp_formats_sink[] = { + { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, + { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, + { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, + { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, +}; + +static const struct stf_isp_format isp_formats_source[] = { + { MEDIA_BUS_FMT_YUYV8_1_5X8, 8 }, +}; + +static const struct stf_isp_format_table isp_formats_st7110[] = { + { isp_formats_sink, ARRAY_SIZE(isp_formats_sink) }, + { isp_formats_source, ARRAY_SIZE(isp_formats_source) }, +}; + +static const struct stf_isp_format * +stf_g_fmt_by_mcode(const struct stf_isp_format_table *fmt_table, u32 mcode) +{ + unsigned int i; + + for (i = 0; i < fmt_table->nfmts; i++) { + if (fmt_table->fmts[i].code == mcode) + return &fmt_table->fmts[i]; + } + + return NULL; +} + +int stf_isp_init(struct stfcamss *stfcamss) +{ + struct stf_isp_dev *isp_dev = &stfcamss->isp_dev; + + isp_dev->stfcamss = stfcamss; + isp_dev->formats = isp_formats_st7110; + isp_dev->nformats = ARRAY_SIZE(isp_formats_st7110); + isp_dev->current_fmt = &isp_formats_source[0]; + + return 0; +} + +static int isp_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd); + struct v4l2_subdev_state *sd_state; + struct v4l2_mbus_framefmt *fmt; + struct v4l2_rect *crop; + + sd_state = v4l2_subdev_lock_and_get_active_state(sd); + fmt = v4l2_subdev_state_get_format(sd_state, STF_ISP_PAD_SINK); + crop = v4l2_subdev_state_get_crop(sd_state, STF_ISP_PAD_SRC); + + if (enable) { + stf_isp_reset(isp_dev); + stf_isp_init_cfg(isp_dev); + stf_isp_settings(isp_dev, crop, fmt->code); + stf_isp_stream_set(isp_dev); + } + + v4l2_subdev_call(isp_dev->source_subdev, video, s_stream, enable); + + v4l2_subdev_unlock_state(sd_state); + return 0; +} + +static void isp_try_format(struct stf_isp_dev *isp_dev, + struct v4l2_subdev_state *state, + unsigned int pad, + struct v4l2_mbus_framefmt *fmt) +{ + const struct stf_isp_format_table *formats; + + if (pad >= STF_ISP_PAD_MAX) { + fmt->colorspace = V4L2_COLORSPACE_SRGB; + return; + } + + if (pad == STF_ISP_PAD_SINK) + formats = &isp_dev->formats[SINK_FORMATS_INDEX]; + else if (pad == STF_ISP_PAD_SRC) + formats = &isp_dev->formats[SOURCE_FORMATS_INDEX]; + + fmt->width = clamp_t(u32, fmt->width, STFCAMSS_FRAME_MIN_WIDTH, + STFCAMSS_FRAME_MAX_WIDTH); + fmt->height = clamp_t(u32, fmt->height, STFCAMSS_FRAME_MIN_HEIGHT, + STFCAMSS_FRAME_MAX_HEIGHT); + fmt->height &= ~0x1; + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->flags = 0; + + if (!stf_g_fmt_by_mcode(formats, fmt->code)) + fmt->code = formats->fmts[0].code; +} + +static int isp_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd); + const struct stf_isp_format_table *formats; + + if (code->pad == STF_ISP_PAD_SINK) { + if (code->index > ARRAY_SIZE(isp_formats_sink)) + return -EINVAL; + + formats = &isp_dev->formats[SINK_FORMATS_INDEX]; + code->code = formats->fmts[code->index].code; + } else { + struct v4l2_mbus_framefmt *sink_fmt; + + if (code->index > ARRAY_SIZE(isp_formats_source)) + return -EINVAL; + + sink_fmt = v4l2_subdev_state_get_format(state, + STF_ISP_PAD_SRC); + + code->code = sink_fmt->code; + if (!code->code) + return -EINVAL; + } + code->flags = 0; + + return 0; +} + +static int isp_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *fmt) +{ + struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_state_get_format(state, fmt->pad); + if (!format) + return -EINVAL; + + isp_try_format(isp_dev, state, fmt->pad, &fmt->format); + *format = fmt->format; + + isp_dev->current_fmt = stf_g_fmt_by_mcode(&isp_dev->formats[fmt->pad], + fmt->format.code); + + /* Propagate to in crop */ + if (fmt->pad == STF_ISP_PAD_SINK) { + struct v4l2_subdev_selection sel = { 0 }; + + /* Reset sink pad compose selection */ + sel.which = fmt->which; + sel.pad = STF_ISP_PAD_SINK; + sel.target = V4L2_SEL_TGT_CROP; + sel.r.width = fmt->format.width; + sel.r.height = fmt->format.height; + isp_set_selection(sd, state, &sel); + } + + return 0; +} + +static const struct v4l2_rect stf_frame_min_crop = { + .width = STFCAMSS_FRAME_MIN_WIDTH, + .height = STFCAMSS_FRAME_MIN_HEIGHT, + .top = 0, + .left = 0, +}; + +static void isp_try_crop(struct stf_isp_dev *isp_dev, + struct v4l2_subdev_state *state, + struct v4l2_rect *crop) +{ + struct v4l2_mbus_framefmt *fmt = + v4l2_subdev_state_get_format(state, STF_ISP_PAD_SINK); + + const struct v4l2_rect bounds = { + .width = fmt->width, + .height = fmt->height, + .left = 0, + .top = 0, + }; + + v4l2_rect_set_min_size(crop, &stf_frame_min_crop); + v4l2_rect_map_inside(crop, &bounds); +} + +static int isp_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + struct v4l2_subdev_format fmt = { 0 }; + struct v4l2_rect *rect; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + if (sel->pad == STF_ISP_PAD_SINK) { + fmt.format = *v4l2_subdev_state_get_format(state, + sel->pad); + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = fmt.format.width; + sel->r.height = fmt.format.height; + } else if (sel->pad == STF_ISP_PAD_SRC) { + rect = v4l2_subdev_state_get_crop(state, sel->pad); + sel->r = *rect; + } + break; + + case V4L2_SEL_TGT_CROP: + rect = v4l2_subdev_state_get_crop(state, sel->pad); + if (!rect) + return -EINVAL; + + sel->r = *rect; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int isp_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd); + struct v4l2_rect *rect; + + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + if (sel->target == V4L2_SEL_TGT_CROP && + sel->pad == STF_ISP_PAD_SINK) { + struct v4l2_subdev_selection crop = { 0 }; + + rect = v4l2_subdev_state_get_crop(state, sel->pad); + if (!rect) + return -EINVAL; + + isp_try_crop(isp_dev, state, &sel->r); + *rect = sel->r; + + /* Reset source crop selection */ + crop.which = sel->which; + crop.pad = STF_ISP_PAD_SRC; + crop.target = V4L2_SEL_TGT_CROP; + crop.r = *rect; + isp_set_selection(sd, state, &crop); + } else if (sel->target == V4L2_SEL_TGT_CROP && + sel->pad == STF_ISP_PAD_SRC) { + struct v4l2_subdev_format fmt = { 0 }; + + rect = v4l2_subdev_state_get_crop(state, sel->pad); + if (!rect) + return -EINVAL; + + isp_try_crop(isp_dev, state, &sel->r); + *rect = sel->r; + + /* Reset source pad format width and height */ + fmt.which = sel->which; + fmt.pad = STF_ISP_PAD_SRC; + fmt.format.width = rect->width; + fmt.format.height = rect->height; + isp_set_format(sd, state, &fmt); + } + + dev_dbg(isp_dev->stfcamss->dev, "pad: %d sel(%d,%d)/%dx%d\n", + sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height); + + return 0; +} + +static int isp_init_formats(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + struct v4l2_subdev_format format = { + .pad = STF_ISP_PAD_SINK, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .format = { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .width = 1920, + .height = 1080 + } + }; + + return isp_set_format(sd, sd_state, &format); +} + +static const struct v4l2_subdev_video_ops isp_video_ops = { + .s_stream = isp_set_stream, +}; + +static const struct v4l2_subdev_pad_ops isp_pad_ops = { + .enum_mbus_code = isp_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = isp_set_format, + .get_selection = isp_get_selection, + .set_selection = isp_set_selection, +}; + +static const struct v4l2_subdev_ops isp_v4l2_ops = { + .video = &isp_video_ops, + .pad = &isp_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops isp_internal_ops = { + .init_state = isp_init_formats, +}; + +static const struct media_entity_operations isp_media_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +int stf_isp_register(struct stf_isp_dev *isp_dev, struct v4l2_device *v4l2_dev) +{ + struct v4l2_subdev *sd = &isp_dev->subdev; + struct media_pad *pads = isp_dev->pads; + int ret; + + v4l2_subdev_init(sd, &isp_v4l2_ops); + sd->internal_ops = &isp_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + snprintf(sd->name, ARRAY_SIZE(sd->name), "stf_isp"); + v4l2_set_subdevdata(sd, isp_dev); + + pads[STF_ISP_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[STF_ISP_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; + + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_ISP; + sd->entity.ops = &isp_media_ops; + ret = media_entity_pads_init(&sd->entity, STF_ISP_PAD_MAX, pads); + if (ret) { + dev_err(isp_dev->stfcamss->dev, + "Failed to init media entity: %d\n", ret); + return ret; + } + + ret = v4l2_subdev_init_finalize(sd); + if (ret) + goto err_entity_cleanup; + + ret = v4l2_device_register_subdev(v4l2_dev, sd); + if (ret) { + dev_err(isp_dev->stfcamss->dev, + "Failed to register subdev: %d\n", ret); + goto err_subdev_cleanup; + } + + return 0; + +err_subdev_cleanup: + v4l2_subdev_cleanup(sd); +err_entity_cleanup: + media_entity_cleanup(&sd->entity); + return ret; +} + +int stf_isp_unregister(struct stf_isp_dev *isp_dev) +{ + v4l2_device_unregister_subdev(&isp_dev->subdev); + v4l2_subdev_cleanup(&isp_dev->subdev); + media_entity_cleanup(&isp_dev->subdev.entity); + + return 0; +} diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c index ccbb530aa2e236..46a334b602f192 100644 --- a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c +++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c @@ -256,13 +256,13 @@ sun6i_isp_proc_mbus_format_prepare(struct v4l2_mbus_framefmt *mbus_format) mbus_format->xfer_func = V4L2_XFER_FUNC_DEFAULT; } -static int sun6i_isp_proc_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *state) +static int sun6i_isp_proc_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state) { struct sun6i_isp_device *isp_dev = v4l2_get_subdevdata(subdev); unsigned int pad = SUN6I_ISP_PROC_PAD_SINK_CSI; struct v4l2_mbus_framefmt *mbus_format = - v4l2_subdev_get_try_format(subdev, state, pad); + v4l2_subdev_state_get_format(state, pad); struct mutex *lock = &isp_dev->proc.lock; mutex_lock(lock); @@ -302,8 +302,8 @@ static int sun6i_isp_proc_get_fmt(struct v4l2_subdev *subdev, mutex_lock(lock); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *mbus_format = *v4l2_subdev_get_try_format(subdev, state, - format->pad); + *mbus_format = *v4l2_subdev_state_get_format(state, + format->pad); else *mbus_format = isp_dev->proc.mbus_format; @@ -325,7 +325,7 @@ static int sun6i_isp_proc_set_fmt(struct v4l2_subdev *subdev, sun6i_isp_proc_mbus_format_prepare(mbus_format); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *v4l2_subdev_get_try_format(subdev, state, format->pad) = + *v4l2_subdev_state_get_format(state, format->pad) = *mbus_format; else isp_dev->proc.mbus_format = *mbus_format; @@ -336,7 +336,6 @@ static int sun6i_isp_proc_set_fmt(struct v4l2_subdev *subdev, } static const struct v4l2_subdev_pad_ops sun6i_isp_proc_pad_ops = { - .init_cfg = sun6i_isp_proc_init_cfg, .enum_mbus_code = sun6i_isp_proc_enum_mbus_code, .get_fmt = sun6i_isp_proc_get_fmt, .set_fmt = sun6i_isp_proc_set_fmt, @@ -347,6 +346,10 @@ static const struct v4l2_subdev_ops sun6i_isp_proc_subdev_ops = { .pad = &sun6i_isp_proc_pad_ops, }; +static const struct v4l2_subdev_internal_ops sun6i_isp_proc_internal_ops = { + .init_state = sun6i_isp_proc_init_state, +}; + /* Media Entity */ static const struct media_entity_operations sun6i_isp_proc_entity_ops = { @@ -501,6 +504,7 @@ int sun6i_isp_proc_setup(struct sun6i_isp_device *isp_dev) /* V4L2 Subdev */ v4l2_subdev_init(subdev, &sun6i_isp_proc_subdev_ops); + subdev->internal_ops = &sun6i_isp_proc_internal_ops; strscpy(subdev->name, SUN6I_ISP_PROC_NAME, sizeof(subdev->name)); subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; subdev->owner = THIS_MODULE; diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c index 94171e62dee9e4..bc12a564e54f9c 100644 --- a/drivers/staging/media/tegra-video/vi.c +++ b/drivers/staging/media/tegra-video/vi.c @@ -439,6 +439,7 @@ static int __tegra_channel_try_format(struct tegra_vi_channel *chan, .which = V4L2_SUBDEV_FORMAT_ACTIVE, .target = V4L2_SEL_TGT_CROP_BOUNDS, }; + struct v4l2_rect *try_crop; int ret; subdev = tegra_channel_get_remote_source_subdev(chan); @@ -473,24 +474,25 @@ static int __tegra_channel_try_format(struct tegra_vi_channel *chan, * Attempt to obtain the format size from subdev. * If not available, try to get crop boundary from subdev. */ + try_crop = v4l2_subdev_state_get_crop(sd_state, 0); fse.code = fmtinfo->code; ret = v4l2_subdev_call(subdev, pad, enum_frame_size, sd_state, &fse); if (ret) { if (!v4l2_subdev_has_op(subdev, pad, get_selection)) { - sd_state->pads->try_crop.width = 0; - sd_state->pads->try_crop.height = 0; + try_crop->width = 0; + try_crop->height = 0; } else { ret = v4l2_subdev_call(subdev, pad, get_selection, NULL, &sdsel); if (ret) return -EINVAL; - sd_state->pads->try_crop.width = sdsel.r.width; - sd_state->pads->try_crop.height = sdsel.r.height; + try_crop->width = sdsel.r.width; + try_crop->height = sdsel.r.height; } } else { - sd_state->pads->try_crop.width = fse.max_width; - sd_state->pads->try_crop.height = fse.max_height; + try_crop->width = fse.max_width; + try_crop->height = fse.max_height; } ret = v4l2_subdev_call(subdev, pad, set_fmt, sd_state, &fmt); diff --git a/include/media/mipi-csi2.h b/include/media/mipi-csi2.h index c3d8f12234b1fb..40fc0264250d77 100644 --- a/include/media/mipi-csi2.h +++ b/include/media/mipi-csi2.h @@ -19,6 +19,7 @@ #define MIPI_CSI2_DT_NULL 0x10 #define MIPI_CSI2_DT_BLANKING 0x11 #define MIPI_CSI2_DT_EMBEDDED_8B 0x12 +#define MIPI_CSI2_DT_GENERIC_LONG(n) (0x13 + (n) - 1) /* 1..4 */ #define MIPI_CSI2_DT_YUV420_8B 0x18 #define MIPI_CSI2_DT_YUV420_10B 0x19 #define MIPI_CSI2_DT_YUV420_8B_LEGACY 0x1a diff --git a/include/media/v4l2-mc.h b/include/media/v4l2-mc.h index b39586dfba358a..ed0a44b6eadae8 100644 --- a/include/media/v4l2-mc.h +++ b/include/media/v4l2-mc.h @@ -143,6 +143,9 @@ int v4l2_create_fwnode_links(struct v4l2_subdev *src_sd, * v4l2_pipeline_pm_get - Increase the use count of a pipeline * @entity: The root entity of a pipeline * + * THIS FUNCTION IS DEPRECATED. DO NOT USE IN NEW DRIVERS. USE RUNTIME PM + * ON SUB-DEVICE DRIVERS INSTEAD. + * * Update the use count of all entities in the pipeline and power entities on. * * This function is intended to be called in video node open. It uses @@ -157,6 +160,9 @@ int v4l2_pipeline_pm_get(struct media_entity *entity); * v4l2_pipeline_pm_put - Decrease the use count of a pipeline * @entity: The root entity of a pipeline * + * THIS FUNCTION IS DEPRECATED. DO NOT USE IN NEW DRIVERS. USE RUNTIME PM + * ON SUB-DEVICE DRIVERS INSTEAD. + * * Update the use count of all entities in the pipeline and power entities off. * * This function is intended to be called in video node release. It uses diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index ab2a7ef61d420f..a33128d89b3f90 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -688,21 +688,14 @@ struct v4l2_subdev_ir_ops { /** * struct v4l2_subdev_pad_config - Used for storing subdev pad information. * - * @try_fmt: &struct v4l2_mbus_framefmt - * @try_crop: &struct v4l2_rect to be used for crop - * @try_compose: &struct v4l2_rect to be used for compose - * - * This structure only needs to be passed to the pad op if the 'which' field - * of the main argument is set to %V4L2_SUBDEV_FORMAT_TRY. For - * %V4L2_SUBDEV_FORMAT_ACTIVE it is safe to pass %NULL. - * - * Note: This struct is also used in active state, and the 'try' prefix is - * historical and to be removed. + * @format: &struct v4l2_mbus_framefmt + * @crop: &struct v4l2_rect to be used for crop + * @compose: &struct v4l2_rect to be used for compose */ struct v4l2_subdev_pad_config { - struct v4l2_mbus_framefmt try_fmt; - struct v4l2_rect try_crop; - struct v4l2_rect try_compose; + struct v4l2_mbus_framefmt format; + struct v4l2_rect crop; + struct v4l2_rect compose; }; /** @@ -741,12 +734,14 @@ struct v4l2_subdev_stream_configs { /** * struct v4l2_subdev_krouting - subdev routing table * + * @len_routes: length of routes array, in routes * @num_routes: number of routes * @routes: &struct v4l2_subdev_route * * This structure contains the routing table for a subdev. */ struct v4l2_subdev_krouting { + unsigned int len_routes; unsigned int num_routes; struct v4l2_subdev_route *routes; }; @@ -756,6 +751,7 @@ struct v4l2_subdev_krouting { * * @_lock: default for 'lock' * @lock: mutex for the state. May be replaced by the user. + * @sd: the sub-device which the state is related to * @pads: &struct v4l2_subdev_pad_config array * @routing: routing table for the subdev * @stream_configs: stream configurations (only for V4L2_SUBDEV_FL_STREAMS) @@ -768,6 +764,7 @@ struct v4l2_subdev_state { /* lock for the struct v4l2_subdev_state fields */ struct mutex _lock; struct mutex *lock; + struct v4l2_subdev *sd; struct v4l2_subdev_pad_config *pads; struct v4l2_subdev_krouting routing; struct v4l2_subdev_stream_configs stream_configs; @@ -776,7 +773,6 @@ struct v4l2_subdev_state { /** * struct v4l2_subdev_pad_ops - v4l2-subdev pad level operations * - * @init_cfg: initialize the pad config to default values * @enum_mbus_code: callback for VIDIOC_SUBDEV_ENUM_MBUS_CODE() ioctl handler * code. * @enum_frame_size: callback for VIDIOC_SUBDEV_ENUM_FRAME_SIZE() ioctl handler @@ -840,8 +836,6 @@ struct v4l2_subdev_state { * directly, use v4l2_subdev_disable_streams() instead. */ struct v4l2_subdev_pad_ops { - int (*init_cfg)(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state); int (*enum_mbus_code)(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_subdev_mbus_code_enum *code); @@ -918,6 +912,8 @@ struct v4l2_subdev_ops { /** * struct v4l2_subdev_internal_ops - V4L2 subdev internal ops * + * @init_state: initialize the subdev state to default values + * * @registered: called when this subdev is registered. When called the v4l2_dev * field is set to the correct v4l2_device. * @@ -943,6 +939,8 @@ struct v4l2_subdev_ops { * these ops. */ struct v4l2_subdev_internal_ops { + int (*init_state)(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state); int (*registered)(struct v4l2_subdev *sd); void (*unregistered)(struct v4l2_subdev *sd); int (*open)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh); @@ -1038,10 +1036,11 @@ struct v4l2_subdev_platform_data { * @active_state: Active state for the subdev (NULL for subdevs tracking the * state internally). Initialized by calling * v4l2_subdev_init_finalize(). - * @enabled_streams: Bitmask of enabled streams used by - * v4l2_subdev_enable_streams() and - * v4l2_subdev_disable_streams() helper functions for fallback - * cases. + * @enabled_pads: Bitmask of enabled pads used by v4l2_subdev_enable_streams() + * and v4l2_subdev_disable_streams() helper functions for + * fallback cases. + * @s_stream_enabled: Tracks whether streaming has been enabled with s_stream. + * This is only for call_s_stream() internal use. * * Each instance of a subdev driver should create this struct, either * stand-alone or embedded in a larger struct. @@ -1089,7 +1088,8 @@ struct v4l2_subdev { * doesn't support it. */ struct v4l2_subdev_state *active_state; - u64 enabled_streams; + u64 enabled_pads; + bool s_stream_enabled; }; @@ -1146,8 +1146,8 @@ struct v4l2_subdev_fh { #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) /** - * v4l2_subdev_get_pad_format - ancillary routine to call - * &struct v4l2_subdev_pad_config->try_fmt + * v4l2_subdev_get_pad_format - ancillary routine to get + * &struct v4l2_subdev_pad_config->format * * @sd: pointer to &struct v4l2_subdev * @state: pointer to &struct v4l2_subdev_state @@ -1162,12 +1162,12 @@ v4l2_subdev_get_pad_format(struct v4l2_subdev *sd, return NULL; if (WARN_ON(pad >= sd->entity.num_pads)) pad = 0; - return &state->pads[pad].try_fmt; + return &state->pads[pad].format; } /** - * v4l2_subdev_get_pad_crop - ancillary routine to call - * &struct v4l2_subdev_pad_config->try_crop + * v4l2_subdev_get_pad_crop - ancillary routine to get + * &struct v4l2_subdev_pad_config->crop * * @sd: pointer to &struct v4l2_subdev * @state: pointer to &struct v4l2_subdev_state. @@ -1182,12 +1182,12 @@ v4l2_subdev_get_pad_crop(struct v4l2_subdev *sd, return NULL; if (WARN_ON(pad >= sd->entity.num_pads)) pad = 0; - return &state->pads[pad].try_crop; + return &state->pads[pad].crop; } /** - * v4l2_subdev_get_pad_compose - ancillary routine to call - * &struct v4l2_subdev_pad_config->try_compose + * v4l2_subdev_get_pad_compose - ancillary routine to get + * &struct v4l2_subdev_pad_config->compose * * @sd: pointer to &struct v4l2_subdev * @state: pointer to &struct v4l2_subdev_state. @@ -1202,7 +1202,7 @@ v4l2_subdev_get_pad_compose(struct v4l2_subdev *sd, return NULL; if (WARN_ON(pad >= sd->entity.num_pads)) pad = 0; - return &state->pads[pad].try_compose; + return &state->pads[pad].compose; } /* @@ -1313,6 +1313,12 @@ int v4l2_subdev_link_validate_default(struct v4l2_subdev *sd, * calls v4l2_subdev_link_validate_default() to ensure that * width, height and the media bus pixel code are equal on both * source and sink of the link. + * + * The function can be used as a drop-in &media_entity_ops.link_validate + * implementation for v4l2_subdev instances. It supports all links between + * subdevs, as well as links between subdevs and video devices, provided that + * the video devices also implement their &media_entity_ops.link_validate + * operation. */ int v4l2_subdev_link_validate(struct media_link *link); @@ -1393,88 +1399,84 @@ int __v4l2_subdev_init_finalize(struct v4l2_subdev *sd, const char *name, */ void v4l2_subdev_cleanup(struct v4l2_subdev *sd); -/** - * v4l2_subdev_lock_state() - Locks the subdev state - * @state: The subdevice state - * - * Locks the given subdev state. - * - * The state must be unlocked with v4l2_subdev_unlock_state() after use. +/* + * A macro to generate the macro or function name for sub-devices state access + * wrapper macros below. */ -static inline void v4l2_subdev_lock_state(struct v4l2_subdev_state *state) -{ - mutex_lock(state->lock); -} +#define __v4l2_subdev_state_gen_call(NAME, _1, ARG, ...) \ + __v4l2_subdev_state_get_ ## NAME ## ARG /** - * v4l2_subdev_unlock_state() - Unlocks the subdev state - * @state: The subdevice state - * - * Unlocks the given subdev state. - */ -static inline void v4l2_subdev_unlock_state(struct v4l2_subdev_state *state) -{ - mutex_unlock(state->lock); -} - -/** - * v4l2_subdev_get_unlocked_active_state() - Checks that the active subdev state - * is unlocked and returns it - * @sd: The subdevice + * v4l2_subdev_state_get_format() - Get pointer to a stream format + * @state: subdevice state + * @pad: pad id + * @...: stream id (optional argument) * - * Returns the active state for the subdevice, or NULL if the subdev does not - * support active state. If the state is not NULL, calls - * lockdep_assert_not_held() to issue a warning if the state is locked. + * This returns a pointer to &struct v4l2_mbus_framefmt for the given pad + + * stream in the subdev state. * - * This function is to be used e.g. when getting the active state for the sole - * purpose of passing it forward, without accessing the state fields. + * For stream-unaware drivers the format for the corresponding pad is returned. + * If the pad does not exist, NULL is returned. */ -static inline struct v4l2_subdev_state * -v4l2_subdev_get_unlocked_active_state(struct v4l2_subdev *sd) -{ - if (sd->active_state) - lockdep_assert_not_held(sd->active_state->lock); - return sd->active_state; -} +/* + * Wrap v4l2_subdev_state_get_format(), allowing the function to be called with + * two or three arguments. The purpose of the __v4l2_subdev_state_get_format() + * macro below is to come up with the name of the function or macro to call, + * using the last two arguments (_stream and _pad). The selected function or + * macro is then called using the arguments specified by the caller. A similar + * arrangement is used for v4l2_subdev_state_crop() and + * v4l2_subdev_state_compose() below. + */ +#define v4l2_subdev_state_get_format(state, pad, ...) \ + __v4l2_subdev_state_gen_call(format, ##__VA_ARGS__, , _pad) \ + (state, pad, ##__VA_ARGS__) +#define __v4l2_subdev_state_get_format_pad(state, pad) \ + __v4l2_subdev_state_get_format(state, pad, 0) +struct v4l2_mbus_framefmt * +__v4l2_subdev_state_get_format(struct v4l2_subdev_state *state, + unsigned int pad, u32 stream); /** - * v4l2_subdev_get_locked_active_state() - Checks that the active subdev state - * is locked and returns it - * - * @sd: The subdevice + * v4l2_subdev_state_get_crop() - Get pointer to a stream crop rectangle + * @state: subdevice state + * @pad: pad id + * @...: stream id (optional argument) * - * Returns the active state for the subdevice, or NULL if the subdev does not - * support active state. If the state is not NULL, calls lockdep_assert_held() - * to issue a warning if the state is not locked. + * This returns a pointer to crop rectangle for the given pad + stream in the + * subdev state. * - * This function is to be used when the caller knows that the active state is - * already locked. + * For stream-unaware drivers the crop rectangle for the corresponding pad is + * returned. If the pad does not exist, NULL is returned. */ -static inline struct v4l2_subdev_state * -v4l2_subdev_get_locked_active_state(struct v4l2_subdev *sd) -{ - if (sd->active_state) - lockdep_assert_held(sd->active_state->lock); - return sd->active_state; -} +#define v4l2_subdev_state_get_crop(state, pad, ...) \ + __v4l2_subdev_state_gen_call(crop, ##__VA_ARGS__, , _pad) \ + (state, pad, ##__VA_ARGS__) +#define __v4l2_subdev_state_get_crop_pad(state, pad) \ + __v4l2_subdev_state_get_crop(state, pad, 0) +struct v4l2_rect * +__v4l2_subdev_state_get_crop(struct v4l2_subdev_state *state, unsigned int pad, + u32 stream); /** - * v4l2_subdev_lock_and_get_active_state() - Locks and returns the active subdev - * state for the subdevice - * @sd: The subdevice + * v4l2_subdev_state_get_compose() - Get pointer to a stream compose rectangle + * @state: subdevice state + * @pad: pad id + * @...: stream id (optional argument) * - * Returns the locked active state for the subdevice, or NULL if the subdev - * does not support active state. + * This returns a pointer to compose rectangle for the given pad + stream in the + * subdev state. * - * The state must be unlocked with v4l2_subdev_unlock_state() after use. + * For stream-unaware drivers the compose rectangle for the corresponding pad is + * returned. If the pad does not exist, NULL is returned. */ -static inline struct v4l2_subdev_state * -v4l2_subdev_lock_and_get_active_state(struct v4l2_subdev *sd) -{ - if (sd->active_state) - v4l2_subdev_lock_state(sd->active_state); - return sd->active_state; -} +#define v4l2_subdev_state_get_compose(state, pad, ...) \ + __v4l2_subdev_state_gen_call(compose, ##__VA_ARGS__, , _pad) \ + (state, pad, ##__VA_ARGS__) +#define __v4l2_subdev_state_get_compose_pad(state, pad) \ + __v4l2_subdev_state_get_compose(state, pad, 0) +struct v4l2_rect * +__v4l2_subdev_state_get_compose(struct v4l2_subdev_state *state, + unsigned int pad, u32 stream); #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) @@ -1540,52 +1542,6 @@ int v4l2_subdev_set_routing_with_fmt(struct v4l2_subdev *sd, const struct v4l2_subdev_krouting *routing, const struct v4l2_mbus_framefmt *fmt); -/** - * v4l2_subdev_state_get_stream_format() - Get pointer to a stream format - * @state: subdevice state - * @pad: pad id - * @stream: stream id - * - * This returns a pointer to &struct v4l2_mbus_framefmt for the given pad + - * stream in the subdev state. - * - * If the state does not contain the given pad + stream, NULL is returned. - */ -struct v4l2_mbus_framefmt * -v4l2_subdev_state_get_stream_format(struct v4l2_subdev_state *state, - unsigned int pad, u32 stream); - -/** - * v4l2_subdev_state_get_stream_crop() - Get pointer to a stream crop rectangle - * @state: subdevice state - * @pad: pad id - * @stream: stream id - * - * This returns a pointer to crop rectangle for the given pad + stream in the - * subdev state. - * - * If the state does not contain the given pad + stream, NULL is returned. - */ -struct v4l2_rect * -v4l2_subdev_state_get_stream_crop(struct v4l2_subdev_state *state, - unsigned int pad, u32 stream); - -/** - * v4l2_subdev_state_get_stream_compose() - Get pointer to a stream compose - * rectangle - * @state: subdevice state - * @pad: pad id - * @stream: stream id - * - * This returns a pointer to compose rectangle for the given pad + stream in the - * subdev state. - * - * If the state does not contain the given pad + stream, NULL is returned. - */ -struct v4l2_rect * -v4l2_subdev_state_get_stream_compose(struct v4l2_subdev_state *state, - unsigned int pad, u32 stream); - /** * v4l2_subdev_routing_find_opposite_end() - Find the opposite stream * @routing: routing used to find the opposite side @@ -1787,6 +1743,129 @@ int v4l2_subdev_s_stream_helper(struct v4l2_subdev *sd, int enable); #endif /* CONFIG_MEDIA_CONTROLLER */ +/** + * v4l2_subdev_lock_state() - Locks the subdev state + * @state: The subdevice state + * + * Locks the given subdev state. + * + * The state must be unlocked with v4l2_subdev_unlock_state() after use. + */ +static inline void v4l2_subdev_lock_state(struct v4l2_subdev_state *state) +{ + mutex_lock(state->lock); +} + +/** + * v4l2_subdev_unlock_state() - Unlocks the subdev state + * @state: The subdevice state + * + * Unlocks the given subdev state. + */ +static inline void v4l2_subdev_unlock_state(struct v4l2_subdev_state *state) +{ + mutex_unlock(state->lock); +} + +/** + * v4l2_subdev_lock_states - Lock two sub-device states + * @state1: One subdevice state + * @state2: The other subdevice state + * + * Locks the state of two sub-devices. + * + * The states must be unlocked with v4l2_subdev_unlock_states() after use. + * + * This differs from calling v4l2_subdev_lock_state() on both states so that if + * the states share the same lock, the lock is acquired only once (so no + * deadlock occurs). The caller is responsible for ensuring the locks will + * always be acquired in the same order. + */ +static inline void v4l2_subdev_lock_states(struct v4l2_subdev_state *state1, + struct v4l2_subdev_state *state2) +{ + mutex_lock(state1->lock); + if (state1->lock != state2->lock) + mutex_lock(state2->lock); +} + +/** + * v4l2_subdev_unlock_states() - Unlock two sub-device states + * @state1: One subdevice state + * @state2: The other subdevice state + * + * Unlocks the state of two sub-devices. + * + * This differs from calling v4l2_subdev_unlock_state() on both states so that + * if the states share the same lock, the lock is released only once. + */ +static inline void v4l2_subdev_unlock_states(struct v4l2_subdev_state *state1, + struct v4l2_subdev_state *state2) +{ + mutex_unlock(state1->lock); + if (state1->lock != state2->lock) + mutex_unlock(state2->lock); +} + +/** + * v4l2_subdev_get_unlocked_active_state() - Checks that the active subdev state + * is unlocked and returns it + * @sd: The subdevice + * + * Returns the active state for the subdevice, or NULL if the subdev does not + * support active state. If the state is not NULL, calls + * lockdep_assert_not_held() to issue a warning if the state is locked. + * + * This function is to be used e.g. when getting the active state for the sole + * purpose of passing it forward, without accessing the state fields. + */ +static inline struct v4l2_subdev_state * +v4l2_subdev_get_unlocked_active_state(struct v4l2_subdev *sd) +{ + if (sd->active_state) + lockdep_assert_not_held(sd->active_state->lock); + return sd->active_state; +} + +/** + * v4l2_subdev_get_locked_active_state() - Checks that the active subdev state + * is locked and returns it + * + * @sd: The subdevice + * + * Returns the active state for the subdevice, or NULL if the subdev does not + * support active state. If the state is not NULL, calls lockdep_assert_held() + * to issue a warning if the state is not locked. + * + * This function is to be used when the caller knows that the active state is + * already locked. + */ +static inline struct v4l2_subdev_state * +v4l2_subdev_get_locked_active_state(struct v4l2_subdev *sd) +{ + if (sd->active_state) + lockdep_assert_held(sd->active_state->lock); + return sd->active_state; +} + +/** + * v4l2_subdev_lock_and_get_active_state() - Locks and returns the active subdev + * state for the subdevice + * @sd: The subdevice + * + * Returns the locked active state for the subdevice, or NULL if the subdev + * does not support active state. + * + * The state must be unlocked with v4l2_subdev_unlock_state() after use. + */ +static inline struct v4l2_subdev_state * +v4l2_subdev_lock_and_get_active_state(struct v4l2_subdev *sd) +{ + if (sd->active_state) + v4l2_subdev_lock_state(sd->active_state); + return sd->active_state; +} + /** * v4l2_subdev_init - initializes the sub-device struct * @@ -1916,4 +1995,17 @@ extern const struct v4l2_subdev_ops v4l2_subdev_call_wrappers; void v4l2_subdev_notify_event(struct v4l2_subdev *sd, const struct v4l2_event *ev); +/** + * v4l2_subdev_is_streaming() - Returns if the subdevice is streaming + * @sd: The subdevice + * + * v4l2_subdev_is_streaming() tells if the subdevice is currently streaming. + * "Streaming" here means whether .s_stream() or .enable_streams() has been + * successfully called, and the streaming has not yet been disabled. + * + * If the subdevice implements .enable_streams() this function must be called + * while holding the active state lock. + */ +bool v4l2_subdev_is_streaming(struct v4l2_subdev *sd); + #endif /* _V4L2_SUBDEV_H */ diff --git a/include/uapi/linux/media-bus-format.h b/include/uapi/linux/media-bus-format.h index 7fbeb5a95f4518..29d26a59b784fe 100644 --- a/include/uapi/linux/media-bus-format.h +++ b/include/uapi/linux/media-bus-format.h @@ -176,4 +176,17 @@ /* Sensor ancillary metadata formats - next is 0x7002 */ #define MEDIA_BUS_FMT_SENSOR_DATA 0x7002 +/* Generic line based metadata formats for serial buses. Next is 0x8008. */ +#define MEDIA_BUS_FMT_META_8 0x8001 +#define MEDIA_BUS_FMT_META_10 0x8002 +#define MEDIA_BUS_FMT_META_12 0x8003 +#define MEDIA_BUS_FMT_META_14 0x8004 +#define MEDIA_BUS_FMT_META_16 0x8005 +#define MEDIA_BUS_FMT_META_20 0x8006 +#define MEDIA_BUS_FMT_META_24 0x8007 + +/* Specific metadata formats. Next is 0x9003. */ +#define MEDIA_BUS_FMT_CCS_EMBEDDED 0x9001 +#define MEDIA_BUS_FMT_OV2740_EMBEDDED 0x9002 + #endif /* __LINUX_MEDIA_BUS_FORMAT_H */ diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h index 1c80b1d6bbaf36..80cfd12a43fc1a 100644 --- a/include/uapi/linux/media.h +++ b/include/uapi/linux/media.h @@ -208,6 +208,7 @@ struct media_entity_desc { #define MEDIA_PAD_FL_SINK (1U << 0) #define MEDIA_PAD_FL_SOURCE (1U << 1) #define MEDIA_PAD_FL_MUST_CONNECT (1U << 2) +#define MEDIA_PAD_FL_INTERNAL (1U << 3) struct media_pad_desc { __u32 entity; /* entity ID */ diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe_config.h b/include/uapi/linux/media/raspberrypi/pisp_fe_config.h similarity index 61% rename from drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe_config.h rename to include/uapi/linux/media/raspberrypi/pisp_fe_config.h index 4b76f640894b72..77237460a3b5bd 100644 --- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe_config.h +++ b/include/uapi/linux/media/raspberrypi/pisp_fe_config.h @@ -5,12 +5,13 @@ * Copyright (C) 2021 - Raspberry Pi Ltd. * */ -#ifndef _PISP_FE_CONFIG_ -#define _PISP_FE_CONFIG_ +#ifndef _UAPI_PISP_FE_CONFIG_ +#define _UAPI_PISP_FE_CONFIG_ -#include +#include -#include "pisp_statistics.h" +#include "pisp_common.h" +#include "pisp_fe_statistics.h" #define PISP_FE_NUM_OUTPUTS 2 @@ -54,182 +55,182 @@ enum pisp_fe_dirty { }; struct pisp_fe_global_config { - u32 enables; - u8 bayer_order; - u8 pad[3]; -}; + __u32 enables; + __u8 bayer_order; + __u8 pad[3]; +} __attribute__((packed)); struct pisp_fe_input_axi_config { /* burst length minus one, in the range 0..15; OR'd with flags */ - u8 maxlen_flags; + __u8 maxlen_flags; /* { prot[2:0], cache[3:0] } fields */ - u8 cache_prot; + __u8 cache_prot; /* QoS (only 4 LS bits are used) */ - u16 qos; -}; + __u16 qos; +} __attribute__((packed)); struct pisp_fe_output_axi_config { /* burst length minus one, in the range 0..15; OR'd with flags */ - u8 maxlen_flags; + __u8 maxlen_flags; /* { prot[2:0], cache[3:0] } fields */ - u8 cache_prot; + __u8 cache_prot; /* QoS (4 bitfields of 4 bits each for different panic levels) */ - u16 qos; + __u16 qos; /* For Panic mode: Output FIFO panic threshold */ - u16 thresh; + __u16 thresh; /* For Panic mode: Output FIFO statistics throttle threshold */ - u16 throttle; -}; + __u16 throttle; +} __attribute__((packed)); struct pisp_fe_input_config { - u8 streaming; - u8 pad[3]; + __u8 streaming; + __u8 pad[3]; struct pisp_image_format_config format; struct pisp_fe_input_axi_config axi; /* Extra cycles delay before issuing each burst request */ - u8 holdoff; - u8 pad2[3]; -}; + __u8 holdoff; + __u8 pad2[3]; +} __attribute__((packed)); struct pisp_fe_output_config { struct pisp_image_format_config format; - u16 ilines; - u8 pad[2]; -}; + __u16 ilines; + __u8 pad[2]; +} __attribute__((packed)); struct pisp_fe_input_buffer_config { - u32 addr_lo; - u32 addr_hi; - u16 frame_id; - u16 pad; -}; + __u32 addr_lo; + __u32 addr_hi; + __u16 frame_id; + __u16 pad; +} __attribute__((packed)); #define PISP_FE_DECOMPAND_LUT_SIZE 65 struct pisp_fe_decompand_config { - u16 lut[PISP_FE_DECOMPAND_LUT_SIZE]; - u16 pad; -}; + __u16 lut[PISP_FE_DECOMPAND_LUT_SIZE]; + __u16 pad; +} __attribute__((packed)); struct pisp_fe_dpc_config { - u8 coeff_level; - u8 coeff_range; - u8 coeff_range2; + __u8 coeff_level; + __u8 coeff_range; + __u8 coeff_range2; #define PISP_FE_DPC_FLAG_FOLDBACK 1 #define PISP_FE_DPC_FLAG_VFLAG 2 - u8 flags; -}; + __u8 flags; +} __attribute__((packed)); #define PISP_FE_LSC_LUT_SIZE 16 struct pisp_fe_lsc_config { - u8 shift; - u8 pad0; - u16 scale; - u16 centre_x; - u16 centre_y; - u16 lut[PISP_FE_LSC_LUT_SIZE]; -}; + __u8 shift; + __u8 pad0; + __u16 scale; + __u16 centre_x; + __u16 centre_y; + __u16 lut[PISP_FE_LSC_LUT_SIZE]; +} __attribute__((packed)); struct pisp_fe_rgby_config { - u16 gain_r; - u16 gain_g; - u16 gain_b; - u8 maxflag; - u8 pad; -}; + __u16 gain_r; + __u16 gain_g; + __u16 gain_b; + __u8 maxflag; + __u8 pad; +} __attribute__((packed)); struct pisp_fe_agc_stats_config { - u16 offset_x; - u16 offset_y; - u16 size_x; - u16 size_y; + __u16 offset_x; + __u16 offset_y; + __u16 size_x; + __u16 size_y; /* each weight only 4 bits */ - u8 weights[PISP_AGC_STATS_NUM_ZONES / 2]; - u16 row_offset_x; - u16 row_offset_y; - u16 row_size_x; - u16 row_size_y; - u8 row_shift; - u8 float_shift; - u8 pad1[2]; -}; + __u8 weights[PISP_AGC_STATS_NUM_ZONES / 2]; + __u16 row_offset_x; + __u16 row_offset_y; + __u16 row_size_x; + __u16 row_size_y; + __u8 row_shift; + __u8 float_shift; + __u8 pad1[2]; +} __attribute__((packed)); struct pisp_fe_awb_stats_config { - u16 offset_x; - u16 offset_y; - u16 size_x; - u16 size_y; - u8 shift; - u8 pad[3]; - u16 r_lo; - u16 r_hi; - u16 g_lo; - u16 g_hi; - u16 b_lo; - u16 b_hi; -}; + __u16 offset_x; + __u16 offset_y; + __u16 size_x; + __u16 size_y; + __u8 shift; + __u8 pad[3]; + __u16 r_lo; + __u16 r_hi; + __u16 g_lo; + __u16 g_hi; + __u16 b_lo; + __u16 b_hi; +} __attribute__((packed)); struct pisp_fe_floating_stats_region { - u16 offset_x; - u16 offset_y; - u16 size_x; - u16 size_y; -}; + __u16 offset_x; + __u16 offset_y; + __u16 size_x; + __u16 size_y; +} __attribute__((packed)); struct pisp_fe_floating_stats_config { struct pisp_fe_floating_stats_region - regions[PISP_FLOATING_STATS_NUM_ZONES]; -}; + regions[PISP_FLOATING_STATS_NUM_ZONES]; +} __attribute__((packed)); #define PISP_FE_CDAF_NUM_WEIGHTS 8 struct pisp_fe_cdaf_stats_config { - u16 noise_constant; - u16 noise_slope; - u16 offset_x; - u16 offset_y; - u16 size_x; - u16 size_y; - u16 skip_x; - u16 skip_y; - u32 mode; -}; + __u16 noise_constant; + __u16 noise_slope; + __u16 offset_x; + __u16 offset_y; + __u16 size_x; + __u16 size_y; + __u16 skip_x; + __u16 skip_y; + __u32 mode; +} __attribute__((packed)); struct pisp_fe_stats_buffer_config { - u32 addr_lo; - u32 addr_hi; -}; + __u32 addr_lo; + __u32 addr_hi; +} __attribute__((packed)); struct pisp_fe_crop_config { - u16 offset_x; - u16 offset_y; - u16 width; - u16 height; -}; + __u16 offset_x; + __u16 offset_y; + __u16 width; + __u16 height; +} __attribute__((packed)); enum pisp_fe_downscale_flags { - DOWNSCALE_BAYER = - 1, /* downscale the four Bayer components independently... */ - DOWNSCALE_BIN = - 2 /* ...without trying to preserve their spatial relationship */ + /* downscale the four Bayer components independently... */ + DOWNSCALE_BAYER = 1, + /* ...without trying to preserve their spatial relationship */ + DOWNSCALE_BIN = 2, }; struct pisp_fe_downscale_config { - u8 xin; - u8 xout; - u8 yin; - u8 yout; - u8 flags; /* enum pisp_fe_downscale_flags */ - u8 pad[3]; - u16 output_width; - u16 output_height; -}; + __u8 xin; + __u8 xout; + __u8 yin; + __u8 yout; + __u8 flags; /* enum pisp_fe_downscale_flags */ + __u8 pad[3]; + __u16 output_width; + __u16 output_height; +} __attribute__((packed)); struct pisp_fe_output_buffer_config { - u32 addr_lo; - u32 addr_hi; -}; + __u32 addr_lo; + __u32 addr_hi; +} __attribute__((packed)); /* Each of the two output channels/branches: */ struct pisp_fe_output_branch_config { @@ -237,8 +238,8 @@ struct pisp_fe_output_branch_config { struct pisp_fe_downscale_config downscale; struct pisp_compress_config compress; struct pisp_fe_output_config output; - u32 pad; -}; + __u32 pad; +} __attribute__((packed)); /* And finally one to rule them all: */ struct pisp_fe_config { @@ -254,7 +255,7 @@ struct pisp_fe_config { struct pisp_bla_config bla; struct pisp_fe_dpc_config dpc; struct pisp_fe_crop_config stats_crop; - u32 spare1; /* placeholder for future decimate configuration */ + __u32 spare1; /* placeholder for future decimate configuration */ struct pisp_bla_config blc; struct pisp_fe_rgby_config rgby; struct pisp_fe_lsc_config lsc; @@ -265,8 +266,8 @@ struct pisp_fe_config { struct pisp_fe_output_axi_config output_axi; struct pisp_fe_output_branch_config ch[PISP_FE_NUM_OUTPUTS]; /* non-register fields: */ - u32 dirty_flags; /* these use pisp_fe_enable */ - u32 dirty_flags_extra; /* these use pisp_fe_dirty */ -}; + __u32 dirty_flags; /* these use pisp_fe_enable */ + __u32 dirty_flags_extra; /* these use pisp_fe_dirty */ +} __attribute__((packed)); -#endif /* _PISP_FE_CONFIG_ */ +#endif /* _UAPI_PISP_FE_CONFIG_ */ diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_statistics.h b/include/uapi/linux/media/raspberrypi/pisp_fe_statistics.h similarity index 68% rename from drivers/media/platform/raspberrypi/rp1_cfe/pisp_statistics.h rename to include/uapi/linux/media/raspberrypi/pisp_fe_statistics.h index d8d7fcb57fecc0..a7d42985aee813 100644 --- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_statistics.h +++ b/include/uapi/linux/media/raspberrypi/pisp_fe_statistics.h @@ -5,8 +5,10 @@ * Copyright (C) 2021 - Raspberry Pi Ltd. * */ -#ifndef _PISP_FE_STATISTICS_H_ -#define _PISP_FE_STATISTICS_H_ +#ifndef _UAPI_PISP_FE_STATISTICS_H_ +#define _UAPI_PISP_FE_STATISTICS_H_ + +#include #define PISP_FLOATING_STATS_NUM_ZONES 4 #define PISP_AGC_STATS_NUM_BINS 1024 @@ -15,48 +17,48 @@ #define PISP_AGC_STATS_NUM_ROW_SUMS 512 struct pisp_agc_statistics_zone { - u64 Y_sum; - u32 counted; - u32 pad; -}; + __u64 Y_sum; + __u32 counted; + __u32 pad; +} __attribute__((packed)); struct pisp_agc_statistics { - u32 row_sums[PISP_AGC_STATS_NUM_ROW_SUMS]; + __u32 row_sums[PISP_AGC_STATS_NUM_ROW_SUMS]; /* * 32-bits per bin means an image (just less than) 16384x16384 pixels * in size can weight every pixel from 0 to 15. */ - u32 histogram[PISP_AGC_STATS_NUM_BINS]; + __u32 histogram[PISP_AGC_STATS_NUM_BINS]; struct pisp_agc_statistics_zone floating[PISP_FLOATING_STATS_NUM_ZONES]; -}; +} __attribute__((packed)); #define PISP_AWB_STATS_SIZE 32 #define PISP_AWB_STATS_NUM_ZONES (PISP_AWB_STATS_SIZE * PISP_AWB_STATS_SIZE) struct pisp_awb_statistics_zone { - u32 R_sum; - u32 G_sum; - u32 B_sum; - u32 counted; -}; + __u32 R_sum; + __u32 G_sum; + __u32 B_sum; + __u32 counted; +} __attribute__((packed)); struct pisp_awb_statistics { struct pisp_awb_statistics_zone zones[PISP_AWB_STATS_NUM_ZONES]; struct pisp_awb_statistics_zone floating[PISP_FLOATING_STATS_NUM_ZONES]; -}; +} __attribute__((packed)); #define PISP_CDAF_STATS_SIZE 8 #define PISP_CDAF_STATS_NUM_FOMS (PISP_CDAF_STATS_SIZE * PISP_CDAF_STATS_SIZE) struct pisp_cdaf_statistics { - u64 foms[PISP_CDAF_STATS_NUM_FOMS]; - u64 floating[PISP_FLOATING_STATS_NUM_ZONES]; -}; + __u64 foms[PISP_CDAF_STATS_NUM_FOMS]; + __u64 floating[PISP_FLOATING_STATS_NUM_ZONES]; +} __attribute__((packed)); struct pisp_statistics { struct pisp_awb_statistics awb; struct pisp_agc_statistics agc; struct pisp_cdaf_statistics cdaf; -}; +} __attribute__((packed)); -#endif /* _PISP_FE_STATISTICS_H_ */ +#endif /* _UAPI_PISP_FE_STATISTICS_H_ */ diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 22bfc38787cc34..bd1cc0646aad3e 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -205,7 +205,7 @@ enum v4l2_colorfx { /* The base for the bcm2835-isp driver controls. * We reserve 16 controls for this driver. */ -#define V4L2_CID_USER_BCM2835_ISP_BASE (V4L2_CID_USER_BASE + 0x10e0) +#define V4L2_CID_USER_BCM2835_ISP_BASE (V4L2_CID_USER_BASE + 0x11d0) /* * The base for IMX500 driver controls. diff --git a/include/uapi/linux/v4l2-mediabus.h b/include/uapi/linux/v4l2-mediabus.h index 6b07b73473b5ff..946520bc49f10c 100644 --- a/include/uapi/linux/v4l2-mediabus.h +++ b/include/uapi/linux/v4l2-mediabus.h @@ -19,12 +19,18 @@ * @width: image width * @height: image height * @code: data format code (from enum v4l2_mbus_pixelcode) - * @field: used interlacing type (from enum v4l2_field) - * @colorspace: colorspace of the data (from enum v4l2_colorspace) - * @ycbcr_enc: YCbCr encoding of the data (from enum v4l2_ycbcr_encoding) - * @hsv_enc: HSV encoding of the data (from enum v4l2_hsv_encoding) - * @quantization: quantization of the data (from enum v4l2_quantization) - * @xfer_func: transfer function of the data (from enum v4l2_xfer_func) + * @field: used interlacing type (from enum v4l2_field), zero for metadata + * mbus codes + * @colorspace: colorspace of the data (from enum v4l2_colorspace), zero on + * metadata mbus codes + * @ycbcr_enc: YCbCr encoding of the data (from enum v4l2_ycbcr_encoding), zero + * for metadata mbus codes + * @hsv_enc: HSV encoding of the data (from enum v4l2_hsv_encoding), zero for + * metadata mbus codes + * @quantization: quantization of the data (from enum v4l2_quantization), zero + * for metadata mbus codes + * @xfer_func: transfer function of the data (from enum v4l2_xfer_func), zero + * for metadata mbus codes * @flags: flags (V4L2_MBUS_FRAMEFMT_*) * @reserved: reserved bytes that can be later used */ diff --git a/include/uapi/linux/v4l2-subdev.h b/include/uapi/linux/v4l2-subdev.h index b383c2fe0cf354..88e21feb473775 100644 --- a/include/uapi/linux/v4l2-subdev.h +++ b/include/uapi/linux/v4l2-subdev.h @@ -198,6 +198,11 @@ struct v4l2_subdev_capability { * on a video node. */ #define V4L2_SUBDEV_ROUTE_FL_ACTIVE (1U << 0) +/* + * Is the route immutable. The ACTIVE flag of an immutable route may not be + * changed. + */ +#define V4L2_SUBDEV_ROUTE_FL_IMMUTABLE (1U << 1) /** * struct v4l2_subdev_route - A route inside a subdev @@ -222,15 +227,19 @@ struct v4l2_subdev_route { * struct v4l2_subdev_routing - Subdev routing information * * @which: configuration type (from enum v4l2_subdev_format_whence) - * @num_routes: the total number of routes in the routes array + * @len_routes: the length of the routes array, in routes; set by the user, not + * modified by the kernel * @routes: pointer to the routes array + * @num_routes: the total number of routes, possibly more than fits in the + * routes array * @reserved: drivers and applications must zero this array */ struct v4l2_subdev_routing { __u32 which; - __u32 num_routes; + __u32 len_routes; __u64 routes; - __u32 reserved[6]; + __u32 num_routes; + __u32 reserved[11]; }; /* diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index f8157c9c9da689..6e6165ea10efc3 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -880,6 +880,20 @@ struct v4l2_pix_format { /* The metadata format identifier for FE configuration buffers. */ #define V4L2_META_FMT_RPI_FE_STATS v4l2_fourcc('R', 'P', 'F', 'S') +#ifdef __KERNEL__ +/* + * Line-based metadata formats. Remember to update v4l_fill_fmtdesc() when + * adding new ones! + */ +#define V4L2_META_FMT_GENERIC_8 v4l2_fourcc('M', 'E', 'T', '8') /* Generic 8-bit metadata */ +#define V4L2_META_FMT_GENERIC_CSI2_10 v4l2_fourcc('M', 'C', '1', 'A') /* 10-bit CSI-2 packed 8-bit metadata */ +#define V4L2_META_FMT_GENERIC_CSI2_12 v4l2_fourcc('M', 'C', '1', 'C') /* 12-bit CSI-2 packed 8-bit metadata */ +#define V4L2_META_FMT_GENERIC_CSI2_14 v4l2_fourcc('M', 'C', '1', 'E') /* 14-bit CSI-2 packed 8-bit metadata */ +#define V4L2_META_FMT_GENERIC_CSI2_16 v4l2_fourcc('M', 'C', '1', 'G') /* 16-bit CSI-2 packed 8-bit metadata */ +#define V4L2_META_FMT_GENERIC_CSI2_20 v4l2_fourcc('M', 'C', '1', 'K') /* 20-bit CSI-2 packed 8-bit metadata */ +#define V4L2_META_FMT_GENERIC_CSI2_24 v4l2_fourcc('M', 'C', '1', 'O') /* 24-bit CSI-2 packed 8-bit metadata */ +#endif + /* priv field value to indicates that subsequent fields are valid. */ #define V4L2_PIX_FMT_PRIV_MAGIC 0xfeedcafe @@ -910,6 +924,7 @@ struct v4l2_fmtdesc { #define V4L2_FMT_FLAG_CSC_YCBCR_ENC 0x0080 #define V4L2_FMT_FLAG_CSC_HSV_ENC V4L2_FMT_FLAG_CSC_YCBCR_ENC #define V4L2_FMT_FLAG_CSC_QUANTIZATION 0x0100 +#define V4L2_FMT_FLAG_META_LINE_BASED 0x0200 /* Frame Size and frame rate enumeration */ /* @@ -2453,10 +2468,19 @@ struct v4l2_sdr_format { * struct v4l2_meta_format - metadata format definition * @dataformat: little endian four character code (fourcc) * @buffersize: maximum size in bytes required for data + * @width: number of data units of data per line (valid for line + * based formats only, see format documentation) + * @height: number of lines of data per buffer (valid for line based + * formats only) + * @bytesperline: offset between the beginnings of two adjacent lines in + * bytes (valid for line based formats only) */ struct v4l2_meta_format { __u32 dataformat; __u32 buffersize; + __u32 width; + __u32 height; + __u32 bytesperline; } __attribute__ ((packed)); /**