--- /dev/null
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/media/amlogic,meson-ir-tx.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Amlogic Meson IR transmitter
+
+maintainers:
+ - Viktor Prutyanov <viktor.prutyanov@phystech.edu>
+
+description: |
+ Some Amlogic SoCs such as A311D and T950D4 have IR transmitter
+ (also called blaster) controller onboard. It is capable of
+ sending IR signals with arbitrary carrier frequency and duty cycle.
+
+properties:
+ compatible:
+ oneOf:
+ - const: amlogic,meson-ir-tx
+ - items:
+ - const: amlogic,meson-g12a-ir-tx
+ - const: amlogic,meson-ir-tx
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ maxItems: 2
+
+ clock-names:
+ items:
+ - const: sysclk
+ - const: xtal
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+ - clock-names
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/clock/g12a-clkc.h>
+
+ ir@ff80014c {
+ compatible = "amlogic,meson-g12a-ir-tx", "amlogic,meson-ir-tx";
+ reg = <0xff80014c 0x10>;
+ interrupts = <0 198 IRQ_TYPE_EDGE_RISING>;
+ clocks = <&clkc CLKID_CLK81>, <&xtal>;
+ clock-names = "sysclk", "xtal";
+ };
powerdown-gpios:
maxItems: 1
+ reset-gpios:
+ maxItems: 1
+
+ adv,force-bt656-4:
+ description:
+ Indicates that the output is a BT.656-4 compatible stream.
+ type: boolean
+
port:
$ref: /schemas/graph.yaml#/$defs/port-base
unevaluatedProperties: false
--- /dev/null
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) 2021 Intel Corporation
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/i2c/ovti,ov9282.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: OmniVision OV9282 Sensor
+
+maintainers:
+ - Paul J. Murphy <paul.j.murphy@intel.com>
+ - Daniele Alessandrelli <daniele.alessandrelli@intel.com>
+
+description:
+ OV9282 sensor is an OmniVision black & white CMOS active pixel digital image
+ sensor with an active array size of 1296H x 816V. It is programmable through
+ I2C interface. The I2C client address is fixed to 0x60/0x70 as per sensor data
+ sheet. Image data is sent through MIPI CSI-2.
+
+properties:
+ compatible:
+ const: ovti,ov9282
+ reg:
+ description: I2C address
+ maxItems: 1
+
+ assigned-clocks: true
+ assigned-clock-parents: true
+ assigned-clock-rates: true
+
+ clocks:
+ description: Clock frequency from 6 to 27MHz
+ maxItems: 1
+
+ reset-gpios:
+ description: Reference to the GPIO connected to the XCLR pin, if any.
+ maxItems: 1
+
+ port:
+ additionalProperties: false
+ $ref: /schemas/graph.yaml#/properties/port
+
+ properties:
+ endpoint:
+ $ref: /schemas/media/video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ properties:
+ data-lanes: true
+ link-frequencies: true
+
+ required:
+ - data-lanes
+ - link-frequencies
+
+ required:
+ - endpoint
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - port
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ camera@60 {
+ compatible = "ovti,ov9282";
+ reg = <0x60>;
+ clocks = <&ov9282_clk>;
+
+ assigned-clocks = <&ov9282_clk>;
+ assigned-clock-parents = <&ov9282_clk_parent>;
+ assigned-clock-rates = <24000000>;
+
+ port {
+ ov9282: endpoint {
+ remote-endpoint = <&cam>;
+ data-lanes = <1 2>;
+ link-frequencies = /bits/ 64 <800000000>;
+ };
+ };
+ };
+ };
+...
--- /dev/null
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) 2021 Intel Corporation
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/i2c/sony,imx335.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sony IMX335 Sensor
+
+maintainers:
+ - Paul J. Murphy <paul.j.murphy@intel.com>
+ - Daniele Alessandrelli <daniele.alessandrelli@intel.com>
+
+description:
+ IMX335 sensor is a Sony CMOS active pixel digital image sensor with an active
+ array size of 2592H x 1944V. It is programmable through I2C interface. The
+ I2C client address is fixed to 0x1a as per sensor data sheet. Image data is
+ sent through MIPI CSI-2.
+
+properties:
+ compatible:
+ const: sony,imx335
+ reg:
+ description: I2C address
+ maxItems: 1
+
+ assigned-clocks: true
+ assigned-clock-parents: true
+ assigned-clock-rates: true
+
+ clocks:
+ description: Clock frequency from 6 to 27 MHz, 37.125MHz, 74.25MHz
+ maxItems: 1
+
+ reset-gpios:
+ description: Reference to the GPIO connected to the XCLR pin, if any.
+ maxItems: 1
+
+ port:
+ additionalProperties: false
+ $ref: /schemas/graph.yaml#/properties/port
+
+ properties:
+ endpoint:
+ $ref: /schemas/media/video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ properties:
+ data-lanes: true
+ link-frequencies: true
+
+ required:
+ - data-lanes
+ - link-frequencies
+
+ required:
+ - endpoint
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - port
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ camera@1a {
+ compatible = "sony,imx335";
+ reg = <0x1a>;
+ clocks = <&imx335_clk>;
+
+ assigned-clocks = <&imx335_clk>;
+ assigned-clock-parents = <&imx335_clk_parent>;
+ assigned-clock-rates = <24000000>;
+
+ port {
+ imx335: endpoint {
+ remote-endpoint = <&cam>;
+ data-lanes = <1 2 3 4>;
+ link-frequencies = /bits/ 64 <594000000>;
+ };
+ };
+ };
+ };
+...
--- /dev/null
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) 2021 Intel Corporation
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/i2c/sony,imx412.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sony IMX412 Sensor
+
+maintainers:
+ - Paul J. Murphy <paul.j.murphy@intel.com>
+ - Daniele Alessandrelli <daniele.alessandrelli@intel.com>
+
+description:
+ IMX412 sensor is a Sony CMOS active pixel digital image sensor with an active
+ array size of 4072H x 3176V. It is programmable through I2C interface. The
+ I2C client address is fixed to 0x1a as per sensor data sheet. Image data is
+ sent through MIPI CSI-2.
+
+properties:
+ compatible:
+ const: sony,imx412
+ reg:
+ description: I2C address
+ maxItems: 1
+
+ assigned-clocks: true
+ assigned-clock-parents: true
+ assigned-clock-rates: true
+
+ clocks:
+ description: Clock frequency 6MHz, 12MHz, 18MHz, 24MHz or 27MHz
+ maxItems: 1
+
+ reset-gpios:
+ description: Reference to the GPIO connected to the XCLR pin, if any.
+ maxItems: 1
+
+ port:
+ additionalProperties: false
+ $ref: /schemas/graph.yaml#/properties/port
+
+ properties:
+ endpoint:
+ $ref: /schemas/media/video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ properties:
+ data-lanes: true
+ link-frequencies: true
+
+ required:
+ - data-lanes
+ - link-frequencies
+
+ required:
+ - endpoint
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - port
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ camera@1a {
+ compatible = "sony,imx412";
+ reg = <0x1a>;
+ clocks = <&imx412_clk>;
+
+ assigned-clocks = <&imx412_clk>;
+ assigned-clock-parents = <&imx412_clk_parent>;
+ assigned-clock-rates = <24000000>;
+
+ port {
+ imx412: endpoint {
+ remote-endpoint = <&cam>;
+ data-lanes = <1 2 3 4>;
+ link-frequencies = /bits/ 64 <600000000>;
+ };
+ };
+ };
+ };
+...
$id: http://devicetree.org/schemas/media/nxp,imx7-csi.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
-title: i.MX7 CMOS Sensor Interface
+title: i.MX7 and i.MX8 CSI bridge (CMOS Sensor Interface)
maintainers:
- Rui Miguel Silva <rmfrfs@gmail.com>
properties:
compatible:
- enum:
- - fsl,imx7-csi
- - fsl,imx6ul-csi
+ oneOf:
+ - enum:
+ - fsl,imx7-csi
+ - fsl,imx6ul-csi
+ - items:
+ - const: fsl,imx8mm-csi
+ - const: fsl,imx7-csi
reg:
maxItems: 1
--- /dev/null
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/nxp,imx8mq-mipi-csi2.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NXP i.MX8MQ MIPI CSI-2 receiver
+
+maintainers:
+ - Martin Kepplinger <martin.kepplinger@puri.sm>
+
+description: |-
+ This binding covers the CSI-2 RX PHY and host controller included in the
+ NXP i.MX8MQ SoC. It handles the sensor/image input and process for all the
+ input imaging devices.
+
+properties:
+ compatible:
+ enum:
+ - fsl,imx8mq-mipi-csi2
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: core is the RX Controller Core Clock input. This clock
+ must be exactly equal to or faster than the receive
+ byteclock from the RX DPHY.
+ - description: esc is the Rx Escape Clock. This must be the same escape
+ clock that the RX DPHY receives.
+ - description: ui is the pixel clock (phy_ref up to 333Mhz).
+ See the reference manual for details.
+
+ clock-names:
+ items:
+ - const: core
+ - const: esc
+ - const: ui
+
+ power-domains:
+ maxItems: 1
+
+ resets:
+ items:
+ - description: CORE_RESET reset register bit definition
+ - description: PHY_REF_RESET reset register bit definition
+ - description: ESC_RESET reset register bit definition
+
+ fsl,mipi-phy-gpr:
+ description: |
+ The phandle to the imx8mq syscon iomux-gpr with the register
+ for setting RX_ENABLE for the mipi receiver.
+
+ The format should be as follows:
+ <gpr req_gpr>
+ gpr is the phandle to general purpose register node.
+ req_gpr is the gpr register offset of RX_ENABLE for the mipi phy.
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ items:
+ items:
+ - description: The 'gpr' is the phandle to general purpose register node.
+ - description: The 'req_gpr' is the gpr register offset containing
+ CSI2_1_RX_ENABLE or CSI2_2_RX_ENABLE respectively.
+ maximum: 0xff
+
+ interconnects:
+ maxItems: 1
+
+ interconnect-names:
+ const: dram
+
+ ports:
+ $ref: /schemas/graph.yaml#/properties/ports
+
+ properties:
+ port@0:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ unevaluatedProperties: false
+ description:
+ Input port node, single endpoint describing the CSI-2 transmitter.
+
+ properties:
+ endpoint:
+ $ref: video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ properties:
+ data-lanes:
+ items:
+ minItems: 1
+ maxItems: 4
+ items:
+ - const: 1
+ - const: 2
+ - const: 3
+ - const: 4
+
+ required:
+ - data-lanes
+
+ port@1:
+ $ref: /schemas/graph.yaml#/properties/port
+ description:
+ Output port node
+
+ required:
+ - port@0
+ - port@1
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - power-domains
+ - resets
+ - fsl,mipi-phy-gpr
+ - ports
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/imx8mq-clock.h>
+ #include <dt-bindings/interconnect/imx8mq.h>
+ #include <dt-bindings/reset/imx8mq-reset.h>
+
+ csi@30a70000 {
+ compatible = "fsl,imx8mq-mipi-csi2";
+ reg = <0x30a70000 0x1000>;
+ clocks = <&clk IMX8MQ_CLK_CSI1_CORE>,
+ <&clk IMX8MQ_CLK_CSI1_ESC>,
+ <&clk IMX8MQ_CLK_CSI1_PHY_REF>;
+ clock-names = "core", "esc", "ui";
+ assigned-clocks = <&clk IMX8MQ_CLK_CSI1_CORE>,
+ <&clk IMX8MQ_CLK_CSI1_PHY_REF>,
+ <&clk IMX8MQ_CLK_CSI1_ESC>;
+ assigned-clock-rates = <266000000>, <200000000>, <66000000>;
+ assigned-clock-parents = <&clk IMX8MQ_SYS1_PLL_266M>,
+ <&clk IMX8MQ_SYS2_PLL_1000M>,
+ <&clk IMX8MQ_SYS1_PLL_800M>;
+ power-domains = <&pgc_mipi_csi1>;
+ resets = <&src IMX8MQ_RESET_MIPI_CSI1_CORE_RESET>,
+ <&src IMX8MQ_RESET_MIPI_CSI1_PHY_REF_RESET>,
+ <&src IMX8MQ_RESET_MIPI_CSI1_ESC_RESET>;
+ fsl,mipi-phy-gpr = <&iomuxc_gpr 0x88>;
+ interconnects = <&noc IMX8MQ_ICM_CSI1 &noc IMX8MQ_ICS_DRAM>;
+ interconnect-names = "dram";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ imx8mm_mipi_csi_in: endpoint {
+ remote-endpoint = <&imx477_out>;
+ data-lanes = <1 2 3 4>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ imx8mm_mipi_csi_out: endpoint {
+ remote-endpoint = <&csi_in>;
+ };
+ };
+ };
+ };
+
+...
- rockchip,rk3288-vpu
- rockchip,rk3328-vpu
- rockchip,rk3399-vpu
+ - rockchip,px30-vpu
- items:
- const: rockchip,rk3188-vpu
- const: rockchip,rk3066-vpu
Writing camera sensor drivers
=============================
-CSI-2
------
+CSI-2 and parallel (BT.601 and BT.656) busses
+---------------------------------------------
-Please see what is written on :ref:`MIPI_CSI_2`.
+Please see :ref:`transmitter-receiver`.
Handling clocks
---------------
ACPI
~~~~
-Read the "clock-frequency" _DSD property to denote the frequency. The driver can
-rely on this frequency being used.
+Read the ``clock-frequency`` _DSD property to denote the frequency. The driver
+can rely on this frequency being used.
Devicetree
~~~~~~~~~~
-The currently preferred way to achieve this is using "assigned-clock-rates"
-property. See Documentation/devicetree/bindings/clock/clock-bindings.txt for
-more information. The driver then gets the frequency using clk_get_rate().
+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()``.
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
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 smiapp driver (see drivers/media/i2c/smiapp).
+An example of such a driver is the CCS driver (see ``drivers/media/i2c/ccs``).
Register list based drivers
~~~~~~~~~~~~~~~~~~~~~~~~~~~
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.
+``drivers/media/i2c/imx319.c`` for an example.
Frame interval configuration
----------------------------
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 these controls are lines. The
-pixel rate is specified by ``V4L2_CID_PIXEL_RATE`` in the same sub-device. The
-unit of that control is Hz.
+``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
accessed and when it is streaming.
Existing camera sensor drivers may rely on the old
-:c:type:`v4l2_subdev_core_ops`->s_power() callback for bridge or ISP drivers to
+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/smiapp/smiapp-core.c``. The two drivers work in both ACPI
+``drivers/media/i2c/ccs/ccs-core.c``. The two drivers work in both ACPI
and DT based systems.
Control framework
The function returns a non-zero value if it succeeded getting the power count or
runtime PM was disabled, in either of which cases the driver may proceed to
access the device.
-
-Controls
---------
-
-For camera sensors that are connected to a bus where transmitter and receiver
-require common configuration set by drivers, such as CSI-2 or parallel (BT.601
-or BT.656) bus, the ``V4L2_CID_LINK_FREQ`` control is mandatory on transmitter
-drivers. Receiver drivers can use the ``V4L2_CID_LINK_FREQ`` to query the
-frequency used on the bus.
-
-The transmitter drivers should also implement ``V4L2_CID_PIXEL_RATE`` control in
-order to tell the maximum pixel rate to the receiver. This is required on raw
-camera sensors.
int (*adap_enable)(struct cec_adapter *adap, bool enable);
This callback enables or disables the CEC hardware. Enabling the CEC hardware
-means powering it up in a state where no logical addresses are claimed. This
-op assumes that the physical address (adap->phys_addr) is valid when enable is
-true and will not change while the CEC adapter remains enabled. The initial
+means powering it up in a state where no logical addresses are claimed. The
+physical address will always be valid if CEC_CAP_NEEDS_HPD is set. If that
+capability is not set, then the physical address can change while the CEC
+hardware is enabled. CEC drivers should not set CEC_CAP_NEEDS_HPD unless
+the hardware design requires that as this will make it impossible to wake
+up displays that pull the HPD low when in standby mode. The initial
state of the CEC adapter after calling cec_allocate_adapter() is disabled.
Note that adap_enable must return 0 if enable is false.
+++ /dev/null
-.. SPDX-License-Identifier: GPL-2.0
-
-.. _MIPI_CSI_2:
-
-MIPI CSI-2
-==========
-
-CSI-2 is a data bus intended for transferring images from cameras to
-the host SoC. It is defined by the `MIPI alliance`_.
-
-.. _`MIPI alliance`: http://www.mipi.org/
-
-Media bus formats
------------------
-
-See :ref:`v4l2-mbus-pixelcode` for details on which media bus formats should
-be used for CSI-2 interfaces.
-
-Transmitter drivers
--------------------
-
-CSI-2 transmitter, such as a sensor or a TV tuner, drivers need to
-provide the CSI-2 receiver with information on the CSI-2 bus
-configuration. These include the V4L2_CID_LINK_FREQ and
-V4L2_CID_PIXEL_RATE controls and
-(:c:type:`v4l2_subdev_video_ops`->s_stream() callback). These
-interface elements must be present on the sub-device represents the
-CSI-2 transmitter.
-
-The V4L2_CID_LINK_FREQ control is used to tell the receiver driver the
-frequency (and not the symbol rate) of the link. The V4L2_CID_PIXEL_RATE
-control may be used by the receiver to obtain the pixel rate the transmitter
-uses. The :c:type:`v4l2_subdev_video_ops`->s_stream() callback provides an
-ability to start and stop the stream.
-
-The value of the V4L2_CID_PIXEL_RATE is calculated as follows::
-
- pixel_rate = link_freq * 2 * nr_of_lanes * 16 / k / bits_per_sample
-
-where
-
-.. list-table:: variables in pixel rate calculation
- :header-rows: 1
-
- * - variable or constant
- - description
- * - link_freq
- - The value of the V4L2_CID_LINK_FREQ integer64 menu item.
- * - nr_of_lanes
- - Number of data lanes used on the CSI-2 link. This can
- be obtained from the OF endpoint configuration.
- * - 2
- - Two bits are transferred per clock cycle per lane.
- * - bits_per_sample
- - Number of bits per sample.
- * - k
- - 16 for D-PHY and 7 for C-PHY
-
-The transmitter drivers must, if possible, configure the CSI-2
-transmitter to *LP-11 mode* whenever the transmitter is powered on but
-not active, and maintain *LP-11 mode* until stream on. Only at stream
-on should the transmitter activate the clock on the clock lane and
-transition to *HS mode*.
-
-Some transmitters do this automatically but some have to be explicitly
-programmed to do so, and some are unable to do so altogether due to
-hardware constraints.
-
-Stopping the transmitter
-^^^^^^^^^^^^^^^^^^^^^^^^
-
-A transmitter stops sending the stream of images as a result of
-calling the ``.s_stream()`` callback. Some transmitters may stop the
-stream at a frame boundary whereas others stop immediately,
-effectively leaving the current frame unfinished. The receiver driver
-should not make assumptions either way, but function properly in both
-cases.
-
-Receiver drivers
-----------------
-
-Before the receiver driver may enable the CSI-2 transmitter by using
-the :c:type:`v4l2_subdev_video_ops`->s_stream(), it must have powered
-the transmitter up by using the
-:c:type:`v4l2_subdev_core_ops`->s_power() callback. This may take
-place either indirectly by using :c:func:`v4l2_pipeline_pm_get` or
-directly.
-
-Formats
--------
-
-The media bus pixel codes document parallel formats. Should the pixel data be
-transported over a serial bus, the media bus pixel code that describes a
-parallel format that transfers a sample on a single clock cycle is used.
rc-core
mc-core
cec-core
- csi2
+ tx-rx
camera-sensor
drivers/index
--- /dev/null
+.. SPDX-License-Identifier: GPL-2.0
+
+.. _transmitter-receiver:
+
+Pixel data transmitter and receiver drivers
+===========================================
+
+V4L2 supports various devices that transmit and receive pixel data. Examples of
+these devices include a camera sensor, a TV tuner and a parallel or a CSI-2
+receiver in an SoC.
+
+Bus types
+---------
+
+The following busses are the most common. This section discusses these two only.
+
+MIPI CSI-2
+^^^^^^^^^^
+
+CSI-2 is a data bus intended for transferring images from cameras to
+the host SoC. It is defined by the `MIPI alliance`_.
+
+.. _`MIPI alliance`: https://www.mipi.org/
+
+Parallel
+^^^^^^^^
+
+`BT.601`_ and `BT.656`_ are the most common parallel busses.
+
+.. _`BT.601`: https://en.wikipedia.org/wiki/Rec._601
+.. _`BT.656`: https://en.wikipedia.org/wiki/ITU-R_BT.656
+
+Transmitter drivers
+-------------------
+
+Transmitter drivers generally need to provide the receiver drivers with the
+configuration of the transmitter. What is required depends on the type of the
+bus. These are common for both busses.
+
+Media bus pixel code
+^^^^^^^^^^^^^^^^^^^^
+
+See :ref:`v4l2-mbus-pixelcode`.
+
+Link frequency
+^^^^^^^^^^^^^^
+
+The :ref:`V4L2_CID_LINK_FREQ <v4l2-cid-link-freq>` control is used to tell the
+receiver the frequency of the bus (i.e. it is not the same as the symbol rate).
+
+``.s_stream()`` callback
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+The struct struct v4l2_subdev_video_ops->s_stream() callback is used by the
+receiver driver to control the transmitter driver's streaming state.
+
+
+CSI-2 transmitter drivers
+-------------------------
+
+Pixel rate
+^^^^^^^^^^
+
+The pixel rate on the bus is calculated as follows::
+
+ pixel_rate = link_freq * 2 * nr_of_lanes * 16 / k / bits_per_sample
+
+where
+
+.. list-table:: variables in pixel rate calculation
+ :header-rows: 1
+
+ * - variable or constant
+ - description
+ * - link_freq
+ - The value of the ``V4L2_CID_LINK_FREQ`` integer64 menu item.
+ * - nr_of_lanes
+ - Number of data lanes used on the CSI-2 link. This can
+ be obtained from the OF endpoint configuration.
+ * - 2
+ - Data is transferred on both rising and falling edge of the signal.
+ * - bits_per_sample
+ - Number of bits per sample.
+ * - k
+ - 16 for D-PHY and 7 for C-PHY
+
+.. note::
+
+ The pixel rate calculated this way is **not** the same thing as the
+ pixel rate on the camera sensor's pixel array which is indicated by the
+ :ref:`V4L2_CID_PIXEL_RATE <v4l2-cid-pixel-rate>` control.
+
+LP-11 and LP-111 modes
+^^^^^^^^^^^^^^^^^^^^^^
+
+As part of transitioning to high speed mode, a CSI-2 transmitter typically
+briefly sets the bus to LP-11 or LP-111 state, depending on the PHY. This period
+may be as short as 100 µs, during which the receiver observes this state and
+proceeds its own part of high speed mode transition.
+
+Most receivers are capable of autonomously handling this once the software has
+configured them to do so, but there are receivers which require software
+involvement in observing LP-11 or LP-111 state. 100 µs is a brief period to hit
+in software, especially when there is no interrupt telling something is
+happening.
+
+One way to address this is to configure the transmitter side explicitly to LP-11
+or LP-111 mode, which requires support from the transmitter hardware. This is
+not universally available. Many devices return to this state once streaming is
+stopped while the state after power-on is LP-00 or LP-000.
+
+The ``.pre_streamon()`` callback may be used to prepare a transmitter for
+transitioning to streaming state, but not yet start streaming. Similarly, the
+``.post_streamoff()`` callback is used to undo what was done by the
+``.pre_streamon()`` callback. The caller of ``.pre_streamon()`` is thus required
+to call ``.post_streamoff()`` for each successful call of ``.pre_streamon()``.
+
+In the context of CSI-2, the ``.pre_streamon()`` callback is used to transition
+the transmitter to the LP-11 or LP-111 mode. This also requires powering on the
+device, so this should be only done when it is needed.
+
+Receiver drivers that do not need explicit LP-11 or LP-111 mode setup are waived
+from calling the two callbacks.
+
+Stopping the transmitter
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+A transmitter stops sending the stream of images as a result of
+calling the ``.s_stream()`` callback. Some transmitters may stop the
+stream at a frame boundary whereas others stop immediately,
+effectively leaving the current frame unfinished. The receiver driver
+should not make assumptions either way, but function properly in both
+cases.
ignore define CEC_OP_REC_SEQ_WEDNESDAY
ignore define CEC_OP_REC_SEQ_THURSDAY
ignore define CEC_OP_REC_SEQ_FRIDAY
-ignore define CEC_OP_REC_SEQ_SATERDAY
+ignore define CEC_OP_REC_SEQ_SATURDAY
ignore define CEC_OP_REC_SEQ_ONCE_ONLY
ignore define CEC_MSG_CLEAR_DIGITAL_TIMER
Cyclic intra macroblock refresh. This is the number of continuous
macroblocks refreshed every frame. Each frame a successive set of
macroblocks is refreshed until the cycle completes and starts from
- the top of the frame. Applicable to H264, H263 and MPEG4 encoder.
+ the top of the frame. Setting this control to zero means that
+ macroblocks will not be refreshed. Note that this control will not
+ take effect when ``V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD`` control
+ is set to non zero value.
+ Applicable to H264, H263 and MPEG4 encoder.
+
+``V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD (integer)``
+ Intra macroblock refresh period. This sets the period to refresh
+ the whole frame. In other words, this defines the number of frames
+ for which the whole frame will be intra-refreshed. An example:
+ setting period to 1 means that the whole frame will be refreshed,
+ setting period to 2 means that the half of macroblocks will be
+ intra-refreshed on frameX and the other half of macroblocks
+ will be refreshed in frameX + 1 and so on. Setting the period to
+ zero means no period is specified.
+ Note that if the client sets this control to non zero value the
+ ``V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB`` control shall be
+ ignored. Applicable to H264 and HEVC encoders.
``V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE (boolean)``
Frame level rate control enable. If this control is disabled then
* - __u8
- ``pic_struct``
-
+ * - __u32
+ - ``slice_segment_addr``
+ -
* - __u8
- ``ref_idx_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]``
- The list of L0 reference elements as indices in the DPB.
``V4L2_CID_IMAGE_PROC_CLASS (class)``
The IMAGE_PROC class descriptor.
+.. _v4l2-cid-link-freq:
+
``V4L2_CID_LINK_FREQ (integer menu)``
- Data bus frequency. Together with the media bus pixel code, bus type
- (clock cycles per sample), the data bus frequency defines the pixel
- rate (``V4L2_CID_PIXEL_RATE``) in the pixel array (or possibly
- elsewhere, if the device is not an image sensor). The frame rate can
- be calculated from the pixel clock, image width and height and
- horizontal and vertical blanking. While the pixel rate control may
- be defined elsewhere than in the subdev containing the pixel array,
- the frame rate cannot be obtained from that information. This is
- because only on the pixel array it can be assumed that the vertical
- and horizontal blanking information is exact: no other blanking is
- allowed in the pixel array. The selection of frame rate is performed
- by selecting the desired horizontal and vertical blanking. The unit
- of this control is Hz.
+ The frequency of the data bus (e.g. parallel or CSI-2).
+
+.. _v4l2-cid-pixel-rate:
``V4L2_CID_PIXEL_RATE (64-bit integer)``
- Pixel rate in the source pads of the subdev. This control is
+ Pixel sampling rate in the device's pixel array. This control is
read-only and its unit is pixels / second.
+ Some devices use horizontal and vertical balanking to configure the frame
+ rate. The frame rate can be calculated from the pixel rate, analogue crop
+ rectangle as well as horizontal and vertical blanking. The pixel rate
+ control may be present in a different sub-device than the blanking controls
+ and the analogue crop rectangle configuration.
+
+ The configuration of the frame rate is performed by selecting the desired
+ horizontal and vertical blanking. The unit of this control is Hz.
+
``V4L2_CID_TEST_PATTERN (menu)``
Some capture/display/sensor devices have the capability to generate
test pattern images. These hardware specific test patterns can be
F: Documentation/devicetree/bindings/media/i2c/ov8856.yaml
F: drivers/media/i2c/ov8856.c
+OMNIVISION OV9282 SENSOR DRIVER
+M: Paul J. Murphy <paul.j.murphy@intel.com>
+M: Daniele Alessandrelli <daniele.alessandrelli@intel.com>
+L: linux-media@vger.kernel.org
+S: Maintained
+T: git git://linuxtv.org/media_tree.git
+F: Documentation/devicetree/bindings/media/i2c/ovti,ov9282.yaml
+F: drivers/media/i2c/ov9282.c
+
OMNIVISION OV9640 SENSOR DRIVER
M: Petr Cvek <petrcvekcz@gmail.com>
L: linux-media@vger.kernel.org
F: Documentation/devicetree/bindings/media/i2c/sony,imx334.yaml
F: drivers/media/i2c/imx334.c
+SONY IMX335 SENSOR DRIVER
+M: Paul J. Murphy <paul.j.murphy@intel.com>
+M: Daniele Alessandrelli <daniele.alessandrelli@intel.com>
+L: linux-media@vger.kernel.org
+S: Maintained
+T: git git://linuxtv.org/media_tree.git
+F: Documentation/devicetree/bindings/media/i2c/sony,imx335.yaml
+F: drivers/media/i2c/imx335.c
+
SONY IMX355 SENSOR DRIVER
M: Tianshu Qiu <tian.shu.qiu@intel.com>
L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
F: drivers/media/i2c/imx355.c
+SONY IMX412 SENSOR DRIVER
+M: Paul J. Murphy <paul.j.murphy@intel.com>
+M: Daniele Alessandrelli <daniele.alessandrelli@intel.com>
+L: linux-media@vger.kernel.org
+S: Maintained
+T: git git://linuxtv.org/media_tree.git
+F: Documentation/devicetree/bindings/media/i2c/sony,imx412.yaml
+F: drivers/media/i2c/imx412.c
+
SONY MEMORYSTICK SUBSYSTEM
M: Maxim Levitsky <maximlevitsky@gmail.com>
M: Alex Dubov <oakad@yahoo.com>
u32 work_pin_events_dropped_cnt;
ktime_t timer_ts;
u32 timer_cnt;
- u32 timer_100ms_overruns;
- u32 timer_300ms_overruns;
+ u32 timer_100us_overruns;
+ u32 timer_300us_overruns;
u32 timer_max_overrun;
u32 timer_sum_overrun;
if (delta > 100 && pin->state != CEC_ST_IDLE) {
/* Keep track of timer overruns */
pin->timer_sum_overrun += delta;
- pin->timer_100ms_overruns++;
+ pin->timer_100us_overruns++;
if (delta > 300)
- pin->timer_300ms_overruns++;
+ pin->timer_300us_overruns++;
if (delta > pin->timer_max_overrun)
pin->timer_max_overrun = delta;
}
seq_printf(file, "cec pin events dropped: %u\n",
pin->work_pin_events_dropped_cnt);
seq_printf(file, "irq failed: %d\n", pin->enable_irq_failed);
- if (pin->timer_100ms_overruns) {
- seq_printf(file, "timer overruns > 100ms: %u of %u\n",
- pin->timer_100ms_overruns, pin->timer_cnt);
- seq_printf(file, "timer overruns > 300ms: %u of %u\n",
- pin->timer_300ms_overruns, pin->timer_cnt);
+ if (pin->timer_100us_overruns) {
+ seq_printf(file, "timer overruns > 100us: %u of %u\n",
+ pin->timer_100us_overruns, pin->timer_cnt);
+ seq_printf(file, "timer overruns > 300us: %u of %u\n",
+ pin->timer_300us_overruns, pin->timer_cnt);
seq_printf(file, "max timer overrun: %u usecs\n",
pin->timer_max_overrun);
seq_printf(file, "avg timer overrun: %u usecs\n",
- pin->timer_sum_overrun / pin->timer_100ms_overruns);
+ pin->timer_sum_overrun / pin->timer_100us_overruns);
}
if (pin->rx_start_bit_low_too_short_cnt)
seq_printf(file,
seq_printf(file, "tx detected low drive: %u\n", pin->tx_low_drive_cnt);
pin->work_pin_events_dropped_cnt = 0;
pin->timer_cnt = 0;
- pin->timer_100ms_overruns = 0;
- pin->timer_300ms_overruns = 0;
+ pin->timer_100us_overruns = 0;
+ pin->timer_300us_overruns = 0;
pin->timer_max_overrun = 0;
pin->timer_sum_overrun = 0;
pin->rx_start_bit_low_too_short_cnt = 0;
cec->clk_hdmi_cec = devm_clk_get(&pdev->dev, "hdmi-cec");
if (IS_ERR(cec->clk_hdmi_cec) &&
- PTR_ERR(cec->clk_hdmi_cec) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
+ PTR_ERR(cec->clk_hdmi_cec) == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ goto err_unprepare_cec_clk;
+ }
if (!IS_ERR(cec->clk_hdmi_cec)) {
ret = clk_prepare(cec->clk_hdmi_cec);
if (ret) {
dev_err(&pdev->dev, "Can't prepare hdmi-cec clock\n");
- return ret;
+ goto err_unprepare_cec_clk;
}
}
CEC_NAME, caps, CEC_MAX_LOG_ADDRS);
ret = PTR_ERR_OR_ZERO(cec->adap);
if (ret)
- return ret;
+ goto err_unprepare_hdmi_cec_clk;
ret = cec_register_adapter(cec->adap, &pdev->dev);
- if (ret) {
- cec_delete_adapter(cec->adap);
- return ret;
- }
+ if (ret)
+ goto err_delete_adapter;
cec_hw_init(cec);
platform_set_drvdata(pdev, cec);
return 0;
+
+err_delete_adapter:
+ cec_delete_adapter(cec->adap);
+
+err_unprepare_hdmi_cec_clk:
+ clk_unprepare(cec->clk_hdmi_cec);
+
+err_unprepare_cec_clk:
+ clk_unprepare(cec->clk_cec);
+ return ret;
}
static int stm32_cec_remove(struct platform_device *pdev)
return -ENOENT;
}
- clk_prepare_enable(cec->clk);
+ ret = clk_prepare_enable(cec->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to prepare clock for CEC\n");
+ return ret;
+ }
/* set context info. */
cec->dev = &pdev->dev;
dev_notice(&pdev->dev, "Resuming\n");
- clk_prepare_enable(cec->clk);
-
- return 0;
+ return clk_prepare_enable(cec->clk);
}
#endif
"%s: Error attaching frontend %d\n",
KBUILD_MODNAME, demod);
goto error1;
- break;
case 1:
/* new priv instance */
priv->i2c = i2c;
dib8000_write_word(state, 117 + mode, ana_fe[mode]);
}
-static const u16 lut_prbs_2k[14] = {
- 0, 0x423, 0x009, 0x5C7, 0x7A6, 0x3D8, 0x527, 0x7FF, 0x79B, 0x3D6, 0x3A2, 0x53B, 0x2F4, 0x213
+static const u16 lut_prbs_2k[13] = {
+ 0x423, 0x009, 0x5C7,
+ 0x7A6, 0x3D8, 0x527,
+ 0x7FF, 0x79B, 0x3D6,
+ 0x3A2, 0x53B, 0x2F4,
+ 0x213
};
-static const u16 lut_prbs_4k[14] = {
- 0, 0x208, 0x0C3, 0x7B9, 0x423, 0x5C7, 0x3D8, 0x7FF, 0x3D6, 0x53B, 0x213, 0x029, 0x0D0, 0x48E
+
+static const u16 lut_prbs_4k[13] = {
+ 0x208, 0x0C3, 0x7B9,
+ 0x423, 0x5C7, 0x3D8,
+ 0x7FF, 0x3D6, 0x53B,
+ 0x213, 0x029, 0x0D0,
+ 0x48E
};
-static const u16 lut_prbs_8k[14] = {
- 0, 0x740, 0x069, 0x7DD, 0x208, 0x7B9, 0x5C7, 0x7FF, 0x53B, 0x029, 0x48E, 0x4C4, 0x367, 0x684
+
+static const u16 lut_prbs_8k[13] = {
+ 0x740, 0x069, 0x7DD,
+ 0x208, 0x7B9, 0x5C7,
+ 0x7FF, 0x53B, 0x029,
+ 0x48E, 0x4C4, 0x367,
+ 0x684
};
static u16 dib8000_get_init_prbs(struct dib8000_state *state, u16 subchannel)
{
int sub_channel_prbs_group = 0;
+ int prbs_group;
- sub_channel_prbs_group = (subchannel / 3) + 1;
- dprintk("sub_channel_prbs_group = %d , subchannel =%d prbs = 0x%04x\n", sub_channel_prbs_group, subchannel, lut_prbs_8k[sub_channel_prbs_group]);
+ sub_channel_prbs_group = subchannel / 3;
+ if (sub_channel_prbs_group >= ARRAY_SIZE(lut_prbs_2k))
+ return 0;
switch (state->fe[0]->dtv_property_cache.transmission_mode) {
case TRANSMISSION_MODE_2K:
- return lut_prbs_2k[sub_channel_prbs_group];
+ prbs_group = lut_prbs_2k[sub_channel_prbs_group];
+ break;
case TRANSMISSION_MODE_4K:
- return lut_prbs_4k[sub_channel_prbs_group];
+ prbs_group = lut_prbs_4k[sub_channel_prbs_group];
+ break;
default:
case TRANSMISSION_MODE_8K:
- return lut_prbs_8k[sub_channel_prbs_group];
+ prbs_group = lut_prbs_8k[sub_channel_prbs_group];
}
+
+ dprintk("sub_channel_prbs_group = %d , subchannel =%d prbs = 0x%04x\n",
+ sub_channel_prbs_group, subchannel, prbs_group);
+
+ return prbs_group;
}
static void dib8000_set_13seg_channel(struct dib8000_state *state)
/* TSB or ISDBT ? apply it now */
if (c->isdbt_sb_mode) {
dib8000_set_sb_channel(state);
- if (c->isdbt_sb_subchannel < 14)
- init_prbs = dib8000_get_init_prbs(state, c->isdbt_sb_subchannel);
- else
- init_prbs = 0;
+ init_prbs = dib8000_get_init_prbs(state,
+ c->isdbt_sb_subchannel);
} else {
dib8000_set_13seg_channel(state);
init_prbs = 0xfff;
unsigned long *timeout = &state->timeout;
unsigned long now = jiffies;
+ u16 init_prbs;
#ifdef DIB8000_AGC_FREEZE
u16 agc1, agc2;
#endif
break;
case CT_DEMOD_STEP_11: /* 41 : init prbs autosearch */
- if (state->subchannel <= 41) {
- dib8000_set_subchannel_prbs(state, dib8000_get_init_prbs(state, state->subchannel));
+ init_prbs = dib8000_get_init_prbs(state, state->subchannel);
+
+ if (init_prbs) {
+ dib8000_set_subchannel_prbs(state, init_prbs);
*tune_state = CT_DEMOD_STEP_9;
} else {
*tune_state = CT_DEMOD_STOP;
To compile this driver as a module, choose M here: the
module will be called imx334.
+config VIDEO_IMX335
+ tristate "Sony IMX335 sensor support"
+ depends on OF_GPIO
+ depends on I2C && VIDEO_V4L2
+ select VIDEO_V4L2_SUBDEV_API
+ select MEDIA_CONTROLLER
+ select V4L2_FWNODE
+ help
+ This is a Video4Linux2 sensor driver for the Sony
+ IMX335 camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called imx335.
+
config VIDEO_IMX355
tristate "Sony IMX355 sensor support"
depends on I2C && VIDEO_V4L2
To compile this driver as a module, choose M here: the
module will be called imx355.
+config VIDEO_IMX412
+ tristate "Sony IMX412 sensor support"
+ depends on OF_GPIO
+ depends on I2C && VIDEO_V4L2
+ select VIDEO_V4L2_SUBDEV_API
+ select MEDIA_CONTROLLER
+ select V4L2_FWNODE
+ help
+ This is a Video4Linux2 sensor driver for the Sony
+ IMX412 camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called imx412.
+
config VIDEO_OV02A10
tristate "OmniVision OV02A10 sensor support"
depends on VIDEO_V4L2 && I2C
To compile this driver as a module, choose M here: the
module will be called ov8865.
+config VIDEO_OV9282
+ tristate "OmniVision OV9282 sensor support"
+ depends on OF_GPIO
+ depends on I2C && VIDEO_V4L2
+ select VIDEO_V4L2_SUBDEV_API
+ select MEDIA_CONTROLLER
+ select V4L2_FWNODE
+ help
+ This is a Video4Linux2 sensor driver for the OmniVision
+ OV9282 camera sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ov9282.
+
config VIDEO_OV9640
tristate "OmniVision OV9640 sensor support"
depends on I2C && VIDEO_V4L2
obj-$(CONFIG_VIDEO_OV7740) += ov7740.o
obj-$(CONFIG_VIDEO_OV8856) += ov8856.o
obj-$(CONFIG_VIDEO_OV8865) += ov8865.o
+obj-$(CONFIG_VIDEO_OV9282) += ov9282.o
obj-$(CONFIG_VIDEO_OV9640) += ov9640.o
obj-$(CONFIG_VIDEO_OV9650) += ov9650.o
obj-$(CONFIG_VIDEO_OV9734) += ov9734.o
obj-$(CONFIG_VIDEO_IMX290) += imx290.o
obj-$(CONFIG_VIDEO_IMX319) += imx319.o
obj-$(CONFIG_VIDEO_IMX334) += imx334.o
+obj-$(CONFIG_VIDEO_IMX335) += imx335.o
obj-$(CONFIG_VIDEO_IMX355) += imx355.o
+obj-$(CONFIG_VIDEO_IMX412) += imx412.o
obj-$(CONFIG_VIDEO_MAX9286) += max9286.o
obj-$(CONFIG_VIDEO_MAX9271_LIB) += max9271.o
obj-$(CONFIG_VIDEO_RDACM20) += rdacm20.o
obj-$(CONFIG_VIDEO_RDACM21) += rdacm21.o
obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o
-
obj-$(CONFIG_SDR_MAX2175) += max2175.o
#define ADV7180_REG_SHAP_FILTER_CTL_1 0x0017
#define ADV7180_REG_CTRL_2 0x001d
#define ADV7180_REG_VSYNC_FIELD_CTL_1 0x0031
+#define ADV7180_VSYNC_FIELD_CTL_1_NEWAV 0x12
#define ADV7180_REG_MANUAL_WIN_CTL_1 0x003d
#define ADV7180_REG_MANUAL_WIN_CTL_2 0x003e
#define ADV7180_REG_MANUAL_WIN_CTL_3 0x003f
struct mutex mutex; /* mutual excl. when accessing chip */
int irq;
struct gpio_desc *pwdn_gpio;
+ struct gpio_desc *rst_gpio;
v4l2_std_id curr_norm;
bool powered;
bool streaming;
struct i2c_client *vpp_client;
const struct adv7180_chip_info *chip_info;
enum v4l2_field field;
+ bool force_bt656_4;
};
#define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler, \
struct adv7180_state, \
}
}
+static void adv7180_set_reset_pin(struct adv7180_state *state, bool on)
+{
+ if (!state->rst_gpio)
+ return;
+
+ if (on) {
+ gpiod_set_value_cansleep(state->rst_gpio, 1);
+ } else {
+ gpiod_set_value_cansleep(state->rst_gpio, 0);
+ usleep_range(5000, 10000);
+ }
+}
+
static int adv7180_set_power(struct adv7180_state *state, bool on)
{
u8 val;
adv7180_write(state, ADV7180_REG_EXTENDED_OUTPUT_CONTROL, 0x57);
adv7180_write(state, ADV7180_REG_CTRL_2, 0xc0);
} else {
- if (state->chip_info->flags & ADV7180_FLAG_V2)
- adv7180_write(state,
- ADV7180_REG_EXTENDED_OUTPUT_CONTROL,
- 0x17);
+ if (state->chip_info->flags & ADV7180_FLAG_V2) {
+ if (state->force_bt656_4) {
+ /* ITU-R BT.656-4 compatible */
+ adv7180_write(state,
+ ADV7180_REG_EXTENDED_OUTPUT_CONTROL,
+ ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS);
+ /* Manually set NEWAVMODE */
+ adv7180_write(state,
+ ADV7180_REG_VSYNC_FIELD_CTL_1,
+ ADV7180_VSYNC_FIELD_CTL_1_NEWAV);
+ /* Manually set V bit end position in NTSC mode */
+ adv7180_write(state,
+ ADV7180_REG_NTSC_V_BIT_END,
+ ADV7180_NTSC_V_BIT_END_MANUAL_NVEND);
+ } else {
+ adv7180_write(state,
+ ADV7180_REG_EXTENDED_OUTPUT_CONTROL,
+ 0x17);
+ }
+ }
else
adv7180_write(state,
ADV7180_REG_EXTENDED_OUTPUT_CONTROL,
mutex_lock(&state->mutex);
adv7180_set_power_pin(state, true);
+ adv7180_set_reset_pin(state, false);
adv7180_write(state, ADV7180_REG_PWR_MAN, ADV7180_PWR_MAN_RES);
usleep_range(5000, 10000);
static int adv7180_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ struct device_node *np = client->dev.of_node;
struct adv7180_state *state;
struct v4l2_subdev *sd;
int ret;
return ret;
}
+ state->rst_gpio = devm_gpiod_get_optional(&client->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(state->rst_gpio)) {
+ ret = PTR_ERR(state->rst_gpio);
+ v4l_err(client, "request for reset pin failed: %d\n", ret);
+ return ret;
+ }
+
+ if (of_property_read_bool(np, "adv,force-bt656-4"))
+ state->force_bt656_4 = true;
+
if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
state->csi_client = i2c_new_dummy_device(client->adapter,
ADV7180_DEFAULT_CSI_I2C_ADDR);
if (ret)
goto err_free_irq;
- v4l_info(client, "chip found @ 0x%02x (%s)\n",
- client->addr, client->adapter->name);
+ mutex_lock(&state->mutex);
+ ret = adv7180_read(state, ADV7180_REG_IDENT);
+ mutex_unlock(&state->mutex);
+ if (ret < 0)
+ goto err_v4l2_async_unregister;
+
+ v4l_info(client, "chip id 0x%x found @ 0x%02x (%s)\n",
+ ret, client->addr, client->adapter->name);
return 0;
+err_v4l2_async_unregister:
+ v4l2_async_unregister_subdev(sd);
err_free_irq:
if (state->irq > 0)
free_irq(client->irq, state);
i2c_unregister_device(state->vpp_client);
i2c_unregister_device(state->csi_client);
+ adv7180_set_reset_pin(state, true);
adv7180_set_power_pin(state, false);
mutex_destroy(&state->mutex);
return rval;
}
+static int ccs_pre_streamon(struct v4l2_subdev *subdev, u32 flags)
+{
+ struct ccs_sensor *sensor = to_ccs_sensor(subdev);
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ int rval;
+
+ if (flags & V4L2_SUBDEV_PRE_STREAMON_FL_MANUAL_LP) {
+ switch (sensor->hwcfg.csi_signalling_mode) {
+ case CCS_CSI_SIGNALING_MODE_CSI_2_DPHY:
+ if (!(CCS_LIM(sensor, PHY_CTRL_CAPABILITY_2) &
+ CCS_PHY_CTRL_CAPABILITY_2_MANUAL_LP_DPHY))
+ return -EACCES;
+ break;
+ case CCS_CSI_SIGNALING_MODE_CSI_2_CPHY:
+ if (!(CCS_LIM(sensor, PHY_CTRL_CAPABILITY_2) &
+ CCS_PHY_CTRL_CAPABILITY_2_MANUAL_LP_CPHY))
+ return -EACCES;
+ break;
+ default:
+ return -EACCES;
+ }
+ }
+
+ rval = ccs_pm_get_init(sensor);
+ if (rval)
+ return rval;
+
+ if (flags & V4L2_SUBDEV_PRE_STREAMON_FL_MANUAL_LP) {
+ rval = ccs_write(sensor, MANUAL_LP_CTRL,
+ CCS_MANUAL_LP_CTRL_ENABLE);
+ if (rval)
+ pm_runtime_put(&client->dev);
+ }
+
+ return rval;
+}
+
+static int ccs_post_streamoff(struct v4l2_subdev *subdev)
+{
+ struct ccs_sensor *sensor = to_ccs_sensor(subdev);
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+
+ return pm_runtime_put(&client->dev);
+}
+
static int ccs_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_mbus_code_enum *code)
*/
static ssize_t
-ccs_sysfs_nvm_read(struct device *dev, struct device_attribute *attr,
- char *buf)
+nvm_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct v4l2_subdev *subdev = i2c_get_clientdata(to_i2c_client(dev));
struct i2c_client *client = v4l2_get_subdevdata(subdev);
*/
return rval;
}
-static DEVICE_ATTR(nvm, S_IRUGO, ccs_sysfs_nvm_read, NULL);
+static DEVICE_ATTR_RO(nvm);
static ssize_t
-ccs_sysfs_ident_read(struct device *dev, struct device_attribute *attr,
- char *buf)
+ident_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct v4l2_subdev *subdev = i2c_get_clientdata(to_i2c_client(dev));
struct ccs_sensor *sensor = to_ccs_sensor(subdev);
minfo->smia_manufacturer_id, minfo->model_id,
minfo->revision_number) + 1;
}
-
-static DEVICE_ATTR(ident, S_IRUGO, ccs_sysfs_ident_read, NULL);
+static DEVICE_ATTR_RO(ident);
/* -----------------------------------------------------------------------------
* V4L2 subdev core operations
static const struct v4l2_subdev_video_ops ccs_video_ops = {
.s_stream = ccs_set_stream,
+ .pre_streamon = ccs_pre_streamon,
+ .post_streamoff = ccs_post_streamoff,
};
static const struct v4l2_subdev_pad_ops ccs_pad_ops = {
* sysfs attributes
*/
static ssize_t
-et8ek8_priv_mem_read(struct device *dev, struct device_attribute *attr,
- char *buf)
+priv_mem_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct v4l2_subdev *subdev = dev_get_drvdata(dev);
struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
return ET8EK8_PRIV_MEM_SIZE;
}
-static DEVICE_ATTR(priv_mem, 0444, et8ek8_priv_mem_read, NULL);
+static DEVICE_ATTR_RO(priv_mem);
/* --------------------------------------------------------------------------
* V4L2 subdev core operations
#define IMX258_CHIP_ID 0x0258
/* V_TIMING internal */
-#define IMX258_VTS_30FPS 0x0c98
+#define IMX258_VTS_30FPS 0x0c50
#define IMX258_VTS_30FPS_2K 0x0638
#define IMX258_VTS_30FPS_VGA 0x034c
#define IMX258_VTS_MAX 0xffff
/* Analog gain control */
#define IMX258_REG_ANALOG_GAIN 0x0204
#define IMX258_ANA_GAIN_MIN 0
-#define IMX258_ANA_GAIN_MAX 0x1fff
+#define IMX258_ANA_GAIN_MAX 480
#define IMX258_ANA_GAIN_STEP 1
#define IMX258_ANA_GAIN_DEFAULT 0x0
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Sony imx335 Camera Sensor Driver
+ *
+ * Copyright (C) 2021 Intel Corporation
+ */
+#include <asm/unaligned.h>
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+/* Streaming Mode */
+#define IMX335_REG_MODE_SELECT 0x3000
+#define IMX335_MODE_STANDBY 0x01
+#define IMX335_MODE_STREAMING 0x00
+
+/* Lines per frame */
+#define IMX335_REG_LPFR 0x3030
+
+/* Chip ID */
+#define IMX335_REG_ID 0x3912
+#define IMX335_ID 0x00
+
+/* Exposure control */
+#define IMX335_REG_SHUTTER 0x3058
+#define IMX335_EXPOSURE_MIN 1
+#define IMX335_EXPOSURE_OFFSET 9
+#define IMX335_EXPOSURE_STEP 1
+#define IMX335_EXPOSURE_DEFAULT 0x0648
+
+/* Analog gain control */
+#define IMX335_REG_AGAIN 0x30e8
+#define IMX335_AGAIN_MIN 0
+#define IMX335_AGAIN_MAX 240
+#define IMX335_AGAIN_STEP 1
+#define IMX335_AGAIN_DEFAULT 0
+
+/* Group hold register */
+#define IMX335_REG_HOLD 0x3001
+
+/* Input clock rate */
+#define IMX335_INCLK_RATE 24000000
+
+/* CSI2 HW configuration */
+#define IMX335_LINK_FREQ 594000000
+#define IMX335_NUM_DATA_LANES 4
+
+#define IMX335_REG_MIN 0x00
+#define IMX335_REG_MAX 0xfffff
+
+/**
+ * struct imx335_reg - imx335 sensor register
+ * @address: Register address
+ * @val: Register value
+ */
+struct imx335_reg {
+ u16 address;
+ u8 val;
+};
+
+/**
+ * struct imx335_reg_list - imx335 sensor register list
+ * @num_of_regs: Number of registers in the list
+ * @regs: Pointer to register list
+ */
+struct imx335_reg_list {
+ u32 num_of_regs;
+ const struct imx335_reg *regs;
+};
+
+/**
+ * struct imx335_mode - imx335 sensor mode structure
+ * @width: Frame width
+ * @height: Frame height
+ * @code: Format code
+ * @hblank: Horizontal blanking in lines
+ * @vblank: Vertical blanking in lines
+ * @vblank_min: Minimum vertical blanking in lines
+ * @vblank_max: Maximum vertical blanking in lines
+ * @pclk: Sensor pixel clock
+ * @link_freq_idx: Link frequency index
+ * @reg_list: Register list for sensor mode
+ */
+struct imx335_mode {
+ u32 width;
+ u32 height;
+ u32 code;
+ u32 hblank;
+ u32 vblank;
+ u32 vblank_min;
+ u32 vblank_max;
+ u64 pclk;
+ u32 link_freq_idx;
+ struct imx335_reg_list reg_list;
+};
+
+/**
+ * struct imx335 - imx335 sensor device structure
+ * @dev: Pointer to generic device
+ * @client: Pointer to i2c client
+ * @sd: V4L2 sub-device
+ * @pad: Media pad. Only one pad supported
+ * @reset_gpio: Sensor reset gpio
+ * @inclk: Sensor input clock
+ * @ctrl_handler: V4L2 control handler
+ * @link_freq_ctrl: Pointer to link frequency control
+ * @pclk_ctrl: Pointer to pixel clock control
+ * @hblank_ctrl: Pointer to horizontal blanking control
+ * @vblank_ctrl: Pointer to vertical blanking control
+ * @exp_ctrl: Pointer to exposure control
+ * @again_ctrl: Pointer to analog gain control
+ * @vblank: Vertical blanking in lines
+ * @cur_mode: Pointer to current selected sensor mode
+ * @mutex: Mutex for serializing sensor controls
+ * @streaming: Flag indicating streaming state
+ */
+struct imx335 {
+ struct device *dev;
+ struct i2c_client *client;
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+ struct gpio_desc *reset_gpio;
+ struct clk *inclk;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_ctrl *link_freq_ctrl;
+ struct v4l2_ctrl *pclk_ctrl;
+ struct v4l2_ctrl *hblank_ctrl;
+ struct v4l2_ctrl *vblank_ctrl;
+ struct {
+ struct v4l2_ctrl *exp_ctrl;
+ struct v4l2_ctrl *again_ctrl;
+ };
+ u32 vblank;
+ const struct imx335_mode *cur_mode;
+ struct mutex mutex;
+ bool streaming;
+};
+
+static const s64 link_freq[] = {
+ IMX335_LINK_FREQ,
+};
+
+/* Sensor mode registers */
+static const struct imx335_reg mode_2592x1940_regs[] = {
+ {0x3000, 0x01},
+ {0x3002, 0x00},
+ {0x300c, 0x3b},
+ {0x300d, 0x2a},
+ {0x3018, 0x04},
+ {0x302c, 0x3c},
+ {0x302e, 0x20},
+ {0x3056, 0x94},
+ {0x3074, 0xc8},
+ {0x3076, 0x28},
+ {0x304c, 0x00},
+ {0x314c, 0xc6},
+ {0x315a, 0x02},
+ {0x3168, 0xa0},
+ {0x316a, 0x7e},
+ {0x31a1, 0x00},
+ {0x3288, 0x21},
+ {0x328a, 0x02},
+ {0x3414, 0x05},
+ {0x3416, 0x18},
+ {0x3648, 0x01},
+ {0x364a, 0x04},
+ {0x364c, 0x04},
+ {0x3678, 0x01},
+ {0x367c, 0x31},
+ {0x367e, 0x31},
+ {0x3706, 0x10},
+ {0x3708, 0x03},
+ {0x3714, 0x02},
+ {0x3715, 0x02},
+ {0x3716, 0x01},
+ {0x3717, 0x03},
+ {0x371c, 0x3d},
+ {0x371d, 0x3f},
+ {0x372c, 0x00},
+ {0x372d, 0x00},
+ {0x372e, 0x46},
+ {0x372f, 0x00},
+ {0x3730, 0x89},
+ {0x3731, 0x00},
+ {0x3732, 0x08},
+ {0x3733, 0x01},
+ {0x3734, 0xfe},
+ {0x3735, 0x05},
+ {0x3740, 0x02},
+ {0x375d, 0x00},
+ {0x375e, 0x00},
+ {0x375f, 0x11},
+ {0x3760, 0x01},
+ {0x3768, 0x1b},
+ {0x3769, 0x1b},
+ {0x376a, 0x1b},
+ {0x376b, 0x1b},
+ {0x376c, 0x1a},
+ {0x376d, 0x17},
+ {0x376e, 0x0f},
+ {0x3776, 0x00},
+ {0x3777, 0x00},
+ {0x3778, 0x46},
+ {0x3779, 0x00},
+ {0x377a, 0x89},
+ {0x377b, 0x00},
+ {0x377c, 0x08},
+ {0x377d, 0x01},
+ {0x377e, 0x23},
+ {0x377f, 0x02},
+ {0x3780, 0xd9},
+ {0x3781, 0x03},
+ {0x3782, 0xf5},
+ {0x3783, 0x06},
+ {0x3784, 0xa5},
+ {0x3788, 0x0f},
+ {0x378a, 0xd9},
+ {0x378b, 0x03},
+ {0x378c, 0xeb},
+ {0x378d, 0x05},
+ {0x378e, 0x87},
+ {0x378f, 0x06},
+ {0x3790, 0xf5},
+ {0x3792, 0x43},
+ {0x3794, 0x7a},
+ {0x3796, 0xa1},
+ {0x37b0, 0x36},
+ {0x3a00, 0x01},
+};
+
+/* Supported sensor mode configurations */
+static const struct imx335_mode supported_mode = {
+ .width = 2592,
+ .height = 1940,
+ .hblank = 342,
+ .vblank = 2560,
+ .vblank_min = 2560,
+ .vblank_max = 133060,
+ .pclk = 396000000,
+ .link_freq_idx = 0,
+ .code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_2592x1940_regs),
+ .regs = mode_2592x1940_regs,
+ },
+};
+
+/**
+ * to_imx335() - imx335 V4L2 sub-device to imx335 device.
+ * @subdev: pointer to imx335 V4L2 sub-device
+ *
+ * Return: pointer to imx335 device
+ */
+static inline struct imx335 *to_imx335(struct v4l2_subdev *subdev)
+{
+ return container_of(subdev, struct imx335, sd);
+}
+
+/**
+ * imx335_read_reg() - Read registers.
+ * @imx335: pointer to imx335 device
+ * @reg: register address
+ * @len: length of bytes to read. Max supported bytes is 4
+ * @val: pointer to register value to be filled.
+ *
+ * Big endian register addresses with little endian values.
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx335_read_reg(struct imx335 *imx335, u16 reg, u32 len, u32 *val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&imx335->sd);
+ struct i2c_msg msgs[2] = {0};
+ u8 addr_buf[2] = {0};
+ u8 data_buf[4] = {0};
+ int ret;
+
+ if (WARN_ON(len > 4))
+ return -EINVAL;
+
+ put_unaligned_be16(reg, addr_buf);
+
+ /* Write register address */
+ msgs[0].addr = client->addr;
+ msgs[0].flags = 0;
+ msgs[0].len = ARRAY_SIZE(addr_buf);
+ msgs[0].buf = addr_buf;
+
+ /* Read data from register */
+ msgs[1].addr = client->addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = len;
+ msgs[1].buf = data_buf;
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs))
+ return -EIO;
+
+ *val = get_unaligned_le32(data_buf);
+
+ return 0;
+}
+
+/**
+ * imx335_write_reg() - Write register
+ * @imx335: pointer to imx335 device
+ * @reg: register address
+ * @len: length of bytes. Max supported bytes is 4
+ * @val: register value
+ *
+ * Big endian register addresses with little endian values.
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx335_write_reg(struct imx335 *imx335, u16 reg, u32 len, u32 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&imx335->sd);
+ u8 buf[6] = {0};
+
+ if (WARN_ON(len > 4))
+ return -EINVAL;
+
+ put_unaligned_be16(reg, buf);
+ put_unaligned_le32(val, buf + 2);
+ if (i2c_master_send(client, buf, len + 2) != len + 2)
+ return -EIO;
+
+ return 0;
+}
+
+/**
+ * imx335_write_regs() - Write a list of registers
+ * @imx335: pointer to imx335 device
+ * @regs: list of registers to be written
+ * @len: length of registers array
+ *
+ * Return: 0 if successful. error code otherwise.
+ */
+static int imx335_write_regs(struct imx335 *imx335,
+ const struct imx335_reg *regs, u32 len)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < len; i++) {
+ ret = imx335_write_reg(imx335, regs[i].address, 1, regs[i].val);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * imx335_update_controls() - Update control ranges based on streaming mode
+ * @imx335: pointer to imx335 device
+ * @mode: pointer to imx335_mode sensor mode
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx335_update_controls(struct imx335 *imx335,
+ const struct imx335_mode *mode)
+{
+ int ret;
+
+ ret = __v4l2_ctrl_s_ctrl(imx335->link_freq_ctrl, mode->link_freq_idx);
+ if (ret)
+ return ret;
+
+ ret = __v4l2_ctrl_s_ctrl(imx335->hblank_ctrl, mode->hblank);
+ if (ret)
+ return ret;
+
+ return __v4l2_ctrl_modify_range(imx335->vblank_ctrl, mode->vblank_min,
+ mode->vblank_max, 1, mode->vblank);
+}
+
+/**
+ * imx335_update_exp_gain() - Set updated exposure and gain
+ * @imx335: pointer to imx335 device
+ * @exposure: updated exposure value
+ * @gain: updated analog gain value
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx335_update_exp_gain(struct imx335 *imx335, u32 exposure, u32 gain)
+{
+ u32 lpfr, shutter;
+ int ret;
+
+ lpfr = imx335->vblank + imx335->cur_mode->height;
+ shutter = lpfr - exposure;
+
+ dev_dbg(imx335->dev, "Set exp %u, analog gain %u, shutter %u, lpfr %u",
+ exposure, gain, shutter, lpfr);
+
+ ret = imx335_write_reg(imx335, IMX335_REG_HOLD, 1, 1);
+ if (ret)
+ return ret;
+
+ ret = imx335_write_reg(imx335, IMX335_REG_LPFR, 3, lpfr);
+ if (ret)
+ goto error_release_group_hold;
+
+ ret = imx335_write_reg(imx335, IMX335_REG_SHUTTER, 3, shutter);
+ if (ret)
+ goto error_release_group_hold;
+
+ ret = imx335_write_reg(imx335, IMX335_REG_AGAIN, 2, gain);
+
+error_release_group_hold:
+ imx335_write_reg(imx335, IMX335_REG_HOLD, 1, 0);
+
+ return ret;
+}
+
+/**
+ * imx335_set_ctrl() - Set subdevice control
+ * @ctrl: pointer to v4l2_ctrl structure
+ *
+ * Supported controls:
+ * - V4L2_CID_VBLANK
+ * - cluster controls:
+ * - V4L2_CID_ANALOGUE_GAIN
+ * - V4L2_CID_EXPOSURE
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx335_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct imx335 *imx335 =
+ container_of(ctrl->handler, struct imx335, ctrl_handler);
+ u32 analog_gain;
+ u32 exposure;
+ int ret;
+
+ switch (ctrl->id) {
+ case V4L2_CID_VBLANK:
+ imx335->vblank = imx335->vblank_ctrl->val;
+
+ dev_dbg(imx335->dev, "Received vblank %u, new lpfr %u",
+ imx335->vblank,
+ imx335->vblank + imx335->cur_mode->height);
+
+ ret = __v4l2_ctrl_modify_range(imx335->exp_ctrl,
+ IMX335_EXPOSURE_MIN,
+ imx335->vblank +
+ imx335->cur_mode->height -
+ IMX335_EXPOSURE_OFFSET,
+ 1, IMX335_EXPOSURE_DEFAULT);
+ break;
+ case V4L2_CID_EXPOSURE:
+ /* Set controls only if sensor is in power on state */
+ if (!pm_runtime_get_if_in_use(imx335->dev))
+ return 0;
+
+ exposure = ctrl->val;
+ analog_gain = imx335->again_ctrl->val;
+
+ dev_dbg(imx335->dev, "Received exp %u, analog gain %u",
+ exposure, analog_gain);
+
+ ret = imx335_update_exp_gain(imx335, exposure, analog_gain);
+
+ pm_runtime_put(imx335->dev);
+
+ break;
+ default:
+ dev_err(imx335->dev, "Invalid control %d", ctrl->id);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+/* V4l2 subdevice control ops*/
+static const struct v4l2_ctrl_ops imx335_ctrl_ops = {
+ .s_ctrl = imx335_set_ctrl,
+};
+
+/**
+ * imx335_enum_mbus_code() - Enumerate V4L2 sub-device mbus codes
+ * @sd: pointer to imx335 V4L2 sub-device structure
+ * @sd_state: V4L2 sub-device configuration
+ * @code: V4L2 sub-device code enumeration need to be filled
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx335_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index > 0)
+ return -EINVAL;
+
+ code->code = supported_mode.code;
+
+ return 0;
+}
+
+/**
+ * imx335_enum_frame_size() - Enumerate V4L2 sub-device frame sizes
+ * @sd: pointer to imx335 V4L2 sub-device structure
+ * @sd_state: V4L2 sub-device configuration
+ * @fsize: V4L2 sub-device size enumeration need to be filled
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx335_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_size_enum *fsize)
+{
+ if (fsize->index > 0)
+ return -EINVAL;
+
+ if (fsize->code != supported_mode.code)
+ return -EINVAL;
+
+ fsize->min_width = supported_mode.width;
+ fsize->max_width = fsize->min_width;
+ fsize->min_height = supported_mode.height;
+ fsize->max_height = fsize->min_height;
+
+ return 0;
+}
+
+/**
+ * imx335_fill_pad_format() - Fill subdevice pad format
+ * from selected sensor mode
+ * @imx335: pointer to imx335 device
+ * @mode: pointer to imx335_mode sensor mode
+ * @fmt: V4L2 sub-device format need to be filled
+ */
+static void imx335_fill_pad_format(struct imx335 *imx335,
+ const struct imx335_mode *mode,
+ struct v4l2_subdev_format *fmt)
+{
+ fmt->format.width = mode->width;
+ fmt->format.height = mode->height;
+ fmt->format.code = mode->code;
+ fmt->format.field = V4L2_FIELD_NONE;
+ fmt->format.colorspace = V4L2_COLORSPACE_RAW;
+ fmt->format.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ fmt->format.quantization = V4L2_QUANTIZATION_DEFAULT;
+ fmt->format.xfer_func = V4L2_XFER_FUNC_NONE;
+}
+
+/**
+ * imx335_get_pad_format() - Get subdevice pad format
+ * @sd: pointer to imx335 V4L2 sub-device structure
+ * @sd_state: V4L2 sub-device configuration
+ * @fmt: V4L2 sub-device format need to be set
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx335_get_pad_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct imx335 *imx335 = to_imx335(sd);
+
+ mutex_lock(&imx335->mutex);
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ struct v4l2_mbus_framefmt *framefmt;
+
+ framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad);
+ fmt->format = *framefmt;
+ } else {
+ imx335_fill_pad_format(imx335, imx335->cur_mode, fmt);
+ }
+
+ mutex_unlock(&imx335->mutex);
+
+ return 0;
+}
+
+/**
+ * imx335_set_pad_format() - Set subdevice pad format
+ * @sd: pointer to imx335 V4L2 sub-device structure
+ * @sd_state: V4L2 sub-device configuration
+ * @fmt: V4L2 sub-device format need to be set
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx335_set_pad_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct imx335 *imx335 = to_imx335(sd);
+ const struct imx335_mode *mode;
+ int ret = 0;
+
+ mutex_lock(&imx335->mutex);
+
+ mode = &supported_mode;
+ imx335_fill_pad_format(imx335, mode, fmt);
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ struct v4l2_mbus_framefmt *framefmt;
+
+ framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad);
+ *framefmt = fmt->format;
+ } else {
+ ret = imx335_update_controls(imx335, mode);
+ if (!ret)
+ imx335->cur_mode = mode;
+ }
+
+ mutex_unlock(&imx335->mutex);
+
+ return ret;
+}
+
+/**
+ * imx335_init_pad_cfg() - Initialize sub-device pad configuration
+ * @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)
+{
+ struct imx335 *imx335 = to_imx335(sd);
+ struct v4l2_subdev_format fmt = { 0 };
+
+ fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
+ imx335_fill_pad_format(imx335, &supported_mode, &fmt);
+
+ return imx335_set_pad_format(sd, sd_state, &fmt);
+}
+
+/**
+ * imx335_start_streaming() - Start sensor stream
+ * @imx335: pointer to imx335 device
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx335_start_streaming(struct imx335 *imx335)
+{
+ const struct imx335_reg_list *reg_list;
+ int ret;
+
+ /* Write sensor mode registers */
+ reg_list = &imx335->cur_mode->reg_list;
+ ret = imx335_write_regs(imx335, reg_list->regs,
+ reg_list->num_of_regs);
+ if (ret) {
+ dev_err(imx335->dev, "fail to write initial registers");
+ return ret;
+ }
+
+ /* Setup handler will write actual exposure and gain */
+ ret = __v4l2_ctrl_handler_setup(imx335->sd.ctrl_handler);
+ if (ret) {
+ dev_err(imx335->dev, "fail to setup handler");
+ return ret;
+ }
+
+ /* Start streaming */
+ ret = imx335_write_reg(imx335, IMX335_REG_MODE_SELECT,
+ 1, IMX335_MODE_STREAMING);
+ if (ret) {
+ dev_err(imx335->dev, "fail to start streaming");
+ return ret;
+ }
+
+ /* Initial regulator stabilization period */
+ usleep_range(18000, 20000);
+
+ return 0;
+}
+
+/**
+ * imx335_stop_streaming() - Stop sensor stream
+ * @imx335: pointer to imx335 device
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx335_stop_streaming(struct imx335 *imx335)
+{
+ return imx335_write_reg(imx335, IMX335_REG_MODE_SELECT,
+ 1, IMX335_MODE_STANDBY);
+}
+
+/**
+ * imx335_set_stream() - Enable sensor streaming
+ * @sd: pointer to imx335 subdevice
+ * @enable: set to enable sensor streaming
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx335_set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct imx335 *imx335 = to_imx335(sd);
+ int ret;
+
+ mutex_lock(&imx335->mutex);
+
+ if (imx335->streaming == enable) {
+ mutex_unlock(&imx335->mutex);
+ return 0;
+ }
+
+ if (enable) {
+ ret = pm_runtime_resume_and_get(imx335->dev);
+ if (ret)
+ goto error_unlock;
+
+ ret = imx335_start_streaming(imx335);
+ if (ret)
+ goto error_power_off;
+ } else {
+ imx335_stop_streaming(imx335);
+ pm_runtime_put(imx335->dev);
+ }
+
+ imx335->streaming = enable;
+
+ mutex_unlock(&imx335->mutex);
+
+ return 0;
+
+error_power_off:
+ pm_runtime_put(imx335->dev);
+error_unlock:
+ mutex_unlock(&imx335->mutex);
+
+ return ret;
+}
+
+/**
+ * imx335_detect() - Detect imx335 sensor
+ * @imx335: pointer to imx335 device
+ *
+ * Return: 0 if successful, -EIO if sensor id does not match
+ */
+static int imx335_detect(struct imx335 *imx335)
+{
+ int ret;
+ u32 val;
+
+ ret = imx335_read_reg(imx335, IMX335_REG_ID, 2, &val);
+ if (ret)
+ return ret;
+
+ if (val != IMX335_ID) {
+ dev_err(imx335->dev, "chip id mismatch: %x!=%x",
+ IMX335_ID, val);
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+/**
+ * imx335_parse_hw_config() - Parse HW configuration and check if supported
+ * @imx335: pointer to imx335 device
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx335_parse_hw_config(struct imx335 *imx335)
+{
+ struct fwnode_handle *fwnode = dev_fwnode(imx335->dev);
+ struct v4l2_fwnode_endpoint bus_cfg = {
+ .bus_type = V4L2_MBUS_CSI2_DPHY
+ };
+ struct fwnode_handle *ep;
+ unsigned long rate;
+ unsigned int i;
+ int ret;
+
+ if (!fwnode)
+ return -ENXIO;
+
+ /* Request optional reset pin */
+ imx335->reset_gpio = devm_gpiod_get_optional(imx335->dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(imx335->reset_gpio)) {
+ dev_err(imx335->dev, "failed to get reset gpio %ld",
+ PTR_ERR(imx335->reset_gpio));
+ return PTR_ERR(imx335->reset_gpio);
+ }
+
+ /* Get sensor input clock */
+ imx335->inclk = devm_clk_get(imx335->dev, NULL);
+ if (IS_ERR(imx335->inclk)) {
+ dev_err(imx335->dev, "could not get inclk");
+ return PTR_ERR(imx335->inclk);
+ }
+
+ rate = clk_get_rate(imx335->inclk);
+ if (rate != IMX335_INCLK_RATE) {
+ dev_err(imx335->dev, "inclk frequency mismatch");
+ return -EINVAL;
+ }
+
+ ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
+ if (!ep)
+ return -ENXIO;
+
+ ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
+ fwnode_handle_put(ep);
+ if (ret)
+ return ret;
+
+ if (bus_cfg.bus.mipi_csi2.num_data_lanes != IMX335_NUM_DATA_LANES) {
+ dev_err(imx335->dev,
+ "number of CSI2 data lanes %d is not supported",
+ bus_cfg.bus.mipi_csi2.num_data_lanes);
+ ret = -EINVAL;
+ goto done_endpoint_free;
+ }
+
+ if (!bus_cfg.nr_of_link_frequencies) {
+ dev_err(imx335->dev, "no link frequencies defined");
+ ret = -EINVAL;
+ goto done_endpoint_free;
+ }
+
+ for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++)
+ if (bus_cfg.link_frequencies[i] == IMX335_LINK_FREQ)
+ goto done_endpoint_free;
+
+ ret = -EINVAL;
+
+done_endpoint_free:
+ v4l2_fwnode_endpoint_free(&bus_cfg);
+
+ return ret;
+}
+
+/* V4l2 subdevice ops */
+static const struct v4l2_subdev_video_ops imx335_video_ops = {
+ .s_stream = imx335_set_stream,
+};
+
+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,
+ .set_fmt = imx335_set_pad_format,
+};
+
+static const struct v4l2_subdev_ops imx335_subdev_ops = {
+ .video = &imx335_video_ops,
+ .pad = &imx335_pad_ops,
+};
+
+/**
+ * imx335_power_on() - Sensor power on sequence
+ * @dev: pointer to i2c device
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx335_power_on(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct imx335 *imx335 = to_imx335(sd);
+ int ret;
+
+ gpiod_set_value_cansleep(imx335->reset_gpio, 1);
+
+ ret = clk_prepare_enable(imx335->inclk);
+ if (ret) {
+ dev_err(imx335->dev, "fail to enable inclk");
+ goto error_reset;
+ }
+
+ usleep_range(20, 22);
+
+ return 0;
+
+error_reset:
+ gpiod_set_value_cansleep(imx335->reset_gpio, 0);
+
+ return ret;
+}
+
+/**
+ * imx335_power_off() - Sensor power off sequence
+ * @dev: pointer to i2c device
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx335_power_off(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct imx335 *imx335 = to_imx335(sd);
+
+ gpiod_set_value_cansleep(imx335->reset_gpio, 0);
+
+ clk_disable_unprepare(imx335->inclk);
+
+ return 0;
+}
+
+/**
+ * imx335_init_controls() - Initialize sensor subdevice controls
+ * @imx335: pointer to imx335 device
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx335_init_controls(struct imx335 *imx335)
+{
+ struct v4l2_ctrl_handler *ctrl_hdlr = &imx335->ctrl_handler;
+ const struct imx335_mode *mode = imx335->cur_mode;
+ u32 lpfr;
+ int ret;
+
+ ret = v4l2_ctrl_handler_init(ctrl_hdlr, 6);
+ if (ret)
+ return ret;
+
+ /* Serialize controls with sensor device */
+ ctrl_hdlr->lock = &imx335->mutex;
+
+ /* Initialize exposure and gain */
+ lpfr = mode->vblank + mode->height;
+ imx335->exp_ctrl = v4l2_ctrl_new_std(ctrl_hdlr,
+ &imx335_ctrl_ops,
+ V4L2_CID_EXPOSURE,
+ IMX335_EXPOSURE_MIN,
+ lpfr - IMX335_EXPOSURE_OFFSET,
+ IMX335_EXPOSURE_STEP,
+ IMX335_EXPOSURE_DEFAULT);
+
+ imx335->again_ctrl = v4l2_ctrl_new_std(ctrl_hdlr,
+ &imx335_ctrl_ops,
+ V4L2_CID_ANALOGUE_GAIN,
+ IMX335_AGAIN_MIN,
+ IMX335_AGAIN_MAX,
+ IMX335_AGAIN_STEP,
+ IMX335_AGAIN_DEFAULT);
+
+ v4l2_ctrl_cluster(2, &imx335->exp_ctrl);
+
+ imx335->vblank_ctrl = v4l2_ctrl_new_std(ctrl_hdlr,
+ &imx335_ctrl_ops,
+ V4L2_CID_VBLANK,
+ mode->vblank_min,
+ mode->vblank_max,
+ 1, mode->vblank);
+
+ /* Read only controls */
+ imx335->pclk_ctrl = v4l2_ctrl_new_std(ctrl_hdlr,
+ &imx335_ctrl_ops,
+ V4L2_CID_PIXEL_RATE,
+ mode->pclk, mode->pclk,
+ 1, mode->pclk);
+
+ imx335->link_freq_ctrl = v4l2_ctrl_new_int_menu(ctrl_hdlr,
+ &imx335_ctrl_ops,
+ V4L2_CID_LINK_FREQ,
+ ARRAY_SIZE(link_freq) -
+ 1,
+ mode->link_freq_idx,
+ link_freq);
+ if (imx335->link_freq_ctrl)
+ imx335->link_freq_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ imx335->hblank_ctrl = v4l2_ctrl_new_std(ctrl_hdlr,
+ &imx335_ctrl_ops,
+ V4L2_CID_HBLANK,
+ IMX335_REG_MIN,
+ IMX335_REG_MAX,
+ 1, mode->hblank);
+ if (imx335->hblank_ctrl)
+ imx335->hblank_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ if (ctrl_hdlr->error) {
+ dev_err(imx335->dev, "control init failed: %d",
+ ctrl_hdlr->error);
+ v4l2_ctrl_handler_free(ctrl_hdlr);
+ return ctrl_hdlr->error;
+ }
+
+ imx335->sd.ctrl_handler = ctrl_hdlr;
+
+ return 0;
+}
+
+/**
+ * imx335_probe() - I2C client device binding
+ * @client: pointer to i2c client device
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx335_probe(struct i2c_client *client)
+{
+ struct imx335 *imx335;
+ int ret;
+
+ imx335 = devm_kzalloc(&client->dev, sizeof(*imx335), GFP_KERNEL);
+ if (!imx335)
+ return -ENOMEM;
+
+ imx335->dev = &client->dev;
+
+ /* Initialize subdev */
+ v4l2_i2c_subdev_init(&imx335->sd, client, &imx335_subdev_ops);
+
+ ret = imx335_parse_hw_config(imx335);
+ if (ret) {
+ dev_err(imx335->dev, "HW configuration is not supported");
+ return ret;
+ }
+
+ mutex_init(&imx335->mutex);
+
+ ret = imx335_power_on(imx335->dev);
+ if (ret) {
+ dev_err(imx335->dev, "failed to power-on the sensor");
+ goto error_mutex_destroy;
+ }
+
+ /* Check module identity */
+ ret = imx335_detect(imx335);
+ if (ret) {
+ dev_err(imx335->dev, "failed to find sensor: %d", ret);
+ goto error_power_off;
+ }
+
+ /* Set default mode to max resolution */
+ imx335->cur_mode = &supported_mode;
+ imx335->vblank = imx335->cur_mode->vblank;
+
+ ret = imx335_init_controls(imx335);
+ if (ret) {
+ dev_err(imx335->dev, "failed to init controls: %d", ret);
+ goto error_power_off;
+ }
+
+ /* Initialize subdev */
+ imx335->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ imx335->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+
+ /* Initialize source pad */
+ imx335->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&imx335->sd.entity, 1, &imx335->pad);
+ if (ret) {
+ dev_err(imx335->dev, "failed to init entity pads: %d", ret);
+ goto error_handler_free;
+ }
+
+ ret = v4l2_async_register_subdev_sensor(&imx335->sd);
+ if (ret < 0) {
+ dev_err(imx335->dev,
+ "failed to register async subdev: %d", ret);
+ goto error_media_entity;
+ }
+
+ pm_runtime_set_active(imx335->dev);
+ pm_runtime_enable(imx335->dev);
+ pm_runtime_idle(imx335->dev);
+
+ return 0;
+
+error_media_entity:
+ media_entity_cleanup(&imx335->sd.entity);
+error_handler_free:
+ v4l2_ctrl_handler_free(imx335->sd.ctrl_handler);
+error_power_off:
+ imx335_power_off(imx335->dev);
+error_mutex_destroy:
+ mutex_destroy(&imx335->mutex);
+
+ return ret;
+}
+
+/**
+ * imx335_remove() - I2C client device unbinding
+ * @client: pointer to I2C client device
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx335_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct imx335 *imx335 = to_imx335(sd);
+
+ v4l2_async_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
+
+ pm_runtime_disable(&client->dev);
+ if (!pm_runtime_status_suspended(&client->dev))
+ imx335_power_off(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+
+ mutex_destroy(&imx335->mutex);
+
+ return 0;
+}
+
+static const struct dev_pm_ops imx335_pm_ops = {
+ SET_RUNTIME_PM_OPS(imx335_power_off, imx335_power_on, NULL)
+};
+
+static const struct of_device_id imx335_of_match[] = {
+ { .compatible = "sony,imx335" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, imx335_of_match);
+
+static struct i2c_driver imx335_driver = {
+ .probe_new = imx335_probe,
+ .remove = imx335_remove,
+ .driver = {
+ .name = "imx335",
+ .pm = &imx335_pm_ops,
+ .of_match_table = imx335_of_match,
+ },
+};
+
+module_i2c_driver(imx335_driver);
+
+MODULE_DESCRIPTION("Sony imx335 sensor driver");
+MODULE_LICENSE("GPL");
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Sony imx412 Camera Sensor Driver
+ *
+ * Copyright (C) 2021 Intel Corporation
+ */
+#include <asm/unaligned.h>
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+/* Streaming Mode */
+#define IMX412_REG_MODE_SELECT 0x0100
+#define IMX412_MODE_STANDBY 0x00
+#define IMX412_MODE_STREAMING 0x01
+
+/* Lines per frame */
+#define IMX412_REG_LPFR 0x0340
+
+/* Chip ID */
+#define IMX412_REG_ID 0x0016
+#define IMX412_ID 0x577
+
+/* Exposure control */
+#define IMX412_REG_EXPOSURE_CIT 0x0202
+#define IMX412_EXPOSURE_MIN 8
+#define IMX412_EXPOSURE_OFFSET 22
+#define IMX412_EXPOSURE_STEP 1
+#define IMX412_EXPOSURE_DEFAULT 0x0648
+
+/* Analog gain control */
+#define IMX412_REG_AGAIN 0x0204
+#define IMX412_AGAIN_MIN 0
+#define IMX412_AGAIN_MAX 978
+#define IMX412_AGAIN_STEP 1
+#define IMX412_AGAIN_DEFAULT 0
+
+/* Group hold register */
+#define IMX412_REG_HOLD 0x0104
+
+/* Input clock rate */
+#define IMX412_INCLK_RATE 24000000
+
+/* CSI2 HW configuration */
+#define IMX412_LINK_FREQ 600000000
+#define IMX412_NUM_DATA_LANES 4
+
+#define IMX412_REG_MIN 0x00
+#define IMX412_REG_MAX 0xffff
+
+/**
+ * struct imx412_reg - imx412 sensor register
+ * @address: Register address
+ * @val: Register value
+ */
+struct imx412_reg {
+ u16 address;
+ u8 val;
+};
+
+/**
+ * struct imx412_reg_list - imx412 sensor register list
+ * @num_of_regs: Number of registers in the list
+ * @regs: Pointer to register list
+ */
+struct imx412_reg_list {
+ u32 num_of_regs;
+ const struct imx412_reg *regs;
+};
+
+/**
+ * struct imx412_mode - imx412 sensor mode structure
+ * @width: Frame width
+ * @height: Frame height
+ * @code: Format code
+ * @hblank: Horizontal blanking in lines
+ * @vblank: Vertical blanking in lines
+ * @vblank_min: Minimum vertical blanking in lines
+ * @vblank_max: Maximum vertical blanking in lines
+ * @pclk: Sensor pixel clock
+ * @link_freq_idx: Link frequency index
+ * @reg_list: Register list for sensor mode
+ */
+struct imx412_mode {
+ u32 width;
+ u32 height;
+ u32 code;
+ u32 hblank;
+ u32 vblank;
+ u32 vblank_min;
+ u32 vblank_max;
+ u64 pclk;
+ u32 link_freq_idx;
+ struct imx412_reg_list reg_list;
+};
+
+/**
+ * struct imx412 - imx412 sensor device structure
+ * @dev: Pointer to generic device
+ * @client: Pointer to i2c client
+ * @sd: V4L2 sub-device
+ * @pad: Media pad. Only one pad supported
+ * @reset_gpio: Sensor reset gpio
+ * @inclk: Sensor input clock
+ * @ctrl_handler: V4L2 control handler
+ * @link_freq_ctrl: Pointer to link frequency control
+ * @pclk_ctrl: Pointer to pixel clock control
+ * @hblank_ctrl: Pointer to horizontal blanking control
+ * @vblank_ctrl: Pointer to vertical blanking control
+ * @exp_ctrl: Pointer to exposure control
+ * @again_ctrl: Pointer to analog gain control
+ * @vblank: Vertical blanking in lines
+ * @cur_mode: Pointer to current selected sensor mode
+ * @mutex: Mutex for serializing sensor controls
+ * @streaming: Flag indicating streaming state
+ */
+struct imx412 {
+ struct device *dev;
+ struct i2c_client *client;
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+ struct gpio_desc *reset_gpio;
+ struct clk *inclk;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_ctrl *link_freq_ctrl;
+ struct v4l2_ctrl *pclk_ctrl;
+ struct v4l2_ctrl *hblank_ctrl;
+ struct v4l2_ctrl *vblank_ctrl;
+ struct {
+ struct v4l2_ctrl *exp_ctrl;
+ struct v4l2_ctrl *again_ctrl;
+ };
+ u32 vblank;
+ const struct imx412_mode *cur_mode;
+ struct mutex mutex;
+ bool streaming;
+};
+
+static const s64 link_freq[] = {
+ IMX412_LINK_FREQ,
+};
+
+/* Sensor mode registers */
+static const struct imx412_reg mode_4056x3040_regs[] = {
+ {0x0136, 0x18},
+ {0x0137, 0x00},
+ {0x3c7e, 0x08},
+ {0x3c7f, 0x02},
+ {0x38a8, 0x1f},
+ {0x38a9, 0xff},
+ {0x38aa, 0x1f},
+ {0x38ab, 0xff},
+ {0x55d4, 0x00},
+ {0x55d5, 0x00},
+ {0x55d6, 0x07},
+ {0x55d7, 0xff},
+ {0x55e8, 0x07},
+ {0x55e9, 0xff},
+ {0x55ea, 0x00},
+ {0x55eb, 0x00},
+ {0x575c, 0x07},
+ {0x575d, 0xff},
+ {0x575e, 0x00},
+ {0x575f, 0x00},
+ {0x5764, 0x00},
+ {0x5765, 0x00},
+ {0x5766, 0x07},
+ {0x5767, 0xff},
+ {0x5974, 0x04},
+ {0x5975, 0x01},
+ {0x5f10, 0x09},
+ {0x5f11, 0x92},
+ {0x5f12, 0x32},
+ {0x5f13, 0x72},
+ {0x5f14, 0x16},
+ {0x5f15, 0xba},
+ {0x5f17, 0x13},
+ {0x5f18, 0x24},
+ {0x5f19, 0x60},
+ {0x5f1a, 0xe3},
+ {0x5f1b, 0xad},
+ {0x5f1c, 0x74},
+ {0x5f2d, 0x25},
+ {0x5f5c, 0xd0},
+ {0x6a22, 0x00},
+ {0x6a23, 0x1d},
+ {0x7ba8, 0x00},
+ {0x7ba9, 0x00},
+ {0x886b, 0x00},
+ {0x9002, 0x0a},
+ {0x9004, 0x1a},
+ {0x9214, 0x93},
+ {0x9215, 0x69},
+ {0x9216, 0x93},
+ {0x9217, 0x6b},
+ {0x9218, 0x93},
+ {0x9219, 0x6d},
+ {0x921a, 0x57},
+ {0x921b, 0x58},
+ {0x921c, 0x57},
+ {0x921d, 0x59},
+ {0x921e, 0x57},
+ {0x921f, 0x5a},
+ {0x9220, 0x57},
+ {0x9221, 0x5b},
+ {0x9222, 0x93},
+ {0x9223, 0x02},
+ {0x9224, 0x93},
+ {0x9225, 0x03},
+ {0x9226, 0x93},
+ {0x9227, 0x04},
+ {0x9228, 0x93},
+ {0x9229, 0x05},
+ {0x922a, 0x98},
+ {0x922b, 0x21},
+ {0x922c, 0xb2},
+ {0x922d, 0xdb},
+ {0x922e, 0xb2},
+ {0x922f, 0xdc},
+ {0x9230, 0xb2},
+ {0x9231, 0xdd},
+ {0x9232, 0xe2},
+ {0x9233, 0xe1},
+ {0x9234, 0xb2},
+ {0x9235, 0xe2},
+ {0x9236, 0xb2},
+ {0x9237, 0xe3},
+ {0x9238, 0xb7},
+ {0x9239, 0xb9},
+ {0x923a, 0xb7},
+ {0x923b, 0xbb},
+ {0x923c, 0xb7},
+ {0x923d, 0xbc},
+ {0x923e, 0xb7},
+ {0x923f, 0xc5},
+ {0x9240, 0xb7},
+ {0x9241, 0xc7},
+ {0x9242, 0xb7},
+ {0x9243, 0xc9},
+ {0x9244, 0x98},
+ {0x9245, 0x56},
+ {0x9246, 0x98},
+ {0x9247, 0x55},
+ {0x9380, 0x00},
+ {0x9381, 0x62},
+ {0x9382, 0x00},
+ {0x9383, 0x56},
+ {0x9384, 0x00},
+ {0x9385, 0x52},
+ {0x9388, 0x00},
+ {0x9389, 0x55},
+ {0x938a, 0x00},
+ {0x938b, 0x55},
+ {0x938c, 0x00},
+ {0x938d, 0x41},
+ {0x5078, 0x01},
+ {0x0112, 0x0a},
+ {0x0113, 0x0a},
+ {0x0114, 0x03},
+ {0x0342, 0x11},
+ {0x0343, 0xa0},
+ {0x0340, 0x0d},
+ {0x0341, 0xda},
+ {0x3210, 0x00},
+ {0x0344, 0x00},
+ {0x0345, 0x00},
+ {0x0346, 0x00},
+ {0x0347, 0x00},
+ {0x0348, 0x0f},
+ {0x0349, 0xd7},
+ {0x034a, 0x0b},
+ {0x034b, 0xdf},
+ {0x00e3, 0x00},
+ {0x00e4, 0x00},
+ {0x00e5, 0x01},
+ {0x00fc, 0x0a},
+ {0x00fd, 0x0a},
+ {0x00fe, 0x0a},
+ {0x00ff, 0x0a},
+ {0xe013, 0x00},
+ {0x0220, 0x00},
+ {0x0221, 0x11},
+ {0x0381, 0x01},
+ {0x0383, 0x01},
+ {0x0385, 0x01},
+ {0x0387, 0x01},
+ {0x0900, 0x00},
+ {0x0901, 0x11},
+ {0x0902, 0x00},
+ {0x3140, 0x02},
+ {0x3241, 0x11},
+ {0x3250, 0x03},
+ {0x3e10, 0x00},
+ {0x3e11, 0x00},
+ {0x3f0d, 0x00},
+ {0x3f42, 0x00},
+ {0x3f43, 0x00},
+ {0x0401, 0x00},
+ {0x0404, 0x00},
+ {0x0405, 0x10},
+ {0x0408, 0x00},
+ {0x0409, 0x00},
+ {0x040a, 0x00},
+ {0x040b, 0x00},
+ {0x040c, 0x0f},
+ {0x040d, 0xd8},
+ {0x040e, 0x0b},
+ {0x040f, 0xe0},
+ {0x034c, 0x0f},
+ {0x034d, 0xd8},
+ {0x034e, 0x0b},
+ {0x034f, 0xe0},
+ {0x0301, 0x05},
+ {0x0303, 0x02},
+ {0x0305, 0x04},
+ {0x0306, 0x00},
+ {0x0307, 0xc8},
+ {0x0309, 0x0a},
+ {0x030b, 0x01},
+ {0x030d, 0x02},
+ {0x030e, 0x01},
+ {0x030f, 0x5e},
+ {0x0310, 0x00},
+ {0x0820, 0x12},
+ {0x0821, 0xc0},
+ {0x0822, 0x00},
+ {0x0823, 0x00},
+ {0x3e20, 0x01},
+ {0x3e37, 0x00},
+ {0x3f50, 0x00},
+ {0x3f56, 0x00},
+ {0x3f57, 0xe2},
+ {0x3c0a, 0x5a},
+ {0x3c0b, 0x55},
+ {0x3c0c, 0x28},
+ {0x3c0d, 0x07},
+ {0x3c0e, 0xff},
+ {0x3c0f, 0x00},
+ {0x3c10, 0x00},
+ {0x3c11, 0x02},
+ {0x3c12, 0x00},
+ {0x3c13, 0x03},
+ {0x3c14, 0x00},
+ {0x3c15, 0x00},
+ {0x3c16, 0x0c},
+ {0x3c17, 0x0c},
+ {0x3c18, 0x0c},
+ {0x3c19, 0x0a},
+ {0x3c1a, 0x0a},
+ {0x3c1b, 0x0a},
+ {0x3c1c, 0x00},
+ {0x3c1d, 0x00},
+ {0x3c1e, 0x00},
+ {0x3c1f, 0x00},
+ {0x3c20, 0x00},
+ {0x3c21, 0x00},
+ {0x3c22, 0x3f},
+ {0x3c23, 0x0a},
+ {0x3e35, 0x01},
+ {0x3f4a, 0x03},
+ {0x3f4b, 0xbf},
+ {0x3f26, 0x00},
+ {0x0202, 0x0d},
+ {0x0203, 0xc4},
+ {0x0204, 0x00},
+ {0x0205, 0x00},
+ {0x020e, 0x01},
+ {0x020f, 0x00},
+ {0x0210, 0x01},
+ {0x0211, 0x00},
+ {0x0212, 0x01},
+ {0x0213, 0x00},
+ {0x0214, 0x01},
+ {0x0215, 0x00},
+ {0xbcf1, 0x00},
+};
+
+/* Supported sensor mode configurations */
+static const struct imx412_mode supported_mode = {
+ .width = 4056,
+ .height = 3040,
+ .hblank = 456,
+ .vblank = 506,
+ .vblank_min = 506,
+ .vblank_max = 32420,
+ .pclk = 480000000,
+ .link_freq_idx = 0,
+ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_4056x3040_regs),
+ .regs = mode_4056x3040_regs,
+ },
+};
+
+/**
+ * to_imx412() - imx412 V4L2 sub-device to imx412 device.
+ * @subdev: pointer to imx412 V4L2 sub-device
+ *
+ * Return: pointer to imx412 device
+ */
+static inline struct imx412 *to_imx412(struct v4l2_subdev *subdev)
+{
+ return container_of(subdev, struct imx412, sd);
+}
+
+/**
+ * imx412_read_reg() - Read registers.
+ * @imx412: pointer to imx412 device
+ * @reg: register address
+ * @len: length of bytes to read. Max supported bytes is 4
+ * @val: pointer to register value to be filled.
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx412_read_reg(struct imx412 *imx412, u16 reg, u32 len, u32 *val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&imx412->sd);
+ struct i2c_msg msgs[2] = {0};
+ u8 addr_buf[2] = {0};
+ u8 data_buf[4] = {0};
+ int ret;
+
+ if (WARN_ON(len > 4))
+ return -EINVAL;
+
+ put_unaligned_be16(reg, addr_buf);
+
+ /* Write register address */
+ msgs[0].addr = client->addr;
+ msgs[0].flags = 0;
+ msgs[0].len = ARRAY_SIZE(addr_buf);
+ msgs[0].buf = addr_buf;
+
+ /* Read data from register */
+ msgs[1].addr = client->addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = len;
+ msgs[1].buf = &data_buf[4 - len];
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs))
+ return -EIO;
+
+ *val = get_unaligned_be32(data_buf);
+
+ return 0;
+}
+
+/**
+ * imx412_write_reg() - Write register
+ * @imx412: pointer to imx412 device
+ * @reg: register address
+ * @len: length of bytes. Max supported bytes is 4
+ * @val: register value
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx412_write_reg(struct imx412 *imx412, u16 reg, u32 len, u32 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&imx412->sd);
+ u8 buf[6] = {0};
+
+ if (WARN_ON(len > 4))
+ return -EINVAL;
+
+ put_unaligned_be16(reg, buf);
+ put_unaligned_be32(val << (8 * (4 - len)), buf + 2);
+ if (i2c_master_send(client, buf, len + 2) != len + 2)
+ return -EIO;
+
+ return 0;
+}
+
+/**
+ * imx412_write_regs() - Write a list of registers
+ * @imx412: pointer to imx412 device
+ * @regs: list of registers to be written
+ * @len: length of registers array
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx412_write_regs(struct imx412 *imx412,
+ const struct imx412_reg *regs, u32 len)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < len; i++) {
+ ret = imx412_write_reg(imx412, regs[i].address, 1, regs[i].val);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * imx412_update_controls() - Update control ranges based on streaming mode
+ * @imx412: pointer to imx412 device
+ * @mode: pointer to imx412_mode sensor mode
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx412_update_controls(struct imx412 *imx412,
+ const struct imx412_mode *mode)
+{
+ int ret;
+
+ ret = __v4l2_ctrl_s_ctrl(imx412->link_freq_ctrl, mode->link_freq_idx);
+ if (ret)
+ return ret;
+
+ ret = __v4l2_ctrl_s_ctrl(imx412->hblank_ctrl, mode->hblank);
+ if (ret)
+ return ret;
+
+ return __v4l2_ctrl_modify_range(imx412->vblank_ctrl, mode->vblank_min,
+ mode->vblank_max, 1, mode->vblank);
+}
+
+/**
+ * imx412_update_exp_gain() - Set updated exposure and gain
+ * @imx412: pointer to imx412 device
+ * @exposure: updated exposure value
+ * @gain: updated analog gain value
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx412_update_exp_gain(struct imx412 *imx412, u32 exposure, u32 gain)
+{
+ u32 lpfr, shutter;
+ int ret;
+
+ lpfr = imx412->vblank + imx412->cur_mode->height;
+ shutter = lpfr - exposure;
+
+ dev_dbg(imx412->dev, "Set exp %u, analog gain %u, shutter %u, lpfr %u",
+ exposure, gain, shutter, lpfr);
+
+ ret = imx412_write_reg(imx412, IMX412_REG_HOLD, 1, 1);
+ if (ret)
+ return ret;
+
+ ret = imx412_write_reg(imx412, IMX412_REG_LPFR, 2, lpfr);
+ if (ret)
+ goto error_release_group_hold;
+
+ ret = imx412_write_reg(imx412, IMX412_REG_EXPOSURE_CIT, 2, shutter);
+ if (ret)
+ goto error_release_group_hold;
+
+ ret = imx412_write_reg(imx412, IMX412_REG_AGAIN, 2, gain);
+
+error_release_group_hold:
+ imx412_write_reg(imx412, IMX412_REG_HOLD, 1, 0);
+
+ return ret;
+}
+
+/**
+ * imx412_set_ctrl() - Set subdevice control
+ * @ctrl: pointer to v4l2_ctrl structure
+ *
+ * Supported controls:
+ * - V4L2_CID_VBLANK
+ * - cluster controls:
+ * - V4L2_CID_ANALOGUE_GAIN
+ * - V4L2_CID_EXPOSURE
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx412_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct imx412 *imx412 =
+ container_of(ctrl->handler, struct imx412, ctrl_handler);
+ u32 analog_gain;
+ u32 exposure;
+ int ret;
+
+ switch (ctrl->id) {
+ case V4L2_CID_VBLANK:
+ imx412->vblank = imx412->vblank_ctrl->val;
+
+ dev_dbg(imx412->dev, "Received vblank %u, new lpfr %u",
+ imx412->vblank,
+ imx412->vblank + imx412->cur_mode->height);
+
+ ret = __v4l2_ctrl_modify_range(imx412->exp_ctrl,
+ IMX412_EXPOSURE_MIN,
+ imx412->vblank +
+ imx412->cur_mode->height -
+ IMX412_EXPOSURE_OFFSET,
+ 1, IMX412_EXPOSURE_DEFAULT);
+ break;
+ case V4L2_CID_EXPOSURE:
+ /* Set controls only if sensor is in power on state */
+ if (!pm_runtime_get_if_in_use(imx412->dev))
+ return 0;
+
+ exposure = ctrl->val;
+ analog_gain = imx412->again_ctrl->val;
+
+ dev_dbg(imx412->dev, "Received exp %u, analog gain %u",
+ exposure, analog_gain);
+
+ ret = imx412_update_exp_gain(imx412, exposure, analog_gain);
+
+ pm_runtime_put(imx412->dev);
+
+ break;
+ default:
+ dev_err(imx412->dev, "Invalid control %d", ctrl->id);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+/* V4l2 subdevice control ops*/
+static const struct v4l2_ctrl_ops imx412_ctrl_ops = {
+ .s_ctrl = imx412_set_ctrl,
+};
+
+/**
+ * imx412_enum_mbus_code() - Enumerate V4L2 sub-device mbus codes
+ * @sd: pointer to imx412 V4L2 sub-device structure
+ * @sd_state: V4L2 sub-device configuration
+ * @code: V4L2 sub-device code enumeration need to be filled
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx412_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index > 0)
+ return -EINVAL;
+
+ code->code = supported_mode.code;
+
+ return 0;
+}
+
+/**
+ * imx412_enum_frame_size() - Enumerate V4L2 sub-device frame sizes
+ * @sd: pointer to imx412 V4L2 sub-device structure
+ * @sd_state: V4L2 sub-device configuration
+ * @fsize: V4L2 sub-device size enumeration need to be filled
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx412_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_size_enum *fsize)
+{
+ if (fsize->index > 0)
+ return -EINVAL;
+
+ if (fsize->code != supported_mode.code)
+ return -EINVAL;
+
+ fsize->min_width = supported_mode.width;
+ fsize->max_width = fsize->min_width;
+ fsize->min_height = supported_mode.height;
+ fsize->max_height = fsize->min_height;
+
+ return 0;
+}
+
+/**
+ * imx412_fill_pad_format() - Fill subdevice pad format
+ * from selected sensor mode
+ * @imx412: pointer to imx412 device
+ * @mode: pointer to imx412_mode sensor mode
+ * @fmt: V4L2 sub-device format need to be filled
+ */
+static void imx412_fill_pad_format(struct imx412 *imx412,
+ const struct imx412_mode *mode,
+ struct v4l2_subdev_format *fmt)
+{
+ fmt->format.width = mode->width;
+ fmt->format.height = mode->height;
+ fmt->format.code = mode->code;
+ fmt->format.field = V4L2_FIELD_NONE;
+ fmt->format.colorspace = V4L2_COLORSPACE_RAW;
+ fmt->format.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ fmt->format.quantization = V4L2_QUANTIZATION_DEFAULT;
+ fmt->format.xfer_func = V4L2_XFER_FUNC_NONE;
+}
+
+/**
+ * imx412_get_pad_format() - Get subdevice pad format
+ * @sd: pointer to imx412 V4L2 sub-device structure
+ * @sd_state: V4L2 sub-device configuration
+ * @fmt: V4L2 sub-device format need to be set
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx412_get_pad_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct imx412 *imx412 = to_imx412(sd);
+
+ mutex_lock(&imx412->mutex);
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ struct v4l2_mbus_framefmt *framefmt;
+
+ framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad);
+ fmt->format = *framefmt;
+ } else {
+ imx412_fill_pad_format(imx412, imx412->cur_mode, fmt);
+ }
+
+ mutex_unlock(&imx412->mutex);
+
+ return 0;
+}
+
+/**
+ * imx412_set_pad_format() - Set subdevice pad format
+ * @sd: pointer to imx412 V4L2 sub-device structure
+ * @sd_state: V4L2 sub-device configuration
+ * @fmt: V4L2 sub-device format need to be set
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx412_set_pad_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct imx412 *imx412 = to_imx412(sd);
+ const struct imx412_mode *mode;
+ int ret = 0;
+
+ mutex_lock(&imx412->mutex);
+
+ mode = &supported_mode;
+ imx412_fill_pad_format(imx412, mode, fmt);
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ struct v4l2_mbus_framefmt *framefmt;
+
+ framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad);
+ *framefmt = fmt->format;
+ } else {
+ ret = imx412_update_controls(imx412, mode);
+ if (!ret)
+ imx412->cur_mode = mode;
+ }
+
+ mutex_unlock(&imx412->mutex);
+
+ return ret;
+}
+
+/**
+ * imx412_init_pad_cfg() - Initialize sub-device pad configuration
+ * @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)
+{
+ struct imx412 *imx412 = to_imx412(sd);
+ struct v4l2_subdev_format fmt = { 0 };
+
+ fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
+ imx412_fill_pad_format(imx412, &supported_mode, &fmt);
+
+ return imx412_set_pad_format(sd, sd_state, &fmt);
+}
+
+/**
+ * imx412_start_streaming() - Start sensor stream
+ * @imx412: pointer to imx412 device
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx412_start_streaming(struct imx412 *imx412)
+{
+ const struct imx412_reg_list *reg_list;
+ int ret;
+
+ /* Write sensor mode registers */
+ reg_list = &imx412->cur_mode->reg_list;
+ ret = imx412_write_regs(imx412, reg_list->regs,
+ reg_list->num_of_regs);
+ if (ret) {
+ dev_err(imx412->dev, "fail to write initial registers");
+ return ret;
+ }
+
+ /* Setup handler will write actual exposure and gain */
+ ret = __v4l2_ctrl_handler_setup(imx412->sd.ctrl_handler);
+ if (ret) {
+ dev_err(imx412->dev, "fail to setup handler");
+ return ret;
+ }
+
+ /* Delay is required before streaming*/
+ usleep_range(7400, 8000);
+
+ /* Start streaming */
+ ret = imx412_write_reg(imx412, IMX412_REG_MODE_SELECT,
+ 1, IMX412_MODE_STREAMING);
+ if (ret) {
+ dev_err(imx412->dev, "fail to start streaming");
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * imx412_stop_streaming() - Stop sensor stream
+ * @imx412: pointer to imx412 device
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx412_stop_streaming(struct imx412 *imx412)
+{
+ return imx412_write_reg(imx412, IMX412_REG_MODE_SELECT,
+ 1, IMX412_MODE_STANDBY);
+}
+
+/**
+ * imx412_set_stream() - Enable sensor streaming
+ * @sd: pointer to imx412 subdevice
+ * @enable: set to enable sensor streaming
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx412_set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct imx412 *imx412 = to_imx412(sd);
+ int ret;
+
+ mutex_lock(&imx412->mutex);
+
+ if (imx412->streaming == enable) {
+ mutex_unlock(&imx412->mutex);
+ return 0;
+ }
+
+ if (enable) {
+ ret = pm_runtime_resume_and_get(imx412->dev);
+ if (ret)
+ goto error_unlock;
+
+ ret = imx412_start_streaming(imx412);
+ if (ret)
+ goto error_power_off;
+ } else {
+ imx412_stop_streaming(imx412);
+ pm_runtime_put(imx412->dev);
+ }
+
+ imx412->streaming = enable;
+
+ mutex_unlock(&imx412->mutex);
+
+ return 0;
+
+error_power_off:
+ pm_runtime_put(imx412->dev);
+error_unlock:
+ mutex_unlock(&imx412->mutex);
+
+ return ret;
+}
+
+/**
+ * imx412_detect() - Detect imx412 sensor
+ * @imx412: pointer to imx412 device
+ *
+ * Return: 0 if successful, -EIO if sensor id does not match
+ */
+static int imx412_detect(struct imx412 *imx412)
+{
+ int ret;
+ u32 val;
+
+ ret = imx412_read_reg(imx412, IMX412_REG_ID, 2, &val);
+ if (ret)
+ return ret;
+
+ if (val != IMX412_ID) {
+ dev_err(imx412->dev, "chip id mismatch: %x!=%x",
+ IMX412_ID, val);
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+/**
+ * imx412_parse_hw_config() - Parse HW configuration and check if supported
+ * @imx412: pointer to imx412 device
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx412_parse_hw_config(struct imx412 *imx412)
+{
+ struct fwnode_handle *fwnode = dev_fwnode(imx412->dev);
+ struct v4l2_fwnode_endpoint bus_cfg = {
+ .bus_type = V4L2_MBUS_CSI2_DPHY
+ };
+ struct fwnode_handle *ep;
+ unsigned long rate;
+ unsigned int i;
+ int ret;
+
+ if (!fwnode)
+ return -ENXIO;
+
+ /* Request optional reset pin */
+ imx412->reset_gpio = devm_gpiod_get_optional(imx412->dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(imx412->reset_gpio)) {
+ dev_err(imx412->dev, "failed to get reset gpio %ld",
+ PTR_ERR(imx412->reset_gpio));
+ return PTR_ERR(imx412->reset_gpio);
+ }
+
+ /* Get sensor input clock */
+ imx412->inclk = devm_clk_get(imx412->dev, NULL);
+ if (IS_ERR(imx412->inclk)) {
+ dev_err(imx412->dev, "could not get inclk");
+ return PTR_ERR(imx412->inclk);
+ }
+
+ rate = clk_get_rate(imx412->inclk);
+ if (rate != IMX412_INCLK_RATE) {
+ dev_err(imx412->dev, "inclk frequency mismatch");
+ return -EINVAL;
+ }
+
+ ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
+ if (!ep)
+ return -ENXIO;
+
+ ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
+ fwnode_handle_put(ep);
+ if (ret)
+ return ret;
+
+ if (bus_cfg.bus.mipi_csi2.num_data_lanes != IMX412_NUM_DATA_LANES) {
+ dev_err(imx412->dev,
+ "number of CSI2 data lanes %d is not supported",
+ bus_cfg.bus.mipi_csi2.num_data_lanes);
+ ret = -EINVAL;
+ goto done_endpoint_free;
+ }
+
+ if (!bus_cfg.nr_of_link_frequencies) {
+ dev_err(imx412->dev, "no link frequencies defined");
+ ret = -EINVAL;
+ goto done_endpoint_free;
+ }
+
+ for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++)
+ if (bus_cfg.link_frequencies[i] == IMX412_LINK_FREQ)
+ goto done_endpoint_free;
+
+ ret = -EINVAL;
+
+done_endpoint_free:
+ v4l2_fwnode_endpoint_free(&bus_cfg);
+
+ return ret;
+}
+
+/* V4l2 subdevice ops */
+static const struct v4l2_subdev_video_ops imx412_video_ops = {
+ .s_stream = imx412_set_stream,
+};
+
+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,
+ .set_fmt = imx412_set_pad_format,
+};
+
+static const struct v4l2_subdev_ops imx412_subdev_ops = {
+ .video = &imx412_video_ops,
+ .pad = &imx412_pad_ops,
+};
+
+/**
+ * imx412_power_on() - Sensor power on sequence
+ * @dev: pointer to i2c device
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx412_power_on(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct imx412 *imx412 = to_imx412(sd);
+ int ret;
+
+ gpiod_set_value_cansleep(imx412->reset_gpio, 1);
+
+ ret = clk_prepare_enable(imx412->inclk);
+ if (ret) {
+ dev_err(imx412->dev, "fail to enable inclk");
+ goto error_reset;
+ }
+
+ usleep_range(1000, 1200);
+
+ return 0;
+
+error_reset:
+ gpiod_set_value_cansleep(imx412->reset_gpio, 0);
+
+ return ret;
+}
+
+/**
+ * imx412_power_off() - Sensor power off sequence
+ * @dev: pointer to i2c device
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx412_power_off(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct imx412 *imx412 = to_imx412(sd);
+
+ gpiod_set_value_cansleep(imx412->reset_gpio, 0);
+
+ clk_disable_unprepare(imx412->inclk);
+
+ return 0;
+}
+
+/**
+ * imx412_init_controls() - Initialize sensor subdevice controls
+ * @imx412: pointer to imx412 device
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx412_init_controls(struct imx412 *imx412)
+{
+ struct v4l2_ctrl_handler *ctrl_hdlr = &imx412->ctrl_handler;
+ const struct imx412_mode *mode = imx412->cur_mode;
+ u32 lpfr;
+ int ret;
+
+ ret = v4l2_ctrl_handler_init(ctrl_hdlr, 6);
+ if (ret)
+ return ret;
+
+ /* Serialize controls with sensor device */
+ ctrl_hdlr->lock = &imx412->mutex;
+
+ /* Initialize exposure and gain */
+ lpfr = mode->vblank + mode->height;
+ imx412->exp_ctrl = v4l2_ctrl_new_std(ctrl_hdlr,
+ &imx412_ctrl_ops,
+ V4L2_CID_EXPOSURE,
+ IMX412_EXPOSURE_MIN,
+ lpfr - IMX412_EXPOSURE_OFFSET,
+ IMX412_EXPOSURE_STEP,
+ IMX412_EXPOSURE_DEFAULT);
+
+ imx412->again_ctrl = v4l2_ctrl_new_std(ctrl_hdlr,
+ &imx412_ctrl_ops,
+ V4L2_CID_ANALOGUE_GAIN,
+ IMX412_AGAIN_MIN,
+ IMX412_AGAIN_MAX,
+ IMX412_AGAIN_STEP,
+ IMX412_AGAIN_DEFAULT);
+
+ v4l2_ctrl_cluster(2, &imx412->exp_ctrl);
+
+ imx412->vblank_ctrl = v4l2_ctrl_new_std(ctrl_hdlr,
+ &imx412_ctrl_ops,
+ V4L2_CID_VBLANK,
+ mode->vblank_min,
+ mode->vblank_max,
+ 1, mode->vblank);
+
+ /* Read only controls */
+ imx412->pclk_ctrl = v4l2_ctrl_new_std(ctrl_hdlr,
+ &imx412_ctrl_ops,
+ V4L2_CID_PIXEL_RATE,
+ mode->pclk, mode->pclk,
+ 1, mode->pclk);
+
+ imx412->link_freq_ctrl = v4l2_ctrl_new_int_menu(ctrl_hdlr,
+ &imx412_ctrl_ops,
+ V4L2_CID_LINK_FREQ,
+ ARRAY_SIZE(link_freq) -
+ 1,
+ mode->link_freq_idx,
+ link_freq);
+ if (imx412->link_freq_ctrl)
+ imx412->link_freq_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ imx412->hblank_ctrl = v4l2_ctrl_new_std(ctrl_hdlr,
+ &imx412_ctrl_ops,
+ V4L2_CID_HBLANK,
+ IMX412_REG_MIN,
+ IMX412_REG_MAX,
+ 1, mode->hblank);
+ if (imx412->hblank_ctrl)
+ imx412->hblank_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ if (ctrl_hdlr->error) {
+ dev_err(imx412->dev, "control init failed: %d",
+ ctrl_hdlr->error);
+ v4l2_ctrl_handler_free(ctrl_hdlr);
+ return ctrl_hdlr->error;
+ }
+
+ imx412->sd.ctrl_handler = ctrl_hdlr;
+
+ return 0;
+}
+
+/**
+ * imx412_probe() - I2C client device binding
+ * @client: pointer to i2c client device
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx412_probe(struct i2c_client *client)
+{
+ struct imx412 *imx412;
+ int ret;
+
+ imx412 = devm_kzalloc(&client->dev, sizeof(*imx412), GFP_KERNEL);
+ if (!imx412)
+ return -ENOMEM;
+
+ imx412->dev = &client->dev;
+
+ /* Initialize subdev */
+ v4l2_i2c_subdev_init(&imx412->sd, client, &imx412_subdev_ops);
+
+ ret = imx412_parse_hw_config(imx412);
+ if (ret) {
+ dev_err(imx412->dev, "HW configuration is not supported");
+ return ret;
+ }
+
+ mutex_init(&imx412->mutex);
+
+ ret = imx412_power_on(imx412->dev);
+ if (ret) {
+ dev_err(imx412->dev, "failed to power-on the sensor");
+ goto error_mutex_destroy;
+ }
+
+ /* Check module identity */
+ ret = imx412_detect(imx412);
+ if (ret) {
+ dev_err(imx412->dev, "failed to find sensor: %d", ret);
+ goto error_power_off;
+ }
+
+ /* Set default mode to max resolution */
+ imx412->cur_mode = &supported_mode;
+ imx412->vblank = imx412->cur_mode->vblank;
+
+ ret = imx412_init_controls(imx412);
+ if (ret) {
+ dev_err(imx412->dev, "failed to init controls: %d", ret);
+ goto error_power_off;
+ }
+
+ /* Initialize subdev */
+ imx412->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ imx412->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+
+ /* Initialize source pad */
+ imx412->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&imx412->sd.entity, 1, &imx412->pad);
+ if (ret) {
+ dev_err(imx412->dev, "failed to init entity pads: %d", ret);
+ goto error_handler_free;
+ }
+
+ ret = v4l2_async_register_subdev_sensor(&imx412->sd);
+ if (ret < 0) {
+ dev_err(imx412->dev,
+ "failed to register async subdev: %d", ret);
+ goto error_media_entity;
+ }
+
+ pm_runtime_set_active(imx412->dev);
+ pm_runtime_enable(imx412->dev);
+ pm_runtime_idle(imx412->dev);
+
+ return 0;
+
+error_media_entity:
+ media_entity_cleanup(&imx412->sd.entity);
+error_handler_free:
+ v4l2_ctrl_handler_free(imx412->sd.ctrl_handler);
+error_power_off:
+ imx412_power_off(imx412->dev);
+error_mutex_destroy:
+ mutex_destroy(&imx412->mutex);
+
+ return ret;
+}
+
+/**
+ * imx412_remove() - I2C client device unbinding
+ * @client: pointer to I2C client device
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int imx412_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct imx412 *imx412 = to_imx412(sd);
+
+ v4l2_async_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
+
+ pm_runtime_disable(&client->dev);
+ if (!pm_runtime_status_suspended(&client->dev))
+ imx412_power_off(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+
+ mutex_destroy(&imx412->mutex);
+
+ return 0;
+}
+
+static const struct dev_pm_ops imx412_pm_ops = {
+ SET_RUNTIME_PM_OPS(imx412_power_off, imx412_power_on, NULL)
+};
+
+static const struct of_device_id imx412_of_match[] = {
+ { .compatible = "sony,imx412" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, imx412_of_match);
+
+static struct i2c_driver imx412_driver = {
+ .probe_new = imx412_probe,
+ .remove = imx412_remove,
+ .driver = {
+ .name = "imx412",
+ .pm = &imx412_pm_ops,
+ .of_match_table = imx412_of_match,
+ },
+};
+
+module_i2c_driver(imx412_driver);
+
+MODULE_DESCRIPTION("Sony imx412 sensor driver");
+MODULE_LICENSE("GPL");
#define OV2740_REG_MWB_R_GAIN 0x500a
#define OV2740_REG_MWB_G_GAIN 0x500c
#define OV2740_REG_MWB_B_GAIN 0x500e
-#define OV2740_DGTL_GAIN_MIN 0
+#define OV2740_DGTL_GAIN_MIN 1024
#define OV2740_DGTL_GAIN_MAX 4095
#define OV2740_DGTL_GAIN_STEP 1
#define OV2740_DGTL_GAIN_DEFAULT 1024
#define OV2740_TEST_PATTERN_ENABLE BIT(7)
#define OV2740_TEST_PATTERN_BAR_SHIFT 2
+/* Group Access */
+#define OV2740_REG_GROUP_ACCESS 0x3208
+#define OV2740_GROUP_HOLD_START 0x0
+#define OV2740_GROUP_HOLD_END 0x10
+#define OV2740_GROUP_HOLD_LAUNCH 0xa0
+
/* ISP CTRL00 */
#define OV2740_REG_ISP_CTRL00 0x5000
/* ISP CTRL01 */
{
int ret = 0;
+ ret = ov2740_write_reg(ov2740, OV2740_REG_GROUP_ACCESS, 1,
+ OV2740_GROUP_HOLD_START);
+ if (ret)
+ return ret;
+
ret = ov2740_write_reg(ov2740, OV2740_REG_MWB_R_GAIN, 2, d_gain);
if (ret)
return ret;
if (ret)
return ret;
- return ov2740_write_reg(ov2740, OV2740_REG_MWB_B_GAIN, 2, d_gain);
+ ret = ov2740_write_reg(ov2740, OV2740_REG_MWB_B_GAIN, 2, d_gain);
+ if (ret)
+ return ret;
+
+ ret = ov2740_write_reg(ov2740, OV2740_REG_GROUP_ACCESS, 1,
+ OV2740_GROUP_HOLD_END);
+ if (ret)
+ return ret;
+
+ ret = ov2740_write_reg(ov2740, OV2740_REG_GROUP_ACCESS, 1,
+ OV2740_GROUP_HOLD_LAUNCH);
+ return ret;
}
static int ov2740_test_pattern(struct ov2740 *ov2740, u32 pattern)
static const struct ov5640_pixfmt ov5640_formats[] = {
{ MEDIA_BUS_FMT_JPEG_1X8, V4L2_COLORSPACE_JPEG, },
{ MEDIA_BUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_SRGB, },
+ { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_COLORSPACE_SRGB, },
{ MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_SRGB, },
+ { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_COLORSPACE_SRGB, },
{ MEDIA_BUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB, },
{ MEDIA_BUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB, },
{ MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB, },
u8 fmt, mux;
switch (format->code) {
+ case MEDIA_BUS_FMT_UYVY8_1X16:
case MEDIA_BUS_FMT_UYVY8_2X8:
/* YUV422, UYVY */
fmt = 0x3f;
mux = OV5640_FMT_MUX_YUV422;
break;
+ case MEDIA_BUS_FMT_YUYV8_1X16:
case MEDIA_BUS_FMT_YUYV8_2X8:
/* YUV422, YUYV */
fmt = 0x30;
clk_set_rate(ov8856->xvclk, xvclk_rate);
xvclk_rate = clk_get_rate(ov8856->xvclk);
+
+ ov8856->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(ov8856->reset_gpio))
+ return PTR_ERR(ov8856->reset_gpio);
+
+ for (i = 0; i < ARRAY_SIZE(ov8856_supply_names); i++)
+ ov8856->supplies[i].supply = ov8856_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev,
+ ARRAY_SIZE(ov8856_supply_names),
+ ov8856->supplies);
+ if (ret)
+ return ret;
}
if (xvclk_rate != OV8856_XVCLK_19_2)
dev_warn(dev, "external clock rate %u is unsupported",
xvclk_rate);
- ov8856->reset_gpio = devm_gpiod_get_optional(dev, "reset",
- GPIOD_OUT_LOW);
- if (IS_ERR(ov8856->reset_gpio))
- return PTR_ERR(ov8856->reset_gpio);
-
- for (i = 0; i < ARRAY_SIZE(ov8856_supply_names); i++)
- ov8856->supplies[i].supply = ov8856_supply_names[i];
-
- ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ov8856_supply_names),
- ov8856->supplies);
- if (ret)
- return ret;
-
ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
if (!ep)
return -ENXIO;
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * OmniVision ov9282 Camera Sensor Driver
+ *
+ * Copyright (C) 2021 Intel Corporation
+ */
+#include <asm/unaligned.h>
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+/* Streaming Mode */
+#define OV9282_REG_MODE_SELECT 0x0100
+#define OV9282_MODE_STANDBY 0x00
+#define OV9282_MODE_STREAMING 0x01
+
+/* Lines per frame */
+#define OV9282_REG_LPFR 0x380e
+
+/* Chip ID */
+#define OV9282_REG_ID 0x300a
+#define OV9282_ID 0x9281
+
+/* Exposure control */
+#define OV9282_REG_EXPOSURE 0x3500
+#define OV9282_EXPOSURE_MIN 1
+#define OV9282_EXPOSURE_OFFSET 12
+#define OV9282_EXPOSURE_STEP 1
+#define OV9282_EXPOSURE_DEFAULT 0x0282
+
+/* Analog gain control */
+#define OV9282_REG_AGAIN 0x3509
+#define OV9282_AGAIN_MIN 0x10
+#define OV9282_AGAIN_MAX 0xff
+#define OV9282_AGAIN_STEP 1
+#define OV9282_AGAIN_DEFAULT 0x10
+
+/* Group hold register */
+#define OV9282_REG_HOLD 0x3308
+
+/* Input clock rate */
+#define OV9282_INCLK_RATE 24000000
+
+/* CSI2 HW configuration */
+#define OV9282_LINK_FREQ 400000000
+#define OV9282_NUM_DATA_LANES 2
+
+#define OV9282_REG_MIN 0x00
+#define OV9282_REG_MAX 0xfffff
+
+/**
+ * struct ov9282_reg - ov9282 sensor register
+ * @address: Register address
+ * @val: Register value
+ */
+struct ov9282_reg {
+ u16 address;
+ u8 val;
+};
+
+/**
+ * struct ov9282_reg_list - ov9282 sensor register list
+ * @num_of_regs: Number of registers in the list
+ * @regs: Pointer to register list
+ */
+struct ov9282_reg_list {
+ u32 num_of_regs;
+ const struct ov9282_reg *regs;
+};
+
+/**
+ * struct ov9282_mode - ov9282 sensor mode structure
+ * @width: Frame width
+ * @height: Frame height
+ * @code: Format code
+ * @hblank: Horizontal blanking in lines
+ * @vblank: Vertical blanking in lines
+ * @vblank_min: Minimum vertical blanking in lines
+ * @vblank_max: Maximum vertical blanking in lines
+ * @pclk: Sensor pixel clock
+ * @link_freq_idx: Link frequency index
+ * @reg_list: Register list for sensor mode
+ */
+struct ov9282_mode {
+ u32 width;
+ u32 height;
+ u32 code;
+ u32 hblank;
+ u32 vblank;
+ u32 vblank_min;
+ u32 vblank_max;
+ u64 pclk;
+ u32 link_freq_idx;
+ struct ov9282_reg_list reg_list;
+};
+
+/**
+ * struct ov9282 - ov9282 sensor device structure
+ * @dev: Pointer to generic device
+ * @client: Pointer to i2c client
+ * @sd: V4L2 sub-device
+ * @pad: Media pad. Only one pad supported
+ * @reset_gpio: Sensor reset gpio
+ * @inclk: Sensor input clock
+ * @ctrl_handler: V4L2 control handler
+ * @link_freq_ctrl: Pointer to link frequency control
+ * @pclk_ctrl: Pointer to pixel clock control
+ * @hblank_ctrl: Pointer to horizontal blanking control
+ * @vblank_ctrl: Pointer to vertical blanking control
+ * @exp_ctrl: Pointer to exposure control
+ * @again_ctrl: Pointer to analog gain control
+ * @vblank: Vertical blanking in lines
+ * @cur_mode: Pointer to current selected sensor mode
+ * @mutex: Mutex for serializing sensor controls
+ * @streaming: Flag indicating streaming state
+ */
+struct ov9282 {
+ struct device *dev;
+ struct i2c_client *client;
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+ struct gpio_desc *reset_gpio;
+ struct clk *inclk;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_ctrl *link_freq_ctrl;
+ struct v4l2_ctrl *pclk_ctrl;
+ struct v4l2_ctrl *hblank_ctrl;
+ struct v4l2_ctrl *vblank_ctrl;
+ struct {
+ struct v4l2_ctrl *exp_ctrl;
+ struct v4l2_ctrl *again_ctrl;
+ };
+ u32 vblank;
+ const struct ov9282_mode *cur_mode;
+ struct mutex mutex;
+ bool streaming;
+};
+
+static const s64 link_freq[] = {
+ OV9282_LINK_FREQ,
+};
+
+/* Sensor mode registers */
+static const struct ov9282_reg mode_1280x720_regs[] = {
+ {0x0302, 0x32},
+ {0x030d, 0x50},
+ {0x030e, 0x02},
+ {0x3001, 0x00},
+ {0x3004, 0x00},
+ {0x3005, 0x00},
+ {0x3006, 0x04},
+ {0x3011, 0x0a},
+ {0x3013, 0x18},
+ {0x301c, 0xf0},
+ {0x3022, 0x01},
+ {0x3030, 0x10},
+ {0x3039, 0x32},
+ {0x303a, 0x00},
+ {0x3500, 0x00},
+ {0x3501, 0x5f},
+ {0x3502, 0x1e},
+ {0x3503, 0x08},
+ {0x3505, 0x8c},
+ {0x3507, 0x03},
+ {0x3508, 0x00},
+ {0x3509, 0x10},
+ {0x3610, 0x80},
+ {0x3611, 0xa0},
+ {0x3620, 0x6e},
+ {0x3632, 0x56},
+ {0x3633, 0x78},
+ {0x3666, 0x00},
+ {0x366f, 0x5a},
+ {0x3680, 0x84},
+ {0x3712, 0x80},
+ {0x372d, 0x22},
+ {0x3731, 0x80},
+ {0x3732, 0x30},
+ {0x3778, 0x00},
+ {0x377d, 0x22},
+ {0x3788, 0x02},
+ {0x3789, 0xa4},
+ {0x378a, 0x00},
+ {0x378b, 0x4a},
+ {0x3799, 0x20},
+ {0x3800, 0x00},
+ {0x3801, 0x00},
+ {0x3802, 0x00},
+ {0x3803, 0x00},
+ {0x3804, 0x05},
+ {0x3805, 0x0f},
+ {0x3806, 0x02},
+ {0x3807, 0xdf},
+ {0x3808, 0x05},
+ {0x3809, 0x00},
+ {0x380a, 0x02},
+ {0x380b, 0xd0},
+ {0x380c, 0x05},
+ {0x380d, 0xfa},
+ {0x380e, 0x06},
+ {0x380f, 0xce},
+ {0x3810, 0x00},
+ {0x3811, 0x08},
+ {0x3812, 0x00},
+ {0x3813, 0x08},
+ {0x3814, 0x11},
+ {0x3815, 0x11},
+ {0x3820, 0x3c},
+ {0x3821, 0x84},
+ {0x3881, 0x42},
+ {0x38a8, 0x02},
+ {0x38a9, 0x80},
+ {0x38b1, 0x00},
+ {0x38c4, 0x00},
+ {0x38c5, 0xc0},
+ {0x38c6, 0x04},
+ {0x38c7, 0x80},
+ {0x3920, 0xff},
+ {0x4003, 0x40},
+ {0x4008, 0x02},
+ {0x4009, 0x05},
+ {0x400c, 0x00},
+ {0x400d, 0x03},
+ {0x4010, 0x40},
+ {0x4043, 0x40},
+ {0x4307, 0x30},
+ {0x4317, 0x00},
+ {0x4501, 0x00},
+ {0x4507, 0x00},
+ {0x4509, 0x80},
+ {0x450a, 0x08},
+ {0x4601, 0x04},
+ {0x470f, 0x00},
+ {0x4f07, 0x00},
+ {0x4800, 0x20},
+ {0x5000, 0x9f},
+ {0x5001, 0x00},
+ {0x5e00, 0x00},
+ {0x5d00, 0x07},
+ {0x5d01, 0x00},
+ {0x0101, 0x01},
+ {0x1000, 0x03},
+ {0x5a08, 0x84},
+};
+
+/* Supported sensor mode configurations */
+static const struct ov9282_mode supported_mode = {
+ .width = 1280,
+ .height = 720,
+ .hblank = 250,
+ .vblank = 1022,
+ .vblank_min = 151,
+ .vblank_max = 51540,
+ .pclk = 160000000,
+ .link_freq_idx = 0,
+ .code = MEDIA_BUS_FMT_Y10_1X10,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_1280x720_regs),
+ .regs = mode_1280x720_regs,
+ },
+};
+
+/**
+ * to_ov9282() - ov9282 V4L2 sub-device to ov9282 device.
+ * @subdev: pointer to ov9282 V4L2 sub-device
+ *
+ * Return: pointer to ov9282 device
+ */
+static inline struct ov9282 *to_ov9282(struct v4l2_subdev *subdev)
+{
+ return container_of(subdev, struct ov9282, sd);
+}
+
+/**
+ * ov9282_read_reg() - Read registers.
+ * @ov9282: pointer to ov9282 device
+ * @reg: register address
+ * @len: length of bytes to read. Max supported bytes is 4
+ * @val: pointer to register value to be filled.
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int ov9282_read_reg(struct ov9282 *ov9282, u16 reg, u32 len, u32 *val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov9282->sd);
+ struct i2c_msg msgs[2] = {0};
+ u8 addr_buf[2] = {0};
+ u8 data_buf[4] = {0};
+ int ret;
+
+ if (WARN_ON(len > 4))
+ return -EINVAL;
+
+ put_unaligned_be16(reg, addr_buf);
+
+ /* Write register address */
+ msgs[0].addr = client->addr;
+ msgs[0].flags = 0;
+ msgs[0].len = ARRAY_SIZE(addr_buf);
+ msgs[0].buf = addr_buf;
+
+ /* Read data from register */
+ msgs[1].addr = client->addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = len;
+ msgs[1].buf = &data_buf[4 - len];
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs))
+ return -EIO;
+
+ *val = get_unaligned_be32(data_buf);
+
+ return 0;
+}
+
+/**
+ * ov9282_write_reg() - Write register
+ * @ov9282: pointer to ov9282 device
+ * @reg: register address
+ * @len: length of bytes. Max supported bytes is 4
+ * @val: register value
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int ov9282_write_reg(struct ov9282 *ov9282, u16 reg, u32 len, u32 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov9282->sd);
+ u8 buf[6] = {0};
+
+ if (WARN_ON(len > 4))
+ return -EINVAL;
+
+ put_unaligned_be16(reg, buf);
+ put_unaligned_be32(val << (8 * (4 - len)), buf + 2);
+ if (i2c_master_send(client, buf, len + 2) != len + 2)
+ return -EIO;
+
+ return 0;
+}
+
+/**
+ * ov9282_write_regs() - Write a list of registers
+ * @ov9282: pointer to ov9282 device
+ * @regs: list of registers to be written
+ * @len: length of registers array
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int ov9282_write_regs(struct ov9282 *ov9282,
+ const struct ov9282_reg *regs, u32 len)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < len; i++) {
+ ret = ov9282_write_reg(ov9282, regs[i].address, 1, regs[i].val);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * ov9282_update_controls() - Update control ranges based on streaming mode
+ * @ov9282: pointer to ov9282 device
+ * @mode: pointer to ov9282_mode sensor mode
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int ov9282_update_controls(struct ov9282 *ov9282,
+ const struct ov9282_mode *mode)
+{
+ int ret;
+
+ ret = __v4l2_ctrl_s_ctrl(ov9282->link_freq_ctrl, mode->link_freq_idx);
+ if (ret)
+ return ret;
+
+ ret = __v4l2_ctrl_s_ctrl(ov9282->hblank_ctrl, mode->hblank);
+ if (ret)
+ return ret;
+
+ return __v4l2_ctrl_modify_range(ov9282->vblank_ctrl, mode->vblank_min,
+ mode->vblank_max, 1, mode->vblank);
+}
+
+/**
+ * ov9282_update_exp_gain() - Set updated exposure and gain
+ * @ov9282: pointer to ov9282 device
+ * @exposure: updated exposure value
+ * @gain: updated analog gain value
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int ov9282_update_exp_gain(struct ov9282 *ov9282, u32 exposure, u32 gain)
+{
+ u32 lpfr;
+ int ret;
+
+ lpfr = ov9282->vblank + ov9282->cur_mode->height;
+
+ dev_dbg(ov9282->dev, "Set exp %u, analog gain %u, lpfr %u",
+ exposure, gain, lpfr);
+
+ ret = ov9282_write_reg(ov9282, OV9282_REG_HOLD, 1, 1);
+ if (ret)
+ return ret;
+
+ ret = ov9282_write_reg(ov9282, OV9282_REG_LPFR, 2, lpfr);
+ if (ret)
+ goto error_release_group_hold;
+
+ ret = ov9282_write_reg(ov9282, OV9282_REG_EXPOSURE, 3, exposure << 4);
+ if (ret)
+ goto error_release_group_hold;
+
+ ret = ov9282_write_reg(ov9282, OV9282_REG_AGAIN, 1, gain);
+
+error_release_group_hold:
+ ov9282_write_reg(ov9282, OV9282_REG_HOLD, 1, 0);
+
+ return ret;
+}
+
+/**
+ * ov9282_set_ctrl() - Set subdevice control
+ * @ctrl: pointer to v4l2_ctrl structure
+ *
+ * Supported controls:
+ * - V4L2_CID_VBLANK
+ * - cluster controls:
+ * - V4L2_CID_ANALOGUE_GAIN
+ * - V4L2_CID_EXPOSURE
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int ov9282_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov9282 *ov9282 =
+ container_of(ctrl->handler, struct ov9282, ctrl_handler);
+ u32 analog_gain;
+ u32 exposure;
+ int ret;
+
+ switch (ctrl->id) {
+ case V4L2_CID_VBLANK:
+ ov9282->vblank = ov9282->vblank_ctrl->val;
+
+ dev_dbg(ov9282->dev, "Received vblank %u, new lpfr %u",
+ ov9282->vblank,
+ ov9282->vblank + ov9282->cur_mode->height);
+
+ ret = __v4l2_ctrl_modify_range(ov9282->exp_ctrl,
+ OV9282_EXPOSURE_MIN,
+ ov9282->vblank +
+ ov9282->cur_mode->height -
+ OV9282_EXPOSURE_OFFSET,
+ 1, OV9282_EXPOSURE_DEFAULT);
+ break;
+ case V4L2_CID_EXPOSURE:
+ /* Set controls only if sensor is in power on state */
+ if (!pm_runtime_get_if_in_use(ov9282->dev))
+ return 0;
+
+ exposure = ctrl->val;
+ analog_gain = ov9282->again_ctrl->val;
+
+ dev_dbg(ov9282->dev, "Received exp %u, analog gain %u",
+ exposure, analog_gain);
+
+ ret = ov9282_update_exp_gain(ov9282, exposure, analog_gain);
+
+ pm_runtime_put(ov9282->dev);
+
+ break;
+ default:
+ dev_err(ov9282->dev, "Invalid control %d", ctrl->id);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+/* V4l2 subdevice control ops*/
+static const struct v4l2_ctrl_ops ov9282_ctrl_ops = {
+ .s_ctrl = ov9282_set_ctrl,
+};
+
+/**
+ * ov9282_enum_mbus_code() - Enumerate V4L2 sub-device mbus codes
+ * @sd: pointer to ov9282 V4L2 sub-device structure
+ * @sd_state: V4L2 sub-device configuration
+ * @code: V4L2 sub-device code enumeration need to be filled
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int ov9282_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index > 0)
+ return -EINVAL;
+
+ code->code = supported_mode.code;
+
+ return 0;
+}
+
+/**
+ * ov9282_enum_frame_size() - Enumerate V4L2 sub-device frame sizes
+ * @sd: pointer to ov9282 V4L2 sub-device structure
+ * @sd_state: V4L2 sub-device configuration
+ * @fsize: V4L2 sub-device size enumeration need to be filled
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int ov9282_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_size_enum *fsize)
+{
+ if (fsize->index > 0)
+ return -EINVAL;
+
+ if (fsize->code != supported_mode.code)
+ return -EINVAL;
+
+ fsize->min_width = supported_mode.width;
+ fsize->max_width = fsize->min_width;
+ fsize->min_height = supported_mode.height;
+ fsize->max_height = fsize->min_height;
+
+ return 0;
+}
+
+/**
+ * ov9282_fill_pad_format() - Fill subdevice pad format
+ * from selected sensor mode
+ * @ov9282: pointer to ov9282 device
+ * @mode: pointer to ov9282_mode sensor mode
+ * @fmt: V4L2 sub-device format need to be filled
+ */
+static void ov9282_fill_pad_format(struct ov9282 *ov9282,
+ const struct ov9282_mode *mode,
+ struct v4l2_subdev_format *fmt)
+{
+ fmt->format.width = mode->width;
+ fmt->format.height = mode->height;
+ fmt->format.code = mode->code;
+ fmt->format.field = V4L2_FIELD_NONE;
+ fmt->format.colorspace = V4L2_COLORSPACE_RAW;
+ fmt->format.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ fmt->format.quantization = V4L2_QUANTIZATION_DEFAULT;
+ fmt->format.xfer_func = V4L2_XFER_FUNC_NONE;
+}
+
+/**
+ * ov9282_get_pad_format() - Get subdevice pad format
+ * @sd: pointer to ov9282 V4L2 sub-device structure
+ * @sd_state: V4L2 sub-device configuration
+ * @fmt: V4L2 sub-device format need to be set
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int ov9282_get_pad_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct ov9282 *ov9282 = to_ov9282(sd);
+
+ mutex_lock(&ov9282->mutex);
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ struct v4l2_mbus_framefmt *framefmt;
+
+ framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad);
+ fmt->format = *framefmt;
+ } else {
+ ov9282_fill_pad_format(ov9282, ov9282->cur_mode, fmt);
+ }
+
+ mutex_unlock(&ov9282->mutex);
+
+ return 0;
+}
+
+/**
+ * ov9282_set_pad_format() - Set subdevice pad format
+ * @sd: pointer to ov9282 V4L2 sub-device structure
+ * @sd_state: V4L2 sub-device configuration
+ * @fmt: V4L2 sub-device format need to be set
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int ov9282_set_pad_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct ov9282 *ov9282 = to_ov9282(sd);
+ const struct ov9282_mode *mode;
+ int ret = 0;
+
+ mutex_lock(&ov9282->mutex);
+
+ mode = &supported_mode;
+ ov9282_fill_pad_format(ov9282, mode, fmt);
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ struct v4l2_mbus_framefmt *framefmt;
+
+ framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad);
+ *framefmt = fmt->format;
+ } else {
+ ret = ov9282_update_controls(ov9282, mode);
+ if (!ret)
+ ov9282->cur_mode = mode;
+ }
+
+ mutex_unlock(&ov9282->mutex);
+
+ return ret;
+}
+
+/**
+ * ov9282_init_pad_cfg() - Initialize sub-device pad configuration
+ * @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)
+{
+ struct ov9282 *ov9282 = to_ov9282(sd);
+ struct v4l2_subdev_format fmt = { 0 };
+
+ fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
+ ov9282_fill_pad_format(ov9282, &supported_mode, &fmt);
+
+ return ov9282_set_pad_format(sd, sd_state, &fmt);
+}
+
+/**
+ * ov9282_start_streaming() - Start sensor stream
+ * @ov9282: pointer to ov9282 device
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int ov9282_start_streaming(struct ov9282 *ov9282)
+{
+ const struct ov9282_reg_list *reg_list;
+ int ret;
+
+ /* Write sensor mode registers */
+ reg_list = &ov9282->cur_mode->reg_list;
+ ret = ov9282_write_regs(ov9282, reg_list->regs, reg_list->num_of_regs);
+ if (ret) {
+ dev_err(ov9282->dev, "fail to write initial registers");
+ return ret;
+ }
+
+ /* Setup handler will write actual exposure and gain */
+ ret = __v4l2_ctrl_handler_setup(ov9282->sd.ctrl_handler);
+ if (ret) {
+ dev_err(ov9282->dev, "fail to setup handler");
+ return ret;
+ }
+
+ /* Start streaming */
+ ret = ov9282_write_reg(ov9282, OV9282_REG_MODE_SELECT,
+ 1, OV9282_MODE_STREAMING);
+ if (ret) {
+ dev_err(ov9282->dev, "fail to start streaming");
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * ov9282_stop_streaming() - Stop sensor stream
+ * @ov9282: pointer to ov9282 device
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int ov9282_stop_streaming(struct ov9282 *ov9282)
+{
+ return ov9282_write_reg(ov9282, OV9282_REG_MODE_SELECT,
+ 1, OV9282_MODE_STANDBY);
+}
+
+/**
+ * ov9282_set_stream() - Enable sensor streaming
+ * @sd: pointer to ov9282 subdevice
+ * @enable: set to enable sensor streaming
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int ov9282_set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct ov9282 *ov9282 = to_ov9282(sd);
+ int ret;
+
+ 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)
+ goto error_unlock;
+
+ ret = ov9282_start_streaming(ov9282);
+ if (ret)
+ goto error_power_off;
+ } else {
+ ov9282_stop_streaming(ov9282);
+ pm_runtime_put(ov9282->dev);
+ }
+
+ ov9282->streaming = enable;
+
+ mutex_unlock(&ov9282->mutex);
+
+ return 0;
+
+error_power_off:
+ pm_runtime_put(ov9282->dev);
+error_unlock:
+ mutex_unlock(&ov9282->mutex);
+
+ return ret;
+}
+
+/**
+ * ov9282_detect() - Detect ov9282 sensor
+ * @ov9282: pointer to ov9282 device
+ *
+ * Return: 0 if successful, -EIO if sensor id does not match
+ */
+static int ov9282_detect(struct ov9282 *ov9282)
+{
+ int ret;
+ u32 val;
+
+ ret = ov9282_read_reg(ov9282, OV9282_REG_ID, 2, &val);
+ if (ret)
+ return ret;
+
+ if (val != OV9282_ID) {
+ dev_err(ov9282->dev, "chip id mismatch: %x!=%x",
+ OV9282_ID, val);
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+/**
+ * ov9282_parse_hw_config() - Parse HW configuration and check if supported
+ * @ov9282: pointer to ov9282 device
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int ov9282_parse_hw_config(struct ov9282 *ov9282)
+{
+ struct fwnode_handle *fwnode = dev_fwnode(ov9282->dev);
+ struct v4l2_fwnode_endpoint bus_cfg = {
+ .bus_type = V4L2_MBUS_CSI2_DPHY
+ };
+ struct fwnode_handle *ep;
+ unsigned long rate;
+ unsigned int i;
+ int ret;
+
+ if (!fwnode)
+ return -ENXIO;
+
+ /* Request optional reset pin */
+ ov9282->reset_gpio = devm_gpiod_get_optional(ov9282->dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(ov9282->reset_gpio)) {
+ dev_err(ov9282->dev, "failed to get reset gpio %ld",
+ PTR_ERR(ov9282->reset_gpio));
+ return PTR_ERR(ov9282->reset_gpio);
+ }
+
+ /* Get sensor input clock */
+ ov9282->inclk = devm_clk_get(ov9282->dev, NULL);
+ if (IS_ERR(ov9282->inclk)) {
+ dev_err(ov9282->dev, "could not get inclk");
+ return PTR_ERR(ov9282->inclk);
+ }
+
+ rate = clk_get_rate(ov9282->inclk);
+ if (rate != OV9282_INCLK_RATE) {
+ dev_err(ov9282->dev, "inclk frequency mismatch");
+ return -EINVAL;
+ }
+
+ ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
+ if (!ep)
+ return -ENXIO;
+
+ ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
+ fwnode_handle_put(ep);
+ if (ret)
+ return ret;
+
+ if (bus_cfg.bus.mipi_csi2.num_data_lanes != OV9282_NUM_DATA_LANES) {
+ dev_err(ov9282->dev,
+ "number of CSI2 data lanes %d is not supported",
+ bus_cfg.bus.mipi_csi2.num_data_lanes);
+ ret = -EINVAL;
+ goto done_endpoint_free;
+ }
+
+ if (!bus_cfg.nr_of_link_frequencies) {
+ dev_err(ov9282->dev, "no link frequencies defined");
+ ret = -EINVAL;
+ goto done_endpoint_free;
+ }
+
+ for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++)
+ if (bus_cfg.link_frequencies[i] == OV9282_LINK_FREQ)
+ goto done_endpoint_free;
+
+ ret = -EINVAL;
+
+done_endpoint_free:
+ v4l2_fwnode_endpoint_free(&bus_cfg);
+
+ return ret;
+}
+
+/* V4l2 subdevice ops */
+static const struct v4l2_subdev_video_ops ov9282_video_ops = {
+ .s_stream = ov9282_set_stream,
+};
+
+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,
+ .set_fmt = ov9282_set_pad_format,
+};
+
+static const struct v4l2_subdev_ops ov9282_subdev_ops = {
+ .video = &ov9282_video_ops,
+ .pad = &ov9282_pad_ops,
+};
+
+/**
+ * ov9282_power_on() - Sensor power on sequence
+ * @dev: pointer to i2c device
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int ov9282_power_on(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct ov9282 *ov9282 = to_ov9282(sd);
+ int ret;
+
+ usleep_range(400, 600);
+
+ gpiod_set_value_cansleep(ov9282->reset_gpio, 1);
+
+ ret = clk_prepare_enable(ov9282->inclk);
+ if (ret) {
+ dev_err(ov9282->dev, "fail to enable inclk");
+ goto error_reset;
+ }
+
+ usleep_range(400, 600);
+
+ return 0;
+
+error_reset:
+ gpiod_set_value_cansleep(ov9282->reset_gpio, 0);
+
+ return ret;
+}
+
+/**
+ * ov9282_power_off() - Sensor power off sequence
+ * @dev: pointer to i2c device
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int ov9282_power_off(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct ov9282 *ov9282 = to_ov9282(sd);
+
+ gpiod_set_value_cansleep(ov9282->reset_gpio, 0);
+
+ clk_disable_unprepare(ov9282->inclk);
+
+ return 0;
+}
+
+/**
+ * ov9282_init_controls() - Initialize sensor subdevice controls
+ * @ov9282: pointer to ov9282 device
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int ov9282_init_controls(struct ov9282 *ov9282)
+{
+ struct v4l2_ctrl_handler *ctrl_hdlr = &ov9282->ctrl_handler;
+ const struct ov9282_mode *mode = ov9282->cur_mode;
+ u32 lpfr;
+ int ret;
+
+ ret = v4l2_ctrl_handler_init(ctrl_hdlr, 6);
+ if (ret)
+ return ret;
+
+ /* Serialize controls with sensor device */
+ ctrl_hdlr->lock = &ov9282->mutex;
+
+ /* Initialize exposure and gain */
+ lpfr = mode->vblank + mode->height;
+ ov9282->exp_ctrl = v4l2_ctrl_new_std(ctrl_hdlr,
+ &ov9282_ctrl_ops,
+ V4L2_CID_EXPOSURE,
+ OV9282_EXPOSURE_MIN,
+ lpfr - OV9282_EXPOSURE_OFFSET,
+ OV9282_EXPOSURE_STEP,
+ OV9282_EXPOSURE_DEFAULT);
+
+ ov9282->again_ctrl = v4l2_ctrl_new_std(ctrl_hdlr,
+ &ov9282_ctrl_ops,
+ V4L2_CID_ANALOGUE_GAIN,
+ OV9282_AGAIN_MIN,
+ OV9282_AGAIN_MAX,
+ OV9282_AGAIN_STEP,
+ OV9282_AGAIN_DEFAULT);
+
+ v4l2_ctrl_cluster(2, &ov9282->exp_ctrl);
+
+ ov9282->vblank_ctrl = v4l2_ctrl_new_std(ctrl_hdlr,
+ &ov9282_ctrl_ops,
+ V4L2_CID_VBLANK,
+ mode->vblank_min,
+ mode->vblank_max,
+ 1, mode->vblank);
+
+ /* Read only controls */
+ ov9282->pclk_ctrl = v4l2_ctrl_new_std(ctrl_hdlr,
+ &ov9282_ctrl_ops,
+ V4L2_CID_PIXEL_RATE,
+ mode->pclk, mode->pclk,
+ 1, mode->pclk);
+
+ ov9282->link_freq_ctrl = v4l2_ctrl_new_int_menu(ctrl_hdlr,
+ &ov9282_ctrl_ops,
+ V4L2_CID_LINK_FREQ,
+ ARRAY_SIZE(link_freq) -
+ 1,
+ mode->link_freq_idx,
+ link_freq);
+ if (ov9282->link_freq_ctrl)
+ ov9282->link_freq_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ ov9282->hblank_ctrl = v4l2_ctrl_new_std(ctrl_hdlr,
+ &ov9282_ctrl_ops,
+ V4L2_CID_HBLANK,
+ OV9282_REG_MIN,
+ OV9282_REG_MAX,
+ 1, mode->hblank);
+ if (ov9282->hblank_ctrl)
+ ov9282->hblank_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ if (ctrl_hdlr->error) {
+ dev_err(ov9282->dev, "control init failed: %d",
+ ctrl_hdlr->error);
+ v4l2_ctrl_handler_free(ctrl_hdlr);
+ return ctrl_hdlr->error;
+ }
+
+ ov9282->sd.ctrl_handler = ctrl_hdlr;
+
+ return 0;
+}
+
+/**
+ * ov9282_probe() - I2C client device binding
+ * @client: pointer to i2c client device
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int ov9282_probe(struct i2c_client *client)
+{
+ struct ov9282 *ov9282;
+ int ret;
+
+ ov9282 = devm_kzalloc(&client->dev, sizeof(*ov9282), GFP_KERNEL);
+ if (!ov9282)
+ return -ENOMEM;
+
+ ov9282->dev = &client->dev;
+
+ /* Initialize subdev */
+ v4l2_i2c_subdev_init(&ov9282->sd, client, &ov9282_subdev_ops);
+
+ ret = ov9282_parse_hw_config(ov9282);
+ if (ret) {
+ dev_err(ov9282->dev, "HW configuration is not supported");
+ return ret;
+ }
+
+ mutex_init(&ov9282->mutex);
+
+ ret = ov9282_power_on(ov9282->dev);
+ if (ret) {
+ dev_err(ov9282->dev, "failed to power-on the sensor");
+ goto error_mutex_destroy;
+ }
+
+ /* Check module identity */
+ ret = ov9282_detect(ov9282);
+ if (ret) {
+ dev_err(ov9282->dev, "failed to find sensor: %d", ret);
+ goto error_power_off;
+ }
+
+ /* Set default mode to max resolution */
+ ov9282->cur_mode = &supported_mode;
+ ov9282->vblank = ov9282->cur_mode->vblank;
+
+ ret = ov9282_init_controls(ov9282);
+ if (ret) {
+ dev_err(ov9282->dev, "failed to init controls: %d", ret);
+ goto error_power_off;
+ }
+
+ /* Initialize subdev */
+ ov9282->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ ov9282->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+
+ /* Initialize source pad */
+ ov9282->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&ov9282->sd.entity, 1, &ov9282->pad);
+ if (ret) {
+ dev_err(ov9282->dev, "failed to init entity pads: %d", ret);
+ goto error_handler_free;
+ }
+
+ ret = v4l2_async_register_subdev_sensor(&ov9282->sd);
+ if (ret < 0) {
+ dev_err(ov9282->dev,
+ "failed to register async subdev: %d", ret);
+ goto error_media_entity;
+ }
+
+ pm_runtime_set_active(ov9282->dev);
+ pm_runtime_enable(ov9282->dev);
+ pm_runtime_idle(ov9282->dev);
+
+ return 0;
+
+error_media_entity:
+ media_entity_cleanup(&ov9282->sd.entity);
+error_handler_free:
+ v4l2_ctrl_handler_free(ov9282->sd.ctrl_handler);
+error_power_off:
+ ov9282_power_off(ov9282->dev);
+error_mutex_destroy:
+ mutex_destroy(&ov9282->mutex);
+
+ return ret;
+}
+
+/**
+ * ov9282_remove() - I2C client device unbinding
+ * @client: pointer to I2C client device
+ *
+ * Return: 0 if successful, error code otherwise.
+ */
+static int ov9282_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov9282 *ov9282 = to_ov9282(sd);
+
+ v4l2_async_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
+
+ pm_runtime_disable(&client->dev);
+ if (!pm_runtime_status_suspended(&client->dev))
+ ov9282_power_off(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+
+ mutex_destroy(&ov9282->mutex);
+
+ return 0;
+}
+
+static const struct dev_pm_ops ov9282_pm_ops = {
+ SET_RUNTIME_PM_OPS(ov9282_power_off, ov9282_power_on, NULL)
+};
+
+static const struct of_device_id ov9282_of_match[] = {
+ { .compatible = "ovti,ov9282" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, ov9282_of_match);
+
+static struct i2c_driver ov9282_driver = {
+ .probe_new = ov9282_probe,
+ .remove = ov9282_remove,
+ .driver = {
+ .name = "ov9282",
+ .pm = &ov9282_pm_ops,
+ .of_match_table = ov9282_of_match,
+ },
+};
+
+module_i2c_driver(ov9282_driver);
+
+MODULE_DESCRIPTION("OmniVision ov9282 sensor driver");
+MODULE_LICENSE("GPL");
#define OV9734_TEST_PATTERN_ENABLE BIT(7)
#define OV9734_TEST_PATTERN_BAR_SHIFT 2
+/* Group Access */
+#define OV9734_REG_GROUP_ACCESS 0x3208
+#define OV9734_GROUP_HOLD_START 0x0
+#define OV9734_GROUP_HOLD_END 0x10
+#define OV9734_GROUP_HOLD_LAUNCH 0xa0
+
enum {
OV9734_LINK_FREQ_180MHZ_INDEX,
};
{
int ret;
+ ret = ov9734_write_reg(ov9734, OV9734_REG_GROUP_ACCESS, 1,
+ OV9734_GROUP_HOLD_START);
+ if (ret)
+ return ret;
+
ret = ov9734_write_reg(ov9734, OV9734_REG_MWB_R_GAIN, 2, d_gain);
if (ret)
return ret;
if (ret)
return ret;
- return ov9734_write_reg(ov9734, OV9734_REG_MWB_B_GAIN, 2, d_gain);
+ ret = ov9734_write_reg(ov9734, OV9734_REG_MWB_B_GAIN, 2, d_gain);
+ if (ret)
+ return ret;
+
+ ret = ov9734_write_reg(ov9734, OV9734_REG_GROUP_ACCESS, 1,
+ OV9734_GROUP_HOLD_END);
+ if (ret)
+ return ret;
+
+ ret = ov9734_write_reg(ov9734, OV9734_REG_GROUP_ACCESS, 1,
+ OV9734_GROUP_HOLD_LAUNCH);
+ return ret;
}
static int ov9734_test_pattern(struct ov9734 *ov9734, u32 pattern)
delayed_work_enable_hpd);
struct v4l2_subdev *sd = &state->sd;
- v4l2_dbg(2, debug, sd, "%s:\n", __func__);
+ v4l2_dbg(2, debug, sd, "%s\n", __func__);
/* Set HPD high */
tda1997x_manual_hpd(sd, HPD_HIGH_OTHER);
hper = io_read16(sd, REG_H_PER) & MASK_HPER;
hsper = io_read16(sd, REG_HS_WIDTH) & MASK_HSWIDTH;
v4l2_dbg(1, debug, sd, "Signal Timings: %u/%u/%u\n", vper, hper, hsper);
- if (!vper || !hper || !hsper)
+
+ if (!state->input_detect[0] && !state->input_detect[1])
return -ENOLINK;
for (i = 0; v4l2_dv_timings_presets[i].bt.width; i++) {
struct v4l2_dv_timings *timings)
{
struct tda1997x_state *state = to_state(sd);
+ int ret;
v4l_dbg(1, debug, state->client, "%s\n", __func__);
memset(timings, 0, sizeof(struct v4l2_dv_timings));
mutex_lock(&state->lock);
- tda1997x_detect_std(state, timings);
+ ret = tda1997x_detect_std(state, timings);
mutex_unlock(&state->lock);
- return 0;
+ return ret;
}
static const struct v4l2_subdev_video_ops tda1997x_video_ops = {
/* get initial HDMI status */
state->hdmi_status = io_read(sd, REG_HDMI_FLAGS);
+ io_write(sd, REG_EDID_ENABLE, EDID_ENABLE_A_EN | EDID_ENABLE_B_EN);
return 0;
}
/*
* Enable the YCbCr and clock outputs. In discrete sync mode
- * (non-BT.656) additionally enable the the sync outputs.
+ * (non-BT.656) additionally enable the sync outputs.
*/
switch (decoder->mbus_type) {
case V4L2_MBUS_PARALLEL:
* sysfs
*/
-static ssize_t show_model(struct device *cd,
+static ssize_t model_show(struct device *cd,
struct device_attribute *attr, char *buf)
{
struct media_devnode *devnode = to_media_devnode(cd);
return sprintf(buf, "%.*s\n", (int)sizeof(mdev->model), mdev->model);
}
-static DEVICE_ATTR(model, S_IRUGO, show_model, NULL);
+static DEVICE_ATTR_RO(model);
/* -----------------------------------------------------------------------------
* Registration/unregistration
#define IVTV_PCI_ID_SONY 0x104d
/* hardware flags, no gaps allowed */
-#define IVTV_HW_CX25840 (1 << 0)
-#define IVTV_HW_SAA7115 (1 << 1)
-#define IVTV_HW_SAA7127 (1 << 2)
-#define IVTV_HW_MSP34XX (1 << 3)
-#define IVTV_HW_TUNER (1 << 4)
-#define IVTV_HW_WM8775 (1 << 5)
-#define IVTV_HW_CS53L32A (1 << 6)
-#define IVTV_HW_TVEEPROM (1 << 7)
-#define IVTV_HW_SAA7114 (1 << 8)
-#define IVTV_HW_UPD64031A (1 << 9)
-#define IVTV_HW_UPD6408X (1 << 10)
-#define IVTV_HW_SAA717X (1 << 11)
-#define IVTV_HW_WM8739 (1 << 12)
-#define IVTV_HW_VP27SMPX (1 << 13)
-#define IVTV_HW_M52790 (1 << 14)
-#define IVTV_HW_GPIO (1 << 15)
-#define IVTV_HW_I2C_IR_RX_AVER (1 << 16)
-#define IVTV_HW_I2C_IR_RX_HAUP_EXT (1 << 17) /* External before internal */
-#define IVTV_HW_I2C_IR_RX_HAUP_INT (1 << 18)
-#define IVTV_HW_Z8F0811_IR_HAUP (1 << 19)
-#define IVTV_HW_I2C_IR_RX_ADAPTEC (1 << 20)
+enum ivtv_hw_bits {
+ IVTV_HW_BIT_CX25840,
+ IVTV_HW_BIT_SAA7115,
+ IVTV_HW_BIT_SAA7127,
+ IVTV_HW_BIT_MSP34XX,
+ IVTV_HW_BIT_TUNER,
+ IVTV_HW_BIT_WM8775,
+ IVTV_HW_BIT_CS53L32A,
+ IVTV_HW_BIT_TVEEPROM,
+ IVTV_HW_BIT_SAA7114,
+ IVTV_HW_BIT_UPD64031A,
+ IVTV_HW_BIT_UPD6408X,
+ IVTV_HW_BIT_SAA717X,
+ IVTV_HW_BIT_WM8739,
+ IVTV_HW_BIT_VP27SMPX,
+ IVTV_HW_BIT_M52790,
+ IVTV_HW_BIT_GPIO,
+ IVTV_HW_BIT_I2C_IR_RX_AVER,
+ IVTV_HW_BIT_I2C_IR_RX_HAUP_EXT, /* External before internal */
+ IVTV_HW_BIT_I2C_IR_RX_HAUP_INT,
+ IVTV_HW_BIT_Z8F0811_IR_HAUP,
+ IVTV_HW_BIT_I2C_IR_RX_ADAPTEC,
+
+ IVTV_HW_MAX_BITS /* Should be the last one */
+};
+
+#define IVTV_HW_CX25840 BIT(IVTV_HW_BIT_CX25840)
+#define IVTV_HW_SAA7115 BIT(IVTV_HW_BIT_SAA7115)
+#define IVTV_HW_SAA7127 BIT(IVTV_HW_BIT_SAA7127)
+#define IVTV_HW_MSP34XX BIT(IVTV_HW_BIT_MSP34XX)
+#define IVTV_HW_TUNER BIT(IVTV_HW_BIT_TUNER)
+#define IVTV_HW_WM8775 BIT(IVTV_HW_BIT_WM8775)
+#define IVTV_HW_CS53L32A BIT(IVTV_HW_BIT_CS53L32A)
+#define IVTV_HW_TVEEPROM BIT(IVTV_HW_BIT_TVEEPROM)
+#define IVTV_HW_SAA7114 BIT(IVTV_HW_BIT_SAA7114)
+#define IVTV_HW_UPD64031A BIT(IVTV_HW_BIT_UPD64031A)
+#define IVTV_HW_UPD6408X BIT(IVTV_HW_BIT_UPD6408X)
+#define IVTV_HW_SAA717X BIT(IVTV_HW_BIT_SAA717X)
+#define IVTV_HW_WM8739 BIT(IVTV_HW_BIT_WM8739)
+#define IVTV_HW_VP27SMPX BIT(IVTV_HW_BIT_VP27SMPX)
+#define IVTV_HW_M52790 BIT(IVTV_HW_BIT_M52790)
+#define IVTV_HW_GPIO BIT(IVTV_HW_BIT_GPIO)
+#define IVTV_HW_I2C_IR_RX_AVER BIT(IVTV_HW_BIT_I2C_IR_RX_AVER)
+#define IVTV_HW_I2C_IR_RX_HAUP_EXT BIT(IVTV_HW_BIT_I2C_IR_RX_HAUP_EXT)
+#define IVTV_HW_I2C_IR_RX_HAUP_INT BIT(IVTV_HW_BIT_I2C_IR_RX_HAUP_INT)
+#define IVTV_HW_Z8F0811_IR_HAUP BIT(IVTV_HW_BIT_Z8F0811_IR_HAUP)
+#define IVTV_HW_I2C_IR_RX_ADAPTEC BIT(IVTV_HW_BIT_I2C_IR_RX_ADAPTEC)
#define IVTV_HW_SAA711X (IVTV_HW_SAA7115 | IVTV_HW_SAA7114)
#define IVTV_ADAPTEC_IR_ADDR 0x6b
/* This array should match the IVTV_HW_ defines */
-static const u8 hw_addrs[] = {
+static const u8 hw_addrs[IVTV_HW_MAX_BITS] = {
IVTV_CX25840_I2C_ADDR,
IVTV_SAA7115_I2C_ADDR,
IVTV_SAA7127_I2C_ADDR,
};
/* This array should match the IVTV_HW_ defines */
-static const char * const hw_devicenames[] = {
+static const char * const hw_devicenames[IVTV_HW_MAX_BITS] = {
"cx25840",
"saa7115",
"saa7127_auto", /* saa7127 or saa7129 */
int ivtv_i2c_register(struct ivtv *itv, unsigned idx)
{
- struct v4l2_subdev *sd;
struct i2c_adapter *adap = &itv->i2c_adap;
- const char *type = hw_devicenames[idx];
- u32 hw = 1 << idx;
+ struct v4l2_subdev *sd;
+ const char *type;
+ u32 hw;
+
+ if (idx >= IVTV_HW_MAX_BITS)
+ return -ENODEV;
+
+ type = hw_devicenames[idx];
+ hw = 1 << idx;
if (hw == IVTV_HW_TUNER) {
/* special tuner handling */
static int saa7134_alsa_init(void)
{
struct saa7134_dev *dev = NULL;
- struct list_head *list;
saa7134_dmasound_init = alsa_device_init;
saa7134_dmasound_exit = alsa_device_exit;
pr_info("saa7134 ALSA driver for DMA sound loaded\n");
- list_for_each(list,&saa7134_devlist) {
- dev = list_entry(list, struct saa7134_dev, devlist);
+ list_for_each_entry(dev, &saa7134_devlist, devlist) {
if (dev->pci->device == PCI_DEVICE_ID_PHILIPS_SAA7130)
pr_info("%s/alsa: %s doesn't support digital audio\n",
dev->name, saa7134_boards[dev->board].name);
__le32 *cpu;
dma_addr_t dma_addr = 0;
- cpu = pci_alloc_consistent(pci, SAA7134_PGTABLE_SIZE, &dma_addr);
+ cpu = dma_alloc_coherent(&pci->dev, SAA7134_PGTABLE_SIZE, &dma_addr,
+ GFP_KERNEL);
if (NULL == cpu)
return -ENOMEM;
pt->size = SAA7134_PGTABLE_SIZE;
{
if (NULL == pt->cpu)
return;
- pci_free_consistent(pci, pt->size, pt->cpu, pt->dma);
+ dma_free_coherent(&pci->dev, pt->size, pt->cpu, pt->dma);
pt->cpu = NULL;
}
dev->pci_lat,
(unsigned long long)pci_resource_start(pci_dev, 0));
pci_set_master(pci_dev);
- err = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32));
+ err = dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(32));
if (err) {
pr_warn("%s: Oops: no 32bit PCI DMA ???\n", dev->name);
goto err_v4l2_unregister;
/* See of other commands are on the bus */
if (saa7164_cmd_dequeue(dev) != SAA_OK)
printk(KERN_ERR "dequeue(3) failed\n");
-
- continue;
-
} /* (loop) */
/* Release the sequence number allocation */
#define TW5864_SYNC 0x8008
/* Define controls in register TW5864_SYNC */
/*
- * 0 vlc stream to syncrous port
+ * 0 vlc stream to synchronous port
* 1 vlc stream to ddr buffers
*/
#define TW5864_SYNC_CFG BIT(7)
struct regmap *regmap = isc->regmap;
u32 rlp_mode = isc->config.rlp_cfg_mode;
+ /*
+ * In sama5d2, the YUV planar modes and the YUYV modes are treated
+ * in the same way in RLP register.
+ * Normally, YYCC mode should be Luma(n) - Color B(n) - Color R (n)
+ * and YCYC should be Luma(n + 1) - Color B (n) - Luma (n) - Color R (n)
+ * but in sama5d2, the YCYC mode does not exist, and YYCC must be
+ * selected for both planar and interleaved modes, as in fact
+ * both modes are supported.
+ *
+ * Thus, if the YCYC mode is selected, replace it with the
+ * sama5d2-compliant mode which is YYCC .
+ */
+ if ((rlp_mode & ISC_RLP_CFG_MODE_YCYC) == ISC_RLP_CFG_MODE_YCYC) {
+ rlp_mode &= ~ISC_RLP_CFG_MODE_MASK;
+ rlp_mode |= ISC_RLP_CFG_MODE_YYCC;
+ }
+
regmap_update_bits(regmap, ISC_RLP_CFG + isc->offsets.rlp,
ISC_RLP_CFG_MODE_MASK, rlp_mode);
}
u32 src_fourcc, dst_fourcc;
int ret;
+ q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ src_fourcc = q_data_src->fourcc;
+ dst_fourcc = q_data_dst->fourcc;
+
if (!ctx->initialized) {
ret = __coda_decoder_seq_init(ctx);
if (ret < 0)
return ret;
+ } else {
+ ctx->frame_mem_ctrl &= ~(CODA_FRAME_CHROMA_INTERLEAVE | (0x3 << 9) |
+ CODA9_FRAME_TILED2LINEAR);
+ if (dst_fourcc == V4L2_PIX_FMT_NV12 || dst_fourcc == V4L2_PIX_FMT_YUYV)
+ ctx->frame_mem_ctrl |= CODA_FRAME_CHROMA_INTERLEAVE;
+ if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP)
+ ctx->frame_mem_ctrl |= (0x3 << 9) |
+ ((ctx->use_vdoa) ? 0 : CODA9_FRAME_TILED2LINEAR);
}
- q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
- q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
- src_fourcc = q_data_src->fourcc;
- dst_fourcc = q_data_dst->fourcc;
-
coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR);
ret = coda_alloc_framebuffers(ctx, q_data_dst, src_fourcc);
if (!ccdc_cfg) {
/*
* TODO. Will this ever happen? if so, we need to fix it.
- * Proabably we need to add the request to a linked list and
+ * Probably we need to add the request to a linked list and
* walk through it during vpfe probe
*/
printk(KERN_ERR "vpfe capture not initialized\n");
.link_notify = fimc_md_link_notify,
};
-static ssize_t fimc_md_sysfs_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t subdev_conf_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct fimc_md *fmd = dev_get_drvdata(dev);
return strscpy(buf, "V4L2 video node only API (vid-dev)\n", PAGE_SIZE);
}
-static ssize_t fimc_md_sysfs_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t subdev_conf_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct fimc_md *fmd = dev_get_drvdata(dev);
bool subdev_api;
* sub-dev - for media controller API, subdevs must be configured in user
* space before starting streaming.
*/
-static DEVICE_ATTR(subdev_conf_mode, S_IWUSR | S_IRUGO,
- fimc_md_sysfs_show, fimc_md_sysfs_store);
+static DEVICE_ATTR_RW(subdev_conf_mode);
static int cam_clk_prepare(struct clk_hw *hw)
{
* Scatter/gather mode requires stopping the controller between
* frames so we can put in a new DMA descriptor array. If no new
* buffer exists at frame completion, the controller is left stopped;
- * this function is charged with gettig things going again.
+ * this function is charged with getting things going again.
*/
static void mcam_sg_restart(struct mcam_camera *cam)
{
mutex_lock(&isp->media_dev.graph_mutex);
ret = media_entity_enum_init(&isp->crashed, &isp->media_dev);
- if (ret)
+ if (ret) {
+ mutex_unlock(&isp->media_dev.graph_mutex);
return ret;
+ }
list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
if (sd->notifier != &isp->notifier)
u32 h264_loop_filter_mode;
s32 h264_loop_filter_alpha;
s32 h264_loop_filter_beta;
+ u32 h264_8x8_transform;
u32 hevc_i_qp;
u32 hevc_p_qp;
u32 header_mode;
bool aud_enable;
+ u32 intra_refresh_period;
struct {
u32 h264;
if (!IS_V6(inst->core))
return 0;
+ if (inst->opb_fmt == HFI_COLOR_FORMAT_NV12_UBWC)
+ return 0;
+
pconstraint.buffer_type = HFI_BUFFER_OUTPUT2;
pconstraint.num_planes = 2;
pconstraint.plane_format[0].stride_multiples = 128;
break;
}
+ case HFI_PROPERTY_PARAM_VENC_H264_TRANSFORM_8X8: {
+ struct hfi_h264_8x8_transform *in = pdata, *tm = prop_data;
+
+ tm->enable_type = in->enable_type;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*tm);
+ break;
+ }
+
case HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE:
case HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER:
case HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE:
#define HFI_PROPERTY_PARAM_VENC_MAX_NUM_B_FRAMES 0x2005020
#define HFI_PROPERTY_PARAM_VENC_H264_VUI_BITSTREAM_RESTRC 0x2005021
#define HFI_PROPERTY_PARAM_VENC_PRESERVE_TEXT_QUALITY 0x2005023
+#define HFI_PROPERTY_PARAM_VENC_H264_TRANSFORM_8X8 0x2005025
#define HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER 0x2005026
#define HFI_PROPERTY_PARAM_VENC_DISABLE_RC_TIMESTAMP 0x2005027
#define HFI_PROPERTY_PARAM_VENC_INITIAL_QP 0x2005028
u32 layer_id;
};
+struct hfi_h264_8x8_transform {
+ u32 enable_type;
+};
+
#define HFI_CAPABILITY_FRAME_WIDTH 0x01
#define HFI_CAPABILITY_FRAME_HEIGHT 0x02
#define HFI_CAPABILITY_MBS_PER_FRAME 0x03
smem_tbl_ptr = qcom_smem_get(QCOM_SMEM_HOST_ANY,
SMEM_IMG_VER_TBL, &smem_blk_sz);
- if (smem_tbl_ptr && smem_blk_sz >= SMEM_IMG_OFFSET_VENUS + VER_STR_SZ)
+ if (!IS_ERR(smem_tbl_ptr) && smem_blk_sz >= SMEM_IMG_OFFSET_VENUS + VER_STR_SZ)
memcpy(smem_tbl_ptr + SMEM_IMG_OFFSET_VENUS,
img_ver, VER_STR_SZ);
}
else
return NULL;
fmt = find_format(inst, pixmp->pixelformat, f->type);
+ if (!fmt)
+ return NULL;
}
pixmp->width = clamp(pixmp->width, frame_width_min(inst),
struct hfi_quantization_range quant_range;
struct hfi_enable en;
struct hfi_ltr_mode ltr_mode;
+ struct hfi_intra_refresh intra_refresh = {};
u32 ptype, rate_control, bitrate;
u32 profile, level;
int ret;
struct hfi_h264_vui_timing_info info;
struct hfi_h264_entropy_control entropy;
struct hfi_h264_db_control deblock;
+ struct hfi_h264_8x8_transform h264_transform;
ptype = HFI_PROPERTY_PARAM_VENC_H264_VUI_TIMING_INFO;
info.enable = 1;
ret = hfi_session_set_property(inst, ptype, &deblock);
if (ret)
return ret;
+
+ ptype = HFI_PROPERTY_PARAM_VENC_H264_TRANSFORM_8X8;
+ h264_transform.enable_type = 0;
+ if (ctr->profile.h264 == HFI_H264_PROFILE_HIGH ||
+ ctr->profile.h264 == HFI_H264_PROFILE_CONSTRAINED_HIGH)
+ h264_transform.enable_type = ctr->h264_8x8_transform;
+
+ ret = hfi_session_set_property(inst, ptype, &h264_transform);
+ if (ret)
+ return ret;
+
}
if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_H264 ||
en.enable = 1;
ret = hfi_session_set_property(inst, ptype, &en);
+ }
+
+ if ((inst->fmt_cap->pixfmt == V4L2_PIX_FMT_H264 ||
+ inst->fmt_cap->pixfmt == V4L2_PIX_FMT_HEVC) &&
+ (rate_control == HFI_RATE_CONTROL_CBR_VFR ||
+ rate_control == HFI_RATE_CONTROL_CBR_CFR)) {
+ intra_refresh.mode = HFI_INTRA_REFRESH_NONE;
+ intra_refresh.cir_mbs = 0;
+
+ if (ctr->intra_refresh_period) {
+ u32 mbs;
+
+ mbs = ALIGN(inst->width, 16) * ALIGN(inst->height, 16);
+ mbs /= 16 * 16;
+ if (mbs % ctr->intra_refresh_period)
+ mbs++;
+ mbs /= ctr->intra_refresh_period;
+
+ intra_refresh.mode = HFI_INTRA_REFRESH_RANDOM;
+ intra_refresh.cir_mbs = mbs;
+ }
+
+ ptype = HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH;
+
+ ret = hfi_session_set_property(inst, ptype, &intra_refresh);
if (ret)
return ret;
}
#define SLICE_BYTE_SIZE_MAX 1024
#define SLICE_BYTE_SIZE_MIN 1024
#define SLICE_MB_SIZE_MAX 300
-#define INTRA_REFRESH_MBS_MAX 300
#define AT_SLICE_BOUNDARY \
V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY
#define MAX_LTR_FRAME_COUNT 4
}
mutex_unlock(&inst->lock);
break;
- case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB:
- break;
case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
ret = venc_calc_bpframes(ctrl->val, ctr->num_b_frames, &bframes,
&ctr->num_p_frames);
case V4L2_CID_COLORIMETRY_HDR10_MASTERING_DISPLAY:
ctr->mastering = *ctrl->p_new.p_hdr10_mastering;
break;
+ case V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD:
+ ctr->intra_refresh_period = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM:
+ if (ctr->profile.h264 != HFI_H264_PROFILE_HIGH &&
+ ctr->profile.h264 != HFI_H264_PROFILE_CONSTRAINED_HIGH)
+ return -EINVAL;
+
+ /*
+ * In video firmware, 8x8 transform is supported only for
+ * high profile(HP) and constrained high profile(CHP).
+ * If client wants to disable 8x8 transform for HP/CHP,
+ * it is better to set profile as main profile(MP).
+ * Because there is no difference between HP and MP
+ * if we disable 8x8 transform for HP.
+ */
+
+ if (ctrl->val == 0)
+ return -EINVAL;
+
+ ctr->h264_8x8_transform = ctrl->val;
+ break;
default:
return -EINVAL;
}
{
int ret;
- ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 57);
+ ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 58);
if (ret)
return ret;
V4L2_CID_MPEG_VIDEO_H264_I_FRAME_MIN_QP, 1, 51, 1, 1);
v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM, 0, 1, 1, 0);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
V4L2_CID_MPEG_VIDEO_H264_P_FRAME_MIN_QP, 1, 51, 1, 1);
v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA, -6, 6, 1, 0);
v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
- V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB,
- 0, INTRA_REFRESH_MBS_MAX, 1, 0);
-
- v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
V4L2_CID_MPEG_VIDEO_GOP_SIZE, 0, (1 << 16) - 1, 1, 30);
v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
V4L2_CID_COLORIMETRY_HDR10_MASTERING_DISPLAY,
v4l2_ctrl_ptr_create(NULL));
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD, 0,
+ ((4096 * 2304) >> 8), 1, 0);
+
ret = inst->ctrl_handler.error;
if (ret)
goto err;
int ret;
sd_state = v4l2_subdev_alloc_state(sd);
- if (sd_state == NULL)
- return -ENOMEM;
+ if (IS_ERR(sd_state))
+ return PTR_ERR(sd_state);
if (!rvin_format_from_pixel(vin, pix->pixelformat))
pix->pixelformat = RVIN_DEFAULT_FORMAT;
if (IS_ERR(rga->m2m_dev)) {
v4l2_err(&rga->v4l2_dev, "Failed to init mem2mem device\n");
ret = PTR_ERR(rga->m2m_dev);
- goto unreg_video_dev;
+ goto rel_vdev;
}
ret = pm_runtime_resume_and_get(rga->dev);
if (ret < 0)
- goto unreg_video_dev;
+ goto rel_vdev;
rga->version.major = (rga_read(rga, RGA_VERSION_INFO) >> 24) & 0xFF;
rga->version.minor = (rga_read(rga, RGA_VERSION_INFO) >> 20) & 0x0F;
rga->cmdbuf_virt = dma_alloc_attrs(rga->dev, RGA_CMDBUF_SIZE,
&rga->cmdbuf_phy, GFP_KERNEL,
DMA_ATTR_WRITE_COMBINE);
+ if (!rga->cmdbuf_virt) {
+ ret = -ENOMEM;
+ goto rel_vdev;
+ }
rga->src_mmu_pages =
(unsigned int *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 3);
+ if (!rga->src_mmu_pages) {
+ ret = -ENOMEM;
+ goto free_dma;
+ }
rga->dst_mmu_pages =
(unsigned int *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 3);
+ if (rga->dst_mmu_pages) {
+ ret = -ENOMEM;
+ goto free_src_pages;
+ }
def_frame.stride = (def_frame.width * def_frame.fmt->depth) >> 3;
def_frame.size = def_frame.stride * def_frame.height;
ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1);
if (ret) {
v4l2_err(&rga->v4l2_dev, "Failed to register video device\n");
- goto rel_vdev;
+ goto free_dst_pages;
}
v4l2_info(&rga->v4l2_dev, "Registered %s as /dev/%s\n",
return 0;
+free_dst_pages:
+ free_pages((unsigned long)rga->dst_mmu_pages, 3);
+free_src_pages:
+ free_pages((unsigned long)rga->src_mmu_pages, 3);
+free_dma:
+ dma_free_attrs(rga->dev, RGA_CMDBUF_SIZE, rga->cmdbuf_virt,
+ rga->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE);
rel_vdev:
video_device_release(vfd);
-unreg_video_dev:
- video_unregister_device(rga->vfd);
unreg_v4l2_dev:
v4l2_device_unregister(&rga->v4l2_dev);
err_put_clk:
return 0;
}
-static void rkisp1_vb2_buf_queue(struct vb2_buffer *vb)
+static int rkisp1_vb2_buf_init(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct rkisp1_buffer *ispbuf =
if (cap->pix.info->comp_planes == 3 && cap->pix.cfg->uv_swap)
swap(ispbuf->buff_addr[RKISP1_PLANE_CR],
ispbuf->buff_addr[RKISP1_PLANE_CB]);
+ return 0;
+}
+
+static void rkisp1_vb2_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct rkisp1_buffer *ispbuf =
+ container_of(vbuf, struct rkisp1_buffer, vb);
+ struct rkisp1_capture *cap = vb->vb2_queue->drv_priv;
spin_lock_irq(&cap->buf.lock);
list_add_tail(&ispbuf->queue, &cap->buf.queue);
static const struct vb2_ops rkisp1_vb2_ops = {
.queue_setup = rkisp1_vb2_queue_setup,
+ .buf_init = rkisp1_vb2_buf_init,
.buf_queue = rkisp1_vb2_buf_queue,
.buf_prepare = rkisp1_vb2_buf_prepare,
.wait_prepare = vb2_ops_wait_prepare,
* @vb: vb2 buffer
* @queue: entry of the buffer in the queue
* @buff_addr: dma addresses of each plane, used only by the capture devices: selfpath, mainpath
- * @vaddr: virtual address for buffers used by params and stats devices
*/
struct rkisp1_buffer {
struct vb2_v4l2_buffer vb;
struct list_head queue;
- union {
- u32 buff_addr[VIDEO_MAX_PLANES];
- void *vaddr;
- };
+ u32 buff_addr[VIDEO_MAX_PLANES];
};
/*
cur_buf = list_first_entry(¶ms->params,
struct rkisp1_buffer, queue);
- new_params = (struct rkisp1_params_cfg *)(cur_buf->vaddr);
+ new_params = (struct rkisp1_params_cfg *)vb2_plane_vaddr(&cur_buf->vb.vb2_buf, 0);
rkisp1_isp_isr_other_config(params, new_params);
rkisp1_isp_isr_meas_config(params, new_params);
struct vb2_queue *vq = vb->vb2_queue;
struct rkisp1_params *params = vq->drv_priv;
- params_buf->vaddr = vb2_plane_vaddr(vb, 0);
spin_lock_irq(¶ms->config_lock);
list_add_tail(¶ms_buf->queue, ¶ms->params);
spin_unlock_irq(¶ms->config_lock);
struct vb2_queue *vq = vb->vb2_queue;
struct rkisp1_stats *stats_dev = vq->drv_priv;
- stats_buf->vaddr = vb2_plane_vaddr(vb, 0);
spin_lock_irq(&stats_dev->lock);
list_add_tail(&stats_buf->queue, &stats_dev->stat);
if (!cur_buf)
return;
- cur_stat_buf =
- (struct rkisp1_stat_buffer *)(cur_buf->vaddr);
-
+ cur_stat_buf = (struct rkisp1_stat_buffer *)
+ vb2_plane_vaddr(&cur_buf->vb.vb2_buf, 0);
if (isp_ris & RKISP1_CIF_ISP_AWB_DONE)
rkisp1_stats_get_awb_meas(stats, cur_stat_buf);
if (test_and_clear_bit(0, &dev->hw_lock) == 0)
mfc_err("Failed to unlock hardware\n");
- /* This is in deed imporant, as no operation has been
+ /* This is indeed important, as no operation has been
* scheduled, reduce the clock count as no one will
* ever do this, because no interrupt related to this try_run
* will ever come from hardware. */
msg.ipc_buf_size = ipc_buf_size;
msg.ipc_buf_paddr = ctx->ipc_buf->paddr;
- memcpy(msg.name, name, sizeof(msg.name));
- msg.name[sizeof(msg.name) - 1] = 0;
+ strscpy(msg.name, name, sizeof(msg.name));
msg.param_size = param->size;
memcpy(ctx->ipc_buf->vaddr, param->data, msg.param_size);
* ------------------------------------------------------------------
*/
-static s64 cal_camerarx_get_external_rate(struct cal_camerarx *phy)
+static s64 cal_camerarx_get_ext_link_freq(struct cal_camerarx *phy)
{
- struct v4l2_ctrl *ctrl;
- s64 rate;
-
- ctrl = v4l2_ctrl_find(phy->sensor->ctrl_handler, V4L2_CID_PIXEL_RATE);
- if (!ctrl) {
- phy_err(phy, "no pixel rate control in subdev: %s\n",
- phy->sensor->name);
- return -EPIPE;
+ struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = &phy->endpoint.bus.mipi_csi2;
+ u32 num_lanes = mipi_csi2->num_data_lanes;
+ const struct cal_format_info *fmtinfo;
+ u32 bpp;
+ s64 freq;
+
+ fmtinfo = cal_format_by_code(phy->formats[CAL_CAMERARX_PAD_SINK].code);
+ if (!fmtinfo)
+ return -EINVAL;
+
+ bpp = fmtinfo->bpp;
+
+ freq = v4l2_get_link_freq(phy->source->ctrl_handler, bpp, 2 * num_lanes);
+ if (freq < 0) {
+ phy_err(phy, "failed to get link freq for subdev '%s'\n",
+ phy->source->name);
+ return freq;
}
- rate = v4l2_ctrl_g_ctrl_int64(ctrl);
- phy_dbg(3, phy, "sensor Pixel Rate: %llu\n", rate);
+ phy_dbg(3, phy, "Source Link Freq: %llu\n", freq);
- return rate;
+ return freq;
}
static void cal_camerarx_lane_config(struct cal_camerarx *phy)
#define TCLK_MISS 1
#define TCLK_SETTLE 14
-static void cal_camerarx_config(struct cal_camerarx *phy, s64 external_rate)
+static void cal_camerarx_config(struct cal_camerarx *phy, s64 link_freq)
{
unsigned int reg0, reg1;
unsigned int ths_term, ths_settle;
- unsigned int csi2_ddrclk_khz;
- struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 =
- &phy->endpoint.bus.mipi_csi2;
- u32 num_lanes = mipi_csi2->num_data_lanes;
/* DPHY timing configuration */
- /*
- * CSI-2 is DDR and we only count used lanes.
- *
- * csi2_ddrclk_khz = external_rate / 1000
- * / (2 * num_lanes) * phy->fmtinfo->bpp;
- */
- csi2_ddrclk_khz = div_s64(external_rate * phy->fmtinfo->bpp,
- 2 * num_lanes * 1000);
-
- phy_dbg(1, phy, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz);
-
/* THS_TERM: Programmed value = floor(20 ns/DDRClk period) */
- ths_term = 20 * csi2_ddrclk_khz / 1000000;
+ ths_term = div_s64(20 * link_freq, 1000 * 1000 * 1000);
phy_dbg(1, phy, "ths_term: %d (0x%02x)\n", ths_term, ths_term);
/* THS_SETTLE: Programmed value = floor(105 ns/DDRClk period) + 4 */
- ths_settle = (105 * csi2_ddrclk_khz / 1000000) + 4;
+ ths_settle = div_s64(105 * link_freq, 1000 * 1000 * 1000) + 4;
phy_dbg(1, phy, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle);
reg0 = camerarx_read(phy, CAL_CSI2_PHY_REG0);
CAL_CSI2_COMPLEXIO_IRQ_FIFO_OVR_MASK |
CAL_CSI2_COMPLEXIO_IRQ_SHORT_PACKET_MASK |
CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK;
-
- /* Enable CIO error IRQs. */
+ const u32 vc_err_mask =
+ CAL_CSI2_VC_IRQ_CS_IRQ_MASK(0) |
+ CAL_CSI2_VC_IRQ_CS_IRQ_MASK(1) |
+ CAL_CSI2_VC_IRQ_CS_IRQ_MASK(2) |
+ CAL_CSI2_VC_IRQ_CS_IRQ_MASK(3) |
+ CAL_CSI2_VC_IRQ_ECC_CORRECTION_IRQ_MASK(0) |
+ CAL_CSI2_VC_IRQ_ECC_CORRECTION_IRQ_MASK(1) |
+ CAL_CSI2_VC_IRQ_ECC_CORRECTION_IRQ_MASK(2) |
+ CAL_CSI2_VC_IRQ_ECC_CORRECTION_IRQ_MASK(3);
+
+ /* Enable CIO & VC error IRQs. */
cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0),
- CAL_HL_IRQ_CIO_MASK(phy->instance));
+ CAL_HL_IRQ_CIO_MASK(phy->instance) |
+ CAL_HL_IRQ_VC_MASK(phy->instance));
cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance),
cio_err_mask);
+ cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(phy->instance),
+ vc_err_mask);
}
static void cal_camerarx_disable_irqs(struct cal_camerarx *phy)
{
/* Disable CIO error irqs */
cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(0),
- CAL_HL_IRQ_CIO_MASK(phy->instance));
+ CAL_HL_IRQ_CIO_MASK(phy->instance) |
+ CAL_HL_IRQ_VC_MASK(phy->instance));
cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), 0);
+ cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(phy->instance), 0);
}
static void cal_camerarx_ppi_enable(struct cal_camerarx *phy)
{
cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance),
+ 1, CAL_CSI2_PPI_CTRL_ECC_EN_MASK);
+
+ cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance),
1, CAL_CSI2_PPI_CTRL_IF_EN_MASK);
}
static int cal_camerarx_start(struct cal_camerarx *phy)
{
- s64 external_rate;
+ s64 link_freq;
u32 sscounter;
u32 val;
int ret;
- external_rate = cal_camerarx_get_external_rate(phy);
- if (external_rate < 0)
- return external_rate;
+ if (phy->enable_count > 0) {
+ phy->enable_count++;
+ return 0;
+ }
+
+ link_freq = cal_camerarx_get_ext_link_freq(phy);
+ if (link_freq < 0)
+ return link_freq;
- ret = v4l2_subdev_call(phy->sensor, core, s_power, 1);
+ ret = v4l2_subdev_call(phy->source, core, s_power, 1);
if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) {
phy_err(phy, "power on failed in subdev\n");
return ret;
* 2. CSI PHY and link initialization sequence.
*
* a. Deassert the CSI-2 PHY reset. Do not wait for reset completion
- * at this point, as it requires the external sensor to send the
+ * at this point, as it requires the external source to send the
* CSI-2 HS clock.
*/
cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance),
camerarx_read(phy, CAL_CSI2_PHY_REG0);
/* Program the PHY timing parameters. */
- cal_camerarx_config(phy, external_rate);
+ cal_camerarx_config(phy, link_freq);
/*
* b. Assert the FORCERXMODE signal.
cal_camerarx_power(phy, true);
/*
- * Start the sensor to enable the CSI-2 HS clock. We can now wait for
+ * Start the source to enable the CSI-2 HS clock. We can now wait for
* CSI-2 PHY reset to complete.
*/
- ret = v4l2_subdev_call(phy->sensor, video, s_stream, 1);
+ ret = v4l2_subdev_call(phy->source, video, s_stream, 1);
if (ret) {
- v4l2_subdev_call(phy->sensor, core, s_power, 0);
+ v4l2_subdev_call(phy->source, core, s_power, 0);
cal_camerarx_disable_irqs(phy);
phy_err(phy, "stream on failed in subdev\n");
return ret;
/* Finally, enable the PHY Protocol Interface (PPI). */
cal_camerarx_ppi_enable(phy);
+ phy->enable_count++;
+
return 0;
}
static void cal_camerarx_stop(struct cal_camerarx *phy)
{
- unsigned int i;
int ret;
+ if (--phy->enable_count > 0)
+ return;
+
cal_camerarx_ppi_disable(phy);
cal_camerarx_disable_irqs(phy);
CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL,
CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK);
- /* Wait for power down completion */
- for (i = 0; i < 10; i++) {
- if (cal_read_field(phy->cal,
- CAL_CSI2_COMPLEXIO_CFG(phy->instance),
- CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) ==
- CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETONGOING)
- break;
- usleep_range(1000, 1100);
- }
- phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Complex IO in Reset (%d) %s\n",
+ phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Complex IO in Reset\n",
phy->instance,
- cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)), i,
- (i >= 10) ? "(timeout)" : "");
+ cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)));
/* Disable the phy */
cal_camerarx_disable(phy);
- if (v4l2_subdev_call(phy->sensor, video, s_stream, 0))
+ if (v4l2_subdev_call(phy->source, video, s_stream, 0))
phy_err(phy, "stream off failed in subdev\n");
- ret = v4l2_subdev_call(phy->sensor, core, s_power, 0);
+ ret = v4l2_subdev_call(phy->source, core, s_power, 0);
if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
phy_err(phy, "power off failed in subdev\n");
}
endpoint->bus.mipi_csi2.flags);
/* Retrieve the connected device and store it for later use. */
- phy->sensor_ep_node = of_graph_get_remote_endpoint(ep_node);
- phy->sensor_node = of_graph_get_port_parent(phy->sensor_ep_node);
- if (!phy->sensor_node) {
+ phy->source_ep_node = of_graph_get_remote_endpoint(ep_node);
+ phy->source_node = of_graph_get_port_parent(phy->source_ep_node);
+ if (!phy->source_node) {
phy_dbg(3, phy, "Can't get remote parent\n");
- of_node_put(phy->sensor_ep_node);
+ of_node_put(phy->source_ep_node);
ret = -EINVAL;
goto done;
}
- phy_dbg(1, phy, "Found connected device %pOFn\n", phy->sensor_node);
+ phy_dbg(1, phy, "Found connected device %pOFn\n", phy->source_node);
done:
of_node_put(ep_node);
static int cal_camerarx_sd_s_stream(struct v4l2_subdev *sd, int enable)
{
struct cal_camerarx *phy = to_cal_camerarx(sd);
+ int ret = 0;
+
+ mutex_lock(&phy->mutex);
if (enable)
- return cal_camerarx_start(phy);
+ ret = cal_camerarx_start(phy);
+ else
+ cal_camerarx_stop(phy);
- cal_camerarx_stop(phy);
- return 0;
+ mutex_unlock(&phy->mutex);
+
+ return ret;
}
static int cal_camerarx_sd_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_mbus_code_enum *code)
{
struct cal_camerarx *phy = to_cal_camerarx(sd);
+ int ret = 0;
+
+ mutex_lock(&phy->mutex);
/* No transcoding, source and sink codes must match. */
- if (code->pad == CAL_CAMERARX_PAD_SOURCE) {
+ if (cal_rx_pad_is_source(code->pad)) {
struct v4l2_mbus_framefmt *fmt;
- if (code->index > 0)
- return -EINVAL;
+ if (code->index > 0) {
+ ret = -EINVAL;
+ goto out;
+ }
fmt = cal_camerarx_get_pad_format(phy, sd_state,
CAL_CAMERARX_PAD_SINK,
code->which);
code->code = fmt->code;
- return 0;
- }
+ } else {
+ if (code->index >= cal_num_formats) {
+ ret = -EINVAL;
+ goto out;
+ }
- if (code->index >= cal_num_formats)
- return -EINVAL;
+ code->code = cal_formats[code->index].code;
+ }
- code->code = cal_formats[code->index].code;
+out:
+ mutex_unlock(&phy->mutex);
- return 0;
+ return ret;
}
static int cal_camerarx_sd_enum_frame_size(struct v4l2_subdev *sd,
{
struct cal_camerarx *phy = to_cal_camerarx(sd);
const struct cal_format_info *fmtinfo;
+ int ret = 0;
if (fse->index > 0)
return -EINVAL;
+ mutex_lock(&phy->mutex);
+
/* No transcoding, source and sink formats must match. */
- if (fse->pad == CAL_CAMERARX_PAD_SOURCE) {
+ if (cal_rx_pad_is_source(fse->pad)) {
struct v4l2_mbus_framefmt *fmt;
fmt = cal_camerarx_get_pad_format(phy, sd_state,
CAL_CAMERARX_PAD_SINK,
fse->which);
- if (fse->code != fmt->code)
- return -EINVAL;
+ if (fse->code != fmt->code) {
+ ret = -EINVAL;
+ goto out;
+ }
fse->min_width = fmt->width;
fse->max_width = fmt->width;
fse->min_height = fmt->height;
fse->max_height = fmt->height;
+ } else {
+ fmtinfo = cal_format_by_code(fse->code);
+ if (!fmtinfo) {
+ ret = -EINVAL;
+ goto out;
+ }
- return 0;
+ fse->min_width = CAL_MIN_WIDTH_BYTES * 8 / ALIGN(fmtinfo->bpp, 8);
+ fse->max_width = CAL_MAX_WIDTH_BYTES * 8 / ALIGN(fmtinfo->bpp, 8);
+ fse->min_height = CAL_MIN_HEIGHT_LINES;
+ fse->max_height = CAL_MAX_HEIGHT_LINES;
}
- fmtinfo = cal_format_by_code(fse->code);
- if (!fmtinfo)
- return -EINVAL;
-
- fse->min_width = CAL_MIN_WIDTH_BYTES * 8 / ALIGN(fmtinfo->bpp, 8);
- fse->max_width = CAL_MAX_WIDTH_BYTES * 8 / ALIGN(fmtinfo->bpp, 8);
- fse->min_height = CAL_MIN_HEIGHT_LINES;
- fse->max_height = CAL_MAX_HEIGHT_LINES;
+out:
+ mutex_unlock(&phy->mutex);
- return 0;
+ return ret;
}
static int cal_camerarx_sd_get_fmt(struct v4l2_subdev *sd,
struct cal_camerarx *phy = to_cal_camerarx(sd);
struct v4l2_mbus_framefmt *fmt;
+ mutex_lock(&phy->mutex);
+
fmt = cal_camerarx_get_pad_format(phy, sd_state, format->pad,
format->which);
format->format = *fmt;
+ mutex_unlock(&phy->mutex);
+
return 0;
}
unsigned int bpp;
/* No transcoding, source and sink formats must match. */
- if (format->pad == CAL_CAMERARX_PAD_SOURCE)
+ if (cal_rx_pad_is_source(format->pad))
return cal_camerarx_sd_get_fmt(sd, sd_state, format);
/*
- * Default to the first format is the requested media bus code isn't
+ * Default to the first format if the requested media bus code isn't
* supported.
*/
fmtinfo = cal_format_by_code(format->format.code);
if (!fmtinfo)
fmtinfo = &cal_formats[0];
- /*
- * Clamp the size, update the code. The field and colorspace are
- * accepted as-is.
- */
+ /* Clamp the size, update the code. The colorspace is accepted as-is. */
bpp = ALIGN(fmtinfo->bpp, 8);
format->format.width = clamp_t(unsigned int, format->format.width,
CAL_MIN_HEIGHT_LINES,
CAL_MAX_HEIGHT_LINES);
format->format.code = fmtinfo->code;
+ format->format.field = V4L2_FIELD_NONE;
/* Store the format and propagate it to the source pad. */
+
+ mutex_lock(&phy->mutex);
+
fmt = cal_camerarx_get_pad_format(phy, sd_state,
CAL_CAMERARX_PAD_SINK,
format->which);
*fmt = format->format;
fmt = cal_camerarx_get_pad_format(phy, sd_state,
- CAL_CAMERARX_PAD_SOURCE,
+ CAL_CAMERARX_PAD_FIRST_SOURCE,
format->which);
*fmt = format->format;
- if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)
- phy->fmtinfo = fmtinfo;
+ mutex_unlock(&phy->mutex);
return 0;
}
struct platform_device *pdev = to_platform_device(cal->dev);
struct cal_camerarx *phy;
struct v4l2_subdev *sd;
+ unsigned int i;
int ret;
phy = kzalloc(sizeof(*phy), GFP_KERNEL);
phy->cal = cal;
phy->instance = instance;
+ mutex_init(&phy->mutex);
+
phy->res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
(instance == 0) ?
"cal_rx_core0" :
sd->dev = cal->dev;
phy->pads[CAL_CAMERARX_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
- phy->pads[CAL_CAMERARX_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+ for (i = CAL_CAMERARX_PAD_FIRST_SOURCE; i < CAL_CAMERARX_NUM_PADS; ++i)
+ phy->pads[i].flags = MEDIA_PAD_FL_SOURCE;
sd->entity.ops = &cal_camerarx_media_ops;
ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(phy->pads),
phy->pads);
if (ret)
goto error;
- cal_camerarx_sd_init_cfg(sd, NULL);
+ ret = cal_camerarx_sd_init_cfg(sd, NULL);
+ if (ret)
+ goto error;
ret = v4l2_device_register_subdev(&cal->v4l2_dev, sd);
if (ret)
v4l2_device_unregister_subdev(&phy->subdev);
media_entity_cleanup(&phy->subdev.entity);
- of_node_put(phy->sensor_ep_node);
- of_node_put(phy->sensor_node);
+ of_node_put(phy->source_ep_node);
+ of_node_put(phy->source_node);
+ mutex_destroy(&phy->mutex);
kfree(phy);
}
return NULL;
}
-static int cal_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
+static int cal_legacy_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
{
struct cal_ctx *ctx = video_drvdata(file);
const struct cal_format_info *fmtinfo;
sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
sd_fmt.pad = 0;
- ret = v4l2_subdev_call(ctx->phy->sensor, pad, get_fmt, NULL, &sd_fmt);
+ ret = v4l2_subdev_call(ctx->phy->source, pad, get_fmt, NULL, &sd_fmt);
if (ret)
return ret;
sd_fmt.pad = 0;
*mbus_fmt = *fmt;
- ret = v4l2_subdev_call(ctx->phy->sensor, pad, set_fmt, NULL, &sd_fmt);
+ ret = v4l2_subdev_call(ctx->phy->source, pad, set_fmt, NULL, &sd_fmt);
if (ret)
return ret;
f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
}
-static int cal_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
+static int cal_legacy_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
{
struct cal_ctx *ctx = video_drvdata(file);
const struct cal_format_info *fmtinfo;
fse.code = fmtinfo->code;
fse.which = V4L2_SUBDEV_FORMAT_ACTIVE;
for (fse.index = 0; ; fse.index++) {
- ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_size,
+ ret = v4l2_subdev_call(ctx->phy->source, pad, enum_frame_size,
NULL, &fse);
if (ret)
break;
return 0;
}
-static int cal_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
+static int cal_legacy_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
{
struct cal_ctx *ctx = video_drvdata(file);
struct vb2_queue *q = &ctx->vb_vidq;
return -EBUSY;
}
- ret = cal_try_fmt_vid_cap(file, priv, f);
+ ret = cal_legacy_try_fmt_vid_cap(file, priv, f);
if (ret < 0)
return ret;
return 0;
}
-static int cal_enum_framesizes(struct file *file, void *fh,
- struct v4l2_frmsizeenum *fsize)
+static int cal_legacy_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
{
struct cal_ctx *ctx = video_drvdata(file);
const struct cal_format_info *fmtinfo;
fse.code = fmtinfo->code;
fse.which = V4L2_SUBDEV_FORMAT_ACTIVE;
- ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_size, NULL,
+ ret = v4l2_subdev_call(ctx->phy->source, pad, enum_frame_size, NULL,
&fse);
if (ret)
return ret;
return 0;
}
-static int cal_enum_input(struct file *file, void *priv,
- struct v4l2_input *inp)
+static int cal_legacy_enum_input(struct file *file, void *priv,
+ struct v4l2_input *inp)
{
if (inp->index > 0)
return -EINVAL;
return 0;
}
-static int cal_g_input(struct file *file, void *priv, unsigned int *i)
+static int cal_legacy_g_input(struct file *file, void *priv, unsigned int *i)
{
*i = 0;
return 0;
}
-static int cal_s_input(struct file *file, void *priv, unsigned int i)
+static int cal_legacy_s_input(struct file *file, void *priv, unsigned int i)
{
return i > 0 ? -EINVAL : 0;
}
/* timeperframe is arbitrary and continuous */
-static int cal_enum_frameintervals(struct file *file, void *priv,
- struct v4l2_frmivalenum *fival)
+static int cal_legacy_enum_frameintervals(struct file *file, void *priv,
+ struct v4l2_frmivalenum *fival)
{
struct cal_ctx *ctx = video_drvdata(file);
const struct cal_format_info *fmtinfo;
return -EINVAL;
fie.code = fmtinfo->code;
- ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_interval,
+ ret = v4l2_subdev_call(ctx->phy->source, pad, enum_frame_interval,
NULL, &fie);
if (ret)
return ret;
return 0;
}
-static const struct v4l2_ioctl_ops cal_ioctl_video_ops = {
+static int cal_legacy_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
+{
+ struct cal_ctx *ctx = video_drvdata(file);
+
+ return v4l2_g_parm_cap(video_devdata(file), ctx->phy->source, a);
+}
+
+static int cal_legacy_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
+{
+ struct cal_ctx *ctx = video_drvdata(file);
+
+ return v4l2_s_parm_cap(video_devdata(file), ctx->phy->source, a);
+}
+
+static const struct v4l2_ioctl_ops cal_ioctl_legacy_ops = {
.vidioc_querycap = cal_querycap,
- .vidioc_enum_fmt_vid_cap = cal_enum_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_cap = cal_legacy_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = cal_g_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = cal_try_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = cal_s_fmt_vid_cap,
- .vidioc_enum_framesizes = cal_enum_framesizes,
+ .vidioc_try_fmt_vid_cap = cal_legacy_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = cal_legacy_s_fmt_vid_cap,
+ .vidioc_enum_framesizes = cal_legacy_enum_framesizes,
.vidioc_reqbufs = vb2_ioctl_reqbufs,
.vidioc_create_bufs = vb2_ioctl_create_bufs,
.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
.vidioc_qbuf = vb2_ioctl_qbuf,
.vidioc_dqbuf = vb2_ioctl_dqbuf,
.vidioc_expbuf = vb2_ioctl_expbuf,
- .vidioc_enum_input = cal_enum_input,
- .vidioc_g_input = cal_g_input,
- .vidioc_s_input = cal_s_input,
- .vidioc_enum_frameintervals = cal_enum_frameintervals,
+ .vidioc_enum_input = cal_legacy_enum_input,
+ .vidioc_g_input = cal_legacy_g_input,
+ .vidioc_s_input = cal_legacy_s_input,
+ .vidioc_enum_frameintervals = cal_legacy_enum_frameintervals,
.vidioc_streamon = vb2_ioctl_streamon,
.vidioc_streamoff = vb2_ioctl_streamoff,
.vidioc_log_status = v4l2_ctrl_log_status,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+ .vidioc_g_parm = cal_legacy_g_parm,
+ .vidioc_s_parm = cal_legacy_s_parm,
};
/* ------------------------------------------------------------------
static int cal_mc_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
+ unsigned int i;
+ unsigned int idx;
+
if (f->index >= cal_num_formats)
return -EINVAL;
- f->pixelformat = cal_formats[f->index].fourcc;
- f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ idx = 0;
- return 0;
+ for (i = 0; i < cal_num_formats; ++i) {
+ if (f->mbus_code && cal_formats[i].code != f->mbus_code)
+ continue;
+
+ if (idx == f->index) {
+ f->pixelformat = cal_formats[i].fourcc;
+ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ return 0;
+ }
+
+ idx++;
+ }
+
+ return -EINVAL;
}
static void cal_mc_try_fmt(struct cal_ctx *ctx, struct v4l2_format *f,
static int cal_video_check_format(struct cal_ctx *ctx)
{
const struct v4l2_mbus_framefmt *format;
+ struct media_pad *remote_pad;
+
+ remote_pad = media_entity_remote_pad(&ctx->pad);
+ if (!remote_pad)
+ return -ENODEV;
- format = &ctx->phy->formats[CAL_CAMERARX_PAD_SOURCE];
+ format = &ctx->phy->formats[remote_pad->index];
if (ctx->fmtinfo->code != format->code ||
ctx->v_fmt.fmt.pix.height != format->height ||
goto error_pipeline;
}
+ ret = cal_ctx_prepare(ctx);
+ if (ret) {
+ ctx_err(ctx, "Failed to prepare context: %d\n", ret);
+ goto error_pipeline;
+ }
+
spin_lock_irq(&ctx->dma.lock);
buf = list_first_entry(&ctx->dma.queue, struct cal_buffer, list);
- ctx->dma.pending = buf;
+ ctx->dma.active = buf;
list_del(&buf->list);
spin_unlock_irq(&ctx->dma.lock);
error_stop:
cal_ctx_stop(ctx);
pm_runtime_put_sync(ctx->cal->dev);
+ cal_ctx_unprepare(ctx);
error_pipeline:
media_pipeline_stop(&ctx->vdev.entity);
pm_runtime_put_sync(ctx->cal->dev);
+ cal_ctx_unprepare(ctx);
+
cal_release_buffers(ctx, VB2_BUF_STATE_ERROR);
media_pipeline_stop(&ctx->vdev.entity);
memset(&mbus_code, 0, sizeof(mbus_code));
mbus_code.index = j;
mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE;
- ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_mbus_code,
+ ret = v4l2_subdev_call(ctx->phy->source, pad, enum_mbus_code,
NULL, &mbus_code);
if (ret == -EINVAL)
break;
if (ret) {
ctx_err(ctx, "Error enumerating mbus codes in subdev %s: %d\n",
- ctx->phy->sensor->name, ret);
+ ctx->phy->source->name, ret);
return ret;
}
ctx_dbg(2, ctx,
"subdev %s: code: %04x idx: %u\n",
- ctx->phy->sensor->name, mbus_code.code, j);
+ ctx->phy->source->name, mbus_code.code, j);
for (k = 0; k < cal_num_formats; k++) {
fmtinfo = &cal_formats[k];
if (i == 0) {
ctx_err(ctx, "No suitable format reported by subdev %s\n",
- ctx->phy->sensor->name);
+ ctx->phy->source->name);
return -EINVAL;
}
return 0;
}
+static int cal_ctx_v4l2_init_mc_format(struct cal_ctx *ctx)
+{
+ const struct cal_format_info *fmtinfo;
+ struct v4l2_pix_format *pix_fmt = &ctx->v_fmt.fmt.pix;
+
+ fmtinfo = cal_format_by_code(MEDIA_BUS_FMT_UYVY8_2X8);
+ if (!fmtinfo)
+ return -EINVAL;
+
+ 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 = fmtinfo->fourcc;
+
+ ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ /* Save current format */
+ cal_calc_format_size(ctx, fmtinfo, &ctx->v_fmt);
+ ctx->fmtinfo = fmtinfo;
+
+ return 0;
+}
+
int cal_ctx_v4l2_register(struct cal_ctx *ctx)
{
struct video_device *vfd = &ctx->vdev;
int ret;
- ret = cal_ctx_v4l2_init_formats(ctx);
- if (ret)
- return ret;
-
if (!cal_mc_api) {
struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler;
- ret = v4l2_ctrl_add_handler(hdl, ctx->phy->sensor->ctrl_handler,
+ ret = cal_ctx_v4l2_init_formats(ctx);
+ if (ret) {
+ ctx_err(ctx, "Failed to init formats: %d\n", ret);
+ return ret;
+ }
+
+ ret = v4l2_ctrl_add_handler(hdl, ctx->phy->source->ctrl_handler,
NULL, true);
if (ret < 0) {
- ctx_err(ctx, "Failed to add sensor ctrl handler\n");
+ ctx_err(ctx, "Failed to add source ctrl handler\n");
+ return ret;
+ }
+ } else {
+ ret = cal_ctx_v4l2_init_mc_format(ctx);
+ if (ret) {
+ ctx_err(ctx, "Failed to init format: %d\n", ret);
return ret;
}
}
}
ret = media_create_pad_link(&ctx->phy->subdev.entity,
- CAL_CAMERARX_PAD_SOURCE,
+ CAL_CAMERARX_PAD_FIRST_SOURCE,
&vfd->entity, 0,
MEDIA_LNK_FL_IMMUTABLE |
MEDIA_LNK_FL_ENABLED);
if (ret) {
ctx_err(ctx, "Failed to create media link for context %u\n",
- ctx->index);
+ ctx->dma_ctx);
video_unregister_device(vfd);
return ret;
}
| (cal_mc_api ? V4L2_CAP_IO_MC : 0);
vfd->v4l2_dev = &ctx->cal->v4l2_dev;
vfd->queue = q;
- snprintf(vfd->name, sizeof(vfd->name), "CAL output %u", ctx->index);
+ snprintf(vfd->name, sizeof(vfd->name), "CAL output %u", ctx->dma_ctx);
vfd->release = video_device_release_empty;
- vfd->ioctl_ops = cal_mc_api ? &cal_ioctl_mc_ops : &cal_ioctl_video_ops;
+ vfd->ioctl_ops = cal_mc_api ? &cal_ioctl_mc_ops : &cal_ioctl_legacy_ops;
vfd->lock = &ctx->mutex;
video_set_drvdata(vfd, ctx);
* ------------------------------------------------------------------
*/
+#define CAL_MAX_PIX_PROC 4
+
+static int cal_reserve_pix_proc(struct cal_dev *cal)
+{
+ unsigned long ret;
+
+ spin_lock(&cal->v4l2_dev.lock);
+
+ ret = find_first_zero_bit(&cal->reserved_pix_proc_mask, CAL_MAX_PIX_PROC);
+
+ if (ret == CAL_MAX_PIX_PROC) {
+ spin_unlock(&cal->v4l2_dev.lock);
+ return -ENOSPC;
+ }
+
+ cal->reserved_pix_proc_mask |= BIT(ret);
+
+ spin_unlock(&cal->v4l2_dev.lock);
+
+ return ret;
+}
+
+static void cal_release_pix_proc(struct cal_dev *cal, unsigned int pix_proc_num)
+{
+ spin_lock(&cal->v4l2_dev.lock);
+
+ cal->reserved_pix_proc_mask &= ~BIT(pix_proc_num);
+
+ spin_unlock(&cal->v4l2_dev.lock);
+}
+
static void cal_ctx_csi2_config(struct cal_ctx *ctx)
{
u32 val;
- val = cal_read(ctx->cal, CAL_CSI2_CTX0(ctx->index));
+ val = cal_read(ctx->cal, CAL_CSI2_CTX(ctx->phy->instance, ctx->csi2_ctx));
cal_set_field(&val, ctx->cport, CAL_CSI2_CTX_CPORT_MASK);
/*
* DT type: MIPI CSI-2 Specs
* 0x2A: RAW8 1 pixel = 1 byte
* 0x1E: YUV422 2 pixels = 4 bytes
*/
- cal_set_field(&val, 0x1, CAL_CSI2_CTX_DT_MASK);
- cal_set_field(&val, 0, CAL_CSI2_CTX_VC_MASK);
+ cal_set_field(&val, ctx->datatype, CAL_CSI2_CTX_DT_MASK);
+ cal_set_field(&val, ctx->vc, CAL_CSI2_CTX_VC_MASK);
cal_set_field(&val, ctx->v_fmt.fmt.pix.height, CAL_CSI2_CTX_LINES_MASK);
cal_set_field(&val, CAL_CSI2_CTX_ATT_PIX, CAL_CSI2_CTX_ATT_MASK);
cal_set_field(&val, CAL_CSI2_CTX_PACK_MODE_LINE,
CAL_CSI2_CTX_PACK_MODE_MASK);
- cal_write(ctx->cal, CAL_CSI2_CTX0(ctx->index), val);
- ctx_dbg(3, ctx, "CAL_CSI2_CTX0(%d) = 0x%08x\n", ctx->index,
- cal_read(ctx->cal, CAL_CSI2_CTX0(ctx->index)));
+ cal_write(ctx->cal, CAL_CSI2_CTX(ctx->phy->instance, ctx->csi2_ctx), val);
+ ctx_dbg(3, ctx, "CAL_CSI2_CTX(%u, %u) = 0x%08x\n",
+ ctx->phy->instance, ctx->csi2_ctx,
+ cal_read(ctx->cal, CAL_CSI2_CTX(ctx->phy->instance, ctx->csi2_ctx)));
}
static void cal_ctx_pix_proc_config(struct cal_ctx *ctx)
break;
}
- val = cal_read(ctx->cal, CAL_PIX_PROC(ctx->index));
+ val = cal_read(ctx->cal, CAL_PIX_PROC(ctx->pix_proc));
cal_set_field(&val, extract, CAL_PIX_PROC_EXTRACT_MASK);
cal_set_field(&val, CAL_PIX_PROC_DPCMD_BYPASS, CAL_PIX_PROC_DPCMD_MASK);
cal_set_field(&val, CAL_PIX_PROC_DPCME_BYPASS, CAL_PIX_PROC_DPCME_MASK);
cal_set_field(&val, pack, CAL_PIX_PROC_PACK_MASK);
cal_set_field(&val, ctx->cport, CAL_PIX_PROC_CPORT_MASK);
cal_set_field(&val, 1, CAL_PIX_PROC_EN_MASK);
- cal_write(ctx->cal, CAL_PIX_PROC(ctx->index), val);
- ctx_dbg(3, ctx, "CAL_PIX_PROC(%d) = 0x%08x\n", ctx->index,
- cal_read(ctx->cal, CAL_PIX_PROC(ctx->index)));
+ cal_write(ctx->cal, CAL_PIX_PROC(ctx->pix_proc), val);
+ ctx_dbg(3, ctx, "CAL_PIX_PROC(%u) = 0x%08x\n", ctx->pix_proc,
+ cal_read(ctx->cal, CAL_PIX_PROC(ctx->pix_proc)));
}
static void cal_ctx_wr_dma_config(struct cal_ctx *ctx)
unsigned int stride = ctx->v_fmt.fmt.pix.bytesperline;
u32 val;
- val = cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index));
+ val = cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx));
cal_set_field(&val, ctx->cport, CAL_WR_DMA_CTRL_CPORT_MASK);
cal_set_field(&val, ctx->v_fmt.fmt.pix.height,
CAL_WR_DMA_CTRL_YSIZE_MASK);
cal_set_field(&val, CAL_WR_DMA_CTRL_DTAG_PIX_DAT,
CAL_WR_DMA_CTRL_DTAG_MASK);
- cal_set_field(&val, CAL_WR_DMA_CTRL_MODE_CONST,
- CAL_WR_DMA_CTRL_MODE_MASK);
cal_set_field(&val, CAL_WR_DMA_CTRL_PATTERN_LINEAR,
CAL_WR_DMA_CTRL_PATTERN_MASK);
cal_set_field(&val, 1, CAL_WR_DMA_CTRL_STALL_RD_MASK);
- cal_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->index), val);
- ctx_dbg(3, ctx, "CAL_WR_DMA_CTRL(%d) = 0x%08x\n", ctx->index,
- cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index)));
+ cal_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx), val);
+ ctx_dbg(3, ctx, "CAL_WR_DMA_CTRL(%d) = 0x%08x\n", ctx->dma_ctx,
+ cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx)));
- cal_write_field(ctx->cal, CAL_WR_DMA_OFST(ctx->index),
+ cal_write_field(ctx->cal, CAL_WR_DMA_OFST(ctx->dma_ctx),
stride / 16, CAL_WR_DMA_OFST_MASK);
- ctx_dbg(3, ctx, "CAL_WR_DMA_OFST(%d) = 0x%08x\n", ctx->index,
- cal_read(ctx->cal, CAL_WR_DMA_OFST(ctx->index)));
+ ctx_dbg(3, ctx, "CAL_WR_DMA_OFST(%d) = 0x%08x\n", ctx->dma_ctx,
+ cal_read(ctx->cal, CAL_WR_DMA_OFST(ctx->dma_ctx)));
- val = cal_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index));
+ val = cal_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->dma_ctx));
/* 64 bit word means no skipping */
cal_set_field(&val, 0, CAL_WR_DMA_XSIZE_XSKIP_MASK);
/*
* written per line.
*/
cal_set_field(&val, stride / 8, CAL_WR_DMA_XSIZE_MASK);
- cal_write(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index), val);
- ctx_dbg(3, ctx, "CAL_WR_DMA_XSIZE(%d) = 0x%08x\n", ctx->index,
- cal_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index)));
-
- val = cal_read(ctx->cal, CAL_CTRL);
- cal_set_field(&val, CAL_CTRL_BURSTSIZE_BURST128,
- CAL_CTRL_BURSTSIZE_MASK);
- cal_set_field(&val, 0xF, CAL_CTRL_TAGCNT_MASK);
- cal_set_field(&val, CAL_CTRL_POSTED_WRITES_NONPOSTED,
- CAL_CTRL_POSTED_WRITES_MASK);
- cal_set_field(&val, 0xFF, CAL_CTRL_MFLAGL_MASK);
- cal_set_field(&val, 0xFF, CAL_CTRL_MFLAGH_MASK);
- cal_write(ctx->cal, CAL_CTRL, val);
- ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", cal_read(ctx->cal, CAL_CTRL));
+ cal_write(ctx->cal, CAL_WR_DMA_XSIZE(ctx->dma_ctx), val);
+ ctx_dbg(3, ctx, "CAL_WR_DMA_XSIZE(%d) = 0x%08x\n", ctx->dma_ctx,
+ cal_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->dma_ctx)));
}
void cal_ctx_set_dma_addr(struct cal_ctx *ctx, dma_addr_t addr)
{
- cal_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->index), addr);
+ cal_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->dma_ctx), addr);
+}
+
+static void cal_ctx_wr_dma_enable(struct cal_ctx *ctx)
+{
+ u32 val = cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx));
+
+ cal_set_field(&val, CAL_WR_DMA_CTRL_MODE_CONST,
+ CAL_WR_DMA_CTRL_MODE_MASK);
+ cal_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx), val);
}
static void cal_ctx_wr_dma_disable(struct cal_ctx *ctx)
{
- u32 val = cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index));
+ u32 val = cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx));
cal_set_field(&val, CAL_WR_DMA_CTRL_MODE_DIS,
CAL_WR_DMA_CTRL_MODE_MASK);
- cal_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->index), val);
+ cal_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx), val);
}
static bool cal_ctx_wr_dma_stopped(struct cal_ctx *ctx)
return stopped;
}
+int cal_ctx_prepare(struct cal_ctx *ctx)
+{
+ int ret;
+
+ ctx->use_pix_proc = !ctx->fmtinfo->meta;
+
+ if (ctx->use_pix_proc) {
+ ret = cal_reserve_pix_proc(ctx->cal);
+ if (ret < 0) {
+ ctx_err(ctx, "Failed to reserve pix proc: %d\n", ret);
+ return ret;
+ }
+
+ ctx->pix_proc = ret;
+ }
+
+ return 0;
+}
+
+void cal_ctx_unprepare(struct cal_ctx *ctx)
+{
+ if (ctx->use_pix_proc)
+ cal_release_pix_proc(ctx->cal, ctx->pix_proc);
+}
+
void cal_ctx_start(struct cal_ctx *ctx)
{
ctx->sequence = 0;
/* Configure the CSI-2, pixel processing and write DMA contexts. */
cal_ctx_csi2_config(ctx);
- cal_ctx_pix_proc_config(ctx);
+ if (ctx->use_pix_proc)
+ cal_ctx_pix_proc_config(ctx);
cal_ctx_wr_dma_config(ctx);
/* Enable IRQ_WDMA_END and IRQ_WDMA_START. */
cal_write(ctx->cal, CAL_HL_IRQENABLE_SET(1),
- CAL_HL_IRQ_MASK(ctx->index));
+ CAL_HL_IRQ_WDMA_END_MASK(ctx->dma_ctx));
cal_write(ctx->cal, CAL_HL_IRQENABLE_SET(2),
- CAL_HL_IRQ_MASK(ctx->index));
+ CAL_HL_IRQ_WDMA_START_MASK(ctx->dma_ctx));
+
+ cal_ctx_wr_dma_enable(ctx);
}
void cal_ctx_stop(struct cal_ctx *ctx)
/* Disable IRQ_WDMA_END and IRQ_WDMA_START. */
cal_write(ctx->cal, CAL_HL_IRQENABLE_CLR(1),
- CAL_HL_IRQ_MASK(ctx->index));
+ CAL_HL_IRQ_WDMA_END_MASK(ctx->dma_ctx));
cal_write(ctx->cal, CAL_HL_IRQENABLE_CLR(2),
- CAL_HL_IRQ_MASK(ctx->index));
+ CAL_HL_IRQ_WDMA_START_MASK(ctx->dma_ctx));
ctx->dma.state = CAL_DMA_STOPPED;
+
+ /* Disable CSI2 context */
+ cal_write(ctx->cal, CAL_CSI2_CTX(ctx->phy->instance, ctx->csi2_ctx), 0);
+
+ /* Disable pix proc */
+ if (ctx->use_pix_proc)
+ cal_write(ctx->cal, CAL_PIX_PROC(ctx->pix_proc), 0);
}
/* ------------------------------------------------------------------
cal_write(cal, CAL_CSI2_COMPLEXIO_IRQSTATUS(i),
cio_stat);
}
+
+ if (status & CAL_HL_IRQ_VC_MASK(i)) {
+ u32 vc_stat = cal_read(cal, CAL_CSI2_VC_IRQSTATUS(i));
+
+ dev_err_ratelimited(cal->dev,
+ "CIO%u VC error: %#08x\n",
+ i, vc_stat);
+
+ cal_write(cal, CAL_CSI2_VC_IRQSTATUS(i), vc_stat);
+ }
}
}
/* Clear Interrupt status */
cal_write(cal, CAL_HL_IRQSTATUS(1), status);
- for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) {
- if (status & CAL_HL_IRQ_MASK(i))
+ for (i = 0; i < cal->num_contexts; ++i) {
+ if (status & CAL_HL_IRQ_WDMA_END_MASK(i))
cal_irq_wdma_end(cal->ctx[i]);
}
}
/* Clear Interrupt status */
cal_write(cal, CAL_HL_IRQSTATUS(2), status);
- for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) {
- if (status & CAL_HL_IRQ_MASK(i))
+ for (i = 0; i < cal->num_contexts; ++i) {
+ if (status & CAL_HL_IRQ_WDMA_START_MASK(i))
cal_irq_wdma_start(cal->ctx[i]);
}
}
int pad;
int ret;
- if (phy->sensor) {
+ if (phy->source) {
phy_info(phy, "Rejecting subdev %s (Already set!!)",
subdev->name);
return 0;
}
- phy->sensor = subdev;
- phy_dbg(1, phy, "Using sensor %s for capture\n", subdev->name);
+ phy->source = subdev;
+ phy_dbg(1, phy, "Using source %s for capture\n", subdev->name);
pad = media_entity_get_fwnode_pad(&subdev->entity,
- of_fwnode_handle(phy->sensor_ep_node),
+ of_fwnode_handle(phy->source_ep_node),
MEDIA_PAD_FL_SOURCE);
if (pad < 0) {
- phy_err(phy, "Sensor %s has no connected source pad\n",
+ phy_err(phy, "Source %s has no connected source pad\n",
subdev->name);
return pad;
}
MEDIA_LNK_FL_IMMUTABLE |
MEDIA_LNK_FL_ENABLED);
if (ret) {
- phy_err(phy, "Failed to create media link for sensor %s\n",
+ phy_err(phy, "Failed to create media link for source %s\n",
subdev->name);
return ret;
}
{
struct cal_dev *cal = container_of(notifier, struct cal_dev, notifier);
unsigned int i;
- int ret = 0;
+ int ret;
- for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) {
- if (cal->ctx[i])
- cal_ctx_v4l2_register(cal->ctx[i]);
+ for (i = 0; i < cal->num_contexts; ++i) {
+ ret = cal_ctx_v4l2_register(cal->ctx[i]);
+ if (ret)
+ goto err_ctx_unreg;
}
- if (cal_mc_api)
- ret = v4l2_device_register_subdev_nodes(&cal->v4l2_dev);
+ if (!cal_mc_api)
+ return 0;
+
+ ret = v4l2_device_register_subdev_nodes(&cal->v4l2_dev);
+ if (ret)
+ goto err_ctx_unreg;
+
+ return 0;
+
+err_ctx_unreg:
+ for (; i > 0; --i) {
+ if (!cal->ctx[i - 1])
+ continue;
+
+ cal_ctx_v4l2_unregister(cal->ctx[i - 1]);
+ }
return ret;
}
struct cal_v4l2_async_subdev *casd;
struct fwnode_handle *fwnode;
- if (!phy->sensor_node)
+ if (!phy->source_node)
continue;
- fwnode = of_fwnode_handle(phy->sensor_node);
+ fwnode = of_fwnode_handle(phy->source_node);
casd = v4l2_async_notifier_add_fwnode_subdev(&cal->notifier,
fwnode,
struct cal_v4l2_async_subdev);
unsigned int i;
/* Unregister all the V4L2 video devices. */
- for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) {
- if (cal->ctx[i])
- cal_ctx_v4l2_unregister(cal->ctx[i]);
- }
+ for (i = 0; i < cal->num_contexts; i++)
+ cal_ctx_v4l2_unregister(cal->ctx[i]);
cal_async_notifier_unregister(cal);
media_device_unregister(&cal->mdev);
*/
static void cal_media_cleanup(struct cal_dev *cal)
{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) {
- if (cal->ctx[i])
- cal_ctx_v4l2_cleanup(cal->ctx[i]);
- }
-
v4l2_device_unregister(&cal->v4l2_dev);
media_device_cleanup(&cal->mdev);
struct cal_ctx *ctx;
int ret;
- ctx = devm_kzalloc(cal->dev, sizeof(*ctx), GFP_KERNEL);
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return NULL;
ctx->cal = cal;
ctx->phy = cal->phy[inst];
- ctx->index = inst;
+ ctx->dma_ctx = inst;
+ ctx->csi2_ctx = inst;
ctx->cport = inst;
+ ctx->vc = 0;
+ ctx->datatype = CAL_CSI2_CTX_DT_ANY;
ret = cal_ctx_v4l2_init(ctx);
if (ret)
return ctx;
}
+static void cal_ctx_destroy(struct cal_ctx *ctx)
+{
+ cal_ctx_v4l2_cleanup(ctx);
+
+ kfree(ctx);
+}
+
static const struct of_device_id cal_of_match[] = {
{
.compatible = "ti,dra72-cal",
static int cal_probe(struct platform_device *pdev)
{
struct cal_dev *cal;
- struct cal_ctx *ctx;
bool connected = false;
unsigned int i;
int ret;
goto error_camerarx;
}
- if (cal->phy[i]->sensor_node)
+ if (cal->phy[i]->source_node)
connected = true;
}
/* Create contexts. */
for (i = 0; i < cal->data->num_csi2_phy; ++i) {
- if (!cal->phy[i]->sensor_node)
+ if (!cal->phy[i]->source_node)
continue;
- cal->ctx[i] = cal_ctx_create(cal, i);
- if (!cal->ctx[i]) {
- cal_err(cal, "Failed to create context %u\n", i);
+ cal->ctx[cal->num_contexts] = cal_ctx_create(cal, i);
+ if (!cal->ctx[cal->num_contexts]) {
+ cal_err(cal, "Failed to create context %u\n", cal->num_contexts);
ret = -ENODEV;
goto error_context;
}
+
+ cal->num_contexts++;
}
/* Register the media device. */
return 0;
error_context:
- for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) {
- ctx = cal->ctx[i];
- if (ctx)
- cal_ctx_v4l2_cleanup(ctx);
- }
+ for (i = 0; i < cal->num_contexts; i++)
+ cal_ctx_destroy(cal->ctx[i]);
error_camerarx:
for (i = 0; i < cal->data->num_csi2_phy; i++)
cal_media_unregister(cal);
- for (i = 0; i < ARRAY_SIZE(cal->phy); i++) {
- if (cal->phy[i])
- cal_camerarx_disable(cal->phy[i]);
- }
+ for (i = 0; i < cal->data->num_csi2_phy; i++)
+ cal_camerarx_disable(cal->phy[i]);
- cal_media_cleanup(cal);
+ for (i = 0; i < cal->num_contexts; i++)
+ cal_ctx_destroy(cal->ctx[i]);
for (i = 0; i < cal->data->num_csi2_phy; i++)
cal_camerarx_destroy(cal->phy[i]);
+ cal_media_cleanup(cal);
+
if (ret >= 0)
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
{
struct cal_dev *cal = dev_get_drvdata(dev);
unsigned int i;
+ u32 val;
if (cal->data->flags & DRA72_CAL_PRE_ES2_LDO_DISABLE) {
/*
*/
cal_write(cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK);
+ val = cal_read(cal, CAL_CTRL);
+ cal_set_field(&val, CAL_CTRL_BURSTSIZE_BURST128,
+ CAL_CTRL_BURSTSIZE_MASK);
+ cal_set_field(&val, 0xf, CAL_CTRL_TAGCNT_MASK);
+ cal_set_field(&val, CAL_CTRL_POSTED_WRITES_NONPOSTED,
+ CAL_CTRL_POSTED_WRITES_MASK);
+ cal_set_field(&val, 0xff, CAL_CTRL_MFLAGL_MASK);
+ cal_set_field(&val, 0xff, CAL_CTRL_MFLAGH_MASK);
+ cal_write(cal, CAL_CTRL, val);
+ cal_dbg(3, cal, "CAL_CTRL = 0x%08x\n", cal_read(cal, CAL_CTRL));
+
return 0;
}
#include <media/videobuf2-v4l2.h>
#define CAL_MODULE_NAME "cal"
-#define CAL_NUM_CONTEXT 2
+#define CAL_MAX_NUM_CONTEXT 8
#define CAL_NUM_CSI2_PORTS 2
/*
#define CAL_MAX_HEIGHT_LINES 16383
#define CAL_CAMERARX_PAD_SINK 0
-#define CAL_CAMERARX_PAD_SOURCE 1
+#define CAL_CAMERARX_PAD_FIRST_SOURCE 1
+#define CAL_CAMERARX_NUM_SOURCE_PADS 1
+#define CAL_CAMERARX_NUM_PADS (1 + CAL_CAMERARX_NUM_SOURCE_PADS)
+
+static inline bool cal_rx_pad_is_sink(u32 pad)
+{
+ /* Camera RX has 1 sink pad, and N source pads */
+ return pad == 0;
+}
+
+static inline bool cal_rx_pad_is_source(u32 pad)
+{
+ /* Camera RX has 1 sink pad, and N source pads */
+ return pad >= CAL_CAMERARX_PAD_FIRST_SOURCE &&
+ pad <= CAL_CAMERARX_NUM_SOURCE_PADS;
+}
struct device;
struct device_node;
u32 code;
/* Bits per pixel */
u8 bpp;
+ bool meta;
};
/* buffer for one video frame */
struct cal_camerarx {
void __iomem *base;
struct resource *res;
- struct device *dev;
struct regmap_field *fields[F_MAX_FIELDS];
struct cal_dev *cal;
unsigned int instance;
struct v4l2_fwnode_endpoint endpoint;
- struct device_node *sensor_ep_node;
- struct device_node *sensor_node;
- struct v4l2_subdev *sensor;
+ struct device_node *source_ep_node;
+ struct device_node *source_node;
+ struct v4l2_subdev *source;
struct media_pipeline pipe;
struct v4l2_subdev subdev;
- struct media_pad pads[2];
- struct v4l2_mbus_framefmt formats[2];
- const struct cal_format_info *fmtinfo;
+ struct media_pad pads[CAL_CAMERARX_NUM_PADS];
+ struct v4l2_mbus_framefmt formats[CAL_CAMERARX_NUM_PADS];
+
+ /*
+ * Lock for camerarx ops. Protects:
+ * - formats
+ * - enable_count
+ */
+ struct mutex mutex;
+
+ unsigned int enable_count;
};
struct cal_dev {
/* Camera Core Module handle */
struct cal_camerarx *phy[CAL_NUM_CSI2_PORTS];
- struct cal_ctx *ctx[CAL_NUM_CONTEXT];
+ u32 num_contexts;
+ struct cal_ctx *ctx[CAL_MAX_NUM_CONTEXT];
struct media_device mdev;
struct v4l2_device v4l2_dev;
struct v4l2_async_notifier notifier;
+
+ unsigned long reserved_pix_proc_mask;
};
/*
/* Used to store current pixel format */
struct v4l2_format v_fmt;
- /* Current subdev enumerated format */
+ /* Current subdev enumerated format (legacy) */
const struct cal_format_info **active_fmt;
unsigned int num_active_fmt;
unsigned int sequence;
struct vb2_queue vb_vidq;
- unsigned int index;
- unsigned int cport;
+ u8 dma_ctx;
+ u8 cport;
+ u8 csi2_ctx;
+ u8 pix_proc;
+ u8 vc;
+ u8 datatype;
+
+ bool use_pix_proc;
};
extern unsigned int cal_debug;
dev_err((cal)->dev, fmt, ##arg)
#define ctx_dbg(level, ctx, fmt, arg...) \
- cal_dbg(level, (ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg)
+ cal_dbg(level, (ctx)->cal, "ctx%u: " fmt, (ctx)->dma_ctx, ##arg)
#define ctx_info(ctx, fmt, arg...) \
- cal_info((ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg)
+ cal_info((ctx)->cal, "ctx%u: " fmt, (ctx)->dma_ctx, ##arg)
#define ctx_err(ctx, fmt, arg...) \
- cal_err((ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg)
+ cal_err((ctx)->cal, "ctx%u: " fmt, (ctx)->dma_ctx, ##arg)
#define phy_dbg(level, phy, fmt, arg...) \
cal_dbg(level, (phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg)
unsigned int instance);
void cal_camerarx_destroy(struct cal_camerarx *phy);
+int cal_ctx_prepare(struct cal_ctx *ctx);
+void cal_ctx_unprepare(struct cal_ctx *ctx);
void cal_ctx_set_dma_addr(struct cal_ctx *ctx, dma_addr_t addr);
void cal_ctx_start(struct cal_ctx *ctx);
void cal_ctx_stop(struct cal_ctx *ctx);
#define CAL_CSI2_TIMING(m) (0x314U + (m) * 0x80U)
#define CAL_CSI2_VC_IRQENABLE(m) (0x318U + (m) * 0x80U)
#define CAL_CSI2_VC_IRQSTATUS(m) (0x328U + (m) * 0x80U)
-#define CAL_CSI2_CTX0(m) (0x330U + (m) * 0x80U)
-#define CAL_CSI2_CTX1(m) (0x334U + (m) * 0x80U)
-#define CAL_CSI2_CTX2(m) (0x338U + (m) * 0x80U)
-#define CAL_CSI2_CTX3(m) (0x33cU + (m) * 0x80U)
-#define CAL_CSI2_CTX4(m) (0x340U + (m) * 0x80U)
-#define CAL_CSI2_CTX5(m) (0x344U + (m) * 0x80U)
-#define CAL_CSI2_CTX6(m) (0x348U + (m) * 0x80U)
-#define CAL_CSI2_CTX7(m) (0x34cU + (m) * 0x80U)
-#define CAL_CSI2_STATUS0(m) (0x350U + (m) * 0x80U)
-#define CAL_CSI2_STATUS1(m) (0x354U + (m) * 0x80U)
-#define CAL_CSI2_STATUS2(m) (0x358U + (m) * 0x80U)
-#define CAL_CSI2_STATUS3(m) (0x35cU + (m) * 0x80U)
-#define CAL_CSI2_STATUS4(m) (0x360U + (m) * 0x80U)
-#define CAL_CSI2_STATUS5(m) (0x364U + (m) * 0x80U)
-#define CAL_CSI2_STATUS6(m) (0x368U + (m) * 0x80U)
-#define CAL_CSI2_STATUS7(m) (0x36cU + (m) * 0x80U)
+#define CAL_CSI2_CTX(phy, csi2_ctx) (0x330U + (phy) * 0x80U + (csi2_ctx) * 4)
+#define CAL_CSI2_STATUS(phy, csi2_ctx) (0x350U + (phy) * 0x80U + (csi2_ctx) * 4)
/* CAL CSI2 PHY register offsets */
#define CAL_CSI2_PHY_REG0 0x000
#define CAL_HL_IRQ_EOI_LINE_NUMBER_READ0 0
#define CAL_HL_IRQ_EOI_LINE_NUMBER_EOI0 0
-#define CAL_HL_IRQ_MASK(m) BIT(m)
+#define CAL_HL_IRQ_WDMA_END_MASK(m) BIT(m)
+#define CAL_HL_IRQ_WDMA_START_MASK(m) BIT(m)
#define CAL_HL_IRQ_OCPO_ERR_MASK BIT(6)
#define CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK BIT(14)
#define CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK BIT(15)
-#define CAL_CSI2_VC_IRQ_FS_IRQ_0_MASK BIT(0)
-#define CAL_CSI2_VC_IRQ_FE_IRQ_0_MASK BIT(1)
-#define CAL_CSI2_VC_IRQ_LS_IRQ_0_MASK BIT(2)
-#define CAL_CSI2_VC_IRQ_LE_IRQ_0_MASK BIT(3)
-#define CAL_CSI2_VC_IRQ_CS_IRQ_0_MASK BIT(4)
-#define CAL_CSI2_VC_IRQ_ECC_CORRECTION0_IRQ_0_MASK BIT(5)
-#define CAL_CSI2_VC_IRQ_FS_IRQ_1_MASK BIT(8)
-#define CAL_CSI2_VC_IRQ_FE_IRQ_1_MASK BIT(9)
-#define CAL_CSI2_VC_IRQ_LS_IRQ_1_MASK BIT(10)
-#define CAL_CSI2_VC_IRQ_LE_IRQ_1_MASK BIT(11)
-#define CAL_CSI2_VC_IRQ_CS_IRQ_1_MASK BIT(12)
-#define CAL_CSI2_VC_IRQ_ECC_CORRECTION0_IRQ_1_MASK BIT(13)
-#define CAL_CSI2_VC_IRQ_FS_IRQ_2_MASK BIT(16)
-#define CAL_CSI2_VC_IRQ_FE_IRQ_2_MASK BIT(17)
-#define CAL_CSI2_VC_IRQ_LS_IRQ_2_MASK BIT(18)
-#define CAL_CSI2_VC_IRQ_LE_IRQ_2_MASK BIT(19)
-#define CAL_CSI2_VC_IRQ_CS_IRQ_2_MASK BIT(20)
-#define CAL_CSI2_VC_IRQ_ECC_CORRECTION0_IRQ_2_MASK BIT(21)
-#define CAL_CSI2_VC_IRQ_FS_IRQ_3_MASK BIT(24)
-#define CAL_CSI2_VC_IRQ_FE_IRQ_3_MASK BIT(25)
-#define CAL_CSI2_VC_IRQ_LS_IRQ_3_MASK BIT(26)
-#define CAL_CSI2_VC_IRQ_LE_IRQ_3_MASK BIT(27)
-#define CAL_CSI2_VC_IRQ_CS_IRQ_3_MASK BIT(28)
-#define CAL_CSI2_VC_IRQ_ECC_CORRECTION0_IRQ_3_MASK BIT(29)
+#define CAL_CSI2_VC_IRQ_FS_IRQ_MASK(n) BIT(0 + ((n) * 8))
+#define CAL_CSI2_VC_IRQ_FE_IRQ_MASK(n) BIT(1 + ((n) * 8))
+#define CAL_CSI2_VC_IRQ_LS_IRQ_MASK(n) BIT(2 + ((n) * 8))
+#define CAL_CSI2_VC_IRQ_LE_IRQ_MASK(n) BIT(3 + ((n) * 8))
+#define CAL_CSI2_VC_IRQ_CS_IRQ_MASK(n) BIT(4 + ((n) * 8))
+#define CAL_CSI2_VC_IRQ_ECC_CORRECTION_IRQ_MASK(n) BIT(5 + ((n) * 8))
#define CAL_CSI2_CTX_DT_MASK GENMASK(5, 0)
+#define CAL_CSI2_CTX_DT_DISABLED 0
+#define CAL_CSI2_CTX_DT_ANY 1
#define CAL_CSI2_CTX_VC_MASK GENMASK(7, 6)
#define CAL_CSI2_CTX_CPORT_MASK GENMASK(12, 8)
#define CAL_CSI2_CTX_ATT_MASK BIT(13)
* rectangles.
*/
entity->config = v4l2_subdev_alloc_state(&entity->subdev);
- if (entity->config == NULL) {
+ if (IS_ERR(entity->config)) {
media_entity_cleanup(&entity->subdev.entity);
- return -ENOMEM;
+ return PTR_ERR(entity->config);
}
return 0;
To compile this driver as a module, choose M here: the
module will be called meson-ir.
+config IR_MESON_TX
+ tristate "Amlogic Meson IR TX"
+ depends on ARCH_MESON || COMPILE_TEST
+ help
+ Say Y if you want to use the IR transmitter available on
+ Amlogic Meson SoCs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called meson-ir-tx.
+
config IR_MTK
tristate "Mediatek IR remote receiver"
depends on ARCH_MEDIATEK || COMPILE_TEST
obj-$(CONFIG_IR_MCEUSB) += mceusb.o
obj-$(CONFIG_IR_FINTEK) += fintek-cir.o
obj-$(CONFIG_IR_MESON) += meson-ir.o
+obj-$(CONFIG_IR_MESON_TX) += meson-ir-tx.o
obj-$(CONFIG_IR_NUVOTON) += nuvoton-cir.o
obj-$(CONFIG_IR_ENE) += ene_ir.o
obj-$(CONFIG_IR_REDRAT3) += redrat3.o
rdev->device_name = "ENE eHome Infrared Remote Receiver";
if (dev->hw_learning_and_tx_capable) {
- rdev->s_learning_mode = ene_set_learning_mode;
+ rdev->s_wideband_receiver = ene_set_learning_mode;
init_completion(&dev->tx_complete);
rdev->tx_ir = ene_transmit;
rdev->s_tx_mask = ene_set_tx_mask;
val |= LIRC_CAN_SET_REC_CARRIER |
LIRC_CAN_SET_REC_CARRIER_RANGE;
- if (dev->s_learning_mode)
+ if (dev->s_wideband_receiver)
val |= LIRC_CAN_USE_WIDEBAND_RECEIVER;
if (dev->s_carrier_report)
break;
case LIRC_SET_WIDEBAND_RECEIVER:
- if (!dev->s_learning_mode)
+ if (!dev->s_wideband_receiver)
ret = -ENOTTY;
else
- ret = dev->s_learning_mode(dev, !!val);
+ ret = dev->s_wideband_receiver(dev, !!val);
break;
case LIRC_SET_MEASURE_CARRIER_MODE:
rc->tx_ir = mceusb_tx_ir;
}
if (ir->flags.rx2 > 0) {
- rc->s_learning_mode = mceusb_set_rx_wideband;
+ rc->s_wideband_receiver = mceusb_set_rx_wideband;
rc->s_carrier_report = mceusb_set_rx_carrier_report;
}
rc->driver_name = DRIVER_NAME;
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-only
+/**
+ * meson-ir-tx.c - Amlogic Meson IR TX driver
+ *
+ * Copyright (c) 2021, SberDevices. All Rights Reserved.
+ *
+ * Author: Viktor Prutyanov <viktor.prutyanov@phystech.edu>
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/of_irq.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <media/rc-core.h>
+
+#define DEVICE_NAME "Meson IR TX"
+#define DRIVER_NAME "meson-ir-tx"
+
+#define MIRTX_DEFAULT_CARRIER 38000
+#define MIRTX_DEFAULT_DUTY_CYCLE 50
+#define MIRTX_FIFO_THD 32
+
+#define IRB_MOD_1US_CLK_RATE 1000000
+
+#define IRB_FIFO_LEN 128
+
+#define IRB_ADDR0 0x0
+#define IRB_ADDR1 0x4
+#define IRB_ADDR2 0x8
+#define IRB_ADDR3 0xc
+
+#define IRB_MAX_DELAY (1 << 10)
+#define IRB_DELAY_MASK (IRB_MAX_DELAY - 1)
+
+/* IRCTRL_IR_BLASTER_ADDR0 */
+#define IRB_MOD_CLK(x) ((x) << 12)
+#define IRB_MOD_SYS_CLK 0
+#define IRB_MOD_XTAL3_CLK 1
+#define IRB_MOD_1US_CLK 2
+#define IRB_MOD_10US_CLK 3
+#define IRB_INIT_HIGH BIT(2)
+#define IRB_ENABLE BIT(0)
+
+/* IRCTRL_IR_BLASTER_ADDR2 */
+#define IRB_MOD_COUNT(lo, hi) ((((lo) - 1) << 16) | ((hi) - 1))
+
+/* IRCTRL_IR_BLASTER_ADDR2 */
+#define IRB_WRITE_FIFO BIT(16)
+#define IRB_MOD_ENABLE BIT(12)
+#define IRB_TB_1US (0x0 << 10)
+#define IRB_TB_10US (0x1 << 10)
+#define IRB_TB_100US (0x2 << 10)
+#define IRB_TB_MOD_CLK (0x3 << 10)
+
+/* IRCTRL_IR_BLASTER_ADDR3 */
+#define IRB_FIFO_THD_PENDING BIT(16)
+#define IRB_FIFO_IRQ_ENABLE BIT(8)
+
+struct meson_irtx {
+ struct device *dev;
+ void __iomem *reg_base;
+ u32 *buf;
+ unsigned int buf_len;
+ unsigned int buf_head;
+ unsigned int carrier;
+ unsigned int duty_cycle;
+ /* Locks buf */
+ spinlock_t lock;
+ struct completion completion;
+ unsigned long clk_rate;
+};
+
+static void meson_irtx_set_mod(struct meson_irtx *ir)
+{
+ unsigned int cnt = DIV_ROUND_CLOSEST(ir->clk_rate, ir->carrier);
+ unsigned int pulse_cnt = DIV_ROUND_CLOSEST(cnt * ir->duty_cycle, 100);
+ unsigned int space_cnt = cnt - pulse_cnt;
+
+ dev_dbg(ir->dev, "F_mod = %uHz, T_mod = %luns, duty_cycle = %u%%\n",
+ ir->carrier, NSEC_PER_SEC / ir->clk_rate * cnt,
+ 100 * pulse_cnt / cnt);
+
+ writel(IRB_MOD_COUNT(pulse_cnt, space_cnt),
+ ir->reg_base + IRB_ADDR1);
+}
+
+static void meson_irtx_setup(struct meson_irtx *ir, unsigned int clk_nr)
+{
+ /*
+ * Disable the TX, set modulator clock tick and set initialize
+ * output to be high. Set up carrier frequency and duty cycle. Then
+ * unset initialize output. Enable FIFO interrupt, set FIFO interrupt
+ * threshold. Finally, enable the transmitter back.
+ */
+ writel(~IRB_ENABLE & (IRB_MOD_CLK(clk_nr) | IRB_INIT_HIGH),
+ ir->reg_base + IRB_ADDR0);
+ meson_irtx_set_mod(ir);
+ writel(readl(ir->reg_base + IRB_ADDR0) & ~IRB_INIT_HIGH,
+ ir->reg_base + IRB_ADDR0);
+ writel(IRB_FIFO_IRQ_ENABLE | MIRTX_FIFO_THD,
+ ir->reg_base + IRB_ADDR3);
+ writel(readl(ir->reg_base + IRB_ADDR0) | IRB_ENABLE,
+ ir->reg_base + IRB_ADDR0);
+}
+
+static u32 meson_irtx_prepare_pulse(struct meson_irtx *ir, unsigned int time)
+{
+ unsigned int delay;
+ unsigned int tb = IRB_TB_MOD_CLK;
+ unsigned int tb_us = DIV_ROUND_CLOSEST(USEC_PER_SEC, ir->carrier);
+
+ delay = (DIV_ROUND_CLOSEST(time, tb_us) - 1) & IRB_DELAY_MASK;
+
+ return ((IRB_WRITE_FIFO | IRB_MOD_ENABLE) | tb | delay);
+}
+
+static u32 meson_irtx_prepare_space(struct meson_irtx *ir, unsigned int time)
+{
+ unsigned int delay;
+ unsigned int tb = IRB_TB_100US;
+ unsigned int tb_us = 100;
+
+ if (time <= IRB_MAX_DELAY) {
+ tb = IRB_TB_1US;
+ tb_us = 1;
+ } else if (time <= 10 * IRB_MAX_DELAY) {
+ tb = IRB_TB_10US;
+ tb_us = 10;
+ } else if (time <= 100 * IRB_MAX_DELAY) {
+ tb = IRB_TB_100US;
+ tb_us = 100;
+ }
+
+ delay = (DIV_ROUND_CLOSEST(time, tb_us) - 1) & IRB_DELAY_MASK;
+
+ return ((IRB_WRITE_FIFO & ~IRB_MOD_ENABLE) | tb | delay);
+}
+
+static void meson_irtx_send_buffer(struct meson_irtx *ir)
+{
+ unsigned int nr = 0;
+ unsigned int max_fifo_level = IRB_FIFO_LEN - MIRTX_FIFO_THD;
+
+ while (ir->buf_head < ir->buf_len && nr < max_fifo_level) {
+ writel(ir->buf[ir->buf_head], ir->reg_base + IRB_ADDR2);
+
+ ir->buf_head++;
+ nr++;
+ }
+}
+
+static bool meson_irtx_check_buf(struct meson_irtx *ir,
+ unsigned int *buf, unsigned int len)
+{
+ unsigned int i;
+
+ for (i = 0; i < len; i++) {
+ unsigned int max_tb_us;
+ /*
+ * Max space timebase is 100 us.
+ * Pulse timebase equals to carrier period.
+ */
+ if (i % 2 == 0)
+ max_tb_us = USEC_PER_SEC / ir->carrier;
+ else
+ max_tb_us = 100;
+
+ if (buf[i] >= max_tb_us * IRB_MAX_DELAY)
+ return false;
+ }
+
+ return true;
+}
+
+static void meson_irtx_fill_buf(struct meson_irtx *ir, u32 *dst_buf,
+ unsigned int *src_buf, unsigned int len)
+{
+ unsigned int i;
+
+ for (i = 0; i < len; i++) {
+ if (i % 2 == 0)
+ dst_buf[i] = meson_irtx_prepare_pulse(ir, src_buf[i]);
+ else
+ dst_buf[i] = meson_irtx_prepare_space(ir, src_buf[i]);
+ }
+}
+
+static irqreturn_t meson_irtx_irqhandler(int irq, void *data)
+{
+ unsigned long flags;
+ struct meson_irtx *ir = data;
+
+ writel(readl(ir->reg_base + IRB_ADDR3) & ~IRB_FIFO_THD_PENDING,
+ ir->reg_base + IRB_ADDR3);
+
+ if (completion_done(&ir->completion))
+ return IRQ_HANDLED;
+
+ spin_lock_irqsave(&ir->lock, flags);
+ if (ir->buf_head < ir->buf_len)
+ meson_irtx_send_buffer(ir);
+ else
+ complete(&ir->completion);
+ spin_unlock_irqrestore(&ir->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static int meson_irtx_set_carrier(struct rc_dev *rc, u32 carrier)
+{
+ struct meson_irtx *ir = rc->priv;
+
+ if (carrier == 0)
+ return -EINVAL;
+
+ ir->carrier = carrier;
+ meson_irtx_set_mod(ir);
+
+ return 0;
+}
+
+static int meson_irtx_set_duty_cycle(struct rc_dev *rc, u32 duty_cycle)
+{
+ struct meson_irtx *ir = rc->priv;
+
+ ir->duty_cycle = duty_cycle;
+ meson_irtx_set_mod(ir);
+
+ return 0;
+}
+
+static void meson_irtx_update_buf(struct meson_irtx *ir, u32 *buf,
+ unsigned int len, unsigned int head)
+{
+ ir->buf = buf;
+ ir->buf_len = len;
+ ir->buf_head = head;
+}
+
+static int meson_irtx_transmit(struct rc_dev *rc, unsigned int *buf,
+ unsigned int len)
+{
+ unsigned long flags;
+ struct meson_irtx *ir = rc->priv;
+ u32 *tx_buf;
+ int ret = len;
+
+ if (!meson_irtx_check_buf(ir, buf, len))
+ return -EINVAL;
+
+ tx_buf = kmalloc_array(len, sizeof(u32), GFP_KERNEL);
+ if (!tx_buf)
+ return -ENOMEM;
+
+ meson_irtx_fill_buf(ir, tx_buf, buf, len);
+ dev_dbg(ir->dev, "TX buffer filled, length = %u\n", len);
+
+ spin_lock_irqsave(&ir->lock, flags);
+ meson_irtx_update_buf(ir, tx_buf, len, 0);
+ reinit_completion(&ir->completion);
+ meson_irtx_send_buffer(ir);
+ spin_unlock_irqrestore(&ir->lock, flags);
+
+ if (!wait_for_completion_timeout(&ir->completion,
+ usecs_to_jiffies(IR_MAX_DURATION)))
+ ret = -ETIMEDOUT;
+
+ spin_lock_irqsave(&ir->lock, flags);
+ kfree(ir->buf);
+ meson_irtx_update_buf(ir, NULL, 0, 0);
+ spin_unlock_irqrestore(&ir->lock, flags);
+
+ return ret;
+}
+
+static int meson_irtx_mod_clock_probe(struct meson_irtx *ir,
+ unsigned int *clk_nr)
+{
+ struct device_node *np = ir->dev->of_node;
+ struct clk *clock;
+
+ if (!np)
+ return -ENODEV;
+
+ clock = devm_clk_get(ir->dev, "xtal");
+ if (IS_ERR(clock) || clk_prepare_enable(clock))
+ return -ENODEV;
+
+ *clk_nr = IRB_MOD_XTAL3_CLK;
+ ir->clk_rate = clk_get_rate(clock) / 3;
+
+ if (ir->clk_rate < IRB_MOD_1US_CLK_RATE) {
+ *clk_nr = IRB_MOD_1US_CLK;
+ ir->clk_rate = IRB_MOD_1US_CLK_RATE;
+ }
+
+ dev_info(ir->dev, "F_clk = %luHz\n", ir->clk_rate);
+
+ return 0;
+}
+
+static int __init meson_irtx_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct meson_irtx *ir;
+ struct rc_dev *rc;
+ int irq;
+ unsigned int clk_nr;
+ int ret;
+
+ ir = devm_kzalloc(dev, sizeof(*ir), GFP_KERNEL);
+ if (!ir)
+ return -ENOMEM;
+
+ ir->reg_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(ir->reg_base))
+ return PTR_ERR(ir->reg_base);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(dev, "no irq resource found\n");
+ return -ENODEV;
+ }
+
+ ir->dev = dev;
+ ir->carrier = MIRTX_DEFAULT_CARRIER;
+ ir->duty_cycle = MIRTX_DEFAULT_DUTY_CYCLE;
+ init_completion(&ir->completion);
+ spin_lock_init(&ir->lock);
+
+ ret = meson_irtx_mod_clock_probe(ir, &clk_nr);
+ if (ret) {
+ dev_err(dev, "modulator clock setup failed\n");
+ return ret;
+ }
+ meson_irtx_setup(ir, clk_nr);
+
+ ret = devm_request_irq(dev, irq,
+ meson_irtx_irqhandler,
+ IRQF_TRIGGER_RISING,
+ DRIVER_NAME, ir);
+ if (ret) {
+ dev_err(dev, "irq request failed\n");
+ return ret;
+ }
+
+ rc = rc_allocate_device(RC_DRIVER_IR_RAW_TX);
+ if (!rc)
+ return -ENOMEM;
+
+ rc->driver_name = DRIVER_NAME;
+ rc->device_name = DEVICE_NAME;
+ rc->priv = ir;
+
+ rc->tx_ir = meson_irtx_transmit;
+ rc->s_tx_carrier = meson_irtx_set_carrier;
+ rc->s_tx_duty_cycle = meson_irtx_set_duty_cycle;
+
+ ret = rc_register_device(rc);
+ if (ret < 0) {
+ dev_err(dev, "rc_dev registration failed\n");
+ rc_free_device(rc);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, rc);
+
+ return 0;
+}
+
+static int meson_irtx_remove(struct platform_device *pdev)
+{
+ struct rc_dev *rc = platform_get_drvdata(pdev);
+
+ rc_unregister_device(rc);
+
+ return 0;
+}
+
+static const struct of_device_id meson_irtx_dt_match[] = {
+ {
+ .compatible = "amlogic,meson-g12a-ir-tx",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, meson_irtx_dt_match);
+
+static struct platform_driver meson_irtx_pd = {
+ .remove = meson_irtx_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = meson_irtx_dt_match,
+ },
+};
+
+module_platform_driver_probe(meson_irtx_pd, meson_irtx_probe);
+
+MODULE_DESCRIPTION("Meson IR TX driver");
+MODULE_AUTHOR("Viktor Prutyanov <viktor.prutyanov@phystech.edu>");
+MODULE_LICENSE("GPL");
#include <linux/slab.h>
#include <media/rc-core.h>
-#define DRIVER_NAME "rc-loopback"
-#define dprintk(x...) if (debug) printk(KERN_INFO DRIVER_NAME ": " x)
-#define RXMASK_REGULAR 0x1
-#define RXMASK_LEARNING 0x2
-
-static bool debug;
+#define DRIVER_NAME "rc-loopback"
+#define RXMASK_NARROWBAND 0x1
+#define RXMASK_WIDEBAND 0x2
struct loopback_dev {
struct rc_dev *dev;
u32 txcarrier;
u32 txduty;
bool idle;
- bool learning;
+ bool wideband;
bool carrierreport;
u32 rxcarriermin;
u32 rxcarriermax;
{
struct loopback_dev *lodev = dev->priv;
- if ((mask & (RXMASK_REGULAR | RXMASK_LEARNING)) != mask) {
- dprintk("invalid tx mask: %u\n", mask);
- return -EINVAL;
+ if ((mask & (RXMASK_NARROWBAND | RXMASK_WIDEBAND)) != mask) {
+ dev_dbg(&dev->dev, "invalid tx mask: %u\n", mask);
+ return 2;
}
- dprintk("setting tx mask: %u\n", mask);
+ dev_dbg(&dev->dev, "setting tx mask: %u\n", mask);
lodev->txmask = mask;
return 0;
}
{
struct loopback_dev *lodev = dev->priv;
- dprintk("setting tx carrier: %u\n", carrier);
+ dev_dbg(&dev->dev, "setting tx carrier: %u\n", carrier);
lodev->txcarrier = carrier;
return 0;
}
struct loopback_dev *lodev = dev->priv;
if (duty_cycle < 1 || duty_cycle > 99) {
- dprintk("invalid duty cycle: %u\n", duty_cycle);
+ dev_dbg(&dev->dev, "invalid duty cycle: %u\n", duty_cycle);
return -EINVAL;
}
- dprintk("setting duty cycle: %u\n", duty_cycle);
+ dev_dbg(&dev->dev, "setting duty cycle: %u\n", duty_cycle);
lodev->txduty = duty_cycle;
return 0;
}
struct loopback_dev *lodev = dev->priv;
if (min < 1 || min > max) {
- dprintk("invalid rx carrier range %u to %u\n", min, max);
+ dev_dbg(&dev->dev, "invalid rx carrier range %u to %u\n", min, max);
return -EINVAL;
}
- dprintk("setting rx carrier range %u to %u\n", min, max);
+ dev_dbg(&dev->dev, "setting rx carrier range %u to %u\n", min, max);
lodev->rxcarriermin = min;
lodev->rxcarriermax = max;
return 0;
if (lodev->txcarrier < lodev->rxcarriermin ||
lodev->txcarrier > lodev->rxcarriermax) {
- dprintk("ignoring tx, carrier out of range\n");
+ dev_dbg(&dev->dev, "ignoring tx, carrier out of range\n");
goto out;
}
- if (lodev->learning)
- rxmask = RXMASK_LEARNING;
+ if (lodev->wideband)
+ rxmask = RXMASK_WIDEBAND;
else
- rxmask = RXMASK_REGULAR;
+ rxmask = RXMASK_NARROWBAND;
if (!(rxmask & lodev->txmask)) {
- dprintk("ignoring tx, rx mask mismatch\n");
+ dev_dbg(&dev->dev, "ignoring tx, rx mask mismatch\n");
goto out;
}
for (i = 0; i < count; i++) {
rawir.pulse = i % 2 ? false : true;
rawir.duration = txbuf[i];
- if (rawir.duration)
- ir_raw_event_store_with_filter(dev, &rawir);
+
+ ir_raw_event_store_with_filter(dev, &rawir);
+ }
+
+ if (lodev->carrierreport) {
+ rawir.pulse = false;
+ rawir.carrier_report = true;
+ rawir.carrier = lodev->txcarrier;
+
+ ir_raw_event_store(dev, &rawir);
}
/* Fake a silence long enough to cause us to go idle */
struct loopback_dev *lodev = dev->priv;
if (lodev->idle != enable) {
- dprintk("%sing idle mode\n", enable ? "enter" : "exit");
+ dev_dbg(&dev->dev, "%sing idle mode\n", enable ? "enter" : "exit");
lodev->idle = enable;
}
}
-static int loop_set_learning_mode(struct rc_dev *dev, int enable)
+static int loop_set_wideband_receiver(struct rc_dev *dev, int enable)
{
struct loopback_dev *lodev = dev->priv;
- if (lodev->learning != enable) {
- dprintk("%sing learning mode\n", enable ? "enter" : "exit");
- lodev->learning = !!enable;
+ if (lodev->wideband != enable) {
+ dev_dbg(&dev->dev, "using %sband receiver\n", enable ? "wide" : "narrow");
+ lodev->wideband = !!enable;
}
return 0;
struct loopback_dev *lodev = dev->priv;
if (lodev->carrierreport != enable) {
- dprintk("%sabling carrier reports\n", enable ? "en" : "dis");
+ dev_dbg(&dev->dev, "%sabling carrier reports\n", enable ? "en" : "dis");
lodev->carrierreport = !!enable;
}
int ret;
rc = rc_allocate_device(RC_DRIVER_IR_RAW);
- if (!rc) {
- printk(KERN_ERR DRIVER_NAME ": rc_dev allocation failed\n");
+ if (!rc)
return -ENOMEM;
- }
rc->device_name = "rc-core loopback device";
rc->input_phys = "rc-core/virtual";
rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
rc->allowed_wakeup_protocols = RC_PROTO_BIT_ALL_IR_ENCODER;
rc->encode_wakeup = true;
- rc->timeout = MS_TO_US(100); /* 100 ms */
+ rc->timeout = IR_DEFAULT_TIMEOUT;
rc->min_timeout = 1;
- rc->max_timeout = UINT_MAX;
+ rc->max_timeout = IR_MAX_TIMEOUT;
rc->rx_resolution = 1;
rc->tx_resolution = 1;
rc->s_tx_mask = loop_set_tx_mask;
rc->s_rx_carrier_range = loop_set_rx_carrier_range;
rc->tx_ir = loop_tx_ir;
rc->s_idle = loop_set_idle;
- rc->s_learning_mode = loop_set_learning_mode;
+ rc->s_wideband_receiver = loop_set_wideband_receiver;
rc->s_carrier_report = loop_set_carrier_report;
rc->s_wakeup_filter = loop_set_wakeup_filter;
- loopdev.txmask = RXMASK_REGULAR;
+ loopdev.txmask = RXMASK_NARROWBAND;
loopdev.txcarrier = 36000;
loopdev.txduty = 50;
loopdev.rxcarriermin = 1;
loopdev.rxcarriermax = ~0;
loopdev.idle = true;
- loopdev.learning = false;
+ loopdev.wideband = false;
loopdev.carrierreport = false;
ret = rc_register_device(rc);
if (ret < 0) {
- printk(KERN_ERR DRIVER_NAME ": rc_dev registration failed\n");
+ dev_err(&rc->dev, "rc_dev registration failed\n");
rc_free_device(rc);
return ret;
}
module_init(loop_init);
module_exit(loop_exit);
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Enable debug messages");
-
MODULE_DESCRIPTION("Loopback device for rc-core debugging");
MODULE_AUTHOR("David Härdeman <david@hardeman.nu>");
MODULE_LICENSE("GPL");
kfree(path);
/*
- * once the the input device is registered in rc_setup_rx_device,
+ * once the input device is registered in rc_setup_rx_device,
* userspace can open the input device and rc_open() will be called
* as a result. This results in driver code being allowed to submit
* keycodes with rc_keydown, so lirc must be registered first.
* based heavily on the work of Stephen Cox, with additional
* help from RedRat Ltd.
*
- * This driver began life based an an old version of the first-generation
+ * This driver began life based on an old version of the first-generation
* lirc_mceusb driver from the lirc 0.7.2 distribution. It was then
* significantly rewritten by Stephen Cox with the aid of RedRat Ltd's
* Chris Dodge.
ir_raw_event_handle(sz->rdev);
usb_submit_urb(urb, GFP_ATOMIC);
-
- return;
}
static struct rc_dev *streamzap_init_rc_dev(struct streamzap_ir *sz)
if (IS_ERR(dvb_spi->vcc_supply)) {
if (PTR_ERR(dvb_spi->vcc_supply) == -EPROBE_DEFER) {
ret = -EPROBE_DEFER;
- goto fail_adapter;
+ goto fail_regulator;
}
dvb_spi->vcc_supply = NULL;
} else {
ret = regulator_enable(dvb_spi->vcc_supply);
if (ret)
- goto fail_adapter;
+ goto fail_regulator;
}
dvb_spi->spi = spi;
fail_attach:
dvb_unregister_adapter(&dvb_spi->adapter);
fail_adapter:
+ if (!dvb_spi->vcc_supply)
+ regulator_disable(dvb_spi->vcc_supply);
+fail_regulator:
kfree(dvb_spi);
return ret;
}
snprintf(name, sizeof(name), "vivid-%03d-vid-%s%d",
dev->inst, is_source ? "out" : "cap", idx);
return cec_allocate_adapter(&vivid_cec_adap_ops, dev,
- name, caps, 1);
+ name, caps, CEC_MAX_LOG_ADDRS);
}
tristate "Afatech AF9035 DVB-T USB2.0 support"
depends on DVB_USB_V2
select DVB_AF9033
+ select DVB_SI2168 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_TUA9001 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_FC0011 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT
/* register I2C device */
client = i2c_new_client_device(adapter, &board_info);
if (!i2c_client_has_driver(client)) {
+ dev_err(&intf->dev, "failed to bind i2c device to %s driver\n", type);
ret = -ENODEV;
goto err;
}
si2168_config.i2c_adapter = &i2c_adapter;
si2168_config.fe = &adap->fe[0];
si2168_config.ts_mode = SI2168_TS_PARALLEL;
- if (le16_to_cpu(d->udev->descriptor.idProduct) == USB_PID_MYGICA_T230C2)
+ if (le16_to_cpu(d->udev->descriptor.idProduct) == USB_PID_MYGICA_T230C2 ||
+ le16_to_cpu(d->udev->descriptor.idProduct) == USB_PID_MYGICA_T230C2_LITE ||
+ le16_to_cpu(d->udev->descriptor.idProduct) == USB_PID_MYGICA_T230A)
si2168_config.ts_mode |= SI2168_TS_CLK_MANUAL;
si2168_config.ts_clock_inv = 1;
static int dvbsky_identify_state(struct dvb_usb_device *d, const char **name)
{
- dvbsky_gpio_ctrl(d, 0x04, 1);
- msleep(20);
- dvbsky_gpio_ctrl(d, 0x83, 0);
- dvbsky_gpio_ctrl(d, 0xc0, 1);
- msleep(100);
- dvbsky_gpio_ctrl(d, 0x83, 1);
- dvbsky_gpio_ctrl(d, 0xc0, 0);
- msleep(50);
-
+ if (le16_to_cpu(d->udev->descriptor.idProduct) == USB_PID_MYGICA_T230A) {
+ dvbsky_gpio_ctrl(d, 0x87, 0);
+ msleep(20);
+ dvbsky_gpio_ctrl(d, 0x86, 1);
+ dvbsky_gpio_ctrl(d, 0x80, 0);
+ msleep(100);
+ dvbsky_gpio_ctrl(d, 0x80, 1);
+ msleep(50);
+ } else {
+ dvbsky_gpio_ctrl(d, 0x04, 1);
+ msleep(20);
+ dvbsky_gpio_ctrl(d, 0x83, 0);
+ dvbsky_gpio_ctrl(d, 0xc0, 1);
+ msleep(100);
+ dvbsky_gpio_ctrl(d, 0x83, 1);
+ dvbsky_gpio_ctrl(d, 0xc0, 0);
+ msleep(50);
+ }
return WARM;
}
{ DVB_USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230C2,
&mygica_t230c_props, "MyGica Mini DVB-(T/T2/C) USB Stick T230C v2",
RC_MAP_TOTAL_MEDIA_IN_HAND_02) },
+ { DVB_USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230C2_LITE,
+ &mygica_t230c_props, "MyGica Mini DVB-(T/T2/C) USB Stick T230C v2 Lite",
+ NULL) },
+ { DVB_USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230A,
+ &mygica_t230c_props, "MyGica Mini DVB-(T/T2/C) USB Stick T230A",
+ NULL) },
{ }
};
MODULE_DEVICE_TABLE(usb, dvbsky_id_table);
if (d->props.i2c_algo == NULL) {
err("no i2c algorithm specified");
- return -EINVAL;
+ ret = -EINVAL;
+ goto err;
}
strscpy(d->i2c_adap.name, d->desc->name, sizeof(d->i2c_adap.name));
i2c_set_adapdata(&d->i2c_adap, d);
- if ((ret = i2c_add_adapter(&d->i2c_adap)) < 0)
+ ret = i2c_add_adapter(&d->i2c_adap);
+ if (ret < 0) {
err("could not add i2c adapter");
+ goto err;
+ }
d->state |= DVB_USB_STATE_I2C;
+err:
return ret;
}
err_adapter_init:
dvb_usb_adapter_exit(d);
-err_i2c_init:
dvb_usb_i2c_exit(d);
+err_i2c_init:
if (d->priv && d->props.priv_destroy)
d->props.priv_destroy(d);
err_priv_init:
static int nova_t_read_mac_address (struct dvb_usb_device *d, u8 mac[6])
{
- int i;
+ int i, ret;
u8 b;
mac[0] = 0x00;
/* this is a complete guess, but works for my box */
for (i = 136; i < 139; i++) {
- dibusb_read_eeprom_byte(d,i, &b);
+ ret = dibusb_read_eeprom_byte(d, i, &b);
+ if (ret)
+ return ret;
mac[5 - (i - 136)] = b;
}
static int vp702x_read_mac_addr(struct dvb_usb_device *d,u8 mac[6])
{
u8 i, *buf;
+ int ret;
struct vp702x_device_state *st = d->priv;
mutex_lock(&st->buf_mutex);
buf = st->buf;
- for (i = 6; i < 12; i++)
- vp702x_usb_in_op(d, READ_EEPROM_REQ, i, 1, &buf[i - 6], 1);
+ for (i = 6; i < 12; i++) {
+ ret = vp702x_usb_in_op(d, READ_EEPROM_REQ, i, 1,
+ &buf[i - 6], 1);
+ if (ret < 0)
+ goto err;
+ }
memcpy(mac, buf, 6);
+err:
mutex_unlock(&st->buf_mutex);
- return 0;
+ return ret;
}
static int vp702x_frontend_attach(struct dvb_usb_adapter *adap)
kfree(ir);
ref_put:
em28xx_shutdown_buttons(dev);
- kref_put(&dev->ref, em28xx_free_device);
return err;
}
struct device *dev)
{
struct go7007 *go;
- int i;
go = kzalloc(sizeof(struct go7007), GFP_KERNEL);
if (go == NULL)
return NULL;
go->dev = dev;
go->board_info = board;
- go->board_id = 0;
go->tuner_type = -1;
- go->channel_number = 0;
- go->name[0] = 0;
mutex_init(&go->hw_lock);
init_waitqueue_head(&go->frame_waitq);
spin_lock_init(&go->spinlock);
go->status = STATUS_INIT;
- memset(&go->i2c_adapter, 0, sizeof(go->i2c_adapter));
- go->i2c_adapter_online = 0;
- go->interrupt_available = 0;
init_waitqueue_head(&go->interrupt_waitq);
- go->input = 0;
go7007_update_board(go);
- go->encoder_h_halve = 0;
- go->encoder_v_halve = 0;
- go->encoder_subsample = 0;
go->format = V4L2_PIX_FMT_MJPEG;
go->bitrate = 1500000;
go->fps_scale = 1;
- go->pali = 0;
go->aspect_ratio = GO7007_RATIO_1_1;
- go->gop_size = 0;
- go->ipb = 0;
- go->closed_gop = 0;
- go->repeat_seqhead = 0;
- go->seq_header_enable = 0;
- go->gop_header_enable = 0;
- go->dvd_mode = 0;
- go->interlace_coding = 0;
- for (i = 0; i < 4; ++i)
- go->modet[i].enable = 0;
- for (i = 0; i < 1624; ++i)
- go->modet_map[i] = 0;
- go->audio_deliver = NULL;
- go->audio_enabled = 0;
return go;
}
ep = usb->usbdev->ep_in[4];
if (!ep)
- return -ENODEV;
+ goto allocfail;
/* Allocate the URB and buffer for receiving incoming interrupts */
usb->intr_urb = usb_alloc_urb(0, GFP_KERNEL);
depends on VIDEO_V4L2
depends on INPUT || INPUT=n
select VIDEOBUF2_VMALLOC
- default m
help
Say Y here if you want to enable selecting webcams based
on the GSPCA framework.
{}
};
-static const u8 ov7660_NoFliker[][4] = {
+static const u8 ov7660_NoFlicker[][4] = {
{0x00, 0x13, 0x87, 0xaa},
{}
};
static void setlightfreq(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
- static const u8 (*ov7660_freq_tb[3])[4] =
- {ov7660_NoFliker, ov7660_50HZ, ov7660_60HZ};
+ static const u8 (*ov7660_freq_tb[3])[4] = {
+ ov7660_NoFlicker, ov7660_50HZ, ov7660_60HZ};
if (sd->sensor != SENSOR_OV7660)
return;
{0xaa, 0x28, 0x0002}, /* 00,28,02,aa */
{}
};
-static const struct usb_action adcm2700_NoFliker[] = {
+static const struct usb_action adcm2700_NoFlicker[] = {
{0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
{0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */
{0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
{0xa0, 0xff, ZC3XX_R020_HSYNC_3},
{}
};
-static const struct usb_action cs2102_NoFlikerScale[] = {
+static const struct usb_action cs2102_NoFlickerScale[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
{0xaa, 0x23, 0x0001},
{0xaa, 0x24, 0x005f},
{0xa0, 0xff, ZC3XX_R020_HSYNC_3},
{}
};
-static const struct usb_action cs2102_NoFliker[] = {
+static const struct usb_action cs2102_NoFlicker[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
{0xaa, 0x23, 0x0000},
{0xaa, 0x24, 0x00af},
{}
};
-static const struct usb_action gc0305_NoFliker[] = {
+static const struct usb_action gc0305_NoFlicker[] = {
{0xa0, 0x0c, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0c,cc */
{0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
{0xaa, 0x83, 0x0000}, /* 00,83,00,aa */
{0xa0, 0x2c, ZC3XX_R01F_HSYNC_2}, /* 00,1f,2c,cc */
{}
};
-static const struct usb_action hdcs2020_NoFliker[] = {
+static const struct usb_action hdcs2020_NoFlicker[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
{0xaa, 0x13, 0x0010}, /* 00,13,10,aa */
{0xaa, 0x14, 0x0001}, /* 00,14,01,aa */
{0xa0, 0x40, ZC3XX_R020_HSYNC_3}, /* 00,20,40,cc */
{}
};
-static const struct usb_action hv7131b_NoFliker[] = { /* 640x480*/
+static const struct usb_action hv7131b_NoFlicker[] = { /* 640x480*/
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
{0xaa, 0x25, 0x0003}, /* 00,25,03,aa */
{0xaa, 0x26, 0x0000}, /* 00,26,00,aa */
{0xa0, 0x03, ZC3XX_R020_HSYNC_3}, /* 00,20,03,cc */
{}
};
-static const struct usb_action hv7131b_NoFlikerScale[] = { /* 320x240 */
+static const struct usb_action hv7131b_NoFlickerScale[] = { /* 320x240 */
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
{0xaa, 0x25, 0x0003}, /* 00,25,03,aa */
{0xaa, 0x26, 0x0000}, /* 00,26,00,aa */
{0xa0, 0x08, ZC3XX_R020_HSYNC_3},
{}
};
-static const struct usb_action hv7131r_NoFliker[] = {
+static const struct usb_action hv7131r_NoFlicker[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
{0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH},
{0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID},
{0xa0, 0x08, ZC3XX_R020_HSYNC_3},
{}
};
-static const struct usb_action hv7131r_NoFlikerScale[] = {
+static const struct usb_action hv7131r_NoFlickerScale[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
{0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH},
{0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID},
{0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,c0,cc */
{}
};
-static const struct usb_action icm105a_NoFlikerScale[] = {
+static const struct usb_action icm105a_NoFlickerScale[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
{0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
{0xaa, 0x0c, 0x0004}, /* 00,0c,04,aa */
{0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
{}
};
-static const struct usb_action icm105a_NoFliker[] = {
+static const struct usb_action icm105a_NoFlicker[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
{0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
{0xaa, 0x0c, 0x0004}, /* 00,0c,04,aa */
{}
};
-static const struct usb_action mc501cb_NoFliker[] = {
+static const struct usb_action mc501cb_NoFlicker[] = {
{0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
{0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
{0xaa, 0x36, 0x0018}, /* 00,36,18,aa */
{}
};
-static const struct usb_action mc501cb_NoFlikerScale[] = {
+static const struct usb_action mc501cb_NoFlickerScale[] = {
{0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
{0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
{0xaa, 0x36, 0x0030}, /* 00,36,30,aa */
{0xa1, 0x01, 0x0037}, */
{}
};
-static const struct usb_action ov7620_NoFliker[] = {
+static const struct usb_action ov7620_NoFlicker[] = {
{0xdd, 0x00, 0x0100}, /* 00,01,00,dd */
{0xaa, 0x2b, 0x0000}, /* 00,2b,00,aa */
/* disable 1/120s & 1/100s exposures for banding filter */
{0xa0, 0x04, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,04,cc */
{}
};
-static const struct usb_action pas106b_NoFliker[] = {
+static const struct usb_action pas106b_NoFlicker[] = {
{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
{0xa0, 0x06, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,06,cc */
{0xa0, 0x50, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,50,cc */
{0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
{}
};
-static const struct usb_action pas202b_NoFliker[] = {
+static const struct usb_action pas202b_NoFlicker[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
{0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
{0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
{0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
{}
};
-static const struct usb_action pas202b_NoFlikerScale[] = {
+static const struct usb_action pas202b_NoFlickerScale[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
{0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
{0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
{0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
{}
};
-static const struct usb_action mt9v111_1_AENoFliker[] = {
+static const struct usb_action mt9v111_1_AENoFlicker[] = {
{0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
{0xbb, 0x00, 0x0509},
{0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
{}
};
-static const struct usb_action mt9v111_1_AENoFlikerScale[] = {
+static const struct usb_action mt9v111_1_AENoFlickerScale[] = {
{0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
{0xbb, 0x00, 0x0534},
{0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
{}
};
-static const struct usb_action mt9v111_3_AENoFliker[] = {
+static const struct usb_action mt9v111_3_AENoFlicker[] = {
{0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
{0xaa, 0x05, 0x0034},
{0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
{}
};
-static const struct usb_action mt9v111_3_AENoFlikerScale[] = {
+static const struct usb_action mt9v111_3_AENoFlickerScale[] = {
{0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
{0xaa, 0x05, 0x0034},
{0xa0, 0xd0, ZC3XX_R020_HSYNC_3},
{}
};
-static const struct usb_action pb0330_NoFliker[] = {
+static const struct usb_action pb0330_NoFlicker[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
{0xbb, 0x00, 0x0509},
{0xbb, 0x02, 0x0940},
{0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
{}
};
-static const struct usb_action pb0330_NoFlikerScale[] = {
+static const struct usb_action pb0330_NoFlickerScale[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
{0xbb, 0x00, 0x0535},
{0xbb, 0x01, 0x0980},
{}
};
-static const struct usb_action po2030_NoFliker[] = {
+static const struct usb_action po2030_NoFlicker[] = {
{0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
{0xaa, 0x8d, 0x000d}, /* 00,8d,0d,aa */
{0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */
{0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
{}
};
-static const struct usb_action tas5130c_NoFliker[] = {
+static const struct usb_action tas5130c_NoFlicker[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
{0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
{0xaa, 0xa4, 0x0040}, /* 00,a4,40,aa */
{}
};
-static const struct usb_action tas5130c_NoFlikerScale[] = {
+static const struct usb_action tas5130c_NoFlickerScale[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
{0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
{0xaa, 0xa4, 0x0090}, /* 00,a4,90,aa */
{}
};
-static const struct usb_action gc0303_NoFliker[] = {
+static const struct usb_action gc0303_NoFlicker[] = {
{0xa0, 0x0c, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0c,cc, */
{0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
{0xaa, 0x83, 0x0000}, /* 00,83,00,aa */
{}
};
-static const struct usb_action gc0303_NoFlikerScale[] = {
+static const struct usb_action gc0303_NoFlickerScale[] = {
{0xa0, 0x0c, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0c,cc, */
{0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
{0xaa, 0x83, 0x0000}, /* 00,83,00,aa */
* Valid frequencies are:
* 50Hz, for European and Asian lighting (default)
* 60Hz, for American lighting
- * 0 = No Fliker (for outdoore usage)
+ * 0 = No Flicker (for outdoor usage)
*/
static void setlightfreq(struct gspca_dev *gspca_dev, s32 val)
{
int i, mode;
const struct usb_action *zc3_freq;
static const struct usb_action *freq_tb[SENSOR_MAX][6] = {
- [SENSOR_ADCM2700] =
- {adcm2700_NoFliker, adcm2700_NoFliker,
+ [SENSOR_ADCM2700] = {
+ adcm2700_NoFlicker, adcm2700_NoFlicker,
adcm2700_50HZ, adcm2700_50HZ,
adcm2700_60HZ, adcm2700_60HZ},
- [SENSOR_CS2102] =
- {cs2102_NoFliker, cs2102_NoFlikerScale,
+ [SENSOR_CS2102] = {
+ cs2102_NoFlicker, cs2102_NoFlickerScale,
cs2102_50HZ, cs2102_50HZScale,
cs2102_60HZ, cs2102_60HZScale},
- [SENSOR_CS2102K] =
- {cs2102_NoFliker, cs2102_NoFlikerScale,
+ [SENSOR_CS2102K] = {
+ cs2102_NoFlicker, cs2102_NoFlickerScale,
NULL, NULL, /* currently disabled */
NULL, NULL},
- [SENSOR_GC0303] =
- {gc0303_NoFliker, gc0303_NoFlikerScale,
+ [SENSOR_GC0303] = {
+ gc0303_NoFlicker, gc0303_NoFlickerScale,
gc0303_50HZ, gc0303_50HZScale,
gc0303_60HZ, gc0303_60HZScale},
- [SENSOR_GC0305] =
- {gc0305_NoFliker, gc0305_NoFliker,
+ [SENSOR_GC0305] = {
+ gc0305_NoFlicker, gc0305_NoFlicker,
gc0305_50HZ, gc0305_50HZ,
gc0305_60HZ, gc0305_60HZ},
- [SENSOR_HDCS2020] =
- {hdcs2020_NoFliker, hdcs2020_NoFliker,
+ [SENSOR_HDCS2020] = {
+ hdcs2020_NoFlicker, hdcs2020_NoFlicker,
hdcs2020_50HZ, hdcs2020_50HZ,
hdcs2020_60HZ, hdcs2020_60HZ},
- [SENSOR_HV7131B] =
- {hv7131b_NoFliker, hv7131b_NoFlikerScale,
+ [SENSOR_HV7131B] = {
+ hv7131b_NoFlicker, hv7131b_NoFlickerScale,
hv7131b_50HZ, hv7131b_50HZScale,
hv7131b_60HZ, hv7131b_60HZScale},
- [SENSOR_HV7131R] =
- {hv7131r_NoFliker, hv7131r_NoFlikerScale,
+ [SENSOR_HV7131R] = {
+ hv7131r_NoFlicker, hv7131r_NoFlickerScale,
hv7131r_50HZ, hv7131r_50HZScale,
hv7131r_60HZ, hv7131r_60HZScale},
- [SENSOR_ICM105A] =
- {icm105a_NoFliker, icm105a_NoFlikerScale,
+ [SENSOR_ICM105A] = {
+ icm105a_NoFlicker, icm105a_NoFlickerScale,
icm105a_50HZ, icm105a_50HZScale,
icm105a_60HZ, icm105a_60HZScale},
- [SENSOR_MC501CB] =
- {mc501cb_NoFliker, mc501cb_NoFlikerScale,
+ [SENSOR_MC501CB] = {
+ mc501cb_NoFlicker, mc501cb_NoFlickerScale,
mc501cb_50HZ, mc501cb_50HZScale,
mc501cb_60HZ, mc501cb_60HZScale},
- [SENSOR_MT9V111_1] =
- {mt9v111_1_AENoFliker, mt9v111_1_AENoFlikerScale,
+ [SENSOR_MT9V111_1] = {
+ mt9v111_1_AENoFlicker, mt9v111_1_AENoFlickerScale,
mt9v111_1_AE50HZ, mt9v111_1_AE50HZScale,
mt9v111_1_AE60HZ, mt9v111_1_AE60HZScale},
- [SENSOR_MT9V111_3] =
- {mt9v111_3_AENoFliker, mt9v111_3_AENoFlikerScale,
+ [SENSOR_MT9V111_3] = {
+ mt9v111_3_AENoFlicker, mt9v111_3_AENoFlickerScale,
mt9v111_3_AE50HZ, mt9v111_3_AE50HZScale,
mt9v111_3_AE60HZ, mt9v111_3_AE60HZScale},
- [SENSOR_OV7620] =
- {ov7620_NoFliker, ov7620_NoFliker,
+ [SENSOR_OV7620] = {
+ ov7620_NoFlicker, ov7620_NoFlicker,
ov7620_50HZ, ov7620_50HZ,
ov7620_60HZ, ov7620_60HZ},
- [SENSOR_OV7630C] =
- {NULL, NULL,
+ [SENSOR_OV7630C] = {
+ NULL, NULL,
NULL, NULL,
NULL, NULL},
- [SENSOR_PAS106] =
- {pas106b_NoFliker, pas106b_NoFliker,
+ [SENSOR_PAS106] = {
+ pas106b_NoFlicker, pas106b_NoFlicker,
pas106b_50HZ, pas106b_50HZ,
pas106b_60HZ, pas106b_60HZ},
- [SENSOR_PAS202B] =
- {pas202b_NoFliker, pas202b_NoFlikerScale,
+ [SENSOR_PAS202B] = {
+ pas202b_NoFlicker, pas202b_NoFlickerScale,
pas202b_50HZ, pas202b_50HZScale,
pas202b_60HZ, pas202b_60HZScale},
- [SENSOR_PB0330] =
- {pb0330_NoFliker, pb0330_NoFlikerScale,
+ [SENSOR_PB0330] = {
+ pb0330_NoFlicker, pb0330_NoFlickerScale,
pb0330_50HZ, pb0330_50HZScale,
pb0330_60HZ, pb0330_60HZScale},
- [SENSOR_PO2030] =
- {po2030_NoFliker, po2030_NoFliker,
+ [SENSOR_PO2030] = {
+ po2030_NoFlicker, po2030_NoFlicker,
po2030_50HZ, po2030_50HZ,
po2030_60HZ, po2030_60HZ},
- [SENSOR_TAS5130C] =
- {tas5130c_NoFliker, tas5130c_NoFlikerScale,
+ [SENSOR_TAS5130C] = {
+ tas5130c_NoFlicker, tas5130c_NoFlickerScale,
tas5130c_50HZ, tas5130c_50HZScale,
tas5130c_60HZ, tas5130c_60HZScale},
};
if (!dev->isoc_ep) {
pr_err("Could not find isoc-in endpoint\n");
err = -ENODEV;
- goto error;
+ goto error_put;
}
dev->vsettings.palette = V4L2_PIX_FMT_RGB565;
dev->vsettings.mode = MODE_VGA;
err = stk_register_video_device(dev);
if (err)
- goto error;
+ goto error_put;
return 0;
+error_put:
+ usb_put_intf(interface);
error:
v4l2_ctrl_handler_free(hdl);
v4l2_device_unregister(&dev->v4l2_dev);
{
struct uvc_fh *handle = fh;
struct uvc_video_chain *chain = handle->chain;
+ u8 *buf;
int ret;
- u8 i;
if (chain->selector == NULL ||
(chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
return 0;
}
+ buf = kmalloc(1, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, chain->selector->id,
chain->dev->intfnum, UVC_SU_INPUT_SELECT_CONTROL,
- &i, 1);
- if (ret < 0)
- return ret;
+ buf, 1);
+ if (!ret)
+ *input = *buf - 1;
- *input = i - 1;
- return 0;
+ kfree(buf);
+
+ return ret;
}
static int uvc_ioctl_s_input(struct file *file, void *fh, unsigned int input)
{
struct uvc_fh *handle = fh;
struct uvc_video_chain *chain = handle->chain;
+ u8 *buf;
int ret;
- u32 i;
ret = uvc_acquire_privileges(handle);
if (ret < 0)
if (input >= chain->selector->bNrInPins)
return -EINVAL;
- i = input + 1;
- return uvc_query_ctrl(chain->dev, UVC_SET_CUR, chain->selector->id,
- chain->dev->intfnum, UVC_SU_INPUT_SELECT_CONTROL,
- &i, 1);
+ buf = kmalloc(1, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ *buf = input + 1;
+ ret = uvc_query_ctrl(chain->dev, UVC_SET_CUR, chain->selector->id,
+ chain->dev->intfnum, UVC_SU_INPUT_SELECT_CONTROL,
+ buf, 1);
+ kfree(buf);
+
+ return ret;
}
static int uvc_ioctl_queryctrl(struct file *file, void *fh,
case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE: return "Decoder Slice Interface";
case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER: return "MPEG4 Loop Filter Enable";
case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB: return "Number of Intra Refresh MBs";
+ case V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD: return "Intra Refresh Period";
case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: return "Frame Level Rate Control Enable";
case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE: return "H264 MB Level Rate Control";
case V4L2_CID_MPEG_VIDEO_HEADER_MODE: return "Sequence Header Mode";
case V4L2_CID_MPEG_VIDEO_MV_H_SEARCH_RANGE:
case V4L2_CID_MPEG_VIDEO_MV_V_SEARCH_RANGE:
case V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY:
+ case V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD:
*type = V4L2_CTRL_TYPE_INTEGER;
break;
case V4L2_CID_MPEG_VIDEO_LTR_COUNT:
if (!v4l2_valid_dv_timings(t, cap, fnc, fnc_handle))
return false;
- for (i = 0; i < v4l2_dv_timings_presets[i].bt.width; i++) {
+ for (i = 0; v4l2_dv_timings_presets[i].bt.width; i++) {
if (v4l2_valid_dv_timings(v4l2_dv_timings_presets + i, cap,
fnc, fnc_handle) &&
v4l2_match_dv_timings(t, v4l2_dv_timings_presets + i,
{
unsigned int i;
- for (i = 0; i < v4l2_dv_timings_presets[i].bt.width; i++) {
+ for (i = 0; v4l2_dv_timings_presets[i].bt.width; i++) {
const struct v4l2_bt_timings *bt =
&v4l2_dv_timings_presets[i].bt;
return (brightness * ctrl->step) + ctrl->minimum;
}
-static void v4l2_flash_set_led_brightness(struct v4l2_flash *v4l2_flash,
- struct v4l2_ctrl *ctrl)
+static int v4l2_flash_set_led_brightness(struct v4l2_flash *v4l2_flash,
+ struct v4l2_ctrl *ctrl)
{
struct v4l2_ctrl **ctrls = v4l2_flash->ctrls;
+ struct led_classdev *led_cdev;
enum led_brightness brightness;
if (has_flash_op(v4l2_flash, intensity_to_led_brightness))
if (ctrl == ctrls[TORCH_INTENSITY]) {
if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH)
- return;
+ return 0;
- led_set_brightness_sync(&v4l2_flash->fled_cdev->led_cdev,
- brightness);
+ if (WARN_ON_ONCE(!v4l2_flash->fled_cdev))
+ return -EINVAL;
+
+ led_cdev = &v4l2_flash->fled_cdev->led_cdev;
} else {
- led_set_brightness_sync(v4l2_flash->iled_cdev,
- brightness);
+ if (WARN_ON_ONCE(!v4l2_flash->iled_cdev))
+ return -EINVAL;
+
+ led_cdev = v4l2_flash->iled_cdev;
}
+
+ return led_set_brightness_sync(led_cdev, brightness);
}
static int v4l2_flash_update_led_brightness(struct v4l2_flash *v4l2_flash,
*/
if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH)
return 0;
+
+ if (WARN_ON_ONCE(!v4l2_flash->fled_cdev))
+ return -EINVAL;
+
led_cdev = &v4l2_flash->fled_cdev->led_cdev;
} else {
+ if (WARN_ON_ONCE(!v4l2_flash->iled_cdev))
+ return -EINVAL;
+
led_cdev = v4l2_flash->iled_cdev;
}
case V4L2_CID_FLASH_TORCH_INTENSITY:
case V4L2_CID_FLASH_INDICATOR_INTENSITY:
return v4l2_flash_update_led_brightness(v4l2_flash, c);
+ }
+
+ if (!fled_cdev)
+ return -EINVAL;
+
+ switch (c->id) {
case V4L2_CID_FLASH_INTENSITY:
ret = led_update_flash_brightness(fled_cdev);
if (ret < 0)
{
struct v4l2_flash *v4l2_flash = v4l2_ctrl_to_v4l2_flash(c);
struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev;
- struct led_classdev *led_cdev = fled_cdev ? &fled_cdev->led_cdev : NULL;
+ struct led_classdev *led_cdev;
struct v4l2_ctrl **ctrls = v4l2_flash->ctrls;
bool external_strobe;
int ret = 0;
switch (c->id) {
+ case V4L2_CID_FLASH_TORCH_INTENSITY:
+ case V4L2_CID_FLASH_INDICATOR_INTENSITY:
+ return v4l2_flash_set_led_brightness(v4l2_flash, c);
+ }
+
+ if (!fled_cdev)
+ return -EINVAL;
+
+ led_cdev = &fled_cdev->led_cdev;
+
+ switch (c->id) {
case V4L2_CID_FLASH_LED_MODE:
switch (c->val) {
case V4L2_FLASH_LED_MODE_NONE:
if (ret < 0)
return ret;
- v4l2_flash_set_led_brightness(v4l2_flash,
- ctrls[TORCH_INTENSITY]);
- return 0;
+ return v4l2_flash_set_led_brightness(v4l2_flash,
+ ctrls[TORCH_INTENSITY]);
}
break;
case V4L2_CID_FLASH_STROBE_SOURCE:
* microamperes for flash intensity units.
*/
return led_set_flash_brightness(fled_cdev, c->val);
- case V4L2_CID_FLASH_TORCH_INTENSITY:
- case V4L2_CID_FLASH_INDICATOR_INTENSITY:
- v4l2_flash_set_led_brightness(v4l2_flash, c);
- return 0;
}
return -EINVAL;
struct v4l2_ctrl **ctrls = v4l2_flash->ctrls;
int ret = 0;
- if (ctrls[TORCH_INTENSITY])
- v4l2_flash_set_led_brightness(v4l2_flash,
- ctrls[TORCH_INTENSITY]);
+ if (ctrls[TORCH_INTENSITY]) {
+ ret = v4l2_flash_set_led_brightness(v4l2_flash,
+ ctrls[TORCH_INTENSITY]);
+ if (ret < 0)
+ return ret;
+ }
- if (ctrls[INDICATOR_INTENSITY])
- v4l2_flash_set_led_brightness(v4l2_flash,
- ctrls[INDICATOR_INTENSITY]);
+ if (ctrls[INDICATOR_INTENSITY]) {
+ ret = v4l2_flash_set_led_brightness(v4l2_flash,
+ ctrls[INDICATOR_INTENSITY]);
+ if (ret < 0)
+ return ret;
+ }
if (ctrls[FLASH_TIMEOUT]) {
+ if (WARN_ON_ONCE(!fled_cdev))
+ return -EINVAL;
+
ret = led_set_flash_timeout(fled_cdev,
ctrls[FLASH_TIMEOUT]->val);
if (ret < 0)
}
if (ctrls[FLASH_INTENSITY]) {
+ if (WARN_ON_ONCE(!fled_cdev))
+ return -EINVAL;
+
ret = led_set_flash_brightness(fled_cdev,
ctrls[FLASH_INTENSITY]->val);
if (ret < 0)
static int gc2235_detect(struct i2c_client *client)
{
struct i2c_adapter *adapter = client->adapter;
- u16 high, low;
- int ret;
+ u16 high = 0, low = 0;
u16 id;
if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
return -ENODEV;
- ret = gc2235_read_reg(client, GC2235_8BIT,
- GC2235_SENSOR_ID_H, &high);
- if (ret) {
- dev_err(&client->dev, "sensor_id_high = 0x%x\n", high);
- return -ENODEV;
- }
- ret = gc2235_read_reg(client, GC2235_8BIT,
- GC2235_SENSOR_ID_L, &low);
+ gc2235_read_reg(client, GC2235_8BIT, GC2235_SENSOR_ID_H, &high);
+ gc2235_read_reg(client, GC2235_8BIT, GC2235_SENSOR_ID_L, &low);
id = ((high << 8) | low);
if (id != GC2235_ID) {
static int mt9m114_s_power(struct v4l2_subdev *sd, int power)
{
- if (power == 0) {
+ if (power == 0)
return power_down(sd);
- } else {
- if (power_up(sd))
- return -EINVAL;
- return mt9m114_init_common(sd);
- }
+ if (power_up(sd))
+ return -EINVAL;
+
+ return mt9m114_init_common(sd);
}
/*
static int mt9m114_detect(struct mt9m114_device *dev, struct i2c_client *client)
{
struct i2c_adapter *adapter = client->adapter;
- u32 retvalue;
+ u32 model;
+ int ret;
if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) {
dev_err(&client->dev, "%s: i2c error", __func__);
return -ENODEV;
}
- mt9m114_read_reg(client, MISENSOR_16BIT, (u32)MT9M114_PID, &retvalue);
- dev->real_model_id = retvalue;
+ ret = mt9m114_read_reg(client, MISENSOR_16BIT, MT9M114_PID, &model);
+ if (ret)
+ return ret;
+ dev->real_model_id = model;
- if (retvalue != MT9M114_MOD_ID) {
+ if (model != MT9M114_MOD_ID) {
dev_err(&client->dev, "%s: failed: client->addr = %x\n",
__func__, client->addr);
return -ENODEV;
{0x5706, 0x0c},
{0x5707, 0x78},
{0x3820, 0xc0},
- {0x3821, 0x00}, //miror/flip
+ {0x3821, 0x00}, //mirror/flip
// {0x5090, 0x0c},
{}
};
static int ad5823_t_focus_vcm(struct v4l2_subdev *sd, u16 val)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
- int ret = -EINVAL;
+ int ret;
u8 vcm_code;
ret = ad5823_i2c_read(client, AD5823_REG_VCM_CODE_MSB, &vcm_code);
void ia_css_mmu_invalidate_cache(void);
-void ia_css_mmu_invalidate_cache(void);
-
int atomisp_css_start(struct atomisp_sub_device *asd,
enum ia_css_pipe_id pipe_id, bool in_reset);
enum ia_css_pipe_id pipe_id, bool in_reset)
{
struct atomisp_device *isp = asd->isp;
- struct atomisp_s3a_buf *s3a_buf;
- struct atomisp_dis_buf *dis_buf;
- struct atomisp_metadata_buf *md_buf;
unsigned long irqflags;
unsigned int i;
}
/* move stats buffers to free queue list */
- while (!list_empty(&asd->s3a_stats_in_css)) {
- s3a_buf = list_entry(asd->s3a_stats_in_css.next,
- struct atomisp_s3a_buf, list);
- list_del(&s3a_buf->list);
- list_add_tail(&s3a_buf->list, &asd->s3a_stats);
- }
- while (!list_empty(&asd->s3a_stats_ready)) {
- s3a_buf = list_entry(asd->s3a_stats_ready.next,
- struct atomisp_s3a_buf, list);
- list_del(&s3a_buf->list);
- list_add_tail(&s3a_buf->list, &asd->s3a_stats);
- }
+ list_splice_init(&asd->s3a_stats_in_css, &asd->s3a_stats);
+ list_splice_init(&asd->s3a_stats_ready, &asd->s3a_stats);
spin_lock_irqsave(&asd->dis_stats_lock, irqflags);
- while (!list_empty(&asd->dis_stats_in_css)) {
- dis_buf = list_entry(asd->dis_stats_in_css.next,
- struct atomisp_dis_buf, list);
- list_del(&dis_buf->list);
- list_add_tail(&dis_buf->list, &asd->dis_stats);
- }
+ list_splice_init(&asd->dis_stats_in_css, &asd->dis_stats);
asd->params.dis_proj_data_valid = false;
spin_unlock_irqrestore(&asd->dis_stats_lock, irqflags);
for (i = 0; i < ATOMISP_METADATA_TYPE_NUM; i++) {
- while (!list_empty(&asd->metadata_in_css[i])) {
- md_buf = list_entry(asd->metadata_in_css[i].next,
- struct atomisp_metadata_buf, list);
- list_del(&md_buf->list);
- list_add_tail(&md_buf->list, &asd->metadata[i]);
- }
- while (!list_empty(&asd->metadata_ready[i])) {
- md_buf = list_entry(asd->metadata_ready[i].next,
- struct atomisp_metadata_buf, list);
- list_del(&md_buf->list);
- list_add_tail(&md_buf->list, &asd->metadata[i]);
- }
+ list_splice_init(&asd->metadata_in_css[i], &asd->metadata[i]);
+ list_splice_init(&asd->metadata_ready[i], &asd->metadata[i]);
}
atomisp_flush_params_queue(&asd->video_out_capture);
struct v4l2_subdev_state *sd_state,
enum
v4l2_subdev_format_whence
- which, unsigned int pad) {
+ which, unsigned int pad)
+{
if (which == V4L2_SUBDEV_FORMAT_TRY)
return v4l2_subdev_get_try_format(&csi2->subdev, sd_state,
pad);
static ssize_t iunit_dbglvl_show(struct device_driver *drv, char *buf)
{
iunit_debug.dbglvl = dbg_level;
- return sprintf(buf, "dtrace level:%u\n", iunit_debug.dbglvl);
+ return sysfs_emit(buf, "dtrace level:%u\n", iunit_debug.dbglvl);
}
static ssize_t iunit_dbglvl_store(struct device_driver *drv, const char *buf,
static ssize_t iunit_dbgfun_show(struct device_driver *drv, char *buf)
{
iunit_debug.dbgfun = atomisp_get_css_dbgfunc();
- return sprintf(buf, "dbgfun opt:%u\n", iunit_debug.dbgfun);
+ return sysfs_emit(buf, "dbgfun opt:%u\n", iunit_debug.dbgfun);
}
static ssize_t iunit_dbgfun_store(struct device_driver *drv, const char *buf,
static ssize_t iunit_dbgopt_show(struct device_driver *drv, char *buf)
{
- return sprintf(buf, "option:0x%x\n", iunit_debug.dbgopt);
+ return sysfs_emit(buf, "option:0x%x\n", iunit_debug.dbgopt);
}
static ssize_t iunit_dbgopt_store(struct device_driver *drv, const char *buf,
.get_vcm_ctrl = gmin_get_vcm_ctrl,
};
-struct camera_sensor_platform_data *gmin_camera_platform_data(
- struct v4l2_subdev *subdev,
- enum atomisp_input_format csi_format,
- enum atomisp_bayer_order csi_bayer)
+struct camera_sensor_platform_data *
+gmin_camera_platform_data(struct v4l2_subdev *subdev,
+ enum atomisp_input_format csi_format,
+ enum atomisp_bayer_order csi_bayer)
{
u8 pmic_i2c_addr = gmin_detect_pmic(subdev);
struct gmin_subdev *gs;
if (err < 0)
goto register_entities_fail;
/* init atomisp wdts */
- if (init_atomisp_wdts(isp) != 0)
+ err = init_atomisp_wdts(isp);
+ if (err != 0)
goto wdt_work_queue_fail;
/* save the iunit context only once after all the values are init'ed. */
hmm_cleanup();
hmm_pool_unregister(HMM_POOL_TYPE_RESERVED);
hmm_pool_fail:
+ pm_runtime_get_noresume(&pdev->dev);
destroy_workqueue(isp->wdt_work_queue);
wdt_work_queue_fail:
atomisp_acc_cleanup(isp);
ctrl_unit_get_state(ID, sub_id,
&state->ctrl_unit_state[sub_id - CTRL_UNIT0_ID]);
}
-
- return;
}
void receiver_get_state(
_HRT_CSS_RECEIVER_BE_IRQ_STATUS_REG_IDX);
state->be_irq_clear = receiver_reg_load(ID,
_HRT_CSS_RECEIVER_BE_IRQ_CLEAR_REG_IDX);
-
- return;
}
bool is_mipi_format_yuv420(
reg = ((field_id < 6) ? (val << (field_id * 5)) : (val << ((
field_id - 6) * 5)));
receiver_reg_store(ID, addr, reg);
-
- return;
}
void receiver_port_enable(
receiver_port_reg_store(ID, port_ID,
_HRT_CSS_RECEIVER_DEVICE_READY_REG_IDX, reg);
- return;
}
bool is_receiver_port_enabled(
{
receiver_port_reg_store(ID,
port_ID, _HRT_CSS_RECEIVER_IRQ_ENABLE_REG_IDX, irq_info);
- return;
}
rx_irq_info_t receiver_get_irq_info(
{
receiver_port_reg_store(ID,
port_ID, _HRT_CSS_RECEIVER_IRQ_STATUS_REG_IDX, irq_info);
- return;
}
static inline void capture_unit_get_state(
state->FSM_State_Info = input_system_sub_system_reg_load(ID,
sub_id,
CAPT_FSM_STATE_INFO_REG_ID);
-
- return;
}
static inline void acquisition_unit_get_state(
state->Int_Cntr_Info = input_system_sub_system_reg_load(ID,
sub_id,
ACQ_INT_CNTR_INFO_REG_ID);
-
- return;
}
static inline void ctrl_unit_get_state(
state->capt_reserve_one_mem_region = input_system_sub_system_reg_load(ID,
sub_id,
ISYS_CTRL_CAPT_RESERVE_ONE_MEM_REGION_REG_ID);
-
- return;
}
static inline void mipi_port_get_state(
state->lane_sync_count[i] = (uint8_t)((state->sync_count) >> (i * 8));
state->lane_rx_count[i] = (uint8_t)((state->rx_count) >> (i * 8));
}
-
- return;
}
static inline void rx_channel_get_state(
state->comp[i] = (mipi_compressor_t)(val & 0x07);
state->pred[i] = (mipi_predictor_t)((val & 0x18) >> 3);
}
-
- return;
}
// MW: "2400" in the name is not good, but this is to avoid a naming conflict
}
// AM: Additional actions for stopping receiver?
-
- return;
}
//Single function to reset all the devices mapped via GP_DEVICE.
// gp_device_reg_store(ID, _REG_GP_SYNCGEN_FRAME_CNT_ADDR, ZERO);
gp_device_reg_store(ID, _REG_GP_SOFT_RESET_ADDR,
ZERO); // AM: Maybe this soft reset is not safe.
-
- return;
}
static void input_selector_cfg_for_sensor(const gp_device_ID_t ID)
gp_device_reg_store(ID, _REG_GP_ISEL_SBAND_SEL_ADDR, ZERO);
gp_device_reg_store(ID, _REG_GP_ISEL_SYNC_SEL_ADDR, ZERO);
gp_device_reg_store(ID, _REG_GP_SOFT_RESET_ADDR, ZERO);
-
- return;
}
static void input_switch_rst(const gp_device_ID_t ID)
gp_device_reg_store(ID,
_REG_GP_IFMT_input_switch_fsync_lut,
ZERO);
-
- return;
}
static void input_switch_cfg(
gp_device_reg_store(ID,
_REG_GP_IFMT_input_switch_fsync_lut,
cfg->vsync_data_reg);
-
- return;
}
static void input_system_network_rst(const input_system_ID_t ID)
ISYS_CTRL_INIT_REG_ID,
1U); //AM: Is there any named constant?
}
-
- return;
}
// Function that resets current configuration.
error = input_system_configure_channel_sensor(channel);
break;
case INPUT_SYSTEM_SOURCE_TPG:
- return INPUT_SYSTEM_ERR_PARAMETER_NOT_SUPPORTED;
- break;
case INPUT_SYSTEM_SOURCE_PRBS:
- return INPUT_SYSTEM_ERR_PARAMETER_NOT_SUPPORTED;
- break;
case INPUT_SYSTEM_SOURCE_FIFO:
- return INPUT_SYSTEM_ERR_PARAMETER_NOT_SUPPORTED;
- break;
default:
return INPUT_SYSTEM_ERR_PARAMETER_NOT_SUPPORTED;
- break;
}
if (error != INPUT_SYSTEM_ERR_NO_ERROR) return error;
default:
config.csi_buffer_flags[port] |= INPUT_SYSTEM_CFG_FLAG_CONFLICT;
return INPUT_SYSTEM_ERR_PARAMETER_NOT_SUPPORTED;
- break;
}
// Check acquisition buffer specified but set it later since it has to be unique.
default:
return INPUT_SYSTEM_ERR_PARAMETER_NOT_SUPPORTED;
- break;
}
} else {
config.csi_buffer_flags[port] = INPUT_SYSTEM_CFG_FLAG_BLOCKED;
sub_id,
CAPT_NUM_MEM_REGIONS_REG_ID,
cfg->nof_mem_regs);
-
- return;
}
static void acquisition_unit_configure(
sub_id,
ACQ_MEM_REGION_SIZE_REG_ID,
cfg->mem_reg_size);
-
- return;
}
static void ctrl_unit_configure(
sub_id,
ISYS_CTRL_CAPT_RESERVE_ONE_MEM_REGION_REG_ID,
0);
- return;
}
static void input_system_network_configure(
sub_id,
&cfg->ctrl_unit_cfg[sub_id - CTRL_UNIT0_ID]);
}
-
- return;
}
static input_system_err_t configuration_to_registers(void)
break;
case INPUT_SYSTEM_SOURCE_TPG:
-
- break;
-
case INPUT_SYSTEM_SOURCE_PRBS:
-
- break;
-
case INPUT_SYSTEM_SOURCE_FIFO:
break;
default:
return INPUT_SYSTEM_ERR_PARAMETER_NOT_SUPPORTED;
- break;
} // end of switch (source_type)
break;
case INPUT_SYSTEM_FIFO_CAPTURE_WITH_COUNTING:
- return INPUT_SYSTEM_ERR_PARAMETER_NOT_SUPPORTED;
- break;
case INPUT_SYSTEM_XMEM_CAPTURE:
- return INPUT_SYSTEM_ERR_PARAMETER_NOT_SUPPORTED;
- break;
case INPUT_SYSTEM_XMEM_ACQUIRE:
- return INPUT_SYSTEM_ERR_PARAMETER_NOT_SUPPORTED;
- break;
default:
return INPUT_SYSTEM_ERR_PARAMETER_NOT_SUPPORTED;
- break;
}
- return INPUT_SYSTEM_ERR_NO_ERROR;
}
// Test flags and set structure.
// Number of registers
#define ISYS_CTRL_NOF_REGS 23
-// Register id's of MMIO slave accesible registers
+// Register id's of MMIO slave accessible registers
#define ISYS_CTRL_CAPT_START_ADDR_A_REG_ID 0
#define ISYS_CTRL_CAPT_START_ADDR_B_REG_ID 1
#define ISYS_CTRL_CAPT_START_ADDR_C_REG_ID 2
#define __IA_CSS_ANR_TYPES_H
/* @file
-* CSS-API header file for Advanced Noise Reduction kernel v1
-*/
+ * CSS-API header file for Advanced Noise Reduction kernel v1
+ */
/* Application specific DMA settings */
#define ANR_BPP 10
};
/* NOTE: The base has already an offset of 0x0100 */
-static const hrt_address MIPI_PORT_OFFSET[N_MIPI_PORT_ID] = {
+static const hrt_address __maybe_unused MIPI_PORT_OFFSET[N_MIPI_PORT_ID] = {
0x00000000UL,
0x00000100UL,
0x00000200UL
};
-static const mipi_lane_cfg_t MIPI_PORT_MAXLANES[N_MIPI_PORT_ID] = {
- MIPI_4LANE_CFG,
- MIPI_1LANE_CFG,
- MIPI_2LANE_CFG
-};
-
-static const bool MIPI_PORT_ACTIVE[N_RX_MODE][N_MIPI_PORT_ID] = {
- {true, true, false},
- {true, true, false},
- {true, true, false},
- {true, true, false},
- {true, true, true},
- {true, true, true},
- {true, true, true},
- {true, true, true}
-};
-
-static const mipi_lane_cfg_t MIPI_PORT_LANES[N_RX_MODE][N_MIPI_PORT_ID] = {
- {MIPI_4LANE_CFG, MIPI_1LANE_CFG, MIPI_0LANE_CFG},
- {MIPI_3LANE_CFG, MIPI_1LANE_CFG, MIPI_0LANE_CFG},
- {MIPI_2LANE_CFG, MIPI_1LANE_CFG, MIPI_0LANE_CFG},
- {MIPI_1LANE_CFG, MIPI_1LANE_CFG, MIPI_0LANE_CFG},
- {MIPI_2LANE_CFG, MIPI_1LANE_CFG, MIPI_2LANE_CFG},
- {MIPI_3LANE_CFG, MIPI_1LANE_CFG, MIPI_1LANE_CFG},
- {MIPI_2LANE_CFG, MIPI_1LANE_CFG, MIPI_1LANE_CFG},
- {MIPI_1LANE_CFG, MIPI_1LANE_CFG, MIPI_1LANE_CFG}
-};
-
-static const hrt_address SUB_SYSTEM_OFFSET[N_SUB_SYSTEM_ID] = {
+static const hrt_address __maybe_unused SUB_SYSTEM_OFFSET[N_SUB_SYSTEM_ID] = {
0x00001000UL,
0x00002000UL,
0x00003000UL,
#define NOF_ACQ_REGS 12
-// Register id's of MMIO slave accesible registers
+// Register id's of MMIO slave accessible registers
#define ACQ_START_ADDR_REG_ID 0
#define ACQ_MEM_REGION_SIZE_REG_ID 1
#define ACQ_NUM_MEM_REGIONS_REG_ID 2
// Number of registers
#define CAPT_NOF_REGS 16
-// Register id's of MMIO slave accesible registers
+// Register id's of MMIO slave accessible registers
#define CAPT_START_MODE_REG_ID 0
#define CAPT_START_ADDR_REG_ID 1
#define CAPT_MEM_REGION_SIZE_REG_ID 2
#endif
#if !defined(ISP2401)
+static const mipi_lane_cfg_t MIPI_PORT_LANES[N_RX_MODE][N_MIPI_PORT_ID] = {
+ {MIPI_4LANE_CFG, MIPI_1LANE_CFG, MIPI_0LANE_CFG},
+ {MIPI_3LANE_CFG, MIPI_1LANE_CFG, MIPI_0LANE_CFG},
+ {MIPI_2LANE_CFG, MIPI_1LANE_CFG, MIPI_0LANE_CFG},
+ {MIPI_1LANE_CFG, MIPI_1LANE_CFG, MIPI_0LANE_CFG},
+ {MIPI_2LANE_CFG, MIPI_1LANE_CFG, MIPI_2LANE_CFG},
+ {MIPI_3LANE_CFG, MIPI_1LANE_CFG, MIPI_1LANE_CFG},
+ {MIPI_2LANE_CFG, MIPI_1LANE_CFG, MIPI_1LANE_CFG},
+ {MIPI_1LANE_CFG, MIPI_1LANE_CFG, MIPI_1LANE_CFG}
+};
+
void ia_css_isys_rx_configure(const rx_cfg_t *config,
const enum ia_css_input_mode input_mode)
{
- bool port_enabled[N_MIPI_PORT_ID];
bool any_port_enabled = false;
enum mipi_port_id port;
_HRT_CSS_RECEIVER_2400_RX_COUNT_REG_IDX,
config->rxcount);
- port_enabled[port] = true;
-
if (input_mode != IA_CSS_INPUT_MODE_BUFFERED_SENSOR) {
/* MW: A bit of a hack, straight wiring of the capture
* units,assuming they are linearly enumerated. */
#ifdef ISP2401
static unsigned int
-get_crop_lines_for_bayer_order(
- const struct ia_css_stream_config *config)
+get_crop_lines_for_bayer_order(const struct ia_css_stream_config *config)
{
assert(config);
- if ((config->input_config.bayer_order == IA_CSS_BAYER_ORDER_BGGR)
- || (config->input_config.bayer_order == IA_CSS_BAYER_ORDER_GBRG))
+ if ((config->input_config.bayer_order == IA_CSS_BAYER_ORDER_BGGR) ||
+ (config->input_config.bayer_order == IA_CSS_BAYER_ORDER_GBRG))
return 1;
return 0;
}
static unsigned int
-get_crop_columns_for_bayer_order(
- const struct ia_css_stream_config *config)
+get_crop_columns_for_bayer_order(const struct ia_css_stream_config *config)
{
assert(config);
- if ((config->input_config.bayer_order == IA_CSS_BAYER_ORDER_RGGB)
- || (config->input_config.bayer_order == IA_CSS_BAYER_ORDER_GBRG))
+ if ((config->input_config.bayer_order == IA_CSS_BAYER_ORDER_RGGB) ||
+ (config->input_config.bayer_order == IA_CSS_BAYER_ORDER_GBRG))
return 1;
return 0;
if (binary) {
err = ia_css_binary_3a_grid_info(binary, info, pipe);
if (err)
- goto ERR;
+ goto err;
} else {
memset(&info->s3a_grid, 0, sizeof(info->s3a_grid));
}
ia_css_binary_dvs_grid_info(binary, info, pipe);
ia_css_binary_dvs_stat_grid_info(binary, info, pipe);
} else {
- memset(&info->dvs_grid.dvs_grid_info, 0,
- sizeof(info->dvs_grid.dvs_grid_info));
- memset(&info->dvs_grid.dvs_stat_grid_info, 0,
- sizeof(info->dvs_grid.dvs_stat_grid_info));
+ memset(&info->dvs_grid, 0, sizeof(info->dvs_grid));
}
if (binary) {
info->vamem_type = IA_CSS_VAMEM_TYPE_2;
-ERR :
+err:
IA_CSS_LEAVE_ERR_PRIVATE(err);
return err;
}
if (err)
return err;
mycs->num_yuv_scaler = cas_scaler_descr.num_stage;
- mycs->yuv_scaler_binary = kzalloc(cas_scaler_descr.num_stage *
- sizeof(struct ia_css_binary), GFP_KERNEL);
+ mycs->yuv_scaler_binary = kcalloc(cas_scaler_descr.num_stage,
+ sizeof(struct ia_css_binary),
+ GFP_KERNEL);
if (!mycs->yuv_scaler_binary) {
err = -ENOMEM;
return err;
}
- mycs->is_output_stage = kzalloc(cas_scaler_descr.num_stage
- * sizeof(bool), GFP_KERNEL);
+ mycs->is_output_stage = kcalloc(cas_scaler_descr.num_stage,
+ sizeof(bool), GFP_KERNEL);
if (!mycs->is_output_stage) {
err = -ENOMEM;
return err;
return err;
}
mycs->num_yuv_scaler = cas_scaler_descr.num_stage;
- mycs->yuv_scaler_binary = kzalloc(cas_scaler_descr.num_stage *
- sizeof(struct ia_css_binary), GFP_KERNEL);
+ mycs->yuv_scaler_binary = kcalloc(cas_scaler_descr.num_stage,
+ sizeof(struct ia_css_binary),
+ GFP_KERNEL);
if (!mycs->yuv_scaler_binary) {
err = -ENOMEM;
IA_CSS_LEAVE_ERR_PRIVATE(err);
return err;
}
- mycs->is_output_stage = kzalloc(cas_scaler_descr.num_stage *
+ mycs->is_output_stage = kcalloc(cas_scaler_descr.num_stage,
sizeof(bool), GFP_KERNEL);
if (!mycs->is_output_stage) {
err = -ENOMEM;
goto ERR;
mycs->num_output = cas_scaler_descr.num_output_stage;
mycs->num_yuv_scaler = cas_scaler_descr.num_stage;
- mycs->yuv_scaler_binary = kzalloc(cas_scaler_descr.num_stage *
+ mycs->yuv_scaler_binary = kcalloc(cas_scaler_descr.num_stage,
sizeof(struct ia_css_binary),
GFP_KERNEL);
if (!mycs->yuv_scaler_binary) {
err = -ENOMEM;
goto ERR;
}
- mycs->is_output_stage = kzalloc(cas_scaler_descr.num_stage *
+ mycs->is_output_stage = kcalloc(cas_scaler_descr.num_stage,
sizeof(bool), GFP_KERNEL);
if (!mycs->is_output_stage) {
err = -ENOMEM;
mycs->num_vf_pp = 1;
}
- mycs->vf_pp_binary = kzalloc(mycs->num_vf_pp *
+ mycs->vf_pp_binary = kcalloc(mycs->num_vf_pp,
sizeof(struct ia_css_binary),
GFP_KERNEL);
if (!mycs->vf_pp_binary) {
timeout--;
udelay(1);
}
- if ((ia_css_spctrl_get_state(SP0_ID) != IA_CSS_SP_SW_TERMINATED))
+ if (ia_css_spctrl_get_state(SP0_ID) != IA_CSS_SP_SW_TERMINATED)
IA_CSS_WARNING("SP has not terminated (SW)");
if (timeout == 0) {
unsigned int i = 0;
for (i = 0; i < sh_css_num_binaries; i++) {
- if (fw_minibuffer[i].name)
- kfree((void *)fw_minibuffer[i].name);
- if (fw_minibuffer[i].buffer)
- kvfree(fw_minibuffer[i].buffer);
+ kfree(fw_minibuffer[i].name);
+ kvfree(fw_minibuffer[i].buffer);
}
kfree(fw_minibuffer);
fw_minibuffer = NULL;
const enum atomisp_input_format format,
const bool hasSOLandEOL,
const unsigned int embedded_data_size_words,
- unsigned int *size_mem_words) {
+ unsigned int *size_mem_words)
+{
int err = 0;
unsigned int bits_per_pixel = 0;
IA_CSS_ENTER("padded_width=%d, height=%d, format=%d, hasSOLandEOL=%d, embedded_data_size_words=%d\n",
width_padded, height, format, hasSOLandEOL, embedded_data_size_words);
- switch (format)
- {
+ switch (format) {
case ATOMISP_INPUT_FORMAT_RAW_6: /* 4p, 3B, 24bits */
bits_per_pixel = 6;
break;
/* Even lines for YUV420 formats are double in bits_per_pixel. */
if (format == ATOMISP_INPUT_FORMAT_YUV420_8
|| format == ATOMISP_INPUT_FORMAT_YUV420_10
- || format == ATOMISP_INPUT_FORMAT_YUV420_16)
- {
+ || format == ATOMISP_INPUT_FORMAT_YUV420_16) {
even_line_bytes = (width_padded * 2 * bits_per_pixel + 7) >>
3; /* ceil ( bits per line / 8) */
- } else
- {
+ } else {
even_line_bytes = odd_line_bytes;
}
#if !defined(ISP2401)
int
ia_css_mipi_frame_enable_check_on_size(const enum mipi_port_id port,
- const unsigned int size_mem_words) {
+ const unsigned int size_mem_words)
+{
u32 idx;
int err = -EBUSY;
for (idx = 0; idx < IA_CSS_MIPI_SIZE_CHECK_MAX_NOF_ENTRIES_PER_PORT &&
my_css.mipi_sizes_for_check[port][idx] != 0;
- idx++) /* do nothing */
- {
+ idx++) { /* do nothing */
}
- if (idx < IA_CSS_MIPI_SIZE_CHECK_MAX_NOF_ENTRIES_PER_PORT)
- {
+ if (idx < IA_CSS_MIPI_SIZE_CHECK_MAX_NOF_ENTRIES_PER_PORT) {
my_css.mipi_sizes_for_check[port][idx] = size_mem_words;
err = 0;
}
int
calculate_mipi_buff_size(
struct ia_css_stream_config *stream_cfg,
- unsigned int *size_mem_words) {
+ unsigned int *size_mem_words)
+{
#if !defined(ISP2401)
int err = -EINVAL;
(void)stream_cfg;
/* Even lines for YUV420 formats are double in bits_per_pixel. */
if (format == ATOMISP_INPUT_FORMAT_YUV420_8
- || format == ATOMISP_INPUT_FORMAT_YUV420_10)
- {
+ || format == ATOMISP_INPUT_FORMAT_YUV420_10) {
even_line_bytes = (width_padded * 2 * bits_per_pixel + 7) >>
3; /* ceil ( bits per line / 8) */
- } else
- {
+ } else {
even_line_bytes = odd_line_bytes;
}
int
allocate_mipi_frames(struct ia_css_pipe *pipe,
- struct ia_css_stream_info *info) {
+ struct ia_css_stream_info *info)
+{
int err = -EINVAL;
unsigned int port;
assert(pipe);
assert(pipe->stream);
- if ((!pipe) || (!pipe->stream))
- {
+ if ((!pipe) || (!pipe->stream)) {
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
"allocate_mipi_frames(%p) exit: pipe or stream is null.\n",
pipe);
}
#ifdef ISP2401
- if (pipe->stream->config.online)
- {
+ if (pipe->stream->config.online) {
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
"allocate_mipi_frames(%p) exit: no buffers needed for 2401 pipe mode.\n",
pipe);
#endif
#if !defined(ISP2401)
- if (ref_count_mipi_allocation[port] != 0)
- {
+ if (ref_count_mipi_allocation[port] != 0) {
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
"allocate_mipi_frames(%p) exit: already allocated for this port (port=%d).\n",
pipe, port);
* TODO AM: Once that is changed (removed) this code should be removed as well.
* In that case only 2400 related code should remain.
*/
- if (ref_count_mipi_allocation[port] != 0)
- {
+ if (ref_count_mipi_allocation[port] != 0) {
ref_count_mipi_allocation[port]++;
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
"allocate_mipi_frames(%p) leave: nothing to do, already allocated for this port (port=%d).\n",
{ /* limit the scope of i,j */
unsigned int i, j;
- for (i = 0; i < my_css.num_mipi_frames[port]; i++)
- {
+ for (i = 0; i < my_css.num_mipi_frames[port]; i++) {
/* free previous frame */
if (my_css.mipi_frames[port][i]) {
ia_css_frame_free(my_css.mipi_frames[port][i]);
}
int
-free_mipi_frames(struct ia_css_pipe *pipe) {
+free_mipi_frames(struct ia_css_pipe *pipe)
+{
int err = -EINVAL;
unsigned int port;
"free_mipi_frames(%p) enter:\n", pipe);
/* assert(pipe != NULL); TEMP: TODO: Should be assert only. */
- if (pipe)
- {
+ if (pipe) {
assert(pipe->stream);
if ((!pipe) || (!pipe->stream)) {
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
}
#endif
}
- } else /* pipe ==NULL */
- {
+ } else { /* pipe ==NULL */
/* AM TEMP: free-ing all mipi buffers just like a legacy code. */
for (port = CSI_PORT0_ID; port < N_CSI_PORTS; port++) {
unsigned int i;
}
int
-send_mipi_frames(struct ia_css_pipe *pipe) {
+send_mipi_frames(struct ia_css_pipe *pipe)
+{
int err = -EINVAL;
unsigned int i;
#ifndef ISP2401
assert(pipe);
assert(pipe->stream);
- if (!pipe || !pipe->stream)
- {
+ if (!pipe || !pipe->stream) {
IA_CSS_ERROR("pipe or stream is null");
return -EINVAL;
}
}
/* Hand-over the SP-internal mipi buffers */
- for (i = 0; i < my_css.num_mipi_frames[port]; i++)
- {
+ for (i = 0; i < my_css.num_mipi_frames[port]; i++) {
/* Need to include the ofset for port. */
sh_css_update_host2sp_mipi_frame(port * NUM_MIPI_FRAMES_PER_STREAM + i,
my_css.mipi_frames[port][i]);
* Send an event to inform the SP
* that all MIPI frames are passed.
**********************************/
- if (!sh_css_sp_is_running())
- {
+ if (!sh_css_sp_is_running()) {
/* SP is not running. The queues are not valid */
IA_CSS_ERROR("sp is not running");
return err;
}
static int
-store_fpntbl(struct ia_css_isp_parameters *params, ia_css_ptr ptr) {
+store_fpntbl(struct ia_css_isp_parameters *params, ia_css_ptr ptr)
+{
struct ia_css_host_data *isp_data;
assert(params);
assert(ptr != mmgr_NULL);
isp_data = convert_allocate_fpntbl(params);
- if (!isp_data)
- {
+ if (!isp_data) {
IA_CSS_LEAVE_ERR_PRIVATE(-ENOMEM);
return -ENOMEM;
}
static int
sh_css_select_dp_10bpp_config(const struct ia_css_pipe *pipe,
- bool *is_dp_10bpp) {
+ bool *is_dp_10bpp)
+{
int err = 0;
/* Currently we check if 10bpp DPC configuration is required based
* on the use case,i.e. if BDS and DPC is both enabled. The more cleaner
* implementation. (This is because the configuration is set before a
* binary is selected, and the binary info is not available)
*/
- if ((!pipe) || (!is_dp_10bpp))
- {
+ if ((!pipe) || (!is_dp_10bpp)) {
IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL);
err = -EINVAL;
- } else
- {
+ } else {
*is_dp_10bpp = false;
/* check if DPC is enabled from the host */
int
sh_css_set_black_frame(struct ia_css_stream *stream,
- const struct ia_css_frame *raw_black_frame) {
+ const struct ia_css_frame *raw_black_frame)
+{
struct ia_css_isp_parameters *params;
/* this function desperately needs to be moved to the ISP or SP such
* that it can use the DMA.
IA_CSS_ENTER_PRIVATE("black_frame=%p", raw_black_frame);
if (params->fpn_config.data &&
- (params->fpn_config.width != width || params->fpn_config.height != height))
- {
+ (params->fpn_config.width != width || params->fpn_config.height != height)) {
kvfree(params->fpn_config.data);
params->fpn_config.data = NULL;
}
- if (!params->fpn_config.data)
- {
+ if (!params->fpn_config.data) {
params->fpn_config.data = kvmalloc(height * width *
sizeof(short), GFP_KERNEL);
if (!params->fpn_config.data) {
}
/* store raw to fpntbl */
- for (y = 0; y < height; y++)
- {
+ for (y = 0; y < height; y++) {
for (x = 0; x < width; x += (ISP_VEC_NELEMS * 2)) {
int ofs = y * width + x;
static int
ia_css_process_zoom_and_motion(
struct ia_css_isp_parameters *params,
- const struct ia_css_pipeline_stage *first_stage) {
+ const struct ia_css_pipeline_stage *first_stage)
+{
/* first_stage can be NULL */
const struct ia_css_pipeline_stage *stage;
int err = 0;
IA_CSS_ENTER_PRIVATE("");
/* Go through all stages to udate uds and cropping */
- for (stage = first_stage; stage; stage = stage->next)
- {
+ for (stage = first_stage; stage; stage = stage->next) {
struct ia_css_binary *binary;
/* note: the var below is made static as it is quite large;
if it is not static it ends up on the stack which could
int
ia_css_get_3a_statistics(struct ia_css_3a_statistics *host_stats,
- const struct ia_css_isp_3a_statistics *isp_stats) {
+ const struct ia_css_isp_3a_statistics *isp_stats)
+{
struct ia_css_isp_3a_statistics_map *map;
int ret = 0;
assert(isp_stats);
map = ia_css_isp_3a_statistics_map_allocate(isp_stats, NULL);
- if (map)
- {
+ if (map) {
hmm_load(isp_stats->data_ptr, map->data_ptr, isp_stats->size);
ia_css_translate_3a_statistics(host_stats, map);
ia_css_isp_3a_statistics_map_free(map);
- } else
- {
+ } else {
IA_CSS_ERROR("out of memory");
ret = -ENOMEM;
}
int
ia_css_stream_set_isp_config(
struct ia_css_stream *stream,
- const struct ia_css_isp_config *config) {
+ const struct ia_css_isp_config *config)
+{
return ia_css_stream_set_isp_config_on_pipe(stream, config, NULL);
}
ia_css_stream_set_isp_config_on_pipe(
struct ia_css_stream *stream,
const struct ia_css_isp_config *config,
- struct ia_css_pipe *pipe) {
+ struct ia_css_pipe *pipe)
+{
int err = 0;
if ((!stream) || (!config))
int
ia_css_pipe_set_isp_config(struct ia_css_pipe *pipe,
- struct ia_css_isp_config *config) {
+ struct ia_css_isp_config *config)
+{
struct ia_css_pipe *pipe_in = pipe;
int err = 0;
sh_css_set_global_isp_config_on_pipe(
struct ia_css_pipe *curr_pipe,
const struct ia_css_isp_config *config,
- struct ia_css_pipe *pipe) {
+ struct ia_css_pipe *pipe)
+{
int err = 0;
int err1 = 0;
int err2 = 0;
sh_css_set_per_frame_isp_config_on_pipe(
struct ia_css_stream *stream,
const struct ia_css_isp_config *config,
- struct ia_css_pipe *pipe) {
+ struct ia_css_pipe *pipe)
+{
unsigned int i;
bool per_frame_config_created = false;
int err = 0;
IA_CSS_ENTER_PRIVATE("stream=%p, config=%p, pipe=%p", stream, config, pipe);
- if (!pipe)
- {
+ if (!pipe) {
err = -EINVAL;
goto exit;
}
/* create per-frame ISP params object with default values
* from stream->isp_params_configs if one doesn't already exist
*/
- if (!stream->per_frame_isp_params_configs)
- {
+ if (!stream->per_frame_isp_params_configs) {
err = sh_css_create_isp_params(stream,
&stream->per_frame_isp_params_configs);
if (err)
params = stream->per_frame_isp_params_configs;
/* update new ISP params object with the new config */
- if (!sh_css_init_isp_params_from_global(stream, params, false, pipe))
- {
+ if (!sh_css_init_isp_params_from_global(stream, params, false, pipe)) {
err1 = -EINVAL;
}
err2 = sh_css_init_isp_params_from_config(stream->pipes[0], params, config, pipe);
- if (per_frame_config_created)
- {
+ if (per_frame_config_created) {
ddr_ptrs = ¶ms->ddr_ptrs;
ddr_ptrs_size = ¶ms->ddr_ptrs_size;
/* create per pipe reference to general ddr_ptrs */
sh_css_init_isp_params_from_config(struct ia_css_pipe *pipe,
struct ia_css_isp_parameters *params,
const struct ia_css_isp_config *config,
- struct ia_css_pipe *pipe_in) {
+ struct ia_css_pipe *pipe_in)
+{
int err = 0;
bool is_dp_10bpp = true;
}
if (0 ==
- sh_css_select_dp_10bpp_config(pipe, &is_dp_10bpp))
- {
+ sh_css_select_dp_10bpp_config(pipe, &is_dp_10bpp)) {
/* return an error when both DPC and BDS is enabled by the
* user. */
/* we do not exit from this point immediately to allow internal
if (is_dp_10bpp) {
err = -EINVAL;
}
- } else
- {
+ } else {
err = -EINVAL;
goto exit;
}
static unsigned int g_param_buffer_enqueue_count;
int
-ia_css_stream_isp_parameters_init(struct ia_css_stream *stream) {
+ia_css_stream_isp_parameters_init(struct ia_css_stream *stream)
+{
int err = 0;
unsigned int i;
struct sh_css_ddr_address_map *ddr_ptrs;
assert(stream);
IA_CSS_ENTER_PRIVATE("void");
- if (!stream)
- {
+ if (!stream) {
IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL);
return -EINVAL;
}
goto ERR;
params = stream->isp_params_configs;
- if (!sh_css_init_isp_params_from_global(stream, params, true, NULL))
- {
+ if (!sh_css_init_isp_params_from_global(stream, params, true, NULL)) {
/* we do not return the error immediately to enable internal
* firmware feature testing */
err = -EINVAL;
ddr_ptrs_size = ¶ms->ddr_ptrs_size;
/* create per pipe reference to general ddr_ptrs */
- for (i = 0; i < IA_CSS_PIPE_ID_NUM; i++)
- {
+ for (i = 0; i < IA_CSS_PIPE_ID_NUM; i++) {
ref_sh_css_ddr_address_map(ddr_ptrs, ¶ms->pipe_ddr_ptrs[i]);
params->pipe_ddr_ptrs_size[i] = *ddr_ptrs_size;
}
static int
sh_css_create_isp_params(struct ia_css_stream *stream,
- struct ia_css_isp_parameters **isp_params_out) {
+ struct ia_css_isp_parameters **isp_params_out)
+{
bool succ = true;
unsigned int i;
struct sh_css_ddr_address_map *ddr_ptrs;
struct ia_css_isp_parameters *params =
kvmalloc(sizeof(struct ia_css_isp_parameters), GFP_KERNEL);
- if (!params)
- {
+ if (!params) {
*isp_params_out = NULL;
err = -ENOMEM;
IA_CSS_ERROR("%s:%d error: cannot allocate memory", __FILE__, __LINE__);
IA_CSS_LEAVE_ERR_PRIVATE(err);
return err;
- } else
- {
+ } else {
memset(params, 0, sizeof(struct ia_css_isp_parameters));
}
ddr_ptrs = ¶ms->ddr_ptrs;
ddr_ptrs_size = ¶ms->ddr_ptrs_size;
- for (i = 0; i < IA_CSS_PIPE_ID_NUM; i++)
- {
+ for (i = 0; i < IA_CSS_PIPE_ID_NUM; i++) {
memset(¶ms->pipe_ddr_ptrs[i], 0,
sizeof(params->pipe_ddr_ptrs[i]));
memset(¶ms->pipe_ddr_ptrs_size[i], 0,
}
int
-sh_css_params_init(void) {
+sh_css_params_init(void)
+{
int i, p;
IA_CSS_ENTER_PRIVATE("void");
g_param_buffer_dequeue_count = 0;
g_param_buffer_enqueue_count = 0;
- for (p = 0; p < IA_CSS_PIPE_ID_NUM; p++)
- {
+ for (p = 0; p < IA_CSS_PIPE_ID_NUM; p++) {
for (i = 0; i < SH_CSS_MAX_STAGES; i++) {
xmem_sp_stage_ptrs[p][i] =
ia_css_refcount_increment(-1,
ATOMISP_MAP_FLAG_CLEARED));
if ((sp_ddr_ptrs == mmgr_NULL) ||
- (xmem_sp_group_ptrs == mmgr_NULL))
- {
+ (xmem_sp_group_ptrs == mmgr_NULL)) {
ia_css_uninit();
IA_CSS_LEAVE_ERR_PRIVATE(-ENOMEM);
return -ENOMEM;
unsigned int width,
unsigned int height,
ia_css_ptr dest,
- unsigned int aligned_width) {
+ unsigned int aligned_width)
+{
struct ia_css_host_data *isp_data;
assert(dest != mmgr_NULL);
isp_data = convert_allocate_morph_plane(data, width, height, aligned_width);
- if (!isp_data)
- {
+ if (!isp_data) {
IA_CSS_LEAVE_ERR_PRIVATE(-ENOMEM);
return -ENOMEM;
}
sh_css_param_update_isp_params(struct ia_css_pipe *curr_pipe,
struct ia_css_isp_parameters *params,
bool commit,
- struct ia_css_pipe *pipe_in) {
+ struct ia_css_pipe *pipe_in)
+{
int err = 0;
ia_css_ptr cpy;
int i;
raw_bit_depth = ia_css_stream_input_format_bits_per_pixel(curr_pipe->stream);
/* now make the map available to the sp */
- if (!commit)
- {
+ if (!commit) {
IA_CSS_LEAVE_ERR_PRIVATE(err);
return err;
}
/* enqueue a copies of the mem_map to
the designated pipelines */
- for (i = 0; i < curr_pipe->stream->num_pipes; i++)
- {
+ for (i = 0; i < curr_pipe->stream->num_pipes; i++) {
struct ia_css_pipe *pipe;
struct sh_css_ddr_address_map *cur_map;
struct sh_css_ddr_address_map_size *cur_map_size;
struct ia_css_isp_parameters *params,
const struct ia_css_pipeline_stage *stage,
struct sh_css_ddr_address_map *ddr_map,
- struct sh_css_ddr_address_map_size *ddr_map_size) {
+ struct sh_css_ddr_address_map_size *ddr_map_size)
+{
int err;
const struct ia_css_binary *binary;
stage_num = stage->stage_num;
- if (binary->info->sp.enable.fpnr)
- {
+ if (binary->info->sp.enable.fpnr) {
buff_realloced = reallocate_buffer(&ddr_map->fpn_tbl,
&ddr_map_size->fpn_tbl,
(size_t)(FPNTBL_BYTES(binary)),
}
}
- if (binary->info->sp.enable.sc)
- {
+ if (binary->info->sp.enable.sc) {
u32 enable_conv;
size_t bytes;
* DPC kernel. The code below sets the pipe specific configuration to
* individual binaries. */
if (IS_ISP2401 &&
- params->pipe_dpc_config_changed[pipe_id] && binary->info->sp.enable.dpc)
- {
+ params->pipe_dpc_config_changed[pipe_id] && binary->info->sp.enable.dpc) {
unsigned int size =
stage->binary->info->mem_offsets.offsets.param->dmem.dp.size;
}
}
- if (params->config_changed[IA_CSS_MACC_ID] && binary->info->sp.enable.macc)
- {
+ if (params->config_changed[IA_CSS_MACC_ID] && binary->info->sp.enable.macc) {
unsigned int i, j, idx;
unsigned int idx_map[] = {
0, 1, 3, 2, 6, 7, 5, 4, 12, 13, 15, 14, 10, 11, 9, 8
sizeof(converted_macc_table.data));
}
- if (binary->info->sp.enable.dvs_6axis)
- {
+ if (binary->info->sp.enable.dvs_6axis) {
/* because UV is packed into the Y plane, calc total
* YYU size = /2 gives size of UV-only,
* total YYU size = UV-only * 3.
}
}
- if (binary->info->sp.enable.ca_gdc)
- {
+ if (binary->info->sp.enable.ca_gdc) {
unsigned int i;
ia_css_ptr *virt_addr_tetra_x[
}
/* After special cases like SC, FPN since they may change parameters */
- for (mem = 0; mem < N_IA_CSS_MEMORIES; mem++)
- {
+ for (mem = 0; mem < N_IA_CSS_MEMORIES; mem++) {
const struct ia_css_isp_data *isp_data =
ia_css_isp_param_get_isp_mem_init(&binary->info->sp.mem_initializers,
IA_CSS_PARAM_CLASS_PARAM, mem);
static int
free_ia_css_isp_parameter_set_info(
- ia_css_ptr ptr) {
+ ia_css_ptr ptr)
+{
int err = 0;
struct ia_css_isp_parameter_set_info isp_params_info;
unsigned int i;
IA_CSS_ENTER_PRIVATE("ptr = %u", ptr);
/* sanity check - ptr must be valid */
- if (!ia_css_refcount_is_valid(ptr))
- {
+ if (!ia_css_refcount_is_valid(ptr)) {
IA_CSS_ERROR("%s: IA_CSS_REFCOUNT_PARAM_SET_POOL(0x%x) invalid arg", __func__,
ptr);
err = -EINVAL;
hmm_load(ptr, &isp_params_info.mem_map, sizeof(struct sh_css_ddr_address_map));
/* copy map using size info */
for (i = 0; i < (sizeof(struct sh_css_ddr_address_map_size) /
- sizeof(size_t)); i++)
- {
+ sizeof(size_t)); i++) {
if (addrs[i] == mmgr_NULL)
continue;
struct sh_css_uds_info *uds, /* out */
struct sh_css_crop_pos *sp_out_crop_pos, /* out */
struct ia_css_resolution pipe_in_res,
- bool enable_zoom) {
+ bool enable_zoom)
+{
unsigned int x0 = 0, y0 = 0, x1 = 0, y1 = 0;
int err = 0;
/* Note:
if ((x0 > x1) || (y0 > y1) || (x1 > pipe_in_res.width) || (y1 > pipe_in_res.height))
return -EINVAL;
- if (!enable_zoom)
- {
+ if (!enable_zoom) {
uds->curr_dx = HRT_GDC_N;
uds->curr_dy = HRT_GDC_N;
}
- if (info->enable.dvs_envelope)
- {
+ if (info->enable.dvs_envelope) {
/* Zoom region is only supported by the UDS module on ISP
* 2 and higher. It is not supported in video mode on ISP 1 */
return -EINVAL;
- } else
- {
+ } else {
if (enable_zoom) {
/* A. Calculate dx/dy based on crop region using in_frame_info
* Scale the crop region if in_frame_info to the stage is not same as
}
static int
-set_input_frame_buffer(const struct ia_css_frame *frame) {
+set_input_frame_buffer(const struct ia_css_frame *frame)
+{
if (!frame)
return -EINVAL;
- switch (frame->info.format)
- {
+ switch (frame->info.format) {
case IA_CSS_FRAME_FORMAT_QPLANE6:
case IA_CSS_FRAME_FORMAT_YUV420_16:
case IA_CSS_FRAME_FORMAT_RAW_PACKED:
static int
set_output_frame_buffer(const struct ia_css_frame *frame,
- unsigned int idx) {
+ unsigned int idx)
+{
if (!frame)
return -EINVAL;
- switch (frame->info.format)
- {
+ switch (frame->info.format) {
case IA_CSS_FRAME_FORMAT_YUV420:
case IA_CSS_FRAME_FORMAT_YUV422:
case IA_CSS_FRAME_FORMAT_YUV444:
}
static int
-set_view_finder_buffer(const struct ia_css_frame *frame) {
+set_view_finder_buffer(const struct ia_css_frame *frame)
+{
if (!frame)
return -EINVAL;
- switch (frame->info.format)
- {
+ switch (frame->info.format) {
/* the dual output pin */
case IA_CSS_FRAME_FORMAT_NV12:
case IA_CSS_FRAME_FORMAT_NV12_16:
}
static int
-sh_css_sp_write_frame_pointers(const struct sh_css_binary_args *args) {
+sh_css_sp_write_frame_pointers(const struct sh_css_binary_args *args)
+{
int err = 0;
int i;
err = set_input_frame_buffer(args->in_frame);
if (!err && args->out_vf_frame)
err = set_view_finder_buffer(args->out_vf_frame);
- for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++)
- {
+ for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) {
if (!err && args->out_frame[i])
err = set_output_frame_buffer(args->out_frame[i], i);
}
}
static int
-copy_isp_mem_if_to_ddr(struct ia_css_binary *binary) {
+copy_isp_mem_if_to_ddr(struct ia_css_binary *binary)
+{
int err;
err = ia_css_isp_param_copy_isp_mem_if_to_ddr(
const struct ia_css_binary *binary,
const struct sh_css_binary_args *args,
bool two_ppc,
- bool deinterleaved) {
+ bool deinterleaved)
+{
ia_css_fpn_configure(binary, &binary->in_frame_info);
ia_css_crop_configure(binary, &args->delay_frames[0]->info);
ia_css_qplane_configure(pipeline, binary, &binary->in_frame_info);
bool xnr,
const struct ia_css_isp_param_css_segments *isp_mem_if,
unsigned int if_config_index,
- bool two_ppc) {
+ bool two_ppc)
+{
const struct ia_css_binary_xinfo *xinfo;
const struct ia_css_binary_info *info;
int err = 0;
ia_css_pipeline_get_sp_thread_id(pipe_num, &thread_id);
- if (!info)
- {
+ if (!info) {
sh_css_sp_group.pipe[thread_id].sp_stage_addr[stage] = mmgr_NULL;
return 0;
}
ia_css_frame_info_to_frame_sp_info(&sh_css_sp_stage.frames.in.info,
&binary->in_frame_info);
- for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++)
- {
+ for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) {
ia_css_frame_info_to_frame_sp_info(&sh_css_sp_stage.frames.out[i].info,
&binary->out_frame_info[i]);
}
err = sh_css_sp_write_frame_pointers(args);
/* TODO: move it to a better place */
- if (binary->info->sp.enable.s3a)
- {
+ if (binary->info->sp.enable.s3a) {
ia_css_query_internal_queue_id(IA_CSS_BUFFER_TYPE_3A_STATISTICS, thread_id,
&queue_id);
sh_css_copy_buffer_attr_to_spbuffer(&sh_css_sp_stage.frames.s3a_buf, queue_id,
mmgr_EXCEPTION,
IA_CSS_BUFFER_TYPE_3A_STATISTICS);
}
- if (binary->info->sp.enable.dis)
- {
+ if (binary->info->sp.enable.dis) {
ia_css_query_internal_queue_id(IA_CSS_BUFFER_TYPE_DIS_STATISTICS, thread_id,
&queue_id);
sh_css_copy_buffer_attr_to_spbuffer(&sh_css_sp_stage.frames.dvs_buf, queue_id,
* the original out res. for video pipe, it has two output pins --- out and
* vf_out, so it can keep these two resolutions already. */
if (binary->info->sp.pipeline.mode == IA_CSS_BINARY_MODE_PREVIEW &&
- (binary->vf_downscale_log2 > 0))
- {
+ (binary->vf_downscale_log2 > 0)) {
/* TODO: Remove this after preview output decimation is fixed
* by configuring out&vf info fiels properly */
sh_css_sp_stage.frames.out[0].info.padded_width
unsigned int pipe_num,
bool xnr,
unsigned int if_config_index,
- bool two_ppc) {
+ bool two_ppc)
+{
struct ia_css_binary *binary;
const struct ia_css_fw_info *firmware;
const struct sh_css_binary_args *args;
args = &stage->args;
stage_num = stage->stage_num;
- if (binary)
- {
+ if (binary) {
info = binary->info;
binary_name = (const char *)(info->blob->name);
blob_info = &info->blob->header.blob;
ia_css_init_memory_interface(mem_if, &binary->mem_params, &binary->css_params);
- } else if (firmware)
- {
+ } else if (firmware) {
const struct ia_css_frame_info *out_infos[IA_CSS_BINARY_MAX_OUTPUT_PORTS] = {NULL};
if (args->out_frame[0])
binary_name = IA_CSS_EXT_ISP_PROG_NAME(firmware);
blob_info = &firmware->blob;
mem_if = (struct ia_css_isp_param_css_segments *)&firmware->mem_initializers;
- } else
- {
+ } else {
/* SP stage */
assert(stage->sp_func != IA_CSS_PIPELINE_NO_FUNC);
/* binary and blob_info are now NULL.
*internal_frame_origin_bqs_on_sctbl, /* Origin of internal frame
positioned on shading table at shading correction in ISP. */
const struct ia_css_isp_parameters *params
- ) {
+ )
+{
/* Get first stage */
struct ia_css_pipeline_stage *stage = NULL;
struct ia_css_binary *first_binary = NULL;
first_binary = me->stages->binary;
if (input_mode == IA_CSS_INPUT_MODE_SENSOR ||
- input_mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR)
- {
+ input_mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR) {
assert(port_id < N_MIPI_PORT_ID);
if (port_id >= N_MIPI_PORT_ID) /* should not happen but KW does not know */
return; /* we should be able to return an error */
if_config_index = (uint8_t)(port_id - MIPI_PORT0_ID);
- } else if (input_mode == IA_CSS_INPUT_MODE_MEMORY)
- {
+ } else if (input_mode == IA_CSS_INPUT_MODE_MEMORY) {
if_config_index = SH_CSS_IF_CONFIG_NOT_NEEDED;
- } else
- {
+ } else {
if_config_index = 0x0;
}
memset(&sh_css_sp_group.pipe[thread_id], 0, sizeof(struct sh_css_sp_pipeline));
/* Count stages */
- for (stage = me->stages, num = 0; stage; stage = stage->next, num++)
- {
+ for (stage = me->stages, num = 0; stage; stage = stage->next, num++) {
stage->stage_num = num;
ia_css_debug_pipe_graph_dump_stage(stage, id);
}
me->num_stages = num;
- if (first_binary)
- {
+ if (first_binary) {
/* Init pipeline data */
sh_css_sp_init_group(two_ppc, first_binary->input_format,
offline, if_config_index);
/* TODO: next indicates from which queues parameters need to be
sampled, needs checking/improvement */
- if (ia_css_pipeline_uses_params(me))
- {
+ if (ia_css_pipeline_uses_params(me)) {
sh_css_sp_group.pipe[thread_id].pipe_config =
SH_CSS_PIPE_CONFIG_SAMPLE_PARAMS << thread_id;
}
pipe = find_pipe_by_num(pipe_num);
assert(pipe);
- if (!pipe)
- {
+ if (!pipe) {
return;
}
sh_css_sp_group.pipe[thread_id].scaler_pp_lut = sh_css_pipe_get_pp_gdc_lut(pipe);
#if defined(SH_CSS_ENABLE_METADATA)
- if (md_info && md_info->size > 0)
- {
+ if (md_info && md_info->size > 0) {
sh_css_sp_group.pipe[thread_id].metadata.width = md_info->resolution.width;
sh_css_sp_group.pipe[thread_id].metadata.height = md_info->resolution.height;
sh_css_sp_group.pipe[thread_id].metadata.stride = md_info->stride;
#if defined(SH_CSS_ENABLE_PER_FRAME_PARAMS)
sh_css_sp_group.pipe[thread_id].output_frame_queue_id = (uint32_t)SH_CSS_INVALID_QUEUE_ID;
- if (pipe_id != IA_CSS_PIPE_ID_COPY)
- {
+ if (pipe_id != IA_CSS_PIPE_ID_COPY) {
ia_css_query_internal_queue_id(IA_CSS_BUFFER_TYPE_OUTPUT_FRAME, thread_id,
(enum sh_css_queue_id *)(
&sh_css_sp_group.pipe[thread_id].output_frame_queue_id));
* the parameters are passed to the isp for the shading table centering.
*/
if (internal_frame_origin_bqs_on_sctbl &&
- params && params->shading_settings.enable_shading_table_conversion == 0)
- {
+ params && params->shading_settings.enable_shading_table_conversion == 0) {
sh_css_sp_group.pipe[thread_id].shading.internal_frame_origin_x_bqs_on_sctbl
= (uint32_t)internal_frame_origin_bqs_on_sctbl->x;
sh_css_sp_group.pipe[thread_id].shading.internal_frame_origin_y_bqs_on_sctbl
= (uint32_t)internal_frame_origin_bqs_on_sctbl->y;
- } else
- {
+ } else {
sh_css_sp_group.pipe[thread_id].shading.internal_frame_origin_x_bqs_on_sctbl =
0;
sh_css_sp_group.pipe[thread_id].shading.internal_frame_origin_y_bqs_on_sctbl =
IA_CSS_LOG("pipe_id %d port_config %08x",
pipe_id, sh_css_sp_group.pipe[thread_id].inout_port_config);
- for (stage = me->stages, num = 0; stage; stage = stage->next, num++)
- {
+ for (stage = me->stages, num = 0; stage; stage = stage->next, num++) {
sh_css_sp_group.pipe[thread_id].num_stages++;
if (is_sp_stage(stage)) {
sp_init_sp_stage(stage, pipe_num, two_ppc,
}
enum host2sp_commands
-sh_css_read_host2sp_command(void) {
+sh_css_read_host2sp_command(void)
+{
unsigned int HIVE_ADDR_host_sp_com = sh_css_sp_fw.info.sp.host_sp_com;
unsigned int offset = (unsigned int)offsetof(struct host_sp_communication, host2sp_command)
/ sizeof(int);
int
ia_css_pipe_set_irq_mask(struct ia_css_pipe *pipe,
unsigned int or_mask,
- unsigned int and_mask) {
+ unsigned int and_mask)
+{
unsigned int HIVE_ADDR_host_sp_com = sh_css_sp_fw.info.sp.host_sp_com;
unsigned int offset;
struct sh_css_event_irq_mask event_irq_mask;
int
ia_css_event_get_irq_mask(const struct ia_css_pipe *pipe,
unsigned int *or_mask,
- unsigned int *and_mask) {
+ unsigned int *and_mask)
+{
unsigned int HIVE_ADDR_host_sp_com = sh_css_sp_fw.info.sp.host_sp_com;
unsigned int offset;
struct sh_css_event_irq_mask event_irq_mask;
#include "sh_css_firmware.h"
int
-ia_css_get_version(char *version, int max_size) {
+ia_css_get_version(char *version, int max_size)
+{
char *css_version;
if (!IS_ISP2401)
hantro_g2_hevc_dec.o \
hantro_g1_vp8_dec.o \
rockchip_vpu2_hw_jpeg_enc.o \
+ rockchip_vpu2_hw_h264_dec.o \
rockchip_vpu2_hw_mpeg2_dec.o \
rockchip_vpu2_hw_vp8_dec.o \
hantro_jpeg.o \
* @num_postproc_fmts: Number of post-processor formats.
* @codec: Supported codecs
* @codec_ops: Codec ops.
- * @init: Initialize hardware.
- * @runtime_resume: reenable hardware after power gating
+ * @init: Initialize hardware, optional.
+ * @runtime_resume: reenable hardware after power gating, optional.
* @irqs: array of irq names and interrupt handlers
* @num_irqs: number of irqs in the array
* @clk_names: array of clock names
static const struct of_device_id of_hantro_match[] = {
#ifdef CONFIG_VIDEO_HANTRO_ROCKCHIP
+ { .compatible = "rockchip,px30-vpu", .data = &px30_vpu_variant, },
{ .compatible = "rockchip,rk3036-vpu", .data = &rk3036_vpu_variant, },
{ .compatible = "rockchip,rk3066-vpu", .data = &rk3066_vpu_variant, },
{ .compatible = "rockchip,rk3288-vpu", .data = &rk3288_vpu_variant, },
}
}
- ret = vpu->variant->init(vpu);
- if (ret) {
- dev_err(&pdev->dev, "Failed to init VPU hardware\n");
- return ret;
+ if (vpu->variant->init) {
+ ret = vpu->variant->init(vpu);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to init VPU hardware\n");
+ return ret;
+ }
}
pm_runtime_set_autosuspend_delay(vpu->dev, 100);
#include "hantro_hw.h"
#include "hantro_v4l2.h"
-static void set_params(struct hantro_ctx *ctx)
+static void set_params(struct hantro_ctx *ctx, struct vb2_v4l2_buffer *src_buf)
{
const struct hantro_h264_dec_ctrls *ctrls = &ctx->h264_dec.ctrls;
const struct v4l2_ctrl_h264_decode_params *dec_param = ctrls->decode;
const struct v4l2_ctrl_h264_sps *sps = ctrls->sps;
const struct v4l2_ctrl_h264_pps *pps = ctrls->pps;
- struct vb2_v4l2_buffer *src_buf = hantro_get_src_buf(ctx);
struct hantro_dev *vpu = ctx->dev;
u32 reg;
static void set_ref(struct hantro_ctx *ctx)
{
- struct v4l2_h264_dpb_entry *dpb = ctx->h264_dec.dpb;
const u8 *b0_reflist, *b1_reflist, *p_reflist;
struct hantro_dev *vpu = ctx->dev;
- u32 dpb_longterm = 0;
- u32 dpb_valid = 0;
int reg_num;
u32 reg;
int i;
- /*
- * Set up bit maps of valid and long term DPBs.
- * NOTE: The bits are reversed, i.e. MSb is DPB 0.
- */
- for (i = 0; i < HANTRO_H264_DPB_SIZE; ++i) {
- if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)
- dpb_valid |= BIT(HANTRO_H264_DPB_SIZE - 1 - i);
-
- if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM)
- dpb_longterm |= BIT(HANTRO_H264_DPB_SIZE - 1 - i);
- }
- vdpu_write_relaxed(vpu, dpb_valid << 16, G1_REG_VALID_REF);
- vdpu_write_relaxed(vpu, dpb_longterm << 16, G1_REG_LT_REF);
+ vdpu_write_relaxed(vpu, ctx->h264_dec.dpb_valid, G1_REG_VALID_REF);
+ vdpu_write_relaxed(vpu, ctx->h264_dec.dpb_longterm, G1_REG_LT_REF);
/*
* Set up reference frame picture numbers.
* subsequential reference pictures.
*/
for (i = 0; i < HANTRO_H264_DPB_SIZE; i += 2) {
- reg = 0;
- if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM)
- reg |= G1_REG_REF_PIC_REFER0_NBR(dpb[i].pic_num);
- else
- reg |= G1_REG_REF_PIC_REFER0_NBR(dpb[i].frame_num);
-
- if (dpb[i + 1].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM)
- reg |= G1_REG_REF_PIC_REFER1_NBR(dpb[i + 1].pic_num);
- else
- reg |= G1_REG_REF_PIC_REFER1_NBR(dpb[i + 1].frame_num);
-
+ reg = G1_REG_REF_PIC_REFER0_NBR(hantro_h264_get_ref_nbr(ctx, i)) |
+ G1_REG_REF_PIC_REFER1_NBR(hantro_h264_get_ref_nbr(ctx, i + 1));
vdpu_write_relaxed(vpu, reg, G1_REG_REF_PIC(i / 2));
}
}
}
-static void set_buffers(struct hantro_ctx *ctx)
+static void set_buffers(struct hantro_ctx *ctx, struct vb2_v4l2_buffer *src_buf)
{
const struct hantro_h264_dec_ctrls *ctrls = &ctx->h264_dec.ctrls;
- struct vb2_v4l2_buffer *src_buf, *dst_buf;
+ struct vb2_v4l2_buffer *dst_buf;
struct hantro_dev *vpu = ctx->dev;
dma_addr_t src_dma, dst_dma;
size_t offset = 0;
- src_buf = hantro_get_src_buf(ctx);
- dst_buf = hantro_get_dst_buf(ctx);
-
/* Source (stream) buffer. */
src_dma = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
vdpu_write_relaxed(vpu, src_dma, G1_REG_ADDR_STR);
/* Destination (decoded frame) buffer. */
+ dst_buf = hantro_get_dst_buf(ctx);
dst_dma = hantro_get_dec_buf_addr(ctx, &dst_buf->vb2_buf);
/* Adjust dma addr to start at second line for bottom field */
if (ctrls->decode->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD)
int hantro_g1_h264_dec_run(struct hantro_ctx *ctx)
{
struct hantro_dev *vpu = ctx->dev;
+ struct vb2_v4l2_buffer *src_buf;
int ret;
/* Prepare the H264 decoder context. */
return ret;
/* Configure hardware registers. */
- set_params(ctx);
+ src_buf = hantro_get_src_buf(ctx);
+ set_params(ctx, src_buf);
set_ref(ctx);
- set_buffers(ctx);
+ set_buffers(ctx, src_buf);
hantro_end_prepare_run(ctx);
}
static void cfg_ref(struct hantro_ctx *ctx,
- const struct v4l2_ctrl_vp8_frame *hdr)
+ const struct v4l2_ctrl_vp8_frame *hdr,
+ struct vb2_v4l2_buffer *vb2_dst)
{
struct hantro_dev *vpu = ctx->dev;
- struct vb2_v4l2_buffer *vb2_dst;
dma_addr_t ref;
- vb2_dst = hantro_get_dst_buf(ctx);
ref = hantro_get_ref(ctx, hdr->last_frame_ts);
- if (!ref)
+ if (!ref) {
+ vpu_debug(0, "failed to find last frame ts=%llu\n",
+ hdr->last_frame_ts);
ref = vb2_dma_contig_plane_dma_addr(&vb2_dst->vb2_buf, 0);
+ }
vdpu_write_relaxed(vpu, ref, G1_REG_ADDR_REF(0));
ref = hantro_get_ref(ctx, hdr->golden_frame_ts);
- WARN_ON(!ref && hdr->golden_frame_ts);
+ if (!ref && hdr->golden_frame_ts)
+ vpu_debug(0, "failed to find golden frame ts=%llu\n",
+ hdr->golden_frame_ts);
if (!ref)
ref = vb2_dma_contig_plane_dma_addr(&vb2_dst->vb2_buf, 0);
if (hdr->flags & V4L2_VP8_FRAME_FLAG_SIGN_BIAS_GOLDEN)
vdpu_write_relaxed(vpu, ref, G1_REG_ADDR_REF(4));
ref = hantro_get_ref(ctx, hdr->alt_frame_ts);
- WARN_ON(!ref && hdr->alt_frame_ts);
+ if (!ref && hdr->alt_frame_ts)
+ vpu_debug(0, "failed to find alt frame ts=%llu\n",
+ hdr->alt_frame_ts);
if (!ref)
ref = vb2_dma_contig_plane_dma_addr(&vb2_dst->vb2_buf, 0);
if (hdr->flags & V4L2_VP8_FRAME_FLAG_SIGN_BIAS_ALT)
}
static void cfg_buffers(struct hantro_ctx *ctx,
- const struct v4l2_ctrl_vp8_frame *hdr)
+ const struct v4l2_ctrl_vp8_frame *hdr,
+ struct vb2_v4l2_buffer *vb2_dst)
{
const struct v4l2_vp8_segment *seg = &hdr->segment;
struct hantro_dev *vpu = ctx->dev;
- struct vb2_v4l2_buffer *vb2_dst;
dma_addr_t dst_dma;
u32 reg;
- vb2_dst = hantro_get_dst_buf(ctx);
-
/* Set probability table buffer address */
vdpu_write_relaxed(vpu, ctx->vp8_dec.prob_tbl.dma,
G1_REG_ADDR_QTABLE);
{
const struct v4l2_ctrl_vp8_frame *hdr;
struct hantro_dev *vpu = ctx->dev;
+ struct vb2_v4l2_buffer *vb2_dst;
size_t height = ctx->dst_fmt.height;
size_t width = ctx->dst_fmt.width;
u32 mb_width, mb_height;
cfg_qp(ctx, hdr);
cfg_parts(ctx, hdr);
cfg_tap(ctx, hdr);
- cfg_ref(ctx, hdr);
- cfg_buffers(ctx, hdr);
+
+ vb2_dst = hantro_get_dst_buf(ctx);
+ cfg_ref(ctx, hdr, vb2_dst);
+ cfg_buffers(ctx, hdr, vb2_dst);
hantro_end_prepare_run(ctx);
const struct v4l2_ctrl_h264_decode_params *dec_param = ctrls->decode;
struct hantro_h264_dec_priv_tbl *tbl = ctx->h264_dec.priv.cpu;
const struct v4l2_h264_dpb_entry *dpb = ctx->h264_dec.dpb;
+ u32 dpb_longterm = 0;
+ u32 dpb_valid = 0;
int i;
for (i = 0; i < HANTRO_H264_DPB_SIZE; ++i) {
tbl->poc[i * 2] = dpb[i].top_field_order_cnt;
tbl->poc[i * 2 + 1] = dpb[i].bottom_field_order_cnt;
+
+ /*
+ * Set up bit maps of valid and long term DPBs.
+ * NOTE: The bits are reversed, i.e. MSb is DPB 0.
+ */
+ if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)
+ dpb_valid |= BIT(HANTRO_H264_DPB_SIZE - 1 - i);
+ if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM)
+ dpb_longterm |= BIT(HANTRO_H264_DPB_SIZE - 1 - i);
}
+ ctx->h264_dec.dpb_valid = dpb_valid << 16;
+ ctx->h264_dec.dpb_longterm = dpb_longterm << 16;
tbl->poc[32] = dec_param->top_field_order_cnt;
tbl->poc[33] = dec_param->bottom_field_order_cnt;
return dma_addr;
}
+u16 hantro_h264_get_ref_nbr(struct hantro_ctx *ctx, unsigned int dpb_idx)
+{
+ const struct v4l2_h264_dpb_entry *dpb = &ctx->h264_dec.dpb[dpb_idx];
+
+ if (!(dpb->flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE))
+ return 0;
+ if (dpb->flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM)
+ return dpb->pic_num;
+ return dpb->frame_num;
+}
+
int hantro_h264_dec_prepare_run(struct hantro_ctx *ctx)
{
struct hantro_h264_dec_hw_ctx *h264_ctx = &ctx->h264_dec;
* @dpb: DPB
* @reflists: P/B0/B1 reflists
* @ctrls: V4L2 controls attached to a run
+ * @dpb_longterm: DPB long-term
+ * @dpb_valid: DPB valid
*/
struct hantro_h264_dec_hw_ctx {
struct hantro_aux_buf priv;
struct v4l2_h264_dpb_entry dpb[HANTRO_H264_DPB_SIZE];
struct hantro_h264_dec_reflists reflists;
struct hantro_h264_dec_ctrls ctrls;
+ u32 dpb_longterm;
+ u32 dpb_valid;
};
/**
extern const struct hantro_variant imx8mq_vpu_g2_variant;
extern const struct hantro_variant imx8mq_vpu_variant;
+extern const struct hantro_variant px30_vpu_variant;
extern const struct hantro_variant rk3036_vpu_variant;
extern const struct hantro_variant rk3066_vpu_variant;
extern const struct hantro_variant rk3288_vpu_variant;
dma_addr_t hantro_h264_get_ref_buf(struct hantro_ctx *ctx,
unsigned int dpb_idx);
+u16 hantro_h264_get_ref_nbr(struct hantro_ctx *ctx,
+ unsigned int dpb_idx);
int hantro_h264_dec_prepare_run(struct hantro_ctx *ctx);
+int rockchip_vpu2_h264_dec_run(struct hantro_ctx *ctx);
int hantro_g1_h264_dec_run(struct hantro_ctx *ctx);
int hantro_h264_dec_init(struct hantro_ctx *ctx);
void hantro_h264_dec_exit(struct hantro_ctx *ctx);
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Hantro VPU codec driver
+ *
+ * Copyright (c) 2014 Rockchip Electronics Co., Ltd.
+ * Hertz Wong <hertz.wong@rock-chips.com>
+ * Herman Chen <herman.chen@rock-chips.com>
+ *
+ * Copyright (C) 2014 Google, Inc.
+ * Tomasz Figa <tfiga@chromium.org>
+ */
+
+#include <linux/types.h>
+#include <linux/sort.h>
+
+#include <media/v4l2-mem2mem.h>
+
+#include "hantro_hw.h"
+#include "hantro_v4l2.h"
+
+#define VDPU_SWREG(nr) ((nr) * 4)
+
+#define VDPU_REG_DEC_OUT_BASE VDPU_SWREG(63)
+#define VDPU_REG_RLC_VLC_BASE VDPU_SWREG(64)
+#define VDPU_REG_QTABLE_BASE VDPU_SWREG(61)
+#define VDPU_REG_DIR_MV_BASE VDPU_SWREG(62)
+#define VDPU_REG_REFER_BASE(i) (VDPU_SWREG(84 + (i)))
+#define VDPU_REG_DEC_E(v) ((v) ? BIT(0) : 0)
+
+#define VDPU_REG_DEC_ADV_PRE_DIS(v) ((v) ? BIT(11) : 0)
+#define VDPU_REG_DEC_SCMD_DIS(v) ((v) ? BIT(10) : 0)
+#define VDPU_REG_FILTERING_DIS(v) ((v) ? BIT(8) : 0)
+#define VDPU_REG_PIC_FIXED_QUANT(v) ((v) ? BIT(7) : 0)
+#define VDPU_REG_DEC_LATENCY(v) (((v) << 1) & GENMASK(6, 1))
+
+#define VDPU_REG_INIT_QP(v) (((v) << 25) & GENMASK(30, 25))
+#define VDPU_REG_STREAM_LEN(v) (((v) << 0) & GENMASK(23, 0))
+
+#define VDPU_REG_APF_THRESHOLD(v) (((v) << 17) & GENMASK(30, 17))
+#define VDPU_REG_STARTMB_X(v) (((v) << 8) & GENMASK(16, 8))
+#define VDPU_REG_STARTMB_Y(v) (((v) << 0) & GENMASK(7, 0))
+
+#define VDPU_REG_DEC_MODE(v) (((v) << 0) & GENMASK(3, 0))
+
+#define VDPU_REG_DEC_STRENDIAN_E(v) ((v) ? BIT(5) : 0)
+#define VDPU_REG_DEC_STRSWAP32_E(v) ((v) ? BIT(4) : 0)
+#define VDPU_REG_DEC_OUTSWAP32_E(v) ((v) ? BIT(3) : 0)
+#define VDPU_REG_DEC_INSWAP32_E(v) ((v) ? BIT(2) : 0)
+#define VDPU_REG_DEC_OUT_ENDIAN(v) ((v) ? BIT(1) : 0)
+#define VDPU_REG_DEC_IN_ENDIAN(v) ((v) ? BIT(0) : 0)
+
+#define VDPU_REG_DEC_DATA_DISC_E(v) ((v) ? BIT(22) : 0)
+#define VDPU_REG_DEC_MAX_BURST(v) (((v) << 16) & GENMASK(20, 16))
+#define VDPU_REG_DEC_AXI_WR_ID(v) (((v) << 8) & GENMASK(15, 8))
+#define VDPU_REG_DEC_AXI_RD_ID(v) (((v) << 0) & GENMASK(7, 0))
+
+#define VDPU_REG_START_CODE_E(v) ((v) ? BIT(22) : 0)
+#define VDPU_REG_CH_8PIX_ILEAV_E(v) ((v) ? BIT(21) : 0)
+#define VDPU_REG_RLC_MODE_E(v) ((v) ? BIT(20) : 0)
+#define VDPU_REG_PIC_INTERLACE_E(v) ((v) ? BIT(17) : 0)
+#define VDPU_REG_PIC_FIELDMODE_E(v) ((v) ? BIT(16) : 0)
+#define VDPU_REG_PIC_TOPFIELD_E(v) ((v) ? BIT(13) : 0)
+#define VDPU_REG_WRITE_MVS_E(v) ((v) ? BIT(10) : 0)
+#define VDPU_REG_SEQ_MBAFF_E(v) ((v) ? BIT(7) : 0)
+#define VDPU_REG_PICORD_COUNT_E(v) ((v) ? BIT(6) : 0)
+#define VDPU_REG_DEC_TIMEOUT_E(v) ((v) ? BIT(5) : 0)
+#define VDPU_REG_DEC_CLK_GATE_E(v) ((v) ? BIT(4) : 0)
+
+#define VDPU_REG_PRED_BC_TAP_0_0(v) (((v) << 22) & GENMASK(31, 22))
+#define VDPU_REG_PRED_BC_TAP_0_1(v) (((v) << 12) & GENMASK(21, 12))
+#define VDPU_REG_PRED_BC_TAP_0_2(v) (((v) << 2) & GENMASK(11, 2))
+
+#define VDPU_REG_REFBU_E(v) ((v) ? BIT(31) : 0)
+
+#define VDPU_REG_PINIT_RLIST_F9(v) (((v) << 25) & GENMASK(29, 25))
+#define VDPU_REG_PINIT_RLIST_F8(v) (((v) << 20) & GENMASK(24, 20))
+#define VDPU_REG_PINIT_RLIST_F7(v) (((v) << 15) & GENMASK(19, 15))
+#define VDPU_REG_PINIT_RLIST_F6(v) (((v) << 10) & GENMASK(14, 10))
+#define VDPU_REG_PINIT_RLIST_F5(v) (((v) << 5) & GENMASK(9, 5))
+#define VDPU_REG_PINIT_RLIST_F4(v) (((v) << 0) & GENMASK(4, 0))
+
+#define VDPU_REG_PINIT_RLIST_F15(v) (((v) << 25) & GENMASK(29, 25))
+#define VDPU_REG_PINIT_RLIST_F14(v) (((v) << 20) & GENMASK(24, 20))
+#define VDPU_REG_PINIT_RLIST_F13(v) (((v) << 15) & GENMASK(19, 15))
+#define VDPU_REG_PINIT_RLIST_F12(v) (((v) << 10) & GENMASK(14, 10))
+#define VDPU_REG_PINIT_RLIST_F11(v) (((v) << 5) & GENMASK(9, 5))
+#define VDPU_REG_PINIT_RLIST_F10(v) (((v) << 0) & GENMASK(4, 0))
+
+#define VDPU_REG_REFER1_NBR(v) (((v) << 16) & GENMASK(31, 16))
+#define VDPU_REG_REFER0_NBR(v) (((v) << 0) & GENMASK(15, 0))
+
+#define VDPU_REG_REFER3_NBR(v) (((v) << 16) & GENMASK(31, 16))
+#define VDPU_REG_REFER2_NBR(v) (((v) << 0) & GENMASK(15, 0))
+
+#define VDPU_REG_REFER5_NBR(v) (((v) << 16) & GENMASK(31, 16))
+#define VDPU_REG_REFER4_NBR(v) (((v) << 0) & GENMASK(15, 0))
+
+#define VDPU_REG_REFER7_NBR(v) (((v) << 16) & GENMASK(31, 16))
+#define VDPU_REG_REFER6_NBR(v) (((v) << 0) & GENMASK(15, 0))
+
+#define VDPU_REG_REFER9_NBR(v) (((v) << 16) & GENMASK(31, 16))
+#define VDPU_REG_REFER8_NBR(v) (((v) << 0) & GENMASK(15, 0))
+
+#define VDPU_REG_REFER11_NBR(v) (((v) << 16) & GENMASK(31, 16))
+#define VDPU_REG_REFER10_NBR(v) (((v) << 0) & GENMASK(15, 0))
+
+#define VDPU_REG_REFER13_NBR(v) (((v) << 16) & GENMASK(31, 16))
+#define VDPU_REG_REFER12_NBR(v) (((v) << 0) & GENMASK(15, 0))
+
+#define VDPU_REG_REFER15_NBR(v) (((v) << 16) & GENMASK(31, 16))
+#define VDPU_REG_REFER14_NBR(v) (((v) << 0) & GENMASK(15, 0))
+
+#define VDPU_REG_BINIT_RLIST_F5(v) (((v) << 25) & GENMASK(29, 25))
+#define VDPU_REG_BINIT_RLIST_F4(v) (((v) << 20) & GENMASK(24, 20))
+#define VDPU_REG_BINIT_RLIST_F3(v) (((v) << 15) & GENMASK(19, 15))
+#define VDPU_REG_BINIT_RLIST_F2(v) (((v) << 10) & GENMASK(14, 10))
+#define VDPU_REG_BINIT_RLIST_F1(v) (((v) << 5) & GENMASK(9, 5))
+#define VDPU_REG_BINIT_RLIST_F0(v) (((v) << 0) & GENMASK(4, 0))
+
+#define VDPU_REG_BINIT_RLIST_F11(v) (((v) << 25) & GENMASK(29, 25))
+#define VDPU_REG_BINIT_RLIST_F10(v) (((v) << 20) & GENMASK(24, 20))
+#define VDPU_REG_BINIT_RLIST_F9(v) (((v) << 15) & GENMASK(19, 15))
+#define VDPU_REG_BINIT_RLIST_F8(v) (((v) << 10) & GENMASK(14, 10))
+#define VDPU_REG_BINIT_RLIST_F7(v) (((v) << 5) & GENMASK(9, 5))
+#define VDPU_REG_BINIT_RLIST_F6(v) (((v) << 0) & GENMASK(4, 0))
+
+#define VDPU_REG_BINIT_RLIST_F15(v) (((v) << 15) & GENMASK(19, 15))
+#define VDPU_REG_BINIT_RLIST_F14(v) (((v) << 10) & GENMASK(14, 10))
+#define VDPU_REG_BINIT_RLIST_F13(v) (((v) << 5) & GENMASK(9, 5))
+#define VDPU_REG_BINIT_RLIST_F12(v) (((v) << 0) & GENMASK(4, 0))
+
+#define VDPU_REG_BINIT_RLIST_B5(v) (((v) << 25) & GENMASK(29, 25))
+#define VDPU_REG_BINIT_RLIST_B4(v) (((v) << 20) & GENMASK(24, 20))
+#define VDPU_REG_BINIT_RLIST_B3(v) (((v) << 15) & GENMASK(19, 15))
+#define VDPU_REG_BINIT_RLIST_B2(v) (((v) << 10) & GENMASK(14, 10))
+#define VDPU_REG_BINIT_RLIST_B1(v) (((v) << 5) & GENMASK(9, 5))
+#define VDPU_REG_BINIT_RLIST_B0(v) (((v) << 0) & GENMASK(4, 0))
+
+#define VDPU_REG_BINIT_RLIST_B11(v) (((v) << 25) & GENMASK(29, 25))
+#define VDPU_REG_BINIT_RLIST_B10(v) (((v) << 20) & GENMASK(24, 20))
+#define VDPU_REG_BINIT_RLIST_B9(v) (((v) << 15) & GENMASK(19, 15))
+#define VDPU_REG_BINIT_RLIST_B8(v) (((v) << 10) & GENMASK(14, 10))
+#define VDPU_REG_BINIT_RLIST_B7(v) (((v) << 5) & GENMASK(9, 5))
+#define VDPU_REG_BINIT_RLIST_B6(v) (((v) << 0) & GENMASK(4, 0))
+
+#define VDPU_REG_BINIT_RLIST_B15(v) (((v) << 15) & GENMASK(19, 15))
+#define VDPU_REG_BINIT_RLIST_B14(v) (((v) << 10) & GENMASK(14, 10))
+#define VDPU_REG_BINIT_RLIST_B13(v) (((v) << 5) & GENMASK(9, 5))
+#define VDPU_REG_BINIT_RLIST_B12(v) (((v) << 0) & GENMASK(4, 0))
+
+#define VDPU_REG_PINIT_RLIST_F3(v) (((v) << 15) & GENMASK(19, 15))
+#define VDPU_REG_PINIT_RLIST_F2(v) (((v) << 10) & GENMASK(14, 10))
+#define VDPU_REG_PINIT_RLIST_F1(v) (((v) << 5) & GENMASK(9, 5))
+#define VDPU_REG_PINIT_RLIST_F0(v) (((v) << 0) & GENMASK(4, 0))
+
+#define VDPU_REG_REFER_LTERM_E(v) (((v) << 0) & GENMASK(31, 0))
+
+#define VDPU_REG_REFER_VALID_E(v) (((v) << 0) & GENMASK(31, 0))
+
+#define VDPU_REG_STRM_START_BIT(v) (((v) << 0) & GENMASK(5, 0))
+
+#define VDPU_REG_CH_QP_OFFSET2(v) (((v) << 22) & GENMASK(26, 22))
+#define VDPU_REG_CH_QP_OFFSET(v) (((v) << 17) & GENMASK(21, 17))
+#define VDPU_REG_PIC_MB_HEIGHT_P(v) (((v) << 9) & GENMASK(16, 9))
+#define VDPU_REG_PIC_MB_WIDTH(v) (((v) << 0) & GENMASK(8, 0))
+
+#define VDPU_REG_WEIGHT_BIPR_IDC(v) (((v) << 16) & GENMASK(17, 16))
+#define VDPU_REG_REF_FRAMES(v) (((v) << 0) & GENMASK(4, 0))
+
+#define VDPU_REG_FILT_CTRL_PRES(v) ((v) ? BIT(31) : 0)
+#define VDPU_REG_RDPIC_CNT_PRES(v) ((v) ? BIT(30) : 0)
+#define VDPU_REG_FRAMENUM_LEN(v) (((v) << 16) & GENMASK(20, 16))
+#define VDPU_REG_FRAMENUM(v) (((v) << 0) & GENMASK(15, 0))
+
+#define VDPU_REG_REFPIC_MK_LEN(v) (((v) << 16) & GENMASK(26, 16))
+#define VDPU_REG_IDR_PIC_ID(v) (((v) << 0) & GENMASK(15, 0))
+
+#define VDPU_REG_PPS_ID(v) (((v) << 24) & GENMASK(31, 24))
+#define VDPU_REG_REFIDX1_ACTIVE(v) (((v) << 19) & GENMASK(23, 19))
+#define VDPU_REG_REFIDX0_ACTIVE(v) (((v) << 14) & GENMASK(18, 14))
+#define VDPU_REG_POC_LENGTH(v) (((v) << 0) & GENMASK(7, 0))
+
+#define VDPU_REG_IDR_PIC_E(v) ((v) ? BIT(8) : 0)
+#define VDPU_REG_DIR_8X8_INFER_E(v) ((v) ? BIT(7) : 0)
+#define VDPU_REG_BLACKWHITE_E(v) ((v) ? BIT(6) : 0)
+#define VDPU_REG_CABAC_E(v) ((v) ? BIT(5) : 0)
+#define VDPU_REG_WEIGHT_PRED_E(v) ((v) ? BIT(4) : 0)
+#define VDPU_REG_CONST_INTRA_E(v) ((v) ? BIT(3) : 0)
+#define VDPU_REG_8X8TRANS_FLAG_E(v) ((v) ? BIT(2) : 0)
+#define VDPU_REG_TYPE1_QUANT_E(v) ((v) ? BIT(1) : 0)
+#define VDPU_REG_FIELDPIC_FLAG_E(v) ((v) ? BIT(0) : 0)
+
+static void set_params(struct hantro_ctx *ctx, struct vb2_v4l2_buffer *src_buf)
+{
+ const struct hantro_h264_dec_ctrls *ctrls = &ctx->h264_dec.ctrls;
+ const struct v4l2_ctrl_h264_decode_params *dec_param = ctrls->decode;
+ const struct v4l2_ctrl_h264_sps *sps = ctrls->sps;
+ const struct v4l2_ctrl_h264_pps *pps = ctrls->pps;
+ struct hantro_dev *vpu = ctx->dev;
+ u32 reg;
+
+ reg = VDPU_REG_DEC_ADV_PRE_DIS(0) |
+ VDPU_REG_DEC_SCMD_DIS(0) |
+ VDPU_REG_FILTERING_DIS(0) |
+ VDPU_REG_PIC_FIXED_QUANT(0) |
+ VDPU_REG_DEC_LATENCY(0);
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(50));
+
+ reg = VDPU_REG_INIT_QP(pps->pic_init_qp_minus26 + 26) |
+ VDPU_REG_STREAM_LEN(vb2_get_plane_payload(&src_buf->vb2_buf, 0));
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(51));
+
+ reg = VDPU_REG_APF_THRESHOLD(8) |
+ VDPU_REG_STARTMB_X(0) |
+ VDPU_REG_STARTMB_Y(0);
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(52));
+
+ reg = VDPU_REG_DEC_MODE(0);
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(53));
+
+ reg = VDPU_REG_DEC_STRENDIAN_E(1) |
+ VDPU_REG_DEC_STRSWAP32_E(1) |
+ VDPU_REG_DEC_OUTSWAP32_E(1) |
+ VDPU_REG_DEC_INSWAP32_E(1) |
+ VDPU_REG_DEC_OUT_ENDIAN(1) |
+ VDPU_REG_DEC_IN_ENDIAN(0);
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(54));
+
+ reg = VDPU_REG_DEC_DATA_DISC_E(0) |
+ VDPU_REG_DEC_MAX_BURST(16) |
+ VDPU_REG_DEC_AXI_WR_ID(0) |
+ VDPU_REG_DEC_AXI_RD_ID(0xff);
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(56));
+
+ reg = VDPU_REG_START_CODE_E(1) |
+ VDPU_REG_CH_8PIX_ILEAV_E(0) |
+ VDPU_REG_RLC_MODE_E(0) |
+ VDPU_REG_PIC_INTERLACE_E(!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY) &&
+ (sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD ||
+ dec_param->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC)) |
+ VDPU_REG_PIC_FIELDMODE_E(dec_param->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC) |
+ VDPU_REG_PIC_TOPFIELD_E(!(dec_param->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD)) |
+ VDPU_REG_WRITE_MVS_E((sps->profile_idc > 66) && dec_param->nal_ref_idc) |
+ VDPU_REG_SEQ_MBAFF_E(sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD) |
+ VDPU_REG_PICORD_COUNT_E(sps->profile_idc > 66) |
+ VDPU_REG_DEC_TIMEOUT_E(1) |
+ VDPU_REG_DEC_CLK_GATE_E(1);
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(57));
+
+ reg = VDPU_REG_PRED_BC_TAP_0_0(1) |
+ VDPU_REG_PRED_BC_TAP_0_1((u32)-5) |
+ VDPU_REG_PRED_BC_TAP_0_2(20);
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(59));
+
+ reg = VDPU_REG_REFBU_E(0);
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(65));
+
+ reg = VDPU_REG_STRM_START_BIT(0);
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(109));
+
+ reg = VDPU_REG_CH_QP_OFFSET2(pps->second_chroma_qp_index_offset) |
+ VDPU_REG_CH_QP_OFFSET(pps->chroma_qp_index_offset) |
+ VDPU_REG_PIC_MB_HEIGHT_P(MB_HEIGHT(ctx->src_fmt.height)) |
+ VDPU_REG_PIC_MB_WIDTH(MB_WIDTH(ctx->src_fmt.width));
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(110));
+
+ reg = VDPU_REG_WEIGHT_BIPR_IDC(pps->weighted_bipred_idc) |
+ VDPU_REG_REF_FRAMES(sps->max_num_ref_frames);
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(111));
+
+ reg = VDPU_REG_FILT_CTRL_PRES(pps->flags & V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT) |
+ VDPU_REG_RDPIC_CNT_PRES(pps->flags & V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT) |
+ VDPU_REG_FRAMENUM_LEN(sps->log2_max_frame_num_minus4 + 4) |
+ VDPU_REG_FRAMENUM(dec_param->frame_num);
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(112));
+
+ reg = VDPU_REG_REFPIC_MK_LEN(dec_param->dec_ref_pic_marking_bit_size) |
+ VDPU_REG_IDR_PIC_ID(dec_param->idr_pic_id);
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(113));
+
+ reg = VDPU_REG_PPS_ID(pps->pic_parameter_set_id) |
+ VDPU_REG_REFIDX1_ACTIVE(pps->num_ref_idx_l1_default_active_minus1 + 1) |
+ VDPU_REG_REFIDX0_ACTIVE(pps->num_ref_idx_l0_default_active_minus1 + 1) |
+ VDPU_REG_POC_LENGTH(dec_param->pic_order_cnt_bit_size);
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(114));
+
+ reg = VDPU_REG_IDR_PIC_E(dec_param->flags & V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC) |
+ VDPU_REG_DIR_8X8_INFER_E(sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE) |
+ VDPU_REG_BLACKWHITE_E(sps->profile_idc >= 100 && sps->chroma_format_idc == 0) |
+ VDPU_REG_CABAC_E(pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE) |
+ VDPU_REG_WEIGHT_PRED_E(pps->flags & V4L2_H264_PPS_FLAG_WEIGHTED_PRED) |
+ VDPU_REG_CONST_INTRA_E(pps->flags & V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED) |
+ VDPU_REG_8X8TRANS_FLAG_E(pps->flags & V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE) |
+ VDPU_REG_TYPE1_QUANT_E(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT) |
+ VDPU_REG_FIELDPIC_FLAG_E(!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY));
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(115));
+}
+
+static void set_ref(struct hantro_ctx *ctx)
+{
+ const u8 *b0_reflist, *b1_reflist, *p_reflist;
+ struct hantro_dev *vpu = ctx->dev;
+ u32 reg;
+ int i;
+
+ b0_reflist = ctx->h264_dec.reflists.b0;
+ b1_reflist = ctx->h264_dec.reflists.b1;
+ p_reflist = ctx->h264_dec.reflists.p;
+
+ reg = VDPU_REG_PINIT_RLIST_F9(p_reflist[9]) |
+ VDPU_REG_PINIT_RLIST_F8(p_reflist[8]) |
+ VDPU_REG_PINIT_RLIST_F7(p_reflist[7]) |
+ VDPU_REG_PINIT_RLIST_F6(p_reflist[6]) |
+ VDPU_REG_PINIT_RLIST_F5(p_reflist[5]) |
+ VDPU_REG_PINIT_RLIST_F4(p_reflist[4]);
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(74));
+
+ reg = VDPU_REG_PINIT_RLIST_F15(p_reflist[15]) |
+ VDPU_REG_PINIT_RLIST_F14(p_reflist[14]) |
+ VDPU_REG_PINIT_RLIST_F13(p_reflist[13]) |
+ VDPU_REG_PINIT_RLIST_F12(p_reflist[12]) |
+ VDPU_REG_PINIT_RLIST_F11(p_reflist[11]) |
+ VDPU_REG_PINIT_RLIST_F10(p_reflist[10]);
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(75));
+
+ reg = VDPU_REG_REFER1_NBR(hantro_h264_get_ref_nbr(ctx, 1)) |
+ VDPU_REG_REFER0_NBR(hantro_h264_get_ref_nbr(ctx, 0));
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(76));
+
+ reg = VDPU_REG_REFER3_NBR(hantro_h264_get_ref_nbr(ctx, 3)) |
+ VDPU_REG_REFER2_NBR(hantro_h264_get_ref_nbr(ctx, 2));
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(77));
+
+ reg = VDPU_REG_REFER5_NBR(hantro_h264_get_ref_nbr(ctx, 5)) |
+ VDPU_REG_REFER4_NBR(hantro_h264_get_ref_nbr(ctx, 4));
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(78));
+
+ reg = VDPU_REG_REFER7_NBR(hantro_h264_get_ref_nbr(ctx, 7)) |
+ VDPU_REG_REFER6_NBR(hantro_h264_get_ref_nbr(ctx, 6));
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(79));
+
+ reg = VDPU_REG_REFER9_NBR(hantro_h264_get_ref_nbr(ctx, 9)) |
+ VDPU_REG_REFER8_NBR(hantro_h264_get_ref_nbr(ctx, 8));
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(80));
+
+ reg = VDPU_REG_REFER11_NBR(hantro_h264_get_ref_nbr(ctx, 11)) |
+ VDPU_REG_REFER10_NBR(hantro_h264_get_ref_nbr(ctx, 10));
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(81));
+
+ reg = VDPU_REG_REFER13_NBR(hantro_h264_get_ref_nbr(ctx, 13)) |
+ VDPU_REG_REFER12_NBR(hantro_h264_get_ref_nbr(ctx, 12));
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(82));
+
+ reg = VDPU_REG_REFER15_NBR(hantro_h264_get_ref_nbr(ctx, 15)) |
+ VDPU_REG_REFER14_NBR(hantro_h264_get_ref_nbr(ctx, 14));
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(83));
+
+ reg = VDPU_REG_BINIT_RLIST_F5(b0_reflist[5]) |
+ VDPU_REG_BINIT_RLIST_F4(b0_reflist[4]) |
+ VDPU_REG_BINIT_RLIST_F3(b0_reflist[3]) |
+ VDPU_REG_BINIT_RLIST_F2(b0_reflist[2]) |
+ VDPU_REG_BINIT_RLIST_F1(b0_reflist[1]) |
+ VDPU_REG_BINIT_RLIST_F0(b0_reflist[0]);
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(100));
+
+ reg = VDPU_REG_BINIT_RLIST_F11(b0_reflist[11]) |
+ VDPU_REG_BINIT_RLIST_F10(b0_reflist[10]) |
+ VDPU_REG_BINIT_RLIST_F9(b0_reflist[9]) |
+ VDPU_REG_BINIT_RLIST_F8(b0_reflist[8]) |
+ VDPU_REG_BINIT_RLIST_F7(b0_reflist[7]) |
+ VDPU_REG_BINIT_RLIST_F6(b0_reflist[6]);
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(101));
+
+ reg = VDPU_REG_BINIT_RLIST_F15(b0_reflist[15]) |
+ VDPU_REG_BINIT_RLIST_F14(b0_reflist[14]) |
+ VDPU_REG_BINIT_RLIST_F13(b0_reflist[13]) |
+ VDPU_REG_BINIT_RLIST_F12(b0_reflist[12]);
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(102));
+
+ reg = VDPU_REG_BINIT_RLIST_B5(b1_reflist[5]) |
+ VDPU_REG_BINIT_RLIST_B4(b1_reflist[4]) |
+ VDPU_REG_BINIT_RLIST_B3(b1_reflist[3]) |
+ VDPU_REG_BINIT_RLIST_B2(b1_reflist[2]) |
+ VDPU_REG_BINIT_RLIST_B1(b1_reflist[1]) |
+ VDPU_REG_BINIT_RLIST_B0(b1_reflist[0]);
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(103));
+
+ reg = VDPU_REG_BINIT_RLIST_B11(b1_reflist[11]) |
+ VDPU_REG_BINIT_RLIST_B10(b1_reflist[10]) |
+ VDPU_REG_BINIT_RLIST_B9(b1_reflist[9]) |
+ VDPU_REG_BINIT_RLIST_B8(b1_reflist[8]) |
+ VDPU_REG_BINIT_RLIST_B7(b1_reflist[7]) |
+ VDPU_REG_BINIT_RLIST_B6(b1_reflist[6]);
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(104));
+
+ reg = VDPU_REG_BINIT_RLIST_B15(b1_reflist[15]) |
+ VDPU_REG_BINIT_RLIST_B14(b1_reflist[14]) |
+ VDPU_REG_BINIT_RLIST_B13(b1_reflist[13]) |
+ VDPU_REG_BINIT_RLIST_B12(b1_reflist[12]);
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(105));
+
+ reg = VDPU_REG_PINIT_RLIST_F3(p_reflist[3]) |
+ VDPU_REG_PINIT_RLIST_F2(p_reflist[2]) |
+ VDPU_REG_PINIT_RLIST_F1(p_reflist[1]) |
+ VDPU_REG_PINIT_RLIST_F0(p_reflist[0]);
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(106));
+
+ reg = VDPU_REG_REFER_LTERM_E(ctx->h264_dec.dpb_longterm);
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(107));
+
+ reg = VDPU_REG_REFER_VALID_E(ctx->h264_dec.dpb_valid);
+ vdpu_write_relaxed(vpu, reg, VDPU_SWREG(108));
+
+ /* Set up addresses of DPB buffers. */
+ for (i = 0; i < HANTRO_H264_DPB_SIZE; i++) {
+ dma_addr_t dma_addr = hantro_h264_get_ref_buf(ctx, i);
+
+ vdpu_write_relaxed(vpu, dma_addr, VDPU_REG_REFER_BASE(i));
+ }
+}
+
+static void set_buffers(struct hantro_ctx *ctx, struct vb2_v4l2_buffer *src_buf)
+{
+ const struct hantro_h264_dec_ctrls *ctrls = &ctx->h264_dec.ctrls;
+ struct vb2_v4l2_buffer *dst_buf;
+ struct hantro_dev *vpu = ctx->dev;
+ dma_addr_t src_dma, dst_dma;
+ size_t offset = 0;
+
+ /* Source (stream) buffer. */
+ src_dma = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
+ vdpu_write_relaxed(vpu, src_dma, VDPU_REG_RLC_VLC_BASE);
+
+ /* Destination (decoded frame) buffer. */
+ dst_buf = hantro_get_dst_buf(ctx);
+ dst_dma = hantro_get_dec_buf_addr(ctx, &dst_buf->vb2_buf);
+ /* Adjust dma addr to start at second line for bottom field */
+ if (ctrls->decode->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD)
+ offset = ALIGN(ctx->src_fmt.width, MB_DIM);
+ vdpu_write_relaxed(vpu, dst_dma + offset, VDPU_REG_DEC_OUT_BASE);
+
+ /* Higher profiles require DMV buffer appended to reference frames. */
+ if (ctrls->sps->profile_idc > 66 && ctrls->decode->nal_ref_idc) {
+ unsigned int bytes_per_mb = 384;
+
+ /* DMV buffer for monochrome start directly after Y-plane */
+ if (ctrls->sps->profile_idc >= 100 &&
+ ctrls->sps->chroma_format_idc == 0)
+ bytes_per_mb = 256;
+ offset = bytes_per_mb * MB_WIDTH(ctx->src_fmt.width) *
+ MB_HEIGHT(ctx->src_fmt.height);
+
+ /*
+ * DMV buffer is split in two for field encoded frames,
+ * adjust offset for bottom field
+ */
+ if (ctrls->decode->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD)
+ offset += 32 * MB_WIDTH(ctx->src_fmt.width) *
+ MB_HEIGHT(ctx->src_fmt.height);
+ vdpu_write_relaxed(vpu, dst_dma + offset, VDPU_REG_DIR_MV_BASE);
+ }
+
+ /* Auxiliary buffer prepared in hantro_g1_h264_dec_prepare_table(). */
+ vdpu_write_relaxed(vpu, ctx->h264_dec.priv.dma, VDPU_REG_QTABLE_BASE);
+}
+
+int rockchip_vpu2_h264_dec_run(struct hantro_ctx *ctx)
+{
+ struct hantro_dev *vpu = ctx->dev;
+ struct vb2_v4l2_buffer *src_buf;
+ u32 reg;
+ int ret;
+
+ /* Prepare the H264 decoder context. */
+ ret = hantro_h264_dec_prepare_run(ctx);
+ if (ret)
+ return ret;
+
+ src_buf = hantro_get_src_buf(ctx);
+ set_params(ctx, src_buf);
+ set_ref(ctx);
+ set_buffers(ctx, src_buf);
+
+ hantro_end_prepare_run(ctx);
+
+ /* Start decoding! */
+ reg = vdpu_read(vpu, VDPU_SWREG(57)) | VDPU_REG_DEC_E(1);
+ vdpu_write(vpu, reg, VDPU_SWREG(57));
+
+ return 0;
+}
}
static void cfg_ref(struct hantro_ctx *ctx,
- const struct v4l2_ctrl_vp8_frame *hdr)
+ const struct v4l2_ctrl_vp8_frame *hdr,
+ struct vb2_v4l2_buffer *vb2_dst)
{
struct hantro_dev *vpu = ctx->dev;
- struct vb2_v4l2_buffer *vb2_dst;
dma_addr_t ref;
- vb2_dst = hantro_get_dst_buf(ctx);
-
ref = hantro_get_ref(ctx, hdr->last_frame_ts);
- if (!ref)
+ if (!ref) {
+ vpu_debug(0, "failed to find last frame ts=%llu\n",
+ hdr->last_frame_ts);
ref = vb2_dma_contig_plane_dma_addr(&vb2_dst->vb2_buf, 0);
+ }
vdpu_write_relaxed(vpu, ref, VDPU_REG_VP8_ADDR_REF0);
ref = hantro_get_ref(ctx, hdr->golden_frame_ts);
- WARN_ON(!ref && hdr->golden_frame_ts);
+ if (!ref && hdr->golden_frame_ts)
+ vpu_debug(0, "failed to find golden frame ts=%llu\n",
+ hdr->golden_frame_ts);
if (!ref)
ref = vb2_dma_contig_plane_dma_addr(&vb2_dst->vb2_buf, 0);
if (hdr->flags & V4L2_VP8_FRAME_FLAG_SIGN_BIAS_GOLDEN)
vdpu_write_relaxed(vpu, ref, VDPU_REG_VP8_ADDR_REF2_5(2));
ref = hantro_get_ref(ctx, hdr->alt_frame_ts);
- WARN_ON(!ref && hdr->alt_frame_ts);
+ if (!ref && hdr->alt_frame_ts)
+ vpu_debug(0, "failed to find alt frame ts=%llu\n",
+ hdr->alt_frame_ts);
if (!ref)
ref = vb2_dma_contig_plane_dma_addr(&vb2_dst->vb2_buf, 0);
if (hdr->flags & V4L2_VP8_FRAME_FLAG_SIGN_BIAS_ALT)
}
static void cfg_buffers(struct hantro_ctx *ctx,
- const struct v4l2_ctrl_vp8_frame *hdr)
+ const struct v4l2_ctrl_vp8_frame *hdr,
+ struct vb2_v4l2_buffer *vb2_dst)
{
const struct v4l2_vp8_segment *seg = &hdr->segment;
struct hantro_dev *vpu = ctx->dev;
- struct vb2_v4l2_buffer *vb2_dst;
dma_addr_t dst_dma;
u32 reg;
- vb2_dst = hantro_get_dst_buf(ctx);
-
/* Set probability table buffer address */
vdpu_write_relaxed(vpu, ctx->vp8_dec.prob_tbl.dma,
VDPU_REG_ADDR_QTABLE);
{
const struct v4l2_ctrl_vp8_frame *hdr;
struct hantro_dev *vpu = ctx->dev;
+ struct vb2_v4l2_buffer *vb2_dst;
size_t height = ctx->dst_fmt.height;
size_t width = ctx->dst_fmt.width;
u32 mb_width, mb_height;
cfg_qp(ctx, hdr);
cfg_parts(ctx, hdr);
cfg_tap(ctx, hdr);
- cfg_ref(ctx, hdr);
- cfg_buffers(ctx, hdr);
+
+ vb2_dst = hantro_get_dst_buf(ctx);
+ cfg_ref(ctx, hdr, vb2_dst);
+ cfg_buffers(ctx, hdr, vb2_dst);
hantro_end_prepare_run(ctx);
.codec_mode = HANTRO_MODE_NONE,
},
{
+ .fourcc = V4L2_PIX_FMT_H264_SLICE,
+ .codec_mode = HANTRO_MODE_H264_DEC,
+ .max_depth = 2,
+ .frmsize = {
+ .min_width = 48,
+ .max_width = 1920,
+ .step_width = MB_DIM,
+ .min_height = 48,
+ .max_height = 1088,
+ .step_height = MB_DIM,
+ },
+ },
+ {
.fourcc = V4L2_PIX_FMT_MPEG2_SLICE,
.codec_mode = HANTRO_MODE_MPEG2_DEC,
.max_depth = 2,
.init = hantro_jpeg_enc_init,
.exit = hantro_jpeg_enc_exit,
},
+ [HANTRO_MODE_H264_DEC] = {
+ .run = rockchip_vpu2_h264_dec_run,
+ .reset = rockchip_vpu2_dec_reset,
+ .init = hantro_h264_dec_init,
+ .exit = hantro_h264_dec_exit,
+ },
[HANTRO_MODE_MPEG2_DEC] = {
.run = rockchip_vpu2_mpeg2_dec_run,
.reset = rockchip_vpu2_dec_reset,
"aclk", "hclk"
};
+/* VDPU1/VEPU1 */
+
const struct hantro_variant rk3036_vpu_variant = {
.dec_offset = 0x400,
.dec_fmts = rk3066_vpu_dec_fmts,
.num_clocks = ARRAY_SIZE(rockchip_vpu_clk_names)
};
+/* VDPU2/VEPU2 */
+
const struct hantro_variant rk3328_vpu_variant = {
.dec_offset = 0x400,
.dec_fmts = rk3399_vpu_dec_fmts,
.num_dec_fmts = ARRAY_SIZE(rk3399_vpu_dec_fmts),
- .codec = HANTRO_MPEG2_DECODER | HANTRO_VP8_DECODER,
+ .codec = HANTRO_MPEG2_DECODER | HANTRO_VP8_DECODER |
+ HANTRO_H264_DECODER,
.codec_ops = rk3399_vpu_codec_ops,
.irqs = rockchip_vdpu2_irqs,
.num_irqs = ARRAY_SIZE(rockchip_vdpu2_irqs),
.clk_names = rockchip_vpu_clk_names,
.num_clocks = ARRAY_SIZE(rockchip_vpu_clk_names)
};
+
+const struct hantro_variant px30_vpu_variant = {
+ .enc_offset = 0x0,
+ .enc_fmts = rockchip_vpu_enc_fmts,
+ .num_enc_fmts = ARRAY_SIZE(rockchip_vpu_enc_fmts),
+ .dec_offset = 0x400,
+ .dec_fmts = rk3399_vpu_dec_fmts,
+ .num_dec_fmts = ARRAY_SIZE(rk3399_vpu_dec_fmts),
+ .codec = HANTRO_JPEG_ENCODER | HANTRO_MPEG2_DECODER |
+ HANTRO_VP8_DECODER | HANTRO_H264_DECODER,
+ .codec_ops = rk3399_vpu_codec_ops,
+ .irqs = rockchip_vpu2_irqs,
+ .num_irqs = ARRAY_SIZE(rockchip_vpu2_irqs),
+ .init = rk3036_vpu_hw_init,
+ .clk_names = rockchip_vpu_clk_names,
+ .num_clocks = ARRAY_SIZE(rockchip_vpu_clk_names)
+};
},
};
-static int sama5d4_hw_init(struct hantro_dev *vpu)
-{
- return 0;
-}
-
/*
* Supported codec ops.
*/
.codec = HANTRO_MPEG2_DECODER | HANTRO_VP8_DECODER |
HANTRO_H264_DECODER,
.codec_ops = sama5d4_vdec_codec_ops,
- .init = sama5d4_hw_init,
.irqs = sama5d4_irqs,
.num_irqs = ARRAY_SIZE(sama5d4_irqs),
.clk_names = sama5d4_clk_names,
obj-$(CONFIG_VIDEO_IMX7_CSI) += imx7-media-csi.o
obj-$(CONFIG_VIDEO_IMX7_CSI) += imx7-mipi-csis.o
+obj-$(CONFIG_VIDEO_IMX7_CSI) += imx8mq-mipi-csi2.o
vb->timestamp = ktime_get_ns();
vb2_buffer_done(vb, return_status);
+ csi->active_vb2_buf[i] = NULL;
}
}
}
return 0;
}
-static void imx7_csi_dma_cleanup(struct imx7_csi *csi)
+static void imx7_csi_dma_cleanup(struct imx7_csi *csi,
+ enum vb2_buffer_state return_status)
{
- imx7_csi_dma_unsetup_vb2_buf(csi, VB2_BUF_STATE_ERROR);
+ imx7_csi_dma_unsetup_vb2_buf(csi, return_status);
imx_media_free_dma_buf(csi->dev, &csi->underrun_buf);
}
struct v4l2_pix_format *out_pix = &vdev->fmt;
int width = out_pix->width;
u32 stride = 0;
+ u32 cr3 = BIT_FRMCNT_RST;
u32 cr1, cr18;
cr18 = imx7_csi_reg_read(csi, CSI_CSICR18);
case MEDIA_BUS_FMT_SGBRG10_1X10:
case MEDIA_BUS_FMT_SGRBG10_1X10:
case MEDIA_BUS_FMT_SRGGB10_1X10:
+ cr3 |= BIT_TWO_8BIT_SENSOR;
cr18 |= BIT_MIPI_DATA_FORMAT_RAW10;
break;
case MEDIA_BUS_FMT_Y12_1X12:
case MEDIA_BUS_FMT_SGBRG12_1X12:
case MEDIA_BUS_FMT_SGRBG12_1X12:
case MEDIA_BUS_FMT_SRGGB12_1X12:
+ cr3 |= BIT_TWO_8BIT_SENSOR;
cr18 |= BIT_MIPI_DATA_FORMAT_RAW12;
break;
case MEDIA_BUS_FMT_Y14_1X14:
case MEDIA_BUS_FMT_SGBRG14_1X14:
case MEDIA_BUS_FMT_SGRBG14_1X14:
case MEDIA_BUS_FMT_SRGGB14_1X14:
+ cr3 |= BIT_TWO_8BIT_SENSOR;
cr18 |= BIT_MIPI_DATA_FORMAT_RAW14;
break;
/*
cr18 |= BIT_MIPI_DATA_FORMAT_YUV422_8B;
break;
}
-
- switch (out_pix->pixelformat) {
- case V4L2_PIX_FMT_Y10:
- case V4L2_PIX_FMT_Y12:
- case V4L2_PIX_FMT_SBGGR8:
- case V4L2_PIX_FMT_SGBRG8:
- case V4L2_PIX_FMT_SGRBG8:
- case V4L2_PIX_FMT_SRGGB8:
- case V4L2_PIX_FMT_SBGGR16:
- case V4L2_PIX_FMT_SGBRG16:
- case V4L2_PIX_FMT_SGRBG16:
- case V4L2_PIX_FMT_SRGGB16:
- cr1 |= BIT_PIXEL_BIT;
- break;
- }
}
imx7_csi_reg_write(csi, cr1, CSI_CSICR1);
imx7_csi_reg_write(csi, BIT_DMA_BURST_TYPE_RFF_INCR16, CSI_CSICR2);
- imx7_csi_reg_write(csi, BIT_FRMCNT_RST, CSI_CSICR3);
+ imx7_csi_reg_write(csi, cr3, CSI_CSICR3);
imx7_csi_reg_write(csi, cr18, CSI_CSICR18);
imx7_csi_reg_write(csi, (width * out_pix->height) >> 2, CSI_CSIRXCNT);
return 0;
}
-static void imx7_csi_deinit(struct imx7_csi *csi)
+static void imx7_csi_deinit(struct imx7_csi *csi,
+ enum vb2_buffer_state return_status)
{
- imx7_csi_dma_cleanup(csi);
+ imx7_csi_dma_cleanup(csi, return_status);
imx7_csi_init_default(csi);
imx7_csi_dmareq_rff_disable(csi);
clk_disable_unprepare(csi->mclk);
ret = v4l2_subdev_call(csi->src_sd, video, s_stream, 1);
if (ret < 0) {
- imx7_csi_deinit(csi);
+ imx7_csi_deinit(csi, VB2_BUF_STATE_QUEUED);
goto out_unlock;
}
v4l2_subdev_call(csi->src_sd, video, s_stream, 0);
- imx7_csi_deinit(csi);
+ imx7_csi_deinit(csi, VB2_BUF_STATE_ERROR);
}
csi->is_streaming = !!enable;
case 6:
align = 2;
break;
- case 1:
- case 3:
- case 5:
- case 7:
+ default:
+ /* 1, 3, 5, 7 */
align = 3;
break;
}
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * NXP i.MX8MQ SoC series MIPI-CSI2 receiver driver
+ *
+ * Copyright (C) 2021 Purism SPC
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interconnect.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/spinlock.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-subdev.h>
+
+#define MIPI_CSI2_DRIVER_NAME "imx8mq-mipi-csi2"
+#define MIPI_CSI2_SUBDEV_NAME MIPI_CSI2_DRIVER_NAME
+
+#define MIPI_CSI2_PAD_SINK 0
+#define MIPI_CSI2_PAD_SOURCE 1
+#define MIPI_CSI2_PADS_NUM 2
+
+#define MIPI_CSI2_DEF_PIX_WIDTH 640
+#define MIPI_CSI2_DEF_PIX_HEIGHT 480
+
+/* Register map definition */
+
+/* i.MX8MQ CSI-2 controller CSR */
+#define CSI2RX_CFG_NUM_LANES 0x100
+#define CSI2RX_CFG_DISABLE_DATA_LANES 0x104
+#define CSI2RX_BIT_ERR 0x108
+#define CSI2RX_IRQ_STATUS 0x10c
+#define CSI2RX_IRQ_MASK 0x110
+#define CSI2RX_IRQ_MASK_ALL 0x1ff
+#define CSI2RX_IRQ_MASK_ULPS_STATUS_CHANGE 0x8
+#define CSI2RX_ULPS_STATUS 0x114
+#define CSI2RX_PPI_ERRSOT_HS 0x118
+#define CSI2RX_PPI_ERRSOTSYNC_HS 0x11c
+#define CSI2RX_PPI_ERRESC 0x120
+#define CSI2RX_PPI_ERRSYNCESC 0x124
+#define CSI2RX_PPI_ERRCONTROL 0x128
+#define CSI2RX_CFG_DISABLE_PAYLOAD_0 0x12c
+#define CSI2RX_CFG_VID_VC_IGNORE 0x180
+#define CSI2RX_CFG_VID_VC 0x184
+#define CSI2RX_CFG_VID_P_FIFO_SEND_LEVEL 0x188
+#define CSI2RX_CFG_DISABLE_PAYLOAD_1 0x130
+
+enum {
+ ST_POWERED = 1,
+ ST_STREAMING = 2,
+ ST_SUSPENDED = 4,
+};
+
+enum imx8mq_mipi_csi_clk {
+ CSI2_CLK_CORE,
+ CSI2_CLK_ESC,
+ CSI2_CLK_UI,
+ CSI2_NUM_CLKS,
+};
+
+static const char * const imx8mq_mipi_csi_clk_id[CSI2_NUM_CLKS] = {
+ [CSI2_CLK_CORE] = "core",
+ [CSI2_CLK_ESC] = "esc",
+ [CSI2_CLK_UI] = "ui",
+};
+
+#define CSI2_NUM_CLKS ARRAY_SIZE(imx8mq_mipi_csi_clk_id)
+
+#define GPR_CSI2_1_RX_ENABLE BIT(13)
+#define GPR_CSI2_1_VID_INTFC_ENB BIT(12)
+#define GPR_CSI2_1_HSEL BIT(10)
+#define GPR_CSI2_1_CONT_CLK_MODE BIT(8)
+#define GPR_CSI2_1_S_PRG_RXHS_SETTLE(x) (((x) & 0x3f) << 2)
+
+/*
+ * The send level configures the number of entries that must accumulate in
+ * the Pixel FIFO before the data will be transferred to the video output.
+ * The exact value needed for this configuration is dependent on the rate at
+ * which the sensor transfers data to the CSI-2 Controller and the user
+ * video clock.
+ *
+ * The calculation is the classical rate-in rate-out type of problem: If the
+ * video bandwidth is 10% faster than the incoming mipi data and the video
+ * line length is 500 pixels, then the fifo should be allowed to fill
+ * 10% of the line length or 50 pixels. If the gap data is ok, then the level
+ * can be set to 16 and ignored.
+ */
+#define CSI2RX_SEND_LEVEL 64
+
+struct csi_state {
+ struct device *dev;
+ void __iomem *regs;
+ struct clk_bulk_data clks[CSI2_NUM_CLKS];
+ struct reset_control *rst;
+ struct regulator *mipi_phy_regulator;
+
+ struct v4l2_subdev sd;
+ struct media_pad pads[MIPI_CSI2_PADS_NUM];
+ struct v4l2_async_notifier notifier;
+ struct v4l2_subdev *src_sd;
+
+ struct v4l2_fwnode_bus_mipi_csi2 bus;
+
+ struct mutex lock; /* Protect csi2_fmt, format_mbus, state, hs_settle */
+ const struct csi2_pix_format *csi2_fmt;
+ struct v4l2_mbus_framefmt format_mbus[MIPI_CSI2_PADS_NUM];
+ u32 state;
+ u32 hs_settle;
+
+ struct regmap *phy_gpr;
+ u8 phy_gpr_reg;
+
+ struct icc_path *icc_path;
+ s32 icc_path_bw;
+};
+
+/* -----------------------------------------------------------------------------
+ * Format helpers
+ */
+
+struct csi2_pix_format {
+ u32 code;
+ u8 width;
+};
+
+static const struct csi2_pix_format imx8mq_mipi_csi_formats[] = {
+ /* RAW (Bayer and greyscale) formats. */
+ {
+ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .width = 8,
+ }, {
+ .code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .width = 8,
+ }, {
+ .code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .width = 8,
+ }, {
+ .code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .width = 8,
+ }, {
+ .code = MEDIA_BUS_FMT_Y8_1X8,
+ .width = 8,
+ }, {
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .width = 10,
+ }, {
+ .code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .width = 10,
+ }, {
+ .code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .width = 10,
+ }, {
+ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .width = 10,
+ }, {
+ .code = MEDIA_BUS_FMT_Y10_1X10,
+ .width = 10,
+ }, {
+ .code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .width = 12,
+ }, {
+ .code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .width = 12,
+ }, {
+ .code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .width = 12,
+ }, {
+ .code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .width = 12,
+ }, {
+ .code = MEDIA_BUS_FMT_Y12_1X12,
+ .width = 12,
+ }, {
+ .code = MEDIA_BUS_FMT_SBGGR14_1X14,
+ .width = 14,
+ }, {
+ .code = MEDIA_BUS_FMT_SGBRG14_1X14,
+ .width = 14,
+ }, {
+ .code = MEDIA_BUS_FMT_SGRBG14_1X14,
+ .width = 14,
+ }, {
+ .code = MEDIA_BUS_FMT_SRGGB14_1X14,
+ .width = 14,
+ }, {
+ /* YUV formats */
+ .code = MEDIA_BUS_FMT_YUYV8_2X8,
+ .width = 16,
+ }, {
+ .code = MEDIA_BUS_FMT_YUYV8_1X16,
+ .width = 16,
+ }
+};
+
+static const struct csi2_pix_format *find_csi2_format(u32 code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(imx8mq_mipi_csi_formats); i++)
+ if (code == imx8mq_mipi_csi_formats[i].code)
+ return &imx8mq_mipi_csi_formats[i];
+ return NULL;
+}
+
+/* -----------------------------------------------------------------------------
+ * Hardware configuration
+ */
+
+static inline void imx8mq_mipi_csi_write(struct csi_state *state, u32 reg, u32 val)
+{
+ writel(val, state->regs + reg);
+}
+
+static int imx8mq_mipi_csi_sw_reset(struct csi_state *state)
+{
+ int ret;
+
+ /*
+ * these are most likely self-clearing reset bits. to make it
+ * more clear, the reset-imx7 driver should implement the
+ * .reset() operation.
+ */
+ ret = reset_control_assert(state->rst);
+ if (ret < 0) {
+ dev_err(state->dev, "Failed to assert resets: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void imx8mq_mipi_csi_system_enable(struct csi_state *state, int on)
+{
+ if (!on) {
+ imx8mq_mipi_csi_write(state, CSI2RX_CFG_DISABLE_DATA_LANES, 0xf);
+ return;
+ }
+
+ regmap_update_bits(state->phy_gpr,
+ state->phy_gpr_reg,
+ 0x3fff,
+ GPR_CSI2_1_RX_ENABLE |
+ GPR_CSI2_1_VID_INTFC_ENB |
+ GPR_CSI2_1_HSEL |
+ GPR_CSI2_1_CONT_CLK_MODE |
+ GPR_CSI2_1_S_PRG_RXHS_SETTLE(state->hs_settle));
+}
+
+static void imx8mq_mipi_csi_set_params(struct csi_state *state)
+{
+ int lanes = state->bus.num_data_lanes;
+
+ imx8mq_mipi_csi_write(state, CSI2RX_CFG_NUM_LANES, lanes - 1);
+ imx8mq_mipi_csi_write(state, CSI2RX_CFG_DISABLE_DATA_LANES,
+ (0xf << lanes) & 0xf);
+ imx8mq_mipi_csi_write(state, CSI2RX_IRQ_MASK, CSI2RX_IRQ_MASK_ALL);
+ /*
+ * 0x180 bit 0 controls the Virtual Channel behaviour: when set the
+ * interface ignores the Virtual Channel (VC) field in received packets;
+ * when cleared it causes the interface to only accept packets whose VC
+ * matches the value to which VC is set at offset 0x184.
+ */
+ imx8mq_mipi_csi_write(state, CSI2RX_CFG_VID_VC_IGNORE, 1);
+ imx8mq_mipi_csi_write(state, CSI2RX_CFG_VID_P_FIFO_SEND_LEVEL,
+ CSI2RX_SEND_LEVEL);
+}
+
+static int imx8mq_mipi_csi_clk_enable(struct csi_state *state)
+{
+ return clk_bulk_prepare_enable(CSI2_NUM_CLKS, state->clks);
+}
+
+static void imx8mq_mipi_csi_clk_disable(struct csi_state *state)
+{
+ clk_bulk_disable_unprepare(CSI2_NUM_CLKS, state->clks);
+}
+
+static int imx8mq_mipi_csi_clk_get(struct csi_state *state)
+{
+ unsigned int i;
+
+ for (i = 0; i < CSI2_NUM_CLKS; i++)
+ state->clks[i].id = imx8mq_mipi_csi_clk_id[i];
+
+ return devm_clk_bulk_get(state->dev, CSI2_NUM_CLKS, state->clks);
+}
+
+static int imx8mq_mipi_csi_calc_hs_settle(struct csi_state *state)
+{
+ s64 link_freq;
+ u32 lane_rate;
+ unsigned long esc_clk_rate;
+ u32 min_ths_settle, max_ths_settle, ths_settle_ns, esc_clk_period_ns;
+
+ /* Calculate the line rate from the pixel rate. */
+ link_freq = v4l2_get_link_freq(state->src_sd->ctrl_handler,
+ state->csi2_fmt->width,
+ state->bus.num_data_lanes * 2);
+ if (link_freq < 0) {
+ dev_err(state->dev, "Unable to obtain link frequency: %d\n",
+ (int)link_freq);
+ return link_freq;
+ }
+
+ lane_rate = link_freq * 2;
+ if (lane_rate < 80000000 || lane_rate > 1500000000) {
+ dev_dbg(state->dev, "Out-of-bound lane rate %u\n", lane_rate);
+ return -EINVAL;
+ }
+
+ /*
+ * The D-PHY specification requires Ths-settle to be in the range
+ * 85ns + 6*UI to 140ns + 10*UI, with the unit interval UI being half
+ * the clock period.
+ *
+ * The Ths-settle value is expressed in the hardware as a multiple of
+ * the Esc clock period:
+ *
+ * Ths-settle = (PRG_RXHS_SETTLE + 1) * Tperiod of RxClkInEsc
+ *
+ * Due to the one cycle inaccuracy introduced by rounding, the
+ * documentation recommends picking a value away from the boundaries.
+ * Let's pick the average.
+ */
+ esc_clk_rate = clk_get_rate(state->clks[CSI2_CLK_ESC].clk);
+ if (!esc_clk_rate) {
+ dev_err(state->dev, "Could not get esc clock rate.\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(state->dev, "esc clk rate: %lu\n", esc_clk_rate);
+ esc_clk_period_ns = 1000000000 / esc_clk_rate;
+
+ min_ths_settle = 85 + 6 * 1000000 / (lane_rate / 1000);
+ max_ths_settle = 140 + 10 * 1000000 / (lane_rate / 1000);
+ ths_settle_ns = (min_ths_settle + max_ths_settle) / 2;
+
+ state->hs_settle = ths_settle_ns / esc_clk_period_ns - 1;
+
+ dev_dbg(state->dev, "lane rate %u Ths_settle %u hs_settle %u\n",
+ lane_rate, ths_settle_ns, state->hs_settle);
+
+ return 0;
+}
+
+static int imx8mq_mipi_csi_start_stream(struct csi_state *state)
+{
+ int ret;
+
+ ret = imx8mq_mipi_csi_sw_reset(state);
+ if (ret)
+ return ret;
+
+ imx8mq_mipi_csi_set_params(state);
+ ret = imx8mq_mipi_csi_calc_hs_settle(state);
+ if (ret)
+ return ret;
+
+ imx8mq_mipi_csi_system_enable(state, true);
+
+ return 0;
+}
+
+static void imx8mq_mipi_csi_stop_stream(struct csi_state *state)
+{
+ imx8mq_mipi_csi_system_enable(state, false);
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev operations
+ */
+
+static struct csi_state *mipi_sd_to_csi2_state(struct v4l2_subdev *sdev)
+{
+ return container_of(sdev, struct csi_state, sd);
+}
+
+static int imx8mq_mipi_csi_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct csi_state *state = mipi_sd_to_csi2_state(sd);
+ int ret = 0;
+
+ imx8mq_mipi_csi_write(state, CSI2RX_IRQ_MASK,
+ CSI2RX_IRQ_MASK_ULPS_STATUS_CHANGE);
+
+ if (enable) {
+ ret = pm_runtime_resume_and_get(state->dev);
+ if (ret < 0)
+ return ret;
+ }
+
+ mutex_lock(&state->lock);
+
+ if (enable) {
+ if (state->state & ST_SUSPENDED) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ ret = imx8mq_mipi_csi_start_stream(state);
+ if (ret < 0)
+ goto unlock;
+
+ ret = v4l2_subdev_call(state->src_sd, video, s_stream, 1);
+ if (ret < 0)
+ goto unlock;
+
+ state->state |= ST_STREAMING;
+ } else {
+ v4l2_subdev_call(state->src_sd, video, s_stream, 0);
+ imx8mq_mipi_csi_stop_stream(state);
+ state->state &= ~ST_STREAMING;
+ }
+
+unlock:
+ mutex_unlock(&state->lock);
+
+ if (!enable || ret < 0)
+ pm_runtime_put(state->dev);
+
+ return ret;
+}
+
+static struct v4l2_mbus_framefmt *
+imx8mq_mipi_csi_get_format(struct csi_state *state,
+ struct v4l2_subdev_state *sd_state,
+ enum v4l2_subdev_format_whence which,
+ unsigned int pad)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_format(&state->sd, sd_state, pad);
+
+ return &state->format_mbus[pad];
+}
+
+static int imx8mq_mipi_csi_init_cfg(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state)
+{
+ struct csi_state *state = mipi_sd_to_csi2_state(sd);
+ struct v4l2_mbus_framefmt *fmt_sink;
+ struct v4l2_mbus_framefmt *fmt_source;
+ enum v4l2_subdev_format_whence which;
+
+ which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
+ fmt_sink = imx8mq_mipi_csi_get_format(state, sd_state, which,
+ MIPI_CSI2_PAD_SINK);
+
+ fmt_sink->code = MEDIA_BUS_FMT_SGBRG10_1X10;
+ fmt_sink->width = MIPI_CSI2_DEF_PIX_WIDTH;
+ fmt_sink->height = MIPI_CSI2_DEF_PIX_HEIGHT;
+ fmt_sink->field = V4L2_FIELD_NONE;
+
+ fmt_sink->colorspace = V4L2_COLORSPACE_RAW;
+ fmt_sink->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt_sink->colorspace);
+ fmt_sink->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt_sink->colorspace);
+ fmt_sink->quantization =
+ V4L2_MAP_QUANTIZATION_DEFAULT(false, fmt_sink->colorspace,
+ fmt_sink->ycbcr_enc);
+
+ fmt_source = imx8mq_mipi_csi_get_format(state, sd_state, which,
+ MIPI_CSI2_PAD_SOURCE);
+ *fmt_source = *fmt_sink;
+
+ return 0;
+}
+
+static int imx8mq_mipi_csi_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct csi_state *state = mipi_sd_to_csi2_state(sd);
+ struct v4l2_mbus_framefmt *fmt;
+
+ fmt = imx8mq_mipi_csi_get_format(state, sd_state, sdformat->which,
+ sdformat->pad);
+
+ mutex_lock(&state->lock);
+
+ sdformat->format = *fmt;
+
+ mutex_unlock(&state->lock);
+
+ return 0;
+}
+
+static int imx8mq_mipi_csi_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct csi_state *state = mipi_sd_to_csi2_state(sd);
+
+ /*
+ * We can't transcode in any way, the source format is identical
+ * to the sink format.
+ */
+ if (code->pad == MIPI_CSI2_PAD_SOURCE) {
+ struct v4l2_mbus_framefmt *fmt;
+
+ if (code->index > 0)
+ return -EINVAL;
+
+ fmt = imx8mq_mipi_csi_get_format(state, sd_state, code->which,
+ code->pad);
+ code->code = fmt->code;
+ return 0;
+ }
+
+ if (code->pad != MIPI_CSI2_PAD_SINK)
+ return -EINVAL;
+
+ if (code->index >= ARRAY_SIZE(imx8mq_mipi_csi_formats))
+ return -EINVAL;
+
+ code->code = imx8mq_mipi_csi_formats[code->index].code;
+
+ return 0;
+}
+
+static int imx8mq_mipi_csi_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct csi_state *state = mipi_sd_to_csi2_state(sd);
+ struct csi2_pix_format const *csi2_fmt;
+ struct v4l2_mbus_framefmt *fmt;
+
+ /*
+ * The device can't transcode in any way, the source format can't be
+ * modified.
+ */
+ if (sdformat->pad == MIPI_CSI2_PAD_SOURCE)
+ return imx8mq_mipi_csi_get_fmt(sd, sd_state, sdformat);
+
+ if (sdformat->pad != MIPI_CSI2_PAD_SINK)
+ return -EINVAL;
+
+ csi2_fmt = find_csi2_format(sdformat->format.code);
+ if (!csi2_fmt)
+ csi2_fmt = &imx8mq_mipi_csi_formats[0];
+
+ fmt = imx8mq_mipi_csi_get_format(state, sd_state, sdformat->which,
+ sdformat->pad);
+
+ mutex_lock(&state->lock);
+
+ fmt->code = csi2_fmt->code;
+ fmt->width = sdformat->format.width;
+ fmt->height = sdformat->format.height;
+
+ sdformat->format = *fmt;
+
+ /* Propagate the format from sink to source. */
+ fmt = imx8mq_mipi_csi_get_format(state, sd_state, sdformat->which,
+ MIPI_CSI2_PAD_SOURCE);
+ *fmt = sdformat->format;
+
+ /* Store the CSI2 format descriptor for active formats. */
+ if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ state->csi2_fmt = csi2_fmt;
+
+ mutex_unlock(&state->lock);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_video_ops imx8mq_mipi_csi_video_ops = {
+ .s_stream = imx8mq_mipi_csi_s_stream,
+};
+
+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 = imx8mq_mipi_csi_get_fmt,
+ .set_fmt = imx8mq_mipi_csi_set_fmt,
+};
+
+static const struct v4l2_subdev_ops imx8mq_mipi_csi_subdev_ops = {
+ .video = &imx8mq_mipi_csi_video_ops,
+ .pad = &imx8mq_mipi_csi_pad_ops,
+};
+
+/* -----------------------------------------------------------------------------
+ * Media entity operations
+ */
+
+static const struct media_entity_operations imx8mq_mipi_csi_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+ .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1,
+};
+
+/* -----------------------------------------------------------------------------
+ * Async subdev notifier
+ */
+
+static struct csi_state *
+mipi_notifier_to_csi2_state(struct v4l2_async_notifier *n)
+{
+ return container_of(n, struct csi_state, notifier);
+}
+
+static int imx8mq_mipi_csi_notify_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_subdev *asd)
+{
+ struct csi_state *state = mipi_notifier_to_csi2_state(notifier);
+ struct media_pad *sink = &state->sd.entity.pads[MIPI_CSI2_PAD_SINK];
+
+ state->src_sd = sd;
+
+ return v4l2_create_fwnode_links_to_pad(sd, sink, MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+}
+
+static const struct v4l2_async_notifier_operations imx8mq_mipi_csi_notify_ops = {
+ .bound = imx8mq_mipi_csi_notify_bound,
+};
+
+static int imx8mq_mipi_csi_async_register(struct csi_state *state)
+{
+ struct v4l2_fwnode_endpoint vep = {
+ .bus_type = V4L2_MBUS_CSI2_DPHY,
+ };
+ struct v4l2_async_subdev *asd;
+ struct fwnode_handle *ep;
+ unsigned int i;
+ int ret;
+
+ v4l2_async_notifier_init(&state->notifier);
+
+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(state->dev), 0, 0,
+ FWNODE_GRAPH_ENDPOINT_NEXT);
+ if (!ep)
+ return -ENOTCONN;
+
+ ret = v4l2_fwnode_endpoint_parse(ep, &vep);
+ if (ret)
+ goto err_parse;
+
+ for (i = 0; i < vep.bus.mipi_csi2.num_data_lanes; ++i) {
+ if (vep.bus.mipi_csi2.data_lanes[i] != i + 1) {
+ dev_err(state->dev,
+ "data lanes reordering is not supported");
+ ret = -EINVAL;
+ goto err_parse;
+ }
+ }
+
+ state->bus = vep.bus.mipi_csi2;
+
+ dev_dbg(state->dev, "data lanes: %d flags: 0x%08x\n",
+ state->bus.num_data_lanes,
+ state->bus.flags);
+
+ asd = v4l2_async_notifier_add_fwnode_remote_subdev(&state->notifier,
+ ep, struct v4l2_async_subdev);
+ if (IS_ERR(asd)) {
+ ret = PTR_ERR(asd);
+ goto err_parse;
+ }
+
+ fwnode_handle_put(ep);
+
+ state->notifier.ops = &imx8mq_mipi_csi_notify_ops;
+
+ ret = v4l2_async_subdev_notifier_register(&state->sd, &state->notifier);
+ if (ret)
+ return ret;
+
+ return v4l2_async_register_subdev(&state->sd);
+
+err_parse:
+ fwnode_handle_put(ep);
+
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * Suspend/resume
+ */
+
+static int imx8mq_mipi_csi_pm_suspend(struct device *dev, bool runtime)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct csi_state *state = mipi_sd_to_csi2_state(sd);
+ int ret = 0;
+
+ mutex_lock(&state->lock);
+
+ if (state->state & ST_POWERED) {
+ imx8mq_mipi_csi_stop_stream(state);
+ imx8mq_mipi_csi_clk_disable(state);
+ state->state &= ~ST_POWERED;
+ if (!runtime)
+ state->state |= ST_SUSPENDED;
+ }
+
+ mutex_unlock(&state->lock);
+
+ ret = icc_set_bw(state->icc_path, 0, 0);
+ if (ret)
+ dev_err(dev, "icc_set_bw failed with %d\n", ret);
+
+ return ret ? -EAGAIN : 0;
+}
+
+static int imx8mq_mipi_csi_pm_resume(struct device *dev, bool runtime)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct csi_state *state = mipi_sd_to_csi2_state(sd);
+ int ret = 0;
+
+ ret = icc_set_bw(state->icc_path, 0, state->icc_path_bw);
+ if (ret) {
+ dev_err(dev, "icc_set_bw failed with %d\n", ret);
+ return ret;
+ }
+
+ mutex_lock(&state->lock);
+
+ if (!runtime && !(state->state & ST_SUSPENDED))
+ goto unlock;
+
+ if (!(state->state & ST_POWERED)) {
+ state->state |= ST_POWERED;
+ ret = imx8mq_mipi_csi_clk_enable(state);
+ }
+ if (state->state & ST_STREAMING) {
+ ret = imx8mq_mipi_csi_start_stream(state);
+ if (ret)
+ goto unlock;
+ }
+
+ state->state &= ~ST_SUSPENDED;
+
+unlock:
+ mutex_unlock(&state->lock);
+
+ return ret ? -EAGAIN : 0;
+}
+
+static int __maybe_unused imx8mq_mipi_csi_suspend(struct device *dev)
+{
+ return imx8mq_mipi_csi_pm_suspend(dev, false);
+}
+
+static int __maybe_unused imx8mq_mipi_csi_resume(struct device *dev)
+{
+ return imx8mq_mipi_csi_pm_resume(dev, false);
+}
+
+static int __maybe_unused imx8mq_mipi_csi_runtime_suspend(struct device *dev)
+{
+ return imx8mq_mipi_csi_pm_suspend(dev, true);
+}
+
+static int __maybe_unused imx8mq_mipi_csi_runtime_resume(struct device *dev)
+{
+ return imx8mq_mipi_csi_pm_resume(dev, true);
+}
+
+static const struct dev_pm_ops imx8mq_mipi_csi_pm_ops = {
+ SET_RUNTIME_PM_OPS(imx8mq_mipi_csi_runtime_suspend,
+ imx8mq_mipi_csi_runtime_resume,
+ NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(imx8mq_mipi_csi_suspend, imx8mq_mipi_csi_resume)
+};
+
+/* -----------------------------------------------------------------------------
+ * Probe/remove & platform driver
+ */
+
+static int imx8mq_mipi_csi_subdev_init(struct csi_state *state)
+{
+ struct v4l2_subdev *sd = &state->sd;
+
+ v4l2_subdev_init(sd, &imx8mq_mipi_csi_subdev_ops);
+ sd->owner = THIS_MODULE;
+ snprintf(sd->name, sizeof(sd->name), "%s %s",
+ MIPI_CSI2_SUBDEV_NAME, dev_name(state->dev));
+
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+ sd->entity.ops = &imx8mq_mipi_csi_entity_ops;
+
+ sd->dev = state->dev;
+
+ state->csi2_fmt = &imx8mq_mipi_csi_formats[0];
+ imx8mq_mipi_csi_init_cfg(sd, NULL);
+
+ state->pads[MIPI_CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK
+ | MEDIA_PAD_FL_MUST_CONNECT;
+ state->pads[MIPI_CSI2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE
+ | MEDIA_PAD_FL_MUST_CONNECT;
+ return media_entity_pads_init(&sd->entity, MIPI_CSI2_PADS_NUM,
+ state->pads);
+}
+
+static void imx8mq_mipi_csi_release_icc(struct platform_device *pdev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(&pdev->dev);
+ struct csi_state *state = mipi_sd_to_csi2_state(sd);
+
+ icc_put(state->icc_path);
+}
+
+static int imx8mq_mipi_csi_init_icc(struct platform_device *pdev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(&pdev->dev);
+ struct csi_state *state = mipi_sd_to_csi2_state(sd);
+
+ /* Optional interconnect request */
+ state->icc_path = of_icc_get(&pdev->dev, "dram");
+ if (IS_ERR_OR_NULL(state->icc_path))
+ return PTR_ERR_OR_ZERO(state->icc_path);
+
+ state->icc_path_bw = MBps_to_icc(700);
+
+ return 0;
+}
+
+static int imx8mq_mipi_csi_parse_dt(struct csi_state *state)
+{
+ struct device *dev = state->dev;
+ struct device_node *np = state->dev->of_node;
+ struct device_node *node;
+ phandle ph;
+ u32 out_val[2];
+ int ret = 0;
+
+ state->rst = devm_reset_control_array_get_exclusive(dev);
+ if (IS_ERR(state->rst)) {
+ dev_err(dev, "Failed to get reset: %pe\n", state->rst);
+ return PTR_ERR(state->rst);
+ }
+
+ ret = of_property_read_u32_array(np, "fsl,mipi-phy-gpr", out_val,
+ ARRAY_SIZE(out_val));
+ if (ret) {
+ dev_err(dev, "no fsl,mipi-phy-gpr property found: %d\n", ret);
+ return ret;
+ }
+
+ ph = *out_val;
+
+ node = of_find_node_by_phandle(ph);
+ if (!node) {
+ dev_err(dev, "Error finding node by phandle\n");
+ return -ENODEV;
+ }
+ state->phy_gpr = syscon_node_to_regmap(node);
+ of_node_put(node);
+ if (IS_ERR(state->phy_gpr)) {
+ dev_err(dev, "failed to get gpr regmap: %pe\n", state->phy_gpr);
+ return PTR_ERR(state->phy_gpr);
+ }
+
+ state->phy_gpr_reg = out_val[1];
+ dev_dbg(dev, "phy gpr register set to 0x%x\n", state->phy_gpr_reg);
+
+ return ret;
+}
+
+static int imx8mq_mipi_csi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct csi_state *state;
+ int ret;
+
+ state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ state->dev = dev;
+
+ ret = imx8mq_mipi_csi_parse_dt(state);
+ if (ret < 0) {
+ dev_err(dev, "Failed to parse device tree: %d\n", ret);
+ return ret;
+ }
+
+ /* Acquire resources. */
+ state->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(state->regs))
+ return PTR_ERR(state->regs);
+
+ ret = imx8mq_mipi_csi_clk_get(state);
+ if (ret < 0)
+ return ret;
+
+ platform_set_drvdata(pdev, &state->sd);
+
+ mutex_init(&state->lock);
+
+ ret = imx8mq_mipi_csi_subdev_init(state);
+ if (ret < 0)
+ goto mutex;
+
+ ret = imx8mq_mipi_csi_init_icc(pdev);
+ if (ret)
+ goto mutex;
+
+ /* Enable runtime PM. */
+ pm_runtime_enable(dev);
+ if (!pm_runtime_enabled(dev)) {
+ ret = imx8mq_mipi_csi_pm_resume(dev, true);
+ if (ret < 0)
+ goto icc;
+ }
+
+ ret = imx8mq_mipi_csi_async_register(state);
+ if (ret < 0)
+ goto cleanup;
+
+ return 0;
+
+cleanup:
+ pm_runtime_disable(&pdev->dev);
+ imx8mq_mipi_csi_pm_suspend(&pdev->dev, true);
+
+ media_entity_cleanup(&state->sd.entity);
+ v4l2_async_notifier_unregister(&state->notifier);
+ v4l2_async_notifier_cleanup(&state->notifier);
+ v4l2_async_unregister_subdev(&state->sd);
+icc:
+ imx8mq_mipi_csi_release_icc(pdev);
+mutex:
+ mutex_destroy(&state->lock);
+
+ return ret;
+}
+
+static int imx8mq_mipi_csi_remove(struct platform_device *pdev)
+{
+ struct v4l2_subdev *sd = platform_get_drvdata(pdev);
+ struct csi_state *state = mipi_sd_to_csi2_state(sd);
+
+ v4l2_async_notifier_unregister(&state->notifier);
+ v4l2_async_notifier_cleanup(&state->notifier);
+ v4l2_async_unregister_subdev(&state->sd);
+
+ pm_runtime_disable(&pdev->dev);
+ imx8mq_mipi_csi_pm_suspend(&pdev->dev, true);
+ media_entity_cleanup(&state->sd.entity);
+ mutex_destroy(&state->lock);
+ pm_runtime_set_suspended(&pdev->dev);
+ imx8mq_mipi_csi_release_icc(pdev);
+
+ return 0;
+}
+
+static const struct of_device_id imx8mq_mipi_csi_of_match[] = {
+ { .compatible = "fsl,imx8mq-mipi-csi2", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, imx8mq_mipi_csi_of_match);
+
+static struct platform_driver imx8mq_mipi_csi_driver = {
+ .probe = imx8mq_mipi_csi_probe,
+ .remove = imx8mq_mipi_csi_remove,
+ .driver = {
+ .of_match_table = imx8mq_mipi_csi_of_match,
+ .name = MIPI_CSI2_DRIVER_NAME,
+ .pm = &imx8mq_mipi_csi_pm_ops,
+ },
+};
+
+module_platform_driver(imx8mq_mipi_csi_driver);
+
+MODULE_DESCRIPTION("i.MX8MQ MIPI CSI-2 receiver driver");
+MODULE_AUTHOR("Martin Kepplinger <martin.kepplinger@puri.sm>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:imx8mq-mipi-csi2");
const struct v4l2_ctrl_hevc_slice_params *slice_params;
const struct v4l2_ctrl_hevc_decode_params *decode_params;
const struct v4l2_hevc_pred_weight_table *pred_weight_table;
+ unsigned int width_in_ctb_luma, ctb_size_luma;
+ unsigned int log2_max_luma_coding_block_size;
dma_addr_t src_buf_addr;
dma_addr_t src_buf_end_addr;
u32 chroma_log2_weight_denom;
decode_params = run->h265.decode_params;
pred_weight_table = &slice_params->pred_weight_table;
+ log2_max_luma_coding_block_size =
+ sps->log2_min_luma_coding_block_size_minus3 + 3 +
+ sps->log2_diff_max_min_luma_coding_block_size;
+ ctb_size_luma = 1UL << log2_max_luma_coding_block_size;
+ width_in_ctb_luma =
+ DIV_ROUND_UP(sps->pic_width_in_luma_samples, ctb_size_luma);
+
/* MV column buffer size and allocation. */
if (!ctx->codec.h265.mv_col_buf_size) {
unsigned int num_buffers =
run->dst->vb2_buf.vb2_queue->num_buffers;
- unsigned int log2_max_luma_coding_block_size =
- sps->log2_min_luma_coding_block_size_minus3 + 3 +
- sps->log2_diff_max_min_luma_coding_block_size;
- unsigned int ctb_size_luma =
- 1UL << log2_max_luma_coding_block_size;
/*
* Each CTB requires a MV col buffer with a specific unit size.
reg = VE_DEC_H265_BITS_END_ADDR_BASE(src_buf_end_addr);
cedrus_write(dev, VE_DEC_H265_BITS_END_ADDR, reg);
- /* Coding tree block address: start at the beginning. */
- reg = VE_DEC_H265_DEC_CTB_ADDR_X(0) | VE_DEC_H265_DEC_CTB_ADDR_Y(0);
+ /* Coding tree block address */
+ reg = VE_DEC_H265_DEC_CTB_ADDR_X(slice_params->slice_segment_addr % width_in_ctb_luma);
+ reg |= VE_DEC_H265_DEC_CTB_ADDR_Y(slice_params->slice_segment_addr / width_in_ctb_luma);
cedrus_write(dev, VE_DEC_H265_DEC_CTB_ADDR, reg);
cedrus_write(dev, VE_DEC_H265_TILE_START_CTB, 0);
cedrus_write(dev, VE_DEC_H265_TILE_END_CTB, 0);
/* Clear the number of correctly-decoded coding tree blocks. */
- cedrus_write(dev, VE_DEC_H265_DEC_CTB_NUM, 0);
+ if (ctx->fh.m2m_ctx->new_frame)
+ cedrus_write(dev, VE_DEC_H265_DEC_CTB_NUM, 0);
/* Initialize bitstream access. */
cedrus_write(dev, VE_DEC_H265_TRIGGER, VE_DEC_H265_TRIGGER_INIT_SWDEC);
V4L2_HEVC_SLICE_PARAMS_FLAG_DEPENDENT_SLICE_SEGMENT,
slice_params->flags);
- /* FIXME: For multi-slice support. */
- reg |= VE_DEC_H265_DEC_SLICE_HDR_INFO0_FLAG_FIRST_SLICE_SEGMENT_IN_PIC;
+ if (ctx->fh.m2m_ctx->new_frame)
+ reg |= VE_DEC_H265_DEC_SLICE_HDR_INFO0_FLAG_FIRST_SLICE_SEGMENT_IN_PIC;
cedrus_write(dev, VE_DEC_H265_DEC_SLICE_HDR_INFO0, reg);
switch (ctx->src_fmt.pixelformat) {
case V4L2_PIX_FMT_H264_SLICE:
+ case V4L2_PIX_FMT_HEVC_SLICE:
vq->subsystem_flags |=
VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF;
break;
return -ENODEV;
sd_state = v4l2_subdev_alloc_state(subdev);
- if (!sd_state)
- return -ENOMEM;
+ if (IS_ERR(sd_state))
+ return PTR_ERR(sd_state);
/*
* Retrieve the format information and if requested format isn't
* supported, keep the current format.
#define USB_PID_MYGICA_T230C 0xc689
#define USB_PID_MYGICA_T230C2 0xc68a
#define USB_PID_MYGICA_T230C_LITE 0xc699
+#define USB_PID_MYGICA_T230C2_LITE 0xc69a
+#define USB_PID_MYGICA_T230A 0x689a
#define USB_PID_ELGATO_EYETV_DIVERSITY 0x0011
#define USB_PID_ELGATO_EYETV_DTT 0x0021
#define USB_PID_ELGATO_EYETV_DTT_2 0x003f
__u8 pic_struct;
/* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */
+ __u32 slice_segment_addr;
__u8 ref_idx_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
__u8 ref_idx_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
- __u8 padding[5];
+ __u8 padding;
/* ISO/IEC 23008-2, ITU-T Rec. H.265: Weighted prediction parameter */
struct v4l2_hevc_pred_weight_table pred_weight_table;
* @tx_ir: transmit IR
* @s_idle: enable/disable hardware idle mode, upon which,
* device doesn't interrupt host until it sees IR pulses
- * @s_learning_mode: enable wide band receiver used for learning
+ * @s_wideband_receiver: enable wide band receiver used for learning
* @s_carrier_report: enable carrier reports
* @s_filter: set the scancode filter
* @s_wakeup_filter: set the wakeup scancode filter. If the mask is zero
int (*s_rx_carrier_range)(struct rc_dev *dev, u32 min, u32 max);
int (*tx_ir)(struct rc_dev *dev, unsigned *txbuf, unsigned n);
void (*s_idle)(struct rc_dev *dev, bool enable);
- int (*s_learning_mode)(struct rc_dev *dev, int enable);
+ int (*s_wideband_receiver)(struct rc_dev *dev, int enable);
int (*s_carrier_report) (struct rc_dev *dev, int enable);
int (*s_filter)(struct rc_dev *dev,
struct rc_scancode_filter *filter);
#define MS_TO_US(msec) ((msec) * 1000)
#define IR_MAX_DURATION MS_TO_US(500)
#define IR_DEFAULT_TIMEOUT MS_TO_US(125)
+#define IR_MAX_TIMEOUT LIRC_VALUE_MASK
void ir_raw_event_handle(struct rc_dev *dev);
int ir_raw_event_store(struct rc_dev *dev, struct ir_raw_event *ev);
*
* This function initializes the notifier @asd_list. It must be called
* before adding a subdevice to a notifier, using one of:
- * @v4l2_async_notifier_add_fwnode_remote_subdev,
- * @v4l2_async_notifier_add_fwnode_subdev,
- * @v4l2_async_notifier_add_i2c_subdev,
- * @__v4l2_async_notifier_add_subdev or
- * @v4l2_async_notifier_parse_fwnode_endpoints.
+ * v4l2_async_notifier_add_fwnode_remote_subdev(),
+ * v4l2_async_notifier_add_fwnode_subdev(),
+ * v4l2_async_notifier_add_i2c_subdev(),
+ * __v4l2_async_notifier_add_subdev() or
+ * v4l2_async_notifier_parse_fwnode_endpoints().
*/
void v4l2_async_notifier_init(struct v4l2_async_notifier *notifier);
* @asd: pointer to &struct v4l2_async_subdev
*
* \warning: Drivers should avoid using this function and instead use one of:
- * @v4l2_async_notifier_add_fwnode_subdev,
- * @v4l2_async_notifier_add_fwnode_remote_subdev or
- * @v4l2_async_notifier_add_i2c_subdev.
+ * v4l2_async_notifier_add_fwnode_subdev(),
+ * v4l2_async_notifier_add_fwnode_remote_subdev() or
+ * v4l2_async_notifier_add_i2c_subdev().
*
* Call this function before registering a notifier to link the provided @asd to
* the notifiers master @asd_list. The @asd must be allocated with k*alloc() as
* function also gets a reference of the fwnode which is released later at
* notifier cleanup time.
*
- * This is just like @v4l2_async_notifier_add_fwnode_subdev, but with the
+ * This is just like v4l2_async_notifier_add_fwnode_subdev(), but with the
* exception that the fwnode refers to a local endpoint, not the remote one.
*/
#define v4l2_async_notifier_add_fwnode_remote_subdev(notifier, ep, type) \
* sub-devices allocated for the purposes of the notifier but not the notifier
* itself. The user is responsible for calling this function to clean up the
* notifier after calling
- * @v4l2_async_notifier_add_fwnode_remote_subdev,
- * @v4l2_async_notifier_add_fwnode_subdev,
- * @v4l2_async_notifier_add_i2c_subdev,
- * @__v4l2_async_notifier_add_subdev or
- * @v4l2_async_notifier_parse_fwnode_endpoints.
+ * v4l2_async_notifier_add_fwnode_remote_subdev(),
+ * v4l2_async_notifier_add_fwnode_subdev(),
+ * v4l2_async_notifier_add_i2c_subdev(),
+ * __v4l2_async_notifier_add_subdev() or
+ * v4l2_async_notifier_parse_fwnode_endpoints().
*
- * There is no harm from calling v4l2_async_notifier_cleanup in other
+ * There is no harm from calling v4l2_async_notifier_cleanup() in other
* cases as long as its memory has been zeroed after it has been
* allocated.
*/
};
/**
+ * enum v4l2_subdev_pre_streamon_flags - Flags for pre_streamon subdev core op
+ *
+ * @V4L2_SUBDEV_PRE_STREAMON_FL_MANUAL_LP: Set the transmitter to either LP-11
+ * or LP-111 mode before call to s_stream().
+ */
+enum v4l2_subdev_pre_streamon_flags {
+ V4L2_SUBDEV_PRE_STREAMON_FL_MANUAL_LP = BIT(0),
+};
+
+/**
* struct v4l2_subdev_video_ops - Callbacks used when v4l device was opened
* in video mode.
*
* @s_rx_buffer: set a host allocated memory buffer for the subdev. The subdev
* can adjust @size to a lower value and must not write more data to the
* buffer starting at @data than the original value of @size.
+ *
+ * @pre_streamon: May be called before streaming is actually started, to help
+ * initialising the bus. Current usage is to set a CSI-2 transmitter to
+ * LP-11 or LP-111 mode before streaming. See &enum
+ * v4l2_subdev_pre_streamon_flags.
+ *
+ * pre_streamon shall return error if it cannot perform the operation as
+ * indicated by the flags argument. In particular, -EACCES indicates lack
+ * of support for the operation. The caller shall call post_streamoff for
+ * each successful call of pre_streamon.
+ *
+ * @post_streamoff: Called after streaming is stopped, but if and only if
+ * pre_streamon was called earlier.
*/
struct v4l2_subdev_video_ops {
int (*s_routing)(struct v4l2_subdev *sd, u32 input, u32 output, u32 config);
struct v4l2_dv_timings *timings);
int (*s_rx_buffer)(struct v4l2_subdev *sd, void *buf,
unsigned int *size);
+ int (*pre_streamon)(struct v4l2_subdev *sd, u32 flags);
+ int (*post_streamoff)(struct v4l2_subdev *sd);
};
/**
* @asd: Pointer to respective &struct v4l2_async_subdev.
* @notifier: Pointer to the managing notifier.
* @subdev_notifier: A sub-device notifier implicitly registered for the sub-
- * device using v4l2_device_register_sensor_subdev().
+ * device using v4l2_async_register_subdev_sensor().
* @pdata: common part of subdevice platform data
*
* Each instance of a subdev driver should create this struct, either
#define CEC_OP_REC_SEQ_WEDNESDAY 0x08
#define CEC_OP_REC_SEQ_THURSDAY 0x10
#define CEC_OP_REC_SEQ_FRIDAY 0x20
-#define CEC_OP_REC_SEQ_SATERDAY 0x40
+#define CEC_OP_REC_SEQ_SATURDAY 0x40
#define CEC_OP_REC_SEQ_ONCE_ONLY 0x00
#define CEC_MSG_CLEAR_DIGITAL_TIMER 0x99
#define V4L2_CID_MPEG_VIDEO_FRAME_LTR_INDEX (V4L2_CID_CODEC_BASE+233)
#define V4L2_CID_MPEG_VIDEO_USE_LTR_FRAMES (V4L2_CID_CODEC_BASE+234)
#define V4L2_CID_MPEG_VIDEO_DEC_CONCEAL_COLOR (V4L2_CID_CODEC_BASE+235)
+#define V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD (V4L2_CID_CODEC_BASE+236)
/* CIDs for the MPEG-2 Part 2 (H.262) codec */
#define V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL (V4L2_CID_CODEC_BASE+270)