summaryrefslogtreecommitdiffstats
path: root/drivers/media/platform/rockchip
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:27:49 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:27:49 +0000
commitace9429bb58fd418f0c81d4c2835699bddf6bde6 (patch)
treeb2d64bc10158fdd5497876388cd68142ca374ed3 /drivers/media/platform/rockchip
parentInitial commit. (diff)
downloadlinux-ace9429bb58fd418f0c81d4c2835699bddf6bde6.tar.xz
linux-ace9429bb58fd418f0c81d4c2835699bddf6bde6.zip
Adding upstream version 6.6.15.upstream/6.6.15
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/media/platform/rockchip')
-rw-r--r--drivers/media/platform/rockchip/Kconfig6
-rw-r--r--drivers/media/platform/rockchip/Makefile3
-rw-r--r--drivers/media/platform/rockchip/rga/Kconfig14
-rw-r--r--drivers/media/platform/rockchip/rga/Makefile4
-rw-r--r--drivers/media/platform/rockchip/rga/rga-buf.c148
-rw-r--r--drivers/media/platform/rockchip/rga/rga-hw.c416
-rw-r--r--drivers/media/platform/rockchip/rga/rga-hw.h434
-rw-r--r--drivers/media/platform/rockchip/rga/rga.c996
-rw-r--r--drivers/media/platform/rockchip/rga/rga.h115
-rw-r--r--drivers/media/platform/rockchip/rkisp1/Kconfig19
-rw-r--r--drivers/media/platform/rockchip/rkisp1/Makefile14
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c1510
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-common.c180
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-common.h630
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c541
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-csi.h28
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-debug.c243
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c659
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c1062
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-params.c1976
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h1367
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c817
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-stats.c496
23 files changed, 11678 insertions, 0 deletions
diff --git a/drivers/media/platform/rockchip/Kconfig b/drivers/media/platform/rockchip/Kconfig
new file mode 100644
index 0000000000..b41d3960c1
--- /dev/null
+++ b/drivers/media/platform/rockchip/Kconfig
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+comment "Rockchip media platform drivers"
+
+source "drivers/media/platform/rockchip/rga/Kconfig"
+source "drivers/media/platform/rockchip/rkisp1/Kconfig"
diff --git a/drivers/media/platform/rockchip/Makefile b/drivers/media/platform/rockchip/Makefile
new file mode 100644
index 0000000000..4f782b876a
--- /dev/null
+++ b/drivers/media/platform/rockchip/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-y += rga/
+obj-y += rkisp1/
diff --git a/drivers/media/platform/rockchip/rga/Kconfig b/drivers/media/platform/rockchip/rga/Kconfig
new file mode 100644
index 0000000000..727a0f6ea4
--- /dev/null
+++ b/drivers/media/platform/rockchip/rga/Kconfig
@@ -0,0 +1,14 @@
+config VIDEO_ROCKCHIP_RGA
+ tristate "Rockchip Raster 2d Graphic Acceleration Unit"
+ depends on V4L_MEM2MEM_DRIVERS
+ depends on VIDEO_DEV
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
+ select VIDEOBUF2_DMA_SG
+ select V4L2_MEM2MEM_DEV
+ help
+ This is a v4l2 driver for Rockchip SOC RGA 2d graphics accelerator.
+ Rockchip RGA is a separate 2D raster graphic acceleration unit.
+ It accelerates 2D graphics operations, such as point/line drawing,
+ image scaling, rotation, BitBLT, alpha blending and image blur/sharpness.
+
+ To compile this driver as a module choose m here.
diff --git a/drivers/media/platform/rockchip/rga/Makefile b/drivers/media/platform/rockchip/rga/Makefile
new file mode 100644
index 0000000000..1bbecdc3d8
--- /dev/null
+++ b/drivers/media/platform/rockchip/rga/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+rockchip-rga-objs := rga.o rga-hw.o rga-buf.o
+
+obj-$(CONFIG_VIDEO_ROCKCHIP_RGA) += rockchip-rga.o
diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c
new file mode 100644
index 0000000000..81508ed5ab
--- /dev/null
+++ b/drivers/media/platform/rockchip/rga/rga-buf.c
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Jacob Chen <jacob-chen@iotwrt.com>
+ */
+
+#include <linux/pm_runtime.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-dma-sg.h>
+#include <media/videobuf2-v4l2.h>
+
+#include "rga-hw.h"
+#include "rga.h"
+
+static int
+rga_queue_setup(struct vb2_queue *vq,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], struct device *alloc_devs[])
+{
+ struct rga_ctx *ctx = vb2_get_drv_priv(vq);
+ struct rga_frame *f = rga_get_frame(ctx, vq->type);
+
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+
+ if (*nplanes)
+ return sizes[0] < f->size ? -EINVAL : 0;
+
+ sizes[0] = f->size;
+ *nplanes = 1;
+
+ return 0;
+}
+
+static int rga_buf_prepare(struct vb2_buffer *vb)
+{
+ struct rga_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct rga_frame *f = rga_get_frame(ctx, vb->vb2_queue->type);
+
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+
+ vb2_set_plane_payload(vb, 0, f->size);
+
+ return 0;
+}
+
+static void rga_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct rga_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+ v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
+}
+
+static void rga_buf_return_buffers(struct vb2_queue *q,
+ enum vb2_buffer_state state)
+{
+ struct rga_ctx *ctx = vb2_get_drv_priv(q);
+ struct vb2_v4l2_buffer *vbuf;
+
+ for (;;) {
+ if (V4L2_TYPE_IS_OUTPUT(q->type))
+ vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+ else
+ vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+ if (!vbuf)
+ break;
+ v4l2_m2m_buf_done(vbuf, state);
+ }
+}
+
+static int rga_buf_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct rga_ctx *ctx = vb2_get_drv_priv(q);
+ struct rockchip_rga *rga = ctx->rga;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(rga->dev);
+ if (ret < 0) {
+ rga_buf_return_buffers(q, VB2_BUF_STATE_QUEUED);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void rga_buf_stop_streaming(struct vb2_queue *q)
+{
+ struct rga_ctx *ctx = vb2_get_drv_priv(q);
+ struct rockchip_rga *rga = ctx->rga;
+
+ rga_buf_return_buffers(q, VB2_BUF_STATE_ERROR);
+ pm_runtime_put(rga->dev);
+}
+
+const struct vb2_ops rga_qops = {
+ .queue_setup = rga_queue_setup,
+ .buf_prepare = rga_buf_prepare,
+ .buf_queue = rga_buf_queue,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .start_streaming = rga_buf_start_streaming,
+ .stop_streaming = rga_buf_stop_streaming,
+};
+
+/* RGA MMU is a 1-Level MMU, so it can't be used through the IOMMU API.
+ * We use it more like a scatter-gather list.
+ */
+void rga_buf_map(struct vb2_buffer *vb)
+{
+ struct rga_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct rockchip_rga *rga = ctx->rga;
+ struct sg_table *sgt;
+ struct scatterlist *sgl;
+ unsigned int *pages;
+ unsigned int address, len, i, p;
+ unsigned int mapped_size = 0;
+
+ if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ pages = rga->src_mmu_pages;
+ else
+ pages = rga->dst_mmu_pages;
+
+ /* Create local MMU table for RGA */
+ sgt = vb2_plane_cookie(vb, 0);
+
+ for_each_sg(sgt->sgl, sgl, sgt->nents, i) {
+ len = sg_dma_len(sgl) >> PAGE_SHIFT;
+ address = sg_phys(sgl);
+
+ for (p = 0; p < len; p++) {
+ dma_addr_t phys = address +
+ ((dma_addr_t)p << PAGE_SHIFT);
+
+ pages[mapped_size + p] = phys;
+ }
+
+ mapped_size += len;
+ }
+
+ /* sync local MMU table for RGA */
+ dma_sync_single_for_device(rga->dev, virt_to_phys(pages),
+ 8 * PAGE_SIZE, DMA_BIDIRECTIONAL);
+}
diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c
new file mode 100644
index 0000000000..aaa96f2563
--- /dev/null
+++ b/drivers/media/platform/rockchip/rga/rga-hw.c
@@ -0,0 +1,416 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Jacob Chen <jacob-chen@iotwrt.com>
+ */
+
+#include <linux/pm_runtime.h>
+
+#include "rga-hw.h"
+#include "rga.h"
+
+enum e_rga_start_pos {
+ LT = 0,
+ LB = 1,
+ RT = 2,
+ RB = 3,
+};
+
+struct rga_addr_offset {
+ unsigned int y_off;
+ unsigned int u_off;
+ unsigned int v_off;
+};
+
+struct rga_corners_addr_offset {
+ struct rga_addr_offset left_top;
+ struct rga_addr_offset right_top;
+ struct rga_addr_offset left_bottom;
+ struct rga_addr_offset right_bottom;
+};
+
+static unsigned int rga_get_scaling(unsigned int src, unsigned int dst)
+{
+ /*
+ * The rga hw scaling factor is a normalized inverse of the
+ * scaling factor.
+ * For example: When source width is 100 and destination width is 200
+ * (scaling of 2x), then the hw factor is NC * 100 / 200.
+ * The normalization factor (NC) is 2^16 = 0x10000.
+ */
+
+ return (src > dst) ? ((dst << 16) / src) : ((src << 16) / dst);
+}
+
+static struct rga_corners_addr_offset
+rga_get_addr_offset(struct rga_frame *frm, unsigned int x, unsigned int y,
+ unsigned int w, unsigned int h)
+{
+ struct rga_corners_addr_offset offsets;
+ struct rga_addr_offset *lt, *lb, *rt, *rb;
+ unsigned int x_div = 0,
+ y_div = 0, uv_stride = 0, pixel_width = 0, uv_factor = 0;
+
+ lt = &offsets.left_top;
+ lb = &offsets.left_bottom;
+ rt = &offsets.right_top;
+ rb = &offsets.right_bottom;
+
+ x_div = frm->fmt->x_div;
+ y_div = frm->fmt->y_div;
+ uv_factor = frm->fmt->uv_factor;
+ uv_stride = frm->stride / x_div;
+ pixel_width = frm->stride / frm->width;
+
+ lt->y_off = y * frm->stride + x * pixel_width;
+ lt->u_off =
+ frm->width * frm->height + (y / y_div) * uv_stride + x / x_div;
+ lt->v_off = lt->u_off + frm->width * frm->height / uv_factor;
+
+ lb->y_off = lt->y_off + (h - 1) * frm->stride;
+ lb->u_off = lt->u_off + (h / y_div - 1) * uv_stride;
+ lb->v_off = lt->v_off + (h / y_div - 1) * uv_stride;
+
+ rt->y_off = lt->y_off + (w - 1) * pixel_width;
+ rt->u_off = lt->u_off + w / x_div - 1;
+ rt->v_off = lt->v_off + w / x_div - 1;
+
+ rb->y_off = lb->y_off + (w - 1) * pixel_width;
+ rb->u_off = lb->u_off + w / x_div - 1;
+ rb->v_off = lb->v_off + w / x_div - 1;
+
+ return offsets;
+}
+
+static struct rga_addr_offset *rga_lookup_draw_pos(struct
+ rga_corners_addr_offset
+ * offsets, u32 rotate_mode,
+ u32 mirr_mode)
+{
+ static enum e_rga_start_pos rot_mir_point_matrix[4][4] = {
+ {
+ LT, RT, LB, RB,
+ },
+ {
+ RT, LT, RB, LB,
+ },
+ {
+ RB, LB, RT, LT,
+ },
+ {
+ LB, RB, LT, RT,
+ },
+ };
+
+ if (!offsets)
+ return NULL;
+
+ switch (rot_mir_point_matrix[rotate_mode][mirr_mode]) {
+ case LT:
+ return &offsets->left_top;
+ case LB:
+ return &offsets->left_bottom;
+ case RT:
+ return &offsets->right_top;
+ case RB:
+ return &offsets->right_bottom;
+ }
+
+ return NULL;
+}
+
+static void rga_cmd_set_src_addr(struct rga_ctx *ctx, void *mmu_pages)
+{
+ struct rockchip_rga *rga = ctx->rga;
+ u32 *dest = rga->cmdbuf_virt;
+ unsigned int reg;
+
+ reg = RGA_MMU_SRC_BASE - RGA_MODE_BASE_REG;
+ dest[reg >> 2] = virt_to_phys(mmu_pages) >> 4;
+
+ reg = RGA_MMU_CTRL1 - RGA_MODE_BASE_REG;
+ dest[reg >> 2] |= 0x7;
+}
+
+static void rga_cmd_set_src1_addr(struct rga_ctx *ctx, void *mmu_pages)
+{
+ struct rockchip_rga *rga = ctx->rga;
+ u32 *dest = rga->cmdbuf_virt;
+ unsigned int reg;
+
+ reg = RGA_MMU_SRC1_BASE - RGA_MODE_BASE_REG;
+ dest[reg >> 2] = virt_to_phys(mmu_pages) >> 4;
+
+ reg = RGA_MMU_CTRL1 - RGA_MODE_BASE_REG;
+ dest[reg >> 2] |= 0x7 << 4;
+}
+
+static void rga_cmd_set_dst_addr(struct rga_ctx *ctx, void *mmu_pages)
+{
+ struct rockchip_rga *rga = ctx->rga;
+ u32 *dest = rga->cmdbuf_virt;
+ unsigned int reg;
+
+ reg = RGA_MMU_DST_BASE - RGA_MODE_BASE_REG;
+ dest[reg >> 2] = virt_to_phys(mmu_pages) >> 4;
+
+ reg = RGA_MMU_CTRL1 - RGA_MODE_BASE_REG;
+ dest[reg >> 2] |= 0x7 << 8;
+}
+
+static void rga_cmd_set_trans_info(struct rga_ctx *ctx)
+{
+ struct rockchip_rga *rga = ctx->rga;
+ u32 *dest = rga->cmdbuf_virt;
+ unsigned int scale_dst_w, scale_dst_h;
+ unsigned int src_h, src_w, src_x, src_y, dst_h, dst_w, dst_x, dst_y;
+ union rga_src_info src_info;
+ union rga_dst_info dst_info;
+ union rga_src_x_factor x_factor;
+ union rga_src_y_factor y_factor;
+ union rga_src_vir_info src_vir_info;
+ union rga_src_act_info src_act_info;
+ union rga_dst_vir_info dst_vir_info;
+ union rga_dst_act_info dst_act_info;
+
+ struct rga_addr_offset *dst_offset;
+ struct rga_corners_addr_offset offsets;
+ struct rga_corners_addr_offset src_offsets;
+
+ src_h = ctx->in.crop.height;
+ src_w = ctx->in.crop.width;
+ src_x = ctx->in.crop.left;
+ src_y = ctx->in.crop.top;
+ dst_h = ctx->out.crop.height;
+ dst_w = ctx->out.crop.width;
+ dst_x = ctx->out.crop.left;
+ dst_y = ctx->out.crop.top;
+
+ src_info.val = dest[(RGA_SRC_INFO - RGA_MODE_BASE_REG) >> 2];
+ dst_info.val = dest[(RGA_DST_INFO - RGA_MODE_BASE_REG) >> 2];
+ x_factor.val = dest[(RGA_SRC_X_FACTOR - RGA_MODE_BASE_REG) >> 2];
+ y_factor.val = dest[(RGA_SRC_Y_FACTOR - RGA_MODE_BASE_REG) >> 2];
+ src_vir_info.val = dest[(RGA_SRC_VIR_INFO - RGA_MODE_BASE_REG) >> 2];
+ src_act_info.val = dest[(RGA_SRC_ACT_INFO - RGA_MODE_BASE_REG) >> 2];
+ dst_vir_info.val = dest[(RGA_DST_VIR_INFO - RGA_MODE_BASE_REG) >> 2];
+ dst_act_info.val = dest[(RGA_DST_ACT_INFO - RGA_MODE_BASE_REG) >> 2];
+
+ src_info.data.format = ctx->in.fmt->hw_format;
+ src_info.data.swap = ctx->in.fmt->color_swap;
+ dst_info.data.format = ctx->out.fmt->hw_format;
+ dst_info.data.swap = ctx->out.fmt->color_swap;
+
+ /*
+ * CSC mode must only be set when the colorspace families differ between
+ * input and output. It must remain unset (zeroed) if both are the same.
+ */
+
+ if (RGA_COLOR_FMT_IS_YUV(ctx->in.fmt->hw_format) &&
+ RGA_COLOR_FMT_IS_RGB(ctx->out.fmt->hw_format)) {
+ switch (ctx->in.colorspace) {
+ case V4L2_COLORSPACE_REC709:
+ src_info.data.csc_mode = RGA_SRC_CSC_MODE_BT709_R0;
+ break;
+ default:
+ src_info.data.csc_mode = RGA_SRC_CSC_MODE_BT601_R0;
+ break;
+ }
+ }
+
+ if (RGA_COLOR_FMT_IS_RGB(ctx->in.fmt->hw_format) &&
+ RGA_COLOR_FMT_IS_YUV(ctx->out.fmt->hw_format)) {
+ switch (ctx->out.colorspace) {
+ case V4L2_COLORSPACE_REC709:
+ dst_info.data.csc_mode = RGA_SRC_CSC_MODE_BT709_R0;
+ break;
+ default:
+ dst_info.data.csc_mode = RGA_DST_CSC_MODE_BT601_R0;
+ break;
+ }
+ }
+
+ if (ctx->vflip)
+ src_info.data.mir_mode |= RGA_SRC_MIRR_MODE_X;
+
+ if (ctx->hflip)
+ src_info.data.mir_mode |= RGA_SRC_MIRR_MODE_Y;
+
+ switch (ctx->rotate) {
+ case 90:
+ src_info.data.rot_mode = RGA_SRC_ROT_MODE_90_DEGREE;
+ break;
+ case 180:
+ src_info.data.rot_mode = RGA_SRC_ROT_MODE_180_DEGREE;
+ break;
+ case 270:
+ src_info.data.rot_mode = RGA_SRC_ROT_MODE_270_DEGREE;
+ break;
+ default:
+ src_info.data.rot_mode = RGA_SRC_ROT_MODE_0_DEGREE;
+ break;
+ }
+
+ /*
+ * Calculate the up/down scaling mode/factor.
+ *
+ * RGA used to scale the picture first, and then rotate second,
+ * so we need to swap the w/h when rotate degree is 90/270.
+ */
+ if (src_info.data.rot_mode == RGA_SRC_ROT_MODE_90_DEGREE ||
+ src_info.data.rot_mode == RGA_SRC_ROT_MODE_270_DEGREE) {
+ if (rga->version.major == 0 || rga->version.minor == 0) {
+ if (dst_w == src_h)
+ src_h -= 8;
+ if (abs(src_w - dst_h) < 16)
+ src_w -= 16;
+ }
+
+ scale_dst_h = dst_w;
+ scale_dst_w = dst_h;
+ } else {
+ scale_dst_w = dst_w;
+ scale_dst_h = dst_h;
+ }
+
+ if (src_w == scale_dst_w) {
+ src_info.data.hscl_mode = RGA_SRC_HSCL_MODE_NO;
+ x_factor.val = 0;
+ } else if (src_w > scale_dst_w) {
+ src_info.data.hscl_mode = RGA_SRC_HSCL_MODE_DOWN;
+ x_factor.data.down_scale_factor =
+ rga_get_scaling(src_w, scale_dst_w) + 1;
+ } else {
+ src_info.data.hscl_mode = RGA_SRC_HSCL_MODE_UP;
+ x_factor.data.up_scale_factor =
+ rga_get_scaling(src_w - 1, scale_dst_w - 1);
+ }
+
+ if (src_h == scale_dst_h) {
+ src_info.data.vscl_mode = RGA_SRC_VSCL_MODE_NO;
+ y_factor.val = 0;
+ } else if (src_h > scale_dst_h) {
+ src_info.data.vscl_mode = RGA_SRC_VSCL_MODE_DOWN;
+ y_factor.data.down_scale_factor =
+ rga_get_scaling(src_h, scale_dst_h) + 1;
+ } else {
+ src_info.data.vscl_mode = RGA_SRC_VSCL_MODE_UP;
+ y_factor.data.up_scale_factor =
+ rga_get_scaling(src_h - 1, scale_dst_h - 1);
+ }
+
+ /*
+ * Calculate the framebuffer virtual strides and active size,
+ * note that the step of vir_stride / vir_width is 4 byte words
+ */
+ src_vir_info.data.vir_stride = ctx->in.stride >> 2;
+ src_vir_info.data.vir_width = ctx->in.stride >> 2;
+
+ src_act_info.data.act_height = src_h - 1;
+ src_act_info.data.act_width = src_w - 1;
+
+ dst_vir_info.data.vir_stride = ctx->out.stride >> 2;
+ dst_act_info.data.act_height = dst_h - 1;
+ dst_act_info.data.act_width = dst_w - 1;
+
+ /*
+ * Calculate the source framebuffer base address with offset pixel.
+ */
+ src_offsets = rga_get_addr_offset(&ctx->in, src_x, src_y,
+ src_w, src_h);
+
+ /*
+ * Configure the dest framebuffer base address with pixel offset.
+ */
+ offsets = rga_get_addr_offset(&ctx->out, dst_x, dst_y, dst_w, dst_h);
+ dst_offset = rga_lookup_draw_pos(&offsets, src_info.data.rot_mode,
+ src_info.data.mir_mode);
+
+ dest[(RGA_SRC_Y_RGB_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] =
+ src_offsets.left_top.y_off;
+ dest[(RGA_SRC_CB_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] =
+ src_offsets.left_top.u_off;
+ dest[(RGA_SRC_CR_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] =
+ src_offsets.left_top.v_off;
+
+ dest[(RGA_SRC_X_FACTOR - RGA_MODE_BASE_REG) >> 2] = x_factor.val;
+ dest[(RGA_SRC_Y_FACTOR - RGA_MODE_BASE_REG) >> 2] = y_factor.val;
+ dest[(RGA_SRC_VIR_INFO - RGA_MODE_BASE_REG) >> 2] = src_vir_info.val;
+ dest[(RGA_SRC_ACT_INFO - RGA_MODE_BASE_REG) >> 2] = src_act_info.val;
+
+ dest[(RGA_SRC_INFO - RGA_MODE_BASE_REG) >> 2] = src_info.val;
+
+ dest[(RGA_DST_Y_RGB_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] =
+ dst_offset->y_off;
+ dest[(RGA_DST_CB_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] =
+ dst_offset->u_off;
+ dest[(RGA_DST_CR_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] =
+ dst_offset->v_off;
+
+ dest[(RGA_DST_VIR_INFO - RGA_MODE_BASE_REG) >> 2] = dst_vir_info.val;
+ dest[(RGA_DST_ACT_INFO - RGA_MODE_BASE_REG) >> 2] = dst_act_info.val;
+
+ dest[(RGA_DST_INFO - RGA_MODE_BASE_REG) >> 2] = dst_info.val;
+}
+
+static void rga_cmd_set_mode(struct rga_ctx *ctx)
+{
+ struct rockchip_rga *rga = ctx->rga;
+ u32 *dest = rga->cmdbuf_virt;
+ union rga_mode_ctrl mode;
+ union rga_alpha_ctrl0 alpha_ctrl0;
+ union rga_alpha_ctrl1 alpha_ctrl1;
+
+ mode.val = 0;
+ alpha_ctrl0.val = 0;
+ alpha_ctrl1.val = 0;
+
+ mode.data.gradient_sat = 1;
+ mode.data.render = RGA_MODE_RENDER_BITBLT;
+ mode.data.bitblt = RGA_MODE_BITBLT_MODE_SRC_TO_DST;
+
+ /* disable alpha blending */
+ dest[(RGA_ALPHA_CTRL0 - RGA_MODE_BASE_REG) >> 2] = alpha_ctrl0.val;
+ dest[(RGA_ALPHA_CTRL1 - RGA_MODE_BASE_REG) >> 2] = alpha_ctrl1.val;
+
+ dest[(RGA_MODE_CTRL - RGA_MODE_BASE_REG) >> 2] = mode.val;
+}
+
+static void rga_cmd_set(struct rga_ctx *ctx)
+{
+ struct rockchip_rga *rga = ctx->rga;
+
+ memset(rga->cmdbuf_virt, 0, RGA_CMDBUF_SIZE * 4);
+
+ rga_cmd_set_src_addr(ctx, rga->src_mmu_pages);
+ /*
+ * Due to hardware bug,
+ * src1 mmu also should be configured when using alpha blending.
+ */
+ rga_cmd_set_src1_addr(ctx, rga->dst_mmu_pages);
+
+ rga_cmd_set_dst_addr(ctx, rga->dst_mmu_pages);
+ rga_cmd_set_mode(ctx);
+
+ rga_cmd_set_trans_info(ctx);
+
+ rga_write(rga, RGA_CMD_BASE, rga->cmdbuf_phy);
+
+ /* sync CMD buf for RGA */
+ dma_sync_single_for_device(rga->dev, rga->cmdbuf_phy,
+ PAGE_SIZE, DMA_BIDIRECTIONAL);
+}
+
+void rga_hw_start(struct rockchip_rga *rga)
+{
+ struct rga_ctx *ctx = rga->curr;
+
+ rga_cmd_set(ctx);
+
+ rga_write(rga, RGA_SYS_CTRL, 0x00);
+
+ rga_write(rga, RGA_SYS_CTRL, 0x22);
+
+ rga_write(rga, RGA_INT, 0x600);
+
+ rga_write(rga, RGA_CMD_CTRL, 0x1);
+}
diff --git a/drivers/media/platform/rockchip/rga/rga-hw.h b/drivers/media/platform/rockchip/rga/rga-hw.h
new file mode 100644
index 0000000000..e8917e5630
--- /dev/null
+++ b/drivers/media/platform/rockchip/rga/rga-hw.h
@@ -0,0 +1,434 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Jacob Chen <jacob-chen@iotwrt.com>
+ */
+#ifndef __RGA_HW_H__
+#define __RGA_HW_H__
+
+#define RGA_CMDBUF_SIZE 0x20
+
+/* Hardware limits */
+#define MAX_WIDTH 8192
+#define MAX_HEIGHT 8192
+
+#define MIN_WIDTH 34
+#define MIN_HEIGHT 34
+
+#define DEFAULT_WIDTH 100
+#define DEFAULT_HEIGHT 100
+
+#define RGA_TIMEOUT 500
+
+/* Registers address */
+#define RGA_SYS_CTRL 0x0000
+#define RGA_CMD_CTRL 0x0004
+#define RGA_CMD_BASE 0x0008
+#define RGA_INT 0x0010
+#define RGA_MMU_CTRL0 0x0014
+#define RGA_VERSION_INFO 0x0028
+
+#define RGA_MODE_BASE_REG 0x0100
+#define RGA_MODE_MAX_REG 0x017C
+
+#define RGA_MODE_CTRL 0x0100
+#define RGA_SRC_INFO 0x0104
+#define RGA_SRC_Y_RGB_BASE_ADDR 0x0108
+#define RGA_SRC_CB_BASE_ADDR 0x010c
+#define RGA_SRC_CR_BASE_ADDR 0x0110
+#define RGA_SRC1_RGB_BASE_ADDR 0x0114
+#define RGA_SRC_VIR_INFO 0x0118
+#define RGA_SRC_ACT_INFO 0x011c
+#define RGA_SRC_X_FACTOR 0x0120
+#define RGA_SRC_Y_FACTOR 0x0124
+#define RGA_SRC_BG_COLOR 0x0128
+#define RGA_SRC_FG_COLOR 0x012c
+#define RGA_SRC_TR_COLOR0 0x0130
+#define RGA_SRC_TR_COLOR1 0x0134
+
+#define RGA_DST_INFO 0x0138
+#define RGA_DST_Y_RGB_BASE_ADDR 0x013c
+#define RGA_DST_CB_BASE_ADDR 0x0140
+#define RGA_DST_CR_BASE_ADDR 0x0144
+#define RGA_DST_VIR_INFO 0x0148
+#define RGA_DST_ACT_INFO 0x014c
+
+#define RGA_ALPHA_CTRL0 0x0150
+#define RGA_ALPHA_CTRL1 0x0154
+#define RGA_FADING_CTRL 0x0158
+#define RGA_PAT_CON 0x015c
+#define RGA_ROP_CON0 0x0160
+#define RGA_ROP_CON1 0x0164
+#define RGA_MASK_BASE 0x0168
+
+#define RGA_MMU_CTRL1 0x016C
+#define RGA_MMU_SRC_BASE 0x0170
+#define RGA_MMU_SRC1_BASE 0x0174
+#define RGA_MMU_DST_BASE 0x0178
+
+/* Registers value */
+#define RGA_MODE_RENDER_BITBLT 0
+#define RGA_MODE_RENDER_COLOR_PALETTE 1
+#define RGA_MODE_RENDER_RECTANGLE_FILL 2
+#define RGA_MODE_RENDER_UPDATE_PALETTE_LUT_RAM 3
+
+#define RGA_MODE_BITBLT_MODE_SRC_TO_DST 0
+#define RGA_MODE_BITBLT_MODE_SRC_SRC1_TO_DST 1
+
+#define RGA_MODE_CF_ROP4_SOLID 0
+#define RGA_MODE_CF_ROP4_PATTERN 1
+
+#define RGA_COLOR_FMT_ABGR8888 0
+#define RGA_COLOR_FMT_XBGR8888 1
+#define RGA_COLOR_FMT_RGB888 2
+#define RGA_COLOR_FMT_BGR565 4
+#define RGA_COLOR_FMT_ABGR1555 5
+#define RGA_COLOR_FMT_ABGR4444 6
+#define RGA_COLOR_FMT_YUV422SP 8
+#define RGA_COLOR_FMT_YUV422P 9
+#define RGA_COLOR_FMT_YUV420SP 10
+#define RGA_COLOR_FMT_YUV420P 11
+/* SRC_COLOR Palette */
+#define RGA_COLOR_FMT_CP_1BPP 12
+#define RGA_COLOR_FMT_CP_2BPP 13
+#define RGA_COLOR_FMT_CP_4BPP 14
+#define RGA_COLOR_FMT_CP_8BPP 15
+#define RGA_COLOR_FMT_MASK 15
+
+#define RGA_COLOR_FMT_IS_YUV(fmt) \
+ (((fmt) >= RGA_COLOR_FMT_YUV422SP) && ((fmt) < RGA_COLOR_FMT_CP_1BPP))
+#define RGA_COLOR_FMT_IS_RGB(fmt) \
+ ((fmt) < RGA_COLOR_FMT_YUV422SP)
+
+#define RGA_COLOR_NONE_SWAP 0
+#define RGA_COLOR_RB_SWAP 1
+#define RGA_COLOR_ALPHA_SWAP 2
+#define RGA_COLOR_UV_SWAP 4
+
+#define RGA_SRC_CSC_MODE_BYPASS 0
+#define RGA_SRC_CSC_MODE_BT601_R0 1
+#define RGA_SRC_CSC_MODE_BT601_R1 2
+#define RGA_SRC_CSC_MODE_BT709_R0 3
+#define RGA_SRC_CSC_MODE_BT709_R1 4
+
+#define RGA_SRC_ROT_MODE_0_DEGREE 0
+#define RGA_SRC_ROT_MODE_90_DEGREE 1
+#define RGA_SRC_ROT_MODE_180_DEGREE 2
+#define RGA_SRC_ROT_MODE_270_DEGREE 3
+
+#define RGA_SRC_MIRR_MODE_NO 0
+#define RGA_SRC_MIRR_MODE_X 1
+#define RGA_SRC_MIRR_MODE_Y 2
+#define RGA_SRC_MIRR_MODE_X_Y 3
+
+#define RGA_SRC_HSCL_MODE_NO 0
+#define RGA_SRC_HSCL_MODE_DOWN 1
+#define RGA_SRC_HSCL_MODE_UP 2
+
+#define RGA_SRC_VSCL_MODE_NO 0
+#define RGA_SRC_VSCL_MODE_DOWN 1
+#define RGA_SRC_VSCL_MODE_UP 2
+
+#define RGA_SRC_TRANS_ENABLE_R 1
+#define RGA_SRC_TRANS_ENABLE_G 2
+#define RGA_SRC_TRANS_ENABLE_B 4
+#define RGA_SRC_TRANS_ENABLE_A 8
+
+#define RGA_SRC_BIC_COE_SELEC_CATROM 0
+#define RGA_SRC_BIC_COE_SELEC_MITCHELL 1
+#define RGA_SRC_BIC_COE_SELEC_HERMITE 2
+#define RGA_SRC_BIC_COE_SELEC_BSPLINE 3
+
+#define RGA_DST_DITHER_MODE_888_TO_666 0
+#define RGA_DST_DITHER_MODE_888_TO_565 1
+#define RGA_DST_DITHER_MODE_888_TO_555 2
+#define RGA_DST_DITHER_MODE_888_TO_444 3
+
+#define RGA_DST_CSC_MODE_BYPASS 0
+#define RGA_DST_CSC_MODE_BT601_R0 1
+#define RGA_DST_CSC_MODE_BT601_R1 2
+#define RGA_DST_CSC_MODE_BT709_R0 3
+
+#define RGA_ALPHA_ROP_MODE_2 0
+#define RGA_ALPHA_ROP_MODE_3 1
+#define RGA_ALPHA_ROP_MODE_4 2
+
+#define RGA_ALPHA_SELECT_ALPHA 0
+#define RGA_ALPHA_SELECT_ROP 1
+
+#define RGA_ALPHA_MASK_BIG_ENDIAN 0
+#define RGA_ALPHA_MASK_LITTLE_ENDIAN 1
+
+#define RGA_ALPHA_NORMAL 0
+#define RGA_ALPHA_REVERSE 1
+
+#define RGA_ALPHA_BLEND_GLOBAL 0
+#define RGA_ALPHA_BLEND_NORMAL 1
+#define RGA_ALPHA_BLEND_MULTIPLY 2
+
+#define RGA_ALPHA_CAL_CUT 0
+#define RGA_ALPHA_CAL_NORMAL 1
+
+#define RGA_ALPHA_FACTOR_ZERO 0
+#define RGA_ALPHA_FACTOR_ONE 1
+#define RGA_ALPHA_FACTOR_OTHER 2
+#define RGA_ALPHA_FACTOR_OTHER_REVERSE 3
+#define RGA_ALPHA_FACTOR_SELF 4
+
+#define RGA_ALPHA_COLOR_NORMAL 0
+#define RGA_ALPHA_COLOR_MULTIPLY_CAL 1
+
+/* Registers union */
+union rga_mode_ctrl {
+ unsigned int val;
+ struct {
+ /* [0:2] */
+ unsigned int render:3;
+ /* [3:6] */
+ unsigned int bitblt:1;
+ unsigned int cf_rop4_pat:1;
+ unsigned int alpha_zero_key:1;
+ unsigned int gradient_sat:1;
+ /* [7:31] */
+ unsigned int reserved:25;
+ } data;
+};
+
+union rga_src_info {
+ unsigned int val;
+ struct {
+ /* [0:3] */
+ unsigned int format:4;
+ /* [4:7] */
+ unsigned int swap:3;
+ unsigned int cp_endian:1;
+ /* [8:17] */
+ unsigned int csc_mode:2;
+ unsigned int rot_mode:2;
+ unsigned int mir_mode:2;
+ unsigned int hscl_mode:2;
+ unsigned int vscl_mode:2;
+ /* [18:22] */
+ unsigned int trans_mode:1;
+ unsigned int trans_enable:4;
+ /* [23:25] */
+ unsigned int dither_up_en:1;
+ unsigned int bic_coe_sel:2;
+ /* [26:31] */
+ unsigned int reserved:6;
+ } data;
+};
+
+union rga_src_vir_info {
+ unsigned int val;
+ struct {
+ /* [0:15] */
+ unsigned int vir_width:15;
+ unsigned int reserved:1;
+ /* [16:25] */
+ unsigned int vir_stride:10;
+ /* [26:31] */
+ unsigned int reserved1:6;
+ } data;
+};
+
+union rga_src_act_info {
+ unsigned int val;
+ struct {
+ /* [0:15] */
+ unsigned int act_width:13;
+ unsigned int reserved:3;
+ /* [16:31] */
+ unsigned int act_height:13;
+ unsigned int reserved1:3;
+ } data;
+};
+
+union rga_src_x_factor {
+ unsigned int val;
+ struct {
+ /* [0:15] */
+ unsigned int down_scale_factor:16;
+ /* [16:31] */
+ unsigned int up_scale_factor:16;
+ } data;
+};
+
+union rga_src_y_factor {
+ unsigned int val;
+ struct {
+ /* [0:15] */
+ unsigned int down_scale_factor:16;
+ /* [16:31] */
+ unsigned int up_scale_factor:16;
+ } data;
+};
+
+/* Alpha / Red / Green / Blue */
+union rga_src_cp_gr_color {
+ unsigned int val;
+ struct {
+ /* [0:15] */
+ unsigned int gradient_x:16;
+ /* [16:31] */
+ unsigned int gradient_y:16;
+ } data;
+};
+
+union rga_src_transparency_color0 {
+ unsigned int val;
+ struct {
+ /* [0:7] */
+ unsigned int trans_rmin:8;
+ /* [8:15] */
+ unsigned int trans_gmin:8;
+ /* [16:23] */
+ unsigned int trans_bmin:8;
+ /* [24:31] */
+ unsigned int trans_amin:8;
+ } data;
+};
+
+union rga_src_transparency_color1 {
+ unsigned int val;
+ struct {
+ /* [0:7] */
+ unsigned int trans_rmax:8;
+ /* [8:15] */
+ unsigned int trans_gmax:8;
+ /* [16:23] */
+ unsigned int trans_bmax:8;
+ /* [24:31] */
+ unsigned int trans_amax:8;
+ } data;
+};
+
+union rga_dst_info {
+ unsigned int val;
+ struct {
+ /* [0:3] */
+ unsigned int format:4;
+ /* [4:6] */
+ unsigned int swap:3;
+ /* [7:9] */
+ unsigned int src1_format:3;
+ /* [10:11] */
+ unsigned int src1_swap:2;
+ /* [12:15] */
+ unsigned int dither_up_en:1;
+ unsigned int dither_down_en:1;
+ unsigned int dither_down_mode:2;
+ /* [16:18] */
+ unsigned int csc_mode:2;
+ unsigned int csc_clip:1;
+ /* [19:31] */
+ unsigned int reserved:13;
+ } data;
+};
+
+union rga_dst_vir_info {
+ unsigned int val;
+ struct {
+ /* [0:15] */
+ unsigned int vir_stride:15;
+ unsigned int reserved:1;
+ /* [16:31] */
+ unsigned int src1_vir_stride:15;
+ unsigned int reserved1:1;
+ } data;
+};
+
+union rga_dst_act_info {
+ unsigned int val;
+ struct {
+ /* [0:15] */
+ unsigned int act_width:12;
+ unsigned int reserved:4;
+ /* [16:31] */
+ unsigned int act_height:12;
+ unsigned int reserved1:4;
+ } data;
+};
+
+union rga_alpha_ctrl0 {
+ unsigned int val;
+ struct {
+ /* [0:3] */
+ unsigned int rop_en:1;
+ unsigned int rop_select:1;
+ unsigned int rop_mode:2;
+ /* [4:11] */
+ unsigned int src_fading_val:8;
+ /* [12:20] */
+ unsigned int dst_fading_val:8;
+ unsigned int mask_endian:1;
+ /* [21:31] */
+ unsigned int reserved:11;
+ } data;
+};
+
+union rga_alpha_ctrl1 {
+ unsigned int val;
+ struct {
+ /* [0:1] */
+ unsigned int dst_color_m0:1;
+ unsigned int src_color_m0:1;
+ /* [2:7] */
+ unsigned int dst_factor_m0:3;
+ unsigned int src_factor_m0:3;
+ /* [8:9] */
+ unsigned int dst_alpha_cal_m0:1;
+ unsigned int src_alpha_cal_m0:1;
+ /* [10:13] */
+ unsigned int dst_blend_m0:2;
+ unsigned int src_blend_m0:2;
+ /* [14:15] */
+ unsigned int dst_alpha_m0:1;
+ unsigned int src_alpha_m0:1;
+ /* [16:21] */
+ unsigned int dst_factor_m1:3;
+ unsigned int src_factor_m1:3;
+ /* [22:23] */
+ unsigned int dst_alpha_cal_m1:1;
+ unsigned int src_alpha_cal_m1:1;
+ /* [24:27] */
+ unsigned int dst_blend_m1:2;
+ unsigned int src_blend_m1:2;
+ /* [28:29] */
+ unsigned int dst_alpha_m1:1;
+ unsigned int src_alpha_m1:1;
+ /* [30:31] */
+ unsigned int reserved:2;
+ } data;
+};
+
+union rga_fading_ctrl {
+ unsigned int val;
+ struct {
+ /* [0:7] */
+ unsigned int fading_offset_r:8;
+ /* [8:15] */
+ unsigned int fading_offset_g:8;
+ /* [16:23] */
+ unsigned int fading_offset_b:8;
+ /* [24:31] */
+ unsigned int fading_en:1;
+ unsigned int reserved:7;
+ } data;
+};
+
+union rga_pat_con {
+ unsigned int val;
+ struct {
+ /* [0:7] */
+ unsigned int width:8;
+ /* [8:15] */
+ unsigned int height:8;
+ /* [16:23] */
+ unsigned int offset_x:8;
+ /* [24:31] */
+ unsigned int offset_y:8;
+ } data;
+};
+
+#endif
diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
new file mode 100644
index 0000000000..f1c532a580
--- /dev/null
+++ b/drivers/media/platform/rockchip/rga/rga.c
@@ -0,0 +1,996 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Jacob Chen <jacob-chen@iotwrt.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+
+#include <linux/platform_device.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-dma-sg.h>
+#include <media/videobuf2-v4l2.h>
+
+#include "rga-hw.h"
+#include "rga.h"
+
+static int debug;
+module_param(debug, int, 0644);
+
+static void device_run(void *prv)
+{
+ struct rga_ctx *ctx = prv;
+ struct rockchip_rga *rga = ctx->rga;
+ struct vb2_v4l2_buffer *src, *dst;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rga->ctrl_lock, flags);
+
+ rga->curr = ctx;
+
+ src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+ dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+
+ rga_buf_map(&src->vb2_buf);
+ rga_buf_map(&dst->vb2_buf);
+
+ rga_hw_start(rga);
+
+ spin_unlock_irqrestore(&rga->ctrl_lock, flags);
+}
+
+static irqreturn_t rga_isr(int irq, void *prv)
+{
+ struct rockchip_rga *rga = prv;
+ int intr;
+
+ intr = rga_read(rga, RGA_INT) & 0xf;
+
+ rga_mod(rga, RGA_INT, intr << 4, 0xf << 4);
+
+ if (intr & 0x04) {
+ struct vb2_v4l2_buffer *src, *dst;
+ struct rga_ctx *ctx = rga->curr;
+
+ WARN_ON(!ctx);
+
+ rga->curr = NULL;
+
+ src = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+ dst = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+
+ WARN_ON(!src);
+ WARN_ON(!dst);
+
+ v4l2_m2m_buf_copy_metadata(src, dst, true);
+
+ v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE);
+ v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE);
+ v4l2_m2m_job_finish(rga->m2m_dev, ctx->fh.m2m_ctx);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static const struct v4l2_m2m_ops rga_m2m_ops = {
+ .device_run = device_run,
+};
+
+static int
+queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
+{
+ struct rga_ctx *ctx = priv;
+ int ret;
+
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ src_vq->drv_priv = ctx;
+ src_vq->ops = &rga_qops;
+ src_vq->mem_ops = &vb2_dma_sg_memops;
+ src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->lock = &ctx->rga->mutex;
+ src_vq->dev = ctx->rga->v4l2_dev.dev;
+
+ ret = vb2_queue_init(src_vq);
+ if (ret)
+ return ret;
+
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ dst_vq->drv_priv = ctx;
+ dst_vq->ops = &rga_qops;
+ dst_vq->mem_ops = &vb2_dma_sg_memops;
+ dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->lock = &ctx->rga->mutex;
+ dst_vq->dev = ctx->rga->v4l2_dev.dev;
+
+ return vb2_queue_init(dst_vq);
+}
+
+static int rga_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct rga_ctx *ctx = container_of(ctrl->handler, struct rga_ctx,
+ ctrl_handler);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctx->rga->ctrl_lock, flags);
+ switch (ctrl->id) {
+ case V4L2_CID_HFLIP:
+ ctx->hflip = ctrl->val;
+ break;
+ case V4L2_CID_VFLIP:
+ ctx->vflip = ctrl->val;
+ break;
+ case V4L2_CID_ROTATE:
+ ctx->rotate = ctrl->val;
+ break;
+ case V4L2_CID_BG_COLOR:
+ ctx->fill_color = ctrl->val;
+ break;
+ }
+ spin_unlock_irqrestore(&ctx->rga->ctrl_lock, flags);
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops rga_ctrl_ops = {
+ .s_ctrl = rga_s_ctrl,
+};
+
+static int rga_setup_ctrls(struct rga_ctx *ctx)
+{
+ struct rockchip_rga *rga = ctx->rga;
+
+ v4l2_ctrl_handler_init(&ctx->ctrl_handler, 4);
+
+ v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+
+ v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+
+ v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops,
+ V4L2_CID_ROTATE, 0, 270, 90, 0);
+
+ v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops,
+ V4L2_CID_BG_COLOR, 0, 0xffffffff, 1, 0);
+
+ if (ctx->ctrl_handler.error) {
+ int err = ctx->ctrl_handler.error;
+
+ v4l2_err(&rga->v4l2_dev, "%s failed\n", __func__);
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ return err;
+ }
+
+ return 0;
+}
+
+static struct rga_fmt formats[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_ARGB32,
+ .color_swap = RGA_COLOR_RB_SWAP,
+ .hw_format = RGA_COLOR_FMT_ABGR8888,
+ .depth = 32,
+ .uv_factor = 1,
+ .y_div = 1,
+ .x_div = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_XRGB32,
+ .color_swap = RGA_COLOR_RB_SWAP,
+ .hw_format = RGA_COLOR_FMT_XBGR8888,
+ .depth = 32,
+ .uv_factor = 1,
+ .y_div = 1,
+ .x_div = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_ABGR32,
+ .color_swap = RGA_COLOR_ALPHA_SWAP,
+ .hw_format = RGA_COLOR_FMT_ABGR8888,
+ .depth = 32,
+ .uv_factor = 1,
+ .y_div = 1,
+ .x_div = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_XBGR32,
+ .color_swap = RGA_COLOR_ALPHA_SWAP,
+ .hw_format = RGA_COLOR_FMT_XBGR8888,
+ .depth = 32,
+ .uv_factor = 1,
+ .y_div = 1,
+ .x_div = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGB24,
+ .color_swap = RGA_COLOR_NONE_SWAP,
+ .hw_format = RGA_COLOR_FMT_RGB888,
+ .depth = 24,
+ .uv_factor = 1,
+ .y_div = 1,
+ .x_div = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_BGR24,
+ .color_swap = RGA_COLOR_RB_SWAP,
+ .hw_format = RGA_COLOR_FMT_RGB888,
+ .depth = 24,
+ .uv_factor = 1,
+ .y_div = 1,
+ .x_div = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_ARGB444,
+ .color_swap = RGA_COLOR_RB_SWAP,
+ .hw_format = RGA_COLOR_FMT_ABGR4444,
+ .depth = 16,
+ .uv_factor = 1,
+ .y_div = 1,
+ .x_div = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_ARGB555,
+ .color_swap = RGA_COLOR_RB_SWAP,
+ .hw_format = RGA_COLOR_FMT_ABGR1555,
+ .depth = 16,
+ .uv_factor = 1,
+ .y_div = 1,
+ .x_div = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .color_swap = RGA_COLOR_RB_SWAP,
+ .hw_format = RGA_COLOR_FMT_BGR565,
+ .depth = 16,
+ .uv_factor = 1,
+ .y_div = 1,
+ .x_div = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV21,
+ .color_swap = RGA_COLOR_UV_SWAP,
+ .hw_format = RGA_COLOR_FMT_YUV420SP,
+ .depth = 12,
+ .uv_factor = 4,
+ .y_div = 2,
+ .x_div = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV61,
+ .color_swap = RGA_COLOR_UV_SWAP,
+ .hw_format = RGA_COLOR_FMT_YUV422SP,
+ .depth = 16,
+ .uv_factor = 2,
+ .y_div = 1,
+ .x_div = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .color_swap = RGA_COLOR_NONE_SWAP,
+ .hw_format = RGA_COLOR_FMT_YUV420SP,
+ .depth = 12,
+ .uv_factor = 4,
+ .y_div = 2,
+ .x_div = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV16,
+ .color_swap = RGA_COLOR_NONE_SWAP,
+ .hw_format = RGA_COLOR_FMT_YUV422SP,
+ .depth = 16,
+ .uv_factor = 2,
+ .y_div = 1,
+ .x_div = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .color_swap = RGA_COLOR_NONE_SWAP,
+ .hw_format = RGA_COLOR_FMT_YUV420P,
+ .depth = 12,
+ .uv_factor = 4,
+ .y_div = 2,
+ .x_div = 2,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YUV422P,
+ .color_swap = RGA_COLOR_NONE_SWAP,
+ .hw_format = RGA_COLOR_FMT_YUV422P,
+ .depth = 16,
+ .uv_factor = 2,
+ .y_div = 1,
+ .x_div = 2,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YVU420,
+ .color_swap = RGA_COLOR_UV_SWAP,
+ .hw_format = RGA_COLOR_FMT_YUV420P,
+ .depth = 12,
+ .uv_factor = 4,
+ .y_div = 2,
+ .x_div = 2,
+ },
+};
+
+#define NUM_FORMATS ARRAY_SIZE(formats)
+
+static struct rga_fmt *rga_fmt_find(struct v4l2_format *f)
+{
+ unsigned int i;
+
+ for (i = 0; i < NUM_FORMATS; i++) {
+ if (formats[i].fourcc == f->fmt.pix.pixelformat)
+ return &formats[i];
+ }
+ return NULL;
+}
+
+static struct rga_frame def_frame = {
+ .width = DEFAULT_WIDTH,
+ .height = DEFAULT_HEIGHT,
+ .colorspace = V4L2_COLORSPACE_DEFAULT,
+ .crop.left = 0,
+ .crop.top = 0,
+ .crop.width = DEFAULT_WIDTH,
+ .crop.height = DEFAULT_HEIGHT,
+ .fmt = &formats[0],
+};
+
+struct rga_frame *rga_get_frame(struct rga_ctx *ctx, enum v4l2_buf_type type)
+{
+ switch (type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ return &ctx->in;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return &ctx->out;
+ default:
+ return ERR_PTR(-EINVAL);
+ }
+}
+
+static int rga_open(struct file *file)
+{
+ struct rockchip_rga *rga = video_drvdata(file);
+ struct rga_ctx *ctx = NULL;
+ int ret = 0;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+ ctx->rga = rga;
+ /* Set default formats */
+ ctx->in = def_frame;
+ ctx->out = def_frame;
+
+ if (mutex_lock_interruptible(&rga->mutex)) {
+ kfree(ctx);
+ return -ERESTARTSYS;
+ }
+ ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(rga->m2m_dev, ctx, &queue_init);
+ if (IS_ERR(ctx->fh.m2m_ctx)) {
+ ret = PTR_ERR(ctx->fh.m2m_ctx);
+ mutex_unlock(&rga->mutex);
+ kfree(ctx);
+ return ret;
+ }
+ v4l2_fh_init(&ctx->fh, video_devdata(file));
+ file->private_data = &ctx->fh;
+ v4l2_fh_add(&ctx->fh);
+
+ rga_setup_ctrls(ctx);
+
+ /* Write the default values to the ctx struct */
+ v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
+
+ ctx->fh.ctrl_handler = &ctx->ctrl_handler;
+ mutex_unlock(&rga->mutex);
+
+ return 0;
+}
+
+static int rga_release(struct file *file)
+{
+ struct rga_ctx *ctx =
+ container_of(file->private_data, struct rga_ctx, fh);
+ struct rockchip_rga *rga = ctx->rga;
+
+ mutex_lock(&rga->mutex);
+
+ v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ kfree(ctx);
+
+ mutex_unlock(&rga->mutex);
+
+ return 0;
+}
+
+static const struct v4l2_file_operations rga_fops = {
+ .owner = THIS_MODULE,
+ .open = rga_open,
+ .release = rga_release,
+ .poll = v4l2_m2m_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = v4l2_m2m_fop_mmap,
+};
+
+static int
+vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap)
+{
+ strscpy(cap->driver, RGA_NAME, sizeof(cap->driver));
+ strscpy(cap->card, "rockchip-rga", sizeof(cap->card));
+ strscpy(cap->bus_info, "platform:rga", sizeof(cap->bus_info));
+
+ return 0;
+}
+
+static int vidioc_enum_fmt(struct file *file, void *prv, struct v4l2_fmtdesc *f)
+{
+ struct rga_fmt *fmt;
+
+ if (f->index >= NUM_FORMATS)
+ return -EINVAL;
+
+ fmt = &formats[f->index];
+ f->pixelformat = fmt->fourcc;
+
+ return 0;
+}
+
+static int vidioc_g_fmt(struct file *file, void *prv, struct v4l2_format *f)
+{
+ struct rga_ctx *ctx = prv;
+ struct vb2_queue *vq;
+ struct rga_frame *frm;
+
+ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+ frm = rga_get_frame(ctx, f->type);
+ if (IS_ERR(frm))
+ return PTR_ERR(frm);
+
+ f->fmt.pix.width = frm->width;
+ f->fmt.pix.height = frm->height;
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.pixelformat = frm->fmt->fourcc;
+ f->fmt.pix.bytesperline = frm->stride;
+ f->fmt.pix.sizeimage = frm->size;
+ f->fmt.pix.colorspace = frm->colorspace;
+
+ return 0;
+}
+
+static int vidioc_try_fmt(struct file *file, void *prv, struct v4l2_format *f)
+{
+ struct rga_fmt *fmt;
+
+ fmt = rga_fmt_find(f);
+ if (!fmt) {
+ fmt = &formats[0];
+ f->fmt.pix.pixelformat = fmt->fourcc;
+ }
+
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+
+ if (f->fmt.pix.width > MAX_WIDTH)
+ f->fmt.pix.width = MAX_WIDTH;
+ if (f->fmt.pix.height > MAX_HEIGHT)
+ f->fmt.pix.height = MAX_HEIGHT;
+
+ if (f->fmt.pix.width < MIN_WIDTH)
+ f->fmt.pix.width = MIN_WIDTH;
+ if (f->fmt.pix.height < MIN_HEIGHT)
+ f->fmt.pix.height = MIN_HEIGHT;
+
+ if (fmt->hw_format >= RGA_COLOR_FMT_YUV422SP)
+ f->fmt.pix.bytesperline = f->fmt.pix.width;
+ else
+ f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
+
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * (f->fmt.pix.width * fmt->depth) >> 3;
+
+ return 0;
+}
+
+static int vidioc_s_fmt(struct file *file, void *prv, struct v4l2_format *f)
+{
+ struct rga_ctx *ctx = prv;
+ struct rockchip_rga *rga = ctx->rga;
+ struct vb2_queue *vq;
+ struct rga_frame *frm;
+ struct rga_fmt *fmt;
+ int ret = 0;
+
+ /* Adjust all values accordingly to the hardware capabilities
+ * and chosen format.
+ */
+ ret = vidioc_try_fmt(file, prv, f);
+ if (ret)
+ return ret;
+ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
+ if (vb2_is_busy(vq)) {
+ v4l2_err(&rga->v4l2_dev, "queue (%d) bust\n", f->type);
+ return -EBUSY;
+ }
+ frm = rga_get_frame(ctx, f->type);
+ if (IS_ERR(frm))
+ return PTR_ERR(frm);
+ fmt = rga_fmt_find(f);
+ if (!fmt)
+ return -EINVAL;
+ frm->width = f->fmt.pix.width;
+ frm->height = f->fmt.pix.height;
+ frm->size = f->fmt.pix.sizeimage;
+ frm->fmt = fmt;
+ frm->stride = f->fmt.pix.bytesperline;
+ frm->colorspace = f->fmt.pix.colorspace;
+
+ /* Reset crop settings */
+ frm->crop.left = 0;
+ frm->crop.top = 0;
+ frm->crop.width = frm->width;
+ frm->crop.height = frm->height;
+
+ return 0;
+}
+
+static int vidioc_g_selection(struct file *file, void *prv,
+ struct v4l2_selection *s)
+{
+ struct rga_ctx *ctx = prv;
+ struct rga_frame *f;
+ bool use_frame = false;
+
+ f = rga_get_frame(ctx, s->type);
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ break;
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+ break;
+ case V4L2_SEL_TGT_COMPOSE:
+ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ use_frame = true;
+ break;
+ case V4L2_SEL_TGT_CROP:
+ if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+ use_frame = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (use_frame) {
+ s->r = f->crop;
+ } else {
+ s->r.left = 0;
+ s->r.top = 0;
+ s->r.width = f->width;
+ s->r.height = f->height;
+ }
+
+ return 0;
+}
+
+static int vidioc_s_selection(struct file *file, void *prv,
+ struct v4l2_selection *s)
+{
+ struct rga_ctx *ctx = prv;
+ struct rockchip_rga *rga = ctx->rga;
+ struct rga_frame *f;
+ int ret = 0;
+
+ f = rga_get_frame(ctx, s->type);
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_COMPOSE:
+ /*
+ * COMPOSE target is only valid for capture buffer type, return
+ * error for output buffer type
+ */
+ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ break;
+ case V4L2_SEL_TGT_CROP:
+ /*
+ * CROP target is only valid for output buffer type, return
+ * error for capture buffer type
+ */
+ if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+ break;
+ /*
+ * bound and default crop/compose targets are invalid targets to
+ * try/set
+ */
+ default:
+ return -EINVAL;
+ }
+
+ if (s->r.top < 0 || s->r.left < 0) {
+ v4l2_dbg(debug, 1, &rga->v4l2_dev,
+ "doesn't support negative values for top & left.\n");
+ return -EINVAL;
+ }
+
+ if (s->r.left + s->r.width > f->width ||
+ s->r.top + s->r.height > f->height ||
+ s->r.width < MIN_WIDTH || s->r.height < MIN_HEIGHT) {
+ v4l2_dbg(debug, 1, &rga->v4l2_dev, "unsupported crop value.\n");
+ return -EINVAL;
+ }
+
+ f->crop = s->r;
+
+ return ret;
+}
+
+static const struct v4l2_ioctl_ops rga_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt,
+
+ .vidioc_enum_fmt_vid_out = vidioc_enum_fmt,
+ .vidioc_g_fmt_vid_out = vidioc_g_fmt,
+ .vidioc_try_fmt_vid_out = vidioc_try_fmt,
+ .vidioc_s_fmt_vid_out = vidioc_s_fmt,
+
+ .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
+ .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
+ .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
+ .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
+ .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
+ .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
+ .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
+
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+
+ .vidioc_streamon = v4l2_m2m_ioctl_streamon,
+ .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
+
+ .vidioc_g_selection = vidioc_g_selection,
+ .vidioc_s_selection = vidioc_s_selection,
+};
+
+static const struct video_device rga_videodev = {
+ .name = "rockchip-rga",
+ .fops = &rga_fops,
+ .ioctl_ops = &rga_ioctl_ops,
+ .minor = -1,
+ .release = video_device_release,
+ .vfl_dir = VFL_DIR_M2M,
+ .device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING,
+};
+
+static int rga_enable_clocks(struct rockchip_rga *rga)
+{
+ int ret;
+
+ ret = clk_prepare_enable(rga->sclk);
+ if (ret) {
+ dev_err(rga->dev, "Cannot enable rga sclk: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(rga->aclk);
+ if (ret) {
+ dev_err(rga->dev, "Cannot enable rga aclk: %d\n", ret);
+ goto err_disable_sclk;
+ }
+
+ ret = clk_prepare_enable(rga->hclk);
+ if (ret) {
+ dev_err(rga->dev, "Cannot enable rga hclk: %d\n", ret);
+ goto err_disable_aclk;
+ }
+
+ return 0;
+
+err_disable_aclk:
+ clk_disable_unprepare(rga->aclk);
+err_disable_sclk:
+ clk_disable_unprepare(rga->sclk);
+
+ return ret;
+}
+
+static void rga_disable_clocks(struct rockchip_rga *rga)
+{
+ clk_disable_unprepare(rga->sclk);
+ clk_disable_unprepare(rga->hclk);
+ clk_disable_unprepare(rga->aclk);
+}
+
+static int rga_parse_dt(struct rockchip_rga *rga)
+{
+ struct reset_control *core_rst, *axi_rst, *ahb_rst;
+
+ core_rst = devm_reset_control_get(rga->dev, "core");
+ if (IS_ERR(core_rst)) {
+ dev_err(rga->dev, "failed to get core reset controller\n");
+ return PTR_ERR(core_rst);
+ }
+
+ axi_rst = devm_reset_control_get(rga->dev, "axi");
+ if (IS_ERR(axi_rst)) {
+ dev_err(rga->dev, "failed to get axi reset controller\n");
+ return PTR_ERR(axi_rst);
+ }
+
+ ahb_rst = devm_reset_control_get(rga->dev, "ahb");
+ if (IS_ERR(ahb_rst)) {
+ dev_err(rga->dev, "failed to get ahb reset controller\n");
+ return PTR_ERR(ahb_rst);
+ }
+
+ reset_control_assert(core_rst);
+ udelay(1);
+ reset_control_deassert(core_rst);
+
+ reset_control_assert(axi_rst);
+ udelay(1);
+ reset_control_deassert(axi_rst);
+
+ reset_control_assert(ahb_rst);
+ udelay(1);
+ reset_control_deassert(ahb_rst);
+
+ rga->sclk = devm_clk_get(rga->dev, "sclk");
+ if (IS_ERR(rga->sclk)) {
+ dev_err(rga->dev, "failed to get sclk clock\n");
+ return PTR_ERR(rga->sclk);
+ }
+
+ rga->aclk = devm_clk_get(rga->dev, "aclk");
+ if (IS_ERR(rga->aclk)) {
+ dev_err(rga->dev, "failed to get aclk clock\n");
+ return PTR_ERR(rga->aclk);
+ }
+
+ rga->hclk = devm_clk_get(rga->dev, "hclk");
+ if (IS_ERR(rga->hclk)) {
+ dev_err(rga->dev, "failed to get hclk clock\n");
+ return PTR_ERR(rga->hclk);
+ }
+
+ return 0;
+}
+
+static int rga_probe(struct platform_device *pdev)
+{
+ struct rockchip_rga *rga;
+ struct video_device *vfd;
+ int ret = 0;
+ int irq;
+
+ if (!pdev->dev.of_node)
+ return -ENODEV;
+
+ rga = devm_kzalloc(&pdev->dev, sizeof(*rga), GFP_KERNEL);
+ if (!rga)
+ return -ENOMEM;
+
+ rga->dev = &pdev->dev;
+ spin_lock_init(&rga->ctrl_lock);
+ mutex_init(&rga->mutex);
+
+ ret = rga_parse_dt(rga);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Unable to parse OF data\n");
+
+ pm_runtime_enable(rga->dev);
+
+ rga->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(rga->regs)) {
+ ret = PTR_ERR(rga->regs);
+ goto err_put_clk;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ ret = irq;
+ goto err_put_clk;
+ }
+
+ ret = devm_request_irq(rga->dev, irq, rga_isr, 0,
+ dev_name(rga->dev), rga);
+ if (ret < 0) {
+ dev_err(rga->dev, "failed to request irq\n");
+ goto err_put_clk;
+ }
+
+ ret = v4l2_device_register(&pdev->dev, &rga->v4l2_dev);
+ if (ret)
+ goto err_put_clk;
+ vfd = video_device_alloc();
+ if (!vfd) {
+ v4l2_err(&rga->v4l2_dev, "Failed to allocate video device\n");
+ ret = -ENOMEM;
+ goto unreg_v4l2_dev;
+ }
+ *vfd = rga_videodev;
+ vfd->lock = &rga->mutex;
+ vfd->v4l2_dev = &rga->v4l2_dev;
+
+ video_set_drvdata(vfd, rga);
+ rga->vfd = vfd;
+
+ platform_set_drvdata(pdev, rga);
+ rga->m2m_dev = v4l2_m2m_init(&rga_m2m_ops);
+ if (IS_ERR(rga->m2m_dev)) {
+ v4l2_err(&rga->v4l2_dev, "Failed to init mem2mem device\n");
+ ret = PTR_ERR(rga->m2m_dev);
+ goto rel_vdev;
+ }
+
+ ret = pm_runtime_resume_and_get(rga->dev);
+ if (ret < 0)
+ goto rel_m2m;
+
+ rga->version.major = (rga_read(rga, RGA_VERSION_INFO) >> 24) & 0xFF;
+ rga->version.minor = (rga_read(rga, RGA_VERSION_INFO) >> 20) & 0x0F;
+
+ v4l2_info(&rga->v4l2_dev, "HW Version: 0x%02x.%02x\n",
+ rga->version.major, rga->version.minor);
+
+ pm_runtime_put(rga->dev);
+
+ /* Create CMD buffer */
+ rga->cmdbuf_virt = dma_alloc_attrs(rga->dev, RGA_CMDBUF_SIZE,
+ &rga->cmdbuf_phy, GFP_KERNEL,
+ DMA_ATTR_WRITE_COMBINE);
+ if (!rga->cmdbuf_virt) {
+ ret = -ENOMEM;
+ goto rel_m2m;
+ }
+
+ rga->src_mmu_pages =
+ (unsigned int *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 3);
+ if (!rga->src_mmu_pages) {
+ ret = -ENOMEM;
+ goto free_dma;
+ }
+ rga->dst_mmu_pages =
+ (unsigned int *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 3);
+ if (!rga->dst_mmu_pages) {
+ ret = -ENOMEM;
+ goto free_src_pages;
+ }
+
+ def_frame.stride = (def_frame.width * def_frame.fmt->depth) >> 3;
+ def_frame.size = def_frame.stride * def_frame.height;
+
+ ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1);
+ if (ret) {
+ v4l2_err(&rga->v4l2_dev, "Failed to register video device\n");
+ goto free_dst_pages;
+ }
+
+ v4l2_info(&rga->v4l2_dev, "Registered %s as /dev/%s\n",
+ vfd->name, video_device_node_name(vfd));
+
+ return 0;
+
+free_dst_pages:
+ free_pages((unsigned long)rga->dst_mmu_pages, 3);
+free_src_pages:
+ free_pages((unsigned long)rga->src_mmu_pages, 3);
+free_dma:
+ dma_free_attrs(rga->dev, RGA_CMDBUF_SIZE, rga->cmdbuf_virt,
+ rga->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE);
+rel_m2m:
+ v4l2_m2m_release(rga->m2m_dev);
+rel_vdev:
+ video_device_release(vfd);
+unreg_v4l2_dev:
+ v4l2_device_unregister(&rga->v4l2_dev);
+err_put_clk:
+ pm_runtime_disable(rga->dev);
+
+ return ret;
+}
+
+static void rga_remove(struct platform_device *pdev)
+{
+ struct rockchip_rga *rga = platform_get_drvdata(pdev);
+
+ dma_free_attrs(rga->dev, RGA_CMDBUF_SIZE, rga->cmdbuf_virt,
+ rga->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE);
+
+ free_pages((unsigned long)rga->src_mmu_pages, 3);
+ free_pages((unsigned long)rga->dst_mmu_pages, 3);
+
+ v4l2_info(&rga->v4l2_dev, "Removing\n");
+
+ v4l2_m2m_release(rga->m2m_dev);
+ video_unregister_device(rga->vfd);
+ v4l2_device_unregister(&rga->v4l2_dev);
+
+ pm_runtime_disable(rga->dev);
+}
+
+static int __maybe_unused rga_runtime_suspend(struct device *dev)
+{
+ struct rockchip_rga *rga = dev_get_drvdata(dev);
+
+ rga_disable_clocks(rga);
+
+ return 0;
+}
+
+static int __maybe_unused rga_runtime_resume(struct device *dev)
+{
+ struct rockchip_rga *rga = dev_get_drvdata(dev);
+
+ return rga_enable_clocks(rga);
+}
+
+static const struct dev_pm_ops rga_pm = {
+ SET_RUNTIME_PM_OPS(rga_runtime_suspend,
+ rga_runtime_resume, NULL)
+};
+
+static const struct of_device_id rockchip_rga_match[] = {
+ {
+ .compatible = "rockchip,rk3288-rga",
+ },
+ {
+ .compatible = "rockchip,rk3399-rga",
+ },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, rockchip_rga_match);
+
+static struct platform_driver rga_pdrv = {
+ .probe = rga_probe,
+ .remove_new = rga_remove,
+ .driver = {
+ .name = RGA_NAME,
+ .pm = &rga_pm,
+ .of_match_table = rockchip_rga_match,
+ },
+};
+
+module_platform_driver(rga_pdrv);
+
+MODULE_AUTHOR("Jacob Chen <jacob-chen@iotwrt.com>");
+MODULE_DESCRIPTION("Rockchip Raster 2d Graphic Acceleration Unit");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h
new file mode 100644
index 0000000000..5fa9d2f366
--- /dev/null
+++ b/drivers/media/platform/rockchip/rga/rga.h
@@ -0,0 +1,115 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Jacob Chen <jacob-chen@iotwrt.com>
+ */
+#ifndef __RGA_H__
+#define __RGA_H__
+
+#include <linux/platform_device.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+
+#define RGA_NAME "rockchip-rga"
+
+struct rga_fmt {
+ u32 fourcc;
+ int depth;
+ u8 uv_factor;
+ u8 y_div;
+ u8 x_div;
+ u8 color_swap;
+ u8 hw_format;
+};
+
+struct rga_frame {
+ /* Original dimensions */
+ u32 width;
+ u32 height;
+ u32 colorspace;
+
+ /* Crop */
+ struct v4l2_rect crop;
+
+ /* Image format */
+ struct rga_fmt *fmt;
+
+ /* Variables that can calculated once and reused */
+ u32 stride;
+ u32 size;
+};
+
+struct rockchip_rga_version {
+ u32 major;
+ u32 minor;
+};
+
+struct rga_ctx {
+ struct v4l2_fh fh;
+ struct rockchip_rga *rga;
+ struct rga_frame in;
+ struct rga_frame out;
+ struct v4l2_ctrl_handler ctrl_handler;
+
+ /* Control values */
+ u32 op;
+ u32 hflip;
+ u32 vflip;
+ u32 rotate;
+ u32 fill_color;
+};
+
+struct rockchip_rga {
+ struct v4l2_device v4l2_dev;
+ struct v4l2_m2m_dev *m2m_dev;
+ struct video_device *vfd;
+
+ struct device *dev;
+ struct regmap *grf;
+ void __iomem *regs;
+ struct clk *sclk;
+ struct clk *aclk;
+ struct clk *hclk;
+ struct rockchip_rga_version version;
+
+ /* vfd lock */
+ struct mutex mutex;
+ /* ctrl parm lock */
+ spinlock_t ctrl_lock;
+
+ struct rga_ctx *curr;
+ dma_addr_t cmdbuf_phy;
+ void *cmdbuf_virt;
+ unsigned int *src_mmu_pages;
+ unsigned int *dst_mmu_pages;
+};
+
+struct rga_frame *rga_get_frame(struct rga_ctx *ctx, enum v4l2_buf_type type);
+
+/* RGA Buffers Manage */
+extern const struct vb2_ops rga_qops;
+void rga_buf_map(struct vb2_buffer *vb);
+
+/* RGA Hardware */
+static inline void rga_write(struct rockchip_rga *rga, u32 reg, u32 value)
+{
+ writel(value, rga->regs + reg);
+};
+
+static inline u32 rga_read(struct rockchip_rga *rga, u32 reg)
+{
+ return readl(rga->regs + reg);
+};
+
+static inline void rga_mod(struct rockchip_rga *rga, u32 reg, u32 val, u32 mask)
+{
+ u32 temp = rga_read(rga, reg) & ~(mask);
+
+ temp |= val & mask;
+ rga_write(rga, reg, temp);
+};
+
+void rga_hw_start(struct rockchip_rga *rga);
+
+#endif
diff --git a/drivers/media/platform/rockchip/rkisp1/Kconfig b/drivers/media/platform/rockchip/rkisp1/Kconfig
new file mode 100644
index 0000000000..731c9acbf6
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkisp1/Kconfig
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config VIDEO_ROCKCHIP_ISP1
+ tristate "Rockchip Image Signal Processing v1 Unit driver"
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV && OF
+ depends on ARCH_ROCKCHIP || ARCH_MXC || COMPILE_TEST
+ select MEDIA_CONTROLLER
+ select VIDEO_V4L2_SUBDEV_API
+ select VIDEOBUF2_DMA_CONTIG
+ select VIDEOBUF2_VMALLOC
+ select V4L2_FWNODE
+ select GENERIC_PHY_MIPI_DPHY
+ default n
+ help
+ Enable this to support the Image Signal Processing (ISP) module
+ present in RK3399 SoCs.
+
+ To compile this driver as a module, choose M here: the module
+ will be called rockchip-isp1.
diff --git a/drivers/media/platform/rockchip/rkisp1/Makefile b/drivers/media/platform/rockchip/rkisp1/Makefile
new file mode 100644
index 0000000000..b3844c4f76
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkisp1/Makefile
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0
+
+rockchip-isp1-y := rkisp1-capture.o \
+ rkisp1-common.o \
+ rkisp1-csi.o \
+ rkisp1-dev.o \
+ rkisp1-isp.o \
+ rkisp1-resizer.o \
+ rkisp1-stats.o \
+ rkisp1-params.o
+
+rockchip-isp1-$(CONFIG_DEBUG_FS) += rkisp1-debug.o
+
+obj-$(CONFIG_VIDEO_ROCKCHIP_ISP1) += rockchip-isp1.o
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c
new file mode 100644
index 0000000000..8f3cba3197
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c
@@ -0,0 +1,1510 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Rockchip ISP1 Driver - V4l capture device
+ *
+ * Copyright (C) 2019 Collabora, Ltd.
+ *
+ * Based on Rockchip ISP1 driver by Rockchip Electronics Co., Ltd.
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ */
+
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "rkisp1-common.h"
+
+/*
+ * NOTE: There are two capture video devices in rkisp1, selfpath and mainpath.
+ *
+ * differences between selfpath and mainpath
+ * available mp sink input: isp
+ * available sp sink input : isp, dma(TODO)
+ * available mp sink pad fmts: yuv422, raw
+ * available sp sink pad fmts: yuv422, yuv420......
+ * available mp source fmts: yuv, raw, jpeg(TODO)
+ * available sp source fmts: yuv, rgb
+ */
+
+#define RKISP1_SP_DEV_NAME RKISP1_DRIVER_NAME "_selfpath"
+#define RKISP1_MP_DEV_NAME RKISP1_DRIVER_NAME "_mainpath"
+
+#define RKISP1_MIN_BUFFERS_NEEDED 3
+
+enum rkisp1_plane {
+ RKISP1_PLANE_Y = 0,
+ RKISP1_PLANE_CB = 1,
+ RKISP1_PLANE_CR = 2
+};
+
+/*
+ * @fourcc: pixel format
+ * @fmt_type: helper filed for pixel format
+ * @uv_swap: if cb cr swapped, for yuv
+ * @write_format: defines how YCbCr self picture data is written to memory
+ * @output_format: defines sp output format
+ * @mbus: the mbus code on the src resizer pad that matches the pixel format
+ */
+struct rkisp1_capture_fmt_cfg {
+ u32 fourcc;
+ u8 uv_swap;
+ u32 write_format;
+ u32 output_format;
+ u32 mbus;
+};
+
+struct rkisp1_capture_ops {
+ void (*config)(struct rkisp1_capture *cap);
+ void (*stop)(struct rkisp1_capture *cap);
+ void (*enable)(struct rkisp1_capture *cap);
+ void (*disable)(struct rkisp1_capture *cap);
+ void (*set_data_path)(struct rkisp1_capture *cap);
+ bool (*is_stopped)(struct rkisp1_capture *cap);
+};
+
+struct rkisp1_capture_config {
+ const struct rkisp1_capture_fmt_cfg *fmts;
+ int fmt_size;
+ struct {
+ u32 y_size_init;
+ u32 cb_size_init;
+ u32 cr_size_init;
+ u32 y_base_ad_init;
+ u32 cb_base_ad_init;
+ u32 cr_base_ad_init;
+ u32 y_offs_cnt_init;
+ u32 cb_offs_cnt_init;
+ u32 cr_offs_cnt_init;
+ } mi;
+};
+
+/*
+ * The supported pixel formats for mainpath. NOTE, pixel formats with identical 'mbus'
+ * are grouped together. This is assumed and used by the function rkisp1_cap_enum_mbus_codes
+ */
+static const struct rkisp1_capture_fmt_cfg rkisp1_mp_fmts[] = {
+ /* yuv422 */
+ {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .uv_swap = 0,
+ .write_format = RKISP1_MI_CTRL_MP_WRITE_YUVINT,
+ .mbus = MEDIA_BUS_FMT_YUYV8_2X8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YUV422P,
+ .uv_swap = 0,
+ .write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8,
+ .mbus = MEDIA_BUS_FMT_YUYV8_2X8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV16,
+ .uv_swap = 0,
+ .write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_SPLA,
+ .mbus = MEDIA_BUS_FMT_YUYV8_2X8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV61,
+ .uv_swap = 1,
+ .write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_SPLA,
+ .mbus = MEDIA_BUS_FMT_YUYV8_2X8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV16M,
+ .uv_swap = 0,
+ .write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_SPLA,
+ .mbus = MEDIA_BUS_FMT_YUYV8_2X8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV61M,
+ .uv_swap = 1,
+ .write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_SPLA,
+ .mbus = MEDIA_BUS_FMT_YUYV8_2X8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YVU422M,
+ .uv_swap = 1,
+ .write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8,
+ .mbus = MEDIA_BUS_FMT_YUYV8_2X8,
+ },
+ /* yuv400 */
+ {
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .uv_swap = 0,
+ .write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8,
+ .mbus = MEDIA_BUS_FMT_YUYV8_2X8,
+ },
+ /* yuv420 */
+ {
+ .fourcc = V4L2_PIX_FMT_NV21,
+ .uv_swap = 1,
+ .write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_SPLA,
+ .mbus = MEDIA_BUS_FMT_YUYV8_1_5X8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .uv_swap = 0,
+ .write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_SPLA,
+ .mbus = MEDIA_BUS_FMT_YUYV8_1_5X8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV21M,
+ .uv_swap = 1,
+ .write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_SPLA,
+ .mbus = MEDIA_BUS_FMT_YUYV8_1_5X8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV12M,
+ .uv_swap = 0,
+ .write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_SPLA,
+ .mbus = MEDIA_BUS_FMT_YUYV8_1_5X8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .uv_swap = 0,
+ .write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8,
+ .mbus = MEDIA_BUS_FMT_YUYV8_1_5X8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YVU420,
+ .uv_swap = 1,
+ .write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8,
+ .mbus = MEDIA_BUS_FMT_YUYV8_1_5X8,
+ },
+ /* raw */
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB8,
+ .write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8,
+ .mbus = MEDIA_BUS_FMT_SRGGB8_1X8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ .write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8,
+ .mbus = MEDIA_BUS_FMT_SGRBG8_1X8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
+ .write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8,
+ .mbus = MEDIA_BUS_FMT_SGBRG8_1X8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ .write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8,
+ .mbus = MEDIA_BUS_FMT_SBGGR8_1X8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SRGGB10,
+ .write_format = RKISP1_MI_CTRL_MP_WRITE_RAW12,
+ .mbus = MEDIA_BUS_FMT_SRGGB10_1X10,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG10,
+ .write_format = RKISP1_MI_CTRL_MP_WRITE_RAW12,
+ .mbus = MEDIA_BUS_FMT_SGRBG10_1X10,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG10,
+ .write_format = RKISP1_MI_CTRL_MP_WRITE_RAW12,
+ .mbus = MEDIA_BUS_FMT_SGBRG10_1X10,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SBGGR10,
+ .write_format = RKISP1_MI_CTRL_MP_WRITE_RAW12,
+ .mbus = MEDIA_BUS_FMT_SBGGR10_1X10,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SRGGB12,
+ .write_format = RKISP1_MI_CTRL_MP_WRITE_RAW12,
+ .mbus = MEDIA_BUS_FMT_SRGGB12_1X12,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG12,
+ .write_format = RKISP1_MI_CTRL_MP_WRITE_RAW12,
+ .mbus = MEDIA_BUS_FMT_SGRBG12_1X12,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG12,
+ .write_format = RKISP1_MI_CTRL_MP_WRITE_RAW12,
+ .mbus = MEDIA_BUS_FMT_SGBRG12_1X12,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SBGGR12,
+ .write_format = RKISP1_MI_CTRL_MP_WRITE_RAW12,
+ .mbus = MEDIA_BUS_FMT_SBGGR12_1X12,
+ },
+};
+
+/*
+ * The supported pixel formats for selfpath. NOTE, pixel formats with identical 'mbus'
+ * are grouped together. This is assumed and used by the function rkisp1_cap_enum_mbus_codes
+ */
+static const struct rkisp1_capture_fmt_cfg rkisp1_sp_fmts[] = {
+ /* yuv422 */
+ {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .uv_swap = 0,
+ .write_format = RKISP1_MI_CTRL_SP_WRITE_INT,
+ .output_format = RKISP1_MI_CTRL_SP_OUTPUT_YUV422,
+ .mbus = MEDIA_BUS_FMT_YUYV8_2X8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YUV422P,
+ .uv_swap = 0,
+ .write_format = RKISP1_MI_CTRL_SP_WRITE_PLA,
+ .output_format = RKISP1_MI_CTRL_SP_OUTPUT_YUV422,
+ .mbus = MEDIA_BUS_FMT_YUYV8_2X8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV16,
+ .uv_swap = 0,
+ .write_format = RKISP1_MI_CTRL_SP_WRITE_SPLA,
+ .output_format = RKISP1_MI_CTRL_SP_OUTPUT_YUV422,
+ .mbus = MEDIA_BUS_FMT_YUYV8_2X8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV61,
+ .uv_swap = 1,
+ .write_format = RKISP1_MI_CTRL_SP_WRITE_SPLA,
+ .output_format = RKISP1_MI_CTRL_SP_OUTPUT_YUV422,
+ .mbus = MEDIA_BUS_FMT_YUYV8_2X8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV16M,
+ .uv_swap = 0,
+ .write_format = RKISP1_MI_CTRL_SP_WRITE_SPLA,
+ .output_format = RKISP1_MI_CTRL_SP_OUTPUT_YUV422,
+ .mbus = MEDIA_BUS_FMT_YUYV8_2X8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV61M,
+ .uv_swap = 1,
+ .write_format = RKISP1_MI_CTRL_SP_WRITE_SPLA,
+ .output_format = RKISP1_MI_CTRL_SP_OUTPUT_YUV422,
+ .mbus = MEDIA_BUS_FMT_YUYV8_2X8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YVU422M,
+ .uv_swap = 1,
+ .write_format = RKISP1_MI_CTRL_SP_WRITE_PLA,
+ .output_format = RKISP1_MI_CTRL_SP_OUTPUT_YUV422,
+ .mbus = MEDIA_BUS_FMT_YUYV8_2X8,
+ },
+ /* yuv400 */
+ {
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .uv_swap = 0,
+ .write_format = RKISP1_MI_CTRL_SP_WRITE_PLA,
+ .output_format = RKISP1_MI_CTRL_SP_OUTPUT_YUV422,
+ .mbus = MEDIA_BUS_FMT_YUYV8_2X8,
+ },
+ /* rgb */
+ {
+ .fourcc = V4L2_PIX_FMT_XBGR32,
+ .write_format = RKISP1_MI_CTRL_SP_WRITE_PLA,
+ .output_format = RKISP1_MI_CTRL_SP_OUTPUT_RGB888,
+ .mbus = MEDIA_BUS_FMT_YUYV8_2X8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .write_format = RKISP1_MI_CTRL_SP_WRITE_PLA,
+ .output_format = RKISP1_MI_CTRL_SP_OUTPUT_RGB565,
+ .mbus = MEDIA_BUS_FMT_YUYV8_2X8,
+ },
+ /* yuv420 */
+ {
+ .fourcc = V4L2_PIX_FMT_NV21,
+ .uv_swap = 1,
+ .write_format = RKISP1_MI_CTRL_SP_WRITE_SPLA,
+ .output_format = RKISP1_MI_CTRL_SP_OUTPUT_YUV420,
+ .mbus = MEDIA_BUS_FMT_YUYV8_1_5X8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .uv_swap = 0,
+ .write_format = RKISP1_MI_CTRL_SP_WRITE_SPLA,
+ .output_format = RKISP1_MI_CTRL_SP_OUTPUT_YUV420,
+ .mbus = MEDIA_BUS_FMT_YUYV8_1_5X8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV21M,
+ .uv_swap = 1,
+ .write_format = RKISP1_MI_CTRL_SP_WRITE_SPLA,
+ .output_format = RKISP1_MI_CTRL_SP_OUTPUT_YUV420,
+ .mbus = MEDIA_BUS_FMT_YUYV8_1_5X8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV12M,
+ .uv_swap = 0,
+ .write_format = RKISP1_MI_CTRL_SP_WRITE_SPLA,
+ .output_format = RKISP1_MI_CTRL_SP_OUTPUT_YUV420,
+ .mbus = MEDIA_BUS_FMT_YUYV8_1_5X8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .uv_swap = 0,
+ .write_format = RKISP1_MI_CTRL_SP_WRITE_PLA,
+ .output_format = RKISP1_MI_CTRL_SP_OUTPUT_YUV420,
+ .mbus = MEDIA_BUS_FMT_YUYV8_1_5X8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YVU420,
+ .uv_swap = 1,
+ .write_format = RKISP1_MI_CTRL_SP_WRITE_PLA,
+ .output_format = RKISP1_MI_CTRL_SP_OUTPUT_YUV420,
+ .mbus = MEDIA_BUS_FMT_YUYV8_1_5X8,
+ },
+};
+
+static const struct rkisp1_capture_config rkisp1_capture_config_mp = {
+ .fmts = rkisp1_mp_fmts,
+ .fmt_size = ARRAY_SIZE(rkisp1_mp_fmts),
+ .mi = {
+ .y_size_init = RKISP1_CIF_MI_MP_Y_SIZE_INIT,
+ .cb_size_init = RKISP1_CIF_MI_MP_CB_SIZE_INIT,
+ .cr_size_init = RKISP1_CIF_MI_MP_CR_SIZE_INIT,
+ .y_base_ad_init = RKISP1_CIF_MI_MP_Y_BASE_AD_INIT,
+ .cb_base_ad_init = RKISP1_CIF_MI_MP_CB_BASE_AD_INIT,
+ .cr_base_ad_init = RKISP1_CIF_MI_MP_CR_BASE_AD_INIT,
+ .y_offs_cnt_init = RKISP1_CIF_MI_MP_Y_OFFS_CNT_INIT,
+ .cb_offs_cnt_init = RKISP1_CIF_MI_MP_CB_OFFS_CNT_INIT,
+ .cr_offs_cnt_init = RKISP1_CIF_MI_MP_CR_OFFS_CNT_INIT,
+ },
+};
+
+static const struct rkisp1_capture_config rkisp1_capture_config_sp = {
+ .fmts = rkisp1_sp_fmts,
+ .fmt_size = ARRAY_SIZE(rkisp1_sp_fmts),
+ .mi = {
+ .y_size_init = RKISP1_CIF_MI_SP_Y_SIZE_INIT,
+ .cb_size_init = RKISP1_CIF_MI_SP_CB_SIZE_INIT,
+ .cr_size_init = RKISP1_CIF_MI_SP_CR_SIZE_INIT,
+ .y_base_ad_init = RKISP1_CIF_MI_SP_Y_BASE_AD_INIT,
+ .cb_base_ad_init = RKISP1_CIF_MI_SP_CB_BASE_AD_INIT,
+ .cr_base_ad_init = RKISP1_CIF_MI_SP_CR_BASE_AD_INIT,
+ .y_offs_cnt_init = RKISP1_CIF_MI_SP_Y_OFFS_CNT_INIT,
+ .cb_offs_cnt_init = RKISP1_CIF_MI_SP_CB_OFFS_CNT_INIT,
+ .cr_offs_cnt_init = RKISP1_CIF_MI_SP_CR_OFFS_CNT_INIT,
+ },
+};
+
+static inline struct rkisp1_vdev_node *
+rkisp1_vdev_to_node(struct video_device *vdev)
+{
+ return container_of(vdev, struct rkisp1_vdev_node, vdev);
+}
+
+int rkisp1_cap_enum_mbus_codes(struct rkisp1_capture *cap,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ const struct rkisp1_capture_fmt_cfg *fmts = cap->config->fmts;
+ /*
+ * initialize curr_mbus to non existing mbus code 0 to ensure it is
+ * different from fmts[0].mbus
+ */
+ u32 curr_mbus = 0;
+ int i, n = 0;
+
+ for (i = 0; i < cap->config->fmt_size; i++) {
+ if (fmts[i].mbus == curr_mbus)
+ continue;
+
+ curr_mbus = fmts[i].mbus;
+ if (n++ == code->index) {
+ code->code = curr_mbus;
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+/* ----------------------------------------------------------------------------
+ * Stream operations for self-picture path (sp) and main-picture path (mp)
+ */
+
+static void rkisp1_mi_config_ctrl(struct rkisp1_capture *cap)
+{
+ u32 mi_ctrl = rkisp1_read(cap->rkisp1, RKISP1_CIF_MI_CTRL);
+
+ mi_ctrl &= ~GENMASK(17, 16);
+ mi_ctrl |= RKISP1_CIF_MI_CTRL_BURST_LEN_LUM_64;
+
+ mi_ctrl &= ~GENMASK(19, 18);
+ mi_ctrl |= RKISP1_CIF_MI_CTRL_BURST_LEN_CHROM_64;
+
+ mi_ctrl |= RKISP1_CIF_MI_CTRL_INIT_BASE_EN |
+ RKISP1_CIF_MI_CTRL_INIT_OFFSET_EN;
+
+ rkisp1_write(cap->rkisp1, RKISP1_CIF_MI_CTRL, mi_ctrl);
+}
+
+static u32 rkisp1_pixfmt_comp_size(const struct v4l2_pix_format_mplane *pixm,
+ unsigned int component)
+{
+ /*
+ * If packed format, then plane_fmt[0].sizeimage is the sum of all
+ * components, so we need to calculate just the size of Y component.
+ * See rkisp1_fill_pixfmt().
+ */
+ if (!component && pixm->num_planes == 1)
+ return pixm->plane_fmt[0].bytesperline * pixm->height;
+ return pixm->plane_fmt[component].sizeimage;
+}
+
+static void rkisp1_irq_frame_end_enable(struct rkisp1_capture *cap)
+{
+ u32 mi_imsc = rkisp1_read(cap->rkisp1, RKISP1_CIF_MI_IMSC);
+
+ mi_imsc |= RKISP1_CIF_MI_FRAME(cap);
+ rkisp1_write(cap->rkisp1, RKISP1_CIF_MI_IMSC, mi_imsc);
+}
+
+static void rkisp1_mp_config(struct rkisp1_capture *cap)
+{
+ const struct v4l2_pix_format_mplane *pixm = &cap->pix.fmt;
+ struct rkisp1_device *rkisp1 = cap->rkisp1;
+ u32 reg;
+
+ rkisp1_write(rkisp1, cap->config->mi.y_size_init,
+ rkisp1_pixfmt_comp_size(pixm, RKISP1_PLANE_Y));
+ rkisp1_write(rkisp1, cap->config->mi.cb_size_init,
+ rkisp1_pixfmt_comp_size(pixm, RKISP1_PLANE_CB));
+ rkisp1_write(rkisp1, cap->config->mi.cr_size_init,
+ rkisp1_pixfmt_comp_size(pixm, RKISP1_PLANE_CR));
+
+ rkisp1_irq_frame_end_enable(cap);
+
+ /* set uv swapping for semiplanar formats */
+ if (cap->pix.info->comp_planes == 2) {
+ reg = rkisp1_read(rkisp1, RKISP1_CIF_MI_XTD_FORMAT_CTRL);
+ if (cap->pix.cfg->uv_swap)
+ reg |= RKISP1_CIF_MI_XTD_FMT_CTRL_MP_CB_CR_SWAP;
+ else
+ reg &= ~RKISP1_CIF_MI_XTD_FMT_CTRL_MP_CB_CR_SWAP;
+ rkisp1_write(rkisp1, RKISP1_CIF_MI_XTD_FORMAT_CTRL, reg);
+ }
+
+ rkisp1_mi_config_ctrl(cap);
+
+ reg = rkisp1_read(rkisp1, RKISP1_CIF_MI_CTRL);
+ reg &= ~RKISP1_MI_CTRL_MP_FMT_MASK;
+ reg |= cap->pix.cfg->write_format;
+ rkisp1_write(rkisp1, RKISP1_CIF_MI_CTRL, reg);
+
+ reg = rkisp1_read(rkisp1, RKISP1_CIF_MI_CTRL);
+ reg |= RKISP1_CIF_MI_MP_AUTOUPDATE_ENABLE;
+ rkisp1_write(rkisp1, RKISP1_CIF_MI_CTRL, reg);
+}
+
+static void rkisp1_sp_config(struct rkisp1_capture *cap)
+{
+ const struct v4l2_pix_format_mplane *pixm = &cap->pix.fmt;
+ struct rkisp1_device *rkisp1 = cap->rkisp1;
+ u32 mi_ctrl, reg;
+
+ rkisp1_write(rkisp1, cap->config->mi.y_size_init,
+ rkisp1_pixfmt_comp_size(pixm, RKISP1_PLANE_Y));
+ rkisp1_write(rkisp1, cap->config->mi.cb_size_init,
+ rkisp1_pixfmt_comp_size(pixm, RKISP1_PLANE_CB));
+ rkisp1_write(rkisp1, cap->config->mi.cr_size_init,
+ rkisp1_pixfmt_comp_size(pixm, RKISP1_PLANE_CR));
+
+ rkisp1_write(rkisp1, RKISP1_CIF_MI_SP_Y_PIC_WIDTH, pixm->width);
+ rkisp1_write(rkisp1, RKISP1_CIF_MI_SP_Y_PIC_HEIGHT, pixm->height);
+ rkisp1_write(rkisp1, RKISP1_CIF_MI_SP_Y_LLENGTH, cap->sp_y_stride);
+
+ rkisp1_irq_frame_end_enable(cap);
+
+ /* set uv swapping for semiplanar formats */
+ if (cap->pix.info->comp_planes == 2) {
+ reg = rkisp1_read(rkisp1, RKISP1_CIF_MI_XTD_FORMAT_CTRL);
+ if (cap->pix.cfg->uv_swap)
+ reg |= RKISP1_CIF_MI_XTD_FMT_CTRL_SP_CB_CR_SWAP;
+ else
+ reg &= ~RKISP1_CIF_MI_XTD_FMT_CTRL_SP_CB_CR_SWAP;
+ rkisp1_write(rkisp1, RKISP1_CIF_MI_XTD_FORMAT_CTRL, reg);
+ }
+
+ rkisp1_mi_config_ctrl(cap);
+
+ mi_ctrl = rkisp1_read(rkisp1, RKISP1_CIF_MI_CTRL);
+ mi_ctrl &= ~RKISP1_MI_CTRL_SP_FMT_MASK;
+ mi_ctrl |= cap->pix.cfg->write_format |
+ RKISP1_MI_CTRL_SP_INPUT_YUV422 |
+ cap->pix.cfg->output_format |
+ RKISP1_CIF_MI_SP_AUTOUPDATE_ENABLE;
+ rkisp1_write(rkisp1, RKISP1_CIF_MI_CTRL, mi_ctrl);
+}
+
+static void rkisp1_mp_disable(struct rkisp1_capture *cap)
+{
+ u32 mi_ctrl = rkisp1_read(cap->rkisp1, RKISP1_CIF_MI_CTRL);
+
+ mi_ctrl &= ~(RKISP1_CIF_MI_CTRL_MP_ENABLE |
+ RKISP1_CIF_MI_CTRL_RAW_ENABLE);
+ rkisp1_write(cap->rkisp1, RKISP1_CIF_MI_CTRL, mi_ctrl);
+}
+
+static void rkisp1_sp_disable(struct rkisp1_capture *cap)
+{
+ u32 mi_ctrl = rkisp1_read(cap->rkisp1, RKISP1_CIF_MI_CTRL);
+
+ mi_ctrl &= ~RKISP1_CIF_MI_CTRL_SP_ENABLE;
+ rkisp1_write(cap->rkisp1, RKISP1_CIF_MI_CTRL, mi_ctrl);
+}
+
+static void rkisp1_mp_enable(struct rkisp1_capture *cap)
+{
+ u32 mi_ctrl;
+
+ rkisp1_mp_disable(cap);
+
+ mi_ctrl = rkisp1_read(cap->rkisp1, RKISP1_CIF_MI_CTRL);
+ if (v4l2_is_format_bayer(cap->pix.info))
+ mi_ctrl |= RKISP1_CIF_MI_CTRL_RAW_ENABLE;
+ /* YUV */
+ else
+ mi_ctrl |= RKISP1_CIF_MI_CTRL_MP_ENABLE;
+
+ rkisp1_write(cap->rkisp1, RKISP1_CIF_MI_CTRL, mi_ctrl);
+}
+
+static void rkisp1_sp_enable(struct rkisp1_capture *cap)
+{
+ u32 mi_ctrl = rkisp1_read(cap->rkisp1, RKISP1_CIF_MI_CTRL);
+
+ mi_ctrl |= RKISP1_CIF_MI_CTRL_SP_ENABLE;
+ rkisp1_write(cap->rkisp1, RKISP1_CIF_MI_CTRL, mi_ctrl);
+}
+
+static void rkisp1_mp_sp_stop(struct rkisp1_capture *cap)
+{
+ if (!cap->is_streaming)
+ return;
+ rkisp1_write(cap->rkisp1, RKISP1_CIF_MI_ICR, RKISP1_CIF_MI_FRAME(cap));
+ cap->ops->disable(cap);
+}
+
+static bool rkisp1_mp_is_stopped(struct rkisp1_capture *cap)
+{
+ u32 en = RKISP1_CIF_MI_CTRL_SHD_MP_IN_ENABLED |
+ RKISP1_CIF_MI_CTRL_SHD_RAW_OUT_ENABLED;
+
+ return !(rkisp1_read(cap->rkisp1, RKISP1_CIF_MI_CTRL_SHD) & en);
+}
+
+static bool rkisp1_sp_is_stopped(struct rkisp1_capture *cap)
+{
+ return !(rkisp1_read(cap->rkisp1, RKISP1_CIF_MI_CTRL_SHD) &
+ RKISP1_CIF_MI_CTRL_SHD_SP_IN_ENABLED);
+}
+
+static void rkisp1_mp_set_data_path(struct rkisp1_capture *cap)
+{
+ u32 dpcl = rkisp1_read(cap->rkisp1, RKISP1_CIF_VI_DPCL);
+
+ dpcl = dpcl | RKISP1_CIF_VI_DPCL_CHAN_MODE_MP |
+ RKISP1_CIF_VI_DPCL_MP_MUX_MRSZ_MI;
+ rkisp1_write(cap->rkisp1, RKISP1_CIF_VI_DPCL, dpcl);
+}
+
+static void rkisp1_sp_set_data_path(struct rkisp1_capture *cap)
+{
+ u32 dpcl = rkisp1_read(cap->rkisp1, RKISP1_CIF_VI_DPCL);
+
+ dpcl |= RKISP1_CIF_VI_DPCL_CHAN_MODE_SP;
+ rkisp1_write(cap->rkisp1, RKISP1_CIF_VI_DPCL, dpcl);
+}
+
+static const struct rkisp1_capture_ops rkisp1_capture_ops_mp = {
+ .config = rkisp1_mp_config,
+ .enable = rkisp1_mp_enable,
+ .disable = rkisp1_mp_disable,
+ .stop = rkisp1_mp_sp_stop,
+ .set_data_path = rkisp1_mp_set_data_path,
+ .is_stopped = rkisp1_mp_is_stopped,
+};
+
+static const struct rkisp1_capture_ops rkisp1_capture_ops_sp = {
+ .config = rkisp1_sp_config,
+ .enable = rkisp1_sp_enable,
+ .disable = rkisp1_sp_disable,
+ .stop = rkisp1_mp_sp_stop,
+ .set_data_path = rkisp1_sp_set_data_path,
+ .is_stopped = rkisp1_sp_is_stopped,
+};
+
+/* ----------------------------------------------------------------------------
+ * Frame buffer operations
+ */
+
+static int rkisp1_dummy_buf_create(struct rkisp1_capture *cap)
+{
+ const struct v4l2_pix_format_mplane *pixm = &cap->pix.fmt;
+ struct rkisp1_dummy_buffer *dummy_buf = &cap->buf.dummy;
+
+ dummy_buf->size = max3(rkisp1_pixfmt_comp_size(pixm, RKISP1_PLANE_Y),
+ rkisp1_pixfmt_comp_size(pixm, RKISP1_PLANE_CB),
+ rkisp1_pixfmt_comp_size(pixm, RKISP1_PLANE_CR));
+
+ /* The driver never access vaddr, no mapping is required */
+ dummy_buf->vaddr = dma_alloc_attrs(cap->rkisp1->dev,
+ dummy_buf->size,
+ &dummy_buf->dma_addr,
+ GFP_KERNEL,
+ DMA_ATTR_NO_KERNEL_MAPPING);
+ if (!dummy_buf->vaddr)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void rkisp1_dummy_buf_destroy(struct rkisp1_capture *cap)
+{
+ dma_free_attrs(cap->rkisp1->dev,
+ cap->buf.dummy.size, cap->buf.dummy.vaddr,
+ cap->buf.dummy.dma_addr, DMA_ATTR_NO_KERNEL_MAPPING);
+}
+
+static void rkisp1_set_next_buf(struct rkisp1_capture *cap)
+{
+ cap->buf.curr = cap->buf.next;
+ cap->buf.next = NULL;
+
+ if (!list_empty(&cap->buf.queue)) {
+ u32 *buff_addr;
+
+ cap->buf.next = list_first_entry(&cap->buf.queue, struct rkisp1_buffer, queue);
+ list_del(&cap->buf.next->queue);
+
+ buff_addr = cap->buf.next->buff_addr;
+
+ rkisp1_write(cap->rkisp1, cap->config->mi.y_base_ad_init,
+ buff_addr[RKISP1_PLANE_Y]);
+ /*
+ * In order to support grey format we capture
+ * YUV422 planar format from the camera and
+ * set the U and V planes to the dummy buffer
+ */
+ if (cap->pix.cfg->fourcc == V4L2_PIX_FMT_GREY) {
+ rkisp1_write(cap->rkisp1,
+ cap->config->mi.cb_base_ad_init,
+ cap->buf.dummy.dma_addr);
+ rkisp1_write(cap->rkisp1,
+ cap->config->mi.cr_base_ad_init,
+ cap->buf.dummy.dma_addr);
+ } else {
+ rkisp1_write(cap->rkisp1,
+ cap->config->mi.cb_base_ad_init,
+ buff_addr[RKISP1_PLANE_CB]);
+ rkisp1_write(cap->rkisp1,
+ cap->config->mi.cr_base_ad_init,
+ buff_addr[RKISP1_PLANE_CR]);
+ }
+ } else {
+ /*
+ * Use the dummy space allocated by dma_alloc_coherent to
+ * throw data if there is no available buffer.
+ */
+ rkisp1_write(cap->rkisp1, cap->config->mi.y_base_ad_init,
+ cap->buf.dummy.dma_addr);
+ rkisp1_write(cap->rkisp1, cap->config->mi.cb_base_ad_init,
+ cap->buf.dummy.dma_addr);
+ rkisp1_write(cap->rkisp1, cap->config->mi.cr_base_ad_init,
+ cap->buf.dummy.dma_addr);
+ }
+
+ /* Set plane offsets */
+ rkisp1_write(cap->rkisp1, cap->config->mi.y_offs_cnt_init, 0);
+ rkisp1_write(cap->rkisp1, cap->config->mi.cb_offs_cnt_init, 0);
+ rkisp1_write(cap->rkisp1, cap->config->mi.cr_offs_cnt_init, 0);
+}
+
+/*
+ * This function is called when a frame end comes. The next frame
+ * is processing and we should set up buffer for next-next frame,
+ * otherwise it will overflow.
+ */
+static void rkisp1_handle_buffer(struct rkisp1_capture *cap)
+{
+ struct rkisp1_isp *isp = &cap->rkisp1->isp;
+ struct rkisp1_buffer *curr_buf;
+
+ spin_lock(&cap->buf.lock);
+ curr_buf = cap->buf.curr;
+
+ if (curr_buf) {
+ curr_buf->vb.sequence = isp->frame_sequence;
+ curr_buf->vb.vb2_buf.timestamp = ktime_get_boottime_ns();
+ curr_buf->vb.field = V4L2_FIELD_NONE;
+ vb2_buffer_done(&curr_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+ } else {
+ cap->rkisp1->debug.frame_drop[cap->id]++;
+ }
+
+ rkisp1_set_next_buf(cap);
+ spin_unlock(&cap->buf.lock);
+}
+
+irqreturn_t rkisp1_capture_isr(int irq, void *ctx)
+{
+ struct device *dev = ctx;
+ struct rkisp1_device *rkisp1 = dev_get_drvdata(dev);
+ unsigned int i;
+ u32 status;
+
+ status = rkisp1_read(rkisp1, RKISP1_CIF_MI_MIS);
+ if (!status)
+ return IRQ_NONE;
+
+ rkisp1_write(rkisp1, RKISP1_CIF_MI_ICR, status);
+
+ for (i = 0; i < ARRAY_SIZE(rkisp1->capture_devs); ++i) {
+ struct rkisp1_capture *cap = &rkisp1->capture_devs[i];
+
+ if (!(status & RKISP1_CIF_MI_FRAME(cap)))
+ continue;
+ if (!cap->is_stopping) {
+ rkisp1_handle_buffer(cap);
+ continue;
+ }
+ /*
+ * Make sure stream is actually stopped, whose state
+ * can be read from the shadow register, before
+ * wake_up() thread which would immediately free all
+ * frame buffers. stop() takes effect at the next
+ * frame end that sync the configurations to shadow
+ * regs.
+ */
+ if (!cap->ops->is_stopped(cap)) {
+ cap->ops->stop(cap);
+ continue;
+ }
+ cap->is_stopping = false;
+ cap->is_streaming = false;
+ wake_up(&cap->done);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/* ----------------------------------------------------------------------------
+ * Vb2 operations
+ */
+
+static int rkisp1_vb2_queue_setup(struct vb2_queue *queue,
+ unsigned int *num_buffers,
+ unsigned int *num_planes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct rkisp1_capture *cap = queue->drv_priv;
+ const struct v4l2_pix_format_mplane *pixm = &cap->pix.fmt;
+ unsigned int i;
+
+ if (*num_planes) {
+ if (*num_planes != pixm->num_planes)
+ return -EINVAL;
+
+ for (i = 0; i < pixm->num_planes; i++)
+ if (sizes[i] < pixm->plane_fmt[i].sizeimage)
+ return -EINVAL;
+ } else {
+ *num_planes = pixm->num_planes;
+ for (i = 0; i < pixm->num_planes; i++)
+ sizes[i] = pixm->plane_fmt[i].sizeimage;
+ }
+
+ return 0;
+}
+
+static int rkisp1_vb2_buf_init(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct rkisp1_buffer *ispbuf =
+ container_of(vbuf, struct rkisp1_buffer, vb);
+ struct rkisp1_capture *cap = vb->vb2_queue->drv_priv;
+ const struct v4l2_pix_format_mplane *pixm = &cap->pix.fmt;
+ unsigned int i;
+
+ memset(ispbuf->buff_addr, 0, sizeof(ispbuf->buff_addr));
+ for (i = 0; i < pixm->num_planes; i++)
+ ispbuf->buff_addr[i] = vb2_dma_contig_plane_dma_addr(vb, i);
+
+ /* Convert to non-MPLANE */
+ if (pixm->num_planes == 1) {
+ ispbuf->buff_addr[RKISP1_PLANE_CB] =
+ ispbuf->buff_addr[RKISP1_PLANE_Y] +
+ rkisp1_pixfmt_comp_size(pixm, RKISP1_PLANE_Y);
+ ispbuf->buff_addr[RKISP1_PLANE_CR] =
+ ispbuf->buff_addr[RKISP1_PLANE_CB] +
+ rkisp1_pixfmt_comp_size(pixm, RKISP1_PLANE_CB);
+ }
+
+ /*
+ * uv swap can be supported for planar formats by switching
+ * the address of cb and cr
+ */
+ if (cap->pix.info->comp_planes == 3 && cap->pix.cfg->uv_swap)
+ swap(ispbuf->buff_addr[RKISP1_PLANE_CR],
+ ispbuf->buff_addr[RKISP1_PLANE_CB]);
+ return 0;
+}
+
+static void rkisp1_vb2_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct rkisp1_buffer *ispbuf =
+ container_of(vbuf, struct rkisp1_buffer, vb);
+ struct rkisp1_capture *cap = vb->vb2_queue->drv_priv;
+
+ spin_lock_irq(&cap->buf.lock);
+ list_add_tail(&ispbuf->queue, &cap->buf.queue);
+ spin_unlock_irq(&cap->buf.lock);
+}
+
+static int rkisp1_vb2_buf_prepare(struct vb2_buffer *vb)
+{
+ struct rkisp1_capture *cap = vb->vb2_queue->drv_priv;
+ unsigned int i;
+
+ for (i = 0; i < cap->pix.fmt.num_planes; i++) {
+ unsigned long size = cap->pix.fmt.plane_fmt[i].sizeimage;
+
+ if (vb2_plane_size(vb, i) < size) {
+ dev_err(cap->rkisp1->dev,
+ "User buffer too small (%ld < %ld)\n",
+ vb2_plane_size(vb, i), size);
+ return -EINVAL;
+ }
+ vb2_set_plane_payload(vb, i, size);
+ }
+
+ return 0;
+}
+
+static void rkisp1_return_all_buffers(struct rkisp1_capture *cap,
+ enum vb2_buffer_state state)
+{
+ struct rkisp1_buffer *buf;
+
+ spin_lock_irq(&cap->buf.lock);
+ if (cap->buf.curr) {
+ vb2_buffer_done(&cap->buf.curr->vb.vb2_buf, state);
+ cap->buf.curr = NULL;
+ }
+ if (cap->buf.next) {
+ vb2_buffer_done(&cap->buf.next->vb.vb2_buf, state);
+ cap->buf.next = NULL;
+ }
+ while (!list_empty(&cap->buf.queue)) {
+ buf = list_first_entry(&cap->buf.queue,
+ struct rkisp1_buffer, queue);
+ list_del(&buf->queue);
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
+ }
+ spin_unlock_irq(&cap->buf.lock);
+}
+
+/*
+ * Most registers inside the rockchip ISP1 have shadow register since
+ * they must not be changed while processing a frame.
+ * Usually, each sub-module updates its shadow register after
+ * processing the last pixel of a frame.
+ */
+static void rkisp1_cap_stream_enable(struct rkisp1_capture *cap)
+{
+ struct rkisp1_device *rkisp1 = cap->rkisp1;
+ struct rkisp1_capture *other = &rkisp1->capture_devs[cap->id ^ 1];
+
+ cap->ops->set_data_path(cap);
+ cap->ops->config(cap);
+
+ /* Setup a buffer for the next frame */
+ spin_lock_irq(&cap->buf.lock);
+ rkisp1_set_next_buf(cap);
+ cap->ops->enable(cap);
+ /* It's safe to configure ACTIVE and SHADOW registers for the
+ * first stream. While when the second is starting, do NOT
+ * force update because it also updates the first one.
+ *
+ * The latter case would drop one more buffer(that is 2) since
+ * there's no buffer in a shadow register when the second FE received.
+ * This's also required because the second FE maybe corrupt
+ * especially when run at 120fps.
+ */
+ if (!other->is_streaming) {
+ /* force cfg update */
+ rkisp1_write(rkisp1, RKISP1_CIF_MI_INIT,
+ RKISP1_CIF_MI_INIT_SOFT_UPD);
+ rkisp1_set_next_buf(cap);
+ }
+ spin_unlock_irq(&cap->buf.lock);
+ cap->is_streaming = true;
+}
+
+static void rkisp1_cap_stream_disable(struct rkisp1_capture *cap)
+{
+ int ret;
+
+ /* Stream should stop in interrupt. If it doesn't, stop it by force. */
+ cap->is_stopping = true;
+ ret = wait_event_timeout(cap->done,
+ !cap->is_streaming,
+ msecs_to_jiffies(1000));
+ if (!ret) {
+ cap->rkisp1->debug.stop_timeout[cap->id]++;
+ cap->ops->stop(cap);
+ cap->is_stopping = false;
+ cap->is_streaming = false;
+ }
+}
+
+/*
+ * rkisp1_pipeline_stream_disable - disable nodes in the pipeline
+ *
+ * Call s_stream(false) in the reverse order from
+ * rkisp1_pipeline_stream_enable() and disable the DMA engine.
+ * Should be called before video_device_pipeline_stop()
+ */
+static void rkisp1_pipeline_stream_disable(struct rkisp1_capture *cap)
+ __must_hold(&cap->rkisp1->stream_lock)
+{
+ struct rkisp1_device *rkisp1 = cap->rkisp1;
+
+ rkisp1_cap_stream_disable(cap);
+
+ /*
+ * If the other capture is streaming, isp and sensor nodes shouldn't
+ * be disabled, skip them.
+ */
+ if (rkisp1->pipe.start_count < 2)
+ v4l2_subdev_call(&rkisp1->isp.sd, video, s_stream, false);
+
+ v4l2_subdev_call(&rkisp1->resizer_devs[cap->id].sd, video, s_stream,
+ false);
+}
+
+/*
+ * rkisp1_pipeline_stream_enable - enable nodes in the pipeline
+ *
+ * Enable the DMA Engine and call s_stream(true) through the pipeline.
+ * Should be called after video_device_pipeline_start()
+ */
+static int rkisp1_pipeline_stream_enable(struct rkisp1_capture *cap)
+ __must_hold(&cap->rkisp1->stream_lock)
+{
+ struct rkisp1_device *rkisp1 = cap->rkisp1;
+ int ret;
+
+ rkisp1_cap_stream_enable(cap);
+
+ ret = v4l2_subdev_call(&rkisp1->resizer_devs[cap->id].sd, video,
+ s_stream, true);
+ if (ret)
+ goto err_disable_cap;
+
+ /*
+ * If the other capture is streaming, isp and sensor nodes are already
+ * enabled, skip them.
+ */
+ if (rkisp1->pipe.start_count > 1)
+ return 0;
+
+ ret = v4l2_subdev_call(&rkisp1->isp.sd, video, s_stream, true);
+ if (ret)
+ goto err_disable_rsz;
+
+ return 0;
+
+err_disable_rsz:
+ v4l2_subdev_call(&rkisp1->resizer_devs[cap->id].sd, video, s_stream,
+ false);
+err_disable_cap:
+ rkisp1_cap_stream_disable(cap);
+
+ return ret;
+}
+
+static void rkisp1_vb2_stop_streaming(struct vb2_queue *queue)
+{
+ struct rkisp1_capture *cap = queue->drv_priv;
+ struct rkisp1_vdev_node *node = &cap->vnode;
+ struct rkisp1_device *rkisp1 = cap->rkisp1;
+ int ret;
+
+ mutex_lock(&cap->rkisp1->stream_lock);
+
+ rkisp1_pipeline_stream_disable(cap);
+
+ rkisp1_return_all_buffers(cap, VB2_BUF_STATE_ERROR);
+
+ v4l2_pipeline_pm_put(&node->vdev.entity);
+ ret = pm_runtime_put(rkisp1->dev);
+ if (ret < 0)
+ dev_err(rkisp1->dev, "power down failed error:%d\n", ret);
+
+ rkisp1_dummy_buf_destroy(cap);
+
+ video_device_pipeline_stop(&node->vdev);
+
+ mutex_unlock(&cap->rkisp1->stream_lock);
+}
+
+static int
+rkisp1_vb2_start_streaming(struct vb2_queue *queue, unsigned int count)
+{
+ struct rkisp1_capture *cap = queue->drv_priv;
+ struct media_entity *entity = &cap->vnode.vdev.entity;
+ int ret;
+
+ mutex_lock(&cap->rkisp1->stream_lock);
+
+ ret = video_device_pipeline_start(&cap->vnode.vdev, &cap->rkisp1->pipe);
+ if (ret) {
+ dev_err(cap->rkisp1->dev, "start pipeline failed %d\n", ret);
+ goto err_ret_buffers;
+ }
+
+ ret = rkisp1_dummy_buf_create(cap);
+ if (ret)
+ goto err_pipeline_stop;
+
+ ret = pm_runtime_resume_and_get(cap->rkisp1->dev);
+ if (ret < 0) {
+ dev_err(cap->rkisp1->dev, "power up failed %d\n", ret);
+ goto err_destroy_dummy;
+ }
+ ret = v4l2_pipeline_pm_get(entity);
+ if (ret) {
+ dev_err(cap->rkisp1->dev, "open cif pipeline failed %d\n", ret);
+ goto err_pipe_pm_put;
+ }
+
+ ret = rkisp1_pipeline_stream_enable(cap);
+ if (ret)
+ goto err_v4l2_pm_put;
+
+ mutex_unlock(&cap->rkisp1->stream_lock);
+
+ return 0;
+
+err_v4l2_pm_put:
+ v4l2_pipeline_pm_put(entity);
+err_pipe_pm_put:
+ pm_runtime_put(cap->rkisp1->dev);
+err_destroy_dummy:
+ rkisp1_dummy_buf_destroy(cap);
+err_pipeline_stop:
+ video_device_pipeline_stop(&cap->vnode.vdev);
+err_ret_buffers:
+ rkisp1_return_all_buffers(cap, VB2_BUF_STATE_QUEUED);
+ mutex_unlock(&cap->rkisp1->stream_lock);
+
+ return ret;
+}
+
+static const struct vb2_ops rkisp1_vb2_ops = {
+ .queue_setup = rkisp1_vb2_queue_setup,
+ .buf_init = rkisp1_vb2_buf_init,
+ .buf_queue = rkisp1_vb2_buf_queue,
+ .buf_prepare = rkisp1_vb2_buf_prepare,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .stop_streaming = rkisp1_vb2_stop_streaming,
+ .start_streaming = rkisp1_vb2_start_streaming,
+};
+
+/* ----------------------------------------------------------------------------
+ * IOCTLs operations
+ */
+
+static const struct v4l2_format_info *
+rkisp1_fill_pixfmt(struct v4l2_pix_format_mplane *pixm,
+ enum rkisp1_stream_id id)
+{
+ struct v4l2_plane_pix_format *plane_y = &pixm->plane_fmt[0];
+ const struct v4l2_format_info *info;
+ unsigned int i;
+ u32 stride;
+
+ memset(pixm->plane_fmt, 0, sizeof(pixm->plane_fmt));
+ info = v4l2_format_info(pixm->pixelformat);
+ pixm->num_planes = info->mem_planes;
+ stride = info->bpp[0] * pixm->width;
+ /* Self path supports custom stride but Main path doesn't */
+ if (id == RKISP1_MAINPATH || plane_y->bytesperline < stride)
+ plane_y->bytesperline = stride;
+ plane_y->sizeimage = plane_y->bytesperline * pixm->height;
+
+ /* normalize stride to pixels per line */
+ stride = DIV_ROUND_UP(plane_y->bytesperline, info->bpp[0]);
+
+ for (i = 1; i < info->comp_planes; i++) {
+ struct v4l2_plane_pix_format *plane = &pixm->plane_fmt[i];
+
+ /* bytesperline for other components derive from Y component */
+ plane->bytesperline = DIV_ROUND_UP(stride, info->hdiv) *
+ info->bpp[i];
+ plane->sizeimage = plane->bytesperline *
+ DIV_ROUND_UP(pixm->height, info->vdiv);
+ }
+
+ /*
+ * If pixfmt is packed, then plane_fmt[0] should contain the total size
+ * considering all components. plane_fmt[i] for i > 0 should be ignored
+ * by userspace as mem_planes == 1, but we are keeping information there
+ * for convenience.
+ */
+ if (info->mem_planes == 1)
+ for (i = 1; i < info->comp_planes; i++)
+ plane_y->sizeimage += pixm->plane_fmt[i].sizeimage;
+
+ return info;
+}
+
+static const struct rkisp1_capture_fmt_cfg *
+rkisp1_find_fmt_cfg(const struct rkisp1_capture *cap, const u32 pixelfmt)
+{
+ unsigned int i;
+
+ for (i = 0; i < cap->config->fmt_size; i++) {
+ if (cap->config->fmts[i].fourcc == pixelfmt)
+ return &cap->config->fmts[i];
+ }
+ return NULL;
+}
+
+static void rkisp1_try_fmt(const struct rkisp1_capture *cap,
+ struct v4l2_pix_format_mplane *pixm,
+ const struct rkisp1_capture_fmt_cfg **fmt_cfg,
+ const struct v4l2_format_info **fmt_info)
+{
+ const struct rkisp1_capture_config *config = cap->config;
+ const struct rkisp1_capture_fmt_cfg *fmt;
+ const struct v4l2_format_info *info;
+ static const unsigned int max_widths[] = {
+ RKISP1_RSZ_MP_SRC_MAX_WIDTH, RKISP1_RSZ_SP_SRC_MAX_WIDTH
+ };
+ static const unsigned int max_heights[] = {
+ RKISP1_RSZ_MP_SRC_MAX_HEIGHT, RKISP1_RSZ_SP_SRC_MAX_HEIGHT
+ };
+
+ fmt = rkisp1_find_fmt_cfg(cap, pixm->pixelformat);
+ if (!fmt) {
+ fmt = config->fmts;
+ pixm->pixelformat = fmt->fourcc;
+ }
+
+ pixm->width = clamp_t(u32, pixm->width,
+ RKISP1_RSZ_SRC_MIN_WIDTH, max_widths[cap->id]);
+ pixm->height = clamp_t(u32, pixm->height,
+ RKISP1_RSZ_SRC_MIN_HEIGHT, max_heights[cap->id]);
+
+ pixm->field = V4L2_FIELD_NONE;
+ pixm->colorspace = V4L2_COLORSPACE_DEFAULT;
+ pixm->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ pixm->quantization = V4L2_QUANTIZATION_DEFAULT;
+
+ info = rkisp1_fill_pixfmt(pixm, cap->id);
+
+ if (fmt_cfg)
+ *fmt_cfg = fmt;
+ if (fmt_info)
+ *fmt_info = info;
+}
+
+static void rkisp1_set_fmt(struct rkisp1_capture *cap,
+ struct v4l2_pix_format_mplane *pixm)
+{
+ rkisp1_try_fmt(cap, pixm, &cap->pix.cfg, &cap->pix.info);
+ cap->pix.fmt = *pixm;
+
+ /* SP supports custom stride in number of pixels of the Y plane */
+ if (cap->id == RKISP1_SELFPATH)
+ cap->sp_y_stride = pixm->plane_fmt[0].bytesperline /
+ cap->pix.info->bpp[0];
+}
+
+static int rkisp1_try_fmt_vid_cap_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct rkisp1_capture *cap = video_drvdata(file);
+
+ rkisp1_try_fmt(cap, &f->fmt.pix_mp, NULL, NULL);
+
+ return 0;
+}
+
+static int rkisp1_enum_fmt_vid_cap_mplane(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct rkisp1_capture *cap = video_drvdata(file);
+ const struct rkisp1_capture_fmt_cfg *fmt = NULL;
+ unsigned int i, n = 0;
+
+ if (!f->mbus_code) {
+ if (f->index >= cap->config->fmt_size)
+ return -EINVAL;
+
+ fmt = &cap->config->fmts[f->index];
+ f->pixelformat = fmt->fourcc;
+ return 0;
+ }
+
+ for (i = 0; i < cap->config->fmt_size; i++) {
+ if (cap->config->fmts[i].mbus != f->mbus_code)
+ continue;
+
+ if (n++ == f->index) {
+ f->pixelformat = cap->config->fmts[i].fourcc;
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static int rkisp1_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ static const unsigned int max_widths[] = {
+ RKISP1_RSZ_MP_SRC_MAX_WIDTH,
+ RKISP1_RSZ_SP_SRC_MAX_WIDTH,
+ };
+ static const unsigned int max_heights[] = {
+ RKISP1_RSZ_MP_SRC_MAX_HEIGHT,
+ RKISP1_RSZ_SP_SRC_MAX_HEIGHT,
+ };
+ struct rkisp1_capture *cap = video_drvdata(file);
+
+ if (fsize->index != 0)
+ return -EINVAL;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+
+ fsize->stepwise.min_width = RKISP1_RSZ_SRC_MIN_WIDTH;
+ fsize->stepwise.max_width = max_widths[cap->id];
+ fsize->stepwise.step_width = 2;
+
+ fsize->stepwise.min_height = RKISP1_RSZ_SRC_MIN_HEIGHT;
+ fsize->stepwise.max_height = max_heights[cap->id];
+ fsize->stepwise.step_height = 2;
+
+ return 0;
+}
+
+static int rkisp1_s_fmt_vid_cap_mplane(struct file *file,
+ void *priv, struct v4l2_format *f)
+{
+ struct rkisp1_capture *cap = video_drvdata(file);
+ struct rkisp1_vdev_node *node =
+ rkisp1_vdev_to_node(&cap->vnode.vdev);
+
+ if (vb2_is_busy(&node->buf_queue))
+ return -EBUSY;
+
+ rkisp1_set_fmt(cap, &f->fmt.pix_mp);
+
+ return 0;
+}
+
+static int rkisp1_g_fmt_vid_cap_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct rkisp1_capture *cap = video_drvdata(file);
+
+ f->fmt.pix_mp = cap->pix.fmt;
+
+ return 0;
+}
+
+static int
+rkisp1_querycap(struct file *file, void *priv, struct v4l2_capability *cap)
+{
+ strscpy(cap->driver, RKISP1_DRIVER_NAME, sizeof(cap->driver));
+ strscpy(cap->card, RKISP1_DRIVER_NAME, sizeof(cap->card));
+ strscpy(cap->bus_info, RKISP1_BUS_INFO, sizeof(cap->bus_info));
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops rkisp1_v4l2_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 = rkisp1_try_fmt_vid_cap_mplane,
+ .vidioc_s_fmt_vid_cap_mplane = rkisp1_s_fmt_vid_cap_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = rkisp1_g_fmt_vid_cap_mplane,
+ .vidioc_enum_fmt_vid_cap = rkisp1_enum_fmt_vid_cap_mplane,
+ .vidioc_enum_framesizes = rkisp1_enum_framesizes,
+ .vidioc_querycap = rkisp1_querycap,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static int rkisp1_capture_link_validate(struct media_link *link)
+{
+ struct video_device *vdev =
+ media_entity_to_video_device(link->sink->entity);
+ struct v4l2_subdev *sd =
+ media_entity_to_v4l2_subdev(link->source->entity);
+ struct rkisp1_capture *cap = video_get_drvdata(vdev);
+ const struct rkisp1_capture_fmt_cfg *fmt =
+ rkisp1_find_fmt_cfg(cap, cap->pix.fmt.pixelformat);
+ struct v4l2_subdev_format sd_fmt = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ .pad = link->source->index,
+ };
+ int ret;
+
+ ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sd_fmt);
+ if (ret)
+ return ret;
+
+ if (sd_fmt.format.height != cap->pix.fmt.height ||
+ sd_fmt.format.width != cap->pix.fmt.width ||
+ sd_fmt.format.code != fmt->mbus) {
+ dev_dbg(cap->rkisp1->dev,
+ "link '%s':%u -> '%s':%u not valid: 0x%04x/%ux%u != 0x%04x/%ux%u\n",
+ link->source->entity->name, link->source->index,
+ link->sink->entity->name, link->sink->index,
+ sd_fmt.format.code, sd_fmt.format.width,
+ sd_fmt.format.height, fmt->mbus, cap->pix.fmt.width,
+ cap->pix.fmt.height);
+ return -EPIPE;
+ }
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------------
+ * core functions
+ */
+
+static const struct media_entity_operations rkisp1_media_ops = {
+ .link_validate = rkisp1_capture_link_validate,
+};
+
+static const struct v4l2_file_operations rkisp1_fops = {
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
+};
+
+static void rkisp1_unregister_capture(struct rkisp1_capture *cap)
+{
+ if (!video_is_registered(&cap->vnode.vdev))
+ return;
+
+ media_entity_cleanup(&cap->vnode.vdev.entity);
+ vb2_video_unregister_device(&cap->vnode.vdev);
+ mutex_destroy(&cap->vnode.vlock);
+}
+
+void rkisp1_capture_devs_unregister(struct rkisp1_device *rkisp1)
+{
+ struct rkisp1_capture *mp = &rkisp1->capture_devs[RKISP1_MAINPATH];
+ struct rkisp1_capture *sp = &rkisp1->capture_devs[RKISP1_SELFPATH];
+
+ rkisp1_unregister_capture(mp);
+ rkisp1_unregister_capture(sp);
+}
+
+static int rkisp1_register_capture(struct rkisp1_capture *cap)
+{
+ static const char * const dev_names[] = {
+ RKISP1_MP_DEV_NAME, RKISP1_SP_DEV_NAME
+ };
+ struct v4l2_device *v4l2_dev = &cap->rkisp1->v4l2_dev;
+ struct video_device *vdev = &cap->vnode.vdev;
+ struct rkisp1_vdev_node *node;
+ struct vb2_queue *q;
+ int ret;
+
+ strscpy(vdev->name, dev_names[cap->id], sizeof(vdev->name));
+ node = rkisp1_vdev_to_node(vdev);
+ mutex_init(&node->vlock);
+
+ vdev->ioctl_ops = &rkisp1_v4l2_ioctl_ops;
+ vdev->release = video_device_release_empty;
+ vdev->fops = &rkisp1_fops;
+ vdev->minor = -1;
+ vdev->v4l2_dev = v4l2_dev;
+ vdev->lock = &node->vlock;
+ vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
+ V4L2_CAP_STREAMING | V4L2_CAP_IO_MC;
+ vdev->entity.ops = &rkisp1_media_ops;
+ video_set_drvdata(vdev, cap);
+ vdev->vfl_dir = VFL_DIR_RX;
+ node->pad.flags = MEDIA_PAD_FL_SINK;
+
+ q = &node->buf_queue;
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ q->io_modes = VB2_MMAP | VB2_DMABUF;
+ q->drv_priv = cap;
+ q->ops = &rkisp1_vb2_ops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct rkisp1_buffer);
+ q->min_buffers_needed = RKISP1_MIN_BUFFERS_NEEDED;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &node->vlock;
+ q->dev = cap->rkisp1->dev;
+ ret = vb2_queue_init(q);
+ if (ret) {
+ dev_err(cap->rkisp1->dev,
+ "vb2 queue init failed (err=%d)\n", ret);
+ goto error;
+ }
+
+ vdev->queue = q;
+
+ ret = media_entity_pads_init(&vdev->entity, 1, &node->pad);
+ if (ret)
+ goto error;
+
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret) {
+ dev_err(cap->rkisp1->dev,
+ "failed to register %s, ret=%d\n", vdev->name, ret);
+ goto error;
+ }
+
+ v4l2_info(v4l2_dev, "registered %s as /dev/video%d\n", vdev->name,
+ vdev->num);
+
+ return 0;
+
+error:
+ media_entity_cleanup(&vdev->entity);
+ mutex_destroy(&node->vlock);
+ return ret;
+}
+
+static void
+rkisp1_capture_init(struct rkisp1_device *rkisp1, enum rkisp1_stream_id id)
+{
+ struct rkisp1_capture *cap = &rkisp1->capture_devs[id];
+ struct v4l2_pix_format_mplane pixm;
+
+ memset(cap, 0, sizeof(*cap));
+ cap->id = id;
+ cap->rkisp1 = rkisp1;
+
+ INIT_LIST_HEAD(&cap->buf.queue);
+ init_waitqueue_head(&cap->done);
+ spin_lock_init(&cap->buf.lock);
+ if (cap->id == RKISP1_SELFPATH) {
+ cap->ops = &rkisp1_capture_ops_sp;
+ cap->config = &rkisp1_capture_config_sp;
+ } else {
+ cap->ops = &rkisp1_capture_ops_mp;
+ cap->config = &rkisp1_capture_config_mp;
+ }
+
+ cap->is_streaming = false;
+
+ memset(&pixm, 0, sizeof(pixm));
+ pixm.pixelformat = V4L2_PIX_FMT_YUYV;
+ pixm.width = RKISP1_DEFAULT_WIDTH;
+ pixm.height = RKISP1_DEFAULT_HEIGHT;
+ rkisp1_set_fmt(cap, &pixm);
+}
+
+int rkisp1_capture_devs_register(struct rkisp1_device *rkisp1)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < ARRAY_SIZE(rkisp1->capture_devs); i++) {
+ struct rkisp1_capture *cap = &rkisp1->capture_devs[i];
+
+ rkisp1_capture_init(rkisp1, i);
+
+ ret = rkisp1_register_capture(cap);
+ if (ret) {
+ rkisp1_capture_devs_unregister(rkisp1);
+ return ret;
+ }
+ }
+
+ return 0;
+
+}
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.c
new file mode 100644
index 0000000000..f956b90a40
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.c
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Rockchip ISP1 Driver - Common definitions
+ *
+ * Copyright (C) 2019 Collabora, Ltd.
+ */
+
+#include <media/mipi-csi2.h>
+#include <media/v4l2-rect.h>
+
+#include "rkisp1-common.h"
+
+static const struct rkisp1_mbus_info rkisp1_formats[] = {
+ {
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
+ .pixel_enc = V4L2_PIXEL_ENC_YUV,
+ .direction = RKISP1_ISP_SD_SRC,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .pixel_enc = V4L2_PIXEL_ENC_BAYER,
+ .mipi_dt = MIPI_CSI2_DT_RAW10,
+ .bayer_pat = RKISP1_RAW_RGGB,
+ .bus_width = 10,
+ .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .pixel_enc = V4L2_PIXEL_ENC_BAYER,
+ .mipi_dt = MIPI_CSI2_DT_RAW10,
+ .bayer_pat = RKISP1_RAW_BGGR,
+ .bus_width = 10,
+ .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .pixel_enc = V4L2_PIXEL_ENC_BAYER,
+ .mipi_dt = MIPI_CSI2_DT_RAW10,
+ .bayer_pat = RKISP1_RAW_GBRG,
+ .bus_width = 10,
+ .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .pixel_enc = V4L2_PIXEL_ENC_BAYER,
+ .mipi_dt = MIPI_CSI2_DT_RAW10,
+ .bayer_pat = RKISP1_RAW_GRBG,
+ .bus_width = 10,
+ .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .pixel_enc = V4L2_PIXEL_ENC_BAYER,
+ .mipi_dt = MIPI_CSI2_DT_RAW12,
+ .bayer_pat = RKISP1_RAW_RGGB,
+ .bus_width = 12,
+ .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .pixel_enc = V4L2_PIXEL_ENC_BAYER,
+ .mipi_dt = MIPI_CSI2_DT_RAW12,
+ .bayer_pat = RKISP1_RAW_BGGR,
+ .bus_width = 12,
+ .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .pixel_enc = V4L2_PIXEL_ENC_BAYER,
+ .mipi_dt = MIPI_CSI2_DT_RAW12,
+ .bayer_pat = RKISP1_RAW_GBRG,
+ .bus_width = 12,
+ .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .pixel_enc = V4L2_PIXEL_ENC_BAYER,
+ .mipi_dt = MIPI_CSI2_DT_RAW12,
+ .bayer_pat = RKISP1_RAW_GRBG,
+ .bus_width = 12,
+ .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .pixel_enc = V4L2_PIXEL_ENC_BAYER,
+ .mipi_dt = MIPI_CSI2_DT_RAW8,
+ .bayer_pat = RKISP1_RAW_RGGB,
+ .bus_width = 8,
+ .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .pixel_enc = V4L2_PIXEL_ENC_BAYER,
+ .mipi_dt = MIPI_CSI2_DT_RAW8,
+ .bayer_pat = RKISP1_RAW_BGGR,
+ .bus_width = 8,
+ .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .pixel_enc = V4L2_PIXEL_ENC_BAYER,
+ .mipi_dt = MIPI_CSI2_DT_RAW8,
+ .bayer_pat = RKISP1_RAW_GBRG,
+ .bus_width = 8,
+ .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .pixel_enc = V4L2_PIXEL_ENC_BAYER,
+ .mipi_dt = MIPI_CSI2_DT_RAW8,
+ .bayer_pat = RKISP1_RAW_GRBG,
+ .bus_width = 8,
+ .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16,
+ .pixel_enc = V4L2_PIXEL_ENC_YUV,
+ .mipi_dt = MIPI_CSI2_DT_YUV422_8B,
+ .yuv_seq = RKISP1_CIF_ISP_ACQ_PROP_YCBYCR,
+ .bus_width = 16,
+ .direction = RKISP1_ISP_SD_SINK,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_YVYU8_1X16,
+ .pixel_enc = V4L2_PIXEL_ENC_YUV,
+ .mipi_dt = MIPI_CSI2_DT_YUV422_8B,
+ .yuv_seq = RKISP1_CIF_ISP_ACQ_PROP_YCRYCB,
+ .bus_width = 16,
+ .direction = RKISP1_ISP_SD_SINK,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .pixel_enc = V4L2_PIXEL_ENC_YUV,
+ .mipi_dt = MIPI_CSI2_DT_YUV422_8B,
+ .yuv_seq = RKISP1_CIF_ISP_ACQ_PROP_CBYCRY,
+ .bus_width = 16,
+ .direction = RKISP1_ISP_SD_SINK,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_VYUY8_1X16,
+ .pixel_enc = V4L2_PIXEL_ENC_YUV,
+ .mipi_dt = MIPI_CSI2_DT_YUV422_8B,
+ .yuv_seq = RKISP1_CIF_ISP_ACQ_PROP_CRYCBY,
+ .bus_width = 16,
+ .direction = RKISP1_ISP_SD_SINK,
+ },
+};
+
+const struct rkisp1_mbus_info *rkisp1_mbus_info_get_by_index(unsigned int index)
+{
+ if (index >= ARRAY_SIZE(rkisp1_formats))
+ return NULL;
+
+ return &rkisp1_formats[index];
+}
+
+const struct rkisp1_mbus_info *rkisp1_mbus_info_get_by_code(u32 mbus_code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(rkisp1_formats); i++) {
+ const struct rkisp1_mbus_info *fmt = &rkisp1_formats[i];
+
+ if (fmt->mbus_code == mbus_code)
+ return fmt;
+ }
+
+ return NULL;
+}
+
+static const struct v4l2_rect rkisp1_sd_min_crop = {
+ .width = RKISP1_ISP_MIN_WIDTH,
+ .height = RKISP1_ISP_MIN_HEIGHT,
+ .top = 0,
+ .left = 0,
+};
+
+void rkisp1_sd_adjust_crop_rect(struct v4l2_rect *crop,
+ const struct v4l2_rect *bounds)
+{
+ v4l2_rect_set_min_size(crop, &rkisp1_sd_min_crop);
+ v4l2_rect_map_inside(crop, bounds);
+}
+
+void rkisp1_sd_adjust_crop(struct v4l2_rect *crop,
+ const struct v4l2_mbus_framefmt *bounds)
+{
+ struct v4l2_rect crop_bounds = {
+ .left = 0,
+ .top = 0,
+ .width = bounds->width,
+ .height = bounds->height,
+ };
+
+ rkisp1_sd_adjust_crop_rect(crop, &crop_bounds);
+}
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h
new file mode 100644
index 0000000000..d30f0ecb1b
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h
@@ -0,0 +1,630 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
+/*
+ * Rockchip ISP1 Driver - Common definitions
+ *
+ * Copyright (C) 2019 Collabora, Ltd.
+ *
+ * Based on Rockchip ISP1 driver by Rockchip Electronics Co., Ltd.
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ */
+
+#ifndef _RKISP1_COMMON_H
+#define _RKISP1_COMMON_H
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/rkisp1-config.h>
+#include <media/media-device.h>
+#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf2-v4l2.h>
+
+#include "rkisp1-regs.h"
+
+struct dentry;
+
+/*
+ * flags on the 'direction' field in struct rkisp1_mbus_info' that indicate
+ * on which pad the media bus format is supported
+ */
+#define RKISP1_ISP_SD_SRC BIT(0)
+#define RKISP1_ISP_SD_SINK BIT(1)
+
+/* min and max values for the widths and heights of the entities */
+#define RKISP1_ISP_MAX_WIDTH 4032
+#define RKISP1_ISP_MAX_HEIGHT 3024
+#define RKISP1_ISP_MIN_WIDTH 32
+#define RKISP1_ISP_MIN_HEIGHT 32
+
+#define RKISP1_RSZ_MP_SRC_MAX_WIDTH 4416
+#define RKISP1_RSZ_MP_SRC_MAX_HEIGHT 3312
+#define RKISP1_RSZ_SP_SRC_MAX_WIDTH 1920
+#define RKISP1_RSZ_SP_SRC_MAX_HEIGHT 1920
+#define RKISP1_RSZ_SRC_MIN_WIDTH 32
+#define RKISP1_RSZ_SRC_MIN_HEIGHT 16
+
+/* the default width and height of all the entities */
+#define RKISP1_DEFAULT_WIDTH 800
+#define RKISP1_DEFAULT_HEIGHT 600
+
+#define RKISP1_DRIVER_NAME "rkisp1"
+#define RKISP1_BUS_INFO "platform:" RKISP1_DRIVER_NAME
+
+/* maximum number of clocks */
+#define RKISP1_MAX_BUS_CLK 8
+
+/* a bitmask of the ready stats */
+#define RKISP1_STATS_MEAS_MASK (RKISP1_CIF_ISP_AWB_DONE | \
+ RKISP1_CIF_ISP_AFM_FIN | \
+ RKISP1_CIF_ISP_EXP_END | \
+ RKISP1_CIF_ISP_HIST_MEASURE_RDY)
+
+/* enum for the resizer pads */
+enum rkisp1_rsz_pad {
+ RKISP1_RSZ_PAD_SINK,
+ RKISP1_RSZ_PAD_SRC,
+ RKISP1_RSZ_PAD_MAX
+};
+
+/* enum for the csi receiver pads */
+enum rkisp1_csi_pad {
+ RKISP1_CSI_PAD_SINK,
+ RKISP1_CSI_PAD_SRC,
+ RKISP1_CSI_PAD_NUM
+};
+
+/* enum for the capture id */
+enum rkisp1_stream_id {
+ RKISP1_MAINPATH,
+ RKISP1_SELFPATH,
+};
+
+/* bayer patterns */
+enum rkisp1_fmt_raw_pat_type {
+ RKISP1_RAW_RGGB = 0,
+ RKISP1_RAW_GRBG,
+ RKISP1_RAW_GBRG,
+ RKISP1_RAW_BGGR,
+};
+
+/* enum for the isp pads */
+enum rkisp1_isp_pad {
+ RKISP1_ISP_PAD_SINK_VIDEO,
+ RKISP1_ISP_PAD_SINK_PARAMS,
+ RKISP1_ISP_PAD_SOURCE_VIDEO,
+ RKISP1_ISP_PAD_SOURCE_STATS,
+ RKISP1_ISP_PAD_MAX
+};
+
+/*
+ * enum rkisp1_feature - ISP features
+ *
+ * @RKISP1_FEATURE_MIPI_CSI2: The ISP has an internal MIPI CSI-2 receiver
+ *
+ * The ISP features are stored in a bitmask in &rkisp1_info.features and allow
+ * the driver to implement support for features present in some ISP versions
+ * only.
+ */
+enum rkisp1_feature {
+ RKISP1_FEATURE_MIPI_CSI2 = BIT(0),
+};
+
+/*
+ * struct rkisp1_info - Model-specific ISP Information
+ *
+ * @clks: array of ISP clock names
+ * @clk_size: number of entries in the @clks array
+ * @isrs: array of ISP interrupt descriptors
+ * @isr_size: number of entries in the @isrs array
+ * @isp_ver: ISP version
+ * @features: bitmask of rkisp1_feature features implemented by the ISP
+ *
+ * This structure contains information about the ISP specific to a particular
+ * ISP model, version, or integration in a particular SoC.
+ */
+struct rkisp1_info {
+ const char * const *clks;
+ unsigned int clk_size;
+ const struct rkisp1_isr_data *isrs;
+ unsigned int isr_size;
+ enum rkisp1_cif_isp_version isp_ver;
+ unsigned int features;
+};
+
+/*
+ * struct rkisp1_sensor_async - A container for the v4l2_async_subdev to add to the notifier
+ * of the v4l2-async API
+ *
+ * @asd: async_subdev variable for the sensor
+ * @index: index of the sensor (counting sensor found in DT)
+ * @source_ep: fwnode for the sensor source endpoint
+ * @lanes: number of lanes
+ * @mbus_type: type of bus (currently only CSI2 is supported)
+ * @mbus_flags: media bus (V4L2_MBUS_*) flags
+ * @sd: a pointer to v4l2_subdev struct of the sensor
+ * @pixel_rate_ctrl: pixel rate of the sensor, used to initialize the phy
+ * @port: port number (0: MIPI, 1: Parallel)
+ */
+struct rkisp1_sensor_async {
+ struct v4l2_async_connection asd;
+ unsigned int index;
+ struct fwnode_handle *source_ep;
+ unsigned int lanes;
+ enum v4l2_mbus_type mbus_type;
+ unsigned int mbus_flags;
+ struct v4l2_subdev *sd;
+ struct v4l2_ctrl *pixel_rate_ctrl;
+ unsigned int port;
+};
+
+/*
+ * struct rkisp1_csi - CSI receiver subdev
+ *
+ * @rkisp1: pointer to the rkisp1 device
+ * @dphy: a pointer to the phy
+ * @is_dphy_errctrl_disabled: if dphy errctrl is disabled (avoid endless interrupt)
+ * @sd: v4l2_subdev variable
+ * @pads: media pads
+ * @pad_cfg: configurations for the pads
+ * @sink_fmt: input format
+ * @lock: protects pad_cfg and sink_fmt
+ * @source: source in-use, set when starting streaming
+ */
+struct rkisp1_csi {
+ struct rkisp1_device *rkisp1;
+ struct phy *dphy;
+ bool is_dphy_errctrl_disabled;
+ struct v4l2_subdev sd;
+ struct media_pad pads[RKISP1_CSI_PAD_NUM];
+ struct v4l2_subdev_pad_config pad_cfg[RKISP1_CSI_PAD_NUM];
+ const struct rkisp1_mbus_info *sink_fmt;
+ struct mutex lock;
+ struct v4l2_subdev *source;
+};
+
+/*
+ * struct rkisp1_isp - ISP subdev entity
+ *
+ * @sd: v4l2_subdev variable
+ * @rkisp1: pointer to rkisp1_device
+ * @pads: media pads
+ * @pad_cfg: pads configurations
+ * @sink_fmt: input format
+ * @src_fmt: output format
+ * @ops_lock: ops serialization
+ * @frame_sequence: used to synchronize frame_id between video devices.
+ */
+struct rkisp1_isp {
+ struct v4l2_subdev sd;
+ struct rkisp1_device *rkisp1;
+ struct media_pad pads[RKISP1_ISP_PAD_MAX];
+ struct v4l2_subdev_pad_config pad_cfg[RKISP1_ISP_PAD_MAX];
+ const struct rkisp1_mbus_info *sink_fmt;
+ const struct rkisp1_mbus_info *src_fmt;
+ struct mutex ops_lock; /* serialize the subdevice ops */
+ __u32 frame_sequence;
+};
+
+/*
+ * struct rkisp1_vdev_node - Container for the video nodes: params, stats, mainpath, selfpath
+ *
+ * @buf_queue: queue of buffers
+ * @vlock: lock of the video node
+ * @vdev: video node
+ * @pad: media pad
+ */
+struct rkisp1_vdev_node {
+ struct vb2_queue buf_queue;
+ struct mutex vlock; /* ioctl serialization mutex */
+ struct video_device vdev;
+ struct media_pad pad;
+};
+
+/*
+ * struct rkisp1_buffer - A container for the vb2 buffers used by the video devices:
+ * params, stats, mainpath, selfpath
+ *
+ * @vb: vb2 buffer
+ * @queue: entry of the buffer in the queue
+ * @buff_addr: dma addresses of each plane, used only by the capture devices: selfpath, mainpath
+ */
+struct rkisp1_buffer {
+ struct vb2_v4l2_buffer vb;
+ struct list_head queue;
+ u32 buff_addr[VIDEO_MAX_PLANES];
+};
+
+/*
+ * struct rkisp1_dummy_buffer - A buffer to write the next frame to in case
+ * there are no vb2 buffers available.
+ *
+ * @vaddr: return value of call to dma_alloc_attrs.
+ * @dma_addr: dma address of the buffer.
+ * @size: size of the buffer.
+ */
+struct rkisp1_dummy_buffer {
+ void *vaddr;
+ dma_addr_t dma_addr;
+ u32 size;
+};
+
+struct rkisp1_device;
+
+/*
+ * struct rkisp1_capture - ISP capture video device
+ *
+ * @vnode: video node
+ * @rkisp1: pointer to rkisp1_device
+ * @id: id of the capture, one of RKISP1_SELFPATH, RKISP1_MAINPATH
+ * @ops: list of callbacks to configure the capture device.
+ * @config: a pointer to the list of registers to configure the capture format.
+ * @is_streaming: device is streaming
+ * @is_stopping: stop_streaming callback was called and the device is in the process of
+ * stopping the streaming.
+ * @done: when stop_streaming callback is called, the device waits for the next irq
+ * handler to stop the streaming by waiting on the 'done' wait queue.
+ * If the irq handler is not called, the stream is stopped by the callback
+ * after timeout.
+ * @sp_y_stride: the selfpath allows to configure a y stride that is longer than the image width.
+ * @buf.lock: lock to protect buf.queue
+ * @buf.queue: queued buffer list
+ * @buf.dummy: dummy space to store dropped data
+ *
+ * rkisp1 uses shadow registers, so it needs two buffers at a time
+ * @buf.curr: the buffer used for current frame
+ * @buf.next: the buffer used for next frame
+ * @pix.cfg: pixel configuration
+ * @pix.info: a pointer to the v4l2_format_info of the pixel format
+ * @pix.fmt: buffer format
+ */
+struct rkisp1_capture {
+ struct rkisp1_vdev_node vnode;
+ struct rkisp1_device *rkisp1;
+ enum rkisp1_stream_id id;
+ const struct rkisp1_capture_ops *ops;
+ const struct rkisp1_capture_config *config;
+ bool is_streaming;
+ bool is_stopping;
+ wait_queue_head_t done;
+ unsigned int sp_y_stride;
+ struct {
+ /* protects queue, curr and next */
+ spinlock_t lock;
+ struct list_head queue;
+ struct rkisp1_dummy_buffer dummy;
+ struct rkisp1_buffer *curr;
+ struct rkisp1_buffer *next;
+ } buf;
+ struct {
+ const struct rkisp1_capture_fmt_cfg *cfg;
+ const struct v4l2_format_info *info;
+ struct v4l2_pix_format_mplane fmt;
+ } pix;
+};
+
+struct rkisp1_stats;
+struct rkisp1_stats_ops {
+ void (*get_awb_meas)(struct rkisp1_stats *stats,
+ struct rkisp1_stat_buffer *pbuf);
+ void (*get_aec_meas)(struct rkisp1_stats *stats,
+ struct rkisp1_stat_buffer *pbuf);
+ void (*get_hst_meas)(struct rkisp1_stats *stats,
+ struct rkisp1_stat_buffer *pbuf);
+};
+
+/*
+ * struct rkisp1_stats - ISP Statistics device
+ *
+ * @vnode: video node
+ * @rkisp1: pointer to the rkisp1 device
+ * @lock: locks the buffer list 'stat'
+ * @stat: queue of rkisp1_buffer
+ * @vdev_fmt: v4l2_format of the metadata format
+ */
+struct rkisp1_stats {
+ struct rkisp1_vdev_node vnode;
+ struct rkisp1_device *rkisp1;
+ const struct rkisp1_stats_ops *ops;
+
+ spinlock_t lock; /* locks the buffers list 'stats' */
+ struct list_head stat;
+ struct v4l2_format vdev_fmt;
+};
+
+struct rkisp1_params;
+struct rkisp1_params_ops {
+ void (*lsc_matrix_config)(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_lsc_config *pconfig);
+ void (*goc_config)(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_goc_config *arg);
+ void (*awb_meas_config)(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_awb_meas_config *arg);
+ void (*awb_meas_enable)(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_awb_meas_config *arg,
+ bool en);
+ void (*awb_gain_config)(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_awb_gain_config *arg);
+ void (*aec_config)(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_aec_config *arg);
+ void (*hst_config)(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_hst_config *arg);
+ void (*hst_enable)(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_hst_config *arg, bool en);
+ void (*afm_config)(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_afc_config *arg);
+};
+
+/*
+ * struct rkisp1_params - ISP input parameters device
+ *
+ * @vnode: video node
+ * @rkisp1: pointer to the rkisp1 device
+ * @ops: pointer to the variant-specific operations
+ * @config_lock: locks the buffer list 'params'
+ * @params: queue of rkisp1_buffer
+ * @vdev_fmt: v4l2_format of the metadata format
+ * @quantization: the quantization configured on the isp's src pad
+ * @raw_type: the bayer pattern on the isp video sink pad
+ */
+struct rkisp1_params {
+ struct rkisp1_vdev_node vnode;
+ struct rkisp1_device *rkisp1;
+ const struct rkisp1_params_ops *ops;
+
+ spinlock_t config_lock; /* locks the buffers list 'params' */
+ struct list_head params;
+ struct v4l2_format vdev_fmt;
+
+ enum v4l2_quantization quantization;
+ enum v4l2_ycbcr_encoding ycbcr_encoding;
+ enum rkisp1_fmt_raw_pat_type raw_type;
+};
+
+/*
+ * struct rkisp1_resizer - Resizer subdev
+ *
+ * @sd: v4l2_subdev variable
+ * @regs_base: base register address offset
+ * @id: id of the resizer, one of RKISP1_SELFPATH, RKISP1_MAINPATH
+ * @rkisp1: pointer to the rkisp1 device
+ * @pads: media pads
+ * @pad_cfg: configurations for the pads
+ * @config: the set of registers to configure the resizer
+ * @pixel_enc: pixel encoding of the resizer
+ * @ops_lock: a lock for the subdev ops
+ */
+struct rkisp1_resizer {
+ struct v4l2_subdev sd;
+ u32 regs_base;
+ enum rkisp1_stream_id id;
+ struct rkisp1_device *rkisp1;
+ struct media_pad pads[RKISP1_RSZ_PAD_MAX];
+ struct v4l2_subdev_pad_config pad_cfg[RKISP1_RSZ_PAD_MAX];
+ const struct rkisp1_rsz_config *config;
+ enum v4l2_pixel_encoding pixel_enc;
+ struct mutex ops_lock; /* serialize the subdevice ops */
+};
+
+/*
+ * struct rkisp1_debug - Values to be exposed on debugfs.
+ * The parameters are counters of the number of times the
+ * event occurred since the driver was loaded.
+ *
+ * @data_loss: loss of data occurred within a line, processing failure
+ * @outform_size_error: size error is generated in outmux submodule
+ * @img_stabilization_size_error: size error is generated in image stabilization submodule
+ * @inform_size_err: size error is generated in inform submodule
+ * @mipi_error: mipi error occurred
+ * @stats_error: writing to the 'Interrupt clear register' did not clear
+ * it in the register 'Masked interrupt status'
+ * @stop_timeout: upon stream stop, the capture waits 1 second for the isr to stop
+ * the stream. This param is incremented in case of timeout.
+ * @frame_drop: a frame was ready but the buffer queue was empty so the frame
+ * was not sent to userspace
+ */
+struct rkisp1_debug {
+ struct dentry *debugfs_dir;
+ unsigned long data_loss;
+ unsigned long outform_size_error;
+ unsigned long img_stabilization_size_error;
+ unsigned long inform_size_error;
+ unsigned long irq_delay;
+ unsigned long mipi_error;
+ unsigned long stats_error;
+ unsigned long stop_timeout[2];
+ unsigned long frame_drop[2];
+};
+
+/*
+ * struct rkisp1_device - ISP platform device
+ *
+ * @base_addr: base register address
+ * @irq: the irq number
+ * @dev: a pointer to the struct device
+ * @clk_size: number of clocks
+ * @clks: array of clocks
+ * @v4l2_dev: v4l2_device variable
+ * @media_dev: media_device variable
+ * @notifier: a notifier to register on the v4l2-async API to be notified on the sensor
+ * @source: source subdev in-use, set when starting streaming
+ * @csi: internal CSI-2 receiver
+ * @isp: ISP sub-device
+ * @resizer_devs: resizer sub-devices
+ * @capture_devs: capture devices
+ * @stats: ISP statistics metadata capture device
+ * @params: ISP parameters metadata output device
+ * @pipe: media pipeline
+ * @stream_lock: serializes {start/stop}_streaming callbacks between the capture devices.
+ * @debug: debug params to be exposed on debugfs
+ * @info: version-specific ISP information
+ */
+struct rkisp1_device {
+ void __iomem *base_addr;
+ struct device *dev;
+ unsigned int clk_size;
+ struct clk_bulk_data clks[RKISP1_MAX_BUS_CLK];
+ struct v4l2_device v4l2_dev;
+ struct media_device media_dev;
+ struct v4l2_async_notifier notifier;
+ struct v4l2_subdev *source;
+ struct rkisp1_csi csi;
+ struct rkisp1_isp isp;
+ struct rkisp1_resizer resizer_devs[2];
+ struct rkisp1_capture capture_devs[2];
+ struct rkisp1_stats stats;
+ struct rkisp1_params params;
+ struct media_pipeline pipe;
+ struct mutex stream_lock; /* serialize {start/stop}_streaming cb between capture devices */
+ struct rkisp1_debug debug;
+ const struct rkisp1_info *info;
+};
+
+/*
+ * struct rkisp1_mbus_info - ISP media bus info, Translates media bus code to hardware
+ * format values
+ *
+ * @mbus_code: media bus code
+ * @pixel_enc: pixel encoding
+ * @mipi_dt: mipi data type
+ * @yuv_seq: the order of the Y, Cb, Cr values
+ * @bus_width: bus width
+ * @bayer_pat: bayer pattern
+ * @direction: a bitmask of the flags indicating on which pad the format is supported on
+ */
+struct rkisp1_mbus_info {
+ u32 mbus_code;
+ enum v4l2_pixel_encoding pixel_enc;
+ u32 mipi_dt;
+ u32 yuv_seq;
+ u8 bus_width;
+ enum rkisp1_fmt_raw_pat_type bayer_pat;
+ unsigned int direction;
+};
+
+static inline void
+rkisp1_write(struct rkisp1_device *rkisp1, unsigned int addr, u32 val)
+{
+ writel(val, rkisp1->base_addr + addr);
+}
+
+static inline u32 rkisp1_read(struct rkisp1_device *rkisp1, unsigned int addr)
+{
+ return readl(rkisp1->base_addr + addr);
+}
+
+/*
+ * rkisp1_cap_enum_mbus_codes - A helper function that return the i'th supported mbus code
+ * of the capture entity. This is used to enumerate the supported
+ * mbus codes on the source pad of the resizer.
+ *
+ * @cap: the capture entity
+ * @code: the mbus code, the function reads the code->index and fills the code->code
+ */
+int rkisp1_cap_enum_mbus_codes(struct rkisp1_capture *cap,
+ struct v4l2_subdev_mbus_code_enum *code);
+
+/*
+ * rkisp1_mbus_info_get_by_index - Retrieve the ith supported mbus info
+ *
+ * @index: index of the mbus info to fetch
+ */
+const struct rkisp1_mbus_info *rkisp1_mbus_info_get_by_index(unsigned int index);
+
+/*
+ * rkisp1_sd_adjust_crop_rect - adjust a rectangle to fit into another rectangle.
+ *
+ * @crop: rectangle to adjust.
+ * @bounds: rectangle used as bounds.
+ */
+void rkisp1_sd_adjust_crop_rect(struct v4l2_rect *crop,
+ const struct v4l2_rect *bounds);
+
+/*
+ * rkisp1_sd_adjust_crop - adjust a rectangle to fit into media bus format
+ *
+ * @crop: rectangle to adjust.
+ * @bounds: media bus format used as bounds.
+ */
+void rkisp1_sd_adjust_crop(struct v4l2_rect *crop,
+ const struct v4l2_mbus_framefmt *bounds);
+
+/*
+ * rkisp1_mbus_info_get_by_code - get the isp info of the media bus code
+ *
+ * @mbus_code: the media bus code
+ */
+const struct rkisp1_mbus_info *rkisp1_mbus_info_get_by_code(u32 mbus_code);
+
+/*
+ * rkisp1_params_pre_configure - Configure the params before stream start
+ *
+ * @params: pointer to rkisp1_params
+ * @bayer_pat: the bayer pattern on the isp video sink pad
+ * @quantization: the quantization configured on the isp's src pad
+ * @ycbcr_encoding: the ycbcr_encoding configured on the isp's src pad
+ *
+ * This function is called by the ISP entity just before the ISP gets started.
+ * It applies the initial ISP parameters from the first params buffer, but
+ * skips LSC as it needs to be configured after the ISP is started.
+ */
+void rkisp1_params_pre_configure(struct rkisp1_params *params,
+ enum rkisp1_fmt_raw_pat_type bayer_pat,
+ enum v4l2_quantization quantization,
+ enum v4l2_ycbcr_encoding ycbcr_encoding);
+
+/*
+ * rkisp1_params_post_configure - Configure the params after stream start
+ *
+ * @params: pointer to rkisp1_params
+ *
+ * This function is called by the ISP entity just after the ISP gets started.
+ * It applies the initial ISP LSC parameters from the first params buffer.
+ */
+void rkisp1_params_post_configure(struct rkisp1_params *params);
+
+/* rkisp1_params_disable - disable all parameters.
+ * This function is called by the isp entity upon stream start
+ * when capturing bayer format.
+ *
+ * @params: pointer to rkisp1_params.
+ */
+void rkisp1_params_disable(struct rkisp1_params *params);
+
+/* irq handlers */
+irqreturn_t rkisp1_isp_isr(int irq, void *ctx);
+irqreturn_t rkisp1_csi_isr(int irq, void *ctx);
+irqreturn_t rkisp1_capture_isr(int irq, void *ctx);
+void rkisp1_stats_isr(struct rkisp1_stats *stats, u32 isp_ris);
+void rkisp1_params_isr(struct rkisp1_device *rkisp1);
+
+/* register/unregisters functions of the entities */
+int rkisp1_capture_devs_register(struct rkisp1_device *rkisp1);
+void rkisp1_capture_devs_unregister(struct rkisp1_device *rkisp1);
+
+int rkisp1_isp_register(struct rkisp1_device *rkisp1);
+void rkisp1_isp_unregister(struct rkisp1_device *rkisp1);
+
+int rkisp1_resizer_devs_register(struct rkisp1_device *rkisp1);
+void rkisp1_resizer_devs_unregister(struct rkisp1_device *rkisp1);
+
+int rkisp1_stats_register(struct rkisp1_device *rkisp1);
+void rkisp1_stats_unregister(struct rkisp1_device *rkisp1);
+
+int rkisp1_params_register(struct rkisp1_device *rkisp1);
+void rkisp1_params_unregister(struct rkisp1_device *rkisp1);
+
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+void rkisp1_debug_init(struct rkisp1_device *rkisp1);
+void rkisp1_debug_cleanup(struct rkisp1_device *rkisp1);
+#else
+static inline void rkisp1_debug_init(struct rkisp1_device *rkisp1)
+{
+}
+static inline void rkisp1_debug_cleanup(struct rkisp1_device *rkisp1)
+{
+}
+#endif
+
+#endif /* _RKISP1_COMMON_H */
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c
new file mode 100644
index 0000000000..fdff3d0da4
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c
@@ -0,0 +1,541 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Rockchip ISP1 Driver - CSI-2 Receiver
+ *
+ * Copyright (C) 2019 Collabora, Ltd.
+ * Copyright (C) 2022 Ideas on Board
+ *
+ * Based on Rockchip ISP1 driver by Rockchip Electronics Co., Ltd.
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/lockdep.h>
+#include <linux/phy/phy.h>
+#include <linux/phy/phy-mipi-dphy.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fwnode.h>
+
+#include "rkisp1-common.h"
+#include "rkisp1-csi.h"
+
+#define RKISP1_CSI_DEV_NAME RKISP1_DRIVER_NAME "_csi"
+
+#define RKISP1_CSI_DEF_FMT MEDIA_BUS_FMT_SRGGB10_1X10
+
+static inline struct rkisp1_csi *to_rkisp1_csi(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct rkisp1_csi, sd);
+}
+
+static struct v4l2_mbus_framefmt *
+rkisp1_csi_get_pad_fmt(struct rkisp1_csi *csi,
+ struct v4l2_subdev_state *sd_state,
+ unsigned int pad, u32 which)
+{
+ struct v4l2_subdev_state state = {
+ .pads = csi->pad_cfg
+ };
+
+ lockdep_assert_held(&csi->lock);
+
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_format(&csi->sd, sd_state, pad);
+ else
+ return v4l2_subdev_get_try_format(&csi->sd, &state, pad);
+}
+
+int rkisp1_csi_link_sensor(struct rkisp1_device *rkisp1, struct v4l2_subdev *sd,
+ struct rkisp1_sensor_async *s_asd,
+ unsigned int source_pad)
+{
+ struct rkisp1_csi *csi = &rkisp1->csi;
+ int ret;
+
+ s_asd->pixel_rate_ctrl = v4l2_ctrl_find(sd->ctrl_handler,
+ V4L2_CID_PIXEL_RATE);
+ if (!s_asd->pixel_rate_ctrl) {
+ dev_err(rkisp1->dev, "No pixel rate control in subdev %s\n",
+ sd->name);
+ return -EINVAL;
+ }
+
+ /* Create the link from the sensor to the CSI receiver. */
+ ret = media_create_pad_link(&sd->entity, source_pad,
+ &csi->sd.entity, RKISP1_CSI_PAD_SINK,
+ !s_asd->index ? MEDIA_LNK_FL_ENABLED : 0);
+ if (ret) {
+ dev_err(csi->rkisp1->dev, "failed to link src pad of %s\n",
+ sd->name);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rkisp1_csi_config(struct rkisp1_csi *csi,
+ const struct rkisp1_sensor_async *sensor)
+{
+ struct rkisp1_device *rkisp1 = csi->rkisp1;
+ unsigned int lanes = sensor->lanes;
+ u32 mipi_ctrl;
+
+ if (lanes < 1 || lanes > 4)
+ return -EINVAL;
+
+ mipi_ctrl = RKISP1_CIF_MIPI_CTRL_NUM_LANES(lanes - 1) |
+ RKISP1_CIF_MIPI_CTRL_SHUTDOWNLANES(0xf) |
+ RKISP1_CIF_MIPI_CTRL_ERR_SOT_SYNC_HS_SKIP |
+ RKISP1_CIF_MIPI_CTRL_CLOCKLANE_ENA;
+
+ rkisp1_write(rkisp1, RKISP1_CIF_MIPI_CTRL, mipi_ctrl);
+
+ /* V12 could also use a newer csi2-host, but we don't want that yet */
+ if (rkisp1->info->isp_ver == RKISP1_V12)
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_CSI0_CTRL0, 0);
+
+ /* Configure Data Type and Virtual Channel */
+ rkisp1_write(rkisp1, RKISP1_CIF_MIPI_IMG_DATA_SEL,
+ RKISP1_CIF_MIPI_DATA_SEL_DT(csi->sink_fmt->mipi_dt) |
+ RKISP1_CIF_MIPI_DATA_SEL_VC(0));
+
+ /* Clear MIPI interrupts */
+ rkisp1_write(rkisp1, RKISP1_CIF_MIPI_ICR, ~0);
+
+ /*
+ * Disable RKISP1_CIF_MIPI_ERR_DPHY interrupt here temporary for
+ * isp bus may be dead when switch isp.
+ */
+ rkisp1_write(rkisp1, RKISP1_CIF_MIPI_IMSC,
+ RKISP1_CIF_MIPI_FRAME_END | RKISP1_CIF_MIPI_ERR_CSI |
+ RKISP1_CIF_MIPI_ERR_DPHY |
+ RKISP1_CIF_MIPI_SYNC_FIFO_OVFLW(0x03) |
+ RKISP1_CIF_MIPI_ADD_DATA_OVFLW);
+
+ dev_dbg(rkisp1->dev, "\n MIPI_CTRL 0x%08x\n"
+ " MIPI_IMG_DATA_SEL 0x%08x\n"
+ " MIPI_STATUS 0x%08x\n"
+ " MIPI_IMSC 0x%08x\n",
+ rkisp1_read(rkisp1, RKISP1_CIF_MIPI_CTRL),
+ rkisp1_read(rkisp1, RKISP1_CIF_MIPI_IMG_DATA_SEL),
+ rkisp1_read(rkisp1, RKISP1_CIF_MIPI_STATUS),
+ rkisp1_read(rkisp1, RKISP1_CIF_MIPI_IMSC));
+
+ return 0;
+}
+
+static void rkisp1_csi_enable(struct rkisp1_csi *csi)
+{
+ struct rkisp1_device *rkisp1 = csi->rkisp1;
+ u32 val;
+
+ val = rkisp1_read(rkisp1, RKISP1_CIF_MIPI_CTRL);
+ rkisp1_write(rkisp1, RKISP1_CIF_MIPI_CTRL,
+ val | RKISP1_CIF_MIPI_CTRL_OUTPUT_ENA);
+}
+
+static void rkisp1_csi_disable(struct rkisp1_csi *csi)
+{
+ struct rkisp1_device *rkisp1 = csi->rkisp1;
+ u32 val;
+
+ /* Mask and clear interrupts. */
+ rkisp1_write(rkisp1, RKISP1_CIF_MIPI_IMSC, 0);
+ rkisp1_write(rkisp1, RKISP1_CIF_MIPI_ICR, ~0);
+
+ val = rkisp1_read(rkisp1, RKISP1_CIF_MIPI_CTRL);
+ rkisp1_write(rkisp1, RKISP1_CIF_MIPI_CTRL,
+ val & (~RKISP1_CIF_MIPI_CTRL_OUTPUT_ENA));
+}
+
+static int rkisp1_csi_start(struct rkisp1_csi *csi,
+ const struct rkisp1_sensor_async *sensor)
+{
+ struct rkisp1_device *rkisp1 = csi->rkisp1;
+ union phy_configure_opts opts;
+ struct phy_configure_opts_mipi_dphy *cfg = &opts.mipi_dphy;
+ s64 pixel_clock;
+ int ret;
+
+ ret = rkisp1_csi_config(csi, sensor);
+ if (ret)
+ return ret;
+
+ pixel_clock = v4l2_ctrl_g_ctrl_int64(sensor->pixel_rate_ctrl);
+ if (!pixel_clock) {
+ dev_err(rkisp1->dev, "Invalid pixel rate value\n");
+ return -EINVAL;
+ }
+
+ phy_mipi_dphy_get_default_config(pixel_clock, csi->sink_fmt->bus_width,
+ sensor->lanes, cfg);
+ phy_set_mode(csi->dphy, PHY_MODE_MIPI_DPHY);
+ phy_configure(csi->dphy, &opts);
+ phy_power_on(csi->dphy);
+
+ rkisp1_csi_enable(csi);
+
+ /*
+ * CIF spec says to wait for sufficient time after enabling
+ * the MIPI interface and before starting the sensor output.
+ */
+ usleep_range(1000, 1200);
+
+ return 0;
+}
+
+static void rkisp1_csi_stop(struct rkisp1_csi *csi)
+{
+ rkisp1_csi_disable(csi);
+
+ phy_power_off(csi->dphy);
+}
+
+irqreturn_t rkisp1_csi_isr(int irq, void *ctx)
+{
+ struct device *dev = ctx;
+ struct rkisp1_device *rkisp1 = dev_get_drvdata(dev);
+ u32 val, status;
+
+ status = rkisp1_read(rkisp1, RKISP1_CIF_MIPI_MIS);
+ if (!status)
+ return IRQ_NONE;
+
+ rkisp1_write(rkisp1, RKISP1_CIF_MIPI_ICR, status);
+
+ /*
+ * Disable DPHY errctrl interrupt, because this dphy
+ * erctrl signal is asserted until the next changes
+ * of line state. This time is may be too long and cpu
+ * is hold in this interrupt.
+ */
+ if (status & RKISP1_CIF_MIPI_ERR_CTRL(0x0f)) {
+ val = rkisp1_read(rkisp1, RKISP1_CIF_MIPI_IMSC);
+ rkisp1_write(rkisp1, RKISP1_CIF_MIPI_IMSC,
+ val & ~RKISP1_CIF_MIPI_ERR_CTRL(0x0f));
+ rkisp1->csi.is_dphy_errctrl_disabled = true;
+ }
+
+ /*
+ * Enable DPHY errctrl interrupt again, if mipi have receive
+ * the whole frame without any error.
+ */
+ if (status == RKISP1_CIF_MIPI_FRAME_END) {
+ /*
+ * Enable DPHY errctrl interrupt again, if mipi have receive
+ * the whole frame without any error.
+ */
+ if (rkisp1->csi.is_dphy_errctrl_disabled) {
+ val = rkisp1_read(rkisp1, RKISP1_CIF_MIPI_IMSC);
+ val |= RKISP1_CIF_MIPI_ERR_CTRL(0x0f);
+ rkisp1_write(rkisp1, RKISP1_CIF_MIPI_IMSC, val);
+ rkisp1->csi.is_dphy_errctrl_disabled = false;
+ }
+ } else {
+ rkisp1->debug.mipi_error++;
+ }
+
+ return IRQ_HANDLED;
+}
+
+/* ----------------------------------------------------------------------------
+ * Subdev pad operations
+ */
+
+static int rkisp1_csi_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct rkisp1_csi *csi = to_rkisp1_csi(sd);
+ unsigned int i;
+ int pos = 0;
+
+ if (code->pad == RKISP1_CSI_PAD_SRC) {
+ const struct v4l2_mbus_framefmt *sink_fmt;
+
+ if (code->index)
+ return -EINVAL;
+
+ mutex_lock(&csi->lock);
+
+ sink_fmt = rkisp1_csi_get_pad_fmt(csi, sd_state,
+ RKISP1_CSI_PAD_SINK,
+ code->which);
+ code->code = sink_fmt->code;
+
+ mutex_unlock(&csi->lock);
+
+ return 0;
+ }
+
+ for (i = 0; ; i++) {
+ const struct rkisp1_mbus_info *fmt =
+ rkisp1_mbus_info_get_by_index(i);
+
+ if (!fmt)
+ return -EINVAL;
+
+ if (!(fmt->direction & RKISP1_ISP_SD_SINK))
+ continue;
+
+ if (code->index == pos) {
+ code->code = fmt->mbus_code;
+ return 0;
+ }
+
+ pos++;
+ }
+
+ return -EINVAL;
+}
+
+static int rkisp1_csi_init_config(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state)
+{
+ struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
+
+ sink_fmt = v4l2_subdev_get_try_format(sd, sd_state,
+ RKISP1_CSI_PAD_SINK);
+ src_fmt = v4l2_subdev_get_try_format(sd, sd_state,
+ RKISP1_CSI_PAD_SRC);
+
+ sink_fmt->width = RKISP1_DEFAULT_WIDTH;
+ sink_fmt->height = RKISP1_DEFAULT_HEIGHT;
+ sink_fmt->field = V4L2_FIELD_NONE;
+ sink_fmt->code = RKISP1_CSI_DEF_FMT;
+
+ *src_fmt = *sink_fmt;
+
+ return 0;
+}
+
+static int rkisp1_csi_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct rkisp1_csi *csi = to_rkisp1_csi(sd);
+
+ mutex_lock(&csi->lock);
+ fmt->format = *rkisp1_csi_get_pad_fmt(csi, sd_state, fmt->pad,
+ fmt->which);
+ mutex_unlock(&csi->lock);
+
+ return 0;
+}
+
+static int rkisp1_csi_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct rkisp1_csi *csi = to_rkisp1_csi(sd);
+ const struct rkisp1_mbus_info *mbus_info;
+ struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
+
+ /* The format on the source pad always matches the sink pad. */
+ if (fmt->pad == RKISP1_CSI_PAD_SRC)
+ return rkisp1_csi_get_fmt(sd, sd_state, fmt);
+
+ mutex_lock(&csi->lock);
+
+ sink_fmt = rkisp1_csi_get_pad_fmt(csi, sd_state, RKISP1_CSI_PAD_SINK,
+ fmt->which);
+
+ sink_fmt->code = fmt->format.code;
+
+ mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code);
+ if (!mbus_info || !(mbus_info->direction & RKISP1_ISP_SD_SINK)) {
+ sink_fmt->code = RKISP1_CSI_DEF_FMT;
+ mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code);
+ }
+
+ sink_fmt->width = clamp_t(u32, fmt->format.width,
+ RKISP1_ISP_MIN_WIDTH,
+ RKISP1_ISP_MAX_WIDTH);
+ sink_fmt->height = clamp_t(u32, fmt->format.height,
+ RKISP1_ISP_MIN_HEIGHT,
+ RKISP1_ISP_MAX_HEIGHT);
+
+ fmt->format = *sink_fmt;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ csi->sink_fmt = mbus_info;
+
+ /* Propagate the format to the source pad. */
+ src_fmt = rkisp1_csi_get_pad_fmt(csi, sd_state, RKISP1_CSI_PAD_SRC,
+ fmt->which);
+ *src_fmt = *sink_fmt;
+
+ mutex_unlock(&csi->lock);
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------------
+ * Subdev video operations
+ */
+
+static int rkisp1_csi_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct rkisp1_csi *csi = to_rkisp1_csi(sd);
+ struct rkisp1_device *rkisp1 = csi->rkisp1;
+ struct rkisp1_sensor_async *source_asd;
+ struct v4l2_async_connection *asc;
+ struct media_pad *source_pad;
+ struct v4l2_subdev *source;
+ int ret;
+
+ if (!enable) {
+ v4l2_subdev_call(csi->source, video, s_stream, false);
+
+ rkisp1_csi_stop(csi);
+
+ return 0;
+ }
+
+ source_pad = media_entity_remote_source_pad_unique(&sd->entity);
+ if (IS_ERR(source_pad)) {
+ dev_dbg(rkisp1->dev, "Failed to get source for CSI: %ld\n",
+ PTR_ERR(source_pad));
+ return -EPIPE;
+ }
+
+ source = media_entity_to_v4l2_subdev(source_pad->entity);
+ if (!source) {
+ /* This should really not happen, so is not worth a message. */
+ return -EPIPE;
+ }
+
+ asc = v4l2_async_connection_unique(source);
+ if (!asc)
+ return -EPIPE;
+
+ source_asd = container_of(asc, struct rkisp1_sensor_async, asd);
+ if (source_asd->mbus_type != V4L2_MBUS_CSI2_DPHY)
+ return -EINVAL;
+
+ mutex_lock(&csi->lock);
+ ret = rkisp1_csi_start(csi, source_asd);
+ mutex_unlock(&csi->lock);
+ if (ret)
+ return ret;
+
+ ret = v4l2_subdev_call(source, video, s_stream, true);
+ if (ret) {
+ rkisp1_csi_stop(csi);
+ return ret;
+ }
+
+ csi->source = source;
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------------
+ * Registration
+ */
+
+static const struct media_entity_operations rkisp1_csi_media_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+static const struct v4l2_subdev_video_ops rkisp1_csi_video_ops = {
+ .s_stream = rkisp1_csi_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops rkisp1_csi_pad_ops = {
+ .enum_mbus_code = rkisp1_csi_enum_mbus_code,
+ .init_cfg = rkisp1_csi_init_config,
+ .get_fmt = rkisp1_csi_get_fmt,
+ .set_fmt = rkisp1_csi_set_fmt,
+};
+
+static const struct v4l2_subdev_ops rkisp1_csi_ops = {
+ .video = &rkisp1_csi_video_ops,
+ .pad = &rkisp1_csi_pad_ops,
+};
+
+int rkisp1_csi_register(struct rkisp1_device *rkisp1)
+{
+ struct rkisp1_csi *csi = &rkisp1->csi;
+ struct v4l2_subdev_state state = {};
+ struct media_pad *pads;
+ struct v4l2_subdev *sd;
+ int ret;
+
+ csi->rkisp1 = rkisp1;
+ mutex_init(&csi->lock);
+
+ sd = &csi->sd;
+ v4l2_subdev_init(sd, &rkisp1_csi_ops);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sd->entity.ops = &rkisp1_csi_media_ops;
+ sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+ sd->owner = THIS_MODULE;
+ strscpy(sd->name, RKISP1_CSI_DEV_NAME, sizeof(sd->name));
+
+ pads = csi->pads;
+ pads[RKISP1_CSI_PAD_SINK].flags = MEDIA_PAD_FL_SINK |
+ MEDIA_PAD_FL_MUST_CONNECT;
+ pads[RKISP1_CSI_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE |
+ MEDIA_PAD_FL_MUST_CONNECT;
+
+ csi->sink_fmt = rkisp1_mbus_info_get_by_code(RKISP1_CSI_DEF_FMT);
+
+ ret = media_entity_pads_init(&sd->entity, RKISP1_CSI_PAD_NUM, pads);
+ if (ret)
+ goto error;
+
+ state.pads = csi->pad_cfg;
+ rkisp1_csi_init_config(sd, &state);
+
+ ret = v4l2_device_register_subdev(&csi->rkisp1->v4l2_dev, sd);
+ if (ret) {
+ dev_err(sd->dev, "Failed to register csi receiver subdev\n");
+ goto error;
+ }
+
+ return 0;
+
+error:
+ media_entity_cleanup(&sd->entity);
+ mutex_destroy(&csi->lock);
+ csi->rkisp1 = NULL;
+ return ret;
+}
+
+void rkisp1_csi_unregister(struct rkisp1_device *rkisp1)
+{
+ struct rkisp1_csi *csi = &rkisp1->csi;
+
+ if (!csi->rkisp1)
+ return;
+
+ v4l2_device_unregister_subdev(&csi->sd);
+ media_entity_cleanup(&csi->sd.entity);
+ mutex_destroy(&csi->lock);
+}
+
+int rkisp1_csi_init(struct rkisp1_device *rkisp1)
+{
+ struct rkisp1_csi *csi = &rkisp1->csi;
+
+ csi->rkisp1 = rkisp1;
+
+ csi->dphy = devm_phy_get(rkisp1->dev, "dphy");
+ if (IS_ERR(csi->dphy))
+ return dev_err_probe(rkisp1->dev, PTR_ERR(csi->dphy),
+ "Couldn't get the MIPI D-PHY\n");
+
+ phy_init(csi->dphy);
+
+ return 0;
+}
+
+void rkisp1_csi_cleanup(struct rkisp1_device *rkisp1)
+{
+ struct rkisp1_csi *csi = &rkisp1->csi;
+
+ phy_exit(csi->dphy);
+}
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.h
new file mode 100644
index 0000000000..1f5f2af31a
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
+/*
+ * Rockchip ISP1 Driver - CSI-2 Receiver
+ *
+ * Copyright (C) 2019 Collabora, Ltd.
+ * Copyright (C) 2022 Ideas on Board
+ *
+ * Based on Rockchip ISP1 driver by Rockchip Electronics Co., Ltd.
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ */
+#ifndef _RKISP1_CSI_H
+#define _RKISP1_CSI_H
+
+struct rkisp1_csi;
+struct rkisp1_device;
+struct rkisp1_sensor_async;
+
+int rkisp1_csi_init(struct rkisp1_device *rkisp1);
+void rkisp1_csi_cleanup(struct rkisp1_device *rkisp1);
+
+int rkisp1_csi_register(struct rkisp1_device *rkisp1);
+void rkisp1_csi_unregister(struct rkisp1_device *rkisp1);
+
+int rkisp1_csi_link_sensor(struct rkisp1_device *rkisp1, struct v4l2_subdev *sd,
+ struct rkisp1_sensor_async *s_asd,
+ unsigned int source_pad);
+
+#endif /* _RKISP1_CSI_H */
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-debug.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-debug.c
new file mode 100644
index 0000000000..71df3dc95e
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-debug.c
@@ -0,0 +1,243 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Rockchip ISP1 Driver - Base driver
+ *
+ * Copyright (C) 2019 Collabora, Ltd.
+ *
+ * Based on Rockchip ISP1 driver by Rockchip Electronics Co., Ltd.
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/minmax.h>
+#include <linux/pm_runtime.h>
+#include <linux/seq_file.h>
+#include <linux/string.h>
+
+#include "rkisp1-common.h"
+#include "rkisp1-regs.h"
+
+struct rkisp1_debug_register {
+ u32 reg;
+ u32 shd;
+ const char * const name;
+};
+
+#define RKISP1_DEBUG_REG(name) { RKISP1_CIF_##name, 0, #name }
+#define RKISP1_DEBUG_SHD_REG(name) { \
+ RKISP1_CIF_##name, RKISP1_CIF_##name##_SHD, #name \
+}
+
+/* Keep this up-to-date when adding new registers. */
+#define RKISP1_MAX_REG_LENGTH 21
+
+static int rkisp1_debug_dump_regs(struct rkisp1_device *rkisp1,
+ struct seq_file *m, unsigned int offset,
+ const struct rkisp1_debug_register *regs)
+{
+ const int width = RKISP1_MAX_REG_LENGTH;
+ u32 val, shd;
+ int ret;
+
+ ret = pm_runtime_get_if_in_use(rkisp1->dev);
+ if (ret <= 0)
+ return ret ? : -ENODATA;
+
+ for (; regs->name; ++regs) {
+ val = rkisp1_read(rkisp1, offset + regs->reg);
+
+ if (regs->shd) {
+ shd = rkisp1_read(rkisp1, offset + regs->shd);
+ seq_printf(m, "%*s: 0x%08x/0x%08x\n", width, regs->name,
+ val, shd);
+ } else {
+ seq_printf(m, "%*s: 0x%08x\n", width, regs->name, val);
+ }
+ }
+
+ pm_runtime_put(rkisp1->dev);
+
+ return 0;
+}
+
+static int rkisp1_debug_dump_core_regs_show(struct seq_file *m, void *p)
+{
+ static const struct rkisp1_debug_register registers[] = {
+ RKISP1_DEBUG_REG(VI_CCL),
+ RKISP1_DEBUG_REG(VI_ICCL),
+ RKISP1_DEBUG_REG(VI_IRCL),
+ RKISP1_DEBUG_REG(VI_DPCL),
+ RKISP1_DEBUG_REG(MI_CTRL),
+ RKISP1_DEBUG_REG(MI_BYTE_CNT),
+ RKISP1_DEBUG_REG(MI_CTRL_SHD),
+ RKISP1_DEBUG_REG(MI_RIS),
+ RKISP1_DEBUG_REG(MI_STATUS),
+ RKISP1_DEBUG_REG(MI_DMA_CTRL),
+ RKISP1_DEBUG_REG(MI_DMA_STATUS),
+ { /* Sentinel */ },
+ };
+ struct rkisp1_device *rkisp1 = m->private;
+
+ return rkisp1_debug_dump_regs(rkisp1, m, 0, registers);
+}
+DEFINE_SHOW_ATTRIBUTE(rkisp1_debug_dump_core_regs);
+
+static int rkisp1_debug_dump_isp_regs_show(struct seq_file *m, void *p)
+{
+ static const struct rkisp1_debug_register registers[] = {
+ RKISP1_DEBUG_REG(ISP_CTRL),
+ RKISP1_DEBUG_REG(ISP_ACQ_PROP),
+ RKISP1_DEBUG_REG(ISP_FLAGS_SHD),
+ RKISP1_DEBUG_REG(ISP_RIS),
+ RKISP1_DEBUG_REG(ISP_ERR),
+ { /* Sentinel */ },
+ };
+ struct rkisp1_device *rkisp1 = m->private;
+
+ return rkisp1_debug_dump_regs(rkisp1, m, 0, registers);
+}
+DEFINE_SHOW_ATTRIBUTE(rkisp1_debug_dump_isp_regs);
+
+static int rkisp1_debug_dump_rsz_regs_show(struct seq_file *m, void *p)
+{
+ static const struct rkisp1_debug_register registers[] = {
+ RKISP1_DEBUG_SHD_REG(RSZ_CTRL),
+ RKISP1_DEBUG_SHD_REG(RSZ_SCALE_HY),
+ RKISP1_DEBUG_SHD_REG(RSZ_SCALE_HCB),
+ RKISP1_DEBUG_SHD_REG(RSZ_SCALE_HCR),
+ RKISP1_DEBUG_SHD_REG(RSZ_SCALE_VY),
+ RKISP1_DEBUG_SHD_REG(RSZ_SCALE_VC),
+ RKISP1_DEBUG_SHD_REG(RSZ_PHASE_HY),
+ RKISP1_DEBUG_SHD_REG(RSZ_PHASE_HC),
+ RKISP1_DEBUG_SHD_REG(RSZ_PHASE_VY),
+ RKISP1_DEBUG_SHD_REG(RSZ_PHASE_VC),
+ { /* Sentinel */ },
+ };
+ struct rkisp1_resizer *rsz = m->private;
+
+ return rkisp1_debug_dump_regs(rsz->rkisp1, m, rsz->regs_base, registers);
+}
+DEFINE_SHOW_ATTRIBUTE(rkisp1_debug_dump_rsz_regs);
+
+static int rkisp1_debug_dump_mi_mp_show(struct seq_file *m, void *p)
+{
+ static const struct rkisp1_debug_register registers[] = {
+ RKISP1_DEBUG_REG(MI_MP_Y_BASE_AD_INIT),
+ RKISP1_DEBUG_REG(MI_MP_Y_BASE_AD_INIT2),
+ RKISP1_DEBUG_REG(MI_MP_Y_BASE_AD_SHD),
+ RKISP1_DEBUG_REG(MI_MP_Y_SIZE_INIT),
+ RKISP1_DEBUG_REG(MI_MP_Y_SIZE_INIT),
+ RKISP1_DEBUG_REG(MI_MP_Y_SIZE_SHD),
+ RKISP1_DEBUG_REG(MI_MP_Y_OFFS_CNT_SHD),
+ { /* Sentinel */ },
+ };
+ struct rkisp1_device *rkisp1 = m->private;
+
+ return rkisp1_debug_dump_regs(rkisp1, m, 0, registers);
+}
+DEFINE_SHOW_ATTRIBUTE(rkisp1_debug_dump_mi_mp);
+
+#define RKISP1_DEBUG_DATA_COUNT_BINS 32
+#define RKISP1_DEBUG_DATA_COUNT_STEP (4096 / RKISP1_DEBUG_DATA_COUNT_BINS)
+
+static int rkisp1_debug_input_status_show(struct seq_file *m, void *p)
+{
+ struct rkisp1_device *rkisp1 = m->private;
+ u16 data_count[RKISP1_DEBUG_DATA_COUNT_BINS] = { };
+ unsigned int hsync_count = 0;
+ unsigned int vsync_count = 0;
+ unsigned int i;
+ u32 data;
+ u32 val;
+ int ret;
+
+ ret = pm_runtime_get_if_in_use(rkisp1->dev);
+ if (ret <= 0)
+ return ret ? : -ENODATA;
+
+ /* Sample the ISP input port status 10000 times with a 1µs interval. */
+ for (i = 0; i < 10000; ++i) {
+ val = rkisp1_read(rkisp1, RKISP1_CIF_ISP_FLAGS_SHD);
+
+ data = (val & RKISP1_CIF_ISP_FLAGS_SHD_S_DATA_MASK)
+ >> RKISP1_CIF_ISP_FLAGS_SHD_S_DATA_SHIFT;
+ data_count[data / RKISP1_DEBUG_DATA_COUNT_STEP]++;
+
+ if (val & RKISP1_CIF_ISP_FLAGS_SHD_S_HSYNC)
+ hsync_count++;
+ if (val & RKISP1_CIF_ISP_FLAGS_SHD_S_VSYNC)
+ vsync_count++;
+
+ udelay(1);
+ }
+
+ pm_runtime_put(rkisp1->dev);
+
+ seq_printf(m, "vsync: %u, hsync: %u\n", vsync_count, hsync_count);
+ seq_puts(m, "data:\n");
+ for (i = 0; i < ARRAY_SIZE(data_count); ++i)
+ seq_printf(m, "- [%04u:%04u]: %u\n",
+ i * RKISP1_DEBUG_DATA_COUNT_STEP,
+ (i + 1) * RKISP1_DEBUG_DATA_COUNT_STEP - 1,
+ data_count[i]);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(rkisp1_debug_input_status);
+
+void rkisp1_debug_init(struct rkisp1_device *rkisp1)
+{
+ struct rkisp1_debug *debug = &rkisp1->debug;
+ struct dentry *regs_dir;
+
+ debug->debugfs_dir = debugfs_create_dir(dev_name(rkisp1->dev), NULL);
+
+ debugfs_create_ulong("data_loss", 0444, debug->debugfs_dir,
+ &debug->data_loss);
+ debugfs_create_ulong("outform_size_err", 0444, debug->debugfs_dir,
+ &debug->outform_size_error);
+ debugfs_create_ulong("img_stabilization_size_error", 0444,
+ debug->debugfs_dir,
+ &debug->img_stabilization_size_error);
+ debugfs_create_ulong("inform_size_error", 0444, debug->debugfs_dir,
+ &debug->inform_size_error);
+ debugfs_create_ulong("irq_delay", 0444, debug->debugfs_dir,
+ &debug->irq_delay);
+ debugfs_create_ulong("mipi_error", 0444, debug->debugfs_dir,
+ &debug->mipi_error);
+ debugfs_create_ulong("stats_error", 0444, debug->debugfs_dir,
+ &debug->stats_error);
+ debugfs_create_ulong("mp_stop_timeout", 0444, debug->debugfs_dir,
+ &debug->stop_timeout[RKISP1_MAINPATH]);
+ debugfs_create_ulong("sp_stop_timeout", 0444, debug->debugfs_dir,
+ &debug->stop_timeout[RKISP1_SELFPATH]);
+ debugfs_create_ulong("mp_frame_drop", 0444, debug->debugfs_dir,
+ &debug->frame_drop[RKISP1_MAINPATH]);
+ debugfs_create_ulong("sp_frame_drop", 0444, debug->debugfs_dir,
+ &debug->frame_drop[RKISP1_SELFPATH]);
+ debugfs_create_file("input_status", 0444, debug->debugfs_dir, rkisp1,
+ &rkisp1_debug_input_status_fops);
+
+ regs_dir = debugfs_create_dir("regs", debug->debugfs_dir);
+
+ debugfs_create_file("core", 0444, regs_dir, rkisp1,
+ &rkisp1_debug_dump_core_regs_fops);
+ debugfs_create_file("isp", 0444, regs_dir, rkisp1,
+ &rkisp1_debug_dump_isp_regs_fops);
+ debugfs_create_file("mrsz", 0444, regs_dir,
+ &rkisp1->resizer_devs[RKISP1_MAINPATH],
+ &rkisp1_debug_dump_rsz_regs_fops);
+ debugfs_create_file("srsz", 0444, regs_dir,
+ &rkisp1->resizer_devs[RKISP1_SELFPATH],
+ &rkisp1_debug_dump_rsz_regs_fops);
+
+ debugfs_create_file("mi_mp", 0444, regs_dir, rkisp1,
+ &rkisp1_debug_dump_mi_mp_fops);
+}
+
+void rkisp1_debug_cleanup(struct rkisp1_device *rkisp1)
+{
+ debugfs_remove_recursive(rkisp1->debug.debugfs_dir);
+}
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c
new file mode 100644
index 0000000000..894d5afaff
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c
@@ -0,0 +1,659 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Rockchip ISP1 Driver - Base driver
+ *
+ * Copyright (C) 2019 Collabora, Ltd.
+ *
+ * Based on Rockchip ISP1 driver by Rockchip Electronics Co., Ltd.
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pm_runtime.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mc.h>
+
+#include "rkisp1-common.h"
+#include "rkisp1-csi.h"
+
+/*
+ * ISP Details
+ * -----------
+ *
+ * ISP Comprises with:
+ * MIPI serial camera interface
+ * Image Signal Processing
+ * Many Image Enhancement Blocks
+ * Crop
+ * Resizer
+ * RBG display ready image
+ * Image Rotation
+ *
+ * ISP Block Diagram
+ * -----------------
+ * rkisp1-resizer.c rkisp1-capture.c
+ * |====================| |=======================|
+ * rkisp1-isp.c Main Picture Path
+ * |==========================| |===============================================|
+ * +-----------+ +--+--+--+--+ +--------+ +--------+ +-----------+
+ * | | | | | | | | | | | | |
+ * +--------+ |\ | | | | | | | -->| Crop |->| RSZ |------------->| |
+ * | MIPI |--->| \ | | | | | | | | | | | | | |
+ * +--------+ | | | | |IE|IE|IE|IE| | +--------+ +--------+ | Memory |
+ * |MUX|--->| ISP |->|0 |1 |2 |3 |---+ | Interface |
+ * +--------+ | | | | | | | | | | +--------+ +--------+ +--------+ | |
+ * |Parallel|--->| / | | | | | | | | | | | | | | | |
+ * +--------+ |/ | | | | | | | -->| Crop |->| RSZ |->| RGB |->| |
+ * | | | | | | | | | | | | Rotate | | |
+ * +-----------+ +--+--+--+--+ +--------+ +--------+ +--------+ +-----------+
+ * ^
+ * +--------+ | |===============================================|
+ * | DMA |------------------------------------+ Self Picture Path
+ * +--------+
+ *
+ * rkisp1-stats.c rkisp1-params.c
+ * |===============| |===============|
+ * +---------------+ +---------------+
+ * | | | |
+ * | ISP | | ISP |
+ * | | | |
+ * +---------------+ +---------------+
+ *
+ *
+ * Media Topology
+ * --------------
+ *
+ * +----------+ +----------+
+ * | Sensor 1 | | Sensor X |
+ * ------------ ... ------------
+ * | 0 | | 0 |
+ * +----------+ +----------+
+ * | |
+ * \----\ /----/
+ * | |
+ * v v
+ * +-------------+
+ * | 0 |
+ * ---------------
+ * | CSI-2 RX |
+ * --------------- +-----------+
+ * | 1 | | params |
+ * +-------------+ | (output) |
+ * | +-----------+
+ * v |
+ * +------+------+ |
+ * | 0 | 1 |<---------+
+ * |------+------|
+ * | ISP |
+ * |------+------|
+ * +-------------| 2 | 3 |----------+
+ * | +------+------+ |
+ * | | |
+ * v v v
+ * +- ---------+ +-----------+ +-----------+
+ * | 0 | | 0 | | stats |
+ * ------------- ------------- | (capture) |
+ * | Resizer | | Resizer | +-----------+
+ * ------------| ------------|
+ * | 1 | | 1 |
+ * +-----------+ +-----------+
+ * | |
+ * v v
+ * +-----------+ +-----------+
+ * | selfpath | | mainpath |
+ * | (capture) | | (capture) |
+ * +-----------+ +-----------+
+ */
+
+struct rkisp1_isr_data {
+ const char *name;
+ irqreturn_t (*isr)(int irq, void *ctx);
+};
+
+/* ----------------------------------------------------------------------------
+ * Sensor DT bindings
+ */
+
+static int rkisp1_subdev_notifier_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_connection *asc)
+{
+ struct rkisp1_device *rkisp1 =
+ container_of(notifier, struct rkisp1_device, notifier);
+ struct rkisp1_sensor_async *s_asd =
+ container_of(asc, struct rkisp1_sensor_async, asd);
+ int source_pad;
+ int ret;
+
+ s_asd->sd = sd;
+
+ source_pad = media_entity_get_fwnode_pad(&sd->entity, s_asd->source_ep,
+ MEDIA_PAD_FL_SOURCE);
+ if (source_pad < 0) {
+ dev_err(rkisp1->dev, "failed to find source pad for %s\n",
+ sd->name);
+ return source_pad;
+ }
+
+ if (s_asd->port == 0)
+ return rkisp1_csi_link_sensor(rkisp1, sd, s_asd, source_pad);
+
+ ret = media_create_pad_link(&sd->entity, source_pad,
+ &rkisp1->isp.sd.entity,
+ RKISP1_ISP_PAD_SINK_VIDEO,
+ !s_asd->index ? MEDIA_LNK_FL_ENABLED : 0);
+ if (ret) {
+ dev_err(rkisp1->dev, "failed to link source pad of %s\n",
+ sd->name);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rkisp1_subdev_notifier_complete(struct v4l2_async_notifier *notifier)
+{
+ struct rkisp1_device *rkisp1 =
+ container_of(notifier, struct rkisp1_device, notifier);
+
+ return v4l2_device_register_subdev_nodes(&rkisp1->v4l2_dev);
+}
+
+static void rkisp1_subdev_notifier_destroy(struct v4l2_async_connection *asc)
+{
+ struct rkisp1_sensor_async *rk_asd =
+ container_of(asc, struct rkisp1_sensor_async, asd);
+
+ fwnode_handle_put(rk_asd->source_ep);
+}
+
+static const struct v4l2_async_notifier_operations rkisp1_subdev_notifier_ops = {
+ .bound = rkisp1_subdev_notifier_bound,
+ .complete = rkisp1_subdev_notifier_complete,
+ .destroy = rkisp1_subdev_notifier_destroy,
+};
+
+static int rkisp1_subdev_notifier_register(struct rkisp1_device *rkisp1)
+{
+ struct v4l2_async_notifier *ntf = &rkisp1->notifier;
+ struct fwnode_handle *fwnode = dev_fwnode(rkisp1->dev);
+ struct fwnode_handle *ep;
+ unsigned int index = 0;
+ int ret = 0;
+
+ v4l2_async_nf_init(ntf, &rkisp1->v4l2_dev);
+
+ ntf->ops = &rkisp1_subdev_notifier_ops;
+
+ fwnode_graph_for_each_endpoint(fwnode, ep) {
+ struct fwnode_handle *port;
+ struct v4l2_fwnode_endpoint vep = { };
+ struct rkisp1_sensor_async *rk_asd;
+ struct fwnode_handle *source;
+ u32 reg = 0;
+
+ /* Select the bus type based on the port. */
+ port = fwnode_get_parent(ep);
+ fwnode_property_read_u32(port, "reg", &reg);
+ fwnode_handle_put(port);
+
+ switch (reg) {
+ case 0:
+ /* MIPI CSI-2 port */
+ if (!(rkisp1->info->features & RKISP1_FEATURE_MIPI_CSI2)) {
+ dev_err(rkisp1->dev,
+ "internal CSI must be available for port 0\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ vep.bus_type = V4L2_MBUS_CSI2_DPHY;
+ break;
+
+ case 1:
+ /*
+ * Parallel port. The bus-type property in DT is
+ * mandatory for port 1, it will be used to determine if
+ * it's PARALLEL or BT656.
+ */
+ vep.bus_type = V4L2_MBUS_UNKNOWN;
+ break;
+ }
+
+ /* Parse the endpoint and validate the bus type. */
+ ret = v4l2_fwnode_endpoint_parse(ep, &vep);
+ if (ret) {
+ dev_err(rkisp1->dev, "failed to parse endpoint %pfw\n",
+ ep);
+ break;
+ }
+
+ if (vep.base.port == 1) {
+ if (vep.bus_type != V4L2_MBUS_PARALLEL &&
+ vep.bus_type != V4L2_MBUS_BT656) {
+ dev_err(rkisp1->dev,
+ "port 1 must be parallel or BT656\n");
+ ret = -EINVAL;
+ break;
+ }
+ }
+
+ /* Add the async subdev to the notifier. */
+ source = fwnode_graph_get_remote_endpoint(ep);
+ if (!source) {
+ dev_err(rkisp1->dev,
+ "endpoint %pfw has no remote endpoint\n",
+ ep);
+ ret = -ENODEV;
+ break;
+ }
+
+ rk_asd = v4l2_async_nf_add_fwnode(ntf, source,
+ struct rkisp1_sensor_async);
+ if (IS_ERR(rk_asd)) {
+ fwnode_handle_put(source);
+ ret = PTR_ERR(rk_asd);
+ break;
+ }
+
+ rk_asd->index = index++;
+ rk_asd->source_ep = source;
+ rk_asd->mbus_type = vep.bus_type;
+ rk_asd->port = vep.base.port;
+
+ if (vep.bus_type == V4L2_MBUS_CSI2_DPHY) {
+ rk_asd->mbus_flags = vep.bus.mipi_csi2.flags;
+ rk_asd->lanes = vep.bus.mipi_csi2.num_data_lanes;
+ } else {
+ rk_asd->mbus_flags = vep.bus.parallel.flags;
+ }
+
+ dev_dbg(rkisp1->dev, "registered ep id %d, bus type %u, %u lanes\n",
+ vep.base.id, rk_asd->mbus_type, rk_asd->lanes);
+ }
+
+ if (ret) {
+ fwnode_handle_put(ep);
+ v4l2_async_nf_cleanup(ntf);
+ return ret;
+ }
+
+ if (!index)
+ dev_dbg(rkisp1->dev, "no remote subdevice found\n");
+
+ ret = v4l2_async_nf_register(ntf);
+ if (ret) {
+ v4l2_async_nf_cleanup(ntf);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------------
+ * Power
+ */
+
+static int __maybe_unused rkisp1_runtime_suspend(struct device *dev)
+{
+ struct rkisp1_device *rkisp1 = dev_get_drvdata(dev);
+
+ clk_bulk_disable_unprepare(rkisp1->clk_size, rkisp1->clks);
+ return pinctrl_pm_select_sleep_state(dev);
+}
+
+static int __maybe_unused rkisp1_runtime_resume(struct device *dev)
+{
+ struct rkisp1_device *rkisp1 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pinctrl_pm_select_default_state(dev);
+ if (ret)
+ return ret;
+ ret = clk_bulk_prepare_enable(rkisp1->clk_size, rkisp1->clks);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const struct dev_pm_ops rkisp1_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(rkisp1_runtime_suspend, rkisp1_runtime_resume, NULL)
+};
+
+/* ----------------------------------------------------------------------------
+ * Core
+ */
+
+static int rkisp1_create_links(struct rkisp1_device *rkisp1)
+{
+ unsigned int i;
+ int ret;
+
+ if (rkisp1->info->features & RKISP1_FEATURE_MIPI_CSI2) {
+ /* Link the CSI receiver to the ISP. */
+ ret = media_create_pad_link(&rkisp1->csi.sd.entity,
+ RKISP1_CSI_PAD_SRC,
+ &rkisp1->isp.sd.entity,
+ RKISP1_ISP_PAD_SINK_VIDEO,
+ MEDIA_LNK_FL_ENABLED);
+ if (ret)
+ return ret;
+ }
+
+ /* create ISP->RSZ->CAP links */
+ for (i = 0; i < 2; i++) {
+ struct media_entity *resizer =
+ &rkisp1->resizer_devs[i].sd.entity;
+ struct media_entity *capture =
+ &rkisp1->capture_devs[i].vnode.vdev.entity;
+
+ ret = media_create_pad_link(&rkisp1->isp.sd.entity,
+ RKISP1_ISP_PAD_SOURCE_VIDEO,
+ resizer, RKISP1_RSZ_PAD_SINK,
+ MEDIA_LNK_FL_ENABLED);
+ if (ret)
+ return ret;
+
+ ret = media_create_pad_link(resizer, RKISP1_RSZ_PAD_SRC,
+ capture, 0,
+ MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+ if (ret)
+ return ret;
+ }
+
+ /* params links */
+ ret = media_create_pad_link(&rkisp1->params.vnode.vdev.entity, 0,
+ &rkisp1->isp.sd.entity,
+ RKISP1_ISP_PAD_SINK_PARAMS,
+ MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+ if (ret)
+ return ret;
+
+ /* 3A stats links */
+ return media_create_pad_link(&rkisp1->isp.sd.entity,
+ RKISP1_ISP_PAD_SOURCE_STATS,
+ &rkisp1->stats.vnode.vdev.entity, 0,
+ MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+}
+
+static void rkisp1_entities_unregister(struct rkisp1_device *rkisp1)
+{
+ if (rkisp1->info->features & RKISP1_FEATURE_MIPI_CSI2)
+ rkisp1_csi_unregister(rkisp1);
+ rkisp1_params_unregister(rkisp1);
+ rkisp1_stats_unregister(rkisp1);
+ rkisp1_capture_devs_unregister(rkisp1);
+ rkisp1_resizer_devs_unregister(rkisp1);
+ rkisp1_isp_unregister(rkisp1);
+}
+
+static int rkisp1_entities_register(struct rkisp1_device *rkisp1)
+{
+ int ret;
+
+ ret = rkisp1_isp_register(rkisp1);
+ if (ret)
+ goto error;
+
+ ret = rkisp1_resizer_devs_register(rkisp1);
+ if (ret)
+ goto error;
+
+ ret = rkisp1_capture_devs_register(rkisp1);
+ if (ret)
+ goto error;
+
+ ret = rkisp1_stats_register(rkisp1);
+ if (ret)
+ goto error;
+
+ ret = rkisp1_params_register(rkisp1);
+ if (ret)
+ goto error;
+
+ if (rkisp1->info->features & RKISP1_FEATURE_MIPI_CSI2) {
+ ret = rkisp1_csi_register(rkisp1);
+ if (ret)
+ goto error;
+ }
+
+ ret = rkisp1_create_links(rkisp1);
+ if (ret)
+ goto error;
+
+ return 0;
+
+error:
+ rkisp1_entities_unregister(rkisp1);
+ return ret;
+}
+
+static irqreturn_t rkisp1_isr(int irq, void *ctx)
+{
+ /*
+ * Call rkisp1_capture_isr() first to handle the frame that
+ * potentially completed using the current frame_sequence number before
+ * it is potentially incremented by rkisp1_isp_isr() in the vertical
+ * sync.
+ */
+ rkisp1_capture_isr(irq, ctx);
+ rkisp1_isp_isr(irq, ctx);
+ rkisp1_csi_isr(irq, ctx);
+
+ return IRQ_HANDLED;
+}
+
+static const char * const px30_isp_clks[] = {
+ "isp",
+ "aclk",
+ "hclk",
+ "pclk",
+};
+
+static const struct rkisp1_isr_data px30_isp_isrs[] = {
+ { "isp", rkisp1_isp_isr },
+ { "mi", rkisp1_capture_isr },
+ { "mipi", rkisp1_csi_isr },
+};
+
+static const struct rkisp1_info px30_isp_info = {
+ .clks = px30_isp_clks,
+ .clk_size = ARRAY_SIZE(px30_isp_clks),
+ .isrs = px30_isp_isrs,
+ .isr_size = ARRAY_SIZE(px30_isp_isrs),
+ .isp_ver = RKISP1_V12,
+ .features = RKISP1_FEATURE_MIPI_CSI2,
+};
+
+static const char * const rk3399_isp_clks[] = {
+ "isp",
+ "aclk",
+ "hclk",
+};
+
+static const struct rkisp1_isr_data rk3399_isp_isrs[] = {
+ { NULL, rkisp1_isr },
+};
+
+static const struct rkisp1_info rk3399_isp_info = {
+ .clks = rk3399_isp_clks,
+ .clk_size = ARRAY_SIZE(rk3399_isp_clks),
+ .isrs = rk3399_isp_isrs,
+ .isr_size = ARRAY_SIZE(rk3399_isp_isrs),
+ .isp_ver = RKISP1_V10,
+ .features = RKISP1_FEATURE_MIPI_CSI2,
+};
+
+static const struct of_device_id rkisp1_of_match[] = {
+ {
+ .compatible = "rockchip,px30-cif-isp",
+ .data = &px30_isp_info,
+ },
+ {
+ .compatible = "rockchip,rk3399-cif-isp",
+ .data = &rk3399_isp_info,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rkisp1_of_match);
+
+static int rkisp1_probe(struct platform_device *pdev)
+{
+ const struct rkisp1_info *info;
+ struct device *dev = &pdev->dev;
+ struct rkisp1_device *rkisp1;
+ struct v4l2_device *v4l2_dev;
+ unsigned int i;
+ int ret, irq;
+ u32 cif_id;
+
+ rkisp1 = devm_kzalloc(dev, sizeof(*rkisp1), GFP_KERNEL);
+ if (!rkisp1)
+ return -ENOMEM;
+
+ info = of_device_get_match_data(dev);
+ rkisp1->info = info;
+
+ dev_set_drvdata(dev, rkisp1);
+ rkisp1->dev = dev;
+
+ mutex_init(&rkisp1->stream_lock);
+
+ rkisp1->base_addr = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(rkisp1->base_addr))
+ return PTR_ERR(rkisp1->base_addr);
+
+ for (i = 0; i < info->isr_size; i++) {
+ irq = info->isrs[i].name
+ ? platform_get_irq_byname(pdev, info->isrs[i].name)
+ : platform_get_irq(pdev, i);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_irq(dev, irq, info->isrs[i].isr, IRQF_SHARED,
+ dev_driver_string(dev), dev);
+ if (ret) {
+ dev_err(dev, "request irq failed: %d\n", ret);
+ return ret;
+ }
+ }
+
+ for (i = 0; i < info->clk_size; i++)
+ rkisp1->clks[i].id = info->clks[i];
+ ret = devm_clk_bulk_get(dev, info->clk_size, rkisp1->clks);
+ if (ret)
+ return ret;
+ rkisp1->clk_size = info->clk_size;
+
+ pm_runtime_enable(&pdev->dev);
+
+ ret = pm_runtime_resume_and_get(&pdev->dev);
+ if (ret)
+ goto err_pm_runtime_disable;
+
+ cif_id = rkisp1_read(rkisp1, RKISP1_CIF_VI_ID);
+ dev_dbg(rkisp1->dev, "CIF_ID 0x%08x\n", cif_id);
+
+ pm_runtime_put(&pdev->dev);
+
+ rkisp1->media_dev.hw_revision = info->isp_ver;
+ strscpy(rkisp1->media_dev.model, RKISP1_DRIVER_NAME,
+ sizeof(rkisp1->media_dev.model));
+ rkisp1->media_dev.dev = &pdev->dev;
+ strscpy(rkisp1->media_dev.bus_info, RKISP1_BUS_INFO,
+ sizeof(rkisp1->media_dev.bus_info));
+ media_device_init(&rkisp1->media_dev);
+
+ v4l2_dev = &rkisp1->v4l2_dev;
+ v4l2_dev->mdev = &rkisp1->media_dev;
+ strscpy(v4l2_dev->name, RKISP1_DRIVER_NAME, sizeof(v4l2_dev->name));
+
+ ret = v4l2_device_register(rkisp1->dev, &rkisp1->v4l2_dev);
+ if (ret)
+ goto err_media_dev_cleanup;
+
+ ret = media_device_register(&rkisp1->media_dev);
+ if (ret) {
+ dev_err(dev, "Failed to register media device: %d\n", ret);
+ goto err_unreg_v4l2_dev;
+ }
+
+ if (rkisp1->info->features & RKISP1_FEATURE_MIPI_CSI2) {
+ ret = rkisp1_csi_init(rkisp1);
+ if (ret)
+ goto err_unreg_media_dev;
+ }
+
+ ret = rkisp1_entities_register(rkisp1);
+ if (ret)
+ goto err_cleanup_csi;
+
+ ret = rkisp1_subdev_notifier_register(rkisp1);
+ if (ret)
+ goto err_unreg_entities;
+
+ rkisp1_debug_init(rkisp1);
+
+ return 0;
+
+err_unreg_entities:
+ rkisp1_entities_unregister(rkisp1);
+err_cleanup_csi:
+ if (rkisp1->info->features & RKISP1_FEATURE_MIPI_CSI2)
+ rkisp1_csi_cleanup(rkisp1);
+err_unreg_media_dev:
+ media_device_unregister(&rkisp1->media_dev);
+err_unreg_v4l2_dev:
+ v4l2_device_unregister(&rkisp1->v4l2_dev);
+err_media_dev_cleanup:
+ media_device_cleanup(&rkisp1->media_dev);
+err_pm_runtime_disable:
+ pm_runtime_disable(&pdev->dev);
+ return ret;
+}
+
+static void rkisp1_remove(struct platform_device *pdev)
+{
+ struct rkisp1_device *rkisp1 = platform_get_drvdata(pdev);
+
+ v4l2_async_nf_unregister(&rkisp1->notifier);
+ v4l2_async_nf_cleanup(&rkisp1->notifier);
+
+ rkisp1_entities_unregister(rkisp1);
+ if (rkisp1->info->features & RKISP1_FEATURE_MIPI_CSI2)
+ rkisp1_csi_cleanup(rkisp1);
+ rkisp1_debug_cleanup(rkisp1);
+
+ media_device_unregister(&rkisp1->media_dev);
+ v4l2_device_unregister(&rkisp1->v4l2_dev);
+
+ media_device_cleanup(&rkisp1->media_dev);
+
+ pm_runtime_disable(&pdev->dev);
+}
+
+static struct platform_driver rkisp1_drv = {
+ .driver = {
+ .name = RKISP1_DRIVER_NAME,
+ .of_match_table = of_match_ptr(rkisp1_of_match),
+ .pm = &rkisp1_pm_ops,
+ },
+ .probe = rkisp1_probe,
+ .remove_new = rkisp1_remove,
+};
+
+module_platform_driver(rkisp1_drv);
+MODULE_DESCRIPTION("Rockchip ISP1 platform driver");
+MODULE_LICENSE("Dual MIT/GPL");
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c
new file mode 100644
index 0000000000..07fbb77ce2
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c
@@ -0,0 +1,1062 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Rockchip ISP1 Driver - ISP Subdevice
+ *
+ * Copyright (C) 2019 Collabora, Ltd.
+ *
+ * Based on Rockchip ISP1 driver by Rockchip Electronics Co., Ltd.
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ */
+
+#include <linux/iopoll.h>
+#include <linux/pm_runtime.h>
+#include <linux/videodev2.h>
+#include <linux/vmalloc.h>
+
+#include <media/v4l2-event.h>
+
+#include "rkisp1-common.h"
+
+#define RKISP1_DEF_SINK_PAD_FMT MEDIA_BUS_FMT_SRGGB10_1X10
+#define RKISP1_DEF_SRC_PAD_FMT MEDIA_BUS_FMT_YUYV8_2X8
+
+#define RKISP1_ISP_DEV_NAME RKISP1_DRIVER_NAME "_isp"
+
+/*
+ * NOTE: MIPI controller and input MUX are also configured in this file.
+ * This is because ISP Subdev describes not only ISP submodule (input size,
+ * format, output size, format), but also a virtual route device.
+ */
+
+/*
+ * There are many variables named with format/frame in below code,
+ * please see here for their meaning.
+ * Cropping in the sink pad defines the image region from the sensor.
+ * Cropping in the source pad defines the region for the Image Stabilizer (IS)
+ *
+ * Cropping regions of ISP
+ *
+ * +---------------------------------------------------------+
+ * | Sensor image |
+ * | +---------------------------------------------------+ |
+ * | | CIF_ISP_ACQ (for black level) | |
+ * | | sink pad format | |
+ * | | +--------------------------------------------+ | |
+ * | | | CIF_ISP_OUT | | |
+ * | | | sink pad crop | | |
+ * | | | +---------------------------------+ | | |
+ * | | | | CIF_ISP_IS | | | |
+ * | | | | source pad crop and format | | | |
+ * | | | +---------------------------------+ | | |
+ * | | +--------------------------------------------+ | |
+ * | +---------------------------------------------------+ |
+ * +---------------------------------------------------------+
+ */
+
+/* ----------------------------------------------------------------------------
+ * Helpers
+ */
+
+static struct v4l2_mbus_framefmt *
+rkisp1_isp_get_pad_fmt(struct rkisp1_isp *isp,
+ struct v4l2_subdev_state *sd_state,
+ unsigned int pad, u32 which)
+{
+ struct v4l2_subdev_state state = {
+ .pads = isp->pad_cfg
+ };
+
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_format(&isp->sd, sd_state, pad);
+ else
+ return v4l2_subdev_get_try_format(&isp->sd, &state, pad);
+}
+
+static struct v4l2_rect *
+rkisp1_isp_get_pad_crop(struct rkisp1_isp *isp,
+ struct v4l2_subdev_state *sd_state,
+ unsigned int pad, u32 which)
+{
+ struct v4l2_subdev_state state = {
+ .pads = isp->pad_cfg
+ };
+
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_crop(&isp->sd, sd_state, pad);
+ else
+ return v4l2_subdev_get_try_crop(&isp->sd, &state, pad);
+}
+
+/* ----------------------------------------------------------------------------
+ * Camera Interface registers configurations
+ */
+
+/*
+ * Image Stabilization.
+ * This should only be called when configuring CIF
+ * or at the frame end interrupt
+ */
+static void rkisp1_config_ism(struct rkisp1_isp *isp)
+{
+ const struct v4l2_rect *src_crop =
+ rkisp1_isp_get_pad_crop(isp, NULL,
+ RKISP1_ISP_PAD_SOURCE_VIDEO,
+ V4L2_SUBDEV_FORMAT_ACTIVE);
+ struct rkisp1_device *rkisp1 = isp->rkisp1;
+ u32 val;
+
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_RECENTER, 0);
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_MAX_DX, 0);
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_MAX_DY, 0);
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_DISPLACE, 0);
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_H_OFFS, src_crop->left);
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_V_OFFS, src_crop->top);
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_H_SIZE, src_crop->width);
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_V_SIZE, src_crop->height);
+
+ /* IS(Image Stabilization) is always on, working as output crop */
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_CTRL, 1);
+ val = rkisp1_read(rkisp1, RKISP1_CIF_ISP_CTRL);
+ val |= RKISP1_CIF_ISP_CTRL_ISP_CFG_UPD;
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL, val);
+}
+
+/*
+ * configure ISP blocks with input format, size......
+ */
+static int rkisp1_config_isp(struct rkisp1_isp *isp,
+ enum v4l2_mbus_type mbus_type, u32 mbus_flags)
+{
+ struct rkisp1_device *rkisp1 = isp->rkisp1;
+ u32 isp_ctrl = 0, irq_mask = 0, acq_mult = 0, acq_prop = 0;
+ const struct rkisp1_mbus_info *sink_fmt = isp->sink_fmt;
+ const struct rkisp1_mbus_info *src_fmt = isp->src_fmt;
+ const struct v4l2_mbus_framefmt *sink_frm;
+ const struct v4l2_rect *sink_crop;
+
+ sink_frm = rkisp1_isp_get_pad_fmt(isp, NULL,
+ RKISP1_ISP_PAD_SINK_VIDEO,
+ V4L2_SUBDEV_FORMAT_ACTIVE);
+ sink_crop = rkisp1_isp_get_pad_crop(isp, NULL,
+ RKISP1_ISP_PAD_SINK_VIDEO,
+ V4L2_SUBDEV_FORMAT_ACTIVE);
+
+ if (sink_fmt->pixel_enc == V4L2_PIXEL_ENC_BAYER) {
+ acq_mult = 1;
+ if (src_fmt->pixel_enc == V4L2_PIXEL_ENC_BAYER) {
+ if (mbus_type == V4L2_MBUS_BT656)
+ isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_RAW_PICT_ITU656;
+ else
+ isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_RAW_PICT;
+ } else {
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_DEMOSAIC,
+ RKISP1_CIF_ISP_DEMOSAIC_TH(0xc));
+
+ if (mbus_type == V4L2_MBUS_BT656)
+ isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_BAYER_ITU656;
+ else
+ isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_BAYER_ITU601;
+ }
+ } else if (sink_fmt->pixel_enc == V4L2_PIXEL_ENC_YUV) {
+ acq_mult = 2;
+ if (mbus_type == V4L2_MBUS_CSI2_DPHY) {
+ isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_ITU601;
+ } else {
+ if (mbus_type == V4L2_MBUS_BT656)
+ isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_ITU656;
+ else
+ isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_ITU601;
+ }
+
+ irq_mask |= RKISP1_CIF_ISP_DATA_LOSS;
+ }
+
+ /* Set up input acquisition properties */
+ if (mbus_type == V4L2_MBUS_BT656 || mbus_type == V4L2_MBUS_PARALLEL) {
+ if (mbus_flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
+ acq_prop |= RKISP1_CIF_ISP_ACQ_PROP_POS_EDGE;
+
+ switch (sink_fmt->bus_width) {
+ case 8:
+ acq_prop |= RKISP1_CIF_ISP_ACQ_PROP_IN_SEL_8B_ZERO;
+ break;
+ case 10:
+ acq_prop |= RKISP1_CIF_ISP_ACQ_PROP_IN_SEL_10B_ZERO;
+ break;
+ case 12:
+ acq_prop |= RKISP1_CIF_ISP_ACQ_PROP_IN_SEL_12B;
+ break;
+ default:
+ dev_err(rkisp1->dev, "Invalid bus width %u\n",
+ sink_fmt->bus_width);
+ return -EINVAL;
+ }
+ }
+
+ if (mbus_type == V4L2_MBUS_PARALLEL) {
+ if (mbus_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
+ acq_prop |= RKISP1_CIF_ISP_ACQ_PROP_VSYNC_LOW;
+
+ if (mbus_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ acq_prop |= RKISP1_CIF_ISP_ACQ_PROP_HSYNC_LOW;
+ }
+
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL, isp_ctrl);
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_ACQ_PROP,
+ acq_prop | sink_fmt->yuv_seq |
+ RKISP1_CIF_ISP_ACQ_PROP_BAYER_PAT(sink_fmt->bayer_pat) |
+ RKISP1_CIF_ISP_ACQ_PROP_FIELD_SEL_ALL);
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_ACQ_NR_FRAMES, 0);
+
+ /* Acquisition Size */
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_ACQ_H_OFFS, 0);
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_ACQ_V_OFFS, 0);
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_ACQ_H_SIZE,
+ acq_mult * sink_frm->width);
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_ACQ_V_SIZE, sink_frm->height);
+
+ /* ISP Out Area */
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_OUT_H_OFFS, sink_crop->left);
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_OUT_V_OFFS, sink_crop->top);
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_OUT_H_SIZE, sink_crop->width);
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_OUT_V_SIZE, sink_crop->height);
+
+ irq_mask |= RKISP1_CIF_ISP_FRAME | RKISP1_CIF_ISP_V_START |
+ RKISP1_CIF_ISP_PIC_SIZE_ERROR;
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_IMSC, irq_mask);
+
+ if (src_fmt->pixel_enc == V4L2_PIXEL_ENC_BAYER) {
+ rkisp1_params_disable(&rkisp1->params);
+ } else {
+ struct v4l2_mbus_framefmt *src_frm;
+
+ src_frm = rkisp1_isp_get_pad_fmt(isp, NULL,
+ RKISP1_ISP_PAD_SOURCE_VIDEO,
+ V4L2_SUBDEV_FORMAT_ACTIVE);
+ rkisp1_params_pre_configure(&rkisp1->params, sink_fmt->bayer_pat,
+ src_frm->quantization,
+ src_frm->ycbcr_enc);
+ }
+
+ return 0;
+}
+
+/* Configure MUX */
+static void rkisp1_config_path(struct rkisp1_isp *isp,
+ enum v4l2_mbus_type mbus_type)
+{
+ struct rkisp1_device *rkisp1 = isp->rkisp1;
+ u32 dpcl = rkisp1_read(rkisp1, RKISP1_CIF_VI_DPCL);
+
+ if (mbus_type == V4L2_MBUS_BT656 || mbus_type == V4L2_MBUS_PARALLEL)
+ dpcl |= RKISP1_CIF_VI_DPCL_IF_SEL_PARALLEL;
+ else if (mbus_type == V4L2_MBUS_CSI2_DPHY)
+ dpcl |= RKISP1_CIF_VI_DPCL_IF_SEL_MIPI;
+
+ rkisp1_write(rkisp1, RKISP1_CIF_VI_DPCL, dpcl);
+}
+
+/* Hardware configure Entry */
+static int rkisp1_config_cif(struct rkisp1_isp *isp,
+ enum v4l2_mbus_type mbus_type, u32 mbus_flags)
+{
+ int ret;
+
+ ret = rkisp1_config_isp(isp, mbus_type, mbus_flags);
+ if (ret)
+ return ret;
+
+ rkisp1_config_path(isp, mbus_type);
+ rkisp1_config_ism(isp);
+
+ return 0;
+}
+
+static void rkisp1_isp_stop(struct rkisp1_isp *isp)
+{
+ struct rkisp1_device *rkisp1 = isp->rkisp1;
+ u32 val;
+
+ /*
+ * ISP(mi) stop in mi frame end -> Stop ISP(mipi) ->
+ * Stop ISP(isp) ->wait for ISP isp off
+ */
+ /* stop and clear MI and ISP interrupts */
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_IMSC, 0);
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_ICR, ~0);
+
+ rkisp1_write(rkisp1, RKISP1_CIF_MI_IMSC, 0);
+ rkisp1_write(rkisp1, RKISP1_CIF_MI_ICR, ~0);
+
+ /* stop ISP */
+ val = rkisp1_read(rkisp1, RKISP1_CIF_ISP_CTRL);
+ val &= ~(RKISP1_CIF_ISP_CTRL_ISP_INFORM_ENABLE |
+ RKISP1_CIF_ISP_CTRL_ISP_ENABLE);
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL, val);
+
+ val = rkisp1_read(rkisp1, RKISP1_CIF_ISP_CTRL);
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL,
+ val | RKISP1_CIF_ISP_CTRL_ISP_CFG_UPD);
+
+ readx_poll_timeout(readl, rkisp1->base_addr + RKISP1_CIF_ISP_RIS,
+ val, val & RKISP1_CIF_ISP_OFF, 20, 100);
+ rkisp1_write(rkisp1, RKISP1_CIF_VI_IRCL,
+ RKISP1_CIF_VI_IRCL_MIPI_SW_RST |
+ RKISP1_CIF_VI_IRCL_ISP_SW_RST);
+ rkisp1_write(rkisp1, RKISP1_CIF_VI_IRCL, 0x0);
+}
+
+static void rkisp1_config_clk(struct rkisp1_isp *isp)
+{
+ struct rkisp1_device *rkisp1 = isp->rkisp1;
+
+ u32 val = RKISP1_CIF_VI_ICCL_ISP_CLK | RKISP1_CIF_VI_ICCL_CP_CLK |
+ RKISP1_CIF_VI_ICCL_MRSZ_CLK | RKISP1_CIF_VI_ICCL_SRSZ_CLK |
+ RKISP1_CIF_VI_ICCL_JPEG_CLK | RKISP1_CIF_VI_ICCL_MI_CLK |
+ RKISP1_CIF_VI_ICCL_IE_CLK | RKISP1_CIF_VI_ICCL_MIPI_CLK |
+ RKISP1_CIF_VI_ICCL_DCROP_CLK;
+
+ rkisp1_write(rkisp1, RKISP1_CIF_VI_ICCL, val);
+
+ /* ensure sp and mp can run at the same time in V12 */
+ if (rkisp1->info->isp_ver == RKISP1_V12) {
+ val = RKISP1_CIF_CLK_CTRL_MI_Y12 | RKISP1_CIF_CLK_CTRL_MI_SP |
+ RKISP1_CIF_CLK_CTRL_MI_RAW0 | RKISP1_CIF_CLK_CTRL_MI_RAW1 |
+ RKISP1_CIF_CLK_CTRL_MI_READ | RKISP1_CIF_CLK_CTRL_MI_RAWRD |
+ RKISP1_CIF_CLK_CTRL_CP | RKISP1_CIF_CLK_CTRL_IE;
+ rkisp1_write(rkisp1, RKISP1_CIF_VI_ISP_CLK_CTRL_V12, val);
+ }
+}
+
+static void rkisp1_isp_start(struct rkisp1_isp *isp)
+{
+ struct rkisp1_device *rkisp1 = isp->rkisp1;
+ u32 val;
+
+ rkisp1_config_clk(isp);
+
+ /* Activate ISP */
+ val = rkisp1_read(rkisp1, RKISP1_CIF_ISP_CTRL);
+ val |= RKISP1_CIF_ISP_CTRL_ISP_CFG_UPD |
+ RKISP1_CIF_ISP_CTRL_ISP_ENABLE |
+ RKISP1_CIF_ISP_CTRL_ISP_INFORM_ENABLE;
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL, val);
+
+ if (isp->src_fmt->pixel_enc != V4L2_PIXEL_ENC_BAYER)
+ rkisp1_params_post_configure(&rkisp1->params);
+}
+
+/* ----------------------------------------------------------------------------
+ * Subdev pad operations
+ */
+
+static inline struct rkisp1_isp *to_rkisp1_isp(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct rkisp1_isp, sd);
+}
+
+static int rkisp1_isp_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ unsigned int i, dir;
+ int pos = 0;
+
+ if (code->pad == RKISP1_ISP_PAD_SINK_VIDEO) {
+ dir = RKISP1_ISP_SD_SINK;
+ } else if (code->pad == RKISP1_ISP_PAD_SOURCE_VIDEO) {
+ dir = RKISP1_ISP_SD_SRC;
+ } else {
+ if (code->index > 0)
+ return -EINVAL;
+ code->code = MEDIA_BUS_FMT_METADATA_FIXED;
+ return 0;
+ }
+
+ for (i = 0; ; i++) {
+ const struct rkisp1_mbus_info *fmt =
+ rkisp1_mbus_info_get_by_index(i);
+
+ if (!fmt)
+ return -EINVAL;
+
+ if (fmt->direction & dir)
+ pos++;
+
+ if (code->index == pos - 1) {
+ code->code = fmt->mbus_code;
+ if (fmt->pixel_enc == V4L2_PIXEL_ENC_YUV &&
+ dir == RKISP1_ISP_SD_SRC)
+ code->flags =
+ V4L2_SUBDEV_MBUS_CODE_CSC_QUANTIZATION;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int rkisp1_isp_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ const struct rkisp1_mbus_info *mbus_info;
+
+ if (fse->pad == RKISP1_ISP_PAD_SINK_PARAMS ||
+ fse->pad == RKISP1_ISP_PAD_SOURCE_STATS)
+ return -ENOTTY;
+
+ if (fse->index > 0)
+ return -EINVAL;
+
+ mbus_info = rkisp1_mbus_info_get_by_code(fse->code);
+ if (!mbus_info)
+ return -EINVAL;
+
+ if (!(mbus_info->direction & RKISP1_ISP_SD_SINK) &&
+ fse->pad == RKISP1_ISP_PAD_SINK_VIDEO)
+ return -EINVAL;
+
+ if (!(mbus_info->direction & RKISP1_ISP_SD_SRC) &&
+ fse->pad == RKISP1_ISP_PAD_SOURCE_VIDEO)
+ return -EINVAL;
+
+ fse->min_width = RKISP1_ISP_MIN_WIDTH;
+ fse->max_width = RKISP1_ISP_MAX_WIDTH;
+ fse->min_height = RKISP1_ISP_MIN_HEIGHT;
+ fse->max_height = RKISP1_ISP_MAX_HEIGHT;
+
+ return 0;
+}
+
+static int rkisp1_isp_init_config(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state)
+{
+ struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
+ struct v4l2_rect *sink_crop, *src_crop;
+
+ /* Video. */
+ sink_fmt = v4l2_subdev_get_try_format(sd, sd_state,
+ RKISP1_ISP_PAD_SINK_VIDEO);
+ sink_fmt->width = RKISP1_DEFAULT_WIDTH;
+ sink_fmt->height = RKISP1_DEFAULT_HEIGHT;
+ sink_fmt->field = V4L2_FIELD_NONE;
+ sink_fmt->code = RKISP1_DEF_SINK_PAD_FMT;
+ sink_fmt->colorspace = V4L2_COLORSPACE_RAW;
+ sink_fmt->xfer_func = V4L2_XFER_FUNC_NONE;
+ sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ sink_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+
+ sink_crop = v4l2_subdev_get_try_crop(sd, sd_state,
+ RKISP1_ISP_PAD_SINK_VIDEO);
+ sink_crop->width = RKISP1_DEFAULT_WIDTH;
+ sink_crop->height = RKISP1_DEFAULT_HEIGHT;
+ sink_crop->left = 0;
+ sink_crop->top = 0;
+
+ src_fmt = v4l2_subdev_get_try_format(sd, sd_state,
+ RKISP1_ISP_PAD_SOURCE_VIDEO);
+ *src_fmt = *sink_fmt;
+ src_fmt->code = RKISP1_DEF_SRC_PAD_FMT;
+ src_fmt->colorspace = V4L2_COLORSPACE_SRGB;
+ src_fmt->xfer_func = V4L2_XFER_FUNC_SRGB;
+ src_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ src_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE;
+
+ src_crop = v4l2_subdev_get_try_crop(sd, sd_state,
+ RKISP1_ISP_PAD_SOURCE_VIDEO);
+ *src_crop = *sink_crop;
+
+ /* Parameters and statistics. */
+ sink_fmt = v4l2_subdev_get_try_format(sd, sd_state,
+ RKISP1_ISP_PAD_SINK_PARAMS);
+ src_fmt = v4l2_subdev_get_try_format(sd, sd_state,
+ RKISP1_ISP_PAD_SOURCE_STATS);
+ sink_fmt->width = 0;
+ sink_fmt->height = 0;
+ sink_fmt->field = V4L2_FIELD_NONE;
+ sink_fmt->code = MEDIA_BUS_FMT_METADATA_FIXED;
+ *src_fmt = *sink_fmt;
+
+ return 0;
+}
+
+static void rkisp1_isp_set_src_fmt(struct rkisp1_isp *isp,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_mbus_framefmt *format,
+ unsigned int which)
+{
+ const struct rkisp1_mbus_info *sink_info;
+ const struct rkisp1_mbus_info *src_info;
+ struct v4l2_mbus_framefmt *sink_fmt;
+ struct v4l2_mbus_framefmt *src_fmt;
+ const struct v4l2_rect *src_crop;
+ bool set_csc;
+
+ sink_fmt = rkisp1_isp_get_pad_fmt(isp, sd_state,
+ RKISP1_ISP_PAD_SINK_VIDEO, which);
+ src_fmt = rkisp1_isp_get_pad_fmt(isp, sd_state,
+ RKISP1_ISP_PAD_SOURCE_VIDEO, which);
+ src_crop = rkisp1_isp_get_pad_crop(isp, sd_state,
+ RKISP1_ISP_PAD_SOURCE_VIDEO, which);
+
+ /*
+ * Media bus code. The ISP can operate in pass-through mode (Bayer in,
+ * Bayer out or YUV in, YUV out) or process Bayer data to YUV, but
+ * can't convert from YUV to Bayer.
+ */
+ sink_info = rkisp1_mbus_info_get_by_code(sink_fmt->code);
+
+ src_fmt->code = format->code;
+ src_info = rkisp1_mbus_info_get_by_code(src_fmt->code);
+ if (!src_info || !(src_info->direction & RKISP1_ISP_SD_SRC)) {
+ src_fmt->code = RKISP1_DEF_SRC_PAD_FMT;
+ src_info = rkisp1_mbus_info_get_by_code(src_fmt->code);
+ }
+
+ if (sink_info->pixel_enc == V4L2_PIXEL_ENC_YUV &&
+ src_info->pixel_enc == V4L2_PIXEL_ENC_BAYER) {
+ src_fmt->code = sink_fmt->code;
+ src_info = sink_info;
+ }
+
+ /*
+ * The source width and height must be identical to the source crop
+ * size.
+ */
+ src_fmt->width = src_crop->width;
+ src_fmt->height = src_crop->height;
+
+ /*
+ * Copy the color space for the sink pad. When converting from Bayer to
+ * YUV, default to a limited quantization range.
+ */
+ src_fmt->colorspace = sink_fmt->colorspace;
+ src_fmt->xfer_func = sink_fmt->xfer_func;
+ src_fmt->ycbcr_enc = sink_fmt->ycbcr_enc;
+
+ if (sink_info->pixel_enc == V4L2_PIXEL_ENC_BAYER &&
+ src_info->pixel_enc == V4L2_PIXEL_ENC_YUV)
+ src_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE;
+ else
+ src_fmt->quantization = sink_fmt->quantization;
+
+ /*
+ * Allow setting the source color space fields when the SET_CSC flag is
+ * set and the source format is YUV. If the sink format is YUV, don't
+ * set the color primaries, transfer function or YCbCr encoding as the
+ * ISP is bypassed in that case and passes YUV data through without
+ * modifications.
+ *
+ * The color primaries and transfer function are configured through the
+ * cross-talk matrix and tone curve respectively. Settings for those
+ * hardware blocks are conveyed through the ISP parameters buffer, as
+ * they need to combine color space information with other image tuning
+ * characteristics and can't thus be computed by the kernel based on the
+ * color space. The source pad colorspace and xfer_func fields are thus
+ * ignored by the driver, but can be set by userspace to propagate
+ * accurate color space information down the pipeline.
+ */
+ set_csc = format->flags & V4L2_MBUS_FRAMEFMT_SET_CSC;
+
+ if (set_csc && src_info->pixel_enc == V4L2_PIXEL_ENC_YUV) {
+ if (sink_info->pixel_enc == V4L2_PIXEL_ENC_BAYER) {
+ if (format->colorspace != V4L2_COLORSPACE_DEFAULT)
+ src_fmt->colorspace = format->colorspace;
+ if (format->xfer_func != V4L2_XFER_FUNC_DEFAULT)
+ src_fmt->xfer_func = format->xfer_func;
+ if (format->ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT)
+ src_fmt->ycbcr_enc = format->ycbcr_enc;
+ }
+
+ if (format->quantization != V4L2_QUANTIZATION_DEFAULT)
+ src_fmt->quantization = format->quantization;
+ }
+
+ *format = *src_fmt;
+
+ /*
+ * Restore the SET_CSC flag if it was set to indicate support for the
+ * CSC setting API.
+ */
+ if (set_csc)
+ format->flags |= V4L2_MBUS_FRAMEFMT_SET_CSC;
+
+ /* Store the source format info when setting the active format. */
+ if (which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ isp->src_fmt = src_info;
+}
+
+static void rkisp1_isp_set_src_crop(struct rkisp1_isp *isp,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_rect *r, unsigned int which)
+{
+ struct v4l2_mbus_framefmt *src_fmt;
+ const struct v4l2_rect *sink_crop;
+ struct v4l2_rect *src_crop;
+
+ src_crop = rkisp1_isp_get_pad_crop(isp, sd_state,
+ RKISP1_ISP_PAD_SOURCE_VIDEO,
+ which);
+ sink_crop = rkisp1_isp_get_pad_crop(isp, sd_state,
+ RKISP1_ISP_PAD_SINK_VIDEO,
+ which);
+
+ src_crop->left = ALIGN(r->left, 2);
+ src_crop->width = ALIGN(r->width, 2);
+ src_crop->top = r->top;
+ src_crop->height = r->height;
+ rkisp1_sd_adjust_crop_rect(src_crop, sink_crop);
+
+ *r = *src_crop;
+
+ /* Propagate to out format */
+ src_fmt = rkisp1_isp_get_pad_fmt(isp, sd_state,
+ RKISP1_ISP_PAD_SOURCE_VIDEO, which);
+ rkisp1_isp_set_src_fmt(isp, sd_state, src_fmt, which);
+}
+
+static void rkisp1_isp_set_sink_crop(struct rkisp1_isp *isp,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_rect *r, unsigned int which)
+{
+ struct v4l2_rect *sink_crop, *src_crop;
+ const struct v4l2_mbus_framefmt *sink_fmt;
+
+ sink_crop = rkisp1_isp_get_pad_crop(isp, sd_state,
+ RKISP1_ISP_PAD_SINK_VIDEO,
+ which);
+ sink_fmt = rkisp1_isp_get_pad_fmt(isp, sd_state,
+ RKISP1_ISP_PAD_SINK_VIDEO,
+ which);
+
+ sink_crop->left = ALIGN(r->left, 2);
+ sink_crop->width = ALIGN(r->width, 2);
+ sink_crop->top = r->top;
+ sink_crop->height = r->height;
+ rkisp1_sd_adjust_crop(sink_crop, sink_fmt);
+
+ *r = *sink_crop;
+
+ /* Propagate to out crop */
+ src_crop = rkisp1_isp_get_pad_crop(isp, sd_state,
+ RKISP1_ISP_PAD_SOURCE_VIDEO, which);
+ rkisp1_isp_set_src_crop(isp, sd_state, src_crop, which);
+}
+
+static void rkisp1_isp_set_sink_fmt(struct rkisp1_isp *isp,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_mbus_framefmt *format,
+ unsigned int which)
+{
+ const struct rkisp1_mbus_info *mbus_info;
+ struct v4l2_mbus_framefmt *sink_fmt;
+ struct v4l2_rect *sink_crop;
+ bool is_yuv;
+
+ sink_fmt = rkisp1_isp_get_pad_fmt(isp, sd_state,
+ RKISP1_ISP_PAD_SINK_VIDEO,
+ which);
+ sink_fmt->code = format->code;
+ mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code);
+ if (!mbus_info || !(mbus_info->direction & RKISP1_ISP_SD_SINK)) {
+ sink_fmt->code = RKISP1_DEF_SINK_PAD_FMT;
+ mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code);
+ }
+ if (which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ isp->sink_fmt = mbus_info;
+
+ sink_fmt->width = clamp_t(u32, format->width,
+ RKISP1_ISP_MIN_WIDTH,
+ RKISP1_ISP_MAX_WIDTH);
+ sink_fmt->height = clamp_t(u32, format->height,
+ RKISP1_ISP_MIN_HEIGHT,
+ RKISP1_ISP_MAX_HEIGHT);
+
+ /*
+ * Adjust the color space fields. Accept any color primaries and
+ * transfer function for both YUV and Bayer. For YUV any YCbCr encoding
+ * and quantization range is also accepted. For Bayer formats, the YCbCr
+ * encoding isn't applicable, and the quantization range can only be
+ * full.
+ */
+ is_yuv = mbus_info->pixel_enc == V4L2_PIXEL_ENC_YUV;
+
+ sink_fmt->colorspace = format->colorspace ? :
+ (is_yuv ? V4L2_COLORSPACE_SRGB :
+ V4L2_COLORSPACE_RAW);
+ sink_fmt->xfer_func = format->xfer_func ? :
+ V4L2_MAP_XFER_FUNC_DEFAULT(sink_fmt->colorspace);
+ if (is_yuv) {
+ sink_fmt->ycbcr_enc = format->ycbcr_enc ? :
+ V4L2_MAP_YCBCR_ENC_DEFAULT(sink_fmt->colorspace);
+ sink_fmt->quantization = format->quantization ? :
+ V4L2_MAP_QUANTIZATION_DEFAULT(false, sink_fmt->colorspace,
+ sink_fmt->ycbcr_enc);
+ } else {
+ /*
+ * The YCbCr encoding isn't applicable for non-YUV formats, but
+ * V4L2 has no "no encoding" value. Hardcode it to Rec. 601, it
+ * should be ignored by userspace.
+ */
+ sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ sink_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ }
+
+ *format = *sink_fmt;
+
+ /* Propagate to in crop */
+ sink_crop = rkisp1_isp_get_pad_crop(isp, sd_state,
+ RKISP1_ISP_PAD_SINK_VIDEO,
+ which);
+ rkisp1_isp_set_sink_crop(isp, sd_state, sink_crop, which);
+}
+
+static int rkisp1_isp_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct rkisp1_isp *isp = to_rkisp1_isp(sd);
+
+ mutex_lock(&isp->ops_lock);
+ fmt->format = *rkisp1_isp_get_pad_fmt(isp, sd_state, fmt->pad,
+ fmt->which);
+ mutex_unlock(&isp->ops_lock);
+ return 0;
+}
+
+static int rkisp1_isp_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct rkisp1_isp *isp = to_rkisp1_isp(sd);
+
+ mutex_lock(&isp->ops_lock);
+ if (fmt->pad == RKISP1_ISP_PAD_SINK_VIDEO)
+ rkisp1_isp_set_sink_fmt(isp, sd_state, &fmt->format,
+ fmt->which);
+ else if (fmt->pad == RKISP1_ISP_PAD_SOURCE_VIDEO)
+ rkisp1_isp_set_src_fmt(isp, sd_state, &fmt->format,
+ fmt->which);
+ else
+ fmt->format = *rkisp1_isp_get_pad_fmt(isp, sd_state, fmt->pad,
+ fmt->which);
+
+ mutex_unlock(&isp->ops_lock);
+ return 0;
+}
+
+static int rkisp1_isp_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_selection *sel)
+{
+ struct rkisp1_isp *isp = to_rkisp1_isp(sd);
+ int ret = 0;
+
+ if (sel->pad != RKISP1_ISP_PAD_SOURCE_VIDEO &&
+ sel->pad != RKISP1_ISP_PAD_SINK_VIDEO)
+ return -EINVAL;
+
+ mutex_lock(&isp->ops_lock);
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ if (sel->pad == RKISP1_ISP_PAD_SINK_VIDEO) {
+ struct v4l2_mbus_framefmt *fmt;
+
+ fmt = rkisp1_isp_get_pad_fmt(isp, sd_state, sel->pad,
+ sel->which);
+ sel->r.height = fmt->height;
+ sel->r.width = fmt->width;
+ sel->r.left = 0;
+ sel->r.top = 0;
+ } else {
+ sel->r = *rkisp1_isp_get_pad_crop(isp, sd_state,
+ RKISP1_ISP_PAD_SINK_VIDEO,
+ sel->which);
+ }
+ break;
+ case V4L2_SEL_TGT_CROP:
+ sel->r = *rkisp1_isp_get_pad_crop(isp, sd_state, sel->pad,
+ sel->which);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ mutex_unlock(&isp->ops_lock);
+ return ret;
+}
+
+static int rkisp1_isp_set_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_selection *sel)
+{
+ struct rkisp1_isp *isp = to_rkisp1_isp(sd);
+ int ret = 0;
+
+ if (sel->target != V4L2_SEL_TGT_CROP)
+ return -EINVAL;
+
+ dev_dbg(isp->rkisp1->dev, "%s: pad: %d sel(%d,%d)/%dx%d\n", __func__,
+ sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height);
+ mutex_lock(&isp->ops_lock);
+ if (sel->pad == RKISP1_ISP_PAD_SINK_VIDEO)
+ rkisp1_isp_set_sink_crop(isp, sd_state, &sel->r, sel->which);
+ else if (sel->pad == RKISP1_ISP_PAD_SOURCE_VIDEO)
+ rkisp1_isp_set_src_crop(isp, sd_state, &sel->r, sel->which);
+ else
+ ret = -EINVAL;
+
+ mutex_unlock(&isp->ops_lock);
+ return ret;
+}
+
+static int rkisp1_subdev_link_validate(struct media_link *link)
+{
+ if (link->sink->index == RKISP1_ISP_PAD_SINK_PARAMS)
+ return 0;
+
+ return v4l2_subdev_link_validate(link);
+}
+
+static const struct v4l2_subdev_pad_ops rkisp1_isp_pad_ops = {
+ .enum_mbus_code = rkisp1_isp_enum_mbus_code,
+ .enum_frame_size = rkisp1_isp_enum_frame_size,
+ .get_selection = rkisp1_isp_get_selection,
+ .set_selection = rkisp1_isp_set_selection,
+ .init_cfg = rkisp1_isp_init_config,
+ .get_fmt = rkisp1_isp_get_fmt,
+ .set_fmt = rkisp1_isp_set_fmt,
+ .link_validate = v4l2_subdev_link_validate_default,
+};
+
+/* ----------------------------------------------------------------------------
+ * Stream operations
+ */
+
+static int rkisp1_isp_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct rkisp1_isp *isp = to_rkisp1_isp(sd);
+ struct rkisp1_device *rkisp1 = isp->rkisp1;
+ struct media_pad *source_pad;
+ struct media_pad *sink_pad;
+ enum v4l2_mbus_type mbus_type;
+ u32 mbus_flags;
+ int ret;
+
+ if (!enable) {
+ v4l2_subdev_call(rkisp1->source, video, s_stream, false);
+ rkisp1_isp_stop(isp);
+ return 0;
+ }
+
+ sink_pad = &isp->pads[RKISP1_ISP_PAD_SINK_VIDEO];
+ source_pad = media_pad_remote_pad_unique(sink_pad);
+ if (IS_ERR(source_pad)) {
+ dev_dbg(rkisp1->dev, "Failed to get source for ISP: %ld\n",
+ PTR_ERR(source_pad));
+ return -EPIPE;
+ }
+
+ rkisp1->source = media_entity_to_v4l2_subdev(source_pad->entity);
+ if (!rkisp1->source) {
+ /* This should really not happen, so is not worth a message. */
+ return -EPIPE;
+ }
+
+ if (rkisp1->source == &rkisp1->csi.sd) {
+ mbus_type = V4L2_MBUS_CSI2_DPHY;
+ mbus_flags = 0;
+ } else {
+ const struct rkisp1_sensor_async *asd;
+ struct v4l2_async_connection *asc;
+
+ asc = v4l2_async_connection_unique(rkisp1->source);
+ if (!asc)
+ return -EPIPE;
+
+ asd = container_of(asc, struct rkisp1_sensor_async, asd);
+
+ mbus_type = asd->mbus_type;
+ mbus_flags = asd->mbus_flags;
+ }
+
+ isp->frame_sequence = -1;
+ mutex_lock(&isp->ops_lock);
+ ret = rkisp1_config_cif(isp, mbus_type, mbus_flags);
+ if (ret)
+ goto mutex_unlock;
+
+ rkisp1_isp_start(isp);
+
+ ret = v4l2_subdev_call(rkisp1->source, video, s_stream, true);
+ if (ret) {
+ rkisp1_isp_stop(isp);
+ goto mutex_unlock;
+ }
+
+mutex_unlock:
+ mutex_unlock(&isp->ops_lock);
+ return ret;
+}
+
+static int rkisp1_isp_subs_evt(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ if (sub->type != V4L2_EVENT_FRAME_SYNC)
+ return -EINVAL;
+
+ /* V4L2_EVENT_FRAME_SYNC doesn't require an id, so zero should be set */
+ if (sub->id != 0)
+ return -EINVAL;
+
+ return v4l2_event_subscribe(fh, sub, 0, NULL);
+}
+
+static const struct media_entity_operations rkisp1_isp_media_ops = {
+ .link_validate = rkisp1_subdev_link_validate,
+};
+
+static const struct v4l2_subdev_video_ops rkisp1_isp_video_ops = {
+ .s_stream = rkisp1_isp_s_stream,
+};
+
+static const struct v4l2_subdev_core_ops rkisp1_isp_core_ops = {
+ .subscribe_event = rkisp1_isp_subs_evt,
+ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
+};
+
+static const struct v4l2_subdev_ops rkisp1_isp_ops = {
+ .core = &rkisp1_isp_core_ops,
+ .video = &rkisp1_isp_video_ops,
+ .pad = &rkisp1_isp_pad_ops,
+};
+
+int rkisp1_isp_register(struct rkisp1_device *rkisp1)
+{
+ struct v4l2_subdev_state state = {
+ .pads = rkisp1->isp.pad_cfg
+ };
+ struct rkisp1_isp *isp = &rkisp1->isp;
+ struct media_pad *pads = isp->pads;
+ struct v4l2_subdev *sd = &isp->sd;
+ int ret;
+
+ isp->rkisp1 = rkisp1;
+
+ v4l2_subdev_init(sd, &rkisp1_isp_ops);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
+ sd->entity.ops = &rkisp1_isp_media_ops;
+ sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
+ sd->owner = THIS_MODULE;
+ strscpy(sd->name, RKISP1_ISP_DEV_NAME, sizeof(sd->name));
+
+ pads[RKISP1_ISP_PAD_SINK_VIDEO].flags = MEDIA_PAD_FL_SINK |
+ MEDIA_PAD_FL_MUST_CONNECT;
+ pads[RKISP1_ISP_PAD_SINK_PARAMS].flags = MEDIA_PAD_FL_SINK;
+ pads[RKISP1_ISP_PAD_SOURCE_VIDEO].flags = MEDIA_PAD_FL_SOURCE;
+ pads[RKISP1_ISP_PAD_SOURCE_STATS].flags = MEDIA_PAD_FL_SOURCE;
+
+ isp->sink_fmt = rkisp1_mbus_info_get_by_code(RKISP1_DEF_SINK_PAD_FMT);
+ isp->src_fmt = rkisp1_mbus_info_get_by_code(RKISP1_DEF_SRC_PAD_FMT);
+
+ mutex_init(&isp->ops_lock);
+ ret = media_entity_pads_init(&sd->entity, RKISP1_ISP_PAD_MAX, pads);
+ if (ret)
+ goto error;
+
+ ret = v4l2_device_register_subdev(&rkisp1->v4l2_dev, sd);
+ if (ret) {
+ dev_err(rkisp1->dev, "Failed to register isp subdev\n");
+ goto error;
+ }
+
+ rkisp1_isp_init_config(sd, &state);
+
+ return 0;
+
+error:
+ media_entity_cleanup(&sd->entity);
+ mutex_destroy(&isp->ops_lock);
+ isp->sd.v4l2_dev = NULL;
+ return ret;
+}
+
+void rkisp1_isp_unregister(struct rkisp1_device *rkisp1)
+{
+ struct rkisp1_isp *isp = &rkisp1->isp;
+
+ if (!isp->sd.v4l2_dev)
+ return;
+
+ v4l2_device_unregister_subdev(&isp->sd);
+ media_entity_cleanup(&isp->sd.entity);
+ mutex_destroy(&isp->ops_lock);
+}
+
+/* ----------------------------------------------------------------------------
+ * Interrupt handlers
+ */
+
+static void rkisp1_isp_queue_event_sof(struct rkisp1_isp *isp)
+{
+ struct v4l2_event event = {
+ .type = V4L2_EVENT_FRAME_SYNC,
+ };
+
+ event.u.frame_sync.frame_sequence = isp->frame_sequence;
+ v4l2_event_queue(isp->sd.devnode, &event);
+}
+
+irqreturn_t rkisp1_isp_isr(int irq, void *ctx)
+{
+ struct device *dev = ctx;
+ struct rkisp1_device *rkisp1 = dev_get_drvdata(dev);
+ u32 status, isp_err;
+
+ status = rkisp1_read(rkisp1, RKISP1_CIF_ISP_MIS);
+ if (!status)
+ return IRQ_NONE;
+
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_ICR, status);
+
+ /* Vertical sync signal, starting generating new frame */
+ if (status & RKISP1_CIF_ISP_V_START) {
+ rkisp1->isp.frame_sequence++;
+ rkisp1_isp_queue_event_sof(&rkisp1->isp);
+ if (status & RKISP1_CIF_ISP_FRAME) {
+ WARN_ONCE(1, "irq delay is too long, buffers might not be in sync\n");
+ rkisp1->debug.irq_delay++;
+ }
+ }
+ if (status & RKISP1_CIF_ISP_PIC_SIZE_ERROR) {
+ /* Clear pic_size_error */
+ isp_err = rkisp1_read(rkisp1, RKISP1_CIF_ISP_ERR);
+ if (isp_err & RKISP1_CIF_ISP_ERR_INFORM_SIZE)
+ rkisp1->debug.inform_size_error++;
+ if (isp_err & RKISP1_CIF_ISP_ERR_IS_SIZE)
+ rkisp1->debug.img_stabilization_size_error++;
+ if (isp_err & RKISP1_CIF_ISP_ERR_OUTFORM_SIZE)
+ rkisp1->debug.outform_size_error++;
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_ERR_CLR, isp_err);
+ } else if (status & RKISP1_CIF_ISP_DATA_LOSS) {
+ /* keep track of data_loss in debugfs */
+ rkisp1->debug.data_loss++;
+ }
+
+ if (status & RKISP1_CIF_ISP_FRAME) {
+ u32 isp_ris;
+
+ /* New frame from the sensor received */
+ isp_ris = rkisp1_read(rkisp1, RKISP1_CIF_ISP_RIS);
+ if (isp_ris & RKISP1_STATS_MEAS_MASK)
+ rkisp1_stats_isr(&rkisp1->stats, isp_ris);
+ /*
+ * Then update changed configs. Some of them involve
+ * lot of register writes. Do those only one per frame.
+ * Do the updates in the order of the processing flow.
+ */
+ rkisp1_params_isr(rkisp1);
+ }
+
+ return IRQ_HANDLED;
+}
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c
new file mode 100644
index 0000000000..3482f7d707
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c
@@ -0,0 +1,1976 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Rockchip ISP1 Driver - Params subdevice
+ *
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ */
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-vmalloc.h> /* for ISP params */
+
+#include "rkisp1-common.h"
+
+#define RKISP1_PARAMS_DEV_NAME RKISP1_DRIVER_NAME "_params"
+
+#define RKISP1_ISP_PARAMS_REQ_BUFS_MIN 2
+#define RKISP1_ISP_PARAMS_REQ_BUFS_MAX 8
+
+#define RKISP1_ISP_DPCC_METHODS_SET(n) \
+ (RKISP1_CIF_ISP_DPCC_METHODS_SET_1 + 0x4 * (n))
+#define RKISP1_ISP_DPCC_LINE_THRESH(n) \
+ (RKISP1_CIF_ISP_DPCC_LINE_THRESH_1 + 0x14 * (n))
+#define RKISP1_ISP_DPCC_LINE_MAD_FAC(n) \
+ (RKISP1_CIF_ISP_DPCC_LINE_MAD_FAC_1 + 0x14 * (n))
+#define RKISP1_ISP_DPCC_PG_FAC(n) \
+ (RKISP1_CIF_ISP_DPCC_PG_FAC_1 + 0x14 * (n))
+#define RKISP1_ISP_DPCC_RND_THRESH(n) \
+ (RKISP1_CIF_ISP_DPCC_RND_THRESH_1 + 0x14 * (n))
+#define RKISP1_ISP_DPCC_RG_FAC(n) \
+ (RKISP1_CIF_ISP_DPCC_RG_FAC_1 + 0x14 * (n))
+#define RKISP1_ISP_CC_COEFF(n) \
+ (RKISP1_CIF_ISP_CC_COEFF_0 + (n) * 4)
+
+static inline void
+rkisp1_param_set_bits(struct rkisp1_params *params, u32 reg, u32 bit_mask)
+{
+ u32 val;
+
+ val = rkisp1_read(params->rkisp1, reg);
+ rkisp1_write(params->rkisp1, reg, val | bit_mask);
+}
+
+static inline void
+rkisp1_param_clear_bits(struct rkisp1_params *params, u32 reg, u32 bit_mask)
+{
+ u32 val;
+
+ val = rkisp1_read(params->rkisp1, reg);
+ rkisp1_write(params->rkisp1, reg, val & ~bit_mask);
+}
+
+/* ISP BP interface function */
+static void rkisp1_dpcc_config(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_dpcc_config *arg)
+{
+ unsigned int i;
+ u32 mode;
+
+ /*
+ * The enable bit is controlled in rkisp1_isp_isr_other_config() and
+ * must be preserved. The grayscale mode should be configured
+ * automatically based on the media bus code on the ISP sink pad, so
+ * only the STAGE1_ENABLE bit can be set by userspace.
+ */
+ mode = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_DPCC_MODE);
+ mode &= RKISP1_CIF_ISP_DPCC_MODE_DPCC_ENABLE;
+ mode |= arg->mode & RKISP1_CIF_ISP_DPCC_MODE_STAGE1_ENABLE;
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPCC_MODE, mode);
+
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPCC_OUTPUT_MODE,
+ arg->output_mode & RKISP1_CIF_ISP_DPCC_OUTPUT_MODE_MASK);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPCC_SET_USE,
+ arg->set_use & RKISP1_CIF_ISP_DPCC_SET_USE_MASK);
+
+ for (i = 0; i < RKISP1_CIF_ISP_DPCC_METHODS_MAX; i++) {
+ rkisp1_write(params->rkisp1, RKISP1_ISP_DPCC_METHODS_SET(i),
+ arg->methods[i].method &
+ RKISP1_CIF_ISP_DPCC_METHODS_SET_MASK);
+ rkisp1_write(params->rkisp1, RKISP1_ISP_DPCC_LINE_THRESH(i),
+ arg->methods[i].line_thresh &
+ RKISP1_CIF_ISP_DPCC_LINE_THRESH_MASK);
+ rkisp1_write(params->rkisp1, RKISP1_ISP_DPCC_LINE_MAD_FAC(i),
+ arg->methods[i].line_mad_fac &
+ RKISP1_CIF_ISP_DPCC_LINE_MAD_FAC_MASK);
+ rkisp1_write(params->rkisp1, RKISP1_ISP_DPCC_PG_FAC(i),
+ arg->methods[i].pg_fac &
+ RKISP1_CIF_ISP_DPCC_PG_FAC_MASK);
+ rkisp1_write(params->rkisp1, RKISP1_ISP_DPCC_RND_THRESH(i),
+ arg->methods[i].rnd_thresh &
+ RKISP1_CIF_ISP_DPCC_RND_THRESH_MASK);
+ rkisp1_write(params->rkisp1, RKISP1_ISP_DPCC_RG_FAC(i),
+ arg->methods[i].rg_fac &
+ RKISP1_CIF_ISP_DPCC_RG_FAC_MASK);
+ }
+
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPCC_RND_OFFS,
+ arg->rnd_offs & RKISP1_CIF_ISP_DPCC_RND_OFFS_MASK);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPCC_RO_LIMITS,
+ arg->ro_limits & RKISP1_CIF_ISP_DPCC_RO_LIMIT_MASK);
+}
+
+/* ISP black level subtraction interface function */
+static void rkisp1_bls_config(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_bls_config *arg)
+{
+ /* avoid to override the old enable value */
+ u32 new_control;
+
+ new_control = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_BLS_CTRL);
+ new_control &= RKISP1_CIF_ISP_BLS_ENA;
+ /* fixed subtraction values */
+ if (!arg->enable_auto) {
+ const struct rkisp1_cif_isp_bls_fixed_val *pval =
+ &arg->fixed_val;
+
+ switch (params->raw_type) {
+ case RKISP1_RAW_BGGR:
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_D_FIXED,
+ pval->r);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_C_FIXED,
+ pval->gr);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_B_FIXED,
+ pval->gb);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_A_FIXED,
+ pval->b);
+ break;
+ case RKISP1_RAW_GBRG:
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_C_FIXED,
+ pval->r);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_D_FIXED,
+ pval->gr);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_A_FIXED,
+ pval->gb);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_B_FIXED,
+ pval->b);
+ break;
+ case RKISP1_RAW_GRBG:
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_B_FIXED,
+ pval->r);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_A_FIXED,
+ pval->gr);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_D_FIXED,
+ pval->gb);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_C_FIXED,
+ pval->b);
+ break;
+ case RKISP1_RAW_RGGB:
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_A_FIXED,
+ pval->r);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_B_FIXED,
+ pval->gr);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_C_FIXED,
+ pval->gb);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_D_FIXED,
+ pval->b);
+ break;
+ default:
+ break;
+ }
+
+ } else {
+ if (arg->en_windows & BIT(1)) {
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_H2_START,
+ arg->bls_window2.h_offs);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_H2_STOP,
+ arg->bls_window2.h_size);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_V2_START,
+ arg->bls_window2.v_offs);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_V2_STOP,
+ arg->bls_window2.v_size);
+ new_control |= RKISP1_CIF_ISP_BLS_WINDOW_2;
+ }
+
+ if (arg->en_windows & BIT(0)) {
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_H1_START,
+ arg->bls_window1.h_offs);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_H1_STOP,
+ arg->bls_window1.h_size);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_V1_START,
+ arg->bls_window1.v_offs);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_V1_STOP,
+ arg->bls_window1.v_size);
+ new_control |= RKISP1_CIF_ISP_BLS_WINDOW_1;
+ }
+
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_SAMPLES,
+ arg->bls_samples);
+
+ new_control |= RKISP1_CIF_ISP_BLS_MODE_MEASURED;
+ }
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_CTRL, new_control);
+}
+
+/* ISP LS correction interface function */
+static void
+rkisp1_lsc_matrix_config_v10(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_lsc_config *pconfig)
+{
+ struct rkisp1_device *rkisp1 = params->rkisp1;
+ u32 lsc_status, sram_addr, lsc_table_sel;
+ unsigned int i, j;
+
+ lsc_status = rkisp1_read(rkisp1, RKISP1_CIF_ISP_LSC_STATUS);
+
+ /* RKISP1_CIF_ISP_LSC_TABLE_ADDRESS_153 = ( 17 * 18 ) >> 1 */
+ sram_addr = lsc_status & RKISP1_CIF_ISP_LSC_ACTIVE_TABLE ?
+ RKISP1_CIF_ISP_LSC_TABLE_ADDRESS_0 :
+ RKISP1_CIF_ISP_LSC_TABLE_ADDRESS_153;
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_R_TABLE_ADDR, sram_addr);
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GR_TABLE_ADDR, sram_addr);
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GB_TABLE_ADDR, sram_addr);
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_B_TABLE_ADDR, sram_addr);
+
+ /* program data tables (table size is 9 * 17 = 153) */
+ for (i = 0; i < RKISP1_CIF_ISP_LSC_SAMPLES_MAX; i++) {
+ const __u16 *r_tbl = pconfig->r_data_tbl[i];
+ const __u16 *gr_tbl = pconfig->gr_data_tbl[i];
+ const __u16 *gb_tbl = pconfig->gb_data_tbl[i];
+ const __u16 *b_tbl = pconfig->b_data_tbl[i];
+
+ /*
+ * 17 sectors with 2 values in one DWORD = 9
+ * DWORDs (2nd value of last DWORD unused)
+ */
+ for (j = 0; j < RKISP1_CIF_ISP_LSC_SAMPLES_MAX - 1; j += 2) {
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_R_TABLE_DATA,
+ RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(
+ r_tbl[j], r_tbl[j + 1]));
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GR_TABLE_DATA,
+ RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(
+ gr_tbl[j], gr_tbl[j + 1]));
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GB_TABLE_DATA,
+ RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(
+ gb_tbl[j], gb_tbl[j + 1]));
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_B_TABLE_DATA,
+ RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(
+ b_tbl[j], b_tbl[j + 1]));
+ }
+
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_R_TABLE_DATA,
+ RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(r_tbl[j], 0));
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GR_TABLE_DATA,
+ RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(gr_tbl[j], 0));
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GB_TABLE_DATA,
+ RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(gb_tbl[j], 0));
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_B_TABLE_DATA,
+ RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(b_tbl[j], 0));
+ }
+
+ lsc_table_sel = lsc_status & RKISP1_CIF_ISP_LSC_ACTIVE_TABLE ?
+ RKISP1_CIF_ISP_LSC_TABLE_0 : RKISP1_CIF_ISP_LSC_TABLE_1;
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_TABLE_SEL, lsc_table_sel);
+}
+
+static void
+rkisp1_lsc_matrix_config_v12(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_lsc_config *pconfig)
+{
+ struct rkisp1_device *rkisp1 = params->rkisp1;
+ u32 lsc_status, sram_addr, lsc_table_sel;
+ unsigned int i, j;
+
+ lsc_status = rkisp1_read(rkisp1, RKISP1_CIF_ISP_LSC_STATUS);
+
+ /* RKISP1_CIF_ISP_LSC_TABLE_ADDRESS_153 = ( 17 * 18 ) >> 1 */
+ sram_addr = lsc_status & RKISP1_CIF_ISP_LSC_ACTIVE_TABLE ?
+ RKISP1_CIF_ISP_LSC_TABLE_ADDRESS_0 :
+ RKISP1_CIF_ISP_LSC_TABLE_ADDRESS_153;
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_R_TABLE_ADDR, sram_addr);
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GR_TABLE_ADDR, sram_addr);
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GB_TABLE_ADDR, sram_addr);
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_B_TABLE_ADDR, sram_addr);
+
+ /* program data tables (table size is 9 * 17 = 153) */
+ for (i = 0; i < RKISP1_CIF_ISP_LSC_SAMPLES_MAX; i++) {
+ const __u16 *r_tbl = pconfig->r_data_tbl[i];
+ const __u16 *gr_tbl = pconfig->gr_data_tbl[i];
+ const __u16 *gb_tbl = pconfig->gb_data_tbl[i];
+ const __u16 *b_tbl = pconfig->b_data_tbl[i];
+
+ /*
+ * 17 sectors with 2 values in one DWORD = 9
+ * DWORDs (2nd value of last DWORD unused)
+ */
+ for (j = 0; j < RKISP1_CIF_ISP_LSC_SAMPLES_MAX - 1; j += 2) {
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_R_TABLE_DATA,
+ RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(
+ r_tbl[j], r_tbl[j + 1]));
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GR_TABLE_DATA,
+ RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(
+ gr_tbl[j], gr_tbl[j + 1]));
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GB_TABLE_DATA,
+ RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(
+ gb_tbl[j], gb_tbl[j + 1]));
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_B_TABLE_DATA,
+ RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(
+ b_tbl[j], b_tbl[j + 1]));
+ }
+
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_R_TABLE_DATA,
+ RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(r_tbl[j], 0));
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GR_TABLE_DATA,
+ RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(gr_tbl[j], 0));
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GB_TABLE_DATA,
+ RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(gb_tbl[j], 0));
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_B_TABLE_DATA,
+ RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(b_tbl[j], 0));
+ }
+
+ lsc_table_sel = lsc_status & RKISP1_CIF_ISP_LSC_ACTIVE_TABLE ?
+ RKISP1_CIF_ISP_LSC_TABLE_0 : RKISP1_CIF_ISP_LSC_TABLE_1;
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_TABLE_SEL, lsc_table_sel);
+}
+
+static void rkisp1_lsc_config(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_lsc_config *arg)
+{
+ struct rkisp1_device *rkisp1 = params->rkisp1;
+ u32 lsc_ctrl, data;
+ unsigned int i;
+
+ /* To config must be off , store the current status firstly */
+ lsc_ctrl = rkisp1_read(rkisp1, RKISP1_CIF_ISP_LSC_CTRL);
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_LSC_CTRL,
+ RKISP1_CIF_ISP_LSC_CTRL_ENA);
+ params->ops->lsc_matrix_config(params, arg);
+
+ for (i = 0; i < RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE / 2; i++) {
+ /* program x size tables */
+ data = RKISP1_CIF_ISP_LSC_SECT_SIZE(arg->x_size_tbl[i * 2],
+ arg->x_size_tbl[i * 2 + 1]);
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_XSIZE(i), data);
+
+ /* program x grad tables */
+ data = RKISP1_CIF_ISP_LSC_SECT_GRAD(arg->x_grad_tbl[i * 2],
+ arg->x_grad_tbl[i * 2 + 1]);
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_XGRAD(i), data);
+
+ /* program y size tables */
+ data = RKISP1_CIF_ISP_LSC_SECT_SIZE(arg->y_size_tbl[i * 2],
+ arg->y_size_tbl[i * 2 + 1]);
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_YSIZE(i), data);
+
+ /* program y grad tables */
+ data = RKISP1_CIF_ISP_LSC_SECT_GRAD(arg->y_grad_tbl[i * 2],
+ arg->y_grad_tbl[i * 2 + 1]);
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_YGRAD(i), data);
+ }
+
+ /* restore the lsc ctrl status */
+ if (lsc_ctrl & RKISP1_CIF_ISP_LSC_CTRL_ENA)
+ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_LSC_CTRL,
+ RKISP1_CIF_ISP_LSC_CTRL_ENA);
+ else
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_LSC_CTRL,
+ RKISP1_CIF_ISP_LSC_CTRL_ENA);
+}
+
+/* ISP Filtering function */
+static void rkisp1_flt_config(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_flt_config *arg)
+{
+ u32 filt_mode;
+
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_FILT_THRESH_BL0,
+ arg->thresh_bl0);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_FILT_THRESH_BL1,
+ arg->thresh_bl1);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_FILT_THRESH_SH0,
+ arg->thresh_sh0);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_FILT_THRESH_SH1,
+ arg->thresh_sh1);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_FILT_FAC_BL0,
+ arg->fac_bl0);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_FILT_FAC_BL1,
+ arg->fac_bl1);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_FILT_FAC_MID,
+ arg->fac_mid);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_FILT_FAC_SH0,
+ arg->fac_sh0);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_FILT_FAC_SH1,
+ arg->fac_sh1);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_FILT_LUM_WEIGHT,
+ arg->lum_weight);
+
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_FILT_MODE,
+ (arg->mode ? RKISP1_CIF_ISP_FLT_MODE_DNR : 0) |
+ RKISP1_CIF_ISP_FLT_CHROMA_V_MODE(arg->chr_v_mode) |
+ RKISP1_CIF_ISP_FLT_CHROMA_H_MODE(arg->chr_h_mode) |
+ RKISP1_CIF_ISP_FLT_GREEN_STAGE1(arg->grn_stage1));
+
+ /* avoid to override the old enable value */
+ filt_mode = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_FILT_MODE);
+ filt_mode &= RKISP1_CIF_ISP_FLT_ENA;
+ if (arg->mode)
+ filt_mode |= RKISP1_CIF_ISP_FLT_MODE_DNR;
+ filt_mode |= RKISP1_CIF_ISP_FLT_CHROMA_V_MODE(arg->chr_v_mode) |
+ RKISP1_CIF_ISP_FLT_CHROMA_H_MODE(arg->chr_h_mode) |
+ RKISP1_CIF_ISP_FLT_GREEN_STAGE1(arg->grn_stage1);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_FILT_MODE, filt_mode);
+}
+
+/* ISP demosaic interface function */
+static int rkisp1_bdm_config(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_bdm_config *arg)
+{
+ u32 bdm_th;
+
+ /* avoid to override the old enable value */
+ bdm_th = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_DEMOSAIC);
+ bdm_th &= RKISP1_CIF_ISP_DEMOSAIC_BYPASS;
+ bdm_th |= arg->demosaic_th & ~RKISP1_CIF_ISP_DEMOSAIC_BYPASS;
+ /* set demosaic threshold */
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DEMOSAIC, bdm_th);
+ return 0;
+}
+
+/* ISP GAMMA correction interface function */
+static void rkisp1_sdg_config(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_sdg_config *arg)
+{
+ unsigned int i;
+
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_GAMMA_DX_LO,
+ arg->xa_pnts.gamma_dx0);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_GAMMA_DX_HI,
+ arg->xa_pnts.gamma_dx1);
+
+ for (i = 0; i < RKISP1_CIF_ISP_DEGAMMA_CURVE_SIZE; i++) {
+ rkisp1_write(params->rkisp1,
+ RKISP1_CIF_ISP_GAMMA_R_Y0 + i * 4,
+ arg->curve_r.gamma_y[i]);
+ rkisp1_write(params->rkisp1,
+ RKISP1_CIF_ISP_GAMMA_G_Y0 + i * 4,
+ arg->curve_g.gamma_y[i]);
+ rkisp1_write(params->rkisp1,
+ RKISP1_CIF_ISP_GAMMA_B_Y0 + i * 4,
+ arg->curve_b.gamma_y[i]);
+ }
+}
+
+/* ISP GAMMA correction interface function */
+static void rkisp1_goc_config_v10(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_goc_config *arg)
+{
+ unsigned int i;
+
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CTRL,
+ RKISP1_CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_GAMMA_OUT_MODE_V10,
+ arg->mode);
+
+ for (i = 0; i < RKISP1_CIF_ISP_GAMMA_OUT_MAX_SAMPLES_V10; i++)
+ rkisp1_write(params->rkisp1,
+ RKISP1_CIF_ISP_GAMMA_OUT_Y_0_V10 + i * 4,
+ arg->gamma_y[i]);
+}
+
+static void rkisp1_goc_config_v12(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_goc_config *arg)
+{
+ unsigned int i;
+ u32 value;
+
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CTRL,
+ RKISP1_CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_GAMMA_OUT_MODE_V12,
+ arg->mode);
+
+ for (i = 0; i < RKISP1_CIF_ISP_GAMMA_OUT_MAX_SAMPLES_V12 / 2; i++) {
+ value = RKISP1_CIF_ISP_GAMMA_VALUE_V12(
+ arg->gamma_y[2 * i + 1],
+ arg->gamma_y[2 * i]);
+ rkisp1_write(params->rkisp1,
+ RKISP1_CIF_ISP_GAMMA_OUT_Y_0_V12 + i * 4, value);
+ }
+}
+
+/* ISP Cross Talk */
+static void rkisp1_ctk_config(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_ctk_config *arg)
+{
+ unsigned int i, j, k = 0;
+
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ rkisp1_write(params->rkisp1,
+ RKISP1_CIF_ISP_CT_COEFF_0 + 4 * k++,
+ arg->coeff[i][j]);
+ for (i = 0; i < 3; i++)
+ rkisp1_write(params->rkisp1,
+ RKISP1_CIF_ISP_CT_OFFSET_R + i * 4,
+ arg->ct_offset[i]);
+}
+
+static void rkisp1_ctk_enable(struct rkisp1_params *params, bool en)
+{
+ if (en)
+ return;
+
+ /* Write back the default values. */
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CT_COEFF_0, 0x80);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CT_COEFF_1, 0);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CT_COEFF_2, 0);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CT_COEFF_3, 0);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CT_COEFF_4, 0x80);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CT_COEFF_5, 0);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CT_COEFF_6, 0);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CT_COEFF_7, 0);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CT_COEFF_8, 0x80);
+
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CT_OFFSET_R, 0);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CT_OFFSET_G, 0);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CT_OFFSET_B, 0);
+}
+
+/* ISP White Balance Mode */
+static void rkisp1_awb_meas_config_v10(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_awb_meas_config *arg)
+{
+ u32 reg_val = 0;
+ /* based on the mode,configure the awb module */
+ if (arg->awb_mode == RKISP1_CIF_ISP_AWB_MODE_YCBCR) {
+ /* Reference Cb and Cr */
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_REF_V10,
+ RKISP1_CIF_ISP_AWB_REF_CR_SET(arg->awb_ref_cr) |
+ arg->awb_ref_cb);
+ /* Yc Threshold */
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_THRESH_V10,
+ RKISP1_CIF_ISP_AWB_MAX_Y_SET(arg->max_y) |
+ RKISP1_CIF_ISP_AWB_MIN_Y_SET(arg->min_y) |
+ RKISP1_CIF_ISP_AWB_MAX_CS_SET(arg->max_csum) |
+ arg->min_c);
+ }
+
+ reg_val = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_AWB_PROP_V10);
+ if (arg->enable_ymax_cmp)
+ reg_val |= RKISP1_CIF_ISP_AWB_YMAX_CMP_EN;
+ else
+ reg_val &= ~RKISP1_CIF_ISP_AWB_YMAX_CMP_EN;
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_PROP_V10, reg_val);
+
+ /* window offset */
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_WND_V_OFFS_V10,
+ arg->awb_wnd.v_offs);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_WND_H_OFFS_V10,
+ arg->awb_wnd.h_offs);
+ /* AWB window size */
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_WND_V_SIZE_V10,
+ arg->awb_wnd.v_size);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_WND_H_SIZE_V10,
+ arg->awb_wnd.h_size);
+ /* Number of frames */
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_FRAMES_V10,
+ arg->frames);
+}
+
+static void rkisp1_awb_meas_config_v12(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_awb_meas_config *arg)
+{
+ u32 reg_val = 0;
+ /* based on the mode,configure the awb module */
+ if (arg->awb_mode == RKISP1_CIF_ISP_AWB_MODE_YCBCR) {
+ /* Reference Cb and Cr */
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_REF_V12,
+ RKISP1_CIF_ISP_AWB_REF_CR_SET(arg->awb_ref_cr) |
+ arg->awb_ref_cb);
+ /* Yc Threshold */
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_THRESH_V12,
+ RKISP1_CIF_ISP_AWB_MAX_Y_SET(arg->max_y) |
+ RKISP1_CIF_ISP_AWB_MIN_Y_SET(arg->min_y) |
+ RKISP1_CIF_ISP_AWB_MAX_CS_SET(arg->max_csum) |
+ arg->min_c);
+ }
+
+ reg_val = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_AWB_PROP_V12);
+ if (arg->enable_ymax_cmp)
+ reg_val |= RKISP1_CIF_ISP_AWB_YMAX_CMP_EN;
+ else
+ reg_val &= ~RKISP1_CIF_ISP_AWB_YMAX_CMP_EN;
+ reg_val &= ~RKISP1_CIF_ISP_AWB_SET_FRAMES_MASK_V12;
+ reg_val |= RKISP1_CIF_ISP_AWB_SET_FRAMES_V12(arg->frames);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_PROP_V12, reg_val);
+
+ /* window offset */
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_OFFS_V12,
+ arg->awb_wnd.v_offs << 16 | arg->awb_wnd.h_offs);
+ /* AWB window size */
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_SIZE_V12,
+ arg->awb_wnd.v_size << 16 | arg->awb_wnd.h_size);
+}
+
+static void
+rkisp1_awb_meas_enable_v10(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_awb_meas_config *arg,
+ bool en)
+{
+ u32 reg_val = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_AWB_PROP_V10);
+
+ /* switch off */
+ reg_val &= RKISP1_CIF_ISP_AWB_MODE_MASK_NONE;
+
+ if (en) {
+ if (arg->awb_mode == RKISP1_CIF_ISP_AWB_MODE_RGB)
+ reg_val |= RKISP1_CIF_ISP_AWB_MODE_RGB_EN;
+ else
+ reg_val |= RKISP1_CIF_ISP_AWB_MODE_YCBCR_EN;
+
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_PROP_V10,
+ reg_val);
+
+ /* Measurements require AWB block be active. */
+ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL,
+ RKISP1_CIF_ISP_CTRL_ISP_AWB_ENA);
+ } else {
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_PROP_V10,
+ reg_val);
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CTRL,
+ RKISP1_CIF_ISP_CTRL_ISP_AWB_ENA);
+ }
+}
+
+static void
+rkisp1_awb_meas_enable_v12(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_awb_meas_config *arg,
+ bool en)
+{
+ u32 reg_val = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_AWB_PROP_V12);
+
+ /* switch off */
+ reg_val &= RKISP1_CIF_ISP_AWB_MODE_MASK_NONE;
+
+ if (en) {
+ if (arg->awb_mode == RKISP1_CIF_ISP_AWB_MODE_RGB)
+ reg_val |= RKISP1_CIF_ISP_AWB_MODE_RGB_EN;
+ else
+ reg_val |= RKISP1_CIF_ISP_AWB_MODE_YCBCR_EN;
+
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_PROP_V12,
+ reg_val);
+
+ /* Measurements require AWB block be active. */
+ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL,
+ RKISP1_CIF_ISP_CTRL_ISP_AWB_ENA);
+ } else {
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_PROP_V12,
+ reg_val);
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CTRL,
+ RKISP1_CIF_ISP_CTRL_ISP_AWB_ENA);
+ }
+}
+
+static void
+rkisp1_awb_gain_config_v10(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_awb_gain_config *arg)
+{
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_GAIN_G_V10,
+ RKISP1_CIF_ISP_AWB_GAIN_R_SET(arg->gain_green_r) |
+ arg->gain_green_b);
+
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_GAIN_RB_V10,
+ RKISP1_CIF_ISP_AWB_GAIN_R_SET(arg->gain_red) |
+ arg->gain_blue);
+}
+
+static void
+rkisp1_awb_gain_config_v12(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_awb_gain_config *arg)
+{
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_GAIN_G_V12,
+ RKISP1_CIF_ISP_AWB_GAIN_R_SET(arg->gain_green_r) |
+ arg->gain_green_b);
+
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_GAIN_RB_V12,
+ RKISP1_CIF_ISP_AWB_GAIN_R_SET(arg->gain_red) |
+ arg->gain_blue);
+}
+
+static void rkisp1_aec_config_v10(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_aec_config *arg)
+{
+ unsigned int block_hsize, block_vsize;
+ u32 exp_ctrl;
+
+ /* avoid to override the old enable value */
+ exp_ctrl = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_EXP_CTRL);
+ exp_ctrl &= RKISP1_CIF_ISP_EXP_ENA;
+ if (arg->autostop)
+ exp_ctrl |= RKISP1_CIF_ISP_EXP_CTRL_AUTOSTOP;
+ if (arg->mode == RKISP1_CIF_ISP_EXP_MEASURING_MODE_1)
+ exp_ctrl |= RKISP1_CIF_ISP_EXP_CTRL_MEASMODE_1;
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_EXP_CTRL, exp_ctrl);
+
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_EXP_H_OFFSET_V10,
+ arg->meas_window.h_offs);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_EXP_V_OFFSET_V10,
+ arg->meas_window.v_offs);
+
+ block_hsize = arg->meas_window.h_size /
+ RKISP1_CIF_ISP_EXP_COLUMN_NUM_V10 - 1;
+ block_vsize = arg->meas_window.v_size /
+ RKISP1_CIF_ISP_EXP_ROW_NUM_V10 - 1;
+
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_EXP_H_SIZE_V10,
+ RKISP1_CIF_ISP_EXP_H_SIZE_SET_V10(block_hsize));
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_EXP_V_SIZE_V10,
+ RKISP1_CIF_ISP_EXP_V_SIZE_SET_V10(block_vsize));
+}
+
+static void rkisp1_aec_config_v12(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_aec_config *arg)
+{
+ u32 exp_ctrl;
+ u32 block_hsize, block_vsize;
+ u32 wnd_num_idx = 1;
+ static const u32 ae_wnd_num[] = { 5, 9, 15, 15 };
+
+ /* avoid to override the old enable value */
+ exp_ctrl = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_EXP_CTRL);
+ exp_ctrl &= RKISP1_CIF_ISP_EXP_ENA;
+ if (arg->autostop)
+ exp_ctrl |= RKISP1_CIF_ISP_EXP_CTRL_AUTOSTOP;
+ if (arg->mode == RKISP1_CIF_ISP_EXP_MEASURING_MODE_1)
+ exp_ctrl |= RKISP1_CIF_ISP_EXP_CTRL_MEASMODE_1;
+ exp_ctrl |= RKISP1_CIF_ISP_EXP_CTRL_WNDNUM_SET_V12(wnd_num_idx);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_EXP_CTRL, exp_ctrl);
+
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_EXP_OFFS_V12,
+ RKISP1_CIF_ISP_EXP_V_OFFSET_SET_V12(arg->meas_window.v_offs) |
+ RKISP1_CIF_ISP_EXP_H_OFFSET_SET_V12(arg->meas_window.h_offs));
+
+ block_hsize = arg->meas_window.h_size / ae_wnd_num[wnd_num_idx] - 1;
+ block_vsize = arg->meas_window.v_size / ae_wnd_num[wnd_num_idx] - 1;
+
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_EXP_SIZE_V12,
+ RKISP1_CIF_ISP_EXP_V_SIZE_SET_V12(block_vsize) |
+ RKISP1_CIF_ISP_EXP_H_SIZE_SET_V12(block_hsize));
+}
+
+static void rkisp1_cproc_config(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_cproc_config *arg)
+{
+ struct rkisp1_cif_isp_isp_other_cfg *cur_other_cfg =
+ container_of(arg, struct rkisp1_cif_isp_isp_other_cfg, cproc_config);
+ struct rkisp1_cif_isp_ie_config *cur_ie_config =
+ &cur_other_cfg->ie_config;
+ u32 effect = cur_ie_config->effect;
+ u32 quantization = params->quantization;
+
+ rkisp1_write(params->rkisp1, RKISP1_CIF_C_PROC_CONTRAST,
+ arg->contrast);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_C_PROC_HUE, arg->hue);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_C_PROC_SATURATION, arg->sat);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_C_PROC_BRIGHTNESS,
+ arg->brightness);
+
+ if (quantization != V4L2_QUANTIZATION_FULL_RANGE ||
+ effect != V4L2_COLORFX_NONE) {
+ rkisp1_param_clear_bits(params, RKISP1_CIF_C_PROC_CTRL,
+ RKISP1_CIF_C_PROC_YOUT_FULL |
+ RKISP1_CIF_C_PROC_YIN_FULL |
+ RKISP1_CIF_C_PROC_COUT_FULL);
+ } else {
+ rkisp1_param_set_bits(params, RKISP1_CIF_C_PROC_CTRL,
+ RKISP1_CIF_C_PROC_YOUT_FULL |
+ RKISP1_CIF_C_PROC_YIN_FULL |
+ RKISP1_CIF_C_PROC_COUT_FULL);
+ }
+}
+
+static void rkisp1_hst_config_v10(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_hst_config *arg)
+{
+ unsigned int block_hsize, block_vsize;
+ static const u32 hist_weight_regs[] = {
+ RKISP1_CIF_ISP_HIST_WEIGHT_00TO30_V10,
+ RKISP1_CIF_ISP_HIST_WEIGHT_40TO21_V10,
+ RKISP1_CIF_ISP_HIST_WEIGHT_31TO12_V10,
+ RKISP1_CIF_ISP_HIST_WEIGHT_22TO03_V10,
+ RKISP1_CIF_ISP_HIST_WEIGHT_13TO43_V10,
+ RKISP1_CIF_ISP_HIST_WEIGHT_04TO34_V10,
+ };
+ const u8 *weight;
+ unsigned int i;
+ u32 hist_prop;
+
+ /* avoid to override the old enable value */
+ hist_prop = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_HIST_PROP_V10);
+ hist_prop &= RKISP1_CIF_ISP_HIST_PROP_MODE_MASK_V10;
+ hist_prop |= RKISP1_CIF_ISP_HIST_PREDIV_SET_V10(arg->histogram_predivider);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_HIST_PROP_V10, hist_prop);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_HIST_H_OFFS_V10,
+ arg->meas_window.h_offs);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_HIST_V_OFFS_V10,
+ arg->meas_window.v_offs);
+
+ block_hsize = arg->meas_window.h_size /
+ RKISP1_CIF_ISP_HIST_COLUMN_NUM_V10 - 1;
+ block_vsize = arg->meas_window.v_size / RKISP1_CIF_ISP_HIST_ROW_NUM_V10 - 1;
+
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_HIST_H_SIZE_V10,
+ block_hsize);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_HIST_V_SIZE_V10,
+ block_vsize);
+
+ weight = arg->hist_weight;
+ for (i = 0; i < ARRAY_SIZE(hist_weight_regs); ++i, weight += 4)
+ rkisp1_write(params->rkisp1, hist_weight_regs[i],
+ RKISP1_CIF_ISP_HIST_WEIGHT_SET_V10(weight[0], weight[1],
+ weight[2], weight[3]));
+
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_HIST_WEIGHT_44_V10,
+ weight[0] & 0x1F);
+}
+
+static void rkisp1_hst_config_v12(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_hst_config *arg)
+{
+ unsigned int i, j;
+ u32 block_hsize, block_vsize;
+ u32 wnd_num_idx, hist_weight_num, hist_ctrl, value;
+ u8 weight15x15[RKISP1_CIF_ISP_HIST_WEIGHT_REG_SIZE_V12];
+ static const u32 hist_wnd_num[] = { 5, 9, 15, 15 };
+
+ /* now we just support 9x9 window */
+ wnd_num_idx = 1;
+ memset(weight15x15, 0x00, sizeof(weight15x15));
+ /* avoid to override the old enable value */
+ hist_ctrl = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_HIST_CTRL_V12);
+ hist_ctrl &= RKISP1_CIF_ISP_HIST_CTRL_MODE_MASK_V12 |
+ RKISP1_CIF_ISP_HIST_CTRL_EN_MASK_V12;
+ hist_ctrl = hist_ctrl |
+ RKISP1_CIF_ISP_HIST_CTRL_INTRSEL_SET_V12(1) |
+ RKISP1_CIF_ISP_HIST_CTRL_DATASEL_SET_V12(0) |
+ RKISP1_CIF_ISP_HIST_CTRL_WATERLINE_SET_V12(0) |
+ RKISP1_CIF_ISP_HIST_CTRL_AUTOSTOP_SET_V12(0) |
+ RKISP1_CIF_ISP_HIST_CTRL_WNDNUM_SET_V12(1) |
+ RKISP1_CIF_ISP_HIST_CTRL_STEPSIZE_SET_V12(arg->histogram_predivider);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_HIST_CTRL_V12, hist_ctrl);
+
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_HIST_OFFS_V12,
+ RKISP1_CIF_ISP_HIST_OFFS_SET_V12(arg->meas_window.h_offs,
+ arg->meas_window.v_offs));
+
+ block_hsize = arg->meas_window.h_size / hist_wnd_num[wnd_num_idx] - 1;
+ block_vsize = arg->meas_window.v_size / hist_wnd_num[wnd_num_idx] - 1;
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_HIST_SIZE_V12,
+ RKISP1_CIF_ISP_HIST_SIZE_SET_V12(block_hsize, block_vsize));
+
+ for (i = 0; i < hist_wnd_num[wnd_num_idx]; i++) {
+ for (j = 0; j < hist_wnd_num[wnd_num_idx]; j++) {
+ weight15x15[i * RKISP1_CIF_ISP_HIST_ROW_NUM_V12 + j] =
+ arg->hist_weight[i * hist_wnd_num[wnd_num_idx] + j];
+ }
+ }
+
+ hist_weight_num = RKISP1_CIF_ISP_HIST_WEIGHT_REG_SIZE_V12;
+ for (i = 0; i < (hist_weight_num / 4); i++) {
+ value = RKISP1_CIF_ISP_HIST_WEIGHT_SET_V12(
+ weight15x15[4 * i + 0],
+ weight15x15[4 * i + 1],
+ weight15x15[4 * i + 2],
+ weight15x15[4 * i + 3]);
+ rkisp1_write(params->rkisp1,
+ RKISP1_CIF_ISP_HIST_WEIGHT_V12 + 4 * i, value);
+ }
+ value = RKISP1_CIF_ISP_HIST_WEIGHT_SET_V12(weight15x15[4 * i + 0], 0, 0, 0);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_HIST_WEIGHT_V12 + 4 * i,
+ value);
+}
+
+static void
+rkisp1_hst_enable_v10(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_hst_config *arg, bool en)
+{
+ if (en) {
+ u32 hist_prop = rkisp1_read(params->rkisp1,
+ RKISP1_CIF_ISP_HIST_PROP_V10);
+
+ hist_prop &= ~RKISP1_CIF_ISP_HIST_PROP_MODE_MASK_V10;
+ hist_prop |= arg->mode;
+ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_HIST_PROP_V10,
+ hist_prop);
+ } else {
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_HIST_PROP_V10,
+ RKISP1_CIF_ISP_HIST_PROP_MODE_MASK_V10);
+ }
+}
+
+static void
+rkisp1_hst_enable_v12(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_hst_config *arg, bool en)
+{
+ if (en) {
+ u32 hist_ctrl = rkisp1_read(params->rkisp1,
+ RKISP1_CIF_ISP_HIST_CTRL_V12);
+
+ hist_ctrl &= ~RKISP1_CIF_ISP_HIST_CTRL_MODE_MASK_V12;
+ hist_ctrl |= RKISP1_CIF_ISP_HIST_CTRL_MODE_SET_V12(arg->mode);
+ hist_ctrl |= RKISP1_CIF_ISP_HIST_CTRL_EN_SET_V12(1);
+ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_HIST_CTRL_V12,
+ hist_ctrl);
+ } else {
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_HIST_CTRL_V12,
+ RKISP1_CIF_ISP_HIST_CTRL_MODE_MASK_V12 |
+ RKISP1_CIF_ISP_HIST_CTRL_EN_MASK_V12);
+ }
+}
+
+static void rkisp1_afm_config_v10(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_afc_config *arg)
+{
+ size_t num_of_win = min_t(size_t, ARRAY_SIZE(arg->afm_win),
+ arg->num_afm_win);
+ u32 afm_ctrl = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_AFM_CTRL);
+ unsigned int i;
+
+ /* Switch off to configure. */
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_AFM_CTRL,
+ RKISP1_CIF_ISP_AFM_ENA);
+
+ for (i = 0; i < num_of_win; i++) {
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AFM_LT_A + i * 8,
+ RKISP1_CIF_ISP_AFM_WINDOW_X(arg->afm_win[i].h_offs) |
+ RKISP1_CIF_ISP_AFM_WINDOW_Y(arg->afm_win[i].v_offs));
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AFM_RB_A + i * 8,
+ RKISP1_CIF_ISP_AFM_WINDOW_X(arg->afm_win[i].h_size +
+ arg->afm_win[i].h_offs) |
+ RKISP1_CIF_ISP_AFM_WINDOW_Y(arg->afm_win[i].v_size +
+ arg->afm_win[i].v_offs));
+ }
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AFM_THRES, arg->thres);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AFM_VAR_SHIFT,
+ arg->var_shift);
+ /* restore afm status */
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AFM_CTRL, afm_ctrl);
+}
+
+static void rkisp1_afm_config_v12(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_afc_config *arg)
+{
+ size_t num_of_win = min_t(size_t, ARRAY_SIZE(arg->afm_win),
+ arg->num_afm_win);
+ u32 afm_ctrl = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_AFM_CTRL);
+ u32 lum_var_shift, afm_var_shift;
+ unsigned int i;
+
+ /* Switch off to configure. */
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_AFM_CTRL,
+ RKISP1_CIF_ISP_AFM_ENA);
+
+ for (i = 0; i < num_of_win; i++) {
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AFM_LT_A + i * 8,
+ RKISP1_CIF_ISP_AFM_WINDOW_X(arg->afm_win[i].h_offs) |
+ RKISP1_CIF_ISP_AFM_WINDOW_Y(arg->afm_win[i].v_offs));
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AFM_RB_A + i * 8,
+ RKISP1_CIF_ISP_AFM_WINDOW_X(arg->afm_win[i].h_size +
+ arg->afm_win[i].h_offs) |
+ RKISP1_CIF_ISP_AFM_WINDOW_Y(arg->afm_win[i].v_size +
+ arg->afm_win[i].v_offs));
+ }
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AFM_THRES, arg->thres);
+
+ lum_var_shift = RKISP1_CIF_ISP_AFM_GET_LUM_SHIFT_a_V12(arg->var_shift);
+ afm_var_shift = RKISP1_CIF_ISP_AFM_GET_AFM_SHIFT_a_V12(arg->var_shift);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AFM_VAR_SHIFT,
+ RKISP1_CIF_ISP_AFM_SET_SHIFT_a_V12(lum_var_shift, afm_var_shift) |
+ RKISP1_CIF_ISP_AFM_SET_SHIFT_b_V12(lum_var_shift, afm_var_shift) |
+ RKISP1_CIF_ISP_AFM_SET_SHIFT_c_V12(lum_var_shift, afm_var_shift));
+
+ /* restore afm status */
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AFM_CTRL, afm_ctrl);
+}
+
+static void rkisp1_ie_config(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_ie_config *arg)
+{
+ u32 eff_ctrl;
+
+ eff_ctrl = rkisp1_read(params->rkisp1, RKISP1_CIF_IMG_EFF_CTRL);
+ eff_ctrl &= ~RKISP1_CIF_IMG_EFF_CTRL_MODE_MASK;
+
+ if (params->quantization == V4L2_QUANTIZATION_FULL_RANGE)
+ eff_ctrl |= RKISP1_CIF_IMG_EFF_CTRL_YCBCR_FULL;
+
+ switch (arg->effect) {
+ case V4L2_COLORFX_SEPIA:
+ eff_ctrl |= RKISP1_CIF_IMG_EFF_CTRL_MODE_SEPIA;
+ break;
+ case V4L2_COLORFX_SET_CBCR:
+ rkisp1_write(params->rkisp1, RKISP1_CIF_IMG_EFF_TINT,
+ arg->eff_tint);
+ eff_ctrl |= RKISP1_CIF_IMG_EFF_CTRL_MODE_SEPIA;
+ break;
+ /*
+ * Color selection is similar to water color(AQUA):
+ * grayscale + selected color w threshold
+ */
+ case V4L2_COLORFX_AQUA:
+ eff_ctrl |= RKISP1_CIF_IMG_EFF_CTRL_MODE_COLOR_SEL;
+ rkisp1_write(params->rkisp1, RKISP1_CIF_IMG_EFF_COLOR_SEL,
+ arg->color_sel);
+ break;
+ case V4L2_COLORFX_EMBOSS:
+ eff_ctrl |= RKISP1_CIF_IMG_EFF_CTRL_MODE_EMBOSS;
+ rkisp1_write(params->rkisp1, RKISP1_CIF_IMG_EFF_MAT_1,
+ arg->eff_mat_1);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_IMG_EFF_MAT_2,
+ arg->eff_mat_2);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_IMG_EFF_MAT_3,
+ arg->eff_mat_3);
+ break;
+ case V4L2_COLORFX_SKETCH:
+ eff_ctrl |= RKISP1_CIF_IMG_EFF_CTRL_MODE_SKETCH;
+ rkisp1_write(params->rkisp1, RKISP1_CIF_IMG_EFF_MAT_3,
+ arg->eff_mat_3);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_IMG_EFF_MAT_4,
+ arg->eff_mat_4);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_IMG_EFF_MAT_5,
+ arg->eff_mat_5);
+ break;
+ case V4L2_COLORFX_BW:
+ eff_ctrl |= RKISP1_CIF_IMG_EFF_CTRL_MODE_BLACKWHITE;
+ break;
+ case V4L2_COLORFX_NEGATIVE:
+ eff_ctrl |= RKISP1_CIF_IMG_EFF_CTRL_MODE_NEGATIVE;
+ break;
+ default:
+ break;
+ }
+
+ rkisp1_write(params->rkisp1, RKISP1_CIF_IMG_EFF_CTRL, eff_ctrl);
+}
+
+static void rkisp1_ie_enable(struct rkisp1_params *params, bool en)
+{
+ if (en) {
+ rkisp1_param_set_bits(params, RKISP1_CIF_VI_ICCL,
+ RKISP1_CIF_VI_ICCL_IE_CLK);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_IMG_EFF_CTRL,
+ RKISP1_CIF_IMG_EFF_CTRL_ENABLE);
+ rkisp1_param_set_bits(params, RKISP1_CIF_IMG_EFF_CTRL,
+ RKISP1_CIF_IMG_EFF_CTRL_CFG_UPD);
+ } else {
+ rkisp1_param_clear_bits(params, RKISP1_CIF_IMG_EFF_CTRL,
+ RKISP1_CIF_IMG_EFF_CTRL_ENABLE);
+ rkisp1_param_clear_bits(params, RKISP1_CIF_VI_ICCL,
+ RKISP1_CIF_VI_ICCL_IE_CLK);
+ }
+}
+
+static void rkisp1_csm_config(struct rkisp1_params *params)
+{
+ struct csm_coeffs {
+ u16 limited[9];
+ u16 full[9];
+ };
+ static const struct csm_coeffs rec601_coeffs = {
+ .limited = {
+ 0x0021, 0x0042, 0x000d,
+ 0x01ed, 0x01db, 0x0038,
+ 0x0038, 0x01d1, 0x01f7,
+ },
+ .full = {
+ 0x0026, 0x004b, 0x000f,
+ 0x01ea, 0x01d6, 0x0040,
+ 0x0040, 0x01ca, 0x01f6,
+ },
+ };
+ static const struct csm_coeffs rec709_coeffs = {
+ .limited = {
+ 0x0018, 0x0050, 0x0008,
+ 0x01f3, 0x01d5, 0x0038,
+ 0x0038, 0x01cd, 0x01fb,
+ },
+ .full = {
+ 0x001b, 0x005c, 0x0009,
+ 0x01f1, 0x01cf, 0x0040,
+ 0x0040, 0x01c6, 0x01fa,
+ },
+ };
+ static const struct csm_coeffs rec2020_coeffs = {
+ .limited = {
+ 0x001d, 0x004c, 0x0007,
+ 0x01f0, 0x01d8, 0x0038,
+ 0x0038, 0x01cd, 0x01fb,
+ },
+ .full = {
+ 0x0022, 0x0057, 0x0008,
+ 0x01ee, 0x01d2, 0x0040,
+ 0x0040, 0x01c5, 0x01fb,
+ },
+ };
+ static const struct csm_coeffs smpte240m_coeffs = {
+ .limited = {
+ 0x0018, 0x004f, 0x000a,
+ 0x01f3, 0x01d5, 0x0038,
+ 0x0038, 0x01ce, 0x01fa,
+ },
+ .full = {
+ 0x001b, 0x005a, 0x000b,
+ 0x01f1, 0x01cf, 0x0040,
+ 0x0040, 0x01c7, 0x01f9,
+ },
+ };
+
+ const struct csm_coeffs *coeffs;
+ const u16 *csm;
+ unsigned int i;
+
+ switch (params->ycbcr_encoding) {
+ case V4L2_YCBCR_ENC_601:
+ default:
+ coeffs = &rec601_coeffs;
+ break;
+ case V4L2_YCBCR_ENC_709:
+ coeffs = &rec709_coeffs;
+ break;
+ case V4L2_YCBCR_ENC_BT2020:
+ coeffs = &rec2020_coeffs;
+ break;
+ case V4L2_YCBCR_ENC_SMPTE240M:
+ coeffs = &smpte240m_coeffs;
+ break;
+ }
+
+ if (params->quantization == V4L2_QUANTIZATION_FULL_RANGE) {
+ csm = coeffs->full;
+ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL,
+ RKISP1_CIF_ISP_CTRL_ISP_CSM_Y_FULL_ENA |
+ RKISP1_CIF_ISP_CTRL_ISP_CSM_C_FULL_ENA);
+ } else {
+ csm = coeffs->limited;
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CTRL,
+ RKISP1_CIF_ISP_CTRL_ISP_CSM_Y_FULL_ENA |
+ RKISP1_CIF_ISP_CTRL_ISP_CSM_C_FULL_ENA);
+ }
+
+ for (i = 0; i < 9; i++)
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CC_COEFF_0 + i * 4,
+ csm[i]);
+}
+
+/* ISP De-noise Pre-Filter(DPF) function */
+static void rkisp1_dpf_config(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_dpf_config *arg)
+{
+ unsigned int isp_dpf_mode, spatial_coeff, i;
+
+ switch (arg->gain.mode) {
+ case RKISP1_CIF_ISP_DPF_GAIN_USAGE_NF_GAINS:
+ isp_dpf_mode = RKISP1_CIF_ISP_DPF_MODE_USE_NF_GAIN |
+ RKISP1_CIF_ISP_DPF_MODE_AWB_GAIN_COMP;
+ break;
+ case RKISP1_CIF_ISP_DPF_GAIN_USAGE_LSC_GAINS:
+ isp_dpf_mode = RKISP1_CIF_ISP_DPF_MODE_LSC_GAIN_COMP;
+ break;
+ case RKISP1_CIF_ISP_DPF_GAIN_USAGE_NF_LSC_GAINS:
+ isp_dpf_mode = RKISP1_CIF_ISP_DPF_MODE_USE_NF_GAIN |
+ RKISP1_CIF_ISP_DPF_MODE_AWB_GAIN_COMP |
+ RKISP1_CIF_ISP_DPF_MODE_LSC_GAIN_COMP;
+ break;
+ case RKISP1_CIF_ISP_DPF_GAIN_USAGE_AWB_GAINS:
+ isp_dpf_mode = RKISP1_CIF_ISP_DPF_MODE_AWB_GAIN_COMP;
+ break;
+ case RKISP1_CIF_ISP_DPF_GAIN_USAGE_AWB_LSC_GAINS:
+ isp_dpf_mode = RKISP1_CIF_ISP_DPF_MODE_LSC_GAIN_COMP |
+ RKISP1_CIF_ISP_DPF_MODE_AWB_GAIN_COMP;
+ break;
+ case RKISP1_CIF_ISP_DPF_GAIN_USAGE_DISABLED:
+ default:
+ isp_dpf_mode = 0;
+ break;
+ }
+
+ if (arg->nll.scale_mode == RKISP1_CIF_ISP_NLL_SCALE_LOGARITHMIC)
+ isp_dpf_mode |= RKISP1_CIF_ISP_DPF_MODE_NLL_SEGMENTATION;
+ if (arg->rb_flt.fltsize == RKISP1_CIF_ISP_DPF_RB_FILTERSIZE_9x9)
+ isp_dpf_mode |= RKISP1_CIF_ISP_DPF_MODE_RB_FLTSIZE_9x9;
+ if (!arg->rb_flt.r_enable)
+ isp_dpf_mode |= RKISP1_CIF_ISP_DPF_MODE_R_FLT_DIS;
+ if (!arg->rb_flt.b_enable)
+ isp_dpf_mode |= RKISP1_CIF_ISP_DPF_MODE_B_FLT_DIS;
+ if (!arg->g_flt.gb_enable)
+ isp_dpf_mode |= RKISP1_CIF_ISP_DPF_MODE_GB_FLT_DIS;
+ if (!arg->g_flt.gr_enable)
+ isp_dpf_mode |= RKISP1_CIF_ISP_DPF_MODE_GR_FLT_DIS;
+
+ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_DPF_MODE,
+ isp_dpf_mode);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPF_NF_GAIN_B,
+ arg->gain.nf_b_gain);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPF_NF_GAIN_R,
+ arg->gain.nf_r_gain);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPF_NF_GAIN_GB,
+ arg->gain.nf_gb_gain);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPF_NF_GAIN_GR,
+ arg->gain.nf_gr_gain);
+
+ for (i = 0; i < RKISP1_CIF_ISP_DPF_MAX_NLF_COEFFS; i++) {
+ rkisp1_write(params->rkisp1,
+ RKISP1_CIF_ISP_DPF_NULL_COEFF_0 + i * 4,
+ arg->nll.coeff[i]);
+ }
+
+ spatial_coeff = arg->g_flt.spatial_coeff[0] |
+ (arg->g_flt.spatial_coeff[1] << 8) |
+ (arg->g_flt.spatial_coeff[2] << 16) |
+ (arg->g_flt.spatial_coeff[3] << 24);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPF_S_WEIGHT_G_1_4,
+ spatial_coeff);
+
+ spatial_coeff = arg->g_flt.spatial_coeff[4] |
+ (arg->g_flt.spatial_coeff[5] << 8);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPF_S_WEIGHT_G_5_6,
+ spatial_coeff);
+
+ spatial_coeff = arg->rb_flt.spatial_coeff[0] |
+ (arg->rb_flt.spatial_coeff[1] << 8) |
+ (arg->rb_flt.spatial_coeff[2] << 16) |
+ (arg->rb_flt.spatial_coeff[3] << 24);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPF_S_WEIGHT_RB_1_4,
+ spatial_coeff);
+
+ spatial_coeff = arg->rb_flt.spatial_coeff[4] |
+ (arg->rb_flt.spatial_coeff[5] << 8);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPF_S_WEIGHT_RB_5_6,
+ spatial_coeff);
+}
+
+static void
+rkisp1_dpf_strength_config(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_dpf_strength_config *arg)
+{
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPF_STRENGTH_B, arg->b);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPF_STRENGTH_G, arg->g);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPF_STRENGTH_R, arg->r);
+}
+
+static void
+rkisp1_isp_isr_other_config(struct rkisp1_params *params,
+ const struct rkisp1_params_cfg *new_params)
+{
+ unsigned int module_en_update, module_cfg_update, module_ens;
+
+ module_en_update = new_params->module_en_update;
+ module_cfg_update = new_params->module_cfg_update;
+ module_ens = new_params->module_ens;
+
+ /* update dpc config */
+ if (module_cfg_update & RKISP1_CIF_ISP_MODULE_DPCC)
+ rkisp1_dpcc_config(params,
+ &new_params->others.dpcc_config);
+
+ if (module_en_update & RKISP1_CIF_ISP_MODULE_DPCC) {
+ if (module_ens & RKISP1_CIF_ISP_MODULE_DPCC)
+ rkisp1_param_set_bits(params,
+ RKISP1_CIF_ISP_DPCC_MODE,
+ RKISP1_CIF_ISP_DPCC_MODE_DPCC_ENABLE);
+ else
+ rkisp1_param_clear_bits(params,
+ RKISP1_CIF_ISP_DPCC_MODE,
+ RKISP1_CIF_ISP_DPCC_MODE_DPCC_ENABLE);
+ }
+
+ /* update bls config */
+ if (module_cfg_update & RKISP1_CIF_ISP_MODULE_BLS)
+ rkisp1_bls_config(params,
+ &new_params->others.bls_config);
+
+ if (module_en_update & RKISP1_CIF_ISP_MODULE_BLS) {
+ if (module_ens & RKISP1_CIF_ISP_MODULE_BLS)
+ rkisp1_param_set_bits(params,
+ RKISP1_CIF_ISP_BLS_CTRL,
+ RKISP1_CIF_ISP_BLS_ENA);
+ else
+ rkisp1_param_clear_bits(params,
+ RKISP1_CIF_ISP_BLS_CTRL,
+ RKISP1_CIF_ISP_BLS_ENA);
+ }
+
+ /* update sdg config */
+ if (module_cfg_update & RKISP1_CIF_ISP_MODULE_SDG)
+ rkisp1_sdg_config(params,
+ &new_params->others.sdg_config);
+
+ if (module_en_update & RKISP1_CIF_ISP_MODULE_SDG) {
+ if (module_ens & RKISP1_CIF_ISP_MODULE_SDG)
+ rkisp1_param_set_bits(params,
+ RKISP1_CIF_ISP_CTRL,
+ RKISP1_CIF_ISP_CTRL_ISP_GAMMA_IN_ENA);
+ else
+ rkisp1_param_clear_bits(params,
+ RKISP1_CIF_ISP_CTRL,
+ RKISP1_CIF_ISP_CTRL_ISP_GAMMA_IN_ENA);
+ }
+
+ /* update awb gains */
+ if (module_cfg_update & RKISP1_CIF_ISP_MODULE_AWB_GAIN)
+ params->ops->awb_gain_config(params, &new_params->others.awb_gain_config);
+
+ if (module_en_update & RKISP1_CIF_ISP_MODULE_AWB_GAIN) {
+ if (module_ens & RKISP1_CIF_ISP_MODULE_AWB_GAIN)
+ rkisp1_param_set_bits(params,
+ RKISP1_CIF_ISP_CTRL,
+ RKISP1_CIF_ISP_CTRL_ISP_AWB_ENA);
+ else
+ rkisp1_param_clear_bits(params,
+ RKISP1_CIF_ISP_CTRL,
+ RKISP1_CIF_ISP_CTRL_ISP_AWB_ENA);
+ }
+
+ /* update bdm config */
+ if (module_cfg_update & RKISP1_CIF_ISP_MODULE_BDM)
+ rkisp1_bdm_config(params,
+ &new_params->others.bdm_config);
+
+ if (module_en_update & RKISP1_CIF_ISP_MODULE_BDM) {
+ if (module_ens & RKISP1_CIF_ISP_MODULE_BDM)
+ rkisp1_param_set_bits(params,
+ RKISP1_CIF_ISP_DEMOSAIC,
+ RKISP1_CIF_ISP_DEMOSAIC_BYPASS);
+ else
+ rkisp1_param_clear_bits(params,
+ RKISP1_CIF_ISP_DEMOSAIC,
+ RKISP1_CIF_ISP_DEMOSAIC_BYPASS);
+ }
+
+ /* update filter config */
+ if (module_cfg_update & RKISP1_CIF_ISP_MODULE_FLT)
+ rkisp1_flt_config(params,
+ &new_params->others.flt_config);
+
+ if (module_en_update & RKISP1_CIF_ISP_MODULE_FLT) {
+ if (module_ens & RKISP1_CIF_ISP_MODULE_FLT)
+ rkisp1_param_set_bits(params,
+ RKISP1_CIF_ISP_FILT_MODE,
+ RKISP1_CIF_ISP_FLT_ENA);
+ else
+ rkisp1_param_clear_bits(params,
+ RKISP1_CIF_ISP_FILT_MODE,
+ RKISP1_CIF_ISP_FLT_ENA);
+ }
+
+ /* update ctk config */
+ if (module_cfg_update & RKISP1_CIF_ISP_MODULE_CTK)
+ rkisp1_ctk_config(params,
+ &new_params->others.ctk_config);
+
+ if (module_en_update & RKISP1_CIF_ISP_MODULE_CTK)
+ rkisp1_ctk_enable(params, !!(module_ens & RKISP1_CIF_ISP_MODULE_CTK));
+
+ /* update goc config */
+ if (module_cfg_update & RKISP1_CIF_ISP_MODULE_GOC)
+ params->ops->goc_config(params, &new_params->others.goc_config);
+
+ if (module_en_update & RKISP1_CIF_ISP_MODULE_GOC) {
+ if (module_ens & RKISP1_CIF_ISP_MODULE_GOC)
+ rkisp1_param_set_bits(params,
+ RKISP1_CIF_ISP_CTRL,
+ RKISP1_CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA);
+ else
+ rkisp1_param_clear_bits(params,
+ RKISP1_CIF_ISP_CTRL,
+ RKISP1_CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA);
+ }
+
+ /* update cproc config */
+ if (module_cfg_update & RKISP1_CIF_ISP_MODULE_CPROC)
+ rkisp1_cproc_config(params,
+ &new_params->others.cproc_config);
+
+ if (module_en_update & RKISP1_CIF_ISP_MODULE_CPROC) {
+ if (module_ens & RKISP1_CIF_ISP_MODULE_CPROC)
+ rkisp1_param_set_bits(params,
+ RKISP1_CIF_C_PROC_CTRL,
+ RKISP1_CIF_C_PROC_CTR_ENABLE);
+ else
+ rkisp1_param_clear_bits(params,
+ RKISP1_CIF_C_PROC_CTRL,
+ RKISP1_CIF_C_PROC_CTR_ENABLE);
+ }
+
+ /* update ie config */
+ if (module_cfg_update & RKISP1_CIF_ISP_MODULE_IE)
+ rkisp1_ie_config(params, &new_params->others.ie_config);
+
+ if (module_en_update & RKISP1_CIF_ISP_MODULE_IE)
+ rkisp1_ie_enable(params, !!(module_ens & RKISP1_CIF_ISP_MODULE_IE));
+
+ /* update dpf config */
+ if (module_cfg_update & RKISP1_CIF_ISP_MODULE_DPF)
+ rkisp1_dpf_config(params, &new_params->others.dpf_config);
+
+ if (module_en_update & RKISP1_CIF_ISP_MODULE_DPF) {
+ if (module_ens & RKISP1_CIF_ISP_MODULE_DPF)
+ rkisp1_param_set_bits(params,
+ RKISP1_CIF_ISP_DPF_MODE,
+ RKISP1_CIF_ISP_DPF_MODE_EN);
+ else
+ rkisp1_param_clear_bits(params,
+ RKISP1_CIF_ISP_DPF_MODE,
+ RKISP1_CIF_ISP_DPF_MODE_EN);
+ }
+
+ if ((module_en_update & RKISP1_CIF_ISP_MODULE_DPF_STRENGTH) ||
+ (module_cfg_update & RKISP1_CIF_ISP_MODULE_DPF_STRENGTH)) {
+ /* update dpf strength config */
+ rkisp1_dpf_strength_config(params,
+ &new_params->others.dpf_strength_config);
+ }
+}
+
+static void
+rkisp1_isp_isr_lsc_config(struct rkisp1_params *params,
+ const struct rkisp1_params_cfg *new_params)
+{
+ unsigned int module_en_update, module_cfg_update, module_ens;
+
+ module_en_update = new_params->module_en_update;
+ module_cfg_update = new_params->module_cfg_update;
+ module_ens = new_params->module_ens;
+
+ /* update lsc config */
+ if (module_cfg_update & RKISP1_CIF_ISP_MODULE_LSC)
+ rkisp1_lsc_config(params,
+ &new_params->others.lsc_config);
+
+ if (module_en_update & RKISP1_CIF_ISP_MODULE_LSC) {
+ if (module_ens & RKISP1_CIF_ISP_MODULE_LSC)
+ rkisp1_param_set_bits(params,
+ RKISP1_CIF_ISP_LSC_CTRL,
+ RKISP1_CIF_ISP_LSC_CTRL_ENA);
+ else
+ rkisp1_param_clear_bits(params,
+ RKISP1_CIF_ISP_LSC_CTRL,
+ RKISP1_CIF_ISP_LSC_CTRL_ENA);
+ }
+}
+
+static void rkisp1_isp_isr_meas_config(struct rkisp1_params *params,
+ struct rkisp1_params_cfg *new_params)
+{
+ unsigned int module_en_update, module_cfg_update, module_ens;
+
+ module_en_update = new_params->module_en_update;
+ module_cfg_update = new_params->module_cfg_update;
+ module_ens = new_params->module_ens;
+
+ /* update awb config */
+ if (module_cfg_update & RKISP1_CIF_ISP_MODULE_AWB)
+ params->ops->awb_meas_config(params, &new_params->meas.awb_meas_config);
+
+ if (module_en_update & RKISP1_CIF_ISP_MODULE_AWB)
+ params->ops->awb_meas_enable(params,
+ &new_params->meas.awb_meas_config,
+ !!(module_ens & RKISP1_CIF_ISP_MODULE_AWB));
+
+ /* update afc config */
+ if (module_cfg_update & RKISP1_CIF_ISP_MODULE_AFC)
+ params->ops->afm_config(params,
+ &new_params->meas.afc_config);
+
+ if (module_en_update & RKISP1_CIF_ISP_MODULE_AFC) {
+ if (module_ens & RKISP1_CIF_ISP_MODULE_AFC)
+ rkisp1_param_set_bits(params,
+ RKISP1_CIF_ISP_AFM_CTRL,
+ RKISP1_CIF_ISP_AFM_ENA);
+ else
+ rkisp1_param_clear_bits(params,
+ RKISP1_CIF_ISP_AFM_CTRL,
+ RKISP1_CIF_ISP_AFM_ENA);
+ }
+
+ /* update hst config */
+ if (module_cfg_update & RKISP1_CIF_ISP_MODULE_HST)
+ params->ops->hst_config(params,
+ &new_params->meas.hst_config);
+
+ if (module_en_update & RKISP1_CIF_ISP_MODULE_HST)
+ params->ops->hst_enable(params,
+ &new_params->meas.hst_config,
+ !!(module_ens & RKISP1_CIF_ISP_MODULE_HST));
+
+ /* update aec config */
+ if (module_cfg_update & RKISP1_CIF_ISP_MODULE_AEC)
+ params->ops->aec_config(params,
+ &new_params->meas.aec_config);
+
+ if (module_en_update & RKISP1_CIF_ISP_MODULE_AEC) {
+ if (module_ens & RKISP1_CIF_ISP_MODULE_AEC)
+ rkisp1_param_set_bits(params,
+ RKISP1_CIF_ISP_EXP_CTRL,
+ RKISP1_CIF_ISP_EXP_ENA);
+ else
+ rkisp1_param_clear_bits(params,
+ RKISP1_CIF_ISP_EXP_CTRL,
+ RKISP1_CIF_ISP_EXP_ENA);
+ }
+}
+
+static bool rkisp1_params_get_buffer(struct rkisp1_params *params,
+ struct rkisp1_buffer **buf,
+ struct rkisp1_params_cfg **cfg)
+{
+ if (list_empty(&params->params))
+ return false;
+
+ *buf = list_first_entry(&params->params, struct rkisp1_buffer, queue);
+ *cfg = vb2_plane_vaddr(&(*buf)->vb.vb2_buf, 0);
+
+ return true;
+}
+
+static void rkisp1_params_complete_buffer(struct rkisp1_params *params,
+ struct rkisp1_buffer *buf,
+ unsigned int frame_sequence)
+{
+ list_del(&buf->queue);
+
+ buf->vb.sequence = frame_sequence;
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+}
+
+void rkisp1_params_isr(struct rkisp1_device *rkisp1)
+{
+ struct rkisp1_params *params = &rkisp1->params;
+ struct rkisp1_params_cfg *new_params;
+ struct rkisp1_buffer *cur_buf;
+
+ spin_lock(&params->config_lock);
+
+ if (!rkisp1_params_get_buffer(params, &cur_buf, &new_params))
+ goto unlock;
+
+ rkisp1_isp_isr_other_config(params, new_params);
+ rkisp1_isp_isr_lsc_config(params, new_params);
+ rkisp1_isp_isr_meas_config(params, new_params);
+
+ /* update shadow register immediately */
+ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL,
+ RKISP1_CIF_ISP_CTRL_ISP_CFG_UPD);
+
+ /*
+ * This isr is called when the ISR finishes processing a frame
+ * (RKISP1_CIF_ISP_FRAME). Configurations performed here will be
+ * applied on the next frame. Since frame_sequence is updated on the
+ * vertical sync signal, we should use frame_sequence + 1 here to
+ * indicate to userspace on which frame these parameters are being
+ * applied.
+ */
+ rkisp1_params_complete_buffer(params, cur_buf,
+ rkisp1->isp.frame_sequence + 1);
+
+unlock:
+ spin_unlock(&params->config_lock);
+}
+
+static const struct rkisp1_cif_isp_awb_meas_config rkisp1_awb_params_default_config = {
+ {
+ 0, 0, RKISP1_DEFAULT_WIDTH, RKISP1_DEFAULT_HEIGHT
+ },
+ RKISP1_CIF_ISP_AWB_MODE_YCBCR, 200, 30, 20, 20, 0, 128, 128
+};
+
+static const struct rkisp1_cif_isp_aec_config rkisp1_aec_params_default_config = {
+ RKISP1_CIF_ISP_EXP_MEASURING_MODE_0,
+ RKISP1_CIF_ISP_EXP_CTRL_AUTOSTOP_0,
+ {
+ RKISP1_DEFAULT_WIDTH >> 2, RKISP1_DEFAULT_HEIGHT >> 2,
+ RKISP1_DEFAULT_WIDTH >> 1, RKISP1_DEFAULT_HEIGHT >> 1
+ }
+};
+
+static const struct rkisp1_cif_isp_hst_config rkisp1_hst_params_default_config = {
+ RKISP1_CIF_ISP_HISTOGRAM_MODE_RGB_COMBINED,
+ 3,
+ {
+ RKISP1_DEFAULT_WIDTH >> 2, RKISP1_DEFAULT_HEIGHT >> 2,
+ RKISP1_DEFAULT_WIDTH >> 1, RKISP1_DEFAULT_HEIGHT >> 1
+ },
+ {
+ 0, /* To be filled in with 0x01 at runtime. */
+ }
+};
+
+static const struct rkisp1_cif_isp_afc_config rkisp1_afc_params_default_config = {
+ 1,
+ {
+ {
+ 300, 225, 200, 150
+ }
+ },
+ 4,
+ 14
+};
+
+void rkisp1_params_pre_configure(struct rkisp1_params *params,
+ enum rkisp1_fmt_raw_pat_type bayer_pat,
+ enum v4l2_quantization quantization,
+ enum v4l2_ycbcr_encoding ycbcr_encoding)
+{
+ struct rkisp1_cif_isp_hst_config hst = rkisp1_hst_params_default_config;
+ struct rkisp1_params_cfg *new_params;
+ struct rkisp1_buffer *cur_buf;
+
+ params->quantization = quantization;
+ params->ycbcr_encoding = ycbcr_encoding;
+ params->raw_type = bayer_pat;
+
+ params->ops->awb_meas_config(params, &rkisp1_awb_params_default_config);
+ params->ops->awb_meas_enable(params, &rkisp1_awb_params_default_config,
+ true);
+
+ params->ops->aec_config(params, &rkisp1_aec_params_default_config);
+ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_EXP_CTRL,
+ RKISP1_CIF_ISP_EXP_ENA);
+
+ params->ops->afm_config(params, &rkisp1_afc_params_default_config);
+ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_AFM_CTRL,
+ RKISP1_CIF_ISP_AFM_ENA);
+
+ memset(hst.hist_weight, 0x01, sizeof(hst.hist_weight));
+ params->ops->hst_config(params, &hst);
+ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_HIST_PROP_V10,
+ rkisp1_hst_params_default_config.mode);
+
+ rkisp1_csm_config(params);
+
+ spin_lock_irq(&params->config_lock);
+
+ /* apply the first buffer if there is one already */
+
+ if (!rkisp1_params_get_buffer(params, &cur_buf, &new_params))
+ goto unlock;
+
+ rkisp1_isp_isr_other_config(params, new_params);
+ rkisp1_isp_isr_meas_config(params, new_params);
+
+ /* update shadow register immediately */
+ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL,
+ RKISP1_CIF_ISP_CTRL_ISP_CFG_UPD);
+
+unlock:
+ spin_unlock_irq(&params->config_lock);
+}
+
+void rkisp1_params_post_configure(struct rkisp1_params *params)
+{
+ struct rkisp1_params_cfg *new_params;
+ struct rkisp1_buffer *cur_buf;
+
+ spin_lock_irq(&params->config_lock);
+
+ /*
+ * Apply LSC parameters from the first buffer (if any is already
+ * available. This must be done after the ISP gets started in the
+ * ISP8000Nano v18.02 (found in the i.MX8MP) as access to the LSC RAM
+ * is gated by the ISP_CTRL.ISP_ENABLE bit. As this initialization
+ * ordering doesn't affect other ISP versions negatively, do so
+ * unconditionally.
+ */
+
+ if (!rkisp1_params_get_buffer(params, &cur_buf, &new_params))
+ goto unlock;
+
+ rkisp1_isp_isr_lsc_config(params, new_params);
+
+ /* update shadow register immediately */
+ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL,
+ RKISP1_CIF_ISP_CTRL_ISP_CFG_UPD);
+
+ rkisp1_params_complete_buffer(params, cur_buf, 0);
+
+unlock:
+ spin_unlock_irq(&params->config_lock);
+}
+
+/*
+ * Not called when the camera is active, therefore there is no need to acquire
+ * a lock.
+ */
+void rkisp1_params_disable(struct rkisp1_params *params)
+{
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_DPCC_MODE,
+ RKISP1_CIF_ISP_DPCC_MODE_DPCC_ENABLE);
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_LSC_CTRL,
+ RKISP1_CIF_ISP_LSC_CTRL_ENA);
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_BLS_CTRL,
+ RKISP1_CIF_ISP_BLS_ENA);
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CTRL,
+ RKISP1_CIF_ISP_CTRL_ISP_GAMMA_IN_ENA);
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CTRL,
+ RKISP1_CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA);
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_DEMOSAIC,
+ RKISP1_CIF_ISP_DEMOSAIC_BYPASS);
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_FILT_MODE,
+ RKISP1_CIF_ISP_FLT_ENA);
+ params->ops->awb_meas_enable(params, NULL, false);
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CTRL,
+ RKISP1_CIF_ISP_CTRL_ISP_AWB_ENA);
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_EXP_CTRL,
+ RKISP1_CIF_ISP_EXP_ENA);
+ rkisp1_ctk_enable(params, false);
+ rkisp1_param_clear_bits(params, RKISP1_CIF_C_PROC_CTRL,
+ RKISP1_CIF_C_PROC_CTR_ENABLE);
+ params->ops->hst_enable(params, NULL, false);
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_AFM_CTRL,
+ RKISP1_CIF_ISP_AFM_ENA);
+ rkisp1_ie_enable(params, false);
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_DPF_MODE,
+ RKISP1_CIF_ISP_DPF_MODE_EN);
+}
+
+static const struct rkisp1_params_ops rkisp1_v10_params_ops = {
+ .lsc_matrix_config = rkisp1_lsc_matrix_config_v10,
+ .goc_config = rkisp1_goc_config_v10,
+ .awb_meas_config = rkisp1_awb_meas_config_v10,
+ .awb_meas_enable = rkisp1_awb_meas_enable_v10,
+ .awb_gain_config = rkisp1_awb_gain_config_v10,
+ .aec_config = rkisp1_aec_config_v10,
+ .hst_config = rkisp1_hst_config_v10,
+ .hst_enable = rkisp1_hst_enable_v10,
+ .afm_config = rkisp1_afm_config_v10,
+};
+
+static struct rkisp1_params_ops rkisp1_v12_params_ops = {
+ .lsc_matrix_config = rkisp1_lsc_matrix_config_v12,
+ .goc_config = rkisp1_goc_config_v12,
+ .awb_meas_config = rkisp1_awb_meas_config_v12,
+ .awb_meas_enable = rkisp1_awb_meas_enable_v12,
+ .awb_gain_config = rkisp1_awb_gain_config_v12,
+ .aec_config = rkisp1_aec_config_v12,
+ .hst_config = rkisp1_hst_config_v12,
+ .hst_enable = rkisp1_hst_enable_v12,
+ .afm_config = rkisp1_afm_config_v12,
+};
+
+static int rkisp1_params_enum_fmt_meta_out(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct video_device *video = video_devdata(file);
+ struct rkisp1_params *params = video_get_drvdata(video);
+
+ if (f->index > 0 || f->type != video->queue->type)
+ return -EINVAL;
+
+ f->pixelformat = params->vdev_fmt.fmt.meta.dataformat;
+
+ return 0;
+}
+
+static int rkisp1_params_g_fmt_meta_out(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct video_device *video = video_devdata(file);
+ struct rkisp1_params *params = video_get_drvdata(video);
+ struct v4l2_meta_format *meta = &f->fmt.meta;
+
+ if (f->type != video->queue->type)
+ return -EINVAL;
+
+ memset(meta, 0, sizeof(*meta));
+ meta->dataformat = params->vdev_fmt.fmt.meta.dataformat;
+ meta->buffersize = params->vdev_fmt.fmt.meta.buffersize;
+
+ return 0;
+}
+
+static int rkisp1_params_querycap(struct file *file,
+ void *priv, struct v4l2_capability *cap)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ strscpy(cap->driver, RKISP1_DRIVER_NAME, sizeof(cap->driver));
+ strscpy(cap->card, vdev->name, sizeof(cap->card));
+ strscpy(cap->bus_info, RKISP1_BUS_INFO, sizeof(cap->bus_info));
+
+ return 0;
+}
+
+/* ISP params video device IOCTLs */
+static const struct v4l2_ioctl_ops rkisp1_params_ioctl = {
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_enum_fmt_meta_out = rkisp1_params_enum_fmt_meta_out,
+ .vidioc_g_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
+ .vidioc_s_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
+ .vidioc_try_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
+ .vidioc_querycap = rkisp1_params_querycap,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static int rkisp1_params_vb2_queue_setup(struct vb2_queue *vq,
+ unsigned int *num_buffers,
+ unsigned int *num_planes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ *num_buffers = clamp_t(u32, *num_buffers,
+ RKISP1_ISP_PARAMS_REQ_BUFS_MIN,
+ RKISP1_ISP_PARAMS_REQ_BUFS_MAX);
+
+ *num_planes = 1;
+
+ sizes[0] = sizeof(struct rkisp1_params_cfg);
+
+ return 0;
+}
+
+static void rkisp1_params_vb2_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct rkisp1_buffer *params_buf =
+ container_of(vbuf, struct rkisp1_buffer, vb);
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct rkisp1_params *params = vq->drv_priv;
+
+ spin_lock_irq(&params->config_lock);
+ list_add_tail(&params_buf->queue, &params->params);
+ spin_unlock_irq(&params->config_lock);
+}
+
+static int rkisp1_params_vb2_buf_prepare(struct vb2_buffer *vb)
+{
+ if (vb2_plane_size(vb, 0) < sizeof(struct rkisp1_params_cfg))
+ return -EINVAL;
+
+ vb2_set_plane_payload(vb, 0, sizeof(struct rkisp1_params_cfg));
+
+ return 0;
+}
+
+static void rkisp1_params_vb2_stop_streaming(struct vb2_queue *vq)
+{
+ struct rkisp1_params *params = vq->drv_priv;
+ struct rkisp1_buffer *buf;
+ LIST_HEAD(tmp_list);
+
+ /*
+ * we first move the buffers into a local list 'tmp_list'
+ * and then we can iterate it and call vb2_buffer_done
+ * without holding the lock
+ */
+ spin_lock_irq(&params->config_lock);
+ list_splice_init(&params->params, &tmp_list);
+ spin_unlock_irq(&params->config_lock);
+
+ list_for_each_entry(buf, &tmp_list, queue)
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+}
+
+static const struct vb2_ops rkisp1_params_vb2_ops = {
+ .queue_setup = rkisp1_params_vb2_queue_setup,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .buf_queue = rkisp1_params_vb2_buf_queue,
+ .buf_prepare = rkisp1_params_vb2_buf_prepare,
+ .stop_streaming = rkisp1_params_vb2_stop_streaming,
+
+};
+
+static const struct v4l2_file_operations rkisp1_params_fops = {
+ .mmap = vb2_fop_mmap,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = vb2_fop_poll,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release
+};
+
+static int rkisp1_params_init_vb2_queue(struct vb2_queue *q,
+ struct rkisp1_params *params)
+{
+ struct rkisp1_vdev_node *node;
+
+ node = container_of(q, struct rkisp1_vdev_node, buf_queue);
+
+ q->type = V4L2_BUF_TYPE_META_OUTPUT;
+ q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
+ q->drv_priv = params;
+ q->ops = &rkisp1_params_vb2_ops;
+ q->mem_ops = &vb2_vmalloc_memops;
+ q->buf_struct_size = sizeof(struct rkisp1_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &node->vlock;
+
+ return vb2_queue_init(q);
+}
+
+static void rkisp1_init_params(struct rkisp1_params *params)
+{
+ params->vdev_fmt.fmt.meta.dataformat =
+ V4L2_META_FMT_RK_ISP1_PARAMS;
+ params->vdev_fmt.fmt.meta.buffersize =
+ sizeof(struct rkisp1_params_cfg);
+
+ if (params->rkisp1->info->isp_ver == RKISP1_V12)
+ params->ops = &rkisp1_v12_params_ops;
+ else
+ params->ops = &rkisp1_v10_params_ops;
+}
+
+int rkisp1_params_register(struct rkisp1_device *rkisp1)
+{
+ struct rkisp1_params *params = &rkisp1->params;
+ struct rkisp1_vdev_node *node = &params->vnode;
+ struct video_device *vdev = &node->vdev;
+ int ret;
+
+ params->rkisp1 = rkisp1;
+ mutex_init(&node->vlock);
+ INIT_LIST_HEAD(&params->params);
+ spin_lock_init(&params->config_lock);
+
+ strscpy(vdev->name, RKISP1_PARAMS_DEV_NAME, sizeof(vdev->name));
+
+ video_set_drvdata(vdev, params);
+ vdev->ioctl_ops = &rkisp1_params_ioctl;
+ vdev->fops = &rkisp1_params_fops;
+ vdev->release = video_device_release_empty;
+ /*
+ * Provide a mutex to v4l2 core. It will be used
+ * to protect all fops and v4l2 ioctls.
+ */
+ vdev->lock = &node->vlock;
+ vdev->v4l2_dev = &rkisp1->v4l2_dev;
+ vdev->queue = &node->buf_queue;
+ vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_META_OUTPUT;
+ vdev->vfl_dir = VFL_DIR_TX;
+ rkisp1_params_init_vb2_queue(vdev->queue, params);
+ rkisp1_init_params(params);
+ video_set_drvdata(vdev, params);
+
+ node->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&vdev->entity, 1, &node->pad);
+ if (ret)
+ goto error;
+
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret) {
+ dev_err(rkisp1->dev,
+ "failed to register %s, ret=%d\n", vdev->name, ret);
+ goto error;
+ }
+
+ return 0;
+
+error:
+ media_entity_cleanup(&vdev->entity);
+ mutex_destroy(&node->vlock);
+ return ret;
+}
+
+void rkisp1_params_unregister(struct rkisp1_device *rkisp1)
+{
+ struct rkisp1_params *params = &rkisp1->params;
+ struct rkisp1_vdev_node *node = &params->vnode;
+ struct video_device *vdev = &node->vdev;
+
+ if (!video_is_registered(vdev))
+ return;
+
+ vb2_video_unregister_device(vdev);
+ media_entity_cleanup(&vdev->entity);
+ mutex_destroy(&node->vlock);
+}
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h
new file mode 100644
index 0000000000..421cc73355
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h
@@ -0,0 +1,1367 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
+/*
+ * Rockchip ISP1 Driver - Registers header
+ *
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ */
+
+#ifndef _RKISP1_REGS_H
+#define _RKISP1_REGS_H
+
+/* ISP_CTRL */
+#define RKISP1_CIF_ISP_CTRL_ISP_ENABLE BIT(0)
+#define RKISP1_CIF_ISP_CTRL_ISP_MODE_RAW_PICT (0 << 1)
+#define RKISP1_CIF_ISP_CTRL_ISP_MODE_ITU656 (1 << 1)
+#define RKISP1_CIF_ISP_CTRL_ISP_MODE_ITU601 (2 << 1)
+#define RKISP1_CIF_ISP_CTRL_ISP_MODE_BAYER_ITU601 (3 << 1)
+#define RKISP1_CIF_ISP_CTRL_ISP_MODE_DATA_MODE (4 << 1)
+#define RKISP1_CIF_ISP_CTRL_ISP_MODE_BAYER_ITU656 (5 << 1)
+#define RKISP1_CIF_ISP_CTRL_ISP_MODE_RAW_PICT_ITU656 (6 << 1)
+#define RKISP1_CIF_ISP_CTRL_ISP_INFORM_ENABLE BIT(4)
+#define RKISP1_CIF_ISP_CTRL_ISP_GAMMA_IN_ENA BIT(6)
+#define RKISP1_CIF_ISP_CTRL_ISP_AWB_ENA BIT(7)
+#define RKISP1_CIF_ISP_CTRL_ISP_CFG_UPD_PERMANENT BIT(8)
+#define RKISP1_CIF_ISP_CTRL_ISP_CFG_UPD BIT(9)
+#define RKISP1_CIF_ISP_CTRL_ISP_GEN_CFG_UPD BIT(10)
+#define RKISP1_CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA BIT(11)
+#define RKISP1_CIF_ISP_CTRL_ISP_FLASH_MODE_ENA BIT(12)
+#define RKISP1_CIF_ISP_CTRL_ISP_CSM_Y_FULL_ENA BIT(13)
+#define RKISP1_CIF_ISP_CTRL_ISP_CSM_C_FULL_ENA BIT(14)
+
+/* ISP_ACQ_PROP */
+#define RKISP1_CIF_ISP_ACQ_PROP_POS_EDGE BIT(0)
+#define RKISP1_CIF_ISP_ACQ_PROP_HSYNC_LOW BIT(1)
+#define RKISP1_CIF_ISP_ACQ_PROP_VSYNC_LOW BIT(2)
+#define RKISP1_CIF_ISP_ACQ_PROP_BAYER_PAT_RGGB (0 << 3)
+#define RKISP1_CIF_ISP_ACQ_PROP_BAYER_PAT_GRBG (1 << 3)
+#define RKISP1_CIF_ISP_ACQ_PROP_BAYER_PAT_GBRG (2 << 3)
+#define RKISP1_CIF_ISP_ACQ_PROP_BAYER_PAT_BGGR (3 << 3)
+#define RKISP1_CIF_ISP_ACQ_PROP_BAYER_PAT(pat) ((pat) << 3)
+#define RKISP1_CIF_ISP_ACQ_PROP_YCBYCR (0 << 7)
+#define RKISP1_CIF_ISP_ACQ_PROP_YCRYCB (1 << 7)
+#define RKISP1_CIF_ISP_ACQ_PROP_CBYCRY (2 << 7)
+#define RKISP1_CIF_ISP_ACQ_PROP_CRYCBY (3 << 7)
+#define RKISP1_CIF_ISP_ACQ_PROP_FIELD_SEL_ALL (0 << 9)
+#define RKISP1_CIF_ISP_ACQ_PROP_FIELD_SEL_EVEN (1 << 9)
+#define RKISP1_CIF_ISP_ACQ_PROP_FIELD_SEL_ODD (2 << 9)
+#define RKISP1_CIF_ISP_ACQ_PROP_IN_SEL_12B (0 << 12)
+#define RKISP1_CIF_ISP_ACQ_PROP_IN_SEL_10B_ZERO (1 << 12)
+#define RKISP1_CIF_ISP_ACQ_PROP_IN_SEL_10B_MSB (2 << 12)
+#define RKISP1_CIF_ISP_ACQ_PROP_IN_SEL_8B_ZERO (3 << 12)
+#define RKISP1_CIF_ISP_ACQ_PROP_IN_SEL_8B_MSB (4 << 12)
+
+/* VI_DPCL */
+#define RKISP1_CIF_VI_DPCL_DMA_JPEG (0 << 0)
+#define RKISP1_CIF_VI_DPCL_MP_MUX_MRSZ_MI (1 << 0)
+#define RKISP1_CIF_VI_DPCL_MP_MUX_MRSZ_JPEG (2 << 0)
+#define RKISP1_CIF_VI_DPCL_CHAN_MODE_MP (1 << 2)
+#define RKISP1_CIF_VI_DPCL_CHAN_MODE_SP (2 << 2)
+#define RKISP1_CIF_VI_DPCL_CHAN_MODE_MPSP (3 << 2)
+#define RKISP1_CIF_VI_DPCL_DMA_SW_SPMUX (0 << 4)
+#define RKISP1_CIF_VI_DPCL_DMA_SW_SI (1 << 4)
+#define RKISP1_CIF_VI_DPCL_DMA_SW_IE (2 << 4)
+#define RKISP1_CIF_VI_DPCL_DMA_SW_JPEG (3 << 4)
+#define RKISP1_CIF_VI_DPCL_DMA_SW_ISP (4 << 4)
+#define RKISP1_CIF_VI_DPCL_IF_SEL_PARALLEL (0 << 8)
+#define RKISP1_CIF_VI_DPCL_IF_SEL_SMIA (1 << 8)
+#define RKISP1_CIF_VI_DPCL_IF_SEL_MIPI (2 << 8)
+#define RKISP1_CIF_VI_DPCL_DMA_IE_MUX_DMA BIT(10)
+#define RKISP1_CIF_VI_DPCL_DMA_SP_MUX_DMA BIT(11)
+
+/* ISP_IMSC - ISP_MIS - ISP_RIS - ISP_ICR - ISP_ISR */
+#define RKISP1_CIF_ISP_OFF BIT(0)
+#define RKISP1_CIF_ISP_FRAME BIT(1)
+#define RKISP1_CIF_ISP_DATA_LOSS BIT(2)
+#define RKISP1_CIF_ISP_PIC_SIZE_ERROR BIT(3)
+#define RKISP1_CIF_ISP_AWB_DONE BIT(4)
+#define RKISP1_CIF_ISP_FRAME_IN BIT(5)
+#define RKISP1_CIF_ISP_V_START BIT(6)
+#define RKISP1_CIF_ISP_H_START BIT(7)
+#define RKISP1_CIF_ISP_FLASH_ON BIT(8)
+#define RKISP1_CIF_ISP_FLASH_OFF BIT(9)
+#define RKISP1_CIF_ISP_SHUTTER_ON BIT(10)
+#define RKISP1_CIF_ISP_SHUTTER_OFF BIT(11)
+#define RKISP1_CIF_ISP_AFM_SUM_OF BIT(12)
+#define RKISP1_CIF_ISP_AFM_LUM_OF BIT(13)
+#define RKISP1_CIF_ISP_AFM_FIN BIT(14)
+#define RKISP1_CIF_ISP_HIST_MEASURE_RDY BIT(15)
+#define RKISP1_CIF_ISP_FLASH_CAP BIT(17)
+#define RKISP1_CIF_ISP_EXP_END BIT(18)
+#define RKISP1_CIF_ISP_VSM_END BIT(19)
+
+/* ISP_ERR */
+#define RKISP1_CIF_ISP_ERR_INFORM_SIZE BIT(0)
+#define RKISP1_CIF_ISP_ERR_IS_SIZE BIT(1)
+#define RKISP1_CIF_ISP_ERR_OUTFORM_SIZE BIT(2)
+
+/* MI_CTRL */
+#define RKISP1_CIF_MI_CTRL_MP_ENABLE BIT(0)
+#define RKISP1_CIF_MI_CTRL_SP_ENABLE (2 << 0)
+#define RKISP1_CIF_MI_CTRL_JPEG_ENABLE (4 << 0)
+#define RKISP1_CIF_MI_CTRL_RAW_ENABLE (8 << 0)
+#define RKISP1_CIF_MI_CTRL_HFLIP BIT(4)
+#define RKISP1_CIF_MI_CTRL_VFLIP BIT(5)
+#define RKISP1_CIF_MI_CTRL_ROT BIT(6)
+#define RKISP1_CIF_MI_BYTE_SWAP BIT(7)
+#define RKISP1_CIF_MI_SP_Y_FULL_YUV2RGB BIT(8)
+#define RKISP1_CIF_MI_SP_CBCR_FULL_YUV2RGB BIT(9)
+#define RKISP1_CIF_MI_SP_422NONCOSITEED BIT(10)
+#define RKISP1_CIF_MI_MP_PINGPONG_ENABLE BIT(11)
+#define RKISP1_CIF_MI_SP_PINGPONG_ENABLE BIT(12)
+#define RKISP1_CIF_MI_MP_AUTOUPDATE_ENABLE BIT(13)
+#define RKISP1_CIF_MI_SP_AUTOUPDATE_ENABLE BIT(14)
+#define RKISP1_CIF_MI_LAST_PIXEL_SIG_ENABLE BIT(15)
+#define RKISP1_CIF_MI_CTRL_BURST_LEN_LUM_16 (0 << 16)
+#define RKISP1_CIF_MI_CTRL_BURST_LEN_LUM_32 (1 << 16)
+#define RKISP1_CIF_MI_CTRL_BURST_LEN_LUM_64 (2 << 16)
+#define RKISP1_CIF_MI_CTRL_BURST_LEN_CHROM_16 (0 << 18)
+#define RKISP1_CIF_MI_CTRL_BURST_LEN_CHROM_32 (1 << 18)
+#define RKISP1_CIF_MI_CTRL_BURST_LEN_CHROM_64 (2 << 18)
+#define RKISP1_CIF_MI_CTRL_INIT_BASE_EN BIT(20)
+#define RKISP1_CIF_MI_CTRL_INIT_OFFSET_EN BIT(21)
+#define RKISP1_MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8 (0 << 22)
+#define RKISP1_MI_CTRL_MP_WRITE_YUV_SPLA (1 << 22)
+#define RKISP1_MI_CTRL_MP_WRITE_YUVINT (2 << 22)
+#define RKISP1_MI_CTRL_MP_WRITE_RAW12 (2 << 22)
+#define RKISP1_MI_CTRL_SP_WRITE_PLA (0 << 24)
+#define RKISP1_MI_CTRL_SP_WRITE_SPLA (1 << 24)
+#define RKISP1_MI_CTRL_SP_WRITE_INT (2 << 24)
+#define RKISP1_MI_CTRL_SP_INPUT_YUV400 (0 << 26)
+#define RKISP1_MI_CTRL_SP_INPUT_YUV420 (1 << 26)
+#define RKISP1_MI_CTRL_SP_INPUT_YUV422 (2 << 26)
+#define RKISP1_MI_CTRL_SP_INPUT_YUV444 (3 << 26)
+#define RKISP1_MI_CTRL_SP_OUTPUT_YUV400 (0 << 28)
+#define RKISP1_MI_CTRL_SP_OUTPUT_YUV420 (1 << 28)
+#define RKISP1_MI_CTRL_SP_OUTPUT_YUV422 (2 << 28)
+#define RKISP1_MI_CTRL_SP_OUTPUT_YUV444 (3 << 28)
+#define RKISP1_MI_CTRL_SP_OUTPUT_RGB565 (4 << 28)
+#define RKISP1_MI_CTRL_SP_OUTPUT_RGB666 (5 << 28)
+#define RKISP1_MI_CTRL_SP_OUTPUT_RGB888 (6 << 28)
+
+#define RKISP1_MI_CTRL_MP_FMT_MASK GENMASK(23, 22)
+#define RKISP1_MI_CTRL_SP_FMT_MASK GENMASK(30, 24)
+
+/* MI_INIT */
+#define RKISP1_CIF_MI_INIT_SKIP BIT(2)
+#define RKISP1_CIF_MI_INIT_SOFT_UPD BIT(4)
+
+/* MI_CTRL_SHD */
+#define RKISP1_CIF_MI_CTRL_SHD_MP_IN_ENABLED BIT(0)
+#define RKISP1_CIF_MI_CTRL_SHD_SP_IN_ENABLED BIT(1)
+#define RKISP1_CIF_MI_CTRL_SHD_JPEG_IN_ENABLED BIT(2)
+#define RKISP1_CIF_MI_CTRL_SHD_RAW_IN_ENABLED BIT(3)
+#define RKISP1_CIF_MI_CTRL_SHD_MP_OUT_ENABLED BIT(16)
+#define RKISP1_CIF_MI_CTRL_SHD_SP_OUT_ENABLED BIT(17)
+#define RKISP1_CIF_MI_CTRL_SHD_JPEG_OUT_ENABLED BIT(18)
+#define RKISP1_CIF_MI_CTRL_SHD_RAW_OUT_ENABLED BIT(19)
+
+/* RSZ_CTRL */
+#define RKISP1_CIF_RSZ_CTRL_SCALE_HY_ENABLE BIT(0)
+#define RKISP1_CIF_RSZ_CTRL_SCALE_HC_ENABLE BIT(1)
+#define RKISP1_CIF_RSZ_CTRL_SCALE_VY_ENABLE BIT(2)
+#define RKISP1_CIF_RSZ_CTRL_SCALE_VC_ENABLE BIT(3)
+#define RKISP1_CIF_RSZ_CTRL_SCALE_HY_UP BIT(4)
+#define RKISP1_CIF_RSZ_CTRL_SCALE_HC_UP BIT(5)
+#define RKISP1_CIF_RSZ_CTRL_SCALE_VY_UP BIT(6)
+#define RKISP1_CIF_RSZ_CTRL_SCALE_VC_UP BIT(7)
+#define RKISP1_CIF_RSZ_CTRL_CFG_UPD BIT(8)
+#define RKISP1_CIF_RSZ_CTRL_CFG_UPD_AUTO BIT(9)
+#define RKISP1_CIF_RSZ_SCALER_FACTOR BIT(16)
+
+/* MI_IMSC - MI_MIS - MI_RIS - MI_ICR - MI_ISR */
+#define RKISP1_CIF_MI_FRAME(stream) BIT((stream)->id)
+#define RKISP1_CIF_MI_MBLK_LINE BIT(2)
+#define RKISP1_CIF_MI_FILL_MP_Y BIT(3)
+#define RKISP1_CIF_MI_WRAP_MP_Y BIT(4)
+#define RKISP1_CIF_MI_WRAP_MP_CB BIT(5)
+#define RKISP1_CIF_MI_WRAP_MP_CR BIT(6)
+#define RKISP1_CIF_MI_WRAP_SP_Y BIT(7)
+#define RKISP1_CIF_MI_WRAP_SP_CB BIT(8)
+#define RKISP1_CIF_MI_WRAP_SP_CR BIT(9)
+#define RKISP1_CIF_MI_DMA_READY BIT(11)
+
+/* MI_STATUS */
+#define RKISP1_CIF_MI_STATUS_MP_Y_FIFO_FULL BIT(0)
+#define RKISP1_CIF_MI_STATUS_SP_Y_FIFO_FULL BIT(4)
+
+/* MI_DMA_CTRL */
+#define RKISP1_CIF_MI_DMA_CTRL_BURST_LEN_LUM_16 (0 << 0)
+#define RKISP1_CIF_MI_DMA_CTRL_BURST_LEN_LUM_32 (1 << 0)
+#define RKISP1_CIF_MI_DMA_CTRL_BURST_LEN_LUM_64 (2 << 0)
+#define RKISP1_CIF_MI_DMA_CTRL_BURST_LEN_CHROM_16 (0 << 2)
+#define RKISP1_CIF_MI_DMA_CTRL_BURST_LEN_CHROM_32 (1 << 2)
+#define RKISP1_CIF_MI_DMA_CTRL_BURST_LEN_CHROM_64 (2 << 2)
+#define RKISP1_CIF_MI_DMA_CTRL_READ_FMT_PLANAR (0 << 4)
+#define RKISP1_CIF_MI_DMA_CTRL_READ_FMT_SPLANAR (1 << 4)
+#define RKISP1_CIF_MI_DMA_CTRL_READ_FMT_PACKED (2 << 4)
+#define RKISP1_CIF_MI_DMA_CTRL_FMT_YUV400 (0 << 6)
+#define RKISP1_CIF_MI_DMA_CTRL_FMT_YUV420 (1 << 6)
+#define RKISP1_CIF_MI_DMA_CTRL_FMT_YUV422 (2 << 6)
+#define RKISP1_CIF_MI_DMA_CTRL_FMT_YUV444 (3 << 6)
+#define RKISP1_CIF_MI_DMA_CTRL_BYTE_SWAP BIT(8)
+#define RKISP1_CIF_MI_DMA_CTRL_CONTINUOUS_ENA BIT(9)
+#define RKISP1_CIF_MI_DMA_CTRL_RGB_BAYER_NO (0 << 12)
+#define RKISP1_CIF_MI_DMA_CTRL_RGB_BAYER_8BIT (1 << 12)
+#define RKISP1_CIF_MI_DMA_CTRL_RGB_BAYER_16BIT (2 << 12)
+/* MI_DMA_START */
+#define RKISP1_CIF_MI_DMA_START_ENABLE BIT(0)
+/* MI_XTD_FORMAT_CTRL */
+#define RKISP1_CIF_MI_XTD_FMT_CTRL_MP_CB_CR_SWAP BIT(0)
+#define RKISP1_CIF_MI_XTD_FMT_CTRL_SP_CB_CR_SWAP BIT(1)
+#define RKISP1_CIF_MI_XTD_FMT_CTRL_DMA_CB_CR_SWAP BIT(2)
+
+/* VI_CCL */
+#define RKISP1_CIF_CCL_CIF_CLK_DIS BIT(2)
+/* VI_ISP_CLK_CTRL */
+#define RKISP1_CIF_CLK_CTRL_ISP_RAW BIT(0)
+#define RKISP1_CIF_CLK_CTRL_ISP_RGB BIT(1)
+#define RKISP1_CIF_CLK_CTRL_ISP_YUV BIT(2)
+#define RKISP1_CIF_CLK_CTRL_ISP_3A BIT(3)
+#define RKISP1_CIF_CLK_CTRL_MIPI_RAW BIT(4)
+#define RKISP1_CIF_CLK_CTRL_ISP_IE BIT(5)
+#define RKISP1_CIF_CLK_CTRL_RSZ_RAM BIT(6)
+#define RKISP1_CIF_CLK_CTRL_JPEG_RAM BIT(7)
+#define RKISP1_CIF_CLK_CTRL_ACLK_ISP BIT(8)
+#define RKISP1_CIF_CLK_CTRL_MI_IDC BIT(9)
+#define RKISP1_CIF_CLK_CTRL_MI_MP BIT(10)
+#define RKISP1_CIF_CLK_CTRL_MI_JPEG BIT(11)
+#define RKISP1_CIF_CLK_CTRL_MI_DP BIT(12)
+#define RKISP1_CIF_CLK_CTRL_MI_Y12 BIT(13)
+#define RKISP1_CIF_CLK_CTRL_MI_SP BIT(14)
+#define RKISP1_CIF_CLK_CTRL_MI_RAW0 BIT(15)
+#define RKISP1_CIF_CLK_CTRL_MI_RAW1 BIT(16)
+#define RKISP1_CIF_CLK_CTRL_MI_READ BIT(17)
+#define RKISP1_CIF_CLK_CTRL_MI_RAWRD BIT(18)
+#define RKISP1_CIF_CLK_CTRL_CP BIT(19)
+#define RKISP1_CIF_CLK_CTRL_IE BIT(20)
+#define RKISP1_CIF_CLK_CTRL_SI BIT(21)
+#define RKISP1_CIF_CLK_CTRL_RSZM BIT(22)
+#define RKISP1_CIF_CLK_CTRL_DPMUX BIT(23)
+#define RKISP1_CIF_CLK_CTRL_JPEG BIT(24)
+#define RKISP1_CIF_CLK_CTRL_RSZS BIT(25)
+#define RKISP1_CIF_CLK_CTRL_MIPI BIT(26)
+#define RKISP1_CIF_CLK_CTRL_MARVINMI BIT(27)
+/* VI_ICCL */
+#define RKISP1_CIF_VI_ICCL_ISP_CLK BIT(0)
+#define RKISP1_CIF_VI_ICCL_CP_CLK BIT(1)
+#define RKISP1_CIF_VI_ICCL_RES_2 BIT(2)
+#define RKISP1_CIF_VI_ICCL_MRSZ_CLK BIT(3)
+#define RKISP1_CIF_VI_ICCL_SRSZ_CLK BIT(4)
+#define RKISP1_CIF_VI_ICCL_JPEG_CLK BIT(5)
+#define RKISP1_CIF_VI_ICCL_MI_CLK BIT(6)
+#define RKISP1_CIF_VI_ICCL_RES_7 BIT(7)
+#define RKISP1_CIF_VI_ICCL_IE_CLK BIT(8)
+#define RKISP1_CIF_VI_ICCL_SIMP_CLK BIT(9)
+#define RKISP1_CIF_VI_ICCL_SMIA_CLK BIT(10)
+#define RKISP1_CIF_VI_ICCL_MIPI_CLK BIT(11)
+#define RKISP1_CIF_VI_ICCL_DCROP_CLK BIT(12)
+/* VI_IRCL */
+#define RKISP1_CIF_VI_IRCL_ISP_SW_RST BIT(0)
+#define RKISP1_CIF_VI_IRCL_CP_SW_RST BIT(1)
+#define RKISP1_CIF_VI_IRCL_YCS_SW_RST BIT(2)
+#define RKISP1_CIF_VI_IRCL_MRSZ_SW_RST BIT(3)
+#define RKISP1_CIF_VI_IRCL_SRSZ_SW_RST BIT(4)
+#define RKISP1_CIF_VI_IRCL_JPEG_SW_RST BIT(5)
+#define RKISP1_CIF_VI_IRCL_MI_SW_RST BIT(6)
+#define RKISP1_CIF_VI_IRCL_CIF_SW_RST BIT(7)
+#define RKISP1_CIF_VI_IRCL_IE_SW_RST BIT(8)
+#define RKISP1_CIF_VI_IRCL_SI_SW_RST BIT(9)
+#define RKISP1_CIF_VI_IRCL_MIPI_SW_RST BIT(11)
+
+/* C_PROC_CTR */
+#define RKISP1_CIF_C_PROC_CTR_ENABLE BIT(0)
+#define RKISP1_CIF_C_PROC_YOUT_FULL BIT(1)
+#define RKISP1_CIF_C_PROC_YIN_FULL BIT(2)
+#define RKISP1_CIF_C_PROC_COUT_FULL BIT(3)
+#define RKISP1_CIF_C_PROC_CTRL_RESERVED 0xFFFFFFFE
+#define RKISP1_CIF_C_PROC_CONTRAST_RESERVED 0xFFFFFF00
+#define RKISP1_CIF_C_PROC_BRIGHTNESS_RESERVED 0xFFFFFF00
+#define RKISP1_CIF_C_PROC_HUE_RESERVED 0xFFFFFF00
+#define RKISP1_CIF_C_PROC_SATURATION_RESERVED 0xFFFFFF00
+#define RKISP1_CIF_C_PROC_MACC_RESERVED 0xE000E000
+#define RKISP1_CIF_C_PROC_TONE_RESERVED 0xF000
+/* DUAL_CROP_CTRL */
+#define RKISP1_CIF_DUAL_CROP_MP_MODE_BYPASS (0 << 0)
+#define RKISP1_CIF_DUAL_CROP_MP_MODE_YUV (1 << 0)
+#define RKISP1_CIF_DUAL_CROP_MP_MODE_RAW (2 << 0)
+#define RKISP1_CIF_DUAL_CROP_SP_MODE_BYPASS (0 << 2)
+#define RKISP1_CIF_DUAL_CROP_SP_MODE_YUV (1 << 2)
+#define RKISP1_CIF_DUAL_CROP_SP_MODE_RAW (2 << 2)
+#define RKISP1_CIF_DUAL_CROP_CFG_UPD_PERMANENT BIT(4)
+#define RKISP1_CIF_DUAL_CROP_CFG_UPD BIT(5)
+#define RKISP1_CIF_DUAL_CROP_GEN_CFG_UPD BIT(6)
+
+/* IMG_EFF_CTRL */
+#define RKISP1_CIF_IMG_EFF_CTRL_ENABLE BIT(0)
+#define RKISP1_CIF_IMG_EFF_CTRL_MODE_BLACKWHITE (0 << 1)
+#define RKISP1_CIF_IMG_EFF_CTRL_MODE_NEGATIVE (1 << 1)
+#define RKISP1_CIF_IMG_EFF_CTRL_MODE_SEPIA (2 << 1)
+#define RKISP1_CIF_IMG_EFF_CTRL_MODE_COLOR_SEL (3 << 1)
+#define RKISP1_CIF_IMG_EFF_CTRL_MODE_EMBOSS (4 << 1)
+#define RKISP1_CIF_IMG_EFF_CTRL_MODE_SKETCH (5 << 1)
+#define RKISP1_CIF_IMG_EFF_CTRL_MODE_SHARPEN (6 << 1)
+#define RKISP1_CIF_IMG_EFF_CTRL_CFG_UPD BIT(4)
+#define RKISP1_CIF_IMG_EFF_CTRL_YCBCR_FULL BIT(5)
+
+#define RKISP1_CIF_IMG_EFF_CTRL_MODE_BLACKWHITE_SHIFT 0
+#define RKISP1_CIF_IMG_EFF_CTRL_MODE_NEGATIVE_SHIFT 1
+#define RKISP1_CIF_IMG_EFF_CTRL_MODE_SEPIA_SHIFT 2
+#define RKISP1_CIF_IMG_EFF_CTRL_MODE_COLOR_SEL_SHIFT 3
+#define RKISP1_CIF_IMG_EFF_CTRL_MODE_EMBOSS_SHIFT 4
+#define RKISP1_CIF_IMG_EFF_CTRL_MODE_SKETCH_SHIFT 5
+#define RKISP1_CIF_IMG_EFF_CTRL_MODE_SHARPEN_SHIFT 6
+#define RKISP1_CIF_IMG_EFF_CTRL_MODE_MASK 0xE
+
+/* IMG_EFF_COLOR_SEL */
+#define RKISP1_CIF_IMG_EFF_COLOR_RGB 0
+#define RKISP1_CIF_IMG_EFF_COLOR_B (1 << 0)
+#define RKISP1_CIF_IMG_EFF_COLOR_G (2 << 0)
+#define RKISP1_CIF_IMG_EFF_COLOR_GB (3 << 0)
+#define RKISP1_CIF_IMG_EFF_COLOR_R (4 << 0)
+#define RKISP1_CIF_IMG_EFF_COLOR_RB (5 << 0)
+#define RKISP1_CIF_IMG_EFF_COLOR_RG (6 << 0)
+#define RKISP1_CIF_IMG_EFF_COLOR_RGB2 (7 << 0)
+
+/* MIPI_CTRL */
+#define RKISP1_CIF_MIPI_CTRL_OUTPUT_ENA BIT(0)
+#define RKISP1_CIF_MIPI_CTRL_SHUTDOWNLANES(a) (((a) & 0xF) << 8)
+#define RKISP1_CIF_MIPI_CTRL_NUM_LANES(a) (((a) & 0x3) << 12)
+#define RKISP1_CIF_MIPI_CTRL_ERR_SOT_HS_SKIP BIT(16)
+#define RKISP1_CIF_MIPI_CTRL_ERR_SOT_SYNC_HS_SKIP BIT(17)
+#define RKISP1_CIF_MIPI_CTRL_CLOCKLANE_ENA BIT(18)
+
+/* MIPI_DATA_SEL */
+#define RKISP1_CIF_MIPI_DATA_SEL_VC(a) (((a) & 0x3) << 6)
+#define RKISP1_CIF_MIPI_DATA_SEL_DT(a) (((a) & 0x3F) << 0)
+
+/* MIPI_IMSC, MIPI_RIS, MIPI_MIS, MIPI_ICR, MIPI_ISR */
+#define RKISP1_CIF_MIPI_SYNC_FIFO_OVFLW(a) (((a) & 0xF) << 0)
+#define RKISP1_CIF_MIPI_ERR_SOT(a) (((a) & 0xF) << 4)
+#define RKISP1_CIF_MIPI_ERR_SOT_SYNC(a) (((a) & 0xF) << 8)
+#define RKISP1_CIF_MIPI_ERR_EOT_SYNC(a) (((a) & 0xF) << 12)
+#define RKISP1_CIF_MIPI_ERR_CTRL(a) (((a) & 0xF) << 16)
+#define RKISP1_CIF_MIPI_ERR_PROTOCOL BIT(20)
+#define RKISP1_CIF_MIPI_ERR_ECC1 BIT(21)
+#define RKISP1_CIF_MIPI_ERR_ECC2 BIT(22)
+#define RKISP1_CIF_MIPI_ERR_CS BIT(23)
+#define RKISP1_CIF_MIPI_FRAME_END BIT(24)
+#define RKISP1_CIF_MIPI_ADD_DATA_OVFLW BIT(25)
+#define RKISP1_CIF_MIPI_ADD_DATA_WATER_MARK BIT(26)
+
+#define RKISP1_CIF_MIPI_ERR_CSI (RKISP1_CIF_MIPI_ERR_PROTOCOL | \
+ RKISP1_CIF_MIPI_ERR_ECC1 | \
+ RKISP1_CIF_MIPI_ERR_ECC2 | \
+ RKISP1_CIF_MIPI_ERR_CS)
+
+#define RKISP1_CIF_MIPI_ERR_DPHY (RKISP1_CIF_MIPI_ERR_SOT(3) | \
+ RKISP1_CIF_MIPI_ERR_SOT_SYNC(3) | \
+ RKISP1_CIF_MIPI_ERR_EOT_SYNC(3) | \
+ RKISP1_CIF_MIPI_ERR_CTRL(3))
+
+/* SUPER_IMPOSE */
+#define RKISP1_CIF_SUPER_IMP_CTRL_NORMAL_MODE BIT(0)
+#define RKISP1_CIF_SUPER_IMP_CTRL_REF_IMG_MEM BIT(1)
+#define RKISP1_CIF_SUPER_IMP_CTRL_TRANSP_DIS BIT(2)
+
+/* ISP HISTOGRAM CALCULATION : ISP_HIST_PROP */
+#define RKISP1_CIF_ISP_HIST_PROP_MODE_DIS_V10 (0 << 0)
+#define RKISP1_CIF_ISP_HIST_PROP_MODE_RGB_V10 (1 << 0)
+#define RKISP1_CIF_ISP_HIST_PROP_MODE_RED_V10 (2 << 0)
+#define RKISP1_CIF_ISP_HIST_PROP_MODE_GREEN_V10 (3 << 0)
+#define RKISP1_CIF_ISP_HIST_PROP_MODE_BLUE_V10 (4 << 0)
+#define RKISP1_CIF_ISP_HIST_PROP_MODE_LUM_V10 (5 << 0)
+#define RKISP1_CIF_ISP_HIST_PROP_MODE_MASK_V10 0x7
+#define RKISP1_CIF_ISP_HIST_PREDIV_SET_V10(x) (((x) & 0x7F) << 3)
+#define RKISP1_CIF_ISP_HIST_WEIGHT_SET_V10(v0, v1, v2, v3) \
+ (((v0) & 0x1F) | (((v1) & 0x1F) << 8) |\
+ (((v2) & 0x1F) << 16) | \
+ (((v3) & 0x1F) << 24))
+
+#define RKISP1_CIF_ISP_HIST_WINDOW_OFFSET_RESERVED_V10 0xFFFFF000
+#define RKISP1_CIF_ISP_HIST_WINDOW_SIZE_RESERVED_V10 0xFFFFF800
+#define RKISP1_CIF_ISP_HIST_WEIGHT_RESERVED_V10 0xE0E0E0E0
+#define RKISP1_CIF_ISP_MAX_HIST_PREDIVIDER_V10 0x0000007F
+#define RKISP1_CIF_ISP_HIST_ROW_NUM_V10 5
+#define RKISP1_CIF_ISP_HIST_COLUMN_NUM_V10 5
+#define RKISP1_CIF_ISP_HIST_GET_BIN_V10(x) ((x) & 0x000FFFFF)
+
+/* ISP HISTOGRAM CALCULATION : CIF_ISP_HIST */
+#define RKISP1_CIF_ISP_HIST_CTRL_EN_SET_V12(x) (((x) & 0x01) << 0)
+#define RKISP1_CIF_ISP_HIST_CTRL_EN_MASK_V12 RKISP1_CIF_ISP_HIST_CTRL_EN_SET_V12(0x01)
+#define RKISP1_CIF_ISP_HIST_CTRL_STEPSIZE_SET_V12(x) (((x) & 0x7F) << 1)
+#define RKISP1_CIF_ISP_HIST_CTRL_MODE_SET_V12(x) (((x) & 0x07) << 8)
+#define RKISP1_CIF_ISP_HIST_CTRL_MODE_MASK_V12 RKISP1_CIF_ISP_HIST_CTRL_MODE_SET_V12(0x07)
+#define RKISP1_CIF_ISP_HIST_CTRL_AUTOSTOP_SET_V12(x) (((x) & 0x01) << 11)
+#define RKISP1_CIF_ISP_HIST_CTRL_WATERLINE_SET_V12(x) (((x) & 0xFFF) << 12)
+#define RKISP1_CIF_ISP_HIST_CTRL_DATASEL_SET_V12(x) (((x) & 0x07) << 24)
+#define RKISP1_CIF_ISP_HIST_CTRL_INTRSEL_SET_V12(x) (((x) & 0x01) << 27)
+#define RKISP1_CIF_ISP_HIST_CTRL_WNDNUM_SET_V12(x) (((x) & 0x03) << 28)
+#define RKISP1_CIF_ISP_HIST_CTRL_DBGEN_SET_V12(x) (((x) & 0x01) << 30)
+#define RKISP1_CIF_ISP_HIST_ROW_NUM_V12 15
+#define RKISP1_CIF_ISP_HIST_COLUMN_NUM_V12 15
+#define RKISP1_CIF_ISP_HIST_WEIGHT_REG_SIZE_V12 \
+ (RKISP1_CIF_ISP_HIST_ROW_NUM_V12 * RKISP1_CIF_ISP_HIST_COLUMN_NUM_V12)
+
+#define RKISP1_CIF_ISP_HIST_WEIGHT_SET_V12(v0, v1, v2, v3) \
+ (((v0) & 0x3F) | (((v1) & 0x3F) << 8) |\
+ (((v2) & 0x3F) << 16) |\
+ (((v3) & 0x3F) << 24))
+
+#define RKISP1_CIF_ISP_HIST_OFFS_SET_V12(v0, v1) \
+ (((v0) & 0x1FFF) | (((v1) & 0x1FFF) << 16))
+#define RKISP1_CIF_ISP_HIST_SIZE_SET_V12(v0, v1) \
+ (((v0) & 0x7FF) | (((v1) & 0x7FF) << 16))
+
+#define RKISP1_CIF_ISP_HIST_GET_BIN0_V12(x) \
+ ((x) & 0xFFFF)
+#define RKISP1_CIF_ISP_HIST_GET_BIN1_V12(x) \
+ (((x) >> 16) & 0xFFFF)
+
+/* AUTO FOCUS MEASUREMENT: ISP_AFM_CTRL */
+#define RKISP1_ISP_AFM_CTRL_ENABLE BIT(0)
+
+/* SHUTTER CONTROL */
+#define RKISP1_CIF_ISP_SH_CTRL_SH_ENA BIT(0)
+#define RKISP1_CIF_ISP_SH_CTRL_REP_EN BIT(1)
+#define RKISP1_CIF_ISP_SH_CTRL_SRC_SH_TRIG BIT(2)
+#define RKISP1_CIF_ISP_SH_CTRL_EDGE_POS BIT(3)
+#define RKISP1_CIF_ISP_SH_CTRL_POL_LOW BIT(4)
+
+/* FLASH MODULE */
+/* ISP_FLASH_CMD */
+#define RKISP1_CIFFLASH_CMD_PRELIGHT_ON BIT(0)
+#define RKISP1_CIFFLASH_CMD_FLASH_ON BIT(1)
+#define RKISP1_CIFFLASH_CMD_PRE_FLASH_ON BIT(2)
+/* ISP_FLASH_CONFIG */
+#define RKISP1_CIFFLASH_CONFIG_PRELIGHT_END BIT(0)
+#define RKISP1_CIFFLASH_CONFIG_VSYNC_POS BIT(1)
+#define RKISP1_CIFFLASH_CONFIG_PRELIGHT_LOW BIT(2)
+#define RKISP1_CIFFLASH_CONFIG_SRC_FL_TRIG BIT(3)
+#define RKISP1_CIFFLASH_CONFIG_DELAY(a) (((a) & 0xF) << 4)
+
+/* Demosaic: ISP_DEMOSAIC */
+#define RKISP1_CIF_ISP_DEMOSAIC_BYPASS BIT(10)
+#define RKISP1_CIF_ISP_DEMOSAIC_TH(x) ((x) & 0xFF)
+
+/* ISP_FLAGS_SHD */
+#define RKISP1_CIF_ISP_FLAGS_SHD_ISP_ENABLE_SHD BIT(0)
+#define RKISP1_CIF_ISP_FLAGS_SHD_ISP_ENABLE_INFORM_SHD BIT(1)
+#define RKISP1_CIF_ISP_FLAGS_SHD_INFORM_FIELD BIT(2)
+#define RKISP1_CIF_ISP_FLAGS_SHD_S_DATA_MASK GENMASK(27, 16)
+#define RKISP1_CIF_ISP_FLAGS_SHD_S_DATA_SHIFT 16
+#define RKISP1_CIF_ISP_FLAGS_SHD_S_VSYNC BIT(30)
+#define RKISP1_CIF_ISP_FLAGS_SHD_S_HSYNC BIT(31)
+
+/* AWB */
+/* ISP_AWB_PROP */
+#define RKISP1_CIF_ISP_AWB_YMAX_CMP_EN BIT(2)
+#define RKISP1_CIF_ISP_AWB_YMAX_READ(x) (((x) >> 2) & 1)
+#define RKISP1_CIF_ISP_AWB_MODE_RGB_EN ((1 << 31) | (0x2 << 0))
+#define RKISP1_CIF_ISP_AWB_MODE_YCBCR_EN ((0 << 31) | (0x2 << 0))
+#define RKISP1_CIF_ISP_AWB_MODE_MASK_NONE 0xFFFFFFFC
+#define RKISP1_CIF_ISP_AWB_MODE_READ(x) ((x) & 3)
+#define RKISP1_CIF_ISP_AWB_SET_FRAMES_V12(x) (((x) & 0x07) << 28)
+#define RKISP1_CIF_ISP_AWB_SET_FRAMES_MASK_V12 RKISP1_CIF_ISP_AWB_SET_FRAMES_V12(0x07)
+/* ISP_AWB_GAIN_RB, ISP_AWB_GAIN_G */
+#define RKISP1_CIF_ISP_AWB_GAIN_R_SET(x) (((x) & 0x3FF) << 16)
+#define RKISP1_CIF_ISP_AWB_GAIN_R_READ(x) (((x) >> 16) & 0x3FF)
+#define RKISP1_CIF_ISP_AWB_GAIN_B_SET(x) ((x) & 0x3FFF)
+#define RKISP1_CIF_ISP_AWB_GAIN_B_READ(x) ((x) & 0x3FFF)
+/* ISP_AWB_REF */
+#define RKISP1_CIF_ISP_AWB_REF_CR_SET(x) (((x) & 0xFF) << 8)
+#define RKISP1_CIF_ISP_AWB_REF_CR_READ(x) (((x) >> 8) & 0xFF)
+#define RKISP1_CIF_ISP_AWB_REF_CB_READ(x) ((x) & 0xFF)
+/* ISP_AWB_THRESH */
+#define RKISP1_CIF_ISP_AWB_MAX_CS_SET(x) (((x) & 0xFF) << 8)
+#define RKISP1_CIF_ISP_AWB_MAX_CS_READ(x) (((x) >> 8) & 0xFF)
+#define RKISP1_CIF_ISP_AWB_MIN_C_READ(x) ((x) & 0xFF)
+#define RKISP1_CIF_ISP_AWB_MIN_Y_SET(x) (((x) & 0xFF) << 16)
+#define RKISP1_CIF_ISP_AWB_MIN_Y_READ(x) (((x) >> 16) & 0xFF)
+#define RKISP1_CIF_ISP_AWB_MAX_Y_SET(x) (((x) & 0xFF) << 24)
+#define RKISP1_CIF_ISP_AWB_MAX_Y_READ(x) (((x) >> 24) & 0xFF)
+/* ISP_AWB_MEAN */
+#define RKISP1_CIF_ISP_AWB_GET_MEAN_CR_R(x) ((x) & 0xFF)
+#define RKISP1_CIF_ISP_AWB_GET_MEAN_CB_B(x) (((x) >> 8) & 0xFF)
+#define RKISP1_CIF_ISP_AWB_GET_MEAN_Y_G(x) (((x) >> 16) & 0xFF)
+/* ISP_AWB_WHITE_CNT */
+#define RKISP1_CIF_ISP_AWB_GET_PIXEL_CNT(x) ((x) & 0x3FFFFFF)
+
+#define RKISP1_CIF_ISP_AWB_GAINS_MAX_VAL 0x000003FF
+#define RKISP1_CIF_ISP_AWB_WINDOW_OFFSET_MAX 0x00000FFF
+#define RKISP1_CIF_ISP_AWB_WINDOW_MAX_SIZE 0x00001FFF
+#define RKISP1_CIF_ISP_AWB_CBCR_MAX_REF 0x000000FF
+#define RKISP1_CIF_ISP_AWB_THRES_MAX_YC 0x000000FF
+
+/* AE */
+/* ISP_EXP_CTRL */
+#define RKISP1_CIF_ISP_EXP_ENA BIT(0)
+#define RKISP1_CIF_ISP_EXP_CTRL_AUTOSTOP BIT(1)
+#define RKISP1_CIF_ISP_EXP_CTRL_WNDNUM_SET_V12(x) (((x) & 0x03) << 2)
+/*
+ *'1' luminance calculation according to Y=(R+G+B) x 0.332 (85/256)
+ *'0' luminance calculation according to Y=16+0.25R+0.5G+0.1094B
+ */
+#define RKISP1_CIF_ISP_EXP_CTRL_MEASMODE_1 BIT(31)
+
+/* ISP_EXP_H_SIZE */
+#define RKISP1_CIF_ISP_EXP_H_SIZE_SET_V10(x) ((x) & 0x7FF)
+#define RKISP1_CIF_ISP_EXP_HEIGHT_MASK_V10 0x000007FF
+#define RKISP1_CIF_ISP_EXP_H_SIZE_SET_V12(x) ((x) & 0x7FF)
+#define RKISP1_CIF_ISP_EXP_HEIGHT_MASK_V12 0x000007FF
+/* ISP_EXP_V_SIZE : vertical size must be a multiple of 2). */
+#define RKISP1_CIF_ISP_EXP_V_SIZE_SET_V10(x) ((x) & 0x7FE)
+#define RKISP1_CIF_ISP_EXP_V_SIZE_SET_V12(x) (((x) & 0x7FE) << 16)
+
+/* ISP_EXP_H_OFFSET */
+#define RKISP1_CIF_ISP_EXP_H_OFFSET_SET_V10(x) ((x) & 0x1FFF)
+#define RKISP1_CIF_ISP_EXP_MAX_HOFFS_V10 2424
+#define RKISP1_CIF_ISP_EXP_H_OFFSET_SET_V12(x) ((x) & 0x1FFF)
+#define RKISP1_CIF_ISP_EXP_MAX_HOFFS_V12 0x1FFF
+/* ISP_EXP_V_OFFSET */
+#define RKISP1_CIF_ISP_EXP_V_OFFSET_SET_V10(x) ((x) & 0x1FFF)
+#define RKISP1_CIF_ISP_EXP_MAX_VOFFS_V10 1806
+#define RKISP1_CIF_ISP_EXP_V_OFFSET_SET_V12(x) (((x) & 0x1FFF) << 16)
+#define RKISP1_CIF_ISP_EXP_MAX_VOFFS_V12 0x1FFF
+
+#define RKISP1_CIF_ISP_EXP_ROW_NUM_V10 5
+#define RKISP1_CIF_ISP_EXP_COLUMN_NUM_V10 5
+#define RKISP1_CIF_ISP_EXP_NUM_LUMA_REGS_V10 \
+ (RKISP1_CIF_ISP_EXP_ROW_NUM_V10 * RKISP1_CIF_ISP_EXP_COLUMN_NUM_V10)
+#define RKISP1_CIF_ISP_EXP_BLOCK_MAX_HSIZE_V10 516
+#define RKISP1_CIF_ISP_EXP_BLOCK_MIN_HSIZE_V10 35
+#define RKISP1_CIF_ISP_EXP_BLOCK_MAX_VSIZE_V10 390
+#define RKISP1_CIF_ISP_EXP_BLOCK_MIN_VSIZE_V10 28
+#define RKISP1_CIF_ISP_EXP_MAX_HSIZE_V10 \
+ (RKISP1_CIF_ISP_EXP_BLOCK_MAX_HSIZE_V10 * RKISP1_CIF_ISP_EXP_COLUMN_NUM_V10 + 1)
+#define RKISP1_CIF_ISP_EXP_MIN_HSIZE_V10 \
+ (RKISP1_CIF_ISP_EXP_BLOCK_MIN_HSIZE_V10 * RKISP1_CIF_ISP_EXP_COLUMN_NUM_V10 + 1)
+#define RKISP1_CIF_ISP_EXP_MAX_VSIZE_V10 \
+ (RKISP1_CIF_ISP_EXP_BLOCK_MAX_VSIZE_V10 * RKISP1_CIF_ISP_EXP_ROW_NUM_V10 + 1)
+#define RKISP1_CIF_ISP_EXP_MIN_VSIZE_V10 \
+ (RKISP1_CIF_ISP_EXP_BLOCK_MIN_VSIZE_V10 * RKISP1_CIF_ISP_EXP_ROW_NUM_V10 + 1)
+
+#define RKISP1_CIF_ISP_EXP_ROW_NUM_V12 15
+#define RKISP1_CIF_ISP_EXP_COLUMN_NUM_V12 15
+#define RKISP1_CIF_ISP_EXP_NUM_LUMA_REGS_V12 \
+ (RKISP1_CIF_ISP_EXP_ROW_NUM_V12 * RKISP1_CIF_ISP_EXP_COLUMN_NUM_V12)
+
+#define RKISP1_CIF_ISP_EXP_BLOCK_MAX_HSIZE_V12 0x7FF
+#define RKISP1_CIF_ISP_EXP_BLOCK_MIN_HSIZE_V12 0xE
+#define RKISP1_CIF_ISP_EXP_BLOCK_MAX_VSIZE_V12 0x7FE
+#define RKISP1_CIF_ISP_EXP_BLOCK_MIN_VSIZE_V12 0xE
+#define RKISP1_CIF_ISP_EXP_MAX_HSIZE_V12 \
+ (RKISP1_CIF_ISP_EXP_BLOCK_MAX_HSIZE_V12 * RKISP1_CIF_ISP_EXP_COLUMN_NUM_V12 + 1)
+#define RKISP1_CIF_ISP_EXP_MIN_HSIZE_V12 \
+ (RKISP1_CIF_ISP_EXP_BLOCK_MIN_HSIZE_V12 * RKISP1_CIF_ISP_EXP_COLUMN_NUM_V12 + 1)
+#define RKISP1_CIF_ISP_EXP_MAX_VSIZE_V12 \
+ (RKISP1_CIF_ISP_EXP_BLOCK_MAX_VSIZE_V12 * RKISP1_CIF_ISP_EXP_ROW_NUM_V12 + 1)
+#define RKISP1_CIF_ISP_EXP_MIN_VSIZE_V12 \
+ (RKISP1_CIF_ISP_EXP_BLOCK_MIN_VSIZE_V12 * RKISP1_CIF_ISP_EXP_ROW_NUM_V12 + 1)
+
+#define RKISP1_CIF_ISP_EXP_GET_MEAN_xy0_V12(x) ((x) & 0xFF)
+#define RKISP1_CIF_ISP_EXP_GET_MEAN_xy1_V12(x) (((x) >> 8) & 0xFF)
+#define RKISP1_CIF_ISP_EXP_GET_MEAN_xy2_V12(x) (((x) >> 16) & 0xFF)
+#define RKISP1_CIF_ISP_EXP_GET_MEAN_xy3_V12(x) (((x) >> 24) & 0xFF)
+
+/* LSC: ISP_LSC_CTRL */
+#define RKISP1_CIF_ISP_LSC_CTRL_ENA BIT(0)
+#define RKISP1_CIF_ISP_LSC_SECT_SIZE_RESERVED 0xFC00FC00
+#define RKISP1_CIF_ISP_LSC_GRAD_RESERVED_V10 0xF000F000
+#define RKISP1_CIF_ISP_LSC_SAMPLE_RESERVED_V10 0xF000F000
+#define RKISP1_CIF_ISP_LSC_GRAD_RESERVED_V12 0xE000E000
+#define RKISP1_CIF_ISP_LSC_SAMPLE_RESERVED_V12 0xE000E000
+#define RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(v0, v1) \
+ (((v0) & 0xFFF) | (((v1) & 0xFFF) << 12))
+#define RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(v0, v1) \
+ (((v0) & 0x1FFF) | (((v1) & 0x1FFF) << 13))
+#define RKISP1_CIF_ISP_LSC_SECT_SIZE(v0, v1) \
+ (((v0) & 0xFFF) | (((v1) & 0xFFF) << 16))
+#define RKISP1_CIF_ISP_LSC_SECT_GRAD(v0, v1) \
+ (((v0) & 0xFFF) | (((v1) & 0xFFF) << 16))
+
+/* LSC: ISP_LSC_TABLE_SEL */
+#define RKISP1_CIF_ISP_LSC_TABLE_0 0
+#define RKISP1_CIF_ISP_LSC_TABLE_1 1
+
+/* LSC: ISP_LSC_STATUS */
+#define RKISP1_CIF_ISP_LSC_ACTIVE_TABLE BIT(1)
+#define RKISP1_CIF_ISP_LSC_TABLE_ADDRESS_0 0
+#define RKISP1_CIF_ISP_LSC_TABLE_ADDRESS_153 153
+
+/* FLT */
+/* ISP_FILT_MODE */
+#define RKISP1_CIF_ISP_FLT_ENA BIT(0)
+
+/*
+ * 0: green filter static mode (active filter factor = FILT_FAC_MID)
+ * 1: dynamic noise reduction/sharpen Default
+ */
+#define RKISP1_CIF_ISP_FLT_MODE_DNR BIT(1)
+#define RKISP1_CIF_ISP_FLT_MODE_MAX 1
+#define RKISP1_CIF_ISP_FLT_CHROMA_V_MODE(x) (((x) & 0x3) << 4)
+#define RKISP1_CIF_ISP_FLT_CHROMA_H_MODE(x) (((x) & 0x3) << 6)
+#define RKISP1_CIF_ISP_FLT_CHROMA_MODE_MAX 3
+#define RKISP1_CIF_ISP_FLT_GREEN_STAGE1(x) (((x) & 0xF) << 8)
+#define RKISP1_CIF_ISP_FLT_GREEN_STAGE1_MAX 8
+#define RKISP1_CIF_ISP_FLT_THREAD_RESERVED 0xFFFFFC00
+#define RKISP1_CIF_ISP_FLT_FAC_RESERVED 0xFFFFFFC0
+#define RKISP1_CIF_ISP_FLT_LUM_WEIGHT_RESERVED 0xFFF80000
+
+#define RKISP1_CIF_ISP_CTK_COEFF_RESERVED 0xFFFFF800
+#define RKISP1_CIF_ISP_XTALK_OFFSET_RESERVED 0xFFFFF000
+
+/* GOC */
+#define RKISP1_CIF_ISP_GAMMA_OUT_MODE_EQU BIT(0)
+#define RKISP1_CIF_ISP_GOC_MODE_MAX 1
+#define RKISP1_CIF_ISP_GOC_RESERVED 0xFFFFF800
+/* ISP_CTRL BIT 11*/
+#define RKISP1_CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA_READ(x) (((x) >> 11) & 1)
+
+/* DPCC */
+#define RKISP1_CIF_ISP_DPCC_MODE_DPCC_ENABLE BIT(0)
+#define RKISP1_CIF_ISP_DPCC_MODE_GRAYSCALE_MODE BIT(1)
+#define RKISP1_CIF_ISP_DPCC_OUTPUT_MODE_MASK GENMASK(3, 0)
+#define RKISP1_CIF_ISP_DPCC_SET_USE_MASK GENMASK(3, 0)
+#define RKISP1_CIF_ISP_DPCC_METHODS_SET_MASK 0x00001f1f
+#define RKISP1_CIF_ISP_DPCC_LINE_THRESH_MASK 0x0000ffff
+#define RKISP1_CIF_ISP_DPCC_LINE_MAD_FAC_MASK 0x00003f3f
+#define RKISP1_CIF_ISP_DPCC_PG_FAC_MASK 0x00003f3f
+#define RKISP1_CIF_ISP_DPCC_RND_THRESH_MASK 0x0000ffff
+#define RKISP1_CIF_ISP_DPCC_RG_FAC_MASK 0x00003f3f
+#define RKISP1_CIF_ISP_DPCC_RO_LIMIT_MASK 0x00000fff
+#define RKISP1_CIF_ISP_DPCC_RND_OFFS_MASK 0x00000fff
+
+/* BLS */
+/* ISP_BLS_CTRL */
+#define RKISP1_CIF_ISP_BLS_ENA BIT(0)
+#define RKISP1_CIF_ISP_BLS_MODE_MEASURED BIT(1)
+#define RKISP1_CIF_ISP_BLS_MODE_FIXED 0
+#define RKISP1_CIF_ISP_BLS_WINDOW_1 (1 << 2)
+#define RKISP1_CIF_ISP_BLS_WINDOW_2 (2 << 2)
+
+/* GAMMA-IN */
+#define RKISP1_CIFISP_DEGAMMA_X_RESERVED \
+ ((1 << 31) | (1 << 27) | (1 << 23) | (1 << 19) |\
+ (1 << 15) | (1 << 11) | (1 << 7) | (1 << 3))
+#define RKISP1_CIFISP_DEGAMMA_Y_RESERVED 0xFFFFF000
+
+/* GAMMA-OUT */
+#define RKISP1_CIF_ISP_GAMMA_VALUE_V12(x, y) \
+ (((x) & 0xFFF) << 16 | ((y) & 0xFFF) << 0)
+
+/* AFM */
+#define RKISP1_CIF_ISP_AFM_ENA BIT(0)
+#define RKISP1_CIF_ISP_AFM_THRES_RESERVED 0xFFFF0000
+#define RKISP1_CIF_ISP_AFM_VAR_SHIFT_RESERVED 0xFFF8FFF8
+#define RKISP1_CIF_ISP_AFM_WINDOW_X_RESERVED 0xE000
+#define RKISP1_CIF_ISP_AFM_WINDOW_Y_RESERVED 0xF000
+#define RKISP1_CIF_ISP_AFM_WINDOW_X_MIN 0x5
+#define RKISP1_CIF_ISP_AFM_WINDOW_Y_MIN 0x2
+#define RKISP1_CIF_ISP_AFM_WINDOW_X(x) (((x) & 0x1FFF) << 16)
+#define RKISP1_CIF_ISP_AFM_WINDOW_Y(x) ((x) & 0x1FFF)
+#define RKISP1_CIF_ISP_AFM_SET_SHIFT_a_V12(x, y) (((x) & 0x7) << 16 | ((y) & 0x7) << 0)
+#define RKISP1_CIF_ISP_AFM_SET_SHIFT_b_V12(x, y) (((x) & 0x7) << 20 | ((y) & 0x7) << 4)
+#define RKISP1_CIF_ISP_AFM_SET_SHIFT_c_V12(x, y) (((x) & 0x7) << 24 | ((y) & 0x7) << 8)
+#define RKISP1_CIF_ISP_AFM_GET_LUM_SHIFT_a_V12(x) (((x) & 0x70000) >> 16)
+#define RKISP1_CIF_ISP_AFM_GET_AFM_SHIFT_a_V12(x) ((x) & 0x7)
+
+/* DPF */
+#define RKISP1_CIF_ISP_DPF_MODE_EN BIT(0)
+#define RKISP1_CIF_ISP_DPF_MODE_B_FLT_DIS BIT(1)
+#define RKISP1_CIF_ISP_DPF_MODE_GB_FLT_DIS BIT(2)
+#define RKISP1_CIF_ISP_DPF_MODE_GR_FLT_DIS BIT(3)
+#define RKISP1_CIF_ISP_DPF_MODE_R_FLT_DIS BIT(4)
+#define RKISP1_CIF_ISP_DPF_MODE_RB_FLTSIZE_9x9 BIT(5)
+#define RKISP1_CIF_ISP_DPF_MODE_NLL_SEGMENTATION BIT(6)
+#define RKISP1_CIF_ISP_DPF_MODE_AWB_GAIN_COMP BIT(7)
+#define RKISP1_CIF_ISP_DPF_MODE_LSC_GAIN_COMP BIT(8)
+#define RKISP1_CIF_ISP_DPF_MODE_USE_NF_GAIN BIT(9)
+#define RKISP1_CIF_ISP_DPF_NF_GAIN_RESERVED 0xFFFFF000
+#define RKISP1_CIF_ISP_DPF_SPATIAL_COEFF_MAX 0x1F
+#define RKISP1_CIF_ISP_DPF_NLL_COEFF_N_MAX 0x3FF
+
+/* =================================================================== */
+/* CIF Registers */
+/* =================================================================== */
+#define RKISP1_CIF_CTRL_BASE 0x00000000
+#define RKISP1_CIF_VI_CCL (RKISP1_CIF_CTRL_BASE + 0x00000000)
+#define RKISP1_CIF_VI_ID (RKISP1_CIF_CTRL_BASE + 0x00000008)
+#define RKISP1_CIF_VI_ISP_CLK_CTRL_V12 (RKISP1_CIF_CTRL_BASE + 0x0000000C)
+#define RKISP1_CIF_VI_ICCL (RKISP1_CIF_CTRL_BASE + 0x00000010)
+#define RKISP1_CIF_VI_IRCL (RKISP1_CIF_CTRL_BASE + 0x00000014)
+#define RKISP1_CIF_VI_DPCL (RKISP1_CIF_CTRL_BASE + 0x00000018)
+
+#define RKISP1_CIF_IMG_EFF_BASE 0x00000200
+#define RKISP1_CIF_IMG_EFF_CTRL (RKISP1_CIF_IMG_EFF_BASE + 0x00000000)
+#define RKISP1_CIF_IMG_EFF_COLOR_SEL (RKISP1_CIF_IMG_EFF_BASE + 0x00000004)
+#define RKISP1_CIF_IMG_EFF_MAT_1 (RKISP1_CIF_IMG_EFF_BASE + 0x00000008)
+#define RKISP1_CIF_IMG_EFF_MAT_2 (RKISP1_CIF_IMG_EFF_BASE + 0x0000000C)
+#define RKISP1_CIF_IMG_EFF_MAT_3 (RKISP1_CIF_IMG_EFF_BASE + 0x00000010)
+#define RKISP1_CIF_IMG_EFF_MAT_4 (RKISP1_CIF_IMG_EFF_BASE + 0x00000014)
+#define RKISP1_CIF_IMG_EFF_MAT_5 (RKISP1_CIF_IMG_EFF_BASE + 0x00000018)
+#define RKISP1_CIF_IMG_EFF_TINT (RKISP1_CIF_IMG_EFF_BASE + 0x0000001C)
+#define RKISP1_CIF_IMG_EFF_CTRL_SHD (RKISP1_CIF_IMG_EFF_BASE + 0x00000020)
+#define RKISP1_CIF_IMG_EFF_SHARPEN (RKISP1_CIF_IMG_EFF_BASE + 0x00000024)
+
+#define RKISP1_CIF_SUPER_IMP_BASE 0x00000300
+#define RKISP1_CIF_SUPER_IMP_CTRL (RKISP1_CIF_SUPER_IMP_BASE + 0x00000000)
+#define RKISP1_CIF_SUPER_IMP_OFFSET_X (RKISP1_CIF_SUPER_IMP_BASE + 0x00000004)
+#define RKISP1_CIF_SUPER_IMP_OFFSET_Y (RKISP1_CIF_SUPER_IMP_BASE + 0x00000008)
+#define RKISP1_CIF_SUPER_IMP_COLOR_Y (RKISP1_CIF_SUPER_IMP_BASE + 0x0000000C)
+#define RKISP1_CIF_SUPER_IMP_COLOR_CB (RKISP1_CIF_SUPER_IMP_BASE + 0x00000010)
+#define RKISP1_CIF_SUPER_IMP_COLOR_CR (RKISP1_CIF_SUPER_IMP_BASE + 0x00000014)
+
+#define RKISP1_CIF_ISP_BASE 0x00000400
+#define RKISP1_CIF_ISP_CTRL (RKISP1_CIF_ISP_BASE + 0x00000000)
+#define RKISP1_CIF_ISP_ACQ_PROP (RKISP1_CIF_ISP_BASE + 0x00000004)
+#define RKISP1_CIF_ISP_ACQ_H_OFFS (RKISP1_CIF_ISP_BASE + 0x00000008)
+#define RKISP1_CIF_ISP_ACQ_V_OFFS (RKISP1_CIF_ISP_BASE + 0x0000000C)
+#define RKISP1_CIF_ISP_ACQ_H_SIZE (RKISP1_CIF_ISP_BASE + 0x00000010)
+#define RKISP1_CIF_ISP_ACQ_V_SIZE (RKISP1_CIF_ISP_BASE + 0x00000014)
+#define RKISP1_CIF_ISP_ACQ_NR_FRAMES (RKISP1_CIF_ISP_BASE + 0x00000018)
+#define RKISP1_CIF_ISP_GAMMA_DX_LO (RKISP1_CIF_ISP_BASE + 0x0000001C)
+#define RKISP1_CIF_ISP_GAMMA_DX_HI (RKISP1_CIF_ISP_BASE + 0x00000020)
+#define RKISP1_CIF_ISP_GAMMA_R_Y0 (RKISP1_CIF_ISP_BASE + 0x00000024)
+#define RKISP1_CIF_ISP_GAMMA_R_Y1 (RKISP1_CIF_ISP_BASE + 0x00000028)
+#define RKISP1_CIF_ISP_GAMMA_R_Y2 (RKISP1_CIF_ISP_BASE + 0x0000002C)
+#define RKISP1_CIF_ISP_GAMMA_R_Y3 (RKISP1_CIF_ISP_BASE + 0x00000030)
+#define RKISP1_CIF_ISP_GAMMA_R_Y4 (RKISP1_CIF_ISP_BASE + 0x00000034)
+#define RKISP1_CIF_ISP_GAMMA_R_Y5 (RKISP1_CIF_ISP_BASE + 0x00000038)
+#define RKISP1_CIF_ISP_GAMMA_R_Y6 (RKISP1_CIF_ISP_BASE + 0x0000003C)
+#define RKISP1_CIF_ISP_GAMMA_R_Y7 (RKISP1_CIF_ISP_BASE + 0x00000040)
+#define RKISP1_CIF_ISP_GAMMA_R_Y8 (RKISP1_CIF_ISP_BASE + 0x00000044)
+#define RKISP1_CIF_ISP_GAMMA_R_Y9 (RKISP1_CIF_ISP_BASE + 0x00000048)
+#define RKISP1_CIF_ISP_GAMMA_R_Y10 (RKISP1_CIF_ISP_BASE + 0x0000004C)
+#define RKISP1_CIF_ISP_GAMMA_R_Y11 (RKISP1_CIF_ISP_BASE + 0x00000050)
+#define RKISP1_CIF_ISP_GAMMA_R_Y12 (RKISP1_CIF_ISP_BASE + 0x00000054)
+#define RKISP1_CIF_ISP_GAMMA_R_Y13 (RKISP1_CIF_ISP_BASE + 0x00000058)
+#define RKISP1_CIF_ISP_GAMMA_R_Y14 (RKISP1_CIF_ISP_BASE + 0x0000005C)
+#define RKISP1_CIF_ISP_GAMMA_R_Y15 (RKISP1_CIF_ISP_BASE + 0x00000060)
+#define RKISP1_CIF_ISP_GAMMA_R_Y16 (RKISP1_CIF_ISP_BASE + 0x00000064)
+#define RKISP1_CIF_ISP_GAMMA_G_Y0 (RKISP1_CIF_ISP_BASE + 0x00000068)
+#define RKISP1_CIF_ISP_GAMMA_G_Y1 (RKISP1_CIF_ISP_BASE + 0x0000006C)
+#define RKISP1_CIF_ISP_GAMMA_G_Y2 (RKISP1_CIF_ISP_BASE + 0x00000070)
+#define RKISP1_CIF_ISP_GAMMA_G_Y3 (RKISP1_CIF_ISP_BASE + 0x00000074)
+#define RKISP1_CIF_ISP_GAMMA_G_Y4 (RKISP1_CIF_ISP_BASE + 0x00000078)
+#define RKISP1_CIF_ISP_GAMMA_G_Y5 (RKISP1_CIF_ISP_BASE + 0x0000007C)
+#define RKISP1_CIF_ISP_GAMMA_G_Y6 (RKISP1_CIF_ISP_BASE + 0x00000080)
+#define RKISP1_CIF_ISP_GAMMA_G_Y7 (RKISP1_CIF_ISP_BASE + 0x00000084)
+#define RKISP1_CIF_ISP_GAMMA_G_Y8 (RKISP1_CIF_ISP_BASE + 0x00000088)
+#define RKISP1_CIF_ISP_GAMMA_G_Y9 (RKISP1_CIF_ISP_BASE + 0x0000008C)
+#define RKISP1_CIF_ISP_GAMMA_G_Y10 (RKISP1_CIF_ISP_BASE + 0x00000090)
+#define RKISP1_CIF_ISP_GAMMA_G_Y11 (RKISP1_CIF_ISP_BASE + 0x00000094)
+#define RKISP1_CIF_ISP_GAMMA_G_Y12 (RKISP1_CIF_ISP_BASE + 0x00000098)
+#define RKISP1_CIF_ISP_GAMMA_G_Y13 (RKISP1_CIF_ISP_BASE + 0x0000009C)
+#define RKISP1_CIF_ISP_GAMMA_G_Y14 (RKISP1_CIF_ISP_BASE + 0x000000A0)
+#define RKISP1_CIF_ISP_GAMMA_G_Y15 (RKISP1_CIF_ISP_BASE + 0x000000A4)
+#define RKISP1_CIF_ISP_GAMMA_G_Y16 (RKISP1_CIF_ISP_BASE + 0x000000A8)
+#define RKISP1_CIF_ISP_GAMMA_B_Y0 (RKISP1_CIF_ISP_BASE + 0x000000AC)
+#define RKISP1_CIF_ISP_GAMMA_B_Y1 (RKISP1_CIF_ISP_BASE + 0x000000B0)
+#define RKISP1_CIF_ISP_GAMMA_B_Y2 (RKISP1_CIF_ISP_BASE + 0x000000B4)
+#define RKISP1_CIF_ISP_GAMMA_B_Y3 (RKISP1_CIF_ISP_BASE + 0x000000B8)
+#define RKISP1_CIF_ISP_GAMMA_B_Y4 (RKISP1_CIF_ISP_BASE + 0x000000BC)
+#define RKISP1_CIF_ISP_GAMMA_B_Y5 (RKISP1_CIF_ISP_BASE + 0x000000C0)
+#define RKISP1_CIF_ISP_GAMMA_B_Y6 (RKISP1_CIF_ISP_BASE + 0x000000C4)
+#define RKISP1_CIF_ISP_GAMMA_B_Y7 (RKISP1_CIF_ISP_BASE + 0x000000C8)
+#define RKISP1_CIF_ISP_GAMMA_B_Y8 (RKISP1_CIF_ISP_BASE + 0x000000CC)
+#define RKISP1_CIF_ISP_GAMMA_B_Y9 (RKISP1_CIF_ISP_BASE + 0x000000D0)
+#define RKISP1_CIF_ISP_GAMMA_B_Y10 (RKISP1_CIF_ISP_BASE + 0x000000D4)
+#define RKISP1_CIF_ISP_GAMMA_B_Y11 (RKISP1_CIF_ISP_BASE + 0x000000D8)
+#define RKISP1_CIF_ISP_GAMMA_B_Y12 (RKISP1_CIF_ISP_BASE + 0x000000DC)
+#define RKISP1_CIF_ISP_GAMMA_B_Y13 (RKISP1_CIF_ISP_BASE + 0x000000E0)
+#define RKISP1_CIF_ISP_GAMMA_B_Y14 (RKISP1_CIF_ISP_BASE + 0x000000E4)
+#define RKISP1_CIF_ISP_GAMMA_B_Y15 (RKISP1_CIF_ISP_BASE + 0x000000E8)
+#define RKISP1_CIF_ISP_GAMMA_B_Y16 (RKISP1_CIF_ISP_BASE + 0x000000EC)
+#define RKISP1_CIF_ISP_AWB_PROP_V10 (RKISP1_CIF_ISP_BASE + 0x00000110)
+#define RKISP1_CIF_ISP_AWB_WND_H_OFFS_V10 (RKISP1_CIF_ISP_BASE + 0x00000114)
+#define RKISP1_CIF_ISP_AWB_WND_V_OFFS_V10 (RKISP1_CIF_ISP_BASE + 0x00000118)
+#define RKISP1_CIF_ISP_AWB_WND_H_SIZE_V10 (RKISP1_CIF_ISP_BASE + 0x0000011C)
+#define RKISP1_CIF_ISP_AWB_WND_V_SIZE_V10 (RKISP1_CIF_ISP_BASE + 0x00000120)
+#define RKISP1_CIF_ISP_AWB_FRAMES_V10 (RKISP1_CIF_ISP_BASE + 0x00000124)
+#define RKISP1_CIF_ISP_AWB_REF_V10 (RKISP1_CIF_ISP_BASE + 0x00000128)
+#define RKISP1_CIF_ISP_AWB_THRESH_V10 (RKISP1_CIF_ISP_BASE + 0x0000012C)
+#define RKISP1_CIF_ISP_AWB_GAIN_G_V10 (RKISP1_CIF_ISP_BASE + 0x00000138)
+#define RKISP1_CIF_ISP_AWB_GAIN_RB_V10 (RKISP1_CIF_ISP_BASE + 0x0000013C)
+#define RKISP1_CIF_ISP_AWB_WHITE_CNT_V10 (RKISP1_CIF_ISP_BASE + 0x00000140)
+#define RKISP1_CIF_ISP_AWB_MEAN_V10 (RKISP1_CIF_ISP_BASE + 0x00000144)
+#define RKISP1_CIF_ISP_AWB_PROP_V12 (RKISP1_CIF_ISP_BASE + 0x00000110)
+#define RKISP1_CIF_ISP_AWB_SIZE_V12 (RKISP1_CIF_ISP_BASE + 0x00000114)
+#define RKISP1_CIF_ISP_AWB_OFFS_V12 (RKISP1_CIF_ISP_BASE + 0x00000118)
+#define RKISP1_CIF_ISP_AWB_REF_V12 (RKISP1_CIF_ISP_BASE + 0x0000011C)
+#define RKISP1_CIF_ISP_AWB_THRESH_V12 (RKISP1_CIF_ISP_BASE + 0x00000120)
+#define RKISP1_CIF_ISP_X_COOR12_V12 (RKISP1_CIF_ISP_BASE + 0x00000124)
+#define RKISP1_CIF_ISP_X_COOR34_V12 (RKISP1_CIF_ISP_BASE + 0x00000128)
+#define RKISP1_CIF_ISP_AWB_WHITE_CNT_V12 (RKISP1_CIF_ISP_BASE + 0x0000012C)
+#define RKISP1_CIF_ISP_AWB_MEAN_V12 (RKISP1_CIF_ISP_BASE + 0x00000130)
+#define RKISP1_CIF_ISP_DEGAIN_V12 (RKISP1_CIF_ISP_BASE + 0x00000134)
+#define RKISP1_CIF_ISP_AWB_GAIN_G_V12 (RKISP1_CIF_ISP_BASE + 0x00000138)
+#define RKISP1_CIF_ISP_AWB_GAIN_RB_V12 (RKISP1_CIF_ISP_BASE + 0x0000013C)
+#define RKISP1_CIF_ISP_REGION_LINE_V12 (RKISP1_CIF_ISP_BASE + 0x00000140)
+#define RKISP1_CIF_ISP_WP_CNT_REGION0_V12 (RKISP1_CIF_ISP_BASE + 0x00000160)
+#define RKISP1_CIF_ISP_WP_CNT_REGION1_V12 (RKISP1_CIF_ISP_BASE + 0x00000164)
+#define RKISP1_CIF_ISP_WP_CNT_REGION2_V12 (RKISP1_CIF_ISP_BASE + 0x00000168)
+#define RKISP1_CIF_ISP_WP_CNT_REGION3_V12 (RKISP1_CIF_ISP_BASE + 0x0000016C)
+#define RKISP1_CIF_ISP_CC_COEFF_0 (RKISP1_CIF_ISP_BASE + 0x00000170)
+#define RKISP1_CIF_ISP_CC_COEFF_1 (RKISP1_CIF_ISP_BASE + 0x00000174)
+#define RKISP1_CIF_ISP_CC_COEFF_2 (RKISP1_CIF_ISP_BASE + 0x00000178)
+#define RKISP1_CIF_ISP_CC_COEFF_3 (RKISP1_CIF_ISP_BASE + 0x0000017C)
+#define RKISP1_CIF_ISP_CC_COEFF_4 (RKISP1_CIF_ISP_BASE + 0x00000180)
+#define RKISP1_CIF_ISP_CC_COEFF_5 (RKISP1_CIF_ISP_BASE + 0x00000184)
+#define RKISP1_CIF_ISP_CC_COEFF_6 (RKISP1_CIF_ISP_BASE + 0x00000188)
+#define RKISP1_CIF_ISP_CC_COEFF_7 (RKISP1_CIF_ISP_BASE + 0x0000018C)
+#define RKISP1_CIF_ISP_CC_COEFF_8 (RKISP1_CIF_ISP_BASE + 0x00000190)
+#define RKISP1_CIF_ISP_OUT_H_OFFS (RKISP1_CIF_ISP_BASE + 0x00000194)
+#define RKISP1_CIF_ISP_OUT_V_OFFS (RKISP1_CIF_ISP_BASE + 0x00000198)
+#define RKISP1_CIF_ISP_OUT_H_SIZE (RKISP1_CIF_ISP_BASE + 0x0000019C)
+#define RKISP1_CIF_ISP_OUT_V_SIZE (RKISP1_CIF_ISP_BASE + 0x000001A0)
+#define RKISP1_CIF_ISP_DEMOSAIC (RKISP1_CIF_ISP_BASE + 0x000001A4)
+#define RKISP1_CIF_ISP_FLAGS_SHD (RKISP1_CIF_ISP_BASE + 0x000001A8)
+#define RKISP1_CIF_ISP_OUT_H_OFFS_SHD (RKISP1_CIF_ISP_BASE + 0x000001AC)
+#define RKISP1_CIF_ISP_OUT_V_OFFS_SHD (RKISP1_CIF_ISP_BASE + 0x000001B0)
+#define RKISP1_CIF_ISP_OUT_H_SIZE_SHD (RKISP1_CIF_ISP_BASE + 0x000001B4)
+#define RKISP1_CIF_ISP_OUT_V_SIZE_SHD (RKISP1_CIF_ISP_BASE + 0x000001B8)
+#define RKISP1_CIF_ISP_IMSC (RKISP1_CIF_ISP_BASE + 0x000001BC)
+#define RKISP1_CIF_ISP_RIS (RKISP1_CIF_ISP_BASE + 0x000001C0)
+#define RKISP1_CIF_ISP_MIS (RKISP1_CIF_ISP_BASE + 0x000001C4)
+#define RKISP1_CIF_ISP_ICR (RKISP1_CIF_ISP_BASE + 0x000001C8)
+#define RKISP1_CIF_ISP_ISR (RKISP1_CIF_ISP_BASE + 0x000001CC)
+#define RKISP1_CIF_ISP_CT_COEFF_0 (RKISP1_CIF_ISP_BASE + 0x000001D0)
+#define RKISP1_CIF_ISP_CT_COEFF_1 (RKISP1_CIF_ISP_BASE + 0x000001D4)
+#define RKISP1_CIF_ISP_CT_COEFF_2 (RKISP1_CIF_ISP_BASE + 0x000001D8)
+#define RKISP1_CIF_ISP_CT_COEFF_3 (RKISP1_CIF_ISP_BASE + 0x000001DC)
+#define RKISP1_CIF_ISP_CT_COEFF_4 (RKISP1_CIF_ISP_BASE + 0x000001E0)
+#define RKISP1_CIF_ISP_CT_COEFF_5 (RKISP1_CIF_ISP_BASE + 0x000001E4)
+#define RKISP1_CIF_ISP_CT_COEFF_6 (RKISP1_CIF_ISP_BASE + 0x000001E8)
+#define RKISP1_CIF_ISP_CT_COEFF_7 (RKISP1_CIF_ISP_BASE + 0x000001EC)
+#define RKISP1_CIF_ISP_CT_COEFF_8 (RKISP1_CIF_ISP_BASE + 0x000001F0)
+#define RKISP1_CIF_ISP_GAMMA_OUT_MODE_V10 (RKISP1_CIF_ISP_BASE + 0x000001F4)
+#define RKISP1_CIF_ISP_GAMMA_OUT_Y_0_V10 (RKISP1_CIF_ISP_BASE + 0x000001F8)
+#define RKISP1_CIF_ISP_GAMMA_OUT_Y_1_V10 (RKISP1_CIF_ISP_BASE + 0x000001FC)
+#define RKISP1_CIF_ISP_GAMMA_OUT_Y_2_V10 (RKISP1_CIF_ISP_BASE + 0x00000200)
+#define RKISP1_CIF_ISP_GAMMA_OUT_Y_3_V10 (RKISP1_CIF_ISP_BASE + 0x00000204)
+#define RKISP1_CIF_ISP_GAMMA_OUT_Y_4_V10 (RKISP1_CIF_ISP_BASE + 0x00000208)
+#define RKISP1_CIF_ISP_GAMMA_OUT_Y_5_V10 (RKISP1_CIF_ISP_BASE + 0x0000020C)
+#define RKISP1_CIF_ISP_GAMMA_OUT_Y_6_V10 (RKISP1_CIF_ISP_BASE + 0x00000210)
+#define RKISP1_CIF_ISP_GAMMA_OUT_Y_7_V10 (RKISP1_CIF_ISP_BASE + 0x00000214)
+#define RKISP1_CIF_ISP_GAMMA_OUT_Y_8_V10 (RKISP1_CIF_ISP_BASE + 0x00000218)
+#define RKISP1_CIF_ISP_GAMMA_OUT_Y_9_V10 (RKISP1_CIF_ISP_BASE + 0x0000021C)
+#define RKISP1_CIF_ISP_GAMMA_OUT_Y_10_V10 (RKISP1_CIF_ISP_BASE + 0x00000220)
+#define RKISP1_CIF_ISP_GAMMA_OUT_Y_11_V10 (RKISP1_CIF_ISP_BASE + 0x00000224)
+#define RKISP1_CIF_ISP_GAMMA_OUT_Y_12_V10 (RKISP1_CIF_ISP_BASE + 0x00000228)
+#define RKISP1_CIF_ISP_GAMMA_OUT_Y_13_V10 (RKISP1_CIF_ISP_BASE + 0x0000022C)
+#define RKISP1_CIF_ISP_GAMMA_OUT_Y_14_V10 (RKISP1_CIF_ISP_BASE + 0x00000230)
+#define RKISP1_CIF_ISP_GAMMA_OUT_Y_15_V10 (RKISP1_CIF_ISP_BASE + 0x00000234)
+#define RKISP1_CIF_ISP_GAMMA_OUT_Y_16_V10 (RKISP1_CIF_ISP_BASE + 0x00000238)
+#define RKISP1_CIF_ISP_ERR (RKISP1_CIF_ISP_BASE + 0x0000023C)
+#define RKISP1_CIF_ISP_ERR_CLR (RKISP1_CIF_ISP_BASE + 0x00000240)
+#define RKISP1_CIF_ISP_FRAME_COUNT (RKISP1_CIF_ISP_BASE + 0x00000244)
+#define RKISP1_CIF_ISP_CT_OFFSET_R (RKISP1_CIF_ISP_BASE + 0x00000248)
+#define RKISP1_CIF_ISP_CT_OFFSET_G (RKISP1_CIF_ISP_BASE + 0x0000024C)
+#define RKISP1_CIF_ISP_CT_OFFSET_B (RKISP1_CIF_ISP_BASE + 0x00000250)
+#define RKISP1_CIF_ISP_GAMMA_OUT_MODE_V12 (RKISP1_CIF_ISP_BASE + 0x00000300)
+#define RKISP1_CIF_ISP_GAMMA_OUT_Y_0_V12 (RKISP1_CIF_ISP_BASE + 0x00000304)
+
+#define RKISP1_CIF_ISP_FLASH_BASE 0x00000660
+#define RKISP1_CIF_ISP_FLASH_CMD (RKISP1_CIF_ISP_FLASH_BASE + 0x00000000)
+#define RKISP1_CIF_ISP_FLASH_CONFIG (RKISP1_CIF_ISP_FLASH_BASE + 0x00000004)
+#define RKISP1_CIF_ISP_FLASH_PREDIV (RKISP1_CIF_ISP_FLASH_BASE + 0x00000008)
+#define RKISP1_CIF_ISP_FLASH_DELAY (RKISP1_CIF_ISP_FLASH_BASE + 0x0000000C)
+#define RKISP1_CIF_ISP_FLASH_TIME (RKISP1_CIF_ISP_FLASH_BASE + 0x00000010)
+#define RKISP1_CIF_ISP_FLASH_MAXP (RKISP1_CIF_ISP_FLASH_BASE + 0x00000014)
+
+#define RKISP1_CIF_ISP_SH_BASE 0x00000680
+#define RKISP1_CIF_ISP_SH_CTRL (RKISP1_CIF_ISP_SH_BASE + 0x00000000)
+#define RKISP1_CIF_ISP_SH_PREDIV (RKISP1_CIF_ISP_SH_BASE + 0x00000004)
+#define RKISP1_CIF_ISP_SH_DELAY (RKISP1_CIF_ISP_SH_BASE + 0x00000008)
+#define RKISP1_CIF_ISP_SH_TIME (RKISP1_CIF_ISP_SH_BASE + 0x0000000C)
+
+#define RKISP1_CIF_C_PROC_BASE 0x00000800
+#define RKISP1_CIF_C_PROC_CTRL (RKISP1_CIF_C_PROC_BASE + 0x00000000)
+#define RKISP1_CIF_C_PROC_CONTRAST (RKISP1_CIF_C_PROC_BASE + 0x00000004)
+#define RKISP1_CIF_C_PROC_BRIGHTNESS (RKISP1_CIF_C_PROC_BASE + 0x00000008)
+#define RKISP1_CIF_C_PROC_SATURATION (RKISP1_CIF_C_PROC_BASE + 0x0000000C)
+#define RKISP1_CIF_C_PROC_HUE (RKISP1_CIF_C_PROC_BASE + 0x00000010)
+
+#define RKISP1_CIF_DUAL_CROP_BASE 0x00000880
+#define RKISP1_CIF_DUAL_CROP_CTRL (RKISP1_CIF_DUAL_CROP_BASE + 0x00000000)
+#define RKISP1_CIF_DUAL_CROP_M_H_OFFS (RKISP1_CIF_DUAL_CROP_BASE + 0x00000004)
+#define RKISP1_CIF_DUAL_CROP_M_V_OFFS (RKISP1_CIF_DUAL_CROP_BASE + 0x00000008)
+#define RKISP1_CIF_DUAL_CROP_M_H_SIZE (RKISP1_CIF_DUAL_CROP_BASE + 0x0000000C)
+#define RKISP1_CIF_DUAL_CROP_M_V_SIZE (RKISP1_CIF_DUAL_CROP_BASE + 0x00000010)
+#define RKISP1_CIF_DUAL_CROP_S_H_OFFS (RKISP1_CIF_DUAL_CROP_BASE + 0x00000014)
+#define RKISP1_CIF_DUAL_CROP_S_V_OFFS (RKISP1_CIF_DUAL_CROP_BASE + 0x00000018)
+#define RKISP1_CIF_DUAL_CROP_S_H_SIZE (RKISP1_CIF_DUAL_CROP_BASE + 0x0000001C)
+#define RKISP1_CIF_DUAL_CROP_S_V_SIZE (RKISP1_CIF_DUAL_CROP_BASE + 0x00000020)
+#define RKISP1_CIF_DUAL_CROP_M_H_OFFS_SHD (RKISP1_CIF_DUAL_CROP_BASE + 0x00000024)
+#define RKISP1_CIF_DUAL_CROP_M_V_OFFS_SHD (RKISP1_CIF_DUAL_CROP_BASE + 0x00000028)
+#define RKISP1_CIF_DUAL_CROP_M_H_SIZE_SHD (RKISP1_CIF_DUAL_CROP_BASE + 0x0000002C)
+#define RKISP1_CIF_DUAL_CROP_M_V_SIZE_SHD (RKISP1_CIF_DUAL_CROP_BASE + 0x00000030)
+#define RKISP1_CIF_DUAL_CROP_S_H_OFFS_SHD (RKISP1_CIF_DUAL_CROP_BASE + 0x00000034)
+#define RKISP1_CIF_DUAL_CROP_S_V_OFFS_SHD (RKISP1_CIF_DUAL_CROP_BASE + 0x00000038)
+#define RKISP1_CIF_DUAL_CROP_S_H_SIZE_SHD (RKISP1_CIF_DUAL_CROP_BASE + 0x0000003C)
+#define RKISP1_CIF_DUAL_CROP_S_V_SIZE_SHD (RKISP1_CIF_DUAL_CROP_BASE + 0x00000040)
+
+#define RKISP1_CIF_MRSZ_BASE 0x00000C00
+#define RKISP1_CIF_SRSZ_BASE 0x00001000
+#define RKISP1_CIF_RSZ_CTRL 0x0000
+#define RKISP1_CIF_RSZ_SCALE_HY 0x0004
+#define RKISP1_CIF_RSZ_SCALE_HCB 0x0008
+#define RKISP1_CIF_RSZ_SCALE_HCR 0x000C
+#define RKISP1_CIF_RSZ_SCALE_VY 0x0010
+#define RKISP1_CIF_RSZ_SCALE_VC 0x0014
+#define RKISP1_CIF_RSZ_PHASE_HY 0x0018
+#define RKISP1_CIF_RSZ_PHASE_HC 0x001C
+#define RKISP1_CIF_RSZ_PHASE_VY 0x0020
+#define RKISP1_CIF_RSZ_PHASE_VC 0x0024
+#define RKISP1_CIF_RSZ_SCALE_LUT_ADDR 0x0028
+#define RKISP1_CIF_RSZ_SCALE_LUT 0x002C
+#define RKISP1_CIF_RSZ_CTRL_SHD 0x0030
+#define RKISP1_CIF_RSZ_SCALE_HY_SHD 0x0034
+#define RKISP1_CIF_RSZ_SCALE_HCB_SHD 0x0038
+#define RKISP1_CIF_RSZ_SCALE_HCR_SHD 0x003C
+#define RKISP1_CIF_RSZ_SCALE_VY_SHD 0x0040
+#define RKISP1_CIF_RSZ_SCALE_VC_SHD 0x0044
+#define RKISP1_CIF_RSZ_PHASE_HY_SHD 0x0048
+#define RKISP1_CIF_RSZ_PHASE_HC_SHD 0x004C
+#define RKISP1_CIF_RSZ_PHASE_VY_SHD 0x0050
+#define RKISP1_CIF_RSZ_PHASE_VC_SHD 0x0054
+
+#define RKISP1_CIF_MI_BASE 0x00001400
+#define RKISP1_CIF_MI_CTRL (RKISP1_CIF_MI_BASE + 0x00000000)
+#define RKISP1_CIF_MI_INIT (RKISP1_CIF_MI_BASE + 0x00000004)
+#define RKISP1_CIF_MI_MP_Y_BASE_AD_INIT (RKISP1_CIF_MI_BASE + 0x00000008)
+#define RKISP1_CIF_MI_MP_Y_SIZE_INIT (RKISP1_CIF_MI_BASE + 0x0000000C)
+#define RKISP1_CIF_MI_MP_Y_OFFS_CNT_INIT (RKISP1_CIF_MI_BASE + 0x00000010)
+#define RKISP1_CIF_MI_MP_Y_OFFS_CNT_START (RKISP1_CIF_MI_BASE + 0x00000014)
+#define RKISP1_CIF_MI_MP_Y_IRQ_OFFS_INIT (RKISP1_CIF_MI_BASE + 0x00000018)
+#define RKISP1_CIF_MI_MP_CB_BASE_AD_INIT (RKISP1_CIF_MI_BASE + 0x0000001C)
+#define RKISP1_CIF_MI_MP_CB_SIZE_INIT (RKISP1_CIF_MI_BASE + 0x00000020)
+#define RKISP1_CIF_MI_MP_CB_OFFS_CNT_INIT (RKISP1_CIF_MI_BASE + 0x00000024)
+#define RKISP1_CIF_MI_MP_CB_OFFS_CNT_START (RKISP1_CIF_MI_BASE + 0x00000028)
+#define RKISP1_CIF_MI_MP_CR_BASE_AD_INIT (RKISP1_CIF_MI_BASE + 0x0000002C)
+#define RKISP1_CIF_MI_MP_CR_SIZE_INIT (RKISP1_CIF_MI_BASE + 0x00000030)
+#define RKISP1_CIF_MI_MP_CR_OFFS_CNT_INIT (RKISP1_CIF_MI_BASE + 0x00000034)
+#define RKISP1_CIF_MI_MP_CR_OFFS_CNT_START (RKISP1_CIF_MI_BASE + 0x00000038)
+#define RKISP1_CIF_MI_SP_Y_BASE_AD_INIT (RKISP1_CIF_MI_BASE + 0x0000003C)
+#define RKISP1_CIF_MI_SP_Y_SIZE_INIT (RKISP1_CIF_MI_BASE + 0x00000040)
+#define RKISP1_CIF_MI_SP_Y_OFFS_CNT_INIT (RKISP1_CIF_MI_BASE + 0x00000044)
+#define RKISP1_CIF_MI_SP_Y_OFFS_CNT_START (RKISP1_CIF_MI_BASE + 0x00000048)
+#define RKISP1_CIF_MI_SP_Y_LLENGTH (RKISP1_CIF_MI_BASE + 0x0000004C)
+#define RKISP1_CIF_MI_SP_CB_BASE_AD_INIT (RKISP1_CIF_MI_BASE + 0x00000050)
+#define RKISP1_CIF_MI_SP_CB_SIZE_INIT (RKISP1_CIF_MI_BASE + 0x00000054)
+#define RKISP1_CIF_MI_SP_CB_OFFS_CNT_INIT (RKISP1_CIF_MI_BASE + 0x00000058)
+#define RKISP1_CIF_MI_SP_CB_OFFS_CNT_START (RKISP1_CIF_MI_BASE + 0x0000005C)
+#define RKISP1_CIF_MI_SP_CR_BASE_AD_INIT (RKISP1_CIF_MI_BASE + 0x00000060)
+#define RKISP1_CIF_MI_SP_CR_SIZE_INIT (RKISP1_CIF_MI_BASE + 0x00000064)
+#define RKISP1_CIF_MI_SP_CR_OFFS_CNT_INIT (RKISP1_CIF_MI_BASE + 0x00000068)
+#define RKISP1_CIF_MI_SP_CR_OFFS_CNT_START (RKISP1_CIF_MI_BASE + 0x0000006C)
+#define RKISP1_CIF_MI_BYTE_CNT (RKISP1_CIF_MI_BASE + 0x00000070)
+#define RKISP1_CIF_MI_CTRL_SHD (RKISP1_CIF_MI_BASE + 0x00000074)
+#define RKISP1_CIF_MI_MP_Y_BASE_AD_SHD (RKISP1_CIF_MI_BASE + 0x00000078)
+#define RKISP1_CIF_MI_MP_Y_SIZE_SHD (RKISP1_CIF_MI_BASE + 0x0000007C)
+#define RKISP1_CIF_MI_MP_Y_OFFS_CNT_SHD (RKISP1_CIF_MI_BASE + 0x00000080)
+#define RKISP1_CIF_MI_MP_Y_IRQ_OFFS_SHD (RKISP1_CIF_MI_BASE + 0x00000084)
+#define RKISP1_CIF_MI_MP_CB_BASE_AD_SHD (RKISP1_CIF_MI_BASE + 0x00000088)
+#define RKISP1_CIF_MI_MP_CB_SIZE_SHD (RKISP1_CIF_MI_BASE + 0x0000008C)
+#define RKISP1_CIF_MI_MP_CB_OFFS_CNT_SHD (RKISP1_CIF_MI_BASE + 0x00000090)
+#define RKISP1_CIF_MI_MP_CR_BASE_AD_SHD (RKISP1_CIF_MI_BASE + 0x00000094)
+#define RKISP1_CIF_MI_MP_CR_SIZE_SHD (RKISP1_CIF_MI_BASE + 0x00000098)
+#define RKISP1_CIF_MI_MP_CR_OFFS_CNT_SHD (RKISP1_CIF_MI_BASE + 0x0000009C)
+#define RKISP1_CIF_MI_SP_Y_BASE_AD_SHD (RKISP1_CIF_MI_BASE + 0x000000A0)
+#define RKISP1_CIF_MI_SP_Y_SIZE_SHD (RKISP1_CIF_MI_BASE + 0x000000A4)
+#define RKISP1_CIF_MI_SP_Y_OFFS_CNT_SHD (RKISP1_CIF_MI_BASE + 0x000000A8)
+#define RKISP1_CIF_MI_SP_CB_BASE_AD_SHD (RKISP1_CIF_MI_BASE + 0x000000B0)
+#define RKISP1_CIF_MI_SP_CB_SIZE_SHD (RKISP1_CIF_MI_BASE + 0x000000B4)
+#define RKISP1_CIF_MI_SP_CB_OFFS_CNT_SHD (RKISP1_CIF_MI_BASE + 0x000000B8)
+#define RKISP1_CIF_MI_SP_CR_BASE_AD_SHD (RKISP1_CIF_MI_BASE + 0x000000BC)
+#define RKISP1_CIF_MI_SP_CR_SIZE_SHD (RKISP1_CIF_MI_BASE + 0x000000C0)
+#define RKISP1_CIF_MI_SP_CR_OFFS_CNT_SHD (RKISP1_CIF_MI_BASE + 0x000000C4)
+#define RKISP1_CIF_MI_DMA_Y_PIC_START_AD (RKISP1_CIF_MI_BASE + 0x000000C8)
+#define RKISP1_CIF_MI_DMA_Y_PIC_WIDTH (RKISP1_CIF_MI_BASE + 0x000000CC)
+#define RKISP1_CIF_MI_DMA_Y_LLENGTH (RKISP1_CIF_MI_BASE + 0x000000D0)
+#define RKISP1_CIF_MI_DMA_Y_PIC_SIZE (RKISP1_CIF_MI_BASE + 0x000000D4)
+#define RKISP1_CIF_MI_DMA_CB_PIC_START_AD (RKISP1_CIF_MI_BASE + 0x000000D8)
+#define RKISP1_CIF_MI_DMA_CR_PIC_START_AD (RKISP1_CIF_MI_BASE + 0x000000E8)
+#define RKISP1_CIF_MI_IMSC (RKISP1_CIF_MI_BASE + 0x000000F8)
+#define RKISP1_CIF_MI_RIS (RKISP1_CIF_MI_BASE + 0x000000FC)
+#define RKISP1_CIF_MI_MIS (RKISP1_CIF_MI_BASE + 0x00000100)
+#define RKISP1_CIF_MI_ICR (RKISP1_CIF_MI_BASE + 0x00000104)
+#define RKISP1_CIF_MI_ISR (RKISP1_CIF_MI_BASE + 0x00000108)
+#define RKISP1_CIF_MI_STATUS (RKISP1_CIF_MI_BASE + 0x0000010C)
+#define RKISP1_CIF_MI_STATUS_CLR (RKISP1_CIF_MI_BASE + 0x00000110)
+#define RKISP1_CIF_MI_SP_Y_PIC_WIDTH (RKISP1_CIF_MI_BASE + 0x00000114)
+#define RKISP1_CIF_MI_SP_Y_PIC_HEIGHT (RKISP1_CIF_MI_BASE + 0x00000118)
+#define RKISP1_CIF_MI_SP_Y_PIC_SIZE (RKISP1_CIF_MI_BASE + 0x0000011C)
+#define RKISP1_CIF_MI_DMA_CTRL (RKISP1_CIF_MI_BASE + 0x00000120)
+#define RKISP1_CIF_MI_DMA_START (RKISP1_CIF_MI_BASE + 0x00000124)
+#define RKISP1_CIF_MI_DMA_STATUS (RKISP1_CIF_MI_BASE + 0x00000128)
+#define RKISP1_CIF_MI_PIXEL_COUNT (RKISP1_CIF_MI_BASE + 0x0000012C)
+#define RKISP1_CIF_MI_MP_Y_BASE_AD_INIT2 (RKISP1_CIF_MI_BASE + 0x00000130)
+#define RKISP1_CIF_MI_MP_CB_BASE_AD_INIT2 (RKISP1_CIF_MI_BASE + 0x00000134)
+#define RKISP1_CIF_MI_MP_CR_BASE_AD_INIT2 (RKISP1_CIF_MI_BASE + 0x00000138)
+#define RKISP1_CIF_MI_SP_Y_BASE_AD_INIT2 (RKISP1_CIF_MI_BASE + 0x0000013C)
+#define RKISP1_CIF_MI_SP_CB_BASE_AD_INIT2 (RKISP1_CIF_MI_BASE + 0x00000140)
+#define RKISP1_CIF_MI_SP_CR_BASE_AD_INIT2 (RKISP1_CIF_MI_BASE + 0x00000144)
+#define RKISP1_CIF_MI_XTD_FORMAT_CTRL (RKISP1_CIF_MI_BASE + 0x00000148)
+
+#define RKISP1_CIF_SMIA_BASE 0x00001A00
+#define RKISP1_CIF_SMIA_CTRL (RKISP1_CIF_SMIA_BASE + 0x00000000)
+#define RKISP1_CIF_SMIA_STATUS (RKISP1_CIF_SMIA_BASE + 0x00000004)
+#define RKISP1_CIF_SMIA_IMSC (RKISP1_CIF_SMIA_BASE + 0x00000008)
+#define RKISP1_CIF_SMIA_RIS (RKISP1_CIF_SMIA_BASE + 0x0000000C)
+#define RKISP1_CIF_SMIA_MIS (RKISP1_CIF_SMIA_BASE + 0x00000010)
+#define RKISP1_CIF_SMIA_ICR (RKISP1_CIF_SMIA_BASE + 0x00000014)
+#define RKISP1_CIF_SMIA_ISR (RKISP1_CIF_SMIA_BASE + 0x00000018)
+#define RKISP1_CIF_SMIA_DATA_FORMAT_SEL (RKISP1_CIF_SMIA_BASE + 0x0000001C)
+#define RKISP1_CIF_SMIA_SOF_EMB_DATA_LINES (RKISP1_CIF_SMIA_BASE + 0x00000020)
+#define RKISP1_CIF_SMIA_EMB_HSTART (RKISP1_CIF_SMIA_BASE + 0x00000024)
+#define RKISP1_CIF_SMIA_EMB_HSIZE (RKISP1_CIF_SMIA_BASE + 0x00000028)
+#define RKISP1_CIF_SMIA_EMB_VSTART (RKISP1_CIF_SMIA_BASE + 0x0000002c)
+#define RKISP1_CIF_SMIA_NUM_LINES (RKISP1_CIF_SMIA_BASE + 0x00000030)
+#define RKISP1_CIF_SMIA_EMB_DATA_FIFO (RKISP1_CIF_SMIA_BASE + 0x00000034)
+#define RKISP1_CIF_SMIA_EMB_DATA_WATERMARK (RKISP1_CIF_SMIA_BASE + 0x00000038)
+
+#define RKISP1_CIF_MIPI_BASE 0x00001C00
+#define RKISP1_CIF_MIPI_CTRL (RKISP1_CIF_MIPI_BASE + 0x00000000)
+#define RKISP1_CIF_MIPI_STATUS (RKISP1_CIF_MIPI_BASE + 0x00000004)
+#define RKISP1_CIF_MIPI_IMSC (RKISP1_CIF_MIPI_BASE + 0x00000008)
+#define RKISP1_CIF_MIPI_RIS (RKISP1_CIF_MIPI_BASE + 0x0000000C)
+#define RKISP1_CIF_MIPI_MIS (RKISP1_CIF_MIPI_BASE + 0x00000010)
+#define RKISP1_CIF_MIPI_ICR (RKISP1_CIF_MIPI_BASE + 0x00000014)
+#define RKISP1_CIF_MIPI_ISR (RKISP1_CIF_MIPI_BASE + 0x00000018)
+#define RKISP1_CIF_MIPI_CUR_DATA_ID (RKISP1_CIF_MIPI_BASE + 0x0000001C)
+#define RKISP1_CIF_MIPI_IMG_DATA_SEL (RKISP1_CIF_MIPI_BASE + 0x00000020)
+#define RKISP1_CIF_MIPI_ADD_DATA_SEL_1 (RKISP1_CIF_MIPI_BASE + 0x00000024)
+#define RKISP1_CIF_MIPI_ADD_DATA_SEL_2 (RKISP1_CIF_MIPI_BASE + 0x00000028)
+#define RKISP1_CIF_MIPI_ADD_DATA_SEL_3 (RKISP1_CIF_MIPI_BASE + 0x0000002C)
+#define RKISP1_CIF_MIPI_ADD_DATA_SEL_4 (RKISP1_CIF_MIPI_BASE + 0x00000030)
+#define RKISP1_CIF_MIPI_ADD_DATA_FIFO (RKISP1_CIF_MIPI_BASE + 0x00000034)
+#define RKISP1_CIF_MIPI_FIFO_FILL_LEVEL (RKISP1_CIF_MIPI_BASE + 0x00000038)
+#define RKISP1_CIF_MIPI_COMPRESSED_MODE (RKISP1_CIF_MIPI_BASE + 0x0000003C)
+#define RKISP1_CIF_MIPI_FRAME (RKISP1_CIF_MIPI_BASE + 0x00000040)
+#define RKISP1_CIF_MIPI_GEN_SHORT_DT (RKISP1_CIF_MIPI_BASE + 0x00000044)
+#define RKISP1_CIF_MIPI_GEN_SHORT_8_9 (RKISP1_CIF_MIPI_BASE + 0x00000048)
+#define RKISP1_CIF_MIPI_GEN_SHORT_A_B (RKISP1_CIF_MIPI_BASE + 0x0000004C)
+#define RKISP1_CIF_MIPI_GEN_SHORT_C_D (RKISP1_CIF_MIPI_BASE + 0x00000050)
+#define RKISP1_CIF_MIPI_GEN_SHORT_E_F (RKISP1_CIF_MIPI_BASE + 0x00000054)
+
+#define RKISP1_CIF_ISP_AFM_BASE 0x00002000
+#define RKISP1_CIF_ISP_AFM_CTRL (RKISP1_CIF_ISP_AFM_BASE + 0x00000000)
+#define RKISP1_CIF_ISP_AFM_LT_A (RKISP1_CIF_ISP_AFM_BASE + 0x00000004)
+#define RKISP1_CIF_ISP_AFM_RB_A (RKISP1_CIF_ISP_AFM_BASE + 0x00000008)
+#define RKISP1_CIF_ISP_AFM_LT_B (RKISP1_CIF_ISP_AFM_BASE + 0x0000000C)
+#define RKISP1_CIF_ISP_AFM_RB_B (RKISP1_CIF_ISP_AFM_BASE + 0x00000010)
+#define RKISP1_CIF_ISP_AFM_LT_C (RKISP1_CIF_ISP_AFM_BASE + 0x00000014)
+#define RKISP1_CIF_ISP_AFM_RB_C (RKISP1_CIF_ISP_AFM_BASE + 0x00000018)
+#define RKISP1_CIF_ISP_AFM_THRES (RKISP1_CIF_ISP_AFM_BASE + 0x0000001C)
+#define RKISP1_CIF_ISP_AFM_VAR_SHIFT (RKISP1_CIF_ISP_AFM_BASE + 0x00000020)
+#define RKISP1_CIF_ISP_AFM_SUM_A (RKISP1_CIF_ISP_AFM_BASE + 0x00000024)
+#define RKISP1_CIF_ISP_AFM_SUM_B (RKISP1_CIF_ISP_AFM_BASE + 0x00000028)
+#define RKISP1_CIF_ISP_AFM_SUM_C (RKISP1_CIF_ISP_AFM_BASE + 0x0000002C)
+#define RKISP1_CIF_ISP_AFM_LUM_A (RKISP1_CIF_ISP_AFM_BASE + 0x00000030)
+#define RKISP1_CIF_ISP_AFM_LUM_B (RKISP1_CIF_ISP_AFM_BASE + 0x00000034)
+#define RKISP1_CIF_ISP_AFM_LUM_C (RKISP1_CIF_ISP_AFM_BASE + 0x00000038)
+
+#define RKISP1_CIF_ISP_LSC_BASE 0x00002200
+#define RKISP1_CIF_ISP_LSC_CTRL (RKISP1_CIF_ISP_LSC_BASE + 0x00000000)
+#define RKISP1_CIF_ISP_LSC_R_TABLE_ADDR (RKISP1_CIF_ISP_LSC_BASE + 0x00000004)
+#define RKISP1_CIF_ISP_LSC_GR_TABLE_ADDR (RKISP1_CIF_ISP_LSC_BASE + 0x00000008)
+#define RKISP1_CIF_ISP_LSC_B_TABLE_ADDR (RKISP1_CIF_ISP_LSC_BASE + 0x0000000C)
+#define RKISP1_CIF_ISP_LSC_GB_TABLE_ADDR (RKISP1_CIF_ISP_LSC_BASE + 0x00000010)
+#define RKISP1_CIF_ISP_LSC_R_TABLE_DATA (RKISP1_CIF_ISP_LSC_BASE + 0x00000014)
+#define RKISP1_CIF_ISP_LSC_GR_TABLE_DATA (RKISP1_CIF_ISP_LSC_BASE + 0x00000018)
+#define RKISP1_CIF_ISP_LSC_B_TABLE_DATA (RKISP1_CIF_ISP_LSC_BASE + 0x0000001C)
+#define RKISP1_CIF_ISP_LSC_GB_TABLE_DATA (RKISP1_CIF_ISP_LSC_BASE + 0x00000020)
+#define RKISP1_CIF_ISP_LSC_XGRAD(n) (RKISP1_CIF_ISP_LSC_BASE + 0x00000024 + (n) * 4)
+#define RKISP1_CIF_ISP_LSC_YGRAD(n) (RKISP1_CIF_ISP_LSC_BASE + 0x00000034 + (n) * 4)
+#define RKISP1_CIF_ISP_LSC_XSIZE(n) (RKISP1_CIF_ISP_LSC_BASE + 0x00000044 + (n) * 4)
+#define RKISP1_CIF_ISP_LSC_YSIZE(n) (RKISP1_CIF_ISP_LSC_BASE + 0x00000054 + (n) * 4)
+#define RKISP1_CIF_ISP_LSC_TABLE_SEL (RKISP1_CIF_ISP_LSC_BASE + 0x00000064)
+#define RKISP1_CIF_ISP_LSC_STATUS (RKISP1_CIF_ISP_LSC_BASE + 0x00000068)
+
+#define RKISP1_CIF_ISP_IS_BASE 0x00002300
+#define RKISP1_CIF_ISP_IS_CTRL (RKISP1_CIF_ISP_IS_BASE + 0x00000000)
+#define RKISP1_CIF_ISP_IS_RECENTER (RKISP1_CIF_ISP_IS_BASE + 0x00000004)
+#define RKISP1_CIF_ISP_IS_H_OFFS (RKISP1_CIF_ISP_IS_BASE + 0x00000008)
+#define RKISP1_CIF_ISP_IS_V_OFFS (RKISP1_CIF_ISP_IS_BASE + 0x0000000C)
+#define RKISP1_CIF_ISP_IS_H_SIZE (RKISP1_CIF_ISP_IS_BASE + 0x00000010)
+#define RKISP1_CIF_ISP_IS_V_SIZE (RKISP1_CIF_ISP_IS_BASE + 0x00000014)
+#define RKISP1_CIF_ISP_IS_MAX_DX (RKISP1_CIF_ISP_IS_BASE + 0x00000018)
+#define RKISP1_CIF_ISP_IS_MAX_DY (RKISP1_CIF_ISP_IS_BASE + 0x0000001C)
+#define RKISP1_CIF_ISP_IS_DISPLACE (RKISP1_CIF_ISP_IS_BASE + 0x00000020)
+#define RKISP1_CIF_ISP_IS_H_OFFS_SHD (RKISP1_CIF_ISP_IS_BASE + 0x00000024)
+#define RKISP1_CIF_ISP_IS_V_OFFS_SHD (RKISP1_CIF_ISP_IS_BASE + 0x00000028)
+#define RKISP1_CIF_ISP_IS_H_SIZE_SHD (RKISP1_CIF_ISP_IS_BASE + 0x0000002C)
+#define RKISP1_CIF_ISP_IS_V_SIZE_SHD (RKISP1_CIF_ISP_IS_BASE + 0x00000030)
+
+#define RKISP1_CIF_ISP_HIST_BASE_V10 0x00002400
+#define RKISP1_CIF_ISP_HIST_PROP_V10 (RKISP1_CIF_ISP_HIST_BASE_V10 + 0x00000000)
+#define RKISP1_CIF_ISP_HIST_H_OFFS_V10 (RKISP1_CIF_ISP_HIST_BASE_V10 + 0x00000004)
+#define RKISP1_CIF_ISP_HIST_V_OFFS_V10 (RKISP1_CIF_ISP_HIST_BASE_V10 + 0x00000008)
+#define RKISP1_CIF_ISP_HIST_H_SIZE_V10 (RKISP1_CIF_ISP_HIST_BASE_V10 + 0x0000000C)
+#define RKISP1_CIF_ISP_HIST_V_SIZE_V10 (RKISP1_CIF_ISP_HIST_BASE_V10 + 0x00000010)
+#define RKISP1_CIF_ISP_HIST_BIN_0_V10 (RKISP1_CIF_ISP_HIST_BASE_V10 + 0x00000014)
+#define RKISP1_CIF_ISP_HIST_BIN_1_V10 (RKISP1_CIF_ISP_HIST_BASE_V10 + 0x00000018)
+#define RKISP1_CIF_ISP_HIST_BIN_2_V10 (RKISP1_CIF_ISP_HIST_BASE_V10 + 0x0000001C)
+#define RKISP1_CIF_ISP_HIST_BIN_3_V10 (RKISP1_CIF_ISP_HIST_BASE_V10 + 0x00000020)
+#define RKISP1_CIF_ISP_HIST_BIN_4_V10 (RKISP1_CIF_ISP_HIST_BASE_V10 + 0x00000024)
+#define RKISP1_CIF_ISP_HIST_BIN_5_V10 (RKISP1_CIF_ISP_HIST_BASE_V10 + 0x00000028)
+#define RKISP1_CIF_ISP_HIST_BIN_6_V10 (RKISP1_CIF_ISP_HIST_BASE_V10 + 0x0000002C)
+#define RKISP1_CIF_ISP_HIST_BIN_7_V10 (RKISP1_CIF_ISP_HIST_BASE_V10 + 0x00000030)
+#define RKISP1_CIF_ISP_HIST_BIN_8_V10 (RKISP1_CIF_ISP_HIST_BASE_V10 + 0x00000034)
+#define RKISP1_CIF_ISP_HIST_BIN_9_V10 (RKISP1_CIF_ISP_HIST_BASE_V10 + 0x00000038)
+#define RKISP1_CIF_ISP_HIST_BIN_10_V10 (RKISP1_CIF_ISP_HIST_BASE_V10 + 0x0000003C)
+#define RKISP1_CIF_ISP_HIST_BIN_11_V10 (RKISP1_CIF_ISP_HIST_BASE_V10 + 0x00000040)
+#define RKISP1_CIF_ISP_HIST_BIN_12_V10 (RKISP1_CIF_ISP_HIST_BASE_V10 + 0x00000044)
+#define RKISP1_CIF_ISP_HIST_BIN_13_V10 (RKISP1_CIF_ISP_HIST_BASE_V10 + 0x00000048)
+#define RKISP1_CIF_ISP_HIST_BIN_14_V10 (RKISP1_CIF_ISP_HIST_BASE_V10 + 0x0000004C)
+#define RKISP1_CIF_ISP_HIST_BIN_15_V10 (RKISP1_CIF_ISP_HIST_BASE_V10 + 0x00000050)
+#define RKISP1_CIF_ISP_HIST_WEIGHT_00TO30_V10 (RKISP1_CIF_ISP_HIST_BASE_V10 + 0x00000054)
+#define RKISP1_CIF_ISP_HIST_WEIGHT_40TO21_V10 (RKISP1_CIF_ISP_HIST_BASE_V10 + 0x00000058)
+#define RKISP1_CIF_ISP_HIST_WEIGHT_31TO12_V10 (RKISP1_CIF_ISP_HIST_BASE_V10 + 0x0000005C)
+#define RKISP1_CIF_ISP_HIST_WEIGHT_22TO03_V10 (RKISP1_CIF_ISP_HIST_BASE_V10 + 0x00000060)
+#define RKISP1_CIF_ISP_HIST_WEIGHT_13TO43_V10 (RKISP1_CIF_ISP_HIST_BASE_V10 + 0x00000064)
+#define RKISP1_CIF_ISP_HIST_WEIGHT_04TO34_V10 (RKISP1_CIF_ISP_HIST_BASE_V10 + 0x00000068)
+#define RKISP1_CIF_ISP_HIST_WEIGHT_44_V10 (RKISP1_CIF_ISP_HIST_BASE_V10 + 0x0000006C)
+
+#define RKISP1_CIF_ISP_FILT_BASE 0x00002500
+#define RKISP1_CIF_ISP_FILT_MODE (RKISP1_CIF_ISP_FILT_BASE + 0x00000000)
+#define RKISP1_CIF_ISP_FILT_THRESH_BL0 (RKISP1_CIF_ISP_FILT_BASE + 0x00000028)
+#define RKISP1_CIF_ISP_FILT_THRESH_BL1 (RKISP1_CIF_ISP_FILT_BASE + 0x0000002c)
+#define RKISP1_CIF_ISP_FILT_THRESH_SH0 (RKISP1_CIF_ISP_FILT_BASE + 0x00000030)
+#define RKISP1_CIF_ISP_FILT_THRESH_SH1 (RKISP1_CIF_ISP_FILT_BASE + 0x00000034)
+#define RKISP1_CIF_ISP_FILT_LUM_WEIGHT (RKISP1_CIF_ISP_FILT_BASE + 0x00000038)
+#define RKISP1_CIF_ISP_FILT_FAC_SH1 (RKISP1_CIF_ISP_FILT_BASE + 0x0000003c)
+#define RKISP1_CIF_ISP_FILT_FAC_SH0 (RKISP1_CIF_ISP_FILT_BASE + 0x00000040)
+#define RKISP1_CIF_ISP_FILT_FAC_MID (RKISP1_CIF_ISP_FILT_BASE + 0x00000044)
+#define RKISP1_CIF_ISP_FILT_FAC_BL0 (RKISP1_CIF_ISP_FILT_BASE + 0x00000048)
+#define RKISP1_CIF_ISP_FILT_FAC_BL1 (RKISP1_CIF_ISP_FILT_BASE + 0x0000004C)
+
+#define RKISP1_CIF_ISP_CAC_BASE 0x00002580
+#define RKISP1_CIF_ISP_CAC_CTRL (RKISP1_CIF_ISP_CAC_BASE + 0x00000000)
+#define RKISP1_CIF_ISP_CAC_COUNT_START (RKISP1_CIF_ISP_CAC_BASE + 0x00000004)
+#define RKISP1_CIF_ISP_CAC_A (RKISP1_CIF_ISP_CAC_BASE + 0x00000008)
+#define RKISP1_CIF_ISP_CAC_B (RKISP1_CIF_ISP_CAC_BASE + 0x0000000C)
+#define RKISP1_CIF_ISP_CAC_C (RKISP1_CIF_ISP_CAC_BASE + 0x00000010)
+#define RKISP1_CIF_ISP_X_NORM (RKISP1_CIF_ISP_CAC_BASE + 0x00000014)
+#define RKISP1_CIF_ISP_Y_NORM (RKISP1_CIF_ISP_CAC_BASE + 0x00000018)
+
+#define RKISP1_CIF_ISP_EXP_BASE 0x00002600
+#define RKISP1_CIF_ISP_EXP_CTRL (RKISP1_CIF_ISP_EXP_BASE + 0x00000000)
+#define RKISP1_CIF_ISP_EXP_H_OFFSET_V10 (RKISP1_CIF_ISP_EXP_BASE + 0x00000004)
+#define RKISP1_CIF_ISP_EXP_V_OFFSET_V10 (RKISP1_CIF_ISP_EXP_BASE + 0x00000008)
+#define RKISP1_CIF_ISP_EXP_H_SIZE_V10 (RKISP1_CIF_ISP_EXP_BASE + 0x0000000C)
+#define RKISP1_CIF_ISP_EXP_V_SIZE_V10 (RKISP1_CIF_ISP_EXP_BASE + 0x00000010)
+#define RKISP1_CIF_ISP_EXP_MEAN_00_V10 (RKISP1_CIF_ISP_EXP_BASE + 0x00000014)
+#define RKISP1_CIF_ISP_EXP_MEAN_10_V10 (RKISP1_CIF_ISP_EXP_BASE + 0x00000018)
+#define RKISP1_CIF_ISP_EXP_MEAN_20_V10 (RKISP1_CIF_ISP_EXP_BASE + 0x0000001c)
+#define RKISP1_CIF_ISP_EXP_MEAN_30_V10 (RKISP1_CIF_ISP_EXP_BASE + 0x00000020)
+#define RKISP1_CIF_ISP_EXP_MEAN_40_V10 (RKISP1_CIF_ISP_EXP_BASE + 0x00000024)
+#define RKISP1_CIF_ISP_EXP_MEAN_01_V10 (RKISP1_CIF_ISP_EXP_BASE + 0x00000028)
+#define RKISP1_CIF_ISP_EXP_MEAN_11_V10 (RKISP1_CIF_ISP_EXP_BASE + 0x0000002c)
+#define RKISP1_CIF_ISP_EXP_MEAN_21_V10 (RKISP1_CIF_ISP_EXP_BASE + 0x00000030)
+#define RKISP1_CIF_ISP_EXP_MEAN_31_V10 (RKISP1_CIF_ISP_EXP_BASE + 0x00000034)
+#define RKISP1_CIF_ISP_EXP_MEAN_41_V10 (RKISP1_CIF_ISP_EXP_BASE + 0x00000038)
+#define RKISP1_CIF_ISP_EXP_MEAN_02_V10 (RKISP1_CIF_ISP_EXP_BASE + 0x0000003c)
+#define RKISP1_CIF_ISP_EXP_MEAN_12_V10 (RKISP1_CIF_ISP_EXP_BASE + 0x00000040)
+#define RKISP1_CIF_ISP_EXP_MEAN_22_V10 (RKISP1_CIF_ISP_EXP_BASE + 0x00000044)
+#define RKISP1_CIF_ISP_EXP_MEAN_32_V10 (RKISP1_CIF_ISP_EXP_BASE + 0x00000048)
+#define RKISP1_CIF_ISP_EXP_MEAN_42_V10 (RKISP1_CIF_ISP_EXP_BASE + 0x0000004c)
+#define RKISP1_CIF_ISP_EXP_MEAN_03_V10 (RKISP1_CIF_ISP_EXP_BASE + 0x00000050)
+#define RKISP1_CIF_ISP_EXP_MEAN_13_V10 (RKISP1_CIF_ISP_EXP_BASE + 0x00000054)
+#define RKISP1_CIF_ISP_EXP_MEAN_23_V10 (RKISP1_CIF_ISP_EXP_BASE + 0x00000058)
+#define RKISP1_CIF_ISP_EXP_MEAN_33_V10 (RKISP1_CIF_ISP_EXP_BASE + 0x0000005c)
+#define RKISP1_CIF_ISP_EXP_MEAN_43_V10 (RKISP1_CIF_ISP_EXP_BASE + 0x00000060)
+#define RKISP1_CIF_ISP_EXP_MEAN_04_V10 (RKISP1_CIF_ISP_EXP_BASE + 0x00000064)
+#define RKISP1_CIF_ISP_EXP_MEAN_14_V10 (RKISP1_CIF_ISP_EXP_BASE + 0x00000068)
+#define RKISP1_CIF_ISP_EXP_MEAN_24_V10 (RKISP1_CIF_ISP_EXP_BASE + 0x0000006c)
+#define RKISP1_CIF_ISP_EXP_MEAN_34_V10 (RKISP1_CIF_ISP_EXP_BASE + 0x00000070)
+#define RKISP1_CIF_ISP_EXP_MEAN_44_V10 (RKISP1_CIF_ISP_EXP_BASE + 0x00000074)
+#define RKISP1_CIF_ISP_EXP_SIZE_V12 (RKISP1_CIF_ISP_EXP_BASE + 0x00000004)
+#define RKISP1_CIF_ISP_EXP_OFFS_V12 (RKISP1_CIF_ISP_EXP_BASE + 0x00000008)
+#define RKISP1_CIF_ISP_EXP_MEAN_V12 (RKISP1_CIF_ISP_EXP_BASE + 0x0000000c)
+
+#define RKISP1_CIF_ISP_BLS_BASE 0x00002700
+#define RKISP1_CIF_ISP_BLS_CTRL (RKISP1_CIF_ISP_BLS_BASE + 0x00000000)
+#define RKISP1_CIF_ISP_BLS_SAMPLES (RKISP1_CIF_ISP_BLS_BASE + 0x00000004)
+#define RKISP1_CIF_ISP_BLS_H1_START (RKISP1_CIF_ISP_BLS_BASE + 0x00000008)
+#define RKISP1_CIF_ISP_BLS_H1_STOP (RKISP1_CIF_ISP_BLS_BASE + 0x0000000c)
+#define RKISP1_CIF_ISP_BLS_V1_START (RKISP1_CIF_ISP_BLS_BASE + 0x00000010)
+#define RKISP1_CIF_ISP_BLS_V1_STOP (RKISP1_CIF_ISP_BLS_BASE + 0x00000014)
+#define RKISP1_CIF_ISP_BLS_H2_START (RKISP1_CIF_ISP_BLS_BASE + 0x00000018)
+#define RKISP1_CIF_ISP_BLS_H2_STOP (RKISP1_CIF_ISP_BLS_BASE + 0x0000001c)
+#define RKISP1_CIF_ISP_BLS_V2_START (RKISP1_CIF_ISP_BLS_BASE + 0x00000020)
+#define RKISP1_CIF_ISP_BLS_V2_STOP (RKISP1_CIF_ISP_BLS_BASE + 0x00000024)
+#define RKISP1_CIF_ISP_BLS_A_FIXED (RKISP1_CIF_ISP_BLS_BASE + 0x00000028)
+#define RKISP1_CIF_ISP_BLS_B_FIXED (RKISP1_CIF_ISP_BLS_BASE + 0x0000002c)
+#define RKISP1_CIF_ISP_BLS_C_FIXED (RKISP1_CIF_ISP_BLS_BASE + 0x00000030)
+#define RKISP1_CIF_ISP_BLS_D_FIXED (RKISP1_CIF_ISP_BLS_BASE + 0x00000034)
+#define RKISP1_CIF_ISP_BLS_A_MEASURED (RKISP1_CIF_ISP_BLS_BASE + 0x00000038)
+#define RKISP1_CIF_ISP_BLS_B_MEASURED (RKISP1_CIF_ISP_BLS_BASE + 0x0000003c)
+#define RKISP1_CIF_ISP_BLS_C_MEASURED (RKISP1_CIF_ISP_BLS_BASE + 0x00000040)
+#define RKISP1_CIF_ISP_BLS_D_MEASURED (RKISP1_CIF_ISP_BLS_BASE + 0x00000044)
+
+#define RKISP1_CIF_ISP_DPF_BASE 0x00002800
+#define RKISP1_CIF_ISP_DPF_MODE (RKISP1_CIF_ISP_DPF_BASE + 0x00000000)
+#define RKISP1_CIF_ISP_DPF_STRENGTH_R (RKISP1_CIF_ISP_DPF_BASE + 0x00000004)
+#define RKISP1_CIF_ISP_DPF_STRENGTH_G (RKISP1_CIF_ISP_DPF_BASE + 0x00000008)
+#define RKISP1_CIF_ISP_DPF_STRENGTH_B (RKISP1_CIF_ISP_DPF_BASE + 0x0000000C)
+#define RKISP1_CIF_ISP_DPF_S_WEIGHT_G_1_4 (RKISP1_CIF_ISP_DPF_BASE + 0x00000010)
+#define RKISP1_CIF_ISP_DPF_S_WEIGHT_G_5_6 (RKISP1_CIF_ISP_DPF_BASE + 0x00000014)
+#define RKISP1_CIF_ISP_DPF_S_WEIGHT_RB_1_4 (RKISP1_CIF_ISP_DPF_BASE + 0x00000018)
+#define RKISP1_CIF_ISP_DPF_S_WEIGHT_RB_5_6 (RKISP1_CIF_ISP_DPF_BASE + 0x0000001C)
+#define RKISP1_CIF_ISP_DPF_NULL_COEFF_0 (RKISP1_CIF_ISP_DPF_BASE + 0x00000020)
+#define RKISP1_CIF_ISP_DPF_NULL_COEFF_1 (RKISP1_CIF_ISP_DPF_BASE + 0x00000024)
+#define RKISP1_CIF_ISP_DPF_NULL_COEFF_2 (RKISP1_CIF_ISP_DPF_BASE + 0x00000028)
+#define RKISP1_CIF_ISP_DPF_NULL_COEFF_3 (RKISP1_CIF_ISP_DPF_BASE + 0x0000002C)
+#define RKISP1_CIF_ISP_DPF_NULL_COEFF_4 (RKISP1_CIF_ISP_DPF_BASE + 0x00000030)
+#define RKISP1_CIF_ISP_DPF_NULL_COEFF_5 (RKISP1_CIF_ISP_DPF_BASE + 0x00000034)
+#define RKISP1_CIF_ISP_DPF_NULL_COEFF_6 (RKISP1_CIF_ISP_DPF_BASE + 0x00000038)
+#define RKISP1_CIF_ISP_DPF_NULL_COEFF_7 (RKISP1_CIF_ISP_DPF_BASE + 0x0000003C)
+#define RKISP1_CIF_ISP_DPF_NULL_COEFF_8 (RKISP1_CIF_ISP_DPF_BASE + 0x00000040)
+#define RKISP1_CIF_ISP_DPF_NULL_COEFF_9 (RKISP1_CIF_ISP_DPF_BASE + 0x00000044)
+#define RKISP1_CIF_ISP_DPF_NULL_COEFF_10 (RKISP1_CIF_ISP_DPF_BASE + 0x00000048)
+#define RKISP1_CIF_ISP_DPF_NULL_COEFF_11 (RKISP1_CIF_ISP_DPF_BASE + 0x0000004C)
+#define RKISP1_CIF_ISP_DPF_NULL_COEFF_12 (RKISP1_CIF_ISP_DPF_BASE + 0x00000050)
+#define RKISP1_CIF_ISP_DPF_NULL_COEFF_13 (RKISP1_CIF_ISP_DPF_BASE + 0x00000054)
+#define RKISP1_CIF_ISP_DPF_NULL_COEFF_14 (RKISP1_CIF_ISP_DPF_BASE + 0x00000058)
+#define RKISP1_CIF_ISP_DPF_NULL_COEFF_15 (RKISP1_CIF_ISP_DPF_BASE + 0x0000005C)
+#define RKISP1_CIF_ISP_DPF_NULL_COEFF_16 (RKISP1_CIF_ISP_DPF_BASE + 0x00000060)
+#define RKISP1_CIF_ISP_DPF_NF_GAIN_R (RKISP1_CIF_ISP_DPF_BASE + 0x00000064)
+#define RKISP1_CIF_ISP_DPF_NF_GAIN_GR (RKISP1_CIF_ISP_DPF_BASE + 0x00000068)
+#define RKISP1_CIF_ISP_DPF_NF_GAIN_GB (RKISP1_CIF_ISP_DPF_BASE + 0x0000006C)
+#define RKISP1_CIF_ISP_DPF_NF_GAIN_B (RKISP1_CIF_ISP_DPF_BASE + 0x00000070)
+
+#define RKISP1_CIF_ISP_DPCC_BASE 0x00002900
+#define RKISP1_CIF_ISP_DPCC_MODE (RKISP1_CIF_ISP_DPCC_BASE + 0x00000000)
+#define RKISP1_CIF_ISP_DPCC_OUTPUT_MODE (RKISP1_CIF_ISP_DPCC_BASE + 0x00000004)
+#define RKISP1_CIF_ISP_DPCC_SET_USE (RKISP1_CIF_ISP_DPCC_BASE + 0x00000008)
+#define RKISP1_CIF_ISP_DPCC_METHODS_SET_1 (RKISP1_CIF_ISP_DPCC_BASE + 0x0000000C)
+#define RKISP1_CIF_ISP_DPCC_METHODS_SET_2 (RKISP1_CIF_ISP_DPCC_BASE + 0x00000010)
+#define RKISP1_CIF_ISP_DPCC_METHODS_SET_3 (RKISP1_CIF_ISP_DPCC_BASE + 0x00000014)
+#define RKISP1_CIF_ISP_DPCC_LINE_THRESH_1 (RKISP1_CIF_ISP_DPCC_BASE + 0x00000018)
+#define RKISP1_CIF_ISP_DPCC_LINE_MAD_FAC_1 (RKISP1_CIF_ISP_DPCC_BASE + 0x0000001C)
+#define RKISP1_CIF_ISP_DPCC_PG_FAC_1 (RKISP1_CIF_ISP_DPCC_BASE + 0x00000020)
+#define RKISP1_CIF_ISP_DPCC_RND_THRESH_1 (RKISP1_CIF_ISP_DPCC_BASE + 0x00000024)
+#define RKISP1_CIF_ISP_DPCC_RG_FAC_1 (RKISP1_CIF_ISP_DPCC_BASE + 0x00000028)
+#define RKISP1_CIF_ISP_DPCC_LINE_THRESH_2 (RKISP1_CIF_ISP_DPCC_BASE + 0x0000002C)
+#define RKISP1_CIF_ISP_DPCC_LINE_MAD_FAC_2 (RKISP1_CIF_ISP_DPCC_BASE + 0x00000030)
+#define RKISP1_CIF_ISP_DPCC_PG_FAC_2 (RKISP1_CIF_ISP_DPCC_BASE + 0x00000034)
+#define RKISP1_CIF_ISP_DPCC_RND_THRESH_2 (RKISP1_CIF_ISP_DPCC_BASE + 0x00000038)
+#define RKISP1_CIF_ISP_DPCC_RG_FAC_2 (RKISP1_CIF_ISP_DPCC_BASE + 0x0000003C)
+#define RKISP1_CIF_ISP_DPCC_LINE_THRESH_3 (RKISP1_CIF_ISP_DPCC_BASE + 0x00000040)
+#define RKISP1_CIF_ISP_DPCC_LINE_MAD_FAC_3 (RKISP1_CIF_ISP_DPCC_BASE + 0x00000044)
+#define RKISP1_CIF_ISP_DPCC_PG_FAC_3 (RKISP1_CIF_ISP_DPCC_BASE + 0x00000048)
+#define RKISP1_CIF_ISP_DPCC_RND_THRESH_3 (RKISP1_CIF_ISP_DPCC_BASE + 0x0000004C)
+#define RKISP1_CIF_ISP_DPCC_RG_FAC_3 (RKISP1_CIF_ISP_DPCC_BASE + 0x00000050)
+#define RKISP1_CIF_ISP_DPCC_RO_LIMITS (RKISP1_CIF_ISP_DPCC_BASE + 0x00000054)
+#define RKISP1_CIF_ISP_DPCC_RND_OFFS (RKISP1_CIF_ISP_DPCC_BASE + 0x00000058)
+#define RKISP1_CIF_ISP_DPCC_BPT_CTRL (RKISP1_CIF_ISP_DPCC_BASE + 0x0000005C)
+#define RKISP1_CIF_ISP_DPCC_BPT_NUMBER (RKISP1_CIF_ISP_DPCC_BASE + 0x00000060)
+#define RKISP1_CIF_ISP_DPCC_BPT_ADDR (RKISP1_CIF_ISP_DPCC_BASE + 0x00000064)
+#define RKISP1_CIF_ISP_DPCC_BPT_DATA (RKISP1_CIF_ISP_DPCC_BASE + 0x00000068)
+
+#define RKISP1_CIF_ISP_WDR_BASE 0x00002A00
+#define RKISP1_CIF_ISP_WDR_CTRL (RKISP1_CIF_ISP_WDR_BASE + 0x00000000)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_1 (RKISP1_CIF_ISP_WDR_BASE + 0x00000004)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_2 (RKISP1_CIF_ISP_WDR_BASE + 0x00000008)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_3 (RKISP1_CIF_ISP_WDR_BASE + 0x0000000C)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_4 (RKISP1_CIF_ISP_WDR_BASE + 0x00000010)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_0 (RKISP1_CIF_ISP_WDR_BASE + 0x00000014)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_1 (RKISP1_CIF_ISP_WDR_BASE + 0x00000018)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_2 (RKISP1_CIF_ISP_WDR_BASE + 0x0000001C)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_3 (RKISP1_CIF_ISP_WDR_BASE + 0x00000020)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_4 (RKISP1_CIF_ISP_WDR_BASE + 0x00000024)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_5 (RKISP1_CIF_ISP_WDR_BASE + 0x00000028)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_6 (RKISP1_CIF_ISP_WDR_BASE + 0x0000002C)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_7 (RKISP1_CIF_ISP_WDR_BASE + 0x00000030)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_8 (RKISP1_CIF_ISP_WDR_BASE + 0x00000034)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_9 (RKISP1_CIF_ISP_WDR_BASE + 0x00000038)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_10 (RKISP1_CIF_ISP_WDR_BASE + 0x0000003C)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_11 (RKISP1_CIF_ISP_WDR_BASE + 0x00000040)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_12 (RKISP1_CIF_ISP_WDR_BASE + 0x00000044)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_13 (RKISP1_CIF_ISP_WDR_BASE + 0x00000048)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_14 (RKISP1_CIF_ISP_WDR_BASE + 0x0000004C)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_15 (RKISP1_CIF_ISP_WDR_BASE + 0x00000050)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_16 (RKISP1_CIF_ISP_WDR_BASE + 0x00000054)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_17 (RKISP1_CIF_ISP_WDR_BASE + 0x00000058)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_18 (RKISP1_CIF_ISP_WDR_BASE + 0x0000005C)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_19 (RKISP1_CIF_ISP_WDR_BASE + 0x00000060)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_20 (RKISP1_CIF_ISP_WDR_BASE + 0x00000064)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_21 (RKISP1_CIF_ISP_WDR_BASE + 0x00000068)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_22 (RKISP1_CIF_ISP_WDR_BASE + 0x0000006C)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_23 (RKISP1_CIF_ISP_WDR_BASE + 0x00000070)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_24 (RKISP1_CIF_ISP_WDR_BASE + 0x00000074)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_25 (RKISP1_CIF_ISP_WDR_BASE + 0x00000078)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_26 (RKISP1_CIF_ISP_WDR_BASE + 0x0000007C)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_27 (RKISP1_CIF_ISP_WDR_BASE + 0x00000080)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_28 (RKISP1_CIF_ISP_WDR_BASE + 0x00000084)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_29 (RKISP1_CIF_ISP_WDR_BASE + 0x00000088)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_30 (RKISP1_CIF_ISP_WDR_BASE + 0x0000008C)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_31 (RKISP1_CIF_ISP_WDR_BASE + 0x00000090)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_32 (RKISP1_CIF_ISP_WDR_BASE + 0x00000094)
+#define RKISP1_CIF_ISP_WDR_OFFSET (RKISP1_CIF_ISP_WDR_BASE + 0x00000098)
+#define RKISP1_CIF_ISP_WDR_DELTAMIN (RKISP1_CIF_ISP_WDR_BASE + 0x0000009C)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_1_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000A0)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_2_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000A4)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_3_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000A8)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_4_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000AC)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_0_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000B0)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_1_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000B4)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_2_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000B8)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_3_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000BC)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_4_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000C0)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_5_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000C4)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_6_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000C8)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_7_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000CC)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_8_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000D0)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_9_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000D4)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_10_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000D8)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_11_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000DC)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_12_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000E0)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_13_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000E4)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_14_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000E8)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_15_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000EC)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_16_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000F0)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_17_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000F4)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_18_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000F8)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_19_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000FC)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_20_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000100)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_21_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000104)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_22_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000108)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_23_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x0000010C)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_24_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000110)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_25_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000114)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_26_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000118)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_27_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x0000011C)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_28_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000120)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_29_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000124)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_30_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000128)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_31_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x0000012C)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_32_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000130)
+
+#define RKISP1_CIF_ISP_HIST_BASE_V12 0x00002C00
+#define RKISP1_CIF_ISP_HIST_CTRL_V12 (RKISP1_CIF_ISP_HIST_BASE_V12 + 0x00000000)
+#define RKISP1_CIF_ISP_HIST_SIZE_V12 (RKISP1_CIF_ISP_HIST_BASE_V12 + 0x00000004)
+#define RKISP1_CIF_ISP_HIST_OFFS_V12 (RKISP1_CIF_ISP_HIST_BASE_V12 + 0x00000008)
+#define RKISP1_CIF_ISP_HIST_DBG1_V12 (RKISP1_CIF_ISP_HIST_BASE_V12 + 0x0000000C)
+#define RKISP1_CIF_ISP_HIST_DBG2_V12 (RKISP1_CIF_ISP_HIST_BASE_V12 + 0x0000001C)
+#define RKISP1_CIF_ISP_HIST_DBG3_V12 (RKISP1_CIF_ISP_HIST_BASE_V12 + 0x0000002C)
+#define RKISP1_CIF_ISP_HIST_WEIGHT_V12 (RKISP1_CIF_ISP_HIST_BASE_V12 + 0x0000003C)
+#define RKISP1_CIF_ISP_HIST_BIN_V12 (RKISP1_CIF_ISP_HIST_BASE_V12 + 0x00000120)
+
+#define RKISP1_CIF_ISP_VSM_BASE 0x00002F00
+#define RKISP1_CIF_ISP_VSM_MODE (RKISP1_CIF_ISP_VSM_BASE + 0x00000000)
+#define RKISP1_CIF_ISP_VSM_H_OFFS (RKISP1_CIF_ISP_VSM_BASE + 0x00000004)
+#define RKISP1_CIF_ISP_VSM_V_OFFS (RKISP1_CIF_ISP_VSM_BASE + 0x00000008)
+#define RKISP1_CIF_ISP_VSM_H_SIZE (RKISP1_CIF_ISP_VSM_BASE + 0x0000000C)
+#define RKISP1_CIF_ISP_VSM_V_SIZE (RKISP1_CIF_ISP_VSM_BASE + 0x00000010)
+#define RKISP1_CIF_ISP_VSM_H_SEGMENTS (RKISP1_CIF_ISP_VSM_BASE + 0x00000014)
+#define RKISP1_CIF_ISP_VSM_V_SEGMENTS (RKISP1_CIF_ISP_VSM_BASE + 0x00000018)
+#define RKISP1_CIF_ISP_VSM_DELTA_H (RKISP1_CIF_ISP_VSM_BASE + 0x0000001C)
+#define RKISP1_CIF_ISP_VSM_DELTA_V (RKISP1_CIF_ISP_VSM_BASE + 0x00000020)
+
+#define RKISP1_CIF_ISP_CSI0_BASE 0x00007000
+#define RKISP1_CIF_ISP_CSI0_CTRL0 (RKISP1_CIF_ISP_CSI0_BASE + 0x00000000)
+
+#endif /* _RKISP1_REGS_H */
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c
new file mode 100644
index 0000000000..c15ae02181
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c
@@ -0,0 +1,817 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Rockchip ISP1 Driver - V4l resizer device
+ *
+ * Copyright (C) 2019 Collabora, Ltd.
+ *
+ * Based on Rockchip ISP1 driver by Rockchip Electronics Co., Ltd.
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ */
+
+#include "rkisp1-common.h"
+
+#define RKISP1_RSZ_SP_DEV_NAME RKISP1_DRIVER_NAME "_resizer_selfpath"
+#define RKISP1_RSZ_MP_DEV_NAME RKISP1_DRIVER_NAME "_resizer_mainpath"
+
+#define RKISP1_DEF_FMT MEDIA_BUS_FMT_YUYV8_2X8
+#define RKISP1_DEF_PIXEL_ENC V4L2_PIXEL_ENC_YUV
+
+struct rkisp1_rsz_yuv_mbus_info {
+ u32 mbus_code;
+ u32 hdiv;
+ u32 vdiv;
+};
+
+static const struct rkisp1_rsz_yuv_mbus_info rkisp1_rsz_yuv_src_formats[] = {
+ {
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, /* YUV422 */
+ .hdiv = 2,
+ .vdiv = 1,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_1_5X8, /* YUV420 */
+ .hdiv = 2,
+ .vdiv = 2,
+ },
+};
+
+static const struct rkisp1_rsz_yuv_mbus_info *rkisp1_rsz_get_yuv_mbus_info(u32 mbus_code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(rkisp1_rsz_yuv_src_formats); i++) {
+ if (rkisp1_rsz_yuv_src_formats[i].mbus_code == mbus_code)
+ return &rkisp1_rsz_yuv_src_formats[i];
+ }
+
+ return NULL;
+}
+
+enum rkisp1_shadow_regs_when {
+ RKISP1_SHADOW_REGS_SYNC,
+ RKISP1_SHADOW_REGS_ASYNC,
+};
+
+struct rkisp1_rsz_config {
+ /* constrains */
+ const int max_rsz_width;
+ const int max_rsz_height;
+ const int min_rsz_width;
+ const int min_rsz_height;
+ /* registers */
+ struct {
+ u32 ctrl;
+ u32 yuvmode_mask;
+ u32 rawmode_mask;
+ u32 h_offset;
+ u32 v_offset;
+ u32 h_size;
+ u32 v_size;
+ } dual_crop;
+};
+
+static const struct rkisp1_rsz_config rkisp1_rsz_config_mp = {
+ /* constraints */
+ .max_rsz_width = RKISP1_RSZ_MP_SRC_MAX_WIDTH,
+ .max_rsz_height = RKISP1_RSZ_MP_SRC_MAX_HEIGHT,
+ .min_rsz_width = RKISP1_RSZ_SRC_MIN_WIDTH,
+ .min_rsz_height = RKISP1_RSZ_SRC_MIN_HEIGHT,
+ /* registers */
+ .dual_crop = {
+ .ctrl = RKISP1_CIF_DUAL_CROP_CTRL,
+ .yuvmode_mask = RKISP1_CIF_DUAL_CROP_MP_MODE_YUV,
+ .rawmode_mask = RKISP1_CIF_DUAL_CROP_MP_MODE_RAW,
+ .h_offset = RKISP1_CIF_DUAL_CROP_M_H_OFFS,
+ .v_offset = RKISP1_CIF_DUAL_CROP_M_V_OFFS,
+ .h_size = RKISP1_CIF_DUAL_CROP_M_H_SIZE,
+ .v_size = RKISP1_CIF_DUAL_CROP_M_V_SIZE,
+ },
+};
+
+static const struct rkisp1_rsz_config rkisp1_rsz_config_sp = {
+ /* constraints */
+ .max_rsz_width = RKISP1_RSZ_SP_SRC_MAX_WIDTH,
+ .max_rsz_height = RKISP1_RSZ_SP_SRC_MAX_HEIGHT,
+ .min_rsz_width = RKISP1_RSZ_SRC_MIN_WIDTH,
+ .min_rsz_height = RKISP1_RSZ_SRC_MIN_HEIGHT,
+ /* registers */
+ .dual_crop = {
+ .ctrl = RKISP1_CIF_DUAL_CROP_CTRL,
+ .yuvmode_mask = RKISP1_CIF_DUAL_CROP_SP_MODE_YUV,
+ .rawmode_mask = RKISP1_CIF_DUAL_CROP_SP_MODE_RAW,
+ .h_offset = RKISP1_CIF_DUAL_CROP_S_H_OFFS,
+ .v_offset = RKISP1_CIF_DUAL_CROP_S_V_OFFS,
+ .h_size = RKISP1_CIF_DUAL_CROP_S_H_SIZE,
+ .v_size = RKISP1_CIF_DUAL_CROP_S_V_SIZE,
+ },
+};
+
+static inline u32 rkisp1_rsz_read(struct rkisp1_resizer *rsz, u32 offset)
+{
+ return rkisp1_read(rsz->rkisp1, rsz->regs_base + offset);
+}
+
+static inline void rkisp1_rsz_write(struct rkisp1_resizer *rsz, u32 offset,
+ u32 value)
+{
+ rkisp1_write(rsz->rkisp1, rsz->regs_base + offset, value);
+}
+
+static struct v4l2_mbus_framefmt *
+rkisp1_rsz_get_pad_fmt(struct rkisp1_resizer *rsz,
+ struct v4l2_subdev_state *sd_state,
+ unsigned int pad, u32 which)
+{
+ struct v4l2_subdev_state state = {
+ .pads = rsz->pad_cfg,
+ };
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_format(&rsz->sd, sd_state, pad);
+ else
+ return v4l2_subdev_get_try_format(&rsz->sd, &state, pad);
+}
+
+static struct v4l2_rect *
+rkisp1_rsz_get_pad_crop(struct rkisp1_resizer *rsz,
+ struct v4l2_subdev_state *sd_state,
+ unsigned int pad, u32 which)
+{
+ struct v4l2_subdev_state state = {
+ .pads = rsz->pad_cfg,
+ };
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_crop(&rsz->sd, sd_state, pad);
+ else
+ return v4l2_subdev_get_try_crop(&rsz->sd, &state, pad);
+}
+
+/* ----------------------------------------------------------------------------
+ * Dual crop hw configs
+ */
+
+static void rkisp1_dcrop_disable(struct rkisp1_resizer *rsz,
+ enum rkisp1_shadow_regs_when when)
+{
+ u32 dc_ctrl = rkisp1_read(rsz->rkisp1, rsz->config->dual_crop.ctrl);
+ u32 mask = ~(rsz->config->dual_crop.yuvmode_mask |
+ rsz->config->dual_crop.rawmode_mask);
+
+ dc_ctrl &= mask;
+ if (when == RKISP1_SHADOW_REGS_ASYNC)
+ dc_ctrl |= RKISP1_CIF_DUAL_CROP_GEN_CFG_UPD;
+ else
+ dc_ctrl |= RKISP1_CIF_DUAL_CROP_CFG_UPD;
+ rkisp1_write(rsz->rkisp1, rsz->config->dual_crop.ctrl, dc_ctrl);
+}
+
+/* configure dual-crop unit */
+static void rkisp1_dcrop_config(struct rkisp1_resizer *rsz)
+{
+ struct rkisp1_device *rkisp1 = rsz->rkisp1;
+ struct v4l2_mbus_framefmt *sink_fmt;
+ struct v4l2_rect *sink_crop;
+ u32 dc_ctrl;
+
+ sink_crop = rkisp1_rsz_get_pad_crop(rsz, NULL, RKISP1_RSZ_PAD_SINK,
+ V4L2_SUBDEV_FORMAT_ACTIVE);
+ sink_fmt = rkisp1_rsz_get_pad_fmt(rsz, NULL, RKISP1_RSZ_PAD_SINK,
+ V4L2_SUBDEV_FORMAT_ACTIVE);
+
+ if (sink_crop->width == sink_fmt->width &&
+ sink_crop->height == sink_fmt->height &&
+ sink_crop->left == 0 && sink_crop->top == 0) {
+ rkisp1_dcrop_disable(rsz, RKISP1_SHADOW_REGS_SYNC);
+ dev_dbg(rkisp1->dev, "capture %d crop disabled\n", rsz->id);
+ return;
+ }
+
+ dc_ctrl = rkisp1_read(rkisp1, rsz->config->dual_crop.ctrl);
+ rkisp1_write(rkisp1, rsz->config->dual_crop.h_offset, sink_crop->left);
+ rkisp1_write(rkisp1, rsz->config->dual_crop.v_offset, sink_crop->top);
+ rkisp1_write(rkisp1, rsz->config->dual_crop.h_size, sink_crop->width);
+ rkisp1_write(rkisp1, rsz->config->dual_crop.v_size, sink_crop->height);
+ dc_ctrl |= rsz->config->dual_crop.yuvmode_mask;
+ dc_ctrl |= RKISP1_CIF_DUAL_CROP_CFG_UPD;
+ rkisp1_write(rkisp1, rsz->config->dual_crop.ctrl, dc_ctrl);
+
+ dev_dbg(rkisp1->dev, "stream %d crop: %dx%d -> %dx%d\n", rsz->id,
+ sink_fmt->width, sink_fmt->height,
+ sink_crop->width, sink_crop->height);
+}
+
+/* ----------------------------------------------------------------------------
+ * Resizer hw configs
+ */
+
+static void rkisp1_rsz_update_shadow(struct rkisp1_resizer *rsz,
+ enum rkisp1_shadow_regs_when when)
+{
+ u32 ctrl_cfg = rkisp1_rsz_read(rsz, RKISP1_CIF_RSZ_CTRL);
+
+ if (when == RKISP1_SHADOW_REGS_ASYNC)
+ ctrl_cfg |= RKISP1_CIF_RSZ_CTRL_CFG_UPD_AUTO;
+ else
+ ctrl_cfg |= RKISP1_CIF_RSZ_CTRL_CFG_UPD;
+
+ rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_CTRL, ctrl_cfg);
+}
+
+static u32 rkisp1_rsz_calc_ratio(u32 len_sink, u32 len_src)
+{
+ if (len_sink < len_src)
+ return ((len_sink - 1) * RKISP1_CIF_RSZ_SCALER_FACTOR) /
+ (len_src - 1);
+
+ return ((len_src - 1) * RKISP1_CIF_RSZ_SCALER_FACTOR) /
+ (len_sink - 1) + 1;
+}
+
+static void rkisp1_rsz_disable(struct rkisp1_resizer *rsz,
+ enum rkisp1_shadow_regs_when when)
+{
+ rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_CTRL, 0);
+
+ if (when == RKISP1_SHADOW_REGS_SYNC)
+ rkisp1_rsz_update_shadow(rsz, when);
+}
+
+static void rkisp1_rsz_config_regs(struct rkisp1_resizer *rsz,
+ struct v4l2_rect *sink_y,
+ struct v4l2_rect *sink_c,
+ struct v4l2_rect *src_y,
+ struct v4l2_rect *src_c,
+ enum rkisp1_shadow_regs_when when)
+{
+ u32 ratio, rsz_ctrl = 0;
+ unsigned int i;
+
+ /* No phase offset */
+ rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_PHASE_HY, 0);
+ rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_PHASE_HC, 0);
+ rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_PHASE_VY, 0);
+ rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_PHASE_VC, 0);
+
+ /* Linear interpolation */
+ for (i = 0; i < 64; i++) {
+ rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_SCALE_LUT_ADDR, i);
+ rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_SCALE_LUT, i);
+ }
+
+ if (sink_y->width != src_y->width) {
+ rsz_ctrl |= RKISP1_CIF_RSZ_CTRL_SCALE_HY_ENABLE;
+ if (sink_y->width < src_y->width)
+ rsz_ctrl |= RKISP1_CIF_RSZ_CTRL_SCALE_HY_UP;
+ ratio = rkisp1_rsz_calc_ratio(sink_y->width, src_y->width);
+ rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_SCALE_HY, ratio);
+ }
+
+ if (sink_c->width != src_c->width) {
+ rsz_ctrl |= RKISP1_CIF_RSZ_CTRL_SCALE_HC_ENABLE;
+ if (sink_c->width < src_c->width)
+ rsz_ctrl |= RKISP1_CIF_RSZ_CTRL_SCALE_HC_UP;
+ ratio = rkisp1_rsz_calc_ratio(sink_c->width, src_c->width);
+ rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_SCALE_HCB, ratio);
+ rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_SCALE_HCR, ratio);
+ }
+
+ if (sink_y->height != src_y->height) {
+ rsz_ctrl |= RKISP1_CIF_RSZ_CTRL_SCALE_VY_ENABLE;
+ if (sink_y->height < src_y->height)
+ rsz_ctrl |= RKISP1_CIF_RSZ_CTRL_SCALE_VY_UP;
+ ratio = rkisp1_rsz_calc_ratio(sink_y->height, src_y->height);
+ rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_SCALE_VY, ratio);
+ }
+
+ if (sink_c->height != src_c->height) {
+ rsz_ctrl |= RKISP1_CIF_RSZ_CTRL_SCALE_VC_ENABLE;
+ if (sink_c->height < src_c->height)
+ rsz_ctrl |= RKISP1_CIF_RSZ_CTRL_SCALE_VC_UP;
+ ratio = rkisp1_rsz_calc_ratio(sink_c->height, src_c->height);
+ rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_SCALE_VC, ratio);
+ }
+
+ rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_CTRL, rsz_ctrl);
+
+ rkisp1_rsz_update_shadow(rsz, when);
+}
+
+static void rkisp1_rsz_config(struct rkisp1_resizer *rsz,
+ enum rkisp1_shadow_regs_when when)
+{
+ const struct rkisp1_rsz_yuv_mbus_info *sink_yuv_info, *src_yuv_info;
+ struct v4l2_rect sink_y, sink_c, src_y, src_c;
+ struct v4l2_mbus_framefmt *src_fmt, *sink_fmt;
+ struct v4l2_rect *sink_crop;
+
+ sink_crop = rkisp1_rsz_get_pad_crop(rsz, NULL, RKISP1_RSZ_PAD_SINK,
+ V4L2_SUBDEV_FORMAT_ACTIVE);
+ src_fmt = rkisp1_rsz_get_pad_fmt(rsz, NULL, RKISP1_RSZ_PAD_SRC,
+ V4L2_SUBDEV_FORMAT_ACTIVE);
+ src_yuv_info = rkisp1_rsz_get_yuv_mbus_info(src_fmt->code);
+ sink_fmt = rkisp1_rsz_get_pad_fmt(rsz, NULL, RKISP1_RSZ_PAD_SINK,
+ V4L2_SUBDEV_FORMAT_ACTIVE);
+ sink_yuv_info = rkisp1_rsz_get_yuv_mbus_info(sink_fmt->code);
+
+ /*
+ * The resizer only works on yuv formats,
+ * so return if it is bayer format.
+ */
+ if (rsz->pixel_enc == V4L2_PIXEL_ENC_BAYER) {
+ rkisp1_rsz_disable(rsz, when);
+ return;
+ }
+
+ sink_y.width = sink_crop->width;
+ sink_y.height = sink_crop->height;
+ src_y.width = src_fmt->width;
+ src_y.height = src_fmt->height;
+
+ sink_c.width = sink_y.width / sink_yuv_info->hdiv;
+ sink_c.height = sink_y.height / sink_yuv_info->vdiv;
+
+ /*
+ * The resizer is used not only to change the dimensions of the frame
+ * but also to change the scale for YUV formats,
+ * (4:2:2 -> 4:2:0 for example). So the width/height of the CbCr
+ * streams should be set according to the media bus format in the src pad.
+ */
+ src_c.width = src_y.width / src_yuv_info->hdiv;
+ src_c.height = src_y.height / src_yuv_info->vdiv;
+
+ if (sink_c.width == src_c.width && sink_c.height == src_c.height) {
+ rkisp1_rsz_disable(rsz, when);
+ return;
+ }
+
+ dev_dbg(rsz->rkisp1->dev, "stream %d rsz/scale: %dx%d -> %dx%d\n",
+ rsz->id, sink_crop->width, sink_crop->height,
+ src_fmt->width, src_fmt->height);
+ dev_dbg(rsz->rkisp1->dev, "chroma scaling %dx%d -> %dx%d\n",
+ sink_c.width, sink_c.height, src_c.width, src_c.height);
+
+ /* set values in the hw */
+ rkisp1_rsz_config_regs(rsz, &sink_y, &sink_c, &src_y, &src_c, when);
+}
+
+/* ----------------------------------------------------------------------------
+ * Subdev pad operations
+ */
+
+static int rkisp1_rsz_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct rkisp1_resizer *rsz =
+ container_of(sd, struct rkisp1_resizer, sd);
+ struct v4l2_subdev_pad_config dummy_cfg;
+ struct v4l2_subdev_state pad_state = {
+ .pads = &dummy_cfg
+ };
+ u32 pad = code->pad;
+ int ret;
+
+ if (code->pad == RKISP1_RSZ_PAD_SRC) {
+ /* supported mbus codes on the src are the same as in the capture */
+ struct rkisp1_capture *cap = &rsz->rkisp1->capture_devs[rsz->id];
+
+ return rkisp1_cap_enum_mbus_codes(cap, code);
+ }
+
+ /*
+ * The selfpath capture doesn't support bayer formats. Therefore the selfpath resizer
+ * should support only YUV422 on the sink pad
+ */
+ if (rsz->id == RKISP1_SELFPATH) {
+ if (code->index > 0)
+ return -EINVAL;
+ code->code = MEDIA_BUS_FMT_YUYV8_2X8;
+ return 0;
+ }
+
+ /* supported mbus codes on the sink pad are the same as isp src pad */
+ code->pad = RKISP1_ISP_PAD_SOURCE_VIDEO;
+ ret = v4l2_subdev_call(&rsz->rkisp1->isp.sd, pad, enum_mbus_code,
+ &pad_state, code);
+
+ /* restore pad */
+ code->pad = pad;
+ code->flags = 0;
+ return ret;
+}
+
+static int rkisp1_rsz_init_config(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state)
+{
+ struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
+ struct v4l2_rect *sink_crop;
+
+ sink_fmt = v4l2_subdev_get_try_format(sd, sd_state,
+ RKISP1_RSZ_PAD_SRC);
+ sink_fmt->width = RKISP1_DEFAULT_WIDTH;
+ sink_fmt->height = RKISP1_DEFAULT_HEIGHT;
+ sink_fmt->field = V4L2_FIELD_NONE;
+ sink_fmt->code = RKISP1_DEF_FMT;
+ sink_fmt->colorspace = V4L2_COLORSPACE_SRGB;
+ sink_fmt->xfer_func = V4L2_XFER_FUNC_SRGB;
+ sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ sink_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE;
+
+ sink_crop = v4l2_subdev_get_try_crop(sd, sd_state,
+ RKISP1_RSZ_PAD_SINK);
+ sink_crop->width = RKISP1_DEFAULT_WIDTH;
+ sink_crop->height = RKISP1_DEFAULT_HEIGHT;
+ sink_crop->left = 0;
+ sink_crop->top = 0;
+
+ src_fmt = v4l2_subdev_get_try_format(sd, sd_state,
+ RKISP1_RSZ_PAD_SINK);
+ *src_fmt = *sink_fmt;
+
+ /* NOTE: there is no crop in the source pad, only in the sink */
+
+ return 0;
+}
+
+static void rkisp1_rsz_set_src_fmt(struct rkisp1_resizer *rsz,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_mbus_framefmt *format,
+ unsigned int which)
+{
+ const struct rkisp1_mbus_info *sink_mbus_info;
+ struct v4l2_mbus_framefmt *src_fmt, *sink_fmt;
+
+ sink_fmt = rkisp1_rsz_get_pad_fmt(rsz, sd_state, RKISP1_RSZ_PAD_SINK,
+ which);
+ src_fmt = rkisp1_rsz_get_pad_fmt(rsz, sd_state, RKISP1_RSZ_PAD_SRC,
+ which);
+ sink_mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code);
+
+ /* for YUV formats, userspace can change the mbus code on the src pad if it is supported */
+ if (sink_mbus_info->pixel_enc == V4L2_PIXEL_ENC_YUV &&
+ rkisp1_rsz_get_yuv_mbus_info(format->code))
+ src_fmt->code = format->code;
+
+ src_fmt->width = clamp_t(u32, format->width,
+ rsz->config->min_rsz_width,
+ rsz->config->max_rsz_width);
+ src_fmt->height = clamp_t(u32, format->height,
+ rsz->config->min_rsz_height,
+ rsz->config->max_rsz_height);
+
+ *format = *src_fmt;
+}
+
+static void rkisp1_rsz_set_sink_crop(struct rkisp1_resizer *rsz,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_rect *r,
+ unsigned int which)
+{
+ const struct rkisp1_mbus_info *mbus_info;
+ struct v4l2_mbus_framefmt *sink_fmt;
+ struct v4l2_rect *sink_crop;
+
+ sink_fmt = rkisp1_rsz_get_pad_fmt(rsz, sd_state, RKISP1_RSZ_PAD_SINK,
+ which);
+ sink_crop = rkisp1_rsz_get_pad_crop(rsz, sd_state,
+ RKISP1_RSZ_PAD_SINK,
+ which);
+
+ /* Not crop for MP bayer raw data */
+ mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code);
+
+ if (rsz->id == RKISP1_MAINPATH &&
+ mbus_info->pixel_enc == V4L2_PIXEL_ENC_BAYER) {
+ sink_crop->left = 0;
+ sink_crop->top = 0;
+ sink_crop->width = sink_fmt->width;
+ sink_crop->height = sink_fmt->height;
+
+ *r = *sink_crop;
+ return;
+ }
+
+ sink_crop->left = ALIGN(r->left, 2);
+ sink_crop->width = ALIGN(r->width, 2);
+ sink_crop->top = r->top;
+ sink_crop->height = r->height;
+ rkisp1_sd_adjust_crop(sink_crop, sink_fmt);
+
+ *r = *sink_crop;
+}
+
+static void rkisp1_rsz_set_sink_fmt(struct rkisp1_resizer *rsz,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_mbus_framefmt *format,
+ unsigned int which)
+{
+ const struct rkisp1_mbus_info *mbus_info;
+ struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
+ struct v4l2_rect *sink_crop;
+ bool is_yuv;
+
+ sink_fmt = rkisp1_rsz_get_pad_fmt(rsz, sd_state, RKISP1_RSZ_PAD_SINK,
+ which);
+ src_fmt = rkisp1_rsz_get_pad_fmt(rsz, sd_state, RKISP1_RSZ_PAD_SRC,
+ which);
+ sink_crop = rkisp1_rsz_get_pad_crop(rsz, sd_state,
+ RKISP1_RSZ_PAD_SINK,
+ which);
+ if (rsz->id == RKISP1_SELFPATH)
+ sink_fmt->code = MEDIA_BUS_FMT_YUYV8_2X8;
+ else
+ sink_fmt->code = format->code;
+
+ mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code);
+ if (!mbus_info || !(mbus_info->direction & RKISP1_ISP_SD_SRC)) {
+ sink_fmt->code = RKISP1_DEF_FMT;
+ mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code);
+ }
+ if (which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ rsz->pixel_enc = mbus_info->pixel_enc;
+
+ sink_fmt->width = clamp_t(u32, format->width,
+ RKISP1_ISP_MIN_WIDTH,
+ RKISP1_ISP_MAX_WIDTH);
+ sink_fmt->height = clamp_t(u32, format->height,
+ RKISP1_ISP_MIN_HEIGHT,
+ RKISP1_ISP_MAX_HEIGHT);
+
+ /*
+ * Adjust the color space fields. Accept any color primaries and
+ * transfer function for both YUV and Bayer. For YUV any YCbCr encoding
+ * and quantization range is also accepted. For Bayer formats, the YCbCr
+ * encoding isn't applicable, and the quantization range can only be
+ * full.
+ */
+ is_yuv = mbus_info->pixel_enc == V4L2_PIXEL_ENC_YUV;
+
+ sink_fmt->colorspace = format->colorspace ? :
+ (is_yuv ? V4L2_COLORSPACE_SRGB :
+ V4L2_COLORSPACE_RAW);
+ sink_fmt->xfer_func = format->xfer_func ? :
+ V4L2_MAP_XFER_FUNC_DEFAULT(sink_fmt->colorspace);
+ if (is_yuv) {
+ sink_fmt->ycbcr_enc = format->ycbcr_enc ? :
+ V4L2_MAP_YCBCR_ENC_DEFAULT(sink_fmt->colorspace);
+ sink_fmt->quantization = format->quantization ? :
+ V4L2_MAP_QUANTIZATION_DEFAULT(false, sink_fmt->colorspace,
+ sink_fmt->ycbcr_enc);
+ } else {
+ /*
+ * The YCbCr encoding isn't applicable for non-YUV formats, but
+ * V4L2 has no "no encoding" value. Hardcode it to Rec. 601, it
+ * should be ignored by userspace.
+ */
+ sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ sink_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ }
+
+ *format = *sink_fmt;
+
+ /* Propagate the media bus code and color space to the source pad. */
+ src_fmt->code = sink_fmt->code;
+ src_fmt->colorspace = sink_fmt->colorspace;
+ src_fmt->xfer_func = sink_fmt->xfer_func;
+ src_fmt->ycbcr_enc = sink_fmt->ycbcr_enc;
+ src_fmt->quantization = sink_fmt->quantization;
+
+ /* Update sink crop */
+ rkisp1_rsz_set_sink_crop(rsz, sd_state, sink_crop, which);
+}
+
+static int rkisp1_rsz_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct rkisp1_resizer *rsz =
+ container_of(sd, struct rkisp1_resizer, sd);
+
+ mutex_lock(&rsz->ops_lock);
+ fmt->format = *rkisp1_rsz_get_pad_fmt(rsz, sd_state, fmt->pad,
+ fmt->which);
+ mutex_unlock(&rsz->ops_lock);
+ return 0;
+}
+
+static int rkisp1_rsz_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct rkisp1_resizer *rsz =
+ container_of(sd, struct rkisp1_resizer, sd);
+
+ mutex_lock(&rsz->ops_lock);
+ if (fmt->pad == RKISP1_RSZ_PAD_SINK)
+ rkisp1_rsz_set_sink_fmt(rsz, sd_state, &fmt->format,
+ fmt->which);
+ else
+ rkisp1_rsz_set_src_fmt(rsz, sd_state, &fmt->format,
+ fmt->which);
+
+ mutex_unlock(&rsz->ops_lock);
+ return 0;
+}
+
+static int rkisp1_rsz_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_selection *sel)
+{
+ struct rkisp1_resizer *rsz =
+ container_of(sd, struct rkisp1_resizer, sd);
+ struct v4l2_mbus_framefmt *mf_sink;
+ int ret = 0;
+
+ if (sel->pad == RKISP1_RSZ_PAD_SRC)
+ return -EINVAL;
+
+ mutex_lock(&rsz->ops_lock);
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ mf_sink = rkisp1_rsz_get_pad_fmt(rsz, sd_state,
+ RKISP1_RSZ_PAD_SINK,
+ sel->which);
+ sel->r.height = mf_sink->height;
+ sel->r.width = mf_sink->width;
+ sel->r.left = 0;
+ sel->r.top = 0;
+ break;
+ case V4L2_SEL_TGT_CROP:
+ sel->r = *rkisp1_rsz_get_pad_crop(rsz, sd_state,
+ RKISP1_RSZ_PAD_SINK,
+ sel->which);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ mutex_unlock(&rsz->ops_lock);
+ return ret;
+}
+
+static int rkisp1_rsz_set_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_selection *sel)
+{
+ struct rkisp1_resizer *rsz =
+ container_of(sd, struct rkisp1_resizer, sd);
+
+ if (sel->target != V4L2_SEL_TGT_CROP || sel->pad == RKISP1_RSZ_PAD_SRC)
+ return -EINVAL;
+
+ dev_dbg(rsz->rkisp1->dev, "%s: pad: %d sel(%d,%d)/%dx%d\n", __func__,
+ sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height);
+
+ mutex_lock(&rsz->ops_lock);
+ rkisp1_rsz_set_sink_crop(rsz, sd_state, &sel->r, sel->which);
+ mutex_unlock(&rsz->ops_lock);
+
+ return 0;
+}
+
+static const struct media_entity_operations rkisp1_rsz_media_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+static const struct v4l2_subdev_pad_ops rkisp1_rsz_pad_ops = {
+ .enum_mbus_code = rkisp1_rsz_enum_mbus_code,
+ .get_selection = rkisp1_rsz_get_selection,
+ .set_selection = rkisp1_rsz_set_selection,
+ .init_cfg = rkisp1_rsz_init_config,
+ .get_fmt = rkisp1_rsz_get_fmt,
+ .set_fmt = rkisp1_rsz_set_fmt,
+ .link_validate = v4l2_subdev_link_validate_default,
+};
+
+/* ----------------------------------------------------------------------------
+ * Stream operations
+ */
+
+static int rkisp1_rsz_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct rkisp1_resizer *rsz =
+ container_of(sd, struct rkisp1_resizer, sd);
+ struct rkisp1_device *rkisp1 = rsz->rkisp1;
+ struct rkisp1_capture *other = &rkisp1->capture_devs[rsz->id ^ 1];
+ enum rkisp1_shadow_regs_when when = RKISP1_SHADOW_REGS_SYNC;
+
+ if (!enable) {
+ rkisp1_dcrop_disable(rsz, RKISP1_SHADOW_REGS_ASYNC);
+ rkisp1_rsz_disable(rsz, RKISP1_SHADOW_REGS_ASYNC);
+ return 0;
+ }
+
+ if (other->is_streaming)
+ when = RKISP1_SHADOW_REGS_ASYNC;
+
+ mutex_lock(&rsz->ops_lock);
+ rkisp1_rsz_config(rsz, when);
+ rkisp1_dcrop_config(rsz);
+
+ mutex_unlock(&rsz->ops_lock);
+ return 0;
+}
+
+static const struct v4l2_subdev_video_ops rkisp1_rsz_video_ops = {
+ .s_stream = rkisp1_rsz_s_stream,
+};
+
+static const struct v4l2_subdev_ops rkisp1_rsz_ops = {
+ .video = &rkisp1_rsz_video_ops,
+ .pad = &rkisp1_rsz_pad_ops,
+};
+
+static void rkisp1_rsz_unregister(struct rkisp1_resizer *rsz)
+{
+ if (!rsz->rkisp1)
+ return;
+
+ v4l2_device_unregister_subdev(&rsz->sd);
+ media_entity_cleanup(&rsz->sd.entity);
+ mutex_destroy(&rsz->ops_lock);
+}
+
+static int rkisp1_rsz_register(struct rkisp1_resizer *rsz)
+{
+ struct v4l2_subdev_state state = {
+ .pads = rsz->pad_cfg,
+ };
+ static const char * const dev_names[] = {
+ RKISP1_RSZ_MP_DEV_NAME,
+ RKISP1_RSZ_SP_DEV_NAME
+ };
+ struct media_pad *pads = rsz->pads;
+ struct v4l2_subdev *sd = &rsz->sd;
+ int ret;
+
+ if (rsz->id == RKISP1_SELFPATH) {
+ rsz->regs_base = RKISP1_CIF_SRSZ_BASE;
+ rsz->config = &rkisp1_rsz_config_sp;
+ } else {
+ rsz->regs_base = RKISP1_CIF_MRSZ_BASE;
+ rsz->config = &rkisp1_rsz_config_mp;
+ }
+
+ v4l2_subdev_init(sd, &rkisp1_rsz_ops);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sd->entity.ops = &rkisp1_rsz_media_ops;
+ sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER;
+ sd->owner = THIS_MODULE;
+ strscpy(sd->name, dev_names[rsz->id], sizeof(sd->name));
+
+ pads[RKISP1_RSZ_PAD_SINK].flags = MEDIA_PAD_FL_SINK |
+ MEDIA_PAD_FL_MUST_CONNECT;
+ pads[RKISP1_RSZ_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE |
+ MEDIA_PAD_FL_MUST_CONNECT;
+
+ rsz->pixel_enc = RKISP1_DEF_PIXEL_ENC;
+
+ mutex_init(&rsz->ops_lock);
+ ret = media_entity_pads_init(&sd->entity, RKISP1_RSZ_PAD_MAX, pads);
+ if (ret)
+ goto error;
+
+ ret = v4l2_device_register_subdev(&rsz->rkisp1->v4l2_dev, sd);
+ if (ret) {
+ dev_err(sd->dev, "Failed to register resizer subdev\n");
+ goto error;
+ }
+
+ rkisp1_rsz_init_config(sd, &state);
+ return 0;
+
+error:
+ media_entity_cleanup(&sd->entity);
+ mutex_destroy(&rsz->ops_lock);
+ return ret;
+}
+
+int rkisp1_resizer_devs_register(struct rkisp1_device *rkisp1)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < ARRAY_SIZE(rkisp1->resizer_devs); i++) {
+ struct rkisp1_resizer *rsz = &rkisp1->resizer_devs[i];
+
+ rsz->rkisp1 = rkisp1;
+ rsz->id = i;
+
+ ret = rkisp1_rsz_register(rsz);
+ if (ret) {
+ rsz->rkisp1 = NULL;
+ rkisp1_resizer_devs_unregister(rkisp1);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+void rkisp1_resizer_devs_unregister(struct rkisp1_device *rkisp1)
+{
+ struct rkisp1_resizer *mp = &rkisp1->resizer_devs[RKISP1_MAINPATH];
+ struct rkisp1_resizer *sp = &rkisp1->resizer_devs[RKISP1_SELFPATH];
+
+ rkisp1_rsz_unregister(mp);
+ rkisp1_rsz_unregister(sp);
+}
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-stats.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-stats.c
new file mode 100644
index 0000000000..2795eef91b
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-stats.c
@@ -0,0 +1,496 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Rockchip ISP1 Driver - Stats subdevice
+ *
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ */
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-vmalloc.h> /* for ISP statistics */
+
+#include "rkisp1-common.h"
+
+#define RKISP1_STATS_DEV_NAME RKISP1_DRIVER_NAME "_stats"
+
+#define RKISP1_ISP_STATS_REQ_BUFS_MIN 2
+#define RKISP1_ISP_STATS_REQ_BUFS_MAX 8
+
+static int rkisp1_stats_enum_fmt_meta_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct video_device *video = video_devdata(file);
+ struct rkisp1_stats *stats = video_get_drvdata(video);
+
+ if (f->index > 0 || f->type != video->queue->type)
+ return -EINVAL;
+
+ f->pixelformat = stats->vdev_fmt.fmt.meta.dataformat;
+ return 0;
+}
+
+static int rkisp1_stats_g_fmt_meta_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct video_device *video = video_devdata(file);
+ struct rkisp1_stats *stats = video_get_drvdata(video);
+ struct v4l2_meta_format *meta = &f->fmt.meta;
+
+ if (f->type != video->queue->type)
+ return -EINVAL;
+
+ memset(meta, 0, sizeof(*meta));
+ meta->dataformat = stats->vdev_fmt.fmt.meta.dataformat;
+ meta->buffersize = stats->vdev_fmt.fmt.meta.buffersize;
+
+ return 0;
+}
+
+static int rkisp1_stats_querycap(struct file *file,
+ void *priv, struct v4l2_capability *cap)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ strscpy(cap->driver, RKISP1_DRIVER_NAME, sizeof(cap->driver));
+ strscpy(cap->card, vdev->name, sizeof(cap->card));
+ strscpy(cap->bus_info, RKISP1_BUS_INFO, sizeof(cap->bus_info));
+
+ return 0;
+}
+
+/* ISP video device IOCTLs */
+static const struct v4l2_ioctl_ops rkisp1_stats_ioctl = {
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_enum_fmt_meta_cap = rkisp1_stats_enum_fmt_meta_cap,
+ .vidioc_g_fmt_meta_cap = rkisp1_stats_g_fmt_meta_cap,
+ .vidioc_s_fmt_meta_cap = rkisp1_stats_g_fmt_meta_cap,
+ .vidioc_try_fmt_meta_cap = rkisp1_stats_g_fmt_meta_cap,
+ .vidioc_querycap = rkisp1_stats_querycap,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct v4l2_file_operations rkisp1_stats_fops = {
+ .mmap = vb2_fop_mmap,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = vb2_fop_poll,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release
+};
+
+static int rkisp1_stats_vb2_queue_setup(struct vb2_queue *vq,
+ unsigned int *num_buffers,
+ unsigned int *num_planes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ *num_planes = 1;
+
+ *num_buffers = clamp_t(u32, *num_buffers, RKISP1_ISP_STATS_REQ_BUFS_MIN,
+ RKISP1_ISP_STATS_REQ_BUFS_MAX);
+
+ sizes[0] = sizeof(struct rkisp1_stat_buffer);
+
+ return 0;
+}
+
+static void rkisp1_stats_vb2_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct rkisp1_buffer *stats_buf =
+ container_of(vbuf, struct rkisp1_buffer, vb);
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct rkisp1_stats *stats_dev = vq->drv_priv;
+
+
+ spin_lock_irq(&stats_dev->lock);
+ list_add_tail(&stats_buf->queue, &stats_dev->stat);
+ spin_unlock_irq(&stats_dev->lock);
+}
+
+static int rkisp1_stats_vb2_buf_prepare(struct vb2_buffer *vb)
+{
+ if (vb2_plane_size(vb, 0) < sizeof(struct rkisp1_stat_buffer))
+ return -EINVAL;
+
+ vb2_set_plane_payload(vb, 0, sizeof(struct rkisp1_stat_buffer));
+
+ return 0;
+}
+
+static void rkisp1_stats_vb2_stop_streaming(struct vb2_queue *vq)
+{
+ struct rkisp1_stats *stats = vq->drv_priv;
+ struct rkisp1_buffer *buf;
+ unsigned int i;
+
+ spin_lock_irq(&stats->lock);
+ for (i = 0; i < RKISP1_ISP_STATS_REQ_BUFS_MAX; i++) {
+ if (list_empty(&stats->stat))
+ break;
+ buf = list_first_entry(&stats->stat,
+ struct rkisp1_buffer, queue);
+ list_del(&buf->queue);
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ }
+ spin_unlock_irq(&stats->lock);
+}
+
+static const struct vb2_ops rkisp1_stats_vb2_ops = {
+ .queue_setup = rkisp1_stats_vb2_queue_setup,
+ .buf_queue = rkisp1_stats_vb2_buf_queue,
+ .buf_prepare = rkisp1_stats_vb2_buf_prepare,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .stop_streaming = rkisp1_stats_vb2_stop_streaming,
+};
+
+static int
+rkisp1_stats_init_vb2_queue(struct vb2_queue *q, struct rkisp1_stats *stats)
+{
+ struct rkisp1_vdev_node *node;
+
+ node = container_of(q, struct rkisp1_vdev_node, buf_queue);
+
+ q->type = V4L2_BUF_TYPE_META_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
+ q->drv_priv = stats;
+ q->ops = &rkisp1_stats_vb2_ops;
+ q->mem_ops = &vb2_vmalloc_memops;
+ q->buf_struct_size = sizeof(struct rkisp1_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &node->vlock;
+
+ return vb2_queue_init(q);
+}
+
+static void rkisp1_stats_get_awb_meas_v10(struct rkisp1_stats *stats,
+ struct rkisp1_stat_buffer *pbuf)
+{
+ /* Protect against concurrent access from ISR? */
+ struct rkisp1_device *rkisp1 = stats->rkisp1;
+ u32 reg_val;
+
+ pbuf->meas_type |= RKISP1_CIF_ISP_STAT_AWB;
+ reg_val = rkisp1_read(rkisp1, RKISP1_CIF_ISP_AWB_WHITE_CNT_V10);
+ pbuf->params.awb.awb_mean[0].cnt =
+ RKISP1_CIF_ISP_AWB_GET_PIXEL_CNT(reg_val);
+ reg_val = rkisp1_read(rkisp1, RKISP1_CIF_ISP_AWB_MEAN_V10);
+
+ pbuf->params.awb.awb_mean[0].mean_cr_or_r =
+ RKISP1_CIF_ISP_AWB_GET_MEAN_CR_R(reg_val);
+ pbuf->params.awb.awb_mean[0].mean_cb_or_b =
+ RKISP1_CIF_ISP_AWB_GET_MEAN_CB_B(reg_val);
+ pbuf->params.awb.awb_mean[0].mean_y_or_g =
+ RKISP1_CIF_ISP_AWB_GET_MEAN_Y_G(reg_val);
+}
+
+static void rkisp1_stats_get_awb_meas_v12(struct rkisp1_stats *stats,
+ struct rkisp1_stat_buffer *pbuf)
+{
+ /* Protect against concurrent access from ISR? */
+ struct rkisp1_device *rkisp1 = stats->rkisp1;
+ u32 reg_val;
+
+ pbuf->meas_type |= RKISP1_CIF_ISP_STAT_AWB;
+ reg_val = rkisp1_read(rkisp1, RKISP1_CIF_ISP_AWB_WHITE_CNT_V12);
+ pbuf->params.awb.awb_mean[0].cnt =
+ RKISP1_CIF_ISP_AWB_GET_PIXEL_CNT(reg_val);
+ reg_val = rkisp1_read(rkisp1, RKISP1_CIF_ISP_AWB_MEAN_V12);
+
+ pbuf->params.awb.awb_mean[0].mean_cr_or_r =
+ RKISP1_CIF_ISP_AWB_GET_MEAN_CR_R(reg_val);
+ pbuf->params.awb.awb_mean[0].mean_cb_or_b =
+ RKISP1_CIF_ISP_AWB_GET_MEAN_CB_B(reg_val);
+ pbuf->params.awb.awb_mean[0].mean_y_or_g =
+ RKISP1_CIF_ISP_AWB_GET_MEAN_Y_G(reg_val);
+}
+
+static void rkisp1_stats_get_aec_meas_v10(struct rkisp1_stats *stats,
+ struct rkisp1_stat_buffer *pbuf)
+{
+ struct rkisp1_device *rkisp1 = stats->rkisp1;
+ unsigned int i;
+
+ pbuf->meas_type |= RKISP1_CIF_ISP_STAT_AUTOEXP;
+ for (i = 0; i < RKISP1_CIF_ISP_AE_MEAN_MAX_V10; i++)
+ pbuf->params.ae.exp_mean[i] =
+ (u8)rkisp1_read(rkisp1,
+ RKISP1_CIF_ISP_EXP_MEAN_00_V10 + i * 4);
+}
+
+static void rkisp1_stats_get_aec_meas_v12(struct rkisp1_stats *stats,
+ struct rkisp1_stat_buffer *pbuf)
+{
+ struct rkisp1_device *rkisp1 = stats->rkisp1;
+ u32 value;
+ int i;
+
+ pbuf->meas_type |= RKISP1_CIF_ISP_STAT_AUTOEXP;
+ for (i = 0; i < RKISP1_CIF_ISP_AE_MEAN_MAX_V12 / 4; i++) {
+ value = rkisp1_read(rkisp1, RKISP1_CIF_ISP_EXP_MEAN_V12 + i * 4);
+ pbuf->params.ae.exp_mean[4 * i + 0] =
+ RKISP1_CIF_ISP_EXP_GET_MEAN_xy0_V12(value);
+ pbuf->params.ae.exp_mean[4 * i + 1] =
+ RKISP1_CIF_ISP_EXP_GET_MEAN_xy1_V12(value);
+ pbuf->params.ae.exp_mean[4 * i + 2] =
+ RKISP1_CIF_ISP_EXP_GET_MEAN_xy2_V12(value);
+ pbuf->params.ae.exp_mean[4 * i + 3] =
+ RKISP1_CIF_ISP_EXP_GET_MEAN_xy3_V12(value);
+ }
+
+ value = rkisp1_read(rkisp1, RKISP1_CIF_ISP_EXP_MEAN_V12 + i * 4);
+ pbuf->params.ae.exp_mean[4 * i + 0] = RKISP1_CIF_ISP_EXP_GET_MEAN_xy0_V12(value);
+}
+
+static void rkisp1_stats_get_afc_meas(struct rkisp1_stats *stats,
+ struct rkisp1_stat_buffer *pbuf)
+{
+ struct rkisp1_device *rkisp1 = stats->rkisp1;
+ struct rkisp1_cif_isp_af_stat *af;
+
+ pbuf->meas_type |= RKISP1_CIF_ISP_STAT_AFM;
+
+ af = &pbuf->params.af;
+ af->window[0].sum = rkisp1_read(rkisp1, RKISP1_CIF_ISP_AFM_SUM_A);
+ af->window[0].lum = rkisp1_read(rkisp1, RKISP1_CIF_ISP_AFM_LUM_A);
+ af->window[1].sum = rkisp1_read(rkisp1, RKISP1_CIF_ISP_AFM_SUM_B);
+ af->window[1].lum = rkisp1_read(rkisp1, RKISP1_CIF_ISP_AFM_LUM_B);
+ af->window[2].sum = rkisp1_read(rkisp1, RKISP1_CIF_ISP_AFM_SUM_C);
+ af->window[2].lum = rkisp1_read(rkisp1, RKISP1_CIF_ISP_AFM_LUM_C);
+}
+
+static void rkisp1_stats_get_hst_meas_v10(struct rkisp1_stats *stats,
+ struct rkisp1_stat_buffer *pbuf)
+{
+ struct rkisp1_device *rkisp1 = stats->rkisp1;
+ unsigned int i;
+
+ pbuf->meas_type |= RKISP1_CIF_ISP_STAT_HIST;
+ for (i = 0; i < RKISP1_CIF_ISP_HIST_BIN_N_MAX_V10; i++) {
+ u32 reg_val = rkisp1_read(rkisp1, RKISP1_CIF_ISP_HIST_BIN_0_V10 + i * 4);
+
+ pbuf->params.hist.hist_bins[i] = RKISP1_CIF_ISP_HIST_GET_BIN_V10(reg_val);
+ }
+}
+
+static void rkisp1_stats_get_hst_meas_v12(struct rkisp1_stats *stats,
+ struct rkisp1_stat_buffer *pbuf)
+{
+ struct rkisp1_device *rkisp1 = stats->rkisp1;
+ u32 value;
+ int i;
+
+ pbuf->meas_type |= RKISP1_CIF_ISP_STAT_HIST;
+ for (i = 0; i < RKISP1_CIF_ISP_HIST_BIN_N_MAX_V12 / 2; i++) {
+ value = rkisp1_read(rkisp1, RKISP1_CIF_ISP_HIST_BIN_V12 + i * 4);
+ pbuf->params.hist.hist_bins[2 * i] =
+ RKISP1_CIF_ISP_HIST_GET_BIN0_V12(value);
+ pbuf->params.hist.hist_bins[2 * i + 1] =
+ RKISP1_CIF_ISP_HIST_GET_BIN1_V12(value);
+ }
+}
+
+static void rkisp1_stats_get_bls_meas(struct rkisp1_stats *stats,
+ struct rkisp1_stat_buffer *pbuf)
+{
+ struct rkisp1_device *rkisp1 = stats->rkisp1;
+ const struct rkisp1_mbus_info *in_fmt = rkisp1->isp.sink_fmt;
+ struct rkisp1_cif_isp_bls_meas_val *bls_val;
+
+ bls_val = &pbuf->params.ae.bls_val;
+ if (in_fmt->bayer_pat == RKISP1_RAW_BGGR) {
+ bls_val->meas_b =
+ rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_A_MEASURED);
+ bls_val->meas_gb =
+ rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_B_MEASURED);
+ bls_val->meas_gr =
+ rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_C_MEASURED);
+ bls_val->meas_r =
+ rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_D_MEASURED);
+ } else if (in_fmt->bayer_pat == RKISP1_RAW_GBRG) {
+ bls_val->meas_gb =
+ rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_A_MEASURED);
+ bls_val->meas_b =
+ rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_B_MEASURED);
+ bls_val->meas_r =
+ rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_C_MEASURED);
+ bls_val->meas_gr =
+ rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_D_MEASURED);
+ } else if (in_fmt->bayer_pat == RKISP1_RAW_GRBG) {
+ bls_val->meas_gr =
+ rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_A_MEASURED);
+ bls_val->meas_r =
+ rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_B_MEASURED);
+ bls_val->meas_b =
+ rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_C_MEASURED);
+ bls_val->meas_gb =
+ rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_D_MEASURED);
+ } else if (in_fmt->bayer_pat == RKISP1_RAW_RGGB) {
+ bls_val->meas_r =
+ rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_A_MEASURED);
+ bls_val->meas_gr =
+ rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_B_MEASURED);
+ bls_val->meas_gb =
+ rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_C_MEASURED);
+ bls_val->meas_b =
+ rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_D_MEASURED);
+ }
+}
+
+static const struct rkisp1_stats_ops rkisp1_v10_stats_ops = {
+ .get_awb_meas = rkisp1_stats_get_awb_meas_v10,
+ .get_aec_meas = rkisp1_stats_get_aec_meas_v10,
+ .get_hst_meas = rkisp1_stats_get_hst_meas_v10,
+};
+
+static struct rkisp1_stats_ops rkisp1_v12_stats_ops = {
+ .get_awb_meas = rkisp1_stats_get_awb_meas_v12,
+ .get_aec_meas = rkisp1_stats_get_aec_meas_v12,
+ .get_hst_meas = rkisp1_stats_get_hst_meas_v12,
+};
+
+static void
+rkisp1_stats_send_measurement(struct rkisp1_stats *stats, u32 isp_ris)
+{
+ struct rkisp1_stat_buffer *cur_stat_buf;
+ struct rkisp1_buffer *cur_buf = NULL;
+ unsigned int frame_sequence = stats->rkisp1->isp.frame_sequence;
+ u64 timestamp = ktime_get_ns();
+
+ /* get one empty buffer */
+ if (!list_empty(&stats->stat)) {
+ cur_buf = list_first_entry(&stats->stat,
+ struct rkisp1_buffer, queue);
+ list_del(&cur_buf->queue);
+ }
+
+ if (!cur_buf)
+ return;
+
+ cur_stat_buf = (struct rkisp1_stat_buffer *)
+ vb2_plane_vaddr(&cur_buf->vb.vb2_buf, 0);
+ if (isp_ris & RKISP1_CIF_ISP_AWB_DONE)
+ stats->ops->get_awb_meas(stats, cur_stat_buf);
+
+ if (isp_ris & RKISP1_CIF_ISP_AFM_FIN)
+ rkisp1_stats_get_afc_meas(stats, cur_stat_buf);
+
+ if (isp_ris & RKISP1_CIF_ISP_EXP_END) {
+ stats->ops->get_aec_meas(stats, cur_stat_buf);
+ rkisp1_stats_get_bls_meas(stats, cur_stat_buf);
+ }
+
+ if (isp_ris & RKISP1_CIF_ISP_HIST_MEASURE_RDY)
+ stats->ops->get_hst_meas(stats, cur_stat_buf);
+
+ vb2_set_plane_payload(&cur_buf->vb.vb2_buf, 0,
+ sizeof(struct rkisp1_stat_buffer));
+ cur_buf->vb.sequence = frame_sequence;
+ cur_buf->vb.vb2_buf.timestamp = timestamp;
+ vb2_buffer_done(&cur_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+}
+
+void rkisp1_stats_isr(struct rkisp1_stats *stats, u32 isp_ris)
+{
+ struct rkisp1_device *rkisp1 = stats->rkisp1;
+ unsigned int isp_mis_tmp = 0;
+
+ spin_lock(&stats->lock);
+
+ rkisp1_write(rkisp1, RKISP1_CIF_ISP_ICR, RKISP1_STATS_MEAS_MASK);
+
+ isp_mis_tmp = rkisp1_read(rkisp1, RKISP1_CIF_ISP_MIS);
+ if (isp_mis_tmp & RKISP1_STATS_MEAS_MASK)
+ rkisp1->debug.stats_error++;
+
+ if (isp_ris & RKISP1_STATS_MEAS_MASK)
+ rkisp1_stats_send_measurement(stats, isp_ris);
+
+ spin_unlock(&stats->lock);
+}
+
+static void rkisp1_init_stats(struct rkisp1_stats *stats)
+{
+ stats->vdev_fmt.fmt.meta.dataformat =
+ V4L2_META_FMT_RK_ISP1_STAT_3A;
+ stats->vdev_fmt.fmt.meta.buffersize =
+ sizeof(struct rkisp1_stat_buffer);
+
+ if (stats->rkisp1->info->isp_ver == RKISP1_V12)
+ stats->ops = &rkisp1_v12_stats_ops;
+ else
+ stats->ops = &rkisp1_v10_stats_ops;
+}
+
+int rkisp1_stats_register(struct rkisp1_device *rkisp1)
+{
+ struct rkisp1_stats *stats = &rkisp1->stats;
+ struct rkisp1_vdev_node *node = &stats->vnode;
+ struct video_device *vdev = &node->vdev;
+ int ret;
+
+ stats->rkisp1 = rkisp1;
+ mutex_init(&node->vlock);
+ INIT_LIST_HEAD(&stats->stat);
+ spin_lock_init(&stats->lock);
+
+ strscpy(vdev->name, RKISP1_STATS_DEV_NAME, sizeof(vdev->name));
+
+ video_set_drvdata(vdev, stats);
+ vdev->ioctl_ops = &rkisp1_stats_ioctl;
+ vdev->fops = &rkisp1_stats_fops;
+ vdev->release = video_device_release_empty;
+ vdev->lock = &node->vlock;
+ vdev->v4l2_dev = &rkisp1->v4l2_dev;
+ vdev->queue = &node->buf_queue;
+ vdev->device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING;
+ vdev->vfl_dir = VFL_DIR_RX;
+ rkisp1_stats_init_vb2_queue(vdev->queue, stats);
+ rkisp1_init_stats(stats);
+ video_set_drvdata(vdev, stats);
+
+ node->pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_pads_init(&vdev->entity, 1, &node->pad);
+ if (ret)
+ goto error;
+
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret) {
+ dev_err(&vdev->dev,
+ "failed to register %s, ret=%d\n", vdev->name, ret);
+ goto error;
+ }
+
+ return 0;
+
+error:
+ media_entity_cleanup(&vdev->entity);
+ mutex_destroy(&node->vlock);
+ stats->rkisp1 = NULL;
+ return ret;
+}
+
+void rkisp1_stats_unregister(struct rkisp1_device *rkisp1)
+{
+ struct rkisp1_stats *stats = &rkisp1->stats;
+ struct rkisp1_vdev_node *node = &stats->vnode;
+ struct video_device *vdev = &node->vdev;
+
+ if (!stats->rkisp1)
+ return;
+
+ vb2_video_unregister_device(vdev);
+ media_entity_cleanup(&vdev->entity);
+ mutex_destroy(&node->vlock);
+}