[PATCH v14 2/8] drm/mediatek: Add DRM Driver for Mediatek SoC MT8173.

Daniel Vetter daniel at ffwll.ch
Tue Apr 26 01:48:00 PDT 2016


On Tue, Apr 26, 2016 at 10:05:22AM +0200, Philipp Zabel wrote:
> From 24982961a7406c9c6ed139329c9ee1263ddc92c2 Mon Sep 17 00:00:00 2001
> From: CK Hu <ck.hu at mediatek.com>
> Date: Mon, 4 Jan 2016 18:36:34 +0100
> Subject: [PATCH v14.5 2/8] drm/mediatek: Add DRM Driver for Mediatek SoC MT8173.
> 
> 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 at mediatek.com>
> Signed-off-by: YT Shen <yt.shen at mediatek.com>
> Signed-off-by: Daniel Kurtz <djkurtz at chromium.org>
> Signed-off-by: Bibby Hsieh <bibby.hsieh at mediatek.com>
> Signed-off-by: Mao Huang <littlecvr at chromium.org>
> Signed-off-by: Philipp Zabel <p.zabel at pengutronix.de>
> ---
> Changes since v14:
>  - Fixed module build
> ---
>  drivers/gpu/drm/Kconfig                     |   2 +
>  drivers/gpu/drm/Makefile                    |   1 +
>  drivers/gpu/drm/mediatek/Kconfig            |  12 +
>  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      | 565 +++++++++++++++++++++++++++
>  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, 3388 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 f2a74d0..5482012 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -281,3 +281,5 @@ source "drivers/gpu/drm/imx/Kconfig"
>  source "drivers/gpu/drm/vc4/Kconfig"
>  
>  source "drivers/gpu/drm/etnaviv/Kconfig"
> +
> +source "drivers/gpu/drm/mediatek/Kconfig"
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index 6eb94fc..02b1f3e 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -73,6 +73,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..8dad892
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/Kconfig
> @@ -0,0 +1,12 @@
> +config DRM_MEDIATEK
> +	tristate "DRM Support for Mediatek SoCs"
> +	depends on DRM
> +	depends on ARCH_MEDIATEK || (ARM && COMPILE_TEST)
> +	select DRM_KMS_HELPER
> +	select IOMMU_DMA
> +	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..f8cb6c6
> --- /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_send_vblank_event(crtc->dev, mtk_crtc->event->pipe, 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 at mediatek.com>
> + *	CK Hu <ck.hu at 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..ab1b45d
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
> @@ -0,0 +1,565 @@
> +/*
> + * Copyright (c) 2015 MediaTek Inc.
> + * Author: YT SHEN <yt.shen at 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 <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 int mtk_drm_unload(struct drm_device *drm)
> +{
> +	mtk_drm_kms_deinit(drm);
> +	drm->dev_private = NULL;
> +
> +	return 0;
> +}

Like load, the unload hook is deprecated. And in drm-next we now have some
nice helpers to make not using an unload hook easier. Please refactor per
existing examples and get rid of your unload hook.

> +static const struct vm_operations_struct mtk_drm_gem_vm_ops = {
> +	.open = drm_gem_vm_open,
> +	.close = drm_gem_vm_close,
> +};

Drive-by review I just spotted: Please use drm_gem_cma_vm_ops instead of
rolling your own.

Thanks, Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch



More information about the Linux-mediatek mailing list