lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20241009111551.27052-8-Shu-hsiang.Yang@mediatek.com>
Date: Wed, 9 Oct 2024 19:15:48 +0800
From: Shu-hsiang Yang <Shu-hsiang.Yang@...iatek.com>
To: Mauro Carvalho Chehab <mchehab@...nel.org>, Rob Herring <robh@...nel.org>,
	Krzysztof Kozlowski <krzk+dt@...nel.org>, Conor Dooley <conor+dt@...nel.org>,
	Matthias Brugger <matthias.bgg@...il.com>, AngeloGioacchino Del Regno
	<angelogioacchino.delregno@...labora.com>, Sumit Semwal
	<sumit.semwal@...aro.org>, Christian Konig <christian.koenig@....com>
CC: <linux-media@...r.kernel.org>, <devicetree@...r.kernel.org>,
	<linux-kernel@...r.kernel.org>, <linux-arm-kernel@...ts.infradead.org>,
	<linux-mediatek@...ts.infradead.org>, <dri-devel@...ts.freedesktop.org>,
	<linaro-mm-sig@...ts.linaro.org>,
	<Project_Global_Chrome_Upstream_Group@...iatek.com>,
	<yaya.chang@...iatek.com>, <teddy.chen@...iatek.com>,
	<hidenorik@...omium.org>, <yunkec@...omium.org>, <shun-yi.wang@...iatek.com>,
	Shu-hsiang Yang <Shu-hsiang.Yang@...iatek.com>
Subject: [PATCH v1 07/10] media: platform: mediatek: add isp_7x video ops

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@...iatek.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

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 */
-- 
2.18.0


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ