mirror of
https://github.com/openwrt/openwrt.git
synced 2026-10-14 02:19:06 +04:00
rockchip: backport RK3588 MIPI CSI-2 camera capture
The RK3588 camera path needs two drivers that landed after 6.18: the Rockchip video capture unit, which gained rk3588 support in 7.2, and the DesignWare MIPI CSI-2 receiver it is fed from, which arrived in 7.0. The CSI D-PHY and the imx415, imx219 and ov5647 sensors are in 6.18 already, so these two are what is missing. Backport both drivers with their bindings. The i.MX93 and i.MX95 additions to the receiver come along, which keeps it at an upstream state so that later fixes apply as they are. Signed-off-by: Daniel Golle <daniel@makrotopia.org>
This commit is contained in:
1 parent
444686aecc
commit
3d5db97adc
28 files changed
+7170
No files matched your search
+147
@@ -0,0 +1,147 @@
|
||||
From a36a0b28b94e86432239c4893e6fc2c39ce31a53 Mon Sep 17 00:00:00 2001
|
||||
From: Mehdi Djait <mehdi.djait@bootlin.com>
|
||||
Date: Fri, 14 Nov 2025 16:20:14 +0100
|
||||
Subject: [PATCH] media: dt-bindings: add rockchip px30 vip
|
||||
|
||||
Add documentation for the Rockchip PX30 Video Input Processor (VIP).
|
||||
|
||||
Signed-off-by: Mehdi Djait <mehdi.djait@bootlin.com>
|
||||
[revised description]
|
||||
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
|
||||
Signed-off-by: Michael Riesch <michael.riesch@wolfvision.net>
|
||||
Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
|
||||
Signed-off-by: Michael Riesch <michael.riesch@collabora.com>
|
||||
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
|
||||
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
|
||||
---
|
||||
.../bindings/media/rockchip,px30-vip.yaml | 124 ++++++++++++++++++
|
||||
1 file changed, 124 insertions(+)
|
||||
create mode 100644 Documentation/devicetree/bindings/media/rockchip,px30-vip.yaml
|
||||
|
||||
--- /dev/null
|
||||
+++ b/Documentation/devicetree/bindings/media/rockchip,px30-vip.yaml
|
||||
@@ -0,0 +1,124 @@
|
||||
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
+%YAML 1.2
|
||||
+---
|
||||
+$id: http://devicetree.org/schemas/media/rockchip,px30-vip.yaml#
|
||||
+$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
+
|
||||
+title: Rockchip PX30 Video Input Processor (VIP)
|
||||
+
|
||||
+maintainers:
|
||||
+ - Mehdi Djait <mehdi.djait@linux.intel.com>
|
||||
+ - Michael Riesch <michael.riesch@collabora.com>
|
||||
+
|
||||
+description:
|
||||
+ The Rockchip PX30 Video Input Processor (VIP) receives the data from a camera
|
||||
+ sensor or CCIR656 encoder and transfers it into system main memory by AXI bus.
|
||||
+
|
||||
+properties:
|
||||
+ compatible:
|
||||
+ const: rockchip,px30-vip
|
||||
+
|
||||
+ reg:
|
||||
+ maxItems: 1
|
||||
+
|
||||
+ interrupts:
|
||||
+ maxItems: 1
|
||||
+
|
||||
+ clocks:
|
||||
+ items:
|
||||
+ - description: ACLK
|
||||
+ - description: HCLK
|
||||
+ - description: PCLK
|
||||
+
|
||||
+ clock-names:
|
||||
+ items:
|
||||
+ - const: aclk
|
||||
+ - const: hclk
|
||||
+ - const: pclk
|
||||
+
|
||||
+ resets:
|
||||
+ items:
|
||||
+ - description: AXI
|
||||
+ - description: AHB
|
||||
+ - description: PCLK IN
|
||||
+
|
||||
+ reset-names:
|
||||
+ items:
|
||||
+ - const: axi
|
||||
+ - const: ahb
|
||||
+ - const: pclkin
|
||||
+
|
||||
+ power-domains:
|
||||
+ maxItems: 1
|
||||
+
|
||||
+ ports:
|
||||
+ $ref: /schemas/graph.yaml#/properties/ports
|
||||
+
|
||||
+ properties:
|
||||
+ port@0:
|
||||
+ $ref: /schemas/graph.yaml#/$defs/port-base
|
||||
+ unevaluatedProperties: false
|
||||
+ description: input port on the parallel interface
|
||||
+
|
||||
+ properties:
|
||||
+ endpoint:
|
||||
+ $ref: video-interfaces.yaml#
|
||||
+ unevaluatedProperties: false
|
||||
+
|
||||
+ properties:
|
||||
+ bus-type:
|
||||
+ enum:
|
||||
+ - 5 # MEDIA_BUS_TYPE_PARALLEL
|
||||
+ - 6 # MEDIA_BUS_TYPE_BT656
|
||||
+
|
||||
+ required:
|
||||
+ - bus-type
|
||||
+
|
||||
+ required:
|
||||
+ - port@0
|
||||
+
|
||||
+required:
|
||||
+ - compatible
|
||||
+ - reg
|
||||
+ - interrupts
|
||||
+ - clocks
|
||||
+ - ports
|
||||
+
|
||||
+additionalProperties: false
|
||||
+
|
||||
+examples:
|
||||
+ - |
|
||||
+ #include <dt-bindings/clock/px30-cru.h>
|
||||
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
+ #include <dt-bindings/media/video-interfaces.h>
|
||||
+ #include <dt-bindings/power/px30-power.h>
|
||||
+
|
||||
+ soc {
|
||||
+ #address-cells = <2>;
|
||||
+ #size-cells = <2>;
|
||||
+
|
||||
+ video-capture@ff490000 {
|
||||
+ compatible = "rockchip,px30-vip";
|
||||
+ reg = <0x0 0xff490000 0x0 0x200>;
|
||||
+ interrupts = <GIC_SPI 69 IRQ_TYPE_LEVEL_HIGH>;
|
||||
+ clocks = <&cru ACLK_CIF>, <&cru HCLK_CIF>, <&cru PCLK_CIF>;
|
||||
+ clock-names = "aclk", "hclk", "pclk";
|
||||
+ power-domains = <&power PX30_PD_VI>;
|
||||
+ resets = <&cru SRST_CIF_A>, <&cru SRST_CIF_H>, <&cru SRST_CIF_PCLKIN>;
|
||||
+ reset-names = "axi", "ahb", "pclkin";
|
||||
+
|
||||
+ ports {
|
||||
+ #address-cells = <1>;
|
||||
+ #size-cells = <0>;
|
||||
+
|
||||
+ port@0 {
|
||||
+ reg = <0>;
|
||||
+
|
||||
+ cif_in: endpoint {
|
||||
+ remote-endpoint = <&tw9900_out>;
|
||||
+ bus-type = <MEDIA_BUS_TYPE_BT656>;
|
||||
+ };
|
||||
+ };
|
||||
+ };
|
||||
+ };
|
||||
+ };
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
From 60836eeca0fb27ad6a41bc2c303be80a0371447f Mon Sep 17 00:00:00 2001
|
||||
From: Michael Riesch <michael.riesch@collabora.com>
|
||||
Date: Fri, 14 Nov 2025 16:20:15 +0100
|
||||
Subject: [PATCH] media: dt-bindings: add rockchip rk3568 vicap
|
||||
|
||||
Add documentation for the Rockchip RK3568 Video Capture (VICAP) unit.
|
||||
|
||||
Signed-off-by: Michael Riesch <michael.riesch@wolfvision.net>
|
||||
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
|
||||
Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
|
||||
Signed-off-by: Michael Riesch <michael.riesch@collabora.com>
|
||||
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
|
||||
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
|
||||
---
|
||||
.../bindings/media/rockchip,rk3568-vicap.yaml | 172 ++++++++++++++++++
|
||||
1 file changed, 172 insertions(+)
|
||||
create mode 100644 Documentation/devicetree/bindings/media/rockchip,rk3568-vicap.yaml
|
||||
|
||||
--- /dev/null
|
||||
+++ b/Documentation/devicetree/bindings/media/rockchip,rk3568-vicap.yaml
|
||||
@@ -0,0 +1,172 @@
|
||||
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
+%YAML 1.2
|
||||
+---
|
||||
+$id: http://devicetree.org/schemas/media/rockchip,rk3568-vicap.yaml#
|
||||
+$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
+
|
||||
+title: Rockchip RK3568 Video Capture (VICAP)
|
||||
+
|
||||
+maintainers:
|
||||
+ - Michael Riesch <michael.riesch@collabora.com>
|
||||
+
|
||||
+description:
|
||||
+ The Rockchip RK3568 Video Capture (VICAP) block features a digital video
|
||||
+ port (DVP, a parallel video interface) and a MIPI CSI-2 port. It receives
|
||||
+ the data from camera sensors, video decoders, or other companion ICs and
|
||||
+ transfers it into system main memory by AXI bus.
|
||||
+
|
||||
+properties:
|
||||
+ compatible:
|
||||
+ const: rockchip,rk3568-vicap
|
||||
+
|
||||
+ reg:
|
||||
+ maxItems: 1
|
||||
+
|
||||
+ interrupts:
|
||||
+ maxItems: 1
|
||||
+
|
||||
+ clocks:
|
||||
+ items:
|
||||
+ - description: ACLK
|
||||
+ - description: HCLK
|
||||
+ - description: DCLK
|
||||
+ - description: ICLK
|
||||
+
|
||||
+ clock-names:
|
||||
+ items:
|
||||
+ - const: aclk
|
||||
+ - const: hclk
|
||||
+ - const: dclk
|
||||
+ - const: iclk
|
||||
+
|
||||
+ iommus:
|
||||
+ maxItems: 1
|
||||
+
|
||||
+ resets:
|
||||
+ items:
|
||||
+ - description: ARST
|
||||
+ - description: HRST
|
||||
+ - description: DRST
|
||||
+ - description: PRST
|
||||
+ - description: IRST
|
||||
+
|
||||
+ reset-names:
|
||||
+ items:
|
||||
+ - const: arst
|
||||
+ - const: hrst
|
||||
+ - const: drst
|
||||
+ - const: prst
|
||||
+ - const: irst
|
||||
+
|
||||
+ rockchip,grf:
|
||||
+ $ref: /schemas/types.yaml#/definitions/phandle
|
||||
+ description: Phandle to general register file used for video input block control.
|
||||
+
|
||||
+ power-domains:
|
||||
+ maxItems: 1
|
||||
+
|
||||
+ ports:
|
||||
+ $ref: /schemas/graph.yaml#/properties/ports
|
||||
+
|
||||
+ properties:
|
||||
+ port@0:
|
||||
+ $ref: /schemas/graph.yaml#/$defs/port-base
|
||||
+ unevaluatedProperties: false
|
||||
+ description: The digital video port (DVP, a parallel video interface).
|
||||
+
|
||||
+ properties:
|
||||
+ endpoint:
|
||||
+ $ref: video-interfaces.yaml#
|
||||
+ unevaluatedProperties: false
|
||||
+
|
||||
+ properties:
|
||||
+ bus-type:
|
||||
+ enum:
|
||||
+ - 5 # MEDIA_BUS_TYPE_PARALLEL
|
||||
+ - 6 # MEDIA_BUS_TYPE_BT656
|
||||
+
|
||||
+ rockchip,dvp-clk-delay:
|
||||
+ $ref: /schemas/types.yaml#/definitions/uint32
|
||||
+ default: 0
|
||||
+ minimum: 0
|
||||
+ maximum: 127
|
||||
+ description:
|
||||
+ Delay the DVP path clock input to align the sampling phase,
|
||||
+ only valid in dual edge sampling mode. Delay is zero by
|
||||
+ default and can be adjusted optionally.
|
||||
+
|
||||
+ required:
|
||||
+ - bus-type
|
||||
+
|
||||
+ port@1:
|
||||
+ $ref: /schemas/graph.yaml#/properties/port
|
||||
+ description: Port connected to the MIPI CSI-2 receiver output.
|
||||
+
|
||||
+ properties:
|
||||
+ endpoint:
|
||||
+ $ref: video-interfaces.yaml#
|
||||
+ unevaluatedProperties: false
|
||||
+
|
||||
+required:
|
||||
+ - compatible
|
||||
+ - reg
|
||||
+ - interrupts
|
||||
+ - clocks
|
||||
+ - ports
|
||||
+
|
||||
+additionalProperties: false
|
||||
+
|
||||
+examples:
|
||||
+ - |
|
||||
+ #include <dt-bindings/clock/rk3568-cru.h>
|
||||
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
+ #include <dt-bindings/interrupt-controller/irq.h>
|
||||
+ #include <dt-bindings/power/rk3568-power.h>
|
||||
+ #include <dt-bindings/media/video-interfaces.h>
|
||||
+
|
||||
+ soc {
|
||||
+ #address-cells = <2>;
|
||||
+ #size-cells = <2>;
|
||||
+
|
||||
+ vicap: video-capture@fdfe0000 {
|
||||
+ compatible = "rockchip,rk3568-vicap";
|
||||
+ reg = <0x0 0xfdfe0000 0x0 0x200>;
|
||||
+ interrupts = <GIC_SPI 146 IRQ_TYPE_LEVEL_HIGH>;
|
||||
+ assigned-clocks = <&cru DCLK_VICAP>;
|
||||
+ assigned-clock-rates = <300000000>;
|
||||
+ clocks = <&cru ACLK_VICAP>, <&cru HCLK_VICAP>,
|
||||
+ <&cru DCLK_VICAP>, <&cru ICLK_VICAP_G>;
|
||||
+ clock-names = "aclk", "hclk", "dclk", "iclk";
|
||||
+ iommus = <&vicap_mmu>;
|
||||
+ power-domains = <&power RK3568_PD_VI>;
|
||||
+ resets = <&cru SRST_A_VICAP>, <&cru SRST_H_VICAP>,
|
||||
+ <&cru SRST_D_VICAP>, <&cru SRST_P_VICAP>,
|
||||
+ <&cru SRST_I_VICAP>;
|
||||
+ reset-names = "arst", "hrst", "drst", "prst", "irst";
|
||||
+ rockchip,grf = <&grf>;
|
||||
+
|
||||
+ ports {
|
||||
+ #address-cells = <1>;
|
||||
+ #size-cells = <0>;
|
||||
+
|
||||
+ vicap_dvp: port@0 {
|
||||
+ reg = <0>;
|
||||
+
|
||||
+ vicap_dvp_input: endpoint {
|
||||
+ bus-type = <MEDIA_BUS_TYPE_BT656>;
|
||||
+ bus-width = <16>;
|
||||
+ pclk-sample = <MEDIA_PCLK_SAMPLE_DUAL_EDGE>;
|
||||
+ remote-endpoint = <&it6801_output>;
|
||||
+ };
|
||||
+ };
|
||||
+
|
||||
+ vicap_mipi: port@1 {
|
||||
+ reg = <1>;
|
||||
+
|
||||
+ vicap_mipi_input: endpoint {
|
||||
+ remote-endpoint = <&csi_output>;
|
||||
+ };
|
||||
+ };
|
||||
+ };
|
||||
+ };
|
||||
+ };
|
||||
+398
@@ -0,0 +1,398 @@
|
||||
From f53fb31a388901cfe11b95a3a2cf1594355321e4 Mon Sep 17 00:00:00 2001
|
||||
From: Michael Riesch <michael.riesch@collabora.com>
|
||||
Date: Fri, 14 Nov 2025 16:20:16 +0100
|
||||
Subject: [PATCH] media: rockchip: add driver for the rockchip camera interface
|
||||
|
||||
Add the skeleton for a media controller centric V4L2 driver for the
|
||||
Rockchip Camera Interface (CIF).
|
||||
The skeleton features support for the PX30 Video Input Processor (VIP)
|
||||
and the RK3568 Video Capture (VICAP) unit.
|
||||
|
||||
Tested-by: Gerald Loacker <gerald.loacker@wolfvision.net>
|
||||
Reviewed-by: Gerald Loacker <gerald.loacker@wolfvision.net>
|
||||
Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
|
||||
Reviewed-by: Mehdi Djait <mehdi.djait@linux.intel.com>
|
||||
Signed-off-by: Michael Riesch <michael.riesch@collabora.com>
|
||||
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
|
||||
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
|
||||
---
|
||||
drivers/media/platform/rockchip/Kconfig | 1 +
|
||||
drivers/media/platform/rockchip/Makefile | 1 +
|
||||
drivers/media/platform/rockchip/rkcif/Kconfig | 18 ++
|
||||
.../media/platform/rockchip/rkcif/Makefile | 4 +
|
||||
.../platform/rockchip/rkcif/rkcif-common.h | 55 ++++
|
||||
.../media/platform/rockchip/rkcif/rkcif-dev.c | 262 ++++++++++++++++++
|
||||
6 files changed, 341 insertions(+)
|
||||
create mode 100644 drivers/media/platform/rockchip/rkcif/Kconfig
|
||||
create mode 100644 drivers/media/platform/rockchip/rkcif/Makefile
|
||||
create mode 100644 drivers/media/platform/rockchip/rkcif/rkcif-common.h
|
||||
create mode 100644 drivers/media/platform/rockchip/rkcif/rkcif-dev.c
|
||||
|
||||
--- a/drivers/media/platform/rockchip/Kconfig
|
||||
+++ b/drivers/media/platform/rockchip/Kconfig
|
||||
@@ -3,5 +3,6 @@
|
||||
comment "Rockchip media platform drivers"
|
||||
|
||||
source "drivers/media/platform/rockchip/rga/Kconfig"
|
||||
+source "drivers/media/platform/rockchip/rkcif/Kconfig"
|
||||
source "drivers/media/platform/rockchip/rkisp1/Kconfig"
|
||||
source "drivers/media/platform/rockchip/rkvdec/Kconfig"
|
||||
--- a/drivers/media/platform/rockchip/Makefile
|
||||
+++ b/drivers/media/platform/rockchip/Makefile
|
||||
@@ -1,4 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-y += rga/
|
||||
+obj-y += rkcif/
|
||||
obj-y += rkisp1/
|
||||
obj-y += rkvdec/
|
||||
--- /dev/null
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/Kconfig
|
||||
@@ -0,0 +1,18 @@
|
||||
+config VIDEO_ROCKCHIP_CIF
|
||||
+ tristate "Rockchip Camera Interface (CIF)"
|
||||
+ depends on VIDEO_DEV
|
||||
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
|
||||
+ depends on V4L_PLATFORM_DRIVERS
|
||||
+ depends on PM && COMMON_CLK
|
||||
+ select MEDIA_CONTROLLER
|
||||
+ select VIDEOBUF2_DMA_CONTIG
|
||||
+ select V4L2_FWNODE
|
||||
+ select VIDEO_V4L2_SUBDEV_API
|
||||
+ help
|
||||
+ This is a driver for Rockchip Camera Interface (CIF). It is featured
|
||||
+ in many Rockchips SoCs in different variations, such as the PX30
|
||||
+ Video Input Processor (VIP, one Digital Video Port (DVP)) or the
|
||||
+ RK3568 Video Capture (VICAP, one DVP, one MIPI CSI-2 receiver) unit.
|
||||
+
|
||||
+ To compile this driver as a module, choose M here: the module
|
||||
+ will be called rockchip-cif.
|
||||
--- /dev/null
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/Makefile
|
||||
@@ -0,0 +1,4 @@
|
||||
+# SPDX-License-Identifier: GPL-2.0
|
||||
+obj-$(CONFIG_VIDEO_ROCKCHIP_CIF) += rockchip-cif.o
|
||||
+
|
||||
+rockchip-cif-objs += rkcif-dev.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-common.h
|
||||
@@ -0,0 +1,55 @@
|
||||
+/* SPDX-License-Identifier: GPL-2.0 */
|
||||
+/*
|
||||
+ * Rockchip Camera Interface (CIF) Driver
|
||||
+ *
|
||||
+ * Copyright (C) 2018 Rockchip Electronics Co., Ltd.
|
||||
+ * Copyright (C) 2023 Mehdi Djait <mehdi.djait@bootlin.com>
|
||||
+ * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net>
|
||||
+ * Copyright (C) 2025 Collabora, Ltd.
|
||||
+ */
|
||||
+
|
||||
+#ifndef _RKCIF_COMMON_H
|
||||
+#define _RKCIF_COMMON_H
|
||||
+
|
||||
+#include <linux/clk.h>
|
||||
+#include <linux/mutex.h>
|
||||
+#include <linux/regmap.h>
|
||||
+
|
||||
+#include <media/media-device.h>
|
||||
+#include <media/media-entity.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>
|
||||
+#include <media/videobuf2-v4l2.h>
|
||||
+
|
||||
+#define RKCIF_DRIVER_NAME "rockchip-cif"
|
||||
+#define RKCIF_CLK_MAX 4
|
||||
+
|
||||
+struct rkcif_remote {
|
||||
+ struct v4l2_async_connection async_conn;
|
||||
+ struct v4l2_subdev *sd;
|
||||
+};
|
||||
+
|
||||
+struct rkcif_match_data {
|
||||
+ const char *const *clks;
|
||||
+ unsigned int clks_num;
|
||||
+};
|
||||
+
|
||||
+struct rkcif_device {
|
||||
+ struct device *dev;
|
||||
+
|
||||
+ const struct rkcif_match_data *match_data;
|
||||
+ struct clk_bulk_data clks[RKCIF_CLK_MAX];
|
||||
+ unsigned int clks_num;
|
||||
+ struct regmap *grf;
|
||||
+ struct reset_control *reset;
|
||||
+ void __iomem *base_addr;
|
||||
+
|
||||
+ struct media_device media_dev;
|
||||
+ struct v4l2_device v4l2_dev;
|
||||
+ struct v4l2_async_notifier notifier;
|
||||
+};
|
||||
+
|
||||
+#endif
|
||||
--- /dev/null
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-dev.c
|
||||
@@ -0,0 +1,262 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0
|
||||
+/*
|
||||
+ * Rockchip Camera Interface (CIF) Driver
|
||||
+ *
|
||||
+ * Copyright (C) 2018 Rockchip Electronics Co., Ltd.
|
||||
+ * Copyright (C) 2020 Maxime Chevallier <maxime.chevallier@bootlin.com>
|
||||
+ * Copyright (C) 2023 Mehdi Djait <mehdi.djait@bootlin.com>
|
||||
+ * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net>
|
||||
+ * Copyright (C) 2025 Collabora, Ltd.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/clk.h>
|
||||
+#include <linux/delay.h>
|
||||
+#include <linux/interrupt.h>
|
||||
+#include <linux/mfd/syscon.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/of_graph.h>
|
||||
+#include <linux/of_platform.h>
|
||||
+#include <linux/pm_runtime.h>
|
||||
+#include <linux/reset.h>
|
||||
+
|
||||
+#include <media/v4l2-fwnode.h>
|
||||
+#include <media/v4l2-mc.h>
|
||||
+
|
||||
+#include "rkcif-common.h"
|
||||
+
|
||||
+static const char *const px30_vip_clks[] = {
|
||||
+ "aclk",
|
||||
+ "hclk",
|
||||
+ "pclk",
|
||||
+};
|
||||
+
|
||||
+static const struct rkcif_match_data px30_vip_match_data = {
|
||||
+ .clks = px30_vip_clks,
|
||||
+ .clks_num = ARRAY_SIZE(px30_vip_clks),
|
||||
+};
|
||||
+
|
||||
+static const char *const rk3568_vicap_clks[] = {
|
||||
+ "aclk",
|
||||
+ "hclk",
|
||||
+ "dclk",
|
||||
+ "iclk",
|
||||
+};
|
||||
+
|
||||
+static const struct rkcif_match_data rk3568_vicap_match_data = {
|
||||
+ .clks = rk3568_vicap_clks,
|
||||
+ .clks_num = ARRAY_SIZE(rk3568_vicap_clks),
|
||||
+};
|
||||
+
|
||||
+static const struct of_device_id rkcif_plat_of_match[] = {
|
||||
+ {
|
||||
+ .compatible = "rockchip,px30-vip",
|
||||
+ .data = &px30_vip_match_data,
|
||||
+ },
|
||||
+ {
|
||||
+ .compatible = "rockchip,rk3568-vicap",
|
||||
+ .data = &rk3568_vicap_match_data,
|
||||
+ },
|
||||
+ {}
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, rkcif_plat_of_match);
|
||||
+
|
||||
+static int rkcif_register(struct rkcif_device *rkcif)
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void rkcif_unregister(struct rkcif_device *rkcif)
|
||||
+{
|
||||
+}
|
||||
+
|
||||
+static int rkcif_notifier_bound(struct v4l2_async_notifier *notifier,
|
||||
+ struct v4l2_subdev *sd,
|
||||
+ struct v4l2_async_connection *asd)
|
||||
+{
|
||||
+ struct rkcif_remote *remote =
|
||||
+ container_of(asd, struct rkcif_remote, async_conn);
|
||||
+
|
||||
+ remote->sd = sd;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int rkcif_notifier_complete(struct v4l2_async_notifier *notifier)
|
||||
+{
|
||||
+ struct rkcif_device *rkcif =
|
||||
+ container_of(notifier, struct rkcif_device, notifier);
|
||||
+
|
||||
+ return v4l2_device_register_subdev_nodes(&rkcif->v4l2_dev);
|
||||
+}
|
||||
+
|
||||
+static const struct v4l2_async_notifier_operations rkcif_notifier_ops = {
|
||||
+ .bound = rkcif_notifier_bound,
|
||||
+ .complete = rkcif_notifier_complete,
|
||||
+};
|
||||
+
|
||||
+static irqreturn_t rkcif_isr(int irq, void *ctx)
|
||||
+{
|
||||
+ irqreturn_t ret = IRQ_NONE;
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int rkcif_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct device *dev = &pdev->dev;
|
||||
+ struct rkcif_device *rkcif;
|
||||
+ int ret, irq;
|
||||
+
|
||||
+ rkcif = devm_kzalloc(dev, sizeof(*rkcif), GFP_KERNEL);
|
||||
+ if (!rkcif)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ rkcif->match_data = of_device_get_match_data(dev);
|
||||
+ if (!rkcif->match_data)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ dev_set_drvdata(dev, rkcif);
|
||||
+ rkcif->dev = dev;
|
||||
+
|
||||
+ rkcif->base_addr = devm_platform_ioremap_resource(pdev, 0);
|
||||
+ if (IS_ERR(rkcif->base_addr))
|
||||
+ return PTR_ERR(rkcif->base_addr);
|
||||
+
|
||||
+ irq = platform_get_irq(pdev, 0);
|
||||
+ if (irq < 0)
|
||||
+ return irq;
|
||||
+
|
||||
+ ret = devm_request_irq(dev, irq, rkcif_isr, IRQF_SHARED,
|
||||
+ dev_driver_string(dev), dev);
|
||||
+ if (ret)
|
||||
+ return dev_err_probe(dev, ret, "failed to request irq\n");
|
||||
+
|
||||
+ if (rkcif->match_data->clks_num > RKCIF_CLK_MAX)
|
||||
+ return dev_err_probe(dev, -EINVAL, "invalid number of clocks\n");
|
||||
+
|
||||
+ rkcif->clks_num = rkcif->match_data->clks_num;
|
||||
+ for (unsigned int i = 0; i < rkcif->clks_num; i++)
|
||||
+ rkcif->clks[i].id = rkcif->match_data->clks[i];
|
||||
+ ret = devm_clk_bulk_get(dev, rkcif->clks_num, rkcif->clks);
|
||||
+ if (ret)
|
||||
+ return dev_err_probe(dev, ret, "failed to get clocks\n");
|
||||
+
|
||||
+ rkcif->reset = devm_reset_control_array_get_exclusive(dev);
|
||||
+ if (IS_ERR(rkcif->reset))
|
||||
+ return PTR_ERR(rkcif->reset);
|
||||
+
|
||||
+ rkcif->grf = syscon_regmap_lookup_by_phandle(dev->of_node,
|
||||
+ "rockchip,grf");
|
||||
+ if (IS_ERR(rkcif->grf))
|
||||
+ rkcif->grf = NULL;
|
||||
+
|
||||
+ pm_runtime_enable(&pdev->dev);
|
||||
+
|
||||
+ rkcif->media_dev.dev = dev;
|
||||
+ strscpy(rkcif->media_dev.model, RKCIF_DRIVER_NAME,
|
||||
+ sizeof(rkcif->media_dev.model));
|
||||
+ media_device_init(&rkcif->media_dev);
|
||||
+
|
||||
+ rkcif->v4l2_dev.mdev = &rkcif->media_dev;
|
||||
+ ret = v4l2_device_register(dev, &rkcif->v4l2_dev);
|
||||
+ if (ret)
|
||||
+ goto err_media_dev_cleanup;
|
||||
+
|
||||
+ ret = media_device_register(&rkcif->media_dev);
|
||||
+ if (ret < 0) {
|
||||
+ dev_err(dev, "failed to register media device: %d\n", ret);
|
||||
+ goto err_v4l2_dev_unregister;
|
||||
+ }
|
||||
+
|
||||
+ v4l2_async_nf_init(&rkcif->notifier, &rkcif->v4l2_dev);
|
||||
+ rkcif->notifier.ops = &rkcif_notifier_ops;
|
||||
+
|
||||
+ ret = rkcif_register(rkcif);
|
||||
+ if (ret) {
|
||||
+ dev_err(dev, "failed to register media entities: %d\n", ret);
|
||||
+ goto err_notifier_cleanup;
|
||||
+ }
|
||||
+
|
||||
+ ret = v4l2_async_nf_register(&rkcif->notifier);
|
||||
+ if (ret)
|
||||
+ goto err_rkcif_unregister;
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+err_rkcif_unregister:
|
||||
+ rkcif_unregister(rkcif);
|
||||
+err_notifier_cleanup:
|
||||
+ v4l2_async_nf_cleanup(&rkcif->notifier);
|
||||
+ media_device_unregister(&rkcif->media_dev);
|
||||
+err_v4l2_dev_unregister:
|
||||
+ v4l2_device_unregister(&rkcif->v4l2_dev);
|
||||
+err_media_dev_cleanup:
|
||||
+ media_device_cleanup(&rkcif->media_dev);
|
||||
+ pm_runtime_disable(&pdev->dev);
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static void rkcif_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct rkcif_device *rkcif = platform_get_drvdata(pdev);
|
||||
+
|
||||
+ v4l2_async_nf_unregister(&rkcif->notifier);
|
||||
+ rkcif_unregister(rkcif);
|
||||
+ v4l2_async_nf_cleanup(&rkcif->notifier);
|
||||
+ media_device_unregister(&rkcif->media_dev);
|
||||
+ v4l2_device_unregister(&rkcif->v4l2_dev);
|
||||
+ media_device_cleanup(&rkcif->media_dev);
|
||||
+ pm_runtime_disable(&pdev->dev);
|
||||
+}
|
||||
+
|
||||
+static int rkcif_runtime_suspend(struct device *dev)
|
||||
+{
|
||||
+ struct rkcif_device *rkcif = dev_get_drvdata(dev);
|
||||
+
|
||||
+ /*
|
||||
+ * Reset CIF (CRU, DMA, FIFOs) to allow a clean resume.
|
||||
+ * Since this resets the IOMMU too, we cannot issue this reset when
|
||||
+ * resuming.
|
||||
+ */
|
||||
+ reset_control_assert(rkcif->reset);
|
||||
+ udelay(5);
|
||||
+ reset_control_deassert(rkcif->reset);
|
||||
+
|
||||
+ clk_bulk_disable_unprepare(rkcif->clks_num, rkcif->clks);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int rkcif_runtime_resume(struct device *dev)
|
||||
+{
|
||||
+ struct rkcif_device *rkcif = dev_get_drvdata(dev);
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = clk_bulk_prepare_enable(rkcif->clks_num, rkcif->clks);
|
||||
+ if (ret) {
|
||||
+ dev_err(dev, "failed to enable clocks\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct dev_pm_ops rkcif_plat_pm_ops = {
|
||||
+ .runtime_suspend = rkcif_runtime_suspend,
|
||||
+ .runtime_resume = rkcif_runtime_resume,
|
||||
+};
|
||||
+
|
||||
+static struct platform_driver rkcif_plat_drv = {
|
||||
+ .driver = {
|
||||
+ .name = RKCIF_DRIVER_NAME,
|
||||
+ .of_match_table = rkcif_plat_of_match,
|
||||
+ .pm = &rkcif_plat_pm_ops,
|
||||
+ },
|
||||
+ .probe = rkcif_probe,
|
||||
+ .remove = rkcif_remove,
|
||||
+};
|
||||
+module_platform_driver(rkcif_plat_drv);
|
||||
+
|
||||
+MODULE_DESCRIPTION("Rockchip Camera Interface (CIF) platform driver");
|
||||
+MODULE_LICENSE("GPL");
|
||||
+595
@@ -0,0 +1,595 @@
|
||||
From 85411d17bee99b0a99e983f37188f9cdacfded54 Mon Sep 17 00:00:00 2001
|
||||
From: Michael Riesch <michael.riesch@collabora.com>
|
||||
Date: Fri, 14 Nov 2025 16:20:17 +0100
|
||||
Subject: [PATCH] media: rockchip: rkcif: add abstraction for interface and
|
||||
crop blocks
|
||||
|
||||
Add an abstraction for the INTERFACE and CROP parts of the different
|
||||
Rockchip Camera Interface (CIF) variants. These parts are represented
|
||||
as V4L2 subdevice with one sink pad and one source pad. The sink pad
|
||||
is connected to a subdevice: either the subdevice provided by the
|
||||
driver of the companion chip connected to the DVP, or the subdevice
|
||||
provided by the MIPI CSI-2 receiver. The source pad is connected to
|
||||
V4l2 device(s) provided by one or many instance(s) of the DMA
|
||||
abstraction.
|
||||
|
||||
Tested-by: Gerald Loacker <gerald.loacker@wolfvision.net>
|
||||
Reviewed-by: Gerald Loacker <gerald.loacker@wolfvision.net>
|
||||
Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
|
||||
Reviewed-by: Mehdi Djait <mehdi.djait@linux.intel.com>
|
||||
Signed-off-by: Michael Riesch <michael.riesch@collabora.com>
|
||||
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
|
||||
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
|
||||
---
|
||||
.../media/platform/rockchip/rkcif/Makefile | 1 +
|
||||
.../platform/rockchip/rkcif/rkcif-common.h | 71 +++
|
||||
.../media/platform/rockchip/rkcif/rkcif-dev.c | 13 +
|
||||
.../platform/rockchip/rkcif/rkcif-interface.c | 405 ++++++++++++++++++
|
||||
.../platform/rockchip/rkcif/rkcif-interface.h | 31 ++
|
||||
5 files changed, 521 insertions(+)
|
||||
create mode 100644 drivers/media/platform/rockchip/rkcif/rkcif-interface.c
|
||||
create mode 100644 drivers/media/platform/rockchip/rkcif/rkcif-interface.h
|
||||
|
||||
--- a/drivers/media/platform/rockchip/rkcif/Makefile
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/Makefile
|
||||
@@ -2,3 +2,4 @@
|
||||
obj-$(CONFIG_VIDEO_ROCKCHIP_CIF) += rockchip-cif.o
|
||||
|
||||
rockchip-cif-objs += rkcif-dev.o
|
||||
+rockchip-cif-objs += rkcif-interface.o
|
||||
--- a/drivers/media/platform/rockchip/rkcif/rkcif-common.h
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-common.h
|
||||
@@ -27,9 +27,78 @@
|
||||
#define RKCIF_DRIVER_NAME "rockchip-cif"
|
||||
#define RKCIF_CLK_MAX 4
|
||||
|
||||
+enum rkcif_format_type {
|
||||
+ RKCIF_FMT_TYPE_INVALID,
|
||||
+ RKCIF_FMT_TYPE_YUV,
|
||||
+ RKCIF_FMT_TYPE_RAW,
|
||||
+};
|
||||
+
|
||||
+enum rkcif_interface_index {
|
||||
+ RKCIF_DVP,
|
||||
+ RKCIF_MIPI_BASE,
|
||||
+ RKCIF_MIPI1 = RKCIF_MIPI_BASE,
|
||||
+ RKCIF_MIPI2,
|
||||
+ RKCIF_MIPI3,
|
||||
+ RKCIF_MIPI4,
|
||||
+ RKCIF_MIPI5,
|
||||
+ RKCIF_MIPI6,
|
||||
+ RKCIF_MIPI_MAX,
|
||||
+ RKCIF_IF_MAX = RKCIF_MIPI_MAX
|
||||
+};
|
||||
+
|
||||
+enum rkcif_interface_pad_index {
|
||||
+ RKCIF_IF_PAD_SINK,
|
||||
+ RKCIF_IF_PAD_SRC,
|
||||
+ RKCIF_IF_PAD_MAX
|
||||
+};
|
||||
+
|
||||
+enum rkcif_interface_status {
|
||||
+ RKCIF_IF_INACTIVE,
|
||||
+ RKCIF_IF_ACTIVE,
|
||||
+};
|
||||
+
|
||||
+enum rkcif_interface_type {
|
||||
+ RKCIF_IF_INVALID,
|
||||
+ RKCIF_IF_DVP,
|
||||
+ RKCIF_IF_MIPI,
|
||||
+};
|
||||
+
|
||||
+struct rkcif_input_fmt {
|
||||
+ u32 mbus_code;
|
||||
+
|
||||
+ enum rkcif_format_type fmt_type;
|
||||
+ enum v4l2_field field;
|
||||
+};
|
||||
+
|
||||
+struct rkcif_interface;
|
||||
+
|
||||
struct rkcif_remote {
|
||||
struct v4l2_async_connection async_conn;
|
||||
struct v4l2_subdev *sd;
|
||||
+
|
||||
+ struct rkcif_interface *interface;
|
||||
+};
|
||||
+
|
||||
+struct rkcif_dvp {
|
||||
+ u32 dvp_clk_delay;
|
||||
+};
|
||||
+
|
||||
+struct rkcif_interface {
|
||||
+ enum rkcif_interface_type type;
|
||||
+ enum rkcif_interface_status status;
|
||||
+ enum rkcif_interface_index index;
|
||||
+ struct rkcif_device *rkcif;
|
||||
+ struct rkcif_remote *remote;
|
||||
+ const struct rkcif_input_fmt *in_fmts;
|
||||
+ unsigned int in_fmts_num;
|
||||
+
|
||||
+ struct media_pad pads[RKCIF_IF_PAD_MAX];
|
||||
+ struct v4l2_fwnode_endpoint vep;
|
||||
+ struct v4l2_subdev sd;
|
||||
+
|
||||
+ union {
|
||||
+ struct rkcif_dvp dvp;
|
||||
+ };
|
||||
};
|
||||
|
||||
struct rkcif_match_data {
|
||||
@@ -47,6 +116,8 @@ struct rkcif_device {
|
||||
struct reset_control *reset;
|
||||
void __iomem *base_addr;
|
||||
|
||||
+ struct rkcif_interface interfaces[RKCIF_IF_MAX];
|
||||
+
|
||||
struct media_device media_dev;
|
||||
struct v4l2_device v4l2_dev;
|
||||
struct v4l2_async_notifier notifier;
|
||||
--- a/drivers/media/platform/rockchip/rkcif/rkcif-dev.c
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-dev.c
|
||||
@@ -74,8 +74,21 @@ static int rkcif_notifier_bound(struct v
|
||||
struct v4l2_subdev *sd,
|
||||
struct v4l2_async_connection *asd)
|
||||
{
|
||||
+ struct rkcif_device *rkcif =
|
||||
+ container_of(notifier, struct rkcif_device, notifier);
|
||||
struct rkcif_remote *remote =
|
||||
container_of(asd, struct rkcif_remote, async_conn);
|
||||
+ struct media_pad *sink_pad =
|
||||
+ &remote->interface->pads[RKCIF_IF_PAD_SINK];
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = v4l2_create_fwnode_links_to_pad(sd, sink_pad,
|
||||
+ MEDIA_LNK_FL_ENABLED);
|
||||
+ if (ret) {
|
||||
+ dev_err(rkcif->dev, "failed to link source pad of %s\n",
|
||||
+ sd->name);
|
||||
+ return ret;
|
||||
+ }
|
||||
|
||||
remote->sd = sd;
|
||||
|
||||
--- /dev/null
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-interface.c
|
||||
@@ -0,0 +1,405 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0
|
||||
+/*
|
||||
+ * Rockchip Camera Interface (CIF) Driver
|
||||
+ *
|
||||
+ * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net>
|
||||
+ * Copyright (C) 2025 Collabora, Ltd.
|
||||
+ */
|
||||
+
|
||||
+#include <media/v4l2-common.h>
|
||||
+#include <media/v4l2-fwnode.h>
|
||||
+#include <media/v4l2-mc.h>
|
||||
+#include <media/v4l2-subdev.h>
|
||||
+
|
||||
+#include "rkcif-common.h"
|
||||
+#include "rkcif-interface.h"
|
||||
+
|
||||
+static inline struct rkcif_interface *to_rkcif_interface(struct v4l2_subdev *sd)
|
||||
+{
|
||||
+ return container_of(sd, struct rkcif_interface, sd);
|
||||
+}
|
||||
+
|
||||
+static const struct media_entity_operations rkcif_interface_media_ops = {
|
||||
+ .link_validate = v4l2_subdev_link_validate,
|
||||
+ .has_pad_interdep = v4l2_subdev_has_pad_interdep,
|
||||
+};
|
||||
+
|
||||
+static int rkcif_interface_set_fmt(struct v4l2_subdev *sd,
|
||||
+ struct v4l2_subdev_state *state,
|
||||
+ struct v4l2_subdev_format *format)
|
||||
+{
|
||||
+ struct rkcif_interface *interface = to_rkcif_interface(sd);
|
||||
+ const struct rkcif_input_fmt *input;
|
||||
+ struct v4l2_mbus_framefmt *sink, *src;
|
||||
+ struct v4l2_rect *crop;
|
||||
+ u32 other_pad, other_stream;
|
||||
+ int ret;
|
||||
+
|
||||
+ /* the format on the source pad always matches the sink pad */
|
||||
+ if (format->pad == RKCIF_IF_PAD_SRC)
|
||||
+ return v4l2_subdev_get_fmt(sd, state, format);
|
||||
+
|
||||
+ input = rkcif_interface_find_input_fmt(interface, true,
|
||||
+ format->format.code);
|
||||
+ format->format.code = input->mbus_code;
|
||||
+
|
||||
+ sink = v4l2_subdev_state_get_format(state, format->pad, format->stream);
|
||||
+ if (!sink)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ *sink = format->format;
|
||||
+
|
||||
+ /* propagate the format to the source pad */
|
||||
+ src = v4l2_subdev_state_get_opposite_stream_format(state, format->pad,
|
||||
+ format->stream);
|
||||
+ if (!src)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ *src = *sink;
|
||||
+
|
||||
+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing,
|
||||
+ format->pad, format->stream,
|
||||
+ &other_pad, &other_stream);
|
||||
+ if (ret)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ crop = v4l2_subdev_state_get_crop(state, other_pad, other_stream);
|
||||
+ if (!crop)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ /* reset crop */
|
||||
+ crop->left = 0;
|
||||
+ crop->top = 0;
|
||||
+ crop->width = sink->width;
|
||||
+ crop->height = sink->height;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int rkcif_interface_get_sel(struct v4l2_subdev *sd,
|
||||
+ struct v4l2_subdev_state *state,
|
||||
+ struct v4l2_subdev_selection *sel)
|
||||
+{
|
||||
+ struct v4l2_mbus_framefmt *sink;
|
||||
+ struct v4l2_rect *crop;
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ if (sel->pad != RKCIF_IF_PAD_SRC)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ sink = v4l2_subdev_state_get_opposite_stream_format(state, sel->pad,
|
||||
+ sel->stream);
|
||||
+ if (!sink)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream);
|
||||
+ if (!crop)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ switch (sel->target) {
|
||||
+ case V4L2_SEL_TGT_CROP_DEFAULT:
|
||||
+ case V4L2_SEL_TGT_CROP_BOUNDS:
|
||||
+ sel->r.left = 0;
|
||||
+ sel->r.top = 0;
|
||||
+ sel->r.width = sink->width;
|
||||
+ sel->r.height = sink->height;
|
||||
+ break;
|
||||
+ case V4L2_SEL_TGT_CROP:
|
||||
+ sel->r = *crop;
|
||||
+ break;
|
||||
+ default:
|
||||
+ ret = -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int rkcif_interface_set_sel(struct v4l2_subdev *sd,
|
||||
+ struct v4l2_subdev_state *state,
|
||||
+ struct v4l2_subdev_selection *sel)
|
||||
+{
|
||||
+ struct v4l2_mbus_framefmt *sink, *src;
|
||||
+ struct v4l2_rect *crop;
|
||||
+
|
||||
+ if (sel->pad != RKCIF_IF_PAD_SRC || sel->target != V4L2_SEL_TGT_CROP)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ sink = v4l2_subdev_state_get_opposite_stream_format(state, sel->pad,
|
||||
+ sel->stream);
|
||||
+ if (!sink)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ src = v4l2_subdev_state_get_format(state, sel->pad, sel->stream);
|
||||
+ if (!src)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream);
|
||||
+ if (!crop)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ *crop = sel->r;
|
||||
+
|
||||
+ src->height = sel->r.height;
|
||||
+ src->width = sel->r.width;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int rkcif_interface_set_routing(struct v4l2_subdev *sd,
|
||||
+ struct v4l2_subdev_state *state,
|
||||
+ enum v4l2_subdev_format_whence which,
|
||||
+ struct v4l2_subdev_krouting *routing)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = v4l2_subdev_routing_validate(sd, routing,
|
||||
+ V4L2_SUBDEV_ROUTING_ONLY_1_TO_1);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = v4l2_subdev_set_routing(sd, state, routing);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int rkcif_interface_enable_streams(struct v4l2_subdev *sd,
|
||||
+ struct v4l2_subdev_state *state,
|
||||
+ u32 pad, u64 streams_mask)
|
||||
+{
|
||||
+ struct v4l2_subdev *remote_sd;
|
||||
+ struct media_pad *remote_pad;
|
||||
+ u64 mask;
|
||||
+
|
||||
+ remote_pad =
|
||||
+ media_pad_remote_pad_first(&sd->entity.pads[RKCIF_IF_PAD_SINK]);
|
||||
+ remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
|
||||
+
|
||||
+ mask = v4l2_subdev_state_xlate_streams(state, RKCIF_IF_PAD_SINK,
|
||||
+ RKCIF_IF_PAD_SRC, &streams_mask);
|
||||
+
|
||||
+ return v4l2_subdev_enable_streams(remote_sd, remote_pad->index, mask);
|
||||
+}
|
||||
+
|
||||
+static int rkcif_interface_disable_streams(struct v4l2_subdev *sd,
|
||||
+ struct v4l2_subdev_state *state,
|
||||
+ u32 pad, u64 streams_mask)
|
||||
+{
|
||||
+ struct v4l2_subdev *remote_sd;
|
||||
+ struct media_pad *remote_pad;
|
||||
+ u64 mask;
|
||||
+
|
||||
+ remote_pad =
|
||||
+ media_pad_remote_pad_first(&sd->entity.pads[RKCIF_IF_PAD_SINK]);
|
||||
+ remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
|
||||
+
|
||||
+ mask = v4l2_subdev_state_xlate_streams(state, RKCIF_IF_PAD_SINK,
|
||||
+ RKCIF_IF_PAD_SRC, &streams_mask);
|
||||
+
|
||||
+ return v4l2_subdev_disable_streams(remote_sd, remote_pad->index, mask);
|
||||
+}
|
||||
+
|
||||
+static const struct v4l2_subdev_pad_ops rkcif_interface_pad_ops = {
|
||||
+ .get_fmt = v4l2_subdev_get_fmt,
|
||||
+ .set_fmt = rkcif_interface_set_fmt,
|
||||
+ .get_selection = rkcif_interface_get_sel,
|
||||
+ .set_selection = rkcif_interface_set_sel,
|
||||
+ .set_routing = rkcif_interface_set_routing,
|
||||
+ .enable_streams = rkcif_interface_enable_streams,
|
||||
+ .disable_streams = rkcif_interface_disable_streams,
|
||||
+};
|
||||
+
|
||||
+static const struct v4l2_subdev_ops rkcif_interface_ops = {
|
||||
+ .pad = &rkcif_interface_pad_ops,
|
||||
+};
|
||||
+
|
||||
+static int rkcif_interface_init_state(struct v4l2_subdev *sd,
|
||||
+ struct v4l2_subdev_state *state)
|
||||
+{
|
||||
+ struct rkcif_interface *interface = to_rkcif_interface(sd);
|
||||
+ struct v4l2_subdev_route routes[] = {
|
||||
+ {
|
||||
+ .sink_pad = RKCIF_IF_PAD_SINK,
|
||||
+ .sink_stream = 0,
|
||||
+ .source_pad = RKCIF_IF_PAD_SRC,
|
||||
+ .source_stream = 0,
|
||||
+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
|
||||
+ },
|
||||
+ };
|
||||
+ struct v4l2_subdev_krouting routing = {
|
||||
+ .len_routes = ARRAY_SIZE(routes),
|
||||
+ .num_routes = ARRAY_SIZE(routes),
|
||||
+ .routes = routes,
|
||||
+ };
|
||||
+ const struct v4l2_mbus_framefmt dvp_default_format = {
|
||||
+ .width = 3840,
|
||||
+ .height = 2160,
|
||||
+ .code = MEDIA_BUS_FMT_YUYV8_1X16,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ .colorspace = V4L2_COLORSPACE_REC709,
|
||||
+ .ycbcr_enc = V4L2_YCBCR_ENC_709,
|
||||
+ .quantization = V4L2_QUANTIZATION_LIM_RANGE,
|
||||
+ .xfer_func = V4L2_XFER_FUNC_NONE,
|
||||
+ };
|
||||
+ const struct v4l2_mbus_framefmt mipi_default_format = {
|
||||
+ .width = 3840,
|
||||
+ .height = 2160,
|
||||
+ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ .colorspace = V4L2_COLORSPACE_RAW,
|
||||
+ .ycbcr_enc = V4L2_YCBCR_ENC_601,
|
||||
+ .quantization = V4L2_QUANTIZATION_FULL_RANGE,
|
||||
+ .xfer_func = V4L2_XFER_FUNC_NONE,
|
||||
+ };
|
||||
+ const struct v4l2_mbus_framefmt *default_format;
|
||||
+ int ret;
|
||||
+
|
||||
+ default_format = (interface->type == RKCIF_IF_DVP) ?
|
||||
+ &dvp_default_format :
|
||||
+ &mipi_default_format;
|
||||
+
|
||||
+ ret = v4l2_subdev_set_routing_with_fmt(sd, state, &routing,
|
||||
+ default_format);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static const struct v4l2_subdev_internal_ops rkcif_interface_internal_ops = {
|
||||
+ .init_state = rkcif_interface_init_state,
|
||||
+};
|
||||
+
|
||||
+static int rkcif_interface_add(struct rkcif_interface *interface)
|
||||
+{
|
||||
+ struct rkcif_device *rkcif = interface->rkcif;
|
||||
+ struct rkcif_remote *remote;
|
||||
+ struct v4l2_async_notifier *ntf = &rkcif->notifier;
|
||||
+ struct v4l2_fwnode_endpoint *vep = &interface->vep;
|
||||
+ struct device *dev = rkcif->dev;
|
||||
+ struct fwnode_handle *ep;
|
||||
+ u32 dvp_clk_delay = 0;
|
||||
+ int ret;
|
||||
+
|
||||
+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), interface->index,
|
||||
+ 0, 0);
|
||||
+ if (!ep)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ vep->bus_type = V4L2_MBUS_UNKNOWN;
|
||||
+ ret = v4l2_fwnode_endpoint_parse(ep, vep);
|
||||
+ if (ret)
|
||||
+ goto complete;
|
||||
+
|
||||
+ if (interface->type == RKCIF_IF_DVP) {
|
||||
+ if (vep->bus_type != V4L2_MBUS_BT656 &&
|
||||
+ vep->bus_type != V4L2_MBUS_PARALLEL) {
|
||||
+ ret = dev_err_probe(dev, -EINVAL,
|
||||
+ "unsupported bus type\n");
|
||||
+ goto complete;
|
||||
+ }
|
||||
+
|
||||
+ fwnode_property_read_u32(ep, "rockchip,dvp-clk-delay",
|
||||
+ &dvp_clk_delay);
|
||||
+ interface->dvp.dvp_clk_delay = dvp_clk_delay;
|
||||
+ }
|
||||
+
|
||||
+ remote = v4l2_async_nf_add_fwnode_remote(ntf, ep, struct rkcif_remote);
|
||||
+ if (IS_ERR(remote)) {
|
||||
+ ret = PTR_ERR(remote);
|
||||
+ goto complete;
|
||||
+ }
|
||||
+
|
||||
+ remote->interface = interface;
|
||||
+ interface->remote = remote;
|
||||
+ interface->status = RKCIF_IF_ACTIVE;
|
||||
+ ret = 0;
|
||||
+
|
||||
+complete:
|
||||
+ fwnode_handle_put(ep);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+int rkcif_interface_register(struct rkcif_device *rkcif,
|
||||
+ struct rkcif_interface *interface)
|
||||
+{
|
||||
+ struct media_pad *pads = interface->pads;
|
||||
+ struct v4l2_subdev *sd = &interface->sd;
|
||||
+ int ret;
|
||||
+
|
||||
+ interface->rkcif = rkcif;
|
||||
+
|
||||
+ v4l2_subdev_init(sd, &rkcif_interface_ops);
|
||||
+ sd->dev = rkcif->dev;
|
||||
+ sd->entity.ops = &rkcif_interface_media_ops;
|
||||
+ sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
|
||||
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;
|
||||
+ sd->internal_ops = &rkcif_interface_internal_ops;
|
||||
+ sd->owner = THIS_MODULE;
|
||||
+
|
||||
+ if (interface->type == RKCIF_IF_DVP)
|
||||
+ snprintf(sd->name, sizeof(sd->name), "rkcif-dvp0");
|
||||
+ else if (interface->type == RKCIF_IF_MIPI)
|
||||
+ snprintf(sd->name, sizeof(sd->name), "rkcif-mipi%d",
|
||||
+ interface->index - RKCIF_MIPI_BASE);
|
||||
+
|
||||
+ pads[RKCIF_IF_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
|
||||
+ pads[RKCIF_IF_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
|
||||
+ ret = media_entity_pads_init(&sd->entity, RKCIF_IF_PAD_MAX, pads);
|
||||
+ if (ret)
|
||||
+ goto err;
|
||||
+
|
||||
+ ret = v4l2_subdev_init_finalize(sd);
|
||||
+ if (ret)
|
||||
+ goto err_entity_cleanup;
|
||||
+
|
||||
+ ret = v4l2_device_register_subdev(&rkcif->v4l2_dev, sd);
|
||||
+ if (ret) {
|
||||
+ dev_err(sd->dev, "failed to register subdev\n");
|
||||
+ goto err_subdev_cleanup;
|
||||
+ }
|
||||
+
|
||||
+ ret = rkcif_interface_add(interface);
|
||||
+ if (ret)
|
||||
+ goto err_subdev_unregister;
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+err_subdev_unregister:
|
||||
+ v4l2_device_unregister_subdev(sd);
|
||||
+err_subdev_cleanup:
|
||||
+ v4l2_subdev_cleanup(sd);
|
||||
+err_entity_cleanup:
|
||||
+ media_entity_cleanup(&sd->entity);
|
||||
+err:
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+void rkcif_interface_unregister(struct rkcif_interface *interface)
|
||||
+{
|
||||
+ struct v4l2_subdev *sd = &interface->sd;
|
||||
+
|
||||
+ if (interface->status != RKCIF_IF_ACTIVE)
|
||||
+ return;
|
||||
+
|
||||
+ v4l2_device_unregister_subdev(sd);
|
||||
+ v4l2_subdev_cleanup(sd);
|
||||
+ media_entity_cleanup(&sd->entity);
|
||||
+}
|
||||
+
|
||||
+const struct rkcif_input_fmt *
|
||||
+rkcif_interface_find_input_fmt(struct rkcif_interface *interface, bool ret_def,
|
||||
+ u32 mbus_code)
|
||||
+{
|
||||
+ const struct rkcif_input_fmt *fmt;
|
||||
+
|
||||
+ WARN_ON(interface->in_fmts_num == 0);
|
||||
+
|
||||
+ for (unsigned int i = 0; i < interface->in_fmts_num; i++) {
|
||||
+ fmt = &interface->in_fmts[i];
|
||||
+ if (fmt->mbus_code == mbus_code)
|
||||
+ return fmt;
|
||||
+ }
|
||||
+ if (ret_def)
|
||||
+ return &interface->in_fmts[0];
|
||||
+ else
|
||||
+ return NULL;
|
||||
+}
|
||||
--- /dev/null
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-interface.h
|
||||
@@ -0,0 +1,31 @@
|
||||
+/* SPDX-License-Identifier: GPL-2.0 */
|
||||
+/*
|
||||
+ * Rockchip Camera Interface (CIF) Driver
|
||||
+ *
|
||||
+ * Abstraction for the INTERFACE and CROP parts of the different CIF variants.
|
||||
+ * They shall be represented as V4L2 subdevice with one sink pad and one
|
||||
+ * source pad. The sink pad is connected to a subdevice: either the subdevice
|
||||
+ * provided by the driver of the companion chip connected to the DVP, or the
|
||||
+ * subdevice provided by the MIPI CSI-2 receiver driver. The source pad is
|
||||
+ * to V4l2 device(s) provided by one or many instance(s) of the DMA
|
||||
+ * abstraction.
|
||||
+ *
|
||||
+ * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net>
|
||||
+ * Copyright (C) 2025 Collabora, Ltd.
|
||||
+ */
|
||||
+
|
||||
+#ifndef _RKCIF_INTERFACE_H
|
||||
+#define _RKCIF_INTERFACE_H
|
||||
+
|
||||
+#include "rkcif-common.h"
|
||||
+
|
||||
+int rkcif_interface_register(struct rkcif_device *rkcif,
|
||||
+ struct rkcif_interface *interface);
|
||||
+
|
||||
+void rkcif_interface_unregister(struct rkcif_interface *interface);
|
||||
+
|
||||
+const struct rkcif_input_fmt *
|
||||
+rkcif_interface_find_input_fmt(struct rkcif_interface *interface, bool ret_def,
|
||||
+ u32 mbus_code);
|
||||
+
|
||||
+#endif
|
||||
+883
@@ -0,0 +1,883 @@
|
||||
From 501802e2ad517a1e2b9766e1b4e396b10f13f140 Mon Sep 17 00:00:00 2001
|
||||
From: Michael Riesch <michael.riesch@collabora.com>
|
||||
Date: Fri, 14 Nov 2025 16:20:18 +0100
|
||||
Subject: [PATCH] media: rockchip: rkcif: add abstraction for dma blocks
|
||||
|
||||
Add an abstraction for the DMA parts and the ping-pong scheme (a
|
||||
double-buffering mechanism) of the different CIF variants. Each
|
||||
stream is represented as V4L2 device whose corresponding media
|
||||
entity has one sink pad. This sink pad is connected to an instance
|
||||
of the INTERFACE/CROP abstraction.
|
||||
|
||||
Tested-by: Gerald Loacker <gerald.loacker@wolfvision.net>
|
||||
Reviewed-by: Gerald Loacker <gerald.loacker@wolfvision.net>
|
||||
Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
|
||||
Reviewed-by: Mehdi Djait <mehdi.djait@linux.intel.com>
|
||||
Signed-off-by: Michael Riesch <michael.riesch@collabora.com>
|
||||
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
|
||||
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
|
||||
[hverkuil: dropped deprecated vb2_ops_wait_prepare/finish callbacks]
|
||||
---
|
||||
.../media/platform/rockchip/rkcif/Makefile | 1 +
|
||||
.../platform/rockchip/rkcif/rkcif-common.h | 67 ++
|
||||
.../platform/rockchip/rkcif/rkcif-interface.c | 37 +
|
||||
.../platform/rockchip/rkcif/rkcif-stream.c | 636 ++++++++++++++++++
|
||||
.../platform/rockchip/rkcif/rkcif-stream.h | 32 +
|
||||
5 files changed, 773 insertions(+)
|
||||
create mode 100644 drivers/media/platform/rockchip/rkcif/rkcif-stream.c
|
||||
create mode 100644 drivers/media/platform/rockchip/rkcif/rkcif-stream.h
|
||||
|
||||
--- a/drivers/media/platform/rockchip/rkcif/Makefile
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/Makefile
|
||||
@@ -3,3 +3,4 @@ obj-$(CONFIG_VIDEO_ROCKCHIP_CIF) += rock
|
||||
|
||||
rockchip-cif-objs += rkcif-dev.o
|
||||
rockchip-cif-objs += rkcif-interface.o
|
||||
+rockchip-cif-objs += rkcif-stream.o
|
||||
--- a/drivers/media/platform/rockchip/rkcif/rkcif-common.h
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-common.h
|
||||
@@ -33,6 +33,14 @@ enum rkcif_format_type {
|
||||
RKCIF_FMT_TYPE_RAW,
|
||||
};
|
||||
|
||||
+enum rkcif_id_index {
|
||||
+ RKCIF_ID0,
|
||||
+ RKCIF_ID1,
|
||||
+ RKCIF_ID2,
|
||||
+ RKCIF_ID3,
|
||||
+ RKCIF_ID_MAX
|
||||
+};
|
||||
+
|
||||
enum rkcif_interface_index {
|
||||
RKCIF_DVP,
|
||||
RKCIF_MIPI_BASE,
|
||||
@@ -63,6 +71,19 @@ enum rkcif_interface_type {
|
||||
RKCIF_IF_MIPI,
|
||||
};
|
||||
|
||||
+struct rkcif_buffer {
|
||||
+ struct vb2_v4l2_buffer vb;
|
||||
+ struct list_head queue;
|
||||
+ dma_addr_t buff_addr[VIDEO_MAX_PLANES];
|
||||
+ bool is_dummy;
|
||||
+};
|
||||
+
|
||||
+struct rkcif_dummy_buffer {
|
||||
+ struct rkcif_buffer buffer;
|
||||
+ void *vaddr;
|
||||
+ u32 size;
|
||||
+};
|
||||
+
|
||||
struct rkcif_input_fmt {
|
||||
u32 mbus_code;
|
||||
|
||||
@@ -70,6 +91,12 @@ struct rkcif_input_fmt {
|
||||
enum v4l2_field field;
|
||||
};
|
||||
|
||||
+struct rkcif_output_fmt {
|
||||
+ u32 fourcc;
|
||||
+ u32 mbus_code;
|
||||
+ u8 cplanes;
|
||||
+};
|
||||
+
|
||||
struct rkcif_interface;
|
||||
|
||||
struct rkcif_remote {
|
||||
@@ -79,6 +106,42 @@ struct rkcif_remote {
|
||||
struct rkcif_interface *interface;
|
||||
};
|
||||
|
||||
+struct rkcif_stream {
|
||||
+ enum rkcif_id_index id;
|
||||
+ struct rkcif_device *rkcif;
|
||||
+ struct rkcif_interface *interface;
|
||||
+ const struct rkcif_output_fmt *out_fmts;
|
||||
+ unsigned int out_fmts_num;
|
||||
+
|
||||
+ /* in ping-pong mode, two buffers can be provided to the HW */
|
||||
+ struct rkcif_buffer *buffers[2];
|
||||
+ int frame_idx;
|
||||
+ int frame_phase;
|
||||
+
|
||||
+ /* in case of no available buffer, HW can write to the dummy buffer */
|
||||
+ struct rkcif_dummy_buffer dummy;
|
||||
+
|
||||
+ bool stopping;
|
||||
+ wait_queue_head_t wq_stopped;
|
||||
+
|
||||
+ /* queue of available buffers plus spinlock that protects it */
|
||||
+ spinlock_t driver_queue_lock;
|
||||
+ struct list_head driver_queue;
|
||||
+
|
||||
+ /* lock used by the V4L2 core */
|
||||
+ struct mutex vlock;
|
||||
+
|
||||
+ struct media_pad pad;
|
||||
+ struct media_pipeline pipeline;
|
||||
+ struct v4l2_pix_format_mplane pix;
|
||||
+ struct vb2_queue buf_queue;
|
||||
+ struct video_device vdev;
|
||||
+
|
||||
+ void (*queue_buffer)(struct rkcif_stream *stream, unsigned int index);
|
||||
+ int (*start_streaming)(struct rkcif_stream *stream);
|
||||
+ void (*stop_streaming)(struct rkcif_stream *stream);
|
||||
+};
|
||||
+
|
||||
struct rkcif_dvp {
|
||||
u32 dvp_clk_delay;
|
||||
};
|
||||
@@ -89,6 +152,8 @@ struct rkcif_interface {
|
||||
enum rkcif_interface_index index;
|
||||
struct rkcif_device *rkcif;
|
||||
struct rkcif_remote *remote;
|
||||
+ struct rkcif_stream streams[RKCIF_ID_MAX];
|
||||
+ unsigned int streams_num;
|
||||
const struct rkcif_input_fmt *in_fmts;
|
||||
unsigned int in_fmts_num;
|
||||
|
||||
@@ -99,6 +164,8 @@ struct rkcif_interface {
|
||||
union {
|
||||
struct rkcif_dvp dvp;
|
||||
};
|
||||
+
|
||||
+ void (*set_crop)(struct rkcif_stream *stream, u16 left, u16 top);
|
||||
};
|
||||
|
||||
struct rkcif_match_data {
|
||||
--- a/drivers/media/platform/rockchip/rkcif/rkcif-interface.c
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-interface.c
|
||||
@@ -157,15 +157,41 @@ static int rkcif_interface_set_routing(s
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
+ for (unsigned int i = 0; i < routing->num_routes; i++) {
|
||||
+ const struct v4l2_subdev_route *route = &routing->routes[i];
|
||||
+
|
||||
+ if (route->source_stream >= RKCIF_ID_MAX)
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
ret = v4l2_subdev_set_routing(sd, state, routing);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
+static int rkcif_interface_apply_crop(struct rkcif_stream *stream,
|
||||
+ struct v4l2_subdev_state *state)
|
||||
+{
|
||||
+ struct rkcif_interface *interface = stream->interface;
|
||||
+ struct v4l2_rect *crop;
|
||||
+
|
||||
+ crop = v4l2_subdev_state_get_crop(state, RKCIF_IF_PAD_SRC, stream->id);
|
||||
+ if (!crop)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (interface->set_crop)
|
||||
+ interface->set_crop(stream, crop->left, crop->top);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static int rkcif_interface_enable_streams(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *state,
|
||||
u32 pad, u64 streams_mask)
|
||||
{
|
||||
+ struct rkcif_interface *interface = to_rkcif_interface(sd);
|
||||
+ struct rkcif_stream *stream;
|
||||
+ struct v4l2_subdev_route *route;
|
||||
struct v4l2_subdev *remote_sd;
|
||||
struct media_pad *remote_pad;
|
||||
u64 mask;
|
||||
@@ -174,6 +200,17 @@ static int rkcif_interface_enable_stream
|
||||
media_pad_remote_pad_first(&sd->entity.pads[RKCIF_IF_PAD_SINK]);
|
||||
remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
|
||||
|
||||
+ /* DVP has one crop setting for all IDs */
|
||||
+ if (interface->type == RKCIF_IF_DVP) {
|
||||
+ stream = &interface->streams[RKCIF_ID0];
|
||||
+ rkcif_interface_apply_crop(stream, state);
|
||||
+ } else {
|
||||
+ for_each_active_route(&state->routing, route) {
|
||||
+ stream = &interface->streams[route->sink_stream];
|
||||
+ rkcif_interface_apply_crop(stream, state);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
mask = v4l2_subdev_state_xlate_streams(state, RKCIF_IF_PAD_SINK,
|
||||
RKCIF_IF_PAD_SRC, &streams_mask);
|
||||
|
||||
--- /dev/null
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-stream.c
|
||||
@@ -0,0 +1,636 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0
|
||||
+/*
|
||||
+ * Rockchip Camera Interface (CIF) Driver
|
||||
+ *
|
||||
+ * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net>
|
||||
+ * Copyright (C) 2025 Collabora, Ltd.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/pm_runtime.h>
|
||||
+
|
||||
+#include <media/v4l2-common.h>
|
||||
+#include <media/v4l2-fwnode.h>
|
||||
+#include <media/v4l2-ioctl.h>
|
||||
+#include <media/v4l2-mc.h>
|
||||
+#include <media/v4l2-subdev.h>
|
||||
+#include <media/videobuf2-dma-contig.h>
|
||||
+
|
||||
+#include "rkcif-common.h"
|
||||
+#include "rkcif-stream.h"
|
||||
+
|
||||
+#define CIF_REQ_BUFS_MIN 1
|
||||
+#define CIF_MIN_WIDTH 64
|
||||
+#define CIF_MIN_HEIGHT 64
|
||||
+#define CIF_MAX_WIDTH 8192
|
||||
+#define CIF_MAX_HEIGHT 8192
|
||||
+
|
||||
+static inline struct rkcif_buffer *to_rkcif_buffer(struct vb2_v4l2_buffer *vb)
|
||||
+{
|
||||
+ return container_of(vb, struct rkcif_buffer, vb);
|
||||
+}
|
||||
+
|
||||
+static inline struct rkcif_stream *to_rkcif_stream(struct video_device *vdev)
|
||||
+{
|
||||
+ return container_of(vdev, struct rkcif_stream, vdev);
|
||||
+}
|
||||
+
|
||||
+static struct rkcif_buffer *rkcif_stream_pop_buffer(struct rkcif_stream *stream)
|
||||
+{
|
||||
+ struct rkcif_buffer *buffer;
|
||||
+
|
||||
+ guard(spinlock_irqsave)(&stream->driver_queue_lock);
|
||||
+
|
||||
+ if (list_empty(&stream->driver_queue))
|
||||
+ return NULL;
|
||||
+
|
||||
+ buffer = list_first_entry(&stream->driver_queue, struct rkcif_buffer,
|
||||
+ queue);
|
||||
+ list_del(&buffer->queue);
|
||||
+
|
||||
+ return buffer;
|
||||
+}
|
||||
+
|
||||
+static void rkcif_stream_push_buffer(struct rkcif_stream *stream,
|
||||
+ struct rkcif_buffer *buffer)
|
||||
+{
|
||||
+ guard(spinlock_irqsave)(&stream->driver_queue_lock);
|
||||
+
|
||||
+ list_add_tail(&buffer->queue, &stream->driver_queue);
|
||||
+}
|
||||
+
|
||||
+static inline void rkcif_stream_return_buffer(struct rkcif_buffer *buffer,
|
||||
+ enum vb2_buffer_state state)
|
||||
+{
|
||||
+ struct vb2_v4l2_buffer *vb = &buffer->vb;
|
||||
+
|
||||
+ vb2_buffer_done(&vb->vb2_buf, state);
|
||||
+}
|
||||
+
|
||||
+static void rkcif_stream_complete_buffer(struct rkcif_stream *stream,
|
||||
+ struct rkcif_buffer *buffer)
|
||||
+{
|
||||
+ struct vb2_v4l2_buffer *vb = &buffer->vb;
|
||||
+
|
||||
+ vb->vb2_buf.timestamp = ktime_get_ns();
|
||||
+ vb->sequence = stream->frame_idx;
|
||||
+ vb2_buffer_done(&vb->vb2_buf, VB2_BUF_STATE_DONE);
|
||||
+ stream->frame_idx++;
|
||||
+}
|
||||
+
|
||||
+void rkcif_stream_pingpong(struct rkcif_stream *stream)
|
||||
+{
|
||||
+ struct rkcif_buffer *buffer;
|
||||
+
|
||||
+ buffer = stream->buffers[stream->frame_phase];
|
||||
+ if (!buffer->is_dummy)
|
||||
+ rkcif_stream_complete_buffer(stream, buffer);
|
||||
+
|
||||
+ buffer = rkcif_stream_pop_buffer(stream);
|
||||
+ if (buffer) {
|
||||
+ stream->buffers[stream->frame_phase] = buffer;
|
||||
+ stream->buffers[stream->frame_phase]->is_dummy = false;
|
||||
+ } else {
|
||||
+ stream->buffers[stream->frame_phase] = &stream->dummy.buffer;
|
||||
+ stream->buffers[stream->frame_phase]->is_dummy = true;
|
||||
+ dev_dbg(stream->rkcif->dev,
|
||||
+ "no buffer available, frame will be dropped\n");
|
||||
+ }
|
||||
+
|
||||
+ if (stream->queue_buffer)
|
||||
+ stream->queue_buffer(stream, stream->frame_phase);
|
||||
+
|
||||
+ stream->frame_phase = 1 - stream->frame_phase;
|
||||
+}
|
||||
+
|
||||
+static int rkcif_stream_init_buffers(struct rkcif_stream *stream)
|
||||
+{
|
||||
+ struct v4l2_pix_format_mplane *pix = &stream->pix;
|
||||
+
|
||||
+ stream->buffers[0] = rkcif_stream_pop_buffer(stream);
|
||||
+ if (!stream->buffers[0])
|
||||
+ goto err_buff_0;
|
||||
+
|
||||
+ stream->buffers[1] = rkcif_stream_pop_buffer(stream);
|
||||
+ if (!stream->buffers[1])
|
||||
+ goto err_buff_1;
|
||||
+
|
||||
+ if (stream->queue_buffer) {
|
||||
+ stream->queue_buffer(stream, 0);
|
||||
+ stream->queue_buffer(stream, 1);
|
||||
+ }
|
||||
+
|
||||
+ stream->dummy.size = pix->num_planes * pix->plane_fmt[0].sizeimage;
|
||||
+ stream->dummy.vaddr =
|
||||
+ dma_alloc_attrs(stream->rkcif->dev, stream->dummy.size,
|
||||
+ &stream->dummy.buffer.buff_addr[0], GFP_KERNEL,
|
||||
+ DMA_ATTR_NO_KERNEL_MAPPING);
|
||||
+ if (!stream->dummy.vaddr)
|
||||
+ goto err_dummy;
|
||||
+
|
||||
+ for (unsigned int i = 1; i < pix->num_planes; i++)
|
||||
+ stream->dummy.buffer.buff_addr[i] =
|
||||
+ stream->dummy.buffer.buff_addr[i - 1] +
|
||||
+ pix->plane_fmt[i - 1].bytesperline * pix->height;
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+err_dummy:
|
||||
+ rkcif_stream_return_buffer(stream->buffers[1], VB2_BUF_STATE_QUEUED);
|
||||
+ stream->buffers[1] = NULL;
|
||||
+
|
||||
+err_buff_1:
|
||||
+ rkcif_stream_return_buffer(stream->buffers[0], VB2_BUF_STATE_QUEUED);
|
||||
+ stream->buffers[0] = NULL;
|
||||
+err_buff_0:
|
||||
+ return -EINVAL;
|
||||
+}
|
||||
+
|
||||
+static void rkcif_stream_return_all_buffers(struct rkcif_stream *stream,
|
||||
+ enum vb2_buffer_state state)
|
||||
+{
|
||||
+ struct rkcif_buffer *buffer;
|
||||
+
|
||||
+ if (stream->buffers[0] && !stream->buffers[0]->is_dummy) {
|
||||
+ rkcif_stream_return_buffer(stream->buffers[0], state);
|
||||
+ stream->buffers[0] = NULL;
|
||||
+ }
|
||||
+
|
||||
+ if (stream->buffers[1] && !stream->buffers[1]->is_dummy) {
|
||||
+ rkcif_stream_return_buffer(stream->buffers[1], state);
|
||||
+ stream->buffers[1] = NULL;
|
||||
+ }
|
||||
+
|
||||
+ while ((buffer = rkcif_stream_pop_buffer(stream)))
|
||||
+ rkcif_stream_return_buffer(buffer, state);
|
||||
+
|
||||
+ if (stream->dummy.vaddr) {
|
||||
+ dma_free_attrs(stream->rkcif->dev, stream->dummy.size,
|
||||
+ stream->dummy.vaddr,
|
||||
+ stream->dummy.buffer.buff_addr[0],
|
||||
+ DMA_ATTR_NO_KERNEL_MAPPING);
|
||||
+ stream->dummy.vaddr = NULL;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int rkcif_stream_setup_queue(struct vb2_queue *queue,
|
||||
+ unsigned int *num_buffers,
|
||||
+ unsigned int *num_planes,
|
||||
+ unsigned int sizes[],
|
||||
+ struct device *alloc_devs[])
|
||||
+{
|
||||
+ struct rkcif_stream *stream = queue->drv_priv;
|
||||
+ struct v4l2_pix_format_mplane *pix = &stream->pix;
|
||||
+
|
||||
+ if (*num_planes) {
|
||||
+ if (*num_planes != pix->num_planes)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ for (unsigned int i = 0; i < pix->num_planes; i++)
|
||||
+ if (sizes[i] < pix->plane_fmt[i].sizeimage)
|
||||
+ return -EINVAL;
|
||||
+ } else {
|
||||
+ *num_planes = pix->num_planes;
|
||||
+ for (unsigned int i = 0; i < pix->num_planes; i++)
|
||||
+ sizes[i] = pix->plane_fmt[i].sizeimage;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int rkcif_stream_prepare_buffer(struct vb2_buffer *vb)
|
||||
+{
|
||||
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
|
||||
+ struct rkcif_buffer *buffer = to_rkcif_buffer(vbuf);
|
||||
+ struct rkcif_stream *stream = vb->vb2_queue->drv_priv;
|
||||
+ const struct rkcif_output_fmt *fmt;
|
||||
+ struct v4l2_pix_format_mplane *pix = &stream->pix;
|
||||
+ unsigned int i;
|
||||
+
|
||||
+ memset(buffer->buff_addr, 0, sizeof(buffer->buff_addr));
|
||||
+ for (i = 0; i < pix->num_planes; i++)
|
||||
+ buffer->buff_addr[i] = vb2_dma_contig_plane_dma_addr(vb, i);
|
||||
+
|
||||
+ /* apply fallback for non-mplane formats, if required */
|
||||
+ if (pix->num_planes == 1) {
|
||||
+ fmt = rkcif_stream_find_output_fmt(stream, true,
|
||||
+ pix->pixelformat);
|
||||
+ for (i = 1; i < fmt->cplanes; i++)
|
||||
+ buffer->buff_addr[i] =
|
||||
+ buffer->buff_addr[i - 1] +
|
||||
+ pix->plane_fmt[i - 1].bytesperline *
|
||||
+ pix->height;
|
||||
+ }
|
||||
+
|
||||
+ for (i = 0; i < pix->num_planes; i++) {
|
||||
+ unsigned long size = pix->plane_fmt[i].sizeimage;
|
||||
+
|
||||
+ if (vb2_plane_size(vb, i) < size) {
|
||||
+ dev_err(stream->rkcif->dev,
|
||||
+ "user buffer too small (%ld < %ld)\n",
|
||||
+ vb2_plane_size(vb, i), size);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ vb2_set_plane_payload(vb, i, size);
|
||||
+ }
|
||||
+
|
||||
+ vbuf->field = V4L2_FIELD_NONE;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void rkcif_stream_queue_buffer(struct vb2_buffer *vb)
|
||||
+{
|
||||
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
|
||||
+ struct rkcif_buffer *buffer = to_rkcif_buffer(vbuf);
|
||||
+ struct rkcif_stream *stream = vb->vb2_queue->drv_priv;
|
||||
+
|
||||
+ rkcif_stream_push_buffer(stream, buffer);
|
||||
+}
|
||||
+
|
||||
+static int rkcif_stream_start_streaming(struct vb2_queue *queue,
|
||||
+ unsigned int count)
|
||||
+{
|
||||
+ struct rkcif_stream *stream = queue->drv_priv;
|
||||
+ struct rkcif_device *rkcif = stream->rkcif;
|
||||
+ u64 mask;
|
||||
+ int ret;
|
||||
+
|
||||
+ stream->frame_idx = 0;
|
||||
+ stream->frame_phase = 0;
|
||||
+
|
||||
+ ret = video_device_pipeline_start(&stream->vdev, &stream->pipeline);
|
||||
+ if (ret) {
|
||||
+ dev_err(rkcif->dev, "failed to start pipeline %d\n", ret);
|
||||
+ goto err_out;
|
||||
+ }
|
||||
+
|
||||
+ ret = pm_runtime_resume_and_get(rkcif->dev);
|
||||
+ if (ret < 0) {
|
||||
+ dev_err(rkcif->dev, "failed to get runtime pm, %d\n", ret);
|
||||
+ goto err_pipeline_stop;
|
||||
+ }
|
||||
+
|
||||
+ ret = rkcif_stream_init_buffers(stream);
|
||||
+ if (ret)
|
||||
+ goto err_runtime_put;
|
||||
+
|
||||
+ if (stream->start_streaming) {
|
||||
+ ret = stream->start_streaming(stream);
|
||||
+ if (ret < 0)
|
||||
+ goto err_runtime_put;
|
||||
+ }
|
||||
+
|
||||
+ mask = BIT_ULL(stream->id);
|
||||
+ ret = v4l2_subdev_enable_streams(&stream->interface->sd,
|
||||
+ RKCIF_IF_PAD_SRC, mask);
|
||||
+ if (ret < 0)
|
||||
+ goto err_stop_stream;
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+err_stop_stream:
|
||||
+ if (stream->stop_streaming)
|
||||
+ stream->stop_streaming(stream);
|
||||
+err_runtime_put:
|
||||
+ pm_runtime_put(rkcif->dev);
|
||||
+err_pipeline_stop:
|
||||
+ video_device_pipeline_stop(&stream->vdev);
|
||||
+err_out:
|
||||
+ rkcif_stream_return_all_buffers(stream, VB2_BUF_STATE_QUEUED);
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static void rkcif_stream_stop_streaming(struct vb2_queue *queue)
|
||||
+{
|
||||
+ struct rkcif_stream *stream = queue->drv_priv;
|
||||
+ struct rkcif_device *rkcif = stream->rkcif;
|
||||
+ u64 mask;
|
||||
+ int ret;
|
||||
+
|
||||
+ mask = BIT_ULL(stream->id);
|
||||
+ v4l2_subdev_disable_streams(&stream->interface->sd, RKCIF_IF_PAD_SRC,
|
||||
+ mask);
|
||||
+
|
||||
+ stream->stopping = true;
|
||||
+ ret = wait_event_timeout(stream->wq_stopped, !stream->stopping,
|
||||
+ msecs_to_jiffies(1000));
|
||||
+
|
||||
+ if (!ret && stream->stop_streaming)
|
||||
+ stream->stop_streaming(stream);
|
||||
+
|
||||
+ pm_runtime_put(rkcif->dev);
|
||||
+
|
||||
+ rkcif_stream_return_all_buffers(stream, VB2_BUF_STATE_ERROR);
|
||||
+
|
||||
+ video_device_pipeline_stop(&stream->vdev);
|
||||
+}
|
||||
+
|
||||
+static const struct vb2_ops rkcif_stream_vb2_ops = {
|
||||
+ .queue_setup = rkcif_stream_setup_queue,
|
||||
+ .buf_prepare = rkcif_stream_prepare_buffer,
|
||||
+ .buf_queue = rkcif_stream_queue_buffer,
|
||||
+ .start_streaming = rkcif_stream_start_streaming,
|
||||
+ .stop_streaming = rkcif_stream_stop_streaming,
|
||||
+};
|
||||
+
|
||||
+static int rkcif_stream_fill_format(struct rkcif_stream *stream,
|
||||
+ struct v4l2_pix_format_mplane *pix)
|
||||
+{
|
||||
+ const struct rkcif_output_fmt *fmt;
|
||||
+ u32 height, width;
|
||||
+ int ret;
|
||||
+
|
||||
+ fmt = rkcif_stream_find_output_fmt(stream, true, pix->pixelformat);
|
||||
+ height = clamp_t(u32, pix->height, CIF_MIN_HEIGHT, CIF_MAX_HEIGHT);
|
||||
+ width = clamp_t(u32, pix->width, CIF_MIN_WIDTH, CIF_MAX_WIDTH);
|
||||
+ ret = v4l2_fill_pixfmt_mp(pix, fmt->fourcc, width, height);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ pix->field = V4L2_FIELD_NONE;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int rkcif_stream_try_format(struct file *file, void *fh,
|
||||
+ struct v4l2_format *f)
|
||||
+{
|
||||
+ struct rkcif_stream *stream = video_drvdata(file);
|
||||
+ struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
|
||||
+
|
||||
+ return rkcif_stream_fill_format(stream, pix);
|
||||
+}
|
||||
+
|
||||
+static int rkcif_stream_set_format(struct file *file, void *priv,
|
||||
+ struct v4l2_format *f)
|
||||
+{
|
||||
+ struct rkcif_stream *stream = video_drvdata(file);
|
||||
+ struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (vb2_is_busy(&stream->buf_queue))
|
||||
+ return -EBUSY;
|
||||
+
|
||||
+ ret = rkcif_stream_try_format(file, priv, f);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ stream->pix = *pix;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int rkcif_stream_get_format(struct file *file, void *fh,
|
||||
+ struct v4l2_format *f)
|
||||
+{
|
||||
+ struct rkcif_stream *stream = video_drvdata(file);
|
||||
+
|
||||
+ f->fmt.pix_mp = stream->pix;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int rkcif_stream_enum_formats(struct file *file, void *priv,
|
||||
+ struct v4l2_fmtdesc *f)
|
||||
+{
|
||||
+ struct rkcif_stream *stream = video_drvdata(file);
|
||||
+
|
||||
+ if (f->index >= stream->out_fmts_num)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ f->pixelformat = stream->out_fmts[f->index].fourcc;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int rkcif_stream_enum_framesizes(struct file *file, void *fh,
|
||||
+ struct v4l2_frmsizeenum *fsize)
|
||||
+{
|
||||
+ struct rkcif_stream *stream = video_drvdata(file);
|
||||
+
|
||||
+ if (fsize->index > 0)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (!rkcif_stream_find_output_fmt(stream, false, fsize->pixel_format))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
|
||||
+ fsize->stepwise.min_width = CIF_MIN_WIDTH;
|
||||
+ fsize->stepwise.max_width = CIF_MAX_WIDTH;
|
||||
+ fsize->stepwise.step_width = 8;
|
||||
+ fsize->stepwise.min_height = CIF_MIN_HEIGHT;
|
||||
+ fsize->stepwise.max_height = CIF_MAX_HEIGHT;
|
||||
+ fsize->stepwise.step_height = 8;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int rkcif_stream_querycap(struct file *file, void *priv,
|
||||
+ struct v4l2_capability *cap)
|
||||
+{
|
||||
+ struct rkcif_stream *stream = video_drvdata(file);
|
||||
+ struct device *dev = stream->rkcif->dev;
|
||||
+
|
||||
+ strscpy(cap->driver, dev->driver->name, sizeof(cap->driver));
|
||||
+ strscpy(cap->card, dev->driver->name, sizeof(cap->card));
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct v4l2_ioctl_ops rkcif_stream_ioctl_ops = {
|
||||
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
|
||||
+ .vidioc_querybuf = vb2_ioctl_querybuf,
|
||||
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
|
||||
+ .vidioc_qbuf = vb2_ioctl_qbuf,
|
||||
+ .vidioc_expbuf = vb2_ioctl_expbuf,
|
||||
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
|
||||
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
|
||||
+ .vidioc_streamon = vb2_ioctl_streamon,
|
||||
+ .vidioc_streamoff = vb2_ioctl_streamoff,
|
||||
+ .vidioc_try_fmt_vid_cap_mplane = rkcif_stream_try_format,
|
||||
+ .vidioc_s_fmt_vid_cap_mplane = rkcif_stream_set_format,
|
||||
+ .vidioc_g_fmt_vid_cap_mplane = rkcif_stream_get_format,
|
||||
+ .vidioc_enum_fmt_vid_cap = rkcif_stream_enum_formats,
|
||||
+ .vidioc_enum_framesizes = rkcif_stream_enum_framesizes,
|
||||
+ .vidioc_querycap = rkcif_stream_querycap,
|
||||
+};
|
||||
+
|
||||
+static int rkcif_stream_link_validate(struct media_link *link)
|
||||
+{
|
||||
+ struct video_device *vdev =
|
||||
+ media_entity_to_video_device(link->sink->entity);
|
||||
+ struct v4l2_mbus_framefmt *source_fmt;
|
||||
+ struct v4l2_subdev *sd;
|
||||
+ struct v4l2_subdev_state *state;
|
||||
+ struct rkcif_stream *stream = to_rkcif_stream(vdev);
|
||||
+ int ret = -EINVAL;
|
||||
+
|
||||
+ if (!media_entity_remote_source_pad_unique(link->sink->entity))
|
||||
+ return -ENOTCONN;
|
||||
+
|
||||
+ sd = media_entity_to_v4l2_subdev(link->source->entity);
|
||||
+
|
||||
+ state = v4l2_subdev_lock_and_get_active_state(sd);
|
||||
+
|
||||
+ source_fmt = v4l2_subdev_state_get_format(state, link->source->index,
|
||||
+ stream->id);
|
||||
+ if (!source_fmt)
|
||||
+ goto out;
|
||||
+
|
||||
+ if (source_fmt->height != stream->pix.height ||
|
||||
+ source_fmt->width != stream->pix.width) {
|
||||
+ dev_dbg(stream->rkcif->dev,
|
||||
+ "link '%s':%u -> '%s':%u not valid: %ux%u != %ux%u\n",
|
||||
+ link->source->entity->name, link->source->index,
|
||||
+ link->sink->entity->name, link->sink->index,
|
||||
+ source_fmt->width, source_fmt->height,
|
||||
+ stream->pix.width, stream->pix.height);
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ ret = 0;
|
||||
+
|
||||
+out:
|
||||
+ v4l2_subdev_unlock_state(state);
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static const struct media_entity_operations rkcif_stream_media_ops = {
|
||||
+ .link_validate = rkcif_stream_link_validate,
|
||||
+};
|
||||
+
|
||||
+static const struct v4l2_file_operations rkcif_stream_file_ops = {
|
||||
+ .open = v4l2_fh_open,
|
||||
+ .release = vb2_fop_release,
|
||||
+ .unlocked_ioctl = video_ioctl2,
|
||||
+ .poll = vb2_fop_poll,
|
||||
+ .mmap = vb2_fop_mmap,
|
||||
+};
|
||||
+
|
||||
+static int rkcif_stream_init_vb2_queue(struct vb2_queue *q,
|
||||
+ struct rkcif_stream *stream)
|
||||
+{
|
||||
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
|
||||
+ q->io_modes = VB2_MMAP | VB2_DMABUF;
|
||||
+ q->drv_priv = stream;
|
||||
+ q->ops = &rkcif_stream_vb2_ops;
|
||||
+ q->mem_ops = &vb2_dma_contig_memops;
|
||||
+ q->buf_struct_size = sizeof(struct rkcif_buffer);
|
||||
+ q->min_queued_buffers = CIF_REQ_BUFS_MIN;
|
||||
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
|
||||
+ q->lock = &stream->vlock;
|
||||
+ q->dev = stream->rkcif->dev;
|
||||
+
|
||||
+ return vb2_queue_init(q);
|
||||
+}
|
||||
+
|
||||
+int rkcif_stream_register(struct rkcif_device *rkcif,
|
||||
+ struct rkcif_stream *stream)
|
||||
+{
|
||||
+ struct rkcif_interface *interface = stream->interface;
|
||||
+ struct v4l2_device *v4l2_dev = &rkcif->v4l2_dev;
|
||||
+ struct video_device *vdev = &stream->vdev;
|
||||
+ u32 link_flags = 0;
|
||||
+ int ret;
|
||||
+
|
||||
+ stream->rkcif = rkcif;
|
||||
+
|
||||
+ INIT_LIST_HEAD(&stream->driver_queue);
|
||||
+ spin_lock_init(&stream->driver_queue_lock);
|
||||
+
|
||||
+ init_waitqueue_head(&stream->wq_stopped);
|
||||
+
|
||||
+ mutex_init(&stream->vlock);
|
||||
+
|
||||
+ vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING |
|
||||
+ V4L2_CAP_IO_MC;
|
||||
+ vdev->entity.ops = &rkcif_stream_media_ops;
|
||||
+ vdev->fops = &rkcif_stream_file_ops;
|
||||
+ vdev->ioctl_ops = &rkcif_stream_ioctl_ops;
|
||||
+ vdev->lock = &stream->vlock;
|
||||
+ vdev->minor = -1;
|
||||
+ vdev->release = video_device_release_empty;
|
||||
+ vdev->v4l2_dev = v4l2_dev;
|
||||
+ vdev->vfl_dir = VFL_DIR_RX;
|
||||
+ video_set_drvdata(vdev, stream);
|
||||
+
|
||||
+ stream->pad.flags = MEDIA_PAD_FL_SINK;
|
||||
+
|
||||
+ stream->pix.height = CIF_MIN_HEIGHT;
|
||||
+ stream->pix.width = CIF_MIN_WIDTH;
|
||||
+ rkcif_stream_fill_format(stream, &stream->pix);
|
||||
+
|
||||
+ rkcif_stream_init_vb2_queue(&stream->buf_queue, stream);
|
||||
+
|
||||
+ vdev->queue = &stream->buf_queue;
|
||||
+ if (interface->type == RKCIF_IF_DVP)
|
||||
+ snprintf(vdev->name, sizeof(vdev->name), "rkcif-dvp0-id%d",
|
||||
+ stream->id);
|
||||
+ else if (interface->type == RKCIF_IF_MIPI)
|
||||
+ snprintf(vdev->name, sizeof(vdev->name), "rkcif-mipi%d-id%d",
|
||||
+ interface->index - RKCIF_MIPI_BASE, stream->id);
|
||||
+
|
||||
+ ret = media_entity_pads_init(&vdev->entity, 1, &stream->pad);
|
||||
+ if (ret < 0) {
|
||||
+ dev_err(rkcif->dev,
|
||||
+ "failed to initialize stream media pad: %d\n", ret);
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
|
||||
+ if (ret < 0) {
|
||||
+ dev_err(rkcif->dev, "failed to register video device: %d\n",
|
||||
+ ret);
|
||||
+ goto err_media_entity_cleanup;
|
||||
+ }
|
||||
+
|
||||
+ /* enable only stream ID0 by default */
|
||||
+ if (stream->id == RKCIF_ID0)
|
||||
+ link_flags |= MEDIA_LNK_FL_ENABLED;
|
||||
+
|
||||
+ ret = media_create_pad_link(&interface->sd.entity, RKCIF_IF_PAD_SRC,
|
||||
+ &stream->vdev.entity, 0, link_flags);
|
||||
+ if (ret) {
|
||||
+ dev_err(rkcif->dev, "failed to link stream media pad: %d\n",
|
||||
+ ret);
|
||||
+ goto err_video_unregister;
|
||||
+ }
|
||||
+
|
||||
+ v4l2_info(v4l2_dev, "registered %s as /dev/video%d\n", vdev->name,
|
||||
+ vdev->num);
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+err_video_unregister:
|
||||
+ video_unregister_device(&stream->vdev);
|
||||
+err_media_entity_cleanup:
|
||||
+ media_entity_cleanup(&stream->vdev.entity);
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+void rkcif_stream_unregister(struct rkcif_stream *stream)
|
||||
+{
|
||||
+ video_unregister_device(&stream->vdev);
|
||||
+ media_entity_cleanup(&stream->vdev.entity);
|
||||
+}
|
||||
+
|
||||
+const struct rkcif_output_fmt *
|
||||
+rkcif_stream_find_output_fmt(struct rkcif_stream *stream, bool ret_def,
|
||||
+ u32 pixelfmt)
|
||||
+{
|
||||
+ const struct rkcif_output_fmt *fmt;
|
||||
+
|
||||
+ WARN_ON(stream->out_fmts_num == 0);
|
||||
+
|
||||
+ for (unsigned int i = 0; i < stream->out_fmts_num; i++) {
|
||||
+ fmt = &stream->out_fmts[i];
|
||||
+ if (fmt->fourcc == pixelfmt)
|
||||
+ return fmt;
|
||||
+ }
|
||||
+
|
||||
+ if (ret_def)
|
||||
+ return &stream->out_fmts[0];
|
||||
+ else
|
||||
+ return NULL;
|
||||
+}
|
||||
--- /dev/null
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-stream.h
|
||||
@@ -0,0 +1,32 @@
|
||||
+/* SPDX-License-Identifier: GPL-2.0 */
|
||||
+/*
|
||||
+ * Rockchip Camera Interface (CIF) Driver
|
||||
+ *
|
||||
+ * Abstraction for the DMA part and the ping-pong scheme (a double-buffering
|
||||
+ * mechanism) of the different CIF variants.
|
||||
+ * Each stream is represented as V4L2 device whose corresponding media entity
|
||||
+ * has one sink pad.
|
||||
+ * The sink pad is connected to an instance of the INTERFACE/CROP abstraction
|
||||
+ * in rkcif-interface.c.
|
||||
+ *
|
||||
+ * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net>
|
||||
+ * Copyright (C) 2025 Collabora, Ltd.
|
||||
+ */
|
||||
+
|
||||
+#ifndef _RKCIF_STREAM_H
|
||||
+#define _RKCIF_STREAM_H
|
||||
+
|
||||
+#include "rkcif-common.h"
|
||||
+
|
||||
+void rkcif_stream_pingpong(struct rkcif_stream *stream);
|
||||
+
|
||||
+int rkcif_stream_register(struct rkcif_device *rkcif,
|
||||
+ struct rkcif_stream *stream);
|
||||
+
|
||||
+void rkcif_stream_unregister(struct rkcif_stream *stream);
|
||||
+
|
||||
+const struct rkcif_output_fmt *
|
||||
+rkcif_stream_find_output_fmt(struct rkcif_stream *stream, bool ret_def,
|
||||
+ u32 pixelfmt);
|
||||
+
|
||||
+#endif
|
||||
+906
@@ -0,0 +1,906 @@
|
||||
From 75e28594624f68322c6eb052e5f4e8c0589d89a6 Mon Sep 17 00:00:00 2001
|
||||
From: Michael Riesch <michael.riesch@collabora.com>
|
||||
Date: Fri, 14 Nov 2025 16:20:19 +0100
|
||||
Subject: [PATCH] media: rockchip: rkcif: add support for px30 vip dvp capture
|
||||
|
||||
The PX30 Video Input Processor (VIP) unit features a Digital Video
|
||||
Port (DVP). Add support for the DVP in general and for the PX30
|
||||
VIP DVP in particular.
|
||||
|
||||
Tested-by: Gerald Loacker <gerald.loacker@wolfvision.net>
|
||||
Reviewed-by: Gerald Loacker <gerald.loacker@wolfvision.net>
|
||||
Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
|
||||
Reviewed-by: Mehdi Djait <mehdi.djait@linux.intel.com>
|
||||
Signed-off-by: Michael Riesch <michael.riesch@collabora.com>
|
||||
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
|
||||
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
|
||||
---
|
||||
.../media/platform/rockchip/rkcif/Makefile | 1 +
|
||||
.../rockchip/rkcif/rkcif-capture-dvp.c | 589 ++++++++++++++++++
|
||||
.../rockchip/rkcif/rkcif-capture-dvp.h | 24 +
|
||||
.../platform/rockchip/rkcif/rkcif-common.h | 28 +
|
||||
.../media/platform/rockchip/rkcif/rkcif-dev.c | 15 +
|
||||
.../platform/rockchip/rkcif/rkcif-regs.h | 131 ++++
|
||||
6 files changed, 788 insertions(+)
|
||||
create mode 100644 drivers/media/platform/rockchip/rkcif/rkcif-capture-dvp.c
|
||||
create mode 100644 drivers/media/platform/rockchip/rkcif/rkcif-capture-dvp.h
|
||||
create mode 100644 drivers/media/platform/rockchip/rkcif/rkcif-regs.h
|
||||
|
||||
--- a/drivers/media/platform/rockchip/rkcif/Makefile
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/Makefile
|
||||
@@ -1,6 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_VIDEO_ROCKCHIP_CIF) += rockchip-cif.o
|
||||
|
||||
+rockchip-cif-objs += rkcif-capture-dvp.o
|
||||
rockchip-cif-objs += rkcif-dev.o
|
||||
rockchip-cif-objs += rkcif-interface.o
|
||||
rockchip-cif-objs += rkcif-stream.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-capture-dvp.c
|
||||
@@ -0,0 +1,589 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0
|
||||
+/*
|
||||
+ * Rockchip Camera Interface (CIF) Driver
|
||||
+ *
|
||||
+ * Copyright (C) 2018 Rockchip Electronics Co., Ltd.
|
||||
+ * Copyright (C) 2020 Maxime Chevallier <maxime.chevallier@bootlin.com>
|
||||
+ * Copyright (C) 2023 Mehdi Djait <mehdi.djait@bootlin.com>
|
||||
+ * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net>
|
||||
+ * Copyright (C) 2025 Collabora, Ltd.
|
||||
+ */
|
||||
+
|
||||
+#include <media/v4l2-common.h>
|
||||
+#include <media/v4l2-fwnode.h>
|
||||
+#include <media/v4l2-mc.h>
|
||||
+#include <media/v4l2-subdev.h>
|
||||
+
|
||||
+#include "rkcif-capture-dvp.h"
|
||||
+#include "rkcif-common.h"
|
||||
+#include "rkcif-interface.h"
|
||||
+#include "rkcif-regs.h"
|
||||
+#include "rkcif-stream.h"
|
||||
+
|
||||
+static const struct rkcif_output_fmt dvp_out_fmts[] = {
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_NV16,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_OUTPUT_422 |
|
||||
+ RKCIF_FORMAT_UV_STORAGE_ORDER_UVUV,
|
||||
+ .cplanes = 2,
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_NV16M,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_OUTPUT_422 |
|
||||
+ RKCIF_FORMAT_UV_STORAGE_ORDER_UVUV,
|
||||
+ .cplanes = 2,
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_NV61,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_OUTPUT_422 |
|
||||
+ RKCIF_FORMAT_UV_STORAGE_ORDER_VUVU,
|
||||
+ .cplanes = 2,
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_NV61M,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_OUTPUT_422 |
|
||||
+ RKCIF_FORMAT_UV_STORAGE_ORDER_VUVU,
|
||||
+ .cplanes = 2,
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_NV12,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_OUTPUT_420 |
|
||||
+ RKCIF_FORMAT_UV_STORAGE_ORDER_UVUV,
|
||||
+ .cplanes = 2,
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_NV12M,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_OUTPUT_420 |
|
||||
+ RKCIF_FORMAT_UV_STORAGE_ORDER_UVUV,
|
||||
+ .cplanes = 2,
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_NV21,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_OUTPUT_420 |
|
||||
+ RKCIF_FORMAT_UV_STORAGE_ORDER_VUVU,
|
||||
+ .cplanes = 2,
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_NV21M,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_OUTPUT_420 |
|
||||
+ RKCIF_FORMAT_UV_STORAGE_ORDER_VUVU,
|
||||
+ .cplanes = 2,
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_RGB24,
|
||||
+ .cplanes = 1,
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_RGB565,
|
||||
+ .cplanes = 1,
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_BGR666,
|
||||
+ .cplanes = 1,
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_SRGGB8,
|
||||
+ .cplanes = 1,
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
|
||||
+ .cplanes = 1,
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
|
||||
+ .cplanes = 1,
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
|
||||
+ .cplanes = 1,
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_SRGGB10,
|
||||
+ .cplanes = 1,
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_SGRBG10,
|
||||
+ .cplanes = 1,
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_SGBRG10,
|
||||
+ .cplanes = 1,
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_SBGGR10,
|
||||
+ .cplanes = 1,
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_SRGGB12,
|
||||
+ .cplanes = 1,
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_SGRBG12,
|
||||
+ .cplanes = 1,
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_SGBRG12,
|
||||
+ .cplanes = 1,
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_SBGGR12,
|
||||
+ .cplanes = 1,
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_SBGGR16,
|
||||
+ .cplanes = 1,
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_Y16,
|
||||
+ .cplanes = 1,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static const struct rkcif_input_fmt px30_dvp_in_fmts[] = {
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
|
||||
+ RKCIF_FORMAT_YUV_INPUT_ORDER_YUYV,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_YUV,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
|
||||
+ RKCIF_FORMAT_YUV_INPUT_ORDER_YUYV,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_YUV,
|
||||
+ .field = V4L2_FIELD_INTERLACED,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
|
||||
+ RKCIF_FORMAT_YUV_INPUT_ORDER_YVYU,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_YUV,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
|
||||
+ RKCIF_FORMAT_YUV_INPUT_ORDER_YVYU,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_YUV,
|
||||
+ .field = V4L2_FIELD_INTERLACED,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
|
||||
+ RKCIF_FORMAT_YUV_INPUT_ORDER_UYVY,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_YUV,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
|
||||
+ RKCIF_FORMAT_YUV_INPUT_ORDER_UYVY,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_YUV,
|
||||
+ .field = V4L2_FIELD_INTERLACED,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
|
||||
+ RKCIF_FORMAT_YUV_INPUT_ORDER_VYUY,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_YUV,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
|
||||
+ RKCIF_FORMAT_YUV_INPUT_ORDER_VYUY,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_YUV,
|
||||
+ .field = V4L2_FIELD_INTERLACED,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
|
||||
+ RKCIF_FORMAT_RAW_DATA_WIDTH_8,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
|
||||
+ RKCIF_FORMAT_RAW_DATA_WIDTH_8,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
|
||||
+ RKCIF_FORMAT_RAW_DATA_WIDTH_8,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
|
||||
+ RKCIF_FORMAT_RAW_DATA_WIDTH_8,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
|
||||
+ RKCIF_FORMAT_RAW_DATA_WIDTH_10,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
|
||||
+ RKCIF_FORMAT_RAW_DATA_WIDTH_10,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
|
||||
+ RKCIF_FORMAT_RAW_DATA_WIDTH_10,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
|
||||
+ RKCIF_FORMAT_RAW_DATA_WIDTH_10,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
|
||||
+ RKCIF_FORMAT_RAW_DATA_WIDTH_12,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
|
||||
+ RKCIF_FORMAT_RAW_DATA_WIDTH_12,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
|
||||
+ RKCIF_FORMAT_RAW_DATA_WIDTH_12,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
|
||||
+ RKCIF_FORMAT_RAW_DATA_WIDTH_12,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_RGB888_1X24,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_Y8_1X8,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
|
||||
+ RKCIF_FORMAT_RAW_DATA_WIDTH_8,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_Y10_1X10,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
|
||||
+ RKCIF_FORMAT_RAW_DATA_WIDTH_10,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_Y12_1X12,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
|
||||
+ RKCIF_FORMAT_RAW_DATA_WIDTH_12,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ }
|
||||
+};
|
||||
+
|
||||
+const struct rkcif_dvp_match_data rkcif_px30_vip_dvp_match_data = {
|
||||
+ .in_fmts = px30_dvp_in_fmts,
|
||||
+ .in_fmts_num = ARRAY_SIZE(px30_dvp_in_fmts),
|
||||
+ .out_fmts = dvp_out_fmts,
|
||||
+ .out_fmts_num = ARRAY_SIZE(dvp_out_fmts),
|
||||
+ .has_scaler = true,
|
||||
+ .regs = {
|
||||
+ [RKCIF_DVP_CTRL] = 0x00,
|
||||
+ [RKCIF_DVP_INTEN] = 0x04,
|
||||
+ [RKCIF_DVP_INTSTAT] = 0x08,
|
||||
+ [RKCIF_DVP_FOR] = 0x0c,
|
||||
+ [RKCIF_DVP_LINE_NUM_ADDR] = 0x10,
|
||||
+ [RKCIF_DVP_FRM0_ADDR_Y] = 0x14,
|
||||
+ [RKCIF_DVP_FRM0_ADDR_UV] = 0x18,
|
||||
+ [RKCIF_DVP_FRM1_ADDR_Y] = 0x1c,
|
||||
+ [RKCIF_DVP_FRM1_ADDR_UV] = 0x20,
|
||||
+ [RKCIF_DVP_VIR_LINE_WIDTH] = 0x24,
|
||||
+ [RKCIF_DVP_SET_SIZE] = 0x28,
|
||||
+ [RKCIF_DVP_SCL_CTRL] = 0x48,
|
||||
+ [RKCIF_DVP_FRAME_STATUS] = 0x60,
|
||||
+ [RKCIF_DVP_LAST_LINE] = 0x68,
|
||||
+ [RKCIF_DVP_LAST_PIX] = 0x6c,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static inline unsigned int rkcif_dvp_get_addr(struct rkcif_device *rkcif,
|
||||
+ unsigned int index)
|
||||
+{
|
||||
+ if (WARN_ON_ONCE(index >= RKCIF_DVP_REGISTER_MAX))
|
||||
+ return RKCIF_REGISTER_NOTSUPPORTED;
|
||||
+
|
||||
+ return rkcif->match_data->dvp->regs[index];
|
||||
+}
|
||||
+
|
||||
+static inline __maybe_unused void rkcif_dvp_write(struct rkcif_device *rkcif,
|
||||
+ unsigned int index, u32 val)
|
||||
+{
|
||||
+ unsigned int addr = rkcif_dvp_get_addr(rkcif, index);
|
||||
+
|
||||
+ if (addr == RKCIF_REGISTER_NOTSUPPORTED)
|
||||
+ return;
|
||||
+
|
||||
+ writel(val, rkcif->base_addr + addr);
|
||||
+}
|
||||
+
|
||||
+static inline __maybe_unused u32 rkcif_dvp_read(struct rkcif_device *rkcif,
|
||||
+ unsigned int index)
|
||||
+{
|
||||
+ unsigned int addr = rkcif_dvp_get_addr(rkcif, index);
|
||||
+
|
||||
+ if (addr == RKCIF_REGISTER_NOTSUPPORTED)
|
||||
+ return 0;
|
||||
+
|
||||
+ return readl(rkcif->base_addr + addr);
|
||||
+}
|
||||
+
|
||||
+static void rkcif_dvp_queue_buffer(struct rkcif_stream *stream,
|
||||
+ unsigned int index)
|
||||
+{
|
||||
+ struct rkcif_device *rkcif = stream->rkcif;
|
||||
+ struct rkcif_buffer *buffer = stream->buffers[index];
|
||||
+ u32 frm_addr_y, frm_addr_uv;
|
||||
+
|
||||
+ frm_addr_y = index ? RKCIF_DVP_FRM1_ADDR_Y : RKCIF_DVP_FRM0_ADDR_Y;
|
||||
+ frm_addr_uv = index ? RKCIF_DVP_FRM1_ADDR_UV : RKCIF_DVP_FRM0_ADDR_UV;
|
||||
+
|
||||
+ rkcif_dvp_write(rkcif, frm_addr_y, buffer->buff_addr[RKCIF_PLANE_Y]);
|
||||
+ rkcif_dvp_write(rkcif, frm_addr_uv, buffer->buff_addr[RKCIF_PLANE_UV]);
|
||||
+}
|
||||
+
|
||||
+static int rkcif_dvp_start_streaming(struct rkcif_stream *stream)
|
||||
+{
|
||||
+ struct rkcif_device *rkcif = stream->rkcif;
|
||||
+ struct rkcif_interface *interface = stream->interface;
|
||||
+ struct v4l2_mbus_config_parallel *parallel;
|
||||
+ struct v4l2_mbus_framefmt *source_fmt;
|
||||
+ struct v4l2_subdev_state *state;
|
||||
+ const struct rkcif_input_fmt *active_in_fmt;
|
||||
+ const struct rkcif_output_fmt *active_out_fmt;
|
||||
+ u32 val = 0;
|
||||
+ int ret = -EINVAL;
|
||||
+
|
||||
+ state = v4l2_subdev_lock_and_get_active_state(&interface->sd);
|
||||
+ source_fmt = v4l2_subdev_state_get_format(state, RKCIF_IF_PAD_SRC,
|
||||
+ stream->id);
|
||||
+ if (!source_fmt)
|
||||
+ goto out;
|
||||
+
|
||||
+ active_in_fmt = rkcif_interface_find_input_fmt(interface, false,
|
||||
+ source_fmt->code);
|
||||
+ active_out_fmt = rkcif_stream_find_output_fmt(stream, false,
|
||||
+ stream->pix.pixelformat);
|
||||
+ if (!active_in_fmt || !active_out_fmt)
|
||||
+ goto out;
|
||||
+
|
||||
+ parallel = &interface->vep.bus.parallel;
|
||||
+ if (parallel->bus_width == 16 &&
|
||||
+ (parallel->flags & V4L2_MBUS_PCLK_SAMPLE_DUALEDGE))
|
||||
+ val |= RKCIF_FORMAT_BT1120_CLOCK_DOUBLE_EDGES;
|
||||
+ val |= active_in_fmt->dvp_fmt_val;
|
||||
+ val |= active_out_fmt->dvp_fmt_val;
|
||||
+ rkcif_dvp_write(rkcif, RKCIF_DVP_FOR, val);
|
||||
+
|
||||
+ val = stream->pix.width;
|
||||
+ if (active_in_fmt->fmt_type == RKCIF_FMT_TYPE_RAW)
|
||||
+ val = stream->pix.width * 2;
|
||||
+ rkcif_dvp_write(rkcif, RKCIF_DVP_VIR_LINE_WIDTH, val);
|
||||
+
|
||||
+ val = RKCIF_XY_COORD(stream->pix.width, stream->pix.height);
|
||||
+ rkcif_dvp_write(rkcif, RKCIF_DVP_SET_SIZE, val);
|
||||
+
|
||||
+ rkcif_dvp_write(rkcif, RKCIF_DVP_FRAME_STATUS, RKCIF_FRAME_STAT_CLS);
|
||||
+ rkcif_dvp_write(rkcif, RKCIF_DVP_INTSTAT, RKCIF_INTSTAT_CLS);
|
||||
+ if (rkcif->match_data->dvp->has_scaler) {
|
||||
+ val = active_in_fmt->fmt_type == RKCIF_FMT_TYPE_YUV ?
|
||||
+ RKCIF_SCL_CTRL_ENABLE_YUV_16BIT_BYPASS :
|
||||
+ RKCIF_SCL_CTRL_ENABLE_RAW_16BIT_BYPASS;
|
||||
+ rkcif_dvp_write(rkcif, RKCIF_DVP_SCL_CTRL, val);
|
||||
+ }
|
||||
+
|
||||
+ rkcif_dvp_write(rkcif, RKCIF_DVP_INTEN,
|
||||
+ RKCIF_INTEN_FRAME_END_EN |
|
||||
+ RKCIF_INTEN_PST_INF_FRAME_END_EN);
|
||||
+
|
||||
+ rkcif_dvp_write(rkcif, RKCIF_DVP_CTRL,
|
||||
+ RKCIF_CTRL_AXI_BURST_16 | RKCIF_CTRL_MODE_PINGPONG |
|
||||
+ RKCIF_CTRL_ENABLE_CAPTURE);
|
||||
+
|
||||
+ ret = 0;
|
||||
+
|
||||
+out:
|
||||
+ v4l2_subdev_unlock_state(state);
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static void rkcif_dvp_stop_streaming(struct rkcif_stream *stream)
|
||||
+{
|
||||
+ struct rkcif_device *rkcif = stream->rkcif;
|
||||
+ u32 val;
|
||||
+
|
||||
+ val = rkcif_dvp_read(rkcif, RKCIF_DVP_CTRL);
|
||||
+ rkcif_dvp_write(rkcif, RKCIF_DVP_CTRL,
|
||||
+ val & (~RKCIF_CTRL_ENABLE_CAPTURE));
|
||||
+ rkcif_dvp_write(rkcif, RKCIF_DVP_INTEN, 0x0);
|
||||
+ rkcif_dvp_write(rkcif, RKCIF_DVP_INTSTAT, 0x3ff);
|
||||
+ rkcif_dvp_write(rkcif, RKCIF_DVP_FRAME_STATUS, 0x0);
|
||||
+
|
||||
+ stream->stopping = false;
|
||||
+}
|
||||
+
|
||||
+static void rkcif_dvp_reset_stream(struct rkcif_device *rkcif)
|
||||
+{
|
||||
+ u32 ctl = rkcif_dvp_read(rkcif, RKCIF_DVP_CTRL);
|
||||
+
|
||||
+ rkcif_dvp_write(rkcif, RKCIF_DVP_CTRL,
|
||||
+ ctl & (~RKCIF_CTRL_ENABLE_CAPTURE));
|
||||
+ rkcif_dvp_write(rkcif, RKCIF_DVP_CTRL, ctl | RKCIF_CTRL_ENABLE_CAPTURE);
|
||||
+}
|
||||
+
|
||||
+static void rkcif_dvp_set_crop(struct rkcif_stream *stream, u16 left, u16 top)
|
||||
+{
|
||||
+ struct rkcif_device *rkcif = stream->rkcif;
|
||||
+ u32 val;
|
||||
+
|
||||
+ val = RKCIF_XY_COORD(left, top);
|
||||
+ rkcif_dvp_write(rkcif, RKCIF_DVP_CROP, val);
|
||||
+}
|
||||
+
|
||||
+irqreturn_t rkcif_dvp_isr(int irq, void *ctx)
|
||||
+{
|
||||
+ struct device *dev = ctx;
|
||||
+ struct rkcif_device *rkcif = dev_get_drvdata(dev);
|
||||
+ struct rkcif_stream *stream;
|
||||
+ u32 intstat, lastline, lastpix, cif_frmst;
|
||||
+ irqreturn_t ret = IRQ_NONE;
|
||||
+
|
||||
+ if (!rkcif->match_data->dvp)
|
||||
+ return ret;
|
||||
+
|
||||
+ intstat = rkcif_dvp_read(rkcif, RKCIF_DVP_INTSTAT);
|
||||
+ cif_frmst = rkcif_dvp_read(rkcif, RKCIF_DVP_FRAME_STATUS);
|
||||
+ lastline = RKCIF_FETCH_Y(rkcif_dvp_read(rkcif, RKCIF_DVP_LAST_LINE));
|
||||
+ lastpix = RKCIF_FETCH_Y(rkcif_dvp_read(rkcif, RKCIF_DVP_LAST_PIX));
|
||||
+
|
||||
+ if (intstat & RKCIF_INTSTAT_FRAME_END) {
|
||||
+ rkcif_dvp_write(rkcif, RKCIF_DVP_INTSTAT,
|
||||
+ RKCIF_INTSTAT_FRAME_END_CLR |
|
||||
+ RKCIF_INTSTAT_LINE_END_CLR);
|
||||
+
|
||||
+ stream = &rkcif->interfaces[RKCIF_DVP].streams[RKCIF_ID0];
|
||||
+
|
||||
+ if (stream->stopping) {
|
||||
+ rkcif_dvp_stop_streaming(stream);
|
||||
+ wake_up(&stream->wq_stopped);
|
||||
+ ret = IRQ_HANDLED;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ if (lastline != stream->pix.height) {
|
||||
+ v4l2_err(&rkcif->v4l2_dev,
|
||||
+ "bad frame, irq:%#x frmst:%#x size:%dx%d\n",
|
||||
+ intstat, cif_frmst, lastpix, lastline);
|
||||
+
|
||||
+ rkcif_dvp_reset_stream(rkcif);
|
||||
+ }
|
||||
+
|
||||
+ rkcif_stream_pingpong(stream);
|
||||
+
|
||||
+ ret = IRQ_HANDLED;
|
||||
+ }
|
||||
+out:
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+int rkcif_dvp_register(struct rkcif_device *rkcif)
|
||||
+{
|
||||
+ struct rkcif_interface *interface;
|
||||
+ unsigned int streams_num;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (!rkcif->match_data->dvp)
|
||||
+ return 0;
|
||||
+
|
||||
+ interface = &rkcif->interfaces[RKCIF_DVP];
|
||||
+ interface->index = RKCIF_DVP;
|
||||
+ interface->type = RKCIF_IF_DVP;
|
||||
+ interface->in_fmts = rkcif->match_data->dvp->in_fmts;
|
||||
+ interface->in_fmts_num = rkcif->match_data->dvp->in_fmts_num;
|
||||
+ interface->set_crop = rkcif_dvp_set_crop;
|
||||
+ ret = rkcif_interface_register(rkcif, interface);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ if (rkcif->match_data->dvp->setup)
|
||||
+ rkcif->match_data->dvp->setup(rkcif);
|
||||
+
|
||||
+ streams_num = rkcif->match_data->dvp->has_ids ? 4 : 1;
|
||||
+ for (unsigned int i = 0; i < streams_num; i++) {
|
||||
+ struct rkcif_stream *stream = &interface->streams[i];
|
||||
+
|
||||
+ stream->id = i;
|
||||
+ stream->interface = interface;
|
||||
+ stream->out_fmts = rkcif->match_data->dvp->out_fmts;
|
||||
+ stream->out_fmts_num = rkcif->match_data->dvp->out_fmts_num;
|
||||
+ stream->queue_buffer = rkcif_dvp_queue_buffer;
|
||||
+ stream->start_streaming = rkcif_dvp_start_streaming;
|
||||
+ stream->stop_streaming = rkcif_dvp_stop_streaming;
|
||||
+
|
||||
+ ret = rkcif_stream_register(rkcif, stream);
|
||||
+ if (ret)
|
||||
+ goto err_streams_unregister;
|
||||
+
|
||||
+ interface->streams_num++;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+err_streams_unregister:
|
||||
+ for (unsigned int i = 0; i < interface->streams_num; i++)
|
||||
+ rkcif_stream_unregister(&interface->streams[i]);
|
||||
+
|
||||
+ rkcif_interface_unregister(interface);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+void rkcif_dvp_unregister(struct rkcif_device *rkcif)
|
||||
+{
|
||||
+ struct rkcif_interface *interface;
|
||||
+
|
||||
+ if (!rkcif->match_data->dvp)
|
||||
+ return;
|
||||
+
|
||||
+ interface = &rkcif->interfaces[RKCIF_DVP];
|
||||
+
|
||||
+ for (unsigned int i = 0; i < interface->streams_num; i++)
|
||||
+ rkcif_stream_unregister(&interface->streams[i]);
|
||||
+
|
||||
+ rkcif_interface_unregister(interface);
|
||||
+}
|
||||
--- /dev/null
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-capture-dvp.h
|
||||
@@ -0,0 +1,24 @@
|
||||
+/* SPDX-License-Identifier: GPL-2.0 */
|
||||
+/*
|
||||
+ * Rockchip Camera Interface (CIF) Driver
|
||||
+ *
|
||||
+ * Copyright (C) 2018 Rockchip Electronics Co., Ltd.
|
||||
+ * Copyright (C) 2023 Mehdi Djait <mehdi.djait@bootlin.com>
|
||||
+ * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net>
|
||||
+ * Copyright (C) 2025 Collabora, Ltd.
|
||||
+ */
|
||||
+
|
||||
+#ifndef _RKCIF_CAPTURE_DVP_H
|
||||
+#define _RKCIF_CAPTURE_DVP_H
|
||||
+
|
||||
+#include "rkcif-common.h"
|
||||
+
|
||||
+extern const struct rkcif_dvp_match_data rkcif_px30_vip_dvp_match_data;
|
||||
+
|
||||
+int rkcif_dvp_register(struct rkcif_device *rkcif);
|
||||
+
|
||||
+void rkcif_dvp_unregister(struct rkcif_device *rkcif);
|
||||
+
|
||||
+irqreturn_t rkcif_dvp_isr(int irq, void *ctx);
|
||||
+
|
||||
+#endif
|
||||
--- a/drivers/media/platform/rockchip/rkcif/rkcif-common.h
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-common.h
|
||||
@@ -24,6 +24,8 @@
|
||||
#include <media/v4l2-subdev.h>
|
||||
#include <media/videobuf2-v4l2.h>
|
||||
|
||||
+#include "rkcif-regs.h"
|
||||
+
|
||||
#define RKCIF_DRIVER_NAME "rockchip-cif"
|
||||
#define RKCIF_CLK_MAX 4
|
||||
|
||||
@@ -84,17 +86,31 @@ struct rkcif_dummy_buffer {
|
||||
u32 size;
|
||||
};
|
||||
|
||||
+enum rkcif_plane_index {
|
||||
+ RKCIF_PLANE_Y,
|
||||
+ RKCIF_PLANE_UV,
|
||||
+ RKCIF_PLANE_MAX
|
||||
+};
|
||||
+
|
||||
struct rkcif_input_fmt {
|
||||
u32 mbus_code;
|
||||
|
||||
enum rkcif_format_type fmt_type;
|
||||
enum v4l2_field field;
|
||||
+
|
||||
+ union {
|
||||
+ u32 dvp_fmt_val;
|
||||
+ };
|
||||
};
|
||||
|
||||
struct rkcif_output_fmt {
|
||||
u32 fourcc;
|
||||
u32 mbus_code;
|
||||
u8 cplanes;
|
||||
+
|
||||
+ union {
|
||||
+ u32 dvp_fmt_val;
|
||||
+ };
|
||||
};
|
||||
|
||||
struct rkcif_interface;
|
||||
@@ -168,9 +184,21 @@ struct rkcif_interface {
|
||||
void (*set_crop)(struct rkcif_stream *stream, u16 left, u16 top);
|
||||
};
|
||||
|
||||
+struct rkcif_dvp_match_data {
|
||||
+ const struct rkcif_input_fmt *in_fmts;
|
||||
+ unsigned int in_fmts_num;
|
||||
+ const struct rkcif_output_fmt *out_fmts;
|
||||
+ unsigned int out_fmts_num;
|
||||
+ void (*setup)(struct rkcif_device *rkcif);
|
||||
+ bool has_scaler;
|
||||
+ bool has_ids;
|
||||
+ unsigned int regs[RKCIF_DVP_REGISTER_MAX];
|
||||
+};
|
||||
+
|
||||
struct rkcif_match_data {
|
||||
const char *const *clks;
|
||||
unsigned int clks_num;
|
||||
+ const struct rkcif_dvp_match_data *dvp;
|
||||
};
|
||||
|
||||
struct rkcif_device {
|
||||
--- a/drivers/media/platform/rockchip/rkcif/rkcif-dev.c
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-dev.c
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <media/v4l2-fwnode.h>
|
||||
#include <media/v4l2-mc.h>
|
||||
|
||||
+#include "rkcif-capture-dvp.h"
|
||||
#include "rkcif-common.h"
|
||||
|
||||
static const char *const px30_vip_clks[] = {
|
||||
@@ -34,6 +35,7 @@ static const char *const px30_vip_clks[]
|
||||
static const struct rkcif_match_data px30_vip_match_data = {
|
||||
.clks = px30_vip_clks,
|
||||
.clks_num = ARRAY_SIZE(px30_vip_clks),
|
||||
+ .dvp = &rkcif_px30_vip_dvp_match_data,
|
||||
};
|
||||
|
||||
static const char *const rk3568_vicap_clks[] = {
|
||||
@@ -63,11 +65,21 @@ MODULE_DEVICE_TABLE(of, rkcif_plat_of_ma
|
||||
|
||||
static int rkcif_register(struct rkcif_device *rkcif)
|
||||
{
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = rkcif_dvp_register(rkcif);
|
||||
+ if (ret && ret != -ENODEV)
|
||||
+ goto err;
|
||||
+
|
||||
return 0;
|
||||
+
|
||||
+err:
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
static void rkcif_unregister(struct rkcif_device *rkcif)
|
||||
{
|
||||
+ rkcif_dvp_unregister(rkcif);
|
||||
}
|
||||
|
||||
static int rkcif_notifier_bound(struct v4l2_async_notifier *notifier,
|
||||
@@ -112,6 +124,9 @@ static irqreturn_t rkcif_isr(int irq, vo
|
||||
{
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
|
||||
+ if (rkcif_dvp_isr(irq, ctx) == IRQ_HANDLED)
|
||||
+ ret = IRQ_HANDLED;
|
||||
+
|
||||
return ret;
|
||||
}
|
||||
|
||||
--- /dev/null
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-regs.h
|
||||
@@ -0,0 +1,131 @@
|
||||
+/* SPDX-License-Identifier: GPL-2.0 */
|
||||
+/*
|
||||
+ * Rockchip Camera Interface (CIF) Driver
|
||||
+ *
|
||||
+ * Copyright (C) 2018 Rockchip Electronics Co., Ltd.
|
||||
+ * Copyright (C) 2023 Mehdi Djait <mehdi.djait@bootlin.com>
|
||||
+ * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net>
|
||||
+ */
|
||||
+
|
||||
+#ifndef _RKCIF_REGS_H
|
||||
+#define _RKCIF_REGS_H
|
||||
+
|
||||
+#define RKCIF_REGISTER_NOTSUPPORTED 0x420000
|
||||
+#define RKCIF_FETCH_Y(VAL) ((VAL) & 0x1fff)
|
||||
+#define RKCIF_XY_COORD(x, y) (((y) << 16) | (x))
|
||||
+
|
||||
+/* DVP register contents */
|
||||
+#define RKCIF_CTRL_ENABLE_CAPTURE BIT(0)
|
||||
+#define RKCIF_CTRL_MODE_PINGPONG BIT(1)
|
||||
+#define RKCIF_CTRL_MODE_LINELOOP BIT(2)
|
||||
+#define RKCIF_CTRL_AXI_BURST_16 (0xf << 12)
|
||||
+
|
||||
+#define RKCIF_INTEN_FRAME_END_EN BIT(0)
|
||||
+#define RKCIF_INTEN_LINE_ERR_EN BIT(2)
|
||||
+#define RKCIF_INTEN_BUS_ERR_EN BIT(6)
|
||||
+#define RKCIF_INTEN_SCL_ERR_EN BIT(7)
|
||||
+#define RKCIF_INTEN_PST_INF_FRAME_END_EN BIT(9)
|
||||
+
|
||||
+#define RKCIF_INTSTAT_CLS 0x3ff
|
||||
+#define RKCIF_INTSTAT_FRAME_END BIT(0)
|
||||
+#define RKCIF_INTSTAT_LINE_END BIT(1)
|
||||
+#define RKCIF_INTSTAT_LINE_ERR BIT(2)
|
||||
+#define RKCIF_INTSTAT_PIX_ERR BIT(3)
|
||||
+#define RKCIF_INTSTAT_DFIFO_OF BIT(5)
|
||||
+#define RKCIF_INTSTAT_BUS_ERR BIT(6)
|
||||
+#define RKCIF_INTSTAT_PRE_INF_FRAME_END BIT(8)
|
||||
+#define RKCIF_INTSTAT_PST_INF_FRAME_END BIT(9)
|
||||
+#define RKCIF_INTSTAT_FRAME_END_CLR BIT(0)
|
||||
+#define RKCIF_INTSTAT_LINE_END_CLR BIT(1)
|
||||
+#define RKCIF_INTSTAT_LINE_ERR_CLR BIT(2)
|
||||
+#define RKCIF_INTSTAT_PST_INF_FRAME_END_CLR BIT(9)
|
||||
+#define RKCIF_INTSTAT_ERR 0xfc
|
||||
+
|
||||
+#define RKCIF_FRAME_STAT_CLS 0x00
|
||||
+#define RKCIF_FRAME_FRM0_STAT_CLS 0x20
|
||||
+
|
||||
+#define RKCIF_FORMAT_VSY_HIGH_ACTIVE BIT(0)
|
||||
+#define RKCIF_FORMAT_HSY_LOW_ACTIVE BIT(1)
|
||||
+
|
||||
+#define RKCIF_FORMAT_INPUT_MODE_YUV (0x00 << 2)
|
||||
+#define RKCIF_FORMAT_INPUT_MODE_PAL (0x02 << 2)
|
||||
+#define RKCIF_FORMAT_INPUT_MODE_NTSC (0x03 << 2)
|
||||
+#define RKCIF_FORMAT_INPUT_MODE_BT1120 (0x07 << 2)
|
||||
+#define RKCIF_FORMAT_INPUT_MODE_RAW (0x04 << 2)
|
||||
+#define RKCIF_FORMAT_INPUT_MODE_JPEG (0x05 << 2)
|
||||
+#define RKCIF_FORMAT_INPUT_MODE_MIPI (0x06 << 2)
|
||||
+
|
||||
+#define RKCIF_FORMAT_YUV_INPUT_ORDER_UYVY (0x00 << 5)
|
||||
+#define RKCIF_FORMAT_YUV_INPUT_ORDER_YVYU (0x01 << 5)
|
||||
+#define RKCIF_FORMAT_YUV_INPUT_ORDER_VYUY (0x02 << 5)
|
||||
+#define RKCIF_FORMAT_YUV_INPUT_ORDER_YUYV (0x03 << 5)
|
||||
+#define RKCIF_FORMAT_YUV_INPUT_422 (0x00 << 7)
|
||||
+#define RKCIF_FORMAT_YUV_INPUT_420 BIT(7)
|
||||
+
|
||||
+#define RKCIF_FORMAT_INPUT_420_ORDER_ODD BIT(8)
|
||||
+
|
||||
+#define RKCIF_FORMAT_CCIR_INPUT_ORDER_EVEN BIT(9)
|
||||
+
|
||||
+#define RKCIF_FORMAT_RAW_DATA_WIDTH_8 (0x00 << 11)
|
||||
+#define RKCIF_FORMAT_RAW_DATA_WIDTH_10 (0x01 << 11)
|
||||
+#define RKCIF_FORMAT_RAW_DATA_WIDTH_12 (0x02 << 11)
|
||||
+
|
||||
+#define RKCIF_FORMAT_YUV_OUTPUT_422 (0x00 << 16)
|
||||
+#define RKCIF_FORMAT_YUV_OUTPUT_420 BIT(16)
|
||||
+
|
||||
+#define RKCIF_FORMAT_OUTPUT_420_ORDER_EVEN (0x00 << 17)
|
||||
+#define RKCIF_FORMAT_OUTPUT_420_ORDER_ODD BIT(17)
|
||||
+
|
||||
+#define RKCIF_FORMAT_RAWD_DATA_LITTLE_ENDIAN (0x00 << 18)
|
||||
+#define RKCIF_FORMAT_RAWD_DATA_BIG_ENDIAN BIT(18)
|
||||
+
|
||||
+#define RKCIF_FORMAT_UV_STORAGE_ORDER_UVUV (0x00 << 19)
|
||||
+#define RKCIF_FORMAT_UV_STORAGE_ORDER_VUVU BIT(19)
|
||||
+
|
||||
+#define RKCIF_FORMAT_BT1120_CLOCK_SINGLE_EDGES (0x00 << 24)
|
||||
+#define RKCIF_FORMAT_BT1120_CLOCK_DOUBLE_EDGES BIT(24)
|
||||
+#define RKCIF_FORMAT_BT1120_TRANSMIT_INTERFACE (0x00 << 25)
|
||||
+#define RKCIF_FORMAT_BT1120_TRANSMIT_PROGRESS BIT(25)
|
||||
+#define RKCIF_FORMAT_BT1120_YC_SWAP BIT(26)
|
||||
+
|
||||
+#define RKCIF_SCL_CTRL_ENABLE_SCL_DOWN BIT(0)
|
||||
+#define RKCIF_SCL_CTRL_ENABLE_SCL_UP BIT(1)
|
||||
+#define RKCIF_SCL_CTRL_ENABLE_YUV_16BIT_BYPASS BIT(4)
|
||||
+#define RKCIF_SCL_CTRL_ENABLE_RAW_16BIT_BYPASS BIT(5)
|
||||
+#define RKCIF_SCL_CTRL_ENABLE_32BIT_BYPASS BIT(6)
|
||||
+#define RKCIF_SCL_CTRL_DISABLE_32BIT_BYPASS (0x00 << 6)
|
||||
+
|
||||
+#define RKCIF_INTSTAT_F0_READY BIT(0)
|
||||
+#define RKCIF_INTSTAT_F1_READY BIT(1)
|
||||
+
|
||||
+/* GRF register offsets and contents */
|
||||
+#define RK3568_GRF_VI_CON0 0x340
|
||||
+#define RK3568_GRF_VI_CON1 0x344
|
||||
+#define RK3568_GRF_VI_STATUS0 0x348
|
||||
+
|
||||
+#define RK3568_GRF_VI_CON1_CIF_DATAPATH BIT(9)
|
||||
+#define RK3568_GRF_VI_CON1_CIF_CLK_DELAYNUM GENMASK(6, 0)
|
||||
+
|
||||
+#define RK3568_GRF_WRITE_ENABLE(x) ((x) << 16)
|
||||
+
|
||||
+enum rkcif_dvp_register_index {
|
||||
+ RKCIF_DVP_CTRL,
|
||||
+ RKCIF_DVP_INTEN,
|
||||
+ RKCIF_DVP_INTSTAT,
|
||||
+ RKCIF_DVP_FOR,
|
||||
+ RKCIF_DVP_LINE_NUM_ADDR,
|
||||
+ RKCIF_DVP_FRM0_ADDR_Y,
|
||||
+ RKCIF_DVP_FRM0_ADDR_UV,
|
||||
+ RKCIF_DVP_FRM1_ADDR_Y,
|
||||
+ RKCIF_DVP_FRM1_ADDR_UV,
|
||||
+ RKCIF_DVP_VIR_LINE_WIDTH,
|
||||
+ RKCIF_DVP_SET_SIZE,
|
||||
+ RKCIF_DVP_SCL_CTRL,
|
||||
+ RKCIF_DVP_CROP,
|
||||
+ RKCIF_DVP_FRAME_STATUS,
|
||||
+ RKCIF_DVP_LAST_LINE,
|
||||
+ RKCIF_DVP_LAST_PIX,
|
||||
+ RKCIF_DVP_REGISTER_MAX
|
||||
+};
|
||||
+
|
||||
+#endif
|
||||
+327
@@ -0,0 +1,327 @@
|
||||
From c348d671697b556d2198c7d2e49e95390dc33e66 Mon Sep 17 00:00:00 2001
|
||||
From: Michael Riesch <michael.riesch@collabora.com>
|
||||
Date: Fri, 14 Nov 2025 16:20:20 +0100
|
||||
Subject: [PATCH] media: rockchip: rkcif: add support for rk3568 vicap dvp
|
||||
capture
|
||||
|
||||
The RK3568 Video Capture (VICAP) unit features a Digital Video Port
|
||||
(DVP). Add support for it.
|
||||
|
||||
Tested-by: Gerald Loacker <gerald.loacker@wolfvision.net>
|
||||
Reviewed-by: Gerald Loacker <gerald.loacker@wolfvision.net>
|
||||
Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
|
||||
Reviewed-by: Mehdi Djait <mehdi.djait@linux.intel.com>
|
||||
Signed-off-by: Michael Riesch <michael.riesch@collabora.com>
|
||||
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
|
||||
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
|
||||
---
|
||||
.../rockchip/rkcif/rkcif-capture-dvp.c | 276 ++++++++++++++++++
|
||||
.../rockchip/rkcif/rkcif-capture-dvp.h | 1 +
|
||||
.../media/platform/rockchip/rkcif/rkcif-dev.c | 1 +
|
||||
3 files changed, 278 insertions(+)
|
||||
|
||||
--- a/drivers/media/platform/rockchip/rkcif/rkcif-capture-dvp.c
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-capture-dvp.c
|
||||
@@ -332,6 +332,282 @@ const struct rkcif_dvp_match_data rkcif_
|
||||
},
|
||||
};
|
||||
|
||||
+static const struct rkcif_input_fmt rk3568_dvp_in_fmts[] = {
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
|
||||
+ RKCIF_FORMAT_YUV_INPUT_ORDER_YUYV,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_YUV,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
|
||||
+ RKCIF_FORMAT_YUV_INPUT_ORDER_YUYV,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_YUV,
|
||||
+ .field = V4L2_FIELD_INTERLACED,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
|
||||
+ RKCIF_FORMAT_YUV_INPUT_ORDER_YVYU,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_YUV,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
|
||||
+ RKCIF_FORMAT_YUV_INPUT_ORDER_YVYU,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_YUV,
|
||||
+ .field = V4L2_FIELD_INTERLACED,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
|
||||
+ RKCIF_FORMAT_YUV_INPUT_ORDER_UYVY,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_YUV,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
|
||||
+ RKCIF_FORMAT_YUV_INPUT_ORDER_UYVY,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_YUV,
|
||||
+ .field = V4L2_FIELD_INTERLACED,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
|
||||
+ RKCIF_FORMAT_YUV_INPUT_ORDER_VYUY,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_YUV,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
|
||||
+ RKCIF_FORMAT_YUV_INPUT_ORDER_VYUY,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_YUV,
|
||||
+ .field = V4L2_FIELD_INTERLACED,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
|
||||
+ RKCIF_FORMAT_YUV_INPUT_ORDER_YUYV |
|
||||
+ RKCIF_FORMAT_INPUT_MODE_BT1120 |
|
||||
+ RKCIF_FORMAT_BT1120_TRANSMIT_PROGRESS,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
|
||||
+ RKCIF_FORMAT_YUV_INPUT_ORDER_YUYV |
|
||||
+ RKCIF_FORMAT_INPUT_MODE_BT1120,
|
||||
+ .field = V4L2_FIELD_INTERLACED,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_YVYU8_1X16,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
|
||||
+ RKCIF_FORMAT_YUV_INPUT_ORDER_YVYU |
|
||||
+ RKCIF_FORMAT_INPUT_MODE_BT1120 |
|
||||
+ RKCIF_FORMAT_BT1120_TRANSMIT_PROGRESS,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_YVYU8_1X16,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
|
||||
+ RKCIF_FORMAT_YUV_INPUT_ORDER_YVYU |
|
||||
+ RKCIF_FORMAT_INPUT_MODE_BT1120,
|
||||
+ .field = V4L2_FIELD_INTERLACED,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
|
||||
+ RKCIF_FORMAT_YUV_INPUT_ORDER_YUYV |
|
||||
+ RKCIF_FORMAT_INPUT_MODE_BT1120 |
|
||||
+ RKCIF_FORMAT_BT1120_YC_SWAP |
|
||||
+ RKCIF_FORMAT_BT1120_TRANSMIT_PROGRESS,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
|
||||
+ RKCIF_FORMAT_YUV_INPUT_ORDER_YUYV |
|
||||
+ RKCIF_FORMAT_BT1120_YC_SWAP |
|
||||
+ RKCIF_FORMAT_INPUT_MODE_BT1120,
|
||||
+ .field = V4L2_FIELD_INTERLACED,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_VYUY8_1X16,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
|
||||
+ RKCIF_FORMAT_YUV_INPUT_ORDER_YVYU |
|
||||
+ RKCIF_FORMAT_INPUT_MODE_BT1120 |
|
||||
+ RKCIF_FORMAT_BT1120_YC_SWAP |
|
||||
+ RKCIF_FORMAT_BT1120_TRANSMIT_PROGRESS,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_VYUY8_1X16,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
|
||||
+ RKCIF_FORMAT_YUV_INPUT_ORDER_YVYU |
|
||||
+ RKCIF_FORMAT_BT1120_YC_SWAP |
|
||||
+ RKCIF_FORMAT_INPUT_MODE_BT1120,
|
||||
+ .field = V4L2_FIELD_INTERLACED,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
|
||||
+ RKCIF_FORMAT_RAW_DATA_WIDTH_8,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
|
||||
+ RKCIF_FORMAT_RAW_DATA_WIDTH_8,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
|
||||
+ RKCIF_FORMAT_RAW_DATA_WIDTH_8,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
|
||||
+ RKCIF_FORMAT_RAW_DATA_WIDTH_8,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
|
||||
+ RKCIF_FORMAT_RAW_DATA_WIDTH_10,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
|
||||
+ RKCIF_FORMAT_RAW_DATA_WIDTH_10,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
|
||||
+ RKCIF_FORMAT_RAW_DATA_WIDTH_10,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
|
||||
+ RKCIF_FORMAT_RAW_DATA_WIDTH_10,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
|
||||
+ RKCIF_FORMAT_RAW_DATA_WIDTH_12,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
|
||||
+ RKCIF_FORMAT_RAW_DATA_WIDTH_12,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
|
||||
+ RKCIF_FORMAT_RAW_DATA_WIDTH_12,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
|
||||
+ RKCIF_FORMAT_RAW_DATA_WIDTH_12,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_RGB888_1X24,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_Y8_1X8,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
|
||||
+ RKCIF_FORMAT_RAW_DATA_WIDTH_8,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_Y10_1X10,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
|
||||
+ RKCIF_FORMAT_RAW_DATA_WIDTH_10,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_Y12_1X12,
|
||||
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
|
||||
+ RKCIF_FORMAT_RAW_DATA_WIDTH_12,
|
||||
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static void rk3568_dvp_grf_setup(struct rkcif_device *rkcif)
|
||||
+{
|
||||
+ u32 con1 = RK3568_GRF_WRITE_ENABLE(RK3568_GRF_VI_CON1_CIF_DATAPATH |
|
||||
+ RK3568_GRF_VI_CON1_CIF_CLK_DELAYNUM);
|
||||
+
|
||||
+ if (!rkcif->grf)
|
||||
+ return;
|
||||
+
|
||||
+ con1 |= rkcif->interfaces[RKCIF_DVP].dvp.dvp_clk_delay &
|
||||
+ RK3568_GRF_VI_CON1_CIF_CLK_DELAYNUM;
|
||||
+
|
||||
+ if (rkcif->interfaces[RKCIF_DVP].vep.bus.parallel.flags &
|
||||
+ V4L2_MBUS_PCLK_SAMPLE_DUALEDGE)
|
||||
+ con1 |= RK3568_GRF_VI_CON1_CIF_DATAPATH;
|
||||
+
|
||||
+ regmap_write(rkcif->grf, RK3568_GRF_VI_CON1, con1);
|
||||
+}
|
||||
+
|
||||
+const struct rkcif_dvp_match_data rkcif_rk3568_vicap_dvp_match_data = {
|
||||
+ .in_fmts = rk3568_dvp_in_fmts,
|
||||
+ .in_fmts_num = ARRAY_SIZE(rk3568_dvp_in_fmts),
|
||||
+ .out_fmts = dvp_out_fmts,
|
||||
+ .out_fmts_num = ARRAY_SIZE(dvp_out_fmts),
|
||||
+ .setup = rk3568_dvp_grf_setup,
|
||||
+ .has_scaler = false,
|
||||
+ .regs = {
|
||||
+ [RKCIF_DVP_CTRL] = 0x00,
|
||||
+ [RKCIF_DVP_INTEN] = 0x04,
|
||||
+ [RKCIF_DVP_INTSTAT] = 0x08,
|
||||
+ [RKCIF_DVP_FOR] = 0x0c,
|
||||
+ [RKCIF_DVP_LINE_NUM_ADDR] = 0x2c,
|
||||
+ [RKCIF_DVP_FRM0_ADDR_Y] = 0x14,
|
||||
+ [RKCIF_DVP_FRM0_ADDR_UV] = 0x18,
|
||||
+ [RKCIF_DVP_FRM1_ADDR_Y] = 0x1c,
|
||||
+ [RKCIF_DVP_FRM1_ADDR_UV] = 0x20,
|
||||
+ [RKCIF_DVP_VIR_LINE_WIDTH] = 0x24,
|
||||
+ [RKCIF_DVP_SET_SIZE] = 0x28,
|
||||
+ [RKCIF_DVP_CROP] = 0x34,
|
||||
+ [RKCIF_DVP_FRAME_STATUS] = 0x3c,
|
||||
+ [RKCIF_DVP_LAST_LINE] = 0x44,
|
||||
+ [RKCIF_DVP_LAST_PIX] = 0x48,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
static inline unsigned int rkcif_dvp_get_addr(struct rkcif_device *rkcif,
|
||||
unsigned int index)
|
||||
{
|
||||
--- a/drivers/media/platform/rockchip/rkcif/rkcif-capture-dvp.h
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-capture-dvp.h
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "rkcif-common.h"
|
||||
|
||||
extern const struct rkcif_dvp_match_data rkcif_px30_vip_dvp_match_data;
|
||||
+extern const struct rkcif_dvp_match_data rkcif_rk3568_vicap_dvp_match_data;
|
||||
|
||||
int rkcif_dvp_register(struct rkcif_device *rkcif);
|
||||
|
||||
--- a/drivers/media/platform/rockchip/rkcif/rkcif-dev.c
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-dev.c
|
||||
@@ -48,6 +48,7 @@ static const char *const rk3568_vicap_cl
|
||||
static const struct rkcif_match_data rk3568_vicap_match_data = {
|
||||
.clks = rk3568_vicap_clks,
|
||||
.clks_num = ARRAY_SIZE(rk3568_vicap_clks),
|
||||
+ .dvp = &rkcif_rk3568_vicap_dvp_match_data,
|
||||
};
|
||||
|
||||
static const struct of_device_id rkcif_plat_of_match[] = {
|
||||
+984
@@ -0,0 +1,984 @@
|
||||
From 1f2353f5a1af995efbf7bea44341aa0d03460b28 Mon Sep 17 00:00:00 2001
|
||||
From: Michael Riesch <michael.riesch@collabora.com>
|
||||
Date: Fri, 14 Nov 2025 16:20:21 +0100
|
||||
Subject: [PATCH] media: rockchip: rkcif: add support for rk3568 vicap mipi
|
||||
capture
|
||||
|
||||
The RK3568 Video Capture (VICAP) unit features a MIPI CSI-2 capture
|
||||
interface. Add support for the MIPI capture interface in general
|
||||
and for the RK3568 VICAP MIPI capture in particular.
|
||||
|
||||
Signed-off-by: Michael Riesch <michael.riesch@wolfvision.net>
|
||||
Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
|
||||
Reviewed-by: Mehdi Djait <mehdi.djait@linux.intel.com>
|
||||
Signed-off-by: Michael Riesch <michael.riesch@collabora.com>
|
||||
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
|
||||
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
|
||||
---
|
||||
.../media/platform/rockchip/rkcif/Makefile | 1 +
|
||||
.../rockchip/rkcif/rkcif-capture-mipi.c | 777 ++++++++++++++++++
|
||||
.../rockchip/rkcif/rkcif-capture-mipi.h | 23 +
|
||||
.../platform/rockchip/rkcif/rkcif-common.h | 29 +
|
||||
.../media/platform/rockchip/rkcif/rkcif-dev.c | 12 +
|
||||
.../platform/rockchip/rkcif/rkcif-regs.h | 22 +
|
||||
6 files changed, 864 insertions(+)
|
||||
create mode 100644 drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c
|
||||
create mode 100644 drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.h
|
||||
|
||||
--- a/drivers/media/platform/rockchip/rkcif/Makefile
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/Makefile
|
||||
@@ -2,6 +2,7 @@
|
||||
obj-$(CONFIG_VIDEO_ROCKCHIP_CIF) += rockchip-cif.o
|
||||
|
||||
rockchip-cif-objs += rkcif-capture-dvp.o
|
||||
+rockchip-cif-objs += rkcif-capture-mipi.o
|
||||
rockchip-cif-objs += rkcif-dev.o
|
||||
rockchip-cif-objs += rkcif-interface.o
|
||||
rockchip-cif-objs += rkcif-stream.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c
|
||||
@@ -0,0 +1,777 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0
|
||||
+/*
|
||||
+ * Rockchip Camera Interface (CIF) Driver
|
||||
+ *
|
||||
+ * Copyright (C) 2018 Rockchip Electronics Co., Ltd.
|
||||
+ * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net>
|
||||
+ * Copyright (C) 2025 Collabora, Ltd.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/interrupt.h>
|
||||
+
|
||||
+#include <media/mipi-csi2.h>
|
||||
+#include <media/v4l2-common.h>
|
||||
+#include <media/v4l2-event.h>
|
||||
+#include <media/v4l2-fh.h>
|
||||
+#include <media/v4l2-fwnode.h>
|
||||
+#include <media/v4l2-ioctl.h>
|
||||
+#include <media/v4l2-mc.h>
|
||||
+#include <media/v4l2-subdev.h>
|
||||
+
|
||||
+#include "rkcif-capture-mipi.h"
|
||||
+#include "rkcif-common.h"
|
||||
+#include "rkcif-interface.h"
|
||||
+#include "rkcif-regs.h"
|
||||
+#include "rkcif-stream.h"
|
||||
+
|
||||
+#define RK3568_MIPI_CTRL0_HIGH_ALIGN BIT(31)
|
||||
+#define RK3568_MIPI_CTRL0_UV_SWAP_EN BIT(7)
|
||||
+#define RK3568_MIPI_CTRL0_COMPACT_EN BIT(6)
|
||||
+#define RK3568_MIPI_CTRL0_CROP_EN BIT(5)
|
||||
+#define RK3568_MIPI_CTRL0_WRDDR(type) ((type) << 1)
|
||||
+
|
||||
+#define RKCIF_MIPI_CTRL0_DT_ID(id) ((id) << 10)
|
||||
+#define RKCIF_MIPI_CTRL0_VC_ID(id) ((id) << 8)
|
||||
+#define RKCIF_MIPI_CTRL0_CAP_EN BIT(0)
|
||||
+
|
||||
+#define RKCIF_MIPI_INT_FRAME0_END(id) BIT(8 + (id) * 2 + 0)
|
||||
+#define RKCIF_MIPI_INT_FRAME1_END(id) BIT(8 + (id) * 2 + 1)
|
||||
+
|
||||
+static const struct rkcif_output_fmt mipi_out_fmts[] = {
|
||||
+ /* YUV formats */
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_YUYV,
|
||||
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16,
|
||||
+ .depth = 16,
|
||||
+ .cplanes = 1,
|
||||
+ .mipi = {
|
||||
+ .dt = MIPI_CSI2_DT_YUV422_8B,
|
||||
+ .type = RKCIF_MIPI_TYPE_RAW8,
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_UYVY,
|
||||
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16,
|
||||
+ .depth = 16,
|
||||
+ .cplanes = 1,
|
||||
+ .mipi = {
|
||||
+ .dt = MIPI_CSI2_DT_YUV422_8B,
|
||||
+ .type = RKCIF_MIPI_TYPE_RAW8,
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_YVYU,
|
||||
+ .mbus_code = MEDIA_BUS_FMT_YVYU8_1X16,
|
||||
+ .depth = 16,
|
||||
+ .cplanes = 1,
|
||||
+ .mipi = {
|
||||
+ .dt = MIPI_CSI2_DT_YUV422_8B,
|
||||
+ .type = RKCIF_MIPI_TYPE_RAW8,
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_VYUY,
|
||||
+ .mbus_code = MEDIA_BUS_FMT_VYUY8_1X16,
|
||||
+ .depth = 16,
|
||||
+ .cplanes = 1,
|
||||
+ .mipi = {
|
||||
+ .dt = MIPI_CSI2_DT_YUV422_8B,
|
||||
+ .type = RKCIF_MIPI_TYPE_RAW8,
|
||||
+ },
|
||||
+ },
|
||||
+ /* RGB formats */
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_RGB24,
|
||||
+ .mbus_code = MEDIA_BUS_FMT_RGB888_1X24,
|
||||
+ .depth = 24,
|
||||
+ .cplanes = 1,
|
||||
+ .mipi = {
|
||||
+ .dt = MIPI_CSI2_DT_RGB888,
|
||||
+ .type = RKCIF_MIPI_TYPE_RGB888,
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_BGR24,
|
||||
+ .mbus_code = MEDIA_BUS_FMT_BGR888_1X24,
|
||||
+ .depth = 24,
|
||||
+ .cplanes = 1,
|
||||
+ .mipi = {
|
||||
+ .dt = MIPI_CSI2_DT_RGB888,
|
||||
+ .type = RKCIF_MIPI_TYPE_RGB888,
|
||||
+ },
|
||||
+ },
|
||||
+ /* Bayer formats */
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
|
||||
+ .depth = 8,
|
||||
+ .cplanes = 1,
|
||||
+ .mipi = {
|
||||
+ .dt = MIPI_CSI2_DT_RAW8,
|
||||
+ .type = RKCIF_MIPI_TYPE_RAW8,
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,
|
||||
+ .depth = 8,
|
||||
+ .cplanes = 1,
|
||||
+ .mipi = {
|
||||
+ .dt = MIPI_CSI2_DT_RAW8,
|
||||
+ .type = RKCIF_MIPI_TYPE_RAW8,
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
|
||||
+ .depth = 8,
|
||||
+ .cplanes = 1,
|
||||
+ .mipi = {
|
||||
+ .dt = MIPI_CSI2_DT_RAW8,
|
||||
+ .type = RKCIF_MIPI_TYPE_RAW8,
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_SRGGB8,
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,
|
||||
+ .depth = 8,
|
||||
+ .cplanes = 1,
|
||||
+ .mipi = {
|
||||
+ .dt = MIPI_CSI2_DT_RAW8,
|
||||
+ .type = RKCIF_MIPI_TYPE_RAW8,
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_SBGGR10,
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
|
||||
+ .depth = 10,
|
||||
+ .cplanes = 1,
|
||||
+ .mipi = {
|
||||
+ .dt = MIPI_CSI2_DT_RAW10,
|
||||
+ .type = RKCIF_MIPI_TYPE_RAW10,
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_SBGGR10P,
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
|
||||
+ .depth = 10,
|
||||
+ .cplanes = 1,
|
||||
+ .mipi = {
|
||||
+ .dt = MIPI_CSI2_DT_RAW10,
|
||||
+ .compact = true,
|
||||
+ .type = RKCIF_MIPI_TYPE_RAW10,
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_SGBRG10,
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
|
||||
+ .depth = 10,
|
||||
+ .cplanes = 1,
|
||||
+ .mipi = {
|
||||
+ .dt = MIPI_CSI2_DT_RAW10,
|
||||
+ .type = RKCIF_MIPI_TYPE_RAW10,
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_SGBRG10P,
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
|
||||
+ .depth = 10,
|
||||
+ .cplanes = 1,
|
||||
+ .mipi = {
|
||||
+ .dt = MIPI_CSI2_DT_RAW10,
|
||||
+ .compact = true,
|
||||
+ .type = RKCIF_MIPI_TYPE_RAW10,
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_SGRBG10,
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
|
||||
+ .depth = 10,
|
||||
+ .cplanes = 1,
|
||||
+ .mipi = {
|
||||
+ .dt = MIPI_CSI2_DT_RAW10,
|
||||
+ .type = RKCIF_MIPI_TYPE_RAW10,
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_SGRBG10P,
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
|
||||
+ .depth = 10,
|
||||
+ .cplanes = 1,
|
||||
+ .mipi = {
|
||||
+ .dt = MIPI_CSI2_DT_RAW10,
|
||||
+ .compact = true,
|
||||
+ .type = RKCIF_MIPI_TYPE_RAW10,
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_SRGGB10,
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
|
||||
+ .depth = 10,
|
||||
+ .cplanes = 1,
|
||||
+ .mipi = {
|
||||
+ .dt = MIPI_CSI2_DT_RAW10,
|
||||
+ .type = RKCIF_MIPI_TYPE_RAW10,
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_SRGGB10P,
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
|
||||
+ .depth = 10,
|
||||
+ .cplanes = 1,
|
||||
+ .mipi = {
|
||||
+ .dt = MIPI_CSI2_DT_RAW10,
|
||||
+ .compact = true,
|
||||
+ .type = RKCIF_MIPI_TYPE_RAW10,
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_SBGGR12,
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,
|
||||
+ .depth = 12,
|
||||
+ .cplanes = 1,
|
||||
+ .mipi = {
|
||||
+ .dt = MIPI_CSI2_DT_RAW12,
|
||||
+ .type = RKCIF_MIPI_TYPE_RAW12,
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_SBGGR12P,
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,
|
||||
+ .depth = 12,
|
||||
+ .cplanes = 1,
|
||||
+ .mipi = {
|
||||
+ .dt = MIPI_CSI2_DT_RAW12,
|
||||
+ .compact = true,
|
||||
+ .type = RKCIF_MIPI_TYPE_RAW12,
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_SGBRG12,
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,
|
||||
+ .depth = 12,
|
||||
+ .cplanes = 1,
|
||||
+ .mipi = {
|
||||
+ .dt = MIPI_CSI2_DT_RAW12,
|
||||
+ .type = RKCIF_MIPI_TYPE_RAW12,
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_SGBRG12P,
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,
|
||||
+ .depth = 12,
|
||||
+ .cplanes = 1,
|
||||
+ .mipi = {
|
||||
+ .dt = MIPI_CSI2_DT_RAW12,
|
||||
+ .compact = true,
|
||||
+ .type = RKCIF_MIPI_TYPE_RAW12,
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_SGRBG12,
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
|
||||
+ .depth = 12,
|
||||
+ .cplanes = 1,
|
||||
+ .mipi = {
|
||||
+ .dt = MIPI_CSI2_DT_RAW12,
|
||||
+ .type = RKCIF_MIPI_TYPE_RAW12,
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_SGRBG12P,
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
|
||||
+ .depth = 12,
|
||||
+ .cplanes = 1,
|
||||
+ .mipi = {
|
||||
+ .dt = MIPI_CSI2_DT_RAW12,
|
||||
+ .compact = true,
|
||||
+ .type = RKCIF_MIPI_TYPE_RAW12,
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_SRGGB12,
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,
|
||||
+ .depth = 12,
|
||||
+ .cplanes = 1,
|
||||
+ .mipi = {
|
||||
+ .dt = MIPI_CSI2_DT_RAW12,
|
||||
+ .type = RKCIF_MIPI_TYPE_RAW12,
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ .fourcc = V4L2_PIX_FMT_SRGGB12P,
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,
|
||||
+ .depth = 12,
|
||||
+ .cplanes = 1,
|
||||
+ .mipi = {
|
||||
+ .dt = MIPI_CSI2_DT_RAW12,
|
||||
+ .compact = true,
|
||||
+ .type = RKCIF_MIPI_TYPE_RAW12,
|
||||
+ },
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static const struct rkcif_input_fmt mipi_in_fmts[] = {
|
||||
+ /* YUV formats */
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_YVYU8_1X16,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_VYUY8_1X16,
|
||||
+ },
|
||||
+ /* RGB formats */
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_RGB888_1X24,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_BGR888_1X24,
|
||||
+ },
|
||||
+ /* Bayer formats */
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
|
||||
+ },
|
||||
+ {
|
||||
+ .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static u32
|
||||
+rkcif_rk3568_mipi_ctrl0(struct rkcif_stream *stream,
|
||||
+ const struct rkcif_output_fmt *active_out_fmt)
|
||||
+{
|
||||
+ u32 ctrl0 = 0;
|
||||
+
|
||||
+ ctrl0 |= RKCIF_MIPI_CTRL0_DT_ID(active_out_fmt->mipi.dt);
|
||||
+ ctrl0 |= RKCIF_MIPI_CTRL0_CAP_EN;
|
||||
+ ctrl0 |= RK3568_MIPI_CTRL0_CROP_EN;
|
||||
+
|
||||
+ if (active_out_fmt->mipi.compact)
|
||||
+ ctrl0 |= RK3568_MIPI_CTRL0_COMPACT_EN;
|
||||
+
|
||||
+ switch (active_out_fmt->mipi.type) {
|
||||
+ case RKCIF_MIPI_TYPE_RAW8:
|
||||
+ break;
|
||||
+ case RKCIF_MIPI_TYPE_RAW10:
|
||||
+ ctrl0 |= RK3568_MIPI_CTRL0_WRDDR(0x1);
|
||||
+ break;
|
||||
+ case RKCIF_MIPI_TYPE_RAW12:
|
||||
+ ctrl0 |= RK3568_MIPI_CTRL0_WRDDR(0x2);
|
||||
+ break;
|
||||
+ case RKCIF_MIPI_TYPE_RGB888:
|
||||
+ ctrl0 |= RK3568_MIPI_CTRL0_WRDDR(0x3);
|
||||
+ break;
|
||||
+ case RKCIF_MIPI_TYPE_YUV422SP:
|
||||
+ ctrl0 |= RK3568_MIPI_CTRL0_WRDDR(0x4);
|
||||
+ break;
|
||||
+ case RKCIF_MIPI_TYPE_YUV420SP:
|
||||
+ ctrl0 |= RK3568_MIPI_CTRL0_WRDDR(0x5);
|
||||
+ break;
|
||||
+ case RKCIF_MIPI_TYPE_YUV400:
|
||||
+ ctrl0 |= RK3568_MIPI_CTRL0_WRDDR(0x6);
|
||||
+ break;
|
||||
+ default:
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ return ctrl0;
|
||||
+}
|
||||
+
|
||||
+const struct rkcif_mipi_match_data rkcif_rk3568_vicap_mipi_match_data = {
|
||||
+ .mipi_num = 1,
|
||||
+ .mipi_ctrl0 = rkcif_rk3568_mipi_ctrl0,
|
||||
+ .regs = {
|
||||
+ [RKCIF_MIPI_CTRL] = 0x20,
|
||||
+ [RKCIF_MIPI_INTEN] = 0xa4,
|
||||
+ [RKCIF_MIPI_INTSTAT] = 0xa8,
|
||||
+ },
|
||||
+ .regs_id = {
|
||||
+ [RKCIF_ID0] = {
|
||||
+ [RKCIF_MIPI_CTRL0] = 0x00,
|
||||
+ [RKCIF_MIPI_CTRL1] = 0x04,
|
||||
+ [RKCIF_MIPI_FRAME0_ADDR_Y] = 0x24,
|
||||
+ [RKCIF_MIPI_FRAME0_ADDR_UV] = 0x2c,
|
||||
+ [RKCIF_MIPI_FRAME0_VLW_Y] = 0x34,
|
||||
+ [RKCIF_MIPI_FRAME0_VLW_UV] = 0x3c,
|
||||
+ [RKCIF_MIPI_FRAME1_ADDR_Y] = 0x28,
|
||||
+ [RKCIF_MIPI_FRAME1_ADDR_UV] = 0x30,
|
||||
+ [RKCIF_MIPI_FRAME1_VLW_Y] = 0x38,
|
||||
+ [RKCIF_MIPI_FRAME1_VLW_UV] = 0x40,
|
||||
+ [RKCIF_MIPI_CROP_START] = 0xbc,
|
||||
+ },
|
||||
+ [RKCIF_ID1] = {
|
||||
+ [RKCIF_MIPI_CTRL0] = 0x08,
|
||||
+ [RKCIF_MIPI_CTRL1] = 0x0c,
|
||||
+ [RKCIF_MIPI_FRAME0_ADDR_Y] = 0x44,
|
||||
+ [RKCIF_MIPI_FRAME0_ADDR_UV] = 0x4c,
|
||||
+ [RKCIF_MIPI_FRAME0_VLW_Y] = 0x54,
|
||||
+ [RKCIF_MIPI_FRAME0_VLW_UV] = 0x5c,
|
||||
+ [RKCIF_MIPI_FRAME1_ADDR_Y] = 0x48,
|
||||
+ [RKCIF_MIPI_FRAME1_ADDR_UV] = 0x50,
|
||||
+ [RKCIF_MIPI_FRAME1_VLW_Y] = 0x58,
|
||||
+ [RKCIF_MIPI_FRAME1_VLW_UV] = 0x60,
|
||||
+ [RKCIF_MIPI_CROP_START] = 0xc0,
|
||||
+ },
|
||||
+ [RKCIF_ID2] = {
|
||||
+ [RKCIF_MIPI_CTRL0] = 0x10,
|
||||
+ [RKCIF_MIPI_CTRL1] = 0x14,
|
||||
+ [RKCIF_MIPI_FRAME0_ADDR_Y] = 0x64,
|
||||
+ [RKCIF_MIPI_FRAME0_ADDR_UV] = 0x6c,
|
||||
+ [RKCIF_MIPI_FRAME0_VLW_Y] = 0x74,
|
||||
+ [RKCIF_MIPI_FRAME0_VLW_UV] = 0x7c,
|
||||
+ [RKCIF_MIPI_FRAME1_ADDR_Y] = 0x68,
|
||||
+ [RKCIF_MIPI_FRAME1_ADDR_UV] = 0x70,
|
||||
+ [RKCIF_MIPI_FRAME1_VLW_Y] = 0x78,
|
||||
+ [RKCIF_MIPI_FRAME1_VLW_UV] = 0x80,
|
||||
+ [RKCIF_MIPI_CROP_START] = 0xc4,
|
||||
+ },
|
||||
+ [RKCIF_ID3] = {
|
||||
+ [RKCIF_MIPI_CTRL0] = 0x18,
|
||||
+ [RKCIF_MIPI_CTRL1] = 0x1c,
|
||||
+ [RKCIF_MIPI_FRAME0_ADDR_Y] = 0x84,
|
||||
+ [RKCIF_MIPI_FRAME0_ADDR_UV] = 0x8c,
|
||||
+ [RKCIF_MIPI_FRAME0_VLW_Y] = 0x94,
|
||||
+ [RKCIF_MIPI_FRAME0_VLW_UV] = 0x9c,
|
||||
+ [RKCIF_MIPI_FRAME1_ADDR_Y] = 0x88,
|
||||
+ [RKCIF_MIPI_FRAME1_ADDR_UV] = 0x90,
|
||||
+ [RKCIF_MIPI_FRAME1_VLW_Y] = 0x98,
|
||||
+ [RKCIF_MIPI_FRAME1_VLW_UV] = 0xa0,
|
||||
+ [RKCIF_MIPI_CROP_START] = 0xc8,
|
||||
+ },
|
||||
+ },
|
||||
+ .blocks = {
|
||||
+ {
|
||||
+ .offset = 0x80,
|
||||
+ },
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static inline unsigned int rkcif_mipi_get_reg(struct rkcif_interface *interface,
|
||||
+ unsigned int index)
|
||||
+{
|
||||
+ struct rkcif_device *rkcif = interface->rkcif;
|
||||
+ unsigned int block, offset, reg;
|
||||
+
|
||||
+ block = interface->index - RKCIF_MIPI_BASE;
|
||||
+
|
||||
+ if (WARN_ON_ONCE(block > RKCIF_MIPI_MAX - RKCIF_MIPI_BASE) ||
|
||||
+ WARN_ON_ONCE(index > RKCIF_MIPI_REGISTER_MAX))
|
||||
+ return RKCIF_REGISTER_NOTSUPPORTED;
|
||||
+
|
||||
+ offset = rkcif->match_data->mipi->blocks[block].offset;
|
||||
+ reg = rkcif->match_data->mipi->regs[index];
|
||||
+ if (reg == RKCIF_REGISTER_NOTSUPPORTED)
|
||||
+ return reg;
|
||||
+
|
||||
+ return offset + reg;
|
||||
+}
|
||||
+
|
||||
+static inline unsigned int rkcif_mipi_id_get_reg(struct rkcif_stream *stream,
|
||||
+ unsigned int index)
|
||||
+{
|
||||
+ struct rkcif_device *rkcif = stream->rkcif;
|
||||
+ unsigned int block, id, offset, reg;
|
||||
+
|
||||
+ block = stream->interface->index - RKCIF_MIPI_BASE;
|
||||
+ id = stream->id;
|
||||
+
|
||||
+ if (WARN_ON_ONCE(block > RKCIF_MIPI_MAX - RKCIF_MIPI_BASE) ||
|
||||
+ WARN_ON_ONCE(id > RKCIF_ID_MAX) ||
|
||||
+ WARN_ON_ONCE(index > RKCIF_MIPI_ID_REGISTER_MAX))
|
||||
+ return RKCIF_REGISTER_NOTSUPPORTED;
|
||||
+
|
||||
+ offset = rkcif->match_data->mipi->blocks[block].offset;
|
||||
+ reg = rkcif->match_data->mipi->regs_id[id][index];
|
||||
+ if (reg == RKCIF_REGISTER_NOTSUPPORTED)
|
||||
+ return reg;
|
||||
+
|
||||
+ return offset + reg;
|
||||
+}
|
||||
+
|
||||
+static inline __maybe_unused void
|
||||
+rkcif_mipi_write(struct rkcif_interface *interface, unsigned int index, u32 val)
|
||||
+{
|
||||
+ unsigned int addr = rkcif_mipi_get_reg(interface, index);
|
||||
+
|
||||
+ if (addr == RKCIF_REGISTER_NOTSUPPORTED)
|
||||
+ return;
|
||||
+
|
||||
+ writel(val, interface->rkcif->base_addr + addr);
|
||||
+}
|
||||
+
|
||||
+static inline __maybe_unused void
|
||||
+rkcif_mipi_stream_write(struct rkcif_stream *stream, unsigned int index,
|
||||
+ u32 val)
|
||||
+{
|
||||
+ unsigned int addr = rkcif_mipi_id_get_reg(stream, index);
|
||||
+
|
||||
+ if (addr == RKCIF_REGISTER_NOTSUPPORTED)
|
||||
+ return;
|
||||
+
|
||||
+ writel(val, stream->rkcif->base_addr + addr);
|
||||
+}
|
||||
+
|
||||
+static inline __maybe_unused u32
|
||||
+rkcif_mipi_read(struct rkcif_interface *interface, unsigned int index)
|
||||
+{
|
||||
+ unsigned int addr = rkcif_mipi_get_reg(interface, index);
|
||||
+
|
||||
+ if (addr == RKCIF_REGISTER_NOTSUPPORTED)
|
||||
+ return 0;
|
||||
+
|
||||
+ return readl(interface->rkcif->base_addr + addr);
|
||||
+}
|
||||
+
|
||||
+static inline __maybe_unused u32
|
||||
+rkcif_mipi_stream_read(struct rkcif_stream *stream, unsigned int index)
|
||||
+{
|
||||
+ unsigned int addr = rkcif_mipi_id_get_reg(stream, index);
|
||||
+
|
||||
+ if (addr == RKCIF_REGISTER_NOTSUPPORTED)
|
||||
+ return 0;
|
||||
+
|
||||
+ return readl(stream->rkcif->base_addr + addr);
|
||||
+}
|
||||
+
|
||||
+static void rkcif_mipi_queue_buffer(struct rkcif_stream *stream,
|
||||
+ unsigned int index)
|
||||
+{
|
||||
+ struct rkcif_buffer *buffer = stream->buffers[index];
|
||||
+ u32 frm_addr_y, frm_addr_uv;
|
||||
+
|
||||
+ frm_addr_y = index ? RKCIF_MIPI_FRAME1_ADDR_Y :
|
||||
+ RKCIF_MIPI_FRAME0_ADDR_Y;
|
||||
+ frm_addr_uv = index ? RKCIF_MIPI_FRAME1_ADDR_UV :
|
||||
+ RKCIF_MIPI_FRAME0_ADDR_UV;
|
||||
+
|
||||
+ rkcif_mipi_stream_write(stream, frm_addr_y,
|
||||
+ buffer->buff_addr[RKCIF_PLANE_Y]);
|
||||
+ rkcif_mipi_stream_write(stream, frm_addr_uv,
|
||||
+ buffer->buff_addr[RKCIF_PLANE_UV]);
|
||||
+}
|
||||
+
|
||||
+static int rkcif_mipi_start_streaming(struct rkcif_stream *stream)
|
||||
+{
|
||||
+ struct rkcif_interface *interface = stream->interface;
|
||||
+ const struct rkcif_output_fmt *active_out_fmt;
|
||||
+ const struct rkcif_mipi_match_data *match_data;
|
||||
+ struct v4l2_subdev_state *state;
|
||||
+ u32 ctrl0 = 0, ctrl1 = 0, int_temp = 0, int_mask = 0, vlw = 0;
|
||||
+ u16 height, width;
|
||||
+ int ret = -EINVAL;
|
||||
+
|
||||
+ state = v4l2_subdev_lock_and_get_active_state(&interface->sd);
|
||||
+
|
||||
+ active_out_fmt = rkcif_stream_find_output_fmt(stream, false,
|
||||
+ stream->pix.pixelformat);
|
||||
+ if (!active_out_fmt)
|
||||
+ goto out;
|
||||
+
|
||||
+ height = stream->pix.height;
|
||||
+ width = stream->pix.width;
|
||||
+ vlw = stream->pix.plane_fmt[0].bytesperline;
|
||||
+
|
||||
+ match_data = stream->rkcif->match_data->mipi;
|
||||
+ if (match_data->mipi_ctrl0)
|
||||
+ ctrl0 = match_data->mipi_ctrl0(stream, active_out_fmt);
|
||||
+
|
||||
+ ctrl1 = RKCIF_XY_COORD(width, height);
|
||||
+
|
||||
+ int_mask |= RKCIF_MIPI_INT_FRAME0_END(stream->id);
|
||||
+ int_mask |= RKCIF_MIPI_INT_FRAME1_END(stream->id);
|
||||
+
|
||||
+ int_temp = rkcif_mipi_read(interface, RKCIF_MIPI_INTEN);
|
||||
+ int_temp |= int_mask;
|
||||
+ rkcif_mipi_write(interface, RKCIF_MIPI_INTEN, int_temp);
|
||||
+
|
||||
+ int_temp = rkcif_mipi_read(interface, RKCIF_MIPI_INTSTAT);
|
||||
+ int_temp &= ~int_mask;
|
||||
+ rkcif_mipi_write(interface, RKCIF_MIPI_INTSTAT, int_temp);
|
||||
+
|
||||
+ rkcif_mipi_stream_write(stream, RKCIF_MIPI_FRAME0_VLW_Y, vlw);
|
||||
+ rkcif_mipi_stream_write(stream, RKCIF_MIPI_FRAME1_VLW_Y, vlw);
|
||||
+ rkcif_mipi_stream_write(stream, RKCIF_MIPI_FRAME0_VLW_UV, vlw);
|
||||
+ rkcif_mipi_stream_write(stream, RKCIF_MIPI_FRAME1_VLW_UV, vlw);
|
||||
+ rkcif_mipi_stream_write(stream, RKCIF_MIPI_CROP_START, 0x0);
|
||||
+ rkcif_mipi_stream_write(stream, RKCIF_MIPI_CTRL1, ctrl1);
|
||||
+ rkcif_mipi_stream_write(stream, RKCIF_MIPI_CTRL0, ctrl0);
|
||||
+
|
||||
+ ret = 0;
|
||||
+
|
||||
+out:
|
||||
+ v4l2_subdev_unlock_state(state);
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static void rkcif_mipi_stop_streaming(struct rkcif_stream *stream)
|
||||
+{
|
||||
+ struct rkcif_interface *interface = stream->interface;
|
||||
+ struct v4l2_subdev_state *state;
|
||||
+ u32 int_temp = 0, int_mask = 0;
|
||||
+
|
||||
+ state = v4l2_subdev_lock_and_get_active_state(&interface->sd);
|
||||
+
|
||||
+ rkcif_mipi_stream_write(stream, RKCIF_MIPI_CTRL0, 0);
|
||||
+
|
||||
+ int_mask |= RKCIF_MIPI_INT_FRAME0_END(stream->id);
|
||||
+ int_mask |= RKCIF_MIPI_INT_FRAME1_END(stream->id);
|
||||
+
|
||||
+ int_temp = rkcif_mipi_read(interface, RKCIF_MIPI_INTEN);
|
||||
+ int_temp &= ~int_mask;
|
||||
+ rkcif_mipi_write(interface, RKCIF_MIPI_INTEN, int_temp);
|
||||
+
|
||||
+ int_temp = rkcif_mipi_read(interface, RKCIF_MIPI_INTSTAT);
|
||||
+ int_temp &= ~int_mask;
|
||||
+ rkcif_mipi_write(interface, RKCIF_MIPI_INTSTAT, int_temp);
|
||||
+
|
||||
+ stream->stopping = false;
|
||||
+
|
||||
+ v4l2_subdev_unlock_state(state);
|
||||
+}
|
||||
+
|
||||
+static void rkcif_mipi_set_crop(struct rkcif_stream *stream, u16 left, u16 top)
|
||||
+{
|
||||
+ u32 val;
|
||||
+
|
||||
+ val = RKCIF_XY_COORD(left, top);
|
||||
+ rkcif_mipi_stream_write(stream, RKCIF_MIPI_CROP_START, val);
|
||||
+}
|
||||
+
|
||||
+irqreturn_t rkcif_mipi_isr(int irq, void *ctx)
|
||||
+{
|
||||
+ struct device *dev = ctx;
|
||||
+ struct rkcif_device *rkcif = dev_get_drvdata(dev);
|
||||
+ irqreturn_t ret = IRQ_NONE;
|
||||
+ u32 intstat;
|
||||
+
|
||||
+ for (unsigned int i = 0; i < rkcif->match_data->mipi->mipi_num; i++) {
|
||||
+ enum rkcif_interface_index index = RKCIF_MIPI_BASE + i;
|
||||
+ struct rkcif_interface *interface = &rkcif->interfaces[index];
|
||||
+
|
||||
+ intstat = rkcif_mipi_read(interface, RKCIF_MIPI_INTSTAT);
|
||||
+ rkcif_mipi_write(interface, RKCIF_MIPI_INTSTAT, intstat);
|
||||
+
|
||||
+ for (unsigned int j = 0; j < interface->streams_num; j++) {
|
||||
+ struct rkcif_stream *stream = &interface->streams[j];
|
||||
+
|
||||
+ if (intstat & RKCIF_MIPI_INT_FRAME0_END(stream->id) ||
|
||||
+ intstat & RKCIF_MIPI_INT_FRAME1_END(stream->id)) {
|
||||
+ ret = IRQ_HANDLED;
|
||||
+
|
||||
+ if (stream->stopping) {
|
||||
+ rkcif_mipi_stop_streaming(stream);
|
||||
+ wake_up(&stream->wq_stopped);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ rkcif_stream_pingpong(stream);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+int rkcif_mipi_register(struct rkcif_device *rkcif)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ if (!rkcif->match_data->mipi)
|
||||
+ return 0;
|
||||
+
|
||||
+ for (unsigned int i = 0; i < rkcif->match_data->mipi->mipi_num; i++) {
|
||||
+ enum rkcif_interface_index index = RKCIF_MIPI_BASE + i;
|
||||
+ struct rkcif_interface *interface = &rkcif->interfaces[index];
|
||||
+
|
||||
+ interface->index = index;
|
||||
+ interface->type = RKCIF_IF_MIPI;
|
||||
+ interface->in_fmts = mipi_in_fmts;
|
||||
+ interface->in_fmts_num = ARRAY_SIZE(mipi_in_fmts);
|
||||
+ interface->set_crop = rkcif_mipi_set_crop;
|
||||
+ interface->streams_num = 0;
|
||||
+ ret = rkcif_interface_register(rkcif, interface);
|
||||
+ if (ret)
|
||||
+ continue;
|
||||
+
|
||||
+ for (unsigned int j = 0; j < RKCIF_ID_MAX; j++) {
|
||||
+ struct rkcif_stream *stream = &interface->streams[j];
|
||||
+
|
||||
+ stream->id = j;
|
||||
+ stream->interface = interface;
|
||||
+ stream->out_fmts = mipi_out_fmts;
|
||||
+ stream->out_fmts_num = ARRAY_SIZE(mipi_out_fmts);
|
||||
+ stream->queue_buffer = rkcif_mipi_queue_buffer;
|
||||
+ stream->start_streaming = rkcif_mipi_start_streaming;
|
||||
+ stream->stop_streaming = rkcif_mipi_stop_streaming;
|
||||
+ ret = rkcif_stream_register(rkcif, stream);
|
||||
+ if (ret)
|
||||
+ goto err;
|
||||
+ interface->streams_num++;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+err:
|
||||
+ for (unsigned int i = 0; i < rkcif->match_data->mipi->mipi_num; i++) {
|
||||
+ enum rkcif_interface_index index = RKCIF_MIPI_BASE + i;
|
||||
+ struct rkcif_interface *interface = &rkcif->interfaces[index];
|
||||
+
|
||||
+ for (unsigned int j = 0; j < interface->streams_num; j++)
|
||||
+ rkcif_stream_unregister(&interface->streams[j]);
|
||||
+
|
||||
+ rkcif_interface_unregister(interface);
|
||||
+ }
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+void rkcif_mipi_unregister(struct rkcif_device *rkcif)
|
||||
+{
|
||||
+ if (!rkcif->match_data->mipi)
|
||||
+ return;
|
||||
+
|
||||
+ for (unsigned int i = 0; i < rkcif->match_data->mipi->mipi_num; i++) {
|
||||
+ enum rkcif_interface_index index = RKCIF_MIPI_BASE + i;
|
||||
+ struct rkcif_interface *interface = &rkcif->interfaces[index];
|
||||
+
|
||||
+ for (unsigned int j = 0; j < interface->streams_num; j++)
|
||||
+ rkcif_stream_unregister(&interface->streams[j]);
|
||||
+
|
||||
+ rkcif_interface_unregister(interface);
|
||||
+ }
|
||||
+}
|
||||
--- /dev/null
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.h
|
||||
@@ -0,0 +1,23 @@
|
||||
+/* SPDX-License-Identifier: GPL-2.0 */
|
||||
+/*
|
||||
+ * Rockchip Camera Interface (CIF) Driver
|
||||
+ *
|
||||
+ * Copyright (C) 2018 Rockchip Electronics Co., Ltd.
|
||||
+ * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net>
|
||||
+ * Copyright (C) 2025 Collabora, Ltd.
|
||||
+ */
|
||||
+
|
||||
+#ifndef _RKCIF_CAPTURE_MIPI_H
|
||||
+#define _RKCIF_CAPTURE_MIPI_H
|
||||
+
|
||||
+#include "rkcif-common.h"
|
||||
+
|
||||
+extern const struct rkcif_mipi_match_data rkcif_rk3568_vicap_mipi_match_data;
|
||||
+
|
||||
+int rkcif_mipi_register(struct rkcif_device *rkcif);
|
||||
+
|
||||
+void rkcif_mipi_unregister(struct rkcif_device *rkcif);
|
||||
+
|
||||
+irqreturn_t rkcif_mipi_isr(int irq, void *ctx);
|
||||
+
|
||||
+#endif
|
||||
--- a/drivers/media/platform/rockchip/rkcif/rkcif-common.h
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-common.h
|
||||
@@ -73,6 +73,17 @@ enum rkcif_interface_type {
|
||||
RKCIF_IF_MIPI,
|
||||
};
|
||||
|
||||
+enum rkcif_mipi_format_type {
|
||||
+ RKCIF_MIPI_TYPE_INVALID,
|
||||
+ RKCIF_MIPI_TYPE_RAW8,
|
||||
+ RKCIF_MIPI_TYPE_RAW10,
|
||||
+ RKCIF_MIPI_TYPE_RAW12,
|
||||
+ RKCIF_MIPI_TYPE_RGB888,
|
||||
+ RKCIF_MIPI_TYPE_YUV422SP,
|
||||
+ RKCIF_MIPI_TYPE_YUV420SP,
|
||||
+ RKCIF_MIPI_TYPE_YUV400,
|
||||
+};
|
||||
+
|
||||
struct rkcif_buffer {
|
||||
struct vb2_v4l2_buffer vb;
|
||||
struct list_head queue;
|
||||
@@ -107,9 +118,15 @@ struct rkcif_output_fmt {
|
||||
u32 fourcc;
|
||||
u32 mbus_code;
|
||||
u8 cplanes;
|
||||
+ u8 depth;
|
||||
|
||||
union {
|
||||
u32 dvp_fmt_val;
|
||||
+ struct {
|
||||
+ u8 dt;
|
||||
+ bool compact;
|
||||
+ enum rkcif_mipi_format_type type;
|
||||
+ } mipi;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -184,6 +201,17 @@ struct rkcif_interface {
|
||||
void (*set_crop)(struct rkcif_stream *stream, u16 left, u16 top);
|
||||
};
|
||||
|
||||
+struct rkcif_mipi_match_data {
|
||||
+ unsigned int mipi_num;
|
||||
+ unsigned int regs[RKCIF_MIPI_REGISTER_MAX];
|
||||
+ unsigned int regs_id[RKCIF_ID_MAX][RKCIF_MIPI_ID_REGISTER_MAX];
|
||||
+ u32 (*mipi_ctrl0)(struct rkcif_stream *stream,
|
||||
+ const struct rkcif_output_fmt *active_out_fmt);
|
||||
+ struct {
|
||||
+ unsigned int offset;
|
||||
+ } blocks[RKCIF_MIPI_MAX - RKCIF_MIPI_BASE];
|
||||
+};
|
||||
+
|
||||
struct rkcif_dvp_match_data {
|
||||
const struct rkcif_input_fmt *in_fmts;
|
||||
unsigned int in_fmts_num;
|
||||
@@ -199,6 +227,7 @@ struct rkcif_match_data {
|
||||
const char *const *clks;
|
||||
unsigned int clks_num;
|
||||
const struct rkcif_dvp_match_data *dvp;
|
||||
+ const struct rkcif_mipi_match_data *mipi;
|
||||
};
|
||||
|
||||
struct rkcif_device {
|
||||
--- a/drivers/media/platform/rockchip/rkcif/rkcif-dev.c
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-dev.c
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <media/v4l2-mc.h>
|
||||
|
||||
#include "rkcif-capture-dvp.h"
|
||||
+#include "rkcif-capture-mipi.h"
|
||||
#include "rkcif-common.h"
|
||||
|
||||
static const char *const px30_vip_clks[] = {
|
||||
@@ -49,6 +50,7 @@ static const struct rkcif_match_data rk3
|
||||
.clks = rk3568_vicap_clks,
|
||||
.clks_num = ARRAY_SIZE(rk3568_vicap_clks),
|
||||
.dvp = &rkcif_rk3568_vicap_dvp_match_data,
|
||||
+ .mipi = &rkcif_rk3568_vicap_mipi_match_data,
|
||||
};
|
||||
|
||||
static const struct of_device_id rkcif_plat_of_match[] = {
|
||||
@@ -72,14 +74,21 @@ static int rkcif_register(struct rkcif_d
|
||||
if (ret && ret != -ENODEV)
|
||||
goto err;
|
||||
|
||||
+ ret = rkcif_mipi_register(rkcif);
|
||||
+ if (ret && ret != -ENODEV)
|
||||
+ goto err_dvp_unregister;
|
||||
+
|
||||
return 0;
|
||||
|
||||
+err_dvp_unregister:
|
||||
+ rkcif_dvp_unregister(rkcif);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void rkcif_unregister(struct rkcif_device *rkcif)
|
||||
{
|
||||
+ rkcif_mipi_unregister(rkcif);
|
||||
rkcif_dvp_unregister(rkcif);
|
||||
}
|
||||
|
||||
@@ -128,6 +137,9 @@ static irqreturn_t rkcif_isr(int irq, vo
|
||||
if (rkcif_dvp_isr(irq, ctx) == IRQ_HANDLED)
|
||||
ret = IRQ_HANDLED;
|
||||
|
||||
+ if (rkcif_mipi_isr(irq, ctx) == IRQ_HANDLED)
|
||||
+ ret = IRQ_HANDLED;
|
||||
+
|
||||
return ret;
|
||||
}
|
||||
|
||||
--- a/drivers/media/platform/rockchip/rkcif/rkcif-regs.h
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-regs.h
|
||||
@@ -128,4 +128,26 @@ enum rkcif_dvp_register_index {
|
||||
RKCIF_DVP_REGISTER_MAX
|
||||
};
|
||||
|
||||
+enum rkcif_mipi_register_index {
|
||||
+ RKCIF_MIPI_CTRL,
|
||||
+ RKCIF_MIPI_INTEN,
|
||||
+ RKCIF_MIPI_INTSTAT,
|
||||
+ RKCIF_MIPI_REGISTER_MAX
|
||||
+};
|
||||
+
|
||||
+enum rkcif_mipi_id_register_index {
|
||||
+ RKCIF_MIPI_CTRL0,
|
||||
+ RKCIF_MIPI_CTRL1,
|
||||
+ RKCIF_MIPI_FRAME0_ADDR_Y,
|
||||
+ RKCIF_MIPI_FRAME0_ADDR_UV,
|
||||
+ RKCIF_MIPI_FRAME0_VLW_Y,
|
||||
+ RKCIF_MIPI_FRAME0_VLW_UV,
|
||||
+ RKCIF_MIPI_FRAME1_ADDR_Y,
|
||||
+ RKCIF_MIPI_FRAME1_ADDR_UV,
|
||||
+ RKCIF_MIPI_FRAME1_VLW_Y,
|
||||
+ RKCIF_MIPI_FRAME1_VLW_UV,
|
||||
+ RKCIF_MIPI_CROP_START,
|
||||
+ RKCIF_MIPI_ID_REGISTER_MAX
|
||||
+};
|
||||
+
|
||||
#endif
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
From a1da27d0c3e08d88980a3205fc61773ec6089666 Mon Sep 17 00:00:00 2001
|
||||
From: Michael Riesch <michael.riesch@collabora.com>
|
||||
Date: Tue, 20 Jan 2026 13:22:27 +0100
|
||||
Subject: [PATCH] media: dt-bindings: add rockchip mipi csi-2 receiver
|
||||
|
||||
Add documentation for the Rockchip MIPI CSI-2 Receiver.
|
||||
|
||||
Signed-off-by: Michael Riesch <michael.riesch@wolfvision.net>
|
||||
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
|
||||
Signed-off-by: Michael Riesch <michael.riesch@collabora.com>
|
||||
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
|
||||
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
|
||||
---
|
||||
.../media/rockchip,rk3568-mipi-csi2.yaml | 141 ++++++++++++++++++
|
||||
1 file changed, 141 insertions(+)
|
||||
create mode 100644 Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml
|
||||
|
||||
--- /dev/null
|
||||
+++ b/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml
|
||||
@@ -0,0 +1,141 @@
|
||||
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
+%YAML 1.2
|
||||
+---
|
||||
+$id: http://devicetree.org/schemas/media/rockchip,rk3568-mipi-csi2.yaml#
|
||||
+$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
+
|
||||
+title: Rockchip MIPI CSI-2 Receiver
|
||||
+
|
||||
+maintainers:
|
||||
+ - Michael Riesch <michael.riesch@collabora.com>
|
||||
+
|
||||
+description:
|
||||
+ The Rockchip MIPI CSI-2 Receiver is a CSI-2 bridge with one input port and
|
||||
+ one output port. It receives the data with the help of an external MIPI PHY
|
||||
+ (C-PHY or D-PHY) and passes it to the Rockchip Video Capture (VICAP) block.
|
||||
+
|
||||
+properties:
|
||||
+ compatible:
|
||||
+ enum:
|
||||
+ - rockchip,rk3568-mipi-csi2
|
||||
+
|
||||
+ reg:
|
||||
+ maxItems: 1
|
||||
+
|
||||
+ interrupts:
|
||||
+ items:
|
||||
+ - description: Interrupt that signals changes in CSI2HOST_ERR1.
|
||||
+ - description: Interrupt that signals changes in CSI2HOST_ERR2.
|
||||
+
|
||||
+ interrupt-names:
|
||||
+ items:
|
||||
+ - const: err1
|
||||
+ - const: err2
|
||||
+
|
||||
+ clocks:
|
||||
+ maxItems: 1
|
||||
+
|
||||
+ phys:
|
||||
+ maxItems: 1
|
||||
+ description: MIPI C-PHY or D-PHY.
|
||||
+
|
||||
+ ports:
|
||||
+ $ref: /schemas/graph.yaml#/properties/ports
|
||||
+
|
||||
+ properties:
|
||||
+ port@0:
|
||||
+ $ref: /schemas/graph.yaml#/$defs/port-base
|
||||
+ unevaluatedProperties: false
|
||||
+ description: Input port node. Connect to e.g., a MIPI CSI-2 image sensor.
|
||||
+
|
||||
+ properties:
|
||||
+ endpoint:
|
||||
+ $ref: video-interfaces.yaml#
|
||||
+ unevaluatedProperties: false
|
||||
+
|
||||
+ properties:
|
||||
+ bus-type:
|
||||
+ enum:
|
||||
+ - 1 # MEDIA_BUS_TYPE_CSI2_CPHY
|
||||
+ - 4 # MEDIA_BUS_TYPE_CSI2_DPHY
|
||||
+
|
||||
+ data-lanes:
|
||||
+ minItems: 1
|
||||
+ maxItems: 4
|
||||
+
|
||||
+ required:
|
||||
+ - bus-type
|
||||
+ - data-lanes
|
||||
+
|
||||
+ port@1:
|
||||
+ $ref: /schemas/graph.yaml#/properties/port
|
||||
+ description: Output port connected to a Rockchip VICAP port.
|
||||
+
|
||||
+ required:
|
||||
+ - port@0
|
||||
+ - port@1
|
||||
+
|
||||
+ power-domains:
|
||||
+ maxItems: 1
|
||||
+
|
||||
+ resets:
|
||||
+ maxItems: 1
|
||||
+
|
||||
+required:
|
||||
+ - compatible
|
||||
+ - reg
|
||||
+ - clocks
|
||||
+ - phys
|
||||
+ - ports
|
||||
+ - power-domains
|
||||
+ - resets
|
||||
+
|
||||
+additionalProperties: false
|
||||
+
|
||||
+examples:
|
||||
+ - |
|
||||
+ #include <dt-bindings/clock/rk3568-cru.h>
|
||||
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
+ #include <dt-bindings/media/video-interfaces.h>
|
||||
+ #include <dt-bindings/power/rk3568-power.h>
|
||||
+
|
||||
+ soc {
|
||||
+ interrupt-parent = <&gic>;
|
||||
+ #address-cells = <2>;
|
||||
+ #size-cells = <2>;
|
||||
+
|
||||
+ csi: csi@fdfb0000 {
|
||||
+ compatible = "rockchip,rk3568-mipi-csi2";
|
||||
+ reg = <0x0 0xfdfb0000 0x0 0x10000>;
|
||||
+ interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>,
|
||||
+ <GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>;
|
||||
+ interrupt-names = "err1", "err2";
|
||||
+ clocks = <&cru PCLK_CSI2HOST1>;
|
||||
+ phys = <&csi_dphy>;
|
||||
+ power-domains = <&power RK3568_PD_VI>;
|
||||
+ resets = <&cru SRST_P_CSI2HOST1>;
|
||||
+
|
||||
+ ports {
|
||||
+ #address-cells = <1>;
|
||||
+ #size-cells = <0>;
|
||||
+
|
||||
+ csi_in: port@0 {
|
||||
+ reg = <0>;
|
||||
+
|
||||
+ csi_input: endpoint {
|
||||
+ bus-type = <MEDIA_BUS_TYPE_CSI2_DPHY>;
|
||||
+ data-lanes = <1 2 3 4>;
|
||||
+ remote-endpoint = <&imx415_output>;
|
||||
+ };
|
||||
+ };
|
||||
+
|
||||
+ csi_out: port@1 {
|
||||
+ reg = <1>;
|
||||
+
|
||||
+ csi_output: endpoint {
|
||||
+ remote-endpoint = <&vicap_mipi_input>;
|
||||
+ };
|
||||
+ };
|
||||
+ };
|
||||
+ };
|
||||
+ };
|
||||
+783
@@ -0,0 +1,783 @@
|
||||
From 355a110040665e439af126e8ec9c3968e73f163c Mon Sep 17 00:00:00 2001
|
||||
From: Michael Riesch <michael.riesch@collabora.com>
|
||||
Date: Tue, 20 Jan 2026 13:22:28 +0100
|
||||
Subject: [PATCH] media: synopsys: add driver for the designware mipi csi-2
|
||||
receiver
|
||||
|
||||
The Synopsys DesignWare MIPI CSI-2 Receiver is a CSI-2 bridge with
|
||||
one input port and one output port. It receives the data with the
|
||||
help of an external MIPI PHY (C-PHY or D-PHY) and passes it to e.g.,
|
||||
the Rockchip Video Capture (VICAP) block on recent Rockchip SoCs.
|
||||
|
||||
Add a V4L2 subdevice driver for this unit.
|
||||
|
||||
Signed-off-by: Michael Riesch <michael.riesch@wolfvision.net>
|
||||
Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
|
||||
Reviewed-by: Mehdi Djait <mehdi.djait@linux.intel.com>
|
||||
Signed-off-by: Michael Riesch <michael.riesch@collabora.com>
|
||||
[Sakari Ailus: Make sparse and smatch happy.]
|
||||
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
|
||||
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
|
||||
---
|
||||
drivers/media/platform/synopsys/Kconfig | 18 +
|
||||
drivers/media/platform/synopsys/Makefile | 2 +
|
||||
.../media/platform/synopsys/dw-mipi-csi2rx.c | 722 ++++++++++++++++++
|
||||
3 files changed, 742 insertions(+)
|
||||
create mode 100644 drivers/media/platform/synopsys/dw-mipi-csi2rx.c
|
||||
|
||||
--- a/drivers/media/platform/synopsys/Kconfig
|
||||
+++ b/drivers/media/platform/synopsys/Kconfig
|
||||
@@ -1,3 +1,21 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
source "drivers/media/platform/synopsys/hdmirx/Kconfig"
|
||||
+
|
||||
+config VIDEO_DW_MIPI_CSI2RX
|
||||
+ tristate "Synopsys DesignWare MIPI CSI-2 Receiver"
|
||||
+ depends on VIDEO_DEV
|
||||
+ depends on V4L_PLATFORM_DRIVERS
|
||||
+ depends on PM && COMMON_CLK
|
||||
+ select MEDIA_CONTROLLER
|
||||
+ select V4L2_FWNODE
|
||||
+ select VIDEO_V4L2_SUBDEV_API
|
||||
+ help
|
||||
+ The Synopsys DesignWare MIPI CSI-2 Receiver is a CSI-2 bridge with
|
||||
+ one input port and one output port. It receives the data with the
|
||||
+ help of an external MIPI PHY (C-PHY or D-PHY) and passes it to e.g.,
|
||||
+ the Rockchip Video Capture (VICAP) block on recent Rockchip SoCs.
|
||||
+ This is a driver for this unit.
|
||||
+
|
||||
+ To compile this driver as a module, choose M here: the module
|
||||
+ will be called dw-mipi-csi2rx.
|
||||
--- a/drivers/media/platform/synopsys/Makefile
|
||||
+++ b/drivers/media/platform/synopsys/Makefile
|
||||
@@ -1,2 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-y += hdmirx/
|
||||
+
|
||||
+obj-$(CONFIG_VIDEO_DW_MIPI_CSI2RX) += dw-mipi-csi2rx.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
|
||||
@@ -0,0 +1,722 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0
|
||||
+/*
|
||||
+ * Synopsys DesignWare MIPI CSI-2 Receiver Driver
|
||||
+ *
|
||||
+ * Copyright (C) 2019 Rockchip Electronics Co., Ltd.
|
||||
+ * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net>
|
||||
+ * Copyright (C) 2026 Collabora, Ltd.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/clk.h>
|
||||
+#include <linux/delay.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/phy/phy.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/pm_runtime.h>
|
||||
+#include <linux/property.h>
|
||||
+#include <linux/reset.h>
|
||||
+
|
||||
+#include <media/mipi-csi2.h>
|
||||
+#include <media/v4l2-ctrls.h>
|
||||
+#include <media/v4l2-fwnode.h>
|
||||
+#include <media/v4l2-mc.h>
|
||||
+#include <media/v4l2-subdev.h>
|
||||
+
|
||||
+#define DW_MIPI_CSI2RX_N_LANES 0x04
|
||||
+#define DW_MIPI_CSI2RX_RESETN 0x10
|
||||
+#define DW_MIPI_CSI2RX_PHY_STATE 0x14
|
||||
+#define DW_MIPI_CSI2RX_ERR1 0x20
|
||||
+#define DW_MIPI_CSI2RX_ERR2 0x24
|
||||
+#define DW_MIPI_CSI2RX_MSK1 0x28
|
||||
+#define DW_MIPI_CSI2RX_MSK2 0x2c
|
||||
+#define DW_MIPI_CSI2RX_CONTROL 0x40
|
||||
+
|
||||
+#define SW_CPHY_EN(x) ((x) << 0)
|
||||
+#define SW_DSI_EN(x) ((x) << 4)
|
||||
+#define SW_DATATYPE_FS(x) ((x) << 8)
|
||||
+#define SW_DATATYPE_FE(x) ((x) << 14)
|
||||
+#define SW_DATATYPE_LS(x) ((x) << 20)
|
||||
+#define SW_DATATYPE_LE(x) ((x) << 26)
|
||||
+
|
||||
+#define DW_MIPI_CSI2RX_CLKS_MAX 1
|
||||
+
|
||||
+enum {
|
||||
+ DW_MIPI_CSI2RX_PAD_SINK,
|
||||
+ DW_MIPI_CSI2RX_PAD_SRC,
|
||||
+ DW_MIPI_CSI2RX_PAD_MAX,
|
||||
+};
|
||||
+
|
||||
+struct dw_mipi_csi2rx_format {
|
||||
+ u32 code;
|
||||
+ u8 depth;
|
||||
+ u8 csi_dt;
|
||||
+};
|
||||
+
|
||||
+struct dw_mipi_csi2rx_device {
|
||||
+ struct device *dev;
|
||||
+
|
||||
+ void __iomem *base_addr;
|
||||
+ struct clk_bulk_data *clks;
|
||||
+ unsigned int clks_num;
|
||||
+ struct phy *phy;
|
||||
+ struct reset_control *reset;
|
||||
+
|
||||
+ const struct dw_mipi_csi2rx_format *formats;
|
||||
+ unsigned int formats_num;
|
||||
+
|
||||
+ struct media_pad pads[DW_MIPI_CSI2RX_PAD_MAX];
|
||||
+ struct v4l2_async_notifier notifier;
|
||||
+ struct v4l2_subdev sd;
|
||||
+
|
||||
+ enum v4l2_mbus_type bus_type;
|
||||
+ u32 lanes_num;
|
||||
+};
|
||||
+
|
||||
+static const struct v4l2_mbus_framefmt default_format = {
|
||||
+ .width = 3840,
|
||||
+ .height = 2160,
|
||||
+ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
|
||||
+ .field = V4L2_FIELD_NONE,
|
||||
+ .colorspace = V4L2_COLORSPACE_RAW,
|
||||
+ .ycbcr_enc = V4L2_YCBCR_ENC_601,
|
||||
+ .quantization = V4L2_QUANTIZATION_FULL_RANGE,
|
||||
+ .xfer_func = V4L2_XFER_FUNC_NONE,
|
||||
+};
|
||||
+
|
||||
+static const struct dw_mipi_csi2rx_format formats[] = {
|
||||
+ /* YUV formats */
|
||||
+ {
|
||||
+ .code = MEDIA_BUS_FMT_YUYV8_1X16,
|
||||
+ .depth = 16,
|
||||
+ .csi_dt = MIPI_CSI2_DT_YUV422_8B,
|
||||
+ },
|
||||
+ {
|
||||
+ .code = MEDIA_BUS_FMT_UYVY8_1X16,
|
||||
+ .depth = 16,
|
||||
+ .csi_dt = MIPI_CSI2_DT_YUV422_8B,
|
||||
+ },
|
||||
+ {
|
||||
+ .code = MEDIA_BUS_FMT_YVYU8_1X16,
|
||||
+ .depth = 16,
|
||||
+ .csi_dt = MIPI_CSI2_DT_YUV422_8B,
|
||||
+ },
|
||||
+ {
|
||||
+ .code = MEDIA_BUS_FMT_VYUY8_1X16,
|
||||
+ .depth = 16,
|
||||
+ .csi_dt = MIPI_CSI2_DT_YUV422_8B,
|
||||
+ },
|
||||
+ /* RGB formats */
|
||||
+ {
|
||||
+ .code = MEDIA_BUS_FMT_RGB888_1X24,
|
||||
+ .depth = 24,
|
||||
+ .csi_dt = MIPI_CSI2_DT_RGB888,
|
||||
+ },
|
||||
+ {
|
||||
+ .code = MEDIA_BUS_FMT_BGR888_1X24,
|
||||
+ .depth = 24,
|
||||
+ .csi_dt = MIPI_CSI2_DT_RGB888,
|
||||
+ },
|
||||
+ /* Bayer formats */
|
||||
+ {
|
||||
+ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
|
||||
+ .depth = 8,
|
||||
+ .csi_dt = MIPI_CSI2_DT_RAW8,
|
||||
+ },
|
||||
+ {
|
||||
+ .code = MEDIA_BUS_FMT_SGBRG8_1X8,
|
||||
+ .depth = 8,
|
||||
+ .csi_dt = MIPI_CSI2_DT_RAW8,
|
||||
+ },
|
||||
+ {
|
||||
+ .code = MEDIA_BUS_FMT_SGRBG8_1X8,
|
||||
+ .depth = 8,
|
||||
+ .csi_dt = MIPI_CSI2_DT_RAW8,
|
||||
+ },
|
||||
+ {
|
||||
+ .code = MEDIA_BUS_FMT_SRGGB8_1X8,
|
||||
+ .depth = 8,
|
||||
+ .csi_dt = MIPI_CSI2_DT_RAW8,
|
||||
+ },
|
||||
+ {
|
||||
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
|
||||
+ .depth = 10,
|
||||
+ .csi_dt = MIPI_CSI2_DT_RAW10,
|
||||
+ },
|
||||
+ {
|
||||
+ .code = MEDIA_BUS_FMT_SGBRG10_1X10,
|
||||
+ .depth = 10,
|
||||
+ .csi_dt = MIPI_CSI2_DT_RAW10,
|
||||
+ },
|
||||
+ {
|
||||
+ .code = MEDIA_BUS_FMT_SGRBG10_1X10,
|
||||
+ .depth = 10,
|
||||
+ .csi_dt = MIPI_CSI2_DT_RAW10,
|
||||
+ },
|
||||
+ {
|
||||
+ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
|
||||
+ .depth = 10,
|
||||
+ .csi_dt = MIPI_CSI2_DT_RAW10,
|
||||
+ },
|
||||
+ {
|
||||
+ .code = MEDIA_BUS_FMT_SBGGR12_1X12,
|
||||
+ .depth = 12,
|
||||
+ .csi_dt = MIPI_CSI2_DT_RAW12,
|
||||
+ },
|
||||
+ {
|
||||
+ .code = MEDIA_BUS_FMT_SGBRG12_1X12,
|
||||
+ .depth = 12,
|
||||
+ .csi_dt = MIPI_CSI2_DT_RAW12,
|
||||
+ },
|
||||
+ {
|
||||
+ .code = MEDIA_BUS_FMT_SGRBG12_1X12,
|
||||
+ .depth = 12,
|
||||
+ .csi_dt = MIPI_CSI2_DT_RAW12,
|
||||
+ },
|
||||
+ {
|
||||
+ .code = MEDIA_BUS_FMT_SRGGB12_1X12,
|
||||
+ .depth = 12,
|
||||
+ .csi_dt = MIPI_CSI2_DT_RAW12,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static inline struct dw_mipi_csi2rx_device *to_csi2(struct v4l2_subdev *sd)
|
||||
+{
|
||||
+ return container_of(sd, struct dw_mipi_csi2rx_device, sd);
|
||||
+}
|
||||
+
|
||||
+static inline void dw_mipi_csi2rx_write(struct dw_mipi_csi2rx_device *csi2,
|
||||
+ unsigned int addr, u32 val)
|
||||
+{
|
||||
+ writel(val, csi2->base_addr + addr);
|
||||
+}
|
||||
+
|
||||
+static inline u32 dw_mipi_csi2rx_read(struct dw_mipi_csi2rx_device *csi2,
|
||||
+ unsigned int addr)
|
||||
+{
|
||||
+ return readl(csi2->base_addr + addr);
|
||||
+}
|
||||
+
|
||||
+static const struct dw_mipi_csi2rx_format *
|
||||
+dw_mipi_csi2rx_find_format(struct dw_mipi_csi2rx_device *csi2, u32 mbus_code)
|
||||
+{
|
||||
+ WARN_ON(csi2->formats_num == 0);
|
||||
+
|
||||
+ for (unsigned int i = 0; i < csi2->formats_num; i++) {
|
||||
+ const struct dw_mipi_csi2rx_format *format = &csi2->formats[i];
|
||||
+
|
||||
+ if (format->code == mbus_code)
|
||||
+ return format;
|
||||
+ }
|
||||
+
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+static int dw_mipi_csi2rx_start(struct dw_mipi_csi2rx_device *csi2)
|
||||
+{
|
||||
+ struct media_pad *source_pad;
|
||||
+ union phy_configure_opts opts;
|
||||
+ u32 lanes = csi2->lanes_num;
|
||||
+ u32 control = 0;
|
||||
+ s64 link_freq;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (lanes < 1 || lanes > 4)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ source_pad = media_pad_remote_pad_unique(
|
||||
+ &csi2->pads[DW_MIPI_CSI2RX_PAD_SINK]);
|
||||
+ if (IS_ERR(source_pad))
|
||||
+ return PTR_ERR(source_pad);
|
||||
+
|
||||
+ /* set mult and div to 0, thus completely rely on V4L2_CID_LINK_FREQ */
|
||||
+ link_freq = v4l2_get_link_freq(source_pad, 0, 0);
|
||||
+ if (link_freq < 0)
|
||||
+ return link_freq;
|
||||
+
|
||||
+ switch (csi2->bus_type) {
|
||||
+ case V4L2_MBUS_CSI2_DPHY:
|
||||
+ ret = phy_mipi_dphy_get_default_config_for_hsclk(link_freq * 2,
|
||||
+ lanes, &opts.mipi_dphy);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = phy_set_mode(csi2->phy, PHY_MODE_MIPI_DPHY);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = phy_configure(csi2->phy, &opts);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ control |= SW_CPHY_EN(0);
|
||||
+ break;
|
||||
+
|
||||
+ case V4L2_MBUS_CSI2_CPHY:
|
||||
+ /* TODO: implement CPHY configuration */
|
||||
+ return -EOPNOTSUPP;
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ control |= SW_DATATYPE_FS(0x00) | SW_DATATYPE_FE(0x01) |
|
||||
+ SW_DATATYPE_LS(0x02) | SW_DATATYPE_LE(0x03);
|
||||
+
|
||||
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_N_LANES, lanes - 1);
|
||||
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_CONTROL, control);
|
||||
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_RESETN, 1);
|
||||
+
|
||||
+ return phy_power_on(csi2->phy);
|
||||
+}
|
||||
+
|
||||
+static void dw_mipi_csi2rx_stop(struct dw_mipi_csi2rx_device *csi2)
|
||||
+{
|
||||
+ phy_power_off(csi2->phy);
|
||||
+
|
||||
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_RESETN, 0);
|
||||
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_MSK1, ~0);
|
||||
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_MSK2, ~0);
|
||||
+}
|
||||
+
|
||||
+static const struct media_entity_operations dw_mipi_csi2rx_media_ops = {
|
||||
+ .link_validate = v4l2_subdev_link_validate,
|
||||
+};
|
||||
+
|
||||
+static int
|
||||
+dw_mipi_csi2rx_enum_mbus_code(struct v4l2_subdev *sd,
|
||||
+ struct v4l2_subdev_state *sd_state,
|
||||
+ struct v4l2_subdev_mbus_code_enum *code)
|
||||
+{
|
||||
+ struct dw_mipi_csi2rx_device *csi2 = to_csi2(sd);
|
||||
+
|
||||
+ switch (code->pad) {
|
||||
+ case DW_MIPI_CSI2RX_PAD_SRC:
|
||||
+ if (code->index)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ code->code =
|
||||
+ v4l2_subdev_state_get_format(sd_state,
|
||||
+ DW_MIPI_CSI2RX_PAD_SINK)->code;
|
||||
+
|
||||
+ return 0;
|
||||
+ case DW_MIPI_CSI2RX_PAD_SINK:
|
||||
+ if (code->index > csi2->formats_num)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ code->code = csi2->formats[code->index].code;
|
||||
+ return 0;
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int dw_mipi_csi2rx_set_fmt(struct v4l2_subdev *sd,
|
||||
+ struct v4l2_subdev_state *state,
|
||||
+ struct v4l2_subdev_format *format)
|
||||
+{
|
||||
+ struct dw_mipi_csi2rx_device *csi2 = to_csi2(sd);
|
||||
+ const struct dw_mipi_csi2rx_format *fmt;
|
||||
+ struct v4l2_mbus_framefmt *sink, *src;
|
||||
+
|
||||
+ /* the format on the source pad always matches the sink pad */
|
||||
+ if (format->pad == DW_MIPI_CSI2RX_PAD_SRC)
|
||||
+ return v4l2_subdev_get_fmt(sd, state, format);
|
||||
+
|
||||
+ sink = v4l2_subdev_state_get_format(state, format->pad, format->stream);
|
||||
+ if (!sink)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ fmt = dw_mipi_csi2rx_find_format(csi2, format->format.code);
|
||||
+ if (!fmt)
|
||||
+ format->format = default_format;
|
||||
+
|
||||
+ *sink = format->format;
|
||||
+
|
||||
+ /* propagate the format to the source pad */
|
||||
+ src = v4l2_subdev_state_get_opposite_stream_format(state, format->pad,
|
||||
+ format->stream);
|
||||
+ if (!src)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ *src = *sink;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int dw_mipi_csi2rx_set_routing(struct v4l2_subdev *sd,
|
||||
+ struct v4l2_subdev_state *state,
|
||||
+ enum v4l2_subdev_format_whence which,
|
||||
+ struct v4l2_subdev_krouting *routing)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = v4l2_subdev_routing_validate(sd, routing,
|
||||
+ V4L2_SUBDEV_ROUTING_ONLY_1_TO_1);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ return v4l2_subdev_set_routing_with_fmt(sd, state, routing,
|
||||
+ &default_format);
|
||||
+}
|
||||
+
|
||||
+static int dw_mipi_csi2rx_enable_streams(struct v4l2_subdev *sd,
|
||||
+ struct v4l2_subdev_state *state,
|
||||
+ u32 pad, u64 streams_mask)
|
||||
+{
|
||||
+ struct dw_mipi_csi2rx_device *csi2 = to_csi2(sd);
|
||||
+ struct v4l2_subdev *remote_sd;
|
||||
+ struct media_pad *sink_pad, *remote_pad;
|
||||
+ struct device *dev = csi2->dev;
|
||||
+ u64 mask;
|
||||
+ int ret;
|
||||
+
|
||||
+ sink_pad = &sd->entity.pads[DW_MIPI_CSI2RX_PAD_SINK];
|
||||
+ remote_pad = media_pad_remote_pad_first(sink_pad);
|
||||
+ remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
|
||||
+
|
||||
+ mask = v4l2_subdev_state_xlate_streams(state, DW_MIPI_CSI2RX_PAD_SINK,
|
||||
+ DW_MIPI_CSI2RX_PAD_SRC,
|
||||
+ &streams_mask);
|
||||
+
|
||||
+ ret = pm_runtime_resume_and_get(dev);
|
||||
+ if (ret)
|
||||
+ goto err;
|
||||
+
|
||||
+ ret = dw_mipi_csi2rx_start(csi2);
|
||||
+ if (ret) {
|
||||
+ dev_err(dev, "failed to enable CSI hardware\n");
|
||||
+ goto err_pm_runtime_put;
|
||||
+ }
|
||||
+
|
||||
+ ret = v4l2_subdev_enable_streams(remote_sd, remote_pad->index, mask);
|
||||
+ if (ret)
|
||||
+ goto err_csi_stop;
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+err_csi_stop:
|
||||
+ dw_mipi_csi2rx_stop(csi2);
|
||||
+err_pm_runtime_put:
|
||||
+ pm_runtime_put(dev);
|
||||
+err:
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int dw_mipi_csi2rx_disable_streams(struct v4l2_subdev *sd,
|
||||
+ struct v4l2_subdev_state *state,
|
||||
+ u32 pad, u64 streams_mask)
|
||||
+{
|
||||
+ struct dw_mipi_csi2rx_device *csi2 = to_csi2(sd);
|
||||
+ struct v4l2_subdev *remote_sd;
|
||||
+ struct media_pad *sink_pad, *remote_pad;
|
||||
+ struct device *dev = csi2->dev;
|
||||
+ u64 mask;
|
||||
+ int ret;
|
||||
+
|
||||
+ sink_pad = &sd->entity.pads[DW_MIPI_CSI2RX_PAD_SINK];
|
||||
+ remote_pad = media_pad_remote_pad_first(sink_pad);
|
||||
+ remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
|
||||
+
|
||||
+ mask = v4l2_subdev_state_xlate_streams(state, DW_MIPI_CSI2RX_PAD_SINK,
|
||||
+ DW_MIPI_CSI2RX_PAD_SRC,
|
||||
+ &streams_mask);
|
||||
+
|
||||
+ ret = v4l2_subdev_disable_streams(remote_sd, remote_pad->index, mask);
|
||||
+
|
||||
+ dw_mipi_csi2rx_stop(csi2);
|
||||
+
|
||||
+ pm_runtime_put(dev);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static const struct v4l2_subdev_pad_ops dw_mipi_csi2rx_pad_ops = {
|
||||
+ .enum_mbus_code = dw_mipi_csi2rx_enum_mbus_code,
|
||||
+ .get_fmt = v4l2_subdev_get_fmt,
|
||||
+ .set_fmt = dw_mipi_csi2rx_set_fmt,
|
||||
+ .set_routing = dw_mipi_csi2rx_set_routing,
|
||||
+ .enable_streams = dw_mipi_csi2rx_enable_streams,
|
||||
+ .disable_streams = dw_mipi_csi2rx_disable_streams,
|
||||
+};
|
||||
+
|
||||
+static const struct v4l2_subdev_ops dw_mipi_csi2rx_ops = {
|
||||
+ .pad = &dw_mipi_csi2rx_pad_ops,
|
||||
+};
|
||||
+
|
||||
+static int dw_mipi_csi2rx_init_state(struct v4l2_subdev *sd,
|
||||
+ struct v4l2_subdev_state *state)
|
||||
+{
|
||||
+ struct v4l2_subdev_route routes[] = {
|
||||
+ {
|
||||
+ .sink_pad = DW_MIPI_CSI2RX_PAD_SINK,
|
||||
+ .sink_stream = 0,
|
||||
+ .source_pad = DW_MIPI_CSI2RX_PAD_SRC,
|
||||
+ .source_stream = 0,
|
||||
+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
|
||||
+ },
|
||||
+ };
|
||||
+ struct v4l2_subdev_krouting routing = {
|
||||
+ .len_routes = ARRAY_SIZE(routes),
|
||||
+ .num_routes = ARRAY_SIZE(routes),
|
||||
+ .routes = routes,
|
||||
+ };
|
||||
+
|
||||
+ return v4l2_subdev_set_routing_with_fmt(sd, state, &routing,
|
||||
+ &default_format);
|
||||
+}
|
||||
+
|
||||
+static const struct v4l2_subdev_internal_ops dw_mipi_csi2rx_internal_ops = {
|
||||
+ .init_state = dw_mipi_csi2rx_init_state,
|
||||
+};
|
||||
+
|
||||
+static int dw_mipi_csi2rx_notifier_bound(struct v4l2_async_notifier *notifier,
|
||||
+ struct v4l2_subdev *sd,
|
||||
+ struct v4l2_async_connection *asd)
|
||||
+{
|
||||
+ struct dw_mipi_csi2rx_device *csi2 =
|
||||
+ container_of(notifier, struct dw_mipi_csi2rx_device, notifier);
|
||||
+ struct media_pad *sink_pad = &csi2->pads[DW_MIPI_CSI2RX_PAD_SINK];
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = v4l2_create_fwnode_links_to_pad(sd, sink_pad,
|
||||
+ MEDIA_LNK_FL_ENABLED);
|
||||
+ if (ret) {
|
||||
+ dev_err(csi2->dev, "failed to link source pad of %s\n",
|
||||
+ sd->name);
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct v4l2_async_notifier_operations dw_mipi_csi2rx_notifier_ops = {
|
||||
+ .bound = dw_mipi_csi2rx_notifier_bound,
|
||||
+};
|
||||
+
|
||||
+static int dw_mipi_csi2rx_register_notifier(struct dw_mipi_csi2rx_device *csi2)
|
||||
+{
|
||||
+ struct v4l2_async_connection *asd;
|
||||
+ struct v4l2_async_notifier *ntf = &csi2->notifier;
|
||||
+ struct v4l2_fwnode_endpoint vep;
|
||||
+ struct v4l2_subdev *sd = &csi2->sd;
|
||||
+ struct device *dev = csi2->dev;
|
||||
+ int ret;
|
||||
+
|
||||
+ struct fwnode_handle *ep __free(fwnode_handle) =
|
||||
+ fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0);
|
||||
+ if (!ep)
|
||||
+ return dev_err_probe(dev, -ENODEV, "failed to get endpoint\n");
|
||||
+
|
||||
+ vep.bus_type = V4L2_MBUS_UNKNOWN;
|
||||
+ ret = v4l2_fwnode_endpoint_parse(ep, &vep);
|
||||
+ if (ret)
|
||||
+ return dev_err_probe(dev, ret, "failed to parse endpoint\n");
|
||||
+
|
||||
+ if (vep.bus_type != V4L2_MBUS_CSI2_DPHY &&
|
||||
+ vep.bus_type != V4L2_MBUS_CSI2_CPHY)
|
||||
+ return dev_err_probe(dev, -EINVAL,
|
||||
+ "invalid bus type of endpoint\n");
|
||||
+
|
||||
+ csi2->bus_type = vep.bus_type;
|
||||
+ csi2->lanes_num = vep.bus.mipi_csi2.num_data_lanes;
|
||||
+
|
||||
+ v4l2_async_subdev_nf_init(ntf, sd);
|
||||
+ ntf->ops = &dw_mipi_csi2rx_notifier_ops;
|
||||
+
|
||||
+ asd = v4l2_async_nf_add_fwnode_remote(ntf, ep,
|
||||
+ struct v4l2_async_connection);
|
||||
+ if (IS_ERR(asd)) {
|
||||
+ ret = PTR_ERR(asd);
|
||||
+ goto err_nf_cleanup;
|
||||
+ }
|
||||
+
|
||||
+ ret = v4l2_async_nf_register(ntf);
|
||||
+ if (ret) {
|
||||
+ ret = dev_err_probe(dev, ret, "failed to register notifier\n");
|
||||
+ goto err_nf_cleanup;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+err_nf_cleanup:
|
||||
+ v4l2_async_nf_cleanup(ntf);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int dw_mipi_csi2rx_register(struct dw_mipi_csi2rx_device *csi2)
|
||||
+{
|
||||
+ struct media_pad *pads = csi2->pads;
|
||||
+ struct v4l2_subdev *sd = &csi2->sd;
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = dw_mipi_csi2rx_register_notifier(csi2);
|
||||
+ if (ret)
|
||||
+ goto err;
|
||||
+
|
||||
+ v4l2_subdev_init(sd, &dw_mipi_csi2rx_ops);
|
||||
+ sd->dev = csi2->dev;
|
||||
+ sd->entity.ops = &dw_mipi_csi2rx_media_ops;
|
||||
+ sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
|
||||
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;
|
||||
+ sd->internal_ops = &dw_mipi_csi2rx_internal_ops;
|
||||
+ snprintf(sd->name, sizeof(sd->name), "dw-mipi-csi2rx %s",
|
||||
+ dev_name(csi2->dev));
|
||||
+
|
||||
+ pads[DW_MIPI_CSI2RX_PAD_SINK].flags = MEDIA_PAD_FL_SINK |
|
||||
+ MEDIA_PAD_FL_MUST_CONNECT;
|
||||
+ pads[DW_MIPI_CSI2RX_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
|
||||
+ ret = media_entity_pads_init(&sd->entity, DW_MIPI_CSI2RX_PAD_MAX, pads);
|
||||
+ if (ret)
|
||||
+ goto err_notifier_unregister;
|
||||
+
|
||||
+ ret = v4l2_subdev_init_finalize(sd);
|
||||
+ if (ret)
|
||||
+ goto err_entity_cleanup;
|
||||
+
|
||||
+ ret = v4l2_async_register_subdev(sd);
|
||||
+ if (ret) {
|
||||
+ dev_err(sd->dev, "failed to register CSI-2 subdev\n");
|
||||
+ goto err_subdev_cleanup;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+err_subdev_cleanup:
|
||||
+ v4l2_subdev_cleanup(sd);
|
||||
+err_entity_cleanup:
|
||||
+ media_entity_cleanup(&sd->entity);
|
||||
+err_notifier_unregister:
|
||||
+ v4l2_async_nf_unregister(&csi2->notifier);
|
||||
+ v4l2_async_nf_cleanup(&csi2->notifier);
|
||||
+err:
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static void dw_mipi_csi2rx_unregister(struct dw_mipi_csi2rx_device *csi2)
|
||||
+{
|
||||
+ struct v4l2_subdev *sd = &csi2->sd;
|
||||
+
|
||||
+ v4l2_async_unregister_subdev(sd);
|
||||
+ v4l2_subdev_cleanup(sd);
|
||||
+ media_entity_cleanup(&sd->entity);
|
||||
+ v4l2_async_nf_unregister(&csi2->notifier);
|
||||
+ v4l2_async_nf_cleanup(&csi2->notifier);
|
||||
+}
|
||||
+
|
||||
+static const struct of_device_id dw_mipi_csi2rx_of_match[] = {
|
||||
+ {
|
||||
+ .compatible = "rockchip,rk3568-mipi-csi2",
|
||||
+ },
|
||||
+ {}
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, dw_mipi_csi2rx_of_match);
|
||||
+
|
||||
+static int dw_mipi_csi2rx_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct device *dev = &pdev->dev;
|
||||
+ struct dw_mipi_csi2rx_device *csi2;
|
||||
+ int ret;
|
||||
+
|
||||
+ csi2 = devm_kzalloc(dev, sizeof(*csi2), GFP_KERNEL);
|
||||
+ if (!csi2)
|
||||
+ return -ENOMEM;
|
||||
+ csi2->dev = dev;
|
||||
+ dev_set_drvdata(dev, csi2);
|
||||
+
|
||||
+ csi2->base_addr = devm_platform_ioremap_resource(pdev, 0);
|
||||
+ if (IS_ERR(csi2->base_addr))
|
||||
+ return PTR_ERR(csi2->base_addr);
|
||||
+
|
||||
+ ret = devm_clk_bulk_get_all(dev, &csi2->clks);
|
||||
+ if (ret != DW_MIPI_CSI2RX_CLKS_MAX)
|
||||
+ return dev_err_probe(dev, -ENODEV, "failed to get clocks\n");
|
||||
+ csi2->clks_num = ret;
|
||||
+
|
||||
+ csi2->phy = devm_phy_get(dev, NULL);
|
||||
+ if (IS_ERR(csi2->phy))
|
||||
+ return dev_err_probe(dev, PTR_ERR(csi2->phy),
|
||||
+ "failed to get MIPI CSI-2 PHY\n");
|
||||
+
|
||||
+ csi2->reset = devm_reset_control_get_exclusive(dev, NULL);
|
||||
+ if (IS_ERR(csi2->reset))
|
||||
+ return dev_err_probe(dev, PTR_ERR(csi2->reset),
|
||||
+ "failed to get reset\n");
|
||||
+
|
||||
+ csi2->formats = formats;
|
||||
+ csi2->formats_num = ARRAY_SIZE(formats);
|
||||
+
|
||||
+ ret = devm_pm_runtime_enable(dev);
|
||||
+ if (ret)
|
||||
+ return dev_err_probe(dev, ret, "failed to enable pm runtime\n");
|
||||
+
|
||||
+ ret = phy_init(csi2->phy);
|
||||
+ if (ret)
|
||||
+ return dev_err_probe(dev, ret,
|
||||
+ "failed to initialize MIPI CSI-2 PHY\n");
|
||||
+
|
||||
+ ret = dw_mipi_csi2rx_register(csi2);
|
||||
+ if (ret)
|
||||
+ goto err_phy_exit;
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+err_phy_exit:
|
||||
+ phy_exit(csi2->phy);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static void dw_mipi_csi2rx_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct dw_mipi_csi2rx_device *csi2 = platform_get_drvdata(pdev);
|
||||
+
|
||||
+ dw_mipi_csi2rx_unregister(csi2);
|
||||
+ phy_exit(csi2->phy);
|
||||
+}
|
||||
+
|
||||
+static int dw_mipi_csi2rx_runtime_suspend(struct device *dev)
|
||||
+{
|
||||
+ struct dw_mipi_csi2rx_device *csi2 = dev_get_drvdata(dev);
|
||||
+
|
||||
+ clk_bulk_disable_unprepare(csi2->clks_num, csi2->clks);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int dw_mipi_csi2rx_runtime_resume(struct device *dev)
|
||||
+{
|
||||
+ struct dw_mipi_csi2rx_device *csi2 = dev_get_drvdata(dev);
|
||||
+ int ret;
|
||||
+
|
||||
+ reset_control_assert(csi2->reset);
|
||||
+ udelay(5);
|
||||
+ reset_control_deassert(csi2->reset);
|
||||
+
|
||||
+ ret = clk_bulk_prepare_enable(csi2->clks_num, csi2->clks);
|
||||
+ if (ret) {
|
||||
+ dev_err(dev, "failed to enable clocks\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static DEFINE_RUNTIME_DEV_PM_OPS(dw_mipi_csi2rx_pm_ops,
|
||||
+ dw_mipi_csi2rx_runtime_suspend,
|
||||
+ dw_mipi_csi2rx_runtime_resume, NULL);
|
||||
+
|
||||
+static struct platform_driver dw_mipi_csi2rx_drv = {
|
||||
+ .driver = {
|
||||
+ .name = "dw-mipi-csi2rx",
|
||||
+ .of_match_table = dw_mipi_csi2rx_of_match,
|
||||
+ .pm = pm_ptr(&dw_mipi_csi2rx_pm_ops),
|
||||
+ },
|
||||
+ .probe = dw_mipi_csi2rx_probe,
|
||||
+ .remove = dw_mipi_csi2rx_remove,
|
||||
+};
|
||||
+module_platform_driver(dw_mipi_csi2rx_drv);
|
||||
+
|
||||
+MODULE_DESCRIPTION("Synopsys DesignWare MIPI CSI-2 Receiver platform driver");
|
||||
+MODULE_LICENSE("GPL");
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
From 9232fa05921bc0ea0bc71947440ec9a50b3ad26e Mon Sep 17 00:00:00 2001
|
||||
From: Michael Riesch <michael.riesch@collabora.com>
|
||||
Date: Mon, 16 Feb 2026 13:28:47 +0100
|
||||
Subject: [PATCH] media: synopsys: csi2rx: fix out-of-bounds check for formats
|
||||
array
|
||||
|
||||
The out-of-bounds check for the format array is off by one. Fix the
|
||||
check.
|
||||
|
||||
Fixes: 355a11004066 ("media: synopsys: add driver for the designware mipi csi-2 receiver")
|
||||
Cc: stable@kernel.org
|
||||
Suggested-by: Dan Carpenter <dan.carpenter@linaro.org>
|
||||
Signed-off-by: Michael Riesch <michael.riesch@collabora.com>
|
||||
Reviewed-by: Frank Li <Frank.Li@nxp.com>
|
||||
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
|
||||
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
|
||||
---
|
||||
drivers/media/platform/synopsys/dw-mipi-csi2rx.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
|
||||
+++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
|
||||
@@ -301,7 +301,7 @@ dw_mipi_csi2rx_enum_mbus_code(struct v4l
|
||||
|
||||
return 0;
|
||||
case DW_MIPI_CSI2RX_PAD_SINK:
|
||||
- if (code->index > csi2->formats_num)
|
||||
+ if (code->index >= csi2->formats_num)
|
||||
return -EINVAL;
|
||||
|
||||
code->code = csi2->formats[code->index].code;
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
From 6c03c1c4e8a4d9873432134fb7d22f3c02df7226 Mon Sep 17 00:00:00 2001
|
||||
From: Frank Li <Frank.Li@nxp.com>
|
||||
Date: Mon, 16 Feb 2026 14:18:39 -0500
|
||||
Subject: [PATCH] media: synopsys: csi2rx: use
|
||||
devm_reset_control_get_optional_exclusive()
|
||||
|
||||
The DW MIPI CSI-2 RX is used on different SoCs, not all of which provide a
|
||||
reset controller. Switch to devm_reset_control_get_optional_exclusive()
|
||||
to support such platforms.
|
||||
|
||||
Reset presence and numbering are validated by the DT binding.
|
||||
|
||||
Reviewed-by: Michael Riesch <michael.riesch@collabora.com>
|
||||
Signed-off-by: Frank Li <Frank.Li@nxp.com>
|
||||
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
|
||||
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
|
||||
---
|
||||
drivers/media/platform/synopsys/dw-mipi-csi2rx.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
|
||||
+++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
|
||||
@@ -639,7 +639,7 @@ static int dw_mipi_csi2rx_probe(struct p
|
||||
return dev_err_probe(dev, PTR_ERR(csi2->phy),
|
||||
"failed to get MIPI CSI-2 PHY\n");
|
||||
|
||||
- csi2->reset = devm_reset_control_get_exclusive(dev, NULL);
|
||||
+ csi2->reset = devm_reset_control_get_optional_exclusive(dev, NULL);
|
||||
if (IS_ERR(csi2->reset))
|
||||
return dev_err_probe(dev, PTR_ERR(csi2->reset),
|
||||
"failed to get reset\n");
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
From 6fb44d14929036ca4d10ed5d1df47af8a6b642b6 Mon Sep 17 00:00:00 2001
|
||||
From: Frank Li <Frank.Li@nxp.com>
|
||||
Date: Mon, 16 Feb 2026 14:18:40 -0500
|
||||
Subject: [PATCH] media: synopsys: csi2rx: only check errors from
|
||||
devm_clk_bulk_get_all()
|
||||
|
||||
devm_clk_bulk_get_all() returns all clocks described in the DT, which are
|
||||
already validated by the binding. Do not need enforce an expected clock
|
||||
count.
|
||||
|
||||
Only check for error returns (< 0) to support more SoCs.
|
||||
|
||||
Reviewed-by: Michael Riesch <michael.riesch@collabora.com>
|
||||
Signed-off-by: Frank Li <Frank.Li@nxp.com>
|
||||
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
|
||||
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
|
||||
---
|
||||
drivers/media/platform/synopsys/dw-mipi-csi2rx.c | 4 +---
|
||||
1 file changed, 1 insertion(+), 3 deletions(-)
|
||||
|
||||
--- a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
|
||||
+++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
|
||||
@@ -40,8 +40,6 @@
|
||||
#define SW_DATATYPE_LS(x) ((x) << 20)
|
||||
#define SW_DATATYPE_LE(x) ((x) << 26)
|
||||
|
||||
-#define DW_MIPI_CSI2RX_CLKS_MAX 1
|
||||
-
|
||||
enum {
|
||||
DW_MIPI_CSI2RX_PAD_SINK,
|
||||
DW_MIPI_CSI2RX_PAD_SRC,
|
||||
@@ -630,7 +628,7 @@ static int dw_mipi_csi2rx_probe(struct p
|
||||
return PTR_ERR(csi2->base_addr);
|
||||
|
||||
ret = devm_clk_bulk_get_all(dev, &csi2->clks);
|
||||
- if (ret != DW_MIPI_CSI2RX_CLKS_MAX)
|
||||
+ if (ret < 0)
|
||||
return dev_err_probe(dev, -ENODEV, "failed to get clocks\n");
|
||||
csi2->clks_num = ret;
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
From 6f4699ae1e012f807af73617405fcc755d1343ad Mon Sep 17 00:00:00 2001
|
||||
From: Frank Li <Frank.Li@nxp.com>
|
||||
Date: Mon, 16 Feb 2026 14:18:41 -0500
|
||||
Subject: [PATCH] media: synopsys: csi2rx: implement .get_frame_desc() callback
|
||||
|
||||
Implement the .get_frame_desc() callback to fetch information from the
|
||||
remote endpoint.
|
||||
|
||||
Signed-off-by: Frank Li <Frank.Li@nxp.com>
|
||||
Reviewed-by: Michael Riesch <michael.riesch@collabora.com>
|
||||
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
|
||||
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
|
||||
---
|
||||
.../media/platform/synopsys/dw-mipi-csi2rx.c | 21 +++++++++++++++++++
|
||||
1 file changed, 21 insertions(+)
|
||||
|
||||
--- a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
|
||||
+++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
|
||||
@@ -429,10 +429,31 @@ static int dw_mipi_csi2rx_disable_stream
|
||||
return ret;
|
||||
}
|
||||
|
||||
+static int
|
||||
+dw_mipi_csi2rx_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
|
||||
+ struct v4l2_mbus_frame_desc *fd)
|
||||
+{
|
||||
+ struct dw_mipi_csi2rx_device *csi2 = to_csi2(sd);
|
||||
+ struct v4l2_subdev *remote_sd;
|
||||
+ struct media_pad *remote_pad;
|
||||
+
|
||||
+ remote_pad = media_pad_remote_pad_unique(&csi2->pads[DW_MIPI_CSI2RX_PAD_SINK]);
|
||||
+ if (IS_ERR(remote_pad)) {
|
||||
+ dev_err(csi2->dev, "can't get remote source pad\n");
|
||||
+ return PTR_ERR(remote_pad);
|
||||
+ }
|
||||
+
|
||||
+ remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
|
||||
+
|
||||
+ return v4l2_subdev_call(remote_sd, pad, get_frame_desc,
|
||||
+ remote_pad->index, fd);
|
||||
+}
|
||||
+
|
||||
static const struct v4l2_subdev_pad_ops dw_mipi_csi2rx_pad_ops = {
|
||||
.enum_mbus_code = dw_mipi_csi2rx_enum_mbus_code,
|
||||
.get_fmt = v4l2_subdev_get_fmt,
|
||||
.set_fmt = dw_mipi_csi2rx_set_fmt,
|
||||
+ .get_frame_desc = dw_mipi_csi2rx_get_frame_desc,
|
||||
.set_routing = dw_mipi_csi2rx_set_routing,
|
||||
.enable_streams = dw_mipi_csi2rx_enable_streams,
|
||||
.disable_streams = dw_mipi_csi2rx_disable_streams,
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
From 2b301f534883ee1eed1a78a5d43e49ead72c2a0c Mon Sep 17 00:00:00 2001
|
||||
From: Frank Li <Frank.Li@nxp.com>
|
||||
Date: Mon, 16 Feb 2026 14:18:42 -0500
|
||||
Subject: [PATCH] media: synopsys: csi2rx: Use enum and u32 array for register
|
||||
offsets
|
||||
|
||||
Use enum dw_mipi_csi2rx_regs_index together with a u32 array to describe
|
||||
register offsets. This allows supporting new IP versions with different
|
||||
register layouts in a structured way.
|
||||
|
||||
Add rk3568_regs matching the previous macro definitions and pass it as
|
||||
driver data during probe.
|
||||
|
||||
No functional change intended.
|
||||
|
||||
Reviewed-by: Michael Riesch <michael.riesch@collabora.com>
|
||||
Signed-off-by: Frank Li <Frank.Li@nxp.com>
|
||||
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
|
||||
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
|
||||
---
|
||||
.../media/platform/synopsys/dw-mipi-csi2rx.c | 94 ++++++++++++++++---
|
||||
1 file changed, 81 insertions(+), 13 deletions(-)
|
||||
|
||||
--- a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
|
||||
+++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
|
||||
@@ -24,15 +24,6 @@
|
||||
#include <media/v4l2-mc.h>
|
||||
#include <media/v4l2-subdev.h>
|
||||
|
||||
-#define DW_MIPI_CSI2RX_N_LANES 0x04
|
||||
-#define DW_MIPI_CSI2RX_RESETN 0x10
|
||||
-#define DW_MIPI_CSI2RX_PHY_STATE 0x14
|
||||
-#define DW_MIPI_CSI2RX_ERR1 0x20
|
||||
-#define DW_MIPI_CSI2RX_ERR2 0x24
|
||||
-#define DW_MIPI_CSI2RX_MSK1 0x28
|
||||
-#define DW_MIPI_CSI2RX_MSK2 0x2c
|
||||
-#define DW_MIPI_CSI2RX_CONTROL 0x40
|
||||
-
|
||||
#define SW_CPHY_EN(x) ((x) << 0)
|
||||
#define SW_DSI_EN(x) ((x) << 4)
|
||||
#define SW_DATATYPE_FS(x) ((x) << 8)
|
||||
@@ -40,12 +31,32 @@
|
||||
#define SW_DATATYPE_LS(x) ((x) << 20)
|
||||
#define SW_DATATYPE_LE(x) ((x) << 26)
|
||||
|
||||
+#define DW_REG_EXIST BIT(31)
|
||||
+#define DW_REG(x) (DW_REG_EXIST | (x))
|
||||
+
|
||||
+enum dw_mipi_csi2rx_regs_index {
|
||||
+ DW_MIPI_CSI2RX_N_LANES,
|
||||
+ DW_MIPI_CSI2RX_RESETN,
|
||||
+ DW_MIPI_CSI2RX_PHY_STATE,
|
||||
+ DW_MIPI_CSI2RX_ERR1,
|
||||
+ DW_MIPI_CSI2RX_ERR2,
|
||||
+ DW_MIPI_CSI2RX_MSK1,
|
||||
+ DW_MIPI_CSI2RX_MSK2,
|
||||
+ DW_MIPI_CSI2RX_CONTROL,
|
||||
+
|
||||
+ DW_MIPI_CSI2RX_MAX,
|
||||
+};
|
||||
+
|
||||
enum {
|
||||
DW_MIPI_CSI2RX_PAD_SINK,
|
||||
DW_MIPI_CSI2RX_PAD_SRC,
|
||||
DW_MIPI_CSI2RX_PAD_MAX,
|
||||
};
|
||||
|
||||
+struct dw_mipi_csi2rx_drvdata {
|
||||
+ const u32 *regs;
|
||||
+};
|
||||
+
|
||||
struct dw_mipi_csi2rx_format {
|
||||
u32 code;
|
||||
u8 depth;
|
||||
@@ -70,6 +81,23 @@ struct dw_mipi_csi2rx_device {
|
||||
|
||||
enum v4l2_mbus_type bus_type;
|
||||
u32 lanes_num;
|
||||
+
|
||||
+ const struct dw_mipi_csi2rx_drvdata *drvdata;
|
||||
+};
|
||||
+
|
||||
+static const u32 rk3568_regs[DW_MIPI_CSI2RX_MAX] = {
|
||||
+ [DW_MIPI_CSI2RX_N_LANES] = DW_REG(0x4),
|
||||
+ [DW_MIPI_CSI2RX_RESETN] = DW_REG(0x10),
|
||||
+ [DW_MIPI_CSI2RX_PHY_STATE] = DW_REG(0x14),
|
||||
+ [DW_MIPI_CSI2RX_ERR1] = DW_REG(0x20),
|
||||
+ [DW_MIPI_CSI2RX_ERR2] = DW_REG(0x24),
|
||||
+ [DW_MIPI_CSI2RX_MSK1] = DW_REG(0x28),
|
||||
+ [DW_MIPI_CSI2RX_MSK2] = DW_REG(0x2c),
|
||||
+ [DW_MIPI_CSI2RX_CONTROL] = DW_REG(0x40),
|
||||
+};
|
||||
+
|
||||
+static const struct dw_mipi_csi2rx_drvdata rk3568_drvdata = {
|
||||
+ .regs = rk3568_regs,
|
||||
};
|
||||
|
||||
static const struct v4l2_mbus_framefmt default_format = {
|
||||
@@ -184,16 +212,50 @@ static inline struct dw_mipi_csi2rx_devi
|
||||
return container_of(sd, struct dw_mipi_csi2rx_device, sd);
|
||||
}
|
||||
|
||||
+static bool dw_mipi_csi2rx_has_reg(struct dw_mipi_csi2rx_device *csi2,
|
||||
+ enum dw_mipi_csi2rx_regs_index index)
|
||||
+{
|
||||
+ if (index < DW_MIPI_CSI2RX_MAX &&
|
||||
+ (csi2->drvdata->regs[index] & DW_REG_EXIST))
|
||||
+ return true;
|
||||
+
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
+static void __iomem *
|
||||
+dw_mipi_csi2rx_get_regaddr(struct dw_mipi_csi2rx_device *csi2,
|
||||
+ enum dw_mipi_csi2rx_regs_index index)
|
||||
+{
|
||||
+ u32 off = (~DW_REG_EXIST) & csi2->drvdata->regs[index];
|
||||
+
|
||||
+ return csi2->base_addr + off;
|
||||
+}
|
||||
+
|
||||
static inline void dw_mipi_csi2rx_write(struct dw_mipi_csi2rx_device *csi2,
|
||||
- unsigned int addr, u32 val)
|
||||
+ enum dw_mipi_csi2rx_regs_index index,
|
||||
+ u32 val)
|
||||
{
|
||||
- writel(val, csi2->base_addr + addr);
|
||||
+ if (!dw_mipi_csi2rx_has_reg(csi2, index)) {
|
||||
+ dev_err_once(csi2->dev,
|
||||
+ "write to non-existent register index: %d\n",
|
||||
+ index);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ writel(val, dw_mipi_csi2rx_get_regaddr(csi2, index));
|
||||
}
|
||||
|
||||
static inline u32 dw_mipi_csi2rx_read(struct dw_mipi_csi2rx_device *csi2,
|
||||
- unsigned int addr)
|
||||
+ enum dw_mipi_csi2rx_regs_index index)
|
||||
{
|
||||
- return readl(csi2->base_addr + addr);
|
||||
+ if (!dw_mipi_csi2rx_has_reg(csi2, index)) {
|
||||
+ dev_err_once(csi2->dev,
|
||||
+ "read non-existent register index: %d\n", index);
|
||||
+ /* return 0 for non-existent registers */
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ return readl(dw_mipi_csi2rx_get_regaddr(csi2, index));
|
||||
}
|
||||
|
||||
static const struct dw_mipi_csi2rx_format *
|
||||
@@ -627,6 +689,7 @@ static void dw_mipi_csi2rx_unregister(st
|
||||
static const struct of_device_id dw_mipi_csi2rx_of_match[] = {
|
||||
{
|
||||
.compatible = "rockchip,rk3568-mipi-csi2",
|
||||
+ .data = &rk3568_drvdata,
|
||||
},
|
||||
{}
|
||||
};
|
||||
@@ -648,6 +711,11 @@ static int dw_mipi_csi2rx_probe(struct p
|
||||
if (IS_ERR(csi2->base_addr))
|
||||
return PTR_ERR(csi2->base_addr);
|
||||
|
||||
+ csi2->drvdata = device_get_match_data(dev);
|
||||
+ if (!csi2->drvdata)
|
||||
+ return dev_err_probe(dev, -EINVAL,
|
||||
+ "failed to get driver data\n");
|
||||
+
|
||||
ret = devm_clk_bulk_get_all(dev, &csi2->clks);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, -ENODEV, "failed to get clocks\n");
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
From 7425b2eaf50a90f57cde586a4a7a0e2e9c2fc0d1 Mon Sep 17 00:00:00 2001
|
||||
From: Frank Li <Frank.Li@nxp.com>
|
||||
Date: Mon, 16 Feb 2026 14:18:43 -0500
|
||||
Subject: [PATCH] media: dt-bindings: add NXP i.MX93 compatible string
|
||||
|
||||
The i.MX93 uses the DW CSI-2 RX controller, which is similar to the
|
||||
Rockchip RK3568 implementation.
|
||||
|
||||
The i.MX93 variant provides one IRQ, two clocks, and no resets. Add the
|
||||
"fsl,imx93-mipi-csi2" compatible string and keep the same constraints for
|
||||
RK3568.
|
||||
|
||||
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
|
||||
Acked-by: Michael Riesch <michael.riesch@collabora.com>
|
||||
Signed-off-by: Frank Li <Frank.Li@nxp.com>
|
||||
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
|
||||
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
|
||||
---
|
||||
.../media/rockchip,rk3568-mipi-csi2.yaml | 47 ++++++++++++++++++-
|
||||
1 file changed, 45 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml
|
||||
+++ b/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml
|
||||
@@ -17,6 +17,7 @@ description:
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
+ - fsl,imx93-mipi-csi2
|
||||
- rockchip,rk3568-mipi-csi2
|
||||
|
||||
reg:
|
||||
@@ -26,14 +27,23 @@ properties:
|
||||
items:
|
||||
- description: Interrupt that signals changes in CSI2HOST_ERR1.
|
||||
- description: Interrupt that signals changes in CSI2HOST_ERR2.
|
||||
+ minItems: 1
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: err1
|
||||
- const: err2
|
||||
+ minItems: 1
|
||||
|
||||
clocks:
|
||||
- maxItems: 1
|
||||
+ minItems: 1
|
||||
+ maxItems: 2
|
||||
+
|
||||
+ clock-names:
|
||||
+ items:
|
||||
+ - const: per
|
||||
+ - const: pixel
|
||||
+ minItems: 1
|
||||
|
||||
phys:
|
||||
maxItems: 1
|
||||
@@ -88,10 +98,43 @@ required:
|
||||
- phys
|
||||
- ports
|
||||
- power-domains
|
||||
- - resets
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
+allOf:
|
||||
+ - if:
|
||||
+ properties:
|
||||
+ compatible:
|
||||
+ contains:
|
||||
+ const: rockchip,rk3568-mipi-csi2
|
||||
+ then:
|
||||
+ properties:
|
||||
+ interrupts:
|
||||
+ minItems: 2
|
||||
+ interrupt-names:
|
||||
+ minItems: 2
|
||||
+ clocks:
|
||||
+ maxItems: 1
|
||||
+ clock-names:
|
||||
+ maxItems: 1
|
||||
+ required:
|
||||
+ - resets
|
||||
+
|
||||
+ - if:
|
||||
+ properties:
|
||||
+ compatible:
|
||||
+ contains:
|
||||
+ const: fsl,imx93-mipi-csi2
|
||||
+ then:
|
||||
+ properties:
|
||||
+ interrupts:
|
||||
+ maxItems: 1
|
||||
+ interrupt-names: false
|
||||
+ clocks:
|
||||
+ minItems: 2
|
||||
+ clock-names:
|
||||
+ minItems: 2
|
||||
+
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/rk3568-cru.h>
|
||||
+251
@@ -0,0 +1,251 @@
|
||||
From ec40b431f0abe682db2e443b857d638cd3653330 Mon Sep 17 00:00:00 2001
|
||||
From: Frank Li <Frank.Li@nxp.com>
|
||||
Date: Mon, 16 Feb 2026 14:18:44 -0500
|
||||
Subject: [PATCH] media: synopsys: csi2rx: add i.MX93 support
|
||||
|
||||
The i.MX93 uses a newer version of the DW CSI-2 controller with a changed
|
||||
register layout and an integrated Image Pixel Interface (IPI), which
|
||||
converts the received CSI-2 packets from byte to pixel format and produces
|
||||
a pixel data bus containing vertical and horizontal synchronization
|
||||
information.
|
||||
|
||||
The reset flow also differs, so add the .assert_reset(), .deassert_reset(),
|
||||
and .idi_enable() callbacks to support it.
|
||||
|
||||
Reviewed-by: Michael Riesch <michael.riesch@collabora.com>
|
||||
Signed-off-by: Frank Li <Frank.Li@nxp.com>
|
||||
[Sakari Ailus: include missing linux/bitfield.h.]
|
||||
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
|
||||
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
|
||||
---
|
||||
.../media/platform/synopsys/dw-mipi-csi2rx.c | 160 +++++++++++++++++-
|
||||
1 file changed, 156 insertions(+), 4 deletions(-)
|
||||
|
||||
--- a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
|
||||
+++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
|
||||
@@ -7,6 +7,7 @@
|
||||
* Copyright (C) 2026 Collabora, Ltd.
|
||||
*/
|
||||
|
||||
+#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
@@ -34,6 +35,22 @@
|
||||
#define DW_REG_EXIST BIT(31)
|
||||
#define DW_REG(x) (DW_REG_EXIST | (x))
|
||||
|
||||
+#define DPHY_TEST_CTRL0_TEST_CLR BIT(0)
|
||||
+
|
||||
+#define IPI_VCID_VC(x) FIELD_PREP(GENMASK(1, 0), (x))
|
||||
+#define IPI_VCID_VC_0_1(x) FIELD_PREP(GENMASK(3, 2), (x))
|
||||
+#define IPI_VCID_VC_2 BIT(4)
|
||||
+
|
||||
+#define IPI_DATA_TYPE_DT(x) FIELD_PREP(GENMASK(5, 0), (x))
|
||||
+#define IPI_DATA_TYPE_EMB_DATA_EN BIT(8)
|
||||
+
|
||||
+#define IPI_MODE_CONTROLLER BIT(1)
|
||||
+#define IPI_MODE_COLOR_MODE16 BIT(8)
|
||||
+#define IPI_MODE_CUT_THROUGH BIT(16)
|
||||
+#define IPI_MODE_ENABLE BIT(24)
|
||||
+
|
||||
+#define IPI_MEM_FLUSH_AUTO BIT(8)
|
||||
+
|
||||
enum dw_mipi_csi2rx_regs_index {
|
||||
DW_MIPI_CSI2RX_N_LANES,
|
||||
DW_MIPI_CSI2RX_RESETN,
|
||||
@@ -43,6 +60,16 @@ enum dw_mipi_csi2rx_regs_index {
|
||||
DW_MIPI_CSI2RX_MSK1,
|
||||
DW_MIPI_CSI2RX_MSK2,
|
||||
DW_MIPI_CSI2RX_CONTROL,
|
||||
+ /* imx93 (v150) new register */
|
||||
+ DW_MIPI_CSI2RX_DPHY_RSTZ,
|
||||
+ DW_MIPI_CSI2RX_PHY_TST_CTRL0,
|
||||
+ DW_MIPI_CSI2RX_PHY_TST_CTRL1,
|
||||
+ DW_MIPI_CSI2RX_PHY_SHUTDOWNZ,
|
||||
+ DW_MIPI_CSI2RX_IPI_DATATYPE,
|
||||
+ DW_MIPI_CSI2RX_IPI_MEM_FLUSH,
|
||||
+ DW_MIPI_CSI2RX_IPI_MODE,
|
||||
+ DW_MIPI_CSI2RX_IPI_SOFTRSTN,
|
||||
+ DW_MIPI_CSI2RX_IPI_VCID,
|
||||
|
||||
DW_MIPI_CSI2RX_MAX,
|
||||
};
|
||||
@@ -53,8 +80,13 @@ enum {
|
||||
DW_MIPI_CSI2RX_PAD_MAX,
|
||||
};
|
||||
|
||||
+struct dw_mipi_csi2rx_device;
|
||||
+
|
||||
struct dw_mipi_csi2rx_drvdata {
|
||||
const u32 *regs;
|
||||
+ void (*dphy_assert_reset)(struct dw_mipi_csi2rx_device *csi2);
|
||||
+ void (*dphy_deassert_reset)(struct dw_mipi_csi2rx_device *csi2);
|
||||
+ void (*ipi_enable)(struct dw_mipi_csi2rx_device *csi2);
|
||||
};
|
||||
|
||||
struct dw_mipi_csi2rx_format {
|
||||
@@ -100,6 +132,21 @@ static const struct dw_mipi_csi2rx_drvda
|
||||
.regs = rk3568_regs,
|
||||
};
|
||||
|
||||
+static const u32 imx93_regs[DW_MIPI_CSI2RX_MAX] = {
|
||||
+ [DW_MIPI_CSI2RX_N_LANES] = DW_REG(0x4),
|
||||
+ [DW_MIPI_CSI2RX_RESETN] = DW_REG(0x8),
|
||||
+ [DW_MIPI_CSI2RX_PHY_SHUTDOWNZ] = DW_REG(0x40),
|
||||
+ [DW_MIPI_CSI2RX_DPHY_RSTZ] = DW_REG(0x44),
|
||||
+ [DW_MIPI_CSI2RX_PHY_STATE] = DW_REG(0x48),
|
||||
+ [DW_MIPI_CSI2RX_PHY_TST_CTRL0] = DW_REG(0x50),
|
||||
+ [DW_MIPI_CSI2RX_PHY_TST_CTRL1] = DW_REG(0x54),
|
||||
+ [DW_MIPI_CSI2RX_IPI_MODE] = DW_REG(0x80),
|
||||
+ [DW_MIPI_CSI2RX_IPI_VCID] = DW_REG(0x84),
|
||||
+ [DW_MIPI_CSI2RX_IPI_DATATYPE] = DW_REG(0x88),
|
||||
+ [DW_MIPI_CSI2RX_IPI_MEM_FLUSH] = DW_REG(0x8c),
|
||||
+ [DW_MIPI_CSI2RX_IPI_SOFTRSTN] = DW_REG(0xa0),
|
||||
+};
|
||||
+
|
||||
static const struct v4l2_mbus_framefmt default_format = {
|
||||
.width = 3840,
|
||||
.height = 2160,
|
||||
@@ -320,14 +367,32 @@ static int dw_mipi_csi2rx_start(struct d
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_RESETN, 0);
|
||||
+
|
||||
+ if (csi2->drvdata->dphy_assert_reset)
|
||||
+ csi2->drvdata->dphy_assert_reset(csi2);
|
||||
+
|
||||
control |= SW_DATATYPE_FS(0x00) | SW_DATATYPE_FE(0x01) |
|
||||
SW_DATATYPE_LS(0x02) | SW_DATATYPE_LE(0x03);
|
||||
|
||||
dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_N_LANES, lanes - 1);
|
||||
- dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_CONTROL, control);
|
||||
+
|
||||
+ if (dw_mipi_csi2rx_has_reg(csi2, DW_MIPI_CSI2RX_CONTROL))
|
||||
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_CONTROL, control);
|
||||
+
|
||||
+ ret = phy_power_on(csi2->phy);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ if (csi2->drvdata->dphy_deassert_reset)
|
||||
+ csi2->drvdata->dphy_deassert_reset(csi2);
|
||||
+
|
||||
dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_RESETN, 1);
|
||||
|
||||
- return phy_power_on(csi2->phy);
|
||||
+ if (csi2->drvdata->ipi_enable)
|
||||
+ csi2->drvdata->ipi_enable(csi2);
|
||||
+
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
static void dw_mipi_csi2rx_stop(struct dw_mipi_csi2rx_device *csi2)
|
||||
@@ -335,8 +400,12 @@ static void dw_mipi_csi2rx_stop(struct d
|
||||
phy_power_off(csi2->phy);
|
||||
|
||||
dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_RESETN, 0);
|
||||
- dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_MSK1, ~0);
|
||||
- dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_MSK2, ~0);
|
||||
+
|
||||
+ if (dw_mipi_csi2rx_has_reg(csi2, DW_MIPI_CSI2RX_MSK1))
|
||||
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_MSK1, ~0);
|
||||
+
|
||||
+ if (dw_mipi_csi2rx_has_reg(csi2, DW_MIPI_CSI2RX_MSK2))
|
||||
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_MSK2, ~0);
|
||||
}
|
||||
|
||||
static const struct media_entity_operations dw_mipi_csi2rx_media_ops = {
|
||||
@@ -686,8 +755,91 @@ static void dw_mipi_csi2rx_unregister(st
|
||||
v4l2_async_nf_cleanup(&csi2->notifier);
|
||||
}
|
||||
|
||||
+static void imx93_csi2rx_dphy_assert_reset(struct dw_mipi_csi2rx_device *csi2)
|
||||
+{
|
||||
+ u32 val;
|
||||
+
|
||||
+ /* Release Synopsys DPHY test codes from reset */
|
||||
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_DPHY_RSTZ, 0);
|
||||
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_PHY_SHUTDOWNZ, 0);
|
||||
+
|
||||
+ val = dw_mipi_csi2rx_read(csi2, DW_MIPI_CSI2RX_PHY_TST_CTRL0);
|
||||
+ val &= ~DPHY_TEST_CTRL0_TEST_CLR;
|
||||
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_PHY_TST_CTRL0, val);
|
||||
+
|
||||
+ val = dw_mipi_csi2rx_read(csi2, DW_MIPI_CSI2RX_PHY_TST_CTRL0);
|
||||
+ /* Wait for at least 15ns */
|
||||
+ ndelay(15);
|
||||
+ val |= DPHY_TEST_CTRL0_TEST_CLR;
|
||||
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_PHY_TST_CTRL0, val);
|
||||
+}
|
||||
+
|
||||
+static void imx93_csi2rx_dphy_deassert_reset(struct dw_mipi_csi2rx_device *csi2)
|
||||
+{
|
||||
+ /* Release PHY from reset */
|
||||
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_PHY_SHUTDOWNZ, 0x1);
|
||||
+ /*
|
||||
+ * ndelay() is not necessary have MMIO operation, need dummy read to
|
||||
+ * ensure that the write operation above reaches its target.
|
||||
+ */
|
||||
+ dw_mipi_csi2rx_read(csi2, DW_MIPI_CSI2RX_PHY_SHUTDOWNZ);
|
||||
+ ndelay(5);
|
||||
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_DPHY_RSTZ, 0x1);
|
||||
+
|
||||
+ dw_mipi_csi2rx_read(csi2, DW_MIPI_CSI2RX_DPHY_RSTZ);
|
||||
+ ndelay(5);
|
||||
+}
|
||||
+
|
||||
+static void imx93_csi2rx_dphy_ipi_enable(struct dw_mipi_csi2rx_device *csi2)
|
||||
+{
|
||||
+ int dt = csi2->formats->csi_dt;
|
||||
+ u32 val;
|
||||
+
|
||||
+ /* Do IPI soft reset */
|
||||
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_IPI_SOFTRSTN, 0x0);
|
||||
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_IPI_SOFTRSTN, 0x1);
|
||||
+
|
||||
+ /* Select virtual channel and data type to be processed by IPI */
|
||||
+ val = IPI_DATA_TYPE_DT(dt);
|
||||
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_IPI_DATATYPE, val);
|
||||
+
|
||||
+ /* Set virtual channel 0 as default */
|
||||
+ val = IPI_VCID_VC(0);
|
||||
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_IPI_VCID, val);
|
||||
+
|
||||
+ /*
|
||||
+ * Select IPI camera timing mode and allow the pixel stream
|
||||
+ * to be non-continuous when pixel interface FIFO is empty
|
||||
+ */
|
||||
+ val = dw_mipi_csi2rx_read(csi2, DW_MIPI_CSI2RX_IPI_MODE);
|
||||
+ val &= ~IPI_MODE_CONTROLLER;
|
||||
+ val &= ~IPI_MODE_COLOR_MODE16;
|
||||
+ val |= IPI_MODE_CUT_THROUGH;
|
||||
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_IPI_MODE, val);
|
||||
+
|
||||
+ /* Memory is automatically flushed at each Frame Start */
|
||||
+ val = IPI_MEM_FLUSH_AUTO;
|
||||
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_IPI_MEM_FLUSH, val);
|
||||
+
|
||||
+ /* Enable IPI */
|
||||
+ val = dw_mipi_csi2rx_read(csi2, DW_MIPI_CSI2RX_IPI_MODE);
|
||||
+ val |= IPI_MODE_ENABLE;
|
||||
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_IPI_MODE, val);
|
||||
+}
|
||||
+
|
||||
+static const struct dw_mipi_csi2rx_drvdata imx93_drvdata = {
|
||||
+ .regs = imx93_regs,
|
||||
+ .dphy_assert_reset = imx93_csi2rx_dphy_assert_reset,
|
||||
+ .dphy_deassert_reset = imx93_csi2rx_dphy_deassert_reset,
|
||||
+ .ipi_enable = imx93_csi2rx_dphy_ipi_enable,
|
||||
+};
|
||||
+
|
||||
static const struct of_device_id dw_mipi_csi2rx_of_match[] = {
|
||||
{
|
||||
+ .compatible = "fsl,imx93-mipi-csi2",
|
||||
+ .data = &imx93_drvdata,
|
||||
+ },
|
||||
+ {
|
||||
.compatible = "rockchip,rk3568-mipi-csi2",
|
||||
.data = &rk3568_drvdata,
|
||||
},
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
From e4056b84af0fc18c84b4e5741df04ecd8ca17973 Mon Sep 17 00:00:00 2001
|
||||
From: Dan Carpenter <dan.carpenter@linaro.org>
|
||||
Date: Fri, 20 Feb 2026 09:15:07 +0100
|
||||
Subject: [PATCH] media: rockchip: rkcif: fix off by one bugs
|
||||
|
||||
Change these comparisons from > vs >= to avoid accessing one element
|
||||
beyond the end of the arrays.
|
||||
While at it, use ARRAY_SIZE instead of the _MAX enum values.
|
||||
|
||||
Fixes: 1f2353f5a1af ("media: rockchip: rkcif: add support for rk3568 vicap mipi capture")
|
||||
Cc: stable@kernel.org
|
||||
Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
|
||||
Reviewed-by: Michael Riesch <michael.riesch@collabora.com>
|
||||
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
|
||||
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
|
||||
Tested-by: Chen-Yu Tsai <wens@kernel.org>
|
||||
[fix cosmetic issues]
|
||||
Signed-off-by: Michael Riesch <michael.riesch@collabora.com>
|
||||
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
|
||||
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
|
||||
---
|
||||
.../media/platform/rockchip/rkcif/rkcif-capture-mipi.c | 10 +++++-----
|
||||
1 file changed, 5 insertions(+), 5 deletions(-)
|
||||
|
||||
--- a/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c
|
||||
@@ -489,8 +489,8 @@ static inline unsigned int rkcif_mipi_ge
|
||||
|
||||
block = interface->index - RKCIF_MIPI_BASE;
|
||||
|
||||
- if (WARN_ON_ONCE(block > RKCIF_MIPI_MAX - RKCIF_MIPI_BASE) ||
|
||||
- WARN_ON_ONCE(index > RKCIF_MIPI_REGISTER_MAX))
|
||||
+ if (WARN_ON_ONCE(block >= ARRAY_SIZE(rkcif->match_data->mipi->blocks)) ||
|
||||
+ WARN_ON_ONCE(index >= ARRAY_SIZE(rkcif->match_data->mipi->regs)))
|
||||
return RKCIF_REGISTER_NOTSUPPORTED;
|
||||
|
||||
offset = rkcif->match_data->mipi->blocks[block].offset;
|
||||
@@ -510,9 +510,9 @@ static inline unsigned int rkcif_mipi_id
|
||||
block = stream->interface->index - RKCIF_MIPI_BASE;
|
||||
id = stream->id;
|
||||
|
||||
- if (WARN_ON_ONCE(block > RKCIF_MIPI_MAX - RKCIF_MIPI_BASE) ||
|
||||
- WARN_ON_ONCE(id > RKCIF_ID_MAX) ||
|
||||
- WARN_ON_ONCE(index > RKCIF_MIPI_ID_REGISTER_MAX))
|
||||
+ if (WARN_ON_ONCE(block >= ARRAY_SIZE(rkcif->match_data->mipi->blocks)) ||
|
||||
+ WARN_ON_ONCE(id >= ARRAY_SIZE(rkcif->match_data->mipi->regs_id)) ||
|
||||
+ WARN_ON_ONCE(index >= ARRAY_SIZE(rkcif->match_data->mipi->regs_id[id])))
|
||||
return RKCIF_REGISTER_NOTSUPPORTED;
|
||||
|
||||
offset = rkcif->match_data->mipi->blocks[block].offset;
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
From 48c8292d4445088d8b3c9d639c7982744a94d819 Mon Sep 17 00:00:00 2001
|
||||
From: Michael Riesch <michael.riesch@collabora.com>
|
||||
Date: Fri, 20 Feb 2026 09:15:08 +0100
|
||||
Subject: [PATCH] media: rockchip: rkcif: comply with minimum number of buffers
|
||||
requirement
|
||||
|
||||
Each stream requires CIF_REQ_BUFS_MIN=1 buffers to enable streaming.
|
||||
However, it failed with only one buffer provided.
|
||||
|
||||
Comply with the minimum number of buffers requirement and accept
|
||||
exactly one buffer.
|
||||
|
||||
Fixes: 501802e2ad51 ("media: rockchip: rkcif: add abstraction for dma blocks")
|
||||
Cc: stable@kernel.org
|
||||
Tested-by: Paul Elder <paul.elder@ideasonboard.com>
|
||||
Tested-by: Chen-Yu Tsai <wens@kernel.org>
|
||||
Signed-off-by: Michael Riesch <michael.riesch@collabora.com>
|
||||
Reviewed-by: Isaac Scott <isaac.scott@ideasonboard.com>
|
||||
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
|
||||
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
|
||||
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
|
||||
---
|
||||
.../platform/rockchip/rkcif/rkcif-stream.c | 44 +++++++++----------
|
||||
1 file changed, 22 insertions(+), 22 deletions(-)
|
||||
|
||||
--- a/drivers/media/platform/rockchip/rkcif/rkcif-stream.c
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-stream.c
|
||||
@@ -106,42 +106,42 @@ static int rkcif_stream_init_buffers(str
|
||||
{
|
||||
struct v4l2_pix_format_mplane *pix = &stream->pix;
|
||||
|
||||
- stream->buffers[0] = rkcif_stream_pop_buffer(stream);
|
||||
- if (!stream->buffers[0])
|
||||
- goto err_buff_0;
|
||||
-
|
||||
- stream->buffers[1] = rkcif_stream_pop_buffer(stream);
|
||||
- if (!stream->buffers[1])
|
||||
- goto err_buff_1;
|
||||
-
|
||||
- if (stream->queue_buffer) {
|
||||
- stream->queue_buffer(stream, 0);
|
||||
- stream->queue_buffer(stream, 1);
|
||||
- }
|
||||
-
|
||||
stream->dummy.size = pix->num_planes * pix->plane_fmt[0].sizeimage;
|
||||
stream->dummy.vaddr =
|
||||
dma_alloc_attrs(stream->rkcif->dev, stream->dummy.size,
|
||||
&stream->dummy.buffer.buff_addr[0], GFP_KERNEL,
|
||||
DMA_ATTR_NO_KERNEL_MAPPING);
|
||||
if (!stream->dummy.vaddr)
|
||||
- goto err_dummy;
|
||||
+ return -ENOMEM;
|
||||
|
||||
for (unsigned int i = 1; i < pix->num_planes; i++)
|
||||
stream->dummy.buffer.buff_addr[i] =
|
||||
stream->dummy.buffer.buff_addr[i - 1] +
|
||||
pix->plane_fmt[i - 1].bytesperline * pix->height;
|
||||
|
||||
+ stream->buffers[0] = rkcif_stream_pop_buffer(stream);
|
||||
+ if (!stream->buffers[0])
|
||||
+ goto err_dummy_free;
|
||||
+
|
||||
+ stream->buffers[1] = rkcif_stream_pop_buffer(stream);
|
||||
+ if (!stream->buffers[1]) {
|
||||
+ stream->buffers[1] = &stream->dummy.buffer;
|
||||
+ stream->buffers[1]->is_dummy = true;
|
||||
+ }
|
||||
+
|
||||
+ if (stream->queue_buffer) {
|
||||
+ stream->queue_buffer(stream, 0);
|
||||
+ stream->queue_buffer(stream, 1);
|
||||
+ }
|
||||
+
|
||||
return 0;
|
||||
|
||||
-err_dummy:
|
||||
- rkcif_stream_return_buffer(stream->buffers[1], VB2_BUF_STATE_QUEUED);
|
||||
- stream->buffers[1] = NULL;
|
||||
-
|
||||
-err_buff_1:
|
||||
- rkcif_stream_return_buffer(stream->buffers[0], VB2_BUF_STATE_QUEUED);
|
||||
- stream->buffers[0] = NULL;
|
||||
-err_buff_0:
|
||||
+err_dummy_free:
|
||||
+ dma_free_attrs(stream->rkcif->dev, stream->dummy.size,
|
||||
+ stream->dummy.vaddr,
|
||||
+ stream->dummy.buffer.buff_addr[0],
|
||||
+ DMA_ATTR_NO_KERNEL_MAPPING);
|
||||
+ stream->dummy.vaddr = NULL;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
From 8e3c751259dc2d1325838eff26f41032523c7b57 Mon Sep 17 00:00:00 2001
|
||||
From: Dang Huynh <dang.huynh@mainlining.org>
|
||||
Date: Thu, 29 Jan 2026 14:24:02 +0700
|
||||
Subject: [PATCH] media: rockchip: rkcif: Add missing MUST_CONNECT flag to pads
|
||||
|
||||
The pads missed checks for connected devices which may a null dereference
|
||||
when the stream is enabled.
|
||||
|
||||
Unable to handle kernel NULL pointer dereference at virtual address
|
||||
0000000000000020
|
||||
pc : rkcif_interface_enable_streams+0x48/0xf0
|
||||
lr : rkcif_interface_enable_streams+0x44/0xf0
|
||||
Call trace:
|
||||
rkcif_interface_enable_streams+0x48/0xf0
|
||||
v4l2_subdev_enable_streams+0x26c/0x3f0
|
||||
rkcif_stream_start_streaming+0x140/0x278
|
||||
vb2_start_streaming+0x74/0x188
|
||||
vb2_core_streamon+0xe0/0x1d8
|
||||
vb2_ioctl_streamon+0x60/0xa8
|
||||
v4l_streamon+0x2c/0x40
|
||||
__video_do_ioctl+0x34c/0x400
|
||||
video_usercopy+0x2d0/0x800
|
||||
video_ioctl2+0x20/0x60
|
||||
v4l2_ioctl+0x48/0x78
|
||||
|
||||
Fixes: 501802e2ad51 ("media: rockchip: rkcif: add abstraction for dma blocks")
|
||||
Fixes: 85411d17bee9 ("media: rockchip: rkcif: add abstraction for interface and crop blocks")
|
||||
Cc: stable@vger.kernel.org
|
||||
Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
|
||||
Reviewed-by: Michael Riesch <michael.riesch@collabora.com>
|
||||
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
|
||||
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
|
||||
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
|
||||
---
|
||||
drivers/media/platform/rockchip/rkcif/rkcif-interface.c | 3 ++-
|
||||
drivers/media/platform/rockchip/rkcif/rkcif-stream.c | 2 +-
|
||||
2 files changed, 3 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/media/platform/rockchip/rkcif/rkcif-interface.c
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-interface.c
|
||||
@@ -378,7 +378,8 @@ int rkcif_interface_register(struct rkci
|
||||
snprintf(sd->name, sizeof(sd->name), "rkcif-mipi%d",
|
||||
interface->index - RKCIF_MIPI_BASE);
|
||||
|
||||
- pads[RKCIF_IF_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
|
||||
+ pads[RKCIF_IF_PAD_SINK].flags = MEDIA_PAD_FL_SINK |
|
||||
+ MEDIA_PAD_FL_MUST_CONNECT;
|
||||
pads[RKCIF_IF_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
|
||||
ret = media_entity_pads_init(&sd->entity, RKCIF_IF_PAD_MAX, pads);
|
||||
if (ret)
|
||||
--- a/drivers/media/platform/rockchip/rkcif/rkcif-stream.c
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-stream.c
|
||||
@@ -555,7 +555,7 @@ int rkcif_stream_register(struct rkcif_d
|
||||
vdev->vfl_dir = VFL_DIR_RX;
|
||||
video_set_drvdata(vdev, stream);
|
||||
|
||||
- stream->pad.flags = MEDIA_PAD_FL_SINK;
|
||||
+ stream->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
|
||||
|
||||
stream->pix.height = CIF_MIN_HEIGHT;
|
||||
stream->pix.width = CIF_MIN_WIDTH;
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
From 8ba166ff7c921da610376156d7c0a5fc985fa983 Mon Sep 17 00:00:00 2001
|
||||
From: Guoniu Zhou <guoniu.zhou@oss.nxp.com>
|
||||
Date: Tue, 19 May 2026 10:07:38 +0800
|
||||
Subject: [PATCH] media: synopsys: Fix IPI using hardcoded datatype
|
||||
|
||||
The imx93_csi2rx_dphy_ipi_enable() function configures the IPI datatype
|
||||
using csi2->formats->csi_dt, which is initialized during probe but never
|
||||
updated in set_fmt(). This causes the IPI to always use the probe-time
|
||||
default datatype, ignoring the actual media bus format negotiated at
|
||||
runtime. When userspace requests a different format, the IPI hardware is
|
||||
configured with the wrong datatype, resulting in incorrect image output.
|
||||
|
||||
Fix by updating csi2->formats in the set_fmt callback to reflect the
|
||||
currently negotiated format, ensuring the IPI configuration matches the
|
||||
runtime datatype.
|
||||
|
||||
Fixes: ec40b431f0ab ("media: synopsys: csi2rx: add i.MX93 support")
|
||||
Reviewed-by: Frank Li <Frank.Li@nxp.com>
|
||||
Signed-off-by: Guoniu Zhou <guoniu.zhou@oss.nxp.com>
|
||||
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
|
||||
---
|
||||
drivers/media/platform/synopsys/dw-mipi-csi2rx.c | 15 +++++++++++++--
|
||||
1 file changed, 13 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
|
||||
+++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
|
||||
@@ -311,7 +311,7 @@ dw_mipi_csi2rx_find_format(struct dw_mip
|
||||
WARN_ON(csi2->formats_num == 0);
|
||||
|
||||
for (unsigned int i = 0; i < csi2->formats_num; i++) {
|
||||
- const struct dw_mipi_csi2rx_format *format = &csi2->formats[i];
|
||||
+ const struct dw_mipi_csi2rx_format *format = &formats[i];
|
||||
|
||||
if (format->code == mbus_code)
|
||||
return format;
|
||||
@@ -433,7 +433,7 @@ dw_mipi_csi2rx_enum_mbus_code(struct v4l
|
||||
if (code->index >= csi2->formats_num)
|
||||
return -EINVAL;
|
||||
|
||||
- code->code = csi2->formats[code->index].code;
|
||||
+ code->code = formats[code->index].code;
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@@ -470,6 +470,17 @@ static int dw_mipi_csi2rx_set_fmt(struct
|
||||
|
||||
*src = *sink;
|
||||
|
||||
+ /* Store the CSIS format descriptor for active formats. */
|
||||
+ if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
|
||||
+ csi2->formats = fmt ? :
|
||||
+ dw_mipi_csi2rx_find_format(csi2, default_format.code);
|
||||
+
|
||||
+ if (!csi2->formats) {
|
||||
+ dev_err(csi2->dev, "Failed to find valid format\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
From 4a7004c03d2227643c2ef2e546ad173b2b1480b7 Mon Sep 17 00:00:00 2001
|
||||
From: Guoniu Zhou <guoniu.zhou@oss.nxp.com>
|
||||
Date: Tue, 19 May 2026 10:07:39 +0800
|
||||
Subject: [PATCH] media: synopsys: Add support for RAW16 Bayer formats
|
||||
|
||||
Add higher bit-depth raw image data support for the sensors, which supports
|
||||
16-bit output.
|
||||
|
||||
Reviewed-by: Frank Li <Frank.Li@nxp.com>
|
||||
Signed-off-by: Guoniu Zhou <guoniu.zhou@oss.nxp.com>
|
||||
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
|
||||
---
|
||||
.../media/platform/synopsys/dw-mipi-csi2rx.c | 20 +++++++++++++++++++
|
||||
1 file changed, 20 insertions(+)
|
||||
|
||||
--- a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
|
||||
+++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
|
||||
@@ -252,6 +252,26 @@ static const struct dw_mipi_csi2rx_forma
|
||||
.depth = 12,
|
||||
.csi_dt = MIPI_CSI2_DT_RAW12,
|
||||
},
|
||||
+ {
|
||||
+ .code = MEDIA_BUS_FMT_SBGGR16_1X16,
|
||||
+ .depth = 16,
|
||||
+ .csi_dt = MIPI_CSI2_DT_RAW16,
|
||||
+ },
|
||||
+ {
|
||||
+ .code = MEDIA_BUS_FMT_SGBRG16_1X16,
|
||||
+ .depth = 16,
|
||||
+ .csi_dt = MIPI_CSI2_DT_RAW16,
|
||||
+ },
|
||||
+ {
|
||||
+ .code = MEDIA_BUS_FMT_SGRBG16_1X16,
|
||||
+ .depth = 16,
|
||||
+ .csi_dt = MIPI_CSI2_DT_RAW16,
|
||||
+ },
|
||||
+ {
|
||||
+ .code = MEDIA_BUS_FMT_SRGGB16_1X16,
|
||||
+ .depth = 16,
|
||||
+ .csi_dt = MIPI_CSI2_DT_RAW16,
|
||||
+ },
|
||||
};
|
||||
|
||||
static inline struct dw_mipi_csi2rx_device *to_csi2(struct v4l2_subdev *sd)
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
From 9823c6bd80082906e2b1d0ca03f470ff395b5557 Mon Sep 17 00:00:00 2001
|
||||
From: Guoniu Zhou <guoniu.zhou@oss.nxp.com>
|
||||
Date: Tue, 19 May 2026 10:07:40 +0800
|
||||
Subject: [PATCH] media: synopsys: Add support for multiple streams
|
||||
|
||||
The current driver only supports single stream operation. Add support
|
||||
for multiple concurrent streams by tracking enabled streams with a
|
||||
bitmask and only initializing the hardware once for the first stream.
|
||||
|
||||
This enables use cases such as surround view systems where multiple
|
||||
camera streams need to be processed simultaneously through the same
|
||||
CSI-2 receiver interface.
|
||||
|
||||
Reviewed-by: Frank Li <Frank.Li@nxp.com>
|
||||
Signed-off-by: Guoniu Zhou <guoniu.zhou@oss.nxp.com>
|
||||
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
|
||||
---
|
||||
.../media/platform/synopsys/dw-mipi-csi2rx.c | 35 +++++++++++++------
|
||||
1 file changed, 24 insertions(+), 11 deletions(-)
|
||||
|
||||
--- a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
|
||||
+++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
|
||||
@@ -113,6 +113,7 @@ struct dw_mipi_csi2rx_device {
|
||||
|
||||
enum v4l2_mbus_type bus_type;
|
||||
u32 lanes_num;
|
||||
+ u64 enabled_streams;
|
||||
|
||||
const struct dw_mipi_csi2rx_drvdata *drvdata;
|
||||
};
|
||||
@@ -539,26 +540,33 @@ static int dw_mipi_csi2rx_enable_streams
|
||||
DW_MIPI_CSI2RX_PAD_SRC,
|
||||
&streams_mask);
|
||||
|
||||
- ret = pm_runtime_resume_and_get(dev);
|
||||
- if (ret)
|
||||
- goto err;
|
||||
+ if (!csi2->enabled_streams) {
|
||||
+ ret = pm_runtime_resume_and_get(dev);
|
||||
+ if (ret)
|
||||
+ goto err;
|
||||
|
||||
- ret = dw_mipi_csi2rx_start(csi2);
|
||||
- if (ret) {
|
||||
- dev_err(dev, "failed to enable CSI hardware\n");
|
||||
- goto err_pm_runtime_put;
|
||||
+ ret = dw_mipi_csi2rx_start(csi2);
|
||||
+ if (ret) {
|
||||
+ dev_err(dev, "failed to enable CSI hardware\n");
|
||||
+ goto err_pm_runtime_put;
|
||||
+ }
|
||||
}
|
||||
|
||||
ret = v4l2_subdev_enable_streams(remote_sd, remote_pad->index, mask);
|
||||
if (ret)
|
||||
goto err_csi_stop;
|
||||
|
||||
+ csi2->enabled_streams |= streams_mask;
|
||||
+
|
||||
return 0;
|
||||
|
||||
err_csi_stop:
|
||||
- dw_mipi_csi2rx_stop(csi2);
|
||||
+ /* Stop CSI hardware if no streams are enabled */
|
||||
+ if (!csi2->enabled_streams)
|
||||
+ dw_mipi_csi2rx_stop(csi2);
|
||||
err_pm_runtime_put:
|
||||
- pm_runtime_put(dev);
|
||||
+ if (!csi2->enabled_streams)
|
||||
+ pm_runtime_put(dev);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
@@ -583,10 +591,15 @@ static int dw_mipi_csi2rx_disable_stream
|
||||
&streams_mask);
|
||||
|
||||
ret = v4l2_subdev_disable_streams(remote_sd, remote_pad->index, mask);
|
||||
+ if (ret)
|
||||
+ dev_err(dev, "failed to disable streams on remote subdev: %d\n", ret);
|
||||
|
||||
- dw_mipi_csi2rx_stop(csi2);
|
||||
+ csi2->enabled_streams &= ~streams_mask;
|
||||
|
||||
- pm_runtime_put(dev);
|
||||
+ if (!csi2->enabled_streams) {
|
||||
+ dw_mipi_csi2rx_stop(csi2);
|
||||
+ pm_runtime_put(dev);
|
||||
+ }
|
||||
|
||||
return ret;
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
From 82cdee9be284af7f3aa2b2ba1fa61c410642cac4 Mon Sep 17 00:00:00 2001
|
||||
From: Guoniu Zhou <guoniu.zhou@oss.nxp.com>
|
||||
Date: Tue, 19 May 2026 10:07:41 +0800
|
||||
Subject: [PATCH] media: synopsys: Add PHY stopstate wait for i.MX93
|
||||
|
||||
Implement waiting for D-PHY lanes to enter stop state on i.MX93. This
|
||||
ensures proper PHY initialization by verifying that the clock lane and
|
||||
all active data lanes have entered the stop state before proceeding with
|
||||
further operations.
|
||||
|
||||
Reviewed-by: Frank Li <Frank.Li@nxp.com>
|
||||
Signed-off-by: Guoniu Zhou <guoniu.zhou@oss.nxp.com>
|
||||
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
|
||||
---
|
||||
.../media/platform/synopsys/dw-mipi-csi2rx.c | 36 +++++++++++++++++++
|
||||
1 file changed, 36 insertions(+)
|
||||
|
||||
--- a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
|
||||
+++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
+#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/phy/phy.h>
|
||||
@@ -35,6 +36,8 @@
|
||||
#define DW_REG_EXIST BIT(31)
|
||||
#define DW_REG(x) (DW_REG_EXIST | (x))
|
||||
|
||||
+#define DPHY_STOPSTATE_CLK_LANE BIT(16)
|
||||
+
|
||||
#define DPHY_TEST_CTRL0_TEST_CLR BIT(0)
|
||||
|
||||
#define IPI_VCID_VC(x) FIELD_PREP(GENMASK(1, 0), (x))
|
||||
@@ -65,6 +68,7 @@ enum dw_mipi_csi2rx_regs_index {
|
||||
DW_MIPI_CSI2RX_PHY_TST_CTRL0,
|
||||
DW_MIPI_CSI2RX_PHY_TST_CTRL1,
|
||||
DW_MIPI_CSI2RX_PHY_SHUTDOWNZ,
|
||||
+ DW_MIPI_CSI2RX_PHY_STOPSTATE,
|
||||
DW_MIPI_CSI2RX_IPI_DATATYPE,
|
||||
DW_MIPI_CSI2RX_IPI_MEM_FLUSH,
|
||||
DW_MIPI_CSI2RX_IPI_MODE,
|
||||
@@ -87,6 +91,7 @@ struct dw_mipi_csi2rx_drvdata {
|
||||
void (*dphy_assert_reset)(struct dw_mipi_csi2rx_device *csi2);
|
||||
void (*dphy_deassert_reset)(struct dw_mipi_csi2rx_device *csi2);
|
||||
void (*ipi_enable)(struct dw_mipi_csi2rx_device *csi2);
|
||||
+ int (*wait_for_phy_stopstate)(struct dw_mipi_csi2rx_device *csi2);
|
||||
};
|
||||
|
||||
struct dw_mipi_csi2rx_format {
|
||||
@@ -139,6 +144,7 @@ static const u32 imx93_regs[DW_MIPI_CSI2
|
||||
[DW_MIPI_CSI2RX_PHY_SHUTDOWNZ] = DW_REG(0x40),
|
||||
[DW_MIPI_CSI2RX_DPHY_RSTZ] = DW_REG(0x44),
|
||||
[DW_MIPI_CSI2RX_PHY_STATE] = DW_REG(0x48),
|
||||
+ [DW_MIPI_CSI2RX_PHY_STOPSTATE] = DW_REG(0x4c),
|
||||
[DW_MIPI_CSI2RX_PHY_TST_CTRL0] = DW_REG(0x50),
|
||||
[DW_MIPI_CSI2RX_PHY_TST_CTRL1] = DW_REG(0x54),
|
||||
[DW_MIPI_CSI2RX_IPI_MODE] = DW_REG(0x80),
|
||||
@@ -556,10 +562,19 @@ static int dw_mipi_csi2rx_enable_streams
|
||||
if (ret)
|
||||
goto err_csi_stop;
|
||||
|
||||
+ if (!csi2->enabled_streams &&
|
||||
+ csi2->drvdata->wait_for_phy_stopstate) {
|
||||
+ ret = csi2->drvdata->wait_for_phy_stopstate(csi2);
|
||||
+ if (ret)
|
||||
+ goto err_disable_streams;
|
||||
+ }
|
||||
+
|
||||
csi2->enabled_streams |= streams_mask;
|
||||
|
||||
return 0;
|
||||
|
||||
+err_disable_streams:
|
||||
+ v4l2_subdev_disable_streams(remote_sd, remote_pad->index, mask);
|
||||
err_csi_stop:
|
||||
/* Stop CSI hardware if no streams are enabled */
|
||||
if (!csi2->enabled_streams)
|
||||
@@ -871,11 +886,32 @@ static void imx93_csi2rx_dphy_ipi_enable
|
||||
dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_IPI_MODE, val);
|
||||
}
|
||||
|
||||
+static int imx93_csi2rx_wait_for_phy_stopstate(struct dw_mipi_csi2rx_device *csi2)
|
||||
+{
|
||||
+ struct device *dev = csi2->dev;
|
||||
+ u32 stopstate_mask;
|
||||
+ u32 val;
|
||||
+ int ret;
|
||||
+
|
||||
+ stopstate_mask = DPHY_STOPSTATE_CLK_LANE | GENMASK(csi2->lanes_num - 1, 0);
|
||||
+
|
||||
+ ret = read_poll_timeout(dw_mipi_csi2rx_read, val,
|
||||
+ (val & stopstate_mask) == stopstate_mask,
|
||||
+ 10, 1000, true,
|
||||
+ csi2, DW_MIPI_CSI2RX_PHY_STOPSTATE);
|
||||
+ if (ret)
|
||||
+ dev_err(dev, "lanes are not in stop state: %#x, expected %#x\n",
|
||||
+ val, stopstate_mask);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
static const struct dw_mipi_csi2rx_drvdata imx93_drvdata = {
|
||||
.regs = imx93_regs,
|
||||
.dphy_assert_reset = imx93_csi2rx_dphy_assert_reset,
|
||||
.dphy_deassert_reset = imx93_csi2rx_dphy_deassert_reset,
|
||||
.ipi_enable = imx93_csi2rx_dphy_ipi_enable,
|
||||
+ .wait_for_phy_stopstate = imx93_csi2rx_wait_for_phy_stopstate,
|
||||
};
|
||||
|
||||
static const struct of_device_id dw_mipi_csi2rx_of_match[] = {
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
From af92c6d86b2bc3849935dc6ecfe78eba016a47a4 Mon Sep 17 00:00:00 2001
|
||||
From: Guoniu Zhou <guoniu.zhou@oss.nxp.com>
|
||||
Date: Tue, 19 May 2026 10:07:43 +0800
|
||||
Subject: [PATCH] media: synopsys: Add support for i.MX95
|
||||
|
||||
Add support for the i.MX95 MIPI CSI-2 receiver. The i.MX95 variant is
|
||||
nearly identical to i.MX93, with the main difference being the use of
|
||||
IDI (Image Data Interface) instead of IPI (Image Pixel Interface).
|
||||
However, the IDI interface is transparent to software, requiring only
|
||||
a different register map definition while sharing the same PHY control
|
||||
functions with i.MX93.
|
||||
|
||||
Reviewed-by: Frank Li <Frank.Li@nxp.com>
|
||||
Signed-off-by: Guoniu Zhou <guoniu.zhou@oss.nxp.com>
|
||||
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
|
||||
---
|
||||
.../media/platform/synopsys/dw-mipi-csi2rx.c | 22 +++++++++++++++++++
|
||||
1 file changed, 22 insertions(+)
|
||||
|
||||
--- a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
|
||||
+++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
|
||||
@@ -154,6 +154,17 @@ static const u32 imx93_regs[DW_MIPI_CSI2
|
||||
[DW_MIPI_CSI2RX_IPI_SOFTRSTN] = DW_REG(0xa0),
|
||||
};
|
||||
|
||||
+static const u32 imx95_regs[DW_MIPI_CSI2RX_MAX] = {
|
||||
+ [DW_MIPI_CSI2RX_N_LANES] = DW_REG(0x4),
|
||||
+ [DW_MIPI_CSI2RX_RESETN] = DW_REG(0x8),
|
||||
+ [DW_MIPI_CSI2RX_PHY_SHUTDOWNZ] = DW_REG(0x40),
|
||||
+ [DW_MIPI_CSI2RX_DPHY_RSTZ] = DW_REG(0x44),
|
||||
+ [DW_MIPI_CSI2RX_PHY_STATE] = DW_REG(0x48),
|
||||
+ [DW_MIPI_CSI2RX_PHY_STOPSTATE] = DW_REG(0x4c),
|
||||
+ [DW_MIPI_CSI2RX_PHY_TST_CTRL0] = DW_REG(0x50),
|
||||
+ [DW_MIPI_CSI2RX_PHY_TST_CTRL1] = DW_REG(0x54),
|
||||
+};
|
||||
+
|
||||
static const struct v4l2_mbus_framefmt default_format = {
|
||||
.width = 3840,
|
||||
.height = 2160,
|
||||
@@ -914,12 +925,23 @@ static const struct dw_mipi_csi2rx_drvda
|
||||
.wait_for_phy_stopstate = imx93_csi2rx_wait_for_phy_stopstate,
|
||||
};
|
||||
|
||||
+static const struct dw_mipi_csi2rx_drvdata imx95_drvdata = {
|
||||
+ .regs = imx95_regs,
|
||||
+ .dphy_assert_reset = imx93_csi2rx_dphy_assert_reset,
|
||||
+ .dphy_deassert_reset = imx93_csi2rx_dphy_deassert_reset,
|
||||
+ .wait_for_phy_stopstate = imx93_csi2rx_wait_for_phy_stopstate,
|
||||
+};
|
||||
+
|
||||
static const struct of_device_id dw_mipi_csi2rx_of_match[] = {
|
||||
{
|
||||
.compatible = "fsl,imx93-mipi-csi2",
|
||||
.data = &imx93_drvdata,
|
||||
},
|
||||
{
|
||||
+ .compatible = "fsl,imx95-mipi-csi2",
|
||||
+ .data = &imx95_drvdata,
|
||||
+ },
|
||||
+ {
|
||||
.compatible = "rockchip,rk3568-mipi-csi2",
|
||||
.data = &rk3568_drvdata,
|
||||
},
|
||||
+250
@@ -0,0 +1,250 @@
|
||||
From 4a6b7bf1de948251ca5f09ca1a632f036e52085c Mon Sep 17 00:00:00 2001
|
||||
From: Michael Riesch <michael.riesch@collabora.com>
|
||||
Date: Fri, 22 May 2026 23:23:08 +0200
|
||||
Subject: [PATCH] media: dt-bindings: add rockchip rk3588 vicap
|
||||
|
||||
Add documentation for the Rockchip RK3588 Video Capture (VICAP) unit. To
|
||||
that end, make the existing rockchip,rk3568-vicap documentation more
|
||||
general and introduce variant specific constraints.
|
||||
|
||||
Acked-by: Conor Dooley <conor.dooley@microchip.com>
|
||||
Signed-off-by: Michael Riesch <michael.riesch@collabora.com>
|
||||
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
|
||||
---
|
||||
.../bindings/media/rockchip,rk3568-vicap.yaml | 173 ++++++++++++++++--
|
||||
1 file changed, 154 insertions(+), 19 deletions(-)
|
||||
|
||||
--- a/Documentation/devicetree/bindings/media/rockchip,rk3568-vicap.yaml
|
||||
+++ b/Documentation/devicetree/bindings/media/rockchip,rk3568-vicap.yaml
|
||||
@@ -15,9 +15,15 @@ description:
|
||||
the data from camera sensors, video decoders, or other companion ICs and
|
||||
transfers it into system main memory by AXI bus.
|
||||
|
||||
+ The Rockchip RK3588 Video Capture (VICAP) is similar to its RK3568
|
||||
+ counterpart, but features six MIPI CSI-2 ports and additional connections
|
||||
+ to the image signal processor (ISP) blocks.
|
||||
+
|
||||
properties:
|
||||
compatible:
|
||||
- const: rockchip,rk3568-vicap
|
||||
+ enum:
|
||||
+ - rockchip,rk3568-vicap
|
||||
+ - rockchip,rk3588-vicap
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
@@ -26,11 +32,8 @@ properties:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
- items:
|
||||
- - description: ACLK
|
||||
- - description: HCLK
|
||||
- - description: DCLK
|
||||
- - description: ICLK
|
||||
+ minItems: 4
|
||||
+ maxItems: 5
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
@@ -38,25 +41,19 @@ properties:
|
||||
- const: hclk
|
||||
- const: dclk
|
||||
- const: iclk
|
||||
+ - const: iclk1
|
||||
+ minItems: 4
|
||||
|
||||
iommus:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
- items:
|
||||
- - description: ARST
|
||||
- - description: HRST
|
||||
- - description: DRST
|
||||
- - description: PRST
|
||||
- - description: IRST
|
||||
+ minItems: 5
|
||||
+ maxItems: 9
|
||||
|
||||
reset-names:
|
||||
- items:
|
||||
- - const: arst
|
||||
- - const: hrst
|
||||
- - const: drst
|
||||
- - const: prst
|
||||
- - const: irst
|
||||
+ minItems: 5
|
||||
+ maxItems: 9
|
||||
|
||||
rockchip,grf:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
@@ -67,8 +64,15 @@ properties:
|
||||
|
||||
ports:
|
||||
$ref: /schemas/graph.yaml#/properties/ports
|
||||
+ additionalProperties: false
|
||||
|
||||
properties:
|
||||
+ "#address-cells":
|
||||
+ const: 1
|
||||
+
|
||||
+ "#size-cells":
|
||||
+ const: 0
|
||||
+
|
||||
port@0:
|
||||
$ref: /schemas/graph.yaml#/$defs/port-base
|
||||
unevaluatedProperties: false
|
||||
@@ -100,13 +104,75 @@ properties:
|
||||
|
||||
port@1:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
- description: Port connected to the MIPI CSI-2 receiver output.
|
||||
+ description: Port connected to the MIPI CSI-2 receiver 0 output.
|
||||
+
|
||||
+ properties:
|
||||
+ endpoint:
|
||||
+ $ref: video-interfaces.yaml#
|
||||
+ unevaluatedProperties: false
|
||||
+
|
||||
+ port@2:
|
||||
+ $ref: /schemas/graph.yaml#/properties/port
|
||||
+ description: Port connected to the MIPI CSI-2 receiver 1 output.
|
||||
+
|
||||
+ properties:
|
||||
+ endpoint:
|
||||
+ $ref: video-interfaces.yaml#
|
||||
+ unevaluatedProperties: false
|
||||
+
|
||||
+ port@3:
|
||||
+ $ref: /schemas/graph.yaml#/properties/port
|
||||
+ description: Port connected to the MIPI CSI-2 receiver 2 output.
|
||||
+
|
||||
+ properties:
|
||||
+ endpoint:
|
||||
+ $ref: video-interfaces.yaml#
|
||||
+ unevaluatedProperties: false
|
||||
+
|
||||
+ port@4:
|
||||
+ $ref: /schemas/graph.yaml#/properties/port
|
||||
+ description: Port connected to the MIPI CSI-2 receiver 3 output.
|
||||
|
||||
properties:
|
||||
endpoint:
|
||||
$ref: video-interfaces.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
+ port@5:
|
||||
+ $ref: /schemas/graph.yaml#/properties/port
|
||||
+ description: Port connected to the MIPI CSI-2 receiver 4 output.
|
||||
+
|
||||
+ properties:
|
||||
+ endpoint:
|
||||
+ $ref: video-interfaces.yaml#
|
||||
+ unevaluatedProperties: false
|
||||
+
|
||||
+ port@6:
|
||||
+ $ref: /schemas/graph.yaml#/properties/port
|
||||
+ description: Port connected to the MIPI CSI-2 receiver 5 output.
|
||||
+
|
||||
+ properties:
|
||||
+ endpoint:
|
||||
+ $ref: video-interfaces.yaml#
|
||||
+ unevaluatedProperties: false
|
||||
+
|
||||
+ port@10:
|
||||
+ $ref: /schemas/graph.yaml#/properties/port
|
||||
+ description: Port connected to the ISP0 input.
|
||||
+
|
||||
+ properties:
|
||||
+ endpoint:
|
||||
+ $ref: video-interfaces.yaml#
|
||||
+ unevaluatedProperties: false
|
||||
+
|
||||
+ port@11:
|
||||
+ $ref: /schemas/graph.yaml#/properties/port
|
||||
+ description: Port connected to the ISP1 input.
|
||||
+
|
||||
+ properties:
|
||||
+ endpoint:
|
||||
+ $ref: video-interfaces.yaml#
|
||||
+ unevaluatedProperties: false
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
@@ -114,6 +180,75 @@ required:
|
||||
- clocks
|
||||
- ports
|
||||
|
||||
+allOf:
|
||||
+ - if:
|
||||
+ properties:
|
||||
+ compatible:
|
||||
+ contains:
|
||||
+ const: rockchip,rk3568-vicap
|
||||
+ then:
|
||||
+ properties:
|
||||
+ clocks:
|
||||
+ maxItems: 4
|
||||
+
|
||||
+ clock-names:
|
||||
+ maxItems: 4
|
||||
+
|
||||
+ resets:
|
||||
+ maxItems: 5
|
||||
+
|
||||
+ reset-names:
|
||||
+ items:
|
||||
+ - const: arst
|
||||
+ - const: hrst
|
||||
+ - const: drst
|
||||
+ - const: prst
|
||||
+ - const: irst
|
||||
+
|
||||
+ ports:
|
||||
+ properties:
|
||||
+ port@2: false
|
||||
+
|
||||
+ port@3: false
|
||||
+
|
||||
+ port@4: false
|
||||
+
|
||||
+ port@5: false
|
||||
+
|
||||
+ port@6: false
|
||||
+
|
||||
+ port@10: false
|
||||
+
|
||||
+ port@11: false
|
||||
+
|
||||
+ - if:
|
||||
+ properties:
|
||||
+ compatible:
|
||||
+ contains:
|
||||
+ const: rockchip,rk3588-vicap
|
||||
+ then:
|
||||
+ properties:
|
||||
+ clocks:
|
||||
+ minItems: 5
|
||||
+
|
||||
+ clock-names:
|
||||
+ minItems: 5
|
||||
+
|
||||
+ resets:
|
||||
+ minItems: 9
|
||||
+
|
||||
+ reset-names:
|
||||
+ items:
|
||||
+ - const: arst
|
||||
+ - const: hrst
|
||||
+ - const: drst
|
||||
+ - const: irst0
|
||||
+ - const: irst1
|
||||
+ - const: irst2
|
||||
+ - const: irst3
|
||||
+ - const: irst4
|
||||
+ - const: irst5
|
||||
+
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
+253
@@ -0,0 +1,253 @@
|
||||
From 2175323fdf820ebf2d861283d1de7ef394b048c5 Mon Sep 17 00:00:00 2001
|
||||
From: Michael Riesch <michael.riesch@collabora.com>
|
||||
Date: Fri, 22 May 2026 23:23:09 +0200
|
||||
Subject: [PATCH] media: rockchip: rkcif: add support for rk3588 vicap mipi
|
||||
capture
|
||||
|
||||
The RK3588 Video Capture (VICAP) unit features a Digital Video Port (DVP)
|
||||
and six MIPI CSI-2 capture interfaces. Add initial support for this variant
|
||||
to the rkcif driver and enable the MIPI CSI-2 capture interfaces.
|
||||
|
||||
Reviewed-by: Mehdi Djait <mehdi.djait@linux.intel.com>
|
||||
Signed-off-by: Michael Riesch <michael.riesch@collabora.com>
|
||||
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
|
||||
---
|
||||
.../rockchip/rkcif/rkcif-capture-mipi.c | 148 +++++++++++++++++-
|
||||
.../rockchip/rkcif/rkcif-capture-mipi.h | 1 +
|
||||
.../platform/rockchip/rkcif/rkcif-common.h | 2 +-
|
||||
.../media/platform/rockchip/rkcif/rkcif-dev.c | 18 +++
|
||||
4 files changed, 163 insertions(+), 6 deletions(-)
|
||||
|
||||
--- a/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c
|
||||
@@ -30,6 +30,14 @@
|
||||
#define RK3568_MIPI_CTRL0_CROP_EN BIT(5)
|
||||
#define RK3568_MIPI_CTRL0_WRDDR(type) ((type) << 1)
|
||||
|
||||
+#define RK3588_MIPI_CTRL0_DMA_EN BIT(28)
|
||||
+#define RK3588_MIPI_CTRL0_HIGH_ALIGN BIT(27)
|
||||
+#define RK3588_MIPI_CTRL0_WRDDR(type) ((type) << 5)
|
||||
+#define RK3588_MIPI_CTRL0_CROP_EN BIT(4)
|
||||
+#define RK3588_MIPI_CTRL0_PARSE(type) ((type) << 1)
|
||||
+
|
||||
+#define RK3588_MIPI_CTRL_CAP_EN BIT(0)
|
||||
+
|
||||
#define RKCIF_MIPI_CTRL0_DT_ID(id) ((id) << 10)
|
||||
#define RKCIF_MIPI_CTRL0_VC_ID(id) ((id) << 8)
|
||||
#define RKCIF_MIPI_CTRL0_CAP_EN BIT(0)
|
||||
@@ -375,11 +383,8 @@ static u32
|
||||
rkcif_rk3568_mipi_ctrl0(struct rkcif_stream *stream,
|
||||
const struct rkcif_output_fmt *active_out_fmt)
|
||||
{
|
||||
- u32 ctrl0 = 0;
|
||||
-
|
||||
- ctrl0 |= RKCIF_MIPI_CTRL0_DT_ID(active_out_fmt->mipi.dt);
|
||||
- ctrl0 |= RKCIF_MIPI_CTRL0_CAP_EN;
|
||||
- ctrl0 |= RK3568_MIPI_CTRL0_CROP_EN;
|
||||
+ u32 ctrl0 = RKCIF_MIPI_CTRL0_DT_ID(active_out_fmt->mipi.dt) |
|
||||
+ RKCIF_MIPI_CTRL0_CAP_EN | RK3568_MIPI_CTRL0_CROP_EN;
|
||||
|
||||
if (active_out_fmt->mipi.compact)
|
||||
ctrl0 |= RK3568_MIPI_CTRL0_COMPACT_EN;
|
||||
@@ -481,6 +486,132 @@ const struct rkcif_mipi_match_data rkcif
|
||||
},
|
||||
};
|
||||
|
||||
+static u32
|
||||
+rkcif_rk3588_mipi_ctrl0(struct rkcif_stream *stream,
|
||||
+ const struct rkcif_output_fmt *active_out_fmt)
|
||||
+{
|
||||
+ u32 ctrl0 = 0;
|
||||
+
|
||||
+ ctrl0 |= RK3588_MIPI_CTRL0_DMA_EN;
|
||||
+ ctrl0 |= RKCIF_MIPI_CTRL0_DT_ID(active_out_fmt->mipi.dt);
|
||||
+ ctrl0 |= RK3588_MIPI_CTRL0_CROP_EN;
|
||||
+ ctrl0 |= RKCIF_MIPI_CTRL0_CAP_EN;
|
||||
+
|
||||
+ switch (active_out_fmt->mipi.type) {
|
||||
+ case RKCIF_MIPI_TYPE_RAW8:
|
||||
+ break;
|
||||
+ case RKCIF_MIPI_TYPE_RAW10:
|
||||
+ ctrl0 |= RK3588_MIPI_CTRL0_PARSE(0x1);
|
||||
+ if (!active_out_fmt->mipi.compact)
|
||||
+ ctrl0 |= RK3588_MIPI_CTRL0_WRDDR(0x1);
|
||||
+ break;
|
||||
+ case RKCIF_MIPI_TYPE_RAW12:
|
||||
+ ctrl0 |= RK3588_MIPI_CTRL0_PARSE(0x2);
|
||||
+ if (!active_out_fmt->mipi.compact)
|
||||
+ ctrl0 |= RK3588_MIPI_CTRL0_WRDDR(0x1);
|
||||
+ break;
|
||||
+ case RKCIF_MIPI_TYPE_RGB888:
|
||||
+ break;
|
||||
+ case RKCIF_MIPI_TYPE_YUV422SP:
|
||||
+ ctrl0 |= RK3588_MIPI_CTRL0_WRDDR(0x4);
|
||||
+ break;
|
||||
+ case RKCIF_MIPI_TYPE_YUV420SP:
|
||||
+ ctrl0 |= RK3588_MIPI_CTRL0_WRDDR(0x5);
|
||||
+ break;
|
||||
+ case RKCIF_MIPI_TYPE_YUV400:
|
||||
+ ctrl0 |= RK3588_MIPI_CTRL0_WRDDR(0x3);
|
||||
+ break;
|
||||
+ default:
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ return ctrl0;
|
||||
+}
|
||||
+
|
||||
+const struct rkcif_mipi_match_data rkcif_rk3588_vicap_mipi_match_data = {
|
||||
+ .mipi_num = 6,
|
||||
+ .mipi_ctrl0 = rkcif_rk3588_mipi_ctrl0,
|
||||
+ .regs = {
|
||||
+ [RKCIF_MIPI_CTRL] = 0x20,
|
||||
+ [RKCIF_MIPI_INTEN] = 0x74,
|
||||
+ [RKCIF_MIPI_INTSTAT] = 0x78,
|
||||
+ },
|
||||
+ .regs_id = {
|
||||
+ [RKCIF_ID0] = {
|
||||
+ [RKCIF_MIPI_CTRL0] = 0x00,
|
||||
+ [RKCIF_MIPI_CTRL1] = 0x04,
|
||||
+ [RKCIF_MIPI_FRAME0_ADDR_Y] = 0x24,
|
||||
+ [RKCIF_MIPI_FRAME0_ADDR_UV] = 0x2c,
|
||||
+ [RKCIF_MIPI_FRAME0_VLW_Y] = 0x34,
|
||||
+ [RKCIF_MIPI_FRAME0_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED,
|
||||
+ [RKCIF_MIPI_FRAME1_ADDR_Y] = 0x28,
|
||||
+ [RKCIF_MIPI_FRAME1_ADDR_UV] = 0x30,
|
||||
+ [RKCIF_MIPI_FRAME1_VLW_Y] = RKCIF_REGISTER_NOTSUPPORTED,
|
||||
+ [RKCIF_MIPI_FRAME1_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED,
|
||||
+ [RKCIF_MIPI_CROP_START] = 0x8c,
|
||||
+ },
|
||||
+ [RKCIF_ID1] = {
|
||||
+ [RKCIF_MIPI_CTRL0] = 0x08,
|
||||
+ [RKCIF_MIPI_CTRL1] = 0x0c,
|
||||
+ [RKCIF_MIPI_FRAME0_ADDR_Y] = 0x38,
|
||||
+ [RKCIF_MIPI_FRAME0_ADDR_UV] = 0x40,
|
||||
+ [RKCIF_MIPI_FRAME0_VLW_Y] = 0x48,
|
||||
+ [RKCIF_MIPI_FRAME0_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED,
|
||||
+ [RKCIF_MIPI_FRAME1_ADDR_Y] = 0x3c,
|
||||
+ [RKCIF_MIPI_FRAME1_ADDR_UV] = 0x44,
|
||||
+ [RKCIF_MIPI_FRAME1_VLW_Y] = RKCIF_REGISTER_NOTSUPPORTED,
|
||||
+ [RKCIF_MIPI_FRAME1_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED,
|
||||
+ [RKCIF_MIPI_CROP_START] = 0x90,
|
||||
+ },
|
||||
+ [RKCIF_ID2] = {
|
||||
+ [RKCIF_MIPI_CTRL0] = 0x10,
|
||||
+ [RKCIF_MIPI_CTRL1] = 0x14,
|
||||
+ [RKCIF_MIPI_FRAME0_ADDR_Y] = 0x4c,
|
||||
+ [RKCIF_MIPI_FRAME0_ADDR_UV] = 0x54,
|
||||
+ [RKCIF_MIPI_FRAME0_VLW_Y] = 0x5c,
|
||||
+ [RKCIF_MIPI_FRAME0_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED,
|
||||
+ [RKCIF_MIPI_FRAME1_ADDR_Y] = 0x50,
|
||||
+ [RKCIF_MIPI_FRAME1_ADDR_UV] = 0x58,
|
||||
+ [RKCIF_MIPI_FRAME1_VLW_Y] = RKCIF_REGISTER_NOTSUPPORTED,
|
||||
+ [RKCIF_MIPI_FRAME1_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED,
|
||||
+ [RKCIF_MIPI_CROP_START] = 0x94,
|
||||
+ },
|
||||
+ [RKCIF_ID3] = {
|
||||
+ [RKCIF_MIPI_CTRL0] = 0x18,
|
||||
+ [RKCIF_MIPI_CTRL1] = 0x1c,
|
||||
+ [RKCIF_MIPI_FRAME0_ADDR_Y] = 0x60,
|
||||
+ [RKCIF_MIPI_FRAME0_ADDR_UV] = 0x68,
|
||||
+ [RKCIF_MIPI_FRAME0_VLW_Y] = 0x70,
|
||||
+ [RKCIF_MIPI_FRAME0_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED,
|
||||
+ [RKCIF_MIPI_FRAME1_ADDR_Y] = 0x64,
|
||||
+ [RKCIF_MIPI_FRAME1_ADDR_UV] = 0x6c,
|
||||
+ [RKCIF_MIPI_FRAME1_VLW_Y] = RKCIF_REGISTER_NOTSUPPORTED,
|
||||
+ [RKCIF_MIPI_FRAME1_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED,
|
||||
+ [RKCIF_MIPI_CROP_START] = 0x98,
|
||||
+ },
|
||||
+ },
|
||||
+ .blocks = {
|
||||
+ {
|
||||
+ .offset = 0x100,
|
||||
+ },
|
||||
+ {
|
||||
+ .offset = 0x200,
|
||||
+ },
|
||||
+ {
|
||||
+ .offset = 0x300,
|
||||
+ },
|
||||
+ {
|
||||
+ .offset = 0x400,
|
||||
+ },
|
||||
+ {
|
||||
+ .offset = 0x500,
|
||||
+ },
|
||||
+ {
|
||||
+ .offset = 0x600,
|
||||
+ },
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
static inline unsigned int rkcif_mipi_get_reg(struct rkcif_interface *interface,
|
||||
unsigned int index)
|
||||
{
|
||||
@@ -631,6 +762,13 @@ static int rkcif_mipi_start_streaming(st
|
||||
rkcif_mipi_stream_write(stream, RKCIF_MIPI_CTRL1, ctrl1);
|
||||
rkcif_mipi_stream_write(stream, RKCIF_MIPI_CTRL0, ctrl0);
|
||||
|
||||
+ /*
|
||||
+ * TODO: This bit has a different meaning on the RK3568, but it is
|
||||
+ * set there by default anyway. While correct, this is not exactly
|
||||
+ * nice and shall be reworked during the next refactoring.
|
||||
+ */
|
||||
+ rkcif_mipi_write(interface, RKCIF_MIPI_CTRL, RK3588_MIPI_CTRL_CAP_EN);
|
||||
+
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
--- a/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.h
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.h
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "rkcif-common.h"
|
||||
|
||||
extern const struct rkcif_mipi_match_data rkcif_rk3568_vicap_mipi_match_data;
|
||||
+extern const struct rkcif_mipi_match_data rkcif_rk3588_vicap_mipi_match_data;
|
||||
|
||||
int rkcif_mipi_register(struct rkcif_device *rkcif);
|
||||
|
||||
--- a/drivers/media/platform/rockchip/rkcif/rkcif-common.h
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-common.h
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "rkcif-regs.h"
|
||||
|
||||
#define RKCIF_DRIVER_NAME "rockchip-cif"
|
||||
-#define RKCIF_CLK_MAX 4
|
||||
+#define RKCIF_CLK_MAX 5
|
||||
|
||||
enum rkcif_format_type {
|
||||
RKCIF_FMT_TYPE_INVALID,
|
||||
--- a/drivers/media/platform/rockchip/rkcif/rkcif-dev.c
|
||||
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-dev.c
|
||||
@@ -53,6 +53,20 @@ static const struct rkcif_match_data rk3
|
||||
.mipi = &rkcif_rk3568_vicap_mipi_match_data,
|
||||
};
|
||||
|
||||
+static const char *const rk3588_vicap_clks[] = {
|
||||
+ "aclk",
|
||||
+ "hclk",
|
||||
+ "dclk",
|
||||
+ "iclk",
|
||||
+ "iclk1",
|
||||
+};
|
||||
+
|
||||
+static const struct rkcif_match_data rk3588_vicap_match_data = {
|
||||
+ .clks = rk3588_vicap_clks,
|
||||
+ .clks_num = ARRAY_SIZE(rk3588_vicap_clks),
|
||||
+ .mipi = &rkcif_rk3588_vicap_mipi_match_data,
|
||||
+};
|
||||
+
|
||||
static const struct of_device_id rkcif_plat_of_match[] = {
|
||||
{
|
||||
.compatible = "rockchip,px30-vip",
|
||||
@@ -62,6 +76,10 @@ static const struct of_device_id rkcif_p
|
||||
.compatible = "rockchip,rk3568-vicap",
|
||||
.data = &rk3568_vicap_match_data,
|
||||
},
|
||||
+ {
|
||||
+ .compatible = "rockchip,rk3588-vicap",
|
||||
+ .data = &rk3588_vicap_match_data,
|
||||
+ },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rkcif_plat_of_match);
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
From 658810422076140f9a003a174f73b0eeaffa0611 Mon Sep 17 00:00:00 2001
|
||||
From: Michael Riesch <michael.riesch@collabora.com>
|
||||
Date: Fri, 27 Mar 2026 00:10:00 +0100
|
||||
Subject: [PATCH] media: dt-bindings: rockchip,rk3568-mipi-csi2: add rk3588
|
||||
compatible
|
||||
|
||||
The RK3588 MIPI CSI-2 receivers are compatible to the ones found in the
|
||||
RK3568. Introduce a list of compatible variants and add the RK3588 variant
|
||||
to it.
|
||||
|
||||
Acked-by: Rob Herring (Arm) <robh@kernel.org>
|
||||
Signed-off-by: Michael Riesch <michael.riesch@collabora.com>
|
||||
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
|
||||
---
|
||||
.../bindings/media/rockchip,rk3568-mipi-csi2.yaml | 11 ++++++++---
|
||||
1 file changed, 8 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml
|
||||
+++ b/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml
|
||||
@@ -16,9 +16,14 @@ description:
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
- enum:
|
||||
- - fsl,imx93-mipi-csi2
|
||||
- - rockchip,rk3568-mipi-csi2
|
||||
+ oneOf:
|
||||
+ - enum:
|
||||
+ - fsl,imx93-mipi-csi2
|
||||
+ - rockchip,rk3568-mipi-csi2
|
||||
+ - items:
|
||||
+ - enum:
|
||||
+ - rockchip,rk3588-mipi-csi2
|
||||
+ - const: rockchip,rk3568-mipi-csi2
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
Reference in new issue
Block a user