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

From: CK Hu
Date: Wed May 13 2015 - 11:24:10 EST


This patch is a DRM Driver for Mediatek SoC MT8173.
Now support one crtc with MIPI DSI interface.
We used GEM framework for buffer management and use iommu for
physically non-continuous memory.

Signed-off-by: CK Hu <ck.hu@xxxxxxxxxxxx>
---
drivers/gpu/drm/Kconfig | 2 +
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/mediatek/Kconfig | 28 +
drivers/gpu/drm/mediatek/Makefile | 13 +
drivers/gpu/drm/mediatek/mediatek_drm_crtc.c | 246 ++++
drivers/gpu/drm/mediatek/mediatek_drm_crtc.h | 80 ++
drivers/gpu/drm/mediatek/mediatek_drm_crtc_main.c | 420 +++++++
drivers/gpu/drm/mediatek/mediatek_drm_ddp.c | 202 ++++
drivers/gpu/drm/mediatek/mediatek_drm_ddp.h | 23 +
drivers/gpu/drm/mediatek/mediatek_drm_ddp_comp.c | 346 ++++++
drivers/gpu/drm/mediatek/mediatek_drm_ddp_comp.h | 33 +
drivers/gpu/drm/mediatek/mediatek_drm_drv.c | 369 ++++++
drivers/gpu/drm/mediatek/mediatek_drm_drv.h | 37 +
drivers/gpu/drm/mediatek/mediatek_drm_dsi.c | 1333 +++++++++++++++++++++
drivers/gpu/drm/mediatek/mediatek_drm_dsi.h | 71 ++
drivers/gpu/drm/mediatek/mediatek_drm_fb.c | 339 ++++++
drivers/gpu/drm/mediatek/mediatek_drm_fb.h | 43 +
drivers/gpu/drm/mediatek/mediatek_drm_gem.c | 315 +++++
drivers/gpu/drm/mediatek/mediatek_drm_gem.h | 94 ++
include/uapi/drm/mediatek_drm.h | 59 +
20 files changed, 4054 insertions(+)
create mode 100644 drivers/gpu/drm/mediatek/Kconfig
create mode 100644 drivers/gpu/drm/mediatek/Makefile
create mode 100644 drivers/gpu/drm/mediatek/mediatek_drm_crtc.c
create mode 100644 drivers/gpu/drm/mediatek/mediatek_drm_crtc.h
create mode 100644 drivers/gpu/drm/mediatek/mediatek_drm_crtc_main.c
create mode 100644 drivers/gpu/drm/mediatek/mediatek_drm_ddp.c
create mode 100644 drivers/gpu/drm/mediatek/mediatek_drm_ddp.h
create mode 100644 drivers/gpu/drm/mediatek/mediatek_drm_ddp_comp.c
create mode 100644 drivers/gpu/drm/mediatek/mediatek_drm_ddp_comp.h
create mode 100644 drivers/gpu/drm/mediatek/mediatek_drm_drv.c
create mode 100644 drivers/gpu/drm/mediatek/mediatek_drm_drv.h
create mode 100644 drivers/gpu/drm/mediatek/mediatek_drm_dsi.c
create mode 100644 drivers/gpu/drm/mediatek/mediatek_drm_dsi.h
create mode 100644 drivers/gpu/drm/mediatek/mediatek_drm_fb.c
create mode 100644 drivers/gpu/drm/mediatek/mediatek_drm_fb.h
create mode 100644 drivers/gpu/drm/mediatek/mediatek_drm_gem.c
create mode 100644 drivers/gpu/drm/mediatek/mediatek_drm_gem.h
create mode 100644 include/uapi/drm/mediatek_drm.h

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 47f2ce8..441be2d 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -217,3 +217,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 7d4944e..55fe66c 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..fa581fb
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/Kconfig
@@ -0,0 +1,28 @@
+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.
+
+config DRM_MEDIATEK_FBDEV
+ bool "Enable legacy fbdev support for Mediatek DRM"
+ depends on DRM_MEDIATEK
+ select FB_SYS_FILLRECT
+ select FB_SYS_COPYAREA
+ select FB_SYS_IMAGEBLIT
+ select DRM_KMS_FB_HELPER
+ help
+ Choose this option if you have a need for the legacy
+ fbdev support. Note that this support also provides
+ the Linux console on top of the Mediatek DRM mode
+ setting driver.
diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
new file mode 100644
index 0000000..a566a83
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/Makefile
@@ -0,0 +1,13 @@
+mediatek-drm-objs := mediatek_drm_drv.o \
+ mediatek_drm_crtc.o \
+ mediatek_drm_fb.o \
+ mediatek_drm_gem.o \
+ mediatek_drm_dsi.o \
+ mediatek_drm_ddp.o \
+ mediatek_drm_ddp_comp.o \
+ mediatek_drm_crtc_main.o
+
+obj-$(CONFIG_DRM_MEDIATEK) += mediatek-drm.o
+
+ccflags-y += \
+ -Idrivers/gpu/drm
diff --git a/drivers/gpu/drm/mediatek/mediatek_drm_crtc.c b/drivers/gpu/drm/mediatek/mediatek_drm_crtc.c
new file mode 100644
index 0000000..e1437c6
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mediatek_drm_crtc.c
@@ -0,0 +1,246 @@
+/*
+ * 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 <drm/drm_crtc_helper.h>
+#include <drm/drm_plane_helper.h>
+#include <linux/dma-buf.h>
+#include <linux/reservation.h>
+
+#include "mediatek_drm_drv.h"
+#include "mediatek_drm_crtc.h"
+#include "mediatek_drm_fb.h"
+#include "mediatek_drm_gem.h"
+
+
+void mtk_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 mediatek_drm_crtc_pending_ovl_config(struct mtk_drm_crtc *mtk_crtc,
+ bool enable, unsigned int addr)
+{
+ if (mtk_crtc->ops && mtk_crtc->ops->ovl_layer_config)
+ mtk_crtc->ops->ovl_layer_config(mtk_crtc, enable, addr);
+}
+
+static void mediatek_drm_crtc_pending_ovl_cursor_config(
+ struct mtk_drm_crtc *mtk_crtc,
+ bool enable, unsigned int addr)
+{
+ if (mtk_crtc->ops && mtk_crtc->ops->ovl_layer_config_cursor)
+ mtk_crtc->ops->ovl_layer_config_cursor(mtk_crtc, enable, addr);
+}
+
+static int mtk_drm_crtc_page_flip(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ struct drm_pending_vblank_event *event,
+ uint32_t page_flip_flags)
+{
+ struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
+ struct mtk_drm_fb *mtk_fb = to_mtk_fb(fb);
+ struct drm_device *dev = crtc->dev;
+ unsigned long flags;
+ bool busy;
+ int ret;
+
+ spin_lock_irqsave(&dev->event_lock, flags);
+ busy = !!mtk_crtc->event;
+ if (!busy)
+ mtk_crtc->event = event;
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+ if (busy)
+ return -EBUSY;
+
+ if (fb->width != crtc->mode.hdisplay ||
+ fb->height != crtc->mode.vdisplay) {
+ DRM_ERROR("mtk_drm_crtc_page_flip width/height not match !!\n");
+ return -EINVAL;
+ }
+
+ if (event) {
+ ret = drm_crtc_vblank_get(crtc);
+ if (ret) {
+ DRM_ERROR("failed to acquire vblank events\n");
+ return ret;
+ }
+ }
+
+ /*
+ * the values related to a buffer of the drm framebuffer
+ * to be applied should be set at here. because these values
+ * first, are set to shadow registers and then to
+ * real registers at vsync front porch period.
+ */
+ crtc->primary->fb = fb;
+ mtk_crtc->flip_buffer = to_mtk_gem_obj(mtk_fb->gem_obj[0])->buffer;
+
+ mediatek_drm_crtc_pending_ovl_config(mtk_crtc, true,
+ mtk_crtc->flip_buffer->mva_addr);
+
+ spin_lock_irqsave(&dev->event_lock, flags);
+ if (mtk_crtc->event)
+ mtk_crtc->pending_needs_vblank = true;
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+
+ return ret;
+}
+
+static void mtk_drm_crtc_destroy(struct drm_crtc *crtc)
+{
+ drm_crtc_cleanup(crtc);
+}
+
+static void mtk_drm_crtc_prepare(struct drm_crtc *crtc)
+{
+ /* drm framework doesn't check NULL. */
+}
+
+static void mtk_drm_crtc_commit(struct drm_crtc *crtc)
+{
+ /*
+ * when set_crtc is requested from user or at booting time,
+ * crtc->commit would be called without dpms call so if dpms is
+ * no power on then crtc->dpms should be called
+ * with DRM_MODE_DPMS_ON for the hardware power to be on.
+ */
+}
+
+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 int mtk_drm_crtc_mode_set(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode,
+ int x, int y, struct drm_framebuffer *old_fb)
+{
+ struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
+ struct drm_framebuffer *fb;
+ struct mtk_drm_fb *mtk_fb;
+ struct mtk_drm_gem_buf *buffer;
+
+ fb = crtc->primary->fb;
+ mtk_fb = to_mtk_fb(fb);
+
+ buffer = to_mtk_gem_obj(mtk_fb->gem_obj[0])->buffer;
+
+ mediatek_drm_crtc_pending_ovl_config(mtk_crtc, true,
+ buffer->mva_addr);
+ /*
+ * copy the mode data adjusted by mode_fixup() into crtc->mode
+ * so that hardware can be seet to proper mode.
+ */
+ memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode));
+
+ /* Take a reference to the new fb as we're using it */
+ drm_framebuffer_reference(crtc->primary->fb);
+
+ return 0;
+}
+
+int mtk_drm_crtc_enable_vblank(struct drm_device *drm, int pipe)
+{
+ struct mtk_drm_private *priv =
+ (struct mtk_drm_private *)drm->dev_private;
+ struct mtk_drm_crtc *mtk_crtc;
+
+ if (pipe >= MAX_CRTC || pipe < 0) {
+ DRM_ERROR(" - %s: invalid crtc (%d)\n", __func__, pipe);
+ return -EINVAL;
+ }
+
+ mtk_crtc = to_mtk_crtc(priv->crtc[pipe]);
+
+ if (mtk_crtc->ops->enable_vblank)
+ mtk_crtc->ops->enable_vblank(mtk_crtc);
+
+ return 0;
+}
+
+void mtk_drm_crtc_disable_vblank(struct drm_device *drm, int pipe)
+{
+ struct mtk_drm_private *priv =
+ (struct mtk_drm_private *)drm->dev_private;
+ struct mtk_drm_crtc *mtk_crtc;
+
+ if (pipe >= MAX_CRTC || pipe < 0)
+ DRM_ERROR(" - %s: invalid crtc (%d)\n", __func__, pipe);
+
+ mtk_crtc = to_mtk_crtc(priv->crtc[pipe]);
+ if (mtk_crtc->ops->disable_vblank)
+ mtk_crtc->ops->disable_vblank(mtk_crtc);
+}
+
+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);
+
+ mediatek_drm_crtc_pending_ovl_config(mtk_crtc, false, 0);
+ mediatek_drm_crtc_pending_ovl_cursor_config(mtk_crtc, false, 0);
+}
+
+static struct drm_crtc_funcs mediatek_crtc_funcs = {
+ .set_config = drm_crtc_helper_set_config,
+ .page_flip = mtk_drm_crtc_page_flip,
+ .destroy = mtk_drm_crtc_destroy,
+};
+
+static struct drm_crtc_helper_funcs mediatek_crtc_helper_funcs = {
+ .prepare = mtk_drm_crtc_prepare,
+ .commit = mtk_drm_crtc_commit,
+ .mode_fixup = mtk_drm_crtc_mode_fixup,
+ .mode_set = mtk_drm_crtc_mode_set,
+ .disable = mtk_drm_crtc_disable,
+};
+
+struct mtk_drm_crtc *mtk_drm_crtc_create(
+ struct drm_device *drm_dev, int pipe,
+ struct mediatek_drm_crtc_ops *ops,
+ void *ctx)
+{
+ struct mtk_drm_private *priv =
+ (struct mtk_drm_private *)drm_dev->dev_private;
+ struct mtk_drm_crtc *mtk_crtc;
+
+ mtk_crtc = devm_kzalloc(drm_dev->dev, sizeof(*mtk_crtc), GFP_KERNEL);
+ if (!mtk_crtc) {
+ DRM_ERROR("failed to allocate mtk crtc\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ mtk_crtc->pipe = pipe;
+ mtk_crtc->ops = ops;
+ mtk_crtc->ctx = ctx;
+
+ priv->crtc[pipe] = &mtk_crtc->base;
+
+ drm_crtc_init(drm_dev, &mtk_crtc->base, &mediatek_crtc_funcs);
+ drm_crtc_helper_add(&mtk_crtc->base, &mediatek_crtc_helper_funcs);
+
+ return mtk_crtc;
+}
+
+
diff --git a/drivers/gpu/drm/mediatek/mediatek_drm_crtc.h b/drivers/gpu/drm/mediatek/mediatek_drm_crtc.h
new file mode 100644
index 0000000..1732927
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mediatek_drm_crtc.h
@@ -0,0 +1,80 @@
+/*
+ * 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 _MEDIATEL_DRM_CRTC_H_
+#define _MEDIATEL_DRM_CRTC_H_
+
+#include "mediatek_drm_ddp.h"
+
+
+#define MAX_FB_BUFFER 4
+#define DEFAULT_ZPOS -1
+
+struct mtk_drm_crtc;
+struct mediatek_drm_crtc_ops {
+ void (*dpms)(struct mtk_drm_crtc *crtc, int mode);
+ int (*enable_vblank)(struct mtk_drm_crtc *crtc);
+ void (*disable_vblank)(struct mtk_drm_crtc *crtc);
+ void (*ovl_layer_config)(struct mtk_drm_crtc *crtc,
+ bool enable, unsigned int addr);
+ void (*ovl_layer_config_cursor)(struct mtk_drm_crtc *crtc,
+ bool enable, unsigned int addr);
+};
+
+/*
+ * 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.
+ */
+struct mtk_drm_crtc {
+ struct drm_crtc base;
+
+ unsigned int pipe;
+ struct drm_pending_vblank_event *event;
+ struct mtk_drm_gem_buf *flip_buffer;
+ struct mediatek_drm_crtc_ops *ops;
+ void *ctx;
+ bool pending_needs_vblank;
+
+ bool pending_ovl_config;
+ bool pending_ovl_enabled;
+ unsigned int pending_ovl_addr;
+ unsigned int pending_ovl_width;
+ unsigned int pending_ovl_height;
+ unsigned int pending_ovl_pitch;
+ unsigned int pending_ovl_format;
+
+};
+
+#define to_mtk_crtc(x) container_of(x, struct mtk_drm_crtc, base)
+
+struct mtk_drm_crtc *mtk_drm_crtc_create(
+ struct drm_device *drm_dev, int pipe,
+ struct mediatek_drm_crtc_ops *ops,
+ void *ctx);
+void mtk_drm_crtc_irq(struct mtk_drm_crtc *mtk_crtc);
+
+void mtk_crtc_finish_page_flip(struct mtk_drm_crtc *mtk_crtc);
+int mtk_drm_crtc_enable_vblank(struct drm_device *drm, int pipe);
+void mtk_drm_crtc_disable_vblank(struct drm_device *drm, int pipe);
+
+#endif
+
diff --git a/drivers/gpu/drm/mediatek/mediatek_drm_crtc_main.c b/drivers/gpu/drm/mediatek/mediatek_drm_crtc_main.c
new file mode 100644
index 0000000..4d16620
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mediatek_drm_crtc_main.c
@@ -0,0 +1,420 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ * Authors:
+ * YT Shen <yt.shen@xxxxxxxxxxxx>
+ * CK Hu <ck.hu@xxxxxxxxxxxx>
+ *
+ * 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/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/of_device.h>
+#include <linux/component.h>
+#include <linux/pm_runtime.h>
+#include "mediatek_drm_drv.h"
+#include "mediatek_drm_crtc.h"
+#include "mediatek_drm_gem.h"
+#include "mediatek_drm_ddp_comp.h"
+
+
+struct crtc_main_context {
+ struct device *dev;
+ struct drm_device *drm_dev;
+ struct mtk_drm_crtc *crtc;
+ int pipe;
+
+ struct device *ddp_dev;
+ struct clk *ovl0_disp_clk;
+ struct clk *rdma0_disp_clk;
+ struct clk *color0_disp_clk;
+ struct clk *aal_disp_clk;
+ struct clk *ufoe_disp_clk;
+ struct clk *od_disp_clk;
+
+ void __iomem *ovl0_regs;
+ void __iomem *rdma0_regs;
+ void __iomem *color0_regs;
+ void __iomem *aal_regs;
+ void __iomem *ufoe_regs;
+ void __iomem *od_regs;
+
+ bool pending_ovl_config;
+ bool pending_ovl_enable;
+ unsigned int pending_ovl_addr;
+ unsigned int pending_ovl_width;
+ unsigned int pending_ovl_height;
+ unsigned int pending_ovl_pitch;
+ unsigned int pending_ovl_format;
+};
+
+
+static int crtc_main_ctx_initialize(struct crtc_main_context *ctx,
+ struct drm_device *drm_dev)
+{
+ struct mtk_drm_private *priv;
+
+ priv = drm_dev->dev_private;
+ ctx->drm_dev = drm_dev;
+ ctx->pipe = priv->pipe++;
+
+ return 0;
+}
+
+static void crtc_main_ctx_remove(struct crtc_main_context *ctx)
+{
+}
+
+static void crtc_main_power_on(struct crtc_main_context *ctx)
+{
+ int ret;
+
+ ret = clk_prepare_enable(ctx->ovl0_disp_clk);
+ if (ret != 0)
+ DRM_ERROR("clk_prepare_enable(ctx->ovl0_disp_clk) error!\n");
+
+ ret = clk_prepare_enable(ctx->rdma0_disp_clk);
+ if (ret != 0)
+ DRM_ERROR("clk_prepare_enable(ctx->rdma0_disp_clk) error!\n");
+
+ ret = clk_prepare_enable(ctx->color0_disp_clk);
+ if (ret != 0)
+ DRM_ERROR("clk_prepare_enable(ctx->color0_disp_clk) error!\n");
+
+ ret = clk_prepare_enable(ctx->aal_disp_clk);
+ if (ret != 0)
+ DRM_ERROR("clk_prepare_enable(ctx->aal_disp_clk) error!\n");
+
+ ret = clk_prepare_enable(ctx->ufoe_disp_clk);
+ if (ret != 0)
+ DRM_ERROR("clk_prepare_enable(ctx->ufoe_disp_clk) error!\n");
+
+ ret = clk_prepare_enable(ctx->od_disp_clk);
+ if (ret != 0)
+ DRM_ERROR("clk_prepare_enable(ctx->od_disp_clk) error!\n");
+}
+
+static void crtc_main_power_off(struct crtc_main_context *ctx)
+{
+ clk_disable_unprepare(ctx->ovl0_disp_clk);
+
+ clk_disable_unprepare(ctx->rdma0_disp_clk);
+
+ clk_disable_unprepare(ctx->color0_disp_clk);
+
+ clk_disable_unprepare(ctx->aal_disp_clk);
+
+ clk_disable_unprepare(ctx->ufoe_disp_clk);
+
+ clk_disable_unprepare(ctx->od_disp_clk);
+}
+
+static void crtc_main_dpms(struct mtk_drm_crtc *crtc, int mode)
+{
+ /* DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode); */
+
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ crtc_main_power_on(crtc->ctx);
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ case DRM_MODE_DPMS_OFF:
+ crtc_main_power_off(crtc->ctx);
+ break;
+ default:
+ DRM_DEBUG_KMS("unspecified mode %d\n", mode);
+ break;
+ }
+}
+
+static int crtc_main_enable_vblank(struct mtk_drm_crtc *crtc)
+{
+ struct crtc_main_context *ctx = (struct crtc_main_context *)crtc->ctx;
+
+ mediatek_od_enable_vblank(ctx->od_regs);
+
+ return 0;
+}
+
+static void crtc_main_disable_vblank(struct mtk_drm_crtc *crtc)
+{
+ struct crtc_main_context *ctx = (struct crtc_main_context *)crtc->ctx;
+
+ mediatek_od_disable_vblank(ctx->od_regs);
+}
+
+static void crtc_main_ovl_layer_config(struct mtk_drm_crtc *crtc,
+ bool enable, unsigned int addr)
+{
+ struct crtc_main_context *ctx = (struct crtc_main_context *)crtc->ctx;
+ unsigned int pitch = 0;
+
+ if (crtc->base.primary->fb && crtc->base.primary->fb->pitches[0])
+ pitch = crtc->base.primary->fb->pitches[0];
+
+ ctx->pending_ovl_enable = enable;
+ if (enable) {
+ ctx->pending_ovl_addr = addr;
+ ctx->pending_ovl_width = crtc->base.mode.hdisplay;
+ ctx->pending_ovl_height = crtc->base.mode.vdisplay;
+ ctx->pending_ovl_pitch = pitch;
+ ctx->pending_ovl_format = crtc->base.primary->fb->pixel_format;
+ }
+ ctx->pending_ovl_config = true;
+}
+
+static struct mediatek_drm_crtc_ops crtc_main_crtc_ops = {
+ .dpms = crtc_main_dpms,
+ .enable_vblank = crtc_main_enable_vblank,
+ .disable_vblank = crtc_main_disable_vblank,
+ .ovl_layer_config = crtc_main_ovl_layer_config,
+};
+
+static void crtc_main_irq(struct crtc_main_context *ctx)
+{
+ struct drm_device *dev = ctx->drm_dev;
+ struct mtk_drm_crtc *mtk_crtc = ctx->crtc;
+ unsigned long flags;
+
+ if (ctx->pending_ovl_config) {
+ ctx->pending_ovl_config = false;
+ mediatek_ovl_layer_config(ctx->ovl0_regs,
+ ctx->pending_ovl_enable,
+ ctx->pending_ovl_addr,
+ ctx->pending_ovl_width,
+ ctx->pending_ovl_height,
+ ctx->pending_ovl_pitch,
+ ctx->pending_ovl_format);
+ }
+
+ drm_handle_vblank(ctx->drm_dev, ctx->pipe);
+ spin_lock_irqsave(&dev->event_lock, flags);
+ if (mtk_crtc->pending_needs_vblank) {
+ mtk_crtc_finish_page_flip(mtk_crtc);
+ mtk_crtc->pending_needs_vblank = false;
+ }
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+irqreturn_t crtc_main_irq_handler(int irq, void *dev_id)
+{
+ struct crtc_main_context *ctx = (struct crtc_main_context *)dev_id;
+
+ mediatek_od_clear_vblank(ctx->od_regs);
+
+ if (ctx->pipe < 0 || !ctx->drm_dev)
+ goto out;
+
+ crtc_main_irq(ctx);
+out:
+ return IRQ_HANDLED;
+}
+
+static int crtc_main_bind(struct device *dev, struct device *master, void *data)
+{
+ struct crtc_main_context *ctx = dev_get_drvdata(dev);
+ struct drm_device *drm_dev = data;
+ int ret;
+
+ ret = crtc_main_ctx_initialize(ctx, drm_dev);
+ if (ret) {
+ DRM_ERROR("crtc_main_ctx_initialize failed.\n");
+ return ret;
+ }
+
+ ctx->crtc = mtk_drm_crtc_create(drm_dev, ctx->pipe,
+ &crtc_main_crtc_ops, ctx);
+
+ if (IS_ERR(ctx->crtc)) {
+ crtc_main_ctx_remove(ctx);
+ return PTR_ERR(ctx->crtc);
+ }
+
+ DRM_INFO("mediatek_ddp_clock_on\n");
+ mediatek_ddp_clock_on(ctx->ddp_dev);
+
+ DRM_INFO("mediatek_ddp_main_path_setup\n");
+ mediatek_ddp_main_path_setup(ctx->ddp_dev);
+
+ DRM_INFO("main_disp_path_power_on\n");
+ main_disp_path_power_on(ctx->ovl0_regs, ctx->rdma0_regs,
+ ctx->color0_regs, ctx->ufoe_regs, ctx->od_regs);
+
+ return 0;
+
+}
+
+static void crtc_main_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct crtc_main_context *ctx = dev_get_drvdata(dev);
+
+ crtc_main_ctx_remove(ctx);
+}
+
+static const struct component_ops crtc_main_component_ops = {
+ .bind = crtc_main_bind,
+ .unbind = crtc_main_unbind,
+};
+
+static int crtc_main_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct crtc_main_context *ctx;
+ struct device_node *node;
+ struct platform_device *ddp_pdev;
+ struct resource *regs;
+ int irq;
+ int ret;
+
+ if (!dev->of_node)
+ return -ENODEV;
+
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ node = of_parse_phandle(dev->of_node, "ddp", 0);
+ if (!node) {
+ dev_err(dev, "crtc_main_probe: Get ddp node fail.\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ddp_pdev = of_find_device_by_node(node);
+ if (WARN_ON(!ddp_pdev)) {
+ dev_err(dev, "crtc_main_probe: Find ddp device fail.\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ ctx->ddp_dev = &ddp_pdev->dev;
+
+ ctx->ovl0_disp_clk = devm_clk_get(dev, "ovl0_disp");
+ if (IS_ERR(ctx->ovl0_disp_clk)) {
+ dev_err(dev, "crtc_main_probe: Get ovl0_disp_clk fail.\n");
+ ret = PTR_ERR(ctx->ovl0_disp_clk);
+ goto err;
+ }
+
+ ctx->rdma0_disp_clk = devm_clk_get(dev, "rdma0_disp");
+ if (IS_ERR(ctx->rdma0_disp_clk)) {
+ dev_err(dev, "crtc_main_probe: Get rdma0_disp_clk.\n");
+ ret = PTR_ERR(ctx->rdma0_disp_clk);
+ goto err;
+ }
+
+ ctx->color0_disp_clk = devm_clk_get(dev, "color0_disp");
+ if (IS_ERR(ctx->color0_disp_clk)) {
+ dev_err(dev, "crtc_main_probe: Get color0_disp_clk fail.\n");
+ ret = PTR_ERR(ctx->color0_disp_clk);
+ goto err;
+ }
+
+ ctx->aal_disp_clk = devm_clk_get(dev, "aal_disp");
+ if (IS_ERR(ctx->aal_disp_clk)) {
+ dev_err(dev, "crtc_main_probe: Get aal_disp_clk fail.\n");
+ ret = PTR_ERR(ctx->aal_disp_clk);
+ goto err;
+ }
+
+ ctx->ufoe_disp_clk = devm_clk_get(dev, "ufoe_disp");
+ if (IS_ERR(ctx->ufoe_disp_clk)) {
+ dev_err(dev, "crtc_main_probe: Get ufoe_disp_clk fail.\n");
+ ret = PTR_ERR(ctx->ufoe_disp_clk);
+ goto err;
+ }
+
+ ctx->od_disp_clk = devm_clk_get(dev, "od_disp");
+ if (IS_ERR(ctx->od_disp_clk)) {
+ dev_err(dev, "crtc_main_probe: Get od_disp_clk fail.\n");
+ ret = PTR_ERR(ctx->od_disp_clk);
+ goto err;
+ }
+
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ctx->ovl0_regs = devm_ioremap_resource(dev, regs);
+ if (IS_ERR(ctx->ovl0_regs))
+ return PTR_ERR(ctx->ovl0_regs);
+
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ ctx->rdma0_regs = devm_ioremap_resource(dev, regs);
+ if (IS_ERR(ctx->rdma0_regs))
+ return PTR_ERR(ctx->rdma0_regs);
+
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ ctx->color0_regs = devm_ioremap_resource(dev, regs);
+ if (IS_ERR(ctx->color0_regs))
+ return PTR_ERR(ctx->color0_regs);
+
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 3);
+ ctx->aal_regs = devm_ioremap_resource(dev, regs);
+ if (IS_ERR(ctx->aal_regs))
+ return PTR_ERR(ctx->aal_regs);
+
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 4);
+ ctx->ufoe_regs = devm_ioremap_resource(dev, regs);
+ if (IS_ERR(ctx->ufoe_regs))
+ return PTR_ERR(ctx->ufoe_regs);
+
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 5);
+ ctx->od_regs = devm_ioremap_resource(dev, regs);
+ if (IS_ERR(ctx->od_regs))
+ return PTR_ERR(ctx->od_regs);
+
+ irq = platform_get_irq(pdev, 0);
+ ret = devm_request_irq(dev, irq, crtc_main_irq_handler,
+ IRQF_TRIGGER_NONE, dev_name(dev), ctx);
+ if (ret < 0) {
+ dev_err(dev, "devm_request_irq %d fail %d\n", irq, ret);
+ ret = -ENXIO;
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, ctx);
+
+ ret = component_add(&pdev->dev, &crtc_main_component_ops);
+ if (ret)
+ goto err;
+
+ return 0;
+
+err:
+ if (node)
+ of_node_put(node);
+
+ return ret;
+}
+
+static int crtc_main_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &crtc_main_component_ops);
+
+ return 0;
+}
+
+static const struct of_device_id crtc_main_driver_dt_match[] = {
+ { .compatible = "mediatek,mt8173-crtc-main" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, crtc_main_driver_dt_match);
+
+struct platform_driver mediatek_crtc_main_driver = {
+ .probe = crtc_main_probe,
+ .remove = crtc_main_remove,
+ .driver = {
+ .name = "mediatek-crtc-main",
+ .owner = THIS_MODULE,
+ .of_match_table = crtc_main_driver_dt_match,
+ },
+};
+
diff --git a/drivers/gpu/drm/mediatek/mediatek_drm_ddp.c b/drivers/gpu/drm/mediatek/mediatek_drm_ddp.c
new file mode 100644
index 0000000..bb6959b
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mediatek_drm_ddp.c
@@ -0,0 +1,202 @@
+/*
+ * 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/platform_device.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <linux/of_device.h>
+#include <linux/component.h>
+
+#include "mediatek_drm_crtc.h"
+#include "mediatek_drm_ddp.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_PATH1_SOUT_SEL_IN 0x0C8
+#define DISP_REG_CONFIG_MMSYS_CG_CON0 0x100
+
+#define DISP_REG_CONFIG_MUTEX_EN(n) (0x20 + 0x20 * n)
+#define DISP_REG_CONFIG_MUTEX_MOD(n) (0x2C + 0x20 * n)
+#define DISP_REG_CONFIG_MUTEX_SOF(n) (0x30 + 0x20 * n)
+
+
+enum {
+ MUTEX_MOD_OVL0 = 11,
+ MUTEX_MOD_RDMA0 = 13,
+ MUTEX_MOD_COLOR0 = 18,
+ MUTEX_MOD_AAL = 20,
+ MUTEX_MOD_UFOE = 22,
+ MUTEX_MOD_PWM0 = 23,
+ MUTEX_MOD_OD = 25,
+};
+
+enum {
+ MUTEX_SOF_DSI0 = 1,
+};
+
+enum {
+ OVL0_MOUT_EN_COLOR0 = 0x1,
+};
+
+enum {
+ OD_MOUT_EN_RDMA0 = 0x1,
+};
+
+enum {
+ UFOE_MOUT_EN_DSI0 = 0x1,
+};
+
+enum {
+ COLOR0_SEL_IN_OVL0 = 0x1,
+};
+
+struct ddp_context {
+ struct device *dev;
+ struct drm_device *drm_dev;
+ struct mediatek_drm_crtc *crtc;
+ int pipe;
+
+ struct clk *mutex_disp_clk;
+
+ void __iomem *config_regs;
+ void __iomem *mutex_regs;
+
+ bool pending_ovl_config;
+ bool pending_ovl_enable;
+ unsigned int pending_ovl_addr;
+ unsigned int pending_ovl_width;
+ unsigned int pending_ovl_height;
+ unsigned int pending_ovl_pitch;
+ unsigned int pending_ovl_format;
+};
+
+
+static void disp_config_main_path_connection(void __iomem *disp_base)
+{
+ writel(OVL0_MOUT_EN_COLOR0,
+ disp_base + DISP_REG_CONFIG_DISP_OVL0_MOUT_EN);
+ writel(OD_MOUT_EN_RDMA0, disp_base + DISP_REG_CONFIG_DISP_OD_MOUT_EN);
+ writel(UFOE_MOUT_EN_DSI0,
+ disp_base + DISP_REG_CONFIG_DISP_UFOE_MOUT_EN);
+ writel(COLOR0_SEL_IN_OVL0,
+ disp_base + DISP_REG_CONFIG_DISP_COLOR0_SEL_IN);
+}
+
+static void disp_config_main_path_mutex(void __iomem *mutex_base)
+{
+ unsigned int id = 0;
+
+ writel((1 << MUTEX_MOD_OVL0 | 1 << MUTEX_MOD_RDMA0 |
+ 1 << MUTEX_MOD_COLOR0 | 1 << MUTEX_MOD_AAL |
+ 1 << MUTEX_MOD_UFOE | 1 << MUTEX_MOD_PWM0 |
+ 1 << MUTEX_MOD_OD),
+ mutex_base + DISP_REG_CONFIG_MUTEX_MOD(id));
+
+ writel(MUTEX_SOF_DSI0, mutex_base + DISP_REG_CONFIG_MUTEX_SOF(id));
+ writel(1, mutex_base + DISP_REG_CONFIG_MUTEX_EN(id));
+}
+
+void mediatek_ddp_main_path_setup(struct device *dev)
+{
+ struct ddp_context *ddp = dev_get_drvdata(dev);
+
+ disp_config_main_path_connection(ddp->config_regs);
+ disp_config_main_path_mutex(ddp->mutex_regs);
+}
+
+void mediatek_ddp_clock_on(struct device *dev)
+{
+ struct ddp_context *ddp = dev_get_drvdata(dev);
+ int ret;
+
+ /* disp_mtcmos */
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0)
+ DRM_ERROR("failed to get_sync(%d)\n", ret);
+
+ ret = clk_prepare_enable(ddp->mutex_disp_clk);
+ if (ret != 0)
+ DRM_ERROR("clk_prepare_enable(mutex_disp_clk) error!\n");
+}
+
+static int ddp_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct ddp_context *ddp;
+ struct resource *regs;
+ int ret;
+
+ if (!dev->of_node)
+ return -ENODEV;
+
+ ddp = devm_kzalloc(dev, sizeof(*ddp), GFP_KERNEL);
+ if (!ddp)
+ return -ENOMEM;
+
+ ddp->mutex_disp_clk = devm_clk_get(dev, "mutex_disp");
+ if (IS_ERR(ddp->mutex_disp_clk)) {
+ dev_err(dev, "ddp_probe: Get mutex_disp_clk fail.\n");
+ ret = PTR_ERR(ddp->mutex_disp_clk);
+ goto err;
+ }
+
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ddp->config_regs = devm_ioremap_resource(dev, regs);
+ if (IS_ERR(ddp->config_regs))
+ return PTR_ERR(ddp->config_regs);
+
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ ddp->mutex_regs = devm_ioremap_resource(dev, regs);
+ if (IS_ERR(ddp->mutex_regs))
+ return PTR_ERR(ddp->mutex_regs);
+
+ platform_set_drvdata(pdev, ddp);
+
+ pm_runtime_enable(dev);
+
+ return 0;
+
+err:
+
+ return ret;
+}
+
+static int ddp_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static const struct of_device_id ddp_driver_dt_match[] = {
+ { .compatible = "mediatek,mt8173-ddp" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ddp_driver_dt_match);
+
+struct platform_driver mediatek_ddp_driver = {
+ .probe = ddp_probe,
+ .remove = ddp_remove,
+ .driver = {
+ .name = "mediatek-ddp",
+ .owner = THIS_MODULE,
+ .of_match_table = ddp_driver_dt_match,
+ },
+};
+
diff --git a/drivers/gpu/drm/mediatek/mediatek_drm_ddp.h b/drivers/gpu/drm/mediatek/mediatek_drm_ddp.h
new file mode 100644
index 0000000..07dd637
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mediatek_drm_ddp.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MEDIATEK_DRM_DDP_H_
+#define _MEDIATEK_DRM_DDP_H_
+
+void mediatek_ddp_main_path_setup(struct device *dev);
+
+void mediatek_ddp_clock_on(struct device *dev);
+
+
+#endif
+
diff --git a/drivers/gpu/drm/mediatek/mediatek_drm_ddp_comp.c b/drivers/gpu/drm/mediatek/mediatek_drm_ddp_comp.c
new file mode 100644
index 0000000..dd7ac83
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mediatek_drm_ddp_comp.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ * Authors:
+ * YT Shen <yt.shen@xxxxxxxxxxxx>
+ * CK Hu <ck.hu@xxxxxxxxxxxx>
+ *
+ * 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>
+
+
+#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_L0_CON 0x0030
+#define DISP_REG_OVL_L0_SRCKEY 0x0034
+#define DISP_REG_OVL_L0_SRC_SIZE 0x0038
+#define DISP_REG_OVL_L0_OFFSET 0x003C
+#define DISP_REG_OVL_L0_PITCH 0x0044
+#define DISP_REG_OVL_L1_CON 0x0050
+#define DISP_REG_OVL_L1_SRCKEY 0x0054
+#define DISP_REG_OVL_L1_SRC_SIZE 0x0058
+#define DISP_REG_OVL_L1_OFFSET 0x005C
+#define DISP_REG_OVL_L1_PITCH 0x0064
+#define DISP_REG_OVL_RDMA0_CTRL 0x00C0
+#define DISP_REG_OVL_RDMA0_MEM_GMC_SETTING 0x00C8
+#define DISP_REG_OVL_RDMA1_CTRL 0x00E0
+#define DISP_REG_OVL_RDMA1_MEM_GMC_SETTING 0x00E8
+#define DISP_REG_OVL_RDMA1_FIFO_CTRL 0x00F0
+#define DISP_REG_OVL_L0_ADDR 0x0f40
+#define DISP_REG_OVL_L1_ADDR 0x0f60
+
+#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 0x000
+#define DISP_OD_INTEN 0x008
+#define DISP_OD_INTS 0x00C
+#define DISP_OD_CFG 0x020
+#define DISP_OD_SIZE 0x030
+
+#define DISP_REG_UFO_START 0x000
+
+#define DISP_COLOR_CFG_MAIN 0x400
+#define DISP_COLOR_START 0xC00
+
+enum DISPLAY_PATH {
+ PRIMARY_PATH = 0,
+ EXTERNAL_PATH = 1,
+};
+
+enum RDMA_MODE {
+ RDMA_MODE_DIRECT_LINK = 0,
+ RDMA_MODE_MEMORY = 1,
+};
+
+enum RDMA_OUTPUT_FORMAT {
+ RDMA_OUTPUT_FORMAT_ARGB = 0,
+ RDMA_OUTPUT_FORMAT_YUV444 = 1,
+};
+
+#define OVL_COLOR_BASE 30
+enum OVL_INPUT_FORMAT {
+ OVL_INFMT_RGB565 = 0,
+ OVL_INFMT_RGB888 = 1,
+ OVL_INFMT_RGBA8888 = 2,
+ OVL_INFMT_ARGB8888 = 3,
+ OVL_INFMT_UYVY = 4,
+ OVL_INFMT_YUYV = 5,
+ OVL_INFMT_UNKNOWN = 16,
+
+ OVL_INFMT_BGR565 = OVL_INFMT_RGB565 + OVL_COLOR_BASE,
+ OVL_INFMT_BGR888 = OVL_INFMT_RGB888 + OVL_COLOR_BASE,
+ OVL_INFMT_BGRA8888 = OVL_INFMT_RGBA8888 + OVL_COLOR_BASE,
+ OVL_INFMT_ABGR8888 = OVL_INFMT_ARGB8888 + OVL_COLOR_BASE,
+};
+
+enum {
+ OD_RELAY_MODE = 0x1,
+};
+
+enum {
+ UFO_BYPASS = 0x4,
+};
+
+enum {
+ COLOR_BYPASS_ALL = (1UL<<7),
+ COLOR_SEQ_SEL = (1UL<<13),
+};
+
+enum {
+ OVL_LAYER_SRC_DRAM = 0,
+};
+
+
+static void mediatek_ovl_start(void __iomem *ovl_base)
+{
+ writel(0x01, ovl_base + DISP_REG_OVL_EN);
+}
+
+static void mediatek_ovl_roi(void __iomem *ovl_base,
+ unsigned int w, unsigned int h, unsigned int bg_color)
+{
+ writel(h << 16 | w, ovl_base + DISP_REG_OVL_ROI_SIZE);
+ writel(bg_color, ovl_base + DISP_REG_OVL_ROI_BGCLR);
+}
+
+void mediatek_ovl_layer_switch(void __iomem *ovl_base,
+ unsigned layer, bool en)
+{
+ u32 reg;
+
+ reg = readl(ovl_base + DISP_REG_OVL_SRC_CON);
+ if (en)
+ reg |= (1U<<layer);
+ else
+ reg &= ~(1U<<layer);
+
+ writel(reg, ovl_base + DISP_REG_OVL_SRC_CON);
+ writel(0x1, ovl_base + DISP_REG_OVL_RST);
+ writel(0x0, ovl_base + DISP_REG_OVL_RST);
+}
+
+static unsigned int ovl_fmt_convert(unsigned int fmt)
+{
+ switch (fmt) {
+ case DRM_FORMAT_RGB888:
+ return OVL_INFMT_RGB888;
+ case DRM_FORMAT_RGB565:
+ return OVL_INFMT_RGB565;
+ case DRM_FORMAT_ARGB8888:
+ return OVL_INFMT_ARGB8888;
+ case DRM_FORMAT_RGBA8888:
+ return OVL_INFMT_RGBA8888;
+ case DRM_FORMAT_BGR888:
+ return OVL_INFMT_BGR888;
+ case DRM_FORMAT_BGR565:
+ return OVL_INFMT_BGR565;
+ case DRM_FORMAT_ABGR8888:
+ return OVL_INFMT_ABGR8888;
+ case DRM_FORMAT_XRGB8888:
+ case DRM_FORMAT_BGRA8888:
+ return OVL_INFMT_BGRA8888;
+ case DRM_FORMAT_YUYV:
+ return OVL_INFMT_YUYV;
+ case DRM_FORMAT_UYVY:
+ return OVL_INFMT_UYVY;
+ default:
+ return OVL_INFMT_UNKNOWN;
+ }
+}
+
+void mediatek_ovl_layer_config(void __iomem *ovl_base, bool enabled,
+ unsigned int addr, unsigned int width, unsigned int height,
+ unsigned int pitch, unsigned int format)
+{
+ unsigned int reg;
+ unsigned int dst_x = 0;
+ unsigned int dst_y = 0;
+ bool color_key_en = 1;
+ unsigned int color_key = 0xFF000000;
+ bool alpha_en = 0;
+ unsigned char alpha = 0x0;
+ unsigned int src_con, new_set;
+
+ unsigned int rgb_swap, bpp;
+ unsigned int fmt = ovl_fmt_convert(format);
+
+ if (fmt == OVL_INFMT_BGR888 || fmt == OVL_INFMT_BGR565 ||
+ fmt == OVL_INFMT_ABGR8888 || fmt == OVL_INFMT_BGRA8888) {
+ fmt -= OVL_COLOR_BASE;
+ rgb_swap = 1;
+ } else {
+ rgb_swap = 0;
+ }
+
+ switch (fmt) {
+ case OVL_INFMT_ARGB8888:
+ case OVL_INFMT_RGBA8888:
+ bpp = 4;
+ break;
+ case OVL_INFMT_RGB888:
+ bpp = 3;
+ break;
+ case OVL_INFMT_RGB565:
+ case OVL_INFMT_YUYV:
+ case OVL_INFMT_UYVY:
+ bpp = 2;
+ break;
+ default:
+ bpp = 1;
+ }
+
+ if (pitch == 0)
+ pitch = width * bpp;
+
+ src_con = readl(ovl_base + DISP_REG_OVL_SRC_CON);
+ if (enabled == true)
+ new_set = src_con | 0x1;
+ else
+ new_set = src_con & ~(0x1);
+
+ writel(0x1, ovl_base + DISP_REG_OVL_RST);
+ writel(0x0, ovl_base + DISP_REG_OVL_RST);
+
+ writel(new_set, ovl_base + DISP_REG_OVL_SRC_CON);
+
+ writel(0x00000001, ovl_base + DISP_REG_OVL_RDMA0_CTRL);
+ writel(0x40402020, ovl_base + DISP_REG_OVL_RDMA0_MEM_GMC_SETTING);
+
+ reg = color_key_en << 30 | OVL_LAYER_SRC_DRAM << 28 |
+ rgb_swap << 25 | fmt << 12 | alpha_en << 8 | alpha;
+ writel(reg, ovl_base + DISP_REG_OVL_L0_CON);
+ writel(color_key, ovl_base + DISP_REG_OVL_L0_SRCKEY);
+ writel(height << 16 | width, ovl_base + DISP_REG_OVL_L0_SRC_SIZE);
+ writel(dst_y << 16 | dst_x, ovl_base + DISP_REG_OVL_L0_OFFSET);
+ writel(addr, ovl_base + DISP_REG_OVL_L0_ADDR);
+ writel(pitch & 0xFFFF, ovl_base + DISP_REG_OVL_L0_PITCH);
+}
+
+static void mediatek_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 mediatek_rdma_config_direct_link(void __iomem *rdma_base,
+ unsigned width, unsigned height)
+{
+ unsigned int reg;
+ enum RDMA_MODE mode = RDMA_MODE_DIRECT_LINK;
+ enum RDMA_OUTPUT_FORMAT output_format = RDMA_OUTPUT_FORMAT_ARGB;
+
+ reg = readl(rdma_base + DISP_REG_RDMA_GLOBAL_CON);
+ if (mode == RDMA_MODE_DIRECT_LINK)
+ reg &= ~(0x2U);
+ writel(reg, rdma_base + DISP_REG_RDMA_GLOBAL_CON);
+
+ reg = readl(rdma_base + DISP_REG_RDMA_SIZE_CON_0);
+ if (output_format == RDMA_OUTPUT_FORMAT_ARGB)
+ reg &= ~(0x20000000U);
+ else
+ reg |= 0x20000000U;
+ writel(reg, rdma_base + DISP_REG_RDMA_SIZE_CON_0);
+
+ reg = readl(rdma_base + DISP_REG_RDMA_SIZE_CON_0);
+ reg = (reg & ~(0xFFFU)) | (width & 0xFFFU);
+ writel(reg, rdma_base + DISP_REG_RDMA_SIZE_CON_0);
+
+ reg = readl(rdma_base + DISP_REG_RDMA_SIZE_CON_1);
+ reg = (reg & ~(0xFFFFFU)) | (height & 0xFFFFFU);
+ writel(reg, rdma_base + DISP_REG_RDMA_SIZE_CON_1);
+
+ writel(0x80F00008, rdma_base + DISP_REG_RDMA_FIFO_CON);
+}
+
+void mediatek_od_enable_vblank(void __iomem *disp_base)
+{
+ writel(0x1, disp_base + DISP_OD_INTEN);
+}
+
+void mediatek_od_disable_vblank(void __iomem *disp_base)
+{
+ writel(0x0, disp_base + DISP_OD_INTEN);
+}
+
+void mediatek_od_clear_vblank(void __iomem *disp_base)
+{
+ writel(0x0, disp_base + DISP_OD_INTS);
+}
+
+static void mediatek_od_start(void __iomem *od_base, unsigned int w,
+ unsigned int h)
+{
+ writel(w << 16 | h, od_base + DISP_OD_SIZE);
+ writel(OD_RELAY_MODE, od_base + DISP_OD_CFG);
+ writel(1, od_base + DISP_OD_EN);
+}
+
+static void mediatek_ufoe_start(void __iomem *ufoe_base)
+{
+ writel(UFO_BYPASS, ufoe_base + DISP_REG_UFO_START);
+}
+
+static void mediatek_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);
+}
+
+void main_disp_path_power_on(void __iomem *ovl_base,
+ void __iomem *rdma_base,
+ void __iomem *color_base,
+ void __iomem *ufoe_base,
+ void __iomem *od_base)
+{
+ struct device_node *node;
+ unsigned int width, height;
+ int err;
+
+ node = of_find_compatible_node(NULL, NULL, "mediatek,mt8173-dsi");
+
+ err = of_property_read_u32(node, "mediatek,width", &width);
+ if (err < 0)
+ return;
+
+ err = of_property_read_u32(node, "mediatek,height", &height);
+ if (err < 0)
+ return;
+
+ width = ((width + 3)>>2)<<2;
+
+ mediatek_ovl_start(ovl_base);
+ mediatek_rdma_start(rdma_base);
+
+ mediatek_ovl_roi(ovl_base, width, height, 0x00000000);
+ mediatek_ovl_layer_switch(ovl_base, 0, 1);
+ mediatek_rdma_config_direct_link(rdma_base, width, height);
+ mediatek_od_start(od_base, width, height);
+ mediatek_ufoe_start(ufoe_base);
+ mediatek_color_start(color_base);
+}
+
+
diff --git a/drivers/gpu/drm/mediatek/mediatek_drm_ddp_comp.h b/drivers/gpu/drm/mediatek/mediatek_drm_ddp_comp.h
new file mode 100644
index 0000000..d3ed3e1
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mediatek_drm_ddp_comp.h
@@ -0,0 +1,33 @@
+/*
+ * 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 _MEDIATEK_DRM_DDP_COMP_H_
+#define _MEDIATEK_DRM_DDP_COMP_H_
+
+
+void mediatek_od_enable_vblank(void __iomem *drm_disp_base);
+void mediatek_od_disable_vblank(void __iomem *drm_disp_base);
+void mediatek_od_clear_vblank(void __iomem *drm_disp_base);
+void mediatek_ovl_layer_config(void __iomem *ovl_base, bool enabled,
+ unsigned int addr, unsigned int width, unsigned int height,
+ unsigned int pitch, unsigned int format);
+
+void main_disp_path_power_on(void __iomem *ovl_base,
+ void __iomem *rdma_base, void __iomem *color_base,
+ void __iomem *ufoe_base, void __iomem *od_base);
+
+void mediatek_ovl_layer_switch(void __iomem *ovl_base,
+ unsigned layer, bool en);
+
+#endif
+
diff --git a/drivers/gpu/drm/mediatek/mediatek_drm_drv.c b/drivers/gpu/drm/mediatek/mediatek_drm_drv.c
new file mode 100644
index 0000000..dfd816f
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mediatek_drm_drv.c
@@ -0,0 +1,369 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ * Author: YT SHEN <yt.shen@xxxxxxxxxxxx>
+ *
+ * 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 <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <linux/of_platform.h>
+#include <linux/component.h>
+#include <linux/mtk-smi.h>
+#include <linux/pm_runtime.h>
+#include <linux/dma-iommu.h>
+
+#include "mediatek_drm_drv.h"
+#include "mediatek_drm_crtc.h"
+#include "mediatek_drm_fb.h"
+#include "mediatek_drm_gem.h"
+
+#include "drm/mediatek_drm.h"
+
+#define DRIVER_NAME "mediatek"
+#define DRIVER_DESC "Mediatek SoC DRM"
+#define DRIVER_DATE "20150513"
+#define DRIVER_MAJOR 1
+#define DRIVER_MINOR 0
+
+static struct drm_mode_config_funcs mediatek_drm_mode_config_funcs = {
+ .fb_create = mtk_drm_mode_fb_create,
+ .output_poll_changed = mtk_drm_mode_output_poll_changed,
+};
+
+static int mtk_drm_kms_init(struct drm_device *dev)
+{
+ struct device_node *node;
+ struct platform_device *pdev;
+ int err;
+
+ drm_mode_config_init(dev);
+
+ dev->mode_config.min_width = 640;
+ dev->mode_config.min_height = 480;
+
+ /*
+ * set max width and height as default value(4096x4096).
+ * this value would be used to check framebuffer size limitation
+ * at drm_mode_addfb().
+ */
+ dev->mode_config.max_width = 4096;
+ dev->mode_config.max_height = 4096;
+ dev->mode_config.funcs = &mediatek_drm_mode_config_funcs;
+
+ err = component_bind_all(dev->dev, dev);
+ if (err)
+ 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.
+ */
+ dev->irq_enabled = true;
+ err = drm_vblank_init(dev, MAX_CRTC);
+ if (err < 0)
+ goto err_crtc;
+
+ drm_kms_helper_poll_init(dev);
+
+ node = of_parse_phandle(dev->dev->of_node, "iommus", 0);
+ if (!node)
+ return 0;
+
+ pdev = of_find_device_by_node(node);
+ if (WARN_ON(!pdev)) {
+ of_node_put(node);
+ return -EINVAL;
+ }
+ err = iommu_dma_attach_device(dev->dev,
+ arch_get_dma_domain(&pdev->dev));
+ if (err)
+ DRM_ERROR("iommu_dma_attach_device fail %d\n", err);
+
+ node = of_parse_phandle(dev->dev->of_node, "larb", 0);
+ if (!node)
+ return 0;
+
+ pdev = of_find_device_by_node(node);
+ if (WARN_ON(!pdev)) {
+ of_node_put(node);
+ return -EINVAL;
+ }
+
+ err = mtk_smi_larb_get(&pdev->dev);
+ if (err)
+ DRM_ERROR("mtk_smi_larb_get fail %d\n", err);
+
+ node = of_parse_phandle(dev->dev->of_node, "larb", 1);
+ if (!node)
+ return 0;
+
+ pdev = of_find_device_by_node(node);
+ if (WARN_ON(!pdev)) {
+ of_node_put(node);
+ return -EINVAL;
+ }
+
+ err = mtk_smi_larb_get(&pdev->dev);
+ if (err)
+ DRM_ERROR("mtk_smi_larb_get fail %d\n", err);
+
+ mtk_fbdev_create(dev);
+
+ return 0;
+err_crtc:
+ drm_mode_config_cleanup(dev);
+
+ return err;
+}
+
+static int mtk_drm_load(struct drm_device *dev, unsigned long flags)
+{
+ struct mtk_drm_private *priv;
+
+ priv = devm_kzalloc(dev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev->dev_private = priv;
+ platform_set_drvdata(dev->platformdev, dev);
+
+ return mtk_drm_kms_init(dev);
+}
+
+static void mtk_drm_kms_deinit(struct drm_device *dev)
+{
+ drm_kms_helper_poll_fini(dev);
+
+ mtk_fbdev_destroy(dev);
+
+ drm_vblank_cleanup(dev);
+ drm_mode_config_cleanup(dev);
+
+ pm_runtime_disable(dev->dev);
+}
+
+static int mtk_drm_unload(struct drm_device *dev)
+{
+ mtk_drm_kms_deinit(dev);
+ dev->dev_private = NULL;
+
+ return 0;
+}
+
+static int mtk_drm_open(struct drm_device *drm, struct drm_file *filp)
+{
+ return 0;
+}
+
+static void mediatek_drm_preclose(struct drm_device *drm, struct drm_file *file)
+{
+}
+
+static void mediatek_drm_lastclose(struct drm_device *drm)
+{
+}
+
+static const struct vm_operations_struct mediatek_drm_gem_vm_ops = {
+ .open = drm_gem_vm_open,
+ .close = drm_gem_vm_close,
+};
+
+static const struct drm_ioctl_desc mtk_ioctls[] = {
+ DRM_IOCTL_DEF_DRV(MTK_GEM_CREATE, mediatek_gem_create_ioctl,
+ DRM_UNLOCKED | DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(MTK_GEM_MAP_OFFSET,
+ mediatek_gem_map_offset_ioctl,
+ DRM_UNLOCKED | DRM_AUTH),
+};
+
+static const struct file_operations mediatek_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 mediatek_drm_driver = {
+ .driver_features = DRIVER_MODESET | DRIVER_GEM,
+ .load = mtk_drm_load,
+ .unload = mtk_drm_unload,
+ .open = mtk_drm_open,
+ .preclose = mediatek_drm_preclose,
+ .lastclose = mediatek_drm_lastclose,
+ .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 = &mediatek_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,
+
+ .num_ioctls = 0,
+ .fops = &mediatek_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_add_components(struct device *master, struct master *m)
+{
+ struct device_node *np = master->of_node;
+ unsigned i;
+ int ret;
+
+ for (i = 0; ; i++) {
+ struct device_node *node;
+
+ node = of_parse_phandle(np, "connectors", i);
+ if (!node)
+ break;
+
+ ret = component_master_add_child(m, compare_of, node);
+ of_node_put(node);
+ if (ret) {
+ dev_err(master, "component_master_add_child %s fail.\n",
+ node->full_name);
+ return ret;
+ }
+ }
+
+ for (i = 0; ; i++) {
+ struct device_node *node;
+
+ node = of_parse_phandle(np, "crtcs", i);
+ if (!node)
+ break;
+
+ ret = component_master_add_child(m, compare_of, node);
+ of_node_put(node);
+ if (ret) {
+ dev_err(master, "component_master_add_child %s fail.\n",
+ node->full_name);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int mtk_drm_bind(struct device *dev)
+{
+ return drm_platform_init(&mediatek_drm_driver, to_platform_device(dev));
+}
+
+static void mtk_drm_unbind(struct device *dev)
+{
+ drm_put_dev(platform_get_drvdata(to_platform_device(dev)));
+}
+
+static const struct component_master_ops mtk_drm_ops = {
+ .add_components = mtk_drm_add_components,
+ .bind = mtk_drm_bind,
+ .unbind = mtk_drm_unbind,
+};
+
+static int mtk_drm_probe(struct platform_device *pdev)
+{
+ component_master_add(&pdev->dev, &mtk_drm_ops);
+
+ return 0;
+}
+
+static int mtk_drm_remove(struct platform_device *pdev)
+{
+ drm_put_dev(platform_get_drvdata(pdev));
+
+ return 0;
+}
+
+static const struct of_device_id mediatek_drm_of_ids[] = {
+ { .compatible = "mediatek,mt8173-drm", },
+ { }
+};
+
+static struct platform_driver mediatek_drm_platform_driver = {
+ .probe = mtk_drm_probe,
+ .remove = mtk_drm_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "mediatek-drm",
+ .of_match_table = mediatek_drm_of_ids,
+ /*.pm = &mtk_pm_ops, */
+ },
+ /* .id_table = mtk_drm_platform_ids, */
+};
+
+static int mediatek_drm_init(void)
+{
+ int err;
+
+ err = platform_driver_register(&mediatek_ddp_driver);
+ if (err < 0) {
+ DRM_DEBUG_DRIVER("register ddp driver fail.\n");
+ return err;
+ }
+
+ err = platform_driver_register(&mtk_dsi_driver);
+ if (err < 0) {
+ DRM_DEBUG_DRIVER("register dsi driver fail.\n");
+ return err;
+ }
+
+ err = platform_driver_register(&mediatek_crtc_main_driver);
+ if (err < 0) {
+ DRM_DEBUG_DRIVER("register crtc_main driver fail.\n");
+ return err;
+ }
+
+ err = platform_driver_register(&mediatek_drm_platform_driver);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static void mediatek_drm_exit(void)
+{
+ platform_driver_unregister(&mediatek_drm_platform_driver);
+ platform_driver_unregister(&mtk_dsi_driver);
+}
+
+late_initcall(mediatek_drm_init);
+module_exit(mediatek_drm_exit);
+
+MODULE_AUTHOR("YT SHEN <yt.shen@xxxxxxxxxxxx>");
+MODULE_DESCRIPTION("Mediatek SoC DRM driver");
+MODULE_LICENSE("GPL");
+
+
diff --git a/drivers/gpu/drm/mediatek/mediatek_drm_drv.h b/drivers/gpu/drm/mediatek/mediatek_drm_drv.h
new file mode 100644
index 0000000..10ee4c4
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mediatek_drm_drv.h
@@ -0,0 +1,37 @@
+/*
+ * 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 _MEDIATEK_DRM_DRV_H_
+#define _MEDIATEK_DRM_DRV_H_
+
+#define MAX_CRTC 2
+#define MAX_PLANE 4
+
+extern struct platform_driver mediatek_ddp_driver;
+extern struct platform_driver mtk_dsi_driver;
+extern struct platform_driver mediatek_crtc_main_driver;
+
+struct mtk_drm_private {
+ struct drm_fb_helper *fb_helper;
+
+ /*
+ * 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];
+ unsigned int pipe;
+};
+
+
+#endif
+
diff --git a/drivers/gpu/drm/mediatek/mediatek_drm_dsi.c b/drivers/gpu/drm/mediatek/mediatek_drm_dsi.c
new file mode 100644
index 0000000..199ff9d
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mediatek_drm_dsi.c
@@ -0,0 +1,1333 @@
+/*
+ * 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 <drm/drm_crtc_helper.h>
+
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/of_graph.h>
+#include <linux/component.h>
+
+#include <linux/regulator/consumer.h>
+
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+
+#include <video/mipi_display.h>
+#include <video/videomode.h>
+
+#include "mediatek_drm_drv.h"
+#include "mediatek_drm_crtc.h"
+
+#include "mediatek_drm_ddp.h"
+
+#include "mediatek_drm_gem.h"
+#include "mediatek_drm_dsi.h"
+
+
+#define DSI_VIDEO_FIFO_DEPTH (1920 / 4)
+#define DSI_HOST_FIFO_DEPTH 64
+
+
+#define DSI_START 0x00
+
+#define DSI_CON_CTRL 0x10
+ #define DSI_RESET (1)
+
+#define DSI_MODE_CTRL 0x14
+ #define MODE 2
+ #define CMD_MODE 0
+ #define SYNC_PULSE_MODE 1
+ #define SYNC_EVENT_MODE 2
+ #define BURST_MODE 3
+ #define FRM_MODE (1<<16)
+ #define MIX_MODE (1<<17)
+
+#define DSI_TXRX_CTRL 0x18
+ #define VC_NUM (2<<0)
+ #define LANE_NUM (0xf<<2)
+ #define DIS_EOT (1<<6)
+ #define NULL_EN (1<<7)
+ #define TE_FREERUN (1<<8)
+ #define EXT_TE_EN (1<<9)
+ #define EXT_TE_EDGE (1<<10)
+ #define MAX_RTN_SIZE (0xf<<12)
+ #define HSTX_CKLP_EN (1<<16)
+
+#define DSI_PSCTRL 0x1c
+ #define DSI_PS_WC 0x3fff
+ #define DSI_PS_SEL (2<<16)
+ #define PACKED_PS_16BIT_RGB565 (0<<16)
+ #define LOOSELY_PS_18BIT_RGB666 (1<<16)
+ #define PACKED_PS_18BIT_RGB666 (2<<16)
+ #define PACKED_PS_24BIT_RGB888 (3<<16)
+
+#define DSI_VSA_NL 0x20
+#define DSI_VBP_NL 0x24
+#define DSI_VFP_NL 0x28
+
+#define DSI_VACT_NL 0x2C
+
+#define DSI_HSA_WC 0x50
+#define DSI_HBP_WC 0x54
+#define DSI_HFP_WC 0x58
+
+#define DSI_HSTX_CKL_WC 0x64
+
+#define DSI_PHY_LCCON 0x104
+ #define LC_HS_TX_EN (1)
+ #define LC_ULPM_EN (1<<1)
+ #define LC_WAKEUP_EN (1<<2)
+
+#define DSI_PHY_LD0CON 0x108
+ #define LD0_HS_TX_EN (1)
+ #define LD0_ULPM_EN (1<<1)
+ #define LD0_WAKEUP_EN (1<<2)
+
+#define DSI_PHY_TIMECON0 0x0110
+ #define LPX (0xff<<0)
+ #define HS_PRPR (0xff<<8)
+ #define HS_ZERO (0xff<<16)
+ #define HS_TRAIL (0xff<<24)
+
+#define DSI_PHY_TIMECON1 0x0114
+ #define TA_GO (0xff<<0)
+ #define TA_SURE (0xff<<8)
+ #define TA_GET (0xff<<16)
+ #define DA_HS_EXIT (0xff<<24)
+
+#define DSI_PHY_TIMECON2 0x0118
+ #define CONT_DET (0xff<<0)
+ #define CLK_ZERO (0xff<<16)
+ #define CLK_TRAIL (0xff<<24)
+
+#define DSI_PHY_TIMECON3 0x011c
+ #define CLK_HS_PRPR (0xff<<0)
+ #define CLK_HS_POST (0xff<<8)
+ #define CLK_HS_EXIT (0xff<<16)
+
+#define MIPITX_DSI0_CON 0x00
+ #define RG_DSI0_LDOCORE_EN (1)
+ #define RG_DSI0_CKG_LDOOUT_EN (1<<1)
+ #define RG_DSI0_BCLK_SEL (3<<2)
+ #define RG_DSI0_LD_IDX_SEL (7<<4)
+ #define RG_DSI0_PHYCLK_SEL (2<<8)
+ #define RG_DSI0_DSICLK_FREQ_SEL (1<<10)
+ #define RG_DSI0_LPTX_CLMP_EN (1<<11)
+
+#define MIPITX_DSI0_CLOCK_LANE 0x04
+ #define RG_DSI0_LNTC_LDOOUT_EN (1)
+ #define RG_DSI0_LNTC_CKLANE_EN (1<<1)
+ #define RG_DSI0_LNTC_LPTX_IPLUS1 (1<<2)
+ #define RG_DSI0_LNTC_LPTX_IPLUS2 (1<<3)
+ #define RG_DSI0_LNTC_LPTX_IMINUS (1<<4)
+ #define RG_DSI0_LNTC_LPCD_IPLUS (1<<5)
+ #define RG_DSI0_LNTC_LPCD_IMLUS (1<<6)
+ #define RG_DSI0_LNTC_RT_CODE (0xf<<8)
+
+#define MIPITX_DSI0_DATA_LANE0 0x08
+ #define RG_DSI0_LNT0_LDOOUT_EN (1)
+ #define RG_DSI0_LNT0_CKLANE_EN (1<<1)
+ #define RG_DSI0_LNT0_LPTX_IPLUS1 (1<<2)
+ #define RG_DSI0_LNT0_LPTX_IPLUS2 (1<<3)
+ #define RG_DSI0_LNT0_LPTX_IMINUS (1<<4)
+ #define RG_DSI0_LNT0_LPCD_IPLUS (1<<5)
+ #define RG_DSI0_LNT0_LPCD_IMINUS (1<<6)
+ #define RG_DSI0_LNT0_RT_CODE (0xf<<8)
+
+#define MIPITX_DSI0_DATA_LANE1 0x0c
+ #define RG_DSI0_LNT1_LDOOUT_EN (1)
+ #define RG_DSI0_LNT1_CKLANE_EN (1<<1)
+ #define RG_DSI0_LNT1_LPTX_IPLUS1 (1<<2)
+ #define RG_DSI0_LNT1_LPTX_IPLUS2 (1<<3)
+ #define RG_DSI0_LNT1_LPTX_IMINUS (1<<4)
+ #define RG_DSI0_LNT1_LPCD_IPLUS (1<<5)
+ #define RG_DSI0_LNT1_LPCD_IMINUS (1<<6)
+ #define RG_DSI0_LNT1_RT_CODE (0xf<<8)
+
+#define MIPITX_DSI0_DATA_LANE2 0x10
+ #define RG_DSI0_LNT2_LDOOUT_EN (1)
+ #define RG_DSI0_LNT2_CKLANE_EN (1<<1)
+ #define RG_DSI0_LNT2_LPTX_IPLUS1 (1<<2)
+ #define RG_DSI0_LNT2_LPTX_IPLUS2 (1<<3)
+ #define RG_DSI0_LNT2_LPTX_IMINUS (1<<4)
+ #define RG_DSI0_LNT2_LPCD_IPLUS (1<<5)
+ #define RG_DSI0_LNT2_LPCD_IMINUS (1<<6)
+ #define RG_DSI0_LNT2_RT_CODE (0xf<<8)
+
+#define MIPITX_DSI0_DATA_LANE3 0x14
+ #define RG_DSI0_LNT3_LDOOUT_EN (1)
+ #define RG_DSI0_LNT3_CKLANE_EN (1<<1)
+ #define RG_DSI0_LNT3_LPTX_IPLUS1 (1<<2)
+ #define RG_DSI0_LNT3_LPTX_IPLUS2 (1<<3)
+ #define RG_DSI0_LNT3_LPTX_IMINUS (1<<4)
+ #define RG_DSI0_LNT3_LPCD_IPLUS (1<<5)
+ #define RG_DSI0_LNT3_LPCD_IMINUS (1<<6)
+ #define RG_DSI0_LNT3_RT_CODE (0xf<<8)
+
+#define MIPITX_DSI_TOP_CON 0x40
+ #define RG_DSI_LNT_INTR_EN (1)
+ #define RG_DSI_LNT_HS_BIAS_EN (1<<1)
+ #define RG_DSI_LNT_IMP_CAL_EN (1<<2)
+ #define RG_DSI_LNT_TESTMODE_EN (1<<3)
+ #define RG_DSI_LNT_IMP_CAL_CODE (0xf<<4)
+ #define RG_DSI_LNT_AIO_SEL (7<<8)
+ #define RG_DSI_PAD_TIE_LOW_EN (1<<11)
+ #define RG_DSI_DEBUG_INPUT_EN (1<<12)
+ #define RG_DSI_PRESERVE (7<<13)
+
+#define MIPITX_DSI_BG_CON 0x44
+ #define RG_DSI_BG_CORE_EN 1
+ #define RG_DSI_BG_CKEN (1<<1)
+ #define RG_DSI_BG_DIV (0x3<<2)
+ #define RG_DSI_BG_FAST_CHARGE (1<<4)
+ #define RG_DSI_V12_SEL (7<<5)
+ #define RG_DSI_V10_SEL (7<<8)
+ #define RG_DSI_V072_SEL (7<<11)
+ #define RG_DSI_V04_SEL (7<<14)
+ #define RG_DSI_V032_SEL (7<<17)
+ #define RG_DSI_V02_SEL (7<<20)
+ #define rsv_23 (1<<)
+ #define RG_DSI_BG_R1_TRIM (0xf<<24)
+ #define RG_DSI_BG_R2_TRIM (0xf<<28)
+
+#define MIPITX_DSI_PLL_CON0 0x50
+ #define RG_DSI0_MPPLL_PLL_EN (1<<0)
+ #define RG_DSI0_MPPLL_PREDIV (3<<1)
+ #define RG_DSI0_MPPLL_TXDIV0 (3<<3)
+ #define RG_DSI0_MPPLL_TXDIV1 (3<<5)
+ #define RG_DSI0_MPPLL_POSDIV (7<<7)
+ #define RG_DSI0_MPPLL_MONVC_EN (1<<10)
+ #define RG_DSI0_MPPLL_MONREF_EN (1<<11)
+ #define RG_DSI0_MPPLL_VOD_EN (1<<12)
+
+#define MIPITX_DSI_PLL_CON1 0x54
+ #define RG_DSI0_MPPLL_SDM_FRA_EN (1)
+ #define RG_DSI0_MPPLL_SDM_SSC_PH_INIT (1<<1)
+ #define RG_DSI0_MPPLL_SDM_SSC_EN (1<<2)
+ #define RG_DSI0_MPPLL_SDM_SSC_PRD (0xffff<<16)
+
+#define MIPITX_DSI_PLL_CON2 0x58
+
+#define MIPITX_DSI_PLL_PWR 0x68
+ #define RG_DSI_MPPLL_SDM_PWR_ON (1<<0)
+ #define RG_DSI_MPPLL_SDM_ISO_EN (1<<1)
+ #define RG_DSI_MPPLL_SDM_PWR_ACK (1<<8)
+
+#define MIPITX_DSI_SW_CTRL 0x80
+ #define SW_CTRL_EN (1<<0)
+
+#define MIPITX_DSI_SW_CTRL_CON0 0x84
+ #define SW_LNTC_LPTX_PRE_OE (1<<0)
+ #define SW_LNTC_LPTX_OE (1<<1)
+ #define SW_LNTC_LPTX_P (1<<2)
+ #define SW_LNTC_LPTX_N (1<<3)
+ #define SW_LNTC_HSTX_PRE_OE (1<<4)
+ #define SW_LNTC_HSTX_OE (1<<5)
+ #define SW_LNTC_HSTX_ZEROCLK (1<<6)
+ #define SW_LNT0_LPTX_PRE_OE (1<<7)
+ #define SW_LNT0_LPTX_OE (1<<8)
+ #define SW_LNT0_LPTX_P (1<<9)
+ #define SW_LNT0_LPTX_N (1<<10)
+ #define SW_LNT0_HSTX_PRE_OE (1<<11)
+ #define SW_LNT0_HSTX_OE (1<<12)
+ #define SW_LNT0_LPRX_EN (1<<13)
+ #define SW_LNT1_LPTX_PRE_OE (1<<14)
+ #define SW_LNT1_LPTX_OE (1<<15)
+ #define SW_LNT1_LPTX_P (1<<16)
+ #define SW_LNT1_LPTX_N (1<<17)
+ #define SW_LNT1_HSTX_PRE_OE (1<<18)
+ #define SW_LNT1_HSTX_OE (1<<19)
+ #define SW_LNT2_LPTX_PRE_OE (1<<20)
+ #define SW_LNT2_LPTX_OE (1<<21)
+ #define SW_LNT2_LPTX_P (1<<22)
+ #define SW_LNT2_LPTX_N (1<<23)
+ #define SW_LNT2_HSTX_PRE_OE (1<<24)
+ #define SW_LNT2_HSTX_OE (1<<25)
+
+#define NS_TO_CYCLE(n, c) ((n) / c + (((n) % c) ? 1 : 0))
+
+
+static inline unsigned long mtk_dsi_readl(struct mtk_dsi *dsi,
+ unsigned long reg)
+{
+ return readl(dsi->dsi_reg_base + (reg << 2));
+}
+
+static inline void mtk_dsi_writel(struct mtk_dsi *dsi, unsigned long value,
+ unsigned long reg)
+{
+ writel(value, dsi->dsi_reg_base + (reg << 2));
+}
+
+static int mtk_dsi_of_read_u32(const struct device_node *np,
+ const char *propname, u32 *out_value)
+{
+ int ret = of_property_read_u32(np, propname, out_value);
+
+ if (ret < 0)
+ DRM_ERROR("%s: failed to get '%s' property\n", np->full_name,
+ propname);
+
+ return ret;
+}
+
+static void dsi_phy_clk_switch_off(struct mtk_dsi *dsi)
+{
+ u32 tmp_reg;
+
+ tmp_reg = readl(dsi->dsi_tx_reg_base + MIPITX_DSI_SW_CTRL_CON0);
+
+ tmp_reg = tmp_reg | (SW_LNTC_LPTX_PRE_OE | SW_LNTC_LPTX_OE |
+ SW_LNTC_HSTX_PRE_OE | SW_LNTC_HSTX_OE |
+ SW_LNT0_LPTX_PRE_OE | SW_LNT0_LPTX_OE |
+ SW_LNT0_HSTX_PRE_OE | SW_LNT0_HSTX_OE |
+ SW_LNT1_LPTX_PRE_OE | SW_LNT1_LPTX_OE |
+ SW_LNT1_HSTX_PRE_OE | SW_LNT1_HSTX_OE |
+ SW_LNT2_LPTX_PRE_OE | SW_LNT2_LPTX_OE |
+ SW_LNT2_HSTX_PRE_OE | SW_LNT2_HSTX_OE);
+ writel(tmp_reg, dsi->dsi_tx_reg_base + MIPITX_DSI_SW_CTRL_CON0);
+
+
+
+ tmp_reg = readl(dsi->dsi_tx_reg_base + MIPITX_DSI_SW_CTRL);
+ tmp_reg = (tmp_reg | SW_CTRL_EN);
+ writel(tmp_reg, dsi->dsi_tx_reg_base + MIPITX_DSI_SW_CTRL);
+
+
+ tmp_reg = readl(dsi->dsi_tx_reg_base + MIPITX_DSI_PLL_CON0);
+ tmp_reg = (tmp_reg & (~RG_DSI0_MPPLL_PLL_EN));
+ writel(tmp_reg, dsi->dsi_tx_reg_base + MIPITX_DSI_PLL_CON0);
+
+
+ udelay(100);
+
+ tmp_reg = readl(dsi->dsi_tx_reg_base + MIPITX_DSI_TOP_CON);
+ tmp_reg = (tmp_reg & (~(RG_DSI_LNT_HS_BIAS_EN |
+ RG_DSI_LNT_IMP_CAL_EN |
+ RG_DSI_LNT_TESTMODE_EN)));
+ writel(tmp_reg, dsi->dsi_tx_reg_base + MIPITX_DSI_TOP_CON);
+
+ tmp_reg = readl(dsi->dsi_tx_reg_base + MIPITX_DSI0_CLOCK_LANE);
+ tmp_reg = tmp_reg & (~RG_DSI0_LNTC_LDOOUT_EN);
+ writel(tmp_reg, dsi->dsi_tx_reg_base + MIPITX_DSI0_CLOCK_LANE);
+
+ tmp_reg = readl(dsi->dsi_tx_reg_base + MIPITX_DSI0_DATA_LANE0);
+ tmp_reg = tmp_reg & (~RG_DSI0_LNT0_LDOOUT_EN);
+ writel(tmp_reg, dsi->dsi_tx_reg_base + MIPITX_DSI0_DATA_LANE0);
+
+ tmp_reg = readl(dsi->dsi_tx_reg_base + MIPITX_DSI0_DATA_LANE1);
+ tmp_reg = tmp_reg & (~RG_DSI0_LNT1_LDOOUT_EN);
+ writel(tmp_reg, dsi->dsi_tx_reg_base + MIPITX_DSI0_DATA_LANE1);
+
+ tmp_reg = readl(dsi->dsi_tx_reg_base + MIPITX_DSI0_DATA_LANE2);
+ tmp_reg = tmp_reg & (~RG_DSI0_LNT2_LDOOUT_EN);
+ writel(tmp_reg, dsi->dsi_tx_reg_base + MIPITX_DSI0_DATA_LANE2);
+
+ tmp_reg = readl(dsi->dsi_tx_reg_base + MIPITX_DSI0_DATA_LANE3);
+ tmp_reg = tmp_reg & (~RG_DSI0_LNT3_LDOOUT_EN);
+ writel(tmp_reg, dsi->dsi_tx_reg_base + MIPITX_DSI0_DATA_LANE3);
+
+
+ udelay(100);
+
+ tmp_reg = readl(dsi->dsi_tx_reg_base + MIPITX_DSI0_CON);
+ tmp_reg = tmp_reg & (~(RG_DSI0_CKG_LDOOUT_EN |
+ RG_DSI0_LDOCORE_EN));
+ writel(tmp_reg, dsi->dsi_tx_reg_base + MIPITX_DSI0_CON);
+
+ tmp_reg = readl(dsi->dsi_tx_reg_base + MIPITX_DSI_BG_CON);
+ tmp_reg = tmp_reg & (~(RG_DSI_BG_CKEN | RG_DSI_BG_CORE_EN));
+ writel(tmp_reg, dsi->dsi_tx_reg_base + MIPITX_DSI_BG_CON);
+
+
+ udelay(100);
+
+ tmp_reg = readl(dsi->dsi_tx_reg_base + MIPITX_DSI_PLL_CON0);
+ tmp_reg = (tmp_reg & (~RG_DSI0_MPPLL_PLL_EN));
+ writel(tmp_reg, dsi->dsi_tx_reg_base + MIPITX_DSI_PLL_CON0);
+}
+
+static void dsi_phy_clk_setting(struct mtk_dsi *dsi)
+{
+ unsigned int data_Rate = dsi->pll_clk_rate * 2;
+ unsigned int txdiv = 0;
+ unsigned int txdiv0 = 0;
+ unsigned int txdiv1 = 0;
+ unsigned int pcw = 0;
+ u32 reg;
+ u32 temp;
+
+ reg = readl(dsi->dsi_tx_reg_base + MIPITX_DSI_BG_CON);
+ reg = (reg & (~RG_DSI_V032_SEL)) | (4<<17);
+ reg = (reg & (~RG_DSI_V04_SEL)) | (4<<14);
+ reg = (reg & (~RG_DSI_V072_SEL)) | (4<<11);
+ reg = (reg & (~RG_DSI_V10_SEL)) | (4<<8);
+ reg = (reg & (~RG_DSI_V12_SEL)) | (4<<5);
+ reg = (reg & (~RG_DSI_BG_CKEN)) | (1<<1);
+ reg = (reg & (~RG_DSI_BG_CORE_EN)) | (1);
+ writel(reg, dsi->dsi_tx_reg_base + MIPITX_DSI_BG_CON);
+
+ udelay(1000);
+
+ reg = readl(dsi->dsi_tx_reg_base + MIPITX_DSI_TOP_CON);
+ reg = (reg & (~RG_DSI_LNT_IMP_CAL_CODE)) | (8<<4);
+ reg = (reg & (~RG_DSI_LNT_HS_BIAS_EN)) | (1<<1);
+ writel(reg, dsi->dsi_tx_reg_base + MIPITX_DSI_TOP_CON);
+
+ reg = readl(dsi->dsi_tx_reg_base + MIPITX_DSI0_CON);
+ reg = (reg & (~RG_DSI0_CKG_LDOOUT_EN)) | (1<<1);
+ reg = (reg & (~RG_DSI0_LDOCORE_EN)) | (1);
+ writel(reg, dsi->dsi_tx_reg_base + MIPITX_DSI0_CON);
+
+ reg = readl(dsi->dsi_tx_reg_base + MIPITX_DSI_PLL_PWR);
+ reg = (reg & (~RG_DSI_MPPLL_SDM_PWR_ON)) | (1<<0);
+ reg = (reg & (~RG_DSI_MPPLL_SDM_ISO_EN));
+ writel(reg, dsi->dsi_tx_reg_base + MIPITX_DSI_PLL_PWR);
+
+ reg = readl(dsi->dsi_tx_reg_base + MIPITX_DSI_PLL_CON0);
+ reg = (reg & (~RG_DSI0_MPPLL_PLL_EN));
+ writel(reg, dsi->dsi_tx_reg_base + MIPITX_DSI_PLL_CON0);
+
+ udelay(1000);
+
+ if (data_Rate > 1250) {
+ txdiv = 1;
+ txdiv0 = 0;
+ txdiv1 = 0;
+ } else if (data_Rate >= 500) {
+ txdiv = 1;
+ txdiv0 = 0;
+ txdiv1 = 0;
+ } else if (data_Rate >= 250) {
+ txdiv = 2;
+ txdiv0 = 1;
+ txdiv1 = 0;
+ } else if (data_Rate >= 125) {
+ txdiv = 4;
+ txdiv0 = 2;
+ txdiv1 = 0;
+ } else if (data_Rate > 62) {
+ txdiv = 8;
+ txdiv0 = 2;
+ txdiv1 = 1;
+ } else if (data_Rate >= 50) {
+ txdiv = 16;
+ txdiv0 = 2;
+ txdiv1 = 2;
+ } else {
+ }
+
+ reg = readl(dsi->dsi_tx_reg_base + MIPITX_DSI_PLL_CON0);
+
+ switch (txdiv) {
+ case 1:
+ reg = (reg & (~RG_DSI0_MPPLL_TXDIV0)) | (0<<3);
+ reg = (reg & (~RG_DSI0_MPPLL_TXDIV1)) | (0<<5);
+
+ break;
+ case 2:
+ reg = (reg & (~RG_DSI0_MPPLL_TXDIV0)) | (1<<3);
+ reg = (reg & (~RG_DSI0_MPPLL_TXDIV1)) | (0<<5);
+ break;
+ case 4:
+ reg = (reg & (~RG_DSI0_MPPLL_TXDIV0)) | (2<<3);
+ reg = (reg & (~RG_DSI0_MPPLL_TXDIV1)) | (0<<5);
+ break;
+ case 8:
+ reg = (reg & (~RG_DSI0_MPPLL_TXDIV0)) | (2<<3);
+ reg = (reg & (~RG_DSI0_MPPLL_TXDIV1)) | (1<<5);
+ break;
+ case 16:
+ reg = (reg & (~RG_DSI0_MPPLL_TXDIV0)) | (2<<3);
+ reg = (reg & (~RG_DSI0_MPPLL_TXDIV1)) | (2<<5);
+ break;
+
+ default:
+ break;
+ }
+ reg = (reg & (~RG_DSI0_MPPLL_PREDIV));
+ writel(reg, dsi->dsi_tx_reg_base + MIPITX_DSI_PLL_CON0);
+
+ pcw = data_Rate * txdiv / 13;
+ temp = data_Rate * txdiv % 13;
+ reg = ((pcw & 0x7f)<<24) + (((256 * temp / 13) & 0xff)<<16)
+ + (((256 * (256 * temp % 13)/13) & 0xff)<<8)
+ + ((256 * (256 * (256 * temp % 13) % 13) / 13) & 0xff);
+ writel(reg, dsi->dsi_tx_reg_base + MIPITX_DSI_PLL_CON2);
+
+ reg = readl(dsi->dsi_tx_reg_base + MIPITX_DSI_PLL_CON1);
+ reg = (reg & (~RG_DSI0_MPPLL_SDM_FRA_EN)) | (1<<0);
+ writel(reg, dsi->dsi_tx_reg_base + MIPITX_DSI_PLL_CON1);
+
+ reg = readl(dsi->dsi_tx_reg_base + MIPITX_DSI0_CLOCK_LANE);
+ reg = (reg & (~RG_DSI0_LNTC_LDOOUT_EN)) | (1<<0);
+ writel(reg, dsi->dsi_tx_reg_base + MIPITX_DSI0_CLOCK_LANE);
+
+ reg = readl(dsi->dsi_tx_reg_base + MIPITX_DSI0_DATA_LANE0);
+ reg = (reg & (~RG_DSI0_LNT0_LDOOUT_EN)) | (1<<0);
+ writel(reg, dsi->dsi_tx_reg_base + MIPITX_DSI0_DATA_LANE0);
+
+ reg = readl(dsi->dsi_tx_reg_base + MIPITX_DSI0_DATA_LANE1);
+ reg = (reg & (~RG_DSI0_LNT1_LDOOUT_EN)) | (1<<0);
+ writel(reg, dsi->dsi_tx_reg_base + MIPITX_DSI0_DATA_LANE1);
+
+ reg = readl(dsi->dsi_tx_reg_base + MIPITX_DSI0_DATA_LANE2);
+ reg = (reg & (~RG_DSI0_LNT2_LDOOUT_EN)) | (1<<0);
+ writel(reg, dsi->dsi_tx_reg_base + MIPITX_DSI0_DATA_LANE2);
+
+ reg = readl(dsi->dsi_tx_reg_base + MIPITX_DSI0_DATA_LANE3);
+ reg = (reg & (~RG_DSI0_LNT3_LDOOUT_EN)) | (1<<0);
+ writel(reg, dsi->dsi_tx_reg_base + MIPITX_DSI0_DATA_LANE3);
+
+ reg = readl(dsi->dsi_tx_reg_base + MIPITX_DSI_PLL_CON0);
+ reg = (reg & (~RG_DSI0_MPPLL_PLL_EN)) | (1<<0);
+ writel(reg, dsi->dsi_tx_reg_base + MIPITX_DSI_PLL_CON0);
+
+ udelay(1000);
+ reg = readl(dsi->dsi_tx_reg_base + MIPITX_DSI_PLL_CON1);
+ reg = (reg & (~RG_DSI0_MPPLL_SDM_SSC_EN));
+ writel(reg, dsi->dsi_tx_reg_base + MIPITX_DSI_PLL_CON1);
+
+ reg = readl(dsi->dsi_tx_reg_base + MIPITX_DSI_TOP_CON);
+ reg = (reg & (~RG_DSI_PAD_TIE_LOW_EN));
+ writel(reg, dsi->dsi_tx_reg_base + MIPITX_DSI_TOP_CON);
+}
+
+static void dsi_phy_timconfig(struct mtk_dsi *dsi)
+{
+ u32 timcon0 = 0;
+ u32 timcon1 = 0;
+ u32 timcon2 = 0;
+ u32 timcon3 = 0;
+ unsigned int lane_no = dsi->lanes;
+ unsigned int cycle_time;
+ unsigned int ui;
+ unsigned int hs_trail_m, hs_trail_n;
+
+ ui = 1000/(250 * 2) + 0x01;
+ cycle_time = 8000/(250 * 2) + 0x01;
+
+ hs_trail_m = lane_no;
+ hs_trail_n = NS_TO_CYCLE(((lane_no * 4 * ui) + 60), cycle_time);
+
+ timcon0 = (timcon0 & (~HS_TRAIL)) | (8<<24);
+ timcon0 = (timcon0 & (~HS_PRPR)) | 0x6<<8;
+
+ if ((timcon0 & HS_PRPR) == 0)
+ timcon0 = (timcon0 & (~HS_PRPR)) | 1<<8;
+
+ timcon0 = (timcon0 & (~HS_ZERO)) | 0xA<<16;
+ timcon0 = (timcon0 & (~LPX)) | 5;
+
+ if ((timcon0 & LPX) == 0)
+ timcon0 = (timcon0 & (~LPX)) | 1;
+
+ timcon1 = (timcon1 & (~TA_GET)) | (5 * (timcon0 & LPX)<<16);
+ timcon1 = (timcon1 & (~TA_SURE)) | ((3 * (timcon0 & LPX) / 2) << 8);
+ timcon1 = (timcon1 & (~TA_GO)) | (4 * (timcon0 & LPX));
+ timcon1 = (timcon1 & (~DA_HS_EXIT)) | (7<<24);
+ timcon2 = (timcon2 & (~CLK_TRAIL)) | ((NS_TO_CYCLE(0x64, cycle_time) +
+ 0x0a)<<24);
+
+ if (((timcon2 & CLK_TRAIL)>>24) < 2)
+ timcon2 = (timcon2 & (~CLK_TRAIL)) | (2<<24);
+
+ timcon2 = (timcon2 & (~CONT_DET));
+ timcon3 = (timcon3 & (~CLK_HS_PRPR)) | NS_TO_CYCLE(0x40, cycle_time);
+ if ((timcon3 & CLK_HS_PRPR) == 0)
+ timcon3 = (timcon3 & (~CLK_HS_PRPR)) | 1;
+
+ timcon2 = (timcon2 & (~CLK_ZERO)) |
+ (NS_TO_CYCLE(0x190 - (timcon3 & CLK_HS_PRPR) * cycle_time,
+ cycle_time)<<16);
+
+ timcon3 = (timcon3 & (~CLK_HS_EXIT)) | ((2 * (timcon0 & LPX))<<16);
+ timcon3 = (timcon3 & (~CLK_HS_POST)) | (NS_TO_CYCLE((80 + 52 * ui),
+ cycle_time)<<8);
+
+ writel(timcon0, dsi->dsi_reg_base + DSI_PHY_TIMECON0);
+ writel(timcon1, dsi->dsi_reg_base + DSI_PHY_TIMECON1);
+ writel(timcon2, dsi->dsi_reg_base + DSI_PHY_TIMECON2);
+ writel(timcon3, dsi->dsi_reg_base + DSI_PHY_TIMECON3);
+}
+
+static void mtk_dsi_reset(struct mtk_dsi *dsi)
+{
+ writel(3, dsi->dsi_reg_base + DSI_CON_CTRL);
+ writel(2, dsi->dsi_reg_base + DSI_CON_CTRL);
+}
+
+static int mtk_dsi_poweron(struct mtk_dsi *dsi)
+{
+ int ret;
+ struct drm_device *dev = dsi->drm_dev;
+
+ dsi_phy_clk_setting(dsi);
+
+ ret = clk_prepare_enable(dsi->dsi0_engine_clk_cg);
+ if (ret < 0) {
+ dev_err(dev->dev, "can't enable dsi0_engine_clk_cg %d\n", ret);
+ goto err_dsi0_engine_clk_cg;
+ }
+
+ ret = clk_prepare_enable(dsi->dsi0_digital_clk_cg);
+ if (ret < 0) {
+ dev_err(dev->dev, "can't enable dsi0_digital_clk_cg %d\n", ret);
+ goto err_dsi0_digital_clk_cg;
+ }
+
+ mtk_dsi_reset((dsi));
+ dsi_phy_timconfig(dsi);
+
+ return 0;
+
+err_dsi0_digital_clk_cg:
+ clk_disable_unprepare(dsi->dsi0_engine_clk_cg);
+
+err_dsi0_engine_clk_cg:
+
+ return ret;
+}
+
+static void dsi_clk_ulp_mode_enter(struct mtk_dsi *dsi)
+{
+ u32 tmp_reg1;
+
+ tmp_reg1 = readl(dsi->dsi_reg_base + DSI_PHY_LCCON);
+ tmp_reg1 = tmp_reg1 & (~LC_HS_TX_EN);
+ writel(tmp_reg1, dsi->dsi_reg_base + DSI_PHY_LCCON);
+ udelay(100);
+ tmp_reg1 = tmp_reg1 & (~LC_ULPM_EN);
+ writel(tmp_reg1, dsi->dsi_reg_base + DSI_PHY_LCCON);
+ udelay(100);
+}
+
+static void dsi_clk_ulp_mode_leave(struct mtk_dsi *dsi)
+{
+ u32 tmp_reg1;
+
+ tmp_reg1 = readl(dsi->dsi_reg_base + DSI_PHY_LCCON);
+ tmp_reg1 = tmp_reg1 & (~LC_ULPM_EN);
+ writel(tmp_reg1, dsi->dsi_reg_base + DSI_PHY_LCCON);
+ udelay(100);
+ tmp_reg1 = tmp_reg1 | LC_WAKEUP_EN;
+ writel(tmp_reg1, dsi->dsi_reg_base + DSI_PHY_LCCON);
+ udelay(100);
+ tmp_reg1 = tmp_reg1 & (~LC_WAKEUP_EN);
+ writel(tmp_reg1, dsi->dsi_reg_base + DSI_PHY_LCCON);
+ udelay(100);
+}
+
+static void dsi_lane0_ulp_mode(struct mtk_dsi *dsi, bool enter)
+{
+ u32 tmp_reg1;
+
+ tmp_reg1 = readl(dsi->dsi_reg_base + DSI_PHY_LD0CON);
+
+ if (enter) {
+ tmp_reg1 = tmp_reg1 & (~LD0_HS_TX_EN);
+ writel(tmp_reg1, dsi->dsi_reg_base + DSI_PHY_LD0CON);
+ udelay(100);
+ tmp_reg1 = tmp_reg1 & (~LD0_ULPM_EN);
+ writel(tmp_reg1, dsi->dsi_reg_base + DSI_PHY_LD0CON);
+ udelay(100);
+ } else {
+ tmp_reg1 = tmp_reg1 & (~LD0_ULPM_EN);
+ writel(tmp_reg1, dsi->dsi_reg_base + DSI_PHY_LD0CON);
+ udelay(100);
+ tmp_reg1 = tmp_reg1 | LD0_WAKEUP_EN;
+ writel(tmp_reg1, dsi->dsi_reg_base + DSI_PHY_LD0CON);
+ udelay(100);
+ tmp_reg1 = tmp_reg1 & (~LD0_WAKEUP_EN);
+ writel(tmp_reg1, dsi->dsi_reg_base + DSI_PHY_LD0CON);
+ udelay(100);
+ }
+}
+
+static bool dsi_clk_hs_state(struct mtk_dsi *dsi)
+{
+ u32 tmp_reg1;
+
+ tmp_reg1 = readl(dsi->dsi_reg_base + DSI_PHY_LCCON);
+
+ return ((tmp_reg1 & LC_HS_TX_EN) == 1) ? true : false;
+}
+
+static void dsi_clk_hs_mode(struct mtk_dsi *dsi, bool enter)
+{
+ u32 tmp_reg1;
+
+ tmp_reg1 = readl(dsi->dsi_reg_base + DSI_PHY_LCCON);
+
+ if (enter && !dsi_clk_hs_state(dsi)) {
+ tmp_reg1 = tmp_reg1 | LC_HS_TX_EN;
+ writel(tmp_reg1, dsi->dsi_reg_base + DSI_PHY_LCCON);
+ } else if (!enter && dsi_clk_hs_state(dsi)) {
+ tmp_reg1 = tmp_reg1 & (~LC_HS_TX_EN);
+ writel(tmp_reg1, dsi->dsi_reg_base + DSI_PHY_LCCON);
+ }
+}
+
+static void dsi_set_mode(struct mtk_dsi *dsi)
+{
+ u32 tmp_reg1;
+
+ tmp_reg1 = 0;
+
+ if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
+ tmp_reg1 = SYNC_PULSE_MODE;
+
+ if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
+ tmp_reg1 = BURST_MODE;
+
+ if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
+ tmp_reg1 = SYNC_PULSE_MODE;
+ }
+
+ writel(tmp_reg1, dsi->dsi_reg_base + DSI_MODE_CTRL);
+}
+
+static void dsi_ps_control_vact(struct mtk_dsi *dsi)
+{
+ struct videomode *vm = &dsi->vm;
+ u32 dsiTmpBufBpp, ps_wc;
+ u32 tmp_reg;
+ u32 tmp_hstx_cklp_wc;
+
+ tmp_reg = 0;
+
+ if (dsi->format == MIPI_DSI_FMT_RGB565)
+ dsiTmpBufBpp = 2;
+ else
+ dsiTmpBufBpp = 3;
+
+ ps_wc = vm->vactive * dsiTmpBufBpp;
+
+ tmp_reg = ps_wc;
+
+ switch (dsi->format) {
+ case MIPI_DSI_FMT_RGB888:
+ tmp_reg |= PACKED_PS_24BIT_RGB888;
+ break;
+ case MIPI_DSI_FMT_RGB666:
+ tmp_reg |= PACKED_PS_18BIT_RGB666;
+ break;
+ case MIPI_DSI_FMT_RGB666_PACKED:
+ tmp_reg |= LOOSELY_PS_18BIT_RGB666;
+ break;
+ case MIPI_DSI_FMT_RGB565:
+ tmp_reg |= PACKED_PS_16BIT_RGB565;
+ break;
+ }
+
+ tmp_hstx_cklp_wc = ps_wc;
+
+ writel(vm->vactive, dsi->dsi_reg_base + DSI_VACT_NL);
+ writel(tmp_reg, dsi->dsi_reg_base + DSI_PSCTRL);
+ writel(tmp_hstx_cklp_wc, dsi->dsi_reg_base + DSI_HSTX_CKL_WC);
+}
+
+static void dsi_rxtx_control(struct mtk_dsi *dsi)
+{
+ u32 tmp_reg = 0;
+
+ switch (dsi->lanes) {
+ case 1:
+ tmp_reg = 1<<2;
+ break;
+ case 2:
+ tmp_reg = 3<<2;
+ break;
+ case 3:
+ tmp_reg = 7<<2;
+ break;
+ case 4:
+ tmp_reg = 0xF<<2;
+ break;
+ default:
+ tmp_reg = 0xF<<2;
+ break;
+ }
+
+ writel(tmp_reg, dsi->dsi_reg_base + DSI_TXRX_CTRL);
+}
+
+void dsi_ps_control(struct mtk_dsi *dsi)
+{
+ unsigned int dsi_tmp_buf_bpp;
+ u32 tmp_reg1 = 0;
+
+ switch (dsi->format) {
+ case MIPI_DSI_FMT_RGB888:
+ tmp_reg1 = PACKED_PS_24BIT_RGB888;
+ dsi_tmp_buf_bpp = 3;
+ break;
+ case MIPI_DSI_FMT_RGB666:
+ tmp_reg1 = LOOSELY_PS_18BIT_RGB666;
+ dsi_tmp_buf_bpp = 3;
+ break;
+ case MIPI_DSI_FMT_RGB666_PACKED:
+ tmp_reg1 = PACKED_PS_18BIT_RGB666;
+ dsi_tmp_buf_bpp = 3;
+ break;
+ case MIPI_DSI_FMT_RGB565:
+ tmp_reg1 = PACKED_PS_16BIT_RGB565;
+ dsi_tmp_buf_bpp = 2;
+ break;
+ default:
+ tmp_reg1 = PACKED_PS_24BIT_RGB888;
+ dsi_tmp_buf_bpp = 3;
+ break;
+ }
+
+ tmp_reg1 = tmp_reg1 + ((dsi->vm.hactive * dsi_tmp_buf_bpp) & DSI_PS_WC);
+
+ writel(tmp_reg1, dsi->dsi_reg_base + DSI_PSCTRL);
+}
+
+static void dsi_config_vdo_timing(struct mtk_dsi *dsi)
+{
+ unsigned int horizontal_sync_active_byte;
+ unsigned int horizontal_backporch_byte;
+ unsigned int horizontal_frontporch_byte;
+ unsigned int dsi_tmp_buf_bpp;
+
+ struct videomode *vm = &dsi->vm;
+
+ if (dsi->format == MIPI_DSI_FMT_RGB565)
+ dsi_tmp_buf_bpp = 2;
+ else
+ dsi_tmp_buf_bpp = 3;
+
+ writel(vm->vsync_len, dsi->dsi_reg_base + DSI_VSA_NL);
+ writel(vm->vback_porch, dsi->dsi_reg_base + DSI_VBP_NL);
+ writel(vm->vfront_porch, dsi->dsi_reg_base + DSI_VFP_NL);
+ writel(vm->vactive, dsi->dsi_reg_base + DSI_VACT_NL);
+
+ horizontal_sync_active_byte = (vm->hsync_len * dsi_tmp_buf_bpp - 10);
+
+ if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
+ horizontal_backporch_byte =
+ (vm->hback_porch * dsi_tmp_buf_bpp - 10);
+ else
+ horizontal_backporch_byte = ((vm->hback_porch + vm->hsync_len) *
+ dsi_tmp_buf_bpp - 10);
+
+ horizontal_frontporch_byte = (vm->vfront_porch * dsi_tmp_buf_bpp - 12);
+
+ writel(vm->hsync_len, dsi->dsi_reg_base + DSI_HSA_WC);
+ writel(vm->hback_porch, dsi->dsi_reg_base + DSI_HBP_WC);
+ writel(vm->hfront_porch, dsi->dsi_reg_base + DSI_HFP_WC);
+
+ dsi_ps_control(dsi);
+}
+
+static void mtk_dsi_start(struct mtk_dsi *dsi)
+{
+ writel(0, dsi->dsi_reg_base + DSI_START);
+ writel(1, dsi->dsi_reg_base + DSI_START);
+}
+
+static void mtk_dsi_poweroff(struct mtk_dsi *dsi)
+{
+ clk_disable_unprepare(dsi->dsi0_engine_clk_cg);
+ clk_disable_unprepare(dsi->dsi0_digital_clk_cg);
+
+ usleep_range(10000, 20000);
+
+ dsi_phy_clk_switch_off(dsi);
+}
+
+static int mtk_output_dsi_enable(struct mtk_dsi *dsi)
+{
+ int ret;
+
+ if (dsi->enabled == true)
+ return 0;
+
+ ret = mtk_dsi_poweron(dsi);
+ if (ret < 0)
+ return ret;
+
+ dsi_rxtx_control(dsi);
+
+ dsi_clk_ulp_mode_leave(dsi);
+ dsi_lane0_ulp_mode(dsi, 0);
+ dsi_clk_hs_mode(dsi, 0);
+ dsi_set_mode(dsi);
+
+ dsi_ps_control_vact(dsi);
+ dsi_config_vdo_timing(dsi);
+
+ dsi_set_mode(dsi);
+ dsi_clk_hs_mode(dsi, 1);
+
+ mtk_dsi_start(dsi);
+
+ dsi->enabled = true;
+
+ return 0;
+}
+
+static int mtk_output_dsi_disable(struct mtk_dsi *dsi)
+{
+ if (dsi->enabled == false)
+ return 0;
+
+ dsi_lane0_ulp_mode(dsi, 1);
+ dsi_clk_ulp_mode_enter(dsi);
+ mtk_dsi_poweroff(dsi);
+ dsi_phy_clk_switch_off(dsi);
+
+ dsi->enabled = false;
+
+ return 0;
+}
+
+static void mtk_dsi_encoder_destroy(struct drm_encoder *encoder)
+{
+ drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_funcs mtk_dsi_encoder_funcs = {
+ .destroy = mtk_dsi_encoder_destroy,
+};
+
+static void mtk_dsi_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct mtk_dsi *dsi = encoder_to_dsi(encoder);
+ struct drm_panel *panel = dsi->panel;
+
+ mtk_dsi_info("%s dpms mode = %d !\n", __func__, mode);
+
+ if (mode != DRM_MODE_DPMS_ON) {
+ drm_panel_disable(panel);
+ mtk_output_dsi_disable(dsi);
+ } else {
+ mtk_output_dsi_enable(dsi);
+ drm_panel_enable(panel);
+ }
+}
+
+static bool mtk_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+
+static void mtk_dsi_encoder_prepare(struct drm_encoder *encoder)
+{
+ /* drm framework doesn't check NULL. */
+}
+
+static void mtk_dsi_encoder_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode, struct drm_display_mode *adjusted)
+{
+}
+
+static void mtk_dsi_encoder_commit(struct drm_encoder *encoder)
+{
+ /* DRM_MODE_DPMS_ON? */
+}
+
+static enum drm_connector_status mtk_dsi_connector_detect(
+ struct drm_connector *connector, bool force)
+{
+ enum drm_connector_status status = connector_status_unknown;
+
+ status = connector_status_connected; /* FIXME? */
+
+ return status;
+}
+
+static void mtk_dsi_connector_destroy(struct drm_connector *connector)
+{
+ drm_connector_unregister(connector);
+ drm_connector_cleanup(connector);
+}
+
+static const struct drm_display_mode default_modes[] = {
+ /* 1368x768@60Hz */
+ { DRM_MODE("1368x768", DRM_MODE_TYPE_DRIVER, 72070,
+ 1368, 1368 + 58, 1368 + 58 + 58, 1368 + 58 + 58 + 58, 0,
+ 768, 768 + 4, 768 + 4 + 4, 768 + 4 + 4 + 4, 0, 0) },
+};
+
+static int mtk_dsi_connector_get_modes(struct drm_connector *connector)
+{
+ const struct drm_display_mode *ptr = &default_modes[0];
+ struct drm_display_mode *mode;
+ int count = 0;
+
+ mode = drm_mode_duplicate(connector->dev, ptr);
+ if (mode) {
+ drm_mode_probed_add(connector, mode);
+ count++;
+ }
+
+ connector->display_info.width_mm = mode->hdisplay;
+ connector->display_info.height_mm = mode->vdisplay;
+
+ return 1;
+}
+
+static struct drm_encoder *
+mtk_dsi_connector_best_encoder(struct drm_connector *connector)
+{
+ struct mtk_dsi *dsi = connector_to_dsi(connector);
+
+ return &dsi->encoder;
+}
+
+static const struct drm_encoder_helper_funcs mtk_dsi_encoder_helper_funcs = {
+ .dpms = mtk_dsi_encoder_dpms,
+ .mode_fixup = mtk_dsi_encoder_mode_fixup,
+ .prepare = mtk_dsi_encoder_prepare,
+ .mode_set = mtk_dsi_encoder_mode_set,
+ .commit = mtk_dsi_encoder_commit,
+};
+
+static const struct drm_connector_funcs mtk_dsi_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .detect = mtk_dsi_connector_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = mtk_dsi_connector_destroy,
+};
+
+static const struct drm_connector_helper_funcs
+ mtk_dsi_connector_helper_funcs = {
+ .get_modes = mtk_dsi_connector_get_modes,
+ .best_encoder = mtk_dsi_connector_best_encoder,
+};
+
+struct bridge_init {
+ struct i2c_client *mipirx_client;
+ struct i2c_client *dptx_client;
+ struct device_node *node_mipirx;
+ struct device_node *node_dptx;
+};
+
+static int mtk_drm_attach_lcm_bridge(struct drm_bridge *bridge,
+ struct drm_encoder *encoder)
+{
+ int ret;
+
+ encoder->bridge = bridge;
+ bridge->encoder = encoder;
+ ret = drm_bridge_attach(encoder->dev, bridge);
+ if (ret) {
+ DRM_ERROR("Failed to attach bridge to drm\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mtk_dsi_create_conn_enc(struct mtk_dsi *dsi)
+{
+ int ret;
+
+ ret = drm_encoder_init(dsi->drm_dev, &dsi->encoder,
+ &mtk_dsi_encoder_funcs, DRM_MODE_ENCODER_DSI);
+
+ if (ret)
+ goto errcode;
+
+ drm_encoder_helper_add(&dsi->encoder, &mtk_dsi_encoder_helper_funcs);
+
+ dsi->encoder.possible_crtcs = 1;
+
+ /* Pre-empt DP connector creation if there's a bridge */
+ ret = mtk_drm_attach_lcm_bridge(dsi->bridge, &dsi->encoder);
+ if (!ret)
+ return 0;
+
+ ret = drm_connector_init(dsi->drm_dev, &dsi->conn,
+ &mtk_dsi_connector_funcs, DRM_MODE_CONNECTOR_DSI);
+ if (ret)
+ goto errcode;
+
+ drm_connector_helper_add(&dsi->conn, &mtk_dsi_connector_helper_funcs);
+
+ ret = drm_connector_register(&dsi->conn);
+ if (ret)
+ goto errcode;
+
+ dsi->conn.dpms = DRM_MODE_DPMS_OFF;
+ dsi->conn.encoder = &dsi->encoder;
+
+ drm_mode_connector_attach_encoder(&dsi->conn, &dsi->encoder);
+
+ if (dsi->panel)
+ ret = drm_panel_attach(dsi->panel, &dsi->conn);
+
+ return 0;
+
+errcode:
+ drm_encoder_cleanup(&dsi->encoder);
+ drm_connector_unregister(&dsi->conn);
+ drm_connector_cleanup(&dsi->conn);
+
+ return ret;
+}
+
+static void mtk_dsi_destroy_conn_enc(struct mtk_dsi *dsi)
+{
+ drm_encoder_cleanup(&dsi->encoder);
+ drm_connector_unregister(&dsi->conn);
+ drm_connector_cleanup(&dsi->conn);
+}
+
+static int mtk_dsi_bind(struct device *dev, struct device *master,
+ void *data)
+{
+ int ret;
+ struct mtk_dsi *dsi = NULL;
+
+ dsi = platform_get_drvdata(to_platform_device(dev));
+ if (!dsi) {
+ ret = -EFAULT;
+ goto errcode;
+ }
+
+ dsi->drm_dev = data;
+
+ ret = mtk_dsi_create_conn_enc(dsi);
+ if (ret) {
+ DRM_ERROR("Encoder create failed with %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+
+errcode:
+ return ret;
+
+}
+
+static void mtk_dsi_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct mtk_dsi *dsi = NULL;
+
+ dsi = platform_get_drvdata(to_platform_device(dev));
+ mtk_dsi_destroy_conn_enc(dsi);
+
+ dsi->drm_dev = NULL;
+}
+
+static const struct component_ops mtk_dsi_component_ops = {
+ .bind = mtk_dsi_bind,
+ .unbind = mtk_dsi_unbind,
+};
+
+static int mtk_dsi_host_attach(struct mipi_dsi_host *host,
+ struct mipi_dsi_device *device)
+{
+ struct mtk_dsi *dsi = host_to_mtk(host);
+
+ dsi->mode_flags = device->mode_flags;
+ dsi->format = device->format;
+ dsi->lanes = device->lanes;
+
+ dsi->panel = of_drm_find_panel(device->dev.of_node);
+ if (dsi->panel) {
+ if (dsi->conn.dev)
+ drm_helper_hpd_irq_event(dsi->conn.dev);
+ }
+
+ return 0;
+}
+
+static int mtk_dsi_host_detach(struct mipi_dsi_host *host,
+ struct mipi_dsi_device *device)
+{
+ struct mtk_dsi *dsi = host_to_mtk(host);
+
+ if (dsi->panel && &device->dev == dsi->panel->dev) {
+ if (dsi->conn.dev)
+ drm_helper_hpd_irq_event(dsi->conn.dev);
+
+ dsi->panel = NULL;
+ }
+
+ return 0;
+}
+
+static const struct mipi_dsi_host_ops mtk_dsi_host_ops = {
+ .attach = mtk_dsi_host_attach,
+ .detach = mtk_dsi_host_detach,
+};
+
+static int mtk_dsi_probe(struct platform_device *pdev)
+{
+ struct mtk_dsi *dsi = NULL;
+ struct device *dev = &pdev->dev;
+ struct device_node *panel_node, *bridge_node, *endpoint;
+ struct resource *regs;
+ int err;
+ int ret;
+
+ dsi = kzalloc(sizeof(struct mtk_dsi), GFP_KERNEL);
+
+ dsi->mode_flags = MIPI_DSI_MODE_VIDEO;
+ dsi->format = MIPI_DSI_FMT_RGB888;
+ dsi->lanes = 4;
+
+ dsi->vm.pixelclock = 76000;
+ dsi->vm.hactive = 1368;
+ dsi->vm.hback_porch = 100;
+ dsi->vm.hfront_porch = 106;
+ dsi->vm.hsync_len = 26;
+ dsi->vm.vactive = 768;
+ dsi->vm.vback_porch = 10;
+ dsi->vm.vfront_porch = 10;
+ dsi->vm.vsync_len = 12;
+
+ err = mtk_dsi_of_read_u32(dev->of_node, "mediatek,width",
+ &dsi->vm.hactive);
+ if (err < 0)
+ return err;
+
+ err = mtk_dsi_of_read_u32(dev->of_node, "mediatek,height",
+ &dsi->vm.vactive);
+ if (err < 0)
+ return err;
+
+ err = mtk_dsi_of_read_u32(dev->of_node, "mediatek,mipi-tx-burst-freq",
+ &dsi->pll_clk_rate);
+
+ if (err < 0)
+ return err;
+
+ endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
+ if (endpoint) {
+ bridge_node = of_graph_get_remote_port_parent(endpoint);
+ if (!bridge_node)
+ return -EPROBE_DEFER;
+
+ dsi->bridge = of_drm_find_bridge(bridge_node);
+ of_node_put(bridge_node);
+ if (!dsi->bridge)
+ return -EPROBE_DEFER;
+ }
+
+ dsi->dsi0_engine_clk_cg = devm_clk_get(dev, "dsi0_engine_disp_ck");
+ if (IS_ERR(dsi->dsi0_engine_clk_cg)) {
+ dev_err(dev, "cannot get dsi0_engine_clk_cg\n");
+ return PTR_ERR(dsi->dsi0_engine_clk_cg);
+ }
+
+ dsi->dsi0_digital_clk_cg = devm_clk_get(dev, "dsi0_digital_disp_ck");
+ if (IS_ERR(dsi->dsi0_digital_clk_cg)) {
+ dev_err(dev, "cannot get dsi0_digital_disp_ck\n");
+ return PTR_ERR(dsi->dsi0_digital_clk_cg);
+ }
+
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ dsi->dsi_reg_base = devm_ioremap_resource(dev, regs);
+
+ if (IS_ERR(dsi->dsi_reg_base)) {
+ dev_err(dev, "cannot get dsi->dsi_reg_base\n");
+ return PTR_ERR(dsi->dsi_reg_base);
+ }
+
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ dsi->dsi_tx_reg_base = devm_ioremap_resource(dev, regs);
+ if (IS_ERR(dsi->dsi_tx_reg_base)) {
+ dev_err(dev, "cannot get dsi->dsi_tx_reg_base\n");
+ return PTR_ERR(dsi->dsi_tx_reg_base);
+ }
+
+ dsi->disp_supplies = devm_regulator_get(&pdev->dev, "disp-bdg");
+ if (IS_ERR(dsi->disp_supplies)) {
+ dev_err(dev, "cannot get dsi->disp_supplies\n");
+ return PTR_ERR(dsi->disp_supplies);
+ }
+
+ ret = regulator_set_voltage(dsi->disp_supplies, 1800000, 1800000);
+ if (ret != 0) {
+ dev_err(dev, "lcm failed to set lcm_vgp voltage: %d\n", ret);
+ return PTR_ERR(dsi->disp_supplies);
+ }
+
+ ret = regulator_enable(dsi->disp_supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable lcm_vgp: %d\n", ret);
+ return PTR_ERR(dsi->disp_supplies);
+ }
+
+ panel_node = of_parse_phandle(dev->of_node, "mediatek,panel", 0);
+ if (panel_node) {
+ dsi->panel = of_drm_find_panel(panel_node);
+ of_node_put(panel_node);
+ if (!dsi->panel)
+ return -EPROBE_DEFER;
+ } else
+ return -EPROBE_DEFER;
+
+ platform_set_drvdata(pdev, dsi);
+
+ ret = component_add(&pdev->dev, &mtk_dsi_component_ops);
+ if (ret)
+ goto err_del_component;
+
+ return 0;
+
+err_del_component:
+ component_del(&pdev->dev, &mtk_dsi_component_ops);
+ return -EPROBE_DEFER;
+
+}
+
+static int mtk_dsi_remove(struct platform_device *pdev)
+{
+ struct mtk_dsi *dsi = platform_get_drvdata(pdev);
+
+ mtk_output_dsi_disable(dsi);
+ component_del(&pdev->dev, &mtk_dsi_component_ops);
+
+ return 0;
+}
+
+static const struct of_device_id mtk_dsi_of_match[] = {
+ { .compatible = "mediatek,mt8173-dsi" },
+ { },
+};
+
+struct platform_driver mtk_dsi_driver = {
+ .probe = mtk_dsi_probe,
+ .remove = mtk_dsi_remove,
+ .driver = {
+ .name = "mtk-dsi",
+ .of_match_table = mtk_dsi_of_match,
+ .owner = THIS_MODULE,
+ },
+};
+
+
diff --git a/drivers/gpu/drm/mediatek/mediatek_drm_dsi.h b/drivers/gpu/drm/mediatek/mediatek_drm_dsi.h
new file mode 100644
index 0000000..7b7b93b
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mediatek_drm_dsi.h
@@ -0,0 +1,71 @@
+/*
+ * 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 _MEDIATEK_DRM_DSI_H_
+#define _MEDIATEK_DRM_DSI_H_
+
+
+struct mtk_dsi {
+ struct drm_device *drm_dev;
+ struct drm_encoder encoder;
+ struct drm_connector conn;
+ struct drm_panel *panel;
+ struct drm_bridge *bridge;
+ struct mipi_dsi_host host;
+ struct regulator *disp_supplies;
+
+ void __iomem *dsi_reg_base;
+ void __iomem *dsi_tx_reg_base;
+
+ struct clk *dsi_disp_clk_cg;
+ struct clk *dsi_dsi_clk_cg;
+ struct clk *dsi_div2_clk_cg;
+
+ struct clk *dsi0_engine_clk_cg;
+ struct clk *dsi0_digital_clk_cg;
+
+ u32 pll_clk_rate;
+
+ unsigned long mode_flags;
+ enum mipi_dsi_pixel_format format;
+ unsigned int lanes;
+ struct videomode vm;
+ bool enabled;
+};
+
+
+static inline struct mtk_dsi *host_to_mtk(struct mipi_dsi_host *host)
+{
+ return container_of(host, struct mtk_dsi, host);
+}
+
+static inline struct mtk_dsi *encoder_to_dsi(struct drm_encoder *e)
+{
+ return container_of(e, struct mtk_dsi, encoder);
+}
+
+#define connector_to_dsi(c) container_of(c, struct mtk_dsi, conn)
+
+
+#define mtk_dsi_err(fmt, ...) \
+ pr_err("[mediatek drm dsi] ERROR!!! fun:%s, line:%d " \
+ fmt, __func__, __LINE__, ##__VA_ARGS__)
+#define mtk_dsi_info(fmt, ...) \
+ pr_info("[mediatek drm dsi] INFO fun:%s, line:%d " \
+ fmt, __func__, __LINE__, ##__VA_ARGS__)
+#define mtk_dsi_output(fmt, ...) \
+ pr_info(fmt, ##__VA_ARGS__)
+
+#endif
+
+
diff --git a/drivers/gpu/drm/mediatek/mediatek_drm_fb.c b/drivers/gpu/drm/mediatek/mediatek_drm_fb.c
new file mode 100644
index 0000000..fbaba95
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mediatek_drm_fb.c
@@ -0,0 +1,339 @@
+/*
+ * 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 <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_helper.h>
+
+#include "mediatek_drm_drv.h"
+#include "mediatek_drm_fb.h"
+#include "mediatek_drm_gem.h"
+
+
+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);
+ }
+}
+
+static struct drm_framebuffer_funcs mediatek_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 = devm_kzalloc(dev->dev, 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, &mediatek_drm_fb_funcs);
+ if (ret) {
+ DRM_ERROR("failed to initialize framebuffer\n");
+ return ERR_PTR(ret);
+ }
+
+ return mtk_fb;
+}
+
+static int mtk_drm_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
+{
+ struct drm_fb_helper *helper = info->par;
+ struct device *dev = ((struct drm_device *)helper->dev)->dev;
+ struct mtk_drm_fb *mtk_fb = to_mtk_fb(helper->fb);
+ struct mtk_drm_gem_buf *buffer =
+ to_mtk_gem_obj(mtk_fb->gem_obj[0])->buffer;
+ int ret;
+
+ vma->vm_flags |= VM_MIXEDMAP;
+
+ if (dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, &buffer->dma_attrs)) {
+ ret = dma_mmap_attrs(dev, vma, buffer->pages,
+ buffer->mva_addr, buffer->size, &buffer->dma_attrs);
+ } else {
+ ret = dma_mmap_attrs(dev, vma, buffer->kvaddr,
+ buffer->mva_addr, buffer->size, &buffer->dma_attrs);
+ }
+
+ if (ret) {
+ DRM_ERROR("failed to fb_mmap %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct fb_ops mediatek_fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_fillrect = sys_fillrect,
+ .fb_copyarea = sys_copyarea,
+ .fb_imageblit = sys_imageblit,
+ .fb_check_var = drm_fb_helper_check_var,
+ .fb_set_par = drm_fb_helper_set_par,
+ .fb_blank = drm_fb_helper_blank,
+ .fb_pan_display = drm_fb_helper_pan_display,
+ .fb_setcmap = drm_fb_helper_setcmap,
+ .fb_mmap = mtk_drm_fb_mmap,
+};
+
+static int mtk_fbdev_probe(struct drm_fb_helper *helper,
+ struct drm_fb_helper_surface_size *sizes)
+{
+ struct drm_device *dev = helper->dev;
+ struct drm_mode_fb_cmd2 mode = { 0 };
+ struct mtk_drm_fb *mtk_fb;
+ struct mtk_drm_gem_buf *buffer;
+ struct mtk_drm_gem_obj *mtk_gem;
+ struct drm_gem_object *gem;
+ struct fb_info *info;
+ struct drm_framebuffer *fb;
+ unsigned long offset;
+ size_t size;
+ int err;
+
+ mode.width = sizes->surface_width;
+ mode.height = sizes->surface_height;
+ mode.pitches[0] = sizes->surface_width * ((sizes->surface_bpp + 7) / 8);
+ mode.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
+ sizes->surface_depth);
+
+ mode.height = mode.height;/* << 1; for fb use? */
+ size = mode.pitches[0] * mode.height;
+ dev_info(dev->dev, "mtk_fbdev_probe %dx%d bpp %d pitch %d size %zu\n",
+ mode.width, mode.height, sizes->surface_bpp, mode.pitches[0],
+ size);
+
+ mtk_gem = mtk_drm_gem_create(dev, 3, size);
+ if (IS_ERR(mtk_gem)) {
+ err = PTR_ERR(mtk_gem);
+ goto fini;
+ }
+
+ gem = &mtk_gem->base;
+ buffer = mtk_gem->buffer;
+
+ mtk_fb = mtk_drm_framebuffer_init(dev, &mode, &gem);
+ if (IS_ERR(mtk_fb)) {
+ dev_err(dev->dev, "failed to allocate DRM framebuffer\n");
+ err = PTR_ERR(mtk_fb);
+ goto free;
+ }
+ fb = &mtk_fb->base;
+
+ info = framebuffer_alloc(0, dev->dev);
+ if (!info) {
+ dev_err(dev->dev, "failed to allocate framebuffer info\n");
+ err = PTR_ERR(info);
+ goto release;
+ }
+
+ helper->fb = fb;
+ helper->fbdev = info;
+
+ info->par = helper;
+ info->flags = FBINFO_FLAG_DEFAULT;
+ info->fbops = &mediatek_fb_ops;
+
+ err = fb_alloc_cmap(&info->cmap, 256, 0);
+ if (err < 0) {
+ dev_err(dev->dev, "failed to allocate color map: %d\n", err);
+ goto destroy;
+ }
+
+ drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
+ drm_fb_helper_fill_var(info, helper, fb->width, fb->height);
+
+ offset = info->var.xoffset * (fb->bits_per_pixel + 7) / 8;
+ offset += info->var.yoffset * fb->pitches[0];
+
+ strcpy(info->fix.id, "mtk");
+ /* dev->mode_config.fb_base = (resource_size_t)bo->paddr; */
+ info->var.yres = info->var.yres_virtual;/* >> 1; for fb use? */
+ info->fix.smem_start = buffer->mva_addr + offset;
+ info->fix.smem_len = size;
+ info->screen_base = buffer->kvaddr + offset;
+ info->screen_size = size;
+
+ return 0;
+
+destroy:
+ drm_framebuffer_unregister_private(fb);
+ mtk_drm_fb_destroy(fb);
+release:
+ framebuffer_release(info);
+free:
+ mtk_drm_gem_free_object(&mtk_gem->base);
+fini:
+ dev_err(dev->dev, "mtk_fbdev_probe fail\n");
+ return err;
+}
+
+static struct drm_fb_helper_funcs mediatek_drm_fb_helper_funcs = {
+ .fb_probe = mtk_fbdev_probe,
+};
+
+int mtk_fbdev_create(struct drm_device *dev)
+{
+ struct mtk_drm_private *priv =
+ (struct mtk_drm_private *)dev->dev_private;
+ struct drm_fb_helper *fbdev;
+ int ret;
+
+ fbdev = devm_kzalloc(dev->dev, sizeof(*fbdev), GFP_KERNEL);
+ if (!fbdev)
+ return -ENOMEM;
+
+ drm_fb_helper_prepare(dev, fbdev, &mediatek_drm_fb_helper_funcs);
+
+ ret = drm_fb_helper_init(dev, fbdev, dev->mode_config.num_crtc,
+ dev->mode_config.num_connector);
+ if (ret) {
+ dev_err(dev->dev, "failed to initialize DRM FB helper\n");
+ goto fini;
+ }
+
+ ret = drm_fb_helper_single_add_all_connectors(fbdev);
+ if (ret) {
+ dev_err(dev->dev, "failed to add connectors\n");
+ goto fini;
+ }
+
+ drm_helper_disable_unused_functions(dev);
+
+ ret = drm_fb_helper_initial_config(fbdev, FBDEV_BPP);
+ if (ret) {
+ dev_err(dev->dev, "failed to set initial configuration\n");
+ goto fini;
+ }
+ priv->fb_helper = fbdev;
+
+ return 0;
+
+fini:
+ drm_fb_helper_fini(fbdev);
+
+ return ret;
+}
+
+void mtk_fbdev_destroy(struct drm_device *dev)
+{
+ struct mtk_drm_private *priv =
+ (struct mtk_drm_private *)dev->dev_private;
+ struct drm_fb_helper *fbdev = priv->fb_helper;
+ struct fb_info *info = priv->fb_helper->fbdev;
+
+ if (info) {
+ int err;
+
+ err = unregister_framebuffer(info);
+ if (err < 0)
+ DRM_DEBUG_KMS("failed to unregister framebuffer\n");
+
+ if (info->cmap.len)
+ fb_dealloc_cmap(&info->cmap);
+
+ framebuffer_release(info);
+ }
+
+ if (fbdev->fb) {
+ drm_framebuffer_unregister_private(fbdev->fb);
+ mtk_drm_fb_destroy(fbdev->fb);
+ }
+
+ drm_fb_helper_fini(fbdev);
+ kfree(fbdev);
+}
+
+void mtk_drm_mode_output_poll_changed(struct drm_device *dev)
+{
+ struct mtk_drm_private *priv =
+ (struct mtk_drm_private *)dev->dev_private;
+
+ if (priv->fb_helper)
+ drm_fb_helper_hotplug_event(priv->fb_helper);
+}
+
+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) {
+ err = -EINVAL;
+ goto unreference;
+ }
+ }
+
+ mtk_fb = mtk_drm_framebuffer_init(dev, cmd, gem);
+
+ 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/mediatek_drm_fb.h b/drivers/gpu/drm/mediatek/mediatek_drm_fb.h
new file mode 100644
index 0000000..ee8b6a6
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mediatek_drm_fb.h
@@ -0,0 +1,43 @@
+/*
+ * 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 _MEDIATEK_DRM_FB_H_
+#define _MEDIATEK_DRM_FB_H_
+
+#define MAX_FB_OBJ 4
+#define FBDEV_BPP 16
+
+/*
+ * mtk specific framebuffer structure.
+ *
+ * @fb: drm framebuffer object.
+ * @mtk_gem_obj: array of mtk specific gem object containing a gem object.
+ */
+struct mtk_drm_fb {
+ struct drm_framebuffer base;
+ struct drm_gem_object *gem_obj[MAX_FB_OBJ]; /* FIXME? mtk_gem_obj? */
+};
+
+#define to_mtk_fb(x) container_of(x, struct mtk_drm_fb, base)
+
+void mtk_drm_mode_output_poll_changed(struct drm_device *dev);
+struct drm_framebuffer *mtk_drm_mode_fb_create(struct drm_device *dev,
+ struct drm_file *file,
+ struct drm_mode_fb_cmd2 *cmd);
+
+int mtk_fbdev_create(struct drm_device *dev);
+void mtk_fbdev_destroy(struct drm_device *dev);
+
+#endif
+
+
diff --git a/drivers/gpu/drm/mediatek/mediatek_drm_gem.c b/drivers/gpu/drm/mediatek/mediatek_drm_gem.c
new file mode 100644
index 0000000..3df3f4f
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mediatek_drm_gem.c
@@ -0,0 +1,315 @@
+/*
+ * 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 "mediatek_drm_gem.h"
+#include "drm/mediatek_drm.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;
+ struct drm_gem_object *obj;
+ int ret;
+
+ mtk_gem_obj = kzalloc(sizeof(*mtk_gem_obj), GFP_KERNEL);
+ if (!mtk_gem_obj)
+ return NULL;
+
+ mtk_gem_obj->size = size;
+ obj = &mtk_gem_obj->base;
+
+ ret = drm_gem_object_init(dev, obj, size);
+ if (ret < 0) {
+ DRM_ERROR("failed to initialize gem object\n");
+ kfree(mtk_gem_obj);
+ return NULL;
+ }
+
+ DRM_DEBUG_KMS("created file object = 0x%p\n", obj->filp);
+
+ return mtk_gem_obj;
+}
+
+struct mtk_drm_gem_obj *mtk_drm_gem_create(struct drm_device *dev,
+ unsigned int flags, unsigned long size)
+{
+ struct mtk_drm_gem_obj *mtk_gem;
+ struct mtk_drm_gem_buf *mtk_buf;
+ int ret;
+
+ if (!size) {
+ DRM_ERROR("invalid size.\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ mtk_gem = kzalloc(sizeof(*mtk_gem), GFP_KERNEL);
+ if (!mtk_gem) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ mtk_buf = kzalloc(sizeof(*mtk_buf), GFP_KERNEL);
+ if (!mtk_buf) {
+ ret = -ENOMEM;
+ goto err_buf;
+ }
+ mtk_gem->buffer = mtk_buf;
+
+ if (flags == 0) {
+ size = round_up(size, PAGE_SIZE);
+ mtk_buf->kvaddr = kzalloc(size, GFP_KERNEL);
+ if (!mtk_buf->kvaddr) {
+ ret = -ENOMEM;
+ goto err_size;
+ }
+
+ mtk_buf->paddr = virt_to_phys(mtk_buf->kvaddr);
+ mtk_buf->size = size;
+ mtk_buf->mva_addr = mtk_buf->paddr;
+ } else {
+ struct page **pages;
+ int npages, size_pages;
+ int offset, index;
+
+ size = PAGE_ALIGN(size);
+ npages = size >> PAGE_SHIFT;
+ size_pages = npages * sizeof(*pages);
+ pages = kmalloc(size_pages, GFP_KERNEL);
+ if (!pages) {
+ ret = -ENOMEM;
+ goto err_size;
+ }
+ mtk_buf->pages = pages;
+
+ init_dma_attrs(&mtk_buf->dma_attrs);
+
+ mtk_buf->kvaddr = dma_alloc_attrs(dev->dev, size,
+ (dma_addr_t *)&mtk_buf->mva_addr, GFP_KERNEL,
+ &mtk_buf->dma_attrs);
+ if (!mtk_buf->kvaddr) {
+ ret = -ENOMEM;
+ goto err_mem;
+ }
+
+ mtk_buf->paddr = 0;
+ mtk_buf->size = size;
+
+ for (offset = 0, index = 0;
+ offset < size; offset += PAGE_SIZE, index++)
+ mtk_buf->pages[index] =
+ vmalloc_to_page(mtk_buf->kvaddr + offset);
+
+ mtk_buf->sgt = drm_prime_pages_to_sg(mtk_buf->pages, npages);
+ }
+ mtk_gem->flags = flags;
+
+ DRM_INFO("kvaddr = %p mva_addr = %X\n",
+ mtk_buf->kvaddr, mtk_buf->mva_addr);
+ ret = drm_gem_object_init(dev, &mtk_gem->base, size);
+ if (ret)
+ goto err_mem;
+
+ return mtk_gem;
+
+err_mem:
+ if (mtk_buf->paddr)
+ kfree(mtk_buf->kvaddr);
+ else
+ dma_free_attrs(dev->dev, size, mtk_buf->kvaddr,
+ mtk_buf->mva_addr, &mtk_buf->dma_attrs);
+
+ kfree(mtk_buf->pages);
+
+err_size:
+ kfree(mtk_buf);
+err_buf:
+ kfree(mtk_gem);
+err:
+ return ERR_PTR(ret);
+}
+
+void mtk_drm_gem_free_object(struct drm_gem_object *gem)
+{
+ struct mtk_drm_gem_obj *mtk_gem = to_mtk_gem_obj(gem);
+
+ DRM_DEBUG_KMS("handle count = %d\n", gem->handle_count);
+
+ drm_gem_free_mmap_offset(gem);
+
+ /* release file pointer to gem object. */
+ drm_gem_object_release(gem);
+
+ if (mtk_gem->flags == 0)
+ kfree(mtk_gem->buffer->kvaddr);
+ else
+ dma_free_attrs(gem->dev->dev, mtk_gem->buffer->size,
+ mtk_gem->buffer->kvaddr, mtk_gem->buffer->mva_addr,
+ &mtk_gem->buffer->dma_attrs);
+
+ kfree(mtk_gem->buffer->pages);
+ kfree(mtk_gem->buffer);
+ 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;
+ unsigned int min_pitch = args->width * ((args->bpp + 7) / 8);
+ int ret;
+
+ args->pitch = min_pitch;
+ args->size = args->pitch * args->height;
+
+ mtk_gem = mtk_drm_gem_create(dev, 3, args->size);
+ 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)
+ return ret;
+
+ /* drop reference from allocate - handle holds it now. */
+ drm_gem_object_unreference_unlocked(&mtk_gem->base);
+
+ return 0;
+}
+
+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 = 0;
+
+ mutex_lock(&dev->struct_mutex);
+
+ /*
+ * get offset of memory allocated for drm framebuffer.
+ * - this callback would be called by user application
+ * with DRM_IOCTL_MODE_MAP_DUMB command.
+ */
+
+ 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%lx\n", (unsigned long)*offset);
+
+out:
+ drm_gem_object_unreference(obj);
+unlock:
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+}
+
+int mtk_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct mtk_drm_gem_obj *mtk_gem;
+ struct drm_gem_object *gem;
+ int ret;
+
+ /* set vm_area_struct. */
+ ret = drm_gem_mmap(filp, vma);
+ if (ret) {
+ DRM_ERROR("failed to mmap.\n");
+ return ret;
+ }
+
+ gem = vma->vm_private_data;
+ mtk_gem = to_mtk_gem_obj(gem);
+
+ if (mtk_gem->flags == 0) {
+ /*
+ * get page frame number to physical memory to be mapped
+ * to user space.
+ */
+ ret = remap_pfn_range(vma, vma->vm_start,
+ mtk_gem->buffer->paddr >> PAGE_SHIFT,
+ vma->vm_end - vma->vm_start, vma->vm_page_prot);
+ } else {
+ struct drm_file *file_priv = filp->private_data;
+ struct drm_device *dev = file_priv->minor->dev;
+ struct mtk_drm_gem_buf *buffer = mtk_gem->buffer;
+
+ vma->vm_flags |= VM_MIXEDMAP;
+
+ ret = dma_mmap_attrs(dev->dev, vma, buffer->kvaddr,
+ buffer->mva_addr, buffer->size, &buffer->dma_attrs);
+ if (ret) {
+ DRM_ERROR("failed to remap dma %d\n", ret);
+ return ret;
+ }
+ }
+
+ if (ret)
+ drm_gem_vm_close(vma);
+
+ return ret;
+}
+
+int mediatek_gem_map_offset_ioctl(struct drm_device *drm, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_mtk_gem_map_off *args = data;
+
+ return mtk_drm_gem_dumb_map_offset(file_priv, drm, args->handle,
+ &args->offset);
+}
+
+int mediatek_gem_create_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct mtk_drm_gem_obj *mtk_gem;
+ struct drm_mtk_gem_create *args = data;
+ int ret;
+
+ mtk_gem = mtk_drm_gem_create(dev, 3, args->size);
+
+ 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)
+ return ret;
+
+ /* drop reference from allocate - handle holds it now. */
+ drm_gem_object_unreference_unlocked(&mtk_gem->base);
+
+ return 0;
+}
+
+
diff --git a/drivers/gpu/drm/mediatek/mediatek_drm_gem.h b/drivers/gpu/drm/mediatek/mediatek_drm_gem.h
new file mode 100644
index 0000000..1529481
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mediatek_drm_gem.h
@@ -0,0 +1,94 @@
+/*
+ * 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 _MEDIATEK_DRM_GEM_H_
+#define _MEDIATEK_DRM_GEM_H_
+
+#include <drm/drm_gem.h>
+
+struct drm_gem_object;
+
+/*
+ * mtk drm gem buffer structure.
+ *
+ * @kvaddr: kernel virtual address to allocated memory region.
+ * @dma_addr: bus address(accessed by dma) to allocated memory region.
+ * - this address could be physical address without IOMMU and
+ * device address with IOMMU.
+ * @sgt: sg table to transfer page data.
+ * @pages: contain all pages to allocated memory region.
+ * @size: size of allocated memory region.
+ */
+struct mtk_drm_gem_buf {
+ void __iomem *kvaddr;
+ dma_addr_t dma_addr;
+ struct dma_attrs dma_attrs;
+ struct sg_table *sgt;
+ struct page **pages;
+ unsigned long size;
+ unsigned int mva_addr;
+ unsigned int paddr;
+};
+
+/*
+ * mtk drm buffer structure.
+ *
+ * @base: a gem object.
+ * - a new handle to this gem object would be created
+ * by drm_gem_handle_create().
+ * @buffer: a pointer to mtk_drm_gem_buffer object.
+ * - contain the information to memory region allocated
+ * by user request or at framebuffer creation.
+ * continuous memory region allocated by user request
+ * or at framebuffer creation.
+ * @size: total memory size to physically non-continuous memory region.
+ * @flags: indicate memory type to allocated buffer and cache attruibute.
+ *
+ * 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;
+ struct mtk_drm_gem_buf *buffer;
+ unsigned long size;
+ unsigned int flags;
+};
+
+#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 int flags, unsigned long size);
+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);
+
+/*
+ * request gem object creation and buffer allocation as the size
+ * that it is calculated with framebuffer information such as width,
+ * height and bpp.
+ */
+int mediatek_gem_create_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+
+/* get buffer offset to map to user space. */
+int mediatek_gem_map_offset_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+
+
+#endif
+
diff --git a/include/uapi/drm/mediatek_drm.h b/include/uapi/drm/mediatek_drm.h
new file mode 100644
index 0000000..19ea357
--- /dev/null
+++ b/include/uapi/drm/mediatek_drm.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#ifndef _UAPI_MEDIATEK_DRM_H
+#define _UAPI_MEDIATEK_DRM_H
+
+#include <drm/drm.h>
+
+/**
+ * User-desired buffer creation information structure.
+ *
+ * @size: user-desired memory allocation size.
+ * - this size value would be page-aligned internally.
+ * @flags: user request for setting memory type or cache attributes.
+ * @handle: returned a handle to created gem object.
+ * - this handle will be set by gem module of kernel side.
+ */
+struct drm_mtk_gem_create {
+ uint64_t size;
+ uint32_t flags;
+ uint32_t handle;
+};
+
+/**
+ * A structure for getting buffer offset.
+ *
+ * @handle: a pointer to gem object created.
+ * @pad: just padding to be 64-bit aligned.
+ * @offset: relatived offset value of the memory region allocated.
+ * - this value should be set by user.
+ */
+struct drm_mtk_gem_map_off {
+ uint32_t handle;
+ uint32_t pad;
+ uint64_t offset;
+};
+
+#define DRM_MTK_GEM_CREATE 0x00
+#define DRM_MTK_GEM_MAP_OFFSET 0x01
+
+#define DRM_IOCTL_MTK_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + \
+ DRM_MTK_GEM_CREATE, struct drm_mtk_gem_create)
+
+#define DRM_IOCTL_MTK_GEM_MAP_OFFSET DRM_IOWR(DRM_COMMAND_BASE + \
+ DRM_MTK_GEM_MAP_OFFSET, struct drm_mtk_gem_map_off)
+
+
+#endif /* _UAPI_MEDIATEK_DRM_H */
--
1.8.1.1.dirty

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/