diff mbox series

[v1,07/10] media: platform: mediatek: add isp_7x video ops

Message ID 20241009111551.27052-8-Shu-hsiang.Yang@mediatek.com (mailing list archive)
State New
Headers show
Series Add MediaTek ISP7 camera system driver | expand

Commit Message

Shu-hsiang Yang Oct. 9, 2024, 11:15 a.m. UTC
Introduces the V4L2 video interface and feature management for the
MediaTek ISP7x CAMSYS. These interfaces include various functionalities,
such as video operation initialization and registration. They also
manage MediaTek-specific formats and handle buffers for MediaTek camera
video devices. This integrates CAMSYS functionalities to be compatible
with the V4L2 framework.

Signed-off-by: Shu-hsiang Yang <Shu-hsiang.Yang@mediatek.com>
---
 .../isp/isp_7x/camsys/mtk_cam-feature.c       |   40 +
 .../isp/isp_7x/camsys/mtk_cam-feature.h       |   26 +
 .../isp/isp_7x/camsys/mtk_cam-video.c         | 1817 +++++++++++++++++
 .../isp/isp_7x/camsys/mtk_cam-video.h         |  224 ++
 4 files changed, 2107 insertions(+)
 create mode 100644 drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-feature.c
 create mode 100644 drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-feature.h
 create mode 100644 drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-video.c
 create mode 100644 drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-video.h

Comments

CK Hu (胡俊光) Nov. 4, 2024, 3:24 a.m. UTC | #1
Hi, Shu-hsiang:

On Wed, 2024-10-09 at 19:15 +0800, Shu-hsiang Yang wrote:
> Introduces the V4L2 video interface and feature management for the
> MediaTek ISP7x CAMSYS. These interfaces include various functionalities,
> such as video operation initialization and registration. They also
> manage MediaTek-specific formats and handle buffers for MediaTek camera
> video devices. This integrates CAMSYS functionalities to be compatible
> with the V4L2 framework.
> 
> Signed-off-by: Shu-hsiang Yang <Shu-hsiang.Yang@mediatek.com>
> ---

[snip]

> +
> +static void mtk_cam_fill_ext_fmtdesc(struct v4l2_fmtdesc *fmt)
> +{
> +	const char *descr = NULL;
> +	const unsigned int sz = sizeof(fmt->description);
> +
> +	switch (fmt->pixelformat) {
> +	case V4L2_PIX_FMT_MTISP_SBGGR8:
> +		descr = "8-bit Bayer BGGR MTISP Packed";

V4L2_PIX_FMT_PISP_COMP1_RGGB is a Raspberry Pi format, and its fmtdesc is filled in v4l_fill_fmtdesc().
So move these fill code into v4l_fill_fmtdesc().

Regards,
CK

> +		break;
> +	case V4L2_PIX_FMT_MTISP_SGBRG8:
> +		descr = "8-bit Bayer GBRG MTISP Packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_SGRBG8:
> +		descr = "8-bit Bayer GRBG MTISP Packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_SRGGB8:
> +		descr = "8-bit Bayer RGGB MTISP Packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_SBGGR10:
> +		descr = "10-bit Bayer BGGR MTISP Packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_SGBRG10:
> +		descr = "10-bit Bayer GBRG MTISP Packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_SGRBG10:
> +		descr = "10-bit Bayer GRBG MTISP Packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_SRGGB10:
> +		descr = "10-bit Bayer RGGB MTISP Packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_SBGGR12:
> +		descr = "12-bit Bayer BGGR MTISP Packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_SGBRG12:
> +		descr = "12-bit Bayer GBRG MTISP Packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_SGRBG12:
> +		descr = "12-bit Bayer GRBG MTISP Packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_SRGGB12:
> +		descr = "12-bit Bayer RGGB MTISP Packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_SBGGR14:
> +		descr = "14-bit Bayer BGGR MTISP Packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_SGBRG14:
> +		descr = "14-bit Bayer GBRG MTISP Packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_SGRBG14:
> +		descr = "14-bit Bayer GRBG MTISP Packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_SRGGB14:
> +		descr = "14-bit Bayer RGGB MTISP Packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_SBGGR8F:
> +		descr = "8-bit Full-G Bayer BGGR Packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_SGBRG8F:
> +		descr = "8-bit Full-G Bayer GBRG Packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_SGRBG8F:
> +		descr = "8-bit Full-G Bayer GRBG Packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_SRGGB8F:
> +		descr = "8-bit Full-G Bayer RGGB Packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_SBGGR10F:
> +		descr = "10-bit Full-G Bayer BGGR Packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_SGBRG10F:
> +		descr = "10-bit Full-G Bayer GBRG Packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_SGRBG10F:
> +		descr = "10-bit Full-G Bayer GRBG Packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_SRGGB10F:
> +		descr = "10-bit Full-G Bayer RGGB Packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_SBGGR12F:
> +		descr = "12-bit Full-G Bayer BGGR Packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_SGBRG12F:
> +		descr = "12-bit Full-G Bayer GBRG Packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_SGRBG12F:
> +		descr = "12-bit Full-G Bayer GRBG Packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_SRGGB12F:
> +		descr = "12-bit Full-G Bayer RGGB Packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_SBGGR14F:
> +		descr = "14-bit Full-G Bayer BGGR Packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_SGBRG14F:
> +		descr = "14-bit Full-G Bayer GBRG Packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_SGRBG14F:
> +		descr = "14-bit Full-G Bayer GRBG Packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_SRGGB14F:
> +		descr = "14-bit Full-G Bayer RGGB Packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_NV12_10P:
> +		descr = "Y/CbCr 4:2:0 10 bits packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_NV21_10P:
> +		descr = "Y/CrCb 4:2:0 10 bits packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_NV16_10P:
> +		descr = "Y/CbCr 4:2:2 10 bits packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_NV61_10P:
> +		descr = "Y/CrCb 4:2:2 10 bits packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_YUYV10P:
> +		descr = "YUYV 4:2:2 10 bits packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_YVYU10P:
> +		descr = "YVYU 4:2:2 10 bits packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_UYVY10P:
> +		descr = "UYVY 4:2:2 10 bits packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_VYUY10P:
> +		descr = "VYUY 4:2:2 10 bits packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_NV12_12P:
> +		descr = "Y/CbCr 4:2:0 12 bits packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_NV21_12P:
> +		descr = "Y/CrCb 4:2:0 12 bits packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_NV16_12P:
> +		descr = "Y/CbCr 4:2:2 12 bits packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_NV61_12P:
> +		descr = "Y/CrCb 4:2:2 12 bits packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_YUYV12P:
> +		descr = "YUYV 4:2:2 12 bits packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_YVYU12P:
> +		descr = "YVYU 4:2:2 12 bits packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_UYVY12P:
> +		descr = "UYVY 4:2:2 12 bits packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_VYUY12P:
> +		descr = "VYUY 4:2:2 12 bits packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_NV12_UFBC:
> +		descr = "YCbCr 420 8 bits compress";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_NV21_UFBC:
> +		descr = "YCrCb 420 8 bits compress";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_NV12_10_UFBC:
> +		descr = "YCbCr 420 10 bits compress";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_NV21_10_UFBC:
> +		descr = "YCrCb 420 10 bits compress";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_NV12_12_UFBC:
> +		descr = "YCbCr 420 12 bits compress";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_NV21_12_UFBC:
> +		descr = "YCrCb 420 12 bits compress";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_BAYER8_UFBC:
> +		descr = "RAW 8 bits compress";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_BAYER10_UFBC:
> +		descr = "RAW 10 bits compress";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_BAYER12_UFBC:
> +		descr = "RAW 12 bits compress";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_BAYER14_UFBC:
> +		descr = "RAW 14 bits compress";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_SGRB8F:
> +		descr = "8-bit 3 plane GRB Packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_SGRB10F:
> +		descr = "10-bit 3 plane GRB Packed";
> +		break;
> +	case V4L2_PIX_FMT_MTISP_SGRB12F:
> +		descr = "12-bit 3 plane GRB Packed";
> +		break;
> +	case V4L2_META_FMT_MTISP_PARAMS:
> +		descr = "MTK ISP Tuning Metadata";
> +		break;
> +	case V4L2_META_FMT_MTISP_3A:
> +		descr = "MTK 3A Statistics";
> +		break;
> +	case V4L2_META_FMT_MTISP_AF:
> +		descr = "MTK AF Statistics";
> +		break;
> +	case V4L2_META_FMT_MTISP_LCS:
> +		descr = "MTK LCS Statistics";
> +		break;
> +	case V4L2_META_FMT_MTISP_LMV:
> +		descr = "MTK LMV Statistics";
> +		break;
> +	default:
> +		descr = NULL;
> +		break;
> +	}
> +
> +	if (descr)
> +		WARN_ON(strscpy(fmt->description, descr, sz) < 0);
> +}
> +
diff mbox series

Patch

diff --git a/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-feature.c b/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-feature.c
new file mode 100644
index 000000000000..639c1e1671fc
--- /dev/null
+++ b/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-feature.c
@@ -0,0 +1,40 @@ 
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (c) 2022 MediaTek Inc.
+
+#include "mtk_cam-feature.h"
+
+struct mtk_cam_ctx;
+
+/*
+ * mtk_cam_is_[feature] is used for raw_feature,
+ * which must not change during streaming.
+ */
+
+static bool mtk_cam_is_no_seninf(struct mtk_cam_ctx *ctx)
+{
+	return !media_pad_remote_pad_first(&ctx->pipe->pads[MTK_RAW_SINK]);
+}
+
+bool mtk_cam_is_pure_m2m(struct mtk_cam_ctx *ctx)
+{
+	if (!ctx->used_raw_num)
+		return false;
+
+	if (ctx->pipe->feature_pending & MTK_CAM_FEATURE_PURE_OFFLINE_M2M_MASK ||
+	    mtk_cam_is_no_seninf(ctx))
+		return true;
+	else
+		return false;
+}
+
+bool mtk_cam_is_m2m(struct mtk_cam_ctx *ctx)
+{
+	if (!ctx->used_raw_num)
+		return false;
+
+	if (mtk_cam_is_no_seninf(ctx))
+		return true;
+	else
+		return mtk_cam_feature_is_m2m(ctx->pipe->feature_pending);
+}
diff --git a/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-feature.h b/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-feature.h
new file mode 100644
index 000000000000..dd5acc754765
--- /dev/null
+++ b/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-feature.h
@@ -0,0 +1,26 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ */
+
+#ifndef __MTK_CAM_FEATURE_H
+#define __MTK_CAM_FEATURE_H
+
+#include "mtk_cam.h"
+#include "mtk_cam-raw.h"
+
+static inline bool mtk_cam_feature_is_m2m(int feature)
+{
+	return !!(feature & MTK_CAM_FEATURE_OFFLINE_M2M_MASK) ||
+			!!(feature & MTK_CAM_FEATURE_PURE_OFFLINE_M2M_MASK);
+}
+
+static inline bool mtk_cam_feature_is_pure_m2m(int feature)
+{
+	return !!(feature & MTK_CAM_FEATURE_PURE_OFFLINE_M2M_MASK);
+}
+
+bool mtk_cam_is_m2m(struct mtk_cam_ctx *ctx);
+bool mtk_cam_is_pure_m2m(struct mtk_cam_ctx *ctx);
+
+#endif /*__MTK_CAM_FEATURE_H */
diff --git a/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-video.c b/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-video.c
new file mode 100644
index 000000000000..95cdf1a2fb4c
--- /dev/null
+++ b/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-video.c
@@ -0,0 +1,1817 @@ 
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (c) 2022 MediaTek Inc.
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+#include <linux/videodev2.h>
+#include <linux/iommu.h>
+
+#include "mtk_cam.h"
+#include "mtk_cam-feature.h"
+#include "mtk_cam-video.h"
+#include "mtk_camera-v4l2-controls.h"
+#include "mtk_cam-ufbc-def.h"
+
+/*
+ * differt dma (fmt) would have different bus_size
+ * align xsize(bytes per line) with [bus_size * pixel_mode]
+ */
+static inline int mtk_cam_is_fullg(unsigned int ipi_fmt)
+{
+	return (ipi_fmt == MTKCAM_IPI_IMG_FMT_FG_BAYER8) ||
+	       (ipi_fmt == MTKCAM_IPI_IMG_FMT_FG_BAYER10) ||
+	       (ipi_fmt == MTKCAM_IPI_IMG_FMT_FG_BAYER12);
+}
+
+static inline
+unsigned int mtk_cam_dma_bus_size(int bpp, int pixel_mode_shift, int is_fg)
+{
+	unsigned int bus_size = ALIGN(bpp, 16) << pixel_mode_shift;
+
+	if (is_fg)
+		bus_size <<= 1;
+	return bus_size / 8; /* in bytes */
+}
+
+static inline
+unsigned int mtk_cam_yuv_dma_bus_size(int bpp, int pixel_mode_shift)
+{
+	unsigned int bus_size = ALIGN(bpp, 32) << pixel_mode_shift;
+
+	return bus_size / 8; /* in bytes */
+}
+
+static inline
+unsigned int mtk_cam_dmao_xsize(int w, unsigned int ipi_fmt, int pixel_mode_shift)
+{
+	const unsigned int is_fg = mtk_cam_is_fullg(ipi_fmt);
+	const unsigned int bpp = mtk_cam_get_pixel_bits(ipi_fmt);
+	const unsigned int bytes = is_fg ?
+		DIV_ROUND_UP(w * bpp * 3 / 2, 8) : DIV_ROUND_UP(w * bpp, 8);
+	const unsigned int bus_size =
+		mtk_cam_dma_bus_size(bpp, pixel_mode_shift, is_fg);
+
+	return ALIGN(bytes, bus_size);
+}
+
+static void mtk_cam_release_all_buffer(struct mtk_cam_device *cam)
+{
+	struct mtk_cam_buffer *buf, *tmp;
+
+	spin_lock(&cam->dma_pending_lock);
+	list_for_each_entry_safe(buf, tmp, &cam->dma_pending, list) {
+		list_del(&buf->list);
+		vb2_buffer_done(&buf->vbb.vb2_buf, VB2_BUF_STATE_ERROR);
+	}
+	spin_unlock(&cam->dma_pending_lock);
+
+	spin_lock(&cam->dma_processing_lock);
+	list_for_each_entry_safe(buf, tmp, &cam->dma_processing, list) {
+		list_del(&buf->list);
+		vb2_buffer_done(&buf->vbb.vb2_buf, VB2_BUF_STATE_ERROR);
+	}
+	spin_unlock(&cam->dma_processing_lock);
+}
+
+static int mtk_cam_vb2_queue_setup(struct vb2_queue *vq,
+				   unsigned int *num_buffers,
+				   unsigned int *num_planes,
+				   unsigned int sizes[],
+				   struct device *alloc_devs[])
+{
+	struct mtk_cam_device *cam = vb2_get_drv_priv(vq);
+	struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vq);
+	struct mtk_raw_pipeline *raw_pipeline;
+	unsigned int max_buffer_count = node->desc.max_buf_count;
+	const struct v4l2_format *fmt = &node->active_fmt;
+	unsigned int size;
+	int i;
+
+	/* Check the limitation of buffer size */
+	if (max_buffer_count)
+		*num_buffers = clamp_val(*num_buffers, 1, max_buffer_count);
+
+	if (node->desc.smem_alloc)
+		vq->dma_attrs |= DMA_ATTR_NO_KERNEL_MAPPING;
+
+	if (vq->type == V4L2_BUF_TYPE_META_OUTPUT ||
+	    vq->type == V4L2_BUF_TYPE_META_CAPTURE)
+		size = fmt->fmt.meta.buffersize;
+	else
+		size = fmt->fmt.pix_mp.plane_fmt[0].sizeimage;
+
+	/* Add for q.create_bufs with fmt.g_sizeimage(p) / 2 test */
+	if (*num_planes) {
+		if (sizes[0] < size || *num_planes != 1)
+			return -EINVAL;
+	} else {
+		/* Set default as one plane */
+		*num_planes = 1;
+		sizes[0] = size;
+
+		if (is_raw_subdev(node->uid.pipe_id)) {
+			raw_pipeline =
+				mtk_cam_dev_get_raw_pipeline(cam, node->uid.pipe_id);
+
+			if (raw_pipeline &&
+			    raw_pipeline->user_res.raw_res.feature &&
+			    fmt->fmt.pix_mp.num_planes > 1) {
+				*num_planes = fmt->fmt.pix_mp.num_planes;
+				for (i = 0; i < *num_planes; i++)
+					sizes[i] = size;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int mtk_cam_vb2_buf_init(struct vb2_buffer *vb)
+{
+	struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vb->vb2_queue);
+	struct mtk_cam_buffer *buf;
+	struct mtk_cam_device *cam = vb2_get_drv_priv(vb->vb2_queue);
+	struct device *dev = cam->raw.devs[0];
+	dma_addr_t addr;
+
+	buf = mtk_cam_vb2_buf_to_dev_buf(vb);
+	buf->daddr = vb2_dma_contig_plane_dma_addr(vb, 0);
+	buf->scp_addr = 0;
+
+	/* SCP address is only valid for meta input buffer */
+	if (!node->desc.smem_alloc)
+		return 0;
+
+	/* Use coherent address to get iova address */
+	addr = dma_map_resource(dev, buf->daddr, vb->planes[0].length,
+				DMA_BIDIRECTIONAL, DMA_ATTR_SKIP_CPU_SYNC);
+	if (dma_mapping_error(dev, addr)) {
+		dev_err(dev, "failed to map addr:%pad\n", &buf->daddr);
+		return -EFAULT;
+	}
+	buf->scp_addr = buf->daddr;
+	buf->daddr = addr;
+
+	return 0;
+}
+
+static int mtk_cam_vb2_buf_prepare(struct vb2_buffer *vb)
+{
+	struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vb->vb2_queue);
+	struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
+	const struct v4l2_format *fmt = &node->active_fmt;
+	unsigned int size;
+
+	dev_dbg(vb->vb2_queue->dev, "%s: %s\n", __func__, node->desc.name);
+
+	if (vb->vb2_queue->type == V4L2_BUF_TYPE_META_OUTPUT ||
+	    vb->vb2_queue->type == V4L2_BUF_TYPE_META_CAPTURE)
+		size = fmt->fmt.meta.buffersize;
+	else
+		size = fmt->fmt.pix_mp.plane_fmt[0].sizeimage;
+
+	if (vb2_plane_size(vb, 0) < size) {
+		dev_info(vb->vb2_queue->dev,
+			 "plane size is too small:%lu<%u\n",
+			 vb2_plane_size(vb, 0), size);
+	}
+
+	if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
+		if ((vb2_get_plane_payload(vb, 0) != size) &&
+		    vb->vb2_queue->streaming) {
+			dev_dbg(vb->vb2_queue->dev,
+				"plane payload is mismatch:%lu:%u\n",
+				vb2_get_plane_payload(vb, 0), size);
+		}
+		return 0;
+	}
+
+	v4l2_buf->field = V4L2_FIELD_NONE;
+	vb2_set_plane_payload(vb, 0, size);
+
+	return 0;
+}
+
+static int mtk_cam_vb2_start_streaming(struct vb2_queue *vq,
+				       unsigned int count)
+{
+	struct mtk_cam_device *cam = vb2_get_drv_priv(vq);
+	struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vq);
+	struct media_entity *entity = &node->vdev.entity;
+	struct mtk_cam_ctx *ctx = NULL;
+	struct device *dev = cam->dev;
+	int ret;
+
+	/* check entity is linked */
+	if (!node->enabled) {
+		dev_info(cam->dev,
+			 "%s: stream on failed, node is not enabled\n",
+			 node->desc.name);
+		ret = -ENOLINK;
+		goto fail_return_buffer;
+	}
+
+	if (!media_entity_is_streaming(entity)) {
+		ctx = mtk_cam_start_ctx(cam, node);
+		if (!ctx) {
+			ret = -ENOLINK;
+			goto fail_return_buffer;
+		}
+	} else {
+		ctx = mtk_cam_find_ctx(cam, entity);
+		if (WARN_ON(!ctx)) {
+			ret = -ENOLINK;
+			goto fail_return_buffer;
+		}
+	}
+
+	cam->streaming_pipe |= (1 << node->uid.pipe_id);
+	ctx->streaming_pipe |= (1 << node->uid.pipe_id);
+	ctx->streaming_node_cnt++;
+
+	if (ctx->streaming_node_cnt == 1)
+		if (is_raw_subdev(node->uid.pipe_id)) {
+			if (!isp_composer_create_session(ctx)) {
+				ctx->session_created = 1;
+			} else {
+				complete(&ctx->session_complete);
+				ret = -EBUSY;
+				goto fail_stop_ctx;
+			}
+		}
+
+	dev_dbg(dev, "%s:%s:ctx(%d): node:%d count info:%d\n", __func__,
+		node->desc.name, ctx->stream_id, node->desc.id,
+		ctx->streaming_node_cnt);
+
+	ret = mtk_cam_ctx_stream_on(ctx, node);
+	if (ret)
+		goto fail_destroy_session;
+
+	mtk_cam_buf_try_queue(ctx);
+
+	return 0;
+
+fail_destroy_session:
+	if (ctx->session_created)
+		isp_composer_destroy_session(ctx);
+fail_stop_ctx:
+	ctx->streaming_node_cnt--;
+	ctx->streaming_pipe &= ~(1 << node->uid.pipe_id);
+	cam->streaming_pipe &= ~(1 << node->uid.pipe_id);
+	mtk_cam_dev_req_cleanup(ctx, node->uid.pipe_id, VB2_BUF_STATE_QUEUED);
+	mtk_cam_stop_ctx(ctx, node);
+fail_return_buffer:
+	mtk_cam_release_all_buffer(cam);
+	/* relese bufs by request */
+	return ret;
+}
+
+static void mtk_cam_vb2_stop_streaming(struct vb2_queue *vq)
+{
+	struct mtk_cam_device *cam = vb2_get_drv_priv(vq);
+	struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vq);
+	struct device *dev = cam->dev;
+	struct mtk_cam_ctx *ctx;
+
+	ctx = mtk_cam_find_ctx(cam, &node->vdev.entity);
+	if (WARN_ON(!ctx)) {
+		/* the ctx is stop, media_pipeline_stop is called */
+		mtk_cam_dev_req_clean_pending(cam, node->uid.pipe_id,
+					      VB2_BUF_STATE_ERROR);
+		return;
+	}
+
+	dev_dbg(dev, "%s:%s:ctx(%d): node:%d count info:%d\n", __func__,
+		node->desc.name, ctx->stream_id, node->desc.id,
+		ctx->streaming_node_cnt);
+
+	mtk_cam_ctx_stream_off(ctx, node);
+
+	if (cam->streaming_pipe & (1 << node->uid.pipe_id)) {
+		/* NOTE: take multi-pipelines case into consideration     */
+		/* Moreover, must clean bit mask before req cleanup       */
+		/* Otherwise, would cause req not removed in pending list */
+		cam->streaming_pipe &= ~(1 << node->uid.pipe_id);
+		mtk_cam_dev_req_cleanup(ctx, node->uid.pipe_id, VB2_BUF_STATE_ERROR);
+	}
+
+	/* all bufs of node should be return by per requests */
+	mtk_cam_release_all_buffer(ctx->cam);
+
+	/* NOTE: take multi-pipelines case into consideration */
+	cam->streaming_pipe &= ~(1 << node->uid.pipe_id);
+	ctx->streaming_node_cnt--;
+	if (ctx->streaming_node_cnt)
+		return;
+
+	mtk_cam_stop_ctx(ctx, node);
+}
+
+int is_mtk_format(u32 pixelformat)
+{
+	switch (pixelformat) {
+	case V4L2_PIX_FMT_YUYV10:
+	case V4L2_PIX_FMT_YVYU10:
+	case V4L2_PIX_FMT_UYVY10:
+	case V4L2_PIX_FMT_VYUY10:
+	case V4L2_PIX_FMT_YUYV12:
+	case V4L2_PIX_FMT_YVYU12:
+	case V4L2_PIX_FMT_UYVY12:
+	case V4L2_PIX_FMT_VYUY12:
+	case V4L2_PIX_FMT_MTISP_YUYV10P:
+	case V4L2_PIX_FMT_MTISP_YVYU10P:
+	case V4L2_PIX_FMT_MTISP_UYVY10P:
+	case V4L2_PIX_FMT_MTISP_VYUY10P:
+	case V4L2_PIX_FMT_MTISP_YUYV12P:
+	case V4L2_PIX_FMT_MTISP_YVYU12P:
+	case V4L2_PIX_FMT_MTISP_UYVY12P:
+	case V4L2_PIX_FMT_MTISP_VYUY12P:
+	case V4L2_PIX_FMT_NV12_10:
+	case V4L2_PIX_FMT_NV21_10:
+	case V4L2_PIX_FMT_NV16_10:
+	case V4L2_PIX_FMT_NV61_10:
+	case V4L2_PIX_FMT_NV12_12:
+	case V4L2_PIX_FMT_NV21_12:
+	case V4L2_PIX_FMT_NV16_12:
+	case V4L2_PIX_FMT_NV61_12:
+	case V4L2_PIX_FMT_MTISP_NV12_10P:
+	case V4L2_PIX_FMT_MTISP_NV21_10P:
+	case V4L2_PIX_FMT_MTISP_NV16_10P:
+	case V4L2_PIX_FMT_MTISP_NV61_10P:
+	case V4L2_PIX_FMT_MTISP_NV12_12P:
+	case V4L2_PIX_FMT_MTISP_NV21_12P:
+	case V4L2_PIX_FMT_MTISP_NV16_12P:
+	case V4L2_PIX_FMT_MTISP_NV61_12P:
+	case V4L2_PIX_FMT_MTISP_NV12_UFBC:
+	case V4L2_PIX_FMT_MTISP_NV21_UFBC:
+	case V4L2_PIX_FMT_MTISP_NV12_10_UFBC:
+	case V4L2_PIX_FMT_MTISP_NV21_10_UFBC:
+	case V4L2_PIX_FMT_MTISP_NV12_12_UFBC:
+	case V4L2_PIX_FMT_MTISP_NV21_12_UFBC:
+	case V4L2_PIX_FMT_MTISP_BAYER8_UFBC:
+	case V4L2_PIX_FMT_MTISP_BAYER10_UFBC:
+	case V4L2_PIX_FMT_MTISP_BAYER12_UFBC:
+	case V4L2_PIX_FMT_MTISP_BAYER14_UFBC:
+	case V4L2_PIX_FMT_MTISP_SGRB8F:
+	case V4L2_PIX_FMT_MTISP_SGRB10F:
+	case V4L2_PIX_FMT_MTISP_SGRB12F:
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+int is_yuv_ufo(u32 pixelformat)
+{
+	switch (pixelformat) {
+	case V4L2_PIX_FMT_MTISP_NV12_UFBC:
+	case V4L2_PIX_FMT_MTISP_NV21_UFBC:
+	case V4L2_PIX_FMT_MTISP_NV12_10_UFBC:
+	case V4L2_PIX_FMT_MTISP_NV21_10_UFBC:
+	case V4L2_PIX_FMT_MTISP_NV12_12_UFBC:
+	case V4L2_PIX_FMT_MTISP_NV21_12_UFBC:
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+int is_raw_ufo(u32 pixelformat)
+{
+	switch (pixelformat) {
+	case V4L2_PIX_FMT_MTISP_BAYER8_UFBC:
+	case V4L2_PIX_FMT_MTISP_BAYER10_UFBC:
+	case V4L2_PIX_FMT_MTISP_BAYER12_UFBC:
+	case V4L2_PIX_FMT_MTISP_BAYER14_UFBC:
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+int is_fullg_rb(u32 pixelformat)
+{
+	switch (pixelformat) {
+	case V4L2_PIX_FMT_MTISP_SGRB8F:
+	case V4L2_PIX_FMT_MTISP_SGRB10F:
+	case V4L2_PIX_FMT_MTISP_SGRB12F:
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+const struct mtk_format_info *mtk_format_info(u32 format)
+{
+	static const struct mtk_format_info formats[] = {
+		/* YUV planar formats */
+		{ .format = V4L2_PIX_FMT_NV12_10,
+			.mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 },
+			.hdiv = 2, .vdiv = 2, .bit_r_num = 2, .bit_r_den = 1 },
+		{ .format = V4L2_PIX_FMT_NV21_10,
+			.mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 },
+			.hdiv = 2, .vdiv = 2, .bit_r_num = 2, .bit_r_den = 1 },
+		{ .format = V4L2_PIX_FMT_NV16_10,
+			.mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 },
+			.hdiv = 2, .vdiv = 1, .bit_r_num = 2, .bit_r_den = 1 },
+		{ .format = V4L2_PIX_FMT_NV61_10,
+			.mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 },
+			.hdiv = 2, .vdiv = 1, .bit_r_num = 2, .bit_r_den = 1 },
+		{ .format = V4L2_PIX_FMT_YUYV10,
+			.mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 },
+			.hdiv = 2, .vdiv = 1, .bit_r_num = 2, .bit_r_den = 1 },
+		{ .format = V4L2_PIX_FMT_YVYU10,
+			.mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 },
+			.hdiv = 2, .vdiv = 1, .bit_r_num = 2, .bit_r_den = 1 },
+		{ .format = V4L2_PIX_FMT_UYVY10,
+			.mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 },
+			.hdiv = 2, .vdiv = 1, .bit_r_num = 2, .bit_r_den = 1 },
+		{ .format = V4L2_PIX_FMT_VYUY10,
+			.mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 },
+			.hdiv = 2, .vdiv = 1, .bit_r_num = 2, .bit_r_den = 1 },
+		{ .format = V4L2_PIX_FMT_NV12_12,
+			.mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 },
+			.hdiv = 2, .vdiv = 2, .bit_r_num = 2, .bit_r_den = 1 },
+		{ .format = V4L2_PIX_FMT_NV21_12,
+			.mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 },
+			.hdiv = 2, .vdiv = 2, .bit_r_num = 2, .bit_r_den = 1 },
+		{ .format = V4L2_PIX_FMT_NV16_12,
+			.mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 },
+			.hdiv = 2, .vdiv = 1, .bit_r_num = 2, .bit_r_den = 1 },
+		{ .format = V4L2_PIX_FMT_NV61_12,
+			.mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 },
+			.hdiv = 2, .vdiv = 1, .bit_r_num = 2, .bit_r_den = 1 },
+		{ .format = V4L2_PIX_FMT_YUYV12,
+			.mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 },
+			.hdiv = 2, .vdiv = 1, .bit_r_num = 2, .bit_r_den = 1 },
+		{ .format = V4L2_PIX_FMT_YVYU12,
+			.mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 },
+			.hdiv = 2, .vdiv = 1, .bit_r_num = 2, .bit_r_den = 1 },
+		{ .format = V4L2_PIX_FMT_UYVY12,
+			.mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 },
+			.hdiv = 2, .vdiv = 1, .bit_r_num = 2, .bit_r_den = 1 },
+		{ .format = V4L2_PIX_FMT_VYUY12,
+			.mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 },
+			.hdiv = 2, .vdiv = 1, .bit_r_num = 2, .bit_r_den = 1 },
+		/* YUV packed formats */
+		{ .format = V4L2_PIX_FMT_MTISP_YUYV10P,
+			.mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 },
+			.hdiv = 2, .vdiv = 1, .bit_r_num = 5, .bit_r_den = 4 },
+		{ .format = V4L2_PIX_FMT_MTISP_YVYU10P,
+			.mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 },
+			.hdiv = 2, .vdiv = 1, .bit_r_num = 5, .bit_r_den = 4 },
+		{ .format = V4L2_PIX_FMT_MTISP_UYVY10P,
+			.mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 },
+			.hdiv = 2, .vdiv = 1, .bit_r_num = 5, .bit_r_den = 4 },
+		{ .format = V4L2_PIX_FMT_MTISP_VYUY10P,
+			.mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 },
+			.hdiv = 2, .vdiv = 1, .bit_r_num = 5, .bit_r_den = 4 },
+		{ .format = V4L2_PIX_FMT_MTISP_NV12_10P,
+			.mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 },
+			.hdiv = 2, .vdiv = 2, .bit_r_num = 5, .bit_r_den = 4 },
+		{ .format = V4L2_PIX_FMT_MTISP_NV21_10P,
+			.mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 },
+			.hdiv = 2, .vdiv = 2, .bit_r_num = 5, .bit_r_den = 4 },
+		{ .format = V4L2_PIX_FMT_MTISP_NV16_10P,
+			.mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 },
+			.hdiv = 2, .vdiv = 1, .bit_r_num = 5, .bit_r_den = 4 },
+		{ .format = V4L2_PIX_FMT_MTISP_NV61_10P,
+			.mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 },
+			.hdiv = 2, .vdiv = 1, .bit_r_num = 5, .bit_r_den = 4 },
+		{ .format = V4L2_PIX_FMT_MTISP_YUYV12P,
+			.mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 },
+			.hdiv = 2, .vdiv = 1, .bit_r_num = 3, .bit_r_den = 2 },
+		{ .format = V4L2_PIX_FMT_MTISP_YVYU12P,
+			.mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 },
+			.hdiv = 2, .vdiv = 1, .bit_r_num = 3, .bit_r_den = 2 },
+		{ .format = V4L2_PIX_FMT_MTISP_UYVY12P,
+			.mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 },
+			.hdiv = 2, .vdiv = 1, .bit_r_num = 3, .bit_r_den = 2 },
+		{ .format = V4L2_PIX_FMT_MTISP_VYUY12P,
+			.mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 },
+			.hdiv = 2, .vdiv = 1, .bit_r_num = 3, .bit_r_den = 2 },
+		{ .format = V4L2_PIX_FMT_MTISP_NV12_12P,
+			.mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 },
+			.hdiv = 2, .vdiv = 2, .bit_r_num = 3, .bit_r_den = 2 },
+		{ .format = V4L2_PIX_FMT_MTISP_NV21_12P,
+			.mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 },
+			.hdiv = 2, .vdiv = 2, .bit_r_num = 3, .bit_r_den = 2 },
+		{ .format = V4L2_PIX_FMT_MTISP_NV16_12P,
+			.mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 },
+			.hdiv = 2, .vdiv = 1, .bit_r_num = 3, .bit_r_den = 2 },
+		{ .format = V4L2_PIX_FMT_MTISP_NV61_12P,
+			.mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 },
+			.hdiv = 2, .vdiv = 1, .bit_r_num = 3, .bit_r_den = 2 },
+		/* YUV UFBC formats */
+		{ .format = V4L2_PIX_FMT_MTISP_NV12_UFBC,
+			.mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 },
+			.hdiv = 2, .vdiv = 2, .bit_r_num = 2, .bit_r_den = 1 },
+		{ .format = V4L2_PIX_FMT_MTISP_NV21_UFBC,
+			.mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 },
+			.hdiv = 2, .vdiv = 2, .bit_r_num = 2, .bit_r_den = 1 },
+		{ .format = V4L2_PIX_FMT_MTISP_NV12_10_UFBC,
+			.mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 },
+			.hdiv = 2, .vdiv = 2, .bit_r_num = 5, .bit_r_den = 4 },
+		{ .format = V4L2_PIX_FMT_MTISP_NV21_10_UFBC,
+			.mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 },
+			.hdiv = 2, .vdiv = 2, .bit_r_num = 5, .bit_r_den = 4 },
+		{ .format = V4L2_PIX_FMT_MTISP_NV12_12_UFBC,
+			.mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 },
+			.hdiv = 2, .vdiv = 2, .bit_r_num = 3, .bit_r_den = 2 },
+		{ .format = V4L2_PIX_FMT_MTISP_NV21_12_UFBC,
+			.mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 },
+			.hdiv = 2, .vdiv = 2, .bit_r_num = 3, .bit_r_den = 2 },
+		{ .format = V4L2_PIX_FMT_MTISP_BAYER8_UFBC,
+			.mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 },
+			.hdiv = 1, .vdiv = 1, .bit_r_num = 1, .bit_r_den = 1 },
+		{ .format = V4L2_PIX_FMT_MTISP_BAYER10_UFBC,
+			.mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 },
+			.hdiv = 1, .vdiv = 1, .bit_r_num = 5, .bit_r_den = 4 },
+		{ .format = V4L2_PIX_FMT_MTISP_BAYER12_UFBC,
+			.mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 },
+			.hdiv = 1, .vdiv = 1, .bit_r_num = 3, .bit_r_den = 2 },
+		{ .format = V4L2_PIX_FMT_MTISP_BAYER14_UFBC,
+			.mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 },
+			.hdiv = 1, .vdiv = 1, .bit_r_num = 7, .bit_r_den = 4 },
+		/* Full-G RGB formats */
+		{ .format = V4L2_PIX_FMT_MTISP_SGRB8F,
+			.mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 },
+			.hdiv = 2, .vdiv = 2, .bit_r_num = 1, .bit_r_den = 1 },
+		{ .format = V4L2_PIX_FMT_MTISP_SGRB10F,
+			.mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 },
+			.hdiv = 2, .vdiv = 2, .bit_r_num = 5, .bit_r_den = 4 },
+		{ .format = V4L2_PIX_FMT_MTISP_SGRB12F,
+			.mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 },
+			.hdiv = 2, .vdiv = 2, .bit_r_num = 3, .bit_r_den = 2 },
+	};
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(formats); ++i)
+		if (formats[i].format == format)
+			return &formats[i];
+	return NULL;
+}
+
+static void mtk_cam_vb2_buf_queue(struct vb2_buffer *vb)
+{
+	struct mtk_cam_device *cam = vb2_get_drv_priv(vb->vb2_queue);
+	struct mtk_cam_buffer *buf = mtk_cam_vb2_buf_to_dev_buf(vb);
+	struct mtk_cam_request *req = to_mtk_cam_req(vb->request);
+	struct mtk_cam_request_stream_data *req_stream_data;
+	struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vb->vb2_queue);
+	struct mtk_raw_pde_config *pde_cfg;
+	struct device *dev = cam->dev;
+	struct mtkcam_ipi_frame_param *frame_param;
+	struct mtkcam_ipi_meta_input *meta_in;
+	struct mtkcam_ipi_meta_output *meta_out;
+	struct mtk_cam_ctx *ctx;
+	unsigned int pipe_id;
+	unsigned int desc_id;
+	unsigned int dma_port = node->desc.dma_port;
+
+	if (!vb->vb2_queue->uses_requests) {
+		spin_lock(&cam->dma_pending_lock);
+		list_add_tail(&buf->list, &cam->dma_pending);
+		spin_unlock(&cam->dma_pending_lock);
+		buf->state.estate = E_BUF_STATE_QUEUED;
+		if (media_entity_is_streaming(&node->vdev.entity)) {
+			ctx = mtk_cam_find_ctx(cam, &node->vdev.entity);
+			mtk_cam_buf_try_queue(ctx);
+		}
+		return;
+	}
+
+	dma_port = node->desc.dma_port;
+	pipe_id = node->uid.pipe_id;
+	req_stream_data = mtk_cam_req_get_s_data(req, pipe_id, 0);
+	frame_param = &req_stream_data->frame_params;
+	mtk_cam_s_data_set_vbuf(req_stream_data, buf, node->desc.id);
+
+	/* update buffer internal address */
+	switch (dma_port) {
+	case MTKCAM_IPI_RAW_META_STATS_CFG:
+		desc_id = node->desc.id - MTK_RAW_SINK_NUM;
+		meta_in = &frame_param->meta_inputs[desc_id];
+		meta_in->buf.size = node->active_fmt.fmt.meta.buffersize;
+		meta_in->buf.iova = buf->daddr;
+		meta_in->buf.scp_addr = buf->scp_addr;
+		meta_in->uid.id = dma_port;
+		break;
+	case MTKCAM_IPI_RAW_META_STATS_0:
+	case MTKCAM_IPI_RAW_META_STATS_1:
+	case MTKCAM_IPI_RAW_META_STATS_2:
+		pde_cfg = &cam->raw.pipelines[node->uid.pipe_id].pde_config;
+		desc_id = node->desc.id - MTK_RAW_META_OUT_BEGIN;
+		meta_out = &frame_param->meta_outputs[desc_id];
+		meta_out->buf.size = node->active_fmt.fmt.meta.buffersize;
+		meta_out->buf.iova = buf->daddr;
+		meta_out->buf.scp_addr = buf->scp_addr;
+		meta_out->uid.id = dma_port;
+		camsys_set_meta_stats_info(dma_port, vb, pde_cfg);
+		break;
+	default:
+		dev_dbg(dev, "%s:pipe(%d):buffer with invalid port(%d)\n",
+			__func__, pipe_id, dma_port);
+		break;
+	}
+}
+
+static void mtk_cam_vb2_buf_cleanup(struct vb2_buffer *vb)
+{
+	struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vb->vb2_queue);
+	struct mtk_cam_buffer *buf;
+	struct mtk_cam_device *cam = vb2_get_drv_priv(vb->vb2_queue);
+	struct device *dev = cam->raw.devs[0];
+
+	/* SCP address is only valid for meta input buffer */
+	if (!node->desc.smem_alloc)
+		return;
+
+	buf = mtk_cam_vb2_buf_to_dev_buf(vb);
+	dma_unmap_page_attrs(dev, buf->daddr, vb->planes[0].length,
+			     DMA_BIDIRECTIONAL, DMA_ATTR_SKIP_CPU_SYNC);
+}
+
+static void mtk_cam_vb2_request_complete(struct vb2_buffer *vb)
+{
+	struct mtk_cam_device *cam = vb2_get_drv_priv(vb->vb2_queue);
+
+	dev_dbg(vb->vb2_queue->dev, "camsys | request %s\n", __func__);
+
+	v4l2_ctrl_request_complete(vb->req_obj.req, cam->v4l2_dev.ctrl_handler);
+}
+
+static int mtk_cam_vb2_buf_out_validate(struct vb2_buffer *vb)
+{
+	struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
+
+	if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		if (v4l2_buf->field == V4L2_FIELD_ANY)
+			v4l2_buf->field = V4L2_FIELD_NONE;
+
+		if (v4l2_buf->field != V4L2_FIELD_NONE)
+			return -EINVAL;
+	}
+	return 0;
+}
+
+static const struct vb2_ops mtk_cam_vb2_ops = {
+	.queue_setup = mtk_cam_vb2_queue_setup,
+
+	.wait_prepare = vb2_ops_wait_prepare,
+	.wait_finish = vb2_ops_wait_finish,
+
+	.buf_out_validate = mtk_cam_vb2_buf_out_validate,
+	.buf_init = mtk_cam_vb2_buf_init,
+	.buf_prepare = mtk_cam_vb2_buf_prepare,
+
+	.start_streaming = mtk_cam_vb2_start_streaming,
+	.stop_streaming = mtk_cam_vb2_stop_streaming,
+
+	.buf_queue = mtk_cam_vb2_buf_queue,
+	.buf_cleanup = mtk_cam_vb2_buf_cleanup,
+	.buf_request_complete = mtk_cam_vb2_request_complete,
+};
+
+static const struct v4l2_file_operations mtk_cam_v4l2_fops = {
+	.owner = THIS_MODULE,
+	.unlocked_ioctl = video_ioctl2,
+	.open = v4l2_fh_open,
+	.release = vb2_fop_release,
+	.poll = vb2_fop_poll,
+	.mmap = vb2_fop_mmap,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl32 = v4l2_compat_ioctl32,
+#endif
+};
+
+unsigned int mtk_cam_get_sensor_pixel_id(unsigned int fmt)
+{
+	switch (fmt & SENSOR_FMT_MASK) {
+	case MEDIA_BUS_FMT_SBGGR8_1X8:
+	case MEDIA_BUS_FMT_SBGGR10_1X10:
+	case MEDIA_BUS_FMT_SBGGR12_1X12:
+	case MEDIA_BUS_FMT_SBGGR14_1X14:
+		return MTKCAM_IPI_BAYER_PXL_ID_B;
+	case MEDIA_BUS_FMT_SGBRG8_1X8:
+	case MEDIA_BUS_FMT_SGBRG10_1X10:
+	case MEDIA_BUS_FMT_SGBRG12_1X12:
+	case MEDIA_BUS_FMT_SGBRG14_1X14:
+		return MTKCAM_IPI_BAYER_PXL_ID_GB;
+	case MEDIA_BUS_FMT_SGRBG8_1X8:
+	case MEDIA_BUS_FMT_SGRBG10_1X10:
+	case MEDIA_BUS_FMT_SGRBG12_1X12:
+	case MEDIA_BUS_FMT_SGRBG14_1X14:
+		return MTKCAM_IPI_BAYER_PXL_ID_GR;
+	case MEDIA_BUS_FMT_SRGGB8_1X8:
+	case MEDIA_BUS_FMT_SRGGB10_1X10:
+	case MEDIA_BUS_FMT_SRGGB12_1X12:
+	case MEDIA_BUS_FMT_SRGGB14_1X14:
+		return MTKCAM_IPI_BAYER_PXL_ID_R;
+	default:
+		return MTKCAM_IPI_BAYER_PXL_ID_UNKNOWN;
+	}
+}
+
+unsigned int mtk_cam_get_sensor_fmt(unsigned int fmt)
+{
+	switch (fmt & SENSOR_FMT_MASK) {
+	case MEDIA_BUS_FMT_SBGGR8_1X8:
+	case MEDIA_BUS_FMT_SGBRG8_1X8:
+	case MEDIA_BUS_FMT_SGRBG8_1X8:
+	case MEDIA_BUS_FMT_SRGGB8_1X8:
+		return MTKCAM_IPI_IMG_FMT_BAYER8;
+	case MEDIA_BUS_FMT_SBGGR10_1X10:
+	case MEDIA_BUS_FMT_SGBRG10_1X10:
+	case MEDIA_BUS_FMT_SGRBG10_1X10:
+	case MEDIA_BUS_FMT_SRGGB10_1X10:
+		return MTKCAM_IPI_IMG_FMT_BAYER10;
+	case MEDIA_BUS_FMT_SBGGR12_1X12:
+	case MEDIA_BUS_FMT_SGBRG12_1X12:
+	case MEDIA_BUS_FMT_SGRBG12_1X12:
+	case MEDIA_BUS_FMT_SRGGB12_1X12:
+		return MTKCAM_IPI_IMG_FMT_BAYER12;
+	case MEDIA_BUS_FMT_SBGGR14_1X14:
+	case MEDIA_BUS_FMT_SGBRG14_1X14:
+	case MEDIA_BUS_FMT_SGRBG14_1X14:
+	case MEDIA_BUS_FMT_SRGGB14_1X14:
+		return MTKCAM_IPI_IMG_FMT_BAYER14;
+	default:
+		return MTKCAM_IPI_IMG_FMT_UNKNOWN;
+	}
+}
+
+unsigned int mtk_cam_get_pixel_bits(unsigned int ipi_fmt)
+{
+	switch (ipi_fmt) {
+	case MTKCAM_IPI_IMG_FMT_BAYER8:
+	case MTKCAM_IPI_IMG_FMT_FG_BAYER8:
+		return 8;
+	case MTKCAM_IPI_IMG_FMT_BAYER10:
+	case MTKCAM_IPI_IMG_FMT_FG_BAYER10:
+	case MTKCAM_IPI_IMG_FMT_BAYER10_MIPI:
+		return 10;
+	case MTKCAM_IPI_IMG_FMT_BAYER12:
+	case MTKCAM_IPI_IMG_FMT_FG_BAYER12:
+		return 12;
+	case MTKCAM_IPI_IMG_FMT_BAYER14:
+	case MTKCAM_IPI_IMG_FMT_UFBC_BAYER14:
+		return 14;
+	case MTKCAM_IPI_IMG_FMT_BAYER10_UNPACKED:
+	case MTKCAM_IPI_IMG_FMT_BAYER12_UNPACKED:
+	case MTKCAM_IPI_IMG_FMT_BAYER14_UNPACKED:
+	case MTKCAM_IPI_IMG_FMT_BAYER16:
+	case MTKCAM_IPI_IMG_FMT_YUYV:
+	case MTKCAM_IPI_IMG_FMT_YVYU:
+	case MTKCAM_IPI_IMG_FMT_UYVY:
+	case MTKCAM_IPI_IMG_FMT_VYUY:
+		return 16;
+	case MTKCAM_IPI_IMG_FMT_Y8:
+	case MTKCAM_IPI_IMG_FMT_YUV_422_2P:
+	case MTKCAM_IPI_IMG_FMT_YVU_422_2P:
+	case MTKCAM_IPI_IMG_FMT_YUV_422_3P:
+	case MTKCAM_IPI_IMG_FMT_YVU_422_3P:
+	case MTKCAM_IPI_IMG_FMT_YUV_420_2P:
+	case MTKCAM_IPI_IMG_FMT_YVU_420_2P:
+	case MTKCAM_IPI_IMG_FMT_YUV_420_3P:
+	case MTKCAM_IPI_IMG_FMT_YVU_420_3P:
+		return 8;
+	case MTKCAM_IPI_IMG_FMT_YUYV_Y210:
+	case MTKCAM_IPI_IMG_FMT_YVYU_Y210:
+	case MTKCAM_IPI_IMG_FMT_UYVY_Y210:
+	case MTKCAM_IPI_IMG_FMT_VYUY_Y210:
+		return 32;
+	case MTKCAM_IPI_IMG_FMT_YUV_P210:
+	case MTKCAM_IPI_IMG_FMT_YVU_P210:
+	case MTKCAM_IPI_IMG_FMT_YUV_P010:
+	case MTKCAM_IPI_IMG_FMT_YVU_P010:
+	case MTKCAM_IPI_IMG_FMT_YUV_P212:
+	case MTKCAM_IPI_IMG_FMT_YVU_P212:
+	case MTKCAM_IPI_IMG_FMT_YUV_P012:
+	case MTKCAM_IPI_IMG_FMT_YVU_P012:
+		return 16;
+	case MTKCAM_IPI_IMG_FMT_YUYV_Y210_PACKED:
+	case MTKCAM_IPI_IMG_FMT_YVYU_Y210_PACKED:
+	case MTKCAM_IPI_IMG_FMT_UYVY_Y210_PACKED:
+	case MTKCAM_IPI_IMG_FMT_VYUY_Y210_PACKED:
+		return 20;
+	case MTKCAM_IPI_IMG_FMT_YUV_P210_PACKED:
+	case MTKCAM_IPI_IMG_FMT_YVU_P210_PACKED:
+	case MTKCAM_IPI_IMG_FMT_YUV_P010_PACKED:
+	case MTKCAM_IPI_IMG_FMT_YVU_P010_PACKED:
+		return 10;
+	case MTKCAM_IPI_IMG_FMT_YUV_P212_PACKED:
+	case MTKCAM_IPI_IMG_FMT_YVU_P212_PACKED:
+	case MTKCAM_IPI_IMG_FMT_YUV_P012_PACKED:
+	case MTKCAM_IPI_IMG_FMT_YVU_P012_PACKED:
+		return 12;
+	case MTKCAM_IPI_IMG_FMT_RGB_8B_3P:
+	case MTKCAM_IPI_IMG_FMT_FG_BAYER8_3P:
+	case MTKCAM_IPI_IMG_FMT_UFBC_NV12:
+	case MTKCAM_IPI_IMG_FMT_UFBC_NV21:
+	case MTKCAM_IPI_IMG_FMT_UFBC_BAYER8:
+		return 8;
+	case MTKCAM_IPI_IMG_FMT_RGB_10B_3P_PACKED:
+	case MTKCAM_IPI_IMG_FMT_FG_BAYER10_3P_PACKED:
+	case MTKCAM_IPI_IMG_FMT_UFBC_YUV_P010:
+	case MTKCAM_IPI_IMG_FMT_UFBC_YVU_P010:
+	case MTKCAM_IPI_IMG_FMT_UFBC_BAYER10:
+		return 10;
+	case MTKCAM_IPI_IMG_FMT_RGB_12B_3P_PACKED:
+	case MTKCAM_IPI_IMG_FMT_FG_BAYER12_3P_PACKED:
+	case MTKCAM_IPI_IMG_FMT_UFBC_YUV_P012:
+	case MTKCAM_IPI_IMG_FMT_UFBC_YVU_P012:
+	case MTKCAM_IPI_IMG_FMT_UFBC_BAYER12:
+		return 12;
+	case MTKCAM_IPI_IMG_FMT_RGB_10B_3P:
+	case MTKCAM_IPI_IMG_FMT_FG_BAYER10_3P:
+	case MTKCAM_IPI_IMG_FMT_RGB_12B_3P:
+	case MTKCAM_IPI_IMG_FMT_FG_BAYER12_3P:
+		return 16;
+	default:
+		break;
+	}
+	pr_debug("not supported ipi-fmt 0x%08x", ipi_fmt);
+
+	return -1;
+}
+
+unsigned int mtk_cam_get_img_fmt(unsigned int fourcc)
+{
+	switch (fourcc) {
+	case V4L2_PIX_FMT_GREY:
+		return MTKCAM_IPI_IMG_FMT_Y8;
+	case V4L2_PIX_FMT_YUYV:
+		return MTKCAM_IPI_IMG_FMT_YUYV;
+	case V4L2_PIX_FMT_YVYU:
+		return MTKCAM_IPI_IMG_FMT_YVYU;
+	case V4L2_PIX_FMT_NV16:
+		return MTKCAM_IPI_IMG_FMT_YUV_422_2P;
+	case V4L2_PIX_FMT_NV61:
+		return MTKCAM_IPI_IMG_FMT_YVU_422_2P;
+	case V4L2_PIX_FMT_NV12:
+		return MTKCAM_IPI_IMG_FMT_YUV_420_2P;
+	case V4L2_PIX_FMT_NV21:
+		return MTKCAM_IPI_IMG_FMT_YVU_420_2P;
+	case V4L2_PIX_FMT_YUV422P:
+		return MTKCAM_IPI_IMG_FMT_YUV_422_3P;
+	case V4L2_PIX_FMT_YUV420:
+		return MTKCAM_IPI_IMG_FMT_YUV_420_3P;
+	case V4L2_PIX_FMT_YVU420:
+		return MTKCAM_IPI_IMG_FMT_YVU_420_3P;
+	case V4L2_PIX_FMT_NV12_10:
+		return MTKCAM_IPI_IMG_FMT_YUV_P010;
+	case V4L2_PIX_FMT_NV21_10:
+		return MTKCAM_IPI_IMG_FMT_YVU_P010;
+	case V4L2_PIX_FMT_NV16_10:
+		return MTKCAM_IPI_IMG_FMT_YUV_P210;
+	case V4L2_PIX_FMT_NV61_10:
+		return MTKCAM_IPI_IMG_FMT_YVU_P210;
+	case V4L2_PIX_FMT_MTISP_NV12_10P:
+		return MTKCAM_IPI_IMG_FMT_YUV_P010_PACKED;
+	case V4L2_PIX_FMT_MTISP_NV21_10P:
+		return MTKCAM_IPI_IMG_FMT_YVU_P010_PACKED;
+	case V4L2_PIX_FMT_MTISP_NV16_10P:
+		return MTKCAM_IPI_IMG_FMT_YUV_P210_PACKED;
+	case V4L2_PIX_FMT_MTISP_NV61_10P:
+		return MTKCAM_IPI_IMG_FMT_YVU_P210_PACKED;
+	case V4L2_PIX_FMT_YUYV10:
+		return MTKCAM_IPI_IMG_FMT_YUYV_Y210;
+	case V4L2_PIX_FMT_YVYU10:
+		return MTKCAM_IPI_IMG_FMT_YVYU_Y210;
+	case V4L2_PIX_FMT_UYVY10:
+		return MTKCAM_IPI_IMG_FMT_UYVY_Y210;
+	case V4L2_PIX_FMT_VYUY10:
+		return MTKCAM_IPI_IMG_FMT_VYUY_Y210;
+	case V4L2_PIX_FMT_MTISP_YUYV10P:
+		return MTKCAM_IPI_IMG_FMT_YUYV_Y210_PACKED;
+	case V4L2_PIX_FMT_MTISP_YVYU10P:
+		return MTKCAM_IPI_IMG_FMT_YVYU_Y210_PACKED;
+	case V4L2_PIX_FMT_MTISP_UYVY10P:
+		return MTKCAM_IPI_IMG_FMT_UYVY_Y210_PACKED;
+	case V4L2_PIX_FMT_MTISP_VYUY10P:
+		return MTKCAM_IPI_IMG_FMT_VYUY_Y210_PACKED;
+	case V4L2_PIX_FMT_NV12_12:
+		return MTKCAM_IPI_IMG_FMT_YUV_P012;
+	case V4L2_PIX_FMT_NV21_12:
+		return MTKCAM_IPI_IMG_FMT_YVU_P012;
+	case V4L2_PIX_FMT_NV16_12:
+		return MTKCAM_IPI_IMG_FMT_YUV_P212;
+	case V4L2_PIX_FMT_NV61_12:
+		return MTKCAM_IPI_IMG_FMT_YVU_P212;
+	case V4L2_PIX_FMT_MTISP_NV12_12P:
+		return MTKCAM_IPI_IMG_FMT_YUV_P012_PACKED;
+	case V4L2_PIX_FMT_MTISP_NV21_12P:
+		return MTKCAM_IPI_IMG_FMT_YVU_P012_PACKED;
+	case V4L2_PIX_FMT_MTISP_NV16_12P:
+		return MTKCAM_IPI_IMG_FMT_YUV_P212_PACKED;
+	case V4L2_PIX_FMT_MTISP_NV61_12P:
+		return MTKCAM_IPI_IMG_FMT_YVU_P212_PACKED;
+	case V4L2_PIX_FMT_SBGGR8:
+	case V4L2_PIX_FMT_SGBRG8:
+	case V4L2_PIX_FMT_SGRBG8:
+	case V4L2_PIX_FMT_SRGGB8:
+		return MTKCAM_IPI_IMG_FMT_BAYER8;
+	case V4L2_PIX_FMT_MTISP_SBGGR8F:
+	case V4L2_PIX_FMT_MTISP_SGBRG8F:
+	case V4L2_PIX_FMT_MTISP_SGRBG8F:
+	case V4L2_PIX_FMT_MTISP_SRGGB8F:
+		return MTKCAM_IPI_IMG_FMT_FG_BAYER8;
+	case V4L2_PIX_FMT_SBGGR10:
+	case V4L2_PIX_FMT_SGBRG10:
+	case V4L2_PIX_FMT_SGRBG10:
+	case V4L2_PIX_FMT_SRGGB10:
+		return MTKCAM_IPI_IMG_FMT_BAYER10_UNPACKED;
+	case V4L2_PIX_FMT_SBGGR10P:
+	case V4L2_PIX_FMT_SGBRG10P:
+	case V4L2_PIX_FMT_SGRBG10P:
+	case V4L2_PIX_FMT_SRGGB10P:
+		return MTKCAM_IPI_IMG_FMT_BAYER10_MIPI;
+	case V4L2_PIX_FMT_MTISP_SBGGR10:
+	case V4L2_PIX_FMT_MTISP_SGBRG10:
+	case V4L2_PIX_FMT_MTISP_SGRBG10:
+	case V4L2_PIX_FMT_MTISP_SRGGB10:
+		return MTKCAM_IPI_IMG_FMT_BAYER10;
+	case V4L2_PIX_FMT_MTISP_SBGGR10F:
+	case V4L2_PIX_FMT_MTISP_SGBRG10F:
+	case V4L2_PIX_FMT_MTISP_SGRBG10F:
+	case V4L2_PIX_FMT_MTISP_SRGGB10F:
+		return MTKCAM_IPI_IMG_FMT_FG_BAYER10;
+	case V4L2_PIX_FMT_SBGGR12:
+	case V4L2_PIX_FMT_SGBRG12:
+	case V4L2_PIX_FMT_SGRBG12:
+	case V4L2_PIX_FMT_SRGGB12:
+		return MTKCAM_IPI_IMG_FMT_BAYER12_UNPACKED;
+	case V4L2_PIX_FMT_MTISP_SBGGR12:
+	case V4L2_PIX_FMT_MTISP_SGBRG12:
+	case V4L2_PIX_FMT_MTISP_SGRBG12:
+	case V4L2_PIX_FMT_MTISP_SRGGB12:
+		return MTKCAM_IPI_IMG_FMT_BAYER12;
+	case V4L2_PIX_FMT_MTISP_SBGGR12F:
+	case V4L2_PIX_FMT_MTISP_SGBRG12F:
+	case V4L2_PIX_FMT_MTISP_SGRBG12F:
+	case V4L2_PIX_FMT_MTISP_SRGGB12F:
+		return MTKCAM_IPI_IMG_FMT_FG_BAYER12;
+	case V4L2_PIX_FMT_SBGGR14:
+	case V4L2_PIX_FMT_SGBRG14:
+	case V4L2_PIX_FMT_SGRBG14:
+	case V4L2_PIX_FMT_SRGGB14:
+		return MTKCAM_IPI_IMG_FMT_BAYER14_UNPACKED;
+	case V4L2_PIX_FMT_MTISP_SBGGR14:
+	case V4L2_PIX_FMT_MTISP_SGBRG14:
+	case V4L2_PIX_FMT_MTISP_SGRBG14:
+	case V4L2_PIX_FMT_MTISP_SRGGB14:
+		return MTKCAM_IPI_IMG_FMT_BAYER14;
+	case V4L2_PIX_FMT_MTISP_SBGGR14F:
+	case V4L2_PIX_FMT_MTISP_SGBRG14F:
+	case V4L2_PIX_FMT_MTISP_SGRBG14F:
+	case V4L2_PIX_FMT_MTISP_SRGGB14F:
+		return MTKCAM_IPI_IMG_FMT_FG_BAYER14;
+	case V4L2_PIX_FMT_SBGGR16:
+	case V4L2_PIX_FMT_SGBRG16:
+	case V4L2_PIX_FMT_SGRBG16:
+	case V4L2_PIX_FMT_SRGGB16:
+		return MTKCAM_IPI_IMG_FMT_BAYER16;
+	case V4L2_PIX_FMT_MTISP_NV12_UFBC:
+		return MTKCAM_IPI_IMG_FMT_UFBC_NV12;
+	case V4L2_PIX_FMT_MTISP_NV21_UFBC:
+		return MTKCAM_IPI_IMG_FMT_UFBC_NV21;
+	case V4L2_PIX_FMT_MTISP_NV12_10_UFBC:
+		return MTKCAM_IPI_IMG_FMT_UFBC_YUV_P010;
+	case V4L2_PIX_FMT_MTISP_NV21_10_UFBC:
+		return MTKCAM_IPI_IMG_FMT_UFBC_YVU_P010;
+	case V4L2_PIX_FMT_MTISP_NV12_12_UFBC:
+		return MTKCAM_IPI_IMG_FMT_UFBC_YUV_P012;
+	case V4L2_PIX_FMT_MTISP_NV21_12_UFBC:
+		return MTKCAM_IPI_IMG_FMT_UFBC_YVU_P012;
+	case V4L2_PIX_FMT_MTISP_BAYER8_UFBC:
+		return MTKCAM_IPI_IMG_FMT_UFBC_BAYER8;
+	case V4L2_PIX_FMT_MTISP_BAYER10_UFBC:
+		return MTKCAM_IPI_IMG_FMT_UFBC_BAYER10;
+	case V4L2_PIX_FMT_MTISP_BAYER12_UFBC:
+		return MTKCAM_IPI_IMG_FMT_UFBC_BAYER12;
+	case V4L2_PIX_FMT_MTISP_BAYER14_UFBC:
+		return MTKCAM_IPI_IMG_FMT_UFBC_BAYER14;
+	case V4L2_PIX_FMT_MTISP_SGRB8F:
+		return MTKCAM_IPI_IMG_FMT_FG_BAYER8_3P;
+	case V4L2_PIX_FMT_MTISP_SGRB10F:
+		return MTKCAM_IPI_IMG_FMT_FG_BAYER10_3P_PACKED;
+	case V4L2_PIX_FMT_MTISP_SGRB12F:
+		return MTKCAM_IPI_IMG_FMT_FG_BAYER12_3P_PACKED;
+	default:
+		return MTKCAM_IPI_IMG_FMT_UNKNOWN;
+	}
+}
+
+static void mtk_cam_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt,
+				   u32 pixelformat, u32 width, u32 height)
+{
+	struct v4l2_plane_pix_format *plane;
+	unsigned int ipi_fmt = mtk_cam_get_img_fmt(pixelformat);
+	u8 pixel_bits = mtk_cam_get_pixel_bits(ipi_fmt);
+	u32 stride;
+	u32 aligned_width;
+	u8 bus_size;
+	u8 i;
+
+	pixfmt->width = width;
+	pixfmt->height = height;
+	pixfmt->pixelformat = pixelformat;
+	plane = &pixfmt->plane_fmt[0];
+	bus_size = mtk_cam_yuv_dma_bus_size(pixel_bits, 0);
+	plane->sizeimage = 0;
+
+	if (is_mtk_format(pixelformat)) {
+		const struct mtk_format_info *info;
+
+		info = mtk_format_info(pixelformat);
+		if (!info)
+			return;
+
+		pixfmt->num_planes = info->mem_planes;
+		if (info->mem_planes == 1) {
+			if (is_yuv_ufo(pixelformat)) {
+				/* UFO format width should align 64 pixel */
+				aligned_width = ALIGN(width, 64);
+				stride = aligned_width * info->bit_r_num / info->bit_r_den;
+
+				if (stride > plane->bytesperline)
+					plane->bytesperline = stride;
+				plane->sizeimage = stride * height;
+				plane->sizeimage += stride * height / 2;
+				plane->sizeimage += ALIGN((aligned_width / 64), 8) * height;
+				plane->sizeimage += ALIGN((aligned_width / 64), 8) * height / 2;
+				plane->sizeimage += sizeof(struct ufbc_buffer_header);
+			} else if (is_raw_ufo(pixelformat)) {
+				/* UFO format width should align 64 pixel */
+				aligned_width = ALIGN(width, 64);
+				stride = aligned_width * info->bit_r_num / info->bit_r_den;
+
+				if (stride > plane->bytesperline)
+					plane->bytesperline = stride;
+				plane->sizeimage = stride * height;
+				plane->sizeimage += ALIGN((aligned_width / 64), 8) * height;
+				plane->sizeimage += sizeof(struct ufbc_buffer_header);
+			} else {
+				/* width should be bus_size align */
+				aligned_width = ALIGN(DIV_ROUND_UP(width
+					* info->bit_r_num, info->bit_r_den), bus_size);
+				stride = aligned_width * info->bpp[0];
+
+				if (stride > plane->bytesperline)
+					plane->bytesperline = stride;
+
+				for (i = 0; i < info->comp_planes; i++) {
+					unsigned int hdiv = (i == 0) ? 1 : info->hdiv;
+					unsigned int vdiv = (i == 0) ? 1 : info->vdiv;
+
+					if (plane->bytesperline > stride &&
+					    is_fullg_rb(pixelformat)) {
+						plane->sizeimage +=
+							DIV_ROUND_UP(plane->bytesperline, hdiv)
+							* DIV_ROUND_UP(height, vdiv);
+					} else if (plane->bytesperline > stride &&
+						   !is_fullg_rb(pixelformat)) {
+						plane->sizeimage +=
+							plane->bytesperline
+							* DIV_ROUND_UP(height, vdiv);
+					} else {
+						plane->sizeimage += info->bpp[i]
+							* DIV_ROUND_UP(aligned_width, hdiv)
+							* DIV_ROUND_UP(height, vdiv);
+					}
+				}
+			}
+			pr_debug("%s stride %d sizeimage %d\n", __func__,
+				 plane->bytesperline, plane->sizeimage);
+		} else {
+			pr_debug("do not support non contiguous mplane\n");
+		}
+	} else {
+		const struct v4l2_format_info *info;
+
+		pr_debug("pixelformat:0x%x sizeimage:%d\n",
+			 pixelformat, plane->sizeimage);
+		info = v4l2_format_info(pixelformat);
+		if (!info)
+			return;
+
+		pixfmt->num_planes = info->mem_planes;
+		if (info->mem_planes == 1) {
+			aligned_width = ALIGN(width, bus_size);
+			stride = aligned_width * info->bpp[0];
+			if (stride > plane->bytesperline)
+				plane->bytesperline = stride;
+
+			for (i = 0; i < info->comp_planes; i++) {
+				unsigned int hdiv = (i == 0) ? 1 : info->hdiv;
+				unsigned int vdiv = (i == 0) ? 1 : info->vdiv;
+
+				plane->sizeimage += info->bpp[i]
+					* DIV_ROUND_UP(aligned_width, hdiv)
+					* DIV_ROUND_UP(height, vdiv);
+			}
+			pr_debug("%s stride %d sizeimage %d\n", __func__,
+				 plane->bytesperline, plane->sizeimage);
+		} else {
+			pr_warn("do not support non contiguous mplane\n");
+		}
+	}
+}
+
+static void mtk_cam_fill_ext_fmtdesc(struct v4l2_fmtdesc *fmt)
+{
+	const char *descr = NULL;
+	const unsigned int sz = sizeof(fmt->description);
+
+	switch (fmt->pixelformat) {
+	case V4L2_PIX_FMT_MTISP_SBGGR8:
+		descr = "8-bit Bayer BGGR MTISP Packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_SGBRG8:
+		descr = "8-bit Bayer GBRG MTISP Packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_SGRBG8:
+		descr = "8-bit Bayer GRBG MTISP Packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_SRGGB8:
+		descr = "8-bit Bayer RGGB MTISP Packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_SBGGR10:
+		descr = "10-bit Bayer BGGR MTISP Packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_SGBRG10:
+		descr = "10-bit Bayer GBRG MTISP Packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_SGRBG10:
+		descr = "10-bit Bayer GRBG MTISP Packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_SRGGB10:
+		descr = "10-bit Bayer RGGB MTISP Packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_SBGGR12:
+		descr = "12-bit Bayer BGGR MTISP Packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_SGBRG12:
+		descr = "12-bit Bayer GBRG MTISP Packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_SGRBG12:
+		descr = "12-bit Bayer GRBG MTISP Packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_SRGGB12:
+		descr = "12-bit Bayer RGGB MTISP Packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_SBGGR14:
+		descr = "14-bit Bayer BGGR MTISP Packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_SGBRG14:
+		descr = "14-bit Bayer GBRG MTISP Packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_SGRBG14:
+		descr = "14-bit Bayer GRBG MTISP Packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_SRGGB14:
+		descr = "14-bit Bayer RGGB MTISP Packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_SBGGR8F:
+		descr = "8-bit Full-G Bayer BGGR Packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_SGBRG8F:
+		descr = "8-bit Full-G Bayer GBRG Packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_SGRBG8F:
+		descr = "8-bit Full-G Bayer GRBG Packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_SRGGB8F:
+		descr = "8-bit Full-G Bayer RGGB Packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_SBGGR10F:
+		descr = "10-bit Full-G Bayer BGGR Packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_SGBRG10F:
+		descr = "10-bit Full-G Bayer GBRG Packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_SGRBG10F:
+		descr = "10-bit Full-G Bayer GRBG Packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_SRGGB10F:
+		descr = "10-bit Full-G Bayer RGGB Packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_SBGGR12F:
+		descr = "12-bit Full-G Bayer BGGR Packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_SGBRG12F:
+		descr = "12-bit Full-G Bayer GBRG Packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_SGRBG12F:
+		descr = "12-bit Full-G Bayer GRBG Packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_SRGGB12F:
+		descr = "12-bit Full-G Bayer RGGB Packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_SBGGR14F:
+		descr = "14-bit Full-G Bayer BGGR Packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_SGBRG14F:
+		descr = "14-bit Full-G Bayer GBRG Packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_SGRBG14F:
+		descr = "14-bit Full-G Bayer GRBG Packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_SRGGB14F:
+		descr = "14-bit Full-G Bayer RGGB Packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_NV12_10P:
+		descr = "Y/CbCr 4:2:0 10 bits packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_NV21_10P:
+		descr = "Y/CrCb 4:2:0 10 bits packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_NV16_10P:
+		descr = "Y/CbCr 4:2:2 10 bits packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_NV61_10P:
+		descr = "Y/CrCb 4:2:2 10 bits packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_YUYV10P:
+		descr = "YUYV 4:2:2 10 bits packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_YVYU10P:
+		descr = "YVYU 4:2:2 10 bits packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_UYVY10P:
+		descr = "UYVY 4:2:2 10 bits packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_VYUY10P:
+		descr = "VYUY 4:2:2 10 bits packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_NV12_12P:
+		descr = "Y/CbCr 4:2:0 12 bits packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_NV21_12P:
+		descr = "Y/CrCb 4:2:0 12 bits packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_NV16_12P:
+		descr = "Y/CbCr 4:2:2 12 bits packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_NV61_12P:
+		descr = "Y/CrCb 4:2:2 12 bits packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_YUYV12P:
+		descr = "YUYV 4:2:2 12 bits packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_YVYU12P:
+		descr = "YVYU 4:2:2 12 bits packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_UYVY12P:
+		descr = "UYVY 4:2:2 12 bits packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_VYUY12P:
+		descr = "VYUY 4:2:2 12 bits packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_NV12_UFBC:
+		descr = "YCbCr 420 8 bits compress";
+		break;
+	case V4L2_PIX_FMT_MTISP_NV21_UFBC:
+		descr = "YCrCb 420 8 bits compress";
+		break;
+	case V4L2_PIX_FMT_MTISP_NV12_10_UFBC:
+		descr = "YCbCr 420 10 bits compress";
+		break;
+	case V4L2_PIX_FMT_MTISP_NV21_10_UFBC:
+		descr = "YCrCb 420 10 bits compress";
+		break;
+	case V4L2_PIX_FMT_MTISP_NV12_12_UFBC:
+		descr = "YCbCr 420 12 bits compress";
+		break;
+	case V4L2_PIX_FMT_MTISP_NV21_12_UFBC:
+		descr = "YCrCb 420 12 bits compress";
+		break;
+	case V4L2_PIX_FMT_MTISP_BAYER8_UFBC:
+		descr = "RAW 8 bits compress";
+		break;
+	case V4L2_PIX_FMT_MTISP_BAYER10_UFBC:
+		descr = "RAW 10 bits compress";
+		break;
+	case V4L2_PIX_FMT_MTISP_BAYER12_UFBC:
+		descr = "RAW 12 bits compress";
+		break;
+	case V4L2_PIX_FMT_MTISP_BAYER14_UFBC:
+		descr = "RAW 14 bits compress";
+		break;
+	case V4L2_PIX_FMT_MTISP_SGRB8F:
+		descr = "8-bit 3 plane GRB Packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_SGRB10F:
+		descr = "10-bit 3 plane GRB Packed";
+		break;
+	case V4L2_PIX_FMT_MTISP_SGRB12F:
+		descr = "12-bit 3 plane GRB Packed";
+		break;
+	case V4L2_META_FMT_MTISP_PARAMS:
+		descr = "MTK ISP Tuning Metadata";
+		break;
+	case V4L2_META_FMT_MTISP_3A:
+		descr = "MTK 3A Statistics";
+		break;
+	case V4L2_META_FMT_MTISP_AF:
+		descr = "MTK AF Statistics";
+		break;
+	case V4L2_META_FMT_MTISP_LCS:
+		descr = "MTK LCS Statistics";
+		break;
+	case V4L2_META_FMT_MTISP_LMV:
+		descr = "MTK LMV Statistics";
+		break;
+	default:
+		descr = NULL;
+		break;
+	}
+
+	if (descr)
+		WARN_ON(strscpy(fmt->description, descr, sz) < 0);
+}
+
+static void cal_image_pix_mp(unsigned int node_id,
+			     struct v4l2_pix_format_mplane *mp,
+			     unsigned int pixel_mode)
+{
+	unsigned int ipi_fmt = mtk_cam_get_img_fmt(mp->pixelformat);
+	unsigned int width = mp->width;
+	unsigned int height = mp->height;
+	unsigned int stride, i;
+
+	pr_debug("fmt:0x%x ipi_fmt:%d\n", mp->pixelformat, ipi_fmt);
+	switch (ipi_fmt) {
+	case MTKCAM_IPI_IMG_FMT_BAYER8:
+	case MTKCAM_IPI_IMG_FMT_BAYER10:
+	case MTKCAM_IPI_IMG_FMT_BAYER12:
+	case MTKCAM_IPI_IMG_FMT_BAYER14:
+	case MTKCAM_IPI_IMG_FMT_BAYER16:
+	case MTKCAM_IPI_IMG_FMT_BAYER10_MIPI:
+	case MTKCAM_IPI_IMG_FMT_BAYER10_UNPACKED:
+	case MTKCAM_IPI_IMG_FMT_BAYER12_UNPACKED:
+	case MTKCAM_IPI_IMG_FMT_BAYER14_UNPACKED:
+	case MTKCAM_IPI_IMG_FMT_FG_BAYER8:
+	case MTKCAM_IPI_IMG_FMT_FG_BAYER10:
+	case MTKCAM_IPI_IMG_FMT_FG_BAYER12:
+	case MTKCAM_IPI_IMG_FMT_FG_BAYER14:
+		stride = mtk_cam_dmao_xsize(width, ipi_fmt, pixel_mode);
+		for (i = 0; i < mp->num_planes; i++) {
+			if (stride > mp->plane_fmt[i].bytesperline)
+				mp->plane_fmt[i].bytesperline = stride;
+			mp->plane_fmt[i].sizeimage =
+				mp->plane_fmt[i].bytesperline * height;
+		}
+	break;
+	case MTKCAM_IPI_IMG_FMT_YUYV:
+	case MTKCAM_IPI_IMG_FMT_YVYU:
+	case MTKCAM_IPI_IMG_FMT_UYVY:
+	case MTKCAM_IPI_IMG_FMT_VYUY:
+	case MTKCAM_IPI_IMG_FMT_YUV_422_2P:
+	case MTKCAM_IPI_IMG_FMT_YVU_422_2P:
+	case MTKCAM_IPI_IMG_FMT_YUV_422_3P:
+	case MTKCAM_IPI_IMG_FMT_YVU_422_3P:
+	case MTKCAM_IPI_IMG_FMT_YUV_420_2P:
+	case MTKCAM_IPI_IMG_FMT_YVU_420_2P:
+	case MTKCAM_IPI_IMG_FMT_YUV_420_3P:
+	case MTKCAM_IPI_IMG_FMT_YVU_420_3P:
+	case MTKCAM_IPI_IMG_FMT_Y8:
+	case MTKCAM_IPI_IMG_FMT_YUYV_Y210:
+	case MTKCAM_IPI_IMG_FMT_YVYU_Y210:
+	case MTKCAM_IPI_IMG_FMT_UYVY_Y210:
+	case MTKCAM_IPI_IMG_FMT_VYUY_Y210:
+	case MTKCAM_IPI_IMG_FMT_YUYV_Y210_PACKED:
+	case MTKCAM_IPI_IMG_FMT_YVYU_Y210_PACKED:
+	case MTKCAM_IPI_IMG_FMT_UYVY_Y210_PACKED:
+	case MTKCAM_IPI_IMG_FMT_VYUY_Y210_PACKED:
+	case MTKCAM_IPI_IMG_FMT_YUV_P210:
+	case MTKCAM_IPI_IMG_FMT_YVU_P210:
+	case MTKCAM_IPI_IMG_FMT_YUV_P010:
+	case MTKCAM_IPI_IMG_FMT_YVU_P010:
+	case MTKCAM_IPI_IMG_FMT_YUV_P210_PACKED:
+	case MTKCAM_IPI_IMG_FMT_YVU_P210_PACKED:
+	case MTKCAM_IPI_IMG_FMT_YUV_P010_PACKED:
+	case MTKCAM_IPI_IMG_FMT_YVU_P010_PACKED:
+	case MTKCAM_IPI_IMG_FMT_YUV_P212:
+	case MTKCAM_IPI_IMG_FMT_YVU_P212:
+	case MTKCAM_IPI_IMG_FMT_YUV_P012:
+	case MTKCAM_IPI_IMG_FMT_YVU_P012:
+	case MTKCAM_IPI_IMG_FMT_YUV_P212_PACKED:
+	case MTKCAM_IPI_IMG_FMT_YVU_P212_PACKED:
+	case MTKCAM_IPI_IMG_FMT_YUV_P012_PACKED:
+	case MTKCAM_IPI_IMG_FMT_YVU_P012_PACKED:
+	case MTKCAM_IPI_IMG_FMT_UFBC_NV12:
+	case MTKCAM_IPI_IMG_FMT_UFBC_NV21:
+	case MTKCAM_IPI_IMG_FMT_UFBC_YUV_P010:
+	case MTKCAM_IPI_IMG_FMT_UFBC_YVU_P010:
+	case MTKCAM_IPI_IMG_FMT_UFBC_YUV_P012:
+	case MTKCAM_IPI_IMG_FMT_UFBC_YVU_P012:
+	case MTKCAM_IPI_IMG_FMT_UFBC_BAYER8:
+	case MTKCAM_IPI_IMG_FMT_UFBC_BAYER10:
+	case MTKCAM_IPI_IMG_FMT_UFBC_BAYER12:
+	case MTKCAM_IPI_IMG_FMT_UFBC_BAYER14:
+	case MTKCAM_IPI_IMG_FMT_FG_BAYER8_3P:
+	case MTKCAM_IPI_IMG_FMT_FG_BAYER10_3P_PACKED:
+	case MTKCAM_IPI_IMG_FMT_FG_BAYER12_3P_PACKED:
+		mtk_cam_fill_pixfmt_mp(mp, mp->pixelformat, width, height);
+	break;
+	default:
+		break;
+	}
+}
+
+static int mtk_video_init_format(struct mtk_cam_video_device *video)
+{
+	struct mtk_cam_dev_node_desc *desc = &video->desc;
+	struct v4l2_format *active = &video->active_fmt;
+	const struct v4l2_format *default_fmt =
+		&desc->fmts[desc->default_fmt_idx].vfmt;
+
+	active->type = desc->buf_type;
+
+	if (!desc->image) {
+		active->fmt.meta.dataformat = default_fmt->fmt.meta.dataformat;
+		active->fmt.meta.buffersize = default_fmt->fmt.meta.buffersize;
+		return 0;
+	}
+
+	active->fmt.pix_mp.pixelformat = default_fmt->fmt.pix_mp.pixelformat;
+	active->fmt.pix_mp.width = default_fmt->fmt.pix_mp.width;
+	active->fmt.pix_mp.height = default_fmt->fmt.pix_mp.height;
+	active->fmt.pix_mp.num_planes = default_fmt->fmt.pix_mp.num_planes;
+
+	cal_image_pix_mp(desc->id, &active->fmt.pix_mp, 0);
+
+	/* set init one-plane */
+	active->fmt.pix_mp.num_planes = 1;
+	active->fmt.pix_mp.colorspace = V4L2_COLORSPACE_SRGB;
+	active->fmt.pix_mp.field = V4L2_FIELD_NONE;
+	active->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+	active->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT;
+	active->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_SRGB;
+
+	return 0;
+}
+
+int mtk_cam_video_register(struct mtk_cam_video_device *video,
+			   struct v4l2_device *v4l2_dev)
+{
+	struct mtk_cam_device *cam =
+		container_of(v4l2_dev, struct mtk_cam_device, v4l2_dev);
+	struct media_pad *pad = &video->pad;
+	struct video_device *vdev = &video->vdev;
+	struct vb2_queue *q = &video->vb2_q;
+	unsigned int output = V4L2_TYPE_IS_OUTPUT(video->desc.buf_type);
+	int ret;
+
+	if (video->desc.link_flags & MEDIA_LNK_FL_ENABLED)
+		video->enabled = true;
+	else
+		video->enabled = false;
+
+	mutex_init(&video->q_lock);
+
+	/* initialize vb2_queue */
+	q->type = video->desc.buf_type;
+	q->io_modes = VB2_MMAP | VB2_DMABUF;
+
+	if (q->type == V4L2_BUF_TYPE_META_OUTPUT)
+		q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	else
+		q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_BOOTIME;
+
+	if (video->desc.smem_alloc) {
+		q->bidirectional = 1;
+		/* reserved memory */
+		q->dev = cam->smem_dev;
+	} else if (is_yuv_node(video->desc.id)) {
+		q->dev = cam->raw.yuvs[0];
+	} else {
+		q->dev = cam->raw.devs[0];
+	}
+
+	q->supports_requests = true;
+	q->lock = &video->q_lock;
+	q->ops = &mtk_cam_vb2_ops;
+	q->mem_ops = &vb2_dma_contig_memops;
+	q->drv_priv = cam;
+	q->buf_struct_size = sizeof(struct mtk_cam_buffer);
+
+	if (output)
+		q->timestamp_flags |= V4L2_BUF_FLAG_TSTAMP_SRC_EOF;
+	else
+		q->timestamp_flags |= V4L2_BUF_FLAG_TSTAMP_SRC_SOE;
+
+	/* No minimum buffers limitation */
+	q->min_queued_buffers = 0;
+
+	ret = vb2_queue_init(q);
+	if (ret < 0) {
+		dev_info(v4l2_dev->dev, "Failed to init vb2 queue: %d\n", ret);
+		goto error_vb2_init;
+	}
+
+	pad->flags = output ? MEDIA_PAD_FL_SOURCE : MEDIA_PAD_FL_SINK;
+	ret = media_entity_pads_init(&vdev->entity, 1, pad);
+	if (ret < 0) {
+		dev_info(v4l2_dev->dev, "Failed to init video entity: %d\n", ret);
+		goto error_media_init;
+	}
+
+	ret = mtk_video_init_format(video);
+	if (ret < 0) {
+		dev_info(v4l2_dev->dev, "Failed to init format: %d\n", ret);
+		goto error_video_register;
+	}
+
+	vdev->entity.function = MEDIA_ENT_F_IO_V4L;
+	vdev->entity.ops = NULL;
+	vdev->fops = &mtk_cam_v4l2_fops;
+	vdev->device_caps = video->desc.cap | V4L2_CAP_STREAMING;
+	vdev->v4l2_dev = v4l2_dev;
+
+	vdev->vfl_dir = output ? VFL_DIR_TX : VFL_DIR_RX;
+	vdev->queue = &video->vb2_q;
+	vdev->ioctl_ops = video->desc.ioctl_ops;
+	vdev->release = video_device_release_empty;
+	/* share q_lock */
+	vdev->lock = &video->q_lock;
+	strscpy(vdev->name, video->desc.name, sizeof(vdev->name));
+
+	ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+	if (ret < 0) {
+		dev_info(v4l2_dev->dev, "Failed to register video device: %d\n",
+			 ret);
+		goto error_video_register;
+	}
+	video_set_drvdata(vdev, cam);
+
+	dev_dbg(v4l2_dev->dev, "registered vdev:%d:%s\n",
+		video->desc.id, vdev->name);
+
+	return 0;
+
+error_video_register:
+	media_entity_cleanup(&vdev->entity);
+error_media_init:
+	vb2_queue_release(&video->vb2_q);
+error_vb2_init:
+	mutex_destroy(&video->q_lock);
+
+	return ret;
+}
+
+void mtk_cam_video_unregister(struct mtk_cam_video_device *video)
+{
+	video_unregister_device(&video->vdev);
+	vb2_queue_release(&video->vb2_q);
+	media_entity_cleanup(&video->vdev.entity);
+	mutex_destroy(&video->q_lock);
+}
+
+const struct v4l2_format *
+mtk_cam_dev_find_fmt(struct mtk_cam_dev_node_desc *desc, u32 format)
+{
+	unsigned int i;
+	const struct v4l2_format *fmt;
+
+	for (i = 0; i < desc->num_fmts; i++) {
+		fmt = &desc->fmts[i].vfmt;
+		if (fmt->fmt.pix_mp.pixelformat == format)
+			return fmt;
+	}
+
+	return NULL;
+}
+
+int mtk_cam_vidioc_querycap(struct file *file, void *fh,
+			    struct v4l2_capability *cap)
+{
+	struct mtk_cam_device *cam = video_drvdata(file);
+
+	strscpy(cap->driver, dev_driver_string(cam->dev), sizeof(cap->driver));
+	strscpy(cap->card, dev_driver_string(cam->dev), sizeof(cap->card));
+	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+		 dev_name(cam->dev));
+
+	return 0;
+}
+
+int mtk_cam_vidioc_enum_framesizes(struct file *filp, void *priv,
+				   struct v4l2_frmsizeenum *sizes)
+{
+	struct mtk_cam_video_device *node = file_to_mtk_cam_node(filp);
+	const struct v4l2_format *dev_fmt;
+
+	dev_fmt = mtk_cam_dev_find_fmt(&node->desc, sizes->pixel_format);
+	if (!dev_fmt || sizes->index)
+		return -EINVAL;
+
+	sizes->type = node->desc.frmsizes->type;
+	memcpy(&sizes->stepwise, &node->desc.frmsizes->stepwise,
+	       sizeof(sizes->stepwise));
+	return 0;
+}
+
+int mtk_cam_vidioc_enum_fmt(struct file *file, void *fh,
+			    struct v4l2_fmtdesc *f)
+{
+	struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
+
+	if (f->index >= node->desc.num_fmts)
+		return -EINVAL;
+
+	f->pixelformat = node->desc.fmts[f->index].vfmt.fmt.pix_mp.pixelformat;
+	f->flags = 0;
+	/* extended fmt description is filled here */
+	/* common fmt description is filled in v4l_fill_fmtdesc */
+	mtk_cam_fill_ext_fmtdesc(f);
+
+	return 0;
+}
+
+int mtk_cam_vidioc_g_fmt(struct file *file, void *fh,
+			 struct v4l2_format *f)
+{
+	struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
+
+	f->fmt = node->active_fmt.fmt;
+
+	return 0;
+}
+
+int mtk_cam_vidioc_s_fmt(struct file *file, void *fh,
+			 struct v4l2_format *f)
+{
+	struct mtk_cam_device *cam = video_drvdata(file);
+	struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
+	struct mtk_raw_pipeline *raw_pipeline;
+	int raw_feature = 0;
+
+	raw_pipeline = mtk_cam_dev_get_raw_pipeline(cam, node->uid.pipe_id);
+
+	if (!vb2_is_busy(node->vdev.queue)) {
+		/* Get the valid format */
+		if (raw_pipeline)
+			raw_feature = raw_pipeline->user_res.raw_res.feature;
+
+		mtk_cam_video_set_fmt(node, f, raw_feature);
+
+		/* Configure to video device */
+		node->active_fmt = *f;
+		return 0;
+	}
+
+	dev_info(cam->dev,
+		 "%s:pipe(%d):%s:Cannot change format while streaming\n",
+		 __func__, node->uid.pipe_id, node->desc.name);
+
+	return -EBUSY;
+}
+
+int mtk_cam_video_set_fmt(struct mtk_cam_video_device *node,
+			  struct v4l2_format *f, int raw_feature)
+{
+	struct mtk_cam_device *cam = video_get_drvdata(&node->vdev);
+	const struct v4l2_format *dev_fmt;
+	struct v4l2_format try_fmt;
+	s32 i;
+
+	dev_dbg(cam->dev,
+		"%s:pipe(%d):%s:feature(0x%x)\n",
+		__func__, node->uid.pipe_id, node->desc.name, raw_feature);
+
+	memset(&try_fmt, 0, sizeof(try_fmt));
+	try_fmt.type = f->type;
+
+	/* Validate pixelformat */
+	dev_fmt = mtk_cam_dev_find_fmt(&node->desc, f->fmt.pix_mp.pixelformat);
+	if (!dev_fmt) {
+		dev_dbg(cam->dev, "unknown fmt:%d\n",
+			f->fmt.pix_mp.pixelformat);
+		dev_fmt = &node->desc.fmts[node->desc.default_fmt_idx].vfmt;
+	}
+	try_fmt.fmt.pix_mp.pixelformat = dev_fmt->fmt.pix_mp.pixelformat;
+
+	/* Validate image width & height range */
+	try_fmt.fmt.pix_mp.width = clamp_val(f->fmt.pix_mp.width,
+					     IMG_MIN_WIDTH, IMG_MAX_WIDTH);
+	try_fmt.fmt.pix_mp.height = clamp_val(f->fmt.pix_mp.height,
+					      IMG_MIN_HEIGHT, IMG_MAX_HEIGHT);
+	/* 4 bytes alignment for width */
+	/* width and stride should align bus_size */
+	try_fmt.fmt.pix_mp.width = ALIGN(try_fmt.fmt.pix_mp.width, IMG_PIX_ALIGN);
+	try_fmt.fmt.pix_mp.num_planes = 1;
+
+	for (i = 0 ; i < try_fmt.fmt.pix_mp.num_planes ; i++)
+		try_fmt.fmt.pix_mp.plane_fmt[i].bytesperline =
+				f->fmt.pix_mp.plane_fmt[i].bytesperline;
+
+	/* bytesperline & sizeimage calculation */
+	cal_image_pix_mp(node->desc.id, &try_fmt.fmt.pix_mp, 0);
+
+	/* Constant format fields */
+	try_fmt.fmt.pix_mp.colorspace = V4L2_COLORSPACE_SRGB;
+	try_fmt.fmt.pix_mp.field = V4L2_FIELD_NONE;
+	try_fmt.fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+	try_fmt.fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT;
+	try_fmt.fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_SRGB;
+
+	*f = try_fmt;
+
+	return 0;
+}
+
+int mtk_cam_vidioc_try_fmt(struct file *file, void *fh,
+			   struct v4l2_format *f)
+{
+	struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
+
+	mtk_cam_video_set_fmt(node, f, 0);
+
+	return 0;
+}
+
+int mtk_cam_vidioc_meta_enum_fmt(struct file *file, void *fh,
+				 struct v4l2_fmtdesc *f)
+{
+	struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
+
+	if (f->index)
+		return -EINVAL;
+
+	f->pixelformat = node->active_fmt.fmt.meta.dataformat;
+	f->flags = 0;
+	/* extended fmt description is filled here */
+	/* common fmt description is filled in v4l_fill_fmtdesc */
+	mtk_cam_fill_ext_fmtdesc(f);
+
+	return 0;
+}
+
+int mtk_cam_vidioc_g_meta_fmt(struct file *file, void *fh,
+			      struct v4l2_format *f)
+{
+	struct mtk_cam_device *cam = video_drvdata(file);
+	struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
+	struct mtk_cam_dev_node_desc *desc = &node->desc;
+	const struct v4l2_format *default_fmt =
+		&desc->fmts[desc->default_fmt_idx].vfmt;
+	struct mtk_raw_pde_config *pde_cfg;
+	struct mtk_cam_pde_info *pde_info;
+
+	if (node->desc.dma_port == MTKCAM_IPI_RAW_META_STATS_CFG) {
+		pde_cfg = &cam->raw.pipelines[node->uid.pipe_id].pde_config;
+		pde_info = &pde_cfg->pde_info;
+		if (pde_info->pd_table_offset) {
+			node->active_fmt.fmt.meta.buffersize =
+				default_fmt->fmt.meta.buffersize +
+				pde_info->pdi_max_size;
+
+			dev_dbg(cam->dev, "PDE: node(%d), enlarge meta size(%u)",
+				node->desc.dma_port,
+				node->active_fmt.fmt.meta.buffersize);
+		}
+	}
+	if (node->desc.dma_port == MTKCAM_IPI_RAW_META_STATS_0) {
+		pde_cfg = &cam->raw.pipelines[node->uid.pipe_id].pde_config;
+		pde_info = &pde_cfg->pde_info;
+		if (pde_info->pd_table_offset) {
+			node->active_fmt.fmt.meta.buffersize =
+				default_fmt->fmt.meta.buffersize +
+				pde_info->pdo_max_size;
+
+			dev_dbg(cam->dev, "PDE: node(%d), enlarge meta size(%u)",
+				node->desc.dma_port,
+				node->active_fmt.fmt.meta.buffersize);
+		}
+	}
+	f->fmt.meta.dataformat = node->active_fmt.fmt.meta.dataformat;
+	f->fmt.meta.buffersize = node->active_fmt.fmt.meta.buffersize;
+
+	return 0;
+}
+
+int mtk_cam_vidioc_s_selection(struct file *file, void *fh,
+			       struct v4l2_selection *s)
+{
+	struct mtk_cam_device *cam = video_drvdata(file);
+	struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
+	struct mtk_raw_pipeline *raw_pipeline;
+
+	raw_pipeline = mtk_cam_dev_get_raw_pipeline(cam, node->uid.pipe_id);
+	if (raw_pipeline) {
+		node->active_crop = *s;
+
+		dev_dbg(raw_pipeline->subdev.v4l2_dev->dev,
+			"%s:%s:%s:Set selection (%d,%d,%d,%d)\n",
+			__func__, raw_pipeline->subdev.name, node->desc.name,
+			s->r.left, s->r.top, s->r.width, s->r.height);
+	}
+
+	return 0;
+}
diff --git a/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-video.h b/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-video.h
new file mode 100644
index 000000000000..6c79987670f8
--- /dev/null
+++ b/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-video.h
@@ -0,0 +1,224 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ */
+
+#ifndef __MTK_CAM_VIDEO_H
+#define __MTK_CAM_VIDEO_H
+
+#include <linux/types.h>
+#include <linux/videodev2.h>
+#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-dev.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-v4l2.h>
+
+#include "mtk_cam-ipi.h"
+
+#define MAX_PLANE_NUM 3
+
+struct mtk_cam_resource;
+struct mtk_raw_pde_config;
+
+typedef int (*set_pad_fmt_func_t)(struct v4l2_subdev *sd,
+			struct v4l2_subdev_state *sd_state,
+			struct v4l2_mbus_framefmt *sink_fmt,
+			struct mtk_cam_resource *res,
+			int pad, int which);
+
+typedef int (*set_pad_selection_func_t)(struct v4l2_subdev *sd,
+			struct v4l2_subdev_state *sd_state,
+			struct v4l2_mbus_framefmt *sink_fmt,
+			struct mtk_cam_resource *res,
+			int pad, int which);
+
+/*For state analysis and controlling for request*/
+enum MTK_BUF_STATE {
+	E_BUF_STATE_QUEUED = 0x0,
+	E_BUF_STATE_COMPOSED,
+	E_BUF_STATE_CQ,
+	E_BUF_STATE_OUTER,
+};
+
+struct mtk_buf_state {
+	enum MTK_BUF_STATE estate;
+	struct list_head list;
+};
+
+/**
+ * struct mtk_cam_buffer - MTK camera device buffer.
+ *
+ * @vbb: Embedded struct vb2_v4l2_buffer.
+ * @list: List entry for the buffer queue
+ * @daddr: The DMA address of this buffer.
+ * @scp_addr: The SCP address of this buffer which
+ *            is only supported for meta input node.
+ * @state: The camera buffer status.
+ */
+struct mtk_cam_buffer {
+	struct vb2_v4l2_buffer vbb;
+	struct list_head list;
+
+	dma_addr_t daddr;
+	dma_addr_t scp_addr;
+	struct mtk_buf_state state;
+};
+
+struct mtk_cam_format_desc {
+	struct v4l2_format vfmt;
+	struct v4l2_mbus_framefmt pfmt;
+};
+
+struct mtk_cam_pad_ops {
+	set_pad_fmt_func_t set_pad_fmt;
+	set_pad_selection_func_t set_pad_selection;
+};
+
+/**
+ * struct mtk_cam_dev_node_desc - MTK camera device node descriptor
+ *
+ * @id: id of the node
+ * @name: name of the node
+ * @cap: supported V4L2 capabilities
+ * @buf_type: supported V4L2 buffer type
+ * @dma_port: the dma ports associated to the node
+ * @link_flags: default media link flags
+ * @smem_alloc: using the smem_dev as alloc device or not
+ * @need_cache_sync_on_prepare: do cache sync at buf_prepare (userspace sync)
+ * @need_cache_sync_on_finish: do cache sync at buf_finish (userspace sync)
+ * @image: true for image node, false for meta node
+ * @num_fmts: the number of supported node formats
+ * @default_fmt_idx: default format of this node
+ * @max_buf_count: maximum VB2 buffer count
+ * @ioctl_ops:  mapped to v4l2_ioctl_ops
+ * @fmts: supported format
+ * @frmsizes: supported V4L2 frame size number
+ * @pad_ops: set and select pad configurations and formats
+ */
+struct mtk_cam_dev_node_desc {
+	u8 id;
+	const char *name;
+	u32 cap;
+	u32 buf_type;
+	u32 dma_port;
+	u32 link_flags;
+	u8 smem_alloc:1;
+	u8 image:1;
+	u8 num_fmts;
+	u8 default_fmt_idx;
+	u8 max_buf_count;
+	const struct v4l2_ioctl_ops *ioctl_ops;
+	const struct mtk_cam_format_desc *fmts;
+	const struct v4l2_frmsizeenum *frmsizes;
+	struct mtk_cam_pad_ops *pad_ops;
+};
+
+/**
+ * struct mtk_cam_video_device - MediaTek video device structure.
+ */
+struct mtk_cam_video_device {
+	struct mtkcam_ipi_uid uid;
+	struct mtk_cam_dev_node_desc desc;
+	unsigned int enabled;
+
+	struct vb2_queue vb2_q;
+	struct video_device vdev;
+	struct media_pad pad;
+	struct v4l2_format active_fmt;
+	/* use first 4 elements of reserved field of v4l2_pix_format_mplane as request fd */
+	struct v4l2_format pending_fmt;
+	/* use first elements of reserved field of v4l2_selection as request fd*/
+	struct v4l2_selection active_crop;
+	/* Serializes vb2 queue and video device operations */
+	struct mutex q_lock;
+	int streaming_id;
+
+	/* cached ctx info */
+	struct mtk_cam_ctx *ctx;
+};
+
+struct mtk_format_info {
+	u32 format;
+	u8 mem_planes;
+	u8 comp_planes;
+	u8 bpp[4];
+	u8 hdiv;
+	u8 vdiv;
+	u8 bit_r_num; /* numerator of bit ratio */
+	u8 bit_r_den; /* denominator of bit ratio */
+};
+
+int mtk_cam_video_register(struct mtk_cam_video_device *video,
+			   struct v4l2_device *v4l2_dev);
+
+void mtk_cam_video_unregister(struct mtk_cam_video_device *video);
+
+static inline struct mtk_cam_video_device *
+file_to_mtk_cam_node(struct file *__file)
+{
+	return container_of(video_devdata(__file), struct mtk_cam_video_device, vdev);
+}
+
+static inline struct mtk_cam_buffer *
+mtk_cam_vb2_buf_to_dev_buf(struct vb2_buffer *__vb)
+{
+	return container_of(__vb, struct mtk_cam_buffer, vbb.vb2_buf);
+}
+
+static inline struct mtk_cam_video_device *
+mtk_cam_vbq_to_vdev(struct vb2_queue *__vq)
+{
+	return container_of(__vq, struct mtk_cam_video_device, vb2_q);
+}
+
+const struct v4l2_format *
+mtk_cam_dev_find_fmt(struct mtk_cam_dev_node_desc *desc, u32 format);
+
+int mtk_cam_vidioc_querycap(struct file *file, void *fh,
+			    struct v4l2_capability *cap);
+
+int mtk_cam_vidioc_enum_framesizes(struct file *filp, void *priv,
+				   struct v4l2_frmsizeenum *sizes);
+
+int mtk_cam_vidioc_enum_fmt(struct file *file, void *fh,
+			    struct v4l2_fmtdesc *f);
+
+int mtk_cam_vidioc_g_fmt(struct file *file, void *fh, struct v4l2_format *f);
+
+int mtk_cam_vidioc_s_fmt(struct file *file, void *fh, struct v4l2_format *f);
+
+int mtk_cam_vidioc_try_fmt(struct file *file, void *fh, struct v4l2_format *f);
+
+int mtk_cam_vidioc_meta_enum_fmt(struct file *file, void *fh,
+				 struct v4l2_fmtdesc *f);
+
+int mtk_cam_vidioc_s_selection(struct file *file, void *fh,
+			       struct v4l2_selection *s);
+
+int mtk_cam_vidioc_g_meta_fmt(struct file *file, void *fh,
+			      struct v4l2_format *f);
+
+/* Utility functions to convert format enum */
+unsigned int mtk_cam_get_sensor_pixel_id(unsigned int fmt);
+
+unsigned int mtk_cam_get_sensor_fmt(unsigned int fmt);
+
+unsigned int mtk_cam_get_pixel_bits(unsigned int pix_fmt);
+
+unsigned int mtk_cam_get_img_fmt(unsigned int fourcc);
+
+int mtk_cam_video_set_fmt(struct mtk_cam_video_device *node,
+			  struct v4l2_format *f, int feature);
+
+int is_mtk_format(u32 pixelformat);
+
+int is_yuv_ufo(u32 pixelformat);
+
+int is_raw_ufo(u32 pixelformat);
+
+int is_fullg_rb(u32 pixelformat);
+
+const struct mtk_format_info *mtk_format_info(u32 format);
+
+#endif /*__MTK_CAM_VIDEO_H */