On Fri Nov 14 16:20:21 2025 +0100, Michael Riesch wrote:
> 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 <[email protected]>
> Reviewed-by: Bryan O'Donoghue <[email protected]>
> Reviewed-by: Mehdi Djait <[email protected]>
> Signed-off-by: Michael Riesch <[email protected]>
> Signed-off-by: Sakari Ailus <[email protected]>
> Signed-off-by: Hans Verkuil <[email protected]>

Patch committed.

Thanks,
Hans Verkuil

 drivers/media/platform/rockchip/rkcif/Makefile     |   1 +
 .../platform/rockchip/rkcif/rkcif-capture-mipi.c   | 777 +++++++++++++++++++++
 .../platform/rockchip/rkcif/rkcif-capture-mipi.h   |  23 +
 .../media/platform/rockchip/rkcif/rkcif-common.h   |  29 +
 drivers/media/platform/rockchip/rkcif/rkcif-dev.c  |  12 +
 drivers/media/platform/rockchip/rkcif/rkcif-regs.h |  22 +
 6 files changed, 864 insertions(+)

---

diff --git a/drivers/media/platform/rockchip/rkcif/Makefile 
b/drivers/media/platform/rockchip/rkcif/Makefile
index a36e294d569d..dca2bf45159f 100644
--- 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
diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c 
b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c
new file mode 100644
index 000000000000..1b81bcc067ef
--- /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 <[email protected]>
+ * 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);
+       }
+}
diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.h 
b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.h
new file mode 100644
index 000000000000..7f16eadc474c
--- /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 <[email protected]>
+ * 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
diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-common.h 
b/drivers/media/platform/rockchip/rkcif/rkcif-common.h
index c6ec578e1049..dd92cfbc879f 100644
--- 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 {
diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-dev.c 
b/drivers/media/platform/rockchip/rkcif/rkcif-dev.c
index addc118ff8bf..b4cf1146f131 100644
--- 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 rk3568_vicap_match_data 
= {
        .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_device *rkcif)
        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, void *ctx)
        if (rkcif_dvp_isr(irq, ctx) == IRQ_HANDLED)
                ret = IRQ_HANDLED;
 
+       if (rkcif_mipi_isr(irq, ctx) == IRQ_HANDLED)
+               ret = IRQ_HANDLED;
+
        return ret;
 }
 
diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-regs.h 
b/drivers/media/platform/rockchip/rkcif/rkcif-regs.h
index 91d42d31fd10..3cf7ee19de30 100644
--- 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
_______________________________________________
linuxtv-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to