Re: [PATCH v8 07/13] media: rockchip: add driver for mipi csi-2 receiver

From: Bryan O'Donoghue
Date: Fri Jun 20 2025 - 07:35:14 EST


On 11/06/2025 18:06, Michael Riesch via B4 Relay wrote:
From: Michael Riesch <michael.riesch@xxxxxxxxxxxxx>

The Rockchip RK3568 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 RK3568 Video Capture (VICAP) block.

Add a V4L2 subdevice driver for this unit.

Signed-off-by: Michael Riesch <michael.riesch@xxxxxxxxxxxxxx>
Signed-off-by: Michael Riesch <michael.riesch@xxxxxxxxxxxxx>
---
MAINTAINERS | 1 +
drivers/media/platform/rockchip/Kconfig | 1 +
drivers/media/platform/rockchip/Makefile | 1 +
drivers/media/platform/rockchip/rkcsi/Kconfig | 16 +
drivers/media/platform/rockchip/rkcsi/Makefile | 3 +
drivers/media/platform/rockchip/rkcsi/rkcsi.c | 735 +++++++++++++++++++++++++
6 files changed, 757 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 40d806c206be..5b9228f6ddae 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21463,6 +21463,7 @@ M: Michael Riesch <michael.riesch@xxxxxxxxxxxxx>
L: linux-media@xxxxxxxxxxxxxxx
S: Maintained
F: Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi.yaml
+F: drivers/media/platform/rockchip/rkcsi/
ROCKCHIP RK3568 RANDOM NUMBER GENERATOR SUPPORT
M: Daniel Golle <daniel@xxxxxxxxxxxxxx>
diff --git a/drivers/media/platform/rockchip/Kconfig b/drivers/media/platform/rockchip/Kconfig
index 549f4e9f443e..96b38768c17e 100644
--- a/drivers/media/platform/rockchip/Kconfig
+++ b/drivers/media/platform/rockchip/Kconfig
@@ -4,4 +4,5 @@ comment "Rockchip media platform drivers"
source "drivers/media/platform/rockchip/rga/Kconfig"
source "drivers/media/platform/rockchip/rkcif/Kconfig"
+source "drivers/media/platform/rockchip/rkcsi/Kconfig"
source "drivers/media/platform/rockchip/rkisp1/Kconfig"
diff --git a/drivers/media/platform/rockchip/Makefile b/drivers/media/platform/rockchip/Makefile
index 6aba32c8830c..932be2d3fdd6 100644
--- 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 += rkcsi/
obj-y += rkisp1/
diff --git a/drivers/media/platform/rockchip/rkcsi/Kconfig b/drivers/media/platform/rockchip/rkcsi/Kconfig
new file mode 100644
index 000000000000..d8004198c386
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkcsi/Kconfig
@@ -0,0 +1,16 @@
+config VIDEO_ROCKCHIP_CSI
+ tristate "Rockchip MIPI CSI-2 Receiver"
+ depends on VIDEO_DEV
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
+ depends on V4L_PLATFORM_DRIVERS
+ depends on PM && COMMON_CLK
+ select MEDIA_CONTROLLER
+ select V4L2_FWNODE
+ select VIDEO_V4L2_SUBDEV_API
+ help
+ This is a driver for Rockchip MIPI CSI-2 Receiver. It is featured
+ in various Rockchips SoCs, usually in combination with a Video
+ Capture (VICAP) unit (see Rockchip Camera Interface (CIF) driver).
+
+ To compile this driver as a module, choose M here: the module
+ will be called rockchip-mipi-csi.
diff --git a/drivers/media/platform/rockchip/rkcsi/Makefile b/drivers/media/platform/rockchip/rkcsi/Makefile
new file mode 100644
index 000000000000..147712cbb68a
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkcsi/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_VIDEO_ROCKCHIP_CSI) += rockchip-mipi-csi.o
+rockchip-mipi-csi-objs += rkcsi.o
diff --git a/drivers/media/platform/rockchip/rkcsi/rkcsi.c b/drivers/media/platform/rockchip/rkcsi/rkcsi.c
new file mode 100644
index 000000000000..eaad3608337e
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkcsi/rkcsi.c
@@ -0,0 +1,735 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Rockchip MIPI CSI-2 Receiver Driver
+ *
+ * Copyright (C) 2019 Rockchip Electronics Co., Ltd.
+ * Copyright (C) 2025 Michael Riesch <michael.riesch@xxxxxxxxxxxxxx>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+
+#include <media/mipi-csi2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+#define CSI2HOST_N_LANES 0x04
+#define CSI2HOST_CSI2_RESETN 0x10
+#define CSI2HOST_PHY_STATE 0x14
+#define CSI2HOST_ERR1 0x20
+#define CSI2HOST_ERR2 0x24
+#define CSI2HOST_MSK1 0x28
+#define CSI2HOST_MSK2 0x2c
+#define CSI2HOST_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 RKCSI_CLKS_MAX 1
+
+enum {
+ RKCSI_PAD_SINK,
+ RKCSI_PAD_SRC,
+ RKCSI_PAD_MAX,
+};
+
+struct rkcsi_format {
+ u32 code;
+ u8 depth;
+ u8 csi_dt;
+};
+
+struct rkcsi_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 rkcsi_format *formats;
+ unsigned int formats_num;
+
+ struct media_pad pads[RKCSI_PAD_MAX];
+ struct v4l2_async_notifier notifier;
+ struct v4l2_fwnode_endpoint vep;
+ struct v4l2_subdev sd;
+
+ struct v4l2_subdev *source_sd;
+ u32 source_pad;
+};
+
+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 rkcsi_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 rkcsi_device *to_rkcsi(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct rkcsi_device, sd);
+}
+
+static inline __maybe_unused void rkcsi_write(struct rkcsi_device *csi_dev,
+ unsigned int addr, u32 val)
+{
+ writel(val, csi_dev->base_addr + addr);
+}
+
+static inline __maybe_unused u32 rkcsi_read(struct rkcsi_device *csi_dev,
+ unsigned int addr)
+{
+ return readl(csi_dev->base_addr + addr);
+}
+
+static const struct rkcsi_format *
+rkcsi_find_format(struct rkcsi_device *csi_dev, u32 mbus_code)
+{
+ const struct rkcsi_format *format;
+
+ WARN_ON(csi_dev->formats_num == 0);
+
+ for (int i = 0; i < csi_dev->formats_num; i++) {
+ format = &csi_dev->formats[i];
+ if (format->code == mbus_code)
+ return format;
+ }
+
+ return NULL;
+}
+
+static int rkcsi_start(struct rkcsi_device *csi_dev)
+{
+ struct media_pad *source_pad =
+ &csi_dev->source_sd->entity.pads[csi_dev->source_pad];
+ enum v4l2_mbus_type bus_type = csi_dev->vep.bus_type;
+ union phy_configure_opts opts;
+ s64 link_freq;
+ u32 lanes = csi_dev->vep.bus.mipi_csi2.num_data_lanes;
+ u32 control = 0;
+ int ret;
+
+ if (lanes < 1 || lanes > 4)
+ return -EINVAL;
+
+ /* 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 -EINVAL;
+
+ if (bus_type == V4L2_MBUS_CSI2_DPHY) {
+ struct phy_configure_opts_mipi_dphy *cfg = &opts.mipi_dphy;
+
+ phy_mipi_dphy_get_default_config_for_hsclk(link_freq * 2, lanes,
+ cfg);
+ phy_set_mode(csi_dev->phy, PHY_MODE_MIPI_DPHY);
+ phy_configure(csi_dev->phy, &opts);

This function can return an error, shouldn't you be capturing it ?

drivers/phy/rockchip/phy-rockchip-innoc-csidphy.c::rockchip_inno_csidphy_configure();

+
+ control |= SW_CPHY_EN(0);
+
+ } else if (bus_type == V4L2_MBUS_CSI2_CPHY) {
+ control |= SW_CPHY_EN(1);

Do you need to flag this when you are returning an error ?

Other than that looks pretty good.

---
bod