All of lore.kernel.org
 help / color / mirror / Atom feed
From: Frederic Chen <frederic.chen-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
To: hans.verkuil-FYB4Gu1CFyUAvxtiuMwx3w@public.gmane.org,
	laurent.pinchart+renesas-ryLnwIuWjnjg/C1BVhZhaw@public.gmane.org,
	tfiga-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org,
	matthias.bgg-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org,
	mchehab-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org
Cc: shik-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	Sean.Cheng-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org,
	Rynn.Wu-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org,
	srv_heupstream-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org,
	holmes.chiou-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org,
	suleiman-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org,
	Jerry-ch.Chen-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org,
	jungo.lin-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org,
	sj.huang-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org,
	yuzhao-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org,
	linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
	zwisler-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org,
	christie.yu-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org,
	frederic.chen-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
	linux-media-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Subject: [RFC PATCH V1 6/6] platform: mtk-isp: Add Mediatek DIP driver
Date: Wed, 17 Apr 2019 18:45:11 +0800	[thread overview]
Message-ID: <20190417104511.21514-7-frederic.chen@mediatek.com> (raw)
In-Reply-To: <20190417104511.21514-1-frederic.chen-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>

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.chen-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
---
 drivers/media/platform/mtk-isp/Makefile       |   18 +
 .../media/platform/mtk-isp/isp_50/Makefile    |   17 +
 .../platform/mtk-isp/isp_50/dip/Makefile      |   32 +
 .../mtk-isp/isp_50/dip/mtk_dip-ctrl.c         |  124 ++
 .../platform/mtk-isp/isp_50/dip/mtk_dip-dev.c | 1116 +++++++++++++
 .../platform/mtk-isp/isp_50/dip/mtk_dip-dev.h |  321 ++++
 .../platform/mtk-isp/isp_50/dip/mtk_dip-hw.h  |  167 ++
 .../mtk-isp/isp_50/dip/mtk_dip-smem.c         |  322 ++++
 .../mtk-isp/isp_50/dip/mtk_dip-smem.h         |   39 +
 .../platform/mtk-isp/isp_50/dip/mtk_dip-sys.c | 1384 +++++++++++++++++
 .../mtk-isp/isp_50/dip/mtk_dip-v4l2.c         | 1310 ++++++++++++++++
 11 files changed, 4850 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-ctrl.c
 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-smem.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-smem.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..24bc5354e2f6
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/Makefile
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2018 MediaTek Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+
+obj-$(CONFIG_VIDEO_MEDIATEK_ISP_COMMON) += common/
+
+obj-y += isp_50/
+
+obj-$(CONFIG_VIDEO_MEDIATEK_ISP_FD_SUPPORT) += fd/
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..fd0e5bd3c781
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/Makefile
@@ -0,0 +1,17 @@
+#
+# Copyright (C) 2018 MediaTek Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+
+ifeq ($(CONFIG_VIDEO_MEDIATEK_ISP_DIP_SUPPORT),y)
+obj-y += dip/
+endif
+
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..03137416857b
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/dip/Makefile
@@ -0,0 +1,32 @@
+#
+# Copyright (C) 2018 MediaTek Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+$(info $(srctree))
+ccflags-y += -I$(srctree)/drivers/media/platform/mtk-mdp3
+
+obj-y += mtk_dip-sys.o
+
+# To provide alloc context managing memory shared
+# between CPU and ISP coprocessor
+mtk_dip_smem-objs := \
+mtk_dip-smem.o
+
+obj-y += mtk_dip_smem.o
+
+# Utilits to provide frame-based streaming model
+# with v4l2 user interfaces
+mtk_dip_util-objs := \
+mtk_dip-dev.o \
+mtk_dip-v4l2.o \
+mtk_dip-ctrl.o \
+
+obj-y += mtk_dip_util.o
diff --git a/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-ctrl.c b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-ctrl.c
new file mode 100644
index 000000000000..e35574818120
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-ctrl.c
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Frederic Chen <frederic.chen-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include "mtk_dip-dev.h"
+
+static void handle_buf_usage_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 < MTK_DIP_V4l2_BUF_USAGE_DEFAULT ||
+	    ctrl->val >= MTK_DIP_V4l2_BUF_USAGE_NONE) {
+		pr_err("Invalid buffer usage id %d", ctrl->val);
+		return;
+	}
+	node->dev_q.buffer_usage = ctrl->val;
+}
+
+static void 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("Invalid buffer rotation %d", ctrl->val);
+		return;
+	}
+	node->dev_q.rotation = ctrl->val;
+}
+
+static int mtk_dip_video_device_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	switch (ctrl->id) {
+	case V4L2_CID_PRIVATE_SET_BUFFER_USAGE:
+		handle_buf_usage_config(ctrl);
+		break;
+	case V4L2_CID_ROTATE:
+		handle_buf_rotate_config(ctrl);
+		break;
+	default:
+			break;
+	}
+	return 0;
+}
+
+static const struct v4l2_ctrl_ops mtk_dip_video_device_ctrl_ops = {
+	.s_ctrl = mtk_dip_video_device_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config mtk_dip_buf_usage_config = {
+	.ops	= &mtk_dip_video_device_ctrl_ops,
+	.id	= V4L2_CID_PRIVATE_SET_BUFFER_USAGE,
+	.name	= "MTK ISP SET BUFFER USAGE",
+	.type	= V4L2_CTRL_TYPE_INTEGER,
+	.min	= MTK_DIP_V4l2_BUF_USAGE_DEFAULT,
+	.max	= MTK_DIP_V4l2_BUF_USAGE_POSTPROC,
+	.step	= 1,
+	.def	= MTK_DIP_V4l2_BUF_USAGE_DEFAULT,
+	.flags	= V4L2_CTRL_FLAG_SLIDER | V4L2_CTRL_FLAG_EXECUTE_ON_WRITE,
+	};
+
+int mtk_dip_ctrl_init(struct mtk_dip_pipe *dip_pipe)
+{
+	struct v4l2_ctrl_handler *hdl = &dip_pipe->ctrl_handler;
+	struct mtk_dip_video_device *node;
+	int i;
+	int img_nodes_to_be_init[3] = {
+		MTK_DIP_VIDEO_NODE_ID_RAW_OUT,
+		MTK_DIP_VIDEO_NODE_ID_MDP0_CAPTURE,
+		MTK_DIP_VIDEO_NODE_ID_MDP1_CAPTURE
+	};
+
+	v4l2_ctrl_handler_init(hdl, V4L2_CID_MTK_DIP_MAX);
+
+	pr_debug("%s init ctrl: %p\n", __func__, hdl);
+
+	if (hdl->error) {
+		pr_err("Failed in v4l2_ctrl_handler_init\n");
+		return hdl->error;
+	}
+
+	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);
+
+	for (i = 0; i < ARRAY_SIZE(img_nodes_to_be_init); i++) {
+		node = &dip_pipe->nodes[img_nodes_to_be_init[i]];
+
+		if (v4l2_ctrl_new_custom(&node->ctrl_handler,
+					 &mtk_dip_buf_usage_config,
+					 NULL) == NULL)
+			dev_err(&dip_pipe->dip_dev->pdev->dev,
+				"Node(%s) create buf_usage_config ctrl failed:(%d)",
+				node->desc->name,
+				node->ctrl_handler.error);
+
+		if (v4l2_ctrl_new_std(&dip_pipe->ctrl_handler,
+				      &mtk_dip_video_device_ctrl_ops,
+			V4L2_CID_ROTATE, 0, 270, 90, 0)	== NULL)
+			dev_err(&dip_pipe->dip_dev->pdev->dev,
+				"Node(%s) create rotate ctrl failed:(%d)",
+				node->desc->name, node->ctrl_handler.error);
+	}
+
+return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_dip_ctrl_init);
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..9f450dae2820
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-dev.c
@@ -0,0 +1,1116 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Frederic Chen <frederic.chen-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <media/videobuf2-dma-contig.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <media/videobuf2-dma-contig.h>
+#include <linux/dma-mapping.h>
+#include <media/v4l2-event.h>
+#include "mtk_dip-dev.h"
+#include "mtk_dip-smem.h"
+#include "mtk-mdp3-regs.h"
+#include "mtk-img-ipi.h"
+
+int mtk_dip_pipe_init(struct mtk_dip_pipe *dip_pipe,
+		      struct mtk_dip_dev *dip_dev,
+		      struct mtk_dip_pipe_desc *setting,
+		      struct media_device *media_dev,
+		      struct v4l2_device *v4l2_dev,
+		      struct mtk_dip_smem_dev *smem_alloc_dev)
+{
+	int ret, i;
+
+	dip_pipe->dip_dev = dip_dev;
+	dip_pipe->desc = setting;
+	dip_pipe->smem_alloc_dev = smem_alloc_dev;
+
+	atomic_set(&dip_pipe->pipe_job_sequence, 0);
+	spin_lock_init(&dip_pipe->job_lock);
+	mutex_init(&dip_pipe->lock);
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev, "init pipe(%s,%d)\n",
+		dip_pipe->desc->name,
+		dip_pipe->desc->id);
+
+	dip_pipe->num_nodes = MTK_DIP_VIDEO_NODE_ID_TOTAL_NUM;
+
+	for (i = 0; i < MTK_DIP_VIDEO_NODE_ID_OUT_TOTAL_NUM; i++) {
+		dip_pipe->nodes[i].desc =
+			&dip_pipe->desc->output_queue_descs[i];
+		dip_pipe->nodes[i].immutable = 0;
+		dip_pipe->nodes[i].enabled =
+			dip_pipe->nodes[i].desc->default_enable;
+		atomic_set(&dip_pipe->nodes[i].sequence, 0);
+
+		dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+			"%s: init node(%s,%d)\n",
+			dip_pipe->desc->name,
+			dip_pipe->nodes[i].desc->name, i);
+	}
+
+	for (i = MTK_DIP_VIDEO_NODE_ID_OUT_TOTAL_NUM;
+	     i < MTK_DIP_VIDEO_NODE_ID_TOTAL_NUM; i++) {
+		int cap_idx = i - MTK_DIP_VIDEO_NODE_ID_OUT_TOTAL_NUM;
+
+		dip_pipe->nodes[i].desc =
+			&dip_pipe->desc->capture_queue_descs[cap_idx];
+		dip_pipe->nodes[i].immutable = 0;
+		dip_pipe->nodes[i].enabled =
+			dip_pipe->nodes[i].desc->default_enable;
+		atomic_set(&dip_pipe->nodes[i].sequence, 0);
+
+		dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+			"%s: init node(%s,%d)\n",
+			dip_pipe->desc->name,
+			dip_pipe->nodes[i].desc->name, i);
+	}
+
+	if (dip_pipe->desc->master >= 0 &&
+	    dip_pipe->desc->master < MTK_DIP_VIDEO_NODE_ID_TOTAL_NUM) {
+		dip_pipe->nodes[dip_pipe->desc->master].immutable = 1;
+		dip_pipe->nodes[dip_pipe->desc->master].enabled = 1;
+	}
+
+	ret = mtk_dip_ctrl_init(dip_pipe);
+
+	if (ret) {
+		dev_err(&dip_pipe->dip_dev->pdev->dev,
+			"%s: failed(%d) to initialize ctrls\n",
+			dip_pipe->desc->name, ret);
+		goto failed_ctrl;
+	}
+
+	ret = mtk_dip_pipe_v4l2_register(dip_pipe, media_dev, v4l2_dev);
+
+	if (ret) {
+		dev_err(&dip_pipe->dip_dev->pdev->dev,
+			"%s: failed(%d) to create V4L2 devices\n",
+			dip_pipe->desc->name, ret);
+		goto failed_pipe;
+	}
+
+	return 0;
+
+failed_ctrl:
+failed_pipe:
+	mutex_destroy(&dip_pipe->lock);
+	return ret;
+}
+
+static int mtk_dip_pipe_next_job_id(struct mtk_dip_pipe *dip_pipe)
+{
+	int global_job_id =
+		atomic_inc_return(&dip_pipe->pipe_job_sequence);
+
+	global_job_id =
+		(global_job_id & 0x0000FFFF) |
+		(dip_pipe->desc->id << 16);
+
+	return global_job_id;
+}
+
+int mtk_dip_pipe_init_job_infos(struct mtk_dip_pipe *dip_pipe)
+{
+	int i;
+
+	spin_lock(&dip_pipe->job_lock);
+
+	dip_pipe->num_pipe_job_infos = ARRAY_SIZE(dip_pipe->pipe_job_infos);
+	INIT_LIST_HEAD(&dip_pipe->pipe_job_running_list);
+	INIT_LIST_HEAD(&dip_pipe->pipe_job_free_list);
+
+	for (i = 0; i < dip_pipe->num_pipe_job_infos; i++) {
+		struct mtk_dip_pipe_job_info *pipe_job_info =
+			&dip_pipe->pipe_job_infos[i];
+		list_add_tail(&pipe_job_info->list,
+			      &dip_pipe->pipe_job_free_list);
+	}
+
+	spin_unlock(&dip_pipe->job_lock);
+
+	return 0;
+}
+
+static int mtk_dip_pipe_process_pipe_job_info(struct mtk_dip_pipe *dip_pipe,
+					      struct mtk_dip_pipe_job_info
+					      *pipe_job_info)
+{
+	spin_lock(&dip_pipe->job_lock);
+
+	list_del(&pipe_job_info->list);
+	list_add_tail(&pipe_job_info->list, &dip_pipe->pipe_job_running_list);
+
+	spin_unlock(&dip_pipe->job_lock);
+	return 0;
+}
+
+struct mtk_dip_pipe_job_info *
+mtk_dip_pipe_get_running_job_info(struct mtk_dip_pipe *dip_pipe,
+				  int pipe_job_id)
+{
+	struct mtk_dip_pipe_job_info *pipe_job_info = NULL;
+
+	spin_lock(&dip_pipe->job_lock);
+
+	list_for_each_entry(pipe_job_info,
+			    &dip_pipe->pipe_job_running_list, list) {
+		if (pipe_job_info->id == pipe_job_id) {
+			spin_unlock(&dip_pipe->job_lock);
+			return pipe_job_info;
+		}
+	}
+
+	spin_unlock(&dip_pipe->job_lock);
+
+	return NULL;
+}
+
+static int
+mtk_dip_pipe_free_job_info(struct mtk_dip_pipe *dip_pipe,
+			   struct mtk_dip_pipe_job_info *pipe_job_info)
+{
+	spin_lock(&dip_pipe->job_lock);
+
+	list_del(&pipe_job_info->list);
+	list_add_tail(&pipe_job_info->list, &dip_pipe->pipe_job_free_list);
+
+	spin_unlock(&dip_pipe->job_lock);
+
+	return 0;
+}
+
+static struct mtk_dip_pipe_job_info *
+mtk_dip_pipe_get_free_job_info(struct mtk_dip_pipe *dip_pipe)
+{
+	struct mtk_dip_pipe_job_info *pipe_job_info = NULL;
+
+	spin_lock(&dip_pipe->job_lock);
+	list_for_each_entry(pipe_job_info,
+			    &dip_pipe->pipe_job_free_list, list) {
+		dev_dbg(&dip_pipe->dip_dev->pdev->dev, "Found free pipe job\n");
+		spin_unlock(&dip_pipe->job_lock);
+		return pipe_job_info;
+	}
+	spin_unlock(&dip_pipe->job_lock);
+
+	dev_err(&dip_pipe->dip_dev->pdev->dev,
+		"%s: can't found free pipe job\n",
+		dip_pipe->desc->name);
+
+	return NULL;
+}
+
+static void
+mtk_dip_pipe_update_job_info(struct mtk_dip_pipe *dip_pipe,
+			     struct mtk_dip_pipe_job_info *pipe_job_info,
+			     struct mtk_dip_video_device *node,
+			     struct mtk_dip_dev_buffer *dev_buf)
+{
+	if (!pipe_job_info || !dev_buf || !node) {
+		dev_err(&dip_pipe->dip_dev->pdev->dev,
+			"%s: update pipe-job(%p) failed, buf(%p),node(%p)\n",
+			dip_pipe->desc->name,
+			pipe_job_info, dev_buf, node);
+		return;
+	}
+
+	if (pipe_job_info->buf_map[node->desc->id])
+		dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+			"%s:%s: buf overwrite\n",
+			 dip_pipe->desc->name,
+			 node->desc->name);
+
+	if (node->desc->buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+		pipe_job_info->num_img_capture_bufs++;
+
+	if (node->desc->buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+		pipe_job_info->num_img_output_bufs++;
+
+	if (node->desc->buf_type == V4L2_BUF_TYPE_META_OUTPUT)
+		pipe_job_info->num_meta_output_bufs++;
+
+	if (node->desc->buf_type == V4L2_BUF_TYPE_META_CAPTURE)
+		pipe_job_info->num_meta_capture_bufs++;
+
+	pipe_job_info->buf_map[node->desc->id] = dev_buf;
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"%s:%s: added buf(%p) to pipe-job(%p)\n",
+		dip_pipe->desc->name, node->desc->name, dev_buf,
+		pipe_job_info);
+}
+
+static void mtk_dip_pipe_debug_job(struct mtk_dip_pipe *dip_pipe,
+				   struct mtk_dip_pipe_job_info *pipe_job_info)
+{
+	int i;
+
+	if (!dip_pipe || !pipe_job_info)
+		return;
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"%s: pipe-job(%p),id(%d),req(%p)buf nums(%d,%d,%d,%d)\n",
+		dip_pipe->desc->name,
+		pipe_job_info,
+		pipe_job_info->id,
+		pipe_job_info->req,
+		pipe_job_info->num_img_capture_bufs,
+		pipe_job_info->num_img_output_bufs,
+		pipe_job_info->num_meta_capture_bufs,
+		pipe_job_info->num_meta_output_bufs);
+
+	for (i = 0; i < MTK_DIP_VIDEO_NODE_ID_TOTAL_NUM ; i++) {
+		if (pipe_job_info->buf_map[i])
+			dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+				"Node(%s,%d), buf(%p)\n",
+				dip_pipe->nodes[i].desc->name, i,
+				pipe_job_info->buf_map[i]);
+	}
+}
+
+int mtk_dip_pipe_job_finish(struct mtk_dip_pipe *dip_pipe,
+			    unsigned int pipe_job_info_id,
+			    enum vb2_buffer_state vbf_state)
+{
+	int i;
+	struct mtk_dip_pipe_job_info *job_info = NULL;
+	const int pipe_id =
+		mtk_dip_pipe_get_pipe_from_job_id(pipe_job_info_id);
+	u64 timestamp = 0;
+
+	if (!dip_pipe)
+		pr_err("%s: pipe-job id(%d) release failed, dip_pipe is null\n",
+		       __func__, pipe_job_info_id);
+
+	job_info = mtk_dip_pipe_get_running_job_info(dip_pipe,
+						     pipe_job_info_id);
+
+	if (!job_info) {
+		dev_err(&dip_pipe->dip_dev->pdev->dev,
+			"%s:%s: can't find pipe-job id(%d)\n",
+			__func__, dip_pipe->desc->name, pipe_id);
+		return -EINVAL;
+	}
+
+	timestamp = ktime_get_ns();
+
+	for (i = 0; i < MTK_DIP_VIDEO_NODE_ID_TOTAL_NUM; i++) {
+		struct mtk_dip_dev_buffer *dev_buf = job_info->buf_map[i];
+
+		if (!dev_buf) {
+			continue;
+		} else {
+			dev_buf->vbb.vb2_buf.timestamp = ktime_get_ns();
+			mtk_dip_v4l2_buffer_done(&dev_buf->vbb.vb2_buf,
+						 vbf_state);
+		}
+	}
+
+	mtk_dip_pipe_free_job_info(dip_pipe, job_info);
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"%s:%s: finish pipe-job, id(%d), vb state(%d)\n",
+		__func__, dip_pipe->desc->name, pipe_id,
+		pipe_job_info_id, vbf_state);
+
+	return 0;
+}
+
+static void mtk_dip_dev_buf_fill_info(struct mtk_dip_pipe *dip_pipe,
+				      struct mtk_dip_dev_buffer *dev_buf)
+{
+	struct vb2_v4l2_buffer *b;
+	struct mtk_dip_video_device *node;
+	struct mtk_dip_video_device_desc *desc;
+
+	b = &dev_buf->vbb;
+	node = mtk_dip_vbq_to_node(b->vb2_buf.vb2_queue);
+	desc = node->desc;
+	dev_buf->fmt = node->vdev_fmt;
+	dev_buf->dev_fmt = node->dev_q.dev_fmt;
+	dev_buf->isp_daddr =
+		vb2_dma_contig_plane_dma_addr(&b->vb2_buf, 0);
+	dev_buf->vaddr = vb2_plane_vaddr(&b->vb2_buf, 0);
+	dev_buf->buffer_usage = node->dev_q.buffer_usage;
+	dev_buf->rotation = node->dev_q.rotation;
+
+	if (desc->smem_alloc) {
+		dev_buf->scp_daddr =
+			mtk_dip_smem_iova_to_phys
+			(dip_pipe->smem_alloc_dev,
+			 dev_buf->isp_daddr);
+	} else {
+		dev_buf->scp_daddr = 0;
+	}
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"%s:%s: buf type(%d), idx(%d), mem(%d), isp_daddr(%p), scp_daddr(%p)\n",
+		dip_pipe->desc->name,
+		desc->name,
+		b->vb2_buf.type,
+		b->vb2_buf.index,
+		b->vb2_buf.memory,
+		dev_buf->isp_daddr,
+		dev_buf->scp_daddr);
+}
+
+int mtk_dip_pipe_queue_buffers(struct media_request *req,
+			       int initial)
+{
+	struct media_request_object *obj;
+	struct mtk_dip_pipe *dip_pipe;
+	struct mtk_dip_pipe_job_info *pipe_job_info = NULL;
+	int ret = 0;
+
+	list_for_each_entry(obj, &req->objects, list) {
+		struct vb2_buffer *vb;
+
+		if (vb2_request_object_is_buffer(obj)) {
+			struct mtk_dip_dev_buffer *buf;
+			struct mtk_dip_dev_buffer *dev_buf;
+			struct mtk_dip_video_device *node;
+
+			vb = container_of(obj, struct vb2_buffer, req_obj);
+			node = mtk_dip_vbq_to_node(vb->vb2_queue);
+			dip_pipe = vb2_get_drv_priv(vb->vb2_queue);
+			dev_buf = mtk_dip_vb2_buf_to_dev_buf(vb);
+			buf = dev_buf;
+
+			if (!pipe_job_info) {
+				pipe_job_info = mtk_dip_pipe_get_free_job_info
+					(dip_pipe);
+
+				if (!pipe_job_info)
+					goto FAILE_JOB_NOT_TRIGGER;
+
+				memset(pipe_job_info->buf_map, 0,
+				       sizeof(pipe_job_info->buf_map));
+				pipe_job_info->num_img_capture_bufs = 0;
+				pipe_job_info->num_img_output_bufs = 0;
+				pipe_job_info->num_meta_capture_bufs = 0;
+				pipe_job_info->num_meta_output_bufs = 0;
+			}
+
+			mtk_dip_dev_buf_fill_info(dip_pipe,
+						  buf);
+
+			mtk_dip_pipe_update_job_info(dip_pipe,
+						     pipe_job_info,
+						     node,
+						     buf);
+		}
+	}
+
+	if (!pipe_job_info)
+		return -EINVAL;
+
+	pipe_job_info->id =
+		mtk_dip_pipe_next_job_id(dip_pipe);
+
+	mtk_dip_pipe_debug_job(dip_pipe, pipe_job_info);
+
+	mutex_lock(&dip_pipe->lock);
+
+	if (!dip_pipe->streaming) {
+		dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+			"%s:%s:  stream is off, no hw enqueue triggered\n",
+			__func__, dip_pipe->desc->name);
+		mutex_unlock(&dip_pipe->lock);
+		return 0;
+	}
+
+	if (mtk_dip_pipe_process_pipe_job_info(dip_pipe, pipe_job_info)) {
+		dev_err(&dip_pipe->dip_dev->pdev->dev,
+			"%s:%s: can't start to run pipe job id(%d)\n",
+			__func__, dip_pipe->desc->name,
+			pipe_job_info->id);
+		mutex_unlock(&dip_pipe->lock);
+		goto FAILE_JOB_NOT_TRIGGER;
+	}
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"%s: trigger pipe job, id(%d)\n",
+		dip_pipe->desc->name,
+		dip_pipe->desc->id);
+
+	if (mtk_dip_pipe_job_start(dip_pipe, pipe_job_info)) {
+		mutex_unlock(&dip_pipe->lock);
+		goto FAILE_JOB_NOT_TRIGGER;
+	}
+
+	mutex_unlock(&dip_pipe->lock);
+
+	return 0;
+
+FAILE_JOB_NOT_TRIGGER:
+	if (initial)
+		return ret;
+
+	mtk_dip_pipe_job_finish(dip_pipe, pipe_job_info->id,
+				VB2_BUF_STATE_ERROR);
+
+	return -EINVAL;
+}
+
+int mtk_dip_pipe_release(struct mtk_dip_pipe *dip_pipe)
+{
+	mtk_dip_pipe_v4l2_unregister(dip_pipe);
+	v4l2_ctrl_handler_free(&dip_pipe->ctrl_handler);
+	mutex_destroy(&dip_pipe->lock);
+
+	return 0;
+}
+
+static void set_img_fmt(struct v4l2_pix_format_mplane *mfmt_to_fill,
+			struct mtk_dip_dev_format *dev_fmt)
+{
+	int i;
+
+	mfmt_to_fill->pixelformat = dev_fmt->fmt.img.pixelformat;
+	mfmt_to_fill->num_planes = dev_fmt->fmt.img.num_planes;
+	mfmt_to_fill->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+	mfmt_to_fill->quantization = V4L2_QUANTIZATION_DEFAULT;
+	mfmt_to_fill->colorspace = dev_fmt->fmt.img.colorspace;
+
+	memset(mfmt_to_fill->reserved, 0, sizeof(mfmt_to_fill->reserved));
+
+	pr_debug("%s: Fmt(%d),w(%d),h(%d),f(%d)\n",
+		 __func__,
+		 mfmt_to_fill->pixelformat,
+		 mfmt_to_fill->width,
+		 mfmt_to_fill->height,
+		 mfmt_to_fill->field);
+
+	for (i = 0 ; i < mfmt_to_fill->num_planes; ++i) {
+		int bpl = (mfmt_to_fill->width *
+			dev_fmt->fmt.img.row_depth[i]) / 8;
+		int sizeimage = (mfmt_to_fill->width * mfmt_to_fill->height *
+			dev_fmt->fmt.img.depth[i]) / 8;
+
+		mfmt_to_fill->plane_fmt[i].bytesperline = bpl;
+		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));
+
+		pr_debug("plane(%d):bpl(%d),sizeimage(%u)\n",
+			 i, bpl,
+			 mfmt_to_fill->plane_fmt[i].sizeimage);
+	}
+}
+
+static void set_meta_fmt(struct v4l2_meta_format *metafmt_to_fill,
+			 struct mtk_dip_dev_format *dev_fmt)
+{
+	metafmt_to_fill->dataformat = dev_fmt->fmt.meta.dataformat;
+
+	if (dev_fmt->fmt.meta.max_buffer_size <= 0) {
+		pr_debug("Invalid meta buf size(%u), use default(%u)\n",
+			 dev_fmt->fmt.meta.max_buffer_size,
+			 MTK_DIP_DEV_META_BUF_DEFAULT_SIZE);
+		metafmt_to_fill->buffersize = MTK_DIP_DEV_META_BUF_DEFAULT_SIZE;
+	} else {
+		pr_debug("Use meta size(%u)\n",
+			 dev_fmt->fmt.meta.max_buffer_size);
+		metafmt_to_fill->buffersize = dev_fmt->fmt.meta.max_buffer_size;
+	}
+}
+
+void mtk_dip_pipe_load_default_fmt(struct mtk_dip_pipe *dip_pipe,
+				   struct mtk_dip_video_device *node,
+				   struct v4l2_format *fmt_to_fill)
+{
+	struct mtk_dip_dev_format *dev_fmt;
+	struct mtk_dip_video_device_desc *desc = node->desc;
+
+	if (desc->num_fmts == 0) {
+		pr_err("%s:%s: desc->num_fmts is 0, no format support list\n",
+		       __func__, desc->name);
+		return;
+	}
+
+	if (desc->default_fmt_idx >= desc->num_fmts) {
+		pr_debug("%s:%s: invalid idx(%d), must < num_fmts(%d)\n",
+			 __func__, desc->name, desc->default_fmt_idx,
+			desc->num_fmts);
+		desc->default_fmt_idx = 0;
+	}
+
+	dev_fmt	= &desc->fmts[desc->default_fmt_idx];
+	fmt_to_fill->type = desc->buf_type;
+	if (mtk_dip_buf_is_meta(desc->buf_type)) {
+		set_meta_fmt(&fmt_to_fill->fmt.meta, dev_fmt);
+	} else {
+		fmt_to_fill->fmt.pix_mp.width = desc->default_width;
+		fmt_to_fill->fmt.pix_mp.height = desc->default_height;
+		fmt_to_fill->fmt.pix_mp.field = V4L2_FIELD_NONE;
+
+		set_img_fmt(&fmt_to_fill->fmt.pix_mp, dev_fmt);
+	}
+}
+
+struct mtk_dip_dev_format *
+mtk_dip_pipe_find_fmt(struct mtk_dip_pipe *dip_pipe,
+		      struct mtk_dip_video_device *node,
+		      u32 format)
+{
+	int i;
+	struct mtk_dip_dev_format *dev_fmt;
+
+	struct mtk_dip_video_device_desc *desc = node->desc;
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev, "fmt to find(%x)\n", format);
+
+	for (i = 0; i < desc->num_fmts; i++) {
+		dev_fmt = &desc->fmts[i];
+		if (!mtk_dip_buf_is_meta(desc->buf_type)) {
+			if (dev_fmt->fmt.img.pixelformat == format)
+				return dev_fmt;
+		} else {
+			if (dev_fmt->fmt.meta.dataformat == format)
+				return dev_fmt;
+		}
+	}
+
+	return NULL;
+}
+
+int mtk_dip_pipe_set_meta_fmt(struct mtk_dip_pipe *dip_pipe,
+			      struct mtk_dip_video_device *node,
+			      struct v4l2_meta_format *user_fmt,
+			      struct v4l2_meta_format *node_fmt)
+{
+	struct mtk_dip_dev_format *dev_fmt;
+
+	if (!user_fmt || !node_fmt)
+		return -EINVAL;
+
+	dev_fmt = mtk_dip_pipe_find_fmt(dip_pipe, node,
+					user_fmt->dataformat);
+
+	if (!dev_fmt)
+		return -EINVAL;
+
+	node->dev_q.dev_fmt = dev_fmt;
+	set_meta_fmt(node_fmt, dev_fmt);
+	*user_fmt = *node_fmt;
+
+	return 0;
+}
+
+int mtk_dip_pipe_set_img_fmt(struct mtk_dip_pipe *dip_pipe,
+			     struct mtk_dip_video_device *node,
+			     struct v4l2_pix_format_mplane *user_fmt,
+			     struct v4l2_pix_format_mplane *dest_fmt)
+{
+	struct mtk_dip_dev_format *dev_fmt;
+
+	if (!user_fmt || !dest_fmt)
+		return -EINVAL;
+
+	dev_fmt = mtk_dip_pipe_find_fmt(dip_pipe, node,
+					user_fmt->pixelformat);
+
+	if (!dev_fmt) {
+		pr_debug("%s:%s:%s: dev_fmt(%d) not found\n",
+			 __func__, dip_pipe->desc->name,
+			 node->desc->name, user_fmt->pixelformat);
+		return -EINVAL;
+	}
+
+	node->dev_q.dev_fmt = dev_fmt;
+	dest_fmt->width = user_fmt->width;
+	dest_fmt->height = user_fmt->height;
+	dest_fmt->field = V4L2_FIELD_NONE;
+
+	set_img_fmt(dest_fmt, dev_fmt);
+
+	return 0;
+}
+
+int mtk_dip_pipe_streamon(struct mtk_dip_pipe *dip_pipe)
+{
+	int ret;
+	struct mtk_dip_dev *dip_dev;
+
+	if (!dip_pipe)
+		return -EINVAL;
+
+	dip_dev = dev_get_drvdata(&dip_pipe->dip_dev->pdev->dev);
+
+	mutex_lock(&dip_pipe->lock);
+
+	ret = mtk_dip_hw_streamon(&dip_dev->dip_hw,
+				  dip_pipe->desc->id);
+
+	if (ret) {
+		dev_err(&dip_pipe->dip_dev->pdev->dev,
+			"%s:%s:%d: failed to start hw\n",
+			__func__, dip_pipe->desc->name,
+			dip_pipe->desc->id);
+		mutex_unlock(&dip_pipe->lock);
+		return -EBUSY;
+	}
+
+	dip_pipe->streaming = 1;
+	mutex_unlock(&dip_pipe->lock);
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"%s:%s:%d: start hw\n",
+		__func__, dip_pipe->desc->name,
+		dip_pipe->desc->id);
+
+	return ret;
+}
+
+int mtk_dip_pipe_streamoff(struct mtk_dip_pipe *dip_pipe)
+{
+	int ret;
+	struct mtk_dip_dev *dip_dev;
+
+	if (!dip_pipe)
+		return -EINVAL;
+
+	dip_dev = dev_get_drvdata(&dip_pipe->dip_dev->pdev->dev);
+
+	mutex_lock(&dip_pipe->lock);
+
+	ret = mtk_dip_hw_streamoff(&dip_dev->dip_hw,
+				   dip_pipe->desc->id);
+
+	if (ret) {
+		dev_err(&dip_pipe->dip_dev->pdev->dev,
+			"%s:%s:%d: failed to stop hw\n",
+			__func__, dip_pipe->desc->name,
+			dip_pipe->desc->id);
+		mutex_unlock(&dip_pipe->lock);
+		return -EBUSY;
+	}
+
+	dip_pipe->streaming = 0;
+	mutex_unlock(&dip_pipe->lock);
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"%s:%s:%d: stop hw\n",
+		__func__, dip_pipe->desc->name,
+		dip_pipe->desc->id);
+
+	return 0;
+}
+
+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;
+}
+
+/* Stride that is accepted by MDP HW */
+static u32 dip_mdp_fmt_get_stride(const struct mtk_dip_dev_mdp_format *fmt,
+				  u32 bytesperline,
+				  unsigned int plane)
+{
+	enum mdp_color c = fmt->mdp_color;
+	u32 stride;
+
+	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_mdp_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_mdp_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 is_contig_mp_buffer(struct mtk_dip_dev_buffer *dev_buf)
+{
+	if (MDP_COLOR_GET_PLANE_COUNT(dev_buf->dev_fmt->fmt.img.mdp_color)
+	    == 1)
+		return 0;
+	else
+		return 1;
+}
+
+static int fill_ipi_img_param_mp(struct mtk_dip_pipe *dip_pipe,
+				 struct img_image_buffer *b,
+				 struct mtk_dip_dev_buffer *dev_buf,
+				 char *buf_name)
+{
+	struct v4l2_pix_format_mplane *pix_mp;
+	struct mtk_dip_dev_mdp_format *mdp_fmt;
+	unsigned int i;
+	unsigned int total_plane_size = 0;
+
+	if (!dev_buf || !dev_buf->dev_fmt) {
+		dev_err(&dip_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->fmt.img;
+
+	b->format.colorformat = dev_buf->dev_fmt->fmt.img.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->fmt.img.mdp_color);
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"%s: buf(%s), IPI: w(%d),h(%d),c(0x%x)\n",
+		dip_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 =
+			dip_mdp_fmt_get_stride
+			(mdp_fmt, pix_mp->plane_fmt[i].bytesperline, 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] = dev_buf->isp_daddr;
+		dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+			"Contiguous-mp-buf:plane(%i),stride(%d),size(%d),iova(%p)",
+			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;
+	}
+
+	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(&dip_pipe->dip_dev->pdev->dev,
+			"Contiguous-mp-buf:plane(%i),stride(%d),size(%d),iova(%p)",
+			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;
+	}
+
+	b->usage = dev_buf->buffer_usage;
+
+	dev_dbg(&dip_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_ipi_img_param(struct mtk_dip_pipe *dip_pipe,
+			      struct img_image_buffer *img,
+			      struct mtk_dip_dev_buffer *dev_buf,
+			      char *buf_name)
+{
+	img->format.width = dev_buf->fmt.fmt.pix_mp.width;
+	img->format.height = dev_buf->fmt.fmt.pix_mp.height;
+
+	if (dev_buf && dev_buf->dev_fmt) {
+		img->format.colorformat =
+			dev_buf->dev_fmt->fmt.img.mdp_color;
+	} else {
+		dev_err(&dip_pipe->dip_dev->pdev->dev,
+			"%s: %s's dev format not set\n",
+			__func__, buf_name);
+		return -EINVAL;
+	}
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"%s: buf(%s) IPI: w(%d),h(%d),c(0x%x)\n",
+		dip_pipe->desc->name,
+		buf_name,
+		img->format.width,
+		img->format.height,
+		img->format.colorformat);
+
+	img->format.plane_fmt[0].size =
+		dev_buf->fmt.fmt.pix_mp.plane_fmt[0].sizeimage;
+	img->format.plane_fmt[0].stride =
+		dev_buf->fmt.fmt.pix_mp.plane_fmt[0].bytesperline;
+	img->iova[0] = dev_buf->isp_daddr;
+	img->usage = dev_buf->buffer_usage;
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"size(%d), stride(%d),ycbcr(%d),iova(%p),u(%d)\n",
+		img->format.plane_fmt[0].size,
+		img->format.plane_fmt[0].stride,
+		img->format.ycbcr_prof,
+		img->iova[0],
+		img->usage);
+
+	return 0;
+}
+
+static int fill_input_ipi_param(struct mtk_dip_pipe *dip_pipe,
+				struct img_input *iin,
+				struct mtk_dip_dev_buffer *dev_buf,
+				char *buf_name)
+{
+	struct img_image_buffer *img = &iin->buffer;
+
+	/* Will map the vale with V4L2 color space in the future */
+	img->format.ycbcr_prof = 1;
+	if (is_contig_mp_buffer(dev_buf))
+		return fill_ipi_img_param_mp(dip_pipe, img, dev_buf,
+					     buf_name);
+	else
+		return fill_ipi_img_param(dip_pipe, img, dev_buf,
+					  buf_name);
+}
+
+static int fill_output_ipi_param(struct mtk_dip_pipe *dip_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;
+
+	img->format.ycbcr_prof = 0;
+
+	if (is_contig_mp_buffer(dev_buf_out))
+		ret = fill_ipi_img_param_mp(dip_pipe, img, dev_buf_out,
+					    buf_name);
+	else
+		ret = fill_ipi_img_param(dip_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(&dip_pipe->dip_dev->pdev->dev,
+		"%s: buf(%s) IPI-ext:c_l(%d),c_t(%d),c_w(%d),c_h(%d)\n",
+		dip_pipe->desc->name,
+		buf_name,
+		iout->crop.left,
+		iout->crop.top,
+		iout->crop.width,
+		iout->crop.height);
+
+	dev_dbg(&dip_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;
+}
+
+int mtk_dip_pipe_job_start(struct mtk_dip_pipe *dip_pipe,
+			   struct mtk_dip_pipe_job_info *pipe_job_info)
+{
+	struct platform_device *pdev = dip_pipe->dip_dev->pdev;
+	int ret;
+	int out_img_buf_idx;
+	struct img_ipi_frameparam dip_param;
+	struct mtk_dip_dev_buffer *dev_buf_in;
+	struct mtk_dip_dev_buffer *dev_buf_out;
+	struct mtk_dip_dev_buffer *dev_buf_tuning;
+
+	if (!pipe_job_info) {
+		dev_err(&pdev->dev,
+			"pipe_job_info(%p) in start can't be NULL\n",
+			pipe_job_info);
+		return -EINVAL;
+	}
+
+	/* We need RAW and at least MDP0 or MDP 1 buffer */
+	if (!pipe_job_info->buf_map[MTK_DIP_VIDEO_NODE_ID_RAW_OUT] ||
+	    (!pipe_job_info->buf_map[MTK_DIP_VIDEO_NODE_ID_MDP0_CAPTURE] &&
+		 !pipe_job_info->buf_map[MTK_DIP_VIDEO_NODE_ID_MDP1_CAPTURE])){
+		struct mtk_dip_dev_buffer **map = pipe_job_info->buf_map;
+
+		dev_dbg(&pdev->dev,
+			"can't trigger 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]);
+		return -EINVAL;
+	}
+
+	dev_dbg(&pdev->dev,
+		"%s:%s: pipe-job id(%d)\n",
+		__func__, dip_pipe->desc->name,
+		pipe_job_info->id);
+
+	/* Fill ipi params for DIP driver */
+	memset(&dip_param, 0, sizeof(struct img_ipi_frameparam));
+
+	dip_param.index = pipe_job_info->id;
+	dip_param.num_outputs = pipe_job_info->num_img_capture_bufs;
+	dip_param.num_inputs = pipe_job_info->num_img_output_bufs;
+	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: pa(%p),va(%p),iova(%p)\n",
+			dev_buf_tuning->scp_daddr,
+			dev_buf_tuning->vaddr,
+			dev_buf_tuning->isp_daddr);
+		dip_param.tuning_data.pa = (uint32_t)dev_buf_tuning->scp_daddr;
+		dip_param.tuning_data.va = (uint64_t)dev_buf_tuning->vaddr;
+		dip_param.tuning_data.iova =
+			(uint32_t)dev_buf_tuning->isp_daddr;
+	} else {
+		dev_dbg(&pdev->dev,
+			"Doesn't enqueued tuning buffer, by-pass\n");
+	dip_param.tuning_data.pa = 0;
+	dip_param.tuning_data.va = 0;
+	dip_param.tuning_data.iova = 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[0];
+
+		fill_input_ipi_param(dip_pipe, iin, dev_buf_in, "RAW");
+	}
+
+	out_img_buf_idx = 0;
+
+	/* MDP 0 buffer */
+	dev_buf_out =
+		pipe_job_info->buf_map[MTK_DIP_VIDEO_NODE_ID_MDP0_CAPTURE];
+	if (dev_buf_out) {
+		struct img_output *iout = &dip_param.outputs[out_img_buf_idx];
+
+		fill_output_ipi_param(dip_pipe, iout, dev_buf_out,
+				      dev_buf_in, "MDP0");
+		out_img_buf_idx++;
+	}
+
+	/* MDP 1 buffer */
+	dev_buf_out =
+		pipe_job_info->buf_map[MTK_DIP_VIDEO_NODE_ID_MDP1_CAPTURE];
+	if (dev_buf_out) {
+		struct img_output *iout = &dip_param.outputs[out_img_buf_idx];
+
+		fill_output_ipi_param(dip_pipe, iout, dev_buf_out,
+				      dev_buf_in, "MDP1");
+		out_img_buf_idx++;
+	}
+
+	ret = mtk_dip_hw_enqueue(&dip_pipe->dip_dev->dip_hw, &dip_param);
+
+	if (ret) {
+		dev_dbg(&pdev->dev,
+			"%s:%s: enqueue to HW failed(%d)\n",
+			 __func__, dip_pipe->desc->name, ret);
+		return -EBUSY;
+	}
+
+	return ret;
+}
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..f51f7a44379a
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-dev.h
@@ -0,0 +1,321 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Frederic Chen <frederic.chen-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MTK_DIP_DEV_H_
+#define _MTK_DIP_DEV_H_
+
+#include <linux/types.h>
+#include <linux/device.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"
+#include "mtk_dip-smem.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 (1110 * 1024)
+
+#define V4L2_CID_PRIVATE_UT_NUM			(V4L2_CID_USER_BASE | 0x1001)
+#define V4L2_CID_PRIVATE_SET_BUFFER_USAGE	(V4L2_CID_PRIVATE_UT_NUM + 2)
+#define V4L2_CID_MTK_DIP_MAX			100
+
+enum mtk_dip_v4l2_buffer_usage {
+	MTK_DIP_V4l2_BUF_USAGE_DEFAULT = 0,
+	MTK_DIP_V4l2_BUF_USAGE_FD,
+	MTK_DIP_V4l2_BUF_USAGE_POSTPROC,
+	MTK_DIP_V4l2_BUF_USAGE_NONE,
+};
+
+/*
+ * Supported format and the information used for
+ * size calculation
+ */
+struct mtk_dip_dev_meta_format {
+	u32 dataformat;
+	u32 max_buffer_size;
+	u8 flags;
+};
+
+/* MDP part private format definitation */
+struct mtk_dip_dev_mdp_format {
+	u32 pixelformat;
+	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;
+};
+
+struct mtk_dip_dev_format {
+	union {
+		struct mtk_dip_dev_meta_format meta;
+		struct mtk_dip_dev_mdp_format img;
+	} fmt;
+};
+
+struct mtk_dip_pipe_job_info {
+	struct media_request *req;
+	int id;
+	struct mtk_dip_dev_buffer*
+		buf_map[MTK_DIP_VIDEO_NODE_ID_TOTAL_NUM];
+	int num_img_capture_bufs;
+	int num_img_output_bufs;
+	int num_meta_capture_bufs;
+	int num_meta_output_bufs;
+	struct list_head list;
+};
+
+struct mtk_dip_dev_buffer {
+	struct vb2_v4l2_buffer vbb;
+	struct v4l2_format fmt;
+	struct mtk_dip_dev_format *dev_fmt;
+	int pipe_job_id;
+	void *vaddr;
+	dma_addr_t isp_daddr;
+	dma_addr_t scp_daddr;
+	unsigned int buffer_usage;
+	int rotation;
+	struct list_head list;
+};
+
+struct mtk_dip_pipe_desc {
+	char *name;
+	int master;
+	int id;
+	struct mtk_dip_video_device_desc *output_queue_descs;
+	int total_output_queues;
+	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;
+	int dynamic;
+	int default_enable;
+	struct mtk_dip_dev_format *fmts;
+	int num_fmts;
+	char *description;
+	int default_width;
+	int default_height;
+	const struct v4l2_ioctl_ops *ops;
+	int default_fmt_idx;
+};
+
+struct mtk_dip_dev_queue {
+	struct vb2_queue vbq;
+	/* Serializes vb2 queue and video device operations */
+	struct mutex lock;
+	struct mtk_dip_dev_format *dev_fmt;
+	/* Firmware uses buffer_usage to select suitable DMA ports */
+	unsigned int buffer_usage;
+	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;
+	int immutable;
+	int enabled;
+	struct mtk_dip_video_device_desc *desc;
+	atomic_t sequence;
+};
+
+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;
+	int streaming;
+	struct media_pad *subdev_pads;
+	struct media_pipeline pipeline;
+	struct v4l2_subdev subdev;
+	struct v4l2_subdev_fh *fh;
+	struct v4l2_ctrl_handler ctrl_handler;
+	struct mtk_dip_smem_dev *smem_alloc_dev;
+	atomic_t pipe_job_sequence;
+	struct mtk_dip_pipe_job_info pipe_job_infos[VB2_MAX_FRAME];
+	int num_pipe_job_infos;
+	struct list_head pipe_job_running_list;
+	struct list_head pipe_job_free_list;
+	/* Serializes pipe's stream on/off and buffers enqueue operations */
+	struct mutex lock;
+	spinlock_t job_lock; /* protect the pipe job list */
+	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_smem_dev smem_alloc_dev;
+	struct mtk_dip_hw dip_hw;
+};
+
+int mtk_dip_dev_media_register(struct device *dev,
+			       struct media_device *media_dev,
+			       const char *model);
+
+int mtk_dip_dev_v4l2_init(struct mtk_dip_dev *dip_dev);
+
+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 *dip_pipe,
+			       struct media_device *media_dev,
+			       struct v4l2_device *v4l2_dev);
+
+int mtk_dip_pipe_v4l2_unregister(struct mtk_dip_pipe *dip_pipe);
+
+void mtk_dip_v4l2_buffer_done(struct vb2_buffer *vb,
+			      enum vb2_buffer_state state);
+
+int mtk_dip_pipe_queue_buffers(struct media_request *req, int initial);
+
+int mtk_dip_pipe_init(struct mtk_dip_pipe *dip_pipe,
+		      struct mtk_dip_dev *dip_dev,
+		      struct mtk_dip_pipe_desc *setting,
+		      struct media_device *media_dev,
+		      struct v4l2_device *v4l2_dev,
+		      struct mtk_dip_smem_dev *smem_alloc_dev);
+
+int mtk_dip_pipe_release(struct mtk_dip_pipe *dip_pipe);
+
+int mtk_dip_pipe_job_finish(struct mtk_dip_pipe *dip_pipe,
+			    unsigned int pipe_job_info_id,
+			    enum vb2_buffer_state state);
+
+int mtk_dip_pipe_job_start(struct mtk_dip_pipe *dip_pipe,
+			   struct mtk_dip_pipe_job_info *pipe_job_info);
+
+int mtk_dip_pipe_init_job_infos(struct mtk_dip_pipe *dip_pipe);
+
+struct mtk_dip_dev_format *
+mtk_dip_pipe_find_fmt(struct mtk_dip_pipe *dip_pipe,
+		      struct mtk_dip_video_device *node,
+		      u32 format);
+
+int mtk_dip_pipe_set_img_fmt(struct mtk_dip_pipe *dip_pipe,
+			     struct mtk_dip_video_device *node,
+			     struct v4l2_pix_format_mplane *user_fmt,
+			     struct v4l2_pix_format_mplane *node_fmt);
+
+int mtk_dip_pipe_set_meta_fmt(struct mtk_dip_pipe *dip_pipe,
+			      struct mtk_dip_video_device *node,
+			      struct v4l2_meta_format *user_fmt,
+			      struct v4l2_meta_format *node_fmt);
+
+void mtk_dip_pipe_load_default_fmt(struct mtk_dip_pipe *dip_pipe,
+				   struct mtk_dip_video_device *node,
+				   struct v4l2_format *fmt_to_fill);
+
+int mtk_dip_pipe_streamon(struct mtk_dip_pipe *dip_pipe);
+
+int mtk_dip_pipe_streamoff(struct mtk_dip_pipe *dip_pipe);
+
+int mtk_dip_ctrl_init(struct mtk_dip_pipe *dip_pipe);
+
+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_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 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..d813d8b92e8b
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-hw.h
@@ -0,0 +1,167 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ * Author: Holmes Chiou <holmes.chiou-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MTK_DIP_HW_H_
+#define _MTK_DIP_HW_H_
+
+#include <linux/clk.h>
+#include "mtk-img-ipi.h"
+
+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_STREAMON,
+	DIP_STATE_STREAMOFF
+};
+
+struct mtk_dip_hw_frame_job {
+	struct img_frameparam fparam;
+	int sequence;
+};
+
+struct mtk_dip_hw_user_id {
+	struct list_head list_entry;
+	u16 id;
+	u32 num;
+	u16 state;
+};
+
+struct mtk_dip_hw_subframe {
+	struct img_addr buffer;
+	struct sg_table table;
+	struct img_sw_addr config_data;
+	struct img_addr tuning_buf;
+	struct img_sw_addr frameparam;
+	struct list_head list_entry;
+};
+
+struct mtk_dip_hw_queue {
+	struct list_head queue;
+	struct mutex queuelock; /* protect queue and queue_cnt */
+	u32 queue_cnt;
+};
+
+struct mtk_dip_hw_joblist {
+	struct list_head queue;
+	spinlock_t queuelock; /* protect job list */
+	u32 queue_cnt;
+};
+
+struct mtk_dip_hw_thread {
+	struct task_struct *thread;
+	wait_queue_head_t wq;
+};
+
+struct mtk_dip_hw_work {
+	struct list_head list_entry;
+	struct img_ipi_frameparam frameparams;
+	struct mtk_dip_hw_user_id *user_id;
+};
+
+struct mtk_dip_hw_submit_work {
+	struct work_struct frame_work;
+	struct mtk_dip_hw *dip_hw;
+};
+
+struct mtk_dip_hw_mdpcb_work {
+	struct work_struct frame_work;
+	struct img_ipi_frameparam *frameparams;
+};
+
+struct mtk_dip_hw_clk {
+	struct clk *img_larb5;
+	struct clk *img_dip;
+};
+
+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 {
+	struct mtk_dip_hw_clk dip_clk;
+	struct device *larb_dev;
+	struct mtk_dip_hw_joblist dip_gcejoblist;
+	struct mtk_dip_hw_queue dip_freebufferlist;
+	struct mtk_dip_hw_queue dip_usedbufferlist;
+	struct mtk_dip_hw_thread dip_runner_thread;
+	struct mtk_dip_hw_queue dip_useridlist;
+	struct mtk_dip_hw_queue dip_worklist;
+	struct workqueue_struct *composer_wq;
+	struct mtk_dip_hw_submit_work submit_work;
+	wait_queue_head_t composing_wq;
+	wait_queue_head_t flushing_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 VPU driver  */
+	struct platform_device *vpu_pdev;
+	struct rproc *rproc_handle;
+	dma_addr_t scp_workingbuf_addr;
+	/* increase after enqueue */
+	atomic_t dip_enque_cnt;
+	/* increase after Stream ON, decrease when Stream OFF */
+	atomic_t dip_stream_cnt;
+	/* increase after open, decrease when close */
+	atomic_t dip_user_cnt;
+};
+
+int mtk_dip_hw_enqueue(struct mtk_dip_hw *dip_hw,
+		       struct img_ipi_frameparam *frameparams);
+int mtk_dip_hw_connect(struct mtk_dip_hw *dip_hw);
+int mtk_dip_hw_disconnect(struct mtk_dip_hw *dip_hw);
+int mtk_dip_hw_streamon(struct mtk_dip_hw *dip_hw, u16 id);
+int mtk_dip_hw_streamoff(struct mtk_dip_hw *dip_hw, u16 id);
+
+static inline struct mtk_dip_hw_frame_job
+*mtk_dip_fparam_to_job(struct img_frameparam *fparam)
+{
+	return container_of(fparam, struct mtk_dip_hw_frame_job, fparam);
+}
+
+static inline struct mtk_dip_hw_frame_job *
+mtk_dip_ipi_fparam_to_job(struct img_ipi_frameparam *ipi_fparam)
+{
+	return container_of(ipi_fparam,
+			    struct mtk_dip_hw_frame_job,
+			    fparam.frameparam);
+}
+
+#endif /* _MTK_DIP_HW_H_ */
+
diff --git a/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-smem.c b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-smem.c
new file mode 100644
index 000000000000..5456c0b54ad4
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-smem.c
@@ -0,0 +1,322 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Frederic Chen <frederic.chen-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/dma-contiguous.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/iommu.h>
+#include <asm/cacheflush.h>
+#include "mtk_dip-smem.h"
+
+#define MTK_DIP_SMEM_DEV_NAME "MTK-DIP-SMEM"
+
+static struct reserved_mem *dip_reserved_smem;
+static struct dma_map_ops smem_dma_ops;
+
+struct dma_coherent_mem {
+	void		*virt_base;
+	dma_addr_t	device_base;
+	unsigned long	pfn_base;
+	int		size;
+	int		flags;
+	unsigned long	*bitmap;
+	spinlock_t	spinlock; /* protect dma_coherent_mem member */
+	bool		use_dev_dma_pfn_offset;
+};
+
+static struct dma_coherent_mem *dev_get_coherent_memory(struct device *dev)
+{
+	if (dev && dev->dma_mem)
+		return dev->dma_mem;
+	return NULL;
+}
+
+phys_addr_t mtk_dip_smem_iova_to_phys(struct mtk_dip_smem_dev *smem_dev,
+				      dma_addr_t iova)
+{
+		struct iommu_domain *smem_dom;
+		phys_addr_t addr;
+		phys_addr_t limit;
+
+		if (!smem_dev)
+			return 0;
+
+		smem_dom = iommu_get_domain_for_dev(smem_dev->dev.parent);
+
+		if (!smem_dom)
+			return 0;
+
+		addr = iommu_iova_to_phys(smem_dom, iova);
+
+		limit = smem_dev->smem_base + smem_dev->smem_size;
+
+		if (addr < smem_dev->smem_base || addr >= limit) {
+			dev_err(&smem_dev->dev,
+				"Unexpected scp_daddr %pa (must >= %pa and <%pa)\n",
+				&addr, &smem_dev->smem_base, &limit);
+			return 0;
+		}
+		dev_dbg(&smem_dev->dev, "Pa verifcation pass: %pa(>=%pa, <%pa)\n",
+			&addr, &smem_dev->smem_base, &limit);
+		return addr;
+}
+
+/********************************************
+ * MTK DIP SMEM DMA ops *
+ ********************************************/
+static int mtk_dip_smem_get_sgtable(struct device *dev,
+				    struct sg_table *sgt,
+				    void *cpu_addr,
+				    dma_addr_t dma_addr,
+				    size_t size, unsigned long attrs)
+{
+	struct mtk_dip_smem_dev *smem_dev = dev_get_drvdata(dev);
+	int n_pages_align;
+	int size_align;
+	int page_start;
+	unsigned long long offset_p;
+
+	phys_addr_t paddr = mtk_dip_smem_iova_to_phys(smem_dev, dma_addr);
+
+	offset_p = (unsigned long long)paddr -
+		(unsigned long long)smem_dev->smem_base;
+
+	dev_dbg(dev, "%s: dma_addr(%p), cpu_addr(%p), pa(%p), size(%d)\n",
+		__func__, dma_addr, cpu_addr, paddr, size);
+
+	size_align = round_up(size, PAGE_SIZE);
+	n_pages_align = size_align >> PAGE_SHIFT;
+	page_start = offset_p >> PAGE_SHIFT;
+
+	dev_dbg(dev, "%s: page_start(%d), page pa(%p), pa(%p), aligned size(%d)\n",
+		__func__,
+		page_start,
+		page_to_phys(*(smem_dev->smem_pages + page_start)),
+		paddr,
+		size_align
+		);
+
+	if (!smem_dev) {
+		dev_err(dev, "can't get sgtable from smem_dev\n");
+		return -EINVAL;
+	}
+
+	dev_dbg(dev, "%s: get sgt of the smem: %d pages\n", __func__,
+		n_pages_align);
+
+	return sg_alloc_table_from_pages(sgt,
+					 smem_dev->smem_pages + page_start,
+					 n_pages_align,
+					 0, size_align, GFP_KERNEL);
+}
+
+static void *mtk_dip_smem_get_cpu_addr(struct mtk_dip_smem_dev *smem_dev,
+				       struct scatterlist *sg)
+{
+	struct device *dev = &smem_dev->dev;
+	struct dma_coherent_mem *dma_mem =
+		dev_get_coherent_memory(dev);
+
+	phys_addr_t addr = (phys_addr_t)sg_phys(sg);
+
+	if (addr < smem_dev->smem_base ||
+	    addr > smem_dev->smem_base + smem_dev->smem_size) {
+		dev_err(dev, "%s: Invalid paddr %p from sg\n", __func__, addr);
+		return NULL;
+	}
+
+	return dma_mem->virt_base + (addr - smem_dev->smem_base);
+}
+
+static void mtk_dip_smem_sync_sg_for_cpu(struct device *dev,
+					 struct scatterlist *sgl,
+					 int nelems,
+					 enum dma_data_direction dir)
+{
+	struct mtk_dip_smem_dev *smem_dev =
+		dev_get_drvdata(dev);
+	void *cpu_addr;
+
+	cpu_addr = mtk_dip_smem_get_cpu_addr(smem_dev, sgl);
+
+	dev_dbg(dev, "%s: paddr(%p),vaddr(%p),size(%d)\n",
+		__func__, sg_phys(sgl), cpu_addr, sgl->length);
+
+	if (cpu_addr)
+		__dma_unmap_area(cpu_addr, sgl->length, dir);
+}
+
+static void mtk_dip_smem_sync_sg_for_device(struct device *dev,
+					    struct scatterlist *sgl,
+					    int nelems,
+					    enum dma_data_direction dir)
+{
+	struct mtk_dip_smem_dev *smem_dev =
+			dev_get_drvdata(dev);
+	void *cpu_addr;
+
+	cpu_addr = mtk_dip_smem_get_cpu_addr(smem_dev, sgl);
+
+	dev_dbg(dev, "%s: pa(%p),va(%p),size(%d),dir(%d)\n",
+		__func__, sg_phys(sgl), cpu_addr, sgl->length, dir);
+
+	if (cpu_addr)
+		__dma_map_area(cpu_addr, sgl->length, dir);
+}
+
+static int mtk_dip_smem_setup_dma_ops(struct device *dev,
+				      struct device *default_alloc_dev)
+{
+	memcpy((void *)&smem_dma_ops, default_alloc_dev->dma_ops,
+	       sizeof(smem_dma_ops));
+
+	smem_dma_ops.get_sgtable =
+		mtk_dip_smem_get_sgtable;
+	smem_dma_ops.sync_sg_for_device =
+		mtk_dip_smem_sync_sg_for_device;
+	smem_dma_ops.sync_sg_for_cpu =
+		mtk_dip_smem_sync_sg_for_cpu;
+
+	dev->dma_ops = &smem_dma_ops;
+
+	dev_dbg(dev, "setup smem_dma_ops: %p\n", dev->dma_ops);
+
+	return 0;
+}
+
+void mtk_dip_smem_alloc_dev_release(struct mtk_dip_smem_dev *smem_dev)
+{
+	device_unregister(&smem_dev->dev);
+}
+
+int mtk_dip_smem_alloc_dev_init(struct mtk_dip_smem_dev *smem_dev,
+				struct device *parent)
+{
+	int ret;
+	struct device *dev = &smem_dev->dev;
+
+	dev->parent  = parent;
+	dev_set_name(&smem_dev->dev, "dip-smem");
+
+	ret = device_register(dev);
+
+	if (ret)
+		dev_err(parent, "Failed to register smem device\n");
+
+	dev_dbg(dev, "init alloc dev(%p), parent(%p)\n", dev, dev->parent);
+
+	dev_set_drvdata(dev, smem_dev);
+
+	if (dip_reserved_smem) {
+		dma_addr_t dma_addr;
+		phys_addr_t addr;
+		struct iommu_domain *smem_dom;
+		int i;
+		int size_align;
+		struct page **pages;
+		int n_pages;
+		struct sg_table *sgt = &smem_dev->sgt;
+
+		size_align = round_down(dip_reserved_smem->size, PAGE_SIZE);
+		n_pages = size_align >> PAGE_SHIFT;
+		pages = kmalloc_array(n_pages, sizeof(struct page *),
+				      GFP_KERNEL);
+
+		if (!pages)
+			return -ENOMEM;
+
+		for (i = 0; i < n_pages; i++)
+			pages[i] = phys_to_page(dip_reserved_smem->base
+						+ i * PAGE_SIZE);
+
+		ret = sg_alloc_table_from_pages(sgt, pages, n_pages, 0,
+						size_align, GFP_KERNEL);
+
+		if (ret) {
+			dev_err(dev, "failed to get alloca sg table\n");
+			return -ENOMEM;
+		}
+
+		dma_map_sg_attrs(parent, sgt->sgl, sgt->nents,
+				 DMA_BIDIRECTIONAL,
+				 DMA_ATTR_SKIP_CPU_SYNC);
+
+		dma_addr = sg_dma_address(sgt->sgl);
+		smem_dom = iommu_get_domain_for_dev(parent);
+		addr = iommu_iova_to_phys(smem_dom, dma_addr);
+
+		if (addr != dip_reserved_smem->base)
+			dev_warn(dev,
+				 "incorrect pa(%p) from iommu_iova_to_phys, should be %p\n",
+				 addr, dip_reserved_smem->base);
+
+		ret = dma_declare_coherent_memory(dev,
+						  dip_reserved_smem->base,
+						  dma_addr, size_align,
+						  DMA_MEMORY_EXCLUSIVE);
+
+		dev_dbg(dev, "Coherent mem base(%p,%p),size(%lx),ret(%d)\n",
+			dip_reserved_smem->base, dma_addr, size_align, ret);
+
+		smem_dev->smem_base = dip_reserved_smem->base;
+		smem_dev->smem_size = size_align;
+		smem_dev->smem_pages = pages;
+		smem_dev->num_smem_pages = n_pages;
+		smem_dev->smem_dma_base = dma_addr;
+
+		dev_dbg(dev, "smem_dev setting (%p,%lx,%p,%d)\n",
+			smem_dev->smem_base, smem_dev->smem_size,
+			smem_dev->smem_pages, smem_dev->num_smem_pages);
+	}
+
+	ret = mtk_dip_smem_setup_dma_ops(dev, parent);
+
+	return ret;
+}
+
+static int __init mtk_dip_smem_dma_setup(struct reserved_mem *rmem)
+{
+	unsigned long node = rmem->fdt_node;
+
+	if (of_get_flat_dt_prop(node, "reusable", NULL))
+		return -EINVAL;
+
+	if (!of_get_flat_dt_prop(node, "no-map", NULL)) {
+		pr_err("Reserved memory: regions without no-map are not yet supported\n");
+		return -EINVAL;
+	}
+
+	dip_reserved_smem = rmem;
+
+	pr_debug("Reserved memory: created DMA memory pool at %pa, size %ld MiB\n",
+		 &rmem->base, (unsigned long)rmem->size / SZ_1M);
+	return 0;
+}
+
+RESERVEDMEM_OF_DECLARE(mtk_dip_smem,
+		       "mediatek,reserve-memory-dip_smem",
+		       mtk_dip_smem_dma_setup);
+
+MODULE_AUTHOR("Frederic Chen <frederic.chen-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Mediatek Camera DIP shared memory alloc device");
+
diff --git a/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-smem.h b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-smem.h
new file mode 100644
index 000000000000..a2f7559cc49d
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-smem.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Frederic Chen <frederic.chen-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MTK_DIP_SMEM_H_
+#define _MTK_DIP_SMEM_H_
+
+#include <linux/dma-mapping.h>
+#include <linux/device.h>
+
+struct mtk_dip_smem_dev {
+	struct device dev;
+	struct sg_table sgt;
+	struct page **smem_pages;
+	int num_smem_pages;
+	phys_addr_t smem_base;
+	dma_addr_t smem_dma_base;
+	int smem_size;
+};
+
+phys_addr_t mtk_dip_smem_iova_to_phys(struct mtk_dip_smem_dev *smem_dev,
+				      dma_addr_t iova);
+int mtk_dip_smem_alloc_dev_init(struct mtk_dip_smem_dev *smem_dev,
+				struct device *default_alloc_dev);
+void mtk_dip_smem_alloc_dev_release(struct mtk_dip_smem_dev *smem_dev);
+
+#endif /*_MTK_DIP_SMEM_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..54d2b5f5b802
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-sys.c
@@ -0,0 +1,1384 @@
+// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Holmes Chiou <holmes.chiou-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/kthread.h>
+#include <linux/pm_runtime.h>
+#include <linux/dma-iommu.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/remoteproc.h>
+#include <linux/platform_data/mtk_scp.h>
+#include "mtk-mdp3-cmdq.h"
+#include "mtk_dip-dev.h"
+#include "mtk_dip-hw.h"
+
+#define DIP_DEV_NAME			"camera-dip"
+
+#define DIP_COMPOSER_THREAD_TIMEOUT     16U
+#define DIP_COMPOSING_WQ_TIMEOUT	16U
+#define DIP_COMPOSING_MAX_NUM		3
+#define DIP_FLUSHING_WQ_TIMEOUT		16U
+#define DIP_MAX_ERR_COUNT		188U
+
+#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)
+
+static inline struct mtk_dip_hw *get_dip_device(struct device *dev)
+{
+	struct mtk_dip_dev *dip_dev = dev_get_drvdata(dev);
+
+	if (dip_dev)
+		return &dip_dev->dip_hw;
+	else
+		return NULL;
+}
+
+static struct img_frameparam *dip_create_framejob(int sequence)
+{
+	struct mtk_dip_hw_frame_job *fjob;
+
+	fjob = kzalloc(sizeof(*fjob), GFP_ATOMIC);
+
+	if (!fjob)
+		return NULL;
+
+	fjob->sequence = sequence;
+
+	return &fjob->fparam;
+}
+
+static void dip_free_framejob(struct img_frameparam *fparam)
+{
+	struct mtk_dip_hw_frame_job *fjob;
+
+	fjob = mtk_dip_fparam_to_job(fparam);
+
+	/* to avoid use after free issue */
+	fjob->sequence = -1;
+
+	kfree(fjob);
+}
+
+static void dip_enable_ccf_clock(struct mtk_dip_hw *dip_hw)
+{
+	struct mtk_dip_dev *dip_dev;
+	int ret;
+
+	dip_dev = mtk_dip_hw_to_dev(dip_hw);
+	ret = pm_runtime_get_sync(dip_hw->larb_dev);
+	if (ret < 0)
+		dev_err(&dip_dev->pdev->dev, "%cannot get smi larb clock\n");
+
+	ret = clk_prepare_enable(dip_hw->dip_clk.img_larb5);
+	if (ret)
+		dev_err(&dip_dev->pdev->dev,
+			"cannot prepare and enable img_larb5 clock\n");
+
+	ret = clk_prepare_enable(dip_hw->dip_clk.img_dip);
+	if (ret)
+		dev_err(&dip_dev->pdev->dev,
+			"cannot prepare and enable img_dip clock\n");
+}
+
+static void dip_disable_ccf_clock(struct mtk_dip_hw *dip_hw)
+{
+	clk_disable_unprepare(dip_hw->dip_clk.img_dip);
+	clk_disable_unprepare(dip_hw->dip_clk.img_larb5);
+	pm_runtime_put_sync(dip_hw->larb_dev);
+}
+
+static int dip_send(struct platform_device *pdev, enum scp_ipi_id id,
+		    void *buf, unsigned int  len, unsigned int wait)
+{
+	return scp_ipi_send(pdev, id, buf, len, wait);
+}
+
+static struct mtk_dip_pipe *get_mtk_dip_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 void call_mtk_dip_pipe_finish(struct mtk_dip_hw *dip_hw,
+				     struct img_ipi_frameparam *iparam)
+{
+	struct mtk_dip_dev *dip_dev;
+	struct mtk_dip_pipe *dip_pipe;
+	enum vb2_buffer_state vbf_state;
+	int pipe_id;
+	int ret;
+
+	if (!dip_hw) {
+		pr_err("%s: can't update buffer status, dip_hw is NULL\n",
+		       __func__);
+		return;
+	}
+
+	dip_dev = mtk_dip_hw_to_dev(dip_hw);
+
+	if (!iparam) {
+		dev_dbg(&dip_dev->pdev->dev, "%s: iparam can't be NULL\n",
+			__func__);
+		return;
+	}
+
+	pipe_id = mtk_dip_pipe_get_pipe_from_job_id(iparam->index);
+	dip_pipe = get_mtk_dip_pipe(dip_dev, pipe_id);
+
+	if (!dip_pipe) {
+		dev_dbg(&dip_dev->pdev->dev,
+			"%s: unknown pipe id(%d)\n", __func__, pipe_id);
+		return;
+	}
+
+	if (iparam->state == FRAME_STATE_ERROR)
+		vbf_state = VB2_BUF_STATE_ERROR;
+	else
+		vbf_state = VB2_BUF_STATE_DONE;
+
+	dev_dbg(&dip_dev->pdev->dev,
+		"%s: ready to return buffers,pipe(%d),pipe_job_id(%d)\n",
+		__func__, pipe_id, iparam->index);
+
+	ret = mtk_dip_pipe_job_finish(dip_pipe, iparam->index, vbf_state);
+
+	if (ret)
+		dev_dbg(&dip_dev->pdev->dev, "%s: finish CB failed(%d)\n",
+			__func__, ret);
+}
+
+static void mtk_dip_notify(void *data)
+{
+	struct mtk_dip_hw *dip_hw;
+	struct mtk_dip_dev *dip_dev;
+	struct img_frameparam *framejob;
+	struct mtk_dip_hw_user_id *user_id;
+	struct mtk_dip_hw_subframe *buf, *tmpbuf;
+	struct img_ipi_frameparam *frameparam;
+	u32 num;
+	bool found = false;
+
+	frameparam = (struct img_ipi_frameparam *)data;
+	dip_hw = (struct mtk_dip_hw *)frameparam->drv_data;
+	dip_dev = mtk_dip_hw_to_dev(dip_hw);
+	framejob = container_of(frameparam,
+				struct img_frameparam,
+				frameparam);
+
+	if (frameparam->state == FRAME_STATE_HW_TIMEOUT) {
+		dip_send(dip_hw->vpu_pdev, SCP_IPI_DIP_FRAME,
+			 (void *)frameparam, sizeof(*frameparam), 0);
+		dev_err(&dip_dev->pdev->dev, "%s: frame no(%d) HW timeout\n",
+			__func__, frameparam->frame_no);
+	}
+
+	mutex_lock(&dip_hw->dip_usedbufferlist.queuelock);
+	list_for_each_entry_safe(buf, tmpbuf,
+				 &dip_hw->dip_usedbufferlist.queue,
+				 list_entry) {
+		if (buf->buffer.pa == frameparam->subfrm_data.pa) {
+			list_del(&buf->list_entry);
+			dip_hw->dip_usedbufferlist.queue_cnt--;
+			found = true;
+			dev_dbg(&dip_dev->pdev->dev,
+				"%s: Found used buffer(%x)\n",
+				__func__, buf->buffer.pa);
+			break;
+		}
+	}
+	mutex_unlock(&dip_hw->dip_usedbufferlist.queuelock);
+
+	if (!found) {
+		dev_err(&dip_dev->pdev->dev,
+			"%s: frame_no(%d), buf(%x), used buf cnt(%d)\n",
+			__func__, frameparam->frame_no,
+			frameparam->subfrm_data.pa,
+			dip_hw->dip_usedbufferlist.queue_cnt);
+
+		frameparam->state = FRAME_STATE_ERROR;
+
+	} else {
+		mutex_lock(&dip_hw->dip_freebufferlist.queuelock);
+		list_add_tail(&buf->list_entry,
+			      &dip_hw->dip_freebufferlist.queue);
+		dip_hw->dip_freebufferlist.queue_cnt++;
+		mutex_unlock(&dip_hw->dip_freebufferlist.queuelock);
+
+		frameparam->state = FRAME_STATE_DONE;
+	}
+
+	call_mtk_dip_pipe_finish(dip_hw, frameparam);
+
+	found = false;
+	mutex_lock(&dip_hw->dip_useridlist.queuelock);
+	list_for_each_entry(user_id,
+			    &dip_hw->dip_useridlist.queue,
+			    list_entry) {
+		if (mtk_dip_pipe_get_pipe_from_job_id(frameparam->index)
+			== user_id->id) {
+			user_id->num--;
+			dev_dbg(&dip_dev->pdev->dev,
+				"%s: user_id(%x) found, cnt(%d)\n",
+				__func__, user_id->id, user_id->num);
+			found = true;
+			break;
+		}
+	}
+	mutex_unlock(&dip_hw->dip_useridlist.queuelock);
+	wake_up(&dip_hw->flushing_wq);
+	dev_dbg(&dip_dev->pdev->dev,
+		"%s: frame_no(%d) is finished\n",
+		__func__, framejob->frameparam.frame_no);
+	dip_free_framejob(framejob);
+
+	num = atomic_dec_return(&dip_hw->num_running);
+	dev_dbg(&dip_dev->pdev->dev, "%s: running cnt(%d)\n", __func__, num);
+}
+
+static void mdp_cb_worker(struct work_struct *work)
+{
+	struct mtk_dip_hw_mdpcb_work *mdpcb_work;
+
+	mdpcb_work = container_of(work, struct mtk_dip_hw_mdpcb_work,
+				  frame_work);
+	mtk_dip_notify(mdpcb_work->frameparams);
+	kfree(mdpcb_work);
+}
+
+static struct img_ipi_frameparam *convert_to_fparam(struct cmdq_cb_data *data)
+{
+	struct mtk_dip_hw *dip_hw;
+	struct mtk_dip_dev *dip_dev;
+	struct mtk_dip_hw_frame_job *fjob;
+	struct img_ipi_frameparam *ipi_fparam;
+
+	if (!data) {
+		pr_err("%s: cmdq_cb_data can't be NULL\n",
+		       __func__);
+		return NULL;
+	}
+
+	if (data->sta != CMDQ_CB_NORMAL)
+		pr_debug("%s: got CMDQ CB(%d) without CMDQ_CB_NORMAL\n",
+			 __func__, data->sta);
+
+	if (!data->data) {
+		pr_err("%s: got NULL in cmdq_cb_data\n",
+		       __func__);
+		return NULL;
+	}
+
+	fjob = mtk_dip_ipi_fparam_to_job(data->data);
+
+	if (fjob->sequence == -1) {
+		pr_err("%s: Invalid cmdq_cb_data(%p)\n",
+		       __func__, data);
+		ipi_fparam = NULL;
+	} else {
+		ipi_fparam = &fjob->fparam.frameparam;
+		dip_hw = (struct mtk_dip_hw *)ipi_fparam->drv_data;
+		dip_dev = mtk_dip_hw_to_dev(dip_hw);
+	}
+
+	dev_dbg(&dip_dev->pdev->dev, "%s: framejob(%p), seq(%d)\n",
+		__func__, fjob, fjob->sequence);
+	dev_dbg(&dip_dev->pdev->dev, "%s: idx(%d), no(%d), s(%d), n_in(%d), n_out(%d), drv(%p)\n",
+		__func__,
+		fjob->fparam.frameparam.index,
+		fjob->fparam.frameparam.frame_no,
+		fjob->fparam.frameparam.state,
+		fjob->fparam.frameparam.num_inputs,
+		fjob->fparam.frameparam.num_outputs,
+		fjob->fparam.frameparam.drv_data
+	);
+
+	return ipi_fparam;
+}
+
+/* Maybe in IRQ context of cmdq */
+static void dip_mdp_cb_func(struct cmdq_cb_data data)
+{
+	struct img_ipi_frameparam *frameparam;
+	struct mtk_dip_hw *dip_hw;
+	struct mtk_dip_dev *dip_dev;
+	struct mtk_dip_hw_mdpcb_work *mdpcb_work;
+
+	frameparam = convert_to_fparam(&data);
+
+	if (!frameparam) {
+		pr_err("%s: return due to NULL cmdq_cb_data)",
+		       __func__, &data);
+		return;
+	}
+
+	dip_hw = (struct mtk_dip_hw *)frameparam->drv_data;
+	dip_dev = mtk_dip_hw_to_dev(dip_hw);
+
+	mdpcb_work = kzalloc(sizeof(*mdpcb_work), GFP_ATOMIC);
+
+	if (WARN_ONCE(!mdpcb_work, "%s: frame_no(%d) is lost",
+		      __func__, frameparam->frame_no))
+		return;
+
+	INIT_WORK(&mdpcb_work->frame_work, mdp_cb_worker);
+	mdpcb_work->frameparams = frameparam;
+	if (data.sta != CMDQ_CB_NORMAL)
+		mdpcb_work->frameparams->state = FRAME_STATE_HW_TIMEOUT;
+
+	queue_work(dip_hw->mdpcb_workqueue, &mdpcb_work->frame_work);
+}
+
+static void dip_vpu_handler(void *data, unsigned int len, void *priv)
+{
+	struct img_frameparam *framejob;
+	struct img_ipi_frameparam *frameparam;
+	struct mtk_dip_hw *dip_hw;
+	struct mtk_dip_dev *dip_dev;
+	unsigned long flags;
+	u32 num;
+
+	if (WARN_ONCE(!data, "%s: failed due to NULL data\n", __func__))
+		return;
+
+	frameparam = (struct img_ipi_frameparam *)data;
+	framejob = dip_create_framejob(frameparam->index);
+
+	if (WARN_ONCE(!framejob, "%s: frame_no(%d) is lost\n",
+		      __func__, frameparam->frame_no))
+		return;
+
+	dip_hw = (struct mtk_dip_hw *)frameparam->drv_data;
+	dip_dev = mtk_dip_hw_to_dev(dip_hw);
+
+	wake_up(&dip_hw->composing_wq);
+	memcpy(&framejob->frameparam, data, sizeof(framejob->frameparam));
+	num = atomic_dec_return(&dip_hw->num_composing);
+
+	spin_lock_irqsave(&dip_hw->dip_gcejoblist.queuelock, flags);
+	list_add_tail(&framejob->list_entry, &dip_hw->dip_gcejoblist.queue);
+	dip_hw->dip_gcejoblist.queue_cnt++;
+	spin_unlock_irqrestore(&dip_hw->dip_gcejoblist.queuelock, flags);
+
+	dev_dbg(&dip_dev->pdev->dev,
+		"%s: frame_no(%d) is back, composing num(%d)\n",
+		__func__, frameparam->frame_no, num);
+
+	wake_up(&dip_hw->dip_runner_thread.wq);
+}
+
+static int dip_runner_func(void *data)
+{
+	struct img_frameparam *framejob;
+	struct mtk_dip_hw *dip_hw;
+	struct mtk_dip_dev *dip_dev;
+	struct mtk_dip_hw_user_id *user_id;
+	unsigned long flags;
+	bool found;
+	u32 queuecnt, num;
+	int ret;
+
+	dip_hw = (struct mtk_dip_hw *)data;
+	dip_dev = mtk_dip_hw_to_dev(dip_hw);
+
+	while (1) {
+		spin_lock_irqsave(&dip_hw->dip_gcejoblist.queuelock, flags);
+		queuecnt = dip_hw->dip_gcejoblist.queue_cnt;
+		spin_unlock_irqrestore(&dip_hw->dip_gcejoblist.queuelock,
+				       flags);
+
+		ret = wait_event_interruptible_timeout
+			(dip_hw->dip_runner_thread.wq,
+			 queuecnt || kthread_should_stop(),
+			 msecs_to_jiffies(DIP_COMPOSER_THREAD_TIMEOUT));
+
+		if (kthread_should_stop())
+			break;
+
+		if (ret == 0) {
+			/* Timeout */
+			ret = -ETIME;
+		} else if (ret == -ERESTARTSYS) {
+			dev_err(&dip_dev->pdev->dev,
+				"%s: interrupted by a signal\n", __func__);
+		}
+
+		if (queuecnt > 0) {
+			spin_lock_irqsave(&dip_hw->dip_gcejoblist.queuelock,
+					  flags);
+			framejob = list_first_entry
+				(&dip_hw->dip_gcejoblist.queue,
+				 struct img_frameparam, list_entry);
+
+			dip_hw->dip_gcejoblist.queue_cnt--;
+			list_del(&framejob->list_entry);
+			spin_unlock_irqrestore
+				(&dip_hw->dip_gcejoblist.queuelock, flags);
+
+			found = false;
+			mutex_lock(&dip_hw->dip_useridlist.queuelock);
+			list_for_each_entry(user_id,
+					    &dip_hw->dip_useridlist.queue,
+					    list_entry) {
+				int id = framejob->frameparam.index;
+
+				if (mtk_dip_pipe_get_pipe_from_job_id(id) ==
+					user_id->id) {
+					found = true;
+					break;
+				}
+			}
+			mutex_unlock(&dip_hw->dip_useridlist.queuelock);
+
+			if (!found) {
+				dev_err(&dip_dev->pdev->dev,
+					"%s: frame_no(%d), idx(0x%x) is abnormal\n",
+					__func__,
+					framejob->frameparam.frame_no,
+					framejob->frameparam.index);
+				/*
+				 * Due to error index, DIP driver could NOT
+				 * notify the V4L2 common driver to
+				 * return buffer
+				 */
+				dip_free_framejob(framejob);
+				continue;
+			}
+
+			mutex_lock(&dip_hw->dip_useridlist.queuelock);
+			if (user_id->state == DIP_STATE_STREAMOFF) {
+				user_id->num--;
+				mutex_unlock
+					(&dip_hw->dip_useridlist.queuelock);
+				framejob->frameparam.state =
+					FRAME_STATE_STREAMOFF;
+				call_mtk_dip_pipe_finish(dip_hw,
+							 &framejob->frameparam);
+
+				dev_dbg(&dip_dev->pdev->dev,
+					"%s: user_id(%x) streamoff, current num(%d); frame_no(%d) flushed\n",
+					__func__, user_id->id, user_id->num,
+					framejob->frameparam.frame_no);
+
+				dip_free_framejob(framejob);
+				continue;
+			}
+			mutex_unlock(&dip_hw->dip_useridlist.queuelock);
+
+			dev_dbg(&dip_dev->pdev->dev,
+				"%s: MDP run frame_no(%d) and the rest joblist cnt(%d)\n",
+				__func__, framejob->frameparam.frame_no,
+				dip_hw->dip_gcejoblist.queue_cnt);
+
+			/*
+			 * Call MDP/GCE API to do HW excecution
+			 * Pass the framejob to MDP driver
+			 */
+			framejob->frameparam.state = FRAME_STATE_COMPOSING;
+
+			mdp_cmdq_sendtask
+				(dip_hw->mdp_pdev,
+				 (struct img_config *)
+					framejob->frameparam.config_data.va,
+				 &framejob->frameparam, NULL, false,
+				 dip_mdp_cb_func,
+				 (void *)&framejob->frameparam);
+
+			num = atomic_inc_return(&dip_hw->num_running);
+			dev_dbg(&dip_dev->pdev->dev,
+				"%s,MDP running num(%d)\n", __func__, num);
+		}
+
+	};
+
+	return 0;
+}
+
+static void dip_submit_worker(struct work_struct *work)
+{
+	struct mtk_dip_hw_submit_work *dip_submit_work =
+		container_of(work, struct mtk_dip_hw_submit_work, frame_work);
+	struct mtk_dip_hw *dip_hw = dip_submit_work->dip_hw;
+	struct mtk_dip_dev *dip_dev = mtk_dip_hw_to_dev(dip_hw);
+	struct mtk_dip_hw_work *dip_work;
+	struct mtk_dip_hw_subframe *buf;
+	u32 len, num;
+	int ret;
+
+	num  = atomic_read(&dip_hw->num_composing);
+
+	mutex_lock(&dip_hw->dip_worklist.queuelock);
+	dip_work = list_first_entry(&dip_hw->dip_worklist.queue,
+				    struct mtk_dip_hw_work, list_entry);
+	list_del(&dip_work->list_entry);
+	dip_hw->dip_worklist.queue_cnt--;
+	len = dip_hw->dip_worklist.queue_cnt;
+	mutex_unlock(&dip_hw->dip_worklist.queuelock);
+
+	mutex_lock(&dip_hw->dip_useridlist.queuelock);
+	if (dip_work->user_id->state == DIP_STATE_STREAMOFF) {
+		dip_work->user_id->num--;
+		mutex_unlock(&dip_hw->dip_useridlist.queuelock);
+
+		dip_work->frameparams.state = FRAME_STATE_STREAMOFF;
+		call_mtk_dip_pipe_finish(dip_hw, &dip_work->frameparams);
+
+		dev_dbg(&dip_dev->pdev->dev,
+			"%s: user_id(%x) is streamoff,num(%d),frame_no(%d),idx:0x%x\n",
+			 __func__, dip_work->user_id->id,
+			 dip_work->user_id->num,
+			 dip_work->frameparams.frame_no,
+			 dip_work->frameparams.index);
+
+		goto free_work_list;
+	}
+	mutex_unlock(&dip_hw->dip_useridlist.queuelock);
+
+	while (num >= DIP_COMPOSING_MAX_NUM) {
+		ret = wait_event_interruptible_timeout
+			(dip_hw->composing_wq,
+			 (num < DIP_COMPOSING_MAX_NUM),
+			 msecs_to_jiffies(DIP_COMPOSING_WQ_TIMEOUT));
+
+		if (ret == -ERESTARTSYS)
+			dev_err(&dip_dev->pdev->dev,
+				"%s: interrupted by a signal\n", __func__);
+		else if (ret == 0)
+			dev_dbg(&dip_dev->pdev->dev,
+				"%s: timeout frame_no(%d),num(%d)\n",
+				__func__, dip_work->frameparams.frame_no,
+				num);
+		else
+			dev_dbg(&dip_dev->pdev->dev,
+				"%s: wakeup frame_no(%d),num(%d)\n",
+				__func__, dip_work->frameparams.frame_no, num);
+
+		num = atomic_read(&dip_hw->num_composing);
+	};
+
+	mutex_lock(&dip_hw->dip_freebufferlist.queuelock);
+	if (list_empty(&dip_hw->dip_freebufferlist.queue)) {
+		mutex_unlock(&dip_hw->dip_freebufferlist.queuelock);
+
+		dev_err(&dip_dev->pdev->dev,
+			"%s: frame_no(%d), idx(0x%x), no free buffer(%d)\n",
+			__func__, dip_work->frameparams.frame_no,
+			dip_work->frameparams.index,
+			dip_hw->dip_freebufferlist.queue_cnt);
+
+		/*
+		 * Call callback to notify V4L2 common framework
+		 * for failure of enqueue
+		 */
+		dip_work->frameparams.state = FRAME_STATE_ERROR;
+		call_mtk_dip_pipe_finish(dip_hw, &dip_work->frameparams);
+
+		mutex_lock(&dip_hw->dip_useridlist.queuelock);
+		dip_work->user_id->num--;
+		mutex_unlock(&dip_hw->dip_useridlist.queuelock);
+
+		goto free_work_list;
+	}
+
+	buf = list_first_entry(&dip_hw->dip_freebufferlist.queue,
+			       struct mtk_dip_hw_subframe,
+			       list_entry);
+	list_del(&buf->list_entry);
+	dip_hw->dip_freebufferlist.queue_cnt--;
+	mutex_unlock(&dip_hw->dip_freebufferlist.queuelock);
+
+	mutex_lock(&dip_hw->dip_usedbufferlist.queuelock);
+	list_add_tail(&buf->list_entry, &dip_hw->dip_usedbufferlist.queue);
+	dip_hw->dip_usedbufferlist.queue_cnt++;
+	mutex_unlock(&dip_hw->dip_usedbufferlist.queuelock);
+
+	memcpy(&dip_work->frameparams.subfrm_data,
+	       &buf->buffer, sizeof(buf->buffer));
+	memset((char *)buf->buffer.va, 0, DIP_SUB_FRM_SZ);
+	memcpy(&dip_work->frameparams.config_data,
+	       &buf->config_data, sizeof(buf->config_data));
+	memset((char *)buf->config_data.va, 0, DIP_COMP_SZ);
+
+	if (dip_work->frameparams.tuning_data.pa == 0) {
+		dev_dbg(&dip_dev->pdev->dev,
+			"%s: frame_no(%d) has no tuning_data\n",
+			__func__, dip_work->frameparams.frame_no);
+
+		memcpy(&dip_work->frameparams.tuning_data,
+		       &buf->tuning_buf, sizeof(buf->tuning_buf));
+		memset((char *)buf->tuning_buf.va, 0, DIP_TUNING_SZ);
+		/*
+		 * When user enqueued without tuning buffer,
+		 * it would use driver internal buffer.
+		 * So, tuning_data.va should be 0
+		 */
+		dip_work->frameparams.tuning_data.va = 0;
+	}
+
+	dip_work->frameparams.drv_data = (u64)dip_hw;
+	dip_work->frameparams.state = FRAME_STATE_COMPOSING;
+
+	memcpy((void *)buf->frameparam.va, &dip_work->frameparams,
+	       sizeof(dip_work->frameparams));
+
+	dip_send(dip_hw->vpu_pdev, SCP_IPI_DIP_FRAME,
+		 (void *)&dip_work->frameparams,
+		 sizeof(dip_work->frameparams), 0);
+	num = atomic_inc_return(&dip_hw->num_composing);
+
+free_work_list:
+
+	dev_dbg(&dip_dev->pdev->dev,
+		"%s, free: frame_no(%d),idx(0x%x),worklist cnt(%d),composing num(%d)\n",
+		__func__, dip_work->frameparams.frame_no,
+		dip_work->frameparams.index, len, num);
+
+	kfree(dip_work);
+}
+
+static void mtk_dip_hw_set_clk(struct mtk_dip_hw *dip_hw, bool enable)
+{
+	struct mtk_dip_dev *dip_dev = mtk_dip_hw_to_dev(dip_hw);
+
+	if (enable) {
+		dev_dbg(&dip_dev->pdev->dev, "CCF:prepare_enable clk\n");
+		dip_enable_ccf_clock(dip_hw);
+	} else {
+		dev_dbg(&dip_dev->pdev->dev, "CCF:disable_unprepare clk\n");
+		dip_disable_ccf_clock(dip_hw);
+	}
+}
+
+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);
+	u32 i;
+	dma_addr_t scp_daddr;
+	u64 scp_mem_va;
+	int ret = 0;
+
+	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;
+	}
+
+	init_waitqueue_head(&dip_hw->dip_runner_thread.wq);
+
+	/*  All lists in DIP initialization */
+	INIT_LIST_HEAD(&dip_hw->dip_gcejoblist.queue);
+	spin_lock_init(&dip_hw->dip_gcejoblist.queuelock);
+	dip_hw->dip_gcejoblist.queue_cnt = 0;
+
+	INIT_LIST_HEAD(&dip_hw->dip_freebufferlist.queue);
+	mutex_init(&dip_hw->dip_freebufferlist.queuelock);
+	dip_hw->dip_freebufferlist.queue_cnt = 0;
+
+	INIT_LIST_HEAD(&dip_hw->dip_usedbufferlist.queue);
+	mutex_init(&dip_hw->dip_usedbufferlist.queuelock);
+	dip_hw->dip_usedbufferlist.queue_cnt = 0;
+
+	dip_hw->mdpcb_workqueue =
+		create_singlethread_workqueue("mdp_callback");
+	if (!dip_hw->mdpcb_workqueue) {
+		dev_err(&dip_dev->pdev->dev,
+			"%s: unable to alloc mdpcb workqueue\n", __func__);
+		ret = -ENOMEM;
+		goto err_alloc_mdpcb_wq;
+	}
+
+	dip_hw->composer_wq =
+		create_singlethread_workqueue("dip_composer");
+	if (!dip_hw->composer_wq) {
+		dev_err(&dip_dev->pdev->dev,
+			"%s: unable to alloc composer workqueue\n", __func__);
+		ret = -ENOMEM;
+		goto err_alloc_composer_wq;
+	}
+	init_waitqueue_head(&dip_hw->composing_wq);
+	init_waitqueue_head(&dip_hw->flushing_wq);
+
+	dip_hw->submit_work.dip_hw = dip_hw;
+	INIT_WORK(&dip_hw->submit_work.frame_work, dip_submit_worker);
+
+	INIT_LIST_HEAD(&dip_hw->dip_worklist.queue);
+	mutex_init(&dip_hw->dip_worklist.queuelock);
+	dip_hw->dip_worklist.queue_cnt = 0;
+
+	INIT_LIST_HEAD(&dip_hw->dip_useridlist.queue);
+	mutex_init(&dip_hw->dip_useridlist.queuelock);
+	dip_hw->dip_useridlist.queue_cnt = 0;
+
+	dip_hw->dip_runner_thread.thread =
+		kthread_run(dip_runner_func, (void *)dip_hw, "dip_runner");
+
+	if (IS_ERR(dip_hw->dip_runner_thread.thread)) {
+		dev_err(&dip_dev->pdev->dev, "%s: unable to alloc workqueue\n",
+			__func__);
+		ret = PTR_ERR(dip_hw->dip_runner_thread.thread);
+		dip_hw->dip_runner_thread.thread = NULL;
+		goto err_create_thread;
+	}
+
+	scp_mem_va = scp_get_reserve_mem_virt(SCP_DIP_MEM_ID);
+	scp_daddr = scp_get_reserve_mem_phys(SCP_DIP_MEM_ID);
+	dip_hw->scp_workingbuf_addr = scp_daddr + DIP_SCP_WORKINGBUF_OFFSET;
+	dev_dbg(&dip_dev->pdev->dev,
+		"%s: scp_mem_va(%p) ,pa(%p)\n", __func__, scp_mem_va,
+		(u64)scp_daddr);
+
+	scp_ipi_register(dip_hw->vpu_pdev, SCP_IPI_DIP_FRAME,
+			 dip_vpu_handler, NULL);
+
+	for (i = 0; i < DIP_SUB_FRM_DATA_NUM; i++) {
+		u32 size_align;
+		struct mtk_dip_hw_subframe *buf;
+		struct sg_table *sgt;
+		struct page **pages;
+		u32 npages, j;
+
+		buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+		if (!buf) {
+			ret = -ENOMEM;
+			goto err_create_thread;
+		}
+
+		/*
+		 * Total: 0 ~ 72 KB
+		 * SubFrame: 0 ~ 16 KB
+		 */
+		buf->buffer.pa = scp_daddr + i * DIP_FRM_SZ;
+		buf->buffer.va = scp_mem_va + i * DIP_FRM_SZ;
+
+		/* Tuning: 16 ~ 48 KB */
+		buf->tuning_buf.pa = buf->buffer.pa + DIP_TUNING_OFFSET;
+		buf->tuning_buf.va = buf->buffer.va + DIP_TUNING_OFFSET;
+
+		/* Config_data: 48 ~ 72 KB */
+		buf->config_data.pa = buf->buffer.pa + DIP_COMP_OFFSET;
+		buf->config_data.va = buf->buffer.va + DIP_COMP_OFFSET;
+
+		/* Frame parameters: 72 ~ 76 KB */
+		buf->frameparam.pa = buf->buffer.pa + DIP_FRAMEPARAM_OFFSET;
+		buf->frameparam.va = buf->buffer.va + DIP_FRAMEPARAM_OFFSET;
+
+		/* get iova */
+		npages = (DIP_SUB_FRM_SZ + DIP_TUNING_SZ) >> PAGE_SHIFT;
+		pages = kmalloc_array(npages,
+				      sizeof(struct page *),
+				      GFP_KERNEL);
+		if (!pages) {
+			kfree(buf);
+			ret = -ENOMEM;
+			goto err_create_thread;
+		}
+
+		sgt = &buf->table;
+		for (j = 0; j < npages; j++)
+			pages[j] =
+				phys_to_page(buf->buffer.pa + j * PAGE_SIZE);
+
+		size_align = round_up(DIP_SUB_FRM_SZ + DIP_TUNING_SZ,
+				      PAGE_SIZE);
+		ret = sg_alloc_table_from_pages(sgt, pages, npages,
+						0, size_align, GFP_KERNEL);
+		if (ret < 0) {
+			dev_err(&dip_dev->pdev->dev,
+				"%s: failed to get sgt from pages\n", __func__);
+			ret = -ENOMEM;
+			kfree(pages);
+			kfree(buf);
+			goto err_create_thread;
+		}
+
+		dma_map_sg_attrs(&dip_dev->pdev->dev, sgt->sgl, sgt->nents,
+				 DMA_BIDIRECTIONAL, DMA_ATTR_SKIP_CPU_SYNC);
+		buf->buffer.iova = sg_dma_address(buf->table.sgl);
+		buf->tuning_buf.iova = buf->buffer.iova +
+			DIP_TUNING_OFFSET;
+
+		dev_dbg(&dip_dev->pdev->dev,
+			"%s: buf(%d), pa(%p), iova(%p)\n",
+			__func__, i, buf->buffer.pa, buf->buffer.iova);
+
+		dev_dbg(&dip_dev->pdev->dev,
+			"%s: config_data(%d), pa(%p), iova(%p)\n",
+			__func__, i, buf->config_data.pa, buf->config_data.va);
+
+		dev_dbg(&dip_dev->pdev->dev,
+			"%s: tuning_buf(%d), pa(%p), iova(%p)\n",
+			__func__, i, buf->tuning_buf.pa, buf->tuning_buf.iova);
+
+		dev_dbg(&dip_dev->pdev->dev,
+			"%s: frameparam(%d), pa(%p), iova(%p)\n",
+			__func__, i, buf->frameparam.pa, buf->frameparam.va);
+
+		list_add_tail(&buf->list_entry,
+			      &dip_hw->dip_freebufferlist.queue);
+		dip_hw->dip_freebufferlist.queue_cnt++;
+		kfree(pages);
+	}
+
+	return 0;
+
+err_create_thread:
+	mutex_destroy(&dip_hw->dip_useridlist.queuelock);
+	mutex_destroy(&dip_hw->dip_worklist.queuelock);
+	mutex_destroy(&dip_hw->dip_usedbufferlist.queuelock);
+	mutex_destroy(&dip_hw->dip_freebufferlist.queuelock);
+
+err_alloc_composer_wq:
+	destroy_workqueue(dip_hw->composer_wq);
+
+err_alloc_mdpcb_wq:
+	destroy_workqueue(dip_hw->mdpcb_workqueue);
+
+	return ret;
+}
+
+static int mtk_dip_hw_res_release(struct mtk_dip_hw *dip_hw)
+{
+	u32 i = 0;
+	struct mtk_dip_dev *dip_dev = mtk_dip_hw_to_dev(dip_hw);
+	struct mtk_dip_hw_subframe *buf, *tmpbuf;
+	struct mtk_dip_hw_work *dip_work, *tmp_work;
+	struct mtk_dip_hw_user_id  *dip_userid, *tmp_id;
+
+	dev_dbg(&dip_dev->pdev->dev, "%s: composer work queue(%d)\n",
+		__func__, dip_hw->dip_worklist.queue_cnt);
+
+	mutex_lock(&dip_hw->dip_worklist.queuelock);
+	list_for_each_entry_safe(dip_work, tmp_work,
+				 &dip_hw->dip_worklist.queue,
+				 list_entry) {
+		list_del(&dip_work->list_entry);
+		dev_dbg(&dip_dev->pdev->dev, "%s: dip work frame no(%d)\n",
+			__func__, dip_work->frameparams.frame_no);
+		kfree(dip_work);
+		dip_hw->dip_worklist.queue_cnt--;
+	}
+	mutex_unlock(&dip_hw->dip_worklist.queuelock);
+
+	if (dip_hw->dip_worklist.queue_cnt != 0)
+		dev_dbg(&dip_dev->pdev->dev,
+			"%s: dip_worklist is not empty(%d)\n",
+			__func__, dip_hw->dip_worklist.queue_cnt);
+
+	mutex_lock(&dip_hw->dip_useridlist.queuelock);
+	list_for_each_entry_safe(dip_userid, tmp_id,
+				 &dip_hw->dip_useridlist.queue,
+				 list_entry) {
+		list_del(&dip_userid->list_entry);
+		dev_dbg(&dip_dev->pdev->dev, "%s: dip user id(0x%x)\n",
+			__func__, dip_userid->id);
+		kfree(dip_userid);
+		dip_hw->dip_useridlist.queue_cnt--;
+	}
+	mutex_unlock(&dip_hw->dip_useridlist.queuelock);
+
+	if (dip_hw->dip_useridlist.queue_cnt != 0)
+		dev_dbg(&dip_dev->pdev->dev,
+			"%s: dip_useridlist is not empty(%d)\n",
+			__func__, dip_hw->dip_useridlist.queue_cnt);
+
+	flush_workqueue(dip_hw->mdpcb_workqueue);
+	destroy_workqueue(dip_hw->mdpcb_workqueue);
+	dip_hw->mdpcb_workqueue = NULL;
+
+	flush_workqueue(dip_hw->composer_wq);
+	destroy_workqueue(dip_hw->composer_wq);
+	dip_hw->composer_wq = NULL;
+
+	atomic_set(&dip_hw->num_composing, 0);
+	atomic_set(&dip_hw->num_running, 0);
+
+	kthread_stop(dip_hw->dip_runner_thread.thread);
+	dip_hw->dip_runner_thread.thread = NULL;
+
+	atomic_set(&dip_hw->dip_user_cnt, 0);
+	atomic_set(&dip_hw->dip_stream_cnt, 0);
+	atomic_set(&dip_hw->dip_enque_cnt, 0);
+
+	/* All the buffer should be in the freebufferlist when release */
+	list_for_each_entry_safe(buf, tmpbuf,
+				 &dip_hw->dip_freebufferlist.queue,
+				 list_entry) {
+		struct sg_table *sgt = &buf->table;
+
+		dev_dbg(&dip_dev->pdev->dev,
+			"%s: buf(%d) pa(%p)\n", __func__, i,
+			buf->buffer.pa);
+		dip_hw->dip_freebufferlist.queue_cnt--;
+		dma_unmap_sg_attrs(&dip_dev->pdev->dev, sgt->sgl,
+				   sgt->orig_nents,
+				   DMA_BIDIRECTIONAL, DMA_ATTR_SKIP_CPU_SYNC);
+		sg_free_table(sgt);
+		list_del(&buf->list_entry);
+		kfree(buf);
+		buf = NULL;
+		i++;
+	}
+
+	if (dip_hw->dip_freebufferlist.queue_cnt != 0 &&
+	    i != DIP_SUB_FRM_DATA_NUM)
+		dev_err(&dip_dev->pdev->dev,
+			"%s: dip_freebufferlist is not empty (%d/%d)\n",
+			__func__, dip_hw->dip_freebufferlist.queue_cnt, i);
+
+	mutex_destroy(&dip_hw->dip_useridlist.queuelock);
+	mutex_destroy(&dip_hw->dip_worklist.queuelock);
+	mutex_destroy(&dip_hw->dip_usedbufferlist.queuelock);
+	mutex_destroy(&dip_hw->dip_freebufferlist.queuelock);
+
+	return 0;
+}
+
+static int mtk_dip_hw_flush_by_id(struct mtk_dip_hw *dip_hw,
+				  u16 id,
+			       struct mtk_dip_hw_user_id *user_id)
+{
+	struct mtk_dip_dev *dip_dev;
+	u32 num, err_cnt;
+	int ret;
+
+	dip_dev = mtk_dip_hw_to_dev(dip_hw);
+	err_cnt = 0;
+	do {
+		mutex_lock(&dip_hw->dip_useridlist.queuelock);
+		num = user_id->num;
+		mutex_unlock(&dip_hw->dip_useridlist.queuelock);
+
+		ret = wait_event_interruptible_timeout
+			(dip_hw->flushing_wq,
+			 (num == 0),
+			 msecs_to_jiffies(DIP_FLUSHING_WQ_TIMEOUT));
+
+		if (ret == -ERESTARTSYS)
+			dev_err(&dip_dev->pdev->dev,
+				"%s: interrupted by a signal, num(%d)\n",
+				__func__, num);
+		else if (ret == 0)
+			dev_dbg(&dip_dev->pdev->dev,
+				"%s: timeout num(%d)\n", __func__, num);
+		else
+			dev_dbg(&dip_dev->pdev->dev,
+				"%s: wakeup  num(%d)\n", __func__, num);
+
+		err_cnt++;
+
+		if (num > 0 && err_cnt >= DIP_MAX_ERR_COUNT) {
+			dev_err(&dip_dev->pdev->dev,
+				"%s: flushing is aborted, num(%d), err_cnt(%d)\n",
+				__func__, num, err_cnt);
+			return -EINVAL;
+		}
+
+	} while (num > 0);
+
+	dev_dbg(&dip_dev->pdev->dev, "Flushing is done num: %d\n", num);
+	return 0;
+}
+
+int mtk_dip_hw_connect(struct mtk_dip_hw *dip_hw)
+{
+	int ret;
+	s32 usercount;
+	struct mtk_dip_dev *dip_dev = mtk_dip_hw_to_dev(dip_hw);
+	phandle rproc_phandle;
+
+	if (!dip_hw) {
+		pr_err("%s: dip_hw can't be NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	usercount = atomic_inc_return(&dip_hw->dip_user_cnt);
+
+	if (usercount == 1) {
+		struct img_ipi_frameparam frameparam;
+
+		dip_hw->vpu_pdev = scp_get_pdev(dip_dev->pdev);
+		if (!dip_hw->vpu_pdev) {
+			dev_err(&dip_dev->pdev->dev,
+				"%s: failed to get VPU 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;
+		}
+
+		/*
+		 * Return 0 if downloading firmware successfully,
+		 * otherwise it is failed.
+		 */
+		if (rproc_boot(dip_hw->rproc_handle)) {
+			dev_err(&dip_dev->pdev->dev,
+				"%s: FW load failed(rproc:%p):%d\n",
+				__func__, dip_hw->rproc_handle,	ret);
+			return -EINVAL;
+		}
+
+		dev_dbg(&dip_dev->pdev->dev, "%s: FW loaded(rproc:%p)\n",
+			__func__, dip_hw->rproc_handle);
+
+		/* Enable clocks */
+		mtk_dip_hw_set_clk(dip_hw, true);
+		/* DIP HW INIT */
+		memset(&frameparam, 0, sizeof(frameparam));
+		/* SCP only support 32bits address */
+		frameparam.drv_data = (u64)dip_hw;
+		frameparam.state = FRAME_STATE_INIT;
+		dip_send(dip_hw->vpu_pdev, SCP_IPI_DIP_INIT,
+			 (void *)&frameparam, sizeof(frameparam), 0);
+
+		mtk_dip_hw_res_init(dip_hw);
+	}
+
+	dev_dbg(&dip_dev->pdev->dev, "%s: dip_hw connected, usercount(%d)\n",
+		__func__, usercount);
+
+	return 0;
+}
+
+int mtk_dip_hw_disconnect(struct mtk_dip_hw *dip_hw)
+{
+	struct mtk_dip_dev *dip_dev = mtk_dip_hw_to_dev(dip_hw);
+
+	if (atomic_dec_and_test(&dip_hw->dip_user_cnt)) {
+		mtk_dip_hw_res_release(dip_hw);
+		mtk_dip_hw_set_clk(dip_hw, false);
+	}
+
+	dev_dbg(&dip_dev->pdev->dev,
+		"%s: dip_hw disconnected, usercount(%d)\n",
+		__func__, atomic_read(&dip_hw->dip_user_cnt));
+
+	return 0;
+}
+
+int mtk_dip_hw_streamon(struct mtk_dip_hw  *dip_hw, u16 id)
+{
+	struct mtk_dip_dev *dip_dev = mtk_dip_hw_to_dev(dip_hw);
+	struct mtk_dip_hw_user_id *user_id;
+	s32 count, len;
+
+	count = atomic_inc_return(&dip_hw->dip_stream_cnt);
+
+	dev_dbg(&dip_dev->pdev->dev, "%s: id(0x%x)\n", __func__, id);
+
+	user_id = kzalloc(sizeof(*user_id), GFP_KERNEL);
+
+	if (!user_id)
+		return -ENOMEM;
+
+	user_id->id = id;
+	user_id->state = DIP_STATE_STREAMON;
+
+	mutex_lock(&dip_hw->dip_useridlist.queuelock);
+	list_add_tail(&user_id->list_entry, &dip_hw->dip_useridlist.queue);
+	dip_hw->dip_useridlist.queue_cnt++;
+	len = dip_hw->dip_useridlist.queue_cnt;
+	mutex_unlock(&dip_hw->dip_useridlist.queuelock);
+
+	dev_dbg(&dip_dev->pdev->dev,
+		"%s: stream count(%d),id(0x%x),len(%d)\n", __func__,
+		count, id, len);
+
+	return 0;
+}
+
+int mtk_dip_hw_streamoff(struct mtk_dip_hw  *dip_hw, u16 id)
+{
+	struct mtk_dip_hw_user_id *user_id;
+	struct mtk_dip_dev *dip_dev = mtk_dip_hw_to_dev(dip_hw);
+	s32 count = -1;
+	bool found = false;
+	int ret;
+
+	dev_dbg(&dip_dev->pdev->dev, "%s: streamoff id(0x%x)\n",
+		__func__, id);
+
+	mutex_lock(&dip_hw->dip_useridlist.queuelock);
+	list_for_each_entry(user_id,
+			    &dip_hw->dip_useridlist.queue, list_entry) {
+		if (user_id->id == id) {
+			user_id->state = DIP_STATE_STREAMOFF;
+			found = true;
+			break;
+		}
+	}
+	mutex_unlock(&dip_hw->dip_useridlist.queuelock);
+
+	if (found) {
+		ret = mtk_dip_hw_flush_by_id(dip_hw, id, user_id);
+		if (ret != 0) {
+			dev_err(&dip_dev->pdev->dev,
+				"%s: stream id(0x%x), streamoff err(%d)\n",
+				__func__, id, ret);
+			WARN_ON(1);
+		}
+
+		mutex_lock(&dip_hw->dip_useridlist.queuelock);
+		list_del(&user_id->list_entry);
+		dip_hw->dip_useridlist.queue_cnt--;
+		dev_dbg(&dip_dev->pdev->dev,
+			"%s: stream id(%x), user_id cnt(%d)\n",
+			__func__, id, dip_hw->dip_useridlist.queue_cnt);
+		mutex_unlock(&dip_hw->dip_useridlist.queuelock);
+
+		kfree(user_id);
+		user_id = NULL;
+		count = atomic_dec_return(&dip_hw->dip_stream_cnt);
+
+		dev_dbg(&dip_dev->pdev->dev, "%s: stream id(%d),cnt(%d)\n",
+			__func__, id, count);
+	} else {
+		dev_dbg(&dip_dev->pdev->dev,
+			"%s: stream id(%x) not found\n",
+			__func__, id);
+	}
+
+	if (count < 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+int mtk_dip_hw_enqueue(struct mtk_dip_hw *dip_hw,
+		       struct img_ipi_frameparam *frameparams)
+{
+	struct mtk_dip_hw_work	*framework = NULL;
+	struct mtk_dip_hw_user_id *user_id = NULL;
+	struct mtk_dip_dev *dip_dev = mtk_dip_hw_to_dev(dip_hw);
+	bool	found = false;
+	u32	tmpcount;
+
+	dev_dbg(&dip_dev->pdev->dev, "%s: frame idx(0x%x)",
+		__func__, frameparams->index);
+
+	mutex_lock(&dip_hw->dip_useridlist.queuelock);
+	list_for_each_entry(user_id, &dip_hw->dip_useridlist.queue,
+			    list_entry) {
+		if (mtk_dip_pipe_get_pipe_from_job_id(frameparams->index) ==
+			user_id->id) {
+			user_id->num++;
+			dev_dbg(&dip_dev->pdev->dev,
+				"%s: user_id(%x) found, current num(%d)\n",
+				__func__, user_id->id, user_id->num);
+			found = true;
+			break;
+		}
+	}
+	mutex_unlock(&dip_hw->dip_useridlist.queuelock);
+
+	if (!found) {
+		dev_err(&dip_dev->pdev->dev,
+			"%s: user_id(0x%x) not found, idx(0x%x)\n",
+			__func__,
+			mtk_dip_pipe_get_pipe_from_job_id(frameparams->index),
+			frameparams->index);
+		return -EINVAL;
+	}
+
+	framework = kzalloc(sizeof(*framework), GFP_KERNEL);
+	if (!framework)
+		return -ENOMEM;
+
+	memcpy(&framework->frameparams, frameparams, sizeof(*frameparams));
+	framework->frameparams.state = FRAME_STATE_INIT;
+	framework->frameparams.frame_no =
+		atomic_inc_return(&dip_hw->dip_enque_cnt);
+	framework->user_id = user_id;
+
+	mutex_lock(&dip_hw->dip_worklist.queuelock);
+	list_add_tail(&framework->list_entry, &dip_hw->dip_worklist.queue);
+	dip_hw->dip_worklist.queue_cnt++;
+	tmpcount = dip_hw->dip_worklist.queue_cnt;
+	mutex_unlock(&dip_hw->dip_worklist.queuelock);
+	dev_dbg(&dip_dev->pdev->dev,
+		"%s: frame_no(%d) into worklist, cnt(%d)\n",
+		__func__, framework->frameparams.frame_no, tmpcount);
+
+	queue_work(dip_hw->composer_wq, &dip_hw->submit_work.frame_work);
+	return 0;
+}
+
+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;
+	int ret = 0;
+
+	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->larb_dev = &larb_pdev->dev;
+
+	/* Grab clock */
+	dip_hw->dip_clk.img_larb5 = devm_clk_get(&pdev->dev,
+						 "DIP_CG_IMG_LARB5");
+	dip_hw->dip_clk.img_dip = devm_clk_get(&pdev->dev,
+					       "DIP_CG_IMG_DIP");
+	if (IS_ERR(dip_hw->dip_clk.img_larb5)) {
+		dev_err(&pdev->dev, "Cannot get img_larb5 clock\n");
+		return PTR_ERR(dip_hw->dip_clk.img_larb5);
+	}
+	if (IS_ERR(dip_hw->dip_clk.img_dip)) {
+		dev_err(&pdev->dev, "Cannot get img_dip clock\n");
+		return PTR_ERR(dip_hw->dip_clk.img_dip);
+	}
+
+	pm_runtime_enable(&pdev->dev);
+
+	atomic_set(&dip_hw->dip_user_cnt, 0);
+	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);
+	dip_hw->dip_worklist.queue_cnt = 0;
+
+	ret = mtk_dip_dev_v4l2_init(dip_dev);
+
+	if (ret)
+		dev_err(&pdev->dev, "v4l2 init failed(%d)\n", ret);
+
+	dev_info(&pdev->dev, "DIP driver probe\n");
+
+	return ret;
+}
+
+static int mtk_dip_remove(struct platform_device *pdev)
+{
+	mtk_dip_dev_v4l2_release(dev_get_drvdata(&pdev->dev));
+	pm_runtime_disable(&pdev->dev);
+
+	return 0;
+}
+
+static int __maybe_unused mtk_dip_pm_suspend(struct device *dev)
+{
+	struct mtk_dip_dev *dip_dev = dev_get_drvdata(dev);
+
+	if (atomic_read(&dip_dev->dip_hw.dip_user_cnt) > 0) {
+		mtk_dip_hw_set_clk(&dip_dev->dip_hw, false);
+		dev_dbg(&dip_dev->pdev->dev, "%s: disable clock\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);
+
+	if (atomic_read(&dip_dev->dip_hw.dip_user_cnt) > 0) {
+		mtk_dip_hw_set_clk(&dip_dev->dip_hw, true);
+		dev_dbg(&dip_dev->pdev->dev, "%s: enable clock\n",
+			__func__);
+	}
+
+	return 0;
+}
+
+static int __maybe_unused mtk_dip_suspend(struct device *dev)
+{
+	if (pm_runtime_suspended(dev))
+		return 0;
+
+	return mtk_dip_pm_suspend(dev);
+}
+
+static int __maybe_unused mtk_dip_resume(struct device *dev)
+{
+	if (pm_runtime_suspended(dev))
+		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	= DIP_DEV_NAME,
+		.pm	= &mtk_dip_pm_ops,
+		.of_match_table = mtk_dip_of_match,
+	}
+};
+
+module_platform_driver(mtk_dip_driver);
+
+MODULE_DESCRIPTION("Mediatek Camera DIP V4L2 driver");
+MODULE_LICENSE("GPL v2");
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..fa1c0d029208
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-v4l2.c
@@ -0,0 +1,1310 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Frederic Chen <frederic.chen-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.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 *dip_pipe = mtk_dip_subdev_to_pipe(sd);
+	struct mtk_dip_dev *dip_dev =
+		dev_get_drvdata(&dip_pipe->dip_dev->pdev->dev);
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"%s:%s: pipe(%d) connects to dip_hw\n",
+		__func__, dip_pipe->desc->name,
+		dip_pipe->desc->id);
+
+	dip_pipe->fh = fh;
+
+	mtk_dip_pipe_init_job_infos(dip_pipe);
+
+	return mtk_dip_hw_connect(&dip_dev->dip_hw);
+}
+
+static int mtk_dip_subdev_close(struct v4l2_subdev *sd,
+				struct v4l2_subdev_fh *fh)
+{
+	struct mtk_dip_pipe *dip_pipe = mtk_dip_subdev_to_pipe(sd);
+	struct mtk_dip_dev *dip_dev =
+		dev_get_drvdata(&dip_pipe->dip_dev->pdev->dev);
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"%s:%s: pipe(%d) disconnect to dip_hw\n",
+		__func__, dip_pipe->desc->name,
+		dip_pipe->desc->id);
+
+	return mtk_dip_hw_disconnect(&dip_dev->dip_hw);
+}
+
+static int mtk_dip_subdev_s_stream(struct v4l2_subdev *sd,
+				   int enable)
+{
+	struct mtk_dip_pipe *dip_pipe = mtk_dip_subdev_to_pipe(sd);
+	int ret;
+
+	if (enable)
+		ret = mtk_dip_pipe_streamon(dip_pipe);
+	else
+		ret = mtk_dip_pipe_streamoff(dip_pipe);
+
+	return ret;
+}
+
+static int mtk_dip_subdev_subscribe_event(struct v4l2_subdev *subdev,
+					  struct v4l2_fh *fh,
+					  struct v4l2_event_subscription *sub)
+{
+	switch (sub->type) {
+	case V4L2_EVENT_CTRL:
+		return v4l2_ctrl_subscribe_event(fh, sub);
+	default:
+		return -EINVAL;
+	}
+}
+
+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 *dip_pipe =
+		container_of(entity, struct mtk_dip_pipe, subdev.entity);
+	u32 pad = local->index;
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"%s: link setup, flags(0x%x), (%s)%d -->(%s)%d\n",
+		dip_pipe->desc->name,
+		flags,
+		local->entity->name,
+		local->index,
+		remote->entity->name,
+		remote->index);
+
+	WARN_ON(entity->obj_type != MEDIA_ENTITY_TYPE_V4L2_SUBDEV);
+
+	WARN_ON(pad >= dip_pipe->num_nodes);
+
+	dip_pipe->nodes[pad].enabled = !!(flags & MEDIA_LNK_FL_ENABLED);
+
+	return 0;
+}
+
+static int mtk_dip_vb2_buf_prepare(struct vb2_buffer *vb)
+{
+	struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
+
+	v4l2_buf->field = V4L2_FIELD_NONE;
+	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);
+
+	v4l2_buf->field = V4L2_FIELD_NONE;
+
+	return 0;
+}
+
+static void mtk_dip_vb2_buf_queue(struct vb2_buffer *vb)
+{
+	struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
+
+	v4l2_buf->field = V4L2_FIELD_NONE;
+}
+
+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 *dip_pipe = vb2_get_drv_priv(vq);
+	struct mtk_dip_video_device *node =
+		mtk_dip_vbq_to_node(vq);
+	struct device *dev = &dip_pipe->dip_dev->pdev->dev;
+	struct device *buf_alloc_ctx;
+
+	/* Get V4L2 format with the following method */
+	const struct v4l2_format *fmt = &node->vdev_fmt;
+	unsigned int size;
+
+	*num_buffers = clamp_val(*num_buffers, 1, VB2_MAX_FRAME);
+
+	if (node->desc->smem_alloc) {
+		buf_alloc_ctx = &dip_pipe->smem_alloc_dev->dev;
+		dev_dbg(dev, "%s:%s: select smem_vb2_alloc_ctx(%p)\n",
+			dip_pipe->desc->name,
+			node->desc->name,
+			buf_alloc_ctx);
+	} else {
+		buf_alloc_ctx = &dip_pipe->dip_dev->pdev->dev;
+		dev_dbg(dev, "%s:%s: select default_vb2_alloc_ctx(%p)\n",
+			dip_pipe->desc->name,
+			node->desc->name,
+			buf_alloc_ctx);
+	}
+
+	alloc_devs[0] = buf_alloc_ctx;
+
+	if (vq->type == V4L2_BUF_TYPE_META_CAPTURE ||
+	    vq->type == V4L2_BUF_TYPE_META_OUTPUT)
+		size = fmt->fmt.meta.buffersize;
+	else
+		size = fmt->fmt.pix_mp.plane_fmt[0].sizeimage;
+
+	if (*num_planes) {
+		if (sizes[0] < size) {
+			dev_dbg(dev, "%s:%s:%s: size error(user:%d, max:%d)\n",
+				__func__, dip_pipe->desc->name,
+				node->desc->name, sizes[0], size);
+			return -EINVAL;
+		}
+	} else {
+		*num_planes = 1;
+		sizes[0] = size;
+	}
+
+	dev_dbg(dev, "%s:%s:%s: n_planes(%d), n_bufs(%d), size(%d)\n",
+		__func__, dip_pipe->desc->name,
+		node->desc->name, *num_planes, *num_buffers, sizes[0]);
+
+	return 0;
+}
+
+static int
+	mtk_dip_all_nodes_streaming(struct mtk_dip_pipe *dip_pipe,
+				    struct mtk_dip_video_device *except)
+{
+	int i;
+
+	for (i = 0; i < dip_pipe->num_nodes; i++) {
+		struct mtk_dip_video_device *node = &dip_pipe->nodes[i];
+
+		if (node == except)
+			continue;
+		if (node->enabled &&
+		    !vb2_start_streaming_called(&node->dev_q.vbq))
+			return 0;
+	}
+
+	return 1;
+}
+
+static void mtk_dip_return_all_buffers(struct mtk_dip_pipe *dip_pipe,
+				       struct mtk_dip_video_device *node,
+				       enum vb2_buffer_state state)
+{
+	int i;
+
+	for (i = 0; i < node->dev_q.vbq.num_buffers; i++) {
+		if (node->dev_q.vbq.bufs[i]->state ==
+			VB2_BUF_STATE_ACTIVE)
+			vb2_buffer_done(node->dev_q.vbq.bufs[i],
+					state);
+	}
+}
+
+static int mtk_dip_vb2_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+	struct mtk_dip_pipe *dip_pipe = vb2_get_drv_priv(vq);
+	struct mtk_dip_video_device *node =
+		mtk_dip_vbq_to_node(vq);
+	int ret;
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"%s:%s:%s\n",
+		dip_pipe->desc->name, node->desc->name,
+		__func__);
+
+	if (!node->enabled) {
+		dev_err(&dip_pipe->dip_dev->pdev->dev,
+			"%s:%s: stream on failed, node is not enabled\n",
+			dip_pipe->desc->name, node->desc->name);
+		ret = -EINVAL;
+		goto fail_return_bufs;
+	}
+
+	ret = media_pipeline_start(&node->vdev.entity, &dip_pipe->pipeline);
+
+	if (ret < 0) {
+		dev_err(&dip_pipe->dip_dev->pdev->dev,
+			"%s:%s: media_pipeline_start failed(%d)\n",
+			dip_pipe->desc->name, node->desc->name,
+			ret);
+		goto fail_return_bufs;
+	}
+
+	if (!mtk_dip_all_nodes_streaming(dip_pipe, node))
+		return 0;
+
+	/* Start streaming of the whole pipeline */
+	ret = v4l2_subdev_call(&dip_pipe->subdev, video, s_stream, 1);
+	if (ret < 0) {
+		dev_err(&dip_pipe->dip_dev->pdev->dev,
+			"%s:%s: sub dev s_stream(1) failed(%d)\n",
+			dip_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(dip_pipe, node, VB2_BUF_STATE_QUEUED);
+
+	return ret;
+}
+
+static void mtk_dip_vb2_stop_streaming(struct vb2_queue *vq)
+{
+	struct mtk_dip_pipe *dip_pipe = vb2_get_drv_priv(vq);
+	struct mtk_dip_video_device *node =
+		mtk_dip_vbq_to_node(vq);
+	int ret;
+
+	WARN_ON(!node->enabled);
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"%s:%s:%s\n",
+		dip_pipe->desc->name, node->desc->name,
+		__func__);
+
+	if (mtk_dip_all_nodes_streaming(dip_pipe, node)) {
+		ret = v4l2_subdev_call(&dip_pipe->subdev, video, s_stream, 0);
+
+	if (ret)
+		dev_err(&dip_pipe->dip_dev->pdev->dev,
+			"%s:%s: sub dev s_stream(0) failed(%d)\n",
+			dip_pipe->desc->name, node->desc->name,
+			ret);
+	}
+
+	mtk_dip_return_all_buffers(dip_pipe, node, VB2_BUF_STATE_ERROR);
+	media_pipeline_stop(&node->vdev.entity);
+}
+
+static void mtk_dip_vb2_request_complete(struct vb2_buffer *vb)
+{
+	struct mtk_dip_video_device *node =
+		mtk_dip_vbq_to_node(vb->vb2_queue);
+
+	v4l2_ctrl_request_complete(vb->req_obj.req,
+				   &node->ctrl_handler);
+}
+
+static u32 mtk_dip_node_get_v4l2_cap(struct mtk_dip_pipe *dip_pipe,
+				     struct mtk_dip_video_device *node)
+{
+	return node->desc->cap;
+}
+
+static int mtk_dip_videoc_querycap(struct file *file, void *fh,
+				   struct v4l2_capability *cap)
+{
+	struct mtk_dip_pipe *dip_pipe = video_drvdata(file);
+
+	strlcpy(cap->driver, dip_pipe->desc->name,
+		sizeof(cap->driver));
+	strlcpy(cap->card, dip_pipe->desc->name,
+		sizeof(cap->card));
+	snprintf(cap->bus_info, sizeof(cap->bus_info),
+		 "platform:%s", dev_name(dip_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 *dip_pipe = video_drvdata(file);
+	struct mtk_dip_video_device *node = mtk_dip_file_to_node(file);
+	struct v4l2_format try_fmt;
+	int ret;
+
+	memset(&try_fmt, 0, sizeof(try_fmt));
+
+	try_fmt.type = node->dev_q.vbq.type;
+
+	ret = mtk_dip_pipe_set_img_fmt(dip_pipe, node, &f->fmt.pix_mp,
+				       &try_fmt.fmt.pix_mp);
+
+	if (ret)
+		mtk_dip_pipe_load_default_fmt(dip_pipe, node, &try_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 *dip_pipe = video_drvdata(file);
+
+	int ret;
+
+	if (dip_pipe->streaming)
+		return -EBUSY;
+
+	ret = mtk_dip_videoc_try_fmt(file, fh, f);
+
+	if (!ret)
+		node->vdev_fmt = *f;
+
+	return 0;
+}
+
+static int mtk_dip_videoc_enum_framesizes(struct file *file,
+					  void *priv,
+					  struct v4l2_frmsizeenum *sizes)
+{
+	struct mtk_dip_pipe *dip_pipe = video_drvdata(file);
+	struct mtk_dip_video_device *node = mtk_dip_file_to_node(file);
+	struct mtk_dip_dev_format *dev_fmt;
+
+	dev_fmt = mtk_dip_pipe_find_fmt(dip_pipe, node, sizes->pixel_format);
+
+	if (!dev_fmt || sizes->index)
+		return -EINVAL;
+
+	sizes->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
+
+	if (V4L2_TYPE_IS_OUTPUT(node->desc->buf_type)) {
+		sizes->stepwise.max_width = MTK_DIP_OUTPUT_MAX_WIDTH;
+		sizes->stepwise.min_width = MTK_DIP_OUTPUT_MIN_WIDTH;
+		sizes->stepwise.max_height = MTK_DIP_OUTPUT_MAX_HEIGHT;
+		sizes->stepwise.min_height = MTK_DIP_OUTPUT_MIN_HEIGHT;
+		sizes->stepwise.step_height = 1;
+		sizes->stepwise.step_width = 1;
+	} else {
+		sizes->stepwise.max_width = MTK_DIP_CAPTURE_MAX_WIDTH;
+		sizes->stepwise.min_width = MTK_DIP_CAPTURE_MIN_WIDTH;
+		sizes->stepwise.max_height = MTK_DIP_CAPTURE_MAX_HEIGHT;
+		sizes->stepwise.min_height = MTK_DIP_CAPTURE_MIN_HEIGHT;
+		sizes->stepwise.step_height = 1;
+		sizes->stepwise.step_width = 1;
+	}
+
+	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 ||
+	    f->type != node->dev_q.vbq.type)
+		return -EINVAL;
+
+	strscpy(f->description, node->desc->description,
+		sizeof(f->description));
+
+	f->pixelformat = node->desc->fmts[f->index].fmt.img.pixelformat;
+	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 || f->type != node->dev_q.vbq.type)
+		return -EINVAL;
+
+	strscpy(f->description, node->desc->description,
+		sizeof(f->description));
+
+	f->pixelformat = node->vdev_fmt.fmt.meta.dataformat;
+
+	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
+mtk_dip_vidioc_subscribe_event(struct v4l2_fh *fh,
+			       const struct v4l2_event_subscription *sub)
+{
+	switch (sub->type) {
+	case V4L2_EVENT_CTRL:
+		return v4l2_ctrl_subscribe_event(fh, sub);
+	default:
+		return -EINVAL;
+	}
+}
+
+/******************** function pointers ********************/
+
+/* subdev internal operations */
+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_core_ops mtk_dip_subdev_core_ops = {
+	.subscribe_event = mtk_dip_subdev_subscribe_event,
+	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
+};
+
+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 = {
+	.core = &mtk_dip_subdev_core_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 int mtk_dip_request_buf_validate(struct media_request *req,
+					int all_enable_node_need_buf)
+{
+	struct media_request_object *obj;
+	struct mtk_dip_pipe *dip_pipe;
+	struct mtk_dip_pipe *dip_dev_first;
+	struct vb2_buffer *vbs[MTK_DIP_VIDEO_NODE_ID_TOTAL_NUM] = {};
+	int count = 0;
+
+	if (!all_enable_node_need_buf)
+		return	vb2_request_validate(req);
+
+	list_for_each_entry(obj, &req->objects, list) {
+		struct vb2_buffer *vb;
+
+		if (vb2_request_object_is_buffer(obj)) {
+			struct mtk_dip_video_device *node;
+
+			vb = container_of(obj, struct vb2_buffer, req_obj);
+			node = mtk_dip_vbq_to_node(vb->vb2_queue);
+			dip_pipe = vb2_get_drv_priv(vb->vb2_queue);
+			vbs[node->desc->id] = vb;
+
+			if (count == 0)
+				dip_dev_first = dip_pipe;
+
+			if (dip_dev_first != dip_pipe) {
+				pr_err("Req(%p):found buf of different pipes(%p,%p)",
+				       req, dip_dev_first, dip_pipe);
+				return -EINVAL;
+			}
+		}
+	}
+
+	if (!dip_pipe) {
+		pr_debug("No dip dev found for the request\n");
+		return -EINVAL;
+	}
+
+	for (count = 0; count < MTK_DIP_VIDEO_NODE_ID_TOTAL_NUM; count++) {
+		if (dip_pipe->nodes[count].enabled) {
+			pr_debug("Node(%d:%s): vb(0x%x)\n",
+				 count, dip_pipe->nodes[count].desc->name,
+				 vbs[count]);
+
+			if (!vbs[count]) {
+				pr_debug("Node(%s) enable and no buf enqueue\n",
+					 dip_pipe->nodes[count].desc->name);
+				return -EINVAL;
+			}
+		}
+	}
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"%s:%s: all bufs found, ready for req(%p) enqueue\n",
+		__func__, dip_pipe->desc->name, req);
+
+	return vb2_request_validate(req);
+}
+
+static int mtk_dip_vb2_request_validate(struct media_request *req)
+{
+	return mtk_dip_request_buf_validate(req, 0);
+}
+
+static void mtk_dip_vb2_request_queue(struct media_request *req)
+{
+	vb2_request_queue(req);
+	mtk_dip_pipe_queue_buffers(req, 0);
+}
+
+static const struct media_device_ops mtk_dip_media_req_ops = {
+	.req_validate = mtk_dip_vb2_request_validate,
+	.req_queue = mtk_dip_vb2_request_queue,
+};
+
+static const struct vb2_ops mtk_dip_vb2_ops = {
+	.buf_queue = mtk_dip_vb2_buf_queue,
+	.queue_setup = mtk_dip_vb2_queue_setup,
+	.buf_prepare  = mtk_dip_vb2_buf_prepare,
+	.buf_out_validate = mtk_dip_vb2_buf_out_validate,
+	.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,
+	.buf_request_complete = mtk_dip_vb2_request_complete,
+};
+
+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
+};
+
+static void mtk_dip_node_to_v4l2(struct mtk_dip_pipe *dip_pipe,
+				 u32 idx,
+				 struct video_device *vdev,
+				 struct v4l2_format *f)
+{
+	u32 cap;
+	struct mtk_dip_video_device *node = &dip_pipe->nodes[idx];
+
+	cap = mtk_dip_node_get_v4l2_cap(dip_pipe, node);
+	vdev->ioctl_ops = node->desc->ops;
+	vdev->device_caps = V4L2_CAP_STREAMING | cap;
+	f->type = node->desc->buf_type;
+	mtk_dip_pipe_load_default_fmt(dip_pipe, node, f);
+}
+
+int mtk_dip_dev_media_register(struct device *dev,
+			       struct media_device *media_dev,
+			       const char *model)
+{
+	int ret = 0;
+
+	media_dev->dev = dev;
+	dev_dbg(dev, "setup media_dev.dev: %p\n",
+		media_dev->dev);
+
+	strlcpy(media_dev->model, model,
+		sizeof(media_dev->model));
+	dev_dbg(dev, "setup media_dev.model: %s\n",
+		media_dev->model);
+
+	snprintf(media_dev->bus_info, sizeof(media_dev->bus_info),
+		 "platform:%s", dev_name(dev));
+	dev_dbg(dev, "setup media_dev.bus_info: %s\n",
+		media_dev->bus_info);
+
+	media_dev->hw_revision = 0;
+	dev_dbg(dev, "setup media_dev.hw_revision: %d\n",
+		media_dev->hw_revision);
+
+	media_dev->ops = &mtk_dip_media_req_ops;
+
+	dev_dbg(dev, "media_device_init: media_dev:%p\n",
+		media_dev);
+	media_device_init(media_dev);
+
+	pr_debug("Register media device: %s, %p",
+		 media_dev->model,
+		media_dev);
+
+	ret = media_device_register(media_dev);
+
+	if (ret) {
+		dev_err(dev, "failed to register media device (%d)\n", ret);
+		goto fail_media_dev;
+	}
+	return 0;
+
+fail_media_dev:
+	media_device_unregister(media_dev);
+	media_device_cleanup(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 = 0;
+	/* Set up v4l2 device */
+	v4l2_dev->mdev = media_dev;
+	dev_dbg(dev, "setup v4l2_dev->mdev: %p",
+		v4l2_dev->mdev);
+	v4l2_dev->ctrl_handler = NULL;
+	dev_dbg(dev, "setup v4l2_dev->ctrl_handler: %p",
+		v4l2_dev->ctrl_handler);
+
+	pr_debug("Register v4l2 device: %p",
+		 v4l2_dev);
+
+	ret = v4l2_device_register(dev, v4l2_dev);
+
+	if (ret) {
+		dev_err(dev, "failed to register V4L2 device (%d)\n", ret);
+		goto fail_v4l2_dev;
+	}
+
+	return 0;
+
+fail_v4l2_dev:
+	media_device_unregister(media_dev);
+	media_device_cleanup(media_dev);
+
+	return ret;
+}
+
+int mtk_dip_pipe_v4l2_register(struct mtk_dip_pipe *dip_pipe,
+			       struct media_device *media_dev,
+			       struct v4l2_device *v4l2_dev)
+{
+	int i, ret;
+
+	/* Initialize miscellaneous variables */
+	dip_pipe->streaming = 0;
+
+	/* Initialize subdev media entity */
+	dip_pipe->subdev_pads = kcalloc(dip_pipe->num_nodes,
+					sizeof(*dip_pipe->subdev_pads),
+					GFP_KERNEL);
+	if (!dip_pipe->subdev_pads) {
+		ret = -ENOMEM;
+		goto fail_subdev_pads;
+	}
+
+	ret = media_entity_pads_init(&dip_pipe->subdev.entity,
+				     dip_pipe->num_nodes,
+				     dip_pipe->subdev_pads);
+	if (ret) {
+		dev_err(&dip_pipe->dip_dev->pdev->dev,
+			"failed initialize subdev media entity (%d)\n", ret);
+		goto fail_media_entity;
+	}
+
+	/* Initialize subdev */
+	v4l2_subdev_init(&dip_pipe->subdev, &mtk_dip_subdev_ops);
+
+	dip_pipe->subdev.entity.function =
+		MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
+
+	dip_pipe->subdev.entity.ops = &mtk_dip_media_ops;
+
+	for (i = 0; i < dip_pipe->num_nodes; i++) {
+		struct mtk_dip_video_device_desc *desc =
+			dip_pipe->nodes[i].desc;
+
+		dip_pipe->subdev_pads[i].flags =
+			V4L2_TYPE_IS_OUTPUT(desc->buf_type) ?
+			MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
+	}
+
+	dip_pipe->subdev.flags =
+		V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
+	snprintf(dip_pipe->subdev.name, sizeof(dip_pipe->subdev.name),
+		 "%s", dip_pipe->desc->name);
+	v4l2_set_subdevdata(&dip_pipe->subdev, dip_pipe);
+	dip_pipe->subdev.ctrl_handler = &dip_pipe->ctrl_handler;
+	dip_pipe->subdev.internal_ops = &mtk_dip_subdev_internal_ops;
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"register subdev: %s, ctrl_handler %p\n",
+		 dip_pipe->subdev.name, dip_pipe->subdev.ctrl_handler);
+	ret = v4l2_device_register_subdev(&dip_pipe->dip_dev->v4l2_dev,
+					  &dip_pipe->subdev);
+	if (ret) {
+		dev_err(&dip_pipe->dip_dev->pdev->dev,
+			"failed initialize subdev (%d)\n", ret);
+		goto fail_subdev;
+	}
+
+	ret = v4l2_device_register_subdev_nodes(&dip_pipe->dip_dev->v4l2_dev);
+	if (ret) {
+		dev_err(&dip_pipe->dip_dev->pdev->dev,
+			"failed to register subdevs (%d)\n", ret);
+		goto fail_subdevs;
+	}
+
+	/* Create video nodes and links */
+	for (i = 0; i < dip_pipe->num_nodes; i++) {
+		struct mtk_dip_video_device *node = &dip_pipe->nodes[i];
+		struct video_device *vdev = &node->vdev;
+		struct vb2_queue *vbq = &node->dev_q.vbq;
+		struct mtk_dip_video_device_desc *desc = node->desc;
+		u32 flags;
+
+		/* Initialize miscellaneous variables */
+		mutex_init(&node->dev_q.lock);
+
+		/* Initialize formats to default values */
+		mtk_dip_node_to_v4l2(dip_pipe, i, vdev, &node->vdev_fmt);
+
+		/* Initialize media entities */
+		ret = media_entity_pads_init(&vdev->entity, 1, &node->vdev_pad);
+		if (ret) {
+			dev_err(&dip_pipe->dip_dev->pdev->dev,
+				"failed initialize media entity (%d)\n", ret);
+			goto fail_vdev_media_entity;
+		}
+
+		node->vdev_pad.flags = V4L2_TYPE_IS_OUTPUT(desc->buf_type) ?
+			MEDIA_PAD_FL_SOURCE : MEDIA_PAD_FL_SINK;
+		vdev->entity.ops = NULL;
+
+		/* 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;
+		/* Put the process hub sub device in the vb2 private data*/
+		vbq->drv_priv = dip_pipe;
+		vbq->lock = &node->dev_q.lock;
+		ret = vb2_queue_init(vbq);
+		if (ret) {
+			dev_err(&dip_pipe->dip_dev->pdev->dev,
+				"failed to initialize video queue (%d)\n", ret);
+			goto fail_vdev;
+		}
+
+		/* Initialize vdev */
+		snprintf(vdev->name, sizeof(vdev->name), "%s %s",
+			 dip_pipe->desc->name,
+			 node->desc->name);
+		vdev->release = video_device_release_empty;
+		vdev->fops = &mtk_dip_v4l2_fops;
+		vdev->lock = &node->dev_q.lock;
+		vdev->ctrl_handler = &dip_pipe->nodes[i].ctrl_handler;
+		vdev->v4l2_dev = &dip_pipe->dip_dev->v4l2_dev;
+		vdev->queue = &node->dev_q.vbq;
+		vdev->vfl_dir = V4L2_TYPE_IS_OUTPUT(desc->buf_type) ?
+			VFL_DIR_TX : VFL_DIR_RX;
+		video_set_drvdata(vdev, dip_pipe);
+		pr_debug("register vdev: %s\n", vdev->name);
+		ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+		if (ret) {
+			dev_err(&dip_pipe->dip_dev->pdev->dev,
+				"failed to register video device (%d)\n", ret);
+			goto fail_vdev;
+		}
+
+		/* Create link between video node and the subdev pad */
+		flags = 0;
+		if (desc->dynamic)
+			flags |= MEDIA_LNK_FL_DYNAMIC;
+		if (node->enabled)
+			flags |= MEDIA_LNK_FL_ENABLED;
+		if (node->immutable)
+			flags |= MEDIA_LNK_FL_IMMUTABLE;
+
+		if (V4L2_TYPE_IS_OUTPUT(desc->buf_type))
+			ret = media_create_pad_link(&vdev->entity, 0,
+						    &dip_pipe->subdev.entity,
+						    i, flags);
+		else
+			ret = media_create_pad_link(&dip_pipe->subdev.entity,
+						    i, &vdev->entity, 0,
+						    flags);
+
+		if (ret)
+			goto fail_link;
+	}
+
+	return 0;
+
+	for (; i >= 0; i--) {
+fail_link:
+		video_unregister_device(&dip_pipe->nodes[i].vdev);
+fail_vdev:
+		vb2_queue_release(&dip_pipe->nodes[i].dev_q.vbq);
+		media_entity_cleanup(&dip_pipe->nodes[i].vdev.entity);
+fail_vdev_media_entity:
+		mutex_destroy(&dip_pipe->nodes[i].dev_q.lock);
+	}
+fail_subdevs:
+	v4l2_device_unregister_subdev(&dip_pipe->subdev);
+fail_subdev:
+	media_entity_cleanup(&dip_pipe->subdev.entity);
+fail_media_entity:
+	kfree(dip_pipe->subdev_pads);
+fail_subdev_pads:
+	v4l2_device_unregister(&dip_pipe->dip_dev->v4l2_dev);
+	pr_err("fail_v4l2_dev: media_device_unregister and clenaup:%p",
+	       &dip_pipe->dip_dev->mdev);
+	media_device_unregister(&dip_pipe->dip_dev->mdev);
+	media_device_cleanup(&dip_pipe->dip_dev->mdev);
+
+	return ret;
+}
+
+int mtk_dip_pipe_v4l2_unregister(struct mtk_dip_pipe *dip_pipe)
+{
+	unsigned int i;
+
+	for (i = 0; i < dip_pipe->num_nodes; i++) {
+		video_unregister_device(&dip_pipe->nodes[i].vdev);
+		vb2_queue_release(&dip_pipe->nodes[i].dev_q.vbq);
+		media_entity_cleanup(&dip_pipe->nodes[i].vdev.entity);
+		mutex_destroy(&dip_pipe->nodes[i].dev_q.lock);
+	}
+
+	v4l2_device_unregister_subdev(&dip_pipe->subdev);
+	media_entity_cleanup(&dip_pipe->subdev.entity);
+	kfree(dip_pipe->subdev_pads);
+	v4l2_device_unregister(&dip_pipe->dip_dev->v4l2_dev);
+	media_device_unregister(&dip_pipe->dip_dev->mdev);
+	media_device_cleanup(&dip_pipe->dip_dev->mdev);
+
+	return 0;
+}
+
+void mtk_dip_v4l2_buffer_done(struct vb2_buffer *vb,
+			      enum vb2_buffer_state state)
+{
+	struct mtk_dip_pipe *dip_pipe;
+	struct mtk_dip_video_device *node;
+
+	dip_pipe = vb2_get_drv_priv(vb->vb2_queue);
+	node = mtk_dip_vbq_to_node(vb->vb2_queue);
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"%s:%s: return buf, idx(%d), state(%d)\n",
+		dip_pipe->desc->name, node->desc->name,
+		vb->index, state);
+	vb2_buffer_done(vb, state);
+}
+
+/********************************************
+ * 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 = mtk_dip_vidioc_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 = mtk_dip_vidioc_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 struct mtk_dip_dev_format fw_param_fmts[] = {
+	{
+		.fmt.meta = {
+			.dataformat = V4L2_META_FMT_MTISP_PARAMS,
+			.max_buffer_size = 1024 * 30,
+		},
+	},
+};
+
+static struct mtk_dip_dev_format in_fmts[] = {
+	{
+		.fmt.img = {
+			.pixelformat = V4L2_PIX_FMT_MTISP_B10,
+			.mdp_color = MDP_COLOR_BAYER10,
+			.colorspace = V4L2_COLORSPACE_SRGB,
+			.depth = { 10 },
+			.row_depth = { 10 },
+			.num_planes = 1,
+		},
+	},
+	{
+		.fmt.img = {
+			.pixelformat = V4L2_PIX_FMT_MTISP_F10,
+			.mdp_color = MDP_COLOR_FULLG10,
+			.colorspace = V4L2_COLORSPACE_RAW,
+			.depth = { 15 },
+			.row_depth = { 15 },
+			.num_planes = 1,
+		},
+	},
+	{
+		.fmt.img = {
+			.pixelformat = V4L2_PIX_FMT_VYUY,
+			.mdp_color = MDP_COLOR_VYUY,
+			.colorspace = V4L2_COLORSPACE_BT2020,
+			.depth	 = { 16 },
+			.row_depth = { 16 },
+			.num_planes = 1,
+		},
+	},
+	{
+		.fmt.img = {
+			.pixelformat = V4L2_PIX_FMT_YUYV,
+			.mdp_color = MDP_COLOR_YUYV,
+			.colorspace = V4L2_COLORSPACE_BT2020,
+			.depth	 = { 16 },
+			.row_depth = { 16 },
+			.num_planes = 1,
+		},
+	},
+	{
+		.fmt.img = {
+			.pixelformat = V4L2_PIX_FMT_YVYU,
+			.mdp_color = MDP_COLOR_YVYU,
+			.colorspace = V4L2_COLORSPACE_BT2020,
+			.depth	 = { 16 },
+			.row_depth = { 16 },
+			.num_planes = 1,
+		},
+	},
+	{
+		.fmt.img = {
+			.pixelformat = V4L2_PIX_FMT_NV12,
+			.mdp_color = MDP_COLOR_NV12,
+			.colorspace = V4L2_COLORSPACE_BT2020,
+			.depth = { 12 },
+			.row_depth = { 8 },
+			.num_planes = 1,
+		},
+	}
+};
+
+static struct mtk_dip_dev_format out_fmts[] = {
+	{
+		.fmt.img = {
+			.pixelformat = V4L2_PIX_FMT_VYUY,
+			.mdp_color = MDP_COLOR_VYUY,
+			.colorspace = MDP_YCBCR_PROFILE_BT601,
+			.depth = { 16 },
+			.row_depth = { 16 },
+			.num_planes = 1,
+		},
+	},
+	{
+		.fmt.img = {
+			.pixelformat = V4L2_PIX_FMT_YUYV,
+			.mdp_color = MDP_COLOR_YUYV,
+			.colorspace = MDP_YCBCR_PROFILE_BT601,
+			.depth = { 16 },
+			.row_depth = { 16 },
+			.num_planes = 1,
+		},
+	},
+	{
+		.fmt.img = {
+			.pixelformat = V4L2_PIX_FMT_YVYU,
+			.mdp_color = MDP_COLOR_YVYU,
+			.colorspace = MDP_YCBCR_PROFILE_BT601,
+			.depth = { 16 },
+			.row_depth = { 16 },
+			.num_planes = 1,
+		},
+	},
+	{
+		.fmt.img = {
+			.pixelformat = V4L2_PIX_FMT_YVU420,
+			.mdp_color = MDP_COLOR_YV12,
+			.colorspace = MDP_YCBCR_PROFILE_BT601,
+			.depth = { 12 },
+			.row_depth = { 8 },
+			.num_planes = 1,
+		},
+	},
+	{
+		.fmt.img = {
+			.pixelformat = V4L2_PIX_FMT_NV12,
+			.mdp_color = MDP_COLOR_NV12,
+			.colorspace = MDP_YCBCR_PROFILE_BT601,
+			.depth = { 12 },
+			.row_depth = { 8 },
+			.num_planes = 1,
+		},
+	}
+};
+
+static 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,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+		.dynamic = 0,
+		.smem_alloc = 0,
+		.default_enable = 1,
+		.fmts = in_fmts,
+		.num_fmts = ARRAY_SIZE(in_fmts),
+		.default_fmt_idx = 0,
+		.default_width = MTK_DIP_CAPTURE_MAX_WIDTH,
+		.default_height = MTK_DIP_CAPTURE_MAX_HEIGHT,
+		.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,
+		.buf_type = V4L2_BUF_TYPE_META_OUTPUT,
+		.dynamic = 0,
+		.smem_alloc = 1,
+		.default_enable = 0,
+		.fmts = fw_param_fmts,
+		.num_fmts = 1,
+		.default_fmt_idx = 0,
+		.ops = &mtk_dip_v4l2_meta_out_ioctl_ops,
+		.description = "Tuning data",
+	},
+};
+
+static 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,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+		.dynamic = 0,
+		.smem_alloc = 0,
+		.default_enable = 1,
+		.fmts = in_fmts,
+		.num_fmts = ARRAY_SIZE(in_fmts),
+		.default_fmt_idx = 5,
+		.default_width = MTK_DIP_CAPTURE_MAX_WIDTH,
+		.default_height = MTK_DIP_CAPTURE_MAX_HEIGHT,
+		.ops = &mtk_dip_v4l2_video_out_ioctl_ops,
+		.description = "Source image to be process",
+
+	},
+	{
+		.id = MTK_DIP_VIDEO_NODE_ID_TUNING_OUT,
+		.name = "Tuning",
+		.dynamic = 0,
+		.cap = V4L2_CAP_META_OUTPUT,
+		.buf_type = V4L2_BUF_TYPE_META_OUTPUT,
+		.smem_alloc = 1,
+		.default_enable = 0,
+		.fmts = fw_param_fmts,
+		.num_fmts = 1,
+		.default_fmt_idx = 0,
+		.ops = &mtk_dip_v4l2_meta_out_ioctl_ops,
+		.description = "Tuning data for image enhancement",
+	},
+};
+
+static 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,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.dynamic = 1,
+		.smem_alloc = 0,
+		.default_enable = 1,
+		.fmts = out_fmts,
+		.num_fmts = ARRAY_SIZE(out_fmts),
+		.default_fmt_idx = 1,
+		.default_width = MTK_DIP_OUTPUT_MAX_WIDTH,
+		.default_height = MTK_DIP_OUTPUT_MAX_HEIGHT,
+		.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,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.dynamic = 1,
+		.smem_alloc = 0,
+		.default_enable = 1,
+		.fmts = out_fmts,
+		.num_fmts = ARRAY_SIZE(out_fmts),
+		.default_fmt_idx = 1,
+		.default_width = MTK_DIP_OUTPUT_MAX_WIDTH,
+		.default_height = MTK_DIP_OUTPUT_MAX_HEIGHT,
+		.ops = &mtk_dip_v4l2_video_cap_ioctl_ops,
+		.description = "Output quality enhanced image",
+
+	},
+};
+
+static 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,
+	},
+};
+
+int mtk_dip_dev_v4l2_init(struct mtk_dip_dev *dip_dev)
+{
+	struct media_device *media_dev;
+	struct v4l2_device *v4l2_dev;
+	struct mtk_dip_smem_dev *smem_alloc_dev = &dip_dev->smem_alloc_dev;
+	int i;
+	int ret = 0;
+
+	media_dev = &dip_dev->mdev;
+	v4l2_dev = &dip_dev->v4l2_dev;
+
+	ret = mtk_dip_dev_media_register(&dip_dev->pdev->dev,
+					 media_dev,
+					 MTK_DIP_DEV_DIP_MEDIA_MODEL_NAME);
+
+	ret = mtk_dip_dev_v4l2_register(&dip_dev->pdev->dev,
+					media_dev,
+					v4l2_dev);
+
+	ret = mtk_dip_smem_alloc_dev_init(smem_alloc_dev, &dip_dev->pdev->dev);
+
+	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, smem_alloc_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);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+void mtk_dip_dev_v4l2_release(struct mtk_dip_dev *dip_dev)
+{
+	int i = 0;
+
+	if (dip_dev)
+		for (i = 0; i < MTK_DIP_PIPE_ID_TOTAL_NUM; i++)
+			mtk_dip_pipe_release(&dip_dev->dip_pipe[i]);
+
+	mtk_dip_smem_alloc_dev_release(&dip_dev->smem_alloc_dev);
+}
+
-- 
2.18.0

WARNING: multiple messages have this Message-ID (diff)
From: Frederic Chen <frederic.chen@mediatek.com>
To: <hans.verkuil@cisco.com>,
	<laurent.pinchart+renesas@ideasonboard.com>, <tfiga@chromium.org>,
	<matthias.bgg@gmail.com>, <mchehab@kernel.org>
Cc: <yuzhao@chromium.org>, <zwisler@chromium.org>,
	<linux-mediatek@lists.infradead.org>,
	<linux-arm-kernel@lists.infradead.org>, <Sean.Cheng@mediatek.com>,
	<sj.huang@mediatek.com>, <christie.yu@mediatek.com>,
	<holmes.chiou@mediatek.com>, <frederic.chen@mediatek.com>,
	<Jerry-ch.Chen@mediatek.com>, <jungo.lin@mediatek.com>,
	<Rynn.Wu@mediatek.com>, <linux-media@vger.kernel.org>,
	<srv_heupstream@mediatek.com>, <devicetree@vger.kernel.org>,
	<shik@chromium.org>, <suleiman@chromium.org>
Subject: [RFC PATCH V1 6/6] platform: mtk-isp: Add Mediatek DIP driver
Date: Wed, 17 Apr 2019 18:45:11 +0800	[thread overview]
Message-ID: <20190417104511.21514-7-frederic.chen@mediatek.com> (raw)
In-Reply-To: <20190417104511.21514-1-frederic.chen@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.chen@mediatek.com>
---
 drivers/media/platform/mtk-isp/Makefile       |   18 +
 .../media/platform/mtk-isp/isp_50/Makefile    |   17 +
 .../platform/mtk-isp/isp_50/dip/Makefile      |   32 +
 .../mtk-isp/isp_50/dip/mtk_dip-ctrl.c         |  124 ++
 .../platform/mtk-isp/isp_50/dip/mtk_dip-dev.c | 1116 +++++++++++++
 .../platform/mtk-isp/isp_50/dip/mtk_dip-dev.h |  321 ++++
 .../platform/mtk-isp/isp_50/dip/mtk_dip-hw.h  |  167 ++
 .../mtk-isp/isp_50/dip/mtk_dip-smem.c         |  322 ++++
 .../mtk-isp/isp_50/dip/mtk_dip-smem.h         |   39 +
 .../platform/mtk-isp/isp_50/dip/mtk_dip-sys.c | 1384 +++++++++++++++++
 .../mtk-isp/isp_50/dip/mtk_dip-v4l2.c         | 1310 ++++++++++++++++
 11 files changed, 4850 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-ctrl.c
 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-smem.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-smem.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..24bc5354e2f6
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/Makefile
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2018 MediaTek Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+
+obj-$(CONFIG_VIDEO_MEDIATEK_ISP_COMMON) += common/
+
+obj-y += isp_50/
+
+obj-$(CONFIG_VIDEO_MEDIATEK_ISP_FD_SUPPORT) += fd/
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..fd0e5bd3c781
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/Makefile
@@ -0,0 +1,17 @@
+#
+# Copyright (C) 2018 MediaTek Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+
+ifeq ($(CONFIG_VIDEO_MEDIATEK_ISP_DIP_SUPPORT),y)
+obj-y += dip/
+endif
+
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..03137416857b
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/dip/Makefile
@@ -0,0 +1,32 @@
+#
+# Copyright (C) 2018 MediaTek Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+$(info $(srctree))
+ccflags-y += -I$(srctree)/drivers/media/platform/mtk-mdp3
+
+obj-y += mtk_dip-sys.o
+
+# To provide alloc context managing memory shared
+# between CPU and ISP coprocessor
+mtk_dip_smem-objs := \
+mtk_dip-smem.o
+
+obj-y += mtk_dip_smem.o
+
+# Utilits to provide frame-based streaming model
+# with v4l2 user interfaces
+mtk_dip_util-objs := \
+mtk_dip-dev.o \
+mtk_dip-v4l2.o \
+mtk_dip-ctrl.o \
+
+obj-y += mtk_dip_util.o
diff --git a/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-ctrl.c b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-ctrl.c
new file mode 100644
index 000000000000..e35574818120
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-ctrl.c
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Frederic Chen <frederic.chen@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include "mtk_dip-dev.h"
+
+static void handle_buf_usage_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 < MTK_DIP_V4l2_BUF_USAGE_DEFAULT ||
+	    ctrl->val >= MTK_DIP_V4l2_BUF_USAGE_NONE) {
+		pr_err("Invalid buffer usage id %d", ctrl->val);
+		return;
+	}
+	node->dev_q.buffer_usage = ctrl->val;
+}
+
+static void 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("Invalid buffer rotation %d", ctrl->val);
+		return;
+	}
+	node->dev_q.rotation = ctrl->val;
+}
+
+static int mtk_dip_video_device_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	switch (ctrl->id) {
+	case V4L2_CID_PRIVATE_SET_BUFFER_USAGE:
+		handle_buf_usage_config(ctrl);
+		break;
+	case V4L2_CID_ROTATE:
+		handle_buf_rotate_config(ctrl);
+		break;
+	default:
+			break;
+	}
+	return 0;
+}
+
+static const struct v4l2_ctrl_ops mtk_dip_video_device_ctrl_ops = {
+	.s_ctrl = mtk_dip_video_device_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config mtk_dip_buf_usage_config = {
+	.ops	= &mtk_dip_video_device_ctrl_ops,
+	.id	= V4L2_CID_PRIVATE_SET_BUFFER_USAGE,
+	.name	= "MTK ISP SET BUFFER USAGE",
+	.type	= V4L2_CTRL_TYPE_INTEGER,
+	.min	= MTK_DIP_V4l2_BUF_USAGE_DEFAULT,
+	.max	= MTK_DIP_V4l2_BUF_USAGE_POSTPROC,
+	.step	= 1,
+	.def	= MTK_DIP_V4l2_BUF_USAGE_DEFAULT,
+	.flags	= V4L2_CTRL_FLAG_SLIDER | V4L2_CTRL_FLAG_EXECUTE_ON_WRITE,
+	};
+
+int mtk_dip_ctrl_init(struct mtk_dip_pipe *dip_pipe)
+{
+	struct v4l2_ctrl_handler *hdl = &dip_pipe->ctrl_handler;
+	struct mtk_dip_video_device *node;
+	int i;
+	int img_nodes_to_be_init[3] = {
+		MTK_DIP_VIDEO_NODE_ID_RAW_OUT,
+		MTK_DIP_VIDEO_NODE_ID_MDP0_CAPTURE,
+		MTK_DIP_VIDEO_NODE_ID_MDP1_CAPTURE
+	};
+
+	v4l2_ctrl_handler_init(hdl, V4L2_CID_MTK_DIP_MAX);
+
+	pr_debug("%s init ctrl: %p\n", __func__, hdl);
+
+	if (hdl->error) {
+		pr_err("Failed in v4l2_ctrl_handler_init\n");
+		return hdl->error;
+	}
+
+	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);
+
+	for (i = 0; i < ARRAY_SIZE(img_nodes_to_be_init); i++) {
+		node = &dip_pipe->nodes[img_nodes_to_be_init[i]];
+
+		if (v4l2_ctrl_new_custom(&node->ctrl_handler,
+					 &mtk_dip_buf_usage_config,
+					 NULL) == NULL)
+			dev_err(&dip_pipe->dip_dev->pdev->dev,
+				"Node(%s) create buf_usage_config ctrl failed:(%d)",
+				node->desc->name,
+				node->ctrl_handler.error);
+
+		if (v4l2_ctrl_new_std(&dip_pipe->ctrl_handler,
+				      &mtk_dip_video_device_ctrl_ops,
+			V4L2_CID_ROTATE, 0, 270, 90, 0)	== NULL)
+			dev_err(&dip_pipe->dip_dev->pdev->dev,
+				"Node(%s) create rotate ctrl failed:(%d)",
+				node->desc->name, node->ctrl_handler.error);
+	}
+
+return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_dip_ctrl_init);
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..9f450dae2820
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-dev.c
@@ -0,0 +1,1116 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Frederic Chen <frederic.chen@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <media/videobuf2-dma-contig.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <media/videobuf2-dma-contig.h>
+#include <linux/dma-mapping.h>
+#include <media/v4l2-event.h>
+#include "mtk_dip-dev.h"
+#include "mtk_dip-smem.h"
+#include "mtk-mdp3-regs.h"
+#include "mtk-img-ipi.h"
+
+int mtk_dip_pipe_init(struct mtk_dip_pipe *dip_pipe,
+		      struct mtk_dip_dev *dip_dev,
+		      struct mtk_dip_pipe_desc *setting,
+		      struct media_device *media_dev,
+		      struct v4l2_device *v4l2_dev,
+		      struct mtk_dip_smem_dev *smem_alloc_dev)
+{
+	int ret, i;
+
+	dip_pipe->dip_dev = dip_dev;
+	dip_pipe->desc = setting;
+	dip_pipe->smem_alloc_dev = smem_alloc_dev;
+
+	atomic_set(&dip_pipe->pipe_job_sequence, 0);
+	spin_lock_init(&dip_pipe->job_lock);
+	mutex_init(&dip_pipe->lock);
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev, "init pipe(%s,%d)\n",
+		dip_pipe->desc->name,
+		dip_pipe->desc->id);
+
+	dip_pipe->num_nodes = MTK_DIP_VIDEO_NODE_ID_TOTAL_NUM;
+
+	for (i = 0; i < MTK_DIP_VIDEO_NODE_ID_OUT_TOTAL_NUM; i++) {
+		dip_pipe->nodes[i].desc =
+			&dip_pipe->desc->output_queue_descs[i];
+		dip_pipe->nodes[i].immutable = 0;
+		dip_pipe->nodes[i].enabled =
+			dip_pipe->nodes[i].desc->default_enable;
+		atomic_set(&dip_pipe->nodes[i].sequence, 0);
+
+		dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+			"%s: init node(%s,%d)\n",
+			dip_pipe->desc->name,
+			dip_pipe->nodes[i].desc->name, i);
+	}
+
+	for (i = MTK_DIP_VIDEO_NODE_ID_OUT_TOTAL_NUM;
+	     i < MTK_DIP_VIDEO_NODE_ID_TOTAL_NUM; i++) {
+		int cap_idx = i - MTK_DIP_VIDEO_NODE_ID_OUT_TOTAL_NUM;
+
+		dip_pipe->nodes[i].desc =
+			&dip_pipe->desc->capture_queue_descs[cap_idx];
+		dip_pipe->nodes[i].immutable = 0;
+		dip_pipe->nodes[i].enabled =
+			dip_pipe->nodes[i].desc->default_enable;
+		atomic_set(&dip_pipe->nodes[i].sequence, 0);
+
+		dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+			"%s: init node(%s,%d)\n",
+			dip_pipe->desc->name,
+			dip_pipe->nodes[i].desc->name, i);
+	}
+
+	if (dip_pipe->desc->master >= 0 &&
+	    dip_pipe->desc->master < MTK_DIP_VIDEO_NODE_ID_TOTAL_NUM) {
+		dip_pipe->nodes[dip_pipe->desc->master].immutable = 1;
+		dip_pipe->nodes[dip_pipe->desc->master].enabled = 1;
+	}
+
+	ret = mtk_dip_ctrl_init(dip_pipe);
+
+	if (ret) {
+		dev_err(&dip_pipe->dip_dev->pdev->dev,
+			"%s: failed(%d) to initialize ctrls\n",
+			dip_pipe->desc->name, ret);
+		goto failed_ctrl;
+	}
+
+	ret = mtk_dip_pipe_v4l2_register(dip_pipe, media_dev, v4l2_dev);
+
+	if (ret) {
+		dev_err(&dip_pipe->dip_dev->pdev->dev,
+			"%s: failed(%d) to create V4L2 devices\n",
+			dip_pipe->desc->name, ret);
+		goto failed_pipe;
+	}
+
+	return 0;
+
+failed_ctrl:
+failed_pipe:
+	mutex_destroy(&dip_pipe->lock);
+	return ret;
+}
+
+static int mtk_dip_pipe_next_job_id(struct mtk_dip_pipe *dip_pipe)
+{
+	int global_job_id =
+		atomic_inc_return(&dip_pipe->pipe_job_sequence);
+
+	global_job_id =
+		(global_job_id & 0x0000FFFF) |
+		(dip_pipe->desc->id << 16);
+
+	return global_job_id;
+}
+
+int mtk_dip_pipe_init_job_infos(struct mtk_dip_pipe *dip_pipe)
+{
+	int i;
+
+	spin_lock(&dip_pipe->job_lock);
+
+	dip_pipe->num_pipe_job_infos = ARRAY_SIZE(dip_pipe->pipe_job_infos);
+	INIT_LIST_HEAD(&dip_pipe->pipe_job_running_list);
+	INIT_LIST_HEAD(&dip_pipe->pipe_job_free_list);
+
+	for (i = 0; i < dip_pipe->num_pipe_job_infos; i++) {
+		struct mtk_dip_pipe_job_info *pipe_job_info =
+			&dip_pipe->pipe_job_infos[i];
+		list_add_tail(&pipe_job_info->list,
+			      &dip_pipe->pipe_job_free_list);
+	}
+
+	spin_unlock(&dip_pipe->job_lock);
+
+	return 0;
+}
+
+static int mtk_dip_pipe_process_pipe_job_info(struct mtk_dip_pipe *dip_pipe,
+					      struct mtk_dip_pipe_job_info
+					      *pipe_job_info)
+{
+	spin_lock(&dip_pipe->job_lock);
+
+	list_del(&pipe_job_info->list);
+	list_add_tail(&pipe_job_info->list, &dip_pipe->pipe_job_running_list);
+
+	spin_unlock(&dip_pipe->job_lock);
+	return 0;
+}
+
+struct mtk_dip_pipe_job_info *
+mtk_dip_pipe_get_running_job_info(struct mtk_dip_pipe *dip_pipe,
+				  int pipe_job_id)
+{
+	struct mtk_dip_pipe_job_info *pipe_job_info = NULL;
+
+	spin_lock(&dip_pipe->job_lock);
+
+	list_for_each_entry(pipe_job_info,
+			    &dip_pipe->pipe_job_running_list, list) {
+		if (pipe_job_info->id == pipe_job_id) {
+			spin_unlock(&dip_pipe->job_lock);
+			return pipe_job_info;
+		}
+	}
+
+	spin_unlock(&dip_pipe->job_lock);
+
+	return NULL;
+}
+
+static int
+mtk_dip_pipe_free_job_info(struct mtk_dip_pipe *dip_pipe,
+			   struct mtk_dip_pipe_job_info *pipe_job_info)
+{
+	spin_lock(&dip_pipe->job_lock);
+
+	list_del(&pipe_job_info->list);
+	list_add_tail(&pipe_job_info->list, &dip_pipe->pipe_job_free_list);
+
+	spin_unlock(&dip_pipe->job_lock);
+
+	return 0;
+}
+
+static struct mtk_dip_pipe_job_info *
+mtk_dip_pipe_get_free_job_info(struct mtk_dip_pipe *dip_pipe)
+{
+	struct mtk_dip_pipe_job_info *pipe_job_info = NULL;
+
+	spin_lock(&dip_pipe->job_lock);
+	list_for_each_entry(pipe_job_info,
+			    &dip_pipe->pipe_job_free_list, list) {
+		dev_dbg(&dip_pipe->dip_dev->pdev->dev, "Found free pipe job\n");
+		spin_unlock(&dip_pipe->job_lock);
+		return pipe_job_info;
+	}
+	spin_unlock(&dip_pipe->job_lock);
+
+	dev_err(&dip_pipe->dip_dev->pdev->dev,
+		"%s: can't found free pipe job\n",
+		dip_pipe->desc->name);
+
+	return NULL;
+}
+
+static void
+mtk_dip_pipe_update_job_info(struct mtk_dip_pipe *dip_pipe,
+			     struct mtk_dip_pipe_job_info *pipe_job_info,
+			     struct mtk_dip_video_device *node,
+			     struct mtk_dip_dev_buffer *dev_buf)
+{
+	if (!pipe_job_info || !dev_buf || !node) {
+		dev_err(&dip_pipe->dip_dev->pdev->dev,
+			"%s: update pipe-job(%p) failed, buf(%p),node(%p)\n",
+			dip_pipe->desc->name,
+			pipe_job_info, dev_buf, node);
+		return;
+	}
+
+	if (pipe_job_info->buf_map[node->desc->id])
+		dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+			"%s:%s: buf overwrite\n",
+			 dip_pipe->desc->name,
+			 node->desc->name);
+
+	if (node->desc->buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+		pipe_job_info->num_img_capture_bufs++;
+
+	if (node->desc->buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+		pipe_job_info->num_img_output_bufs++;
+
+	if (node->desc->buf_type == V4L2_BUF_TYPE_META_OUTPUT)
+		pipe_job_info->num_meta_output_bufs++;
+
+	if (node->desc->buf_type == V4L2_BUF_TYPE_META_CAPTURE)
+		pipe_job_info->num_meta_capture_bufs++;
+
+	pipe_job_info->buf_map[node->desc->id] = dev_buf;
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"%s:%s: added buf(%p) to pipe-job(%p)\n",
+		dip_pipe->desc->name, node->desc->name, dev_buf,
+		pipe_job_info);
+}
+
+static void mtk_dip_pipe_debug_job(struct mtk_dip_pipe *dip_pipe,
+				   struct mtk_dip_pipe_job_info *pipe_job_info)
+{
+	int i;
+
+	if (!dip_pipe || !pipe_job_info)
+		return;
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"%s: pipe-job(%p),id(%d),req(%p)buf nums(%d,%d,%d,%d)\n",
+		dip_pipe->desc->name,
+		pipe_job_info,
+		pipe_job_info->id,
+		pipe_job_info->req,
+		pipe_job_info->num_img_capture_bufs,
+		pipe_job_info->num_img_output_bufs,
+		pipe_job_info->num_meta_capture_bufs,
+		pipe_job_info->num_meta_output_bufs);
+
+	for (i = 0; i < MTK_DIP_VIDEO_NODE_ID_TOTAL_NUM ; i++) {
+		if (pipe_job_info->buf_map[i])
+			dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+				"Node(%s,%d), buf(%p)\n",
+				dip_pipe->nodes[i].desc->name, i,
+				pipe_job_info->buf_map[i]);
+	}
+}
+
+int mtk_dip_pipe_job_finish(struct mtk_dip_pipe *dip_pipe,
+			    unsigned int pipe_job_info_id,
+			    enum vb2_buffer_state vbf_state)
+{
+	int i;
+	struct mtk_dip_pipe_job_info *job_info = NULL;
+	const int pipe_id =
+		mtk_dip_pipe_get_pipe_from_job_id(pipe_job_info_id);
+	u64 timestamp = 0;
+
+	if (!dip_pipe)
+		pr_err("%s: pipe-job id(%d) release failed, dip_pipe is null\n",
+		       __func__, pipe_job_info_id);
+
+	job_info = mtk_dip_pipe_get_running_job_info(dip_pipe,
+						     pipe_job_info_id);
+
+	if (!job_info) {
+		dev_err(&dip_pipe->dip_dev->pdev->dev,
+			"%s:%s: can't find pipe-job id(%d)\n",
+			__func__, dip_pipe->desc->name, pipe_id);
+		return -EINVAL;
+	}
+
+	timestamp = ktime_get_ns();
+
+	for (i = 0; i < MTK_DIP_VIDEO_NODE_ID_TOTAL_NUM; i++) {
+		struct mtk_dip_dev_buffer *dev_buf = job_info->buf_map[i];
+
+		if (!dev_buf) {
+			continue;
+		} else {
+			dev_buf->vbb.vb2_buf.timestamp = ktime_get_ns();
+			mtk_dip_v4l2_buffer_done(&dev_buf->vbb.vb2_buf,
+						 vbf_state);
+		}
+	}
+
+	mtk_dip_pipe_free_job_info(dip_pipe, job_info);
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"%s:%s: finish pipe-job, id(%d), vb state(%d)\n",
+		__func__, dip_pipe->desc->name, pipe_id,
+		pipe_job_info_id, vbf_state);
+
+	return 0;
+}
+
+static void mtk_dip_dev_buf_fill_info(struct mtk_dip_pipe *dip_pipe,
+				      struct mtk_dip_dev_buffer *dev_buf)
+{
+	struct vb2_v4l2_buffer *b;
+	struct mtk_dip_video_device *node;
+	struct mtk_dip_video_device_desc *desc;
+
+	b = &dev_buf->vbb;
+	node = mtk_dip_vbq_to_node(b->vb2_buf.vb2_queue);
+	desc = node->desc;
+	dev_buf->fmt = node->vdev_fmt;
+	dev_buf->dev_fmt = node->dev_q.dev_fmt;
+	dev_buf->isp_daddr =
+		vb2_dma_contig_plane_dma_addr(&b->vb2_buf, 0);
+	dev_buf->vaddr = vb2_plane_vaddr(&b->vb2_buf, 0);
+	dev_buf->buffer_usage = node->dev_q.buffer_usage;
+	dev_buf->rotation = node->dev_q.rotation;
+
+	if (desc->smem_alloc) {
+		dev_buf->scp_daddr =
+			mtk_dip_smem_iova_to_phys
+			(dip_pipe->smem_alloc_dev,
+			 dev_buf->isp_daddr);
+	} else {
+		dev_buf->scp_daddr = 0;
+	}
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"%s:%s: buf type(%d), idx(%d), mem(%d), isp_daddr(%p), scp_daddr(%p)\n",
+		dip_pipe->desc->name,
+		desc->name,
+		b->vb2_buf.type,
+		b->vb2_buf.index,
+		b->vb2_buf.memory,
+		dev_buf->isp_daddr,
+		dev_buf->scp_daddr);
+}
+
+int mtk_dip_pipe_queue_buffers(struct media_request *req,
+			       int initial)
+{
+	struct media_request_object *obj;
+	struct mtk_dip_pipe *dip_pipe;
+	struct mtk_dip_pipe_job_info *pipe_job_info = NULL;
+	int ret = 0;
+
+	list_for_each_entry(obj, &req->objects, list) {
+		struct vb2_buffer *vb;
+
+		if (vb2_request_object_is_buffer(obj)) {
+			struct mtk_dip_dev_buffer *buf;
+			struct mtk_dip_dev_buffer *dev_buf;
+			struct mtk_dip_video_device *node;
+
+			vb = container_of(obj, struct vb2_buffer, req_obj);
+			node = mtk_dip_vbq_to_node(vb->vb2_queue);
+			dip_pipe = vb2_get_drv_priv(vb->vb2_queue);
+			dev_buf = mtk_dip_vb2_buf_to_dev_buf(vb);
+			buf = dev_buf;
+
+			if (!pipe_job_info) {
+				pipe_job_info = mtk_dip_pipe_get_free_job_info
+					(dip_pipe);
+
+				if (!pipe_job_info)
+					goto FAILE_JOB_NOT_TRIGGER;
+
+				memset(pipe_job_info->buf_map, 0,
+				       sizeof(pipe_job_info->buf_map));
+				pipe_job_info->num_img_capture_bufs = 0;
+				pipe_job_info->num_img_output_bufs = 0;
+				pipe_job_info->num_meta_capture_bufs = 0;
+				pipe_job_info->num_meta_output_bufs = 0;
+			}
+
+			mtk_dip_dev_buf_fill_info(dip_pipe,
+						  buf);
+
+			mtk_dip_pipe_update_job_info(dip_pipe,
+						     pipe_job_info,
+						     node,
+						     buf);
+		}
+	}
+
+	if (!pipe_job_info)
+		return -EINVAL;
+
+	pipe_job_info->id =
+		mtk_dip_pipe_next_job_id(dip_pipe);
+
+	mtk_dip_pipe_debug_job(dip_pipe, pipe_job_info);
+
+	mutex_lock(&dip_pipe->lock);
+
+	if (!dip_pipe->streaming) {
+		dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+			"%s:%s:  stream is off, no hw enqueue triggered\n",
+			__func__, dip_pipe->desc->name);
+		mutex_unlock(&dip_pipe->lock);
+		return 0;
+	}
+
+	if (mtk_dip_pipe_process_pipe_job_info(dip_pipe, pipe_job_info)) {
+		dev_err(&dip_pipe->dip_dev->pdev->dev,
+			"%s:%s: can't start to run pipe job id(%d)\n",
+			__func__, dip_pipe->desc->name,
+			pipe_job_info->id);
+		mutex_unlock(&dip_pipe->lock);
+		goto FAILE_JOB_NOT_TRIGGER;
+	}
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"%s: trigger pipe job, id(%d)\n",
+		dip_pipe->desc->name,
+		dip_pipe->desc->id);
+
+	if (mtk_dip_pipe_job_start(dip_pipe, pipe_job_info)) {
+		mutex_unlock(&dip_pipe->lock);
+		goto FAILE_JOB_NOT_TRIGGER;
+	}
+
+	mutex_unlock(&dip_pipe->lock);
+
+	return 0;
+
+FAILE_JOB_NOT_TRIGGER:
+	if (initial)
+		return ret;
+
+	mtk_dip_pipe_job_finish(dip_pipe, pipe_job_info->id,
+				VB2_BUF_STATE_ERROR);
+
+	return -EINVAL;
+}
+
+int mtk_dip_pipe_release(struct mtk_dip_pipe *dip_pipe)
+{
+	mtk_dip_pipe_v4l2_unregister(dip_pipe);
+	v4l2_ctrl_handler_free(&dip_pipe->ctrl_handler);
+	mutex_destroy(&dip_pipe->lock);
+
+	return 0;
+}
+
+static void set_img_fmt(struct v4l2_pix_format_mplane *mfmt_to_fill,
+			struct mtk_dip_dev_format *dev_fmt)
+{
+	int i;
+
+	mfmt_to_fill->pixelformat = dev_fmt->fmt.img.pixelformat;
+	mfmt_to_fill->num_planes = dev_fmt->fmt.img.num_planes;
+	mfmt_to_fill->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+	mfmt_to_fill->quantization = V4L2_QUANTIZATION_DEFAULT;
+	mfmt_to_fill->colorspace = dev_fmt->fmt.img.colorspace;
+
+	memset(mfmt_to_fill->reserved, 0, sizeof(mfmt_to_fill->reserved));
+
+	pr_debug("%s: Fmt(%d),w(%d),h(%d),f(%d)\n",
+		 __func__,
+		 mfmt_to_fill->pixelformat,
+		 mfmt_to_fill->width,
+		 mfmt_to_fill->height,
+		 mfmt_to_fill->field);
+
+	for (i = 0 ; i < mfmt_to_fill->num_planes; ++i) {
+		int bpl = (mfmt_to_fill->width *
+			dev_fmt->fmt.img.row_depth[i]) / 8;
+		int sizeimage = (mfmt_to_fill->width * mfmt_to_fill->height *
+			dev_fmt->fmt.img.depth[i]) / 8;
+
+		mfmt_to_fill->plane_fmt[i].bytesperline = bpl;
+		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));
+
+		pr_debug("plane(%d):bpl(%d),sizeimage(%u)\n",
+			 i, bpl,
+			 mfmt_to_fill->plane_fmt[i].sizeimage);
+	}
+}
+
+static void set_meta_fmt(struct v4l2_meta_format *metafmt_to_fill,
+			 struct mtk_dip_dev_format *dev_fmt)
+{
+	metafmt_to_fill->dataformat = dev_fmt->fmt.meta.dataformat;
+
+	if (dev_fmt->fmt.meta.max_buffer_size <= 0) {
+		pr_debug("Invalid meta buf size(%u), use default(%u)\n",
+			 dev_fmt->fmt.meta.max_buffer_size,
+			 MTK_DIP_DEV_META_BUF_DEFAULT_SIZE);
+		metafmt_to_fill->buffersize = MTK_DIP_DEV_META_BUF_DEFAULT_SIZE;
+	} else {
+		pr_debug("Use meta size(%u)\n",
+			 dev_fmt->fmt.meta.max_buffer_size);
+		metafmt_to_fill->buffersize = dev_fmt->fmt.meta.max_buffer_size;
+	}
+}
+
+void mtk_dip_pipe_load_default_fmt(struct mtk_dip_pipe *dip_pipe,
+				   struct mtk_dip_video_device *node,
+				   struct v4l2_format *fmt_to_fill)
+{
+	struct mtk_dip_dev_format *dev_fmt;
+	struct mtk_dip_video_device_desc *desc = node->desc;
+
+	if (desc->num_fmts == 0) {
+		pr_err("%s:%s: desc->num_fmts is 0, no format support list\n",
+		       __func__, desc->name);
+		return;
+	}
+
+	if (desc->default_fmt_idx >= desc->num_fmts) {
+		pr_debug("%s:%s: invalid idx(%d), must < num_fmts(%d)\n",
+			 __func__, desc->name, desc->default_fmt_idx,
+			desc->num_fmts);
+		desc->default_fmt_idx = 0;
+	}
+
+	dev_fmt	= &desc->fmts[desc->default_fmt_idx];
+	fmt_to_fill->type = desc->buf_type;
+	if (mtk_dip_buf_is_meta(desc->buf_type)) {
+		set_meta_fmt(&fmt_to_fill->fmt.meta, dev_fmt);
+	} else {
+		fmt_to_fill->fmt.pix_mp.width = desc->default_width;
+		fmt_to_fill->fmt.pix_mp.height = desc->default_height;
+		fmt_to_fill->fmt.pix_mp.field = V4L2_FIELD_NONE;
+
+		set_img_fmt(&fmt_to_fill->fmt.pix_mp, dev_fmt);
+	}
+}
+
+struct mtk_dip_dev_format *
+mtk_dip_pipe_find_fmt(struct mtk_dip_pipe *dip_pipe,
+		      struct mtk_dip_video_device *node,
+		      u32 format)
+{
+	int i;
+	struct mtk_dip_dev_format *dev_fmt;
+
+	struct mtk_dip_video_device_desc *desc = node->desc;
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev, "fmt to find(%x)\n", format);
+
+	for (i = 0; i < desc->num_fmts; i++) {
+		dev_fmt = &desc->fmts[i];
+		if (!mtk_dip_buf_is_meta(desc->buf_type)) {
+			if (dev_fmt->fmt.img.pixelformat == format)
+				return dev_fmt;
+		} else {
+			if (dev_fmt->fmt.meta.dataformat == format)
+				return dev_fmt;
+		}
+	}
+
+	return NULL;
+}
+
+int mtk_dip_pipe_set_meta_fmt(struct mtk_dip_pipe *dip_pipe,
+			      struct mtk_dip_video_device *node,
+			      struct v4l2_meta_format *user_fmt,
+			      struct v4l2_meta_format *node_fmt)
+{
+	struct mtk_dip_dev_format *dev_fmt;
+
+	if (!user_fmt || !node_fmt)
+		return -EINVAL;
+
+	dev_fmt = mtk_dip_pipe_find_fmt(dip_pipe, node,
+					user_fmt->dataformat);
+
+	if (!dev_fmt)
+		return -EINVAL;
+
+	node->dev_q.dev_fmt = dev_fmt;
+	set_meta_fmt(node_fmt, dev_fmt);
+	*user_fmt = *node_fmt;
+
+	return 0;
+}
+
+int mtk_dip_pipe_set_img_fmt(struct mtk_dip_pipe *dip_pipe,
+			     struct mtk_dip_video_device *node,
+			     struct v4l2_pix_format_mplane *user_fmt,
+			     struct v4l2_pix_format_mplane *dest_fmt)
+{
+	struct mtk_dip_dev_format *dev_fmt;
+
+	if (!user_fmt || !dest_fmt)
+		return -EINVAL;
+
+	dev_fmt = mtk_dip_pipe_find_fmt(dip_pipe, node,
+					user_fmt->pixelformat);
+
+	if (!dev_fmt) {
+		pr_debug("%s:%s:%s: dev_fmt(%d) not found\n",
+			 __func__, dip_pipe->desc->name,
+			 node->desc->name, user_fmt->pixelformat);
+		return -EINVAL;
+	}
+
+	node->dev_q.dev_fmt = dev_fmt;
+	dest_fmt->width = user_fmt->width;
+	dest_fmt->height = user_fmt->height;
+	dest_fmt->field = V4L2_FIELD_NONE;
+
+	set_img_fmt(dest_fmt, dev_fmt);
+
+	return 0;
+}
+
+int mtk_dip_pipe_streamon(struct mtk_dip_pipe *dip_pipe)
+{
+	int ret;
+	struct mtk_dip_dev *dip_dev;
+
+	if (!dip_pipe)
+		return -EINVAL;
+
+	dip_dev = dev_get_drvdata(&dip_pipe->dip_dev->pdev->dev);
+
+	mutex_lock(&dip_pipe->lock);
+
+	ret = mtk_dip_hw_streamon(&dip_dev->dip_hw,
+				  dip_pipe->desc->id);
+
+	if (ret) {
+		dev_err(&dip_pipe->dip_dev->pdev->dev,
+			"%s:%s:%d: failed to start hw\n",
+			__func__, dip_pipe->desc->name,
+			dip_pipe->desc->id);
+		mutex_unlock(&dip_pipe->lock);
+		return -EBUSY;
+	}
+
+	dip_pipe->streaming = 1;
+	mutex_unlock(&dip_pipe->lock);
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"%s:%s:%d: start hw\n",
+		__func__, dip_pipe->desc->name,
+		dip_pipe->desc->id);
+
+	return ret;
+}
+
+int mtk_dip_pipe_streamoff(struct mtk_dip_pipe *dip_pipe)
+{
+	int ret;
+	struct mtk_dip_dev *dip_dev;
+
+	if (!dip_pipe)
+		return -EINVAL;
+
+	dip_dev = dev_get_drvdata(&dip_pipe->dip_dev->pdev->dev);
+
+	mutex_lock(&dip_pipe->lock);
+
+	ret = mtk_dip_hw_streamoff(&dip_dev->dip_hw,
+				   dip_pipe->desc->id);
+
+	if (ret) {
+		dev_err(&dip_pipe->dip_dev->pdev->dev,
+			"%s:%s:%d: failed to stop hw\n",
+			__func__, dip_pipe->desc->name,
+			dip_pipe->desc->id);
+		mutex_unlock(&dip_pipe->lock);
+		return -EBUSY;
+	}
+
+	dip_pipe->streaming = 0;
+	mutex_unlock(&dip_pipe->lock);
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"%s:%s:%d: stop hw\n",
+		__func__, dip_pipe->desc->name,
+		dip_pipe->desc->id);
+
+	return 0;
+}
+
+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;
+}
+
+/* Stride that is accepted by MDP HW */
+static u32 dip_mdp_fmt_get_stride(const struct mtk_dip_dev_mdp_format *fmt,
+				  u32 bytesperline,
+				  unsigned int plane)
+{
+	enum mdp_color c = fmt->mdp_color;
+	u32 stride;
+
+	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_mdp_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_mdp_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 is_contig_mp_buffer(struct mtk_dip_dev_buffer *dev_buf)
+{
+	if (MDP_COLOR_GET_PLANE_COUNT(dev_buf->dev_fmt->fmt.img.mdp_color)
+	    == 1)
+		return 0;
+	else
+		return 1;
+}
+
+static int fill_ipi_img_param_mp(struct mtk_dip_pipe *dip_pipe,
+				 struct img_image_buffer *b,
+				 struct mtk_dip_dev_buffer *dev_buf,
+				 char *buf_name)
+{
+	struct v4l2_pix_format_mplane *pix_mp;
+	struct mtk_dip_dev_mdp_format *mdp_fmt;
+	unsigned int i;
+	unsigned int total_plane_size = 0;
+
+	if (!dev_buf || !dev_buf->dev_fmt) {
+		dev_err(&dip_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->fmt.img;
+
+	b->format.colorformat = dev_buf->dev_fmt->fmt.img.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->fmt.img.mdp_color);
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"%s: buf(%s), IPI: w(%d),h(%d),c(0x%x)\n",
+		dip_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 =
+			dip_mdp_fmt_get_stride
+			(mdp_fmt, pix_mp->plane_fmt[i].bytesperline, 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] = dev_buf->isp_daddr;
+		dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+			"Contiguous-mp-buf:plane(%i),stride(%d),size(%d),iova(%p)",
+			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;
+	}
+
+	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(&dip_pipe->dip_dev->pdev->dev,
+			"Contiguous-mp-buf:plane(%i),stride(%d),size(%d),iova(%p)",
+			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;
+	}
+
+	b->usage = dev_buf->buffer_usage;
+
+	dev_dbg(&dip_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_ipi_img_param(struct mtk_dip_pipe *dip_pipe,
+			      struct img_image_buffer *img,
+			      struct mtk_dip_dev_buffer *dev_buf,
+			      char *buf_name)
+{
+	img->format.width = dev_buf->fmt.fmt.pix_mp.width;
+	img->format.height = dev_buf->fmt.fmt.pix_mp.height;
+
+	if (dev_buf && dev_buf->dev_fmt) {
+		img->format.colorformat =
+			dev_buf->dev_fmt->fmt.img.mdp_color;
+	} else {
+		dev_err(&dip_pipe->dip_dev->pdev->dev,
+			"%s: %s's dev format not set\n",
+			__func__, buf_name);
+		return -EINVAL;
+	}
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"%s: buf(%s) IPI: w(%d),h(%d),c(0x%x)\n",
+		dip_pipe->desc->name,
+		buf_name,
+		img->format.width,
+		img->format.height,
+		img->format.colorformat);
+
+	img->format.plane_fmt[0].size =
+		dev_buf->fmt.fmt.pix_mp.plane_fmt[0].sizeimage;
+	img->format.plane_fmt[0].stride =
+		dev_buf->fmt.fmt.pix_mp.plane_fmt[0].bytesperline;
+	img->iova[0] = dev_buf->isp_daddr;
+	img->usage = dev_buf->buffer_usage;
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"size(%d), stride(%d),ycbcr(%d),iova(%p),u(%d)\n",
+		img->format.plane_fmt[0].size,
+		img->format.plane_fmt[0].stride,
+		img->format.ycbcr_prof,
+		img->iova[0],
+		img->usage);
+
+	return 0;
+}
+
+static int fill_input_ipi_param(struct mtk_dip_pipe *dip_pipe,
+				struct img_input *iin,
+				struct mtk_dip_dev_buffer *dev_buf,
+				char *buf_name)
+{
+	struct img_image_buffer *img = &iin->buffer;
+
+	/* Will map the vale with V4L2 color space in the future */
+	img->format.ycbcr_prof = 1;
+	if (is_contig_mp_buffer(dev_buf))
+		return fill_ipi_img_param_mp(dip_pipe, img, dev_buf,
+					     buf_name);
+	else
+		return fill_ipi_img_param(dip_pipe, img, dev_buf,
+					  buf_name);
+}
+
+static int fill_output_ipi_param(struct mtk_dip_pipe *dip_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;
+
+	img->format.ycbcr_prof = 0;
+
+	if (is_contig_mp_buffer(dev_buf_out))
+		ret = fill_ipi_img_param_mp(dip_pipe, img, dev_buf_out,
+					    buf_name);
+	else
+		ret = fill_ipi_img_param(dip_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(&dip_pipe->dip_dev->pdev->dev,
+		"%s: buf(%s) IPI-ext:c_l(%d),c_t(%d),c_w(%d),c_h(%d)\n",
+		dip_pipe->desc->name,
+		buf_name,
+		iout->crop.left,
+		iout->crop.top,
+		iout->crop.width,
+		iout->crop.height);
+
+	dev_dbg(&dip_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;
+}
+
+int mtk_dip_pipe_job_start(struct mtk_dip_pipe *dip_pipe,
+			   struct mtk_dip_pipe_job_info *pipe_job_info)
+{
+	struct platform_device *pdev = dip_pipe->dip_dev->pdev;
+	int ret;
+	int out_img_buf_idx;
+	struct img_ipi_frameparam dip_param;
+	struct mtk_dip_dev_buffer *dev_buf_in;
+	struct mtk_dip_dev_buffer *dev_buf_out;
+	struct mtk_dip_dev_buffer *dev_buf_tuning;
+
+	if (!pipe_job_info) {
+		dev_err(&pdev->dev,
+			"pipe_job_info(%p) in start can't be NULL\n",
+			pipe_job_info);
+		return -EINVAL;
+	}
+
+	/* We need RAW and at least MDP0 or MDP 1 buffer */
+	if (!pipe_job_info->buf_map[MTK_DIP_VIDEO_NODE_ID_RAW_OUT] ||
+	    (!pipe_job_info->buf_map[MTK_DIP_VIDEO_NODE_ID_MDP0_CAPTURE] &&
+		 !pipe_job_info->buf_map[MTK_DIP_VIDEO_NODE_ID_MDP1_CAPTURE])){
+		struct mtk_dip_dev_buffer **map = pipe_job_info->buf_map;
+
+		dev_dbg(&pdev->dev,
+			"can't trigger 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]);
+		return -EINVAL;
+	}
+
+	dev_dbg(&pdev->dev,
+		"%s:%s: pipe-job id(%d)\n",
+		__func__, dip_pipe->desc->name,
+		pipe_job_info->id);
+
+	/* Fill ipi params for DIP driver */
+	memset(&dip_param, 0, sizeof(struct img_ipi_frameparam));
+
+	dip_param.index = pipe_job_info->id;
+	dip_param.num_outputs = pipe_job_info->num_img_capture_bufs;
+	dip_param.num_inputs = pipe_job_info->num_img_output_bufs;
+	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: pa(%p),va(%p),iova(%p)\n",
+			dev_buf_tuning->scp_daddr,
+			dev_buf_tuning->vaddr,
+			dev_buf_tuning->isp_daddr);
+		dip_param.tuning_data.pa = (uint32_t)dev_buf_tuning->scp_daddr;
+		dip_param.tuning_data.va = (uint64_t)dev_buf_tuning->vaddr;
+		dip_param.tuning_data.iova =
+			(uint32_t)dev_buf_tuning->isp_daddr;
+	} else {
+		dev_dbg(&pdev->dev,
+			"Doesn't enqueued tuning buffer, by-pass\n");
+	dip_param.tuning_data.pa = 0;
+	dip_param.tuning_data.va = 0;
+	dip_param.tuning_data.iova = 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[0];
+
+		fill_input_ipi_param(dip_pipe, iin, dev_buf_in, "RAW");
+	}
+
+	out_img_buf_idx = 0;
+
+	/* MDP 0 buffer */
+	dev_buf_out =
+		pipe_job_info->buf_map[MTK_DIP_VIDEO_NODE_ID_MDP0_CAPTURE];
+	if (dev_buf_out) {
+		struct img_output *iout = &dip_param.outputs[out_img_buf_idx];
+
+		fill_output_ipi_param(dip_pipe, iout, dev_buf_out,
+				      dev_buf_in, "MDP0");
+		out_img_buf_idx++;
+	}
+
+	/* MDP 1 buffer */
+	dev_buf_out =
+		pipe_job_info->buf_map[MTK_DIP_VIDEO_NODE_ID_MDP1_CAPTURE];
+	if (dev_buf_out) {
+		struct img_output *iout = &dip_param.outputs[out_img_buf_idx];
+
+		fill_output_ipi_param(dip_pipe, iout, dev_buf_out,
+				      dev_buf_in, "MDP1");
+		out_img_buf_idx++;
+	}
+
+	ret = mtk_dip_hw_enqueue(&dip_pipe->dip_dev->dip_hw, &dip_param);
+
+	if (ret) {
+		dev_dbg(&pdev->dev,
+			"%s:%s: enqueue to HW failed(%d)\n",
+			 __func__, dip_pipe->desc->name, ret);
+		return -EBUSY;
+	}
+
+	return ret;
+}
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..f51f7a44379a
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-dev.h
@@ -0,0 +1,321 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Frederic Chen <frederic.chen@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MTK_DIP_DEV_H_
+#define _MTK_DIP_DEV_H_
+
+#include <linux/types.h>
+#include <linux/device.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"
+#include "mtk_dip-smem.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 (1110 * 1024)
+
+#define V4L2_CID_PRIVATE_UT_NUM			(V4L2_CID_USER_BASE | 0x1001)
+#define V4L2_CID_PRIVATE_SET_BUFFER_USAGE	(V4L2_CID_PRIVATE_UT_NUM + 2)
+#define V4L2_CID_MTK_DIP_MAX			100
+
+enum mtk_dip_v4l2_buffer_usage {
+	MTK_DIP_V4l2_BUF_USAGE_DEFAULT = 0,
+	MTK_DIP_V4l2_BUF_USAGE_FD,
+	MTK_DIP_V4l2_BUF_USAGE_POSTPROC,
+	MTK_DIP_V4l2_BUF_USAGE_NONE,
+};
+
+/*
+ * Supported format and the information used for
+ * size calculation
+ */
+struct mtk_dip_dev_meta_format {
+	u32 dataformat;
+	u32 max_buffer_size;
+	u8 flags;
+};
+
+/* MDP part private format definitation */
+struct mtk_dip_dev_mdp_format {
+	u32 pixelformat;
+	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;
+};
+
+struct mtk_dip_dev_format {
+	union {
+		struct mtk_dip_dev_meta_format meta;
+		struct mtk_dip_dev_mdp_format img;
+	} fmt;
+};
+
+struct mtk_dip_pipe_job_info {
+	struct media_request *req;
+	int id;
+	struct mtk_dip_dev_buffer*
+		buf_map[MTK_DIP_VIDEO_NODE_ID_TOTAL_NUM];
+	int num_img_capture_bufs;
+	int num_img_output_bufs;
+	int num_meta_capture_bufs;
+	int num_meta_output_bufs;
+	struct list_head list;
+};
+
+struct mtk_dip_dev_buffer {
+	struct vb2_v4l2_buffer vbb;
+	struct v4l2_format fmt;
+	struct mtk_dip_dev_format *dev_fmt;
+	int pipe_job_id;
+	void *vaddr;
+	dma_addr_t isp_daddr;
+	dma_addr_t scp_daddr;
+	unsigned int buffer_usage;
+	int rotation;
+	struct list_head list;
+};
+
+struct mtk_dip_pipe_desc {
+	char *name;
+	int master;
+	int id;
+	struct mtk_dip_video_device_desc *output_queue_descs;
+	int total_output_queues;
+	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;
+	int dynamic;
+	int default_enable;
+	struct mtk_dip_dev_format *fmts;
+	int num_fmts;
+	char *description;
+	int default_width;
+	int default_height;
+	const struct v4l2_ioctl_ops *ops;
+	int default_fmt_idx;
+};
+
+struct mtk_dip_dev_queue {
+	struct vb2_queue vbq;
+	/* Serializes vb2 queue and video device operations */
+	struct mutex lock;
+	struct mtk_dip_dev_format *dev_fmt;
+	/* Firmware uses buffer_usage to select suitable DMA ports */
+	unsigned int buffer_usage;
+	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;
+	int immutable;
+	int enabled;
+	struct mtk_dip_video_device_desc *desc;
+	atomic_t sequence;
+};
+
+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;
+	int streaming;
+	struct media_pad *subdev_pads;
+	struct media_pipeline pipeline;
+	struct v4l2_subdev subdev;
+	struct v4l2_subdev_fh *fh;
+	struct v4l2_ctrl_handler ctrl_handler;
+	struct mtk_dip_smem_dev *smem_alloc_dev;
+	atomic_t pipe_job_sequence;
+	struct mtk_dip_pipe_job_info pipe_job_infos[VB2_MAX_FRAME];
+	int num_pipe_job_infos;
+	struct list_head pipe_job_running_list;
+	struct list_head pipe_job_free_list;
+	/* Serializes pipe's stream on/off and buffers enqueue operations */
+	struct mutex lock;
+	spinlock_t job_lock; /* protect the pipe job list */
+	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_smem_dev smem_alloc_dev;
+	struct mtk_dip_hw dip_hw;
+};
+
+int mtk_dip_dev_media_register(struct device *dev,
+			       struct media_device *media_dev,
+			       const char *model);
+
+int mtk_dip_dev_v4l2_init(struct mtk_dip_dev *dip_dev);
+
+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 *dip_pipe,
+			       struct media_device *media_dev,
+			       struct v4l2_device *v4l2_dev);
+
+int mtk_dip_pipe_v4l2_unregister(struct mtk_dip_pipe *dip_pipe);
+
+void mtk_dip_v4l2_buffer_done(struct vb2_buffer *vb,
+			      enum vb2_buffer_state state);
+
+int mtk_dip_pipe_queue_buffers(struct media_request *req, int initial);
+
+int mtk_dip_pipe_init(struct mtk_dip_pipe *dip_pipe,
+		      struct mtk_dip_dev *dip_dev,
+		      struct mtk_dip_pipe_desc *setting,
+		      struct media_device *media_dev,
+		      struct v4l2_device *v4l2_dev,
+		      struct mtk_dip_smem_dev *smem_alloc_dev);
+
+int mtk_dip_pipe_release(struct mtk_dip_pipe *dip_pipe);
+
+int mtk_dip_pipe_job_finish(struct mtk_dip_pipe *dip_pipe,
+			    unsigned int pipe_job_info_id,
+			    enum vb2_buffer_state state);
+
+int mtk_dip_pipe_job_start(struct mtk_dip_pipe *dip_pipe,
+			   struct mtk_dip_pipe_job_info *pipe_job_info);
+
+int mtk_dip_pipe_init_job_infos(struct mtk_dip_pipe *dip_pipe);
+
+struct mtk_dip_dev_format *
+mtk_dip_pipe_find_fmt(struct mtk_dip_pipe *dip_pipe,
+		      struct mtk_dip_video_device *node,
+		      u32 format);
+
+int mtk_dip_pipe_set_img_fmt(struct mtk_dip_pipe *dip_pipe,
+			     struct mtk_dip_video_device *node,
+			     struct v4l2_pix_format_mplane *user_fmt,
+			     struct v4l2_pix_format_mplane *node_fmt);
+
+int mtk_dip_pipe_set_meta_fmt(struct mtk_dip_pipe *dip_pipe,
+			      struct mtk_dip_video_device *node,
+			      struct v4l2_meta_format *user_fmt,
+			      struct v4l2_meta_format *node_fmt);
+
+void mtk_dip_pipe_load_default_fmt(struct mtk_dip_pipe *dip_pipe,
+				   struct mtk_dip_video_device *node,
+				   struct v4l2_format *fmt_to_fill);
+
+int mtk_dip_pipe_streamon(struct mtk_dip_pipe *dip_pipe);
+
+int mtk_dip_pipe_streamoff(struct mtk_dip_pipe *dip_pipe);
+
+int mtk_dip_ctrl_init(struct mtk_dip_pipe *dip_pipe);
+
+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_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 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..d813d8b92e8b
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-hw.h
@@ -0,0 +1,167 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ * Author: Holmes Chiou <holmes.chiou@mediatek.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MTK_DIP_HW_H_
+#define _MTK_DIP_HW_H_
+
+#include <linux/clk.h>
+#include "mtk-img-ipi.h"
+
+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_STREAMON,
+	DIP_STATE_STREAMOFF
+};
+
+struct mtk_dip_hw_frame_job {
+	struct img_frameparam fparam;
+	int sequence;
+};
+
+struct mtk_dip_hw_user_id {
+	struct list_head list_entry;
+	u16 id;
+	u32 num;
+	u16 state;
+};
+
+struct mtk_dip_hw_subframe {
+	struct img_addr buffer;
+	struct sg_table table;
+	struct img_sw_addr config_data;
+	struct img_addr tuning_buf;
+	struct img_sw_addr frameparam;
+	struct list_head list_entry;
+};
+
+struct mtk_dip_hw_queue {
+	struct list_head queue;
+	struct mutex queuelock; /* protect queue and queue_cnt */
+	u32 queue_cnt;
+};
+
+struct mtk_dip_hw_joblist {
+	struct list_head queue;
+	spinlock_t queuelock; /* protect job list */
+	u32 queue_cnt;
+};
+
+struct mtk_dip_hw_thread {
+	struct task_struct *thread;
+	wait_queue_head_t wq;
+};
+
+struct mtk_dip_hw_work {
+	struct list_head list_entry;
+	struct img_ipi_frameparam frameparams;
+	struct mtk_dip_hw_user_id *user_id;
+};
+
+struct mtk_dip_hw_submit_work {
+	struct work_struct frame_work;
+	struct mtk_dip_hw *dip_hw;
+};
+
+struct mtk_dip_hw_mdpcb_work {
+	struct work_struct frame_work;
+	struct img_ipi_frameparam *frameparams;
+};
+
+struct mtk_dip_hw_clk {
+	struct clk *img_larb5;
+	struct clk *img_dip;
+};
+
+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 {
+	struct mtk_dip_hw_clk dip_clk;
+	struct device *larb_dev;
+	struct mtk_dip_hw_joblist dip_gcejoblist;
+	struct mtk_dip_hw_queue dip_freebufferlist;
+	struct mtk_dip_hw_queue dip_usedbufferlist;
+	struct mtk_dip_hw_thread dip_runner_thread;
+	struct mtk_dip_hw_queue dip_useridlist;
+	struct mtk_dip_hw_queue dip_worklist;
+	struct workqueue_struct *composer_wq;
+	struct mtk_dip_hw_submit_work submit_work;
+	wait_queue_head_t composing_wq;
+	wait_queue_head_t flushing_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 VPU driver  */
+	struct platform_device *vpu_pdev;
+	struct rproc *rproc_handle;
+	dma_addr_t scp_workingbuf_addr;
+	/* increase after enqueue */
+	atomic_t dip_enque_cnt;
+	/* increase after Stream ON, decrease when Stream OFF */
+	atomic_t dip_stream_cnt;
+	/* increase after open, decrease when close */
+	atomic_t dip_user_cnt;
+};
+
+int mtk_dip_hw_enqueue(struct mtk_dip_hw *dip_hw,
+		       struct img_ipi_frameparam *frameparams);
+int mtk_dip_hw_connect(struct mtk_dip_hw *dip_hw);
+int mtk_dip_hw_disconnect(struct mtk_dip_hw *dip_hw);
+int mtk_dip_hw_streamon(struct mtk_dip_hw *dip_hw, u16 id);
+int mtk_dip_hw_streamoff(struct mtk_dip_hw *dip_hw, u16 id);
+
+static inline struct mtk_dip_hw_frame_job
+*mtk_dip_fparam_to_job(struct img_frameparam *fparam)
+{
+	return container_of(fparam, struct mtk_dip_hw_frame_job, fparam);
+}
+
+static inline struct mtk_dip_hw_frame_job *
+mtk_dip_ipi_fparam_to_job(struct img_ipi_frameparam *ipi_fparam)
+{
+	return container_of(ipi_fparam,
+			    struct mtk_dip_hw_frame_job,
+			    fparam.frameparam);
+}
+
+#endif /* _MTK_DIP_HW_H_ */
+
diff --git a/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-smem.c b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-smem.c
new file mode 100644
index 000000000000..5456c0b54ad4
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-smem.c
@@ -0,0 +1,322 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Frederic Chen <frederic.chen@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/dma-contiguous.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/iommu.h>
+#include <asm/cacheflush.h>
+#include "mtk_dip-smem.h"
+
+#define MTK_DIP_SMEM_DEV_NAME "MTK-DIP-SMEM"
+
+static struct reserved_mem *dip_reserved_smem;
+static struct dma_map_ops smem_dma_ops;
+
+struct dma_coherent_mem {
+	void		*virt_base;
+	dma_addr_t	device_base;
+	unsigned long	pfn_base;
+	int		size;
+	int		flags;
+	unsigned long	*bitmap;
+	spinlock_t	spinlock; /* protect dma_coherent_mem member */
+	bool		use_dev_dma_pfn_offset;
+};
+
+static struct dma_coherent_mem *dev_get_coherent_memory(struct device *dev)
+{
+	if (dev && dev->dma_mem)
+		return dev->dma_mem;
+	return NULL;
+}
+
+phys_addr_t mtk_dip_smem_iova_to_phys(struct mtk_dip_smem_dev *smem_dev,
+				      dma_addr_t iova)
+{
+		struct iommu_domain *smem_dom;
+		phys_addr_t addr;
+		phys_addr_t limit;
+
+		if (!smem_dev)
+			return 0;
+
+		smem_dom = iommu_get_domain_for_dev(smem_dev->dev.parent);
+
+		if (!smem_dom)
+			return 0;
+
+		addr = iommu_iova_to_phys(smem_dom, iova);
+
+		limit = smem_dev->smem_base + smem_dev->smem_size;
+
+		if (addr < smem_dev->smem_base || addr >= limit) {
+			dev_err(&smem_dev->dev,
+				"Unexpected scp_daddr %pa (must >= %pa and <%pa)\n",
+				&addr, &smem_dev->smem_base, &limit);
+			return 0;
+		}
+		dev_dbg(&smem_dev->dev, "Pa verifcation pass: %pa(>=%pa, <%pa)\n",
+			&addr, &smem_dev->smem_base, &limit);
+		return addr;
+}
+
+/********************************************
+ * MTK DIP SMEM DMA ops *
+ ********************************************/
+static int mtk_dip_smem_get_sgtable(struct device *dev,
+				    struct sg_table *sgt,
+				    void *cpu_addr,
+				    dma_addr_t dma_addr,
+				    size_t size, unsigned long attrs)
+{
+	struct mtk_dip_smem_dev *smem_dev = dev_get_drvdata(dev);
+	int n_pages_align;
+	int size_align;
+	int page_start;
+	unsigned long long offset_p;
+
+	phys_addr_t paddr = mtk_dip_smem_iova_to_phys(smem_dev, dma_addr);
+
+	offset_p = (unsigned long long)paddr -
+		(unsigned long long)smem_dev->smem_base;
+
+	dev_dbg(dev, "%s: dma_addr(%p), cpu_addr(%p), pa(%p), size(%d)\n",
+		__func__, dma_addr, cpu_addr, paddr, size);
+
+	size_align = round_up(size, PAGE_SIZE);
+	n_pages_align = size_align >> PAGE_SHIFT;
+	page_start = offset_p >> PAGE_SHIFT;
+
+	dev_dbg(dev, "%s: page_start(%d), page pa(%p), pa(%p), aligned size(%d)\n",
+		__func__,
+		page_start,
+		page_to_phys(*(smem_dev->smem_pages + page_start)),
+		paddr,
+		size_align
+		);
+
+	if (!smem_dev) {
+		dev_err(dev, "can't get sgtable from smem_dev\n");
+		return -EINVAL;
+	}
+
+	dev_dbg(dev, "%s: get sgt of the smem: %d pages\n", __func__,
+		n_pages_align);
+
+	return sg_alloc_table_from_pages(sgt,
+					 smem_dev->smem_pages + page_start,
+					 n_pages_align,
+					 0, size_align, GFP_KERNEL);
+}
+
+static void *mtk_dip_smem_get_cpu_addr(struct mtk_dip_smem_dev *smem_dev,
+				       struct scatterlist *sg)
+{
+	struct device *dev = &smem_dev->dev;
+	struct dma_coherent_mem *dma_mem =
+		dev_get_coherent_memory(dev);
+
+	phys_addr_t addr = (phys_addr_t)sg_phys(sg);
+
+	if (addr < smem_dev->smem_base ||
+	    addr > smem_dev->smem_base + smem_dev->smem_size) {
+		dev_err(dev, "%s: Invalid paddr %p from sg\n", __func__, addr);
+		return NULL;
+	}
+
+	return dma_mem->virt_base + (addr - smem_dev->smem_base);
+}
+
+static void mtk_dip_smem_sync_sg_for_cpu(struct device *dev,
+					 struct scatterlist *sgl,
+					 int nelems,
+					 enum dma_data_direction dir)
+{
+	struct mtk_dip_smem_dev *smem_dev =
+		dev_get_drvdata(dev);
+	void *cpu_addr;
+
+	cpu_addr = mtk_dip_smem_get_cpu_addr(smem_dev, sgl);
+
+	dev_dbg(dev, "%s: paddr(%p),vaddr(%p),size(%d)\n",
+		__func__, sg_phys(sgl), cpu_addr, sgl->length);
+
+	if (cpu_addr)
+		__dma_unmap_area(cpu_addr, sgl->length, dir);
+}
+
+static void mtk_dip_smem_sync_sg_for_device(struct device *dev,
+					    struct scatterlist *sgl,
+					    int nelems,
+					    enum dma_data_direction dir)
+{
+	struct mtk_dip_smem_dev *smem_dev =
+			dev_get_drvdata(dev);
+	void *cpu_addr;
+
+	cpu_addr = mtk_dip_smem_get_cpu_addr(smem_dev, sgl);
+
+	dev_dbg(dev, "%s: pa(%p),va(%p),size(%d),dir(%d)\n",
+		__func__, sg_phys(sgl), cpu_addr, sgl->length, dir);
+
+	if (cpu_addr)
+		__dma_map_area(cpu_addr, sgl->length, dir);
+}
+
+static int mtk_dip_smem_setup_dma_ops(struct device *dev,
+				      struct device *default_alloc_dev)
+{
+	memcpy((void *)&smem_dma_ops, default_alloc_dev->dma_ops,
+	       sizeof(smem_dma_ops));
+
+	smem_dma_ops.get_sgtable =
+		mtk_dip_smem_get_sgtable;
+	smem_dma_ops.sync_sg_for_device =
+		mtk_dip_smem_sync_sg_for_device;
+	smem_dma_ops.sync_sg_for_cpu =
+		mtk_dip_smem_sync_sg_for_cpu;
+
+	dev->dma_ops = &smem_dma_ops;
+
+	dev_dbg(dev, "setup smem_dma_ops: %p\n", dev->dma_ops);
+
+	return 0;
+}
+
+void mtk_dip_smem_alloc_dev_release(struct mtk_dip_smem_dev *smem_dev)
+{
+	device_unregister(&smem_dev->dev);
+}
+
+int mtk_dip_smem_alloc_dev_init(struct mtk_dip_smem_dev *smem_dev,
+				struct device *parent)
+{
+	int ret;
+	struct device *dev = &smem_dev->dev;
+
+	dev->parent  = parent;
+	dev_set_name(&smem_dev->dev, "dip-smem");
+
+	ret = device_register(dev);
+
+	if (ret)
+		dev_err(parent, "Failed to register smem device\n");
+
+	dev_dbg(dev, "init alloc dev(%p), parent(%p)\n", dev, dev->parent);
+
+	dev_set_drvdata(dev, smem_dev);
+
+	if (dip_reserved_smem) {
+		dma_addr_t dma_addr;
+		phys_addr_t addr;
+		struct iommu_domain *smem_dom;
+		int i;
+		int size_align;
+		struct page **pages;
+		int n_pages;
+		struct sg_table *sgt = &smem_dev->sgt;
+
+		size_align = round_down(dip_reserved_smem->size, PAGE_SIZE);
+		n_pages = size_align >> PAGE_SHIFT;
+		pages = kmalloc_array(n_pages, sizeof(struct page *),
+				      GFP_KERNEL);
+
+		if (!pages)
+			return -ENOMEM;
+
+		for (i = 0; i < n_pages; i++)
+			pages[i] = phys_to_page(dip_reserved_smem->base
+						+ i * PAGE_SIZE);
+
+		ret = sg_alloc_table_from_pages(sgt, pages, n_pages, 0,
+						size_align, GFP_KERNEL);
+
+		if (ret) {
+			dev_err(dev, "failed to get alloca sg table\n");
+			return -ENOMEM;
+		}
+
+		dma_map_sg_attrs(parent, sgt->sgl, sgt->nents,
+				 DMA_BIDIRECTIONAL,
+				 DMA_ATTR_SKIP_CPU_SYNC);
+
+		dma_addr = sg_dma_address(sgt->sgl);
+		smem_dom = iommu_get_domain_for_dev(parent);
+		addr = iommu_iova_to_phys(smem_dom, dma_addr);
+
+		if (addr != dip_reserved_smem->base)
+			dev_warn(dev,
+				 "incorrect pa(%p) from iommu_iova_to_phys, should be %p\n",
+				 addr, dip_reserved_smem->base);
+
+		ret = dma_declare_coherent_memory(dev,
+						  dip_reserved_smem->base,
+						  dma_addr, size_align,
+						  DMA_MEMORY_EXCLUSIVE);
+
+		dev_dbg(dev, "Coherent mem base(%p,%p),size(%lx),ret(%d)\n",
+			dip_reserved_smem->base, dma_addr, size_align, ret);
+
+		smem_dev->smem_base = dip_reserved_smem->base;
+		smem_dev->smem_size = size_align;
+		smem_dev->smem_pages = pages;
+		smem_dev->num_smem_pages = n_pages;
+		smem_dev->smem_dma_base = dma_addr;
+
+		dev_dbg(dev, "smem_dev setting (%p,%lx,%p,%d)\n",
+			smem_dev->smem_base, smem_dev->smem_size,
+			smem_dev->smem_pages, smem_dev->num_smem_pages);
+	}
+
+	ret = mtk_dip_smem_setup_dma_ops(dev, parent);
+
+	return ret;
+}
+
+static int __init mtk_dip_smem_dma_setup(struct reserved_mem *rmem)
+{
+	unsigned long node = rmem->fdt_node;
+
+	if (of_get_flat_dt_prop(node, "reusable", NULL))
+		return -EINVAL;
+
+	if (!of_get_flat_dt_prop(node, "no-map", NULL)) {
+		pr_err("Reserved memory: regions without no-map are not yet supported\n");
+		return -EINVAL;
+	}
+
+	dip_reserved_smem = rmem;
+
+	pr_debug("Reserved memory: created DMA memory pool at %pa, size %ld MiB\n",
+		 &rmem->base, (unsigned long)rmem->size / SZ_1M);
+	return 0;
+}
+
+RESERVEDMEM_OF_DECLARE(mtk_dip_smem,
+		       "mediatek,reserve-memory-dip_smem",
+		       mtk_dip_smem_dma_setup);
+
+MODULE_AUTHOR("Frederic Chen <frederic.chen@mediatek.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Mediatek Camera DIP shared memory alloc device");
+
diff --git a/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-smem.h b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-smem.h
new file mode 100644
index 000000000000..a2f7559cc49d
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-smem.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Frederic Chen <frederic.chen@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MTK_DIP_SMEM_H_
+#define _MTK_DIP_SMEM_H_
+
+#include <linux/dma-mapping.h>
+#include <linux/device.h>
+
+struct mtk_dip_smem_dev {
+	struct device dev;
+	struct sg_table sgt;
+	struct page **smem_pages;
+	int num_smem_pages;
+	phys_addr_t smem_base;
+	dma_addr_t smem_dma_base;
+	int smem_size;
+};
+
+phys_addr_t mtk_dip_smem_iova_to_phys(struct mtk_dip_smem_dev *smem_dev,
+				      dma_addr_t iova);
+int mtk_dip_smem_alloc_dev_init(struct mtk_dip_smem_dev *smem_dev,
+				struct device *default_alloc_dev);
+void mtk_dip_smem_alloc_dev_release(struct mtk_dip_smem_dev *smem_dev);
+
+#endif /*_MTK_DIP_SMEM_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..54d2b5f5b802
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-sys.c
@@ -0,0 +1,1384 @@
+// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Holmes Chiou <holmes.chiou@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/kthread.h>
+#include <linux/pm_runtime.h>
+#include <linux/dma-iommu.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/remoteproc.h>
+#include <linux/platform_data/mtk_scp.h>
+#include "mtk-mdp3-cmdq.h"
+#include "mtk_dip-dev.h"
+#include "mtk_dip-hw.h"
+
+#define DIP_DEV_NAME			"camera-dip"
+
+#define DIP_COMPOSER_THREAD_TIMEOUT     16U
+#define DIP_COMPOSING_WQ_TIMEOUT	16U
+#define DIP_COMPOSING_MAX_NUM		3
+#define DIP_FLUSHING_WQ_TIMEOUT		16U
+#define DIP_MAX_ERR_COUNT		188U
+
+#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)
+
+static inline struct mtk_dip_hw *get_dip_device(struct device *dev)
+{
+	struct mtk_dip_dev *dip_dev = dev_get_drvdata(dev);
+
+	if (dip_dev)
+		return &dip_dev->dip_hw;
+	else
+		return NULL;
+}
+
+static struct img_frameparam *dip_create_framejob(int sequence)
+{
+	struct mtk_dip_hw_frame_job *fjob;
+
+	fjob = kzalloc(sizeof(*fjob), GFP_ATOMIC);
+
+	if (!fjob)
+		return NULL;
+
+	fjob->sequence = sequence;
+
+	return &fjob->fparam;
+}
+
+static void dip_free_framejob(struct img_frameparam *fparam)
+{
+	struct mtk_dip_hw_frame_job *fjob;
+
+	fjob = mtk_dip_fparam_to_job(fparam);
+
+	/* to avoid use after free issue */
+	fjob->sequence = -1;
+
+	kfree(fjob);
+}
+
+static void dip_enable_ccf_clock(struct mtk_dip_hw *dip_hw)
+{
+	struct mtk_dip_dev *dip_dev;
+	int ret;
+
+	dip_dev = mtk_dip_hw_to_dev(dip_hw);
+	ret = pm_runtime_get_sync(dip_hw->larb_dev);
+	if (ret < 0)
+		dev_err(&dip_dev->pdev->dev, "%cannot get smi larb clock\n");
+
+	ret = clk_prepare_enable(dip_hw->dip_clk.img_larb5);
+	if (ret)
+		dev_err(&dip_dev->pdev->dev,
+			"cannot prepare and enable img_larb5 clock\n");
+
+	ret = clk_prepare_enable(dip_hw->dip_clk.img_dip);
+	if (ret)
+		dev_err(&dip_dev->pdev->dev,
+			"cannot prepare and enable img_dip clock\n");
+}
+
+static void dip_disable_ccf_clock(struct mtk_dip_hw *dip_hw)
+{
+	clk_disable_unprepare(dip_hw->dip_clk.img_dip);
+	clk_disable_unprepare(dip_hw->dip_clk.img_larb5);
+	pm_runtime_put_sync(dip_hw->larb_dev);
+}
+
+static int dip_send(struct platform_device *pdev, enum scp_ipi_id id,
+		    void *buf, unsigned int  len, unsigned int wait)
+{
+	return scp_ipi_send(pdev, id, buf, len, wait);
+}
+
+static struct mtk_dip_pipe *get_mtk_dip_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 void call_mtk_dip_pipe_finish(struct mtk_dip_hw *dip_hw,
+				     struct img_ipi_frameparam *iparam)
+{
+	struct mtk_dip_dev *dip_dev;
+	struct mtk_dip_pipe *dip_pipe;
+	enum vb2_buffer_state vbf_state;
+	int pipe_id;
+	int ret;
+
+	if (!dip_hw) {
+		pr_err("%s: can't update buffer status, dip_hw is NULL\n",
+		       __func__);
+		return;
+	}
+
+	dip_dev = mtk_dip_hw_to_dev(dip_hw);
+
+	if (!iparam) {
+		dev_dbg(&dip_dev->pdev->dev, "%s: iparam can't be NULL\n",
+			__func__);
+		return;
+	}
+
+	pipe_id = mtk_dip_pipe_get_pipe_from_job_id(iparam->index);
+	dip_pipe = get_mtk_dip_pipe(dip_dev, pipe_id);
+
+	if (!dip_pipe) {
+		dev_dbg(&dip_dev->pdev->dev,
+			"%s: unknown pipe id(%d)\n", __func__, pipe_id);
+		return;
+	}
+
+	if (iparam->state == FRAME_STATE_ERROR)
+		vbf_state = VB2_BUF_STATE_ERROR;
+	else
+		vbf_state = VB2_BUF_STATE_DONE;
+
+	dev_dbg(&dip_dev->pdev->dev,
+		"%s: ready to return buffers,pipe(%d),pipe_job_id(%d)\n",
+		__func__, pipe_id, iparam->index);
+
+	ret = mtk_dip_pipe_job_finish(dip_pipe, iparam->index, vbf_state);
+
+	if (ret)
+		dev_dbg(&dip_dev->pdev->dev, "%s: finish CB failed(%d)\n",
+			__func__, ret);
+}
+
+static void mtk_dip_notify(void *data)
+{
+	struct mtk_dip_hw *dip_hw;
+	struct mtk_dip_dev *dip_dev;
+	struct img_frameparam *framejob;
+	struct mtk_dip_hw_user_id *user_id;
+	struct mtk_dip_hw_subframe *buf, *tmpbuf;
+	struct img_ipi_frameparam *frameparam;
+	u32 num;
+	bool found = false;
+
+	frameparam = (struct img_ipi_frameparam *)data;
+	dip_hw = (struct mtk_dip_hw *)frameparam->drv_data;
+	dip_dev = mtk_dip_hw_to_dev(dip_hw);
+	framejob = container_of(frameparam,
+				struct img_frameparam,
+				frameparam);
+
+	if (frameparam->state == FRAME_STATE_HW_TIMEOUT) {
+		dip_send(dip_hw->vpu_pdev, SCP_IPI_DIP_FRAME,
+			 (void *)frameparam, sizeof(*frameparam), 0);
+		dev_err(&dip_dev->pdev->dev, "%s: frame no(%d) HW timeout\n",
+			__func__, frameparam->frame_no);
+	}
+
+	mutex_lock(&dip_hw->dip_usedbufferlist.queuelock);
+	list_for_each_entry_safe(buf, tmpbuf,
+				 &dip_hw->dip_usedbufferlist.queue,
+				 list_entry) {
+		if (buf->buffer.pa == frameparam->subfrm_data.pa) {
+			list_del(&buf->list_entry);
+			dip_hw->dip_usedbufferlist.queue_cnt--;
+			found = true;
+			dev_dbg(&dip_dev->pdev->dev,
+				"%s: Found used buffer(%x)\n",
+				__func__, buf->buffer.pa);
+			break;
+		}
+	}
+	mutex_unlock(&dip_hw->dip_usedbufferlist.queuelock);
+
+	if (!found) {
+		dev_err(&dip_dev->pdev->dev,
+			"%s: frame_no(%d), buf(%x), used buf cnt(%d)\n",
+			__func__, frameparam->frame_no,
+			frameparam->subfrm_data.pa,
+			dip_hw->dip_usedbufferlist.queue_cnt);
+
+		frameparam->state = FRAME_STATE_ERROR;
+
+	} else {
+		mutex_lock(&dip_hw->dip_freebufferlist.queuelock);
+		list_add_tail(&buf->list_entry,
+			      &dip_hw->dip_freebufferlist.queue);
+		dip_hw->dip_freebufferlist.queue_cnt++;
+		mutex_unlock(&dip_hw->dip_freebufferlist.queuelock);
+
+		frameparam->state = FRAME_STATE_DONE;
+	}
+
+	call_mtk_dip_pipe_finish(dip_hw, frameparam);
+
+	found = false;
+	mutex_lock(&dip_hw->dip_useridlist.queuelock);
+	list_for_each_entry(user_id,
+			    &dip_hw->dip_useridlist.queue,
+			    list_entry) {
+		if (mtk_dip_pipe_get_pipe_from_job_id(frameparam->index)
+			== user_id->id) {
+			user_id->num--;
+			dev_dbg(&dip_dev->pdev->dev,
+				"%s: user_id(%x) found, cnt(%d)\n",
+				__func__, user_id->id, user_id->num);
+			found = true;
+			break;
+		}
+	}
+	mutex_unlock(&dip_hw->dip_useridlist.queuelock);
+	wake_up(&dip_hw->flushing_wq);
+	dev_dbg(&dip_dev->pdev->dev,
+		"%s: frame_no(%d) is finished\n",
+		__func__, framejob->frameparam.frame_no);
+	dip_free_framejob(framejob);
+
+	num = atomic_dec_return(&dip_hw->num_running);
+	dev_dbg(&dip_dev->pdev->dev, "%s: running cnt(%d)\n", __func__, num);
+}
+
+static void mdp_cb_worker(struct work_struct *work)
+{
+	struct mtk_dip_hw_mdpcb_work *mdpcb_work;
+
+	mdpcb_work = container_of(work, struct mtk_dip_hw_mdpcb_work,
+				  frame_work);
+	mtk_dip_notify(mdpcb_work->frameparams);
+	kfree(mdpcb_work);
+}
+
+static struct img_ipi_frameparam *convert_to_fparam(struct cmdq_cb_data *data)
+{
+	struct mtk_dip_hw *dip_hw;
+	struct mtk_dip_dev *dip_dev;
+	struct mtk_dip_hw_frame_job *fjob;
+	struct img_ipi_frameparam *ipi_fparam;
+
+	if (!data) {
+		pr_err("%s: cmdq_cb_data can't be NULL\n",
+		       __func__);
+		return NULL;
+	}
+
+	if (data->sta != CMDQ_CB_NORMAL)
+		pr_debug("%s: got CMDQ CB(%d) without CMDQ_CB_NORMAL\n",
+			 __func__, data->sta);
+
+	if (!data->data) {
+		pr_err("%s: got NULL in cmdq_cb_data\n",
+		       __func__);
+		return NULL;
+	}
+
+	fjob = mtk_dip_ipi_fparam_to_job(data->data);
+
+	if (fjob->sequence == -1) {
+		pr_err("%s: Invalid cmdq_cb_data(%p)\n",
+		       __func__, data);
+		ipi_fparam = NULL;
+	} else {
+		ipi_fparam = &fjob->fparam.frameparam;
+		dip_hw = (struct mtk_dip_hw *)ipi_fparam->drv_data;
+		dip_dev = mtk_dip_hw_to_dev(dip_hw);
+	}
+
+	dev_dbg(&dip_dev->pdev->dev, "%s: framejob(%p), seq(%d)\n",
+		__func__, fjob, fjob->sequence);
+	dev_dbg(&dip_dev->pdev->dev, "%s: idx(%d), no(%d), s(%d), n_in(%d), n_out(%d), drv(%p)\n",
+		__func__,
+		fjob->fparam.frameparam.index,
+		fjob->fparam.frameparam.frame_no,
+		fjob->fparam.frameparam.state,
+		fjob->fparam.frameparam.num_inputs,
+		fjob->fparam.frameparam.num_outputs,
+		fjob->fparam.frameparam.drv_data
+	);
+
+	return ipi_fparam;
+}
+
+/* Maybe in IRQ context of cmdq */
+static void dip_mdp_cb_func(struct cmdq_cb_data data)
+{
+	struct img_ipi_frameparam *frameparam;
+	struct mtk_dip_hw *dip_hw;
+	struct mtk_dip_dev *dip_dev;
+	struct mtk_dip_hw_mdpcb_work *mdpcb_work;
+
+	frameparam = convert_to_fparam(&data);
+
+	if (!frameparam) {
+		pr_err("%s: return due to NULL cmdq_cb_data)",
+		       __func__, &data);
+		return;
+	}
+
+	dip_hw = (struct mtk_dip_hw *)frameparam->drv_data;
+	dip_dev = mtk_dip_hw_to_dev(dip_hw);
+
+	mdpcb_work = kzalloc(sizeof(*mdpcb_work), GFP_ATOMIC);
+
+	if (WARN_ONCE(!mdpcb_work, "%s: frame_no(%d) is lost",
+		      __func__, frameparam->frame_no))
+		return;
+
+	INIT_WORK(&mdpcb_work->frame_work, mdp_cb_worker);
+	mdpcb_work->frameparams = frameparam;
+	if (data.sta != CMDQ_CB_NORMAL)
+		mdpcb_work->frameparams->state = FRAME_STATE_HW_TIMEOUT;
+
+	queue_work(dip_hw->mdpcb_workqueue, &mdpcb_work->frame_work);
+}
+
+static void dip_vpu_handler(void *data, unsigned int len, void *priv)
+{
+	struct img_frameparam *framejob;
+	struct img_ipi_frameparam *frameparam;
+	struct mtk_dip_hw *dip_hw;
+	struct mtk_dip_dev *dip_dev;
+	unsigned long flags;
+	u32 num;
+
+	if (WARN_ONCE(!data, "%s: failed due to NULL data\n", __func__))
+		return;
+
+	frameparam = (struct img_ipi_frameparam *)data;
+	framejob = dip_create_framejob(frameparam->index);
+
+	if (WARN_ONCE(!framejob, "%s: frame_no(%d) is lost\n",
+		      __func__, frameparam->frame_no))
+		return;
+
+	dip_hw = (struct mtk_dip_hw *)frameparam->drv_data;
+	dip_dev = mtk_dip_hw_to_dev(dip_hw);
+
+	wake_up(&dip_hw->composing_wq);
+	memcpy(&framejob->frameparam, data, sizeof(framejob->frameparam));
+	num = atomic_dec_return(&dip_hw->num_composing);
+
+	spin_lock_irqsave(&dip_hw->dip_gcejoblist.queuelock, flags);
+	list_add_tail(&framejob->list_entry, &dip_hw->dip_gcejoblist.queue);
+	dip_hw->dip_gcejoblist.queue_cnt++;
+	spin_unlock_irqrestore(&dip_hw->dip_gcejoblist.queuelock, flags);
+
+	dev_dbg(&dip_dev->pdev->dev,
+		"%s: frame_no(%d) is back, composing num(%d)\n",
+		__func__, frameparam->frame_no, num);
+
+	wake_up(&dip_hw->dip_runner_thread.wq);
+}
+
+static int dip_runner_func(void *data)
+{
+	struct img_frameparam *framejob;
+	struct mtk_dip_hw *dip_hw;
+	struct mtk_dip_dev *dip_dev;
+	struct mtk_dip_hw_user_id *user_id;
+	unsigned long flags;
+	bool found;
+	u32 queuecnt, num;
+	int ret;
+
+	dip_hw = (struct mtk_dip_hw *)data;
+	dip_dev = mtk_dip_hw_to_dev(dip_hw);
+
+	while (1) {
+		spin_lock_irqsave(&dip_hw->dip_gcejoblist.queuelock, flags);
+		queuecnt = dip_hw->dip_gcejoblist.queue_cnt;
+		spin_unlock_irqrestore(&dip_hw->dip_gcejoblist.queuelock,
+				       flags);
+
+		ret = wait_event_interruptible_timeout
+			(dip_hw->dip_runner_thread.wq,
+			 queuecnt || kthread_should_stop(),
+			 msecs_to_jiffies(DIP_COMPOSER_THREAD_TIMEOUT));
+
+		if (kthread_should_stop())
+			break;
+
+		if (ret == 0) {
+			/* Timeout */
+			ret = -ETIME;
+		} else if (ret == -ERESTARTSYS) {
+			dev_err(&dip_dev->pdev->dev,
+				"%s: interrupted by a signal\n", __func__);
+		}
+
+		if (queuecnt > 0) {
+			spin_lock_irqsave(&dip_hw->dip_gcejoblist.queuelock,
+					  flags);
+			framejob = list_first_entry
+				(&dip_hw->dip_gcejoblist.queue,
+				 struct img_frameparam, list_entry);
+
+			dip_hw->dip_gcejoblist.queue_cnt--;
+			list_del(&framejob->list_entry);
+			spin_unlock_irqrestore
+				(&dip_hw->dip_gcejoblist.queuelock, flags);
+
+			found = false;
+			mutex_lock(&dip_hw->dip_useridlist.queuelock);
+			list_for_each_entry(user_id,
+					    &dip_hw->dip_useridlist.queue,
+					    list_entry) {
+				int id = framejob->frameparam.index;
+
+				if (mtk_dip_pipe_get_pipe_from_job_id(id) ==
+					user_id->id) {
+					found = true;
+					break;
+				}
+			}
+			mutex_unlock(&dip_hw->dip_useridlist.queuelock);
+
+			if (!found) {
+				dev_err(&dip_dev->pdev->dev,
+					"%s: frame_no(%d), idx(0x%x) is abnormal\n",
+					__func__,
+					framejob->frameparam.frame_no,
+					framejob->frameparam.index);
+				/*
+				 * Due to error index, DIP driver could NOT
+				 * notify the V4L2 common driver to
+				 * return buffer
+				 */
+				dip_free_framejob(framejob);
+				continue;
+			}
+
+			mutex_lock(&dip_hw->dip_useridlist.queuelock);
+			if (user_id->state == DIP_STATE_STREAMOFF) {
+				user_id->num--;
+				mutex_unlock
+					(&dip_hw->dip_useridlist.queuelock);
+				framejob->frameparam.state =
+					FRAME_STATE_STREAMOFF;
+				call_mtk_dip_pipe_finish(dip_hw,
+							 &framejob->frameparam);
+
+				dev_dbg(&dip_dev->pdev->dev,
+					"%s: user_id(%x) streamoff, current num(%d); frame_no(%d) flushed\n",
+					__func__, user_id->id, user_id->num,
+					framejob->frameparam.frame_no);
+
+				dip_free_framejob(framejob);
+				continue;
+			}
+			mutex_unlock(&dip_hw->dip_useridlist.queuelock);
+
+			dev_dbg(&dip_dev->pdev->dev,
+				"%s: MDP run frame_no(%d) and the rest joblist cnt(%d)\n",
+				__func__, framejob->frameparam.frame_no,
+				dip_hw->dip_gcejoblist.queue_cnt);
+
+			/*
+			 * Call MDP/GCE API to do HW excecution
+			 * Pass the framejob to MDP driver
+			 */
+			framejob->frameparam.state = FRAME_STATE_COMPOSING;
+
+			mdp_cmdq_sendtask
+				(dip_hw->mdp_pdev,
+				 (struct img_config *)
+					framejob->frameparam.config_data.va,
+				 &framejob->frameparam, NULL, false,
+				 dip_mdp_cb_func,
+				 (void *)&framejob->frameparam);
+
+			num = atomic_inc_return(&dip_hw->num_running);
+			dev_dbg(&dip_dev->pdev->dev,
+				"%s,MDP running num(%d)\n", __func__, num);
+		}
+
+	};
+
+	return 0;
+}
+
+static void dip_submit_worker(struct work_struct *work)
+{
+	struct mtk_dip_hw_submit_work *dip_submit_work =
+		container_of(work, struct mtk_dip_hw_submit_work, frame_work);
+	struct mtk_dip_hw *dip_hw = dip_submit_work->dip_hw;
+	struct mtk_dip_dev *dip_dev = mtk_dip_hw_to_dev(dip_hw);
+	struct mtk_dip_hw_work *dip_work;
+	struct mtk_dip_hw_subframe *buf;
+	u32 len, num;
+	int ret;
+
+	num  = atomic_read(&dip_hw->num_composing);
+
+	mutex_lock(&dip_hw->dip_worklist.queuelock);
+	dip_work = list_first_entry(&dip_hw->dip_worklist.queue,
+				    struct mtk_dip_hw_work, list_entry);
+	list_del(&dip_work->list_entry);
+	dip_hw->dip_worklist.queue_cnt--;
+	len = dip_hw->dip_worklist.queue_cnt;
+	mutex_unlock(&dip_hw->dip_worklist.queuelock);
+
+	mutex_lock(&dip_hw->dip_useridlist.queuelock);
+	if (dip_work->user_id->state == DIP_STATE_STREAMOFF) {
+		dip_work->user_id->num--;
+		mutex_unlock(&dip_hw->dip_useridlist.queuelock);
+
+		dip_work->frameparams.state = FRAME_STATE_STREAMOFF;
+		call_mtk_dip_pipe_finish(dip_hw, &dip_work->frameparams);
+
+		dev_dbg(&dip_dev->pdev->dev,
+			"%s: user_id(%x) is streamoff,num(%d),frame_no(%d),idx:0x%x\n",
+			 __func__, dip_work->user_id->id,
+			 dip_work->user_id->num,
+			 dip_work->frameparams.frame_no,
+			 dip_work->frameparams.index);
+
+		goto free_work_list;
+	}
+	mutex_unlock(&dip_hw->dip_useridlist.queuelock);
+
+	while (num >= DIP_COMPOSING_MAX_NUM) {
+		ret = wait_event_interruptible_timeout
+			(dip_hw->composing_wq,
+			 (num < DIP_COMPOSING_MAX_NUM),
+			 msecs_to_jiffies(DIP_COMPOSING_WQ_TIMEOUT));
+
+		if (ret == -ERESTARTSYS)
+			dev_err(&dip_dev->pdev->dev,
+				"%s: interrupted by a signal\n", __func__);
+		else if (ret == 0)
+			dev_dbg(&dip_dev->pdev->dev,
+				"%s: timeout frame_no(%d),num(%d)\n",
+				__func__, dip_work->frameparams.frame_no,
+				num);
+		else
+			dev_dbg(&dip_dev->pdev->dev,
+				"%s: wakeup frame_no(%d),num(%d)\n",
+				__func__, dip_work->frameparams.frame_no, num);
+
+		num = atomic_read(&dip_hw->num_composing);
+	};
+
+	mutex_lock(&dip_hw->dip_freebufferlist.queuelock);
+	if (list_empty(&dip_hw->dip_freebufferlist.queue)) {
+		mutex_unlock(&dip_hw->dip_freebufferlist.queuelock);
+
+		dev_err(&dip_dev->pdev->dev,
+			"%s: frame_no(%d), idx(0x%x), no free buffer(%d)\n",
+			__func__, dip_work->frameparams.frame_no,
+			dip_work->frameparams.index,
+			dip_hw->dip_freebufferlist.queue_cnt);
+
+		/*
+		 * Call callback to notify V4L2 common framework
+		 * for failure of enqueue
+		 */
+		dip_work->frameparams.state = FRAME_STATE_ERROR;
+		call_mtk_dip_pipe_finish(dip_hw, &dip_work->frameparams);
+
+		mutex_lock(&dip_hw->dip_useridlist.queuelock);
+		dip_work->user_id->num--;
+		mutex_unlock(&dip_hw->dip_useridlist.queuelock);
+
+		goto free_work_list;
+	}
+
+	buf = list_first_entry(&dip_hw->dip_freebufferlist.queue,
+			       struct mtk_dip_hw_subframe,
+			       list_entry);
+	list_del(&buf->list_entry);
+	dip_hw->dip_freebufferlist.queue_cnt--;
+	mutex_unlock(&dip_hw->dip_freebufferlist.queuelock);
+
+	mutex_lock(&dip_hw->dip_usedbufferlist.queuelock);
+	list_add_tail(&buf->list_entry, &dip_hw->dip_usedbufferlist.queue);
+	dip_hw->dip_usedbufferlist.queue_cnt++;
+	mutex_unlock(&dip_hw->dip_usedbufferlist.queuelock);
+
+	memcpy(&dip_work->frameparams.subfrm_data,
+	       &buf->buffer, sizeof(buf->buffer));
+	memset((char *)buf->buffer.va, 0, DIP_SUB_FRM_SZ);
+	memcpy(&dip_work->frameparams.config_data,
+	       &buf->config_data, sizeof(buf->config_data));
+	memset((char *)buf->config_data.va, 0, DIP_COMP_SZ);
+
+	if (dip_work->frameparams.tuning_data.pa == 0) {
+		dev_dbg(&dip_dev->pdev->dev,
+			"%s: frame_no(%d) has no tuning_data\n",
+			__func__, dip_work->frameparams.frame_no);
+
+		memcpy(&dip_work->frameparams.tuning_data,
+		       &buf->tuning_buf, sizeof(buf->tuning_buf));
+		memset((char *)buf->tuning_buf.va, 0, DIP_TUNING_SZ);
+		/*
+		 * When user enqueued without tuning buffer,
+		 * it would use driver internal buffer.
+		 * So, tuning_data.va should be 0
+		 */
+		dip_work->frameparams.tuning_data.va = 0;
+	}
+
+	dip_work->frameparams.drv_data = (u64)dip_hw;
+	dip_work->frameparams.state = FRAME_STATE_COMPOSING;
+
+	memcpy((void *)buf->frameparam.va, &dip_work->frameparams,
+	       sizeof(dip_work->frameparams));
+
+	dip_send(dip_hw->vpu_pdev, SCP_IPI_DIP_FRAME,
+		 (void *)&dip_work->frameparams,
+		 sizeof(dip_work->frameparams), 0);
+	num = atomic_inc_return(&dip_hw->num_composing);
+
+free_work_list:
+
+	dev_dbg(&dip_dev->pdev->dev,
+		"%s, free: frame_no(%d),idx(0x%x),worklist cnt(%d),composing num(%d)\n",
+		__func__, dip_work->frameparams.frame_no,
+		dip_work->frameparams.index, len, num);
+
+	kfree(dip_work);
+}
+
+static void mtk_dip_hw_set_clk(struct mtk_dip_hw *dip_hw, bool enable)
+{
+	struct mtk_dip_dev *dip_dev = mtk_dip_hw_to_dev(dip_hw);
+
+	if (enable) {
+		dev_dbg(&dip_dev->pdev->dev, "CCF:prepare_enable clk\n");
+		dip_enable_ccf_clock(dip_hw);
+	} else {
+		dev_dbg(&dip_dev->pdev->dev, "CCF:disable_unprepare clk\n");
+		dip_disable_ccf_clock(dip_hw);
+	}
+}
+
+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);
+	u32 i;
+	dma_addr_t scp_daddr;
+	u64 scp_mem_va;
+	int ret = 0;
+
+	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;
+	}
+
+	init_waitqueue_head(&dip_hw->dip_runner_thread.wq);
+
+	/*  All lists in DIP initialization */
+	INIT_LIST_HEAD(&dip_hw->dip_gcejoblist.queue);
+	spin_lock_init(&dip_hw->dip_gcejoblist.queuelock);
+	dip_hw->dip_gcejoblist.queue_cnt = 0;
+
+	INIT_LIST_HEAD(&dip_hw->dip_freebufferlist.queue);
+	mutex_init(&dip_hw->dip_freebufferlist.queuelock);
+	dip_hw->dip_freebufferlist.queue_cnt = 0;
+
+	INIT_LIST_HEAD(&dip_hw->dip_usedbufferlist.queue);
+	mutex_init(&dip_hw->dip_usedbufferlist.queuelock);
+	dip_hw->dip_usedbufferlist.queue_cnt = 0;
+
+	dip_hw->mdpcb_workqueue =
+		create_singlethread_workqueue("mdp_callback");
+	if (!dip_hw->mdpcb_workqueue) {
+		dev_err(&dip_dev->pdev->dev,
+			"%s: unable to alloc mdpcb workqueue\n", __func__);
+		ret = -ENOMEM;
+		goto err_alloc_mdpcb_wq;
+	}
+
+	dip_hw->composer_wq =
+		create_singlethread_workqueue("dip_composer");
+	if (!dip_hw->composer_wq) {
+		dev_err(&dip_dev->pdev->dev,
+			"%s: unable to alloc composer workqueue\n", __func__);
+		ret = -ENOMEM;
+		goto err_alloc_composer_wq;
+	}
+	init_waitqueue_head(&dip_hw->composing_wq);
+	init_waitqueue_head(&dip_hw->flushing_wq);
+
+	dip_hw->submit_work.dip_hw = dip_hw;
+	INIT_WORK(&dip_hw->submit_work.frame_work, dip_submit_worker);
+
+	INIT_LIST_HEAD(&dip_hw->dip_worklist.queue);
+	mutex_init(&dip_hw->dip_worklist.queuelock);
+	dip_hw->dip_worklist.queue_cnt = 0;
+
+	INIT_LIST_HEAD(&dip_hw->dip_useridlist.queue);
+	mutex_init(&dip_hw->dip_useridlist.queuelock);
+	dip_hw->dip_useridlist.queue_cnt = 0;
+
+	dip_hw->dip_runner_thread.thread =
+		kthread_run(dip_runner_func, (void *)dip_hw, "dip_runner");
+
+	if (IS_ERR(dip_hw->dip_runner_thread.thread)) {
+		dev_err(&dip_dev->pdev->dev, "%s: unable to alloc workqueue\n",
+			__func__);
+		ret = PTR_ERR(dip_hw->dip_runner_thread.thread);
+		dip_hw->dip_runner_thread.thread = NULL;
+		goto err_create_thread;
+	}
+
+	scp_mem_va = scp_get_reserve_mem_virt(SCP_DIP_MEM_ID);
+	scp_daddr = scp_get_reserve_mem_phys(SCP_DIP_MEM_ID);
+	dip_hw->scp_workingbuf_addr = scp_daddr + DIP_SCP_WORKINGBUF_OFFSET;
+	dev_dbg(&dip_dev->pdev->dev,
+		"%s: scp_mem_va(%p) ,pa(%p)\n", __func__, scp_mem_va,
+		(u64)scp_daddr);
+
+	scp_ipi_register(dip_hw->vpu_pdev, SCP_IPI_DIP_FRAME,
+			 dip_vpu_handler, NULL);
+
+	for (i = 0; i < DIP_SUB_FRM_DATA_NUM; i++) {
+		u32 size_align;
+		struct mtk_dip_hw_subframe *buf;
+		struct sg_table *sgt;
+		struct page **pages;
+		u32 npages, j;
+
+		buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+		if (!buf) {
+			ret = -ENOMEM;
+			goto err_create_thread;
+		}
+
+		/*
+		 * Total: 0 ~ 72 KB
+		 * SubFrame: 0 ~ 16 KB
+		 */
+		buf->buffer.pa = scp_daddr + i * DIP_FRM_SZ;
+		buf->buffer.va = scp_mem_va + i * DIP_FRM_SZ;
+
+		/* Tuning: 16 ~ 48 KB */
+		buf->tuning_buf.pa = buf->buffer.pa + DIP_TUNING_OFFSET;
+		buf->tuning_buf.va = buf->buffer.va + DIP_TUNING_OFFSET;
+
+		/* Config_data: 48 ~ 72 KB */
+		buf->config_data.pa = buf->buffer.pa + DIP_COMP_OFFSET;
+		buf->config_data.va = buf->buffer.va + DIP_COMP_OFFSET;
+
+		/* Frame parameters: 72 ~ 76 KB */
+		buf->frameparam.pa = buf->buffer.pa + DIP_FRAMEPARAM_OFFSET;
+		buf->frameparam.va = buf->buffer.va + DIP_FRAMEPARAM_OFFSET;
+
+		/* get iova */
+		npages = (DIP_SUB_FRM_SZ + DIP_TUNING_SZ) >> PAGE_SHIFT;
+		pages = kmalloc_array(npages,
+				      sizeof(struct page *),
+				      GFP_KERNEL);
+		if (!pages) {
+			kfree(buf);
+			ret = -ENOMEM;
+			goto err_create_thread;
+		}
+
+		sgt = &buf->table;
+		for (j = 0; j < npages; j++)
+			pages[j] =
+				phys_to_page(buf->buffer.pa + j * PAGE_SIZE);
+
+		size_align = round_up(DIP_SUB_FRM_SZ + DIP_TUNING_SZ,
+				      PAGE_SIZE);
+		ret = sg_alloc_table_from_pages(sgt, pages, npages,
+						0, size_align, GFP_KERNEL);
+		if (ret < 0) {
+			dev_err(&dip_dev->pdev->dev,
+				"%s: failed to get sgt from pages\n", __func__);
+			ret = -ENOMEM;
+			kfree(pages);
+			kfree(buf);
+			goto err_create_thread;
+		}
+
+		dma_map_sg_attrs(&dip_dev->pdev->dev, sgt->sgl, sgt->nents,
+				 DMA_BIDIRECTIONAL, DMA_ATTR_SKIP_CPU_SYNC);
+		buf->buffer.iova = sg_dma_address(buf->table.sgl);
+		buf->tuning_buf.iova = buf->buffer.iova +
+			DIP_TUNING_OFFSET;
+
+		dev_dbg(&dip_dev->pdev->dev,
+			"%s: buf(%d), pa(%p), iova(%p)\n",
+			__func__, i, buf->buffer.pa, buf->buffer.iova);
+
+		dev_dbg(&dip_dev->pdev->dev,
+			"%s: config_data(%d), pa(%p), iova(%p)\n",
+			__func__, i, buf->config_data.pa, buf->config_data.va);
+
+		dev_dbg(&dip_dev->pdev->dev,
+			"%s: tuning_buf(%d), pa(%p), iova(%p)\n",
+			__func__, i, buf->tuning_buf.pa, buf->tuning_buf.iova);
+
+		dev_dbg(&dip_dev->pdev->dev,
+			"%s: frameparam(%d), pa(%p), iova(%p)\n",
+			__func__, i, buf->frameparam.pa, buf->frameparam.va);
+
+		list_add_tail(&buf->list_entry,
+			      &dip_hw->dip_freebufferlist.queue);
+		dip_hw->dip_freebufferlist.queue_cnt++;
+		kfree(pages);
+	}
+
+	return 0;
+
+err_create_thread:
+	mutex_destroy(&dip_hw->dip_useridlist.queuelock);
+	mutex_destroy(&dip_hw->dip_worklist.queuelock);
+	mutex_destroy(&dip_hw->dip_usedbufferlist.queuelock);
+	mutex_destroy(&dip_hw->dip_freebufferlist.queuelock);
+
+err_alloc_composer_wq:
+	destroy_workqueue(dip_hw->composer_wq);
+
+err_alloc_mdpcb_wq:
+	destroy_workqueue(dip_hw->mdpcb_workqueue);
+
+	return ret;
+}
+
+static int mtk_dip_hw_res_release(struct mtk_dip_hw *dip_hw)
+{
+	u32 i = 0;
+	struct mtk_dip_dev *dip_dev = mtk_dip_hw_to_dev(dip_hw);
+	struct mtk_dip_hw_subframe *buf, *tmpbuf;
+	struct mtk_dip_hw_work *dip_work, *tmp_work;
+	struct mtk_dip_hw_user_id  *dip_userid, *tmp_id;
+
+	dev_dbg(&dip_dev->pdev->dev, "%s: composer work queue(%d)\n",
+		__func__, dip_hw->dip_worklist.queue_cnt);
+
+	mutex_lock(&dip_hw->dip_worklist.queuelock);
+	list_for_each_entry_safe(dip_work, tmp_work,
+				 &dip_hw->dip_worklist.queue,
+				 list_entry) {
+		list_del(&dip_work->list_entry);
+		dev_dbg(&dip_dev->pdev->dev, "%s: dip work frame no(%d)\n",
+			__func__, dip_work->frameparams.frame_no);
+		kfree(dip_work);
+		dip_hw->dip_worklist.queue_cnt--;
+	}
+	mutex_unlock(&dip_hw->dip_worklist.queuelock);
+
+	if (dip_hw->dip_worklist.queue_cnt != 0)
+		dev_dbg(&dip_dev->pdev->dev,
+			"%s: dip_worklist is not empty(%d)\n",
+			__func__, dip_hw->dip_worklist.queue_cnt);
+
+	mutex_lock(&dip_hw->dip_useridlist.queuelock);
+	list_for_each_entry_safe(dip_userid, tmp_id,
+				 &dip_hw->dip_useridlist.queue,
+				 list_entry) {
+		list_del(&dip_userid->list_entry);
+		dev_dbg(&dip_dev->pdev->dev, "%s: dip user id(0x%x)\n",
+			__func__, dip_userid->id);
+		kfree(dip_userid);
+		dip_hw->dip_useridlist.queue_cnt--;
+	}
+	mutex_unlock(&dip_hw->dip_useridlist.queuelock);
+
+	if (dip_hw->dip_useridlist.queue_cnt != 0)
+		dev_dbg(&dip_dev->pdev->dev,
+			"%s: dip_useridlist is not empty(%d)\n",
+			__func__, dip_hw->dip_useridlist.queue_cnt);
+
+	flush_workqueue(dip_hw->mdpcb_workqueue);
+	destroy_workqueue(dip_hw->mdpcb_workqueue);
+	dip_hw->mdpcb_workqueue = NULL;
+
+	flush_workqueue(dip_hw->composer_wq);
+	destroy_workqueue(dip_hw->composer_wq);
+	dip_hw->composer_wq = NULL;
+
+	atomic_set(&dip_hw->num_composing, 0);
+	atomic_set(&dip_hw->num_running, 0);
+
+	kthread_stop(dip_hw->dip_runner_thread.thread);
+	dip_hw->dip_runner_thread.thread = NULL;
+
+	atomic_set(&dip_hw->dip_user_cnt, 0);
+	atomic_set(&dip_hw->dip_stream_cnt, 0);
+	atomic_set(&dip_hw->dip_enque_cnt, 0);
+
+	/* All the buffer should be in the freebufferlist when release */
+	list_for_each_entry_safe(buf, tmpbuf,
+				 &dip_hw->dip_freebufferlist.queue,
+				 list_entry) {
+		struct sg_table *sgt = &buf->table;
+
+		dev_dbg(&dip_dev->pdev->dev,
+			"%s: buf(%d) pa(%p)\n", __func__, i,
+			buf->buffer.pa);
+		dip_hw->dip_freebufferlist.queue_cnt--;
+		dma_unmap_sg_attrs(&dip_dev->pdev->dev, sgt->sgl,
+				   sgt->orig_nents,
+				   DMA_BIDIRECTIONAL, DMA_ATTR_SKIP_CPU_SYNC);
+		sg_free_table(sgt);
+		list_del(&buf->list_entry);
+		kfree(buf);
+		buf = NULL;
+		i++;
+	}
+
+	if (dip_hw->dip_freebufferlist.queue_cnt != 0 &&
+	    i != DIP_SUB_FRM_DATA_NUM)
+		dev_err(&dip_dev->pdev->dev,
+			"%s: dip_freebufferlist is not empty (%d/%d)\n",
+			__func__, dip_hw->dip_freebufferlist.queue_cnt, i);
+
+	mutex_destroy(&dip_hw->dip_useridlist.queuelock);
+	mutex_destroy(&dip_hw->dip_worklist.queuelock);
+	mutex_destroy(&dip_hw->dip_usedbufferlist.queuelock);
+	mutex_destroy(&dip_hw->dip_freebufferlist.queuelock);
+
+	return 0;
+}
+
+static int mtk_dip_hw_flush_by_id(struct mtk_dip_hw *dip_hw,
+				  u16 id,
+			       struct mtk_dip_hw_user_id *user_id)
+{
+	struct mtk_dip_dev *dip_dev;
+	u32 num, err_cnt;
+	int ret;
+
+	dip_dev = mtk_dip_hw_to_dev(dip_hw);
+	err_cnt = 0;
+	do {
+		mutex_lock(&dip_hw->dip_useridlist.queuelock);
+		num = user_id->num;
+		mutex_unlock(&dip_hw->dip_useridlist.queuelock);
+
+		ret = wait_event_interruptible_timeout
+			(dip_hw->flushing_wq,
+			 (num == 0),
+			 msecs_to_jiffies(DIP_FLUSHING_WQ_TIMEOUT));
+
+		if (ret == -ERESTARTSYS)
+			dev_err(&dip_dev->pdev->dev,
+				"%s: interrupted by a signal, num(%d)\n",
+				__func__, num);
+		else if (ret == 0)
+			dev_dbg(&dip_dev->pdev->dev,
+				"%s: timeout num(%d)\n", __func__, num);
+		else
+			dev_dbg(&dip_dev->pdev->dev,
+				"%s: wakeup  num(%d)\n", __func__, num);
+
+		err_cnt++;
+
+		if (num > 0 && err_cnt >= DIP_MAX_ERR_COUNT) {
+			dev_err(&dip_dev->pdev->dev,
+				"%s: flushing is aborted, num(%d), err_cnt(%d)\n",
+				__func__, num, err_cnt);
+			return -EINVAL;
+		}
+
+	} while (num > 0);
+
+	dev_dbg(&dip_dev->pdev->dev, "Flushing is done num: %d\n", num);
+	return 0;
+}
+
+int mtk_dip_hw_connect(struct mtk_dip_hw *dip_hw)
+{
+	int ret;
+	s32 usercount;
+	struct mtk_dip_dev *dip_dev = mtk_dip_hw_to_dev(dip_hw);
+	phandle rproc_phandle;
+
+	if (!dip_hw) {
+		pr_err("%s: dip_hw can't be NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	usercount = atomic_inc_return(&dip_hw->dip_user_cnt);
+
+	if (usercount == 1) {
+		struct img_ipi_frameparam frameparam;
+
+		dip_hw->vpu_pdev = scp_get_pdev(dip_dev->pdev);
+		if (!dip_hw->vpu_pdev) {
+			dev_err(&dip_dev->pdev->dev,
+				"%s: failed to get VPU 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;
+		}
+
+		/*
+		 * Return 0 if downloading firmware successfully,
+		 * otherwise it is failed.
+		 */
+		if (rproc_boot(dip_hw->rproc_handle)) {
+			dev_err(&dip_dev->pdev->dev,
+				"%s: FW load failed(rproc:%p):%d\n",
+				__func__, dip_hw->rproc_handle,	ret);
+			return -EINVAL;
+		}
+
+		dev_dbg(&dip_dev->pdev->dev, "%s: FW loaded(rproc:%p)\n",
+			__func__, dip_hw->rproc_handle);
+
+		/* Enable clocks */
+		mtk_dip_hw_set_clk(dip_hw, true);
+		/* DIP HW INIT */
+		memset(&frameparam, 0, sizeof(frameparam));
+		/* SCP only support 32bits address */
+		frameparam.drv_data = (u64)dip_hw;
+		frameparam.state = FRAME_STATE_INIT;
+		dip_send(dip_hw->vpu_pdev, SCP_IPI_DIP_INIT,
+			 (void *)&frameparam, sizeof(frameparam), 0);
+
+		mtk_dip_hw_res_init(dip_hw);
+	}
+
+	dev_dbg(&dip_dev->pdev->dev, "%s: dip_hw connected, usercount(%d)\n",
+		__func__, usercount);
+
+	return 0;
+}
+
+int mtk_dip_hw_disconnect(struct mtk_dip_hw *dip_hw)
+{
+	struct mtk_dip_dev *dip_dev = mtk_dip_hw_to_dev(dip_hw);
+
+	if (atomic_dec_and_test(&dip_hw->dip_user_cnt)) {
+		mtk_dip_hw_res_release(dip_hw);
+		mtk_dip_hw_set_clk(dip_hw, false);
+	}
+
+	dev_dbg(&dip_dev->pdev->dev,
+		"%s: dip_hw disconnected, usercount(%d)\n",
+		__func__, atomic_read(&dip_hw->dip_user_cnt));
+
+	return 0;
+}
+
+int mtk_dip_hw_streamon(struct mtk_dip_hw  *dip_hw, u16 id)
+{
+	struct mtk_dip_dev *dip_dev = mtk_dip_hw_to_dev(dip_hw);
+	struct mtk_dip_hw_user_id *user_id;
+	s32 count, len;
+
+	count = atomic_inc_return(&dip_hw->dip_stream_cnt);
+
+	dev_dbg(&dip_dev->pdev->dev, "%s: id(0x%x)\n", __func__, id);
+
+	user_id = kzalloc(sizeof(*user_id), GFP_KERNEL);
+
+	if (!user_id)
+		return -ENOMEM;
+
+	user_id->id = id;
+	user_id->state = DIP_STATE_STREAMON;
+
+	mutex_lock(&dip_hw->dip_useridlist.queuelock);
+	list_add_tail(&user_id->list_entry, &dip_hw->dip_useridlist.queue);
+	dip_hw->dip_useridlist.queue_cnt++;
+	len = dip_hw->dip_useridlist.queue_cnt;
+	mutex_unlock(&dip_hw->dip_useridlist.queuelock);
+
+	dev_dbg(&dip_dev->pdev->dev,
+		"%s: stream count(%d),id(0x%x),len(%d)\n", __func__,
+		count, id, len);
+
+	return 0;
+}
+
+int mtk_dip_hw_streamoff(struct mtk_dip_hw  *dip_hw, u16 id)
+{
+	struct mtk_dip_hw_user_id *user_id;
+	struct mtk_dip_dev *dip_dev = mtk_dip_hw_to_dev(dip_hw);
+	s32 count = -1;
+	bool found = false;
+	int ret;
+
+	dev_dbg(&dip_dev->pdev->dev, "%s: streamoff id(0x%x)\n",
+		__func__, id);
+
+	mutex_lock(&dip_hw->dip_useridlist.queuelock);
+	list_for_each_entry(user_id,
+			    &dip_hw->dip_useridlist.queue, list_entry) {
+		if (user_id->id == id) {
+			user_id->state = DIP_STATE_STREAMOFF;
+			found = true;
+			break;
+		}
+	}
+	mutex_unlock(&dip_hw->dip_useridlist.queuelock);
+
+	if (found) {
+		ret = mtk_dip_hw_flush_by_id(dip_hw, id, user_id);
+		if (ret != 0) {
+			dev_err(&dip_dev->pdev->dev,
+				"%s: stream id(0x%x), streamoff err(%d)\n",
+				__func__, id, ret);
+			WARN_ON(1);
+		}
+
+		mutex_lock(&dip_hw->dip_useridlist.queuelock);
+		list_del(&user_id->list_entry);
+		dip_hw->dip_useridlist.queue_cnt--;
+		dev_dbg(&dip_dev->pdev->dev,
+			"%s: stream id(%x), user_id cnt(%d)\n",
+			__func__, id, dip_hw->dip_useridlist.queue_cnt);
+		mutex_unlock(&dip_hw->dip_useridlist.queuelock);
+
+		kfree(user_id);
+		user_id = NULL;
+		count = atomic_dec_return(&dip_hw->dip_stream_cnt);
+
+		dev_dbg(&dip_dev->pdev->dev, "%s: stream id(%d),cnt(%d)\n",
+			__func__, id, count);
+	} else {
+		dev_dbg(&dip_dev->pdev->dev,
+			"%s: stream id(%x) not found\n",
+			__func__, id);
+	}
+
+	if (count < 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+int mtk_dip_hw_enqueue(struct mtk_dip_hw *dip_hw,
+		       struct img_ipi_frameparam *frameparams)
+{
+	struct mtk_dip_hw_work	*framework = NULL;
+	struct mtk_dip_hw_user_id *user_id = NULL;
+	struct mtk_dip_dev *dip_dev = mtk_dip_hw_to_dev(dip_hw);
+	bool	found = false;
+	u32	tmpcount;
+
+	dev_dbg(&dip_dev->pdev->dev, "%s: frame idx(0x%x)",
+		__func__, frameparams->index);
+
+	mutex_lock(&dip_hw->dip_useridlist.queuelock);
+	list_for_each_entry(user_id, &dip_hw->dip_useridlist.queue,
+			    list_entry) {
+		if (mtk_dip_pipe_get_pipe_from_job_id(frameparams->index) ==
+			user_id->id) {
+			user_id->num++;
+			dev_dbg(&dip_dev->pdev->dev,
+				"%s: user_id(%x) found, current num(%d)\n",
+				__func__, user_id->id, user_id->num);
+			found = true;
+			break;
+		}
+	}
+	mutex_unlock(&dip_hw->dip_useridlist.queuelock);
+
+	if (!found) {
+		dev_err(&dip_dev->pdev->dev,
+			"%s: user_id(0x%x) not found, idx(0x%x)\n",
+			__func__,
+			mtk_dip_pipe_get_pipe_from_job_id(frameparams->index),
+			frameparams->index);
+		return -EINVAL;
+	}
+
+	framework = kzalloc(sizeof(*framework), GFP_KERNEL);
+	if (!framework)
+		return -ENOMEM;
+
+	memcpy(&framework->frameparams, frameparams, sizeof(*frameparams));
+	framework->frameparams.state = FRAME_STATE_INIT;
+	framework->frameparams.frame_no =
+		atomic_inc_return(&dip_hw->dip_enque_cnt);
+	framework->user_id = user_id;
+
+	mutex_lock(&dip_hw->dip_worklist.queuelock);
+	list_add_tail(&framework->list_entry, &dip_hw->dip_worklist.queue);
+	dip_hw->dip_worklist.queue_cnt++;
+	tmpcount = dip_hw->dip_worklist.queue_cnt;
+	mutex_unlock(&dip_hw->dip_worklist.queuelock);
+	dev_dbg(&dip_dev->pdev->dev,
+		"%s: frame_no(%d) into worklist, cnt(%d)\n",
+		__func__, framework->frameparams.frame_no, tmpcount);
+
+	queue_work(dip_hw->composer_wq, &dip_hw->submit_work.frame_work);
+	return 0;
+}
+
+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;
+	int ret = 0;
+
+	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->larb_dev = &larb_pdev->dev;
+
+	/* Grab clock */
+	dip_hw->dip_clk.img_larb5 = devm_clk_get(&pdev->dev,
+						 "DIP_CG_IMG_LARB5");
+	dip_hw->dip_clk.img_dip = devm_clk_get(&pdev->dev,
+					       "DIP_CG_IMG_DIP");
+	if (IS_ERR(dip_hw->dip_clk.img_larb5)) {
+		dev_err(&pdev->dev, "Cannot get img_larb5 clock\n");
+		return PTR_ERR(dip_hw->dip_clk.img_larb5);
+	}
+	if (IS_ERR(dip_hw->dip_clk.img_dip)) {
+		dev_err(&pdev->dev, "Cannot get img_dip clock\n");
+		return PTR_ERR(dip_hw->dip_clk.img_dip);
+	}
+
+	pm_runtime_enable(&pdev->dev);
+
+	atomic_set(&dip_hw->dip_user_cnt, 0);
+	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);
+	dip_hw->dip_worklist.queue_cnt = 0;
+
+	ret = mtk_dip_dev_v4l2_init(dip_dev);
+
+	if (ret)
+		dev_err(&pdev->dev, "v4l2 init failed(%d)\n", ret);
+
+	dev_info(&pdev->dev, "DIP driver probe\n");
+
+	return ret;
+}
+
+static int mtk_dip_remove(struct platform_device *pdev)
+{
+	mtk_dip_dev_v4l2_release(dev_get_drvdata(&pdev->dev));
+	pm_runtime_disable(&pdev->dev);
+
+	return 0;
+}
+
+static int __maybe_unused mtk_dip_pm_suspend(struct device *dev)
+{
+	struct mtk_dip_dev *dip_dev = dev_get_drvdata(dev);
+
+	if (atomic_read(&dip_dev->dip_hw.dip_user_cnt) > 0) {
+		mtk_dip_hw_set_clk(&dip_dev->dip_hw, false);
+		dev_dbg(&dip_dev->pdev->dev, "%s: disable clock\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);
+
+	if (atomic_read(&dip_dev->dip_hw.dip_user_cnt) > 0) {
+		mtk_dip_hw_set_clk(&dip_dev->dip_hw, true);
+		dev_dbg(&dip_dev->pdev->dev, "%s: enable clock\n",
+			__func__);
+	}
+
+	return 0;
+}
+
+static int __maybe_unused mtk_dip_suspend(struct device *dev)
+{
+	if (pm_runtime_suspended(dev))
+		return 0;
+
+	return mtk_dip_pm_suspend(dev);
+}
+
+static int __maybe_unused mtk_dip_resume(struct device *dev)
+{
+	if (pm_runtime_suspended(dev))
+		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	= DIP_DEV_NAME,
+		.pm	= &mtk_dip_pm_ops,
+		.of_match_table = mtk_dip_of_match,
+	}
+};
+
+module_platform_driver(mtk_dip_driver);
+
+MODULE_DESCRIPTION("Mediatek Camera DIP V4L2 driver");
+MODULE_LICENSE("GPL v2");
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..fa1c0d029208
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-v4l2.c
@@ -0,0 +1,1310 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Frederic Chen <frederic.chen@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.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 *dip_pipe = mtk_dip_subdev_to_pipe(sd);
+	struct mtk_dip_dev *dip_dev =
+		dev_get_drvdata(&dip_pipe->dip_dev->pdev->dev);
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"%s:%s: pipe(%d) connects to dip_hw\n",
+		__func__, dip_pipe->desc->name,
+		dip_pipe->desc->id);
+
+	dip_pipe->fh = fh;
+
+	mtk_dip_pipe_init_job_infos(dip_pipe);
+
+	return mtk_dip_hw_connect(&dip_dev->dip_hw);
+}
+
+static int mtk_dip_subdev_close(struct v4l2_subdev *sd,
+				struct v4l2_subdev_fh *fh)
+{
+	struct mtk_dip_pipe *dip_pipe = mtk_dip_subdev_to_pipe(sd);
+	struct mtk_dip_dev *dip_dev =
+		dev_get_drvdata(&dip_pipe->dip_dev->pdev->dev);
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"%s:%s: pipe(%d) disconnect to dip_hw\n",
+		__func__, dip_pipe->desc->name,
+		dip_pipe->desc->id);
+
+	return mtk_dip_hw_disconnect(&dip_dev->dip_hw);
+}
+
+static int mtk_dip_subdev_s_stream(struct v4l2_subdev *sd,
+				   int enable)
+{
+	struct mtk_dip_pipe *dip_pipe = mtk_dip_subdev_to_pipe(sd);
+	int ret;
+
+	if (enable)
+		ret = mtk_dip_pipe_streamon(dip_pipe);
+	else
+		ret = mtk_dip_pipe_streamoff(dip_pipe);
+
+	return ret;
+}
+
+static int mtk_dip_subdev_subscribe_event(struct v4l2_subdev *subdev,
+					  struct v4l2_fh *fh,
+					  struct v4l2_event_subscription *sub)
+{
+	switch (sub->type) {
+	case V4L2_EVENT_CTRL:
+		return v4l2_ctrl_subscribe_event(fh, sub);
+	default:
+		return -EINVAL;
+	}
+}
+
+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 *dip_pipe =
+		container_of(entity, struct mtk_dip_pipe, subdev.entity);
+	u32 pad = local->index;
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"%s: link setup, flags(0x%x), (%s)%d -->(%s)%d\n",
+		dip_pipe->desc->name,
+		flags,
+		local->entity->name,
+		local->index,
+		remote->entity->name,
+		remote->index);
+
+	WARN_ON(entity->obj_type != MEDIA_ENTITY_TYPE_V4L2_SUBDEV);
+
+	WARN_ON(pad >= dip_pipe->num_nodes);
+
+	dip_pipe->nodes[pad].enabled = !!(flags & MEDIA_LNK_FL_ENABLED);
+
+	return 0;
+}
+
+static int mtk_dip_vb2_buf_prepare(struct vb2_buffer *vb)
+{
+	struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
+
+	v4l2_buf->field = V4L2_FIELD_NONE;
+	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);
+
+	v4l2_buf->field = V4L2_FIELD_NONE;
+
+	return 0;
+}
+
+static void mtk_dip_vb2_buf_queue(struct vb2_buffer *vb)
+{
+	struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
+
+	v4l2_buf->field = V4L2_FIELD_NONE;
+}
+
+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 *dip_pipe = vb2_get_drv_priv(vq);
+	struct mtk_dip_video_device *node =
+		mtk_dip_vbq_to_node(vq);
+	struct device *dev = &dip_pipe->dip_dev->pdev->dev;
+	struct device *buf_alloc_ctx;
+
+	/* Get V4L2 format with the following method */
+	const struct v4l2_format *fmt = &node->vdev_fmt;
+	unsigned int size;
+
+	*num_buffers = clamp_val(*num_buffers, 1, VB2_MAX_FRAME);
+
+	if (node->desc->smem_alloc) {
+		buf_alloc_ctx = &dip_pipe->smem_alloc_dev->dev;
+		dev_dbg(dev, "%s:%s: select smem_vb2_alloc_ctx(%p)\n",
+			dip_pipe->desc->name,
+			node->desc->name,
+			buf_alloc_ctx);
+	} else {
+		buf_alloc_ctx = &dip_pipe->dip_dev->pdev->dev;
+		dev_dbg(dev, "%s:%s: select default_vb2_alloc_ctx(%p)\n",
+			dip_pipe->desc->name,
+			node->desc->name,
+			buf_alloc_ctx);
+	}
+
+	alloc_devs[0] = buf_alloc_ctx;
+
+	if (vq->type == V4L2_BUF_TYPE_META_CAPTURE ||
+	    vq->type == V4L2_BUF_TYPE_META_OUTPUT)
+		size = fmt->fmt.meta.buffersize;
+	else
+		size = fmt->fmt.pix_mp.plane_fmt[0].sizeimage;
+
+	if (*num_planes) {
+		if (sizes[0] < size) {
+			dev_dbg(dev, "%s:%s:%s: size error(user:%d, max:%d)\n",
+				__func__, dip_pipe->desc->name,
+				node->desc->name, sizes[0], size);
+			return -EINVAL;
+		}
+	} else {
+		*num_planes = 1;
+		sizes[0] = size;
+	}
+
+	dev_dbg(dev, "%s:%s:%s: n_planes(%d), n_bufs(%d), size(%d)\n",
+		__func__, dip_pipe->desc->name,
+		node->desc->name, *num_planes, *num_buffers, sizes[0]);
+
+	return 0;
+}
+
+static int
+	mtk_dip_all_nodes_streaming(struct mtk_dip_pipe *dip_pipe,
+				    struct mtk_dip_video_device *except)
+{
+	int i;
+
+	for (i = 0; i < dip_pipe->num_nodes; i++) {
+		struct mtk_dip_video_device *node = &dip_pipe->nodes[i];
+
+		if (node == except)
+			continue;
+		if (node->enabled &&
+		    !vb2_start_streaming_called(&node->dev_q.vbq))
+			return 0;
+	}
+
+	return 1;
+}
+
+static void mtk_dip_return_all_buffers(struct mtk_dip_pipe *dip_pipe,
+				       struct mtk_dip_video_device *node,
+				       enum vb2_buffer_state state)
+{
+	int i;
+
+	for (i = 0; i < node->dev_q.vbq.num_buffers; i++) {
+		if (node->dev_q.vbq.bufs[i]->state ==
+			VB2_BUF_STATE_ACTIVE)
+			vb2_buffer_done(node->dev_q.vbq.bufs[i],
+					state);
+	}
+}
+
+static int mtk_dip_vb2_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+	struct mtk_dip_pipe *dip_pipe = vb2_get_drv_priv(vq);
+	struct mtk_dip_video_device *node =
+		mtk_dip_vbq_to_node(vq);
+	int ret;
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"%s:%s:%s\n",
+		dip_pipe->desc->name, node->desc->name,
+		__func__);
+
+	if (!node->enabled) {
+		dev_err(&dip_pipe->dip_dev->pdev->dev,
+			"%s:%s: stream on failed, node is not enabled\n",
+			dip_pipe->desc->name, node->desc->name);
+		ret = -EINVAL;
+		goto fail_return_bufs;
+	}
+
+	ret = media_pipeline_start(&node->vdev.entity, &dip_pipe->pipeline);
+
+	if (ret < 0) {
+		dev_err(&dip_pipe->dip_dev->pdev->dev,
+			"%s:%s: media_pipeline_start failed(%d)\n",
+			dip_pipe->desc->name, node->desc->name,
+			ret);
+		goto fail_return_bufs;
+	}
+
+	if (!mtk_dip_all_nodes_streaming(dip_pipe, node))
+		return 0;
+
+	/* Start streaming of the whole pipeline */
+	ret = v4l2_subdev_call(&dip_pipe->subdev, video, s_stream, 1);
+	if (ret < 0) {
+		dev_err(&dip_pipe->dip_dev->pdev->dev,
+			"%s:%s: sub dev s_stream(1) failed(%d)\n",
+			dip_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(dip_pipe, node, VB2_BUF_STATE_QUEUED);
+
+	return ret;
+}
+
+static void mtk_dip_vb2_stop_streaming(struct vb2_queue *vq)
+{
+	struct mtk_dip_pipe *dip_pipe = vb2_get_drv_priv(vq);
+	struct mtk_dip_video_device *node =
+		mtk_dip_vbq_to_node(vq);
+	int ret;
+
+	WARN_ON(!node->enabled);
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"%s:%s:%s\n",
+		dip_pipe->desc->name, node->desc->name,
+		__func__);
+
+	if (mtk_dip_all_nodes_streaming(dip_pipe, node)) {
+		ret = v4l2_subdev_call(&dip_pipe->subdev, video, s_stream, 0);
+
+	if (ret)
+		dev_err(&dip_pipe->dip_dev->pdev->dev,
+			"%s:%s: sub dev s_stream(0) failed(%d)\n",
+			dip_pipe->desc->name, node->desc->name,
+			ret);
+	}
+
+	mtk_dip_return_all_buffers(dip_pipe, node, VB2_BUF_STATE_ERROR);
+	media_pipeline_stop(&node->vdev.entity);
+}
+
+static void mtk_dip_vb2_request_complete(struct vb2_buffer *vb)
+{
+	struct mtk_dip_video_device *node =
+		mtk_dip_vbq_to_node(vb->vb2_queue);
+
+	v4l2_ctrl_request_complete(vb->req_obj.req,
+				   &node->ctrl_handler);
+}
+
+static u32 mtk_dip_node_get_v4l2_cap(struct mtk_dip_pipe *dip_pipe,
+				     struct mtk_dip_video_device *node)
+{
+	return node->desc->cap;
+}
+
+static int mtk_dip_videoc_querycap(struct file *file, void *fh,
+				   struct v4l2_capability *cap)
+{
+	struct mtk_dip_pipe *dip_pipe = video_drvdata(file);
+
+	strlcpy(cap->driver, dip_pipe->desc->name,
+		sizeof(cap->driver));
+	strlcpy(cap->card, dip_pipe->desc->name,
+		sizeof(cap->card));
+	snprintf(cap->bus_info, sizeof(cap->bus_info),
+		 "platform:%s", dev_name(dip_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 *dip_pipe = video_drvdata(file);
+	struct mtk_dip_video_device *node = mtk_dip_file_to_node(file);
+	struct v4l2_format try_fmt;
+	int ret;
+
+	memset(&try_fmt, 0, sizeof(try_fmt));
+
+	try_fmt.type = node->dev_q.vbq.type;
+
+	ret = mtk_dip_pipe_set_img_fmt(dip_pipe, node, &f->fmt.pix_mp,
+				       &try_fmt.fmt.pix_mp);
+
+	if (ret)
+		mtk_dip_pipe_load_default_fmt(dip_pipe, node, &try_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 *dip_pipe = video_drvdata(file);
+
+	int ret;
+
+	if (dip_pipe->streaming)
+		return -EBUSY;
+
+	ret = mtk_dip_videoc_try_fmt(file, fh, f);
+
+	if (!ret)
+		node->vdev_fmt = *f;
+
+	return 0;
+}
+
+static int mtk_dip_videoc_enum_framesizes(struct file *file,
+					  void *priv,
+					  struct v4l2_frmsizeenum *sizes)
+{
+	struct mtk_dip_pipe *dip_pipe = video_drvdata(file);
+	struct mtk_dip_video_device *node = mtk_dip_file_to_node(file);
+	struct mtk_dip_dev_format *dev_fmt;
+
+	dev_fmt = mtk_dip_pipe_find_fmt(dip_pipe, node, sizes->pixel_format);
+
+	if (!dev_fmt || sizes->index)
+		return -EINVAL;
+
+	sizes->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
+
+	if (V4L2_TYPE_IS_OUTPUT(node->desc->buf_type)) {
+		sizes->stepwise.max_width = MTK_DIP_OUTPUT_MAX_WIDTH;
+		sizes->stepwise.min_width = MTK_DIP_OUTPUT_MIN_WIDTH;
+		sizes->stepwise.max_height = MTK_DIP_OUTPUT_MAX_HEIGHT;
+		sizes->stepwise.min_height = MTK_DIP_OUTPUT_MIN_HEIGHT;
+		sizes->stepwise.step_height = 1;
+		sizes->stepwise.step_width = 1;
+	} else {
+		sizes->stepwise.max_width = MTK_DIP_CAPTURE_MAX_WIDTH;
+		sizes->stepwise.min_width = MTK_DIP_CAPTURE_MIN_WIDTH;
+		sizes->stepwise.max_height = MTK_DIP_CAPTURE_MAX_HEIGHT;
+		sizes->stepwise.min_height = MTK_DIP_CAPTURE_MIN_HEIGHT;
+		sizes->stepwise.step_height = 1;
+		sizes->stepwise.step_width = 1;
+	}
+
+	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 ||
+	    f->type != node->dev_q.vbq.type)
+		return -EINVAL;
+
+	strscpy(f->description, node->desc->description,
+		sizeof(f->description));
+
+	f->pixelformat = node->desc->fmts[f->index].fmt.img.pixelformat;
+	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 || f->type != node->dev_q.vbq.type)
+		return -EINVAL;
+
+	strscpy(f->description, node->desc->description,
+		sizeof(f->description));
+
+	f->pixelformat = node->vdev_fmt.fmt.meta.dataformat;
+
+	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
+mtk_dip_vidioc_subscribe_event(struct v4l2_fh *fh,
+			       const struct v4l2_event_subscription *sub)
+{
+	switch (sub->type) {
+	case V4L2_EVENT_CTRL:
+		return v4l2_ctrl_subscribe_event(fh, sub);
+	default:
+		return -EINVAL;
+	}
+}
+
+/******************** function pointers ********************/
+
+/* subdev internal operations */
+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_core_ops mtk_dip_subdev_core_ops = {
+	.subscribe_event = mtk_dip_subdev_subscribe_event,
+	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
+};
+
+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 = {
+	.core = &mtk_dip_subdev_core_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 int mtk_dip_request_buf_validate(struct media_request *req,
+					int all_enable_node_need_buf)
+{
+	struct media_request_object *obj;
+	struct mtk_dip_pipe *dip_pipe;
+	struct mtk_dip_pipe *dip_dev_first;
+	struct vb2_buffer *vbs[MTK_DIP_VIDEO_NODE_ID_TOTAL_NUM] = {};
+	int count = 0;
+
+	if (!all_enable_node_need_buf)
+		return	vb2_request_validate(req);
+
+	list_for_each_entry(obj, &req->objects, list) {
+		struct vb2_buffer *vb;
+
+		if (vb2_request_object_is_buffer(obj)) {
+			struct mtk_dip_video_device *node;
+
+			vb = container_of(obj, struct vb2_buffer, req_obj);
+			node = mtk_dip_vbq_to_node(vb->vb2_queue);
+			dip_pipe = vb2_get_drv_priv(vb->vb2_queue);
+			vbs[node->desc->id] = vb;
+
+			if (count == 0)
+				dip_dev_first = dip_pipe;
+
+			if (dip_dev_first != dip_pipe) {
+				pr_err("Req(%p):found buf of different pipes(%p,%p)",
+				       req, dip_dev_first, dip_pipe);
+				return -EINVAL;
+			}
+		}
+	}
+
+	if (!dip_pipe) {
+		pr_debug("No dip dev found for the request\n");
+		return -EINVAL;
+	}
+
+	for (count = 0; count < MTK_DIP_VIDEO_NODE_ID_TOTAL_NUM; count++) {
+		if (dip_pipe->nodes[count].enabled) {
+			pr_debug("Node(%d:%s): vb(0x%x)\n",
+				 count, dip_pipe->nodes[count].desc->name,
+				 vbs[count]);
+
+			if (!vbs[count]) {
+				pr_debug("Node(%s) enable and no buf enqueue\n",
+					 dip_pipe->nodes[count].desc->name);
+				return -EINVAL;
+			}
+		}
+	}
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"%s:%s: all bufs found, ready for req(%p) enqueue\n",
+		__func__, dip_pipe->desc->name, req);
+
+	return vb2_request_validate(req);
+}
+
+static int mtk_dip_vb2_request_validate(struct media_request *req)
+{
+	return mtk_dip_request_buf_validate(req, 0);
+}
+
+static void mtk_dip_vb2_request_queue(struct media_request *req)
+{
+	vb2_request_queue(req);
+	mtk_dip_pipe_queue_buffers(req, 0);
+}
+
+static const struct media_device_ops mtk_dip_media_req_ops = {
+	.req_validate = mtk_dip_vb2_request_validate,
+	.req_queue = mtk_dip_vb2_request_queue,
+};
+
+static const struct vb2_ops mtk_dip_vb2_ops = {
+	.buf_queue = mtk_dip_vb2_buf_queue,
+	.queue_setup = mtk_dip_vb2_queue_setup,
+	.buf_prepare  = mtk_dip_vb2_buf_prepare,
+	.buf_out_validate = mtk_dip_vb2_buf_out_validate,
+	.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,
+	.buf_request_complete = mtk_dip_vb2_request_complete,
+};
+
+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
+};
+
+static void mtk_dip_node_to_v4l2(struct mtk_dip_pipe *dip_pipe,
+				 u32 idx,
+				 struct video_device *vdev,
+				 struct v4l2_format *f)
+{
+	u32 cap;
+	struct mtk_dip_video_device *node = &dip_pipe->nodes[idx];
+
+	cap = mtk_dip_node_get_v4l2_cap(dip_pipe, node);
+	vdev->ioctl_ops = node->desc->ops;
+	vdev->device_caps = V4L2_CAP_STREAMING | cap;
+	f->type = node->desc->buf_type;
+	mtk_dip_pipe_load_default_fmt(dip_pipe, node, f);
+}
+
+int mtk_dip_dev_media_register(struct device *dev,
+			       struct media_device *media_dev,
+			       const char *model)
+{
+	int ret = 0;
+
+	media_dev->dev = dev;
+	dev_dbg(dev, "setup media_dev.dev: %p\n",
+		media_dev->dev);
+
+	strlcpy(media_dev->model, model,
+		sizeof(media_dev->model));
+	dev_dbg(dev, "setup media_dev.model: %s\n",
+		media_dev->model);
+
+	snprintf(media_dev->bus_info, sizeof(media_dev->bus_info),
+		 "platform:%s", dev_name(dev));
+	dev_dbg(dev, "setup media_dev.bus_info: %s\n",
+		media_dev->bus_info);
+
+	media_dev->hw_revision = 0;
+	dev_dbg(dev, "setup media_dev.hw_revision: %d\n",
+		media_dev->hw_revision);
+
+	media_dev->ops = &mtk_dip_media_req_ops;
+
+	dev_dbg(dev, "media_device_init: media_dev:%p\n",
+		media_dev);
+	media_device_init(media_dev);
+
+	pr_debug("Register media device: %s, %p",
+		 media_dev->model,
+		media_dev);
+
+	ret = media_device_register(media_dev);
+
+	if (ret) {
+		dev_err(dev, "failed to register media device (%d)\n", ret);
+		goto fail_media_dev;
+	}
+	return 0;
+
+fail_media_dev:
+	media_device_unregister(media_dev);
+	media_device_cleanup(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 = 0;
+	/* Set up v4l2 device */
+	v4l2_dev->mdev = media_dev;
+	dev_dbg(dev, "setup v4l2_dev->mdev: %p",
+		v4l2_dev->mdev);
+	v4l2_dev->ctrl_handler = NULL;
+	dev_dbg(dev, "setup v4l2_dev->ctrl_handler: %p",
+		v4l2_dev->ctrl_handler);
+
+	pr_debug("Register v4l2 device: %p",
+		 v4l2_dev);
+
+	ret = v4l2_device_register(dev, v4l2_dev);
+
+	if (ret) {
+		dev_err(dev, "failed to register V4L2 device (%d)\n", ret);
+		goto fail_v4l2_dev;
+	}
+
+	return 0;
+
+fail_v4l2_dev:
+	media_device_unregister(media_dev);
+	media_device_cleanup(media_dev);
+
+	return ret;
+}
+
+int mtk_dip_pipe_v4l2_register(struct mtk_dip_pipe *dip_pipe,
+			       struct media_device *media_dev,
+			       struct v4l2_device *v4l2_dev)
+{
+	int i, ret;
+
+	/* Initialize miscellaneous variables */
+	dip_pipe->streaming = 0;
+
+	/* Initialize subdev media entity */
+	dip_pipe->subdev_pads = kcalloc(dip_pipe->num_nodes,
+					sizeof(*dip_pipe->subdev_pads),
+					GFP_KERNEL);
+	if (!dip_pipe->subdev_pads) {
+		ret = -ENOMEM;
+		goto fail_subdev_pads;
+	}
+
+	ret = media_entity_pads_init(&dip_pipe->subdev.entity,
+				     dip_pipe->num_nodes,
+				     dip_pipe->subdev_pads);
+	if (ret) {
+		dev_err(&dip_pipe->dip_dev->pdev->dev,
+			"failed initialize subdev media entity (%d)\n", ret);
+		goto fail_media_entity;
+	}
+
+	/* Initialize subdev */
+	v4l2_subdev_init(&dip_pipe->subdev, &mtk_dip_subdev_ops);
+
+	dip_pipe->subdev.entity.function =
+		MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
+
+	dip_pipe->subdev.entity.ops = &mtk_dip_media_ops;
+
+	for (i = 0; i < dip_pipe->num_nodes; i++) {
+		struct mtk_dip_video_device_desc *desc =
+			dip_pipe->nodes[i].desc;
+
+		dip_pipe->subdev_pads[i].flags =
+			V4L2_TYPE_IS_OUTPUT(desc->buf_type) ?
+			MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
+	}
+
+	dip_pipe->subdev.flags =
+		V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
+	snprintf(dip_pipe->subdev.name, sizeof(dip_pipe->subdev.name),
+		 "%s", dip_pipe->desc->name);
+	v4l2_set_subdevdata(&dip_pipe->subdev, dip_pipe);
+	dip_pipe->subdev.ctrl_handler = &dip_pipe->ctrl_handler;
+	dip_pipe->subdev.internal_ops = &mtk_dip_subdev_internal_ops;
+
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"register subdev: %s, ctrl_handler %p\n",
+		 dip_pipe->subdev.name, dip_pipe->subdev.ctrl_handler);
+	ret = v4l2_device_register_subdev(&dip_pipe->dip_dev->v4l2_dev,
+					  &dip_pipe->subdev);
+	if (ret) {
+		dev_err(&dip_pipe->dip_dev->pdev->dev,
+			"failed initialize subdev (%d)\n", ret);
+		goto fail_subdev;
+	}
+
+	ret = v4l2_device_register_subdev_nodes(&dip_pipe->dip_dev->v4l2_dev);
+	if (ret) {
+		dev_err(&dip_pipe->dip_dev->pdev->dev,
+			"failed to register subdevs (%d)\n", ret);
+		goto fail_subdevs;
+	}
+
+	/* Create video nodes and links */
+	for (i = 0; i < dip_pipe->num_nodes; i++) {
+		struct mtk_dip_video_device *node = &dip_pipe->nodes[i];
+		struct video_device *vdev = &node->vdev;
+		struct vb2_queue *vbq = &node->dev_q.vbq;
+		struct mtk_dip_video_device_desc *desc = node->desc;
+		u32 flags;
+
+		/* Initialize miscellaneous variables */
+		mutex_init(&node->dev_q.lock);
+
+		/* Initialize formats to default values */
+		mtk_dip_node_to_v4l2(dip_pipe, i, vdev, &node->vdev_fmt);
+
+		/* Initialize media entities */
+		ret = media_entity_pads_init(&vdev->entity, 1, &node->vdev_pad);
+		if (ret) {
+			dev_err(&dip_pipe->dip_dev->pdev->dev,
+				"failed initialize media entity (%d)\n", ret);
+			goto fail_vdev_media_entity;
+		}
+
+		node->vdev_pad.flags = V4L2_TYPE_IS_OUTPUT(desc->buf_type) ?
+			MEDIA_PAD_FL_SOURCE : MEDIA_PAD_FL_SINK;
+		vdev->entity.ops = NULL;
+
+		/* 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;
+		/* Put the process hub sub device in the vb2 private data*/
+		vbq->drv_priv = dip_pipe;
+		vbq->lock = &node->dev_q.lock;
+		ret = vb2_queue_init(vbq);
+		if (ret) {
+			dev_err(&dip_pipe->dip_dev->pdev->dev,
+				"failed to initialize video queue (%d)\n", ret);
+			goto fail_vdev;
+		}
+
+		/* Initialize vdev */
+		snprintf(vdev->name, sizeof(vdev->name), "%s %s",
+			 dip_pipe->desc->name,
+			 node->desc->name);
+		vdev->release = video_device_release_empty;
+		vdev->fops = &mtk_dip_v4l2_fops;
+		vdev->lock = &node->dev_q.lock;
+		vdev->ctrl_handler = &dip_pipe->nodes[i].ctrl_handler;
+		vdev->v4l2_dev = &dip_pipe->dip_dev->v4l2_dev;
+		vdev->queue = &node->dev_q.vbq;
+		vdev->vfl_dir = V4L2_TYPE_IS_OUTPUT(desc->buf_type) ?
+			VFL_DIR_TX : VFL_DIR_RX;
+		video_set_drvdata(vdev, dip_pipe);
+		pr_debug("register vdev: %s\n", vdev->name);
+		ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+		if (ret) {
+			dev_err(&dip_pipe->dip_dev->pdev->dev,
+				"failed to register video device (%d)\n", ret);
+			goto fail_vdev;
+		}
+
+		/* Create link between video node and the subdev pad */
+		flags = 0;
+		if (desc->dynamic)
+			flags |= MEDIA_LNK_FL_DYNAMIC;
+		if (node->enabled)
+			flags |= MEDIA_LNK_FL_ENABLED;
+		if (node->immutable)
+			flags |= MEDIA_LNK_FL_IMMUTABLE;
+
+		if (V4L2_TYPE_IS_OUTPUT(desc->buf_type))
+			ret = media_create_pad_link(&vdev->entity, 0,
+						    &dip_pipe->subdev.entity,
+						    i, flags);
+		else
+			ret = media_create_pad_link(&dip_pipe->subdev.entity,
+						    i, &vdev->entity, 0,
+						    flags);
+
+		if (ret)
+			goto fail_link;
+	}
+
+	return 0;
+
+	for (; i >= 0; i--) {
+fail_link:
+		video_unregister_device(&dip_pipe->nodes[i].vdev);
+fail_vdev:
+		vb2_queue_release(&dip_pipe->nodes[i].dev_q.vbq);
+		media_entity_cleanup(&dip_pipe->nodes[i].vdev.entity);
+fail_vdev_media_entity:
+		mutex_destroy(&dip_pipe->nodes[i].dev_q.lock);
+	}
+fail_subdevs:
+	v4l2_device_unregister_subdev(&dip_pipe->subdev);
+fail_subdev:
+	media_entity_cleanup(&dip_pipe->subdev.entity);
+fail_media_entity:
+	kfree(dip_pipe->subdev_pads);
+fail_subdev_pads:
+	v4l2_device_unregister(&dip_pipe->dip_dev->v4l2_dev);
+	pr_err("fail_v4l2_dev: media_device_unregister and clenaup:%p",
+	       &dip_pipe->dip_dev->mdev);
+	media_device_unregister(&dip_pipe->dip_dev->mdev);
+	media_device_cleanup(&dip_pipe->dip_dev->mdev);
+
+	return ret;
+}
+
+int mtk_dip_pipe_v4l2_unregister(struct mtk_dip_pipe *dip_pipe)
+{
+	unsigned int i;
+
+	for (i = 0; i < dip_pipe->num_nodes; i++) {
+		video_unregister_device(&dip_pipe->nodes[i].vdev);
+		vb2_queue_release(&dip_pipe->nodes[i].dev_q.vbq);
+		media_entity_cleanup(&dip_pipe->nodes[i].vdev.entity);
+		mutex_destroy(&dip_pipe->nodes[i].dev_q.lock);
+	}
+
+	v4l2_device_unregister_subdev(&dip_pipe->subdev);
+	media_entity_cleanup(&dip_pipe->subdev.entity);
+	kfree(dip_pipe->subdev_pads);
+	v4l2_device_unregister(&dip_pipe->dip_dev->v4l2_dev);
+	media_device_unregister(&dip_pipe->dip_dev->mdev);
+	media_device_cleanup(&dip_pipe->dip_dev->mdev);
+
+	return 0;
+}
+
+void mtk_dip_v4l2_buffer_done(struct vb2_buffer *vb,
+			      enum vb2_buffer_state state)
+{
+	struct mtk_dip_pipe *dip_pipe;
+	struct mtk_dip_video_device *node;
+
+	dip_pipe = vb2_get_drv_priv(vb->vb2_queue);
+	node = mtk_dip_vbq_to_node(vb->vb2_queue);
+	dev_dbg(&dip_pipe->dip_dev->pdev->dev,
+		"%s:%s: return buf, idx(%d), state(%d)\n",
+		dip_pipe->desc->name, node->desc->name,
+		vb->index, state);
+	vb2_buffer_done(vb, state);
+}
+
+/********************************************
+ * 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 = mtk_dip_vidioc_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 = mtk_dip_vidioc_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 struct mtk_dip_dev_format fw_param_fmts[] = {
+	{
+		.fmt.meta = {
+			.dataformat = V4L2_META_FMT_MTISP_PARAMS,
+			.max_buffer_size = 1024 * 30,
+		},
+	},
+};
+
+static struct mtk_dip_dev_format in_fmts[] = {
+	{
+		.fmt.img = {
+			.pixelformat = V4L2_PIX_FMT_MTISP_B10,
+			.mdp_color = MDP_COLOR_BAYER10,
+			.colorspace = V4L2_COLORSPACE_SRGB,
+			.depth = { 10 },
+			.row_depth = { 10 },
+			.num_planes = 1,
+		},
+	},
+	{
+		.fmt.img = {
+			.pixelformat = V4L2_PIX_FMT_MTISP_F10,
+			.mdp_color = MDP_COLOR_FULLG10,
+			.colorspace = V4L2_COLORSPACE_RAW,
+			.depth = { 15 },
+			.row_depth = { 15 },
+			.num_planes = 1,
+		},
+	},
+	{
+		.fmt.img = {
+			.pixelformat = V4L2_PIX_FMT_VYUY,
+			.mdp_color = MDP_COLOR_VYUY,
+			.colorspace = V4L2_COLORSPACE_BT2020,
+			.depth	 = { 16 },
+			.row_depth = { 16 },
+			.num_planes = 1,
+		},
+	},
+	{
+		.fmt.img = {
+			.pixelformat = V4L2_PIX_FMT_YUYV,
+			.mdp_color = MDP_COLOR_YUYV,
+			.colorspace = V4L2_COLORSPACE_BT2020,
+			.depth	 = { 16 },
+			.row_depth = { 16 },
+			.num_planes = 1,
+		},
+	},
+	{
+		.fmt.img = {
+			.pixelformat = V4L2_PIX_FMT_YVYU,
+			.mdp_color = MDP_COLOR_YVYU,
+			.colorspace = V4L2_COLORSPACE_BT2020,
+			.depth	 = { 16 },
+			.row_depth = { 16 },
+			.num_planes = 1,
+		},
+	},
+	{
+		.fmt.img = {
+			.pixelformat = V4L2_PIX_FMT_NV12,
+			.mdp_color = MDP_COLOR_NV12,
+			.colorspace = V4L2_COLORSPACE_BT2020,
+			.depth = { 12 },
+			.row_depth = { 8 },
+			.num_planes = 1,
+		},
+	}
+};
+
+static struct mtk_dip_dev_format out_fmts[] = {
+	{
+		.fmt.img = {
+			.pixelformat = V4L2_PIX_FMT_VYUY,
+			.mdp_color = MDP_COLOR_VYUY,
+			.colorspace = MDP_YCBCR_PROFILE_BT601,
+			.depth = { 16 },
+			.row_depth = { 16 },
+			.num_planes = 1,
+		},
+	},
+	{
+		.fmt.img = {
+			.pixelformat = V4L2_PIX_FMT_YUYV,
+			.mdp_color = MDP_COLOR_YUYV,
+			.colorspace = MDP_YCBCR_PROFILE_BT601,
+			.depth = { 16 },
+			.row_depth = { 16 },
+			.num_planes = 1,
+		},
+	},
+	{
+		.fmt.img = {
+			.pixelformat = V4L2_PIX_FMT_YVYU,
+			.mdp_color = MDP_COLOR_YVYU,
+			.colorspace = MDP_YCBCR_PROFILE_BT601,
+			.depth = { 16 },
+			.row_depth = { 16 },
+			.num_planes = 1,
+		},
+	},
+	{
+		.fmt.img = {
+			.pixelformat = V4L2_PIX_FMT_YVU420,
+			.mdp_color = MDP_COLOR_YV12,
+			.colorspace = MDP_YCBCR_PROFILE_BT601,
+			.depth = { 12 },
+			.row_depth = { 8 },
+			.num_planes = 1,
+		},
+	},
+	{
+		.fmt.img = {
+			.pixelformat = V4L2_PIX_FMT_NV12,
+			.mdp_color = MDP_COLOR_NV12,
+			.colorspace = MDP_YCBCR_PROFILE_BT601,
+			.depth = { 12 },
+			.row_depth = { 8 },
+			.num_planes = 1,
+		},
+	}
+};
+
+static 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,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+		.dynamic = 0,
+		.smem_alloc = 0,
+		.default_enable = 1,
+		.fmts = in_fmts,
+		.num_fmts = ARRAY_SIZE(in_fmts),
+		.default_fmt_idx = 0,
+		.default_width = MTK_DIP_CAPTURE_MAX_WIDTH,
+		.default_height = MTK_DIP_CAPTURE_MAX_HEIGHT,
+		.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,
+		.buf_type = V4L2_BUF_TYPE_META_OUTPUT,
+		.dynamic = 0,
+		.smem_alloc = 1,
+		.default_enable = 0,
+		.fmts = fw_param_fmts,
+		.num_fmts = 1,
+		.default_fmt_idx = 0,
+		.ops = &mtk_dip_v4l2_meta_out_ioctl_ops,
+		.description = "Tuning data",
+	},
+};
+
+static 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,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+		.dynamic = 0,
+		.smem_alloc = 0,
+		.default_enable = 1,
+		.fmts = in_fmts,
+		.num_fmts = ARRAY_SIZE(in_fmts),
+		.default_fmt_idx = 5,
+		.default_width = MTK_DIP_CAPTURE_MAX_WIDTH,
+		.default_height = MTK_DIP_CAPTURE_MAX_HEIGHT,
+		.ops = &mtk_dip_v4l2_video_out_ioctl_ops,
+		.description = "Source image to be process",
+
+	},
+	{
+		.id = MTK_DIP_VIDEO_NODE_ID_TUNING_OUT,
+		.name = "Tuning",
+		.dynamic = 0,
+		.cap = V4L2_CAP_META_OUTPUT,
+		.buf_type = V4L2_BUF_TYPE_META_OUTPUT,
+		.smem_alloc = 1,
+		.default_enable = 0,
+		.fmts = fw_param_fmts,
+		.num_fmts = 1,
+		.default_fmt_idx = 0,
+		.ops = &mtk_dip_v4l2_meta_out_ioctl_ops,
+		.description = "Tuning data for image enhancement",
+	},
+};
+
+static 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,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.dynamic = 1,
+		.smem_alloc = 0,
+		.default_enable = 1,
+		.fmts = out_fmts,
+		.num_fmts = ARRAY_SIZE(out_fmts),
+		.default_fmt_idx = 1,
+		.default_width = MTK_DIP_OUTPUT_MAX_WIDTH,
+		.default_height = MTK_DIP_OUTPUT_MAX_HEIGHT,
+		.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,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.dynamic = 1,
+		.smem_alloc = 0,
+		.default_enable = 1,
+		.fmts = out_fmts,
+		.num_fmts = ARRAY_SIZE(out_fmts),
+		.default_fmt_idx = 1,
+		.default_width = MTK_DIP_OUTPUT_MAX_WIDTH,
+		.default_height = MTK_DIP_OUTPUT_MAX_HEIGHT,
+		.ops = &mtk_dip_v4l2_video_cap_ioctl_ops,
+		.description = "Output quality enhanced image",
+
+	},
+};
+
+static 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,
+	},
+};
+
+int mtk_dip_dev_v4l2_init(struct mtk_dip_dev *dip_dev)
+{
+	struct media_device *media_dev;
+	struct v4l2_device *v4l2_dev;
+	struct mtk_dip_smem_dev *smem_alloc_dev = &dip_dev->smem_alloc_dev;
+	int i;
+	int ret = 0;
+
+	media_dev = &dip_dev->mdev;
+	v4l2_dev = &dip_dev->v4l2_dev;
+
+	ret = mtk_dip_dev_media_register(&dip_dev->pdev->dev,
+					 media_dev,
+					 MTK_DIP_DEV_DIP_MEDIA_MODEL_NAME);
+
+	ret = mtk_dip_dev_v4l2_register(&dip_dev->pdev->dev,
+					media_dev,
+					v4l2_dev);
+
+	ret = mtk_dip_smem_alloc_dev_init(smem_alloc_dev, &dip_dev->pdev->dev);
+
+	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, smem_alloc_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);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+void mtk_dip_dev_v4l2_release(struct mtk_dip_dev *dip_dev)
+{
+	int i = 0;
+
+	if (dip_dev)
+		for (i = 0; i < MTK_DIP_PIPE_ID_TOTAL_NUM; i++)
+			mtk_dip_pipe_release(&dip_dev->dip_pipe[i]);
+
+	mtk_dip_smem_alloc_dev_release(&dip_dev->smem_alloc_dev);
+}
+
-- 
2.18.0


  parent reply	other threads:[~2019-04-17 10:45 UTC|newest]

Thread overview: 82+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-04-17 10:45 [RFC PATCH V1 0/6] media: platform: Add support for Digital Image Processing (DIP) on mt8183 SoC Frederic Chen
2019-04-17 10:45 ` Frederic Chen
2019-04-17 10:45 ` Frederic Chen
2019-04-17 10:45 ` [RFC PATCH V1 1/6] dt-bindings: mt8183: Add binding for DIP shared memory Frederic Chen
2019-04-17 10:45   ` Frederic Chen
2019-04-17 10:45   ` Frederic Chen
2019-04-30  1:15   ` Rob Herring
2019-04-30  1:15     ` Rob Herring
2019-04-30  1:15     ` Rob Herring
2019-05-07 14:22     ` Frederic Chen
2019-05-07 14:22       ` Frederic Chen
2019-05-07 14:22       ` Frederic Chen
2019-05-14 16:19       ` Rob Herring
2019-05-14 16:19         ` Rob Herring
2019-05-14 16:19         ` Rob Herring
     [not found]         ` <CAL_JsqKGW9WqyNgqKD0MxsqxYHKZ+VNV5A2p+neGqwmKmiODOQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2019-05-16  6:12           ` Tomasz Figa
2019-05-16  6:12             ` Tomasz Figa
2019-05-16  6:12             ` Tomasz Figa
2019-05-17 22:22             ` Rob Herring
2019-05-17 22:22               ` Rob Herring
2019-05-17 22:22               ` Rob Herring
2019-04-17 10:45 ` [RFC PATCH V1 3/6] dt-bindings: mt8183: Added DIP dt-bindings Frederic Chen
2019-04-17 10:45   ` Frederic Chen
2019-04-17 10:45   ` Frederic Chen
2019-04-30  1:16   ` Rob Herring
2019-04-30  1:16     ` Rob Herring
2019-04-30  1:16     ` Rob Herring
2019-05-07 14:16     ` Frederic Chen
2019-05-07 14:16       ` Frederic Chen
2019-05-14 16:14       ` Rob Herring
2019-05-14 16:14         ` Rob Herring
2019-04-17 10:45 ` [RFC PATCH V1 4/6] dts: arm64: mt8183: Add DIP nodes Frederic Chen
2019-04-17 10:45   ` Frederic Chen
2019-04-17 10:45   ` Frederic Chen
2019-04-17 10:45 ` [RFC PATCH V1 5/6] media: platform: Add Mediatek DIP driver KConfig Frederic Chen
2019-04-17 10:45   ` Frederic Chen
2019-04-17 10:45   ` Frederic Chen
     [not found] ` <20190417104511.21514-1-frederic.chen-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
2019-04-17 10:45   ` [RFC PATCH V1 2/6] dts: arm64: mt8183: Add DIP shared memory node Frederic Chen
2019-04-17 10:45     ` Frederic Chen
2019-04-17 10:45     ` Frederic Chen
2019-04-17 10:45   ` Frederic Chen [this message]
2019-04-17 10:45     ` [RFC PATCH V1 6/6] platform: mtk-isp: Add Mediatek DIP driver Frederic Chen
     [not found]     ` <20190417104511.21514-7-frederic.chen-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
2019-05-09  9:48       ` Tomasz Figa
2019-05-09  9:48         ` Tomasz Figa
     [not found]         ` <20190509094846.GA65444-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
2019-05-21 19:14           ` Frederic Chen
2019-05-21 19:14             ` Frederic Chen
2019-05-22 10:25             ` Tomasz Figa
2019-05-22 10:25               ` Tomasz Figa
2019-05-22 10:25               ` Tomasz Figa
2019-05-23 13:46               ` Frederic Chen
2019-05-23 13:46                 ` Frederic Chen
2019-05-23 13:46                 ` Frederic Chen
2019-05-29  3:38                 ` Tomasz Figa
2019-05-29  3:38                   ` Tomasz Figa
2019-05-29  3:38                   ` Tomasz Figa
2019-06-11  8:48             ` Frederic Chen
2019-06-11  8:48               ` Frederic Chen
2019-06-11  8:48               ` Frederic Chen
2019-06-11  8:59               ` Tomasz Figa
2019-06-11  8:59                 ` Tomasz Figa
2019-06-11  8:59                 ` Tomasz Figa
2019-06-11 10:07                 ` Frederic Chen
2019-06-11 10:07                   ` Frederic Chen
2019-06-11 10:07                   ` Frederic Chen
2019-06-11 10:13                   ` Tomasz Figa
2019-06-11 10:13                     ` Tomasz Figa
2019-06-11 10:13                     ` Tomasz Figa
2019-06-25 11:35                     ` Frederic Chen
2019-06-25 11:35                       ` Frederic Chen
2019-06-25 11:35                       ` Frederic Chen
2019-06-25 12:16         ` Frederic Chen
2019-06-25 12:16           ` Frederic Chen
2019-06-25 12:16           ` Frederic Chen
2019-06-26  4:24           ` Tomasz Figa
2019-06-26  4:24             ` Tomasz Figa
2019-06-26  4:24             ` Tomasz Figa
2019-05-22  9:51     ` Shik Chen
2019-05-22  9:51       ` Shik Chen
2019-05-22  9:51       ` Shik Chen
2019-05-23 14:24       ` Frederic Chen
2019-05-23 14:24         ` Frederic Chen
2019-05-23 14:24         ` Frederic Chen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20190417104511.21514-7-frederic.chen@mediatek.com \
    --to=frederic.chen-nus5lvnupcjwk0htik3j/w@public.gmane.org \
    --cc=Jerry-ch.Chen-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org \
    --cc=Rynn.Wu-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org \
    --cc=Sean.Cheng-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org \
    --cc=christie.yu-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org \
    --cc=devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=hans.verkuil-FYB4Gu1CFyUAvxtiuMwx3w@public.gmane.org \
    --cc=holmes.chiou-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org \
    --cc=jungo.lin-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org \
    --cc=laurent.pinchart+renesas-ryLnwIuWjnjg/C1BVhZhaw@public.gmane.org \
    --cc=linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org \
    --cc=linux-media-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org \
    --cc=matthias.bgg-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org \
    --cc=mchehab-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org \
    --cc=shik-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org \
    --cc=sj.huang-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org \
    --cc=srv_heupstream-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org \
    --cc=suleiman-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org \
    --cc=tfiga-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org \
    --cc=yuzhao-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org \
    --cc=zwisler-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.