linux-media.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC v2 0/4] media: mediatek: support mdp3 on mt8183 platform
@ 2019-05-16  3:23 Daoyuan Huang
  2019-05-16  3:23 ` [RFC v2 1/4] dt-binding: mt8183: Add Mediatek MDP3 dt-bindings Daoyuan Huang
                   ` (3 more replies)
  0 siblings, 4 replies; 12+ messages in thread
From: Daoyuan Huang @ 2019-05-16  3:23 UTC (permalink / raw)
  To: hans.verkuil, laurent.pinchart+renesas, tfiga, matthias.bgg, mchehab
  Cc: yuzhao, zwisler, linux-mediatek, linux-arm-kernel, Sean.Cheng,
	sj.huang, christie.yu, holmes.chiou, frederic.chen,
	Jerry-ch.Chen, jungo.lin, Rynn.Wu, linux-media, srv_heupstream,
	devicetree

Hi,

This is the first version of RFC patch for Media Data Path 3 (MDP3),
MDP3 is used for scaling and color format conversion.
support using GCE to write register in critical time limitation.
support V4L2 m2m device control.

Changes since v1:
- modify code for CMDQ v3 API support
- EC ipi cmd migration
- fix compliance test fail item (m2m cmd with -f)
due to there is two problem in runing all format(-f) cmd:
1. out of memory before test complete
	Due to capture buffer mmap (refcount + 1) after reqbuf but seems
	no corresponding munmap called before device close.
	There are total 12XX items(formats) in format test and each format
	alloc 8 capture/output buffers.
2. unceasingly captureBufs() (randomly)
	Seems the break statement didn't catch the count == 0 situation:
	In v4l2-test-buffers.cpp, function: captureBufs()
			...
			count--;
			if (!node->is_m2m && !count)
				break;
	Log is as attachment

I will paste the test result with problem part in another e-mail



---
Based on v5.0-rc1 and these series:
device tree:
http://lists.infradead.org/pipermail/linux-mediatek/2019-February/017570.html
clock control:
http://lists.infradead.org/pipermail/linux-mediatek/2019-February/017320.html
system control processor (SCP):
http://lists.infradead.org/pipermail/linux-mediatek/2019-February/017774.html
global command engine (GCE):
http://lists.infradead.org/pipermail/linux-mediatek/2019-January/017143.html
---

daoyuan huang (4):
  dt-binding: mt8183: Add Mediatek MDP3 dt-bindings
  dts: arm64: mt8183: Add Mediatek MDP3 nodes
  media: platform: Add Mediatek MDP3 driver KConfig
  media: platform: mtk-mdp3: Add Mediatek MDP3 driver

 .../bindings/media/mediatek,mt8183-mdp3.txt   |  217 +++
 arch/arm64/boot/dts/mediatek/mt8183.dtsi      |  173 +++
 drivers/media/platform/Kconfig                |   18 +
 drivers/media/platform/Makefile               |    2 +
 drivers/media/platform/mtk-mdp3/Makefile      |    9 +
 drivers/media/platform/mtk-mdp3/isp_reg.h     |   38 +
 .../media/platform/mtk-mdp3/mdp-platform.h    |   67 +
 .../media/platform/mtk-mdp3/mdp_reg_ccorr.h   |   76 +
 .../media/platform/mtk-mdp3/mdp_reg_rdma.h    |  207 +++
 drivers/media/platform/mtk-mdp3/mdp_reg_rsz.h |  110 ++
 .../media/platform/mtk-mdp3/mdp_reg_wdma.h    |  126 ++
 .../media/platform/mtk-mdp3/mdp_reg_wrot.h    |  116 ++
 .../media/platform/mtk-mdp3/mmsys_config.h    |  189 +++
 drivers/media/platform/mtk-mdp3/mmsys_mutex.h |   36 +
 .../media/platform/mtk-mdp3/mmsys_reg_base.h  |   39 +
 drivers/media/platform/mtk-mdp3/mtk-img-ipi.h |  282 ++++
 .../media/platform/mtk-mdp3/mtk-mdp3-cmdq.c   |  442 ++++++
 .../media/platform/mtk-mdp3/mtk-mdp3-cmdq.h   |   57 +
 .../media/platform/mtk-mdp3/mtk-mdp3-comp.c   | 1325 +++++++++++++++++
 .../media/platform/mtk-mdp3/mtk-mdp3-comp.h   |  177 +++
 .../media/platform/mtk-mdp3/mtk-mdp3-core.c   |  256 ++++
 .../media/platform/mtk-mdp3/mtk-mdp3-core.h   |   88 ++
 .../media/platform/mtk-mdp3/mtk-mdp3-m2m.c    |  823 ++++++++++
 .../media/platform/mtk-mdp3/mtk-mdp3-m2m.h    |   52 +
 .../media/platform/mtk-mdp3/mtk-mdp3-regs.c   |  757 ++++++++++
 .../media/platform/mtk-mdp3/mtk-mdp3-regs.h   |  386 +++++
 .../media/platform/mtk-mdp3/mtk-mdp3-vpu.c    |  277 ++++
 .../media/platform/mtk-mdp3/mtk-mdp3-vpu.h    |   90 ++
 28 files changed, 6435 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/mediatek,mt8183-mdp3.txt
 create mode 100644 drivers/media/platform/mtk-mdp3/Makefile
 create mode 100644 drivers/media/platform/mtk-mdp3/isp_reg.h
 create mode 100644 drivers/media/platform/mtk-mdp3/mdp-platform.h
 create mode 100644 drivers/media/platform/mtk-mdp3/mdp_reg_ccorr.h
 create mode 100644 drivers/media/platform/mtk-mdp3/mdp_reg_rdma.h
 create mode 100644 drivers/media/platform/mtk-mdp3/mdp_reg_rsz.h
 create mode 100644 drivers/media/platform/mtk-mdp3/mdp_reg_wdma.h
 create mode 100644 drivers/media/platform/mtk-mdp3/mdp_reg_wrot.h
 create mode 100644 drivers/media/platform/mtk-mdp3/mmsys_config.h
 create mode 100644 drivers/media/platform/mtk-mdp3/mmsys_mutex.h
 create mode 100644 drivers/media/platform/mtk-mdp3/mmsys_reg_base.h
 create mode 100644 drivers/media/platform/mtk-mdp3/mtk-img-ipi.h
 create mode 100644 drivers/media/platform/mtk-mdp3/mtk-mdp3-cmdq.c
 create mode 100644 drivers/media/platform/mtk-mdp3/mtk-mdp3-cmdq.h
 create mode 100644 drivers/media/platform/mtk-mdp3/mtk-mdp3-comp.c
 create mode 100644 drivers/media/platform/mtk-mdp3/mtk-mdp3-comp.h
 create mode 100644 drivers/media/platform/mtk-mdp3/mtk-mdp3-core.c
 create mode 100644 drivers/media/platform/mtk-mdp3/mtk-mdp3-core.h
 create mode 100644 drivers/media/platform/mtk-mdp3/mtk-mdp3-m2m.c
 create mode 100644 drivers/media/platform/mtk-mdp3/mtk-mdp3-m2m.h
 create mode 100644 drivers/media/platform/mtk-mdp3/mtk-mdp3-regs.c
 create mode 100644 drivers/media/platform/mtk-mdp3/mtk-mdp3-regs.h
 create mode 100644 drivers/media/platform/mtk-mdp3/mtk-mdp3-vpu.c
 create mode 100644 drivers/media/platform/mtk-mdp3/mtk-mdp3-vpu.h

-- 
2.18.0




^ permalink raw reply	[flat|nested] 12+ messages in thread

* [RFC v2 1/4] dt-binding: mt8183: Add Mediatek MDP3 dt-bindings
  2019-05-16  3:23 [RFC v2 0/4] media: mediatek: support mdp3 on mt8183 platform Daoyuan Huang
@ 2019-05-16  3:23 ` Daoyuan Huang
  2019-06-13 21:25   ` Rob Herring
  2019-05-16  3:23 ` [RFC v2 2/4] dts: arm64: mt8183: Add Mediatek MDP3 nodes Daoyuan Huang
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 12+ messages in thread
From: Daoyuan Huang @ 2019-05-16  3:23 UTC (permalink / raw)
  To: hans.verkuil, laurent.pinchart+renesas, tfiga, matthias.bgg, mchehab
  Cc: yuzhao, zwisler, linux-mediatek, linux-arm-kernel, Sean.Cheng,
	sj.huang, christie.yu, holmes.chiou, frederic.chen,
	Jerry-ch.Chen, jungo.lin, Rynn.Wu, linux-media, srv_heupstream,
	devicetree, daoyuan huang, Ping-Hsun Wu

From: daoyuan huang <daoyuan.huang@mediatek.com>

This patch adds DT binding document for Media Data Path 3 (MDP3)
a unit in multimedia system used for scaling and color format convert.

Signed-off-by: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
Signed-off-by: daoyuan huang <daoyuan.huang@mediatek.com>
---
 .../bindings/media/mediatek,mt8183-mdp3.txt   | 217 ++++++++++++++++++
 1 file changed, 217 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/mediatek,mt8183-mdp3.txt

diff --git a/Documentation/devicetree/bindings/media/mediatek,mt8183-mdp3.txt b/Documentation/devicetree/bindings/media/mediatek,mt8183-mdp3.txt
new file mode 100644
index 000000000000..cf3e808b7146
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/mediatek,mt8183-mdp3.txt
@@ -0,0 +1,217 @@
+* Mediatek Media Data Path 3
+
+Media Data Path 3 (MDP3) is used for scaling and color space conversion.
+
+Required properties (controller node):
+- compatible: "mediatek,mt8183-mdp"
+- mediatek,scp: the node of system control processor (SCP), using the
+  remoteproc & rpmsg framework, see
+  Documentation/devicetree/bindings/remoteproc/mtk,scp.txt for details.
+- mediatek,mmsys: the node of mux(multiplexer) controller for HW connections.
+- mediatek,mm-mutex: the node of sof(start of frame) signal controller.
+- mediatek,mailbox-gce: the node of global command engine (GCE), used to
+  read/write registers with critical time limitation, see
+  Documentation/devicetree/bindings/mailbox/mtk-gce.txt for details.
+- mboxes: mailbox number used to communicate with GCE.
+- gce-subsys: sub-system id corresponding to the register address.
+- gce-event-names: in use event name list, used to correspond to event IDs.
+- gce-events: in use event IDs list, all IDs are defined in
+  'dt-bindings/gce/mt8183-gce.h'.
+
+Required properties (all function blocks, child node):
+- compatible: Should be one of
+        "mediatek,mt8183-mdp-rdma"  - read DMA
+        "mediatek,mt8183-mdp-rsz"   - resizer
+        "mediatek,mt8183-mdp-wdma"  - write DMA
+        "mediatek,mt8183-mdp-wrot"  - write DMA with rotation
+        "mediatek,mt8183-mdp-ccorr" - color correction with 3X3 matrix
+- reg: Physical base address and length of the function block register space
+- clocks: device clocks, see
+  Documentation/devicetree/bindings/clock/clock-bindings.txt for details.
+- power-domains: a phandle to the power domain, see
+  Documentation/devicetree/bindings/power/power_domain.txt for details.
+- mediatek,mdp-id: HW index to distinguish same functionality modules.
+
+Required properties (DMA function blocks, child node):
+- compatible: Should be one of
+        "mediatek,mt8183-mdp-rdma"
+        "mediatek,mt8183-mdp-wdma"
+        "mediatek,mt8183-mdp-wrot"
+- iommus: should point to the respective IOMMU block with master port as
+  argument, see Documentation/devicetree/bindings/iommu/mediatek,iommu.txt
+  for details.
+- mediatek,larb: must contain the local arbiters in the current Socs, see
+  Documentation/devicetree/bindings/memory-controllers/mediatek,smi-larb.txt
+  for details.
+
+Required properties (input path selection node):
+- compatible:
+        "mediatek,mt8183-mdp-dl"    - MDP direct link input source selection
+- reg: Physical base address and length of the function block register space
+- clocks: device clocks, see
+  Documentation/devicetree/bindings/clock/clock-bindings.txt for details.
+- mediatek,mdp-id: HW index to distinguish same functionality modules.
+
+Required properties (ISP PASS2 (DIP) module path selection node):
+- compatible:
+        "mediatek,mt8183-mdp-imgi"  - input DMA of ISP PASS2 (DIP) module for raw image input
+- reg: Physical base address and length of the function block register space
+- mediatek,mdp-id: HW index to distinguish same functionality modules.
+
+Required properties (SW node):
+- compatible: Should be one of
+        "mediatek,mt8183-mdp-exto"  - output DMA of ISP PASS2 (DIP) module for yuv image output
+        "mediatek,mt8183-mdp-path"  - MDP output path selection
+- mediatek,mdp-id: HW index to distinguish same functionality modules.
+
+Example:
+		mdp_camin@14000000 {
+			compatible = "mediatek,mt8183-mdp-dl";
+			mediatek,mdp-id = <0>;
+			reg = <0 0x14000000 0 0x1000>;
+			clocks = <&mmsys CLK_MM_MDP_DL_TXCK>,
+				<&mmsys CLK_MM_MDP_DL_RX>;
+		};
+
+		mdp_camin2@14000000 {
+			compatible = "mediatek,mt8183-mdp-dl";
+			mediatek,mdp-id = <1>;
+			reg = <0 0x14000000 0 0x1000>;
+			clocks = <&mmsys CLK_MM_IPU_DL_TXCK>,
+				<&mmsys CLK_MM_IPU_DL_RX>;
+		};
+
+		mdp_rdma0: mdp_rdma0@14001000 {
+			compatible = "mediatek,mt8183-mdp-rdma", "mediatek,mt8183-mdp3";
+			mediatek,scp = <&scp>;
+			mediatek,mdp-id = <0>;
+			reg = <0 0x14001000 0 0x1000>;
+			power-domains = <&scpsys MT8183_POWER_DOMAIN_DISP>;
+			clocks = <&mmsys CLK_MM_MDP_RDMA0>,
+				<&mmsys CLK_MM_MDP_RSZ1>;
+			iommus = <&iommu M4U_PORT_MDP_RDMA0>;
+			mediatek,larb = <&larb0>;
+			mediatek,mmsys = <&mmsys>;
+			mediatek,mm-mutex = <&mutex>;
+			mediatek,mailbox-gce = <&gce>;
+			mboxes = <&gce 20 0 CMDQ_THR_PRIO_LOWEST>,
+				<&gce 21 0 CMDQ_THR_PRIO_LOWEST>,
+				<&gce 22 0 CMDQ_THR_PRIO_LOWEST>,
+				<&gce 23 0 CMDQ_THR_PRIO_LOWEST>;
+			gce-subsys = <&gce 0x14000000 SUBSYS_1400XXXX>,
+				<&gce 0x14010000 SUBSYS_1401XXXX>,
+				<&gce 0x14020000 SUBSYS_1402XXXX>,
+				<&gce 0x15020000 SUBSYS_1502XXXX>;
+			gce-event-names = "rdma0_sof",
+				"rsz0_sof",
+				"rsz1_sof",
+				"tdshp0_sof",
+				"wrot0_sof",
+				"wdma0_sof",
+				"rdma0_done",
+				"wrot0_done",
+				"wdma0_done",
+				"isp_p2_0_done",
+				"isp_p2_1_done",
+				"isp_p2_2_done",
+				"isp_p2_3_done",
+				"isp_p2_4_done",
+				"isp_p2_5_done",
+				"isp_p2_6_done",
+				"isp_p2_7_done",
+				"isp_p2_8_done",
+				"isp_p2_9_done",
+				"isp_p2_10_done",
+				"isp_p2_11_done",
+				"isp_p2_12_done",
+				"isp_p2_13_done",
+				"isp_p2_14_done",
+				"wpe_done",
+				"wpe_b_done";
+			gce-events = <&gce CMDQ_EVENT_MDP_RDMA0_SOF>,
+				<&gce CMDQ_EVENT_MDP_RSZ0_SOF>,
+				<&gce CMDQ_EVENT_MDP_RSZ1_SOF>,
+				<&gce CMDQ_EVENT_MDP_TDSHP_SOF>,
+				<&gce CMDQ_EVENT_MDP_WROT0_SOF>,
+				<&gce CMDQ_EVENT_MDP_WDMA0_SOF>,
+				<&gce CMDQ_EVENT_MDP_RDMA0_EOF>,
+				<&gce CMDQ_EVENT_MDP_WROT0_EOF>,
+				<&gce CMDQ_EVENT_MDP_WDMA0_EOF>,
+				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_0>,
+				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_1>,
+				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_2>,
+				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_3>,
+				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_4>,
+				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_5>,
+				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_6>,
+				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_7>,
+				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_8>,
+				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_9>,
+				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_10>,
+				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_11>,
+				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_12>,
+				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_13>,
+				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_14>,
+				<&gce CMDQ_EVENT_WPE_A_DONE>,
+				<&gce CMDQ_EVENT_SPE_B_DONE>;
+		};
+
+		mdp_imgi@15020000 {
+			compatible = "mediatek,mt8183-mdp-imgi";
+			mediatek,mdp-id = <0>;
+			reg = <0 0x15020000 0 0x1000>;
+		};
+
+		mdp_img2o@15020000 {
+			compatible = "mediatek,mt8183-mdp-exto";
+			mediatek,mdp-id = <1>;
+		};
+
+		mdp_rsz0: mdp_rsz0@14003000 {
+			compatible = "mediatek,mt8183-mdp-rsz";
+			mediatek,mdp-id = <0>;
+			reg = <0 0x14003000 0 0x1000>;
+			clocks = <&mmsys CLK_MM_MDP_RSZ0>;
+		};
+
+		mdp_rsz1: mdp_rsz1@14004000 {
+			compatible = "mediatek,mt8183-mdp-rsz";
+			mediatek,mdp-id = <1>;
+			reg = <0 0x14004000 0 0x1000>;
+			clocks = <&mmsys CLK_MM_MDP_RSZ1>;
+		};
+
+		mdp_wrot0: mdp_wrot0@14005000 {
+			compatible = "mediatek,mt8183-mdp-wrot";
+			mediatek,mdp-id = <0>;
+			reg = <0 0x14005000 0 0x1000>;
+			clocks = <&mmsys CLK_MM_MDP_WROT0>;
+			iommus = <&iommu M4U_PORT_MDP_WROT0>;
+			mediatek,larb = <&larb0>;
+		};
+
+		mdp_path0_sout@14005000 {
+			compatible = "mediatek,mt8183-mdp-path";
+			mediatek,mdp-id = <0>;
+		};
+
+		mdp_wdma: mdp_wdma@14006000 {
+			compatible = "mediatek,mt8183-mdp-wdma";
+			mediatek,mdp-id = <0>;
+			reg = <0 0x14006000 0 0x1000>;
+			clocks = <&mmsys CLK_MM_MDP_WDMA0>;
+			iommus = <&iommu M4U_PORT_MDP_WDMA0>;
+			mediatek,larb = <&larb0>;
+		};
+
+		mdp_path1_sout@14006000 {
+			compatible = "mediatek,mt8183-mdp-path";
+			mediatek,mdp-id = <1>;
+		};
+
+		mdp_ccorr: mdp_ccorr@1401c000 {
+			compatible = "mediatek,mt8183-mdp-ccorr";
+			mediatek,mdp-id = <0>;
+			reg = <0 0x1401c000 0 0x1000>;
+			clocks = <&mmsys CLK_MM_MDP_CCORR>;
+		};
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [RFC v2 2/4] dts: arm64: mt8183: Add Mediatek MDP3 nodes
  2019-05-16  3:23 [RFC v2 0/4] media: mediatek: support mdp3 on mt8183 platform Daoyuan Huang
  2019-05-16  3:23 ` [RFC v2 1/4] dt-binding: mt8183: Add Mediatek MDP3 dt-bindings Daoyuan Huang
@ 2019-05-16  3:23 ` Daoyuan Huang
  2019-05-16  3:23 ` [RFC v2 3/4] media: platform: Add Mediatek MDP3 driver KConfig Daoyuan Huang
  2019-05-16  3:23 ` [RFC v2 4/4] media: platform: mtk-mdp3: Add Mediatek MDP3 driver Daoyuan Huang
  3 siblings, 0 replies; 12+ messages in thread
From: Daoyuan Huang @ 2019-05-16  3:23 UTC (permalink / raw)
  To: hans.verkuil, laurent.pinchart+renesas, tfiga, matthias.bgg, mchehab
  Cc: yuzhao, zwisler, linux-mediatek, linux-arm-kernel, Sean.Cheng,
	sj.huang, christie.yu, holmes.chiou, frederic.chen,
	Jerry-ch.Chen, jungo.lin, Rynn.Wu, linux-media, srv_heupstream,
	devicetree, daoyuan huang, Ping-Hsun Wu

From: daoyuan huang <daoyuan.huang@mediatek.com>

Add device nodes for Media Data Path 3 (MDP3) modules.

Signed-off-by: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
Signed-off-by: daoyuan huang <daoyuan.huang@mediatek.com>
---
 arch/arm64/boot/dts/mediatek/mt8183.dtsi | 173 +++++++++++++++++++++++
 1 file changed, 173 insertions(+)

diff --git a/arch/arm64/boot/dts/mediatek/mt8183.dtsi b/arch/arm64/boot/dts/mediatek/mt8183.dtsi
index c3a516e63141..e3e4c3bcd7b6 100644
--- a/arch/arm64/boot/dts/mediatek/mt8183.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8183.dtsi
@@ -418,14 +418,187 @@
 		mmsys: syscon@14000000 {
 			compatible = "mediatek,mt8183-mmsys", "syscon";
 			reg = <0 0x14000000 0 0x1000>;
+			mediatek,gce-client-reg =
+					<&gce SUBSYS_1400XXXX 0 0x1000>;
 			#clock-cells = <1>;
 		};
 
+		mdp_camin@14000000 {
+			compatible = "mediatek,mt8183-mdp-dl";
+			mediatek,mdp-id = <0>;
+			reg = <0 0x14000000 0 0x1000>;
+			mediatek,gce-client-reg =
+					<&gce SUBSYS_1400XXXX 0 0x1000>;
+			clocks = <&mmsys CLK_MM_MDP_DL_TXCK>,
+				<&mmsys CLK_MM_MDP_DL_RX>;
+		};
+
+		mdp_camin2@14000000 {
+			compatible = "mediatek,mt8183-mdp-dl";
+			mediatek,mdp-id = <1>;
+			reg = <0 0x14000000 0 0x1000>;
+			mediatek,gce-client-reg =
+					<&gce SUBSYS_1400XXXX 0 0x1000>;
+			clocks = <&mmsys CLK_MM_IPU_DL_TXCK>,
+				<&mmsys CLK_MM_IPU_DL_RX>;
+		};
+
+		mdp_rdma0: mdp_rdma0@14001000 {
+			compatible = "mediatek,mt8183-mdp-rdma",
+				     "mediatek,mt8183-mdp3";
+			mediatek,vpu = <&vpu>;
+			mediatek,scp = <&scp>;
+			mediatek,mdp-id = <0>;
+			reg = <0 0x14001000 0 0x1000>;
+			mediatek,gce-client-reg =
+					<&gce SUBSYS_1400XXXX 0x1000 0x1000>;
+			power-domains = <&scpsys MT8183_POWER_DOMAIN_DISP>;
+			clocks = <&mmsys CLK_MM_MDP_RDMA0>,
+				<&mmsys CLK_MM_MDP_RSZ1>;
+			iommus = <&iommu M4U_PORT_MDP_RDMA0>;
+			mediatek,larb = <&larb0>;
+			mediatek,mmsys = <&mmsys>;
+			mediatek,mm-mutex = <&mutex>;
+			mediatek,mailbox-gce = <&gce>;
+			mboxes = <&gce 20 0 CMDQ_THR_PRIO_LOWEST>,
+				<&gce 21 0 CMDQ_THR_PRIO_LOWEST>,
+				<&gce 22 0 CMDQ_THR_PRIO_LOWEST>,
+				<&gce 23 0 CMDQ_THR_PRIO_LOWEST>;
+			gce-subsys = <&gce 0x14000000 SUBSYS_1400XXXX>,
+				<&gce 0x14010000 SUBSYS_1401XXXX>,
+				<&gce 0x14020000 SUBSYS_1402XXXX>,
+				<&gce 0x15020000 SUBSYS_1502XXXX>;
+			mediatek,gce-event-names = "rdma0_sof",
+				"rsz0_sof",
+				"rsz1_sof",
+				"tdshp0_sof",
+				"wrot0_sof",
+				"wdma0_sof",
+				"rdma0_done",
+				"wrot0_done",
+				"wdma0_done",
+				"isp_p2_0_done",
+				"isp_p2_1_done",
+				"isp_p2_2_done",
+				"isp_p2_3_done",
+				"isp_p2_4_done",
+				"isp_p2_5_done",
+				"isp_p2_6_done",
+				"isp_p2_7_done",
+				"isp_p2_8_done",
+				"isp_p2_9_done",
+				"isp_p2_10_done",
+				"isp_p2_11_done",
+				"isp_p2_12_done",
+				"isp_p2_13_done",
+				"isp_p2_14_done",
+				"wpe_done",
+				"wpe_b_done";
+			mediatek,gce-events = <&gce CMDQ_EVENT_MDP_RDMA0_SOF>,
+				<&gce CMDQ_EVENT_MDP_RSZ0_SOF>,
+				<&gce CMDQ_EVENT_MDP_RSZ1_SOF>,
+				<&gce CMDQ_EVENT_MDP_TDSHP_SOF>,
+				<&gce CMDQ_EVENT_MDP_WROT0_SOF>,
+				<&gce CMDQ_EVENT_MDP_WDMA0_SOF>,
+				<&gce CMDQ_EVENT_MDP_RDMA0_EOF>,
+				<&gce CMDQ_EVENT_MDP_WROT0_EOF>,
+				<&gce CMDQ_EVENT_MDP_WDMA0_EOF>,
+				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_0>,
+				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_1>,
+				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_2>,
+				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_3>,
+				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_4>,
+				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_5>,
+				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_6>,
+				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_7>,
+				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_8>,
+				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_9>,
+				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_10>,
+				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_11>,
+				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_12>,
+				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_13>,
+				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_14>,
+				<&gce CMDQ_EVENT_WPE_A_DONE>,
+				<&gce CMDQ_EVENT_SPE_B_DONE>;
+		};
+
+		mdp_imgi@15020000 {
+			compatible = "mediatek,mt8183-mdp-imgi";
+			mediatek,mdp-id = <0>;
+			reg = <0 0x15020000 0 0x1000>;
+			mediatek,gce-client-reg =
+					<&gce SUBSYS_1502XXXX 0 0x1000>;
+		};
+
+		mdp_img2o@15020000 {
+			compatible = "mediatek,mt8183-mdp-exto";
+			mediatek,mdp-id = <1>;
+		};
+
+		mdp_rsz0: mdp_rsz0@14003000 {
+			compatible = "mediatek,mt8183-mdp-rsz";
+			mediatek,mdp-id = <0>;
+			reg = <0 0x14003000 0 0x1000>;
+			mediatek,gce-client-reg =
+					<&gce SUBSYS_1400XXXX 0x3000 0x1000>;
+			clocks = <&mmsys CLK_MM_MDP_RSZ0>;
+		};
+
+		mdp_rsz1: mdp_rsz1@14004000 {
+			compatible = "mediatek,mt8183-mdp-rsz";
+			mediatek,mdp-id = <1>;
+			reg = <0 0x14004000 0 0x1000>;
+			mediatek,gce-client-reg =
+					<&gce SUBSYS_1400XXXX 0x4000 0x1000>;
+			clocks = <&mmsys CLK_MM_MDP_RSZ1>;
+		};
+
+		mdp_wrot0: mdp_wrot0@14005000 {
+			compatible = "mediatek,mt8183-mdp-wrot";
+			mediatek,mdp-id = <0>;
+			reg = <0 0x14005000 0 0x1000>;
+			mediatek,gce-client-reg =
+					<&gce SUBSYS_1400XXXX 0x5000 0x1000>;
+			clocks = <&mmsys CLK_MM_MDP_WROT0>;
+			iommus = <&iommu M4U_PORT_MDP_WROT0>;
+			mediatek,larb = <&larb0>;
+		};
+
+		mdp_path0_sout@14005000 {
+			compatible = "mediatek,mt8183-mdp-path";
+			mediatek,mdp-id = <0>;
+		};
+
+		mdp_wdma: mdp_wdma@14006000 {
+			compatible = "mediatek,mt8183-mdp-wdma";
+			mediatek,mdp-id = <0>;
+			reg = <0 0x14006000 0 0x1000>;
+			mediatek,gce-client-reg =
+					<&gce SUBSYS_1400XXXX 0x6000 0x1000>;
+			clocks = <&mmsys CLK_MM_MDP_WDMA0>;
+			iommus = <&iommu M4U_PORT_MDP_WDMA0>;
+			mediatek,larb = <&larb0>;
+		};
+
+		mdp_path1_sout@14006000 {
+			compatible = "mediatek,mt8183-mdp-path";
+			mediatek,mdp-id = <1>;
+		};
+
 		smi_common: smi@14019000 {
 			compatible = "mediatek,mt8183-smi-common", "syscon";
 			reg = <0 0x14019000 0 0x1000>;
 		};
 
+		mdp_ccorr: mdp_ccorr@1401c000 {
+			compatible = "mediatek,mt8183-mdp-ccorr";
+			mediatek,mdp-id = <0>;
+			reg = <0 0x1401c000 0 0x1000>;
+			mediatek,gce-client-reg =
+					<&gce SUBSYS_1401XXXX 0xc000 0x1000>;
+			clocks = <&mmsys CLK_MM_MDP_CCORR>;
+		};
+
 		imgsys: syscon@15020000 {
 			compatible = "mediatek,mt8183-imgsys", "syscon";
 			reg = <0 0x15020000 0 0x1000>;
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [RFC v2 3/4] media: platform: Add Mediatek MDP3 driver KConfig
  2019-05-16  3:23 [RFC v2 0/4] media: mediatek: support mdp3 on mt8183 platform Daoyuan Huang
  2019-05-16  3:23 ` [RFC v2 1/4] dt-binding: mt8183: Add Mediatek MDP3 dt-bindings Daoyuan Huang
  2019-05-16  3:23 ` [RFC v2 2/4] dts: arm64: mt8183: Add Mediatek MDP3 nodes Daoyuan Huang
@ 2019-05-16  3:23 ` Daoyuan Huang
  2019-05-16  3:23 ` [RFC v2 4/4] media: platform: mtk-mdp3: Add Mediatek MDP3 driver Daoyuan Huang
  3 siblings, 0 replies; 12+ messages in thread
From: Daoyuan Huang @ 2019-05-16  3:23 UTC (permalink / raw)
  To: hans.verkuil, laurent.pinchart+renesas, tfiga, matthias.bgg, mchehab
  Cc: yuzhao, zwisler, linux-mediatek, linux-arm-kernel, Sean.Cheng,
	sj.huang, christie.yu, holmes.chiou, frederic.chen,
	Jerry-ch.Chen, jungo.lin, Rynn.Wu, linux-media, srv_heupstream,
	devicetree, daoyuan huang, Ping-Hsun Wu

From: daoyuan huang <daoyuan.huang@mediatek.com>

This patch adds Kconfig for Mediatek Media Data Path 3 (MDP3)
driver. MDP3 is used to do scaling and color format conversion.

Signed-off-by: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
Signed-off-by: daoyuan huang <daoyuan.huang@mediatek.com>
---
 drivers/media/platform/Kconfig  | 18 ++++++++++++++++++
 drivers/media/platform/Makefile |  2 ++
 2 files changed, 20 insertions(+)

diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index a505e9f5a1e2..6e8b594a1569 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -244,6 +244,24 @@ config VIDEO_MEDIATEK_MDP
 	    To compile this driver as a module, choose M here: the
 	    module will be called mtk-mdp.
 
+config VIDEO_MEDIATEK_MDP3
+	tristate "Mediatek MDP v3 driver"
+	depends on MTK_IOMMU || COMPILE_TEST
+	depends on VIDEO_DEV && VIDEO_V4L2
+	depends on ARCH_MEDIATEK || COMPILE_TEST
+	depends on HAS_DMA
+	select VIDEOBUF2_DMA_CONTIG
+	select V4L2_MEM2MEM_DEV
+	select VIDEO_MEDIATEK_VPU
+	select MTK_CMDQ
+	default n
+	help
+	    It is a v4l2 driver and present in Mediatek MT8183 SoC.
+	    The driver supports for scaling and color space conversion.
+
+	    To compile this driver as a module, choose M here: the
+	    module will be called mtk-mdp3.
+
 config VIDEO_MEDIATEK_VCODEC
 	tristate "Mediatek Video Codec driver"
 	depends on MTK_IOMMU || COMPILE_TEST
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index e6deb2597738..1e2cb92bc6da 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -92,6 +92,8 @@ obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC)	+= mtk-vcodec/
 
 obj-$(CONFIG_VIDEO_MEDIATEK_MDP)	+= mtk-mdp/
 
+obj-$(CONFIG_VIDEO_MEDIATEK_MDP3)	+= mtk-mdp3/
+
 obj-$(CONFIG_VIDEO_MEDIATEK_JPEG)	+= mtk-jpeg/
 
 obj-$(CONFIG_VIDEO_QCOM_CAMSS)		+= qcom/camss/
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [RFC v2 4/4] media: platform: mtk-mdp3: Add Mediatek MDP3 driver
  2019-05-16  3:23 [RFC v2 0/4] media: mediatek: support mdp3 on mt8183 platform Daoyuan Huang
                   ` (2 preceding siblings ...)
  2019-05-16  3:23 ` [RFC v2 3/4] media: platform: Add Mediatek MDP3 driver KConfig Daoyuan Huang
@ 2019-05-16  3:23 ` Daoyuan Huang
  2019-06-04 11:20   ` Tomasz Figa
  2019-06-20  4:47   ` Alexandre Courbot
  3 siblings, 2 replies; 12+ messages in thread
From: Daoyuan Huang @ 2019-05-16  3:23 UTC (permalink / raw)
  To: hans.verkuil, laurent.pinchart+renesas, tfiga, matthias.bgg, mchehab
  Cc: yuzhao, zwisler, linux-mediatek, linux-arm-kernel, Sean.Cheng,
	sj.huang, christie.yu, holmes.chiou, frederic.chen,
	Jerry-ch.Chen, jungo.lin, Rynn.Wu, linux-media, srv_heupstream,
	devicetree, daoyuan huang, Ping-Hsun Wu

From: daoyuan huang <daoyuan.huang@mediatek.com>

This patch adds driver for Media Data Path 3 (MDP3).
Each modules' related operation control is sited in mtk-mdp3-comp.c
Each modules' register table is defined in file with "mdp_reg_"
and "mmsys_" prefix
GCE related API, operation control  sited in mtk-mdp3-cmdq.c
V4L2 m2m device functions are implemented in mtk-mdp3-m2m.c
Probe, power, suspend/resume, system level functions are defined in
mtk-mdp3-core.c

Signed-off-by: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
Signed-off-by: daoyuan huang <daoyuan.huang@mediatek.com>
---
 drivers/media/platform/mtk-mdp3/Makefile      |    9 +
 drivers/media/platform/mtk-mdp3/isp_reg.h     |   38 +
 .../media/platform/mtk-mdp3/mdp-platform.h    |   67 +
 .../media/platform/mtk-mdp3/mdp_reg_ccorr.h   |   76 +
 .../media/platform/mtk-mdp3/mdp_reg_rdma.h    |  207 +++
 drivers/media/platform/mtk-mdp3/mdp_reg_rsz.h |  110 ++
 .../media/platform/mtk-mdp3/mdp_reg_wdma.h    |  126 ++
 .../media/platform/mtk-mdp3/mdp_reg_wrot.h    |  116 ++
 .../media/platform/mtk-mdp3/mmsys_config.h    |  189 +++
 drivers/media/platform/mtk-mdp3/mmsys_mutex.h |   36 +
 .../media/platform/mtk-mdp3/mmsys_reg_base.h  |   39 +
 drivers/media/platform/mtk-mdp3/mtk-img-ipi.h |  282 ++++
 .../media/platform/mtk-mdp3/mtk-mdp3-cmdq.c   |  442 ++++++
 .../media/platform/mtk-mdp3/mtk-mdp3-cmdq.h   |   57 +
 .../media/platform/mtk-mdp3/mtk-mdp3-comp.c   | 1325 +++++++++++++++++
 .../media/platform/mtk-mdp3/mtk-mdp3-comp.h   |  177 +++
 .../media/platform/mtk-mdp3/mtk-mdp3-core.c   |  256 ++++
 .../media/platform/mtk-mdp3/mtk-mdp3-core.h   |   88 ++
 .../media/platform/mtk-mdp3/mtk-mdp3-m2m.c    |  823 ++++++++++
 .../media/platform/mtk-mdp3/mtk-mdp3-m2m.h    |   52 +
 .../media/platform/mtk-mdp3/mtk-mdp3-regs.c   |  757 ++++++++++
 .../media/platform/mtk-mdp3/mtk-mdp3-regs.h   |  386 +++++
 .../media/platform/mtk-mdp3/mtk-mdp3-vpu.c    |  277 ++++
 .../media/platform/mtk-mdp3/mtk-mdp3-vpu.h    |   90 ++
 24 files changed, 6025 insertions(+)
 create mode 100644 drivers/media/platform/mtk-mdp3/Makefile
 create mode 100644 drivers/media/platform/mtk-mdp3/isp_reg.h
 create mode 100644 drivers/media/platform/mtk-mdp3/mdp-platform.h
 create mode 100644 drivers/media/platform/mtk-mdp3/mdp_reg_ccorr.h
 create mode 100644 drivers/media/platform/mtk-mdp3/mdp_reg_rdma.h
 create mode 100644 drivers/media/platform/mtk-mdp3/mdp_reg_rsz.h
 create mode 100644 drivers/media/platform/mtk-mdp3/mdp_reg_wdma.h
 create mode 100644 drivers/media/platform/mtk-mdp3/mdp_reg_wrot.h
 create mode 100644 drivers/media/platform/mtk-mdp3/mmsys_config.h
 create mode 100644 drivers/media/platform/mtk-mdp3/mmsys_mutex.h
 create mode 100644 drivers/media/platform/mtk-mdp3/mmsys_reg_base.h
 create mode 100644 drivers/media/platform/mtk-mdp3/mtk-img-ipi.h
 create mode 100644 drivers/media/platform/mtk-mdp3/mtk-mdp3-cmdq.c
 create mode 100644 drivers/media/platform/mtk-mdp3/mtk-mdp3-cmdq.h
 create mode 100644 drivers/media/platform/mtk-mdp3/mtk-mdp3-comp.c
 create mode 100644 drivers/media/platform/mtk-mdp3/mtk-mdp3-comp.h
 create mode 100644 drivers/media/platform/mtk-mdp3/mtk-mdp3-core.c
 create mode 100644 drivers/media/platform/mtk-mdp3/mtk-mdp3-core.h
 create mode 100644 drivers/media/platform/mtk-mdp3/mtk-mdp3-m2m.c
 create mode 100644 drivers/media/platform/mtk-mdp3/mtk-mdp3-m2m.h
 create mode 100644 drivers/media/platform/mtk-mdp3/mtk-mdp3-regs.c
 create mode 100644 drivers/media/platform/mtk-mdp3/mtk-mdp3-regs.h
 create mode 100644 drivers/media/platform/mtk-mdp3/mtk-mdp3-vpu.c
 create mode 100644 drivers/media/platform/mtk-mdp3/mtk-mdp3-vpu.h

diff --git a/drivers/media/platform/mtk-mdp3/Makefile b/drivers/media/platform/mtk-mdp3/Makefile
new file mode 100644
index 000000000000..3368c1499f5e
--- /dev/null
+++ b/drivers/media/platform/mtk-mdp3/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+mtk-mdp3-y += mtk-mdp3-core.o mtk-mdp3-vpu.o mtk-mdp3-regs.o
+mtk-mdp3-y += mtk-mdp3-m2m.o
+mtk-mdp3-y += mtk-mdp3-comp.o mtk-mdp3-cmdq.o
+
+obj-$(CONFIG_VIDEO_MEDIATEK_MDP3) += mtk-mdp3.o
+
+ccflags-y += -I$(srctree)/drivers/media/platform/mtk-vpu
+
diff --git a/drivers/media/platform/mtk-mdp3/isp_reg.h b/drivers/media/platform/mtk-mdp3/isp_reg.h
new file mode 100644
index 000000000000..93552943c323
--- /dev/null
+++ b/drivers/media/platform/mtk-mdp3/isp_reg.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ * Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
+ *
+ */
+
+#ifndef __ISP_REG_H__
+#define __ISP_REG_H__
+
+enum ISP_DIP_CQ {
+	ISP_DRV_DIP_CQ_THRE0 = 0,
+	ISP_DRV_DIP_CQ_THRE1,
+	ISP_DRV_DIP_CQ_THRE2,
+	ISP_DRV_DIP_CQ_THRE3,
+	ISP_DRV_DIP_CQ_THRE4,
+	ISP_DRV_DIP_CQ_THRE5,
+	ISP_DRV_DIP_CQ_THRE6,
+	ISP_DRV_DIP_CQ_THRE7,
+	ISP_DRV_DIP_CQ_THRE8,
+	ISP_DRV_DIP_CQ_THRE9,
+	ISP_DRV_DIP_CQ_THRE10,
+	ISP_DRV_DIP_CQ_THRE11,
+	ISP_DRV_DIP_CQ_NUM,
+	ISP_DRV_DIP_CQ_NONE,
+	/* we only need 12 CQ threads in this chip,
+	 *so we move the following enum behind ISP_DRV_DIP_CQ_NUM
+	 */
+	ISP_DRV_DIP_CQ_THRE12,
+	ISP_DRV_DIP_CQ_THRE13,
+	ISP_DRV_DIP_CQ_THRE14,
+	ISP_DRV_DIP_CQ_THRE15,	/* CQ_THREAD15 does not connect to GCE */
+	ISP_DRV_DIP_CQ_THRE16,	/* CQ_THREAD16 does not connect to GCE */
+	ISP_DRV_DIP_CQ_THRE17,	/* CQ_THREAD17 does not connect to GCE */
+	ISP_DRV_DIP_CQ_THRE18,	/* CQ_THREAD18 does not connect to GCE */
+};
+
+#endif  // __ISP_REG_H__
diff --git a/drivers/media/platform/mtk-mdp3/mdp-platform.h b/drivers/media/platform/mtk-mdp3/mdp-platform.h
new file mode 100644
index 000000000000..6926c3a754fa
--- /dev/null
+++ b/drivers/media/platform/mtk-mdp3/mdp-platform.h
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Ping-Hsun Wu <ping-hsun.wu@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 __MDP_PLATFORM_H__
+#define __MDP_PLATFORM_H__
+
+#include "mtk-mdp3-comp.h"
+
+/* CAM */
+#define MDP_WPEI           MDP_COMP_WPEI
+#define MDP_WPEO           MDP_COMP_WPEO
+#define MDP_WPEI2          MDP_COMP_WPEI2
+#define MDP_WPEO2          MDP_COMP_WPEO2
+#define MDP_IMGI           MDP_COMP_ISP_IMGI
+#define MDP_IMGO           MDP_COMP_ISP_IMGO
+#define MDP_IMG2O          MDP_COMP_ISP_IMG2O
+
+/* IPU */
+#define MDP_IPUI           MDP_COMP_NONE
+#define MDP_IPUO           MDP_COMP_NONE
+
+/* MDP */
+#define MDP_CAMIN          MDP_COMP_CAMIN
+#define MDP_CAMIN2         MDP_COMP_CAMIN2
+#define MDP_RDMA0          MDP_COMP_RDMA0
+#define MDP_RDMA1          MDP_COMP_NONE
+#define MDP_AAL0           MDP_COMP_AAL0
+#define MDP_CCORR0         MDP_COMP_CCORR0
+#define MDP_SCL0           MDP_COMP_RSZ0
+#define MDP_SCL1           MDP_COMP_RSZ1
+#define MDP_SCL2           MDP_COMP_NONE
+#define MDP_TDSHP0         MDP_COMP_TDSHP0
+#define MDP_COLOR0         MDP_COMP_COLOR0
+#define MDP_WROT0          MDP_COMP_WROT0
+#define MDP_WROT1          MDP_COMP_NONE
+#define MDP_WDMA           MDP_COMP_WDMA
+#define MDP_PATH0_SOUT     MDP_COMP_PATH0_SOUT
+#define MDP_PATH1_SOUT     MDP_COMP_PATH1_SOUT
+
+#define MDP_TOTAL          (MDP_COMP_WDMA + 1)
+
+/* Platform options */
+#define ESL_SETTING			1
+#define RDMA_SUPPORT_10BIT		1
+#define RDMA0_RSZ1_SRAM_SHARING		1
+#define RDMA_UPSAMPLE_REPEAT_ONLY	1
+#define RSZ_DISABLE_DCM_SMALL_TILE	0
+#define WROT_FILTER_CONSTRAINT		0
+#define WROT0_DISP_SRAM_SHARING		0
+
+#define MM_MUTEX_MOD_OFFSET	0x30
+#define MM_MUTEX_SOF_OFFSET	0x2c
+
+#endif  /* __MDP_PLATFORM_H__ */
+
diff --git a/drivers/media/platform/mtk-mdp3/mdp_reg_ccorr.h b/drivers/media/platform/mtk-mdp3/mdp_reg_ccorr.h
new file mode 100644
index 000000000000..e3340726e6d4
--- /dev/null
+++ b/drivers/media/platform/mtk-mdp3/mdp_reg_ccorr.h
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ * Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
+ *
+ */
+
+#ifndef __MDP_REG_CCORR_H__
+#define __MDP_REG_CCORR_H__
+
+#include "mmsys_reg_base.h"
+
+#define MDP_CCORR_EN                0x000
+#define MDP_CCORR_RESET             0x004
+#define MDP_CCORR_INTEN             0x008
+#define MDP_CCORR_INTSTA            0x00c
+#define MDP_CCORR_STATUS            0x010
+#define MDP_CCORR_CFG               0x020
+#define MDP_CCORR_INPUT_COUNT       0x024
+#define MDP_CCORR_OUTPUT_COUNT      0x028
+#define MDP_CCORR_CHKSUM            0x02c
+#define MDP_CCORR_SIZE              0x030
+#define MDP_CCORR_Y2R_00            0x034
+#define MDP_CCORR_Y2R_01            0x038
+#define MDP_CCORR_Y2R_02            0x03c
+#define MDP_CCORR_Y2R_03            0x040
+#define MDP_CCORR_Y2R_04            0x044
+#define MDP_CCORR_Y2R_05            0x048
+#define MDP_CCORR_R2Y_00            0x04c
+#define MDP_CCORR_R2Y_01            0x050
+#define MDP_CCORR_R2Y_02            0x054
+#define MDP_CCORR_R2Y_03            0x058
+#define MDP_CCORR_R2Y_04            0x05c
+#define MDP_CCORR_R2Y_05            0x060
+#define MDP_CCORR_COEF_0            0x080
+#define MDP_CCORR_COEF_1            0x084
+#define MDP_CCORR_COEF_2            0x088
+#define MDP_CCORR_COEF_3            0x08c
+#define MDP_CCORR_COEF_4            0x090
+#define MDP_CCORR_SHADOW            0x0a0
+#define MDP_CCORR_DUMMY_REG         0x0c0
+#define MDP_CCORR_ATPG              0x0fc
+
+/* MASK */
+#define MDP_CCORR_EN_MASK           0x00000001
+#define MDP_CCORR_RESET_MASK        0x00000001
+#define MDP_CCORR_INTEN_MASK        0x00000003
+#define MDP_CCORR_INTSTA_MASK       0x00000003
+#define MDP_CCORR_STATUS_MASK       0xfffffff3
+#define MDP_CCORR_CFG_MASK          0x70001317
+#define MDP_CCORR_INPUT_COUNT_MASK  0x1fff1fff
+#define MDP_CCORR_OUTPUT_COUNT_MASK 0x1fff1fff
+#define MDP_CCORR_CHKSUM_MASK       0xffffffff
+#define MDP_CCORR_SIZE_MASK         0x1fff1fff
+#define MDP_CCORR_Y2R_00_MASK       0x01ff01ff
+#define MDP_CCORR_Y2R_01_MASK       0x1fff01ff
+#define MDP_CCORR_Y2R_02_MASK       0x1fff1fff
+#define MDP_CCORR_Y2R_03_MASK       0x1fff1fff
+#define MDP_CCORR_Y2R_04_MASK       0x1fff1fff
+#define MDP_CCORR_Y2R_05_MASK       0x1fff1fff
+#define MDP_CCORR_R2Y_00_MASK       0x01ff01ff
+#define MDP_CCORR_R2Y_01_MASK       0x07ff01ff
+#define MDP_CCORR_R2Y_02_MASK       0x07ff07ff
+#define MDP_CCORR_R2Y_03_MASK       0x07ff07ff
+#define MDP_CCORR_R2Y_04_MASK       0x07ff07ff
+#define MDP_CCORR_R2Y_05_MASK       0x07ff07ff
+#define MDP_CCORR_COEF_0_MASK       0x1fff1fff
+#define MDP_CCORR_COEF_1_MASK       0x1fff1fff
+#define MDP_CCORR_COEF_2_MASK       0x1fff1fff
+#define MDP_CCORR_COEF_3_MASK       0x1fff1fff
+#define MDP_CCORR_COEF_4_MASK       0x1fff1fff
+#define MDP_CCORR_SHADOW_MASK       0x00000007
+#define MDP_CCORR_DUMMY_REG_MASK    0xffffffff
+#define MDP_CCORR_ATPG_MASK         0x00000003
+
+#endif  // __MDP_REG_CCORR_H__
diff --git a/drivers/media/platform/mtk-mdp3/mdp_reg_rdma.h b/drivers/media/platform/mtk-mdp3/mdp_reg_rdma.h
new file mode 100644
index 000000000000..5625d9f7810d
--- /dev/null
+++ b/drivers/media/platform/mtk-mdp3/mdp_reg_rdma.h
@@ -0,0 +1,207 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ * Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
+ *
+ */
+
+#ifndef __MDP_REG_RDMA_H__
+#define __MDP_REG_RDMA_H__
+
+#include "mmsys_reg_base.h"
+
+#define MDP_RDMA_EN                     0x000
+#define MDP_RDMA_RESET                  0x008
+#define MDP_RDMA_INTERRUPT_ENABLE       0x010
+#define MDP_RDMA_INTERRUPT_STATUS       0x018
+#define MDP_RDMA_CON                    0x020
+#define MDP_RDMA_GMCIF_CON              0x028
+#define MDP_RDMA_SRC_CON                0x030
+#define MDP_RDMA_SRC_BASE_0             0xf00
+#define MDP_RDMA_SRC_BASE_1             0xf08
+#define MDP_RDMA_SRC_BASE_2             0xf10
+#define MDP_RDMA_UFO_DEC_LENGTH_BASE_Y  0xf20
+#define MDP_RDMA_UFO_DEC_LENGTH_BASE_C  0xf28
+#define MDP_RDMA_MF_BKGD_SIZE_IN_BYTE   0x060
+#define MDP_RDMA_MF_BKGD_SIZE_IN_PXL    0x068
+#define MDP_RDMA_MF_SRC_SIZE            0x070
+#define MDP_RDMA_MF_CLIP_SIZE           0x078
+#define MDP_RDMA_MF_OFFSET_1            0x080
+#define MDP_RDMA_MF_PAR                 0x088
+#define MDP_RDMA_SF_BKGD_SIZE_IN_BYTE   0x090
+#define MDP_RDMA_SF_PAR                 0x0b8
+#define MDP_RDMA_MB_DEPTH               0x0c0
+#define MDP_RDMA_MB_BASE                0x0c8
+#define MDP_RDMA_MB_CON                 0x0d0
+#define MDP_RDMA_SB_DEPTH               0x0d8
+#define MDP_RDMA_SB_BASE                0x0e0
+#define MDP_RDMA_SB_CON                 0x0e8
+#define MDP_RDMA_VC1_RANGE              0x0f0
+#define MDP_RDMA_SRC_END_0              0x100
+#define MDP_RDMA_SRC_END_1              0x108
+#define MDP_RDMA_SRC_END_2              0x110
+#define MDP_RDMA_SRC_OFFSET_0           0x118
+#define MDP_RDMA_SRC_OFFSET_1           0x120
+#define MDP_RDMA_SRC_OFFSET_2           0x128
+#define MDP_RDMA_SRC_OFFSET_W_0         0x130
+#define MDP_RDMA_SRC_OFFSET_W_1         0x138
+#define MDP_RDMA_SRC_OFFSET_W_2         0x140
+#define MDP_RDMA_SRC_OFFSET_0_P         0x148
+#define MDP_RDMA_TRANSFORM_0            0x200
+#define MDP_RDMA_TRANSFORM_1            0x208
+#define MDP_RDMA_TRANSFORM_2            0x210
+#define MDP_RDMA_TRANSFORM_3            0x218
+#define MDP_RDMA_TRANSFORM_4            0x220
+#define MDP_RDMA_TRANSFORM_5            0x228
+#define MDP_RDMA_TRANSFORM_6            0x230
+#define MDP_RDMA_TRANSFORM_7            0x238
+#define MDP_RDMA_DMABUF_CON_0           0x240
+#define MDP_RDMA_DMAULTRA_CON_0         0x248
+#define MDP_RDMA_DMABUF_CON_1           0x250
+#define MDP_RDMA_DMAULTRA_CON_1         0x258
+#define MDP_RDMA_DMABUF_CON_2           0x260
+#define MDP_RDMA_DMAULTRA_CON_2         0x268
+#define MDP_RDMA_DITHER_CON             0x288
+#define MDP_RDMA_RESV_DUMMY_0           0x2a0
+#define MDP_RDMA_CHKS_EXTR              0x300
+#define MDP_RDMA_CHKS_INTW              0x308
+#define MDP_RDMA_CHKS_INTR              0x310
+#define MDP_RDMA_CHKS_ROTO              0x318
+#define MDP_RDMA_CHKS_SRIY              0x320
+#define MDP_RDMA_CHKS_SRIU              0x328
+#define MDP_RDMA_CHKS_SRIV              0x330
+#define MDP_RDMA_CHKS_SROY              0x338
+#define MDP_RDMA_CHKS_SROU              0x340
+#define MDP_RDMA_CHKS_SROV              0x348
+#define MDP_RDMA_CHKS_VUPI              0x350
+#define MDP_RDMA_CHKS_VUPO              0x358
+#define MDP_RDMA_DEBUG_CON              0x380
+#define MDP_RDMA_MON_STA_0              0x400
+#define MDP_RDMA_MON_STA_1              0x408
+#define MDP_RDMA_MON_STA_2              0x410
+#define MDP_RDMA_MON_STA_3              0x418
+#define MDP_RDMA_MON_STA_4              0x420
+#define MDP_RDMA_MON_STA_5              0x428
+#define MDP_RDMA_MON_STA_6              0x430
+#define MDP_RDMA_MON_STA_7              0x438
+#define MDP_RDMA_MON_STA_8              0x440
+#define MDP_RDMA_MON_STA_9              0x448
+#define MDP_RDMA_MON_STA_10             0x450
+#define MDP_RDMA_MON_STA_11             0x458
+#define MDP_RDMA_MON_STA_12             0x460
+#define MDP_RDMA_MON_STA_13             0x468
+#define MDP_RDMA_MON_STA_14             0x470
+#define MDP_RDMA_MON_STA_15             0x478
+#define MDP_RDMA_MON_STA_16             0x480
+#define MDP_RDMA_MON_STA_17             0x488
+#define MDP_RDMA_MON_STA_18             0x490
+#define MDP_RDMA_MON_STA_19             0x498
+#define MDP_RDMA_MON_STA_20             0x4a0
+#define MDP_RDMA_MON_STA_21             0x4a8
+#define MDP_RDMA_MON_STA_22             0x4b0
+#define MDP_RDMA_MON_STA_23             0x4b8
+#define MDP_RDMA_MON_STA_24             0x4c0
+#define MDP_RDMA_MON_STA_25             0x4c8
+#define MDP_RDMA_MON_STA_26             0x4d0
+#define MDP_RDMA_MON_STA_27             0x4d8
+#define MDP_RDMA_MON_STA_28             0x4e0
+
+/* MASK */
+#define MDP_RDMA_EN_MASK                    0x00000001
+#define MDP_RDMA_RESET_MASK                 0x00000001
+#define MDP_RDMA_INTERRUPT_ENABLE_MASK      0x00000007
+#define MDP_RDMA_INTERRUPT_STATUS_MASK      0x00000007
+#define MDP_RDMA_CON_MASK                   0x00001110
+#define MDP_RDMA_GMCIF_CON_MASK             0xfffb3771
+#define MDP_RDMA_SRC_CON_MASK               0xf3ffffff
+#define MDP_RDMA_SRC_BASE_0_MASK            0xffffffff
+#define MDP_RDMA_SRC_BASE_1_MASK            0xffffffff
+#define MDP_RDMA_SRC_BASE_2_MASK            0xffffffff
+#define MDP_RDMA_UFO_DEC_LENGTH_BASE_Y_MASK 0xffffffff
+#define MDP_RDMA_UFO_DEC_LENGTH_BASE_C_MASK 0xffffffff
+#define MDP_RDMA_MF_BKGD_SIZE_IN_BYTE_MASK  0x001fffff
+#define MDP_RDMA_MF_BKGD_SIZE_IN_PXL_MASK   0x001fffff
+#define MDP_RDMA_MF_SRC_SIZE_MASK           0x1fff1fff
+#define MDP_RDMA_MF_CLIP_SIZE_MASK          0x1fff1fff
+#define MDP_RDMA_MF_OFFSET_1_MASK           0x003f001f
+#define MDP_RDMA_MF_PAR_MASK                0x1ffff3ff
+#define MDP_RDMA_SF_BKGD_SIZE_IN_BYTE_MASK  0x001fffff
+#define MDP_RDMA_SF_PAR_MASK                0x1ffff3ff
+#define MDP_RDMA_MB_DEPTH_MASK              0x0000007f
+#define MDP_RDMA_MB_BASE_MASK               0x0000ffff
+#define MDP_RDMA_MB_CON_MASK                0x3fff1fff
+#define MDP_RDMA_SB_DEPTH_MASK              0x0000007f
+#define MDP_RDMA_SB_BASE_MASK               0x0000ffff
+#define MDP_RDMA_SB_CON_MASK                0x3fff1fff
+#define MDP_RDMA_VC1_RANGE_MASK             0x001f1f11
+#define MDP_RDMA_SRC_END_0_MASK             0xffffffff
+#define MDP_RDMA_SRC_END_1_MASK             0xffffffff
+#define MDP_RDMA_SRC_END_2_MASK             0xffffffff
+#define MDP_RDMA_SRC_OFFSET_0_MASK          0xffffffff
+#define MDP_RDMA_SRC_OFFSET_1_MASK          0xffffffff
+#define MDP_RDMA_SRC_OFFSET_2_MASK          0xffffffff
+#define MDP_RDMA_SRC_OFFSET_W_0_MASK        0x0000ffff
+#define MDP_RDMA_SRC_OFFSET_W_1_MASK        0x0000ffff
+#define MDP_RDMA_SRC_OFFSET_W_2_MASK        0x0000ffff
+#define MDP_RDMA_SRC_OFFSET_0_P_MASK        0xffffffff
+#define MDP_RDMA_TRANSFORM_0_MASK           0xff110777
+#define MDP_RDMA_TRANSFORM_1_MASK           0x1ff7fdff
+#define MDP_RDMA_TRANSFORM_2_MASK           0x1ff7fdff
+#define MDP_RDMA_TRANSFORM_3_MASK           0x1fff1fff
+#define MDP_RDMA_TRANSFORM_4_MASK           0x1fff1fff
+#define MDP_RDMA_TRANSFORM_5_MASK           0x1fff1fff
+#define MDP_RDMA_TRANSFORM_6_MASK           0x1fff1fff
+#define MDP_RDMA_TRANSFORM_7_MASK           0x00001fff
+#define MDP_RDMA_DMABUF_CON_0_MASK          0x077f007f
+#define MDP_RDMA_DMAULTRA_CON_0_MASK        0x7f7f7f7f
+#define MDP_RDMA_DMABUF_CON_1_MASK          0x073f003f
+#define MDP_RDMA_DMAULTRA_CON_1_MASK        0x3f3f3f3f
+#define MDP_RDMA_DMABUF_CON_2_MASK          0x071f001f
+#define MDP_RDMA_DMAULTRA_CON_2_MASK        0x1f1f1f1f
+
+#define MDP_RDMA_DITHER_CON_MASK            0xffffffff
+#define MDP_RDMA_RESV_DUMMY_0_MASK          0xffffffff
+#define MDP_RDMA_CHKS_EXTR_MASK             0xffffff01
+#define MDP_RDMA_CHKS_INTW_MASK             0xffffff01
+#define MDP_RDMA_CHKS_INTR_MASK             0xffffff01
+#define MDP_RDMA_CHKS_ROTO_MASK             0xffffff01
+#define MDP_RDMA_CHKS_SRIY_MASK             0xffffff01
+#define MDP_RDMA_CHKS_SRIU_MASK             0xffffff01
+#define MDP_RDMA_CHKS_SRIV_MASK             0xffffff01
+#define MDP_RDMA_CHKS_SROY_MASK             0xffffff01
+#define MDP_RDMA_CHKS_SROU_MASK             0xffffff01
+#define MDP_RDMA_CHKS_SROV_MASK             0xffffff01
+#define MDP_RDMA_CHKS_VUPI_MASK             0xffffff01
+#define MDP_RDMA_CHKS_VUPO_MASK             0xffffff01
+#define MDP_RDMA_DEBUG_CON_MASK             0x00001f11
+#define MDP_RDMA_MON_STA_0_MASK             0xffffffff
+#define MDP_RDMA_MON_STA_1_MASK             0xffffffff
+#define MDP_RDMA_MON_STA_2_MASK             0xffffffff
+#define MDP_RDMA_MON_STA_3_MASK             0xffffffff
+#define MDP_RDMA_MON_STA_4_MASK             0xffffffff
+#define MDP_RDMA_MON_STA_5_MASK             0xffffffff
+#define MDP_RDMA_MON_STA_6_MASK             0xffffffff
+#define MDP_RDMA_MON_STA_7_MASK             0xffffffff
+#define MDP_RDMA_MON_STA_8_MASK             0xffffffff
+#define MDP_RDMA_MON_STA_9_MASK             0xffffffff
+#define MDP_RDMA_MON_STA_10_MASK            0xffffffff
+#define MDP_RDMA_MON_STA_11_MASK            0xffffffff
+#define MDP_RDMA_MON_STA_12_MASK            0xffffffff
+#define MDP_RDMA_MON_STA_13_MASK            0xffffffff
+#define MDP_RDMA_MON_STA_14_MASK            0xffffffff
+#define MDP_RDMA_MON_STA_15_MASK            0xffffffff
+#define MDP_RDMA_MON_STA_16_MASK            0xffffffff
+#define MDP_RDMA_MON_STA_17_MASK            0xffffffff
+#define MDP_RDMA_MON_STA_18_MASK            0xffffffff
+#define MDP_RDMA_MON_STA_19_MASK            0xffffffff
+#define MDP_RDMA_MON_STA_20_MASK            0xffffffff
+#define MDP_RDMA_MON_STA_21_MASK            0xffffffff
+#define MDP_RDMA_MON_STA_22_MASK            0xffffffff
+#define MDP_RDMA_MON_STA_23_MASK            0xffffffff
+#define MDP_RDMA_MON_STA_24_MASK            0xffffffff
+#define MDP_RDMA_MON_STA_25_MASK            0xffffffff
+#define MDP_RDMA_MON_STA_26_MASK            0xffffffff
+#define MDP_RDMA_MON_STA_27_MASK            0xffffffff
+#define MDP_RDMA_MON_STA_28_MASK            0xffffffff
+
+#endif  // __MDP_REG_RDMA_H__
diff --git a/drivers/media/platform/mtk-mdp3/mdp_reg_rsz.h b/drivers/media/platform/mtk-mdp3/mdp_reg_rsz.h
new file mode 100644
index 000000000000..407b7691e9d1
--- /dev/null
+++ b/drivers/media/platform/mtk-mdp3/mdp_reg_rsz.h
@@ -0,0 +1,110 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ * Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
+ *
+ */
+
+#ifndef __MDP_REG_RSZ_H__
+#define __MDP_REG_RSZ_H__
+
+#include "mmsys_reg_base.h"
+
+#define PRZ_ENABLE                                        0x000
+#define PRZ_CONTROL_1                                     0x004
+#define PRZ_CONTROL_2                                     0x008
+#define PRZ_INT_FLAG                                      0x00c
+#define PRZ_INPUT_IMAGE                                   0x010
+#define PRZ_OUTPUT_IMAGE                                  0x014
+#define PRZ_HORIZONTAL_COEFF_STEP                         0x018
+#define PRZ_VERTICAL_COEFF_STEP                           0x01c
+#define PRZ_LUMA_HORIZONTAL_INTEGER_OFFSET                0x020
+#define PRZ_LUMA_HORIZONTAL_SUBPIXEL_OFFSET               0x024
+#define PRZ_LUMA_VERTICAL_INTEGER_OFFSET                  0x028
+#define PRZ_LUMA_VERTICAL_SUBPIXEL_OFFSET                 0x02c
+#define PRZ_CHROMA_HORIZONTAL_INTEGER_OFFSET              0x030
+#define PRZ_CHROMA_HORIZONTAL_SUBPIXEL_OFFSET             0x034
+#define PRZ_RSV                                           0x040
+#define PRZ_DEBUG_SEL                                     0x044
+#define PRZ_DEBUG                                         0x048
+#define PRZ_TAP_ADAPT                                     0x04c
+#define PRZ_IBSE_SOFTCLIP                                 0x050
+#define PRZ_IBSE_YLEVEL_1                                 0x054
+#define PRZ_IBSE_YLEVEL_2                                 0x058
+#define PRZ_IBSE_YLEVEL_3                                 0x05c
+#define PRZ_IBSE_YLEVEL_4                                 0x060
+#define PRZ_IBSE_YLEVEL_5                                 0x064
+#define PRZ_IBSE_GAINCONTROL_1                            0x068
+#define PRZ_IBSE_GAINCONTROL_2                            0x06c
+#define PRZ_DEMO_IN_HMASK                                 0x070
+#define PRZ_DEMO_IN_VMASK                                 0x074
+#define PRZ_DEMO_OUT_HMASK                                0x078
+#define PRZ_DEMO_OUT_VMASK                                0x07c
+#define PRZ_ATPG                                          0x0fc
+#define PRZ_PAT1_GEN_SET                                  0x100
+#define PRZ_PAT1_GEN_FRM_SIZE                             0x104
+#define PRZ_PAT1_GEN_COLOR0                               0x108
+#define PRZ_PAT1_GEN_COLOR1                               0x10c
+#define PRZ_PAT1_GEN_COLOR2                               0x110
+#define PRZ_PAT1_GEN_POS                                  0x114
+#define PRZ_PAT1_GEN_TILE_POS                             0x124
+#define PRZ_PAT1_GEN_TILE_OV                              0x128
+#define PRZ_PAT2_GEN_SET                                  0x200
+#define PRZ_PAT2_GEN_COLOR0                               0x208
+#define PRZ_PAT2_GEN_COLOR1                               0x20c
+#define PRZ_PAT2_GEN_POS                                  0x214
+#define PRZ_PAT2_GEN_CURSOR_RB0                           0x218
+#define PRZ_PAT2_GEN_CURSOR_RB1                           0x21c
+#define PRZ_PAT2_GEN_TILE_POS                             0x224
+#define PRZ_PAT2_GEN_TILE_OV                              0x228
+
+/* MASK */
+#define PRZ_ENABLE_MASK                                   0x00010001
+#define PRZ_CONTROL_1_MASK                                0xfffffff3
+#define PRZ_CONTROL_2_MASK                                0x0ffffaff
+#define PRZ_INT_FLAG_MASK                                 0x00000033
+#define PRZ_INPUT_IMAGE_MASK                              0xffffffff
+#define PRZ_OUTPUT_IMAGE_MASK                             0xffffffff
+#define PRZ_HORIZONTAL_COEFF_STEP_MASK                    0x007fffff
+#define PRZ_VERTICAL_COEFF_STEP_MASK                      0x007fffff
+#define PRZ_LUMA_HORIZONTAL_INTEGER_OFFSET_MASK           0x0000ffff
+#define PRZ_LUMA_HORIZONTAL_SUBPIXEL_OFFSET_MASK          0x001fffff
+#define PRZ_LUMA_VERTICAL_INTEGER_OFFSET_MASK             0x0000ffff
+#define PRZ_LUMA_VERTICAL_SUBPIXEL_OFFSET_MASK            0x001fffff
+#define PRZ_CHROMA_HORIZONTAL_INTEGER_OFFSET_MASK         0x0000ffff
+#define PRZ_CHROMA_HORIZONTAL_SUBPIXEL_OFFSET_MASK        0x001fffff
+#define PRZ_RSV_MASK                                      0xffffffff
+#define PRZ_DEBUG_SEL_MASK                                0x0000000f
+#define PRZ_DEBUG_MASK                                    0xffffffff
+#define PRZ_TAP_ADAPT_MASK                                0x03ffffff
+#define PRZ_IBSE_SOFTCLIP_MASK                            0x000fffff
+#define PRZ_IBSE_YLEVEL_1_MASK                            0xffffffff
+#define PRZ_IBSE_YLEVEL_2_MASK                            0xffffffff
+#define PRZ_IBSE_YLEVEL_3_MASK                            0xffffffff
+#define PRZ_IBSE_YLEVEL_4_MASK                            0xffffffff
+#define PRZ_IBSE_YLEVEL_5_MASK                            0x0000ff3f
+#define PRZ_IBSE_GAINCONTROL_1_MASK                       0xffffffff
+#define PRZ_IBSE_GAINCONTROL_2_MASK                       0x0fffff0f
+#define PRZ_DEMO_IN_HMASK_MASK                            0xffffffff
+#define PRZ_DEMO_IN_VMASK_MASK                            0xffffffff
+#define PRZ_DEMO_OUT_HMASK_MASK                           0xffffffff
+#define PRZ_DEMO_OUT_VMASK_MASK                           0xffffffff
+#define PRZ_ATPG_MASK                                     0x00000003
+#define PRZ_PAT1_GEN_SET_MASK                             0x00ff00fd
+#define PRZ_PAT1_GEN_FRM_SIZE_MASK                        0x1fff1fff
+#define PRZ_PAT1_GEN_COLOR0_MASK                          0x00ff00ff
+#define PRZ_PAT1_GEN_COLOR1_MASK                          0x00ff00ff
+#define PRZ_PAT1_GEN_COLOR2_MASK                          0x00ff00ff
+#define PRZ_PAT1_GEN_POS_MASK                             0x1fff1fff
+#define PRZ_PAT1_GEN_TILE_POS_MASK                        0x1fff1fff
+#define PRZ_PAT1_GEN_TILE_OV_MASK                         0x0000ffff
+#define PRZ_PAT2_GEN_SET_MASK                             0x00ff0003
+#define PRZ_PAT2_GEN_COLOR0_MASK                          0x00ff00ff
+#define PRZ_PAT2_GEN_COLOR1_MASK                          0x000000ff
+#define PRZ_PAT2_GEN_POS_MASK                             0x1fff1fff
+#define PRZ_PAT2_GEN_CURSOR_RB0_MASK                      0x00ff00ff
+#define PRZ_PAT2_GEN_CURSOR_RB1_MASK                      0x000000ff
+#define PRZ_PAT2_GEN_TILE_POS_MASK                        0x1fff1fff
+#define PRZ_PAT2_GEN_TILE_OV_MASK                         0x0000ffff
+
+#endif // __MDP_REG_RSZ_H__
diff --git a/drivers/media/platform/mtk-mdp3/mdp_reg_wdma.h b/drivers/media/platform/mtk-mdp3/mdp_reg_wdma.h
new file mode 100644
index 000000000000..7a3b857953e2
--- /dev/null
+++ b/drivers/media/platform/mtk-mdp3/mdp_reg_wdma.h
@@ -0,0 +1,126 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ * Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
+ *
+ */
+
+#ifndef __MDP_REG_WDMA_H__
+#define __MDP_REG_WDMA_H__
+
+#include "mmsys_reg_base.h"
+
+#define WDMA_INTEN              0x000
+#define WDMA_INTSTA             0x004
+#define WDMA_EN                 0x008
+#define WDMA_RST                0x00c
+#define WDMA_SMI_CON            0x010
+#define WDMA_CFG                0x014
+#define WDMA_SRC_SIZE           0x018
+#define WDMA_CLIP_SIZE          0x01c
+#define WDMA_CLIP_COORD         0x020
+#define WDMA_DST_ADDR           0xf00
+#define WDMA_DST_W_IN_BYTE      0x028
+#define WDMA_ALPHA              0x02c
+#define WDMA_BUF_CON1           0x038
+#define WDMA_BUF_CON2           0x03c
+#define WDMA_C00                0x040
+#define WDMA_C02                0x044
+#define WDMA_C10                0x048
+#define WDMA_C12                0x04c
+#define WDMA_C20                0x050
+#define WDMA_C22                0x054
+#define WDMA_PRE_ADD0           0x058
+#define WDMA_PRE_ADD2           0x05c
+#define WDMA_POST_ADD0          0x060
+#define WDMA_POST_ADD2          0x064
+#define WDMA_DST_U_ADDR         0xf04
+#define WDMA_DST_V_ADDR         0xf08
+#define WDMA_DST_UV_PITCH       0x078
+#define WDMA_DST_ADDR_OFFSET    0x080
+#define WDMA_DST_U_ADDR_OFFSET  0x084
+#define WDMA_DST_V_ADDR_OFFSET  0x088
+#define PROC_TRACK_CON_0        0x090
+#define PROC_TRACK_CON_1        0x094
+#define PROC_TRACK_CON_2        0x098
+#define WDMA_FLOW_CTRL_DBG      0x0a0
+#define WDMA_EXEC_DBG           0x0a4
+#define WDMA_CT_DBG             0x0a8
+#define WDMA_SMI_TRAFFIC_DBG    0x0ac
+#define WDMA_PROC_TRACK_DBG_0   0x0b0
+#define WDMA_PROC_TRACK_DBG_1   0x0b4
+#define WDMA_DEBUG              0x0b8
+#define WDMA_DUMMY              0x100
+#define WDMA_DITHER_0           0xe00
+#define WDMA_DITHER_5           0xe14
+#define WDMA_DITHER_6           0xe18
+#define WDMA_DITHER_7           0xe1c
+#define WDMA_DITHER_8           0xe20
+#define WDMA_DITHER_9           0xe24
+#define WDMA_DITHER_10          0xe28
+#define WDMA_DITHER_11          0xe2c
+#define WDMA_DITHER_12          0xe30
+#define WDMA_DITHER_13          0xe34
+#define WDMA_DITHER_14          0xe38
+#define WDMA_DITHER_15          0xe3c
+#define WDMA_DITHER_16          0xe40
+#define WDMA_DITHER_17          0xe44
+
+/* MASK */
+#define WDMA_INTEN_MASK             0x00000003
+#define WDMA_INTSTA_MASK            0x00000003
+#define WDMA_EN_MASK                0x00000001
+#define WDMA_RST_MASK               0x00000001
+#define WDMA_SMI_CON_MASK           0x0fffffff
+#define WDMA_CFG_MASK               0xff03bff0
+#define WDMA_SRC_SIZE_MASK          0x3fff3fff
+#define WDMA_CLIP_SIZE_MASK         0x3fff3fff
+#define WDMA_CLIP_COORD_MASK        0x3fff3fff
+#define WDMA_DST_ADDR_MASK          0xffffffff
+#define WDMA_DST_W_IN_BYTE_MASK     0x0000ffff
+#define WDMA_ALPHA_MASK             0x800000ff
+#define WDMA_BUF_CON1_MASK          0xd1ff01ff
+#define WDMA_BUF_CON2_MASK          0xffffffff
+#define WDMA_C00_MASK               0x1fff1fff
+#define WDMA_C02_MASK               0x00001fff
+#define WDMA_C10_MASK               0x1fff1fff
+#define WDMA_C12_MASK               0x00001fff
+#define WDMA_C20_MASK               0x1fff1fff
+#define WDMA_C22_MASK               0x00001fff
+#define WDMA_PRE_ADD0_MASK          0x01ff01ff
+#define WDMA_PRE_ADD2_MASK          0x000001ff
+#define WDMA_POST_ADD0_MASK         0x01ff01ff
+#define WDMA_POST_ADD2_MASK         0x000001ff
+#define WDMA_DST_U_ADDR_MASK        0xffffffff
+#define WDMA_DST_V_ADDR_MASK        0xffffffff
+#define WDMA_DST_UV_PITCH_MASK      0x0000ffff
+#define WDMA_DST_ADDR_OFFSET_MASK   0x0fffffff
+#define WDMA_DST_U_ADDR_OFFSET_MASK 0x0fffffff
+#define WDMA_DST_V_ADDR_OFFSET_MASK 0x0fffffff
+#define PROC_TRACK_CON_0_MASK       0x70000fff
+#define PROC_TRACK_CON_1_MASK       0x00ffffff
+#define PROC_TRACK_CON_2_MASK       0x00ffffff
+#define WDMA_FLOW_CTRL_DBG_MASK     0x0000f3ff
+#define WDMA_EXEC_DBG_MASK          0x003f003f
+#define WDMA_CT_DBG_MASK            0x3fff3fff
+#define WDMA_SMI_TRAFFIC_DBG_MASK   0xffffffff
+#define WDMA_PROC_TRACK_DBG_0_MASK  0xffffffff
+#define WDMA_PROC_TRACK_DBG_1_MASK  0xffffffff
+#define WDMA_DEBUG_MASK             0xffffffff
+#define WDMA_DUMMY_MASK             0xffffffff
+#define WDMA_DITHER_0_MASK          0x0111ff11
+#define WDMA_DITHER_5_MASK          0x0000ffff
+#define WDMA_DITHER_6_MASK          0x0001f3ff
+#define WDMA_DITHER_7_MASK          0x00000333
+#define WDMA_DITHER_8_MASK          0x03ff0001
+#define WDMA_DITHER_9_MASK          0x03ff03ff
+#define WDMA_DITHER_10_MASK         0x00000733
+#define WDMA_DITHER_11_MASK         0x00003331
+#define WDMA_DITHER_12_MASK         0xffff0031
+#define WDMA_DITHER_13_MASK         0x00000777
+#define WDMA_DITHER_14_MASK         0x00000371
+#define WDMA_DITHER_15_MASK         0x77770001
+#define WDMA_DITHER_16_MASK         0x77777777
+#define WDMA_DITHER_17_MASK         0x0001ffff
+
+#endif  // __MDP_REG_WDMA_H__
diff --git a/drivers/media/platform/mtk-mdp3/mdp_reg_wrot.h b/drivers/media/platform/mtk-mdp3/mdp_reg_wrot.h
new file mode 100644
index 000000000000..376bc85c7703
--- /dev/null
+++ b/drivers/media/platform/mtk-mdp3/mdp_reg_wrot.h
@@ -0,0 +1,116 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ * Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
+ *
+ */
+
+#ifndef __MDP_REG_WROT_H__
+#define __MDP_REG_WROT_H__
+
+#include "mmsys_reg_base.h"
+
+#define VIDO_CTRL                   0x000
+#define VIDO_DMA_PERF               0x004
+#define VIDO_MAIN_BUF_SIZE          0x008
+#define VIDO_SOFT_RST               0x010
+#define VIDO_SOFT_RST_STAT          0x014
+#define VIDO_INT_EN                 0x018
+#define VIDO_INT                    0x01c
+#define VIDO_CROP_OFST              0x020
+#define VIDO_TAR_SIZE               0x024
+#define VIDO_BASE_ADDR              0xf00
+#define VIDO_OFST_ADDR              0x02c
+#define VIDO_STRIDE                 0x030
+#define VIDO_BASE_ADDR_C            0xf04
+#define VIDO_OFST_ADDR_C            0x038
+#define VIDO_STRIDE_C               0x03c
+#define VIDO_DITHER                 0x054
+#define VIDO_BASE_ADDR_V            0xf08
+#define VIDO_OFST_ADDR_V            0x068
+#define VIDO_STRIDE_V               0x06c
+#define VIDO_RSV_1                  0x070
+#define VIDO_DMA_PREULTRA           0x074
+#define VIDO_IN_SIZE                0x078
+#define VIDO_ROT_EN                 0x07c
+#define VIDO_FIFO_TEST              0x080
+#define VIDO_MAT_CTRL               0x084
+#define VIDO_MAT_RMY                0x088
+#define VIDO_MAT_RMV                0x08c
+#define VIDO_MAT_GMY                0x090
+#define VIDO_MAT_BMY                0x094
+#define VIDO_MAT_BMV                0x098
+#define VIDO_MAT_PREADD             0x09c
+#define VIDO_MAT_POSTADD            0x0a0
+#define VIDO_DITHER_00              0x0a4
+#define VIDO_DITHER_02              0x0ac
+#define VIDO_DITHER_03              0x0b0
+#define VIDO_DITHER_04              0x0b4
+#define VIDO_DITHER_05              0x0b8
+#define VIDO_DITHER_06              0x0bc
+#define VIDO_DITHER_07              0x0c0
+#define VIDO_DITHER_08              0x0c4
+#define VIDO_DITHER_09              0x0c8
+#define VIDO_DITHER_10              0x0cc
+#define VIDO_DEBUG                  0x0d0
+#define VIDO_ARB_SW_CTL             0x0d4
+#define MDP_WROT_TRACK_CTL          0x0e0
+#define MDP_WROT_TRACK_WINDOW       0x0e4
+#define MDP_WROT_TRACK_TARGET       0x0e8
+#define MDP_WROT_TRACK_STOP         0x0ec
+#define MDP_WROT_TRACK_PROC_CNT0    0x0f0
+#define MDP_WROT_TRACK_PROC_CNT1    0x0f4
+
+/* MASK */
+#define VIDO_CTRL_MASK                  0xf530711f
+#define VIDO_DMA_PERF_MASK              0x3fffffff
+#define VIDO_MAIN_BUF_SIZE_MASK         0x1fff7f77
+#define VIDO_SOFT_RST_MASK              0x00000001
+#define VIDO_SOFT_RST_STAT_MASK         0x00000001
+#define VIDO_INT_EN_MASK                0x00003f07
+#define VIDO_INT_MASK                   0x00000007
+#define VIDO_CROP_OFST_MASK             0x1fff1fff
+#define VIDO_TAR_SIZE_MASK              0x1fff1fff
+#define VIDO_BASE_ADDR_MASK             0xffffffff
+#define VIDO_OFST_ADDR_MASK             0x0fffffff
+#define VIDO_STRIDE_MASK                0x0000ffff
+#define VIDO_BASE_ADDR_C_MASK           0xffffffff
+#define VIDO_OFST_ADDR_C_MASK           0x0fffffff
+#define VIDO_STRIDE_C_MASK              0x0000ffff
+#define VIDO_DITHER_MASK                0xff000001
+#define VIDO_BASE_ADDR_V_MASK           0xffffffff
+#define VIDO_OFST_ADDR_V_MASK           0x0fffffff
+#define VIDO_STRIDE_V_MASK              0x0000ffff
+#define VIDO_RSV_1_MASK                 0xffffffff
+#define VIDO_DMA_PREULTRA_MASK          0x00ffffff
+#define VIDO_IN_SIZE_MASK               0x1fff1fff
+#define VIDO_ROT_EN_MASK                0x00000001
+#define VIDO_FIFO_TEST_MASK             0x00000fff
+#define VIDO_MAT_CTRL_MASK              0x000000f3
+#define VIDO_MAT_RMY_MASK               0x1fff1fff
+#define VIDO_MAT_RMV_MASK               0x1fff1fff
+#define VIDO_MAT_GMY_MASK               0x1fff1fff
+#define VIDO_MAT_BMY_MASK               0x1fff1fff
+#define VIDO_MAT_BMV_MASK               0x00001fff
+#define VIDO_MAT_PREADD_MASK            0x1ff7fdff
+#define VIDO_MAT_POSTADD_MASK           0x1ff7fdff
+#define VIDO_DITHER_00_MASK             0x0000ff3f
+#define VIDO_DITHER_02_MASK             0xffff3fff
+#define VIDO_DITHER_03_MASK             0x0000003f
+#define VIDO_DITHER_04_MASK             0xbfffffff
+#define VIDO_DITHER_05_MASK             0xffff7fff
+#define VIDO_DITHER_06_MASK             0x003ff773
+#define VIDO_DITHER_07_MASK             0x00007777
+#define VIDO_DITHER_08_MASK             0x00007777
+#define VIDO_DITHER_09_MASK             0x00007777
+#define VIDO_DITHER_10_MASK             0x0001ffff
+#define VIDO_DEBUG_MASK                 0xffffffff
+#define VIDO_ARB_SW_CTL_MASK            0x00000007
+#define MDP_WROT_TRACK_CTL_MASK         0x0000001f
+#define MDP_WROT_TRACK_WINDOW_MASK      0x00000fff
+#define MDP_WROT_TRACK_TARGET_MASK      0x00ffffff
+#define MDP_WROT_TRACK_STOP_MASK        0x00ffffff
+#define MDP_WROT_TRACK_PROC_CNT0_MASK   0xffffffff
+#define MDP_WROT_TRACK_PROC_CNT1_MASK   0x00000001
+
+#endif  // __MDP_REG_WROT_H__
diff --git a/drivers/media/platform/mtk-mdp3/mmsys_config.h b/drivers/media/platform/mtk-mdp3/mmsys_config.h
new file mode 100644
index 000000000000..d1328840bbf2
--- /dev/null
+++ b/drivers/media/platform/mtk-mdp3/mmsys_config.h
@@ -0,0 +1,189 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ * Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
+ *
+ */
+
+#ifndef __MMSYS_CONFIG_H__
+#define __MMSYS_CONFIG_H__
+
+#include "mmsys_reg_base.h"
+
+#define MMSYS_INTEN                   0x000
+#define MMSYS_INTSTA                  0x004
+#define MJC_APB_TX_CON                0x00c
+
+#define ISP_MOUT_EN                   0xf80
+#define MDP_RDMA0_MOUT_EN             0xf84
+#define MDP_RDMA1_MOUT_EN             0xf88
+#define MDP_PRZ0_MOUT_EN              0xf8c
+#define MDP_PRZ1_MOUT_EN              0xf90
+#define MDP_COLOR_MOUT_EN             0xf94
+#define IPU_MOUT_EN                   0xf98
+#define DISP_TO_WROT_SOUT_SEL         0xfa0
+#define MDP_COLOR_IN_SOUT_SEL         0xfa4
+#define MDP_PATH0_SOUT_SEL            0xfa8
+#define MDP_PATH1_SOUT_SEL            0xfac
+#define MDP_TDSHP_SOUT_SEL            0xfb0
+
+#define DISP_OVL0_MOUT_EN             0xf00
+#define DISP_OVL0_2L_MOUT_EN          0xf04
+#define DISP_OVL1_2L_MOUT_EN          0xf08
+#define DISP_DITHER0_MOUT_EN          0xf0c
+#define DISP_RSZ_MOUT_EN              0xf10
+
+#define MMSYS_MOUT_RST                0x048
+#define MDP_PRZ0_SEL_IN               0xfc0
+#define MDP_PRZ1_SEL_IN               0xfc4
+#define MDP_TDSHP_SEL_IN              0xfc8
+#define DISP_WDMA0_SEL_IN             0xfcc
+#define MDP_WROT0_SEL_IN              0xfd0
+#define MDP_WDMA_SEL_IN               0xfd4
+#define MDP_COLOR_OUT_SEL_IN          0xfd8
+#define MDP_COLOR_SEL_IN              0xfdc
+#define MDP_PATH0_SEL_IN              0xfe0
+#define MDP_PATH1_SEL_IN              0xfe4
+
+#define DISP_COLOR_OUT_SEL_IN         0xf20
+#define DISP_PATH0_SEL_IN             0xf24
+#define DISP_WDMA0_PRE_SEL_IN         0xf28
+#define DSI0_SEL_IN                   0xf2c
+#define DSI1_SEL_IN                   0xf30
+#define DISP_OVL0_SEL_IN              0xf34
+#define DISP_OVL0_2L_SEL_IN           0xf38
+#define OVL_TO_RSZ_SEL_IN             0xf3c
+#define OVL_TO_WDMA_SEL_IN            0xf40
+#define OVL_TO_WROT_SEL_IN            0xf44
+#define DISP_RSZ_SEL_IN               0xf48
+#define DISP_RDMA0_SOUT_SEL_IN        0xf50
+#define DISP_RDMA1_SOUT_SEL_IN        0xf54
+#define MDP_TO_DISP0_SOUT_SEL_IN      0xf58
+#define MDP_TO_DISP1_SOUT_SEL_IN      0xf5c
+#define DISP_RDMA0_RSZ_IN_SOUT_SEL_IN 0xf60
+#define DISP_RDMA0_RSZ_OUT_SEL_IN     0xf64
+#define MDP_AAL_MOUT_EN               0xfe8
+#define MDP_AAL_SEL_IN                0xfec
+#define MDP_CCORR_SEL_IN              0xff0
+#define MDP_CCORR_SOUT_SEL            0xff4
+
+#define MMSYS_MISC                    0x0f0
+#define MMSYS_SMI_LARB_SEL            0x0f4
+#define MMSYS_SODI_REQ_MASK           0x0f8
+#define MMSYS_CG_CON0                 0x100
+#define MMSYS_CG_SET0                 0x104
+#define MMSYS_CG_CLR0                 0x108
+#define MMSYS_CG_CON1                 0x110
+#define MMSYS_CG_SET1                 0x114
+#define MMSYS_CG_CLR1                 0x118
+#define MMSYS_HW_DCM_DIS0             0x120
+#define MMSYS_HW_DCM_DIS_SET0         0x124
+#define MMSYS_HW_DCM_DIS_CLR0         0x128
+#define MMSYS_HW_DCM_DIS1             0x130
+#define MMSYS_HW_DCM_DIS_SET1         0x134
+#define MMSYS_HW_DCM_DIS_CLR1         0x138
+#define MMSYS_HW_DCM_EVENT_CTL1       0x13c
+#define MMSYS_SW0_RST_B               0x140
+#define MMSYS_SW1_RST_B               0x144
+#define MMSYS_LCM_RST_B               0x150
+#define LARB6_AXI_ASIF_CFG_WD         0x180
+#define LARB6_AXI_ASIF_CFG_RD         0x184
+#define PROC_TRACK_EMI_BUSY_CON       0x190
+#define DISP_FAKE_ENG_EN              0x200
+#define DISP_FAKE_ENG_RST             0x204
+#define DISP_FAKE_ENG_CON0            0x208
+#define DISP_FAKE_ENG_CON1            0x20c
+#define DISP_FAKE_ENG_RD_ADDR         0x210
+#define DISP_FAKE_ENG_WR_ADDR         0x214
+#define DISP_FAKE_ENG_STATE           0x218
+#define DISP_FAKE_ENG2_EN             0x220
+#define DISP_FAKE_ENG2_RST            0x224
+#define DISP_FAKE_ENG2_CON0           0x228
+#define DISP_FAKE_ENG2_CON1           0x22c
+#define DISP_FAKE_ENG2_RD_ADDR        0x230
+#define DISP_FAKE_ENG2_WR_ADDR        0x234
+#define DISP_FAKE_ENG2_STATE          0x238
+#define MMSYS_MBIST_CON               0x800
+#define MMSYS_MBIST_DONE              0x804
+#define MMSYS_MBIST_HOLDB             0x808
+#define MMSYS_MBIST_MODE              0x80c
+#define MMSYS_MBIST_FAIL0             0x810
+#define MMSYS_MBIST_FAIL1             0x814
+#define MMSYS_MBIST_FAIL2             0x818
+#define MMSYS_MBIST_DEBUG             0x820
+#define MMSYS_MBIST_DIAG_SCANOUT      0x824
+#define MMSYS_MBIST_PRE_FUSE          0x828
+#define MMSYS_MBIST_BSEL0             0x82c
+#define MMSYS_MBIST_BSEL1             0x830
+#define MMSYS_MBIST_BSEL2             0x834
+#define MMSYS_MBIST_BSEL3             0x838
+#define MMSYS_MBIST_HDEN              0x83c
+#define MDP_RDMA0_MEM_DELSEL          0x840
+#define MDP_RDMA1_MEM_DELSEL          0x844
+#define MDP_RSZ_MEM_DELSEL            0x848
+#define MDP_TDSHP_MEM_DELSEL          0x84c
+#define MDP_AAL_MEM_DELSEL            0x850
+
+#define MDP_WROT0_MEM_DELSEL          0x854
+#define MDP_WDMA_MEM_DELSEL           0x858
+#define DISP_OVL_MEM_DELSEL           0x85c
+#define DISP_OVL_2L_MEM_DELSEL        0x860
+#define DISP_RDMA_MEM_DELSEL          0x864
+#define DISP_WDMA0_MEM_DELSEL         0x868
+#define DISP_GAMMA_MEM_DELSEL         0x870
+#define DSI_MEM_DELSEL                0x874
+#define DISP_SPLIT_MEM_DELSEL         0x878
+#define DISP_DSC_MEM_DELSEL           0x87c
+#define MMSYS_DEBUG_OUT_SEL           0x88c
+#define MMSYS_MBIST_RP_RST_B          0x890
+#define MMSYS_MBIST_RP_FAIL0          0x894
+#define MMSYS_MBIST_RP_FAIL1          0x898
+#define MMSYS_MBIST_RP_OK0            0x89c
+#define MMSYS_MBIST_RP_OK1            0x8a0
+#define MMSYS_DUMMY0                  0x8a4
+#define MMSYS_DUMMY1                  0x8a8
+#define MMSYS_DUMMY2                  0x8ac
+#define MMSYS_DUMMY3                  0x8b0
+#define DISP_DL_VALID_0               0x8b4
+#define DISP_DL_VALID_1               0x8b8
+#define DISP_DL_VALID_2               0x8bc
+#define DISP_DL_READY_0               0x8c0
+#define DISP_DL_READY_1               0x8c4
+#define DISP_DL_READY_2               0x8C8
+#define MDP_DL_VALID_0                0x8cc
+#define MDP_DL_VALID_1                0x8d0
+#define MDP_DL_READY_0                0x8d4
+#define MDP_DL_READY_1                0x8d8
+#define SMI_LARB0_GREQ                0x8dc
+#define DISP_MOUT_MASK                0x8e0
+#define DISP_MOUT_MASK1               0x8e4
+#define MDP_MOUT_MASK                 0x8e8
+#define MMSYS_POWER_READ              0x8ec
+#define TOP_RELAY_FSM_RD              0x960
+#define MDP_ASYNC_CFG_WD              0x934
+#define MDP_ASYNC_CFG_RD              0x938
+#define MDP_ASYNC_IPU_CFG_WD          0x93C
+#define MDP_ASYNC_CFG_IPU_RD          0x940
+#define MDP_ASYNC_CFG_OUT_RD          0x958
+#define MDP_ASYNC_IPU_CFG_OUT_RD      0x95C
+#define ISP_RELAY_CFG_WD              0x994
+#define ISP_RELAY_CNT_RD              0x998
+#define ISP_RELAY_CNT_LATCH_RD        0x99c
+#define IPU_RELAY_CFG_WD              0x9a0
+#define IPU_RELAY_CNT_RD              0x9a4
+#define IPU_RELAY_CNT_LATCH_RD        0x9a8
+
+/* MASK */
+#define MMSYS_SW0_RST_B_MASK          0xffffffff
+#define MMSYS_SW1_RST_B_MASK          0xffffffff
+#define MDP_COLOR_IN_SOUT_SEL_MASK    0x0000000f
+#define DISP_COLOR_OUT_SEL_IN_MASK    0xffffffff
+#define MDP_ASYNC_CFG_WD_MASK         0xffffffff
+#define MDP_ASYNC_IPU_CFG_WD_MASK     0xffffffff
+#define MMSYS_HW_DCM_DIS0_MASK        0xffffffff
+#define MMSYS_HW_DCM_DIS1_MASK        0xffffffff
+#define MDP_ASYNC_CFG_WD_MASK         0xffffffff
+#define ISP_RELAY_CFG_WD_MASK         0xffffffff
+#define IPU_RELAY_CFG_WD_MASK         0xffffffff
+
+#endif  // __MMSYS_CONFIG_H__
diff --git a/drivers/media/platform/mtk-mdp3/mmsys_mutex.h b/drivers/media/platform/mtk-mdp3/mmsys_mutex.h
new file mode 100644
index 000000000000..9ff382cc37c8
--- /dev/null
+++ b/drivers/media/platform/mtk-mdp3/mmsys_mutex.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ * Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
+ *
+ */
+
+#ifndef __MMSYS_MUTEX_H__
+#define __MMSYS_MUTEX_H__
+
+#include "mmsys_reg_base.h"
+#include "mdp-platform.h"
+
+#define MM_MUTEX_INTEN              0x00
+#define MM_MUTEX_INTSTA             0x04
+#define MM_MUTEX_CFG                0x08
+
+#define MM_MUTEX_EN                 (0x20 + mutex_id * 0x20)
+#define MM_MUTEX_GET                (0x24 + mutex_id * 0x20)
+#define MM_MUTEX_RST                (0x28 + mutex_id * 0x20)
+#define MM_MUTEX_MOD                (MM_MUTEX_MOD_OFFSET + mutex_id * 0x20)
+#define MM_MUTEX_SOF                (MM_MUTEX_SOF_OFFSET + mutex_id * 0x20)
+
+// MASK
+#define MM_MUTEX_INTEN_MASK         0x0fff
+#define MM_MUTEX_INTSTA_MASK        0x0fff
+#define MM_MUTEX_DEBUG_OUT_SEL_MASK 0x03
+#define MM_MUTEX_CFG_MASK           0x01
+
+#define MM_MUTEX_EN_MASK            0x01
+#define MM_MUTEX_GET_MASK           0x03
+#define MM_MUTEX_RST_MASK           0x01
+#define MM_MUTEX_MOD_MASK           0x07ffffff
+#define MM_MUTEX_SOF_MASK           0x0f
+
+#endif  // __MMSYS_MUTEX_H__
diff --git a/drivers/media/platform/mtk-mdp3/mmsys_reg_base.h b/drivers/media/platform/mtk-mdp3/mmsys_reg_base.h
new file mode 100644
index 000000000000..441cb56fcbba
--- /dev/null
+++ b/drivers/media/platform/mtk-mdp3/mmsys_reg_base.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ * Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
+ *
+ */
+
+#ifndef __MMSYS_REG_BASE_H__
+#define __MMSYS_REG_BASE_H__
+
+#define MM_REG_WRITE_MASK(cmd, id, base, ofst, val, mask, ...) \
+	cmdq_pkt_write_mask(cmd->pkt, id, \
+		(base) + (ofst), (val), (mask), ##__VA_ARGS__)
+#define MM_REG_WRITE(cmd, id, base, ofst, val, mask, ...) \
+	MM_REG_WRITE_MASK(cmd, id, base, ofst, val, \
+		(((mask) & (ofst##_MASK)) == (ofst##_MASK)) ? \
+			(0xffffffff) : (mask), ##__VA_ARGS__)
+
+#define MM_REG_WAIT(cmd, evt) \
+	cmdq_pkt_wfe(cmd->pkt, cmd->event[(evt)])
+
+#define MM_REG_WAIT_NO_CLEAR(cmd, evt) \
+	cmdq_pkt_wait_no_clear(cmd->pkt, cmd->event[(evt)])
+
+#define MM_REG_CLEAR(cmd, evt) \
+	cmdq_pkt_clear_event(cmd->pkt, cmd->event[(evt)])
+
+#define MM_REG_SET_EVENT(cmd, evt) \
+	cmdq_pkt_set_event(cmd->pkt, cmd->event[(evt)])
+
+#define MM_REG_POLL_MASK(cmd, id, base, ofst, val, mask, ...) \
+	cmdq_pkt_poll(cmd->pkt, id, \
+		(base) + (ofst), (val), (mask), ##__VA_ARGS__)
+#define MM_REG_POLL(cmd, id, base, ofst, val, mask, ...) \
+	MM_REG_POLL_MASK(cmd, id, base, ofst, val, \
+		(((mask) & (ofst##_MASK)) == (ofst##_MASK)) ? \
+			(0xffffffff) : (mask), ##__VA_ARGS__)
+
+#endif  // __MM_REG_BASE_H__
diff --git a/drivers/media/platform/mtk-mdp3/mtk-img-ipi.h b/drivers/media/platform/mtk-mdp3/mtk-img-ipi.h
new file mode 100644
index 000000000000..9fabe7e8b71d
--- /dev/null
+++ b/drivers/media/platform/mtk-mdp3/mtk-img-ipi.h
@@ -0,0 +1,282 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Holmes Chiou <holmes.chiou@mediatek.com>
+ *         Ping-Hsun Wu <ping-hsun.wu@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_IMG_IPI_H__
+#define __MTK_IMG_IPI_H__
+
+#include <linux/types.h>
+
+/* ISP-MDP generic input information */
+
+#define IMG_MAX_HW_INPUTS	1
+
+#define IMG_MAX_HW_OUTPUTS	4
+
+#define IMG_MAX_PLANES		3
+
+#define IMG_IPI_INIT    1
+#define IMG_IPI_DEINIT  2
+#define IMG_IPI_FRAME   3
+#define IMG_IPI_DEBUG   4
+
+struct img_addr {
+	u64	va;	/* Used by Linux OS access */
+	u32	pa;	/* Used by CM4 access */
+	u32	iova;	/* Used by IOMMU HW access */
+} __attribute__ ((__packed__));
+
+struct img_sw_addr {
+	u64	va;	/* Used by APMCU access */
+	u32	pa;	/* Used by CM4 access */
+} __attribute__ ((__packed__));
+
+struct img_plane_format {
+	u32	size;
+	u16	stride;
+} __attribute__ ((__packed__));
+
+struct img_pix_format {
+	u16		width;
+	u16		height;
+	u32		colorformat;	/* enum mdp_color */
+	u16		ycbcr_prof;	/* enum mdp_ycbcr_profile */
+	struct img_plane_format	plane_fmt[IMG_MAX_PLANES];
+} __attribute__ ((__packed__));
+
+struct img_image_buffer {
+	struct img_pix_format	format;
+	u32		iova[IMG_MAX_PLANES];
+	/* enum mdp_buffer_usage, FD or advanced ISP usages */
+	u32		usage;
+} __attribute__ ((__packed__));
+
+#define IMG_SUBPIXEL_SHIFT	20
+
+struct img_crop {
+	s16		left;
+	s16		top;
+	u16	width;
+	u16	height;
+	u32	left_subpix;
+	u32	top_subpix;
+	u32	width_subpix;
+	u32	height_subpix;
+} __attribute__ ((__packed__));
+
+#define IMG_CTRL_FLAG_HFLIP	BIT(0)
+#define IMG_CTRL_FLAG_DITHER	BIT(1)
+#define IMG_CTRL_FLAG_SHARPNESS	BIT(4)
+#define IMG_CTRL_FLAG_HDR	BIT(5)
+#define IMG_CTRL_FLAG_DRE	BIT(6)
+
+struct img_input {
+	struct img_image_buffer	buffer;
+	u16		flags;	/* HDR, DRE, dither */
+} __attribute__ ((__packed__));
+
+struct img_output {
+	struct img_image_buffer	buffer;
+	struct img_crop		crop;
+	s16			rotation;
+	u16		flags;	/* H-flip, sharpness, dither */
+} __attribute__ ((__packed__));
+
+struct img_ipi_frameparam {
+	u32		index;
+	u32		frame_no;
+	u64		timestamp;
+	u8			type;	/* enum mdp_stream_type */
+	u8			state;
+	u8			num_inputs;
+	u8			num_outputs;
+	u64		drv_data;
+	struct img_input	inputs[IMG_MAX_HW_INPUTS];
+	struct img_output	outputs[IMG_MAX_HW_OUTPUTS];
+	struct img_addr		tuning_data;
+	struct img_addr		subfrm_data;
+	struct img_sw_addr	config_data;
+	struct img_sw_addr  self_data;
+	/* u8		pq_data[]; */
+} __attribute__ ((__packed__));
+
+struct img_ipi_param {
+	u8	usage;
+	struct	img_sw_addr frm_param;
+} __attribute__ ((__packed__));
+
+struct img_frameparam {
+	struct list_head	list_entry;
+	struct img_ipi_frameparam frameparam;
+};
+
+/* ISP-MDP generic output information */
+
+struct img_comp_frame {
+	u32	output_disable:1;
+	u32	bypass:1;
+	u16	in_width;
+	u16	in_height;
+	u16	out_width;
+	u16	out_height;
+	struct img_crop	crop;
+	u16	in_total_width;
+	u16	out_total_width;
+} __attribute__ ((__packed__));
+
+struct img_region {
+	s16	left;
+	s16	right;
+	s16	top;
+	s16	bottom;
+} __attribute__ ((__packed__));
+
+struct img_offset {
+	s16		left;
+	s16		top;
+	u32	left_subpix;
+	u32	top_subpix;
+} __attribute__ ((__packed__));
+
+struct img_comp_subfrm {
+	u32		tile_disable:1;
+	struct img_region	in;
+	struct img_region	out;
+	struct img_offset	luma;
+	struct img_offset	chroma;
+	s16			out_vertical;	/* Output vertical index */
+	s16			out_horizontal;	/* Output horizontal index */
+} __attribute__ ((__packed__));
+
+#define IMG_MAX_SUBFRAMES	12
+
+struct mdp_rdma_subfrm {
+	u32	offset[IMG_MAX_PLANES];
+	u32	offset_0_p;
+	u32	src;
+	u32	clip;
+	u32	clip_ofst;
+} __attribute__ ((__packed__));
+
+struct mdp_rdma_data {
+	u32		src_ctrl;
+	u32		control;
+	u32		iova[IMG_MAX_PLANES];
+	u32		iova_end[IMG_MAX_PLANES];
+	u32		mf_bkgd;
+	u32		mf_bkgd_in_pxl;
+	u32		sf_bkgd;
+	u32		ufo_dec_y;
+	u32		ufo_dec_c;
+	u32		transform;
+	struct mdp_rdma_subfrm	subfrms[IMG_MAX_SUBFRAMES];
+} __attribute__ ((__packed__));
+
+struct mdp_rsz_subfrm {
+	u32	control2;
+	u32	src;
+	u32	clip;
+} __attribute__ ((__packed__));
+
+struct mdp_rsz_data {
+	u32		coeff_step_x;
+	u32		coeff_step_y;
+	u32		control1;
+	u32		control2;
+	struct mdp_rsz_subfrm	subfrms[IMG_MAX_SUBFRAMES];
+} __attribute__ ((__packed__));
+
+struct mdp_wrot_subfrm {
+	u32	offset[IMG_MAX_PLANES];
+	u32	src;
+	u32	clip;
+	u32	clip_ofst;
+	u32	main_buf;
+} __attribute__ ((__packed__));
+
+struct mdp_wrot_data {
+	u32		iova[IMG_MAX_PLANES];
+	u32		control;
+	u32		stride[IMG_MAX_PLANES];
+	u32		mat_ctrl;
+	u32		fifo_test;
+	u32		filter;
+	struct mdp_wrot_subfrm	subfrms[IMG_MAX_SUBFRAMES];
+} __attribute__ ((__packed__));
+
+struct mdp_wdma_subfrm {
+	u32	offset[IMG_MAX_PLANES];
+	u32	src;
+	u32	clip;
+	u32	clip_ofst;
+} __attribute__ ((__packed__));
+
+struct mdp_wdma_data {
+	u32		wdma_cfg;
+	u32		iova[IMG_MAX_PLANES];
+	u32		w_in_byte;
+	u32		uv_stride;
+	struct mdp_wdma_subfrm	subfrms[IMG_MAX_SUBFRAMES];
+} __attribute__ ((__packed__));
+
+struct isp_data {
+	u64	dl_flags;	/* 1 << (enum mdp_comp_type) */
+	u32	smxi_iova[4];
+	u32	cq_idx;
+	u32	cq_iova;
+	u32	tpipe_iova[IMG_MAX_SUBFRAMES];
+} __attribute__ ((__packed__));
+
+struct img_compparam {
+	u16		type;	/* enum mdp_comp_type */
+	u16		id;	/* enum mdp_comp_id */
+	u32		input;
+	u32		outputs[IMG_MAX_HW_OUTPUTS];
+	u32		num_outputs;
+	struct img_comp_frame	frame;
+	struct img_comp_subfrm	subfrms[IMG_MAX_SUBFRAMES];
+	u32		num_subfrms;
+	union {
+		struct mdp_rdma_data	rdma;
+		struct mdp_rsz_data	rsz;
+		struct mdp_wrot_data	wrot;
+		struct mdp_wdma_data	wdma;
+		/* struct mdp_hdr_data	hdr; */
+		struct isp_data		isp;
+		/* struct wpe_data	wpe; */
+	};
+} __attribute__ ((__packed__));
+
+#define IMG_MAX_COMPONENTS	20
+
+struct img_mux {
+	u32	reg;
+	u32	value;
+};
+
+struct img_mmsys_ctrl {
+	struct img_mux	sets[IMG_MAX_COMPONENTS * 2];
+	u32	num_sets;
+};
+
+struct img_config {
+	struct img_compparam	components[IMG_MAX_COMPONENTS];
+	u32		num_components;
+	struct img_mmsys_ctrl	ctrls[IMG_MAX_SUBFRAMES];
+	u32		num_subfrms;
+} __attribute__ ((__packed__));
+
+#endif  /* __MTK_IMG_IPI_H__ */
+
diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-cmdq.c b/drivers/media/platform/mtk-mdp3/mtk-mdp3-cmdq.c
new file mode 100644
index 000000000000..562ad8509fc2
--- /dev/null
+++ b/drivers/media/platform/mtk-mdp3/mtk-mdp3-cmdq.c
@@ -0,0 +1,442 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Ping-Hsun Wu <ping-hsun.wu@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/platform_device.h>
+#include "mtk-mdp3-cmdq.h"
+#include "mtk-mdp3-comp.h"
+#include "mtk-mdp3-core.h"
+
+#include "mdp-platform.h"
+#include "mmsys_mutex.h"
+
+#define DISP_MUTEX_MDP_FIRST	(5)
+#define DISP_MUTEX_MDP_COUNT	(5)
+
+#define MDP_PATH_MAX_COMPS	IMG_MAX_COMPONENTS
+
+struct mdp_path {
+	struct mdp_dev		*mdp_dev;
+	struct mdp_comp_ctx	comps[MDP_PATH_MAX_COMPS];
+	u32			num_comps;
+	const struct img_config	*config;
+	const struct img_ipi_frameparam *param;
+	const struct v4l2_rect	*composes[IMG_MAX_HW_OUTPUTS];
+	struct v4l2_rect	bounds[IMG_MAX_HW_OUTPUTS];
+};
+
+#define has_op(ctx, op) \
+	(ctx->comp->ops && ctx->comp->ops->op)
+#define call_op(ctx, op, ...) \
+	(has_op(ctx, op) ? ctx->comp->ops->op(ctx, ##__VA_ARGS__) : 0)
+
+struct mdp_path_subfrm {
+	s32	mutex_id;
+	u32	mutex_mod;
+	s32	sofs[MDP_PATH_MAX_COMPS];
+	u32	num_sofs;
+};
+
+bool is_output_disable(const struct img_compparam *param, u32 count)
+{
+	return (count < param->num_subfrms) ?
+		(param->frame.output_disable ||
+		param->subfrms[count].tile_disable) :
+		true;
+}
+
+int mdp_path_subfrm_require(struct mdp_path_subfrm *subfrm,
+			    const struct mdp_path *path, struct mdp_cmd *cmd,
+			    u32 count)
+{
+	const struct img_config *config = path->config;
+	const struct mdp_comp_ctx *ctx;
+	phys_addr_t mm_mutex = path->mdp_dev->mm_mutex.reg_base;
+	s32 mutex_id = -1;
+	u32 mutex_sof = 0;
+	int mdp_color = 0;
+	int index;
+	u8 subsys_id = path->mdp_dev->mm_mutex.subsys_id;
+
+	/* Default value */
+	memset(subfrm, 0, sizeof(*subfrm));
+
+	for (index = 0; index < config->num_components; index++) {
+		ctx = &path->comps[index];
+		if (is_output_disable(ctx->param, count))
+			continue;
+		switch (ctx->comp->id) {
+		/**********************************************
+		 * Name            MSB LSB
+		 * DISP_MUTEX_MOD   23   0
+		 *
+		 * Specifies which modules are in this mutex.
+		 * Every bit denotes a module. Bit definition:
+		 *  2 mdp_rdma0
+		 *  4 mdp_rsz0
+		 *  5 mdp_rsz1
+		 *  6 mdp_tdshp
+		 *  7 mdp_wrot0
+		 *  8 mdp_wdma
+		 *  13 mdp_color
+		 *  23 mdp_aal
+		 *  24 mdp_ccorr
+		 **********************************************/
+		case MDP_AAL0:
+			subfrm->mutex_mod |= 1 << 23;
+			break;
+		case MDP_CCORR0:
+			subfrm->mutex_mod |= 1 << 24;
+			break;
+		case MDP_COLOR0:
+			if (mdp_color)
+				subfrm->mutex_mod |= 1 << 13;
+			break;
+		case MDP_WDMA:
+			subfrm->mutex_mod |= 1 << 8;
+			subfrm->sofs[subfrm->num_sofs++] = MDP_WDMA;
+			break;
+		case MDP_WROT0:
+			subfrm->mutex_mod |= 1 << 7;
+			subfrm->sofs[subfrm->num_sofs++] = MDP_WROT0;
+			break;
+		case MDP_TDSHP0:
+			subfrm->mutex_mod |= 1 << 6;
+			subfrm->sofs[subfrm->num_sofs++] = MDP_TDSHP0;
+			break;
+		case MDP_SCL1:
+			subfrm->mutex_mod |= 1 << 5;
+			subfrm->sofs[subfrm->num_sofs++] = MDP_SCL1;
+			break;
+		case MDP_SCL0:
+			subfrm->mutex_mod |= 1 << 4;
+			subfrm->sofs[subfrm->num_sofs++] = MDP_SCL0;
+			break;
+		case MDP_RDMA0:
+			mutex_id = DISP_MUTEX_MDP_FIRST + 1;
+			subfrm->mutex_mod |= 1 << 2;
+			subfrm->sofs[subfrm->num_sofs++] = MDP_RDMA0;
+			break;
+		case MDP_IMGI:
+			mutex_id = DISP_MUTEX_MDP_FIRST;
+			break;
+		case MDP_WPEI:
+			mutex_id = DISP_MUTEX_MDP_FIRST + 3;
+			break;
+		case MDP_WPEI2:
+			mutex_id = DISP_MUTEX_MDP_FIRST + 4;
+			break;
+		default:
+			break;
+		}
+	}
+
+	subfrm->mutex_id = mutex_id;
+	if (-1 == mutex_id) {
+		mdp_err("No mutex assigned");
+		return -EINVAL;
+	}
+
+	if (subfrm->mutex_mod) {
+		/* Set mutex modules */
+		MM_REG_WRITE(cmd, subsys_id, mm_mutex, MM_MUTEX_MOD,
+			     subfrm->mutex_mod, 0x07FFFFFF);
+		MM_REG_WRITE(cmd, subsys_id, mm_mutex, MM_MUTEX_SOF,
+			     mutex_sof, 0x00000007);
+	}
+	return 0;
+}
+
+int mdp_path_subfrm_run(const struct mdp_path_subfrm *subfrm,
+			const struct mdp_path *path, struct mdp_cmd *cmd)
+{
+	phys_addr_t mm_mutex = path->mdp_dev->mm_mutex.reg_base;
+	s32 mutex_id = subfrm->mutex_id;
+	u8 subsys_id = path->mdp_dev->mm_mutex.subsys_id;
+
+	if (-1 == mutex_id) {
+		mdp_err("Incorrect mutex id");
+		return -EINVAL;
+	}
+
+	if (subfrm->mutex_mod) {
+		int index;
+
+		/* Wait WROT SRAM shared to DISP RDMA */
+		/* Clear SOF event for each engine */
+		for (index = 0; index < subfrm->num_sofs; index++) {
+			switch (subfrm->sofs[index]) {
+			case MDP_RDMA0:
+				MM_REG_CLEAR(cmd, RDMA0_SOF);
+				break;
+			case MDP_TDSHP0:
+				MM_REG_CLEAR(cmd, TDSHP0_SOF);
+				break;
+			case MDP_SCL0:
+				MM_REG_CLEAR(cmd, RSZ0_SOF);
+				break;
+			case MDP_SCL1:
+				MM_REG_CLEAR(cmd, RSZ1_SOF);
+				break;
+			case MDP_WDMA:
+				MM_REG_CLEAR(cmd, WDMA0_SOF);
+				break;
+			case MDP_WROT0:
+#if WROT0_DISP_SRAM_SHARING
+				MM_REG_WAIT_NO_CLEAR(cmd, WROT0_SRAM_READY);
+#endif
+				MM_REG_CLEAR(cmd, WROT0_SOF);
+				break;
+			default:
+				break;
+			}
+		}
+
+		/* Enable the mutex */
+		MM_REG_WRITE(cmd, subsys_id, mm_mutex, MM_MUTEX_EN, 0x1,
+			     0x00000001);
+
+		/* Wait SOF events and clear mutex modules (optional) */
+		for (index = 0; index < subfrm->num_sofs; index++) {
+			switch (subfrm->sofs[index]) {
+			case MDP_RDMA0:
+				MM_REG_WAIT(cmd, RDMA0_SOF);
+				break;
+			case MDP_TDSHP0:
+				MM_REG_WAIT(cmd, TDSHP0_SOF);
+				break;
+			case MDP_SCL0:
+				MM_REG_WAIT(cmd, RSZ0_SOF);
+				break;
+			case MDP_SCL1:
+				MM_REG_WAIT(cmd, RSZ1_SOF);
+				break;
+			case MDP_WDMA:
+				MM_REG_WAIT(cmd, WDMA0_SOF);
+				break;
+			case MDP_WROT0:
+				MM_REG_WAIT(cmd, WROT0_SOF);
+				break;
+			default:
+				break;
+			}
+		}
+	}
+	return 0;
+}
+
+static int mdp_path_config_subfrm(struct mdp_cmd *cmd, struct mdp_path *path,
+				  u32 count)
+{
+	struct mdp_path_subfrm subfrm;
+	const struct img_config *config = path->config;
+	const struct img_mmsys_ctrl *ctrl = &config->ctrls[count];
+	const struct img_mux *set;
+	struct mdp_comp_ctx *ctx;
+	phys_addr_t mmsys = path->mdp_dev->mmsys.reg_base;
+	int index, ret;
+	u8 subsys_id = path->mdp_dev->mmsys.subsys_id;
+
+	/* Acquire components */
+	ret = mdp_path_subfrm_require(&subfrm, path, cmd, count);
+	if (ret)
+		return ret;
+	/* Enable mux settings */
+	for (index = 0; index < ctrl->num_sets; index++) {
+		set = &ctrl->sets[index];
+		MM_REG_WRITE_MASK(cmd, subsys_id, mmsys, set->reg, set->value,
+				  0xFFFFFFFF);
+	}
+	/* Config sub-frame information */
+	for (index = (config->num_components - 1); index >= 0; index--) {
+		ctx = &path->comps[index];
+		if (is_output_disable(ctx->param, count))
+			continue;
+		ret = call_op(ctx, config_subfrm, cmd, count);
+		if (ret)
+			return ret;
+	}
+	/* Run components */
+	ret = mdp_path_subfrm_run(&subfrm, path, cmd);
+	if (ret)
+		return ret;
+	/* Wait components done */
+	for (index = 0; index < config->num_components; index++) {
+		ctx = &path->comps[index];
+		if (is_output_disable(ctx->param, count))
+			continue;
+		ret = call_op(ctx, wait_comp_event, cmd);
+		if (ret)
+			return ret;
+	}
+	/* Advance to the next sub-frame */
+	for (index = 0; index < config->num_components; index++) {
+		ctx = &path->comps[index];
+		ret = call_op(ctx, advance_subfrm, cmd, count);
+		if (ret)
+			return ret;
+	}
+	/* Disable mux settings */
+	for (index = 0; index < ctrl->num_sets; index++) {
+		set = &ctrl->sets[index];
+		MM_REG_WRITE_MASK(cmd, subsys_id, mmsys, set->reg, 0,
+				  0xFFFFFFFF);
+	}
+	return 0;
+}
+
+static int mdp_path_config(struct mdp_dev *mdp, struct mdp_cmd *cmd,
+			   struct mdp_path *path)
+{
+	const struct img_config *config = path->config;
+	struct mdp_comp_ctx *ctx;
+	int index, count, ret;
+
+	for (index = 0; index < config->num_components; index++) {
+		ret = mdp_comp_ctx_init(mdp, &path->comps[index],
+					&config->components[index],
+					path->param);
+		if (ret)
+			return ret;
+	}
+
+	/* Config path frame */
+	/* Reset components */
+	for (index = 0; index < config->num_components; index++) {
+		ctx = &path->comps[index];
+		ret = call_op(ctx, init_comp, cmd);
+		if (ret)
+			return ret;
+	}
+	/* Config frame mode */
+	for (index = 0; index < config->num_components; index++) {
+		const struct v4l2_rect *compose =
+			path->composes[ctx->param->outputs[0]];
+
+		ctx = &path->comps[index];
+		ret = call_op(ctx, config_frame, cmd, compose);
+		if (ret)
+			return ret;
+	}
+
+	/* Config path sub-frames */
+	for (count = 0; count < config->num_subfrms; count++) {
+		ret = mdp_path_config_subfrm(cmd, path, count);
+		if (ret)
+			return ret;
+	}
+	/* Post processing information */
+	for (index = 0; index < config->num_components; index++) {
+		ctx = &path->comps[index];
+		ret = call_op(ctx, post_process, cmd);
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+
+void mdp_handle_cmdq_callback(struct cmdq_cb_data data)
+{
+	struct mdp_cmdq_cb_param *cb_param;
+
+	if (!data.data) {
+		mdp_err("%s:no callback data\n", __func__);
+		return;
+	}
+
+	cb_param = (struct mdp_cmdq_cb_param *)data.data;
+	if (cb_param->user_cmdq_cb) {
+		struct cmdq_cb_data user_cb_data;
+
+		user_cb_data.sta = data.sta;
+		user_cb_data.data = cb_param->user_cb_data;
+		cb_param->user_cmdq_cb(user_cb_data);
+	}
+
+	cmdq_pkt_destroy(cb_param->pkt);
+	kfree(cb_param);
+}
+
+int mdp_cmdq_send(struct mdp_dev *mdp, struct mdp_cmdq_param *param)
+{
+	struct mdp_cmd cmd;
+	struct mdp_path path;
+	int i, ret;
+
+	cmd.pkt = cmdq_pkt_create(mdp->cmdq_clt, SZ_16K);
+	if (IS_ERR(cmd.pkt))
+		return PTR_ERR(cmd.pkt);
+	cmd.event = &mdp->event[0];
+
+	path.mdp_dev = mdp;
+	path.config = param->config;
+	path.param = param->param;
+	for (i = 0; i < param->param->num_outputs; i++) {
+		path.bounds[i].left = 0;
+		path.bounds[i].top = 0;
+		path.bounds[i].width =
+			param->param->outputs[i].buffer.format.width;
+		path.bounds[i].height =
+			param->param->outputs[i].buffer.format.height;
+		path.composes[i] = param->composes[i] ?
+			param->composes[i] : &path.bounds[i];
+	}
+	ret = mdp_path_config(mdp, &cmd, &path);
+	if (ret)
+		return ret;
+
+	// TODO: engine conflict dispatch
+	for (i = 0; i < param->config->num_components; i++)
+		mdp_comp_clock_on(&mdp->pdev->dev, path.comps[i].comp);
+
+	if (param->wait) {
+		ret = cmdq_pkt_flush(cmd.pkt);
+		cmdq_pkt_destroy(cmd.pkt);
+		for (i = 0; i < param->config->num_components; i++)
+			mdp_comp_clock_off(&mdp->pdev->dev, path.comps[i].comp);
+	} else {
+		struct mdp_cmdq_cb_param *cb_param =
+		kzalloc(sizeof(struct mdp_cmdq_cb_param), GFP_KERNEL);
+
+		cb_param->user_cmdq_cb = param->cmdq_cb;
+		cb_param->user_cb_data = param->cb_data;
+		cb_param->pkt = cmd.pkt;
+
+		ret = cmdq_pkt_flush_async(cmd.pkt,
+					   mdp_handle_cmdq_callback,
+					   (void *)cb_param);
+		// TODO: destroy & clock-off after callback
+	}
+	return ret;
+}
+
+int mdp_cmdq_sendtask(struct platform_device *pdev, struct img_config *config,
+		      struct img_ipi_frameparam *param,
+		      struct v4l2_rect *compose, unsigned int wait,
+		      void (*cmdq_cb)(struct cmdq_cb_data data), void *cb_data)
+{
+	struct mdp_dev *mdp = platform_get_drvdata(pdev);
+	struct mdp_cmdq_param task = {
+		.config = config,
+		.param = param,
+		.composes[0] = compose,
+		.wait = wait,
+		.cmdq_cb = cmdq_cb,
+		.cb_data = cb_data,
+	};
+
+	return mdp_cmdq_send(mdp, &task);
+}
+EXPORT_SYMBOL_GPL(mdp_cmdq_sendtask);
+
diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-cmdq.h b/drivers/media/platform/mtk-mdp3/mtk-mdp3-cmdq.h
new file mode 100644
index 000000000000..b61ab1ac4325
--- /dev/null
+++ b/drivers/media/platform/mtk-mdp3/mtk-mdp3-cmdq.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Ping-Hsun Wu <ping-hsun.wu@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_MDP3_CMDQ_H__
+#define __MTK_MDP3_CMDQ_H__
+
+#include <linux/platform_device.h>
+#include <linux/videodev2.h>
+#include <linux/soc/mediatek/mtk-cmdq.h>
+#include "mtk-img-ipi.h"
+
+struct platform_device *mdp_get_plat_device(struct platform_device *pdev);
+
+int mdp_cmdq_sendtask(struct platform_device *pdev, struct img_config *config,
+		      struct img_ipi_frameparam *param,
+		      struct v4l2_rect *compose, unsigned int wait,
+		      void (*cmdq_cb)(struct cmdq_cb_data data), void *cb_data);
+
+struct mdp_cmd {
+	struct cmdq_pkt *pkt;
+	s32 *event;
+};
+
+struct mdp_cmdq_param {
+	struct img_config	*config;
+	struct img_ipi_frameparam *param;
+	const struct v4l2_rect	*composes[IMG_MAX_HW_OUTPUTS];
+	unsigned int		wait;
+
+	void (*cmdq_cb)(struct cmdq_cb_data data);
+	void *cb_data;
+};
+
+struct mdp_cmdq_cb_param {
+	void (*user_cmdq_cb)(struct cmdq_cb_data data);
+	void *user_cb_data;
+	struct cmdq_pkt *pkt;
+};
+
+struct mdp_dev;
+
+int mdp_cmdq_send(struct mdp_dev *mdp, struct mdp_cmdq_param *param);
+
+#endif  /* __MTK_MDP3_CMDQ_H__ */
+
diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-comp.c b/drivers/media/platform/mtk-mdp3/mtk-mdp3-comp.c
new file mode 100644
index 000000000000..3dc36915a744
--- /dev/null
+++ b/drivers/media/platform/mtk-mdp3/mtk-mdp3-comp.c
@@ -0,0 +1,1325 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Ping-Hsun Wu <ping-hsun.wu@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/clk.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/pm_runtime.h>
+#include "mtk-mdp3-comp.h"
+#include "mtk-mdp3-core.h"
+#include "mtk-mdp3-regs.h"
+
+#include "mdp-platform.h"
+#include "mmsys_config.h"
+#include "mdp_reg_rdma.h"
+#include "mdp_reg_ccorr.h"
+#include "mdp_reg_rsz.h"
+#include "mdp_reg_wrot.h"
+#include "mdp_reg_wdma.h"
+#include "isp_reg.h"
+
+static s64 get_comp_flag(const struct mdp_comp_ctx *ctx)
+{
+#if RDMA0_RSZ1_SRAM_SHARING
+	if (ctx->comp->id == MDP_RDMA0)
+		return (1 << MDP_RDMA0) | (1 << MDP_SCL1);
+#endif
+	return 1 << ctx->comp->id;
+}
+
+static int init_rdma(struct mdp_comp_ctx *ctx, struct mdp_cmd *cmd)
+{
+	phys_addr_t base = ctx->comp->reg_base;
+	u8 subsys_id = ctx->comp->subsys_id;
+#if RDMA0_RSZ1_SRAM_SHARING
+	struct mdp_comp *prz1 = ctx->comp->mdp_dev->comp[MDP_SCL1];
+
+	/* Disable RSZ1 */
+	if (ctx->comp->id == MDP_RDMA0 && prz1)
+		MM_REG_WRITE(cmd, subsys_id, prz1->reg_base, PRZ_ENABLE,
+			     0x00000000, 0x00000001);
+#endif
+	/* Reset RDMA */
+	MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_RESET, 0x00000001,
+		     0x00000001);
+	MM_REG_POLL(cmd, subsys_id, base, MDP_RDMA_MON_STA_1, 0x00000100,
+		    0x00000100);
+	MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_RESET, 0x00000000,
+		     0x00000001);
+	return 0;
+}
+
+static int config_rdma_frame(struct mdp_comp_ctx *ctx, struct mdp_cmd *cmd,
+			     const struct v4l2_rect *compose)
+{
+	const struct mdp_rdma_data *rdma = &ctx->param->rdma;
+	u32 colorformat = ctx->input->buffer.format.colorformat;
+	bool block10bit = MDP_COLOR_IS_10BIT_PACKED(colorformat);
+	bool en_ufo = MDP_COLOR_IS_UFP(colorformat);
+	phys_addr_t base = ctx->comp->reg_base;
+	u8 subsys_id = ctx->comp->subsys_id;
+
+#if RDMA_SUPPORT_10BIT
+	if (block10bit)
+		MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_RESV_DUMMY_0,
+			     0x00000007, 0x00000007);
+	else
+		MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_RESV_DUMMY_0,
+			     0x00000000, 0x00000007);
+#endif
+
+	/* Setup smi control */
+	MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_GMCIF_CON,
+		     (1 <<  0) +
+		     (7 <<  4) + //burst type to 8
+		     (1 << 16),  //enable pre-ultra
+		     0x00030071);
+
+	/* Setup source frame info */
+	MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_CON, rdma->src_ctrl,
+		     0x03C8FE0F);
+#if RDMA_SUPPORT_10BIT
+	if (en_ufo) {
+		/* Setup source buffer base */
+		MM_REG_WRITE(cmd, subsys_id,
+			     base, MDP_RDMA_UFO_DEC_LENGTH_BASE_Y,
+			     rdma->ufo_dec_y, 0xFFFFFFFF);
+		MM_REG_WRITE(cmd, subsys_id,
+			     base, MDP_RDMA_UFO_DEC_LENGTH_BASE_C,
+			     rdma->ufo_dec_c, 0xFFFFFFFF);
+		/* Set 10bit source frame pitch */
+		if (block10bit)
+			MM_REG_WRITE(cmd, subsys_id,
+				     base, MDP_RDMA_MF_BKGD_SIZE_IN_PXL,
+				     rdma->mf_bkgd_in_pxl, 0x001FFFFF);
+	}
+#endif
+	MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_CON, rdma->control,
+		     0x00001110);
+	/* Setup source buffer base */
+	MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_BASE_0, rdma->iova[0],
+		     0xFFFFFFFF);
+	MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_BASE_1, rdma->iova[1],
+		     0xFFFFFFFF);
+	MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_BASE_2, rdma->iova[2],
+		     0xFFFFFFFF);
+	/* Setup source buffer end */
+	MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_END_0,
+		     rdma->iova_end[0], 0xFFFFFFFF);
+	MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_END_1,
+		     rdma->iova_end[1], 0xFFFFFFFF);
+	MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_END_2,
+		     rdma->iova_end[2], 0xFFFFFFFF);
+	/* Setup source frame pitch */
+	MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_MF_BKGD_SIZE_IN_BYTE,
+		     rdma->mf_bkgd, 0x001FFFFF);
+	MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SF_BKGD_SIZE_IN_BYTE,
+		     rdma->sf_bkgd, 0x001FFFFF);
+	/* Setup color transform */
+	MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_TRANSFORM_0,
+		     rdma->transform, 0x0F110000);
+
+	return 0;
+}
+
+static int config_rdma_subfrm(struct mdp_comp_ctx *ctx,
+			      struct mdp_cmd *cmd, u32 index)
+{
+	const struct mdp_rdma_subfrm *subfrm = &ctx->param->rdma.subfrms[index];
+	const struct img_comp_subfrm *csf = &ctx->param->subfrms[index];
+	u32 colorformat = ctx->input->buffer.format.colorformat;
+	bool block10bit = MDP_COLOR_IS_10BIT_PACKED(colorformat);
+	bool en_ufo = MDP_COLOR_IS_UFP(colorformat);
+	phys_addr_t base = ctx->comp->reg_base;
+	u8 subsys_id = ctx->comp->subsys_id;
+
+	/* Enable RDMA */
+	MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_EN, 0x00000001,
+		     0x00000001);
+
+	/* Set Y pixel offset */
+	MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_OFFSET_0,
+		     subfrm->offset[0], 0xFFFFFFFF);
+#if RDMA_SUPPORT_10BIT
+	/* Set 10bit UFO mode */
+	if (block10bit && en_ufo)
+		MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_OFFSET_0_P,
+			     subfrm->offset_0_p, 0xFFFFFFFF);
+#endif
+	/* Set U pixel offset */
+	MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_OFFSET_1,
+		     subfrm->offset[1], 0xFFFFFFFF);
+	/* Set V pixel offset */
+	MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_OFFSET_2,
+		     subfrm->offset[2], 0xFFFFFFFF);
+	/* Set source size */
+	MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_MF_SRC_SIZE, subfrm->src,
+		     0x1FFF1FFF);
+	/* Set target size */
+	MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_MF_CLIP_SIZE,
+		     subfrm->clip, 0x1FFF1FFF);
+	/* Set crop offset */
+	MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_MF_OFFSET_1,
+		     subfrm->clip_ofst, 0x003F001F);
+
+#if RDMA_UPSAMPLE_REPEAT_ONLY
+	if ((csf->in.right - csf->in.left + 1) > 320)
+		MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_RESV_DUMMY_0,
+			     0x00000004, 0x00000004);
+#endif
+
+	return 0;
+}
+
+static int wait_rdma_event(struct mdp_comp_ctx *ctx, struct mdp_cmd *cmd)
+{
+	phys_addr_t base = ctx->comp->reg_base;
+	u8 subsys_id = ctx->comp->subsys_id;
+
+	if (ctx->comp->alias_id == 0)
+		MM_REG_WAIT(cmd, RDMA0_DONE);
+	else
+		MM_REG_WAIT(cmd, RDMA1_DONE);
+	/* Disable RDMA */
+	MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_EN, 0x00000000,
+		     0x00000001);
+	return 0;
+}
+
+static const struct mdp_comp_ops rdma_ops = {
+	.get_comp_flag = get_comp_flag,
+	.init_comp = init_rdma,
+	.config_frame = config_rdma_frame,
+	.config_subfrm = config_rdma_subfrm,
+	/* .reconfig_frame = reconfig_rdma_frame, */
+	/* .reconfig_subfrms = reconfig_rdma_subfrms, */
+	.wait_comp_event = wait_rdma_event,
+	.advance_subfrm = NULL,
+	.post_process = NULL,
+};
+
+static int init_rsz(struct mdp_comp_ctx *ctx, struct mdp_cmd *cmd)
+{
+	phys_addr_t base = ctx->comp->reg_base;
+	u8 subsys_id = ctx->comp->subsys_id;
+
+	/* Reset RSZ */
+	MM_REG_WRITE(cmd, subsys_id, base, PRZ_ENABLE, 0x00010000,
+		     0x00010000);
+	MM_REG_WRITE(cmd, subsys_id, base, PRZ_ENABLE, 0x00000000,
+		     0x00010000);
+	/* Enable RSZ */
+	MM_REG_WRITE(cmd, subsys_id, base, PRZ_ENABLE, 0x00000001,
+		     0x00000001);
+	return 0;
+}
+
+static int config_rsz_frame(struct mdp_comp_ctx *ctx, struct mdp_cmd *cmd,
+			    const struct v4l2_rect *compose)
+{
+	const struct mdp_rsz_data *rsz = &ctx->param->rsz;
+	phys_addr_t base = ctx->comp->reg_base;
+	u8 subsys_id = ctx->comp->subsys_id;
+
+	if (ctx->param->frame.bypass) {
+		/* Disable RSZ */
+		MM_REG_WRITE(cmd, subsys_id, base, PRZ_ENABLE, 0x00000000,
+			     0x00000001);
+		return 0;
+	}
+
+	MM_REG_WRITE(cmd, subsys_id, base, PRZ_CONTROL_1, rsz->control1,
+		     0x03FFFDF3);
+	MM_REG_WRITE(cmd, subsys_id, base, PRZ_CONTROL_2, rsz->control2,
+		     0x0FFFC290);
+	MM_REG_WRITE(cmd, subsys_id, base, PRZ_HORIZONTAL_COEFF_STEP,
+		     rsz->coeff_step_x, 0x007FFFFF);
+	MM_REG_WRITE(cmd, subsys_id, base, PRZ_VERTICAL_COEFF_STEP,
+		     rsz->coeff_step_y, 0x007FFFFF);
+	return 0;
+}
+
+static int config_rsz_subfrm(struct mdp_comp_ctx *ctx,
+			     struct mdp_cmd *cmd, u32 index)
+{
+	const struct mdp_rsz_subfrm *subfrm = &ctx->param->rsz.subfrms[index];
+	const struct img_comp_subfrm *csf = &ctx->param->subfrms[index];
+	phys_addr_t base = ctx->comp->reg_base;
+	u8 subsys_id = ctx->comp->subsys_id;
+
+	MM_REG_WRITE(cmd, subsys_id, base, PRZ_CONTROL_2, subfrm->control2,
+		     0x00003800);
+	MM_REG_WRITE(cmd, subsys_id, base, PRZ_INPUT_IMAGE, subfrm->src,
+		     0xFFFFFFFF);
+#if RSZ_DISABLE_DCM_SMALL_TILE
+	if ((csf->in.right - csf->in.left + 1) <= 16)
+		MM_REG_WRITE(cmd, subsys_id, base, PRZ_CONTROL_1, 1 << 27,
+			     1 << 27);
+#endif
+	MM_REG_WRITE(cmd, subsys_id, base, PRZ_LUMA_HORIZONTAL_INTEGER_OFFSET,
+		     csf->luma.left, 0x0000FFFF);
+	MM_REG_WRITE(cmd, subsys_id,
+		     base, PRZ_LUMA_HORIZONTAL_SUBPIXEL_OFFSET,
+		     csf->luma.left_subpix, 0x001FFFFF);
+	MM_REG_WRITE(cmd, subsys_id, base, PRZ_LUMA_VERTICAL_INTEGER_OFFSET,
+		     csf->luma.top, 0x0000FFFF);
+	MM_REG_WRITE(cmd, subsys_id, base, PRZ_LUMA_VERTICAL_SUBPIXEL_OFFSET,
+		     csf->luma.top_subpix, 0x001FFFFF);
+	MM_REG_WRITE(cmd, subsys_id,
+		     base, PRZ_CHROMA_HORIZONTAL_INTEGER_OFFSET,
+		     csf->chroma.left, 0x0000FFFF);
+	MM_REG_WRITE(cmd, subsys_id,
+		     base, PRZ_CHROMA_HORIZONTAL_SUBPIXEL_OFFSET,
+		     csf->chroma.left_subpix, 0x001FFFFF);
+
+	MM_REG_WRITE(cmd, subsys_id, base, PRZ_OUTPUT_IMAGE, subfrm->clip,
+		     0xFFFFFFFF);
+
+	return 0;
+}
+
+static int advance_rsz_subfrm(struct mdp_comp_ctx *ctx,
+			      struct mdp_cmd *cmd, u32 index)
+{
+#if RSZ_DISABLE_DCM_SMALL_TILE
+	const struct img_comp_subfrm *csf = &ctx->param->subfrms[index];
+	phys_addr_t base = ctx->comp->reg_base;
+	u8 subsys_id = ctx->comp->subsys_id;
+
+	if ((csf->in.right - csf->in.left + 1) <= 16)
+		MM_REG_WRITE(cmd, subsys_id, base, PRZ_CONTROL_1, 0, 1 << 27);
+#endif
+	return 0;
+}
+
+static const struct mdp_comp_ops rsz_ops = {
+	.get_comp_flag = get_comp_flag,
+	.init_comp = init_rsz,
+	.config_frame = config_rsz_frame,
+	.config_subfrm = config_rsz_subfrm,
+	/* .reconfig_frame = NULL, */
+	/* .reconfig_subfrms = NULL, */
+	.wait_comp_event = NULL,
+	.advance_subfrm = advance_rsz_subfrm,
+	.post_process = NULL,
+};
+
+static int init_wrot(struct mdp_comp_ctx *ctx, struct mdp_cmd *cmd)
+{
+	phys_addr_t base = ctx->comp->reg_base;
+	u8 subsys_id = ctx->comp->subsys_id;
+
+#if WROT_FILTER_CONSTRAINT
+	/* Wait WROT SRAM shared to DISP RDMA */
+	if (ctx->comp->alias_id == 0)
+		MM_REG_WAIT_NO_CLEAR(cmd, WROT0_SRAM_READY);
+	else
+		MM_REG_WAIT_NO_CLEAR(cmd, WROT1_SRAM_READY);
+#endif
+	/* Reset WROT */
+	MM_REG_WRITE(cmd, subsys_id, base, VIDO_SOFT_RST, 0x01, 0x00000001);
+	MM_REG_POLL(cmd, subsys_id, base, VIDO_SOFT_RST_STAT, 0x01,
+		    0x00000001);
+	MM_REG_WRITE(cmd, subsys_id, base, VIDO_SOFT_RST, 0x00, 0x00000001);
+	MM_REG_POLL(cmd, subsys_id, base, VIDO_SOFT_RST_STAT, 0x00,
+		    0x00000001);
+	return 0;
+}
+
+static int config_wrot_frame(struct mdp_comp_ctx *ctx, struct mdp_cmd *cmd,
+			     const struct v4l2_rect *compose)
+{
+	const struct mdp_wrot_data *wrot = &ctx->param->wrot;
+	phys_addr_t base = ctx->comp->reg_base;
+	u8 subsys_id = ctx->comp->subsys_id;
+
+	/* Write frame base address */
+	MM_REG_WRITE(cmd, subsys_id, base, VIDO_BASE_ADDR, wrot->iova[0],
+		     0xFFFFFFFF);
+	MM_REG_WRITE(cmd, subsys_id, base, VIDO_BASE_ADDR_C, wrot->iova[1],
+		     0xFFFFFFFF);
+	MM_REG_WRITE(cmd, subsys_id, base, VIDO_BASE_ADDR_V, wrot->iova[2],
+		     0xFFFFFFFF);
+	/* Write frame related registers */
+	MM_REG_WRITE(cmd, subsys_id, base, VIDO_CTRL, wrot->control,
+		     0xF131510F);
+	/* Write frame Y pitch */
+	MM_REG_WRITE(cmd, subsys_id, base, VIDO_STRIDE, wrot->stride[0],
+		     0x0000FFFF);
+	/* Write frame UV pitch */
+	MM_REG_WRITE(cmd, subsys_id, base, VIDO_STRIDE_C, wrot->stride[1],
+		     0x0000FFFF);
+	MM_REG_WRITE(cmd, subsys_id, base, VIDO_STRIDE_V, wrot->stride[2],
+		     0x0000FFFF);
+	/* Write matrix control */
+	MM_REG_WRITE(cmd, subsys_id, base, VIDO_MAT_CTRL, wrot->mat_ctrl,
+		     0x000000F3);
+
+	/* Set the fixed ALPHA as 0xFF */
+	MM_REG_WRITE(cmd, subsys_id, base, VIDO_DITHER, 0xFF000000,
+		     0xFF000000);
+	/* Set VIDO_EOL_SEL */
+	MM_REG_WRITE(cmd, subsys_id, base, VIDO_RSV_1, 0x80000000,
+		     0x80000000);
+	/* Set VIDO_FIFO_TEST */
+	if (wrot->fifo_test != 0)
+		MM_REG_WRITE(cmd, subsys_id, base, VIDO_FIFO_TEST,
+			     wrot->fifo_test, 0x00000FFF);
+
+#if WROT_FILTER_CONSTRAINT
+	/* Filter enable */
+	MM_REG_WRITE(cmd, subsys_id, base, VIDO_MAIN_BUF_SIZE, wrot->filter,
+		     0x00000077);
+#endif
+
+	return 0;
+}
+
+static int config_wrot_subfrm(struct mdp_comp_ctx *ctx,
+			      struct mdp_cmd *cmd, u32 index)
+{
+	const struct mdp_wrot_subfrm *subfrm = &ctx->param->wrot.subfrms[index];
+	phys_addr_t base = ctx->comp->reg_base;
+	u8 subsys_id = ctx->comp->subsys_id;
+
+	/* Write Y pixel offset */
+	MM_REG_WRITE(cmd, subsys_id, base, VIDO_OFST_ADDR,
+		     subfrm->offset[0], 0x0FFFFFFF);
+	/* Write U pixel offset */
+	MM_REG_WRITE(cmd, subsys_id, base, VIDO_OFST_ADDR_C,
+		     subfrm->offset[1], 0x0FFFFFFF);
+	/* Write V pixel offset */
+	MM_REG_WRITE(cmd, subsys_id, base, VIDO_OFST_ADDR_V,
+		     subfrm->offset[2], 0x0FFFFFFF);
+	/* Write source size */
+	MM_REG_WRITE(cmd, subsys_id, base, VIDO_IN_SIZE, subfrm->src,
+		     0x1FFF1FFF);
+	/* Write target size */
+	MM_REG_WRITE(cmd, subsys_id, base, VIDO_TAR_SIZE, subfrm->clip,
+		     0x1FFF1FFF);
+	MM_REG_WRITE(cmd, subsys_id, base, VIDO_CROP_OFST, subfrm->clip_ofst,
+		     0x1FFF1FFF);
+
+	MM_REG_WRITE(cmd, subsys_id, base, VIDO_MAIN_BUF_SIZE,
+		     subfrm->main_buf, 0x1FFF7F00);
+
+	/* Enable WROT */
+	MM_REG_WRITE(cmd, subsys_id, base, VIDO_ROT_EN, 0x01, 0x00000001);
+
+	return 0;
+}
+
+static int wait_wrot_event(struct mdp_comp_ctx *ctx, struct mdp_cmd *cmd)
+{
+	phys_addr_t base = ctx->comp->reg_base;
+	u8 subsys_id = ctx->comp->subsys_id;
+
+	if (ctx->comp->alias_id == 0)
+		MM_REG_WAIT(cmd, WROT0_DONE);
+	else
+		MM_REG_WAIT(cmd, WROT1_DONE);
+#if WROT_FILTER_CONSTRAINT
+	/* Filter disable */
+	MM_REG_WRITE(cmd, subsys_id, base, VIDO_MAIN_BUF_SIZE,
+		     (0 << 4) +
+		(0 << 0),
+		0x00000077);
+#endif
+	/* Disable WROT */
+	MM_REG_WRITE(cmd, subsys_id, base, VIDO_ROT_EN, 0x00, 0x00000001);
+
+	return 0;
+}
+
+static const struct mdp_comp_ops wrot_ops = {
+	.get_comp_flag = get_comp_flag,
+	.init_comp = init_wrot,
+	.config_frame = config_wrot_frame,
+	.config_subfrm = config_wrot_subfrm,
+	/* .reconfig_frame = reconfig_wrot_frame, */
+	/* .reconfig_subfrms = reconfig_wrot_subfrms, */
+	.wait_comp_event = wait_wrot_event,
+	.advance_subfrm = NULL,
+	.post_process = NULL,
+};
+
+static int init_wdma(struct mdp_comp_ctx *ctx, struct mdp_cmd *cmd)
+{
+	phys_addr_t base = ctx->comp->reg_base;
+	u8 subsys_id = ctx->comp->subsys_id;
+
+	/* Reset WDMA */
+	MM_REG_WRITE(cmd, subsys_id, base, WDMA_RST, 0x1, 0x00000001);
+	MM_REG_POLL(cmd, subsys_id, base, WDMA_FLOW_CTRL_DBG, 0x01,
+		    0x00000001);
+	MM_REG_WRITE(cmd, subsys_id, base, WDMA_RST, 0x0, 0x00000001);
+	return 0;
+}
+
+static int config_wdma_frame(struct mdp_comp_ctx *ctx, struct mdp_cmd *cmd,
+			     const struct v4l2_rect *compose)
+{
+	const struct mdp_wdma_data *wdma = &ctx->param->wdma;
+	phys_addr_t base = ctx->comp->reg_base;
+	u8 subsys_id = ctx->comp->subsys_id;
+
+	MM_REG_WRITE(cmd, subsys_id, base, WDMA_BUF_CON2, 0x10101050,
+		     0xFFFFFFFF);
+
+	/* Setup frame information */
+	MM_REG_WRITE(cmd, subsys_id, base, WDMA_CFG, wdma->wdma_cfg,
+		     0x0F01B8F0);
+	/* Setup frame base address */
+	MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_ADDR,   wdma->iova[0],
+		     0xFFFFFFFF);
+	MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_U_ADDR, wdma->iova[1],
+		     0xFFFFFFFF);
+	MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_V_ADDR, wdma->iova[2],
+		     0xFFFFFFFF);
+	/* Setup Y pitch */
+	MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_W_IN_BYTE,
+		     wdma->w_in_byte, 0x0000FFFF);
+	/* Setup UV pitch */
+	MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_UV_PITCH,
+		     wdma->uv_stride, 0x0000FFFF);
+	/* Set the fixed ALPHA as 0xFF */
+	MM_REG_WRITE(cmd, subsys_id, base, WDMA_ALPHA, 0x800000FF,
+		     0x800000FF);
+
+	return 0;
+}
+
+static int config_wdma_subfrm(struct mdp_comp_ctx *ctx,
+			      struct mdp_cmd *cmd, u32 index)
+{
+	const struct mdp_wdma_subfrm *subfrm = &ctx->param->wdma.subfrms[index];
+	phys_addr_t base = ctx->comp->reg_base;
+	u8 subsys_id = ctx->comp->subsys_id;
+
+	/* Write Y pixel offset */
+	MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_ADDR_OFFSET,
+		     subfrm->offset[0], 0x0FFFFFFF);
+	/* Write U pixel offset */
+	MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_U_ADDR_OFFSET,
+		     subfrm->offset[1], 0x0FFFFFFF);
+	/* Write V pixel offset */
+	MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_V_ADDR_OFFSET,
+		     subfrm->offset[2], 0x0FFFFFFF);
+	/* Write source size */
+	MM_REG_WRITE(cmd, subsys_id, base, WDMA_SRC_SIZE, subfrm->src,
+		     0x3FFF3FFF);
+	/* Write target size */
+	MM_REG_WRITE(cmd, subsys_id, base, WDMA_CLIP_SIZE, subfrm->clip,
+		     0x3FFF3FFF);
+	/* Write clip offset */
+	MM_REG_WRITE(cmd, subsys_id, base, WDMA_CLIP_COORD, subfrm->clip_ofst,
+		     0x3FFF3FFF);
+
+	/* Enable WDMA */
+	MM_REG_WRITE(cmd, subsys_id, base, WDMA_EN, 0x01, 0x00000001);
+
+	return 0;
+}
+
+static int wait_wdma_event(struct mdp_comp_ctx *ctx, struct mdp_cmd *cmd)
+{
+	phys_addr_t base = ctx->comp->reg_base;
+	u8 subsys_id = ctx->comp->subsys_id;
+
+	MM_REG_WAIT(cmd, WDMA0_DONE);
+	/* Disable WDMA */
+	MM_REG_WRITE(cmd, subsys_id, base, WDMA_EN, 0x00, 0x00000001);
+	return 0;
+}
+
+static const struct mdp_comp_ops wdma_ops = {
+	.get_comp_flag = get_comp_flag,
+	.init_comp = init_wdma,
+	.config_frame = config_wdma_frame,
+	.config_subfrm = config_wdma_subfrm,
+	/* .reconfig_frame = reconfig_wdma_frame, */
+	/* .reconfig_subfrms = reconfig_wdma_subfrms, */
+	.wait_comp_event = wait_wdma_event,
+	.advance_subfrm = NULL,
+	.post_process = NULL,
+};
+
+static int init_ccorr(struct mdp_comp_ctx *ctx, struct mdp_cmd *cmd)
+{
+	phys_addr_t base = ctx->comp->reg_base;
+	u8 subsys_id = ctx->comp->subsys_id;
+
+	/* CCORR enable */
+	MM_REG_WRITE(cmd, subsys_id, base, MDP_CCORR_EN, 0x1, 0x1);
+	/* Relay mode */
+	MM_REG_WRITE(cmd, subsys_id, base, MDP_CCORR_CFG, 0x1, 0x1);
+	return 0;
+}
+
+static int config_ccorr_frame(struct mdp_comp_ctx *ctx, struct mdp_cmd *cmd,
+			      const struct v4l2_rect *compose)
+{
+	/* Disabled function */
+	return 0;
+}
+
+static int config_ccorr_subfrm(struct mdp_comp_ctx *ctx,
+			       struct mdp_cmd *cmd, u32 index)
+{
+	const struct img_comp_subfrm *csf = &ctx->param->subfrms[index];
+	phys_addr_t base = ctx->comp->reg_base;
+	u8 subsys_id = ctx->comp->subsys_id;
+	u32 hsize, vsize;
+
+	hsize = csf->in.right - csf->in.left + 1;
+	vsize = csf->in.bottom - csf->in.top + 1;
+	MM_REG_WRITE(cmd, subsys_id, base, MDP_CCORR_SIZE,
+		     (hsize << 16) + (vsize <<  0), 0x1FFF1FFF);
+	return 0;
+}
+
+static const struct mdp_comp_ops ccorr_ops = {
+	.get_comp_flag = get_comp_flag,
+	.init_comp = init_ccorr,
+	.config_frame = config_ccorr_frame,
+	.config_subfrm = config_ccorr_subfrm,
+	/* .reconfig_frame = NULL, */
+	/* .reconfig_subfrms = NULL, */
+	.wait_comp_event = NULL,
+	.advance_subfrm = NULL,
+	.post_process = NULL,
+};
+
+static int init_isp(struct mdp_comp_ctx *ctx, struct mdp_cmd *cmd)
+{
+	const struct isp_data *isp = &ctx->param->isp;
+	phys_addr_t mmsys = ctx->comp->mdp_dev->mmsys.reg_base;
+	u8 subsys_id = ctx->comp->subsys_id;
+
+	/* Direct link */
+	if (isp->dl_flags & (1 << MDP_CAMIN)) {
+		mdp_dbg(2, "SW_RST ASYNC");
+		/* Reset MDP_DL_ASYNC_TX */
+		/* Bit  3: MDP_DL_ASYNC_TX / MDP_RELAY */
+		MM_REG_WRITE(cmd, subsys_id, mmsys, MMSYS_SW0_RST_B, 0x0,
+			     0x00000008);
+		MM_REG_WRITE(cmd, subsys_id, mmsys, MMSYS_SW0_RST_B, 1 << 3,
+			     0x00000008);
+		/* Reset MDP_DL_ASYNC_RX */
+		/* Bit  10: MDP_DL_ASYNC_RX */
+		MM_REG_WRITE(cmd, subsys_id, mmsys, MMSYS_SW1_RST_B, 0x0,
+			     0x00000400);
+		MM_REG_WRITE(cmd, subsys_id, mmsys, MMSYS_SW1_RST_B, 1 << 10,
+			     0x00000400);
+
+		/* Enable sof mode */
+		MM_REG_WRITE(cmd, subsys_id, mmsys, ISP_RELAY_CFG_WD, 0 << 31,
+			     0x80000000);
+	}
+
+	if (isp->dl_flags & (1 << MDP_CAMIN2)) {
+		mdp_dbg(2, "SW_RST ASYNC2");
+		/* Reset MDP_DL_ASYNC2_TX */
+		/* Bit  4: MDP_DL_ASYNC2_TX / MDP_RELAY2 */
+		MM_REG_WRITE(cmd, subsys_id, mmsys, MMSYS_SW0_RST_B, 0x0,
+			     0x00000010);
+		MM_REG_WRITE(cmd, subsys_id, mmsys, MMSYS_SW0_RST_B, 1 << 4,
+			     0x00000010);
+		/* Reset MDP_DL_ASYNC2_RX */
+		/* Bit  11: MDP_DL_ASYNC2_RX */
+		MM_REG_WRITE(cmd, subsys_id, mmsys, MMSYS_SW1_RST_B, 0x0,
+			     0x00000800);
+		MM_REG_WRITE(cmd, subsys_id, mmsys, MMSYS_SW1_RST_B, 1 << 11,
+			     0x00000800);
+
+		/* Enable sof mode */
+		MM_REG_WRITE(cmd, subsys_id, mmsys, IPU_RELAY_CFG_WD, 0 << 31,
+			     0x80000000);
+	}
+
+	return 0;
+}
+
+static int config_isp_frame(struct mdp_comp_ctx *ctx, struct mdp_cmd *cmd,
+			    const struct v4l2_rect *compose)
+{
+	const struct isp_data *isp = &ctx->param->isp;
+	phys_addr_t base = ctx->comp->reg_base;
+	u8 subsys_id = ctx->comp->subsys_id;
+
+	/* DIP_X_SMX1I_BASE_ADDR, DIP_X_SMX1O_BASE_ADDR */
+	MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x2890, isp->smxi_iova[0],
+			  0xFFFFFFFF);
+	MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x27D0, isp->smxi_iova[0],
+			  0xFFFFFFFF);
+	/* DIP_X_SMX2I_BASE_ADDR, DIP_X_SMX2O_BASE_ADDR */
+	MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x28C0, isp->smxi_iova[1],
+			  0xFFFFFFFF);
+	MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x2800, isp->smxi_iova[1],
+			  0xFFFFFFFF);
+	/* DIP_X_SMX3I_BASE_ADDR, DIP_X_SMX3O_BASE_ADDR */
+	MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x28F0, isp->smxi_iova[2],
+			  0xFFFFFFFF);
+	MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x2830, isp->smxi_iova[2],
+			  0xFFFFFFFF);
+	/* DIP_X_SMX4I_BASE_ADDR, DIP_X_SMX4O_BASE_ADDR */
+	MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x2920, isp->smxi_iova[3],
+			  0xFFFFFFFF);
+	MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x2860, isp->smxi_iova[3],
+			  0xFFFFFFFF);
+
+	switch (isp->cq_idx) {
+	case ISP_DRV_DIP_CQ_THRE0:
+		MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x2208,
+				  isp->cq_iova, 0xFFFFFFFF);
+		break;
+	case ISP_DRV_DIP_CQ_THRE1:
+		MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x2214,
+				  isp->cq_iova, 0xFFFFFFFF);
+		break;
+	case ISP_DRV_DIP_CQ_THRE2:
+		MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x2220,
+				  isp->cq_iova, 0xFFFFFFFF);
+		break;
+	case ISP_DRV_DIP_CQ_THRE3:
+		MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x222C,
+				  isp->cq_iova, 0xFFFFFFFF);
+		break;
+	case ISP_DRV_DIP_CQ_THRE4:
+		MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x2238,
+				  isp->cq_iova, 0xFFFFFFFF);
+		break;
+	case ISP_DRV_DIP_CQ_THRE5:
+		MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x2244,
+				  isp->cq_iova, 0xFFFFFFFF);
+		break;
+	case ISP_DRV_DIP_CQ_THRE6:
+		MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x2250,
+				  isp->cq_iova, 0xFFFFFFFF);
+		break;
+	case ISP_DRV_DIP_CQ_THRE7:
+		MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x225C,
+				  isp->cq_iova, 0xFFFFFFFF);
+		break;
+	case ISP_DRV_DIP_CQ_THRE8:
+		MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x2268,
+				  isp->cq_iova, 0xFFFFFFFF);
+		break;
+	case ISP_DRV_DIP_CQ_THRE9:
+		MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x2274,
+				  isp->cq_iova, 0xFFFFFFFF);
+		break;
+	case ISP_DRV_DIP_CQ_THRE10:
+		MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x2280,
+				  isp->cq_iova, 0xFFFFFFFF);
+		break;
+	case ISP_DRV_DIP_CQ_THRE11:
+		MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x228C,
+				  isp->cq_iova, 0xFFFFFFFF);
+		break;
+	case ISP_DRV_DIP_CQ_THRE12:
+		MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x2298,
+				  isp->cq_iova, 0xFFFFFFFF);
+		break;
+	case ISP_DRV_DIP_CQ_THRE13:
+		MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x22A4,
+				  isp->cq_iova, 0xFFFFFFFF);
+		break;
+	case ISP_DRV_DIP_CQ_THRE14:
+		MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x22B0,
+				  isp->cq_iova, 0xFFFFFFFF);
+		break;
+	/* From CQ15 to CQ18, these do not connect to GCE */
+	default:
+		mdp_err("Do not support this cq (%d)", isp->cq_idx);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int config_isp_subfrm(struct mdp_comp_ctx *ctx,
+			     struct mdp_cmd *cmd, u32 index)
+{
+	phys_addr_t base = ctx->comp->reg_base;
+	u8 subsys_id = ctx->comp->subsys_id;
+
+	MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x2304,
+			  ctx->param->isp.tpipe_iova[index], 0xFFFFFFFF);
+	return 0;
+}
+
+static int wait_isp_event(struct mdp_comp_ctx *ctx, struct mdp_cmd *cmd)
+{
+	const struct isp_data *isp = &ctx->param->isp;
+	phys_addr_t base = ctx->comp->reg_base;
+	u8 subsys_id = ctx->comp->subsys_id;
+
+	/* MDP_DL_SEL: select MDP_CROP */
+	if (isp->dl_flags & (1 << MDP_CAMIN))
+		MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x0030, 0x00000000,
+				  0x00000200);
+	/* MDP2_DL_SEL: select MDP_CROP2 */
+	if (isp->dl_flags & (1 << MDP_CAMIN2))
+		MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x0030, 0x00000000,
+				  0x00000C00);
+
+	switch (isp->cq_idx) {
+	case ISP_DRV_DIP_CQ_THRE0:
+		MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x2000, 0x0001,
+				  0x00000001);
+		MM_REG_WAIT(cmd, ISP_P2_0_DONE);
+		break;
+	case ISP_DRV_DIP_CQ_THRE1:
+		MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x2000, 0x0002,
+				  0x00000002);
+		MM_REG_WAIT(cmd, ISP_P2_1_DONE);
+		break;
+	case ISP_DRV_DIP_CQ_THRE2:
+		MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x2000, 0x0004,
+				  0x00000004);
+		MM_REG_WAIT(cmd, ISP_P2_2_DONE);
+		break;
+	case ISP_DRV_DIP_CQ_THRE3:
+		MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x2000, 0x0008,
+				  0x00000008);
+		MM_REG_WAIT(cmd, ISP_P2_3_DONE);
+		break;
+	case ISP_DRV_DIP_CQ_THRE4:
+		MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x2000, 0x0010,
+				  0x00000010);
+		MM_REG_WAIT(cmd, ISP_P2_4_DONE);
+		break;
+	case ISP_DRV_DIP_CQ_THRE5:
+		MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x2000, 0x0020,
+				  0x00000020);
+		MM_REG_WAIT(cmd, ISP_P2_5_DONE);
+		break;
+	case ISP_DRV_DIP_CQ_THRE6:
+		MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x2000, 0x0040,
+				  0x00000040);
+		MM_REG_WAIT(cmd, ISP_P2_6_DONE);
+		break;
+	case ISP_DRV_DIP_CQ_THRE7:
+		MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x2000, 0x0080,
+				  0x00000080);
+		MM_REG_WAIT(cmd, ISP_P2_7_DONE);
+		break;
+	case ISP_DRV_DIP_CQ_THRE8:
+		MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x2000, 0x0100,
+				  0x00000100);
+		MM_REG_WAIT(cmd, ISP_P2_8_DONE);
+		break;
+	case ISP_DRV_DIP_CQ_THRE9:
+		MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x2000, 0x0200,
+				  0x00000200);
+		MM_REG_WAIT(cmd, ISP_P2_9_DONE);
+		break;
+	case ISP_DRV_DIP_CQ_THRE10:
+		MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x2000, 0x0400,
+				  0x00000400);
+		MM_REG_WAIT(cmd, ISP_P2_10_DONE);
+		break;
+	case ISP_DRV_DIP_CQ_THRE11:
+		MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x2000, 0x0800,
+				  0x00000800);
+		MM_REG_WAIT(cmd, ISP_P2_11_DONE);
+		break;
+	case ISP_DRV_DIP_CQ_THRE12:
+		MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x2000, 0x1000,
+				  0x00001000);
+		MM_REG_WAIT(cmd, ISP_P2_12_DONE);
+		break;
+	case ISP_DRV_DIP_CQ_THRE13:
+		MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x2000, 0x2000,
+				  0x00002000);
+		MM_REG_WAIT(cmd, ISP_P2_13_DONE);
+		break;
+	case ISP_DRV_DIP_CQ_THRE14:
+		MM_REG_WRITE_MASK(cmd, subsys_id, base, 0x2000, 0x4000,
+				  0x00004000);
+		MM_REG_WAIT(cmd, ISP_P2_14_DONE);
+		break;
+	/* From CQ15 to CQ18, these do not connect to GCE */
+	default:
+		mdp_err("Do not support this cq (%d)", isp->cq_idx);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct mdp_comp_ops imgi_ops = {
+	.get_comp_flag = get_comp_flag,
+	.init_comp = init_isp,
+	.config_frame = config_isp_frame,
+	.config_subfrm = config_isp_subfrm,
+	/* .reconfig_frame = reconfig_isp_frame, */
+	/* .reconfig_subfrms = reconfig_isp_subfrms, */
+	.wait_comp_event = wait_isp_event,
+	.advance_subfrm = NULL,
+	.post_process = NULL,
+};
+
+static int config_camin_subfrm(struct mdp_comp_ctx *ctx,
+			       struct mdp_cmd *cmd, u32 index)
+{
+	const struct img_comp_subfrm *csf = &ctx->param->subfrms[index];
+	phys_addr_t base = ctx->comp->reg_base;
+	u8 subsys_id = ctx->comp->subsys_id;
+	u32 isp_dl_w, isp_dl_h;
+
+	isp_dl_w = csf->in.right - csf->in.left + 1;
+	isp_dl_h = csf->in.bottom - csf->in.top + 1;
+
+	/* Config for direct link */
+	if (ctx->comp->alias_id == 0) {
+#ifdef MDP_ASYNC_CFG_WD
+		MM_REG_WRITE(cmd, subsys_id, base, MDP_ASYNC_CFG_WD,
+			     (isp_dl_h << 16) + isp_dl_w, 0x3FFF3FFF);
+#endif
+#ifdef ISP_RELAY_CFG_WD
+		MM_REG_WRITE(cmd, subsys_id, base, ISP_RELAY_CFG_WD,
+			     (isp_dl_h << 16) + isp_dl_w, 0x3FFF3FFF);
+#endif
+	} else {
+#ifdef MDP_ASYNC_IPU_CFG_WD
+		MM_REG_WRITE(cmd, subsys_id, base, MDP_ASYNC_IPU_CFG_WD,
+			     (isp_dl_h << 16) + isp_dl_w, 0x3FFF3FFF);
+#endif
+#ifdef IPU_RELAY_CFG_WD
+		MM_REG_WRITE(cmd, subsys_id, base, IPU_RELAY_CFG_WD,
+			     (isp_dl_h << 16) + isp_dl_w, 0x3FFF3FFF);
+#endif
+	}
+
+	return 0;
+}
+
+static const struct mdp_comp_ops camin_ops = {
+	.get_comp_flag = get_comp_flag,
+	.init_comp = NULL,
+	.config_frame = NULL,
+	.config_subfrm = config_camin_subfrm,
+	/* .reconfig_frame = NULL, */
+	/* .reconfig_subfrms = NULL, */
+	.wait_comp_event = NULL,
+	.advance_subfrm = NULL,
+	.post_process = NULL,
+};
+
+static const struct mdp_comp_ops *mdp_comp_ops[MDP_COMP_TYPE_COUNT] = {
+	[MDP_COMP_TYPE_RDMA] =		&rdma_ops,
+	[MDP_COMP_TYPE_RSZ] =		&rsz_ops,
+	[MDP_COMP_TYPE_WROT] =		&wrot_ops,
+	[MDP_COMP_TYPE_WDMA] =		&wdma_ops,
+	[MDP_COMP_TYPE_PATH] =		NULL,
+
+	[MDP_COMP_TYPE_CCORR] =		&ccorr_ops,
+
+	[MDP_COMP_TYPE_IMGI] =		&imgi_ops,
+	[MDP_COMP_TYPE_EXTO] =		NULL,
+	[MDP_COMP_TYPE_DL_PATH] =	&camin_ops,
+};
+
+struct mdp_comp_match {
+	enum mdp_comp_type	type;
+	u32			alias_id;
+};
+
+static const struct mdp_comp_match mdp_comp_matches[MDP_MAX_COMP_COUNT] = {
+	[MDP_COMP_WPEI] =	{ MDP_COMP_TYPE_WPEI, 0 },
+	[MDP_COMP_WPEO] =	{ MDP_COMP_TYPE_EXTO, 2 },
+	[MDP_COMP_WPEI2] =	{ MDP_COMP_TYPE_WPEI, 1 },
+	[MDP_COMP_WPEO2] =	{ MDP_COMP_TYPE_EXTO, 3 },
+	[MDP_COMP_ISP_IMGI] =	{ MDP_COMP_TYPE_IMGI, 0 },
+	[MDP_COMP_ISP_IMGO] =	{ MDP_COMP_TYPE_EXTO, 0 },
+	[MDP_COMP_ISP_IMG2O] =	{ MDP_COMP_TYPE_EXTO, 1 },
+
+	[MDP_COMP_CAMIN] =	{ MDP_COMP_TYPE_DL_PATH, 0 },
+	[MDP_COMP_CAMIN2] =	{ MDP_COMP_TYPE_DL_PATH, 1 },
+	[MDP_COMP_RDMA0] =	{ MDP_COMP_TYPE_RDMA, 0 },
+	[MDP_COMP_CCORR0] =	{ MDP_COMP_TYPE_CCORR, 0 },
+	[MDP_COMP_RSZ0] =	{ MDP_COMP_TYPE_RSZ, 0 },
+	[MDP_COMP_RSZ1] =	{ MDP_COMP_TYPE_RSZ, 1 },
+	[MDP_COMP_PATH0_SOUT] =	{ MDP_COMP_TYPE_PATH, 0 },
+	[MDP_COMP_PATH1_SOUT] =	{ MDP_COMP_TYPE_PATH, 1 },
+	[MDP_COMP_WROT0] =	{ MDP_COMP_TYPE_WROT, 0 },
+	[MDP_COMP_WDMA] =	{ MDP_COMP_TYPE_WDMA, 0 },
+};
+
+static const char * const gce_event_names[MDP_MAX_EVENT_COUNT] = {
+	[RDMA0_SOF] = "rdma0_sof",
+	[RDMA0_DONE] = "rdma0_done",
+	[RDMA1_SOF] = "rdma1_sof",
+	[RDMA1_DONE] = "rdma1_done",
+	[RSZ0_SOF] = "rsz0_sof",
+	[RSZ1_SOF] = "rsz1_sof",
+	[TDSHP0_SOF] = "tdshp0_sof",
+	[WROT0_SOF] = "wrot0_sof",
+	[WROT0_DONE] = "wrot0_done",
+	[WROT1_SOF] = "wrot1_sof",
+	[WROT1_DONE] = "wrot1_done",
+	[WDMA0_SOF] = "wdma0_sof",
+	[WDMA0_DONE] = "wdma0_done",
+	[IMG_DL_SOF] = "img_dl_sof",
+
+	[ISP_P2_0_DONE] = "isp_p2_0_done",
+	[ISP_P2_1_DONE] = "isp_p2_1_done",
+	[ISP_P2_2_DONE] = "isp_p2_2_done",
+	[ISP_P2_3_DONE] = "isp_p2_3_done",
+	[ISP_P2_4_DONE] = "isp_p2_4_done",
+	[ISP_P2_5_DONE] = "isp_p2_5_done",
+	[ISP_P2_6_DONE] = "isp_p2_6_done",
+	[ISP_P2_7_DONE] = "isp_p2_7_done",
+	[ISP_P2_8_DONE] = "isp_p2_8_done",
+	[ISP_P2_9_DONE] = "isp_p2_9_done",
+	[ISP_P2_10_DONE] = "isp_p2_10_done",
+	[ISP_P2_11_DONE] = "isp_p2_11_done",
+	[ISP_P2_12_DONE] = "isp_p2_12_done",
+	[ISP_P2_13_DONE] = "isp_p2_13_done",
+	[ISP_P2_14_DONE] = "isp_p2_14_done",
+
+	[WPE_DONE] = "wpe_done",
+	[WPE_B_DONE] = "wpe_b_done",
+	[WROT0_SRAM_READY] = "wrot0_sram_ready",
+	[WROT1_SRAM_READY] = "wrot1_sram_ready",
+};
+
+static const struct of_device_id mdp_comp_dt_ids[] = {
+	{
+		.compatible = "mediatek,mt8183-mdp-rdma",
+		.data = (void *)MDP_COMP_TYPE_RDMA,
+	}, {
+		.compatible = "mediatek,mt8183-mdp-ccorr",
+		.data = (void *)MDP_COMP_TYPE_CCORR,
+	}, {
+		.compatible = "mediatek,mt8183-mdp-rsz",
+		.data = (void *)MDP_COMP_TYPE_RSZ,
+	}, {
+		.compatible = "mediatek,mt8183-mdp-wrot",
+		.data = (void *)MDP_COMP_TYPE_WROT,
+	}, {
+		.compatible = "mediatek,mt8183-mdp-wdma",
+		.data = (void *)MDP_COMP_TYPE_WDMA,
+	}, {
+		.compatible = "mediatek,mt8183-mdp-path",
+		.data = (void *)MDP_COMP_TYPE_PATH,
+	}, {
+		.compatible = "mediatek,mt8183-mdp-imgi",
+		.data = (void *)MDP_COMP_TYPE_IMGI,
+	}, {
+		.compatible = "mediatek,mt8183-mdp-exto",
+		.data = (void *)MDP_COMP_TYPE_EXTO,
+	}, {
+		.compatible = "mediatek,mt8183-mdp-dl",
+		.data = (void *)MDP_COMP_TYPE_DL_PATH,
+	},
+};
+
+static int mdp_comp_get_id(struct device *dev, struct device_node *node,
+			   enum mdp_comp_type type)
+{
+	u32 alias_id;
+	int i, ret;
+
+	ret = of_property_read_u32(node, "mediatek,mdp-id", &alias_id);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < ARRAY_SIZE(mdp_comp_matches); i++)
+		if (mdp_comp_matches[i].type == type &&
+		    mdp_comp_matches[i].alias_id == alias_id)
+			return i;
+
+	dev_err(dev, "Failed to get id. type: %d, alias: %d\n", type, alias_id);
+	return -EINVAL;
+}
+
+void mdp_comp_clock_on(struct device *dev, struct mdp_comp *comp)
+{
+	int i, err;
+
+	if (comp->larb_dev) {
+		err = pm_runtime_get_sync(comp->larb_dev);
+		if (err < 0)
+			dev_err(dev,
+				"Failed to get larb, err %d. type:%d id:%d\n",
+				err, comp->type, comp->id);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(comp->clks); i++) {
+		if (IS_ERR(comp->clks[i]))
+			break;
+		err = clk_prepare_enable(comp->clks[i]);
+		if (err)
+			dev_err(dev,
+				"Failed to enable clock %d, err %d. type:%d id:%d\n",
+				i, err, comp->type, comp->id);
+	}
+}
+
+void mdp_comp_clock_off(struct device *dev, struct mdp_comp *comp)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(comp->clks); i++) {
+		if (IS_ERR(comp->clks[i]))
+			break;
+		clk_disable_unprepare(comp->clks[i]);
+	}
+
+	if (comp->larb_dev)
+		pm_runtime_put_sync(comp->larb_dev);
+}
+
+static int mdp_get_subsys_id(struct device *dev, struct device_node *node,
+			     struct mdp_comp *comp)
+{
+	struct platform_device *comp_pdev;
+	struct cmdq_subsys *subsys;
+
+	if (!dev || !node || !comp)
+		return -EINVAL;
+
+	comp_pdev = of_find_device_by_node(node);
+
+	if (!comp_pdev) {
+		dev_err(dev, "get comp_pdev fail! comp id=%d type=%d\n",
+			comp->id, comp->type);
+		return -ENODEV;
+	}
+
+	subsys = cmdq_dev_get_subsys(&comp_pdev->dev, 0);
+
+	if (IS_ERR(subsys)) {
+		dev_err(&comp_pdev->dev,
+			"cmdq_dev_get_subsys fail!\n");
+		return (int)subsys;
+	}
+
+	comp->subsys_id = subsys->id;
+	dev_err(&comp_pdev->dev, "subsys id=%d\n", subsys->id);
+
+	return 0;
+}
+
+static void __mdp_comp_init(struct mdp_dev *mdp, struct device_node *node,
+			    struct mdp_comp *comp)
+{
+	struct resource res;
+	phys_addr_t base;
+
+	if (of_address_to_resource(node, 0, &res) < 0)
+		base = 0L;
+	else
+		base = 0L | res.start;
+
+	comp->mdp_dev = mdp;
+	/* comp->dev_node = of_node_get(node); */
+	comp->regs = of_iomap(node, 0);
+	comp->reg_base = base;
+}
+
+static int mdp_mm_init(struct device *dev, struct mdp_dev *mdp,
+		       struct mdp_comp *comp, const char *ref_name)
+{
+	struct device_node *node;
+
+	node = of_parse_phandle(dev->of_node, ref_name, 0);
+	if (!node) {
+		dev_err(dev, "Failed to parse dt %s\n", ref_name);
+		return -EINVAL;
+	}
+
+	__mdp_comp_init(mdp, node, comp);
+	mdp_get_subsys_id(dev, node, comp);
+	of_node_put(node);
+	if (!comp->reg_base) {
+		dev_err(dev, "Failed to init %s base\n", ref_name);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int mdp_comp_init(struct device *dev, struct mdp_dev *mdp,
+			 struct device_node *node, struct mdp_comp *comp,
+			 enum mdp_comp_id id)
+{
+	struct device_node *larb_node;
+	struct platform_device *larb_pdev;
+	int i;
+
+	if (id < 0 || id >= MDP_MAX_COMP_COUNT) {
+		dev_err(dev, "Invalid component id %d\n", id);
+		return -EINVAL;
+	}
+
+	__mdp_comp_init(mdp, node, comp);
+	comp->type = mdp_comp_matches[id].type;
+	comp->id = id;
+	comp->alias_id = mdp_comp_matches[id].alias_id;
+	comp->ops = mdp_comp_ops[comp->type];
+
+	for (i = 0; i < ARRAY_SIZE(comp->clks); i++) {
+		comp->clks[i] = of_clk_get(node, i);
+		if (IS_ERR(comp->clks[i]))
+			break;
+	}
+
+	mdp_get_subsys_id(dev, node, comp);
+
+	/* Only DMA capable components need the LARB property */
+	comp->larb_dev = NULL;
+	if (comp->type != MDP_COMP_TYPE_RDMA &&
+	    comp->type != MDP_COMP_TYPE_WROT &&
+		comp->type != MDP_COMP_TYPE_WDMA)
+		return 0;
+
+	larb_node = of_parse_phandle(node, "mediatek,larb", 0);
+	if (!larb_node) {
+		dev_err(dev, "Missing mediatek,larb phandle in %pOF node\n",
+			node);
+		return -EINVAL;
+	}
+
+	larb_pdev = of_find_device_by_node(larb_node);
+	if (!larb_pdev) {
+		dev_warn(dev, "Waiting for larb device %pOF\n", larb_node);
+		of_node_put(larb_node);
+		return -EPROBE_DEFER;
+	}
+	of_node_put(larb_node);
+
+	comp->larb_dev = &larb_pdev->dev;
+
+	return 0;
+}
+
+static void mdp_comp_deinit(struct device *dev, struct mdp_comp *comp)
+{
+	iounmap(comp->regs);
+	/* of_node_put(comp->dev_node); */
+}
+
+void mdp_component_deinit(struct device *dev, struct mdp_dev *mdp)
+{
+	int i;
+
+	mdp_comp_deinit(dev, &mdp->mmsys);
+	mdp_comp_deinit(dev, &mdp->mm_mutex);
+	for (i = 0; i < ARRAY_SIZE(mdp->comp); i++) {
+		if (mdp->comp[i]) {
+			mdp_comp_deinit(dev, mdp->comp[i]);
+			kfree(mdp->comp[i]);
+		}
+	}
+}
+
+int mdp_component_init(struct device *dev, struct mdp_dev *mdp)
+{
+	struct device_node *node, *parent;
+	int i, ret;
+
+	for (i = 0; i < ARRAY_SIZE(gce_event_names); i++) {
+		s32 event_id;
+
+		event_id = cmdq_dev_get_event(dev, gce_event_names[i]);
+		mdp->event[i] = (event_id < 0) ? -i : event_id;
+		dev_info(dev, "Get event %s id:%d\n",
+			 gce_event_names[i], mdp->event[i]);
+	}
+
+	ret = mdp_mm_init(dev, mdp, &mdp->mmsys, "mediatek,mmsys");
+	if (ret)
+		goto err_init_mm;
+
+	ret = mdp_mm_init(dev, mdp, &mdp->mm_mutex, "mediatek,mm-mutex");
+	if (ret)
+		goto err_init_mm;
+
+	parent = dev->of_node->parent;
+	/* Iterate over sibling MDP function blocks */
+	for_each_child_of_node(parent, node) {
+		const struct of_device_id *of_id;
+		enum mdp_comp_type type;
+		int id;
+		struct mdp_comp *comp;
+
+		of_id = of_match_node(mdp_comp_dt_ids, node);
+		if (!of_id)
+			continue;
+
+		if (!of_device_is_available(node)) {
+			dev_err(dev, "Skipping disabled component %pOF\n",
+				node);
+			continue;
+		}
+
+		type = (enum mdp_comp_type)of_id->data;
+		id = mdp_comp_get_id(dev, node, type);
+		if (id < 0) {
+			dev_warn(dev, "Skipping unknown component %pOF\n",
+				 node);
+			continue;
+		}
+
+		comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL);
+		if (!comp) {
+			ret = -ENOMEM;
+			goto err_init_comps;
+		}
+		mdp->comp[id] = comp;
+
+		ret = mdp_comp_init(dev, mdp, node, comp, id);
+		if (ret)
+			goto err_init_comps;
+
+		dev_info(dev, "%s type:%d alias:%d id:%d base:%#x regs:%p\n",
+			 of_id->compatible, type, comp->alias_id, id,
+			(u32)comp->reg_base, comp->regs);
+	}
+	return 0;
+
+err_init_comps:
+	mdp_component_deinit(dev, mdp);
+err_init_mm:
+	return ret;
+}
+
+int mdp_comp_ctx_init(struct mdp_dev *mdp, struct mdp_comp_ctx *ctx,
+		      const struct img_compparam *param,
+	const struct img_ipi_frameparam *frame)
+{
+	int i;
+
+	if (param->type < 0 || param->type >= MDP_MAX_COMP_COUNT) {
+		mdp_err("Invalid component id %d", param->type);
+		return -EINVAL;
+	}
+
+	ctx->comp = mdp->comp[param->type];
+	if (!ctx->comp) {
+		mdp_err("Uninit component id %d", param->type);
+		return -EINVAL;
+	}
+
+	ctx->param = param;
+	ctx->input = &frame->inputs[param->input];
+	for (i = 0; i < param->num_outputs; i++)
+		ctx->outputs[i] = &frame->outputs[param->outputs[i]];
+	return 0;
+}
+
diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-comp.h b/drivers/media/platform/mtk-mdp3/mtk-mdp3-comp.h
new file mode 100644
index 000000000000..0c65214ef695
--- /dev/null
+++ b/drivers/media/platform/mtk-mdp3/mtk-mdp3-comp.h
@@ -0,0 +1,177 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Ping-Hsun Wu <ping-hsun.wu@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_MDP3_COMP_H__
+#define __MTK_MDP3_COMP_H__
+
+#include "mtk-mdp3-cmdq.h"
+
+enum mdp_comp_type {
+	MDP_COMP_TYPE_INVALID = 0,
+
+	MDP_COMP_TYPE_RDMA,
+	MDP_COMP_TYPE_RSZ,
+	MDP_COMP_TYPE_WROT,
+	MDP_COMP_TYPE_WDMA,
+	MDP_COMP_TYPE_PATH,
+
+	MDP_COMP_TYPE_TDSHP,
+	MDP_COMP_TYPE_COLOR,
+	MDP_COMP_TYPE_DRE,
+	MDP_COMP_TYPE_CCORR,
+	MDP_COMP_TYPE_HDR,
+
+	MDP_COMP_TYPE_IMGI,
+	MDP_COMP_TYPE_WPEI,
+	MDP_COMP_TYPE_EXTO,	/* External path */
+	MDP_COMP_TYPE_DL_PATH,	/* Direct-link path */
+
+	MDP_COMP_TYPE_COUNT	/* ALWAYS keep at the end */
+};
+
+enum mdp_comp_id {
+	MDP_COMP_NONE = -1,	/* Invalid engine */
+
+	/* ISP */
+	MDP_COMP_WPEI = 0,
+	MDP_COMP_WPEO,		/* 1 */
+	MDP_COMP_WPEI2,		/* 2 */
+	MDP_COMP_WPEO2,		/* 3 */
+	MDP_COMP_ISP_IMGI,	/* 4 */
+	MDP_COMP_ISP_IMGO,	/* 5 */
+	MDP_COMP_ISP_IMG2O,	/* 6 */
+
+	/* IPU */
+	MDP_COMP_IPUI,		/* 7 */
+	MDP_COMP_IPUO,		/* 8 */
+
+	/* MDP */
+	MDP_COMP_CAMIN,		/* 9 */
+	MDP_COMP_CAMIN2,	/* 10 */
+	MDP_COMP_RDMA0,		/* 11 */
+	MDP_COMP_AAL0,		/* 12 */
+	MDP_COMP_CCORR0,	/* 13 */
+	MDP_COMP_RSZ0,		/* 14 */
+	MDP_COMP_RSZ1,		/* 15 */
+	MDP_COMP_TDSHP0,	/* 16 */
+	MDP_COMP_COLOR0,	/* 17 */
+	MDP_COMP_PATH0_SOUT,	/* 18 */
+	MDP_COMP_PATH1_SOUT,	/* 19 */
+	MDP_COMP_WROT0,		/* 20 */
+	MDP_COMP_WDMA,		/* 21 */
+
+	/* Dummy Engine */
+	MDP_COMP_RDMA1,		/* 22 */
+	MDP_COMP_RSZ2,		/* 23 */
+	MDP_COMP_TDSHP1,	/* 24 */
+	MDP_COMP_WROT1,		/* 25 */
+
+	MDP_MAX_COMP_COUNT	/* ALWAYS keep at the end */
+};
+
+enum mdp_comp_event {
+	RDMA0_SOF,
+	RDMA0_DONE,
+	RDMA1_SOF,
+	RDMA1_DONE,
+	RSZ0_SOF,
+	RSZ1_SOF,
+	TDSHP0_SOF,
+	WROT0_SOF,
+	WROT0_DONE,
+	WROT1_SOF,
+	WROT1_DONE,
+	WDMA0_SOF,
+	WDMA0_DONE,
+	IMG_DL_SOF,
+
+	ISP_P2_0_DONE,
+	ISP_P2_1_DONE,
+	ISP_P2_2_DONE,
+	ISP_P2_3_DONE,
+	ISP_P2_4_DONE,
+	ISP_P2_5_DONE,
+	ISP_P2_6_DONE,
+	ISP_P2_7_DONE,
+	ISP_P2_8_DONE,
+	ISP_P2_9_DONE,
+	ISP_P2_10_DONE,
+	ISP_P2_11_DONE,
+	ISP_P2_12_DONE,
+	ISP_P2_13_DONE,
+	ISP_P2_14_DONE,
+
+	WPE_DONE,
+	WPE_B_DONE,
+	WROT0_SRAM_READY,
+	WROT1_SRAM_READY,
+
+	MDP_MAX_EVENT_COUNT	/* ALWAYS keep at the end */
+};
+
+struct mdp_comp_ops;
+
+struct mdp_comp {
+	struct mdp_dev		*mdp_dev;
+	/* struct device_node	*dev_node; */
+	void __iomem		*regs;
+	phys_addr_t		reg_base;
+	u8			subsys_id;
+	struct clk		*clks[2];
+	struct device		*larb_dev;
+	enum mdp_comp_type	type;
+	enum mdp_comp_id	id;
+	u32			alias_id;
+	const struct mdp_comp_ops *ops;
+};
+
+struct mdp_comp_ctx {
+	struct mdp_comp			*comp;
+	const struct img_compparam	*param;
+	const struct img_input		*input;
+	const struct img_output		*outputs[IMG_MAX_HW_OUTPUTS];
+};
+
+struct mdp_comp_ops {
+	s64 (*get_comp_flag)(const struct mdp_comp_ctx *ctx);
+	/* s64 (*query_feature)(void); */
+	int (*init_comp)(struct mdp_comp_ctx *ctx, struct mdp_cmd *cmd);
+	int (*config_frame)(struct mdp_comp_ctx *ctx, struct mdp_cmd *cmd,
+			    const struct v4l2_rect *compose);
+	/* int (*config_frame_end)(struct mdp_comp_ctx *ctx,
+	 *      struct mdp_cmd *cmd);
+	 */
+	int (*config_subfrm)(struct mdp_comp_ctx *ctx,
+			     struct mdp_cmd *cmd, u32 index);
+	int (*wait_comp_event)(struct mdp_comp_ctx *ctx,
+			       struct mdp_cmd *cmd);
+	int (*advance_subfrm)(struct mdp_comp_ctx *ctx,
+			      struct mdp_cmd *cmd, u32 index);
+	int (*post_process)(struct mdp_comp_ctx *ctx, struct mdp_cmd *cmd);
+	/* void (*release)(struct mdp_comp_ctx *ctx); */
+};
+
+struct mdp_dev;
+
+int mdp_component_init(struct device *dev, struct mdp_dev *mdp);
+void mdp_component_deinit(struct device *dev, struct mdp_dev *mdp);
+void mdp_comp_clock_on(struct device *dev, struct mdp_comp *comp);
+void mdp_comp_clock_off(struct device *dev, struct mdp_comp *comp);
+int mdp_comp_ctx_init(struct mdp_dev *mdp, struct mdp_comp_ctx *ctx,
+		      const struct img_compparam *param,
+	const struct img_ipi_frameparam *frame);
+
+#endif  /* __MTK_MDP3_COMP_H__ */
+
diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-core.c b/drivers/media/platform/mtk-mdp3/mtk-mdp3-core.c
new file mode 100644
index 000000000000..47b5d87834de
--- /dev/null
+++ b/drivers/media/platform/mtk-mdp3/mtk-mdp3-core.c
@@ -0,0 +1,256 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Ping-Hsun Wu <ping-hsun.wu@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/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/remoteproc.h>
+#include <linux/platform_data/mtk_scp.h>
+#include <media/videobuf2-dma-contig.h>
+#include "mtk-mdp3-core.h"
+#include "mtk-mdp3-m2m.h"
+
+/* MDP debug log level (0-3). 3 shows all the logs. */
+int mtk_mdp_debug;
+EXPORT_SYMBOL(mtk_mdp_debug);
+module_param_named(debug, mtk_mdp_debug, int, 0644);
+
+static const struct of_device_id mdp_of_ids[] = {
+	{ .compatible = "mediatek,mt8183-mdp3", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, mdp_of_ids);
+
+struct platform_device *mdp_get_plat_device(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *mdp_node;
+	struct platform_device *mdp_pdev;
+
+	mdp_node = of_parse_phandle(dev->of_node, "mediatek,mdp3", 0);
+	if (!mdp_node) {
+		dev_err(dev, "can't get mdp node\n");
+		return NULL;
+	}
+
+	mdp_pdev = of_find_device_by_node(mdp_node);
+	if (WARN_ON(!mdp_pdev)) {
+		dev_err(dev, "mdp pdev failed\n");
+		of_node_put(mdp_node);
+		return NULL;
+	}
+
+	return mdp_pdev;
+}
+EXPORT_SYMBOL_GPL(mdp_get_plat_device);
+
+int mdp_vpu_get_locked(struct mdp_dev *mdp)
+{
+	int ret = 0;
+
+	if (mdp->vpu_count++ == 0) {
+		ret = rproc_boot(mdp->rproc_handle);
+		if (ret < 0) {
+			dev_err(&mdp->pdev->dev,
+				"vpu_load_firmware failed %d\n", ret);
+			goto err_load_vpu;
+		}
+		ret = mdp_vpu_register(mdp->vpu_dev);
+		if (ret < 0) {
+			dev_err(&mdp->pdev->dev,
+				"mdp_vpu register failed %d\n", ret);
+			goto err_reg_vpu;
+		}
+		ret = mdp_vpu_dev_init(&mdp->vpu, mdp->vpu_dev, &mdp->vpu_lock);
+		if (ret) {
+			dev_err(&mdp->pdev->dev,
+				"mdp_vpu device init failed %d\n", ret);
+			goto err_init_vpu;
+		}
+	}
+	return 0;
+
+err_init_vpu:
+	mdp_vpu_unregister(mdp->vpu_dev);
+err_reg_vpu:
+err_load_vpu:
+	mdp->vpu_count--;
+	return ret;
+}
+
+void mdp_vpu_put_locked(struct mdp_dev *mdp)
+{
+	if (--mdp->vpu_count == 0) {
+		mdp_vpu_dev_deinit(&mdp->vpu);
+		mdp_vpu_unregister(mdp->vpu_dev);
+	}
+}
+
+static int mdp_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct mdp_dev *mdp;
+	phandle rproc_phandle;
+	int ret;
+
+	mdp = devm_kzalloc(dev, sizeof(*mdp), GFP_KERNEL);
+	if (!mdp)
+		return -ENOMEM;
+
+	mdp->pdev = pdev;
+	ret = mdp_component_init(dev, mdp);
+	if (ret) {
+		dev_err(dev, "Failed to initialize mdp components\n");
+		goto err_init_comp;
+	}
+
+	mdp->job_wq = create_singlethread_workqueue(MDP_MODULE_NAME);
+	if (!mdp->job_wq) {
+		dev_err(dev, "Unable to create job workqueue\n");
+		ret = -ENOMEM;
+		goto err_create_job_wq;
+	}
+
+	mdp->vpu_dev = scp_get_pdev(pdev);
+
+	if (of_property_read_u32(pdev->dev.of_node, "mediatek,scp",
+				 &rproc_phandle))
+		dev_err(&pdev->dev, "Could not get scp device\n");
+	else
+		dev_info(&pdev->dev, "Find mediatek,scp phandle:%llx\n",
+			 (unsigned long long)rproc_phandle);
+
+	mdp->rproc_handle = rproc_get_by_phandle(rproc_phandle);
+
+	dev_info(&pdev->dev, "MDP rproc_handle: %llx",
+		 (unsigned long long)mdp->rproc_handle);
+
+	if (!mdp->rproc_handle)
+		dev_err(&pdev->dev, "Could not get MDP's rproc_handle\n");
+
+	/* vpu_wdt_reg_handler(mdp->vpu_dev, mdp_reset_handler, mdp,
+	 *		       VPU_RST_MDP);
+	 */
+	mutex_init(&mdp->vpu_lock);
+	mdp->vpu_count = 0;
+	mdp->id_count = 0;
+
+	mdp->cmdq_clt = cmdq_mbox_create(dev, 0, 1200);
+	if (IS_ERR(mdp->cmdq_clt))
+		goto err_mbox_create;
+
+	ret = v4l2_device_register(dev, &mdp->v4l2_dev);
+	if (ret) {
+		dev_err(dev, "Failed to register v4l2 device\n");
+		ret = -EINVAL;
+		goto err_v4l2_register;
+	}
+
+	ret = mdp_m2m_device_register(mdp);
+	if (ret) {
+		v4l2_err(&mdp->v4l2_dev, "Failed to register m2m device\n");
+		goto err_m2m_register;
+	}
+	mutex_init(&mdp->m2m_lock);
+
+	platform_set_drvdata(pdev, mdp);
+
+	vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32));
+	pm_runtime_enable(dev);
+	dev_dbg(dev, "mdp-%d registered successfully\n", pdev->id);
+	return 0;
+
+err_m2m_register:
+	v4l2_device_unregister(&mdp->v4l2_dev);
+err_v4l2_register:
+err_mbox_create:
+	destroy_workqueue(mdp->job_wq);
+err_create_job_wq:
+err_init_comp:
+	kfree(mdp);
+
+	dev_dbg(dev, "Errno %d\n", ret);
+	return ret;
+}
+
+static int mdp_remove(struct platform_device *pdev)
+{
+	struct mdp_dev *mdp = platform_get_drvdata(pdev);
+
+	pm_runtime_disable(&pdev->dev);
+	vb2_dma_contig_clear_max_seg_size(&pdev->dev);
+	mdp_m2m_device_unregister(mdp);
+	v4l2_device_unregister(&mdp->v4l2_dev);
+
+	flush_workqueue(mdp->job_wq);
+	destroy_workqueue(mdp->job_wq);
+
+	mdp_component_deinit(&pdev->dev, mdp);
+	kfree(mdp);
+
+	dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name);
+	return 0;
+}
+
+static int __maybe_unused mdp_pm_suspend(struct device *dev)
+{
+	// TODO: mdp clock off
+	return 0;
+}
+
+static int __maybe_unused mdp_pm_resume(struct device *dev)
+{
+	// TODO: mdp clock on
+	return 0;
+}
+
+static int __maybe_unused mdp_suspend(struct device *dev)
+{
+	if (pm_runtime_suspended(dev))
+		return 0;
+
+	return mdp_pm_suspend(dev);
+}
+
+static int __maybe_unused mdp_resume(struct device *dev)
+{
+	if (pm_runtime_suspended(dev))
+		return 0;
+
+	return mdp_pm_resume(dev);
+}
+
+static const struct dev_pm_ops mdp_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(mdp_suspend, mdp_resume)
+	SET_RUNTIME_PM_OPS(mdp_pm_suspend, mdp_pm_resume, NULL)
+};
+
+static struct platform_driver mdp_driver = {
+	.probe		= mdp_probe,
+	.remove		= mdp_remove,
+	.driver = {
+		.name	= MDP_MODULE_NAME,
+		.pm	= &mdp_pm_ops,
+		.of_match_table = mdp_of_ids,
+	},
+};
+
+module_platform_driver(mdp_driver);
+
+MODULE_AUTHOR("Ping-Hsun Wu <ping-hsun.wu@mediatek.com>");
+MODULE_DESCRIPTION("Mediatek image processor 3 driver");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-core.h b/drivers/media/platform/mtk-mdp3/mtk-mdp3-core.h
new file mode 100644
index 000000000000..5ef3199b55e2
--- /dev/null
+++ b/drivers/media/platform/mtk-mdp3/mtk-mdp3-core.h
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Ping-Hsun Wu <ping-hsun.wu@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_MDP3_CORE_H__
+#define __MTK_MDP3_CORE_H__
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-mem2mem.h>
+#include "mtk-mdp3-comp.h"
+#include "mtk-mdp3-vpu.h"
+
+#define MDP_MODULE_NAME	"mtk-mdp3"
+
+enum mdp_buffer_usage {
+	MDP_BUFFER_USAGE_HW_READ,
+	MDP_BUFFER_USAGE_MDP,
+	MDP_BUFFER_USAGE_MDP2,
+	MDP_BUFFER_USAGE_ISP,
+	MDP_BUFFER_USAGE_WPE,
+};
+
+struct mdp_dev {
+	struct platform_device	*pdev;
+	struct mdp_comp		mmsys;
+	struct mdp_comp		mm_mutex;
+	struct mdp_comp		*comp[MDP_MAX_COMP_COUNT];
+	s32			event[MDP_MAX_EVENT_COUNT];
+
+	struct workqueue_struct	*job_wq;
+	struct mdp_vpu_dev	vpu;
+	struct platform_device	*vpu_dev;
+	struct rproc *rproc_handle;
+	/* synchronization protect for image configuration reference count */
+	struct mutex		vpu_lock;
+	s32			vpu_count;
+	u32			id_count;
+	struct cmdq_client	*cmdq_clt;
+
+	struct v4l2_device	v4l2_dev;
+	struct video_device	*m2m_vdev;
+	struct v4l2_m2m_dev	*m2m_dev;
+	/* synchronization protect for m2m device operation */
+	struct mutex		m2m_lock;
+};
+
+int mdp_vpu_get_locked(struct mdp_dev *mdp);
+void mdp_vpu_put_locked(struct mdp_dev *mdp);
+
+extern int mtk_mdp_debug;
+
+#define DEBUG
+#if defined(DEBUG)
+
+#define mdp_dbg(level, fmt, ...)\
+	do {\
+		if (mtk_mdp_debug >= (level))\
+			pr_info("[MTK-MDP3] %d %s:%d: " fmt "\n",\
+				level, __func__, __LINE__, ##__VA_ARGS__);\
+	} while (0)
+
+#define mdp_err(fmt, ...)\
+	pr_err("[MTK-MDP3][ERR] %s:%d: " fmt "\n", __func__, __LINE__,\
+		##__VA_ARGS__)
+
+#else
+
+#define mdp_dbg(level, fmt, ...)	do {} while (0)
+#define mdp_err(fmt, ...)		do {} while (0)
+
+#endif
+
+#define mdp_dbg_enter() mdp_dbg(3, "+")
+#define mdp_dbg_leave() mdp_dbg(3, "-")
+
+#endif  /* __MTK_MDP3_CORE_H__ */
+
diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-m2m.c b/drivers/media/platform/mtk-mdp3/mtk-mdp3-m2m.c
new file mode 100644
index 000000000000..638a854c502c
--- /dev/null
+++ b/drivers/media/platform/mtk-mdp3/mtk-mdp3-m2m.c
@@ -0,0 +1,823 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Ping-Hsun Wu <ping-hsun.wu@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/platform_device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-dma-contig.h>
+#include "mtk-mdp3-m2m.h"
+
+static inline struct mdp_m2m_ctx *fh_to_ctx(struct v4l2_fh *fh)
+{
+	return container_of(fh, struct mdp_m2m_ctx, fh);
+}
+
+static inline struct mdp_m2m_ctx *ctrl_to_ctx(struct v4l2_ctrl *ctrl)
+{
+	return container_of(ctrl->handler, struct mdp_m2m_ctx, ctrl_handler);
+}
+
+static inline struct mdp_frame *ctx_get_frame(struct mdp_m2m_ctx *ctx,
+					      enum v4l2_buf_type type)
+{
+	if (V4L2_TYPE_IS_OUTPUT(type))
+		return &ctx->curr_param->output;
+	return &ctx->curr_param->captures[0];
+}
+
+static void mdp_m2m_ctx_set_state(struct mdp_m2m_ctx *ctx, u32 state)
+{
+	mutex_lock(&ctx->curr_param->lock);
+	ctx->curr_param->state |= state;
+	mutex_unlock(&ctx->curr_param->lock);
+}
+
+static bool mdp_m2m_ctx_is_state_set(struct mdp_m2m_ctx *ctx, u32 mask)
+{
+	bool ret;
+
+	mutex_lock(&ctx->curr_param->lock);
+	ret = (ctx->curr_param->state & mask) == mask;
+	mutex_unlock(&ctx->curr_param->lock);
+	return ret;
+}
+
+static void mdp_m2m_ctx_lock(struct vb2_queue *q)
+{
+	struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(q);
+
+	mutex_lock(&ctx->mdp_dev->m2m_lock);
+}
+
+static void mdp_m2m_ctx_unlock(struct vb2_queue *q)
+{
+	struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(q);
+
+	mutex_unlock(&ctx->mdp_dev->m2m_lock);
+}
+
+static void mdp_m2m_job_abort(void *priv)
+{
+}
+
+static void mdp_m2m_process_done(void *priv, int vb_state)
+{
+	struct mdp_m2m_ctx *ctx = priv;
+	struct vb2_buffer *src_vb, *dst_vb;
+	struct vb2_v4l2_buffer *src_vbuf, *dst_vbuf;
+	u32 valid_output_flags = V4L2_BUF_FLAG_TIMECODE |
+				 V4L2_BUF_FLAG_TSTAMP_SRC_MASK |
+				 V4L2_BUF_FLAG_KEYFRAME |
+				 V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_BFRAME;
+
+	src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+	src_vbuf = to_vb2_v4l2_buffer(src_vb);
+	dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
+	dst_vbuf = to_vb2_v4l2_buffer(dst_vb);
+
+	src_vbuf->sequence = ctx->frame_count;
+	dst_vbuf->sequence = src_vbuf->sequence;
+	dst_vbuf->timecode = src_vbuf->timecode;
+	dst_vbuf->flags &= ~valid_output_flags;
+	dst_vbuf->flags |= src_vbuf->flags & valid_output_flags;
+
+	v4l2_m2m_buf_done(src_vbuf, vb_state);
+	v4l2_m2m_buf_done(dst_vbuf, vb_state);
+	v4l2_m2m_job_finish(ctx->mdp_dev->m2m_dev, ctx->m2m_ctx);
+
+	ctx->curr_param->frame_no = ctx->frame_count++;
+}
+
+static void mdp_m2m_worker(struct work_struct *work)
+{
+	struct mdp_m2m_ctx *ctx = container_of(work, struct mdp_m2m_ctx, work);
+	struct mdp_frame *frame;
+	struct vb2_buffer *src_vb, *dst_vb;
+	struct img_ipi_frameparam param = {0};
+	struct mdp_cmdq_param task = {0};
+	enum vb2_buffer_state vb_state = VB2_BUF_STATE_ERROR;
+	int ret;
+
+	if (mdp_m2m_ctx_is_state_set(ctx, MDP_M2M_CTX_ERROR)) {
+		dev_err(&ctx->mdp_dev->pdev->dev,
+			"mdp_m2m_ctx is in error state\n");
+		goto worker_end;
+	}
+
+	param.frame_no = ctx->curr_param->frame_no;
+	param.type = ctx->curr_param->type;
+	param.num_inputs = 1;
+	param.num_outputs = 1;
+
+	frame = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+	src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
+	mdp_set_src_config(&param.inputs[0], frame, src_vb);
+
+	frame = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+	dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+	mdp_set_dst_config(&param.outputs[0], frame, dst_vb);
+
+	dst_vb->timestamp = src_vb->timestamp;
+	param.timestamp = src_vb->timestamp;
+
+	ret = mdp_vpu_process(&ctx->vpu, &param);
+	if (ret) {
+		dev_err(&ctx->mdp_dev->pdev->dev,
+			"VPU MDP process failed: %d\n", ret);
+		goto worker_end;
+	}
+
+	task.config = ctx->vpu.config;
+	task.param = &param;
+	task.composes[0] = &frame->compose;
+	task.wait = 1;
+	task.cmdq_cb = NULL;
+	task.cb_data = NULL;
+
+	ret = mdp_cmdq_send(ctx->mdp_dev, &task);
+	if (ret) {
+		dev_err(&ctx->mdp_dev->pdev->dev,
+			"CMDQ sendtask failed: %d\n", ret);
+		goto worker_end;
+	}
+
+	vb_state = VB2_BUF_STATE_DONE;
+
+worker_end:
+	mdp_m2m_process_done(ctx, vb_state);
+}
+
+static void mdp_m2m_device_run(void *priv)
+{
+	struct mdp_m2m_ctx *ctx = priv;
+
+	queue_work(ctx->mdp_dev->job_wq, &ctx->work);
+}
+
+static int mdp_m2m_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+	struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(q);
+	int ret;
+
+	ret = 0;//pm_runtime_get_sync(&ctx->mdp_dev->pdev->dev);
+	if (ret < 0)
+		mdp_dbg(1, "[%d] pm_runtime_get_sync failed:%d", ctx->id, ret);
+
+	ctx->frame_count = 0;
+
+	return 0;
+}
+
+static struct vb2_buffer *mdp_m2m_buf_remove(struct mdp_m2m_ctx *ctx,
+					     unsigned int type)
+{
+	if (V4L2_TYPE_IS_OUTPUT(type))
+		return v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+	return v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
+}
+
+static void mdp_m2m_stop_streaming(struct vb2_queue *q)
+{
+	struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(q);
+	struct vb2_buffer *vb;
+
+	vb = mdp_m2m_buf_remove(ctx, q->type);
+	while (vb) {
+		v4l2_m2m_buf_done(to_vb2_v4l2_buffer(vb), VB2_BUF_STATE_ERROR);
+		vb = mdp_m2m_buf_remove(ctx, q->type);
+	}
+
+	//pm_runtime_put(&ctx->mdp_dev->pdev->dev);
+}
+
+static int mdp_m2m_queue_setup(struct vb2_queue *q,
+			       unsigned int *num_buffers,
+			       unsigned int *num_planes, unsigned int sizes[],
+			       struct device *alloc_devs[])
+{
+	struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(q);
+	struct v4l2_pix_format_mplane *pix_mp;
+	u32 i;
+
+	pix_mp = &ctx_get_frame(ctx, q->type)->format.fmt.pix_mp;
+
+	/* from VIDIOC_CREATE_BUFS */
+	if (*num_planes) {
+		if (*num_planes != pix_mp->num_planes)
+			return -EINVAL;
+		for (i = 0; i < pix_mp->num_planes; ++i)
+			if (sizes[i] < pix_mp->plane_fmt[i].sizeimage)
+				return -EINVAL;
+	} else {/* from VIDIOC_REQBUFS */
+		*num_planes = pix_mp->num_planes;
+		for (i = 0; i < pix_mp->num_planes; ++i)
+			sizes[i] = pix_mp->plane_fmt[i].sizeimage;
+	}
+
+	mdp_dbg(2, "[%d] type:%d, planes:%u, buffers:%u, size:%u,%u,%u",
+		ctx->id, q->type, *num_planes, *num_buffers,
+		sizes[0], sizes[1], sizes[2]);
+	return 0;
+}
+
+static int mdp_m2m_buf_prepare(struct vb2_buffer *vb)
+{
+	struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+	struct v4l2_pix_format_mplane *pix_mp;
+	struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
+	u32 i;
+
+	v4l2_buf->field = V4L2_FIELD_NONE;
+
+	if (!V4L2_TYPE_IS_OUTPUT(vb->type)) {
+		pix_mp = &ctx_get_frame(ctx, vb->type)->format.fmt.pix_mp;
+		for (i = 0; i < pix_mp->num_planes; ++i) {
+			vb2_set_plane_payload(vb, i,
+					      pix_mp->plane_fmt[i].sizeimage);
+		}
+	}
+	return 0;
+}
+
+static int mdp_m2m_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 mdp_m2m_buf_queue(struct vb2_buffer *vb)
+{
+	struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+	struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
+
+	v4l2_buf->field = V4L2_FIELD_NONE;
+
+	v4l2_m2m_buf_queue(ctx->m2m_ctx, to_vb2_v4l2_buffer(vb));
+}
+
+static const struct vb2_ops mdp_m2m_qops = {
+	.queue_setup	= mdp_m2m_queue_setup,
+	.wait_prepare	= mdp_m2m_ctx_unlock,
+	.wait_finish	= mdp_m2m_ctx_lock,
+	.buf_prepare	= mdp_m2m_buf_prepare,
+	.start_streaming = mdp_m2m_start_streaming,
+	.stop_streaming	= mdp_m2m_stop_streaming,
+	.buf_queue	= mdp_m2m_buf_queue,
+	.buf_out_validate = mdp_m2m_buf_out_validate,
+};
+
+static int mdp_m2m_querycap(struct file *file, void *fh,
+			    struct v4l2_capability *cap)
+{
+	struct mdp_m2m_ctx *ctx = fh_to_ctx(fh);
+
+	strlcpy(cap->driver, MDP_MODULE_NAME, sizeof(cap->driver));
+	strlcpy(cap->card, ctx->mdp_dev->pdev->name, sizeof(cap->card));
+	strlcpy(cap->bus_info, "platform:mt8183", sizeof(cap->bus_info));
+	cap->capabilities = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING |
+			V4L2_CAP_DEVICE_CAPS; /* | V4L2_CAP_META_OUTPUT */
+	cap->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
+	return 0;
+}
+
+static int mdp_m2m_enum_fmt_mplane(struct file *file, void *fh,
+				   struct v4l2_fmtdesc *f)
+{
+	return mdp_enum_fmt_mplane(f);
+}
+
+static int mdp_m2m_g_fmt_mplane(struct file *file, void *fh,
+				struct v4l2_format *f)
+{
+	struct mdp_m2m_ctx *ctx = fh_to_ctx(fh);
+	struct mdp_frame *frame;
+	struct v4l2_pix_format_mplane *pix_mp;
+
+	frame = ctx_get_frame(ctx, f->type);
+	*f = frame->format;
+	pix_mp = &f->fmt.pix_mp;
+	pix_mp->colorspace = ctx->curr_param->colorspace;
+	pix_mp->xfer_func = ctx->curr_param->xfer_func;
+	pix_mp->ycbcr_enc = ctx->curr_param->ycbcr_enc;
+	pix_mp->quantization = ctx->curr_param->quant;
+
+	mdp_dbg(2, "[%d] type:%d, frame:%ux%u colorspace=%d", ctx->id, f->type,
+		f->fmt.pix_mp.width, f->fmt.pix_mp.height,
+		f->fmt.pix_mp.colorspace);
+	return 0;
+}
+
+static int mdp_m2m_s_fmt_mplane(struct file *file, void *fh,
+				struct v4l2_format *f)
+{
+	struct mdp_m2m_ctx *ctx = fh_to_ctx(fh);
+	struct mdp_frame *frame = ctx_get_frame(ctx, f->type);
+	struct mdp_frame *capture;
+	const struct mdp_format *fmt;
+	struct vb2_queue *vq;
+
+	mdp_dbg(2, "[%d] type:%d", ctx->id, f->type);
+
+	fmt = mdp_try_fmt_mplane(f, ctx->curr_param, ctx->id);
+	if (!fmt) {
+		mdp_err("[%d] try_fmt failed, type:%d", ctx->id, f->type);
+		return -EINVAL;
+	}
+
+	vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+	if (vb2_is_streaming(vq)) {
+		dev_info(&ctx->mdp_dev->pdev->dev, "Queue %d busy\n", f->type);
+		return -EBUSY;
+	}
+
+	frame->format = *f;
+	frame->mdp_fmt = fmt;
+	frame->ycbcr_prof = mdp_map_ycbcr_prof_mplane(f, fmt->mdp_color);
+	frame->usage = V4L2_TYPE_IS_OUTPUT(f->type) ?
+		MDP_BUFFER_USAGE_HW_READ : MDP_BUFFER_USAGE_MDP;
+
+	capture = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+	if (V4L2_TYPE_IS_OUTPUT(f->type)) {
+		capture->crop.c.left = 0;
+		capture->crop.c.top = 0;
+		capture->crop.c.width = f->fmt.pix_mp.width;
+		capture->crop.c.height = f->fmt.pix_mp.height;
+		ctx->curr_param->colorspace = f->fmt.pix_mp.colorspace;
+		ctx->curr_param->ycbcr_enc = f->fmt.pix_mp.ycbcr_enc;
+		ctx->curr_param->quant = f->fmt.pix_mp.quantization;
+		ctx->curr_param->xfer_func = f->fmt.pix_mp.xfer_func;
+
+		mdp_m2m_ctx_set_state(ctx, MDP_M2M_SRC_FMT);
+	} else {
+		capture->compose.left = 0;
+		capture->compose.top = 0;
+		capture->compose.width = f->fmt.pix_mp.width;
+		capture->compose.height = f->fmt.pix_mp.height;
+
+		mdp_m2m_ctx_set_state(ctx, MDP_M2M_DST_FMT);
+	}
+
+	ctx->frame_count = 0;
+
+	mdp_dbg(2, "[%d] type:%d, frame:%ux%u", ctx->id, f->type,
+		f->fmt.pix_mp.width, f->fmt.pix_mp.height);
+	return 0;
+}
+
+static int mdp_m2m_try_fmt_mplane(struct file *file, void *fh,
+				  struct v4l2_format *f)
+{
+	struct mdp_m2m_ctx *ctx = fh_to_ctx(fh);
+
+	if (!mdp_try_fmt_mplane(f, ctx->curr_param, ctx->id))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int mdp_m2m_reqbufs(struct file *file, void *fh,
+			   struct v4l2_requestbuffers *reqbufs)
+{
+	struct mdp_m2m_ctx *ctx = fh_to_ctx(fh);
+
+	return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
+}
+
+static int mdp_m2m_streamon(struct file *file, void *fh,
+			    enum v4l2_buf_type type)
+{
+	struct mdp_m2m_ctx *ctx = fh_to_ctx(fh);
+	int ret;
+
+	/* The source and target color formats need to be set */
+	if (V4L2_TYPE_IS_OUTPUT(type)) {
+		if (!mdp_m2m_ctx_is_state_set(ctx, MDP_M2M_SRC_FMT))
+			return -EINVAL;
+	} else {
+		if (!mdp_m2m_ctx_is_state_set(ctx, MDP_M2M_DST_FMT))
+			return -EINVAL;
+	}
+
+	if (!mdp_m2m_ctx_is_state_set(ctx, MDP_VPU_INIT)) {
+		ret = mdp_vpu_ctx_init(&ctx->vpu, &ctx->mdp_dev->vpu,
+				       MDP_DEV_M2M);
+		if (ret) {
+			dev_err(&ctx->mdp_dev->pdev->dev,
+				"VPU init failed %d\n", ret);
+			return -EINVAL;
+		}
+		mdp_m2m_ctx_set_state(ctx, MDP_VPU_INIT);
+	}
+
+	return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
+}
+
+static int mdp_m2m_g_selection(struct file *file, void *fh,
+			       struct v4l2_selection *s)
+{
+	struct mdp_m2m_ctx *ctx = fh_to_ctx(fh);
+	struct mdp_frame *frame;
+	bool valid = false;
+
+	if (s->type <= V4L2_BUF_TYPE_META_OUTPUT) {
+		if (V4L2_TYPE_IS_OUTPUT(s->type))
+			valid = mdp_target_is_crop(s->target);
+		else
+			valid = mdp_target_is_compose(s->target);
+	}
+
+	if (!valid) {
+		mdp_dbg(1, "[%d] invalid type:%u target:%u", ctx->id, s->type,
+			s->target);
+		return -EINVAL;
+	}
+
+	switch (s->target) {
+	case V4L2_SEL_TGT_CROP:
+		frame = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+		s->r = frame->crop.c;
+		return 0;
+	case V4L2_SEL_TGT_COMPOSE:
+		frame = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+		s->r = frame->compose;
+		return 0;
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+	case V4L2_SEL_TGT_CROP_BOUNDS:
+	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+		frame = ctx_get_frame(ctx, s->type);
+		s->r.left = 0;
+		s->r.top = 0;
+		s->r.width = frame->format.fmt.pix_mp.width;
+		s->r.height = frame->format.fmt.pix_mp.height;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int mdp_m2m_s_selection(struct file *file, void *fh,
+			       struct v4l2_selection *s)
+{
+	struct mdp_m2m_ctx *ctx = fh_to_ctx(fh);
+	struct mdp_frame *frame = ctx_get_frame(ctx, s->type);
+	struct mdp_frame *capture;
+	struct v4l2_rect r;
+	bool valid = false;
+	int ret;
+
+	if (s->type <= V4L2_BUF_TYPE_META_OUTPUT) {
+		if (V4L2_TYPE_IS_OUTPUT(s->type))
+			valid = (s->target == V4L2_SEL_TGT_CROP);
+		else
+			valid = (s->target == V4L2_SEL_TGT_COMPOSE);
+	}
+	if (!valid) {
+		mdp_dbg(1, "[%d] invalid type:%u target:%u", ctx->id, s->type,
+			s->target);
+		return -EINVAL;
+	}
+
+	ret = mdp_try_crop(&r, s, frame, ctx->id);
+	if (ret)
+		return ret;
+	capture = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+
+	/* Check to see if scaling ratio is within supported range */
+	if (mdp_m2m_ctx_is_state_set(ctx, MDP_M2M_DST_FMT | MDP_M2M_SRC_FMT)) {
+		if (mdp_target_is_crop(s->target)) {
+			ret = mdp_check_scaling_ratio(&r, &capture->compose,
+						      capture->rotation,
+						      ctx->curr_param->limit);
+		} else {
+			ret = mdp_check_scaling_ratio(&capture->crop.c, &r,
+						      capture->rotation,
+						      ctx->curr_param->limit);
+		}
+
+		if (ret) {
+			dev_info(&ctx->mdp_dev->pdev->dev,
+				 "Out of scaling range\n");
+			return ret;
+		}
+	}
+
+	if (mdp_target_is_crop(s->target))
+		capture->crop.c = r;
+	else
+		capture->compose = r;
+
+	s->r = r;
+	memset(s->reserved, 0, sizeof(s->reserved));
+
+	ctx->frame_count = 0;
+	return 0;
+}
+
+static const struct v4l2_ioctl_ops mdp_m2m_ioctl_ops = {
+	.vidioc_querycap		= mdp_m2m_querycap,
+	.vidioc_enum_fmt_vid_cap_mplane	= mdp_m2m_enum_fmt_mplane,
+	.vidioc_enum_fmt_vid_out_mplane	= mdp_m2m_enum_fmt_mplane,
+	.vidioc_g_fmt_vid_cap_mplane	= mdp_m2m_g_fmt_mplane,
+	.vidioc_g_fmt_vid_out_mplane	= mdp_m2m_g_fmt_mplane,
+	.vidioc_s_fmt_vid_cap_mplane	= mdp_m2m_s_fmt_mplane,
+	.vidioc_s_fmt_vid_out_mplane	= mdp_m2m_s_fmt_mplane,
+	.vidioc_try_fmt_vid_cap_mplane	= mdp_m2m_try_fmt_mplane,
+	.vidioc_try_fmt_vid_out_mplane	= mdp_m2m_try_fmt_mplane,
+	.vidioc_reqbufs			= mdp_m2m_reqbufs,
+	.vidioc_querybuf		= v4l2_m2m_ioctl_querybuf,
+	.vidioc_qbuf			= v4l2_m2m_ioctl_qbuf,
+	.vidioc_expbuf			= v4l2_m2m_ioctl_expbuf,
+	.vidioc_dqbuf			= v4l2_m2m_ioctl_dqbuf,
+	.vidioc_create_bufs		= v4l2_m2m_ioctl_create_bufs,
+	.vidioc_streamon		= mdp_m2m_streamon,
+	.vidioc_streamoff		= v4l2_m2m_ioctl_streamoff,
+	.vidioc_g_selection		= mdp_m2m_g_selection,
+	.vidioc_s_selection		= mdp_m2m_s_selection,
+	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,
+	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
+};
+
+static int mdp_m2m_queue_init(void *priv,
+			      struct vb2_queue *src_vq,
+			      struct vb2_queue *dst_vq)
+{
+	struct mdp_m2m_ctx *ctx = priv;
+	int ret;
+
+	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+	src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+	src_vq->ops = &mdp_m2m_qops;
+	src_vq->mem_ops = &vb2_dma_contig_memops;
+	src_vq->drv_priv = ctx;
+	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+	src_vq->dev = &ctx->mdp_dev->pdev->dev;
+
+	ret = vb2_queue_init(src_vq);
+	if (ret)
+		return ret;
+
+	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+	dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+	dst_vq->ops = &mdp_m2m_qops;
+	dst_vq->mem_ops = &vb2_dma_contig_memops;
+	dst_vq->drv_priv = ctx;
+	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+	dst_vq->dev = &ctx->mdp_dev->pdev->dev;
+
+	return vb2_queue_init(dst_vq);
+}
+
+static int mdp_m2m_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct mdp_m2m_ctx *ctx = ctrl_to_ctx(ctrl);
+	struct mdp_frame *capture;
+
+	if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
+		return 0;
+
+	capture = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+	switch (ctrl->id) {
+	case V4L2_CID_HFLIP:
+		capture->hflip = ctrl->val;
+		break;
+	case V4L2_CID_VFLIP:
+		capture->vflip = ctrl->val;
+		break;
+	case V4L2_CID_ROTATE:
+		if (mdp_m2m_ctx_is_state_set(ctx,
+					     MDP_M2M_DST_FMT |
+					     MDP_M2M_SRC_FMT)) {
+			int ret = mdp_check_scaling_ratio(&capture->crop.c,
+				&capture->compose, ctrl->val,
+				ctx->curr_param->limit);
+
+			if (ret)
+				return ret;
+		}
+		capture->rotation = ctrl->val;
+		break;
+	}
+
+	return 0;
+}
+
+static const struct v4l2_ctrl_ops mdp_m2m_ctrl_ops = {
+	.s_ctrl	= mdp_m2m_s_ctrl,
+};
+
+static int mdp_m2m_ctrls_create(struct mdp_m2m_ctx *ctx)
+{
+	v4l2_ctrl_handler_init(&ctx->ctrl_handler, MDP_MAX_CTRLS);
+	ctx->ctrls.hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,
+					     &mdp_m2m_ctrl_ops, V4L2_CID_HFLIP,
+					     0, 1, 1, 0);
+	ctx->ctrls.vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,
+					     &mdp_m2m_ctrl_ops, V4L2_CID_VFLIP,
+					     0, 1, 1, 0);
+	ctx->ctrls.rotate = v4l2_ctrl_new_std(&ctx->ctrl_handler,
+					      &mdp_m2m_ctrl_ops,
+					      V4L2_CID_ROTATE, 0, 270, 90, 0);
+
+	if (ctx->ctrl_handler.error) {
+		int err = ctx->ctrl_handler.error;
+
+		v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+		dev_err(&ctx->mdp_dev->pdev->dev,
+			"Failed to create control handler\n");
+		return err;
+	}
+	return 0;
+}
+
+static int mdp_m2m_open(struct file *file)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct mdp_dev *mdp = video_get_drvdata(vdev);
+	struct mdp_m2m_ctx *ctx;
+	int ret;
+
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	if (mutex_lock_interruptible(&mdp->m2m_lock)) {
+		ret = -ERESTARTSYS;
+		goto err_lock;
+	}
+
+	ctx->id = mdp->id_count++;
+	ctx->mdp_dev = mdp;
+
+	v4l2_fh_init(&ctx->fh, vdev);
+	file->private_data = &ctx->fh;
+	ret = mdp_m2m_ctrls_create(ctx);
+	if (ret)
+		goto err_ctrls_create;
+
+	/* Use separate control handler per file handle */
+	ctx->fh.ctrl_handler = &ctx->ctrl_handler;
+	v4l2_fh_add(&ctx->fh);
+
+	ctx->m2m_ctx = v4l2_m2m_ctx_init(mdp->m2m_dev, ctx, mdp_m2m_queue_init);
+	if (IS_ERR(ctx->m2m_ctx)) {
+		dev_err(&mdp->pdev->dev, "Failed to initialize m2m context\n");
+		ret = PTR_ERR(ctx->m2m_ctx);
+		goto err_m2m_ctx;
+	}
+	ctx->fh.m2m_ctx = ctx->m2m_ctx;
+
+	INIT_WORK(&ctx->work, mdp_m2m_worker);
+	ctx->frame_count = 0;
+
+	ctx->curr_param = mdp_frameparam_init();
+	if (IS_ERR(ctx->curr_param)) {
+		dev_err(&mdp->pdev->dev,
+			"Failed to initialize mdp parameter\n");
+		ret = PTR_ERR(ctx->curr_param);
+		goto err_param_init;
+	}
+	ctx->curr_param->type = MDP_STREAM_TYPE_BITBLT;
+
+	INIT_LIST_HEAD(&ctx->param_list);
+	list_add_tail(&ctx->curr_param->list, &ctx->param_list);
+
+	ret = mdp_vpu_get_locked(mdp);
+	if (ret < 0)
+		goto err_load_vpu;
+
+	mutex_unlock(&mdp->m2m_lock);
+
+	mdp_dbg(0, "%s [%d]", dev_name(&mdp->pdev->dev), ctx->id);
+
+	return 0;
+
+err_load_vpu:
+	mdp_frameparam_release(ctx->curr_param);
+err_param_init:
+	v4l2_m2m_ctx_release(ctx->m2m_ctx);
+err_m2m_ctx:
+	v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+	v4l2_fh_del(&ctx->fh);
+err_ctrls_create:
+	v4l2_fh_exit(&ctx->fh);
+	mutex_unlock(&mdp->m2m_lock);
+err_lock:
+	kfree(ctx);
+
+	return ret;
+}
+
+static int mdp_m2m_release(struct file *file)
+{
+	struct mdp_m2m_ctx *ctx = fh_to_ctx(file->private_data);
+	struct mdp_dev *mdp = video_drvdata(file);
+	struct mdp_frameparam *param, *n;
+
+	flush_workqueue(mdp->job_wq);
+	mutex_lock(&mdp->m2m_lock);
+	if (mdp_m2m_ctx_is_state_set(ctx, MDP_VPU_INIT))
+		mdp_vpu_ctx_deinit(&ctx->vpu);
+	mdp_vpu_put_locked(mdp);
+	list_for_each_entry_safe(param, n, &ctx->param_list, list) {
+		mdp_frameparam_release(param);
+	}
+
+	v4l2_m2m_ctx_release(ctx->m2m_ctx);
+	v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+	v4l2_fh_del(&ctx->fh);
+	v4l2_fh_exit(&ctx->fh);
+	mutex_unlock(&mdp->m2m_lock);
+
+	mdp_dbg(0, "%s [%d]", dev_name(&mdp->pdev->dev), ctx->id);
+	kfree(ctx);
+
+	return 0;
+}
+
+static const struct v4l2_file_operations mdp_m2m_fops = {
+	.owner		= THIS_MODULE,
+	.poll		= v4l2_m2m_fop_poll,
+	.unlocked_ioctl	= video_ioctl2,
+	.mmap		= v4l2_m2m_fop_mmap,
+	.open		= mdp_m2m_open,
+	.release	= mdp_m2m_release,
+};
+
+static const struct v4l2_m2m_ops mdp_m2m_ops = {
+	.device_run	= mdp_m2m_device_run,
+	.job_abort	= mdp_m2m_job_abort,
+};
+
+int mdp_m2m_device_register(struct mdp_dev *mdp)
+{
+	struct device *dev = &mdp->pdev->dev;
+	int ret = 0;
+
+	mdp->m2m_vdev = video_device_alloc();
+	if (!mdp->m2m_vdev) {
+		dev_err(dev, "Failed to allocate video device\n");
+		ret = -ENOMEM;
+		goto err_video_alloc;
+	}
+	//mdp->m2m_vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE |
+	//	V4L2_CAP_STREAMING;
+	mdp->m2m_vdev->fops = &mdp_m2m_fops;
+	mdp->m2m_vdev->ioctl_ops = &mdp_m2m_ioctl_ops;
+	mdp->m2m_vdev->release = video_device_release;
+	mdp->m2m_vdev->lock = &mdp->m2m_lock;
+	mdp->m2m_vdev->vfl_dir = VFL_DIR_M2M;
+	mdp->m2m_vdev->v4l2_dev = &mdp->v4l2_dev;
+	snprintf(mdp->m2m_vdev->name, sizeof(mdp->m2m_vdev->name), "%s:m2m",
+		 MDP_MODULE_NAME);
+	video_set_drvdata(mdp->m2m_vdev, mdp);
+
+	mdp->m2m_dev = v4l2_m2m_init(&mdp_m2m_ops);
+	if (IS_ERR(mdp->m2m_dev)) {
+		dev_err(dev, "Failed to initialize v4l2-m2m device\n");
+		ret = PTR_ERR(mdp->m2m_dev);
+		goto err_m2m_init;
+	}
+
+	ret = video_register_device(mdp->m2m_vdev, VFL_TYPE_GRABBER, 2);
+	if (ret) {
+		dev_err(dev, "Failed to register video device\n");
+		goto err_video_register;
+	}
+
+	v4l2_info(&mdp->v4l2_dev, "Driver registered as /dev/video%d",
+		  mdp->m2m_vdev->num);
+	return 0;
+
+err_video_register:
+	v4l2_m2m_release(mdp->m2m_dev);
+err_m2m_init:
+	video_device_release(mdp->m2m_vdev);
+err_video_alloc:
+
+	return ret;
+}
+
+void mdp_m2m_device_unregister(struct mdp_dev *mdp)
+{
+	video_unregister_device(mdp->m2m_vdev);
+	video_device_release(mdp->m2m_vdev);
+	v4l2_m2m_release(mdp->m2m_dev);
+}
+
diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-m2m.h b/drivers/media/platform/mtk-mdp3/mtk-mdp3-m2m.h
new file mode 100644
index 000000000000..1f681b48c2ad
--- /dev/null
+++ b/drivers/media/platform/mtk-mdp3/mtk-mdp3-m2m.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Ping-Hsun Wu <ping-hsun.wu@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_MDP3_M2M_H__
+#define __MTK_MDP3_M2M_H__
+
+#include <media/v4l2-ctrls.h>
+#include "mtk-mdp3-core.h"
+#include "mtk-mdp3-vpu.h"
+#include "mtk-mdp3-regs.h"
+
+#define MDP_MAX_CTRLS	10
+
+struct mdp_m2m_ctrls {
+	struct v4l2_ctrl	*hflip;
+	struct v4l2_ctrl	*vflip;
+	/* struct v4l2_ctrl	*sharpness; */
+	struct v4l2_ctrl	*rotate;
+};
+
+struct mdp_m2m_ctx {
+	u32				id;
+	struct mdp_dev			*mdp_dev;
+	struct v4l2_fh			fh;
+	struct v4l2_ctrl_handler	ctrl_handler;
+	struct mdp_m2m_ctrls		ctrls;
+	struct v4l2_m2m_ctx		*m2m_ctx;
+	struct mdp_vpu_ctx		vpu;
+	struct work_struct		work;
+	u32				frame_count;
+
+	struct mdp_frameparam		*curr_param;
+	struct list_head		param_list;
+};
+
+int mdp_m2m_device_register(struct mdp_dev *mdp);
+void mdp_m2m_device_unregister(struct mdp_dev *mdp);
+
+#endif  /* __MTK_MDP3_M2M_H__ */
+
diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-regs.c b/drivers/media/platform/mtk-mdp3/mtk-mdp3-regs.c
new file mode 100644
index 000000000000..b623eda06c7d
--- /dev/null
+++ b/drivers/media/platform/mtk-mdp3/mtk-mdp3-regs.c
@@ -0,0 +1,757 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Ping-Hsun Wu <ping-hsun.wu@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 <media/v4l2-common.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-dma-contig.h>
+#include "mtk-mdp3-core.h"
+#include "mtk-mdp3-regs.h"
+
+static const struct mdp_format mdp_formats[] = {
+	{
+		.pixelformat	= V4L2_PIX_FMT_GREY,
+		.mdp_color	= MDP_COLOR_GREY,
+		.depth		= { 8 },
+		.row_depth	= { 8 },
+		.num_planes	= 1,
+		.flags		= MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
+	}, {
+		.pixelformat	= V4L2_PIX_FMT_RGB565X,
+		.mdp_color	= MDP_COLOR_RGB565,
+		.depth		= { 16 },
+		.row_depth	= { 16 },
+		.num_planes	= 1,
+		.flags		= MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
+	}, {
+		.pixelformat	= V4L2_PIX_FMT_RGB565,
+		.mdp_color	= MDP_COLOR_BGR565,
+		.depth		= { 16 },
+		.row_depth	= { 16 },
+		.num_planes	= 1,
+		.flags		= MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
+	}, {
+		.pixelformat	= V4L2_PIX_FMT_RGB24,
+		.mdp_color	= MDP_COLOR_RGB888,
+		.depth		= { 24 },
+		.row_depth	= { 24 },
+		.num_planes	= 1,
+		.flags		= MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
+	}, {
+		.pixelformat	= V4L2_PIX_FMT_BGR24,
+		.mdp_color	= MDP_COLOR_BGR888,
+		.depth		= { 24 },
+		.row_depth	= { 24 },
+		.num_planes	= 1,
+		.flags		= MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
+	}, {
+		.pixelformat	= V4L2_PIX_FMT_ABGR32,
+		.mdp_color	= MDP_COLOR_BGRA8888,
+		.depth		= { 32 },
+		.row_depth	= { 32 },
+		.num_planes	= 1,
+		.flags		= MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
+	}, {
+		.pixelformat	= V4L2_PIX_FMT_ARGB32,
+		.mdp_color	= MDP_COLOR_ARGB8888,
+		.depth		= { 32 },
+		.row_depth	= { 32 },
+		.num_planes	= 1,
+		.flags		= MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
+	}, {
+		.pixelformat	= V4L2_PIX_FMT_UYVY,
+		.mdp_color	= MDP_COLOR_UYVY,
+		.depth		= { 16 },
+		.row_depth	= { 16 },
+		.num_planes	= 1,
+		.walign		= 1,
+		.flags		= MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
+	}, {
+		.pixelformat	= V4L2_PIX_FMT_VYUY,
+		.mdp_color	= MDP_COLOR_VYUY,
+		.depth		= { 16 },
+		.row_depth	= { 16 },
+		.num_planes	= 1,
+		.walign		= 1,
+		.flags		= MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
+	}, {
+		.pixelformat	= V4L2_PIX_FMT_YUYV,
+		.mdp_color	= MDP_COLOR_YUYV,
+		.depth		= { 16 },
+		.row_depth	= { 16 },
+		.num_planes	= 1,
+		.walign		= 1,
+		.flags		= MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
+	}, {
+		.pixelformat	= V4L2_PIX_FMT_YVYU,
+		.mdp_color	= MDP_COLOR_YVYU,
+		.depth		= { 16 },
+		.row_depth	= { 16 },
+		.num_planes	= 1,
+		.walign		= 1,
+		.flags		= MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
+	}, {
+		.pixelformat	= V4L2_PIX_FMT_YUV420,
+		.mdp_color	= MDP_COLOR_I420,
+		.depth		= { 12 },
+		.row_depth	= { 8 },
+		.num_planes	= 1,
+		.walign		= 1,
+		.halign		= 1,
+		.flags		= MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
+	}, {
+		.pixelformat	= V4L2_PIX_FMT_YVU420,
+		.mdp_color	= MDP_COLOR_YV12,
+		.depth		= { 12 },
+		.row_depth	= { 8 },
+		.num_planes	= 1,
+		.walign		= 1,
+		.halign		= 1,
+		.flags		= MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
+	}, {
+		.pixelformat	= V4L2_PIX_FMT_NV12,
+		.mdp_color	= MDP_COLOR_NV12,
+		.depth		= { 12 },
+		.row_depth	= { 8 },
+		.num_planes	= 1,
+		.walign		= 1,
+		.halign		= 1,
+		.flags		= MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
+	}, {
+		.pixelformat	= V4L2_PIX_FMT_NV21,
+		.mdp_color	= MDP_COLOR_NV21,
+		.depth		= { 12 },
+		.row_depth	= { 8 },
+		.num_planes	= 1,
+		.walign		= 1,
+		.halign		= 1,
+		.flags		= MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
+	}, {
+		.pixelformat	= V4L2_PIX_FMT_NV16,
+		.mdp_color	= MDP_COLOR_NV16,
+		.depth		= { 16 },
+		.row_depth	= { 8 },
+		.num_planes	= 1,
+		.walign		= 1,
+		.flags		= MDP_FMT_FLAG_OUTPUT,
+	}, {
+		.pixelformat	= V4L2_PIX_FMT_NV61,
+		.mdp_color	= MDP_COLOR_NV61,
+		.depth		= { 16 },
+		.row_depth	= { 8 },
+		.num_planes	= 1,
+		.walign		= 1,
+		.flags		= MDP_FMT_FLAG_OUTPUT,
+	}, {
+		.pixelformat	= V4L2_PIX_FMT_NV24,
+		.mdp_color	= MDP_COLOR_NV24,
+		.depth		= { 24 },
+		.row_depth	= { 8 },
+		.num_planes	= 1,
+		.flags		= MDP_FMT_FLAG_OUTPUT,
+	}, {
+		.pixelformat	= V4L2_PIX_FMT_NV42,
+		.mdp_color	= MDP_COLOR_NV42,
+		.depth		= { 24 },
+		.row_depth	= { 8 },
+		.num_planes	= 1,
+		.flags		= MDP_FMT_FLAG_OUTPUT,
+	}, {
+		.pixelformat	= V4L2_PIX_FMT_MT21C,
+		.mdp_color	= MDP_COLOR_420_BLK_UFO,
+		.depth		= { 8, 4 },
+		.row_depth	= { 8, 8 },
+		.num_planes	= 2,
+		.walign		= 4,
+		.halign		= 5,
+		.flags		= MDP_FMT_FLAG_OUTPUT,
+	}, {
+		.pixelformat	= V4L2_PIX_FMT_NV12MT,
+		.mdp_color	= MDP_COLOR_420_BLK,
+		.depth		= { 8, 4 },
+		.row_depth	= { 8, 8 },
+		.num_planes	= 2,
+		.walign		= 4,
+		.halign		= 5,
+		.flags		= MDP_FMT_FLAG_OUTPUT,
+	}, {
+		.pixelformat	= V4L2_PIX_FMT_NV12M,
+		.mdp_color	= MDP_COLOR_NV12,
+		.depth		= { 8, 4 },
+		.row_depth	= { 8, 8 },
+		.num_planes	= 2,
+		.walign		= 1,
+		.halign		= 1,
+		.flags		= MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
+	}, {
+		.pixelformat	= V4L2_PIX_FMT_NV21M,
+		.mdp_color	= MDP_COLOR_NV21,
+		.depth		= { 8, 4 },
+		.row_depth	= { 8, 8 },
+		.num_planes	= 2,
+		.walign		= 1,
+		.halign		= 1,
+		.flags		= MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
+	}, {
+		.pixelformat	= V4L2_PIX_FMT_NV16M,
+		.mdp_color	= MDP_COLOR_NV16,
+		.depth		= { 8, 8 },
+		.row_depth	= { 8, 8 },
+		.num_planes	= 2,
+		.walign		= 1,
+		.flags		= MDP_FMT_FLAG_OUTPUT,
+	}, {
+		.pixelformat	= V4L2_PIX_FMT_NV61M,
+		.mdp_color	= MDP_COLOR_NV61,
+		.depth		= { 8, 8 },
+		.row_depth	= { 8, 8 },
+		.num_planes	= 2,
+		.walign		= 1,
+		.flags		= MDP_FMT_FLAG_OUTPUT,
+	}, {
+		.pixelformat	= V4L2_PIX_FMT_YUV420M,
+		.mdp_color	= MDP_COLOR_I420,
+		.depth		= { 8, 2, 2 },
+		.row_depth	= { 8, 4, 4 },
+		.num_planes	= 3,
+		.walign		= 1,
+		.halign		= 1,
+		.flags		= MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
+	}, {
+		.pixelformat	= V4L2_PIX_FMT_YVU420M,
+		.mdp_color	= MDP_COLOR_YV12,
+		.depth		= { 8, 2, 2 },
+		.row_depth	= { 8, 4, 4 },
+		.num_planes	= 3,
+		.walign		= 1,
+		.halign		= 1,
+		.flags		= MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
+	}
+};
+
+static const struct mdp_limit mdp_def_limit = {
+	.out_limit = {
+		.wmin	= 16,
+		.hmin	= 16,
+		.wmax	= 8176,
+		.hmax	= 8176,
+	},
+	.cap_limit = {
+		.wmin	= 2,
+		.hmin	= 2,
+		.wmax	= 8176,
+		.hmax	= 8176,
+	},
+	.h_scale_up_max = 32,
+	.v_scale_up_max = 32,
+	.h_scale_down_max = 20,
+	.v_scale_down_max = 128,
+};
+
+static const struct mdp_format *mdp_find_fmt(u32 pixelformat, u32 type)
+{
+	u32 i, flag;
+
+	flag = V4L2_TYPE_IS_OUTPUT(type) ? MDP_FMT_FLAG_OUTPUT :
+					MDP_FMT_FLAG_CAPTURE;
+	for (i = 0; i < ARRAY_SIZE(mdp_formats); ++i) {
+		if (!(mdp_formats[i].flags & flag))
+			continue;
+		if (mdp_formats[i].pixelformat == pixelformat)
+			return &mdp_formats[i];
+	}
+	return NULL;
+}
+
+static const struct mdp_format *mdp_find_fmt_by_index(u32 index, u32 type)
+{
+	u32 i, flag, num = 0;
+
+	flag = V4L2_TYPE_IS_OUTPUT(type) ? MDP_FMT_FLAG_OUTPUT :
+					MDP_FMT_FLAG_CAPTURE;
+	for (i = 0; i < ARRAY_SIZE(mdp_formats); ++i) {
+		if (!(mdp_formats[i].flags & flag))
+			continue;
+		if (index == num)
+			return &mdp_formats[i];
+		num++;
+	}
+	return NULL;
+}
+
+enum mdp_ycbcr_profile mdp_map_ycbcr_prof_mplane(struct v4l2_format *f,
+						 u32 mdp_color)
+{
+	struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+
+	if (MDP_COLOR_IS_RGB(mdp_color))
+		return MDP_YCBCR_PROFILE_FULL_BT601;
+
+	switch (pix_mp->colorspace) {
+	case V4L2_COLORSPACE_JPEG:
+		return MDP_YCBCR_PROFILE_JPEG;
+	case V4L2_COLORSPACE_REC709:
+	case V4L2_COLORSPACE_DCI_P3:
+		if (pix_mp->quantization == V4L2_QUANTIZATION_FULL_RANGE)
+			return MDP_YCBCR_PROFILE_FULL_BT709;
+		return MDP_YCBCR_PROFILE_BT709;
+	case V4L2_COLORSPACE_BT2020:
+		if (pix_mp->quantization == V4L2_QUANTIZATION_FULL_RANGE)
+			return MDP_YCBCR_PROFILE_FULL_BT2020;
+		return MDP_YCBCR_PROFILE_BT2020;
+	}
+	/* V4L2_COLORSPACE_SRGB or else */
+	if (pix_mp->quantization == V4L2_QUANTIZATION_FULL_RANGE)
+		return MDP_YCBCR_PROFILE_FULL_BT601;
+	return MDP_YCBCR_PROFILE_BT601;
+}
+
+static void mdp_bound_align_image(u32 *w, unsigned int wmin, unsigned int wmax,
+				  unsigned int walign,
+				u32 *h, unsigned int hmin, unsigned int hmax,
+				unsigned int halign, unsigned int salign)
+{
+	unsigned int org_w, org_h, wstep, hstep;
+
+	org_w = *w;
+	org_h = *h;
+	v4l_bound_align_image(w, wmin, wmax, walign, h, hmin, hmax, halign,
+			      salign);
+
+	wstep = 1 << walign;
+	hstep = 1 << halign;
+	if (*w < org_w && (*w + wstep) <= wmax)
+		*w += wstep;
+	if (*h < org_h && (*h + hstep) <= hmax)
+		*h += hstep;
+}
+
+static int mdp_clamp_align(s32 *x, int min, int max, unsigned int align)
+{
+	unsigned int mask;
+
+	if (min < 0 || max < 0)
+		return -ERANGE;
+
+	/* Bits that must be zero to be aligned */
+	mask = ~((1 << align) - 1);
+
+	min = 0 ? 0 : ((min + ~mask) & mask);
+	max = max & mask;
+	if ((unsigned int)min > (unsigned int)max)
+		return -ERANGE;
+
+	/* Clamp to aligned min and max */
+	*x = clamp(*x, min, max);
+
+	/* Round to nearest aligned value */
+	if (align)
+		*x = (*x + (1 << (align - 1))) & mask;
+	return 0;
+}
+
+int mdp_enum_fmt_mplane(struct v4l2_fmtdesc *f)
+{
+	const struct mdp_format *fmt;
+
+	if (!V4L2_TYPE_IS_MULTIPLANAR(f->type))
+		return -EINVAL;
+
+	fmt = mdp_find_fmt_by_index(f->index, f->type);
+	if (!fmt)
+		return -EINVAL;
+
+	/* f->description */
+	f->pixelformat = fmt->pixelformat;
+	return 0;
+}
+
+const struct mdp_format *mdp_try_fmt_mplane(struct v4l2_format *f,
+					    struct mdp_frameparam *param,
+					    u32 ctx_id)
+{
+	struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+	const struct mdp_format *fmt;
+	const struct mdp_pix_limit *pix_limit;
+	u32 wmin, wmax, hmin, hmax, org_w, org_h;
+	unsigned int i;
+
+	if (!V4L2_TYPE_IS_MULTIPLANAR(f->type))
+		return NULL;
+
+	fmt = mdp_find_fmt(pix_mp->pixelformat, f->type);
+	if (!fmt)
+		fmt = mdp_find_fmt_by_index(0, f->type);
+	if (!fmt) {
+		mdp_dbg(0, "[%d] pixelformat %c%c%c%c invalid", ctx_id,
+			(pix_mp->pixelformat & 0xff),
+			(pix_mp->pixelformat >>  8) & 0xff,
+			(pix_mp->pixelformat >> 16) & 0xff,
+			(pix_mp->pixelformat >> 24) & 0xff);
+		return NULL;
+	}
+
+	pix_mp->field = V4L2_FIELD_NONE;
+	pix_mp->flags = 0;
+	pix_mp->pixelformat = fmt->pixelformat;
+	if (!V4L2_TYPE_IS_OUTPUT(f->type)) {
+		pix_mp->colorspace = param->colorspace;
+		pix_mp->xfer_func = param->xfer_func;
+		pix_mp->ycbcr_enc = param->ycbcr_enc;
+		pix_mp->quantization = param->quant;
+	}
+	memset(pix_mp->reserved, 0, sizeof(pix_mp->reserved));
+
+	pix_limit = V4L2_TYPE_IS_OUTPUT(f->type) ? &param->limit->out_limit :
+		&param->limit->cap_limit;
+	wmin = pix_limit->wmin;
+	wmax = pix_limit->wmax;
+	hmin = pix_limit->hmin;
+	hmax = pix_limit->hmax;
+	org_w = pix_mp->width;
+	org_h = pix_mp->height;
+
+	mdp_bound_align_image(&pix_mp->width, wmin, wmax, fmt->walign,
+			      &pix_mp->height, hmin, hmax, fmt->halign,
+				fmt->salign);
+	if (org_w != pix_mp->width || org_h != pix_mp->height)
+		mdp_dbg(1, "[%d] size change: %ux%u to %ux%u", ctx_id,
+			org_w, org_h, pix_mp->width, pix_mp->height);
+
+	if (pix_mp->num_planes && pix_mp->num_planes != fmt->num_planes)
+		mdp_dbg(1, "[%d] num of planes change: %u to %u", ctx_id,
+			pix_mp->num_planes, fmt->num_planes);
+	pix_mp->num_planes = fmt->num_planes;
+
+	for (i = 0; i < pix_mp->num_planes; ++i) {
+		u32 min_bpl = (pix_mp->width * fmt->row_depth[i]) / 8;
+		u32 bpl = pix_mp->plane_fmt[i].bytesperline;
+		u32 si;
+
+		if (bpl < min_bpl)
+			bpl = min_bpl;
+		si = (bpl * pix_mp->height * fmt->depth[i]) / fmt->row_depth[i];
+
+		pix_mp->plane_fmt[i].bytesperline = bpl;
+		if (pix_mp->plane_fmt[i].sizeimage < si)
+			pix_mp->plane_fmt[i].sizeimage = si;
+		memset(pix_mp->plane_fmt[i].reserved, 0,
+		       sizeof(pix_mp->plane_fmt[i].reserved));
+		mdp_dbg(2, "[%d] p%u, bpl:%u (%u), sizeimage:%u (%u)", ctx_id,
+			i, bpl, min_bpl, pix_mp->plane_fmt[i].sizeimage, si);
+	}
+
+	return fmt;
+}
+
+static inline int mdp_clamp_start(s32 *x, int min, int max, unsigned int align,
+				  u32 flags)
+{
+	if (flags & V4L2_SEL_FLAG_GE)
+		max = *x;
+	if (flags & V4L2_SEL_FLAG_LE)
+		min = *x;
+	return mdp_clamp_align(x, min, max, align);
+}
+
+static inline int mdp_clamp_end(s32 *x, int min, int max, unsigned int align,
+				u32 flags)
+{
+	if (flags & V4L2_SEL_FLAG_GE)
+		min = *x;
+	if (flags & V4L2_SEL_FLAG_LE)
+		max = *x;
+	return mdp_clamp_align(x, min, max, align);
+}
+
+int mdp_try_crop(struct v4l2_rect *r, const struct v4l2_selection *s,
+		 struct mdp_frame *frame, u32 ctx_id)
+{
+	s32 left, top, right, bottom;
+	u32 framew, frameh, walign, halign;
+	int ret;
+
+	mdp_dbg(2, "[%d] target:%d, set:(%d,%d) %ux%u", ctx_id, s->target,
+		s->r.left, s->r.top, s->r.width, s->r.height);
+
+	left = s->r.left;
+	top = s->r.top;
+	right = s->r.left + s->r.width;
+	bottom = s->r.top + s->r.height;
+	framew = frame->format.fmt.pix_mp.width;
+	frameh = frame->format.fmt.pix_mp.height;
+
+	if (mdp_target_is_crop(s->target)) {
+		walign = 1;
+		halign = 1;
+	} else {
+		walign = frame->mdp_fmt->walign;
+		halign = frame->mdp_fmt->halign;
+	}
+
+	mdp_dbg(2, "[%d] align:%u,%u, bound:%ux%u", ctx_id,
+		walign, halign, framew, frameh);
+
+	ret = mdp_clamp_start(&left, 0, right, walign, s->flags);
+	if (ret)
+		return ret;
+	ret = mdp_clamp_start(&top, 0, bottom, halign, s->flags);
+	if (ret)
+		return ret;
+	ret = mdp_clamp_end(&right, left, framew, walign, s->flags);
+	if (ret)
+		return ret;
+	ret = mdp_clamp_end(&bottom, top, frameh, halign, s->flags);
+	if (ret)
+		return ret;
+
+	r->left = left;
+	r->top = top;
+	r->width = right - left;
+	r->height = bottom - top;
+
+	mdp_dbg(2, "[%d] crop:(%d,%d) %ux%u", ctx_id,
+		r->left, r->top, r->width, r->height);
+	return 0;
+}
+
+int mdp_check_scaling_ratio(const struct v4l2_rect *crop,
+			    const struct v4l2_rect *compose, s32 rotation,
+	const struct mdp_limit *limit)
+{
+	u32 crop_w, crop_h, comp_w, comp_h;
+
+	crop_w = crop->width;
+	crop_h = crop->height;
+	if (90 == rotation || 270 == rotation) {
+		comp_w = compose->height;
+		comp_h = compose->width;
+	} else {
+		comp_w = compose->width;
+		comp_h = compose->height;
+	}
+
+	if ((crop_w / comp_w) > limit->h_scale_down_max ||
+	    (crop_h / comp_h) > limit->v_scale_down_max ||
+	    (comp_w / crop_w) > limit->h_scale_up_max ||
+	    (comp_h / crop_h) > limit->v_scale_up_max)
+		return -ERANGE;
+	return 0;
+}
+
+/* Stride that is accepted by MDP HW */
+static u32 mdp_fmt_get_stride(const struct 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 mdp_fmt_get_stride_contig(const struct 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 mdp_fmt_get_plane_size(const struct 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 void mdp_prepare_buffer(struct img_image_buffer *b,
+			       struct mdp_frame *frame, struct vb2_buffer *vb)
+{
+	struct v4l2_pix_format_mplane *pix_mp = &frame->format.fmt.pix_mp;
+	unsigned int i;
+
+	b->format.colorformat = frame->mdp_fmt->mdp_color;
+	b->format.ycbcr_prof = frame->ycbcr_prof;
+	for (i = 0; i < pix_mp->num_planes; ++i) {
+		u32 stride = mdp_fmt_get_stride(frame->mdp_fmt,
+			pix_mp->plane_fmt[i].bytesperline, i);
+
+		b->format.plane_fmt[i].stride = stride;
+		b->format.plane_fmt[i].size =
+			mdp_fmt_get_plane_size(frame->mdp_fmt, stride,
+					       pix_mp->height, i);
+		b->iova[i] = vb2_dma_contig_plane_dma_addr(vb, i);
+	}
+	for (; i < MDP_COLOR_GET_PLANE_COUNT(b->format.colorformat); ++i) {
+		u32 stride = mdp_fmt_get_stride_contig(frame->mdp_fmt,
+			b->format.plane_fmt[0].stride, i);
+
+		b->format.plane_fmt[i].stride = stride;
+		b->format.plane_fmt[i].size =
+			mdp_fmt_get_plane_size(frame->mdp_fmt, stride,
+					       pix_mp->height, i);
+		b->iova[i] = b->iova[i - 1] + b->format.plane_fmt[i - 1].size;
+	}
+	b->usage = frame->usage;
+}
+
+void mdp_set_src_config(struct img_input *in,
+			struct mdp_frame *frame, struct vb2_buffer *vb)
+{
+	in->buffer.format.width = frame->format.fmt.pix_mp.width;
+	in->buffer.format.height = frame->format.fmt.pix_mp.height;
+	mdp_prepare_buffer(&in->buffer, frame, vb);
+
+	/* in->flags |= ; */	/* HDR, DRE, dither */
+}
+
+static u32 mdp_to_fixed(u32 *r, struct v4l2_fract *f)
+{
+	u32 q;
+
+	if (f->denominator == 0) {
+		*r = 0;
+		return 0;
+	}
+
+	q = f->numerator / f->denominator;
+	*r = (((u64)f->numerator - q * f->denominator) << IMG_SUBPIXEL_SHIFT)
+		/ f->denominator;
+	return q;
+}
+
+static void mdp_set_src_crop(struct img_crop *c, struct mdp_crop *crop)
+{
+	c->left = crop->c.left
+		+ mdp_to_fixed(&c->left_subpix, &crop->left_subpix);
+	c->top = crop->c.top
+		+ mdp_to_fixed(&c->top_subpix, &crop->top_subpix);
+	c->width = crop->c.width
+		+ mdp_to_fixed(&c->width_subpix, &crop->width_subpix);
+	c->height = crop->c.height
+		+ mdp_to_fixed(&c->height_subpix, &crop->height_subpix);
+}
+
+static void mdp_set_orientation(struct img_output *out,
+				s32 rotation, bool hflip, bool vflip)
+{
+	u8 flip = 0;
+
+	if (hflip)
+		flip ^= 1;
+	if (vflip) {
+		/*
+		 * A vertical flip is equivalent to
+		 * a 180-degree rotation with a horizontal flip
+		 */
+		rotation += 180;
+		flip ^= 1;
+	}
+
+	out->rotation = rotation % 360;
+	if (flip != 0)
+		out->flags |= IMG_CTRL_FLAG_HFLIP;
+	else
+		out->flags &= ~IMG_CTRL_FLAG_HFLIP;
+}
+
+void mdp_set_dst_config(struct img_output *out,
+			struct mdp_frame *frame, struct vb2_buffer *vb)
+{
+	out->buffer.format.width = frame->compose.width;
+	out->buffer.format.height = frame->compose.height;
+	mdp_prepare_buffer(&out->buffer, frame, vb);
+	mdp_set_src_crop(&out->crop, &frame->crop);
+	mdp_set_orientation(out, frame->rotation, frame->hflip, frame->vflip);
+
+	/* out->flags |= ; */	/* sharpness, dither */
+}
+
+struct mdp_frameparam *mdp_frameparam_init(void)
+{
+	struct mdp_frameparam *param;
+	struct mdp_frame *frame;
+
+	param = kzalloc(sizeof(*param), GFP_KERNEL);
+	if (!param)
+		return ERR_PTR(-ENOMEM);
+
+	INIT_LIST_HEAD(&param->list);
+	mutex_init(&param->lock);
+	param->state = 0;
+	param->limit = &mdp_def_limit;
+	param->type = MDP_STREAM_TYPE_UNKNOWN;
+	param->frame_no = 0;
+
+	frame = &param->output;
+	frame->format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+	frame->mdp_fmt = mdp_try_fmt_mplane(&frame->format, param, 0);
+	frame->ycbcr_prof =
+		mdp_map_ycbcr_prof_mplane(&frame->format,
+					  frame->mdp_fmt->mdp_color);
+	frame->usage = MDP_BUFFER_USAGE_HW_READ;
+
+	param->num_captures = 1;
+	frame = &param->captures[0];
+	frame->format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+	frame->mdp_fmt = mdp_try_fmt_mplane(&frame->format, param, 0);
+	frame->ycbcr_prof =
+		mdp_map_ycbcr_prof_mplane(&frame->format,
+					  frame->mdp_fmt->mdp_color);
+	frame->usage = MDP_BUFFER_USAGE_MDP;
+	frame->crop.c.width = param->output.format.fmt.pix_mp.width;
+	frame->crop.c.height = param->output.format.fmt.pix_mp.height;
+	frame->compose.width = frame->format.fmt.pix_mp.width;
+	frame->compose.height = frame->format.fmt.pix_mp.height;
+
+	return param;
+}
+
+void mdp_frameparam_release(struct mdp_frameparam *param)
+{
+	kfree(param);
+}
+
diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-regs.h b/drivers/media/platform/mtk-mdp3/mtk-mdp3-regs.h
new file mode 100644
index 000000000000..a2b5c31d3d39
--- /dev/null
+++ b/drivers/media/platform/mtk-mdp3/mtk-mdp3-regs.h
@@ -0,0 +1,386 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Ping-Hsun Wu <ping-hsun.wu@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_MDP3_REGS_H__
+#define __MTK_MDP3_REGS_H__
+
+#include <linux/videodev2.h>
+#include <media/videobuf2-core.h>
+#include "mtk-img-ipi.h"
+
+/*
+ * MDP native color code
+ * Plane count: 1, 2, 3
+ * H-subsample: 0, 1, 2
+ * V-subsample: 0, 1
+ * Color group: 0-RGB, 1-YUV, 2-raw
+ */
+#define MDP_COLOR(PACKED, LOOSE, VIDEO, PLANE, HF, VF, BITS, GROUP, SWAP, ID)\
+	(((PACKED) << 27) | ((LOOSE) << 26) | ((VIDEO) << 23) |\
+	 ((PLANE) << 21) | ((HF) << 19) | ((VF) << 18) | ((BITS) << 8) |\
+	 ((GROUP) << 6) | ((SWAP) << 5) | ((ID) << 0))
+
+#define MDP_COLOR_IS_10BIT_PACKED(c)    ((0x08000000 & (c)) >> 27)
+#define MDP_COLOR_IS_10BIT_LOOSE(c)    (((0x0c000000 & (c)) >> 26) == 1)
+#define MDP_COLOR_IS_10BIT_TILE(c)     (((0x0c000000 & (c)) >> 26) == 3)
+#define MDP_COLOR_IS_UFP(c)             ((0x02000000 & (c)) >> 25)
+#define MDP_COLOR_IS_INTERLACED(c)      ((0x01000000 & (c)) >> 24)
+#define MDP_COLOR_IS_BLOCK_MODE(c)      ((0x00800000 & (c)) >> 23)
+#define MDP_COLOR_GET_PLANE_COUNT(c)    ((0x00600000 & (c)) >> 21)
+#define MDP_COLOR_GET_H_SUBSAMPLE(c)    ((0x00180000 & (c)) >> 19)
+#define MDP_COLOR_GET_V_SUBSAMPLE(c)    ((0x00040000 & (c)) >> 18)
+#define MDP_COLOR_BITS_PER_PIXEL(c)     ((0x0003ff00 & (c)) >>  8)
+#define MDP_COLOR_GET_GROUP(c)          ((0x000000c0 & (c)) >>  6)
+#define MDP_COLOR_IS_SWAPPED(c)         ((0x00000020 & (c)) >>  5)
+#define MDP_COLOR_GET_UNIQUE_ID(c)      ((0x0000001f & (c)) >>  0)
+#define MDP_COLOR_GET_HW_FORMAT(c)      ((0x0000001f & (c)) >>  0)
+
+#define MDP_COLOR_IS_RGB(c)		(MDP_COLOR_GET_GROUP(c) == 0)
+#define MDP_COLOR_IS_YUV(c)		(MDP_COLOR_GET_GROUP(c) == 1)
+#define MDP_COLOR_IS_UV_COPLANE(c)	((MDP_COLOR_GET_PLANE_COUNT(c) == 2) &&\
+					 MDP_COLOR_IS_YUV(c))
+
+enum mdp_color {
+	MDP_COLOR_UNKNOWN	= 0,
+
+	//MDP_COLOR_FULLG8,
+	MDP_COLOR_FULLG8_RGGB	= MDP_COLOR(0, 0, 0, 1, 0, 0,  8, 2,  0, 21),
+	MDP_COLOR_FULLG8_GRBG	= MDP_COLOR(0, 0, 0, 1, 0, 1,  8, 2,  0, 21),
+	MDP_COLOR_FULLG8_GBRG	= MDP_COLOR(0, 0, 0, 1, 1, 0,  8, 2,  0, 21),
+	MDP_COLOR_FULLG8_BGGR	= MDP_COLOR(0, 0, 0, 1, 1, 1,  8, 2,  0, 21),
+	MDP_COLOR_FULLG8	= MDP_COLOR_FULLG8_BGGR,
+
+	//MDP_COLOR_FULLG10,
+	MDP_COLOR_FULLG10_RGGB	= MDP_COLOR(0, 0, 0, 1, 0, 0, 10, 2,  0, 21),
+	MDP_COLOR_FULLG10_GRBG	= MDP_COLOR(0, 0, 0, 1, 0, 1, 10, 2,  0, 21),
+	MDP_COLOR_FULLG10_GBRG	= MDP_COLOR(0, 0, 0, 1, 1, 0, 10, 2,  0, 21),
+	MDP_COLOR_FULLG10_BGGR	= MDP_COLOR(0, 0, 0, 1, 1, 1, 10, 2,  0, 21),
+	MDP_COLOR_FULLG10	= MDP_COLOR_FULLG10_BGGR,
+
+	//MDP_COLOR_FULLG12,
+	MDP_COLOR_FULLG12_RGGB	= MDP_COLOR(0, 0, 0, 1, 0, 0, 12, 2,  0, 21),
+	MDP_COLOR_FULLG12_GRBG	= MDP_COLOR(0, 0, 0, 1, 0, 1, 12, 2,  0, 21),
+	MDP_COLOR_FULLG12_GBRG	= MDP_COLOR(0, 0, 0, 1, 1, 0, 12, 2,  0, 21),
+	MDP_COLOR_FULLG12_BGGR	= MDP_COLOR(0, 0, 0, 1, 1, 1, 12, 2,  0, 21),
+	MDP_COLOR_FULLG12	= MDP_COLOR_FULLG12_BGGR,
+
+	//MDP_COLOR_FULLG14,
+	MDP_COLOR_FULLG14_RGGB	= MDP_COLOR(0, 0, 0, 1, 0, 0, 14, 2,  0, 21),
+	MDP_COLOR_FULLG14_GRBG	= MDP_COLOR(0, 0, 0, 1, 0, 1, 14, 2,  0, 21),
+	MDP_COLOR_FULLG14_GBRG	= MDP_COLOR(0, 0, 0, 1, 1, 0, 14, 2,  0, 21),
+	MDP_COLOR_FULLG14_BGGR	= MDP_COLOR(0, 0, 0, 1, 1, 1, 14, 2,  0, 21),
+	MDP_COLOR_FULLG14	= MDP_COLOR_FULLG14_BGGR,
+
+	MDP_COLOR_UFO10		= MDP_COLOR(0, 0, 0, 1, 0, 0, 10, 2,  0, 24),
+
+	//MDP_COLOR_BAYER8,
+	MDP_COLOR_BAYER8_RGGB	= MDP_COLOR(0, 0, 0, 1, 0, 0,  8, 2,  0, 20),
+	MDP_COLOR_BAYER8_GRBG	= MDP_COLOR(0, 0, 0, 1, 0, 1,  8, 2,  0, 20),
+	MDP_COLOR_BAYER8_GBRG	= MDP_COLOR(0, 0, 0, 1, 1, 0,  8, 2,  0, 20),
+	MDP_COLOR_BAYER8_BGGR	= MDP_COLOR(0, 0, 0, 1, 1, 1,  8, 2,  0, 20),
+	MDP_COLOR_BAYER8	= MDP_COLOR_BAYER8_BGGR,
+
+	//MDP_COLOR_BAYER10,
+	MDP_COLOR_BAYER10_RGGB	= MDP_COLOR(0, 0, 0, 1, 0, 0, 10, 2,  0, 20),
+	MDP_COLOR_BAYER10_GRBG	= MDP_COLOR(0, 0, 0, 1, 0, 1, 10, 2,  0, 20),
+	MDP_COLOR_BAYER10_GBRG	= MDP_COLOR(0, 0, 0, 1, 1, 0, 10, 2,  0, 20),
+	MDP_COLOR_BAYER10_BGGR	= MDP_COLOR(0, 0, 0, 1, 1, 1, 10, 2,  0, 20),
+	MDP_COLOR_BAYER10	= MDP_COLOR_BAYER10_BGGR,
+
+	//MDP_COLOR_BAYER12,
+	MDP_COLOR_BAYER12_RGGB	= MDP_COLOR(0, 0, 0, 1, 0, 0, 12, 2,  0, 20),
+	MDP_COLOR_BAYER12_GRBG	= MDP_COLOR(0, 0, 0, 1, 0, 1, 12, 2,  0, 20),
+	MDP_COLOR_BAYER12_GBRG	= MDP_COLOR(0, 0, 0, 1, 1, 0, 12, 2,  0, 20),
+	MDP_COLOR_BAYER12_BGGR	= MDP_COLOR(0, 0, 0, 1, 1, 1, 12, 2,  0, 20),
+	MDP_COLOR_BAYER12	= MDP_COLOR_BAYER12_BGGR,
+
+	MDP_COLOR_RGB48		= MDP_COLOR(0, 0, 0, 1, 0, 0, 48, 0,  0, 23),
+	/* For bayer+mono raw-16 */
+	MDP_COLOR_RGB565_RAW	= MDP_COLOR(0, 0, 0, 1, 0, 0, 16, 2,  0, 0),
+
+	MDP_COLOR_BAYER8_UNPAK	= MDP_COLOR(0, 0, 0, 1, 0, 0,  8, 2,  0, 22),
+	MDP_COLOR_BAYER10_UNPAK	= MDP_COLOR(0, 0, 0, 1, 0, 0, 10, 2,  0, 22),
+	MDP_COLOR_BAYER12_UNPAK	= MDP_COLOR(0, 0, 0, 1, 0, 0, 12, 2,  0, 22),
+	MDP_COLOR_BAYER14_UNPAK	= MDP_COLOR(0, 0, 0, 1, 0, 0, 14, 2,  0, 22),
+
+	/* Unified formats */
+	MDP_COLOR_GREY		= MDP_COLOR(0, 0, 0, 1, 0, 0,  8, 1,  0, 7),
+
+	MDP_COLOR_RGB565	= MDP_COLOR(0, 0, 0, 1, 0, 0, 16, 0,  0, 0),
+	MDP_COLOR_BGR565	= MDP_COLOR(0, 0, 0, 1, 0, 0, 16, 0,  1, 0),
+	MDP_COLOR_RGB888	= MDP_COLOR(0, 0, 0, 1, 0, 0, 24, 0,  1, 1),
+	MDP_COLOR_BGR888	= MDP_COLOR(0, 0, 0, 1, 0, 0, 24, 0,  0, 1),
+	MDP_COLOR_RGBA8888	= MDP_COLOR(0, 0, 0, 1, 0, 0, 32, 0,  1, 2),
+	MDP_COLOR_BGRA8888	= MDP_COLOR(0, 0, 0, 1, 0, 0, 32, 0,  0, 2),
+	MDP_COLOR_ARGB8888	= MDP_COLOR(0, 0, 0, 1, 0, 0, 32, 0,  1, 3),
+	MDP_COLOR_ABGR8888	= MDP_COLOR(0, 0, 0, 1, 0, 0, 32, 0,  0, 3),
+
+	MDP_COLOR_UYVY		= MDP_COLOR(0, 0, 0, 1, 1, 0, 16, 1,  0, 4),
+	MDP_COLOR_VYUY		= MDP_COLOR(0, 0, 0, 1, 1, 0, 16, 1,  1, 4),
+	MDP_COLOR_YUYV		= MDP_COLOR(0, 0, 0, 1, 1, 0, 16, 1,  0, 5),
+	MDP_COLOR_YVYU		= MDP_COLOR(0, 0, 0, 1, 1, 0, 16, 1,  1, 5),
+
+	MDP_COLOR_I420		= MDP_COLOR(0, 0, 0, 3, 1, 1,  8, 1,  0, 8),
+	MDP_COLOR_YV12		= MDP_COLOR(0, 0, 0, 3, 1, 1,  8, 1,  1, 8),
+	MDP_COLOR_I422		= MDP_COLOR(0, 0, 0, 3, 1, 0,  8, 1,  0, 9),
+	MDP_COLOR_YV16		= MDP_COLOR(0, 0, 0, 3, 1, 0,  8, 1,  1, 9),
+	MDP_COLOR_I444		= MDP_COLOR(0, 0, 0, 3, 0, 0,  8, 1,  0, 10),
+	MDP_COLOR_YV24		= MDP_COLOR(0, 0, 0, 3, 0, 0,  8, 1,  1, 10),
+
+	MDP_COLOR_NV12		= MDP_COLOR(0, 0, 0, 2, 1, 1,  8, 1,  0, 12),
+	MDP_COLOR_NV21		= MDP_COLOR(0, 0, 0, 2, 1, 1,  8, 1,  1, 12),
+	MDP_COLOR_NV16		= MDP_COLOR(0, 0, 0, 2, 1, 0,  8, 1,  0, 13),
+	MDP_COLOR_NV61		= MDP_COLOR(0, 0, 0, 2, 1, 0,  8, 1,  1, 13),
+	MDP_COLOR_NV24		= MDP_COLOR(0, 0, 0, 2, 0, 0,  8, 1,  0, 14),
+	MDP_COLOR_NV42		= MDP_COLOR(0, 0, 0, 2, 0, 0,  8, 1,  1, 14),
+
+	/* Mediatek proprietary formats */
+	/* UFO encoded block mode */
+	MDP_COLOR_420_BLK_UFO	= MDP_COLOR(0, 0, 5, 2, 1, 1, 256, 1, 0, 12),
+	/* Block mode */
+	MDP_COLOR_420_BLK	= MDP_COLOR(0, 0, 1, 2, 1, 1, 256, 1, 0, 12),
+	/* Block mode + field mode */
+	MDP_COLOR_420_BLKI	= MDP_COLOR(0, 0, 3, 2, 1, 1, 256, 1, 0, 12),
+	/* Block mode */
+	MDP_COLOR_422_BLK	= MDP_COLOR(0, 0, 1, 1, 1, 0, 512, 1, 0, 4),
+
+	MDP_COLOR_IYU2		= MDP_COLOR(0, 0, 0, 1, 0, 0, 24,  1, 0, 25),
+	MDP_COLOR_YUV444	= MDP_COLOR(0, 0, 0, 1, 0, 0, 24,  1, 0, 30),
+
+	/* Packed 10-bit formats */
+	MDP_COLOR_RGBA1010102	= MDP_COLOR(1, 0, 0, 1, 0, 0, 32,  0, 1, 2),
+	MDP_COLOR_BGRA1010102	= MDP_COLOR(1, 0, 0, 1, 0, 0, 32,  0, 0, 2),
+	/* Packed 10-bit UYVY */
+	MDP_COLOR_UYVY_10P	= MDP_COLOR(1, 0, 0, 1, 1, 0, 20,  1, 0, 4),
+	/* Packed 10-bit NV21 */
+	MDP_COLOR_NV21_10P	= MDP_COLOR(1, 0, 0, 2, 1, 1, 10,  1, 1, 12),
+	/* 10-bit block mode */
+	MDP_COLOR_420_BLK_10_H	= MDP_COLOR(1, 0, 1, 2, 1, 1, 320, 1, 0, 12),
+	/* 10-bit HEVC tile mode */
+	MDP_COLOR_420_BLK_10_V	= MDP_COLOR(1, 1, 1, 2, 1, 1, 320, 1, 0, 12),
+	/* UFO encoded 10-bit block mode */
+	MDP_COLOR_420_BLK_U10_H	= MDP_COLOR(1, 0, 5, 2, 1, 1, 320, 1, 0, 12),
+	/* UFO encoded 10-bit HEVC tile mode */
+	MDP_COLOR_420_BLK_U10_V	= MDP_COLOR(1, 1, 5, 2, 1, 1, 320, 1, 0, 12),
+
+	/* Loose 10-bit formats */
+	MDP_COLOR_UYVY_10L	= MDP_COLOR(0, 1, 0, 1, 1, 0, 20,  1, 0, 4),
+	MDP_COLOR_VYUY_10L	= MDP_COLOR(0, 1, 0, 1, 1, 0, 20,  1, 1, 4),
+	MDP_COLOR_YUYV_10L	= MDP_COLOR(0, 1, 0, 1, 1, 0, 20,  1, 0, 5),
+	MDP_COLOR_YVYU_10L	= MDP_COLOR(0, 1, 0, 1, 1, 0, 20,  1, 1, 5),
+	MDP_COLOR_NV12_10L	= MDP_COLOR(0, 1, 0, 2, 1, 1, 10,  1, 0, 12),
+	MDP_COLOR_NV21_10L	= MDP_COLOR(0, 1, 0, 2, 1, 1, 10,  1, 1, 12),
+	MDP_COLOR_NV16_10L	= MDP_COLOR(0, 1, 0, 2, 1, 0, 10,  1, 0, 13),
+	MDP_COLOR_NV61_10L	= MDP_COLOR(0, 1, 0, 2, 1, 0, 10,  1, 1, 13),
+	MDP_COLOR_YV12_10L	= MDP_COLOR(0, 1, 0, 3, 1, 1, 10,  1, 1, 8),
+	MDP_COLOR_I420_10L	= MDP_COLOR(0, 1, 0, 3, 1, 1, 10,  1, 0, 8),
+};
+
+/* Minimum Y stride that is accepted by MDP HW */
+static inline u32 mdp_color_get_min_y_stride(enum mdp_color c, u32 width)
+{
+	return ((MDP_COLOR_BITS_PER_PIXEL(c) * width) + 4) >> 3;
+}
+
+/* Minimum UV stride that is accepted by MDP HW */
+static inline u32 mdp_color_get_min_uv_stride(enum mdp_color c, u32 width)
+{
+	u32 min_stride;
+
+	if (MDP_COLOR_GET_PLANE_COUNT(c) == 1)
+		return 0;
+	min_stride = mdp_color_get_min_y_stride(c, width)
+		>> MDP_COLOR_GET_H_SUBSAMPLE(c);
+	if (MDP_COLOR_IS_UV_COPLANE(c) && !MDP_COLOR_IS_BLOCK_MODE(c))
+		min_stride = min_stride * 2;
+	return min_stride;
+}
+
+/* Minimum Y plane size that is necessary in buffer */
+static inline u32 mdp_color_get_min_y_size(enum mdp_color c,
+					   u32 width, u32 height)
+{
+	if (MDP_COLOR_IS_BLOCK_MODE(c))
+		return ((MDP_COLOR_BITS_PER_PIXEL(c) * width) >> 8) * height;
+	return mdp_color_get_min_y_stride(c, width) * height;
+}
+
+/* Minimum UV plane size that is necessary in buffer */
+static inline u32 mdp_color_get_min_uv_size(enum mdp_color c,
+					    u32 width, u32 height)
+{
+	height = height >> MDP_COLOR_GET_V_SUBSAMPLE(c);
+	if (MDP_COLOR_IS_BLOCK_MODE(c) && (MDP_COLOR_GET_PLANE_COUNT(c) > 1))
+		return ((MDP_COLOR_BITS_PER_PIXEL(c) * width) >> 8) * height;
+	return mdp_color_get_min_uv_stride(c, width) * height;
+}
+
+/* Combine colorspace, xfer_func, ycbcr_encoding, and quantization */
+enum mdp_ycbcr_profile {
+	/* V4L2_YCBCR_ENC_601 and V4L2_QUANTIZATION_LIM_RANGE */
+	MDP_YCBCR_PROFILE_BT601,
+	/* V4L2_YCBCR_ENC_709 and V4L2_QUANTIZATION_LIM_RANGE */
+	MDP_YCBCR_PROFILE_BT709,
+	/* V4L2_YCBCR_ENC_601 and V4L2_QUANTIZATION_FULL_RANGE */
+	MDP_YCBCR_PROFILE_JPEG,
+	MDP_YCBCR_PROFILE_FULL_BT601 = MDP_YCBCR_PROFILE_JPEG,
+
+	/* Colorspaces not support for capture */
+	/* V4L2_YCBCR_ENC_BT2020 and V4L2_QUANTIZATION_LIM_RANGE */
+	MDP_YCBCR_PROFILE_BT2020,
+	/* V4L2_YCBCR_ENC_709 and V4L2_QUANTIZATION_FULL_RANGE */
+	MDP_YCBCR_PROFILE_FULL_BT709,
+	/* V4L2_YCBCR_ENC_BT2020 and V4L2_QUANTIZATION_FULL_RANGE */
+	MDP_YCBCR_PROFILE_FULL_BT2020,
+};
+
+#define MDP_FMT_FLAG_OUTPUT	BIT(0)
+#define MDP_FMT_FLAG_CAPTURE	BIT(1)
+
+struct mdp_format {
+	u32	pixelformat;
+	u32	mdp_color;
+	u8	depth[VIDEO_MAX_PLANES];
+	u8	row_depth[VIDEO_MAX_PLANES];
+	u8	num_planes;
+	u8	walign;
+	u8	halign;
+	u8	salign;
+	u32	flags;
+};
+
+struct mdp_pix_limit {
+	u32	wmin;
+	u32	hmin;
+	u32	wmax;
+	u32	hmax;
+};
+
+struct mdp_limit {
+	struct mdp_pix_limit	out_limit;
+	struct mdp_pix_limit	cap_limit;
+	u32			h_scale_up_max;
+	u32			v_scale_up_max;
+	u32			h_scale_down_max;
+	u32			v_scale_down_max;
+};
+
+enum mdp_stream_type {
+	MDP_STREAM_TYPE_UNKNOWN,
+
+	MDP_STREAM_TYPE_BITBLT,
+	MDP_STREAM_TYPE_GPU_BITBLT,
+	MDP_STREAM_TYPE_DUAL_BITBLT,
+	MDP_STREAM_TYPE_2ND_BITBLT,
+
+	/* MDP_STREAM_TYPE_FRAG, */
+	/* MDP_STREAM_TYPE_FRAG_JPEGDEC, */
+
+	MDP_STREAM_TYPE_ISP_IC,
+	MDP_STREAM_TYPE_ISP_VR,
+	MDP_STREAM_TYPE_ISP_ZSD,
+	MDP_STREAM_TYPE_ISP_IP,
+	MDP_STREAM_TYPE_ISP_VSS,
+	MDP_STREAM_TYPE_ISP_ZSD_SLOW,
+	/* MDP_STREAM_TYPE_ISP_ZSD_ONE, */
+
+	MDP_STREAM_TYPE_WPE,
+	MDP_STREAM_TYPE_WPE2,
+};
+
+struct mdp_crop {
+	struct v4l2_rect	c;
+	struct v4l2_fract	left_subpix;
+	struct v4l2_fract	top_subpix;
+	struct v4l2_fract	width_subpix;
+	struct v4l2_fract	height_subpix;
+};
+
+struct mdp_frame {
+	struct v4l2_format	format;
+	const struct mdp_format	*mdp_fmt;
+	u32			ycbcr_prof;	/* enum mdp_ycbcr_profile */
+	u32			usage;		/* enum mdp_buffer_usage */
+	struct mdp_crop		crop;
+	struct v4l2_rect	compose;
+	s32			rotation;
+	u32			hflip:1;
+	u32			vflip:1;
+	u32			hdr:1;
+	u32			dre:1;
+	u32			sharpness:1;
+	u32			dither:1;
+};
+
+static inline bool mdp_target_is_crop(u32 target)
+{
+	return (target == V4L2_SEL_TGT_CROP) ||
+		(target == V4L2_SEL_TGT_CROP_DEFAULT) ||
+		(target == V4L2_SEL_TGT_CROP_BOUNDS);
+}
+
+static inline bool mdp_target_is_compose(u32 target)
+{
+	return (target == V4L2_SEL_TGT_COMPOSE) ||
+		(target == V4L2_SEL_TGT_COMPOSE_DEFAULT) ||
+		(target == V4L2_SEL_TGT_COMPOSE_BOUNDS);
+}
+
+#define MDP_MAX_CAPTURES	IMG_MAX_HW_OUTPUTS
+
+#define MDP_VPU_INIT		BIT(0)
+#define MDP_M2M_SRC_FMT		BIT(1)
+#define MDP_M2M_DST_FMT		BIT(2)
+#define MDP_M2M_CTX_ERROR	BIT(5)
+
+struct mdp_frameparam {
+	struct list_head	list;
+	/* synchronization protect for m2m context state */
+	struct mutex		lock;
+	u32			state;
+	const struct mdp_limit	*limit;
+	u32			type;	/* enum mdp_stream_type */
+	u32			frame_no;
+	struct mdp_frame	output;
+	struct mdp_frame	captures[MDP_MAX_CAPTURES];
+	u32			num_captures;
+	/* __u8			pq_data[]; */
+	enum v4l2_colorspace		colorspace;
+	enum v4l2_ycbcr_encoding	ycbcr_enc;
+	enum v4l2_xfer_func		xfer_func;
+	enum v4l2_quantization		quant;
+};
+
+int mdp_enum_fmt_mplane(struct v4l2_fmtdesc *f);
+const struct mdp_format *mdp_try_fmt_mplane(struct v4l2_format *f,
+					    struct mdp_frameparam *param,
+					    u32 ctx_id);
+enum mdp_ycbcr_profile mdp_map_ycbcr_prof_mplane(struct v4l2_format *f,
+						 u32 mdp_color);
+int mdp_try_crop(struct v4l2_rect *r, const struct v4l2_selection *s,
+		 struct mdp_frame *frame, u32 ctx_id);
+int mdp_check_scaling_ratio(const struct v4l2_rect *crop,
+			    const struct v4l2_rect *compose, s32 rotation,
+	const struct mdp_limit *limit);
+void mdp_set_src_config(struct img_input *in,
+			struct mdp_frame *frame, struct vb2_buffer *vb);
+void mdp_set_dst_config(struct img_output *out,
+			struct mdp_frame *frame, struct vb2_buffer *vb);
+
+struct mdp_frameparam *mdp_frameparam_init(void);
+void mdp_frameparam_release(struct mdp_frameparam *param);
+
+#endif  /* __MTK_MDP3_REGS_H__ */
+
diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-vpu.c b/drivers/media/platform/mtk-mdp3/mtk-mdp3-vpu.c
new file mode 100644
index 000000000000..0ad443cb52e1
--- /dev/null
+++ b/drivers/media/platform/mtk-mdp3/mtk-mdp3-vpu.c
@@ -0,0 +1,277 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Ping-Hsun Wu <ping-hsun.wu@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/remoteproc.h>
+#include <linux/platform_data/mtk_scp.h>
+#include "mtk-mdp3-vpu.h"
+#include "mtk-mdp3-core.h"
+
+#define MDP_VPU_MESSAGE_TIMEOUT 500U
+
+static inline struct mdp_dev *vpu_to_mdp(struct mdp_vpu_dev *vpu)
+{
+	return container_of(vpu, struct mdp_dev, vpu);
+}
+
+static void mdp_vpu_ipi_handle_init_ack(void *data, unsigned int len,
+					void *priv)
+{
+	struct mdp_ipi_init_msg *msg = (struct mdp_ipi_init_msg *)data;
+	struct mdp_vpu_dev *vpu =
+		(struct mdp_vpu_dev *)(unsigned long)msg->drv_data;
+
+	if (!vpu->work_size)
+		vpu->work_size = msg->work_size;
+	else
+		vpu->status = msg->status;
+	complete(&vpu->ipi_acked);
+}
+
+static void mdp_vpu_ipi_handle_deinit_ack(void *data, unsigned int len,
+					  void *priv)
+{
+	struct mdp_ipi_deinit_msg *msg = (struct mdp_ipi_deinit_msg *)data;
+	struct mdp_vpu_dev *vpu =
+		(struct mdp_vpu_dev *)(unsigned long)msg->drv_data;
+
+	vpu->status = msg->status;
+	complete(&vpu->ipi_acked);
+}
+
+static void mdp_vpu_ipi_handle_frame_ack(void *data, unsigned int len,
+					 void *priv)
+{
+	struct img_ipi_frameparam *param = (struct img_ipi_frameparam *)data;
+	struct mdp_vpu_ctx *ctx =
+		(struct mdp_vpu_ctx *)(unsigned long)param->drv_data;
+
+	ctx->failure = param->state;
+	if (ctx->failure) {
+		struct mdp_dev *mdp = vpu_to_mdp(ctx->vpu_dev);
+
+		dev_info(&mdp->pdev->dev, "VPU MDP failure:%d\n", ctx->failure);
+	}
+	complete(&ctx->vpu_dev->ipi_acked);
+}
+
+int mdp_vpu_register(struct platform_device *pdev)
+{
+	int err;
+
+	err = scp_ipi_register(pdev, SCP_IPI_MDP_INIT,
+			       mdp_vpu_ipi_handle_init_ack, NULL);
+	if (err) {
+		dev_err(&pdev->dev, "scp_ipi_register failed %d\n", err);
+		goto err_ipi_init;
+	}
+	err = scp_ipi_register(pdev, SCP_IPI_MDP_DEINIT,
+			       mdp_vpu_ipi_handle_deinit_ack, NULL);
+	if (err) {
+		dev_err(&pdev->dev, "scp_ipi_register failed %d\n", err);
+		goto err_ipi_deinit;
+	}
+	err = scp_ipi_register(pdev, SCP_IPI_MDP_FRAME,
+			       mdp_vpu_ipi_handle_frame_ack, NULL);
+	if (err) {
+		dev_err(&pdev->dev, "scp_ipi_register failed %d\n", err);
+		goto err_ipi_frame;
+	}
+	return 0;
+
+err_ipi_frame:
+	/* vpu_ipi_unregister(IPI_MDP_DEINIT); */
+err_ipi_deinit:
+	/* vpu_ipi_unregister(IPI_MDP_INIT); */
+err_ipi_init:
+
+	return err;
+}
+
+void mdp_vpu_unregister(struct platform_device *pdev)
+{
+	/* vpu_ipi_unregister(IPI_MDP_INIT); */
+	/* vpu_ipi_unregister(IPI_MDP_DEINIT); */
+	/* vpu_ipi_unregister(IPI_MDP_FRAME); */
+}
+
+static int mdp_vpu_sendmsg(struct mdp_vpu_dev *vpu, enum scp_ipi_id id,
+			   void *buf, unsigned int len)
+{
+	int ret;
+
+	if (!vpu->pdev) {
+		struct mdp_dev *mdp = vpu_to_mdp(vpu);
+
+		dev_dbg(&mdp->pdev->dev, "vpu pdev is NULL");
+		return -EINVAL;
+	}
+	ret = scp_ipi_send(vpu->pdev, id, buf, len, 2000);
+
+	if (ret) {
+		dev_err(&vpu->pdev->dev, "scp_ipi_send failed %d\n", ret);
+		return -EPERM;
+	}
+	ret =
+	wait_for_completion_timeout(&vpu->ipi_acked,
+				    msecs_to_jiffies(MDP_VPU_MESSAGE_TIMEOUT));
+	if (!ret)
+		ret = -ETIME;
+	else if (vpu->status)
+		ret = -EINVAL;
+	else
+		ret = 0;
+	return ret;
+}
+
+int mdp_vpu_dev_init(struct mdp_vpu_dev *vpu, struct platform_device *pdev,
+		     struct mutex *lock)
+{
+	struct mdp_ipi_init_msg msg = {
+		.drv_data = (unsigned long)vpu,
+	};
+	phys_addr_t mem_size, pool;
+	const size_t pool_size = sizeof(struct mdp_config_pool);
+	struct mdp_dev *mdp = vpu_to_mdp(vpu);
+	int err;
+
+	init_completion(&vpu->ipi_acked);
+	vpu->pdev = pdev;
+	vpu->lock = lock;
+	vpu->work_size = 0;
+	err = mdp_vpu_sendmsg(vpu, SCP_IPI_MDP_INIT, &msg, sizeof(msg));
+	if (err)
+		goto err_work_size;
+	/* vpu work_size was set in mdp_vpu_ipi_handle_init_ack */
+
+	vpu->work = scp_get_reserve_mem_virt(SCP_MDP_MEM_ID);
+	vpu->work_addr = scp_get_reserve_mem_phys(SCP_MDP_MEM_ID);
+	mem_size = scp_get_reserve_mem_size(SCP_MDP_MEM_ID);
+	pool = ALIGN(vpu->work + vpu->work_size, 8);
+	if (pool + pool_size - vpu->work > mem_size) {
+		dev_err(&mdp->pdev->dev,
+			"VPU memory insufficient: %lx + %lx > %llx",
+			vpu->work_size, pool_size, mem_size);
+		err = -ENOMEM;
+		goto err_mem_size;
+	}
+
+	dev_info(&mdp->pdev->dev,
+		 "VPU work:%llx pa:%llx sz:%lx pool:%llx sz:%lx (mem sz:%llx)",
+		vpu->work, vpu->work_addr, vpu->work_size,
+		pool, pool_size, mem_size);
+	vpu->pool = (struct mdp_config_pool *)pool;
+	msg.work_addr = vpu->work_addr;
+	msg.work_size = vpu->work_size;
+	err = mdp_vpu_sendmsg(vpu, SCP_IPI_MDP_INIT, &msg, sizeof(msg));
+	if (err)
+		goto err_work_size;
+	memset(vpu->pool, 0, sizeof(*vpu->pool));
+	return 0;
+
+err_work_size:
+	switch (vpu->status) {
+	case -MDP_IPI_EBUSY:
+		err = -EBUSY;
+		break;
+	case -MDP_IPI_ENOMEM:
+		err = -ENOSPC;	/* -ENOMEM */
+		break;
+	}
+err_mem_size:
+	return err;
+}
+
+int mdp_vpu_dev_deinit(struct mdp_vpu_dev *vpu)
+{
+	struct mdp_ipi_deinit_msg msg = {
+		.drv_data = (unsigned long)vpu,
+		.work_addr = vpu->work_addr,
+	};
+
+	return mdp_vpu_sendmsg(vpu, SCP_IPI_MDP_DEINIT, &msg, sizeof(msg));
+}
+
+static struct img_config *mdp_config_get(struct mdp_vpu_dev *vpu,
+					 enum mdp_config_id id, uint32_t *addr)
+{
+	struct img_config *config;
+
+	if (id < 0 || id >= MDP_CONFIG_POOL_SIZE)
+		return ERR_PTR(-EINVAL);
+	if (vpu->lock)
+		mutex_lock(vpu->lock);
+	vpu->pool->cfg_count[id]++;
+	config = &vpu->pool->configs[id];
+	*addr = vpu->work_addr + ((unsigned long)config - vpu->work);
+	if (vpu->lock)
+		mutex_unlock(vpu->lock);
+	return config;
+}
+
+static int mdp_config_put(struct mdp_vpu_dev *vpu,
+			  enum mdp_config_id id,
+			  const struct img_config *config)
+{
+	int err = 0;
+
+	if (id < 0 || id >= MDP_CONFIG_POOL_SIZE)
+		return -EINVAL;
+	if (vpu->lock)
+		mutex_lock(vpu->lock);
+	if (!vpu->pool->cfg_count[id] || config != &vpu->pool->configs[id])
+		err = -EINVAL;
+	else
+		vpu->pool->cfg_count[id]--;
+	if (vpu->lock)
+		mutex_unlock(vpu->lock);
+	return err;
+}
+
+int mdp_vpu_ctx_init(struct mdp_vpu_ctx *ctx, struct mdp_vpu_dev *vpu,
+		     enum mdp_config_id id)
+{
+	ctx->config = mdp_config_get(vpu, id, &ctx->inst_addr);
+	if (IS_ERR(ctx->config)) {
+		int err = PTR_ERR(ctx->config);
+
+		ctx->config = NULL;
+		return err;
+	}
+	ctx->config_id = id;
+	ctx->vpu_dev = vpu;
+	return 0;
+}
+
+int mdp_vpu_ctx_deinit(struct mdp_vpu_ctx *ctx)
+{
+	int err = mdp_config_put(ctx->vpu_dev, ctx->config_id, ctx->config);
+
+	ctx->config_id = 0;
+	ctx->config = NULL;
+	ctx->inst_addr = 0;
+	return err;
+}
+
+int mdp_vpu_process(struct mdp_vpu_ctx *ctx, struct img_ipi_frameparam *param)
+{
+	memset((void *)ctx->vpu_dev->work, 0, ctx->vpu_dev->work_size);
+	memset(ctx->config, 0, sizeof(*ctx->config));
+	param->config_data.va = (unsigned long)ctx->config;
+	param->config_data.pa = ctx->inst_addr;
+	param->drv_data = (unsigned long)ctx;
+	return mdp_vpu_sendmsg(ctx->vpu_dev, SCP_IPI_MDP_FRAME,
+		param, sizeof(*param));
+}
+
diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-vpu.h b/drivers/media/platform/mtk-mdp3/mtk-mdp3-vpu.h
new file mode 100644
index 000000000000..09b5b0026e4b
--- /dev/null
+++ b/drivers/media/platform/mtk-mdp3/mtk-mdp3-vpu.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Ping-Hsun Wu <ping-hsun.wu@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_MDP3_VPU_H__
+#define __MTK_MDP3_VPU_H__
+
+#include <linux/platform_device.h>
+#include "mtk-img-ipi.h"
+
+enum mdp_ipi_result {
+	MDP_IPI_SUCCESS	= 0,
+	MDP_IPI_ENOMEM	= 12,
+	MDP_IPI_EBUSY	= 16,
+	MDP_IPI_EINVAL	= 22,
+	MDP_IPI_EMINST	= 24,
+	MDP_IPI_ERANGE	= 34,
+	MDP_IPI_NR_ERRNO,
+
+	MDP_IPI_EOTHER	= MDP_IPI_NR_ERRNO,
+	MDP_IPI_PATH_CANT_MERGE,
+	MDP_IPI_OP_FAIL,
+};
+
+struct mdp_ipi_init_msg {
+	u32	status;
+	u64	drv_data;
+	u32	work_addr;	/* [in] working buffer address */
+	u32	work_size;	/* [in] working buffer size */
+} __attribute__ ((__packed__));
+
+struct mdp_ipi_deinit_msg {
+	u32	status;
+	u64	drv_data;
+	u32	work_addr;
+} __attribute__ ((__packed__));
+
+enum mdp_config_id {
+	MDP_DEV_M2M = 0,
+	MDP_CONFIG_POOL_SIZE	/* ALWAYS keep at the end */
+};
+
+struct mdp_config_pool {
+	u64		cfg_count[MDP_CONFIG_POOL_SIZE];
+	struct img_config	configs[MDP_CONFIG_POOL_SIZE];
+};
+
+struct mdp_vpu_dev {
+	/* synchronization protect for image configuration reference count */
+	struct mutex		*lock;
+	struct platform_device	*pdev;
+	struct completion	ipi_acked;
+	phys_addr_t		work;
+	phys_addr_t		work_addr;
+	size_t			work_size;
+	struct mdp_config_pool	*pool;
+	u32		status;
+};
+
+struct mdp_vpu_ctx {
+	struct mdp_vpu_dev	*vpu_dev;
+	u32		config_id;
+	struct img_config	*config;
+	u32		inst_addr;
+	s32			failure;
+};
+
+int mdp_vpu_register(struct platform_device *pdev);
+void mdp_vpu_unregister(struct platform_device *pdev);
+int mdp_vpu_dev_init(struct mdp_vpu_dev *vpu, struct platform_device *pdev,
+		     struct mutex *lock);
+int mdp_vpu_dev_deinit(struct mdp_vpu_dev *vpu);
+int mdp_vpu_ctx_init(struct mdp_vpu_ctx *ctx, struct mdp_vpu_dev *vpu,
+		     enum mdp_config_id id);
+int mdp_vpu_ctx_deinit(struct mdp_vpu_ctx *ctx);
+int mdp_vpu_process(struct mdp_vpu_ctx *vpu, struct img_ipi_frameparam *param);
+
+#endif  /* __MTK_MDP3_VPU_H__ */
+
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [RFC v2 4/4] media: platform: mtk-mdp3: Add Mediatek MDP3 driver
  2019-05-16  3:23 ` [RFC v2 4/4] media: platform: mtk-mdp3: Add Mediatek MDP3 driver Daoyuan Huang
@ 2019-06-04 11:20   ` Tomasz Figa
  2019-06-11  9:20     ` Daoyuan Huang
  2019-06-20  4:48     ` Alexandre Courbot
  2019-06-20  4:47   ` Alexandre Courbot
  1 sibling, 2 replies; 12+ messages in thread
From: Tomasz Figa @ 2019-06-04 11:20 UTC (permalink / raw)
  To: Daoyuan Huang
  Cc: hans.verkuil, laurent.pinchart+renesas, matthias.bgg, mchehab,
	yuzhao, zwisler, linux-mediatek, linux-arm-kernel, Sean.Cheng,
	sj.huang, christie.yu, holmes.chiou, frederic.chen,
	Jerry-ch.Chen, jungo.lin, Rynn.Wu, linux-media, srv_heupstream,
	devicetree, Ping-Hsun Wu, acourbot

Hi Daoyuan,

On Thu, May 16, 2019 at 11:23:32AM +0800, Daoyuan Huang wrote:
> From: daoyuan huang <daoyuan.huang@mediatek.com>
> 
> This patch adds driver for Media Data Path 3 (MDP3).
> Each modules' related operation control is sited in mtk-mdp3-comp.c
> Each modules' register table is defined in file with "mdp_reg_"
> and "mmsys_" prefix
> GCE related API, operation control  sited in mtk-mdp3-cmdq.c
> V4L2 m2m device functions are implemented in mtk-mdp3-m2m.c
> Probe, power, suspend/resume, system level functions are defined in
> mtk-mdp3-core.c

Thanks for the patch. Some initial comments inline.

[snip]
> +void mdp_comp_clock_on(struct device *dev, struct mdp_comp *comp)
> +{
> +	int i, err;
> +
> +	if (comp->larb_dev) {
> +		err = pm_runtime_get_sync(comp->larb_dev);
> +		if (err < 0)
> +			dev_err(dev,
> +				"Failed to get larb, err %d. type:%d id:%d\n",
> +				err, comp->type, comp->id);

There is no need to make this conditional. Actually, we always need to grab
a runtime PM enable count, otherwise the power domain would power off (if
this SoC has gate'able power domains).

> +	}
> +
> +	for (i = 0; i < ARRAY_SIZE(comp->clks); i++) {
> +		if (IS_ERR(comp->clks[i]))
> +			break;
> +		err = clk_prepare_enable(comp->clks[i]);
> +		if (err)
> +			dev_err(dev,
> +				"Failed to enable clock %d, err %d. type:%d id:%d\n",
> +				i, err, comp->type, comp->id);
> +	}
> +}
> +
> +void mdp_comp_clock_off(struct device *dev, struct mdp_comp *comp)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(comp->clks); i++) {
> +		if (IS_ERR(comp->clks[i]))
> +			break;
> +		clk_disable_unprepare(comp->clks[i]);
> +	}
> +
> +	if (comp->larb_dev)
> +		pm_runtime_put_sync(comp->larb_dev);

Ditto. Also, it doesn't make sense to use the _sync variant here, we don't
care if it powers off before the function returns or a bit after.

[snip]

> +static int mdp_comp_init(struct device *dev, struct mdp_dev *mdp,
> +			 struct device_node *node, struct mdp_comp *comp,
> +			 enum mdp_comp_id id)
> +{
> +	struct device_node *larb_node;
> +	struct platform_device *larb_pdev;
> +	int i;
> +
> +	if (id < 0 || id >= MDP_MAX_COMP_COUNT) {
> +		dev_err(dev, "Invalid component id %d\n", id);
> +		return -EINVAL;
> +	}
> +
> +	__mdp_comp_init(mdp, node, comp);
> +	comp->type = mdp_comp_matches[id].type;
> +	comp->id = id;
> +	comp->alias_id = mdp_comp_matches[id].alias_id;
> +	comp->ops = mdp_comp_ops[comp->type];
> +
> +	for (i = 0; i < ARRAY_SIZE(comp->clks); i++) {
> +		comp->clks[i] = of_clk_get(node, i);
> +		if (IS_ERR(comp->clks[i]))
> +			break;
> +	}
> +
> +	mdp_get_subsys_id(dev, node, comp);
> +
> +	/* Only DMA capable components need the LARB property */
> +	comp->larb_dev = NULL;
> +	if (comp->type != MDP_COMP_TYPE_RDMA &&
> +	    comp->type != MDP_COMP_TYPE_WROT &&
> +		comp->type != MDP_COMP_TYPE_WDMA)
> +		return 0;
> +
> +	larb_node = of_parse_phandle(node, "mediatek,larb", 0);
> +	if (!larb_node) {
> +		dev_err(dev, "Missing mediatek,larb phandle in %pOF node\n",
> +			node);
> +		return -EINVAL;
> +	}
> +
> +	larb_pdev = of_find_device_by_node(larb_node);
> +	if (!larb_pdev) {
> +		dev_warn(dev, "Waiting for larb device %pOF\n", larb_node);
> +		of_node_put(larb_node);
> +		return -EPROBE_DEFER;
> +	}
> +	of_node_put(larb_node);
> +
> +	comp->larb_dev = &larb_pdev->dev;

Why do we need this larb_dev? I believe we already made the handling
transparent by using device links, so as long as the driver handles runtime
PM properly, it should automatically handle larbs.

> +
> +	return 0;
> +}
> +
> +static void mdp_comp_deinit(struct device *dev, struct mdp_comp *comp)
> +{
> +	iounmap(comp->regs);
> +	/* of_node_put(comp->dev_node); */
> +}
> +
> +void mdp_component_deinit(struct device *dev, struct mdp_dev *mdp)
> +{
> +	int i;
> +
> +	mdp_comp_deinit(dev, &mdp->mmsys);
> +	mdp_comp_deinit(dev, &mdp->mm_mutex);
> +	for (i = 0; i < ARRAY_SIZE(mdp->comp); i++) {
> +		if (mdp->comp[i]) {
> +			mdp_comp_deinit(dev, mdp->comp[i]);
> +			kfree(mdp->comp[i]);
> +		}
> +	}
> +}
> +
> +int mdp_component_init(struct device *dev, struct mdp_dev *mdp)
> +{
> +	struct device_node *node, *parent;
> +	int i, ret;
> +
> +	for (i = 0; i < ARRAY_SIZE(gce_event_names); i++) {
> +		s32 event_id;
> +
> +		event_id = cmdq_dev_get_event(dev, gce_event_names[i]);
> +		mdp->event[i] = (event_id < 0) ? -i : event_id;
> +		dev_info(dev, "Get event %s id:%d\n",
> +			 gce_event_names[i], mdp->event[i]);
> +	}
> +
> +	ret = mdp_mm_init(dev, mdp, &mdp->mmsys, "mediatek,mmsys");
> +	if (ret)
> +		goto err_init_mm;
> +
> +	ret = mdp_mm_init(dev, mdp, &mdp->mm_mutex, "mediatek,mm-mutex");
> +	if (ret)
> +		goto err_init_mm;
> +
> +	parent = dev->of_node->parent;
> +	/* Iterate over sibling MDP function blocks */
> +	for_each_child_of_node(parent, node) {
> +		const struct of_device_id *of_id;
> +		enum mdp_comp_type type;
> +		int id;
> +		struct mdp_comp *comp;
> +
> +		of_id = of_match_node(mdp_comp_dt_ids, node);
> +		if (!of_id)
> +			continue;
> +
> +		if (!of_device_is_available(node)) {
> +			dev_err(dev, "Skipping disabled component %pOF\n",
> +				node);

The function doesn't fail, so this doesn't look like error. Perhaps
dev_info()?

> +			continue;
> +		}
> +
> +		type = (enum mdp_comp_type)of_id->data;
> +		id = mdp_comp_get_id(dev, node, type);
> +		if (id < 0) {
> +			dev_warn(dev, "Skipping unknown component %pOF\n",
> +				 node);
> +			continue;
> +		}
> +
> +		comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL);
> +		if (!comp) {
> +			ret = -ENOMEM;
> +			goto err_init_comps;
> +		}
> +		mdp->comp[id] = comp;
> +
> +		ret = mdp_comp_init(dev, mdp, node, comp, id);
> +		if (ret)
> +			goto err_init_comps;
> +
> +		dev_info(dev, "%s type:%d alias:%d id:%d base:%#x regs:%p\n",
> +			 of_id->compatible, type, comp->alias_id, id,
> +			(u32)comp->reg_base, comp->regs);
> +	}
> +	return 0;
> +
> +err_init_comps:
> +	mdp_component_deinit(dev, mdp);
> +err_init_mm:
> +	return ret;
> +}
[snip]
> +static int mdp_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct mdp_dev *mdp;
> +	phandle rproc_phandle;
> +	int ret;
> +
> +	mdp = devm_kzalloc(dev, sizeof(*mdp), GFP_KERNEL);
> +	if (!mdp)
> +		return -ENOMEM;
> +
> +	mdp->pdev = pdev;
> +	ret = mdp_component_init(dev, mdp);
> +	if (ret) {
> +		dev_err(dev, "Failed to initialize mdp components\n");
> +		goto err_init_comp;

There should be no cleanup needed here.

Please always name the labels after the first cleanup step that is needed
after the jump. That helps avoiding such mistakes.

> +	}
> +
> +	mdp->job_wq = create_singlethread_workqueue(MDP_MODULE_NAME);

We should make it freezable.

> +	if (!mdp->job_wq) {
> +		dev_err(dev, "Unable to create job workqueue\n");
> +		ret = -ENOMEM;
> +		goto err_create_job_wq;
> +	}
> +
> +	mdp->vpu_dev = scp_get_pdev(pdev);
> +
> +	if (of_property_read_u32(pdev->dev.of_node, "mediatek,scp",
> +				 &rproc_phandle))
> +		dev_err(&pdev->dev, "Could not get scp device\n");

This should fail the probe.

> +	else
> +		dev_info(&pdev->dev, "Find mediatek,scp phandle:%llx\n",
> +			 (unsigned long long)rproc_phandle);

No need for this positive log.

> +
> +	mdp->rproc_handle = rproc_get_by_phandle(rproc_phandle);
> +
> +	dev_info(&pdev->dev, "MDP rproc_handle: %llx",
> +		 (unsigned long long)mdp->rproc_handle);
> +
> +	if (!mdp->rproc_handle)
> +		dev_err(&pdev->dev, "Could not get MDP's rproc_handle\n");

This should fail the probe too.

> +
> +	/* vpu_wdt_reg_handler(mdp->vpu_dev, mdp_reset_handler, mdp,
> +	 *		       VPU_RST_MDP);
> +	 */

Please remove unnecessary code.

> +	mutex_init(&mdp->vpu_lock);
> +	mdp->vpu_count = 0;
> +	mdp->id_count = 0;

No need to explicitly initialize to 0, because we just allocated a zeroed
struct earlier in this function.

> +
> +	mdp->cmdq_clt = cmdq_mbox_create(dev, 0, 1200);
> +	if (IS_ERR(mdp->cmdq_clt))
> +		goto err_mbox_create;
> +
> +	ret = v4l2_device_register(dev, &mdp->v4l2_dev);
> +	if (ret) {
> +		dev_err(dev, "Failed to register v4l2 device\n");
> +		ret = -EINVAL;
> +		goto err_v4l2_register;
> +	}
> +
> +	ret = mdp_m2m_device_register(mdp);
> +	if (ret) {
> +		v4l2_err(&mdp->v4l2_dev, "Failed to register m2m device\n");
> +		goto err_m2m_register;
> +	}
> +	mutex_init(&mdp->m2m_lock);
> +
> +	platform_set_drvdata(pdev, mdp);
> +
> +	vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32));
> +	pm_runtime_enable(dev);

This is racy, because mdp_m2m_device_register() already exposed the video
node to the userspace, before we ended the initialization. Please reorder the
initialization so that the device node is registered at the end, when the
driver is fully ready to accept userspace requests.

> +	dev_dbg(dev, "mdp-%d registered successfully\n", pdev->id);
> +	return 0;
> +
> +err_m2m_register:
> +	v4l2_device_unregister(&mdp->v4l2_dev);
> +err_v4l2_register:

Shouldn't we destroy the cmdq_mbox?

> +err_mbox_create:
> +	destroy_workqueue(mdp->job_wq);
> +err_create_job_wq:
> +err_init_comp:
> +	kfree(mdp);

This was allocated using devm_kzalloc(), so no need to free it explicitly.

> +
> +	dev_dbg(dev, "Errno %d\n", ret);
> +	return ret;
> +}
> +
> +static int mdp_remove(struct platform_device *pdev)
> +{
> +	struct mdp_dev *mdp = platform_get_drvdata(pdev);
> +
> +	pm_runtime_disable(&pdev->dev);
> +	vb2_dma_contig_clear_max_seg_size(&pdev->dev);
> +	mdp_m2m_device_unregister(mdp);

Same as in probe, removing must start with unregistering the userspace
interfaces.

> +	v4l2_device_unregister(&mdp->v4l2_dev);
> +
> +	flush_workqueue(mdp->job_wq);
> +	destroy_workqueue(mdp->job_wq);

destroy_workqueue() includes a flush (or drain specifically, which is
stricter than flush).

> +
> +	mdp_component_deinit(&pdev->dev, mdp);
> +	kfree(mdp);

Allocated by devm_kzalloc(), so no need to free here.

> +
> +	dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name);
> +	return 0;
> +}
> +
> +static int __maybe_unused mdp_pm_suspend(struct device *dev)
> +{
> +	// TODO: mdp clock off

Is MDP inside a power domain that is controlled by genpd? If yes, runtime
suspend would be cover for the power domain sequencing latency, which would
cause the clock to be running unnecessarily for that time. To avoid that,
one would control the clocks separately, outside of runtime PM callbacks.

> +	return 0;
> +}
> +
> +static int __maybe_unused mdp_pm_resume(struct device *dev)
> +{
> +	// TODO: mdp clock on
> +	return 0;
> +}
> +
> +static int __maybe_unused mdp_suspend(struct device *dev)
> +{
> +	if (pm_runtime_suspended(dev))
> +		return 0;
> +

We need to ensure here that the hardware is not doing any processing
anymore.

Hint: Looks like mdp_m2m_worker() is synchronous, so we could make the
workqueue freezable and the PM core would wait for the current pending work
to complete and then freeze the workqueue.

> +	return mdp_pm_suspend(dev);
> +}
> +
> +static int __maybe_unused mdp_resume(struct device *dev)
> +{
> +	if (pm_runtime_suspended(dev))
> +		return 0;
> +
> +	return mdp_pm_resume(dev);

We need to resume any processing that was queued to the driver before the
system suspended. Should be possible to handle by switching the workqueue to
freezable too.

> +}
> +
> +static const struct dev_pm_ops mdp_pm_ops = {
> +	SET_SYSTEM_SLEEP_PM_OPS(mdp_suspend, mdp_resume)
> +	SET_RUNTIME_PM_OPS(mdp_pm_suspend, mdp_pm_resume, NULL)
> +};
> +
> +static struct platform_driver mdp_driver = {
> +	.probe		= mdp_probe,
> +	.remove		= mdp_remove,
> +	.driver = {
> +		.name	= MDP_MODULE_NAME,
> +		.pm	= &mdp_pm_ops,
> +		.of_match_table = mdp_of_ids,

Please use the of_match_ptr() wrapper.

[snip]
> +static int mdp_m2m_ctrls_create(struct mdp_m2m_ctx *ctx)
> +{
> +	v4l2_ctrl_handler_init(&ctx->ctrl_handler, MDP_MAX_CTRLS);
> +	ctx->ctrls.hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,
> +					     &mdp_m2m_ctrl_ops, V4L2_CID_HFLIP,
> +					     0, 1, 1, 0);
> +	ctx->ctrls.vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,
> +					     &mdp_m2m_ctrl_ops, V4L2_CID_VFLIP,
> +					     0, 1, 1, 0);
> +	ctx->ctrls.rotate = v4l2_ctrl_new_std(&ctx->ctrl_handler,
> +					      &mdp_m2m_ctrl_ops,
> +					      V4L2_CID_ROTATE, 0, 270, 90, 0);
> +
> +	if (ctx->ctrl_handler.error) {
> +		int err = ctx->ctrl_handler.error;
> +
> +		v4l2_ctrl_handler_free(&ctx->ctrl_handler);
> +		dev_err(&ctx->mdp_dev->pdev->dev,
> +			"Failed to create control handler\n");

Perhaps "Failed to register controls"?

> +		return err;
> +	}
> +	return 0;
> +}
> +
> +static int mdp_m2m_open(struct file *file)
> +{
> +	struct video_device *vdev = video_devdata(file);
> +	struct mdp_dev *mdp = video_get_drvdata(vdev);
> +	struct mdp_m2m_ctx *ctx;
> +	int ret;
> +
> +	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
> +	if (!ctx)
> +		return -ENOMEM;
> +
> +	if (mutex_lock_interruptible(&mdp->m2m_lock)) {
> +		ret = -ERESTARTSYS;
> +		goto err_lock;
> +	}
> +
> +	ctx->id = mdp->id_count++;

Hmm, wouldn't this leave holes in the id space after we close? Should we use
something like ida?
See: https://www.kernel.org/doc/htmldocs/kernel-api/idr.html

> +	ctx->mdp_dev = mdp;
> +
> +	v4l2_fh_init(&ctx->fh, vdev);
> +	file->private_data = &ctx->fh;
> +	ret = mdp_m2m_ctrls_create(ctx);
> +	if (ret)
> +		goto err_ctrls_create;
> +
> +	/* Use separate control handler per file handle */
> +	ctx->fh.ctrl_handler = &ctx->ctrl_handler;
> +	v4l2_fh_add(&ctx->fh);
> +
> +	ctx->m2m_ctx = v4l2_m2m_ctx_init(mdp->m2m_dev, ctx, mdp_m2m_queue_init);
> +	if (IS_ERR(ctx->m2m_ctx)) {
> +		dev_err(&mdp->pdev->dev, "Failed to initialize m2m context\n");
> +		ret = PTR_ERR(ctx->m2m_ctx);
> +		goto err_m2m_ctx;
> +	}
> +	ctx->fh.m2m_ctx = ctx->m2m_ctx;
> +
> +	INIT_WORK(&ctx->work, mdp_m2m_worker);
> +	ctx->frame_count = 0;

No need to explicitly initialize fields to 0, because we just allocated the
struct using kzalloc().

> +
> +	ctx->curr_param = mdp_frameparam_init();
> +	if (IS_ERR(ctx->curr_param)) {
> +		dev_err(&mdp->pdev->dev,
> +			"Failed to initialize mdp parameter\n");
> +		ret = PTR_ERR(ctx->curr_param);
> +		goto err_param_init;
> +	}
> +	ctx->curr_param->type = MDP_STREAM_TYPE_BITBLT;

Why not initialize this in mdp_frameparam_init()? We only seem to be using
this value in this driver.

> +
> +	INIT_LIST_HEAD(&ctx->param_list);
> +	list_add_tail(&ctx->curr_param->list, &ctx->param_list);

Why do we need this list? We only seem to have ctx->curr_param in this
driver.

> +
> +	ret = mdp_vpu_get_locked(mdp);
> +	if (ret < 0)
> +		goto err_load_vpu;

This shouldn't happen in open(), but rather the latest possible point in
time. If one needs to keep the VPU running for the time of streaming, then
it should be start_streaming. If one can safely turn the VPU off if there is
no frame queued for long time, it should be just in m2m job_run.

Generally the userspace should be able to
just open an m2m device for querying it, without any side effects like
actually powering on the hardware or grabbing a hardware instance (which
could block some other processes, trying to grab one too).

> +
> +	mutex_unlock(&mdp->m2m_lock);
> +
> +	mdp_dbg(0, "%s [%d]", dev_name(&mdp->pdev->dev), ctx->id);
> +
> +	return 0;
> +
> +err_load_vpu:
> +	mdp_frameparam_release(ctx->curr_param);
> +err_param_init:
> +	v4l2_m2m_ctx_release(ctx->m2m_ctx);
> +err_m2m_ctx:
> +	v4l2_ctrl_handler_free(&ctx->ctrl_handler);
> +	v4l2_fh_del(&ctx->fh);
> +err_ctrls_create:
> +	v4l2_fh_exit(&ctx->fh);
> +	mutex_unlock(&mdp->m2m_lock);
> +err_lock:

Incorrect naming of all the error labels here.

> +	kfree(ctx);
> +
> +	return ret;
> +}
[snip]
> +enum mdp_ycbcr_profile mdp_map_ycbcr_prof_mplane(struct v4l2_format *f,
> +						 u32 mdp_color)
> +{
> +	struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
> +
> +	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;

Putting this under the default clause of the switch statement would be
cleaner and the comment wouldn't be needed.

[snip]
> +struct mdp_frameparam *mdp_frameparam_init(void)
> +{
> +	struct mdp_frameparam *param;
> +	struct mdp_frame *frame;
> +
> +	param = kzalloc(sizeof(*param), GFP_KERNEL);
> +	if (!param)
> +		return ERR_PTR(-ENOMEM);

We could just embed mdp_frameparam into the mdp_m2m_ctx struct and then
wouldn't need any dynamic allocation here anymore. And as a side effect, the
function could be just made void, because it couldn't fail.

> +
> +	INIT_LIST_HEAD(&param->list);
> +	mutex_init(&param->lock);
> +	param->state = 0;
> +	param->limit = &mdp_def_limit;
> +	param->type = MDP_STREAM_TYPE_UNKNOWN;

We always seem to use MDP_STREAM_TYPE_BITBLT in this driver.

> +	param->frame_no = 0;

No need for explicit initialization to 0.

Best regards,
Tomasz


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC v2 4/4] media: platform: mtk-mdp3: Add Mediatek MDP3 driver
  2019-06-04 11:20   ` Tomasz Figa
@ 2019-06-11  9:20     ` Daoyuan Huang
  2019-06-11 10:11       ` Tomasz Figa
  2019-06-20  4:48     ` Alexandre Courbot
  1 sibling, 1 reply; 12+ messages in thread
From: Daoyuan Huang @ 2019-06-11  9:20 UTC (permalink / raw)
  To: Tomasz Figa
  Cc: hans.verkuil, laurent.pinchart+renesas, matthias.bgg, mchehab,
	yuzhao, zwisler, linux-mediatek, linux-arm-kernel, Sean.Cheng,
	sj.huang, christie.yu, holmes.chiou, frederic.chen,
	Jerry-ch.Chen, jungo.lin, Rynn.Wu, linux-media, srv_heupstream,
	devicetree, Ping-Hsun Wu, acourbot

hi Tomasz:

Thanks for your review comments, the corresponding modification
& explanation is under preparation, will update soon.

Thanks.

BR
Daoyuan

On Tue, 2019-06-04 at 20:20 +0900, Tomasz Figa wrote:
> Hi Daoyuan,
> 
> On Thu, May 16, 2019 at 11:23:32AM +0800, Daoyuan Huang wrote:
> > From: daoyuan huang <daoyuan.huang@mediatek.com>
> > 
> > This patch adds driver for Media Data Path 3 (MDP3).
> > Each modules' related operation control is sited in mtk-mdp3-comp.c
> > Each modules' register table is defined in file with "mdp_reg_"
> > and "mmsys_" prefix
> > GCE related API, operation control  sited in mtk-mdp3-cmdq.c
> > V4L2 m2m device functions are implemented in mtk-mdp3-m2m.c
> > Probe, power, suspend/resume, system level functions are defined in
> > mtk-mdp3-core.c
> 
> Thanks for the patch. Some initial comments inline.
> 
> [snip]
> > +void mdp_comp_clock_on(struct device *dev, struct mdp_comp *comp)
> > +{
> > +	int i, err;
> > +
> > +	if (comp->larb_dev) {
> > +		err = pm_runtime_get_sync(comp->larb_dev);
> > +		if (err < 0)
> > +			dev_err(dev,
> > +				"Failed to get larb, err %d. type:%d id:%d\n",
> > +				err, comp->type, comp->id);
> 
> There is no need to make this conditional. Actually, we always need to grab
> a runtime PM enable count, otherwise the power domain would power off (if
> this SoC has gate'able power domains).
> 
> > +	}
> > +
> > +	for (i = 0; i < ARRAY_SIZE(comp->clks); i++) {
> > +		if (IS_ERR(comp->clks[i]))
> > +			break;
> > +		err = clk_prepare_enable(comp->clks[i]);
> > +		if (err)
> > +			dev_err(dev,
> > +				"Failed to enable clock %d, err %d. type:%d id:%d\n",
> > +				i, err, comp->type, comp->id);
> > +	}
> > +}
> > +
> > +void mdp_comp_clock_off(struct device *dev, struct mdp_comp *comp)
> > +{
> > +	int i;
> > +
> > +	for (i = 0; i < ARRAY_SIZE(comp->clks); i++) {
> > +		if (IS_ERR(comp->clks[i]))
> > +			break;
> > +		clk_disable_unprepare(comp->clks[i]);
> > +	}
> > +
> > +	if (comp->larb_dev)
> > +		pm_runtime_put_sync(comp->larb_dev);
> 
> Ditto. Also, it doesn't make sense to use the _sync variant here, we don't
> care if it powers off before the function returns or a bit after.
> 
> [snip]
> 
> > +static int mdp_comp_init(struct device *dev, struct mdp_dev *mdp,
> > +			 struct device_node *node, struct mdp_comp *comp,
> > +			 enum mdp_comp_id id)
> > +{
> > +	struct device_node *larb_node;
> > +	struct platform_device *larb_pdev;
> > +	int i;
> > +
> > +	if (id < 0 || id >= MDP_MAX_COMP_COUNT) {
> > +		dev_err(dev, "Invalid component id %d\n", id);
> > +		return -EINVAL;
> > +	}
> > +
> > +	__mdp_comp_init(mdp, node, comp);
> > +	comp->type = mdp_comp_matches[id].type;
> > +	comp->id = id;
> > +	comp->alias_id = mdp_comp_matches[id].alias_id;
> > +	comp->ops = mdp_comp_ops[comp->type];
> > +
> > +	for (i = 0; i < ARRAY_SIZE(comp->clks); i++) {
> > +		comp->clks[i] = of_clk_get(node, i);
> > +		if (IS_ERR(comp->clks[i]))
> > +			break;
> > +	}
> > +
> > +	mdp_get_subsys_id(dev, node, comp);
> > +
> > +	/* Only DMA capable components need the LARB property */
> > +	comp->larb_dev = NULL;
> > +	if (comp->type != MDP_COMP_TYPE_RDMA &&
> > +	    comp->type != MDP_COMP_TYPE_WROT &&
> > +		comp->type != MDP_COMP_TYPE_WDMA)
> > +		return 0;
> > +
> > +	larb_node = of_parse_phandle(node, "mediatek,larb", 0);
> > +	if (!larb_node) {
> > +		dev_err(dev, "Missing mediatek,larb phandle in %pOF node\n",
> > +			node);
> > +		return -EINVAL;
> > +	}
> > +
> > +	larb_pdev = of_find_device_by_node(larb_node);
> > +	if (!larb_pdev) {
> > +		dev_warn(dev, "Waiting for larb device %pOF\n", larb_node);
> > +		of_node_put(larb_node);
> > +		return -EPROBE_DEFER;
> > +	}
> > +	of_node_put(larb_node);
> > +
> > +	comp->larb_dev = &larb_pdev->dev;
> 
> Why do we need this larb_dev? I believe we already made the handling
> transparent by using device links, so as long as the driver handles runtime
> PM properly, it should automatically handle larbs.
> 
> > +
> > +	return 0;
> > +}
> > +
> > +static void mdp_comp_deinit(struct device *dev, struct mdp_comp *comp)
> > +{
> > +	iounmap(comp->regs);
> > +	/* of_node_put(comp->dev_node); */
> > +}
> > +
> > +void mdp_component_deinit(struct device *dev, struct mdp_dev *mdp)
> > +{
> > +	int i;
> > +
> > +	mdp_comp_deinit(dev, &mdp->mmsys);
> > +	mdp_comp_deinit(dev, &mdp->mm_mutex);
> > +	for (i = 0; i < ARRAY_SIZE(mdp->comp); i++) {
> > +		if (mdp->comp[i]) {
> > +			mdp_comp_deinit(dev, mdp->comp[i]);
> > +			kfree(mdp->comp[i]);
> > +		}
> > +	}
> > +}
> > +
> > +int mdp_component_init(struct device *dev, struct mdp_dev *mdp)
> > +{
> > +	struct device_node *node, *parent;
> > +	int i, ret;
> > +
> > +	for (i = 0; i < ARRAY_SIZE(gce_event_names); i++) {
> > +		s32 event_id;
> > +
> > +		event_id = cmdq_dev_get_event(dev, gce_event_names[i]);
> > +		mdp->event[i] = (event_id < 0) ? -i : event_id;
> > +		dev_info(dev, "Get event %s id:%d\n",
> > +			 gce_event_names[i], mdp->event[i]);
> > +	}
> > +
> > +	ret = mdp_mm_init(dev, mdp, &mdp->mmsys, "mediatek,mmsys");
> > +	if (ret)
> > +		goto err_init_mm;
> > +
> > +	ret = mdp_mm_init(dev, mdp, &mdp->mm_mutex, "mediatek,mm-mutex");
> > +	if (ret)
> > +		goto err_init_mm;
> > +
> > +	parent = dev->of_node->parent;
> > +	/* Iterate over sibling MDP function blocks */
> > +	for_each_child_of_node(parent, node) {
> > +		const struct of_device_id *of_id;
> > +		enum mdp_comp_type type;
> > +		int id;
> > +		struct mdp_comp *comp;
> > +
> > +		of_id = of_match_node(mdp_comp_dt_ids, node);
> > +		if (!of_id)
> > +			continue;
> > +
> > +		if (!of_device_is_available(node)) {
> > +			dev_err(dev, "Skipping disabled component %pOF\n",
> > +				node);
> 
> The function doesn't fail, so this doesn't look like error. Perhaps
> dev_info()?
> 
> > +			continue;
> > +		}
> > +
> > +		type = (enum mdp_comp_type)of_id->data;
> > +		id = mdp_comp_get_id(dev, node, type);
> > +		if (id < 0) {
> > +			dev_warn(dev, "Skipping unknown component %pOF\n",
> > +				 node);
> > +			continue;
> > +		}
> > +
> > +		comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL);
> > +		if (!comp) {
> > +			ret = -ENOMEM;
> > +			goto err_init_comps;
> > +		}
> > +		mdp->comp[id] = comp;
> > +
> > +		ret = mdp_comp_init(dev, mdp, node, comp, id);
> > +		if (ret)
> > +			goto err_init_comps;
> > +
> > +		dev_info(dev, "%s type:%d alias:%d id:%d base:%#x regs:%p\n",
> > +			 of_id->compatible, type, comp->alias_id, id,
> > +			(u32)comp->reg_base, comp->regs);
> > +	}
> > +	return 0;
> > +
> > +err_init_comps:
> > +	mdp_component_deinit(dev, mdp);
> > +err_init_mm:
> > +	return ret;
> > +}
> [snip]
> > +static int mdp_probe(struct platform_device *pdev)
> > +{
> > +	struct device *dev = &pdev->dev;
> > +	struct mdp_dev *mdp;
> > +	phandle rproc_phandle;
> > +	int ret;
> > +
> > +	mdp = devm_kzalloc(dev, sizeof(*mdp), GFP_KERNEL);
> > +	if (!mdp)
> > +		return -ENOMEM;
> > +
> > +	mdp->pdev = pdev;
> > +	ret = mdp_component_init(dev, mdp);
> > +	if (ret) {
> > +		dev_err(dev, "Failed to initialize mdp components\n");
> > +		goto err_init_comp;
> 
> There should be no cleanup needed here.
> 
> Please always name the labels after the first cleanup step that is needed
> after the jump. That helps avoiding such mistakes.
> 
> > +	}
> > +
> > +	mdp->job_wq = create_singlethread_workqueue(MDP_MODULE_NAME);
> 
> We should make it freezable.
> 
> > +	if (!mdp->job_wq) {
> > +		dev_err(dev, "Unable to create job workqueue\n");
> > +		ret = -ENOMEM;
> > +		goto err_create_job_wq;
> > +	}
> > +
> > +	mdp->vpu_dev = scp_get_pdev(pdev);
> > +
> > +	if (of_property_read_u32(pdev->dev.of_node, "mediatek,scp",
> > +				 &rproc_phandle))
> > +		dev_err(&pdev->dev, "Could not get scp device\n");
> 
> This should fail the probe.
> 
> > +	else
> > +		dev_info(&pdev->dev, "Find mediatek,scp phandle:%llx\n",
> > +			 (unsigned long long)rproc_phandle);
> 
> No need for this positive log.
> 
> > +
> > +	mdp->rproc_handle = rproc_get_by_phandle(rproc_phandle);
> > +
> > +	dev_info(&pdev->dev, "MDP rproc_handle: %llx",
> > +		 (unsigned long long)mdp->rproc_handle);
> > +
> > +	if (!mdp->rproc_handle)
> > +		dev_err(&pdev->dev, "Could not get MDP's rproc_handle\n");
> 
> This should fail the probe too.
> 
> > +
> > +	/* vpu_wdt_reg_handler(mdp->vpu_dev, mdp_reset_handler, mdp,
> > +	 *		       VPU_RST_MDP);
> > +	 */
> 
> Please remove unnecessary code.
> 
> > +	mutex_init(&mdp->vpu_lock);
> > +	mdp->vpu_count = 0;
> > +	mdp->id_count = 0;
> 
> No need to explicitly initialize to 0, because we just allocated a zeroed
> struct earlier in this function.
> 
> > +
> > +	mdp->cmdq_clt = cmdq_mbox_create(dev, 0, 1200);
> > +	if (IS_ERR(mdp->cmdq_clt))
> > +		goto err_mbox_create;
> > +
> > +	ret = v4l2_device_register(dev, &mdp->v4l2_dev);
> > +	if (ret) {
> > +		dev_err(dev, "Failed to register v4l2 device\n");
> > +		ret = -EINVAL;
> > +		goto err_v4l2_register;
> > +	}
> > +
> > +	ret = mdp_m2m_device_register(mdp);
> > +	if (ret) {
> > +		v4l2_err(&mdp->v4l2_dev, "Failed to register m2m device\n");
> > +		goto err_m2m_register;
> > +	}
> > +	mutex_init(&mdp->m2m_lock);
> > +
> > +	platform_set_drvdata(pdev, mdp);
> > +
> > +	vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32));
> > +	pm_runtime_enable(dev);
> 
> This is racy, because mdp_m2m_device_register() already exposed the video
> node to the userspace, before we ended the initialization. Please reorder the
> initialization so that the device node is registered at the end, when the
> driver is fully ready to accept userspace requests.
> 
> > +	dev_dbg(dev, "mdp-%d registered successfully\n", pdev->id);
> > +	return 0;
> > +
> > +err_m2m_register:
> > +	v4l2_device_unregister(&mdp->v4l2_dev);
> > +err_v4l2_register:
> 
> Shouldn't we destroy the cmdq_mbox?
> 
> > +err_mbox_create:
> > +	destroy_workqueue(mdp->job_wq);
> > +err_create_job_wq:
> > +err_init_comp:
> > +	kfree(mdp);
> 
> This was allocated using devm_kzalloc(), so no need to free it explicitly.
> 
> > +
> > +	dev_dbg(dev, "Errno %d\n", ret);
> > +	return ret;
> > +}
> > +
> > +static int mdp_remove(struct platform_device *pdev)
> > +{
> > +	struct mdp_dev *mdp = platform_get_drvdata(pdev);
> > +
> > +	pm_runtime_disable(&pdev->dev);
> > +	vb2_dma_contig_clear_max_seg_size(&pdev->dev);
> > +	mdp_m2m_device_unregister(mdp);
> 
> Same as in probe, removing must start with unregistering the userspace
> interfaces.
> 
> > +	v4l2_device_unregister(&mdp->v4l2_dev);
> > +
> > +	flush_workqueue(mdp->job_wq);
> > +	destroy_workqueue(mdp->job_wq);
> 
> destroy_workqueue() includes a flush (or drain specifically, which is
> stricter than flush).
> 
> > +
> > +	mdp_component_deinit(&pdev->dev, mdp);
> > +	kfree(mdp);
> 
> Allocated by devm_kzalloc(), so no need to free here.
> 
> > +
> > +	dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name);
> > +	return 0;
> > +}
> > +
> > +static int __maybe_unused mdp_pm_suspend(struct device *dev)
> > +{
> > +	// TODO: mdp clock off
> 
> Is MDP inside a power domain that is controlled by genpd? If yes, runtime
> suspend would be cover for the power domain sequencing latency, which would
> cause the clock to be running unnecessarily for that time. To avoid that,
> one would control the clocks separately, outside of runtime PM callbacks.
> 
> > +	return 0;
> > +}
> > +
> > +static int __maybe_unused mdp_pm_resume(struct device *dev)
> > +{
> > +	// TODO: mdp clock on
> > +	return 0;
> > +}
> > +
> > +static int __maybe_unused mdp_suspend(struct device *dev)
> > +{
> > +	if (pm_runtime_suspended(dev))
> > +		return 0;
> > +
> 
> We need to ensure here that the hardware is not doing any processing
> anymore.
> 
> Hint: Looks like mdp_m2m_worker() is synchronous, so we could make the
> workqueue freezable and the PM core would wait for the current pending work
> to complete and then freeze the workqueue.
> 
> > +	return mdp_pm_suspend(dev);
> > +}
> > +
> > +static int __maybe_unused mdp_resume(struct device *dev)
> > +{
> > +	if (pm_runtime_suspended(dev))
> > +		return 0;
> > +
> > +	return mdp_pm_resume(dev);
> 
> We need to resume any processing that was queued to the driver before the
> system suspended. Should be possible to handle by switching the workqueue to
> freezable too.
> 
> > +}
> > +
> > +static const struct dev_pm_ops mdp_pm_ops = {
> > +	SET_SYSTEM_SLEEP_PM_OPS(mdp_suspend, mdp_resume)
> > +	SET_RUNTIME_PM_OPS(mdp_pm_suspend, mdp_pm_resume, NULL)
> > +};
> > +
> > +static struct platform_driver mdp_driver = {
> > +	.probe		= mdp_probe,
> > +	.remove		= mdp_remove,
> > +	.driver = {
> > +		.name	= MDP_MODULE_NAME,
> > +		.pm	= &mdp_pm_ops,
> > +		.of_match_table = mdp_of_ids,
> 
> Please use the of_match_ptr() wrapper.
> 
> [snip]
> > +static int mdp_m2m_ctrls_create(struct mdp_m2m_ctx *ctx)
> > +{
> > +	v4l2_ctrl_handler_init(&ctx->ctrl_handler, MDP_MAX_CTRLS);
> > +	ctx->ctrls.hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,
> > +					     &mdp_m2m_ctrl_ops, V4L2_CID_HFLIP,
> > +					     0, 1, 1, 0);
> > +	ctx->ctrls.vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,
> > +					     &mdp_m2m_ctrl_ops, V4L2_CID_VFLIP,
> > +					     0, 1, 1, 0);
> > +	ctx->ctrls.rotate = v4l2_ctrl_new_std(&ctx->ctrl_handler,
> > +					      &mdp_m2m_ctrl_ops,
> > +					      V4L2_CID_ROTATE, 0, 270, 90, 0);
> > +
> > +	if (ctx->ctrl_handler.error) {
> > +		int err = ctx->ctrl_handler.error;
> > +
> > +		v4l2_ctrl_handler_free(&ctx->ctrl_handler);
> > +		dev_err(&ctx->mdp_dev->pdev->dev,
> > +			"Failed to create control handler\n");
> 
> Perhaps "Failed to register controls"?
> 
> > +		return err;
> > +	}
> > +	return 0;
> > +}
> > +
> > +static int mdp_m2m_open(struct file *file)
> > +{
> > +	struct video_device *vdev = video_devdata(file);
> > +	struct mdp_dev *mdp = video_get_drvdata(vdev);
> > +	struct mdp_m2m_ctx *ctx;
> > +	int ret;
> > +
> > +	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
> > +	if (!ctx)
> > +		return -ENOMEM;
> > +
> > +	if (mutex_lock_interruptible(&mdp->m2m_lock)) {
> > +		ret = -ERESTARTSYS;
> > +		goto err_lock;
> > +	}
> > +
> > +	ctx->id = mdp->id_count++;
> 
> Hmm, wouldn't this leave holes in the id space after we close? Should we use
> something like ida?
> See: https://www.kernel.org/doc/htmldocs/kernel-api/idr.html
> 
> > +	ctx->mdp_dev = mdp;
> > +
> > +	v4l2_fh_init(&ctx->fh, vdev);
> > +	file->private_data = &ctx->fh;
> > +	ret = mdp_m2m_ctrls_create(ctx);
> > +	if (ret)
> > +		goto err_ctrls_create;
> > +
> > +	/* Use separate control handler per file handle */
> > +	ctx->fh.ctrl_handler = &ctx->ctrl_handler;
> > +	v4l2_fh_add(&ctx->fh);
> > +
> > +	ctx->m2m_ctx = v4l2_m2m_ctx_init(mdp->m2m_dev, ctx, mdp_m2m_queue_init);
> > +	if (IS_ERR(ctx->m2m_ctx)) {
> > +		dev_err(&mdp->pdev->dev, "Failed to initialize m2m context\n");
> > +		ret = PTR_ERR(ctx->m2m_ctx);
> > +		goto err_m2m_ctx;
> > +	}
> > +	ctx->fh.m2m_ctx = ctx->m2m_ctx;
> > +
> > +	INIT_WORK(&ctx->work, mdp_m2m_worker);
> > +	ctx->frame_count = 0;
> 
> No need to explicitly initialize fields to 0, because we just allocated the
> struct using kzalloc().
> 
> > +
> > +	ctx->curr_param = mdp_frameparam_init();
> > +	if (IS_ERR(ctx->curr_param)) {
> > +		dev_err(&mdp->pdev->dev,
> > +			"Failed to initialize mdp parameter\n");
> > +		ret = PTR_ERR(ctx->curr_param);
> > +		goto err_param_init;
> > +	}
> > +	ctx->curr_param->type = MDP_STREAM_TYPE_BITBLT;
> 
> Why not initialize this in mdp_frameparam_init()? We only seem to be using
> this value in this driver.
> 
> > +
> > +	INIT_LIST_HEAD(&ctx->param_list);
> > +	list_add_tail(&ctx->curr_param->list, &ctx->param_list);
> 
> Why do we need this list? We only seem to have ctx->curr_param in this
> driver.
> 
> > +
> > +	ret = mdp_vpu_get_locked(mdp);
> > +	if (ret < 0)
> > +		goto err_load_vpu;
> 
> This shouldn't happen in open(), but rather the latest possible point in
> time. If one needs to keep the VPU running for the time of streaming, then
> it should be start_streaming. If one can safely turn the VPU off if there is
> no frame queued for long time, it should be just in m2m job_run.
> 
> Generally the userspace should be able to
> just open an m2m device for querying it, without any side effects like
> actually powering on the hardware or grabbing a hardware instance (which
> could block some other processes, trying to grab one too).
> 
> > +
> > +	mutex_unlock(&mdp->m2m_lock);
> > +
> > +	mdp_dbg(0, "%s [%d]", dev_name(&mdp->pdev->dev), ctx->id);
> > +
> > +	return 0;
> > +
> > +err_load_vpu:
> > +	mdp_frameparam_release(ctx->curr_param);
> > +err_param_init:
> > +	v4l2_m2m_ctx_release(ctx->m2m_ctx);
> > +err_m2m_ctx:
> > +	v4l2_ctrl_handler_free(&ctx->ctrl_handler);
> > +	v4l2_fh_del(&ctx->fh);
> > +err_ctrls_create:
> > +	v4l2_fh_exit(&ctx->fh);
> > +	mutex_unlock(&mdp->m2m_lock);
> > +err_lock:
> 
> Incorrect naming of all the error labels here.
> 
> > +	kfree(ctx);
> > +
> > +	return ret;
> > +}
> [snip]
> > +enum mdp_ycbcr_profile mdp_map_ycbcr_prof_mplane(struct v4l2_format *f,
> > +						 u32 mdp_color)
> > +{
> > +	struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
> > +
> > +	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;
> 
> Putting this under the default clause of the switch statement would be
> cleaner and the comment wouldn't be needed.
> 
> [snip]
> > +struct mdp_frameparam *mdp_frameparam_init(void)
> > +{
> > +	struct mdp_frameparam *param;
> > +	struct mdp_frame *frame;
> > +
> > +	param = kzalloc(sizeof(*param), GFP_KERNEL);
> > +	if (!param)
> > +		return ERR_PTR(-ENOMEM);
> 
> We could just embed mdp_frameparam into the mdp_m2m_ctx struct and then
> wouldn't need any dynamic allocation here anymore. And as a side effect, the
> function could be just made void, because it couldn't fail.
> 
> > +
> > +	INIT_LIST_HEAD(&param->list);
> > +	mutex_init(&param->lock);
> > +	param->state = 0;
> > +	param->limit = &mdp_def_limit;
> > +	param->type = MDP_STREAM_TYPE_UNKNOWN;
> 
> We always seem to use MDP_STREAM_TYPE_BITBLT in this driver.
> 
> > +	param->frame_no = 0;
> 
> No need for explicit initialization to 0.
> 
> Best regards,
> Tomasz
> 



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC v2 4/4] media: platform: mtk-mdp3: Add Mediatek MDP3 driver
  2019-06-11  9:20     ` Daoyuan Huang
@ 2019-06-11 10:11       ` Tomasz Figa
  0 siblings, 0 replies; 12+ messages in thread
From: Tomasz Figa @ 2019-06-11 10:11 UTC (permalink / raw)
  To: Daoyuan Huang, Alexandre Courbot
  Cc: Hans Verkuil, Laurent Pinchart, Matthias Brugger,
	Mauro Carvalho Chehab, yuzhao, zwisler,
	moderated list:ARM/Mediatek SoC support,
	list@263.net:IOMMU DRIVERS
	<iommu@lists.linux-foundation.org>,
	Joerg Roedel <joro@8bytes.org>,,
	Sean Cheng (鄭昇弘),
	Sj Huang, Christie Yu (游雅惠),
	Holmes Chiou (邱挺),
	Frederic Chen (陳俊元),
	Jerry-ch Chen, Jungo Lin (林明俊),
	Rynn Wu (吳育恩),
	Linux Media Mailing List, srv_heupstream, devicetree,
	Ping-Hsun Wu

Hi Daoyuan,

On Tue, Jun 11, 2019 at 6:20 PM Daoyuan Huang
<daoyuan.huang@mediatek.com> wrote:
>
> hi Tomasz:
>
> Thanks for your review comments, the corresponding modification
> & explanation is under preparation, will update soon.
>
> Thanks.

Thanks.

Note that Alexandre may already be reviewing the rest of this patch,
so I'd consult with him if sending a next revision or waiting for his
review is preferred.

Best regards,
Tomasz

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC v2 1/4] dt-binding: mt8183: Add Mediatek MDP3 dt-bindings
  2019-05-16  3:23 ` [RFC v2 1/4] dt-binding: mt8183: Add Mediatek MDP3 dt-bindings Daoyuan Huang
@ 2019-06-13 21:25   ` Rob Herring
  0 siblings, 0 replies; 12+ messages in thread
From: Rob Herring @ 2019-06-13 21:25 UTC (permalink / raw)
  To: Daoyuan Huang
  Cc: hans.verkuil, laurent.pinchart+renesas, tfiga, matthias.bgg,
	mchehab, yuzhao, zwisler, linux-mediatek, linux-arm-kernel,
	Sean.Cheng, sj.huang, christie.yu, holmes.chiou, frederic.chen,
	Jerry-ch.Chen, jungo.lin, Rynn.Wu, linux-media, srv_heupstream,
	devicetree, Ping-Hsun Wu

On Thu, May 16, 2019 at 11:23:29AM +0800, Daoyuan Huang wrote:
> From: daoyuan huang <daoyuan.huang@mediatek.com>
> 
> This patch adds DT binding document for Media Data Path 3 (MDP3)
> a unit in multimedia system used for scaling and color format convert.
> 
> Signed-off-by: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
> Signed-off-by: daoyuan huang <daoyuan.huang@mediatek.com>
> ---
>  .../bindings/media/mediatek,mt8183-mdp3.txt   | 217 ++++++++++++++++++
>  1 file changed, 217 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/mediatek,mt8183-mdp3.txt
> 
> diff --git a/Documentation/devicetree/bindings/media/mediatek,mt8183-mdp3.txt b/Documentation/devicetree/bindings/media/mediatek,mt8183-mdp3.txt
> new file mode 100644
> index 000000000000..cf3e808b7146
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/mediatek,mt8183-mdp3.txt
> @@ -0,0 +1,217 @@
> +* Mediatek Media Data Path 3
> +
> +Media Data Path 3 (MDP3) is used for scaling and color space conversion.
> +
> +Required properties (controller node):
> +- compatible: "mediatek,mt8183-mdp"
> +- mediatek,scp: the node of system control processor (SCP), using the
> +  remoteproc & rpmsg framework, see
> +  Documentation/devicetree/bindings/remoteproc/mtk,scp.txt for details.
> +- mediatek,mmsys: the node of mux(multiplexer) controller for HW connections.
> +- mediatek,mm-mutex: the node of sof(start of frame) signal controller.
> +- mediatek,mailbox-gce: the node of global command engine (GCE), used to
> +  read/write registers with critical time limitation, see
> +  Documentation/devicetree/bindings/mailbox/mtk-gce.txt for details.
> +- mboxes: mailbox number used to communicate with GCE.
> +- gce-subsys: sub-system id corresponding to the register address.
> +- gce-event-names: in use event name list, used to correspond to event IDs.
> +- gce-events: in use event IDs list, all IDs are defined in
> +  'dt-bindings/gce/mt8183-gce.h'.
> +
> +Required properties (all function blocks, child node):
> +- compatible: Should be one of
> +        "mediatek,mt8183-mdp-rdma"  - read DMA
> +        "mediatek,mt8183-mdp-rsz"   - resizer
> +        "mediatek,mt8183-mdp-wdma"  - write DMA
> +        "mediatek,mt8183-mdp-wrot"  - write DMA with rotation
> +        "mediatek,mt8183-mdp-ccorr" - color correction with 3X3 matrix
> +- reg: Physical base address and length of the function block register space
> +- clocks: device clocks, see
> +  Documentation/devicetree/bindings/clock/clock-bindings.txt for details.
> +- power-domains: a phandle to the power domain, see
> +  Documentation/devicetree/bindings/power/power_domain.txt for details.
> +- mediatek,mdp-id: HW index to distinguish same functionality modules.
> +
> +Required properties (DMA function blocks, child node):
> +- compatible: Should be one of
> +        "mediatek,mt8183-mdp-rdma"
> +        "mediatek,mt8183-mdp-wdma"
> +        "mediatek,mt8183-mdp-wrot"
> +- iommus: should point to the respective IOMMU block with master port as
> +  argument, see Documentation/devicetree/bindings/iommu/mediatek,iommu.txt
> +  for details.
> +- mediatek,larb: must contain the local arbiters in the current Socs, see
> +  Documentation/devicetree/bindings/memory-controllers/mediatek,smi-larb.txt
> +  for details.
> +
> +Required properties (input path selection node):
> +- compatible:
> +        "mediatek,mt8183-mdp-dl"    - MDP direct link input source selection
> +- reg: Physical base address and length of the function block register space
> +- clocks: device clocks, see
> +  Documentation/devicetree/bindings/clock/clock-bindings.txt for details.
> +- mediatek,mdp-id: HW index to distinguish same functionality modules.
> +
> +Required properties (ISP PASS2 (DIP) module path selection node):
> +- compatible:
> +        "mediatek,mt8183-mdp-imgi"  - input DMA of ISP PASS2 (DIP) module for raw image input
> +- reg: Physical base address and length of the function block register space
> +- mediatek,mdp-id: HW index to distinguish same functionality modules.
> +
> +Required properties (SW node):
> +- compatible: Should be one of
> +        "mediatek,mt8183-mdp-exto"  - output DMA of ISP PASS2 (DIP) module for yuv image output
> +        "mediatek,mt8183-mdp-path"  - MDP output path selection
> +- mediatek,mdp-id: HW index to distinguish same functionality modules.
> +
> +Example:
> +		mdp_camin@14000000 {

s/_/-/ in node names

> +			compatible = "mediatek,mt8183-mdp-dl";
> +			mediatek,mdp-id = <0>;
> +			reg = <0 0x14000000 0 0x1000>;
> +			clocks = <&mmsys CLK_MM_MDP_DL_TXCK>,
> +				<&mmsys CLK_MM_MDP_DL_RX>;
> +		};
> +
> +		mdp_camin2@14000000 {
> +			compatible = "mediatek,mt8183-mdp-dl";
> +			mediatek,mdp-id = <1>;
> +			reg = <0 0x14000000 0 0x1000>;

You've got 2 nodes at the same address. You can't do that.

Build your dtb with W=1 and dtc will warn on this. And fix any other 
warnings you get.

> +			clocks = <&mmsys CLK_MM_IPU_DL_TXCK>,
> +				<&mmsys CLK_MM_IPU_DL_RX>;
> +		};
> +
> +		mdp_rdma0: mdp_rdma0@14001000 {
> +			compatible = "mediatek,mt8183-mdp-rdma", "mediatek,mt8183-mdp3";
> +			mediatek,scp = <&scp>;
> +			mediatek,mdp-id = <0>;
> +			reg = <0 0x14001000 0 0x1000>;
> +			power-domains = <&scpsys MT8183_POWER_DOMAIN_DISP>;
> +			clocks = <&mmsys CLK_MM_MDP_RDMA0>,
> +				<&mmsys CLK_MM_MDP_RSZ1>;
> +			iommus = <&iommu M4U_PORT_MDP_RDMA0>;
> +			mediatek,larb = <&larb0>;
> +			mediatek,mmsys = <&mmsys>;
> +			mediatek,mm-mutex = <&mutex>;
> +			mediatek,mailbox-gce = <&gce>;
> +			mboxes = <&gce 20 0 CMDQ_THR_PRIO_LOWEST>,
> +				<&gce 21 0 CMDQ_THR_PRIO_LOWEST>,
> +				<&gce 22 0 CMDQ_THR_PRIO_LOWEST>,
> +				<&gce 23 0 CMDQ_THR_PRIO_LOWEST>;
> +			gce-subsys = <&gce 0x14000000 SUBSYS_1400XXXX>,
> +				<&gce 0x14010000 SUBSYS_1401XXXX>,
> +				<&gce 0x14020000 SUBSYS_1402XXXX>,
> +				<&gce 0x15020000 SUBSYS_1502XXXX>;
> +			gce-event-names = "rdma0_sof",
> +				"rsz0_sof",
> +				"rsz1_sof",
> +				"tdshp0_sof",
> +				"wrot0_sof",
> +				"wdma0_sof",
> +				"rdma0_done",
> +				"wrot0_done",
> +				"wdma0_done",
> +				"isp_p2_0_done",
> +				"isp_p2_1_done",
> +				"isp_p2_2_done",
> +				"isp_p2_3_done",
> +				"isp_p2_4_done",
> +				"isp_p2_5_done",
> +				"isp_p2_6_done",
> +				"isp_p2_7_done",
> +				"isp_p2_8_done",
> +				"isp_p2_9_done",
> +				"isp_p2_10_done",
> +				"isp_p2_11_done",
> +				"isp_p2_12_done",
> +				"isp_p2_13_done",
> +				"isp_p2_14_done",
> +				"wpe_done",
> +				"wpe_b_done";
> +			gce-events = <&gce CMDQ_EVENT_MDP_RDMA0_SOF>,
> +				<&gce CMDQ_EVENT_MDP_RSZ0_SOF>,
> +				<&gce CMDQ_EVENT_MDP_RSZ1_SOF>,
> +				<&gce CMDQ_EVENT_MDP_TDSHP_SOF>,
> +				<&gce CMDQ_EVENT_MDP_WROT0_SOF>,
> +				<&gce CMDQ_EVENT_MDP_WDMA0_SOF>,
> +				<&gce CMDQ_EVENT_MDP_RDMA0_EOF>,
> +				<&gce CMDQ_EVENT_MDP_WROT0_EOF>,
> +				<&gce CMDQ_EVENT_MDP_WDMA0_EOF>,
> +				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_0>,
> +				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_1>,
> +				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_2>,
> +				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_3>,
> +				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_4>,
> +				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_5>,
> +				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_6>,
> +				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_7>,
> +				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_8>,
> +				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_9>,
> +				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_10>,
> +				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_11>,
> +				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_12>,
> +				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_13>,
> +				<&gce CMDQ_EVENT_ISP_FRAME_DONE_P2_14>,
> +				<&gce CMDQ_EVENT_WPE_A_DONE>,
> +				<&gce CMDQ_EVENT_SPE_B_DONE>;
> +		};
> +
> +		mdp_imgi@15020000 {
> +			compatible = "mediatek,mt8183-mdp-imgi";
> +			mediatek,mdp-id = <0>;
> +			reg = <0 0x15020000 0 0x1000>;
> +		};
> +
> +		mdp_img2o@15020000 {
> +			compatible = "mediatek,mt8183-mdp-exto";
> +			mediatek,mdp-id = <1>;

Missing reg? Again, looks like 2 nodes at the same address.

> +		};
> +
> +		mdp_rsz0: mdp_rsz0@14003000 {
> +			compatible = "mediatek,mt8183-mdp-rsz";
> +			mediatek,mdp-id = <0>;
> +			reg = <0 0x14003000 0 0x1000>;
> +			clocks = <&mmsys CLK_MM_MDP_RSZ0>;
> +		};
> +
> +		mdp_rsz1: mdp_rsz1@14004000 {
> +			compatible = "mediatek,mt8183-mdp-rsz";
> +			mediatek,mdp-id = <1>;
> +			reg = <0 0x14004000 0 0x1000>;
> +			clocks = <&mmsys CLK_MM_MDP_RSZ1>;
> +		};
> +
> +		mdp_wrot0: mdp_wrot0@14005000 {
> +			compatible = "mediatek,mt8183-mdp-wrot";
> +			mediatek,mdp-id = <0>;
> +			reg = <0 0x14005000 0 0x1000>;
> +			clocks = <&mmsys CLK_MM_MDP_WROT0>;
> +			iommus = <&iommu M4U_PORT_MDP_WROT0>;
> +			mediatek,larb = <&larb0>;
> +		};
> +
> +		mdp_path0_sout@14005000 {
> +			compatible = "mediatek,mt8183-mdp-path";
> +			mediatek,mdp-id = <0>;

Missing reg? Again, looks like 2 nodes at the same address.

> +		};
> +
> +		mdp_wdma: mdp_wdma@14006000 {
> +			compatible = "mediatek,mt8183-mdp-wdma";
> +			mediatek,mdp-id = <0>;
> +			reg = <0 0x14006000 0 0x1000>;
> +			clocks = <&mmsys CLK_MM_MDP_WDMA0>;
> +			iommus = <&iommu M4U_PORT_MDP_WDMA0>;
> +			mediatek,larb = <&larb0>;
> +		};
> +
> +		mdp_path1_sout@14006000 {
> +			compatible = "mediatek,mt8183-mdp-path";
> +			mediatek,mdp-id = <1>;
> +		};
> +
> +		mdp_ccorr: mdp_ccorr@1401c000 {
> +			compatible = "mediatek,mt8183-mdp-ccorr";
> +			mediatek,mdp-id = <0>;
> +			reg = <0 0x1401c000 0 0x1000>;
> +			clocks = <&mmsys CLK_MM_MDP_CCORR>;
> +		};
> -- 
> 2.18.0
> 

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC v2 4/4] media: platform: mtk-mdp3: Add Mediatek MDP3 driver
  2019-05-16  3:23 ` [RFC v2 4/4] media: platform: mtk-mdp3: Add Mediatek MDP3 driver Daoyuan Huang
  2019-06-04 11:20   ` Tomasz Figa
@ 2019-06-20  4:47   ` Alexandre Courbot
  1 sibling, 0 replies; 12+ messages in thread
From: Alexandre Courbot @ 2019-06-20  4:47 UTC (permalink / raw)
  To: Daoyuan Huang
  Cc: Hans Verkuil, Laurent Pinchart, Tomasz Figa, matthias.bgg,
	Mauro Carvalho Chehab, yuzhao, zwisler, linux-mediatek,
	linux-arm-kernel, Sean.Cheng, Sj Huang, christie.yu,
	holmes.chiou, frederic.chen, Jerry-ch.Chen, jungo.lin, Rynn.Wu,
	Linux Media Mailing List, srv_heupstream, devicetree,
	Ping-Hsun Wu

Hi Daoyuan, sorry for having been so long to come back to this!

I know you have another version prepared, but here are a few extra
things that may need attention.

On Thu, May 16, 2019 at 12:25 PM Daoyuan Huang
<daoyuan.huang@mediatek.com> wrote:
>
> From: daoyuan huang <daoyuan.huang@mediatek.com>
>
> This patch adds driver for Media Data Path 3 (MDP3).
> Each modules' related operation control is sited in mtk-mdp3-comp.c
> Each modules' register table is defined in file with "mdp_reg_"
> and "mmsys_" prefix
> GCE related API, operation control  sited in mtk-mdp3-cmdq.c
> V4L2 m2m device functions are implemented in mtk-mdp3-m2m.c
> Probe, power, suspend/resume, system level functions are defined in
> mtk-mdp3-core.c
>
> Signed-off-by: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
> Signed-off-by: daoyuan huang <daoyuan.huang@mediatek.com>

[snip]

> diff --git a/drivers/media/platform/mtk-mdp3/mtk-img-ipi.h b/drivers/media/platform/mtk-mdp3/mtk-img-ipi.h
> new file mode 100644
> index 000000000000..9fabe7e8b71d
> --- /dev/null
> +++ b/drivers/media/platform/mtk-mdp3/mtk-img-ipi.h
> @@ -0,0 +1,282 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2018 MediaTek Inc.
> + * Author: Holmes Chiou <holmes.chiou@mediatek.com>
> + *         Ping-Hsun Wu <ping-hsun.wu@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_IMG_IPI_H__
> +#define __MTK_IMG_IPI_H__
> +
> +#include <linux/types.h>
> +
> +/* ISP-MDP generic input information */
> +
> +#define IMG_MAX_HW_INPUTS      1
> +
> +#define IMG_MAX_HW_OUTPUTS     4
> +
> +#define IMG_MAX_PLANES         3
> +
> +#define IMG_IPI_INIT    1
> +#define IMG_IPI_DEINIT  2
> +#define IMG_IPI_FRAME   3
> +#define IMG_IPI_DEBUG   4
> +
> +struct img_addr {
> +       u64     va;     /* Used by Linux OS access */
> +       u32     pa;     /* Used by CM4 access */
> +       u32     iova;   /* Used by IOMMU HW access */
> +} __attribute__ ((__packed__));
> +
> +struct img_sw_addr {
> +       u64     va;     /* Used by APMCU access */
> +       u32     pa;     /* Used by CM4 access */
> +} __attribute__ ((__packed__));
> +
> +struct img_plane_format {
> +       u32     size;
> +       u16     stride;
> +} __attribute__ ((__packed__));
> +
> +struct img_pix_format {
> +       u16             width;
> +       u16             height;
> +       u32             colorformat;    /* enum mdp_color */
> +       u16             ycbcr_prof;     /* enum mdp_ycbcr_profile */
> +       struct img_plane_format plane_fmt[IMG_MAX_PLANES];
> +} __attribute__ ((__packed__));
> +
> +struct img_image_buffer {
> +       struct img_pix_format   format;
> +       u32             iova[IMG_MAX_PLANES];
> +       /* enum mdp_buffer_usage, FD or advanced ISP usages */
> +       u32             usage;
> +} __attribute__ ((__packed__));
> +
> +#define IMG_SUBPIXEL_SHIFT     20
> +
> +struct img_crop {
> +       s16             left;
> +       s16             top;

One tab too much here? Looks like there are similar indentation issues
elsewhere as well, please confirm.

> +       u16     width;
> +       u16     height;
> +       u32     left_subpix;
> +       u32     top_subpix;
> +       u32     width_subpix;
> +       u32     height_subpix;
> +} __attribute__ ((__packed__));
> +
> +#define IMG_CTRL_FLAG_HFLIP    BIT(0)
> +#define IMG_CTRL_FLAG_DITHER   BIT(1)
> +#define IMG_CTRL_FLAG_SHARPNESS        BIT(4)
> +#define IMG_CTRL_FLAG_HDR      BIT(5)
> +#define IMG_CTRL_FLAG_DRE      BIT(6)
> +
> +struct img_input {
> +       struct img_image_buffer buffer;
> +       u16             flags;  /* HDR, DRE, dither */
> +} __attribute__ ((__packed__));
> +
> +struct img_output {
> +       struct img_image_buffer buffer;
> +       struct img_crop         crop;
> +       s16                     rotation;
> +       u16             flags;  /* H-flip, sharpness, dither */
> +} __attribute__ ((__packed__));
> +
> +struct img_ipi_frameparam {
> +       u32             index;
> +       u32             frame_no;
> +       u64             timestamp;
> +       u8                      type;   /* enum mdp_stream_type */
> +       u8                      state;
> +       u8                      num_inputs;
> +       u8                      num_outputs;
> +       u64             drv_data;
> +       struct img_input        inputs[IMG_MAX_HW_INPUTS];
> +       struct img_output       outputs[IMG_MAX_HW_OUTPUTS];
> +       struct img_addr         tuning_data;
> +       struct img_addr         subfrm_data;
> +       struct img_sw_addr      config_data;
> +       struct img_sw_addr  self_data;
> +       /* u8           pq_data[]; */
> +} __attribute__ ((__packed__));
> +
> +struct img_ipi_param {
> +       u8      usage;
> +       struct  img_sw_addr frm_param;
> +} __attribute__ ((__packed__));
> +
> +struct img_frameparam {
> +       struct list_head        list_entry;
> +       struct img_ipi_frameparam frameparam;
> +};
> +
> +/* ISP-MDP generic output information */
> +
> +struct img_comp_frame {
> +       u32     output_disable:1;
> +       u32     bypass:1;
> +       u16     in_width;
> +       u16     in_height;
> +       u16     out_width;
> +       u16     out_height;
> +       struct img_crop crop;
> +       u16     in_total_width;
> +       u16     out_total_width;
> +} __attribute__ ((__packed__));
> +
> +struct img_region {
> +       s16     left;
> +       s16     right;
> +       s16     top;
> +       s16     bottom;
> +} __attribute__ ((__packed__));
> +
> +struct img_offset {
> +       s16             left;
> +       s16             top;
> +       u32     left_subpix;
> +       u32     top_subpix;
> +} __attribute__ ((__packed__));
> +
> +struct img_comp_subfrm {
> +       u32             tile_disable:1;
> +       struct img_region       in;
> +       struct img_region       out;
> +       struct img_offset       luma;
> +       struct img_offset       chroma;
> +       s16                     out_vertical;   /* Output vertical index */
> +       s16                     out_horizontal; /* Output horizontal index */
> +} __attribute__ ((__packed__));
> +
> +#define IMG_MAX_SUBFRAMES      12
> +
> +struct mdp_rdma_subfrm {
> +       u32     offset[IMG_MAX_PLANES];
> +       u32     offset_0_p;
> +       u32     src;
> +       u32     clip;
> +       u32     clip_ofst;
> +} __attribute__ ((__packed__));
> +
> +struct mdp_rdma_data {
> +       u32             src_ctrl;
> +       u32             control;
> +       u32             iova[IMG_MAX_PLANES];
> +       u32             iova_end[IMG_MAX_PLANES];
> +       u32             mf_bkgd;
> +       u32             mf_bkgd_in_pxl;
> +       u32             sf_bkgd;
> +       u32             ufo_dec_y;
> +       u32             ufo_dec_c;
> +       u32             transform;
> +       struct mdp_rdma_subfrm  subfrms[IMG_MAX_SUBFRAMES];
> +} __attribute__ ((__packed__));
> +
> +struct mdp_rsz_subfrm {
> +       u32     control2;
> +       u32     src;
> +       u32     clip;
> +} __attribute__ ((__packed__));
> +
> +struct mdp_rsz_data {
> +       u32             coeff_step_x;
> +       u32             coeff_step_y;
> +       u32             control1;
> +       u32             control2;
> +       struct mdp_rsz_subfrm   subfrms[IMG_MAX_SUBFRAMES];
> +} __attribute__ ((__packed__));
> +
> +struct mdp_wrot_subfrm {
> +       u32     offset[IMG_MAX_PLANES];
> +       u32     src;
> +       u32     clip;
> +       u32     clip_ofst;
> +       u32     main_buf;
> +} __attribute__ ((__packed__));
> +
> +struct mdp_wrot_data {
> +       u32             iova[IMG_MAX_PLANES];
> +       u32             control;
> +       u32             stride[IMG_MAX_PLANES];
> +       u32             mat_ctrl;
> +       u32             fifo_test;
> +       u32             filter;
> +       struct mdp_wrot_subfrm  subfrms[IMG_MAX_SUBFRAMES];
> +} __attribute__ ((__packed__));
> +
> +struct mdp_wdma_subfrm {
> +       u32     offset[IMG_MAX_PLANES];
> +       u32     src;
> +       u32     clip;
> +       u32     clip_ofst;
> +} __attribute__ ((__packed__));
> +
> +struct mdp_wdma_data {
> +       u32             wdma_cfg;
> +       u32             iova[IMG_MAX_PLANES];
> +       u32             w_in_byte;
> +       u32             uv_stride;
> +       struct mdp_wdma_subfrm  subfrms[IMG_MAX_SUBFRAMES];
> +} __attribute__ ((__packed__));
> +
> +struct isp_data {
> +       u64     dl_flags;       /* 1 << (enum mdp_comp_type) */
> +       u32     smxi_iova[4];
> +       u32     cq_idx;
> +       u32     cq_iova;
> +       u32     tpipe_iova[IMG_MAX_SUBFRAMES];
> +} __attribute__ ((__packed__));
> +
> +struct img_compparam {
> +       u16             type;   /* enum mdp_comp_type */
> +       u16             id;     /* enum mdp_comp_id */
> +       u32             input;
> +       u32             outputs[IMG_MAX_HW_OUTPUTS];
> +       u32             num_outputs;
> +       struct img_comp_frame   frame;
> +       struct img_comp_subfrm  subfrms[IMG_MAX_SUBFRAMES];
> +       u32             num_subfrms;
> +       union {
> +               struct mdp_rdma_data    rdma;
> +               struct mdp_rsz_data     rsz;
> +               struct mdp_wrot_data    wrot;
> +               struct mdp_wdma_data    wdma;
> +               /* struct mdp_hdr_data  hdr; */
> +               struct isp_data         isp;
> +               /* struct wpe_data      wpe; */
> +       };
> +} __attribute__ ((__packed__));
> +
> +#define IMG_MAX_COMPONENTS     20
> +
> +struct img_mux {
> +       u32     reg;
> +       u32     value;
> +};
> +
> +struct img_mmsys_ctrl {
> +       struct img_mux  sets[IMG_MAX_COMPONENTS * 2];
> +       u32     num_sets;
> +};
> +
> +struct img_config {
> +       struct img_compparam    components[IMG_MAX_COMPONENTS];
> +       u32             num_components;
> +       struct img_mmsys_ctrl   ctrls[IMG_MAX_SUBFRAMES];
> +       u32             num_subfrms;
> +} __attribute__ ((__packed__));
> +
> +#endif  /* __MTK_IMG_IPI_H__ */
> +
> diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-cmdq.c b/drivers/media/platform/mtk-mdp3/mtk-mdp3-cmdq.c
> new file mode 100644
> index 000000000000..562ad8509fc2
> --- /dev/null
> +++ b/drivers/media/platform/mtk-mdp3/mtk-mdp3-cmdq.c
> @@ -0,0 +1,442 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2018 MediaTek Inc.
> + * Author: Ping-Hsun Wu <ping-hsun.wu@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/platform_device.h>
> +#include "mtk-mdp3-cmdq.h"
> +#include "mtk-mdp3-comp.h"
> +#include "mtk-mdp3-core.h"
> +
> +#include "mdp-platform.h"
> +#include "mmsys_mutex.h"
> +
> +#define DISP_MUTEX_MDP_FIRST   (5)
> +#define DISP_MUTEX_MDP_COUNT   (5)
> +
> +#define MDP_PATH_MAX_COMPS     IMG_MAX_COMPONENTS
> +
> +struct mdp_path {
> +       struct mdp_dev          *mdp_dev;
> +       struct mdp_comp_ctx     comps[MDP_PATH_MAX_COMPS];
> +       u32                     num_comps;
> +       const struct img_config *config;
> +       const struct img_ipi_frameparam *param;
> +       const struct v4l2_rect  *composes[IMG_MAX_HW_OUTPUTS];
> +       struct v4l2_rect        bounds[IMG_MAX_HW_OUTPUTS];
> +};
> +
> +#define has_op(ctx, op) \
> +       (ctx->comp->ops && ctx->comp->ops->op)
> +#define call_op(ctx, op, ...) \
> +       (has_op(ctx, op) ? ctx->comp->ops->op(ctx, ##__VA_ARGS__) : 0)
> +
> +struct mdp_path_subfrm {
> +       s32     mutex_id;
> +       u32     mutex_mod;
> +       s32     sofs[MDP_PATH_MAX_COMPS];
> +       u32     num_sofs;
> +};
> +
> +bool is_output_disable(const struct img_compparam *param, u32 count)

This function can be static.

> +{
> +       return (count < param->num_subfrms) ?
> +               (param->frame.output_disable ||
> +               param->subfrms[count].tile_disable) :
> +               true;
> +}
> +
> +int mdp_path_subfrm_require(struct mdp_path_subfrm *subfrm,
> +                           const struct mdp_path *path, struct mdp_cmd *cmd,
> +                           u32 count)

This one as well.

> +{
> +       const struct img_config *config = path->config;
> +       const struct mdp_comp_ctx *ctx;
> +       phys_addr_t mm_mutex = path->mdp_dev->mm_mutex.reg_base;
> +       s32 mutex_id = -1;
> +       u32 mutex_sof = 0;
> +       int mdp_color = 0;
> +       int index;
> +       u8 subsys_id = path->mdp_dev->mm_mutex.subsys_id;
> +
> +       /* Default value */
> +       memset(subfrm, 0, sizeof(*subfrm));
> +
> +       for (index = 0; index < config->num_components; index++) {
> +               ctx = &path->comps[index];
> +               if (is_output_disable(ctx->param, count))
> +                       continue;
> +               switch (ctx->comp->id) {
> +               /**********************************************
> +                * Name            MSB LSB
> +                * DISP_MUTEX_MOD   23   0
> +                *
> +                * Specifies which modules are in this mutex.
> +                * Every bit denotes a module. Bit definition:
> +                *  2 mdp_rdma0
> +                *  4 mdp_rsz0
> +                *  5 mdp_rsz1
> +                *  6 mdp_tdshp
> +                *  7 mdp_wrot0
> +                *  8 mdp_wdma
> +                *  13 mdp_color
> +                *  23 mdp_aal
> +                *  24 mdp_ccorr
> +                **********************************************/
> +               case MDP_AAL0:
> +                       subfrm->mutex_mod |= 1 << 23;
> +                       break;
> +               case MDP_CCORR0:
> +                       subfrm->mutex_mod |= 1 << 24;
> +                       break;
> +               case MDP_COLOR0:
> +                       if (mdp_color)
> +                               subfrm->mutex_mod |= 1 << 13;
> +                       break;
> +               case MDP_WDMA:
> +                       subfrm->mutex_mod |= 1 << 8;
> +                       subfrm->sofs[subfrm->num_sofs++] = MDP_WDMA;
> +                       break;
> +               case MDP_WROT0:
> +                       subfrm->mutex_mod |= 1 << 7;
> +                       subfrm->sofs[subfrm->num_sofs++] = MDP_WROT0;
> +                       break;
> +               case MDP_TDSHP0:
> +                       subfrm->mutex_mod |= 1 << 6;
> +                       subfrm->sofs[subfrm->num_sofs++] = MDP_TDSHP0;
> +                       break;
> +               case MDP_SCL1:
> +                       subfrm->mutex_mod |= 1 << 5;
> +                       subfrm->sofs[subfrm->num_sofs++] = MDP_SCL1;
> +                       break;
> +               case MDP_SCL0:
> +                       subfrm->mutex_mod |= 1 << 4;
> +                       subfrm->sofs[subfrm->num_sofs++] = MDP_SCL0;
> +                       break;
> +               case MDP_RDMA0:
> +                       mutex_id = DISP_MUTEX_MDP_FIRST + 1;
> +                       subfrm->mutex_mod |= 1 << 2;
> +                       subfrm->sofs[subfrm->num_sofs++] = MDP_RDMA0;
> +                       break;
> +               case MDP_IMGI:
> +                       mutex_id = DISP_MUTEX_MDP_FIRST;
> +                       break;
> +               case MDP_WPEI:
> +                       mutex_id = DISP_MUTEX_MDP_FIRST + 3;
> +                       break;
> +               case MDP_WPEI2:
> +                       mutex_id = DISP_MUTEX_MDP_FIRST + 4;
> +                       break;
> +               default:
> +                       break;
> +               }
> +       }
> +
> +       subfrm->mutex_id = mutex_id;
> +       if (-1 == mutex_id) {
> +               mdp_err("No mutex assigned");
> +               return -EINVAL;
> +       }
> +
> +       if (subfrm->mutex_mod) {
> +               /* Set mutex modules */
> +               MM_REG_WRITE(cmd, subsys_id, mm_mutex, MM_MUTEX_MOD,
> +                            subfrm->mutex_mod, 0x07FFFFFF);
> +               MM_REG_WRITE(cmd, subsys_id, mm_mutex, MM_MUTEX_SOF,
> +                            mutex_sof, 0x00000007);
> +       }
> +       return 0;
> +}
> +
> +int mdp_path_subfrm_run(const struct mdp_path_subfrm *subfrm,
> +                       const struct mdp_path *path, struct mdp_cmd *cmd)

And this one.

> +{
> +       phys_addr_t mm_mutex = path->mdp_dev->mm_mutex.reg_base;
> +       s32 mutex_id = subfrm->mutex_id;
> +       u8 subsys_id = path->mdp_dev->mm_mutex.subsys_id;
> +
> +       if (-1 == mutex_id) {
> +               mdp_err("Incorrect mutex id");
> +               return -EINVAL;
> +       }
> +
> +       if (subfrm->mutex_mod) {
> +               int index;
> +
> +               /* Wait WROT SRAM shared to DISP RDMA */
> +               /* Clear SOF event for each engine */
> +               for (index = 0; index < subfrm->num_sofs; index++) {
> +                       switch (subfrm->sofs[index]) {
> +                       case MDP_RDMA0:
> +                               MM_REG_CLEAR(cmd, RDMA0_SOF);
> +                               break;
> +                       case MDP_TDSHP0:
> +                               MM_REG_CLEAR(cmd, TDSHP0_SOF);
> +                               break;
> +                       case MDP_SCL0:
> +                               MM_REG_CLEAR(cmd, RSZ0_SOF);
> +                               break;
> +                       case MDP_SCL1:
> +                               MM_REG_CLEAR(cmd, RSZ1_SOF);
> +                               break;
> +                       case MDP_WDMA:
> +                               MM_REG_CLEAR(cmd, WDMA0_SOF);
> +                               break;
> +                       case MDP_WROT0:
> +#if WROT0_DISP_SRAM_SHARING
> +                               MM_REG_WAIT_NO_CLEAR(cmd, WROT0_SRAM_READY);
> +#endif
> +                               MM_REG_CLEAR(cmd, WROT0_SOF);
> +                               break;
> +                       default:
> +                               break;
> +                       }
> +               }
> +
> +               /* Enable the mutex */
> +               MM_REG_WRITE(cmd, subsys_id, mm_mutex, MM_MUTEX_EN, 0x1,
> +                            0x00000001);
> +
> +               /* Wait SOF events and clear mutex modules (optional) */
> +               for (index = 0; index < subfrm->num_sofs; index++) {
> +                       switch (subfrm->sofs[index]) {
> +                       case MDP_RDMA0:
> +                               MM_REG_WAIT(cmd, RDMA0_SOF);
> +                               break;
> +                       case MDP_TDSHP0:
> +                               MM_REG_WAIT(cmd, TDSHP0_SOF);
> +                               break;
> +                       case MDP_SCL0:
> +                               MM_REG_WAIT(cmd, RSZ0_SOF);
> +                               break;
> +                       case MDP_SCL1:
> +                               MM_REG_WAIT(cmd, RSZ1_SOF);
> +                               break;
> +                       case MDP_WDMA:
> +                               MM_REG_WAIT(cmd, WDMA0_SOF);
> +                               break;
> +                       case MDP_WROT0:
> +                               MM_REG_WAIT(cmd, WROT0_SOF);
> +                               break;
> +                       default:
> +                               break;
> +                       }
> +               }
> +       }
> +       return 0;
> +}
> +
> +static int mdp_path_config_subfrm(struct mdp_cmd *cmd, struct mdp_path *path,
> +                                 u32 count)
> +{
> +       struct mdp_path_subfrm subfrm;
> +       const struct img_config *config = path->config;
> +       const struct img_mmsys_ctrl *ctrl = &config->ctrls[count];
> +       const struct img_mux *set;
> +       struct mdp_comp_ctx *ctx;
> +       phys_addr_t mmsys = path->mdp_dev->mmsys.reg_base;
> +       int index, ret;
> +       u8 subsys_id = path->mdp_dev->mmsys.subsys_id;
> +
> +       /* Acquire components */
> +       ret = mdp_path_subfrm_require(&subfrm, path, cmd, count);
> +       if (ret)
> +               return ret;
> +       /* Enable mux settings */
> +       for (index = 0; index < ctrl->num_sets; index++) {
> +               set = &ctrl->sets[index];
> +               MM_REG_WRITE_MASK(cmd, subsys_id, mmsys, set->reg, set->value,
> +                                 0xFFFFFFFF);
> +       }
> +       /* Config sub-frame information */
> +       for (index = (config->num_components - 1); index >= 0; index--) {
> +               ctx = &path->comps[index];
> +               if (is_output_disable(ctx->param, count))
> +                       continue;
> +               ret = call_op(ctx, config_subfrm, cmd, count);
> +               if (ret)
> +                       return ret;
> +       }
> +       /* Run components */
> +       ret = mdp_path_subfrm_run(&subfrm, path, cmd);
> +       if (ret)
> +               return ret;
> +       /* Wait components done */
> +       for (index = 0; index < config->num_components; index++) {
> +               ctx = &path->comps[index];
> +               if (is_output_disable(ctx->param, count))
> +                       continue;
> +               ret = call_op(ctx, wait_comp_event, cmd);
> +               if (ret)
> +                       return ret;
> +       }
> +       /* Advance to the next sub-frame */
> +       for (index = 0; index < config->num_components; index++) {
> +               ctx = &path->comps[index];
> +               ret = call_op(ctx, advance_subfrm, cmd, count);
> +               if (ret)
> +                       return ret;
> +       }
> +       /* Disable mux settings */
> +       for (index = 0; index < ctrl->num_sets; index++) {
> +               set = &ctrl->sets[index];
> +               MM_REG_WRITE_MASK(cmd, subsys_id, mmsys, set->reg, 0,
> +                                 0xFFFFFFFF);
> +       }
> +       return 0;
> +}
> +
> +static int mdp_path_config(struct mdp_dev *mdp, struct mdp_cmd *cmd,
> +                          struct mdp_path *path)
> +{
> +       const struct img_config *config = path->config;
> +       struct mdp_comp_ctx *ctx;
> +       int index, count, ret;
> +
> +       for (index = 0; index < config->num_components; index++) {
> +               ret = mdp_comp_ctx_init(mdp, &path->comps[index],
> +                                       &config->components[index],
> +                                       path->param);
> +               if (ret)
> +                       return ret;
> +       }
> +
> +       /* Config path frame */
> +       /* Reset components */
> +       for (index = 0; index < config->num_components; index++) {
> +               ctx = &path->comps[index];
> +               ret = call_op(ctx, init_comp, cmd);
> +               if (ret)
> +                       return ret;
> +       }
> +       /* Config frame mode */
> +       for (index = 0; index < config->num_components; index++) {
> +               const struct v4l2_rect *compose =
> +                       path->composes[ctx->param->outputs[0]];
> +
> +               ctx = &path->comps[index];
> +               ret = call_op(ctx, config_frame, cmd, compose);
> +               if (ret)
> +                       return ret;
> +       }
> +
> +       /* Config path sub-frames */
> +       for (count = 0; count < config->num_subfrms; count++) {
> +               ret = mdp_path_config_subfrm(cmd, path, count);
> +               if (ret)
> +                       return ret;
> +       }
> +       /* Post processing information */
> +       for (index = 0; index < config->num_components; index++) {
> +               ctx = &path->comps[index];
> +               ret = call_op(ctx, post_process, cmd);
> +               if (ret)
> +                       return ret;
> +       }
> +       return 0;
> +}
> +
> +void mdp_handle_cmdq_callback(struct cmdq_cb_data data)

This can be static too.

> +{
> +       struct mdp_cmdq_cb_param *cb_param;
> +
> +       if (!data.data) {
> +               mdp_err("%s:no callback data\n", __func__);
> +               return;
> +       }
> +
> +       cb_param = (struct mdp_cmdq_cb_param *)data.data;
> +       if (cb_param->user_cmdq_cb) {
> +               struct cmdq_cb_data user_cb_data;
> +
> +               user_cb_data.sta = data.sta;
> +               user_cb_data.data = cb_param->user_cb_data;
> +               cb_param->user_cmdq_cb(user_cb_data);
> +       }
> +
> +       cmdq_pkt_destroy(cb_param->pkt);
> +       kfree(cb_param);
> +}
> +
> +int mdp_cmdq_send(struct mdp_dev *mdp, struct mdp_cmdq_param *param)
> +{
> +       struct mdp_cmd cmd;
> +       struct mdp_path path;
> +       int i, ret;
> +
> +       cmd.pkt = cmdq_pkt_create(mdp->cmdq_clt, SZ_16K);
> +       if (IS_ERR(cmd.pkt))
> +               return PTR_ERR(cmd.pkt);
> +       cmd.event = &mdp->event[0];
> +
> +       path.mdp_dev = mdp;
> +       path.config = param->config;
> +       path.param = param->param;
> +       for (i = 0; i < param->param->num_outputs; i++) {
> +               path.bounds[i].left = 0;
> +               path.bounds[i].top = 0;
> +               path.bounds[i].width =
> +                       param->param->outputs[i].buffer.format.width;
> +               path.bounds[i].height =
> +                       param->param->outputs[i].buffer.format.height;
> +               path.composes[i] = param->composes[i] ?
> +                       param->composes[i] : &path.bounds[i];
> +       }
> +       ret = mdp_path_config(mdp, &cmd, &path);
> +       if (ret)
> +               return ret;
> +
> +       // TODO: engine conflict dispatch
> +       for (i = 0; i < param->config->num_components; i++)
> +               mdp_comp_clock_on(&mdp->pdev->dev, path.comps[i].comp);
> +
> +       if (param->wait) {
> +               ret = cmdq_pkt_flush(cmd.pkt);
> +               cmdq_pkt_destroy(cmd.pkt);
> +               for (i = 0; i < param->config->num_components; i++)
> +                       mdp_comp_clock_off(&mdp->pdev->dev, path.comps[i].comp);
> +       } else {
> +               struct mdp_cmdq_cb_param *cb_param =
> +               kzalloc(sizeof(struct mdp_cmdq_cb_param), GFP_KERNEL);
> +
> +               cb_param->user_cmdq_cb = param->cmdq_cb;
> +               cb_param->user_cb_data = param->cb_data;
> +               cb_param->pkt = cmd.pkt;
> +
> +               ret = cmdq_pkt_flush_async(cmd.pkt,
> +                                          mdp_handle_cmdq_callback,
> +                                          (void *)cb_param);
> +               // TODO: destroy & clock-off after callback
> +       }
> +       return ret;
> +}
> +
> +int mdp_cmdq_sendtask(struct platform_device *pdev, struct img_config *config,
> +                     struct img_ipi_frameparam *param,
> +                     struct v4l2_rect *compose, unsigned int wait,
> +                     void (*cmdq_cb)(struct cmdq_cb_data data), void *cb_data)
> +{
> +       struct mdp_dev *mdp = platform_get_drvdata(pdev);
> +       struct mdp_cmdq_param task = {
> +               .config = config,
> +               .param = param,
> +               .composes[0] = compose,
> +               .wait = wait,
> +               .cmdq_cb = cmdq_cb,
> +               .cb_data = cb_data,
> +       };
> +
> +       return mdp_cmdq_send(mdp, &task);
> +}
> +EXPORT_SYMBOL_GPL(mdp_cmdq_sendtask);
> +
> diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-cmdq.h b/drivers/media/platform/mtk-mdp3/mtk-mdp3-cmdq.h
> new file mode 100644
> index 000000000000..b61ab1ac4325
> --- /dev/null
> +++ b/drivers/media/platform/mtk-mdp3/mtk-mdp3-cmdq.h
> @@ -0,0 +1,57 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2018 MediaTek Inc.
> + * Author: Ping-Hsun Wu <ping-hsun.wu@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_MDP3_CMDQ_H__
> +#define __MTK_MDP3_CMDQ_H__
> +
> +#include <linux/platform_device.h>
> +#include <linux/videodev2.h>
> +#include <linux/soc/mediatek/mtk-cmdq.h>
> +#include "mtk-img-ipi.h"
> +
> +struct platform_device *mdp_get_plat_device(struct platform_device *pdev);

This function is defined in mtk-mdp3-core.c, either its declaration or
its definition should be moved?

> +
> +int mdp_cmdq_sendtask(struct platform_device *pdev, struct img_config *config,
> +                     struct img_ipi_frameparam *param,
> +                     struct v4l2_rect *compose, unsigned int wait,
> +                     void (*cmdq_cb)(struct cmdq_cb_data data), void *cb_data);

This function is defined and exported, but I don't see any call to it
anywhere? Looks like it can just be removed.

> +
> +struct mdp_cmd {
> +       struct cmdq_pkt *pkt;
> +       s32 *event;
> +};
> +
> +struct mdp_cmdq_param {
> +       struct img_config       *config;
> +       struct img_ipi_frameparam *param;
> +       const struct v4l2_rect  *composes[IMG_MAX_HW_OUTPUTS];
> +       unsigned int            wait;
> +
> +       void (*cmdq_cb)(struct cmdq_cb_data data);
> +       void *cb_data;
> +};
> +
> +struct mdp_cmdq_cb_param {
> +       void (*user_cmdq_cb)(struct cmdq_cb_data data);
> +       void *user_cb_data;
> +       struct cmdq_pkt *pkt;
> +};
> +
> +struct mdp_dev;
> +
> +int mdp_cmdq_send(struct mdp_dev *mdp, struct mdp_cmdq_param *param);
> +
> +#endif  /* __MTK_MDP3_CMDQ_H__ */
> +
> diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-comp.c b/drivers/media/platform/mtk-mdp3/mtk-mdp3-comp.c
> new file mode 100644
> index 000000000000..3dc36915a744

[snip]

> +static void mdp_comp_deinit(struct device *dev, struct mdp_comp *comp)
> +{
> +       iounmap(comp->regs);
> +       /* of_node_put(comp->dev_node); */
> +}
> +
> +void mdp_component_deinit(struct device *dev, struct mdp_dev *mdp)
> +{
> +       int i;
> +
> +       mdp_comp_deinit(dev, &mdp->mmsys);
> +       mdp_comp_deinit(dev, &mdp->mm_mutex);
> +       for (i = 0; i < ARRAY_SIZE(mdp->comp); i++) {
> +               if (mdp->comp[i]) {
> +                       mdp_comp_deinit(dev, mdp->comp[i]);
> +                       kfree(mdp->comp[i]);
> +               }
> +       }
> +}
> +
> +int mdp_component_init(struct device *dev, struct mdp_dev *mdp)

dev can be obtained from mdp->pdev->dev, so there is no need to pass
it as argument.

> +{
> +       struct device_node *node, *parent;
> +       int i, ret;
> +
> +       for (i = 0; i < ARRAY_SIZE(gce_event_names); i++) {
> +               s32 event_id;
> +
> +               event_id = cmdq_dev_get_event(dev, gce_event_names[i]);
> +               mdp->event[i] = (event_id < 0) ? -i : event_id;
> +               dev_info(dev, "Get event %s id:%d\n",
> +                        gce_event_names[i], mdp->event[i]);
> +       }
> +
> +       ret = mdp_mm_init(dev, mdp, &mdp->mmsys, "mediatek,mmsys");

Same for mdp_mm_init.

> +       if (ret)
> +               goto err_init_mm;
> +
> +       ret = mdp_mm_init(dev, mdp, &mdp->mm_mutex, "mediatek,mm-mutex");
> +       if (ret)
> +               goto err_init_mm;
> +
> +       parent = dev->of_node->parent;
> +       /* Iterate over sibling MDP function blocks */
> +       for_each_child_of_node(parent, node) {
> +               const struct of_device_id *of_id;
> +               enum mdp_comp_type type;
> +               int id;
> +               struct mdp_comp *comp;
> +
> +               of_id = of_match_node(mdp_comp_dt_ids, node);
> +               if (!of_id)
> +                       continue;
> +
> +               if (!of_device_is_available(node)) {
> +                       dev_err(dev, "Skipping disabled component %pOF\n",
> +                               node);
> +                       continue;
> +               }
> +
> +               type = (enum mdp_comp_type)of_id->data;
> +               id = mdp_comp_get_id(dev, node, type);
> +               if (id < 0) {
> +                       dev_warn(dev, "Skipping unknown component %pOF\n",
> +                                node);
> +                       continue;
> +               }
> +
> +               comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL);
> +               if (!comp) {
> +                       ret = -ENOMEM;
> +                       goto err_init_comps;
> +               }
> +               mdp->comp[id] = comp;
> +
> +               ret = mdp_comp_init(dev, mdp, node, comp, id);
> +               if (ret)
> +                       goto err_init_comps;
> +
> +               dev_info(dev, "%s type:%d alias:%d id:%d base:%#x regs:%p\n",
> +                        of_id->compatible, type, comp->alias_id, id,
> +                       (u32)comp->reg_base, comp->regs);
> +       }
> +       return 0;
> +
> +err_init_comps:
> +       mdp_component_deinit(dev, mdp);
> +err_init_mm:
> +       return ret;
> +}
> +
> +int mdp_comp_ctx_init(struct mdp_dev *mdp, struct mdp_comp_ctx *ctx,
> +                     const struct img_compparam *param,
> +       const struct img_ipi_frameparam *frame)
> +{
> +       int i;
> +
> +       if (param->type < 0 || param->type >= MDP_MAX_COMP_COUNT) {
> +               mdp_err("Invalid component id %d", param->type);
> +               return -EINVAL;
> +       }
> +
> +       ctx->comp = mdp->comp[param->type];
> +       if (!ctx->comp) {
> +               mdp_err("Uninit component id %d", param->type);
> +               return -EINVAL;
> +       }
> +
> +       ctx->param = param;
> +       ctx->input = &frame->inputs[param->input];
> +       for (i = 0; i < param->num_outputs; i++)
> +               ctx->outputs[i] = &frame->outputs[param->outputs[i]];
> +       return 0;
> +}
> +
> diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-comp.h b/drivers/media/platform/mtk-mdp3/mtk-mdp3-comp.h
> new file mode 100644
> index 000000000000..0c65214ef695
> --- /dev/null
> +++ b/drivers/media/platform/mtk-mdp3/mtk-mdp3-comp.h
> @@ -0,0 +1,177 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2018 MediaTek Inc.
> + * Author: Ping-Hsun Wu <ping-hsun.wu@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.

You already have the SPDX identifier, so please don't also copy the
GPL boilerplate. The SPDX is supposed to replace it. This applies to a
few other files as well.

> + */
> +
> +#ifndef __MTK_MDP3_COMP_H__
> +#define __MTK_MDP3_COMP_H__
> +
> +#include "mtk-mdp3-cmdq.h"
> +
> +enum mdp_comp_type {
> +       MDP_COMP_TYPE_INVALID = 0,
> +
> +       MDP_COMP_TYPE_RDMA,
> +       MDP_COMP_TYPE_RSZ,
> +       MDP_COMP_TYPE_WROT,
> +       MDP_COMP_TYPE_WDMA,
> +       MDP_COMP_TYPE_PATH,
> +
> +       MDP_COMP_TYPE_TDSHP,
> +       MDP_COMP_TYPE_COLOR,
> +       MDP_COMP_TYPE_DRE,
> +       MDP_COMP_TYPE_CCORR,
> +       MDP_COMP_TYPE_HDR,
> +
> +       MDP_COMP_TYPE_IMGI,
> +       MDP_COMP_TYPE_WPEI,
> +       MDP_COMP_TYPE_EXTO,     /* External path */
> +       MDP_COMP_TYPE_DL_PATH,  /* Direct-link path */
> +
> +       MDP_COMP_TYPE_COUNT     /* ALWAYS keep at the end */
> +};
> +
> +enum mdp_comp_id {
> +       MDP_COMP_NONE = -1,     /* Invalid engine */
> +
> +       /* ISP */
> +       MDP_COMP_WPEI = 0,
> +       MDP_COMP_WPEO,          /* 1 */
> +       MDP_COMP_WPEI2,         /* 2 */
> +       MDP_COMP_WPEO2,         /* 3 */
> +       MDP_COMP_ISP_IMGI,      /* 4 */
> +       MDP_COMP_ISP_IMGO,      /* 5 */
> +       MDP_COMP_ISP_IMG2O,     /* 6 */
> +
> +       /* IPU */
> +       MDP_COMP_IPUI,          /* 7 */
> +       MDP_COMP_IPUO,          /* 8 */
> +
> +       /* MDP */
> +       MDP_COMP_CAMIN,         /* 9 */
> +       MDP_COMP_CAMIN2,        /* 10 */
> +       MDP_COMP_RDMA0,         /* 11 */
> +       MDP_COMP_AAL0,          /* 12 */
> +       MDP_COMP_CCORR0,        /* 13 */
> +       MDP_COMP_RSZ0,          /* 14 */
> +       MDP_COMP_RSZ1,          /* 15 */
> +       MDP_COMP_TDSHP0,        /* 16 */
> +       MDP_COMP_COLOR0,        /* 17 */
> +       MDP_COMP_PATH0_SOUT,    /* 18 */
> +       MDP_COMP_PATH1_SOUT,    /* 19 */
> +       MDP_COMP_WROT0,         /* 20 */
> +       MDP_COMP_WDMA,          /* 21 */
> +
> +       /* Dummy Engine */
> +       MDP_COMP_RDMA1,         /* 22 */
> +       MDP_COMP_RSZ2,          /* 23 */
> +       MDP_COMP_TDSHP1,        /* 24 */
> +       MDP_COMP_WROT1,         /* 25 */
> +
> +       MDP_MAX_COMP_COUNT      /* ALWAYS keep at the end */
> +};
> +
> +enum mdp_comp_event {
> +       RDMA0_SOF,
> +       RDMA0_DONE,
> +       RDMA1_SOF,
> +       RDMA1_DONE,
> +       RSZ0_SOF,
> +       RSZ1_SOF,
> +       TDSHP0_SOF,
> +       WROT0_SOF,
> +       WROT0_DONE,
> +       WROT1_SOF,
> +       WROT1_DONE,
> +       WDMA0_SOF,
> +       WDMA0_DONE,
> +       IMG_DL_SOF,
> +
> +       ISP_P2_0_DONE,
> +       ISP_P2_1_DONE,
> +       ISP_P2_2_DONE,
> +       ISP_P2_3_DONE,
> +       ISP_P2_4_DONE,
> +       ISP_P2_5_DONE,
> +       ISP_P2_6_DONE,
> +       ISP_P2_7_DONE,
> +       ISP_P2_8_DONE,
> +       ISP_P2_9_DONE,
> +       ISP_P2_10_DONE,
> +       ISP_P2_11_DONE,
> +       ISP_P2_12_DONE,
> +       ISP_P2_13_DONE,
> +       ISP_P2_14_DONE,
> +
> +       WPE_DONE,
> +       WPE_B_DONE,
> +       WROT0_SRAM_READY,
> +       WROT1_SRAM_READY,
> +
> +       MDP_MAX_EVENT_COUNT     /* ALWAYS keep at the end */
> +};
> +
> +struct mdp_comp_ops;
> +
> +struct mdp_comp {
> +       struct mdp_dev          *mdp_dev;
> +       /* struct device_node   *dev_node; */
> +       void __iomem            *regs;
> +       phys_addr_t             reg_base;
> +       u8                      subsys_id;
> +       struct clk              *clks[2];
> +       struct device           *larb_dev;
> +       enum mdp_comp_type      type;
> +       enum mdp_comp_id        id;
> +       u32                     alias_id;
> +       const struct mdp_comp_ops *ops;
> +};
> +
> +struct mdp_comp_ctx {
> +       struct mdp_comp                 *comp;
> +       const struct img_compparam      *param;
> +       const struct img_input          *input;
> +       const struct img_output         *outputs[IMG_MAX_HW_OUTPUTS];
> +};
> +
> +struct mdp_comp_ops {
> +       s64 (*get_comp_flag)(const struct mdp_comp_ctx *ctx);
> +       /* s64 (*query_feature)(void); */
> +       int (*init_comp)(struct mdp_comp_ctx *ctx, struct mdp_cmd *cmd);
> +       int (*config_frame)(struct mdp_comp_ctx *ctx, struct mdp_cmd *cmd,
> +                           const struct v4l2_rect *compose);
> +       /* int (*config_frame_end)(struct mdp_comp_ctx *ctx,
> +        *      struct mdp_cmd *cmd);
> +        */
> +       int (*config_subfrm)(struct mdp_comp_ctx *ctx,
> +                            struct mdp_cmd *cmd, u32 index);
> +       int (*wait_comp_event)(struct mdp_comp_ctx *ctx,
> +                              struct mdp_cmd *cmd);
> +       int (*advance_subfrm)(struct mdp_comp_ctx *ctx,
> +                             struct mdp_cmd *cmd, u32 index);
> +       int (*post_process)(struct mdp_comp_ctx *ctx, struct mdp_cmd *cmd);
> +       /* void (*release)(struct mdp_comp_ctx *ctx); */
> +};
> +
> +struct mdp_dev;
> +
> +int mdp_component_init(struct device *dev, struct mdp_dev *mdp);
> +void mdp_component_deinit(struct device *dev, struct mdp_dev *mdp);
> +void mdp_comp_clock_on(struct device *dev, struct mdp_comp *comp);
> +void mdp_comp_clock_off(struct device *dev, struct mdp_comp *comp);
> +int mdp_comp_ctx_init(struct mdp_dev *mdp, struct mdp_comp_ctx *ctx,
> +                     const struct img_compparam *param,
> +       const struct img_ipi_frameparam *frame);
> +
> +#endif  /* __MTK_MDP3_COMP_H__ */
> +
> diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-core.c b/drivers/media/platform/mtk-mdp3/mtk-mdp3-core.c
> new file mode 100644
> index 000000000000..47b5d87834de
> --- /dev/null
> +++ b/drivers/media/platform/mtk-mdp3/mtk-mdp3-core.c
> @@ -0,0 +1,256 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2018 MediaTek Inc.
> + * Author: Ping-Hsun Wu <ping-hsun.wu@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/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/remoteproc.h>
> +#include <linux/platform_data/mtk_scp.h>
> +#include <media/videobuf2-dma-contig.h>
> +#include "mtk-mdp3-core.h"
> +#include "mtk-mdp3-m2m.h"
> +
> +/* MDP debug log level (0-3). 3 shows all the logs. */
> +int mtk_mdp_debug;
> +EXPORT_SYMBOL(mtk_mdp_debug);
> +module_param_named(debug, mtk_mdp_debug, int, 0644);
> +
> +static const struct of_device_id mdp_of_ids[] = {
> +       { .compatible = "mediatek,mt8183-mdp3", },
> +       {},
> +};
> +MODULE_DEVICE_TABLE(of, mdp_of_ids);
> +
> +struct platform_device *mdp_get_plat_device(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct device_node *mdp_node;
> +       struct platform_device *mdp_pdev;
> +
> +       mdp_node = of_parse_phandle(dev->of_node, "mediatek,mdp3", 0);
> +       if (!mdp_node) {
> +               dev_err(dev, "can't get mdp node\n");
> +               return NULL;
> +       }
> +
> +       mdp_pdev = of_find_device_by_node(mdp_node);
> +       if (WARN_ON(!mdp_pdev)) {
> +               dev_err(dev, "mdp pdev failed\n");
> +               of_node_put(mdp_node);
> +               return NULL;
> +       }
> +
> +       return mdp_pdev;
> +}
> +EXPORT_SYMBOL_GPL(mdp_get_plat_device);
> +
> +int mdp_vpu_get_locked(struct mdp_dev *mdp)
> +{
> +       int ret = 0;
> +
> +       if (mdp->vpu_count++ == 0) {
> +               ret = rproc_boot(mdp->rproc_handle);
> +               if (ret < 0) {
> +                       dev_err(&mdp->pdev->dev,
> +                               "vpu_load_firmware failed %d\n", ret);
> +                       goto err_load_vpu;
> +               }
> +               ret = mdp_vpu_register(mdp->vpu_dev);

With mdp_vpu_unregister doing nothing, isn't it a problem to register
the same callbacks several times? How about moving this into probe?

> +               if (ret < 0) {
> +                       dev_err(&mdp->pdev->dev,
> +                               "mdp_vpu register failed %d\n", ret);
> +                       goto err_reg_vpu;
> +               }
> +               ret = mdp_vpu_dev_init(&mdp->vpu, mdp->vpu_dev, &mdp->vpu_lock);
> +               if (ret) {
> +                       dev_err(&mdp->pdev->dev,
> +                               "mdp_vpu device init failed %d\n", ret);
> +                       goto err_init_vpu;
> +               }
> +       }
> +       return 0;
> +
> +err_init_vpu:
> +       mdp_vpu_unregister(mdp->vpu_dev);
> +err_reg_vpu:
> +err_load_vpu:
> +       mdp->vpu_count--;
> +       return ret;
> +}
> +
> +void mdp_vpu_put_locked(struct mdp_dev *mdp)
> +{
> +       if (--mdp->vpu_count == 0) {
> +               mdp_vpu_dev_deinit(&mdp->vpu);
> +               mdp_vpu_unregister(mdp->vpu_dev);
> +       }
> +}
> +
> +static int mdp_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct mdp_dev *mdp;
> +       phandle rproc_phandle;
> +       int ret;
> +
> +       mdp = devm_kzalloc(dev, sizeof(*mdp), GFP_KERNEL);
> +       if (!mdp)
> +               return -ENOMEM;
> +
> +       mdp->pdev = pdev;
> +       ret = mdp_component_init(dev, mdp);
> +       if (ret) {
> +               dev_err(dev, "Failed to initialize mdp components\n");
> +               goto err_init_comp;
> +       }
> +
> +       mdp->job_wq = create_singlethread_workqueue(MDP_MODULE_NAME);
> +       if (!mdp->job_wq) {
> +               dev_err(dev, "Unable to create job workqueue\n");
> +               ret = -ENOMEM;
> +               goto err_create_job_wq;
> +       }
> +
> +       mdp->vpu_dev = scp_get_pdev(pdev);
> +
> +       if (of_property_read_u32(pdev->dev.of_node, "mediatek,scp",
> +                                &rproc_phandle))
> +               dev_err(&pdev->dev, "Could not get scp device\n");
> +       else
> +               dev_info(&pdev->dev, "Find mediatek,scp phandle:%llx\n",
> +                        (unsigned long long)rproc_phandle);
> +
> +       mdp->rproc_handle = rproc_get_by_phandle(rproc_phandle);
> +
> +       dev_info(&pdev->dev, "MDP rproc_handle: %llx",
> +                (unsigned long long)mdp->rproc_handle);
> +
> +       if (!mdp->rproc_handle)
> +               dev_err(&pdev->dev, "Could not get MDP's rproc_handle\n");
> +
> +       /* vpu_wdt_reg_handler(mdp->vpu_dev, mdp_reset_handler, mdp,
> +        *                     VPU_RST_MDP);
> +        */
> +       mutex_init(&mdp->vpu_lock);
> +       mdp->vpu_count = 0;
> +       mdp->id_count = 0;
> +
> +       mdp->cmdq_clt = cmdq_mbox_create(dev, 0, 1200);
> +       if (IS_ERR(mdp->cmdq_clt))
> +               goto err_mbox_create;
> +
> +       ret = v4l2_device_register(dev, &mdp->v4l2_dev);
> +       if (ret) {
> +               dev_err(dev, "Failed to register v4l2 device\n");
> +               ret = -EINVAL;
> +               goto err_v4l2_register;
> +       }
> +
> +       ret = mdp_m2m_device_register(mdp);
> +       if (ret) {
> +               v4l2_err(&mdp->v4l2_dev, "Failed to register m2m device\n");
> +               goto err_m2m_register;
> +       }
> +       mutex_init(&mdp->m2m_lock);
> +
> +       platform_set_drvdata(pdev, mdp);
> +
> +       vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32));
> +       pm_runtime_enable(dev);
> +       dev_dbg(dev, "mdp-%d registered successfully\n", pdev->id);
> +       return 0;
> +
> +err_m2m_register:
> +       v4l2_device_unregister(&mdp->v4l2_dev);
> +err_v4l2_register:
> +err_mbox_create:
> +       destroy_workqueue(mdp->job_wq);
> +err_create_job_wq:
> +err_init_comp:
> +       kfree(mdp);
> +
> +       dev_dbg(dev, "Errno %d\n", ret);
> +       return ret;
> +}
> +
> +static int mdp_remove(struct platform_device *pdev)
> +{
> +       struct mdp_dev *mdp = platform_get_drvdata(pdev);
> +
> +       pm_runtime_disable(&pdev->dev);
> +       vb2_dma_contig_clear_max_seg_size(&pdev->dev);
> +       mdp_m2m_device_unregister(mdp);
> +       v4l2_device_unregister(&mdp->v4l2_dev);
> +
> +       flush_workqueue(mdp->job_wq);
> +       destroy_workqueue(mdp->job_wq);
> +
> +       mdp_component_deinit(&pdev->dev, mdp);
> +       kfree(mdp);
> +
> +       dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name);
> +       return 0;
> +}
> +
> +static int __maybe_unused mdp_pm_suspend(struct device *dev)
> +{
> +       // TODO: mdp clock off
> +       return 0;
> +}
> +
> +static int __maybe_unused mdp_pm_resume(struct device *dev)
> +{
> +       // TODO: mdp clock on
> +       return 0;
> +}
> +
> +static int __maybe_unused mdp_suspend(struct device *dev)
> +{
> +       if (pm_runtime_suspended(dev))
> +               return 0;
> +
> +       return mdp_pm_suspend(dev);
> +}
> +
> +static int __maybe_unused mdp_resume(struct device *dev)
> +{
> +       if (pm_runtime_suspended(dev))
> +               return 0;
> +
> +       return mdp_pm_resume(dev);
> +}
> +
> +static const struct dev_pm_ops mdp_pm_ops = {
> +       SET_SYSTEM_SLEEP_PM_OPS(mdp_suspend, mdp_resume)
> +       SET_RUNTIME_PM_OPS(mdp_pm_suspend, mdp_pm_resume, NULL)
> +};
> +
> +static struct platform_driver mdp_driver = {
> +       .probe          = mdp_probe,
> +       .remove         = mdp_remove,
> +       .driver = {
> +               .name   = MDP_MODULE_NAME,
> +               .pm     = &mdp_pm_ops,
> +               .of_match_table = mdp_of_ids,
> +       },
> +};
> +
> +module_platform_driver(mdp_driver);
> +
> +MODULE_AUTHOR("Ping-Hsun Wu <ping-hsun.wu@mediatek.com>");
> +MODULE_DESCRIPTION("Mediatek image processor 3 driver");
> +MODULE_LICENSE("GPL v2");
> +
> diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-core.h b/drivers/media/platform/mtk-mdp3/mtk-mdp3-core.h
> new file mode 100644
> index 000000000000..5ef3199b55e2
> --- /dev/null
> +++ b/drivers/media/platform/mtk-mdp3/mtk-mdp3-core.h
> @@ -0,0 +1,88 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2018 MediaTek Inc.
> + * Author: Ping-Hsun Wu <ping-hsun.wu@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_MDP3_CORE_H__
> +#define __MTK_MDP3_CORE_H__
> +
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-mem2mem.h>
> +#include "mtk-mdp3-comp.h"
> +#include "mtk-mdp3-vpu.h"
> +
> +#define MDP_MODULE_NAME        "mtk-mdp3"
> +
> +enum mdp_buffer_usage {
> +       MDP_BUFFER_USAGE_HW_READ,
> +       MDP_BUFFER_USAGE_MDP,
> +       MDP_BUFFER_USAGE_MDP2,
> +       MDP_BUFFER_USAGE_ISP,
> +       MDP_BUFFER_USAGE_WPE,
> +};
> +
> +struct mdp_dev {
> +       struct platform_device  *pdev;
> +       struct mdp_comp         mmsys;
> +       struct mdp_comp         mm_mutex;
> +       struct mdp_comp         *comp[MDP_MAX_COMP_COUNT];
> +       s32                     event[MDP_MAX_EVENT_COUNT];
> +
> +       struct workqueue_struct *job_wq;
> +       struct mdp_vpu_dev      vpu;
> +       struct platform_device  *vpu_dev;
> +       struct rproc *rproc_handle;
> +       /* synchronization protect for image configuration reference count */
> +       struct mutex            vpu_lock;
> +       s32                     vpu_count;
> +       u32                     id_count;
> +       struct cmdq_client      *cmdq_clt;
> +
> +       struct v4l2_device      v4l2_dev;
> +       struct video_device     *m2m_vdev;
> +       struct v4l2_m2m_dev     *m2m_dev;
> +       /* synchronization protect for m2m device operation */
> +       struct mutex            m2m_lock;
> +};
> +
> +int mdp_vpu_get_locked(struct mdp_dev *mdp);
> +void mdp_vpu_put_locked(struct mdp_dev *mdp);
> +
> +extern int mtk_mdp_debug;
> +
> +#define DEBUG
> +#if defined(DEBUG)
> +
> +#define mdp_dbg(level, fmt, ...)\
> +       do {\
> +               if (mtk_mdp_debug >= (level))\
> +                       pr_info("[MTK-MDP3] %d %s:%d: " fmt "\n",\
> +                               level, __func__, __LINE__, ##__VA_ARGS__);\
> +       } while (0)
> +
> +#define mdp_err(fmt, ...)\
> +       pr_err("[MTK-MDP3][ERR] %s:%d: " fmt "\n", __func__, __LINE__,\
> +               ##__VA_ARGS__)
> +
> +#else
> +
> +#define mdp_dbg(level, fmt, ...)       do {} while (0)
> +#define mdp_err(fmt, ...)              do {} while (0)
> +
> +#endif
> +
> +#define mdp_dbg_enter() mdp_dbg(3, "+")
> +#define mdp_dbg_leave() mdp_dbg(3, "-")
> +
> +#endif  /* __MTK_MDP3_CORE_H__ */
> +
> diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-m2m.c b/drivers/media/platform/mtk-mdp3/mtk-mdp3-m2m.c
> new file mode 100644
> index 000000000000..638a854c502c
> --- /dev/null
> +++ b/drivers/media/platform/mtk-mdp3/mtk-mdp3-m2m.c
> @@ -0,0 +1,823 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2018 MediaTek Inc.
> + * Author: Ping-Hsun Wu <ping-hsun.wu@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/platform_device.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/v4l2-event.h>
> +#include <media/videobuf2-dma-contig.h>
> +#include "mtk-mdp3-m2m.h"
> +
> +static inline struct mdp_m2m_ctx *fh_to_ctx(struct v4l2_fh *fh)
> +{
> +       return container_of(fh, struct mdp_m2m_ctx, fh);
> +}
> +
> +static inline struct mdp_m2m_ctx *ctrl_to_ctx(struct v4l2_ctrl *ctrl)
> +{
> +       return container_of(ctrl->handler, struct mdp_m2m_ctx, ctrl_handler);
> +}
> +
> +static inline struct mdp_frame *ctx_get_frame(struct mdp_m2m_ctx *ctx,
> +                                             enum v4l2_buf_type type)
> +{
> +       if (V4L2_TYPE_IS_OUTPUT(type))
> +               return &ctx->curr_param->output;
> +       return &ctx->curr_param->captures[0];

Let's use an else here for symetry.

> +}
> +
> +static void mdp_m2m_ctx_set_state(struct mdp_m2m_ctx *ctx, u32 state)
> +{
> +       mutex_lock(&ctx->curr_param->lock);
> +       ctx->curr_param->state |= state;
> +       mutex_unlock(&ctx->curr_param->lock);
> +}
> +
> +static bool mdp_m2m_ctx_is_state_set(struct mdp_m2m_ctx *ctx, u32 mask)
> +{
> +       bool ret;
> +
> +       mutex_lock(&ctx->curr_param->lock);
> +       ret = (ctx->curr_param->state & mask) == mask;
> +       mutex_unlock(&ctx->curr_param->lock);
> +       return ret;
> +}
> +
> +static void mdp_m2m_ctx_lock(struct vb2_queue *q)
> +{
> +       struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(q);
> +
> +       mutex_lock(&ctx->mdp_dev->m2m_lock);
> +}
> +
> +static void mdp_m2m_ctx_unlock(struct vb2_queue *q)
> +{
> +       struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(q);
> +
> +       mutex_unlock(&ctx->mdp_dev->m2m_lock);
> +}
> +
> +static void mdp_m2m_job_abort(void *priv)
> +{
> +}
> +
> +static void mdp_m2m_process_done(void *priv, int vb_state)
> +{
> +       struct mdp_m2m_ctx *ctx = priv;
> +       struct vb2_buffer *src_vb, *dst_vb;
> +       struct vb2_v4l2_buffer *src_vbuf, *dst_vbuf;
> +       u32 valid_output_flags = V4L2_BUF_FLAG_TIMECODE |
> +                                V4L2_BUF_FLAG_TSTAMP_SRC_MASK |
> +                                V4L2_BUF_FLAG_KEYFRAME |
> +                                V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_BFRAME;
> +
> +       src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);

Compiler warning: assignment from incompatible pointer.

> +       src_vbuf = to_vb2_v4l2_buffer(src_vb);
> +       dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);

Here too, as well as in other places of this file where these
functions are called.

> +       dst_vbuf = to_vb2_v4l2_buffer(dst_vb);
> +
> +       src_vbuf->sequence = ctx->frame_count;
> +       dst_vbuf->sequence = src_vbuf->sequence;
> +       dst_vbuf->timecode = src_vbuf->timecode;
> +       dst_vbuf->flags &= ~valid_output_flags;
> +       dst_vbuf->flags |= src_vbuf->flags & valid_output_flags;
> +
> +       v4l2_m2m_buf_done(src_vbuf, vb_state);
> +       v4l2_m2m_buf_done(dst_vbuf, vb_state);
> +       v4l2_m2m_job_finish(ctx->mdp_dev->m2m_dev, ctx->m2m_ctx);
> +
> +       ctx->curr_param->frame_no = ctx->frame_count++;
> +}

[snip]

> diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-m2m.h b/drivers/media/platform/mtk-mdp3/mtk-mdp3-m2m.h
> new file mode 100644
> index 000000000000..1f681b48c2ad
> --- /dev/null
> +++ b/drivers/media/platform/mtk-mdp3/mtk-mdp3-m2m.h
> @@ -0,0 +1,52 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2018 MediaTek Inc.
> + * Author: Ping-Hsun Wu <ping-hsun.wu@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_MDP3_M2M_H__
> +#define __MTK_MDP3_M2M_H__
> +
> +#include <media/v4l2-ctrls.h>
> +#include "mtk-mdp3-core.h"
> +#include "mtk-mdp3-vpu.h"
> +#include "mtk-mdp3-regs.h"
> +
> +#define MDP_MAX_CTRLS  10
> +
> +struct mdp_m2m_ctrls {
> +       struct v4l2_ctrl        *hflip;
> +       struct v4l2_ctrl        *vflip;
> +       /* struct v4l2_ctrl     *sharpness; */
> +       struct v4l2_ctrl        *rotate;
> +};

I see the members of this struct assigned in mdp_m2m_ctrls_create(),
but they are then completely untouched. What is their purpose? Looks
like we can just remove them.

> +
> +struct mdp_m2m_ctx {
> +       u32                             id;
> +       struct mdp_dev                  *mdp_dev;
> +       struct v4l2_fh                  fh;
> +       struct v4l2_ctrl_handler        ctrl_handler;
> +       struct mdp_m2m_ctrls            ctrls;
> +       struct v4l2_m2m_ctx             *m2m_ctx;
> +       struct mdp_vpu_ctx              vpu;
> +       struct work_struct              work;
> +       u32                             frame_count;
> +
> +       struct mdp_frameparam           *curr_param;
> +       struct list_head                param_list;
> +};
> +
> +int mdp_m2m_device_register(struct mdp_dev *mdp);
> +void mdp_m2m_device_unregister(struct mdp_dev *mdp);
> +
> +#endif  /* __MTK_MDP3_M2M_H__ */
> +
> diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-regs.c b/drivers/media/platform/mtk-mdp3/mtk-mdp3-regs.c
> new file mode 100644
> index 000000000000..b623eda06c7d
> --- /dev/null
> +++ b/drivers/media/platform/mtk-mdp3/mtk-mdp3-regs.c
> @@ -0,0 +1,757 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2018 MediaTek Inc.
> + * Author: Ping-Hsun Wu <ping-hsun.wu@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 <media/v4l2-common.h>
> +#include <media/videobuf2-v4l2.h>
> +#include <media/videobuf2-dma-contig.h>
> +#include "mtk-mdp3-core.h"
> +#include "mtk-mdp3-regs.h"
> +
> +static const struct mdp_format mdp_formats[] = {
> +       {
> +               .pixelformat    = V4L2_PIX_FMT_GREY,
> +               .mdp_color      = MDP_COLOR_GREY,
> +               .depth          = { 8 },
> +               .row_depth      = { 8 },
> +               .num_planes     = 1,
> +               .flags          = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
> +       }, {
> +               .pixelformat    = V4L2_PIX_FMT_RGB565X,
> +               .mdp_color      = MDP_COLOR_RGB565,
> +               .depth          = { 16 },
> +               .row_depth      = { 16 },
> +               .num_planes     = 1,
> +               .flags          = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
> +       }, {
> +               .pixelformat    = V4L2_PIX_FMT_RGB565,
> +               .mdp_color      = MDP_COLOR_BGR565,
> +               .depth          = { 16 },
> +               .row_depth      = { 16 },
> +               .num_planes     = 1,
> +               .flags          = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
> +       }, {
> +               .pixelformat    = V4L2_PIX_FMT_RGB24,
> +               .mdp_color      = MDP_COLOR_RGB888,
> +               .depth          = { 24 },
> +               .row_depth      = { 24 },
> +               .num_planes     = 1,
> +               .flags          = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
> +       }, {
> +               .pixelformat    = V4L2_PIX_FMT_BGR24,
> +               .mdp_color      = MDP_COLOR_BGR888,
> +               .depth          = { 24 },
> +               .row_depth      = { 24 },
> +               .num_planes     = 1,
> +               .flags          = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
> +       }, {
> +               .pixelformat    = V4L2_PIX_FMT_ABGR32,
> +               .mdp_color      = MDP_COLOR_BGRA8888,
> +               .depth          = { 32 },
> +               .row_depth      = { 32 },
> +               .num_planes     = 1,
> +               .flags          = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
> +       }, {
> +               .pixelformat    = V4L2_PIX_FMT_ARGB32,
> +               .mdp_color      = MDP_COLOR_ARGB8888,
> +               .depth          = { 32 },
> +               .row_depth      = { 32 },
> +               .num_planes     = 1,
> +               .flags          = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
> +       }, {
> +               .pixelformat    = V4L2_PIX_FMT_UYVY,
> +               .mdp_color      = MDP_COLOR_UYVY,
> +               .depth          = { 16 },
> +               .row_depth      = { 16 },
> +               .num_planes     = 1,
> +               .walign         = 1,
> +               .flags          = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
> +       }, {
> +               .pixelformat    = V4L2_PIX_FMT_VYUY,
> +               .mdp_color      = MDP_COLOR_VYUY,
> +               .depth          = { 16 },
> +               .row_depth      = { 16 },
> +               .num_planes     = 1,
> +               .walign         = 1,
> +               .flags          = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
> +       }, {
> +               .pixelformat    = V4L2_PIX_FMT_YUYV,
> +               .mdp_color      = MDP_COLOR_YUYV,
> +               .depth          = { 16 },
> +               .row_depth      = { 16 },
> +               .num_planes     = 1,
> +               .walign         = 1,
> +               .flags          = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
> +       }, {
> +               .pixelformat    = V4L2_PIX_FMT_YVYU,
> +               .mdp_color      = MDP_COLOR_YVYU,
> +               .depth          = { 16 },
> +               .row_depth      = { 16 },
> +               .num_planes     = 1,
> +               .walign         = 1,
> +               .flags          = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
> +       }, {
> +               .pixelformat    = V4L2_PIX_FMT_YUV420,
> +               .mdp_color      = MDP_COLOR_I420,
> +               .depth          = { 12 },
> +               .row_depth      = { 8 },
> +               .num_planes     = 1,
> +               .walign         = 1,
> +               .halign         = 1,
> +               .flags          = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
> +       }, {
> +               .pixelformat    = V4L2_PIX_FMT_YVU420,
> +               .mdp_color      = MDP_COLOR_YV12,
> +               .depth          = { 12 },
> +               .row_depth      = { 8 },
> +               .num_planes     = 1,
> +               .walign         = 1,
> +               .halign         = 1,
> +               .flags          = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
> +       }, {
> +               .pixelformat    = V4L2_PIX_FMT_NV12,
> +               .mdp_color      = MDP_COLOR_NV12,
> +               .depth          = { 12 },
> +               .row_depth      = { 8 },
> +               .num_planes     = 1,
> +               .walign         = 1,
> +               .halign         = 1,
> +               .flags          = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
> +       }, {
> +               .pixelformat    = V4L2_PIX_FMT_NV21,
> +               .mdp_color      = MDP_COLOR_NV21,
> +               .depth          = { 12 },
> +               .row_depth      = { 8 },
> +               .num_planes     = 1,
> +               .walign         = 1,
> +               .halign         = 1,
> +               .flags          = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
> +       }, {
> +               .pixelformat    = V4L2_PIX_FMT_NV16,
> +               .mdp_color      = MDP_COLOR_NV16,
> +               .depth          = { 16 },
> +               .row_depth      = { 8 },
> +               .num_planes     = 1,
> +               .walign         = 1,
> +               .flags          = MDP_FMT_FLAG_OUTPUT,
> +       }, {
> +               .pixelformat    = V4L2_PIX_FMT_NV61,
> +               .mdp_color      = MDP_COLOR_NV61,
> +               .depth          = { 16 },
> +               .row_depth      = { 8 },
> +               .num_planes     = 1,
> +               .walign         = 1,
> +               .flags          = MDP_FMT_FLAG_OUTPUT,
> +       }, {
> +               .pixelformat    = V4L2_PIX_FMT_NV24,
> +               .mdp_color      = MDP_COLOR_NV24,
> +               .depth          = { 24 },
> +               .row_depth      = { 8 },
> +               .num_planes     = 1,
> +               .flags          = MDP_FMT_FLAG_OUTPUT,
> +       }, {
> +               .pixelformat    = V4L2_PIX_FMT_NV42,
> +               .mdp_color      = MDP_COLOR_NV42,
> +               .depth          = { 24 },
> +               .row_depth      = { 8 },
> +               .num_planes     = 1,
> +               .flags          = MDP_FMT_FLAG_OUTPUT,
> +       }, {
> +               .pixelformat    = V4L2_PIX_FMT_MT21C,
> +               .mdp_color      = MDP_COLOR_420_BLK_UFO,
> +               .depth          = { 8, 4 },
> +               .row_depth      = { 8, 8 },
> +               .num_planes     = 2,
> +               .walign         = 4,
> +               .halign         = 5,
> +               .flags          = MDP_FMT_FLAG_OUTPUT,
> +       }, {
> +               .pixelformat    = V4L2_PIX_FMT_NV12MT,
> +               .mdp_color      = MDP_COLOR_420_BLK,
> +               .depth          = { 8, 4 },
> +               .row_depth      = { 8, 8 },
> +               .num_planes     = 2,
> +               .walign         = 4,
> +               .halign         = 5,
> +               .flags          = MDP_FMT_FLAG_OUTPUT,
> +       }, {
> +               .pixelformat    = V4L2_PIX_FMT_NV12M,
> +               .mdp_color      = MDP_COLOR_NV12,
> +               .depth          = { 8, 4 },
> +               .row_depth      = { 8, 8 },
> +               .num_planes     = 2,
> +               .walign         = 1,
> +               .halign         = 1,
> +               .flags          = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
> +       }, {
> +               .pixelformat    = V4L2_PIX_FMT_NV21M,
> +               .mdp_color      = MDP_COLOR_NV21,
> +               .depth          = { 8, 4 },
> +               .row_depth      = { 8, 8 },
> +               .num_planes     = 2,
> +               .walign         = 1,
> +               .halign         = 1,
> +               .flags          = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
> +       }, {
> +               .pixelformat    = V4L2_PIX_FMT_NV16M,
> +               .mdp_color      = MDP_COLOR_NV16,
> +               .depth          = { 8, 8 },
> +               .row_depth      = { 8, 8 },
> +               .num_planes     = 2,
> +               .walign         = 1,
> +               .flags          = MDP_FMT_FLAG_OUTPUT,
> +       }, {
> +               .pixelformat    = V4L2_PIX_FMT_NV61M,
> +               .mdp_color      = MDP_COLOR_NV61,
> +               .depth          = { 8, 8 },
> +               .row_depth      = { 8, 8 },
> +               .num_planes     = 2,
> +               .walign         = 1,
> +               .flags          = MDP_FMT_FLAG_OUTPUT,
> +       }, {
> +               .pixelformat    = V4L2_PIX_FMT_YUV420M,
> +               .mdp_color      = MDP_COLOR_I420,
> +               .depth          = { 8, 2, 2 },
> +               .row_depth      = { 8, 4, 4 },
> +               .num_planes     = 3,
> +               .walign         = 1,
> +               .halign         = 1,
> +               .flags          = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
> +       }, {
> +               .pixelformat    = V4L2_PIX_FMT_YVU420M,
> +               .mdp_color      = MDP_COLOR_YV12,
> +               .depth          = { 8, 2, 2 },
> +               .row_depth      = { 8, 4, 4 },
> +               .num_planes     = 3,
> +               .walign         = 1,
> +               .halign         = 1,
> +               .flags          = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE,
> +       }
> +};
> +
> +static const struct mdp_limit mdp_def_limit = {
> +       .out_limit = {
> +               .wmin   = 16,
> +               .hmin   = 16,
> +               .wmax   = 8176,
> +               .hmax   = 8176,
> +       },
> +       .cap_limit = {
> +               .wmin   = 2,
> +               .hmin   = 2,
> +               .wmax   = 8176,
> +               .hmax   = 8176,
> +       },
> +       .h_scale_up_max = 32,
> +       .v_scale_up_max = 32,
> +       .h_scale_down_max = 20,
> +       .v_scale_down_max = 128,
> +};
> +
> +static const struct mdp_format *mdp_find_fmt(u32 pixelformat, u32 type)
> +{
> +       u32 i, flag;
> +
> +       flag = V4L2_TYPE_IS_OUTPUT(type) ? MDP_FMT_FLAG_OUTPUT :
> +                                       MDP_FMT_FLAG_CAPTURE;
> +       for (i = 0; i < ARRAY_SIZE(mdp_formats); ++i) {
> +               if (!(mdp_formats[i].flags & flag))
> +                       continue;
> +               if (mdp_formats[i].pixelformat == pixelformat)
> +                       return &mdp_formats[i];
> +       }
> +       return NULL;
> +}
> +
> +static const struct mdp_format *mdp_find_fmt_by_index(u32 index, u32 type)
> +{
> +       u32 i, flag, num = 0;
> +
> +       flag = V4L2_TYPE_IS_OUTPUT(type) ? MDP_FMT_FLAG_OUTPUT :
> +                                       MDP_FMT_FLAG_CAPTURE;
> +       for (i = 0; i < ARRAY_SIZE(mdp_formats); ++i) {
> +               if (!(mdp_formats[i].flags & flag))
> +                       continue;
> +               if (index == num)
> +                       return &mdp_formats[i];
> +               num++;
> +       }
> +       return NULL;
> +}
> +
> +enum mdp_ycbcr_profile mdp_map_ycbcr_prof_mplane(struct v4l2_format *f,
> +                                                u32 mdp_color)
> +{
> +       struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
> +
> +       if (MDP_COLOR_IS_RGB(mdp_color))
> +               return MDP_YCBCR_PROFILE_FULL_BT601;
> +
> +       switch (pix_mp->colorspace) {
> +       case V4L2_COLORSPACE_JPEG:
> +               return MDP_YCBCR_PROFILE_JPEG;
> +       case V4L2_COLORSPACE_REC709:
> +       case V4L2_COLORSPACE_DCI_P3:
> +               if (pix_mp->quantization == V4L2_QUANTIZATION_FULL_RANGE)
> +                       return MDP_YCBCR_PROFILE_FULL_BT709;
> +               return MDP_YCBCR_PROFILE_BT709;
> +       case V4L2_COLORSPACE_BT2020:
> +               if (pix_mp->quantization == V4L2_QUANTIZATION_FULL_RANGE)
> +                       return MDP_YCBCR_PROFILE_FULL_BT2020;
> +               return MDP_YCBCR_PROFILE_BT2020;
> +       }
> +       /* V4L2_COLORSPACE_SRGB or else */
> +       if (pix_mp->quantization == V4L2_QUANTIZATION_FULL_RANGE)
> +               return MDP_YCBCR_PROFILE_FULL_BT601;
> +       return MDP_YCBCR_PROFILE_BT601;
> +}
> +
> +static void mdp_bound_align_image(u32 *w, unsigned int wmin, unsigned int wmax,
> +                                 unsigned int walign,
> +                               u32 *h, unsigned int hmin, unsigned int hmax,
> +                               unsigned int halign, unsigned int salign)
> +{
> +       unsigned int org_w, org_h, wstep, hstep;
> +
> +       org_w = *w;
> +       org_h = *h;
> +       v4l_bound_align_image(w, wmin, wmax, walign, h, hmin, hmax, halign,
> +                             salign);
> +
> +       wstep = 1 << walign;
> +       hstep = 1 << halign;
> +       if (*w < org_w && (*w + wstep) <= wmax)
> +               *w += wstep;
> +       if (*h < org_h && (*h + hstep) <= hmax)
> +               *h += hstep;
> +}
> +
> +static int mdp_clamp_align(s32 *x, int min, int max, unsigned int align)
> +{
> +       unsigned int mask;
> +
> +       if (min < 0 || max < 0)
> +               return -ERANGE;
> +
> +       /* Bits that must be zero to be aligned */
> +       mask = ~((1 << align) - 1);
> +
> +       min = 0 ? 0 : ((min + ~mask) & mask);
> +       max = max & mask;
> +       if ((unsigned int)min > (unsigned int)max)
> +               return -ERANGE;
> +
> +       /* Clamp to aligned min and max */
> +       *x = clamp(*x, min, max);
> +
> +       /* Round to nearest aligned value */
> +       if (align)
> +               *x = (*x + (1 << (align - 1))) & mask;
> +       return 0;
> +}
> +
> +int mdp_enum_fmt_mplane(struct v4l2_fmtdesc *f)
> +{
> +       const struct mdp_format *fmt;
> +
> +       if (!V4L2_TYPE_IS_MULTIPLANAR(f->type))
> +               return -EINVAL;
> +
> +       fmt = mdp_find_fmt_by_index(f->index, f->type);
> +       if (!fmt)
> +               return -EINVAL;
> +
> +       /* f->description */
> +       f->pixelformat = fmt->pixelformat;
> +       return 0;
> +}
> +
> +const struct mdp_format *mdp_try_fmt_mplane(struct v4l2_format *f,
> +                                           struct mdp_frameparam *param,
> +                                           u32 ctx_id)
> +{
> +       struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
> +       const struct mdp_format *fmt;
> +       const struct mdp_pix_limit *pix_limit;
> +       u32 wmin, wmax, hmin, hmax, org_w, org_h;
> +       unsigned int i;
> +
> +       if (!V4L2_TYPE_IS_MULTIPLANAR(f->type))
> +               return NULL;
> +
> +       fmt = mdp_find_fmt(pix_mp->pixelformat, f->type);
> +       if (!fmt)
> +               fmt = mdp_find_fmt_by_index(0, f->type);
> +       if (!fmt) {
> +               mdp_dbg(0, "[%d] pixelformat %c%c%c%c invalid", ctx_id,
> +                       (pix_mp->pixelformat & 0xff),
> +                       (pix_mp->pixelformat >>  8) & 0xff,
> +                       (pix_mp->pixelformat >> 16) & 0xff,
> +                       (pix_mp->pixelformat >> 24) & 0xff);
> +               return NULL;
> +       }
> +
> +       pix_mp->field = V4L2_FIELD_NONE;
> +       pix_mp->flags = 0;
> +       pix_mp->pixelformat = fmt->pixelformat;
> +       if (!V4L2_TYPE_IS_OUTPUT(f->type)) {
> +               pix_mp->colorspace = param->colorspace;
> +               pix_mp->xfer_func = param->xfer_func;
> +               pix_mp->ycbcr_enc = param->ycbcr_enc;
> +               pix_mp->quantization = param->quant;
> +       }
> +       memset(pix_mp->reserved, 0, sizeof(pix_mp->reserved));
> +
> +       pix_limit = V4L2_TYPE_IS_OUTPUT(f->type) ? &param->limit->out_limit :
> +               &param->limit->cap_limit;
> +       wmin = pix_limit->wmin;
> +       wmax = pix_limit->wmax;
> +       hmin = pix_limit->hmin;
> +       hmax = pix_limit->hmax;
> +       org_w = pix_mp->width;
> +       org_h = pix_mp->height;
> +
> +       mdp_bound_align_image(&pix_mp->width, wmin, wmax, fmt->walign,
> +                             &pix_mp->height, hmin, hmax, fmt->halign,
> +                               fmt->salign);
> +       if (org_w != pix_mp->width || org_h != pix_mp->height)
> +               mdp_dbg(1, "[%d] size change: %ux%u to %ux%u", ctx_id,
> +                       org_w, org_h, pix_mp->width, pix_mp->height);
> +
> +       if (pix_mp->num_planes && pix_mp->num_planes != fmt->num_planes)
> +               mdp_dbg(1, "[%d] num of planes change: %u to %u", ctx_id,
> +                       pix_mp->num_planes, fmt->num_planes);
> +       pix_mp->num_planes = fmt->num_planes;
> +
> +       for (i = 0; i < pix_mp->num_planes; ++i) {
> +               u32 min_bpl = (pix_mp->width * fmt->row_depth[i]) / 8;
> +               u32 bpl = pix_mp->plane_fmt[i].bytesperline;
> +               u32 si;
> +
> +               if (bpl < min_bpl)
> +                       bpl = min_bpl;
> +               si = (bpl * pix_mp->height * fmt->depth[i]) / fmt->row_depth[i];
> +
> +               pix_mp->plane_fmt[i].bytesperline = bpl;
> +               if (pix_mp->plane_fmt[i].sizeimage < si)
> +                       pix_mp->plane_fmt[i].sizeimage = si;
> +               memset(pix_mp->plane_fmt[i].reserved, 0,
> +                      sizeof(pix_mp->plane_fmt[i].reserved));
> +               mdp_dbg(2, "[%d] p%u, bpl:%u (%u), sizeimage:%u (%u)", ctx_id,
> +                       i, bpl, min_bpl, pix_mp->plane_fmt[i].sizeimage, si);
> +       }
> +
> +       return fmt;
> +}
> +
> +static inline int mdp_clamp_start(s32 *x, int min, int max, unsigned int align,
> +                                 u32 flags)

Please remove the inline here, the compiler will decide whether this
needs to be inlined or not.

> +{
> +       if (flags & V4L2_SEL_FLAG_GE)
> +               max = *x;
> +       if (flags & V4L2_SEL_FLAG_LE)
> +               min = *x;
> +       return mdp_clamp_align(x, min, max, align);
> +}
> +
> +static inline int mdp_clamp_end(s32 *x, int min, int max, unsigned int align,
> +                               u32 flags)

Same here.

> +{
> +       if (flags & V4L2_SEL_FLAG_GE)
> +               min = *x;
> +       if (flags & V4L2_SEL_FLAG_LE)
> +               max = *x;
> +       return mdp_clamp_align(x, min, max, align);
> +}

[snip]

> diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-regs.h b/drivers/media/platform/mtk-mdp3/mtk-mdp3-regs.h
> new file mode 100644
> index 000000000000..a2b5c31d3d39
> --- /dev/null
> +++ b/drivers/media/platform/mtk-mdp3/mtk-mdp3-regs.h
> @@ -0,0 +1,386 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2018 MediaTek Inc.
> + * Author: Ping-Hsun Wu <ping-hsun.wu@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_MDP3_REGS_H__
> +#define __MTK_MDP3_REGS_H__
> +
> +#include <linux/videodev2.h>
> +#include <media/videobuf2-core.h>
> +#include "mtk-img-ipi.h"
> +
> +/*
> + * MDP native color code
> + * Plane count: 1, 2, 3
> + * H-subsample: 0, 1, 2
> + * V-subsample: 0, 1
> + * Color group: 0-RGB, 1-YUV, 2-raw
> + */
> +#define MDP_COLOR(PACKED, LOOSE, VIDEO, PLANE, HF, VF, BITS, GROUP, SWAP, ID)\
> +       (((PACKED) << 27) | ((LOOSE) << 26) | ((VIDEO) << 23) |\
> +        ((PLANE) << 21) | ((HF) << 19) | ((VF) << 18) | ((BITS) << 8) |\
> +        ((GROUP) << 6) | ((SWAP) << 5) | ((ID) << 0))
> +
> +#define MDP_COLOR_IS_10BIT_PACKED(c)    ((0x08000000 & (c)) >> 27)
> +#define MDP_COLOR_IS_10BIT_LOOSE(c)    (((0x0c000000 & (c)) >> 26) == 1)
> +#define MDP_COLOR_IS_10BIT_TILE(c)     (((0x0c000000 & (c)) >> 26) == 3)
> +#define MDP_COLOR_IS_UFP(c)             ((0x02000000 & (c)) >> 25)
> +#define MDP_COLOR_IS_INTERLACED(c)      ((0x01000000 & (c)) >> 24)
> +#define MDP_COLOR_IS_BLOCK_MODE(c)      ((0x00800000 & (c)) >> 23)
> +#define MDP_COLOR_GET_PLANE_COUNT(c)    ((0x00600000 & (c)) >> 21)
> +#define MDP_COLOR_GET_H_SUBSAMPLE(c)    ((0x00180000 & (c)) >> 19)
> +#define MDP_COLOR_GET_V_SUBSAMPLE(c)    ((0x00040000 & (c)) >> 18)
> +#define MDP_COLOR_BITS_PER_PIXEL(c)     ((0x0003ff00 & (c)) >>  8)
> +#define MDP_COLOR_GET_GROUP(c)          ((0x000000c0 & (c)) >>  6)
> +#define MDP_COLOR_IS_SWAPPED(c)         ((0x00000020 & (c)) >>  5)
> +#define MDP_COLOR_GET_UNIQUE_ID(c)      ((0x0000001f & (c)) >>  0)
> +#define MDP_COLOR_GET_HW_FORMAT(c)      ((0x0000001f & (c)) >>  0)
> +
> +#define MDP_COLOR_IS_RGB(c)            (MDP_COLOR_GET_GROUP(c) == 0)
> +#define MDP_COLOR_IS_YUV(c)            (MDP_COLOR_GET_GROUP(c) == 1)
> +#define MDP_COLOR_IS_UV_COPLANE(c)     ((MDP_COLOR_GET_PLANE_COUNT(c) == 2) &&\
> +                                        MDP_COLOR_IS_YUV(c))
> +
> +enum mdp_color {
> +       MDP_COLOR_UNKNOWN       = 0,
> +
> +       //MDP_COLOR_FULLG8,
> +       MDP_COLOR_FULLG8_RGGB   = MDP_COLOR(0, 0, 0, 1, 0, 0,  8, 2,  0, 21),
> +       MDP_COLOR_FULLG8_GRBG   = MDP_COLOR(0, 0, 0, 1, 0, 1,  8, 2,  0, 21),
> +       MDP_COLOR_FULLG8_GBRG   = MDP_COLOR(0, 0, 0, 1, 1, 0,  8, 2,  0, 21),
> +       MDP_COLOR_FULLG8_BGGR   = MDP_COLOR(0, 0, 0, 1, 1, 1,  8, 2,  0, 21),
> +       MDP_COLOR_FULLG8        = MDP_COLOR_FULLG8_BGGR,
> +
> +       //MDP_COLOR_FULLG10,
> +       MDP_COLOR_FULLG10_RGGB  = MDP_COLOR(0, 0, 0, 1, 0, 0, 10, 2,  0, 21),
> +       MDP_COLOR_FULLG10_GRBG  = MDP_COLOR(0, 0, 0, 1, 0, 1, 10, 2,  0, 21),
> +       MDP_COLOR_FULLG10_GBRG  = MDP_COLOR(0, 0, 0, 1, 1, 0, 10, 2,  0, 21),
> +       MDP_COLOR_FULLG10_BGGR  = MDP_COLOR(0, 0, 0, 1, 1, 1, 10, 2,  0, 21),
> +       MDP_COLOR_FULLG10       = MDP_COLOR_FULLG10_BGGR,
> +
> +       //MDP_COLOR_FULLG12,
> +       MDP_COLOR_FULLG12_RGGB  = MDP_COLOR(0, 0, 0, 1, 0, 0, 12, 2,  0, 21),
> +       MDP_COLOR_FULLG12_GRBG  = MDP_COLOR(0, 0, 0, 1, 0, 1, 12, 2,  0, 21),
> +       MDP_COLOR_FULLG12_GBRG  = MDP_COLOR(0, 0, 0, 1, 1, 0, 12, 2,  0, 21),
> +       MDP_COLOR_FULLG12_BGGR  = MDP_COLOR(0, 0, 0, 1, 1, 1, 12, 2,  0, 21),
> +       MDP_COLOR_FULLG12       = MDP_COLOR_FULLG12_BGGR,
> +
> +       //MDP_COLOR_FULLG14,
> +       MDP_COLOR_FULLG14_RGGB  = MDP_COLOR(0, 0, 0, 1, 0, 0, 14, 2,  0, 21),
> +       MDP_COLOR_FULLG14_GRBG  = MDP_COLOR(0, 0, 0, 1, 0, 1, 14, 2,  0, 21),
> +       MDP_COLOR_FULLG14_GBRG  = MDP_COLOR(0, 0, 0, 1, 1, 0, 14, 2,  0, 21),
> +       MDP_COLOR_FULLG14_BGGR  = MDP_COLOR(0, 0, 0, 1, 1, 1, 14, 2,  0, 21),
> +       MDP_COLOR_FULLG14       = MDP_COLOR_FULLG14_BGGR,
> +
> +       MDP_COLOR_UFO10         = MDP_COLOR(0, 0, 0, 1, 0, 0, 10, 2,  0, 24),
> +
> +       //MDP_COLOR_BAYER8,
> +       MDP_COLOR_BAYER8_RGGB   = MDP_COLOR(0, 0, 0, 1, 0, 0,  8, 2,  0, 20),
> +       MDP_COLOR_BAYER8_GRBG   = MDP_COLOR(0, 0, 0, 1, 0, 1,  8, 2,  0, 20),
> +       MDP_COLOR_BAYER8_GBRG   = MDP_COLOR(0, 0, 0, 1, 1, 0,  8, 2,  0, 20),
> +       MDP_COLOR_BAYER8_BGGR   = MDP_COLOR(0, 0, 0, 1, 1, 1,  8, 2,  0, 20),
> +       MDP_COLOR_BAYER8        = MDP_COLOR_BAYER8_BGGR,
> +
> +       //MDP_COLOR_BAYER10,
> +       MDP_COLOR_BAYER10_RGGB  = MDP_COLOR(0, 0, 0, 1, 0, 0, 10, 2,  0, 20),
> +       MDP_COLOR_BAYER10_GRBG  = MDP_COLOR(0, 0, 0, 1, 0, 1, 10, 2,  0, 20),
> +       MDP_COLOR_BAYER10_GBRG  = MDP_COLOR(0, 0, 0, 1, 1, 0, 10, 2,  0, 20),
> +       MDP_COLOR_BAYER10_BGGR  = MDP_COLOR(0, 0, 0, 1, 1, 1, 10, 2,  0, 20),
> +       MDP_COLOR_BAYER10       = MDP_COLOR_BAYER10_BGGR,
> +
> +       //MDP_COLOR_BAYER12,
> +       MDP_COLOR_BAYER12_RGGB  = MDP_COLOR(0, 0, 0, 1, 0, 0, 12, 2,  0, 20),
> +       MDP_COLOR_BAYER12_GRBG  = MDP_COLOR(0, 0, 0, 1, 0, 1, 12, 2,  0, 20),
> +       MDP_COLOR_BAYER12_GBRG  = MDP_COLOR(0, 0, 0, 1, 1, 0, 12, 2,  0, 20),
> +       MDP_COLOR_BAYER12_BGGR  = MDP_COLOR(0, 0, 0, 1, 1, 1, 12, 2,  0, 20),
> +       MDP_COLOR_BAYER12       = MDP_COLOR_BAYER12_BGGR,
> +
> +       MDP_COLOR_RGB48         = MDP_COLOR(0, 0, 0, 1, 0, 0, 48, 0,  0, 23),
> +       /* For bayer+mono raw-16 */
> +       MDP_COLOR_RGB565_RAW    = MDP_COLOR(0, 0, 0, 1, 0, 0, 16, 2,  0, 0),
> +
> +       MDP_COLOR_BAYER8_UNPAK  = MDP_COLOR(0, 0, 0, 1, 0, 0,  8, 2,  0, 22),
> +       MDP_COLOR_BAYER10_UNPAK = MDP_COLOR(0, 0, 0, 1, 0, 0, 10, 2,  0, 22),
> +       MDP_COLOR_BAYER12_UNPAK = MDP_COLOR(0, 0, 0, 1, 0, 0, 12, 2,  0, 22),
> +       MDP_COLOR_BAYER14_UNPAK = MDP_COLOR(0, 0, 0, 1, 0, 0, 14, 2,  0, 22),
> +
> +       /* Unified formats */
> +       MDP_COLOR_GREY          = MDP_COLOR(0, 0, 0, 1, 0, 0,  8, 1,  0, 7),
> +
> +       MDP_COLOR_RGB565        = MDP_COLOR(0, 0, 0, 1, 0, 0, 16, 0,  0, 0),
> +       MDP_COLOR_BGR565        = MDP_COLOR(0, 0, 0, 1, 0, 0, 16, 0,  1, 0),
> +       MDP_COLOR_RGB888        = MDP_COLOR(0, 0, 0, 1, 0, 0, 24, 0,  1, 1),
> +       MDP_COLOR_BGR888        = MDP_COLOR(0, 0, 0, 1, 0, 0, 24, 0,  0, 1),
> +       MDP_COLOR_RGBA8888      = MDP_COLOR(0, 0, 0, 1, 0, 0, 32, 0,  1, 2),
> +       MDP_COLOR_BGRA8888      = MDP_COLOR(0, 0, 0, 1, 0, 0, 32, 0,  0, 2),
> +       MDP_COLOR_ARGB8888      = MDP_COLOR(0, 0, 0, 1, 0, 0, 32, 0,  1, 3),
> +       MDP_COLOR_ABGR8888      = MDP_COLOR(0, 0, 0, 1, 0, 0, 32, 0,  0, 3),
> +
> +       MDP_COLOR_UYVY          = MDP_COLOR(0, 0, 0, 1, 1, 0, 16, 1,  0, 4),
> +       MDP_COLOR_VYUY          = MDP_COLOR(0, 0, 0, 1, 1, 0, 16, 1,  1, 4),
> +       MDP_COLOR_YUYV          = MDP_COLOR(0, 0, 0, 1, 1, 0, 16, 1,  0, 5),
> +       MDP_COLOR_YVYU          = MDP_COLOR(0, 0, 0, 1, 1, 0, 16, 1,  1, 5),
> +
> +       MDP_COLOR_I420          = MDP_COLOR(0, 0, 0, 3, 1, 1,  8, 1,  0, 8),
> +       MDP_COLOR_YV12          = MDP_COLOR(0, 0, 0, 3, 1, 1,  8, 1,  1, 8),
> +       MDP_COLOR_I422          = MDP_COLOR(0, 0, 0, 3, 1, 0,  8, 1,  0, 9),
> +       MDP_COLOR_YV16          = MDP_COLOR(0, 0, 0, 3, 1, 0,  8, 1,  1, 9),
> +       MDP_COLOR_I444          = MDP_COLOR(0, 0, 0, 3, 0, 0,  8, 1,  0, 10),
> +       MDP_COLOR_YV24          = MDP_COLOR(0, 0, 0, 3, 0, 0,  8, 1,  1, 10),
> +
> +       MDP_COLOR_NV12          = MDP_COLOR(0, 0, 0, 2, 1, 1,  8, 1,  0, 12),
> +       MDP_COLOR_NV21          = MDP_COLOR(0, 0, 0, 2, 1, 1,  8, 1,  1, 12),
> +       MDP_COLOR_NV16          = MDP_COLOR(0, 0, 0, 2, 1, 0,  8, 1,  0, 13),
> +       MDP_COLOR_NV61          = MDP_COLOR(0, 0, 0, 2, 1, 0,  8, 1,  1, 13),
> +       MDP_COLOR_NV24          = MDP_COLOR(0, 0, 0, 2, 0, 0,  8, 1,  0, 14),
> +       MDP_COLOR_NV42          = MDP_COLOR(0, 0, 0, 2, 0, 0,  8, 1,  1, 14),
> +
> +       /* Mediatek proprietary formats */
> +       /* UFO encoded block mode */
> +       MDP_COLOR_420_BLK_UFO   = MDP_COLOR(0, 0, 5, 2, 1, 1, 256, 1, 0, 12),
> +       /* Block mode */
> +       MDP_COLOR_420_BLK       = MDP_COLOR(0, 0, 1, 2, 1, 1, 256, 1, 0, 12),
> +       /* Block mode + field mode */
> +       MDP_COLOR_420_BLKI      = MDP_COLOR(0, 0, 3, 2, 1, 1, 256, 1, 0, 12),
> +       /* Block mode */
> +       MDP_COLOR_422_BLK       = MDP_COLOR(0, 0, 1, 1, 1, 0, 512, 1, 0, 4),
> +
> +       MDP_COLOR_IYU2          = MDP_COLOR(0, 0, 0, 1, 0, 0, 24,  1, 0, 25),
> +       MDP_COLOR_YUV444        = MDP_COLOR(0, 0, 0, 1, 0, 0, 24,  1, 0, 30),
> +
> +       /* Packed 10-bit formats */
> +       MDP_COLOR_RGBA1010102   = MDP_COLOR(1, 0, 0, 1, 0, 0, 32,  0, 1, 2),
> +       MDP_COLOR_BGRA1010102   = MDP_COLOR(1, 0, 0, 1, 0, 0, 32,  0, 0, 2),
> +       /* Packed 10-bit UYVY */
> +       MDP_COLOR_UYVY_10P      = MDP_COLOR(1, 0, 0, 1, 1, 0, 20,  1, 0, 4),
> +       /* Packed 10-bit NV21 */
> +       MDP_COLOR_NV21_10P      = MDP_COLOR(1, 0, 0, 2, 1, 1, 10,  1, 1, 12),
> +       /* 10-bit block mode */
> +       MDP_COLOR_420_BLK_10_H  = MDP_COLOR(1, 0, 1, 2, 1, 1, 320, 1, 0, 12),
> +       /* 10-bit HEVC tile mode */
> +       MDP_COLOR_420_BLK_10_V  = MDP_COLOR(1, 1, 1, 2, 1, 1, 320, 1, 0, 12),
> +       /* UFO encoded 10-bit block mode */
> +       MDP_COLOR_420_BLK_U10_H = MDP_COLOR(1, 0, 5, 2, 1, 1, 320, 1, 0, 12),
> +       /* UFO encoded 10-bit HEVC tile mode */
> +       MDP_COLOR_420_BLK_U10_V = MDP_COLOR(1, 1, 5, 2, 1, 1, 320, 1, 0, 12),
> +
> +       /* Loose 10-bit formats */
> +       MDP_COLOR_UYVY_10L      = MDP_COLOR(0, 1, 0, 1, 1, 0, 20,  1, 0, 4),
> +       MDP_COLOR_VYUY_10L      = MDP_COLOR(0, 1, 0, 1, 1, 0, 20,  1, 1, 4),
> +       MDP_COLOR_YUYV_10L      = MDP_COLOR(0, 1, 0, 1, 1, 0, 20,  1, 0, 5),
> +       MDP_COLOR_YVYU_10L      = MDP_COLOR(0, 1, 0, 1, 1, 0, 20,  1, 1, 5),
> +       MDP_COLOR_NV12_10L      = MDP_COLOR(0, 1, 0, 2, 1, 1, 10,  1, 0, 12),
> +       MDP_COLOR_NV21_10L      = MDP_COLOR(0, 1, 0, 2, 1, 1, 10,  1, 1, 12),
> +       MDP_COLOR_NV16_10L      = MDP_COLOR(0, 1, 0, 2, 1, 0, 10,  1, 0, 13),
> +       MDP_COLOR_NV61_10L      = MDP_COLOR(0, 1, 0, 2, 1, 0, 10,  1, 1, 13),
> +       MDP_COLOR_YV12_10L      = MDP_COLOR(0, 1, 0, 3, 1, 1, 10,  1, 1, 8),
> +       MDP_COLOR_I420_10L      = MDP_COLOR(0, 1, 0, 3, 1, 1, 10,  1, 0, 8),
> +};
> +
> +/* Minimum Y stride that is accepted by MDP HW */
> +static inline u32 mdp_color_get_min_y_stride(enum mdp_color c, u32 width)
> +{
> +       return ((MDP_COLOR_BITS_PER_PIXEL(c) * width) + 4) >> 3;
> +}
> +
> +/* Minimum UV stride that is accepted by MDP HW */
> +static inline u32 mdp_color_get_min_uv_stride(enum mdp_color c, u32 width)
> +{
> +       u32 min_stride;
> +
> +       if (MDP_COLOR_GET_PLANE_COUNT(c) == 1)
> +               return 0;
> +       min_stride = mdp_color_get_min_y_stride(c, width)
> +               >> MDP_COLOR_GET_H_SUBSAMPLE(c);
> +       if (MDP_COLOR_IS_UV_COPLANE(c) && !MDP_COLOR_IS_BLOCK_MODE(c))
> +               min_stride = min_stride * 2;
> +       return min_stride;
> +}
> +
> +/* Minimum Y plane size that is necessary in buffer */
> +static inline u32 mdp_color_get_min_y_size(enum mdp_color c,
> +                                          u32 width, u32 height)
> +{
> +       if (MDP_COLOR_IS_BLOCK_MODE(c))
> +               return ((MDP_COLOR_BITS_PER_PIXEL(c) * width) >> 8) * height;
> +       return mdp_color_get_min_y_stride(c, width) * height;
> +}
> +
> +/* Minimum UV plane size that is necessary in buffer */
> +static inline u32 mdp_color_get_min_uv_size(enum mdp_color c,
> +                                           u32 width, u32 height)
> +{
> +       height = height >> MDP_COLOR_GET_V_SUBSAMPLE(c);
> +       if (MDP_COLOR_IS_BLOCK_MODE(c) && (MDP_COLOR_GET_PLANE_COUNT(c) > 1))
> +               return ((MDP_COLOR_BITS_PER_PIXEL(c) * width) >> 8) * height;
> +       return mdp_color_get_min_uv_stride(c, width) * height;
> +}
> +
> +/* Combine colorspace, xfer_func, ycbcr_encoding, and quantization */
> +enum mdp_ycbcr_profile {
> +       /* V4L2_YCBCR_ENC_601 and V4L2_QUANTIZATION_LIM_RANGE */
> +       MDP_YCBCR_PROFILE_BT601,
> +       /* V4L2_YCBCR_ENC_709 and V4L2_QUANTIZATION_LIM_RANGE */
> +       MDP_YCBCR_PROFILE_BT709,
> +       /* V4L2_YCBCR_ENC_601 and V4L2_QUANTIZATION_FULL_RANGE */
> +       MDP_YCBCR_PROFILE_JPEG,
> +       MDP_YCBCR_PROFILE_FULL_BT601 = MDP_YCBCR_PROFILE_JPEG,
> +
> +       /* Colorspaces not support for capture */
> +       /* V4L2_YCBCR_ENC_BT2020 and V4L2_QUANTIZATION_LIM_RANGE */
> +       MDP_YCBCR_PROFILE_BT2020,
> +       /* V4L2_YCBCR_ENC_709 and V4L2_QUANTIZATION_FULL_RANGE */
> +       MDP_YCBCR_PROFILE_FULL_BT709,
> +       /* V4L2_YCBCR_ENC_BT2020 and V4L2_QUANTIZATION_FULL_RANGE */
> +       MDP_YCBCR_PROFILE_FULL_BT2020,
> +};
> +
> +#define MDP_FMT_FLAG_OUTPUT    BIT(0)
> +#define MDP_FMT_FLAG_CAPTURE   BIT(1)
> +
> +struct mdp_format {
> +       u32     pixelformat;
> +       u32     mdp_color;
> +       u8      depth[VIDEO_MAX_PLANES];
> +       u8      row_depth[VIDEO_MAX_PLANES];
> +       u8      num_planes;
> +       u8      walign;
> +       u8      halign;
> +       u8      salign;
> +       u32     flags;
> +};
> +
> +struct mdp_pix_limit {
> +       u32     wmin;
> +       u32     hmin;
> +       u32     wmax;
> +       u32     hmax;
> +};
> +
> +struct mdp_limit {
> +       struct mdp_pix_limit    out_limit;
> +       struct mdp_pix_limit    cap_limit;
> +       u32                     h_scale_up_max;
> +       u32                     v_scale_up_max;
> +       u32                     h_scale_down_max;
> +       u32                     v_scale_down_max;
> +};
> +
> +enum mdp_stream_type {
> +       MDP_STREAM_TYPE_UNKNOWN,
> +
> +       MDP_STREAM_TYPE_BITBLT,
> +       MDP_STREAM_TYPE_GPU_BITBLT,
> +       MDP_STREAM_TYPE_DUAL_BITBLT,
> +       MDP_STREAM_TYPE_2ND_BITBLT,
> +
> +       /* MDP_STREAM_TYPE_FRAG, */
> +       /* MDP_STREAM_TYPE_FRAG_JPEGDEC, */
> +
> +       MDP_STREAM_TYPE_ISP_IC,
> +       MDP_STREAM_TYPE_ISP_VR,
> +       MDP_STREAM_TYPE_ISP_ZSD,
> +       MDP_STREAM_TYPE_ISP_IP,
> +       MDP_STREAM_TYPE_ISP_VSS,
> +       MDP_STREAM_TYPE_ISP_ZSD_SLOW,
> +       /* MDP_STREAM_TYPE_ISP_ZSD_ONE, */
> +
> +       MDP_STREAM_TYPE_WPE,
> +       MDP_STREAM_TYPE_WPE2,
> +};
> +
> +struct mdp_crop {
> +       struct v4l2_rect        c;
> +       struct v4l2_fract       left_subpix;
> +       struct v4l2_fract       top_subpix;
> +       struct v4l2_fract       width_subpix;
> +       struct v4l2_fract       height_subpix;
> +};
> +
> +struct mdp_frame {
> +       struct v4l2_format      format;
> +       const struct mdp_format *mdp_fmt;
> +       u32                     ycbcr_prof;     /* enum mdp_ycbcr_profile */
> +       u32                     usage;          /* enum mdp_buffer_usage */
> +       struct mdp_crop         crop;
> +       struct v4l2_rect        compose;
> +       s32                     rotation;
> +       u32                     hflip:1;
> +       u32                     vflip:1;
> +       u32                     hdr:1;
> +       u32                     dre:1;
> +       u32                     sharpness:1;
> +       u32                     dither:1;
> +};
> +
> +static inline bool mdp_target_is_crop(u32 target)
> +{
> +       return (target == V4L2_SEL_TGT_CROP) ||
> +               (target == V4L2_SEL_TGT_CROP_DEFAULT) ||
> +               (target == V4L2_SEL_TGT_CROP_BOUNDS);
> +}
> +
> +static inline bool mdp_target_is_compose(u32 target)
> +{
> +       return (target == V4L2_SEL_TGT_COMPOSE) ||
> +               (target == V4L2_SEL_TGT_COMPOSE_DEFAULT) ||
> +               (target == V4L2_SEL_TGT_COMPOSE_BOUNDS);
> +}
> +
> +#define MDP_MAX_CAPTURES       IMG_MAX_HW_OUTPUTS
> +
> +#define MDP_VPU_INIT           BIT(0)
> +#define MDP_M2M_SRC_FMT                BIT(1)
> +#define MDP_M2M_DST_FMT                BIT(2)
> +#define MDP_M2M_CTX_ERROR      BIT(5)
> +
> +struct mdp_frameparam {
> +       struct list_head        list;
> +       /* synchronization protect for m2m context state */
> +       struct mutex            lock;

This lock is only used to protect against concurrent accesses to
state. Maybe rename it to state_lock and document this fact?

> +       u32                     state;
> +       const struct mdp_limit  *limit;
> +       u32                     type;   /* enum mdp_stream_type */
> +       u32                     frame_no;
> +       struct mdp_frame        output;
> +       struct mdp_frame        captures[MDP_MAX_CAPTURES];
> +       u32                     num_captures;
> +       /* __u8                 pq_data[]; */
> +       enum v4l2_colorspace            colorspace;
> +       enum v4l2_ycbcr_encoding        ycbcr_enc;
> +       enum v4l2_xfer_func             xfer_func;
> +       enum v4l2_quantization          quant;
> +};
> +
> +int mdp_enum_fmt_mplane(struct v4l2_fmtdesc *f);
> +const struct mdp_format *mdp_try_fmt_mplane(struct v4l2_format *f,
> +                                           struct mdp_frameparam *param,
> +                                           u32 ctx_id);
> +enum mdp_ycbcr_profile mdp_map_ycbcr_prof_mplane(struct v4l2_format *f,
> +                                                u32 mdp_color);
> +int mdp_try_crop(struct v4l2_rect *r, const struct v4l2_selection *s,
> +                struct mdp_frame *frame, u32 ctx_id);
> +int mdp_check_scaling_ratio(const struct v4l2_rect *crop,
> +                           const struct v4l2_rect *compose, s32 rotation,
> +       const struct mdp_limit *limit);
> +void mdp_set_src_config(struct img_input *in,
> +                       struct mdp_frame *frame, struct vb2_buffer *vb);
> +void mdp_set_dst_config(struct img_output *out,
> +                       struct mdp_frame *frame, struct vb2_buffer *vb);
> +
> +struct mdp_frameparam *mdp_frameparam_init(void);
> +void mdp_frameparam_release(struct mdp_frameparam *param);
> +
> +#endif  /* __MTK_MDP3_REGS_H__ */
> +
> diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-vpu.c b/drivers/media/platform/mtk-mdp3/mtk-mdp3-vpu.c
> new file mode 100644
> index 000000000000..0ad443cb52e1
> --- /dev/null
> +++ b/drivers/media/platform/mtk-mdp3/mtk-mdp3-vpu.c
> @@ -0,0 +1,277 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2018 MediaTek Inc.
> + * Author: Ping-Hsun Wu <ping-hsun.wu@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/remoteproc.h>
> +#include <linux/platform_data/mtk_scp.h>
> +#include "mtk-mdp3-vpu.h"
> +#include "mtk-mdp3-core.h"
> +
> +#define MDP_VPU_MESSAGE_TIMEOUT 500U
> +
> +static inline struct mdp_dev *vpu_to_mdp(struct mdp_vpu_dev *vpu)
> +{
> +       return container_of(vpu, struct mdp_dev, vpu);
> +}
> +
> +static void mdp_vpu_ipi_handle_init_ack(void *data, unsigned int len,
> +                                       void *priv)
> +{
> +       struct mdp_ipi_init_msg *msg = (struct mdp_ipi_init_msg *)data;
> +       struct mdp_vpu_dev *vpu =
> +               (struct mdp_vpu_dev *)(unsigned long)msg->drv_data;
> +
> +       if (!vpu->work_size)
> +               vpu->work_size = msg->work_size;
> +       else
> +               vpu->status = msg->status;

Why is this in an else statement? Shouldn't we record the status in all cases?

> +       complete(&vpu->ipi_acked);
> +}
> +
> +static void mdp_vpu_ipi_handle_deinit_ack(void *data, unsigned int len,
> +                                         void *priv)
> +{
> +       struct mdp_ipi_deinit_msg *msg = (struct mdp_ipi_deinit_msg *)data;
> +       struct mdp_vpu_dev *vpu =
> +               (struct mdp_vpu_dev *)(unsigned long)msg->drv_data;
> +
> +       vpu->status = msg->status;
> +       complete(&vpu->ipi_acked);
> +}
> +
> +static void mdp_vpu_ipi_handle_frame_ack(void *data, unsigned int len,
> +                                        void *priv)
> +{
> +       struct img_ipi_frameparam *param = (struct img_ipi_frameparam *)data;
> +       struct mdp_vpu_ctx *ctx =
> +               (struct mdp_vpu_ctx *)(unsigned long)param->drv_data;
> +
> +       ctx->failure = param->state;

You don't need mdp_vpu_ctx::failure at all as it is only used in this
function. Remove it from struct mdp_vpu_ctx and work directly on
param->state.

> +       if (ctx->failure) {
> +               struct mdp_dev *mdp = vpu_to_mdp(ctx->vpu_dev);
> +
> +               dev_info(&mdp->pdev->dev, "VPU MDP failure:%d\n", ctx->failure);
> +       }
> +       complete(&ctx->vpu_dev->ipi_acked);
> +}
> +
> +int mdp_vpu_register(struct platform_device *pdev)
> +{
> +       int err;
> +
> +       err = scp_ipi_register(pdev, SCP_IPI_MDP_INIT,
> +                              mdp_vpu_ipi_handle_init_ack, NULL);
> +       if (err) {
> +               dev_err(&pdev->dev, "scp_ipi_register failed %d\n", err);
> +               goto err_ipi_init;
> +       }
> +       err = scp_ipi_register(pdev, SCP_IPI_MDP_DEINIT,
> +                              mdp_vpu_ipi_handle_deinit_ack, NULL);
> +       if (err) {
> +               dev_err(&pdev->dev, "scp_ipi_register failed %d\n", err);
> +               goto err_ipi_deinit;
> +       }
> +       err = scp_ipi_register(pdev, SCP_IPI_MDP_FRAME,
> +                              mdp_vpu_ipi_handle_frame_ack, NULL);
> +       if (err) {
> +               dev_err(&pdev->dev, "scp_ipi_register failed %d\n", err);
> +               goto err_ipi_frame;
> +       }
> +       return 0;
> +
> +err_ipi_frame:
> +       /* vpu_ipi_unregister(IPI_MDP_DEINIT); */
> +err_ipi_deinit:
> +       /* vpu_ipi_unregister(IPI_MDP_INIT); */
> +err_ipi_init:
> +
> +       return err;
> +}
> +
> +void mdp_vpu_unregister(struct platform_device *pdev)
> +{
> +       /* vpu_ipi_unregister(IPI_MDP_INIT); */
> +       /* vpu_ipi_unregister(IPI_MDP_DEINIT); */
> +       /* vpu_ipi_unregister(IPI_MDP_FRAME); */

Isn't this necessary if we, say, unregister the driver?

> +}
> +
> +static int mdp_vpu_sendmsg(struct mdp_vpu_dev *vpu, enum scp_ipi_id id,
> +                          void *buf, unsigned int len)
> +{
> +       int ret;
> +
> +       if (!vpu->pdev) {
> +               struct mdp_dev *mdp = vpu_to_mdp(vpu);
> +
> +               dev_dbg(&mdp->pdev->dev, "vpu pdev is NULL");
> +               return -EINVAL;
> +       }
> +       ret = scp_ipi_send(vpu->pdev, id, buf, len, 2000);
> +
> +       if (ret) {
> +               dev_err(&vpu->pdev->dev, "scp_ipi_send failed %d\n", ret);
> +               return -EPERM;
> +       }
> +       ret =
> +       wait_for_completion_timeout(&vpu->ipi_acked,
> +                                   msecs_to_jiffies(MDP_VPU_MESSAGE_TIMEOUT));
> +       if (!ret)
> +               ret = -ETIME;
> +       else if (vpu->status)
> +               ret = -EINVAL;
> +       else
> +               ret = 0;
> +       return ret;
> +}
> +
> +int mdp_vpu_dev_init(struct mdp_vpu_dev *vpu, struct platform_device *pdev,
> +                    struct mutex *lock)
> +{
> +       struct mdp_ipi_init_msg msg = {
> +               .drv_data = (unsigned long)vpu,
> +       };
> +       phys_addr_t mem_size, pool;
> +       const size_t pool_size = sizeof(struct mdp_config_pool);
> +       struct mdp_dev *mdp = vpu_to_mdp(vpu);
> +       int err;
> +
> +       init_completion(&vpu->ipi_acked);
> +       vpu->pdev = pdev;
> +       vpu->lock = lock;

Is there any reason why the lock must come from the outside? I have
the feeling that we could simplify the code a bit if vpu->lock was
just a lock that we initialize here.

> +       vpu->work_size = 0;
> +       err = mdp_vpu_sendmsg(vpu, SCP_IPI_MDP_INIT, &msg, sizeof(msg));
> +       if (err)
> +               goto err_work_size;
> +       /* vpu work_size was set in mdp_vpu_ipi_handle_init_ack */
> +
> +       vpu->work = scp_get_reserve_mem_virt(SCP_MDP_MEM_ID);
> +       vpu->work_addr = scp_get_reserve_mem_phys(SCP_MDP_MEM_ID);
> +       mem_size = scp_get_reserve_mem_size(SCP_MDP_MEM_ID);
> +       pool = ALIGN(vpu->work + vpu->work_size, 8);
> +       if (pool + pool_size - vpu->work > mem_size) {
> +               dev_err(&mdp->pdev->dev,
> +                       "VPU memory insufficient: %lx + %lx > %llx",
> +                       vpu->work_size, pool_size, mem_size);
> +               err = -ENOMEM;
> +               goto err_mem_size;
> +       }
> +
> +       dev_info(&mdp->pdev->dev,
> +                "VPU work:%llx pa:%llx sz:%lx pool:%llx sz:%lx (mem sz:%llx)",
> +               vpu->work, vpu->work_addr, vpu->work_size,
> +               pool, pool_size, mem_size);
> +       vpu->pool = (struct mdp_config_pool *)pool;
> +       msg.work_addr = vpu->work_addr;
> +       msg.work_size = vpu->work_size;
> +       err = mdp_vpu_sendmsg(vpu, SCP_IPI_MDP_INIT, &msg, sizeof(msg));

Why do we need to invoke SCP_IPI_MDP_INIT twice?

> +       if (err)
> +               goto err_work_size;
> +       memset(vpu->pool, 0, sizeof(*vpu->pool));
> +       return 0;
> +
> +err_work_size:
> +       switch (vpu->status) {
> +       case -MDP_IPI_EBUSY:
> +               err = -EBUSY;
> +               break;
> +       case -MDP_IPI_ENOMEM:
> +               err = -ENOSPC;  /* -ENOMEM */
> +               break;

What if vpu->status is anything else? We need to manage default to set
err to a valid error state, otherwise we will return an undefined
value (likely 0 even though an error occured).

> +       }
> +err_mem_size:
> +       return err;
> +}
> +
> +int mdp_vpu_dev_deinit(struct mdp_vpu_dev *vpu)
> +{
> +       struct mdp_ipi_deinit_msg msg = {
> +               .drv_data = (unsigned long)vpu,
> +               .work_addr = vpu->work_addr,
> +       };
> +
> +       return mdp_vpu_sendmsg(vpu, SCP_IPI_MDP_DEINIT, &msg, sizeof(msg));
> +}
> +
> +static struct img_config *mdp_config_get(struct mdp_vpu_dev *vpu,
> +                                        enum mdp_config_id id, uint32_t *addr)
> +{
> +       struct img_config *config;
> +
> +       if (id < 0 || id >= MDP_CONFIG_POOL_SIZE)
> +               return ERR_PTR(-EINVAL);
> +       if (vpu->lock)

We always call mdp_vpu_dev_init with a lock, so we can drop this conditional.

> +               mutex_lock(vpu->lock);
> +       vpu->pool->cfg_count[id]++;
> +       config = &vpu->pool->configs[id];
> +       *addr = vpu->work_addr + ((unsigned long)config - vpu->work);
> +       if (vpu->lock)
> +               mutex_unlock(vpu->lock);
> +       return config;
> +}
> +
> +static int mdp_config_put(struct mdp_vpu_dev *vpu,
> +                         enum mdp_config_id id,
> +                         const struct img_config *config)
> +{
> +       int err = 0;
> +
> +       if (id < 0 || id >= MDP_CONFIG_POOL_SIZE)
> +               return -EINVAL;
> +       if (vpu->lock)
> +               mutex_lock(vpu->lock);
> +       if (!vpu->pool->cfg_count[id] || config != &vpu->pool->configs[id])
> +               err = -EINVAL;
> +       else
> +               vpu->pool->cfg_count[id]--;
> +       if (vpu->lock)
> +               mutex_unlock(vpu->lock);
> +       return err;
> +}
> +
> +int mdp_vpu_ctx_init(struct mdp_vpu_ctx *ctx, struct mdp_vpu_dev *vpu,
> +                    enum mdp_config_id id)
> +{
> +       ctx->config = mdp_config_get(vpu, id, &ctx->inst_addr);
> +       if (IS_ERR(ctx->config)) {
> +               int err = PTR_ERR(ctx->config);
> +
> +               ctx->config = NULL;
> +               return err;
> +       }
> +       ctx->config_id = id;
> +       ctx->vpu_dev = vpu;
> +       return 0;
> +}
> +
> +int mdp_vpu_ctx_deinit(struct mdp_vpu_ctx *ctx)
> +{
> +       int err = mdp_config_put(ctx->vpu_dev, ctx->config_id, ctx->config);
> +
> +       ctx->config_id = 0;
> +       ctx->config = NULL;
> +       ctx->inst_addr = 0;
> +       return err;
> +}
> +
> +int mdp_vpu_process(struct mdp_vpu_ctx *ctx, struct img_ipi_frameparam *param)
> +{
> +       memset((void *)ctx->vpu_dev->work, 0, ctx->vpu_dev->work_size);
> +       memset(ctx->config, 0, sizeof(*ctx->config));
> +       param->config_data.va = (unsigned long)ctx->config;
> +       param->config_data.pa = ctx->inst_addr;
> +       param->drv_data = (unsigned long)ctx;
> +       return mdp_vpu_sendmsg(ctx->vpu_dev, SCP_IPI_MDP_FRAME,
> +               param, sizeof(*param));
> +}
> +
> diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-vpu.h b/drivers/media/platform/mtk-mdp3/mtk-mdp3-vpu.h
> new file mode 100644
> index 000000000000..09b5b0026e4b
> --- /dev/null
> +++ b/drivers/media/platform/mtk-mdp3/mtk-mdp3-vpu.h
> @@ -0,0 +1,90 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2018 MediaTek Inc.
> + * Author: Ping-Hsun Wu <ping-hsun.wu@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_MDP3_VPU_H__
> +#define __MTK_MDP3_VPU_H__
> +
> +#include <linux/platform_device.h>
> +#include "mtk-img-ipi.h"
> +
> +enum mdp_ipi_result {
> +       MDP_IPI_SUCCESS = 0,
> +       MDP_IPI_ENOMEM  = 12,
> +       MDP_IPI_EBUSY   = 16,
> +       MDP_IPI_EINVAL  = 22,
> +       MDP_IPI_EMINST  = 24,
> +       MDP_IPI_ERANGE  = 34,
> +       MDP_IPI_NR_ERRNO,
> +
> +       MDP_IPI_EOTHER  = MDP_IPI_NR_ERRNO,
> +       MDP_IPI_PATH_CANT_MERGE,
> +       MDP_IPI_OP_FAIL,
> +};
> +
> +struct mdp_ipi_init_msg {
> +       u32     status;
> +       u64     drv_data;
> +       u32     work_addr;      /* [in] working buffer address */
> +       u32     work_size;      /* [in] working buffer size */
> +} __attribute__ ((__packed__));
> +
> +struct mdp_ipi_deinit_msg {
> +       u32     status;
> +       u64     drv_data;
> +       u32     work_addr;
> +} __attribute__ ((__packed__));
> +
> +enum mdp_config_id {
> +       MDP_DEV_M2M = 0,
> +       MDP_CONFIG_POOL_SIZE    /* ALWAYS keep at the end */
> +};
> +
> +struct mdp_config_pool {
> +       u64             cfg_count[MDP_CONFIG_POOL_SIZE];
> +       struct img_config       configs[MDP_CONFIG_POOL_SIZE];
> +};
> +
> +struct mdp_vpu_dev {
> +       /* synchronization protect for image configuration reference count */
> +       struct mutex            *lock;
> +       struct platform_device  *pdev;
> +       struct completion       ipi_acked;
> +       phys_addr_t             work;
> +       phys_addr_t             work_addr;
> +       size_t                  work_size;
> +       struct mdp_config_pool  *pool;
> +       u32             status;
> +};
> +
> +struct mdp_vpu_ctx {
> +       struct mdp_vpu_dev      *vpu_dev;
> +       u32             config_id;
> +       struct img_config       *config;
> +       u32             inst_addr;
> +       s32                     failure;

This member is only used in mdp_vpu_ipi_handle_frame_ack(). Therefore
is can be replaced by a local variable and does not need to be defined
in this struct.

> +};
> +
> +int mdp_vpu_register(struct platform_device *pdev);
> +void mdp_vpu_unregister(struct platform_device *pdev);
> +int mdp_vpu_dev_init(struct mdp_vpu_dev *vpu, struct platform_device *pdev,
> +                    struct mutex *lock);
> +int mdp_vpu_dev_deinit(struct mdp_vpu_dev *vpu);
> +int mdp_vpu_ctx_init(struct mdp_vpu_ctx *ctx, struct mdp_vpu_dev *vpu,
> +                    enum mdp_config_id id);
> +int mdp_vpu_ctx_deinit(struct mdp_vpu_ctx *ctx);
> +int mdp_vpu_process(struct mdp_vpu_ctx *vpu, struct img_ipi_frameparam *param);
> +
> +#endif  /* __MTK_MDP3_VPU_H__ */
> +
> --
> 2.18.0

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC v2 4/4] media: platform: mtk-mdp3: Add Mediatek MDP3 driver
  2019-06-04 11:20   ` Tomasz Figa
  2019-06-11  9:20     ` Daoyuan Huang
@ 2019-06-20  4:48     ` Alexandre Courbot
  2019-06-26  4:41       ` Tomasz Figa
  1 sibling, 1 reply; 12+ messages in thread
From: Alexandre Courbot @ 2019-06-20  4:48 UTC (permalink / raw)
  To: Tomasz Figa
  Cc: Daoyuan Huang, Hans Verkuil, Laurent Pinchart, matthias.bgg,
	Mauro Carvalho Chehab, yuzhao, zwisler, linux-mediatek,
	linux-arm-kernel, Sean.Cheng, Sj Huang, christie.yu,
	holmes.chiou, frederic.chen, Jerry-ch.Chen, jungo.lin, Rynn.Wu,
	Linux Media Mailing List, srv_heupstream, devicetree,
	Ping-Hsun Wu

On Tue, Jun 4, 2019 at 8:20 PM Tomasz Figa <tfiga@chromium.org> wrote:
> > +
> > +     ret = mdp_vpu_get_locked(mdp);
> > +     if (ret < 0)
> > +             goto err_load_vpu;
>
> This shouldn't happen in open(), but rather the latest possible point in
> time. If one needs to keep the VPU running for the time of streaming, then
> it should be start_streaming. If one can safely turn the VPU off if there is
> no frame queued for long time, it should be just in m2m job_run.
>
> Generally the userspace should be able to
> just open an m2m device for querying it, without any side effects like
> actually powering on the hardware or grabbing a hardware instance (which
> could block some other processes, trying to grab one too).

OTOH looking at the code of mdp_vpu_get_locked(), we do the whole
rproc_boot and VPU init procedure if we were the only user. So I can
understand we want to avoid doing this too often.

Maybe mdp_vpu_get_locked() can be reorganized in a better way. I feel
like the call to mdp_vpu_register() should be done in probe, and maybe
we can use runtime PM (with a reasonable timeout) to control the rproc
and VPU init?


>
> > +
> > +     mutex_unlock(&mdp->m2m_lock);
> > +
> > +     mdp_dbg(0, "%s [%d]", dev_name(&mdp->pdev->dev), ctx->id);
> > +
> > +     return 0;
> > +
> > +err_load_vpu:
> > +     mdp_frameparam_release(ctx->curr_param);
> > +err_param_init:
> > +     v4l2_m2m_ctx_release(ctx->m2m_ctx);
> > +err_m2m_ctx:
> > +     v4l2_ctrl_handler_free(&ctx->ctrl_handler);
> > +     v4l2_fh_del(&ctx->fh);
> > +err_ctrls_create:
> > +     v4l2_fh_exit(&ctx->fh);
> > +     mutex_unlock(&mdp->m2m_lock);
> > +err_lock:
>
> Incorrect naming of all the error labels here.
>
> > +     kfree(ctx);
> > +
> > +     return ret;
> > +}
> [snip]
> > +enum mdp_ycbcr_profile mdp_map_ycbcr_prof_mplane(struct v4l2_format *f,
> > +                                              u32 mdp_color)
> > +{
> > +     struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
> > +
> > +     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;
>
> Putting this under the default clause of the switch statement would be
> cleaner and the comment wouldn't be needed.
>
> [snip]
> > +struct mdp_frameparam *mdp_frameparam_init(void)
> > +{
> > +     struct mdp_frameparam *param;
> > +     struct mdp_frame *frame;
> > +
> > +     param = kzalloc(sizeof(*param), GFP_KERNEL);
> > +     if (!param)
> > +             return ERR_PTR(-ENOMEM);
>
> We could just embed mdp_frameparam into the mdp_m2m_ctx struct and then
> wouldn't need any dynamic allocation here anymore. And as a side effect, the
> function could be just made void, because it couldn't fail.
>
> > +
> > +     INIT_LIST_HEAD(&param->list);
> > +     mutex_init(&param->lock);
> > +     param->state = 0;
> > +     param->limit = &mdp_def_limit;
> > +     param->type = MDP_STREAM_TYPE_UNKNOWN;
>
> We always seem to use MDP_STREAM_TYPE_BITBLT in this driver.
>
> > +     param->frame_no = 0;
>
> No need for explicit initialization to 0.
>
> Best regards,
> Tomasz
>

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC v2 4/4] media: platform: mtk-mdp3: Add Mediatek MDP3 driver
  2019-06-20  4:48     ` Alexandre Courbot
@ 2019-06-26  4:41       ` Tomasz Figa
  0 siblings, 0 replies; 12+ messages in thread
From: Tomasz Figa @ 2019-06-26  4:41 UTC (permalink / raw)
  To: Alexandre Courbot
  Cc: Daoyuan Huang, Hans Verkuil, Laurent Pinchart, Matthias Brugger,
	Mauro Carvalho Chehab, Yu Zhao, Ross Zwisler,
	moderated list:ARM/Mediatek SoC support,
	list@263.net:IOMMU DRIVERS
	<iommu@lists.linux-foundation.org>,
	Joerg Roedel <joro@8bytes.org>,,
	Sean Cheng (鄭昇弘),
	Sj Huang, Christie Yu (游雅惠),
	Holmes Chiou (邱挺),
	Frederic Chen, Jerry-ch Chen,
	Jungo Lin (林明俊),
	Rynn Wu (吳育恩),
	Linux Media Mailing List, srv_heupstream, devicetree,
	Ping-Hsun Wu

On Thu, Jun 20, 2019 at 1:48 PM Alexandre Courbot <acourbot@chromium.org> wrote:
>
> On Tue, Jun 4, 2019 at 8:20 PM Tomasz Figa <tfiga@chromium.org> wrote:
> > > +
> > > +     ret = mdp_vpu_get_locked(mdp);
> > > +     if (ret < 0)
> > > +             goto err_load_vpu;
> >
> > This shouldn't happen in open(), but rather the latest possible point in
> > time. If one needs to keep the VPU running for the time of streaming, then
> > it should be start_streaming. If one can safely turn the VPU off if there is
> > no frame queued for long time, it should be just in m2m job_run.
> >
> > Generally the userspace should be able to
> > just open an m2m device for querying it, without any side effects like
> > actually powering on the hardware or grabbing a hardware instance (which
> > could block some other processes, trying to grab one too).
>
> OTOH looking at the code of mdp_vpu_get_locked(), we do the whole
> rproc_boot and VPU init procedure if we were the only user. So I can
> understand we want to avoid doing this too often.
>
> Maybe mdp_vpu_get_locked() can be reorganized in a better way. I feel
> like the call to mdp_vpu_register() should be done in probe, and maybe
> we can use runtime PM (with a reasonable timeout) to control the rproc
> and VPU init?

I think it depends on when exactly the rproc and VPU need stay
initialized. In general, we want to turn off as much as possible as
quickly as possible, but keeping in mind any turn on latencies.

For example. if it takes 10 ms to boot rproc/VPU, we probably
shouldn't turn it off unless we already spent 20-30 ms idling, which
could be handled with runtime PM with (delayed) autosuspend. However,
things like clock gating are normally very fast, so we could just stop
any clocks as soon as frame processing ends and restart when next
frame is getting scheduled and if we use autosuspend, we wouldn't be
able to do it using PM runtime.

My point was that just open() is not the right place for doing this.
Any later stage should be okay, as long as it suits the hardware
architecture.

Best regards,
Tomasz

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2019-06-26  4:41 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-05-16  3:23 [RFC v2 0/4] media: mediatek: support mdp3 on mt8183 platform Daoyuan Huang
2019-05-16  3:23 ` [RFC v2 1/4] dt-binding: mt8183: Add Mediatek MDP3 dt-bindings Daoyuan Huang
2019-06-13 21:25   ` Rob Herring
2019-05-16  3:23 ` [RFC v2 2/4] dts: arm64: mt8183: Add Mediatek MDP3 nodes Daoyuan Huang
2019-05-16  3:23 ` [RFC v2 3/4] media: platform: Add Mediatek MDP3 driver KConfig Daoyuan Huang
2019-05-16  3:23 ` [RFC v2 4/4] media: platform: mtk-mdp3: Add Mediatek MDP3 driver Daoyuan Huang
2019-06-04 11:20   ` Tomasz Figa
2019-06-11  9:20     ` Daoyuan Huang
2019-06-11 10:11       ` Tomasz Figa
2019-06-20  4:48     ` Alexandre Courbot
2019-06-26  4:41       ` Tomasz Figa
2019-06-20  4:47   ` Alexandre Courbot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).