From: Frederic Chen <frederic.c...@mediatek.com>

This patch adds the driver of Digital Image Processing (DIP)
unit in Mediatek ISP system, providing image format
conversion, resizing, and rotation features.

The mtk-isp directory will contain drivers for multiple IP
blocks found in Mediatek ISP system. It will include ISP
Pass 1 driver(CAM), sensor interface driver, DIP driver and
face detection driver.

Signed-off-by: Frederic Chen <frederic.c...@mediatek.com>
---
 drivers/media/platform/mtk-isp/Makefile       |    7 +
 .../media/platform/mtk-isp/isp_50/Makefile    |    7 +
 .../platform/mtk-isp/isp_50/dip/Makefile      |   18 +
 .../platform/mtk-isp/isp_50/dip/mtk_dip-dev.c |  769 +++++++
 .../platform/mtk-isp/isp_50/dip/mtk_dip-dev.h |  337 ++++
 .../platform/mtk-isp/isp_50/dip/mtk_dip-hw.h  |  155 ++
 .../platform/mtk-isp/isp_50/dip/mtk_dip-sys.c |  794 ++++++++
 .../mtk-isp/isp_50/dip/mtk_dip-v4l2.c         | 1786 +++++++++++++++++
 8 files changed, 3873 insertions(+)
 create mode 100644 drivers/media/platform/mtk-isp/Makefile
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/Makefile
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/dip/Makefile
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-dev.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-dev.h
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-hw.h
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-sys.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-v4l2.c

diff --git a/drivers/media/platform/mtk-isp/Makefile 
b/drivers/media/platform/mtk-isp/Makefile
new file mode 100644
index 000000000000..b08d3bdf2800
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2019 MediaTek Inc.
+#
+
+obj-y += isp_50/
+
diff --git a/drivers/media/platform/mtk-isp/isp_50/Makefile 
b/drivers/media/platform/mtk-isp/isp_50/Makefile
new file mode 100644
index 000000000000..564c3889c34c
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2019 MediaTek Inc.
+#
+
+obj-$(CONFIG_VIDEO_MEDIATEK_ISP_DIP) += dip/
+
diff --git a/drivers/media/platform/mtk-isp/isp_50/dip/Makefile 
b/drivers/media/platform/mtk-isp/isp_50/dip/Makefile
new file mode 100644
index 000000000000..99e760d7d5a9
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/dip/Makefile
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2019 MediaTek Inc.
+#
+
+$(info $(srctree))
+ccflags-y += -I$(srctree)/drivers/media/platform/mtk-mdp3
+
+obj-$(CONFIG_VIDEO_MEDIATEK_ISP_DIP) += mtk_dip-v4l2.o
+
+# Utilities to provide frame-based streaming model
+# with v4l2 user interfaces and alloc context managing
+# memory shared between ISP and coprocessor
+mtk_dip_util-objs := \
+mtk_dip-dev.o \
+mtk_dip-sys.o
+
+obj-$(CONFIG_VIDEO_MEDIATEK_ISP_DIP) += mtk_dip_util.o
diff --git a/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-dev.c 
b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-dev.c
new file mode 100644
index 000000000000..63256fa27428
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-dev.c
@@ -0,0 +1,769 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ *
+ * Author: Frederic Chen <frederic.c...@mediatek.com>
+ *
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/v4l2-event.h>
+#include "mtk_dip-dev.h"
+#include "mtk-mdp3-regs.h"
+#include "mtk-img-ipi.h"
+
+int mtk_dip_pipe_init(struct mtk_dip_pipe *pipe,
+                     struct mtk_dip_dev *dip_dev,
+                     const struct mtk_dip_pipe_desc *setting,
+                     struct media_device *media_dev,
+                     struct v4l2_device *v4l2_dev)
+{
+       int ret, i, count = 0;
+
+       pipe->dip_dev = dip_dev;
+       pipe->desc = setting;
+       pipe->num_nodes = MTK_DIP_VIDEO_NODE_ID_TOTAL_NUM;
+
+       atomic_set(&pipe->pipe_job_sequence, 0);
+       INIT_LIST_HEAD(&pipe->pipe_job_running_list);
+       INIT_LIST_HEAD(&pipe->pipe_job_pending_list);
+       spin_lock_init(&pipe->job_lock);
+       mutex_init(&pipe->lock);
+
+       for (i = 0; i < MTK_DIP_VIDEO_NODE_ID_TOTAL_NUM; i++) {
+               if (i < MTK_DIP_VIDEO_NODE_ID_OUT_TOTAL_NUM) {
+                       pipe->nodes[i].desc =
+                       &pipe->desc->output_queue_descs[i];
+               } else {
+                       int cap_idx = i - MTK_DIP_VIDEO_NODE_ID_OUT_TOTAL_NUM;
+
+                       pipe->nodes[i].desc =
+                               &pipe->desc->capture_queue_descs[cap_idx];
+               }
+
+               pipe->nodes[i].flags =
+                       pipe->nodes[i].desc->flags;
+               atomic_set(&pipe->nodes[i].sequence, 0);
+               spin_lock_init(&pipe->nodes[i].buf_list_lock);
+               INIT_LIST_HEAD(&pipe->nodes[i].buf_list);
+
+               if (pipe->nodes[i].flags & MEDIA_LNK_FL_ENABLED)
+                       count++;
+       }
+
+       if (pipe->desc->master >= 0 &&
+           pipe->desc->master < MTK_DIP_VIDEO_NODE_ID_TOTAL_NUM) {
+               if (!(pipe->nodes[pipe->desc->master].flags &
+                   MEDIA_LNK_FL_ENABLED))
+                       count++;
+
+               pipe->nodes[pipe->desc->master].flags |=
+                       MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE;
+       }
+
+       atomic_set(&pipe->cnt_nodes_not_streaming, count);
+
+       ret = mtk_dip_pipe_v4l2_register(pipe, media_dev, v4l2_dev);
+       if (ret) {
+               dev_err(&pipe->dip_dev->pdev->dev,
+                       "%s: failed(%d) to create V4L2 devices\n",
+                       pipe->desc->name, ret);
+
+               goto err_destroy_pipe_lock;
+       }
+
+       return 0;
+
+err_destroy_pipe_lock:
+       mutex_destroy(&pipe->lock);
+
+       return ret;
+}
+
+int mtk_dip_pipe_release(struct mtk_dip_pipe *pipe)
+{
+       mtk_dip_pipe_v4l2_unregister(pipe);
+       mutex_destroy(&pipe->lock);
+
+       return 0;
+}
+
+int mtk_dip_pipe_next_job_id(struct mtk_dip_pipe *pipe)
+{
+       int global_job_id = atomic_inc_return(&pipe->pipe_job_sequence);
+
+       return (global_job_id & 0x0000FFFF) | (pipe->desc->id << 16);
+}
+
+struct mtk_dip_pipe_job_info*
+mtk_dip_pipe_get_running_job_info(struct mtk_dip_pipe *pipe, int pipe_job_id)
+{
+       struct mtk_dip_pipe_job_info *job_info;
+
+       spin_lock(&pipe->job_lock);
+       list_for_each_entry(job_info,
+                           &pipe->pipe_job_running_list, list) {
+               if (job_info->id == pipe_job_id) {
+                       spin_unlock(&pipe->job_lock);
+                       return job_info;
+               }
+       }
+       spin_unlock(&pipe->job_lock);
+
+       return NULL;
+}
+
+void mtk_dip_pipe_debug_job(struct mtk_dip_pipe *pipe,
+                           struct mtk_dip_pipe_job_info *job_info)
+{
+       int i;
+
+       dev_dbg(&pipe->dip_dev->pdev->dev, "%s: pipe-job(%p),id(%d)\n",
+               pipe->desc->name, job_info, job_info->id);
+
+       for (i = 0; i < MTK_DIP_VIDEO_NODE_ID_TOTAL_NUM ; i++) {
+               if (job_info->buf_map[i])
+                       dev_dbg(&pipe->dip_dev->pdev->dev, "%s:%s:buf(%p)\n",
+                               pipe->desc->name, pipe->nodes[i].desc->name, i,
+                               job_info->buf_map[i]);
+       }
+}
+
+int mtk_dip_pipe_job_finish(struct mtk_dip_pipe *pipe,
+                           unsigned int pipe_job_info_id,
+                           enum vb2_buffer_state vbf_state)
+{
+       struct mtk_dip_pipe_job_info *job_info;
+       struct mtk_dip_dev_buffer *in_buf;
+       int i, num_running_jobs;
+
+       job_info = mtk_dip_pipe_get_running_job_info(pipe, pipe_job_info_id);
+       in_buf = job_info->buf_map[MTK_DIP_VIDEO_NODE_ID_RAW_OUT];
+
+       spin_lock(&pipe->job_lock);
+       list_del(&job_info->list);
+       num_running_jobs = --pipe->num_jobs;
+       dev_dbg(&pipe->dip_dev->pdev->dev,
+               "%s:%s: num of running jobs(%d)\n",
+               __func__, pipe->desc->name, pipe->num_jobs);
+       spin_unlock(&pipe->job_lock);
+
+       for (i = 0; i < MTK_DIP_VIDEO_NODE_ID_TOTAL_NUM; i++) {
+               struct mtk_dip_dev_buffer *dev_buf = job_info->buf_map[i];
+               struct mtk_dip_video_device *node;
+
+               if (!dev_buf)
+                       continue;
+
+               if (in_buf && dev_buf != in_buf)
+                       dev_buf->vbb.vb2_buf.timestamp =
+                               in_buf->vbb.vb2_buf.timestamp;
+
+               vb2_buffer_done(&dev_buf->vbb.vb2_buf, vbf_state);
+
+               node = mtk_dip_vbq_to_node(dev_buf->vbb.vb2_buf.vb2_queue);
+               spin_lock(&node->buf_list_lock);
+               list_del(&dev_buf->list);
+               spin_unlock(&node->buf_list_lock);
+
+               dev_dbg(&pipe->dip_dev->pdev->dev,
+                       "%s:%s: return buf, idx(%d), state(%d)\n",
+                       pipe->desc->name, node->desc->name,
+                       dev_buf->vbb.vb2_buf.index, vbf_state);
+       }
+
+       dev_dbg(&pipe->dip_dev->pdev->dev,
+               "%s:%s: finished job id(%d), vbf_state(%d), running jobs(%d)\n",
+               __func__, pipe->desc->name, pipe_job_info_id, vbf_state,
+               num_running_jobs);
+
+       return 0;
+}
+
+static __u32 get_pixel_byte_by_fmt(__u32 pix_fmt)
+{
+       switch (pix_fmt) {
+       case V4L2_PIX_FMT_MTISP_B8:
+       case V4L2_PIX_FMT_MTISP_F8:
+               return 8;
+       case V4L2_PIX_FMT_MTISP_B10:
+       case V4L2_PIX_FMT_MTISP_F10:
+               return 10;
+       case V4L2_PIX_FMT_MTISP_B12:
+       case V4L2_PIX_FMT_MTISP_F12:
+               return 12;
+       case V4L2_PIX_FMT_MTISP_B14:
+       case V4L2_PIX_FMT_MTISP_F14:
+               return 14;
+       case V4L2_PIX_FMT_MTISP_U8:
+       case V4L2_PIX_FMT_MTISP_U10:
+       case V4L2_PIX_FMT_MTISP_U12:
+       case V4L2_PIX_FMT_MTISP_U14:
+               return 16;
+       default:
+               return 0;
+       }
+}
+
+static __u32
+mtk_dip_pass1_cal_main_stride(__u32 width, __u32 pix_fmt)
+{
+       __u32 stride;
+       __u32 pixel_byte = get_pixel_byte_by_fmt(pix_fmt);
+
+       width = ALIGN(width, 4);
+       stride = ALIGN(DIV_ROUND_UP(width * pixel_byte, 8), 2);
+
+       return ALIGN(stride, 4);
+}
+
+static __u32
+mtk_dip_pass1_cal_pack_stride(__u32 width, __u32 pix_fmt)
+{
+       __u32 stride;
+       __u32 pixel_byte = get_pixel_byte_by_fmt(pix_fmt);
+
+       width = ALIGN(width, 4);
+       stride = DIV_ROUND_UP(width * 3, 2);
+       stride = DIV_ROUND_UP(stride * pixel_byte, 8);
+
+       if (pix_fmt == V4L2_PIX_FMT_MTISP_F10)
+               stride =  ALIGN(stride, 4);
+       else
+               stride =  ALIGN(stride, 8);
+
+       return stride;
+}
+
+static int is_stride_need_to_align(u32 format, u32 *need_aligned_fmts,
+                                  int length)
+{
+       int i;
+
+       for (i = 0; i < length; i++) {
+               if (format == need_aligned_fmts[i])
+                       return true;
+       }
+
+       return false;
+}
+
+/* Stride that is accepted by MDP HW */
+static u32 dip_mdp_fmt_get_stride(struct v4l2_pix_format_mplane *mfmt,
+                                 const struct mtk_dip_dev_format *fmt,
+                                 unsigned int plane)
+{
+       enum mdp_color c = fmt->mdp_color;
+       u32 bytesperline = (mfmt->width * fmt->row_depth[plane]) / 8;
+       u32 stride = (bytesperline * MDP_COLOR_BITS_PER_PIXEL(c))
+               / fmt->row_depth[0];
+
+       if (plane == 0)
+               return stride;
+
+       if (plane < MDP_COLOR_GET_PLANE_COUNT(c)) {
+               if (MDP_COLOR_IS_BLOCK_MODE(c))
+                       stride = stride / 2;
+               return stride;
+       }
+
+       return 0;
+}
+
+/* Stride that is accepted by MDP HW of format with contiguous planes */
+static u32
+dip_mdp_fmt_get_stride_contig(const struct mtk_dip_dev_format *fmt,
+                             u32 pix_stride,
+                             unsigned int plane)
+{
+       enum mdp_color c = fmt->mdp_color;
+       u32 stride = pix_stride;
+
+       if (plane == 0)
+               return stride;
+
+       if (plane < MDP_COLOR_GET_PLANE_COUNT(c)) {
+               stride = stride >> MDP_COLOR_GET_H_SUBSAMPLE(c);
+               if (MDP_COLOR_IS_UV_COPLANE(c) && !MDP_COLOR_IS_BLOCK_MODE(c))
+                       stride = stride * 2;
+               return stride;
+       }
+
+       return 0;
+}
+
+/* Plane size that is accepted by MDP HW */
+static u32
+dip_mdp_fmt_get_plane_size(const struct mtk_dip_dev_format *fmt,
+                          u32 stride, u32 height,
+                          unsigned int plane)
+{
+       enum mdp_color c = fmt->mdp_color;
+       u32 bytesperline;
+
+       bytesperline = (stride * fmt->row_depth[0])
+               / MDP_COLOR_BITS_PER_PIXEL(c);
+       if (plane == 0)
+               return bytesperline * height;
+       if (plane < MDP_COLOR_GET_PLANE_COUNT(c)) {
+               height = height >> MDP_COLOR_GET_V_SUBSAMPLE(c);
+               if (MDP_COLOR_IS_BLOCK_MODE(c))
+                       bytesperline = bytesperline * 2;
+               return bytesperline * height;
+       }
+
+       return 0;
+}
+
+static int mtk_dip_pipe_get_stride(struct mtk_dip_pipe *pipe,
+                                  struct v4l2_pix_format_mplane *mfmt,
+                                  const struct mtk_dip_dev_format *dfmt,
+                                  int plane,
+                                  char *buf_name)
+{
+       int bpl;
+       u32 fmts_pass1_main[] = {
+               V4L2_PIX_FMT_MTISP_B8,
+               V4L2_PIX_FMT_MTISP_B10
+       };
+       u32 fmts_pass1_pack[] = {
+               V4L2_PIX_FMT_MTISP_F8,
+               V4L2_PIX_FMT_MTISP_F10
+       };
+
+       if (is_stride_need_to_align(mfmt->pixelformat, fmts_pass1_main,
+                                   ARRAY_SIZE(fmts_pass1_main)))
+               bpl = mtk_dip_pass1_cal_main_stride(mfmt->width,
+                                                   mfmt->pixelformat);
+       else if (is_stride_need_to_align(mfmt->pixelformat, fmts_pass1_pack,
+                                        ARRAY_SIZE(fmts_pass1_pack)))
+               bpl = mtk_dip_pass1_cal_pack_stride(mfmt->width,
+                                                   mfmt->pixelformat);
+       else
+               bpl = dip_mdp_fmt_get_stride(mfmt, dfmt, plane);
+
+       dev_dbg(&pipe->dip_dev->pdev->dev,
+               "%s:%s:%s: plane(%d), pixelformat(%x) width(%d), stride(%d)",
+               __func__, pipe->desc->name, buf_name, plane,
+               mfmt->pixelformat, mfmt->width, bpl);
+
+       return bpl;
+}
+
+void mtk_dip_pipe_set_img_fmt(struct mtk_dip_pipe *pipe,
+                             struct mtk_dip_video_device *node,
+                             struct v4l2_pix_format_mplane *mfmt_to_fill,
+                             const struct mtk_dip_dev_format *dev_fmt)
+{
+       int i;
+
+       mfmt_to_fill->pixelformat = dev_fmt->format;
+       mfmt_to_fill->num_planes = dev_fmt->num_planes;
+       mfmt_to_fill->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+       mfmt_to_fill->quantization = V4L2_QUANTIZATION_DEFAULT;
+       mfmt_to_fill->colorspace = dev_fmt->colorspace;
+       mfmt_to_fill->field = V4L2_FIELD_NONE;
+
+       memset(mfmt_to_fill->reserved, 0, sizeof(mfmt_to_fill->reserved));
+
+       dev_dbg(&pipe->dip_dev->pdev->dev,
+               "%s:%s:%s: Fmt(%c%c%c%c),  w(%d),h(%d), f(%d)\n",
+               __func__, pipe->desc->name, node->desc->name,
+               mfmt_to_fill->pixelformat & 0xff,
+               mfmt_to_fill->pixelformat >> 8 & 0xff,
+               mfmt_to_fill->pixelformat >> 16 & 0xff,
+               mfmt_to_fill->pixelformat >> 24 & 0xff,
+               mfmt_to_fill->width, mfmt_to_fill->height,
+               mfmt_to_fill->field);
+
+       for (i = 0; i < mfmt_to_fill->num_planes; ++i) {
+               u32 min_bpl = (mfmt_to_fill->width * dev_fmt->row_depth[i]) / 8;
+               u32 bpl = mfmt_to_fill->plane_fmt[i].bytesperline;
+               u32 sizeimage;
+
+               if (bpl < min_bpl)
+                       bpl = min_bpl;
+
+               sizeimage = (bpl * mfmt_to_fill->height * dev_fmt->depth[i])
+                       / dev_fmt->row_depth[i];
+               mfmt_to_fill->plane_fmt[i].bytesperline = bpl;
+               if (mfmt_to_fill->plane_fmt[i].sizeimage < sizeimage)
+                       mfmt_to_fill->plane_fmt[i].sizeimage = sizeimage;
+
+               memset(mfmt_to_fill->plane_fmt[i].reserved,
+                      0, sizeof(mfmt_to_fill->plane_fmt[i].reserved));
+
+               dev_dbg(&pipe->dip_dev->pdev->dev,
+                       "%s:%s:%s: plane(%d): bpl(%u), min_bpl(%u), s(%u)\n",
+                       __func__, pipe->desc->name, node->desc->name,
+                       i, bpl, min_bpl, mfmt_to_fill->plane_fmt[i].sizeimage);
+       }
+}
+
+static void set_meta_fmt(struct mtk_dip_pipe *pipe,
+                        struct v4l2_meta_format *metafmt_to_fill,
+                        const struct mtk_dip_dev_format *dev_fmt)
+{
+       metafmt_to_fill->dataformat = dev_fmt->format;
+
+       if (dev_fmt->buffer_size <= 0) {
+               dev_dbg(&pipe->dip_dev->pdev->dev,
+                       "%s: Invalid meta buf size(%u), use default(%u)\n",
+                       pipe->desc->name, dev_fmt->buffer_size,
+                       MTK_DIP_DEV_META_BUF_DEFAULT_SIZE);
+
+               metafmt_to_fill->buffersize =
+                       MTK_DIP_DEV_META_BUF_DEFAULT_SIZE;
+       } else {
+               metafmt_to_fill->buffersize = dev_fmt->buffer_size;
+       }
+}
+
+void mtk_dip_pipe_load_default_fmt(struct mtk_dip_pipe *pipe,
+                                  struct mtk_dip_video_device *node,
+                                  struct v4l2_format *fmt_to_fill)
+{
+       int idx = node->desc->default_fmt_idx;
+
+       if (node->desc->num_fmts == 0) {
+               dev_err(&pipe->dip_dev->pdev->dev,
+                       "%s:%s: desc->num_fmts is 0, no format support list\n",
+                      __func__, node->desc->name);
+
+               return;
+       }
+
+       if (idx >= node->desc->num_fmts) {
+               dev_err(&pipe->dip_dev->pdev->dev,
+                       "%s:%s: invalid idx(%d), must < num_fmts(%d)\n",
+                       __func__, node->desc->name, idx, node->desc->num_fmts);
+
+               idx = 0;
+       }
+
+       fmt_to_fill->type = node->desc->buf_type;
+       if (mtk_dip_buf_is_meta(node->desc->buf_type)) {
+               set_meta_fmt(pipe, &fmt_to_fill->fmt.meta,
+                            &node->desc->fmts[idx]);
+       } else {
+               fmt_to_fill->fmt.pix_mp.width = node->desc->default_width;
+               fmt_to_fill->fmt.pix_mp.height = node->desc->default_height;
+               mtk_dip_pipe_set_img_fmt(pipe, node, &fmt_to_fill->fmt.pix_mp,
+                                        &node->desc->fmts[idx]);
+       }
+}
+
+const struct mtk_dip_dev_format*
+mtk_dip_pipe_find_fmt(struct mtk_dip_pipe *pipe,
+                     struct mtk_dip_video_device *node,
+                     u32 format)
+{
+       int i;
+
+       for (i = 0; i < node->desc->num_fmts; i++) {
+               if (node->desc->fmts[i].format == format)
+                       return &node->desc->fmts[i];
+       }
+
+       return NULL;
+}
+
+static enum mdp_ycbcr_profile
+map_ycbcr_prof_mplane(struct v4l2_pix_format_mplane *pix_mp, u32 mdp_color)
+{
+       if (MDP_COLOR_IS_RGB(mdp_color))
+               return MDP_YCBCR_PROFILE_FULL_BT601;
+
+       switch (pix_mp->colorspace) {
+       case V4L2_COLORSPACE_JPEG:
+               return MDP_YCBCR_PROFILE_JPEG;
+       case V4L2_COLORSPACE_REC709:
+       case V4L2_COLORSPACE_DCI_P3:
+               if (pix_mp->quantization == V4L2_QUANTIZATION_FULL_RANGE)
+                       return MDP_YCBCR_PROFILE_FULL_BT709;
+               return MDP_YCBCR_PROFILE_BT709;
+       case V4L2_COLORSPACE_BT2020:
+               if (pix_mp->quantization == V4L2_QUANTIZATION_FULL_RANGE)
+                       return MDP_YCBCR_PROFILE_FULL_BT2020;
+               return MDP_YCBCR_PROFILE_BT2020;
+       }
+       /* V4L2_COLORSPACE_SRGB or else */
+       if (pix_mp->quantization == V4L2_QUANTIZATION_FULL_RANGE)
+               return MDP_YCBCR_PROFILE_FULL_BT601;
+
+       return MDP_YCBCR_PROFILE_BT601;
+}
+
+static inline int is_contig_mp_buffer(struct mtk_dip_dev_buffer *dev_buf)
+{
+       return !(MDP_COLOR_GET_PLANE_COUNT(dev_buf->dev_fmt->mdp_color) == 1);
+}
+
+static int fill_ipi_img_param_mp(struct mtk_dip_pipe *pipe,
+                                struct img_image_buffer *b,
+                                struct mtk_dip_dev_buffer *dev_buf,
+                                char *buf_name)
+{
+       struct v4l2_pix_format_mplane *pix_mp;
+       const struct mtk_dip_dev_format *mdp_fmt;
+       unsigned int i;
+       unsigned int total_plane_size = 0;
+
+       if (!dev_buf->dev_fmt) {
+               dev_err(&pipe->dip_dev->pdev->dev,
+                       "%s: %s's dev format not set\n",
+                       __func__, buf_name);
+               return -EINVAL;
+       }
+
+       pix_mp = &dev_buf->fmt.fmt.pix_mp;
+       mdp_fmt = dev_buf->dev_fmt;
+
+       b->usage = dev_buf->dma_port;
+       b->format.colorformat = dev_buf->dev_fmt->mdp_color;
+       b->format.width = dev_buf->fmt.fmt.pix_mp.width;
+       b->format.height = dev_buf->fmt.fmt.pix_mp.height;
+       b->format.ycbcr_prof =
+               map_ycbcr_prof_mplane(pix_mp,
+                                     dev_buf->dev_fmt->mdp_color);
+       dev_dbg(&pipe->dip_dev->pdev->dev,
+               "%s: buf(%s), IPI: w(%d),h(%d),c(0x%x)\n",
+               pipe->desc->name,
+               buf_name,
+               b->format.width,
+               b->format.height,
+               b->format.colorformat);
+
+       for (i = 0; i < pix_mp->num_planes; ++i) {
+               u32 stride = mtk_dip_pipe_get_stride(pipe, pix_mp, mdp_fmt,
+                                                    i, buf_name);
+
+               b->format.plane_fmt[i].stride = stride;
+               b->format.plane_fmt[i].size =
+                       dip_mdp_fmt_get_plane_size(mdp_fmt, stride,
+                                                  pix_mp->height, i);
+               b->iova[i] = dev_buf->isp_daddr[i];
+               dev_dbg(&pipe->dip_dev->pdev->dev,
+                       
"Non-contiguous-mp-buf:plane(%i),stride(%d),size(%d),iova(%lx)",
+                       i,
+                       b->format.plane_fmt[i].stride,
+                       b->format.plane_fmt[i].size,
+                       b->iova[i]);
+               total_plane_size += b->format.plane_fmt[i].size;
+       }
+
+       if (!is_contig_mp_buffer(dev_buf)) {
+               dev_dbg(&pipe->dip_dev->pdev->dev,
+                       
"Non-contiguous-mp-buf(%s),total-plane-size(%d),dma_port(%d)\n",
+                       buf_name,
+                       total_plane_size,
+                       b->usage);
+               return 0;
+       }
+
+       for (; i < MDP_COLOR_GET_PLANE_COUNT(b->format.colorformat); ++i) {
+               u32 stride = dip_mdp_fmt_get_stride_contig
+                               (mdp_fmt, b->format.plane_fmt[0].stride, i);
+
+               b->format.plane_fmt[i].stride = stride;
+               b->format.plane_fmt[i].size =
+                       dip_mdp_fmt_get_plane_size(mdp_fmt, stride,
+                                                  pix_mp->height, i);
+               b->iova[i] = b->iova[i - 1] + b->format.plane_fmt[i - 1].size;
+               dev_dbg(&pipe->dip_dev->pdev->dev,
+                       
"Contiguous-mp-buf:plane(%i),stride(%d),size(%d),iova(%lx)",
+                       i,
+                       b->format.plane_fmt[i].stride,
+                       b->format.plane_fmt[i].size,
+                       b->iova[i]);
+               total_plane_size += b->format.plane_fmt[i].size;
+       }
+
+       dev_dbg(&pipe->dip_dev->pdev->dev,
+               
"Contiguous-mp-buf(%s),v4l2-sizeimage(%d),total-plane-size(%d)\n",
+               buf_name,
+               pix_mp->plane_fmt[0].sizeimage,
+               total_plane_size);
+
+       return 0;
+}
+
+static int fill_input_ipi_param(struct mtk_dip_pipe *pipe,
+                               struct img_input *iin,
+                               struct mtk_dip_dev_buffer *dev_buf,
+                               char *buf_name)
+{
+       struct img_image_buffer *img = &iin->buffer;
+
+       return fill_ipi_img_param_mp(pipe, img, dev_buf,
+                                    buf_name);
+}
+
+static int fill_output_ipi_param(struct mtk_dip_pipe *pipe,
+                                struct img_output *iout,
+                                struct mtk_dip_dev_buffer *dev_buf_out,
+                                struct mtk_dip_dev_buffer *dev_buf_in,
+                                char *buf_name)
+{
+       int ret;
+       struct img_image_buffer *img = &iout->buffer;
+
+       ret = fill_ipi_img_param_mp(pipe, img, dev_buf_out,
+                                   buf_name);
+       iout->crop.left = 0;
+       iout->crop.top = 0;
+       iout->crop.width = dev_buf_in->fmt.fmt.pix_mp.width;
+       iout->crop.height = dev_buf_in->fmt.fmt.pix_mp.height;
+       iout->crop.left_subpix = 0;
+       iout->crop.top_subpix = 0;
+       iout->crop.width_subpix = 0;
+       iout->crop.height_subpix = 0;
+       iout->rotation = dev_buf_out->rotation;
+
+       dev_dbg(&pipe->dip_dev->pdev->dev,
+               "%s: buf(%s) IPI-ext:c_l(%d),c_t(%d),c_w(%d),c_h(%d)\n",
+               pipe->desc->name,
+               buf_name,
+               iout->crop.left,
+               iout->crop.top,
+               iout->crop.width,
+               iout->crop.height);
+
+       dev_dbg(&pipe->dip_dev->pdev->dev,
+               "c_ls(%d),c_ts(%d),c_ws(%d),c_hs(%d),rot(%d)\n",
+               iout->crop.left_subpix,
+               iout->crop.top_subpix,
+               iout->crop.width_subpix,
+               iout->crop.height_subpix,
+               iout->rotation);
+
+       return ret;
+}
+
+void mtk_dip_pipe_ipi_params_config(struct mtk_dip_request *req)
+{
+       struct mtk_dip_pipe *pipe = req->dip_pipe;
+       struct platform_device *pdev = pipe->dip_dev->pdev;
+       struct mtk_dip_pipe_job_info *pipe_job_info = &req->job_info;
+       int out_img_buf_idx;
+       int in_img_buf_idx;
+       struct img_ipi_frameparam *dip_param = &req->img_fparam.frameparam;
+       struct mtk_dip_dev_buffer *dev_buf_in;
+       struct mtk_dip_dev_buffer *dev_buf_out;
+       struct mtk_dip_dev_buffer *dev_buf_tuning;
+       int i = 0;
+       int mdp_ids[2] = {MTK_DIP_VIDEO_NODE_ID_MDP0_CAPTURE,
+                         MTK_DIP_VIDEO_NODE_ID_MDP1_CAPTURE};
+       char *mdp_names[2] = {"mdp0", "mdp1"};
+
+       dev_dbg(&pdev->dev,
+               "%s:%s: pipe-job id(%d)\n",
+               __func__, pipe->desc->name,
+               pipe_job_info->id);
+
+       /* Fill ipi params for DIP driver */
+       memset(dip_param, 0, sizeof(*dip_param));
+       dip_param->index = pipe_job_info->id;
+       dip_param->type = STREAM_ISP_IC;
+
+       /* Tuning buffer */
+       dev_buf_tuning =
+               pipe_job_info->buf_map[MTK_DIP_VIDEO_NODE_ID_TUNING_OUT];
+       if (dev_buf_tuning) {
+               dev_dbg(&pdev->dev,
+                       "Tuning buf queued: scp_daddr(%pad),isp_daddr(%pad)\n",
+                       &dev_buf_tuning->scp_daddr[0],
+                       &dev_buf_tuning->isp_daddr[0]);
+               dip_param->tuning_data.pa =
+                       (uint32_t)dev_buf_tuning->scp_daddr[0];
+               dip_param->tuning_data.present = true;
+               dip_param->tuning_data.iova =
+                       (uint32_t)dev_buf_tuning->isp_daddr[0];
+       } else {
+               dev_dbg(&pdev->dev,
+                       "No enqueued tuning buffer: 
scp_daddr(%llx),present(%llx),isp_daddr(%llx\n",
+                       dip_param->tuning_data.pa,
+                       dip_param->tuning_data.present,
+                       dip_param->tuning_data.iova);
+       }
+
+       in_img_buf_idx = 0;
+
+       /* Raw-in buffer */
+       dev_buf_in =
+               pipe_job_info->buf_map[MTK_DIP_VIDEO_NODE_ID_RAW_OUT];
+       if (dev_buf_in) {
+               struct img_input *iin = &dip_param->inputs[in_img_buf_idx];
+
+               fill_input_ipi_param(pipe, iin, dev_buf_in, "RAW");
+               in_img_buf_idx++;
+       }
+
+       /* Array of MDP buffers */
+       out_img_buf_idx = 0;
+
+       for (i = 0; i < ARRAY_SIZE(mdp_ids); i++) {
+               dev_buf_out =
+                       pipe_job_info->buf_map[mdp_ids[i]];
+               if (dev_buf_out) {
+                       struct img_output *iout =
+                               &dip_param->outputs[out_img_buf_idx];
+
+                       fill_output_ipi_param(pipe, iout, dev_buf_out,
+                                             dev_buf_in, mdp_names[i]);
+                       out_img_buf_idx++;
+               }
+       }
+
+       dip_param->num_outputs = out_img_buf_idx;
+       dip_param->num_inputs = in_img_buf_idx;
+}
+
+void mtk_dip_pipe_try_enqueue(struct mtk_dip_pipe *pipe)
+{
+       struct mtk_dip_pipe_job_info *job_info;
+       struct mtk_dip_pipe_job_info *tmp_job_info;
+       struct list_head enqueue_job_list;
+
+       INIT_LIST_HEAD(&enqueue_job_list);
+       if (!pipe->streaming)
+               return;
+
+       spin_lock(&pipe->job_lock);
+       list_for_each_entry_safe(job_info, tmp_job_info,
+                                &pipe->pipe_job_pending_list,
+                                list) {
+               list_del(&job_info->list);
+               pipe->num_pending_jobs--;
+               dev_dbg(&pipe->dip_dev->pdev->dev,
+                       "%s:%s: current num of pending jobs(%d)\n",
+                       __func__, pipe->desc->name,
+                       pipe->num_pending_jobs);
+               list_add_tail(&job_info->list,
+                             &enqueue_job_list);
+       }
+       spin_unlock(&pipe->job_lock);
+
+       list_for_each_entry_safe(job_info, tmp_job_info,
+                                &enqueue_job_list,
+                                list) {
+               struct mtk_dip_request *req =
+                               mtk_dip_pipe_job_info_to_req(job_info);
+
+               list_del(&job_info->list);
+
+               spin_lock(&pipe->job_lock);
+               list_add_tail(&job_info->list,
+                             &pipe->pipe_job_running_list);
+                       pipe->num_jobs++;
+                       dev_dbg(&pipe->dip_dev->pdev->dev,
+                               "%s:%s: current num of running jobs(%d)\n",
+                               __func__, pipe->desc->name,
+                               pipe->num_jobs);
+               spin_unlock(&pipe->job_lock);
+
+               mtk_dip_hw_enqueue(&pipe->dip_dev->dip_hw, req);
+       }
+}
diff --git a/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-dev.h 
b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-dev.h
new file mode 100644
index 000000000000..e3372c291f9a
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-dev.h
@@ -0,0 +1,337 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ *
+ * Author: Frederic Chen <frederic.c...@mediatek.com>
+ *
+ */
+
+#ifndef _MTK_DIP_DEV_H_
+#define _MTK_DIP_DEV_H_
+
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-device.h>
+#include <linux/videodev2.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-v4l2.h>
+
+#include "mtk_dip-hw.h"
+
+#define MTK_DIP_PIPE_ID_PREVIEW                                0
+#define MTK_DIP_PIPE_ID_CAPTURE                                1
+#define MTK_DIP_PIPE_ID_REPROCESS                      2
+#define MTK_DIP_PIPE_ID_TOTAL_NUM                      3
+
+#define MTK_DIP_VIDEO_NODE_ID_RAW_OUT                  0
+#define MTK_DIP_VIDEO_NODE_ID_TUNING_OUT               1
+#define MTK_DIP_VIDEO_NODE_ID_OUT_TOTAL_NUM            2
+#define MTK_DIP_VIDEO_NODE_ID_MDP0_CAPTURE             2
+#define MTK_DIP_VIDEO_NODE_ID_MDP1_CAPTURE             3
+#define MTK_DIP_VIDEO_NODE_ID_CAPTURE_TOTAL_NUM                2
+#define MTK_DIP_VIDEO_NODE_ID_TOTAL_NUM \
+       (MTK_DIP_VIDEO_NODE_ID_OUT_TOTAL_NUM + \
+       MTK_DIP_VIDEO_NODE_ID_CAPTURE_TOTAL_NUM)
+
+#define MTK_DIP_VIDEO_NODE_ID_NO_MASTER                        -1
+
+#define MTK_DIP_OUTPUT_MIN_WIDTH               2U
+#define MTK_DIP_OUTPUT_MIN_HEIGHT              2U
+#define MTK_DIP_OUTPUT_MAX_WIDTH               5376U
+#define MTK_DIP_OUTPUT_MAX_HEIGHT              4032U
+#define MTK_DIP_CAPTURE_MIN_WIDTH              2U
+#define MTK_DIP_CAPTURE_MIN_HEIGHT             2U
+#define MTK_DIP_CAPTURE_MAX_WIDTH              5376U
+#define MTK_DIP_CAPTURE_MAX_HEIGHT             4032U
+
+#define MTK_DIP_DEV_DIP_MEDIA_MODEL_NAME       "MTK-ISP-DIP-V4L2"
+#define MTK_DIP_DEV_DIP_PREVIEW_NAME \
+       MTK_DIP_DEV_DIP_MEDIA_MODEL_NAME
+#define MTK_DIP_DEV_DIP_CAPTURE_NAME           "MTK-ISP-DIP-CAP-V4L2"
+#define MTK_DIP_DEV_DIP_REPROCESS_NAME         "MTK-ISP-DIP-REP-V4L2"
+
+#define MTK_DIP_DEV_META_BUF_DEFAULT_SIZE      (1024 * 128)
+#define MTK_DIP_DEV_META_BUF_POOL_MAX_SIZE     (1024 * 1024 * 6)
+
+#define V4L2_CID_MTK_DIP_MAX                   100
+
+enum mtk_dip_pixel_mode {
+       mtk_dip_pixel_mode_default = 0,
+       mtk_dip_pixel_mode_1,
+       mtk_dip_pixel_mode_2,
+       mtk_dip_pixel_mode_4,
+       mtk_dip_pixel_mode_num,
+};
+
+struct mtk_dip_dev_format {
+       u32 format;
+       u32 mdp_color;
+       u32 colorspace;
+       u8 depth[VIDEO_MAX_PLANES];
+       u8 row_depth[VIDEO_MAX_PLANES];
+       u8 num_planes;
+       u8 walign;
+       u8 halign;
+       u8 salign;
+       u32 flags;
+       u32 buffer_size;
+};
+
+struct mtk_dip_pipe_job_info {
+       int id;
+       struct mtk_dip_dev_buffer*
+               buf_map[MTK_DIP_VIDEO_NODE_ID_TOTAL_NUM];
+       struct list_head list;
+};
+
+struct mtk_dip_dev_buffer {
+       struct vb2_v4l2_buffer vbb;
+       struct v4l2_format fmt;
+       const struct mtk_dip_dev_format *dev_fmt;
+       int pipe_job_id;
+       dma_addr_t isp_daddr[VB2_MAX_PLANES];
+       dma_addr_t scp_daddr[VB2_MAX_PLANES];
+       unsigned int dma_port;
+       int rotation;
+       struct list_head list;
+};
+
+struct mtk_dip_pipe_desc {
+       char *name;
+       int master;
+       int id;
+       const struct mtk_dip_video_device_desc *output_queue_descs;
+       int total_output_queues;
+       const struct mtk_dip_video_device_desc *capture_queue_descs;
+       int total_capture_queues;
+};
+
+struct mtk_dip_video_device_desc {
+       int id;
+       char *name;
+       u32 buf_type;
+       u32 cap;
+       int smem_alloc;
+       const struct mtk_dip_dev_format *fmts;
+       int num_fmts;
+       char *description;
+       int default_width;
+       int default_height;
+       unsigned int dma_port;
+       const struct v4l2_frmsizeenum *frmsizeenum;
+       const struct v4l2_ioctl_ops *ops;
+       u32 flags;
+       int default_fmt_idx;
+};
+
+struct mtk_dip_dev_queue {
+       struct vb2_queue vbq;
+       /* Serializes vb2 queue and video device operations */
+       struct mutex lock;
+       const struct mtk_dip_dev_format *dev_fmt;
+       int rotation;
+};
+
+struct mtk_dip_video_device {
+       struct video_device vdev;
+       struct mtk_dip_dev_queue dev_q;
+       struct v4l2_format vdev_fmt;
+       struct media_pad vdev_pad;
+       struct v4l2_mbus_framefmt pad_fmt;
+       struct v4l2_ctrl_handler ctrl_handler;
+       u32 flags;
+       const struct mtk_dip_video_device_desc *desc;
+       atomic_t sequence;
+       struct list_head buf_list;
+       /* protect the in-device buffer list */
+       spinlock_t buf_list_lock;
+};
+
+struct mtk_dip_pipe {
+       struct mtk_dip_dev *dip_dev;
+       struct mtk_dip_video_device nodes[MTK_DIP_VIDEO_NODE_ID_TOTAL_NUM];
+       int num_nodes;
+       atomic_t cnt_nodes_not_streaming;
+       int streaming;
+       struct media_pad *subdev_pads;
+       struct media_pipeline pipeline;
+       struct v4l2_subdev subdev;
+       struct v4l2_subdev_fh *fh;
+       atomic_t pipe_job_sequence;
+       struct list_head pipe_job_pending_list;
+       int num_pending_jobs;
+       struct list_head pipe_job_running_list;
+       int num_jobs;
+       /* Serializes pipe's stream on/off and buffers enqueue operations */
+       struct mutex lock;
+       spinlock_t job_lock; /* protect the pipe job list */
+       const struct mtk_dip_pipe_desc *desc;
+};
+
+struct mtk_dip_dev {
+       struct platform_device *pdev;
+       struct media_device mdev;
+       struct v4l2_device v4l2_dev;
+       struct mtk_dip_pipe dip_pipe[MTK_DIP_PIPE_ID_TOTAL_NUM];
+       struct mtk_dip_hw dip_hw;
+};
+
+struct mtk_dip_request {
+       struct media_request req;
+       struct mtk_dip_pipe *dip_pipe;
+       struct mtk_dip_pipe_job_info job_info;
+       struct img_frameparam img_fparam;
+       struct work_struct fw_work;
+       struct work_struct mdp_work;
+       /* It is used only in timeout handling flow */
+       struct work_struct mdpcb_work;
+       struct mtk_dip_hw_subframe *working_buf;
+       atomic_t buf_count;
+};
+
+int mtk_dip_dev_media_register(struct device *dev,
+                              struct media_device *media_dev,
+                              const char *model);
+
+void mtk_dip_dev_v4l2_release(struct mtk_dip_dev *dip_dev);
+
+int mtk_dip_dev_v4l2_register(struct device *dev,
+                             struct media_device *media_dev,
+                             struct v4l2_device *v4l2_dev);
+
+int mtk_dip_pipe_v4l2_register(struct mtk_dip_pipe *pipe,
+                              struct media_device *media_dev,
+                              struct v4l2_device *v4l2_dev);
+
+void mtk_dip_pipe_v4l2_unregister(struct mtk_dip_pipe *pipe);
+
+int mtk_dip_pipe_queue_buffers(struct media_request *req, int initial);
+
+int mtk_dip_pipe_init(struct mtk_dip_pipe *pipe,
+                     struct mtk_dip_dev *dip_dev,
+                     const struct mtk_dip_pipe_desc *setting,
+                     struct media_device *media_dev,
+                     struct v4l2_device *v4l2_dev);
+
+void mtk_dip_pipe_ipi_params_config(struct mtk_dip_request *req);
+
+int mtk_dip_pipe_release(struct mtk_dip_pipe *pipe);
+
+struct mtk_dip_pipe_job_info *
+mtk_dip_pipe_get_running_job_info(struct mtk_dip_pipe *pipe,
+                                 int pipe_job_id);
+
+int mtk_dip_pipe_next_job_id(struct mtk_dip_pipe *pipe);
+
+void mtk_dip_pipe_debug_job(struct mtk_dip_pipe *pipe,
+                           struct mtk_dip_pipe_job_info *pipe_job_info);
+
+int mtk_dip_pipe_job_finish(struct mtk_dip_pipe *pipe,
+                           unsigned int pipe_job_info_id,
+                           enum vb2_buffer_state state);
+
+const struct mtk_dip_dev_format *
+mtk_dip_pipe_find_fmt(struct mtk_dip_pipe *pipe,
+                     struct mtk_dip_video_device *node,
+                     u32 format);
+
+void mtk_dip_pipe_set_img_fmt(struct mtk_dip_pipe *pipe,
+                             struct mtk_dip_video_device *node,
+                             struct v4l2_pix_format_mplane *mfmt_to_fill,
+                             const struct mtk_dip_dev_format *dev_fmt);
+
+void mtk_dip_pipe_load_default_fmt(struct mtk_dip_pipe *pipe,
+                                  struct mtk_dip_video_device *node,
+                                  struct v4l2_format *fmt_to_fill);
+
+void mtk_dip_pipe_try_enqueue(struct mtk_dip_pipe *pipe);
+
+void mtk_dip_hw_enqueue(struct mtk_dip_hw *dip_hw, struct mtk_dip_request 
*req);
+
+int mtk_dip_hw_streamoff(struct mtk_dip_pipe *pipe);
+
+int mtk_dip_hw_streamon(struct mtk_dip_pipe *pipe);
+
+static inline struct mtk_dip_pipe*
+mtk_dip_dev_get_pipe(struct mtk_dip_dev *dip_dev, unsigned int pipe_id)
+{
+       if (pipe_id < 0 && pipe_id >= MTK_DIP_PIPE_ID_TOTAL_NUM)
+               return NULL;
+
+       return &dip_dev->dip_pipe[pipe_id];
+}
+
+static inline struct mtk_dip_video_device*
+mtk_dip_file_to_node(struct file *file)
+{
+       return container_of(video_devdata(file),
+                           struct mtk_dip_video_device, vdev);
+}
+
+static inline struct mtk_dip_pipe*
+mtk_dip_subdev_to_pipe(struct v4l2_subdev *sd)
+{
+       return container_of(sd, struct mtk_dip_pipe, subdev);
+}
+
+static inline struct mtk_dip_dev*
+mtk_dip_mdev_to_dev(struct media_device *mdev)
+{
+       return container_of(mdev, struct mtk_dip_dev, mdev);
+}
+
+static inline struct mtk_dip_video_device*
+mtk_dip_vbq_to_node(struct vb2_queue *vq)
+{
+       return container_of(vq, struct mtk_dip_video_device, dev_q.vbq);
+}
+
+static inline struct mtk_dip_dev_buffer*
+mtk_dip_vb2_buf_to_dev_buf(struct vb2_buffer *vb)
+{
+       return container_of(vb, struct mtk_dip_dev_buffer, vbb.vb2_buf);
+}
+
+static inline struct mtk_dip_dev *mtk_dip_hw_to_dev(struct mtk_dip_hw *dip_hw)
+{
+       return container_of(dip_hw, struct mtk_dip_dev, dip_hw);
+}
+
+static inline struct mtk_dip_request*
+mtk_dip_pipe_job_info_to_req(struct mtk_dip_pipe_job_info *job_info)
+{
+       return container_of(job_info, struct mtk_dip_request, job_info);
+}
+
+static inline struct mtk_dip_request*
+mtk_dip_hw_fw_work_to_req(struct work_struct *fw_work)
+{
+       return container_of(fw_work, struct mtk_dip_request, fw_work);
+}
+
+static inline struct mtk_dip_request*
+mtk_dip_hw_mdp_work_to_req(struct work_struct *mdp_work)
+{
+       return container_of(mdp_work, struct mtk_dip_request, mdp_work);
+}
+
+static inline struct mtk_dip_request *
+mtk_dip_hw_mdpcb_work_to_req(struct work_struct *mdpcb_work)
+{
+       return container_of(mdpcb_work, struct mtk_dip_request, mdpcb_work);
+}
+
+static inline int mtk_dip_buf_is_meta(u32 type)
+{
+       return type == V4L2_BUF_TYPE_META_CAPTURE ||
+               type == V4L2_BUF_TYPE_META_OUTPUT;
+}
+
+static inline int mtk_dip_pipe_get_pipe_from_job_id(int pipe_job_id)
+{
+       return (pipe_job_id >> 16) & 0x0000FFFF;
+}
+
+#endif /* _MTK_DIP_DEV_H_ */
diff --git a/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-hw.h 
b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-hw.h
new file mode 100644
index 000000000000..2dd4dae4336c
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-hw.h
@@ -0,0 +1,155 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ *
+ * Author: Frederic Chen <frederic.c...@mediatek.com>
+ *         Holmes Chiou <holmes.ch...@mediatek.com>
+ *
+ */
+
+#ifndef _MTK_DIP_HW_H_
+#define _MTK_DIP_HW_H_
+
+#include <linux/clk.h>
+#include "mtk-img-ipi.h"
+
+#define DIP_COMPOSER_THREAD_TIMEOUT    16U
+#define DIP_COMPOSING_MAX_NUM          3
+#define DIP_MAX_ERR_COUNT              188U
+#define DIP_FLUSHING_WQ_TIMEOUT                (16U * DIP_MAX_ERR_COUNT)
+
+#define DIP_FRM_SZ                     (76 * 1024)
+#define DIP_SUB_FRM_SZ                 (16 * 1024)
+#define DIP_TUNING_SZ                  (32 * 1024)
+#define DIP_COMP_SZ                    (24 * 1024)
+#define DIP_FRAMEPARAM_SZ              (4 * 1024)
+
+#define DIP_TUNING_OFFSET              DIP_SUB_FRM_SZ
+#define DIP_COMP_OFFSET                        (DIP_TUNING_OFFSET + 
DIP_TUNING_SZ)
+#define DIP_FRAMEPARAM_OFFSET          (DIP_COMP_OFFSET + DIP_COMP_SZ)
+#define DIP_SUB_FRM_DATA_NUM           32
+#define DIP_SCP_WORKINGBUF_OFFSET      (5 * 1024 * 1024)
+#define DIP_V4l2_META_BUF_OFFSET       (DIP_SCP_WORKINGBUF_OFFSET + \
+                                        DIP_SUB_FRM_DATA_NUM * DIP_FRM_SZ)
+
+#define MTK_DIP_CLK_NUM 2
+
+enum STREAM_TYPE_ENUM {
+       STREAM_UNKNOWN,
+       STREAM_BITBLT,
+       STREAM_GPU_BITBLT,
+       STREAM_DUAL_BITBLT,
+       STREAM_2ND_BITBLT,
+       STREAM_ISP_IC,
+       STREAM_ISP_VR,
+       STREAM_ISP_ZSD,
+       STREAM_ISP_IP,
+       STREAM_ISP_VSS,
+       STREAM_ISP_ZSD_SLOW,
+       STREAM_WPE,
+       STREAM_WPE2,
+};
+
+enum mtk_dip_hw_user_state {
+       DIP_STATE_INIT  = 0,
+       DIP_STATE_OPENED,
+       DIP_STATE_STREAMON,
+       DIP_STATE_STREAMOFF
+};
+
+struct mtk_dip_hw_working_buf {
+       dma_addr_t scp_daddr;
+       void *vaddr;
+       dma_addr_t isp_daddr;
+};
+
+struct mtk_dip_hw_subframe {
+       struct mtk_dip_hw_working_buf buffer;
+       int size;
+       struct mtk_dip_hw_working_buf config_data;
+       struct mtk_dip_hw_working_buf tuning_buf;
+       struct mtk_dip_hw_working_buf frameparam;
+       struct list_head list_entry;
+};
+
+enum frame_state {
+       FRAME_STATE_INIT = 0,
+       FRAME_STATE_COMPOSING,
+       FRAME_STATE_RUNNING,
+       FRAME_STATE_DONE,
+       FRAME_STATE_STREAMOFF,
+       FRAME_STATE_ERROR,
+       FRAME_STATE_HW_TIMEOUT
+};
+
+struct mtk_dip_hw_working_buf_list {
+       struct list_head list;
+       u32 cnt;
+       spinlock_t lock; /* protect the list and cnt */
+
+};
+
+struct mtk_dip_hw {
+       struct clk_bulk_data clks[MTK_DIP_CLK_NUM];
+       struct workqueue_struct *composer_wq;
+       struct workqueue_struct *mdp_wq;
+       wait_queue_head_t working_buf_wq;
+       wait_queue_head_t flushing_wq;
+       wait_queue_head_t flushing_hw_wq;
+       atomic_t num_composing; /* increase after ipi */
+       /* increase after calling MDP driver */
+       atomic_t num_running;
+       /*MDP/GCE callback workqueue */
+       struct workqueue_struct *mdpcb_workqueue;
+       /* for MDP driver  */
+       struct platform_device *mdp_pdev;
+       /* for SCP driver  */
+       struct platform_device *scp_pdev;
+       struct rproc *rproc_handle;
+       struct mtk_dip_hw_working_buf_list dip_freebufferlist;
+       struct mtk_dip_hw_working_buf_list dip_usedbufferlist;
+       dma_addr_t working_buf_mem_scp_daddr;
+       void *working_buf_mem_vaddr;
+       dma_addr_t working_buf_mem_isp_daddr;
+       int working_buf_mem_size;
+       struct mtk_dip_hw_subframe working_buf[DIP_SUB_FRM_DATA_NUM];
+       /* increase after enqueue */
+       atomic_t dip_enque_cnt;
+       /* increase after stream on, decrease when stream off */
+       atomic_t dip_stream_cnt;
+       /* To serialize request opertion to DIP co-procrosser and hadrware */
+       struct mutex hw_op_lock;
+       /* To restrict the max number of request be send to SCP */
+       struct semaphore sem;
+};
+
+static inline void
+mtk_dip_wbuf_to_ipi_img_sw_addr(struct img_sw_addr *ipi_addr,
+                               struct mtk_dip_hw_working_buf *wbuf)
+{
+       ipi_addr->va = (u64)wbuf->vaddr;
+       ipi_addr->pa = (u32)wbuf->scp_daddr;
+}
+
+static inline void
+mtk_dip_wbuf_to_ipi_img_addr(struct img_addr *ipi_addr,
+                            struct mtk_dip_hw_working_buf *wbuf)
+{
+       ipi_addr->va = (u64)wbuf->vaddr;
+       ipi_addr->pa = (u32)wbuf->scp_daddr;
+       ipi_addr->iova = (u32)wbuf->isp_daddr;
+}
+
+static inline void
+mtk_dip_wbuf_to_ipi_tuning_addr(struct tuning_addr *ipi_addr,
+                               struct mtk_dip_hw_working_buf *wbuf)
+{
+       ipi_addr->pa = (u32)wbuf->scp_daddr;
+       ipi_addr->iova = (u32)wbuf->isp_daddr;
+}
+
+int mtk_dip_hw_working_buf_pool_init(struct mtk_dip_hw *dip_hw);
+void mtk_dip_hw_working_buf_pool_release(struct mtk_dip_hw *dip_hw);
+
+#endif /* _MTK_DIP_HW_H_ */
+
diff --git a/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-sys.c 
b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-sys.c
new file mode 100644
index 000000000000..603be116b03f
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-sys.c
@@ -0,0 +1,794 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ *
+ * Author: Frederic Chen <frederic.c...@mediatek.com>
+ *         Holmes Chiou <holmes.ch...@mediatek.com>
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/dma-iommu.h>
+#include <linux/freezer.h>
+#include <linux/platform_data/mtk_scp.h>
+#include <linux/pm_runtime.h>
+#include <linux/remoteproc.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include "mtk-mdp3-cmdq.h"
+#include "mtk_dip-dev.h"
+#include "mtk_dip-hw.h"
+
+int mtk_dip_hw_working_buf_pool_init(struct mtk_dip_hw *dip_hw)
+{
+       int i;
+       struct mtk_dip_dev *dip_dev = mtk_dip_hw_to_dev(dip_hw);
+       const int working_buf_size = round_up(DIP_FRM_SZ, PAGE_SIZE);
+       phys_addr_t working_buf_paddr;
+
+       INIT_LIST_HEAD(&dip_hw->dip_freebufferlist.list);
+       spin_lock_init(&dip_hw->dip_freebufferlist.lock);
+       dip_hw->dip_freebufferlist.cnt = 0;
+
+       INIT_LIST_HEAD(&dip_hw->dip_usedbufferlist.list);
+       spin_lock_init(&dip_hw->dip_usedbufferlist.lock);
+       dip_hw->dip_usedbufferlist.cnt = 0;
+
+       init_waitqueue_head(&dip_hw->working_buf_wq);
+
+       dip_hw->working_buf_mem_size = DIP_SUB_FRM_DATA_NUM * working_buf_size +
+               DIP_SCP_WORKINGBUF_OFFSET;
+       dip_hw->working_buf_mem_vaddr =
+               dma_alloc_coherent(&dip_hw->scp_pdev->dev,
+                                  dip_hw->working_buf_mem_size,
+                                  &dip_hw->working_buf_mem_scp_daddr,
+                                  GFP_KERNEL);
+       if (!dip_hw->working_buf_mem_vaddr) {
+               dev_err(&dip_dev->pdev->dev,
+                       "memory alloc size %ld failed\n",
+                       dip_hw->working_buf_mem_size);
+               return -ENOMEM;
+       }
+
+       /*
+        * We got the incorrect physical address mapped when
+        * using dma_map_single() so I used dma_map_page_attrs()
+        * directly to workaround here.
+        *
+        * When I use dma_map_single() to map the address, the
+        * physical address retrieved back with iommu_get_domain_for_dev()
+        * and iommu_iova_to_phys() was not equal to the
+        * SCP dma address (it should be the same as the physical address
+        * since we don't have iommu), and was shifted by 0x4000000.
+        */
+       working_buf_paddr = dip_hw->working_buf_mem_scp_daddr;
+       dip_hw->working_buf_mem_isp_daddr =
+               dma_map_page_attrs(&dip_dev->pdev->dev,
+                                  phys_to_page(working_buf_paddr),
+                                  0, dip_hw->working_buf_mem_size,
+                                  DMA_BIDIRECTIONAL,
+                                  DMA_ATTR_SKIP_CPU_SYNC);
+       if (dma_mapping_error(&dip_dev->pdev->dev,
+                             dip_hw->working_buf_mem_isp_daddr)) {
+               dev_err(&dip_dev->pdev->dev,
+                       "failed to map buffer: s_daddr(%pad)\n",
+                       &dip_hw->working_buf_mem_scp_daddr);
+               dma_free_coherent(&dip_hw->scp_pdev->dev,
+                                 dip_hw->working_buf_mem_size,
+                                 dip_hw->working_buf_mem_vaddr,
+                                 dip_hw->working_buf_mem_scp_daddr);
+
+               return -ENOMEM;
+       }
+
+       dev_dbg(&dip_dev->pdev->dev,
+               "%s: working_buf_mem: vaddr(%p), scp_daddr(%pad)\n",
+               __func__,
+               dip_hw->working_buf_mem_vaddr,
+               &dip_hw->working_buf_mem_scp_daddr);
+
+       for (i = 0; i < DIP_SUB_FRM_DATA_NUM; i++) {
+               struct mtk_dip_hw_subframe *buf = &dip_hw->working_buf[i];
+
+               /*
+                * Total: 0 ~ 72 KB
+                * SubFrame: 0 ~ 16 KB
+                */
+               buf->buffer.scp_daddr = dip_hw->working_buf_mem_scp_daddr +
+                       DIP_SCP_WORKINGBUF_OFFSET + i * working_buf_size;
+               buf->buffer.vaddr = dip_hw->working_buf_mem_vaddr +
+                       DIP_SCP_WORKINGBUF_OFFSET + i * working_buf_size;
+               buf->buffer.isp_daddr = dip_hw->working_buf_mem_isp_daddr +
+                       DIP_SCP_WORKINGBUF_OFFSET + i * working_buf_size;
+               buf->size = working_buf_size;
+
+               dev_dbg(&dip_dev->pdev->dev,
+                       "%s: buf(%d), scp_daddr(%pad), isp_daddr(%pad)\n",
+                       __func__, i, &buf->buffer.scp_daddr,
+                       &buf->buffer.isp_daddr);
+
+               /* Tuning: 16 ~ 48 KB */
+               buf->tuning_buf.scp_daddr =
+                       buf->buffer.scp_daddr + DIP_TUNING_OFFSET;
+               buf->tuning_buf.vaddr =
+                       buf->buffer.vaddr + DIP_TUNING_OFFSET;
+               buf->tuning_buf.isp_daddr =
+                       buf->buffer.isp_daddr + DIP_TUNING_OFFSET;
+
+               dev_dbg(&dip_dev->pdev->dev,
+                       "%s: tuning_buf(%d), scp_daddr(%pad), 
isp_daddr(%pad)\n",
+                       __func__, i, &buf->tuning_buf.scp_daddr,
+                       &buf->tuning_buf.isp_daddr);
+
+               /* Config_data: 48 ~ 72 KB */
+               buf->config_data.scp_daddr =
+                       buf->buffer.scp_daddr + DIP_COMP_OFFSET;
+               buf->config_data.vaddr = buf->buffer.vaddr + DIP_COMP_OFFSET;
+
+               dev_dbg(&dip_dev->pdev->dev,
+                       "%s: config_data(%d), scp_daddr(%pad), vaddr(%p)\n",
+                       __func__, i, &buf->config_data.scp_daddr,
+                       buf->config_data.vaddr);
+
+               /* Frame parameters: 72 ~ 76 KB */
+               buf->frameparam.scp_daddr =
+                       buf->buffer.scp_daddr + DIP_FRAMEPARAM_OFFSET;
+               buf->frameparam.vaddr =
+                       buf->buffer.vaddr + DIP_FRAMEPARAM_OFFSET;
+
+               dev_dbg(&dip_dev->pdev->dev,
+                       "%s: frameparam(%d), scp_daddr(%pad), vaddr(%p)\n",
+                       __func__, i, &buf->frameparam.scp_daddr,
+                       buf->frameparam.vaddr);
+
+               list_add_tail(&buf->list_entry,
+                             &dip_hw->dip_freebufferlist.list);
+               dip_hw->dip_freebufferlist.cnt++;
+       }
+
+       return 0;
+}
+
+void mtk_dip_hw_working_buf_pool_release(struct mtk_dip_hw *dip_hw)
+{
+       /* All the buffer should be in the freebufferlist when release */
+       struct mtk_dip_dev *dip_dev = mtk_dip_hw_to_dev(dip_hw);
+       u32 i;
+
+       if (dip_hw->dip_usedbufferlist.cnt)
+               dev_warn(&dip_dev->pdev->dev,
+                        "%s: dip_usedbufferlist is not empty (%d/%d)\n",
+                       __func__, dip_hw->dip_usedbufferlist.cnt, i);
+
+       dma_unmap_page_attrs(&dip_dev->pdev->dev,
+                            dip_hw->working_buf_mem_isp_daddr,
+                            dip_hw->working_buf_mem_size, DMA_BIDIRECTIONAL,
+                            DMA_ATTR_SKIP_CPU_SYNC);
+
+       dma_free_coherent(&dip_hw->scp_pdev->dev, dip_hw->working_buf_mem_size,
+                         dip_hw->working_buf_mem_vaddr,
+                         dip_hw->working_buf_mem_scp_daddr);
+}
+
+static void mtk_dip_hw_working_buf_free(struct mtk_dip_hw *dip_hw,
+                                       struct mtk_dip_hw_subframe *working_buf)
+{
+       struct mtk_dip_dev *dip_dev = mtk_dip_hw_to_dev(dip_hw);
+
+       if (!working_buf)
+               return;
+
+       spin_lock(&dip_hw->dip_usedbufferlist.lock);
+       list_del(&working_buf->list_entry);
+                       dip_hw->dip_usedbufferlist.cnt--;
+       dev_dbg(&dip_dev->pdev->dev,
+               "%s: Free used buffer(%pad)\n",
+               __func__, &working_buf->buffer.scp_daddr);
+       spin_unlock(&dip_hw->dip_usedbufferlist.lock);
+
+       spin_lock(&dip_hw->dip_freebufferlist.lock);
+       list_add_tail(&working_buf->list_entry,
+                     &dip_hw->dip_freebufferlist.list);
+       dip_hw->dip_freebufferlist.cnt++;
+       spin_unlock(&dip_hw->dip_freebufferlist.lock);
+}
+
+static struct mtk_dip_hw_subframe*
+mtk_dip_hw_working_buf_alloc(struct mtk_dip_hw *dip_hw)
+{
+       struct mtk_dip_hw_subframe *working_buf;
+
+       spin_lock(&dip_hw->dip_freebufferlist.lock);
+       if (list_empty(&dip_hw->dip_freebufferlist.list)) {
+               spin_unlock(&dip_hw->dip_freebufferlist.lock);
+               return NULL;
+       }
+
+       working_buf = list_first_entry(&dip_hw->dip_freebufferlist.list,
+                                      struct mtk_dip_hw_subframe, list_entry);
+       list_del(&working_buf->list_entry);
+       dip_hw->dip_freebufferlist.cnt--;
+       spin_unlock(&dip_hw->dip_freebufferlist.lock);
+
+       spin_lock(&dip_hw->dip_usedbufferlist.lock);
+       list_add_tail(&working_buf->list_entry,
+                     &dip_hw->dip_usedbufferlist.list);
+       dip_hw->dip_usedbufferlist.cnt++;
+       spin_unlock(&dip_hw->dip_usedbufferlist.lock);
+
+       return working_buf;
+}
+
+static void mtk_dip_notify(struct mtk_dip_request *req)
+{
+       struct mtk_dip_hw *dip_hw = &req->dip_pipe->dip_dev->dip_hw;
+       struct mtk_dip_dev *dip_dev = req->dip_pipe->dip_dev;
+       struct mtk_dip_pipe *pipe = req->dip_pipe;
+       struct img_ipi_frameparam *iparam = &req->img_fparam.frameparam;
+       enum vb2_buffer_state vbf_state;
+       int ret;
+
+       if (iparam->state != FRAME_STATE_HW_TIMEOUT)
+               vbf_state = VB2_BUF_STATE_DONE;
+       else
+               vbf_state = VB2_BUF_STATE_ERROR;
+
+       ret = mtk_dip_pipe_job_finish(pipe, iparam->index, vbf_state);
+       if (ret)
+               dev_dbg(&dip_dev->pdev->dev, "%s: finish CB failed(%d)\n",
+                       __func__, ret);
+
+       pm_runtime_put_autosuspend(&pipe->dip_dev->pdev->dev);
+
+       mtk_dip_hw_working_buf_free(dip_hw, req->working_buf);
+       req->working_buf = NULL;
+       wake_up(&dip_hw->working_buf_wq);
+       wake_up(&dip_hw->flushing_wq);
+
+       dev_dbg(&dip_dev->pdev->dev,
+               "%s:%s: job id(%d), frame_no(%d) finished\n",
+               __func__, pipe->desc->name, iparam->index, iparam->frame_no);
+}
+
+static void mdp_cb_timeout_worker(struct work_struct *work)
+{
+       struct mtk_dip_request *req = mtk_dip_hw_mdpcb_work_to_req(work);
+       struct img_ipi_param ipi_param;
+
+       dev_dbg(&req->dip_pipe->dip_dev->pdev->dev,
+               "%s: send frame no(%d) HW timeout dbg IPI\n",
+               __func__, req->img_fparam.frameparam.frame_no);
+
+       ipi_param.usage = IMG_IPI_DEBUG;
+       scp_ipi_send(req->dip_pipe->dip_dev->dip_hw.scp_pdev, SCP_IPI_DIP,
+                    (void *)&ipi_param, sizeof(ipi_param), 0);
+       mtk_dip_notify(req);
+}
+
+/* Maybe in IRQ context of cmdq */
+static void dip_mdp_cb_func(struct cmdq_cb_data data)
+{
+       struct mtk_dip_request *req;
+       struct mtk_dip_dev *dip_dev;
+
+       if (!data.data) {
+               pr_err("%s: data->data is NULL\n",
+                      __func__);
+               return;
+       }
+
+       req = data.data;
+       dip_dev = req->dip_pipe->dip_dev;
+
+       dev_dbg(&dip_dev->pdev->dev, "%s: req(%p), idx(%d), no(%d), s(%d), 
n_in(%d), n_out(%d)\n",
+               __func__,
+               req,
+               req->img_fparam.frameparam.index,
+               req->img_fparam.frameparam.frame_no,
+               req->img_fparam.frameparam.state,
+               req->img_fparam.frameparam.num_inputs,
+               req->img_fparam.frameparam.num_outputs);
+
+       if (data.sta != CMDQ_CB_NORMAL) {
+               dev_err(&dip_dev->pdev->dev, "%s: frame no(%d) HW timeout\n",
+                       __func__, req->img_fparam.frameparam.frame_no);
+               req->img_fparam.frameparam.state = FRAME_STATE_HW_TIMEOUT;
+               INIT_WORK(&req->mdpcb_work, mdp_cb_timeout_worker);
+               queue_work(req->dip_pipe->dip_dev->dip_hw.mdpcb_workqueue,
+                          &req->mdpcb_work);
+       } else {
+               mtk_dip_notify(req);
+       }
+}
+
+static void dip_runner_func(struct work_struct *work)
+{
+       struct mtk_dip_request *req = mtk_dip_hw_mdp_work_to_req(work);
+       struct mtk_dip_dev *dip_dev = req->dip_pipe->dip_dev;
+       struct img_config *config_data =
+               (struct img_config *)req->img_fparam.frameparam.config_data.va;
+       u32 num;
+
+       /*
+        * Call MDP/GCE API to do HW excecution
+        * Pass the framejob to MDP driver
+        */
+       req->img_fparam.frameparam.state = FRAME_STATE_COMPOSING;
+       num = atomic_inc_return(&dip_dev->dip_hw.num_running);
+       dev_dbg(&dip_dev->pdev->dev,
+               "%s,MDP running num(%d)\n", __func__, num);
+       mdp_cmdq_sendtask(dip_dev->dip_hw.mdp_pdev,
+                         config_data,
+                         &req->img_fparam.frameparam,
+                         NULL,
+                         false,
+                         dip_mdp_cb_func,
+                         req);
+}
+
+static void dip_scp_handler(void *data, unsigned int len, void *priv)
+{
+       struct mtk_dip_pipe_job_info *pipe_job_info;
+       struct mtk_dip_pipe *pipe;
+       int pipe_id;
+       struct mtk_dip_request *req;
+       struct img_ipi_frameparam *frameparam;
+       struct mtk_dip_dev *dip_dev = (struct mtk_dip_dev *)priv;
+       struct mtk_dip_hw *dip_hw = &dip_dev->dip_hw;
+       struct img_ipi_param *ipi_param;
+       u32 num;
+       int ret;
+
+       if (WARN_ONCE(!data, "%s: failed due to NULL data\n", __func__))
+               return;
+
+       ipi_param = (struct img_ipi_param *)data;
+
+       if (ipi_param->usage != IMG_IPI_FRAME) {
+               dev_dbg(&dip_dev->pdev->dev,
+                       "%s: recevied SCP_IPI_DIP ACK, ipi_param.usage(%d)\n",
+                       __func__, ipi_param->usage);
+               return;
+       }
+
+       frameparam = (struct img_ipi_frameparam *)ipi_param->frm_param.va;
+       pipe_id = mtk_dip_pipe_get_pipe_from_job_id(frameparam->index);
+       pipe = mtk_dip_dev_get_pipe(dip_dev, pipe_id);
+
+       if (!pipe) {
+               dev_warn(&dip_dev->pdev->dev,
+                        "%s: get invalid img_ipi_frameparam index(%d) from 
firmware\n",
+                        __func__, frameparam->frame_no);
+               return;
+       }
+
+       pipe_job_info = mtk_dip_pipe_get_running_job_info(pipe,
+                                                         frameparam->index);
+
+       if (WARN_ONCE(!pipe_job_info, "%s: frame_no(%d) is lost\n",
+                     __func__, frameparam->frame_no))
+               return;
+
+       req = mtk_dip_pipe_job_info_to_req(pipe_job_info);
+       memcpy(&req->img_fparam.frameparam, frameparam,
+              sizeof(req->img_fparam.frameparam));
+       num = atomic_dec_return(&dip_hw->num_composing);
+       down(&dip_hw->sem);
+
+       dev_dbg(&dip_dev->pdev->dev,
+               "%s: frame_no(%d) is back, composing num(%d)\n",
+               __func__, frameparam->frame_no, num);
+
+       wake_up(&dip_dev->dip_hw.flushing_hw_wq);
+
+       INIT_WORK(&req->mdp_work, dip_runner_func);
+       ret = queue_work(dip_hw->mdp_wq, &req->mdp_work);
+       if (!ret) {
+               dev_dbg(&dip_dev->pdev->dev,
+                       "frame_no(%d) queue_work failed to mdp_wq: %d\n",
+                       frameparam->frame_no, ret);
+       }
+}
+
+static bool
+mtk_dip_hw_is_working_buf_allocated(struct mtk_dip_request *req)
+{
+       req->working_buf =
+               mtk_dip_hw_working_buf_alloc(&req->dip_pipe->dip_dev->dip_hw);
+
+       if (!req->working_buf) {
+               dev_dbg(&req->dip_pipe->dip_dev->pdev->dev,
+                       "%s:%s:req(%p): no free working buffer available\n",
+                       __func__, req->dip_pipe->desc->name, req);
+               return false;
+       }
+
+       return true;
+}
+
+static void dip_submit_worker(struct work_struct *work)
+{
+       struct mtk_dip_request *req = mtk_dip_hw_fw_work_to_req(work);
+       struct mtk_dip_hw *dip_hw = &req->dip_pipe->dip_dev->dip_hw;
+       struct mtk_dip_dev *dip_dev = req->dip_pipe->dip_dev;
+       struct img_ipi_param ipi_param;
+       struct mtk_dip_hw_subframe *buf;
+       int ret;
+
+       dev_dbg(&dip_dev->pdev->dev,
+               "%s: to send frame_no(%d),num(%d)\n",
+               __func__, req->img_fparam.frameparam.frame_no,
+               atomic_read(&dip_hw->num_composing));
+
+       wait_event_freezable(dip_hw->working_buf_wq,
+                            mtk_dip_hw_is_working_buf_allocated(req));
+       up(&dip_hw->sem);
+
+       dev_dbg(&dip_dev->pdev->dev,
+               "%s: wakeup frame_no(%d),num(%d)\n",
+               __func__, req->img_fparam.frameparam.frame_no,
+               atomic_read(&dip_hw->num_composing));
+
+       buf = req->working_buf;
+       mtk_dip_wbuf_to_ipi_img_addr(&req->img_fparam.frameparam.subfrm_data,
+                                    &buf->buffer);
+       memset(buf->buffer.vaddr, 0, DIP_SUB_FRM_SZ);
+       mtk_dip_wbuf_to_ipi_img_sw_addr(&req->img_fparam.frameparam.config_data,
+                                       &buf->config_data);
+       memset(buf->config_data.vaddr, 0, DIP_COMP_SZ);
+
+       if (!req->img_fparam.frameparam.tuning_data.present) {
+               /*
+                * When user enqueued without tuning buffer,
+                * it would use driver internal buffer.
+                */
+               dev_dbg(&dip_dev->pdev->dev,
+                       "%s: frame_no(%d) has no tuning_data\n",
+                       __func__, req->img_fparam.frameparam.frame_no);
+
+               mtk_dip_wbuf_to_ipi_tuning_addr
+                               (&req->img_fparam.frameparam.tuning_data,
+                                &buf->tuning_buf);
+               memset(buf->tuning_buf.vaddr, 0, DIP_TUNING_SZ);
+       }
+
+       req->img_fparam.frameparam.state = FRAME_STATE_COMPOSING;
+       mtk_dip_wbuf_to_ipi_img_sw_addr(&req->img_fparam.frameparam.self_data,
+                                       &buf->frameparam);
+       memcpy(buf->frameparam.vaddr, &req->img_fparam.frameparam,
+              sizeof(req->img_fparam.frameparam));
+
+       ipi_param.usage = IMG_IPI_FRAME;
+       mtk_dip_wbuf_to_ipi_img_sw_addr(&ipi_param.frm_param,
+                                       &buf->frameparam);
+
+       mutex_lock(&dip_hw->hw_op_lock);
+       atomic_inc(&dip_hw->num_composing);
+       ret = scp_ipi_send(dip_hw->scp_pdev, SCP_IPI_DIP, &ipi_param,
+                          sizeof(ipi_param), 0);
+       if (ret)
+               dev_dbg(&dip_dev->pdev->dev, "%s: send SCP_IPI_DIP_FRAME failed 
%d\n",
+                       __func__, ret);
+       mutex_unlock(&dip_hw->hw_op_lock);
+
+       dev_dbg(&dip_dev->pdev->dev,
+               "%s: frame_no(%d),idx(0x%x), composing num(%d)\n",
+               __func__, req->img_fparam.frameparam.frame_no,
+               req->img_fparam.frameparam.index,
+               atomic_read(&dip_hw->num_composing));
+}
+
+static int mtk_dip_hw_res_init(struct mtk_dip_hw *dip_hw)
+{
+       struct mtk_dip_dev *dip_dev = mtk_dip_hw_to_dev(dip_hw);
+       int ret;
+
+       dip_hw->mdp_pdev = mdp_get_plat_device(dip_dev->pdev);
+       if (!dip_hw->mdp_pdev) {
+               dev_dbg(&dip_dev->pdev->dev,
+                       "%s: failed to get MDP device\n",
+                       __func__);
+               return -EINVAL;
+       }
+
+       dip_hw->mdpcb_workqueue =
+               alloc_ordered_workqueue("%s",
+                                       __WQ_LEGACY | WQ_MEM_RECLAIM |
+                                       WQ_FREEZABLE,
+                                       "mdp_callback");
+       if (!dip_hw->mdpcb_workqueue) {
+               dev_err(&dip_dev->pdev->dev,
+                       "%s: unable to alloc mdpcb workqueue\n", __func__);
+               ret = -ENOMEM;
+               goto destroy_mdpcb_wq;
+       }
+
+       dip_hw->composer_wq =
+               alloc_ordered_workqueue("%s",
+                                       __WQ_LEGACY | WQ_MEM_RECLAIM |
+                                       WQ_FREEZABLE,
+                                       "dip_composer");
+       if (!dip_hw->composer_wq) {
+               dev_err(&dip_dev->pdev->dev,
+                       "%s: unable to alloc composer workqueue\n", __func__);
+               ret = -ENOMEM;
+               goto destroy_dip_composer_wq;
+       }
+
+       dip_hw->mdp_wq =
+               alloc_ordered_workqueue("%s",
+                                       __WQ_LEGACY | WQ_MEM_RECLAIM |
+                                       WQ_FREEZABLE,
+                                       "dip_runner");
+       if (!dip_hw->mdp_wq) {
+               dev_err(&dip_dev->pdev->dev,
+                       "%s: unable to alloc dip_runner\n", __func__);
+               ret = -ENOMEM;
+               goto destroy_dip_runner_wq;
+       }
+
+       init_waitqueue_head(&dip_hw->flushing_wq);
+       init_waitqueue_head(&dip_hw->flushing_hw_wq);
+
+       return 0;
+
+destroy_dip_runner_wq:
+       destroy_workqueue(dip_hw->mdp_wq);
+
+destroy_dip_composer_wq:
+       destroy_workqueue(dip_hw->composer_wq);
+
+destroy_mdpcb_wq:
+       destroy_workqueue(dip_hw->mdpcb_workqueue);
+
+       return ret;
+}
+
+static int mtk_dip_hw_res_release(struct mtk_dip_dev *dip_dev)
+{
+       flush_workqueue(dip_dev->dip_hw.mdp_wq);
+       destroy_workqueue(dip_dev->dip_hw.mdp_wq);
+       dip_dev->dip_hw.mdp_wq = NULL;
+
+       flush_workqueue(dip_dev->dip_hw.mdpcb_workqueue);
+       destroy_workqueue(dip_dev->dip_hw.mdpcb_workqueue);
+       dip_dev->dip_hw.mdpcb_workqueue = NULL;
+
+       flush_workqueue(dip_dev->dip_hw.composer_wq);
+       destroy_workqueue(dip_dev->dip_hw.composer_wq);
+       dip_dev->dip_hw.composer_wq = NULL;
+
+       atomic_set(&dip_dev->dip_hw.num_composing, 0);
+       atomic_set(&dip_dev->dip_hw.num_running, 0);
+       atomic_set(&dip_dev->dip_hw.dip_enque_cnt, 0);
+
+       return 0;
+}
+
+static bool mtk_dip_pipe_running_job_list_empty(struct mtk_dip_pipe *pipe,
+                                               int *num)
+{
+       spin_lock(&pipe->job_lock);
+       *num = pipe->num_jobs;
+       spin_unlock(&pipe->job_lock);
+
+       return !(*num);
+}
+
+static int mtk_dip_hw_flush_pipe_jobs(struct mtk_dip_pipe *pipe)
+{
+       int num;
+       int ret = wait_event_freezable_timeout
+               (pipe->dip_dev->dip_hw.flushing_wq,
+                mtk_dip_pipe_running_job_list_empty(pipe, &num),
+                msecs_to_jiffies(DIP_FLUSHING_WQ_TIMEOUT));
+
+       if (!ret && num) {
+               dev_err(&pipe->dip_dev->pdev->dev,
+                       "%s: flushing is aborted, num(%d)\n",
+                       __func__, num);
+               return -EINVAL;
+       }
+
+       dev_dbg(&pipe->dip_dev->pdev->dev,
+               "%s: wakeup num(%d)\n", __func__, num);
+       return 0;
+}
+
+static int mtk_dip_hw_connect(struct mtk_dip_dev *dip_dev)
+{
+       int ret;
+       struct img_ipi_param ipi_param;
+
+       pm_runtime_get_sync(&dip_dev->pdev->dev);
+
+       scp_ipi_register(dip_dev->dip_hw.scp_pdev, SCP_IPI_DIP, dip_scp_handler,
+                        dip_dev);
+       memset(&ipi_param, 0, sizeof(ipi_param));
+       ipi_param.usage = IMG_IPI_INIT;
+       ret = scp_ipi_send(dip_dev->dip_hw.scp_pdev, SCP_IPI_DIP, &ipi_param,
+                          sizeof(ipi_param), 200);
+       if (ret) {
+               dev_dbg(&dip_dev->pdev->dev, "%s: send SCP_IPI_DIP_FRAME failed 
%d\n",
+                       __func__, ret);
+               return -EBUSY;
+       }
+
+       pm_runtime_put_autosuspend(&dip_dev->pdev->dev);
+
+       ret = mtk_dip_hw_res_init(&dip_dev->dip_hw);
+       if (ret) {
+               dev_err(&dip_dev->pdev->dev,
+                       "%s: mtk_dip_hw_res_init failed(%d)\n", __func__, ret);
+
+               return -EBUSY;
+       }
+
+       return 0;
+}
+
+static void mtk_dip_hw_disconnect(struct mtk_dip_dev *dip_dev)
+{
+       int ret;
+
+       ret = mtk_dip_hw_res_release(dip_dev);
+       if (ret)
+               dev_err(&dip_dev->pdev->dev,
+                       "%s: mtk_dip_hw_res_release failed ret(%d)\n",
+                       __func__, ret);
+}
+
+int mtk_dip_hw_streamon(struct mtk_dip_pipe *pipe)
+{
+       struct mtk_dip_dev *dip_dev = pipe->dip_dev;
+       int count, ret;
+
+       mutex_lock(&dip_dev->dip_hw.hw_op_lock);
+       count = atomic_read(&dip_dev->dip_hw.dip_stream_cnt);
+       if (!count) {
+               ret = mtk_dip_hw_connect(pipe->dip_dev);
+               if (ret) {
+                       dev_err(&pipe->dip_dev->pdev->dev,
+                               "%s:%s: pipe(%d) connect to dip_hw failed\n",
+                               __func__, pipe->desc->name, pipe->desc->id);
+
+                       mutex_unlock(&dip_dev->dip_hw.hw_op_lock);
+
+                       return ret;
+               }
+
+               count = atomic_inc_return(&dip_dev->dip_hw.dip_stream_cnt);
+       }
+       mutex_unlock(&dip_dev->dip_hw.hw_op_lock);
+
+       mutex_lock(&pipe->lock);
+       pipe->streaming = 1;
+       dev_dbg(&pipe->dip_dev->pdev->dev,
+               "%s:%s: started stream, id(%d), stream cnt(%d)\n",
+               __func__, pipe->desc->name, pipe->desc->id, count);
+       mtk_dip_pipe_try_enqueue(pipe);
+       mutex_unlock(&pipe->lock);
+
+       return 0;
+}
+
+int mtk_dip_hw_streamoff(struct mtk_dip_pipe *pipe)
+{
+       struct mtk_dip_dev *dip_dev = pipe->dip_dev;
+       struct mtk_dip_pipe_job_info *job_info;
+       struct mtk_dip_pipe_job_info *tmp_job_info;
+       s32 count;
+       int ret;
+
+       mutex_lock(&pipe->lock);
+       dev_dbg(&dip_dev->pdev->dev,
+               "%s:%s: streamoff, removing all running jobs\n",
+               __func__, pipe->desc->name);
+
+       spin_lock(&pipe->job_lock);
+       list_for_each_entry_safe(job_info, tmp_job_info,
+                                &pipe->pipe_job_running_list, list) {
+               struct mtk_dip_request *req =
+                       mtk_dip_pipe_job_info_to_req(job_info);
+               int i;
+
+               for (i = 0; i < MTK_DIP_VIDEO_NODE_ID_TOTAL_NUM; i++) {
+                       struct mtk_dip_dev_buffer *dev_buf =
+                               job_info->buf_map[i];
+                       struct mtk_dip_video_device *node;
+
+                       if (!dev_buf)
+                               continue;
+
+                       node = mtk_dip_vbq_to_node
+                               (dev_buf->vbb.vb2_buf.vb2_queue);
+                       vb2_buffer_done(&dev_buf->vbb.vb2_buf,
+                                       VB2_BUF_STATE_ERROR);
+                       dev_dbg(&dip_dev->pdev->dev,
+                               "%s:%s: return buf, idx(%d), state(%d)\n",
+                               pipe->desc->name, node->desc->name,
+                               dev_buf->vbb.vb2_buf.index,
+                               VB2_BUF_STATE_ERROR);
+               }
+
+               pipe->num_jobs--;
+               list_del(&job_info->list);
+
+               dev_dbg(&pipe->dip_dev->pdev->dev,
+                       "%s:%s: not run job, id(%d), no(%d), state(%d), job 
cnt(%d)\n",
+                       __func__, pipe->desc->name, job_info->id,
+                       req->img_fparam.frameparam.frame_no,
+                       VB2_BUF_STATE_ERROR, pipe->num_jobs);
+       }
+       spin_unlock(&pipe->job_lock);
+
+       ret = mtk_dip_hw_flush_pipe_jobs(pipe);
+       if (WARN_ON(ret != 0)) {
+               dev_err(&dip_dev->pdev->dev,
+                       "%s:%s: mtk_dip_hw_flush_pipe_jobs, ret(%d)\n",
+                       __func__, pipe->desc->name, ret);
+       }
+
+       pipe->streaming = 0;
+       mutex_unlock(&pipe->lock);
+
+       mutex_lock(&dip_dev->dip_hw.hw_op_lock);
+       count = atomic_read(&dip_dev->dip_hw.dip_stream_cnt);
+       if (count > 0 && atomic_dec_and_test(&dip_dev->dip_hw.dip_stream_cnt)) {
+               mtk_dip_hw_disconnect(dip_dev);
+
+               dev_dbg(&dip_dev->pdev->dev, "%s: dip_hw disconnected, stream 
cnt(%d)\n",
+                       __func__, atomic_read(&dip_dev->dip_hw.dip_stream_cnt));
+       }
+       dev_dbg(&pipe->dip_dev->pdev->dev,
+               "%s:%s: stopped stream id(%d), stream cnt(%d)\n",
+               __func__, pipe->desc->name, pipe->desc->id, count);
+
+       mutex_unlock(&dip_dev->dip_hw.hw_op_lock);
+
+       return 0;
+}
+
+void mtk_dip_hw_enqueue(struct mtk_dip_hw *dip_hw,
+                       struct mtk_dip_request *req)
+{
+       struct img_ipi_frameparam *frameparams = &req->img_fparam.frameparam;
+       struct mtk_dip_dev *dip_dev = mtk_dip_hw_to_dev(dip_hw);
+       int ret;
+       struct mtk_dip_dev_buffer **map = req->job_info.buf_map;
+
+       /*
+        * Workaround to pass v4l2 compliance test when it pass only one buffer
+        * to DIP. The check is already done in request_validate().
+        */
+       if (!map[MTK_DIP_VIDEO_NODE_ID_RAW_OUT] ||
+           (!map[MTK_DIP_VIDEO_NODE_ID_MDP0_CAPTURE] &&
+            !map[MTK_DIP_VIDEO_NODE_ID_MDP1_CAPTURE])){
+               dev_dbg(&dip_dev->pdev->dev,
+                       "won't trigger hw job: raw(%p), mdp0(%p), mdp1(%p)\n",
+                       map[MTK_DIP_VIDEO_NODE_ID_RAW_OUT],
+                       map[MTK_DIP_VIDEO_NODE_ID_MDP0_CAPTURE],
+                       map[MTK_DIP_VIDEO_NODE_ID_MDP1_CAPTURE]);
+
+               mtk_dip_pipe_job_finish(req->dip_pipe, req->job_info.id,
+                                       VB2_BUF_STATE_DONE);
+               return;
+       }
+
+       mtk_dip_pipe_ipi_params_config(req);
+       frameparams->state = FRAME_STATE_INIT;
+       frameparams->frame_no = atomic_inc_return(&dip_hw->dip_enque_cnt);
+
+       dev_dbg(&dip_dev->pdev->dev,
+               "%s: hw job id(%d), frame_no(%d) into worklist\n",
+               __func__, frameparams->index, frameparams->frame_no);
+
+       INIT_WORK(&req->fw_work, dip_submit_worker);
+       pm_runtime_get_sync(&dip_dev->pdev->dev);
+       ret = queue_work(dip_hw->composer_wq, &req->fw_work);
+       if (!ret)
+               dev_dbg(&dip_dev->pdev->dev,
+                       "frame_no(%d) queue_work failed, already on a queue\n",
+                       frameparams->frame_no, ret);
+}
diff --git a/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-v4l2.c 
b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-v4l2.c
new file mode 100644
index 000000000000..7b454952566f
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-v4l2.c
@@ -0,0 +1,1786 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ *
+ * Author: Frederic Chen <frederic.c...@mediatek.com>
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_data/mtk_scp.h>
+#include <linux/pm_runtime.h>
+#include <linux/remoteproc.h>
+#include <linux/videodev2.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-event.h>
+#include "mtk_dip-dev.h"
+#include "mtk_dip-hw.h"
+#include "mtk-mdp3-regs.h"
+
+static int mtk_dip_subdev_open(struct v4l2_subdev *sd,
+                              struct v4l2_subdev_fh *fh)
+{
+       struct mtk_dip_pipe *pipe = mtk_dip_subdev_to_pipe(sd);
+
+       dev_dbg(&pipe->dip_dev->pdev->dev, "%s:%s: pipe(%d) open\n",
+               __func__, pipe->desc->name, pipe->desc->id);
+
+       pipe->fh = fh;
+
+       return 0;
+}
+
+static int mtk_dip_subdev_close(struct v4l2_subdev *sd,
+                               struct v4l2_subdev_fh *fh)
+{
+       struct mtk_dip_pipe *pipe = mtk_dip_subdev_to_pipe(sd);
+
+       dev_dbg(&pipe->dip_dev->pdev->dev, "%s:%s: pipe(%d) close\n",
+               __func__, pipe->desc->name, pipe->desc->id);
+
+       return 0;
+}
+
+static int mtk_dip_subdev_s_stream(struct v4l2_subdev *sd,
+                                  int enable)
+{
+       struct mtk_dip_pipe *pipe = mtk_dip_subdev_to_pipe(sd);
+       int ret;
+
+       if (enable) {
+               ret = mtk_dip_hw_streamon(pipe);
+               if (ret) {
+                       dev_err(&pipe->dip_dev->pdev->dev,
+                               "%s:%s: pipe(%d) streamon failed\n",
+                               __func__, pipe->desc->name, pipe->desc->id);
+               }
+
+               return ret;
+       }
+
+       ret = mtk_dip_hw_streamoff(pipe);
+       if (ret) {
+               dev_err(&pipe->dip_dev->pdev->dev,
+                       "%s:%s: pipe(%d) streamon off with errors\n",
+                       __func__, pipe->desc->name, pipe->desc->id);
+       }
+
+       return ret;
+}
+
+static int mtk_dip_link_setup(struct media_entity *entity,
+                             const struct media_pad *local,
+                             const struct media_pad *remote,
+                             u32 flags)
+{
+       struct mtk_dip_pipe *pipe =
+               container_of(entity, struct mtk_dip_pipe, subdev.entity);
+       u32 pad = local->index;
+       int count;
+
+       WARN_ON(entity->obj_type != MEDIA_ENTITY_TYPE_V4L2_SUBDEV);
+       WARN_ON(pad >= pipe->num_nodes);
+
+       mutex_lock(&pipe->lock);
+       if (!vb2_start_streaming_called(&pipe->nodes[pad].dev_q.vbq)) {
+               int enable = flags & MEDIA_LNK_FL_ENABLED;
+               int node_enabled =
+                       pipe->nodes[pad].flags & MEDIA_LNK_FL_ENABLED;
+
+               if (enable && !node_enabled)
+                       atomic_inc_return(&pipe->cnt_nodes_not_streaming);
+
+               if (!enable && node_enabled)
+                       atomic_dec_return(&pipe->cnt_nodes_not_streaming);
+       }
+       pipe->nodes[pad].flags &= ~(MEDIA_LNK_FL_ENABLED);
+       pipe->nodes[pad].flags |= MEDIA_LNK_FL_ENABLED & flags;
+       count = atomic_read(&pipe->cnt_nodes_not_streaming);
+       mutex_unlock(&pipe->lock);
+
+       dev_dbg(&pipe->dip_dev->pdev->dev,
+               "%s: link setup, flags(0x%x), (%s)%d -->(%s)%d, 
cnt_nodes_ns(%d)\n",
+               pipe->desc->name, flags, local->entity->name, local->index,
+               remote->entity->name, remote->index, count);
+
+       return 0;
+}
+
+static int mtk_dip_vb2_buf_prepare(struct vb2_buffer *vb)
+{
+       struct mtk_dip_pipe *pipe = vb2_get_drv_priv(vb->vb2_queue);
+       struct mtk_dip_video_device *node = mtk_dip_vbq_to_node(vb->vb2_queue);
+       struct device *dev = &pipe->dip_dev->pdev->dev;
+       const struct v4l2_format *fmt = &node->vdev_fmt;
+       unsigned int size;
+       int i;
+
+       if (vb->vb2_queue->type == V4L2_BUF_TYPE_META_CAPTURE ||
+           vb->vb2_queue->type == V4L2_BUF_TYPE_META_OUTPUT){
+               if (vb->planes[0].length < fmt->fmt.meta.buffersize) {
+                       dev_dbg(dev,
+                               "%s:%s:%s: size error(user:%d, required:%d)\n",
+                               __func__, pipe->desc->name, node->desc->name,
+                               vb->planes[0].length, fmt->fmt.meta.buffersize);
+                       return -EINVAL;
+               } else {
+                       return 0;
+               }
+       }
+
+       for (i = 0; i < vb->num_planes; i++) {
+               size = fmt->fmt.pix_mp.plane_fmt[i].sizeimage;
+               if (vb->planes[i].length < size) {
+                       dev_dbg(dev,
+                               "%s:%s:%s: size error(user:%d, max:%d)\n",
+                               __func__, pipe->desc->name, node->desc->name,
+                               vb->planes[i].length, size);
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static int mtk_dip_vb2_buf_out_validate(struct vb2_buffer *vb)
+{
+       struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
+
+       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 int mtk_dip_vb2_queue_buf_init(struct vb2_buffer *vb)
+{
+       struct vb2_v4l2_buffer *b = to_vb2_v4l2_buffer(vb);
+       struct mtk_dip_dev_buffer *dev_buf = mtk_dip_vb2_buf_to_dev_buf(vb);
+       struct mtk_dip_pipe *pipe = vb2_get_drv_priv(vb->vb2_queue);
+       struct mtk_dip_video_device *node = mtk_dip_vbq_to_node(vb->vb2_queue);
+       int i;
+
+       for (i = 0; i < vb->num_planes; i++) {
+               if (node->desc->smem_alloc) {
+                       void *cpu_addr;
+                       phys_addr_t buf_paddr;
+
+                       dev_buf->scp_daddr[i] =
+                               vb2_dma_contig_plane_dma_addr(vb, i);
+                       cpu_addr = vb2_plane_vaddr(vb, i);
+                       if (!cpu_addr) {
+                               dev_err(&pipe->dip_dev->pdev->dev,
+                                       "%s:%s: vb2_plane_vaddr NULL: 
s_daddr(%pad)\n",
+                                       pipe->desc->name, node->desc->name,
+                                       &dev_buf->scp_daddr[i]);
+                               return -EINVAL;
+                       }
+
+                       /*
+                        * We got the incorrect physical address mapped when
+                        * using dma_map_single() so I used dma_map_page_attrs()
+                        * directly to workaround here. Please see the detail in
+                        * mtk_dip-sys.c
+                        */
+                       buf_paddr = dev_buf->scp_daddr[i];
+                       dev_buf->isp_daddr[i] =
+                               dma_map_page_attrs(&pipe->dip_dev->pdev->dev,
+                                                  phys_to_page(buf_paddr),
+                                                  0, vb->planes[i].length,
+                                                  DMA_BIDIRECTIONAL,
+                                                  DMA_ATTR_SKIP_CPU_SYNC);
+                       if (dma_mapping_error(&pipe->dip_dev->pdev->dev,
+                                             dev_buf->isp_daddr[i])) {
+                               dev_err(&pipe->dip_dev->pdev->dev,
+                                       "%s:%s: failed to map buffer: 
s_daddr(%pad)\n",
+                                       pipe->desc->name, node->desc->name,
+                                       &dev_buf->scp_daddr[i]);
+                               return -EINVAL;
+                       }
+               } else {
+                       dev_buf->scp_daddr[i] = 0;
+                       dev_buf->isp_daddr[i] =
+                               vb2_dma_contig_plane_dma_addr(vb, i);
+               }
+
+               dev_dbg(&pipe->dip_dev->pdev->dev,
+                       "%s:%s: buf type(%d), idx(%d), mem(%d), p(%d) 
i_daddr(%pad), s_daddr(%pad)\n",
+                       pipe->desc->name, node->desc->name, b->vb2_buf.type,
+                       b->vb2_buf.index, b->vb2_buf.memory, i,
+                       &dev_buf->isp_daddr[i], &dev_buf->scp_daddr[i]);
+       }
+
+       return 0;
+}
+
+static void mtk_dip_vb2_queue_buf_cleanup(struct vb2_buffer *vb)
+{
+       struct mtk_dip_dev_buffer *dev_buf = mtk_dip_vb2_buf_to_dev_buf(vb);
+       struct mtk_dip_pipe *pipe = vb2_get_drv_priv(vb->vb2_queue);
+       struct mtk_dip_video_device *node = mtk_dip_vbq_to_node(vb->vb2_queue);
+       int i;
+
+       if (!node->desc->smem_alloc)
+               return;
+
+       for (i = 0; i < vb->num_planes; i++) {
+               if (node->desc->smem_alloc)
+                       dma_unmap_page_attrs(&pipe->dip_dev->pdev->dev,
+                                            dev_buf->isp_daddr[i],
+                                            vb->planes[i].length,
+                                            DMA_BIDIRECTIONAL,
+                                            DMA_ATTR_SKIP_CPU_SYNC);
+       }
+}
+
+static void mtk_dip_vb2_buf_queue(struct vb2_buffer *vb)
+{
+       struct vb2_v4l2_buffer *b = to_vb2_v4l2_buffer(vb);
+       struct mtk_dip_dev_buffer *dev_buf = mtk_dip_vb2_buf_to_dev_buf(vb);
+       struct mtk_dip_request *req = (struct mtk_dip_request *)vb->request;
+       struct mtk_dip_video_device *node =
+               mtk_dip_vbq_to_node(vb->vb2_queue);
+       int buf_count;
+
+       dev_buf->fmt = node->vdev_fmt;
+       dev_buf->dev_fmt = node->dev_q.dev_fmt;
+       dev_buf->dma_port = node->desc->dma_port;
+       dev_buf->rotation = node->dev_q.rotation;
+
+       dev_dbg(&req->dip_pipe->dip_dev->pdev->dev,
+               "%s:%s: buf type(%d), idx(%d), mem(%d), i_daddr(%pad), 
s_daddr(%pad)\n",
+               req->dip_pipe->desc->name, node->desc->name, b->vb2_buf.type,
+               b->vb2_buf.index, b->vb2_buf.memory, &dev_buf->isp_daddr,
+               &dev_buf->scp_daddr);
+
+       spin_lock(&node->buf_list_lock);
+       list_add_tail(&dev_buf->list, &node->buf_list);
+       spin_unlock(&node->buf_list_lock);
+
+       buf_count = atomic_dec_return(&req->buf_count);
+       if (!buf_count) {
+               mutex_lock(&req->dip_pipe->lock);
+               mtk_dip_pipe_try_enqueue(req->dip_pipe);
+               mutex_unlock(&req->dip_pipe->lock);
+       }
+
+       dev_dbg(&req->dip_pipe->dip_dev->pdev->dev,
+               "%s:%s:%s:%s:buf_count(%d)\n",
+               __func__, req->dip_pipe->desc->name, node->desc->name,
+               req->req.debug_str, buf_count);
+}
+
+static int mtk_dip_vb2_queue_setup(struct vb2_queue *vq,
+                                  unsigned int *num_buffers,
+                                  unsigned int *num_planes,
+                                  unsigned int sizes[],
+                                  struct device *alloc_devs[])
+{
+       struct mtk_dip_pipe *pipe = vb2_get_drv_priv(vq);
+       struct mtk_dip_video_device *node = mtk_dip_vbq_to_node(vq);
+       const struct v4l2_format *fmt = &node->vdev_fmt;
+       unsigned int size;
+       int i;
+
+       vq->dma_attrs |= DMA_ATTR_NON_CONSISTENT;
+
+       if (!*num_planes) {
+               *num_planes = 1;
+               sizes[0] = fmt->fmt.pix_mp.plane_fmt[0].sizeimage;
+       }
+
+       if (node->desc->id == MTK_DIP_VIDEO_NODE_ID_TUNING_OUT) {
+               int max_meta_bufs_per_pipe;
+
+               size = fmt->fmt.meta.buffersize;
+               max_meta_bufs_per_pipe =
+                       MTK_DIP_DEV_META_BUF_POOL_MAX_SIZE /
+                       round_up(size, PAGE_SIZE) / MTK_DIP_PIPE_ID_TOTAL_NUM;
+               *num_buffers = clamp_val(*num_buffers, 1,
+                                        max_meta_bufs_per_pipe);
+               sizes[0] = size;
+
+               dev_dbg(&pipe->dip_dev->pdev->dev,
+                       "%s:%s:%s: n_p(%d), n_b(%d), s[%d](%u)\n",
+                       __func__, pipe->desc->name, node->desc->name,
+                       *num_planes, *num_buffers, 0, sizes[0]);
+
+       } else {
+               for (i = 0; i < *num_planes; i++) {
+                       if (sizes[i] < fmt->fmt.pix_mp.plane_fmt[i].sizeimage) {
+                               dev_dbg(&pipe->dip_dev->pdev->dev,
+                                       "%s:%s:%s: invalid buf: %u < %u\n",
+                                       __func__, pipe->desc->name,
+                                       node->desc->name, sizes[0], size);
+                                       return -EINVAL;
+                       }
+                       sizes[i] = fmt->fmt.pix_mp.plane_fmt[i].sizeimage;
+                       dev_dbg(&pipe->dip_dev->pdev->dev,
+                               "%s:%s:%s: n_p(%d), n_b(%d), s[%d](%u)\n",
+                               __func__, pipe->desc->name, node->desc->name,
+                               *num_planes, *num_buffers, i, sizes[i]);
+               }
+       }
+
+       return 0;
+}
+
+static void mtk_dip_return_all_buffers(struct mtk_dip_pipe *pipe,
+                                      struct mtk_dip_video_device *node,
+                                      enum vb2_buffer_state state)
+{
+       struct mtk_dip_dev_buffer *b, *b0;
+
+       spin_lock(&node->buf_list_lock);
+       list_for_each_entry_safe(b, b0, &node->buf_list, list) {
+               list_del(&b->list);
+               if (b->vbb.vb2_buf.state == VB2_BUF_STATE_ACTIVE)
+                       vb2_buffer_done(&b->vbb.vb2_buf, state);
+       }
+       spin_unlock(&node->buf_list_lock);
+}
+
+static int mtk_dip_vb2_start_streaming(struct vb2_queue *vq, unsigned int 
count)
+{
+       struct mtk_dip_pipe *pipe = vb2_get_drv_priv(vq);
+       struct mtk_dip_video_device *node = mtk_dip_vbq_to_node(vq);
+       int cnt_node_ns;
+       int ret;
+
+       if (!(node->flags & MEDIA_LNK_FL_ENABLED)) {
+               dev_err(&pipe->dip_dev->pdev->dev,
+                       "%s:%s: stream on failed, node is not enabled\n",
+                       pipe->desc->name, node->desc->name);
+
+               ret = -ENOLINK;
+               goto fail_return_bufs;
+       }
+
+       mutex_lock(&pipe->lock);
+       cnt_node_ns = atomic_dec_return(&pipe->cnt_nodes_not_streaming);
+       mutex_unlock(&pipe->lock);
+
+       dev_dbg(&pipe->dip_dev->pdev->dev,
+               "%s:%s:%s cnt_nodes_not_streaming(%d)\n",
+               __func__, pipe->desc->name, node->desc->name, cnt_node_ns);
+
+       if (cnt_node_ns)
+               return 0;
+
+       ret = media_pipeline_start(&node->vdev.entity, &pipe->pipeline);
+       if (ret < 0) {
+               dev_err(&pipe->dip_dev->pdev->dev,
+                       "%s:%s: media_pipeline_start failed(%d)\n",
+                       pipe->desc->name, node->desc->name, ret);
+
+               goto fail_return_bufs;
+       }
+
+       /* Start streaming of the whole pipeline */
+       ret = v4l2_subdev_call(&pipe->subdev, video, s_stream, 1);
+       if (ret < 0) {
+               dev_err(&pipe->dip_dev->pdev->dev,
+                       "%s:%s: sub dev s_stream(1) failed(%d)\n",
+                       pipe->desc->name, node->desc->name, ret);
+
+               goto fail_stop_pipeline;
+       }
+
+       return 0;
+
+fail_stop_pipeline:
+       media_pipeline_stop(&node->vdev.entity);
+
+fail_return_bufs:
+       mtk_dip_return_all_buffers(pipe, node, VB2_BUF_STATE_QUEUED);
+
+       return ret;
+}
+
+static void mtk_dip_vb2_stop_streaming(struct vb2_queue *vq)
+{
+       struct mtk_dip_pipe *pipe = vb2_get_drv_priv(vq);
+       struct mtk_dip_video_device *node = mtk_dip_vbq_to_node(vq);
+       int ret;
+       int count;
+       int is_pipe_streamon;
+
+       WARN_ON(!(node->flags & MEDIA_LNK_FL_ENABLED));
+
+       mutex_lock(&pipe->lock);
+       count = atomic_inc_return(&pipe->cnt_nodes_not_streaming);
+       is_pipe_streamon = pipe->streaming;
+       mutex_unlock(&pipe->lock);
+
+       dev_dbg(&pipe->dip_dev->pdev->dev,
+               "%s:%s:%s cnt_nodes_not_streaming(%d), is_pipe_streamon(%d)\n",
+               __func__, pipe->desc->name, node->desc->name, count,
+               is_pipe_streamon);
+
+       if (count && is_pipe_streamon) {
+               ret = v4l2_subdev_call(&pipe->subdev, video, s_stream, 0);
+               if (ret)
+                       dev_err(&pipe->dip_dev->pdev->dev,
+                               "%s:%s: sub dev s_stream(0) failed(%d)\n",
+                               pipe->desc->name, node->desc->name, ret);
+               media_pipeline_stop(&node->vdev.entity);
+       }
+
+       mtk_dip_return_all_buffers(pipe, node, VB2_BUF_STATE_ERROR);
+}
+
+static int mtk_dip_videoc_querycap(struct file *file, void *fh,
+                                  struct v4l2_capability *cap)
+{
+       struct mtk_dip_pipe *pipe = video_drvdata(file);
+
+       strlcpy(cap->driver, pipe->desc->name,
+               sizeof(cap->driver));
+       strlcpy(cap->card, pipe->desc->name,
+               sizeof(cap->card));
+       snprintf(cap->bus_info, sizeof(cap->bus_info),
+                "platform:%s", dev_name(pipe->dip_dev->mdev.dev));
+
+       return 0;
+}
+
+static int mtk_dip_videoc_try_fmt(struct file *file, void *fh,
+                                 struct v4l2_format *f)
+{
+       struct mtk_dip_pipe *pipe = video_drvdata(file);
+       struct mtk_dip_video_device *node = mtk_dip_file_to_node(file);
+       const struct mtk_dip_dev_format *dev_fmt;
+       struct v4l2_format try_fmt;
+
+       memset(&try_fmt, 0, sizeof(try_fmt));
+       try_fmt.type = node->dev_q.vbq.type;
+       dev_fmt = mtk_dip_pipe_find_fmt(pipe, node,
+                                       f->fmt.pix_mp.pixelformat);
+
+       if (!dev_fmt) {
+               dev_fmt = &node->desc->fmts[node->desc->default_fmt_idx];
+               dev_dbg(&pipe->dip_dev->pdev->dev,
+                       "%s:%s:%s: dev_fmt(%d) not found, use default(%d)\n",
+                       __func__, pipe->desc->name, node->desc->name,
+                       f->fmt.pix_mp.pixelformat, dev_fmt->format);
+       }
+
+       if (V4L2_TYPE_IS_OUTPUT(node->desc->buf_type)) {
+               try_fmt.fmt.pix_mp.width = clamp_val(f->fmt.pix_mp.width,
+                                                    MTK_DIP_OUTPUT_MIN_WIDTH,
+                                                    MTK_DIP_OUTPUT_MAX_WIDTH);
+               try_fmt.fmt.pix_mp.height =
+                       clamp_val(f->fmt.pix_mp.height,
+                                 MTK_DIP_OUTPUT_MIN_HEIGHT,
+                                 MTK_DIP_OUTPUT_MAX_HEIGHT);
+       } else {
+               try_fmt.fmt.pix_mp.width = clamp_val(f->fmt.pix_mp.width,
+                                                    MTK_DIP_CAPTURE_MIN_WIDTH,
+                                                    MTK_DIP_CAPTURE_MAX_WIDTH);
+               try_fmt.fmt.pix_mp.height =
+                       clamp_val(f->fmt.pix_mp.height,
+                                 MTK_DIP_CAPTURE_MIN_HEIGHT,
+                                 MTK_DIP_CAPTURE_MAX_HEIGHT);
+       }
+
+       node->dev_q.dev_fmt = dev_fmt;
+       try_fmt.fmt.pix_mp.field = V4L2_FIELD_NONE;
+       mtk_dip_pipe_set_img_fmt(pipe, node, &try_fmt.fmt.pix_mp, dev_fmt);
+
+       *f = try_fmt;
+
+       return 0;
+}
+
+static int mtk_dip_videoc_g_fmt(struct file *file, void *fh,
+                               struct v4l2_format *f)
+{
+       struct mtk_dip_video_device *node = mtk_dip_file_to_node(file);
+
+       *f = node->vdev_fmt;
+
+       return 0;
+}
+
+static int mtk_dip_videoc_s_fmt(struct file *file, void *fh,
+                               struct v4l2_format *f)
+{
+       struct mtk_dip_video_device *node = mtk_dip_file_to_node(file);
+       struct mtk_dip_pipe *pipe = video_drvdata(file);
+       int ret;
+
+       if (pipe->streaming)
+               return -EBUSY;
+
+       ret = mtk_dip_videoc_try_fmt(file, fh, f);
+       if (!ret)
+               node->vdev_fmt = *f;
+
+       return ret;
+}
+
+static int mtk_dip_videoc_enum_framesizes(struct file *file, void *priv,
+                                         struct v4l2_frmsizeenum *sizes)
+{
+       struct mtk_dip_pipe *pipe = video_drvdata(file);
+       struct mtk_dip_video_device *node = mtk_dip_file_to_node(file);
+       const struct mtk_dip_dev_format *dev_fmt;
+
+       dev_fmt = mtk_dip_pipe_find_fmt(pipe, node, sizes->pixel_format);
+
+       if (!dev_fmt || sizes->index)
+               return -EINVAL;
+
+       sizes->type = node->desc->frmsizeenum->type;
+       sizes->stepwise.max_width =
+               node->desc->frmsizeenum->stepwise.max_width;
+       sizes->stepwise.min_width =
+               node->desc->frmsizeenum->stepwise.min_width;
+       sizes->stepwise.max_height =
+               node->desc->frmsizeenum->stepwise.max_height;
+       sizes->stepwise.min_height =
+               node->desc->frmsizeenum->stepwise.min_height;
+       sizes->stepwise.step_height =
+               node->desc->frmsizeenum->stepwise.step_height;
+       sizes->stepwise.step_width =
+               node->desc->frmsizeenum->stepwise.step_width;
+
+       return 0;
+}
+
+static int mtk_dip_videoc_enum_fmt(struct file *file, void *fh,
+                                  struct v4l2_fmtdesc *f)
+{
+       struct mtk_dip_video_device *node = mtk_dip_file_to_node(file);
+
+       if (f->index >= node->desc->num_fmts)
+               return -EINVAL;
+
+       strscpy(f->description, node->desc->description,
+               sizeof(f->description));
+       f->pixelformat = node->desc->fmts[f->index].format;
+       f->flags = 0;
+
+       return 0;
+}
+
+static int mtk_dip_meta_enum_format(struct file *file, void *fh,
+                                   struct v4l2_fmtdesc *f)
+{
+       struct mtk_dip_video_device *node = mtk_dip_file_to_node(file);
+
+       if (f->index > 0)
+               return -EINVAL;
+
+       strscpy(f->description, node->desc->description,
+               sizeof(f->description));
+
+       f->pixelformat = node->vdev_fmt.fmt.meta.dataformat;
+       f->flags = 0;
+
+       return 0;
+}
+
+static int mtk_dip_videoc_g_meta_fmt(struct file *file, void *fh,
+                                    struct v4l2_format *f)
+{
+       struct mtk_dip_video_device *node = mtk_dip_file_to_node(file);
+       *f = node->vdev_fmt;
+
+       return 0;
+}
+
+static int handle_buf_rotate_config(struct v4l2_ctrl *ctrl)
+{
+       struct mtk_dip_video_device *node =
+               container_of(ctrl->handler, struct mtk_dip_video_device,
+                            ctrl_handler);
+
+       if (ctrl->val != 0 && ctrl->val != 90 &&
+           ctrl->val != 180 && ctrl->val != 270) {
+               pr_err("%s: Invalid buffer rotation %d", node->desc->name,
+                      ctrl->val);
+               return -EINVAL;
+       }
+
+       node->dev_q.rotation = ctrl->val;
+
+       return 0;
+}
+
+static int mtk_dip_video_device_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       int ret = 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_ROTATE:
+               ret = handle_buf_rotate_config(ctrl);
+               break;
+       default:
+               break;
+       }
+
+       return ret;
+}
+
+/******************** function pointers ********************/
+
+static const struct v4l2_subdev_internal_ops mtk_dip_subdev_internal_ops = {
+       .open = mtk_dip_subdev_open,
+       .close = mtk_dip_subdev_close,
+};
+
+static const struct v4l2_subdev_video_ops mtk_dip_subdev_video_ops = {
+       .s_stream = mtk_dip_subdev_s_stream,
+};
+
+static const struct v4l2_subdev_ops mtk_dip_subdev_ops = {
+       .video = &mtk_dip_subdev_video_ops,
+};
+
+static const struct media_entity_operations mtk_dip_media_ops = {
+       .link_setup = mtk_dip_link_setup,
+       .link_validate = v4l2_subdev_link_validate,
+};
+
+static struct media_request *mtk_dip_request_alloc(struct media_device *mdev)
+{
+       return kzalloc(sizeof(struct mtk_dip_request), GFP_KERNEL);
+}
+
+static void mtk_dip_request_free(struct media_request *req)
+{
+       kfree(req);
+}
+
+static int mtk_dip_vb2_request_validate(struct media_request *req)
+{
+       struct media_request_object *obj;
+       struct mtk_dip_dev *dip_dev = mtk_dip_mdev_to_dev(req->mdev);
+       struct mtk_dip_request *dip_req = (struct mtk_dip_request *)req;
+       struct mtk_dip_pipe *pipe = NULL;
+       struct mtk_dip_pipe *pipe_prev = NULL;
+       struct mtk_dip_dev_buffer **map = dip_req->job_info.buf_map;
+       int buf_count = 0;
+
+       memset(map, 0, sizeof(dip_req->job_info.buf_map));
+
+       list_for_each_entry(obj, &req->objects, list) {
+               struct vb2_buffer *vb;
+               struct mtk_dip_dev_buffer *dev_buf;
+               struct mtk_dip_video_device *node;
+
+               if (!vb2_request_object_is_buffer(obj))
+                       continue;
+
+               vb = container_of(obj, struct vb2_buffer, req_obj);
+               node = mtk_dip_vbq_to_node(vb->vb2_queue);
+               pipe = vb2_get_drv_priv(vb->vb2_queue);
+               if (pipe_prev && pipe != pipe_prev) {
+                       dev_warn(&dip_dev->pdev->dev,
+                                "%s:%s:%s:found buf of different 
pipes(%p,%p)\n",
+                               __func__, node->desc->name,
+                               req->debug_str, pipe, pipe_prev);
+                       return -EINVAL;
+               }
+
+               pipe_prev = pipe;
+               dev_buf = mtk_dip_vb2_buf_to_dev_buf(vb);
+               dip_req->job_info.buf_map[node->desc->id] = dev_buf;
+               buf_count++;
+
+               dev_dbg(&pipe->dip_dev->pdev->dev,
+                       "%s:%s: added buf(%p) to pipe-job(%p), buf_count(%d)\n",
+                       pipe->desc->name, node->desc->name, dev_buf,
+                       dip_req->job_info, buf_count);
+       }
+
+       if (!pipe) {
+               dev_warn(&dip_dev->pdev->dev,
+                        "%s: no buffer in the request(%p)\n",
+                        req->debug_str, req);
+
+               return vb2_request_validate(req);
+       }
+
+       /*
+        * Workaround to pass V4L2 comliance test since it can't know
+        * which buffer is required and may enqueue only 1 buffer.
+        * It seems that we should return an error here and let the
+        * user to enqueue required buffer instead of showing a debug
+        * message only.
+        */
+       if (!map[MTK_DIP_VIDEO_NODE_ID_RAW_OUT] ||
+           (!map[MTK_DIP_VIDEO_NODE_ID_MDP0_CAPTURE] &&
+            !map[MTK_DIP_VIDEO_NODE_ID_MDP1_CAPTURE]))
+               dev_dbg(&dip_dev->pdev->dev,
+                       "won't trigger hw job: raw(%p), mdp0(%p), mdp1(%p)\n",
+                       map[MTK_DIP_VIDEO_NODE_ID_RAW_OUT],
+                       map[MTK_DIP_VIDEO_NODE_ID_MDP0_CAPTURE],
+                       map[MTK_DIP_VIDEO_NODE_ID_MDP1_CAPTURE]);
+
+       atomic_set(&dip_req->buf_count, buf_count);
+       dip_req->job_info.id = mtk_dip_pipe_next_job_id(pipe);
+       dip_req->dip_pipe = pipe;
+       mtk_dip_pipe_debug_job(pipe, &dip_req->job_info);
+
+       spin_lock(&pipe->job_lock);
+       list_add_tail(&dip_req->job_info.list, &pipe->pipe_job_pending_list);
+       pipe->num_pending_jobs++;
+       dev_dbg(&dip_dev->pdev->dev,
+               "%s:%s: current num of pending jobs(%d)\n",
+               __func__, pipe->desc->name, pipe->num_pending_jobs);
+       spin_unlock(&pipe->job_lock);
+
+       return vb2_request_validate(req);
+}
+
+static const struct media_device_ops mtk_dip_media_req_ops = {
+       .req_validate = mtk_dip_vb2_request_validate,
+       .req_queue = vb2_request_queue,
+       .req_alloc = mtk_dip_request_alloc,
+       .req_free = mtk_dip_request_free,
+};
+
+static const struct v4l2_ctrl_ops mtk_dip_video_device_ctrl_ops = {
+       .s_ctrl = mtk_dip_video_device_s_ctrl,
+};
+
+static const struct vb2_ops mtk_dip_vb2_ops = {
+       .buf_queue = mtk_dip_vb2_buf_queue,
+       .queue_setup = mtk_dip_vb2_queue_setup,
+       .buf_init = mtk_dip_vb2_queue_buf_init,
+       .buf_prepare  = mtk_dip_vb2_buf_prepare,
+       .buf_out_validate = mtk_dip_vb2_buf_out_validate,
+       .buf_cleanup = mtk_dip_vb2_queue_buf_cleanup,
+       .start_streaming = mtk_dip_vb2_start_streaming,
+       .stop_streaming = mtk_dip_vb2_stop_streaming,
+       .wait_prepare = vb2_ops_wait_prepare,
+       .wait_finish = vb2_ops_wait_finish,
+};
+
+static const struct v4l2_file_operations mtk_dip_v4l2_fops = {
+       .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
+};
+
+int mtk_dip_dev_media_register(struct device *dev,
+                              struct media_device *media_dev,
+                              const char *model)
+{
+       int ret;
+
+       media_dev->dev = dev;
+       strlcpy(media_dev->model, model, sizeof(media_dev->model));
+       snprintf(media_dev->bus_info, sizeof(media_dev->bus_info),
+                "platform:%s", dev_name(dev));
+       media_dev->hw_revision = 0;
+       media_dev->ops = &mtk_dip_media_req_ops;
+       media_device_init(media_dev);
+
+       ret = media_device_register(media_dev);
+       if (ret) {
+               dev_err(dev, "failed to register media device (%d)\n", ret);
+               media_device_unregister(media_dev);
+               media_device_cleanup(media_dev);
+       }
+
+       dev_dbg(dev, "Registered media device: %s, %p", media_dev->model,
+               media_dev);
+
+       return ret;
+}
+
+int mtk_dip_dev_v4l2_register(struct device *dev,
+                             struct media_device *media_dev,
+                             struct v4l2_device *v4l2_dev)
+{
+       int ret;
+
+       v4l2_dev->mdev = media_dev;
+       v4l2_dev->ctrl_handler = NULL;
+
+       ret = v4l2_device_register(dev, v4l2_dev);
+       if (ret) {
+               dev_err(dev, "failed to register V4L2 device (%d)\n", ret);
+               v4l2_device_unregister(v4l2_dev);
+       }
+
+       dev_dbg(dev, "Registered v4l2 device: %p", v4l2_dev);
+
+       return ret;
+}
+
+static int mtk_dip_video_device_v4l2_register(struct mtk_dip_pipe *pipe,
+                                             struct mtk_dip_video_device *node)
+{
+       struct vb2_queue *vbq = &node->dev_q.vbq;
+       struct video_device *vdev = &node->vdev;
+       struct media_link *link;
+       int ret;
+
+       /* Initialize miscellaneous variables */
+       mutex_init(&node->dev_q.lock);
+
+       /* Initialize formats to default values */
+       vdev->device_caps = node->desc->cap;
+       vdev->ioctl_ops = node->desc->ops;
+       node->vdev_fmt.type = node->desc->buf_type;
+       mtk_dip_pipe_load_default_fmt(pipe, node, &node->vdev_fmt);
+
+       /* Initialize media entities */
+       ret = media_entity_pads_init(&vdev->entity, 1, &node->vdev_pad);
+       if (ret) {
+               dev_err(&pipe->dip_dev->pdev->dev,
+                       "failed initialize media entity (%d)\n", ret);
+               goto err_mutex_destroy;
+       }
+
+       node->vdev_pad.flags = V4L2_TYPE_IS_OUTPUT(node->desc->buf_type) ?
+               MEDIA_PAD_FL_SOURCE : MEDIA_PAD_FL_SINK;
+
+       /* Initialize vbq */
+       vbq->type = node->vdev_fmt.type;
+       vbq->io_modes = VB2_MMAP | VB2_DMABUF;
+       vbq->ops = &mtk_dip_vb2_ops;
+       vbq->mem_ops = &vb2_dma_contig_memops;
+       vbq->supports_requests = true;
+       vbq->buf_struct_size = sizeof(struct mtk_dip_dev_buffer);
+       vbq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+       vbq->min_buffers_needed = 0;
+       vbq->drv_priv = pipe;
+       vbq->lock = &node->dev_q.lock;
+
+       ret = vb2_queue_init(vbq);
+       if (ret) {
+               dev_err(&pipe->dip_dev->pdev->dev,
+                       "%s:%s:%s: failed to init vb2 queue(%d)\n",
+                       __func__, pipe->desc->name, node->desc->name,
+                       ret);
+               goto err_media_entity_cleanup;
+       }
+
+       /* Initialize vdev */
+       snprintf(vdev->name, sizeof(vdev->name), "%s %s", pipe->desc->name,
+                node->desc->name);
+       vdev->entity.name = vdev->name;
+       vdev->entity.function = MEDIA_ENT_F_IO_V4L;
+       vdev->entity.ops = NULL;
+       vdev->release = video_device_release_empty;
+       vdev->fops = &mtk_dip_v4l2_fops;
+       vdev->lock = &node->dev_q.lock;
+       vdev->ctrl_handler = &node->ctrl_handler;
+       vdev->v4l2_dev = &pipe->dip_dev->v4l2_dev;
+       vdev->queue = &node->dev_q.vbq;
+       vdev->vfl_dir = V4L2_TYPE_IS_OUTPUT(node->desc->buf_type) ?
+               VFL_DIR_TX : VFL_DIR_RX;
+
+       if (node->desc->smem_alloc) {
+               vdev->queue->dev = &pipe->dip_dev->dip_hw.scp_pdev->dev;
+               dev_dbg(&pipe->dip_dev->pdev->dev,
+                       "%s:%s: select smem_vb2_alloc_ctx(%p)\n",
+                       pipe->desc->name, node->desc->name,
+                       vdev->queue->dev);
+       } else {
+               vdev->queue->dev = &pipe->dip_dev->pdev->dev;
+               dev_dbg(&pipe->dip_dev->pdev->dev,
+                       "%s:%s: select default_vb2_alloc_ctx(%p)\n",
+                       pipe->desc->name, node->desc->name,
+                       &pipe->dip_dev->pdev->dev);
+       }
+
+       video_set_drvdata(vdev, pipe);
+
+       ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+       if (ret) {
+               dev_err(&pipe->dip_dev->pdev->dev,
+                       "failed to register video device (%d)\n", ret);
+               goto err_vb2_queue_release;
+       }
+       dev_dbg(&pipe->dip_dev->pdev->dev, "registered vdev: %s\n",
+               vdev->name);
+
+       /* Create link between video node and the subdev pad */
+       if (V4L2_TYPE_IS_OUTPUT(node->desc->buf_type))
+               ret = media_create_pad_link(&vdev->entity, 0,
+                                           &pipe->subdev.entity,
+                                           node->desc->id, node->flags);
+       else
+               ret = media_create_pad_link(&pipe->subdev.entity,
+                                           node->desc->id, &vdev->entity,
+                                           0, node->flags);
+       if (ret)
+               goto err_video_unregister_device;
+
+       vdev->intf_devnode = media_devnode_create(&pipe->dip_dev->mdev,
+                                                 MEDIA_INTF_T_V4L_VIDEO, 0,
+                                                 VIDEO_MAJOR, vdev->minor);
+       if (!vdev->intf_devnode) {
+               ret = -ENOMEM;
+               goto err_rm_links;
+       }
+
+       link = media_create_intf_link(&vdev->entity,
+                                     &vdev->intf_devnode->intf,
+                                     node->flags);
+       if (!link) {
+               ret = -ENOMEM;
+               goto err_rm_devnode;
+       }
+
+       return 0;
+
+err_rm_devnode:
+       media_devnode_remove(vdev->intf_devnode);
+
+err_rm_links:
+       media_entity_remove_links(&vdev->entity);
+
+err_video_unregister_device:
+       video_unregister_device(vdev);
+
+err_vb2_queue_release:
+       vb2_queue_release(&node->dev_q.vbq);
+
+err_media_entity_cleanup:
+       media_entity_cleanup(&node->vdev.entity);
+
+err_mutex_destroy:
+       mutex_destroy(&node->dev_q.lock);
+
+       return ret;
+}
+
+static int mtk_dip_pipe_v4l2_ctrl_init(struct mtk_dip_pipe *dip_pipe)
+{
+       struct mtk_dip_video_device *node;
+       int i;
+
+       for (i = 0; i < MTK_DIP_VIDEO_NODE_ID_TOTAL_NUM; i++)
+               v4l2_ctrl_handler_init(&dip_pipe->nodes[i].ctrl_handler,
+                                      V4L2_CID_MTK_DIP_MAX);
+
+       /* Only MDP 0 support rotate in MT8183 */
+       node = &dip_pipe->nodes[MTK_DIP_VIDEO_NODE_ID_MDP0_CAPTURE];
+       if (v4l2_ctrl_new_std(&node->ctrl_handler,
+                             &mtk_dip_video_device_ctrl_ops,
+                             V4L2_CID_ROTATE, 0, 270, 90, 0) == NULL) {
+               dev_err(&dip_pipe->dip_dev->pdev->dev,
+                       "%s create rotate ctrl failed:(%d)",
+                       node->desc->name, node->ctrl_handler.error);
+
+               return -EPROBE_DEFER;
+       }
+
+       return 0;
+}
+
+static void mtk_dip_pipe_v4l2_ctrl_release(struct mtk_dip_pipe *dip_pipe)
+{
+       int i;
+
+       for (i = 0; i < MTK_DIP_VIDEO_NODE_ID_TOTAL_NUM; i++)
+               v4l2_ctrl_handler_free(&dip_pipe->nodes[i].ctrl_handler);
+}
+
+int mtk_dip_pipe_v4l2_register(struct mtk_dip_pipe *pipe,
+                              struct media_device *media_dev,
+                              struct v4l2_device *v4l2_dev)
+{
+       int i, ret;
+
+       ret = mtk_dip_pipe_v4l2_ctrl_init(pipe);
+       if (ret) {
+               dev_err(&pipe->dip_dev->pdev->dev,
+                       "%s: failed(%d) to initialize ctrls\n",
+                       pipe->desc->name, ret);
+
+               return ret;
+       }
+
+       pipe->streaming = 0;
+
+       /* Initialize subdev media entity */
+       pipe->subdev_pads = devm_kcalloc(&pipe->dip_dev->pdev->dev,
+                                        pipe->num_nodes,
+                                        sizeof(*pipe->subdev_pads),
+                                        GFP_KERNEL);
+       if (!pipe->subdev_pads) {
+               dev_err(&pipe->dip_dev->pdev->dev,
+                       "failed to alloc pipe->subdev_pads (%d)\n", ret);
+               ret = -ENOMEM;
+               goto err_release_ctrl;
+       }
+       ret = media_entity_pads_init(&pipe->subdev.entity,
+                                    pipe->num_nodes,
+                                    pipe->subdev_pads);
+       if (ret) {
+               dev_err(&pipe->dip_dev->pdev->dev,
+                       "failed initialize subdev media entity (%d)\n", ret);
+               goto err_free_subdev_pads;
+       }
+
+       /* Initialize subdev */
+       v4l2_subdev_init(&pipe->subdev, &mtk_dip_subdev_ops);
+
+       pipe->subdev.entity.function =
+               MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
+       pipe->subdev.entity.ops = &mtk_dip_media_ops;
+       pipe->subdev.flags =
+               V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
+       pipe->subdev.ctrl_handler = NULL;
+       pipe->subdev.internal_ops = &mtk_dip_subdev_internal_ops;
+
+       for (i = 0; i < pipe->num_nodes; i++)
+               pipe->subdev_pads[i].flags =
+                       V4L2_TYPE_IS_OUTPUT(pipe->nodes[i].desc->buf_type) ?
+                       MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
+
+       snprintf(pipe->subdev.name, sizeof(pipe->subdev.name),
+                "%s", pipe->desc->name);
+       v4l2_set_subdevdata(&pipe->subdev, pipe);
+
+       ret = v4l2_device_register_subdev(&pipe->dip_dev->v4l2_dev,
+                                         &pipe->subdev);
+       if (ret) {
+               dev_err(&pipe->dip_dev->pdev->dev,
+                       "failed initialize subdev (%d)\n", ret);
+               goto err_media_entity_cleanup;
+       }
+
+       dev_dbg(&pipe->dip_dev->pdev->dev,
+               "register subdev: %s, ctrl_handler %p\n",
+                pipe->subdev.name, pipe->subdev.ctrl_handler);
+
+       ret = v4l2_device_register_subdev_nodes(&pipe->dip_dev->v4l2_dev);
+       if (ret) {
+               dev_err(&pipe->dip_dev->pdev->dev,
+                       "failed to register subdevs (%d)\n", ret);
+               goto err_unregister_subdev;
+       }
+
+       /* Create video nodes and links */
+       for (i = 0; i < pipe->num_nodes; i++) {
+               ret = mtk_dip_video_device_v4l2_register(pipe,
+                                                        &pipe->nodes[i]);
+               if (ret)
+                       goto err_unregister_subdev;
+       }
+
+       return 0;
+
+err_unregister_subdev:
+       v4l2_device_unregister_subdev(&pipe->subdev);
+
+err_media_entity_cleanup:
+       media_entity_cleanup(&pipe->subdev.entity);
+
+err_free_subdev_pads:
+       devm_kfree(&pipe->dip_dev->pdev->dev, pipe->subdev_pads);
+
+err_release_ctrl:
+       mtk_dip_pipe_v4l2_ctrl_release(pipe);
+
+       return ret;
+}
+
+void mtk_dip_pipe_v4l2_unregister(struct mtk_dip_pipe *pipe)
+{
+       unsigned int i;
+
+       for (i = 0; i < pipe->num_nodes; i++) {
+               video_unregister_device(&pipe->nodes[i].vdev);
+               vb2_queue_release(&pipe->nodes[i].dev_q.vbq);
+               media_entity_cleanup(&pipe->nodes[i].vdev.entity);
+               mutex_destroy(&pipe->nodes[i].dev_q.lock);
+       }
+
+       v4l2_device_unregister_subdev(&pipe->subdev);
+       media_entity_cleanup(&pipe->subdev.entity);
+       v4l2_device_unregister(&pipe->dip_dev->v4l2_dev);
+       media_device_unregister(&pipe->dip_dev->mdev);
+       media_device_cleanup(&pipe->dip_dev->mdev);
+       mtk_dip_pipe_v4l2_ctrl_release(pipe);
+}
+
+/********************************************
+ * MTK DIP V4L2 Settings *
+ ********************************************/
+
+static const struct v4l2_ioctl_ops mtk_dip_v4l2_video_out_ioctl_ops = {
+       .vidioc_querycap = mtk_dip_videoc_querycap,
+
+       .vidioc_enum_framesizes = mtk_dip_videoc_enum_framesizes,
+       .vidioc_enum_fmt_vid_cap_mplane = mtk_dip_videoc_enum_fmt,
+       .vidioc_g_fmt_vid_cap_mplane = mtk_dip_videoc_g_fmt,
+       .vidioc_s_fmt_vid_cap_mplane = mtk_dip_videoc_s_fmt,
+       .vidioc_try_fmt_vid_cap_mplane = mtk_dip_videoc_try_fmt,
+
+       .vidioc_enum_fmt_vid_out_mplane = mtk_dip_videoc_enum_fmt,
+       .vidioc_g_fmt_vid_out_mplane = mtk_dip_videoc_g_fmt,
+       .vidioc_s_fmt_vid_out_mplane = mtk_dip_videoc_s_fmt,
+       .vidioc_try_fmt_vid_out_mplane = mtk_dip_videoc_try_fmt,
+
+       .vidioc_reqbufs = vb2_ioctl_reqbufs,
+       .vidioc_create_bufs = vb2_ioctl_create_bufs,
+       .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+       .vidioc_querybuf = vb2_ioctl_querybuf,
+       .vidioc_qbuf = vb2_ioctl_qbuf,
+       .vidioc_dqbuf = vb2_ioctl_dqbuf,
+       .vidioc_streamon = vb2_ioctl_streamon,
+       .vidioc_streamoff = vb2_ioctl_streamoff,
+       .vidioc_expbuf = vb2_ioctl_expbuf,
+
+       .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+       .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct v4l2_ioctl_ops mtk_dip_v4l2_video_cap_ioctl_ops = {
+       .vidioc_querycap = mtk_dip_videoc_querycap,
+
+       .vidioc_enum_framesizes = mtk_dip_videoc_enum_framesizes,
+       .vidioc_enum_fmt_vid_cap_mplane = mtk_dip_videoc_enum_fmt,
+       .vidioc_g_fmt_vid_cap_mplane = mtk_dip_videoc_g_fmt,
+       .vidioc_s_fmt_vid_cap_mplane = mtk_dip_videoc_s_fmt,
+       .vidioc_try_fmt_vid_cap_mplane = mtk_dip_videoc_try_fmt,
+
+       .vidioc_enum_fmt_vid_out_mplane = mtk_dip_videoc_enum_fmt,
+       .vidioc_g_fmt_vid_out_mplane = mtk_dip_videoc_g_fmt,
+       .vidioc_s_fmt_vid_out_mplane = mtk_dip_videoc_s_fmt,
+       .vidioc_try_fmt_vid_out_mplane = mtk_dip_videoc_try_fmt,
+
+       .vidioc_reqbufs = vb2_ioctl_reqbufs,
+       .vidioc_create_bufs = vb2_ioctl_create_bufs,
+       .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+       .vidioc_querybuf = vb2_ioctl_querybuf,
+       .vidioc_qbuf = vb2_ioctl_qbuf,
+       .vidioc_dqbuf = vb2_ioctl_dqbuf,
+       .vidioc_streamon = vb2_ioctl_streamon,
+       .vidioc_streamoff = vb2_ioctl_streamoff,
+       .vidioc_expbuf = vb2_ioctl_expbuf,
+
+       .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+       .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct v4l2_ioctl_ops mtk_dip_v4l2_meta_out_ioctl_ops = {
+       .vidioc_querycap = mtk_dip_videoc_querycap,
+
+       .vidioc_enum_fmt_meta_cap = mtk_dip_meta_enum_format,
+       .vidioc_g_fmt_meta_cap = mtk_dip_videoc_g_meta_fmt,
+       .vidioc_s_fmt_meta_cap = mtk_dip_videoc_g_meta_fmt,
+       .vidioc_try_fmt_meta_cap = mtk_dip_videoc_g_meta_fmt,
+
+       .vidioc_enum_fmt_meta_out = mtk_dip_meta_enum_format,
+       .vidioc_g_fmt_meta_out = mtk_dip_videoc_g_meta_fmt,
+       .vidioc_s_fmt_meta_out = mtk_dip_videoc_g_meta_fmt,
+       .vidioc_try_fmt_meta_out = mtk_dip_videoc_g_meta_fmt,
+
+       .vidioc_reqbufs = vb2_ioctl_reqbufs,
+       .vidioc_create_bufs = vb2_ioctl_create_bufs,
+       .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+       .vidioc_querybuf = vb2_ioctl_querybuf,
+       .vidioc_qbuf = vb2_ioctl_qbuf,
+       .vidioc_dqbuf = vb2_ioctl_dqbuf,
+       .vidioc_streamon = vb2_ioctl_streamon,
+       .vidioc_streamoff = vb2_ioctl_streamoff,
+       .vidioc_expbuf = vb2_ioctl_expbuf,
+};
+
+static const struct mtk_dip_dev_format fw_param_fmts[] = {
+       {
+               .format = V4L2_META_FMT_MTISP_PARAMS,
+               .buffer_size = 1024 * 128,
+       },
+};
+
+static const struct mtk_dip_dev_format in_fmts[] = {
+       {
+               .format = V4L2_PIX_FMT_MTISP_B10,
+               .mdp_color = MDP_COLOR_BAYER10,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .depth = { 10 },
+               .row_depth = { 10 },
+               .num_planes = 1,
+       },
+       {
+               .format = V4L2_PIX_FMT_MTISP_F10,
+               .mdp_color = MDP_COLOR_FULLG10,
+               .colorspace = V4L2_COLORSPACE_RAW,
+               .depth = { 15 },
+               .row_depth = { 15 },
+               .num_planes = 1,
+       },
+       {
+               .format = V4L2_PIX_FMT_VYUY,
+               .mdp_color = MDP_COLOR_VYUY,
+               .colorspace = V4L2_COLORSPACE_BT2020,
+               .depth   = { 16 },
+               .row_depth = { 16 },
+               .num_planes = 1,
+       },
+       {
+               .format = V4L2_PIX_FMT_YUYV,
+               .mdp_color = MDP_COLOR_YUYV,
+               .colorspace = V4L2_COLORSPACE_BT2020,
+               .depth   = { 16 },
+               .row_depth = { 16 },
+               .num_planes = 1,
+       },
+       {
+               .format = V4L2_PIX_FMT_YVYU,
+               .mdp_color = MDP_COLOR_YVYU,
+               .colorspace = V4L2_COLORSPACE_BT2020,
+               .depth   = { 16 },
+               .row_depth = { 16 },
+               .num_planes = 1,
+       },
+       {
+               .format = V4L2_PIX_FMT_NV12,
+               .mdp_color = MDP_COLOR_NV12,
+               .colorspace = V4L2_COLORSPACE_BT2020,
+               .depth = { 12 },
+               .row_depth = { 8 },
+               .num_planes = 1,
+       },
+       {
+               .format = V4L2_PIX_FMT_YUV420M,
+               .mdp_color      = MDP_COLOR_I420,
+               .colorspace = V4L2_COLORSPACE_BT2020,
+               .depth          = { 8, 2, 2 },
+               .row_depth      = { 8, 4, 4 },
+               .num_planes     = 3,
+       },
+       {
+               .format = V4L2_PIX_FMT_YVU420M,
+               .mdp_color      = MDP_COLOR_YV12,
+               .colorspace = V4L2_COLORSPACE_BT2020,
+               .depth          = { 8, 2, 2 },
+               .row_depth      = { 8, 4, 4 },
+               .num_planes     = 3,
+       },
+       {
+               .format = V4L2_PIX_FMT_NV12M,
+               .mdp_color      = MDP_COLOR_NV12,
+               .colorspace = V4L2_COLORSPACE_BT2020,
+               .depth          = { 8, 4 },
+               .row_depth      = { 8, 8 },
+               .num_planes     = 2,
+       },
+};
+
+static const struct mtk_dip_dev_format out_fmts[] = {
+       {
+               .format = V4L2_PIX_FMT_VYUY,
+               .mdp_color = MDP_COLOR_VYUY,
+               .colorspace = MDP_YCBCR_PROFILE_BT601,
+               .depth = { 16 },
+               .row_depth = { 16 },
+               .num_planes = 1,
+       },
+       {
+               .format = V4L2_PIX_FMT_YUYV,
+               .mdp_color = MDP_COLOR_YUYV,
+               .colorspace = MDP_YCBCR_PROFILE_BT601,
+               .depth = { 16 },
+               .row_depth = { 16 },
+               .num_planes = 1,
+       },
+       {
+               .format = V4L2_PIX_FMT_YVYU,
+               .mdp_color = MDP_COLOR_YVYU,
+               .colorspace = MDP_YCBCR_PROFILE_BT601,
+               .depth = { 16 },
+               .row_depth = { 16 },
+               .num_planes = 1,
+       },
+       {
+               .format = V4L2_PIX_FMT_YVU420,
+               .mdp_color = MDP_COLOR_YV12,
+               .colorspace = MDP_YCBCR_PROFILE_BT601,
+               .depth = { 12 },
+               .row_depth = { 8 },
+               .num_planes = 1,
+       },
+       {
+               .format = V4L2_PIX_FMT_NV12,
+               .mdp_color = MDP_COLOR_NV12,
+               .colorspace = MDP_YCBCR_PROFILE_BT601,
+               .depth = { 12 },
+               .row_depth = { 8 },
+               .num_planes = 1,
+       },
+       {
+               .format = V4L2_PIX_FMT_YUV420M,
+               .mdp_color      = MDP_COLOR_I420,
+               .colorspace = V4L2_COLORSPACE_BT2020,
+               .depth          = { 8, 2, 2 },
+               .row_depth      = { 8, 4, 4 },
+               .num_planes     = 3,
+       },
+       {
+               .format = V4L2_PIX_FMT_YVU420M,
+               .mdp_color      = MDP_COLOR_YV12,
+               .colorspace = V4L2_COLORSPACE_BT2020,
+               .depth          = { 8, 2, 2 },
+               .row_depth      = { 8, 4, 4 },
+               .num_planes     = 3,
+       },
+       {
+               .format = V4L2_PIX_FMT_NV12M,
+               .mdp_color      = MDP_COLOR_NV12,
+               .colorspace = V4L2_COLORSPACE_BT2020,
+               .depth          = { 8, 4 },
+               .row_depth      = { 8, 8 },
+               .num_planes     = 2,
+       }
+};
+
+static const struct v4l2_frmsizeenum in_frmsizeenum = {
+       .type = V4L2_FRMSIZE_TYPE_CONTINUOUS,
+       .stepwise.max_width = MTK_DIP_CAPTURE_MAX_WIDTH,
+       .stepwise.min_width = MTK_DIP_CAPTURE_MIN_WIDTH,
+       .stepwise.max_height = MTK_DIP_CAPTURE_MAX_HEIGHT,
+       .stepwise.min_height = MTK_DIP_CAPTURE_MIN_HEIGHT,
+       .stepwise.step_height = 1,
+       .stepwise.step_width = 1,
+};
+
+static const struct v4l2_frmsizeenum out_frmsizeenum = {
+       .type = V4L2_FRMSIZE_TYPE_CONTINUOUS,
+       .stepwise.max_width = MTK_DIP_OUTPUT_MAX_WIDTH,
+       .stepwise.min_width = MTK_DIP_OUTPUT_MIN_WIDTH,
+       .stepwise.max_height = MTK_DIP_OUTPUT_MAX_HEIGHT,
+       .stepwise.min_height = MTK_DIP_OUTPUT_MIN_HEIGHT,
+       .stepwise.step_height = 1,
+       .stepwise.step_width = 1,
+};
+
+static const struct mtk_dip_video_device_desc
+output_queues_setting[MTK_DIP_VIDEO_NODE_ID_OUT_TOTAL_NUM] = {
+       {
+               .id = MTK_DIP_VIDEO_NODE_ID_RAW_OUT,
+               .name = "Raw Input",
+               .cap = V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_STREAMING,
+               .buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+               .smem_alloc = 0,
+               .flags = MEDIA_LNK_FL_ENABLED,
+               .fmts = in_fmts,
+               .num_fmts = ARRAY_SIZE(in_fmts),
+               .default_fmt_idx = 0,
+               .default_width = MTK_DIP_OUTPUT_MAX_WIDTH,
+               .default_height = MTK_DIP_OUTPUT_MAX_HEIGHT,
+               .dma_port = 0,
+               .frmsizeenum = &in_frmsizeenum,
+               .ops = &mtk_dip_v4l2_video_out_ioctl_ops,
+               .description = "Main image source",
+       },
+       {
+               .id = MTK_DIP_VIDEO_NODE_ID_TUNING_OUT,
+               .name = "Tuning",
+               .cap = V4L2_CAP_META_OUTPUT | V4L2_CAP_STREAMING,
+               .buf_type = V4L2_BUF_TYPE_META_OUTPUT,
+               .smem_alloc = 1,
+               .flags = 0,
+               .fmts = fw_param_fmts,
+               .num_fmts = 1,
+               .default_fmt_idx = 0,
+               .dma_port = 0,
+               .frmsizeenum = &in_frmsizeenum,
+               .ops = &mtk_dip_v4l2_meta_out_ioctl_ops,
+               .description = "Tuning data",
+       },
+};
+
+static const struct mtk_dip_video_device_desc
+reprocess_output_queues_setting[MTK_DIP_VIDEO_NODE_ID_OUT_TOTAL_NUM] = {
+       {
+               .id = MTK_DIP_VIDEO_NODE_ID_RAW_OUT,
+               .name = "Raw Input",
+               .cap = V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_STREAMING,
+               .buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+               .smem_alloc = 0,
+               .flags = MEDIA_LNK_FL_ENABLED,
+               .fmts = in_fmts,
+               .num_fmts = ARRAY_SIZE(in_fmts),
+               .default_fmt_idx = 5,
+               .default_width = MTK_DIP_OUTPUT_MAX_WIDTH,
+               .default_height = MTK_DIP_OUTPUT_MAX_HEIGHT,
+               .dma_port = 0,
+               .frmsizeenum = &in_frmsizeenum,
+               .ops = &mtk_dip_v4l2_video_out_ioctl_ops,
+               .description = "Source image to be process",
+
+       },
+       {
+               .id = MTK_DIP_VIDEO_NODE_ID_TUNING_OUT,
+               .name = "Tuning",
+               .cap = V4L2_CAP_META_OUTPUT | V4L2_CAP_STREAMING,
+               .buf_type = V4L2_BUF_TYPE_META_OUTPUT,
+               .smem_alloc = 1,
+               .flags = 0,
+               .fmts = fw_param_fmts,
+               .num_fmts = 1,
+               .default_fmt_idx = 0,
+               .dma_port = 0,
+               .frmsizeenum = &in_frmsizeenum,
+               .ops = &mtk_dip_v4l2_meta_out_ioctl_ops,
+               .description = "Tuning data for image enhancement",
+       },
+};
+
+static const struct mtk_dip_video_device_desc
+capture_queues_setting[MTK_DIP_VIDEO_NODE_ID_CAPTURE_TOTAL_NUM] = {
+       {
+               .id = MTK_DIP_VIDEO_NODE_ID_MDP0_CAPTURE,
+               .name = "MDP0",
+               .cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING,
+               .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+               .smem_alloc = 0,
+               .flags = MEDIA_LNK_FL_DYNAMIC | MEDIA_LNK_FL_ENABLED,
+               .fmts = out_fmts,
+               .num_fmts = ARRAY_SIZE(out_fmts),
+               .default_fmt_idx = 1,
+               .default_width = MTK_DIP_CAPTURE_MAX_WIDTH,
+               .default_height = MTK_DIP_CAPTURE_MAX_HEIGHT,
+               .dma_port = 0,
+               .frmsizeenum = &out_frmsizeenum,
+               .ops = &mtk_dip_v4l2_video_cap_ioctl_ops,
+               .description = "Output quality enhanced image",
+       },
+       {
+               .id = MTK_DIP_VIDEO_NODE_ID_MDP1_CAPTURE,
+               .name = "MDP1",
+               .cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING,
+               .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+               .smem_alloc = 0,
+               .flags = MEDIA_LNK_FL_DYNAMIC | MEDIA_LNK_FL_ENABLED,
+               .fmts = out_fmts,
+               .num_fmts = ARRAY_SIZE(out_fmts),
+               .default_fmt_idx = 1,
+               .default_width = MTK_DIP_CAPTURE_MAX_WIDTH,
+               .default_height = MTK_DIP_CAPTURE_MAX_HEIGHT,
+               .dma_port = 0,
+               .frmsizeenum = &out_frmsizeenum,
+               .ops = &mtk_dip_v4l2_video_cap_ioctl_ops,
+               .description = "Output quality enhanced image",
+
+       },
+};
+
+static const struct mtk_dip_pipe_desc
+pipe_settings[MTK_DIP_PIPE_ID_TOTAL_NUM] = {
+       {
+               .name = MTK_DIP_DEV_DIP_PREVIEW_NAME,
+               .id = MTK_DIP_PIPE_ID_PREVIEW,
+               .master = MTK_DIP_VIDEO_NODE_ID_NO_MASTER,
+               .output_queue_descs = output_queues_setting,
+               .total_output_queues = MTK_DIP_VIDEO_NODE_ID_OUT_TOTAL_NUM,
+               .capture_queue_descs = capture_queues_setting,
+               .total_capture_queues = MTK_DIP_VIDEO_NODE_ID_CAPTURE_TOTAL_NUM,
+       },
+       {
+               .name = MTK_DIP_DEV_DIP_CAPTURE_NAME,
+               .id = MTK_DIP_PIPE_ID_CAPTURE,
+               .master = MTK_DIP_VIDEO_NODE_ID_NO_MASTER,
+               .output_queue_descs = output_queues_setting,
+               .total_output_queues = MTK_DIP_VIDEO_NODE_ID_OUT_TOTAL_NUM,
+               .capture_queue_descs = capture_queues_setting,
+               .total_capture_queues = MTK_DIP_VIDEO_NODE_ID_CAPTURE_TOTAL_NUM,
+       },
+       {
+               .name = MTK_DIP_DEV_DIP_REPROCESS_NAME,
+               .id = MTK_DIP_PIPE_ID_REPROCESS,
+               .master = MTK_DIP_VIDEO_NODE_ID_NO_MASTER,
+               .output_queue_descs = reprocess_output_queues_setting,
+               .total_output_queues = MTK_DIP_VIDEO_NODE_ID_OUT_TOTAL_NUM,
+               .capture_queue_descs = capture_queues_setting,
+               .total_capture_queues = MTK_DIP_VIDEO_NODE_ID_CAPTURE_TOTAL_NUM,
+       },
+};
+
+static int mtk_dip_dev_v4l2_init(struct mtk_dip_dev *dip_dev)
+{
+       struct media_device *media_dev = &dip_dev->mdev;
+       struct v4l2_device *v4l2_dev = &dip_dev->v4l2_dev;
+       int i;
+       int ret;
+
+       ret = mtk_dip_dev_media_register(&dip_dev->pdev->dev,
+                                        media_dev,
+                                        MTK_DIP_DEV_DIP_MEDIA_MODEL_NAME);
+       if (ret) {
+               dev_err(&dip_dev->pdev->dev,
+                       "%s: media device register failed(%d)\n",
+                       __func__, ret);
+               return ret;
+       }
+
+       ret = mtk_dip_dev_v4l2_register(&dip_dev->pdev->dev,
+                                       media_dev,
+                                       v4l2_dev);
+       if (ret) {
+               dev_err(&dip_dev->pdev->dev,
+                       "%s: v4l2 device register failed(%d)\n",
+                       __func__, ret);
+               goto err_release_media_device;
+       }
+
+       for (i = 0; i < MTK_DIP_PIPE_ID_TOTAL_NUM; i++) {
+               ret = mtk_dip_pipe_init(&dip_dev->dip_pipe[i], dip_dev,
+                                       &pipe_settings[i], media_dev, v4l2_dev);
+               if (ret) {
+                       dev_err(&dip_dev->pdev->dev,
+                               "%s: Pipe id(%d) init failed(%d)\n",
+                               dip_dev->dip_pipe[i].desc->name,
+                               i, ret);
+                       goto err_release_pipe;
+               }
+       }
+
+       return 0;
+
+err_release_pipe:
+       for (i = i - 1; i >= 0; i--)
+               mtk_dip_pipe_release(&dip_dev->dip_pipe[i]);
+
+       v4l2_device_unregister(v4l2_dev);
+
+err_release_media_device:
+       media_device_unregister(media_dev);
+       media_device_cleanup(media_dev);
+
+       return ret;
+}
+
+void mtk_dip_dev_v4l2_release(struct mtk_dip_dev *dip_dev)
+{
+       int i;
+
+       for (i = 0; i < MTK_DIP_PIPE_ID_TOTAL_NUM; i++)
+               mtk_dip_pipe_release(&dip_dev->dip_pipe[i]);
+
+       v4l2_device_unregister(&dip_dev->v4l2_dev);
+       media_device_unregister(&dip_dev->mdev);
+       media_device_cleanup(&dip_dev->mdev);
+}
+
+static int mtk_dip_probe(struct platform_device *pdev)
+{
+       struct mtk_dip_dev *dip_dev;
+       struct mtk_dip_hw *dip_hw;
+       struct device_node *node;
+       struct platform_device *larb_pdev;
+       phandle rproc_phandle;
+       int ret;
+
+       dip_dev = devm_kzalloc(&pdev->dev, sizeof(*dip_dev), GFP_KERNEL);
+       if (!dip_dev)
+               return -ENOMEM;
+
+       dip_dev->pdev = pdev;
+       dev_set_drvdata(&pdev->dev, dip_dev);
+       dip_hw = &dip_dev->dip_hw;
+
+       node = of_parse_phandle(pdev->dev.of_node, "mediatek,larb", 0);
+       if (!node) {
+               dev_err(&pdev->dev, "No mediatek,larb found");
+               return -EINVAL;
+       }
+
+       larb_pdev = of_find_device_by_node(node);
+       if (!larb_pdev) {
+               dev_err(&pdev->dev, "No mediatek,larb device found");
+               return -EINVAL;
+       }
+
+       dip_hw->clks[0].id = "larb5";
+       dip_hw->clks[1].id = "dip";
+
+       ret = devm_clk_bulk_get(&pdev->dev, MTK_DIP_CLK_NUM,
+                               dip_hw->clks);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to get LARB5 and DIP clks:%d\n",
+                       ret);
+               return ret;
+       }
+
+       dip_hw->scp_pdev = scp_get_pdev(dip_dev->pdev);
+       if (!dip_hw->scp_pdev) {
+               dev_err(&dip_dev->pdev->dev,
+                       "%s: failed to get scp device\n",
+                       __func__);
+               return -EINVAL;
+       }
+
+       if (of_property_read_u32(dip_dev->pdev->dev.of_node,
+                                "mediatek,scp",
+                                &rproc_phandle)) {
+               dev_err(&dip_dev->pdev->dev,
+                       "%s: could not get scp device\n",
+                       __func__);
+               return -EINVAL;
+       }
+
+       dip_hw->rproc_handle = rproc_get_by_phandle(rproc_phandle);
+       if (!dip_hw->rproc_handle) {
+               dev_err(&dip_dev->pdev->dev,
+                       "%s: could not get DIP's rproc_handle\n",
+                       __func__);
+               return -EINVAL;
+       }
+
+       atomic_set(&dip_hw->dip_stream_cnt, 0);
+       atomic_set(&dip_hw->dip_enque_cnt, 0);
+       atomic_set(&dip_hw->num_composing, 0);
+       atomic_set(&dip_hw->num_running, 0);
+       mutex_init(&dip_hw->hw_op_lock);
+       /* Limited by the co-processor side's stack size */
+       sema_init(&dip_hw->sem, DIP_COMPOSING_MAX_NUM);
+
+       ret = mtk_dip_hw_working_buf_pool_init(dip_hw);
+       if (ret) {
+               dev_err(&pdev->dev, "working buffer init failed(%d)\n", ret);
+               return ret;
+       }
+
+       ret = mtk_dip_dev_v4l2_init(dip_dev);
+       if (ret)
+               dev_err(&pdev->dev, "v4l2 init failed(%d)\n", ret);
+
+       pm_runtime_enable(&pdev->dev);
+
+       return ret;
+}
+
+static int mtk_dip_remove(struct platform_device *pdev)
+{
+       struct mtk_dip_dev *dip_dev = dev_get_drvdata(&pdev->dev);
+
+       mtk_dip_hw_working_buf_pool_release(&dip_dev->dip_hw);
+       mtk_dip_dev_v4l2_release(dip_dev);
+       pm_runtime_disable(&pdev->dev);
+       mutex_destroy(&dip_dev->dip_hw.hw_op_lock);
+
+       return 0;
+}
+
+static int __maybe_unused mtk_dip_pm_suspend(struct device *dev)
+{
+       struct mtk_dip_dev *dip_dev = dev_get_drvdata(dev);
+
+       clk_bulk_disable_unprepare(MTK_DIP_CLK_NUM, dip_dev->dip_hw.clks);
+       dev_dbg(dev, "%s: disabled dip clks\n", __func__);
+
+       return 0;
+}
+
+static int __maybe_unused mtk_dip_pm_resume(struct device *dev)
+{
+       struct mtk_dip_dev *dip_dev = dev_get_drvdata(dev);
+       int ret = clk_bulk_prepare_enable(MTK_DIP_CLK_NUM,
+                                         dip_dev->dip_hw.clks);
+       if (ret) {
+               dev_err(dev, "%s: failed to enable dip clks(%d)\n",
+                       __func__, ret);
+               return ret;
+       }
+       dev_dbg(dev, "%s: enabled dip clks\n", __func__);
+
+       ret = rproc_boot(dip_dev->dip_hw.rproc_handle);
+       if (ret)
+               dev_err(dev, "%s: FW load failed(rproc:%p):%d\n",
+                       __func__, dip_dev->dip_hw.rproc_handle, ret);
+       else
+               dev_dbg(dev, "%s: FW loaded(rproc:%p)\n",
+                       __func__, dip_dev->dip_hw.rproc_handle);
+
+       return ret;
+}
+
+static int mtk_dip_get_scp_job_num(struct mtk_dip_dev *dip_dev)
+{
+       int num;
+
+       mutex_lock(&dip_dev->dip_hw.hw_op_lock);
+       num = atomic_read(&dip_dev->dip_hw.num_composing);
+       mutex_unlock(&dip_dev->dip_hw.hw_op_lock);
+
+       return num;
+}
+
+static int __maybe_unused mtk_dip_suspend(struct device *dev)
+{
+       struct mtk_dip_dev *dip_dev = dev_get_drvdata(dev);
+       int ret, num;
+
+       if (pm_runtime_suspended(dev)) {
+               dev_dbg(dev, "%s: pm_runtime_suspended is true, no action\n",
+                       __func__);
+               return 0;
+       }
+
+       ret = wait_event_timeout(dip_dev->dip_hw.flushing_hw_wq,
+                                !(num = mtk_dip_get_scp_job_num(dip_dev)),
+                                msecs_to_jiffies(200));
+       if (!ret && num) {
+               dev_err(dev, "%s: flushing SCP job timeout, num(%d)\n",
+                       __func__, num);
+
+               return -EBUSY;
+       }
+
+       return mtk_dip_pm_suspend(dev);
+}
+
+static int __maybe_unused mtk_dip_resume(struct device *dev)
+{
+       if (pm_runtime_suspended(dev)) {
+               dev_dbg(dev, "%s: pm_runtime_suspended is true, no action\n",
+                       __func__);
+               return 0;
+       }
+
+       return mtk_dip_pm_resume(dev);
+}
+
+static const struct dev_pm_ops mtk_dip_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(mtk_dip_suspend, mtk_dip_resume)
+       SET_RUNTIME_PM_OPS(mtk_dip_suspend, mtk_dip_resume, NULL)
+};
+
+static const struct of_device_id mtk_dip_of_match[] = {
+       { .compatible = "mediatek,mt8183-dip", },
+       {}
+};
+MODULE_DEVICE_TABLE(of, mtk_dip_of_match);
+
+static struct platform_driver mtk_dip_driver = {
+       .probe   = mtk_dip_probe,
+       .remove  = mtk_dip_remove,
+       .driver  = {
+               .name = "camera-dip",
+               .pm = &mtk_dip_pm_ops,
+               .of_match_table = of_match_ptr(mtk_dip_of_match),
+       }
+};
+
+module_platform_driver(mtk_dip_driver);
+
+MODULE_AUTHOR("Frederic Chen <frederic.c...@mediatek.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Mediatek DIP driver");
+
-- 
2.18.0

Reply via email to