diff mbox

[RFC,v4,02/11] drm/mediatek: Add DRM Driver for Mediatek SoC MT8173.

Message ID 1445026333-17013-3-git-send-email-p.zabel@pengutronix.de (mailing list archive)
State New, archived
Headers show

Commit Message

Philipp Zabel Oct. 16, 2015, 8:12 p.m. UTC
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: Philipp Zabel <p.zabel@pengutronix.de>
---
Changes since v3:
 - Removed crtc enabling/disabling on bind/unbind, the enable/disable
   callbacks should suffice.
 - Find sibling components in the device tree via compatible value
 - Split component type and instance id enums
 - Determine component type from compatible, component instance id from
   type and of alias
 - Added remaining DSI, RDMA and WDMA ids
 - Moved register mapping and mutex node search into probe function
 - Bind drm driver to mediatek,mt8173-mmsys compatible node
 - Do not try to call dma_free_attrs if dma_alloc_attrs failed.
---
 drivers/gpu/drm/Kconfig                     |   2 +
 drivers/gpu/drm/Makefile                    |   1 +
 drivers/gpu/drm/mediatek/Kconfig            |  16 +
 drivers/gpu/drm/mediatek/Makefile           |  10 +
 drivers/gpu/drm/mediatek/mtk_drm_crtc.c     | 524 ++++++++++++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_drm_crtc.h     |  84 +++++
 drivers/gpu/drm/mediatek/mtk_drm_ddp.c      | 218 +++++++++++
 drivers/gpu/drm/mediatek/mtk_drm_ddp.h      |  39 ++
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c | 409 ++++++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h |  86 +++++
 drivers/gpu/drm/mediatek/mtk_drm_drv.c      | 556 ++++++++++++++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_drm_drv.h      |  61 +++
 drivers/gpu/drm/mediatek/mtk_drm_fb.c       | 151 ++++++++
 drivers/gpu/drm/mediatek/mtk_drm_fb.h       |  29 ++
 drivers/gpu/drm/mediatek/mtk_drm_gem.c      | 208 +++++++++++
 drivers/gpu/drm/mediatek/mtk_drm_gem.h      |  56 +++
 drivers/gpu/drm/mediatek/mtk_drm_plane.c    | 175 +++++++++
 drivers/gpu/drm/mediatek/mtk_drm_plane.h    |  38 ++
 18 files changed, 2663 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_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

Comments

Daniel Vetter Oct. 19, 2015, 8:56 a.m. UTC | #1
On Fri, Oct 16, 2015 at 10:12:04PM +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: Philipp Zabel <p.zabel@pengutronix.de>

Bunch of drive-by comments below to point out deprecated functions and
more common approaches used by other drivers. Don't consider this a full
review ;-)

Cheers, Daniel

> ---
> Changes since v3:
>  - Removed crtc enabling/disabling on bind/unbind, the enable/disable
>    callbacks should suffice.
>  - Find sibling components in the device tree via compatible value

btw for DT components stuff there's piles of RFCs floating around to
extract this into helper libraries. Would be great we could push one of
them forward.

>  - Split component type and instance id enums
>  - Determine component type from compatible, component instance id from
>    type and of alias
>  - Added remaining DSI, RDMA and WDMA ids
>  - Moved register mapping and mutex node search into probe function
>  - Bind drm driver to mediatek,mt8173-mmsys compatible node
>  - Do not try to call dma_free_attrs if dma_alloc_attrs failed.
> ---
>  drivers/gpu/drm/Kconfig                     |   2 +
>  drivers/gpu/drm/Makefile                    |   1 +
>  drivers/gpu/drm/mediatek/Kconfig            |  16 +
>  drivers/gpu/drm/mediatek/Makefile           |  10 +
>  drivers/gpu/drm/mediatek/mtk_drm_crtc.c     | 524 ++++++++++++++++++++++++++
>  drivers/gpu/drm/mediatek/mtk_drm_crtc.h     |  84 +++++
>  drivers/gpu/drm/mediatek/mtk_drm_ddp.c      | 218 +++++++++++
>  drivers/gpu/drm/mediatek/mtk_drm_ddp.h      |  39 ++
>  drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c | 409 ++++++++++++++++++++
>  drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h |  86 +++++
>  drivers/gpu/drm/mediatek/mtk_drm_drv.c      | 556 ++++++++++++++++++++++++++++
>  drivers/gpu/drm/mediatek/mtk_drm_drv.h      |  61 +++
>  drivers/gpu/drm/mediatek/mtk_drm_fb.c       | 151 ++++++++
>  drivers/gpu/drm/mediatek/mtk_drm_fb.h       |  29 ++
>  drivers/gpu/drm/mediatek/mtk_drm_gem.c      | 208 +++++++++++
>  drivers/gpu/drm/mediatek/mtk_drm_gem.h      |  56 +++
>  drivers/gpu/drm/mediatek/mtk_drm_plane.c    | 175 +++++++++
>  drivers/gpu/drm/mediatek/mtk_drm_plane.h    |  38 ++
>  18 files changed, 2663 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_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 1a0a8df..9e9987b 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -264,3 +264,5 @@ source "drivers/gpu/drm/sti/Kconfig"
>  source "drivers/gpu/drm/amd/amdkfd/Kconfig"
>  
>  source "drivers/gpu/drm/imx/Kconfig"
> +
> +source "drivers/gpu/drm/mediatek/Kconfig"
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index 45e7719..af6b592 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -67,6 +67,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..5343cf1
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/Kconfig
> @@ -0,0 +1,16 @@
> +config DRM_MEDIATEK
> +	tristate "DRM Support for Mediatek SoCs"
> +	depends on DRM
> +	depends on ARCH_MEDIATEK || (ARM && COMPILE_TEST)
> +	select MTK_SMI
> +	select DRM_PANEL
> +	select DRM_MIPI_DSI
> +	select DRM_PANEL_SIMPLE
> +	select DRM_KMS_HELPER
> +	select IOMMU_DMA
> +	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..ba6d3fc
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/Makefile
> @@ -0,0 +1,10 @@
> +mediatek-drm-y := mtk_drm_drv.o \
> +		  mtk_drm_crtc.o \
> +		  mtk_drm_ddp.o \
> +		  mtk_drm_ddp_comp.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_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
> new file mode 100644
> index 0000000..158ca84
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
> @@ -0,0 +1,524 @@
> +/*
> + * 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_plane_helper.h>
> +#include <linux/clk.h>
> +#include <linux/component.h>
> +#include <linux/dma-buf.h>
> +#include <linux/of_device.h>
> +#include <linux/of_irq.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/reservation.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"
> +
> +void mtk_drm_crtc_finish_page_flip(struct mtk_drm_crtc *mtk_crtc)
> +{
> +	struct drm_device *dev = mtk_crtc->base.dev;
> +
> +	drm_send_vblank_event(dev, mtk_crtc->event->pipe, mtk_crtc->event);
> +	drm_crtc_vblank_put(&mtk_crtc->base);
> +	mtk_crtc->event = NULL;
> +}
> +
> +static void mtk_drm_crtc_destroy(struct drm_crtc *crtc)
> +{
> +	drm_crtc_cleanup(crtc);
> +}
> +
> +static bool mtk_drm_crtc_mode_fixup(struct drm_crtc *crtc,
> +		const struct drm_display_mode *mode,
> +		struct drm_display_mode *adjusted_mode)
> +{
> +	/* drm framework doesn't check NULL */
> +	return true;
> +}
> +
> +static void mtk_drm_crtc_mode_set_nofb(struct drm_crtc *crtc)
> +{
> +	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
> +	struct mtk_crtc_ddp_context *ctx = mtk_crtc->ctx;
> +
> +	if (WARN_ON(!crtc->state))
> +		return;
> +
> +	ctx->pending_width = crtc->mode.hdisplay;
> +	ctx->pending_height = crtc->mode.vdisplay;
> +	ctx->pending_config = true;
> +}
> +
> +int mtk_drm_crtc_enable_vblank(struct drm_device *drm, int pipe)
> +{
> +	struct mtk_drm_private *priv = drm->dev_private;
> +	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(priv->crtc[pipe]);
> +	struct mtk_crtc_ddp_context *ctx = mtk_crtc->ctx;
> +	struct mtk_ddp_comp *ovl = &ctx->ddp_comp[0];
> +
> +	if (ovl->funcs->comp_enable_vblank)
> +		ovl->funcs->comp_enable_vblank(ovl->regs);
> +
> +	return 0;
> +}
> +
> +void mtk_drm_crtc_disable_vblank(struct drm_device *drm, int pipe)
> +{
> +	struct mtk_drm_private *priv = drm->dev_private;
> +	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(priv->crtc[pipe]);
> +	struct mtk_crtc_ddp_context *ctx = mtk_crtc->ctx;
> +	struct mtk_ddp_comp *ovl = &ctx->ddp_comp[0];
> +
> +	if (ovl->funcs->comp_disable_vblank)
> +		ovl->funcs->comp_disable_vblank(ovl->regs);
> +}
> +
> +static void mtk_crtc_ddp_power_on(struct mtk_crtc_ddp_context *ctx)
> +{
> +	int ret;
> +	int i;
> +
> +	DRM_INFO("mtk_crtc_ddp_power_on\n");
> +	for (i = 0; i < ctx->ddp_comp_nr; i++) {
> +		ret = clk_enable(ctx->ddp_comp[i].clk);
> +		if (ret)
> +			DRM_ERROR("clk_enable(ctx->ddp_comp_clk[%d])\n", i);
> +	}
> +}
> +
> +static void mtk_crtc_ddp_power_off(struct mtk_crtc_ddp_context *ctx)
> +{
> +	int i;
> +
> +	DRM_INFO("mtk_crtc_ddp_power_off\n");
> +	for (i = 0; i < ctx->ddp_comp_nr; i++)
> +		clk_disable(ctx->ddp_comp[i].clk);
> +}
> +
> +static void mtk_crtc_ddp_hw_init(struct mtk_drm_crtc *crtc)
> +{
> +	struct mtk_crtc_ddp_context *ctx = crtc->ctx;
> +	struct drm_device *drm = crtc->base.dev;
> +	unsigned int width, height;
> +	int ret;
> +	int i;
> +
> +	if (ctx->crtc->base.state) {
> +		width = crtc->base.state->adjusted_mode.hdisplay;
> +		height = crtc->base.state->adjusted_mode.vdisplay;
> +	} else {
> +		width = 1920;
> +		height = 1080;
> +	}
> +
> +	DRM_INFO("mtk_crtc_ddp_hw_init\n");
> +
> +	/* disp_mtcmos */
> +	ret = pm_runtime_get_sync(drm->dev);
> +	if (ret < 0)
> +		DRM_ERROR("Failed to enable power domain: %d\n", ret);
> +
> +	mtk_ddp_clock_on(ctx->mutex_dev);
> +	mtk_crtc_ddp_power_on(ctx);
> +
> +	DRM_INFO("mediatek_ddp_ddp_path_setup\n");
> +	for (i = 0; i < (ctx->ddp_comp_nr - 1); i++) {
> +		mtk_ddp_add_comp_to_path(ctx->config_regs, ctx->pipe,
> +					 ctx->ddp_comp[i].funcs->comp_id,
> +					 ctx->ddp_comp[i+1].funcs->comp_id);
> +		mtk_ddp_add_comp_to_mutex(ctx->mutex_dev, ctx->pipe,
> +					  ctx->ddp_comp[i].funcs->comp_id,
> +					  ctx->ddp_comp[i+1].funcs->comp_id);
> +	}
> +
> +	DRM_INFO("ddp_disp_path_power_on %dx%d\n", width, height);
> +	for (i = 0; i < ctx->ddp_comp_nr; i++) {
> +		struct mtk_ddp_comp *comp = &ctx->ddp_comp[i];
> +
> +		if (comp->funcs->comp_config)
> +			comp->funcs->comp_config(comp->regs, width, height);
> +
> +		if (comp->funcs->comp_power_on)
> +			comp->funcs->comp_power_on(comp->regs);
> +	}
> +}
> +
> +static void mtk_crtc_ddp_hw_fini(struct mtk_drm_crtc *crtc)
> +{
> +	struct mtk_crtc_ddp_context *ctx = crtc->ctx;
> +	struct drm_device *drm = crtc->base.dev;
> +	struct mtk_ddp_comp *ovl = &ctx->ddp_comp[0];
> +	int i;
> +
> +	DRM_INFO("mtk_crtc_ddp_hw_fini\n");
> +	for (i = 0; i < OVL_LAYER_NR; i++) {
> +		ctx->pending_ovl_enable[i] = false;
> +		ovl->funcs->comp_layer_off(ovl->regs, i);
> +	}
> +	mtk_crtc_ddp_power_off(ctx);
> +	mtk_ddp_clock_off(ctx->mutex_dev);
> +
> +	/* disp_mtcmos */
> +	pm_runtime_put_sync(drm->dev);
> +}
> +
> +static void mtk_drm_crtc_enable(struct drm_crtc *crtc)
> +{
> +	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
> +
> +	if (WARN_ON(mtk_crtc->enabled))
> +		return;
> +
> +	mtk_crtc_ddp_hw_init(mtk_crtc);
> +
> +	drm_crtc_vblank_on(crtc);
> +	WARN_ON(drm_crtc_vblank_get(crtc) != 0);
> +
> +	mtk_crtc->enabled = true;
> +}
> +
> +static void mtk_drm_crtc_disable(struct drm_crtc *crtc)
> +{
> +	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
> +
> +	DRM_INFO("mtk_drm_crtc_disable %d\n", crtc->base.id);
> +	if (WARN_ON(!mtk_crtc->enabled))
> +		return;
> +
> +	drm_crtc_vblank_put(crtc);
> +	drm_crtc_vblank_off(crtc);
> +
> +	mtk_crtc_ddp_hw_fini(mtk_crtc);
> +
> +	mtk_crtc->enabled = false;
> +}
> +
> +static void mtk_drm_crtc_atomic_begin(struct drm_crtc *crtc,
> +				      struct drm_crtc_state *old_crtc_state)
> +{
> +	struct drm_crtc_state *state = crtc->state;
> +	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
> +
> +	if (state->event) {
> +		state->event->pipe = drm_crtc_index(crtc);
> +		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
> +		mtk_crtc->event = state->event;
> +		state->event = NULL;
> +	}
> +}
> +
> +void mtk_drm_crtc_commit(struct mtk_drm_crtc *crtc)
> +{
> +	struct mtk_crtc_ddp_context *ctx = crtc->ctx;
> +	unsigned int i;
> +
> +	for (i = 0; i < OVL_LAYER_NR; i++)
> +		if (ctx->pending_ovl_dirty[i]) {
> +			ctx->pending_ovl_config[i] = true;
> +			ctx->pending_ovl_dirty[i] = false;
> +		}
> +}
> +
> +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);
> +
> +	mtk_drm_crtc_commit(mtk_crtc);
> +}
> +
> +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			= drm_atomic_helper_crtc_reset,
> +	.atomic_duplicate_state	= drm_atomic_helper_crtc_duplicate_state,
> +	.atomic_destroy_state	= drm_atomic_helper_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,
> +};
> +
> +struct mtk_drm_crtc *mtk_drm_crtc_create(struct drm_device *drm,
> +		struct drm_plane *primary, struct drm_plane *cursor, int pipe,
> +		void *ctx)
> +{
> +	struct mtk_drm_private *priv = drm->dev_private;
> +	struct mtk_drm_crtc *mtk_crtc;
> +	int ret;
> +
> +	mtk_crtc = devm_kzalloc(drm->dev, sizeof(*mtk_crtc), GFP_KERNEL);
> +	if (!mtk_crtc)
> +		return ERR_PTR(-ENOMEM);
> +
> +	mtk_crtc->pipe = pipe;
> +	mtk_crtc->ctx = ctx;
> +	mtk_crtc->enabled = false;
> +
> +	priv->crtc[pipe] = &mtk_crtc->base;
> +
> +	ret = drm_crtc_init_with_planes(drm, &mtk_crtc->base, primary, cursor,
> +			&mtk_crtc_funcs);
> +	if (ret)
> +		goto err_cleanup_crtc;
> +
> +	drm_crtc_helper_add(&mtk_crtc->base, &mtk_crtc_helper_funcs);
> +
> +	return mtk_crtc;
> +
> +err_cleanup_crtc:
> +	drm_crtc_cleanup(&mtk_crtc->base);
> +	return ERR_PTR(ret);
> +}
> +
> +void mtk_drm_crtc_plane_config(struct mtk_drm_crtc *crtc, unsigned int idx,
> +			       bool enable, dma_addr_t addr)
> +{
> +	struct mtk_crtc_ddp_context *ctx = crtc->ctx;
> +	struct drm_plane *plane = &ctx->planes[idx].base;
> +	unsigned int pitch, format;
> +	int x, y;
> +
> +	if (!plane->fb || !plane->state)
> +		return;
> +
> +	pitch = plane->fb->pitches[0];
> +	format = plane->fb->pixel_format;
> +
> +	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;
> +	}
> +
> +	ctx->pending_ovl_enable[idx] = enable;
> +	ctx->pending_ovl_addr[idx] = addr;
> +	ctx->pending_ovl_pitch[idx] = pitch;
> +	ctx->pending_ovl_format[idx] = format;
> +	ctx->pending_ovl_x[idx] = x;
> +	ctx->pending_ovl_y[idx] = y;
> +	ctx->pending_ovl_size[idx] = ctx->planes[idx].disp_size;
> +	ctx->pending_ovl_dirty[idx] = true;
> +}
> +
> +static void mtk_crtc_ddp_irq(struct mtk_crtc_ddp_context *ctx)
> +{
> +	struct mtk_ddp_comp *ovl = &ctx->ddp_comp[0];
> +	unsigned int i;
> +
> +	if (ctx->pending_config) {
> +		ctx->pending_config = false;
> +
> +		for (i = 0; i < OVL_LAYER_NR; i++)
> +			ovl->funcs->comp_layer_off(ovl->regs, i);
> +		ovl->funcs->comp_config(ovl->regs, ctx->pending_width,
> +						   ctx->pending_height);
> +	}
> +
> +	for (i = 0; i < OVL_LAYER_NR; i++) {
> +		if (ctx->pending_ovl_config[i]) {
> +			if (!ctx->pending_ovl_enable[i])
> +				ovl->funcs->comp_layer_off(ovl->regs, i);
> +
> +			ovl->funcs->comp_layer_config(ovl->regs, i,
> +					ctx->pending_ovl_addr[i],
> +					ctx->pending_ovl_pitch[i],
> +					ctx->pending_ovl_format[i],
> +					ctx->pending_ovl_x[i],
> +					ctx->pending_ovl_y[i],
> +					ctx->pending_ovl_size[i]);
> +
> +			if (ctx->pending_ovl_enable[i])
> +				ovl->funcs->comp_layer_on(ovl->regs, i);
> +		}
> +
> +		ctx->pending_ovl_config[i] = false;
> +	}
> +
> +	drm_handle_vblank(ctx->drm_dev, ctx->pipe);
> +}
> +
> +static irqreturn_t mtk_crtc_ddp_irq_handler(int irq, void *dev_id)
> +{
> +	struct mtk_crtc_ddp_context *ctx = dev_id;
> +	struct mtk_ddp_comp *ovl = &ctx->ddp_comp[0];
> +
> +	if (ovl->funcs->comp_clear_vblank)
> +		ovl->funcs->comp_clear_vblank(ovl->regs);
> +
> +	if (ctx->pipe >= 0 && ctx->drm_dev)
> +		mtk_crtc_ddp_irq(ctx);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int mtk_crtc_ddp_bind(struct device *dev, struct device *master,
> +		void *data)
> +{
> +	struct mtk_crtc_ddp_context *ctx = dev_get_drvdata(dev);
> +	struct drm_device *drm_dev = data;
> +	struct mtk_drm_private *priv = drm_dev->dev_private;
> +	struct device_node *node;
> +	enum drm_plane_type type;
> +	unsigned int zpos;
> +	int ret;
> +	int i;
> +
> +	ctx->drm_dev = drm_dev;
> +	ctx->pipe = priv->pipe++;
> +	ctx->config_regs = priv->config_regs;
> +	ctx->mutex_dev = priv->mutex_dev;
> +	ctx->ddp_comp_nr = priv->path_len[ctx->pipe];
> +	ctx->ddp_comp =	devm_kmalloc_array(dev, ctx->ddp_comp_nr,
> +					   sizeof(*ctx->ddp_comp), GFP_KERNEL);
> +
> +	/* priv->path[ctx->pipe] starts with this OVL node */
> +	priv->comp_node[priv->path[ctx->pipe][0]] = dev->of_node;
> +
> +	for (i = 0; i < ctx->ddp_comp_nr; i++) {
> +		struct mtk_ddp_comp *comp = &ctx->ddp_comp[i];
> +		enum mtk_ddp_comp_id comp_id = priv->path[ctx->pipe][i];
> +
> +		ret = mtk_ddp_comp_init(priv->comp_node[comp_id], comp,
> +					comp_id);
> +		if (ret) {
> +			dev_err(dev, "Failed to initialize component %i\n", i);
> +			goto unprepare;
> +		}
> +
> +		if (comp->clk) {
> +			ret = clk_prepare(comp->clk);
> +			if (ret) {
> +				dev_err(dev, "Failed to prepare clock %d\n", i);
> +				ret = -EINVAL;
> +				goto unprepare;
> +			}
> +		}
> +	}
> +
> +	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, &ctx->planes[zpos],
> +				1 << ctx->pipe, type, zpos, OVL_LAYER_NR);
> +		if (ret)
> +			goto unprepare;
> +	}
> +
> +	ctx->crtc = mtk_drm_crtc_create(drm_dev, &ctx->planes[0].base,
> +			&ctx->planes[1].base, ctx->pipe, ctx);
> +
> +	if (IS_ERR(ctx->crtc)) {
> +		ret = PTR_ERR(ctx->crtc);
> +		goto unprepare;
> +	}
> +
> +	return 0;
> +
> +unprepare:
> +	while (--i >= 0)
> +		clk_unprepare(ctx->ddp_comp[i].clk);
> +	of_node_put(node);
> +
> +	return ret;
> +}
> +
> +static void mtk_crtc_ddp_unbind(struct device *dev, struct device *master,
> +		void *data)
> +{
> +	struct mtk_crtc_ddp_context *ctx = dev_get_drvdata(dev);
> +	int i;
> +
> +	for (i = 0; i < ctx->ddp_comp_nr; i++)
> +		clk_unprepare(ctx->ddp_comp[i].clk);
> +}
> +
> +static const struct component_ops mtk_crtc_ddp_component_ops = {
> +	.bind	= mtk_crtc_ddp_bind,
> +	.unbind = mtk_crtc_ddp_unbind,
> +};
> +
> +static int mtk_crtc_ddp_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct mtk_crtc_ddp_context *ctx;
> +	int irq;
> +	int ret;
> +
> +	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
> +	if (!ctx)
> +		return -ENOMEM;
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0)
> +		return irq;
> +
> +	ret = devm_request_irq(dev, irq, mtk_crtc_ddp_irq_handler,
> +			       IRQF_TRIGGER_NONE, dev_name(dev), ctx);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to request irq %d: %d\n", irq, ret);
> +		return -ENXIO;
> +	}
> +
> +	platform_set_drvdata(pdev, ctx);
> +
> +	ret = component_add(dev, &mtk_crtc_ddp_component_ops);
> +	if (ret)
> +		dev_err(dev, "Failed to add component: %d\n", ret);
> +
> +	return ret;
> +}
> +
> +static int mtk_crtc_ddp_remove(struct platform_device *pdev)
> +{
> +	component_del(&pdev->dev, &mtk_crtc_ddp_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_crtc_ddp_probe,
> +	.remove		= mtk_crtc_ddp_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_drm_crtc.h b/drivers/gpu/drm/mediatek/mtk_drm_crtc.h
> new file mode 100644
> index 0000000..6313f92
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.h
> @@ -0,0 +1,84 @@
> +/*
> + * 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 "mtk_drm_plane.h"
> +
> +#define OVL_LAYER_NR	4
> +
> +struct mtk_crtc_ddp_context;
> +
> +/*
> + * MediaTek specific crtc structure.
> + *
> + * @base: crtc object.
> + * @pipe: a crtc index created at load() with a new crtc object creation
> + *	and the crtc object would be set to private->crtc array
> + *	to get a crtc object corresponding to this pipe from private->crtc
> + *	array when irq interrupt occurred. the reason of using this pipe is that
> + *	drm framework doesn't support multiple irq yet.
> + *	we can refer to the crtc to current hardware interrupt occurred through
> + *	this pipe value.
> + * @enabled: crtc enabled
> + * @ctx: mtk crtc context object
> + * @event: drm pending vblank event
> + * @pending_needs_vblank: Need to send event in vblank.
> + */
> +struct mtk_drm_crtc {
> +	struct drm_crtc				base;
> +	unsigned int				pipe;
> +	bool					enabled;
> +	struct mtk_crtc_ddp_context		*ctx;
> +
> +	struct drm_pending_vblank_event		*event;
> +	bool					pending_needs_vblank;
> +};
> +
> +struct mtk_crtc_ddp_context {
> +	struct device			*dev;
> +	struct drm_device		*drm_dev;
> +	struct mtk_drm_crtc		*crtc;
> +	struct mtk_drm_plane		planes[OVL_LAYER_NR];
> +	int				pipe;
> +
> +	void __iomem			*config_regs;
> +	struct device			*mutex_dev;
> +	u32				ddp_comp_nr;
> +	struct mtk_ddp_comp		*ddp_comp;

All the above probably should just be moved into mtk_drm_crtc. At least I
don't understand why you need this indirection.
> +
> +	bool				pending_config;
> +	unsigned int			pending_width;
> +	unsigned int			pending_height;
> +
> +	bool				pending_ovl_config[OVL_LAYER_NR];
> +	bool				pending_ovl_enable[OVL_LAYER_NR];
> +	unsigned int			pending_ovl_addr[OVL_LAYER_NR];
> +	unsigned int			pending_ovl_pitch[OVL_LAYER_NR];
> +	unsigned int			pending_ovl_format[OVL_LAYER_NR];
> +	int				pending_ovl_x[OVL_LAYER_NR];
> +	int				pending_ovl_y[OVL_LAYER_NR];
> +	unsigned int			pending_ovl_size[OVL_LAYER_NR];
> +	bool				pending_ovl_dirty[OVL_LAYER_NR];

This works since you only touch these in the atomic_commit phase, but the
recommend way to do this with atomic is to subclass drm_crtc_state:

struct mtk_crtc_state {
	struct drm_crtc_state base;

	/* all the pending_ stuff above */
};

Then you just pass the mtk to your irq handler to do the update.


> +};
> +
> +#define to_mtk_crtc(x) container_of(x, struct mtk_drm_crtc, base)
> +
> +int mtk_drm_crtc_enable_vblank(struct drm_device *drm, int pipe);
> +void mtk_drm_crtc_disable_vblank(struct drm_device *drm, int pipe);
> +void mtk_drm_crtc_plane_config(struct mtk_drm_crtc *crtc, unsigned int idx,
> +				  bool enable, dma_addr_t addr);
> +void mtk_drm_crtc_commit(struct mtk_drm_crtc *crtc);
> +
> +#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..9ec2960
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
> @@ -0,0 +1,218 @@
> +/*
> + * 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/platform_device.h>
> +#include <linux/regmap.h>
> +
> +#include "mtk_drm_crtc.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_OVL0		11
> +#define MUTEX_MOD_OVL1		12
> +#define MUTEX_MOD_RDMA0		13
> +#define MUTEX_MOD_RDMA1		14
> +#define MUTEX_MOD_COLOR0	18
> +#define MUTEX_MOD_COLOR1	19
> +#define MUTEX_MOD_AAL		20
> +#define MUTEX_MOD_GAMMA		21
> +#define MUTEX_MOD_UFOE		22
> +#define MUTEX_MOD_PWM0		23
> +#define MUTEX_MOD_OD		25
> +
> +#define MUTEX_SOF_DSI0		1
> +#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
> +
> +static const unsigned int mutex_mod[DDP_COMPONENT_ID_MAX] = {
> +	[DDP_COMPONENT_AAL] = MUTEX_MOD_AAL,
> +	[DDP_COMPONENT_COLOR0] = MUTEX_MOD_COLOR0,
> +	[DDP_COMPONENT_COLOR1] = MUTEX_MOD_COLOR1,
> +	[DDP_COMPONENT_GAMMA] = MUTEX_MOD_GAMMA,
> +	[DDP_COMPONENT_OD] = MUTEX_MOD_OD,
> +	[DDP_COMPONENT_OVL0] = MUTEX_MOD_OVL0,
> +	[DDP_COMPONENT_OVL1] = MUTEX_MOD_OVL1,
> +	[DDP_COMPONENT_PWM0] = MUTEX_MOD_PWM0,
> +	[DDP_COMPONENT_RDMA0] = MUTEX_MOD_RDMA0,
> +	[DDP_COMPONENT_RDMA1] = MUTEX_MOD_RDMA1,
> +	[DDP_COMPONENT_UFOE] = MUTEX_MOD_UFOE,
> +};
> +
> +void mtk_ddp_add_comp_to_path(void __iomem *config_regs, unsigned int pipe,
> +			      enum mtk_ddp_comp_id cur,
> +			      enum mtk_ddp_comp_id next)
> +{
> +	unsigned int addr, 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;
> +	}
> +	if (value) {
> +		unsigned int reg = readl(config_regs + addr) | value;
> +
> +		writel(reg, config_regs + addr);
> +	}
> +
> +	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;
> +	}
> +	if (value) {
> +		unsigned int reg = readl(config_regs + addr) | value;
> +
> +		writel(reg, config_regs + addr);
> +	}
> +}
> +
> +void mtk_ddp_add_comp_to_mutex(struct device *dev, unsigned int pipe,
> +			       enum mtk_ddp_comp_id cur,
> +			       enum mtk_ddp_comp_id next)
> +{
> +	struct mtk_ddp *ddp = dev_get_drvdata(dev);
> +	unsigned int reg;
> +
> +	reg = readl(ddp->mutex_regs + DISP_REG_MUTEX_MOD(pipe));
> +	reg |= BIT(mutex_mod[cur]) | BIT(mutex_mod[next]);
> +	writel(reg, ddp->mutex_regs + DISP_REG_MUTEX_MOD(pipe));
> +
> +	if (next == DDP_COMPONENT_DPI0)
> +		reg = MUTEX_SOF_DPI0;
> +	else
> +		reg = MUTEX_SOF_DSI0;
> +
> +	writel(reg, ddp->mutex_regs + DISP_REG_MUTEX_SOF(pipe));
> +	writel(1, ddp->mutex_regs + DISP_REG_MUTEX_EN(pipe));
> +}
> +
> +void mtk_ddp_clock_on(struct device *dev)
> +{
> +	struct mtk_ddp *ddp = dev_get_drvdata(dev);
> +	int ret;
> +
> +	ret = clk_prepare_enable(ddp->mutex_disp_clk);
> +	if (ret != 0)
> +		DRM_ERROR("clk_prepare_enable(mutex_disp_clk) error!\n");
> +}
> +
> +void mtk_ddp_clock_off(struct device *dev)
> +{
> +	struct mtk_ddp *ddp = dev_get_drvdata(dev);
> +
> +	clk_disable_unprepare(ddp->mutex_disp_clk);
> +}
> +
> +static int mtk_ddp_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct mtk_ddp *ddp;
> +	struct resource *regs;
> +
> +	ddp = devm_kzalloc(dev, sizeof(*ddp), GFP_KERNEL);
> +	if (!ddp)
> +		return -ENOMEM;
> +
> +	ddp->mutex_disp_clk = devm_clk_get(dev, NULL);
> +	if (IS_ERR(ddp->mutex_disp_clk)) {
> +		dev_err(dev, "Failed to get clock\n");
> +		return PTR_ERR(ddp->mutex_disp_clk);
> +	}
> +
> +	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	ddp->mutex_regs = devm_ioremap_resource(dev, regs);
> +	if (IS_ERR(ddp->mutex_regs)) {
> +		dev_err(dev, "Failed to map mutex registers\n");
> +		return PTR_ERR(ddp->mutex_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,
> +	},
> +};
> +
> +module_platform_driver(mtk_ddp_driver);
> 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..55c2db3
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp.h
> @@ -0,0 +1,39 @@
> +/*
> + * 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_ddp {
> +	struct device			*dev;
> +	struct drm_device		*drm_dev;
> +
> +	struct clk			*mutex_disp_clk;
> +	void __iomem			*mutex_regs;
> +};
> +
> +void mtk_ddp_add_comp_to_path(void __iomem *config_regs, unsigned int pipe,
> +			      enum mtk_ddp_comp_id cur,
> +			      enum mtk_ddp_comp_id next);
> +void mtk_ddp_add_comp_to_mutex(struct device *dev, unsigned int pipe,
> +			       enum mtk_ddp_comp_id cur,
> +			       enum mtk_ddp_comp_id next);
> +void mtk_ddp_clock_on(struct device *dev);
> +void mtk_ddp_clock_off(struct device *dev);
> +
> +#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..97a84b0
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
> @@ -0,0 +1,409 @@
> +/*
> + * 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_address.h>
> +#include <linux/of_irq.h>
> +#include <drm/drmP.h>
> +#include "mtk_drm_ddp_comp.h"
> +
> +#define DISP_REG_OVL_INTEN			0x0004
> +#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 DISP_REG_RDMA_INT_ENABLE		0x0000
> +#define DISP_REG_RDMA_INT_STATUS		0x0004
> +#define DISP_REG_RDMA_GLOBAL_CON		0x0010
> +#define DISP_REG_RDMA_SIZE_CON_0		0x0014
> +#define DISP_REG_RDMA_SIZE_CON_1		0x0018
> +#define DISP_REG_RDMA_FIFO_CON			0x0040
> +
> +#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
> +
> +enum OVL_INPUT_FORMAT {
> +	OVL_INFMT_RGB565 = 0,
> +	OVL_INFMT_RGB888 = 1,
> +	OVL_INFMT_RGBA8888 = 2,
> +	OVL_INFMT_ARGB8888 = 3,
> +};
> +
> +#define	OVL_RDMA_MEM_GMC	0x40402020
> +#define	OVL_AEN			BIT(8)
> +#define	OVL_ALPHA		0xff
> +
> +#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_start(void __iomem *color_base)
> +{
> +	writel(COLOR_BYPASS_ALL | COLOR_SEQ_SEL,
> +		color_base + DISP_COLOR_CFG_MAIN);
> +	writel(0x1, color_base + DISP_COLOR_START);
> +}
> +
> +static void mtk_od_config(void __iomem *od_base, unsigned int w, unsigned int h)
> +{
> +	writel(w << 16 | h, od_base + DISP_OD_SIZE);
> +}
> +
> +static void mtk_od_start(void __iomem *od_base)
> +{
> +	writel(OD_RELAY_MODE, od_base + DISP_OD_CFG);
> +	writel(1, od_base + DISP_OD_EN);
> +}
> +
> +static void mtk_ovl_enable_vblank(void __iomem *disp_base)
> +{
> +	writel(0x2, disp_base + DISP_REG_OVL_INTEN);
> +}
> +
> +static void mtk_ovl_disable_vblank(void __iomem *disp_base)
> +{
> +	writel(0x0, disp_base + DISP_REG_OVL_INTEN);
> +}
> +
> +static void mtk_ovl_clear_vblank(void __iomem *disp_base)
> +{
> +	writel(0x0, disp_base + DISP_REG_OVL_INTSTA);
> +}
> +
> +static void mtk_ovl_start(void __iomem *ovl_base)
> +{
> +	writel(0x1, ovl_base + DISP_REG_OVL_EN);
> +}
> +
> +static void mtk_ovl_config(void __iomem *ovl_base,
> +		unsigned int w, unsigned int h)
> +{
> +	if (w != 0 && h != 0)
> +		writel(h << 16 | w, ovl_base + DISP_REG_OVL_ROI_SIZE);
> +	writel(0x0, ovl_base + DISP_REG_OVL_ROI_BGCLR);
> +
> +	writel(0x1, ovl_base + DISP_REG_OVL_RST);
> +	writel(0x0, ovl_base + DISP_REG_OVL_RST);
> +}
> +
> +static bool has_rb_swapped(unsigned int fmt)
> +{
> +	switch (fmt) {
> +	case DRM_FORMAT_BGR888:
> +	case DRM_FORMAT_BGR565:
> +	case DRM_FORMAT_ABGR8888:
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_BGRA8888:
> +	case DRM_FORMAT_BGRX8888:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static unsigned int ovl_fmt_convert(unsigned int fmt)
> +{
> +	switch (fmt) {
> +	case DRM_FORMAT_RGB888:
> +	case DRM_FORMAT_BGR888:
> +		return OVL_INFMT_RGB888;
> +	case DRM_FORMAT_RGB565:
> +	case DRM_FORMAT_BGR565:
> +		return OVL_INFMT_RGB565;
> +	case DRM_FORMAT_RGBX8888:
> +	case DRM_FORMAT_RGBA8888:
> +	case DRM_FORMAT_BGRX8888:
> +	case DRM_FORMAT_BGRA8888:
> +		return OVL_INFMT_ARGB8888;
> +	case DRM_FORMAT_XRGB8888:
> +	case DRM_FORMAT_ARGB8888:
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_ABGR8888:
> +		return OVL_INFMT_RGBA8888;
> +	default:
> +		DRM_ERROR("unsupport format[%08x]\n", fmt);
> +		return -EINVAL;
> +	}
> +}
> +
> +static void mtk_ovl_layer_on(void __iomem *ovl_base, unsigned int idx)
> +{
> +	unsigned int reg;
> +
> +	writel(0x1, ovl_base + DISP_REG_OVL_RDMA_CTRL(idx));
> +	writel(OVL_RDMA_MEM_GMC, ovl_base + DISP_REG_OVL_RDMA_GMC(idx));
> +
> +	reg = readl(ovl_base + DISP_REG_OVL_SRC_CON);
> +	reg = reg | (1 << idx);
> +	writel(reg, ovl_base + DISP_REG_OVL_SRC_CON);
> +}
> +
> +static void mtk_ovl_layer_off(void __iomem *ovl_base, unsigned int idx)
> +{
> +	unsigned int reg;
> +
> +	reg = readl(ovl_base + DISP_REG_OVL_SRC_CON);
> +	reg = reg & ~(1 << idx);
> +	writel(reg, ovl_base + DISP_REG_OVL_SRC_CON);
> +
> +	writel(0x0, ovl_base + DISP_REG_OVL_RDMA_CTRL(idx));
> +}
> +
> +static void mtk_ovl_layer_config(void __iomem *ovl_base, unsigned int idx,
> +		unsigned int addr, unsigned int pitch, unsigned int fmt,
> +		int x, int y, unsigned int size)
> +{
> +	unsigned int reg;
> +
> +	reg = has_rb_swapped(fmt) << 24 | ovl_fmt_convert(fmt) << 12;
> +	if (idx != 0)
> +		reg |= OVL_AEN | OVL_ALPHA;
> +
> +	writel(reg, ovl_base + DISP_REG_OVL_CON(idx));
> +	writel(pitch & 0xFFFF, ovl_base + DISP_REG_OVL_PITCH(idx));
> +	writel(size, ovl_base + DISP_REG_OVL_SRC_SIZE(idx));
> +	writel(y << 16 | x, ovl_base + DISP_REG_OVL_OFFSET(idx));
> +	writel(addr, ovl_base + DISP_REG_OVL_ADDR(idx));
> +}
> +
> +static void mtk_rdma_start(void __iomem *rdma_base)
> +{
> +	unsigned int reg;
> +
> +	writel(0x4, rdma_base + DISP_REG_RDMA_INT_ENABLE);
> +	reg = readl(rdma_base + DISP_REG_RDMA_GLOBAL_CON);
> +	reg |= 1;
> +	writel(reg, rdma_base + DISP_REG_RDMA_GLOBAL_CON);
> +}
> +
> +static void mtk_rdma_config(void __iomem *rdma_base,
> +		unsigned width, unsigned height)
> +{
> +	unsigned int reg;
> +
> +	reg = readl(rdma_base + DISP_REG_RDMA_SIZE_CON_0);
> +	reg = (reg & ~(0xFFF)) | (width & 0xFFF);
> +	writel(reg, rdma_base + DISP_REG_RDMA_SIZE_CON_0);
> +
> +	reg = readl(rdma_base + DISP_REG_RDMA_SIZE_CON_1);
> +	reg = (reg & ~(0xFFFFF)) | (height & 0xFFFFF);
> +	writel(reg, rdma_base + DISP_REG_RDMA_SIZE_CON_1);
> +
> +	writel(0x80F00008, rdma_base + DISP_REG_RDMA_FIFO_CON);
> +}
> +
> +static void mtk_ufoe_start(void __iomem *ufoe_base)
> +{
> +	writel(UFO_BYPASS, ufoe_base + DISP_REG_UFO_START);
> +}
> +
> +static const struct mtk_ddp_comp_funcs ddp_aal = {
> +	.comp_id = DDP_COMPONENT_AAL,
> +};
> +
> +static const struct mtk_ddp_comp_funcs ddp_color0 = {
> +	.comp_id = DDP_COMPONENT_COLOR0,
> +	.comp_power_on = mtk_color_start,
> +};
> +
> +static const struct mtk_ddp_comp_funcs ddp_color1 = {
> +	.comp_id = DDP_COMPONENT_COLOR1,
> +	.comp_power_on = mtk_color_start,
> +};
> +
> +static const struct mtk_ddp_comp_funcs ddp_dpi0 = {
> +	.comp_id = DDP_COMPONENT_DPI0,
> +};
> +
> +static const struct mtk_ddp_comp_funcs ddp_dsi0 = {
> +	.comp_id = DDP_COMPONENT_DSI0,
> +};
> +
> +static const struct mtk_ddp_comp_funcs ddp_dsi1 = {
> +	.comp_id = DDP_COMPONENT_DSI1,
> +};
> +
> +static const struct mtk_ddp_comp_funcs ddp_gamma = {
> +	.comp_id = DDP_COMPONENT_GAMMA,
> +};
> +
> +static const struct mtk_ddp_comp_funcs ddp_od = {
> +	.comp_id = DDP_COMPONENT_OD,
> +	.comp_config = mtk_od_config,
> +	.comp_power_on = mtk_od_start,
> +};
> +
> +static const struct mtk_ddp_comp_funcs ddp_ovl0 = {
> +	.comp_id = DDP_COMPONENT_OVL0,
> +	.comp_config = mtk_ovl_config,
> +	.comp_power_on = mtk_ovl_start,
> +	.comp_enable_vblank = mtk_ovl_enable_vblank,
> +	.comp_disable_vblank = mtk_ovl_disable_vblank,
> +	.comp_clear_vblank = mtk_ovl_clear_vblank,
> +	.comp_layer_on = mtk_ovl_layer_on,
> +	.comp_layer_off = mtk_ovl_layer_off,
> +	.comp_layer_config = mtk_ovl_layer_config,
> +};
> +
> +static const struct mtk_ddp_comp_funcs ddp_ovl1 = {
> +	.comp_id = DDP_COMPONENT_OVL1,
> +	.comp_config = mtk_ovl_config,
> +	.comp_power_on = mtk_ovl_start,
> +	.comp_enable_vblank = mtk_ovl_enable_vblank,
> +	.comp_disable_vblank = mtk_ovl_disable_vblank,
> +	.comp_clear_vblank = mtk_ovl_clear_vblank,
> +	.comp_layer_on = mtk_ovl_layer_on,
> +	.comp_layer_off = mtk_ovl_layer_off,
> +	.comp_layer_config = mtk_ovl_layer_config,
> +};
> +
> +static const struct mtk_ddp_comp_funcs ddp_pwm0 = {
> +	.comp_id = DDP_COMPONENT_PWM0,
> +};
> +
> +static const struct mtk_ddp_comp_funcs ddp_rdma0 = {
> +	.comp_id = DDP_COMPONENT_RDMA0,
> +	.comp_config = mtk_rdma_config,
> +	.comp_power_on = mtk_rdma_start,
> +};
> +
> +static const struct mtk_ddp_comp_funcs ddp_rdma1 = {
> +	.comp_id = DDP_COMPONENT_RDMA1,
> +	.comp_config = mtk_rdma_config,
> +	.comp_power_on = mtk_rdma_start,
> +};
> +
> +static const struct mtk_ddp_comp_funcs ddp_rdma2 = {
> +	.comp_id = DDP_COMPONENT_RDMA2,
> +	.comp_config = mtk_rdma_config,
> +	.comp_power_on = mtk_rdma_start,
> +};
> +
> +static const struct mtk_ddp_comp_funcs ddp_ufoe = {
> +	.comp_id = DDP_COMPONENT_UFOE,
> +	.comp_power_on = mtk_ufoe_start,
> +};
> +
> +static const struct mtk_ddp_comp_funcs ddp_wdma0 = {
> +	.comp_id = DDP_COMPONENT_WDMA0,
> +};
> +
> +static const struct mtk_ddp_comp_funcs ddp_wdma1 = {
> +	.comp_id = DDP_COMPONENT_WDMA1,
> +};
> +
> +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 struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_ID_MAX] = {
> +	[DDP_COMPONENT_AAL]	= { MTK_DISP_AAL,	0, &ddp_aal },
> +	[DDP_COMPONENT_COLOR0]	= { MTK_DISP_COLOR,	0, &ddp_color0 },
> +	[DDP_COMPONENT_COLOR1]	= { MTK_DISP_COLOR,	1, &ddp_color1 },
> +	[DDP_COMPONENT_DPI0]	= { MTK_DPI,		0, &ddp_dpi0 },
> +	[DDP_COMPONENT_DSI0]	= { MTK_DSI,		0, &ddp_dsi0 },
> +	[DDP_COMPONENT_DSI1]	= { MTK_DSI,		1, &ddp_dsi1 },
> +	[DDP_COMPONENT_GAMMA]	= { MTK_DISP_GAMMA,	0, &ddp_gamma },
> +	[DDP_COMPONENT_OD]	= { MTK_DISP_OD,	0, &ddp_od },
> +	[DDP_COMPONENT_OVL0]	= { MTK_DISP_OVL,	0, &ddp_ovl0 },
> +	[DDP_COMPONENT_OVL1]	= { MTK_DISP_OVL,	1, &ddp_ovl1 },
> +	[DDP_COMPONENT_PWM0]	= { MTK_DISP_PWM,	0, &ddp_pwm0 },
> +	[DDP_COMPONENT_RDMA0]	= { MTK_DISP_RDMA,	0, &ddp_rdma0 },
> +	[DDP_COMPONENT_RDMA1]	= { MTK_DISP_RDMA,	1, &ddp_rdma1 },
> +	[DDP_COMPONENT_RDMA2]	= { MTK_DISP_RDMA,	2, &ddp_rdma2 },
> +	[DDP_COMPONENT_UFOE]	= { MTK_DISP_UFOE,	0, &ddp_ufoe },
> +	[DDP_COMPONENT_WDMA0]	= { MTK_DISP_WDMA,	0, &ddp_wdma0 },
> +	[DDP_COMPONENT_WDMA1]	= { MTK_DISP_WDMA,	1, &ddp_wdma1 },
> +};
> +
> +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_node *node, struct mtk_ddp_comp *comp,
> +		      enum mtk_ddp_comp_id comp_id)
> +{
> +	if (comp_id < 0 || comp_id >= DDP_COMPONENT_ID_MAX)
> +		return -EINVAL;
> +
> +	comp->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;
> +
> +	return 0;
> +}
> 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..e2d0c39
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
> @@ -0,0 +1,86 @@
> +/*
> + * 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_node;
> +
> +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_RDMA0,
> +	DDP_COMPONENT_RDMA1,
> +	DDP_COMPONENT_RDMA2,
> +	DDP_COMPONENT_UFOE,
> +	DDP_COMPONENT_WDMA0,
> +	DDP_COMPONENT_WDMA1,
> +	DDP_COMPONENT_ID_MAX,
> +};
> +
> +struct mtk_ddp_comp_funcs {
> +	enum mtk_ddp_comp_id comp_id;
> +	void (*comp_config)(void __iomem *ovl_base,
> +			unsigned int w, unsigned int h);
> +	void (*comp_power_on)(void __iomem *ovl_base);
> +	void (*comp_power_off)(void __iomem *ovl_base);
> +	void (*comp_enable_vblank)(void __iomem *ovl_base);
> +	void (*comp_disable_vblank)(void __iomem *ovl_base);
> +	void (*comp_clear_vblank)(void __iomem *ovl_base);
> +	void (*comp_layer_on)(void __iomem *ovl_base, unsigned int idx);
> +	void (*comp_layer_off)(void __iomem *ovl_base, unsigned int idx);
> +	void (*comp_layer_config)(void __iomem *ovl_base, unsigned int idx,
> +			unsigned int addr, unsigned int pitch, unsigned int fmt,
> +			int x, int y, unsigned int size);
> +};
> +
> +struct mtk_ddp_comp {
> +	struct clk *clk;
> +	void __iomem *regs;
> +	int irq;
> +	const struct mtk_ddp_comp_funcs *funcs;
> +};
> +
> +int mtk_ddp_comp_get_id(struct device_node *node,
> +			enum mtk_ddp_comp_type comp_type);
> +int mtk_ddp_comp_init(struct device_node *node, struct mtk_ddp_comp *comp,
> +		      enum mtk_ddp_comp_id comp_id);
> +
> +#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..bb03b4a
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
> @@ -0,0 +1,556 @@
> +/*
> + * 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 <linux/component.h>
> +#include <linux/dma-iommu.h>
> +#include <linux/of_address.h>
> +#include <linux/of_platform.h>
> +#include <linux/pm_runtime.h>
> +#include <soc/mediatek/smi.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"
> +
> +#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_complete(struct mtk_drm_private *private,
> +				struct drm_atomic_state *state)
> +{
> +	struct drm_device *drm = private->drm;
> +
> +	drm_atomic_helper_commit_modeset_disables(drm, state);
> +	drm_atomic_helper_commit_planes(drm, state);
> +	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;
> +	int ret;
> +	int i;
> +
> +	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;
> +
> +	for (i = 0; i < MAX_CRTC; i++) {
> +		if (!private->larb_node[i])
> +			break;
> +
> +		pdev = of_find_device_by_node(private->larb_node[i]);
> +		if (!pdev) {
> +			dev_err(drm->dev, "Waiting for larb device %s\n",
> +				private->larb_node[i]->full_name);
> +			return -EPROBE_DEFER;
> +		}
> +		private->larb_dev[i] = &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;
> +
> +	/*
> +	 * We currently support two fixed data streams,
> +	 * OVL0 -> COLOR0 -> AAL -> OD -> RDMA0 -> UFOE -> DSI0
> +	 * and OVL1 -> COLOR1 -> GAMMA -> RDMA1 -> DPI0.
> +	 */
> +	private->path_len[0] = ARRAY_SIZE(mtk_ddp_main);
> +	private->path[0] = mtk_ddp_main;
> +	private->path_len[1] = ARRAY_SIZE(mtk_ddp_ext);
> +	private->path[1] = mtk_ddp_ext;
> +
> +	ret = component_bind_all(drm->dev, drm);
> +	if (ret)
> +		goto err_crtc;
> +
> +	/*
> +	 * 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_unbind;
> +
> +	for (i = 0; i < MAX_CRTC; i++) {
> +		if (!private->larb_dev[i])
> +			break;
> +
> +		ret = mtk_smi_larb_get(private->larb_dev[i]);
> +		if (ret) {
> +			DRM_ERROR("Failed to get larb: %d\n", ret);
> +			goto err_larb_get;
> +		}
> +	}
> +
> +	drm_kms_helper_poll_init(drm);
> +	drm_mode_config_reset(drm);
> +
> +	return 0;
> +
> +err_larb_get:
> +	for (i = i - 1; i >= 0; i--)
> +		mtk_smi_larb_put(private->larb_dev[i]);
> +	drm_kms_helper_poll_fini(drm);
> +	drm_vblank_cleanup(drm);
> +err_unbind:
> +	component_unbind_all(drm->dev, drm);
> +err_crtc:
> +	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);
> +	drm_mode_config_cleanup(drm);
> +}
> +
> +static int mtk_drm_load(struct drm_device *drm, unsigned long flags)
> +{
> +	struct device *dev = drm->dev;
> +	struct mtk_drm_private *private = dev_get_drvdata(dev);
> +
> +	drm->dev_private = private;
> +	private->drm = drm;
> +
> +	return mtk_drm_kms_init(drm);
> +}
> +
> +static int mtk_drm_unload(struct drm_device *drm)
> +{
> +	mtk_drm_kms_deinit(drm);
> +	drm->dev_private = NULL;
> +
> +	return 0;
> +}
> +
> +static const struct vm_operations_struct mtk_drm_gem_vm_ops = {
> +	.open = drm_gem_vm_open,
> +	.close = drm_gem_vm_close,
> +};
> +
> +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,
> +	.load = mtk_drm_load,
> +	.unload = mtk_drm_unload,
> +	.set_busid = drm_platform_set_busid,
> +
> +	.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 = &mtk_drm_gem_vm_ops,
> +	.dumb_create = mtk_drm_gem_dumb_create,
> +	.dumb_map_offset = mtk_drm_gem_dumb_map_offset,
> +	.dumb_destroy = drm_gem_dumb_destroy,
> +
> +	.fops = &mtk_drm_fops,
> +
> +	.set_busid = drm_platform_set_busid,
> +
> +	.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)
> +{
> +	return drm_platform_init(&mtk_drm_driver, to_platform_device(dev));

This is deprecated, please use drm_dev_alloc/drm_dev_register instead and
remove your ->load driver callback.

> +}
> +
> +static void mtk_drm_unbind(struct device *dev)
> +{
> +	struct mtk_drm_private *private = dev_get_drvdata(dev);
> +
> +	drm_put_dev(private->drm);
> +}
> +
> +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-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 num_larbs = 0;
> +
> +	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;
> +
> +		comp_type = (enum mtk_ddp_comp_type)of_id->data;
> +
> +		if (comp_type == MTK_DISP_MUTEX) {
> +			private->mutex_node = of_node_get(node);
> +			continue;
> +		}
> +
> +		if (!of_device_is_available(node)) {
> +			dev_dbg(dev, "Skipping disabled component %s\n",
> +				node->full_name);
> +			continue;
> +		}
> +
> +		comp_id = mtk_ddp_comp_get_id(node, comp_type);
> +		if (comp_id < 0) {
> +			dev_info(dev, "Skipping unknown component %s\n",
> +				 node->full_name);
> +			continue;
> +		}
> +
> +		/*
> +		 * Currently only the OVL, DSI, and DPI blocks have separate
> +		 * component platform drivers.
> +		 */
> +		if (comp_type == MTK_DISP_OVL ||
> +		    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);
> +		}
> +
> +		private->comp_node[comp_id] = of_node_get(node);
> +
> +		if (comp_type == MTK_DISP_OVL) {
> +			struct device_node *larb_node;
> +
> +			larb_node = of_parse_phandle(node, "mediatek,larb", 0);
> +			if (larb_node && num_larbs < MAX_CRTC)
> +				private->larb_node[num_larbs++] = larb_node;
> +		}
> +	}
> +
> +	if (!private->mutex_node) {
> +		dev_err(dev, "Failed to find disp-mutex node\n");
> +		return -ENODEV;
> +	}
> +
> +	pm_runtime_enable(dev);
> +
> +	platform_set_drvdata(pdev, private);
> +
> +	return component_master_add_with_match(dev, &mtk_drm_ops, match);
> +}
> +
> +static int mtk_drm_remove(struct platform_device *pdev)
> +{
> +	component_master_del(&pdev->dev, &mtk_drm_ops);
> +	pm_runtime_disable(&pdev->dev);
> +
> +	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;
> +	struct drm_connector *conn;
> +	int i;
> +
> +	drm_kms_helper_poll_disable(drm);
> +
> +	drm_modeset_lock_all(drm);
> +	list_for_each_entry(conn, &drm->mode_config.connector_list, head) {
> +		int old_dpms = conn->dpms;
> +
> +		if (conn->funcs->dpms)
> +			conn->funcs->dpms(conn, DRM_MODE_DPMS_OFF);
> +
> +		/* Set the old mode back to the connector for resume */
> +		conn->dpms = old_dpms;
> +	}
> +	drm_modeset_unlock_all(drm);
> +
> +	for (i = 0; i < MAX_CRTC; i++) {
> +		if (!private->larb_dev[i])
> +			break;
> +
> +		mtk_smi_larb_put(private->larb_dev[i]);
> +	}
> +
> +	DRM_INFO("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;
> +	struct drm_connector *conn;
> +	int ret;
> +	int i;
> +
> +	for (i = 0; i < MAX_CRTC; i++) {
> +		if (!private->larb_dev[i])
> +			break;
> +
> +		ret = mtk_smi_larb_get(private->larb_dev[i]);
> +		if (ret) {
> +			DRM_ERROR("mtk_smi_larb_get fail %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
> +	drm_modeset_lock_all(drm);
> +	list_for_each_entry(conn, &drm->mode_config.connector_list, head) {
> +		int desired_mode = conn->dpms;
> +
> +		/*
> +		 * at suspend time, we save dpms to connector->dpms,
> +		 * restore the old_dpms, and at current time, the connector
> +		 * dpms status must be DRM_MODE_DPMS_OFF.
> +		 */
> +		conn->dpms = DRM_MODE_DPMS_OFF;
> +
> +		/*
> +		 * If the connector has been disconnected during suspend,
> +		 * disconnect it from the encoder and leave it off. We'll notify
> +		 * userspace at the end.
> +		 */
> +		if (conn->funcs->dpms)
> +			conn->funcs->dpms(conn, desired_mode);
> +	}
> +	drm_modeset_unlock_all(drm);
> +
> +	drm_kms_helper_poll_enable(drm);
> +
> +	DRM_INFO("mtk_drm_sys_resume\n");
> +	return 0;
> +}
> +
> +SIMPLE_DEV_PM_OPS(mtk_drm_pm_ops, mtk_drm_sys_suspend, mtk_drm_sys_resume);
> +#endif
> +
> +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,
> +#ifdef CONFIG_PM_SLEEP
> +		.pm     = &mtk_drm_pm_ops,
> +#endif
> +	},
> +};
> +
> +static int __init mtk_drm_init(void)
> +{
> +	int ret;
> +
> +	ret = platform_driver_register(&mtk_drm_platform_driver);
> +	if (ret < 0) {
> +		pr_err("Failed to register DRM platform driver: %d\n", ret);
> +		goto err;
> +	}
> +
> +	ret = platform_driver_register(&mtk_disp_ovl_driver);
> +	if (ret < 0) {
> +		pr_err("Failed to register OVL platform driver: %d\n", ret);
> +		goto drm_err;
> +	}
> +
> +	return 0;
> +
> +drm_err:
> +	platform_driver_unregister(&mtk_drm_platform_driver);
> +err:
> +	return ret;
> +}
> +
> +static void __exit mtk_drm_exit(void)
> +{
> +	platform_driver_unregister(&mtk_disp_ovl_driver);
> +	platform_driver_unregister(&mtk_drm_platform_driver);
> +}
> +
> +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..5e5128e
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
> @@ -0,0 +1,61 @@
> +/*
> + * 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_fb_helper *fb_helper;
> +	struct drm_device *drm;
> +
> +	/*
> +	 * created crtc object would be contained at this array and
> +	 * this array is used to be aware of which crtc did it request vblank.
> +	 */
> +	struct drm_crtc *crtc[MAX_CRTC];
> +	struct drm_property *plane_zpos_property;
> +	unsigned int pipe;
> +
> +	struct device_node *larb_node[MAX_CRTC];
> +	struct device *larb_dev[MAX_CRTC];
> +	struct device_node *mutex_node;
> +	struct device *mutex_dev;
> +	void __iomem *config_regs;
> +	unsigned int path_len[MAX_CRTC];
> +	const enum mtk_ddp_comp_id *path[MAX_CRTC];
> +	struct device_node *comp_node[DDP_COMPONENT_ID_MAX];
> +
> +	struct {
> +		struct drm_atomic_state *state;
> +		struct work_struct work;
> +		struct mutex lock;
> +	} commit;
> +};
> +
> +extern struct platform_driver mtk_disp_ovl_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..dfa931b
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_fb.c
> @@ -0,0 +1,151 @@
> +/*
> + * 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 "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;
> +	struct drm_gem_object	*gem_obj[MAX_FB_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,
> +					  unsigned int plane)
> +{
> +	struct mtk_drm_fb *mtk_fb = to_mtk_fb(fb);
> +
> +	if (plane >= MAX_FB_OBJ)
> +		return NULL;
> +
> +	return mtk_fb->gem_obj[plane];
> +}
> +
> +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[0], handle);
> +}
> +
> +static void mtk_drm_fb_destroy(struct drm_framebuffer *fb)
> +{
> +	unsigned int i;
> +	struct mtk_drm_fb *mtk_fb = to_mtk_fb(fb);
> +	struct drm_gem_object *gem;
> +	int nr = drm_format_num_planes(fb->pixel_format);
> +
> +	drm_framebuffer_cleanup(fb);
> +
> +	for (i = 0; i < nr; i++) {
> +		gem = mtk_fb->gem_obj[i];
> +		drm_gem_object_unreference_unlocked(gem);
> +	}
> +
> +	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,
> +						   struct drm_mode_fb_cmd2 *mode,
> +						   struct drm_gem_object **obj)
> +{
> +	struct mtk_drm_fb *mtk_fb;
> +	unsigned int i;
> +	int ret;
> +
> +	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);
> +
> +	for (i = 0; i < drm_format_num_planes(mode->pixel_format); i++)
> +		mtk_fb->gem_obj[i] = obj[i];
> +
> +	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;
> +}
> +
> +struct drm_framebuffer *mtk_drm_mode_fb_create(struct drm_device *dev,
> +					       struct drm_file *file,
> +					       struct drm_mode_fb_cmd2 *cmd)
> +{
> +	unsigned int hsub, vsub, i;
> +	struct mtk_drm_fb *mtk_fb;
> +	struct drm_gem_object *gem[MAX_FB_OBJ];
> +	int err;
> +
> +	hsub = drm_format_horz_chroma_subsampling(cmd->pixel_format);
> +	vsub = drm_format_vert_chroma_subsampling(cmd->pixel_format);
> +	for (i = 0; i < drm_format_num_planes(cmd->pixel_format); i++) {
> +		unsigned int width = cmd->width / (i ? hsub : 1);
> +		unsigned int height = cmd->height / (i ? vsub : 1);
> +		unsigned int size, bpp;
> +
> +		gem[i] = drm_gem_object_lookup(dev, file, cmd->handles[i]);
> +		if (!gem[i]) {
> +			err = -ENOENT;
> +			goto unreference;
> +		}
> +
> +		bpp = drm_format_plane_cpp(cmd->pixel_format, i);
> +		size = (height - 1) * cmd->pitches[i] + width * bpp;
> +		size += cmd->offsets[i];
> +
> +		if (gem[i]->size < size) {
> +			drm_gem_object_unreference_unlocked(gem[i]);
> +			err = -EINVAL;
> +			goto unreference;
> +		}
> +	}
> +
> +	mtk_fb = mtk_drm_framebuffer_init(dev, cmd, gem);
> +	if (IS_ERR(mtk_fb)) {
> +		err = PTR_ERR(mtk_fb);
> +		goto unreference;
> +	}
> +
> +	return &mtk_fb->base;
> +
> +unreference:
> +	while (i--)
> +		drm_gem_object_unreference_unlocked(gem[i]);
> +
> +	return ERR_PTR(err);
> +}
> 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..9ce7307
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_fb.h
> @@ -0,0 +1,29 @@
> +/*
> + * 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
> +
> +#define MAX_FB_OBJ	3
> +
> +struct drm_gem_object *mtk_fb_get_gem_obj(struct drm_framebuffer *fb,
> +					  unsigned int plane);
> +struct drm_framebuffer *mtk_drm_mode_fb_create(struct drm_device *dev,
> +					       struct drm_file *file,
> +					       struct drm_mode_fb_cmd2 *cmd);
> +
> +void mtk_drm_mode_output_poll_changed(struct drm_device *dev);
> +int mtk_fbdev_create(struct drm_device *dev);
> +void mtk_fbdev_destroy(struct drm_device *dev);
> +
> +#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..fedc896
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_gem.c
> @@ -0,0 +1,208 @@
> +/*
> + * 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 "mtk_drm_gem.h"
> +
> +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,
> +					   unsigned long size, bool alloc_kmap)
> +{
> +	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(dev->dev, obj->size,
> +				(dma_addr_t *)&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_INFO("cookie = %p dma_addr = %llx\n",
> +			mtk_gem->cookie, mtk_gem->dma_addr);
> +
> +	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);
> +
> +	drm_gem_free_mmap_offset(obj);
> +
> +	/* release file pointer to gem object. */
> +	drm_gem_object_release(obj);
> +
> +	dma_free_attrs(obj->dev->dev, obj->size, mtk_gem->cookie,
> +		       mtk_gem->dma_addr, &mtk_gem->dma_attrs);
> +
> +	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 = args->width * DIV_ROUND_UP(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;
> +
> +	mutex_lock(&dev->struct_mutex);

struct_mutex isn't needed here (gem object lookup and the vma stuff are
all protected by looks already). Please drop it.

> +
> +	obj = drm_gem_object_lookup(dev, file_priv, handle);
> +	if (!obj) {
> +		DRM_ERROR("failed to lookup gem object.\n");
> +		ret = -EINVAL;
> +		goto unlock;
> +	}
> +
> +	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(obj);

But then you need unreference_unlocked here.

> +unlock:
> +	mutex_unlock(&dev->struct_mutex);
> +	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 drm_device *drm = obj->dev;
> +
> +	/*
> +	 * dma_alloc_attrs() allocated a struct page table for rk_obj, 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(drm->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)

This function seems unused.
> +{
> +	struct drm_device *drm = obj->dev;
> +	int ret;
> +
> +	mutex_lock(&drm->struct_mutex);
> +	ret = drm_gem_mmap_obj(obj, obj->size, vma);
> +	mutex_unlock(&drm->struct_mutex);

This locking isn't required with latest drm-misc anymore, please rebase
onto latest linux-next and drop struct_mutex.

> +	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);

Aside: Why can't you just use cma helpers for gem objects? CMA just
essentially means backed by dma_alloc memory. Would avoid a lot of
duplicated code I think.

> +}
> 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..fb7953e
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_gem.h
> @@ -0,0 +1,56 @@
> +/*
> + * 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 __iomem		*cookie;
> +	void __iomem		*kvaddr;
> +	dma_addr_t		dma_addr;
> +	struct dma_attrs	dma_attrs;
> +};
> +
> +#define to_mtk_gem_obj(x)	container_of(x, struct mtk_drm_gem_obj, base)
> +
> +struct mtk_drm_gem_obj *mtk_drm_gem_init(struct drm_device *dev,
> +		unsigned long size);
> +void mtk_drm_gem_free_object(struct drm_gem_object *gem);
> +struct mtk_drm_gem_obj *mtk_drm_gem_create(struct drm_device *dev,
> +		unsigned long 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);
> +
> +#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..704a65c
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_plane.c
> @@ -0,0 +1,175 @@
> +/*
> + * 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 <linux/dma-buf.h>
> +#include <linux/reservation.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 uint32_t formats[] = {
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_RGB565,
> +};
> +
> +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 = drm_atomic_helper_plane_reset,
> +	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_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;
> +	int ret;
> +	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, 0)) {
> +		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;
> +
> +	ret = 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);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static void mtk_plane_atomic_update(struct drm_plane *plane,
> +				    struct drm_plane_state *old_state)
> +{
> +	struct drm_plane_state *state = plane->state;
> +	struct drm_gem_object *gem_obj;
> +	struct mtk_drm_crtc *mtk_crtc;
> +	struct mtk_drm_plane *mtk_plane = to_mtk_plane(plane);
> +	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 clip = { 0, };
> +
> +	if (!state->crtc)
> +		return;
> +
> +	clip.x2 = state->crtc->state->mode.hdisplay;
> +	clip.y2 = state->crtc->state->mode.vdisplay;
> +	drm_rect_intersect(&dest, &clip);
> +	mtk_plane->disp_size = (dest.y2 - dest.y1) << 16 | (dest.x2 - dest.x1);
> +
> +	mtk_crtc = to_mtk_crtc(state->crtc);
> +
> +	if (plane->fb)
> +		drm_framebuffer_unreference(plane->fb);
> +	if (state->fb)
> +		drm_framebuffer_reference(state->fb);
> +	plane->fb = state->fb;

There shouldn't be any need to refcount framebuffers yourself. If that's
the case then there's a bug in the drm core/helpers.

> +
> +	gem_obj = mtk_fb_get_gem_obj(state->fb, 0);
> +	mtk_plane->flip_obj = to_mtk_gem_obj(gem_obj);
> +	mtk_plane->mtk_crtc = mtk_crtc;
> +
> +	if (mtk_plane->flip_obj)
> +		mtk_drm_crtc_plane_config(mtk_crtc, mtk_plane->idx, true,
> +				mtk_plane->flip_obj->dma_addr);
> +}
> +
> +static void mtk_plane_atomic_disable(struct drm_plane *plane,
> +				     struct drm_plane_state *old_state)
> +{
> +	struct mtk_drm_plane *mtk_plane = to_mtk_plane(plane);
> +	struct mtk_drm_crtc *mtk_crtc;
> +
> +	if (!old_state->crtc)
> +		return;
> +
> +	mtk_crtc = to_mtk_crtc(old_state->crtc);
> +
> +	mtk_drm_crtc_plane_config(mtk_crtc, mtk_plane->idx, false, 0);
> +
> +	mtk_drm_crtc_commit(mtk_crtc);
> +}
> +
> +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, unsigned int max_plane)
> +{
> +	int err;
> +
> +	err = drm_universal_plane_init(dev, &mtk_plane->base, possible_crtcs,
> +			&mtk_plane_funcs, formats, ARRAY_SIZE(formats), type);
> +
> +	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..80075d493
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_plane.h
> @@ -0,0 +1,38 @@
> +/*
> + * 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_
> +
> +struct fence;
> +struct mtk_crtc;
> +struct mtk_drm_gem_obj;
> +
> +#define to_mtk_plane(x)	container_of(x, struct mtk_drm_plane, base)
> +
> +struct mtk_drm_plane {
> +	struct drm_plane		base;
> +	struct mtk_drm_crtc		*mtk_crtc;
> +	unsigned int			idx;
> +	unsigned int			disp_size;
> +
> +	struct mtk_drm_gem_obj		*flip_obj;
> +};
> +
> +void mtk_plane_finish_page_flip(struct mtk_drm_plane *mtk_plane);
> +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, unsigned int max_plane);
> +
> +#endif
> -- 
> 2.6.1
>
Philipp Zabel Oct. 28, 2015, 5:13 p.m. UTC | #2
Hi Daniel,

Am Montag, den 19.10.2015, 10:56 +0200 schrieb Daniel Vetter:
> On Fri, Oct 16, 2015 at 10:12:04PM +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: Philipp Zabel <p.zabel@pengutronix.de>
> 
> Bunch of drive-by comments below to point out deprecated functions and
> more common approaches used by other drivers. Don't consider this a full
> review ;-)

Much appreciated all the same.

> Cheers, Daniel
> 
> > ---
> > Changes since v3:
> >  - Removed crtc enabling/disabling on bind/unbind, the enable/disable
> >    callbacks should suffice.
> >  - Find sibling components in the device tree via compatible value
> 
> btw for DT components stuff there's piles of RFCs floating around to
> extract this into helper libraries. Would be great we could push one of
> them forward.

The non-mediatek-specific part currently is the

	for_each_child_of_node(bus_node, node) {
		of_id = of_match_node(dt_ids, node);
		if (!of_id)
			continue;
		if (!of_device_is_available(node))
			continue;

		/* ... */
	}

loop. This is somewhat similar to a combination of
for_each_matching_node_and_match and for_each_available_child_of_node.
for_each_available_matching_child_of_node_and_match would be quite a
mouthful though.

[...]
> > +struct mtk_drm_crtc {
> > +	struct drm_crtc				base;
> > +	unsigned int				pipe;
> > +	bool					enabled;
> > +	struct mtk_crtc_ddp_context		*ctx;
> > +
> > +	struct drm_pending_vblank_event		*event;
> > +	bool					pending_needs_vblank;
> > +};
> > +
> > +struct mtk_crtc_ddp_context {
> > +	struct device			*dev;
> > +	struct drm_device		*drm_dev;
> > +	struct mtk_drm_crtc		*crtc;
> > +	struct mtk_drm_plane		planes[OVL_LAYER_NR];
> > +	int				pipe;
> > +
> > +	void __iomem			*config_regs;
> > +	struct device			*mutex_dev;
> > +	u32				ddp_comp_nr;
> > +	struct mtk_ddp_comp		*ddp_comp;
> 
> All the above probably should just be moved into mtk_drm_crtc. At least I
> don't understand why you need this indirection.

Agreed. This was needed for a debugfs patch that I have left out for
now.

> > +
> > +	bool				pending_config;
> > +	unsigned int			pending_width;
> > +	unsigned int			pending_height;
> > +
> > +	bool				pending_ovl_config[OVL_LAYER_NR];
> > +	bool				pending_ovl_enable[OVL_LAYER_NR];
> > +	unsigned int			pending_ovl_addr[OVL_LAYER_NR];
> > +	unsigned int			pending_ovl_pitch[OVL_LAYER_NR];
> > +	unsigned int			pending_ovl_format[OVL_LAYER_NR];
> > +	int				pending_ovl_x[OVL_LAYER_NR];
> > +	int				pending_ovl_y[OVL_LAYER_NR];
> > +	unsigned int			pending_ovl_size[OVL_LAYER_NR];
> > +	bool				pending_ovl_dirty[OVL_LAYER_NR];
> 
> This works since you only touch these in the atomic_commit phase, but the
> recommend way to do this with atomic is to subclass drm_crtc_state:
>
> struct mtk_crtc_state {
> 	struct drm_crtc_state base;
> 
> 	/* all the pending_ stuff above */
> };
> 
> Then you just pass the mtk to your irq handler to do the update.

I'll move these into mtk_crtc_state, but I'm not sure what you mean with
the last sentence. Currently I pass the mtk_crtc_ddp_context to the irq
handler. I can get to the mtk_crtc_state from that.

> > +static int mtk_drm_bind(struct device *dev)
> > +{
> > +	return drm_platform_init(&mtk_drm_driver, to_platform_device(dev));
> 
> This is deprecated, please use drm_dev_alloc/drm_dev_register instead and
> remove your ->load driver callback.

Will replace drm_platform_init with drm_dev_alloc/drm_dev_register and
integrate mtk_drm_load into mtk_drm_bind.

> > +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;
> > +
> > +	mutex_lock(&dev->struct_mutex);
> 
> struct_mutex isn't needed here (gem object lookup and the vma stuff are
> all protected by looks already). Please drop it.
[...]
> > +out:
> > +	drm_gem_object_unreference(obj);
> 
> But then you need unreference_unlocked here.

Ok, dropped the lock.

[...]
> > +int mtk_drm_gem_mmap_buf(struct drm_gem_object *obj, struct vm_area_struct *vma)
> 
> This function seems unused.

Currently unused, yes. I'll move it out of this series.

> > +{
> > +	struct drm_device *drm = obj->dev;
> > +	int ret;
> > +
> > +	mutex_lock(&drm->struct_mutex);
> > +	ret = drm_gem_mmap_obj(obj, obj->size, vma);
> > +	mutex_unlock(&drm->struct_mutex);
> 
> This locking isn't required with latest drm-misc anymore, please rebase
> onto latest linux-next and drop struct_mutex.

Will do.

[...]
> > +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);
> 
> Aside: Why can't you just use cma helpers for gem objects? CMA just
> essentially means backed by dma_alloc memory. Would avoid a lot of
> duplicated code I think.

I suppose we won't need dma_alloc memory eventually, due to the iommu.

[...]
> > +static void mtk_plane_atomic_update(struct drm_plane *plane,
> > +				    struct drm_plane_state *old_state)
> > +{
[...]
> > +	if (plane->fb)
> > +		drm_framebuffer_unreference(plane->fb);
> > +	if (state->fb)
> > +		drm_framebuffer_reference(state->fb);
> > +	plane->fb = state->fb;
> 
> There shouldn't be any need to refcount framebuffers yourself. If that's
> the case then there's a bug in the drm core/helpers.

I was still using plane->fb in mtk_drm_crtc_plane_config. I'll check if
switching to plane->state->fb there will allow me to drop this.

thanks
Philipp
Philipp Zabel Oct. 28, 2015, 5:14 p.m. UTC | #3
Hi Daniel,

Am Montag, den 19.10.2015, 10:56 +0200 schrieb Daniel Vetter:
> On Fri, Oct 16, 2015 at 10:12:04PM +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: Philipp Zabel <p.zabel@pengutronix.de>
> 
> Bunch of drive-by comments below to point out deprecated functions and
> more common approaches used by other drivers. Don't consider this a full
> review ;-)

Much appreciated all the same.

> Cheers, Daniel
> 
> > ---
> > Changes since v3:
> >  - Removed crtc enabling/disabling on bind/unbind, the enable/disable
> >    callbacks should suffice.
> >  - Find sibling components in the device tree via compatible value
> 
> btw for DT components stuff there's piles of RFCs floating around to
> extract this into helper libraries. Would be great we could push one of
> them forward.

The non-mediatek-specific part currently is the

	for_each_child_of_node(bus_node, node) {
		of_id = of_match_node(dt_ids, node);
		if (!of_id)
			continue;
		if (!of_device_is_available(node))
			continue;

		/* ... */
	}

loop. This is somewhat similar to a combination of
for_each_matching_node_and_match and for_each_available_child_of_node.
for_each_available_matching_child_of_node_and_match would be quite a
mouthful though.

[...]
> > +struct mtk_drm_crtc {
> > +	struct drm_crtc				base;
> > +	unsigned int				pipe;
> > +	bool					enabled;
> > +	struct mtk_crtc_ddp_context		*ctx;
> > +
> > +	struct drm_pending_vblank_event		*event;
> > +	bool					pending_needs_vblank;
> > +};
> > +
> > +struct mtk_crtc_ddp_context {
> > +	struct device			*dev;
> > +	struct drm_device		*drm_dev;
> > +	struct mtk_drm_crtc		*crtc;
> > +	struct mtk_drm_plane		planes[OVL_LAYER_NR];
> > +	int				pipe;
> > +
> > +	void __iomem			*config_regs;
> > +	struct device			*mutex_dev;
> > +	u32				ddp_comp_nr;
> > +	struct mtk_ddp_comp		*ddp_comp;
> 
> All the above probably should just be moved into mtk_drm_crtc. At least I
> don't understand why you need this indirection.

Agreed. This was needed for a debugfs patch that I have left out for
now.

> > +
> > +	bool				pending_config;
> > +	unsigned int			pending_width;
> > +	unsigned int			pending_height;
> > +
> > +	bool				pending_ovl_config[OVL_LAYER_NR];
> > +	bool				pending_ovl_enable[OVL_LAYER_NR];
> > +	unsigned int			pending_ovl_addr[OVL_LAYER_NR];
> > +	unsigned int			pending_ovl_pitch[OVL_LAYER_NR];
> > +	unsigned int			pending_ovl_format[OVL_LAYER_NR];
> > +	int				pending_ovl_x[OVL_LAYER_NR];
> > +	int				pending_ovl_y[OVL_LAYER_NR];
> > +	unsigned int			pending_ovl_size[OVL_LAYER_NR];
> > +	bool				pending_ovl_dirty[OVL_LAYER_NR];
> 
> This works since you only touch these in the atomic_commit phase, but the
> recommend way to do this with atomic is to subclass drm_crtc_state:
>
> struct mtk_crtc_state {
> 	struct drm_crtc_state base;
> 
> 	/* all the pending_ stuff above */
> };
> 
> Then you just pass the mtk to your irq handler to do the update.

I'll move these into mtk_crtc_state, but I'm not sure what you mean with
the last sentence. Currently I pass the mtk_crtc_ddp_context to the irq
handler. I can get to the mtk_crtc_state from that.

> > +static int mtk_drm_bind(struct device *dev)
> > +{
> > +	return drm_platform_init(&mtk_drm_driver, to_platform_device(dev));
> 
> This is deprecated, please use drm_dev_alloc/drm_dev_register instead and
> remove your ->load driver callback.

Will replace drm_platform_init with drm_dev_alloc/drm_dev_register and
integrate mtk_drm_load into mtk_drm_bind.

> > +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;
> > +
> > +	mutex_lock(&dev->struct_mutex);
> 
> struct_mutex isn't needed here (gem object lookup and the vma stuff are
> all protected by looks already). Please drop it.
[...]
> > +out:
> > +	drm_gem_object_unreference(obj);
> 
> But then you need unreference_unlocked here.

Ok, dropped the lock.

[...]
> > +int mtk_drm_gem_mmap_buf(struct drm_gem_object *obj, struct vm_area_struct *vma)
> 
> This function seems unused.

Currently unused, yes. I'll move it out of this series.

> > +{
> > +	struct drm_device *drm = obj->dev;
> > +	int ret;
> > +
> > +	mutex_lock(&drm->struct_mutex);
> > +	ret = drm_gem_mmap_obj(obj, obj->size, vma);
> > +	mutex_unlock(&drm->struct_mutex);
> 
> This locking isn't required with latest drm-misc anymore, please rebase
> onto latest linux-next and drop struct_mutex.

Will do.

[...]
> > +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);
> 
> Aside: Why can't you just use cma helpers for gem objects? CMA just
> essentially means backed by dma_alloc memory. Would avoid a lot of
> duplicated code I think.

I suppose we won't need dma_alloc memory eventually, due to the iommu.

[...]
> > +static void mtk_plane_atomic_update(struct drm_plane *plane,
> > +				    struct drm_plane_state *old_state)
> > +{
[...]
> > +	if (plane->fb)
> > +		drm_framebuffer_unreference(plane->fb);
> > +	if (state->fb)
> > +		drm_framebuffer_reference(state->fb);
> > +	plane->fb = state->fb;
> 
> There shouldn't be any need to refcount framebuffers yourself. If that's
> the case then there's a bug in the drm core/helpers.

I was still using plane->fb in mtk_drm_crtc_plane_config. Switching to
plane->state->fb there should allow me to drop this.

thanks
Philipp
Daniel Vetter Oct. 30, 2015, 10:32 a.m. UTC | #4
On Wed, Oct 28, 2015 at 06:13:49PM +0100, Philipp Zabel wrote:
> Hi Daniel,
> 
> Am Montag, den 19.10.2015, 10:56 +0200 schrieb Daniel Vetter:
> > On Fri, Oct 16, 2015 at 10:12:04PM +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: Philipp Zabel <p.zabel@pengutronix.de>
> > 
> > Bunch of drive-by comments below to point out deprecated functions and
> > more common approaches used by other drivers. Don't consider this a full
> > review ;-)
> 
> Much appreciated all the same.
> 
> > Cheers, Daniel
> > 
> > > ---
> > > Changes since v3:
> > >  - Removed crtc enabling/disabling on bind/unbind, the enable/disable
> > >    callbacks should suffice.
> > >  - Find sibling components in the device tree via compatible value
> > 
> > btw for DT components stuff there's piles of RFCs floating around to
> > extract this into helper libraries. Would be great we could push one of
> > them forward.
> 
> The non-mediatek-specific part currently is the
> 
> 	for_each_child_of_node(bus_node, node) {
> 		of_id = of_match_node(dt_ids, node);
> 		if (!of_id)
> 			continue;
> 		if (!of_device_is_available(node))
> 			continue;
> 
> 		/* ... */
> 	}
> 
> loop. This is somewhat similar to a combination of
> for_each_matching_node_and_match and for_each_available_child_of_node.
> for_each_available_matching_child_of_node_and_match would be quite a
> mouthful though.
> 
> [...]
> > > +struct mtk_drm_crtc {
> > > +	struct drm_crtc				base;
> > > +	unsigned int				pipe;
> > > +	bool					enabled;
> > > +	struct mtk_crtc_ddp_context		*ctx;
> > > +
> > > +	struct drm_pending_vblank_event		*event;
> > > +	bool					pending_needs_vblank;
> > > +};
> > > +
> > > +struct mtk_crtc_ddp_context {
> > > +	struct device			*dev;
> > > +	struct drm_device		*drm_dev;
> > > +	struct mtk_drm_crtc		*crtc;
> > > +	struct mtk_drm_plane		planes[OVL_LAYER_NR];
> > > +	int				pipe;
> > > +
> > > +	void __iomem			*config_regs;
> > > +	struct device			*mutex_dev;
> > > +	u32				ddp_comp_nr;
> > > +	struct mtk_ddp_comp		*ddp_comp;
> > 
> > All the above probably should just be moved into mtk_drm_crtc. At least I
> > don't understand why you need this indirection.
> 
> Agreed. This was needed for a debugfs patch that I have left out for
> now.
> 
> > > +
> > > +	bool				pending_config;
> > > +	unsigned int			pending_width;
> > > +	unsigned int			pending_height;
> > > +
> > > +	bool				pending_ovl_config[OVL_LAYER_NR];
> > > +	bool				pending_ovl_enable[OVL_LAYER_NR];
> > > +	unsigned int			pending_ovl_addr[OVL_LAYER_NR];
> > > +	unsigned int			pending_ovl_pitch[OVL_LAYER_NR];
> > > +	unsigned int			pending_ovl_format[OVL_LAYER_NR];
> > > +	int				pending_ovl_x[OVL_LAYER_NR];
> > > +	int				pending_ovl_y[OVL_LAYER_NR];
> > > +	unsigned int			pending_ovl_size[OVL_LAYER_NR];
> > > +	bool				pending_ovl_dirty[OVL_LAYER_NR];
> > 
> > This works since you only touch these in the atomic_commit phase, but the
> > recommend way to do this with atomic is to subclass drm_crtc_state:
> >
> > struct mtk_crtc_state {
> > 	struct drm_crtc_state base;
> > 
> > 	/* all the pending_ stuff above */
> > };
> > 
> > Then you just pass the mtk to your irq handler to do the update.
> 
> I'll move these into mtk_crtc_state, but I'm not sure what you mean with
> the last sentence. Currently I pass the mtk_crtc_ddp_context to the irq
> handler. I can get to the mtk_crtc_state from that.

That works too.

> > > +static int mtk_drm_bind(struct device *dev)
> > > +{
> > > +	return drm_platform_init(&mtk_drm_driver, to_platform_device(dev));
> > 
> > This is deprecated, please use drm_dev_alloc/drm_dev_register instead and
> > remove your ->load driver callback.
> 
> Will replace drm_platform_init with drm_dev_alloc/drm_dev_register and
> integrate mtk_drm_load into mtk_drm_bind.
> 
> > > +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;
> > > +
> > > +	mutex_lock(&dev->struct_mutex);
> > 
> > struct_mutex isn't needed here (gem object lookup and the vma stuff are
> > all protected by looks already). Please drop it.
> [...]
> > > +out:
> > > +	drm_gem_object_unreference(obj);
> > 
> > But then you need unreference_unlocked here.
> 
> Ok, dropped the lock.
> 
> [...]
> > > +int mtk_drm_gem_mmap_buf(struct drm_gem_object *obj, struct vm_area_struct *vma)
> > 
> > This function seems unused.
> 
> Currently unused, yes. I'll move it out of this series.
> 
> > > +{
> > > +	struct drm_device *drm = obj->dev;
> > > +	int ret;
> > > +
> > > +	mutex_lock(&drm->struct_mutex);
> > > +	ret = drm_gem_mmap_obj(obj, obj->size, vma);
> > > +	mutex_unlock(&drm->struct_mutex);
> > 
> > This locking isn't required with latest drm-misc anymore, please rebase
> > onto latest linux-next and drop struct_mutex.
> 
> Will do.
> 
> [...]
> > > +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);
> > 
> > Aside: Why can't you just use cma helpers for gem objects? CMA just
> > essentially means backed by dma_alloc memory. Would avoid a lot of
> > duplicated code I think.
> 
> I suppose we won't need dma_alloc memory eventually, due to the iommu.
> 
> [...]
> > > +static void mtk_plane_atomic_update(struct drm_plane *plane,
> > > +				    struct drm_plane_state *old_state)
> > > +{
> [...]
> > > +	if (plane->fb)
> > > +		drm_framebuffer_unreference(plane->fb);
> > > +	if (state->fb)
> > > +		drm_framebuffer_reference(state->fb);
> > > +	plane->fb = state->fb;
> > 
> > There shouldn't be any need to refcount framebuffers yourself. If that's
> > the case then there's a bug in the drm core/helpers.
> 
> I was still using plane->fb in mtk_drm_crtc_plane_config. I'll check if
> switching to plane->state->fb there will allow me to drop this.

My comment was about the refcounting, not about assigning plane->fb.
Assigning plane->fb = state->fb in your commit functions is allowed (the
drm core will do it if you don't do it), but it's only allowed for
backwards compat with existing drivers. New drivers really should only
look at state->fb and totally ignore plane->fb.

The refcounting otoh is simply buggy since it'll confuse the refcounting
the core does and I think should result in some leaks even.
-Daniel
diff mbox

Patch

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 1a0a8df..9e9987b 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -264,3 +264,5 @@  source "drivers/gpu/drm/sti/Kconfig"
 source "drivers/gpu/drm/amd/amdkfd/Kconfig"
 
 source "drivers/gpu/drm/imx/Kconfig"
+
+source "drivers/gpu/drm/mediatek/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 45e7719..af6b592 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -67,6 +67,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..5343cf1
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/Kconfig
@@ -0,0 +1,16 @@ 
+config DRM_MEDIATEK
+	tristate "DRM Support for Mediatek SoCs"
+	depends on DRM
+	depends on ARCH_MEDIATEK || (ARM && COMPILE_TEST)
+	select MTK_SMI
+	select DRM_PANEL
+	select DRM_MIPI_DSI
+	select DRM_PANEL_SIMPLE
+	select DRM_KMS_HELPER
+	select IOMMU_DMA
+	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..ba6d3fc
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/Makefile
@@ -0,0 +1,10 @@ 
+mediatek-drm-y := mtk_drm_drv.o \
+		  mtk_drm_crtc.o \
+		  mtk_drm_ddp.o \
+		  mtk_drm_ddp_comp.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_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
new file mode 100644
index 0000000..158ca84
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
@@ -0,0 +1,524 @@ 
+/*
+ * 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_plane_helper.h>
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/dma-buf.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reservation.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"
+
+void mtk_drm_crtc_finish_page_flip(struct mtk_drm_crtc *mtk_crtc)
+{
+	struct drm_device *dev = mtk_crtc->base.dev;
+
+	drm_send_vblank_event(dev, mtk_crtc->event->pipe, mtk_crtc->event);
+	drm_crtc_vblank_put(&mtk_crtc->base);
+	mtk_crtc->event = NULL;
+}
+
+static void mtk_drm_crtc_destroy(struct drm_crtc *crtc)
+{
+	drm_crtc_cleanup(crtc);
+}
+
+static bool mtk_drm_crtc_mode_fixup(struct drm_crtc *crtc,
+		const struct drm_display_mode *mode,
+		struct drm_display_mode *adjusted_mode)
+{
+	/* drm framework doesn't check NULL */
+	return true;
+}
+
+static void mtk_drm_crtc_mode_set_nofb(struct drm_crtc *crtc)
+{
+	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
+	struct mtk_crtc_ddp_context *ctx = mtk_crtc->ctx;
+
+	if (WARN_ON(!crtc->state))
+		return;
+
+	ctx->pending_width = crtc->mode.hdisplay;
+	ctx->pending_height = crtc->mode.vdisplay;
+	ctx->pending_config = true;
+}
+
+int mtk_drm_crtc_enable_vblank(struct drm_device *drm, int pipe)
+{
+	struct mtk_drm_private *priv = drm->dev_private;
+	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(priv->crtc[pipe]);
+	struct mtk_crtc_ddp_context *ctx = mtk_crtc->ctx;
+	struct mtk_ddp_comp *ovl = &ctx->ddp_comp[0];
+
+	if (ovl->funcs->comp_enable_vblank)
+		ovl->funcs->comp_enable_vblank(ovl->regs);
+
+	return 0;
+}
+
+void mtk_drm_crtc_disable_vblank(struct drm_device *drm, int pipe)
+{
+	struct mtk_drm_private *priv = drm->dev_private;
+	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(priv->crtc[pipe]);
+	struct mtk_crtc_ddp_context *ctx = mtk_crtc->ctx;
+	struct mtk_ddp_comp *ovl = &ctx->ddp_comp[0];
+
+	if (ovl->funcs->comp_disable_vblank)
+		ovl->funcs->comp_disable_vblank(ovl->regs);
+}
+
+static void mtk_crtc_ddp_power_on(struct mtk_crtc_ddp_context *ctx)
+{
+	int ret;
+	int i;
+
+	DRM_INFO("mtk_crtc_ddp_power_on\n");
+	for (i = 0; i < ctx->ddp_comp_nr; i++) {
+		ret = clk_enable(ctx->ddp_comp[i].clk);
+		if (ret)
+			DRM_ERROR("clk_enable(ctx->ddp_comp_clk[%d])\n", i);
+	}
+}
+
+static void mtk_crtc_ddp_power_off(struct mtk_crtc_ddp_context *ctx)
+{
+	int i;
+
+	DRM_INFO("mtk_crtc_ddp_power_off\n");
+	for (i = 0; i < ctx->ddp_comp_nr; i++)
+		clk_disable(ctx->ddp_comp[i].clk);
+}
+
+static void mtk_crtc_ddp_hw_init(struct mtk_drm_crtc *crtc)
+{
+	struct mtk_crtc_ddp_context *ctx = crtc->ctx;
+	struct drm_device *drm = crtc->base.dev;
+	unsigned int width, height;
+	int ret;
+	int i;
+
+	if (ctx->crtc->base.state) {
+		width = crtc->base.state->adjusted_mode.hdisplay;
+		height = crtc->base.state->adjusted_mode.vdisplay;
+	} else {
+		width = 1920;
+		height = 1080;
+	}
+
+	DRM_INFO("mtk_crtc_ddp_hw_init\n");
+
+	/* disp_mtcmos */
+	ret = pm_runtime_get_sync(drm->dev);
+	if (ret < 0)
+		DRM_ERROR("Failed to enable power domain: %d\n", ret);
+
+	mtk_ddp_clock_on(ctx->mutex_dev);
+	mtk_crtc_ddp_power_on(ctx);
+
+	DRM_INFO("mediatek_ddp_ddp_path_setup\n");
+	for (i = 0; i < (ctx->ddp_comp_nr - 1); i++) {
+		mtk_ddp_add_comp_to_path(ctx->config_regs, ctx->pipe,
+					 ctx->ddp_comp[i].funcs->comp_id,
+					 ctx->ddp_comp[i+1].funcs->comp_id);
+		mtk_ddp_add_comp_to_mutex(ctx->mutex_dev, ctx->pipe,
+					  ctx->ddp_comp[i].funcs->comp_id,
+					  ctx->ddp_comp[i+1].funcs->comp_id);
+	}
+
+	DRM_INFO("ddp_disp_path_power_on %dx%d\n", width, height);
+	for (i = 0; i < ctx->ddp_comp_nr; i++) {
+		struct mtk_ddp_comp *comp = &ctx->ddp_comp[i];
+
+		if (comp->funcs->comp_config)
+			comp->funcs->comp_config(comp->regs, width, height);
+
+		if (comp->funcs->comp_power_on)
+			comp->funcs->comp_power_on(comp->regs);
+	}
+}
+
+static void mtk_crtc_ddp_hw_fini(struct mtk_drm_crtc *crtc)
+{
+	struct mtk_crtc_ddp_context *ctx = crtc->ctx;
+	struct drm_device *drm = crtc->base.dev;
+	struct mtk_ddp_comp *ovl = &ctx->ddp_comp[0];
+	int i;
+
+	DRM_INFO("mtk_crtc_ddp_hw_fini\n");
+	for (i = 0; i < OVL_LAYER_NR; i++) {
+		ctx->pending_ovl_enable[i] = false;
+		ovl->funcs->comp_layer_off(ovl->regs, i);
+	}
+	mtk_crtc_ddp_power_off(ctx);
+	mtk_ddp_clock_off(ctx->mutex_dev);
+
+	/* disp_mtcmos */
+	pm_runtime_put_sync(drm->dev);
+}
+
+static void mtk_drm_crtc_enable(struct drm_crtc *crtc)
+{
+	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
+
+	if (WARN_ON(mtk_crtc->enabled))
+		return;
+
+	mtk_crtc_ddp_hw_init(mtk_crtc);
+
+	drm_crtc_vblank_on(crtc);
+	WARN_ON(drm_crtc_vblank_get(crtc) != 0);
+
+	mtk_crtc->enabled = true;
+}
+
+static void mtk_drm_crtc_disable(struct drm_crtc *crtc)
+{
+	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
+
+	DRM_INFO("mtk_drm_crtc_disable %d\n", crtc->base.id);
+	if (WARN_ON(!mtk_crtc->enabled))
+		return;
+
+	drm_crtc_vblank_put(crtc);
+	drm_crtc_vblank_off(crtc);
+
+	mtk_crtc_ddp_hw_fini(mtk_crtc);
+
+	mtk_crtc->enabled = false;
+}
+
+static void mtk_drm_crtc_atomic_begin(struct drm_crtc *crtc,
+				      struct drm_crtc_state *old_crtc_state)
+{
+	struct drm_crtc_state *state = crtc->state;
+	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
+
+	if (state->event) {
+		state->event->pipe = drm_crtc_index(crtc);
+		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
+		mtk_crtc->event = state->event;
+		state->event = NULL;
+	}
+}
+
+void mtk_drm_crtc_commit(struct mtk_drm_crtc *crtc)
+{
+	struct mtk_crtc_ddp_context *ctx = crtc->ctx;
+	unsigned int i;
+
+	for (i = 0; i < OVL_LAYER_NR; i++)
+		if (ctx->pending_ovl_dirty[i]) {
+			ctx->pending_ovl_config[i] = true;
+			ctx->pending_ovl_dirty[i] = false;
+		}
+}
+
+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);
+
+	mtk_drm_crtc_commit(mtk_crtc);
+}
+
+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			= drm_atomic_helper_crtc_reset,
+	.atomic_duplicate_state	= drm_atomic_helper_crtc_duplicate_state,
+	.atomic_destroy_state	= drm_atomic_helper_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,
+};
+
+struct mtk_drm_crtc *mtk_drm_crtc_create(struct drm_device *drm,
+		struct drm_plane *primary, struct drm_plane *cursor, int pipe,
+		void *ctx)
+{
+	struct mtk_drm_private *priv = drm->dev_private;
+	struct mtk_drm_crtc *mtk_crtc;
+	int ret;
+
+	mtk_crtc = devm_kzalloc(drm->dev, sizeof(*mtk_crtc), GFP_KERNEL);
+	if (!mtk_crtc)
+		return ERR_PTR(-ENOMEM);
+
+	mtk_crtc->pipe = pipe;
+	mtk_crtc->ctx = ctx;
+	mtk_crtc->enabled = false;
+
+	priv->crtc[pipe] = &mtk_crtc->base;
+
+	ret = drm_crtc_init_with_planes(drm, &mtk_crtc->base, primary, cursor,
+			&mtk_crtc_funcs);
+	if (ret)
+		goto err_cleanup_crtc;
+
+	drm_crtc_helper_add(&mtk_crtc->base, &mtk_crtc_helper_funcs);
+
+	return mtk_crtc;
+
+err_cleanup_crtc:
+	drm_crtc_cleanup(&mtk_crtc->base);
+	return ERR_PTR(ret);
+}
+
+void mtk_drm_crtc_plane_config(struct mtk_drm_crtc *crtc, unsigned int idx,
+			       bool enable, dma_addr_t addr)
+{
+	struct mtk_crtc_ddp_context *ctx = crtc->ctx;
+	struct drm_plane *plane = &ctx->planes[idx].base;
+	unsigned int pitch, format;
+	int x, y;
+
+	if (!plane->fb || !plane->state)
+		return;
+
+	pitch = plane->fb->pitches[0];
+	format = plane->fb->pixel_format;
+
+	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;
+	}
+
+	ctx->pending_ovl_enable[idx] = enable;
+	ctx->pending_ovl_addr[idx] = addr;
+	ctx->pending_ovl_pitch[idx] = pitch;
+	ctx->pending_ovl_format[idx] = format;
+	ctx->pending_ovl_x[idx] = x;
+	ctx->pending_ovl_y[idx] = y;
+	ctx->pending_ovl_size[idx] = ctx->planes[idx].disp_size;
+	ctx->pending_ovl_dirty[idx] = true;
+}
+
+static void mtk_crtc_ddp_irq(struct mtk_crtc_ddp_context *ctx)
+{
+	struct mtk_ddp_comp *ovl = &ctx->ddp_comp[0];
+	unsigned int i;
+
+	if (ctx->pending_config) {
+		ctx->pending_config = false;
+
+		for (i = 0; i < OVL_LAYER_NR; i++)
+			ovl->funcs->comp_layer_off(ovl->regs, i);
+		ovl->funcs->comp_config(ovl->regs, ctx->pending_width,
+						   ctx->pending_height);
+	}
+
+	for (i = 0; i < OVL_LAYER_NR; i++) {
+		if (ctx->pending_ovl_config[i]) {
+			if (!ctx->pending_ovl_enable[i])
+				ovl->funcs->comp_layer_off(ovl->regs, i);
+
+			ovl->funcs->comp_layer_config(ovl->regs, i,
+					ctx->pending_ovl_addr[i],
+					ctx->pending_ovl_pitch[i],
+					ctx->pending_ovl_format[i],
+					ctx->pending_ovl_x[i],
+					ctx->pending_ovl_y[i],
+					ctx->pending_ovl_size[i]);
+
+			if (ctx->pending_ovl_enable[i])
+				ovl->funcs->comp_layer_on(ovl->regs, i);
+		}
+
+		ctx->pending_ovl_config[i] = false;
+	}
+
+	drm_handle_vblank(ctx->drm_dev, ctx->pipe);
+}
+
+static irqreturn_t mtk_crtc_ddp_irq_handler(int irq, void *dev_id)
+{
+	struct mtk_crtc_ddp_context *ctx = dev_id;
+	struct mtk_ddp_comp *ovl = &ctx->ddp_comp[0];
+
+	if (ovl->funcs->comp_clear_vblank)
+		ovl->funcs->comp_clear_vblank(ovl->regs);
+
+	if (ctx->pipe >= 0 && ctx->drm_dev)
+		mtk_crtc_ddp_irq(ctx);
+
+	return IRQ_HANDLED;
+}
+
+static int mtk_crtc_ddp_bind(struct device *dev, struct device *master,
+		void *data)
+{
+	struct mtk_crtc_ddp_context *ctx = dev_get_drvdata(dev);
+	struct drm_device *drm_dev = data;
+	struct mtk_drm_private *priv = drm_dev->dev_private;
+	struct device_node *node;
+	enum drm_plane_type type;
+	unsigned int zpos;
+	int ret;
+	int i;
+
+	ctx->drm_dev = drm_dev;
+	ctx->pipe = priv->pipe++;
+	ctx->config_regs = priv->config_regs;
+	ctx->mutex_dev = priv->mutex_dev;
+	ctx->ddp_comp_nr = priv->path_len[ctx->pipe];
+	ctx->ddp_comp =	devm_kmalloc_array(dev, ctx->ddp_comp_nr,
+					   sizeof(*ctx->ddp_comp), GFP_KERNEL);
+
+	/* priv->path[ctx->pipe] starts with this OVL node */
+	priv->comp_node[priv->path[ctx->pipe][0]] = dev->of_node;
+
+	for (i = 0; i < ctx->ddp_comp_nr; i++) {
+		struct mtk_ddp_comp *comp = &ctx->ddp_comp[i];
+		enum mtk_ddp_comp_id comp_id = priv->path[ctx->pipe][i];
+
+		ret = mtk_ddp_comp_init(priv->comp_node[comp_id], comp,
+					comp_id);
+		if (ret) {
+			dev_err(dev, "Failed to initialize component %i\n", i);
+			goto unprepare;
+		}
+
+		if (comp->clk) {
+			ret = clk_prepare(comp->clk);
+			if (ret) {
+				dev_err(dev, "Failed to prepare clock %d\n", i);
+				ret = -EINVAL;
+				goto unprepare;
+			}
+		}
+	}
+
+	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, &ctx->planes[zpos],
+				1 << ctx->pipe, type, zpos, OVL_LAYER_NR);
+		if (ret)
+			goto unprepare;
+	}
+
+	ctx->crtc = mtk_drm_crtc_create(drm_dev, &ctx->planes[0].base,
+			&ctx->planes[1].base, ctx->pipe, ctx);
+
+	if (IS_ERR(ctx->crtc)) {
+		ret = PTR_ERR(ctx->crtc);
+		goto unprepare;
+	}
+
+	return 0;
+
+unprepare:
+	while (--i >= 0)
+		clk_unprepare(ctx->ddp_comp[i].clk);
+	of_node_put(node);
+
+	return ret;
+}
+
+static void mtk_crtc_ddp_unbind(struct device *dev, struct device *master,
+		void *data)
+{
+	struct mtk_crtc_ddp_context *ctx = dev_get_drvdata(dev);
+	int i;
+
+	for (i = 0; i < ctx->ddp_comp_nr; i++)
+		clk_unprepare(ctx->ddp_comp[i].clk);
+}
+
+static const struct component_ops mtk_crtc_ddp_component_ops = {
+	.bind	= mtk_crtc_ddp_bind,
+	.unbind = mtk_crtc_ddp_unbind,
+};
+
+static int mtk_crtc_ddp_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct mtk_crtc_ddp_context *ctx;
+	int irq;
+	int ret;
+
+	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	ret = devm_request_irq(dev, irq, mtk_crtc_ddp_irq_handler,
+			       IRQF_TRIGGER_NONE, dev_name(dev), ctx);
+	if (ret < 0) {
+		dev_err(dev, "Failed to request irq %d: %d\n", irq, ret);
+		return -ENXIO;
+	}
+
+	platform_set_drvdata(pdev, ctx);
+
+	ret = component_add(dev, &mtk_crtc_ddp_component_ops);
+	if (ret)
+		dev_err(dev, "Failed to add component: %d\n", ret);
+
+	return ret;
+}
+
+static int mtk_crtc_ddp_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &mtk_crtc_ddp_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_crtc_ddp_probe,
+	.remove		= mtk_crtc_ddp_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_drm_crtc.h b/drivers/gpu/drm/mediatek/mtk_drm_crtc.h
new file mode 100644
index 0000000..6313f92
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.h
@@ -0,0 +1,84 @@ 
+/*
+ * 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 "mtk_drm_plane.h"
+
+#define OVL_LAYER_NR	4
+
+struct mtk_crtc_ddp_context;
+
+/*
+ * MediaTek specific crtc structure.
+ *
+ * @base: crtc object.
+ * @pipe: a crtc index created at load() with a new crtc object creation
+ *	and the crtc object would be set to private->crtc array
+ *	to get a crtc object corresponding to this pipe from private->crtc
+ *	array when irq interrupt occurred. the reason of using this pipe is that
+ *	drm framework doesn't support multiple irq yet.
+ *	we can refer to the crtc to current hardware interrupt occurred through
+ *	this pipe value.
+ * @enabled: crtc enabled
+ * @ctx: mtk crtc context object
+ * @event: drm pending vblank event
+ * @pending_needs_vblank: Need to send event in vblank.
+ */
+struct mtk_drm_crtc {
+	struct drm_crtc				base;
+	unsigned int				pipe;
+	bool					enabled;
+	struct mtk_crtc_ddp_context		*ctx;
+
+	struct drm_pending_vblank_event		*event;
+	bool					pending_needs_vblank;
+};
+
+struct mtk_crtc_ddp_context {
+	struct device			*dev;
+	struct drm_device		*drm_dev;
+	struct mtk_drm_crtc		*crtc;
+	struct mtk_drm_plane		planes[OVL_LAYER_NR];
+	int				pipe;
+
+	void __iomem			*config_regs;
+	struct device			*mutex_dev;
+	u32				ddp_comp_nr;
+	struct mtk_ddp_comp		*ddp_comp;
+
+	bool				pending_config;
+	unsigned int			pending_width;
+	unsigned int			pending_height;
+
+	bool				pending_ovl_config[OVL_LAYER_NR];
+	bool				pending_ovl_enable[OVL_LAYER_NR];
+	unsigned int			pending_ovl_addr[OVL_LAYER_NR];
+	unsigned int			pending_ovl_pitch[OVL_LAYER_NR];
+	unsigned int			pending_ovl_format[OVL_LAYER_NR];
+	int				pending_ovl_x[OVL_LAYER_NR];
+	int				pending_ovl_y[OVL_LAYER_NR];
+	unsigned int			pending_ovl_size[OVL_LAYER_NR];
+	bool				pending_ovl_dirty[OVL_LAYER_NR];
+};
+
+#define to_mtk_crtc(x) container_of(x, struct mtk_drm_crtc, base)
+
+int mtk_drm_crtc_enable_vblank(struct drm_device *drm, int pipe);
+void mtk_drm_crtc_disable_vblank(struct drm_device *drm, int pipe);
+void mtk_drm_crtc_plane_config(struct mtk_drm_crtc *crtc, unsigned int idx,
+				  bool enable, dma_addr_t addr);
+void mtk_drm_crtc_commit(struct mtk_drm_crtc *crtc);
+
+#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..9ec2960
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
@@ -0,0 +1,218 @@ 
+/*
+ * 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/platform_device.h>
+#include <linux/regmap.h>
+
+#include "mtk_drm_crtc.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_OVL0		11
+#define MUTEX_MOD_OVL1		12
+#define MUTEX_MOD_RDMA0		13
+#define MUTEX_MOD_RDMA1		14
+#define MUTEX_MOD_COLOR0	18
+#define MUTEX_MOD_COLOR1	19
+#define MUTEX_MOD_AAL		20
+#define MUTEX_MOD_GAMMA		21
+#define MUTEX_MOD_UFOE		22
+#define MUTEX_MOD_PWM0		23
+#define MUTEX_MOD_OD		25
+
+#define MUTEX_SOF_DSI0		1
+#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
+
+static const unsigned int mutex_mod[DDP_COMPONENT_ID_MAX] = {
+	[DDP_COMPONENT_AAL] = MUTEX_MOD_AAL,
+	[DDP_COMPONENT_COLOR0] = MUTEX_MOD_COLOR0,
+	[DDP_COMPONENT_COLOR1] = MUTEX_MOD_COLOR1,
+	[DDP_COMPONENT_GAMMA] = MUTEX_MOD_GAMMA,
+	[DDP_COMPONENT_OD] = MUTEX_MOD_OD,
+	[DDP_COMPONENT_OVL0] = MUTEX_MOD_OVL0,
+	[DDP_COMPONENT_OVL1] = MUTEX_MOD_OVL1,
+	[DDP_COMPONENT_PWM0] = MUTEX_MOD_PWM0,
+	[DDP_COMPONENT_RDMA0] = MUTEX_MOD_RDMA0,
+	[DDP_COMPONENT_RDMA1] = MUTEX_MOD_RDMA1,
+	[DDP_COMPONENT_UFOE] = MUTEX_MOD_UFOE,
+};
+
+void mtk_ddp_add_comp_to_path(void __iomem *config_regs, unsigned int pipe,
+			      enum mtk_ddp_comp_id cur,
+			      enum mtk_ddp_comp_id next)
+{
+	unsigned int addr, 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;
+	}
+	if (value) {
+		unsigned int reg = readl(config_regs + addr) | value;
+
+		writel(reg, config_regs + addr);
+	}
+
+	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;
+	}
+	if (value) {
+		unsigned int reg = readl(config_regs + addr) | value;
+
+		writel(reg, config_regs + addr);
+	}
+}
+
+void mtk_ddp_add_comp_to_mutex(struct device *dev, unsigned int pipe,
+			       enum mtk_ddp_comp_id cur,
+			       enum mtk_ddp_comp_id next)
+{
+	struct mtk_ddp *ddp = dev_get_drvdata(dev);
+	unsigned int reg;
+
+	reg = readl(ddp->mutex_regs + DISP_REG_MUTEX_MOD(pipe));
+	reg |= BIT(mutex_mod[cur]) | BIT(mutex_mod[next]);
+	writel(reg, ddp->mutex_regs + DISP_REG_MUTEX_MOD(pipe));
+
+	if (next == DDP_COMPONENT_DPI0)
+		reg = MUTEX_SOF_DPI0;
+	else
+		reg = MUTEX_SOF_DSI0;
+
+	writel(reg, ddp->mutex_regs + DISP_REG_MUTEX_SOF(pipe));
+	writel(1, ddp->mutex_regs + DISP_REG_MUTEX_EN(pipe));
+}
+
+void mtk_ddp_clock_on(struct device *dev)
+{
+	struct mtk_ddp *ddp = dev_get_drvdata(dev);
+	int ret;
+
+	ret = clk_prepare_enable(ddp->mutex_disp_clk);
+	if (ret != 0)
+		DRM_ERROR("clk_prepare_enable(mutex_disp_clk) error!\n");
+}
+
+void mtk_ddp_clock_off(struct device *dev)
+{
+	struct mtk_ddp *ddp = dev_get_drvdata(dev);
+
+	clk_disable_unprepare(ddp->mutex_disp_clk);
+}
+
+static int mtk_ddp_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct mtk_ddp *ddp;
+	struct resource *regs;
+
+	ddp = devm_kzalloc(dev, sizeof(*ddp), GFP_KERNEL);
+	if (!ddp)
+		return -ENOMEM;
+
+	ddp->mutex_disp_clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(ddp->mutex_disp_clk)) {
+		dev_err(dev, "Failed to get clock\n");
+		return PTR_ERR(ddp->mutex_disp_clk);
+	}
+
+	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	ddp->mutex_regs = devm_ioremap_resource(dev, regs);
+	if (IS_ERR(ddp->mutex_regs)) {
+		dev_err(dev, "Failed to map mutex registers\n");
+		return PTR_ERR(ddp->mutex_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,
+	},
+};
+
+module_platform_driver(mtk_ddp_driver);
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..55c2db3
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp.h
@@ -0,0 +1,39 @@ 
+/*
+ * 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_ddp {
+	struct device			*dev;
+	struct drm_device		*drm_dev;
+
+	struct clk			*mutex_disp_clk;
+	void __iomem			*mutex_regs;
+};
+
+void mtk_ddp_add_comp_to_path(void __iomem *config_regs, unsigned int pipe,
+			      enum mtk_ddp_comp_id cur,
+			      enum mtk_ddp_comp_id next);
+void mtk_ddp_add_comp_to_mutex(struct device *dev, unsigned int pipe,
+			       enum mtk_ddp_comp_id cur,
+			       enum mtk_ddp_comp_id next);
+void mtk_ddp_clock_on(struct device *dev);
+void mtk_ddp_clock_off(struct device *dev);
+
+#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..97a84b0
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
@@ -0,0 +1,409 @@ 
+/*
+ * 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_address.h>
+#include <linux/of_irq.h>
+#include <drm/drmP.h>
+#include "mtk_drm_ddp_comp.h"
+
+#define DISP_REG_OVL_INTEN			0x0004
+#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 DISP_REG_RDMA_INT_ENABLE		0x0000
+#define DISP_REG_RDMA_INT_STATUS		0x0004
+#define DISP_REG_RDMA_GLOBAL_CON		0x0010
+#define DISP_REG_RDMA_SIZE_CON_0		0x0014
+#define DISP_REG_RDMA_SIZE_CON_1		0x0018
+#define DISP_REG_RDMA_FIFO_CON			0x0040
+
+#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
+
+enum OVL_INPUT_FORMAT {
+	OVL_INFMT_RGB565 = 0,
+	OVL_INFMT_RGB888 = 1,
+	OVL_INFMT_RGBA8888 = 2,
+	OVL_INFMT_ARGB8888 = 3,
+};
+
+#define	OVL_RDMA_MEM_GMC	0x40402020
+#define	OVL_AEN			BIT(8)
+#define	OVL_ALPHA		0xff
+
+#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_start(void __iomem *color_base)
+{
+	writel(COLOR_BYPASS_ALL | COLOR_SEQ_SEL,
+		color_base + DISP_COLOR_CFG_MAIN);
+	writel(0x1, color_base + DISP_COLOR_START);
+}
+
+static void mtk_od_config(void __iomem *od_base, unsigned int w, unsigned int h)
+{
+	writel(w << 16 | h, od_base + DISP_OD_SIZE);
+}
+
+static void mtk_od_start(void __iomem *od_base)
+{
+	writel(OD_RELAY_MODE, od_base + DISP_OD_CFG);
+	writel(1, od_base + DISP_OD_EN);
+}
+
+static void mtk_ovl_enable_vblank(void __iomem *disp_base)
+{
+	writel(0x2, disp_base + DISP_REG_OVL_INTEN);
+}
+
+static void mtk_ovl_disable_vblank(void __iomem *disp_base)
+{
+	writel(0x0, disp_base + DISP_REG_OVL_INTEN);
+}
+
+static void mtk_ovl_clear_vblank(void __iomem *disp_base)
+{
+	writel(0x0, disp_base + DISP_REG_OVL_INTSTA);
+}
+
+static void mtk_ovl_start(void __iomem *ovl_base)
+{
+	writel(0x1, ovl_base + DISP_REG_OVL_EN);
+}
+
+static void mtk_ovl_config(void __iomem *ovl_base,
+		unsigned int w, unsigned int h)
+{
+	if (w != 0 && h != 0)
+		writel(h << 16 | w, ovl_base + DISP_REG_OVL_ROI_SIZE);
+	writel(0x0, ovl_base + DISP_REG_OVL_ROI_BGCLR);
+
+	writel(0x1, ovl_base + DISP_REG_OVL_RST);
+	writel(0x0, ovl_base + DISP_REG_OVL_RST);
+}
+
+static bool has_rb_swapped(unsigned int fmt)
+{
+	switch (fmt) {
+	case DRM_FORMAT_BGR888:
+	case DRM_FORMAT_BGR565:
+	case DRM_FORMAT_ABGR8888:
+	case DRM_FORMAT_XBGR8888:
+	case DRM_FORMAT_BGRA8888:
+	case DRM_FORMAT_BGRX8888:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static unsigned int ovl_fmt_convert(unsigned int fmt)
+{
+	switch (fmt) {
+	case DRM_FORMAT_RGB888:
+	case DRM_FORMAT_BGR888:
+		return OVL_INFMT_RGB888;
+	case DRM_FORMAT_RGB565:
+	case DRM_FORMAT_BGR565:
+		return OVL_INFMT_RGB565;
+	case DRM_FORMAT_RGBX8888:
+	case DRM_FORMAT_RGBA8888:
+	case DRM_FORMAT_BGRX8888:
+	case DRM_FORMAT_BGRA8888:
+		return OVL_INFMT_ARGB8888;
+	case DRM_FORMAT_XRGB8888:
+	case DRM_FORMAT_ARGB8888:
+	case DRM_FORMAT_XBGR8888:
+	case DRM_FORMAT_ABGR8888:
+		return OVL_INFMT_RGBA8888;
+	default:
+		DRM_ERROR("unsupport format[%08x]\n", fmt);
+		return -EINVAL;
+	}
+}
+
+static void mtk_ovl_layer_on(void __iomem *ovl_base, unsigned int idx)
+{
+	unsigned int reg;
+
+	writel(0x1, ovl_base + DISP_REG_OVL_RDMA_CTRL(idx));
+	writel(OVL_RDMA_MEM_GMC, ovl_base + DISP_REG_OVL_RDMA_GMC(idx));
+
+	reg = readl(ovl_base + DISP_REG_OVL_SRC_CON);
+	reg = reg | (1 << idx);
+	writel(reg, ovl_base + DISP_REG_OVL_SRC_CON);
+}
+
+static void mtk_ovl_layer_off(void __iomem *ovl_base, unsigned int idx)
+{
+	unsigned int reg;
+
+	reg = readl(ovl_base + DISP_REG_OVL_SRC_CON);
+	reg = reg & ~(1 << idx);
+	writel(reg, ovl_base + DISP_REG_OVL_SRC_CON);
+
+	writel(0x0, ovl_base + DISP_REG_OVL_RDMA_CTRL(idx));
+}
+
+static void mtk_ovl_layer_config(void __iomem *ovl_base, unsigned int idx,
+		unsigned int addr, unsigned int pitch, unsigned int fmt,
+		int x, int y, unsigned int size)
+{
+	unsigned int reg;
+
+	reg = has_rb_swapped(fmt) << 24 | ovl_fmt_convert(fmt) << 12;
+	if (idx != 0)
+		reg |= OVL_AEN | OVL_ALPHA;
+
+	writel(reg, ovl_base + DISP_REG_OVL_CON(idx));
+	writel(pitch & 0xFFFF, ovl_base + DISP_REG_OVL_PITCH(idx));
+	writel(size, ovl_base + DISP_REG_OVL_SRC_SIZE(idx));
+	writel(y << 16 | x, ovl_base + DISP_REG_OVL_OFFSET(idx));
+	writel(addr, ovl_base + DISP_REG_OVL_ADDR(idx));
+}
+
+static void mtk_rdma_start(void __iomem *rdma_base)
+{
+	unsigned int reg;
+
+	writel(0x4, rdma_base + DISP_REG_RDMA_INT_ENABLE);
+	reg = readl(rdma_base + DISP_REG_RDMA_GLOBAL_CON);
+	reg |= 1;
+	writel(reg, rdma_base + DISP_REG_RDMA_GLOBAL_CON);
+}
+
+static void mtk_rdma_config(void __iomem *rdma_base,
+		unsigned width, unsigned height)
+{
+	unsigned int reg;
+
+	reg = readl(rdma_base + DISP_REG_RDMA_SIZE_CON_0);
+	reg = (reg & ~(0xFFF)) | (width & 0xFFF);
+	writel(reg, rdma_base + DISP_REG_RDMA_SIZE_CON_0);
+
+	reg = readl(rdma_base + DISP_REG_RDMA_SIZE_CON_1);
+	reg = (reg & ~(0xFFFFF)) | (height & 0xFFFFF);
+	writel(reg, rdma_base + DISP_REG_RDMA_SIZE_CON_1);
+
+	writel(0x80F00008, rdma_base + DISP_REG_RDMA_FIFO_CON);
+}
+
+static void mtk_ufoe_start(void __iomem *ufoe_base)
+{
+	writel(UFO_BYPASS, ufoe_base + DISP_REG_UFO_START);
+}
+
+static const struct mtk_ddp_comp_funcs ddp_aal = {
+	.comp_id = DDP_COMPONENT_AAL,
+};
+
+static const struct mtk_ddp_comp_funcs ddp_color0 = {
+	.comp_id = DDP_COMPONENT_COLOR0,
+	.comp_power_on = mtk_color_start,
+};
+
+static const struct mtk_ddp_comp_funcs ddp_color1 = {
+	.comp_id = DDP_COMPONENT_COLOR1,
+	.comp_power_on = mtk_color_start,
+};
+
+static const struct mtk_ddp_comp_funcs ddp_dpi0 = {
+	.comp_id = DDP_COMPONENT_DPI0,
+};
+
+static const struct mtk_ddp_comp_funcs ddp_dsi0 = {
+	.comp_id = DDP_COMPONENT_DSI0,
+};
+
+static const struct mtk_ddp_comp_funcs ddp_dsi1 = {
+	.comp_id = DDP_COMPONENT_DSI1,
+};
+
+static const struct mtk_ddp_comp_funcs ddp_gamma = {
+	.comp_id = DDP_COMPONENT_GAMMA,
+};
+
+static const struct mtk_ddp_comp_funcs ddp_od = {
+	.comp_id = DDP_COMPONENT_OD,
+	.comp_config = mtk_od_config,
+	.comp_power_on = mtk_od_start,
+};
+
+static const struct mtk_ddp_comp_funcs ddp_ovl0 = {
+	.comp_id = DDP_COMPONENT_OVL0,
+	.comp_config = mtk_ovl_config,
+	.comp_power_on = mtk_ovl_start,
+	.comp_enable_vblank = mtk_ovl_enable_vblank,
+	.comp_disable_vblank = mtk_ovl_disable_vblank,
+	.comp_clear_vblank = mtk_ovl_clear_vblank,
+	.comp_layer_on = mtk_ovl_layer_on,
+	.comp_layer_off = mtk_ovl_layer_off,
+	.comp_layer_config = mtk_ovl_layer_config,
+};
+
+static const struct mtk_ddp_comp_funcs ddp_ovl1 = {
+	.comp_id = DDP_COMPONENT_OVL1,
+	.comp_config = mtk_ovl_config,
+	.comp_power_on = mtk_ovl_start,
+	.comp_enable_vblank = mtk_ovl_enable_vblank,
+	.comp_disable_vblank = mtk_ovl_disable_vblank,
+	.comp_clear_vblank = mtk_ovl_clear_vblank,
+	.comp_layer_on = mtk_ovl_layer_on,
+	.comp_layer_off = mtk_ovl_layer_off,
+	.comp_layer_config = mtk_ovl_layer_config,
+};
+
+static const struct mtk_ddp_comp_funcs ddp_pwm0 = {
+	.comp_id = DDP_COMPONENT_PWM0,
+};
+
+static const struct mtk_ddp_comp_funcs ddp_rdma0 = {
+	.comp_id = DDP_COMPONENT_RDMA0,
+	.comp_config = mtk_rdma_config,
+	.comp_power_on = mtk_rdma_start,
+};
+
+static const struct mtk_ddp_comp_funcs ddp_rdma1 = {
+	.comp_id = DDP_COMPONENT_RDMA1,
+	.comp_config = mtk_rdma_config,
+	.comp_power_on = mtk_rdma_start,
+};
+
+static const struct mtk_ddp_comp_funcs ddp_rdma2 = {
+	.comp_id = DDP_COMPONENT_RDMA2,
+	.comp_config = mtk_rdma_config,
+	.comp_power_on = mtk_rdma_start,
+};
+
+static const struct mtk_ddp_comp_funcs ddp_ufoe = {
+	.comp_id = DDP_COMPONENT_UFOE,
+	.comp_power_on = mtk_ufoe_start,
+};
+
+static const struct mtk_ddp_comp_funcs ddp_wdma0 = {
+	.comp_id = DDP_COMPONENT_WDMA0,
+};
+
+static const struct mtk_ddp_comp_funcs ddp_wdma1 = {
+	.comp_id = DDP_COMPONENT_WDMA1,
+};
+
+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 struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_ID_MAX] = {
+	[DDP_COMPONENT_AAL]	= { MTK_DISP_AAL,	0, &ddp_aal },
+	[DDP_COMPONENT_COLOR0]	= { MTK_DISP_COLOR,	0, &ddp_color0 },
+	[DDP_COMPONENT_COLOR1]	= { MTK_DISP_COLOR,	1, &ddp_color1 },
+	[DDP_COMPONENT_DPI0]	= { MTK_DPI,		0, &ddp_dpi0 },
+	[DDP_COMPONENT_DSI0]	= { MTK_DSI,		0, &ddp_dsi0 },
+	[DDP_COMPONENT_DSI1]	= { MTK_DSI,		1, &ddp_dsi1 },
+	[DDP_COMPONENT_GAMMA]	= { MTK_DISP_GAMMA,	0, &ddp_gamma },
+	[DDP_COMPONENT_OD]	= { MTK_DISP_OD,	0, &ddp_od },
+	[DDP_COMPONENT_OVL0]	= { MTK_DISP_OVL,	0, &ddp_ovl0 },
+	[DDP_COMPONENT_OVL1]	= { MTK_DISP_OVL,	1, &ddp_ovl1 },
+	[DDP_COMPONENT_PWM0]	= { MTK_DISP_PWM,	0, &ddp_pwm0 },
+	[DDP_COMPONENT_RDMA0]	= { MTK_DISP_RDMA,	0, &ddp_rdma0 },
+	[DDP_COMPONENT_RDMA1]	= { MTK_DISP_RDMA,	1, &ddp_rdma1 },
+	[DDP_COMPONENT_RDMA2]	= { MTK_DISP_RDMA,	2, &ddp_rdma2 },
+	[DDP_COMPONENT_UFOE]	= { MTK_DISP_UFOE,	0, &ddp_ufoe },
+	[DDP_COMPONENT_WDMA0]	= { MTK_DISP_WDMA,	0, &ddp_wdma0 },
+	[DDP_COMPONENT_WDMA1]	= { MTK_DISP_WDMA,	1, &ddp_wdma1 },
+};
+
+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_node *node, struct mtk_ddp_comp *comp,
+		      enum mtk_ddp_comp_id comp_id)
+{
+	if (comp_id < 0 || comp_id >= DDP_COMPONENT_ID_MAX)
+		return -EINVAL;
+
+	comp->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;
+
+	return 0;
+}
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..e2d0c39
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
@@ -0,0 +1,86 @@ 
+/*
+ * 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_node;
+
+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_RDMA0,
+	DDP_COMPONENT_RDMA1,
+	DDP_COMPONENT_RDMA2,
+	DDP_COMPONENT_UFOE,
+	DDP_COMPONENT_WDMA0,
+	DDP_COMPONENT_WDMA1,
+	DDP_COMPONENT_ID_MAX,
+};
+
+struct mtk_ddp_comp_funcs {
+	enum mtk_ddp_comp_id comp_id;
+	void (*comp_config)(void __iomem *ovl_base,
+			unsigned int w, unsigned int h);
+	void (*comp_power_on)(void __iomem *ovl_base);
+	void (*comp_power_off)(void __iomem *ovl_base);
+	void (*comp_enable_vblank)(void __iomem *ovl_base);
+	void (*comp_disable_vblank)(void __iomem *ovl_base);
+	void (*comp_clear_vblank)(void __iomem *ovl_base);
+	void (*comp_layer_on)(void __iomem *ovl_base, unsigned int idx);
+	void (*comp_layer_off)(void __iomem *ovl_base, unsigned int idx);
+	void (*comp_layer_config)(void __iomem *ovl_base, unsigned int idx,
+			unsigned int addr, unsigned int pitch, unsigned int fmt,
+			int x, int y, unsigned int size);
+};
+
+struct mtk_ddp_comp {
+	struct clk *clk;
+	void __iomem *regs;
+	int irq;
+	const struct mtk_ddp_comp_funcs *funcs;
+};
+
+int mtk_ddp_comp_get_id(struct device_node *node,
+			enum mtk_ddp_comp_type comp_type);
+int mtk_ddp_comp_init(struct device_node *node, struct mtk_ddp_comp *comp,
+		      enum mtk_ddp_comp_id comp_id);
+
+#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..bb03b4a
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@ -0,0 +1,556 @@ 
+/*
+ * 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 <linux/component.h>
+#include <linux/dma-iommu.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+#include <soc/mediatek/smi.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"
+
+#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_complete(struct mtk_drm_private *private,
+				struct drm_atomic_state *state)
+{
+	struct drm_device *drm = private->drm;
+
+	drm_atomic_helper_commit_modeset_disables(drm, state);
+	drm_atomic_helper_commit_planes(drm, state);
+	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;
+	int ret;
+	int i;
+
+	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;
+
+	for (i = 0; i < MAX_CRTC; i++) {
+		if (!private->larb_node[i])
+			break;
+
+		pdev = of_find_device_by_node(private->larb_node[i]);
+		if (!pdev) {
+			dev_err(drm->dev, "Waiting for larb device %s\n",
+				private->larb_node[i]->full_name);
+			return -EPROBE_DEFER;
+		}
+		private->larb_dev[i] = &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;
+
+	/*
+	 * We currently support two fixed data streams,
+	 * OVL0 -> COLOR0 -> AAL -> OD -> RDMA0 -> UFOE -> DSI0
+	 * and OVL1 -> COLOR1 -> GAMMA -> RDMA1 -> DPI0.
+	 */
+	private->path_len[0] = ARRAY_SIZE(mtk_ddp_main);
+	private->path[0] = mtk_ddp_main;
+	private->path_len[1] = ARRAY_SIZE(mtk_ddp_ext);
+	private->path[1] = mtk_ddp_ext;
+
+	ret = component_bind_all(drm->dev, drm);
+	if (ret)
+		goto err_crtc;
+
+	/*
+	 * 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_unbind;
+
+	for (i = 0; i < MAX_CRTC; i++) {
+		if (!private->larb_dev[i])
+			break;
+
+		ret = mtk_smi_larb_get(private->larb_dev[i]);
+		if (ret) {
+			DRM_ERROR("Failed to get larb: %d\n", ret);
+			goto err_larb_get;
+		}
+	}
+
+	drm_kms_helper_poll_init(drm);
+	drm_mode_config_reset(drm);
+
+	return 0;
+
+err_larb_get:
+	for (i = i - 1; i >= 0; i--)
+		mtk_smi_larb_put(private->larb_dev[i]);
+	drm_kms_helper_poll_fini(drm);
+	drm_vblank_cleanup(drm);
+err_unbind:
+	component_unbind_all(drm->dev, drm);
+err_crtc:
+	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);
+	drm_mode_config_cleanup(drm);
+}
+
+static int mtk_drm_load(struct drm_device *drm, unsigned long flags)
+{
+	struct device *dev = drm->dev;
+	struct mtk_drm_private *private = dev_get_drvdata(dev);
+
+	drm->dev_private = private;
+	private->drm = drm;
+
+	return mtk_drm_kms_init(drm);
+}
+
+static int mtk_drm_unload(struct drm_device *drm)
+{
+	mtk_drm_kms_deinit(drm);
+	drm->dev_private = NULL;
+
+	return 0;
+}
+
+static const struct vm_operations_struct mtk_drm_gem_vm_ops = {
+	.open = drm_gem_vm_open,
+	.close = drm_gem_vm_close,
+};
+
+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,
+	.load = mtk_drm_load,
+	.unload = mtk_drm_unload,
+	.set_busid = drm_platform_set_busid,
+
+	.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 = &mtk_drm_gem_vm_ops,
+	.dumb_create = mtk_drm_gem_dumb_create,
+	.dumb_map_offset = mtk_drm_gem_dumb_map_offset,
+	.dumb_destroy = drm_gem_dumb_destroy,
+
+	.fops = &mtk_drm_fops,
+
+	.set_busid = drm_platform_set_busid,
+
+	.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)
+{
+	return drm_platform_init(&mtk_drm_driver, to_platform_device(dev));
+}
+
+static void mtk_drm_unbind(struct device *dev)
+{
+	struct mtk_drm_private *private = dev_get_drvdata(dev);
+
+	drm_put_dev(private->drm);
+}
+
+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-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 num_larbs = 0;
+
+	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;
+
+		comp_type = (enum mtk_ddp_comp_type)of_id->data;
+
+		if (comp_type == MTK_DISP_MUTEX) {
+			private->mutex_node = of_node_get(node);
+			continue;
+		}
+
+		if (!of_device_is_available(node)) {
+			dev_dbg(dev, "Skipping disabled component %s\n",
+				node->full_name);
+			continue;
+		}
+
+		comp_id = mtk_ddp_comp_get_id(node, comp_type);
+		if (comp_id < 0) {
+			dev_info(dev, "Skipping unknown component %s\n",
+				 node->full_name);
+			continue;
+		}
+
+		/*
+		 * Currently only the OVL, DSI, and DPI blocks have separate
+		 * component platform drivers.
+		 */
+		if (comp_type == MTK_DISP_OVL ||
+		    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);
+		}
+
+		private->comp_node[comp_id] = of_node_get(node);
+
+		if (comp_type == MTK_DISP_OVL) {
+			struct device_node *larb_node;
+
+			larb_node = of_parse_phandle(node, "mediatek,larb", 0);
+			if (larb_node && num_larbs < MAX_CRTC)
+				private->larb_node[num_larbs++] = larb_node;
+		}
+	}
+
+	if (!private->mutex_node) {
+		dev_err(dev, "Failed to find disp-mutex node\n");
+		return -ENODEV;
+	}
+
+	pm_runtime_enable(dev);
+
+	platform_set_drvdata(pdev, private);
+
+	return component_master_add_with_match(dev, &mtk_drm_ops, match);
+}
+
+static int mtk_drm_remove(struct platform_device *pdev)
+{
+	component_master_del(&pdev->dev, &mtk_drm_ops);
+	pm_runtime_disable(&pdev->dev);
+
+	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;
+	struct drm_connector *conn;
+	int i;
+
+	drm_kms_helper_poll_disable(drm);
+
+	drm_modeset_lock_all(drm);
+	list_for_each_entry(conn, &drm->mode_config.connector_list, head) {
+		int old_dpms = conn->dpms;
+
+		if (conn->funcs->dpms)
+			conn->funcs->dpms(conn, DRM_MODE_DPMS_OFF);
+
+		/* Set the old mode back to the connector for resume */
+		conn->dpms = old_dpms;
+	}
+	drm_modeset_unlock_all(drm);
+
+	for (i = 0; i < MAX_CRTC; i++) {
+		if (!private->larb_dev[i])
+			break;
+
+		mtk_smi_larb_put(private->larb_dev[i]);
+	}
+
+	DRM_INFO("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;
+	struct drm_connector *conn;
+	int ret;
+	int i;
+
+	for (i = 0; i < MAX_CRTC; i++) {
+		if (!private->larb_dev[i])
+			break;
+
+		ret = mtk_smi_larb_get(private->larb_dev[i]);
+		if (ret) {
+			DRM_ERROR("mtk_smi_larb_get fail %d\n", ret);
+			return ret;
+		}
+	}
+
+	drm_modeset_lock_all(drm);
+	list_for_each_entry(conn, &drm->mode_config.connector_list, head) {
+		int desired_mode = conn->dpms;
+
+		/*
+		 * at suspend time, we save dpms to connector->dpms,
+		 * restore the old_dpms, and at current time, the connector
+		 * dpms status must be DRM_MODE_DPMS_OFF.
+		 */
+		conn->dpms = DRM_MODE_DPMS_OFF;
+
+		/*
+		 * If the connector has been disconnected during suspend,
+		 * disconnect it from the encoder and leave it off. We'll notify
+		 * userspace at the end.
+		 */
+		if (conn->funcs->dpms)
+			conn->funcs->dpms(conn, desired_mode);
+	}
+	drm_modeset_unlock_all(drm);
+
+	drm_kms_helper_poll_enable(drm);
+
+	DRM_INFO("mtk_drm_sys_resume\n");
+	return 0;
+}
+
+SIMPLE_DEV_PM_OPS(mtk_drm_pm_ops, mtk_drm_sys_suspend, mtk_drm_sys_resume);
+#endif
+
+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,
+#ifdef CONFIG_PM_SLEEP
+		.pm     = &mtk_drm_pm_ops,
+#endif
+	},
+};
+
+static int __init mtk_drm_init(void)
+{
+	int ret;
+
+	ret = platform_driver_register(&mtk_drm_platform_driver);
+	if (ret < 0) {
+		pr_err("Failed to register DRM platform driver: %d\n", ret);
+		goto err;
+	}
+
+	ret = platform_driver_register(&mtk_disp_ovl_driver);
+	if (ret < 0) {
+		pr_err("Failed to register OVL platform driver: %d\n", ret);
+		goto drm_err;
+	}
+
+	return 0;
+
+drm_err:
+	platform_driver_unregister(&mtk_drm_platform_driver);
+err:
+	return ret;
+}
+
+static void __exit mtk_drm_exit(void)
+{
+	platform_driver_unregister(&mtk_disp_ovl_driver);
+	platform_driver_unregister(&mtk_drm_platform_driver);
+}
+
+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..5e5128e
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
@@ -0,0 +1,61 @@ 
+/*
+ * 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_fb_helper *fb_helper;
+	struct drm_device *drm;
+
+	/*
+	 * created crtc object would be contained at this array and
+	 * this array is used to be aware of which crtc did it request vblank.
+	 */
+	struct drm_crtc *crtc[MAX_CRTC];
+	struct drm_property *plane_zpos_property;
+	unsigned int pipe;
+
+	struct device_node *larb_node[MAX_CRTC];
+	struct device *larb_dev[MAX_CRTC];
+	struct device_node *mutex_node;
+	struct device *mutex_dev;
+	void __iomem *config_regs;
+	unsigned int path_len[MAX_CRTC];
+	const enum mtk_ddp_comp_id *path[MAX_CRTC];
+	struct device_node *comp_node[DDP_COMPONENT_ID_MAX];
+
+	struct {
+		struct drm_atomic_state *state;
+		struct work_struct work;
+		struct mutex lock;
+	} commit;
+};
+
+extern struct platform_driver mtk_disp_ovl_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..dfa931b
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_fb.c
@@ -0,0 +1,151 @@ 
+/*
+ * 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 "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;
+	struct drm_gem_object	*gem_obj[MAX_FB_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,
+					  unsigned int plane)
+{
+	struct mtk_drm_fb *mtk_fb = to_mtk_fb(fb);
+
+	if (plane >= MAX_FB_OBJ)
+		return NULL;
+
+	return mtk_fb->gem_obj[plane];
+}
+
+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[0], handle);
+}
+
+static void mtk_drm_fb_destroy(struct drm_framebuffer *fb)
+{
+	unsigned int i;
+	struct mtk_drm_fb *mtk_fb = to_mtk_fb(fb);
+	struct drm_gem_object *gem;
+	int nr = drm_format_num_planes(fb->pixel_format);
+
+	drm_framebuffer_cleanup(fb);
+
+	for (i = 0; i < nr; i++) {
+		gem = mtk_fb->gem_obj[i];
+		drm_gem_object_unreference_unlocked(gem);
+	}
+
+	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,
+						   struct drm_mode_fb_cmd2 *mode,
+						   struct drm_gem_object **obj)
+{
+	struct mtk_drm_fb *mtk_fb;
+	unsigned int i;
+	int ret;
+
+	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);
+
+	for (i = 0; i < drm_format_num_planes(mode->pixel_format); i++)
+		mtk_fb->gem_obj[i] = obj[i];
+
+	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;
+}
+
+struct drm_framebuffer *mtk_drm_mode_fb_create(struct drm_device *dev,
+					       struct drm_file *file,
+					       struct drm_mode_fb_cmd2 *cmd)
+{
+	unsigned int hsub, vsub, i;
+	struct mtk_drm_fb *mtk_fb;
+	struct drm_gem_object *gem[MAX_FB_OBJ];
+	int err;
+
+	hsub = drm_format_horz_chroma_subsampling(cmd->pixel_format);
+	vsub = drm_format_vert_chroma_subsampling(cmd->pixel_format);
+	for (i = 0; i < drm_format_num_planes(cmd->pixel_format); i++) {
+		unsigned int width = cmd->width / (i ? hsub : 1);
+		unsigned int height = cmd->height / (i ? vsub : 1);
+		unsigned int size, bpp;
+
+		gem[i] = drm_gem_object_lookup(dev, file, cmd->handles[i]);
+		if (!gem[i]) {
+			err = -ENOENT;
+			goto unreference;
+		}
+
+		bpp = drm_format_plane_cpp(cmd->pixel_format, i);
+		size = (height - 1) * cmd->pitches[i] + width * bpp;
+		size += cmd->offsets[i];
+
+		if (gem[i]->size < size) {
+			drm_gem_object_unreference_unlocked(gem[i]);
+			err = -EINVAL;
+			goto unreference;
+		}
+	}
+
+	mtk_fb = mtk_drm_framebuffer_init(dev, cmd, gem);
+	if (IS_ERR(mtk_fb)) {
+		err = PTR_ERR(mtk_fb);
+		goto unreference;
+	}
+
+	return &mtk_fb->base;
+
+unreference:
+	while (i--)
+		drm_gem_object_unreference_unlocked(gem[i]);
+
+	return ERR_PTR(err);
+}
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..9ce7307
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_fb.h
@@ -0,0 +1,29 @@ 
+/*
+ * 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
+
+#define MAX_FB_OBJ	3
+
+struct drm_gem_object *mtk_fb_get_gem_obj(struct drm_framebuffer *fb,
+					  unsigned int plane);
+struct drm_framebuffer *mtk_drm_mode_fb_create(struct drm_device *dev,
+					       struct drm_file *file,
+					       struct drm_mode_fb_cmd2 *cmd);
+
+void mtk_drm_mode_output_poll_changed(struct drm_device *dev);
+int mtk_fbdev_create(struct drm_device *dev);
+void mtk_fbdev_destroy(struct drm_device *dev);
+
+#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..fedc896
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_gem.c
@@ -0,0 +1,208 @@ 
+/*
+ * 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 "mtk_drm_gem.h"
+
+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,
+					   unsigned long size, bool alloc_kmap)
+{
+	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(dev->dev, obj->size,
+				(dma_addr_t *)&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_INFO("cookie = %p dma_addr = %llx\n",
+			mtk_gem->cookie, mtk_gem->dma_addr);
+
+	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);
+
+	drm_gem_free_mmap_offset(obj);
+
+	/* release file pointer to gem object. */
+	drm_gem_object_release(obj);
+
+	dma_free_attrs(obj->dev->dev, obj->size, mtk_gem->cookie,
+		       mtk_gem->dma_addr, &mtk_gem->dma_attrs);
+
+	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 = args->width * DIV_ROUND_UP(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;
+
+	mutex_lock(&dev->struct_mutex);
+
+	obj = drm_gem_object_lookup(dev, file_priv, handle);
+	if (!obj) {
+		DRM_ERROR("failed to lookup gem object.\n");
+		ret = -EINVAL;
+		goto unlock;
+	}
+
+	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(obj);
+unlock:
+	mutex_unlock(&dev->struct_mutex);
+	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 drm_device *drm = obj->dev;
+
+	/*
+	 * dma_alloc_attrs() allocated a struct page table for rk_obj, 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(drm->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)
+{
+	struct drm_device *drm = obj->dev;
+	int ret;
+
+	mutex_lock(&drm->struct_mutex);
+	ret = drm_gem_mmap_obj(obj, obj->size, vma);
+	mutex_unlock(&drm->struct_mutex);
+	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);
+}
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..fb7953e
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_gem.h
@@ -0,0 +1,56 @@ 
+/*
+ * 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 __iomem		*cookie;
+	void __iomem		*kvaddr;
+	dma_addr_t		dma_addr;
+	struct dma_attrs	dma_attrs;
+};
+
+#define to_mtk_gem_obj(x)	container_of(x, struct mtk_drm_gem_obj, base)
+
+struct mtk_drm_gem_obj *mtk_drm_gem_init(struct drm_device *dev,
+		unsigned long size);
+void mtk_drm_gem_free_object(struct drm_gem_object *gem);
+struct mtk_drm_gem_obj *mtk_drm_gem_create(struct drm_device *dev,
+		unsigned long 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);
+
+#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..704a65c
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_plane.c
@@ -0,0 +1,175 @@ 
+/*
+ * 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 <linux/dma-buf.h>
+#include <linux/reservation.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 uint32_t formats[] = {
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_RGB565,
+};
+
+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 = drm_atomic_helper_plane_reset,
+	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_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;
+	int ret;
+	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, 0)) {
+		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;
+
+	ret = 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);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static void mtk_plane_atomic_update(struct drm_plane *plane,
+				    struct drm_plane_state *old_state)
+{
+	struct drm_plane_state *state = plane->state;
+	struct drm_gem_object *gem_obj;
+	struct mtk_drm_crtc *mtk_crtc;
+	struct mtk_drm_plane *mtk_plane = to_mtk_plane(plane);
+	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 clip = { 0, };
+
+	if (!state->crtc)
+		return;
+
+	clip.x2 = state->crtc->state->mode.hdisplay;
+	clip.y2 = state->crtc->state->mode.vdisplay;
+	drm_rect_intersect(&dest, &clip);
+	mtk_plane->disp_size = (dest.y2 - dest.y1) << 16 | (dest.x2 - dest.x1);
+
+	mtk_crtc = to_mtk_crtc(state->crtc);
+
+	if (plane->fb)
+		drm_framebuffer_unreference(plane->fb);
+	if (state->fb)
+		drm_framebuffer_reference(state->fb);
+	plane->fb = state->fb;
+
+	gem_obj = mtk_fb_get_gem_obj(state->fb, 0);
+	mtk_plane->flip_obj = to_mtk_gem_obj(gem_obj);
+	mtk_plane->mtk_crtc = mtk_crtc;
+
+	if (mtk_plane->flip_obj)
+		mtk_drm_crtc_plane_config(mtk_crtc, mtk_plane->idx, true,
+				mtk_plane->flip_obj->dma_addr);
+}
+
+static void mtk_plane_atomic_disable(struct drm_plane *plane,
+				     struct drm_plane_state *old_state)
+{
+	struct mtk_drm_plane *mtk_plane = to_mtk_plane(plane);
+	struct mtk_drm_crtc *mtk_crtc;
+
+	if (!old_state->crtc)
+		return;
+
+	mtk_crtc = to_mtk_crtc(old_state->crtc);
+
+	mtk_drm_crtc_plane_config(mtk_crtc, mtk_plane->idx, false, 0);
+
+	mtk_drm_crtc_commit(mtk_crtc);
+}
+
+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, unsigned int max_plane)
+{
+	int err;
+
+	err = drm_universal_plane_init(dev, &mtk_plane->base, possible_crtcs,
+			&mtk_plane_funcs, formats, ARRAY_SIZE(formats), type);
+
+	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..80075d493
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_plane.h
@@ -0,0 +1,38 @@ 
+/*
+ * 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_
+
+struct fence;
+struct mtk_crtc;
+struct mtk_drm_gem_obj;
+
+#define to_mtk_plane(x)	container_of(x, struct mtk_drm_plane, base)
+
+struct mtk_drm_plane {
+	struct drm_plane		base;
+	struct mtk_drm_crtc		*mtk_crtc;
+	unsigned int			idx;
+	unsigned int			disp_size;
+
+	struct mtk_drm_gem_obj		*flip_obj;
+};
+
+void mtk_plane_finish_page_flip(struct mtk_drm_plane *mtk_plane);
+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, unsigned int max_plane);
+
+#endif