MT8173 DRM include one master drm device and three sub device: dsi device,
crtc main device, and ddp device.
Master drm device control the drm sub device and memory management.
dsi device is a drm connector/encoder device which control MIPI/DSI hw block.
crtc main is a drm crtc device which control hw components in the display data
path.
ddp is a device which control display data path.
Display data path of crtc main is:
[OVL0] -> [COLOR0] -> [AAL] -> [OD] -> [UFOE] -> [RDMA0]
This patch depends on the other patches:
1. MT8173 IOMMU support
http://lists.infradead.org/pipermail/linux-mediatek/2015-March/000058.html
2. add IOMMU dma_ops
cherry picked from git://linux-arm.org/linux-rm iommu/dma
commit d76a1911b02185bdc5f8b5525f9228cf266725c5
CK Hu (2):
dt-bindings: drm/mediatek: Add Mediatek DRM dts binding
drm/mediatek: Add DRM Driver for Mediatek SoC MT8173.
.../bindings/drm/mediatek/mediatek,crtc-main.txt | 38 +
.../bindings/drm/mediatek/mediatek,ddp.txt | 22 +
.../bindings/drm/mediatek/mediatek,drm.txt | 27 +
.../bindings/drm/mediatek/mediatek,dsi.txt | 20 +
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 +
24 files changed, 4161 insertions(+)
create mode 100644 Documentation/devicetree/bindings/drm/mediatek/mediatek,crtc-main.txt
create mode 100644 Documentation/devicetree/bindings/drm/mediatek/mediatek,ddp.txt
create mode 100644 Documentation/devicetree/bindings/drm/mediatek/mediatek,drm.txt
create mode 100644 Documentation/devicetree/bindings/drm/mediatek/mediatek,dsi.txt
create mode 100644 drivers/gpu/drm/mediatek/Kconfig
create mode 100644 drivers/gpu/drm/mediatek/Makefile
create mode 100644 drivers/gpu/drm/mediatek/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
--
1.8.1.1.dirty
This patch includes
1. Mediatek DRM Device binding
2. Mediatek DSI Device binding
3. Mediatek CRTC Main Device binding
4. Mediatek DDP Device binding
Signed-off-by: CK Hu <[email protected]>
---
.../bindings/drm/mediatek/mediatek,crtc-main.txt | 38 ++++++++++++++++++++++
.../bindings/drm/mediatek/mediatek,ddp.txt | 22 +++++++++++++
.../bindings/drm/mediatek/mediatek,drm.txt | 27 +++++++++++++++
.../bindings/drm/mediatek/mediatek,dsi.txt | 20 ++++++++++++
4 files changed, 107 insertions(+)
create mode 100644 Documentation/devicetree/bindings/drm/mediatek/mediatek,crtc-main.txt
create mode 100644 Documentation/devicetree/bindings/drm/mediatek/mediatek,ddp.txt
create mode 100644 Documentation/devicetree/bindings/drm/mediatek/mediatek,drm.txt
create mode 100644 Documentation/devicetree/bindings/drm/mediatek/mediatek,dsi.txt
diff --git a/Documentation/devicetree/bindings/drm/mediatek/mediatek,crtc-main.txt b/Documentation/devicetree/bindings/drm/mediatek/mediatek,crtc-main.txt
new file mode 100644
index 0000000..5c6c420
--- /dev/null
+++ b/Documentation/devicetree/bindings/drm/mediatek/mediatek,crtc-main.txt
@@ -0,0 +1,38 @@
+Mediatek CRTC Main Device
+================================
+
+The Mediatek CRTC Main device is a crtc device of DRM system.
+
+Required properties:
+- compatible: "mediatek,<chip>-crtc-main"
+- interrupts: The interrupt signal from the CRTC Main block.
+- reg: Physical base address and length of the controller's registers
+- clocks: device clocks
+ See Documentation/devicetree/bindings/clock/clock-bindings.txt for details.
+- ddp: phandle of ddp device which control display data path.
+
+Example:
+
+crtc_main: crtc@1400c000 {
+ compatible = "mediatek,mt8173-crtc-main";
+ interrupts = <GIC_SPI 196 IRQ_TYPE_LEVEL_LOW>;
+ reg = <0 0x1400c000 0 0x1000>, /* OVL0 */
+ <0 0x1400e000 0 0x1000>, /* RDMA0 */
+ <0 0x14013000 0 0x1000>, /* COLOR0 */
+ <0 0x14015000 0 0x1000>, /* AAL */
+ <0 0x1401a000 0 0x1000>, /* UFOE */
+ <0 0x14023000 0 0x1000>; /* OD */
+ clocks = <&mmsys MM_DISP_OVL0>,
+ <&mmsys MM_DISP_RDMA0>,
+ <&mmsys MM_DISP_COLOR0>,
+ <&mmsys MM_DISP_AAL>,
+ <&mmsys MM_DISP_UFOE>,
+ <&mmsys MM_DISP_OD>;
+ clock-names = "ovl0_disp",
+ "rdma0_disp",
+ "color0_disp",
+ "aal_disp",
+ "ufoe_disp",
+ "od_disp";
+ ddp = <&ddp>;
+};
\ No newline at end of file
diff --git a/Documentation/devicetree/bindings/drm/mediatek/mediatek,ddp.txt b/Documentation/devicetree/bindings/drm/mediatek/mediatek,ddp.txt
new file mode 100644
index 0000000..77cf630
--- /dev/null
+++ b/Documentation/devicetree/bindings/drm/mediatek/mediatek,ddp.txt
@@ -0,0 +1,22 @@
+Mediatek DDP Device
+================================
+
+The Mediatek DDP device control the display data path.
+
+Required properties:
+- compatible: "mediatek,<chip>-ddp"
+- reg: Physical base address and length of the controller's registers
+- power-domains: a phandle to DDP power domain node.
+- clocks: device clocks
+ See Documentation/devicetree/bindings/clock/clock-bindings.txt for details.
+
+Example:
+
+ddp: ddp@14000000 {
+ compatible = "mediatek,mt8173-ddp";
+ reg = <0 0x14000000 0 0x100>, /* CONFIG */
+ <0 0x14020000 0 0x1000>; /* MUTEX */
+ power-domains = <&scpsys MT8173_POWER_DOMAIN_DIS>;
+ clocks = <&mmsys MM_MUTEX_32K>;
+ clock-names = "mutex_disp";
+};
\ No newline at end of file
diff --git a/Documentation/devicetree/bindings/drm/mediatek/mediatek,drm.txt b/Documentation/devicetree/bindings/drm/mediatek/mediatek,drm.txt
new file mode 100644
index 0000000..c4a5702
--- /dev/null
+++ b/Documentation/devicetree/bindings/drm/mediatek/mediatek,drm.txt
@@ -0,0 +1,27 @@
+Mediatek DRM Device
+================================
+
+The Mediatek DRM device is a device needed to list all
+display component nodes that comprise the display subsystem.
+And it list the memory-related interface.
+
+Required properties:
+- compatible: "mediatek,<chip>-drm"
+- larb: Should contain a list of phandles pointing to larb device.
+ larb definitions as defined in
+ Documentation/devicetree/bindings/soc/mediatek/mediatek,smi-larb.txt
+- iommus: required a iommu node
+- connectors: Should contain a list of phandles pointing to connector device.
+ connector device should be one component of this master.
+- crtcs: Should contain a list of phandles pointing to crtc device.
+ crtc device should be one component of this master.
+
+Example:
+
+drm0: drm {
+ compatible = "mediatek,mt8173-drm";
+ larb = <&larb0>;
+ iommus = <&iommu M4U_PORT_DISP_OVL0>;
+ connectors = <&dsi>;
+ crtcs = <&crtc_main>;
+};
\ No newline at end of file
diff --git a/Documentation/devicetree/bindings/drm/mediatek/mediatek,dsi.txt b/Documentation/devicetree/bindings/drm/mediatek/mediatek,dsi.txt
new file mode 100644
index 0000000..16e3eb3
--- /dev/null
+++ b/Documentation/devicetree/bindings/drm/mediatek/mediatek,dsi.txt
@@ -0,0 +1,20 @@
+Mediatek DSI Device
+================================
+
+The Mediatek DSI device is a connector device of DRM system.
+
+Required properties:
+- compatible: "mediatek,<chip>-dsi"
+- reg: Physical base address and length of the controller's registers
+- clocks: device clocks
+ See Documentation/devicetree/bindings/clock/clock-bindings.txt for details.
+
+Example:
+
+dsi: dsi@10215000 {
+ compatible = "mediatek,mt8173-dsi";
+ reg = <0 0x1401B000 0 0x1000>, /* DSI0 */
+ <0 0x10215000 0 0x1000>; /* MIPITX */
+ clocks = <&mmsys MM_DSI0_ENGINE>, <&mmsys MM_DSI0_DIGITAL>;
+ clock-names = "dsi0_engine_disp_ck", "dsi0_digital_disp_ck";
+};
\ No newline at end of file
--
1.8.1.1.dirty
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 <[email protected]>
---
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 <[email protected]>
+ * CK Hu <[email protected]>
+ *
+ * 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 <[email protected]>
+ * CK Hu <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * 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 <[email protected]>");
+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
Hi,
On 13 May 2015 at 16:23, CK Hu <[email protected]> wrote:
> + /*
> + * 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));
Please do not do this. adjusted_mode is already available in crtc->hwmode.
Please also restructure this driver to use the atomic infrastructure.
A good example of how to do this can be found in the Rockchip RK3288
driver which was submitted a while ago: it started off as a legacy
driver like this, but was converted during the submission phase.
I'm sure there are many more comments to come, but certainly
converting to be an atomic driver is the very first step.
Cheers,
Daniel
A naive question and a nit follow. That's probably not what you'd like
to see for an RFC, but the patch got tangled up in my mail filter
anyhow.
On Wed, 2015-05-13 at 23:23 +0800, CK Hu wrote:
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/Kconfig
> +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.
Naive question, as I know next to nothing about drivers/gpu/drm.
DRM_MEDIATEK_FBDEV isn't used anywhere, as far as I can see. So all that
setting this Kconfig symbol does is selecting four other Kconfig
symbols. Is selecting those four symbols enough to get fbdev support?
Taking the Cargo Cult approach of looking at similar symbols
(DRM_I915_FBDEV, DRM_MSM_FBDEV, DRM_STI_FBDEV, and DRM_TEGRA_FBDEV) I
noticed that they all do a bit more than just selecting other symbols.
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mediatek_drm_drv.c
> + * 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 states the license of this driver is GPL v2. (Identical comments
seem to be part of all files added in this patch.)
> +MODULE_LICENSE("GPL");
And, according to include/linux/module.h, this states the license is GPL
v2 or later. So I think that either the comment at the top of these
files or the ident used in the MODULE_LICENSE() macro needs to be
changed.
Thanks,
Paul Bolle
Hi,
overall it looks to me like this binding is modeled after the Linux DRM
abstractions. Instead, the device nodes should be modeled after the
hardware.
Describing each function block as a separate device node is probably not
the best choice, as would be combining all devices in the mmsys range
into a single device node. So a somewhat arbitrary decision has to be
made what to group together. See my comments below:
Am Mittwoch, den 13.05.2015, 23:23 +0800 schrieb CK Hu:
> This patch includes
> 1. Mediatek DRM Device binding
> 2. Mediatek DSI Device binding
> 3. Mediatek CRTC Main Device binding
> 4. Mediatek DDP Device binding
>
> Signed-off-by: CK Hu <[email protected]>
> ---
> .../bindings/drm/mediatek/mediatek,crtc-main.txt | 38 ++++++++++++++++++++++
> .../bindings/drm/mediatek/mediatek,ddp.txt | 22 +++++++++++++
> .../bindings/drm/mediatek/mediatek,drm.txt | 27 +++++++++++++++
> .../bindings/drm/mediatek/mediatek,dsi.txt | 20 ++++++++++++
> 4 files changed, 107 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/drm/mediatek/mediatek,crtc-main.txt
> create mode 100644 Documentation/devicetree/bindings/drm/mediatek/mediatek,ddp.txt
> create mode 100644 Documentation/devicetree/bindings/drm/mediatek/mediatek,drm.txt
> create mode 100644 Documentation/devicetree/bindings/drm/mediatek/mediatek,dsi.txt
>
> diff --git a/Documentation/devicetree/bindings/drm/mediatek/mediatek,crtc-main.txt b/Documentation/devicetree/bindings/drm/mediatek/mediatek,crtc-main.txt
> new file mode 100644
> index 0000000..5c6c420
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/drm/mediatek/mediatek,crtc-main.txt
> @@ -0,0 +1,38 @@
> +Mediatek CRTC Main Device
> +================================
> +
> +The Mediatek CRTC Main device is a crtc device of DRM system.
"crtc" does not belong in the device tree. There is no crtc hardware.
What this node describes is a useful, but fixed configuration of a part
of the DISP subsystem function blocks.
In my opinion, it would be better to not describe the separate display
pipes in the device tree at all if they are dynamically configurable.
What for example if you model two separate fixed pipelines in the device
tree and then in the future you want to support the MERGE or SPLIT
blocks (I'm not sure what MERGE does, SPLIT seems to be needed for
8-lane DSI)?
As far as I currently understand, there are five source function blocks
that can read from memory (OVL0, OVL1, RDMA0, RDMA1, RDMA2) and three
sink function blocks (DSI0, DSI1, DPI0) that can be connected to panels,
or encoder bridges. How these map to the crtcs doesn't necessarily have
to be described in the device tree.
How about a single node that contains all of the DISP functional blocks
that don't need their own node (like DSI, which has to be connectable to
bridges or panels):
disp: disp@0x1400c000 {
compatible = "mediatek,mt8173-disp";
interrupts = <GIC_SPI 180 IRQ_TYPE_LEVEL_LOW>, /* OVL0 */
<GIC_SPI 181 IRQ_TYPE_LEVEL_LOW>; /* OVL1 */
interrupt-names = "ovl0", "ovl1";
reg = <0 0x1400c000 0 0x1000>, /* OVL0 */
<0 0x1400d000 0 0x1000>, /* OVL1 */
<0 0x1400e000 0 0x1000>, /* RDMA0 */
<0 0x1400f000 0 0x1000>, /* RDMA1 */
<0 0x14010000 0 0x1000>, /* RDMA2 */
<0 0x14013000 0 0x1000>, /* COLOR0 */
<0 0x14014000 0 0x1000>, /* COLOR1 */
<0 0x14015000 0 0x1000>, /* AAL */
<0 0x14016000 0 0x1000>, /* GAMMA */
<0 0x14017000 0 0x1000>, /* MERGE */
<0 0x14018000 0 0x1000>, /* SPLIT0 */
<0 0x14019000 0 0x1000>, /* SPLIT1 */
<0 0x1401a000 0 0x1000>, /* UFOE */
<0 0x14020000 0 0x1000>; /* MUTEX */
<0 0x14023000 0 0x1000>; /* OD */
reg-names = "ovl0", "ovl1", "rdma0", "rdma1", "rdma2",
"color0", "color1", "aal", "gamma", "merge",
"split0", "split1", "ufoe", "mutex", "od";
clocks = <&mmsys CLK_MM_DISP_OVL0>,
<&mmsys CLK_MM_DISP_OVL1>,
<&mmsys CLK_MM_DISP_RDMA0>,
<&mmsys CLK_MM_DISP_RDMA1>,
<&mmsys CLK_MM_DISP_RDMA2>,
<&mmsys CLK_MM_DISP_COLOR0>,
<&mmsys CLK_MM_DISP_COLOR1>,
<&mmsys CLK_MM_DISP_AAL>,
<&mmsys CLK_MM_DISP_GAMMA>,
<&mmsys CLK_MM_DISP_MERGE>,
<&mmsys CLK_MM_DISP_SPLIT0>,
<&mmsys CLK_MM_DISP_SPLIT1>,
<&mmsys CLK_MM_DISP_UFOE>,
<&mmsys CLK_MM_MUTEX_32K>,
<&mmsys CLK_MM_DISP_OD>;
clock-names = "ovl0", "ovl1", "rdma0", "rdma1", "rdma2",
"color0", "color1", "aal", "gamma", "merge",
"split0", "split1", "ufoe", "mutex", "od";
power-domains = <&scpsys MT8173_POWER_DOMAIN_DIS>;
config = <&mmsys>; /* syscon */
};
How the muxes in the config area are set up to connect those blocks
could be determined by the driver.
> +Required properties:
> +- compatible: "mediatek,<chip>-crtc-main"
> +- interrupts: The interrupt signal from the CRTC Main block.
> +- reg: Physical base address and length of the controller's registers
> +- clocks: device clocks
> + See Documentation/devicetree/bindings/clock/clock-bindings.txt for details.
> +- ddp: phandle of ddp device which control display data path.
> +
> +Example:
> +
> +crtc_main: crtc@1400c000 {
> + compatible = "mediatek,mt8173-crtc-main";
> + interrupts = <GIC_SPI 196 IRQ_TYPE_LEVEL_LOW>;
Should it be mentioned that this is the interrupt of the source of the
pipeline (OVL0/1)?
> + reg = <0 0x1400c000 0 0x1000>, /* OVL0 */
> + <0 0x1400e000 0 0x1000>, /* RDMA0 */
> + <0 0x14013000 0 0x1000>, /* COLOR0 */
> + <0 0x14015000 0 0x1000>, /* AAL */
> + <0 0x1401a000 0 0x1000>, /* UFOE */
> + <0 0x14023000 0 0x1000>; /* OD */
With the amount of register ranges and given the symmetry with the
clocks, better use reg-names instead of position to determine the
register space for each function block.
reg-names = "ovl0", "rdma0", "color0", "aal", "ufoe", "od";
> + clocks = <&mmsys MM_DISP_OVL0>,
> + <&mmsys MM_DISP_RDMA0>,
> + <&mmsys MM_DISP_COLOR0>,
> + <&mmsys MM_DISP_AAL>,
> + <&mmsys MM_DISP_UFOE>,
> + <&mmsys MM_DISP_OD>;
> + clock-names = "ovl0_disp",
> + "rdma0_disp",
> + "color0_disp",
> + "aal_disp",
> + "ufoe_disp",
> + "od_disp";
I'd drop the disp suffix from the clock input names.
clock-names = "ovl0", "rdma0", "color0", "aal", "ufoe", "od";
> + ddp = <&ddp>;
> +};
> \ No newline at end of file
> diff --git a/Documentation/devicetree/bindings/drm/mediatek/mediatek,ddp.txt b/Documentation/devicetree/bindings/drm/mediatek/mediatek,ddp.txt
> new file mode 100644
> index 0000000..77cf630
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/drm/mediatek/mediatek,ddp.txt
> @@ -0,0 +1,22 @@
> +Mediatek DDP Device
> +================================
> +
> +The Mediatek DDP device control the display data path.
This is not a real device either. There is nothing wrong with having a
ddp driver, but I don't think it is right to describe this driver in the
device tree. What we really have here is the mutex function block and
some registers in the mmsys config region, which already can be accessed
via the mmsys syscon.
With a single disp device node including the mutex and power domain
phandle this node would not be necessary.
> +Required properties:
> +- compatible: "mediatek,<chip>-ddp"
> +- reg: Physical base address and length of the controller's registers
> +- power-domains: a phandle to DDP power domain node.
> +- clocks: device clocks
> + See Documentation/devicetree/bindings/clock/clock-bindings.txt for details.
> +
> +Example:
> +
> +ddp: ddp@14000000 {
> + compatible = "mediatek,mt8173-ddp";
> + reg = <0 0x14000000 0 0x100>, /* CONFIG */
This remaps part of the mmsys register space, which is already a syscon.
Why not use syscon to access it?
> + <0 0x14020000 0 0x1000>; /* MUTEX */
> + power-domains = <&scpsys MT8173_POWER_DOMAIN_DIS>;
> + clocks = <&mmsys MM_MUTEX_32K>;
> + clock-names = "mutex_disp";
> +};
> \ No newline at end of file
> diff --git a/Documentation/devicetree/bindings/drm/mediatek/mediatek,drm.txt b/Documentation/devicetree/bindings/drm/mediatek/mediatek,drm.txt
> new file mode 100644
> index 0000000..c4a5702
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/drm/mediatek/mediatek,drm.txt
> @@ -0,0 +1,27 @@
> +Mediatek DRM Device
> +================================
> +
> +The Mediatek DRM device is a device needed to list all
> +display component nodes that comprise the display subsystem.
> +And it list the memory-related interface.
"drm" does not belong in the device tree. There is no "drm" hardware.
Other SoCs use the "display-subsystem" name for this node, it would be
better to follow that.
> +Required properties:
> +- compatible: "mediatek,<chip>-drm"
> +- larb: Should contain a list of phandles pointing to larb device.
> + larb definitions as defined in
> + Documentation/devicetree/bindings/soc/mediatek/mediatek,smi-larb.txt
> +- iommus: required a iommu node
> +- connectors: Should contain a list of phandles pointing to connector device.
> + connector device should be one component of this master.
> +- crtcs: Should contain a list of phandles pointing to crtc device.
> + crtc device should be one component of this master.
"connectors" and "crtcs" as used here are DRM concepts, not hardware
devices that should exist in the device tree. Ideally you would use
something recognizable from the datasheet, like "sinks" or from the
device tree, like "ports" if of-graph bindings are used, or just
"components" if this also were to include things like the ddp.
Another possibility would be to even merge the
> +
> +Example:
> +
> +drm0: drm {
> + compatible = "mediatek,mt8173-drm";
> + larb = <&larb0>;
> + iommus = <&iommu M4U_PORT_DISP_OVL0>;
> + connectors = <&dsi>;
> + crtcs = <&crtc_main>;
> +};
> \ No newline at end of file
> diff --git a/Documentation/devicetree/bindings/drm/mediatek/mediatek,dsi.txt b/Documentation/devicetree/bindings/drm/mediatek/mediatek,dsi.txt
> new file mode 100644
> index 0000000..16e3eb3
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/drm/mediatek/mediatek,dsi.txt
> @@ -0,0 +1,20 @@
> +Mediatek DSI Device
> +================================
> +
> +The Mediatek DSI device is a connector device of DRM system.
> +
> +Required properties:
> +- compatible: "mediatek,<chip>-dsi"
> +- reg: Physical base address and length of the controller's registers
> +- clocks: device clocks
> + See Documentation/devicetree/bindings/clock/clock-bindings.txt for details.
Will this use the
Documentation/devicetree/bindings/mipi/dsi/mipi-dsi-bus.txt
bindings to connect panels controlled via the DSI command channel
or the
Documentation/devicetree/bindings/graph.txt
bindings to connect panels controlled via I2C or other control buses?
If so, this should be documented here.
> +Example:
> +
> +dsi: dsi@10215000 {
> + compatible = "mediatek,mt8173-dsi";
> + reg = <0 0x1401B000 0 0x1000>, /* DSI0 */
> + <0 0x10215000 0 0x1000>; /* MIPITX */
> + clocks = <&mmsys MM_DSI0_ENGINE>, <&mmsys MM_DSI0_DIGITAL>;
> + clock-names = "dsi0_engine_disp_ck", "dsi0_digital_disp_ck";
Maybe shorten the clock names to "engine" and "digital" ?
And add an example of panel or bridge connected to the DSI sink.
What about DSI1 and the DPI sink?
best regards
Philipp