devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v16 0/8] MT8173 DRM support
@ 2016-05-06 13:05 Philipp Zabel
  2016-05-06 13:05 ` [PATCH v16 1/8] memory: mtk-smi: export mtk_smi_larb_get/put Philipp Zabel
                   ` (7 more replies)
  0 siblings, 8 replies; 11+ messages in thread
From: Philipp Zabel @ 2016-05-06 13:05 UTC (permalink / raw)
  To: dri-devel
  Cc: Mark Rutland, Stephen Boyd, Michael Turquette, Jie Qiu,
	Cawa Cheng, YT Shen, Yingjoe Chen, devicetree, Jitao Shi, kernel,
	Pawel Moll, Ian Campbell, Rob Herring, linux-mediatek,
	Matthias Brugger, Paul Bolle, Emil Velikov, Tomasz Figa,
	Kumar Gala

Rebased onto drm-next, replaced drm_send_vblank_event() with
drm_crtc_send_vblank_event() and reordered a bit: the platform driver
list alphabetically, and the mtk_smi_larb_get/put export patch first.

regards
Philipp

Changes since v15:
 - Export mtk_smi_larb_get/put before using it
 - Use drm_crtc_send_vblank_event() instead of drm_send_vblank_event()
 - Order mtk_drm_drivers alphabetically

CK Hu (3):
  dt-bindings: drm/mediatek: Add Mediatek display subsystem dts binding
  drm/mediatek: Add DRM Driver for Mediatek SoC MT8173.
  drm/mediatek: Add DSI sub driver

Jie Qiu (1):
  drm/mediatek: Add DPI sub driver

Philipp Zabel (4):
  memory: mtk-smi: export mtk_smi_larb_get/put
  clk: mediatek: make dpi0_sel propagate rate changes
  clk: mediatek: Add hdmi_ref HDMI PHY PLL reference clock output
  clk: mediatek: remove hdmitx_dig_cts from TOP clocks

 .../bindings/display/mediatek/mediatek,disp.txt    | 203 +++++
 .../bindings/display/mediatek/mediatek,dpi.txt     |  35 +
 .../bindings/display/mediatek/mediatek,dsi.txt     |  60 ++
 drivers/clk/mediatek/clk-mt8173.c                  |  12 +-
 drivers/clk/mediatek/clk-mtk.h                     |  15 +-
 drivers/gpu/drm/Kconfig                            |   2 +
 drivers/gpu/drm/Makefile                           |   1 +
 drivers/gpu/drm/mediatek/Kconfig                   |  16 +
 drivers/gpu/drm/mediatek/Makefile                  |  14 +
 drivers/gpu/drm/mediatek/mtk_disp_ovl.c            | 302 +++++++
 drivers/gpu/drm/mediatek/mtk_disp_rdma.c           | 240 ++++++
 drivers/gpu/drm/mediatek/mtk_dpi.c                 | 769 +++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_dpi_regs.h            | 228 +++++
 drivers/gpu/drm/mediatek/mtk_drm_crtc.c            | 582 +++++++++++++
 drivers/gpu/drm/mediatek/mtk_drm_crtc.h            |  32 +
 drivers/gpu/drm/mediatek/mtk_drm_ddp.c             | 353 ++++++++
 drivers/gpu/drm/mediatek/mtk_drm_ddp.h             |  41 +
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c        | 225 +++++
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h        | 150 ++++
 drivers/gpu/drm/mediatek/mtk_drm_drv.c             | 561 +++++++++++++
 drivers/gpu/drm/mediatek/mtk_drm_drv.h             |  60 ++
 drivers/gpu/drm/mediatek/mtk_drm_fb.c              | 165 ++++
 drivers/gpu/drm/mediatek/mtk_drm_fb.h              |  23 +
 drivers/gpu/drm/mediatek/mtk_drm_gem.c             | 269 ++++++
 drivers/gpu/drm/mediatek/mtk_drm_gem.h             |  59 ++
 drivers/gpu/drm/mediatek/mtk_drm_plane.c           | 240 ++++++
 drivers/gpu/drm/mediatek/mtk_drm_plane.h           |  59 ++
 drivers/gpu/drm/mediatek/mtk_dsi.c                 | 927 +++++++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_mipi_tx.c             | 463 ++++++++++
 drivers/memory/mtk-smi.c                           |   2 +
 include/dt-bindings/clock/mt8173-clk.h             |   3 +-
 31 files changed, 6106 insertions(+), 5 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/display/mediatek/mediatek,disp.txt
 create mode 100644 Documentation/devicetree/bindings/display/mediatek/mediatek,dpi.txt
 create mode 100644 Documentation/devicetree/bindings/display/mediatek/mediatek,dsi.txt
 create mode 100644 drivers/gpu/drm/mediatek/Kconfig
 create mode 100644 drivers/gpu/drm/mediatek/Makefile
 create mode 100644 drivers/gpu/drm/mediatek/mtk_disp_ovl.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_disp_rdma.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_dpi.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_dpi_regs.h
 create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_crtc.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_crtc.h
 create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_ddp.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_ddp.h
 create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
 create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_drv.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_drv.h
 create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_fb.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_fb.h
 create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_gem.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_gem.h
 create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_plane.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_plane.h
 create mode 100644 drivers/gpu/drm/mediatek/mtk_dsi.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_mipi_tx.c

-- 
2.8.0.rc3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCH v16 1/8] memory: mtk-smi: export mtk_smi_larb_get/put
  2016-05-06 13:05 [PATCH v16 0/8] MT8173 DRM support Philipp Zabel
@ 2016-05-06 13:05 ` Philipp Zabel
  2016-05-06 13:05 ` [PATCH v16 2/8] dt-bindings: drm/mediatek: Add Mediatek display subsystem dts binding Philipp Zabel
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: Philipp Zabel @ 2016-05-06 13:05 UTC (permalink / raw)
  To: dri-devel
  Cc: Mark Rutland, Stephen Boyd, Michael Turquette, Jie Qiu,
	Cawa Cheng, YT Shen, Yingjoe Chen, devicetree, Jitao Shi, kernel,
	Pawel Moll, Ian Campbell, Rob Herring, linux-mediatek,
	Matthias Brugger, Paul Bolle, Emil Velikov, Tomasz Figa,
	Kumar Gala

To allow building mediatek-drm.ko as a module, the
mtk_smi_larb_get and mtk_smi_larb_put symbols have
to be exported.

Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
---
 drivers/memory/mtk-smi.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/memory/mtk-smi.c b/drivers/memory/mtk-smi.c
index 089091f..f6b5757 100644
--- a/drivers/memory/mtk-smi.c
+++ b/drivers/memory/mtk-smi.c
@@ -91,6 +91,7 @@ int mtk_smi_larb_get(struct device *larbdev)
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(mtk_smi_larb_get);
 
 void mtk_smi_larb_put(struct device *larbdev)
 {
@@ -106,6 +107,7 @@ void mtk_smi_larb_put(struct device *larbdev)
 	mtk_smi_disable(&larb->smi);
 	mtk_smi_disable(common);
 }
+EXPORT_SYMBOL_GPL(mtk_smi_larb_put);
 
 static int
 mtk_smi_larb_bind(struct device *dev, struct device *master, void *data)
-- 
2.8.0.rc3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCH v16 2/8] dt-bindings: drm/mediatek: Add Mediatek display subsystem dts binding
  2016-05-06 13:05 [PATCH v16 0/8] MT8173 DRM support Philipp Zabel
  2016-05-06 13:05 ` [PATCH v16 1/8] memory: mtk-smi: export mtk_smi_larb_get/put Philipp Zabel
@ 2016-05-06 13:05 ` Philipp Zabel
  2016-05-06 13:05 ` [PATCH v16 3/8] drm/mediatek: Add DRM Driver for Mediatek SoC MT8173 Philipp Zabel
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: Philipp Zabel @ 2016-05-06 13:05 UTC (permalink / raw)
  To: dri-devel
  Cc: Mark Rutland, Stephen Boyd, Michael Turquette, Jie Qiu,
	Cawa Cheng, YT Shen, Yingjoe Chen, devicetree, Jitao Shi, kernel,
	Pawel Moll, Ian Campbell, Rob Herring, linux-mediatek,
	Matthias Brugger, Paul Bolle, Emil Velikov, Tomasz Figa,
	Kumar Gala

From: CK Hu <ck.hu@mediatek.com>

Add device tree binding documentation for the display subsystem in
Mediatek MT8173 SoCs.

Signed-off-by: CK Hu <ck.hu@mediatek.com>
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
Acked-by: Rob Herring <robh@kernel.org>
---
 .../bindings/display/mediatek/mediatek,disp.txt    | 203 +++++++++++++++++++++
 .../bindings/display/mediatek/mediatek,dpi.txt     |  35 ++++
 .../bindings/display/mediatek/mediatek,dsi.txt     |  60 ++++++
 3 files changed, 298 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/mediatek/mediatek,disp.txt
 create mode 100644 Documentation/devicetree/bindings/display/mediatek/mediatek,dpi.txt
 create mode 100644 Documentation/devicetree/bindings/display/mediatek/mediatek,dsi.txt

diff --git a/Documentation/devicetree/bindings/display/mediatek/mediatek,disp.txt b/Documentation/devicetree/bindings/display/mediatek/mediatek,disp.txt
new file mode 100644
index 0000000..db6e77e
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/mediatek/mediatek,disp.txt
@@ -0,0 +1,203 @@
+Mediatek display subsystem
+==========================
+
+The Mediatek display subsystem consists of various DISP function blocks in the
+MMSYS register space. The connections between them can be configured by output
+and input selectors in the MMSYS_CONFIG register space. Pixel clock and start
+of frame signal are distributed to the other function blocks by a DISP_MUTEX
+function block.
+
+All DISP device tree nodes must be siblings to the central MMSYS_CONFIG node.
+For a description of the MMSYS_CONFIG binding, see
+Documentation/devicetree/bindings/arm/mediatek/mediatek,mmsys.txt.
+
+DISP function blocks
+====================
+
+A display stream starts at a source function block that reads pixel data from
+memory and ends with a sink function block that drives pixels on a display
+interface, or writes pixels back to memory. All DISP function blocks have
+their own register space, interrupt, and clock gate. The blocks that can
+access memory additionally have to list the IOMMU and local arbiter they are
+connected to.
+
+For a description of the display interface sink function blocks, see
+Documentation/devicetree/bindings/display/mediatek/mediatek,dsi.txt and
+Documentation/devicetree/bindings/display/mediatek/mediatek,dpi.txt.
+
+Required properties (all function blocks):
+- compatible: "mediatek,<chip>-disp-<function>", one of
+	"mediatek,<chip>-disp-ovl"   - overlay (4 layers, blending, csc)
+	"mediatek,<chip>-disp-rdma"  - read DMA / line buffer
+	"mediatek,<chip>-disp-wdma"  - write DMA
+	"mediatek,<chip>-disp-color" - color processor
+	"mediatek,<chip>-disp-aal"   - adaptive ambient light controller
+	"mediatek,<chip>-disp-gamma" - gamma correction
+	"mediatek,<chip>-disp-merge" - merge streams from two RDMA sources
+	"mediatek,<chip>-disp-split" - split stream to two encoders
+	"mediatek,<chip>-disp-ufoe"  - data compression engine
+	"mediatek,<chip>-dsi"        - DSI controller, see mediatek,dsi.txt
+	"mediatek,<chip>-dpi"        - DPI controller, see mediatek,dpi.txt
+	"mediatek,<chip>-disp-mutex" - display mutex
+	"mediatek,<chip>-disp-od"    - overdrive
+- reg: Physical base address and length of the function block register space
+- interrupts: The interrupt signal from the function block (required, except for
+  merge and split function blocks).
+- clocks: device clocks
+  See Documentation/devicetree/bindings/clock/clock-bindings.txt for details.
+  For most function blocks this is just a single clock input. Only the DSI and
+  DPI controller nodes have multiple clock inputs. These are documented in
+  mediatek,dsi.txt and mediatek,dpi.txt, respectively.
+
+Required properties (DMA function blocks):
+- compatible: Should be one of
+	"mediatek,<chip>-disp-ovl"
+	"mediatek,<chip>-disp-rdma"
+	"mediatek,<chip>-disp-wdma"
+- larb: Should contain a phandle pointing to the local arbiter device as defined
+  in Documentation/devicetree/bindings/soc/mediatek/mediatek,smi-larb.txt
+- iommus: Should point to the respective IOMMU block with master port as
+  argument, see Documentation/devicetree/bindings/iommu/mediatek,iommu.txt
+  for details.
+
+Examples:
+
+mmsys: clock-controller@14000000 {
+	compatible = "mediatek,mt8173-mmsys", "syscon";
+	reg = <0 0x14000000 0 0x1000>;
+	power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+	#clock-cells = <1>;
+};
+
+ovl0: ovl@1400c000 {
+	compatible = "mediatek,mt8173-disp-ovl";
+	reg = <0 0x1400c000 0 0x1000>;
+	interrupts = <GIC_SPI 180 IRQ_TYPE_LEVEL_LOW>;
+	power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+	clocks = <&mmsys CLK_MM_DISP_OVL0>;
+	iommus = <&iommu M4U_PORT_DISP_OVL0>;
+	mediatek,larb = <&larb0>;
+};
+
+ovl1: ovl@1400d000 {
+	compatible = "mediatek,mt8173-disp-ovl";
+	reg = <0 0x1400d000 0 0x1000>;
+	interrupts = <GIC_SPI 181 IRQ_TYPE_LEVEL_LOW>;
+	power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+	clocks = <&mmsys CLK_MM_DISP_OVL1>;
+	iommus = <&iommu M4U_PORT_DISP_OVL1>;
+	mediatek,larb = <&larb4>;
+};
+
+rdma0: rdma@1400e000 {
+	compatible = "mediatek,mt8173-disp-rdma";
+	reg = <0 0x1400e000 0 0x1000>;
+	interrupts = <GIC_SPI 182 IRQ_TYPE_LEVEL_LOW>;
+	power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+	clocks = <&mmsys CLK_MM_DISP_RDMA0>;
+	iommus = <&iommu M4U_PORT_DISP_RDMA0>;
+	mediatek,larb = <&larb0>;
+};
+
+rdma1: rdma@1400f000 {
+	compatible = "mediatek,mt8173-disp-rdma";
+	reg = <0 0x1400f000 0 0x1000>;
+	interrupts = <GIC_SPI 183 IRQ_TYPE_LEVEL_LOW>;
+	power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+	clocks = <&mmsys CLK_MM_DISP_RDMA1>;
+	iommus = <&iommu M4U_PORT_DISP_RDMA1>;
+	mediatek,larb = <&larb4>;
+};
+
+rdma2: rdma@14010000 {
+	compatible = "mediatek,mt8173-disp-rdma";
+	reg = <0 0x14010000 0 0x1000>;
+	interrupts = <GIC_SPI 184 IRQ_TYPE_LEVEL_LOW>;
+	power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+	clocks = <&mmsys CLK_MM_DISP_RDMA2>;
+	iommus = <&iommu M4U_PORT_DISP_RDMA2>;
+	mediatek,larb = <&larb4>;
+};
+
+wdma0: wdma@14011000 {
+	compatible = "mediatek,mt8173-disp-wdma";
+	reg = <0 0x14011000 0 0x1000>;
+	interrupts = <GIC_SPI 185 IRQ_TYPE_LEVEL_LOW>;
+	power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+	clocks = <&mmsys CLK_MM_DISP_WDMA0>;
+	iommus = <&iommu M4U_PORT_DISP_WDMA0>;
+	mediatek,larb = <&larb0>;
+};
+
+wdma1: wdma@14012000 {
+	compatible = "mediatek,mt8173-disp-wdma";
+	reg = <0 0x14012000 0 0x1000>;
+	interrupts = <GIC_SPI 186 IRQ_TYPE_LEVEL_LOW>;
+	power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+	clocks = <&mmsys CLK_MM_DISP_WDMA1>;
+	iommus = <&iommu M4U_PORT_DISP_WDMA1>;
+	mediatek,larb = <&larb4>;
+};
+
+color0: color@14013000 {
+	compatible = "mediatek,mt8173-disp-color";
+	reg = <0 0x14013000 0 0x1000>;
+	interrupts = <GIC_SPI 187 IRQ_TYPE_LEVEL_LOW>;
+	power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+	clocks = <&mmsys CLK_MM_DISP_COLOR0>;
+};
+
+color1: color@14014000 {
+	compatible = "mediatek,mt8173-disp-color";
+	reg = <0 0x14014000 0 0x1000>;
+	interrupts = <GIC_SPI 188 IRQ_TYPE_LEVEL_LOW>;
+	power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+	clocks = <&mmsys CLK_MM_DISP_COLOR1>;
+};
+
+aal@14015000 {
+	compatible = "mediatek,mt8173-disp-aal";
+	reg = <0 0x14015000 0 0x1000>;
+	interrupts = <GIC_SPI 189 IRQ_TYPE_LEVEL_LOW>;
+	power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+	clocks = <&mmsys CLK_MM_DISP_AAL>;
+};
+
+gamma@14016000 {
+	compatible = "mediatek,mt8173-disp-gamma";
+	reg = <0 0x14016000 0 0x1000>;
+	interrupts = <GIC_SPI 190 IRQ_TYPE_LEVEL_LOW>;
+	power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+	clocks = <&mmsys CLK_MM_DISP_GAMMA>;
+};
+
+ufoe@1401a000 {
+	compatible = "mediatek,mt8173-disp-ufoe";
+	reg = <0 0x1401a000 0 0x1000>;
+	interrupts = <GIC_SPI 191 IRQ_TYPE_LEVEL_LOW>;
+	power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+	clocks = <&mmsys CLK_MM_DISP_UFOE>;
+};
+
+dsi0: dsi@1401b000 {
+	/* See mediatek,dsi.txt for details */
+};
+
+dpi0: dpi@1401d000 {
+	/* See mediatek,dpi.txt for details */
+};
+
+mutex: mutex@14020000 {
+	compatible = "mediatek,mt8173-disp-mutex";
+	reg = <0 0x14020000 0 0x1000>;
+	interrupts = <GIC_SPI 169 IRQ_TYPE_LEVEL_LOW>;
+	power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+	clocks = <&mmsys CLK_MM_MUTEX_32K>;
+};
+
+od@14023000 {
+	compatible = "mediatek,mt8173-disp-od";
+	reg = <0 0x14023000 0 0x1000>;
+	power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+	clocks = <&mmsys CLK_MM_DISP_OD>;
+};
diff --git a/Documentation/devicetree/bindings/display/mediatek/mediatek,dpi.txt b/Documentation/devicetree/bindings/display/mediatek/mediatek,dpi.txt
new file mode 100644
index 0000000..b6a7e73
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/mediatek/mediatek,dpi.txt
@@ -0,0 +1,35 @@
+Mediatek DPI Device
+===================
+
+The Mediatek DPI function block is a sink of the display subsystem and
+provides 8-bit RGB/YUV444 or 8/10/10-bit YUV422 pixel data on a parallel
+output bus.
+
+Required properties:
+- compatible: "mediatek,<chip>-dpi"
+- reg: Physical base address and length of the controller's registers
+- interrupts: The interrupt signal from the function block.
+- clocks: device clocks
+  See Documentation/devicetree/bindings/clock/clock-bindings.txt for details.
+- clock-names: must contain "pixel", "engine", and "pll"
+- port: Output port node with endpoint definitions as described in
+  Documentation/devicetree/bindings/graph.txt. This port should be connected
+  to the input port of an attached HDMI or LVDS encoder chip.
+
+Example:
+
+dpi0: dpi@1401d000 {
+	compatible = "mediatek,mt8173-dpi";
+	reg = <0 0x1401d000 0 0x1000>;
+	interrupts = <GIC_SPI 194 IRQ_TYPE_LEVEL_LOW>;
+	clocks = <&mmsys CLK_MM_DPI_PIXEL>,
+		 <&mmsys CLK_MM_DPI_ENGINE>,
+		 <&apmixedsys CLK_APMIXED_TVDPLL>;
+	clock-names = "pixel", "engine", "pll";
+
+	port {
+		dpi0_out: endpoint {
+			remote-endpoint = <&hdmi0_in>;
+		};
+	};
+};
diff --git a/Documentation/devicetree/bindings/display/mediatek/mediatek,dsi.txt b/Documentation/devicetree/bindings/display/mediatek/mediatek,dsi.txt
new file mode 100644
index 0000000..2b1585a
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/mediatek/mediatek,dsi.txt
@@ -0,0 +1,60 @@
+Mediatek DSI Device
+===================
+
+The Mediatek DSI function block is a sink of the display subsystem and can
+drive up to 4-lane MIPI DSI output. Two DSIs can be synchronized for dual-
+channel output.
+
+Required properties:
+- compatible: "mediatek,<chip>-dsi"
+- reg: Physical base address and length of the controller's registers
+- interrupts: The interrupt signal from the function block.
+- clocks: device clocks
+  See Documentation/devicetree/bindings/clock/clock-bindings.txt for details.
+- clock-names: must contain "engine", "digital", and "hs"
+- phys: phandle link to the MIPI D-PHY controller.
+- phy-names: must contain "dphy"
+- port: Output port node with endpoint definitions as described in
+  Documentation/devicetree/bindings/graph.txt. This port should be connected
+  to the input port of an attached DSI panel or DSI-to-eDP encoder chip.
+
+MIPI TX Configuration Module
+============================
+
+The MIPI TX configuration module controls the MIPI D-PHY.
+
+Required properties:
+- compatible: "mediatek,<chip>-mipi-tx"
+- reg: Physical base address and length of the controller's registers
+- clocks: PLL reference clock
+- clock-output-names: name of the output clock line to the DSI encoder
+- #clock-cells: must be <0>;
+- #phy-cells: must be <0>.
+
+Example:
+
+mipi_tx0: mipi-dphy@10215000 {
+	compatible = "mediatek,mt8173-mipi-tx";
+	reg = <0 0x10215000 0 0x1000>;
+	clocks = <&clk26m>;
+	clock-output-names = "mipi_tx0_pll";
+	#clock-cells = <0>;
+	#phy-cells = <0>;
+};
+
+dsi0: dsi@1401b000 {
+	compatible = "mediatek,mt8173-dsi";
+	reg = <0 0x1401b000 0 0x1000>;
+	interrupts = <GIC_SPI 192 IRQ_TYPE_LEVEL_LOW>;
+	clocks = <&mmsys MM_DSI0_ENGINE>, <&mmsys MM_DSI0_DIGITAL>,
+		 <&mipi_tx0>;
+	clock-names = "engine", "digital", "hs";
+	phys = <&mipi_tx0>;
+	phy-names = "dphy";
+
+	port {
+		dsi0_out: endpoint {
+			remote-endpoint = <&panel_in>;
+		};
+	};
+};
-- 
2.8.0.rc3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCH v16 3/8] drm/mediatek: Add DRM Driver for Mediatek SoC MT8173.
  2016-05-06 13:05 [PATCH v16 0/8] MT8173 DRM support Philipp Zabel
  2016-05-06 13:05 ` [PATCH v16 1/8] memory: mtk-smi: export mtk_smi_larb_get/put Philipp Zabel
  2016-05-06 13:05 ` [PATCH v16 2/8] dt-bindings: drm/mediatek: Add Mediatek display subsystem dts binding Philipp Zabel
@ 2016-05-06 13:05 ` Philipp Zabel
  2016-05-06 13:16   ` Daniel Vetter
  2016-05-06 13:05 ` [PATCH v16 4/8] drm/mediatek: Add DSI sub driver Philipp Zabel
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 11+ messages in thread
From: Philipp Zabel @ 2016-05-06 13:05 UTC (permalink / raw)
  To: dri-devel
  Cc: Mark Rutland, Stephen Boyd, Michael Turquette, Jie Qiu,
	Cawa Cheng, Mao Huang, YT Shen, Yingjoe Chen, devicetree,
	Jitao Shi, kernel, Pawel Moll, Ian Campbell, Rob Herring,
	linux-mediatek, Matthias Brugger, Paul Bolle, Emil Velikov,
	Tomasz Figa, Kumar Gala

From: CK Hu <ck.hu@mediatek.com>

This patch adds an initial DRM driver for the Mediatek MT8173 DISP
subsystem. It currently supports two fixed output streams from the
OVL0/OVL1 sources to the DSI0/DPI0 sinks, respectively.

Signed-off-by: CK Hu <ck.hu@mediatek.com>
Signed-off-by: YT Shen <yt.shen@mediatek.com>
Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
Signed-off-by: Bibby Hsieh <bibby.hsieh@mediatek.com>
Signed-off-by: Mao Huang <littlecvr@chromium.org>
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
---
Changes since v15:
 - Rebased onto drm-next
 - Use drm_crtc_send_vblank_event() instead of drm_send_vblank_event()
 - Order mtk_drm_drivers alphabetically
---
 drivers/gpu/drm/Kconfig                     |   2 +
 drivers/gpu/drm/Makefile                    |   1 +
 drivers/gpu/drm/mediatek/Kconfig            |  14 +
 drivers/gpu/drm/mediatek/Makefile           |  11 +
 drivers/gpu/drm/mediatek/mtk_disp_ovl.c     | 302 +++++++++++++++
 drivers/gpu/drm/mediatek/mtk_disp_rdma.c    | 240 ++++++++++++
 drivers/gpu/drm/mediatek/mtk_drm_crtc.c     | 582 ++++++++++++++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_drm_crtc.h     |  32 ++
 drivers/gpu/drm/mediatek/mtk_drm_ddp.c      | 353 +++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_drm_ddp.h      |  41 ++
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c | 225 +++++++++++
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h | 150 +++++++
 drivers/gpu/drm/mediatek/mtk_drm_drv.c      | 558 ++++++++++++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_drm_drv.h      |  57 +++
 drivers/gpu/drm/mediatek/mtk_drm_fb.c       | 165 ++++++++
 drivers/gpu/drm/mediatek/mtk_drm_fb.h       |  23 ++
 drivers/gpu/drm/mediatek/mtk_drm_gem.c      | 269 +++++++++++++
 drivers/gpu/drm/mediatek/mtk_drm_gem.h      |  59 +++
 drivers/gpu/drm/mediatek/mtk_drm_plane.c    | 240 ++++++++++++
 drivers/gpu/drm/mediatek/mtk_drm_plane.h    |  59 +++
 20 files changed, 3383 insertions(+)
 create mode 100644 drivers/gpu/drm/mediatek/Kconfig
 create mode 100644 drivers/gpu/drm/mediatek/Makefile
 create mode 100644 drivers/gpu/drm/mediatek/mtk_disp_ovl.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_disp_rdma.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_crtc.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_crtc.h
 create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_ddp.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_ddp.h
 create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
 create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_drv.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_drv.h
 create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_fb.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_fb.h
 create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_gem.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_gem.h
 create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_plane.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_plane.h

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 16e4c21..fc35731 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -288,3 +288,5 @@ source "drivers/gpu/drm/etnaviv/Kconfig"
 source "drivers/gpu/drm/arc/Kconfig"
 
 source "drivers/gpu/drm/hisilicon/Kconfig"
+
+source "drivers/gpu/drm/mediatek/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 43c2abf..2bd3e5a 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -74,6 +74,7 @@ obj-$(CONFIG_DRM_MSM) += msm/
 obj-$(CONFIG_DRM_TEGRA) += tegra/
 obj-$(CONFIG_DRM_STI) += sti/
 obj-$(CONFIG_DRM_IMX) += imx/
+obj-$(CONFIG_DRM_MEDIATEK) += mediatek/
 obj-y			+= i2c/
 obj-y			+= panel/
 obj-y			+= bridge/
diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
new file mode 100644
index 0000000..30e5371
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/Kconfig
@@ -0,0 +1,14 @@
+config DRM_MEDIATEK
+	tristate "DRM Support for Mediatek SoCs"
+	depends on DRM
+	depends on ARCH_MEDIATEK || (ARM && COMPILE_TEST)
+	select DRM_GEM_CMA_HELPER
+	select DRM_KMS_HELPER
+	select IOMMU_DMA
+	select MEMORY
+	select MTK_SMI
+	help
+	  Choose this option if you have a Mediatek SoCs.
+	  The module will be called mediatek-drm
+	  This driver provides kernel mode setting and
+	  buffer management to userspace.
diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
new file mode 100644
index 0000000..d4bde7c
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/Makefile
@@ -0,0 +1,11 @@
+mediatek-drm-y := mtk_disp_ovl.o \
+		  mtk_disp_rdma.o \
+		  mtk_drm_crtc.o \
+		  mtk_drm_ddp.o \
+		  mtk_drm_ddp_comp.o \
+		  mtk_drm_drv.o \
+		  mtk_drm_fb.o \
+		  mtk_drm_gem.o \
+		  mtk_drm_plane.o
+
+obj-$(CONFIG_DRM_MEDIATEK) += mediatek-drm.o
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
new file mode 100644
index 0000000..8f62671f
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drmP.h>
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+
+#include "mtk_drm_crtc.h"
+#include "mtk_drm_ddp_comp.h"
+
+#define DISP_REG_OVL_INTEN			0x0004
+#define OVL_FME_CPL_INT					BIT(1)
+#define DISP_REG_OVL_INTSTA			0x0008
+#define DISP_REG_OVL_EN				0x000c
+#define DISP_REG_OVL_RST			0x0014
+#define DISP_REG_OVL_ROI_SIZE			0x0020
+#define DISP_REG_OVL_ROI_BGCLR			0x0028
+#define DISP_REG_OVL_SRC_CON			0x002c
+#define DISP_REG_OVL_CON(n)			(0x0030 + 0x20 * (n))
+#define DISP_REG_OVL_SRC_SIZE(n)		(0x0038 + 0x20 * (n))
+#define DISP_REG_OVL_OFFSET(n)			(0x003c + 0x20 * (n))
+#define DISP_REG_OVL_PITCH(n)			(0x0044 + 0x20 * (n))
+#define DISP_REG_OVL_RDMA_CTRL(n)		(0x00c0 + 0x20 * (n))
+#define DISP_REG_OVL_RDMA_GMC(n)		(0x00c8 + 0x20 * (n))
+#define DISP_REG_OVL_ADDR(n)			(0x0f40 + 0x20 * (n))
+
+#define	OVL_RDMA_MEM_GMC	0x40402020
+
+#define OVL_CON_BYTE_SWAP	BIT(24)
+#define OVL_CON_CLRFMT_RGB565	(0 << 12)
+#define OVL_CON_CLRFMT_RGB888	(1 << 12)
+#define OVL_CON_CLRFMT_RGBA8888	(2 << 12)
+#define OVL_CON_CLRFMT_ARGB8888	(3 << 12)
+#define	OVL_CON_AEN		BIT(8)
+#define	OVL_CON_ALPHA		0xff
+
+/**
+ * struct mtk_disp_ovl - DISP_OVL driver structure
+ * @ddp_comp - structure containing type enum and hardware resources
+ * @crtc - associated crtc to report vblank events to
+ */
+struct mtk_disp_ovl {
+	struct mtk_ddp_comp		ddp_comp;
+	struct drm_crtc			*crtc;
+};
+
+static irqreturn_t mtk_disp_ovl_irq_handler(int irq, void *dev_id)
+{
+	struct mtk_disp_ovl *priv = dev_id;
+	struct mtk_ddp_comp *ovl = &priv->ddp_comp;
+
+	/* Clear frame completion interrupt */
+	writel(0x0, ovl->regs + DISP_REG_OVL_INTSTA);
+
+	if (!priv->crtc)
+		return IRQ_NONE;
+
+	mtk_crtc_ddp_irq(priv->crtc, ovl);
+
+	return IRQ_HANDLED;
+}
+
+static void mtk_ovl_enable_vblank(struct mtk_ddp_comp *comp,
+				  struct drm_crtc *crtc)
+{
+	struct mtk_disp_ovl *priv = container_of(comp, struct mtk_disp_ovl,
+						 ddp_comp);
+
+	priv->crtc = crtc;
+	writel_relaxed(OVL_FME_CPL_INT, comp->regs + DISP_REG_OVL_INTEN);
+}
+
+static void mtk_ovl_disable_vblank(struct mtk_ddp_comp *comp)
+{
+	struct mtk_disp_ovl *priv = container_of(comp, struct mtk_disp_ovl,
+						 ddp_comp);
+
+	priv->crtc = NULL;
+	writel_relaxed(0x0, comp->regs + DISP_REG_OVL_INTEN);
+}
+
+static void mtk_ovl_start(struct mtk_ddp_comp *comp)
+{
+	writel_relaxed(0x1, comp->regs + DISP_REG_OVL_EN);
+}
+
+static void mtk_ovl_stop(struct mtk_ddp_comp *comp)
+{
+	writel_relaxed(0x0, comp->regs + DISP_REG_OVL_EN);
+}
+
+static void mtk_ovl_config(struct mtk_ddp_comp *comp, unsigned int w,
+			   unsigned int h, unsigned int vrefresh)
+{
+	if (w != 0 && h != 0)
+		writel_relaxed(h << 16 | w, comp->regs + DISP_REG_OVL_ROI_SIZE);
+	writel_relaxed(0x0, comp->regs + DISP_REG_OVL_ROI_BGCLR);
+
+	writel(0x1, comp->regs + DISP_REG_OVL_RST);
+	writel(0x0, comp->regs + DISP_REG_OVL_RST);
+}
+
+static void mtk_ovl_layer_on(struct mtk_ddp_comp *comp, unsigned int idx)
+{
+	unsigned int reg;
+
+	writel(0x1, comp->regs + DISP_REG_OVL_RDMA_CTRL(idx));
+	writel(OVL_RDMA_MEM_GMC, comp->regs + DISP_REG_OVL_RDMA_GMC(idx));
+
+	reg = readl(comp->regs + DISP_REG_OVL_SRC_CON);
+	reg = reg | BIT(idx);
+	writel(reg, comp->regs + DISP_REG_OVL_SRC_CON);
+}
+
+static void mtk_ovl_layer_off(struct mtk_ddp_comp *comp, unsigned int idx)
+{
+	unsigned int reg;
+
+	reg = readl(comp->regs + DISP_REG_OVL_SRC_CON);
+	reg = reg & ~BIT(idx);
+	writel(reg, comp->regs + DISP_REG_OVL_SRC_CON);
+
+	writel(0x0, comp->regs + DISP_REG_OVL_RDMA_CTRL(idx));
+}
+
+static unsigned int ovl_fmt_convert(unsigned int fmt)
+{
+	switch (fmt) {
+	default:
+	case DRM_FORMAT_RGB565:
+		return OVL_CON_CLRFMT_RGB565;
+	case DRM_FORMAT_BGR565:
+		return OVL_CON_CLRFMT_RGB565 | OVL_CON_BYTE_SWAP;
+	case DRM_FORMAT_RGB888:
+		return OVL_CON_CLRFMT_RGB888;
+	case DRM_FORMAT_BGR888:
+		return OVL_CON_CLRFMT_RGB888 | OVL_CON_BYTE_SWAP;
+	case DRM_FORMAT_RGBX8888:
+	case DRM_FORMAT_RGBA8888:
+		return OVL_CON_CLRFMT_ARGB8888;
+	case DRM_FORMAT_BGRX8888:
+	case DRM_FORMAT_BGRA8888:
+		return OVL_CON_CLRFMT_ARGB8888 | OVL_CON_BYTE_SWAP;
+	case DRM_FORMAT_XRGB8888:
+	case DRM_FORMAT_ARGB8888:
+		return OVL_CON_CLRFMT_RGBA8888;
+	case DRM_FORMAT_XBGR8888:
+	case DRM_FORMAT_ABGR8888:
+		return OVL_CON_CLRFMT_RGBA8888 | OVL_CON_BYTE_SWAP;
+	}
+}
+
+static void mtk_ovl_layer_config(struct mtk_ddp_comp *comp, unsigned int idx,
+				 struct mtk_plane_state *state)
+{
+	struct mtk_plane_pending_state *pending = &state->pending;
+	unsigned int addr = pending->addr;
+	unsigned int pitch = pending->pitch & 0xffff;
+	unsigned int fmt = pending->format;
+	unsigned int offset = (pending->y << 16) | pending->x;
+	unsigned int src_size = (pending->height << 16) | pending->width;
+	unsigned int con;
+
+	if (!pending->enable)
+		mtk_ovl_layer_off(comp, idx);
+
+	con = ovl_fmt_convert(fmt);
+	if (idx != 0)
+		con |= OVL_CON_AEN | OVL_CON_ALPHA;
+
+	writel_relaxed(con, comp->regs + DISP_REG_OVL_CON(idx));
+	writel_relaxed(pitch, comp->regs + DISP_REG_OVL_PITCH(idx));
+	writel_relaxed(src_size, comp->regs + DISP_REG_OVL_SRC_SIZE(idx));
+	writel_relaxed(offset, comp->regs + DISP_REG_OVL_OFFSET(idx));
+	writel_relaxed(addr, comp->regs + DISP_REG_OVL_ADDR(idx));
+
+	if (pending->enable)
+		mtk_ovl_layer_on(comp, idx);
+}
+
+static const struct mtk_ddp_comp_funcs mtk_disp_ovl_funcs = {
+	.config = mtk_ovl_config,
+	.start = mtk_ovl_start,
+	.stop = mtk_ovl_stop,
+	.enable_vblank = mtk_ovl_enable_vblank,
+	.disable_vblank = mtk_ovl_disable_vblank,
+	.layer_on = mtk_ovl_layer_on,
+	.layer_off = mtk_ovl_layer_off,
+	.layer_config = mtk_ovl_layer_config,
+};
+
+static int mtk_disp_ovl_bind(struct device *dev, struct device *master,
+			     void *data)
+{
+	struct mtk_disp_ovl *priv = dev_get_drvdata(dev);
+	struct drm_device *drm_dev = data;
+	int ret;
+
+	ret = mtk_ddp_comp_register(drm_dev, &priv->ddp_comp);
+	if (ret < 0) {
+		dev_err(dev, "Failed to register component %s: %d\n",
+			dev->of_node->full_name, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void mtk_disp_ovl_unbind(struct device *dev, struct device *master,
+				void *data)
+{
+	struct mtk_disp_ovl *priv = dev_get_drvdata(dev);
+	struct drm_device *drm_dev = data;
+
+	mtk_ddp_comp_unregister(drm_dev, &priv->ddp_comp);
+}
+
+static const struct component_ops mtk_disp_ovl_component_ops = {
+	.bind	= mtk_disp_ovl_bind,
+	.unbind = mtk_disp_ovl_unbind,
+};
+
+static int mtk_disp_ovl_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct mtk_disp_ovl *priv;
+	int comp_id;
+	int irq;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	ret = devm_request_irq(dev, irq, mtk_disp_ovl_irq_handler,
+			       IRQF_TRIGGER_NONE, dev_name(dev), priv);
+	if (ret < 0) {
+		dev_err(dev, "Failed to request irq %d: %d\n", irq, ret);
+		return ret;
+	}
+
+	comp_id = mtk_ddp_comp_get_id(dev->of_node, MTK_DISP_OVL);
+	if (comp_id < 0) {
+		dev_err(dev, "Failed to identify by alias: %d\n", comp_id);
+		return comp_id;
+	}
+
+	ret = mtk_ddp_comp_init(dev, dev->of_node, &priv->ddp_comp, comp_id,
+				&mtk_disp_ovl_funcs);
+	if (ret) {
+		dev_err(dev, "Failed to initialize component: %d\n", ret);
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, priv);
+
+	ret = component_add(dev, &mtk_disp_ovl_component_ops);
+	if (ret)
+		dev_err(dev, "Failed to add component: %d\n", ret);
+
+	return ret;
+}
+
+static int mtk_disp_ovl_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &mtk_disp_ovl_component_ops);
+
+	return 0;
+}
+
+static const struct of_device_id mtk_disp_ovl_driver_dt_match[] = {
+	{ .compatible = "mediatek,mt8173-disp-ovl", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, mtk_disp_ovl_driver_dt_match);
+
+struct platform_driver mtk_disp_ovl_driver = {
+	.probe		= mtk_disp_ovl_probe,
+	.remove		= mtk_disp_ovl_remove,
+	.driver		= {
+		.name	= "mediatek-disp-ovl",
+		.owner	= THIS_MODULE,
+		.of_match_table = mtk_disp_ovl_driver_dt_match,
+	},
+};
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_rdma.c b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c
new file mode 100644
index 0000000..5fb80cb
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drmP.h>
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+
+#include "mtk_drm_crtc.h"
+#include "mtk_drm_ddp_comp.h"
+
+#define DISP_REG_RDMA_INT_ENABLE		0x0000
+#define DISP_REG_RDMA_INT_STATUS		0x0004
+#define RDMA_TARGET_LINE_INT				BIT(5)
+#define RDMA_FIFO_UNDERFLOW_INT				BIT(4)
+#define RDMA_EOF_ABNORMAL_INT				BIT(3)
+#define RDMA_FRAME_END_INT				BIT(2)
+#define RDMA_FRAME_START_INT				BIT(1)
+#define RDMA_REG_UPDATE_INT				BIT(0)
+#define DISP_REG_RDMA_GLOBAL_CON		0x0010
+#define RDMA_ENGINE_EN					BIT(0)
+#define DISP_REG_RDMA_SIZE_CON_0		0x0014
+#define DISP_REG_RDMA_SIZE_CON_1		0x0018
+#define DISP_REG_RDMA_TARGET_LINE		0x001c
+#define DISP_REG_RDMA_FIFO_CON			0x0040
+#define RDMA_FIFO_UNDERFLOW_EN				BIT(31)
+#define RDMA_FIFO_PSEUDO_SIZE(bytes)			(((bytes) / 16) << 16)
+#define RDMA_OUTPUT_VALID_FIFO_THRESHOLD(bytes)		((bytes) / 16)
+
+/**
+ * struct mtk_disp_rdma - DISP_RDMA driver structure
+ * @ddp_comp - structure containing type enum and hardware resources
+ * @crtc - associated crtc to report irq events to
+ */
+struct mtk_disp_rdma {
+	struct mtk_ddp_comp		ddp_comp;
+	struct drm_crtc			*crtc;
+};
+
+static irqreturn_t mtk_disp_rdma_irq_handler(int irq, void *dev_id)
+{
+	struct mtk_disp_rdma *priv = dev_id;
+	struct mtk_ddp_comp *rdma = &priv->ddp_comp;
+
+	/* Clear frame completion interrupt */
+	writel(0x0, rdma->regs + DISP_REG_RDMA_INT_STATUS);
+
+	if (!priv->crtc)
+		return IRQ_NONE;
+
+	mtk_crtc_ddp_irq(priv->crtc, rdma);
+
+	return IRQ_HANDLED;
+}
+
+static void rdma_update_bits(struct mtk_ddp_comp *comp, unsigned int reg,
+			     unsigned int mask, unsigned int val)
+{
+	unsigned int tmp = readl(comp->regs + reg);
+
+	tmp = (tmp & ~mask) | (val & mask);
+	writel(tmp, comp->regs + reg);
+}
+
+static void mtk_rdma_enable_vblank(struct mtk_ddp_comp *comp,
+				   struct drm_crtc *crtc)
+{
+	struct mtk_disp_rdma *priv = container_of(comp, struct mtk_disp_rdma,
+						  ddp_comp);
+
+	priv->crtc = crtc;
+	rdma_update_bits(comp, DISP_REG_RDMA_INT_ENABLE, RDMA_FRAME_END_INT,
+			 RDMA_FRAME_END_INT);
+}
+
+static void mtk_rdma_disable_vblank(struct mtk_ddp_comp *comp)
+{
+	struct mtk_disp_rdma *priv = container_of(comp, struct mtk_disp_rdma,
+						  ddp_comp);
+
+	priv->crtc = NULL;
+	rdma_update_bits(comp, DISP_REG_RDMA_INT_ENABLE, RDMA_FRAME_END_INT, 0);
+}
+
+static void mtk_rdma_start(struct mtk_ddp_comp *comp)
+{
+	rdma_update_bits(comp, DISP_REG_RDMA_GLOBAL_CON, RDMA_ENGINE_EN,
+			 RDMA_ENGINE_EN);
+}
+
+static void mtk_rdma_stop(struct mtk_ddp_comp *comp)
+{
+	rdma_update_bits(comp, DISP_REG_RDMA_GLOBAL_CON, RDMA_ENGINE_EN, 0);
+}
+
+static void mtk_rdma_config(struct mtk_ddp_comp *comp, unsigned int width,
+			    unsigned int height, unsigned int vrefresh)
+{
+	unsigned int threshold;
+	unsigned int reg;
+
+	rdma_update_bits(comp, DISP_REG_RDMA_SIZE_CON_0, 0xfff, width);
+	rdma_update_bits(comp, DISP_REG_RDMA_SIZE_CON_1, 0xfffff, height);
+
+	/*
+	 * Enable FIFO underflow since DSI and DPI can't be blocked.
+	 * Keep the FIFO pseudo size reset default of 8 KiB. Set the
+	 * output threshold to 6 microseconds with 7/6 overhead to
+	 * account for blanking, and with a pixel depth of 4 bytes:
+	 */
+	threshold = width * height * vrefresh * 4 * 7 / 1000000;
+	reg = RDMA_FIFO_UNDERFLOW_EN |
+	      RDMA_FIFO_PSEUDO_SIZE(SZ_8K) |
+	      RDMA_OUTPUT_VALID_FIFO_THRESHOLD(threshold);
+	writel(reg, comp->regs + DISP_REG_RDMA_FIFO_CON);
+}
+
+static const struct mtk_ddp_comp_funcs mtk_disp_rdma_funcs = {
+	.config = mtk_rdma_config,
+	.start = mtk_rdma_start,
+	.stop = mtk_rdma_stop,
+	.enable_vblank = mtk_rdma_enable_vblank,
+	.disable_vblank = mtk_rdma_disable_vblank,
+};
+
+static int mtk_disp_rdma_bind(struct device *dev, struct device *master,
+			      void *data)
+{
+	struct mtk_disp_rdma *priv = dev_get_drvdata(dev);
+	struct drm_device *drm_dev = data;
+	int ret;
+
+	ret = mtk_ddp_comp_register(drm_dev, &priv->ddp_comp);
+	if (ret < 0) {
+		dev_err(dev, "Failed to register component %s: %d\n",
+			dev->of_node->full_name, ret);
+		return ret;
+	}
+
+	return 0;
+
+}
+
+static void mtk_disp_rdma_unbind(struct device *dev, struct device *master,
+				 void *data)
+{
+	struct mtk_disp_rdma *priv = dev_get_drvdata(dev);
+	struct drm_device *drm_dev = data;
+
+	mtk_ddp_comp_unregister(drm_dev, &priv->ddp_comp);
+}
+
+static const struct component_ops mtk_disp_rdma_component_ops = {
+	.bind	= mtk_disp_rdma_bind,
+	.unbind = mtk_disp_rdma_unbind,
+};
+
+static int mtk_disp_rdma_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct mtk_disp_rdma *priv;
+	int comp_id;
+	int irq;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	comp_id = mtk_ddp_comp_get_id(dev->of_node, MTK_DISP_RDMA);
+	if (comp_id < 0) {
+		dev_err(dev, "Failed to identify by alias: %d\n", comp_id);
+		return comp_id;
+	}
+
+	ret = mtk_ddp_comp_init(dev, dev->of_node, &priv->ddp_comp, comp_id,
+				&mtk_disp_rdma_funcs);
+	if (ret) {
+		dev_err(dev, "Failed to initialize component: %d\n", ret);
+		return ret;
+	}
+
+	/* Disable and clear pending interrupts */
+	writel(0x0, priv->ddp_comp.regs + DISP_REG_RDMA_INT_ENABLE);
+	writel(0x0, priv->ddp_comp.regs + DISP_REG_RDMA_INT_STATUS);
+
+	ret = devm_request_irq(dev, irq, mtk_disp_rdma_irq_handler,
+			       IRQF_TRIGGER_NONE, dev_name(dev), priv);
+	if (ret < 0) {
+		dev_err(dev, "Failed to request irq %d: %d\n", irq, ret);
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, priv);
+
+	ret = component_add(dev, &mtk_disp_rdma_component_ops);
+	if (ret)
+		dev_err(dev, "Failed to add component: %d\n", ret);
+
+	return ret;
+}
+
+static int mtk_disp_rdma_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &mtk_disp_rdma_component_ops);
+
+	return 0;
+}
+
+static const struct of_device_id mtk_disp_rdma_driver_dt_match[] = {
+	{ .compatible = "mediatek,mt8173-disp-rdma", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, mtk_disp_rdma_driver_dt_match);
+
+struct platform_driver mtk_disp_rdma_driver = {
+	.probe		= mtk_disp_rdma_probe,
+	.remove		= mtk_disp_rdma_remove,
+	.driver		= {
+		.name	= "mediatek-disp-rdma",
+		.owner	= THIS_MODULE,
+		.of_match_table = mtk_disp_rdma_driver_dt_match,
+	},
+};
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
new file mode 100644
index 0000000..3095fc1
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
@@ -0,0 +1,582 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/barrier.h>
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_plane_helper.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <soc/mediatek/smi.h>
+
+#include "mtk_drm_drv.h"
+#include "mtk_drm_crtc.h"
+#include "mtk_drm_ddp.h"
+#include "mtk_drm_ddp_comp.h"
+#include "mtk_drm_gem.h"
+#include "mtk_drm_plane.h"
+
+/**
+ * struct mtk_drm_crtc - MediaTek specific crtc structure.
+ * @base: crtc object.
+ * @enabled: records whether crtc_enable succeeded
+ * @planes: array of 4 mtk_drm_plane structures, one for each overlay plane
+ * @pending_planes: whether any plane has pending changes to be applied
+ * @config_regs: memory mapped mmsys configuration register space
+ * @mutex: handle to one of the ten disp_mutex streams
+ * @ddp_comp_nr: number of components in ddp_comp
+ * @ddp_comp: array of pointers the mtk_ddp_comp structures used by this crtc
+ */
+struct mtk_drm_crtc {
+	struct drm_crtc			base;
+	bool				enabled;
+
+	bool				pending_needs_vblank;
+	struct drm_pending_vblank_event	*event;
+
+	struct mtk_drm_plane		planes[OVL_LAYER_NR];
+	bool				pending_planes;
+
+	void __iomem			*config_regs;
+	struct mtk_disp_mutex		*mutex;
+	unsigned int			ddp_comp_nr;
+	struct mtk_ddp_comp		**ddp_comp;
+};
+
+struct mtk_crtc_state {
+	struct drm_crtc_state		base;
+
+	bool				pending_config;
+	unsigned int			pending_width;
+	unsigned int			pending_height;
+	unsigned int			pending_vrefresh;
+};
+
+static inline struct mtk_drm_crtc *to_mtk_crtc(struct drm_crtc *c)
+{
+	return container_of(c, struct mtk_drm_crtc, base);
+}
+
+static inline struct mtk_crtc_state *to_mtk_crtc_state(struct drm_crtc_state *s)
+{
+	return container_of(s, struct mtk_crtc_state, base);
+}
+
+static void mtk_drm_crtc_finish_page_flip(struct mtk_drm_crtc *mtk_crtc)
+{
+	struct drm_crtc *crtc = &mtk_crtc->base;
+	unsigned long flags;
+
+	spin_lock_irqsave(&crtc->dev->event_lock, flags);
+	drm_crtc_send_vblank_event(crtc, mtk_crtc->event);
+	drm_crtc_vblank_put(crtc);
+	mtk_crtc->event = NULL;
+	spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
+}
+
+static void mtk_drm_finish_page_flip(struct mtk_drm_crtc *mtk_crtc)
+{
+	drm_crtc_handle_vblank(&mtk_crtc->base);
+	if (mtk_crtc->pending_needs_vblank) {
+		mtk_drm_crtc_finish_page_flip(mtk_crtc);
+		mtk_crtc->pending_needs_vblank = false;
+	}
+}
+
+static void mtk_drm_crtc_destroy(struct drm_crtc *crtc)
+{
+	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
+	int i;
+
+	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++)
+		clk_unprepare(mtk_crtc->ddp_comp[i]->clk);
+
+	mtk_disp_mutex_put(mtk_crtc->mutex);
+
+	drm_crtc_cleanup(crtc);
+}
+
+static void mtk_drm_crtc_reset(struct drm_crtc *crtc)
+{
+	struct mtk_crtc_state *state;
+
+	if (crtc->state) {
+		if (crtc->state->mode_blob)
+			drm_property_unreference_blob(crtc->state->mode_blob);
+
+		state = to_mtk_crtc_state(crtc->state);
+		memset(state, 0, sizeof(*state));
+	} else {
+		state = kzalloc(sizeof(*state), GFP_KERNEL);
+		if (!state)
+			return;
+		crtc->state = &state->base;
+	}
+
+	state->base.crtc = crtc;
+}
+
+static struct drm_crtc_state *mtk_drm_crtc_duplicate_state(struct drm_crtc *crtc)
+{
+	struct mtk_crtc_state *state;
+
+	state = kzalloc(sizeof(*state), GFP_KERNEL);
+	if (!state)
+		return NULL;
+
+	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
+
+	WARN_ON(state->base.crtc != crtc);
+	state->base.crtc = crtc;
+
+	return &state->base;
+}
+
+static void mtk_drm_crtc_destroy_state(struct drm_crtc *crtc,
+				       struct drm_crtc_state *state)
+{
+	__drm_atomic_helper_crtc_destroy_state(crtc, state);
+	kfree(to_mtk_crtc_state(state));
+}
+
+static bool mtk_drm_crtc_mode_fixup(struct drm_crtc *crtc,
+				    const struct drm_display_mode *mode,
+				    struct drm_display_mode *adjusted_mode)
+{
+	/* Nothing to do here, but this callback is mandatory. */
+	return true;
+}
+
+static void mtk_drm_crtc_mode_set_nofb(struct drm_crtc *crtc)
+{
+	struct mtk_crtc_state *state = to_mtk_crtc_state(crtc->state);
+
+	state->pending_width = crtc->mode.hdisplay;
+	state->pending_height = crtc->mode.vdisplay;
+	state->pending_vrefresh = crtc->mode.vrefresh;
+	wmb();	/* Make sure the above parameters are set before update */
+	state->pending_config = true;
+}
+
+int mtk_drm_crtc_enable_vblank(struct drm_device *drm, unsigned int pipe)
+{
+	struct mtk_drm_private *priv = drm->dev_private;
+	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(priv->crtc[pipe]);
+	struct mtk_ddp_comp *ovl = mtk_crtc->ddp_comp[0];
+
+	mtk_ddp_comp_enable_vblank(ovl, &mtk_crtc->base);
+
+	return 0;
+}
+
+void mtk_drm_crtc_disable_vblank(struct drm_device *drm, unsigned int pipe)
+{
+	struct mtk_drm_private *priv = drm->dev_private;
+	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(priv->crtc[pipe]);
+	struct mtk_ddp_comp *ovl = mtk_crtc->ddp_comp[0];
+
+	mtk_ddp_comp_disable_vblank(ovl);
+}
+
+static int mtk_crtc_ddp_clk_enable(struct mtk_drm_crtc *mtk_crtc)
+{
+	int ret;
+	int i;
+
+	DRM_DEBUG_DRIVER("%s\n", __func__);
+	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
+		ret = clk_enable(mtk_crtc->ddp_comp[i]->clk);
+		if (ret) {
+			DRM_ERROR("Failed to enable clock %d: %d\n", i, ret);
+			goto err;
+		}
+	}
+
+	return 0;
+err:
+	while (--i >= 0)
+		clk_disable(mtk_crtc->ddp_comp[i]->clk);
+	return ret;
+}
+
+static void mtk_crtc_ddp_clk_disable(struct mtk_drm_crtc *mtk_crtc)
+{
+	int i;
+
+	DRM_DEBUG_DRIVER("%s\n", __func__);
+	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++)
+		clk_disable(mtk_crtc->ddp_comp[i]->clk);
+}
+
+static int mtk_crtc_ddp_hw_init(struct mtk_drm_crtc *mtk_crtc)
+{
+	struct drm_crtc *crtc = &mtk_crtc->base;
+	unsigned int width, height, vrefresh;
+	int ret;
+	int i;
+
+	DRM_DEBUG_DRIVER("%s\n", __func__);
+	if (WARN_ON(!crtc->state))
+		return -EINVAL;
+
+	width = crtc->state->adjusted_mode.hdisplay;
+	height = crtc->state->adjusted_mode.vdisplay;
+	vrefresh = crtc->state->adjusted_mode.vrefresh;
+
+	ret = pm_runtime_get_sync(crtc->dev->dev);
+	if (ret < 0) {
+		DRM_ERROR("Failed to enable power domain: %d\n", ret);
+		return ret;
+	}
+
+	ret = mtk_disp_mutex_prepare(mtk_crtc->mutex);
+	if (ret < 0) {
+		DRM_ERROR("Failed to enable mutex clock: %d\n", ret);
+		goto err_pm_runtime_put;
+	}
+
+	ret = mtk_crtc_ddp_clk_enable(mtk_crtc);
+	if (ret < 0) {
+		DRM_ERROR("Failed to enable component clocks: %d\n", ret);
+		goto err_mutex_unprepare;
+	}
+
+	DRM_DEBUG_DRIVER("mediatek_ddp_ddp_path_setup\n");
+	for (i = 0; i < mtk_crtc->ddp_comp_nr - 1; i++) {
+		mtk_ddp_add_comp_to_path(mtk_crtc->config_regs,
+					 mtk_crtc->ddp_comp[i]->id,
+					 mtk_crtc->ddp_comp[i + 1]->id);
+		mtk_disp_mutex_add_comp(mtk_crtc->mutex,
+					mtk_crtc->ddp_comp[i]->id);
+	}
+	mtk_disp_mutex_add_comp(mtk_crtc->mutex, mtk_crtc->ddp_comp[i]->id);
+	mtk_disp_mutex_enable(mtk_crtc->mutex);
+
+	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
+		struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[i];
+
+		mtk_ddp_comp_config(comp, width, height, vrefresh);
+		mtk_ddp_comp_start(comp);
+	}
+
+	/* Initially configure all planes */
+	for (i = 0; i < OVL_LAYER_NR; i++) {
+		struct drm_plane *plane = &mtk_crtc->planes[i].base;
+		struct mtk_plane_state *plane_state;
+
+		plane_state = to_mtk_plane_state(plane->state);
+		mtk_ddp_comp_layer_config(mtk_crtc->ddp_comp[0], i,
+					  plane_state);
+	}
+
+	return 0;
+
+err_mutex_unprepare:
+	mtk_disp_mutex_unprepare(mtk_crtc->mutex);
+err_pm_runtime_put:
+	pm_runtime_put(crtc->dev->dev);
+	return ret;
+}
+
+static void mtk_crtc_ddp_hw_fini(struct mtk_drm_crtc *mtk_crtc)
+{
+	struct drm_device *drm = mtk_crtc->base.dev;
+	int i;
+
+	DRM_DEBUG_DRIVER("%s\n", __func__);
+	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++)
+		mtk_ddp_comp_stop(mtk_crtc->ddp_comp[i]);
+	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++)
+		mtk_disp_mutex_remove_comp(mtk_crtc->mutex,
+					   mtk_crtc->ddp_comp[i]->id);
+	mtk_disp_mutex_disable(mtk_crtc->mutex);
+	for (i = 0; i < mtk_crtc->ddp_comp_nr - 1; i++) {
+		mtk_ddp_remove_comp_from_path(mtk_crtc->config_regs,
+					      mtk_crtc->ddp_comp[i]->id,
+					      mtk_crtc->ddp_comp[i + 1]->id);
+		mtk_disp_mutex_remove_comp(mtk_crtc->mutex,
+					   mtk_crtc->ddp_comp[i]->id);
+	}
+	mtk_disp_mutex_remove_comp(mtk_crtc->mutex, mtk_crtc->ddp_comp[i]->id);
+	mtk_crtc_ddp_clk_disable(mtk_crtc);
+	mtk_disp_mutex_unprepare(mtk_crtc->mutex);
+
+	pm_runtime_put(drm->dev);
+}
+
+static void mtk_drm_crtc_enable(struct drm_crtc *crtc)
+{
+	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
+	struct mtk_ddp_comp *ovl = mtk_crtc->ddp_comp[0];
+	int ret;
+
+	DRM_DEBUG_DRIVER("%s %d\n", __func__, crtc->base.id);
+
+	ret = mtk_smi_larb_get(ovl->larb_dev);
+	if (ret) {
+		DRM_ERROR("Failed to get larb: %d\n", ret);
+		return;
+	}
+
+	ret = mtk_crtc_ddp_hw_init(mtk_crtc);
+	if (ret) {
+		mtk_smi_larb_put(ovl->larb_dev);
+		return;
+	}
+
+	drm_crtc_vblank_on(crtc);
+	mtk_crtc->enabled = true;
+}
+
+static void mtk_drm_crtc_disable(struct drm_crtc *crtc)
+{
+	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
+	struct mtk_ddp_comp *ovl = mtk_crtc->ddp_comp[0];
+	int i;
+
+	DRM_DEBUG_DRIVER("%s %d\n", __func__, crtc->base.id);
+	if (!mtk_crtc->enabled)
+		return;
+
+	/* Set all pending plane state to disabled */
+	for (i = 0; i < OVL_LAYER_NR; i++) {
+		struct drm_plane *plane = &mtk_crtc->planes[i].base;
+		struct mtk_plane_state *plane_state;
+
+		plane_state = to_mtk_plane_state(plane->state);
+		plane_state->pending.enable = false;
+		plane_state->pending.config = true;
+	}
+	mtk_crtc->pending_planes = true;
+
+	/* Wait for planes to be disabled */
+	drm_crtc_wait_one_vblank(crtc);
+
+	drm_crtc_vblank_off(crtc);
+	mtk_crtc_ddp_hw_fini(mtk_crtc);
+	mtk_smi_larb_put(ovl->larb_dev);
+
+	mtk_crtc->enabled = false;
+}
+
+static void mtk_drm_crtc_atomic_begin(struct drm_crtc *crtc,
+				      struct drm_crtc_state *old_crtc_state)
+{
+	struct mtk_crtc_state *state = to_mtk_crtc_state(crtc->state);
+	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
+
+	if (mtk_crtc->event && state->base.event)
+		DRM_ERROR("new event while there is still a pending event\n");
+
+	if (state->base.event) {
+		state->base.event->pipe = drm_crtc_index(crtc);
+		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
+		mtk_crtc->event = state->base.event;
+		state->base.event = NULL;
+	}
+}
+
+static void mtk_drm_crtc_atomic_flush(struct drm_crtc *crtc,
+				      struct drm_crtc_state *old_crtc_state)
+{
+	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
+	unsigned int pending_planes = 0;
+	int i;
+
+	if (mtk_crtc->event)
+		mtk_crtc->pending_needs_vblank = true;
+	for (i = 0; i < OVL_LAYER_NR; i++) {
+		struct drm_plane *plane = &mtk_crtc->planes[i].base;
+		struct mtk_plane_state *plane_state;
+
+		plane_state = to_mtk_plane_state(plane->state);
+		if (plane_state->pending.dirty) {
+			plane_state->pending.config = true;
+			plane_state->pending.dirty = false;
+			pending_planes |= BIT(i);
+		}
+	}
+	if (pending_planes)
+		mtk_crtc->pending_planes = true;
+}
+
+static const struct drm_crtc_funcs mtk_crtc_funcs = {
+	.set_config		= drm_atomic_helper_set_config,
+	.page_flip		= drm_atomic_helper_page_flip,
+	.destroy		= mtk_drm_crtc_destroy,
+	.reset			= mtk_drm_crtc_reset,
+	.atomic_duplicate_state	= mtk_drm_crtc_duplicate_state,
+	.atomic_destroy_state	= mtk_drm_crtc_destroy_state,
+};
+
+static const struct drm_crtc_helper_funcs mtk_crtc_helper_funcs = {
+	.mode_fixup	= mtk_drm_crtc_mode_fixup,
+	.mode_set_nofb	= mtk_drm_crtc_mode_set_nofb,
+	.enable		= mtk_drm_crtc_enable,
+	.disable	= mtk_drm_crtc_disable,
+	.atomic_begin	= mtk_drm_crtc_atomic_begin,
+	.atomic_flush	= mtk_drm_crtc_atomic_flush,
+};
+
+static int mtk_drm_crtc_init(struct drm_device *drm,
+			     struct mtk_drm_crtc *mtk_crtc,
+			     struct drm_plane *primary,
+			     struct drm_plane *cursor, unsigned int pipe)
+{
+	int ret;
+
+	ret = drm_crtc_init_with_planes(drm, &mtk_crtc->base, primary, cursor,
+					&mtk_crtc_funcs, NULL);
+	if (ret)
+		goto err_cleanup_crtc;
+
+	drm_crtc_helper_add(&mtk_crtc->base, &mtk_crtc_helper_funcs);
+
+	return 0;
+
+err_cleanup_crtc:
+	drm_crtc_cleanup(&mtk_crtc->base);
+	return ret;
+}
+
+void mtk_crtc_ddp_irq(struct drm_crtc *crtc, struct mtk_ddp_comp *ovl)
+{
+	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
+	struct mtk_crtc_state *state = to_mtk_crtc_state(mtk_crtc->base.state);
+	unsigned int i;
+
+	/*
+	 * TODO: instead of updating the registers here, we should prepare
+	 * working registers in atomic_commit and let the hardware command
+	 * queue update module registers on vblank.
+	 */
+	if (state->pending_config) {
+		mtk_ddp_comp_config(ovl, state->pending_width,
+				    state->pending_height,
+				    state->pending_vrefresh);
+
+		state->pending_config = false;
+	}
+
+	if (mtk_crtc->pending_planes) {
+		for (i = 0; i < OVL_LAYER_NR; i++) {
+			struct drm_plane *plane = &mtk_crtc->planes[i].base;
+			struct mtk_plane_state *plane_state;
+
+			plane_state = to_mtk_plane_state(plane->state);
+
+			if (plane_state->pending.config) {
+				mtk_ddp_comp_layer_config(ovl, i, plane_state);
+				plane_state->pending.config = false;
+			}
+		}
+		mtk_crtc->pending_planes = false;
+	}
+
+	mtk_drm_finish_page_flip(mtk_crtc);
+}
+
+int mtk_drm_crtc_create(struct drm_device *drm_dev,
+			const enum mtk_ddp_comp_id *path, unsigned int path_len)
+{
+	struct mtk_drm_private *priv = drm_dev->dev_private;
+	struct device *dev = drm_dev->dev;
+	struct mtk_drm_crtc *mtk_crtc;
+	enum drm_plane_type type;
+	unsigned int zpos;
+	int pipe = priv->num_pipes;
+	int ret;
+	int i;
+
+	for (i = 0; i < path_len; i++) {
+		enum mtk_ddp_comp_id comp_id = path[i];
+		struct device_node *node;
+
+		node = priv->comp_node[comp_id];
+		if (!node) {
+			dev_info(dev,
+				 "Not creating crtc %d because component %d is disabled or missing\n",
+				 pipe, comp_id);
+			return 0;
+		}
+	}
+
+	mtk_crtc = devm_kzalloc(dev, sizeof(*mtk_crtc), GFP_KERNEL);
+	if (!mtk_crtc)
+		return -ENOMEM;
+
+	mtk_crtc->config_regs = priv->config_regs;
+	mtk_crtc->ddp_comp_nr = path_len;
+	mtk_crtc->ddp_comp = devm_kmalloc_array(dev, mtk_crtc->ddp_comp_nr,
+						sizeof(*mtk_crtc->ddp_comp),
+						GFP_KERNEL);
+
+	mtk_crtc->mutex = mtk_disp_mutex_get(priv->mutex_dev, pipe);
+	if (IS_ERR(mtk_crtc->mutex)) {
+		ret = PTR_ERR(mtk_crtc->mutex);
+		dev_err(dev, "Failed to get mutex: %d\n", ret);
+		return ret;
+	}
+
+	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
+		enum mtk_ddp_comp_id comp_id = path[i];
+		struct mtk_ddp_comp *comp;
+		struct device_node *node;
+
+		node = priv->comp_node[comp_id];
+		comp = priv->ddp_comp[comp_id];
+		if (!comp) {
+			dev_err(dev, "Component %s not initialized\n",
+				node->full_name);
+			ret = -ENODEV;
+			goto unprepare;
+		}
+
+		ret = clk_prepare(comp->clk);
+		if (ret) {
+			dev_err(dev,
+				"Failed to prepare clock for component %s: %d\n",
+				node->full_name, ret);
+			goto unprepare;
+		}
+
+		mtk_crtc->ddp_comp[i] = comp;
+	}
+
+	for (zpos = 0; zpos < OVL_LAYER_NR; zpos++) {
+		type = (zpos == 0) ? DRM_PLANE_TYPE_PRIMARY :
+				(zpos == 1) ? DRM_PLANE_TYPE_CURSOR :
+						DRM_PLANE_TYPE_OVERLAY;
+		ret = mtk_plane_init(drm_dev, &mtk_crtc->planes[zpos],
+				     BIT(pipe), type, zpos);
+		if (ret)
+			goto unprepare;
+	}
+
+	ret = mtk_drm_crtc_init(drm_dev, mtk_crtc, &mtk_crtc->planes[0].base,
+				&mtk_crtc->planes[1].base, pipe);
+	if (ret < 0)
+		goto unprepare;
+
+	priv->crtc[pipe] = &mtk_crtc->base;
+	priv->num_pipes++;
+
+	return 0;
+
+unprepare:
+	while (--i >= 0)
+		clk_unprepare(mtk_crtc->ddp_comp[i]->clk);
+
+	return ret;
+}
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.h b/drivers/gpu/drm/mediatek/mtk_drm_crtc.h
new file mode 100644
index 0000000..81e5566
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MTK_DRM_CRTC_H
+#define MTK_DRM_CRTC_H
+
+#include <drm/drm_crtc.h>
+#include "mtk_drm_ddp_comp.h"
+#include "mtk_drm_plane.h"
+
+#define OVL_LAYER_NR	4
+
+int mtk_drm_crtc_enable_vblank(struct drm_device *drm, unsigned int pipe);
+void mtk_drm_crtc_disable_vblank(struct drm_device *drm, unsigned int pipe);
+void mtk_drm_crtc_check_flush(struct drm_crtc *crtc);
+void mtk_drm_crtc_commit(struct drm_crtc *crtc);
+void mtk_crtc_ddp_irq(struct drm_crtc *crtc, struct mtk_ddp_comp *ovl);
+int mtk_drm_crtc_create(struct drm_device *drm_dev,
+			const enum mtk_ddp_comp_id *path,
+			unsigned int path_len);
+
+#endif /* MTK_DRM_CRTC_H */
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
new file mode 100644
index 0000000..17ba9355
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include "mtk_drm_ddp.h"
+#include "mtk_drm_ddp_comp.h"
+
+#define DISP_REG_CONFIG_DISP_OVL0_MOUT_EN	0x040
+#define DISP_REG_CONFIG_DISP_OVL1_MOUT_EN	0x044
+#define DISP_REG_CONFIG_DISP_OD_MOUT_EN		0x048
+#define DISP_REG_CONFIG_DISP_GAMMA_MOUT_EN	0x04c
+#define DISP_REG_CONFIG_DISP_UFOE_MOUT_EN	0x050
+#define DISP_REG_CONFIG_DISP_COLOR0_SEL_IN	0x084
+#define DISP_REG_CONFIG_DISP_COLOR1_SEL_IN	0x088
+#define DISP_REG_CONFIG_DPI_SEL_IN		0x0ac
+#define DISP_REG_CONFIG_DISP_RDMA1_MOUT_EN	0x0c8
+#define DISP_REG_CONFIG_MMSYS_CG_CON0		0x100
+
+#define DISP_REG_MUTEX_EN(n)	(0x20 + 0x20 * (n))
+#define DISP_REG_MUTEX_RST(n)	(0x28 + 0x20 * (n))
+#define DISP_REG_MUTEX_MOD(n)	(0x2c + 0x20 * (n))
+#define DISP_REG_MUTEX_SOF(n)	(0x30 + 0x20 * (n))
+
+#define MUTEX_MOD_DISP_OVL0		BIT(11)
+#define MUTEX_MOD_DISP_OVL1		BIT(12)
+#define MUTEX_MOD_DISP_RDMA0		BIT(13)
+#define MUTEX_MOD_DISP_RDMA1		BIT(14)
+#define MUTEX_MOD_DISP_RDMA2		BIT(15)
+#define MUTEX_MOD_DISP_WDMA0		BIT(16)
+#define MUTEX_MOD_DISP_WDMA1		BIT(17)
+#define MUTEX_MOD_DISP_COLOR0		BIT(18)
+#define MUTEX_MOD_DISP_COLOR1		BIT(19)
+#define MUTEX_MOD_DISP_AAL		BIT(20)
+#define MUTEX_MOD_DISP_GAMMA		BIT(21)
+#define MUTEX_MOD_DISP_UFOE		BIT(22)
+#define MUTEX_MOD_DISP_PWM0		BIT(23)
+#define MUTEX_MOD_DISP_PWM1		BIT(24)
+#define MUTEX_MOD_DISP_OD		BIT(25)
+
+#define MUTEX_SOF_SINGLE_MODE		0
+#define MUTEX_SOF_DSI0			1
+#define MUTEX_SOF_DSI1			2
+#define MUTEX_SOF_DPI0			3
+
+#define OVL0_MOUT_EN_COLOR0		0x1
+#define OD_MOUT_EN_RDMA0		0x1
+#define UFOE_MOUT_EN_DSI0		0x1
+#define COLOR0_SEL_IN_OVL0		0x1
+#define OVL1_MOUT_EN_COLOR1		0x1
+#define GAMMA_MOUT_EN_RDMA1		0x1
+#define RDMA1_MOUT_DPI0			0x2
+#define DPI0_SEL_IN_RDMA1		0x1
+#define COLOR1_SEL_IN_OVL1		0x1
+
+struct mtk_disp_mutex {
+	int id;
+	bool claimed;
+};
+
+struct mtk_ddp {
+	struct device			*dev;
+	struct clk			*clk;
+	void __iomem			*regs;
+	struct mtk_disp_mutex		mutex[10];
+};
+
+static const unsigned int mutex_mod[DDP_COMPONENT_ID_MAX] = {
+	[DDP_COMPONENT_AAL] = MUTEX_MOD_DISP_AAL,
+	[DDP_COMPONENT_COLOR0] = MUTEX_MOD_DISP_COLOR0,
+	[DDP_COMPONENT_COLOR1] = MUTEX_MOD_DISP_COLOR1,
+	[DDP_COMPONENT_GAMMA] = MUTEX_MOD_DISP_GAMMA,
+	[DDP_COMPONENT_OD] = MUTEX_MOD_DISP_OD,
+	[DDP_COMPONENT_OVL0] = MUTEX_MOD_DISP_OVL0,
+	[DDP_COMPONENT_OVL1] = MUTEX_MOD_DISP_OVL1,
+	[DDP_COMPONENT_PWM0] = MUTEX_MOD_DISP_PWM0,
+	[DDP_COMPONENT_PWM1] = MUTEX_MOD_DISP_PWM1,
+	[DDP_COMPONENT_RDMA0] = MUTEX_MOD_DISP_RDMA0,
+	[DDP_COMPONENT_RDMA1] = MUTEX_MOD_DISP_RDMA1,
+	[DDP_COMPONENT_RDMA2] = MUTEX_MOD_DISP_RDMA2,
+	[DDP_COMPONENT_UFOE] = MUTEX_MOD_DISP_UFOE,
+	[DDP_COMPONENT_WDMA0] = MUTEX_MOD_DISP_WDMA0,
+	[DDP_COMPONENT_WDMA1] = MUTEX_MOD_DISP_WDMA1,
+};
+
+static unsigned int mtk_ddp_mout_en(enum mtk_ddp_comp_id cur,
+				    enum mtk_ddp_comp_id next,
+				    unsigned int *addr)
+{
+	unsigned int value;
+
+	if (cur == DDP_COMPONENT_OVL0 && next == DDP_COMPONENT_COLOR0) {
+		*addr = DISP_REG_CONFIG_DISP_OVL0_MOUT_EN;
+		value = OVL0_MOUT_EN_COLOR0;
+	} else if (cur == DDP_COMPONENT_OD && next == DDP_COMPONENT_RDMA0) {
+		*addr = DISP_REG_CONFIG_DISP_OD_MOUT_EN;
+		value = OD_MOUT_EN_RDMA0;
+	} else if (cur == DDP_COMPONENT_UFOE && next == DDP_COMPONENT_DSI0) {
+		*addr = DISP_REG_CONFIG_DISP_UFOE_MOUT_EN;
+		value = UFOE_MOUT_EN_DSI0;
+	} else if (cur == DDP_COMPONENT_OVL1 && next == DDP_COMPONENT_COLOR1) {
+		*addr = DISP_REG_CONFIG_DISP_OVL1_MOUT_EN;
+		value = OVL1_MOUT_EN_COLOR1;
+	} else if (cur == DDP_COMPONENT_GAMMA && next == DDP_COMPONENT_RDMA1) {
+		*addr = DISP_REG_CONFIG_DISP_GAMMA_MOUT_EN;
+		value = GAMMA_MOUT_EN_RDMA1;
+	} else if (cur == DDP_COMPONENT_RDMA1 && next == DDP_COMPONENT_DPI0) {
+		*addr = DISP_REG_CONFIG_DISP_RDMA1_MOUT_EN;
+		value = RDMA1_MOUT_DPI0;
+	} else {
+		value = 0;
+	}
+
+	return value;
+}
+
+static unsigned int mtk_ddp_sel_in(enum mtk_ddp_comp_id cur,
+				   enum mtk_ddp_comp_id next,
+				   unsigned int *addr)
+{
+	unsigned int value;
+
+	if (cur == DDP_COMPONENT_OVL0 && next == DDP_COMPONENT_COLOR0) {
+		*addr = DISP_REG_CONFIG_DISP_COLOR0_SEL_IN;
+		value = COLOR0_SEL_IN_OVL0;
+	} else if (cur == DDP_COMPONENT_RDMA1 && next == DDP_COMPONENT_DPI0) {
+		*addr = DISP_REG_CONFIG_DPI_SEL_IN;
+		value = DPI0_SEL_IN_RDMA1;
+	} else if (cur == DDP_COMPONENT_OVL1 && next == DDP_COMPONENT_COLOR1) {
+		*addr = DISP_REG_CONFIG_DISP_COLOR1_SEL_IN;
+		value = COLOR1_SEL_IN_OVL1;
+	} else {
+		value = 0;
+	}
+
+	return value;
+}
+
+void mtk_ddp_add_comp_to_path(void __iomem *config_regs,
+			      enum mtk_ddp_comp_id cur,
+			      enum mtk_ddp_comp_id next)
+{
+	unsigned int addr, value, reg;
+
+	value = mtk_ddp_mout_en(cur, next, &addr);
+	if (value) {
+		reg = readl_relaxed(config_regs + addr) | value;
+		writel_relaxed(reg, config_regs + addr);
+	}
+
+	value = mtk_ddp_sel_in(cur, next, &addr);
+	if (value) {
+		reg = readl_relaxed(config_regs + addr) | value;
+		writel_relaxed(reg, config_regs + addr);
+	}
+}
+
+void mtk_ddp_remove_comp_from_path(void __iomem *config_regs,
+				   enum mtk_ddp_comp_id cur,
+				   enum mtk_ddp_comp_id next)
+{
+	unsigned int addr, value, reg;
+
+	value = mtk_ddp_mout_en(cur, next, &addr);
+	if (value) {
+		reg = readl_relaxed(config_regs + addr) & ~value;
+		writel_relaxed(reg, config_regs + addr);
+	}
+
+	value = mtk_ddp_sel_in(cur, next, &addr);
+	if (value) {
+		reg = readl_relaxed(config_regs + addr) & ~value;
+		writel_relaxed(reg, config_regs + addr);
+	}
+}
+
+struct mtk_disp_mutex *mtk_disp_mutex_get(struct device *dev, unsigned int id)
+{
+	struct mtk_ddp *ddp = dev_get_drvdata(dev);
+
+	if (id >= 10)
+		return ERR_PTR(-EINVAL);
+	if (ddp->mutex[id].claimed)
+		return ERR_PTR(-EBUSY);
+
+	ddp->mutex[id].claimed = true;
+
+	return &ddp->mutex[id];
+}
+
+void mtk_disp_mutex_put(struct mtk_disp_mutex *mutex)
+{
+	struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
+					   mutex[mutex->id]);
+
+	WARN_ON(&ddp->mutex[mutex->id] != mutex);
+
+	mutex->claimed = false;
+}
+
+int mtk_disp_mutex_prepare(struct mtk_disp_mutex *mutex)
+{
+	struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
+					   mutex[mutex->id]);
+	return clk_prepare_enable(ddp->clk);
+}
+
+void mtk_disp_mutex_unprepare(struct mtk_disp_mutex *mutex)
+{
+	struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
+					   mutex[mutex->id]);
+	clk_disable_unprepare(ddp->clk);
+}
+
+void mtk_disp_mutex_add_comp(struct mtk_disp_mutex *mutex,
+			     enum mtk_ddp_comp_id id)
+{
+	struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
+					   mutex[mutex->id]);
+	unsigned int reg;
+
+	WARN_ON(&ddp->mutex[mutex->id] != mutex);
+
+	switch (id) {
+	case DDP_COMPONENT_DSI0:
+		reg = MUTEX_SOF_DSI0;
+		break;
+	case DDP_COMPONENT_DSI1:
+		reg = MUTEX_SOF_DSI0;
+		break;
+	case DDP_COMPONENT_DPI0:
+		reg = MUTEX_SOF_DPI0;
+		break;
+	default:
+		reg = readl_relaxed(ddp->regs + DISP_REG_MUTEX_MOD(mutex->id));
+		reg |= mutex_mod[id];
+		writel_relaxed(reg, ddp->regs + DISP_REG_MUTEX_MOD(mutex->id));
+		return;
+	}
+
+	writel_relaxed(reg, ddp->regs + DISP_REG_MUTEX_SOF(mutex->id));
+}
+
+void mtk_disp_mutex_remove_comp(struct mtk_disp_mutex *mutex,
+				enum mtk_ddp_comp_id id)
+{
+	struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
+					   mutex[mutex->id]);
+	unsigned int reg;
+
+	WARN_ON(&ddp->mutex[mutex->id] != mutex);
+
+	switch (id) {
+	case DDP_COMPONENT_DSI0:
+	case DDP_COMPONENT_DSI1:
+	case DDP_COMPONENT_DPI0:
+		writel_relaxed(MUTEX_SOF_SINGLE_MODE,
+			       ddp->regs + DISP_REG_MUTEX_SOF(mutex->id));
+		break;
+	default:
+		reg = readl_relaxed(ddp->regs + DISP_REG_MUTEX_MOD(mutex->id));
+		reg &= ~mutex_mod[id];
+		writel_relaxed(reg, ddp->regs + DISP_REG_MUTEX_MOD(mutex->id));
+		break;
+	}
+}
+
+void mtk_disp_mutex_enable(struct mtk_disp_mutex *mutex)
+{
+	struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
+					   mutex[mutex->id]);
+
+	WARN_ON(&ddp->mutex[mutex->id] != mutex);
+
+	writel(1, ddp->regs + DISP_REG_MUTEX_EN(mutex->id));
+}
+
+void mtk_disp_mutex_disable(struct mtk_disp_mutex *mutex)
+{
+	struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
+					   mutex[mutex->id]);
+
+	WARN_ON(&ddp->mutex[mutex->id] != mutex);
+
+	writel(0, ddp->regs + DISP_REG_MUTEX_EN(mutex->id));
+}
+
+static int mtk_ddp_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct mtk_ddp *ddp;
+	struct resource *regs;
+	int i;
+
+	ddp = devm_kzalloc(dev, sizeof(*ddp), GFP_KERNEL);
+	if (!ddp)
+		return -ENOMEM;
+
+	for (i = 0; i < 10; i++)
+		ddp->mutex[i].id = i;
+
+	ddp->clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(ddp->clk)) {
+		dev_err(dev, "Failed to get clock\n");
+		return PTR_ERR(ddp->clk);
+	}
+
+	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	ddp->regs = devm_ioremap_resource(dev, regs);
+	if (IS_ERR(ddp->regs)) {
+		dev_err(dev, "Failed to map mutex registers\n");
+		return PTR_ERR(ddp->regs);
+	}
+
+	platform_set_drvdata(pdev, ddp);
+
+	return 0;
+}
+
+static int mtk_ddp_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static const struct of_device_id ddp_driver_dt_match[] = {
+	{ .compatible = "mediatek,mt8173-disp-mutex" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, ddp_driver_dt_match);
+
+struct platform_driver mtk_ddp_driver = {
+	.probe		= mtk_ddp_probe,
+	.remove		= mtk_ddp_remove,
+	.driver		= {
+		.name	= "mediatek-ddp",
+		.owner	= THIS_MODULE,
+		.of_match_table = ddp_driver_dt_match,
+	},
+};
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp.h
new file mode 100644
index 0000000..92c1175
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MTK_DRM_DDP_H
+#define MTK_DRM_DDP_H
+
+#include "mtk_drm_ddp_comp.h"
+
+struct regmap;
+struct device;
+struct mtk_disp_mutex;
+
+void mtk_ddp_add_comp_to_path(void __iomem *config_regs,
+			      enum mtk_ddp_comp_id cur,
+			      enum mtk_ddp_comp_id next);
+void mtk_ddp_remove_comp_from_path(void __iomem *config_regs,
+				   enum mtk_ddp_comp_id cur,
+				   enum mtk_ddp_comp_id next);
+
+struct mtk_disp_mutex *mtk_disp_mutex_get(struct device *dev, unsigned int id);
+int mtk_disp_mutex_prepare(struct mtk_disp_mutex *mutex);
+void mtk_disp_mutex_add_comp(struct mtk_disp_mutex *mutex,
+			     enum mtk_ddp_comp_id id);
+void mtk_disp_mutex_enable(struct mtk_disp_mutex *mutex);
+void mtk_disp_mutex_disable(struct mtk_disp_mutex *mutex);
+void mtk_disp_mutex_remove_comp(struct mtk_disp_mutex *mutex,
+				enum mtk_ddp_comp_id id);
+void mtk_disp_mutex_unprepare(struct mtk_disp_mutex *mutex);
+void mtk_disp_mutex_put(struct mtk_disp_mutex *mutex);
+
+#endif /* MTK_DRM_DDP_H */
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
new file mode 100644
index 0000000..3970fcf
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ * Authors:
+ *	YT Shen <yt.shen@mediatek.com>
+ *	CK Hu <ck.hu@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.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <drm/drmP.h>
+#include "mtk_drm_drv.h"
+#include "mtk_drm_plane.h"
+#include "mtk_drm_ddp_comp.h"
+
+#define DISP_OD_EN				0x0000
+#define DISP_OD_INTEN				0x0008
+#define DISP_OD_INTSTA				0x000c
+#define DISP_OD_CFG				0x0020
+#define DISP_OD_SIZE				0x0030
+
+#define DISP_REG_UFO_START			0x0000
+
+#define DISP_COLOR_CFG_MAIN			0x0400
+#define DISP_COLOR_START			0x0c00
+#define DISP_COLOR_WIDTH			0x0c50
+#define DISP_COLOR_HEIGHT			0x0c54
+
+#define	OD_RELAY_MODE		BIT(0)
+
+#define	UFO_BYPASS		BIT(2)
+
+#define	COLOR_BYPASS_ALL	BIT(7)
+#define	COLOR_SEQ_SEL		BIT(13)
+
+static void mtk_color_config(struct mtk_ddp_comp *comp, unsigned int w,
+			     unsigned int h, unsigned int vrefresh)
+{
+	writel(w, comp->regs + DISP_COLOR_WIDTH);
+	writel(h, comp->regs + DISP_COLOR_HEIGHT);
+}
+
+static void mtk_color_start(struct mtk_ddp_comp *comp)
+{
+	writel(COLOR_BYPASS_ALL | COLOR_SEQ_SEL,
+	       comp->regs + DISP_COLOR_CFG_MAIN);
+	writel(0x1, comp->regs + DISP_COLOR_START);
+}
+
+static void mtk_od_config(struct mtk_ddp_comp *comp, unsigned int w,
+			  unsigned int h, unsigned int vrefresh)
+{
+	writel(w << 16 | h, comp->regs + DISP_OD_SIZE);
+}
+
+static void mtk_od_start(struct mtk_ddp_comp *comp)
+{
+	writel(OD_RELAY_MODE, comp->regs + DISP_OD_CFG);
+	writel(1, comp->regs + DISP_OD_EN);
+}
+
+static void mtk_ufoe_start(struct mtk_ddp_comp *comp)
+{
+	writel(UFO_BYPASS, comp->regs + DISP_REG_UFO_START);
+}
+
+static const struct mtk_ddp_comp_funcs ddp_color = {
+	.config = mtk_color_config,
+	.start = mtk_color_start,
+};
+
+static const struct mtk_ddp_comp_funcs ddp_od = {
+	.config = mtk_od_config,
+	.start = mtk_od_start,
+};
+
+static const struct mtk_ddp_comp_funcs ddp_ufoe = {
+	.start = mtk_ufoe_start,
+};
+
+static const char * const mtk_ddp_comp_stem[MTK_DDP_COMP_TYPE_MAX] = {
+	[MTK_DISP_OVL] = "ovl",
+	[MTK_DISP_RDMA] = "rdma",
+	[MTK_DISP_WDMA] = "wdma",
+	[MTK_DISP_COLOR] = "color",
+	[MTK_DISP_AAL] = "aal",
+	[MTK_DISP_GAMMA] = "gamma",
+	[MTK_DISP_UFOE] = "ufoe",
+	[MTK_DSI] = "dsi",
+	[MTK_DPI] = "dpi",
+	[MTK_DISP_PWM] = "pwm",
+	[MTK_DISP_MUTEX] = "mutex",
+	[MTK_DISP_OD] = "od",
+};
+
+struct mtk_ddp_comp_match {
+	enum mtk_ddp_comp_type type;
+	int alias_id;
+	const struct mtk_ddp_comp_funcs *funcs;
+};
+
+static const struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_ID_MAX] = {
+	[DDP_COMPONENT_AAL]	= { MTK_DISP_AAL,	0, NULL },
+	[DDP_COMPONENT_COLOR0]	= { MTK_DISP_COLOR,	0, &ddp_color },
+	[DDP_COMPONENT_COLOR1]	= { MTK_DISP_COLOR,	1, &ddp_color },
+	[DDP_COMPONENT_DPI0]	= { MTK_DPI,		0, NULL },
+	[DDP_COMPONENT_DSI0]	= { MTK_DSI,		0, NULL },
+	[DDP_COMPONENT_DSI1]	= { MTK_DSI,		1, NULL },
+	[DDP_COMPONENT_GAMMA]	= { MTK_DISP_GAMMA,	0, NULL },
+	[DDP_COMPONENT_OD]	= { MTK_DISP_OD,	0, &ddp_od },
+	[DDP_COMPONENT_OVL0]	= { MTK_DISP_OVL,	0, NULL },
+	[DDP_COMPONENT_OVL1]	= { MTK_DISP_OVL,	1, NULL },
+	[DDP_COMPONENT_PWM0]	= { MTK_DISP_PWM,	0, NULL },
+	[DDP_COMPONENT_RDMA0]	= { MTK_DISP_RDMA,	0, NULL },
+	[DDP_COMPONENT_RDMA1]	= { MTK_DISP_RDMA,	1, NULL },
+	[DDP_COMPONENT_RDMA2]	= { MTK_DISP_RDMA,	2, NULL },
+	[DDP_COMPONENT_UFOE]	= { MTK_DISP_UFOE,	0, &ddp_ufoe },
+	[DDP_COMPONENT_WDMA0]	= { MTK_DISP_WDMA,	0, NULL },
+	[DDP_COMPONENT_WDMA1]	= { MTK_DISP_WDMA,	1, NULL },
+};
+
+int mtk_ddp_comp_get_id(struct device_node *node,
+			enum mtk_ddp_comp_type comp_type)
+{
+	int id = of_alias_get_id(node, mtk_ddp_comp_stem[comp_type]);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(mtk_ddp_matches); i++) {
+		if (comp_type == mtk_ddp_matches[i].type &&
+		    (id < 0 || id == mtk_ddp_matches[i].alias_id))
+			return i;
+	}
+
+	return -EINVAL;
+}
+
+int mtk_ddp_comp_init(struct device *dev, struct device_node *node,
+		      struct mtk_ddp_comp *comp, enum mtk_ddp_comp_id comp_id,
+		      const struct mtk_ddp_comp_funcs *funcs)
+{
+	enum mtk_ddp_comp_type type;
+	struct device_node *larb_node;
+	struct platform_device *larb_pdev;
+
+	if (comp_id < 0 || comp_id >= DDP_COMPONENT_ID_MAX)
+		return -EINVAL;
+
+	comp->id = comp_id;
+	comp->funcs = funcs ?: mtk_ddp_matches[comp_id].funcs;
+
+	if (comp_id == DDP_COMPONENT_DPI0 ||
+	    comp_id == DDP_COMPONENT_DSI0 ||
+	    comp_id == DDP_COMPONENT_PWM0) {
+		comp->regs = NULL;
+		comp->clk = NULL;
+		comp->irq = 0;
+		return 0;
+	}
+
+	comp->regs = of_iomap(node, 0);
+	comp->irq = of_irq_get(node, 0);
+	comp->clk = of_clk_get(node, 0);
+	if (IS_ERR(comp->clk))
+		comp->clk = NULL;
+
+	type = mtk_ddp_matches[comp_id].type;
+
+	/* Only DMA capable components need the LARB property */
+	comp->larb_dev = NULL;
+	if (type != MTK_DISP_OVL &&
+	    type != MTK_DISP_RDMA &&
+	    type != MTK_DISP_WDMA)
+		return 0;
+
+	larb_node = of_parse_phandle(node, "mediatek,larb", 0);
+	if (!larb_node) {
+		dev_err(dev,
+			"Missing mediadek,larb phandle in %s node\n",
+			node->full_name);
+		return -EINVAL;
+	}
+
+	larb_pdev = of_find_device_by_node(larb_node);
+	if (!larb_pdev) {
+		dev_warn(dev, "Waiting for larb device %s\n",
+			 larb_node->full_name);
+		of_node_put(larb_node);
+		return -EPROBE_DEFER;
+	}
+	of_node_put(larb_node);
+
+	comp->larb_dev = &larb_pdev->dev;
+
+	return 0;
+}
+
+int mtk_ddp_comp_register(struct drm_device *drm, struct mtk_ddp_comp *comp)
+{
+	struct mtk_drm_private *private = drm->dev_private;
+
+	if (private->ddp_comp[comp->id])
+		return -EBUSY;
+
+	private->ddp_comp[comp->id] = comp;
+	return 0;
+}
+
+void mtk_ddp_comp_unregister(struct drm_device *drm, struct mtk_ddp_comp *comp)
+{
+	struct mtk_drm_private *private = drm->dev_private;
+
+	private->ddp_comp[comp->id] = NULL;
+}
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
new file mode 100644
index 0000000..6b13ba9
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MTK_DRM_DDP_COMP_H
+#define MTK_DRM_DDP_COMP_H
+
+#include <linux/io.h>
+
+struct device;
+struct device_node;
+struct drm_crtc;
+struct drm_device;
+struct mtk_plane_state;
+
+enum mtk_ddp_comp_type {
+	MTK_DISP_OVL,
+	MTK_DISP_RDMA,
+	MTK_DISP_WDMA,
+	MTK_DISP_COLOR,
+	MTK_DISP_AAL,
+	MTK_DISP_GAMMA,
+	MTK_DISP_UFOE,
+	MTK_DSI,
+	MTK_DPI,
+	MTK_DISP_PWM,
+	MTK_DISP_MUTEX,
+	MTK_DISP_OD,
+	MTK_DDP_COMP_TYPE_MAX,
+};
+
+enum mtk_ddp_comp_id {
+	DDP_COMPONENT_AAL,
+	DDP_COMPONENT_COLOR0,
+	DDP_COMPONENT_COLOR1,
+	DDP_COMPONENT_DPI0,
+	DDP_COMPONENT_DSI0,
+	DDP_COMPONENT_DSI1,
+	DDP_COMPONENT_GAMMA,
+	DDP_COMPONENT_OD,
+	DDP_COMPONENT_OVL0,
+	DDP_COMPONENT_OVL1,
+	DDP_COMPONENT_PWM0,
+	DDP_COMPONENT_PWM1,
+	DDP_COMPONENT_RDMA0,
+	DDP_COMPONENT_RDMA1,
+	DDP_COMPONENT_RDMA2,
+	DDP_COMPONENT_UFOE,
+	DDP_COMPONENT_WDMA0,
+	DDP_COMPONENT_WDMA1,
+	DDP_COMPONENT_ID_MAX,
+};
+
+struct mtk_ddp_comp;
+
+struct mtk_ddp_comp_funcs {
+	void (*config)(struct mtk_ddp_comp *comp, unsigned int w,
+		       unsigned int h, unsigned int vrefresh);
+	void (*start)(struct mtk_ddp_comp *comp);
+	void (*stop)(struct mtk_ddp_comp *comp);
+	void (*enable_vblank)(struct mtk_ddp_comp *comp, struct drm_crtc *crtc);
+	void (*disable_vblank)(struct mtk_ddp_comp *comp);
+	void (*layer_on)(struct mtk_ddp_comp *comp, unsigned int idx);
+	void (*layer_off)(struct mtk_ddp_comp *comp, unsigned int idx);
+	void (*layer_config)(struct mtk_ddp_comp *comp, unsigned int idx,
+			     struct mtk_plane_state *state);
+};
+
+struct mtk_ddp_comp {
+	struct clk *clk;
+	void __iomem *regs;
+	int irq;
+	struct device *larb_dev;
+	enum mtk_ddp_comp_id id;
+	const struct mtk_ddp_comp_funcs *funcs;
+};
+
+static inline void mtk_ddp_comp_config(struct mtk_ddp_comp *comp,
+				       unsigned int w, unsigned int h,
+				       unsigned int vrefresh)
+{
+	if (comp->funcs && comp->funcs->config)
+		comp->funcs->config(comp, w, h, vrefresh);
+}
+
+static inline void mtk_ddp_comp_start(struct mtk_ddp_comp *comp)
+{
+	if (comp->funcs && comp->funcs->start)
+		comp->funcs->start(comp);
+}
+
+static inline void mtk_ddp_comp_stop(struct mtk_ddp_comp *comp)
+{
+	if (comp->funcs && comp->funcs->stop)
+		comp->funcs->stop(comp);
+}
+
+static inline void mtk_ddp_comp_enable_vblank(struct mtk_ddp_comp *comp,
+					      struct drm_crtc *crtc)
+{
+	if (comp->funcs && comp->funcs->enable_vblank)
+		comp->funcs->enable_vblank(comp, crtc);
+}
+
+static inline void mtk_ddp_comp_disable_vblank(struct mtk_ddp_comp *comp)
+{
+	if (comp->funcs && comp->funcs->disable_vblank)
+		comp->funcs->disable_vblank(comp);
+}
+
+static inline void mtk_ddp_comp_layer_on(struct mtk_ddp_comp *comp,
+					 unsigned int idx)
+{
+	if (comp->funcs && comp->funcs->layer_on)
+		comp->funcs->layer_on(comp, idx);
+}
+
+static inline void mtk_ddp_comp_layer_off(struct mtk_ddp_comp *comp,
+					  unsigned int idx)
+{
+	if (comp->funcs && comp->funcs->layer_off)
+		comp->funcs->layer_off(comp, idx);
+}
+
+static inline void mtk_ddp_comp_layer_config(struct mtk_ddp_comp *comp,
+					     unsigned int idx,
+					     struct mtk_plane_state *state)
+{
+	if (comp->funcs && comp->funcs->layer_config)
+		comp->funcs->layer_config(comp, idx, state);
+}
+
+int mtk_ddp_comp_get_id(struct device_node *node,
+			enum mtk_ddp_comp_type comp_type);
+int mtk_ddp_comp_init(struct device *dev, struct device_node *comp_node,
+		      struct mtk_ddp_comp *comp, enum mtk_ddp_comp_id comp_id,
+		      const struct mtk_ddp_comp_funcs *funcs);
+int mtk_ddp_comp_register(struct drm_device *drm, struct mtk_ddp_comp *comp);
+void mtk_ddp_comp_unregister(struct drm_device *drm, struct mtk_ddp_comp *comp);
+
+#endif /* MTK_DRM_DDP_COMP_H */
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
new file mode 100644
index 0000000..e6e5654
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@ -0,0 +1,558 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ * Author: YT SHEN <yt.shen@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 <drm/drmP.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <linux/component.h>
+#include <linux/iommu.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+
+#include "mtk_drm_crtc.h"
+#include "mtk_drm_ddp.h"
+#include "mtk_drm_ddp_comp.h"
+#include "mtk_drm_drv.h"
+#include "mtk_drm_fb.h"
+#include "mtk_drm_gem.h"
+
+#define DRIVER_NAME "mediatek"
+#define DRIVER_DESC "Mediatek SoC DRM"
+#define DRIVER_DATE "20150513"
+#define DRIVER_MAJOR 1
+#define DRIVER_MINOR 0
+
+static void mtk_atomic_schedule(struct mtk_drm_private *private,
+				struct drm_atomic_state *state)
+{
+	private->commit.state = state;
+	schedule_work(&private->commit.work);
+}
+
+static void mtk_atomic_wait_for_fences(struct drm_atomic_state *state)
+{
+	struct drm_plane *plane;
+	struct drm_plane_state *plane_state;
+	int i;
+
+	for_each_plane_in_state(state, plane, plane_state, i)
+		mtk_fb_wait(plane->state->fb);
+}
+
+static void mtk_atomic_complete(struct mtk_drm_private *private,
+				struct drm_atomic_state *state)
+{
+	struct drm_device *drm = private->drm;
+
+	mtk_atomic_wait_for_fences(state);
+
+	drm_atomic_helper_commit_modeset_disables(drm, state);
+	drm_atomic_helper_commit_planes(drm, state, false);
+	drm_atomic_helper_commit_modeset_enables(drm, state);
+	drm_atomic_helper_wait_for_vblanks(drm, state);
+	drm_atomic_helper_cleanup_planes(drm, state);
+	drm_atomic_state_free(state);
+}
+
+static void mtk_atomic_work(struct work_struct *work)
+{
+	struct mtk_drm_private *private = container_of(work,
+			struct mtk_drm_private, commit.work);
+
+	mtk_atomic_complete(private, private->commit.state);
+}
+
+static int mtk_atomic_commit(struct drm_device *drm,
+			     struct drm_atomic_state *state,
+			     bool async)
+{
+	struct mtk_drm_private *private = drm->dev_private;
+	int ret;
+
+	ret = drm_atomic_helper_prepare_planes(drm, state);
+	if (ret)
+		return ret;
+
+	mutex_lock(&private->commit.lock);
+	flush_work(&private->commit.work);
+
+	drm_atomic_helper_swap_state(drm, state);
+
+	if (async)
+		mtk_atomic_schedule(private, state);
+	else
+		mtk_atomic_complete(private, state);
+
+	mutex_unlock(&private->commit.lock);
+
+	return 0;
+}
+
+static const struct drm_mode_config_funcs mtk_drm_mode_config_funcs = {
+	.fb_create = mtk_drm_mode_fb_create,
+	.atomic_check = drm_atomic_helper_check,
+	.atomic_commit = mtk_atomic_commit,
+};
+
+static const enum mtk_ddp_comp_id mtk_ddp_main[] = {
+	DDP_COMPONENT_OVL0,
+	DDP_COMPONENT_COLOR0,
+	DDP_COMPONENT_AAL,
+	DDP_COMPONENT_OD,
+	DDP_COMPONENT_RDMA0,
+	DDP_COMPONENT_UFOE,
+	DDP_COMPONENT_DSI0,
+	DDP_COMPONENT_PWM0,
+};
+
+static const enum mtk_ddp_comp_id mtk_ddp_ext[] = {
+	DDP_COMPONENT_OVL1,
+	DDP_COMPONENT_COLOR1,
+	DDP_COMPONENT_GAMMA,
+	DDP_COMPONENT_RDMA1,
+	DDP_COMPONENT_DPI0,
+};
+
+static int mtk_drm_kms_init(struct drm_device *drm)
+{
+	struct mtk_drm_private *private = drm->dev_private;
+	struct platform_device *pdev;
+	struct device_node *np;
+	int ret;
+
+	if (!iommu_present(&platform_bus_type))
+		return -EPROBE_DEFER;
+
+	pdev = of_find_device_by_node(private->mutex_node);
+	if (!pdev) {
+		dev_err(drm->dev, "Waiting for disp-mutex device %s\n",
+			private->mutex_node->full_name);
+		of_node_put(private->mutex_node);
+		return -EPROBE_DEFER;
+	}
+	private->mutex_dev = &pdev->dev;
+
+	drm_mode_config_init(drm);
+
+	drm->mode_config.min_width = 64;
+	drm->mode_config.min_height = 64;
+
+	/*
+	 * set max width and height as default value(4096x4096).
+	 * this value would be used to check framebuffer size limitation
+	 * at drm_mode_addfb().
+	 */
+	drm->mode_config.max_width = 4096;
+	drm->mode_config.max_height = 4096;
+	drm->mode_config.funcs = &mtk_drm_mode_config_funcs;
+
+	ret = component_bind_all(drm->dev, drm);
+	if (ret)
+		goto err_config_cleanup;
+
+	/*
+	 * We currently support two fixed data streams, each optional,
+	 * and each statically assigned to a crtc:
+	 * OVL0 -> COLOR0 -> AAL -> OD -> RDMA0 -> UFOE -> DSI0 ...
+	 */
+	ret = mtk_drm_crtc_create(drm, mtk_ddp_main, ARRAY_SIZE(mtk_ddp_main));
+	if (ret < 0)
+		goto err_component_unbind;
+	/* ... and OVL1 -> COLOR1 -> GAMMA -> RDMA1 -> DPI0. */
+	ret = mtk_drm_crtc_create(drm, mtk_ddp_ext, ARRAY_SIZE(mtk_ddp_ext));
+	if (ret < 0)
+		goto err_component_unbind;
+
+	/* Use OVL device for all DMA memory allocations */
+	np = private->comp_node[mtk_ddp_main[0]] ?:
+	     private->comp_node[mtk_ddp_ext[0]];
+	pdev = of_find_device_by_node(np);
+	if (!pdev) {
+		ret = -ENODEV;
+		dev_err(drm->dev, "Need at least one OVL device\n");
+		goto err_component_unbind;
+	}
+
+	private->dma_dev = &pdev->dev;
+
+	/*
+	 * We don't use the drm_irq_install() helpers provided by the DRM
+	 * core, so we need to set this manually in order to allow the
+	 * DRM_IOCTL_WAIT_VBLANK to operate correctly.
+	 */
+	drm->irq_enabled = true;
+	ret = drm_vblank_init(drm, MAX_CRTC);
+	if (ret < 0)
+		goto err_component_unbind;
+
+	drm_kms_helper_poll_init(drm);
+	drm_mode_config_reset(drm);
+
+	return 0;
+
+err_component_unbind:
+	component_unbind_all(drm->dev, drm);
+err_config_cleanup:
+	drm_mode_config_cleanup(drm);
+
+	return ret;
+}
+
+static void mtk_drm_kms_deinit(struct drm_device *drm)
+{
+	drm_kms_helper_poll_fini(drm);
+
+	drm_vblank_cleanup(drm);
+	component_unbind_all(drm->dev, drm);
+	drm_mode_config_cleanup(drm);
+}
+
+static const struct file_operations mtk_drm_fops = {
+	.owner = THIS_MODULE,
+	.open = drm_open,
+	.release = drm_release,
+	.unlocked_ioctl = drm_ioctl,
+	.mmap = mtk_drm_gem_mmap,
+	.poll = drm_poll,
+	.read = drm_read,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = drm_compat_ioctl,
+#endif
+};
+
+static struct drm_driver mtk_drm_driver = {
+	.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME |
+			   DRIVER_ATOMIC,
+
+	.get_vblank_counter = drm_vblank_count,
+	.enable_vblank = mtk_drm_crtc_enable_vblank,
+	.disable_vblank = mtk_drm_crtc_disable_vblank,
+
+	.gem_free_object = mtk_drm_gem_free_object,
+	.gem_vm_ops = &drm_gem_cma_vm_ops,
+	.dumb_create = mtk_drm_gem_dumb_create,
+	.dumb_map_offset = mtk_drm_gem_dumb_map_offset,
+	.dumb_destroy = drm_gem_dumb_destroy,
+
+	.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+	.gem_prime_export = drm_gem_prime_export,
+	.gem_prime_import = drm_gem_prime_import,
+	.gem_prime_get_sg_table = mtk_gem_prime_get_sg_table,
+	.gem_prime_import_sg_table = mtk_gem_prime_import_sg_table,
+	.gem_prime_mmap = mtk_drm_gem_mmap_buf,
+	.fops = &mtk_drm_fops,
+
+	.name = DRIVER_NAME,
+	.desc = DRIVER_DESC,
+	.date = DRIVER_DATE,
+	.major = DRIVER_MAJOR,
+	.minor = DRIVER_MINOR,
+};
+
+static int compare_of(struct device *dev, void *data)
+{
+	return dev->of_node == data;
+}
+
+static int mtk_drm_bind(struct device *dev)
+{
+	struct mtk_drm_private *private = dev_get_drvdata(dev);
+	struct drm_device *drm;
+	int ret;
+
+	drm = drm_dev_alloc(&mtk_drm_driver, dev);
+	if (!drm)
+		return -ENOMEM;
+
+	drm_dev_set_unique(drm, dev_name(dev));
+
+	ret = drm_dev_register(drm, 0);
+	if (ret < 0)
+		goto err_free;
+
+	drm->dev_private = private;
+	private->drm = drm;
+
+	ret = mtk_drm_kms_init(drm);
+	if (ret < 0)
+		goto err_unregister;
+
+	return 0;
+
+err_unregister:
+	drm_dev_unregister(drm);
+err_free:
+	drm_dev_unref(drm);
+	return ret;
+}
+
+static void mtk_drm_unbind(struct device *dev)
+{
+	struct mtk_drm_private *private = dev_get_drvdata(dev);
+
+	drm_put_dev(private->drm);
+	private->drm = NULL;
+}
+
+static const struct component_master_ops mtk_drm_ops = {
+	.bind		= mtk_drm_bind,
+	.unbind		= mtk_drm_unbind,
+};
+
+static const struct of_device_id mtk_ddp_comp_dt_ids[] = {
+	{ .compatible = "mediatek,mt8173-disp-ovl",   .data = (void *)MTK_DISP_OVL },
+	{ .compatible = "mediatek,mt8173-disp-rdma",  .data = (void *)MTK_DISP_RDMA },
+	{ .compatible = "mediatek,mt8173-disp-wdma",  .data = (void *)MTK_DISP_WDMA },
+	{ .compatible = "mediatek,mt8173-disp-color", .data = (void *)MTK_DISP_COLOR },
+	{ .compatible = "mediatek,mt8173-disp-aal",   .data = (void *)MTK_DISP_AAL},
+	{ .compatible = "mediatek,mt8173-disp-gamma", .data = (void *)MTK_DISP_GAMMA, },
+	{ .compatible = "mediatek,mt8173-disp-ufoe",  .data = (void *)MTK_DISP_UFOE },
+	{ .compatible = "mediatek,mt8173-dsi",        .data = (void *)MTK_DSI },
+	{ .compatible = "mediatek,mt8173-dpi",        .data = (void *)MTK_DPI },
+	{ .compatible = "mediatek,mt8173-disp-mutex", .data = (void *)MTK_DISP_MUTEX },
+	{ .compatible = "mediatek,mt8173-disp-pwm",   .data = (void *)MTK_DISP_PWM },
+	{ .compatible = "mediatek,mt8173-disp-od",    .data = (void *)MTK_DISP_OD },
+	{ }
+};
+
+static int mtk_drm_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct mtk_drm_private *private;
+	struct resource *mem;
+	struct device_node *node;
+	struct component_match *match = NULL;
+	int ret;
+	int i;
+
+	private = devm_kzalloc(dev, sizeof(*private), GFP_KERNEL);
+	if (!private)
+		return -ENOMEM;
+
+	mutex_init(&private->commit.lock);
+	INIT_WORK(&private->commit.work, mtk_atomic_work);
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	private->config_regs = devm_ioremap_resource(dev, mem);
+	if (IS_ERR(private->config_regs)) {
+		ret = PTR_ERR(private->config_regs);
+		dev_err(dev, "Failed to ioremap mmsys-config resource: %d\n",
+			ret);
+		return ret;
+	}
+
+	/* Iterate over sibling DISP function blocks */
+	for_each_child_of_node(dev->of_node->parent, node) {
+		const struct of_device_id *of_id;
+		enum mtk_ddp_comp_type comp_type;
+		int comp_id;
+
+		of_id = of_match_node(mtk_ddp_comp_dt_ids, node);
+		if (!of_id)
+			continue;
+
+		if (!of_device_is_available(node)) {
+			dev_dbg(dev, "Skipping disabled component %s\n",
+				node->full_name);
+			continue;
+		}
+
+		comp_type = (enum mtk_ddp_comp_type)of_id->data;
+
+		if (comp_type == MTK_DISP_MUTEX) {
+			private->mutex_node = of_node_get(node);
+			continue;
+		}
+
+		comp_id = mtk_ddp_comp_get_id(node, comp_type);
+		if (comp_id < 0) {
+			dev_warn(dev, "Skipping unknown component %s\n",
+				 node->full_name);
+			continue;
+		}
+
+		private->comp_node[comp_id] = of_node_get(node);
+
+		/*
+		 * Currently only the OVL, RDMA, DSI, and DPI blocks have
+		 * separate component platform drivers and initialize their own
+		 * DDP component structure. The others are initialized here.
+		 */
+		if (comp_type == MTK_DISP_OVL ||
+		    comp_type == MTK_DISP_RDMA ||
+		    comp_type == MTK_DSI ||
+		    comp_type == MTK_DPI) {
+			dev_info(dev, "Adding component match for %s\n",
+				 node->full_name);
+			component_match_add(dev, &match, compare_of, node);
+		} else {
+			struct mtk_ddp_comp *comp;
+
+			comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL);
+			if (!comp) {
+				ret = -ENOMEM;
+				goto err_node;
+			}
+
+			ret = mtk_ddp_comp_init(dev, node, comp, comp_id, NULL);
+			if (ret)
+				goto err_node;
+
+			private->ddp_comp[comp_id] = comp;
+		}
+	}
+
+	if (!private->mutex_node) {
+		dev_err(dev, "Failed to find disp-mutex node\n");
+		ret = -ENODEV;
+		goto err_node;
+	}
+
+	pm_runtime_enable(dev);
+
+	platform_set_drvdata(pdev, private);
+
+	ret = component_master_add_with_match(dev, &mtk_drm_ops, match);
+	if (ret)
+		goto err_pm;
+
+	return 0;
+
+err_pm:
+	pm_runtime_disable(dev);
+err_node:
+	of_node_put(private->mutex_node);
+	for (i = 0; i < DDP_COMPONENT_ID_MAX; i++)
+		of_node_put(private->comp_node[i]);
+	return ret;
+}
+
+static int mtk_drm_remove(struct platform_device *pdev)
+{
+	struct mtk_drm_private *private = platform_get_drvdata(pdev);
+	struct drm_device *drm = private->drm;
+	int i;
+
+	drm_connector_unregister_all(drm);
+	drm_dev_unregister(drm);
+	mtk_drm_kms_deinit(drm);
+	drm_dev_unref(drm);
+
+	component_master_del(&pdev->dev, &mtk_drm_ops);
+	pm_runtime_disable(&pdev->dev);
+	of_node_put(private->mutex_node);
+	for (i = 0; i < DDP_COMPONENT_ID_MAX; i++)
+		of_node_put(private->comp_node[i]);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mtk_drm_sys_suspend(struct device *dev)
+{
+	struct mtk_drm_private *private = dev_get_drvdata(dev);
+	struct drm_device *drm = private->drm;
+
+	drm_kms_helper_poll_disable(drm);
+
+	private->suspend_state = drm_atomic_helper_suspend(drm);
+	if (IS_ERR(private->suspend_state)) {
+		drm_kms_helper_poll_enable(drm);
+		return PTR_ERR(private->suspend_state);
+	}
+
+	DRM_DEBUG_DRIVER("mtk_drm_sys_suspend\n");
+	return 0;
+}
+
+static int mtk_drm_sys_resume(struct device *dev)
+{
+	struct mtk_drm_private *private = dev_get_drvdata(dev);
+	struct drm_device *drm = private->drm;
+
+	drm_atomic_helper_resume(drm, private->suspend_state);
+	drm_kms_helper_poll_enable(drm);
+
+	DRM_DEBUG_DRIVER("mtk_drm_sys_resume\n");
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(mtk_drm_pm_ops, mtk_drm_sys_suspend,
+			 mtk_drm_sys_resume);
+
+static const struct of_device_id mtk_drm_of_ids[] = {
+	{ .compatible = "mediatek,mt8173-mmsys", },
+	{ }
+};
+
+static struct platform_driver mtk_drm_platform_driver = {
+	.probe	= mtk_drm_probe,
+	.remove	= mtk_drm_remove,
+	.driver	= {
+		.name	= "mediatek-drm",
+		.of_match_table = mtk_drm_of_ids,
+		.pm     = &mtk_drm_pm_ops,
+	},
+};
+
+static struct platform_driver * const mtk_drm_drivers[] = {
+	&mtk_ddp_driver,
+	&mtk_disp_ovl_driver,
+	&mtk_disp_rdma_driver,
+	&mtk_drm_platform_driver,
+};
+
+static int __init mtk_drm_init(void)
+{
+	int ret;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(mtk_drm_drivers); i++) {
+		ret = platform_driver_register(mtk_drm_drivers[i]);
+		if (ret < 0) {
+			pr_err("Failed to register %s driver: %d\n",
+			       mtk_drm_drivers[i]->driver.name, ret);
+			goto err;
+		}
+	}
+
+	return 0;
+
+err:
+	while (--i >= 0)
+		platform_driver_unregister(mtk_drm_drivers[i]);
+
+	return ret;
+}
+
+static void __exit mtk_drm_exit(void)
+{
+	int i;
+
+	for (i = ARRAY_SIZE(mtk_drm_drivers) - 1; i >= 0; i--)
+		platform_driver_unregister(mtk_drm_drivers[i]);
+}
+
+module_init(mtk_drm_init);
+module_exit(mtk_drm_exit);
+
+MODULE_AUTHOR("YT SHEN <yt.shen@mediatek.com>");
+MODULE_DESCRIPTION("Mediatek SoC DRM driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.h b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
new file mode 100644
index 0000000..27dc8fa
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MTK_DRM_DRV_H
+#define MTK_DRM_DRV_H
+
+#include <linux/io.h>
+#include "mtk_drm_ddp_comp.h"
+
+#define MAX_CRTC	2
+#define MAX_CONNECTOR	2
+
+struct device;
+struct device_node;
+struct drm_crtc;
+struct drm_device;
+struct drm_fb_helper;
+struct drm_property;
+struct regmap;
+
+struct mtk_drm_private {
+	struct drm_device *drm;
+	struct device *dma_dev;
+
+	struct drm_crtc *crtc[MAX_CRTC];
+	unsigned int num_pipes;
+
+	struct device_node *mutex_node;
+	struct device *mutex_dev;
+	void __iomem *config_regs;
+	struct device_node *comp_node[DDP_COMPONENT_ID_MAX];
+	struct mtk_ddp_comp *ddp_comp[DDP_COMPONENT_ID_MAX];
+
+	struct {
+		struct drm_atomic_state *state;
+		struct work_struct work;
+		struct mutex lock;
+	} commit;
+
+	struct drm_atomic_state *suspend_state;
+};
+
+extern struct platform_driver mtk_ddp_driver;
+extern struct platform_driver mtk_disp_ovl_driver;
+extern struct platform_driver mtk_disp_rdma_driver;
+
+#endif /* MTK_DRM_DRV_H */
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_fb.c b/drivers/gpu/drm/mediatek/mtk_drm_fb.c
new file mode 100644
index 0000000..33d30c1
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_fb.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_gem.h>
+#include <linux/dma-buf.h>
+#include <linux/reservation.h>
+
+#include "mtk_drm_drv.h"
+#include "mtk_drm_fb.h"
+#include "mtk_drm_gem.h"
+
+/*
+ * mtk specific framebuffer structure.
+ *
+ * @fb: drm framebuffer object.
+ * @gem_obj: array of gem objects.
+ */
+struct mtk_drm_fb {
+	struct drm_framebuffer	base;
+	/* For now we only support a single plane */
+	struct drm_gem_object	*gem_obj;
+};
+
+#define to_mtk_fb(x) container_of(x, struct mtk_drm_fb, base)
+
+struct drm_gem_object *mtk_fb_get_gem_obj(struct drm_framebuffer *fb)
+{
+	struct mtk_drm_fb *mtk_fb = to_mtk_fb(fb);
+
+	return mtk_fb->gem_obj;
+}
+
+static int mtk_drm_fb_create_handle(struct drm_framebuffer *fb,
+				    struct drm_file *file_priv,
+				    unsigned int *handle)
+{
+	struct mtk_drm_fb *mtk_fb = to_mtk_fb(fb);
+
+	return drm_gem_handle_create(file_priv, mtk_fb->gem_obj, handle);
+}
+
+static void mtk_drm_fb_destroy(struct drm_framebuffer *fb)
+{
+	struct mtk_drm_fb *mtk_fb = to_mtk_fb(fb);
+
+	drm_framebuffer_cleanup(fb);
+
+	drm_gem_object_unreference_unlocked(mtk_fb->gem_obj);
+
+	kfree(mtk_fb);
+}
+
+static const struct drm_framebuffer_funcs mtk_drm_fb_funcs = {
+	.create_handle = mtk_drm_fb_create_handle,
+	.destroy = mtk_drm_fb_destroy,
+};
+
+static struct mtk_drm_fb *mtk_drm_framebuffer_init(struct drm_device *dev,
+					const struct drm_mode_fb_cmd2 *mode,
+					struct drm_gem_object *obj)
+{
+	struct mtk_drm_fb *mtk_fb;
+	int ret;
+
+	if (drm_format_num_planes(mode->pixel_format) != 1)
+		return ERR_PTR(-EINVAL);
+
+	mtk_fb = kzalloc(sizeof(*mtk_fb), GFP_KERNEL);
+	if (!mtk_fb)
+		return ERR_PTR(-ENOMEM);
+
+	drm_helper_mode_fill_fb_struct(&mtk_fb->base, mode);
+
+	mtk_fb->gem_obj = obj;
+
+	ret = drm_framebuffer_init(dev, &mtk_fb->base, &mtk_drm_fb_funcs);
+	if (ret) {
+		DRM_ERROR("failed to initialize framebuffer\n");
+		kfree(mtk_fb);
+		return ERR_PTR(ret);
+	}
+
+	return mtk_fb;
+}
+
+/*
+ * Wait for any exclusive fence in fb's gem object's reservation object.
+ *
+ * Returns -ERESTARTSYS if interrupted, else 0.
+ */
+int mtk_fb_wait(struct drm_framebuffer *fb)
+{
+	struct drm_gem_object *gem;
+	struct reservation_object *resv;
+	long ret;
+
+	if (!fb)
+		return 0;
+
+	gem = mtk_fb_get_gem_obj(fb);
+	if (!gem || !gem->dma_buf || !gem->dma_buf->resv)
+		return 0;
+
+	resv = gem->dma_buf->resv;
+	ret = reservation_object_wait_timeout_rcu(resv, false, true,
+						  MAX_SCHEDULE_TIMEOUT);
+	/* MAX_SCHEDULE_TIMEOUT on success, -ERESTARTSYS if interrupted */
+	if (WARN_ON(ret < 0))
+		return ret;
+
+	return 0;
+}
+
+struct drm_framebuffer *mtk_drm_mode_fb_create(struct drm_device *dev,
+					       struct drm_file *file,
+					       const struct drm_mode_fb_cmd2 *cmd)
+{
+	struct mtk_drm_fb *mtk_fb;
+	struct drm_gem_object *gem;
+	unsigned int width = cmd->width;
+	unsigned int height = cmd->height;
+	unsigned int size, bpp;
+	int ret;
+
+	if (drm_format_num_planes(cmd->pixel_format) != 1)
+		return ERR_PTR(-EINVAL);
+
+	gem = drm_gem_object_lookup(dev, file, cmd->handles[0]);
+	if (!gem)
+		return ERR_PTR(-ENOENT);
+
+	bpp = drm_format_plane_cpp(cmd->pixel_format, 0);
+	size = (height - 1) * cmd->pitches[0] + width * bpp;
+	size += cmd->offsets[0];
+
+	if (gem->size < size) {
+		ret = -EINVAL;
+		goto unreference;
+	}
+
+	mtk_fb = mtk_drm_framebuffer_init(dev, cmd, gem);
+	if (IS_ERR(mtk_fb)) {
+		ret = PTR_ERR(mtk_fb);
+		goto unreference;
+	}
+
+	return &mtk_fb->base;
+
+unreference:
+	drm_gem_object_unreference_unlocked(gem);
+	return ERR_PTR(ret);
+}
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_fb.h b/drivers/gpu/drm/mediatek/mtk_drm_fb.h
new file mode 100644
index 0000000..9b2ae34
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_fb.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MTK_DRM_FB_H
+#define MTK_DRM_FB_H
+
+struct drm_gem_object *mtk_fb_get_gem_obj(struct drm_framebuffer *fb);
+int mtk_fb_wait(struct drm_framebuffer *fb);
+struct drm_framebuffer *mtk_drm_mode_fb_create(struct drm_device *dev,
+					       struct drm_file *file,
+					       const struct drm_mode_fb_cmd2 *cmd);
+
+#endif /* MTK_DRM_FB_H */
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_gem.c b/drivers/gpu/drm/mediatek/mtk_drm_gem.c
new file mode 100644
index 0000000..a773bfa
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_gem.c
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_gem.h>
+#include <linux/dma-buf.h>
+
+#include "mtk_drm_drv.h"
+#include "mtk_drm_gem.h"
+
+static struct mtk_drm_gem_obj *mtk_drm_gem_init(struct drm_device *dev,
+						unsigned long size)
+{
+	struct mtk_drm_gem_obj *mtk_gem_obj;
+	int ret;
+
+	size = round_up(size, PAGE_SIZE);
+
+	mtk_gem_obj = kzalloc(sizeof(*mtk_gem_obj), GFP_KERNEL);
+	if (!mtk_gem_obj)
+		return ERR_PTR(-ENOMEM);
+
+	ret = drm_gem_object_init(dev, &mtk_gem_obj->base, size);
+	if (ret < 0) {
+		DRM_ERROR("failed to initialize gem object\n");
+		kfree(mtk_gem_obj);
+		return ERR_PTR(ret);
+	}
+
+	return mtk_gem_obj;
+}
+
+struct mtk_drm_gem_obj *mtk_drm_gem_create(struct drm_device *dev,
+					   size_t size, bool alloc_kmap)
+{
+	struct mtk_drm_private *priv = dev->dev_private;
+	struct mtk_drm_gem_obj *mtk_gem;
+	struct drm_gem_object *obj;
+	int ret;
+
+	mtk_gem = mtk_drm_gem_init(dev, size);
+	if (IS_ERR(mtk_gem))
+		return ERR_CAST(mtk_gem);
+
+	obj = &mtk_gem->base;
+
+	init_dma_attrs(&mtk_gem->dma_attrs);
+	dma_set_attr(DMA_ATTR_WRITE_COMBINE, &mtk_gem->dma_attrs);
+
+	if (!alloc_kmap)
+		dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &mtk_gem->dma_attrs);
+
+	mtk_gem->cookie = dma_alloc_attrs(priv->dma_dev, obj->size,
+					  &mtk_gem->dma_addr, GFP_KERNEL,
+					  &mtk_gem->dma_attrs);
+	if (!mtk_gem->cookie) {
+		DRM_ERROR("failed to allocate %zx byte dma buffer", obj->size);
+		ret = -ENOMEM;
+		goto err_gem_free;
+	}
+
+	if (alloc_kmap)
+		mtk_gem->kvaddr = mtk_gem->cookie;
+
+	DRM_DEBUG_DRIVER("cookie = %p dma_addr = %pad size = %zu\n",
+			 mtk_gem->cookie, &mtk_gem->dma_addr,
+			 size);
+
+	return mtk_gem;
+
+err_gem_free:
+	drm_gem_object_release(obj);
+	kfree(mtk_gem);
+	return ERR_PTR(ret);
+}
+
+void mtk_drm_gem_free_object(struct drm_gem_object *obj)
+{
+	struct mtk_drm_gem_obj *mtk_gem = to_mtk_gem_obj(obj);
+	struct mtk_drm_private *priv = obj->dev->dev_private;
+
+	if (mtk_gem->sg)
+		drm_prime_gem_destroy(obj, mtk_gem->sg);
+	else
+		dma_free_attrs(priv->dma_dev, obj->size, mtk_gem->cookie,
+			       mtk_gem->dma_addr, &mtk_gem->dma_attrs);
+
+	/* release file pointer to gem object. */
+	drm_gem_object_release(obj);
+
+	kfree(mtk_gem);
+}
+
+int mtk_drm_gem_dumb_create(struct drm_file *file_priv, struct drm_device *dev,
+			    struct drm_mode_create_dumb *args)
+{
+	struct mtk_drm_gem_obj *mtk_gem;
+	int ret;
+
+	args->pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
+	args->size = args->pitch * args->height;
+
+	mtk_gem = mtk_drm_gem_create(dev, args->size, false);
+	if (IS_ERR(mtk_gem))
+		return PTR_ERR(mtk_gem);
+
+	/*
+	 * allocate a id of idr table where the obj is registered
+	 * and handle has the id what user can see.
+	 */
+	ret = drm_gem_handle_create(file_priv, &mtk_gem->base, &args->handle);
+	if (ret)
+		goto err_handle_create;
+
+	/* drop reference from allocate - handle holds it now. */
+	drm_gem_object_unreference_unlocked(&mtk_gem->base);
+
+	return 0;
+
+err_handle_create:
+	mtk_drm_gem_free_object(&mtk_gem->base);
+	return ret;
+}
+
+int mtk_drm_gem_dumb_map_offset(struct drm_file *file_priv,
+				struct drm_device *dev, uint32_t handle,
+				uint64_t *offset)
+{
+	struct drm_gem_object *obj;
+	int ret;
+
+	obj = drm_gem_object_lookup(dev, file_priv, handle);
+	if (!obj) {
+		DRM_ERROR("failed to lookup gem object.\n");
+		return -EINVAL;
+	}
+
+	ret = drm_gem_create_mmap_offset(obj);
+	if (ret)
+		goto out;
+
+	*offset = drm_vma_node_offset_addr(&obj->vma_node);
+	DRM_DEBUG_KMS("offset = 0x%llx\n", *offset);
+
+out:
+	drm_gem_object_unreference_unlocked(obj);
+	return ret;
+}
+
+static int mtk_drm_gem_object_mmap(struct drm_gem_object *obj,
+				   struct vm_area_struct *vma)
+
+{
+	int ret;
+	struct mtk_drm_gem_obj *mtk_gem = to_mtk_gem_obj(obj);
+	struct mtk_drm_private *priv = obj->dev->dev_private;
+
+	/*
+	 * dma_alloc_attrs() allocated a struct page table for mtk_gem, so clear
+	 * VM_PFNMAP flag that was set by drm_gem_mmap_obj()/drm_gem_mmap().
+	 */
+	vma->vm_flags &= ~VM_PFNMAP;
+	vma->vm_pgoff = 0;
+
+	ret = dma_mmap_attrs(priv->dma_dev, vma, mtk_gem->cookie,
+			     mtk_gem->dma_addr, obj->size, &mtk_gem->dma_attrs);
+	if (ret)
+		drm_gem_vm_close(vma);
+
+	return ret;
+}
+
+int mtk_drm_gem_mmap_buf(struct drm_gem_object *obj, struct vm_area_struct *vma)
+{
+	int ret;
+
+	ret = drm_gem_mmap_obj(obj, obj->size, vma);
+	if (ret)
+		return ret;
+
+	return mtk_drm_gem_object_mmap(obj, vma);
+}
+
+int mtk_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	struct drm_gem_object *obj;
+	int ret;
+
+	ret = drm_gem_mmap(filp, vma);
+	if (ret)
+		return ret;
+
+	obj = vma->vm_private_data;
+
+	return mtk_drm_gem_object_mmap(obj, vma);
+}
+
+/*
+ * Allocate a sg_table for this GEM object.
+ * Note: Both the table's contents, and the sg_table itself must be freed by
+ *       the caller.
+ * Returns a pointer to the newly allocated sg_table, or an ERR_PTR() error.
+ */
+struct sg_table *mtk_gem_prime_get_sg_table(struct drm_gem_object *obj)
+{
+	struct mtk_drm_gem_obj *mtk_gem = to_mtk_gem_obj(obj);
+	struct mtk_drm_private *priv = obj->dev->dev_private;
+	struct sg_table *sgt;
+	int ret;
+
+	sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
+	if (!sgt)
+		return ERR_PTR(-ENOMEM);
+
+	ret = dma_get_sgtable_attrs(priv->dma_dev, sgt, mtk_gem->cookie,
+				    mtk_gem->dma_addr, obj->size,
+				    &mtk_gem->dma_attrs);
+	if (ret) {
+		DRM_ERROR("failed to allocate sgt, %d\n", ret);
+		kfree(sgt);
+		return ERR_PTR(ret);
+	}
+
+	return sgt;
+}
+
+struct drm_gem_object *mtk_gem_prime_import_sg_table(struct drm_device *dev,
+			struct dma_buf_attachment *attach, struct sg_table *sg)
+{
+	struct mtk_drm_gem_obj *mtk_gem;
+	int ret;
+	struct scatterlist *s;
+	unsigned int i;
+	dma_addr_t expected;
+
+	mtk_gem = mtk_drm_gem_init(dev, attach->dmabuf->size);
+
+	if (IS_ERR(mtk_gem))
+		return ERR_PTR(PTR_ERR(mtk_gem));
+
+	expected = sg_dma_address(sg->sgl);
+	for_each_sg(sg->sgl, s, sg->nents, i) {
+		if (sg_dma_address(s) != expected) {
+			DRM_ERROR("sg_table is not contiguous");
+			ret = -EINVAL;
+			goto err_gem_free;
+		}
+		expected = sg_dma_address(s) + sg_dma_len(s);
+	}
+
+	mtk_gem->dma_addr = sg_dma_address(sg->sgl);
+	mtk_gem->sg = sg;
+
+	return &mtk_gem->base;
+
+err_gem_free:
+	kfree(mtk_gem);
+	return ERR_PTR(ret);
+}
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_gem.h b/drivers/gpu/drm/mediatek/mtk_drm_gem.h
new file mode 100644
index 0000000..3a2a562
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_gem.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MTK_DRM_GEM_H_
+#define _MTK_DRM_GEM_H_
+
+#include <drm/drm_gem.h>
+
+/*
+ * mtk drm buffer structure.
+ *
+ * @base: a gem object.
+ *	- a new handle to this gem object would be created
+ *	by drm_gem_handle_create().
+ * @cookie: the return value of dma_alloc_attrs(), keep it for dma_free_attrs()
+ * @kvaddr: kernel virtual address of gem buffer.
+ * @dma_addr: dma address of gem buffer.
+ * @dma_attrs: dma attributes of gem buffer.
+ *
+ * P.S. this object would be transferred to user as kms_bo.handle so
+ *	user can access the buffer through kms_bo.handle.
+ */
+struct mtk_drm_gem_obj {
+	struct drm_gem_object	base;
+	void			*cookie;
+	void			*kvaddr;
+	dma_addr_t		dma_addr;
+	struct dma_attrs	dma_attrs;
+	struct sg_table		*sg;
+};
+
+#define to_mtk_gem_obj(x)	container_of(x, struct mtk_drm_gem_obj, base)
+
+void mtk_drm_gem_free_object(struct drm_gem_object *gem);
+struct mtk_drm_gem_obj *mtk_drm_gem_create(struct drm_device *dev, size_t size,
+					   bool alloc_kmap);
+int mtk_drm_gem_dumb_create(struct drm_file *file_priv, struct drm_device *dev,
+			    struct drm_mode_create_dumb *args);
+int mtk_drm_gem_dumb_map_offset(struct drm_file *file_priv,
+				struct drm_device *dev, uint32_t handle,
+				uint64_t *offset);
+int mtk_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
+int mtk_drm_gem_mmap_buf(struct drm_gem_object *obj,
+			 struct vm_area_struct *vma);
+struct sg_table *mtk_gem_prime_get_sg_table(struct drm_gem_object *obj);
+struct drm_gem_object *mtk_gem_prime_import_sg_table(struct drm_device *dev,
+			struct dma_buf_attachment *attach, struct sg_table *sg);
+
+#endif
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_plane.c b/drivers/gpu/drm/mediatek/mtk_drm_plane.c
new file mode 100644
index 0000000..c898788
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_plane.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ * Author: CK Hu <ck.hu@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 <drm/drmP.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_plane_helper.h>
+
+#include "mtk_drm_crtc.h"
+#include "mtk_drm_ddp_comp.h"
+#include "mtk_drm_drv.h"
+#include "mtk_drm_fb.h"
+#include "mtk_drm_gem.h"
+#include "mtk_drm_plane.h"
+
+static const u32 formats[] = {
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_RGB565,
+};
+
+static void mtk_plane_enable(struct mtk_drm_plane *mtk_plane, bool enable,
+			     dma_addr_t addr, struct drm_rect *dest)
+{
+	struct drm_plane *plane = &mtk_plane->base;
+	struct mtk_plane_state *state = to_mtk_plane_state(plane->state);
+	unsigned int pitch, format;
+	int x, y;
+
+	if (WARN_ON(!plane->state || (enable && !plane->state->fb)))
+		return;
+
+	if (plane->state->fb) {
+		pitch = plane->state->fb->pitches[0];
+		format = plane->state->fb->pixel_format;
+	} else {
+		pitch = 0;
+		format = DRM_FORMAT_RGBA8888;
+	}
+
+	x = plane->state->crtc_x;
+	y = plane->state->crtc_y;
+
+	if (x < 0) {
+		addr -= x * 4;
+		x = 0;
+	}
+
+	if (y < 0) {
+		addr -= y * pitch;
+		y = 0;
+	}
+
+	state->pending.enable = enable;
+	state->pending.pitch = pitch;
+	state->pending.format = format;
+	state->pending.addr = addr;
+	state->pending.x = x;
+	state->pending.y = y;
+	state->pending.width = dest->x2 - dest->x1;
+	state->pending.height = dest->y2 - dest->y1;
+	wmb(); /* Make sure the above parameters are set before update */
+	state->pending.dirty = true;
+}
+
+static void mtk_plane_reset(struct drm_plane *plane)
+{
+	struct mtk_plane_state *state;
+
+	if (plane->state) {
+		if (plane->state->fb)
+			drm_framebuffer_unreference(plane->state->fb);
+
+		state = to_mtk_plane_state(plane->state);
+		memset(state, 0, sizeof(*state));
+	} else {
+		state = kzalloc(sizeof(*state), GFP_KERNEL);
+		if (!state)
+			return;
+		plane->state = &state->base;
+	}
+
+	state->base.plane = plane;
+	state->pending.format = DRM_FORMAT_RGB565;
+}
+
+static struct drm_plane_state *mtk_plane_duplicate_state(struct drm_plane *plane)
+{
+	struct mtk_plane_state *old_state = to_mtk_plane_state(plane->state);
+	struct mtk_plane_state *state;
+
+	state = kzalloc(sizeof(*state), GFP_KERNEL);
+	if (!state)
+		return NULL;
+
+	__drm_atomic_helper_plane_duplicate_state(plane, &state->base);
+
+	WARN_ON(state->base.plane != plane);
+
+	state->pending = old_state->pending;
+
+	return &state->base;
+}
+
+static void mtk_drm_plane_destroy_state(struct drm_plane *plane,
+					struct drm_plane_state *state)
+{
+	__drm_atomic_helper_plane_destroy_state(plane, state);
+	kfree(to_mtk_plane_state(state));
+}
+
+static const struct drm_plane_funcs mtk_plane_funcs = {
+	.update_plane = drm_atomic_helper_update_plane,
+	.disable_plane = drm_atomic_helper_disable_plane,
+	.destroy = drm_plane_cleanup,
+	.reset = mtk_plane_reset,
+	.atomic_duplicate_state = mtk_plane_duplicate_state,
+	.atomic_destroy_state = mtk_drm_plane_destroy_state,
+};
+
+static int mtk_plane_atomic_check(struct drm_plane *plane,
+				  struct drm_plane_state *state)
+{
+	struct drm_framebuffer *fb = state->fb;
+	struct drm_crtc_state *crtc_state;
+	bool visible;
+	struct drm_rect dest = {
+		.x1 = state->crtc_x,
+		.y1 = state->crtc_y,
+		.x2 = state->crtc_x + state->crtc_w,
+		.y2 = state->crtc_y + state->crtc_h,
+	};
+	struct drm_rect src = {
+		/* 16.16 fixed point */
+		.x1 = state->src_x,
+		.y1 = state->src_y,
+		.x2 = state->src_x + state->src_w,
+		.y2 = state->src_y + state->src_h,
+	};
+	struct drm_rect clip = { 0, };
+
+	if (!fb)
+		return 0;
+
+	if (!mtk_fb_get_gem_obj(fb)) {
+		DRM_DEBUG_KMS("buffer is null\n");
+		return -EFAULT;
+	}
+
+	if (!state->crtc)
+		return 0;
+
+	crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc);
+	if (IS_ERR(crtc_state))
+		return PTR_ERR(crtc_state);
+
+	clip.x2 = crtc_state->mode.hdisplay;
+	clip.y2 = crtc_state->mode.vdisplay;
+
+	return drm_plane_helper_check_update(plane, state->crtc, fb,
+					     &src, &dest, &clip,
+					     DRM_PLANE_HELPER_NO_SCALING,
+					     DRM_PLANE_HELPER_NO_SCALING,
+					     true, true, &visible);
+}
+
+static void mtk_plane_atomic_update(struct drm_plane *plane,
+				    struct drm_plane_state *old_state)
+{
+	struct mtk_plane_state *state = to_mtk_plane_state(plane->state);
+	struct drm_crtc *crtc = state->base.crtc;
+	struct drm_gem_object *gem;
+	struct mtk_drm_gem_obj *mtk_gem;
+	struct mtk_drm_plane *mtk_plane = to_mtk_plane(plane);
+	struct drm_rect dest = {
+		.x1 = state->base.crtc_x,
+		.y1 = state->base.crtc_y,
+		.x2 = state->base.crtc_x + state->base.crtc_w,
+		.y2 = state->base.crtc_y + state->base.crtc_h,
+	};
+	struct drm_rect clip = { 0, };
+
+	if (!crtc)
+		return;
+
+	clip.x2 = state->base.crtc->state->mode.hdisplay;
+	clip.y2 = state->base.crtc->state->mode.vdisplay;
+	drm_rect_intersect(&dest, &clip);
+
+	gem = mtk_fb_get_gem_obj(state->base.fb);
+	mtk_gem = to_mtk_gem_obj(gem);
+	mtk_plane_enable(mtk_plane, true, mtk_gem->dma_addr, &dest);
+}
+
+static void mtk_plane_atomic_disable(struct drm_plane *plane,
+				     struct drm_plane_state *old_state)
+{
+	struct mtk_plane_state *state = to_mtk_plane_state(plane->state);
+
+	state->pending.enable = false;
+	wmb(); /* Make sure the above parameter is set before update */
+	state->pending.dirty = true;
+}
+
+static const struct drm_plane_helper_funcs mtk_plane_helper_funcs = {
+	.atomic_check = mtk_plane_atomic_check,
+	.atomic_update = mtk_plane_atomic_update,
+	.atomic_disable = mtk_plane_atomic_disable,
+};
+
+int mtk_plane_init(struct drm_device *dev, struct mtk_drm_plane *mtk_plane,
+		   unsigned long possible_crtcs, enum drm_plane_type type,
+		   unsigned int zpos)
+{
+	int err;
+
+	err = drm_universal_plane_init(dev, &mtk_plane->base, possible_crtcs,
+				       &mtk_plane_funcs, formats,
+				       ARRAY_SIZE(formats), type, NULL);
+	if (err) {
+		DRM_ERROR("failed to initialize plane\n");
+		return err;
+	}
+
+	drm_plane_helper_add(&mtk_plane->base, &mtk_plane_helper_funcs);
+	mtk_plane->idx = zpos;
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_plane.h b/drivers/gpu/drm/mediatek/mtk_drm_plane.h
new file mode 100644
index 0000000..72a7b3e
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_plane.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ * Author: CK Hu <ck.hu@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_DRM_PLANE_H_
+#define _MTK_DRM_PLANE_H_
+
+#include <drm/drm_crtc.h>
+#include <linux/types.h>
+
+struct mtk_drm_plane {
+	struct drm_plane		base;
+	unsigned int			idx;
+};
+
+struct mtk_plane_pending_state {
+	bool				config;
+	bool				enable;
+	dma_addr_t			addr;
+	unsigned int			pitch;
+	unsigned int			format;
+	unsigned int			x;
+	unsigned int			y;
+	unsigned int			width;
+	unsigned int			height;
+	bool				dirty;
+};
+
+struct mtk_plane_state {
+	struct drm_plane_state		base;
+	struct mtk_plane_pending_state	pending;
+};
+
+static inline struct mtk_drm_plane *to_mtk_plane(struct drm_plane *plane)
+{
+	return container_of(plane, struct mtk_drm_plane, base);
+}
+
+static inline struct mtk_plane_state *
+to_mtk_plane_state(struct drm_plane_state *state)
+{
+	return container_of(state, struct mtk_plane_state, base);
+}
+
+int mtk_plane_init(struct drm_device *dev, struct mtk_drm_plane *mtk_plane,
+		   unsigned long possible_crtcs, enum drm_plane_type type,
+		   unsigned int zpos);
+
+#endif
-- 
2.8.0.rc3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCH v16 4/8] drm/mediatek: Add DSI sub driver
  2016-05-06 13:05 [PATCH v16 0/8] MT8173 DRM support Philipp Zabel
                   ` (2 preceding siblings ...)
  2016-05-06 13:05 ` [PATCH v16 3/8] drm/mediatek: Add DRM Driver for Mediatek SoC MT8173 Philipp Zabel
@ 2016-05-06 13:05 ` Philipp Zabel
  2016-05-06 13:05 ` [PATCH v16 5/8] drm/mediatek: Add DPI " Philipp Zabel
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: Philipp Zabel @ 2016-05-06 13:05 UTC (permalink / raw)
  To: dri-devel
  Cc: Mark Rutland, Stephen Boyd, Michael Turquette, Jie Qiu,
	Cawa Cheng, YT Shen, Yingjoe Chen, devicetree, Jitao Shi, kernel,
	Pawel Moll, Ian Campbell, Rob Herring, linux-mediatek,
	Matthias Brugger, Paul Bolle, Emil Velikov, Tomasz Figa,
	Kumar Gala

From: CK Hu <ck.hu@mediatek.com>

This patch add a drm encoder/connector driver for the MIPI DSI function
block of the Mediatek display subsystem and a phy driver for the MIPI TX
D-PHY control module.

Signed-off-by: Jitao Shi <jitao.shi@mediatek.com>
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
---
Changes since v15:
 - Order mtk_drm_drivers alphabetically
---
 drivers/gpu/drm/mediatek/Kconfig       |   2 +
 drivers/gpu/drm/mediatek/Makefile      |   4 +-
 drivers/gpu/drm/mediatek/mtk_drm_drv.c |   2 +
 drivers/gpu/drm/mediatek/mtk_drm_drv.h |   2 +
 drivers/gpu/drm/mediatek/mtk_dsi.c     | 927 +++++++++++++++++++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_mipi_tx.c | 463 ++++++++++++++++
 6 files changed, 1399 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/mediatek/mtk_dsi.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_mipi_tx.c

diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
index 30e5371..eeefc97 100644
--- a/drivers/gpu/drm/mediatek/Kconfig
+++ b/drivers/gpu/drm/mediatek/Kconfig
@@ -4,6 +4,8 @@ config DRM_MEDIATEK
 	depends on ARCH_MEDIATEK || (ARM && COMPILE_TEST)
 	select DRM_GEM_CMA_HELPER
 	select DRM_KMS_HELPER
+	select DRM_MIPI_DSI
+	select DRM_PANEL
 	select IOMMU_DMA
 	select MEMORY
 	select MTK_SMI
diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
index d4bde7c..e781db5a 100644
--- a/drivers/gpu/drm/mediatek/Makefile
+++ b/drivers/gpu/drm/mediatek/Makefile
@@ -6,6 +6,8 @@ mediatek-drm-y := mtk_disp_ovl.o \
 		  mtk_drm_drv.o \
 		  mtk_drm_fb.o \
 		  mtk_drm_gem.o \
-		  mtk_drm_plane.o
+		  mtk_drm_plane.o \
+		  mtk_dsi.o \
+		  mtk_mipi_tx.o
 
 obj-$(CONFIG_DRM_MEDIATEK) += mediatek-drm.o
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
index e6e5654..08782d7 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@ -517,6 +517,8 @@ static struct platform_driver * const mtk_drm_drivers[] = {
 	&mtk_disp_ovl_driver,
 	&mtk_disp_rdma_driver,
 	&mtk_drm_platform_driver,
+	&mtk_dsi_driver,
+	&mtk_mipi_tx_driver,
 };
 
 static int __init mtk_drm_init(void)
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.h b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
index 27dc8fa..ac6c92b 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
@@ -53,5 +53,7 @@ struct mtk_drm_private {
 extern struct platform_driver mtk_ddp_driver;
 extern struct platform_driver mtk_disp_ovl_driver;
 extern struct platform_driver mtk_disp_rdma_driver;
+extern struct platform_driver mtk_dsi_driver;
+extern struct platform_driver mtk_mipi_tx_driver;
 
 #endif /* MTK_DRM_DRV_H */
diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c
new file mode 100644
index 0000000..54e8ff5
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_dsi.c
@@ -0,0 +1,927 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_graph.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <video/videomode.h>
+
+#include "mtk_drm_ddp_comp.h"
+
+#define DSI_VIDEO_FIFO_DEPTH	(1920 / 4)
+#define DSI_HOST_FIFO_DEPTH	64
+
+#define DSI_START		0x00
+
+#define DSI_CON_CTRL		0x10
+#define DSI_RESET			BIT(0)
+#define DSI_EN				BIT(1)
+
+#define DSI_MODE_CTRL		0x14
+#define MODE				(3)
+#define CMD_MODE			0
+#define SYNC_PULSE_MODE			1
+#define SYNC_EVENT_MODE			2
+#define BURST_MODE			3
+#define FRM_MODE			BIT(16)
+#define MIX_MODE			BIT(17)
+
+#define DSI_TXRX_CTRL		0x18
+#define VC_NUM				(2 << 0)
+#define LANE_NUM			(0xf << 2)
+#define DIS_EOT				BIT(6)
+#define NULL_EN				BIT(7)
+#define TE_FREERUN			BIT(8)
+#define EXT_TE_EN			BIT(9)
+#define EXT_TE_EDGE			BIT(10)
+#define MAX_RTN_SIZE			(0xf << 12)
+#define HSTX_CKLP_EN			BIT(16)
+
+#define DSI_PSCTRL		0x1c
+#define DSI_PS_WC			0x3fff
+#define DSI_PS_SEL			(3 << 16)
+#define PACKED_PS_16BIT_RGB565		(0 << 16)
+#define LOOSELY_PS_18BIT_RGB666		(1 << 16)
+#define PACKED_PS_18BIT_RGB666		(2 << 16)
+#define PACKED_PS_24BIT_RGB888		(3 << 16)
+
+#define DSI_VSA_NL		0x20
+#define DSI_VBP_NL		0x24
+#define DSI_VFP_NL		0x28
+#define DSI_VACT_NL		0x2C
+#define DSI_HSA_WC		0x50
+#define DSI_HBP_WC		0x54
+#define DSI_HFP_WC		0x58
+
+#define DSI_HSTX_CKL_WC		0x64
+
+#define DSI_PHY_LCCON		0x104
+#define LC_HS_TX_EN			BIT(0)
+#define LC_ULPM_EN			BIT(1)
+#define LC_WAKEUP_EN			BIT(2)
+
+#define DSI_PHY_LD0CON		0x108
+#define LD0_HS_TX_EN			BIT(0)
+#define LD0_ULPM_EN			BIT(1)
+#define LD0_WAKEUP_EN			BIT(2)
+
+#define DSI_PHY_TIMECON0	0x110
+#define LPX				(0xff << 0)
+#define HS_PRPR				(0xff << 8)
+#define HS_ZERO				(0xff << 16)
+#define HS_TRAIL			(0xff << 24)
+
+#define DSI_PHY_TIMECON1	0x114
+#define TA_GO				(0xff << 0)
+#define TA_SURE				(0xff << 8)
+#define TA_GET				(0xff << 16)
+#define DA_HS_EXIT			(0xff << 24)
+
+#define DSI_PHY_TIMECON2	0x118
+#define CONT_DET			(0xff << 0)
+#define CLK_ZERO			(0xff << 16)
+#define CLK_TRAIL			(0xff << 24)
+
+#define DSI_PHY_TIMECON3	0x11c
+#define CLK_HS_PRPR			(0xff << 0)
+#define CLK_HS_POST			(0xff << 8)
+#define CLK_HS_EXIT			(0xff << 16)
+
+#define NS_TO_CYCLE(n, c)    ((n) / (c) + (((n) % (c)) ? 1 : 0))
+
+struct phy;
+
+struct mtk_dsi {
+	struct mtk_ddp_comp ddp_comp;
+	struct device *dev;
+	struct mipi_dsi_host host;
+	struct drm_encoder encoder;
+	struct drm_connector conn;
+	struct drm_panel *panel;
+	struct drm_bridge *bridge;
+	struct phy *phy;
+
+	void __iomem *regs;
+
+	struct clk *engine_clk;
+	struct clk *digital_clk;
+	struct clk *hs_clk;
+
+	u32 data_rate;
+
+	unsigned long mode_flags;
+	enum mipi_dsi_pixel_format format;
+	unsigned int lanes;
+	struct videomode vm;
+	int refcount;
+	bool enabled;
+};
+
+static inline struct mtk_dsi *encoder_to_dsi(struct drm_encoder *e)
+{
+	return container_of(e, struct mtk_dsi, encoder);
+}
+
+static inline struct mtk_dsi *connector_to_dsi(struct drm_connector *c)
+{
+	return container_of(c, struct mtk_dsi, conn);
+}
+
+static inline struct mtk_dsi *host_to_dsi(struct mipi_dsi_host *h)
+{
+	return container_of(h, struct mtk_dsi, host);
+}
+
+static void mtk_dsi_mask(struct mtk_dsi *dsi, u32 offset, u32 mask, u32 data)
+{
+	u32 temp = readl(dsi->regs + offset);
+
+	writel((temp & ~mask) | (data & mask), dsi->regs + offset);
+}
+
+static void dsi_phy_timconfig(struct mtk_dsi *dsi)
+{
+	u32 timcon0, timcon1, timcon2, timcon3;
+	unsigned int ui, cycle_time;
+	unsigned int lpx;
+
+	ui = 1000 / dsi->data_rate + 0x01;
+	cycle_time = 8000 / dsi->data_rate + 0x01;
+	lpx = 5;
+
+	timcon0 = (8 << 24) | (0xa << 16) | (0x6 << 8) | lpx;
+	timcon1 = (7 << 24) | (5 * lpx << 16) | ((3 * lpx) / 2) << 8 |
+		  (4 * lpx);
+	timcon2 = ((NS_TO_CYCLE(0x64, cycle_time) + 0xa) << 24) |
+		  (NS_TO_CYCLE(0x150, cycle_time) << 16);
+	timcon3 = (2 * lpx) << 16 | NS_TO_CYCLE(80 + 52 * ui, cycle_time) << 8 |
+		   NS_TO_CYCLE(0x40, cycle_time);
+
+	writel(timcon0, dsi->regs + DSI_PHY_TIMECON0);
+	writel(timcon1, dsi->regs + DSI_PHY_TIMECON1);
+	writel(timcon2, dsi->regs + DSI_PHY_TIMECON2);
+	writel(timcon3, dsi->regs + DSI_PHY_TIMECON3);
+}
+
+static void mtk_dsi_enable(struct mtk_dsi *dsi)
+{
+	mtk_dsi_mask(dsi, DSI_CON_CTRL, DSI_EN, DSI_EN);
+}
+
+static void mtk_dsi_disable(struct mtk_dsi *dsi)
+{
+	mtk_dsi_mask(dsi, DSI_CON_CTRL, DSI_EN, 0);
+}
+
+static void mtk_dsi_reset(struct mtk_dsi *dsi)
+{
+	mtk_dsi_mask(dsi, DSI_CON_CTRL, DSI_RESET, DSI_RESET);
+	mtk_dsi_mask(dsi, DSI_CON_CTRL, DSI_RESET, 0);
+}
+
+static int mtk_dsi_poweron(struct mtk_dsi *dsi)
+{
+	struct device *dev = dsi->dev;
+	int ret;
+
+	if (++dsi->refcount != 1)
+		return 0;
+
+	/**
+	 * data_rate = (pixel_clock / 1000) * pixel_dipth * mipi_ratio;
+	 * pixel_clock unit is Khz, data_rata unit is MHz, so need divide 1000.
+	 * mipi_ratio is mipi clk coefficient for balance the pixel clk in mipi.
+	 * we set mipi_ratio is 1.05.
+	 */
+	dsi->data_rate = dsi->vm.pixelclock * 3 * 21 / (1 * 1000 * 10);
+
+	ret = clk_set_rate(dsi->hs_clk, dsi->data_rate * 1000000);
+	if (ret < 0) {
+		dev_err(dev, "Failed to set data rate: %d\n", ret);
+		goto err_refcount;
+	}
+
+	phy_power_on(dsi->phy);
+
+	ret = clk_prepare_enable(dsi->engine_clk);
+	if (ret < 0) {
+		dev_err(dev, "Failed to enable engine clock: %d\n", ret);
+		goto err_phy_power_off;
+	}
+
+	ret = clk_prepare_enable(dsi->digital_clk);
+	if (ret < 0) {
+		dev_err(dev, "Failed to enable digital clock: %d\n", ret);
+		goto err_disable_engine_clk;
+	}
+
+	mtk_dsi_enable(dsi);
+	mtk_dsi_reset(dsi);
+	dsi_phy_timconfig(dsi);
+
+	return 0;
+
+err_disable_engine_clk:
+	clk_disable_unprepare(dsi->engine_clk);
+err_phy_power_off:
+	phy_power_off(dsi->phy);
+err_refcount:
+	dsi->refcount--;
+	return ret;
+}
+
+static void dsi_clk_ulp_mode_enter(struct mtk_dsi *dsi)
+{
+	mtk_dsi_mask(dsi, DSI_PHY_LCCON, LC_HS_TX_EN, 0);
+	mtk_dsi_mask(dsi, DSI_PHY_LCCON, LC_ULPM_EN, 0);
+}
+
+static void dsi_clk_ulp_mode_leave(struct mtk_dsi *dsi)
+{
+	mtk_dsi_mask(dsi, DSI_PHY_LCCON, LC_ULPM_EN, 0);
+	mtk_dsi_mask(dsi, DSI_PHY_LCCON, LC_WAKEUP_EN, LC_WAKEUP_EN);
+	mtk_dsi_mask(dsi, DSI_PHY_LCCON, LC_WAKEUP_EN, 0);
+}
+
+static void dsi_lane0_ulp_mode_enter(struct mtk_dsi *dsi)
+{
+	mtk_dsi_mask(dsi, DSI_PHY_LD0CON, LD0_HS_TX_EN, 0);
+	mtk_dsi_mask(dsi, DSI_PHY_LD0CON, LD0_ULPM_EN, 0);
+}
+
+static void dsi_lane0_ulp_mode_leave(struct mtk_dsi *dsi)
+{
+	mtk_dsi_mask(dsi, DSI_PHY_LD0CON, LD0_ULPM_EN, 0);
+	mtk_dsi_mask(dsi, DSI_PHY_LD0CON, LD0_WAKEUP_EN, LD0_WAKEUP_EN);
+	mtk_dsi_mask(dsi, DSI_PHY_LD0CON, LD0_WAKEUP_EN, 0);
+}
+
+static bool dsi_clk_hs_state(struct mtk_dsi *dsi)
+{
+	u32 tmp_reg1;
+
+	tmp_reg1 = readl(dsi->regs + DSI_PHY_LCCON);
+	return ((tmp_reg1 & LC_HS_TX_EN) == 1) ? true : false;
+}
+
+static void dsi_clk_hs_mode(struct mtk_dsi *dsi, bool enter)
+{
+	if (enter && !dsi_clk_hs_state(dsi))
+		mtk_dsi_mask(dsi, DSI_PHY_LCCON, LC_HS_TX_EN, LC_HS_TX_EN);
+	else if (!enter && dsi_clk_hs_state(dsi))
+		mtk_dsi_mask(dsi, DSI_PHY_LCCON, LC_HS_TX_EN, 0);
+}
+
+static void dsi_set_mode(struct mtk_dsi *dsi)
+{
+	u32 vid_mode = CMD_MODE;
+
+	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
+		vid_mode = SYNC_PULSE_MODE;
+
+		if ((dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) &&
+		    !(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE))
+			vid_mode = BURST_MODE;
+	}
+
+	writel(vid_mode, dsi->regs + DSI_MODE_CTRL);
+}
+
+static void dsi_ps_control_vact(struct mtk_dsi *dsi)
+{
+	struct videomode *vm = &dsi->vm;
+	u32 dsi_buf_bpp, ps_wc;
+	u32 ps_bpp_mode;
+
+	if (dsi->format == MIPI_DSI_FMT_RGB565)
+		dsi_buf_bpp = 2;
+	else
+		dsi_buf_bpp = 3;
+
+	ps_wc = vm->hactive * dsi_buf_bpp;
+	ps_bpp_mode = ps_wc;
+
+	switch (dsi->format) {
+	case MIPI_DSI_FMT_RGB888:
+		ps_bpp_mode |= PACKED_PS_24BIT_RGB888;
+		break;
+	case MIPI_DSI_FMT_RGB666:
+		ps_bpp_mode |= PACKED_PS_18BIT_RGB666;
+		break;
+	case MIPI_DSI_FMT_RGB666_PACKED:
+		ps_bpp_mode |= LOOSELY_PS_18BIT_RGB666;
+		break;
+	case MIPI_DSI_FMT_RGB565:
+		ps_bpp_mode |= PACKED_PS_16BIT_RGB565;
+		break;
+	}
+
+	writel(vm->vactive, dsi->regs + DSI_VACT_NL);
+	writel(ps_bpp_mode, dsi->regs + DSI_PSCTRL);
+	writel(ps_wc, dsi->regs + DSI_HSTX_CKL_WC);
+}
+
+static void dsi_rxtx_control(struct mtk_dsi *dsi)
+{
+	u32 tmp_reg;
+
+	switch (dsi->lanes) {
+	case 1:
+		tmp_reg = 1 << 2;
+		break;
+	case 2:
+		tmp_reg = 3 << 2;
+		break;
+	case 3:
+		tmp_reg = 7 << 2;
+		break;
+	case 4:
+		tmp_reg = 0xf << 2;
+		break;
+	default:
+		tmp_reg = 0xf << 2;
+		break;
+	}
+
+	writel(tmp_reg, dsi->regs + DSI_TXRX_CTRL);
+}
+
+static void dsi_ps_control(struct mtk_dsi *dsi)
+{
+	unsigned int dsi_tmp_buf_bpp;
+	u32 tmp_reg;
+
+	switch (dsi->format) {
+	case MIPI_DSI_FMT_RGB888:
+		tmp_reg = PACKED_PS_24BIT_RGB888;
+		dsi_tmp_buf_bpp = 3;
+		break;
+	case MIPI_DSI_FMT_RGB666:
+		tmp_reg = LOOSELY_PS_18BIT_RGB666;
+		dsi_tmp_buf_bpp = 3;
+		break;
+	case MIPI_DSI_FMT_RGB666_PACKED:
+		tmp_reg = PACKED_PS_18BIT_RGB666;
+		dsi_tmp_buf_bpp = 3;
+		break;
+	case MIPI_DSI_FMT_RGB565:
+		tmp_reg = PACKED_PS_16BIT_RGB565;
+		dsi_tmp_buf_bpp = 2;
+		break;
+	default:
+		tmp_reg = PACKED_PS_24BIT_RGB888;
+		dsi_tmp_buf_bpp = 3;
+		break;
+	}
+
+	tmp_reg += dsi->vm.hactive * dsi_tmp_buf_bpp & DSI_PS_WC;
+	writel(tmp_reg, dsi->regs + DSI_PSCTRL);
+}
+
+static void dsi_config_vdo_timing(struct mtk_dsi *dsi)
+{
+	unsigned int horizontal_sync_active_byte;
+	unsigned int horizontal_backporch_byte;
+	unsigned int horizontal_frontporch_byte;
+	unsigned int dsi_tmp_buf_bpp;
+
+	struct videomode *vm = &dsi->vm;
+
+	if (dsi->format == MIPI_DSI_FMT_RGB565)
+		dsi_tmp_buf_bpp = 2;
+	else
+		dsi_tmp_buf_bpp = 3;
+
+	writel(vm->vsync_len, dsi->regs + DSI_VSA_NL);
+	writel(vm->vback_porch, dsi->regs + DSI_VBP_NL);
+	writel(vm->vfront_porch, dsi->regs + DSI_VFP_NL);
+	writel(vm->vactive, dsi->regs + DSI_VACT_NL);
+
+	horizontal_sync_active_byte = (vm->hsync_len * dsi_tmp_buf_bpp - 10);
+
+	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
+		horizontal_backporch_byte =
+			(vm->hback_porch * dsi_tmp_buf_bpp - 10);
+	else
+		horizontal_backporch_byte = ((vm->hback_porch + vm->hsync_len) *
+			dsi_tmp_buf_bpp - 10);
+
+	horizontal_frontporch_byte = (vm->hfront_porch * dsi_tmp_buf_bpp - 12);
+
+	writel(horizontal_sync_active_byte, dsi->regs + DSI_HSA_WC);
+	writel(horizontal_backporch_byte, dsi->regs + DSI_HBP_WC);
+	writel(horizontal_frontporch_byte, dsi->regs + DSI_HFP_WC);
+
+	dsi_ps_control(dsi);
+}
+
+static void mtk_dsi_start(struct mtk_dsi *dsi)
+{
+	writel(0, dsi->regs + DSI_START);
+	writel(1, dsi->regs + DSI_START);
+}
+
+static void mtk_dsi_poweroff(struct mtk_dsi *dsi)
+{
+	if (WARN_ON(dsi->refcount == 0))
+		return;
+
+	if (--dsi->refcount != 0)
+		return;
+
+	dsi_lane0_ulp_mode_enter(dsi);
+	dsi_clk_ulp_mode_enter(dsi);
+
+	mtk_dsi_disable(dsi);
+
+	clk_disable_unprepare(dsi->engine_clk);
+	clk_disable_unprepare(dsi->digital_clk);
+
+	phy_power_off(dsi->phy);
+}
+
+static void mtk_output_dsi_enable(struct mtk_dsi *dsi)
+{
+	int ret;
+
+	if (dsi->enabled)
+		return;
+
+	if (dsi->panel) {
+		if (drm_panel_prepare(dsi->panel)) {
+			DRM_ERROR("failed to setup the panel\n");
+			return;
+		}
+	}
+
+	ret = mtk_dsi_poweron(dsi);
+	if (ret < 0) {
+		DRM_ERROR("failed to power on dsi\n");
+		return;
+	}
+
+	dsi_rxtx_control(dsi);
+
+	dsi_clk_ulp_mode_leave(dsi);
+	dsi_lane0_ulp_mode_leave(dsi);
+	dsi_clk_hs_mode(dsi, 0);
+	dsi_set_mode(dsi);
+
+	dsi_ps_control_vact(dsi);
+	dsi_config_vdo_timing(dsi);
+
+	dsi_set_mode(dsi);
+	dsi_clk_hs_mode(dsi, 1);
+
+	mtk_dsi_start(dsi);
+
+	dsi->enabled = true;
+}
+
+static void mtk_output_dsi_disable(struct mtk_dsi *dsi)
+{
+	if (!dsi->enabled)
+		return;
+
+	if (dsi->panel) {
+		if (drm_panel_disable(dsi->panel)) {
+			DRM_ERROR("failed to disable the panel\n");
+			return;
+		}
+	}
+
+	mtk_dsi_poweroff(dsi);
+
+	dsi->enabled = false;
+}
+
+static void mtk_dsi_encoder_destroy(struct drm_encoder *encoder)
+{
+	drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_funcs mtk_dsi_encoder_funcs = {
+	.destroy = mtk_dsi_encoder_destroy,
+};
+
+static bool mtk_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
+				       const struct drm_display_mode *mode,
+				       struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+static void mtk_dsi_encoder_mode_set(struct drm_encoder *encoder,
+				     struct drm_display_mode *mode,
+				     struct drm_display_mode *adjusted)
+{
+	struct mtk_dsi *dsi = encoder_to_dsi(encoder);
+
+	dsi->vm.pixelclock = adjusted->clock;
+	dsi->vm.hactive = adjusted->hdisplay;
+	dsi->vm.hback_porch = adjusted->htotal - adjusted->hsync_end;
+	dsi->vm.hfront_porch = adjusted->hsync_start - adjusted->hdisplay;
+	dsi->vm.hsync_len = adjusted->hsync_end - adjusted->hsync_start;
+
+	dsi->vm.vactive = adjusted->vdisplay;
+	dsi->vm.vback_porch = adjusted->vtotal - adjusted->vsync_end;
+	dsi->vm.vfront_porch = adjusted->vsync_start - adjusted->vdisplay;
+	dsi->vm.vsync_len = adjusted->vsync_end - adjusted->vsync_start;
+}
+
+static void mtk_dsi_encoder_disable(struct drm_encoder *encoder)
+{
+	struct mtk_dsi *dsi = encoder_to_dsi(encoder);
+
+	mtk_output_dsi_disable(dsi);
+}
+
+static void mtk_dsi_encoder_enable(struct drm_encoder *encoder)
+{
+	struct mtk_dsi *dsi = encoder_to_dsi(encoder);
+
+	mtk_output_dsi_enable(dsi);
+}
+
+static enum drm_connector_status mtk_dsi_connector_detect(
+	struct drm_connector *connector, bool force)
+{
+	return connector_status_connected;
+}
+
+static void mtk_dsi_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+static int mtk_dsi_connector_get_modes(struct drm_connector *connector)
+{
+	struct mtk_dsi *dsi = connector_to_dsi(connector);
+
+	return drm_panel_get_modes(dsi->panel);
+}
+
+static struct drm_encoder *mtk_dsi_connector_best_encoder(
+		struct drm_connector *connector)
+{
+	struct mtk_dsi *dsi = connector_to_dsi(connector);
+
+	return &dsi->encoder;
+}
+
+static const struct drm_encoder_helper_funcs mtk_dsi_encoder_helper_funcs = {
+	.mode_fixup = mtk_dsi_encoder_mode_fixup,
+	.mode_set = mtk_dsi_encoder_mode_set,
+	.disable = mtk_dsi_encoder_disable,
+	.enable = mtk_dsi_encoder_enable,
+};
+
+static const struct drm_connector_funcs mtk_dsi_connector_funcs = {
+	.dpms = drm_atomic_helper_connector_dpms,
+	.detect = mtk_dsi_connector_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = mtk_dsi_connector_destroy,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static const struct drm_connector_helper_funcs
+	mtk_dsi_connector_helper_funcs = {
+	.get_modes = mtk_dsi_connector_get_modes,
+	.best_encoder = mtk_dsi_connector_best_encoder,
+};
+
+static int mtk_drm_attach_bridge(struct drm_bridge *bridge,
+				 struct drm_encoder *encoder)
+{
+	int ret;
+
+	if (!bridge)
+		return -ENOENT;
+
+	encoder->bridge = bridge;
+	bridge->encoder = encoder;
+	ret = drm_bridge_attach(encoder->dev, bridge);
+	if (ret) {
+		DRM_ERROR("Failed to attach bridge to drm\n");
+		encoder->bridge = NULL;
+		bridge->encoder = NULL;
+	}
+
+	return ret;
+}
+
+static int mtk_dsi_create_connector(struct drm_device *drm, struct mtk_dsi *dsi)
+{
+	int ret;
+
+	ret = drm_connector_init(drm, &dsi->conn, &mtk_dsi_connector_funcs,
+				 DRM_MODE_CONNECTOR_DSI);
+	if (ret) {
+		DRM_ERROR("Failed to connector init to drm\n");
+		return ret;
+	}
+
+	drm_connector_helper_add(&dsi->conn, &mtk_dsi_connector_helper_funcs);
+
+	ret = drm_connector_register(&dsi->conn);
+	if (ret) {
+		DRM_ERROR("Failed to connector register to drm\n");
+		goto err_connector_cleanup;
+	}
+
+	dsi->conn.dpms = DRM_MODE_DPMS_OFF;
+	drm_mode_connector_attach_encoder(&dsi->conn, &dsi->encoder);
+
+	if (dsi->panel) {
+		ret = drm_panel_attach(dsi->panel, &dsi->conn);
+		if (ret) {
+			DRM_ERROR("Failed to attach panel to drm\n");
+			goto err_connector_unregister;
+		}
+	}
+
+	return 0;
+
+err_connector_unregister:
+	drm_connector_unregister(&dsi->conn);
+err_connector_cleanup:
+	drm_connector_cleanup(&dsi->conn);
+	return ret;
+}
+
+static int mtk_dsi_create_conn_enc(struct drm_device *drm, struct mtk_dsi *dsi)
+{
+	int ret;
+
+	ret = drm_encoder_init(drm, &dsi->encoder, &mtk_dsi_encoder_funcs,
+			       DRM_MODE_ENCODER_DSI, NULL);
+	if (ret) {
+		DRM_ERROR("Failed to encoder init to drm\n");
+		return ret;
+	}
+	drm_encoder_helper_add(&dsi->encoder, &mtk_dsi_encoder_helper_funcs);
+
+	/*
+	 * Currently display data paths are statically assigned to a crtc each.
+	 * crtc 0 is OVL0 -> COLOR0 -> AAL -> OD -> RDMA0 -> UFOE -> DSI0
+	 */
+	dsi->encoder.possible_crtcs = 1;
+
+	/* If there's a bridge, attach to it and let it create the connector */
+	ret = mtk_drm_attach_bridge(dsi->bridge, &dsi->encoder);
+	if (ret) {
+		/* Otherwise create our own connector and attach to a panel */
+		ret = mtk_dsi_create_connector(drm, dsi);
+		if (ret)
+			goto err_encoder_cleanup;
+	}
+
+	return 0;
+
+err_encoder_cleanup:
+	drm_encoder_cleanup(&dsi->encoder);
+	return ret;
+}
+
+static void mtk_dsi_destroy_conn_enc(struct mtk_dsi *dsi)
+{
+	drm_encoder_cleanup(&dsi->encoder);
+	/* Skip connector cleanup if creation was delegated to the bridge */
+	if (dsi->conn.dev) {
+		drm_connector_unregister(&dsi->conn);
+		drm_connector_cleanup(&dsi->conn);
+	}
+}
+
+static void mtk_dsi_ddp_start(struct mtk_ddp_comp *comp)
+{
+	struct mtk_dsi *dsi = container_of(comp, struct mtk_dsi, ddp_comp);
+
+	mtk_dsi_poweron(dsi);
+}
+
+static void mtk_dsi_ddp_stop(struct mtk_ddp_comp *comp)
+{
+	struct mtk_dsi *dsi = container_of(comp, struct mtk_dsi, ddp_comp);
+
+	mtk_dsi_poweroff(dsi);
+}
+
+static const struct mtk_ddp_comp_funcs mtk_dsi_funcs = {
+	.start = mtk_dsi_ddp_start,
+	.stop = mtk_dsi_ddp_stop,
+};
+
+static int mtk_dsi_host_attach(struct mipi_dsi_host *host,
+			       struct mipi_dsi_device *device)
+{
+	struct mtk_dsi *dsi = host_to_dsi(host);
+
+	dsi->lanes = device->lanes;
+	dsi->format = device->format;
+	dsi->mode_flags = device->mode_flags;
+
+	if (dsi->conn.dev)
+		drm_helper_hpd_irq_event(dsi->conn.dev);
+
+	return 0;
+}
+
+static int mtk_dsi_host_detach(struct mipi_dsi_host *host,
+			       struct mipi_dsi_device *device)
+{
+	struct mtk_dsi *dsi = host_to_dsi(host);
+
+	if (dsi->conn.dev)
+		drm_helper_hpd_irq_event(dsi->conn.dev);
+
+	return 0;
+}
+
+static const struct mipi_dsi_host_ops mtk_dsi_ops = {
+	.attach = mtk_dsi_host_attach,
+	.detach = mtk_dsi_host_detach,
+};
+
+static int mtk_dsi_bind(struct device *dev, struct device *master, void *data)
+{
+	int ret;
+	struct drm_device *drm = data;
+	struct mtk_dsi *dsi = dev_get_drvdata(dev);
+
+	ret = mtk_ddp_comp_register(drm, &dsi->ddp_comp);
+	if (ret < 0) {
+		dev_err(dev, "Failed to register component %s: %d\n",
+			dev->of_node->full_name, ret);
+		return ret;
+	}
+
+	ret = mipi_dsi_host_register(&dsi->host);
+	if (ret < 0) {
+		dev_err(dev, "failed to register DSI host: %d\n", ret);
+		goto err_ddp_comp_unregister;
+	}
+
+	ret = mtk_dsi_create_conn_enc(drm, dsi);
+	if (ret) {
+		DRM_ERROR("Encoder create failed with %d\n", ret);
+		goto err_unregister;
+	}
+
+	return 0;
+
+err_unregister:
+	mipi_dsi_host_unregister(&dsi->host);
+err_ddp_comp_unregister:
+	mtk_ddp_comp_unregister(drm, &dsi->ddp_comp);
+	return ret;
+}
+
+static void mtk_dsi_unbind(struct device *dev, struct device *master,
+			   void *data)
+{
+	struct drm_device *drm = data;
+	struct mtk_dsi *dsi = dev_get_drvdata(dev);
+
+	mtk_dsi_destroy_conn_enc(dsi);
+	mipi_dsi_host_unregister(&dsi->host);
+	mtk_ddp_comp_unregister(drm, &dsi->ddp_comp);
+}
+
+static const struct component_ops mtk_dsi_component_ops = {
+	.bind = mtk_dsi_bind,
+	.unbind = mtk_dsi_unbind,
+};
+
+static int mtk_dsi_probe(struct platform_device *pdev)
+{
+	struct mtk_dsi *dsi;
+	struct device *dev = &pdev->dev;
+	struct device_node *remote_node, *endpoint;
+	struct resource *regs;
+	int comp_id;
+	int ret;
+
+	dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
+	if (!dsi)
+		return -ENOMEM;
+
+	dsi->host.ops = &mtk_dsi_ops;
+	dsi->host.dev = dev;
+
+	endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
+	if (endpoint) {
+		remote_node = of_graph_get_remote_port_parent(endpoint);
+		if (!remote_node) {
+			dev_err(dev, "No panel connected\n");
+			return -ENODEV;
+		}
+
+		dsi->bridge = of_drm_find_bridge(remote_node);
+		dsi->panel = of_drm_find_panel(remote_node);
+		of_node_put(remote_node);
+		if (!dsi->bridge && !dsi->panel) {
+			dev_info(dev, "Waiting for bridge or panel driver\n");
+			return -EPROBE_DEFER;
+		}
+	}
+
+	dsi->engine_clk = devm_clk_get(dev, "engine");
+	if (IS_ERR(dsi->engine_clk)) {
+		ret = PTR_ERR(dsi->engine_clk);
+		dev_err(dev, "Failed to get engine clock: %d\n", ret);
+		return ret;
+	}
+
+	dsi->digital_clk = devm_clk_get(dev, "digital");
+	if (IS_ERR(dsi->digital_clk)) {
+		ret = PTR_ERR(dsi->digital_clk);
+		dev_err(dev, "Failed to get digital clock: %d\n", ret);
+		return ret;
+	}
+
+	dsi->hs_clk = devm_clk_get(dev, "hs");
+	if (IS_ERR(dsi->hs_clk)) {
+		ret = PTR_ERR(dsi->hs_clk);
+		dev_err(dev, "Failed to get hs clock: %d\n", ret);
+		return ret;
+	}
+
+	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	dsi->regs = devm_ioremap_resource(dev, regs);
+	if (IS_ERR(dsi->regs)) {
+		ret = PTR_ERR(dsi->regs);
+		dev_err(dev, "Failed to ioremap memory: %d\n", ret);
+		return ret;
+	}
+
+	dsi->phy = devm_phy_get(dev, "dphy");
+	if (IS_ERR(dsi->phy)) {
+		ret = PTR_ERR(dsi->phy);
+		dev_err(dev, "Failed to get MIPI-DPHY: %d\n", ret);
+		return ret;
+	}
+
+	comp_id = mtk_ddp_comp_get_id(dev->of_node, MTK_DSI);
+	if (comp_id < 0) {
+		dev_err(dev, "Failed to identify by alias: %d\n", comp_id);
+		return comp_id;
+	}
+
+	ret = mtk_ddp_comp_init(dev, dev->of_node, &dsi->ddp_comp, comp_id,
+				&mtk_dsi_funcs);
+	if (ret) {
+		dev_err(dev, "Failed to initialize component: %d\n", ret);
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, dsi);
+
+	return component_add(&pdev->dev, &mtk_dsi_component_ops);
+}
+
+static int mtk_dsi_remove(struct platform_device *pdev)
+{
+	struct mtk_dsi *dsi = platform_get_drvdata(pdev);
+
+	mtk_output_dsi_disable(dsi);
+	component_del(&pdev->dev, &mtk_dsi_component_ops);
+
+	return 0;
+}
+
+static const struct of_device_id mtk_dsi_of_match[] = {
+	{ .compatible = "mediatek,mt8173-dsi" },
+	{ },
+};
+
+struct platform_driver mtk_dsi_driver = {
+	.probe = mtk_dsi_probe,
+	.remove = mtk_dsi_remove,
+	.driver = {
+		.name = "mtk-dsi",
+		.of_match_table = mtk_dsi_of_match,
+	},
+};
diff --git a/drivers/gpu/drm/mediatek/mtk_mipi_tx.c b/drivers/gpu/drm/mediatek/mtk_mipi_tx.c
new file mode 100644
index 0000000..cf8f38d
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_mipi_tx.c
@@ -0,0 +1,463 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/phy/phy.h>
+
+#define MIPITX_DSI_CON		0x00
+#define RG_DSI_LDOCORE_EN		BIT(0)
+#define RG_DSI_CKG_LDOOUT_EN		BIT(1)
+#define RG_DSI_BCLK_SEL			(3 << 2)
+#define RG_DSI_LD_IDX_SEL		(7 << 4)
+#define RG_DSI_PHYCLK_SEL		(2 << 8)
+#define RG_DSI_DSICLK_FREQ_SEL		BIT(10)
+#define RG_DSI_LPTX_CLMP_EN		BIT(11)
+
+#define MIPITX_DSI_CLOCK_LANE	0x04
+#define MIPITX_DSI_DATA_LANE0	0x08
+#define MIPITX_DSI_DATA_LANE1	0x0c
+#define MIPITX_DSI_DATA_LANE2	0x10
+#define MIPITX_DSI_DATA_LANE3	0x14
+#define RG_DSI_LNTx_LDOOUT_EN		BIT(0)
+#define RG_DSI_LNTx_CKLANE_EN		BIT(1)
+#define RG_DSI_LNTx_LPTX_IPLUS1		BIT(2)
+#define RG_DSI_LNTx_LPTX_IPLUS2		BIT(3)
+#define RG_DSI_LNTx_LPTX_IMINUS		BIT(4)
+#define RG_DSI_LNTx_LPCD_IPLUS		BIT(5)
+#define RG_DSI_LNTx_LPCD_IMINUS		BIT(6)
+#define RG_DSI_LNTx_RT_CODE		(0xf << 8)
+
+#define MIPITX_DSI_TOP_CON	0x40
+#define RG_DSI_LNT_INTR_EN		BIT(0)
+#define RG_DSI_LNT_HS_BIAS_EN		BIT(1)
+#define RG_DSI_LNT_IMP_CAL_EN		BIT(2)
+#define RG_DSI_LNT_TESTMODE_EN		BIT(3)
+#define RG_DSI_LNT_IMP_CAL_CODE		(0xf << 4)
+#define RG_DSI_LNT_AIO_SEL		(7 << 8)
+#define RG_DSI_PAD_TIE_LOW_EN		BIT(11)
+#define RG_DSI_DEBUG_INPUT_EN		BIT(12)
+#define RG_DSI_PRESERVE			(7 << 13)
+
+#define MIPITX_DSI_BG_CON	0x44
+#define RG_DSI_BG_CORE_EN		BIT(0)
+#define RG_DSI_BG_CKEN			BIT(1)
+#define RG_DSI_BG_DIV			(0x3 << 2)
+#define RG_DSI_BG_FAST_CHARGE		BIT(4)
+#define RG_DSI_VOUT_MSK			(0x3ffff << 5)
+#define RG_DSI_V12_SEL			(7 << 5)
+#define RG_DSI_V10_SEL			(7 << 8)
+#define RG_DSI_V072_SEL			(7 << 11)
+#define RG_DSI_V04_SEL			(7 << 14)
+#define RG_DSI_V032_SEL			(7 << 17)
+#define RG_DSI_V02_SEL			(7 << 20)
+#define RG_DSI_BG_R1_TRIM		(0xf << 24)
+#define RG_DSI_BG_R2_TRIM		(0xf << 28)
+
+#define MIPITX_DSI_PLL_CON0	0x50
+#define RG_DSI_MPPLL_PLL_EN		BIT(0)
+#define RG_DSI_MPPLL_DIV_MSK		(0x1ff << 1)
+#define RG_DSI_MPPLL_PREDIV		(3 << 1)
+#define RG_DSI_MPPLL_TXDIV0		(3 << 3)
+#define RG_DSI_MPPLL_TXDIV1		(3 << 5)
+#define RG_DSI_MPPLL_POSDIV		(7 << 7)
+#define RG_DSI_MPPLL_MONVC_EN		BIT(10)
+#define RG_DSI_MPPLL_MONREF_EN		BIT(11)
+#define RG_DSI_MPPLL_VOD_EN		BIT(12)
+
+#define MIPITX_DSI_PLL_CON1	0x54
+#define RG_DSI_MPPLL_SDM_FRA_EN		BIT(0)
+#define RG_DSI_MPPLL_SDM_SSC_PH_INIT	BIT(1)
+#define RG_DSI_MPPLL_SDM_SSC_EN		BIT(2)
+#define RG_DSI_MPPLL_SDM_SSC_PRD	(0xffff << 16)
+
+#define MIPITX_DSI_PLL_CON2	0x58
+
+#define MIPITX_DSI_PLL_PWR	0x68
+#define RG_DSI_MPPLL_SDM_PWR_ON		BIT(0)
+#define RG_DSI_MPPLL_SDM_ISO_EN		BIT(1)
+#define RG_DSI_MPPLL_SDM_PWR_ACK	BIT(8)
+
+#define MIPITX_DSI_SW_CTRL	0x80
+#define SW_CTRL_EN			BIT(0)
+
+#define MIPITX_DSI_SW_CTRL_CON0	0x84
+#define SW_LNTC_LPTX_PRE_OE		BIT(0)
+#define SW_LNTC_LPTX_OE			BIT(1)
+#define SW_LNTC_LPTX_P			BIT(2)
+#define SW_LNTC_LPTX_N			BIT(3)
+#define SW_LNTC_HSTX_PRE_OE		BIT(4)
+#define SW_LNTC_HSTX_OE			BIT(5)
+#define SW_LNTC_HSTX_ZEROCLK		BIT(6)
+#define SW_LNT0_LPTX_PRE_OE		BIT(7)
+#define SW_LNT0_LPTX_OE			BIT(8)
+#define SW_LNT0_LPTX_P			BIT(9)
+#define SW_LNT0_LPTX_N			BIT(10)
+#define SW_LNT0_HSTX_PRE_OE		BIT(11)
+#define SW_LNT0_HSTX_OE			BIT(12)
+#define SW_LNT0_LPRX_EN			BIT(13)
+#define SW_LNT1_LPTX_PRE_OE		BIT(14)
+#define SW_LNT1_LPTX_OE			BIT(15)
+#define SW_LNT1_LPTX_P			BIT(16)
+#define SW_LNT1_LPTX_N			BIT(17)
+#define SW_LNT1_HSTX_PRE_OE		BIT(18)
+#define SW_LNT1_HSTX_OE			BIT(19)
+#define SW_LNT2_LPTX_PRE_OE		BIT(20)
+#define SW_LNT2_LPTX_OE			BIT(21)
+#define SW_LNT2_LPTX_P			BIT(22)
+#define SW_LNT2_LPTX_N			BIT(23)
+#define SW_LNT2_HSTX_PRE_OE		BIT(24)
+#define SW_LNT2_HSTX_OE			BIT(25)
+
+struct mtk_mipi_tx {
+	struct device *dev;
+	void __iomem *regs;
+	unsigned int data_rate;
+	struct clk_hw pll_hw;
+	struct clk *pll;
+};
+
+static inline struct mtk_mipi_tx *mtk_mipi_tx_from_clk_hw(struct clk_hw *hw)
+{
+	return container_of(hw, struct mtk_mipi_tx, pll_hw);
+}
+
+static void mtk_mipi_tx_clear_bits(struct mtk_mipi_tx *mipi_tx, u32 offset,
+				   u32 bits)
+{
+	u32 temp = readl(mipi_tx->regs + offset);
+
+	writel(temp & ~bits, mipi_tx->regs + offset);
+}
+
+static void mtk_mipi_tx_set_bits(struct mtk_mipi_tx *mipi_tx, u32 offset,
+				 u32 bits)
+{
+	u32 temp = readl(mipi_tx->regs + offset);
+
+	writel(temp | bits, mipi_tx->regs + offset);
+}
+
+static void mtk_mipi_tx_update_bits(struct mtk_mipi_tx *mipi_tx, u32 offset,
+				    u32 mask, u32 data)
+{
+	u32 temp = readl(mipi_tx->regs + offset);
+
+	writel((temp & ~mask) | (data & mask), mipi_tx->regs + offset);
+}
+
+static int mtk_mipi_tx_pll_prepare(struct clk_hw *hw)
+{
+	struct mtk_mipi_tx *mipi_tx = mtk_mipi_tx_from_clk_hw(hw);
+	unsigned int txdiv, txdiv0, txdiv1;
+	u64 pcw;
+
+	dev_dbg(mipi_tx->dev, "prepare: %u Hz\n", mipi_tx->data_rate);
+
+	if (mipi_tx->data_rate >= 500000000) {
+		txdiv = 1;
+		txdiv0 = 0;
+		txdiv1 = 0;
+	} else if (mipi_tx->data_rate >= 250000000) {
+		txdiv = 2;
+		txdiv0 = 1;
+		txdiv1 = 0;
+	} else if (mipi_tx->data_rate >= 125000000) {
+		txdiv = 4;
+		txdiv0 = 2;
+		txdiv1 = 0;
+	} else if (mipi_tx->data_rate > 62000000) {
+		txdiv = 8;
+		txdiv0 = 2;
+		txdiv1 = 1;
+	} else if (mipi_tx->data_rate >= 50000000) {
+		txdiv = 16;
+		txdiv0 = 2;
+		txdiv1 = 2;
+	} else {
+		return -EINVAL;
+	}
+
+	mtk_mipi_tx_update_bits(mipi_tx, MIPITX_DSI_BG_CON,
+				RG_DSI_VOUT_MSK |
+				RG_DSI_BG_CKEN | RG_DSI_BG_CORE_EN,
+				(4 << 20) | (4 << 17) | (4 << 14) |
+				(4 << 11) | (4 << 8) | (4 << 5) |
+				RG_DSI_BG_CKEN | RG_DSI_BG_CORE_EN);
+
+	usleep_range(30, 100);
+
+	mtk_mipi_tx_update_bits(mipi_tx, MIPITX_DSI_TOP_CON,
+				RG_DSI_LNT_IMP_CAL_CODE | RG_DSI_LNT_HS_BIAS_EN,
+				(8 << 4) | RG_DSI_LNT_HS_BIAS_EN);
+
+	mtk_mipi_tx_set_bits(mipi_tx, MIPITX_DSI_CON,
+			     RG_DSI_CKG_LDOOUT_EN | RG_DSI_LDOCORE_EN);
+
+	mtk_mipi_tx_update_bits(mipi_tx, MIPITX_DSI_PLL_PWR,
+				RG_DSI_MPPLL_SDM_PWR_ON |
+				RG_DSI_MPPLL_SDM_ISO_EN,
+				RG_DSI_MPPLL_SDM_PWR_ON);
+
+	mtk_mipi_tx_clear_bits(mipi_tx, MIPITX_DSI_PLL_CON0,
+			       RG_DSI_MPPLL_PLL_EN);
+
+	mtk_mipi_tx_update_bits(mipi_tx, MIPITX_DSI_PLL_CON0,
+				RG_DSI_MPPLL_TXDIV0 | RG_DSI_MPPLL_TXDIV1 |
+				RG_DSI_MPPLL_PREDIV,
+				(txdiv0 << 3) | (txdiv1 << 5));
+
+	/*
+	 * PLL PCW config
+	 * PCW bit 24~30 = integer part of pcw
+	 * PCW bit 0~23 = fractional part of pcw
+	 * pcw = data_Rate*4*txdiv/(Ref_clk*2);
+	 * Post DIV =4, so need data_Rate*4
+	 * Ref_clk is 26MHz
+	 */
+	pcw = div_u64(((u64)mipi_tx->data_rate * 2 * txdiv) << 24,
+		      26000000);
+	writel(pcw, mipi_tx->regs + MIPITX_DSI_PLL_CON2);
+
+	mtk_mipi_tx_set_bits(mipi_tx, MIPITX_DSI_PLL_CON1,
+			     RG_DSI_MPPLL_SDM_FRA_EN);
+
+	mtk_mipi_tx_set_bits(mipi_tx, MIPITX_DSI_PLL_CON0, RG_DSI_MPPLL_PLL_EN);
+
+	usleep_range(20, 100);
+
+	mtk_mipi_tx_clear_bits(mipi_tx, MIPITX_DSI_PLL_CON1,
+			       RG_DSI_MPPLL_SDM_SSC_EN);
+
+	return 0;
+}
+
+static void mtk_mipi_tx_pll_unprepare(struct clk_hw *hw)
+{
+	struct mtk_mipi_tx *mipi_tx = mtk_mipi_tx_from_clk_hw(hw);
+
+	dev_dbg(mipi_tx->dev, "unprepare\n");
+
+	mtk_mipi_tx_clear_bits(mipi_tx, MIPITX_DSI_PLL_CON0,
+			       RG_DSI_MPPLL_PLL_EN);
+
+	mtk_mipi_tx_update_bits(mipi_tx, MIPITX_DSI_PLL_PWR,
+				RG_DSI_MPPLL_SDM_ISO_EN |
+				RG_DSI_MPPLL_SDM_PWR_ON,
+				RG_DSI_MPPLL_SDM_ISO_EN);
+
+	mtk_mipi_tx_clear_bits(mipi_tx, MIPITX_DSI_TOP_CON,
+			       RG_DSI_LNT_HS_BIAS_EN);
+
+	mtk_mipi_tx_clear_bits(mipi_tx, MIPITX_DSI_CON,
+			       RG_DSI_CKG_LDOOUT_EN | RG_DSI_LDOCORE_EN);
+
+	mtk_mipi_tx_clear_bits(mipi_tx, MIPITX_DSI_BG_CON,
+			       RG_DSI_BG_CKEN | RG_DSI_BG_CORE_EN);
+
+	mtk_mipi_tx_clear_bits(mipi_tx, MIPITX_DSI_PLL_CON0,
+			       RG_DSI_MPPLL_DIV_MSK);
+}
+
+static long mtk_mipi_tx_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+				       unsigned long *prate)
+{
+	return clamp_val(rate, 50000000, 1250000000);
+}
+
+static int mtk_mipi_tx_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+				    unsigned long parent_rate)
+{
+	struct mtk_mipi_tx *mipi_tx = mtk_mipi_tx_from_clk_hw(hw);
+
+	dev_dbg(mipi_tx->dev, "set rate: %lu Hz\n", rate);
+
+	mipi_tx->data_rate = rate;
+
+	return 0;
+}
+
+static unsigned long mtk_mipi_tx_pll_recalc_rate(struct clk_hw *hw,
+						 unsigned long parent_rate)
+{
+	struct mtk_mipi_tx *mipi_tx = mtk_mipi_tx_from_clk_hw(hw);
+
+	return mipi_tx->data_rate;
+}
+
+static const struct clk_ops mtk_mipi_tx_pll_ops = {
+	.prepare = mtk_mipi_tx_pll_prepare,
+	.unprepare = mtk_mipi_tx_pll_unprepare,
+	.round_rate = mtk_mipi_tx_pll_round_rate,
+	.set_rate = mtk_mipi_tx_pll_set_rate,
+	.recalc_rate = mtk_mipi_tx_pll_recalc_rate,
+};
+
+static int mtk_mipi_tx_power_on_signal(struct phy *phy)
+{
+	struct mtk_mipi_tx *mipi_tx = phy_get_drvdata(phy);
+	unsigned int reg;
+
+	for (reg = MIPITX_DSI_CLOCK_LANE;
+	     reg <= MIPITX_DSI_DATA_LANE3; reg += 4)
+		mtk_mipi_tx_set_bits(mipi_tx, reg, RG_DSI_LNTx_LDOOUT_EN);
+
+	mtk_mipi_tx_clear_bits(mipi_tx, MIPITX_DSI_TOP_CON,
+			       RG_DSI_PAD_TIE_LOW_EN);
+
+	return 0;
+}
+
+static int mtk_mipi_tx_power_on(struct phy *phy)
+{
+	struct mtk_mipi_tx *mipi_tx = phy_get_drvdata(phy);
+	int ret;
+
+	/* Power up core and enable PLL */
+	ret = clk_prepare_enable(mipi_tx->pll);
+	if (ret < 0)
+		return ret;
+
+	/* Enable DSI Lane LDO outputs, disable pad tie low */
+	mtk_mipi_tx_power_on_signal(phy);
+
+	return 0;
+}
+
+static void mtk_mipi_tx_power_off_signal(struct phy *phy)
+{
+	struct mtk_mipi_tx *mipi_tx = phy_get_drvdata(phy);
+	unsigned int reg;
+
+	mtk_mipi_tx_set_bits(mipi_tx, MIPITX_DSI_TOP_CON,
+			     RG_DSI_PAD_TIE_LOW_EN);
+
+	for (reg = MIPITX_DSI_CLOCK_LANE;
+	     reg <= MIPITX_DSI_DATA_LANE3; reg += 4)
+		mtk_mipi_tx_clear_bits(mipi_tx, reg, RG_DSI_LNTx_LDOOUT_EN);
+}
+
+static int mtk_mipi_tx_power_off(struct phy *phy)
+{
+	struct mtk_mipi_tx *mipi_tx = phy_get_drvdata(phy);
+
+	/* Enable pad tie low, disable DSI Lane LDO outputs */
+	mtk_mipi_tx_power_off_signal(phy);
+
+	/* Disable PLL and power down core */
+	clk_disable_unprepare(mipi_tx->pll);
+
+	return 0;
+}
+
+static const struct phy_ops mtk_mipi_tx_ops = {
+	.power_on = mtk_mipi_tx_power_on,
+	.power_off = mtk_mipi_tx_power_off,
+	.owner = THIS_MODULE,
+};
+
+static int mtk_mipi_tx_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct mtk_mipi_tx *mipi_tx;
+	struct resource *mem;
+	struct clk *ref_clk;
+	const char *ref_clk_name;
+	struct clk_init_data clk_init = {
+		.ops = &mtk_mipi_tx_pll_ops,
+		.num_parents = 1,
+		.parent_names = (const char * const *)&ref_clk_name,
+		.flags = CLK_SET_RATE_GATE,
+	};
+	struct phy *phy;
+	struct phy_provider *phy_provider;
+	int ret;
+
+	mipi_tx = devm_kzalloc(dev, sizeof(*mipi_tx), GFP_KERNEL);
+	if (!mipi_tx)
+		return -ENOMEM;
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	mipi_tx->regs = devm_ioremap_resource(dev, mem);
+	if (IS_ERR(mipi_tx->regs)) {
+		ret = PTR_ERR(mipi_tx->regs);
+		dev_err(dev, "Failed to get memory resource: %d\n", ret);
+		return ret;
+	}
+
+	ref_clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(ref_clk)) {
+		ret = PTR_ERR(ref_clk);
+		dev_err(dev, "Failed to get reference clock: %d\n", ret);
+		return ret;
+	}
+	ref_clk_name = __clk_get_name(ref_clk);
+
+	ret = of_property_read_string(dev->of_node, "clock-output-names",
+				      &clk_init.name);
+	if (ret < 0) {
+		dev_err(dev, "Failed to read clock-output-names: %d\n", ret);
+		return ret;
+	}
+
+	mipi_tx->pll_hw.init = &clk_init;
+	mipi_tx->pll = devm_clk_register(dev, &mipi_tx->pll_hw);
+	if (IS_ERR(mipi_tx->pll)) {
+		ret = PTR_ERR(mipi_tx->pll);
+		dev_err(dev, "Failed to register PLL: %d\n", ret);
+		return ret;
+	}
+
+	phy = devm_phy_create(dev, NULL, &mtk_mipi_tx_ops);
+	if (IS_ERR(phy)) {
+		ret = PTR_ERR(phy);
+		dev_err(dev, "Failed to create MIPI D-PHY: %d\n", ret);
+		return ret;
+	}
+	phy_set_drvdata(phy, mipi_tx);
+
+	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+	if (IS_ERR(phy)) {
+		ret = PTR_ERR(phy_provider);
+		return ret;
+	}
+
+	mipi_tx->dev = dev;
+
+	return of_clk_add_provider(dev->of_node, of_clk_src_simple_get,
+				   mipi_tx->pll);
+}
+
+static int mtk_mipi_tx_remove(struct platform_device *pdev)
+{
+	of_clk_del_provider(pdev->dev.of_node);
+	return 0;
+}
+
+static const struct of_device_id mtk_mipi_tx_match[] = {
+	{ .compatible = "mediatek,mt8173-mipi-tx", },
+	{},
+};
+
+struct platform_driver mtk_mipi_tx_driver = {
+	.probe = mtk_mipi_tx_probe,
+	.remove = mtk_mipi_tx_remove,
+	.driver = {
+		.name = "mediatek-mipi-tx",
+		.of_match_table = mtk_mipi_tx_match,
+	},
+};
-- 
2.8.0.rc3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCH v16 5/8] drm/mediatek: Add DPI sub driver
  2016-05-06 13:05 [PATCH v16 0/8] MT8173 DRM support Philipp Zabel
                   ` (3 preceding siblings ...)
  2016-05-06 13:05 ` [PATCH v16 4/8] drm/mediatek: Add DSI sub driver Philipp Zabel
@ 2016-05-06 13:05 ` Philipp Zabel
  2016-05-06 13:05 ` [PATCH v16 6/8] clk: mediatek: make dpi0_sel propagate rate changes Philipp Zabel
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: Philipp Zabel @ 2016-05-06 13:05 UTC (permalink / raw)
  To: dri-devel
  Cc: Mark Rutland, Stephen Boyd, Michael Turquette, Jie Qiu,
	Cawa Cheng, YT Shen, Yingjoe Chen, devicetree, Jitao Shi, kernel,
	Pawel Moll, Ian Campbell, Rob Herring, linux-mediatek,
	Matthias Brugger, Paul Bolle, Emil Velikov, Tomasz Figa,
	Kumar Gala

From: Jie Qiu <jie.qiu@mediatek.com>

Add DPI connector/encoder to support HDMI output via the
attached HDMI bridge.

Signed-off-by: Jie Qiu <jie.qiu@mediatek.com>
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
---
Changes since v15:
 - Order mtk_drm_drivers alphabetically
---
 drivers/gpu/drm/mediatek/Makefile       |   3 +-
 drivers/gpu/drm/mediatek/mtk_dpi.c      | 769 ++++++++++++++++++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_dpi_regs.h | 228 ++++++++++
 drivers/gpu/drm/mediatek/mtk_drm_drv.c  |   1 +
 drivers/gpu/drm/mediatek/mtk_drm_drv.h  |   1 +
 5 files changed, 1001 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/mediatek/mtk_dpi.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_dpi_regs.h

diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
index e781db5a..5fcf58e 100644
--- a/drivers/gpu/drm/mediatek/Makefile
+++ b/drivers/gpu/drm/mediatek/Makefile
@@ -8,6 +8,7 @@ mediatek-drm-y := mtk_disp_ovl.o \
 		  mtk_drm_gem.o \
 		  mtk_drm_plane.o \
 		  mtk_dsi.o \
-		  mtk_mipi_tx.o
+		  mtk_mipi_tx.o \
+		  mtk_dpi.o
 
 obj-$(CONFIG_DRM_MEDIATEK) += mediatek-drm.o
diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
new file mode 100644
index 0000000..d05ca79
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
@@ -0,0 +1,769 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: Jie Qiu <jie.qiu@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 <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <linux/kernel.h>
+#include <linux/component.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/clk.h>
+
+#include "mtk_dpi_regs.h"
+#include "mtk_drm_ddp_comp.h"
+
+enum mtk_dpi_out_bit_num {
+	MTK_DPI_OUT_BIT_NUM_8BITS,
+	MTK_DPI_OUT_BIT_NUM_10BITS,
+	MTK_DPI_OUT_BIT_NUM_12BITS,
+	MTK_DPI_OUT_BIT_NUM_16BITS
+};
+
+enum mtk_dpi_out_yc_map {
+	MTK_DPI_OUT_YC_MAP_RGB,
+	MTK_DPI_OUT_YC_MAP_CYCY,
+	MTK_DPI_OUT_YC_MAP_YCYC,
+	MTK_DPI_OUT_YC_MAP_CY,
+	MTK_DPI_OUT_YC_MAP_YC
+};
+
+enum mtk_dpi_out_channel_swap {
+	MTK_DPI_OUT_CHANNEL_SWAP_RGB,
+	MTK_DPI_OUT_CHANNEL_SWAP_GBR,
+	MTK_DPI_OUT_CHANNEL_SWAP_BRG,
+	MTK_DPI_OUT_CHANNEL_SWAP_RBG,
+	MTK_DPI_OUT_CHANNEL_SWAP_GRB,
+	MTK_DPI_OUT_CHANNEL_SWAP_BGR
+};
+
+enum mtk_dpi_out_color_format {
+	MTK_DPI_COLOR_FORMAT_RGB,
+	MTK_DPI_COLOR_FORMAT_RGB_FULL,
+	MTK_DPI_COLOR_FORMAT_YCBCR_444,
+	MTK_DPI_COLOR_FORMAT_YCBCR_422,
+	MTK_DPI_COLOR_FORMAT_XV_YCC,
+	MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL,
+	MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL
+};
+
+struct mtk_dpi {
+	struct mtk_ddp_comp ddp_comp;
+	struct drm_encoder encoder;
+	void __iomem *regs;
+	struct device *dev;
+	struct clk *engine_clk;
+	struct clk *pixel_clk;
+	struct clk *tvd_clk;
+	int irq;
+	struct drm_display_mode mode;
+	enum mtk_dpi_out_color_format color_format;
+	enum mtk_dpi_out_yc_map yc_map;
+	enum mtk_dpi_out_bit_num bit_num;
+	enum mtk_dpi_out_channel_swap channel_swap;
+	bool power_sta;
+	u8 power_ctl;
+};
+
+static inline struct mtk_dpi *mtk_dpi_from_encoder(struct drm_encoder *e)
+{
+	return container_of(e, struct mtk_dpi, encoder);
+}
+
+enum mtk_dpi_polarity {
+	MTK_DPI_POLARITY_RISING,
+	MTK_DPI_POLARITY_FALLING,
+};
+
+enum mtk_dpi_power_ctl {
+	DPI_POWER_START = BIT(0),
+	DPI_POWER_ENABLE = BIT(1),
+};
+
+struct mtk_dpi_polarities {
+	enum mtk_dpi_polarity de_pol;
+	enum mtk_dpi_polarity ck_pol;
+	enum mtk_dpi_polarity hsync_pol;
+	enum mtk_dpi_polarity vsync_pol;
+};
+
+struct mtk_dpi_sync_param {
+	u32 sync_width;
+	u32 front_porch;
+	u32 back_porch;
+	bool shift_half_line;
+};
+
+struct mtk_dpi_yc_limit {
+	u16 y_top;
+	u16 y_bottom;
+	u16 c_top;
+	u16 c_bottom;
+};
+
+static void mtk_dpi_mask(struct mtk_dpi *dpi, u32 offset, u32 val, u32 mask)
+{
+	u32 tmp = readl(dpi->regs + offset) & ~mask;
+
+	tmp |= (val & mask);
+	writel(tmp, dpi->regs + offset);
+}
+
+static void mtk_dpi_sw_reset(struct mtk_dpi *dpi, bool reset)
+{
+	mtk_dpi_mask(dpi, DPI_RET, reset ? RST : 0, RST);
+}
+
+static void mtk_dpi_enable(struct mtk_dpi *dpi)
+{
+	mtk_dpi_mask(dpi, DPI_EN, EN, EN);
+}
+
+static void mtk_dpi_disable(struct mtk_dpi *dpi)
+{
+	mtk_dpi_mask(dpi, DPI_EN, 0, EN);
+}
+
+static void mtk_dpi_config_hsync(struct mtk_dpi *dpi,
+				 struct mtk_dpi_sync_param *sync)
+{
+	mtk_dpi_mask(dpi, DPI_TGEN_HWIDTH,
+		     sync->sync_width << HPW, HPW_MASK);
+	mtk_dpi_mask(dpi, DPI_TGEN_HPORCH,
+		     sync->back_porch << HBP, HBP_MASK);
+	mtk_dpi_mask(dpi, DPI_TGEN_HPORCH, sync->front_porch << HFP,
+		     HFP_MASK);
+}
+
+static void mtk_dpi_config_vsync(struct mtk_dpi *dpi,
+				 struct mtk_dpi_sync_param *sync,
+				 u32 width_addr, u32 porch_addr)
+{
+	mtk_dpi_mask(dpi, width_addr,
+		     sync->sync_width << VSYNC_WIDTH_SHIFT,
+		     VSYNC_WIDTH_MASK);
+	mtk_dpi_mask(dpi, width_addr,
+		     sync->shift_half_line << VSYNC_HALF_LINE_SHIFT,
+		     VSYNC_HALF_LINE_MASK);
+	mtk_dpi_mask(dpi, porch_addr,
+		     sync->back_porch << VSYNC_BACK_PORCH_SHIFT,
+		     VSYNC_BACK_PORCH_MASK);
+	mtk_dpi_mask(dpi, porch_addr,
+		     sync->front_porch << VSYNC_FRONT_PORCH_SHIFT,
+		     VSYNC_FRONT_PORCH_MASK);
+}
+
+static void mtk_dpi_config_vsync_lodd(struct mtk_dpi *dpi,
+				      struct mtk_dpi_sync_param *sync)
+{
+	mtk_dpi_config_vsync(dpi, sync, DPI_TGEN_VWIDTH, DPI_TGEN_VPORCH);
+}
+
+static void mtk_dpi_config_vsync_leven(struct mtk_dpi *dpi,
+				       struct mtk_dpi_sync_param *sync)
+{
+	mtk_dpi_config_vsync(dpi, sync, DPI_TGEN_VWIDTH_LEVEN,
+			     DPI_TGEN_VPORCH_LEVEN);
+}
+
+static void mtk_dpi_config_vsync_rodd(struct mtk_dpi *dpi,
+				      struct mtk_dpi_sync_param *sync)
+{
+	mtk_dpi_config_vsync(dpi, sync, DPI_TGEN_VWIDTH_RODD,
+			     DPI_TGEN_VPORCH_RODD);
+}
+
+static void mtk_dpi_config_vsync_reven(struct mtk_dpi *dpi,
+				       struct mtk_dpi_sync_param *sync)
+{
+	mtk_dpi_config_vsync(dpi, sync, DPI_TGEN_VWIDTH_REVEN,
+			     DPI_TGEN_VPORCH_REVEN);
+}
+
+static void mtk_dpi_config_pol(struct mtk_dpi *dpi,
+			       struct mtk_dpi_polarities *dpi_pol)
+{
+	unsigned int pol;
+
+	pol = (dpi_pol->ck_pol == MTK_DPI_POLARITY_RISING ? 0 : CK_POL) |
+	      (dpi_pol->de_pol == MTK_DPI_POLARITY_RISING ? 0 : DE_POL) |
+	      (dpi_pol->hsync_pol == MTK_DPI_POLARITY_RISING ? 0 : HSYNC_POL) |
+	      (dpi_pol->vsync_pol == MTK_DPI_POLARITY_RISING ? 0 : VSYNC_POL);
+	mtk_dpi_mask(dpi, DPI_OUTPUT_SETTING, pol,
+		     CK_POL | DE_POL | HSYNC_POL | VSYNC_POL);
+}
+
+static void mtk_dpi_config_3d(struct mtk_dpi *dpi, bool en_3d)
+{
+	mtk_dpi_mask(dpi, DPI_CON, en_3d ? TDFP_EN : 0, TDFP_EN);
+}
+
+static void mtk_dpi_config_interface(struct mtk_dpi *dpi, bool inter)
+{
+	mtk_dpi_mask(dpi, DPI_CON, inter ? INTL_EN : 0, INTL_EN);
+}
+
+static void mtk_dpi_config_fb_size(struct mtk_dpi *dpi, u32 width, u32 height)
+{
+	mtk_dpi_mask(dpi, DPI_SIZE, width << HSIZE, HSIZE_MASK);
+	mtk_dpi_mask(dpi, DPI_SIZE, height << VSIZE, VSIZE_MASK);
+}
+
+static void mtk_dpi_config_channel_limit(struct mtk_dpi *dpi,
+					 struct mtk_dpi_yc_limit *limit)
+{
+	mtk_dpi_mask(dpi, DPI_Y_LIMIT, limit->y_bottom << Y_LIMINT_BOT,
+		     Y_LIMINT_BOT_MASK);
+	mtk_dpi_mask(dpi, DPI_Y_LIMIT, limit->y_top << Y_LIMINT_TOP,
+		     Y_LIMINT_TOP_MASK);
+	mtk_dpi_mask(dpi, DPI_C_LIMIT, limit->c_bottom << C_LIMIT_BOT,
+		     C_LIMIT_BOT_MASK);
+	mtk_dpi_mask(dpi, DPI_C_LIMIT, limit->c_top << C_LIMIT_TOP,
+		     C_LIMIT_TOP_MASK);
+}
+
+static void mtk_dpi_config_bit_num(struct mtk_dpi *dpi,
+				   enum mtk_dpi_out_bit_num num)
+{
+	u32 val;
+
+	switch (num) {
+	case MTK_DPI_OUT_BIT_NUM_8BITS:
+		val = OUT_BIT_8;
+		break;
+	case MTK_DPI_OUT_BIT_NUM_10BITS:
+		val = OUT_BIT_10;
+		break;
+	case MTK_DPI_OUT_BIT_NUM_12BITS:
+		val = OUT_BIT_12;
+		break;
+	case MTK_DPI_OUT_BIT_NUM_16BITS:
+		val = OUT_BIT_16;
+		break;
+	default:
+		val = OUT_BIT_8;
+		break;
+	}
+	mtk_dpi_mask(dpi, DPI_OUTPUT_SETTING, val << OUT_BIT,
+		     OUT_BIT_MASK);
+}
+
+static void mtk_dpi_config_yc_map(struct mtk_dpi *dpi,
+				  enum mtk_dpi_out_yc_map map)
+{
+	u32 val;
+
+	switch (map) {
+	case MTK_DPI_OUT_YC_MAP_RGB:
+		val = YC_MAP_RGB;
+		break;
+	case MTK_DPI_OUT_YC_MAP_CYCY:
+		val = YC_MAP_CYCY;
+		break;
+	case MTK_DPI_OUT_YC_MAP_YCYC:
+		val = YC_MAP_YCYC;
+		break;
+	case MTK_DPI_OUT_YC_MAP_CY:
+		val = YC_MAP_CY;
+		break;
+	case MTK_DPI_OUT_YC_MAP_YC:
+		val = YC_MAP_YC;
+		break;
+	default:
+		val = YC_MAP_RGB;
+		break;
+	}
+
+	mtk_dpi_mask(dpi, DPI_OUTPUT_SETTING, val << YC_MAP, YC_MAP_MASK);
+}
+
+static void mtk_dpi_config_channel_swap(struct mtk_dpi *dpi,
+					enum mtk_dpi_out_channel_swap swap)
+{
+	u32 val;
+
+	switch (swap) {
+	case MTK_DPI_OUT_CHANNEL_SWAP_RGB:
+		val = SWAP_RGB;
+		break;
+	case MTK_DPI_OUT_CHANNEL_SWAP_GBR:
+		val = SWAP_GBR;
+		break;
+	case MTK_DPI_OUT_CHANNEL_SWAP_BRG:
+		val = SWAP_BRG;
+		break;
+	case MTK_DPI_OUT_CHANNEL_SWAP_RBG:
+		val = SWAP_RBG;
+		break;
+	case MTK_DPI_OUT_CHANNEL_SWAP_GRB:
+		val = SWAP_GRB;
+		break;
+	case MTK_DPI_OUT_CHANNEL_SWAP_BGR:
+		val = SWAP_BGR;
+		break;
+	default:
+		val = SWAP_RGB;
+		break;
+	}
+
+	mtk_dpi_mask(dpi, DPI_OUTPUT_SETTING, val << CH_SWAP, CH_SWAP_MASK);
+}
+
+static void mtk_dpi_config_yuv422_enable(struct mtk_dpi *dpi, bool enable)
+{
+	mtk_dpi_mask(dpi, DPI_CON, enable ? YUV422_EN : 0, YUV422_EN);
+}
+
+static void mtk_dpi_config_csc_enable(struct mtk_dpi *dpi, bool enable)
+{
+	mtk_dpi_mask(dpi, DPI_CON, enable ? CSC_ENABLE : 0, CSC_ENABLE);
+}
+
+static void mtk_dpi_config_swap_input(struct mtk_dpi *dpi, bool enable)
+{
+	mtk_dpi_mask(dpi, DPI_CON, enable ? IN_RB_SWAP : 0, IN_RB_SWAP);
+}
+
+static void mtk_dpi_config_2n_h_fre(struct mtk_dpi *dpi)
+{
+	mtk_dpi_mask(dpi, DPI_H_FRE_CON, H_FRE_2N, H_FRE_2N);
+}
+
+static void mtk_dpi_config_color_format(struct mtk_dpi *dpi,
+					enum mtk_dpi_out_color_format format)
+{
+	if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_444) ||
+	    (format == MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL)) {
+		mtk_dpi_config_yuv422_enable(dpi, false);
+		mtk_dpi_config_csc_enable(dpi, true);
+		mtk_dpi_config_swap_input(dpi, false);
+		mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_BGR);
+	} else if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_422) ||
+		   (format == MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL)) {
+		mtk_dpi_config_yuv422_enable(dpi, true);
+		mtk_dpi_config_csc_enable(dpi, true);
+		mtk_dpi_config_swap_input(dpi, true);
+		mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
+	} else {
+		mtk_dpi_config_yuv422_enable(dpi, false);
+		mtk_dpi_config_csc_enable(dpi, false);
+		mtk_dpi_config_swap_input(dpi, false);
+		mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
+	}
+}
+
+static void mtk_dpi_power_off(struct mtk_dpi *dpi, enum mtk_dpi_power_ctl pctl)
+{
+	dpi->power_ctl &= ~pctl;
+
+	if ((dpi->power_ctl & DPI_POWER_START) ||
+	    (dpi->power_ctl & DPI_POWER_ENABLE))
+		return;
+
+	if (!dpi->power_sta)
+		return;
+
+	mtk_dpi_disable(dpi);
+	clk_disable_unprepare(dpi->pixel_clk);
+	clk_disable_unprepare(dpi->engine_clk);
+	dpi->power_sta = false;
+}
+
+static int mtk_dpi_power_on(struct mtk_dpi *dpi, enum mtk_dpi_power_ctl pctl)
+{
+	int ret;
+
+	dpi->power_ctl |= pctl;
+
+	if (!(dpi->power_ctl & DPI_POWER_START) &&
+	    !(dpi->power_ctl & DPI_POWER_ENABLE))
+		return 0;
+
+	if (dpi->power_sta)
+		return 0;
+
+	ret = clk_prepare_enable(dpi->engine_clk);
+	if (ret) {
+		dev_err(dpi->dev, "Failed to enable engine clock: %d\n", ret);
+		goto err_eng;
+	}
+
+	ret = clk_prepare_enable(dpi->pixel_clk);
+	if (ret) {
+		dev_err(dpi->dev, "Failed to enable pixel clock: %d\n", ret);
+		goto err_pixel;
+	}
+
+	mtk_dpi_enable(dpi);
+	dpi->power_sta = true;
+	return 0;
+
+err_pixel:
+	clk_disable_unprepare(dpi->engine_clk);
+err_eng:
+	dpi->power_ctl &= ~pctl;
+	return ret;
+}
+
+static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
+				    struct drm_display_mode *mode)
+{
+	struct mtk_dpi_yc_limit limit;
+	struct mtk_dpi_polarities dpi_pol;
+	struct mtk_dpi_sync_param hsync;
+	struct mtk_dpi_sync_param vsync_lodd = { 0 };
+	struct mtk_dpi_sync_param vsync_leven = { 0 };
+	struct mtk_dpi_sync_param vsync_rodd = { 0 };
+	struct mtk_dpi_sync_param vsync_reven = { 0 };
+	unsigned long pix_rate;
+	unsigned long pll_rate;
+	unsigned int factor;
+
+	if (!dpi) {
+		dev_err(dpi->dev, "invalid argument\n");
+		return -EINVAL;
+	}
+
+	pix_rate = 1000UL * mode->clock;
+	if (mode->clock <= 74000)
+		factor = 8 * 3;
+	else
+		factor = 4 * 3;
+	pll_rate = pix_rate * factor;
+
+	dev_dbg(dpi->dev, "Want PLL %lu Hz, pixel clock %lu Hz\n",
+		pll_rate, pix_rate);
+
+	clk_set_rate(dpi->tvd_clk, pll_rate);
+	pll_rate = clk_get_rate(dpi->tvd_clk);
+
+	pix_rate = pll_rate / factor;
+	clk_set_rate(dpi->pixel_clk, pix_rate);
+	pix_rate = clk_get_rate(dpi->pixel_clk);
+
+	dev_dbg(dpi->dev, "Got  PLL %lu Hz, pixel clock %lu Hz\n",
+		pll_rate, pix_rate);
+
+	limit.c_bottom = 0x0010;
+	limit.c_top = 0x0FE0;
+	limit.y_bottom = 0x0010;
+	limit.y_top = 0x0FE0;
+
+	dpi_pol.ck_pol = MTK_DPI_POLARITY_FALLING;
+	dpi_pol.de_pol = MTK_DPI_POLARITY_RISING;
+	dpi_pol.hsync_pol = mode->flags & DRM_MODE_FLAG_PHSYNC ?
+			    MTK_DPI_POLARITY_FALLING : MTK_DPI_POLARITY_RISING;
+	dpi_pol.vsync_pol = mode->flags & DRM_MODE_FLAG_PVSYNC ?
+			    MTK_DPI_POLARITY_FALLING : MTK_DPI_POLARITY_RISING;
+
+	hsync.sync_width = mode->hsync_end - mode->hsync_start;
+	hsync.back_porch = mode->htotal - mode->hsync_end;
+	hsync.front_porch = mode->hsync_start - mode->hdisplay;
+	hsync.shift_half_line = false;
+
+	vsync_lodd.sync_width = mode->vsync_end - mode->vsync_start;
+	vsync_lodd.back_porch = mode->vtotal - mode->vsync_end;
+	vsync_lodd.front_porch = mode->vsync_start - mode->vdisplay;
+	vsync_lodd.shift_half_line = false;
+
+	if (mode->flags & DRM_MODE_FLAG_INTERLACE &&
+	    mode->flags & DRM_MODE_FLAG_3D_MASK) {
+		vsync_leven = vsync_lodd;
+		vsync_rodd = vsync_lodd;
+		vsync_reven = vsync_lodd;
+		vsync_leven.shift_half_line = true;
+		vsync_reven.shift_half_line = true;
+	} else if (mode->flags & DRM_MODE_FLAG_INTERLACE &&
+		   !(mode->flags & DRM_MODE_FLAG_3D_MASK)) {
+		vsync_leven = vsync_lodd;
+		vsync_leven.shift_half_line = true;
+	} else if (!(mode->flags & DRM_MODE_FLAG_INTERLACE) &&
+		   mode->flags & DRM_MODE_FLAG_3D_MASK) {
+		vsync_rodd = vsync_lodd;
+	}
+	mtk_dpi_sw_reset(dpi, true);
+	mtk_dpi_config_pol(dpi, &dpi_pol);
+
+	mtk_dpi_config_hsync(dpi, &hsync);
+	mtk_dpi_config_vsync_lodd(dpi, &vsync_lodd);
+	mtk_dpi_config_vsync_rodd(dpi, &vsync_rodd);
+	mtk_dpi_config_vsync_leven(dpi, &vsync_leven);
+	mtk_dpi_config_vsync_reven(dpi, &vsync_reven);
+
+	mtk_dpi_config_3d(dpi, !!(mode->flags & DRM_MODE_FLAG_3D_MASK));
+	mtk_dpi_config_interface(dpi, !!(mode->flags &
+					 DRM_MODE_FLAG_INTERLACE));
+	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+		mtk_dpi_config_fb_size(dpi, mode->hdisplay, mode->vdisplay / 2);
+	else
+		mtk_dpi_config_fb_size(dpi, mode->hdisplay, mode->vdisplay);
+
+	mtk_dpi_config_channel_limit(dpi, &limit);
+	mtk_dpi_config_bit_num(dpi, dpi->bit_num);
+	mtk_dpi_config_channel_swap(dpi, dpi->channel_swap);
+	mtk_dpi_config_yc_map(dpi, dpi->yc_map);
+	mtk_dpi_config_color_format(dpi, dpi->color_format);
+	mtk_dpi_config_2n_h_fre(dpi);
+	mtk_dpi_sw_reset(dpi, false);
+
+	return 0;
+}
+
+static void mtk_dpi_encoder_destroy(struct drm_encoder *encoder)
+{
+	drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_funcs mtk_dpi_encoder_funcs = {
+	.destroy = mtk_dpi_encoder_destroy,
+};
+
+static bool mtk_dpi_encoder_mode_fixup(struct drm_encoder *encoder,
+				       const struct drm_display_mode *mode,
+				       struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+static void mtk_dpi_encoder_mode_set(struct drm_encoder *encoder,
+				     struct drm_display_mode *mode,
+				     struct drm_display_mode *adjusted_mode)
+{
+	struct mtk_dpi *dpi = mtk_dpi_from_encoder(encoder);
+
+	drm_mode_copy(&dpi->mode, adjusted_mode);
+}
+
+static void mtk_dpi_encoder_disable(struct drm_encoder *encoder)
+{
+	struct mtk_dpi *dpi = mtk_dpi_from_encoder(encoder);
+
+	mtk_dpi_power_off(dpi, DPI_POWER_ENABLE);
+}
+
+static void mtk_dpi_encoder_enable(struct drm_encoder *encoder)
+{
+	struct mtk_dpi *dpi = mtk_dpi_from_encoder(encoder);
+
+	mtk_dpi_power_on(dpi, DPI_POWER_ENABLE);
+	mtk_dpi_set_display_mode(dpi, &dpi->mode);
+}
+
+static int mtk_dpi_atomic_check(struct drm_encoder *encoder,
+				struct drm_crtc_state *crtc_state,
+				struct drm_connector_state *conn_state)
+{
+	return 0;
+}
+
+static const struct drm_encoder_helper_funcs mtk_dpi_encoder_helper_funcs = {
+	.mode_fixup = mtk_dpi_encoder_mode_fixup,
+	.mode_set = mtk_dpi_encoder_mode_set,
+	.disable = mtk_dpi_encoder_disable,
+	.enable = mtk_dpi_encoder_enable,
+	.atomic_check = mtk_dpi_atomic_check,
+};
+
+static void mtk_dpi_start(struct mtk_ddp_comp *comp)
+{
+	struct mtk_dpi *dpi = container_of(comp, struct mtk_dpi, ddp_comp);
+
+	mtk_dpi_power_on(dpi, DPI_POWER_START);
+}
+
+static void mtk_dpi_stop(struct mtk_ddp_comp *comp)
+{
+	struct mtk_dpi *dpi = container_of(comp, struct mtk_dpi, ddp_comp);
+
+	mtk_dpi_power_off(dpi, DPI_POWER_START);
+}
+
+static const struct mtk_ddp_comp_funcs mtk_dpi_funcs = {
+	.start = mtk_dpi_start,
+	.stop = mtk_dpi_stop,
+};
+
+static int mtk_dpi_bind(struct device *dev, struct device *master, void *data)
+{
+	struct mtk_dpi *dpi = dev_get_drvdata(dev);
+	struct drm_device *drm_dev = data;
+	int ret;
+
+	ret = mtk_ddp_comp_register(drm_dev, &dpi->ddp_comp);
+	if (ret < 0) {
+		dev_err(dev, "Failed to register component %s: %d\n",
+			dev->of_node->full_name, ret);
+		return ret;
+	}
+
+	ret = drm_encoder_init(drm_dev, &dpi->encoder, &mtk_dpi_encoder_funcs,
+			       DRM_MODE_ENCODER_TMDS, NULL);
+	if (ret) {
+		dev_err(dev, "Failed to initialize decoder: %d\n", ret);
+		goto err_unregister;
+	}
+	drm_encoder_helper_add(&dpi->encoder, &mtk_dpi_encoder_helper_funcs);
+
+	/* Currently DPI0 is fixed to be driven by OVL1 */
+	dpi->encoder.possible_crtcs = BIT(1);
+
+	dpi->encoder.bridge->encoder = &dpi->encoder;
+	ret = drm_bridge_attach(dpi->encoder.dev, dpi->encoder.bridge);
+	if (ret) {
+		dev_err(dev, "Failed to attach bridge: %d\n", ret);
+		goto err_cleanup;
+	}
+
+	dpi->bit_num = MTK_DPI_OUT_BIT_NUM_8BITS;
+	dpi->channel_swap = MTK_DPI_OUT_CHANNEL_SWAP_RGB;
+	dpi->yc_map = MTK_DPI_OUT_YC_MAP_RGB;
+	dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
+
+	return 0;
+
+err_cleanup:
+	drm_encoder_cleanup(&dpi->encoder);
+err_unregister:
+	mtk_ddp_comp_unregister(drm_dev, &dpi->ddp_comp);
+	return ret;
+}
+
+static void mtk_dpi_unbind(struct device *dev, struct device *master,
+			   void *data)
+{
+	struct mtk_dpi *dpi = dev_get_drvdata(dev);
+	struct drm_device *drm_dev = data;
+
+	drm_encoder_cleanup(&dpi->encoder);
+	mtk_ddp_comp_unregister(drm_dev, &dpi->ddp_comp);
+}
+
+static const struct component_ops mtk_dpi_component_ops = {
+	.bind = mtk_dpi_bind,
+	.unbind = mtk_dpi_unbind,
+};
+
+static int mtk_dpi_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct mtk_dpi *dpi;
+	struct resource *mem;
+	struct device_node *ep, *bridge_node = NULL;
+	int comp_id;
+	int ret;
+
+	dpi = devm_kzalloc(dev, sizeof(*dpi), GFP_KERNEL);
+	if (!dpi)
+		return -ENOMEM;
+
+	dpi->dev = dev;
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	dpi->regs = devm_ioremap_resource(dev, mem);
+	if (IS_ERR(dpi->regs)) {
+		ret = PTR_ERR(dpi->regs);
+		dev_err(dev, "Failed to ioremap mem resource: %d\n", ret);
+		return ret;
+	}
+
+	dpi->engine_clk = devm_clk_get(dev, "engine");
+	if (IS_ERR(dpi->engine_clk)) {
+		ret = PTR_ERR(dpi->engine_clk);
+		dev_err(dev, "Failed to get engine clock: %d\n", ret);
+		return ret;
+	}
+
+	dpi->pixel_clk = devm_clk_get(dev, "pixel");
+	if (IS_ERR(dpi->pixel_clk)) {
+		ret = PTR_ERR(dpi->pixel_clk);
+		dev_err(dev, "Failed to get pixel clock: %d\n", ret);
+		return ret;
+	}
+
+	dpi->tvd_clk = devm_clk_get(dev, "pll");
+	if (IS_ERR(dpi->tvd_clk)) {
+		ret = PTR_ERR(dpi->tvd_clk);
+		dev_err(dev, "Failed to get tvdpll clock: %d\n", ret);
+		return ret;
+	}
+
+	dpi->irq = platform_get_irq(pdev, 0);
+	if (dpi->irq <= 0) {
+		dev_err(dev, "Failed to get irq: %d\n", dpi->irq);
+		return -EINVAL;
+	}
+
+	ep = of_graph_get_next_endpoint(dev->of_node, NULL);
+	if (ep) {
+		bridge_node = of_graph_get_remote_port_parent(ep);
+		of_node_put(ep);
+	}
+	if (!bridge_node) {
+		dev_err(dev, "Failed to find bridge node\n");
+		return -ENODEV;
+	}
+
+	dev_info(dev, "Found bridge node: %s\n", bridge_node->full_name);
+
+	dpi->encoder.bridge = of_drm_find_bridge(bridge_node);
+	of_node_put(bridge_node);
+	if (!dpi->encoder.bridge)
+		return -EPROBE_DEFER;
+
+	comp_id = mtk_ddp_comp_get_id(dev->of_node, MTK_DPI);
+	if (comp_id < 0) {
+		dev_err(dev, "Failed to identify by alias: %d\n", comp_id);
+		return comp_id;
+	}
+
+	ret = mtk_ddp_comp_init(dev, dev->of_node, &dpi->ddp_comp, comp_id,
+				&mtk_dpi_funcs);
+	if (ret) {
+		dev_err(dev, "Failed to initialize component: %d\n", ret);
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, dpi);
+
+	ret = component_add(dev, &mtk_dpi_component_ops);
+	if (ret) {
+		dev_err(dev, "Failed to add component: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int mtk_dpi_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &mtk_dpi_component_ops);
+
+	return 0;
+}
+
+static const struct of_device_id mtk_dpi_of_ids[] = {
+	{ .compatible = "mediatek,mt8173-dpi", },
+	{}
+};
+
+struct platform_driver mtk_dpi_driver = {
+	.probe = mtk_dpi_probe,
+	.remove = mtk_dpi_remove,
+	.driver = {
+		.name = "mediatek-dpi",
+		.of_match_table = mtk_dpi_of_ids,
+	},
+};
diff --git a/drivers/gpu/drm/mediatek/mtk_dpi_regs.h b/drivers/gpu/drm/mediatek/mtk_dpi_regs.h
new file mode 100644
index 0000000..4b6ad47
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_dpi_regs.h
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: Jie Qiu <jie.qiu@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_DPI_REGS_H
+#define __MTK_DPI_REGS_H
+
+#define DPI_EN			0x00
+#define EN				BIT(0)
+
+#define DPI_RET			0x04
+#define RST				BIT(0)
+
+#define DPI_INTEN		0x08
+#define INT_VSYNC_EN			BIT(0)
+#define INT_VDE_EN			BIT(1)
+#define INT_UNDERFLOW_EN		BIT(2)
+
+#define DPI_INTSTA		0x0C
+#define INT_VSYNC_STA			BIT(0)
+#define INT_VDE_STA			BIT(1)
+#define INT_UNDERFLOW_STA		BIT(2)
+
+#define DPI_CON			0x10
+#define BG_ENABLE			BIT(0)
+#define IN_RB_SWAP			BIT(1)
+#define INTL_EN				BIT(2)
+#define TDFP_EN				BIT(3)
+#define CLPF_EN				BIT(4)
+#define YUV422_EN			BIT(5)
+#define CSC_ENABLE			BIT(6)
+#define R601_SEL			BIT(7)
+#define EMBSYNC_EN			BIT(8)
+#define VS_LODD_EN			BIT(16)
+#define VS_LEVEN_EN			BIT(17)
+#define VS_RODD_EN			BIT(18)
+#define VS_REVEN			BIT(19)
+#define FAKE_DE_LODD			BIT(20)
+#define FAKE_DE_LEVEN			BIT(21)
+#define FAKE_DE_RODD			BIT(22)
+#define FAKE_DE_REVEN			BIT(23)
+
+#define DPI_OUTPUT_SETTING	0x14
+#define CH_SWAP				0
+#define CH_SWAP_MASK			(0x7 << 0)
+#define SWAP_RGB			0x00
+#define SWAP_GBR			0x01
+#define SWAP_BRG			0x02
+#define SWAP_RBG			0x03
+#define SWAP_GRB			0x04
+#define SWAP_BGR			0x05
+#define BIT_SWAP			BIT(3)
+#define B_MASK				BIT(4)
+#define G_MASK				BIT(5)
+#define R_MASK				BIT(6)
+#define DE_MASK				BIT(8)
+#define HS_MASK				BIT(9)
+#define VS_MASK				BIT(10)
+#define DE_POL				BIT(12)
+#define HSYNC_POL			BIT(13)
+#define VSYNC_POL			BIT(14)
+#define CK_POL				BIT(15)
+#define OEN_OFF				BIT(16)
+#define EDGE_SEL			BIT(17)
+#define OUT_BIT				18
+#define OUT_BIT_MASK			(0x3 << 18)
+#define OUT_BIT_8			0x00
+#define OUT_BIT_10			0x01
+#define OUT_BIT_12			0x02
+#define OUT_BIT_16			0x03
+#define YC_MAP				20
+#define YC_MAP_MASK			(0x7 << 20)
+#define YC_MAP_RGB			0x00
+#define YC_MAP_CYCY			0x04
+#define YC_MAP_YCYC			0x05
+#define YC_MAP_CY			0x06
+#define YC_MAP_YC			0x07
+
+#define DPI_SIZE		0x18
+#define HSIZE				0
+#define HSIZE_MASK			(0x1FFF << 0)
+#define VSIZE				16
+#define VSIZE_MASK			(0x1FFF << 16)
+
+#define DPI_DDR_SETTING		0x1C
+#define DDR_EN				BIT(0)
+#define DDDR_SEL			BIT(1)
+#define DDR_4PHASE			BIT(2)
+#define DDR_WIDTH			(0x3 << 4)
+#define DDR_PAD_MODE			(0x1 << 8)
+
+#define DPI_TGEN_HWIDTH		0x20
+#define HPW				0
+#define HPW_MASK			(0xFFF << 0)
+
+#define DPI_TGEN_HPORCH		0x24
+#define HBP				0
+#define HBP_MASK			(0xFFF << 0)
+#define HFP				16
+#define HFP_MASK			(0xFFF << 16)
+
+#define DPI_TGEN_VWIDTH		0x28
+#define DPI_TGEN_VPORCH		0x2C
+
+#define VSYNC_WIDTH_SHIFT		0
+#define VSYNC_WIDTH_MASK		(0xFFF << 0)
+#define VSYNC_HALF_LINE_SHIFT		16
+#define VSYNC_HALF_LINE_MASK		BIT(16)
+#define VSYNC_BACK_PORCH_SHIFT		0
+#define VSYNC_BACK_PORCH_MASK		(0xFFF << 0)
+#define VSYNC_FRONT_PORCH_SHIFT		16
+#define VSYNC_FRONT_PORCH_MASK		(0xFFF << 16)
+
+#define DPI_BG_HCNTL		0x30
+#define BG_RIGHT			(0x1FFF << 0)
+#define BG_LEFT				(0x1FFF << 16)
+
+#define DPI_BG_VCNTL		0x34
+#define BG_BOT				(0x1FFF << 0)
+#define BG_TOP				(0x1FFF << 16)
+
+#define DPI_BG_COLOR		0x38
+#define BG_B				(0xF << 0)
+#define BG_G				(0xF << 8)
+#define BG_R				(0xF << 16)
+
+#define DPI_FIFO_CTL		0x3C
+#define FIFO_VALID_SET			(0x1F << 0)
+#define FIFO_RST_SEL			(0x1 << 8)
+
+#define DPI_STATUS		0x40
+#define VCOUNTER			(0x1FFF << 0)
+#define DPI_BUSY			BIT(16)
+#define OUTEN				BIT(17)
+#define FIELD				BIT(20)
+#define TDLR				BIT(21)
+
+#define DPI_TMODE		0x44
+#define DPI_OEN_ON			BIT(0)
+
+#define DPI_CHECKSUM		0x48
+#define DPI_CHECKSUM_MASK		(0xFFFFFF << 0)
+#define DPI_CHECKSUM_READY		BIT(30)
+#define DPI_CHECKSUM_EN			BIT(31)
+
+#define DPI_DUMMY		0x50
+#define DPI_DUMMY_MASK			(0xFFFFFFFF << 0)
+
+#define DPI_TGEN_VWIDTH_LEVEN	0x68
+#define DPI_TGEN_VPORCH_LEVEN	0x6C
+#define DPI_TGEN_VWIDTH_RODD	0x70
+#define DPI_TGEN_VPORCH_RODD	0x74
+#define DPI_TGEN_VWIDTH_REVEN	0x78
+#define DPI_TGEN_VPORCH_REVEN	0x7C
+
+#define DPI_ESAV_VTIMING_LODD	0x80
+#define ESAV_VOFST_LODD			(0xFFF << 0)
+#define ESAV_VWID_LODD			(0xFFF << 16)
+
+#define DPI_ESAV_VTIMING_LEVEN	0x84
+#define ESAV_VOFST_LEVEN		(0xFFF << 0)
+#define ESAV_VWID_LEVEN			(0xFFF << 16)
+
+#define DPI_ESAV_VTIMING_RODD	0x88
+#define ESAV_VOFST_RODD			(0xFFF << 0)
+#define ESAV_VWID_RODD			(0xFFF << 16)
+
+#define DPI_ESAV_VTIMING_REVEN	0x8C
+#define ESAV_VOFST_REVEN		(0xFFF << 0)
+#define ESAV_VWID_REVEN			(0xFFF << 16)
+
+#define DPI_ESAV_FTIMING	0x90
+#define ESAV_FOFST_ODD			(0xFFF << 0)
+#define ESAV_FOFST_EVEN			(0xFFF << 16)
+
+#define DPI_CLPF_SETTING	0x94
+#define CLPF_TYPE			(0x3 << 0)
+#define ROUND_EN			BIT(4)
+
+#define DPI_Y_LIMIT		0x98
+#define Y_LIMINT_BOT			0
+#define Y_LIMINT_BOT_MASK		(0xFFF << 0)
+#define Y_LIMINT_TOP			16
+#define Y_LIMINT_TOP_MASK		(0xFFF << 16)
+
+#define DPI_C_LIMIT		0x9C
+#define C_LIMIT_BOT			0
+#define C_LIMIT_BOT_MASK		(0xFFF << 0)
+#define C_LIMIT_TOP			16
+#define C_LIMIT_TOP_MASK		(0xFFF << 16)
+
+#define DPI_YUV422_SETTING	0xA0
+#define UV_SWAP				BIT(0)
+#define CR_DELSEL			BIT(4)
+#define CB_DELSEL			BIT(5)
+#define Y_DELSEL			BIT(6)
+#define DE_DELSEL			BIT(7)
+
+#define DPI_EMBSYNC_SETTING	0xA4
+#define EMBSYNC_R_CR_EN			BIT(0)
+#define EMPSYNC_G_Y_EN			BIT(1)
+#define EMPSYNC_B_CB_EN			BIT(2)
+#define ESAV_F_INV			BIT(4)
+#define ESAV_V_INV			BIT(5)
+#define ESAV_H_INV			BIT(6)
+#define ESAV_CODE_MAN			BIT(8)
+#define VS_OUT_SEL			(0x7 << 12)
+
+#define DPI_ESAV_CODE_SET0	0xA8
+#define ESAV_CODE0			(0xFFF << 0)
+#define ESAV_CODE1			(0xFFF << 16)
+
+#define DPI_ESAV_CODE_SET1	0xAC
+#define ESAV_CODE2			(0xFFF << 0)
+#define ESAV_CODE3_MSB			BIT(16)
+
+#define DPI_H_FRE_CON		0xE0
+#define H_FRE_2N			BIT(25)
+#endif /* __MTK_DPI_REGS_H */
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
index 08782d7..2774cbd 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@ -516,6 +516,7 @@ static struct platform_driver * const mtk_drm_drivers[] = {
 	&mtk_ddp_driver,
 	&mtk_disp_ovl_driver,
 	&mtk_disp_rdma_driver,
+	&mtk_dpi_driver,
 	&mtk_drm_platform_driver,
 	&mtk_dsi_driver,
 	&mtk_mipi_tx_driver,
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.h b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
index ac6c92b..aa93894 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
@@ -53,6 +53,7 @@ struct mtk_drm_private {
 extern struct platform_driver mtk_ddp_driver;
 extern struct platform_driver mtk_disp_ovl_driver;
 extern struct platform_driver mtk_disp_rdma_driver;
+extern struct platform_driver mtk_dpi_driver;
 extern struct platform_driver mtk_dsi_driver;
 extern struct platform_driver mtk_mipi_tx_driver;
 
-- 
2.8.0.rc3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCH v16 6/8] clk: mediatek: make dpi0_sel propagate rate changes
  2016-05-06 13:05 [PATCH v16 0/8] MT8173 DRM support Philipp Zabel
                   ` (4 preceding siblings ...)
  2016-05-06 13:05 ` [PATCH v16 5/8] drm/mediatek: Add DPI " Philipp Zabel
@ 2016-05-06 13:05 ` Philipp Zabel
  2016-05-06 13:05 ` [PATCH v16 7/8] clk: mediatek: Add hdmi_ref HDMI PHY PLL reference clock output Philipp Zabel
  2016-05-06 13:05 ` [PATCH v16 8/8] clk: mediatek: remove hdmitx_dig_cts from TOP clocks Philipp Zabel
  7 siblings, 0 replies; 11+ messages in thread
From: Philipp Zabel @ 2016-05-06 13:05 UTC (permalink / raw)
  To: dri-devel
  Cc: Mark Rutland, Stephen Boyd, Michael Turquette, Jie Qiu,
	Cawa Cheng, YT Shen, Yingjoe Chen, devicetree, Jitao Shi, kernel,
	Pawel Moll, Ian Campbell, Rob Herring, linux-mediatek,
	Matthias Brugger, Paul Bolle, Emil Velikov, Tomasz Figa,
	Kumar Gala

This mux is supposed to select a fitting divider after the PLL
is already set to the correct rate.

Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
Acked-by: James Liao <jamesjj.liao@mediatek.com>
Acked-by: Stephen Boyd <sboyd@codeaurora.org>
---
 drivers/clk/mediatek/clk-mt8173.c |  6 +++++-
 drivers/clk/mediatek/clk-mtk.h    | 15 +++++++++++++--
 2 files changed, 18 insertions(+), 3 deletions(-)

diff --git a/drivers/clk/mediatek/clk-mt8173.c b/drivers/clk/mediatek/clk-mt8173.c
index 227e356..85c0bfc 100644
--- a/drivers/clk/mediatek/clk-mt8173.c
+++ b/drivers/clk/mediatek/clk-mt8173.c
@@ -558,7 +558,11 @@ static const struct mtk_composite top_muxes[] __initconst = {
 	MUX_GATE(CLK_TOP_ATB_SEL, "atb_sel", atb_parents, 0x0090, 16, 2, 23),
 	MUX_GATE(CLK_TOP_VENC_LT_SEL, "venclt_sel", venc_lt_parents, 0x0090, 24, 4, 31),
 	/* CLK_CFG_6 */
-	MUX_GATE(CLK_TOP_DPI0_SEL, "dpi0_sel", dpi0_parents, 0x00a0, 0, 3, 7),
+	/*
+	 * The dpi0_sel clock should not propagate rate changes to its parent
+	 * clock so the dpi driver can have full control over PLL and divider.
+	 */
+	MUX_GATE_FLAGS(CLK_TOP_DPI0_SEL, "dpi0_sel", dpi0_parents, 0x00a0, 0, 3, 7, 0),
 	MUX_GATE(CLK_TOP_IRDA_SEL, "irda_sel", irda_parents, 0x00a0, 8, 2, 15),
 	MUX_GATE(CLK_TOP_CCI400_SEL, "cci400_sel", cci400_parents, 0x00a0, 16, 3, 23),
 	MUX_GATE(CLK_TOP_AUD_1_SEL, "aud_1_sel", aud_1_parents, 0x00a0, 24, 2, 31),
diff --git a/drivers/clk/mediatek/clk-mtk.h b/drivers/clk/mediatek/clk-mtk.h
index 32d2e45..9f24fcf 100644
--- a/drivers/clk/mediatek/clk-mtk.h
+++ b/drivers/clk/mediatek/clk-mtk.h
@@ -83,7 +83,11 @@ struct mtk_composite {
 	signed char num_parents;
 };
 
-#define MUX_GATE(_id, _name, _parents, _reg, _shift, _width, _gate) {	\
+/*
+ * In case the rate change propagation to parent clocks is undesirable,
+ * this macro allows to specify the clock flags manually.
+ */
+#define MUX_GATE_FLAGS(_id, _name, _parents, _reg, _shift, _width, _gate, _flags) {	\
 		.id = _id,						\
 		.name = _name,						\
 		.mux_reg = _reg,					\
@@ -94,9 +98,16 @@ struct mtk_composite {
 		.divider_shift = -1,					\
 		.parent_names = _parents,				\
 		.num_parents = ARRAY_SIZE(_parents),			\
-		.flags = CLK_SET_RATE_PARENT,				\
+		.flags = _flags,					\
 	}
 
+/*
+ * Unless necessary, all MUX_GATE clocks propagate rate changes to their
+ * parent clock by default.
+ */
+#define MUX_GATE(_id, _name, _parents, _reg, _shift, _width, _gate)	\
+	MUX_GATE_FLAGS(_id, _name, _parents, _reg, _shift, _width, _gate, CLK_SET_RATE_PARENT)
+
 #define MUX(_id, _name, _parents, _reg, _shift, _width) {		\
 		.id = _id,						\
 		.name = _name,						\
-- 
2.8.0.rc3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCH v16 7/8] clk: mediatek: Add hdmi_ref HDMI PHY PLL reference clock output
  2016-05-06 13:05 [PATCH v16 0/8] MT8173 DRM support Philipp Zabel
                   ` (5 preceding siblings ...)
  2016-05-06 13:05 ` [PATCH v16 6/8] clk: mediatek: make dpi0_sel propagate rate changes Philipp Zabel
@ 2016-05-06 13:05 ` Philipp Zabel
  2016-05-06 13:05 ` [PATCH v16 8/8] clk: mediatek: remove hdmitx_dig_cts from TOP clocks Philipp Zabel
  7 siblings, 0 replies; 11+ messages in thread
From: Philipp Zabel @ 2016-05-06 13:05 UTC (permalink / raw)
  To: dri-devel
  Cc: Mark Rutland, Stephen Boyd, Michael Turquette, Jie Qiu,
	Cawa Cheng, YT Shen, Yingjoe Chen, devicetree, Jitao Shi, kernel,
	Pawel Moll, Ian Campbell, Rob Herring, linux-mediatek,
	Matthias Brugger, Paul Bolle, Emil Velikov, Tomasz Figa,
	Kumar Gala

The configurable hdmi_ref output of the PLL block is derived from
the tvdpll_594m clock signal via a configurable PLL post-divider.
It is used as the PLL reference input to the HDMI PHY module.

Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
Acked-by: James Liao <jamesjj.liao@mediatek.com>
Acked-by: Stephen Boyd <sboyd@codeaurora.org>
---
 drivers/clk/mediatek/clk-mt8173.c      | 5 +++++
 include/dt-bindings/clock/mt8173-clk.h | 3 ++-
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/drivers/clk/mediatek/clk-mt8173.c b/drivers/clk/mediatek/clk-mt8173.c
index 85c0bfc..cf4fcb6 100644
--- a/drivers/clk/mediatek/clk-mt8173.c
+++ b/drivers/clk/mediatek/clk-mt8173.c
@@ -1095,6 +1095,11 @@ static void __init mtk_apmixedsys_init(struct device_node *node)
 		clk_data->clks[cku->id] = clk;
 	}
 
+	clk = clk_register_divider(NULL, "hdmi_ref", "tvdpll_594m", 0,
+				   base + 0x40, 16, 3, CLK_DIVIDER_POWER_OF_TWO,
+				   NULL);
+	clk_data->clks[CLK_APMIXED_HDMI_REF] = clk;
+
 	r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
 	if (r)
 		pr_err("%s(): could not register clock provider: %d\n",
diff --git a/include/dt-bindings/clock/mt8173-clk.h b/include/dt-bindings/clock/mt8173-clk.h
index 7956ba1..6094bf7 100644
--- a/include/dt-bindings/clock/mt8173-clk.h
+++ b/include/dt-bindings/clock/mt8173-clk.h
@@ -176,7 +176,8 @@
 #define CLK_APMIXED_LVDSPLL		13
 #define CLK_APMIXED_MSDCPLL2		14
 #define CLK_APMIXED_REF2USB_TX		15
-#define CLK_APMIXED_NR_CLK		16
+#define CLK_APMIXED_HDMI_REF		16
+#define CLK_APMIXED_NR_CLK		17
 
 /* INFRA_SYS */
 
-- 
2.8.0.rc3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCH v16 8/8] clk: mediatek: remove hdmitx_dig_cts from TOP clocks
  2016-05-06 13:05 [PATCH v16 0/8] MT8173 DRM support Philipp Zabel
                   ` (6 preceding siblings ...)
  2016-05-06 13:05 ` [PATCH v16 7/8] clk: mediatek: Add hdmi_ref HDMI PHY PLL reference clock output Philipp Zabel
@ 2016-05-06 13:05 ` Philipp Zabel
  7 siblings, 0 replies; 11+ messages in thread
From: Philipp Zabel @ 2016-05-06 13:05 UTC (permalink / raw)
  To: dri-devel
  Cc: Mark Rutland, Stephen Boyd, Michael Turquette, Jie Qiu,
	Cawa Cheng, YT Shen, Yingjoe Chen, devicetree, Jitao Shi, kernel,
	Pawel Moll, Ian Campbell, Rob Herring, linux-mediatek,
	Matthias Brugger, Paul Bolle, Emil Velikov, Tomasz Figa,
	Kumar Gala

The hdmitx_dig_cts clock signal is not a child of tvdpll_445p5m,
but is routed out of the HDMI PHY module.

Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
Acked-by: Stephen Boyd <sboyd@codeaurora.org>
---
 drivers/clk/mediatek/clk-mt8173.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/clk/mediatek/clk-mt8173.c b/drivers/clk/mediatek/clk-mt8173.c
index cf4fcb6..10c9860 100644
--- a/drivers/clk/mediatek/clk-mt8173.c
+++ b/drivers/clk/mediatek/clk-mt8173.c
@@ -61,7 +61,6 @@ static const struct mtk_fixed_factor top_divs[] __initconst = {
 	FACTOR(CLK_TOP_CLKRTC_INT, "clkrtc_int", "clk26m", 1, 793),
 	FACTOR(CLK_TOP_FPC, "fpc_ck", "clk26m", 1, 1),
 
-	FACTOR(CLK_TOP_HDMITX_DIG_CTS, "hdmitx_dig_cts", "tvdpll_445p5m", 1, 3),
 	FACTOR(CLK_TOP_HDMITXPLL_D2, "hdmitxpll_d2", "hdmitx_dig_cts", 1, 2),
 	FACTOR(CLK_TOP_HDMITXPLL_D3, "hdmitxpll_d3", "hdmitx_dig_cts", 1, 3),
 
-- 
2.8.0.rc3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH v16 3/8] drm/mediatek: Add DRM Driver for Mediatek SoC MT8173.
  2016-05-06 13:05 ` [PATCH v16 3/8] drm/mediatek: Add DRM Driver for Mediatek SoC MT8173 Philipp Zabel
@ 2016-05-06 13:16   ` Daniel Vetter
  2016-05-06 14:40     ` Philipp Zabel
  0 siblings, 1 reply; 11+ messages in thread
From: Daniel Vetter @ 2016-05-06 13:16 UTC (permalink / raw)
  To: Philipp Zabel
  Cc: Mark Rutland, Stephen Boyd, Michael Turquette, dri-devel,
	Jie Qiu, Cawa Cheng, Mao Huang, YT Shen, Yingjoe Chen,
	devicetree, Jitao Shi, kernel, Pawel Moll, Ian Campbell,
	Rob Herring, linux-mediatek, Matthias Brugger, Paul Bolle,
	Emil Velikov, Tomasz Figa, Kumar Gala

On Fri, May 06, 2016 at 03:05:49PM +0200, Philipp Zabel wrote:
> From: CK Hu <ck.hu@mediatek.com>
> 
> This patch adds an initial DRM driver for the Mediatek MT8173 DISP
> subsystem. It currently supports two fixed output streams from the
> OVL0/OVL1 sources to the DSI0/DPI0 sinks, respectively.
> 
> Signed-off-by: CK Hu <ck.hu@mediatek.com>
> Signed-off-by: YT Shen <yt.shen@mediatek.com>
> Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
> Signed-off-by: Bibby Hsieh <bibby.hsieh@mediatek.com>
> Signed-off-by: Mao Huang <littlecvr@chromium.org>
> Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>

Spotted two tiny things below.

> ---
> Changes since v15:
>  - Rebased onto drm-next
>  - Use drm_crtc_send_vblank_event() instead of drm_send_vblank_event()
>  - Order mtk_drm_drivers alphabetically
> ---
>  drivers/gpu/drm/Kconfig                     |   2 +
>  drivers/gpu/drm/Makefile                    |   1 +
>  drivers/gpu/drm/mediatek/Kconfig            |  14 +
>  drivers/gpu/drm/mediatek/Makefile           |  11 +
>  drivers/gpu/drm/mediatek/mtk_disp_ovl.c     | 302 +++++++++++++++
>  drivers/gpu/drm/mediatek/mtk_disp_rdma.c    | 240 ++++++++++++
>  drivers/gpu/drm/mediatek/mtk_drm_crtc.c     | 582 ++++++++++++++++++++++++++++
>  drivers/gpu/drm/mediatek/mtk_drm_crtc.h     |  32 ++
>  drivers/gpu/drm/mediatek/mtk_drm_ddp.c      | 353 +++++++++++++++++
>  drivers/gpu/drm/mediatek/mtk_drm_ddp.h      |  41 ++
>  drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c | 225 +++++++++++
>  drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h | 150 +++++++
>  drivers/gpu/drm/mediatek/mtk_drm_drv.c      | 558 ++++++++++++++++++++++++++
>  drivers/gpu/drm/mediatek/mtk_drm_drv.h      |  57 +++
>  drivers/gpu/drm/mediatek/mtk_drm_fb.c       | 165 ++++++++
>  drivers/gpu/drm/mediatek/mtk_drm_fb.h       |  23 ++
>  drivers/gpu/drm/mediatek/mtk_drm_gem.c      | 269 +++++++++++++
>  drivers/gpu/drm/mediatek/mtk_drm_gem.h      |  59 +++
>  drivers/gpu/drm/mediatek/mtk_drm_plane.c    | 240 ++++++++++++
>  drivers/gpu/drm/mediatek/mtk_drm_plane.h    |  59 +++
>  20 files changed, 3383 insertions(+)
>  create mode 100644 drivers/gpu/drm/mediatek/Kconfig
>  create mode 100644 drivers/gpu/drm/mediatek/Makefile
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_disp_ovl.c
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_disp_rdma.c
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_crtc.c
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_crtc.h
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_ddp.c
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_ddp.h
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_drv.c
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_drv.h
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_fb.c
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_fb.h
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_gem.c
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_gem.h
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_plane.c
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_plane.h
> 
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index 16e4c21..fc35731 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -288,3 +288,5 @@ source "drivers/gpu/drm/etnaviv/Kconfig"
>  source "drivers/gpu/drm/arc/Kconfig"
>  
>  source "drivers/gpu/drm/hisilicon/Kconfig"
> +
> +source "drivers/gpu/drm/mediatek/Kconfig"
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index 43c2abf..2bd3e5a 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -74,6 +74,7 @@ obj-$(CONFIG_DRM_MSM) += msm/
>  obj-$(CONFIG_DRM_TEGRA) += tegra/
>  obj-$(CONFIG_DRM_STI) += sti/
>  obj-$(CONFIG_DRM_IMX) += imx/
> +obj-$(CONFIG_DRM_MEDIATEK) += mediatek/
>  obj-y			+= i2c/
>  obj-y			+= panel/
>  obj-y			+= bridge/
> diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
> new file mode 100644
> index 0000000..30e5371
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/Kconfig
> @@ -0,0 +1,14 @@
> +config DRM_MEDIATEK
> +	tristate "DRM Support for Mediatek SoCs"
> +	depends on DRM
> +	depends on ARCH_MEDIATEK || (ARM && COMPILE_TEST)
> +	select DRM_GEM_CMA_HELPER
> +	select DRM_KMS_HELPER
> +	select IOMMU_DMA
> +	select MEMORY
> +	select MTK_SMI
> +	help
> +	  Choose this option if you have a Mediatek SoCs.
> +	  The module will be called mediatek-drm
> +	  This driver provides kernel mode setting and
> +	  buffer management to userspace.
> diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
> new file mode 100644
> index 0000000..d4bde7c
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/Makefile
> @@ -0,0 +1,11 @@
> +mediatek-drm-y := mtk_disp_ovl.o \
> +		  mtk_disp_rdma.o \
> +		  mtk_drm_crtc.o \
> +		  mtk_drm_ddp.o \
> +		  mtk_drm_ddp_comp.o \
> +		  mtk_drm_drv.o \
> +		  mtk_drm_fb.o \
> +		  mtk_drm_gem.o \
> +		  mtk_drm_plane.o
> +
> +obj-$(CONFIG_DRM_MEDIATEK) += mediatek-drm.o
> diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
> new file mode 100644
> index 0000000..8f62671f
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
> @@ -0,0 +1,302 @@
> +/*
> + * Copyright (c) 2015 MediaTek Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <drm/drmP.h>
> +#include <linux/clk.h>
> +#include <linux/component.h>
> +#include <linux/of_device.h>
> +#include <linux/of_irq.h>
> +#include <linux/platform_device.h>
> +
> +#include "mtk_drm_crtc.h"
> +#include "mtk_drm_ddp_comp.h"
> +
> +#define DISP_REG_OVL_INTEN			0x0004
> +#define OVL_FME_CPL_INT					BIT(1)
> +#define DISP_REG_OVL_INTSTA			0x0008
> +#define DISP_REG_OVL_EN				0x000c
> +#define DISP_REG_OVL_RST			0x0014
> +#define DISP_REG_OVL_ROI_SIZE			0x0020
> +#define DISP_REG_OVL_ROI_BGCLR			0x0028
> +#define DISP_REG_OVL_SRC_CON			0x002c
> +#define DISP_REG_OVL_CON(n)			(0x0030 + 0x20 * (n))
> +#define DISP_REG_OVL_SRC_SIZE(n)		(0x0038 + 0x20 * (n))
> +#define DISP_REG_OVL_OFFSET(n)			(0x003c + 0x20 * (n))
> +#define DISP_REG_OVL_PITCH(n)			(0x0044 + 0x20 * (n))
> +#define DISP_REG_OVL_RDMA_CTRL(n)		(0x00c0 + 0x20 * (n))
> +#define DISP_REG_OVL_RDMA_GMC(n)		(0x00c8 + 0x20 * (n))
> +#define DISP_REG_OVL_ADDR(n)			(0x0f40 + 0x20 * (n))
> +
> +#define	OVL_RDMA_MEM_GMC	0x40402020
> +
> +#define OVL_CON_BYTE_SWAP	BIT(24)
> +#define OVL_CON_CLRFMT_RGB565	(0 << 12)
> +#define OVL_CON_CLRFMT_RGB888	(1 << 12)
> +#define OVL_CON_CLRFMT_RGBA8888	(2 << 12)
> +#define OVL_CON_CLRFMT_ARGB8888	(3 << 12)
> +#define	OVL_CON_AEN		BIT(8)
> +#define	OVL_CON_ALPHA		0xff
> +
> +/**
> + * struct mtk_disp_ovl - DISP_OVL driver structure
> + * @ddp_comp - structure containing type enum and hardware resources
> + * @crtc - associated crtc to report vblank events to
> + */
> +struct mtk_disp_ovl {
> +	struct mtk_ddp_comp		ddp_comp;
> +	struct drm_crtc			*crtc;
> +};
> +
> +static irqreturn_t mtk_disp_ovl_irq_handler(int irq, void *dev_id)
> +{
> +	struct mtk_disp_ovl *priv = dev_id;
> +	struct mtk_ddp_comp *ovl = &priv->ddp_comp;
> +
> +	/* Clear frame completion interrupt */
> +	writel(0x0, ovl->regs + DISP_REG_OVL_INTSTA);
> +
> +	if (!priv->crtc)
> +		return IRQ_NONE;
> +
> +	mtk_crtc_ddp_irq(priv->crtc, ovl);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static void mtk_ovl_enable_vblank(struct mtk_ddp_comp *comp,
> +				  struct drm_crtc *crtc)
> +{
> +	struct mtk_disp_ovl *priv = container_of(comp, struct mtk_disp_ovl,
> +						 ddp_comp);
> +
> +	priv->crtc = crtc;
> +	writel_relaxed(OVL_FME_CPL_INT, comp->regs + DISP_REG_OVL_INTEN);
> +}
> +
> +static void mtk_ovl_disable_vblank(struct mtk_ddp_comp *comp)
> +{
> +	struct mtk_disp_ovl *priv = container_of(comp, struct mtk_disp_ovl,
> +						 ddp_comp);
> +
> +	priv->crtc = NULL;
> +	writel_relaxed(0x0, comp->regs + DISP_REG_OVL_INTEN);
> +}
> +
> +static void mtk_ovl_start(struct mtk_ddp_comp *comp)
> +{
> +	writel_relaxed(0x1, comp->regs + DISP_REG_OVL_EN);
> +}
> +
> +static void mtk_ovl_stop(struct mtk_ddp_comp *comp)
> +{
> +	writel_relaxed(0x0, comp->regs + DISP_REG_OVL_EN);
> +}
> +
> +static void mtk_ovl_config(struct mtk_ddp_comp *comp, unsigned int w,
> +			   unsigned int h, unsigned int vrefresh)
> +{
> +	if (w != 0 && h != 0)
> +		writel_relaxed(h << 16 | w, comp->regs + DISP_REG_OVL_ROI_SIZE);
> +	writel_relaxed(0x0, comp->regs + DISP_REG_OVL_ROI_BGCLR);
> +
> +	writel(0x1, comp->regs + DISP_REG_OVL_RST);
> +	writel(0x0, comp->regs + DISP_REG_OVL_RST);
> +}
> +
> +static void mtk_ovl_layer_on(struct mtk_ddp_comp *comp, unsigned int idx)
> +{
> +	unsigned int reg;
> +
> +	writel(0x1, comp->regs + DISP_REG_OVL_RDMA_CTRL(idx));
> +	writel(OVL_RDMA_MEM_GMC, comp->regs + DISP_REG_OVL_RDMA_GMC(idx));
> +
> +	reg = readl(comp->regs + DISP_REG_OVL_SRC_CON);
> +	reg = reg | BIT(idx);
> +	writel(reg, comp->regs + DISP_REG_OVL_SRC_CON);
> +}
> +
> +static void mtk_ovl_layer_off(struct mtk_ddp_comp *comp, unsigned int idx)
> +{
> +	unsigned int reg;
> +
> +	reg = readl(comp->regs + DISP_REG_OVL_SRC_CON);
> +	reg = reg & ~BIT(idx);
> +	writel(reg, comp->regs + DISP_REG_OVL_SRC_CON);
> +
> +	writel(0x0, comp->regs + DISP_REG_OVL_RDMA_CTRL(idx));
> +}
> +
> +static unsigned int ovl_fmt_convert(unsigned int fmt)
> +{
> +	switch (fmt) {
> +	default:
> +	case DRM_FORMAT_RGB565:
> +		return OVL_CON_CLRFMT_RGB565;
> +	case DRM_FORMAT_BGR565:
> +		return OVL_CON_CLRFMT_RGB565 | OVL_CON_BYTE_SWAP;
> +	case DRM_FORMAT_RGB888:
> +		return OVL_CON_CLRFMT_RGB888;
> +	case DRM_FORMAT_BGR888:
> +		return OVL_CON_CLRFMT_RGB888 | OVL_CON_BYTE_SWAP;
> +	case DRM_FORMAT_RGBX8888:
> +	case DRM_FORMAT_RGBA8888:
> +		return OVL_CON_CLRFMT_ARGB8888;
> +	case DRM_FORMAT_BGRX8888:
> +	case DRM_FORMAT_BGRA8888:
> +		return OVL_CON_CLRFMT_ARGB8888 | OVL_CON_BYTE_SWAP;
> +	case DRM_FORMAT_XRGB8888:
> +	case DRM_FORMAT_ARGB8888:
> +		return OVL_CON_CLRFMT_RGBA8888;
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_ABGR8888:
> +		return OVL_CON_CLRFMT_RGBA8888 | OVL_CON_BYTE_SWAP;
> +	}
> +}
> +
> +static void mtk_ovl_layer_config(struct mtk_ddp_comp *comp, unsigned int idx,
> +				 struct mtk_plane_state *state)
> +{
> +	struct mtk_plane_pending_state *pending = &state->pending;
> +	unsigned int addr = pending->addr;
> +	unsigned int pitch = pending->pitch & 0xffff;
> +	unsigned int fmt = pending->format;
> +	unsigned int offset = (pending->y << 16) | pending->x;
> +	unsigned int src_size = (pending->height << 16) | pending->width;
> +	unsigned int con;
> +
> +	if (!pending->enable)
> +		mtk_ovl_layer_off(comp, idx);
> +
> +	con = ovl_fmt_convert(fmt);
> +	if (idx != 0)
> +		con |= OVL_CON_AEN | OVL_CON_ALPHA;
> +
> +	writel_relaxed(con, comp->regs + DISP_REG_OVL_CON(idx));
> +	writel_relaxed(pitch, comp->regs + DISP_REG_OVL_PITCH(idx));
> +	writel_relaxed(src_size, comp->regs + DISP_REG_OVL_SRC_SIZE(idx));
> +	writel_relaxed(offset, comp->regs + DISP_REG_OVL_OFFSET(idx));
> +	writel_relaxed(addr, comp->regs + DISP_REG_OVL_ADDR(idx));
> +
> +	if (pending->enable)
> +		mtk_ovl_layer_on(comp, idx);
> +}
> +
> +static const struct mtk_ddp_comp_funcs mtk_disp_ovl_funcs = {
> +	.config = mtk_ovl_config,
> +	.start = mtk_ovl_start,
> +	.stop = mtk_ovl_stop,
> +	.enable_vblank = mtk_ovl_enable_vblank,
> +	.disable_vblank = mtk_ovl_disable_vblank,
> +	.layer_on = mtk_ovl_layer_on,
> +	.layer_off = mtk_ovl_layer_off,
> +	.layer_config = mtk_ovl_layer_config,
> +};
> +
> +static int mtk_disp_ovl_bind(struct device *dev, struct device *master,
> +			     void *data)
> +{
> +	struct mtk_disp_ovl *priv = dev_get_drvdata(dev);
> +	struct drm_device *drm_dev = data;
> +	int ret;
> +
> +	ret = mtk_ddp_comp_register(drm_dev, &priv->ddp_comp);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to register component %s: %d\n",
> +			dev->of_node->full_name, ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static void mtk_disp_ovl_unbind(struct device *dev, struct device *master,
> +				void *data)
> +{
> +	struct mtk_disp_ovl *priv = dev_get_drvdata(dev);
> +	struct drm_device *drm_dev = data;
> +
> +	mtk_ddp_comp_unregister(drm_dev, &priv->ddp_comp);
> +}
> +
> +static const struct component_ops mtk_disp_ovl_component_ops = {
> +	.bind	= mtk_disp_ovl_bind,
> +	.unbind = mtk_disp_ovl_unbind,
> +};
> +
> +static int mtk_disp_ovl_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct mtk_disp_ovl *priv;
> +	int comp_id;
> +	int irq;
> +	int ret;
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0)
> +		return irq;
> +
> +	ret = devm_request_irq(dev, irq, mtk_disp_ovl_irq_handler,
> +			       IRQF_TRIGGER_NONE, dev_name(dev), priv);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to request irq %d: %d\n", irq, ret);
> +		return ret;
> +	}
> +
> +	comp_id = mtk_ddp_comp_get_id(dev->of_node, MTK_DISP_OVL);
> +	if (comp_id < 0) {
> +		dev_err(dev, "Failed to identify by alias: %d\n", comp_id);
> +		return comp_id;
> +	}
> +
> +	ret = mtk_ddp_comp_init(dev, dev->of_node, &priv->ddp_comp, comp_id,
> +				&mtk_disp_ovl_funcs);
> +	if (ret) {
> +		dev_err(dev, "Failed to initialize component: %d\n", ret);
> +		return ret;
> +	}
> +
> +	platform_set_drvdata(pdev, priv);
> +
> +	ret = component_add(dev, &mtk_disp_ovl_component_ops);
> +	if (ret)
> +		dev_err(dev, "Failed to add component: %d\n", ret);
> +
> +	return ret;
> +}
> +
> +static int mtk_disp_ovl_remove(struct platform_device *pdev)
> +{
> +	component_del(&pdev->dev, &mtk_disp_ovl_component_ops);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id mtk_disp_ovl_driver_dt_match[] = {
> +	{ .compatible = "mediatek,mt8173-disp-ovl", },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, mtk_disp_ovl_driver_dt_match);
> +
> +struct platform_driver mtk_disp_ovl_driver = {
> +	.probe		= mtk_disp_ovl_probe,
> +	.remove		= mtk_disp_ovl_remove,
> +	.driver		= {
> +		.name	= "mediatek-disp-ovl",
> +		.owner	= THIS_MODULE,
> +		.of_match_table = mtk_disp_ovl_driver_dt_match,
> +	},
> +};
> diff --git a/drivers/gpu/drm/mediatek/mtk_disp_rdma.c b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c
> new file mode 100644
> index 0000000..5fb80cb
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c
> @@ -0,0 +1,240 @@
> +/*
> + * Copyright (c) 2015 MediaTek Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <drm/drmP.h>
> +#include <linux/clk.h>
> +#include <linux/component.h>
> +#include <linux/of_device.h>
> +#include <linux/of_irq.h>
> +#include <linux/platform_device.h>
> +
> +#include "mtk_drm_crtc.h"
> +#include "mtk_drm_ddp_comp.h"
> +
> +#define DISP_REG_RDMA_INT_ENABLE		0x0000
> +#define DISP_REG_RDMA_INT_STATUS		0x0004
> +#define RDMA_TARGET_LINE_INT				BIT(5)
> +#define RDMA_FIFO_UNDERFLOW_INT				BIT(4)
> +#define RDMA_EOF_ABNORMAL_INT				BIT(3)
> +#define RDMA_FRAME_END_INT				BIT(2)
> +#define RDMA_FRAME_START_INT				BIT(1)
> +#define RDMA_REG_UPDATE_INT				BIT(0)
> +#define DISP_REG_RDMA_GLOBAL_CON		0x0010
> +#define RDMA_ENGINE_EN					BIT(0)
> +#define DISP_REG_RDMA_SIZE_CON_0		0x0014
> +#define DISP_REG_RDMA_SIZE_CON_1		0x0018
> +#define DISP_REG_RDMA_TARGET_LINE		0x001c
> +#define DISP_REG_RDMA_FIFO_CON			0x0040
> +#define RDMA_FIFO_UNDERFLOW_EN				BIT(31)
> +#define RDMA_FIFO_PSEUDO_SIZE(bytes)			(((bytes) / 16) << 16)
> +#define RDMA_OUTPUT_VALID_FIFO_THRESHOLD(bytes)		((bytes) / 16)
> +
> +/**
> + * struct mtk_disp_rdma - DISP_RDMA driver structure
> + * @ddp_comp - structure containing type enum and hardware resources
> + * @crtc - associated crtc to report irq events to
> + */
> +struct mtk_disp_rdma {
> +	struct mtk_ddp_comp		ddp_comp;
> +	struct drm_crtc			*crtc;
> +};
> +
> +static irqreturn_t mtk_disp_rdma_irq_handler(int irq, void *dev_id)
> +{
> +	struct mtk_disp_rdma *priv = dev_id;
> +	struct mtk_ddp_comp *rdma = &priv->ddp_comp;
> +
> +	/* Clear frame completion interrupt */
> +	writel(0x0, rdma->regs + DISP_REG_RDMA_INT_STATUS);
> +
> +	if (!priv->crtc)
> +		return IRQ_NONE;
> +
> +	mtk_crtc_ddp_irq(priv->crtc, rdma);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static void rdma_update_bits(struct mtk_ddp_comp *comp, unsigned int reg,
> +			     unsigned int mask, unsigned int val)
> +{
> +	unsigned int tmp = readl(comp->regs + reg);
> +
> +	tmp = (tmp & ~mask) | (val & mask);
> +	writel(tmp, comp->regs + reg);
> +}
> +
> +static void mtk_rdma_enable_vblank(struct mtk_ddp_comp *comp,
> +				   struct drm_crtc *crtc)
> +{
> +	struct mtk_disp_rdma *priv = container_of(comp, struct mtk_disp_rdma,
> +						  ddp_comp);
> +
> +	priv->crtc = crtc;
> +	rdma_update_bits(comp, DISP_REG_RDMA_INT_ENABLE, RDMA_FRAME_END_INT,
> +			 RDMA_FRAME_END_INT);
> +}
> +
> +static void mtk_rdma_disable_vblank(struct mtk_ddp_comp *comp)
> +{
> +	struct mtk_disp_rdma *priv = container_of(comp, struct mtk_disp_rdma,
> +						  ddp_comp);
> +
> +	priv->crtc = NULL;
> +	rdma_update_bits(comp, DISP_REG_RDMA_INT_ENABLE, RDMA_FRAME_END_INT, 0);
> +}
> +
> +static void mtk_rdma_start(struct mtk_ddp_comp *comp)
> +{
> +	rdma_update_bits(comp, DISP_REG_RDMA_GLOBAL_CON, RDMA_ENGINE_EN,
> +			 RDMA_ENGINE_EN);
> +}
> +
> +static void mtk_rdma_stop(struct mtk_ddp_comp *comp)
> +{
> +	rdma_update_bits(comp, DISP_REG_RDMA_GLOBAL_CON, RDMA_ENGINE_EN, 0);
> +}
> +
> +static void mtk_rdma_config(struct mtk_ddp_comp *comp, unsigned int width,
> +			    unsigned int height, unsigned int vrefresh)
> +{
> +	unsigned int threshold;
> +	unsigned int reg;
> +
> +	rdma_update_bits(comp, DISP_REG_RDMA_SIZE_CON_0, 0xfff, width);
> +	rdma_update_bits(comp, DISP_REG_RDMA_SIZE_CON_1, 0xfffff, height);
> +
> +	/*
> +	 * Enable FIFO underflow since DSI and DPI can't be blocked.
> +	 * Keep the FIFO pseudo size reset default of 8 KiB. Set the
> +	 * output threshold to 6 microseconds with 7/6 overhead to
> +	 * account for blanking, and with a pixel depth of 4 bytes:
> +	 */
> +	threshold = width * height * vrefresh * 4 * 7 / 1000000;
> +	reg = RDMA_FIFO_UNDERFLOW_EN |
> +	      RDMA_FIFO_PSEUDO_SIZE(SZ_8K) |
> +	      RDMA_OUTPUT_VALID_FIFO_THRESHOLD(threshold);
> +	writel(reg, comp->regs + DISP_REG_RDMA_FIFO_CON);
> +}
> +
> +static const struct mtk_ddp_comp_funcs mtk_disp_rdma_funcs = {
> +	.config = mtk_rdma_config,
> +	.start = mtk_rdma_start,
> +	.stop = mtk_rdma_stop,
> +	.enable_vblank = mtk_rdma_enable_vblank,
> +	.disable_vblank = mtk_rdma_disable_vblank,
> +};
> +
> +static int mtk_disp_rdma_bind(struct device *dev, struct device *master,
> +			      void *data)
> +{
> +	struct mtk_disp_rdma *priv = dev_get_drvdata(dev);
> +	struct drm_device *drm_dev = data;
> +	int ret;
> +
> +	ret = mtk_ddp_comp_register(drm_dev, &priv->ddp_comp);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to register component %s: %d\n",
> +			dev->of_node->full_name, ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +
> +}
> +
> +static void mtk_disp_rdma_unbind(struct device *dev, struct device *master,
> +				 void *data)
> +{
> +	struct mtk_disp_rdma *priv = dev_get_drvdata(dev);
> +	struct drm_device *drm_dev = data;
> +
> +	mtk_ddp_comp_unregister(drm_dev, &priv->ddp_comp);
> +}
> +
> +static const struct component_ops mtk_disp_rdma_component_ops = {
> +	.bind	= mtk_disp_rdma_bind,
> +	.unbind = mtk_disp_rdma_unbind,
> +};
> +
> +static int mtk_disp_rdma_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct mtk_disp_rdma *priv;
> +	int comp_id;
> +	int irq;
> +	int ret;
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0)
> +		return irq;
> +
> +	comp_id = mtk_ddp_comp_get_id(dev->of_node, MTK_DISP_RDMA);
> +	if (comp_id < 0) {
> +		dev_err(dev, "Failed to identify by alias: %d\n", comp_id);
> +		return comp_id;
> +	}
> +
> +	ret = mtk_ddp_comp_init(dev, dev->of_node, &priv->ddp_comp, comp_id,
> +				&mtk_disp_rdma_funcs);
> +	if (ret) {
> +		dev_err(dev, "Failed to initialize component: %d\n", ret);
> +		return ret;
> +	}
> +
> +	/* Disable and clear pending interrupts */
> +	writel(0x0, priv->ddp_comp.regs + DISP_REG_RDMA_INT_ENABLE);
> +	writel(0x0, priv->ddp_comp.regs + DISP_REG_RDMA_INT_STATUS);
> +
> +	ret = devm_request_irq(dev, irq, mtk_disp_rdma_irq_handler,
> +			       IRQF_TRIGGER_NONE, dev_name(dev), priv);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to request irq %d: %d\n", irq, ret);
> +		return ret;
> +	}
> +
> +	platform_set_drvdata(pdev, priv);
> +
> +	ret = component_add(dev, &mtk_disp_rdma_component_ops);
> +	if (ret)
> +		dev_err(dev, "Failed to add component: %d\n", ret);
> +
> +	return ret;
> +}
> +
> +static int mtk_disp_rdma_remove(struct platform_device *pdev)
> +{
> +	component_del(&pdev->dev, &mtk_disp_rdma_component_ops);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id mtk_disp_rdma_driver_dt_match[] = {
> +	{ .compatible = "mediatek,mt8173-disp-rdma", },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, mtk_disp_rdma_driver_dt_match);
> +
> +struct platform_driver mtk_disp_rdma_driver = {
> +	.probe		= mtk_disp_rdma_probe,
> +	.remove		= mtk_disp_rdma_remove,
> +	.driver		= {
> +		.name	= "mediatek-disp-rdma",
> +		.owner	= THIS_MODULE,
> +		.of_match_table = mtk_disp_rdma_driver_dt_match,
> +	},
> +};
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
> new file mode 100644
> index 0000000..3095fc1
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
> @@ -0,0 +1,582 @@
> +/*
> + * Copyright (c) 2015 MediaTek Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <asm/barrier.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_plane_helper.h>
> +#include <linux/clk.h>
> +#include <linux/pm_runtime.h>
> +#include <soc/mediatek/smi.h>
> +
> +#include "mtk_drm_drv.h"
> +#include "mtk_drm_crtc.h"
> +#include "mtk_drm_ddp.h"
> +#include "mtk_drm_ddp_comp.h"
> +#include "mtk_drm_gem.h"
> +#include "mtk_drm_plane.h"
> +
> +/**
> + * struct mtk_drm_crtc - MediaTek specific crtc structure.
> + * @base: crtc object.
> + * @enabled: records whether crtc_enable succeeded
> + * @planes: array of 4 mtk_drm_plane structures, one for each overlay plane
> + * @pending_planes: whether any plane has pending changes to be applied
> + * @config_regs: memory mapped mmsys configuration register space
> + * @mutex: handle to one of the ten disp_mutex streams
> + * @ddp_comp_nr: number of components in ddp_comp
> + * @ddp_comp: array of pointers the mtk_ddp_comp structures used by this crtc
> + */
> +struct mtk_drm_crtc {
> +	struct drm_crtc			base;
> +	bool				enabled;
> +
> +	bool				pending_needs_vblank;
> +	struct drm_pending_vblank_event	*event;
> +
> +	struct mtk_drm_plane		planes[OVL_LAYER_NR];
> +	bool				pending_planes;
> +
> +	void __iomem			*config_regs;
> +	struct mtk_disp_mutex		*mutex;
> +	unsigned int			ddp_comp_nr;
> +	struct mtk_ddp_comp		**ddp_comp;
> +};
> +
> +struct mtk_crtc_state {
> +	struct drm_crtc_state		base;
> +
> +	bool				pending_config;
> +	unsigned int			pending_width;
> +	unsigned int			pending_height;
> +	unsigned int			pending_vrefresh;
> +};
> +
> +static inline struct mtk_drm_crtc *to_mtk_crtc(struct drm_crtc *c)
> +{
> +	return container_of(c, struct mtk_drm_crtc, base);
> +}
> +
> +static inline struct mtk_crtc_state *to_mtk_crtc_state(struct drm_crtc_state *s)
> +{
> +	return container_of(s, struct mtk_crtc_state, base);
> +}
> +
> +static void mtk_drm_crtc_finish_page_flip(struct mtk_drm_crtc *mtk_crtc)
> +{
> +	struct drm_crtc *crtc = &mtk_crtc->base;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&crtc->dev->event_lock, flags);
> +	drm_crtc_send_vblank_event(crtc, mtk_crtc->event);
> +	drm_crtc_vblank_put(crtc);
> +	mtk_crtc->event = NULL;
> +	spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
> +}
> +
> +static void mtk_drm_finish_page_flip(struct mtk_drm_crtc *mtk_crtc)
> +{
> +	drm_crtc_handle_vblank(&mtk_crtc->base);
> +	if (mtk_crtc->pending_needs_vblank) {
> +		mtk_drm_crtc_finish_page_flip(mtk_crtc);
> +		mtk_crtc->pending_needs_vblank = false;
> +	}
> +}
> +
> +static void mtk_drm_crtc_destroy(struct drm_crtc *crtc)
> +{
> +	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
> +	int i;
> +
> +	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++)
> +		clk_unprepare(mtk_crtc->ddp_comp[i]->clk);
> +
> +	mtk_disp_mutex_put(mtk_crtc->mutex);
> +
> +	drm_crtc_cleanup(crtc);
> +}
> +
> +static void mtk_drm_crtc_reset(struct drm_crtc *crtc)
> +{
> +	struct mtk_crtc_state *state;
> +
> +	if (crtc->state) {
> +		if (crtc->state->mode_blob)
> +			drm_property_unreference_blob(crtc->state->mode_blob);
> +
> +		state = to_mtk_crtc_state(crtc->state);
> +		memset(state, 0, sizeof(*state));
> +	} else {
> +		state = kzalloc(sizeof(*state), GFP_KERNEL);
> +		if (!state)
> +			return;
> +		crtc->state = &state->base;
> +	}
> +
> +	state->base.crtc = crtc;
> +}
> +
> +static struct drm_crtc_state *mtk_drm_crtc_duplicate_state(struct drm_crtc *crtc)
> +{
> +	struct mtk_crtc_state *state;
> +
> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
> +	if (!state)
> +		return NULL;
> +
> +	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
> +
> +	WARN_ON(state->base.crtc != crtc);
> +	state->base.crtc = crtc;
> +
> +	return &state->base;
> +}
> +
> +static void mtk_drm_crtc_destroy_state(struct drm_crtc *crtc,
> +				       struct drm_crtc_state *state)
> +{
> +	__drm_atomic_helper_crtc_destroy_state(crtc, state);
> +	kfree(to_mtk_crtc_state(state));
> +}
> +
> +static bool mtk_drm_crtc_mode_fixup(struct drm_crtc *crtc,
> +				    const struct drm_display_mode *mode,
> +				    struct drm_display_mode *adjusted_mode)
> +{
> +	/* Nothing to do here, but this callback is mandatory. */
> +	return true;
> +}
> +
> +static void mtk_drm_crtc_mode_set_nofb(struct drm_crtc *crtc)
> +{
> +	struct mtk_crtc_state *state = to_mtk_crtc_state(crtc->state);
> +
> +	state->pending_width = crtc->mode.hdisplay;
> +	state->pending_height = crtc->mode.vdisplay;
> +	state->pending_vrefresh = crtc->mode.vrefresh;
> +	wmb();	/* Make sure the above parameters are set before update */
> +	state->pending_config = true;
> +}
> +
> +int mtk_drm_crtc_enable_vblank(struct drm_device *drm, unsigned int pipe)
> +{
> +	struct mtk_drm_private *priv = drm->dev_private;
> +	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(priv->crtc[pipe]);
> +	struct mtk_ddp_comp *ovl = mtk_crtc->ddp_comp[0];
> +
> +	mtk_ddp_comp_enable_vblank(ovl, &mtk_crtc->base);
> +
> +	return 0;
> +}
> +
> +void mtk_drm_crtc_disable_vblank(struct drm_device *drm, unsigned int pipe)
> +{
> +	struct mtk_drm_private *priv = drm->dev_private;
> +	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(priv->crtc[pipe]);
> +	struct mtk_ddp_comp *ovl = mtk_crtc->ddp_comp[0];
> +
> +	mtk_ddp_comp_disable_vblank(ovl);
> +}
> +
> +static int mtk_crtc_ddp_clk_enable(struct mtk_drm_crtc *mtk_crtc)
> +{
> +	int ret;
> +	int i;
> +
> +	DRM_DEBUG_DRIVER("%s\n", __func__);
> +	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
> +		ret = clk_enable(mtk_crtc->ddp_comp[i]->clk);
> +		if (ret) {
> +			DRM_ERROR("Failed to enable clock %d: %d\n", i, ret);
> +			goto err;
> +		}
> +	}
> +
> +	return 0;
> +err:
> +	while (--i >= 0)
> +		clk_disable(mtk_crtc->ddp_comp[i]->clk);
> +	return ret;
> +}
> +
> +static void mtk_crtc_ddp_clk_disable(struct mtk_drm_crtc *mtk_crtc)
> +{
> +	int i;
> +
> +	DRM_DEBUG_DRIVER("%s\n", __func__);
> +	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++)
> +		clk_disable(mtk_crtc->ddp_comp[i]->clk);
> +}
> +
> +static int mtk_crtc_ddp_hw_init(struct mtk_drm_crtc *mtk_crtc)
> +{
> +	struct drm_crtc *crtc = &mtk_crtc->base;
> +	unsigned int width, height, vrefresh;
> +	int ret;
> +	int i;
> +
> +	DRM_DEBUG_DRIVER("%s\n", __func__);
> +	if (WARN_ON(!crtc->state))
> +		return -EINVAL;
> +
> +	width = crtc->state->adjusted_mode.hdisplay;
> +	height = crtc->state->adjusted_mode.vdisplay;
> +	vrefresh = crtc->state->adjusted_mode.vrefresh;
> +
> +	ret = pm_runtime_get_sync(crtc->dev->dev);
> +	if (ret < 0) {
> +		DRM_ERROR("Failed to enable power domain: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = mtk_disp_mutex_prepare(mtk_crtc->mutex);
> +	if (ret < 0) {
> +		DRM_ERROR("Failed to enable mutex clock: %d\n", ret);
> +		goto err_pm_runtime_put;
> +	}
> +
> +	ret = mtk_crtc_ddp_clk_enable(mtk_crtc);
> +	if (ret < 0) {
> +		DRM_ERROR("Failed to enable component clocks: %d\n", ret);
> +		goto err_mutex_unprepare;
> +	}
> +
> +	DRM_DEBUG_DRIVER("mediatek_ddp_ddp_path_setup\n");
> +	for (i = 0; i < mtk_crtc->ddp_comp_nr - 1; i++) {
> +		mtk_ddp_add_comp_to_path(mtk_crtc->config_regs,
> +					 mtk_crtc->ddp_comp[i]->id,
> +					 mtk_crtc->ddp_comp[i + 1]->id);
> +		mtk_disp_mutex_add_comp(mtk_crtc->mutex,
> +					mtk_crtc->ddp_comp[i]->id);
> +	}
> +	mtk_disp_mutex_add_comp(mtk_crtc->mutex, mtk_crtc->ddp_comp[i]->id);
> +	mtk_disp_mutex_enable(mtk_crtc->mutex);
> +
> +	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
> +		struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[i];
> +
> +		mtk_ddp_comp_config(comp, width, height, vrefresh);
> +		mtk_ddp_comp_start(comp);
> +	}
> +
> +	/* Initially configure all planes */
> +	for (i = 0; i < OVL_LAYER_NR; i++) {
> +		struct drm_plane *plane = &mtk_crtc->planes[i].base;
> +		struct mtk_plane_state *plane_state;
> +
> +		plane_state = to_mtk_plane_state(plane->state);
> +		mtk_ddp_comp_layer_config(mtk_crtc->ddp_comp[0], i,
> +					  plane_state);
> +	}
> +
> +	return 0;
> +
> +err_mutex_unprepare:
> +	mtk_disp_mutex_unprepare(mtk_crtc->mutex);
> +err_pm_runtime_put:
> +	pm_runtime_put(crtc->dev->dev);
> +	return ret;
> +}
> +
> +static void mtk_crtc_ddp_hw_fini(struct mtk_drm_crtc *mtk_crtc)
> +{
> +	struct drm_device *drm = mtk_crtc->base.dev;
> +	int i;
> +
> +	DRM_DEBUG_DRIVER("%s\n", __func__);
> +	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++)
> +		mtk_ddp_comp_stop(mtk_crtc->ddp_comp[i]);
> +	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++)
> +		mtk_disp_mutex_remove_comp(mtk_crtc->mutex,
> +					   mtk_crtc->ddp_comp[i]->id);
> +	mtk_disp_mutex_disable(mtk_crtc->mutex);
> +	for (i = 0; i < mtk_crtc->ddp_comp_nr - 1; i++) {
> +		mtk_ddp_remove_comp_from_path(mtk_crtc->config_regs,
> +					      mtk_crtc->ddp_comp[i]->id,
> +					      mtk_crtc->ddp_comp[i + 1]->id);
> +		mtk_disp_mutex_remove_comp(mtk_crtc->mutex,
> +					   mtk_crtc->ddp_comp[i]->id);
> +	}
> +	mtk_disp_mutex_remove_comp(mtk_crtc->mutex, mtk_crtc->ddp_comp[i]->id);
> +	mtk_crtc_ddp_clk_disable(mtk_crtc);
> +	mtk_disp_mutex_unprepare(mtk_crtc->mutex);
> +
> +	pm_runtime_put(drm->dev);
> +}
> +
> +static void mtk_drm_crtc_enable(struct drm_crtc *crtc)
> +{
> +	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
> +	struct mtk_ddp_comp *ovl = mtk_crtc->ddp_comp[0];
> +	int ret;
> +
> +	DRM_DEBUG_DRIVER("%s %d\n", __func__, crtc->base.id);
> +
> +	ret = mtk_smi_larb_get(ovl->larb_dev);
> +	if (ret) {
> +		DRM_ERROR("Failed to get larb: %d\n", ret);
> +		return;
> +	}
> +
> +	ret = mtk_crtc_ddp_hw_init(mtk_crtc);
> +	if (ret) {
> +		mtk_smi_larb_put(ovl->larb_dev);
> +		return;
> +	}
> +
> +	drm_crtc_vblank_on(crtc);
> +	mtk_crtc->enabled = true;
> +}
> +
> +static void mtk_drm_crtc_disable(struct drm_crtc *crtc)
> +{
> +	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
> +	struct mtk_ddp_comp *ovl = mtk_crtc->ddp_comp[0];
> +	int i;
> +
> +	DRM_DEBUG_DRIVER("%s %d\n", __func__, crtc->base.id);
> +	if (!mtk_crtc->enabled)
> +		return;
> +
> +	/* Set all pending plane state to disabled */
> +	for (i = 0; i < OVL_LAYER_NR; i++) {
> +		struct drm_plane *plane = &mtk_crtc->planes[i].base;
> +		struct mtk_plane_state *plane_state;
> +
> +		plane_state = to_mtk_plane_state(plane->state);
> +		plane_state->pending.enable = false;
> +		plane_state->pending.config = true;
> +	}
> +	mtk_crtc->pending_planes = true;
> +
> +	/* Wait for planes to be disabled */
> +	drm_crtc_wait_one_vblank(crtc);
> +
> +	drm_crtc_vblank_off(crtc);
> +	mtk_crtc_ddp_hw_fini(mtk_crtc);
> +	mtk_smi_larb_put(ovl->larb_dev);
> +
> +	mtk_crtc->enabled = false;
> +}
> +
> +static void mtk_drm_crtc_atomic_begin(struct drm_crtc *crtc,
> +				      struct drm_crtc_state *old_crtc_state)
> +{
> +	struct mtk_crtc_state *state = to_mtk_crtc_state(crtc->state);
> +	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
> +
> +	if (mtk_crtc->event && state->base.event)
> +		DRM_ERROR("new event while there is still a pending event\n");
> +
> +	if (state->base.event) {
> +		state->base.event->pipe = drm_crtc_index(crtc);
> +		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
> +		mtk_crtc->event = state->base.event;
> +		state->base.event = NULL;
> +	}
> +}
> +
> +static void mtk_drm_crtc_atomic_flush(struct drm_crtc *crtc,
> +				      struct drm_crtc_state *old_crtc_state)
> +{
> +	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
> +	unsigned int pending_planes = 0;
> +	int i;
> +
> +	if (mtk_crtc->event)
> +		mtk_crtc->pending_needs_vblank = true;
> +	for (i = 0; i < OVL_LAYER_NR; i++) {
> +		struct drm_plane *plane = &mtk_crtc->planes[i].base;
> +		struct mtk_plane_state *plane_state;
> +
> +		plane_state = to_mtk_plane_state(plane->state);
> +		if (plane_state->pending.dirty) {
> +			plane_state->pending.config = true;
> +			plane_state->pending.dirty = false;
> +			pending_planes |= BIT(i);
> +		}
> +	}
> +	if (pending_planes)
> +		mtk_crtc->pending_planes = true;
> +}
> +
> +static const struct drm_crtc_funcs mtk_crtc_funcs = {
> +	.set_config		= drm_atomic_helper_set_config,
> +	.page_flip		= drm_atomic_helper_page_flip,
> +	.destroy		= mtk_drm_crtc_destroy,
> +	.reset			= mtk_drm_crtc_reset,
> +	.atomic_duplicate_state	= mtk_drm_crtc_duplicate_state,
> +	.atomic_destroy_state	= mtk_drm_crtc_destroy_state,
> +};
> +
> +static const struct drm_crtc_helper_funcs mtk_crtc_helper_funcs = {
> +	.mode_fixup	= mtk_drm_crtc_mode_fixup,
> +	.mode_set_nofb	= mtk_drm_crtc_mode_set_nofb,
> +	.enable		= mtk_drm_crtc_enable,
> +	.disable	= mtk_drm_crtc_disable,
> +	.atomic_begin	= mtk_drm_crtc_atomic_begin,
> +	.atomic_flush	= mtk_drm_crtc_atomic_flush,
> +};
> +
> +static int mtk_drm_crtc_init(struct drm_device *drm,
> +			     struct mtk_drm_crtc *mtk_crtc,
> +			     struct drm_plane *primary,
> +			     struct drm_plane *cursor, unsigned int pipe)
> +{
> +	int ret;
> +
> +	ret = drm_crtc_init_with_planes(drm, &mtk_crtc->base, primary, cursor,
> +					&mtk_crtc_funcs, NULL);
> +	if (ret)
> +		goto err_cleanup_crtc;
> +
> +	drm_crtc_helper_add(&mtk_crtc->base, &mtk_crtc_helper_funcs);
> +
> +	return 0;
> +
> +err_cleanup_crtc:
> +	drm_crtc_cleanup(&mtk_crtc->base);
> +	return ret;
> +}
> +
> +void mtk_crtc_ddp_irq(struct drm_crtc *crtc, struct mtk_ddp_comp *ovl)
> +{
> +	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
> +	struct mtk_crtc_state *state = to_mtk_crtc_state(mtk_crtc->base.state);
> +	unsigned int i;
> +
> +	/*
> +	 * TODO: instead of updating the registers here, we should prepare
> +	 * working registers in atomic_commit and let the hardware command
> +	 * queue update module registers on vblank.
> +	 */
> +	if (state->pending_config) {
> +		mtk_ddp_comp_config(ovl, state->pending_width,
> +				    state->pending_height,
> +				    state->pending_vrefresh);
> +
> +		state->pending_config = false;
> +	}
> +
> +	if (mtk_crtc->pending_planes) {
> +		for (i = 0; i < OVL_LAYER_NR; i++) {
> +			struct drm_plane *plane = &mtk_crtc->planes[i].base;
> +			struct mtk_plane_state *plane_state;
> +
> +			plane_state = to_mtk_plane_state(plane->state);
> +
> +			if (plane_state->pending.config) {
> +				mtk_ddp_comp_layer_config(ovl, i, plane_state);
> +				plane_state->pending.config = false;
> +			}
> +		}
> +		mtk_crtc->pending_planes = false;
> +	}
> +
> +	mtk_drm_finish_page_flip(mtk_crtc);
> +}
> +
> +int mtk_drm_crtc_create(struct drm_device *drm_dev,
> +			const enum mtk_ddp_comp_id *path, unsigned int path_len)
> +{
> +	struct mtk_drm_private *priv = drm_dev->dev_private;
> +	struct device *dev = drm_dev->dev;
> +	struct mtk_drm_crtc *mtk_crtc;
> +	enum drm_plane_type type;
> +	unsigned int zpos;
> +	int pipe = priv->num_pipes;
> +	int ret;
> +	int i;
> +
> +	for (i = 0; i < path_len; i++) {
> +		enum mtk_ddp_comp_id comp_id = path[i];
> +		struct device_node *node;
> +
> +		node = priv->comp_node[comp_id];
> +		if (!node) {
> +			dev_info(dev,
> +				 "Not creating crtc %d because component %d is disabled or missing\n",
> +				 pipe, comp_id);
> +			return 0;
> +		}
> +	}
> +
> +	mtk_crtc = devm_kzalloc(dev, sizeof(*mtk_crtc), GFP_KERNEL);
> +	if (!mtk_crtc)
> +		return -ENOMEM;
> +
> +	mtk_crtc->config_regs = priv->config_regs;
> +	mtk_crtc->ddp_comp_nr = path_len;
> +	mtk_crtc->ddp_comp = devm_kmalloc_array(dev, mtk_crtc->ddp_comp_nr,
> +						sizeof(*mtk_crtc->ddp_comp),
> +						GFP_KERNEL);
> +
> +	mtk_crtc->mutex = mtk_disp_mutex_get(priv->mutex_dev, pipe);
> +	if (IS_ERR(mtk_crtc->mutex)) {
> +		ret = PTR_ERR(mtk_crtc->mutex);
> +		dev_err(dev, "Failed to get mutex: %d\n", ret);
> +		return ret;
> +	}
> +
> +	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
> +		enum mtk_ddp_comp_id comp_id = path[i];
> +		struct mtk_ddp_comp *comp;
> +		struct device_node *node;
> +
> +		node = priv->comp_node[comp_id];
> +		comp = priv->ddp_comp[comp_id];
> +		if (!comp) {
> +			dev_err(dev, "Component %s not initialized\n",
> +				node->full_name);
> +			ret = -ENODEV;
> +			goto unprepare;
> +		}
> +
> +		ret = clk_prepare(comp->clk);
> +		if (ret) {
> +			dev_err(dev,
> +				"Failed to prepare clock for component %s: %d\n",
> +				node->full_name, ret);
> +			goto unprepare;
> +		}
> +
> +		mtk_crtc->ddp_comp[i] = comp;
> +	}
> +
> +	for (zpos = 0; zpos < OVL_LAYER_NR; zpos++) {
> +		type = (zpos == 0) ? DRM_PLANE_TYPE_PRIMARY :
> +				(zpos == 1) ? DRM_PLANE_TYPE_CURSOR :
> +						DRM_PLANE_TYPE_OVERLAY;
> +		ret = mtk_plane_init(drm_dev, &mtk_crtc->planes[zpos],
> +				     BIT(pipe), type, zpos);
> +		if (ret)
> +			goto unprepare;
> +	}
> +
> +	ret = mtk_drm_crtc_init(drm_dev, mtk_crtc, &mtk_crtc->planes[0].base,
> +				&mtk_crtc->planes[1].base, pipe);
> +	if (ret < 0)
> +		goto unprepare;
> +
> +	priv->crtc[pipe] = &mtk_crtc->base;
> +	priv->num_pipes++;
> +
> +	return 0;
> +
> +unprepare:
> +	while (--i >= 0)
> +		clk_unprepare(mtk_crtc->ddp_comp[i]->clk);
> +
> +	return ret;
> +}
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.h b/drivers/gpu/drm/mediatek/mtk_drm_crtc.h
> new file mode 100644
> index 0000000..81e5566
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.h
> @@ -0,0 +1,32 @@
> +/*
> + * Copyright (c) 2015 MediaTek Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef MTK_DRM_CRTC_H
> +#define MTK_DRM_CRTC_H
> +
> +#include <drm/drm_crtc.h>
> +#include "mtk_drm_ddp_comp.h"
> +#include "mtk_drm_plane.h"
> +
> +#define OVL_LAYER_NR	4
> +
> +int mtk_drm_crtc_enable_vblank(struct drm_device *drm, unsigned int pipe);
> +void mtk_drm_crtc_disable_vblank(struct drm_device *drm, unsigned int pipe);
> +void mtk_drm_crtc_check_flush(struct drm_crtc *crtc);
> +void mtk_drm_crtc_commit(struct drm_crtc *crtc);
> +void mtk_crtc_ddp_irq(struct drm_crtc *crtc, struct mtk_ddp_comp *ovl);
> +int mtk_drm_crtc_create(struct drm_device *drm_dev,
> +			const enum mtk_ddp_comp_id *path,
> +			unsigned int path_len);
> +
> +#endif /* MTK_DRM_CRTC_H */
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
> new file mode 100644
> index 0000000..17ba9355
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
> @@ -0,0 +1,353 @@
> +/*
> + * Copyright (c) 2015 MediaTek Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +
> +#include "mtk_drm_ddp.h"
> +#include "mtk_drm_ddp_comp.h"
> +
> +#define DISP_REG_CONFIG_DISP_OVL0_MOUT_EN	0x040
> +#define DISP_REG_CONFIG_DISP_OVL1_MOUT_EN	0x044
> +#define DISP_REG_CONFIG_DISP_OD_MOUT_EN		0x048
> +#define DISP_REG_CONFIG_DISP_GAMMA_MOUT_EN	0x04c
> +#define DISP_REG_CONFIG_DISP_UFOE_MOUT_EN	0x050
> +#define DISP_REG_CONFIG_DISP_COLOR0_SEL_IN	0x084
> +#define DISP_REG_CONFIG_DISP_COLOR1_SEL_IN	0x088
> +#define DISP_REG_CONFIG_DPI_SEL_IN		0x0ac
> +#define DISP_REG_CONFIG_DISP_RDMA1_MOUT_EN	0x0c8
> +#define DISP_REG_CONFIG_MMSYS_CG_CON0		0x100
> +
> +#define DISP_REG_MUTEX_EN(n)	(0x20 + 0x20 * (n))
> +#define DISP_REG_MUTEX_RST(n)	(0x28 + 0x20 * (n))
> +#define DISP_REG_MUTEX_MOD(n)	(0x2c + 0x20 * (n))
> +#define DISP_REG_MUTEX_SOF(n)	(0x30 + 0x20 * (n))
> +
> +#define MUTEX_MOD_DISP_OVL0		BIT(11)
> +#define MUTEX_MOD_DISP_OVL1		BIT(12)
> +#define MUTEX_MOD_DISP_RDMA0		BIT(13)
> +#define MUTEX_MOD_DISP_RDMA1		BIT(14)
> +#define MUTEX_MOD_DISP_RDMA2		BIT(15)
> +#define MUTEX_MOD_DISP_WDMA0		BIT(16)
> +#define MUTEX_MOD_DISP_WDMA1		BIT(17)
> +#define MUTEX_MOD_DISP_COLOR0		BIT(18)
> +#define MUTEX_MOD_DISP_COLOR1		BIT(19)
> +#define MUTEX_MOD_DISP_AAL		BIT(20)
> +#define MUTEX_MOD_DISP_GAMMA		BIT(21)
> +#define MUTEX_MOD_DISP_UFOE		BIT(22)
> +#define MUTEX_MOD_DISP_PWM0		BIT(23)
> +#define MUTEX_MOD_DISP_PWM1		BIT(24)
> +#define MUTEX_MOD_DISP_OD		BIT(25)
> +
> +#define MUTEX_SOF_SINGLE_MODE		0
> +#define MUTEX_SOF_DSI0			1
> +#define MUTEX_SOF_DSI1			2
> +#define MUTEX_SOF_DPI0			3
> +
> +#define OVL0_MOUT_EN_COLOR0		0x1
> +#define OD_MOUT_EN_RDMA0		0x1
> +#define UFOE_MOUT_EN_DSI0		0x1
> +#define COLOR0_SEL_IN_OVL0		0x1
> +#define OVL1_MOUT_EN_COLOR1		0x1
> +#define GAMMA_MOUT_EN_RDMA1		0x1
> +#define RDMA1_MOUT_DPI0			0x2
> +#define DPI0_SEL_IN_RDMA1		0x1
> +#define COLOR1_SEL_IN_OVL1		0x1
> +
> +struct mtk_disp_mutex {
> +	int id;
> +	bool claimed;
> +};
> +
> +struct mtk_ddp {
> +	struct device			*dev;
> +	struct clk			*clk;
> +	void __iomem			*regs;
> +	struct mtk_disp_mutex		mutex[10];
> +};
> +
> +static const unsigned int mutex_mod[DDP_COMPONENT_ID_MAX] = {
> +	[DDP_COMPONENT_AAL] = MUTEX_MOD_DISP_AAL,
> +	[DDP_COMPONENT_COLOR0] = MUTEX_MOD_DISP_COLOR0,
> +	[DDP_COMPONENT_COLOR1] = MUTEX_MOD_DISP_COLOR1,
> +	[DDP_COMPONENT_GAMMA] = MUTEX_MOD_DISP_GAMMA,
> +	[DDP_COMPONENT_OD] = MUTEX_MOD_DISP_OD,
> +	[DDP_COMPONENT_OVL0] = MUTEX_MOD_DISP_OVL0,
> +	[DDP_COMPONENT_OVL1] = MUTEX_MOD_DISP_OVL1,
> +	[DDP_COMPONENT_PWM0] = MUTEX_MOD_DISP_PWM0,
> +	[DDP_COMPONENT_PWM1] = MUTEX_MOD_DISP_PWM1,
> +	[DDP_COMPONENT_RDMA0] = MUTEX_MOD_DISP_RDMA0,
> +	[DDP_COMPONENT_RDMA1] = MUTEX_MOD_DISP_RDMA1,
> +	[DDP_COMPONENT_RDMA2] = MUTEX_MOD_DISP_RDMA2,
> +	[DDP_COMPONENT_UFOE] = MUTEX_MOD_DISP_UFOE,
> +	[DDP_COMPONENT_WDMA0] = MUTEX_MOD_DISP_WDMA0,
> +	[DDP_COMPONENT_WDMA1] = MUTEX_MOD_DISP_WDMA1,
> +};
> +
> +static unsigned int mtk_ddp_mout_en(enum mtk_ddp_comp_id cur,
> +				    enum mtk_ddp_comp_id next,
> +				    unsigned int *addr)
> +{
> +	unsigned int value;
> +
> +	if (cur == DDP_COMPONENT_OVL0 && next == DDP_COMPONENT_COLOR0) {
> +		*addr = DISP_REG_CONFIG_DISP_OVL0_MOUT_EN;
> +		value = OVL0_MOUT_EN_COLOR0;
> +	} else if (cur == DDP_COMPONENT_OD && next == DDP_COMPONENT_RDMA0) {
> +		*addr = DISP_REG_CONFIG_DISP_OD_MOUT_EN;
> +		value = OD_MOUT_EN_RDMA0;
> +	} else if (cur == DDP_COMPONENT_UFOE && next == DDP_COMPONENT_DSI0) {
> +		*addr = DISP_REG_CONFIG_DISP_UFOE_MOUT_EN;
> +		value = UFOE_MOUT_EN_DSI0;
> +	} else if (cur == DDP_COMPONENT_OVL1 && next == DDP_COMPONENT_COLOR1) {
> +		*addr = DISP_REG_CONFIG_DISP_OVL1_MOUT_EN;
> +		value = OVL1_MOUT_EN_COLOR1;
> +	} else if (cur == DDP_COMPONENT_GAMMA && next == DDP_COMPONENT_RDMA1) {
> +		*addr = DISP_REG_CONFIG_DISP_GAMMA_MOUT_EN;
> +		value = GAMMA_MOUT_EN_RDMA1;
> +	} else if (cur == DDP_COMPONENT_RDMA1 && next == DDP_COMPONENT_DPI0) {
> +		*addr = DISP_REG_CONFIG_DISP_RDMA1_MOUT_EN;
> +		value = RDMA1_MOUT_DPI0;
> +	} else {
> +		value = 0;
> +	}
> +
> +	return value;
> +}
> +
> +static unsigned int mtk_ddp_sel_in(enum mtk_ddp_comp_id cur,
> +				   enum mtk_ddp_comp_id next,
> +				   unsigned int *addr)
> +{
> +	unsigned int value;
> +
> +	if (cur == DDP_COMPONENT_OVL0 && next == DDP_COMPONENT_COLOR0) {
> +		*addr = DISP_REG_CONFIG_DISP_COLOR0_SEL_IN;
> +		value = COLOR0_SEL_IN_OVL0;
> +	} else if (cur == DDP_COMPONENT_RDMA1 && next == DDP_COMPONENT_DPI0) {
> +		*addr = DISP_REG_CONFIG_DPI_SEL_IN;
> +		value = DPI0_SEL_IN_RDMA1;
> +	} else if (cur == DDP_COMPONENT_OVL1 && next == DDP_COMPONENT_COLOR1) {
> +		*addr = DISP_REG_CONFIG_DISP_COLOR1_SEL_IN;
> +		value = COLOR1_SEL_IN_OVL1;
> +	} else {
> +		value = 0;
> +	}
> +
> +	return value;
> +}
> +
> +void mtk_ddp_add_comp_to_path(void __iomem *config_regs,
> +			      enum mtk_ddp_comp_id cur,
> +			      enum mtk_ddp_comp_id next)
> +{
> +	unsigned int addr, value, reg;
> +
> +	value = mtk_ddp_mout_en(cur, next, &addr);
> +	if (value) {
> +		reg = readl_relaxed(config_regs + addr) | value;
> +		writel_relaxed(reg, config_regs + addr);
> +	}
> +
> +	value = mtk_ddp_sel_in(cur, next, &addr);
> +	if (value) {
> +		reg = readl_relaxed(config_regs + addr) | value;
> +		writel_relaxed(reg, config_regs + addr);
> +	}
> +}
> +
> +void mtk_ddp_remove_comp_from_path(void __iomem *config_regs,
> +				   enum mtk_ddp_comp_id cur,
> +				   enum mtk_ddp_comp_id next)
> +{
> +	unsigned int addr, value, reg;
> +
> +	value = mtk_ddp_mout_en(cur, next, &addr);
> +	if (value) {
> +		reg = readl_relaxed(config_regs + addr) & ~value;
> +		writel_relaxed(reg, config_regs + addr);
> +	}
> +
> +	value = mtk_ddp_sel_in(cur, next, &addr);
> +	if (value) {
> +		reg = readl_relaxed(config_regs + addr) & ~value;
> +		writel_relaxed(reg, config_regs + addr);
> +	}
> +}
> +
> +struct mtk_disp_mutex *mtk_disp_mutex_get(struct device *dev, unsigned int id)
> +{
> +	struct mtk_ddp *ddp = dev_get_drvdata(dev);
> +
> +	if (id >= 10)
> +		return ERR_PTR(-EINVAL);
> +	if (ddp->mutex[id].claimed)
> +		return ERR_PTR(-EBUSY);
> +
> +	ddp->mutex[id].claimed = true;
> +
> +	return &ddp->mutex[id];
> +}
> +
> +void mtk_disp_mutex_put(struct mtk_disp_mutex *mutex)
> +{
> +	struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
> +					   mutex[mutex->id]);
> +
> +	WARN_ON(&ddp->mutex[mutex->id] != mutex);
> +
> +	mutex->claimed = false;
> +}
> +
> +int mtk_disp_mutex_prepare(struct mtk_disp_mutex *mutex)
> +{
> +	struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
> +					   mutex[mutex->id]);
> +	return clk_prepare_enable(ddp->clk);
> +}
> +
> +void mtk_disp_mutex_unprepare(struct mtk_disp_mutex *mutex)
> +{
> +	struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
> +					   mutex[mutex->id]);
> +	clk_disable_unprepare(ddp->clk);
> +}
> +
> +void mtk_disp_mutex_add_comp(struct mtk_disp_mutex *mutex,
> +			     enum mtk_ddp_comp_id id)
> +{
> +	struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
> +					   mutex[mutex->id]);
> +	unsigned int reg;
> +
> +	WARN_ON(&ddp->mutex[mutex->id] != mutex);
> +
> +	switch (id) {
> +	case DDP_COMPONENT_DSI0:
> +		reg = MUTEX_SOF_DSI0;
> +		break;
> +	case DDP_COMPONENT_DSI1:
> +		reg = MUTEX_SOF_DSI0;
> +		break;
> +	case DDP_COMPONENT_DPI0:
> +		reg = MUTEX_SOF_DPI0;
> +		break;
> +	default:
> +		reg = readl_relaxed(ddp->regs + DISP_REG_MUTEX_MOD(mutex->id));
> +		reg |= mutex_mod[id];
> +		writel_relaxed(reg, ddp->regs + DISP_REG_MUTEX_MOD(mutex->id));
> +		return;
> +	}
> +
> +	writel_relaxed(reg, ddp->regs + DISP_REG_MUTEX_SOF(mutex->id));
> +}
> +
> +void mtk_disp_mutex_remove_comp(struct mtk_disp_mutex *mutex,
> +				enum mtk_ddp_comp_id id)
> +{
> +	struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
> +					   mutex[mutex->id]);
> +	unsigned int reg;
> +
> +	WARN_ON(&ddp->mutex[mutex->id] != mutex);
> +
> +	switch (id) {
> +	case DDP_COMPONENT_DSI0:
> +	case DDP_COMPONENT_DSI1:
> +	case DDP_COMPONENT_DPI0:
> +		writel_relaxed(MUTEX_SOF_SINGLE_MODE,
> +			       ddp->regs + DISP_REG_MUTEX_SOF(mutex->id));
> +		break;
> +	default:
> +		reg = readl_relaxed(ddp->regs + DISP_REG_MUTEX_MOD(mutex->id));
> +		reg &= ~mutex_mod[id];
> +		writel_relaxed(reg, ddp->regs + DISP_REG_MUTEX_MOD(mutex->id));
> +		break;
> +	}
> +}
> +
> +void mtk_disp_mutex_enable(struct mtk_disp_mutex *mutex)
> +{
> +	struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
> +					   mutex[mutex->id]);
> +
> +	WARN_ON(&ddp->mutex[mutex->id] != mutex);
> +
> +	writel(1, ddp->regs + DISP_REG_MUTEX_EN(mutex->id));
> +}
> +
> +void mtk_disp_mutex_disable(struct mtk_disp_mutex *mutex)
> +{
> +	struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
> +					   mutex[mutex->id]);
> +
> +	WARN_ON(&ddp->mutex[mutex->id] != mutex);
> +
> +	writel(0, ddp->regs + DISP_REG_MUTEX_EN(mutex->id));
> +}
> +
> +static int mtk_ddp_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct mtk_ddp *ddp;
> +	struct resource *regs;
> +	int i;
> +
> +	ddp = devm_kzalloc(dev, sizeof(*ddp), GFP_KERNEL);
> +	if (!ddp)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < 10; i++)
> +		ddp->mutex[i].id = i;
> +
> +	ddp->clk = devm_clk_get(dev, NULL);
> +	if (IS_ERR(ddp->clk)) {
> +		dev_err(dev, "Failed to get clock\n");
> +		return PTR_ERR(ddp->clk);
> +	}
> +
> +	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	ddp->regs = devm_ioremap_resource(dev, regs);
> +	if (IS_ERR(ddp->regs)) {
> +		dev_err(dev, "Failed to map mutex registers\n");
> +		return PTR_ERR(ddp->regs);
> +	}
> +
> +	platform_set_drvdata(pdev, ddp);
> +
> +	return 0;
> +}
> +
> +static int mtk_ddp_remove(struct platform_device *pdev)
> +{
> +	return 0;
> +}
> +
> +static const struct of_device_id ddp_driver_dt_match[] = {
> +	{ .compatible = "mediatek,mt8173-disp-mutex" },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, ddp_driver_dt_match);
> +
> +struct platform_driver mtk_ddp_driver = {
> +	.probe		= mtk_ddp_probe,
> +	.remove		= mtk_ddp_remove,
> +	.driver		= {
> +		.name	= "mediatek-ddp",
> +		.owner	= THIS_MODULE,
> +		.of_match_table = ddp_driver_dt_match,
> +	},
> +};
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp.h
> new file mode 100644
> index 0000000..92c1175
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp.h
> @@ -0,0 +1,41 @@
> +/*
> + * Copyright (c) 2015 MediaTek Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef MTK_DRM_DDP_H
> +#define MTK_DRM_DDP_H
> +
> +#include "mtk_drm_ddp_comp.h"
> +
> +struct regmap;
> +struct device;
> +struct mtk_disp_mutex;
> +
> +void mtk_ddp_add_comp_to_path(void __iomem *config_regs,
> +			      enum mtk_ddp_comp_id cur,
> +			      enum mtk_ddp_comp_id next);
> +void mtk_ddp_remove_comp_from_path(void __iomem *config_regs,
> +				   enum mtk_ddp_comp_id cur,
> +				   enum mtk_ddp_comp_id next);
> +
> +struct mtk_disp_mutex *mtk_disp_mutex_get(struct device *dev, unsigned int id);
> +int mtk_disp_mutex_prepare(struct mtk_disp_mutex *mutex);
> +void mtk_disp_mutex_add_comp(struct mtk_disp_mutex *mutex,
> +			     enum mtk_ddp_comp_id id);
> +void mtk_disp_mutex_enable(struct mtk_disp_mutex *mutex);
> +void mtk_disp_mutex_disable(struct mtk_disp_mutex *mutex);
> +void mtk_disp_mutex_remove_comp(struct mtk_disp_mutex *mutex,
> +				enum mtk_ddp_comp_id id);
> +void mtk_disp_mutex_unprepare(struct mtk_disp_mutex *mutex);
> +void mtk_disp_mutex_put(struct mtk_disp_mutex *mutex);
> +
> +#endif /* MTK_DRM_DDP_H */
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
> new file mode 100644
> index 0000000..3970fcf
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
> @@ -0,0 +1,225 @@
> +/*
> + * Copyright (c) 2015 MediaTek Inc.
> + * Authors:
> + *	YT Shen <yt.shen@mediatek.com>
> + *	CK Hu <ck.hu@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.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <drm/drmP.h>
> +#include "mtk_drm_drv.h"
> +#include "mtk_drm_plane.h"
> +#include "mtk_drm_ddp_comp.h"
> +
> +#define DISP_OD_EN				0x0000
> +#define DISP_OD_INTEN				0x0008
> +#define DISP_OD_INTSTA				0x000c
> +#define DISP_OD_CFG				0x0020
> +#define DISP_OD_SIZE				0x0030
> +
> +#define DISP_REG_UFO_START			0x0000
> +
> +#define DISP_COLOR_CFG_MAIN			0x0400
> +#define DISP_COLOR_START			0x0c00
> +#define DISP_COLOR_WIDTH			0x0c50
> +#define DISP_COLOR_HEIGHT			0x0c54
> +
> +#define	OD_RELAY_MODE		BIT(0)
> +
> +#define	UFO_BYPASS		BIT(2)
> +
> +#define	COLOR_BYPASS_ALL	BIT(7)
> +#define	COLOR_SEQ_SEL		BIT(13)
> +
> +static void mtk_color_config(struct mtk_ddp_comp *comp, unsigned int w,
> +			     unsigned int h, unsigned int vrefresh)
> +{
> +	writel(w, comp->regs + DISP_COLOR_WIDTH);
> +	writel(h, comp->regs + DISP_COLOR_HEIGHT);
> +}
> +
> +static void mtk_color_start(struct mtk_ddp_comp *comp)
> +{
> +	writel(COLOR_BYPASS_ALL | COLOR_SEQ_SEL,
> +	       comp->regs + DISP_COLOR_CFG_MAIN);
> +	writel(0x1, comp->regs + DISP_COLOR_START);
> +}
> +
> +static void mtk_od_config(struct mtk_ddp_comp *comp, unsigned int w,
> +			  unsigned int h, unsigned int vrefresh)
> +{
> +	writel(w << 16 | h, comp->regs + DISP_OD_SIZE);
> +}
> +
> +static void mtk_od_start(struct mtk_ddp_comp *comp)
> +{
> +	writel(OD_RELAY_MODE, comp->regs + DISP_OD_CFG);
> +	writel(1, comp->regs + DISP_OD_EN);
> +}
> +
> +static void mtk_ufoe_start(struct mtk_ddp_comp *comp)
> +{
> +	writel(UFO_BYPASS, comp->regs + DISP_REG_UFO_START);
> +}
> +
> +static const struct mtk_ddp_comp_funcs ddp_color = {
> +	.config = mtk_color_config,
> +	.start = mtk_color_start,
> +};
> +
> +static const struct mtk_ddp_comp_funcs ddp_od = {
> +	.config = mtk_od_config,
> +	.start = mtk_od_start,
> +};
> +
> +static const struct mtk_ddp_comp_funcs ddp_ufoe = {
> +	.start = mtk_ufoe_start,
> +};
> +
> +static const char * const mtk_ddp_comp_stem[MTK_DDP_COMP_TYPE_MAX] = {
> +	[MTK_DISP_OVL] = "ovl",
> +	[MTK_DISP_RDMA] = "rdma",
> +	[MTK_DISP_WDMA] = "wdma",
> +	[MTK_DISP_COLOR] = "color",
> +	[MTK_DISP_AAL] = "aal",
> +	[MTK_DISP_GAMMA] = "gamma",
> +	[MTK_DISP_UFOE] = "ufoe",
> +	[MTK_DSI] = "dsi",
> +	[MTK_DPI] = "dpi",
> +	[MTK_DISP_PWM] = "pwm",
> +	[MTK_DISP_MUTEX] = "mutex",
> +	[MTK_DISP_OD] = "od",
> +};
> +
> +struct mtk_ddp_comp_match {
> +	enum mtk_ddp_comp_type type;
> +	int alias_id;
> +	const struct mtk_ddp_comp_funcs *funcs;
> +};
> +
> +static const struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_ID_MAX] = {
> +	[DDP_COMPONENT_AAL]	= { MTK_DISP_AAL,	0, NULL },
> +	[DDP_COMPONENT_COLOR0]	= { MTK_DISP_COLOR,	0, &ddp_color },
> +	[DDP_COMPONENT_COLOR1]	= { MTK_DISP_COLOR,	1, &ddp_color },
> +	[DDP_COMPONENT_DPI0]	= { MTK_DPI,		0, NULL },
> +	[DDP_COMPONENT_DSI0]	= { MTK_DSI,		0, NULL },
> +	[DDP_COMPONENT_DSI1]	= { MTK_DSI,		1, NULL },
> +	[DDP_COMPONENT_GAMMA]	= { MTK_DISP_GAMMA,	0, NULL },
> +	[DDP_COMPONENT_OD]	= { MTK_DISP_OD,	0, &ddp_od },
> +	[DDP_COMPONENT_OVL0]	= { MTK_DISP_OVL,	0, NULL },
> +	[DDP_COMPONENT_OVL1]	= { MTK_DISP_OVL,	1, NULL },
> +	[DDP_COMPONENT_PWM0]	= { MTK_DISP_PWM,	0, NULL },
> +	[DDP_COMPONENT_RDMA0]	= { MTK_DISP_RDMA,	0, NULL },
> +	[DDP_COMPONENT_RDMA1]	= { MTK_DISP_RDMA,	1, NULL },
> +	[DDP_COMPONENT_RDMA2]	= { MTK_DISP_RDMA,	2, NULL },
> +	[DDP_COMPONENT_UFOE]	= { MTK_DISP_UFOE,	0, &ddp_ufoe },
> +	[DDP_COMPONENT_WDMA0]	= { MTK_DISP_WDMA,	0, NULL },
> +	[DDP_COMPONENT_WDMA1]	= { MTK_DISP_WDMA,	1, NULL },
> +};
> +
> +int mtk_ddp_comp_get_id(struct device_node *node,
> +			enum mtk_ddp_comp_type comp_type)
> +{
> +	int id = of_alias_get_id(node, mtk_ddp_comp_stem[comp_type]);
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(mtk_ddp_matches); i++) {
> +		if (comp_type == mtk_ddp_matches[i].type &&
> +		    (id < 0 || id == mtk_ddp_matches[i].alias_id))
> +			return i;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +int mtk_ddp_comp_init(struct device *dev, struct device_node *node,
> +		      struct mtk_ddp_comp *comp, enum mtk_ddp_comp_id comp_id,
> +		      const struct mtk_ddp_comp_funcs *funcs)
> +{
> +	enum mtk_ddp_comp_type type;
> +	struct device_node *larb_node;
> +	struct platform_device *larb_pdev;
> +
> +	if (comp_id < 0 || comp_id >= DDP_COMPONENT_ID_MAX)
> +		return -EINVAL;
> +
> +	comp->id = comp_id;
> +	comp->funcs = funcs ?: mtk_ddp_matches[comp_id].funcs;
> +
> +	if (comp_id == DDP_COMPONENT_DPI0 ||
> +	    comp_id == DDP_COMPONENT_DSI0 ||
> +	    comp_id == DDP_COMPONENT_PWM0) {
> +		comp->regs = NULL;
> +		comp->clk = NULL;
> +		comp->irq = 0;
> +		return 0;
> +	}
> +
> +	comp->regs = of_iomap(node, 0);
> +	comp->irq = of_irq_get(node, 0);
> +	comp->clk = of_clk_get(node, 0);
> +	if (IS_ERR(comp->clk))
> +		comp->clk = NULL;
> +
> +	type = mtk_ddp_matches[comp_id].type;
> +
> +	/* Only DMA capable components need the LARB property */
> +	comp->larb_dev = NULL;
> +	if (type != MTK_DISP_OVL &&
> +	    type != MTK_DISP_RDMA &&
> +	    type != MTK_DISP_WDMA)
> +		return 0;
> +
> +	larb_node = of_parse_phandle(node, "mediatek,larb", 0);
> +	if (!larb_node) {
> +		dev_err(dev,
> +			"Missing mediadek,larb phandle in %s node\n",
> +			node->full_name);
> +		return -EINVAL;
> +	}
> +
> +	larb_pdev = of_find_device_by_node(larb_node);
> +	if (!larb_pdev) {
> +		dev_warn(dev, "Waiting for larb device %s\n",
> +			 larb_node->full_name);
> +		of_node_put(larb_node);
> +		return -EPROBE_DEFER;
> +	}
> +	of_node_put(larb_node);
> +
> +	comp->larb_dev = &larb_pdev->dev;
> +
> +	return 0;
> +}
> +
> +int mtk_ddp_comp_register(struct drm_device *drm, struct mtk_ddp_comp *comp)
> +{
> +	struct mtk_drm_private *private = drm->dev_private;
> +
> +	if (private->ddp_comp[comp->id])
> +		return -EBUSY;
> +
> +	private->ddp_comp[comp->id] = comp;
> +	return 0;
> +}
> +
> +void mtk_ddp_comp_unregister(struct drm_device *drm, struct mtk_ddp_comp *comp)
> +{
> +	struct mtk_drm_private *private = drm->dev_private;
> +
> +	private->ddp_comp[comp->id] = NULL;
> +}
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
> new file mode 100644
> index 0000000..6b13ba9
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
> @@ -0,0 +1,150 @@
> +/*
> + * Copyright (c) 2015 MediaTek Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef MTK_DRM_DDP_COMP_H
> +#define MTK_DRM_DDP_COMP_H
> +
> +#include <linux/io.h>
> +
> +struct device;
> +struct device_node;
> +struct drm_crtc;
> +struct drm_device;
> +struct mtk_plane_state;
> +
> +enum mtk_ddp_comp_type {
> +	MTK_DISP_OVL,
> +	MTK_DISP_RDMA,
> +	MTK_DISP_WDMA,
> +	MTK_DISP_COLOR,
> +	MTK_DISP_AAL,
> +	MTK_DISP_GAMMA,
> +	MTK_DISP_UFOE,
> +	MTK_DSI,
> +	MTK_DPI,
> +	MTK_DISP_PWM,
> +	MTK_DISP_MUTEX,
> +	MTK_DISP_OD,
> +	MTK_DDP_COMP_TYPE_MAX,
> +};
> +
> +enum mtk_ddp_comp_id {
> +	DDP_COMPONENT_AAL,
> +	DDP_COMPONENT_COLOR0,
> +	DDP_COMPONENT_COLOR1,
> +	DDP_COMPONENT_DPI0,
> +	DDP_COMPONENT_DSI0,
> +	DDP_COMPONENT_DSI1,
> +	DDP_COMPONENT_GAMMA,
> +	DDP_COMPONENT_OD,
> +	DDP_COMPONENT_OVL0,
> +	DDP_COMPONENT_OVL1,
> +	DDP_COMPONENT_PWM0,
> +	DDP_COMPONENT_PWM1,
> +	DDP_COMPONENT_RDMA0,
> +	DDP_COMPONENT_RDMA1,
> +	DDP_COMPONENT_RDMA2,
> +	DDP_COMPONENT_UFOE,
> +	DDP_COMPONENT_WDMA0,
> +	DDP_COMPONENT_WDMA1,
> +	DDP_COMPONENT_ID_MAX,
> +};
> +
> +struct mtk_ddp_comp;
> +
> +struct mtk_ddp_comp_funcs {
> +	void (*config)(struct mtk_ddp_comp *comp, unsigned int w,
> +		       unsigned int h, unsigned int vrefresh);
> +	void (*start)(struct mtk_ddp_comp *comp);
> +	void (*stop)(struct mtk_ddp_comp *comp);
> +	void (*enable_vblank)(struct mtk_ddp_comp *comp, struct drm_crtc *crtc);
> +	void (*disable_vblank)(struct mtk_ddp_comp *comp);
> +	void (*layer_on)(struct mtk_ddp_comp *comp, unsigned int idx);
> +	void (*layer_off)(struct mtk_ddp_comp *comp, unsigned int idx);
> +	void (*layer_config)(struct mtk_ddp_comp *comp, unsigned int idx,
> +			     struct mtk_plane_state *state);
> +};
> +
> +struct mtk_ddp_comp {
> +	struct clk *clk;
> +	void __iomem *regs;
> +	int irq;
> +	struct device *larb_dev;
> +	enum mtk_ddp_comp_id id;
> +	const struct mtk_ddp_comp_funcs *funcs;
> +};
> +
> +static inline void mtk_ddp_comp_config(struct mtk_ddp_comp *comp,
> +				       unsigned int w, unsigned int h,
> +				       unsigned int vrefresh)
> +{
> +	if (comp->funcs && comp->funcs->config)
> +		comp->funcs->config(comp, w, h, vrefresh);
> +}
> +
> +static inline void mtk_ddp_comp_start(struct mtk_ddp_comp *comp)
> +{
> +	if (comp->funcs && comp->funcs->start)
> +		comp->funcs->start(comp);
> +}
> +
> +static inline void mtk_ddp_comp_stop(struct mtk_ddp_comp *comp)
> +{
> +	if (comp->funcs && comp->funcs->stop)
> +		comp->funcs->stop(comp);
> +}
> +
> +static inline void mtk_ddp_comp_enable_vblank(struct mtk_ddp_comp *comp,
> +					      struct drm_crtc *crtc)
> +{
> +	if (comp->funcs && comp->funcs->enable_vblank)
> +		comp->funcs->enable_vblank(comp, crtc);
> +}
> +
> +static inline void mtk_ddp_comp_disable_vblank(struct mtk_ddp_comp *comp)
> +{
> +	if (comp->funcs && comp->funcs->disable_vblank)
> +		comp->funcs->disable_vblank(comp);
> +}
> +
> +static inline void mtk_ddp_comp_layer_on(struct mtk_ddp_comp *comp,
> +					 unsigned int idx)
> +{
> +	if (comp->funcs && comp->funcs->layer_on)
> +		comp->funcs->layer_on(comp, idx);
> +}
> +
> +static inline void mtk_ddp_comp_layer_off(struct mtk_ddp_comp *comp,
> +					  unsigned int idx)
> +{
> +	if (comp->funcs && comp->funcs->layer_off)
> +		comp->funcs->layer_off(comp, idx);
> +}
> +
> +static inline void mtk_ddp_comp_layer_config(struct mtk_ddp_comp *comp,
> +					     unsigned int idx,
> +					     struct mtk_plane_state *state)
> +{
> +	if (comp->funcs && comp->funcs->layer_config)
> +		comp->funcs->layer_config(comp, idx, state);
> +}
> +
> +int mtk_ddp_comp_get_id(struct device_node *node,
> +			enum mtk_ddp_comp_type comp_type);
> +int mtk_ddp_comp_init(struct device *dev, struct device_node *comp_node,
> +		      struct mtk_ddp_comp *comp, enum mtk_ddp_comp_id comp_id,
> +		      const struct mtk_ddp_comp_funcs *funcs);
> +int mtk_ddp_comp_register(struct drm_device *drm, struct mtk_ddp_comp *comp);
> +void mtk_ddp_comp_unregister(struct drm_device *drm, struct mtk_ddp_comp *comp);
> +
> +#endif /* MTK_DRM_DDP_COMP_H */
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
> new file mode 100644
> index 0000000..e6e5654
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
> @@ -0,0 +1,558 @@
> +/*
> + * Copyright (c) 2015 MediaTek Inc.
> + * Author: YT SHEN <yt.shen@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 <drm/drmP.h>
> +#include <drm/drm_atomic.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_gem.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include <linux/component.h>
> +#include <linux/iommu.h>
> +#include <linux/of_address.h>
> +#include <linux/of_platform.h>
> +#include <linux/pm_runtime.h>
> +
> +#include "mtk_drm_crtc.h"
> +#include "mtk_drm_ddp.h"
> +#include "mtk_drm_ddp_comp.h"
> +#include "mtk_drm_drv.h"
> +#include "mtk_drm_fb.h"
> +#include "mtk_drm_gem.h"
> +
> +#define DRIVER_NAME "mediatek"
> +#define DRIVER_DESC "Mediatek SoC DRM"
> +#define DRIVER_DATE "20150513"
> +#define DRIVER_MAJOR 1
> +#define DRIVER_MINOR 0
> +
> +static void mtk_atomic_schedule(struct mtk_drm_private *private,
> +				struct drm_atomic_state *state)
> +{
> +	private->commit.state = state;
> +	schedule_work(&private->commit.work);
> +}
> +
> +static void mtk_atomic_wait_for_fences(struct drm_atomic_state *state)
> +{
> +	struct drm_plane *plane;
> +	struct drm_plane_state *plane_state;
> +	int i;
> +
> +	for_each_plane_in_state(state, plane, plane_state, i)
> +		mtk_fb_wait(plane->state->fb);
> +}
> +
> +static void mtk_atomic_complete(struct mtk_drm_private *private,
> +				struct drm_atomic_state *state)
> +{
> +	struct drm_device *drm = private->drm;
> +
> +	mtk_atomic_wait_for_fences(state);
> +
> +	drm_atomic_helper_commit_modeset_disables(drm, state);
> +	drm_atomic_helper_commit_planes(drm, state, false);
> +	drm_atomic_helper_commit_modeset_enables(drm, state);
> +	drm_atomic_helper_wait_for_vblanks(drm, state);
> +	drm_atomic_helper_cleanup_planes(drm, state);
> +	drm_atomic_state_free(state);
> +}
> +
> +static void mtk_atomic_work(struct work_struct *work)
> +{
> +	struct mtk_drm_private *private = container_of(work,
> +			struct mtk_drm_private, commit.work);
> +
> +	mtk_atomic_complete(private, private->commit.state);
> +}
> +
> +static int mtk_atomic_commit(struct drm_device *drm,
> +			     struct drm_atomic_state *state,
> +			     bool async)
> +{
> +	struct mtk_drm_private *private = drm->dev_private;
> +	int ret;
> +
> +	ret = drm_atomic_helper_prepare_planes(drm, state);
> +	if (ret)
> +		return ret;
> +
> +	mutex_lock(&private->commit.lock);
> +	flush_work(&private->commit.work);
> +
> +	drm_atomic_helper_swap_state(drm, state);
> +
> +	if (async)
> +		mtk_atomic_schedule(private, state);
> +	else
> +		mtk_atomic_complete(private, state);
> +
> +	mutex_unlock(&private->commit.lock);
> +
> +	return 0;
> +}
> +
> +static const struct drm_mode_config_funcs mtk_drm_mode_config_funcs = {
> +	.fb_create = mtk_drm_mode_fb_create,
> +	.atomic_check = drm_atomic_helper_check,
> +	.atomic_commit = mtk_atomic_commit,
> +};
> +
> +static const enum mtk_ddp_comp_id mtk_ddp_main[] = {
> +	DDP_COMPONENT_OVL0,
> +	DDP_COMPONENT_COLOR0,
> +	DDP_COMPONENT_AAL,
> +	DDP_COMPONENT_OD,
> +	DDP_COMPONENT_RDMA0,
> +	DDP_COMPONENT_UFOE,
> +	DDP_COMPONENT_DSI0,
> +	DDP_COMPONENT_PWM0,
> +};
> +
> +static const enum mtk_ddp_comp_id mtk_ddp_ext[] = {
> +	DDP_COMPONENT_OVL1,
> +	DDP_COMPONENT_COLOR1,
> +	DDP_COMPONENT_GAMMA,
> +	DDP_COMPONENT_RDMA1,
> +	DDP_COMPONENT_DPI0,
> +};
> +
> +static int mtk_drm_kms_init(struct drm_device *drm)
> +{
> +	struct mtk_drm_private *private = drm->dev_private;
> +	struct platform_device *pdev;
> +	struct device_node *np;
> +	int ret;
> +
> +	if (!iommu_present(&platform_bus_type))
> +		return -EPROBE_DEFER;
> +
> +	pdev = of_find_device_by_node(private->mutex_node);
> +	if (!pdev) {
> +		dev_err(drm->dev, "Waiting for disp-mutex device %s\n",
> +			private->mutex_node->full_name);
> +		of_node_put(private->mutex_node);
> +		return -EPROBE_DEFER;
> +	}
> +	private->mutex_dev = &pdev->dev;
> +
> +	drm_mode_config_init(drm);
> +
> +	drm->mode_config.min_width = 64;
> +	drm->mode_config.min_height = 64;
> +
> +	/*
> +	 * set max width and height as default value(4096x4096).
> +	 * this value would be used to check framebuffer size limitation
> +	 * at drm_mode_addfb().
> +	 */
> +	drm->mode_config.max_width = 4096;
> +	drm->mode_config.max_height = 4096;
> +	drm->mode_config.funcs = &mtk_drm_mode_config_funcs;
> +
> +	ret = component_bind_all(drm->dev, drm);
> +	if (ret)
> +		goto err_config_cleanup;
> +
> +	/*
> +	 * We currently support two fixed data streams, each optional,
> +	 * and each statically assigned to a crtc:
> +	 * OVL0 -> COLOR0 -> AAL -> OD -> RDMA0 -> UFOE -> DSI0 ...
> +	 */
> +	ret = mtk_drm_crtc_create(drm, mtk_ddp_main, ARRAY_SIZE(mtk_ddp_main));
> +	if (ret < 0)
> +		goto err_component_unbind;
> +	/* ... and OVL1 -> COLOR1 -> GAMMA -> RDMA1 -> DPI0. */
> +	ret = mtk_drm_crtc_create(drm, mtk_ddp_ext, ARRAY_SIZE(mtk_ddp_ext));
> +	if (ret < 0)
> +		goto err_component_unbind;
> +
> +	/* Use OVL device for all DMA memory allocations */
> +	np = private->comp_node[mtk_ddp_main[0]] ?:
> +	     private->comp_node[mtk_ddp_ext[0]];
> +	pdev = of_find_device_by_node(np);
> +	if (!pdev) {
> +		ret = -ENODEV;
> +		dev_err(drm->dev, "Need at least one OVL device\n");
> +		goto err_component_unbind;
> +	}
> +
> +	private->dma_dev = &pdev->dev;
> +
> +	/*
> +	 * We don't use the drm_irq_install() helpers provided by the DRM
> +	 * core, so we need to set this manually in order to allow the
> +	 * DRM_IOCTL_WAIT_VBLANK to operate correctly.
> +	 */
> +	drm->irq_enabled = true;
> +	ret = drm_vblank_init(drm, MAX_CRTC);
> +	if (ret < 0)
> +		goto err_component_unbind;
> +
> +	drm_kms_helper_poll_init(drm);
> +	drm_mode_config_reset(drm);
> +
> +	return 0;
> +
> +err_component_unbind:
> +	component_unbind_all(drm->dev, drm);
> +err_config_cleanup:
> +	drm_mode_config_cleanup(drm);
> +
> +	return ret;
> +}
> +
> +static void mtk_drm_kms_deinit(struct drm_device *drm)
> +{
> +	drm_kms_helper_poll_fini(drm);
> +
> +	drm_vblank_cleanup(drm);
> +	component_unbind_all(drm->dev, drm);
> +	drm_mode_config_cleanup(drm);
> +}
> +
> +static const struct file_operations mtk_drm_fops = {
> +	.owner = THIS_MODULE,
> +	.open = drm_open,
> +	.release = drm_release,
> +	.unlocked_ioctl = drm_ioctl,
> +	.mmap = mtk_drm_gem_mmap,
> +	.poll = drm_poll,
> +	.read = drm_read,
> +#ifdef CONFIG_COMPAT
> +	.compat_ioctl = drm_compat_ioctl,
> +#endif
> +};
> +
> +static struct drm_driver mtk_drm_driver = {
> +	.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME |
> +			   DRIVER_ATOMIC,
> +
> +	.get_vblank_counter = drm_vblank_count,
> +	.enable_vblank = mtk_drm_crtc_enable_vblank,
> +	.disable_vblank = mtk_drm_crtc_disable_vblank,
> +
> +	.gem_free_object = mtk_drm_gem_free_object,

Please use gem_free_object_unlocked, you're driver is struct mutex free.

> +	.gem_vm_ops = &drm_gem_cma_vm_ops,
> +	.dumb_create = mtk_drm_gem_dumb_create,
> +	.dumb_map_offset = mtk_drm_gem_dumb_map_offset,
> +	.dumb_destroy = drm_gem_dumb_destroy,
> +
> +	.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
> +	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
> +	.gem_prime_export = drm_gem_prime_export,
> +	.gem_prime_import = drm_gem_prime_import,
> +	.gem_prime_get_sg_table = mtk_gem_prime_get_sg_table,
> +	.gem_prime_import_sg_table = mtk_gem_prime_import_sg_table,
> +	.gem_prime_mmap = mtk_drm_gem_mmap_buf,
> +	.fops = &mtk_drm_fops,
> +
> +	.name = DRIVER_NAME,
> +	.desc = DRIVER_DESC,
> +	.date = DRIVER_DATE,
> +	.major = DRIVER_MAJOR,
> +	.minor = DRIVER_MINOR,
> +};
> +
> +static int compare_of(struct device *dev, void *data)
> +{
> +	return dev->of_node == data;
> +}
> +
> +static int mtk_drm_bind(struct device *dev)
> +{
> +	struct mtk_drm_private *private = dev_get_drvdata(dev);
> +	struct drm_device *drm;
> +	int ret;
> +
> +	drm = drm_dev_alloc(&mtk_drm_driver, dev);
> +	if (!drm)
> +		return -ENOMEM;
> +
> +	drm_dev_set_unique(drm, dev_name(dev));
> +
> +	ret = drm_dev_register(drm, 0);
> +	if (ret < 0)
> +		goto err_free;
> +
> +	drm->dev_private = private;
> +	private->drm = drm;
> +
> +	ret = mtk_drm_kms_init(drm);
> +	if (ret < 0)
> +		goto err_unregister;

Wrong way round, drm_dev_register should be last. And you're missing
drm_connector_register_all here.
-Daniel

> +
> +	return 0;
> +
> +err_unregister:
> +	drm_dev_unregister(drm);
> +err_free:
> +	drm_dev_unref(drm);
> +	return ret;
> +}
> +
> +static void mtk_drm_unbind(struct device *dev)
> +{
> +	struct mtk_drm_private *private = dev_get_drvdata(dev);
> +
> +	drm_put_dev(private->drm);
> +	private->drm = NULL;
> +}
> +
> +static const struct component_master_ops mtk_drm_ops = {
> +	.bind		= mtk_drm_bind,
> +	.unbind		= mtk_drm_unbind,
> +};
> +
> +static const struct of_device_id mtk_ddp_comp_dt_ids[] = {
> +	{ .compatible = "mediatek,mt8173-disp-ovl",   .data = (void *)MTK_DISP_OVL },
> +	{ .compatible = "mediatek,mt8173-disp-rdma",  .data = (void *)MTK_DISP_RDMA },
> +	{ .compatible = "mediatek,mt8173-disp-wdma",  .data = (void *)MTK_DISP_WDMA },
> +	{ .compatible = "mediatek,mt8173-disp-color", .data = (void *)MTK_DISP_COLOR },
> +	{ .compatible = "mediatek,mt8173-disp-aal",   .data = (void *)MTK_DISP_AAL},
> +	{ .compatible = "mediatek,mt8173-disp-gamma", .data = (void *)MTK_DISP_GAMMA, },
> +	{ .compatible = "mediatek,mt8173-disp-ufoe",  .data = (void *)MTK_DISP_UFOE },
> +	{ .compatible = "mediatek,mt8173-dsi",        .data = (void *)MTK_DSI },
> +	{ .compatible = "mediatek,mt8173-dpi",        .data = (void *)MTK_DPI },
> +	{ .compatible = "mediatek,mt8173-disp-mutex", .data = (void *)MTK_DISP_MUTEX },
> +	{ .compatible = "mediatek,mt8173-disp-pwm",   .data = (void *)MTK_DISP_PWM },
> +	{ .compatible = "mediatek,mt8173-disp-od",    .data = (void *)MTK_DISP_OD },
> +	{ }
> +};
> +
> +static int mtk_drm_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct mtk_drm_private *private;
> +	struct resource *mem;
> +	struct device_node *node;
> +	struct component_match *match = NULL;
> +	int ret;
> +	int i;
> +
> +	private = devm_kzalloc(dev, sizeof(*private), GFP_KERNEL);
> +	if (!private)
> +		return -ENOMEM;
> +
> +	mutex_init(&private->commit.lock);
> +	INIT_WORK(&private->commit.work, mtk_atomic_work);
> +
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	private->config_regs = devm_ioremap_resource(dev, mem);
> +	if (IS_ERR(private->config_regs)) {
> +		ret = PTR_ERR(private->config_regs);
> +		dev_err(dev, "Failed to ioremap mmsys-config resource: %d\n",
> +			ret);
> +		return ret;
> +	}
> +
> +	/* Iterate over sibling DISP function blocks */
> +	for_each_child_of_node(dev->of_node->parent, node) {
> +		const struct of_device_id *of_id;
> +		enum mtk_ddp_comp_type comp_type;
> +		int comp_id;
> +
> +		of_id = of_match_node(mtk_ddp_comp_dt_ids, node);
> +		if (!of_id)
> +			continue;
> +
> +		if (!of_device_is_available(node)) {
> +			dev_dbg(dev, "Skipping disabled component %s\n",
> +				node->full_name);
> +			continue;
> +		}
> +
> +		comp_type = (enum mtk_ddp_comp_type)of_id->data;
> +
> +		if (comp_type == MTK_DISP_MUTEX) {
> +			private->mutex_node = of_node_get(node);
> +			continue;
> +		}
> +
> +		comp_id = mtk_ddp_comp_get_id(node, comp_type);
> +		if (comp_id < 0) {
> +			dev_warn(dev, "Skipping unknown component %s\n",
> +				 node->full_name);
> +			continue;
> +		}
> +
> +		private->comp_node[comp_id] = of_node_get(node);
> +
> +		/*
> +		 * Currently only the OVL, RDMA, DSI, and DPI blocks have
> +		 * separate component platform drivers and initialize their own
> +		 * DDP component structure. The others are initialized here.
> +		 */
> +		if (comp_type == MTK_DISP_OVL ||
> +		    comp_type == MTK_DISP_RDMA ||
> +		    comp_type == MTK_DSI ||
> +		    comp_type == MTK_DPI) {
> +			dev_info(dev, "Adding component match for %s\n",
> +				 node->full_name);
> +			component_match_add(dev, &match, compare_of, node);
> +		} else {
> +			struct mtk_ddp_comp *comp;
> +
> +			comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL);
> +			if (!comp) {
> +				ret = -ENOMEM;
> +				goto err_node;
> +			}
> +
> +			ret = mtk_ddp_comp_init(dev, node, comp, comp_id, NULL);
> +			if (ret)
> +				goto err_node;
> +
> +			private->ddp_comp[comp_id] = comp;
> +		}
> +	}
> +
> +	if (!private->mutex_node) {
> +		dev_err(dev, "Failed to find disp-mutex node\n");
> +		ret = -ENODEV;
> +		goto err_node;
> +	}
> +
> +	pm_runtime_enable(dev);
> +
> +	platform_set_drvdata(pdev, private);
> +
> +	ret = component_master_add_with_match(dev, &mtk_drm_ops, match);
> +	if (ret)
> +		goto err_pm;
> +
> +	return 0;
> +
> +err_pm:
> +	pm_runtime_disable(dev);
> +err_node:
> +	of_node_put(private->mutex_node);
> +	for (i = 0; i < DDP_COMPONENT_ID_MAX; i++)
> +		of_node_put(private->comp_node[i]);
> +	return ret;
> +}
> +
> +static int mtk_drm_remove(struct platform_device *pdev)
> +{
> +	struct mtk_drm_private *private = platform_get_drvdata(pdev);
> +	struct drm_device *drm = private->drm;
> +	int i;
> +
> +	drm_connector_unregister_all(drm);
> +	drm_dev_unregister(drm);
> +	mtk_drm_kms_deinit(drm);
> +	drm_dev_unref(drm);
> +
> +	component_master_del(&pdev->dev, &mtk_drm_ops);
> +	pm_runtime_disable(&pdev->dev);
> +	of_node_put(private->mutex_node);
> +	for (i = 0; i < DDP_COMPONENT_ID_MAX; i++)
> +		of_node_put(private->comp_node[i]);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int mtk_drm_sys_suspend(struct device *dev)
> +{
> +	struct mtk_drm_private *private = dev_get_drvdata(dev);
> +	struct drm_device *drm = private->drm;
> +
> +	drm_kms_helper_poll_disable(drm);
> +
> +	private->suspend_state = drm_atomic_helper_suspend(drm);
> +	if (IS_ERR(private->suspend_state)) {
> +		drm_kms_helper_poll_enable(drm);
> +		return PTR_ERR(private->suspend_state);
> +	}
> +
> +	DRM_DEBUG_DRIVER("mtk_drm_sys_suspend\n");
> +	return 0;
> +}
> +
> +static int mtk_drm_sys_resume(struct device *dev)
> +{
> +	struct mtk_drm_private *private = dev_get_drvdata(dev);
> +	struct drm_device *drm = private->drm;
> +
> +	drm_atomic_helper_resume(drm, private->suspend_state);
> +	drm_kms_helper_poll_enable(drm);
> +
> +	DRM_DEBUG_DRIVER("mtk_drm_sys_resume\n");
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(mtk_drm_pm_ops, mtk_drm_sys_suspend,
> +			 mtk_drm_sys_resume);
> +
> +static const struct of_device_id mtk_drm_of_ids[] = {
> +	{ .compatible = "mediatek,mt8173-mmsys", },
> +	{ }
> +};
> +
> +static struct platform_driver mtk_drm_platform_driver = {
> +	.probe	= mtk_drm_probe,
> +	.remove	= mtk_drm_remove,
> +	.driver	= {
> +		.name	= "mediatek-drm",
> +		.of_match_table = mtk_drm_of_ids,
> +		.pm     = &mtk_drm_pm_ops,
> +	},
> +};
> +
> +static struct platform_driver * const mtk_drm_drivers[] = {
> +	&mtk_ddp_driver,
> +	&mtk_disp_ovl_driver,
> +	&mtk_disp_rdma_driver,
> +	&mtk_drm_platform_driver,
> +};
> +
> +static int __init mtk_drm_init(void)
> +{
> +	int ret;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(mtk_drm_drivers); i++) {
> +		ret = platform_driver_register(mtk_drm_drivers[i]);
> +		if (ret < 0) {
> +			pr_err("Failed to register %s driver: %d\n",
> +			       mtk_drm_drivers[i]->driver.name, ret);
> +			goto err;
> +		}
> +	}
> +
> +	return 0;
> +
> +err:
> +	while (--i >= 0)
> +		platform_driver_unregister(mtk_drm_drivers[i]);
> +
> +	return ret;
> +}
> +
> +static void __exit mtk_drm_exit(void)
> +{
> +	int i;
> +
> +	for (i = ARRAY_SIZE(mtk_drm_drivers) - 1; i >= 0; i--)
> +		platform_driver_unregister(mtk_drm_drivers[i]);
> +}
> +
> +module_init(mtk_drm_init);
> +module_exit(mtk_drm_exit);
> +
> +MODULE_AUTHOR("YT SHEN <yt.shen@mediatek.com>");
> +MODULE_DESCRIPTION("Mediatek SoC DRM driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.h b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
> new file mode 100644
> index 0000000..27dc8fa
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
> @@ -0,0 +1,57 @@
> +/*
> + * Copyright (c) 2015 MediaTek Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef MTK_DRM_DRV_H
> +#define MTK_DRM_DRV_H
> +
> +#include <linux/io.h>
> +#include "mtk_drm_ddp_comp.h"
> +
> +#define MAX_CRTC	2
> +#define MAX_CONNECTOR	2
> +
> +struct device;
> +struct device_node;
> +struct drm_crtc;
> +struct drm_device;
> +struct drm_fb_helper;
> +struct drm_property;
> +struct regmap;
> +
> +struct mtk_drm_private {
> +	struct drm_device *drm;
> +	struct device *dma_dev;
> +
> +	struct drm_crtc *crtc[MAX_CRTC];
> +	unsigned int num_pipes;
> +
> +	struct device_node *mutex_node;
> +	struct device *mutex_dev;
> +	void __iomem *config_regs;
> +	struct device_node *comp_node[DDP_COMPONENT_ID_MAX];
> +	struct mtk_ddp_comp *ddp_comp[DDP_COMPONENT_ID_MAX];
> +
> +	struct {
> +		struct drm_atomic_state *state;
> +		struct work_struct work;
> +		struct mutex lock;
> +	} commit;
> +
> +	struct drm_atomic_state *suspend_state;
> +};
> +
> +extern struct platform_driver mtk_ddp_driver;
> +extern struct platform_driver mtk_disp_ovl_driver;
> +extern struct platform_driver mtk_disp_rdma_driver;
> +
> +#endif /* MTK_DRM_DRV_H */
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_fb.c b/drivers/gpu/drm/mediatek/mtk_drm_fb.c
> new file mode 100644
> index 0000000..33d30c1
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_fb.c
> @@ -0,0 +1,165 @@
> +/*
> + * Copyright (c) 2015 MediaTek Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_fb_helper.h>
> +#include <drm/drm_gem.h>
> +#include <linux/dma-buf.h>
> +#include <linux/reservation.h>
> +
> +#include "mtk_drm_drv.h"
> +#include "mtk_drm_fb.h"
> +#include "mtk_drm_gem.h"
> +
> +/*
> + * mtk specific framebuffer structure.
> + *
> + * @fb: drm framebuffer object.
> + * @gem_obj: array of gem objects.
> + */
> +struct mtk_drm_fb {
> +	struct drm_framebuffer	base;
> +	/* For now we only support a single plane */
> +	struct drm_gem_object	*gem_obj;
> +};
> +
> +#define to_mtk_fb(x) container_of(x, struct mtk_drm_fb, base)
> +
> +struct drm_gem_object *mtk_fb_get_gem_obj(struct drm_framebuffer *fb)
> +{
> +	struct mtk_drm_fb *mtk_fb = to_mtk_fb(fb);
> +
> +	return mtk_fb->gem_obj;
> +}
> +
> +static int mtk_drm_fb_create_handle(struct drm_framebuffer *fb,
> +				    struct drm_file *file_priv,
> +				    unsigned int *handle)
> +{
> +	struct mtk_drm_fb *mtk_fb = to_mtk_fb(fb);
> +
> +	return drm_gem_handle_create(file_priv, mtk_fb->gem_obj, handle);
> +}
> +
> +static void mtk_drm_fb_destroy(struct drm_framebuffer *fb)
> +{
> +	struct mtk_drm_fb *mtk_fb = to_mtk_fb(fb);
> +
> +	drm_framebuffer_cleanup(fb);
> +
> +	drm_gem_object_unreference_unlocked(mtk_fb->gem_obj);
> +
> +	kfree(mtk_fb);
> +}
> +
> +static const struct drm_framebuffer_funcs mtk_drm_fb_funcs = {
> +	.create_handle = mtk_drm_fb_create_handle,
> +	.destroy = mtk_drm_fb_destroy,
> +};
> +
> +static struct mtk_drm_fb *mtk_drm_framebuffer_init(struct drm_device *dev,
> +					const struct drm_mode_fb_cmd2 *mode,
> +					struct drm_gem_object *obj)
> +{
> +	struct mtk_drm_fb *mtk_fb;
> +	int ret;
> +
> +	if (drm_format_num_planes(mode->pixel_format) != 1)
> +		return ERR_PTR(-EINVAL);
> +
> +	mtk_fb = kzalloc(sizeof(*mtk_fb), GFP_KERNEL);
> +	if (!mtk_fb)
> +		return ERR_PTR(-ENOMEM);
> +
> +	drm_helper_mode_fill_fb_struct(&mtk_fb->base, mode);
> +
> +	mtk_fb->gem_obj = obj;
> +
> +	ret = drm_framebuffer_init(dev, &mtk_fb->base, &mtk_drm_fb_funcs);
> +	if (ret) {
> +		DRM_ERROR("failed to initialize framebuffer\n");
> +		kfree(mtk_fb);
> +		return ERR_PTR(ret);
> +	}
> +
> +	return mtk_fb;
> +}
> +
> +/*
> + * Wait for any exclusive fence in fb's gem object's reservation object.
> + *
> + * Returns -ERESTARTSYS if interrupted, else 0.
> + */
> +int mtk_fb_wait(struct drm_framebuffer *fb)
> +{
> +	struct drm_gem_object *gem;
> +	struct reservation_object *resv;
> +	long ret;
> +
> +	if (!fb)
> +		return 0;
> +
> +	gem = mtk_fb_get_gem_obj(fb);
> +	if (!gem || !gem->dma_buf || !gem->dma_buf->resv)
> +		return 0;
> +
> +	resv = gem->dma_buf->resv;
> +	ret = reservation_object_wait_timeout_rcu(resv, false, true,
> +						  MAX_SCHEDULE_TIMEOUT);
> +	/* MAX_SCHEDULE_TIMEOUT on success, -ERESTARTSYS if interrupted */
> +	if (WARN_ON(ret < 0))
> +		return ret;
> +
> +	return 0;
> +}
> +
> +struct drm_framebuffer *mtk_drm_mode_fb_create(struct drm_device *dev,
> +					       struct drm_file *file,
> +					       const struct drm_mode_fb_cmd2 *cmd)
> +{
> +	struct mtk_drm_fb *mtk_fb;
> +	struct drm_gem_object *gem;
> +	unsigned int width = cmd->width;
> +	unsigned int height = cmd->height;
> +	unsigned int size, bpp;
> +	int ret;
> +
> +	if (drm_format_num_planes(cmd->pixel_format) != 1)
> +		return ERR_PTR(-EINVAL);
> +
> +	gem = drm_gem_object_lookup(dev, file, cmd->handles[0]);
> +	if (!gem)
> +		return ERR_PTR(-ENOENT);
> +
> +	bpp = drm_format_plane_cpp(cmd->pixel_format, 0);
> +	size = (height - 1) * cmd->pitches[0] + width * bpp;
> +	size += cmd->offsets[0];
> +
> +	if (gem->size < size) {
> +		ret = -EINVAL;
> +		goto unreference;
> +	}
> +
> +	mtk_fb = mtk_drm_framebuffer_init(dev, cmd, gem);
> +	if (IS_ERR(mtk_fb)) {
> +		ret = PTR_ERR(mtk_fb);
> +		goto unreference;
> +	}
> +
> +	return &mtk_fb->base;
> +
> +unreference:
> +	drm_gem_object_unreference_unlocked(gem);
> +	return ERR_PTR(ret);
> +}
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_fb.h b/drivers/gpu/drm/mediatek/mtk_drm_fb.h
> new file mode 100644
> index 0000000..9b2ae34
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_fb.h
> @@ -0,0 +1,23 @@
> +/*
> + * Copyright (c) 2015 MediaTek Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef MTK_DRM_FB_H
> +#define MTK_DRM_FB_H
> +
> +struct drm_gem_object *mtk_fb_get_gem_obj(struct drm_framebuffer *fb);
> +int mtk_fb_wait(struct drm_framebuffer *fb);
> +struct drm_framebuffer *mtk_drm_mode_fb_create(struct drm_device *dev,
> +					       struct drm_file *file,
> +					       const struct drm_mode_fb_cmd2 *cmd);
> +
> +#endif /* MTK_DRM_FB_H */
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_gem.c b/drivers/gpu/drm/mediatek/mtk_drm_gem.c
> new file mode 100644
> index 0000000..a773bfa
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_gem.c
> @@ -0,0 +1,269 @@
> +/*
> + * Copyright (c) 2015 MediaTek Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_gem.h>
> +#include <linux/dma-buf.h>
> +
> +#include "mtk_drm_drv.h"
> +#include "mtk_drm_gem.h"
> +
> +static struct mtk_drm_gem_obj *mtk_drm_gem_init(struct drm_device *dev,
> +						unsigned long size)
> +{
> +	struct mtk_drm_gem_obj *mtk_gem_obj;
> +	int ret;
> +
> +	size = round_up(size, PAGE_SIZE);
> +
> +	mtk_gem_obj = kzalloc(sizeof(*mtk_gem_obj), GFP_KERNEL);
> +	if (!mtk_gem_obj)
> +		return ERR_PTR(-ENOMEM);
> +
> +	ret = drm_gem_object_init(dev, &mtk_gem_obj->base, size);
> +	if (ret < 0) {
> +		DRM_ERROR("failed to initialize gem object\n");
> +		kfree(mtk_gem_obj);
> +		return ERR_PTR(ret);
> +	}
> +
> +	return mtk_gem_obj;
> +}
> +
> +struct mtk_drm_gem_obj *mtk_drm_gem_create(struct drm_device *dev,
> +					   size_t size, bool alloc_kmap)
> +{
> +	struct mtk_drm_private *priv = dev->dev_private;
> +	struct mtk_drm_gem_obj *mtk_gem;
> +	struct drm_gem_object *obj;
> +	int ret;
> +
> +	mtk_gem = mtk_drm_gem_init(dev, size);
> +	if (IS_ERR(mtk_gem))
> +		return ERR_CAST(mtk_gem);
> +
> +	obj = &mtk_gem->base;
> +
> +	init_dma_attrs(&mtk_gem->dma_attrs);
> +	dma_set_attr(DMA_ATTR_WRITE_COMBINE, &mtk_gem->dma_attrs);
> +
> +	if (!alloc_kmap)
> +		dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &mtk_gem->dma_attrs);
> +
> +	mtk_gem->cookie = dma_alloc_attrs(priv->dma_dev, obj->size,
> +					  &mtk_gem->dma_addr, GFP_KERNEL,
> +					  &mtk_gem->dma_attrs);
> +	if (!mtk_gem->cookie) {
> +		DRM_ERROR("failed to allocate %zx byte dma buffer", obj->size);
> +		ret = -ENOMEM;
> +		goto err_gem_free;
> +	}
> +
> +	if (alloc_kmap)
> +		mtk_gem->kvaddr = mtk_gem->cookie;
> +
> +	DRM_DEBUG_DRIVER("cookie = %p dma_addr = %pad size = %zu\n",
> +			 mtk_gem->cookie, &mtk_gem->dma_addr,
> +			 size);
> +
> +	return mtk_gem;
> +
> +err_gem_free:
> +	drm_gem_object_release(obj);
> +	kfree(mtk_gem);
> +	return ERR_PTR(ret);
> +}
> +
> +void mtk_drm_gem_free_object(struct drm_gem_object *obj)
> +{
> +	struct mtk_drm_gem_obj *mtk_gem = to_mtk_gem_obj(obj);
> +	struct mtk_drm_private *priv = obj->dev->dev_private;
> +
> +	if (mtk_gem->sg)
> +		drm_prime_gem_destroy(obj, mtk_gem->sg);
> +	else
> +		dma_free_attrs(priv->dma_dev, obj->size, mtk_gem->cookie,
> +			       mtk_gem->dma_addr, &mtk_gem->dma_attrs);
> +
> +	/* release file pointer to gem object. */
> +	drm_gem_object_release(obj);
> +
> +	kfree(mtk_gem);
> +}
> +
> +int mtk_drm_gem_dumb_create(struct drm_file *file_priv, struct drm_device *dev,
> +			    struct drm_mode_create_dumb *args)
> +{
> +	struct mtk_drm_gem_obj *mtk_gem;
> +	int ret;
> +
> +	args->pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
> +	args->size = args->pitch * args->height;
> +
> +	mtk_gem = mtk_drm_gem_create(dev, args->size, false);
> +	if (IS_ERR(mtk_gem))
> +		return PTR_ERR(mtk_gem);
> +
> +	/*
> +	 * allocate a id of idr table where the obj is registered
> +	 * and handle has the id what user can see.
> +	 */
> +	ret = drm_gem_handle_create(file_priv, &mtk_gem->base, &args->handle);
> +	if (ret)
> +		goto err_handle_create;
> +
> +	/* drop reference from allocate - handle holds it now. */
> +	drm_gem_object_unreference_unlocked(&mtk_gem->base);
> +
> +	return 0;
> +
> +err_handle_create:
> +	mtk_drm_gem_free_object(&mtk_gem->base);
> +	return ret;
> +}
> +
> +int mtk_drm_gem_dumb_map_offset(struct drm_file *file_priv,
> +				struct drm_device *dev, uint32_t handle,
> +				uint64_t *offset)
> +{
> +	struct drm_gem_object *obj;
> +	int ret;
> +
> +	obj = drm_gem_object_lookup(dev, file_priv, handle);
> +	if (!obj) {
> +		DRM_ERROR("failed to lookup gem object.\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = drm_gem_create_mmap_offset(obj);
> +	if (ret)
> +		goto out;
> +
> +	*offset = drm_vma_node_offset_addr(&obj->vma_node);
> +	DRM_DEBUG_KMS("offset = 0x%llx\n", *offset);
> +
> +out:
> +	drm_gem_object_unreference_unlocked(obj);
> +	return ret;
> +}
> +
> +static int mtk_drm_gem_object_mmap(struct drm_gem_object *obj,
> +				   struct vm_area_struct *vma)
> +
> +{
> +	int ret;
> +	struct mtk_drm_gem_obj *mtk_gem = to_mtk_gem_obj(obj);
> +	struct mtk_drm_private *priv = obj->dev->dev_private;
> +
> +	/*
> +	 * dma_alloc_attrs() allocated a struct page table for mtk_gem, so clear
> +	 * VM_PFNMAP flag that was set by drm_gem_mmap_obj()/drm_gem_mmap().
> +	 */
> +	vma->vm_flags &= ~VM_PFNMAP;
> +	vma->vm_pgoff = 0;
> +
> +	ret = dma_mmap_attrs(priv->dma_dev, vma, mtk_gem->cookie,
> +			     mtk_gem->dma_addr, obj->size, &mtk_gem->dma_attrs);
> +	if (ret)
> +		drm_gem_vm_close(vma);
> +
> +	return ret;
> +}
> +
> +int mtk_drm_gem_mmap_buf(struct drm_gem_object *obj, struct vm_area_struct *vma)
> +{
> +	int ret;
> +
> +	ret = drm_gem_mmap_obj(obj, obj->size, vma);
> +	if (ret)
> +		return ret;
> +
> +	return mtk_drm_gem_object_mmap(obj, vma);
> +}
> +
> +int mtk_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
> +{
> +	struct drm_gem_object *obj;
> +	int ret;
> +
> +	ret = drm_gem_mmap(filp, vma);
> +	if (ret)
> +		return ret;
> +
> +	obj = vma->vm_private_data;
> +
> +	return mtk_drm_gem_object_mmap(obj, vma);
> +}
> +
> +/*
> + * Allocate a sg_table for this GEM object.
> + * Note: Both the table's contents, and the sg_table itself must be freed by
> + *       the caller.
> + * Returns a pointer to the newly allocated sg_table, or an ERR_PTR() error.
> + */
> +struct sg_table *mtk_gem_prime_get_sg_table(struct drm_gem_object *obj)
> +{
> +	struct mtk_drm_gem_obj *mtk_gem = to_mtk_gem_obj(obj);
> +	struct mtk_drm_private *priv = obj->dev->dev_private;
> +	struct sg_table *sgt;
> +	int ret;
> +
> +	sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
> +	if (!sgt)
> +		return ERR_PTR(-ENOMEM);
> +
> +	ret = dma_get_sgtable_attrs(priv->dma_dev, sgt, mtk_gem->cookie,
> +				    mtk_gem->dma_addr, obj->size,
> +				    &mtk_gem->dma_attrs);
> +	if (ret) {
> +		DRM_ERROR("failed to allocate sgt, %d\n", ret);
> +		kfree(sgt);
> +		return ERR_PTR(ret);
> +	}
> +
> +	return sgt;
> +}
> +
> +struct drm_gem_object *mtk_gem_prime_import_sg_table(struct drm_device *dev,
> +			struct dma_buf_attachment *attach, struct sg_table *sg)
> +{
> +	struct mtk_drm_gem_obj *mtk_gem;
> +	int ret;
> +	struct scatterlist *s;
> +	unsigned int i;
> +	dma_addr_t expected;
> +
> +	mtk_gem = mtk_drm_gem_init(dev, attach->dmabuf->size);
> +
> +	if (IS_ERR(mtk_gem))
> +		return ERR_PTR(PTR_ERR(mtk_gem));
> +
> +	expected = sg_dma_address(sg->sgl);
> +	for_each_sg(sg->sgl, s, sg->nents, i) {
> +		if (sg_dma_address(s) != expected) {
> +			DRM_ERROR("sg_table is not contiguous");
> +			ret = -EINVAL;
> +			goto err_gem_free;
> +		}
> +		expected = sg_dma_address(s) + sg_dma_len(s);
> +	}
> +
> +	mtk_gem->dma_addr = sg_dma_address(sg->sgl);
> +	mtk_gem->sg = sg;
> +
> +	return &mtk_gem->base;
> +
> +err_gem_free:
> +	kfree(mtk_gem);
> +	return ERR_PTR(ret);
> +}
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_gem.h b/drivers/gpu/drm/mediatek/mtk_drm_gem.h
> new file mode 100644
> index 0000000..3a2a562
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_gem.h
> @@ -0,0 +1,59 @@
> +/*
> + * Copyright (c) 2015 MediaTek Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef _MTK_DRM_GEM_H_
> +#define _MTK_DRM_GEM_H_
> +
> +#include <drm/drm_gem.h>
> +
> +/*
> + * mtk drm buffer structure.
> + *
> + * @base: a gem object.
> + *	- a new handle to this gem object would be created
> + *	by drm_gem_handle_create().
> + * @cookie: the return value of dma_alloc_attrs(), keep it for dma_free_attrs()
> + * @kvaddr: kernel virtual address of gem buffer.
> + * @dma_addr: dma address of gem buffer.
> + * @dma_attrs: dma attributes of gem buffer.
> + *
> + * P.S. this object would be transferred to user as kms_bo.handle so
> + *	user can access the buffer through kms_bo.handle.
> + */
> +struct mtk_drm_gem_obj {
> +	struct drm_gem_object	base;
> +	void			*cookie;
> +	void			*kvaddr;
> +	dma_addr_t		dma_addr;
> +	struct dma_attrs	dma_attrs;
> +	struct sg_table		*sg;
> +};
> +
> +#define to_mtk_gem_obj(x)	container_of(x, struct mtk_drm_gem_obj, base)
> +
> +void mtk_drm_gem_free_object(struct drm_gem_object *gem);
> +struct mtk_drm_gem_obj *mtk_drm_gem_create(struct drm_device *dev, size_t size,
> +					   bool alloc_kmap);
> +int mtk_drm_gem_dumb_create(struct drm_file *file_priv, struct drm_device *dev,
> +			    struct drm_mode_create_dumb *args);
> +int mtk_drm_gem_dumb_map_offset(struct drm_file *file_priv,
> +				struct drm_device *dev, uint32_t handle,
> +				uint64_t *offset);
> +int mtk_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
> +int mtk_drm_gem_mmap_buf(struct drm_gem_object *obj,
> +			 struct vm_area_struct *vma);
> +struct sg_table *mtk_gem_prime_get_sg_table(struct drm_gem_object *obj);
> +struct drm_gem_object *mtk_gem_prime_import_sg_table(struct drm_device *dev,
> +			struct dma_buf_attachment *attach, struct sg_table *sg);
> +
> +#endif
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_plane.c b/drivers/gpu/drm/mediatek/mtk_drm_plane.c
> new file mode 100644
> index 0000000..c898788
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_plane.c
> @@ -0,0 +1,240 @@
> +/*
> + * Copyright (c) 2015 MediaTek Inc.
> + * Author: CK Hu <ck.hu@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 <drm/drmP.h>
> +#include <drm/drm_atomic.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_plane_helper.h>
> +
> +#include "mtk_drm_crtc.h"
> +#include "mtk_drm_ddp_comp.h"
> +#include "mtk_drm_drv.h"
> +#include "mtk_drm_fb.h"
> +#include "mtk_drm_gem.h"
> +#include "mtk_drm_plane.h"
> +
> +static const u32 formats[] = {
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_RGB565,
> +};
> +
> +static void mtk_plane_enable(struct mtk_drm_plane *mtk_plane, bool enable,
> +			     dma_addr_t addr, struct drm_rect *dest)
> +{
> +	struct drm_plane *plane = &mtk_plane->base;
> +	struct mtk_plane_state *state = to_mtk_plane_state(plane->state);
> +	unsigned int pitch, format;
> +	int x, y;
> +
> +	if (WARN_ON(!plane->state || (enable && !plane->state->fb)))
> +		return;
> +
> +	if (plane->state->fb) {
> +		pitch = plane->state->fb->pitches[0];
> +		format = plane->state->fb->pixel_format;
> +	} else {
> +		pitch = 0;
> +		format = DRM_FORMAT_RGBA8888;
> +	}
> +
> +	x = plane->state->crtc_x;
> +	y = plane->state->crtc_y;
> +
> +	if (x < 0) {
> +		addr -= x * 4;
> +		x = 0;
> +	}
> +
> +	if (y < 0) {
> +		addr -= y * pitch;
> +		y = 0;
> +	}
> +
> +	state->pending.enable = enable;
> +	state->pending.pitch = pitch;
> +	state->pending.format = format;
> +	state->pending.addr = addr;
> +	state->pending.x = x;
> +	state->pending.y = y;
> +	state->pending.width = dest->x2 - dest->x1;
> +	state->pending.height = dest->y2 - dest->y1;
> +	wmb(); /* Make sure the above parameters are set before update */
> +	state->pending.dirty = true;
> +}
> +
> +static void mtk_plane_reset(struct drm_plane *plane)
> +{
> +	struct mtk_plane_state *state;
> +
> +	if (plane->state) {
> +		if (plane->state->fb)
> +			drm_framebuffer_unreference(plane->state->fb);
> +
> +		state = to_mtk_plane_state(plane->state);
> +		memset(state, 0, sizeof(*state));
> +	} else {
> +		state = kzalloc(sizeof(*state), GFP_KERNEL);
> +		if (!state)
> +			return;
> +		plane->state = &state->base;
> +	}
> +
> +	state->base.plane = plane;
> +	state->pending.format = DRM_FORMAT_RGB565;
> +}
> +
> +static struct drm_plane_state *mtk_plane_duplicate_state(struct drm_plane *plane)
> +{
> +	struct mtk_plane_state *old_state = to_mtk_plane_state(plane->state);
> +	struct mtk_plane_state *state;
> +
> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
> +	if (!state)
> +		return NULL;
> +
> +	__drm_atomic_helper_plane_duplicate_state(plane, &state->base);
> +
> +	WARN_ON(state->base.plane != plane);
> +
> +	state->pending = old_state->pending;
> +
> +	return &state->base;
> +}
> +
> +static void mtk_drm_plane_destroy_state(struct drm_plane *plane,
> +					struct drm_plane_state *state)
> +{
> +	__drm_atomic_helper_plane_destroy_state(plane, state);
> +	kfree(to_mtk_plane_state(state));
> +}
> +
> +static const struct drm_plane_funcs mtk_plane_funcs = {
> +	.update_plane = drm_atomic_helper_update_plane,
> +	.disable_plane = drm_atomic_helper_disable_plane,
> +	.destroy = drm_plane_cleanup,
> +	.reset = mtk_plane_reset,
> +	.atomic_duplicate_state = mtk_plane_duplicate_state,
> +	.atomic_destroy_state = mtk_drm_plane_destroy_state,
> +};
> +
> +static int mtk_plane_atomic_check(struct drm_plane *plane,
> +				  struct drm_plane_state *state)
> +{
> +	struct drm_framebuffer *fb = state->fb;
> +	struct drm_crtc_state *crtc_state;
> +	bool visible;
> +	struct drm_rect dest = {
> +		.x1 = state->crtc_x,
> +		.y1 = state->crtc_y,
> +		.x2 = state->crtc_x + state->crtc_w,
> +		.y2 = state->crtc_y + state->crtc_h,
> +	};
> +	struct drm_rect src = {
> +		/* 16.16 fixed point */
> +		.x1 = state->src_x,
> +		.y1 = state->src_y,
> +		.x2 = state->src_x + state->src_w,
> +		.y2 = state->src_y + state->src_h,
> +	};
> +	struct drm_rect clip = { 0, };
> +
> +	if (!fb)
> +		return 0;
> +
> +	if (!mtk_fb_get_gem_obj(fb)) {
> +		DRM_DEBUG_KMS("buffer is null\n");
> +		return -EFAULT;
> +	}
> +
> +	if (!state->crtc)
> +		return 0;
> +
> +	crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc);
> +	if (IS_ERR(crtc_state))
> +		return PTR_ERR(crtc_state);
> +
> +	clip.x2 = crtc_state->mode.hdisplay;
> +	clip.y2 = crtc_state->mode.vdisplay;
> +
> +	return drm_plane_helper_check_update(plane, state->crtc, fb,
> +					     &src, &dest, &clip,
> +					     DRM_PLANE_HELPER_NO_SCALING,
> +					     DRM_PLANE_HELPER_NO_SCALING,
> +					     true, true, &visible);
> +}
> +
> +static void mtk_plane_atomic_update(struct drm_plane *plane,
> +				    struct drm_plane_state *old_state)
> +{
> +	struct mtk_plane_state *state = to_mtk_plane_state(plane->state);
> +	struct drm_crtc *crtc = state->base.crtc;
> +	struct drm_gem_object *gem;
> +	struct mtk_drm_gem_obj *mtk_gem;
> +	struct mtk_drm_plane *mtk_plane = to_mtk_plane(plane);
> +	struct drm_rect dest = {
> +		.x1 = state->base.crtc_x,
> +		.y1 = state->base.crtc_y,
> +		.x2 = state->base.crtc_x + state->base.crtc_w,
> +		.y2 = state->base.crtc_y + state->base.crtc_h,
> +	};
> +	struct drm_rect clip = { 0, };
> +
> +	if (!crtc)
> +		return;
> +
> +	clip.x2 = state->base.crtc->state->mode.hdisplay;
> +	clip.y2 = state->base.crtc->state->mode.vdisplay;
> +	drm_rect_intersect(&dest, &clip);
> +
> +	gem = mtk_fb_get_gem_obj(state->base.fb);
> +	mtk_gem = to_mtk_gem_obj(gem);
> +	mtk_plane_enable(mtk_plane, true, mtk_gem->dma_addr, &dest);
> +}
> +
> +static void mtk_plane_atomic_disable(struct drm_plane *plane,
> +				     struct drm_plane_state *old_state)
> +{
> +	struct mtk_plane_state *state = to_mtk_plane_state(plane->state);
> +
> +	state->pending.enable = false;
> +	wmb(); /* Make sure the above parameter is set before update */
> +	state->pending.dirty = true;
> +}
> +
> +static const struct drm_plane_helper_funcs mtk_plane_helper_funcs = {
> +	.atomic_check = mtk_plane_atomic_check,
> +	.atomic_update = mtk_plane_atomic_update,
> +	.atomic_disable = mtk_plane_atomic_disable,
> +};
> +
> +int mtk_plane_init(struct drm_device *dev, struct mtk_drm_plane *mtk_plane,
> +		   unsigned long possible_crtcs, enum drm_plane_type type,
> +		   unsigned int zpos)
> +{
> +	int err;
> +
> +	err = drm_universal_plane_init(dev, &mtk_plane->base, possible_crtcs,
> +				       &mtk_plane_funcs, formats,
> +				       ARRAY_SIZE(formats), type, NULL);
> +	if (err) {
> +		DRM_ERROR("failed to initialize plane\n");
> +		return err;
> +	}
> +
> +	drm_plane_helper_add(&mtk_plane->base, &mtk_plane_helper_funcs);
> +	mtk_plane->idx = zpos;
> +
> +	return 0;
> +}
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_plane.h b/drivers/gpu/drm/mediatek/mtk_drm_plane.h
> new file mode 100644
> index 0000000..72a7b3e
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_plane.h
> @@ -0,0 +1,59 @@
> +/*
> + * Copyright (c) 2015 MediaTek Inc.
> + * Author: CK Hu <ck.hu@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_DRM_PLANE_H_
> +#define _MTK_DRM_PLANE_H_
> +
> +#include <drm/drm_crtc.h>
> +#include <linux/types.h>
> +
> +struct mtk_drm_plane {
> +	struct drm_plane		base;
> +	unsigned int			idx;
> +};
> +
> +struct mtk_plane_pending_state {
> +	bool				config;
> +	bool				enable;
> +	dma_addr_t			addr;
> +	unsigned int			pitch;
> +	unsigned int			format;
> +	unsigned int			x;
> +	unsigned int			y;
> +	unsigned int			width;
> +	unsigned int			height;
> +	bool				dirty;
> +};
> +
> +struct mtk_plane_state {
> +	struct drm_plane_state		base;
> +	struct mtk_plane_pending_state	pending;
> +};
> +
> +static inline struct mtk_drm_plane *to_mtk_plane(struct drm_plane *plane)
> +{
> +	return container_of(plane, struct mtk_drm_plane, base);
> +}
> +
> +static inline struct mtk_plane_state *
> +to_mtk_plane_state(struct drm_plane_state *state)
> +{
> +	return container_of(state, struct mtk_plane_state, base);
> +}
> +
> +int mtk_plane_init(struct drm_device *dev, struct mtk_drm_plane *mtk_plane,
> +		   unsigned long possible_crtcs, enum drm_plane_type type,
> +		   unsigned int zpos);
> +
> +#endif
> -- 
> 2.8.0.rc3
> 

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH v16 3/8] drm/mediatek: Add DRM Driver for Mediatek SoC MT8173.
  2016-05-06 13:16   ` Daniel Vetter
@ 2016-05-06 14:40     ` Philipp Zabel
  0 siblings, 0 replies; 11+ messages in thread
From: Philipp Zabel @ 2016-05-06 14:40 UTC (permalink / raw)
  To: Daniel Vetter
  Cc: Mark Rutland, Stephen Boyd, Michael Turquette, dri-devel,
	Jie Qiu, Cawa Cheng, Mao Huang, YT Shen, Yingjoe Chen,
	devicetree, Jitao Shi, Pawel Moll, Ian Campbell, Rob Herring,
	linux-mediatek, Matthias Brugger, Paul Bolle, Emil Velikov,
	Tomasz Figa, kernel, Kumar Gala

Hi Daniel,

Am Freitag, den 06.05.2016, 15:16 +0200 schrieb Daniel Vetter:
> On Fri, May 06, 2016 at 03:05:49PM +0200, Philipp Zabel wrote:
> > From: CK Hu <ck.hu@mediatek.com>
> > 
> > This patch adds an initial DRM driver for the Mediatek MT8173 DISP
> > subsystem. It currently supports two fixed output streams from the
> > OVL0/OVL1 sources to the DSI0/DPI0 sinks, respectively.
> > 
> > Signed-off-by: CK Hu <ck.hu@mediatek.com>
> > Signed-off-by: YT Shen <yt.shen@mediatek.com>
> > Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
> > Signed-off-by: Bibby Hsieh <bibby.hsieh@mediatek.com>
> > Signed-off-by: Mao Huang <littlecvr@chromium.org>
> > Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
> 
> Spotted two tiny things below.
[...]
> > +static int mtk_drm_bind(struct device *dev)
> > +{
> > +	struct mtk_drm_private *private = dev_get_drvdata(dev);
> > +	struct drm_device *drm;
> > +	int ret;
> > +
> > +	drm = drm_dev_alloc(&mtk_drm_driver, dev);
> > +	if (!drm)
> > +		return -ENOMEM;
> > +
> > +	drm_dev_set_unique(drm, dev_name(dev));
> > +
> > +	ret = drm_dev_register(drm, 0);
> > +	if (ret < 0)
> > +		goto err_free;
> > +
> > +	drm->dev_private = private;
> > +	private->drm = drm;
> > +
> > +	ret = mtk_drm_kms_init(drm);
> > +	if (ret < 0)
> > +		goto err_unregister;
> 
> Wrong way round, drm_dev_register should be last. And you're missing
> drm_connector_register_all here.
> -Daniel

Thanks, that also means the mtk_dsi and mtk_hdmi drivers must not call
drm_connector_(un)register anymore.
What about the connectors registered by external bridge drivers? Should
those check for (dev->driver->load) before calling
drm_connector_unregister?

regards
Philipp

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

end of thread, other threads:[~2016-05-06 14:40 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-05-06 13:05 [PATCH v16 0/8] MT8173 DRM support Philipp Zabel
2016-05-06 13:05 ` [PATCH v16 1/8] memory: mtk-smi: export mtk_smi_larb_get/put Philipp Zabel
2016-05-06 13:05 ` [PATCH v16 2/8] dt-bindings: drm/mediatek: Add Mediatek display subsystem dts binding Philipp Zabel
2016-05-06 13:05 ` [PATCH v16 3/8] drm/mediatek: Add DRM Driver for Mediatek SoC MT8173 Philipp Zabel
2016-05-06 13:16   ` Daniel Vetter
2016-05-06 14:40     ` Philipp Zabel
2016-05-06 13:05 ` [PATCH v16 4/8] drm/mediatek: Add DSI sub driver Philipp Zabel
2016-05-06 13:05 ` [PATCH v16 5/8] drm/mediatek: Add DPI " Philipp Zabel
2016-05-06 13:05 ` [PATCH v16 6/8] clk: mediatek: make dpi0_sel propagate rate changes Philipp Zabel
2016-05-06 13:05 ` [PATCH v16 7/8] clk: mediatek: Add hdmi_ref HDMI PHY PLL reference clock output Philipp Zabel
2016-05-06 13:05 ` [PATCH v16 8/8] clk: mediatek: remove hdmitx_dig_cts from TOP clocks Philipp Zabel

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).