[PATCH v9 08/13] media: rockchip: rkcif: add support for mipi csi-2 capture
Michael Riesch via B4 Relay
devnull+michael.riesch.collabora.com at kernel.org
Wed Jun 18 08:38:03 PDT 2025
From: Michael Riesch <michael.riesch at collabora.com>
The RK3568 Video Capture (VICAP) unit features a MIPI CSI-2 capture
interface that can receive video data and write it into system memory
using the ping-pong scheme. Add support for it.
Signed-off-by: Michael Riesch <michael.riesch at wolfvision.net>
Reviewed-by: Bryan O'Donoghue <bryan.odonoghue at linaro.org>
Signed-off-by: Michael Riesch <michael.riesch at collabora.com>
---
.../platform/rockchip/rkcif/rkcif-capture-mipi.c | 743 +++++++++++++++++++++
.../platform/rockchip/rkcif/rkcif-capture-mipi.h | 2 +
.../media/platform/rockchip/rkcif/rkcif-common.h | 29 +
drivers/media/platform/rockchip/rkcif/rkcif-dev.c | 1 +
.../platform/rockchip/rkcif/rkcif-interface.c | 6 +-
drivers/media/platform/rockchip/rkcif/rkcif-regs.h | 24 +-
6 files changed, 802 insertions(+), 3 deletions(-)
diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c
index 0c3f7b8cfa18..ad38fc0d1db5 100644
--- a/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c
@@ -6,22 +6,765 @@
* Copyright (C) 2025 Michael Riesch <michael.riesch at wolfvision.net>
*/
+#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;
+ /* TODO there may be different factors and/or alignment constraints */
+ vlw = ALIGN(width * 2, 8);
+
+ 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)) {
+ rkcif_stream_pingpong(stream);
+ ret = IRQ_HANDLED;
+ }
+ }
+ }
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
index ee1a50a59505..1248af70bdab 100644
--- a/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.h
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.h
@@ -11,6 +11,8 @@
#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);
diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-common.h b/drivers/media/platform/rockchip/rkcif/rkcif-common.h
index 5e7bff585bca..a6c5cd2f01da 100644
--- a/drivers/media/platform/rockchip/rkcif/rkcif-common.h
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-common.h
@@ -34,6 +34,17 @@ enum rkcif_format_type {
RKCIF_FMT_TYPE_RAW,
};
+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,
+};
+
enum rkcif_id_index {
RKCIF_ID0,
RKCIF_ID1,
@@ -93,9 +104,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;
};
};
@@ -183,6 +200,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;
@@ -198,6 +226,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 b9463d135451..d857c4a5b5db 100644
--- a/drivers/media/platform/rockchip/rkcif/rkcif-dev.c
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-dev.c
@@ -49,6 +49,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[] = {
diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-interface.c b/drivers/media/platform/rockchip/rkcif/rkcif-interface.c
index 45a232ea96e3..ef07759604a7 100644
--- a/drivers/media/platform/rockchip/rkcif/rkcif-interface.c
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-interface.c
@@ -188,8 +188,10 @@ static int rkcif_interface_enable_streams(struct v4l2_subdev *sd,
stream = &interface->streams[RKCIF_ID0];
rkcif_interface_apply_crop(stream, state);
} else {
- /* TODO implement for MIPI */
- return -EOPNOTSUPP;
+ 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,
diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-regs.h b/drivers/media/platform/rockchip/rkcif/rkcif-regs.h
index 07fd64174e80..3d1f0c45c638 100644
--- a/drivers/media/platform/rockchip/rkcif/rkcif-regs.h
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-regs.h
@@ -30,7 +30,29 @@ enum rkcif_dvp_register_index {
RKCIF_DVP_REGISTER_MAX
};
-#define RKCIF_REGISTER_NOTSUPPORTED 0x420000
+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
+};
+
+#define RKCIF_REGISTER_NOTSUPPORTED 0x420000
#define RKCIF_FETCH_Y(VAL) ((VAL) & 0x1fff)
--
2.39.5
More information about the linux-arm-kernel
mailing list