All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/9] Add drm driver for Rockchip Socs
@ 2014-08-04  4:41 mark yao
  2014-08-04  4:45 ` [PATCH 1/9] drm: " mark yao
                   ` (9 more replies)
  0 siblings, 10 replies; 38+ messages in thread
From: mark yao @ 2014-08-04  4:41 UTC (permalink / raw)
  To: heiko, Rob Clark, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Randy Dunlap, David Airlie,
	Grant Likely, Greg Kroah-Hartman, John Stultz, Rom Lemarchand
  Cc: devicetree, linux-doc, linux-kernel, dri-devel, linux-api, olof,
	djkurtz, xjq, kfx, cym, cf, zyw, zwl, xxm, huangtao, kever.yang,
	zhangqing, yxj, wxt, xw, mark yao

From: mark yao <yzq@rock-chips.com>

This a series of patches is a DRM Driver for Rockchip Socs, driver provides
an abstraction for the graphics hardware, as lcd controller and
connector interface.

add rk3288 lcd controller.
add rk3288 lvds and rk3288 edp connector.

Tested on rk3288 sdk board, use lvds or edp connector, boot and display OK

mark yao (9):
  drm: Add drm driver for Rockchip Socs
  Add devicetree bindings for panels used by the Rockchip DRM
  drm: add driver for panels used by the Rockchip DRM
  Add devicetree bindings for Rockchip lcd controller
  drm: add Rockchip rk3288 lcd controller driver
  Add devicetree bindings for Rockchip Soc LVDS
  drm: add Rockchip Soc rk3288 lvds connector
  Add devicetree bindings for Rockchip Soc EDP
  drm: add Rockchip Soc rk3288 edp connector


-- 
1.7.9.5



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

* [PATCH 1/9] drm: Add drm driver for Rockchip Socs
  2014-08-04  4:41 [PATCH 0/9] Add drm driver for Rockchip Socs mark yao
@ 2014-08-04  4:45 ` mark yao
  2014-08-04 14:46     ` Daniel Vetter
  2014-08-07  8:32   ` mark yao
  2014-08-04  4:47   ` mark yao
                   ` (8 subsequent siblings)
  9 siblings, 2 replies; 38+ messages in thread
From: mark yao @ 2014-08-04  4:45 UTC (permalink / raw)
  To: heiko, Rob Clark, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Randy Dunlap, David Airlie,
	Grant Likely, Greg Kroah-Hartman, John Stultz, Rom Lemarchand
  Cc: devicetree, linux-doc, linux-kernel, dri-devel, linux-api, olof,
	djkurtz, xjq, kfx, cym, cf, zyw, zwl, xxm, huangtao, kever.yang,
	zhangqing, yxj, wxt, xw, mark yao

This patch is a DRM Driver for Rockchip Socs, driver provides an abstraction
for the graphics hardware, such as lcd controller and connector interface.

Signed-off-by: mark yao <yzq@rock-chips.com>
---
 drivers/gpu/drm/Kconfig                           |    2 +
 drivers/gpu/drm/Makefile                          |    1 +
 drivers/gpu/drm/rockchip/Kconfig                  |   40 ++
 drivers/gpu/drm/rockchip/Makefile                 |   12 +
 drivers/gpu/drm/rockchip/rockchip_drm_connector.c |  412 ++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_connector.h |   36 ++
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c       |  600 +++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h       |  128 ++++
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c        |   48 ++
 drivers/gpu/drm/rockchip/rockchip_drm_fb.h        |   28 +
 drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c     |   63 ++
 drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h     |   24 +
 drivers/gpu/drm/rockchip/rockchip_drm_gem.c       |  163 +++++
 drivers/gpu/drm/rockchip/rockchip_drm_gem.h       |   40 ++
 drivers/gpu/drm/rockchip/rockchip_drm_lcdc.c      |  720 +++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_lcdc.h      |  131 ++++
 include/uapi/drm/rockchip_drm.h                   |  110 ++++
 17 files changed, 2558 insertions(+)
 create mode 100644 drivers/gpu/drm/rockchip/Kconfig
 create mode 100644 drivers/gpu/drm/rockchip/Makefile
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_connector.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_connector.h
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_drv.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_drv.h
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fb.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fb.h
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_gem.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_gem.h
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_lcdc.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_lcdc.h
 create mode 100644 include/uapi/drm/rockchip_drm.h

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index f512004..5951c2c 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -170,6 +170,8 @@ config DRM_SAVAGE
 
 source "drivers/gpu/drm/exynos/Kconfig"
 
+source "drivers/gpu/drm/rockchip/Kconfig"
+
 source "drivers/gpu/drm/vmwgfx/Kconfig"
 
 source "drivers/gpu/drm/gma500/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index dd2ba42..40babd2 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -51,6 +51,7 @@ obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/
 obj-$(CONFIG_DRM_VIA)	+=via/
 obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
 obj-$(CONFIG_DRM_EXYNOS) +=exynos/
+obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/
 obj-$(CONFIG_DRM_GMA500) += gma500/
 obj-$(CONFIG_DRM_UDL) += udl/
 obj-$(CONFIG_DRM_AST) += ast/
diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
new file mode 100644
index 0000000..592e999
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -0,0 +1,40 @@
+config DRM_ROCKCHIP
+	tristate "DRM Support for Rockchip"
+	depends on DRM
+	select DRM_KMS_HELPER
+	select DRM_KMS_FB_HELPER
+	select DRM_KMS_CMA_HELPER
+	select DRM_GEM_CMA_HELPER
+	select DRM_PANEL
+	select FB_CFB_FILLRECT
+	select FB_CFB_COPYAREA
+	select FB_CFB_IMAGEBLIT
+	select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE
+	select VIDEOMODE_HELPERS
+	select OF
+	help
+	  Choose this option if you have a Rockchip soc chipset.
+	  This driver provides kernel mode setting and buffer
+	  management to userspace. This driver does not provides
+	  2D or 3D acceleration; acceleration is performed by other
+	  IP found on the SoC.
+
+config DRM_ROCKCHIP_LCDC
+	bool "ROCKCHIP DRM LCDC"
+	depends on DRM_ROCKCHIP
+	select FB_MODE_HELPERS
+	help
+	  Choose this option if you want to use Rockchip lcdc for DRM.
+	  The driver provides an abstraction for Rockchip lcd controller,
+	  lcd controller is the display interface from memory frame buffer
+	  to display device.
+
+config DRM_ROCKCHIP_CONNECTOR
+	bool "ROCKCHIP DRM CONNECTOR"
+	depends on OF && DRM_ROCKCHIP && DRM_ROCKCHIP_LCDC
+	select FB_MODE_HELPERS
+	select VIDEOMODE_HELPERS
+	help
+	  Choose this option if you want to use Rockchip Primary DISPLAY.
+	  The driver provides an abstraction for Rockchip display devices,
+	  such as lcd plane, lvds, edp , mipi, etc.
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
new file mode 100644
index 0000000..45c9d50
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for the drm device driver.  This driver provides support for the
+# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
+
+ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/rockchip
+
+rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_gem.o \
+		rockchip_drm_fb.o rockchip_drm_fbdev.o
+
+obj-$(CONFIG_DRM_ROCKCHIP_CONNECTOR) += rockchip_drm_connector.o
+obj-$(CONFIG_DRM_ROCKCHIP_LCDC) += rockchip_drm_lcdc.o
+obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_connector.c b/drivers/gpu/drm/rockchip/rockchip_drm_connector.c
new file mode 100644
index 0000000..337b618
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_connector.c
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:mark yao <mark.yao@rock-chips.com>
+ *
+ * based on exynos_drm_dpi.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_panel.h>
+
+#include <linux/component.h>
+
+#include <video/of_videomode.h>
+#include <video/videomode.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_connector.h"
+#include "rockchip_drm_lcdc.h"
+
+struct rockchip_conn_context {
+	struct device *dev;
+
+	struct drm_panel *panel;
+	struct rockchip_connector *conn;
+	struct drm_connector connector;
+	struct drm_encoder encoder;
+
+	struct drm_display_mode mode;
+
+	u32 flags;
+	int type;
+
+	int dpms_mode;
+};
+
+#define connector_to_ctx(c) \
+		container_of(c, struct rockchip_conn_context, connector)
+
+#define encoder_to_ctx(c) \
+		container_of(c, struct rockchip_conn_context, encoder)
+
+static inline int rockchip_convert_conn_type(int type)
+{
+	switch (type) {
+	case ROCKCHIP_DISPLAY_TYPE_RGB:
+	case ROCKCHIP_DISPLAY_TYPE_LVDS:
+		return DRM_MODE_CONNECTOR_LVDS;
+	case ROCKCHIP_DISPLAY_TYPE_EDP:
+		return DRM_MODE_CONNECTOR_eDP;
+	}
+
+	return  DRM_MODE_CONNECTOR_Unknown;
+}
+
+static inline int rockchip_convert_encoder_type(int type)
+{
+	switch (type) {
+	case ROCKCHIP_DISPLAY_TYPE_RGB:
+	case ROCKCHIP_DISPLAY_TYPE_LVDS:
+	case ROCKCHIP_DISPLAY_TYPE_EDP:
+		return DRM_MODE_ENCODER_LVDS;
+	}
+
+	return DRM_MODE_ENCODER_NONE;
+}
+
+static enum drm_connector_status
+rockchip_conn_detect(struct drm_connector *connector, bool force)
+{
+	return true;
+}
+
+static void rockchip_connector_destroy(struct drm_connector *connector)
+{
+	drm_sysfs_connector_remove(connector);
+	drm_connector_cleanup(connector);
+}
+
+static struct drm_connector_funcs rockchip_connector_funcs = {
+	.dpms = drm_helper_connector_dpms,
+	.detect = rockchip_conn_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = rockchip_connector_destroy,
+};
+
+static int rockchip_conn_get_modes(struct drm_connector *connector)
+{
+	struct rockchip_conn_context *ctx = connector_to_ctx(connector);
+	struct drm_panel *panel = ctx->panel;
+
+	return panel->funcs->get_modes(panel);
+}
+
+static struct drm_encoder *
+	rockchip_conn_best_encoder(struct drm_connector *connector)
+{
+	struct rockchip_conn_context *ctx = connector_to_ctx(connector);
+
+	return &ctx->encoder;
+}
+
+static struct drm_connector_helper_funcs rockchip_connector_helper_funcs = {
+	.get_modes = rockchip_conn_get_modes,
+	.best_encoder = rockchip_conn_best_encoder,
+};
+
+static void rockchip_drm_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+	struct rockchip_conn_context *ctx = encoder_to_ctx(encoder);
+	struct rockchip_connector *conn = ctx->conn;
+
+	switch (mode) {
+	case DRM_MODE_DPMS_ON:
+		if (ctx->dpms_mode != DRM_MODE_DPMS_ON) {
+			conn->enable(conn);
+			ctx->panel->funcs->enable(ctx->panel);
+		}
+		break;
+	case DRM_MODE_DPMS_STANDBY:
+	case DRM_MODE_DPMS_SUSPEND:
+	case DRM_MODE_DPMS_OFF:
+		if (ctx->dpms_mode == DRM_MODE_DPMS_ON) {
+			ctx->panel->funcs->disable(ctx->panel);
+			conn->disable(conn);
+		}
+		break;
+	default:
+		break;
+	}
+
+	ctx->dpms_mode = mode;
+}
+
+static bool
+rockchip_drm_encoder_mode_fixup(struct drm_encoder *encoder,
+				const struct drm_display_mode *mode,
+				struct drm_display_mode *adjusted_mode)
+{
+	struct rockchip_panel_special *priv_mode =
+					(void *)adjusted_mode->private;
+	struct rockchip_conn_context *ctx = encoder_to_ctx(encoder);
+
+	priv_mode->out_type = ctx->conn->type;
+
+	return true;
+}
+
+static void rockchip_drm_encoder_mode_set(struct drm_encoder *encoder,
+					  struct drm_display_mode *mode,
+					  struct drm_display_mode *adjusted)
+{
+	/* just set dummy now */
+}
+
+static void rockchip_drm_encoder_prepare(struct drm_encoder *encoder)
+{
+	/* drm framework doesn't check NULL. */
+}
+
+static void rockchip_drm_encoder_commit(struct drm_encoder *encoder)
+{
+	rockchip_drm_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
+}
+
+static void rockchip_drm_encoder_disable(struct drm_encoder *encoder)
+{
+	struct drm_plane *plane;
+	struct drm_device *dev = encoder->dev;
+
+	rockchip_drm_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+
+	/* all planes connected to this encoder should be also disabled. */
+	list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
+		if (plane->crtc && (plane->crtc == encoder->crtc))
+			plane->funcs->disable_plane(plane);
+	}
+}
+
+
+static struct drm_encoder_helper_funcs rockchip_encoder_helper_funcs = {
+	.dpms = rockchip_drm_encoder_dpms,
+	.mode_fixup = rockchip_drm_encoder_mode_fixup,
+	.mode_set = rockchip_drm_encoder_mode_set,
+	.prepare = rockchip_drm_encoder_prepare,
+	.commit = rockchip_drm_encoder_commit,
+	.disable = rockchip_drm_encoder_disable,
+};
+static void rockchip_drm_encoder_destroy(struct drm_encoder *encoder)
+{
+	drm_encoder_cleanup(encoder);
+}
+
+static struct drm_encoder_funcs rockchip_encoder_funcs = {
+	.destroy = rockchip_drm_encoder_destroy,
+};
+
+static unsigned int rockchip_drm_encoder_clones(struct drm_encoder *encoder)
+{
+	struct rockchip_conn_context *ctx = encoder_to_ctx(encoder);
+	struct drm_encoder *clone;
+	struct drm_device *dev = encoder->dev;
+	unsigned int clone_mask = 0;
+	int cnt = 0;
+
+	list_for_each_entry(clone, &dev->mode_config.encoder_list, head) {
+		switch (ctx->type) {
+		case ROCKCHIP_DISPLAY_TYPE_RGB:
+		case ROCKCHIP_DISPLAY_TYPE_LVDS:
+		case ROCKCHIP_DISPLAY_TYPE_EDP:
+		case ROCKCHIP_DISPLAY_TYPE_HDMI:
+			clone_mask |= (1 << (cnt++));
+			break;
+		default:
+			continue;
+		}
+	}
+
+	return clone_mask;
+}
+
+static int rockchip_conn_bind(struct device *dev, struct device *master,
+			      void *data)
+{
+	struct rockchip_conn_context *ctx;
+	unsigned long possible_crtcs = 0;
+	struct drm_encoder *encoder;
+	struct drm_connector *connector;
+	struct device_node *panel_node;
+	struct drm_device *drm_dev = data;
+	int ret;
+
+	ctx = rockchip_drm_component_data_get(dev,
+					      ROCKCHIP_DEVICE_TYPE_CONNECTOR);
+	if (!ctx) {
+		DRM_ERROR("can't find dp content form component\n");
+		return -EINVAL;
+	}
+
+	ret = rockchip_drm_pipe_get(dev);
+	if (ret < 0) {
+		DRM_ERROR("failed to bind display port\n");
+		return ret;
+	}
+
+	possible_crtcs |= 1 << ret;
+
+	encoder = &ctx->encoder;
+	encoder->possible_crtcs = possible_crtcs;
+
+	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
+
+	ret = drm_encoder_init(drm_dev, encoder, &rockchip_encoder_funcs,
+			       rockchip_convert_encoder_type(ctx->type));
+	if (ret) {
+		DRM_ERROR("failed to initialize encoder with drm\n");
+		return ret;
+	}
+
+	drm_encoder_helper_add(encoder, &rockchip_encoder_helper_funcs);
+
+	connector = &ctx->connector;
+	connector->polled = DRM_CONNECTOR_POLL_HPD;
+	connector->dpms = DRM_MODE_DPMS_OFF;
+
+	ret = drm_connector_init(drm_dev, connector,
+				 &rockchip_connector_funcs,
+				 rockchip_convert_conn_type(ctx->type));
+	if (ret) {
+		DRM_ERROR("failed to initialize connector with drm\n");
+		goto err_free_encoder;
+	}
+
+	drm_connector_helper_add(connector,
+				 &rockchip_connector_helper_funcs);
+
+	ret = drm_sysfs_connector_add(connector);
+	if (ret) {
+		DRM_ERROR("failed to add drm_sysfs\n");
+		goto err_free_connector;
+	}
+
+	ret = drm_mode_connector_attach_encoder(connector, encoder);
+	if (ret) {
+		DRM_ERROR("failed to attach connector and encoder\n");
+		goto err_free_connector_sysfs;
+	}
+
+	panel_node = of_parse_phandle(dev->of_node, "rockchip,panel", 0);
+	if (!panel_node) {
+		DRM_ERROR("failed to find diaplay panel\n");
+		goto err_free_connector_sysfs;
+	}
+	ctx->panel = of_drm_find_panel(panel_node);
+	if (!ctx->panel) {
+		DRM_ERROR("failed to find diaplay panel\n");
+		ret = -ENODEV;
+		goto err_free_connector_sysfs;
+	}
+
+	of_node_put(panel_node);
+
+	ret = drm_panel_attach(ctx->panel, connector);
+	if (ret) {
+		DRM_ERROR("failed to attach connector and encoder\n");
+		goto err_free_connector_sysfs;
+	}
+
+	return 0;
+
+err_free_connector_sysfs:
+	drm_sysfs_connector_remove(connector);
+err_free_connector:
+	drm_connector_cleanup(connector);
+err_free_encoder:
+	drm_encoder_cleanup(encoder);
+	return ret;
+}
+
+static void rockchip_conn_unbind(struct device *dev, struct device *master,
+				 void *data)
+{
+	struct rockchip_conn_context *ctx;
+	struct drm_encoder *encoder;
+
+	ctx = rockchip_drm_component_data_get(dev,
+					      ROCKCHIP_DEVICE_TYPE_CONNECTOR);
+	encoder = &ctx->encoder;
+
+	drm_panel_detach(ctx->panel);
+
+	rockchip_drm_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+	encoder->funcs->destroy(encoder);
+	drm_sysfs_connector_remove(&ctx->connector);
+	drm_connector_cleanup(&ctx->connector);
+	drm_encoder_cleanup(encoder);
+
+	rockchip_drm_component_del(dev, ROCKCHIP_DEVICE_TYPE_CONNECTOR);
+}
+
+static const struct component_ops rockchip_conn_component_ops = {
+	.bind = rockchip_conn_bind,
+	.unbind = rockchip_conn_unbind,
+};
+
+void *rockchip_connector_register(struct rockchip_connector *conn)
+{
+	struct rockchip_conn_context *ctx;
+	struct device *dev = conn->dev;
+	int ret;
+
+	if (!dev) {
+		DRM_ERROR("please provide a device at dp register\n");
+		return NULL;
+	}
+
+	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return NULL;
+
+	ret = rockchip_drm_component_add(dev, ROCKCHIP_DEVICE_TYPE_CONNECTOR,
+					 conn->type, ctx);
+	if (ret < 0) {
+		DRM_ERROR("register connector component fail ret =%d\n", ret);
+		return NULL;
+	}
+
+	ctx->conn = conn;
+	ctx->dev = dev;
+	ctx->type = conn->type;
+	ctx->dpms_mode = DRM_MODE_DPMS_OFF;
+
+	ret = component_add(dev, &rockchip_conn_component_ops);
+	if (ret)
+		goto err_del_component;
+
+	DRM_DEBUG_KMS("succes register connector type=%d\n", conn->type);
+
+	return ctx;
+
+err_del_component:
+	rockchip_drm_component_del(dev, ROCKCHIP_DEVICE_TYPE_CONNECTOR);
+	return NULL;
+}
+
+void rockchip_connector_unregister(void *data)
+{
+	struct rockchip_conn_context *ctx = data;
+
+	if (!ctx)
+		return;
+	rockchip_drm_component_del(ctx->dev, ROCKCHIP_DEVICE_TYPE_CONNECTOR);
+	component_del(ctx->dev, &rockchip_conn_component_ops);
+}
+
+void rockchip_drm_encoder_setup(struct drm_device *dev)
+{
+	struct drm_encoder *encoder;
+
+	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
+		encoder->possible_clones =
+				rockchip_drm_encoder_clones(encoder);
+}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_connector.h b/drivers/gpu/drm/rockchip/rockchip_drm_connector.h
new file mode 100644
index 0000000..191f9fc
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_connector.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:mark yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _ROCKCHIP_DRM_CONNECTOR_H_
+#define _ROCKCHIP_DRM_CONNECTOR_H_
+
+#include <drm/drm_crtc.h>
+
+#include "rockchip_drm_drv.h"
+
+struct rockchip_connector {
+	struct device *dev;
+	int type;
+	void *priv;
+	u32 flags;
+
+	void (*enable)(struct rockchip_connector *conn);
+	void (*disable)(struct rockchip_connector *conn);
+	int (*setmode)(struct rockchip_connector *conn,
+		       struct drm_display_mode *mode);
+};
+
+void *rockchip_connector_register(struct rockchip_connector *conn);
+void rockchip_connector_unregister(void *data);
+#endif
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
new file mode 100644
index 0000000..4871867
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -0,0 +1,600 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:mark yao <mark.yao@rock-chips.com>
+ *
+ * based on exynos_drm_drv.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/pm_runtime.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include <linux/anon_inodes.h>
+#include <linux/component.h>
+
+#include <drm/rockchip_drm.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_fb.h"
+#include "rockchip_drm_fbdev.h"
+#include "rockchip_drm_gem.h"
+
+#define DRIVER_NAME	"rockchip-drm"
+#define DRIVER_DESC	"RockChip Soc DRM"
+#define DRIVER_DATE	"20140725"
+#define DRIVER_MAJOR	1
+#define DRIVER_MINOR	0
+
+#define VBLANK_OFF_DELAY	50000
+
+static struct platform_device *rockchip_drm_pdev;
+
+static DEFINE_MUTEX(drm_component_lock);
+static LIST_HEAD(drm_component_list);
+
+struct component_dev {
+	struct list_head list;
+	struct device *crtc_dev;
+	struct device *conn_dev;
+	unsigned int out_type;
+	int pipe;
+	void *crtc_data;
+	void *conn_data;
+};
+
+static int rockchip_drm_load(struct drm_device *dev, unsigned long flags)
+{
+	struct rockchip_drm_private *private;
+	int ret;
+	int nr;
+
+	private = kzalloc(sizeof(*private), GFP_KERNEL);
+	if (!private)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev->dev, dev);
+	dev->dev_private = (void *)private;
+
+	drm_mode_config_init(dev);
+
+	rockchip_drm_mode_config_init(dev);
+
+
+	/* Try to bind all sub drivers. */
+	ret = component_bind_all(dev->dev, dev);
+	if (ret)
+		goto err_cleanup_vblank;
+
+	for (nr = 0; nr < MAX_PLANE; nr++) {
+		struct drm_plane *plane;
+		unsigned long possible_crtcs = (1 << MAX_CRTC) - 1;
+
+		plane = rockchip_plane_init(dev, possible_crtcs, false);
+		if (!plane)
+			goto err_mode_config_cleanup;
+	}
+
+	/* init kms poll for handling hpd */
+	drm_kms_helper_poll_init(dev);
+
+	ret = drm_vblank_init(dev, MAX_CRTC);
+	if (ret)
+		goto err_mode_config_cleanup;
+
+	/* setup possible_clones. */
+	rockchip_drm_encoder_setup(dev);
+
+	drm_vblank_offdelay = VBLANK_OFF_DELAY;
+
+	platform_set_drvdata(dev->platformdev, dev);
+	rockchip_drm_fbdev_init(dev);
+
+	/* force connectors detection */
+	drm_helper_hpd_irq_event(dev);
+
+	return 0;
+
+err_cleanup_vblank:
+	drm_vblank_cleanup(dev);
+err_mode_config_cleanup:
+	drm_mode_config_cleanup(dev);
+	kfree(private);
+
+	return ret;
+}
+
+static int rockchip_drm_unload(struct drm_device *dev)
+{
+	rockchip_drm_fbdev_fini(dev);
+	drm_vblank_cleanup(dev);
+	drm_kms_helper_poll_fini(dev);
+	drm_mode_config_cleanup(dev);
+
+	kfree(dev->dev_private);
+
+	component_unbind_all(dev->dev, dev);
+	dev->dev_private = NULL;
+
+	return 0;
+}
+
+static int rockchip_drm_suspend(struct drm_device *dev, pm_message_t state)
+{
+	struct drm_connector *connector;
+
+	drm_modeset_lock_all(dev);
+	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+		int old_dpms = connector->dpms;
+
+		if (connector->funcs->dpms)
+			connector->funcs->dpms(connector, DRM_MODE_DPMS_OFF);
+
+		/* Set the old mode back to the connector for resume */
+		connector->dpms = old_dpms;
+	}
+	drm_modeset_unlock_all(dev);
+
+	return 0;
+}
+
+static int rockchip_drm_resume(struct drm_device *dev)
+{
+	struct drm_connector *connector;
+
+	drm_modeset_lock_all(dev);
+	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+		if (connector->funcs->dpms)
+			connector->funcs->dpms(connector, connector->dpms);
+	}
+	drm_modeset_unlock_all(dev);
+
+	drm_helper_resume_force_mode(dev);
+
+	return 0;
+}
+
+static int rockchip_drm_open(struct drm_device *dev, struct drm_file *file)
+{
+	return 0;
+}
+
+static void rockchip_drm_postclose(struct drm_device *dev,
+				   struct drm_file *file)
+{
+	struct drm_pending_event *e, *et;
+	unsigned long flags;
+
+	if (!file->driver_priv)
+		return;
+
+	/* Release all events not unhandled by page flip handler. */
+	rockchip_drm_crtc_cancel_pending_flip(dev);
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+
+	/* Release all events handled by page flip handler but not freed. */
+	list_for_each_entry_safe(e, et, &file->event_list, link) {
+		list_del(&e->link);
+		e->destroy(e);
+	}
+
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+
+	kfree(file->driver_priv);
+	file->driver_priv = NULL;
+}
+
+static const struct drm_ioctl_desc rockchip_ioctls[] = {
+	DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_CREATE, rockchip_drm_gem_create_ioctl,
+			  DRM_UNLOCKED | DRM_AUTH),
+	DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_MAP_OFFSET,
+			  rockchip_drm_gem_map_offset_ioctl, DRM_UNLOCKED |
+			  DRM_AUTH),
+	DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_MMAP, rockchip_drm_gem_mmap_ioctl,
+			  DRM_UNLOCKED | DRM_AUTH),
+	DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_GET, rockchip_drm_gem_get_ioctl,
+			  DRM_UNLOCKED),
+};
+
+static const struct file_operations rockchip_drm_driver_fops = {
+	.owner = THIS_MODULE,
+	.open = drm_open,
+	.mmap = drm_gem_cma_mmap,
+	.poll = drm_poll,
+	.read = drm_read,
+	.unlocked_ioctl = drm_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = drm_compat_ioctl,
+#endif
+	.release = drm_release,
+};
+
+static struct drm_driver rockchip_drm_driver = {
+	.driver_features	= DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
+	.load			= rockchip_drm_load,
+	.unload			= rockchip_drm_unload,
+	.suspend		= rockchip_drm_suspend,
+	.resume			= rockchip_drm_resume,
+	.open			= rockchip_drm_open,
+	.postclose		= rockchip_drm_postclose,
+	.get_vblank_counter	= drm_vblank_count,
+	.enable_vblank		= rockchip_drm_crtc_enable_vblank,
+	.disable_vblank		= rockchip_drm_crtc_disable_vblank,
+	.gem_free_object        = drm_gem_cma_free_object,
+	.gem_vm_ops             = &drm_gem_cma_vm_ops,
+	.dumb_create            = drm_gem_cma_dumb_create,
+	.dumb_map_offset        = drm_gem_cma_dumb_map_offset,
+	.dumb_destroy           = drm_gem_dumb_destroy,
+	.prime_handle_to_fd	= drm_gem_prime_handle_to_fd,
+	.prime_fd_to_handle	= drm_gem_prime_fd_to_handle,
+	.gem_prime_import       = drm_gem_prime_import,
+	.gem_prime_export       = drm_gem_prime_export,
+	.gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
+	.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
+	.gem_prime_vmap         = drm_gem_cma_prime_vmap,
+	.gem_prime_vunmap       = drm_gem_cma_prime_vunmap,
+	.gem_prime_mmap         = drm_gem_cma_prime_mmap,
+	.ioctls			= rockchip_ioctls,
+	.num_ioctls		= ARRAY_SIZE(rockchip_ioctls),
+	.fops			= &rockchip_drm_driver_fops,
+	.name	= DRIVER_NAME,
+	.desc	= DRIVER_DESC,
+	.date	= DRIVER_DATE,
+	.major	= DRIVER_MAJOR,
+	.minor	= DRIVER_MINOR,
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int rockchip_drm_sys_suspend(struct device *dev)
+{
+	struct drm_device *drm_dev = dev_get_drvdata(dev);
+	pm_message_t message;
+
+	if (pm_runtime_suspended(dev))
+		return 0;
+
+	message.event = PM_EVENT_SUSPEND;
+
+	return rockchip_drm_suspend(drm_dev, message);
+}
+
+static int rockchip_drm_sys_resume(struct device *dev)
+{
+	struct drm_device *drm_dev = dev_get_drvdata(dev);
+
+	if (pm_runtime_suspended(dev))
+		return 0;
+
+	return rockchip_drm_resume(drm_dev);
+}
+#endif
+
+static const struct dev_pm_ops rockchip_drm_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(rockchip_drm_sys_suspend,
+				rockchip_drm_sys_resume)
+};
+
+int rockchip_drm_pipe_get(struct device *dev)
+{
+	struct component_dev *cdev, *next;
+	int pipe = -1;
+
+	mutex_lock(&drm_component_lock);
+
+	list_for_each_entry_safe(cdev, next, &drm_component_list, list) {
+		if ((cdev->crtc_dev == dev) || (cdev->conn_dev == dev)) {
+			pipe = cdev->pipe;
+			break;
+		}
+	}
+
+	mutex_unlock(&drm_component_lock);
+
+	return pipe;
+}
+
+int rockchip_drm_out_type_get(struct device *dev)
+{
+	struct component_dev *cdev, *next;
+	int type = -1;
+
+	mutex_lock(&drm_component_lock);
+
+	list_for_each_entry_safe(cdev, next, &drm_component_list, list) {
+		if ((cdev->crtc_dev == dev) || (cdev->conn_dev == dev)) {
+			type = cdev->out_type;
+			break;
+		}
+	}
+
+	mutex_unlock(&drm_component_lock);
+
+	return type;
+}
+
+void *rockchip_drm_component_data_get(struct device *dev,
+				      enum rockchip_drm_device_type dev_type)
+{
+	struct component_dev *cdev, *next;
+	void *data = NULL;
+
+	mutex_lock(&drm_component_lock);
+
+	list_for_each_entry_safe(cdev, next, &drm_component_list, list) {
+		if ((cdev->crtc_dev == dev) || (cdev->conn_dev == dev)) {
+			if (dev_type == ROCKCHIP_DEVICE_TYPE_CRTC)
+				data = cdev->crtc_data;
+			else if (dev_type == ROCKCHIP_DEVICE_TYPE_CONNECTOR)
+				data = cdev->conn_data;
+			break;
+		}
+	}
+
+	mutex_unlock(&drm_component_lock);
+
+	return data;
+}
+
+int rockchip_drm_component_add(struct device *dev,
+			       enum rockchip_drm_device_type dev_type,
+			       int out_type, void *data)
+{
+	struct component_dev *cdev;
+	int pipe = -1;
+
+	if (dev_type != ROCKCHIP_DEVICE_TYPE_CRTC &&
+	    dev_type != ROCKCHIP_DEVICE_TYPE_CONNECTOR) {
+		DRM_ERROR("invalid device type.\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&drm_component_lock);
+
+	/*
+	 * Make sure to check if there is a component which has two device
+	 * objects, for connector and for encoder/connector.
+	 * It should make sure that crtc and encoder/connector drivers are
+	 * ready before rockchip drm core binds them.
+	 */
+	list_for_each_entry(cdev, &drm_component_list, list) {
+		pipe++;
+		/*
+		 * out_type from crtc and display port, crtc set possible
+		 * out_type maskbit at out_type, and display posr set out_type
+		 * directly. and if crtc and display port all register, set
+		 * out_type not maskbit;
+		 */
+		if (cdev->out_type & out_type) {
+			if (cdev->crtc_dev && cdev->conn_dev) {
+				DRM_ERROR("already register, not allow");
+				return -EINVAL;
+			}
+
+			if (dev_type == ROCKCHIP_DEVICE_TYPE_CRTC) {
+				cdev->pipe = pipe;
+				cdev->crtc_dev = dev;
+				cdev->crtc_data = data;
+			} else if (dev_type == ROCKCHIP_DEVICE_TYPE_CONNECTOR) {
+				cdev->conn_dev = dev;
+				cdev->conn_data = data;
+				cdev->out_type = out_type;
+			}
+
+			mutex_unlock(&drm_component_lock);
+			return 0;
+		}
+	}
+
+	mutex_unlock(&drm_component_lock);
+
+	cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
+	if (!cdev)
+		return -ENOMEM;
+
+	if (dev_type == ROCKCHIP_DEVICE_TYPE_CRTC) {
+		cdev->crtc_dev = dev;
+		cdev->crtc_data = data;
+	} else if (dev_type == ROCKCHIP_DEVICE_TYPE_CONNECTOR) {
+		cdev->conn_dev = dev;
+		cdev->conn_data = data;
+	}
+
+	cdev->out_type = out_type;
+
+	mutex_lock(&drm_component_lock);
+	list_add_tail(&cdev->list, &drm_component_list);
+	mutex_unlock(&drm_component_lock);
+
+	return 0;
+}
+
+void rockchip_drm_component_del(struct device *dev,
+				enum rockchip_drm_device_type dev_type)
+{
+	struct component_dev *cdev, *next;
+
+	mutex_lock(&drm_component_lock);
+
+	list_for_each_entry_safe(cdev, next, &drm_component_list, list) {
+		if (dev_type == ROCKCHIP_DEVICE_TYPE_CRTC) {
+			if (cdev->crtc_dev == dev)
+				cdev->crtc_dev = NULL;
+		}
+
+		if (dev_type == ROCKCHIP_DEVICE_TYPE_CONNECTOR) {
+			if (cdev->conn_dev == dev)
+				cdev->conn_dev = NULL;
+		}
+
+		/*
+		 * Release cdev object only in case that both of crtc and
+		 * encoder/connector device objects are NULL.
+		 */
+		if (!cdev->crtc_dev && !cdev->conn_dev) {
+			list_del(&cdev->list);
+			kfree(cdev);
+		}
+
+		break;
+	}
+
+	mutex_unlock(&drm_component_lock);
+}
+
+static int compare_of(struct device *dev, void *data)
+{
+	return dev == (struct device *)data;
+}
+
+static int rockchip_drm_add_components(struct device *dev, struct master *m)
+{
+	struct component_dev *cdev;
+	unsigned int attach_cnt = 0;
+
+	mutex_lock(&drm_component_lock);
+
+	list_for_each_entry(cdev, &drm_component_list, list) {
+		int ret;
+
+		/*
+		 * Add components to master only in case that crtc and
+		 * encoder/connector device objects exist.
+		 */
+		if (!cdev->crtc_dev || !cdev->conn_dev)
+			continue;
+
+		attach_cnt++;
+
+		mutex_unlock(&drm_component_lock);
+
+		/*
+		 * Do not chage below call order.
+		 * crtc device first should be added to master because
+		 * connector/encoder need pipe number of crtc when they
+		 * are created.
+		 */
+		ret = component_master_add_child(m, compare_of, cdev->crtc_dev);
+		ret |= component_master_add_child(m, compare_of,
+						  cdev->conn_dev);
+		if (ret < 0)
+			return ret;
+
+		mutex_lock(&drm_component_lock);
+	}
+
+	mutex_unlock(&drm_component_lock);
+
+	return attach_cnt ? 0 : -ENODEV;
+}
+
+static int rockchip_drm_bind(struct device *dev)
+{
+	return drm_platform_init(&rockchip_drm_driver, to_platform_device(dev));
+}
+
+static void rockchip_drm_unbind(struct device *dev)
+{
+	drm_put_dev(dev_get_drvdata(dev));
+}
+
+static const struct component_master_ops rockchip_drm_ops = {
+	.add_components = rockchip_drm_add_components,
+	.bind = rockchip_drm_bind,
+	.unbind = rockchip_drm_unbind,
+};
+
+static int rockchip_drm_platform_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+	rockchip_drm_driver.num_ioctls = ARRAY_SIZE(rockchip_ioctls);
+
+	ret = component_master_add(&pdev->dev, &rockchip_drm_ops);
+	if (ret < 0)
+		DRM_DEBUG_KMS("re-tried by last sub driver probed later.\n");
+
+	return 0;
+}
+
+static int rockchip_drm_platform_remove(struct platform_device *pdev)
+{
+	component_master_del(&pdev->dev, &rockchip_drm_ops);
+
+	return 0;
+}
+
+static struct platform_driver rockchip_drm_platform_driver = {
+	.probe = rockchip_drm_platform_probe,
+	.remove = rockchip_drm_platform_remove,
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "rockchip-drm",
+		.pm = &rockchip_drm_pm_ops,
+	},
+};
+
+static int rockchip_drm_init(void)
+{
+	int ret;
+
+	ret = platform_driver_register(&rockchip_panel_platform_driver);
+	if (ret < 0)
+		return -ENOMEM;
+
+#ifdef CONFIG_DRM_ROCKCHIP_LCDC
+	ret = platform_driver_register(&rockchip_lcdc_platform_driver);
+	if (ret < 0)
+		goto out_lcdc;
+#endif
+
+	rockchip_drm_pdev = platform_device_register_simple("rockchip-drm", -1,
+							    NULL, 0);
+	if (IS_ERR(rockchip_drm_pdev)) {
+		ret = PTR_ERR(rockchip_drm_pdev);
+		goto out_drm_pdev;
+	}
+
+	ret = platform_driver_register(&rockchip_drm_platform_driver);
+	if (ret)
+		goto out_drm_driver;
+
+	return 0;
+
+out_drm_driver:
+	platform_device_unregister(rockchip_drm_pdev);
+out_drm_pdev:
+#ifdef CONFIG_DRM_ROCKCHIP_LCDC
+	platform_driver_unregister(&rockchip_lcdc_platform_driver);
+out_lcdc:
+#endif
+	platform_driver_unregister(&rockchip_panel_platform_driver);
+	return ret;
+}
+
+static void rockchip_drm_exit(void)
+{
+	platform_device_unregister(rockchip_drm_pdev);
+	platform_driver_unregister(&rockchip_drm_platform_driver);
+#ifdef CONFIG_DRM_ROCKCHIP_LCDC
+	platform_driver_unregister(&rockchip_lcdc_platform_driver);
+#endif
+	platform_driver_unregister(&rockchip_panel_platform_driver);
+}
+
+module_init(rockchip_drm_init);
+module_exit(rockchip_drm_exit);
+
+MODULE_AUTHOR("mark yao <mark.yao@rock-chips.com>");
+MODULE_DESCRIPTION("ROCKCHIP DRM Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
new file mode 100644
index 0000000..c0c1d89
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:mark yao <mark.yao@rock-chips.com>
+ *
+ * based on exynos_drm_drv.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _ROCKCHIP_DRM_DRV_H_
+#define _ROCKCHIP_DRM_DRV_H_
+
+#include <linux/module.h>
+
+#define MAX_CRTC	3
+#define MAX_PLANE	5
+#define MAX_FB_BUFFER	4
+#define DEFAULT_ZPOS	-1
+
+struct drm_device;
+struct drm_connector;
+
+/*
+ * display output interface supported by rockchip lcdc
+ */
+#define ROCKCHIP_OUTFACE_P888	0
+#define ROCKCHIP_OUTFACE_P666	1
+#define ROCKCHIP_OUTFACE_P565	2
+/* for use special outface */
+#define ROCKCHIP_OUTFACE_AAAA	15
+
+#define ROCKCHIP_COLOR_SWAP_RG	0x1
+#define ROCKCHIP_COLOR_SWAP_RB	0x2
+#define ROCKCHIP_COLOR_SWAP_GB	0x4
+/*
+ * Special panel info for rockchip
+ *
+ * @out_type: lcd controller need to know the sceen type.
+ * @out_face: the output pin interface.
+ * @color_swap: if want to swap color at output, use this.
+ * @pwr18: choice the power supply 1.8 or 3.3 mode for lcdc
+ * @dither: use dither func at lcd output
+ * @flags: the display flags, now just for pin sync level.
+ */
+struct rockchip_panel_special {
+	int out_type;
+	int out_face;
+	u32 color_swap;
+	bool pwr18;
+	bool dither;
+	u32 flags;
+};
+
+/* This enumerates device type. */
+enum rockchip_drm_device_type {
+	ROCKCHIP_DEVICE_TYPE_NONE,
+	ROCKCHIP_DEVICE_TYPE_CRTC,
+	ROCKCHIP_DEVICE_TYPE_CONNECTOR,
+};
+
+/* this enumerates display type. */
+enum rockchip_drm_output_type {
+	ROCKCHIP_DISPLAY_TYPE_NONE = 0,
+	/* RGB Interface. */
+	ROCKCHIP_DISPLAY_TYPE_RGB = (1 << 0),
+	/* LVDS Interface. */
+	ROCKCHIP_DISPLAY_TYPE_LVDS = (1 << 1),
+	/* DUAL LVDS Interface. */
+	ROCKCHIP_DISPLAY_TYPE_DUAL_LVDS = (1 << 2),
+	/* EDP Interface. */
+	ROCKCHIP_DISPLAY_TYPE_EDP = (1 << 3),
+	/* MIPI Interface. */
+	ROCKCHIP_DISPLAY_TYPE_MIPI = (1 << 4),
+	/* HDMI Interface. */
+	ROCKCHIP_DISPLAY_TYPE_HDMI = (1 << 5),
+};
+
+/*
+ * Rockchip drm private structure.
+ *
+ * @pipe: the pipe number for this crtc/manager.
+ */
+struct rockchip_drm_private {
+	struct drm_fbdev_cma *fbdev_cma;
+	/*
+	 * created crtc object would be contained at this array and
+	 * this array is used to be aware of which crtc did it request vblank.
+	 */
+	struct drm_crtc *crtc[MAX_CRTC];
+	struct drm_property *plane_zpos_property;
+	struct drm_property *crtc_mode_property;
+
+	unsigned int pipe;
+};
+
+
+void rockchip_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe);
+void rockchip_drm_crtc_cancel_pending_flip(struct drm_device *dev);
+int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, int pipe);
+void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, int pipe);
+
+struct drm_plane *rockchip_plane_init(struct drm_device *dev,
+				      unsigned long possible_crtcs, bool priv);
+
+void rockchip_drm_encoder_setup(struct drm_device *dev);
+
+void *rockchip_drm_component_data_get(struct device *dev,
+				      enum rockchip_drm_device_type dev_type);
+int rockchip_drm_pipe_get(struct device *dev);
+
+int rockchip_drm_component_add(struct device *dev,
+			       enum rockchip_drm_device_type dev_type,
+			       int out_type, void *data);
+void rockchip_drm_component_del(struct device *dev,
+				enum rockchip_drm_device_type dev_type);
+
+extern struct platform_driver rockchip_panel_platform_driver;
+#ifdef CONFIG_DRM_ROCKCHIP_LCDC
+extern struct platform_driver rockchip_lcdc_platform_driver;
+#endif
+#endif /* _ROCKCHIP_DRM_DRV_H_ */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
new file mode 100644
index 0000000..a04024b
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:mark yao <mark.yao@rock-chips.com>
+ *
+ * based on exynos_drm_fb.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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_fb_cma_helper.h>
+#include <drm/drm_fb_helper.h>
+
+#include <uapi/drm/rockchip_drm.h>
+
+static struct drm_framebuffer *
+rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
+			struct drm_mode_fb_cmd2 *mode_cmd)
+{
+	return drm_fb_cma_create(dev, file_priv, mode_cmd);
+}
+
+static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
+	.fb_create = rockchip_user_fb_create,
+};
+
+void rockchip_drm_mode_config_init(struct drm_device *dev)
+{
+	dev->mode_config.min_width = 0;
+	dev->mode_config.min_height = 0;
+
+	/*
+	 * 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 = &rockchip_drm_mode_config_funcs;
+}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.h b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h
new file mode 100644
index 0000000..6258de6
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:mark yao <mark.yao@rock-chips.com>
+ *
+ * based on exynos_drm_fb.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _ROCKCHIP_DRM_FB_H_
+#define _ROCKCHIP_DRM_FB_H_
+
+struct drm_framebuffer *
+rockchip_drm_framebuffer_init(struct drm_device *dev,
+			      struct drm_mode_fb_cmd2 *mode_cmd,
+			      struct drm_gem_object *obj);
+
+void rockchip_drm_mode_config_init(struct drm_device *dev);
+
+#endif /* _ROCKCHIP_DRM_FB_H_ */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
new file mode 100644
index 0000000..d32fa57
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:mark yao <mark.yao@rock-chips.com>
+ *
+ * based on exynos_drm_fbdev.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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_fb_cma_helper.h>
+
+#include <drm/rockchip_drm.h>
+
+#include "rockchip_drm_drv.h"
+
+#define MAX_CONNECTOR		4
+#define PREFERRED_BPP		32
+
+int rockchip_drm_fbdev_init(struct drm_device *dev)
+{
+	struct rockchip_drm_private *private = dev->dev_private;
+	struct drm_fbdev_cma *fbdev_cma;
+	unsigned int num_crtc;
+
+	if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector)
+		return 0;
+
+	if (private->fbdev_cma) {
+		DRM_ERROR("no allow to reinit cma fbdev\n");
+		return -EINVAL;
+	}
+
+	num_crtc = dev->mode_config.num_crtc;
+
+	fbdev_cma = drm_fbdev_cma_init(dev, PREFERRED_BPP, num_crtc,
+				       MAX_CONNECTOR);
+	if (!fbdev_cma) {
+		DRM_ERROR("failed to init cma fbdev\n");
+		return -ENOMEM;
+	}
+
+	private->fbdev_cma = fbdev_cma;
+
+	return 0;
+}
+
+void rockchip_drm_fbdev_fini(struct drm_device *dev)
+{
+	struct rockchip_drm_private *private = dev->dev_private;
+
+	if (!private || !private->fbdev_cma)
+		return;
+
+	drm_fbdev_cma_fini(private->fbdev_cma);
+}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
new file mode 100644
index 0000000..91cb535
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:mark yao <mark.yao@rock-chips.com>
+ *
+ * based on exynos_drm_fbdev.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _ROCKCHIP_DRM_FBDEV_H_
+#define _ROCKCHIP_DRM_FBDEV_H_
+
+int rockchip_drm_fbdev_init(struct drm_device *dev);
+void rockchip_drm_fbdev_fini(struct drm_device *dev);
+
+#endif /* _ROCKCHIP_DRM_FBDEV_H_ */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
new file mode 100644
index 0000000..f0219cd
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
@@ -0,0 +1,163 @@
+/*
+ *
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:mark yao <mark.yao@rock-chips.com>
+ *
+ * based on exynos_drm_gem.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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_vma_manager.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include <drm/rockchip_drm.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_gem.h"
+
+int rockchip_drm_gem_create_ioctl(struct drm_device *dev, void *data,
+				  struct drm_file *file_priv)
+{
+	struct drm_rockchip_gem_create *args = data;
+	struct drm_gem_cma_object *cma_obj;
+	struct drm_gem_object *gem_obj;
+	int ret;
+
+	cma_obj = drm_gem_cma_create(dev, args->size);
+	if (IS_ERR(cma_obj))
+		return PTR_ERR_OR_ZERO(cma_obj);
+
+	gem_obj = &cma_obj->base;
+
+	/*
+	 * 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, gem_obj, &args->handle);
+	if (ret)
+		goto err_handle_create;
+
+	/* drop reference from allocate - handle holds it now. */
+	drm_gem_object_unreference_unlocked(gem_obj);
+
+	return PTR_ERR_OR_ZERO(cma_obj);
+
+err_handle_create:
+	drm_gem_cma_free_object(gem_obj);
+	return ret;
+}
+
+int rockchip_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data,
+				      struct drm_file *file_priv)
+{
+	struct drm_rockchip_gem_map_off *args = data;
+
+	DRM_DEBUG_KMS("handle = 0x%x, offset = 0x%lx\n",
+		      args->handle, (unsigned long)args->offset);
+
+	return drm_gem_cma_dumb_map_offset(file_priv, dev, args->handle,
+					   &args->offset);
+}
+
+int rockchip_drm_gem_mmap_ioctl(struct drm_device *dev, void *data,
+				struct drm_file *file_priv)
+{
+	struct drm_rockchip_gem_mmap *args = data;
+	struct drm_gem_object *obj;
+	unsigned long addr;
+
+	mutex_lock(&dev->struct_mutex);
+
+	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+	if (!obj) {
+		DRM_ERROR("failed to lookup gem object.\n");
+		mutex_unlock(&dev->struct_mutex);
+		return -EINVAL;
+	}
+
+	addr = vm_mmap(obj->filp, 0, args->size, PROT_READ | PROT_WRITE,
+		       MAP_SHARED, 0);
+
+	drm_gem_object_unreference(obj);
+
+	if (IS_ERR_VALUE(addr)) {
+		mutex_unlock(&dev->struct_mutex);
+		return (int)addr;
+	}
+
+	mutex_unlock(&dev->struct_mutex);
+
+	args->mapped = addr;
+
+	DRM_DEBUG_KMS("mapped = 0x%lx\n", (unsigned long)args->mapped);
+
+	return 0;
+}
+
+int rockchip_drm_gem_get_ioctl(struct drm_device *dev, void *data,
+			       struct drm_file *file_priv)
+{
+	struct drm_rockchip_gem_info *args = data;
+	struct drm_gem_object *obj;
+
+	mutex_lock(&dev->struct_mutex);
+
+	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+	if (!obj) {
+		DRM_ERROR("failed to lookup gem object.\n");
+		mutex_unlock(&dev->struct_mutex);
+		return -EINVAL;
+	}
+
+	args->size = obj->size;
+
+	drm_gem_object_unreference(obj);
+	mutex_unlock(&dev->struct_mutex);
+
+	return 0;
+}
+
+int rockchip_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;
+}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
new file mode 100644
index 0000000..fe8285f
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:mark yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _ROCKCHIP_DRM_GEM_H_
+#define _ROCKCHIP_DRM_GEM_H_
+
+/*
+ * request gem object creation and buffer allocation as the size
+ * that it is calculated with framebuffer information such as width,
+ * height and bpp.
+ */
+int rockchip_drm_gem_create_ioctl(struct drm_device *dev, void *data,
+				  struct drm_file *file_priv);
+
+/* get buffer offset to map to user space. */
+int rockchip_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data,
+				      struct drm_file *file_priv);
+
+/*
+ * mmap the physically continuous memory that a gem object contains
+ * to user space.
+ */
+int rockchip_drm_gem_mmap_ioctl(struct drm_device *dev, void *data,
+				struct drm_file *file_priv);
+
+/* get buffer information to memory region allocated by gem. */
+int rockchip_drm_gem_get_ioctl(struct drm_device *dev, void *data,
+			       struct drm_file *file_priv);
+#endif
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_lcdc.c b/drivers/gpu/drm/rockchip/rockchip_drm_lcdc.c
new file mode 100644
index 0000000..68991d4
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_lcdc.c
@@ -0,0 +1,720 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:mark yao <mark.yao@rock-chips.com>
+ *
+ * based on exynos_drm_fimd.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/component.h>
+
+#include <drm/rockchip_drm.h>
+
+#include <video/of_display_timing.h>
+#include <video/of_videomode.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_fbdev.h"
+#include "rockchip_drm_lcdc.h"
+
+#define LCDC_DEFAULT_FRAMERATE 60
+
+#define ROCKCHIP_DISPLAY_TYPE_LCD (ROCKCHIP_DISPLAY_TYPE_RGB | \
+					ROCKCHIP_DISPLAY_TYPE_LVDS | \
+					ROCKCHIP_DISPLAY_TYPE_EDP)
+
+static const uint32_t formats[] = {
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_ARGB8888,
+};
+
+struct rockchip_plane {
+	int zpos;
+	struct drm_plane base;
+};
+
+struct lcdc_context {
+	struct device *dev;
+	struct drm_device *drm_dev;
+	struct drm_crtc crtc;
+	struct drm_pending_vblank_event *event;
+	struct drm_display_mode mode;
+	struct drm_plane *plane;
+	struct lcdc_driver *drv;
+	unsigned int default_win;
+	unsigned int dpms;
+	int pipe;
+	wait_queue_head_t wait_vsync_queue;
+	atomic_t wait_vsync_event;
+};
+
+#define to_lcdc_data(x) ((x)->drv->data)
+#define to_lcdc_ctx(x) container_of(x, struct lcdc_context, crtc)
+#define to_rockchip_plane(x) container_of(x, struct rockchip_plane, base)
+
+const struct of_device_id lcdc_driver_dt_match[] = {
+#ifdef CONFIG_LCDC_RK3288
+	{ .compatible = "rockchip,rk3288-lcdc",
+	  .data = (void *)&rockchip_rk3288_lcdc },
+#endif
+	{},
+};
+
+static inline struct lcdc_driver_data *drm_lcdc_get_driver_data(
+	struct platform_device *pdev)
+{
+	const struct of_device_id *of_id =
+			of_match_device(lcdc_driver_dt_match, &pdev->dev);
+
+	return (struct lcdc_driver_data *)of_id->data;
+}
+
+static int rockchip_plane_get_size(int start, unsigned length, unsigned last)
+{
+	int end = start + length;
+	int size = 0;
+
+	if (start <= 0) {
+		if (end > 0)
+			size = min_t(unsigned, end, last);
+	} else if (start <= last) {
+		size = min_t(unsigned, last - start, length);
+	}
+
+	return size;
+}
+
+static int rockchip_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
+				 struct drm_framebuffer *fb, int crtc_x,
+				 int crtc_y, unsigned int crtc_w,
+				 unsigned int crtc_h, uint32_t src_x,
+				 uint32_t src_y, uint32_t src_w, uint32_t src_h)
+{
+	struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
+	struct lcdc_context *ctx = to_lcdc_ctx(crtc);
+	struct lcdc_driver_data *lcdc_data = to_lcdc_data(ctx);
+	struct drm_gem_cma_object *gem;
+	struct lcdc_win_data *win_data;
+	unsigned long offset;
+	unsigned int actual_w;
+	unsigned int actual_h;
+	int win;
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	gem = drm_fb_cma_get_gem_obj(fb, 0);
+	if (!gem) {
+		DRM_ERROR("fail to get cma object from framebuffer\n");
+		return -EINVAL;
+	}
+
+	actual_w = rockchip_plane_get_size(crtc_x,
+					   crtc_w, crtc->mode.hdisplay);
+	actual_h = rockchip_plane_get_size(crtc_y,
+					   crtc_h, crtc->mode.vdisplay);
+	if (crtc_x < 0) {
+		if (actual_w)
+			src_x -= crtc_x;
+		crtc_x = 0;
+	}
+
+	if (crtc_y < 0) {
+		if (actual_h)
+			src_y -= crtc_y;
+		crtc_y = 0;
+	}
+
+	win = rockchip_plane->zpos;
+	if (win == DEFAULT_ZPOS)
+		win = ctx->default_win;
+
+	if (win < 0 || win >= ZPOS_MAX_NUM)
+		return -EINVAL;
+
+	offset = (src_x >> 16) * (fb->bits_per_pixel >> 3);
+	offset += (src_y >> 16) * fb->pitches[0];
+
+	DRM_DEBUG_KMS("offset = 0x%lx, pitch = %x\n", offset, fb->pitches[0]);
+
+	win_data = lcdc_data->get_win(ctx->drv, win);
+
+	win_data->xpos = crtc_x;
+	win_data->ypos = crtc_y;
+	win_data->xsize = actual_w;
+	win_data->ysize = actual_h;
+	win_data->xact = fb->width;
+	win_data->yact = fb->height;
+	win_data->y_vir_stride = fb->pitches[0] / (fb->bits_per_pixel >> 3);
+	win_data->yrgb_addr = gem->paddr + offset;
+	win_data->uv_addr = 0;
+	win_data->alpha_en = false;
+
+	switch (fb->pixel_format) {
+	case DRM_FORMAT_ARGB8888:
+		win_data->alpha_en = true;
+	case DRM_FORMAT_XRGB8888:
+		win_data->format = ARGB888;
+		break;
+	case DRM_FORMAT_RGB565:
+		win_data->format = RGB565;
+		win_data->y_vir_stride =
+			((win_data->y_vir_stride * 3) >> 2)
+			+ win_data->y_vir_stride % 3;
+		break;
+	default:
+		DRM_DEBUG_KMS("invalid pixel size so using unpacked 24bpp.\n");
+		win_data->alpha_en = false;
+		win_data->format = ARGB888;
+		break;
+	}
+	win_data->enabled = true;
+	DRM_DEBUG_KMS("offset_x = %d, offset_y = %d\n",
+		      win_data->xpos, win_data->ypos);
+	DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
+		      win_data->xsize, win_data->ysize);
+	DRM_DEBUG_KMS("paddr = 0x%lx\n", (unsigned long)win_data->yrgb_addr);
+	DRM_DEBUG_KMS("fb_width = %d, actual_w = %d\n",
+		      fb->width, actual_w);
+
+	lcdc_data->win_commit(ctx->drv, win_data);
+	return 0;
+}
+
+static int rockchip_disable_plane(struct drm_plane *plane)
+{
+	struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
+	struct lcdc_context *ctx = to_lcdc_ctx(plane->crtc);
+	struct lcdc_driver_data *lcdc_data = to_lcdc_data(ctx);
+	struct lcdc_win_data *win_data;
+	int win = rockchip_plane->zpos;
+
+	if (win == DEFAULT_ZPOS)
+		win = ctx->default_win;
+
+	if (win < 0 || win >= ZPOS_MAX_NUM)
+		return -EINVAL;
+
+	win_data = lcdc_data->get_win(ctx->drv, win);
+
+	win_data->enabled = false;
+	lcdc_data->win_commit(ctx->drv, win_data);
+
+	return 0;
+}
+
+static void rockchip_plane_destroy(struct drm_plane *plane)
+{
+	struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	rockchip_disable_plane(plane);
+	drm_plane_cleanup(plane);
+	kfree(rockchip_plane);
+}
+
+static int rockchip_plane_set_property(struct drm_plane *plane,
+				       struct drm_property *property,
+				       uint64_t val)
+{
+	struct drm_device *dev = plane->dev;
+	struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
+	struct rockchip_drm_private *dev_priv = dev->dev_private;
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	if (property == dev_priv->plane_zpos_property) {
+		rockchip_plane->zpos = val;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static struct drm_plane_funcs rockchip_plane_funcs = {
+	.update_plane = rockchip_update_plane,
+	.disable_plane = rockchip_disable_plane,
+	.destroy = rockchip_plane_destroy,
+	.set_property = rockchip_plane_set_property,
+};
+
+static void rockchip_plane_attach_zpos_property(struct drm_plane *plane)
+{
+	struct drm_device *dev = plane->dev;
+	struct rockchip_drm_private *dev_priv = dev->dev_private;
+	struct drm_property *prop;
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	prop = dev_priv->plane_zpos_property;
+	if (!prop) {
+		prop = drm_property_create_range(dev, 0, "zpos", 0,
+						 MAX_PLANE - 1);
+		if (!prop)
+			return;
+
+		dev_priv->plane_zpos_property = prop;
+	}
+
+	drm_object_attach_property(&plane->base, prop, 0);
+}
+
+struct drm_plane *rockchip_plane_init(struct drm_device *dev,
+				      unsigned long possible_crtcs, bool priv)
+{
+	struct rockchip_plane *rockchip_plane;
+	struct rockchip_drm_private *private = dev->dev_private;
+	int err;
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	rockchip_plane = kzalloc(sizeof(*rockchip_plane), GFP_KERNEL);
+	if (!rockchip_plane)
+		return NULL;
+
+	err = drm_plane_init(dev, &rockchip_plane->base, possible_crtcs,
+			     &rockchip_plane_funcs, formats,
+			     ARRAY_SIZE(formats), priv);
+	if (err) {
+		DRM_ERROR("failed to initialize plane\n");
+		kfree(rockchip_plane);
+		return NULL;
+	}
+
+	if (priv) {
+		rockchip_plane->base.crtc = private->crtc[0];
+		rockchip_plane->zpos = DEFAULT_ZPOS;
+	} else {
+		rockchip_plane_attach_zpos_property(&rockchip_plane->base);
+	}
+
+	return &rockchip_plane->base;
+}
+
+int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, int pipe)
+{
+	struct rockchip_drm_private *private = dev->dev_private;
+	struct lcdc_context *ctx = to_lcdc_ctx(private->crtc[pipe]);
+	struct lcdc_driver_data *lcdc_data = to_lcdc_data(ctx);
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	if (ctx->dpms != DRM_MODE_DPMS_ON)
+		return -EPERM;
+
+	lcdc_data->enable_vblank(ctx->drv);
+
+	return 0;
+}
+
+void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, int pipe)
+{
+	struct rockchip_drm_private *private = dev->dev_private;
+	struct lcdc_context *ctx = to_lcdc_ctx(private->crtc[pipe]);
+	struct lcdc_driver_data *lcdc_data = to_lcdc_data(ctx);
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	if (ctx->dpms != DRM_MODE_DPMS_ON)
+		return;
+
+	lcdc_data->disable_vblank(ctx->drv);
+}
+
+static void rockchip_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+	struct lcdc_context *ctx = to_lcdc_ctx(crtc);
+	struct lcdc_driver_data *lcdc_data = to_lcdc_data(ctx);
+
+	DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode);
+
+	if (ctx->dpms == mode) {
+		DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
+		return;
+	}
+
+	if (mode > DRM_MODE_DPMS_ON) {
+		/* wait for the completion of page flip. */
+		if (!wait_event_timeout(ctx->wait_vsync_queue,
+					!atomic_read(&ctx->wait_vsync_event),
+					HZ/20))
+			DRM_DEBUG_KMS("vblank wait timed out.\n");
+		drm_vblank_off(crtc->dev, ctx->pipe);
+	}
+
+	switch (mode) {
+	case DRM_MODE_DPMS_ON:
+		lcdc_data->dpms(ctx->drv, DRM_MODE_DPMS_ON);
+		break;
+	case DRM_MODE_DPMS_STANDBY:
+	case DRM_MODE_DPMS_SUSPEND:
+	case DRM_MODE_DPMS_OFF:
+		lcdc_data->dpms(ctx->drv, DRM_MODE_DPMS_OFF);
+		break;
+	default:
+		DRM_DEBUG_KMS("unspecified mode %d\n", mode);
+		break;
+	}
+
+	ctx->dpms = mode;
+}
+
+static void rockchip_drm_crtc_prepare(struct drm_crtc *crtc)
+{
+	/* drm framework doesn't check NULL. */
+}
+
+static bool rockchip_drm_crtc_mode_fixup(struct drm_crtc *crtc,
+					 const struct drm_display_mode *mode,
+					 struct drm_display_mode *adjusted_mode)
+{
+	/* just do dummy now */
+
+	return true;
+}
+
+static int rockchip_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 *fb)
+{
+	struct lcdc_context *ctx = to_lcdc_ctx(crtc);
+	struct lcdc_driver_data *lcdc_data = to_lcdc_data(ctx);
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	/* nothing to do if we haven't set the mode yet */
+	if (adjusted_mode->htotal == 0 || adjusted_mode->vtotal == 0)
+		return -EINVAL;
+
+	drm_mode_copy(&ctx->mode, adjusted_mode);
+	lcdc_data->mode_set(ctx->drv, &ctx->mode);
+
+	return 0;
+}
+static int rockchip_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+					   struct drm_framebuffer *old_fb)
+{
+	struct lcdc_context *ctx = to_lcdc_ctx(crtc);
+	unsigned int crtc_w;
+	unsigned int crtc_h;
+	int ret;
+
+	crtc_w = crtc->primary->fb->width - crtc->x;
+	crtc_h = crtc->primary->fb->height - crtc->y;
+
+	ret = rockchip_update_plane(ctx->plane, crtc, crtc->primary->fb, 0, 0,
+				    crtc_w, crtc_h, crtc->x, crtc->y, crtc_w,
+				    crtc_h);
+	if (ret < 0) {
+		DRM_ERROR("fail to update plane\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void rockchip_drm_crtc_commit(struct drm_crtc *crtc)
+{
+	rockchip_drm_crtc_mode_set_base(crtc, crtc->x, crtc->y,
+					crtc->primary->fb);
+}
+
+static struct drm_crtc_helper_funcs rockchip_crtc_helper_funcs = {
+	.dpms = rockchip_drm_crtc_dpms,
+	.prepare = rockchip_drm_crtc_prepare,
+	.mode_fixup = rockchip_drm_crtc_mode_fixup,
+	.mode_set = rockchip_drm_crtc_mode_set,
+	.mode_set_base = rockchip_drm_crtc_mode_set_base,
+	.commit = rockchip_drm_crtc_commit,
+};
+
+static int rockchip_drm_crtc_page_flip(struct drm_crtc *crtc,
+				       struct drm_framebuffer *fb,
+				       struct drm_pending_vblank_event *event,
+				       uint32_t page_flip_flags)
+{
+	struct drm_device *dev = crtc->dev;
+	struct lcdc_context *ctx = to_lcdc_ctx(crtc);
+	struct drm_framebuffer *old_fb = crtc->primary->fb;
+	unsigned int crtc_w;
+	unsigned int crtc_h;
+	int ret = -EINVAL;
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	/* when the page flip is requested, crtc's dpms should be on */
+	if (ctx->dpms > DRM_MODE_DPMS_ON) {
+		DRM_ERROR("failed page flip request.\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&dev->struct_mutex);
+
+	/*
+	 * the pipe from user always is 0 so we can set pipe number
+	 * of current owner to event.
+	 */
+	ret = drm_vblank_get(dev, ctx->pipe);
+	if (ret) {
+		DRM_DEBUG("failed to acquire vblank counter\n");
+		goto out;
+	}
+
+	spin_lock_irq(&dev->event_lock);
+	if (ctx->event) {
+		spin_unlock_irq(&dev->event_lock);
+		DRM_ERROR("already pending flip!\n");
+		ret = -EBUSY;
+		goto out;
+	}
+	ctx->event = event;
+	atomic_set(&ctx->wait_vsync_event, 1);
+	spin_unlock_irq(&dev->event_lock);
+
+	crtc->primary->fb = fb;
+	crtc_w = crtc->primary->fb->width - crtc->x;
+	crtc_h = crtc->primary->fb->height - crtc->y;
+
+	ret = rockchip_update_plane(ctx->plane, crtc, fb, 0, 0, crtc_w, crtc_h,
+				    crtc->x, crtc->y, crtc_w, crtc_h);
+	if (ret) {
+		crtc->primary->fb = old_fb;
+
+		spin_lock_irq(&dev->event_lock);
+		drm_vblank_put(dev, ctx->pipe);
+		atomic_set(&ctx->wait_vsync_event, 0);
+		ctx->event = NULL;
+		spin_unlock_irq(&dev->event_lock);
+
+		goto out;
+	}
+out:
+	mutex_unlock(&dev->struct_mutex);
+	return ret;
+}
+
+void rockchip_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe)
+{
+	struct rockchip_drm_private *dev_priv = dev->dev_private;
+	struct drm_crtc *drm_crtc = dev_priv->crtc[pipe];
+	struct lcdc_context *ctx;
+	struct drm_pending_vblank_event *event;
+	unsigned long flags;
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	if (!drm_crtc)
+		return;
+
+	ctx = to_lcdc_ctx(drm_crtc);
+	event = ctx->event;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+
+	if (event) {
+		ctx->event = NULL;
+		drm_send_vblank_event(dev, -1, event);
+		drm_vblank_put(dev, pipe);
+		atomic_set(&ctx->wait_vsync_event, 0);
+		wake_up(&ctx->wait_vsync_queue);
+	}
+
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+void rockchip_drm_crtc_cancel_pending_flip(struct drm_device *dev)
+{
+	int i;
+
+	DRM_DEBUG_KMS("cancle pending flip\n");
+	for (i = 0; i < dev->num_crtcs; i++)
+		rockchip_drm_crtc_finish_pageflip(dev, i);
+}
+
+static void rockchip_drm_crtc_destroy(struct drm_crtc *crtc)
+{
+	struct lcdc_context *ctx = to_lcdc_ctx(crtc);
+	struct rockchip_drm_private *private = crtc->dev->dev_private;
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	private->crtc[ctx->pipe] = NULL;
+
+	drm_crtc_cleanup(crtc);
+}
+
+static struct drm_crtc_funcs rockchip_crtc_funcs = {
+	.set_config = drm_crtc_helper_set_config,
+	.page_flip = rockchip_drm_crtc_page_flip,
+	.destroy = rockchip_drm_crtc_destroy,
+};
+
+void lcdc_vsync_event_handler(struct device *dev)
+{
+	struct drm_pending_vblank_event *event;
+	struct lcdc_context *ctx = dev_get_drvdata(dev);
+	struct drm_device *drm_dev;
+	unsigned long flags;
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	/* check the crtc is detached already from encoder */
+	if (ctx && (ctx->pipe < 0 || !ctx->drm_dev))
+		return;
+
+	drm_handle_vblank(ctx->drm_dev, ctx->pipe);
+
+	event = ctx->event;
+	drm_dev = ctx->drm_dev;
+
+	spin_lock_irqsave(&drm_dev->event_lock, flags);
+
+	if (event) {
+		ctx->event = NULL;
+		drm_send_vblank_event(drm_dev, -1, event);
+		drm_vblank_put(drm_dev, ctx->pipe);
+		atomic_set(&ctx->wait_vsync_event, 0);
+		wake_up(&ctx->wait_vsync_queue);
+	}
+
+	spin_unlock_irqrestore(&drm_dev->event_lock, flags);
+}
+
+static int lcdc_bind(struct device *dev, struct device *master, void *data)
+{
+	struct drm_device *drm_dev = data;
+	struct rockchip_drm_private *private = drm_dev->dev_private;
+	struct lcdc_context *ctx = dev_get_drvdata(dev);
+	struct drm_crtc *crtc;
+
+	ctx->drm_dev = drm_dev;
+
+	ctx->pipe = rockchip_drm_pipe_get(dev);
+	ctx->dpms = DRM_MODE_DPMS_OFF;
+	crtc = &ctx->crtc;
+
+	private->crtc[ctx->pipe] = crtc;
+	ctx->plane = rockchip_plane_init(drm_dev, 1 << ctx->pipe, true);
+	drm_crtc_init(drm_dev, crtc, &rockchip_crtc_funcs);
+	drm_crtc_helper_add(crtc, &rockchip_crtc_helper_funcs);
+
+	/*
+	 * enable drm irq mode.
+	 * - with irq_enabled = true, we can use the vblank feature.
+	 *
+	 * P.S. note that we wouldn't use drm irq handler but
+	 *      just specific driver own one instead because
+	 *      drm framework supports only one irq handler.
+	 */
+	drm_dev->irq_enabled = true;
+
+	/*
+	 * with vblank_disable_allowed = true, vblank interrupt will be disabled
+	 * by drm timer once a current process gives up ownership of
+	 * vblank event.(after drm_vblank_put function is called)
+	 */
+	drm_dev->vblank_disable_allowed = true;
+
+	return 0;
+}
+
+static void lcdc_unbind(struct device *dev, struct device *master,
+			void *data)
+{	struct drm_device *drm_dev = data;
+	struct rockchip_drm_private *private = drm_dev->dev_private;
+	struct lcdc_context *ctx = dev_get_drvdata(dev);
+	struct drm_crtc *crtc = &ctx->crtc;
+
+	drm_crtc_cleanup(crtc);
+	private->crtc[ctx->pipe] = NULL;
+}
+
+static const struct component_ops lcdc_component_ops = {
+	.bind = lcdc_bind,
+	.unbind = lcdc_unbind,
+};
+
+static int lcdc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct lcdc_context *ctx;
+	struct lcdc_driver *lcdc_drv;
+	struct lcdc_driver_data *lcdc_data = drm_lcdc_get_driver_data(pdev);
+	int ret = -EINVAL;
+
+	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	if (!(lcdc_data->num_win && lcdc_data->init &&
+	      lcdc_data->deinit && lcdc_data->dpms &&
+	      lcdc_data->mode_set && lcdc_data->enable_vblank &&
+	      lcdc_data->disable_vblank &&
+	      lcdc_data->win_commit)) {
+		DRM_ERROR("lcdc driver ops is Incomplete\n");
+		return -EINVAL;
+	}
+	lcdc_drv = lcdc_data->init(pdev);
+	if (!lcdc_drv)
+		return -EINVAL;
+
+	lcdc_drv->data = lcdc_data;
+	ret = rockchip_drm_component_add(&pdev->dev, ROCKCHIP_DEVICE_TYPE_CRTC,
+					 ROCKCHIP_DISPLAY_TYPE_LCD, ctx);
+	if (ret)
+		goto err_deinit_lcdc;
+
+	ctx->dev = dev;
+	ctx->default_win = ZPOS_DEFAULT_WIN;
+
+	ctx->drv = lcdc_drv;
+
+	init_waitqueue_head(&ctx->wait_vsync_queue);
+	atomic_set(&ctx->wait_vsync_event, 0);
+
+	platform_set_drvdata(pdev, ctx);
+
+	pm_runtime_enable(&pdev->dev);
+
+	ret = component_add(&pdev->dev, &lcdc_component_ops);
+	if (ret)
+		goto err_disable_pm_runtime;
+
+	return ret;
+
+err_disable_pm_runtime:
+	pm_runtime_disable(dev);
+	rockchip_drm_component_del(dev, ROCKCHIP_DEVICE_TYPE_CRTC);
+err_deinit_lcdc:
+	lcdc_data->deinit(ctx->drv);
+	return ret;
+}
+
+static int lcdc_remove(struct platform_device *pdev)
+{
+	pm_runtime_disable(&pdev->dev);
+
+	component_del(&pdev->dev, &lcdc_component_ops);
+	rockchip_drm_component_del(&pdev->dev, ROCKCHIP_DEVICE_TYPE_CRTC);
+
+	return 0;
+}
+
+struct platform_driver rockchip_lcdc_platform_driver = {
+	.probe = lcdc_probe,
+	.remove = lcdc_remove,
+	.driver = {
+		.name = "rockchip-lcdc",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(lcdc_driver_dt_match),
+	},
+};
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_lcdc.h b/drivers/gpu/drm/rockchip/rockchip_drm_lcdc.h
new file mode 100644
index 0000000..0a0f9c2
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_lcdc.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:mark yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _ROCKCHIP_DRM_LCDC_H_
+#define _ROCKCHIP_DRM_LCDC_H_
+#include <linux/platform_device.h>
+#include <drm/drm_crtc.h>
+
+#include "rockchip_drm_drv.h"
+
+enum {
+	ZPOS_DEFAULT_WIN = 0,
+	ZPOS_CURSOR_WIN,
+	ZPOS_MAX_NUM,
+	ZPOS_UNUSED_WIN
+};
+
+enum data_format {
+	ARGB888 = 0,
+	RGB888,
+	RGB565,
+	YUV420 = 4,
+	YUV422,
+	YUV444,
+	XRGB888,
+	XBGR888,
+	ABGR888,
+	YUV420_A = 10,
+	YUV422_A,
+	YUV444_A
+};
+
+struct lcdc_win_data {
+	int zpos;
+	int id;
+	enum data_format format;
+	u32 xact;
+	u32 yact;
+	u32 xsize;
+	u32 ysize;
+	u32 xpos;
+	u32 ypos;
+	u32 y_vir_stride;
+	u32 uv_vir_stride;
+	bool alpha_en;
+	bool enabled;
+	bool resume;
+
+	dma_addr_t yrgb_addr;
+	dma_addr_t uv_addr;
+
+	int dsp_stx;
+	int dsp_sty;
+	/* win sel layer */
+	int z_order;
+	u8 fmt_cfg;
+	u8 fmt_10;
+	u8 swap_rb;
+	u32 reserved;
+	u32 area_num;
+	u32 scale_yrgb_x;
+	u32 scale_yrgb_y;
+	u32 scale_cbcr_x;
+	u32 scale_cbcr_y;
+	bool support_3d;
+
+	u8 win_lb_mode;
+
+	u8 bic_coe_el;
+	/* h 01:scale up ;10:down */
+	u8 yrgb_hor_scl_mode;
+	/* v 01:scale up ;10:down */
+	u8 yrgb_ver_scl_mode;
+	/* h scale down mode */
+	u8 yrgb_hsd_mode;
+	/* v scale up mode */
+	u8 yrgb_vsu_mode;
+	/* v scale down mode */
+	u8 yrgb_vsd_mode;
+	u8 cbr_hor_scl_mode;
+	u8 cbr_ver_scl_mode;
+	u8 cbr_hsd_mode;
+	u8 cbr_vsu_mode;
+	u8 cbr_vsd_mode;
+	u8 vsd_yrgb_gt4;
+	u8 vsd_yrgb_gt2;
+	u8 vsd_cbr_gt4;
+	u8 vsd_cbr_gt2;
+
+	u32 alpha_mode;
+	u32 g_alpha_val;
+	u32 color_key_val;
+};
+
+struct lcdc_driver_data {
+	int num_win;
+	struct lcdc_driver * (*init)(struct platform_device *pdev);
+	void (*deinit)(struct lcdc_driver *drv);
+	void (*dpms)(struct lcdc_driver *drv, int mode);
+	void (*mode_set)(struct lcdc_driver *drv,
+			 struct drm_display_mode *mode);
+	void (*enable_vblank)(struct lcdc_driver *drv);
+	void (*disable_vblank)(struct lcdc_driver *drv);
+	struct lcdc_win_data * (*get_win)(struct lcdc_driver *drv, int zpos);
+	void (*win_commit)(struct lcdc_driver *drv,
+			   struct lcdc_win_data *win);
+};
+
+struct lcdc_driver {
+	int id;
+
+	struct lcdc_driver_data *data;
+};
+
+
+void lcdc_vsync_event_handler(struct device *dev);
+#ifdef CONFIG_LCDC_RK3288
+extern struct lcdc_driver_data rockchip_rk3288_lcdc;
+#endif
+#endif /* _ROCKCHIP_DRM_LCDC_H_ */
diff --git a/include/uapi/drm/rockchip_drm.h b/include/uapi/drm/rockchip_drm.h
new file mode 100644
index 0000000..1b567f7
--- /dev/null
+++ b/include/uapi/drm/rockchip_drm.h
@@ -0,0 +1,110 @@
+/*
+ *
+ * Copyright (c) Fuzhou Rockchip Electronics Co.Ltd
+ * Authors:
+ *       mark yao <yzq@rock-chips.com>
+ *
+ * base on exynos_drm.h
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#ifndef _UAPI_ROCKCHIP_DRM_H_
+#define _UAPI_ROCKCHIP_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_rockchip_gem_create {
+	uint64_t size;
+	unsigned int flags;
+	unsigned int 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_rockchip_gem_map_off {
+	unsigned int handle;
+	unsigned int pad;
+	uint64_t offset;
+};
+
+/**
+ * A structure for mapping buffer.
+ *
+ * @handle: a handle to gem object created.
+ * @pad: just padding to be 64-bit aligned.
+ * @size: memory size to be mapped.
+ * @mapped: having user virtual address mmaped.
+ *      - this variable would be filled by rockchip gem module
+ *      of kernel side with user virtual address which is allocated
+ *      by do_mmap().
+ */
+struct drm_rockchip_gem_mmap {
+	unsigned int handle;
+	unsigned int pad;
+	uint64_t size;
+	uint64_t mapped;
+};
+
+/**
+ * A structure to gem information.
+ *
+ * @handle: a handle to gem object created.
+ * @flags: flag value including memory type and cache attribute and
+ *      this value would be set by driver.
+ * @size: size to memory region allocated by gem and this size would
+ *      be set by driver.
+ */
+struct drm_rockchip_gem_info {
+	unsigned int handle;
+	unsigned int flags;
+	uint64_t size;
+};
+
+/* memory type definitions. */
+enum e_drm_rockchip_gem_mem_type {
+	/* non-cachable mapping and used as default. */
+	ROCKCHIP_BO_NONCACHABLE = 0 << 0,
+	/* cachable mapping. */
+	ROCKCHIP_BO_CACHABLE = 1 << 0,
+	/* write-combine mapping. */
+	ROCKCHIP_BO_WC = 1 << 1,
+	ROCKCHIP_BO_MASK = ROCKCHIP_BO_CACHABLE | ROCKCHIP_BO_WC
+};
+
+#define DRM_ROCKCHIP_GEM_CREATE		0x00
+#define DRM_ROCKCHIP_GEM_MAP_OFFSET	0x01
+#define DRM_ROCKCHIP_GEM_MMAP		0x02
+/* Reserved 0x03 ~ 0x05 for rockchip specific gem ioctl */
+#define DRM_ROCKCHIP_GEM_GET		0x04
+
+#define DRM_IOCTL_ROCKCHIP_GEM_CREATE	DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_ROCKCHIP_GEM_CREATE, struct drm_rockchip_gem_create)
+
+#define DRM_IOCTL_ROCKCHIP_GEM_MAP_OFFSET	DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_ROCKCHIP_GEM_MAP_OFFSET, struct drm_rockchip_gem_map_off)
+
+#define DRM_IOCTL_ROCKCHIP_GEM_MMAP	DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_ROCKCHIP_GEM_MMAP, struct drm_rockchip_gem_mmap)
+
+#define DRM_IOCTL_ROCKCHIP_GEM_GET	DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_ROCKCHIP_GEM_GET, struct drm_rockchip_gem_info)
+#endif /* _UAPI_ROCKCHIP_DRM_H_ */
-- 
1.7.9.5



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

* [PATCH 2/9] Add devicetree bindings for panels used by the Rockchip DRM
@ 2014-08-04  4:47   ` mark yao
  0 siblings, 0 replies; 38+ messages in thread
From: mark yao @ 2014-08-04  4:47 UTC (permalink / raw)
  To: heiko, Rob Clark, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Randy Dunlap, David Airlie,
	Grant Likely, Greg Kroah-Hartman, John Stultz, Rom Lemarchand
  Cc: devicetree, linux-doc, linux-kernel, dri-devel, linux-api, olof,
	djkurtz, xjq, kfx, cym, cf, zyw, zwl, xxm, huangtao, kever.yang,
	zhangqing, yxj, wxt, xw, mark yao

Signed-off-by: mark yao <yzq@rock-chips.com>
---
 .../devicetree/bindings/video/rockchip-panel.txt   |   52 ++++++++++++++++++++
 1 file changed, 52 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/video/rockchip-panel.txt

diff --git a/Documentation/devicetree/bindings/video/rockchip-panel.txt b/Documentation/devicetree/bindings/video/rockchip-panel.txt
new file mode 100644
index 0000000..9fc200a
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/rockchip-panel.txt
@@ -0,0 +1,52 @@
+Rockchip LCD Display interface
+================================
+Required properties:
+-compatible: "rockchip,rockchip-panel";
+
+- rockchip,output-face: lcdc display mode value should set as following
+		"r8g8b8": 24bit display port, R8 G8 B8
+		"r6g6b6": 18bit display port, R6 G6 B6
+		"r5g6b5": 16bit display port, R5 G6 B5
+
+Optional properties:
+- display-timings: timings for the connected panel as described by
+	Documentation/devicetree/bindings/video/display-timing.txt
+Optional properties:
+- rockchip,power-gpios: gpio to power on or off the LCD (as many as needed)
+- lcdc-vcc18: Defined if power supply of lcd controller is 1.8v,
+		Not defined if power supply of lcd controller is 3.3v.
+- output-dither: support dithering the output color.
+- color-swap-rb: swap R and B color per pixel.
+- color-swap-rg: swap R and G color per pixel.
+- color-swap-bg: swap B and G color per pixel.
+
+Example:
+	panel {
+		compatible = "rockchip,rockchip-panel";
+		rockchip,output-face = "r6g6b6";
+
+		enable-gpios = <&gpio7 3 GPIO_ACTIVE_HIGH
+				&gpio7 4 GPIO_ACTIVE_HIGH>;
+		output-dither;
+
+		display-timings {
+			native-mode = <&timing_disp0>;
+
+			timing_disp0: 1280x800 {
+				clock-frequency = <71000000>;
+				hactive = <1280>;
+				vactive = <800>;
+				hback-porch = <100>;
+				hfront-porch = <18>;
+				vback-porch = <8>;
+				vfront-porch = <6>;
+				hsync-len = <10>;
+				vsync-len = <2>;
+				hsync-active = <0>;
+				vsync-active = <0>;
+				de-active = <0>;
+				pixelclk-active = <0>;
+			};
+		};
+
+	};
-- 
1.7.9.5



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

* [PATCH 2/9] Add devicetree bindings for panels used by the Rockchip DRM
@ 2014-08-04  4:47   ` mark yao
  0 siblings, 0 replies; 38+ messages in thread
From: mark yao @ 2014-08-04  4:47 UTC (permalink / raw)
  To: heiko-4mtYJXux2i+zQB+pC5nmwQ, Rob Clark, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, Randy Dunlap,
	David Airlie, Grant Likely, Greg Kroah-Hartman, John Stultz,
	Rom Lemarchand
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-api-u79uwXL29TY76Z2rM5mHXA, olof-nZhT3qVonbNeoWH0uzbU5w,
	djkurtz-F7+t8E8rja9g9hUCZPvPmw, xjq-TNX95d0MmH7DzftRWevZcw,
	kfx-TNX95d0MmH7DzftRWevZcw, cym-TNX95d0MmH7DzftRWevZcw,
	cf-TNX95d0MmH7DzftRWevZcw, zyw-TNX95d0MmH7DzftRWevZcw,
	zwl-TNX95d0MmH7DzftRWevZcw, xxm-TNX95d0MmH7DzftRWevZcw,
	huangtao-TNX95d0MmH7DzftRWevZcw,
	kever.yang-TNX95d0MmH7DzftRWevZcw,
	zhangqing-TNX95d0MmH7DzftRWevZcw, yxj-TNX95d0MmH7DzftRWevZcw,
	wxt-TNX95d0MmH7DzftRWevZcw, xw-TNX95d0MmH7DzftRWevZcw, mark yao

Signed-off-by: mark yao <yzq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
 .../devicetree/bindings/video/rockchip-panel.txt   |   52 ++++++++++++++++++++
 1 file changed, 52 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/video/rockchip-panel.txt

diff --git a/Documentation/devicetree/bindings/video/rockchip-panel.txt b/Documentation/devicetree/bindings/video/rockchip-panel.txt
new file mode 100644
index 0000000..9fc200a
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/rockchip-panel.txt
@@ -0,0 +1,52 @@
+Rockchip LCD Display interface
+================================
+Required properties:
+-compatible: "rockchip,rockchip-panel";
+
+- rockchip,output-face: lcdc display mode value should set as following
+		"r8g8b8": 24bit display port, R8 G8 B8
+		"r6g6b6": 18bit display port, R6 G6 B6
+		"r5g6b5": 16bit display port, R5 G6 B5
+
+Optional properties:
+- display-timings: timings for the connected panel as described by
+	Documentation/devicetree/bindings/video/display-timing.txt
+Optional properties:
+- rockchip,power-gpios: gpio to power on or off the LCD (as many as needed)
+- lcdc-vcc18: Defined if power supply of lcd controller is 1.8v,
+		Not defined if power supply of lcd controller is 3.3v.
+- output-dither: support dithering the output color.
+- color-swap-rb: swap R and B color per pixel.
+- color-swap-rg: swap R and G color per pixel.
+- color-swap-bg: swap B and G color per pixel.
+
+Example:
+	panel {
+		compatible = "rockchip,rockchip-panel";
+		rockchip,output-face = "r6g6b6";
+
+		enable-gpios = <&gpio7 3 GPIO_ACTIVE_HIGH
+				&gpio7 4 GPIO_ACTIVE_HIGH>;
+		output-dither;
+
+		display-timings {
+			native-mode = <&timing_disp0>;
+
+			timing_disp0: 1280x800 {
+				clock-frequency = <71000000>;
+				hactive = <1280>;
+				vactive = <800>;
+				hback-porch = <100>;
+				hfront-porch = <18>;
+				vback-porch = <8>;
+				vfront-porch = <6>;
+				hsync-len = <10>;
+				vsync-len = <2>;
+				hsync-active = <0>;
+				vsync-active = <0>;
+				de-active = <0>;
+				pixelclk-active = <0>;
+			};
+		};
+
+	};
-- 
1.7.9.5

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

* [PATCH 3/9] drm: add driver for panels used by the Rockchip DRM
@ 2014-08-04  4:48   ` mark yao
  0 siblings, 0 replies; 38+ messages in thread
From: mark yao @ 2014-08-04  4:48 UTC (permalink / raw)
  To: heiko, Rob Clark, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Randy Dunlap, David Airlie,
	Grant Likely, Greg Kroah-Hartman, John Stultz, Rom Lemarchand
  Cc: devicetree, linux-doc, linux-kernel, dri-devel, linux-api, olof,
	djkurtz, xjq, kfx, cym, cf, zyw, zwl, xxm, huangtao, kever.yang,
	zhangqing, yxj, wxt, xw, mark yao

Signed-off-by: mark yao <yzq@rock-chips.com>
---
 drivers/gpu/drm/rockchip/Makefile         |    3 +-
 drivers/gpu/drm/rockchip/rockchip_panel.c |  297 +++++++++++++++++++++++++++++
 2 files changed, 299 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_panel.c

diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index 45c9d50..a5e5132 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -5,7 +5,8 @@
 ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/rockchip
 
 rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_gem.o \
-		rockchip_drm_fb.o rockchip_drm_fbdev.o
+		rockchip_drm_fb.o rockchip_drm_fbdev.o \
+		rockchip_panel.o
 
 obj-$(CONFIG_DRM_ROCKCHIP_CONNECTOR) += rockchip_drm_connector.o
 obj-$(CONFIG_DRM_ROCKCHIP_LCDC) += rockchip_drm_lcdc.o
diff --git a/drivers/gpu/drm/rockchip/rockchip_panel.c b/drivers/gpu/drm/rockchip/rockchip_panel.c
new file mode 100644
index 0000000..87401a2
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_panel.c
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:mark yao <mark.yao@rock-chips.com>
+ *
+ * based on panel-simple.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <video/of_videomode.h>
+#include <video/videomode.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_panel.h>
+
+#include "rockchip_drm_drv.h"
+
+/* TODO: convert to gpiod_*() API once it's been merged */
+#define GPIO_ACTIVE_LOW	(1 << 0)
+
+struct pwr_gpio {
+	struct list_head head;
+	unsigned long enable_gpio_flags;
+	int enable_gpio;
+};
+
+struct rockchip_panel {
+	struct drm_panel base;
+	bool enabled;
+
+	struct drm_display_mode mode;
+	struct rockchip_panel_special priv;
+
+	struct list_head pwrlist;
+};
+
+static inline struct rockchip_panel *to_rockchip_panel(struct drm_panel *panel)
+{
+	return container_of(panel, struct rockchip_panel, base);
+}
+
+static int rockchip_panel_disable(struct drm_panel *panel)
+{
+	struct rockchip_panel *p = to_rockchip_panel(panel);
+	struct pwr_gpio *pwr;
+	struct list_head *pos;
+
+	if (!p->enabled)
+		return 0;
+
+	list_for_each(pos, &p->pwrlist) {
+		pwr = list_entry(pos, struct pwr_gpio, head);
+		if (gpio_is_valid(pwr->enable_gpio)) {
+			if (pwr->enable_gpio_flags & GPIO_ACTIVE_LOW)
+				gpio_set_value(pwr->enable_gpio, 1);
+			else
+				gpio_set_value(pwr->enable_gpio, 0);
+		}
+	}
+
+	p->enabled = false;
+
+	return 0;
+}
+
+static int rockchip_panel_enable(struct drm_panel *panel)
+{
+	struct rockchip_panel *p = to_rockchip_panel(panel);
+	struct pwr_gpio *pwr;
+	struct list_head *pos;
+
+	if (p->enabled)
+		return 0;
+
+	list_for_each(pos, &p->pwrlist) {
+		pwr = list_entry(pos, struct pwr_gpio, head);
+		if (gpio_is_valid(pwr->enable_gpio)) {
+			if (pwr->enable_gpio_flags & GPIO_ACTIVE_LOW)
+				gpio_set_value(pwr->enable_gpio, 0);
+			else
+				gpio_set_value(pwr->enable_gpio, 1);
+		}
+	}
+
+	p->enabled = true;
+
+	return 0;
+}
+
+static int rockchip_panel_get_modes(struct drm_panel *panel)
+{
+	struct rockchip_panel *p = to_rockchip_panel(panel);
+	struct drm_device *drm = panel->drm;
+	struct drm_connector *connector = panel->connector;
+	const struct drm_display_mode *m = &p->mode;
+	struct drm_display_mode *mode;
+
+	mode = drm_mode_duplicate(drm, m);
+	if (!mode) {
+		dev_err(drm->dev, "failed to add mode %ux%u@%u\n",
+			m->hdisplay, m->vdisplay, m->vrefresh);
+		return 0;
+	}
+
+	drm_mode_set_name(mode);
+	drm_mode_probed_add(connector, mode);
+
+	return 1;
+}
+
+static const struct drm_panel_funcs rockchip_panel_funcs = {
+	.disable = rockchip_panel_disable,
+	.enable = rockchip_panel_enable,
+	.get_modes = rockchip_panel_get_modes,
+};
+
+static int rockchip_name_to_face(const char *s)
+{
+	if (!s)
+		return 0;
+
+	if (strncmp(s, "r8g8b8", 6) == 0)
+		return ROCKCHIP_OUTFACE_P888;
+	else if (strncmp(s, "r6g6b6", 6) == 0)
+		return ROCKCHIP_OUTFACE_P666;
+	else if (strncmp(s, "r5g6b5", 6) == 0)
+		return ROCKCHIP_OUTFACE_P565;
+
+	DRM_ERROR("unsupport display output face[%s]\n", s);
+
+	return 0;
+}
+
+static int rockchip_panel_probe(struct platform_device *pdev)
+{
+	struct rockchip_panel *panel;
+	struct device *dev = &pdev->dev;
+	struct rockchip_panel_special *priv;
+	struct device_node *dn = dev->of_node;
+	struct device_node *np;
+	enum of_gpio_flags flags;
+	struct videomode vm;
+	const char *name;
+	struct pwr_gpio *pwr;
+	struct list_head *pos;
+	int value;
+	int err, i;
+	int num_gpio;
+
+	panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL);
+	if (!panel)
+		return -ENOMEM;
+
+	priv = &panel->priv;
+
+	INIT_LIST_HEAD(&panel->pwrlist);
+	num_gpio = of_gpio_named_count(dn, "enable-gpios");
+	for (i = 0; i < num_gpio; i++) {
+		pwr = kmalloc(sizeof(*pwr), GFP_KERNEL);
+		pwr->enable_gpio = of_get_named_gpio_flags(dn,
+							   "enable-gpios", i,
+							   &flags);
+		if (flags & OF_GPIO_ACTIVE_LOW)
+			pwr->enable_gpio_flags |= GPIO_ACTIVE_LOW;
+
+		if (gpio_is_valid(pwr->enable_gpio)) {
+			err = gpio_request(pwr->enable_gpio, NULL);
+			if (err < 0) {
+				dev_err(dev, "failed to request GPIO#%u: %d\n",
+					pwr->enable_gpio, err);
+				gpio_free(pwr->enable_gpio);
+				kfree(pwr);
+				continue;
+			}
+			value = (pwr->enable_gpio_flags & GPIO_ACTIVE_LOW) != 0;
+			err = gpio_direction_output(pwr->enable_gpio, value);
+			if (err < 0) {
+				dev_err(dev, "failed to setup GPIO%u: %d\n",
+					pwr->enable_gpio, err);
+				gpio_free(pwr->enable_gpio);
+				kfree(pwr);
+				continue;
+			}
+
+			list_add_tail(&pwr->head, &panel->pwrlist);
+		}
+	}
+
+	if (of_property_read_bool(dn, "color-swap-rb"))
+		priv->color_swap = ROCKCHIP_COLOR_SWAP_RB;
+
+	if (of_property_read_bool(dn, "color-swap-rg"))
+		priv->color_swap |= ROCKCHIP_COLOR_SWAP_RG;
+
+	if (of_property_read_bool(dn, "color-swap-gb"))
+		priv->color_swap |= ROCKCHIP_COLOR_SWAP_GB;
+
+	if (of_property_read_string(dn, "rockchip,output-face", &name))
+		/* default set it as RGB screen */
+		priv->out_face = ROCKCHIP_OUTFACE_P666;
+	else
+		priv->out_face = rockchip_name_to_face(name);
+
+	priv->pwr18 = of_property_read_bool(dn, "lcd-vcc18");
+	priv->dither = of_property_read_bool(dn, "output-dither");
+
+	np = of_get_child_by_name(dn, "display-timings");
+	if (!np) {
+		DRM_ERROR("can't find display timings\n");
+		return 0;
+	}
+
+	of_node_put(np);
+	memset(&vm, 0, sizeof(vm));
+
+	err = of_get_videomode(dn, &vm, 0);
+	if (err < 0)
+		return err;
+
+	drm_display_mode_from_videomode(&vm, &panel->mode);
+	panel->mode.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+
+	priv->flags = vm.flags;
+	panel->mode.private = (void *)priv;
+
+	drm_panel_init(&panel->base);
+	panel->base.dev = dev;
+	panel->base.funcs = &rockchip_panel_funcs;
+
+	err = drm_panel_add(&panel->base);
+	if (err < 0)
+		goto free_gpio;
+
+	dev_set_drvdata(dev, panel);
+
+	return 0;
+
+free_gpio:
+	list_for_each(pos, &panel->pwrlist) {
+		pwr = list_entry(pos, struct pwr_gpio, head);
+		if (gpio_is_valid(pwr->enable_gpio))
+			gpio_free(pwr->enable_gpio);
+		kfree(pwr);
+	}
+	return err;
+}
+
+static const struct of_device_id platform_of_match[] = {
+	{
+		.compatible = "rockchip,panel",
+	}, {
+		/* sentinel */
+	}
+};
+MODULE_DEVICE_TABLE(of, platform_of_match);
+
+static int rockchip_panel_remove(struct platform_device *pdev)
+{
+	struct rockchip_panel *panel = dev_get_drvdata(&pdev->dev);
+	struct pwr_gpio *pwr;
+	struct list_head *pos;
+
+	drm_panel_detach(&panel->base);
+	drm_panel_remove(&panel->base);
+
+	list_for_each(pos, &panel->pwrlist) {
+		pwr = list_entry(pos, struct pwr_gpio, head);
+		if (gpio_is_valid(pwr->enable_gpio))
+			gpio_free(pwr->enable_gpio);
+		kfree(pwr);
+	}
+
+	return 0;
+}
+
+struct platform_driver rockchip_panel_platform_driver = {
+	.driver = {
+		.name = "rockchip,panel",
+		.owner = THIS_MODULE,
+		.of_match_table = platform_of_match,
+	},
+	.probe = rockchip_panel_probe,
+	.remove = rockchip_panel_remove,
+};
-- 
1.7.9.5



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

* [PATCH 3/9] drm: add driver for panels used by the Rockchip DRM
@ 2014-08-04  4:48   ` mark yao
  0 siblings, 0 replies; 38+ messages in thread
From: mark yao @ 2014-08-04  4:48 UTC (permalink / raw)
  To: heiko-4mtYJXux2i+zQB+pC5nmwQ, Rob Clark, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, Randy Dunlap,
	David Airlie, Grant Likely, Greg Kroah-Hartman, John Stultz,
	Rom Lemarchand
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-api-u79uwXL29TY76Z2rM5mHXA, olof-nZhT3qVonbNeoWH0uzbU5w,
	djkurtz-F7+t8E8rja9g9hUCZPvPmw, xjq-TNX95d0MmH7DzftRWevZcw,
	kfx-TNX95d0MmH7DzftRWevZcw, cym-TNX95d0MmH7DzftRWevZcw,
	cf-TNX95d0MmH7DzftRWevZcw, zyw-TNX95d0MmH7DzftRWevZcw,
	zwl-TNX95d0MmH7DzftRWevZcw, xxm-TNX95d0MmH7DzftRWevZcw,
	huangtao-TNX95d0MmH7DzftRWevZcw,
	kever.yang-TNX95d0MmH7DzftRWevZcw,
	zhangqing-TNX95d0MmH7DzftRWevZcw, yxj-TNX95d0MmH7DzftRWevZcw,
	wxt-TNX95d0MmH7DzftRWevZcw, xw-TNX95d0MmH7DzftRWevZcw, mark yao

Signed-off-by: mark yao <yzq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
 drivers/gpu/drm/rockchip/Makefile         |    3 +-
 drivers/gpu/drm/rockchip/rockchip_panel.c |  297 +++++++++++++++++++++++++++++
 2 files changed, 299 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_panel.c

diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index 45c9d50..a5e5132 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -5,7 +5,8 @@
 ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/rockchip
 
 rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_gem.o \
-		rockchip_drm_fb.o rockchip_drm_fbdev.o
+		rockchip_drm_fb.o rockchip_drm_fbdev.o \
+		rockchip_panel.o
 
 obj-$(CONFIG_DRM_ROCKCHIP_CONNECTOR) += rockchip_drm_connector.o
 obj-$(CONFIG_DRM_ROCKCHIP_LCDC) += rockchip_drm_lcdc.o
diff --git a/drivers/gpu/drm/rockchip/rockchip_panel.c b/drivers/gpu/drm/rockchip/rockchip_panel.c
new file mode 100644
index 0000000..87401a2
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_panel.c
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:mark yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *
+ * based on panel-simple.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <video/of_videomode.h>
+#include <video/videomode.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_panel.h>
+
+#include "rockchip_drm_drv.h"
+
+/* TODO: convert to gpiod_*() API once it's been merged */
+#define GPIO_ACTIVE_LOW	(1 << 0)
+
+struct pwr_gpio {
+	struct list_head head;
+	unsigned long enable_gpio_flags;
+	int enable_gpio;
+};
+
+struct rockchip_panel {
+	struct drm_panel base;
+	bool enabled;
+
+	struct drm_display_mode mode;
+	struct rockchip_panel_special priv;
+
+	struct list_head pwrlist;
+};
+
+static inline struct rockchip_panel *to_rockchip_panel(struct drm_panel *panel)
+{
+	return container_of(panel, struct rockchip_panel, base);
+}
+
+static int rockchip_panel_disable(struct drm_panel *panel)
+{
+	struct rockchip_panel *p = to_rockchip_panel(panel);
+	struct pwr_gpio *pwr;
+	struct list_head *pos;
+
+	if (!p->enabled)
+		return 0;
+
+	list_for_each(pos, &p->pwrlist) {
+		pwr = list_entry(pos, struct pwr_gpio, head);
+		if (gpio_is_valid(pwr->enable_gpio)) {
+			if (pwr->enable_gpio_flags & GPIO_ACTIVE_LOW)
+				gpio_set_value(pwr->enable_gpio, 1);
+			else
+				gpio_set_value(pwr->enable_gpio, 0);
+		}
+	}
+
+	p->enabled = false;
+
+	return 0;
+}
+
+static int rockchip_panel_enable(struct drm_panel *panel)
+{
+	struct rockchip_panel *p = to_rockchip_panel(panel);
+	struct pwr_gpio *pwr;
+	struct list_head *pos;
+
+	if (p->enabled)
+		return 0;
+
+	list_for_each(pos, &p->pwrlist) {
+		pwr = list_entry(pos, struct pwr_gpio, head);
+		if (gpio_is_valid(pwr->enable_gpio)) {
+			if (pwr->enable_gpio_flags & GPIO_ACTIVE_LOW)
+				gpio_set_value(pwr->enable_gpio, 0);
+			else
+				gpio_set_value(pwr->enable_gpio, 1);
+		}
+	}
+
+	p->enabled = true;
+
+	return 0;
+}
+
+static int rockchip_panel_get_modes(struct drm_panel *panel)
+{
+	struct rockchip_panel *p = to_rockchip_panel(panel);
+	struct drm_device *drm = panel->drm;
+	struct drm_connector *connector = panel->connector;
+	const struct drm_display_mode *m = &p->mode;
+	struct drm_display_mode *mode;
+
+	mode = drm_mode_duplicate(drm, m);
+	if (!mode) {
+		dev_err(drm->dev, "failed to add mode %ux%u@%u\n",
+			m->hdisplay, m->vdisplay, m->vrefresh);
+		return 0;
+	}
+
+	drm_mode_set_name(mode);
+	drm_mode_probed_add(connector, mode);
+
+	return 1;
+}
+
+static const struct drm_panel_funcs rockchip_panel_funcs = {
+	.disable = rockchip_panel_disable,
+	.enable = rockchip_panel_enable,
+	.get_modes = rockchip_panel_get_modes,
+};
+
+static int rockchip_name_to_face(const char *s)
+{
+	if (!s)
+		return 0;
+
+	if (strncmp(s, "r8g8b8", 6) == 0)
+		return ROCKCHIP_OUTFACE_P888;
+	else if (strncmp(s, "r6g6b6", 6) == 0)
+		return ROCKCHIP_OUTFACE_P666;
+	else if (strncmp(s, "r5g6b5", 6) == 0)
+		return ROCKCHIP_OUTFACE_P565;
+
+	DRM_ERROR("unsupport display output face[%s]\n", s);
+
+	return 0;
+}
+
+static int rockchip_panel_probe(struct platform_device *pdev)
+{
+	struct rockchip_panel *panel;
+	struct device *dev = &pdev->dev;
+	struct rockchip_panel_special *priv;
+	struct device_node *dn = dev->of_node;
+	struct device_node *np;
+	enum of_gpio_flags flags;
+	struct videomode vm;
+	const char *name;
+	struct pwr_gpio *pwr;
+	struct list_head *pos;
+	int value;
+	int err, i;
+	int num_gpio;
+
+	panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL);
+	if (!panel)
+		return -ENOMEM;
+
+	priv = &panel->priv;
+
+	INIT_LIST_HEAD(&panel->pwrlist);
+	num_gpio = of_gpio_named_count(dn, "enable-gpios");
+	for (i = 0; i < num_gpio; i++) {
+		pwr = kmalloc(sizeof(*pwr), GFP_KERNEL);
+		pwr->enable_gpio = of_get_named_gpio_flags(dn,
+							   "enable-gpios", i,
+							   &flags);
+		if (flags & OF_GPIO_ACTIVE_LOW)
+			pwr->enable_gpio_flags |= GPIO_ACTIVE_LOW;
+
+		if (gpio_is_valid(pwr->enable_gpio)) {
+			err = gpio_request(pwr->enable_gpio, NULL);
+			if (err < 0) {
+				dev_err(dev, "failed to request GPIO#%u: %d\n",
+					pwr->enable_gpio, err);
+				gpio_free(pwr->enable_gpio);
+				kfree(pwr);
+				continue;
+			}
+			value = (pwr->enable_gpio_flags & GPIO_ACTIVE_LOW) != 0;
+			err = gpio_direction_output(pwr->enable_gpio, value);
+			if (err < 0) {
+				dev_err(dev, "failed to setup GPIO%u: %d\n",
+					pwr->enable_gpio, err);
+				gpio_free(pwr->enable_gpio);
+				kfree(pwr);
+				continue;
+			}
+
+			list_add_tail(&pwr->head, &panel->pwrlist);
+		}
+	}
+
+	if (of_property_read_bool(dn, "color-swap-rb"))
+		priv->color_swap = ROCKCHIP_COLOR_SWAP_RB;
+
+	if (of_property_read_bool(dn, "color-swap-rg"))
+		priv->color_swap |= ROCKCHIP_COLOR_SWAP_RG;
+
+	if (of_property_read_bool(dn, "color-swap-gb"))
+		priv->color_swap |= ROCKCHIP_COLOR_SWAP_GB;
+
+	if (of_property_read_string(dn, "rockchip,output-face", &name))
+		/* default set it as RGB screen */
+		priv->out_face = ROCKCHIP_OUTFACE_P666;
+	else
+		priv->out_face = rockchip_name_to_face(name);
+
+	priv->pwr18 = of_property_read_bool(dn, "lcd-vcc18");
+	priv->dither = of_property_read_bool(dn, "output-dither");
+
+	np = of_get_child_by_name(dn, "display-timings");
+	if (!np) {
+		DRM_ERROR("can't find display timings\n");
+		return 0;
+	}
+
+	of_node_put(np);
+	memset(&vm, 0, sizeof(vm));
+
+	err = of_get_videomode(dn, &vm, 0);
+	if (err < 0)
+		return err;
+
+	drm_display_mode_from_videomode(&vm, &panel->mode);
+	panel->mode.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+
+	priv->flags = vm.flags;
+	panel->mode.private = (void *)priv;
+
+	drm_panel_init(&panel->base);
+	panel->base.dev = dev;
+	panel->base.funcs = &rockchip_panel_funcs;
+
+	err = drm_panel_add(&panel->base);
+	if (err < 0)
+		goto free_gpio;
+
+	dev_set_drvdata(dev, panel);
+
+	return 0;
+
+free_gpio:
+	list_for_each(pos, &panel->pwrlist) {
+		pwr = list_entry(pos, struct pwr_gpio, head);
+		if (gpio_is_valid(pwr->enable_gpio))
+			gpio_free(pwr->enable_gpio);
+		kfree(pwr);
+	}
+	return err;
+}
+
+static const struct of_device_id platform_of_match[] = {
+	{
+		.compatible = "rockchip,panel",
+	}, {
+		/* sentinel */
+	}
+};
+MODULE_DEVICE_TABLE(of, platform_of_match);
+
+static int rockchip_panel_remove(struct platform_device *pdev)
+{
+	struct rockchip_panel *panel = dev_get_drvdata(&pdev->dev);
+	struct pwr_gpio *pwr;
+	struct list_head *pos;
+
+	drm_panel_detach(&panel->base);
+	drm_panel_remove(&panel->base);
+
+	list_for_each(pos, &panel->pwrlist) {
+		pwr = list_entry(pos, struct pwr_gpio, head);
+		if (gpio_is_valid(pwr->enable_gpio))
+			gpio_free(pwr->enable_gpio);
+		kfree(pwr);
+	}
+
+	return 0;
+}
+
+struct platform_driver rockchip_panel_platform_driver = {
+	.driver = {
+		.name = "rockchip,panel",
+		.owner = THIS_MODULE,
+		.of_match_table = platform_of_match,
+	},
+	.probe = rockchip_panel_probe,
+	.remove = rockchip_panel_remove,
+};
-- 
1.7.9.5

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

* [PATCH 4/9] Add devicetree bindings for Rockchip lcd controller
@ 2014-08-04  4:50   ` mark yao
  0 siblings, 0 replies; 38+ messages in thread
From: mark yao @ 2014-08-04  4:50 UTC (permalink / raw)
  To: heiko, Rob Clark, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Randy Dunlap, David Airlie,
	Grant Likely, Greg Kroah-Hartman, John Stultz, Rom Lemarchand
  Cc: devicetree, linux-doc, linux-kernel, dri-devel, linux-api, olof,
	djkurtz, xjq, kfx, cym, cf, zyw, zwl, xxm, huangtao, kever.yang,
	zhangqing, yxj, wxt, xw, mark yao

Signed-off-by: mark yao <yzq@rock-chips.com>
---
 .../devicetree/bindings/video/rockchip-lcdc.txt    |   33 ++++++++++++++++++++
 1 file changed, 33 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/video/rockchip-lcdc.txt

diff --git a/Documentation/devicetree/bindings/video/rockchip-lcdc.txt b/Documentation/devicetree/bindings/video/rockchip-lcdc.txt
new file mode 100644
index 0000000..7fb2b73
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/rockchip-lcdc.txt
@@ -0,0 +1,33 @@
+Device-Tree bindings for Rockchip SoC display controller (LCDC)
+
+LCDC (LCD Controller) is the Display Controller for the Rockchip
+series of SoCs which transfers the image data from a video memory
+buffer to an external LCD interface.
+
+Required properties:
+- compatible: value should be one of the following
+		"rockchip,rk3066-lcdc";
+		"rockchip,rk3188-lcdc";
+		"rockchip,rk3288-lcdc";
+
+- interrupts: should contain a list of all LCDC IP block interrupts in the
+		 order: VSYNC, LCD_SYSTEM. The interrupt specifier
+		 format depends on the interrupt controller used.
+
+- clocks: must include clock specifiers corresponding to entries in the
+		clock-names property.
+
+- clock-names: Must contain
+		aclk_lcdc: for ddr buffer transfer.
+		hclk_lcdc: for ahb bus to R/W the phy regs.
+		dclk_lcdc: pixel clock.
+
+Example:
+SoC specific DT entry:
+	lcdc1: lcdc@ff940000 {
+		compatible = "rockchip,rk3288-lcdc";
+		reg = <0xff940000 0x19c>;
+		interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru ACLK_LCDC1>, <&cru DCLK_LCDC1>, <&cru HCLK_LCDC1>;
+		clock-names = "aclk_lcdc", "dclk_lcdc", "hclk_lcdc";
+	};
-- 
1.7.9.5



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

* [PATCH 4/9] Add devicetree bindings for Rockchip lcd controller
@ 2014-08-04  4:50   ` mark yao
  0 siblings, 0 replies; 38+ messages in thread
From: mark yao @ 2014-08-04  4:50 UTC (permalink / raw)
  To: heiko-4mtYJXux2i+zQB+pC5nmwQ, Rob Clark, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, Randy Dunlap,
	David Airlie, Grant Likely, Greg Kroah-Hartman, John Stultz,
	Rom Lemarchand
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-api-u79uwXL29TY76Z2rM5mHXA, olof-nZhT3qVonbNeoWH0uzbU5w,
	djkurtz-F7+t8E8rja9g9hUCZPvPmw, xjq-TNX95d0MmH7DzftRWevZcw,
	kfx-TNX95d0MmH7DzftRWevZcw, cym-TNX95d0MmH7DzftRWevZcw,
	cf-TNX95d0MmH7DzftRWevZcw, zyw-TNX95d0MmH7DzftRWevZcw,
	zwl-TNX95d0MmH7DzftRWevZcw, xxm-TNX95d0MmH7DzftRWevZcw,
	huangtao-TNX95d0MmH7DzftRWevZcw,
	kever.yang-TNX95d0MmH7DzftRWevZcw,
	zhangqing-TNX95d0MmH7DzftRWevZcw, yxj-TNX95d0MmH7DzftRWevZcw,
	wxt-TNX95d0MmH7DzftRWevZcw, xw-TNX95d0MmH7DzftRWevZcw, mark yao

Signed-off-by: mark yao <yzq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
 .../devicetree/bindings/video/rockchip-lcdc.txt    |   33 ++++++++++++++++++++
 1 file changed, 33 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/video/rockchip-lcdc.txt

diff --git a/Documentation/devicetree/bindings/video/rockchip-lcdc.txt b/Documentation/devicetree/bindings/video/rockchip-lcdc.txt
new file mode 100644
index 0000000..7fb2b73
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/rockchip-lcdc.txt
@@ -0,0 +1,33 @@
+Device-Tree bindings for Rockchip SoC display controller (LCDC)
+
+LCDC (LCD Controller) is the Display Controller for the Rockchip
+series of SoCs which transfers the image data from a video memory
+buffer to an external LCD interface.
+
+Required properties:
+- compatible: value should be one of the following
+		"rockchip,rk3066-lcdc";
+		"rockchip,rk3188-lcdc";
+		"rockchip,rk3288-lcdc";
+
+- interrupts: should contain a list of all LCDC IP block interrupts in the
+		 order: VSYNC, LCD_SYSTEM. The interrupt specifier
+		 format depends on the interrupt controller used.
+
+- clocks: must include clock specifiers corresponding to entries in the
+		clock-names property.
+
+- clock-names: Must contain
+		aclk_lcdc: for ddr buffer transfer.
+		hclk_lcdc: for ahb bus to R/W the phy regs.
+		dclk_lcdc: pixel clock.
+
+Example:
+SoC specific DT entry:
+	lcdc1: lcdc@ff940000 {
+		compatible = "rockchip,rk3288-lcdc";
+		reg = <0xff940000 0x19c>;
+		interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru ACLK_LCDC1>, <&cru DCLK_LCDC1>, <&cru HCLK_LCDC1>;
+		clock-names = "aclk_lcdc", "dclk_lcdc", "hclk_lcdc";
+	};
-- 
1.7.9.5

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

* [PATCH 5/9] drm: add Rockchip rk3288 lcd controller driver
@ 2014-08-04  4:51   ` mark yao
  0 siblings, 0 replies; 38+ messages in thread
From: mark yao @ 2014-08-04  4:51 UTC (permalink / raw)
  To: heiko, Rob Clark, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Randy Dunlap, David Airlie,
	Grant Likely, Greg Kroah-Hartman, John Stultz, Rom Lemarchand
  Cc: devicetree, linux-doc, linux-kernel, dri-devel, linux-api, olof,
	djkurtz, xjq, kfx, cym, cf, zyw, zwl, xxm, huangtao, kever.yang,
	zhangqing, yxj, wxt, xw, mark yao

Signed-off-by: mark yao <yzq@rock-chips.com>
---
 drivers/gpu/drm/rockchip/Kconfig            |    2 +
 drivers/gpu/drm/rockchip/Makefile           |    2 +-
 drivers/gpu/drm/rockchip/lcdc/Kconfig       |    9 +
 drivers/gpu/drm/rockchip/lcdc/Makefile      |    4 +
 drivers/gpu/drm/rockchip/lcdc/rk3288_lcdc.c |  819 ++++++++++++++++++
 drivers/gpu/drm/rockchip/lcdc/rk3288_lcdc.h | 1202 +++++++++++++++++++++++++++
 6 files changed, 2037 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/rockchip/lcdc/Kconfig
 create mode 100644 drivers/gpu/drm/rockchip/lcdc/Makefile
 create mode 100644 drivers/gpu/drm/rockchip/lcdc/rk3288_lcdc.c
 create mode 100644 drivers/gpu/drm/rockchip/lcdc/rk3288_lcdc.h

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 592e999..ccce827 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -38,3 +38,5 @@ config DRM_ROCKCHIP_CONNECTOR
 	  Choose this option if you want to use Rockchip Primary DISPLAY.
 	  The driver provides an abstraction for Rockchip display devices,
 	  such as lcd plane, lvds, edp , mipi, etc.
+
+source "drivers/gpu/drm/rockchip/lcdc/Kconfig"
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index a5e5132..6d49edc 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -9,5 +9,5 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_gem.o \
 		rockchip_panel.o
 
 obj-$(CONFIG_DRM_ROCKCHIP_CONNECTOR) += rockchip_drm_connector.o
-obj-$(CONFIG_DRM_ROCKCHIP_LCDC) += rockchip_drm_lcdc.o
+obj-$(CONFIG_DRM_ROCKCHIP_LCDC) += rockchip_drm_lcdc.o lcdc/
 obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o
diff --git a/drivers/gpu/drm/rockchip/lcdc/Kconfig b/drivers/gpu/drm/rockchip/lcdc/Kconfig
new file mode 100644
index 0000000..2b94057
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/lcdc/Kconfig
@@ -0,0 +1,9 @@
+config LCDC_RK3288
+        bool "rk3288 lcdc support"
+        depends on DRM_ROCKCHIP_LCDC
+        help
+	  Choose this option if you have a rk3288 lcd controller.
+	  rk3288 lcdc is the display interface from memory frame buffer
+	  to display device. There are two lcd controllers on rk3288,
+	  They have same regs setting, can use same drivers. We use the
+	  lcdc id distinguish between them
diff --git a/drivers/gpu/drm/rockchip/lcdc/Makefile b/drivers/gpu/drm/rockchip/lcdc/Makefile
new file mode 100644
index 0000000..943dcd6
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/lcdc/Makefile
@@ -0,0 +1,4 @@
+#
+# Makefile for the lcd control device driver.
+
+obj-$(CONFIG_LCDC_RK3288) += rk3288_lcdc.o
diff --git a/drivers/gpu/drm/rockchip/lcdc/rk3288_lcdc.c b/drivers/gpu/drm/rockchip/lcdc/rk3288_lcdc.c
new file mode 100644
index 0000000..f1b016c
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/lcdc/rk3288_lcdc.c
@@ -0,0 +1,819 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:
+ *      hjc <hjc@rock-chips.com>
+ *      mark yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <video/display_timing.h>
+#include <drm/rockchip_drm.h>
+#include <drm/drm_crtc.h>
+
+#ifdef CONFIG_OF
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#endif
+
+#include "../rockchip_drm_lcdc.h"
+#include "rk3288_lcdc.h"
+
+#define RK3288_LCDC_BIG_BASE 0xff930000
+#define RK3288_LCDC_LIT_BASE 0xff940000
+
+static struct lcdc_win_data lcdc_win[] = {
+	[0] = {
+		.id = 0,
+		.zpos = ZPOS_DEFAULT_WIN,
+		.fmt_10 = 0,
+		.win_lb_mode = 0x4,
+		.swap_rb = 0,
+	},
+	[1] = {
+		.id = 1,
+		.zpos = ZPOS_UNUSED_WIN,
+		.fmt_10 = 0,
+		.win_lb_mode = 0x4,
+		.swap_rb = 0,
+	},
+	[2] = {
+		.id = 2,
+		.zpos = ZPOS_CURSOR_WIN,
+		.fmt_10 = 0,
+		.win_lb_mode = 0x4,
+		.swap_rb = 0,
+	},
+	[3] = {
+		.id = 3,
+		.zpos = ZPOS_UNUSED_WIN,
+	},
+};
+
+static void rk3288_lcdc_win_commit(struct lcdc_driver *lcdc_drv,
+				   struct lcdc_win_data *win);
+static void rk3288_lcdc_dpms(struct lcdc_driver *lcdc_drv, int mode);
+
+static int rk3288_lcdc_get_id(u32 phy_base)
+{
+	if (phy_base == RK3288_LCDC_BIG_BASE)
+		return 0;
+	else if (phy_base == RK3288_LCDC_LIT_BASE)
+		return 1;
+	else
+		return -EINVAL;
+}
+
+static int rk3288_lcdc_clk_enable(struct lcdc_device *lcdc_dev)
+{
+	int ret = 0;
+
+	if (!lcdc_dev->clk_on) {
+		ret = clk_prepare_enable(lcdc_dev->hclk);
+		if (ret < 0) {
+			dev_err(lcdc_dev->dev, "failed to enable hclk\n");
+			return ret;
+		}
+
+		ret = clk_prepare_enable(lcdc_dev->dclk);
+		if (ret < 0) {
+			dev_err(lcdc_dev->dev, "failed to enable dclk\n");
+			goto err_dclk;
+		}
+
+		clk_prepare_enable(lcdc_dev->aclk);
+		if (ret < 0) {
+			dev_err(lcdc_dev->dev, "failed to enable aclk\n");
+			goto err_aclk;
+		}
+
+		spin_lock(&lcdc_dev->reg_lock);
+		lcdc_dev->clk_on = 1;
+		spin_unlock(&lcdc_dev->reg_lock);
+	}
+
+	return ret;
+err_aclk:
+	clk_disable_unprepare(lcdc_dev->aclk);
+err_dclk:
+	clk_disable_unprepare(lcdc_dev->hclk);
+	return ret;
+}
+
+static void rk3288_lcdc_clk_disable(struct lcdc_device *lcdc_dev)
+{
+	if (lcdc_dev->clk_on) {
+		spin_lock(&lcdc_dev->reg_lock);
+		lcdc_dev->clk_on = 0;
+		spin_unlock(&lcdc_dev->reg_lock);
+		mdelay(25);
+		clk_disable_unprepare(lcdc_dev->dclk);
+		clk_disable_unprepare(lcdc_dev->hclk);
+		clk_disable_unprepare(lcdc_dev->aclk);
+	}
+}
+
+static void rk3288_lcdc_disable_irq(struct lcdc_device *lcdc_dev)
+{
+	u32 mask, val;
+
+	if (likely(lcdc_dev->clk_on)) {
+		spin_lock(&lcdc_dev->reg_lock);
+		mask = M_DSP_HOLD_VALID_INTR_EN | M_FS_INTR_EN |
+			M_LINE_FLAG_INTR_EN | M_BUS_ERROR_INTR_EN;
+		val = V_DSP_HOLD_VALID_INTR_EN(0) | V_FS_INTR_EN(0) |
+			V_LINE_FLAG_INTR_EN(0) | V_BUS_ERROR_INTR_EN(0);
+		lcdc_msk_reg(lcdc_dev, INTR_CTRL0, mask, val);
+
+		mask = M_DSP_HOLD_VALID_INTR_CLR | M_FS_INTR_CLR |
+			M_LINE_FLAG_INTR_CLR | M_LINE_FLAG_INTR_CLR;
+		val = V_DSP_HOLD_VALID_INTR_CLR(0) | V_FS_INTR_CLR(0) |
+			V_LINE_FLAG_INTR_CLR(0) | V_BUS_ERROR_INTR_CLR(0);
+		lcdc_msk_reg(lcdc_dev, INTR_CTRL0, mask, val);
+
+		mask = M_WIN0_EMPTY_INTR_EN | M_WIN1_EMPTY_INTR_EN |
+			M_WIN2_EMPTY_INTR_EN | M_WIN3_EMPTY_INTR_EN |
+			M_HWC_EMPTY_INTR_EN | M_POST_BUF_EMPTY_INTR_EN |
+			M_POST_BUF_EMPTY_INTR_EN;
+		val = V_WIN0_EMPTY_INTR_EN(0) | V_WIN1_EMPTY_INTR_EN(0) |
+			V_WIN2_EMPTY_INTR_EN(0) | V_WIN3_EMPTY_INTR_EN(0) |
+			V_HWC_EMPTY_INTR_EN(0) | V_POST_BUF_EMPTY_INTR_EN(0) |
+			V_PWM_GEN_INTR_EN(0);
+		lcdc_msk_reg(lcdc_dev, INTR_CTRL1, mask, val);
+
+		mask = M_WIN0_EMPTY_INTR_CLR | M_WIN1_EMPTY_INTR_CLR |
+			M_WIN2_EMPTY_INTR_CLR | M_WIN3_EMPTY_INTR_CLR |
+			M_HWC_EMPTY_INTR_CLR | M_POST_BUF_EMPTY_INTR_CLR |
+			M_POST_BUF_EMPTY_INTR_CLR;
+		val = V_WIN0_EMPTY_INTR_CLR(0) | V_WIN1_EMPTY_INTR_CLR(0) |
+			V_WIN2_EMPTY_INTR_CLR(0) | V_WIN3_EMPTY_INTR_CLR(0) |
+			V_HWC_EMPTY_INTR_CLR(0) |
+			V_POST_BUF_EMPTY_INTR_CLR(0) |
+			V_PWM_GEN_INTR_CLR(0);
+		lcdc_msk_reg(lcdc_dev, INTR_CTRL1, mask, val);
+		lcdc_cfg_done(lcdc_dev);
+		spin_unlock(&lcdc_dev->reg_lock);
+	}
+}
+
+static void rk3288_lcdc_enable_irq(struct lcdc_device *lcdc_dev)
+{
+	u32 mask, val;
+
+	if (likely(lcdc_dev->clk_on)) {
+		spin_lock(&lcdc_dev->reg_lock);
+		mask = M_FS_INTR_CLR | M_FS_INTR_EN;
+		val = V_FS_INTR_CLR(1) | V_FS_INTR_EN(1);
+		lcdc_msk_reg(lcdc_dev, INTR_CTRL0, mask, val);
+		spin_unlock(&lcdc_dev->reg_lock);
+	}
+}
+
+static irqreturn_t rk3288_lcdc_isr(int irq, void *dev_id)
+{
+	struct lcdc_device *lcdc_dev =
+			(struct lcdc_device *)dev_id;
+	u32 intr0_reg;
+
+	intr0_reg = lcdc_readl(lcdc_dev, INTR_CTRL0);
+	if (intr0_reg & M_FS_INTR_STS) {
+		lcdc_msk_reg(lcdc_dev, INTR_CTRL0, M_FS_INTR_CLR,
+			     V_FS_INTR_CLR(1));
+		lcdc_vsync_event_handler(lcdc_dev->dev);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void rk3288_lcdc_alpha_cfg(struct lcdc_device *lcdc_dev,
+				  struct lcdc_win_data *layer)
+{
+	struct alpha_config alpha_config;
+
+	u32 mask, val;
+	int ppixel_alpha, global_alpha;
+	u32 src_alpha_ctl, dst_alpha_ctl;
+
+	ppixel_alpha = ((layer->format == ARGB888) ||
+			(layer->format == ABGR888)) ? 1 : 0;
+	global_alpha = (layer->g_alpha_val == 0) ? 0 : 1;
+	alpha_config.src_global_alpha_val = layer->g_alpha_val;
+	layer->alpha_mode = AB_SRC_OVER;
+	switch (layer->alpha_mode) {
+	case AB_USER_DEFINE:
+		break;
+	case AB_CLEAR:
+		alpha_config.src_factor_mode = AA_ZERO;
+		alpha_config.dst_factor_mode = AA_ZERO;
+		break;
+	case AB_SRC:
+		alpha_config.src_factor_mode = AA_ONE;
+		alpha_config.dst_factor_mode = AA_ZERO;
+		break;
+	case AB_DST:
+		alpha_config.src_factor_mode = AA_ZERO;
+		alpha_config.dst_factor_mode = AA_ONE;
+		break;
+	case AB_SRC_OVER:
+		alpha_config.src_color_mode = AA_SRC_PRE_MUL;
+		if (global_alpha)
+			alpha_config.src_factor_mode = AA_SRC_GLOBAL;
+		else
+			alpha_config.src_factor_mode = AA_ONE;
+		alpha_config.dst_factor_mode = AA_SRC_INVERSE;
+		break;
+	case AB_DST_OVER:
+		alpha_config.src_color_mode = AA_SRC_PRE_MUL;
+		alpha_config.src_factor_mode = AA_SRC_INVERSE;
+		alpha_config.dst_factor_mode = AA_ONE;
+		break;
+	case AB_SRC_IN:
+		alpha_config.src_color_mode = AA_SRC_PRE_MUL;
+		alpha_config.src_factor_mode = AA_SRC;
+		alpha_config.dst_factor_mode = AA_ZERO;
+		break;
+	case AB_DST_IN:
+		alpha_config.src_factor_mode = AA_ZERO;
+		alpha_config.dst_factor_mode = AA_SRC;
+		break;
+	case AB_SRC_OUT:
+		alpha_config.src_color_mode = AA_SRC_PRE_MUL;
+		alpha_config.src_factor_mode = AA_SRC_INVERSE;
+		alpha_config.dst_factor_mode = AA_ZERO;
+		break;
+	case AB_DST_OUT:
+		alpha_config.src_factor_mode = AA_ZERO;
+		alpha_config.dst_factor_mode = AA_SRC_INVERSE;
+		break;
+	case AB_SRC_ATOP:
+		alpha_config.src_color_mode = AA_SRC_PRE_MUL;
+		alpha_config.src_factor_mode = AA_SRC;
+		alpha_config.dst_factor_mode = AA_SRC_INVERSE;
+		break;
+	case AB_DST_ATOP:
+		alpha_config.src_color_mode = AA_SRC_PRE_MUL;
+		alpha_config.src_factor_mode = AA_SRC_INVERSE;
+		alpha_config.dst_factor_mode = AA_SRC;
+		break;
+	case XOR:
+		alpha_config.src_color_mode = AA_SRC_PRE_MUL;
+		alpha_config.src_factor_mode = AA_SRC_INVERSE;
+		alpha_config.dst_factor_mode = AA_SRC_INVERSE;
+		break;
+	case AB_SRC_OVER_GLOBAL:
+		alpha_config.src_global_alpha_mode = AA_PER_PIX_GLOBAL;
+		alpha_config.src_color_mode = AA_SRC_NO_PRE_MUL;
+		alpha_config.src_factor_mode = AA_SRC_GLOBAL;
+		alpha_config.dst_factor_mode = AA_SRC_INVERSE;
+		break;
+	default:
+		pr_err("alpha mode error\n");
+		break;
+	}
+	if ((ppixel_alpha == 1) && (global_alpha == 1))
+		alpha_config.src_global_alpha_mode = AA_PER_PIX_GLOBAL;
+	else if (ppixel_alpha == 1)
+		alpha_config.src_global_alpha_mode = AA_PER_PIX;
+	else if (global_alpha == 1)
+		alpha_config.src_global_alpha_mode = AA_GLOBAL;
+	else
+		dev_warn(lcdc_dev->dev, "alpha_en should be 0\n");
+	alpha_config.src_alpha_mode = AA_STRAIGHT;
+	alpha_config.src_alpha_cal_m0 = AA_NO_SAT;
+
+	switch (layer->id) {
+	case 0:
+		src_alpha_ctl = 0x60;
+		dst_alpha_ctl = 0x64;
+		break;
+	case 1:
+		src_alpha_ctl = 0xa0;
+		dst_alpha_ctl = 0xa4;
+		break;
+	case 2:
+		src_alpha_ctl = 0xdc;
+		dst_alpha_ctl = 0xec;
+		break;
+	case 3:
+		src_alpha_ctl = 0x12c;
+		dst_alpha_ctl = 0x13c;
+		break;
+	}
+	mask = M_WIN0_DST_FACTOR_M0;
+	val = V_WIN0_DST_FACTOR_M0(alpha_config.dst_factor_mode);
+	lcdc_msk_reg(lcdc_dev, dst_alpha_ctl, mask, val);
+	mask = M_WIN0_SRC_ALPHA_EN | M_WIN0_SRC_COLOR_M0 |
+		M_WIN0_SRC_ALPHA_M0 | M_WIN0_SRC_BLEND_M0 |
+		M_WIN0_SRC_ALPHA_CAL_M0 | M_WIN0_SRC_FACTOR_M0|
+		M_WIN0_SRC_GLOBAL_ALPHA;
+	val = V_WIN0_SRC_ALPHA_EN(1) |
+		V_WIN0_SRC_COLOR_M0(alpha_config.src_color_mode) |
+		V_WIN0_SRC_ALPHA_M0(alpha_config.src_alpha_mode) |
+		V_WIN0_SRC_BLEND_M0(alpha_config.src_global_alpha_mode) |
+		V_WIN0_SRC_ALPHA_CAL_M0(alpha_config.src_alpha_cal_m0) |
+		V_WIN0_SRC_FACTOR_M0(alpha_config.src_factor_mode) |
+		V_WIN0_SRC_GLOBAL_ALPHA(alpha_config.src_global_alpha_val);
+	lcdc_msk_reg(lcdc_dev, src_alpha_ctl, mask, val);
+}
+
+static void rk3288_lcdc_win01_set(struct lcdc_device *lcdc_dev,
+				  struct lcdc_win_data *win)
+{
+	unsigned int mask, val;
+	unsigned int off = win->id * 0x40;
+	struct drm_display_mode *mode = lcdc_dev->mode;
+
+	spin_lock(&lcdc_dev->reg_lock);
+
+	if (mode && win->enabled) {
+		u32 dsp_stx = win->xpos + mode->htotal - mode->hsync_start;
+		u32 dsp_sty = win->ypos + mode->vtotal - mode->vsync_start;
+
+		mask = M_WIN0_EN | M_WIN0_DATA_FMT | M_WIN0_FMT_10 |
+			M_WIN0_LB_MODE | M_WIN0_RB_SWAP;
+		val = V_WIN0_EN(1) | V_WIN0_DATA_FMT(win->format) |
+			V_WIN0_FMT_10(win->fmt_10) |
+			V_WIN0_LB_MODE(win->win_lb_mode) |
+			V_WIN0_RB_SWAP(win->swap_rb);
+		lcdc_msk_reg(lcdc_dev, WIN0_CTRL0+off, mask, val);
+
+		mask = M_WIN0_BIC_COE_SEL |
+			M_WIN0_VSD_YRGB_GT4 | M_WIN0_VSD_YRGB_GT2 |
+			M_WIN0_VSD_CBR_GT4 | M_WIN0_VSD_CBR_GT2 |
+			M_WIN0_YRGB_HOR_SCL_MODE | M_WIN0_YRGB_VER_SCL_MODE |
+			M_WIN0_YRGB_HSD_MODE | M_WIN0_YRGB_VSU_MODE |
+			M_WIN0_YRGB_VSD_MODE | M_WIN0_CBR_HOR_SCL_MODE |
+			M_WIN0_CBR_VER_SCL_MODE | M_WIN0_CBR_HSD_MODE |
+			M_WIN0_CBR_VSU_MODE | M_WIN0_CBR_VSD_MODE;
+		val = V_WIN0_BIC_COE_SEL(win->bic_coe_el) |
+			V_WIN0_VSD_YRGB_GT4(win->vsd_yrgb_gt4) |
+			V_WIN0_VSD_YRGB_GT2(win->vsd_yrgb_gt2) |
+			V_WIN0_VSD_CBR_GT4(win->vsd_cbr_gt4) |
+			V_WIN0_VSD_CBR_GT2(win->vsd_cbr_gt2) |
+			V_WIN0_YRGB_HOR_SCL_MODE(win->yrgb_hor_scl_mode) |
+			V_WIN0_YRGB_VER_SCL_MODE(win->yrgb_ver_scl_mode) |
+			V_WIN0_YRGB_HSD_MODE(win->yrgb_hsd_mode) |
+			V_WIN0_YRGB_VSU_MODE(win->yrgb_vsu_mode) |
+			V_WIN0_YRGB_VSD_MODE(win->yrgb_vsd_mode) |
+			V_WIN0_CBR_HOR_SCL_MODE(win->cbr_hor_scl_mode) |
+			V_WIN0_CBR_VER_SCL_MODE(win->cbr_ver_scl_mode) |
+			V_WIN0_CBR_HSD_MODE(win->cbr_hsd_mode) |
+			V_WIN0_CBR_VSU_MODE(win->cbr_vsu_mode) |
+			V_WIN0_CBR_VSD_MODE(win->cbr_vsd_mode);
+		lcdc_msk_reg(lcdc_dev, WIN0_CTRL1+off, mask, val);
+
+		val = V_WIN0_VIR_STRIDE(win->y_vir_stride) |
+			V_WIN0_VIR_STRIDE_UV(win->uv_vir_stride);
+		lcdc_writel(lcdc_dev, WIN0_VIR+off, val);
+		lcdc_writel(lcdc_dev, WIN0_YRGB_MST+off, win->yrgb_addr);
+		lcdc_writel(lcdc_dev, WIN0_CBR_MST+off, win->uv_addr);
+		val = V_WIN0_ACT_WIDTH(win->xact) |
+			V_WIN0_ACT_HEIGHT(win->yact);
+		lcdc_writel(lcdc_dev, WIN0_ACT_INFO+off, val);
+
+		val = V_WIN0_DSP_WIDTH(win->xsize) |
+			V_WIN0_DSP_HEIGHT(win->ysize);
+		lcdc_writel(lcdc_dev, WIN0_DSP_INFO+off, val);
+
+		val = V_WIN0_DSP_XST(dsp_stx) |
+			V_WIN0_DSP_YST(dsp_sty);
+		lcdc_writel(lcdc_dev, WIN0_DSP_ST+off, val);
+
+		val = V_WIN0_HS_FACTOR_YRGB(0x1000) |
+			V_WIN0_VS_FACTOR_YRGB(0x1000);
+		lcdc_writel(lcdc_dev, WIN0_SCL_FACTOR_YRGB+off, val);
+
+		val = V_WIN0_HS_FACTOR_CBR(0x1000) |
+			V_WIN0_VS_FACTOR_CBR(0x1000);
+		lcdc_writel(lcdc_dev, WIN0_SCL_FACTOR_CBR+off, val);
+
+		lcdc_writel(lcdc_dev, WIN0_COLOR_KEY, 0x80000000);
+		if (win->alpha_en == 1) {
+			rk3288_lcdc_alpha_cfg(lcdc_dev, win);
+		} else {
+			mask = M_WIN0_SRC_ALPHA_EN;
+			val = V_WIN0_SRC_ALPHA_EN(0);
+			lcdc_msk_reg(lcdc_dev,
+				     WIN0_SRC_ALPHA_CTRL+off, mask, val);
+		}
+	} else {
+		mask = M_WIN0_EN;
+		val = V_WIN0_EN(0);
+		lcdc_msk_reg(lcdc_dev, WIN0_CTRL0+off, mask, val);
+	}
+
+	spin_unlock(&lcdc_dev->reg_lock);
+}
+
+static void rk3288_lcdc_win23_set(struct lcdc_device *lcdc_dev,
+				  struct lcdc_win_data *win)
+{
+	unsigned int mask, val;
+	unsigned int off = (win->id-2) * 0x50;
+	struct drm_display_mode *mode = lcdc_dev->mode;
+
+	if (mode && win->enabled) {
+		u32 dsp_stx = win->xpos + mode->htotal - mode->hdisplay;
+		u32 dsp_sty = win->ypos + mode->vtotal - mode->vdisplay;
+
+		mask = M_WIN2_EN | M_WIN2_DATA_FMT | M_WIN2_RB_SWAP;
+		val = V_WIN2_EN(1) | V_WIN2_DATA_FMT(win->fmt_cfg) |
+			V_WIN2_RB_SWAP(win->swap_rb);
+		lcdc_msk_reg(lcdc_dev, WIN2_CTRL0+off, mask, val);
+		mask = M_WIN2_MST0_EN;
+		val = V_WIN2_MST0_EN(1);
+		lcdc_msk_reg(lcdc_dev, WIN2_CTRL0+off, mask, val);
+
+		mask = M_WIN2_VIR_STRIDE0;
+		val = V_WIN2_VIR_STRIDE0(win->y_vir_stride);
+		lcdc_msk_reg(lcdc_dev, WIN2_VIR0_1+off, mask, val);
+
+		lcdc_writel(lcdc_dev, WIN2_MST0+off, win->yrgb_addr);
+		val = V_WIN2_DSP_WIDTH0(win->xsize) |
+			V_WIN2_DSP_HEIGHT0(win->ysize);
+		lcdc_writel(lcdc_dev, WIN2_DSP_INFO0+off, val);
+		val = V_WIN2_DSP_XST0(dsp_stx) |
+			V_WIN2_DSP_YST0(dsp_sty);
+		lcdc_writel(lcdc_dev, WIN2_DSP_ST0+off, val);
+		if (win->alpha_en == 1) {
+			rk3288_lcdc_alpha_cfg(lcdc_dev, win);
+		} else {
+			mask = M_WIN2_SRC_ALPHA_EN;
+			val = V_WIN2_SRC_ALPHA_EN(0);
+			lcdc_msk_reg(lcdc_dev,
+				     WIN2_SRC_ALPHA_CTRL+off, mask, val);
+		}
+	} else {
+		mask = M_WIN2_EN | M_WIN2_MST0_EN |
+			M_WIN2_MST0_EN | M_WIN2_MST2_EN |
+			M_WIN2_MST3_EN;
+		val = V_WIN2_EN(0) | V_WIN2_MST0_EN(0) |
+			V_WIN2_MST1_EN(0) | V_WIN2_MST2_EN(0) |
+			V_WIN2_MST3_EN(0);
+		lcdc_msk_reg(lcdc_dev, WIN2_CTRL0+off, mask, val);
+	}
+}
+
+static int rk3288_lcdc_initial(struct lcdc_device *lcdc_dev)
+{
+	int i;
+
+	lcdc_dev->hclk = devm_clk_get(lcdc_dev->dev, "hclk_lcdc");
+	lcdc_dev->aclk = devm_clk_get(lcdc_dev->dev, "aclk_lcdc");
+	lcdc_dev->dclk = devm_clk_get(lcdc_dev->dev, "dclk_lcdc");
+
+	if ((IS_ERR(lcdc_dev->aclk)) || (IS_ERR(lcdc_dev->dclk)) ||
+	    (IS_ERR(lcdc_dev->hclk))) {
+		dev_err(lcdc_dev->dev, "failed to get lcdc%d clk source\n",
+			lcdc_dev->id);
+		return -ENODEV;
+	}
+
+	if (rk3288_lcdc_clk_enable(lcdc_dev) < 0) {
+		dev_err(lcdc_dev->dev, "failed to enable lcdc%d clks\n",
+			lcdc_dev->id);
+		return -ENODEV;
+	}
+
+	memcpy(lcdc_dev->regsbak, lcdc_dev->regs, lcdc_dev->len);
+
+	lcdc_set_bit(lcdc_dev, SYS_CTRL, M_AUTO_GATING_EN);
+	lcdc_cfg_done(lcdc_dev);
+
+	lcdc_dev->standby = DRM_MODE_DPMS_OFF;
+
+	for (i = 0; i < ARRAY_SIZE(lcdc_win); i++) {
+		lcdc_win[i].enabled = false;
+		rk3288_lcdc_win_commit(&lcdc_dev->lcdc_drv, &lcdc_win[i]);
+	}
+
+	return 0;
+}
+
+static struct lcdc_driver *rk3288_lcdc_init(struct platform_device *pdev)
+{
+	struct lcdc_device *lcdc_dev;
+	struct lcdc_driver *lcdc_drv;
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	struct device_node *np;
+	int ret = 0;
+
+	if (!dev->of_node)
+		return NULL;
+
+	np = pdev->dev.of_node;
+
+	/* if the primary lcdc has not registered, the extend
+	 * lcdc register later
+	*/
+	lcdc_dev = devm_kzalloc(dev, sizeof(struct lcdc_device), GFP_KERNEL);
+	if (!lcdc_dev) {
+		dev_err(&pdev->dev, "rk3288 lcdc device kmalloc fail!");
+		return NULL;
+	}
+	lcdc_dev->dev = dev;
+	lcdc_drv = &lcdc_dev->lcdc_drv;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	lcdc_dev->reg_phy_base = res->start;
+	lcdc_dev->len = resource_size(res);
+	lcdc_dev->regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(lcdc_dev->regs)) {
+		dev_err(&pdev->dev, "ioremap lcdc devices fail\n");
+		return NULL;
+	}
+
+	lcdc_dev->regsbak = devm_kzalloc(dev, lcdc_dev->len, GFP_KERNEL);
+	if (IS_ERR(lcdc_dev->regsbak)) {
+		dev_err(&pdev->dev, "lcdc devices kzalloc reg backup fail\n");
+		return NULL;
+	}
+
+	lcdc_dev->id = rk3288_lcdc_get_id(lcdc_dev->reg_phy_base);
+	if (lcdc_dev->id < 0) {
+		dev_err(&pdev->dev, "no such lcdc device id[%d]!\n",
+			lcdc_dev->id);
+		return NULL;
+	}
+
+	dev_set_name(lcdc_dev->dev, "lcdc%d", lcdc_dev->id);
+
+	lcdc_dev->irq = platform_get_irq(pdev, 0);
+	if (lcdc_dev->irq < 0) {
+		dev_err(&pdev->dev, "cannot find IRQ for lcdc%d\n",
+			lcdc_dev->id);
+		return NULL;
+	}
+
+	ret = devm_request_irq(dev, lcdc_dev->irq, rk3288_lcdc_isr,
+			       IRQF_DISABLED | IRQF_SHARED,
+			       dev_name(dev), lcdc_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "cannot requeset irq %d - err %d\n",
+			lcdc_dev->irq, ret);
+		return NULL;
+	}
+
+	spin_lock_init(&lcdc_dev->reg_lock);
+
+	ret = rk3288_lcdc_initial(lcdc_dev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "cannot initial lcdc dev - err %d\n", ret);
+		return NULL;
+	}
+
+	dev_info(dev, "lcdc%d probe ok\n", lcdc_dev->id);
+
+	return lcdc_drv;
+}
+
+static void rk3288_lcdc_deinit(struct lcdc_driver *lcdc_drv)
+{
+}
+
+static void rk3288_lcdc_dpms(struct lcdc_driver *lcdc_drv, int mode)
+{
+	struct lcdc_device *lcdc_dev =
+			container_of(lcdc_drv, struct lcdc_device, lcdc_drv);
+
+	if (lcdc_dev->standby == mode)
+		return;
+
+	if (mode == DRM_MODE_DPMS_ON) {
+		if (rk3288_lcdc_clk_enable(lcdc_dev) < 0) {
+			dev_err(lcdc_dev->dev, "failed to enable lcdc%d clks\n",
+				lcdc_dev->id);
+			return;
+		}
+
+		spin_lock(&lcdc_dev->reg_lock);
+		lcdc_msk_reg(lcdc_dev, SYS_CTRL, M_STANDBY_EN, V_STANDBY_EN(0));
+		spin_unlock(&lcdc_dev->reg_lock);
+	} else {
+		if (lcdc_dev->clk_on) {
+			spin_lock(&lcdc_dev->reg_lock);
+			lcdc_msk_reg(lcdc_dev, SYS_CTRL,
+				     M_STANDBY_EN, V_STANDBY_EN(1));
+			spin_unlock(&lcdc_dev->reg_lock);
+			rk3288_lcdc_clk_disable(lcdc_dev);
+		}
+	}
+
+	lcdc_dev->standby = mode;
+}
+
+static void rk3288_lcdc_mode_set(struct lcdc_driver *lcdc_drv,
+				 struct drm_display_mode *mode)
+{
+	struct lcdc_device *lcdc_dev =
+			container_of(lcdc_drv, struct lcdc_device, lcdc_drv);
+	struct rockchip_panel_special *priv_mode = (void *)mode->private;
+	u32 mask, val;
+	u16 x_res = mode->hdisplay;
+	u16 y_res = mode->vdisplay;
+	u16 h_total = mode->htotal;
+	u16 v_total = mode->vtotal;
+	u16 hsync_len = mode->hsync_end - mode->hsync_start;
+	u16 left_margin = mode->htotal - mode->hsync_end;
+	u16 vsync_len = mode->vsync_end - mode->vsync_start;
+	u16 upper_margin = mode->vtotal - mode->vsync_end;
+
+	u16 face = priv_mode->out_face;
+	u8 pin_hsync = (priv_mode->flags & DISPLAY_FLAGS_HSYNC_HIGH) ? 1 : 0;
+	u8 pin_vsync = (priv_mode->flags & DISPLAY_FLAGS_VSYNC_HIGH) ? 1 : 0;
+	u8 pin_den = (priv_mode->flags & DISPLAY_FLAGS_DE_HIGH) ? 1 : 0;
+	u8 pin_dclk = (priv_mode->flags &
+		       DISPLAY_FLAGS_PIXDATA_NEGEDGE) ? 1 : 0;
+	u8 swap_rb = (priv_mode->color_swap & ROCKCHIP_COLOR_SWAP_RB) ? 1 : 0;
+	u8 swap_rg = (priv_mode->color_swap & ROCKCHIP_COLOR_SWAP_RG) ? 1 : 0;
+	u8 swap_gb = (priv_mode->color_swap & ROCKCHIP_COLOR_SWAP_GB) ? 1 : 0;
+	u8 swap_dumy = 0;
+	u8 swap_delta = 0;
+	bool dither = priv_mode->dither;
+	u8 type = priv_mode->out_type;
+
+	spin_lock(&lcdc_dev->reg_lock);
+	if (likely(lcdc_dev->clk_on)) {
+		val = 0;
+		switch (face) {
+		case ROCKCHIP_OUTFACE_P565:
+			val = V_DITHER_DOWN_EN(1) | V_DITHER_DOWN_MODE(0) |
+				V_DITHER_DOWN_SEL(1);
+			break;
+		case ROCKCHIP_OUTFACE_P666:
+			val = V_DITHER_DOWN_EN(1) | V_DITHER_DOWN_MODE(1) |
+				V_DITHER_DOWN_SEL(1);
+			break;
+		case ROCKCHIP_OUTFACE_P888:
+			break;
+		default:
+			dev_err(lcdc_dev->dev, "un supported interface[%d]!\n",
+				face);
+			break;
+		}
+
+		mask = M_DITHER_DOWN_EN | M_DITHER_DOWN_MODE |
+			M_DITHER_DOWN_SEL;
+		lcdc_msk_reg(lcdc_dev, DSP_CTRL1, mask, val);
+
+		if (dither)
+			face = ROCKCHIP_OUTFACE_P888;
+
+		switch (type) {
+		case ROCKCHIP_DISPLAY_TYPE_RGB:
+		case ROCKCHIP_DISPLAY_TYPE_LVDS:
+			mask = M_RGB_OUT_EN;
+			val = V_RGB_OUT_EN(1);
+			break;
+		case ROCKCHIP_DISPLAY_TYPE_HDMI:
+			face = ROCKCHIP_OUTFACE_AAAA;
+			mask = M_HDMI_OUT_EN;
+			val = V_HDMI_OUT_EN(1);
+			break;
+		case ROCKCHIP_DISPLAY_TYPE_MIPI:
+			mask = M_MIPI_OUT_EN;
+			val = V_MIPI_OUT_EN(1);
+			break;
+		case ROCKCHIP_DISPLAY_TYPE_EDP:
+			face = ROCKCHIP_OUTFACE_AAAA;
+			mask = M_DITHER_DOWN_EN | M_DITHER_UP_EN;
+			val = V_DITHER_DOWN_EN(0) | V_DITHER_UP_EN(0);
+			lcdc_msk_reg(lcdc_dev, DSP_CTRL1, mask, val);
+			mask = M_EDP_OUT_EN;
+			val = V_EDP_OUT_EN(1);
+			break;
+		default:
+			dev_err(lcdc_dev->dev, "unsupported display type[%d]\n",
+				type);
+		}
+		lcdc_msk_reg(lcdc_dev, SYS_CTRL, mask, val);
+
+		mask = M_DSP_OUT_MODE | M_DSP_HSYNC_POL | M_DSP_VSYNC_POL |
+			M_DSP_DEN_POL | M_DSP_DCLK_POL | M_DSP_BG_SWAP |
+			M_DSP_RB_SWAP | M_DSP_RG_SWAP | M_DSP_DELTA_SWAP |
+			M_DSP_DUMMY_SWAP | M_DSP_OUT_ZERO | M_DSP_BLANK_EN |
+			M_DSP_BLACK_EN | M_DSP_X_MIR_EN | M_DSP_Y_MIR_EN;
+		val = V_DSP_OUT_MODE(face) | V_DSP_HSYNC_POL(pin_hsync) |
+			V_DSP_VSYNC_POL(pin_vsync) |
+			V_DSP_DEN_POL(pin_den) | V_DSP_DCLK_POL(pin_dclk) |
+			V_DSP_BG_SWAP(swap_gb) | V_DSP_RB_SWAP(swap_rb) |
+			V_DSP_RG_SWAP(swap_rg) |
+			V_DSP_DELTA_SWAP(swap_delta) |
+			V_DSP_DUMMY_SWAP(swap_dumy) | V_DSP_OUT_ZERO(0) |
+			V_DSP_BLANK_EN(0) | V_DSP_BLACK_EN(0);
+		lcdc_msk_reg(lcdc_dev, DSP_CTRL0, mask, val);
+
+		mask = M_DSP_BG_BLUE | M_DSP_BG_GREEN | M_DSP_BG_RED;
+		val = V_DSP_BG_BLUE(0) | V_DSP_BG_GREEN(0) | V_DSP_BG_RED(0);
+		lcdc_msk_reg(lcdc_dev, DSP_BG, mask, val);
+
+		mask = M_DSP_HS_PW | M_DSP_HTOTAL;
+		val = V_DSP_HS_PW(hsync_len) | V_DSP_HTOTAL(h_total);
+		lcdc_msk_reg(lcdc_dev, DSP_HTOTAL_HS_END, mask, val);
+
+		mask = M_DSP_HACT_END | M_DSP_HACT_ST;
+		val = V_DSP_HACT_END(hsync_len + left_margin + x_res) |
+			V_DSP_HACT_ST(hsync_len + left_margin);
+		lcdc_msk_reg(lcdc_dev, DSP_HACT_ST_END, mask, val);
+
+		mask = M_DSP_VS_PW | M_DSP_VTOTAL;
+		val = V_DSP_VS_PW(vsync_len) | V_DSP_VTOTAL(v_total);
+		lcdc_msk_reg(lcdc_dev, DSP_VTOTAL_VS_END, mask, val);
+
+		mask = M_DSP_VACT_END | M_DSP_VACT_ST;
+		val = V_DSP_VACT_END(vsync_len + upper_margin + y_res) |
+			V_DSP_VACT_ST(vsync_len + upper_margin);
+		lcdc_msk_reg(lcdc_dev, DSP_VACT_ST_END, mask, val);
+
+		mask = M_DSP_HACT_END_POST | M_DSP_HACT_ST_POST;
+		val = V_DSP_HACT_END_POST(hsync_len + left_margin + x_res) |
+			V_DSP_HACT_ST_POST(hsync_len + left_margin);
+		lcdc_msk_reg(lcdc_dev, POST_DSP_HACT_INFO, mask, val);
+
+		mask = M_DSP_VACT_END_POST | M_DSP_VACT_ST_POST;
+		val = V_DSP_VACT_END_POST(vsync_len + upper_margin + y_res) |
+			V_DSP_VACT_ST_POST(vsync_len + upper_margin);
+		lcdc_msk_reg(lcdc_dev, POST_DSP_VACT_INFO, mask, val);
+	}
+
+	spin_unlock(&lcdc_dev->reg_lock);
+	clk_set_rate(lcdc_dev->dclk, mode->clock * 1000);
+
+	lcdc_dev->mode = mode;
+}
+
+static void rk3288_lcdc_enable_vblank(struct lcdc_driver *lcdc_drv)
+{
+	rk3288_lcdc_enable_irq(container_of(lcdc_drv,
+					    struct lcdc_device, lcdc_drv));
+}
+
+static void rk3288_lcdc_disable_vblank(struct lcdc_driver *lcdc_drv)
+{
+	rk3288_lcdc_disable_irq(container_of(lcdc_drv,
+					     struct lcdc_device, lcdc_drv));
+}
+
+static struct lcdc_win_data *
+	rk3288_lcdc_get_win(struct lcdc_driver *lcdc_drv, int zpos)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(lcdc_win); i++) {
+		if (lcdc_win[i].zpos == zpos)
+			return &lcdc_win[i];
+	}
+
+	return NULL;
+}
+
+static void rk3288_lcdc_win_commit(struct lcdc_driver *lcdc_drv,
+				   struct lcdc_win_data *win)
+{
+	struct lcdc_device *lcdc_dev =
+			container_of(lcdc_drv, struct lcdc_device, lcdc_drv);
+	struct device *dev = lcdc_dev->dev;
+
+	switch (win->id) {
+	case 0:
+	case 1:
+		rk3288_lcdc_win01_set(lcdc_dev, win);
+		break;
+	case 2:
+	case 3:
+		rk3288_lcdc_win23_set(lcdc_dev, win);
+		break;
+	default:
+		dev_info(dev, "not support win%d\n", win->id);
+	}
+
+	spin_lock(&lcdc_dev->reg_lock);
+	lcdc_cfg_done(lcdc_dev);
+	spin_unlock(&lcdc_dev->reg_lock);
+}
+
+struct lcdc_driver_data rockchip_rk3288_lcdc = {
+	.init = rk3288_lcdc_init,
+	.deinit = rk3288_lcdc_deinit,
+	.dpms = rk3288_lcdc_dpms,
+	.mode_set = rk3288_lcdc_mode_set,
+	.enable_vblank = rk3288_lcdc_enable_vblank,
+	.disable_vblank = rk3288_lcdc_disable_vblank,
+	.get_win = rk3288_lcdc_get_win,
+	.win_commit = rk3288_lcdc_win_commit,
+	.num_win = ARRAY_SIZE(lcdc_win),
+};
diff --git a/drivers/gpu/drm/rockchip/lcdc/rk3288_lcdc.h b/drivers/gpu/drm/rockchip/lcdc/rk3288_lcdc.h
new file mode 100644
index 0000000..d2e1110
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/lcdc/rk3288_lcdc.h
@@ -0,0 +1,1202 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:
+ *      hjc <hjc@rock-chips.com>
+ *      mark yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _RK3288_LCDC_H_
+#define _RK3288_LCDC_H_
+
+#define SET_BIT(x, bit) ((x) << (bit))
+#define SET_BIT_MASK(x, bit, mask) SET_BIT((x) & (mask), bit)
+
+#define GPIO		0
+#define REGULATOR	1
+
+/* register definition */
+#define REG_CFG_DONE			(0x0000)
+#define VERSION_INFO			(0x0004)
+#define M_RTL_VERSION			SET_BIT(0xffff, 0)
+#define M_FPGA_VERSION			SET_BIT(0xffff, 16)
+#define SYS_CTRL			(0x0008)
+#define V_DIRECT_PATH_EN(x)		SET_BIT_MASK(x, 0, 1)
+#define V_DIRECT_PATCH_SEL(x)		SET_BIT_MASK(x, 1, 3)
+#define V_DOUB_CHANNEL_EN(x)		SET_BIT_MASK(x, 3, 1)
+#define V_DOUB_CH_OVERLAP_NUM(x)	SET_BIT_MASK(x, 4, 0xf)
+#define V_EDPI_HALT_EN(x)		SET_BIT_MASK(x, 8, 1)
+#define V_EDPI_WMS_MODE(x)		SET_BIT_MASK(x, 9, 1)
+#define V_EDPI_WMS_FS(x)		SET_BIT_MASK(x, 10, 1)
+#define V_RGB_OUT_EN(x)			SET_BIT_MASK(x, 12, 1)
+#define V_HDMI_OUT_EN(x)		SET_BIT_MASK(x, 13, 1)
+#define V_EDP_OUT_EN(x)			SET_BIT_MASK(x, 14, 1)
+#define V_MIPI_OUT_EN(x)		SET_BIT_MASK(x, 15, 1)
+#define V_DMA_BURST_LENGTH(x)		SET_BIT_MASK(x, 18, 3)
+#define V_MMU_EN(x)			SET_BIT_MASK(x, 20, 1)
+#define V_DMA_STOP(x)			SET_BIT_MASK(x, 21, 1)
+#define V_STANDBY_EN(x)			SET_BIT_MASK(x, 22, 1)
+#define V_AUTO_GATING_EN(x)		SET_BIT_MASK(x, 23, 1)
+
+#define M_DIRECT_PATH_EN		SET_BIT(1, 0)
+#define M_DIRECT_PATCH_SEL		SET_BIT(3, 1)
+#define M_DOUB_CHANNEL_EN		SET_BIT(1, 3)
+#define M_DOUB_CH_OVERLAP_NUM		SET_BIT(0xf, 4)
+#define M_EDPI_HALT_EN			SET_BIT(1, 8)
+#define M_EDPI_WMS_MODE			SET_BIT(1, 9)
+#define M_EDPI_WMS_FS			SET_BIT(1, 10)
+#define M_RGB_OUT_EN			SET_BIT(1, 12)
+#define M_HDMI_OUT_EN			SET_BIT(1, 13)
+#define M_EDP_OUT_EN			SET_BIT(1, 14)
+#define M_MIPI_OUT_EN			SET_BIT(1, 15)
+#define M_DMA_BURST_LENGTH		SET_BIT(3, 18)
+#define M_MMU_EN			SET_BIT(1, 20)
+#define M_DMA_STOP			SET_BIT(1, 21)
+#define M_STANDBY_EN			SET_BIT(1, 22)
+#define M_AUTO_GATING_EN		SET_BIT(1, 23)
+#define SYS_CTRL1			(0x000c)
+#define V_NOC_HURRY_EN(x)		SET_BIT_MASK(x, 0, 0x1)
+#define V_NOC_HURRY_VALUE(x)		SET_BIT_MASK(x, 1, 0x3)
+#define V_NOC_HURRY_THRESHOLD(x)	SET_BIT_MASK(x, 3, 0x3f)
+#define V_NOC_QOS_EN(x)			SET_BIT_MASK(x, 9, 0x1)
+#define V_NOC_WIN_QOS(x)		SET_BIT_MASK(x, 10, 0x3)
+#define V_AXI_MAX_OUTSTANDING_EN(x)	SET_BIT_MASK(x, 12, 0x1)
+#define V_AXI_OUTSTANDING_MAX_NUM(x)	SET_BIT_MASK(x, 13, 0x1f)
+
+#define M_NOC_HURRY_EN			SET_BIT(0x1, 0)
+#define M_NOC_HURRY_VALUE		SET_BIT(0x3, 1)
+#define M_NOC_HURRY_THRESHOLD		SET_BIT(0x3f, 3)
+#define M_NOC_QOS_EN			SET_BIT(0x1, 9)
+#define M_NOC_WIN_QOS			SET_BIT(0x3, 10)
+#define M_AXI_MAX_OUTSTANDING_EN	SET_BIT(0x1, 12)
+#define M_AXI_OUTSTANDING_MAX_NUM	SET_BIT(0x1f, 13)
+
+#define DSP_CTRL0		(0x0010)
+#define V_DSP_OUT_MODE(x)	SET_BIT_MASK(x, 0, 0x0f)
+#define V_DSP_HSYNC_POL(x)	SET_BIT_MASK(x, 4, 1)
+#define V_DSP_VSYNC_POL(x)	SET_BIT_MASK(x, 5, 1)
+#define V_DSP_DEN_POL(x)	SET_BIT_MASK(x, 6, 1)
+#define V_DSP_DCLK_POL(x)	SET_BIT_MASK(x, 7, 1)
+#define V_DSP_DCLK_DDR(x)	SET_BIT_MASK(x, 8, 1)
+#define V_DSP_DDR_PHASE(x)	SET_BIT_MASK(x, 9, 1)
+#define V_DSP_INTERLACE(x)	SET_BIT_MASK(x, 10, 1)
+#define V_DSP_FIELD_POL(x)	SET_BIT_MASK(x, 11, 1)
+#define V_DSP_BG_SWAP(x)	SET_BIT_MASK(x, 12, 1)
+#define V_DSP_RB_SWAP(x)	SET_BIT_MASK(x, 13, 1)
+#define V_DSP_RG_SWAP(x)	SET_BIT_MASK(x, 14, 1)
+#define V_DSP_DELTA_SWAP(x)	SET_BIT_MASK(x, 15, 1)
+#define V_DSP_DUMMY_SWAP(x)	SET_BIT_MASK(x, 16, 1)
+#define V_DSP_OUT_ZERO(x)	SET_BIT_MASK(x, 17, 1)
+#define V_DSP_BLANK_EN(x)	SET_BIT_MASK(x, 18, 1)
+#define V_DSP_BLACK_EN(x)	SET_BIT_MASK(x, 19, 1)
+#define V_DSP_CCIR656_AVG(x)	SET_BIT_MASK(x, 20, 1)
+#define V_DSP_YUV_CLIP(x)	SET_BIT_MASK(x, 21, 1)
+#define V_DSP_X_MIR_EN(x)	SET_BIT_MASK(x, 22, 1)
+#define V_DSP_Y_MIR_EN(x)	SET_BIT_MASK(x, 23, 1)
+#define M_DSP_OUT_MODE		SET_BIT(0x0f, 0)
+#define M_DSP_HSYNC_POL		SET_BIT(1, 4)
+#define M_DSP_VSYNC_POL		SET_BIT(1, 5)
+#define M_DSP_DEN_POL		SET_BIT(1, 6)
+#define M_DSP_DCLK_POL		SET_BIT(1, 7)
+#define M_DSP_DCLK_DDR		SET_BIT(1, 8)
+#define M_DSP_DDR_PHASE		SET_BIT(1, 9)
+#define M_DSP_INTERLACE		SET_BIT(1, 10)
+#define M_DSP_FIELD_POL		SET_BIT(1, 11)
+#define M_DSP_BG_SWAP		SET_BIT(1, 12)
+#define M_DSP_RB_SWAP		SET_BIT(1, 13)
+#define M_DSP_RG_SWAP		SET_BIT(1, 14)
+#define M_DSP_DELTA_SWAP	SET_BIT(1, 15)
+#define M_DSP_DUMMY_SWAP	SET_BIT(1, 16)
+#define M_DSP_OUT_ZERO		SET_BIT(1, 17)
+#define M_DSP_BLANK_EN		SET_BIT(1, 18)
+#define M_DSP_BLACK_EN		SET_BIT(1, 19)
+#define M_DSP_CCIR656_AVG	SET_BIT(1, 20)
+#define M_DSP_YUV_CLIP		SET_BIT(1, 21)
+#define M_DSP_X_MIR_EN		SET_BIT(1, 22)
+#define M_DSP_Y_MIR_EN		SET_BIT(1, 23)
+
+#define DSP_CTRL1		(0x0014)
+#define V_DSP_LUT_EN(x)		SET_BIT_MASK(x, 0, 1)
+#define V_PRE_DITHER_DOWN_EN(x)	SET_BIT_MASK(x, 1, 1)
+#define V_DITHER_DOWN_EN(x)	SET_BIT_MASK(x, 2, 1)
+#define V_DITHER_DOWN_MODE(x)	SET_BIT_MASK(x, 3, 1)
+#define V_DITHER_DOWN_SEL(x)	SET_BIT_MASK(x, 4, 1)
+#define V_DITHER_UP_EN(x)	SET_BIT_MASK(x, 6, 1)
+#define V_DSP_LAYER0_SEL(x)	SET_BIT_MASK(x, 8, 3)
+#define V_DSP_LAYER1_SEL(x)	SET_BIT_MASK(x, 10, 3)
+#define V_DSP_LAYER2_SEL(x)	SET_BIT_MASK(x, 12, 3)
+#define V_DSP_LAYER3_SEL(x)	SET_BIT_MASK(x, 14, 3)
+#define M_DSP_LUT_EN		SET_BIT(1, 0)
+#define M_PRE_DITHER_DOWN_EN	SET_BIT(1, 1)
+#define M_DITHER_DOWN_EN	SET_BIT(1, 2)
+#define M_DITHER_DOWN_MODE	SET_BIT(1, 3)
+#define M_DITHER_DOWN_SEL	SET_BIT(1, 4)
+#define M_DITHER_UP_EN		SET_BIT(1, 6)
+#define M_DSP_LAYER0_SEL	SET_BIT(3, 8)
+#define M_DSP_LAYER1_SEL	SET_BIT(3, 10)
+#define M_DSP_LAYER2_SEL	SET_BIT(3, 12)
+#define M_DSP_LAYER3_SEL	SET_BIT(3, 14)
+
+#define DSP_BG			(0x0018)
+#define V_DSP_BG_BLUE(x)	SET_BIT_MASK(x << 2, 0, 0x3ff)
+#define V_DSP_BG_GREEN(x)	SET_BIT_MASK(x << 2, 10, 0x3ff)
+#define V_DSP_BG_RED(x)		SET_BIT_MASK(x << 2, 20, 0x3ff)
+#define M_DSP_BG_BLUE		SET_BIT(0x3ff, 0)
+#define M_DSP_BG_GREEN		SET_BIT(0x3ff, 10)
+#define M_DSP_BG_RED		SET_BIT(0x3ff, 20)
+
+#define MCU_CTRL		(0x001c)
+#define V_MCU_PIX_TOTAL(x)	SET_BIT_MASK(x, 0, 0x3f)
+#define V_MCU_CS_PST(x)		SET_BIT_MASK(x, 6, 0xf)
+#define V_MCU_CS_PEND(x)	SET_BIT_MASK(x, 10, 0x3f)
+#define V_MCU_RW_PST(x)		SET_BIT_MASK(x, 16, 0xf)
+#define V_MCU_RW_PEND(x)	SET_BIT_MASK(x, 20, 0x3f)
+#define V_MCU_CLK_SEL(x)	SET_BIT_MASK(x, 26, 1)
+#define V_MCU_HOLD_MODE(x)	SET_BIT_MASK(x, 27, 1)
+#define V_MCU_FRAME_ST(x)	SET_BIT_MASK(x, 28, 1)
+#define V_MCU_RS(x)		SET_BIT_MASK(x, 29, 1)
+#define V_MCU_BYPASS(x)		SET_BIT_MASK(x, 30, 1)
+#define V_MCU_TYPE(x)		SET_BIT_MASK(x, 31, 1)
+#define M_MCU_PIX_TOTAL		SET_BIT(0x3f, 0)
+#define M_MCU_CS_PST		SET_BIT(0xf, 6)
+#define M_MCU_CS_PEND		SET_BIT(0x3f, 10)
+#define M_MCU_RW_PST		SET_BIT(0xf, 16)
+#define M_MCU_RW_PEND		SET_BIT(0x3f, 20)
+#define M_MCU_CLK_SEL		SET_BIT(1, 26)
+#define M_MCU_HOLD_MODE		SET_BIT(1, 27)
+#define M_MCU_FRAME_ST		SET_BIT(1, 28)
+#define M_MCU_RS		SET_BIT(1, 29)
+#define M_MCU_BYPASS		SET_BIT(1, 30)
+#define M_MCU_TYPE		SET_BIT((u32)1, 31)
+
+#define INTR_CTRL0			(0x0020)
+#define V_DSP_HOLD_VALID_INTR_STS(x)	SET_BIT_MASK(x, 0, 1)
+#define V_FS_INTR_STS(x)		SET_BIT_MASK(x, 1, 1)
+#define V_LINE_FLAG_INTR_STS(x)		SET_BIT_MASK(x, 2, 1)
+#define V_BUS_ERROR_INTR_STS(x)		SET_BIT_MASK(x, 3, 1)
+#define V_DSP_HOLD_VALID_INTR_EN(x)	SET_BIT_MASK(x, 4, 1)
+#define V_FS_INTR_EN(x)			SET_BIT_MASK(x, 5, 1)
+#define V_LINE_FLAG_INTR_EN(x)		SET_BIT_MASK(x, 6, 1)
+#define V_BUS_ERROR_INTR_EN(x)		SET_BIT_MASK(x, 7, 1)
+#define V_DSP_HOLD_VALID_INTR_CLR(x)	SET_BIT_MASK(x, 8, 1)
+#define V_FS_INTR_CLR(x)		SET_BIT_MASK(x, 9, 1)
+#define V_LINE_FLAG_INTR_CLR(x)		SET_BIT_MASK(x, 10, 1)
+#define V_BUS_ERROR_INTR_CLR(x)		SET_BIT_MASK(x, 11, 1)
+#define V_DSP_LINE_FLAG_NUM(x)		SET_BIT_MASK(x, 12, 0xfff)
+
+#define M_DSP_HOLD_VALID_INTR_STS	SET_BIT(1, 0)
+#define M_FS_INTR_STS			SET_BIT(1, 1)
+#define M_LINE_FLAG_INTR_STS		SET_BIT(1, 2)
+#define M_BUS_ERROR_INTR_STS		SET_BIT(1, 3)
+#define M_DSP_HOLD_VALID_INTR_EN	SET_BIT(1, 4)
+#define M_FS_INTR_EN			SET_BIT(1, 5)
+#define M_LINE_FLAG_INTR_EN		SET_BIT(1, 6)
+#define M_BUS_ERROR_INTR_EN		SET_BIT(1, 7)
+#define M_DSP_HOLD_VALID_INTR_CLR	SET_BIT(1, 8)
+#define M_FS_INTR_CLR			SET_BIT(1, 9)
+#define M_LINE_FLAG_INTR_CLR		SET_BIT(1, 10)
+#define M_BUS_ERROR_INTR_CLR		SET_BIT(1, 11)
+#define M_DSP_LINE_FLAG_NUM		SET_BIT(0xfff, 12)
+
+#define INTR_CTRL1			(0x0024)
+#define V_WIN0_EMPTY_INTR_STS(x)	SET_BIT_MASK(x, 0, 1)
+#define V_WIN1_EMPTY_INTR_STS(x)	SET_BIT_MASK(x, 1, 1)
+#define V_WIN2_EMPTY_INTR_STS(x)	SET_BIT_MASK(x, 2, 1)
+#define V_WIN3_EMPTY_INTR_STS(x)	SET_BIT_MASK(x, 3, 1)
+#define V_HWC_EMPTY_INTR_STS(x)		SET_BIT_MASK(x, 4, 1)
+#define V_POST_BUF_EMPTY_INTR_STS(x)	SET_BIT_MASK(x, 5, 1)
+#define V_PWM_GEN_INTR_STS(x)		SET_BIT_MASK(x, 6, 1)
+#define V_WIN0_EMPTY_INTR_EN(x)		SET_BIT_MASK(x, 8, 1)
+#define V_WIN1_EMPTY_INTR_EN(x)		SET_BIT_MASK(x, 9, 1)
+#define V_WIN2_EMPTY_INTR_EN(x)		SET_BIT_MASK(x, 10, 1)
+#define V_WIN3_EMPTY_INTR_EN(x)		SET_BIT_MASK(x, 11, 1)
+#define V_HWC_EMPTY_INTR_EN(x)		SET_BIT_MASK(x, 12, 1)
+#define V_POST_BUF_EMPTY_INTR_EN(x)	SET_BIT_MASK(x, 13, 1)
+#define V_PWM_GEN_INTR_EN(x)		SET_BIT_MASK(x, 14, 1)
+#define V_WIN0_EMPTY_INTR_CLR(x)	SET_BIT_MASK(x, 16, 1)
+#define V_WIN1_EMPTY_INTR_CLR(x)	SET_BIT_MASK(x, 17, 1)
+#define V_WIN2_EMPTY_INTR_CLR(x)	SET_BIT_MASK(x, 18, 1)
+#define V_WIN3_EMPTY_INTR_CLR(x)	SET_BIT_MASK(x, 19, 1)
+#define V_HWC_EMPTY_INTR_CLR(x)		SET_BIT_MASK(x, 20, 1)
+#define V_POST_BUF_EMPTY_INTR_CLR(x)	SET_BIT_MASK(x, 21, 1)
+#define V_PWM_GEN_INTR_CLR(x)		SET_BIT_MASK(x, 22, 1)
+
+#define M_WIN0_EMPTY_INTR_STS		SET_BIT(1, 0)
+#define M_WIN1_EMPTY_INTR_STS		SET_BIT(1, 1)
+#define M_WIN2_EMPTY_INTR_STS		SET_BIT(1, 2)
+#define M_WIN3_EMPTY_INTR_STS		SET_BIT(1, 3)
+#define M_HWC_EMPTY_INTR_STS		SET_BIT(1, 4)
+#define M_POST_BUF_EMPTY_INTR_STS	SET_BIT(1, 5)
+#define M_PWM_GEN_INTR_STS		SET_BIT(1, 6)
+#define M_WIN0_EMPTY_INTR_EN		SET_BIT(1, 8)
+#define M_WIN1_EMPTY_INTR_EN		SET_BIT(1, 9)
+#define M_WIN2_EMPTY_INTR_EN		SET_BIT(1, 10)
+#define M_WIN3_EMPTY_INTR_EN		SET_BIT(1, 11)
+#define M_HWC_EMPTY_INTR_EN		SET_BIT(1, 12)
+#define M_POST_BUF_EMPTY_INTR_EN	SET_BIT(1, 13)
+#define M_PWM_GEN_INTR_EN		SET_BIT(1, 14)
+#define M_WIN0_EMPTY_INTR_CLR		SET_BIT(1, 16)
+#define M_WIN1_EMPTY_INTR_CLR		SET_BIT(1, 17)
+#define M_WIN2_EMPTY_INTR_CLR		SET_BIT(1, 18)
+#define M_WIN3_EMPTY_INTR_CLR		SET_BIT(1, 19)
+#define M_HWC_EMPTY_INTR_CLR		SET_BIT(1, 20)
+#define M_POST_BUF_EMPTY_INTR_CLR	SET_BIT(1, 21)
+#define M_PWM_GEN_INTR_CLR		SET_BIT(1, 22)
+
+/* win0 register */
+#define WIN0_CTRL0			(0x0030)
+#define V_WIN0_EN(x)			SET_BIT_MASK(x, 0, 1)
+#define V_WIN0_DATA_FMT(x)		SET_BIT_MASK(x, 1, 7)
+#define V_WIN0_FMT_10(x)		SET_BIT_MASK(x, 4, 1)
+#define V_WIN0_LB_MODE(x)		SET_BIT_MASK(x, 5, 7)
+#define V_WIN0_INTERLACE_READ(x)	SET_BIT_MASK(x, 8, 1)
+#define V_WIN0_NO_OUTSTANDING(x)	SET_BIT_MASK(x, 9, 1)
+#define V_WIN0_CSC_MODE(x)		SET_BIT_MASK(x, 10, 3)
+#define V_WIN0_RB_SWAP(x)		SET_BIT_MASK(x, 12, 1)
+#define V_WIN0_ALPHA_SWAP(x)		SET_BIT_MASK(x, 13, 1)
+#define V_WIN0_MID_SWAP(x)		SET_BIT_MASK(x, 14, 1)
+#define V_WIN0_UV_SWAP(x)		SET_BIT_MASK(x, 15, 1)
+#define V_WIN0_PPAS_ZERO_EN(x)		SET_BIT_MASK(x, 16, 1)
+#define V_WIN0_YRGB_DEFLICK(x)		SET_BIT_MASK(x, 18, 1)
+#define V_WIN0_CBR_DEFLICK(x)		SET_BIT_MASK(x, 19, 1)
+#define V_WIN0_YUV_CLIP(x)		SET_BIT_MASK(x, 20, 1)
+
+#define M_WIN0_EN			SET_BIT(1, 0)
+#define M_WIN0_DATA_FMT			SET_BIT(7, 1)
+#define M_WIN0_FMT_10			SET_BIT(1, 4)
+#define M_WIN0_LB_MODE			SET_BIT(7, 5)
+#define M_WIN0_INTERLACE_READ		SET_BIT(1, 8)
+#define M_WIN0_NO_OUTSTANDING		SET_BIT(1, 9)
+#define M_WIN0_CSC_MODE			SET_BIT(3, 10)
+#define M_WIN0_RB_SWAP			SET_BIT(1, 12)
+#define M_WIN0_ALPHA_SWAP		SET_BIT(1, 13)
+#define M_WIN0_MID_SWAP			SET_BIT(1, 14)
+#define M_WIN0_UV_SWAP			SET_BIT(1, 15)
+#define M_WIN0_PPAS_ZERO_EN		SET_BIT(1, 16)
+#define M_WIN0_YRGB_DEFLICK		SET_BIT(1, 18)
+#define M_WIN0_CBR_DEFLICK		SET_BIT(1, 19)
+#define M_WIN0_YUV_CLIP			SET_BIT(1, 20)
+
+#define WIN0_CTRL1			(0x0034)
+#define V_WIN0_YRGB_AXI_GATHER_EN(x)	SET_BIT_MASK(x, 0, 1)
+#define V_WIN0_CBR_AXI_GATHER_EN(x)	SET_BIT_MASK(x, 1, 1)
+#define V_WIN0_BIC_COE_SEL(x)		SET_BIT_MASK(x, 2, 3)
+#define V_WIN0_VSD_YRGB_GT4(x)		SET_BIT_MASK(x, 4, 1)
+#define V_WIN0_VSD_YRGB_GT2(x)		SET_BIT_MASK(x, 5, 1)
+#define V_WIN0_VSD_CBR_GT4(x)		SET_BIT_MASK(x, 6, 1)
+#define V_WIN0_VSD_CBR_GT2(x)		SET_BIT_MASK(x, 7, 1)
+#define V_WIN0_YRGB_AXI_GATHER_NUM(x)	SET_BIT_MASK(x, 8, 0xf)
+#define V_WIN0_CBR_AXI_GATHER_NUM(x)	SET_BIT_MASK(x, 12, 7)
+#define V_WIN0_LINE_LOAD_MODE(x)	SET_BIT_MASK(x, 15, 1)
+#define V_WIN0_YRGB_HOR_SCL_MODE(x)	SET_BIT_MASK(x, 16, 3)
+#define V_WIN0_YRGB_VER_SCL_MODE(x)	SET_BIT_MASK(x, 18, 3)
+#define V_WIN0_YRGB_HSD_MODE(x)		SET_BIT_MASK(x, 20, 3)
+#define V_WIN0_YRGB_VSU_MODE(x)		SET_BIT_MASK(x, 22, 1)
+#define V_WIN0_YRGB_VSD_MODE(x)		SET_BIT_MASK(x, 23, 1)
+#define V_WIN0_CBR_HOR_SCL_MODE(x)	SET_BIT_MASK(x, 24, 3)
+#define V_WIN0_CBR_VER_SCL_MODE(x)	SET_BIT_MASK(x, 26, 3)
+#define V_WIN0_CBR_HSD_MODE(x)		SET_BIT_MASK(x, 28, 3)
+#define V_WIN0_CBR_VSU_MODE(x)		SET_BIT_MASK(x, 30, 1)
+#define V_WIN0_CBR_VSD_MODE(x)		SET_BIT_MASK(x, 31, 1)
+
+#define M_WIN0_YRGB_AXI_GATHER_EN	SET_BIT(1, 0)
+#define M_WIN0_CBR_AXI_GATHER_EN	SET_BIT(1, 1)
+#define M_WIN0_BIC_COE_SEL		SET_BIT(3, 2)
+#define M_WIN0_VSD_YRGB_GT4		SET_BIT(1, 4)
+#define M_WIN0_VSD_YRGB_GT2		SET_BIT(1, 5)
+#define M_WIN0_VSD_CBR_GT4		SET_BIT(1, 6)
+#define M_WIN0_VSD_CBR_GT2		SET_BIT(1, 7)
+#define M_WIN0_YRGB_AXI_GATHER_NUM	SET_BIT(0xf, 8)
+#define M_WIN0_CBR_AXI_GATHER_NUM	SET_BIT(7, 12)
+#define M_WIN0_LINE_LOAD_MODE		SET_BIT(1, 15)
+#define M_WIN0_YRGB_HOR_SCL_MODE	SET_BIT(3, 16)
+#define M_WIN0_YRGB_VER_SCL_MODE	SET_BIT(3, 18)
+#define M_WIN0_YRGB_HSD_MODE		SET_BIT(3, 20)
+#define M_WIN0_YRGB_VSU_MODE		SET_BIT(1, 22)
+#define M_WIN0_YRGB_VSD_MODE		SET_BIT(1, 23)
+#define M_WIN0_CBR_HOR_SCL_MODE		SET_BIT(3, 24)
+#define M_WIN0_CBR_VER_SCL_MODE		SET_BIT(3, 26)
+#define M_WIN0_CBR_HSD_MODE		SET_BIT(3, 28)
+#define M_WIN0_CBR_VSU_MODE		SET_BIT((u32)1, 30)
+#define M_WIN0_CBR_VSD_MODE		SET_BIT((u32)1, 31)
+
+#define WIN0_COLOR_KEY			(0x0038)
+#define V_WIN0_COLOR_KEY(x)		SET_BIT_MASK(x, 0, 0x3fffffff)
+#define V_WIN0_COLOR_KEY_EN(x)		SET_BIT_MASK(x, 31, 1)
+#define M_WIN0_COLOR_KEY		SET_BIT(0x3fffffff, 0)
+#define M_WIN0_COLOR_KEY_EN		SET_BIT((u32)1, 31)
+
+#define WIN0_VIR			(0x003c)
+#define V_WIN0_VIR_STRIDE(x)		SET_BIT_MASK(x, 0, 0x3fff)
+#define V_WIN0_VIR_STRIDE_UV(x)		SET_BIT_MASK(x, 16, 0x3fff)
+#define M_WIN0_VIR_STRIDE		SET_BIT(0x3fff, 0)
+#define M_WIN0_VIR_STRIDE_UV		SET_BIT(0x3fff, 16)
+
+#define WIN0_YRGB_MST			(0x0040)
+#define WIN0_CBR_MST			(0x0044)
+#define WIN0_ACT_INFO			(0x0048)
+#define V_WIN0_ACT_WIDTH(x)		SET_BIT_MASK(x-1, 0, 0x1fff)
+#define V_WIN0_ACT_HEIGHT(x)		SET_BIT_MASK(x-1, 16, 0x1fff)
+#define M_WIN0_ACT_WIDTH		SET_BIT(0x1fff, 0)
+#define M_WIN0_ACT_HEIGHT		SET_BIT(0x1fff, 16)
+
+#define WIN0_DSP_INFO			(0x004c)
+#define V_WIN0_DSP_WIDTH(x)		SET_BIT_MASK(x-1, 0, 0xfff)
+#define V_WIN0_DSP_HEIGHT(x)		SET_BIT_MASK(x-1, 16, 0xfff)
+#define M_WIN0_DSP_WIDTH		SET_BIT(0xfff, 0)
+#define M_WIN0_DSP_HEIGHT		SET_BIT(0xfff, 16)
+
+#define WIN0_DSP_ST			(0x0050)
+#define V_WIN0_DSP_XST(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN0_DSP_YST(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN0_DSP_XST			SET_BIT(0x1fff, 0)
+#define M_WIN0_DSP_YST			SET_BIT(0x1fff, 16)
+
+#define WIN0_SCL_FACTOR_YRGB		(0x0054)
+#define V_WIN0_HS_FACTOR_YRGB(x)	SET_BIT_MASK(x, 0, 0xffff)
+#define V_WIN0_VS_FACTOR_YRGB(x)	SET_BIT_MASK(x, 16, 0xffff)
+#define M_WIN0_HS_FACTOR_YRGB		SET_BIT(0xffff, 0)
+#define M_WIN0_VS_FACTOR_YRGB		SET_BIT((u32)0xffff, 16)
+
+#define WIN0_SCL_FACTOR_CBR		(0x0058)
+#define V_WIN0_HS_FACTOR_CBR(x)		SET_BIT_MASK(x, 0, 0xffff)
+#define V_WIN0_VS_FACTOR_CBR(x)		SET_BIT_MASK(x, 16, 0xffff)
+#define M_WIN0_HS_FACTOR_CBR		SET_BIT(0xffff, 0)
+#define M_WIN0_VS_FACTOR_CBR		SET_BIT((u32)0xffff, 16)
+
+#define WIN0_SCL_OFFSET			(0x005c)
+#define V_WIN0_HS_OFFSET_YRGB(x)	SET_BIT_MASK(x, 0, 0xff)
+#define V_WIN0_HS_OFFSET_CBR(x)		SET_BIT_MASK(x, 8, 0xff)
+#define V_WIN0_VS_OFFSET_YRGB(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_WIN0_VS_OFFSET_CBR(x)		SET_BIT_MASK(x, 24, 0xff)
+
+#define M_WIN0_HS_OFFSET_YRGB		SET_BIT(0xff, 0)
+#define M_WIN0_HS_OFFSET_CBR		SET_BIT(0xff, 8)
+#define M_WIN0_VS_OFFSET_YRGB		SET_BIT(0xff, 16)
+#define M_WIN0_VS_OFFSET_CBR		SET_BIT((u32)0xff, 24)
+
+#define WIN0_SRC_ALPHA_CTRL		(0x0060)
+#define V_WIN0_SRC_ALPHA_EN(x)		SET_BIT_MASK(x, 0, 1)
+#define V_WIN0_SRC_COLOR_M0(x)		SET_BIT_MASK(x, 1, 1)
+#define V_WIN0_SRC_ALPHA_M0(x)		SET_BIT_MASK(x, 2, 1)
+#define V_WIN0_SRC_BLEND_M0(x)		SET_BIT_MASK(x, 3, 3)
+#define V_WIN0_SRC_ALPHA_CAL_M0(x)	SET_BIT_MASK(x, 5, 1)
+#define V_WIN0_SRC_FACTOR_M0(x)		SET_BIT_MASK(x, 6, 7)
+#define V_WIN0_SRC_GLOBAL_ALPHA(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_WIN0_FADING_VALUE(x)		SET_BIT_MASK(x, 24, 0xff)
+
+#define M_WIN0_SRC_ALPHA_EN		SET_BIT(1, 0)
+#define M_WIN0_SRC_COLOR_M0		SET_BIT(1, 1)
+#define M_WIN0_SRC_ALPHA_M0		SET_BIT(1, 2)
+#define M_WIN0_SRC_BLEND_M0		SET_BIT(3, 3)
+#define M_WIN0_SRC_ALPHA_CAL_M0		SET_BIT(1, 5)
+#define M_WIN0_SRC_FACTOR_M0		SET_BIT(7, 6)
+#define M_WIN0_SRC_GLOBAL_ALPHA		SET_BIT(0xff, 16)
+#define M_WIN0_FADING_VALUE		SET_BIT(0xff, 24)
+
+#define WIN0_DST_ALPHA_CTRL		(0x0064)
+#define V_WIN0_DST_FACTOR_M0(x)		SET_BIT_MASK(x, 6, 7)
+#define M_WIN0_DST_FACTOR_M0		SET_BIT(7, 6)
+
+#define WIN0_FADING_CTRL		(0x0068)
+#define V_WIN0_FADING_OFFSET_R(x)	SET_BIT_MASK(x, 0, 0xff)
+#define V_WIN0_FADING_OFFSET_G(x)	SET_BIT_MASK(x, 8, 0xff)
+#define V_WIN0_FADING_OFFSET_B(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_WIN0_FADING_EN(x)		SET_BIT_MASK(x, 24, 1)
+
+#define M_WIN0_FADING_OFFSET_R		SET_BIT(0xff, 0)
+#define M_WIN0_FADING_OFFSET_G		SET_BIT(0xff, 8)
+#define M_WIN0_FADING_OFFSET_B		SET_BIT(0xff, 16)
+#define M_WIN0_FADING_EN		SET_BIT(1, 24)
+
+/* win1 register */
+#define WIN1_CTRL0			(0x0070)
+#define V_WIN1_EN(x)			SET_BIT_MASK(x, 0, 1)
+#define V_WIN1_DATA_FMT(x)		SET_BIT_MASK(x, 1, 7)
+#define V_WIN1_FMT_10(x)		SET_BIT_MASK(x, 4, 1)
+#define V_WIN1_LB_MODE(x)		SET_BIT_MASK(x, 5, 7)
+#define V_WIN1_INTERLACE_READ_MODE(x)	SET_BIT_MASK(x, 8, 1)
+#define V_WIN1_NO_OUTSTANDING(x)	SET_BIT_MASK(x, 9, 1)
+#define V_WIN1_CSC_MODE(x)		SET_BIT_MASK(x, 10, 3)
+#define V_WIN1_RB_SWAP(x)		SET_BIT_MASK(x, 12, 1)
+#define V_WIN1_ALPHA_SWAP(x)		SET_BIT_MASK(x, 13, 1)
+#define V_WIN1_MID_SWAP(x)		SET_BIT_MASK(x, 14, 1)
+#define V_WIN1_UV_SWAP(x)		SET_BIT_MASK(x, 15, 1)
+#define V_WIN1_PPAS_ZERO_EN(x)		SET_BIT_MASK(x, 16, 1)
+#define V_WIN1_YRGB_DEFLICK(x)		SET_BIT_MASK(x, 18, 1)
+#define V_WIN1_CBR_DEFLICK(x)		SET_BIT_MASK(x, 19, 1)
+#define V_WIN1_YUV_CLIP(x)		SET_BIT_MASK(x, 20, 1)
+
+#define M_WIN1_EN			SET_BIT(1, 0)
+#define M_WIN1_DATA_FMT			SET_BIT(7, 1)
+#define M_WIN1_FMT_10			SET_BIT(1, 4)
+#define M_WIN1_LB_MODE			SET_BIT(7, 5)
+#define M_WIN1_INTERLACE_READ_MODE	SET_BIT(1, 8)
+#define M_WIN1_NO_OUTSTANDING		SET_BIT(1, 9)
+#define M_WIN1_CSC_MODE			SET_BIT(3, 10)
+#define M_WIN1_RB_SWAP			SET_BIT(1, 12)
+#define M_WIN1_ALPHA_SWAP		SET_BIT(1, 13)
+#define M_WIN1_MID_SWAP			SET_BIT(1, 14)
+#define M_WIN1_UV_SWAP			SET_BIT(1, 15)
+#define M_WIN1_PPAS_ZERO_EN		SET_BIT(1, 16)
+#define M_WIN1_YRGB_DEFLICK		SET_BIT(1, 18)
+#define M_WIN1_CBR_DEFLICK		SET_BIT(1, 19)
+#define M_WIN1_YUV_CLIP			SET_BIT(1, 20)
+
+#define WIN1_CTRL1			(0x0074)
+#define V_WIN1_YRGB_AXI_GATHER_EN(x)	SET_BIT_MASK(x, 0, 1)
+#define V_WIN1_CBR_AXI_GATHER_EN(x)	SET_BIT_MASK(x, 1, 1)
+#define V_WIN1_BIC_COE_SEL(x)		SET_BIT_MASK(x, 2, 3)
+#define V_WIN1_VSD_YRGB_GT4(x)		SET_BIT_MASK(x, 4, 1)
+#define V_WIN1_VSD_YRGB_GT2(x)		SET_BIT_MASK(x, 5, 1)
+#define V_WIN1_VSD_CBR_GT4(x)		SET_BIT_MASK(x, 6, 1)
+#define V_WIN1_VSD_CBR_GT2(x)		SET_BIT_MASK(x, 7, 1)
+#define V_WIN1_YRGB_AXI_GATHER_NUM(x)	SET_BIT_MASK(x, 8, 0xf)
+#define V_WIN1_CBR_AXI_GATHER_NUM(x)	SET_BIT_MASK(x, 12, 7)
+#define V_WIN1_LINE_LOAD_MODE(x)	SET_BIT_MASK(x, 15, 1)
+#define V_WIN1_YRGB_HOR_SCL_MODE(x)	SET_BIT_MASK(x, 16, 3)
+#define V_WIN1_YRGB_VER_SCL_MODE(x)	SET_BIT_MASK(x, 18, 3)
+#define V_WIN1_YRGB_HSD_MODE(x)		SET_BIT_MASK(x, 20, 3)
+#define V_WIN1_YRGB_VSU_MODE(x)		SET_BIT_MASK(x, 22, 1)
+#define V_WIN1_YRGB_VSD_MODE(x)		SET_BIT_MASK(x, 23, 1)
+#define V_WIN1_CBR_HOR_SCL_MODE(x)	SET_BIT_MASK(x, 24, 3)
+#define V_WIN1_CBR_VER_SCL_MODE(x)	SET_BIT_MASK(x, 26, 3)
+#define V_WIN1_CBR_HSD_MODE(x)		SET_BIT_MASK(x, 28, 3)
+#define V_WIN1_CBR_VSU_MODE(x)		SET_BIT_MASK(x, 30, 1)
+#define V_WIN1_CBR_VSD_MODE(x)		SET_BIT_MASK(x, 31, 1)
+
+#define M_WIN1_YRGB_AXI_GATHER_EN	SET_BIT(1, 0)
+#define M_WIN1_CBR_AXI_GATHER_EN	SET_BIT(1, 1)
+#define M_WIN1_BIC_COE_SEL		SET_BIT(3, 2)
+#define M_WIN1_VSD_YRGB_GT4		SET_BIT(1, 4)
+#define M_WIN1_VSD_YRGB_GT2		SET_BIT(1, 5)
+#define M_WIN1_VSD_CBR_GT4		SET_BIT(1, 6)
+#define M_WIN1_VSD_CBR_GT2		SET_BIT(1, 7)
+#define M_WIN1_YRGB_AXI_GATHER_NUM	SET_BIT(0xf, 8)
+#define M_WIN1_CBR_AXI_GATHER_NUM	SET_BIT(7, 12)
+#define M_WIN1_LINE_LOAD_MODE		SET_BIT(1, 15)
+#define M_WIN1_YRGB_HOR_SCL_MODE	SET_BIT(3, 16)
+#define M_WIN1_YRGB_VER_SCL_MODE	SET_BIT(3, 18)
+#define M_WIN1_YRGB_HSD_MODE		SET_BIT(3, 20)
+#define M_WIN1_YRGB_VSU_MODE		SET_BIT(1, 22)
+#define M_WIN1_YRGB_VSD_MODE		SET_BIT(1, 23)
+#define M_WIN1_CBR_HOR_SCL_MODE		SET_BIT(3, 24)
+#define M_WIN1_CBR_VER_SCL_MODE		SET_BIT(3, 26)
+#define M_WIN1_CBR_HSD_MODE		SET_BIT(3, 28)
+#define M_WIN1_CBR_VSU_MODE		SET_BIT(1, 30)
+#define M_WIN1_CBR_VSD_MODE		SET_BIT((u32)1, 31)
+
+#define WIN1_COLOR_KEY			(0x0078)
+#define V_WIN1_COLOR_KEY(x)		SET_BIT_MASK(x, 0, 0x3fffffff)
+#define V_WIN1_COLOR_KEY_EN(x)		SET_BIT_MASK(x, 31, 1)
+#define M_WIN1_COLOR_KEY		SET_BIT(0x3fffffff, 0)
+#define M_WIN1_COLOR_KEY_EN		SET_BIT((u32)1, 31)
+
+#define WIN1_VIR			(0x007c)
+#define V_WIN1_VIR_STRIDE(x)		SET_BIT_MASK(x, 0, 0x3fff)
+#define V_WIN1_VIR_STRIDE_UV(x)		SET_BIT_MASK(x, 16, 0x3fff)
+#define M_WIN1_VIR_STRIDE		SET_BIT(0x3fff, 0)
+#define M_WIN1_VIR_STRIDE_UV		SET_BIT(0x3fff, 16)
+
+#define WIN1_YRGB_MST			(0x0080)
+#define WIN1_CBR_MST			(0x0084)
+#define WIN1_ACT_INFO			(0x0088)
+#define V_WIN1_ACT_WIDTH(x)		SET_BIT_MASK(x-1, 0, 0x1fff)
+#define V_WIN1_ACT_HEIGHT(x)		SET_BIT_MASK(x-1, 16, 0x1fff)
+#define M_WIN1_ACT_WIDTH		SET_BIT(0x1fff, 0)
+#define M_WIN1_ACT_HEIGHT		SET_BIT(0x1fff, 16)
+
+#define WIN1_DSP_INFO			(0x008c)
+#define V_WIN1_DSP_WIDTH(x)		SET_BIT_MASK(x-1, 0, 0xfff)
+#define V_WIN1_DSP_HEIGHT(x)		SET_BIT_MASK(x-1, 16, 0xfff)
+#define M_WIN1_DSP_WIDTH		SET_BIT(0xfff, 0)
+#define M_WIN1_DSP_HEIGHT		SET_BIT(0xfff, 16)
+
+#define WIN1_DSP_ST			(0x0090)
+#define V_WIN1_DSP_XST(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN1_DSP_YST(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN1_DSP_XST			SET_BIT(0x1fff, 0)
+#define M_WIN1_DSP_YST			SET_BIT(0x1fff, 16)
+
+#define WIN1_SCL_FACTOR_YRGB		(0x0094)
+#define V_WIN1_HS_FACTOR_YRGB(x)	SET_BIT_MASK(x, 0, 0xffff)
+#define V_WIN1_VS_FACTOR_YRGB(x)	SET_BIT_MASK(x, 16, 0xffff)
+#define M_WIN1_HS_FACTOR_YRGB		SET_BIT(0xffff, 0)
+#define M_WIN1_VS_FACTOR_YRGB		SET_BIT((u32)0xffff, 16)
+
+#define WIN1_SCL_FACTOR_CBR		(0x0098)
+#define V_WIN1_HS_FACTOR_CBR(x)		SET_BIT_MASK(x, 0, 0xffff)
+#define V_WIN1_VS_FACTOR_CBR(x)		SET_BIT_MASK(x, 16, 0xffff)
+#define M_WIN1_HS_FACTOR_CBR		SET_BIT(0xffff, 0)
+#define M_WIN1_VS_FACTOR_CBR		SET_BIT((u32)0xffff, 16)
+
+#define WIN1_SCL_OFFSET			(0x009c)
+#define V_WIN1_HS_OFFSET_YRGB(x)	SET_BIT_MASK(x, 0, 0xff)
+#define V_WIN1_HS_OFFSET_CBR(x)		SET_BIT_MASK(x, 8, 0xff)
+#define V_WIN1_VS_OFFSET_YRGB(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_WIN1_VS_OFFSET_CBR(x)		SET_BIT_MASK(x, 24, 0xff)
+
+#define M_WIN1_HS_OFFSET_YRGB		SET_BIT(0xff, 0)
+#define M_WIN1_HS_OFFSET_CBR		SET_BIT(0xff, 8)
+#define M_WIN1_VS_OFFSET_YRGB		SET_BIT(0xff, 16)
+#define M_WIN1_VS_OFFSET_CBR		SET_BIT((u32)0xff, 24)
+
+#define WIN1_SRC_ALPHA_CTRL		(0x00a0)
+#define V_WIN1_SRC_ALPHA_EN(x)		SET_BIT_MASK(x, 0, 1)
+#define V_WIN1_SRC_COLOR_M0(x)		SET_BIT_MASK(x, 1, 1)
+#define V_WIN1_SRC_ALPHA_M0(x)		SET_BIT_MASK(x, 2, 1)
+#define V_WIN1_SRC_BLEND_M0(x)		SET_BIT_MASK(x, 3, 3)
+#define V_WIN1_SRC_ALPHA_CAL_M0(x)	SET_BIT_MASK(x, 5, 1)
+#define V_WIN1_SRC_FACTOR_M0(x)		SET_BIT_MASK(x, 6, 7)
+#define V_WIN1_SRC_GLOBAL_ALPHA(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_WIN1_FADING_VALUE(x)		SET_BIT_MASK(x, 24, 0xff)
+
+#define M_WIN1_SRC_ALPHA_EN		SET_BIT(1, 0)
+#define M_WIN1_SRC_COLOR_M0		SET_BIT(1, 1)
+#define M_WIN1_SRC_ALPHA_M0		SET_BIT(1, 2)
+#define M_WIN1_SRC_BLEND_M0		SET_BIT(3, 3)
+#define M_WIN1_SRC_ALPHA_CAL_M0		SET_BIT(1, 5)
+#define M_WIN1_SRC_FACTOR_M0		SET_BIT(7, 6)
+#define M_WIN1_SRC_GLOBAL_ALPHA		SET_BIT(0xff, 16)
+#define M_WIN1_FADING_VALUE		SET_BIT(0xff, 24)
+
+#define WIN1_DST_ALPHA_CTRL		(0x00a4)
+#define V_WIN1_DST_FACTOR_M0(x)		SET_BIT_MASK(x, 6, 7)
+#define M_WIN1_DST_FACTOR_M0		SET_BIT(7, 6)
+
+#define WIN1_FADING_CTRL		(0x00a8)
+#define V_WIN1_FADING_OFFSET_R(x)	SET_BIT_MASK(x, 0, 0xff)
+#define V_WIN1_FADING_OFFSET_G(x)	SET_BIT_MASK(x, 8, 0xff)
+#define V_WIN1_FADING_OFFSET_B(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_WIN1_FADING_EN(x)		SET_BIT_MASK(x, 24, 1)
+
+#define M_WIN1_FADING_OFFSET_R		SET_BIT(0xff, 0)
+#define M_WIN1_FADING_OFFSET_G		SET_BIT(0xff, 8)
+#define M_WIN1_FADING_OFFSET_B		SET_BIT(0xff, 16)
+#define M_WIN1_FADING_EN		SET_BIT(1, 24)
+
+/* win2 register */
+#define WIN2_CTRL0			(0x00b0)
+#define V_WIN2_EN(x)			SET_BIT_MASK(x, 0, 1)
+#define V_WIN2_DATA_FMT(x)		SET_BIT_MASK(x, 1, 7)
+#define V_WIN2_MST0_EN(x)		SET_BIT_MASK(x, 4, 1)
+#define V_WIN2_MST1_EN(x)		SET_BIT_MASK(x, 5, 1)
+#define V_WIN2_MST2_EN(x)		SET_BIT_MASK(x, 6, 1)
+#define V_WIN2_MST3_EN(x)		SET_BIT_MASK(x, 7, 1)
+#define V_WIN2_INTERLACE_READ(x)	SET_BIT_MASK(x, 8, 1)
+#define V_WIN2_NO_OUTSTANDING(x)	SET_BIT_MASK(x, 9, 1)
+#define V_WIN2_CSC_MODE(x)		SET_BIT_MASK(x, 10, 1)
+#define V_WIN2_RB_SWAP(x)		SET_BIT_MASK(x, 12, 1)
+#define V_WIN2_ALPHA_SWAP(x)		SET_BIT_MASK(x, 13, 1)
+#define V_WIN2_ENDIAN_MODE(x)		SET_BIT_MASK(x, 14, 1)
+#define V_WIN2_LUT_EN(x)		SET_BIT_MASK(x, 18, 1)
+
+#define M_WIN2_EN			SET_BIT(1, 0)
+#define M_WIN2_DATA_FMT			SET_BIT(7, 1)
+#define M_WIN2_MST0_EN			SET_BIT(1, 4)
+#define M_WIN2_MST1_EN			SET_BIT(1, 5)
+#define M_WIN2_MST2_EN			SET_BIT(1, 6)
+#define M_WIN2_MST3_EN			SET_BIT(1, 7)
+#define M_WIN2_INTERLACE_READ		SET_BIT(1, 8)
+#define M_WIN2_NO_OUTSTANDING		SET_BIT(1, 9)
+#define M_WIN2_CSC_MODE			SET_BIT(1, 10)
+#define M_WIN2_RB_SWAP			SET_BIT(1, 12)
+#define M_WIN2_ALPHA_SWAP		SET_BIT(1, 13)
+#define M_WIN2_ENDIAN_MODE		SET_BIT(1, 14)
+#define M_WIN2_LUT_EN			SET_BIT(1, 18)
+
+#define WIN2_CTRL1			(0x00b4)
+#define V_WIN2_AXI_GATHER_EN(x)		SET_BIT_MASK(x, 0, 1)
+#define V_WIN2_AXI_GATHER_NUM(x)	SET_BIT_MASK(x, 4, 0xf)
+#define M_WIN2_AXI_GATHER_EN		SET_BIT(1, 0)
+#define M_WIN2_AXI_GATHER_NUM		SET_BIT(0xf, 4)
+
+#define WIN2_VIR0_1			(0x00b8)
+#define V_WIN2_VIR_STRIDE0(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN2_VIR_STRIDE1(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN2_VIR_STRIDE0		SET_BIT(0x1fff, 0)
+#define M_WIN2_VIR_STRIDE1		SET_BIT(0x1fff, 16)
+
+#define WIN2_VIR2_3			(0x00bc)
+#define V_WIN2_VIR_STRIDE2(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN2_VIR_STRIDE3(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN2_VIR_STRIDE2		SET_BIT(0x1fff, 0)
+#define M_WIN2_VIR_STRIDE3		SET_BIT(0x1fff, 16)
+
+#define WIN2_MST0			(0x00c0)
+#define WIN2_DSP_INFO0			(0x00c4)
+#define V_WIN2_DSP_WIDTH0(x)		SET_BIT_MASK(x-1, 0, 0xfff)
+#define V_WIN2_DSP_HEIGHT0(x)		SET_BIT_MASK(x-1, 16, 0xfff)
+#define M_WIN2_DSP_WIDTH0		SET_BIT(0xfff, 0)
+#define M_WIN2_DSP_HEIGHT0		SET_BIT(0xfff, 16)
+
+#define WIN2_DSP_ST0			(0x00c8)
+#define V_WIN2_DSP_XST0(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN2_DSP_YST0(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN2_DSP_XST0			SET_BIT(0x1fff, 0)
+#define M_WIN2_DSP_YST0			SET_BIT(0x1fff, 16)
+
+#define WIN2_COLOR_KEY			(0x00cc)
+#define V_WIN2_COLOR_KEY(x)		SET_BIT_MASK(x, 0, 0xffffff)
+#define V_WIN2_KEY_EN(x)		SET_BIT_MASK(x, 24, 1)
+#define M_WIN2_COLOR_KEY		SET_BIT(0xffffff, 0)
+#define M_WIN2_KEY_EN			SET_BIT((u32)1, 24)
+
+#define WIN2_MST1			(0x00d0)
+#define WIN2_DSP_INFO1			(0x00d4)
+#define V_WIN2_DSP_WIDTH1(x)		SET_BIT_MASK(x-1, 0, 0xfff)
+#define V_WIN2_DSP_HEIGHT1(x)		SET_BIT_MASK(x-1, 16, 0xfff)
+
+#define M_WIN2_DSP_WIDTH1		SET_BIT(0xfff, 0)
+#define M_WIN2_DSP_HEIGHT1		SET_BIT(0xfff, 16)
+
+#define WIN2_DSP_ST1			(0x00d8)
+#define V_WIN2_DSP_XST1(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN2_DSP_YST1(x)		SET_BIT_MASK(x, 16, 0x1fff)
+
+#define M_WIN2_DSP_XST1			SET_BIT(0x1fff, 0)
+#define M_WIN2_DSP_YST1			SET_BIT(0x1fff, 16)
+
+#define WIN2_SRC_ALPHA_CTRL		(0x00dc)
+#define V_WIN2_SRC_ALPHA_EN(x)		SET_BIT_MASK(x, 0, 1)
+#define V_WIN2_SRC_COLOR_M0(x)		SET_BIT_MASK(x, 1, 1)
+#define V_WIN2_SRC_ALPHA_M0(x)		SET_BIT_MASK(x, 2, 1)
+#define V_WIN2_SRC_BLEND_M0(x)		SET_BIT_MASK(x, 3, 3)
+#define V_WIN2_SRC_ALPHA_CAL_M0(x)	SET_BIT_MASK(x, 5, 1)
+#define V_WIN2_SRC_FACTOR_M0(x)		SET_BIT_MASK(x, 6, 7)
+#define V_WIN2_SRC_GLOBAL_ALPHA(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_WIN2_FADING_VALUE(x)		SET_BIT_MASK(x, 24, 0xff)
+
+#define M_WIN2_SRC_ALPHA_EN		SET_BIT(1, 0)
+#define M_WIN2_SRC_COLOR_M0		SET_BIT(1, 1)
+#define M_WIN2_SRC_ALPHA_M0		SET_BIT(1, 2)
+#define M_WIN2_SRC_BLEND_M0		SET_BIT(3, 3)
+#define M_WIN2_SRC_ALPHA_CAL_M0		SET_BIT(1, 5)
+#define M_WIN2_SRC_FACTOR_M0		SET_BIT(7, 6)
+#define M_WIN2_SRC_GLOBAL_ALPHA		SET_BIT(0xff, 16)
+#define M_WIN2_FADING_VALUE		SET_BIT(0xff, 24)
+
+#define WIN2_MST2			(0x00e0)
+#define WIN2_DSP_INFO2			(0x00e4)
+#define V_WIN2_DSP_WIDTH2(x)		SET_BIT_MASK(x-1, 0, 0xfff)
+#define V_WIN2_DSP_HEIGHT2(x)		SET_BIT_MASK(x-1, 16, 0xfff)
+
+#define M_WIN2_DSP_WIDTH2		SET_BIT(0xfff, 0)
+#define M_WIN2_DSP_HEIGHT2		SET_BIT(0xfff, 16)
+
+#define WIN2_DSP_ST2			(0x00e8)
+#define V_WIN2_DSP_XST2(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN2_DSP_YST2(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN2_DSP_XST2			SET_BIT(0x1fff, 0)
+#define M_WIN2_DSP_YST2			SET_BIT(0x1fff, 16)
+
+#define WIN2_DST_ALPHA_CTRL		(0x00ec)
+#define V_WIN2_DST_FACTOR_M0(x)		SET_BIT_MASK(x, 6, 7)
+#define M_WIN2_DST_FACTOR_M0		SET_BIT(7, 6)
+
+#define WIN2_MST3			(0x00f0)
+#define WIN2_DSP_INFO3			(0x00f4)
+#define V_WIN2_DSP_WIDTH3(x)		SET_BIT_MASK(x-1, 0, 0xfff)
+#define V_WIN2_DSP_HEIGHT3(x)		SET_BIT_MASK(x-1, 16, 0xfff)
+#define M_WIN2_DSP_WIDTH3		SET_BIT(0xfff, 0)
+#define M_WIN2_DSP_HEIGHT3		SET_BIT(0xfff, 16)
+
+#define WIN2_DSP_ST3			(0x00f8)
+#define V_WIN2_DSP_XST3(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN2_DSP_YST3(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN2_DSP_XST3			SET_BIT(0x1fff, 0)
+#define M_WIN2_DSP_YST3			SET_BIT(0x1fff, 16)
+
+#define WIN2_FADING_CTRL		(0x00fc)
+#define V_WIN2_FADING_OFFSET_R(x)	SET_BIT_MASK(x, 0, 0xff)
+#define V_WIN2_FADING_OFFSET_G(x)	SET_BIT_MASK(x, 8, 0xff)
+#define V_WIN2_FADING_OFFSET_B(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_WIN2_FADING_EN(x)		SET_BIT_MASK(x, 24, 1)
+
+#define M_WIN2_FADING_OFFSET_R		SET_BIT(0xff, 0)
+#define M_WIN2_FADING_OFFSET_G		SET_BIT(0xff, 8)
+#define M_WIN2_FADING_OFFSET_B		SET_BIT(0xff, 16)
+#define M_WIN2_FADING_EN		SET_BIT(1, 24)
+
+/* win3 register */
+#define WIN3_CTRL0			(0x0100)
+#define V_WIN3_EN(x)			SET_BIT_MASK(x, 0, 1)
+#define V_WIN3_DATA_FMT(x)		SET_BIT_MASK(x, 1, 7)
+#define V_WIN3_MST0_EN(x)		SET_BIT_MASK(x, 4, 1)
+#define V_WIN3_MST1_EN(x)		SET_BIT_MASK(x, 5, 1)
+#define V_WIN3_MST2_EN(x)		SET_BIT_MASK(x, 6, 1)
+#define V_WIN3_MST3_EN(x)		SET_BIT_MASK(x, 7, 1)
+#define V_WIN3_INTERLACE_READ(x)	SET_BIT_MASK(x, 8, 1)
+#define V_WIN3_NO_OUTSTANDING(x)	SET_BIT_MASK(x, 9, 1)
+#define V_WIN3_CSC_MODE(x)		SET_BIT_MASK(x, 10, 1)
+#define V_WIN3_RB_SWAP(x)		SET_BIT_MASK(x, 12, 1)
+#define V_WIN3_ALPHA_SWAP(x)		SET_BIT_MASK(x, 13, 1)
+#define V_WIN3_ENDIAN_MODE(x)		SET_BIT_MASK(x, 14, 1)
+#define V_WIN3_LUT_EN(x)		SET_BIT_MASK(x, 18, 1)
+
+#define M_WIN3_EN			SET_BIT(1, 0)
+#define M_WIN3_DATA_FMT			SET_BIT(7, 1)
+#define M_WIN3_MST0_EN			SET_BIT(1, 4)
+#define M_WIN3_MST1_EN			SET_BIT(1, 5)
+#define M_WIN3_MST2_EN			SET_BIT(1, 6)
+#define M_WIN3_MST3_EN			SET_BIT(1, 7)
+#define M_WIN3_INTERLACE_READ		SET_BIT(1, 8)
+#define M_WIN3_NO_OUTSTANDING		SET_BIT(1, 9)
+#define M_WIN3_CSC_MODE			SET_BIT(1, 10)
+#define M_WIN3_RB_SWAP			SET_BIT(1, 12)
+#define M_WIN3_ALPHA_SWAP		SET_BIT(1, 13)
+#define M_WIN3_ENDIAN_MODE		SET_BIT(1, 14)
+#define M_WIN3_LUT_EN			SET_BIT(1, 18)
+
+#define WIN3_CTRL1			(0x0104)
+#define V_WIN3_AXI_GATHER_EN(x)		SET_BIT_MASK(x, 0, 1)
+#define V_WIN3_AXI_GATHER_NUM(x)	SET_BIT_MASK(x, 4, 0xf)
+#define M_WIN3_AXI_GATHER_EN		SET_BIT(1, 0)
+#define M_WIN3_AXI_GATHER_NUM		SET_BIT(0xf, 4)
+
+#define WIN3_VIR0_1			(0x0108)
+#define V_WIN3_VIR_STRIDE0(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN3_VIR_STRIDE1(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN3_VIR_STRIDE0		SET_BIT(0x1fff, 0)
+#define M_WIN3_VIR_STRIDE1		SET_BIT(0x1fff, 16)
+
+#define WIN3_VIR2_3			(0x010c)
+#define V_WIN3_VIR_STRIDE2(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN3_VIR_STRIDE3(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN3_VIR_STRIDE2		SET_BIT(0x1fff, 0)
+#define M_WIN3_VIR_STRIDE3		SET_BIT(0x1fff, 16)
+
+#define WIN3_MST0			(0x0110)
+#define WIN3_DSP_INFO0			(0x0114)
+#define V_WIN3_DSP_WIDTH0(x)		SET_BIT_MASK(x-1, 0, 0xfff)
+#define V_WIN3_DSP_HEIGHT0(x)		SET_BIT_MASK(x-1, 16, 0xfff)
+#define M_WIN3_DSP_WIDTH0		SET_BIT(0xfff, 0)
+#define M_WIN3_DSP_HEIGHT0		SET_BIT(0xfff, 16)
+
+#define WIN3_DSP_ST0			(0x0118)
+#define V_WIN3_DSP_XST0(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN3_DSP_YST0(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN3_DSP_XST0			SET_BIT(0x1fff, 0)
+#define M_WIN3_DSP_YST0			SET_BIT(0x1fff, 16)
+
+#define WIN3_COLOR_KEY			(0x011c)
+#define V_WIN3_COLOR_KEY(x)		SET_BIT_MASK(x, 0, 0xffffff)
+#define V_WIN3_KEY_EN(x)		SET_BIT_MASK(x, 24, 1)
+#define M_WIN3_COLOR_KEY		SET_BIT(0xffffff, 0)
+#define M_WIN3_KEY_EN			SET_BIT((u32)1, 24)
+
+#define WIN3_MST1			(0x0120)
+#define WIN3_DSP_INFO1			(0x0124)
+#define V_WIN3_DSP_WIDTH1(x)		SET_BIT_MASK(x-1, 0, 0xfff)
+#define V_WIN3_DSP_HEIGHT1(x)		SET_BIT_MASK(x-1, 16, 0xfff)
+#define M_WIN3_DSP_WIDTH1		SET_BIT(0xfff, 0)
+#define M_WIN3_DSP_HEIGHT1		SET_BIT(0xfff, 16)
+
+#define WIN3_DSP_ST1			(0x0128)
+#define V_WIN3_DSP_XST1(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN3_DSP_YST1(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN3_DSP_XST1			SET_BIT(0x1fff, 0)
+#define M_WIN3_DSP_YST1			SET_BIT(0x1fff, 16)
+
+#define WIN3_SRC_ALPHA_CTRL		(0x012c)
+#define V_WIN3_SRC_ALPHA_EN(x)		SET_BIT_MASK(x, 0, 1)
+#define V_WIN3_SRC_COLOR_M0(x)		SET_BIT_MASK(x, 1, 1)
+#define V_WIN3_SRC_ALPHA_M0(x)		SET_BIT_MASK(x, 2, 1)
+#define V_WIN3_SRC_BLEND_M0(x)		SET_BIT_MASK(x, 3, 3)
+#define V_WIN3_SRC_ALPHA_CAL_M0(x)	SET_BIT_MASK(x, 5, 1)
+#define V_WIN3_SRC_FACTOR_M0(x)		SET_BIT_MASK(x, 6, 7)
+#define V_WIN3_SRC_GLOBAL_ALPHA(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_WIN3_FADING_VALUE(x)		SET_BIT_MASK(x, 24, 0xff)
+
+#define M_WIN3_SRC_ALPHA_EN		SET_BIT(1, 0)
+#define M_WIN3_SRC_COLOR_M0		SET_BIT(1, 1)
+#define M_WIN3_SRC_ALPHA_M0		SET_BIT(1, 2)
+#define M_WIN3_SRC_BLEND_M0		SET_BIT(3, 3)
+#define M_WIN3_SRC_ALPHA_CAL_M0		SET_BIT(1, 5)
+#define M_WIN3_SRC_FACTOR_M0		SET_BIT(7, 6)
+#define M_WIN3_SRC_GLOBAL_ALPHA		SET_BIT(0xff, 16)
+#define M_WIN3_FADING_VALUE		SET_BIT(0xff, 24)
+
+#define WIN3_MST2			(0x0130)
+#define WIN3_DSP_INFO2			(0x0134)
+#define V_WIN3_DSP_WIDTH2(x)		SET_BIT_MASK(x-1, 0, 0xfff)
+#define V_WIN3_DSP_HEIGHT2(x)		SET_BIT_MASK(x-1, 16, 0xfff)
+#define M_WIN3_DSP_WIDTH2		SET_BIT(0xfff, 0)
+#define M_WIN3_DSP_HEIGHT2		SET_BIT(0xfff, 16)
+
+#define WIN3_DSP_ST2			(0x0138)
+#define V_WIN3_DSP_XST2(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN3_DSP_YST2(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN3_DSP_XST2			SET_BIT(0x1fff, 0)
+#define M_WIN3_DSP_YST2			SET_BIT(0x1fff, 16)
+
+#define WIN3_DST_ALPHA_CTRL		(0x013c)
+#define V_WIN3_DST_FACTOR_M0(x)		SET_BIT_MASK(x, 6, 7)
+#define M_WIN3_DST_FACTOR_M0		SET_BIT(7, 6)
+
+#define WIN3_MST3			(0x0140)
+#define WIN3_DSP_INFO3			(0x0144)
+#define V_WIN3_DSP_WIDTH3(x)		SET_BIT_MASK(x-1, 0, 0xfff)
+#define V_WIN3_DSP_HEIGHT3(x)		SET_BIT_MASK(x-1, 16, 0xfff)
+#define M_WIN3_DSP_WIDTH3		SET_BIT(0xfff, 0)
+#define M_WIN3_DSP_HEIGHT3		SET_BIT(0xfff, 16)
+
+#define WIN3_DSP_ST3			(0x0148)
+#define V_WIN3_DSP_XST3(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN3_DSP_YST3(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN3_DSP_XST3			SET_BIT(0x1fff, 0)
+#define M_WIN3_DSP_YST3			SET_BIT(0x1fff, 16)
+
+#define WIN3_FADING_CTRL		(0x014c)
+#define V_WIN3_FADING_OFFSET_R(x)	SET_BIT_MASK(x, 0, 0xff)
+#define V_WIN3_FADING_OFFSET_G(x)	SET_BIT_MASK(x, 8, 0xff)
+#define V_WIN3_FADING_OFFSET_B(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_WIN3_FADING_EN(x)		SET_BIT_MASK(x, 24, 1)
+
+#define M_WIN3_FADING_OFFSET_R		SET_BIT(0xff, 0)
+#define M_WIN3_FADING_OFFSET_G		SET_BIT(0xff, 8)
+#define M_WIN3_FADING_OFFSET_B		SET_BIT(0xff, 16)
+#define M_WIN3_FADING_EN		SET_BIT(1, 24)
+
+/* hwc register */
+#define HWC_CTRL0			(0x0150)
+#define V_HWC_EN(x)			SET_BIT_MASK(x, 0, 1)
+#define V_HWC_DATA_FMT(x)		SET_BIT_MASK(x, 1, 7)
+#define V_HWC_MODE(x)			SET_BIT_MASK(x, 4, 1)
+#define V_HWC_SIZE(x)			SET_BIT_MASK(x, 5, 3)
+#define V_HWC_INTERLACE_READ(x)		SET_BIT_MASK(x, 8, 1)
+#define V_HWC_NO_OUTSTANDING(x)		SET_BIT_MASK(x, 9, 1)
+#define V_HWC_CSC_MODE(x)		SET_BIT_MASK(x, 10, 1)
+#define V_HWC_RB_SWAP(x)		SET_BIT_MASK(x, 12, 1)
+#define V_HWC_ALPHA_SWAP(x)		SET_BIT_MASK(x, 13, 1)
+#define V_HWC_ENDIAN_MODE(x)		SET_BIT_MASK(x, 14, 1)
+#define V_HWC_LUT_EN(x)			SET_BIT_MASK(x, 18, 1)
+
+#define M_HWC_EN			SET_BIT(1, 0)
+#define M_HWC_DATA_FMT			SET_BIT(7, 1)
+#define M_HWC_MODE			SET_BIT(1, 4)
+#define M_HWC_SIZE			SET_BIT(3, 5)
+#define M_HWC_INTERLACE_READ		SET_BIT(1, 8)
+#define M_HWC_NO_OUTSTANDING		SET_BIT(1, 9)
+#define M_HWC_CSC_MODE			SET_BIT(1, 10)
+#define M_HWC_RB_SWAP			SET_BIT(1, 12)
+#define M_HWC_ALPHA_SWAP		SET_BIT(1, 13)
+#define M_HWC_ENDIAN_MODE		SET_BIT(1, 14)
+#define M_HWC_LUT_EN			SET_BIT(1, 18)
+
+#define HWC_CTRL1			(0x0154)
+#define V_HWC_AXI_GATHER_EN(x)		SET_BIT_MASK(x, 0, 1)
+#define V_HWC_AXI_GATHER_NUM(x)		SET_BIT_MASK(x, 4, 7)
+#define M_HWC_AXI_GATHER_EN		SET_BIT(1, 0)
+#define M_HWC_AXI_GATHER_NUM		SET_BIT(7, 4)
+
+#define HWC_MST				(0x0158)
+#define HWC_DSP_ST			(0x015c)
+#define V_HWC_DSP_XST3(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_HWC_DSP_YST3(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_HWC_DSP_XST3			SET_BIT(0x1fff, 0)
+#define M_HWC_DSP_YST3			SET_BIT(0x1fff, 16)
+
+#define HWC_SRC_ALPHA_CTRL		(0x0160)
+#define V_HWC_SRC_ALPHA_EN(x)		SET_BIT_MASK(x, 0, 1)
+#define V_HWC_SRC_COLOR_M0(x)		SET_BIT_MASK(x, 1, 1)
+#define V_HWC_SRC_ALPHA_M0(x)		SET_BIT_MASK(x, 2, 1)
+#define V_HWC_SRC_BLEND_M0(x)		SET_BIT_MASK(x, 3, 3)
+#define V_HWC_SRC_ALPHA_CAL_M0(x)	SET_BIT_MASK(x, 5, 1)
+#define V_HWC_SRC_FACTOR_M0(x)		SET_BIT_MASK(x, 6, 7)
+#define V_HWC_SRC_GLOBAL_ALPHA(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_HWC_FADING_VALUE(x)		SET_BIT_MASK(x, 24, 0xff)
+
+#define M_HWC_SRC_ALPHA_EN		SET_BIT(1, 0)
+#define M_HWC_SRC_COLOR_M0		SET_BIT(1, 1)
+#define M_HWC_SRC_ALPHA_M0		SET_BIT(1, 2)
+#define M_HWC_SRC_BLEND_M0		SET_BIT(3, 3)
+#define M_HWC_SRC_ALPHA_CAL_M0		SET_BIT(1, 5)
+#define M_HWC_SRC_FACTOR_M0		SET_BIT(7, 6)
+#define M_HWC_SRC_GLOBAL_ALPHA		SET_BIT(0xff, 16)
+#define M_HWC_FADING_VALUE		SET_BIT(0xff, 24)
+
+#define HWC_DST_ALPHA_CTRL		(0x0164)
+#define V_HWC_DST_FACTOR_M0(x)		SET_BIT_MASK(x, 6, 7)
+#define M_HWC_DST_FACTOR_M0		SET_BIT(7, 6)
+
+#define HWC_FADING_CTRL			(0x0168)
+#define V_HWC_FADING_OFFSET_R(x)	SET_BIT_MASK(x, 0, 0xff)
+#define V_HWC_FADING_OFFSET_G(x)	SET_BIT_MASK(x, 8, 0xff)
+#define V_HWC_FADING_OFFSET_B(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_HWC_FADING_EN(x)		SET_BIT_MASK(x, 24, 1)
+
+#define M_HWC_FADING_OFFSET_R		SET_BIT(0xff, 0)
+#define M_HWC_FADING_OFFSET_G		SET_BIT(0xff, 8)
+#define M_HWC_FADING_OFFSET_B		SET_BIT(0xff, 16)
+#define M_HWC_FADING_EN			SET_BIT(1, 24)
+
+/* post process register */
+#define POST_DSP_HACT_INFO		(0x0170)
+#define V_DSP_HACT_END_POST(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_DSP_HACT_ST_POST(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_DSP_HACT_END_POST		SET_BIT(0x1fff, 0)
+#define M_DSP_HACT_ST_POST		SET_BIT(0x1fff, 16)
+
+#define POST_DSP_VACT_INFO		(0x0174)
+#define V_DSP_VACT_END_POST(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_DSP_VACT_ST_POST(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_DSP_VACT_END_POST		SET_BIT(0x1fff, 0)
+#define M_DSP_VACT_ST_POST		SET_BIT(0x1fff, 16)
+
+#define POST_SCL_FACTOR_YRGB		(0x0178)
+#define V_POST_HS_FACTOR_YRGB(x)	SET_BIT_MASK(x, 0, 0xffff)
+#define V_POST_VS_FACTOR_YRGB(x)	SET_BIT_MASK(x, 16, 0xffff)
+#define M_POST_HS_FACTOR_YRGB		SET_BIT(0xffff, 0)
+#define M_POST_VS_FACTOR_YRGB		SET_BIT(0xffff, 16)
+
+#define POST_SCL_CTRL			(0x0180)
+#define V_POST_HOR_SD_EN(x)		SET_BIT_MASK(x, 0, 1)
+#define V_POST_VER_SD_EN(x)		SET_BIT_MASK(x, 1, 1)
+
+#define M_POST_HOR_SD_EN		SET_BIT(0x1, 0)
+#define M_POST_VER_SD_EN		SET_BIT(0x1, 1)
+
+#define POST_DSP_VACT_INFO_F1		(0x0184)
+#define V_DSP_VACT_END_POST_F1(x)	SET_BIT_MASK(x, 0, 0x1fff)
+#define V_DSP_VACT_ST_POST_F1(x)	SET_BIT_MASK(x, 16, 0x1fff)
+
+#define M_DSP_VACT_END_POST_F1		SET_BIT(0x1fff, 0)
+#define M_DSP_VACT_ST_POST_F1		SET_BIT(0x1fff, 16)
+
+#define DSP_HTOTAL_HS_END		(0x0188)
+#define V_DSP_HS_PW(x)			SET_BIT_MASK(x, 0, 0x1fff)
+#define V_DSP_HTOTAL(x)			SET_BIT_MASK(x, 16, 0x1fff)
+#define M_DSP_HS_PW			SET_BIT(0x1fff, 0)
+#define M_DSP_HTOTAL			SET_BIT(0x1fff, 16)
+
+#define DSP_HACT_ST_END			(0x018c)
+#define V_DSP_HACT_END(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_DSP_HACT_ST(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_DSP_HACT_END			SET_BIT(0x1fff, 0)
+#define M_DSP_HACT_ST			SET_BIT(0x1fff, 16)
+
+#define DSP_VTOTAL_VS_END		(0x0190)
+#define V_DSP_VS_PW(x)			SET_BIT_MASK(x, 0, 0x1fff)
+#define V_DSP_VTOTAL(x)			SET_BIT_MASK(x, 16, 0x1fff)
+#define M_DSP_VS_PW			SET_BIT(0x1fff, 0)
+#define M_DSP_VTOTAL			SET_BIT(0x1fff, 16)
+
+#define DSP_VACT_ST_END			(0x0194)
+#define V_DSP_VACT_END(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_DSP_VACT_ST(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_DSP_VACT_END			SET_BIT(0x1fff, 0)
+#define M_DSP_VACT_ST			SET_BIT(0x1fff, 16)
+
+#define DSP_VS_ST_END_F1		(0x0198)
+#define V_DSP_VS_END_F1(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_DSP_VS_ST_F1(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_DSP_VS_END_F1			SET_BIT(0x1fff, 0)
+#define M_DSP_VS_ST_F1			SET_BIT(0x1fff, 16)
+
+#define DSP_VACT_ST_END_F1		(0x019c)
+#define V_DSP_VACT_END_F1(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_DSP_VAC_ST_F1(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_DSP_VACT_END_F1		SET_BIT(0x1fff, 0)
+#define M_DSP_VAC_ST_F1			SET_BIT(0x1fff, 16)
+
+enum lb_mode {
+	LB_YUV_3840X5 = 0x0,
+	LB_YUV_2560X8,
+	LB_RGB_3840X2,
+	LB_RGB_2560X4,
+	LB_RGB_1920X5,
+	LB_RGB_1280X8
+};
+
+enum sacle_up_mode {
+	SCALE_UP_BIL = 0x0,
+	SCALE_UP_BIC
+};
+
+enum scale_down_mode {
+	SCALE_DOWN_BIL = 0x0,
+	SCALE_DOWN_AVG
+};
+
+/* ALPHA BLENDING MODE */
+enum alpha_mode {
+	AB_USER_DEFINE = 0x0,
+	AB_CLEAR,
+	AB_SRC,
+	AB_DST,
+	AB_SRC_OVER,
+	AB_DST_OVER,
+	AB_SRC_IN,
+	AB_DST_IN,
+	AB_SRC_OUT,
+	AB_DST_OUT,
+	AB_SRC_ATOP,
+	AB_DST_ATOP,
+	XOR,
+	AB_SRC_OVER_GLOBAL
+};
+
+enum src_alpha_mode {
+	AA_STRAIGHT = 0x0,
+	AA_INVERSE
+};
+
+enum global_alpha_mode {
+	AA_GLOBAL = 0x0,
+	AA_PER_PIX,
+	AA_PER_PIX_GLOBAL
+};
+
+enum src_alpha_sel {
+	AA_SAT = 0x0,
+	AA_NO_SAT
+};
+
+enum src_color_mode {
+	AA_SRC_PRE_MUL = 0x0,
+	AA_SRC_NO_PRE_MUL
+};
+
+enum factor_mode {
+	AA_ZERO = 0x0,
+	AA_ONE,
+	AA_SRC,
+	AA_SRC_INVERSE,
+	AA_SRC_GLOBAL
+};
+
+struct lcdc_device {
+	int id;
+	struct device *dev;
+	struct lcdc_driver lcdc_drv;
+
+	struct drm_display_mode *mode;
+
+	void __iomem *regs;
+	/* back up reg */
+	void *regsbak;
+	/* physical basic address of lcdc register*/
+	u32 reg_phy_base;
+	/* physical map length of lcdc register*/
+	u32 len;
+	/* one time only one process allowed to config the register */
+	spinlock_t reg_lock;
+
+	int __iomem *dsp_lut_addr_base;
+
+	/* used for primary or extended display device */
+	int prop;
+	bool pre_init;
+	/* if aclk or hclk is closed , cess to register is not allowed */
+	bool clk_on;
+	/* active layer counter, hen atv_layer_cnt = 0, isable lcdc */
+	u8 atv_layer_cnt;
+
+	unsigned int irq;
+
+	/* lcdc AHP clk */
+	struct clk *hclk;
+	/* lcdc dclk */
+	struct clk *dclk;
+	/* lcdc share memory frequency */
+	struct clk *aclk;
+	u32 pixclock;
+	/* 1:standby, 0:wrok */
+	u32 standby;
+};
+
+struct alpha_config {
+	/* win0_src_alpha_m0 */
+	enum src_alpha_mode src_alpha_mode;
+	/* win0_src_global_alpha */
+	u32 src_global_alpha_val;
+	/* win0_src_blend_m0 */
+	enum global_alpha_mode src_global_alpha_mode;
+	/* win0_src_alpha_cal_m0 */
+	enum src_alpha_sel src_alpha_cal_m0;
+	/* win0_src_color_m0 */
+	enum src_color_mode src_color_mode;
+	/* win0_src_factor_m0 */
+	enum factor_mode src_factor_mode;
+	/* win0_dst_factor_m0 */
+	enum factor_mode dst_factor_mode;
+};
+
+static inline void lcdc_writel(struct lcdc_device *lcdc_dev, u32 offset, u32 v)
+{
+	u32 *_pv = (u32 *)lcdc_dev->regsbak;
+
+	_pv += (offset >> 2);
+	*_pv = v;
+	writel_relaxed(v, lcdc_dev->regs + offset);
+}
+
+static inline u32 lcdc_readl(struct lcdc_device *lcdc_dev, u32 offset)
+{
+	u32 v;
+	u32 *_pv = (u32 *)lcdc_dev->regsbak;
+
+	_pv += (offset >> 2);
+	v = readl_relaxed(lcdc_dev->regs + offset);
+	*_pv = v;
+
+	return v;
+}
+
+static inline u32 lcdc_read_bit(struct lcdc_device *lcdc_dev,
+				u32 offset, u32 msk)
+{
+	u32 _v = readl_relaxed(lcdc_dev->regs + offset);
+
+	_v &= msk;
+
+	return _v >> msk;
+}
+
+static inline void lcdc_set_bit(struct lcdc_device *lcdc_dev,
+				u32 offset, u32 msk)
+{
+	u32 *_pv = (u32 *)lcdc_dev->regsbak;
+
+	_pv += (offset >> 2);
+	(*_pv) |= msk;
+	writel_relaxed(*_pv, lcdc_dev->regs + offset);
+}
+
+static inline void lcdc_clr_bit(struct lcdc_device *lcdc_dev,
+				u32 offset, u32 msk)
+{
+	u32 *_pv = (u32 *)lcdc_dev->regsbak;
+
+	_pv += (offset >> 2);
+	(*_pv) &= (~msk);
+	writel_relaxed(*_pv, lcdc_dev->regs + offset);
+}
+
+static inline void lcdc_msk_reg(struct lcdc_device *lcdc_dev,
+				u32 offset, u32 msk, u32 v)
+{
+	u32 *_pv = (u32 *)lcdc_dev->regsbak;
+
+	_pv += (offset >> 2);
+	(*_pv) &= (~msk);
+	(*_pv) |= v;
+	writel_relaxed(*_pv, lcdc_dev->regs + offset);
+}
+
+static inline void lcdc_cfg_done(struct lcdc_device *lcdc_dev)
+{
+	writel_relaxed(0x01, lcdc_dev->regs + REG_CFG_DONE);
+	dsb();
+}
+#endif /* _RK3288_LCDC_H_ */
-- 
1.7.9.5



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

* [PATCH 5/9] drm: add Rockchip rk3288 lcd controller driver
@ 2014-08-04  4:51   ` mark yao
  0 siblings, 0 replies; 38+ messages in thread
From: mark yao @ 2014-08-04  4:51 UTC (permalink / raw)
  To: heiko-4mtYJXux2i+zQB+pC5nmwQ, Rob Clark, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, Randy Dunlap,
	David Airlie, Grant Likely, Greg Kroah-Hartman, John Stultz,
	Rom Lemarchand
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-api-u79uwXL29TY76Z2rM5mHXA, olof-nZhT3qVonbNeoWH0uzbU5w,
	djkurtz-F7+t8E8rja9g9hUCZPvPmw, xjq-TNX95d0MmH7DzftRWevZcw,
	kfx-TNX95d0MmH7DzftRWevZcw, cym-TNX95d0MmH7DzftRWevZcw,
	cf-TNX95d0MmH7DzftRWevZcw, zyw-TNX95d0MmH7DzftRWevZcw,
	zwl-TNX95d0MmH7DzftRWevZcw, xxm-TNX95d0MmH7DzftRWevZcw,
	huangtao-TNX95d0MmH7DzftRWevZcw,
	kever.yang-TNX95d0MmH7DzftRWevZcw,
	zhangqing-TNX95d0MmH7DzftRWevZcw, yxj-TNX95d0MmH7DzftRWevZcw,
	wxt-TNX95d0MmH7DzftRWevZcw, xw-TNX95d0MmH7DzftRWevZcw, mark yao

Signed-off-by: mark yao <yzq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
 drivers/gpu/drm/rockchip/Kconfig            |    2 +
 drivers/gpu/drm/rockchip/Makefile           |    2 +-
 drivers/gpu/drm/rockchip/lcdc/Kconfig       |    9 +
 drivers/gpu/drm/rockchip/lcdc/Makefile      |    4 +
 drivers/gpu/drm/rockchip/lcdc/rk3288_lcdc.c |  819 ++++++++++++++++++
 drivers/gpu/drm/rockchip/lcdc/rk3288_lcdc.h | 1202 +++++++++++++++++++++++++++
 6 files changed, 2037 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/rockchip/lcdc/Kconfig
 create mode 100644 drivers/gpu/drm/rockchip/lcdc/Makefile
 create mode 100644 drivers/gpu/drm/rockchip/lcdc/rk3288_lcdc.c
 create mode 100644 drivers/gpu/drm/rockchip/lcdc/rk3288_lcdc.h

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 592e999..ccce827 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -38,3 +38,5 @@ config DRM_ROCKCHIP_CONNECTOR
 	  Choose this option if you want to use Rockchip Primary DISPLAY.
 	  The driver provides an abstraction for Rockchip display devices,
 	  such as lcd plane, lvds, edp , mipi, etc.
+
+source "drivers/gpu/drm/rockchip/lcdc/Kconfig"
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index a5e5132..6d49edc 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -9,5 +9,5 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_gem.o \
 		rockchip_panel.o
 
 obj-$(CONFIG_DRM_ROCKCHIP_CONNECTOR) += rockchip_drm_connector.o
-obj-$(CONFIG_DRM_ROCKCHIP_LCDC) += rockchip_drm_lcdc.o
+obj-$(CONFIG_DRM_ROCKCHIP_LCDC) += rockchip_drm_lcdc.o lcdc/
 obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o
diff --git a/drivers/gpu/drm/rockchip/lcdc/Kconfig b/drivers/gpu/drm/rockchip/lcdc/Kconfig
new file mode 100644
index 0000000..2b94057
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/lcdc/Kconfig
@@ -0,0 +1,9 @@
+config LCDC_RK3288
+        bool "rk3288 lcdc support"
+        depends on DRM_ROCKCHIP_LCDC
+        help
+	  Choose this option if you have a rk3288 lcd controller.
+	  rk3288 lcdc is the display interface from memory frame buffer
+	  to display device. There are two lcd controllers on rk3288,
+	  They have same regs setting, can use same drivers. We use the
+	  lcdc id distinguish between them
diff --git a/drivers/gpu/drm/rockchip/lcdc/Makefile b/drivers/gpu/drm/rockchip/lcdc/Makefile
new file mode 100644
index 0000000..943dcd6
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/lcdc/Makefile
@@ -0,0 +1,4 @@
+#
+# Makefile for the lcd control device driver.
+
+obj-$(CONFIG_LCDC_RK3288) += rk3288_lcdc.o
diff --git a/drivers/gpu/drm/rockchip/lcdc/rk3288_lcdc.c b/drivers/gpu/drm/rockchip/lcdc/rk3288_lcdc.c
new file mode 100644
index 0000000..f1b016c
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/lcdc/rk3288_lcdc.c
@@ -0,0 +1,819 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:
+ *      hjc <hjc-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *      mark yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <video/display_timing.h>
+#include <drm/rockchip_drm.h>
+#include <drm/drm_crtc.h>
+
+#ifdef CONFIG_OF
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#endif
+
+#include "../rockchip_drm_lcdc.h"
+#include "rk3288_lcdc.h"
+
+#define RK3288_LCDC_BIG_BASE 0xff930000
+#define RK3288_LCDC_LIT_BASE 0xff940000
+
+static struct lcdc_win_data lcdc_win[] = {
+	[0] = {
+		.id = 0,
+		.zpos = ZPOS_DEFAULT_WIN,
+		.fmt_10 = 0,
+		.win_lb_mode = 0x4,
+		.swap_rb = 0,
+	},
+	[1] = {
+		.id = 1,
+		.zpos = ZPOS_UNUSED_WIN,
+		.fmt_10 = 0,
+		.win_lb_mode = 0x4,
+		.swap_rb = 0,
+	},
+	[2] = {
+		.id = 2,
+		.zpos = ZPOS_CURSOR_WIN,
+		.fmt_10 = 0,
+		.win_lb_mode = 0x4,
+		.swap_rb = 0,
+	},
+	[3] = {
+		.id = 3,
+		.zpos = ZPOS_UNUSED_WIN,
+	},
+};
+
+static void rk3288_lcdc_win_commit(struct lcdc_driver *lcdc_drv,
+				   struct lcdc_win_data *win);
+static void rk3288_lcdc_dpms(struct lcdc_driver *lcdc_drv, int mode);
+
+static int rk3288_lcdc_get_id(u32 phy_base)
+{
+	if (phy_base == RK3288_LCDC_BIG_BASE)
+		return 0;
+	else if (phy_base == RK3288_LCDC_LIT_BASE)
+		return 1;
+	else
+		return -EINVAL;
+}
+
+static int rk3288_lcdc_clk_enable(struct lcdc_device *lcdc_dev)
+{
+	int ret = 0;
+
+	if (!lcdc_dev->clk_on) {
+		ret = clk_prepare_enable(lcdc_dev->hclk);
+		if (ret < 0) {
+			dev_err(lcdc_dev->dev, "failed to enable hclk\n");
+			return ret;
+		}
+
+		ret = clk_prepare_enable(lcdc_dev->dclk);
+		if (ret < 0) {
+			dev_err(lcdc_dev->dev, "failed to enable dclk\n");
+			goto err_dclk;
+		}
+
+		clk_prepare_enable(lcdc_dev->aclk);
+		if (ret < 0) {
+			dev_err(lcdc_dev->dev, "failed to enable aclk\n");
+			goto err_aclk;
+		}
+
+		spin_lock(&lcdc_dev->reg_lock);
+		lcdc_dev->clk_on = 1;
+		spin_unlock(&lcdc_dev->reg_lock);
+	}
+
+	return ret;
+err_aclk:
+	clk_disable_unprepare(lcdc_dev->aclk);
+err_dclk:
+	clk_disable_unprepare(lcdc_dev->hclk);
+	return ret;
+}
+
+static void rk3288_lcdc_clk_disable(struct lcdc_device *lcdc_dev)
+{
+	if (lcdc_dev->clk_on) {
+		spin_lock(&lcdc_dev->reg_lock);
+		lcdc_dev->clk_on = 0;
+		spin_unlock(&lcdc_dev->reg_lock);
+		mdelay(25);
+		clk_disable_unprepare(lcdc_dev->dclk);
+		clk_disable_unprepare(lcdc_dev->hclk);
+		clk_disable_unprepare(lcdc_dev->aclk);
+	}
+}
+
+static void rk3288_lcdc_disable_irq(struct lcdc_device *lcdc_dev)
+{
+	u32 mask, val;
+
+	if (likely(lcdc_dev->clk_on)) {
+		spin_lock(&lcdc_dev->reg_lock);
+		mask = M_DSP_HOLD_VALID_INTR_EN | M_FS_INTR_EN |
+			M_LINE_FLAG_INTR_EN | M_BUS_ERROR_INTR_EN;
+		val = V_DSP_HOLD_VALID_INTR_EN(0) | V_FS_INTR_EN(0) |
+			V_LINE_FLAG_INTR_EN(0) | V_BUS_ERROR_INTR_EN(0);
+		lcdc_msk_reg(lcdc_dev, INTR_CTRL0, mask, val);
+
+		mask = M_DSP_HOLD_VALID_INTR_CLR | M_FS_INTR_CLR |
+			M_LINE_FLAG_INTR_CLR | M_LINE_FLAG_INTR_CLR;
+		val = V_DSP_HOLD_VALID_INTR_CLR(0) | V_FS_INTR_CLR(0) |
+			V_LINE_FLAG_INTR_CLR(0) | V_BUS_ERROR_INTR_CLR(0);
+		lcdc_msk_reg(lcdc_dev, INTR_CTRL0, mask, val);
+
+		mask = M_WIN0_EMPTY_INTR_EN | M_WIN1_EMPTY_INTR_EN |
+			M_WIN2_EMPTY_INTR_EN | M_WIN3_EMPTY_INTR_EN |
+			M_HWC_EMPTY_INTR_EN | M_POST_BUF_EMPTY_INTR_EN |
+			M_POST_BUF_EMPTY_INTR_EN;
+		val = V_WIN0_EMPTY_INTR_EN(0) | V_WIN1_EMPTY_INTR_EN(0) |
+			V_WIN2_EMPTY_INTR_EN(0) | V_WIN3_EMPTY_INTR_EN(0) |
+			V_HWC_EMPTY_INTR_EN(0) | V_POST_BUF_EMPTY_INTR_EN(0) |
+			V_PWM_GEN_INTR_EN(0);
+		lcdc_msk_reg(lcdc_dev, INTR_CTRL1, mask, val);
+
+		mask = M_WIN0_EMPTY_INTR_CLR | M_WIN1_EMPTY_INTR_CLR |
+			M_WIN2_EMPTY_INTR_CLR | M_WIN3_EMPTY_INTR_CLR |
+			M_HWC_EMPTY_INTR_CLR | M_POST_BUF_EMPTY_INTR_CLR |
+			M_POST_BUF_EMPTY_INTR_CLR;
+		val = V_WIN0_EMPTY_INTR_CLR(0) | V_WIN1_EMPTY_INTR_CLR(0) |
+			V_WIN2_EMPTY_INTR_CLR(0) | V_WIN3_EMPTY_INTR_CLR(0) |
+			V_HWC_EMPTY_INTR_CLR(0) |
+			V_POST_BUF_EMPTY_INTR_CLR(0) |
+			V_PWM_GEN_INTR_CLR(0);
+		lcdc_msk_reg(lcdc_dev, INTR_CTRL1, mask, val);
+		lcdc_cfg_done(lcdc_dev);
+		spin_unlock(&lcdc_dev->reg_lock);
+	}
+}
+
+static void rk3288_lcdc_enable_irq(struct lcdc_device *lcdc_dev)
+{
+	u32 mask, val;
+
+	if (likely(lcdc_dev->clk_on)) {
+		spin_lock(&lcdc_dev->reg_lock);
+		mask = M_FS_INTR_CLR | M_FS_INTR_EN;
+		val = V_FS_INTR_CLR(1) | V_FS_INTR_EN(1);
+		lcdc_msk_reg(lcdc_dev, INTR_CTRL0, mask, val);
+		spin_unlock(&lcdc_dev->reg_lock);
+	}
+}
+
+static irqreturn_t rk3288_lcdc_isr(int irq, void *dev_id)
+{
+	struct lcdc_device *lcdc_dev =
+			(struct lcdc_device *)dev_id;
+	u32 intr0_reg;
+
+	intr0_reg = lcdc_readl(lcdc_dev, INTR_CTRL0);
+	if (intr0_reg & M_FS_INTR_STS) {
+		lcdc_msk_reg(lcdc_dev, INTR_CTRL0, M_FS_INTR_CLR,
+			     V_FS_INTR_CLR(1));
+		lcdc_vsync_event_handler(lcdc_dev->dev);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void rk3288_lcdc_alpha_cfg(struct lcdc_device *lcdc_dev,
+				  struct lcdc_win_data *layer)
+{
+	struct alpha_config alpha_config;
+
+	u32 mask, val;
+	int ppixel_alpha, global_alpha;
+	u32 src_alpha_ctl, dst_alpha_ctl;
+
+	ppixel_alpha = ((layer->format == ARGB888) ||
+			(layer->format == ABGR888)) ? 1 : 0;
+	global_alpha = (layer->g_alpha_val == 0) ? 0 : 1;
+	alpha_config.src_global_alpha_val = layer->g_alpha_val;
+	layer->alpha_mode = AB_SRC_OVER;
+	switch (layer->alpha_mode) {
+	case AB_USER_DEFINE:
+		break;
+	case AB_CLEAR:
+		alpha_config.src_factor_mode = AA_ZERO;
+		alpha_config.dst_factor_mode = AA_ZERO;
+		break;
+	case AB_SRC:
+		alpha_config.src_factor_mode = AA_ONE;
+		alpha_config.dst_factor_mode = AA_ZERO;
+		break;
+	case AB_DST:
+		alpha_config.src_factor_mode = AA_ZERO;
+		alpha_config.dst_factor_mode = AA_ONE;
+		break;
+	case AB_SRC_OVER:
+		alpha_config.src_color_mode = AA_SRC_PRE_MUL;
+		if (global_alpha)
+			alpha_config.src_factor_mode = AA_SRC_GLOBAL;
+		else
+			alpha_config.src_factor_mode = AA_ONE;
+		alpha_config.dst_factor_mode = AA_SRC_INVERSE;
+		break;
+	case AB_DST_OVER:
+		alpha_config.src_color_mode = AA_SRC_PRE_MUL;
+		alpha_config.src_factor_mode = AA_SRC_INVERSE;
+		alpha_config.dst_factor_mode = AA_ONE;
+		break;
+	case AB_SRC_IN:
+		alpha_config.src_color_mode = AA_SRC_PRE_MUL;
+		alpha_config.src_factor_mode = AA_SRC;
+		alpha_config.dst_factor_mode = AA_ZERO;
+		break;
+	case AB_DST_IN:
+		alpha_config.src_factor_mode = AA_ZERO;
+		alpha_config.dst_factor_mode = AA_SRC;
+		break;
+	case AB_SRC_OUT:
+		alpha_config.src_color_mode = AA_SRC_PRE_MUL;
+		alpha_config.src_factor_mode = AA_SRC_INVERSE;
+		alpha_config.dst_factor_mode = AA_ZERO;
+		break;
+	case AB_DST_OUT:
+		alpha_config.src_factor_mode = AA_ZERO;
+		alpha_config.dst_factor_mode = AA_SRC_INVERSE;
+		break;
+	case AB_SRC_ATOP:
+		alpha_config.src_color_mode = AA_SRC_PRE_MUL;
+		alpha_config.src_factor_mode = AA_SRC;
+		alpha_config.dst_factor_mode = AA_SRC_INVERSE;
+		break;
+	case AB_DST_ATOP:
+		alpha_config.src_color_mode = AA_SRC_PRE_MUL;
+		alpha_config.src_factor_mode = AA_SRC_INVERSE;
+		alpha_config.dst_factor_mode = AA_SRC;
+		break;
+	case XOR:
+		alpha_config.src_color_mode = AA_SRC_PRE_MUL;
+		alpha_config.src_factor_mode = AA_SRC_INVERSE;
+		alpha_config.dst_factor_mode = AA_SRC_INVERSE;
+		break;
+	case AB_SRC_OVER_GLOBAL:
+		alpha_config.src_global_alpha_mode = AA_PER_PIX_GLOBAL;
+		alpha_config.src_color_mode = AA_SRC_NO_PRE_MUL;
+		alpha_config.src_factor_mode = AA_SRC_GLOBAL;
+		alpha_config.dst_factor_mode = AA_SRC_INVERSE;
+		break;
+	default:
+		pr_err("alpha mode error\n");
+		break;
+	}
+	if ((ppixel_alpha == 1) && (global_alpha == 1))
+		alpha_config.src_global_alpha_mode = AA_PER_PIX_GLOBAL;
+	else if (ppixel_alpha == 1)
+		alpha_config.src_global_alpha_mode = AA_PER_PIX;
+	else if (global_alpha == 1)
+		alpha_config.src_global_alpha_mode = AA_GLOBAL;
+	else
+		dev_warn(lcdc_dev->dev, "alpha_en should be 0\n");
+	alpha_config.src_alpha_mode = AA_STRAIGHT;
+	alpha_config.src_alpha_cal_m0 = AA_NO_SAT;
+
+	switch (layer->id) {
+	case 0:
+		src_alpha_ctl = 0x60;
+		dst_alpha_ctl = 0x64;
+		break;
+	case 1:
+		src_alpha_ctl = 0xa0;
+		dst_alpha_ctl = 0xa4;
+		break;
+	case 2:
+		src_alpha_ctl = 0xdc;
+		dst_alpha_ctl = 0xec;
+		break;
+	case 3:
+		src_alpha_ctl = 0x12c;
+		dst_alpha_ctl = 0x13c;
+		break;
+	}
+	mask = M_WIN0_DST_FACTOR_M0;
+	val = V_WIN0_DST_FACTOR_M0(alpha_config.dst_factor_mode);
+	lcdc_msk_reg(lcdc_dev, dst_alpha_ctl, mask, val);
+	mask = M_WIN0_SRC_ALPHA_EN | M_WIN0_SRC_COLOR_M0 |
+		M_WIN0_SRC_ALPHA_M0 | M_WIN0_SRC_BLEND_M0 |
+		M_WIN0_SRC_ALPHA_CAL_M0 | M_WIN0_SRC_FACTOR_M0|
+		M_WIN0_SRC_GLOBAL_ALPHA;
+	val = V_WIN0_SRC_ALPHA_EN(1) |
+		V_WIN0_SRC_COLOR_M0(alpha_config.src_color_mode) |
+		V_WIN0_SRC_ALPHA_M0(alpha_config.src_alpha_mode) |
+		V_WIN0_SRC_BLEND_M0(alpha_config.src_global_alpha_mode) |
+		V_WIN0_SRC_ALPHA_CAL_M0(alpha_config.src_alpha_cal_m0) |
+		V_WIN0_SRC_FACTOR_M0(alpha_config.src_factor_mode) |
+		V_WIN0_SRC_GLOBAL_ALPHA(alpha_config.src_global_alpha_val);
+	lcdc_msk_reg(lcdc_dev, src_alpha_ctl, mask, val);
+}
+
+static void rk3288_lcdc_win01_set(struct lcdc_device *lcdc_dev,
+				  struct lcdc_win_data *win)
+{
+	unsigned int mask, val;
+	unsigned int off = win->id * 0x40;
+	struct drm_display_mode *mode = lcdc_dev->mode;
+
+	spin_lock(&lcdc_dev->reg_lock);
+
+	if (mode && win->enabled) {
+		u32 dsp_stx = win->xpos + mode->htotal - mode->hsync_start;
+		u32 dsp_sty = win->ypos + mode->vtotal - mode->vsync_start;
+
+		mask = M_WIN0_EN | M_WIN0_DATA_FMT | M_WIN0_FMT_10 |
+			M_WIN0_LB_MODE | M_WIN0_RB_SWAP;
+		val = V_WIN0_EN(1) | V_WIN0_DATA_FMT(win->format) |
+			V_WIN0_FMT_10(win->fmt_10) |
+			V_WIN0_LB_MODE(win->win_lb_mode) |
+			V_WIN0_RB_SWAP(win->swap_rb);
+		lcdc_msk_reg(lcdc_dev, WIN0_CTRL0+off, mask, val);
+
+		mask = M_WIN0_BIC_COE_SEL |
+			M_WIN0_VSD_YRGB_GT4 | M_WIN0_VSD_YRGB_GT2 |
+			M_WIN0_VSD_CBR_GT4 | M_WIN0_VSD_CBR_GT2 |
+			M_WIN0_YRGB_HOR_SCL_MODE | M_WIN0_YRGB_VER_SCL_MODE |
+			M_WIN0_YRGB_HSD_MODE | M_WIN0_YRGB_VSU_MODE |
+			M_WIN0_YRGB_VSD_MODE | M_WIN0_CBR_HOR_SCL_MODE |
+			M_WIN0_CBR_VER_SCL_MODE | M_WIN0_CBR_HSD_MODE |
+			M_WIN0_CBR_VSU_MODE | M_WIN0_CBR_VSD_MODE;
+		val = V_WIN0_BIC_COE_SEL(win->bic_coe_el) |
+			V_WIN0_VSD_YRGB_GT4(win->vsd_yrgb_gt4) |
+			V_WIN0_VSD_YRGB_GT2(win->vsd_yrgb_gt2) |
+			V_WIN0_VSD_CBR_GT4(win->vsd_cbr_gt4) |
+			V_WIN0_VSD_CBR_GT2(win->vsd_cbr_gt2) |
+			V_WIN0_YRGB_HOR_SCL_MODE(win->yrgb_hor_scl_mode) |
+			V_WIN0_YRGB_VER_SCL_MODE(win->yrgb_ver_scl_mode) |
+			V_WIN0_YRGB_HSD_MODE(win->yrgb_hsd_mode) |
+			V_WIN0_YRGB_VSU_MODE(win->yrgb_vsu_mode) |
+			V_WIN0_YRGB_VSD_MODE(win->yrgb_vsd_mode) |
+			V_WIN0_CBR_HOR_SCL_MODE(win->cbr_hor_scl_mode) |
+			V_WIN0_CBR_VER_SCL_MODE(win->cbr_ver_scl_mode) |
+			V_WIN0_CBR_HSD_MODE(win->cbr_hsd_mode) |
+			V_WIN0_CBR_VSU_MODE(win->cbr_vsu_mode) |
+			V_WIN0_CBR_VSD_MODE(win->cbr_vsd_mode);
+		lcdc_msk_reg(lcdc_dev, WIN0_CTRL1+off, mask, val);
+
+		val = V_WIN0_VIR_STRIDE(win->y_vir_stride) |
+			V_WIN0_VIR_STRIDE_UV(win->uv_vir_stride);
+		lcdc_writel(lcdc_dev, WIN0_VIR+off, val);
+		lcdc_writel(lcdc_dev, WIN0_YRGB_MST+off, win->yrgb_addr);
+		lcdc_writel(lcdc_dev, WIN0_CBR_MST+off, win->uv_addr);
+		val = V_WIN0_ACT_WIDTH(win->xact) |
+			V_WIN0_ACT_HEIGHT(win->yact);
+		lcdc_writel(lcdc_dev, WIN0_ACT_INFO+off, val);
+
+		val = V_WIN0_DSP_WIDTH(win->xsize) |
+			V_WIN0_DSP_HEIGHT(win->ysize);
+		lcdc_writel(lcdc_dev, WIN0_DSP_INFO+off, val);
+
+		val = V_WIN0_DSP_XST(dsp_stx) |
+			V_WIN0_DSP_YST(dsp_sty);
+		lcdc_writel(lcdc_dev, WIN0_DSP_ST+off, val);
+
+		val = V_WIN0_HS_FACTOR_YRGB(0x1000) |
+			V_WIN0_VS_FACTOR_YRGB(0x1000);
+		lcdc_writel(lcdc_dev, WIN0_SCL_FACTOR_YRGB+off, val);
+
+		val = V_WIN0_HS_FACTOR_CBR(0x1000) |
+			V_WIN0_VS_FACTOR_CBR(0x1000);
+		lcdc_writel(lcdc_dev, WIN0_SCL_FACTOR_CBR+off, val);
+
+		lcdc_writel(lcdc_dev, WIN0_COLOR_KEY, 0x80000000);
+		if (win->alpha_en == 1) {
+			rk3288_lcdc_alpha_cfg(lcdc_dev, win);
+		} else {
+			mask = M_WIN0_SRC_ALPHA_EN;
+			val = V_WIN0_SRC_ALPHA_EN(0);
+			lcdc_msk_reg(lcdc_dev,
+				     WIN0_SRC_ALPHA_CTRL+off, mask, val);
+		}
+	} else {
+		mask = M_WIN0_EN;
+		val = V_WIN0_EN(0);
+		lcdc_msk_reg(lcdc_dev, WIN0_CTRL0+off, mask, val);
+	}
+
+	spin_unlock(&lcdc_dev->reg_lock);
+}
+
+static void rk3288_lcdc_win23_set(struct lcdc_device *lcdc_dev,
+				  struct lcdc_win_data *win)
+{
+	unsigned int mask, val;
+	unsigned int off = (win->id-2) * 0x50;
+	struct drm_display_mode *mode = lcdc_dev->mode;
+
+	if (mode && win->enabled) {
+		u32 dsp_stx = win->xpos + mode->htotal - mode->hdisplay;
+		u32 dsp_sty = win->ypos + mode->vtotal - mode->vdisplay;
+
+		mask = M_WIN2_EN | M_WIN2_DATA_FMT | M_WIN2_RB_SWAP;
+		val = V_WIN2_EN(1) | V_WIN2_DATA_FMT(win->fmt_cfg) |
+			V_WIN2_RB_SWAP(win->swap_rb);
+		lcdc_msk_reg(lcdc_dev, WIN2_CTRL0+off, mask, val);
+		mask = M_WIN2_MST0_EN;
+		val = V_WIN2_MST0_EN(1);
+		lcdc_msk_reg(lcdc_dev, WIN2_CTRL0+off, mask, val);
+
+		mask = M_WIN2_VIR_STRIDE0;
+		val = V_WIN2_VIR_STRIDE0(win->y_vir_stride);
+		lcdc_msk_reg(lcdc_dev, WIN2_VIR0_1+off, mask, val);
+
+		lcdc_writel(lcdc_dev, WIN2_MST0+off, win->yrgb_addr);
+		val = V_WIN2_DSP_WIDTH0(win->xsize) |
+			V_WIN2_DSP_HEIGHT0(win->ysize);
+		lcdc_writel(lcdc_dev, WIN2_DSP_INFO0+off, val);
+		val = V_WIN2_DSP_XST0(dsp_stx) |
+			V_WIN2_DSP_YST0(dsp_sty);
+		lcdc_writel(lcdc_dev, WIN2_DSP_ST0+off, val);
+		if (win->alpha_en == 1) {
+			rk3288_lcdc_alpha_cfg(lcdc_dev, win);
+		} else {
+			mask = M_WIN2_SRC_ALPHA_EN;
+			val = V_WIN2_SRC_ALPHA_EN(0);
+			lcdc_msk_reg(lcdc_dev,
+				     WIN2_SRC_ALPHA_CTRL+off, mask, val);
+		}
+	} else {
+		mask = M_WIN2_EN | M_WIN2_MST0_EN |
+			M_WIN2_MST0_EN | M_WIN2_MST2_EN |
+			M_WIN2_MST3_EN;
+		val = V_WIN2_EN(0) | V_WIN2_MST0_EN(0) |
+			V_WIN2_MST1_EN(0) | V_WIN2_MST2_EN(0) |
+			V_WIN2_MST3_EN(0);
+		lcdc_msk_reg(lcdc_dev, WIN2_CTRL0+off, mask, val);
+	}
+}
+
+static int rk3288_lcdc_initial(struct lcdc_device *lcdc_dev)
+{
+	int i;
+
+	lcdc_dev->hclk = devm_clk_get(lcdc_dev->dev, "hclk_lcdc");
+	lcdc_dev->aclk = devm_clk_get(lcdc_dev->dev, "aclk_lcdc");
+	lcdc_dev->dclk = devm_clk_get(lcdc_dev->dev, "dclk_lcdc");
+
+	if ((IS_ERR(lcdc_dev->aclk)) || (IS_ERR(lcdc_dev->dclk)) ||
+	    (IS_ERR(lcdc_dev->hclk))) {
+		dev_err(lcdc_dev->dev, "failed to get lcdc%d clk source\n",
+			lcdc_dev->id);
+		return -ENODEV;
+	}
+
+	if (rk3288_lcdc_clk_enable(lcdc_dev) < 0) {
+		dev_err(lcdc_dev->dev, "failed to enable lcdc%d clks\n",
+			lcdc_dev->id);
+		return -ENODEV;
+	}
+
+	memcpy(lcdc_dev->regsbak, lcdc_dev->regs, lcdc_dev->len);
+
+	lcdc_set_bit(lcdc_dev, SYS_CTRL, M_AUTO_GATING_EN);
+	lcdc_cfg_done(lcdc_dev);
+
+	lcdc_dev->standby = DRM_MODE_DPMS_OFF;
+
+	for (i = 0; i < ARRAY_SIZE(lcdc_win); i++) {
+		lcdc_win[i].enabled = false;
+		rk3288_lcdc_win_commit(&lcdc_dev->lcdc_drv, &lcdc_win[i]);
+	}
+
+	return 0;
+}
+
+static struct lcdc_driver *rk3288_lcdc_init(struct platform_device *pdev)
+{
+	struct lcdc_device *lcdc_dev;
+	struct lcdc_driver *lcdc_drv;
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	struct device_node *np;
+	int ret = 0;
+
+	if (!dev->of_node)
+		return NULL;
+
+	np = pdev->dev.of_node;
+
+	/* if the primary lcdc has not registered, the extend
+	 * lcdc register later
+	*/
+	lcdc_dev = devm_kzalloc(dev, sizeof(struct lcdc_device), GFP_KERNEL);
+	if (!lcdc_dev) {
+		dev_err(&pdev->dev, "rk3288 lcdc device kmalloc fail!");
+		return NULL;
+	}
+	lcdc_dev->dev = dev;
+	lcdc_drv = &lcdc_dev->lcdc_drv;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	lcdc_dev->reg_phy_base = res->start;
+	lcdc_dev->len = resource_size(res);
+	lcdc_dev->regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(lcdc_dev->regs)) {
+		dev_err(&pdev->dev, "ioremap lcdc devices fail\n");
+		return NULL;
+	}
+
+	lcdc_dev->regsbak = devm_kzalloc(dev, lcdc_dev->len, GFP_KERNEL);
+	if (IS_ERR(lcdc_dev->regsbak)) {
+		dev_err(&pdev->dev, "lcdc devices kzalloc reg backup fail\n");
+		return NULL;
+	}
+
+	lcdc_dev->id = rk3288_lcdc_get_id(lcdc_dev->reg_phy_base);
+	if (lcdc_dev->id < 0) {
+		dev_err(&pdev->dev, "no such lcdc device id[%d]!\n",
+			lcdc_dev->id);
+		return NULL;
+	}
+
+	dev_set_name(lcdc_dev->dev, "lcdc%d", lcdc_dev->id);
+
+	lcdc_dev->irq = platform_get_irq(pdev, 0);
+	if (lcdc_dev->irq < 0) {
+		dev_err(&pdev->dev, "cannot find IRQ for lcdc%d\n",
+			lcdc_dev->id);
+		return NULL;
+	}
+
+	ret = devm_request_irq(dev, lcdc_dev->irq, rk3288_lcdc_isr,
+			       IRQF_DISABLED | IRQF_SHARED,
+			       dev_name(dev), lcdc_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "cannot requeset irq %d - err %d\n",
+			lcdc_dev->irq, ret);
+		return NULL;
+	}
+
+	spin_lock_init(&lcdc_dev->reg_lock);
+
+	ret = rk3288_lcdc_initial(lcdc_dev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "cannot initial lcdc dev - err %d\n", ret);
+		return NULL;
+	}
+
+	dev_info(dev, "lcdc%d probe ok\n", lcdc_dev->id);
+
+	return lcdc_drv;
+}
+
+static void rk3288_lcdc_deinit(struct lcdc_driver *lcdc_drv)
+{
+}
+
+static void rk3288_lcdc_dpms(struct lcdc_driver *lcdc_drv, int mode)
+{
+	struct lcdc_device *lcdc_dev =
+			container_of(lcdc_drv, struct lcdc_device, lcdc_drv);
+
+	if (lcdc_dev->standby == mode)
+		return;
+
+	if (mode == DRM_MODE_DPMS_ON) {
+		if (rk3288_lcdc_clk_enable(lcdc_dev) < 0) {
+			dev_err(lcdc_dev->dev, "failed to enable lcdc%d clks\n",
+				lcdc_dev->id);
+			return;
+		}
+
+		spin_lock(&lcdc_dev->reg_lock);
+		lcdc_msk_reg(lcdc_dev, SYS_CTRL, M_STANDBY_EN, V_STANDBY_EN(0));
+		spin_unlock(&lcdc_dev->reg_lock);
+	} else {
+		if (lcdc_dev->clk_on) {
+			spin_lock(&lcdc_dev->reg_lock);
+			lcdc_msk_reg(lcdc_dev, SYS_CTRL,
+				     M_STANDBY_EN, V_STANDBY_EN(1));
+			spin_unlock(&lcdc_dev->reg_lock);
+			rk3288_lcdc_clk_disable(lcdc_dev);
+		}
+	}
+
+	lcdc_dev->standby = mode;
+}
+
+static void rk3288_lcdc_mode_set(struct lcdc_driver *lcdc_drv,
+				 struct drm_display_mode *mode)
+{
+	struct lcdc_device *lcdc_dev =
+			container_of(lcdc_drv, struct lcdc_device, lcdc_drv);
+	struct rockchip_panel_special *priv_mode = (void *)mode->private;
+	u32 mask, val;
+	u16 x_res = mode->hdisplay;
+	u16 y_res = mode->vdisplay;
+	u16 h_total = mode->htotal;
+	u16 v_total = mode->vtotal;
+	u16 hsync_len = mode->hsync_end - mode->hsync_start;
+	u16 left_margin = mode->htotal - mode->hsync_end;
+	u16 vsync_len = mode->vsync_end - mode->vsync_start;
+	u16 upper_margin = mode->vtotal - mode->vsync_end;
+
+	u16 face = priv_mode->out_face;
+	u8 pin_hsync = (priv_mode->flags & DISPLAY_FLAGS_HSYNC_HIGH) ? 1 : 0;
+	u8 pin_vsync = (priv_mode->flags & DISPLAY_FLAGS_VSYNC_HIGH) ? 1 : 0;
+	u8 pin_den = (priv_mode->flags & DISPLAY_FLAGS_DE_HIGH) ? 1 : 0;
+	u8 pin_dclk = (priv_mode->flags &
+		       DISPLAY_FLAGS_PIXDATA_NEGEDGE) ? 1 : 0;
+	u8 swap_rb = (priv_mode->color_swap & ROCKCHIP_COLOR_SWAP_RB) ? 1 : 0;
+	u8 swap_rg = (priv_mode->color_swap & ROCKCHIP_COLOR_SWAP_RG) ? 1 : 0;
+	u8 swap_gb = (priv_mode->color_swap & ROCKCHIP_COLOR_SWAP_GB) ? 1 : 0;
+	u8 swap_dumy = 0;
+	u8 swap_delta = 0;
+	bool dither = priv_mode->dither;
+	u8 type = priv_mode->out_type;
+
+	spin_lock(&lcdc_dev->reg_lock);
+	if (likely(lcdc_dev->clk_on)) {
+		val = 0;
+		switch (face) {
+		case ROCKCHIP_OUTFACE_P565:
+			val = V_DITHER_DOWN_EN(1) | V_DITHER_DOWN_MODE(0) |
+				V_DITHER_DOWN_SEL(1);
+			break;
+		case ROCKCHIP_OUTFACE_P666:
+			val = V_DITHER_DOWN_EN(1) | V_DITHER_DOWN_MODE(1) |
+				V_DITHER_DOWN_SEL(1);
+			break;
+		case ROCKCHIP_OUTFACE_P888:
+			break;
+		default:
+			dev_err(lcdc_dev->dev, "un supported interface[%d]!\n",
+				face);
+			break;
+		}
+
+		mask = M_DITHER_DOWN_EN | M_DITHER_DOWN_MODE |
+			M_DITHER_DOWN_SEL;
+		lcdc_msk_reg(lcdc_dev, DSP_CTRL1, mask, val);
+
+		if (dither)
+			face = ROCKCHIP_OUTFACE_P888;
+
+		switch (type) {
+		case ROCKCHIP_DISPLAY_TYPE_RGB:
+		case ROCKCHIP_DISPLAY_TYPE_LVDS:
+			mask = M_RGB_OUT_EN;
+			val = V_RGB_OUT_EN(1);
+			break;
+		case ROCKCHIP_DISPLAY_TYPE_HDMI:
+			face = ROCKCHIP_OUTFACE_AAAA;
+			mask = M_HDMI_OUT_EN;
+			val = V_HDMI_OUT_EN(1);
+			break;
+		case ROCKCHIP_DISPLAY_TYPE_MIPI:
+			mask = M_MIPI_OUT_EN;
+			val = V_MIPI_OUT_EN(1);
+			break;
+		case ROCKCHIP_DISPLAY_TYPE_EDP:
+			face = ROCKCHIP_OUTFACE_AAAA;
+			mask = M_DITHER_DOWN_EN | M_DITHER_UP_EN;
+			val = V_DITHER_DOWN_EN(0) | V_DITHER_UP_EN(0);
+			lcdc_msk_reg(lcdc_dev, DSP_CTRL1, mask, val);
+			mask = M_EDP_OUT_EN;
+			val = V_EDP_OUT_EN(1);
+			break;
+		default:
+			dev_err(lcdc_dev->dev, "unsupported display type[%d]\n",
+				type);
+		}
+		lcdc_msk_reg(lcdc_dev, SYS_CTRL, mask, val);
+
+		mask = M_DSP_OUT_MODE | M_DSP_HSYNC_POL | M_DSP_VSYNC_POL |
+			M_DSP_DEN_POL | M_DSP_DCLK_POL | M_DSP_BG_SWAP |
+			M_DSP_RB_SWAP | M_DSP_RG_SWAP | M_DSP_DELTA_SWAP |
+			M_DSP_DUMMY_SWAP | M_DSP_OUT_ZERO | M_DSP_BLANK_EN |
+			M_DSP_BLACK_EN | M_DSP_X_MIR_EN | M_DSP_Y_MIR_EN;
+		val = V_DSP_OUT_MODE(face) | V_DSP_HSYNC_POL(pin_hsync) |
+			V_DSP_VSYNC_POL(pin_vsync) |
+			V_DSP_DEN_POL(pin_den) | V_DSP_DCLK_POL(pin_dclk) |
+			V_DSP_BG_SWAP(swap_gb) | V_DSP_RB_SWAP(swap_rb) |
+			V_DSP_RG_SWAP(swap_rg) |
+			V_DSP_DELTA_SWAP(swap_delta) |
+			V_DSP_DUMMY_SWAP(swap_dumy) | V_DSP_OUT_ZERO(0) |
+			V_DSP_BLANK_EN(0) | V_DSP_BLACK_EN(0);
+		lcdc_msk_reg(lcdc_dev, DSP_CTRL0, mask, val);
+
+		mask = M_DSP_BG_BLUE | M_DSP_BG_GREEN | M_DSP_BG_RED;
+		val = V_DSP_BG_BLUE(0) | V_DSP_BG_GREEN(0) | V_DSP_BG_RED(0);
+		lcdc_msk_reg(lcdc_dev, DSP_BG, mask, val);
+
+		mask = M_DSP_HS_PW | M_DSP_HTOTAL;
+		val = V_DSP_HS_PW(hsync_len) | V_DSP_HTOTAL(h_total);
+		lcdc_msk_reg(lcdc_dev, DSP_HTOTAL_HS_END, mask, val);
+
+		mask = M_DSP_HACT_END | M_DSP_HACT_ST;
+		val = V_DSP_HACT_END(hsync_len + left_margin + x_res) |
+			V_DSP_HACT_ST(hsync_len + left_margin);
+		lcdc_msk_reg(lcdc_dev, DSP_HACT_ST_END, mask, val);
+
+		mask = M_DSP_VS_PW | M_DSP_VTOTAL;
+		val = V_DSP_VS_PW(vsync_len) | V_DSP_VTOTAL(v_total);
+		lcdc_msk_reg(lcdc_dev, DSP_VTOTAL_VS_END, mask, val);
+
+		mask = M_DSP_VACT_END | M_DSP_VACT_ST;
+		val = V_DSP_VACT_END(vsync_len + upper_margin + y_res) |
+			V_DSP_VACT_ST(vsync_len + upper_margin);
+		lcdc_msk_reg(lcdc_dev, DSP_VACT_ST_END, mask, val);
+
+		mask = M_DSP_HACT_END_POST | M_DSP_HACT_ST_POST;
+		val = V_DSP_HACT_END_POST(hsync_len + left_margin + x_res) |
+			V_DSP_HACT_ST_POST(hsync_len + left_margin);
+		lcdc_msk_reg(lcdc_dev, POST_DSP_HACT_INFO, mask, val);
+
+		mask = M_DSP_VACT_END_POST | M_DSP_VACT_ST_POST;
+		val = V_DSP_VACT_END_POST(vsync_len + upper_margin + y_res) |
+			V_DSP_VACT_ST_POST(vsync_len + upper_margin);
+		lcdc_msk_reg(lcdc_dev, POST_DSP_VACT_INFO, mask, val);
+	}
+
+	spin_unlock(&lcdc_dev->reg_lock);
+	clk_set_rate(lcdc_dev->dclk, mode->clock * 1000);
+
+	lcdc_dev->mode = mode;
+}
+
+static void rk3288_lcdc_enable_vblank(struct lcdc_driver *lcdc_drv)
+{
+	rk3288_lcdc_enable_irq(container_of(lcdc_drv,
+					    struct lcdc_device, lcdc_drv));
+}
+
+static void rk3288_lcdc_disable_vblank(struct lcdc_driver *lcdc_drv)
+{
+	rk3288_lcdc_disable_irq(container_of(lcdc_drv,
+					     struct lcdc_device, lcdc_drv));
+}
+
+static struct lcdc_win_data *
+	rk3288_lcdc_get_win(struct lcdc_driver *lcdc_drv, int zpos)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(lcdc_win); i++) {
+		if (lcdc_win[i].zpos == zpos)
+			return &lcdc_win[i];
+	}
+
+	return NULL;
+}
+
+static void rk3288_lcdc_win_commit(struct lcdc_driver *lcdc_drv,
+				   struct lcdc_win_data *win)
+{
+	struct lcdc_device *lcdc_dev =
+			container_of(lcdc_drv, struct lcdc_device, lcdc_drv);
+	struct device *dev = lcdc_dev->dev;
+
+	switch (win->id) {
+	case 0:
+	case 1:
+		rk3288_lcdc_win01_set(lcdc_dev, win);
+		break;
+	case 2:
+	case 3:
+		rk3288_lcdc_win23_set(lcdc_dev, win);
+		break;
+	default:
+		dev_info(dev, "not support win%d\n", win->id);
+	}
+
+	spin_lock(&lcdc_dev->reg_lock);
+	lcdc_cfg_done(lcdc_dev);
+	spin_unlock(&lcdc_dev->reg_lock);
+}
+
+struct lcdc_driver_data rockchip_rk3288_lcdc = {
+	.init = rk3288_lcdc_init,
+	.deinit = rk3288_lcdc_deinit,
+	.dpms = rk3288_lcdc_dpms,
+	.mode_set = rk3288_lcdc_mode_set,
+	.enable_vblank = rk3288_lcdc_enable_vblank,
+	.disable_vblank = rk3288_lcdc_disable_vblank,
+	.get_win = rk3288_lcdc_get_win,
+	.win_commit = rk3288_lcdc_win_commit,
+	.num_win = ARRAY_SIZE(lcdc_win),
+};
diff --git a/drivers/gpu/drm/rockchip/lcdc/rk3288_lcdc.h b/drivers/gpu/drm/rockchip/lcdc/rk3288_lcdc.h
new file mode 100644
index 0000000..d2e1110
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/lcdc/rk3288_lcdc.h
@@ -0,0 +1,1202 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:
+ *      hjc <hjc-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *      mark yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _RK3288_LCDC_H_
+#define _RK3288_LCDC_H_
+
+#define SET_BIT(x, bit) ((x) << (bit))
+#define SET_BIT_MASK(x, bit, mask) SET_BIT((x) & (mask), bit)
+
+#define GPIO		0
+#define REGULATOR	1
+
+/* register definition */
+#define REG_CFG_DONE			(0x0000)
+#define VERSION_INFO			(0x0004)
+#define M_RTL_VERSION			SET_BIT(0xffff, 0)
+#define M_FPGA_VERSION			SET_BIT(0xffff, 16)
+#define SYS_CTRL			(0x0008)
+#define V_DIRECT_PATH_EN(x)		SET_BIT_MASK(x, 0, 1)
+#define V_DIRECT_PATCH_SEL(x)		SET_BIT_MASK(x, 1, 3)
+#define V_DOUB_CHANNEL_EN(x)		SET_BIT_MASK(x, 3, 1)
+#define V_DOUB_CH_OVERLAP_NUM(x)	SET_BIT_MASK(x, 4, 0xf)
+#define V_EDPI_HALT_EN(x)		SET_BIT_MASK(x, 8, 1)
+#define V_EDPI_WMS_MODE(x)		SET_BIT_MASK(x, 9, 1)
+#define V_EDPI_WMS_FS(x)		SET_BIT_MASK(x, 10, 1)
+#define V_RGB_OUT_EN(x)			SET_BIT_MASK(x, 12, 1)
+#define V_HDMI_OUT_EN(x)		SET_BIT_MASK(x, 13, 1)
+#define V_EDP_OUT_EN(x)			SET_BIT_MASK(x, 14, 1)
+#define V_MIPI_OUT_EN(x)		SET_BIT_MASK(x, 15, 1)
+#define V_DMA_BURST_LENGTH(x)		SET_BIT_MASK(x, 18, 3)
+#define V_MMU_EN(x)			SET_BIT_MASK(x, 20, 1)
+#define V_DMA_STOP(x)			SET_BIT_MASK(x, 21, 1)
+#define V_STANDBY_EN(x)			SET_BIT_MASK(x, 22, 1)
+#define V_AUTO_GATING_EN(x)		SET_BIT_MASK(x, 23, 1)
+
+#define M_DIRECT_PATH_EN		SET_BIT(1, 0)
+#define M_DIRECT_PATCH_SEL		SET_BIT(3, 1)
+#define M_DOUB_CHANNEL_EN		SET_BIT(1, 3)
+#define M_DOUB_CH_OVERLAP_NUM		SET_BIT(0xf, 4)
+#define M_EDPI_HALT_EN			SET_BIT(1, 8)
+#define M_EDPI_WMS_MODE			SET_BIT(1, 9)
+#define M_EDPI_WMS_FS			SET_BIT(1, 10)
+#define M_RGB_OUT_EN			SET_BIT(1, 12)
+#define M_HDMI_OUT_EN			SET_BIT(1, 13)
+#define M_EDP_OUT_EN			SET_BIT(1, 14)
+#define M_MIPI_OUT_EN			SET_BIT(1, 15)
+#define M_DMA_BURST_LENGTH		SET_BIT(3, 18)
+#define M_MMU_EN			SET_BIT(1, 20)
+#define M_DMA_STOP			SET_BIT(1, 21)
+#define M_STANDBY_EN			SET_BIT(1, 22)
+#define M_AUTO_GATING_EN		SET_BIT(1, 23)
+#define SYS_CTRL1			(0x000c)
+#define V_NOC_HURRY_EN(x)		SET_BIT_MASK(x, 0, 0x1)
+#define V_NOC_HURRY_VALUE(x)		SET_BIT_MASK(x, 1, 0x3)
+#define V_NOC_HURRY_THRESHOLD(x)	SET_BIT_MASK(x, 3, 0x3f)
+#define V_NOC_QOS_EN(x)			SET_BIT_MASK(x, 9, 0x1)
+#define V_NOC_WIN_QOS(x)		SET_BIT_MASK(x, 10, 0x3)
+#define V_AXI_MAX_OUTSTANDING_EN(x)	SET_BIT_MASK(x, 12, 0x1)
+#define V_AXI_OUTSTANDING_MAX_NUM(x)	SET_BIT_MASK(x, 13, 0x1f)
+
+#define M_NOC_HURRY_EN			SET_BIT(0x1, 0)
+#define M_NOC_HURRY_VALUE		SET_BIT(0x3, 1)
+#define M_NOC_HURRY_THRESHOLD		SET_BIT(0x3f, 3)
+#define M_NOC_QOS_EN			SET_BIT(0x1, 9)
+#define M_NOC_WIN_QOS			SET_BIT(0x3, 10)
+#define M_AXI_MAX_OUTSTANDING_EN	SET_BIT(0x1, 12)
+#define M_AXI_OUTSTANDING_MAX_NUM	SET_BIT(0x1f, 13)
+
+#define DSP_CTRL0		(0x0010)
+#define V_DSP_OUT_MODE(x)	SET_BIT_MASK(x, 0, 0x0f)
+#define V_DSP_HSYNC_POL(x)	SET_BIT_MASK(x, 4, 1)
+#define V_DSP_VSYNC_POL(x)	SET_BIT_MASK(x, 5, 1)
+#define V_DSP_DEN_POL(x)	SET_BIT_MASK(x, 6, 1)
+#define V_DSP_DCLK_POL(x)	SET_BIT_MASK(x, 7, 1)
+#define V_DSP_DCLK_DDR(x)	SET_BIT_MASK(x, 8, 1)
+#define V_DSP_DDR_PHASE(x)	SET_BIT_MASK(x, 9, 1)
+#define V_DSP_INTERLACE(x)	SET_BIT_MASK(x, 10, 1)
+#define V_DSP_FIELD_POL(x)	SET_BIT_MASK(x, 11, 1)
+#define V_DSP_BG_SWAP(x)	SET_BIT_MASK(x, 12, 1)
+#define V_DSP_RB_SWAP(x)	SET_BIT_MASK(x, 13, 1)
+#define V_DSP_RG_SWAP(x)	SET_BIT_MASK(x, 14, 1)
+#define V_DSP_DELTA_SWAP(x)	SET_BIT_MASK(x, 15, 1)
+#define V_DSP_DUMMY_SWAP(x)	SET_BIT_MASK(x, 16, 1)
+#define V_DSP_OUT_ZERO(x)	SET_BIT_MASK(x, 17, 1)
+#define V_DSP_BLANK_EN(x)	SET_BIT_MASK(x, 18, 1)
+#define V_DSP_BLACK_EN(x)	SET_BIT_MASK(x, 19, 1)
+#define V_DSP_CCIR656_AVG(x)	SET_BIT_MASK(x, 20, 1)
+#define V_DSP_YUV_CLIP(x)	SET_BIT_MASK(x, 21, 1)
+#define V_DSP_X_MIR_EN(x)	SET_BIT_MASK(x, 22, 1)
+#define V_DSP_Y_MIR_EN(x)	SET_BIT_MASK(x, 23, 1)
+#define M_DSP_OUT_MODE		SET_BIT(0x0f, 0)
+#define M_DSP_HSYNC_POL		SET_BIT(1, 4)
+#define M_DSP_VSYNC_POL		SET_BIT(1, 5)
+#define M_DSP_DEN_POL		SET_BIT(1, 6)
+#define M_DSP_DCLK_POL		SET_BIT(1, 7)
+#define M_DSP_DCLK_DDR		SET_BIT(1, 8)
+#define M_DSP_DDR_PHASE		SET_BIT(1, 9)
+#define M_DSP_INTERLACE		SET_BIT(1, 10)
+#define M_DSP_FIELD_POL		SET_BIT(1, 11)
+#define M_DSP_BG_SWAP		SET_BIT(1, 12)
+#define M_DSP_RB_SWAP		SET_BIT(1, 13)
+#define M_DSP_RG_SWAP		SET_BIT(1, 14)
+#define M_DSP_DELTA_SWAP	SET_BIT(1, 15)
+#define M_DSP_DUMMY_SWAP	SET_BIT(1, 16)
+#define M_DSP_OUT_ZERO		SET_BIT(1, 17)
+#define M_DSP_BLANK_EN		SET_BIT(1, 18)
+#define M_DSP_BLACK_EN		SET_BIT(1, 19)
+#define M_DSP_CCIR656_AVG	SET_BIT(1, 20)
+#define M_DSP_YUV_CLIP		SET_BIT(1, 21)
+#define M_DSP_X_MIR_EN		SET_BIT(1, 22)
+#define M_DSP_Y_MIR_EN		SET_BIT(1, 23)
+
+#define DSP_CTRL1		(0x0014)
+#define V_DSP_LUT_EN(x)		SET_BIT_MASK(x, 0, 1)
+#define V_PRE_DITHER_DOWN_EN(x)	SET_BIT_MASK(x, 1, 1)
+#define V_DITHER_DOWN_EN(x)	SET_BIT_MASK(x, 2, 1)
+#define V_DITHER_DOWN_MODE(x)	SET_BIT_MASK(x, 3, 1)
+#define V_DITHER_DOWN_SEL(x)	SET_BIT_MASK(x, 4, 1)
+#define V_DITHER_UP_EN(x)	SET_BIT_MASK(x, 6, 1)
+#define V_DSP_LAYER0_SEL(x)	SET_BIT_MASK(x, 8, 3)
+#define V_DSP_LAYER1_SEL(x)	SET_BIT_MASK(x, 10, 3)
+#define V_DSP_LAYER2_SEL(x)	SET_BIT_MASK(x, 12, 3)
+#define V_DSP_LAYER3_SEL(x)	SET_BIT_MASK(x, 14, 3)
+#define M_DSP_LUT_EN		SET_BIT(1, 0)
+#define M_PRE_DITHER_DOWN_EN	SET_BIT(1, 1)
+#define M_DITHER_DOWN_EN	SET_BIT(1, 2)
+#define M_DITHER_DOWN_MODE	SET_BIT(1, 3)
+#define M_DITHER_DOWN_SEL	SET_BIT(1, 4)
+#define M_DITHER_UP_EN		SET_BIT(1, 6)
+#define M_DSP_LAYER0_SEL	SET_BIT(3, 8)
+#define M_DSP_LAYER1_SEL	SET_BIT(3, 10)
+#define M_DSP_LAYER2_SEL	SET_BIT(3, 12)
+#define M_DSP_LAYER3_SEL	SET_BIT(3, 14)
+
+#define DSP_BG			(0x0018)
+#define V_DSP_BG_BLUE(x)	SET_BIT_MASK(x << 2, 0, 0x3ff)
+#define V_DSP_BG_GREEN(x)	SET_BIT_MASK(x << 2, 10, 0x3ff)
+#define V_DSP_BG_RED(x)		SET_BIT_MASK(x << 2, 20, 0x3ff)
+#define M_DSP_BG_BLUE		SET_BIT(0x3ff, 0)
+#define M_DSP_BG_GREEN		SET_BIT(0x3ff, 10)
+#define M_DSP_BG_RED		SET_BIT(0x3ff, 20)
+
+#define MCU_CTRL		(0x001c)
+#define V_MCU_PIX_TOTAL(x)	SET_BIT_MASK(x, 0, 0x3f)
+#define V_MCU_CS_PST(x)		SET_BIT_MASK(x, 6, 0xf)
+#define V_MCU_CS_PEND(x)	SET_BIT_MASK(x, 10, 0x3f)
+#define V_MCU_RW_PST(x)		SET_BIT_MASK(x, 16, 0xf)
+#define V_MCU_RW_PEND(x)	SET_BIT_MASK(x, 20, 0x3f)
+#define V_MCU_CLK_SEL(x)	SET_BIT_MASK(x, 26, 1)
+#define V_MCU_HOLD_MODE(x)	SET_BIT_MASK(x, 27, 1)
+#define V_MCU_FRAME_ST(x)	SET_BIT_MASK(x, 28, 1)
+#define V_MCU_RS(x)		SET_BIT_MASK(x, 29, 1)
+#define V_MCU_BYPASS(x)		SET_BIT_MASK(x, 30, 1)
+#define V_MCU_TYPE(x)		SET_BIT_MASK(x, 31, 1)
+#define M_MCU_PIX_TOTAL		SET_BIT(0x3f, 0)
+#define M_MCU_CS_PST		SET_BIT(0xf, 6)
+#define M_MCU_CS_PEND		SET_BIT(0x3f, 10)
+#define M_MCU_RW_PST		SET_BIT(0xf, 16)
+#define M_MCU_RW_PEND		SET_BIT(0x3f, 20)
+#define M_MCU_CLK_SEL		SET_BIT(1, 26)
+#define M_MCU_HOLD_MODE		SET_BIT(1, 27)
+#define M_MCU_FRAME_ST		SET_BIT(1, 28)
+#define M_MCU_RS		SET_BIT(1, 29)
+#define M_MCU_BYPASS		SET_BIT(1, 30)
+#define M_MCU_TYPE		SET_BIT((u32)1, 31)
+
+#define INTR_CTRL0			(0x0020)
+#define V_DSP_HOLD_VALID_INTR_STS(x)	SET_BIT_MASK(x, 0, 1)
+#define V_FS_INTR_STS(x)		SET_BIT_MASK(x, 1, 1)
+#define V_LINE_FLAG_INTR_STS(x)		SET_BIT_MASK(x, 2, 1)
+#define V_BUS_ERROR_INTR_STS(x)		SET_BIT_MASK(x, 3, 1)
+#define V_DSP_HOLD_VALID_INTR_EN(x)	SET_BIT_MASK(x, 4, 1)
+#define V_FS_INTR_EN(x)			SET_BIT_MASK(x, 5, 1)
+#define V_LINE_FLAG_INTR_EN(x)		SET_BIT_MASK(x, 6, 1)
+#define V_BUS_ERROR_INTR_EN(x)		SET_BIT_MASK(x, 7, 1)
+#define V_DSP_HOLD_VALID_INTR_CLR(x)	SET_BIT_MASK(x, 8, 1)
+#define V_FS_INTR_CLR(x)		SET_BIT_MASK(x, 9, 1)
+#define V_LINE_FLAG_INTR_CLR(x)		SET_BIT_MASK(x, 10, 1)
+#define V_BUS_ERROR_INTR_CLR(x)		SET_BIT_MASK(x, 11, 1)
+#define V_DSP_LINE_FLAG_NUM(x)		SET_BIT_MASK(x, 12, 0xfff)
+
+#define M_DSP_HOLD_VALID_INTR_STS	SET_BIT(1, 0)
+#define M_FS_INTR_STS			SET_BIT(1, 1)
+#define M_LINE_FLAG_INTR_STS		SET_BIT(1, 2)
+#define M_BUS_ERROR_INTR_STS		SET_BIT(1, 3)
+#define M_DSP_HOLD_VALID_INTR_EN	SET_BIT(1, 4)
+#define M_FS_INTR_EN			SET_BIT(1, 5)
+#define M_LINE_FLAG_INTR_EN		SET_BIT(1, 6)
+#define M_BUS_ERROR_INTR_EN		SET_BIT(1, 7)
+#define M_DSP_HOLD_VALID_INTR_CLR	SET_BIT(1, 8)
+#define M_FS_INTR_CLR			SET_BIT(1, 9)
+#define M_LINE_FLAG_INTR_CLR		SET_BIT(1, 10)
+#define M_BUS_ERROR_INTR_CLR		SET_BIT(1, 11)
+#define M_DSP_LINE_FLAG_NUM		SET_BIT(0xfff, 12)
+
+#define INTR_CTRL1			(0x0024)
+#define V_WIN0_EMPTY_INTR_STS(x)	SET_BIT_MASK(x, 0, 1)
+#define V_WIN1_EMPTY_INTR_STS(x)	SET_BIT_MASK(x, 1, 1)
+#define V_WIN2_EMPTY_INTR_STS(x)	SET_BIT_MASK(x, 2, 1)
+#define V_WIN3_EMPTY_INTR_STS(x)	SET_BIT_MASK(x, 3, 1)
+#define V_HWC_EMPTY_INTR_STS(x)		SET_BIT_MASK(x, 4, 1)
+#define V_POST_BUF_EMPTY_INTR_STS(x)	SET_BIT_MASK(x, 5, 1)
+#define V_PWM_GEN_INTR_STS(x)		SET_BIT_MASK(x, 6, 1)
+#define V_WIN0_EMPTY_INTR_EN(x)		SET_BIT_MASK(x, 8, 1)
+#define V_WIN1_EMPTY_INTR_EN(x)		SET_BIT_MASK(x, 9, 1)
+#define V_WIN2_EMPTY_INTR_EN(x)		SET_BIT_MASK(x, 10, 1)
+#define V_WIN3_EMPTY_INTR_EN(x)		SET_BIT_MASK(x, 11, 1)
+#define V_HWC_EMPTY_INTR_EN(x)		SET_BIT_MASK(x, 12, 1)
+#define V_POST_BUF_EMPTY_INTR_EN(x)	SET_BIT_MASK(x, 13, 1)
+#define V_PWM_GEN_INTR_EN(x)		SET_BIT_MASK(x, 14, 1)
+#define V_WIN0_EMPTY_INTR_CLR(x)	SET_BIT_MASK(x, 16, 1)
+#define V_WIN1_EMPTY_INTR_CLR(x)	SET_BIT_MASK(x, 17, 1)
+#define V_WIN2_EMPTY_INTR_CLR(x)	SET_BIT_MASK(x, 18, 1)
+#define V_WIN3_EMPTY_INTR_CLR(x)	SET_BIT_MASK(x, 19, 1)
+#define V_HWC_EMPTY_INTR_CLR(x)		SET_BIT_MASK(x, 20, 1)
+#define V_POST_BUF_EMPTY_INTR_CLR(x)	SET_BIT_MASK(x, 21, 1)
+#define V_PWM_GEN_INTR_CLR(x)		SET_BIT_MASK(x, 22, 1)
+
+#define M_WIN0_EMPTY_INTR_STS		SET_BIT(1, 0)
+#define M_WIN1_EMPTY_INTR_STS		SET_BIT(1, 1)
+#define M_WIN2_EMPTY_INTR_STS		SET_BIT(1, 2)
+#define M_WIN3_EMPTY_INTR_STS		SET_BIT(1, 3)
+#define M_HWC_EMPTY_INTR_STS		SET_BIT(1, 4)
+#define M_POST_BUF_EMPTY_INTR_STS	SET_BIT(1, 5)
+#define M_PWM_GEN_INTR_STS		SET_BIT(1, 6)
+#define M_WIN0_EMPTY_INTR_EN		SET_BIT(1, 8)
+#define M_WIN1_EMPTY_INTR_EN		SET_BIT(1, 9)
+#define M_WIN2_EMPTY_INTR_EN		SET_BIT(1, 10)
+#define M_WIN3_EMPTY_INTR_EN		SET_BIT(1, 11)
+#define M_HWC_EMPTY_INTR_EN		SET_BIT(1, 12)
+#define M_POST_BUF_EMPTY_INTR_EN	SET_BIT(1, 13)
+#define M_PWM_GEN_INTR_EN		SET_BIT(1, 14)
+#define M_WIN0_EMPTY_INTR_CLR		SET_BIT(1, 16)
+#define M_WIN1_EMPTY_INTR_CLR		SET_BIT(1, 17)
+#define M_WIN2_EMPTY_INTR_CLR		SET_BIT(1, 18)
+#define M_WIN3_EMPTY_INTR_CLR		SET_BIT(1, 19)
+#define M_HWC_EMPTY_INTR_CLR		SET_BIT(1, 20)
+#define M_POST_BUF_EMPTY_INTR_CLR	SET_BIT(1, 21)
+#define M_PWM_GEN_INTR_CLR		SET_BIT(1, 22)
+
+/* win0 register */
+#define WIN0_CTRL0			(0x0030)
+#define V_WIN0_EN(x)			SET_BIT_MASK(x, 0, 1)
+#define V_WIN0_DATA_FMT(x)		SET_BIT_MASK(x, 1, 7)
+#define V_WIN0_FMT_10(x)		SET_BIT_MASK(x, 4, 1)
+#define V_WIN0_LB_MODE(x)		SET_BIT_MASK(x, 5, 7)
+#define V_WIN0_INTERLACE_READ(x)	SET_BIT_MASK(x, 8, 1)
+#define V_WIN0_NO_OUTSTANDING(x)	SET_BIT_MASK(x, 9, 1)
+#define V_WIN0_CSC_MODE(x)		SET_BIT_MASK(x, 10, 3)
+#define V_WIN0_RB_SWAP(x)		SET_BIT_MASK(x, 12, 1)
+#define V_WIN0_ALPHA_SWAP(x)		SET_BIT_MASK(x, 13, 1)
+#define V_WIN0_MID_SWAP(x)		SET_BIT_MASK(x, 14, 1)
+#define V_WIN0_UV_SWAP(x)		SET_BIT_MASK(x, 15, 1)
+#define V_WIN0_PPAS_ZERO_EN(x)		SET_BIT_MASK(x, 16, 1)
+#define V_WIN0_YRGB_DEFLICK(x)		SET_BIT_MASK(x, 18, 1)
+#define V_WIN0_CBR_DEFLICK(x)		SET_BIT_MASK(x, 19, 1)
+#define V_WIN0_YUV_CLIP(x)		SET_BIT_MASK(x, 20, 1)
+
+#define M_WIN0_EN			SET_BIT(1, 0)
+#define M_WIN0_DATA_FMT			SET_BIT(7, 1)
+#define M_WIN0_FMT_10			SET_BIT(1, 4)
+#define M_WIN0_LB_MODE			SET_BIT(7, 5)
+#define M_WIN0_INTERLACE_READ		SET_BIT(1, 8)
+#define M_WIN0_NO_OUTSTANDING		SET_BIT(1, 9)
+#define M_WIN0_CSC_MODE			SET_BIT(3, 10)
+#define M_WIN0_RB_SWAP			SET_BIT(1, 12)
+#define M_WIN0_ALPHA_SWAP		SET_BIT(1, 13)
+#define M_WIN0_MID_SWAP			SET_BIT(1, 14)
+#define M_WIN0_UV_SWAP			SET_BIT(1, 15)
+#define M_WIN0_PPAS_ZERO_EN		SET_BIT(1, 16)
+#define M_WIN0_YRGB_DEFLICK		SET_BIT(1, 18)
+#define M_WIN0_CBR_DEFLICK		SET_BIT(1, 19)
+#define M_WIN0_YUV_CLIP			SET_BIT(1, 20)
+
+#define WIN0_CTRL1			(0x0034)
+#define V_WIN0_YRGB_AXI_GATHER_EN(x)	SET_BIT_MASK(x, 0, 1)
+#define V_WIN0_CBR_AXI_GATHER_EN(x)	SET_BIT_MASK(x, 1, 1)
+#define V_WIN0_BIC_COE_SEL(x)		SET_BIT_MASK(x, 2, 3)
+#define V_WIN0_VSD_YRGB_GT4(x)		SET_BIT_MASK(x, 4, 1)
+#define V_WIN0_VSD_YRGB_GT2(x)		SET_BIT_MASK(x, 5, 1)
+#define V_WIN0_VSD_CBR_GT4(x)		SET_BIT_MASK(x, 6, 1)
+#define V_WIN0_VSD_CBR_GT2(x)		SET_BIT_MASK(x, 7, 1)
+#define V_WIN0_YRGB_AXI_GATHER_NUM(x)	SET_BIT_MASK(x, 8, 0xf)
+#define V_WIN0_CBR_AXI_GATHER_NUM(x)	SET_BIT_MASK(x, 12, 7)
+#define V_WIN0_LINE_LOAD_MODE(x)	SET_BIT_MASK(x, 15, 1)
+#define V_WIN0_YRGB_HOR_SCL_MODE(x)	SET_BIT_MASK(x, 16, 3)
+#define V_WIN0_YRGB_VER_SCL_MODE(x)	SET_BIT_MASK(x, 18, 3)
+#define V_WIN0_YRGB_HSD_MODE(x)		SET_BIT_MASK(x, 20, 3)
+#define V_WIN0_YRGB_VSU_MODE(x)		SET_BIT_MASK(x, 22, 1)
+#define V_WIN0_YRGB_VSD_MODE(x)		SET_BIT_MASK(x, 23, 1)
+#define V_WIN0_CBR_HOR_SCL_MODE(x)	SET_BIT_MASK(x, 24, 3)
+#define V_WIN0_CBR_VER_SCL_MODE(x)	SET_BIT_MASK(x, 26, 3)
+#define V_WIN0_CBR_HSD_MODE(x)		SET_BIT_MASK(x, 28, 3)
+#define V_WIN0_CBR_VSU_MODE(x)		SET_BIT_MASK(x, 30, 1)
+#define V_WIN0_CBR_VSD_MODE(x)		SET_BIT_MASK(x, 31, 1)
+
+#define M_WIN0_YRGB_AXI_GATHER_EN	SET_BIT(1, 0)
+#define M_WIN0_CBR_AXI_GATHER_EN	SET_BIT(1, 1)
+#define M_WIN0_BIC_COE_SEL		SET_BIT(3, 2)
+#define M_WIN0_VSD_YRGB_GT4		SET_BIT(1, 4)
+#define M_WIN0_VSD_YRGB_GT2		SET_BIT(1, 5)
+#define M_WIN0_VSD_CBR_GT4		SET_BIT(1, 6)
+#define M_WIN0_VSD_CBR_GT2		SET_BIT(1, 7)
+#define M_WIN0_YRGB_AXI_GATHER_NUM	SET_BIT(0xf, 8)
+#define M_WIN0_CBR_AXI_GATHER_NUM	SET_BIT(7, 12)
+#define M_WIN0_LINE_LOAD_MODE		SET_BIT(1, 15)
+#define M_WIN0_YRGB_HOR_SCL_MODE	SET_BIT(3, 16)
+#define M_WIN0_YRGB_VER_SCL_MODE	SET_BIT(3, 18)
+#define M_WIN0_YRGB_HSD_MODE		SET_BIT(3, 20)
+#define M_WIN0_YRGB_VSU_MODE		SET_BIT(1, 22)
+#define M_WIN0_YRGB_VSD_MODE		SET_BIT(1, 23)
+#define M_WIN0_CBR_HOR_SCL_MODE		SET_BIT(3, 24)
+#define M_WIN0_CBR_VER_SCL_MODE		SET_BIT(3, 26)
+#define M_WIN0_CBR_HSD_MODE		SET_BIT(3, 28)
+#define M_WIN0_CBR_VSU_MODE		SET_BIT((u32)1, 30)
+#define M_WIN0_CBR_VSD_MODE		SET_BIT((u32)1, 31)
+
+#define WIN0_COLOR_KEY			(0x0038)
+#define V_WIN0_COLOR_KEY(x)		SET_BIT_MASK(x, 0, 0x3fffffff)
+#define V_WIN0_COLOR_KEY_EN(x)		SET_BIT_MASK(x, 31, 1)
+#define M_WIN0_COLOR_KEY		SET_BIT(0x3fffffff, 0)
+#define M_WIN0_COLOR_KEY_EN		SET_BIT((u32)1, 31)
+
+#define WIN0_VIR			(0x003c)
+#define V_WIN0_VIR_STRIDE(x)		SET_BIT_MASK(x, 0, 0x3fff)
+#define V_WIN0_VIR_STRIDE_UV(x)		SET_BIT_MASK(x, 16, 0x3fff)
+#define M_WIN0_VIR_STRIDE		SET_BIT(0x3fff, 0)
+#define M_WIN0_VIR_STRIDE_UV		SET_BIT(0x3fff, 16)
+
+#define WIN0_YRGB_MST			(0x0040)
+#define WIN0_CBR_MST			(0x0044)
+#define WIN0_ACT_INFO			(0x0048)
+#define V_WIN0_ACT_WIDTH(x)		SET_BIT_MASK(x-1, 0, 0x1fff)
+#define V_WIN0_ACT_HEIGHT(x)		SET_BIT_MASK(x-1, 16, 0x1fff)
+#define M_WIN0_ACT_WIDTH		SET_BIT(0x1fff, 0)
+#define M_WIN0_ACT_HEIGHT		SET_BIT(0x1fff, 16)
+
+#define WIN0_DSP_INFO			(0x004c)
+#define V_WIN0_DSP_WIDTH(x)		SET_BIT_MASK(x-1, 0, 0xfff)
+#define V_WIN0_DSP_HEIGHT(x)		SET_BIT_MASK(x-1, 16, 0xfff)
+#define M_WIN0_DSP_WIDTH		SET_BIT(0xfff, 0)
+#define M_WIN0_DSP_HEIGHT		SET_BIT(0xfff, 16)
+
+#define WIN0_DSP_ST			(0x0050)
+#define V_WIN0_DSP_XST(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN0_DSP_YST(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN0_DSP_XST			SET_BIT(0x1fff, 0)
+#define M_WIN0_DSP_YST			SET_BIT(0x1fff, 16)
+
+#define WIN0_SCL_FACTOR_YRGB		(0x0054)
+#define V_WIN0_HS_FACTOR_YRGB(x)	SET_BIT_MASK(x, 0, 0xffff)
+#define V_WIN0_VS_FACTOR_YRGB(x)	SET_BIT_MASK(x, 16, 0xffff)
+#define M_WIN0_HS_FACTOR_YRGB		SET_BIT(0xffff, 0)
+#define M_WIN0_VS_FACTOR_YRGB		SET_BIT((u32)0xffff, 16)
+
+#define WIN0_SCL_FACTOR_CBR		(0x0058)
+#define V_WIN0_HS_FACTOR_CBR(x)		SET_BIT_MASK(x, 0, 0xffff)
+#define V_WIN0_VS_FACTOR_CBR(x)		SET_BIT_MASK(x, 16, 0xffff)
+#define M_WIN0_HS_FACTOR_CBR		SET_BIT(0xffff, 0)
+#define M_WIN0_VS_FACTOR_CBR		SET_BIT((u32)0xffff, 16)
+
+#define WIN0_SCL_OFFSET			(0x005c)
+#define V_WIN0_HS_OFFSET_YRGB(x)	SET_BIT_MASK(x, 0, 0xff)
+#define V_WIN0_HS_OFFSET_CBR(x)		SET_BIT_MASK(x, 8, 0xff)
+#define V_WIN0_VS_OFFSET_YRGB(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_WIN0_VS_OFFSET_CBR(x)		SET_BIT_MASK(x, 24, 0xff)
+
+#define M_WIN0_HS_OFFSET_YRGB		SET_BIT(0xff, 0)
+#define M_WIN0_HS_OFFSET_CBR		SET_BIT(0xff, 8)
+#define M_WIN0_VS_OFFSET_YRGB		SET_BIT(0xff, 16)
+#define M_WIN0_VS_OFFSET_CBR		SET_BIT((u32)0xff, 24)
+
+#define WIN0_SRC_ALPHA_CTRL		(0x0060)
+#define V_WIN0_SRC_ALPHA_EN(x)		SET_BIT_MASK(x, 0, 1)
+#define V_WIN0_SRC_COLOR_M0(x)		SET_BIT_MASK(x, 1, 1)
+#define V_WIN0_SRC_ALPHA_M0(x)		SET_BIT_MASK(x, 2, 1)
+#define V_WIN0_SRC_BLEND_M0(x)		SET_BIT_MASK(x, 3, 3)
+#define V_WIN0_SRC_ALPHA_CAL_M0(x)	SET_BIT_MASK(x, 5, 1)
+#define V_WIN0_SRC_FACTOR_M0(x)		SET_BIT_MASK(x, 6, 7)
+#define V_WIN0_SRC_GLOBAL_ALPHA(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_WIN0_FADING_VALUE(x)		SET_BIT_MASK(x, 24, 0xff)
+
+#define M_WIN0_SRC_ALPHA_EN		SET_BIT(1, 0)
+#define M_WIN0_SRC_COLOR_M0		SET_BIT(1, 1)
+#define M_WIN0_SRC_ALPHA_M0		SET_BIT(1, 2)
+#define M_WIN0_SRC_BLEND_M0		SET_BIT(3, 3)
+#define M_WIN0_SRC_ALPHA_CAL_M0		SET_BIT(1, 5)
+#define M_WIN0_SRC_FACTOR_M0		SET_BIT(7, 6)
+#define M_WIN0_SRC_GLOBAL_ALPHA		SET_BIT(0xff, 16)
+#define M_WIN0_FADING_VALUE		SET_BIT(0xff, 24)
+
+#define WIN0_DST_ALPHA_CTRL		(0x0064)
+#define V_WIN0_DST_FACTOR_M0(x)		SET_BIT_MASK(x, 6, 7)
+#define M_WIN0_DST_FACTOR_M0		SET_BIT(7, 6)
+
+#define WIN0_FADING_CTRL		(0x0068)
+#define V_WIN0_FADING_OFFSET_R(x)	SET_BIT_MASK(x, 0, 0xff)
+#define V_WIN0_FADING_OFFSET_G(x)	SET_BIT_MASK(x, 8, 0xff)
+#define V_WIN0_FADING_OFFSET_B(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_WIN0_FADING_EN(x)		SET_BIT_MASK(x, 24, 1)
+
+#define M_WIN0_FADING_OFFSET_R		SET_BIT(0xff, 0)
+#define M_WIN0_FADING_OFFSET_G		SET_BIT(0xff, 8)
+#define M_WIN0_FADING_OFFSET_B		SET_BIT(0xff, 16)
+#define M_WIN0_FADING_EN		SET_BIT(1, 24)
+
+/* win1 register */
+#define WIN1_CTRL0			(0x0070)
+#define V_WIN1_EN(x)			SET_BIT_MASK(x, 0, 1)
+#define V_WIN1_DATA_FMT(x)		SET_BIT_MASK(x, 1, 7)
+#define V_WIN1_FMT_10(x)		SET_BIT_MASK(x, 4, 1)
+#define V_WIN1_LB_MODE(x)		SET_BIT_MASK(x, 5, 7)
+#define V_WIN1_INTERLACE_READ_MODE(x)	SET_BIT_MASK(x, 8, 1)
+#define V_WIN1_NO_OUTSTANDING(x)	SET_BIT_MASK(x, 9, 1)
+#define V_WIN1_CSC_MODE(x)		SET_BIT_MASK(x, 10, 3)
+#define V_WIN1_RB_SWAP(x)		SET_BIT_MASK(x, 12, 1)
+#define V_WIN1_ALPHA_SWAP(x)		SET_BIT_MASK(x, 13, 1)
+#define V_WIN1_MID_SWAP(x)		SET_BIT_MASK(x, 14, 1)
+#define V_WIN1_UV_SWAP(x)		SET_BIT_MASK(x, 15, 1)
+#define V_WIN1_PPAS_ZERO_EN(x)		SET_BIT_MASK(x, 16, 1)
+#define V_WIN1_YRGB_DEFLICK(x)		SET_BIT_MASK(x, 18, 1)
+#define V_WIN1_CBR_DEFLICK(x)		SET_BIT_MASK(x, 19, 1)
+#define V_WIN1_YUV_CLIP(x)		SET_BIT_MASK(x, 20, 1)
+
+#define M_WIN1_EN			SET_BIT(1, 0)
+#define M_WIN1_DATA_FMT			SET_BIT(7, 1)
+#define M_WIN1_FMT_10			SET_BIT(1, 4)
+#define M_WIN1_LB_MODE			SET_BIT(7, 5)
+#define M_WIN1_INTERLACE_READ_MODE	SET_BIT(1, 8)
+#define M_WIN1_NO_OUTSTANDING		SET_BIT(1, 9)
+#define M_WIN1_CSC_MODE			SET_BIT(3, 10)
+#define M_WIN1_RB_SWAP			SET_BIT(1, 12)
+#define M_WIN1_ALPHA_SWAP		SET_BIT(1, 13)
+#define M_WIN1_MID_SWAP			SET_BIT(1, 14)
+#define M_WIN1_UV_SWAP			SET_BIT(1, 15)
+#define M_WIN1_PPAS_ZERO_EN		SET_BIT(1, 16)
+#define M_WIN1_YRGB_DEFLICK		SET_BIT(1, 18)
+#define M_WIN1_CBR_DEFLICK		SET_BIT(1, 19)
+#define M_WIN1_YUV_CLIP			SET_BIT(1, 20)
+
+#define WIN1_CTRL1			(0x0074)
+#define V_WIN1_YRGB_AXI_GATHER_EN(x)	SET_BIT_MASK(x, 0, 1)
+#define V_WIN1_CBR_AXI_GATHER_EN(x)	SET_BIT_MASK(x, 1, 1)
+#define V_WIN1_BIC_COE_SEL(x)		SET_BIT_MASK(x, 2, 3)
+#define V_WIN1_VSD_YRGB_GT4(x)		SET_BIT_MASK(x, 4, 1)
+#define V_WIN1_VSD_YRGB_GT2(x)		SET_BIT_MASK(x, 5, 1)
+#define V_WIN1_VSD_CBR_GT4(x)		SET_BIT_MASK(x, 6, 1)
+#define V_WIN1_VSD_CBR_GT2(x)		SET_BIT_MASK(x, 7, 1)
+#define V_WIN1_YRGB_AXI_GATHER_NUM(x)	SET_BIT_MASK(x, 8, 0xf)
+#define V_WIN1_CBR_AXI_GATHER_NUM(x)	SET_BIT_MASK(x, 12, 7)
+#define V_WIN1_LINE_LOAD_MODE(x)	SET_BIT_MASK(x, 15, 1)
+#define V_WIN1_YRGB_HOR_SCL_MODE(x)	SET_BIT_MASK(x, 16, 3)
+#define V_WIN1_YRGB_VER_SCL_MODE(x)	SET_BIT_MASK(x, 18, 3)
+#define V_WIN1_YRGB_HSD_MODE(x)		SET_BIT_MASK(x, 20, 3)
+#define V_WIN1_YRGB_VSU_MODE(x)		SET_BIT_MASK(x, 22, 1)
+#define V_WIN1_YRGB_VSD_MODE(x)		SET_BIT_MASK(x, 23, 1)
+#define V_WIN1_CBR_HOR_SCL_MODE(x)	SET_BIT_MASK(x, 24, 3)
+#define V_WIN1_CBR_VER_SCL_MODE(x)	SET_BIT_MASK(x, 26, 3)
+#define V_WIN1_CBR_HSD_MODE(x)		SET_BIT_MASK(x, 28, 3)
+#define V_WIN1_CBR_VSU_MODE(x)		SET_BIT_MASK(x, 30, 1)
+#define V_WIN1_CBR_VSD_MODE(x)		SET_BIT_MASK(x, 31, 1)
+
+#define M_WIN1_YRGB_AXI_GATHER_EN	SET_BIT(1, 0)
+#define M_WIN1_CBR_AXI_GATHER_EN	SET_BIT(1, 1)
+#define M_WIN1_BIC_COE_SEL		SET_BIT(3, 2)
+#define M_WIN1_VSD_YRGB_GT4		SET_BIT(1, 4)
+#define M_WIN1_VSD_YRGB_GT2		SET_BIT(1, 5)
+#define M_WIN1_VSD_CBR_GT4		SET_BIT(1, 6)
+#define M_WIN1_VSD_CBR_GT2		SET_BIT(1, 7)
+#define M_WIN1_YRGB_AXI_GATHER_NUM	SET_BIT(0xf, 8)
+#define M_WIN1_CBR_AXI_GATHER_NUM	SET_BIT(7, 12)
+#define M_WIN1_LINE_LOAD_MODE		SET_BIT(1, 15)
+#define M_WIN1_YRGB_HOR_SCL_MODE	SET_BIT(3, 16)
+#define M_WIN1_YRGB_VER_SCL_MODE	SET_BIT(3, 18)
+#define M_WIN1_YRGB_HSD_MODE		SET_BIT(3, 20)
+#define M_WIN1_YRGB_VSU_MODE		SET_BIT(1, 22)
+#define M_WIN1_YRGB_VSD_MODE		SET_BIT(1, 23)
+#define M_WIN1_CBR_HOR_SCL_MODE		SET_BIT(3, 24)
+#define M_WIN1_CBR_VER_SCL_MODE		SET_BIT(3, 26)
+#define M_WIN1_CBR_HSD_MODE		SET_BIT(3, 28)
+#define M_WIN1_CBR_VSU_MODE		SET_BIT(1, 30)
+#define M_WIN1_CBR_VSD_MODE		SET_BIT((u32)1, 31)
+
+#define WIN1_COLOR_KEY			(0x0078)
+#define V_WIN1_COLOR_KEY(x)		SET_BIT_MASK(x, 0, 0x3fffffff)
+#define V_WIN1_COLOR_KEY_EN(x)		SET_BIT_MASK(x, 31, 1)
+#define M_WIN1_COLOR_KEY		SET_BIT(0x3fffffff, 0)
+#define M_WIN1_COLOR_KEY_EN		SET_BIT((u32)1, 31)
+
+#define WIN1_VIR			(0x007c)
+#define V_WIN1_VIR_STRIDE(x)		SET_BIT_MASK(x, 0, 0x3fff)
+#define V_WIN1_VIR_STRIDE_UV(x)		SET_BIT_MASK(x, 16, 0x3fff)
+#define M_WIN1_VIR_STRIDE		SET_BIT(0x3fff, 0)
+#define M_WIN1_VIR_STRIDE_UV		SET_BIT(0x3fff, 16)
+
+#define WIN1_YRGB_MST			(0x0080)
+#define WIN1_CBR_MST			(0x0084)
+#define WIN1_ACT_INFO			(0x0088)
+#define V_WIN1_ACT_WIDTH(x)		SET_BIT_MASK(x-1, 0, 0x1fff)
+#define V_WIN1_ACT_HEIGHT(x)		SET_BIT_MASK(x-1, 16, 0x1fff)
+#define M_WIN1_ACT_WIDTH		SET_BIT(0x1fff, 0)
+#define M_WIN1_ACT_HEIGHT		SET_BIT(0x1fff, 16)
+
+#define WIN1_DSP_INFO			(0x008c)
+#define V_WIN1_DSP_WIDTH(x)		SET_BIT_MASK(x-1, 0, 0xfff)
+#define V_WIN1_DSP_HEIGHT(x)		SET_BIT_MASK(x-1, 16, 0xfff)
+#define M_WIN1_DSP_WIDTH		SET_BIT(0xfff, 0)
+#define M_WIN1_DSP_HEIGHT		SET_BIT(0xfff, 16)
+
+#define WIN1_DSP_ST			(0x0090)
+#define V_WIN1_DSP_XST(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN1_DSP_YST(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN1_DSP_XST			SET_BIT(0x1fff, 0)
+#define M_WIN1_DSP_YST			SET_BIT(0x1fff, 16)
+
+#define WIN1_SCL_FACTOR_YRGB		(0x0094)
+#define V_WIN1_HS_FACTOR_YRGB(x)	SET_BIT_MASK(x, 0, 0xffff)
+#define V_WIN1_VS_FACTOR_YRGB(x)	SET_BIT_MASK(x, 16, 0xffff)
+#define M_WIN1_HS_FACTOR_YRGB		SET_BIT(0xffff, 0)
+#define M_WIN1_VS_FACTOR_YRGB		SET_BIT((u32)0xffff, 16)
+
+#define WIN1_SCL_FACTOR_CBR		(0x0098)
+#define V_WIN1_HS_FACTOR_CBR(x)		SET_BIT_MASK(x, 0, 0xffff)
+#define V_WIN1_VS_FACTOR_CBR(x)		SET_BIT_MASK(x, 16, 0xffff)
+#define M_WIN1_HS_FACTOR_CBR		SET_BIT(0xffff, 0)
+#define M_WIN1_VS_FACTOR_CBR		SET_BIT((u32)0xffff, 16)
+
+#define WIN1_SCL_OFFSET			(0x009c)
+#define V_WIN1_HS_OFFSET_YRGB(x)	SET_BIT_MASK(x, 0, 0xff)
+#define V_WIN1_HS_OFFSET_CBR(x)		SET_BIT_MASK(x, 8, 0xff)
+#define V_WIN1_VS_OFFSET_YRGB(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_WIN1_VS_OFFSET_CBR(x)		SET_BIT_MASK(x, 24, 0xff)
+
+#define M_WIN1_HS_OFFSET_YRGB		SET_BIT(0xff, 0)
+#define M_WIN1_HS_OFFSET_CBR		SET_BIT(0xff, 8)
+#define M_WIN1_VS_OFFSET_YRGB		SET_BIT(0xff, 16)
+#define M_WIN1_VS_OFFSET_CBR		SET_BIT((u32)0xff, 24)
+
+#define WIN1_SRC_ALPHA_CTRL		(0x00a0)
+#define V_WIN1_SRC_ALPHA_EN(x)		SET_BIT_MASK(x, 0, 1)
+#define V_WIN1_SRC_COLOR_M0(x)		SET_BIT_MASK(x, 1, 1)
+#define V_WIN1_SRC_ALPHA_M0(x)		SET_BIT_MASK(x, 2, 1)
+#define V_WIN1_SRC_BLEND_M0(x)		SET_BIT_MASK(x, 3, 3)
+#define V_WIN1_SRC_ALPHA_CAL_M0(x)	SET_BIT_MASK(x, 5, 1)
+#define V_WIN1_SRC_FACTOR_M0(x)		SET_BIT_MASK(x, 6, 7)
+#define V_WIN1_SRC_GLOBAL_ALPHA(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_WIN1_FADING_VALUE(x)		SET_BIT_MASK(x, 24, 0xff)
+
+#define M_WIN1_SRC_ALPHA_EN		SET_BIT(1, 0)
+#define M_WIN1_SRC_COLOR_M0		SET_BIT(1, 1)
+#define M_WIN1_SRC_ALPHA_M0		SET_BIT(1, 2)
+#define M_WIN1_SRC_BLEND_M0		SET_BIT(3, 3)
+#define M_WIN1_SRC_ALPHA_CAL_M0		SET_BIT(1, 5)
+#define M_WIN1_SRC_FACTOR_M0		SET_BIT(7, 6)
+#define M_WIN1_SRC_GLOBAL_ALPHA		SET_BIT(0xff, 16)
+#define M_WIN1_FADING_VALUE		SET_BIT(0xff, 24)
+
+#define WIN1_DST_ALPHA_CTRL		(0x00a4)
+#define V_WIN1_DST_FACTOR_M0(x)		SET_BIT_MASK(x, 6, 7)
+#define M_WIN1_DST_FACTOR_M0		SET_BIT(7, 6)
+
+#define WIN1_FADING_CTRL		(0x00a8)
+#define V_WIN1_FADING_OFFSET_R(x)	SET_BIT_MASK(x, 0, 0xff)
+#define V_WIN1_FADING_OFFSET_G(x)	SET_BIT_MASK(x, 8, 0xff)
+#define V_WIN1_FADING_OFFSET_B(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_WIN1_FADING_EN(x)		SET_BIT_MASK(x, 24, 1)
+
+#define M_WIN1_FADING_OFFSET_R		SET_BIT(0xff, 0)
+#define M_WIN1_FADING_OFFSET_G		SET_BIT(0xff, 8)
+#define M_WIN1_FADING_OFFSET_B		SET_BIT(0xff, 16)
+#define M_WIN1_FADING_EN		SET_BIT(1, 24)
+
+/* win2 register */
+#define WIN2_CTRL0			(0x00b0)
+#define V_WIN2_EN(x)			SET_BIT_MASK(x, 0, 1)
+#define V_WIN2_DATA_FMT(x)		SET_BIT_MASK(x, 1, 7)
+#define V_WIN2_MST0_EN(x)		SET_BIT_MASK(x, 4, 1)
+#define V_WIN2_MST1_EN(x)		SET_BIT_MASK(x, 5, 1)
+#define V_WIN2_MST2_EN(x)		SET_BIT_MASK(x, 6, 1)
+#define V_WIN2_MST3_EN(x)		SET_BIT_MASK(x, 7, 1)
+#define V_WIN2_INTERLACE_READ(x)	SET_BIT_MASK(x, 8, 1)
+#define V_WIN2_NO_OUTSTANDING(x)	SET_BIT_MASK(x, 9, 1)
+#define V_WIN2_CSC_MODE(x)		SET_BIT_MASK(x, 10, 1)
+#define V_WIN2_RB_SWAP(x)		SET_BIT_MASK(x, 12, 1)
+#define V_WIN2_ALPHA_SWAP(x)		SET_BIT_MASK(x, 13, 1)
+#define V_WIN2_ENDIAN_MODE(x)		SET_BIT_MASK(x, 14, 1)
+#define V_WIN2_LUT_EN(x)		SET_BIT_MASK(x, 18, 1)
+
+#define M_WIN2_EN			SET_BIT(1, 0)
+#define M_WIN2_DATA_FMT			SET_BIT(7, 1)
+#define M_WIN2_MST0_EN			SET_BIT(1, 4)
+#define M_WIN2_MST1_EN			SET_BIT(1, 5)
+#define M_WIN2_MST2_EN			SET_BIT(1, 6)
+#define M_WIN2_MST3_EN			SET_BIT(1, 7)
+#define M_WIN2_INTERLACE_READ		SET_BIT(1, 8)
+#define M_WIN2_NO_OUTSTANDING		SET_BIT(1, 9)
+#define M_WIN2_CSC_MODE			SET_BIT(1, 10)
+#define M_WIN2_RB_SWAP			SET_BIT(1, 12)
+#define M_WIN2_ALPHA_SWAP		SET_BIT(1, 13)
+#define M_WIN2_ENDIAN_MODE		SET_BIT(1, 14)
+#define M_WIN2_LUT_EN			SET_BIT(1, 18)
+
+#define WIN2_CTRL1			(0x00b4)
+#define V_WIN2_AXI_GATHER_EN(x)		SET_BIT_MASK(x, 0, 1)
+#define V_WIN2_AXI_GATHER_NUM(x)	SET_BIT_MASK(x, 4, 0xf)
+#define M_WIN2_AXI_GATHER_EN		SET_BIT(1, 0)
+#define M_WIN2_AXI_GATHER_NUM		SET_BIT(0xf, 4)
+
+#define WIN2_VIR0_1			(0x00b8)
+#define V_WIN2_VIR_STRIDE0(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN2_VIR_STRIDE1(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN2_VIR_STRIDE0		SET_BIT(0x1fff, 0)
+#define M_WIN2_VIR_STRIDE1		SET_BIT(0x1fff, 16)
+
+#define WIN2_VIR2_3			(0x00bc)
+#define V_WIN2_VIR_STRIDE2(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN2_VIR_STRIDE3(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN2_VIR_STRIDE2		SET_BIT(0x1fff, 0)
+#define M_WIN2_VIR_STRIDE3		SET_BIT(0x1fff, 16)
+
+#define WIN2_MST0			(0x00c0)
+#define WIN2_DSP_INFO0			(0x00c4)
+#define V_WIN2_DSP_WIDTH0(x)		SET_BIT_MASK(x-1, 0, 0xfff)
+#define V_WIN2_DSP_HEIGHT0(x)		SET_BIT_MASK(x-1, 16, 0xfff)
+#define M_WIN2_DSP_WIDTH0		SET_BIT(0xfff, 0)
+#define M_WIN2_DSP_HEIGHT0		SET_BIT(0xfff, 16)
+
+#define WIN2_DSP_ST0			(0x00c8)
+#define V_WIN2_DSP_XST0(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN2_DSP_YST0(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN2_DSP_XST0			SET_BIT(0x1fff, 0)
+#define M_WIN2_DSP_YST0			SET_BIT(0x1fff, 16)
+
+#define WIN2_COLOR_KEY			(0x00cc)
+#define V_WIN2_COLOR_KEY(x)		SET_BIT_MASK(x, 0, 0xffffff)
+#define V_WIN2_KEY_EN(x)		SET_BIT_MASK(x, 24, 1)
+#define M_WIN2_COLOR_KEY		SET_BIT(0xffffff, 0)
+#define M_WIN2_KEY_EN			SET_BIT((u32)1, 24)
+
+#define WIN2_MST1			(0x00d0)
+#define WIN2_DSP_INFO1			(0x00d4)
+#define V_WIN2_DSP_WIDTH1(x)		SET_BIT_MASK(x-1, 0, 0xfff)
+#define V_WIN2_DSP_HEIGHT1(x)		SET_BIT_MASK(x-1, 16, 0xfff)
+
+#define M_WIN2_DSP_WIDTH1		SET_BIT(0xfff, 0)
+#define M_WIN2_DSP_HEIGHT1		SET_BIT(0xfff, 16)
+
+#define WIN2_DSP_ST1			(0x00d8)
+#define V_WIN2_DSP_XST1(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN2_DSP_YST1(x)		SET_BIT_MASK(x, 16, 0x1fff)
+
+#define M_WIN2_DSP_XST1			SET_BIT(0x1fff, 0)
+#define M_WIN2_DSP_YST1			SET_BIT(0x1fff, 16)
+
+#define WIN2_SRC_ALPHA_CTRL		(0x00dc)
+#define V_WIN2_SRC_ALPHA_EN(x)		SET_BIT_MASK(x, 0, 1)
+#define V_WIN2_SRC_COLOR_M0(x)		SET_BIT_MASK(x, 1, 1)
+#define V_WIN2_SRC_ALPHA_M0(x)		SET_BIT_MASK(x, 2, 1)
+#define V_WIN2_SRC_BLEND_M0(x)		SET_BIT_MASK(x, 3, 3)
+#define V_WIN2_SRC_ALPHA_CAL_M0(x)	SET_BIT_MASK(x, 5, 1)
+#define V_WIN2_SRC_FACTOR_M0(x)		SET_BIT_MASK(x, 6, 7)
+#define V_WIN2_SRC_GLOBAL_ALPHA(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_WIN2_FADING_VALUE(x)		SET_BIT_MASK(x, 24, 0xff)
+
+#define M_WIN2_SRC_ALPHA_EN		SET_BIT(1, 0)
+#define M_WIN2_SRC_COLOR_M0		SET_BIT(1, 1)
+#define M_WIN2_SRC_ALPHA_M0		SET_BIT(1, 2)
+#define M_WIN2_SRC_BLEND_M0		SET_BIT(3, 3)
+#define M_WIN2_SRC_ALPHA_CAL_M0		SET_BIT(1, 5)
+#define M_WIN2_SRC_FACTOR_M0		SET_BIT(7, 6)
+#define M_WIN2_SRC_GLOBAL_ALPHA		SET_BIT(0xff, 16)
+#define M_WIN2_FADING_VALUE		SET_BIT(0xff, 24)
+
+#define WIN2_MST2			(0x00e0)
+#define WIN2_DSP_INFO2			(0x00e4)
+#define V_WIN2_DSP_WIDTH2(x)		SET_BIT_MASK(x-1, 0, 0xfff)
+#define V_WIN2_DSP_HEIGHT2(x)		SET_BIT_MASK(x-1, 16, 0xfff)
+
+#define M_WIN2_DSP_WIDTH2		SET_BIT(0xfff, 0)
+#define M_WIN2_DSP_HEIGHT2		SET_BIT(0xfff, 16)
+
+#define WIN2_DSP_ST2			(0x00e8)
+#define V_WIN2_DSP_XST2(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN2_DSP_YST2(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN2_DSP_XST2			SET_BIT(0x1fff, 0)
+#define M_WIN2_DSP_YST2			SET_BIT(0x1fff, 16)
+
+#define WIN2_DST_ALPHA_CTRL		(0x00ec)
+#define V_WIN2_DST_FACTOR_M0(x)		SET_BIT_MASK(x, 6, 7)
+#define M_WIN2_DST_FACTOR_M0		SET_BIT(7, 6)
+
+#define WIN2_MST3			(0x00f0)
+#define WIN2_DSP_INFO3			(0x00f4)
+#define V_WIN2_DSP_WIDTH3(x)		SET_BIT_MASK(x-1, 0, 0xfff)
+#define V_WIN2_DSP_HEIGHT3(x)		SET_BIT_MASK(x-1, 16, 0xfff)
+#define M_WIN2_DSP_WIDTH3		SET_BIT(0xfff, 0)
+#define M_WIN2_DSP_HEIGHT3		SET_BIT(0xfff, 16)
+
+#define WIN2_DSP_ST3			(0x00f8)
+#define V_WIN2_DSP_XST3(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN2_DSP_YST3(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN2_DSP_XST3			SET_BIT(0x1fff, 0)
+#define M_WIN2_DSP_YST3			SET_BIT(0x1fff, 16)
+
+#define WIN2_FADING_CTRL		(0x00fc)
+#define V_WIN2_FADING_OFFSET_R(x)	SET_BIT_MASK(x, 0, 0xff)
+#define V_WIN2_FADING_OFFSET_G(x)	SET_BIT_MASK(x, 8, 0xff)
+#define V_WIN2_FADING_OFFSET_B(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_WIN2_FADING_EN(x)		SET_BIT_MASK(x, 24, 1)
+
+#define M_WIN2_FADING_OFFSET_R		SET_BIT(0xff, 0)
+#define M_WIN2_FADING_OFFSET_G		SET_BIT(0xff, 8)
+#define M_WIN2_FADING_OFFSET_B		SET_BIT(0xff, 16)
+#define M_WIN2_FADING_EN		SET_BIT(1, 24)
+
+/* win3 register */
+#define WIN3_CTRL0			(0x0100)
+#define V_WIN3_EN(x)			SET_BIT_MASK(x, 0, 1)
+#define V_WIN3_DATA_FMT(x)		SET_BIT_MASK(x, 1, 7)
+#define V_WIN3_MST0_EN(x)		SET_BIT_MASK(x, 4, 1)
+#define V_WIN3_MST1_EN(x)		SET_BIT_MASK(x, 5, 1)
+#define V_WIN3_MST2_EN(x)		SET_BIT_MASK(x, 6, 1)
+#define V_WIN3_MST3_EN(x)		SET_BIT_MASK(x, 7, 1)
+#define V_WIN3_INTERLACE_READ(x)	SET_BIT_MASK(x, 8, 1)
+#define V_WIN3_NO_OUTSTANDING(x)	SET_BIT_MASK(x, 9, 1)
+#define V_WIN3_CSC_MODE(x)		SET_BIT_MASK(x, 10, 1)
+#define V_WIN3_RB_SWAP(x)		SET_BIT_MASK(x, 12, 1)
+#define V_WIN3_ALPHA_SWAP(x)		SET_BIT_MASK(x, 13, 1)
+#define V_WIN3_ENDIAN_MODE(x)		SET_BIT_MASK(x, 14, 1)
+#define V_WIN3_LUT_EN(x)		SET_BIT_MASK(x, 18, 1)
+
+#define M_WIN3_EN			SET_BIT(1, 0)
+#define M_WIN3_DATA_FMT			SET_BIT(7, 1)
+#define M_WIN3_MST0_EN			SET_BIT(1, 4)
+#define M_WIN3_MST1_EN			SET_BIT(1, 5)
+#define M_WIN3_MST2_EN			SET_BIT(1, 6)
+#define M_WIN3_MST3_EN			SET_BIT(1, 7)
+#define M_WIN3_INTERLACE_READ		SET_BIT(1, 8)
+#define M_WIN3_NO_OUTSTANDING		SET_BIT(1, 9)
+#define M_WIN3_CSC_MODE			SET_BIT(1, 10)
+#define M_WIN3_RB_SWAP			SET_BIT(1, 12)
+#define M_WIN3_ALPHA_SWAP		SET_BIT(1, 13)
+#define M_WIN3_ENDIAN_MODE		SET_BIT(1, 14)
+#define M_WIN3_LUT_EN			SET_BIT(1, 18)
+
+#define WIN3_CTRL1			(0x0104)
+#define V_WIN3_AXI_GATHER_EN(x)		SET_BIT_MASK(x, 0, 1)
+#define V_WIN3_AXI_GATHER_NUM(x)	SET_BIT_MASK(x, 4, 0xf)
+#define M_WIN3_AXI_GATHER_EN		SET_BIT(1, 0)
+#define M_WIN3_AXI_GATHER_NUM		SET_BIT(0xf, 4)
+
+#define WIN3_VIR0_1			(0x0108)
+#define V_WIN3_VIR_STRIDE0(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN3_VIR_STRIDE1(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN3_VIR_STRIDE0		SET_BIT(0x1fff, 0)
+#define M_WIN3_VIR_STRIDE1		SET_BIT(0x1fff, 16)
+
+#define WIN3_VIR2_3			(0x010c)
+#define V_WIN3_VIR_STRIDE2(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN3_VIR_STRIDE3(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN3_VIR_STRIDE2		SET_BIT(0x1fff, 0)
+#define M_WIN3_VIR_STRIDE3		SET_BIT(0x1fff, 16)
+
+#define WIN3_MST0			(0x0110)
+#define WIN3_DSP_INFO0			(0x0114)
+#define V_WIN3_DSP_WIDTH0(x)		SET_BIT_MASK(x-1, 0, 0xfff)
+#define V_WIN3_DSP_HEIGHT0(x)		SET_BIT_MASK(x-1, 16, 0xfff)
+#define M_WIN3_DSP_WIDTH0		SET_BIT(0xfff, 0)
+#define M_WIN3_DSP_HEIGHT0		SET_BIT(0xfff, 16)
+
+#define WIN3_DSP_ST0			(0x0118)
+#define V_WIN3_DSP_XST0(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN3_DSP_YST0(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN3_DSP_XST0			SET_BIT(0x1fff, 0)
+#define M_WIN3_DSP_YST0			SET_BIT(0x1fff, 16)
+
+#define WIN3_COLOR_KEY			(0x011c)
+#define V_WIN3_COLOR_KEY(x)		SET_BIT_MASK(x, 0, 0xffffff)
+#define V_WIN3_KEY_EN(x)		SET_BIT_MASK(x, 24, 1)
+#define M_WIN3_COLOR_KEY		SET_BIT(0xffffff, 0)
+#define M_WIN3_KEY_EN			SET_BIT((u32)1, 24)
+
+#define WIN3_MST1			(0x0120)
+#define WIN3_DSP_INFO1			(0x0124)
+#define V_WIN3_DSP_WIDTH1(x)		SET_BIT_MASK(x-1, 0, 0xfff)
+#define V_WIN3_DSP_HEIGHT1(x)		SET_BIT_MASK(x-1, 16, 0xfff)
+#define M_WIN3_DSP_WIDTH1		SET_BIT(0xfff, 0)
+#define M_WIN3_DSP_HEIGHT1		SET_BIT(0xfff, 16)
+
+#define WIN3_DSP_ST1			(0x0128)
+#define V_WIN3_DSP_XST1(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN3_DSP_YST1(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN3_DSP_XST1			SET_BIT(0x1fff, 0)
+#define M_WIN3_DSP_YST1			SET_BIT(0x1fff, 16)
+
+#define WIN3_SRC_ALPHA_CTRL		(0x012c)
+#define V_WIN3_SRC_ALPHA_EN(x)		SET_BIT_MASK(x, 0, 1)
+#define V_WIN3_SRC_COLOR_M0(x)		SET_BIT_MASK(x, 1, 1)
+#define V_WIN3_SRC_ALPHA_M0(x)		SET_BIT_MASK(x, 2, 1)
+#define V_WIN3_SRC_BLEND_M0(x)		SET_BIT_MASK(x, 3, 3)
+#define V_WIN3_SRC_ALPHA_CAL_M0(x)	SET_BIT_MASK(x, 5, 1)
+#define V_WIN3_SRC_FACTOR_M0(x)		SET_BIT_MASK(x, 6, 7)
+#define V_WIN3_SRC_GLOBAL_ALPHA(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_WIN3_FADING_VALUE(x)		SET_BIT_MASK(x, 24, 0xff)
+
+#define M_WIN3_SRC_ALPHA_EN		SET_BIT(1, 0)
+#define M_WIN3_SRC_COLOR_M0		SET_BIT(1, 1)
+#define M_WIN3_SRC_ALPHA_M0		SET_BIT(1, 2)
+#define M_WIN3_SRC_BLEND_M0		SET_BIT(3, 3)
+#define M_WIN3_SRC_ALPHA_CAL_M0		SET_BIT(1, 5)
+#define M_WIN3_SRC_FACTOR_M0		SET_BIT(7, 6)
+#define M_WIN3_SRC_GLOBAL_ALPHA		SET_BIT(0xff, 16)
+#define M_WIN3_FADING_VALUE		SET_BIT(0xff, 24)
+
+#define WIN3_MST2			(0x0130)
+#define WIN3_DSP_INFO2			(0x0134)
+#define V_WIN3_DSP_WIDTH2(x)		SET_BIT_MASK(x-1, 0, 0xfff)
+#define V_WIN3_DSP_HEIGHT2(x)		SET_BIT_MASK(x-1, 16, 0xfff)
+#define M_WIN3_DSP_WIDTH2		SET_BIT(0xfff, 0)
+#define M_WIN3_DSP_HEIGHT2		SET_BIT(0xfff, 16)
+
+#define WIN3_DSP_ST2			(0x0138)
+#define V_WIN3_DSP_XST2(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN3_DSP_YST2(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN3_DSP_XST2			SET_BIT(0x1fff, 0)
+#define M_WIN3_DSP_YST2			SET_BIT(0x1fff, 16)
+
+#define WIN3_DST_ALPHA_CTRL		(0x013c)
+#define V_WIN3_DST_FACTOR_M0(x)		SET_BIT_MASK(x, 6, 7)
+#define M_WIN3_DST_FACTOR_M0		SET_BIT(7, 6)
+
+#define WIN3_MST3			(0x0140)
+#define WIN3_DSP_INFO3			(0x0144)
+#define V_WIN3_DSP_WIDTH3(x)		SET_BIT_MASK(x-1, 0, 0xfff)
+#define V_WIN3_DSP_HEIGHT3(x)		SET_BIT_MASK(x-1, 16, 0xfff)
+#define M_WIN3_DSP_WIDTH3		SET_BIT(0xfff, 0)
+#define M_WIN3_DSP_HEIGHT3		SET_BIT(0xfff, 16)
+
+#define WIN3_DSP_ST3			(0x0148)
+#define V_WIN3_DSP_XST3(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN3_DSP_YST3(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN3_DSP_XST3			SET_BIT(0x1fff, 0)
+#define M_WIN3_DSP_YST3			SET_BIT(0x1fff, 16)
+
+#define WIN3_FADING_CTRL		(0x014c)
+#define V_WIN3_FADING_OFFSET_R(x)	SET_BIT_MASK(x, 0, 0xff)
+#define V_WIN3_FADING_OFFSET_G(x)	SET_BIT_MASK(x, 8, 0xff)
+#define V_WIN3_FADING_OFFSET_B(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_WIN3_FADING_EN(x)		SET_BIT_MASK(x, 24, 1)
+
+#define M_WIN3_FADING_OFFSET_R		SET_BIT(0xff, 0)
+#define M_WIN3_FADING_OFFSET_G		SET_BIT(0xff, 8)
+#define M_WIN3_FADING_OFFSET_B		SET_BIT(0xff, 16)
+#define M_WIN3_FADING_EN		SET_BIT(1, 24)
+
+/* hwc register */
+#define HWC_CTRL0			(0x0150)
+#define V_HWC_EN(x)			SET_BIT_MASK(x, 0, 1)
+#define V_HWC_DATA_FMT(x)		SET_BIT_MASK(x, 1, 7)
+#define V_HWC_MODE(x)			SET_BIT_MASK(x, 4, 1)
+#define V_HWC_SIZE(x)			SET_BIT_MASK(x, 5, 3)
+#define V_HWC_INTERLACE_READ(x)		SET_BIT_MASK(x, 8, 1)
+#define V_HWC_NO_OUTSTANDING(x)		SET_BIT_MASK(x, 9, 1)
+#define V_HWC_CSC_MODE(x)		SET_BIT_MASK(x, 10, 1)
+#define V_HWC_RB_SWAP(x)		SET_BIT_MASK(x, 12, 1)
+#define V_HWC_ALPHA_SWAP(x)		SET_BIT_MASK(x, 13, 1)
+#define V_HWC_ENDIAN_MODE(x)		SET_BIT_MASK(x, 14, 1)
+#define V_HWC_LUT_EN(x)			SET_BIT_MASK(x, 18, 1)
+
+#define M_HWC_EN			SET_BIT(1, 0)
+#define M_HWC_DATA_FMT			SET_BIT(7, 1)
+#define M_HWC_MODE			SET_BIT(1, 4)
+#define M_HWC_SIZE			SET_BIT(3, 5)
+#define M_HWC_INTERLACE_READ		SET_BIT(1, 8)
+#define M_HWC_NO_OUTSTANDING		SET_BIT(1, 9)
+#define M_HWC_CSC_MODE			SET_BIT(1, 10)
+#define M_HWC_RB_SWAP			SET_BIT(1, 12)
+#define M_HWC_ALPHA_SWAP		SET_BIT(1, 13)
+#define M_HWC_ENDIAN_MODE		SET_BIT(1, 14)
+#define M_HWC_LUT_EN			SET_BIT(1, 18)
+
+#define HWC_CTRL1			(0x0154)
+#define V_HWC_AXI_GATHER_EN(x)		SET_BIT_MASK(x, 0, 1)
+#define V_HWC_AXI_GATHER_NUM(x)		SET_BIT_MASK(x, 4, 7)
+#define M_HWC_AXI_GATHER_EN		SET_BIT(1, 0)
+#define M_HWC_AXI_GATHER_NUM		SET_BIT(7, 4)
+
+#define HWC_MST				(0x0158)
+#define HWC_DSP_ST			(0x015c)
+#define V_HWC_DSP_XST3(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_HWC_DSP_YST3(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_HWC_DSP_XST3			SET_BIT(0x1fff, 0)
+#define M_HWC_DSP_YST3			SET_BIT(0x1fff, 16)
+
+#define HWC_SRC_ALPHA_CTRL		(0x0160)
+#define V_HWC_SRC_ALPHA_EN(x)		SET_BIT_MASK(x, 0, 1)
+#define V_HWC_SRC_COLOR_M0(x)		SET_BIT_MASK(x, 1, 1)
+#define V_HWC_SRC_ALPHA_M0(x)		SET_BIT_MASK(x, 2, 1)
+#define V_HWC_SRC_BLEND_M0(x)		SET_BIT_MASK(x, 3, 3)
+#define V_HWC_SRC_ALPHA_CAL_M0(x)	SET_BIT_MASK(x, 5, 1)
+#define V_HWC_SRC_FACTOR_M0(x)		SET_BIT_MASK(x, 6, 7)
+#define V_HWC_SRC_GLOBAL_ALPHA(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_HWC_FADING_VALUE(x)		SET_BIT_MASK(x, 24, 0xff)
+
+#define M_HWC_SRC_ALPHA_EN		SET_BIT(1, 0)
+#define M_HWC_SRC_COLOR_M0		SET_BIT(1, 1)
+#define M_HWC_SRC_ALPHA_M0		SET_BIT(1, 2)
+#define M_HWC_SRC_BLEND_M0		SET_BIT(3, 3)
+#define M_HWC_SRC_ALPHA_CAL_M0		SET_BIT(1, 5)
+#define M_HWC_SRC_FACTOR_M0		SET_BIT(7, 6)
+#define M_HWC_SRC_GLOBAL_ALPHA		SET_BIT(0xff, 16)
+#define M_HWC_FADING_VALUE		SET_BIT(0xff, 24)
+
+#define HWC_DST_ALPHA_CTRL		(0x0164)
+#define V_HWC_DST_FACTOR_M0(x)		SET_BIT_MASK(x, 6, 7)
+#define M_HWC_DST_FACTOR_M0		SET_BIT(7, 6)
+
+#define HWC_FADING_CTRL			(0x0168)
+#define V_HWC_FADING_OFFSET_R(x)	SET_BIT_MASK(x, 0, 0xff)
+#define V_HWC_FADING_OFFSET_G(x)	SET_BIT_MASK(x, 8, 0xff)
+#define V_HWC_FADING_OFFSET_B(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_HWC_FADING_EN(x)		SET_BIT_MASK(x, 24, 1)
+
+#define M_HWC_FADING_OFFSET_R		SET_BIT(0xff, 0)
+#define M_HWC_FADING_OFFSET_G		SET_BIT(0xff, 8)
+#define M_HWC_FADING_OFFSET_B		SET_BIT(0xff, 16)
+#define M_HWC_FADING_EN			SET_BIT(1, 24)
+
+/* post process register */
+#define POST_DSP_HACT_INFO		(0x0170)
+#define V_DSP_HACT_END_POST(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_DSP_HACT_ST_POST(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_DSP_HACT_END_POST		SET_BIT(0x1fff, 0)
+#define M_DSP_HACT_ST_POST		SET_BIT(0x1fff, 16)
+
+#define POST_DSP_VACT_INFO		(0x0174)
+#define V_DSP_VACT_END_POST(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_DSP_VACT_ST_POST(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_DSP_VACT_END_POST		SET_BIT(0x1fff, 0)
+#define M_DSP_VACT_ST_POST		SET_BIT(0x1fff, 16)
+
+#define POST_SCL_FACTOR_YRGB		(0x0178)
+#define V_POST_HS_FACTOR_YRGB(x)	SET_BIT_MASK(x, 0, 0xffff)
+#define V_POST_VS_FACTOR_YRGB(x)	SET_BIT_MASK(x, 16, 0xffff)
+#define M_POST_HS_FACTOR_YRGB		SET_BIT(0xffff, 0)
+#define M_POST_VS_FACTOR_YRGB		SET_BIT(0xffff, 16)
+
+#define POST_SCL_CTRL			(0x0180)
+#define V_POST_HOR_SD_EN(x)		SET_BIT_MASK(x, 0, 1)
+#define V_POST_VER_SD_EN(x)		SET_BIT_MASK(x, 1, 1)
+
+#define M_POST_HOR_SD_EN		SET_BIT(0x1, 0)
+#define M_POST_VER_SD_EN		SET_BIT(0x1, 1)
+
+#define POST_DSP_VACT_INFO_F1		(0x0184)
+#define V_DSP_VACT_END_POST_F1(x)	SET_BIT_MASK(x, 0, 0x1fff)
+#define V_DSP_VACT_ST_POST_F1(x)	SET_BIT_MASK(x, 16, 0x1fff)
+
+#define M_DSP_VACT_END_POST_F1		SET_BIT(0x1fff, 0)
+#define M_DSP_VACT_ST_POST_F1		SET_BIT(0x1fff, 16)
+
+#define DSP_HTOTAL_HS_END		(0x0188)
+#define V_DSP_HS_PW(x)			SET_BIT_MASK(x, 0, 0x1fff)
+#define V_DSP_HTOTAL(x)			SET_BIT_MASK(x, 16, 0x1fff)
+#define M_DSP_HS_PW			SET_BIT(0x1fff, 0)
+#define M_DSP_HTOTAL			SET_BIT(0x1fff, 16)
+
+#define DSP_HACT_ST_END			(0x018c)
+#define V_DSP_HACT_END(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_DSP_HACT_ST(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_DSP_HACT_END			SET_BIT(0x1fff, 0)
+#define M_DSP_HACT_ST			SET_BIT(0x1fff, 16)
+
+#define DSP_VTOTAL_VS_END		(0x0190)
+#define V_DSP_VS_PW(x)			SET_BIT_MASK(x, 0, 0x1fff)
+#define V_DSP_VTOTAL(x)			SET_BIT_MASK(x, 16, 0x1fff)
+#define M_DSP_VS_PW			SET_BIT(0x1fff, 0)
+#define M_DSP_VTOTAL			SET_BIT(0x1fff, 16)
+
+#define DSP_VACT_ST_END			(0x0194)
+#define V_DSP_VACT_END(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_DSP_VACT_ST(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_DSP_VACT_END			SET_BIT(0x1fff, 0)
+#define M_DSP_VACT_ST			SET_BIT(0x1fff, 16)
+
+#define DSP_VS_ST_END_F1		(0x0198)
+#define V_DSP_VS_END_F1(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_DSP_VS_ST_F1(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_DSP_VS_END_F1			SET_BIT(0x1fff, 0)
+#define M_DSP_VS_ST_F1			SET_BIT(0x1fff, 16)
+
+#define DSP_VACT_ST_END_F1		(0x019c)
+#define V_DSP_VACT_END_F1(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_DSP_VAC_ST_F1(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_DSP_VACT_END_F1		SET_BIT(0x1fff, 0)
+#define M_DSP_VAC_ST_F1			SET_BIT(0x1fff, 16)
+
+enum lb_mode {
+	LB_YUV_3840X5 = 0x0,
+	LB_YUV_2560X8,
+	LB_RGB_3840X2,
+	LB_RGB_2560X4,
+	LB_RGB_1920X5,
+	LB_RGB_1280X8
+};
+
+enum sacle_up_mode {
+	SCALE_UP_BIL = 0x0,
+	SCALE_UP_BIC
+};
+
+enum scale_down_mode {
+	SCALE_DOWN_BIL = 0x0,
+	SCALE_DOWN_AVG
+};
+
+/* ALPHA BLENDING MODE */
+enum alpha_mode {
+	AB_USER_DEFINE = 0x0,
+	AB_CLEAR,
+	AB_SRC,
+	AB_DST,
+	AB_SRC_OVER,
+	AB_DST_OVER,
+	AB_SRC_IN,
+	AB_DST_IN,
+	AB_SRC_OUT,
+	AB_DST_OUT,
+	AB_SRC_ATOP,
+	AB_DST_ATOP,
+	XOR,
+	AB_SRC_OVER_GLOBAL
+};
+
+enum src_alpha_mode {
+	AA_STRAIGHT = 0x0,
+	AA_INVERSE
+};
+
+enum global_alpha_mode {
+	AA_GLOBAL = 0x0,
+	AA_PER_PIX,
+	AA_PER_PIX_GLOBAL
+};
+
+enum src_alpha_sel {
+	AA_SAT = 0x0,
+	AA_NO_SAT
+};
+
+enum src_color_mode {
+	AA_SRC_PRE_MUL = 0x0,
+	AA_SRC_NO_PRE_MUL
+};
+
+enum factor_mode {
+	AA_ZERO = 0x0,
+	AA_ONE,
+	AA_SRC,
+	AA_SRC_INVERSE,
+	AA_SRC_GLOBAL
+};
+
+struct lcdc_device {
+	int id;
+	struct device *dev;
+	struct lcdc_driver lcdc_drv;
+
+	struct drm_display_mode *mode;
+
+	void __iomem *regs;
+	/* back up reg */
+	void *regsbak;
+	/* physical basic address of lcdc register*/
+	u32 reg_phy_base;
+	/* physical map length of lcdc register*/
+	u32 len;
+	/* one time only one process allowed to config the register */
+	spinlock_t reg_lock;
+
+	int __iomem *dsp_lut_addr_base;
+
+	/* used for primary or extended display device */
+	int prop;
+	bool pre_init;
+	/* if aclk or hclk is closed , cess to register is not allowed */
+	bool clk_on;
+	/* active layer counter, hen atv_layer_cnt = 0, isable lcdc */
+	u8 atv_layer_cnt;
+
+	unsigned int irq;
+
+	/* lcdc AHP clk */
+	struct clk *hclk;
+	/* lcdc dclk */
+	struct clk *dclk;
+	/* lcdc share memory frequency */
+	struct clk *aclk;
+	u32 pixclock;
+	/* 1:standby, 0:wrok */
+	u32 standby;
+};
+
+struct alpha_config {
+	/* win0_src_alpha_m0 */
+	enum src_alpha_mode src_alpha_mode;
+	/* win0_src_global_alpha */
+	u32 src_global_alpha_val;
+	/* win0_src_blend_m0 */
+	enum global_alpha_mode src_global_alpha_mode;
+	/* win0_src_alpha_cal_m0 */
+	enum src_alpha_sel src_alpha_cal_m0;
+	/* win0_src_color_m0 */
+	enum src_color_mode src_color_mode;
+	/* win0_src_factor_m0 */
+	enum factor_mode src_factor_mode;
+	/* win0_dst_factor_m0 */
+	enum factor_mode dst_factor_mode;
+};
+
+static inline void lcdc_writel(struct lcdc_device *lcdc_dev, u32 offset, u32 v)
+{
+	u32 *_pv = (u32 *)lcdc_dev->regsbak;
+
+	_pv += (offset >> 2);
+	*_pv = v;
+	writel_relaxed(v, lcdc_dev->regs + offset);
+}
+
+static inline u32 lcdc_readl(struct lcdc_device *lcdc_dev, u32 offset)
+{
+	u32 v;
+	u32 *_pv = (u32 *)lcdc_dev->regsbak;
+
+	_pv += (offset >> 2);
+	v = readl_relaxed(lcdc_dev->regs + offset);
+	*_pv = v;
+
+	return v;
+}
+
+static inline u32 lcdc_read_bit(struct lcdc_device *lcdc_dev,
+				u32 offset, u32 msk)
+{
+	u32 _v = readl_relaxed(lcdc_dev->regs + offset);
+
+	_v &= msk;
+
+	return _v >> msk;
+}
+
+static inline void lcdc_set_bit(struct lcdc_device *lcdc_dev,
+				u32 offset, u32 msk)
+{
+	u32 *_pv = (u32 *)lcdc_dev->regsbak;
+
+	_pv += (offset >> 2);
+	(*_pv) |= msk;
+	writel_relaxed(*_pv, lcdc_dev->regs + offset);
+}
+
+static inline void lcdc_clr_bit(struct lcdc_device *lcdc_dev,
+				u32 offset, u32 msk)
+{
+	u32 *_pv = (u32 *)lcdc_dev->regsbak;
+
+	_pv += (offset >> 2);
+	(*_pv) &= (~msk);
+	writel_relaxed(*_pv, lcdc_dev->regs + offset);
+}
+
+static inline void lcdc_msk_reg(struct lcdc_device *lcdc_dev,
+				u32 offset, u32 msk, u32 v)
+{
+	u32 *_pv = (u32 *)lcdc_dev->regsbak;
+
+	_pv += (offset >> 2);
+	(*_pv) &= (~msk);
+	(*_pv) |= v;
+	writel_relaxed(*_pv, lcdc_dev->regs + offset);
+}
+
+static inline void lcdc_cfg_done(struct lcdc_device *lcdc_dev)
+{
+	writel_relaxed(0x01, lcdc_dev->regs + REG_CFG_DONE);
+	dsb();
+}
+#endif /* _RK3288_LCDC_H_ */
-- 
1.7.9.5

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

* [PATCH 6/9] Add devicetree bindings for Rockchip Soc LVDS
@ 2014-08-04  4:53   ` mark yao
  0 siblings, 0 replies; 38+ messages in thread
From: mark yao @ 2014-08-04  4:53 UTC (permalink / raw)
  To: heiko, Rob Clark, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Randy Dunlap, David Airlie,
	Grant Likely, Greg Kroah-Hartman, John Stultz, Rom Lemarchand
  Cc: devicetree, linux-doc, linux-kernel, dri-devel, linux-api, olof,
	djkurtz, xjq, kfx, cym, cf, zyw, zwl, xxm, huangtao, kever.yang,
	zhangqing, yxj, wxt, xw, mark yao

Signed-off-by: mark yao <yzq@rock-chips.com>
---
 .../devicetree/bindings/video/rockchip-panel.txt   |   30 ++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/Documentation/devicetree/bindings/video/rockchip-panel.txt b/Documentation/devicetree/bindings/video/rockchip-panel.txt
index 9fc200a..f599806 100644
--- a/Documentation/devicetree/bindings/video/rockchip-panel.txt
+++ b/Documentation/devicetree/bindings/video/rockchip-panel.txt
@@ -50,3 +50,33 @@ Example:
 		};
 
 	};
+
+Rockchip RK3288 LVDS interface
+================================
+Required properties:
+-compatible: "rockchip,rk3288-lvds";
+
+- reg: physical base address of the controller and length
+- clocks: from common clock binding: handle to dp clock.
+	of memory mapped region.
+- clock-names: from common clock binding: Shall be "pclk_lvds".
+	pclk_lvds: for power domain, if it disable soc will power down
+- rockchip,grf: this soc should set GRF regs, so need get grf here.
+- rockchip,data-mapping: should be "vesa" or "jeida"
+	This describes how the color bits are laid out in the
+	serialized LVDS signal.
+- rockchip,data-width: should be <18> or <24>
+- rockchip,panel: required a panel node
+
+Example:
+	lvds: lvds@ff96c000 {
+		compatible = "rockchip,rk3288-lvds";
+		reg = <0xff96c000 0x4000>;
+		clocks = <&cru PCLK_LVDS_PHY>;
+		clock-names = "pclk_lvds";
+
+		rockchip,grf = <&grf>;
+		rockchip,data-mapping = "jeida";
+		rockchip,data-width = <24>;
+		rockchip,panel = <&panel>;
+	};
-- 
1.7.9.5



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

* [PATCH 6/9] Add devicetree bindings for Rockchip Soc LVDS
@ 2014-08-04  4:53   ` mark yao
  0 siblings, 0 replies; 38+ messages in thread
From: mark yao @ 2014-08-04  4:53 UTC (permalink / raw)
  To: heiko-4mtYJXux2i+zQB+pC5nmwQ, Rob Clark, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, Randy Dunlap,
	David Airlie, Grant Likely, Greg Kroah-Hartman, John Stultz,
	Rom Lemarchand
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-api-u79uwXL29TY76Z2rM5mHXA, olof-nZhT3qVonbNeoWH0uzbU5w,
	djkurtz-F7+t8E8rja9g9hUCZPvPmw, xjq-TNX95d0MmH7DzftRWevZcw,
	kfx-TNX95d0MmH7DzftRWevZcw, cym-TNX95d0MmH7DzftRWevZcw,
	cf-TNX95d0MmH7DzftRWevZcw, zyw-TNX95d0MmH7DzftRWevZcw,
	zwl-TNX95d0MmH7DzftRWevZcw, xxm-TNX95d0MmH7DzftRWevZcw,
	huangtao-TNX95d0MmH7DzftRWevZcw,
	kever.yang-TNX95d0MmH7DzftRWevZcw,
	zhangqing-TNX95d0MmH7DzftRWevZcw, yxj-TNX95d0MmH7DzftRWevZcw,
	wxt-TNX95d0MmH7DzftRWevZcw, xw-TNX95d0MmH7DzftRWevZcw, mark yao

Signed-off-by: mark yao <yzq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
 .../devicetree/bindings/video/rockchip-panel.txt   |   30 ++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/Documentation/devicetree/bindings/video/rockchip-panel.txt b/Documentation/devicetree/bindings/video/rockchip-panel.txt
index 9fc200a..f599806 100644
--- a/Documentation/devicetree/bindings/video/rockchip-panel.txt
+++ b/Documentation/devicetree/bindings/video/rockchip-panel.txt
@@ -50,3 +50,33 @@ Example:
 		};
 
 	};
+
+Rockchip RK3288 LVDS interface
+================================
+Required properties:
+-compatible: "rockchip,rk3288-lvds";
+
+- reg: physical base address of the controller and length
+- clocks: from common clock binding: handle to dp clock.
+	of memory mapped region.
+- clock-names: from common clock binding: Shall be "pclk_lvds".
+	pclk_lvds: for power domain, if it disable soc will power down
+- rockchip,grf: this soc should set GRF regs, so need get grf here.
+- rockchip,data-mapping: should be "vesa" or "jeida"
+	This describes how the color bits are laid out in the
+	serialized LVDS signal.
+- rockchip,data-width: should be <18> or <24>
+- rockchip,panel: required a panel node
+
+Example:
+	lvds: lvds@ff96c000 {
+		compatible = "rockchip,rk3288-lvds";
+		reg = <0xff96c000 0x4000>;
+		clocks = <&cru PCLK_LVDS_PHY>;
+		clock-names = "pclk_lvds";
+
+		rockchip,grf = <&grf>;
+		rockchip,data-mapping = "jeida";
+		rockchip,data-width = <24>;
+		rockchip,panel = <&panel>;
+	};
-- 
1.7.9.5

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

* [PATCH 7/9] drm: add Rockchip Soc rk3288 lvds connector
@ 2014-08-04  4:54   ` mark yao
  0 siblings, 0 replies; 38+ messages in thread
From: mark yao @ 2014-08-04  4:54 UTC (permalink / raw)
  To: heiko, Rob Clark, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Randy Dunlap, David Airlie,
	Grant Likely, Greg Kroah-Hartman, John Stultz, Rom Lemarchand
  Cc: devicetree, linux-doc, linux-kernel, dri-devel, linux-api, olof,
	djkurtz, xjq, kfx, cym, cf, zyw, zwl, xxm, huangtao, kever.yang,
	zhangqing, yxj, wxt, xw, mark yao

Signed-off-by: mark yao <yzq@rock-chips.com>
---
 drivers/gpu/drm/rockchip/Kconfig                 |    1 +
 drivers/gpu/drm/rockchip/Makefile                |    2 +-
 drivers/gpu/drm/rockchip/connector/Kconfig       |    8 +
 drivers/gpu/drm/rockchip/connector/Makefile      |    4 +
 drivers/gpu/drm/rockchip/connector/rk3288_lvds.c |  332 ++++++++++++++++++++++
 drivers/gpu/drm/rockchip/connector/rk3288_lvds.h |   50 ++++
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c      |   13 +
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h      |    3 +
 8 files changed, 412 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/rockchip/connector/Kconfig
 create mode 100644 drivers/gpu/drm/rockchip/connector/Makefile
 create mode 100644 drivers/gpu/drm/rockchip/connector/rk3288_lvds.c
 create mode 100644 drivers/gpu/drm/rockchip/connector/rk3288_lvds.h

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index ccce827..407cbb6 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -40,3 +40,4 @@ config DRM_ROCKCHIP_CONNECTOR
 	  such as lcd plane, lvds, edp , mipi, etc.
 
 source "drivers/gpu/drm/rockchip/lcdc/Kconfig"
+source "drivers/gpu/drm/rockchip/connector/Kconfig"
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index 6d49edc..7d5877a 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -8,6 +8,6 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_gem.o \
 		rockchip_drm_fb.o rockchip_drm_fbdev.o \
 		rockchip_panel.o
 
-obj-$(CONFIG_DRM_ROCKCHIP_CONNECTOR) += rockchip_drm_connector.o
+obj-$(CONFIG_DRM_ROCKCHIP_CONNECTOR) += rockchip_drm_connector.o connector/
 obj-$(CONFIG_DRM_ROCKCHIP_LCDC) += rockchip_drm_lcdc.o lcdc/
 obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o
diff --git a/drivers/gpu/drm/rockchip/connector/Kconfig b/drivers/gpu/drm/rockchip/connector/Kconfig
new file mode 100644
index 0000000..248942f
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/connector/Kconfig
@@ -0,0 +1,8 @@
+config RK3288_LVDS
+	bool "RK3288 lvds connector support"
+	depends on DRM_ROCKCHIP_CONNECTOR
+	help
+	  Choose this option if you have a rk3288 lvds connector.
+	  rk3288 lvds transmitter support ttl rgb, lvds and dual lvds
+	  mode, dual lvds mode is support for the plane which need dual
+	  lvds channels.
diff --git a/drivers/gpu/drm/rockchip/connector/Makefile b/drivers/gpu/drm/rockchip/connector/Makefile
new file mode 100644
index 0000000..dcfbdef
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/connector/Makefile
@@ -0,0 +1,4 @@
+#
+# Makefile for display connector like lvds edp mipi
+#
+obj-$(CONFIG_RK3288_LVDS)	+= rk3288_lvds.o
diff --git a/drivers/gpu/drm/rockchip/connector/rk3288_lvds.c b/drivers/gpu/drm/rockchip/connector/rk3288_lvds.c
new file mode 100644
index 0000000..3ca4c6f
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/connector/rk3288_lvds.c
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:
+ *      hjc <hjc@rock-chips.com>
+ *      mark yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#include <video/display_timing.h>
+
+#include "../rockchip_drm_connector.h"
+#include "../rockchip_drm_lcdc.h"
+#include "rk3288_lvds.h"
+
+/*
+ * @grf_offset: offset inside the grf regmap for setting the rockchip lvds
+ */
+struct rk3288_lvds_soc_data {
+	int grf_gpio1d_iomux;
+	int grf_soc_con6;
+	int grf_soc_con7;
+};
+
+struct rk3288_lvds {
+	void *base;
+	int format;
+	struct drm_display_mode mode;
+	struct device *dev;
+	void __iomem *regs;
+	struct regmap *grf;
+	struct rk3288_lvds_soc_data *soc_data;
+	struct clk *pclk;
+	bool standby;
+};
+
+static inline void lvds_writel(struct rk3288_lvds *lvds, u32 offset, u32 val)
+{
+	writel_relaxed(val, lvds->regs + offset);
+	writel_relaxed(val, lvds->regs + offset + 0x100);
+}
+
+static inline int lvds_name_to_format(const char *s)
+{
+	if (!s)
+		return 0;
+
+	if (strncmp(s, "jeida", 6) == 0)
+		return LVDS_FORMAT_JEIDA;
+	else if (strncmp(s, "vesa", 6) == 0)
+		return LVDS_FORMAT_VESA;
+
+	return 0;
+}
+
+static void rk3288_lvds_disable(struct rockchip_connector *conn)
+{
+	struct rk3288_lvds *lvds = conn->priv;
+	int ret = 0;
+
+	if (lvds->standby)
+		return;
+
+	ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con7, 0xffff8000);
+	if (ret != 0)
+		dev_err(lvds->dev, "Could not write to GRF: %d\n", ret);
+	/* disable tx */
+	writel_relaxed(0x00, lvds->regs + LVDS_CFG_REG_21);
+	/* disable pll */
+	writel_relaxed(0xff, lvds->regs + LVDS_CFG_REG_C);
+
+	clk_disable_unprepare(lvds->pclk);
+	lvds->standby = true;
+}
+
+static void rk3288_lvds_en(struct rockchip_connector *conn)
+{
+	struct rk3288_lvds *lvds = conn->priv;
+	struct drm_display_mode *mode = &lvds->mode;
+	u32 val = 0;
+	u32 h_bp = mode->htotal - mode->hsync_start;
+	u8 pin_hsync = (conn->flags & DISPLAY_FLAGS_HSYNC_HIGH) ? 1 : 0;
+	u8 pin_den = (conn->flags & DISPLAY_FLAGS_DE_HIGH) ? 1 : 0;
+	u8 pin_dclk = (conn->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE) ? 1 : 0;
+	u8 lvds_format = lvds->format;
+	u8 type = conn->type;
+	int lcdc_id = 1;
+	int ret = 0;
+
+	if (!lvds->standby)
+		return;
+
+	/* enable clk */
+	ret = clk_prepare_enable(lvds->pclk);
+	if (ret < 0) {
+		dev_err(lvds->dev, "failed to enable lvds pclk %d\n", ret);
+		return;
+	}
+	/* lcdc1 = vop little, lcdc0 = vop big */
+	if (lcdc_id == 1)
+		val = LVDS_SEL_VOP_LIT | (LVDS_SEL_VOP_LIT << 16);
+	else
+		val = LVDS_SEL_VOP_LIT << 16;
+	ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con6, val);
+	if (ret != 0) {
+		dev_err(lvds->dev, "Could not write to GRF: %d\n", ret);
+		return;
+	}
+
+	val = lvds_format;
+	if (type == ROCKCHIP_DISPLAY_TYPE_DUAL_LVDS)
+		val |= LVDS_DUAL | LVDS_CH0_EN | LVDS_CH1_EN;
+	else if (type == ROCKCHIP_DISPLAY_TYPE_LVDS)
+		val |= LVDS_CH0_EN;
+	else if (type == ROCKCHIP_DISPLAY_TYPE_RGB)
+		val |= LVDS_TTL_EN | LVDS_CH0_EN | LVDS_CH1_EN;
+
+	if (h_bp & 0x01)
+		val |= LVDS_START_PHASE_RST_1;
+
+	val |= (pin_dclk << 8) | (pin_hsync << 9) |
+		(pin_den << 10);
+	val |= (0xffff << 16);
+	ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con7, val);
+	if (ret != 0) {
+		dev_err(lvds->dev, "Could not write to GRF: %d\n", ret);
+		return;
+	}
+
+	if (type == ROCKCHIP_DISPLAY_TYPE_RGB) {
+		val = 0x007f007f;
+		ret = regmap_write(lvds->grf, lvds->soc_data->grf_gpio1d_iomux,
+				   val);
+		if (ret != 0) {
+			dev_err(lvds->dev, "Could not write to GRF: %d\n", ret);
+			return;
+		}
+
+		lvds_writel(lvds, LVDS_CH0_REG_0, 0x7f);
+		lvds_writel(lvds, LVDS_CH0_REG_1, 0x40);
+		lvds_writel(lvds, LVDS_CH0_REG_2, 0x00);
+
+		lvds_writel(lvds, LVDS_CH0_REG_4, 0x3f);
+		lvds_writel(lvds, LVDS_CH0_REG_5, 0x3f);
+		lvds_writel(lvds, LVDS_CH0_REG_3, 0x46);
+		lvds_writel(lvds, LVDS_CH0_REG_D, 0x0a);
+		lvds_writel(lvds, LVDS_CH0_REG_20, 0x44);
+		writel_relaxed(0x00, lvds->regs + LVDS_CFG_REG_C);
+		writel_relaxed(0x92, lvds->regs + LVDS_CFG_REG_21);
+
+		lvds_writel(lvds, 0x100, 0x7f);
+		lvds_writel(lvds, 0x104, 0x40);
+		lvds_writel(lvds, 0x108, 0x00);
+		lvds_writel(lvds, 0x10c, 0x46);
+		lvds_writel(lvds, 0x110, 0x3f);
+		lvds_writel(lvds, 0x114, 0x3f);
+		lvds_writel(lvds, 0x134, 0x0a);
+	} else {
+		lvds_writel(lvds, LVDS_CH0_REG_0, 0xbf);
+		lvds_writel(lvds, LVDS_CH0_REG_1, 0x3f);
+		lvds_writel(lvds, LVDS_CH0_REG_2, 0xfe);
+		lvds_writel(lvds, LVDS_CH0_REG_3, 0x46);
+		lvds_writel(lvds, LVDS_CH0_REG_4, 0x00);
+		lvds_writel(lvds, LVDS_CH0_REG_D, 0x0a);
+		lvds_writel(lvds, LVDS_CH0_REG_20, 0x44);
+		writel_relaxed(0x00, lvds->regs + LVDS_CFG_REG_C);
+		writel_relaxed(0x92, lvds->regs + LVDS_CFG_REG_21);
+	}
+
+	lvds->standby = false;
+}
+
+static int rk3288_lvds_setmode(struct rockchip_connector *conn,
+			       struct drm_display_mode *mode)
+{
+	struct rk3288_lvds *lvds = conn->priv;
+
+	memcpy(&lvds->mode, mode, sizeof(*mode));
+
+	return 0;
+}
+
+static struct rockchip_connector lvds_conn = {
+	.enable = rk3288_lvds_en,
+	.disable = rk3288_lvds_disable,
+	.setmode = rk3288_lvds_setmode,
+};
+
+static struct rk3288_lvds_soc_data soc_data[2] = {
+	{.grf_gpio1d_iomux = 0x000c,
+	 .grf_soc_con6 = 0x025c,
+	 .grf_soc_con7 = 0x0260},
+	{.grf_gpio1d_iomux = -1,
+	 .grf_soc_con6 = -1,
+	/* no lvds switching needed */
+	 .grf_soc_con7 = -1},
+};
+
+static const struct of_device_id rk3288_lvds_dt_ids[] = {
+	{.compatible = "rockchip,rk3288-lvds",
+	 .data = (void *)&soc_data[0] },
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, rockchip_lvds_dt_ids);
+
+static int rk3288_lvds_probe(struct platform_device *pdev)
+{
+	struct rk3288_lvds *lvds;
+	struct resource *res;
+	struct device_node *np = pdev->dev.of_node;
+	const char *name;
+	const struct of_device_id *match;
+	u32 i;
+
+	if (!np) {
+		dev_err(&pdev->dev, "Missing device tree node.\n");
+		return -EINVAL;
+	}
+
+	lvds = devm_kzalloc(&pdev->dev, sizeof(struct rk3288_lvds), GFP_KERNEL);
+	if (!lvds) {
+		dev_err(&pdev->dev, "no memory for state\n");
+		return -ENOMEM;
+	}
+
+	match = of_match_node(rk3288_lvds_dt_ids, np);
+	lvds->soc_data = (struct rk3288_lvds_soc_data *)match->data;
+	/*
+	 * The control bit is located in the GRF register space.
+	 */
+	if (lvds->soc_data->grf_gpio1d_iomux >= 0) {
+		lvds->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+		if (IS_ERR(lvds->grf)) {
+			dev_err(&pdev->dev,
+				"rockchip-lvds needs rockchip,grf property\n");
+			return PTR_ERR(lvds->grf);
+		}
+	}
+
+	if (of_property_read_string(np, "rockchip,data-mapping", &name))
+		/* default set it as format jeida */
+		lvds->format = LVDS_FORMAT_JEIDA;
+	else
+		lvds->format = lvds_name_to_format(name);
+
+	if (of_property_read_u32(np, "rockchip,data-width", &i)) {
+		lvds->format |= LVDS_24BIT;
+	} else {
+		if (i == 24) {
+			lvds->format |= LVDS_24BIT;
+		} else if (i == 18) {
+			lvds->format |= LVDS_18BIT;
+		} else {
+			dev_err(&pdev->dev,
+				"rockchip-lvds unsupport data-width[%d]\n", i);
+			return -EINVAL;
+		}
+	}
+
+	lvds->dev = &pdev->dev;
+
+	lvds_conn.priv = lvds;
+	lvds_conn.dev = &pdev->dev;
+	lvds_conn.type = ROCKCHIP_DISPLAY_TYPE_LVDS;
+	lvds->base = rockchip_connector_register(&lvds_conn);
+	if (!lvds->base)
+		return -EINVAL;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	lvds->regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(lvds->regs)) {
+		dev_err(&pdev->dev, "ioremap reg failed\n");
+		return PTR_ERR(lvds->regs);
+	}
+
+	lvds->pclk = devm_clk_get(&pdev->dev, "pclk_lvds");
+	if (IS_ERR(lvds->pclk)) {
+		dev_err(&pdev->dev, "get clk failed\n");
+		return PTR_ERR(lvds->pclk);
+	}
+
+	lvds->standby = true;
+
+	platform_set_drvdata(pdev, lvds);
+	dev_set_name(lvds->dev, "rk3288-lvds");
+
+	dev_info(&pdev->dev, "rk3288 lvds driver probe success\n");
+
+	return 0;
+}
+
+static int rk3288_lvds_remove(struct platform_device *pdev)
+{
+	struct rk3288_lvds *lvds = lvds_conn.priv;
+
+	rk3288_lvds_disable(&lvds_conn);
+	rockchip_connector_unregister(lvds->base);
+
+	return 0;
+}
+
+struct platform_driver rk3288_lvds_driver = {
+	.probe = rk3288_lvds_probe,
+	.remove = rk3288_lvds_remove,
+	.driver = {
+		   .name = "rk3288-lvds",
+		   .owner = THIS_MODULE,
+		   .of_match_table = of_match_ptr(rk3288_lvds_dt_ids),
+	},
+};
diff --git a/drivers/gpu/drm/rockchip/connector/rk3288_lvds.h b/drivers/gpu/drm/rockchip/connector/rk3288_lvds.h
new file mode 100644
index 0000000..61c71cc
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/connector/rk3288_lvds.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:
+ *      hjc <hjc@rock-chips.com>
+ *      mark yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _RK3288_LVDS_
+#define _RK3288_LVDS_
+
+#define LVDS_CH0_REG_0			0x00
+#define LVDS_CH0_REG_1			0x04
+#define LVDS_CH0_REG_2			0x08
+#define LVDS_CH0_REG_3			0x0c
+#define LVDS_CH0_REG_4			0x10
+#define LVDS_CH0_REG_5			0x14
+#define LVDS_CH0_REG_9			0x24
+#define LVDS_CFG_REG_C			0x30
+#define LVDS_CH0_REG_D			0x34
+#define LVDS_CH0_REG_F			0x3c
+#define LVDS_CH0_REG_20			0x80
+#define LVDS_CFG_REG_21			0x84
+
+#define LVDS_SEL_VOP_LIT		(1 << 3)
+
+#define LVDS_FMT_MASK			(0x07 << 16)
+#define LVDS_MSB			(0x01 << 3)
+#define LVDS_DUAL			(0x01 << 4)
+#define LVDS_FMT_1			(0x01 << 5)
+#define LVDS_TTL_EN			(0x01 << 6)
+#define LVDS_START_PHASE_RST_1		(0x01 << 7)
+#define LVDS_DCLK_INV			(0x01 << 8)
+#define LVDS_CH0_EN			(0x01 << 11)
+#define LVDS_CH1_EN			(0x01 << 12)
+#define LVDS_PWRDN			(0x01 << 15)
+
+#define LVDS_24BIT		(0 << 1)
+#define LVDS_18BIT		(1 << 1)
+#define LVDS_FORMAT_VESA	(0 << 0)
+#define LVDS_FORMAT_JEIDA	(1 << 0)
+#endif /* _RK3288_LVDS_ */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index 4871867..59187aa 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -558,6 +558,12 @@ static int rockchip_drm_init(void)
 		goto out_lcdc;
 #endif
 
+#ifdef CONFIG_RK3288_LVDS
+	ret = platform_driver_register(&rk3288_lvds_driver);
+	if (ret)
+		goto out_lvds;
+#endif
+
 	rockchip_drm_pdev = platform_device_register_simple("rockchip-drm", -1,
 							    NULL, 0);
 	if (IS_ERR(rockchip_drm_pdev)) {
@@ -574,6 +580,10 @@ static int rockchip_drm_init(void)
 out_drm_driver:
 	platform_device_unregister(rockchip_drm_pdev);
 out_drm_pdev:
+#ifdef CONFIG_RK3288_LVDS
+	platform_driver_unregister(&rk3288_lvds_driver);
+out_lvds:
+#endif
 #ifdef CONFIG_DRM_ROCKCHIP_LCDC
 	platform_driver_unregister(&rockchip_lcdc_platform_driver);
 out_lcdc:
@@ -586,6 +596,9 @@ static void rockchip_drm_exit(void)
 {
 	platform_device_unregister(rockchip_drm_pdev);
 	platform_driver_unregister(&rockchip_drm_platform_driver);
+#ifdef CONFIG_RK3288_LVDS
+	platform_driver_unregister(&rk3288_lvds_driver);
+#endif
 #ifdef CONFIG_DRM_ROCKCHIP_LCDC
 	platform_driver_unregister(&rockchip_lcdc_platform_driver);
 #endif
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index c0c1d89..d28f4dc 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -125,4 +125,7 @@ extern struct platform_driver rockchip_panel_platform_driver;
 #ifdef CONFIG_DRM_ROCKCHIP_LCDC
 extern struct platform_driver rockchip_lcdc_platform_driver;
 #endif
+#ifdef CONFIG_RK3288_LVDS
+extern struct platform_driver rk3288_lvds_driver;
+#endif
 #endif /* _ROCKCHIP_DRM_DRV_H_ */
-- 
1.7.9.5



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

* [PATCH 7/9] drm: add Rockchip Soc rk3288 lvds connector
@ 2014-08-04  4:54   ` mark yao
  0 siblings, 0 replies; 38+ messages in thread
From: mark yao @ 2014-08-04  4:54 UTC (permalink / raw)
  To: heiko-4mtYJXux2i+zQB+pC5nmwQ, Rob Clark, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, Randy Dunlap,
	David Airlie, Grant Likely, Greg Kroah-Hartman, John Stultz,
	Rom Lemarchand
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-api-u79uwXL29TY76Z2rM5mHXA, olof-nZhT3qVonbNeoWH0uzbU5w,
	djkurtz-F7+t8E8rja9g9hUCZPvPmw, xjq-TNX95d0MmH7DzftRWevZcw,
	kfx-TNX95d0MmH7DzftRWevZcw, cym-TNX95d0MmH7DzftRWevZcw,
	cf-TNX95d0MmH7DzftRWevZcw, zyw-TNX95d0MmH7DzftRWevZcw,
	zwl-TNX95d0MmH7DzftRWevZcw, xxm-TNX95d0MmH7DzftRWevZcw,
	huangtao-TNX95d0MmH7DzftRWevZcw,
	kever.yang-TNX95d0MmH7DzftRWevZcw,
	zhangqing-TNX95d0MmH7DzftRWevZcw, yxj-TNX95d0MmH7DzftRWevZcw,
	wxt-TNX95d0MmH7DzftRWevZcw, xw-TNX95d0MmH7DzftRWevZcw, mark yao

Signed-off-by: mark yao <yzq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
 drivers/gpu/drm/rockchip/Kconfig                 |    1 +
 drivers/gpu/drm/rockchip/Makefile                |    2 +-
 drivers/gpu/drm/rockchip/connector/Kconfig       |    8 +
 drivers/gpu/drm/rockchip/connector/Makefile      |    4 +
 drivers/gpu/drm/rockchip/connector/rk3288_lvds.c |  332 ++++++++++++++++++++++
 drivers/gpu/drm/rockchip/connector/rk3288_lvds.h |   50 ++++
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c      |   13 +
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h      |    3 +
 8 files changed, 412 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/rockchip/connector/Kconfig
 create mode 100644 drivers/gpu/drm/rockchip/connector/Makefile
 create mode 100644 drivers/gpu/drm/rockchip/connector/rk3288_lvds.c
 create mode 100644 drivers/gpu/drm/rockchip/connector/rk3288_lvds.h

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index ccce827..407cbb6 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -40,3 +40,4 @@ config DRM_ROCKCHIP_CONNECTOR
 	  such as lcd plane, lvds, edp , mipi, etc.
 
 source "drivers/gpu/drm/rockchip/lcdc/Kconfig"
+source "drivers/gpu/drm/rockchip/connector/Kconfig"
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index 6d49edc..7d5877a 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -8,6 +8,6 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_gem.o \
 		rockchip_drm_fb.o rockchip_drm_fbdev.o \
 		rockchip_panel.o
 
-obj-$(CONFIG_DRM_ROCKCHIP_CONNECTOR) += rockchip_drm_connector.o
+obj-$(CONFIG_DRM_ROCKCHIP_CONNECTOR) += rockchip_drm_connector.o connector/
 obj-$(CONFIG_DRM_ROCKCHIP_LCDC) += rockchip_drm_lcdc.o lcdc/
 obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o
diff --git a/drivers/gpu/drm/rockchip/connector/Kconfig b/drivers/gpu/drm/rockchip/connector/Kconfig
new file mode 100644
index 0000000..248942f
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/connector/Kconfig
@@ -0,0 +1,8 @@
+config RK3288_LVDS
+	bool "RK3288 lvds connector support"
+	depends on DRM_ROCKCHIP_CONNECTOR
+	help
+	  Choose this option if you have a rk3288 lvds connector.
+	  rk3288 lvds transmitter support ttl rgb, lvds and dual lvds
+	  mode, dual lvds mode is support for the plane which need dual
+	  lvds channels.
diff --git a/drivers/gpu/drm/rockchip/connector/Makefile b/drivers/gpu/drm/rockchip/connector/Makefile
new file mode 100644
index 0000000..dcfbdef
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/connector/Makefile
@@ -0,0 +1,4 @@
+#
+# Makefile for display connector like lvds edp mipi
+#
+obj-$(CONFIG_RK3288_LVDS)	+= rk3288_lvds.o
diff --git a/drivers/gpu/drm/rockchip/connector/rk3288_lvds.c b/drivers/gpu/drm/rockchip/connector/rk3288_lvds.c
new file mode 100644
index 0000000..3ca4c6f
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/connector/rk3288_lvds.c
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:
+ *      hjc <hjc-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *      mark yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#include <video/display_timing.h>
+
+#include "../rockchip_drm_connector.h"
+#include "../rockchip_drm_lcdc.h"
+#include "rk3288_lvds.h"
+
+/*
+ * @grf_offset: offset inside the grf regmap for setting the rockchip lvds
+ */
+struct rk3288_lvds_soc_data {
+	int grf_gpio1d_iomux;
+	int grf_soc_con6;
+	int grf_soc_con7;
+};
+
+struct rk3288_lvds {
+	void *base;
+	int format;
+	struct drm_display_mode mode;
+	struct device *dev;
+	void __iomem *regs;
+	struct regmap *grf;
+	struct rk3288_lvds_soc_data *soc_data;
+	struct clk *pclk;
+	bool standby;
+};
+
+static inline void lvds_writel(struct rk3288_lvds *lvds, u32 offset, u32 val)
+{
+	writel_relaxed(val, lvds->regs + offset);
+	writel_relaxed(val, lvds->regs + offset + 0x100);
+}
+
+static inline int lvds_name_to_format(const char *s)
+{
+	if (!s)
+		return 0;
+
+	if (strncmp(s, "jeida", 6) == 0)
+		return LVDS_FORMAT_JEIDA;
+	else if (strncmp(s, "vesa", 6) == 0)
+		return LVDS_FORMAT_VESA;
+
+	return 0;
+}
+
+static void rk3288_lvds_disable(struct rockchip_connector *conn)
+{
+	struct rk3288_lvds *lvds = conn->priv;
+	int ret = 0;
+
+	if (lvds->standby)
+		return;
+
+	ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con7, 0xffff8000);
+	if (ret != 0)
+		dev_err(lvds->dev, "Could not write to GRF: %d\n", ret);
+	/* disable tx */
+	writel_relaxed(0x00, lvds->regs + LVDS_CFG_REG_21);
+	/* disable pll */
+	writel_relaxed(0xff, lvds->regs + LVDS_CFG_REG_C);
+
+	clk_disable_unprepare(lvds->pclk);
+	lvds->standby = true;
+}
+
+static void rk3288_lvds_en(struct rockchip_connector *conn)
+{
+	struct rk3288_lvds *lvds = conn->priv;
+	struct drm_display_mode *mode = &lvds->mode;
+	u32 val = 0;
+	u32 h_bp = mode->htotal - mode->hsync_start;
+	u8 pin_hsync = (conn->flags & DISPLAY_FLAGS_HSYNC_HIGH) ? 1 : 0;
+	u8 pin_den = (conn->flags & DISPLAY_FLAGS_DE_HIGH) ? 1 : 0;
+	u8 pin_dclk = (conn->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE) ? 1 : 0;
+	u8 lvds_format = lvds->format;
+	u8 type = conn->type;
+	int lcdc_id = 1;
+	int ret = 0;
+
+	if (!lvds->standby)
+		return;
+
+	/* enable clk */
+	ret = clk_prepare_enable(lvds->pclk);
+	if (ret < 0) {
+		dev_err(lvds->dev, "failed to enable lvds pclk %d\n", ret);
+		return;
+	}
+	/* lcdc1 = vop little, lcdc0 = vop big */
+	if (lcdc_id == 1)
+		val = LVDS_SEL_VOP_LIT | (LVDS_SEL_VOP_LIT << 16);
+	else
+		val = LVDS_SEL_VOP_LIT << 16;
+	ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con6, val);
+	if (ret != 0) {
+		dev_err(lvds->dev, "Could not write to GRF: %d\n", ret);
+		return;
+	}
+
+	val = lvds_format;
+	if (type == ROCKCHIP_DISPLAY_TYPE_DUAL_LVDS)
+		val |= LVDS_DUAL | LVDS_CH0_EN | LVDS_CH1_EN;
+	else if (type == ROCKCHIP_DISPLAY_TYPE_LVDS)
+		val |= LVDS_CH0_EN;
+	else if (type == ROCKCHIP_DISPLAY_TYPE_RGB)
+		val |= LVDS_TTL_EN | LVDS_CH0_EN | LVDS_CH1_EN;
+
+	if (h_bp & 0x01)
+		val |= LVDS_START_PHASE_RST_1;
+
+	val |= (pin_dclk << 8) | (pin_hsync << 9) |
+		(pin_den << 10);
+	val |= (0xffff << 16);
+	ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con7, val);
+	if (ret != 0) {
+		dev_err(lvds->dev, "Could not write to GRF: %d\n", ret);
+		return;
+	}
+
+	if (type == ROCKCHIP_DISPLAY_TYPE_RGB) {
+		val = 0x007f007f;
+		ret = regmap_write(lvds->grf, lvds->soc_data->grf_gpio1d_iomux,
+				   val);
+		if (ret != 0) {
+			dev_err(lvds->dev, "Could not write to GRF: %d\n", ret);
+			return;
+		}
+
+		lvds_writel(lvds, LVDS_CH0_REG_0, 0x7f);
+		lvds_writel(lvds, LVDS_CH0_REG_1, 0x40);
+		lvds_writel(lvds, LVDS_CH0_REG_2, 0x00);
+
+		lvds_writel(lvds, LVDS_CH0_REG_4, 0x3f);
+		lvds_writel(lvds, LVDS_CH0_REG_5, 0x3f);
+		lvds_writel(lvds, LVDS_CH0_REG_3, 0x46);
+		lvds_writel(lvds, LVDS_CH0_REG_D, 0x0a);
+		lvds_writel(lvds, LVDS_CH0_REG_20, 0x44);
+		writel_relaxed(0x00, lvds->regs + LVDS_CFG_REG_C);
+		writel_relaxed(0x92, lvds->regs + LVDS_CFG_REG_21);
+
+		lvds_writel(lvds, 0x100, 0x7f);
+		lvds_writel(lvds, 0x104, 0x40);
+		lvds_writel(lvds, 0x108, 0x00);
+		lvds_writel(lvds, 0x10c, 0x46);
+		lvds_writel(lvds, 0x110, 0x3f);
+		lvds_writel(lvds, 0x114, 0x3f);
+		lvds_writel(lvds, 0x134, 0x0a);
+	} else {
+		lvds_writel(lvds, LVDS_CH0_REG_0, 0xbf);
+		lvds_writel(lvds, LVDS_CH0_REG_1, 0x3f);
+		lvds_writel(lvds, LVDS_CH0_REG_2, 0xfe);
+		lvds_writel(lvds, LVDS_CH0_REG_3, 0x46);
+		lvds_writel(lvds, LVDS_CH0_REG_4, 0x00);
+		lvds_writel(lvds, LVDS_CH0_REG_D, 0x0a);
+		lvds_writel(lvds, LVDS_CH0_REG_20, 0x44);
+		writel_relaxed(0x00, lvds->regs + LVDS_CFG_REG_C);
+		writel_relaxed(0x92, lvds->regs + LVDS_CFG_REG_21);
+	}
+
+	lvds->standby = false;
+}
+
+static int rk3288_lvds_setmode(struct rockchip_connector *conn,
+			       struct drm_display_mode *mode)
+{
+	struct rk3288_lvds *lvds = conn->priv;
+
+	memcpy(&lvds->mode, mode, sizeof(*mode));
+
+	return 0;
+}
+
+static struct rockchip_connector lvds_conn = {
+	.enable = rk3288_lvds_en,
+	.disable = rk3288_lvds_disable,
+	.setmode = rk3288_lvds_setmode,
+};
+
+static struct rk3288_lvds_soc_data soc_data[2] = {
+	{.grf_gpio1d_iomux = 0x000c,
+	 .grf_soc_con6 = 0x025c,
+	 .grf_soc_con7 = 0x0260},
+	{.grf_gpio1d_iomux = -1,
+	 .grf_soc_con6 = -1,
+	/* no lvds switching needed */
+	 .grf_soc_con7 = -1},
+};
+
+static const struct of_device_id rk3288_lvds_dt_ids[] = {
+	{.compatible = "rockchip,rk3288-lvds",
+	 .data = (void *)&soc_data[0] },
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, rockchip_lvds_dt_ids);
+
+static int rk3288_lvds_probe(struct platform_device *pdev)
+{
+	struct rk3288_lvds *lvds;
+	struct resource *res;
+	struct device_node *np = pdev->dev.of_node;
+	const char *name;
+	const struct of_device_id *match;
+	u32 i;
+
+	if (!np) {
+		dev_err(&pdev->dev, "Missing device tree node.\n");
+		return -EINVAL;
+	}
+
+	lvds = devm_kzalloc(&pdev->dev, sizeof(struct rk3288_lvds), GFP_KERNEL);
+	if (!lvds) {
+		dev_err(&pdev->dev, "no memory for state\n");
+		return -ENOMEM;
+	}
+
+	match = of_match_node(rk3288_lvds_dt_ids, np);
+	lvds->soc_data = (struct rk3288_lvds_soc_data *)match->data;
+	/*
+	 * The control bit is located in the GRF register space.
+	 */
+	if (lvds->soc_data->grf_gpio1d_iomux >= 0) {
+		lvds->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+		if (IS_ERR(lvds->grf)) {
+			dev_err(&pdev->dev,
+				"rockchip-lvds needs rockchip,grf property\n");
+			return PTR_ERR(lvds->grf);
+		}
+	}
+
+	if (of_property_read_string(np, "rockchip,data-mapping", &name))
+		/* default set it as format jeida */
+		lvds->format = LVDS_FORMAT_JEIDA;
+	else
+		lvds->format = lvds_name_to_format(name);
+
+	if (of_property_read_u32(np, "rockchip,data-width", &i)) {
+		lvds->format |= LVDS_24BIT;
+	} else {
+		if (i == 24) {
+			lvds->format |= LVDS_24BIT;
+		} else if (i == 18) {
+			lvds->format |= LVDS_18BIT;
+		} else {
+			dev_err(&pdev->dev,
+				"rockchip-lvds unsupport data-width[%d]\n", i);
+			return -EINVAL;
+		}
+	}
+
+	lvds->dev = &pdev->dev;
+
+	lvds_conn.priv = lvds;
+	lvds_conn.dev = &pdev->dev;
+	lvds_conn.type = ROCKCHIP_DISPLAY_TYPE_LVDS;
+	lvds->base = rockchip_connector_register(&lvds_conn);
+	if (!lvds->base)
+		return -EINVAL;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	lvds->regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(lvds->regs)) {
+		dev_err(&pdev->dev, "ioremap reg failed\n");
+		return PTR_ERR(lvds->regs);
+	}
+
+	lvds->pclk = devm_clk_get(&pdev->dev, "pclk_lvds");
+	if (IS_ERR(lvds->pclk)) {
+		dev_err(&pdev->dev, "get clk failed\n");
+		return PTR_ERR(lvds->pclk);
+	}
+
+	lvds->standby = true;
+
+	platform_set_drvdata(pdev, lvds);
+	dev_set_name(lvds->dev, "rk3288-lvds");
+
+	dev_info(&pdev->dev, "rk3288 lvds driver probe success\n");
+
+	return 0;
+}
+
+static int rk3288_lvds_remove(struct platform_device *pdev)
+{
+	struct rk3288_lvds *lvds = lvds_conn.priv;
+
+	rk3288_lvds_disable(&lvds_conn);
+	rockchip_connector_unregister(lvds->base);
+
+	return 0;
+}
+
+struct platform_driver rk3288_lvds_driver = {
+	.probe = rk3288_lvds_probe,
+	.remove = rk3288_lvds_remove,
+	.driver = {
+		   .name = "rk3288-lvds",
+		   .owner = THIS_MODULE,
+		   .of_match_table = of_match_ptr(rk3288_lvds_dt_ids),
+	},
+};
diff --git a/drivers/gpu/drm/rockchip/connector/rk3288_lvds.h b/drivers/gpu/drm/rockchip/connector/rk3288_lvds.h
new file mode 100644
index 0000000..61c71cc
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/connector/rk3288_lvds.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:
+ *      hjc <hjc-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *      mark yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _RK3288_LVDS_
+#define _RK3288_LVDS_
+
+#define LVDS_CH0_REG_0			0x00
+#define LVDS_CH0_REG_1			0x04
+#define LVDS_CH0_REG_2			0x08
+#define LVDS_CH0_REG_3			0x0c
+#define LVDS_CH0_REG_4			0x10
+#define LVDS_CH0_REG_5			0x14
+#define LVDS_CH0_REG_9			0x24
+#define LVDS_CFG_REG_C			0x30
+#define LVDS_CH0_REG_D			0x34
+#define LVDS_CH0_REG_F			0x3c
+#define LVDS_CH0_REG_20			0x80
+#define LVDS_CFG_REG_21			0x84
+
+#define LVDS_SEL_VOP_LIT		(1 << 3)
+
+#define LVDS_FMT_MASK			(0x07 << 16)
+#define LVDS_MSB			(0x01 << 3)
+#define LVDS_DUAL			(0x01 << 4)
+#define LVDS_FMT_1			(0x01 << 5)
+#define LVDS_TTL_EN			(0x01 << 6)
+#define LVDS_START_PHASE_RST_1		(0x01 << 7)
+#define LVDS_DCLK_INV			(0x01 << 8)
+#define LVDS_CH0_EN			(0x01 << 11)
+#define LVDS_CH1_EN			(0x01 << 12)
+#define LVDS_PWRDN			(0x01 << 15)
+
+#define LVDS_24BIT		(0 << 1)
+#define LVDS_18BIT		(1 << 1)
+#define LVDS_FORMAT_VESA	(0 << 0)
+#define LVDS_FORMAT_JEIDA	(1 << 0)
+#endif /* _RK3288_LVDS_ */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index 4871867..59187aa 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -558,6 +558,12 @@ static int rockchip_drm_init(void)
 		goto out_lcdc;
 #endif
 
+#ifdef CONFIG_RK3288_LVDS
+	ret = platform_driver_register(&rk3288_lvds_driver);
+	if (ret)
+		goto out_lvds;
+#endif
+
 	rockchip_drm_pdev = platform_device_register_simple("rockchip-drm", -1,
 							    NULL, 0);
 	if (IS_ERR(rockchip_drm_pdev)) {
@@ -574,6 +580,10 @@ static int rockchip_drm_init(void)
 out_drm_driver:
 	platform_device_unregister(rockchip_drm_pdev);
 out_drm_pdev:
+#ifdef CONFIG_RK3288_LVDS
+	platform_driver_unregister(&rk3288_lvds_driver);
+out_lvds:
+#endif
 #ifdef CONFIG_DRM_ROCKCHIP_LCDC
 	platform_driver_unregister(&rockchip_lcdc_platform_driver);
 out_lcdc:
@@ -586,6 +596,9 @@ static void rockchip_drm_exit(void)
 {
 	platform_device_unregister(rockchip_drm_pdev);
 	platform_driver_unregister(&rockchip_drm_platform_driver);
+#ifdef CONFIG_RK3288_LVDS
+	platform_driver_unregister(&rk3288_lvds_driver);
+#endif
 #ifdef CONFIG_DRM_ROCKCHIP_LCDC
 	platform_driver_unregister(&rockchip_lcdc_platform_driver);
 #endif
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index c0c1d89..d28f4dc 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -125,4 +125,7 @@ extern struct platform_driver rockchip_panel_platform_driver;
 #ifdef CONFIG_DRM_ROCKCHIP_LCDC
 extern struct platform_driver rockchip_lcdc_platform_driver;
 #endif
+#ifdef CONFIG_RK3288_LVDS
+extern struct platform_driver rk3288_lvds_driver;
+#endif
 #endif /* _ROCKCHIP_DRM_DRV_H_ */
-- 
1.7.9.5

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

* [PATCH 8/9] Add devicetree bindings for Rockchip Soc EDP
  2014-08-04  4:41 [PATCH 0/9] Add drm driver for Rockchip Socs mark yao
                   ` (6 preceding siblings ...)
  2014-08-04  4:54   ` mark yao
@ 2014-08-04  4:55 ` mark yao
  2014-08-04 19:39     ` Heiko Stübner
  2014-08-04  4:57   ` mark yao
  2014-09-18  9:34   ` Mark yao
  9 siblings, 1 reply; 38+ messages in thread
From: mark yao @ 2014-08-04  4:55 UTC (permalink / raw)
  To: heiko, Rob Clark, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Randy Dunlap, David Airlie,
	Grant Likely, Greg Kroah-Hartman, John Stultz, Rom Lemarchand
  Cc: devicetree, linux-doc, linux-kernel, dri-devel, linux-api, olof,
	djkurtz, xjq, kfx, cym, cf, zyw, zwl, xxm, huangtao, kever.yang,
	zhangqing, yxj, wxt, xw, mark yao

Signed-off-by: mark yao <yzq@rock-chips.com>
---
 .../devicetree/bindings/video/rockchip-panel.txt   |   34 ++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/Documentation/devicetree/bindings/video/rockchip-panel.txt b/Documentation/devicetree/bindings/video/rockchip-panel.txt
index f599806..f6d80f6 100644
--- a/Documentation/devicetree/bindings/video/rockchip-panel.txt
+++ b/Documentation/devicetree/bindings/video/rockchip-panel.txt
@@ -80,3 +80,37 @@ Example:
 		rockchip,data-width = <24>;
 		rockchip,panel = <&panel>;
 	};
+
+Rockchip RK3288 EDP interface
+================================
+Required properties:
+-compatible: "rockchip,rk3288-edp";
+
+- reg: physical base address of the controller and length
+- clocks: from common clock binding: handle to dp clock.
+	of memory mapped region.
+- clock-names: from common clock binding:
+	Required elements: "clk_edp"
+			"clk_edp_24m"
+			"clk_edp_24m_parent"
+			"pclk_edp"
+- rockchip,grf: this soc should set GRF regs, so need get grf here.
+- resets: Must contain an entry for each entry in reset-names.
+	See ../reset/reset.txt for details.
+- reset-names: Must include the following entries:
+  - edp
+- rockchip,panel: required a panel node
+
+Example:
+	edp: edp@ff970000 {
+		compatible = "rockchip,rk3288-edp";
+                reg = <0xff970000 0x4000>;
+                interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
+                clocks = <&cru SCLK_EDP>, <&cru SCLK_EDP_24M>, <&cru PCLK_EDP_CTRL>, <&xin24m>;
+                clock-names = "clk_edp", "clk_edp_24m", "pclk_edp", "clk_edp_24m_parent";
+
+                rockchip,grf = <&grf>;
+                resets = <&cru 111>;
+                reset-names = "edp";
+		rockchip,panel = <&panel>;
+	};
-- 
1.7.9.5



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

* [PATCH 9/9] drm: add Rockchip Soc rk3288 edp connector
@ 2014-08-04  4:57   ` mark yao
  0 siblings, 0 replies; 38+ messages in thread
From: mark yao @ 2014-08-04  4:57 UTC (permalink / raw)
  To: heiko, Rob Clark, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Randy Dunlap, David Airlie,
	Grant Likely, Greg Kroah-Hartman, John Stultz, Rom Lemarchand
  Cc: devicetree, linux-doc, linux-kernel, dri-devel, linux-api, olof,
	djkurtz, xjq, kfx, cym, cf, zyw, zwl, xxm, huangtao, kever.yang,
	zhangqing, yxj, wxt, xw, mark yao

Signed-off-by: mark yao <yzq@rock-chips.com>
---
 drivers/gpu/drm/rockchip/connector/Kconfig         |    9 +
 drivers/gpu/drm/rockchip/connector/Makefile        |    1 +
 .../gpu/drm/rockchip/connector/rk3288_dp_core.c    |  586 ++++++++++
 .../gpu/drm/rockchip/connector/rk3288_dp_core.h    |  355 ++++++
 drivers/gpu/drm/rockchip/connector/rk3288_dp_reg.c | 1192 ++++++++++++++++++++
 drivers/gpu/drm/rockchip/connector/rk3288_dp_reg.h |  378 +++++++
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c        |   13 +
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h        |    3 +
 8 files changed, 2537 insertions(+)
 create mode 100644 drivers/gpu/drm/rockchip/connector/rk3288_dp_core.c
 create mode 100644 drivers/gpu/drm/rockchip/connector/rk3288_dp_core.h
 create mode 100644 drivers/gpu/drm/rockchip/connector/rk3288_dp_reg.c
 create mode 100644 drivers/gpu/drm/rockchip/connector/rk3288_dp_reg.h

diff --git a/drivers/gpu/drm/rockchip/connector/Kconfig b/drivers/gpu/drm/rockchip/connector/Kconfig
index 248942f..caffc5b 100644
--- a/drivers/gpu/drm/rockchip/connector/Kconfig
+++ b/drivers/gpu/drm/rockchip/connector/Kconfig
@@ -6,3 +6,12 @@ config RK3288_LVDS
 	  rk3288 lvds transmitter support ttl rgb, lvds and dual lvds
 	  mode, dual lvds mode is support for the plane which need dual
 	  lvds channels.
+
+config RK3288_DP
+	bool "RK3288 edp connector support"
+	depends on DRM_ROCKCHIP_CONNECTOR
+	help
+	  Choose this option if you have a rk32xx eDP connector.
+	  Rockchip rk3288 SoC has eDP TX Controller can be used.
+	  If you have an Embedded DisplayPort Panel, say Y to enable its
+	  driver.
diff --git a/drivers/gpu/drm/rockchip/connector/Makefile b/drivers/gpu/drm/rockchip/connector/Makefile
index dcfbdef..4be77ea 100644
--- a/drivers/gpu/drm/rockchip/connector/Makefile
+++ b/drivers/gpu/drm/rockchip/connector/Makefile
@@ -2,3 +2,4 @@
 # Makefile for display connector like lvds edp mipi
 #
 obj-$(CONFIG_RK3288_LVDS)	+= rk3288_lvds.o
+obj-$(CONFIG_RK3288_DP)		+= rk3288_dp_core.o rk3288_dp_reg.o
diff --git a/drivers/gpu/drm/rockchip/connector/rk3288_dp_core.c b/drivers/gpu/drm/rockchip/connector/rk3288_dp_core.c
new file mode 100644
index 0000000..3756383
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/connector/rk3288_dp_core.c
@@ -0,0 +1,586 @@
+/*
+* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+* Author:
+*      yxj <yxj@rock-chips.com>
+*      cym <cym@rock-chips.com>
+*
+* based on exynos_dp_core.c
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/of.h>
+
+#include "rk3288_dp_core.h"
+
+static int rk3288_edp_clk_enable(struct rk3288_edp *edp)
+{
+	int ret = 0;
+
+	if (!edp->clk_on) {
+		ret = clk_prepare_enable(edp->pclk);
+		if (ret < 0) {
+			dev_err(edp->dev, "cannot enable edp pclk %d\n", ret);
+			goto err_pclk;
+		}
+
+		ret = clk_prepare_enable(edp->clk_edp);
+		if (ret < 0) {
+			dev_err(edp->dev, "cannot enable clk_edp %d\n", ret);
+			goto err_clk_edp;
+		}
+
+		ret = clk_set_parent(edp->clk_24m, edp->clk_24m_parent);
+		if (ret < 0) {
+			dev_err(edp->dev, "cannot set edp clk_24m parent %d\n",
+				ret);
+			goto err_clk_24m;
+		}
+
+		ret = clk_prepare_enable(edp->clk_24m);
+		if (ret < 0) {
+			dev_err(edp->dev, "cannot enable edp clk_24m %d\n",
+				ret);
+			goto err_clk_24m;
+		}
+
+		edp->clk_on = true;
+	}
+
+	return 0;
+
+err_clk_24m:
+	clk_disable_unprepare(edp->clk_edp);
+err_clk_edp:
+	clk_disable_unprepare(edp->pclk);
+err_pclk:
+	edp->clk_on = false;
+
+	return ret;
+}
+
+static int rk3288_edp_clk_disable(struct rk3288_edp *edp)
+{
+	if (edp->clk_on) {
+		clk_disable_unprepare(edp->pclk);
+		clk_disable_unprepare(edp->clk_edp);
+		clk_disable_unprepare(edp->clk_24m);
+		edp->clk_on = false;
+	}
+
+	return 0;
+}
+
+static int rk3288_edp_pre_init(struct rk3288_edp *edp)
+{
+	u32 val;
+	int ret = 0;
+
+	val = GRF_EDP_REF_CLK_SEL_INTER | (GRF_EDP_REF_CLK_SEL_INTER << 16);
+	ret = regmap_write(edp->grf, edp->soc_data->grf_soc_con12, val);
+	if (ret != 0) {
+		dev_err(edp->dev, "Could not write to GRF: %d\n", ret);
+		return ret;
+	}
+
+	reset_control_assert(edp->rst);
+	usleep_range(10, 20);
+	reset_control_deassert(edp->rst);
+
+	return 0;
+}
+
+static int rk3288_edp_init_edp(struct rk3288_edp *edp)
+{
+	int lcdc_id = 1;
+	u32 val = 0;
+	int ret = 0;
+
+	/* select lcdc */
+	if (lcdc_id == 1)
+		val = EDP_SEL_VOP_LIT | (EDP_SEL_VOP_LIT << 16);
+	else
+		val = EDP_SEL_VOP_LIT << 16;
+	ret = regmap_write(edp->grf, edp->soc_data->grf_soc_con6, val);
+	if (ret != 0) {
+		dev_err(edp->dev, "Could not write to GRF: %d\n", ret);
+		return ret;
+	}
+
+	rk3288_edp_reset(edp);
+	rk3288_edp_init_refclk(edp);
+	rk3288_edp_init_interrupt(edp);
+	rk3288_edp_enable_sw_function(edp);
+	rk3288_edp_init_analog_func(edp);
+	rk3288_edp_init_hpd(edp);
+	rk3288_edp_init_aux(edp);
+
+	return 0;
+}
+
+static int rk3288_edp_get_max_rx_bandwidth(
+					struct rk3288_edp *edp,
+					u8 *bandwidth)
+{
+	u8 data;
+	int retval = 0;
+
+	/*
+	 * For DP rev.1.1, Maximum link rate of Main Link lanes
+	 * 0x06 = 1.62 Gbps, 0x0a = 2.7 Gbps
+	 */
+	retval = rk3288_edp_read_byte_from_dpcd(
+			edp, DPCD_ADDR_MAX_LINK_RATE, &data);
+	if (retval < 0)
+		*bandwidth = 0;
+	else
+		*bandwidth = data;
+
+	return retval;
+}
+
+static int rk3288_edp_get_max_rx_lane_count(struct rk3288_edp *edp,
+					    u8 *lane_count)
+{
+	u8 data;
+	int retval;
+
+	/*
+	 * For DP rev.1.1, Maximum number of Main Link lanes
+	 * 0x01 = 1 lane, 0x02 = 2 lanes, 0x04 = 4 lanes
+	 */
+	retval = rk3288_edp_read_byte_from_dpcd(
+			edp, DPCD_ADDR_MAX_LANE_COUNT, &data);
+	if (retval < 0)
+		*lane_count = 0;
+	else
+		*lane_count = DPCD_MAX_LANE_COUNT(data);
+
+	return retval;
+}
+
+static int rk3288_edp_init_training(struct rk3288_edp *edp)
+{
+	int retval;
+
+	/*
+	 * MACRO_RST must be applied after the PLL_LOCK to avoid
+	 * the DP inter pair skew issue for at least 10 us
+	 */
+	rk3288_edp_reset_macro(edp);
+
+	retval = rk3288_edp_get_max_rx_bandwidth(
+				edp, &edp->link_train.link_rate);
+	retval = rk3288_edp_get_max_rx_lane_count(
+				edp, &edp->link_train.lane_count);
+	dev_dbg(edp->dev, "max link rate:%d.%dGps max number of lanes:%d\n",
+		edp->link_train.link_rate * 27/100,
+		edp->link_train.link_rate*27%100,
+		edp->link_train.lane_count);
+
+	if ((edp->link_train.link_rate != LINK_RATE_1_62GBPS) &&
+	    (edp->link_train.link_rate != LINK_RATE_2_70GBPS)) {
+		dev_warn(edp->dev, "Rx Max Link Rate is abnormal :%x !\n"
+			 "use default link rate:%d.%dGps\n",
+			 edp->link_train.link_rate,
+			 edp->video_info.link_rate*27/100,
+			 edp->video_info.link_rate*27%100);
+			 edp->link_train.link_rate = edp->video_info.link_rate;
+	}
+
+	if (edp->link_train.lane_count == 0) {
+		dev_err(edp->dev, "Rx Max Lane count is abnormal :%x !\n"
+			"use default lanes:%d\n",
+			edp->link_train.lane_count,
+			edp->video_info.lane_count);
+		edp->link_train.lane_count = edp->video_info.lane_count;
+	}
+
+	rk3288_edp_analog_power_ctr(edp, 1);
+
+	return 0;
+}
+
+static int rk3288_edp_hw_link_training(struct rk3288_edp *edp)
+{
+	u32 cnt = 50;
+	u32 val;
+
+	/* Set link rate and count as you want to establish*/
+	rk3288_edp_set_link_bandwidth(edp, edp->link_train.link_rate);
+	rk3288_edp_set_lane_count(edp, edp->link_train.lane_count);
+	rk3288_edp_hw_link_training_en(edp);
+	val = rk3288_edp_wait_hw_lt_done(edp);
+	while (val) {
+		if (cnt-- <= 0) {
+			dev_err(edp->dev, "hw lt timeout");
+			return -ETIMEDOUT;
+		}
+		mdelay(1);
+		val = rk3288_edp_wait_hw_lt_done(edp);
+	}
+
+	val = rk3288_edp_get_hw_lt_status(edp);
+	if (val)
+		dev_err(edp->dev, "hw lt err:%d\n", val);
+
+	return val;
+}
+
+static int rk3288_edp_set_link_train(struct rk3288_edp *edp)
+{
+	int retval;
+
+	rk3288_edp_init_training(edp);
+
+	retval = rk3288_edp_hw_link_training(edp);
+	if (retval < 0)
+		dev_err(edp->dev, "DP hw LT failed!\n");
+
+	return retval;
+}
+
+static int rk3288_edp_config_video(struct rk3288_edp *edp,
+				   struct video_info *video_info)
+{
+	int retval = 0;
+	int timeout_loop = 0;
+	int done_count = 0;
+
+	rk3288_edp_config_video_slave_mode(edp, video_info);
+
+	rk3288_edp_set_video_color_format(edp, video_info->color_depth,
+					  video_info->color_space,
+					  video_info->dynamic_range,
+					  video_info->ycbcr_coeff);
+
+	if (rk3288_edp_get_pll_lock_status(edp) == DP_PLL_UNLOCKED) {
+		dev_err(edp->dev, "PLL is not locked yet.\n");
+		return -EINVAL;
+	}
+
+	for (;;) {
+		timeout_loop++;
+		if (rk3288_edp_is_slave_video_stream_clock_on(edp) == 0)
+			break;
+
+		if (DP_TIMEOUT_LOOP_CNT < timeout_loop) {
+			dev_err(edp->dev, "Timeout of video streamclk ok\n");
+			return -ETIMEDOUT;
+		}
+
+		udelay(1);
+	}
+
+	/* Set to use the register calculated M/N video */
+	rk3288_edp_set_video_cr_mn(edp, CALCULATED_M, 0, 0);
+
+	/* Disable video mute */
+	rk3288_edp_enable_video_mute(edp, 0);
+
+	/* Configure video slave mode */
+	rk3288_edp_enable_video_master(edp, 0);
+
+	/* Enable video */
+	rk3288_edp_start_video(edp);
+
+	timeout_loop = 0;
+
+	for (;;) {
+		timeout_loop++;
+		if (rk3288_edp_is_video_stream_on(edp) == 0) {
+			done_count++;
+			if (done_count > 10)
+				break;
+		} else if (done_count) {
+			done_count = 0;
+		}
+		if (DP_TIMEOUT_LOOP_CNT < timeout_loop) {
+			dev_err(edp->dev, "Timeout of video streamclk ok\n");
+			return -ETIMEDOUT;
+		}
+
+		mdelay(1);
+	}
+
+	if (retval != 0)
+		dev_err(edp->dev, "Video stream is not detected!\n");
+
+	return retval;
+}
+
+static irqreturn_t rk3288_edp_isr(int irq, void *arg)
+{
+	struct rk3288_edp *edp = arg;
+	enum dp_irq_type irq_type;
+
+	irq_type = rk3288_edp_get_irq_type(edp);
+	switch (irq_type) {
+	case DP_IRQ_TYPE_HP_CABLE_IN:
+		dev_dbg(edp->dev, "Received irq - cable in\n");
+		rk3288_edp_clear_hotplug_interrupts(edp);
+		break;
+	case DP_IRQ_TYPE_HP_CABLE_OUT:
+		dev_dbg(edp->dev, "Received irq - cable out\n");
+		rk3288_edp_clear_hotplug_interrupts(edp);
+		break;
+	case DP_IRQ_TYPE_HP_CHANGE:
+		/*
+		 * We get these change notifications once in a while, but there
+		 * is nothing we can do with them. Just ignore it for now and
+		 * only handle cable changes.
+		 */
+		dev_dbg(edp->dev, "Received irq - hotplug change; ignoring.\n");
+		rk3288_edp_clear_hotplug_interrupts(edp);
+		break;
+	default:
+		dev_err(edp->dev, "Received irq - unknown type[%x]!\n",
+			irq_type);
+		rk3288_edp_clear_hotplug_interrupts(edp);
+		break;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void rk3288_edp_enable(struct rockchip_connector *conn)
+{
+	struct rk3288_edp *edp = conn->priv;
+	int ret = 0;
+
+	ret = rk3288_edp_clk_enable(edp);
+	if (ret < 0) {
+		dev_err(edp->dev, "cannot enable edp clk %d\n", ret);
+		return;
+	}
+
+	ret = rk3288_edp_pre_init(edp);
+	if (ret < 0) {
+		dev_err(edp->dev, "edp pre init fail %d\n", ret);
+		return;
+	}
+
+	ret = rk3288_edp_init_edp(edp);
+	if (ret < 0) {
+		dev_err(edp->dev, "edp init fail %d\n", ret);
+		return;
+	}
+
+	enable_irq(edp->irq);
+
+	ret = rk3288_edp_set_link_train(edp);
+	if (ret)
+		dev_err(edp->dev, "link train failed!\n");
+	else
+		dev_dbg(edp->dev, "link training success.\n");
+
+	rk3288_edp_set_lane_count(edp, edp->link_train.lane_count);
+	rk3288_edp_set_link_bandwidth(edp, edp->link_train.link_rate);
+	rk3288_edp_init_video(edp);
+
+	ret = rk3288_edp_config_video(edp, &edp->video_info);
+	if (ret)
+		dev_err(edp->dev, "unable to config video\n");
+}
+
+static void  rk3288_edp_disable(struct rockchip_connector *conn)
+{
+	struct rk3288_edp *edp = conn->priv;
+
+	disable_irq(edp->irq);
+	rk3288_edp_reset(edp);
+	rk3288_edp_analog_power_ctr(edp, 0);
+	rk3288_edp_clk_disable(edp);
+}
+
+static int rk3288_edp_setmode(struct rockchip_connector *conn,
+			      struct drm_display_mode *mode)
+{
+	struct rk3288_edp *edp = conn->priv;
+
+	memcpy(&edp->mode, mode, sizeof(*mode));
+
+	return 0;
+}
+
+static struct rockchip_connector edp_conn = {
+	.enable = rk3288_edp_enable,
+	.disable = rk3288_edp_disable,
+	.setmode = rk3288_edp_setmode,
+};
+
+static struct rk3288_edp_soc_data soc_data[2] = {
+	{.grf_soc_con6 = 0x025c,
+	 .grf_soc_con12 = 0x0274},
+	{.grf_soc_con6 = -1,
+	 .grf_soc_con12 = -1},
+};
+
+static const struct of_device_id rk3288_edp_dt_ids[] = {
+	{.compatible = "rockchip,rk3288-edp",
+	 .data = (void *)&soc_data[0]},
+	{}
+};
+MODULE_DEVICE_TABLE(of, rk3288_edp_dt_ids);
+
+static int rk3288_edp_probe(struct platform_device *pdev)
+{
+	struct rk3288_edp *edp;
+	struct resource *res;
+	struct device_node *np = pdev->dev.of_node;
+	const struct of_device_id *match;
+	int ret = 0;
+
+	if (!np) {
+		dev_err(&pdev->dev, "Missing device tree node.\n");
+		return -EINVAL;
+	}
+
+	edp = devm_kzalloc(&pdev->dev, sizeof(struct rk3288_edp), GFP_KERNEL);
+	if (!edp) {
+		dev_err(&pdev->dev, "no memory for state\n");
+		return -ENOMEM;
+	}
+
+	match = of_match_node(rk3288_edp_dt_ids, np);
+	edp->soc_data = (struct rk3288_edp_soc_data *)match->data;
+	/*
+	 * The control bit is located in the GRF register space.
+	 */
+	if (edp->soc_data->grf_soc_con6 >= 0) {
+		edp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+		if (IS_ERR(edp->grf)) {
+			dev_err(&pdev->dev,
+				"rk3288-edp needs rockchip,grf property\n");
+			return PTR_ERR(edp->grf);
+		}
+	}
+
+	edp->dev = &pdev->dev;
+	edp->video_info.h_sync_polarity	= 0;
+	edp->video_info.v_sync_polarity	= 0;
+	edp->video_info.interlaced	= 0;
+	edp->video_info.color_space	= CS_RGB;
+	edp->video_info.dynamic_range	= VESA;
+	edp->video_info.ycbcr_coeff	= COLOR_YCBCR601;
+	edp->video_info.color_depth	= COLOR_8;
+
+	edp->video_info.link_rate	= LINK_RATE_1_62GBPS;
+	edp->video_info.lane_count	= LANE_CNT4;
+	edp_conn.type = ROCKCHIP_DISPLAY_TYPE_EDP;
+	edp_conn.priv = edp;
+	edp_conn.dev = &pdev->dev;
+
+	edp->base = rockchip_connector_register(&edp_conn);
+	if (!edp->base) {
+		dev_err(&pdev->dev, "connector register fail\n");
+		return -EINVAL;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	edp->regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(edp->regs)) {
+		dev_err(&pdev->dev, "ioremap reg failed\n");
+		return PTR_ERR(edp->regs);
+	}
+
+	edp->clk_edp = devm_clk_get(&pdev->dev, "clk_edp");
+	if (IS_ERR(edp->clk_edp)) {
+		dev_err(&pdev->dev, "cannot get clk_edp\n");
+		return PTR_ERR(edp->clk_edp);
+	}
+
+	edp->clk_24m_parent = devm_clk_get(&pdev->dev, "clk_edp_24m_parent");
+	if (IS_ERR(edp->clk_24m_parent)) {
+		dev_err(&pdev->dev, "cannot get clk_edp_24m_parent\n");
+		return PTR_ERR(edp->clk_24m_parent);
+	}
+
+	edp->clk_24m = devm_clk_get(&pdev->dev, "clk_edp_24m");
+	if (IS_ERR(edp->clk_24m)) {
+		dev_err(&pdev->dev, "cannot get clk_edp_24m\n");
+		return PTR_ERR(edp->clk_24m);
+	}
+
+	edp->pclk = devm_clk_get(&pdev->dev, "pclk_edp");
+	if (IS_ERR(edp->pclk)) {
+		dev_err(&pdev->dev, "cannot get pclk\n");
+		return PTR_ERR(edp->pclk);
+	}
+
+	edp->rst = devm_reset_control_get(&pdev->dev, "edp");
+	if (IS_ERR(edp->rst)) {
+		dev_err(&pdev->dev, "failed to get reset\n");
+		return PTR_ERR(edp->rst);
+	}
+
+	ret = rk3288_edp_clk_enable(edp);
+	if (ret < 0) {
+		dev_err(edp->dev, "cannot enable edp clk %d\n", ret);
+		return ret;
+	}
+
+	ret = rk3288_edp_pre_init(edp);
+	if (ret < 0) {
+		dev_err(edp->dev, "failed to pre init %d\n", ret);
+		return ret;
+	}
+
+	edp->irq = platform_get_irq(pdev, 0);
+	if (edp->irq < 0) {
+		dev_err(&pdev->dev, "cannot find IRQ\n");
+		return edp->irq;
+	}
+
+	ret = devm_request_irq(&pdev->dev, edp->irq, rk3288_edp_isr, 0,
+			       dev_name(&pdev->dev), edp);
+	if (ret) {
+		dev_err(&pdev->dev, "cannot claim IRQ %d\n", edp->irq);
+		return ret;
+	}
+
+	disable_irq_nosync(edp->irq);
+
+	edp->standby = true;
+
+	platform_set_drvdata(pdev, edp);
+	dev_set_name(edp->dev, "rk3288-edp");
+
+	dev_info(&pdev->dev, "rk3288 edp driver probe success\n");
+
+	return 0;
+}
+
+static int rk3288_edp_remove(struct platform_device *pdev)
+{
+	rk3288_edp_disable(&edp_conn);
+
+	return 0;
+}
+
+struct platform_driver rk3288_edp_driver = {
+	.probe = rk3288_edp_probe,
+	.remove = rk3288_edp_remove,
+	.driver = {
+		   .name = "rk3288-edp",
+		   .owner = THIS_MODULE,
+		   .of_match_table = of_match_ptr(rk3288_edp_dt_ids),
+	},
+};
diff --git a/drivers/gpu/drm/rockchip/connector/rk3288_dp_core.h b/drivers/gpu/drm/rockchip/connector/rk3288_dp_core.h
new file mode 100644
index 0000000..d71df70
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/connector/rk3288_dp_core.h
@@ -0,0 +1,355 @@
+/*
+* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+* Author:
+*      yxj <yxj@rock-chips.com>
+*      cym <cym@rock-chips.com>
+*
+* based on exynos_dp_core.h
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*/
+
+#ifndef __RK3288_DP_H
+#define __RK3288_DP_H
+#include "../rockchip_drm_connector.h"
+
+#define DP_TIMEOUT_LOOP_CNT 100
+#define MAX_CR_LOOP 5
+#define MAX_EQ_LOOP 5
+
+#define GRF_EDP_REF_CLK_SEL_INTER		(1 << 4)
+#define GRF_EDP_HDCP_EN				(1 << 15)
+#define GRF_EDP_BIST_EN				(1 << 14)
+#define GRF_EDP_MEM_CTL_BY_EDP			(1 << 13)
+#define GRF_EDP_SECURE_EN			(1 << 3)
+#define EDP_SEL_VOP_LIT				(1 << 5)
+
+enum dp_irq_type {
+	DP_IRQ_TYPE_HP_CABLE_IN,
+	DP_IRQ_TYPE_HP_CABLE_OUT,
+	DP_IRQ_TYPE_HP_CHANGE,
+	DP_IRQ_TYPE_UNKNOWN,
+};
+
+enum color_coefficient {
+	COLOR_YCBCR601,
+	COLOR_YCBCR709
+};
+
+enum dynamic_range {
+	VESA,
+	CEA
+};
+
+enum pll_status {
+	DP_PLL_UNLOCKED,
+	DP_PLL_LOCKED
+};
+
+enum clock_recovery_m_value_type {
+	CALCULATED_M,
+	REGISTER_M
+};
+
+enum video_timing_recognition_type {
+	VIDEO_TIMING_FROM_CAPTURE,
+	VIDEO_TIMING_FROM_REGISTER
+};
+
+enum pattern_set {
+	PRBS7,
+	D10_2,
+	TRAINING_PTN1,
+	TRAINING_PTN2,
+	DP_NONE
+};
+
+enum color_space {
+	CS_RGB,
+	CS_YCBCR422,
+	CS_YCBCR444
+};
+
+enum color_depth {
+	COLOR_6,
+	COLOR_8,
+	COLOR_10,
+	COLOR_12
+};
+
+enum link_rate_type {
+	LINK_RATE_1_62GBPS = 0x06,
+	LINK_RATE_2_70GBPS = 0x0a
+};
+
+enum link_lane_count_type {
+	LANE_CNT1 = 1,
+	LANE_CNT2 = 2,
+	LANE_CNT4 = 4
+};
+
+enum link_training_state {
+	LT_START,
+	LT_CLK_RECOVERY,
+	LT_EQ_TRAINING,
+	FINISHED,
+	FAILED
+};
+
+enum voltage_swing_level {
+	VOLTAGE_LEVEL_0,
+	VOLTAGE_LEVEL_1,
+	VOLTAGE_LEVEL_2,
+	VOLTAGE_LEVEL_3,
+};
+
+enum pre_emphasis_level {
+	PRE_EMPHASIS_LEVEL_0,
+	PRE_EMPHASIS_LEVEL_1,
+	PRE_EMPHASIS_LEVEL_2,
+	PRE_EMPHASIS_LEVEL_3,
+};
+
+enum analog_power_block {
+	AUX_BLOCK,
+	CH0_BLOCK,
+	CH1_BLOCK,
+	CH2_BLOCK,
+	CH3_BLOCK,
+	ANALOG_TOTAL,
+	POWER_ALL
+};
+
+struct video_info {
+	char *name;
+
+	bool h_sync_polarity;
+	bool v_sync_polarity;
+	bool interlaced;
+
+	enum color_space color_space;
+	enum dynamic_range dynamic_range;
+	enum color_coefficient ycbcr_coeff;
+	enum color_depth color_depth;
+
+	enum link_rate_type link_rate;
+	enum link_lane_count_type lane_count;
+};
+
+struct link_train {
+	int eq_loop;
+	int cr_loop[4];
+
+	u8 link_rate;
+	u8 lane_count;
+	u8 training_lane[4];
+
+	enum link_training_state lt_state;
+};
+
+/*
+ * @grf_offset: offset inside the grf regmap for setting the rk3288 lvds
+ */
+struct rk3288_edp_soc_data {
+	int grf_soc_con6;
+	int grf_soc_con12;
+};
+
+struct rk3288_edp {
+	void *base;
+	struct drm_display_mode mode;
+	struct device *dev;
+	void __iomem *regs;
+	struct regmap *grf;
+	struct rk3288_edp_soc_data *soc_data;
+	unsigned int irq;
+	/* clk for edp controller */
+	struct clk *clk_edp;
+	/* clk for edp phy parent */
+	struct clk *clk_24m_parent;
+	/* clk for edp phy */
+	struct clk *clk_24m;
+	/* clk for phb bus */
+	struct clk *pclk;
+	struct reset_control *rst;
+	struct link_train link_train;
+	struct video_info video_info;
+	bool clk_on;
+	bool standby;
+};
+
+void rk3288_edp_enable_video_mute(struct rk3288_edp *edp, bool enable);
+void rk3288_edp_stop_video(struct rk3288_edp *edp);
+void rk3288_edp_lane_swap(struct rk3288_edp *edp, bool enable);
+void rk3288_edp_init_refclk(struct rk3288_edp *edp);
+void rk3288_edp_init_interrupt(struct rk3288_edp *edp);
+void rk3288_edp_reset(struct rk3288_edp *edp);
+void rk3288_edp_config_interrupt(struct rk3288_edp *edp);
+u32 rk3288_edp_get_pll_lock_status(struct rk3288_edp *edp);
+void rk3288_edp_analog_power_ctr(struct rk3288_edp *edp, bool enable);
+void rk3288_edp_init_analog_func(struct rk3288_edp *edp);
+void rk3288_edp_init_hpd(struct rk3288_edp *edp);
+void rk3288_edp_reset_aux(struct rk3288_edp *edp);
+void rk3288_edp_init_aux(struct rk3288_edp *edp);
+int rk3288_edp_get_plug_in_status(struct rk3288_edp *edp);
+void rk3288_edp_enable_sw_function(struct rk3288_edp *edp);
+int rk3288_edp_start_aux_transaction(struct rk3288_edp *edp);
+int rk3288_edp_write_byte_to_dpcd(struct rk3288_edp *edp,
+				  unsigned int reg_addr,
+				  unsigned char data);
+int rk3288_edp_read_byte_from_dpcd(struct rk3288_edp *edp,
+				   unsigned int reg_addr,
+				   unsigned char *data);
+int rk3288_edp_write_bytes_to_dpcd(struct rk3288_edp *edp,
+				   unsigned int reg_addr,
+				   unsigned int count,
+				   unsigned char data[]);
+int rk3288_edp_read_bytes_from_dpcd(struct rk3288_edp *edp,
+				    unsigned int reg_addr,
+				    unsigned int count,
+				    unsigned char data[]);
+int rk3288_edp_select_i2c_device(struct rk3288_edp *edp,
+				 unsigned int device_addr,
+				 unsigned int reg_addr);
+int rk3288_edp_read_byte_from_i2c(struct rk3288_edp *edp,
+				  unsigned int device_addr,
+				  unsigned int reg_addr,
+				  unsigned int *data);
+int rk3288_edp_read_bytes_from_i2c(struct rk3288_edp *edp,
+				   unsigned int device_addr,
+				   unsigned int reg_addr,
+				   unsigned int count,
+				   unsigned char edid[]);
+void rk3288_edp_set_link_bandwidth(struct rk3288_edp *edp, u32 bwtype);
+void rk3288_edp_get_link_bandwidth(struct rk3288_edp *edp, u32 *bwtype);
+void rk3288_edp_set_lane_count(struct rk3288_edp *edp, u32 count);
+void rk3288_edp_get_lane_count(struct rk3288_edp *edp, u32 *count);
+void rk3288_edp_enable_enhanced_mode(struct rk3288_edp *edp, bool enable);
+void rk3288_edp_set_training_pattern(struct rk3288_edp *edp,
+				     enum pattern_set pattern);
+void rk3288_edp_set_lane0_pre_emphasis(struct rk3288_edp *edp, u32 level);
+void rk3288_edp_set_lane1_pre_emphasis(struct rk3288_edp *edp, u32 level);
+void rk3288_edp_set_lane2_pre_emphasis(struct rk3288_edp *edp, u32 level);
+void rk3288_edp_set_lane3_pre_emphasis(struct rk3288_edp *edp, u32 level);
+void rk3288_edp_set_lane0_link_training(struct rk3288_edp *edp,
+					u32 training_lane);
+void rk3288_edp_set_lane1_link_training(struct rk3288_edp *edp,
+					u32 training_lane);
+void rk3288_edp_set_lane2_link_training(struct rk3288_edp *edp,
+					u32 training_lane);
+void rk3288_edp_set_lane3_link_training(struct rk3288_edp *edp,
+					u32 training_lane);
+u32 rk3288_edp_get_lane0_link_training(struct rk3288_edp *edp);
+u32 rk3288_edp_get_lane1_link_training(struct rk3288_edp *edp);
+u32 rk3288_edp_get_lane2_link_training(struct rk3288_edp *edp);
+u32 rk3288_edp_get_lane3_link_training(struct rk3288_edp *edp);
+void rk3288_edp_reset_macro(struct rk3288_edp *edp);
+int rk3288_edp_init_video(struct rk3288_edp *edp);
+
+void rk3288_edp_set_video_color_format(struct rk3288_edp *edp,
+				       u32 color_depth,
+				       u32 color_space,
+				       u32 dynamic_range,
+				       u32 coeff);
+int rk3288_edp_is_slave_video_stream_clock_on(struct rk3288_edp *edp);
+void rk3288_edp_set_video_cr_mn(
+			struct rk3288_edp *edp,
+			enum clock_recovery_m_value_type type,
+			u32 m_value,
+			u32 n_value);
+void rk3288_edp_set_video_timing_mode(struct rk3288_edp *edp, u32 type);
+void rk3288_edp_enable_video_master(struct rk3288_edp *edp, bool enable);
+void rk3288_edp_start_video(struct rk3288_edp *edp);
+int rk3288_edp_is_video_stream_on(struct rk3288_edp *edp);
+void rk3288_edp_config_video_slave_mode(struct rk3288_edp *edp,
+					struct video_info *video_info);
+void rk3288_edp_enable_scrambling(struct rk3288_edp *edp);
+void rk3288_edp_disable_scrambling(struct rk3288_edp *edp);
+void rk3288_edp_rx_control(struct rk3288_edp *edp, bool enable);
+int rk3288_edp_bist_cfg(struct rk3288_edp *edp);
+void rk3288_edp_hw_link_training_en(struct rk3288_edp *edp);
+int rk3288_edp_get_hw_lt_status(struct rk3288_edp *edp);
+int rk3288_edp_wait_hw_lt_done(struct rk3288_edp *edp);
+enum dp_irq_type rk3288_edp_get_irq_type(struct rk3288_edp *edp);
+void rk3288_edp_clear_hotplug_interrupts(struct rk3288_edp *edp);
+
+/* I2C EDID Chip ID, Slave Address */
+#define I2C_EDID_DEVICE_ADDR			0x50
+#define I2C_E_EDID_DEVICE_ADDR			0x30
+
+#define EDID_BLOCK_LENGTH			0x80
+#define EDID_HEADER_PATTERN			0x00
+#define EDID_EXTENSION_FLAG			0x7e
+#define EDID_CHECKSUM				0x7f
+
+/* Definition for DPCD Register */
+#define DPCD_ADDR_DPCD_REV			0x0000
+#define DPCD_ADDR_MAX_LINK_RATE			0x0001
+#define DPCD_ADDR_MAX_LANE_COUNT		0x0002
+#define DPCD_ADDR_LINK_BW_SET			0x0100
+#define DPCD_ADDR_LANE_COUNT_SET		0x0101
+#define DPCD_ADDR_TRAINING_PATTERN_SET		0x0102
+#define DPCD_ADDR_TRAINING_LANE0_SET		0x0103
+#define DPCD_ADDR_LANE0_1_STATUS		0x0202
+#define DPCD_ADDR_LANE_ALIGN_STATUS_UPDATED	0x0204
+#define DPCD_ADDR_ADJUST_REQUEST_LANE0_1	0x0206
+#define DPCD_ADDR_ADJUST_REQUEST_LANE2_3	0x0207
+#define DPCD_ADDR_TEST_REQUEST			0x0218
+#define DPCD_ADDR_TEST_RESPONSE			0x0260
+#define DPCD_ADDR_TEST_EDID_CHECKSUM		0x0261
+#define DPCD_ADDR_SINK_POWER_STATE		0x0600
+
+/* DPCD_ADDR_MAX_LANE_COUNT */
+#define DPCD_ENHANCED_FRAME_CAP(x)		(((x) >> 7) & 0x1)
+#define DPCD_MAX_LANE_COUNT(x)			((x) & 0x1f)
+
+/* DPCD_ADDR_LANE_COUNT_SET */
+#define DPCD_ENHANCED_FRAME_EN			(0x1 << 7)
+#define DPCD_LANE_COUNT_SET(x)			((x) & 0x1f)
+
+/* DPCD_ADDR_TRAINING_PATTERN_SET */
+#define DPCD_SCRAMBLING_DISABLED		(0x1 << 5)
+#define DPCD_SCRAMBLING_ENABLED			(0x0 << 5)
+#define DPCD_TRAINING_PATTERN_2			(0x2 << 0)
+#define DPCD_TRAINING_PATTERN_1			(0x1 << 0)
+#define DPCD_TRAINING_PATTERN_DISABLED		(0x0 << 0)
+
+/* DPCD_ADDR_TRAINING_LANE0_SET */
+#define DPCD_MAX_PRE_EMPHASIS_REACHED		(0x1 << 5)
+#define DPCD_PRE_EMPHASIS_SET(x)		(((x) & 0x3) << 3)
+#define DPCD_PRE_EMPHASIS_GET(x)		(((x) >> 3) & 0x3)
+#define DPCD_PRE_EMPHASIS_PATTERN2_LEVEL0	(0x0 << 3)
+#define DPCD_MAX_SWING_REACHED			(0x1 << 2)
+#define DPCD_VOLTAGE_SWING_SET(x)		(((x) & 0x3) << 0)
+#define DPCD_VOLTAGE_SWING_GET(x)		(((x) >> 0) & 0x3)
+#define DPCD_VOLTAGE_SWING_PATTERN1_LEVEL0	(0x0 << 0)
+
+/* DPCD_ADDR_LANE0_1_STATUS */
+#define DPCD_LANE_SYMBOL_LOCKED			(0x1 << 2)
+#define DPCD_LANE_CHANNEL_EQ_DONE		(0x1 << 1)
+#define DPCD_LANE_CR_DONE			(0x1 << 0)
+#define DPCD_CHANNEL_EQ_BITS			(DPCD_LANE_CR_DONE|	\
+						 DPCD_LANE_CHANNEL_EQ_DONE|\
+						 DPCD_LANE_SYMBOL_LOCKED)
+
+/* DPCD_ADDR_LANE_ALIGN__STATUS_UPDATED */
+#define DPCD_LINK_STATUS_UPDATED		(0x1 << 7)
+#define DPCD_DOWNSTREAM_PORT_STATUS_CHANGED	(0x1 << 6)
+#define DPCD_INTERLANE_ALIGN_DONE		(0x1 << 0)
+
+/* DPCD_ADDR_TEST_REQUEST */
+#define DPCD_TEST_EDID_READ			(0x1 << 2)
+
+/* DPCD_ADDR_TEST_RESPONSE */
+#define DPCD_TEST_EDID_CHECKSUM_WRITE		(0x1 << 2)
+
+/* DPCD_ADDR_SINK_POWER_STATE */
+#define DPCD_SET_POWER_STATE_D0			(0x1 << 0)
+#define DPCD_SET_POWER_STATE_D4			(0x2 << 0)
+
+#define DPCD_SYMBOL_ERR_CONUT_LANE0		0x210
+
+#endif
diff --git a/drivers/gpu/drm/rockchip/connector/rk3288_dp_reg.c b/drivers/gpu/drm/rockchip/connector/rk3288_dp_reg.c
new file mode 100644
index 0000000..f558cea
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/connector/rk3288_dp_reg.c
@@ -0,0 +1,1192 @@
+/*
+* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+* Author:
+*      yxj <yxj@rock-chips.com>
+*      cym <cym@rock-chips.com>
+*
+* based on exynos_dp_reg.c
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*/
+
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#include "rk3288_dp_core.h"
+#include "rk3288_dp_reg.h"
+
+void rk3288_edp_enable_video_mute(struct rk3288_edp *edp, bool enable)
+{
+	u32 val;
+
+	if (enable) {
+		val = readl(edp->regs + VIDEO_CTL_1);
+		val |= VIDEO_MUTE;
+		writel(val, edp->regs + VIDEO_CTL_1);
+	} else {
+		val = readl(edp->regs + VIDEO_CTL_1);
+		val &= ~VIDEO_MUTE;
+		writel(val, edp->regs + VIDEO_CTL_1);
+	}
+}
+
+void rk3288_edp_stop_video(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + VIDEO_CTL_1);
+	val &= ~VIDEO_EN;
+	writel(val, edp->regs + VIDEO_CTL_1);
+}
+
+void rk3288_edp_lane_swap(struct rk3288_edp *edp, bool enable)
+{
+	u32 val;
+
+	if (enable)
+		val = LANE3_MAP_LOGIC_LANE_0 | LANE2_MAP_LOGIC_LANE_1 |
+			LANE1_MAP_LOGIC_LANE_2 | LANE0_MAP_LOGIC_LANE_3;
+	else
+		val = LANE3_MAP_LOGIC_LANE_3 | LANE2_MAP_LOGIC_LANE_2 |
+			LANE1_MAP_LOGIC_LANE_1 | LANE0_MAP_LOGIC_LANE_0;
+
+	writel(val, edp->regs + LANE_MAP);
+}
+
+void rk3288_edp_init_refclk(struct rk3288_edp *edp)
+{
+	writel(SEL_24M, edp->regs + ANALOG_CTL_2);
+	writel(REF_CLK_24M, edp->regs + PLL_REG_1);
+
+	writel(0x95, edp->regs + PLL_REG_2);
+	writel(0x40, edp->regs + PLL_REG_3);
+	writel(0x58, edp->regs + PLL_REG_4);
+	writel(0x22, edp->regs + PLL_REG_5);
+	writel(0x19, edp->regs + SSC_REG);
+	writel(0x87, edp->regs + TX_REG_COMMON);
+	writel(0x03, edp->regs + DP_AUX);
+	writel(0x46, edp->regs + DP_BIAS);
+	writel(0x55, edp->regs + DP_RESERVE2);
+}
+
+void rk3288_edp_init_interrupt(struct rk3288_edp *edp)
+{
+	/* Set interrupt pin assertion polarity as high */
+	writel(INT_POL, edp->regs + INT_CTL);
+
+	/* Clear pending valisers */
+	writel(0xff, edp->regs + COMMON_INT_STA_1);
+	writel(0x4f, edp->regs + COMMON_INT_STA_2);
+	writel(0xff, edp->regs + COMMON_INT_STA_3);
+	writel(0x27, edp->regs + COMMON_INT_STA_4);
+
+	writel(0x7f, edp->regs + DP_INT_STA);
+
+	/* 0:mask,1: unmask */
+	writel(0x00, edp->regs + COMMON_INT_MASK_1);
+	writel(0x00, edp->regs + COMMON_INT_MASK_2);
+	writel(0x00, edp->regs + COMMON_INT_MASK_3);
+	writel(0x00, edp->regs + COMMON_INT_MASK_4);
+	writel(0x00, edp->regs + DP_INT_STA_MASK);
+}
+
+void rk3288_edp_reset(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	rk3288_edp_stop_video(edp);
+	rk3288_edp_enable_video_mute(edp, 0);
+
+	val = VID_CAP_FUNC_EN_N | AUD_FIFO_FUNC_EN_N |
+		AUD_FUNC_EN_N | HDCP_FUNC_EN_N | SW_FUNC_EN_N;
+	writel(val, edp->regs + FUNC_EN_1);
+
+	val = SSC_FUNC_EN_N | AUX_FUNC_EN_N |
+		SERDES_FIFO_FUNC_EN_N |
+		LS_CLK_DOMAIN_FUNC_EN_N;
+	writel(val, edp->regs + FUNC_EN_2);
+
+	usleep_range(20, 30);
+
+	rk3288_edp_lane_swap(edp, 0);
+
+	writel(0x0, edp->regs + SYS_CTL_1);
+	writel(0x40, edp->regs + SYS_CTL_2);
+	writel(0x0, edp->regs + SYS_CTL_3);
+	writel(0x0, edp->regs + SYS_CTL_4);
+
+	writel(0x0, edp->regs + PKT_SEND_CTL);
+	writel(0x0, edp->regs + HDCP_CTL);
+
+	writel(0x5e, edp->regs + HPD_DEGLITCH_L);
+	writel(0x1a, edp->regs + HPD_DEGLITCH_H);
+
+	writel(0x10, edp->regs + LINK_DEBUG_CTL);
+
+	writel(0x0, edp->regs + VIDEO_FIFO_THRD);
+	writel(0x20, edp->regs + AUDIO_MARGIN);
+
+	writel(0x4, edp->regs + M_VID_GEN_FILTER_TH);
+	writel(0x2, edp->regs + M_AUD_GEN_FILTER_TH);
+
+	writel(0x0, edp->regs + SOC_GENERAL_CTL);
+}
+
+void rk3288_edp_config_interrupt(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	/* 0: mask, 1: unmask */
+	val = 0;
+	writel(val, edp->regs + COMMON_INT_MASK_1);
+
+	writel(val, edp->regs + COMMON_INT_MASK_2);
+
+	writel(val, edp->regs + COMMON_INT_MASK_3);
+
+	writel(val, edp->regs + COMMON_INT_MASK_4);
+
+	writel(val, edp->regs + DP_INT_STA_MASK);
+}
+
+u32 rk3288_edp_get_pll_lock_status(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + DEBUG_CTL);
+	if (val & PLL_LOCK)
+		return DP_PLL_LOCKED;
+	else
+		return DP_PLL_UNLOCKED;
+}
+
+void rk3288_edp_analog_power_ctr(struct rk3288_edp *edp, bool enable)
+{
+	u32 val;
+
+	if (enable) {
+		val = PD_EXP_BG | PD_AUX | PD_PLL |
+			PD_CH3 | PD_CH2 | PD_CH1 | PD_CH0;
+		writel(val, edp->regs + DP_PWRDN);
+		usleep_range(10, 20);
+		writel(0x0, edp->regs + DP_PWRDN);
+	} else {
+		val = PD_EXP_BG | PD_AUX | PD_PLL |
+			PD_CH3 | PD_CH2 | PD_CH1 | PD_CH0;
+		writel(val, edp->regs + DP_PWRDN);
+	}
+}
+
+void rk3288_edp_init_analog_func(struct rk3288_edp *edp)
+{
+	u32 val;
+	int wt = 0;
+
+	rk3288_edp_analog_power_ctr(edp, 1);
+
+	val = PLL_LOCK_CHG;
+	writel(val, edp->regs + COMMON_INT_STA_1);
+
+	val = readl(edp->regs + DEBUG_CTL);
+	val &= ~(F_PLL_LOCK | PLL_LOCK_CTRL);
+	writel(val, edp->regs + DEBUG_CTL);
+
+	/* Power up PLL */
+	while (wt < 100) {
+		if (rk3288_edp_get_pll_lock_status(edp) == DP_PLL_LOCKED) {
+			dev_dbg(edp->dev, "edp pll locked\n");
+			break;
+		} else {
+			wt++;
+			udelay(5);
+		}
+	}
+
+	/* Enable Serdes FIFO function and Link symbol clock domain module */
+	val = readl(edp->regs + FUNC_EN_2);
+	val &= ~(SERDES_FIFO_FUNC_EN_N | LS_CLK_DOMAIN_FUNC_EN_N
+		| AUX_FUNC_EN_N | SSC_FUNC_EN_N);
+	writel(val, edp->regs + FUNC_EN_2);
+}
+
+void rk3288_edp_init_hpd(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = HOTPLUG_CHG | HPD_LOST | PLUG;
+	writel(val, edp->regs + COMMON_INT_STA_4);
+
+	val = INT_HPD;
+	writel(val, edp->regs + DP_INT_STA);
+
+	val = readl(edp->regs + SYS_CTL_3);
+	val |= (F_HPD | HPD_CTRL);
+	writel(val, edp->regs + SYS_CTL_3);
+}
+
+void rk3288_edp_reset_aux(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	/* Disable AUX channel module */
+	val = readl(edp->regs + FUNC_EN_2);
+	val |= AUX_FUNC_EN_N;
+	writel(val, edp->regs + FUNC_EN_2);
+}
+
+void rk3288_edp_init_aux(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	/* Clear inerrupts related to AUX channel */
+	val = RPLY_RECEIV | AUX_ERR;
+	writel(val, edp->regs + DP_INT_STA);
+
+	rk3288_edp_reset_aux(edp);
+
+	/* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */
+	val = DEFER_CTRL_EN | DEFER_COUNT(1);
+	writel(val, edp->regs + AUX_CH_DEFER_CTL);
+
+	/* Enable AUX channel module */
+	val = readl(edp->regs + FUNC_EN_2);
+	val &= ~AUX_FUNC_EN_N;
+	writel(val, edp->regs + FUNC_EN_2);
+}
+
+int rk3288_edp_get_plug_in_status(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + SYS_CTL_3);
+	if (val & HPD_STATUS)
+		return 0;
+
+	return -EINVAL;
+}
+
+void rk3288_edp_enable_sw_function(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + FUNC_EN_1);
+	val &= ~SW_FUNC_EN_N;
+	writel(val, edp->regs + FUNC_EN_1);
+}
+
+int rk3288_edp_start_aux_transaction(struct rk3288_edp *edp)
+{
+	int val;
+	int retval = 0;
+	int timeout_loop = 0;
+	int aux_timeout = 0;
+
+	/* Enable AUX CH operation */
+	val = readl(edp->regs + AUX_CH_CTL_2);
+	val |= AUX_EN;
+	writel(val, edp->regs + AUX_CH_CTL_2);
+
+	/* Is AUX CH operation enabled? */
+	val = readl(edp->regs + AUX_CH_CTL_2);
+	while (val & AUX_EN) {
+		aux_timeout++;
+		if ((DP_TIMEOUT_LOOP_CNT * 10) < aux_timeout) {
+			dev_err(edp->dev, "AUX CH enable timeout!\n");
+			return -ETIMEDOUT;
+		}
+		val = readl(edp->regs + AUX_CH_CTL_2);
+		usleep_range(100, 110);
+	}
+
+	/* Is AUX CH command redply received? */
+	val = readl(edp->regs + DP_INT_STA);
+	while (!(val & RPLY_RECEIV)) {
+		timeout_loop++;
+		if (DP_TIMEOUT_LOOP_CNT < timeout_loop) {
+			dev_err(edp->dev, "AUX CH command redply failed!\n");
+			return -ETIMEDOUT;
+		}
+		val = readl(edp->regs + DP_INT_STA);
+		usleep_range(10, 20);
+	}
+
+	/* Clear interrupt source for AUX CH command redply */
+	writel(RPLY_RECEIV, edp->regs + DP_INT_STA);
+
+	/* Clear interrupt source for AUX CH access error */
+	val = readl(edp->regs + DP_INT_STA);
+	if (val & AUX_ERR) {
+		writel(AUX_ERR, edp->regs + DP_INT_STA);
+		return -EREMOTEIO;
+	}
+
+	/* Check AUX CH error access status */
+	val = readl(edp->regs + AUX_CH_STA);
+	if ((val & AUX_STATUS_MASK) != 0) {
+		dev_err(edp->dev, "AUX CH error happens: %d\n\n",
+			val & AUX_STATUS_MASK);
+		return -EREMOTEIO;
+	}
+
+	return retval;
+}
+
+int rk3288_edp_write_byte_to_dpcd(struct rk3288_edp *edp,
+				  unsigned int val_addr,
+				  unsigned char data)
+{
+	u32 val;
+	int i;
+	int retval;
+
+	for (i = 0; i < 3; i++) {
+		/* Clear AUX CH data buffer */
+		val = BUF_CLR;
+		writel(val, edp->regs + BUFFER_DATA_CTL);
+
+		/* Select DPCD device address */
+		val = AUX_ADDR_7_0(val_addr);
+		writel(val, edp->regs + DP_AUX_ADDR_7_0);
+		val = AUX_ADDR_15_8(val_addr);
+		writel(val, edp->regs + DP_AUX_ADDR_15_8);
+		val = AUX_ADDR_19_16(val_addr);
+		writel(val, edp->regs + DP_AUX_ADDR_19_16);
+
+		/* Write data buffer */
+		val = (unsigned int)data;
+		writel(val, edp->regs + BUF_DATA_0);
+
+		/*
+		 * Set DisplayPort transaction and write 1 byte
+		 * If bit 3 is 1, DisplayPort transaction.
+		 * If Bit 3 is 0, I2C transaction.
+		 */
+		val = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE;
+		writel(val, edp->regs + AUX_CH_CTL_1);
+
+		/* Start AUX transaction */
+		retval = rk3288_edp_start_aux_transaction(edp);
+		if (retval == 0)
+			break;
+		else
+			dev_dbg(edp->dev, "Aux Transaction fail!\n");
+	}
+
+	return retval;
+}
+
+int rk3288_edp_read_byte_from_dpcd(struct rk3288_edp *edp,
+				   unsigned int val_addr,
+				   unsigned char *data)
+{
+	u32 val;
+	int i;
+	int retval;
+
+	for (i = 0; i < 10; i++) {
+		/* Clear AUX CH data buffer */
+		val = BUF_CLR;
+		writel(val, edp->regs + BUFFER_DATA_CTL);
+
+		/* Select DPCD device address */
+		val = AUX_ADDR_7_0(val_addr);
+		writel(val, edp->regs + DP_AUX_ADDR_7_0);
+		val = AUX_ADDR_15_8(val_addr);
+		writel(val, edp->regs + DP_AUX_ADDR_15_8);
+		val = AUX_ADDR_19_16(val_addr);
+		writel(val, edp->regs + DP_AUX_ADDR_19_16);
+
+		/*
+		 * Set DisplayPort transaction and read 1 byte
+		 * If bit 3 is 1, DisplayPort transaction.
+		 * If Bit 3 is 0, I2C transaction.
+		 */
+		val = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ;
+		writel(val, edp->regs + AUX_CH_CTL_1);
+
+		/* Start AUX transaction */
+		retval = rk3288_edp_start_aux_transaction(edp);
+		if (retval == 0)
+			break;
+		else
+			dev_dbg(edp->dev, "Aux Transaction fail!\n");
+	}
+
+	/* Read data buffer */
+	val = readl(edp->regs + BUF_DATA_0);
+	*data = (unsigned char)(val & 0xff);
+
+	return retval;
+}
+
+int rk3288_edp_write_bytes_to_dpcd(struct rk3288_edp *edp,
+				   unsigned int val_addr,
+				   unsigned int count,
+				   unsigned char data[])
+{
+	u32 val;
+	unsigned int start_offset;
+	unsigned int cur_data_count;
+	unsigned int cur_data_idx;
+	int i;
+	int retval = 0;
+
+	/* Clear AUX CH data buffer */
+	val = BUF_CLR;
+	writel(val, edp->regs + BUFFER_DATA_CTL);
+
+	start_offset = 0;
+	while (start_offset < count) {
+		/* Buffer size of AUX CH is 16 * 4bytes */
+		if ((count - start_offset) > 16)
+			cur_data_count = 16;
+		else
+			cur_data_count = count - start_offset;
+
+		for (i = 0; i < 10; i++) {
+			/* Select DPCD device address */
+			val = AUX_ADDR_7_0(val_addr + start_offset);
+			writel(val, edp->regs + DP_AUX_ADDR_7_0);
+			val = AUX_ADDR_15_8(val_addr + start_offset);
+			writel(val, edp->regs + DP_AUX_ADDR_15_8);
+			val = AUX_ADDR_19_16(val_addr + start_offset);
+			writel(val, edp->regs + DP_AUX_ADDR_19_16);
+
+			for (cur_data_idx = 0; cur_data_idx < cur_data_count;
+			     cur_data_idx++) {
+				val = data[start_offset + cur_data_idx];
+				writel(val, edp->regs + BUF_DATA_0
+							  + 4 * cur_data_idx);
+			}
+
+			/*
+			 * Set DisplayPort transaction and write
+			 * If bit 3 is 1, DisplayPort transaction.
+			 * If Bit 3 is 0, I2C transaction.
+			 */
+			val = AUX_LENGTH(cur_data_count) |
+				AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE;
+			writel(val, edp->regs + AUX_CH_CTL_1);
+
+			/* Start AUX transaction */
+			retval = rk3288_edp_start_aux_transaction(edp);
+			if (retval == 0)
+				break;
+			else
+				dev_dbg(edp->dev, "Aux Transaction fail!\n");
+		}
+
+		start_offset += cur_data_count;
+	}
+
+	return retval;
+}
+
+int rk3288_edp_read_bytes_from_dpcd(struct rk3288_edp *edp,
+				    unsigned int val_addr,
+				    unsigned int count,
+				    unsigned char data[])
+{
+	u32 val;
+	unsigned int start_offset;
+	unsigned int cur_data_count;
+	unsigned int cur_data_idx;
+	int i;
+	int retval = 0;
+
+	/* Clear AUX CH data buffer */
+	val = BUF_CLR;
+	writel(val, edp->regs + BUFFER_DATA_CTL);
+
+	start_offset = 0;
+	while (start_offset < count) {
+		/* Buffer size of AUX CH is 16 * 4bytes */
+		if ((count - start_offset) > 16)
+			cur_data_count = 16;
+		else
+			cur_data_count = count - start_offset;
+
+		/* AUX CH Request Transaction process */
+		for (i = 0; i < 10; i++) {
+			/* Select DPCD device address */
+			val = AUX_ADDR_7_0(val_addr + start_offset);
+			writel(val, edp->regs + DP_AUX_ADDR_7_0);
+			val = AUX_ADDR_15_8(val_addr + start_offset);
+			writel(val, edp->regs + DP_AUX_ADDR_15_8);
+			val = AUX_ADDR_19_16(val_addr + start_offset);
+			writel(val, edp->regs + DP_AUX_ADDR_19_16);
+
+			/*
+			 * Set DisplayPort transaction and read
+			 * If bit 3 is 1, DisplayPort transaction.
+			 * If Bit 3 is 0, I2C transaction.
+			 */
+			val = AUX_LENGTH(cur_data_count) |
+				AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ;
+			writel(val, edp->regs + AUX_CH_CTL_1);
+
+			/* Start AUX transaction */
+			retval = rk3288_edp_start_aux_transaction(edp);
+			if (retval == 0)
+				break;
+			else
+				dev_dbg(edp->dev, "Aux Transaction fail!\n");
+		}
+
+		for (cur_data_idx = 0; cur_data_idx < cur_data_count;
+		    cur_data_idx++) {
+			val = readl(edp->regs + BUF_DATA_0
+						 + 4 * cur_data_idx);
+			data[start_offset + cur_data_idx] =
+				(unsigned char)val;
+		}
+
+		start_offset += cur_data_count;
+	}
+
+	return retval;
+}
+
+int rk3288_edp_select_i2c_device(struct rk3288_edp *edp,
+				 unsigned int device_addr,
+				 unsigned int val_addr)
+{
+	u32 val;
+	int retval;
+
+	/* Set EDID device address */
+	val = device_addr;
+	writel(val, edp->regs + DP_AUX_ADDR_7_0);
+	writel(0x0, edp->regs + DP_AUX_ADDR_15_8);
+	writel(0x0, edp->regs + DP_AUX_ADDR_19_16);
+
+	/* Set offset from base address of EDID device */
+	writel(val_addr, edp->regs + BUF_DATA_0);
+
+	/*
+	 * Set I2C transaction and write address
+	 * If bit 3 is 1, DisplayPort transaction.
+	 * If Bit 3 is 0, I2C transaction.
+	 */
+	val = AUX_TX_COMM_I2C_TRANSACTION | AUX_TX_COMM_MOT |
+		AUX_TX_COMM_WRITE;
+	writel(val, edp->regs + AUX_CH_CTL_1);
+
+	/* Start AUX transaction */
+	retval = rk3288_edp_start_aux_transaction(edp);
+	if (retval != 0)
+		dev_dbg(edp->dev, "Aux Transaction fail!\n");
+
+	return retval;
+}
+
+int rk3288_edp_read_byte_from_i2c(struct rk3288_edp *edp,
+				  unsigned int device_addr,
+				  unsigned int val_addr,
+				  unsigned int *data)
+{
+	u32 val;
+	int i;
+	int retval;
+
+	for (i = 0; i < 10; i++) {
+		/* Clear AUX CH data buffer */
+		val = BUF_CLR;
+		writel(val, edp->regs + BUFFER_DATA_CTL);
+
+		/* Select EDID device */
+		retval = rk3288_edp_select_i2c_device(edp,
+						      device_addr,
+						      val_addr);
+		if (retval != 0) {
+			dev_err(edp->dev, "Select EDID device fail!\n");
+			continue;
+		}
+
+		/*
+		 * Set I2C transaction and read data
+		 * If bit 3 is 1, DisplayPort transaction.
+		 * If Bit 3 is 0, I2C transaction.
+		 */
+		val = AUX_TX_COMM_I2C_TRANSACTION | AUX_TX_COMM_READ;
+		writel(val, edp->regs + AUX_CH_CTL_1);
+
+		/* Start AUX transaction */
+		retval = rk3288_edp_start_aux_transaction(edp);
+		if (retval == 0)
+			break;
+		else
+			dev_dbg(edp->dev, "Aux Transaction fail!\n");
+	}
+
+	/* Read data */
+	if (retval == 0)
+		*data = readl(edp->regs + BUF_DATA_0);
+
+	return retval;
+}
+
+int rk3288_edp_read_bytes_from_i2c(struct rk3288_edp *edp,
+				   unsigned int device_addr,
+				   unsigned int val_addr,
+				   unsigned int count,
+				   unsigned char edid[])
+{
+	u32 val;
+	unsigned int i, j;
+	unsigned int cur_data_idx;
+	unsigned int defer = 0;
+	int retval = 0;
+
+	for (i = 0; i < count; i += 16) {
+		for (j = 0; j < 100; j++) {
+			/* Clear AUX CH data buffer */
+			val = BUF_CLR;
+			writel(val, edp->regs + BUFFER_DATA_CTL);
+
+			/* Set normal AUX CH command */
+			val = readl(edp->regs + AUX_CH_CTL_2);
+			val &= ~ADDR_ONLY;
+			writel(val, edp->regs + AUX_CH_CTL_2);
+
+			/*
+			 * If Rx sends defer, Tx sends only reads
+			 * request without sending addres
+			 */
+			if (!defer)
+				retval = rk3288_edp_select_i2c_device(
+						edp, device_addr, val_addr + i);
+			else
+				defer = 0;
+
+			/*
+			 * Set I2C transaction and write data
+			 * If bit 3 is 1, DisplayPort transaction.
+			 * If Bit 3 is 0, I2C transaction.
+			 */
+			val = AUX_LENGTH(16) | AUX_TX_COMM_I2C_TRANSACTION |
+				AUX_TX_COMM_READ;
+			writel(val, edp->regs + AUX_CH_CTL_1);
+
+			/* Start AUX transaction */
+			retval = rk3288_edp_start_aux_transaction(edp);
+			if (retval == 0)
+				break;
+			else
+				dev_dbg(edp->dev, "Aux Transaction fail!\n");
+
+			/* Check if Rx sends defer */
+			val = readl(edp->regs + AUX_RX_COMM);
+			if (val == AUX_RX_COMM_AUX_DEFER ||
+			    val == AUX_RX_COMM_I2C_DEFER) {
+				dev_err(edp->dev, "Defer: %d\n\n", val);
+				defer = 1;
+			}
+		}
+
+		for (cur_data_idx = 0; cur_data_idx < 16; cur_data_idx++) {
+			val = readl(edp->regs + BUF_DATA_0 + 4 * cur_data_idx);
+			edid[i + cur_data_idx] = (unsigned char)val;
+		}
+	}
+
+	return retval;
+}
+
+void rk3288_edp_set_link_bandwidth(struct rk3288_edp *edp, u32 bwtype)
+{
+	u32 val;
+
+	val = bwtype;
+	if ((bwtype == LINK_RATE_2_70GBPS) || (bwtype == LINK_RATE_1_62GBPS))
+		writel(val, edp->regs + LINK_BW_SET);
+}
+
+void rk3288_edp_get_link_bandwidth(struct rk3288_edp *edp, u32 *bwtype)
+{
+	u32 val;
+
+	val = readl(edp->regs + LINK_BW_SET);
+	*bwtype = val;
+}
+
+void rk3288_edp_hw_link_training_en(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = HW_LT_EN;
+	writel(val, edp->regs + HW_LT_CTL);
+}
+
+int rk3288_edp_wait_hw_lt_done(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + DP_INT_STA);
+	if (val&HW_LT_DONE) {
+		writel(val, edp->regs + DP_INT_STA);
+		return 0;
+	} else {
+		return 1;
+	}
+}
+
+int rk3288_edp_get_hw_lt_status(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + HW_LT_CTL);
+	return (val & HW_LT_ERR_CODE_MASK) >> 4;
+}
+void rk3288_edp_set_lane_count(struct rk3288_edp *edp, u32 count)
+{
+	u32 val;
+
+	val = count;
+	writel(val, edp->regs + LANE_CNT_SET);
+}
+
+void rk3288_edp_get_lane_count(struct rk3288_edp *edp, u32 *count)
+{
+	u32 val;
+
+	val = readl(edp->regs + LANE_CNT_SET);
+	*count = val;
+}
+
+void rk3288_edp_enable_enhanced_mode(struct rk3288_edp *edp, bool enable)
+{
+	u32 val;
+
+	if (enable) {
+		val = readl(edp->regs + SYS_CTL_4);
+		val |= ENHANCED;
+		writel(val, edp->regs + SYS_CTL_4);
+	} else {
+		val = readl(edp->regs + SYS_CTL_4);
+		val &= ~ENHANCED;
+		writel(val, edp->regs + SYS_CTL_4);
+	}
+}
+
+void rk3288_edp_set_training_pattern(struct rk3288_edp *edp,
+				     enum pattern_set pattern)
+{
+	u32 val;
+
+	switch (pattern) {
+	case PRBS7:
+		val = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_PRBS7;
+		writel(val, edp->regs + TRAINING_PTN_SET);
+		break;
+	case D10_2:
+		val = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_D10_2;
+		writel(val, edp->regs + TRAINING_PTN_SET);
+		break;
+	case TRAINING_PTN1:
+		val = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN1;
+		writel(val, edp->regs + TRAINING_PTN_SET);
+		break;
+	case TRAINING_PTN2:
+		val = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN2;
+		writel(val, edp->regs + TRAINING_PTN_SET);
+		break;
+	case DP_NONE:
+		val = SCRAMBLING_ENABLE |
+			LINK_QUAL_PATTERN_SET_DISABLE |
+			SW_TRAINING_PATTERN_SET_DISABLE;
+		writel(val, edp->regs + TRAINING_PTN_SET);
+		break;
+	default:
+		break;
+	}
+}
+
+void rk3288_edp_set_lane0_pre_emphasis(struct rk3288_edp *edp, u32 level)
+{
+	u32 val;
+
+	val = level << PRE_EMPHASIS_SET_SHIFT;
+	writel(val, edp->regs + LN0_LINK_TRAINING_CTL);
+}
+
+void rk3288_edp_set_lane1_pre_emphasis(struct rk3288_edp *edp, u32 level)
+{
+	u32 val;
+
+	val = level << PRE_EMPHASIS_SET_SHIFT;
+	writel(val, edp->regs + LN1_LINK_TRAINING_CTL);
+}
+
+void rk3288_edp_set_lane2_pre_emphasis(struct rk3288_edp *edp, u32 level)
+{
+	u32 val;
+
+	val = level << PRE_EMPHASIS_SET_SHIFT;
+	writel(val, edp->regs + LN2_LINK_TRAINING_CTL);
+}
+
+void rk3288_edp_set_lane3_pre_emphasis(struct rk3288_edp *edp, u32 level)
+{
+	u32 val;
+
+	val = level << PRE_EMPHASIS_SET_SHIFT;
+	writel(val, edp->regs + LN3_LINK_TRAINING_CTL);
+}
+
+void rk3288_edp_set_lane0_link_training(struct rk3288_edp *edp,
+					u32 training_lane)
+{
+	u32 val;
+
+	val = training_lane;
+	writel(val, edp->regs + LN0_LINK_TRAINING_CTL);
+}
+
+void rk3288_edp_set_lane1_link_training(struct rk3288_edp *edp,
+					u32 training_lane)
+{
+	u32 val;
+
+	val = training_lane;
+	writel(val, edp->regs + LN1_LINK_TRAINING_CTL);
+}
+
+void rk3288_edp_set_lane2_link_training(struct rk3288_edp *edp,
+					u32 training_lane)
+{
+	u32 val;
+
+	val = training_lane;
+	writel(val, edp->regs + LN2_LINK_TRAINING_CTL);
+}
+
+void rk3288_edp_set_lane3_link_training(struct rk3288_edp *edp,
+					u32 training_lane)
+{
+	u32 val;
+
+	val = training_lane;
+	writel(val, edp->regs + LN3_LINK_TRAINING_CTL);
+}
+
+u32 rk3288_edp_get_lane0_link_training(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + LN0_LINK_TRAINING_CTL);
+	return val;
+}
+
+u32 rk3288_edp_get_lane1_link_training(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + LN1_LINK_TRAINING_CTL);
+	return val;
+}
+
+u32 rk3288_edp_get_lane2_link_training(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + LN2_LINK_TRAINING_CTL);
+	return val;
+}
+
+u32 rk3288_edp_get_lane3_link_training(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + LN3_LINK_TRAINING_CTL);
+	return val;
+}
+
+void rk3288_edp_reset_macro(struct rk3288_edp *edp)
+{
+}
+
+int rk3288_edp_init_video(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = VSYNC_DET | VID_FORMAT_CHG | VID_CLK_CHG;
+	writel(val, edp->regs + COMMON_INT_STA_1);
+
+	val = 0x0;
+	writel(val, edp->regs + SYS_CTL_1);
+
+	val = CHA_CRI(4) | CHA_CTRL;
+	writel(val, edp->regs + SYS_CTL_2);
+
+	val = VID_HRES_TH(2) | VID_VRES_TH(0);
+	writel(val, edp->regs + VIDEO_CTL_8);
+
+	return 0;
+}
+
+void rk3288_edp_set_video_color_format(struct rk3288_edp *edp,
+				       u32 color_dedpth,
+				       u32 color_space,
+				       u32 dynamic_range,
+				       u32 coeff)
+{
+	u32 val;
+
+	/* Configure the input color dedpth, color space, dynamic range */
+	val = (dynamic_range << IN_D_RANGE_SHIFT) |
+		(color_dedpth << IN_BPC_SHIFT) |
+		(color_space << IN_COLOR_F_SHIFT);
+	writel(val, edp->regs + VIDEO_CTL_2);
+
+	/* Set Input Color YCbCr Coefficients to ITU601 or ITU709 */
+	val = readl(edp->regs + VIDEO_CTL_3);
+	val &= ~IN_YC_COEFFI_MASK;
+	if (coeff)
+		val |= IN_YC_COEFFI_ITU709;
+	else
+		val |= IN_YC_COEFFI_ITU601;
+	writel(val, edp->regs + VIDEO_CTL_3);
+}
+
+int rk3288_edp_is_slave_video_stream_clock_on(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + SYS_CTL_1);
+	writel(val, edp->regs + SYS_CTL_1);
+
+	val = readl(edp->regs + SYS_CTL_1);
+
+	if (!(val & DET_STA)) {
+		dev_dbg(edp->dev, "Input stream clock not detected.\n");
+		return -EINVAL;
+	}
+
+	val = readl(edp->regs + SYS_CTL_2);
+	writel(val, edp->regs + SYS_CTL_2);
+
+	val = readl(edp->regs + SYS_CTL_2);
+	if (val & CHA_STA) {
+		dev_dbg(edp->dev, "Input stream clk is changing\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+void rk3288_edp_set_video_cr_mn(struct rk3288_edp *edp,
+				enum clock_recovery_m_value_type type,
+				u32 m_value,
+				u32 n_value)
+{
+	u32 val;
+
+	if (type == REGISTER_M) {
+		val = readl(edp->regs + SYS_CTL_4);
+		val |= FIX_M_VID;
+		writel(val, edp->regs + SYS_CTL_4);
+		val = m_value & 0xff;
+		writel(val, edp->regs + M_VID_0);
+		val = (m_value >> 8) & 0xff;
+		writel(val, edp->regs + M_VID_1);
+		val = (m_value >> 16) & 0xff;
+		writel(val, edp->regs + M_VID_2);
+
+		val = n_value & 0xff;
+		writel(val, edp->regs + N_VID_0);
+		val = (n_value >> 8) & 0xff;
+		writel(val, edp->regs + N_VID_1);
+		val = (n_value >> 16) & 0xff;
+		writel(val, edp->regs + N_VID_2);
+	} else  {
+		val = readl(edp->regs + SYS_CTL_4);
+		val &= ~FIX_M_VID;
+		writel(val, edp->regs + SYS_CTL_4);
+
+		writel(0x00, edp->regs + N_VID_0);
+		writel(0x80, edp->regs + N_VID_1);
+		writel(0x00, edp->regs + N_VID_2);
+	}
+}
+
+void rk3288_edp_set_video_timing_mode(struct rk3288_edp *edp, u32 type)
+{
+	u32 val;
+
+	if (type == VIDEO_TIMING_FROM_CAPTURE) {
+		val = readl(edp->regs + VIDEO_CTL_10);
+		val &= ~F_SEL;
+		writel(val, edp->regs + VIDEO_CTL_10);
+	} else {
+		val = readl(edp->regs + VIDEO_CTL_10);
+		val |= F_SEL;
+		writel(val, edp->regs + VIDEO_CTL_10);
+	}
+}
+
+int rk3288_edp_bist_cfg(struct rk3288_edp *edp)
+{
+	struct video_info *video_info = &edp->video_info;
+	struct drm_display_mode *mode = &edp->mode;
+	u16 x_total, y_total, x_act;
+	u32 val;
+
+	x_total = mode->htotal;
+	y_total = mode->vtotal;
+	x_act = mode->hdisplay;
+
+	rk3288_edp_set_video_cr_mn(edp, CALCULATED_M, 0, 0);
+	rk3288_edp_set_video_color_format(edp, video_info->color_depth,
+					  video_info->color_space,
+					  video_info->dynamic_range,
+					  video_info->ycbcr_coeff);
+
+	val = y_total & 0xff;
+	writel(val, edp->regs + TOTAL_LINE_CFG_L);
+	val = (y_total >> 8);
+	writel(val, edp->regs + TOTAL_LINE_CFG_H);
+	val = (mode->vdisplay & 0xff);
+	writel(val, edp->regs + ATV_LINE_CFG_L);
+	val = (mode->vdisplay >> 8);
+	writel(val, edp->regs + ATV_LINE_CFG_H);
+	val = (mode->vsync_start - mode->vdisplay);
+	writel(val, edp->regs + VF_PORCH_REG);
+	val = (mode->vsync_end - mode->vsync_start);
+	writel(val, edp->regs + VSYNC_CFG_REG);
+	val = (mode->vtotal - mode->vsync_end);
+	writel(val, edp->regs + VB_PORCH_REG);
+	val = x_total & 0xff;
+	writel(val, edp->regs + TOTAL_PIXELL_REG);
+	val = x_total >> 8;
+	writel(val, edp->regs + TOTAL_PIXELH_REG);
+	val = (x_act & 0xff);
+	writel(val, edp->regs + ATV_PIXELL_REG);
+	val = (x_act >> 8);
+	writel(val, edp->regs + ATV_PIXELH_REG);
+	val = (mode->hsync_start - mode->hdisplay) & 0xff;
+	writel(val, edp->regs + HF_PORCHL_REG);
+	val = (mode->hsync_start - mode->hdisplay) >> 8;
+	writel(val, edp->regs + HF_PORCHH_REG);
+	val = (mode->hsync_end - mode->hsync_start) & 0xff;
+	writel(val, edp->regs + HSYNC_CFGL_REG);
+	val = (mode->hsync_end - mode->hsync_start) >> 8;
+	writel(val, edp->regs + HSYNC_CFGH_REG);
+	val = (mode->htotal - mode->hsync_end) & 0xff;
+	writel(val, edp->regs + HB_PORCHL_REG);
+	val = (mode->htotal - mode->hsync_end)  >> 8;
+	writel(val, edp->regs + HB_PORCHH_REG);
+
+	val = BIST_EN | BIST_WH_64 | BIST_TYPE_COLR_BAR;
+	writel(val, edp->regs + VIDEO_CTL_4);
+
+	val = readl(edp->regs + VIDEO_CTL_10);
+	val &= ~F_SEL;
+	writel(val, edp->regs + VIDEO_CTL_10);
+	return 0;
+}
+
+void rk3288_edp_enable_video_master(struct rk3288_edp *edp, bool enable)
+{
+}
+
+void rk3288_edp_start_video(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + VIDEO_CTL_1);
+	val |= VIDEO_EN;
+	writel(val, edp->regs + VIDEO_CTL_1);
+}
+
+int rk3288_edp_is_video_stream_on(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + SYS_CTL_3);
+	writel(val, edp->regs + SYS_CTL_3);
+
+	val = readl(edp->regs + SYS_CTL_3);
+	if (!(val & STRM_VALID)) {
+		dev_dbg(edp->dev, "Input video stream is not detected.\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+void rk3288_edp_config_video_slave_mode(struct rk3288_edp *edp,
+					struct video_info *video_info)
+{
+	u32 val;
+
+	val = readl(edp->regs + FUNC_EN_1);
+	val &= ~(VID_FIFO_FUNC_EN_N | VID_CAP_FUNC_EN_N);
+	writel(val, edp->regs + FUNC_EN_1);
+
+	val = readl(edp->regs + VIDEO_CTL_10);
+	val &= ~INTERACE_SCAN_CFG;
+	val |= (video_info->interlaced << 2);
+	writel(val, edp->regs + VIDEO_CTL_10);
+
+	val = readl(edp->regs + VIDEO_CTL_10);
+	val &= ~VSYNC_POLARITY_CFG;
+	val |= (video_info->v_sync_polarity << 1);
+	writel(val, edp->regs + VIDEO_CTL_10);
+
+	val = readl(edp->regs + VIDEO_CTL_10);
+	val &= ~HSYNC_POLARITY_CFG;
+	val |= (video_info->h_sync_polarity << 0);
+	writel(val, edp->regs + VIDEO_CTL_10);
+}
+
+void rk3288_edp_enable_scrambling(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + TRAINING_PTN_SET);
+	val &= ~SCRAMBLING_DISABLE;
+	writel(val, edp->regs + TRAINING_PTN_SET);
+}
+
+void rk3288_edp_disable_scrambling(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + TRAINING_PTN_SET);
+	val |= SCRAMBLING_DISABLE;
+	writel(val, edp->regs + TRAINING_PTN_SET);
+}
+
+enum dp_irq_type rk3288_edp_get_irq_type(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	/* Parse hotplug interrupt status register */
+	val = readl(edp->regs + COMMON_INT_STA_4);
+	if (val & PLUG)
+		return DP_IRQ_TYPE_HP_CABLE_IN;
+
+	if (val & HPD_LOST)
+		return DP_IRQ_TYPE_HP_CABLE_OUT;
+
+	if (val & HOTPLUG_CHG)
+		return DP_IRQ_TYPE_HP_CHANGE;
+
+	return DP_IRQ_TYPE_UNKNOWN;
+}
+
+void rk3288_edp_clear_hotplug_interrupts(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = HOTPLUG_CHG | HPD_LOST | PLUG;
+	writel(val, edp->regs + COMMON_INT_STA_4);
+
+	val = INT_HPD;
+	writel(val, edp->regs + DP_INT_STA);
+}
diff --git a/drivers/gpu/drm/rockchip/connector/rk3288_dp_reg.h b/drivers/gpu/drm/rockchip/connector/rk3288_dp_reg.h
new file mode 100644
index 0000000..baf8d90
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/connector/rk3288_dp_reg.h
@@ -0,0 +1,378 @@
+/*
+* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+* Author:
+*      yxj <yxj@rock-chips.com>
+*      cym <cym@rock-chips.com>
+*
+* based on exynos_dp_reg.h
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*/
+
+#ifndef _RK32XX_DP_REG_H
+#define _RK32XX_DP_REG_H
+
+#define DP_VERSION				0x10
+
+#define TX_SW_RST				0x14
+
+#define FUNC_EN_1				0x18
+#define VID_CAP_FUNC_EN_N			BIT(6)
+#define VID_FIFO_FUNC_EN_N			BIT(5)
+#define AUD_FIFO_FUNC_EN_N			BIT(4)
+#define AUD_FUNC_EN_N				BIT(3)
+#define HDCP_FUNC_EN_N				BIT(2)
+#define SW_FUNC_EN_N				BIT(0)
+
+#define FUNC_EN_2				0x1C
+#define SSC_FUNC_EN_N				BIT(7)
+#define AUX_FUNC_EN_N				BIT(2)
+#define SERDES_FIFO_FUNC_EN_N			BIT(1)
+#define LS_CLK_DOMAIN_FUNC_EN_N			BIT(0)
+
+#define VIDEO_CTL_1				0x20
+#define VIDEO_EN				BIT(7)
+#define VIDEO_MUTE				BIT(6)
+
+#define VIDEO_CTL_2				0x24
+#define IN_D_RANGE_MASK				(0x1 << 7)
+#define IN_D_RANGE_SHIFT			(7)
+#define IN_D_RANGE_CEA				(0x1 << 7)
+#define IN_D_RANGE_VESA				(0x0 << 7)
+#define IN_BPC_MASK				(0x7 << 4)
+#define IN_BPC_SHIFT				(4)
+#define IN_BPC_12_BITS				(0x3 << 4)
+#define IN_BPC_10_BITS				(0x2 << 4)
+#define IN_BPC_8_BITS				(0x1 << 4)
+#define IN_BPC_6_BITS				(0x0 << 4)
+#define IN_COLOR_F_MASK				(0x3 << 0)
+#define IN_COLOR_F_SHIFT			(0)
+#define IN_COLOR_F_YCBCR444			(0x2 << 0)
+#define IN_COLOR_F_YCBCR422			(0x1 << 0)
+#define IN_COLOR_F_RGB				(0x0 << 0)
+
+#define VIDEO_CTL_3				0x28
+#define IN_YC_COEFFI_MASK			(0x1 << 7)
+#define IN_YC_COEFFI_SHIFT			(7)
+#define IN_YC_COEFFI_ITU709			(0x1 << 7)
+#define IN_YC_COEFFI_ITU601			(0x0 << 7)
+#define VID_CHK_UPDATE_TYPE_MASK		(0x1 << 4)
+#define VID_CHK_UPDATE_TYPE_SHIFT		(4)
+#define VID_CHK_UPDATE_TYPE_1			(0x1 << 4)
+#define VID_CHK_UPDATE_TYPE_0			(0x0 << 4)
+
+#define VIDEO_CTL_4				0x2c
+#define BIST_EN					(0x1 << 3)
+#define BIST_WH_64				(0x1 << 2)
+#define BIST_WH_32				(0x0 << 2)
+#define BIST_TYPE_COLR_BAR			(0x0 << 0)
+#define BIST_TYPE_GRAY_BAR			(0x1 << 0)
+#define BIST_TYPE_MOBILE_BAR			(0x2 << 0)
+
+#define VIDEO_CTL_8				0x3C
+#define VID_HRES_TH(x)				(((x) & 0xf) << 4)
+#define VID_VRES_TH(x)				(((x) & 0xf) << 0)
+
+#define VIDEO_CTL_10				0x44
+#define F_SEL					(0x1 << 4)
+#define INTERACE_SCAN_CFG			(0x1 << 2)
+#define VSYNC_POLARITY_CFG			(0x1 << 1)
+#define HSYNC_POLARITY_CFG			(0x1 << 0)
+
+#define TOTAL_LINE_CFG_L			0x48
+#define TOTAL_LINE_CFG_H			0x4c
+#define ATV_LINE_CFG_L			0x50
+#define ATV_LINE_CFG_H			0x54
+#define VF_PORCH_REG			0x58
+#define VSYNC_CFG_REG			0x5c
+#define VB_PORCH_REG			0x60
+#define TOTAL_PIXELL_REG		0x64
+#define TOTAL_PIXELH_REG		0x68
+#define ATV_PIXELL_REG			0x6c
+#define ATV_PIXELH_REG			0x70
+#define HF_PORCHL_REG			0x74
+#define HF_PORCHH_REG			0x78
+#define HSYNC_CFGL_REG			0x7c
+#define HSYNC_CFGH_REG			0x80
+#define HB_PORCHL_REG			0x84
+#define HB_PORCHH_REG			0x88
+
+
+#define SSC_REG					0x104
+#define TX_REG_COMMON				0x114
+#define DP_AUX					0x120
+#define DP_BIAS					0x124
+
+#define PLL_REG_1				0xfc
+#define REF_CLK_24M				(0x1 << 1)
+#define REF_CLK_27M				(0x0 << 1)
+
+#define PLL_REG_2				0x9e4
+#define PLL_REG_3				0x9e8
+#define PLL_REG_4				0x9ec
+#define PLL_REG_5				0xa00
+#define DP_PWRDN				0x12c
+#define PD_INC_BG				BIT(7)
+#define PD_EXP_BG				BIT(6)
+#define PD_AUX					BIT(5)
+#define PD_PLL					BIT(4)
+#define PD_CH3					BIT(3)
+#define PD_CH2					BIT(2)
+#define PD_CH1					BIT(1)
+#define PD_CH0					BIT(0)
+
+#define DP_RESERVE2				0x134
+
+#define LANE_MAP				0x35C
+#define LANE3_MAP_LOGIC_LANE_0			(0x0 << 6)
+#define LANE3_MAP_LOGIC_LANE_1			(0x1 << 6)
+#define LANE3_MAP_LOGIC_LANE_2			(0x2 << 6)
+#define LANE3_MAP_LOGIC_LANE_3			(0x3 << 6)
+#define LANE2_MAP_LOGIC_LANE_0			(0x0 << 4)
+#define LANE2_MAP_LOGIC_LANE_1			(0x1 << 4)
+#define LANE2_MAP_LOGIC_LANE_2			(0x2 << 4)
+#define LANE2_MAP_LOGIC_LANE_3			(0x3 << 4)
+#define LANE1_MAP_LOGIC_LANE_0			(0x0 << 2)
+#define LANE1_MAP_LOGIC_LANE_1			(0x1 << 2)
+#define LANE1_MAP_LOGIC_LANE_2			(0x2 << 2)
+#define LANE1_MAP_LOGIC_LANE_3			(0x3 << 2)
+#define LANE0_MAP_LOGIC_LANE_0			(0x0 << 0)
+#define LANE0_MAP_LOGIC_LANE_1			(0x1 << 0)
+#define LANE0_MAP_LOGIC_LANE_2			(0x2 << 0)
+#define LANE0_MAP_LOGIC_LANE_3			(0x3 << 0)
+
+#define ANALOG_CTL_2				0x374
+#define SEL_24M					(0x1 << 3)
+
+#define AUX_HW_RETRY_CTL			0x390
+
+#define INT_STA					0x3c0
+
+#define COMMON_INT_STA_1			0x3C4
+#define VSYNC_DET				BIT(7)
+#define PLL_LOCK_CHG				BIT(6)
+#define SPDIF_ERR				BIT(5)
+#define SPDIF_UNSTBL				BIT(4)
+#define VID_FORMAT_CHG				BIT(3)
+#define AUD_CLK_CHG				BIT(2)
+#define VID_CLK_CHG				BIT(1)
+#define SW_INT					BIT(0)
+
+#define COMMON_INT_STA_2			0x3C8
+#define ENC_EN_CHG				BIT(6)
+#define HW_BKSV_RDY				BIT(3)
+#define HW_SHA_DONE				BIT(2)
+#define HW_AUTH_STATE_CHG			BIT(1)
+#define HW_AUTH_DONE				BIT(0)
+
+#define COMMON_INT_STA_3			0x3CC
+#define AFIFO_UNDER				BIT(7)
+#define AFIFO_OVER				BIT(6)
+#define R0_CHK_FLAG				BIT(5)
+
+#define COMMON_INT_STA_4			0x3D0
+#define PSR_ACTIVE				BIT(7)
+#define PSR_INACTIVE				BIT(6)
+#define SPDIF_BI_PHASE_ERR			BIT(5)
+#define HOTPLUG_CHG				BIT(2)
+#define HPD_LOST				BIT(1)
+#define PLUG					BIT(0)
+
+#define DP_INT_STA				0x3DC
+#define INT_HPD					BIT(6)
+#define HW_LT_DONE				BIT(5)
+#define SINK_LOST				BIT(3)
+#define LINK_LOST				BIT(2)
+#define RPLY_RECEIV				BIT(1)
+#define AUX_ERR					BIT(0)
+
+#define COMMON_INT_MASK_1			0x3E0
+#define COMMON_INT_MASK_2			0x3E4
+#define COMMON_INT_MASK_3			0x3E8
+#define COMMON_INT_MASK_4			0x3EC
+#define DP_INT_STA_MASK				0x3F8
+
+#define INT_CTL					0x3FC
+#define SOFT_INT_CTRL				BIT(2)
+#define INT_POL					BIT(0)
+
+#define SYS_CTL_1				0x600
+#define DET_STA					BIT(2)
+#define FORCE_DET				BIT(1)
+#define DET_CTRL				BIT(0)
+
+#define SYS_CTL_2				0x604
+#define CHA_CRI(x)				(((x) & 0xf) << 4)
+#define CHA_STA					BIT(2)
+#define FORCE_CHA				BIT(1)
+#define CHA_CTRL				BIT(0)
+
+#define SYS_CTL_3				0x608
+#define HPD_STATUS				BIT(6)
+#define F_HPD					BIT(5)
+#define HPD_CTRL				BIT(4)
+#define HDCP_RDY				BIT(3)
+#define STRM_VALID				BIT(2)
+#define F_VALID					BIT(1)
+#define VALID_CTRL				BIT(0)
+
+#define SYS_CTL_4				0x60C
+#define FIX_M_AUD				BIT(4)
+#define ENHANCED				BIT(3)
+#define FIX_M_VID				BIT(2)
+#define M_VID_UPDATE_CTRL			BIT(0)
+
+#define PKT_SEND_CTL				0x640
+#define HDCP_CTL				0x648
+
+#define LINK_BW_SET				0x680
+#define LANE_CNT_SET				0x684
+
+#define TRAINING_PTN_SET			0x688
+#define SCRAMBLING_DISABLE			(0x1 << 5)
+#define SCRAMBLING_ENABLE			(0x0 << 5)
+#define LINK_QUAL_PATTERN_SET_MASK		(0x7 << 2)
+#define LINK_QUAL_PATTERN_SET_HBR2		(0x5 << 2)
+#define LINK_QUAL_PATTERN_SET_80BIT		(0x4 << 2)
+#define LINK_QUAL_PATTERN_SET_PRBS7		(0x3 << 2)
+#define LINK_QUAL_PATTERN_SET_D10_2		(0x1 << 2)
+#define LINK_QUAL_PATTERN_SET_DISABLE		(0x0 << 2)
+#define SW_TRAINING_PATTERN_SET_MASK		(0x3 << 0)
+#define SW_TRAINING_PATTERN_SET_PTN2		(0x2 << 0)
+#define SW_TRAINING_PATTERN_SET_PTN1		(0x1 << 0)
+#define SW_TRAINING_PATTERN_SET_DISABLE		(0x0 << 0)
+
+#define LN0_LINK_TRAINING_CTL			0x68C
+#define LN1_LINK_TRAINING_CTL			0x690
+#define LN2_LINK_TRAINING_CTL			0x694
+#define LN3_LINK_TRAINING_CTL			0x698
+
+#define HW_LT_CTL				0x6a0
+#define HW_LT_ERR_CODE_MASK			0x70
+#define HW_LT_EN				BIT(0)
+
+#define DEBUG_CTL				0x6C0
+#define PLL_LOCK				BIT(4)
+#define F_PLL_LOCK				BIT(3)
+#define PLL_LOCK_CTRL				BIT(2)
+#define POLL_EN					BIT(1)
+#define PN_INV					BIT(0)
+
+#define HPD_DEGLITCH_L				0x6C4
+#define HPD_DEGLITCH_H				0x6C8
+#define LINK_DEBUG_CTL				0x6E0
+
+#define M_VID_0					0x700
+#define M_VID_1					0x704
+#define M_VID_2					0x708
+#define N_VID_0					0x70C
+#define N_VID_1					0x710
+#define N_VID_2					0x714
+
+#define VIDEO_FIFO_THRD				0x730
+#define AUDIO_MARGIN				0x73C
+
+#define M_VID_GEN_FILTER_TH			0x764
+#define M_AUD_GEN_FILTER_TH			0x778
+
+#define AUX_CH_STA				0x780
+#define AUX_BUSY				(0x1 << 4)
+#define AUX_STATUS_MASK				(0xf << 0)
+
+#define AUX_CH_DEFER_CTL			0x788
+#define DEFER_CTRL_EN				(0x1 << 7)
+#define DEFER_COUNT(x)				(((x) & 0x7f) << 0)
+
+#define AUX_RX_COMM_I2C_DEFER			(0x2 << 2)
+#define AUX_RX_COMM_AUX_DEFER			(0x2 << 0)
+
+#define AUX_RX_COMM				0x78C
+#define BUFFER_DATA_CTL				0x790
+#define BUF_CLR					(0x1 << 7)
+#define BUF_HAVE_DATA				(0x1 << 4)
+#define BUF_DATA_COUNT(x)			(((x) & 0xf) << 0)
+
+#define AUX_CH_CTL_1				0x794
+#define AUX_LENGTH(x)				(((x - 1) & 0xf) << 4)
+#define AUX_TX_COMM_MASK			(0xf << 0)
+#define AUX_TX_COMM_DP_TRANSACTION		(0x1 << 3)
+#define AUX_TX_COMM_I2C_TRANSACTION		(0x0 << 3)
+#define AUX_TX_COMM_MOT				(0x1 << 2)
+#define AUX_TX_COMM_WRITE			(0x0 << 0)
+#define AUX_TX_COMM_READ			(0x1 << 0)
+
+#define DP_AUX_ADDR_7_0				0x798
+#define DP_AUX_ADDR_15_8			0x79C
+#define DP_AUX_ADDR_19_16			0x7A0
+
+#define AUX_CH_CTL_2				0x7A4
+#define PD_AUX_IDLE				BIT(3)
+#define ADDR_ONLY				BIT(1)
+#define AUX_EN					BIT(0)
+
+#define BUF_DATA_0				0x7C0
+
+#define SOC_GENERAL_CTL				0x800
+
+/* TX_SW_RESET */
+#define RST_DP_TX				BIT(0)
+
+/* ANALOG_CTL_1 */
+#define TX_TERMINAL_CTRL_50_OHM			BIT(4)
+
+/* ANALOG_CTL_3 */
+#define DRIVE_DVDD_BIT_1_0625V			(0x4 << 5)
+#define VCO_BIT_600_MICRO			(0x5 << 0)
+
+/* PLL_FILTER_CTL_1 */
+#define PD_RING_OSC				(0x1 << 6)
+#define AUX_TERMINAL_CTRL_37_5_OHM		(0x0 << 4)
+#define AUX_TERMINAL_CTRL_45_OHM		(0x1 << 4)
+#define AUX_TERMINAL_CTRL_50_OHM		(0x2 << 4)
+#define AUX_TERMINAL_CTRL_65_OHM		(0x3 << 4)
+#define TX_CUR1_2X				(0x1 << 2)
+#define TX_CUR_16_MA				(0x3 << 0)
+
+/* TX_AMP_TUNING_CTL */
+#define CH3_AMP_SHIFT				(24)
+#define CH3_AMP_400_MV				(0x0 << 24)
+#define CH2_AMP_SHIFT				(16)
+#define CH2_AMP_400_MV				(0x0 << 16)
+#define CH1_AMP_SHIFT				(8)
+#define CH1_AMP_400_MV				(0x0 << 8)
+#define CH0_AMP_SHIFT				(0)
+#define CH0_AMP_400_MV				(0x0 << 0)
+
+/* AUX_HW_RETRY_CTL */
+#define AUX_BIT_PERIOD_EXPECTED_DELAY(x)	(((x) & 0x7) << 8)
+#define AUX_HW_RETRY_INTERVAL_MASK		(0x3 << 3)
+#define AUX_HW_RETRY_INTERVAL_600_MICROSECONDS	(0x0 << 3)
+#define AUX_HW_RETRY_INTERVAL_800_MICROSECONDS	(0x1 << 3)
+#define AUX_HW_RETRY_INTERVAL_1000_MICROSECONDS	(0x2 << 3)
+#define AUX_HW_RETRY_INTERVAL_1800_MICROSECONDS	(0x3 << 3)
+#define AUX_HW_RETRY_COUNT_SEL(x)		(((x) & 0x7) << 0)
+
+/* LN0_LINK_TRAINING_CTL */
+#define PRE_EMPHASIS_SET_MASK			(0x3 << 3)
+#define PRE_EMPHASIS_SET_SHIFT			(3)
+
+/* PLL_CTL */
+#define DP_PLL_PD				(0x1 << 7)
+#define DP_PLL_RESET				(0x1 << 6)
+#define DP_PLL_LOOP_BIT_DEFAULT			(0x1 << 4)
+#define DP_PLL_REF_BIT_1_1250V			(0x5 << 0)
+#define DP_PLL_REF_BIT_1_2500V			(0x7 << 0)
+
+/* PHY_TEST */
+#define MACRO_RST				BIT(5)
+#define CH1_TEST				BIT(1)
+#define CH0_TEST				BIT(0)
+
+#define AUX_ADDR_7_0(x)			(((x) >> 0) & 0xff)
+#define AUX_ADDR_15_8(x)		(((x) >> 8) & 0xff)
+#define AUX_ADDR_19_16(x)		(((x) >> 16) & 0x0f)
+
+#endif
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index 59187aa..4f1205f 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -564,6 +564,12 @@ static int rockchip_drm_init(void)
 		goto out_lvds;
 #endif
 
+#ifdef CONFIG_RK3288_DP
+	ret = platform_driver_register(&rk3288_edp_driver);
+	if (ret)
+		goto out_edp;
+#endif
+
 	rockchip_drm_pdev = platform_device_register_simple("rockchip-drm", -1,
 							    NULL, 0);
 	if (IS_ERR(rockchip_drm_pdev)) {
@@ -580,6 +586,10 @@ static int rockchip_drm_init(void)
 out_drm_driver:
 	platform_device_unregister(rockchip_drm_pdev);
 out_drm_pdev:
+#ifdef CONFIG_RK3288_DP
+	platform_driver_unregister(&rk3288_edp_driver);
+out_edp:
+#endif
 #ifdef CONFIG_RK3288_LVDS
 	platform_driver_unregister(&rk3288_lvds_driver);
 out_lvds:
@@ -596,6 +606,9 @@ static void rockchip_drm_exit(void)
 {
 	platform_device_unregister(rockchip_drm_pdev);
 	platform_driver_unregister(&rockchip_drm_platform_driver);
+#ifdef CONFIG_RK3288_DP
+	platform_driver_unregister(&rk3288_edp_driver);
+#endif
 #ifdef CONFIG_RK3288_LVDS
 	platform_driver_unregister(&rk3288_lvds_driver);
 #endif
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index d28f4dc..565276e 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -128,4 +128,7 @@ extern struct platform_driver rockchip_lcdc_platform_driver;
 #ifdef CONFIG_RK3288_LVDS
 extern struct platform_driver rk3288_lvds_driver;
 #endif
+#ifdef CONFIG_RK3288_DP
+extern struct platform_driver rk3288_edp_driver;
+#endif
 #endif /* _ROCKCHIP_DRM_DRV_H_ */
-- 
1.7.9.5



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

* [PATCH 9/9] drm: add Rockchip Soc rk3288 edp connector
@ 2014-08-04  4:57   ` mark yao
  0 siblings, 0 replies; 38+ messages in thread
From: mark yao @ 2014-08-04  4:57 UTC (permalink / raw)
  To: heiko-4mtYJXux2i+zQB+pC5nmwQ, Rob Clark, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, Randy Dunlap,
	David Airlie, Grant Likely, Greg Kroah-Hartman, John Stultz,
	Rom Lemarchand
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-api-u79uwXL29TY76Z2rM5mHXA, olof-nZhT3qVonbNeoWH0uzbU5w,
	djkurtz-F7+t8E8rja9g9hUCZPvPmw, xjq-TNX95d0MmH7DzftRWevZcw,
	kfx-TNX95d0MmH7DzftRWevZcw, cym-TNX95d0MmH7DzftRWevZcw,
	cf-TNX95d0MmH7DzftRWevZcw, zyw-TNX95d0MmH7DzftRWevZcw,
	zwl-TNX95d0MmH7DzftRWevZcw, xxm-TNX95d0MmH7DzftRWevZcw,
	huangtao-TNX95d0MmH7DzftRWevZcw,
	kever.yang-TNX95d0MmH7DzftRWevZcw,
	zhangqing-TNX95d0MmH7DzftRWevZcw, yxj-TNX95d0MmH7DzftRWevZcw,
	wxt-TNX95d0MmH7DzftRWevZcw, xw-TNX95d0MmH7DzftRWevZcw, mark yao

Signed-off-by: mark yao <yzq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
 drivers/gpu/drm/rockchip/connector/Kconfig         |    9 +
 drivers/gpu/drm/rockchip/connector/Makefile        |    1 +
 .../gpu/drm/rockchip/connector/rk3288_dp_core.c    |  586 ++++++++++
 .../gpu/drm/rockchip/connector/rk3288_dp_core.h    |  355 ++++++
 drivers/gpu/drm/rockchip/connector/rk3288_dp_reg.c | 1192 ++++++++++++++++++++
 drivers/gpu/drm/rockchip/connector/rk3288_dp_reg.h |  378 +++++++
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c        |   13 +
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h        |    3 +
 8 files changed, 2537 insertions(+)
 create mode 100644 drivers/gpu/drm/rockchip/connector/rk3288_dp_core.c
 create mode 100644 drivers/gpu/drm/rockchip/connector/rk3288_dp_core.h
 create mode 100644 drivers/gpu/drm/rockchip/connector/rk3288_dp_reg.c
 create mode 100644 drivers/gpu/drm/rockchip/connector/rk3288_dp_reg.h

diff --git a/drivers/gpu/drm/rockchip/connector/Kconfig b/drivers/gpu/drm/rockchip/connector/Kconfig
index 248942f..caffc5b 100644
--- a/drivers/gpu/drm/rockchip/connector/Kconfig
+++ b/drivers/gpu/drm/rockchip/connector/Kconfig
@@ -6,3 +6,12 @@ config RK3288_LVDS
 	  rk3288 lvds transmitter support ttl rgb, lvds and dual lvds
 	  mode, dual lvds mode is support for the plane which need dual
 	  lvds channels.
+
+config RK3288_DP
+	bool "RK3288 edp connector support"
+	depends on DRM_ROCKCHIP_CONNECTOR
+	help
+	  Choose this option if you have a rk32xx eDP connector.
+	  Rockchip rk3288 SoC has eDP TX Controller can be used.
+	  If you have an Embedded DisplayPort Panel, say Y to enable its
+	  driver.
diff --git a/drivers/gpu/drm/rockchip/connector/Makefile b/drivers/gpu/drm/rockchip/connector/Makefile
index dcfbdef..4be77ea 100644
--- a/drivers/gpu/drm/rockchip/connector/Makefile
+++ b/drivers/gpu/drm/rockchip/connector/Makefile
@@ -2,3 +2,4 @@
 # Makefile for display connector like lvds edp mipi
 #
 obj-$(CONFIG_RK3288_LVDS)	+= rk3288_lvds.o
+obj-$(CONFIG_RK3288_DP)		+= rk3288_dp_core.o rk3288_dp_reg.o
diff --git a/drivers/gpu/drm/rockchip/connector/rk3288_dp_core.c b/drivers/gpu/drm/rockchip/connector/rk3288_dp_core.c
new file mode 100644
index 0000000..3756383
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/connector/rk3288_dp_core.c
@@ -0,0 +1,586 @@
+/*
+* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+* Author:
+*      yxj <yxj-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+*      cym <cym-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+*
+* based on exynos_dp_core.c
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/of.h>
+
+#include "rk3288_dp_core.h"
+
+static int rk3288_edp_clk_enable(struct rk3288_edp *edp)
+{
+	int ret = 0;
+
+	if (!edp->clk_on) {
+		ret = clk_prepare_enable(edp->pclk);
+		if (ret < 0) {
+			dev_err(edp->dev, "cannot enable edp pclk %d\n", ret);
+			goto err_pclk;
+		}
+
+		ret = clk_prepare_enable(edp->clk_edp);
+		if (ret < 0) {
+			dev_err(edp->dev, "cannot enable clk_edp %d\n", ret);
+			goto err_clk_edp;
+		}
+
+		ret = clk_set_parent(edp->clk_24m, edp->clk_24m_parent);
+		if (ret < 0) {
+			dev_err(edp->dev, "cannot set edp clk_24m parent %d\n",
+				ret);
+			goto err_clk_24m;
+		}
+
+		ret = clk_prepare_enable(edp->clk_24m);
+		if (ret < 0) {
+			dev_err(edp->dev, "cannot enable edp clk_24m %d\n",
+				ret);
+			goto err_clk_24m;
+		}
+
+		edp->clk_on = true;
+	}
+
+	return 0;
+
+err_clk_24m:
+	clk_disable_unprepare(edp->clk_edp);
+err_clk_edp:
+	clk_disable_unprepare(edp->pclk);
+err_pclk:
+	edp->clk_on = false;
+
+	return ret;
+}
+
+static int rk3288_edp_clk_disable(struct rk3288_edp *edp)
+{
+	if (edp->clk_on) {
+		clk_disable_unprepare(edp->pclk);
+		clk_disable_unprepare(edp->clk_edp);
+		clk_disable_unprepare(edp->clk_24m);
+		edp->clk_on = false;
+	}
+
+	return 0;
+}
+
+static int rk3288_edp_pre_init(struct rk3288_edp *edp)
+{
+	u32 val;
+	int ret = 0;
+
+	val = GRF_EDP_REF_CLK_SEL_INTER | (GRF_EDP_REF_CLK_SEL_INTER << 16);
+	ret = regmap_write(edp->grf, edp->soc_data->grf_soc_con12, val);
+	if (ret != 0) {
+		dev_err(edp->dev, "Could not write to GRF: %d\n", ret);
+		return ret;
+	}
+
+	reset_control_assert(edp->rst);
+	usleep_range(10, 20);
+	reset_control_deassert(edp->rst);
+
+	return 0;
+}
+
+static int rk3288_edp_init_edp(struct rk3288_edp *edp)
+{
+	int lcdc_id = 1;
+	u32 val = 0;
+	int ret = 0;
+
+	/* select lcdc */
+	if (lcdc_id == 1)
+		val = EDP_SEL_VOP_LIT | (EDP_SEL_VOP_LIT << 16);
+	else
+		val = EDP_SEL_VOP_LIT << 16;
+	ret = regmap_write(edp->grf, edp->soc_data->grf_soc_con6, val);
+	if (ret != 0) {
+		dev_err(edp->dev, "Could not write to GRF: %d\n", ret);
+		return ret;
+	}
+
+	rk3288_edp_reset(edp);
+	rk3288_edp_init_refclk(edp);
+	rk3288_edp_init_interrupt(edp);
+	rk3288_edp_enable_sw_function(edp);
+	rk3288_edp_init_analog_func(edp);
+	rk3288_edp_init_hpd(edp);
+	rk3288_edp_init_aux(edp);
+
+	return 0;
+}
+
+static int rk3288_edp_get_max_rx_bandwidth(
+					struct rk3288_edp *edp,
+					u8 *bandwidth)
+{
+	u8 data;
+	int retval = 0;
+
+	/*
+	 * For DP rev.1.1, Maximum link rate of Main Link lanes
+	 * 0x06 = 1.62 Gbps, 0x0a = 2.7 Gbps
+	 */
+	retval = rk3288_edp_read_byte_from_dpcd(
+			edp, DPCD_ADDR_MAX_LINK_RATE, &data);
+	if (retval < 0)
+		*bandwidth = 0;
+	else
+		*bandwidth = data;
+
+	return retval;
+}
+
+static int rk3288_edp_get_max_rx_lane_count(struct rk3288_edp *edp,
+					    u8 *lane_count)
+{
+	u8 data;
+	int retval;
+
+	/*
+	 * For DP rev.1.1, Maximum number of Main Link lanes
+	 * 0x01 = 1 lane, 0x02 = 2 lanes, 0x04 = 4 lanes
+	 */
+	retval = rk3288_edp_read_byte_from_dpcd(
+			edp, DPCD_ADDR_MAX_LANE_COUNT, &data);
+	if (retval < 0)
+		*lane_count = 0;
+	else
+		*lane_count = DPCD_MAX_LANE_COUNT(data);
+
+	return retval;
+}
+
+static int rk3288_edp_init_training(struct rk3288_edp *edp)
+{
+	int retval;
+
+	/*
+	 * MACRO_RST must be applied after the PLL_LOCK to avoid
+	 * the DP inter pair skew issue for at least 10 us
+	 */
+	rk3288_edp_reset_macro(edp);
+
+	retval = rk3288_edp_get_max_rx_bandwidth(
+				edp, &edp->link_train.link_rate);
+	retval = rk3288_edp_get_max_rx_lane_count(
+				edp, &edp->link_train.lane_count);
+	dev_dbg(edp->dev, "max link rate:%d.%dGps max number of lanes:%d\n",
+		edp->link_train.link_rate * 27/100,
+		edp->link_train.link_rate*27%100,
+		edp->link_train.lane_count);
+
+	if ((edp->link_train.link_rate != LINK_RATE_1_62GBPS) &&
+	    (edp->link_train.link_rate != LINK_RATE_2_70GBPS)) {
+		dev_warn(edp->dev, "Rx Max Link Rate is abnormal :%x !\n"
+			 "use default link rate:%d.%dGps\n",
+			 edp->link_train.link_rate,
+			 edp->video_info.link_rate*27/100,
+			 edp->video_info.link_rate*27%100);
+			 edp->link_train.link_rate = edp->video_info.link_rate;
+	}
+
+	if (edp->link_train.lane_count == 0) {
+		dev_err(edp->dev, "Rx Max Lane count is abnormal :%x !\n"
+			"use default lanes:%d\n",
+			edp->link_train.lane_count,
+			edp->video_info.lane_count);
+		edp->link_train.lane_count = edp->video_info.lane_count;
+	}
+
+	rk3288_edp_analog_power_ctr(edp, 1);
+
+	return 0;
+}
+
+static int rk3288_edp_hw_link_training(struct rk3288_edp *edp)
+{
+	u32 cnt = 50;
+	u32 val;
+
+	/* Set link rate and count as you want to establish*/
+	rk3288_edp_set_link_bandwidth(edp, edp->link_train.link_rate);
+	rk3288_edp_set_lane_count(edp, edp->link_train.lane_count);
+	rk3288_edp_hw_link_training_en(edp);
+	val = rk3288_edp_wait_hw_lt_done(edp);
+	while (val) {
+		if (cnt-- <= 0) {
+			dev_err(edp->dev, "hw lt timeout");
+			return -ETIMEDOUT;
+		}
+		mdelay(1);
+		val = rk3288_edp_wait_hw_lt_done(edp);
+	}
+
+	val = rk3288_edp_get_hw_lt_status(edp);
+	if (val)
+		dev_err(edp->dev, "hw lt err:%d\n", val);
+
+	return val;
+}
+
+static int rk3288_edp_set_link_train(struct rk3288_edp *edp)
+{
+	int retval;
+
+	rk3288_edp_init_training(edp);
+
+	retval = rk3288_edp_hw_link_training(edp);
+	if (retval < 0)
+		dev_err(edp->dev, "DP hw LT failed!\n");
+
+	return retval;
+}
+
+static int rk3288_edp_config_video(struct rk3288_edp *edp,
+				   struct video_info *video_info)
+{
+	int retval = 0;
+	int timeout_loop = 0;
+	int done_count = 0;
+
+	rk3288_edp_config_video_slave_mode(edp, video_info);
+
+	rk3288_edp_set_video_color_format(edp, video_info->color_depth,
+					  video_info->color_space,
+					  video_info->dynamic_range,
+					  video_info->ycbcr_coeff);
+
+	if (rk3288_edp_get_pll_lock_status(edp) == DP_PLL_UNLOCKED) {
+		dev_err(edp->dev, "PLL is not locked yet.\n");
+		return -EINVAL;
+	}
+
+	for (;;) {
+		timeout_loop++;
+		if (rk3288_edp_is_slave_video_stream_clock_on(edp) == 0)
+			break;
+
+		if (DP_TIMEOUT_LOOP_CNT < timeout_loop) {
+			dev_err(edp->dev, "Timeout of video streamclk ok\n");
+			return -ETIMEDOUT;
+		}
+
+		udelay(1);
+	}
+
+	/* Set to use the register calculated M/N video */
+	rk3288_edp_set_video_cr_mn(edp, CALCULATED_M, 0, 0);
+
+	/* Disable video mute */
+	rk3288_edp_enable_video_mute(edp, 0);
+
+	/* Configure video slave mode */
+	rk3288_edp_enable_video_master(edp, 0);
+
+	/* Enable video */
+	rk3288_edp_start_video(edp);
+
+	timeout_loop = 0;
+
+	for (;;) {
+		timeout_loop++;
+		if (rk3288_edp_is_video_stream_on(edp) == 0) {
+			done_count++;
+			if (done_count > 10)
+				break;
+		} else if (done_count) {
+			done_count = 0;
+		}
+		if (DP_TIMEOUT_LOOP_CNT < timeout_loop) {
+			dev_err(edp->dev, "Timeout of video streamclk ok\n");
+			return -ETIMEDOUT;
+		}
+
+		mdelay(1);
+	}
+
+	if (retval != 0)
+		dev_err(edp->dev, "Video stream is not detected!\n");
+
+	return retval;
+}
+
+static irqreturn_t rk3288_edp_isr(int irq, void *arg)
+{
+	struct rk3288_edp *edp = arg;
+	enum dp_irq_type irq_type;
+
+	irq_type = rk3288_edp_get_irq_type(edp);
+	switch (irq_type) {
+	case DP_IRQ_TYPE_HP_CABLE_IN:
+		dev_dbg(edp->dev, "Received irq - cable in\n");
+		rk3288_edp_clear_hotplug_interrupts(edp);
+		break;
+	case DP_IRQ_TYPE_HP_CABLE_OUT:
+		dev_dbg(edp->dev, "Received irq - cable out\n");
+		rk3288_edp_clear_hotplug_interrupts(edp);
+		break;
+	case DP_IRQ_TYPE_HP_CHANGE:
+		/*
+		 * We get these change notifications once in a while, but there
+		 * is nothing we can do with them. Just ignore it for now and
+		 * only handle cable changes.
+		 */
+		dev_dbg(edp->dev, "Received irq - hotplug change; ignoring.\n");
+		rk3288_edp_clear_hotplug_interrupts(edp);
+		break;
+	default:
+		dev_err(edp->dev, "Received irq - unknown type[%x]!\n",
+			irq_type);
+		rk3288_edp_clear_hotplug_interrupts(edp);
+		break;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void rk3288_edp_enable(struct rockchip_connector *conn)
+{
+	struct rk3288_edp *edp = conn->priv;
+	int ret = 0;
+
+	ret = rk3288_edp_clk_enable(edp);
+	if (ret < 0) {
+		dev_err(edp->dev, "cannot enable edp clk %d\n", ret);
+		return;
+	}
+
+	ret = rk3288_edp_pre_init(edp);
+	if (ret < 0) {
+		dev_err(edp->dev, "edp pre init fail %d\n", ret);
+		return;
+	}
+
+	ret = rk3288_edp_init_edp(edp);
+	if (ret < 0) {
+		dev_err(edp->dev, "edp init fail %d\n", ret);
+		return;
+	}
+
+	enable_irq(edp->irq);
+
+	ret = rk3288_edp_set_link_train(edp);
+	if (ret)
+		dev_err(edp->dev, "link train failed!\n");
+	else
+		dev_dbg(edp->dev, "link training success.\n");
+
+	rk3288_edp_set_lane_count(edp, edp->link_train.lane_count);
+	rk3288_edp_set_link_bandwidth(edp, edp->link_train.link_rate);
+	rk3288_edp_init_video(edp);
+
+	ret = rk3288_edp_config_video(edp, &edp->video_info);
+	if (ret)
+		dev_err(edp->dev, "unable to config video\n");
+}
+
+static void  rk3288_edp_disable(struct rockchip_connector *conn)
+{
+	struct rk3288_edp *edp = conn->priv;
+
+	disable_irq(edp->irq);
+	rk3288_edp_reset(edp);
+	rk3288_edp_analog_power_ctr(edp, 0);
+	rk3288_edp_clk_disable(edp);
+}
+
+static int rk3288_edp_setmode(struct rockchip_connector *conn,
+			      struct drm_display_mode *mode)
+{
+	struct rk3288_edp *edp = conn->priv;
+
+	memcpy(&edp->mode, mode, sizeof(*mode));
+
+	return 0;
+}
+
+static struct rockchip_connector edp_conn = {
+	.enable = rk3288_edp_enable,
+	.disable = rk3288_edp_disable,
+	.setmode = rk3288_edp_setmode,
+};
+
+static struct rk3288_edp_soc_data soc_data[2] = {
+	{.grf_soc_con6 = 0x025c,
+	 .grf_soc_con12 = 0x0274},
+	{.grf_soc_con6 = -1,
+	 .grf_soc_con12 = -1},
+};
+
+static const struct of_device_id rk3288_edp_dt_ids[] = {
+	{.compatible = "rockchip,rk3288-edp",
+	 .data = (void *)&soc_data[0]},
+	{}
+};
+MODULE_DEVICE_TABLE(of, rk3288_edp_dt_ids);
+
+static int rk3288_edp_probe(struct platform_device *pdev)
+{
+	struct rk3288_edp *edp;
+	struct resource *res;
+	struct device_node *np = pdev->dev.of_node;
+	const struct of_device_id *match;
+	int ret = 0;
+
+	if (!np) {
+		dev_err(&pdev->dev, "Missing device tree node.\n");
+		return -EINVAL;
+	}
+
+	edp = devm_kzalloc(&pdev->dev, sizeof(struct rk3288_edp), GFP_KERNEL);
+	if (!edp) {
+		dev_err(&pdev->dev, "no memory for state\n");
+		return -ENOMEM;
+	}
+
+	match = of_match_node(rk3288_edp_dt_ids, np);
+	edp->soc_data = (struct rk3288_edp_soc_data *)match->data;
+	/*
+	 * The control bit is located in the GRF register space.
+	 */
+	if (edp->soc_data->grf_soc_con6 >= 0) {
+		edp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+		if (IS_ERR(edp->grf)) {
+			dev_err(&pdev->dev,
+				"rk3288-edp needs rockchip,grf property\n");
+			return PTR_ERR(edp->grf);
+		}
+	}
+
+	edp->dev = &pdev->dev;
+	edp->video_info.h_sync_polarity	= 0;
+	edp->video_info.v_sync_polarity	= 0;
+	edp->video_info.interlaced	= 0;
+	edp->video_info.color_space	= CS_RGB;
+	edp->video_info.dynamic_range	= VESA;
+	edp->video_info.ycbcr_coeff	= COLOR_YCBCR601;
+	edp->video_info.color_depth	= COLOR_8;
+
+	edp->video_info.link_rate	= LINK_RATE_1_62GBPS;
+	edp->video_info.lane_count	= LANE_CNT4;
+	edp_conn.type = ROCKCHIP_DISPLAY_TYPE_EDP;
+	edp_conn.priv = edp;
+	edp_conn.dev = &pdev->dev;
+
+	edp->base = rockchip_connector_register(&edp_conn);
+	if (!edp->base) {
+		dev_err(&pdev->dev, "connector register fail\n");
+		return -EINVAL;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	edp->regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(edp->regs)) {
+		dev_err(&pdev->dev, "ioremap reg failed\n");
+		return PTR_ERR(edp->regs);
+	}
+
+	edp->clk_edp = devm_clk_get(&pdev->dev, "clk_edp");
+	if (IS_ERR(edp->clk_edp)) {
+		dev_err(&pdev->dev, "cannot get clk_edp\n");
+		return PTR_ERR(edp->clk_edp);
+	}
+
+	edp->clk_24m_parent = devm_clk_get(&pdev->dev, "clk_edp_24m_parent");
+	if (IS_ERR(edp->clk_24m_parent)) {
+		dev_err(&pdev->dev, "cannot get clk_edp_24m_parent\n");
+		return PTR_ERR(edp->clk_24m_parent);
+	}
+
+	edp->clk_24m = devm_clk_get(&pdev->dev, "clk_edp_24m");
+	if (IS_ERR(edp->clk_24m)) {
+		dev_err(&pdev->dev, "cannot get clk_edp_24m\n");
+		return PTR_ERR(edp->clk_24m);
+	}
+
+	edp->pclk = devm_clk_get(&pdev->dev, "pclk_edp");
+	if (IS_ERR(edp->pclk)) {
+		dev_err(&pdev->dev, "cannot get pclk\n");
+		return PTR_ERR(edp->pclk);
+	}
+
+	edp->rst = devm_reset_control_get(&pdev->dev, "edp");
+	if (IS_ERR(edp->rst)) {
+		dev_err(&pdev->dev, "failed to get reset\n");
+		return PTR_ERR(edp->rst);
+	}
+
+	ret = rk3288_edp_clk_enable(edp);
+	if (ret < 0) {
+		dev_err(edp->dev, "cannot enable edp clk %d\n", ret);
+		return ret;
+	}
+
+	ret = rk3288_edp_pre_init(edp);
+	if (ret < 0) {
+		dev_err(edp->dev, "failed to pre init %d\n", ret);
+		return ret;
+	}
+
+	edp->irq = platform_get_irq(pdev, 0);
+	if (edp->irq < 0) {
+		dev_err(&pdev->dev, "cannot find IRQ\n");
+		return edp->irq;
+	}
+
+	ret = devm_request_irq(&pdev->dev, edp->irq, rk3288_edp_isr, 0,
+			       dev_name(&pdev->dev), edp);
+	if (ret) {
+		dev_err(&pdev->dev, "cannot claim IRQ %d\n", edp->irq);
+		return ret;
+	}
+
+	disable_irq_nosync(edp->irq);
+
+	edp->standby = true;
+
+	platform_set_drvdata(pdev, edp);
+	dev_set_name(edp->dev, "rk3288-edp");
+
+	dev_info(&pdev->dev, "rk3288 edp driver probe success\n");
+
+	return 0;
+}
+
+static int rk3288_edp_remove(struct platform_device *pdev)
+{
+	rk3288_edp_disable(&edp_conn);
+
+	return 0;
+}
+
+struct platform_driver rk3288_edp_driver = {
+	.probe = rk3288_edp_probe,
+	.remove = rk3288_edp_remove,
+	.driver = {
+		   .name = "rk3288-edp",
+		   .owner = THIS_MODULE,
+		   .of_match_table = of_match_ptr(rk3288_edp_dt_ids),
+	},
+};
diff --git a/drivers/gpu/drm/rockchip/connector/rk3288_dp_core.h b/drivers/gpu/drm/rockchip/connector/rk3288_dp_core.h
new file mode 100644
index 0000000..d71df70
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/connector/rk3288_dp_core.h
@@ -0,0 +1,355 @@
+/*
+* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+* Author:
+*      yxj <yxj-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+*      cym <cym-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+*
+* based on exynos_dp_core.h
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*/
+
+#ifndef __RK3288_DP_H
+#define __RK3288_DP_H
+#include "../rockchip_drm_connector.h"
+
+#define DP_TIMEOUT_LOOP_CNT 100
+#define MAX_CR_LOOP 5
+#define MAX_EQ_LOOP 5
+
+#define GRF_EDP_REF_CLK_SEL_INTER		(1 << 4)
+#define GRF_EDP_HDCP_EN				(1 << 15)
+#define GRF_EDP_BIST_EN				(1 << 14)
+#define GRF_EDP_MEM_CTL_BY_EDP			(1 << 13)
+#define GRF_EDP_SECURE_EN			(1 << 3)
+#define EDP_SEL_VOP_LIT				(1 << 5)
+
+enum dp_irq_type {
+	DP_IRQ_TYPE_HP_CABLE_IN,
+	DP_IRQ_TYPE_HP_CABLE_OUT,
+	DP_IRQ_TYPE_HP_CHANGE,
+	DP_IRQ_TYPE_UNKNOWN,
+};
+
+enum color_coefficient {
+	COLOR_YCBCR601,
+	COLOR_YCBCR709
+};
+
+enum dynamic_range {
+	VESA,
+	CEA
+};
+
+enum pll_status {
+	DP_PLL_UNLOCKED,
+	DP_PLL_LOCKED
+};
+
+enum clock_recovery_m_value_type {
+	CALCULATED_M,
+	REGISTER_M
+};
+
+enum video_timing_recognition_type {
+	VIDEO_TIMING_FROM_CAPTURE,
+	VIDEO_TIMING_FROM_REGISTER
+};
+
+enum pattern_set {
+	PRBS7,
+	D10_2,
+	TRAINING_PTN1,
+	TRAINING_PTN2,
+	DP_NONE
+};
+
+enum color_space {
+	CS_RGB,
+	CS_YCBCR422,
+	CS_YCBCR444
+};
+
+enum color_depth {
+	COLOR_6,
+	COLOR_8,
+	COLOR_10,
+	COLOR_12
+};
+
+enum link_rate_type {
+	LINK_RATE_1_62GBPS = 0x06,
+	LINK_RATE_2_70GBPS = 0x0a
+};
+
+enum link_lane_count_type {
+	LANE_CNT1 = 1,
+	LANE_CNT2 = 2,
+	LANE_CNT4 = 4
+};
+
+enum link_training_state {
+	LT_START,
+	LT_CLK_RECOVERY,
+	LT_EQ_TRAINING,
+	FINISHED,
+	FAILED
+};
+
+enum voltage_swing_level {
+	VOLTAGE_LEVEL_0,
+	VOLTAGE_LEVEL_1,
+	VOLTAGE_LEVEL_2,
+	VOLTAGE_LEVEL_3,
+};
+
+enum pre_emphasis_level {
+	PRE_EMPHASIS_LEVEL_0,
+	PRE_EMPHASIS_LEVEL_1,
+	PRE_EMPHASIS_LEVEL_2,
+	PRE_EMPHASIS_LEVEL_3,
+};
+
+enum analog_power_block {
+	AUX_BLOCK,
+	CH0_BLOCK,
+	CH1_BLOCK,
+	CH2_BLOCK,
+	CH3_BLOCK,
+	ANALOG_TOTAL,
+	POWER_ALL
+};
+
+struct video_info {
+	char *name;
+
+	bool h_sync_polarity;
+	bool v_sync_polarity;
+	bool interlaced;
+
+	enum color_space color_space;
+	enum dynamic_range dynamic_range;
+	enum color_coefficient ycbcr_coeff;
+	enum color_depth color_depth;
+
+	enum link_rate_type link_rate;
+	enum link_lane_count_type lane_count;
+};
+
+struct link_train {
+	int eq_loop;
+	int cr_loop[4];
+
+	u8 link_rate;
+	u8 lane_count;
+	u8 training_lane[4];
+
+	enum link_training_state lt_state;
+};
+
+/*
+ * @grf_offset: offset inside the grf regmap for setting the rk3288 lvds
+ */
+struct rk3288_edp_soc_data {
+	int grf_soc_con6;
+	int grf_soc_con12;
+};
+
+struct rk3288_edp {
+	void *base;
+	struct drm_display_mode mode;
+	struct device *dev;
+	void __iomem *regs;
+	struct regmap *grf;
+	struct rk3288_edp_soc_data *soc_data;
+	unsigned int irq;
+	/* clk for edp controller */
+	struct clk *clk_edp;
+	/* clk for edp phy parent */
+	struct clk *clk_24m_parent;
+	/* clk for edp phy */
+	struct clk *clk_24m;
+	/* clk for phb bus */
+	struct clk *pclk;
+	struct reset_control *rst;
+	struct link_train link_train;
+	struct video_info video_info;
+	bool clk_on;
+	bool standby;
+};
+
+void rk3288_edp_enable_video_mute(struct rk3288_edp *edp, bool enable);
+void rk3288_edp_stop_video(struct rk3288_edp *edp);
+void rk3288_edp_lane_swap(struct rk3288_edp *edp, bool enable);
+void rk3288_edp_init_refclk(struct rk3288_edp *edp);
+void rk3288_edp_init_interrupt(struct rk3288_edp *edp);
+void rk3288_edp_reset(struct rk3288_edp *edp);
+void rk3288_edp_config_interrupt(struct rk3288_edp *edp);
+u32 rk3288_edp_get_pll_lock_status(struct rk3288_edp *edp);
+void rk3288_edp_analog_power_ctr(struct rk3288_edp *edp, bool enable);
+void rk3288_edp_init_analog_func(struct rk3288_edp *edp);
+void rk3288_edp_init_hpd(struct rk3288_edp *edp);
+void rk3288_edp_reset_aux(struct rk3288_edp *edp);
+void rk3288_edp_init_aux(struct rk3288_edp *edp);
+int rk3288_edp_get_plug_in_status(struct rk3288_edp *edp);
+void rk3288_edp_enable_sw_function(struct rk3288_edp *edp);
+int rk3288_edp_start_aux_transaction(struct rk3288_edp *edp);
+int rk3288_edp_write_byte_to_dpcd(struct rk3288_edp *edp,
+				  unsigned int reg_addr,
+				  unsigned char data);
+int rk3288_edp_read_byte_from_dpcd(struct rk3288_edp *edp,
+				   unsigned int reg_addr,
+				   unsigned char *data);
+int rk3288_edp_write_bytes_to_dpcd(struct rk3288_edp *edp,
+				   unsigned int reg_addr,
+				   unsigned int count,
+				   unsigned char data[]);
+int rk3288_edp_read_bytes_from_dpcd(struct rk3288_edp *edp,
+				    unsigned int reg_addr,
+				    unsigned int count,
+				    unsigned char data[]);
+int rk3288_edp_select_i2c_device(struct rk3288_edp *edp,
+				 unsigned int device_addr,
+				 unsigned int reg_addr);
+int rk3288_edp_read_byte_from_i2c(struct rk3288_edp *edp,
+				  unsigned int device_addr,
+				  unsigned int reg_addr,
+				  unsigned int *data);
+int rk3288_edp_read_bytes_from_i2c(struct rk3288_edp *edp,
+				   unsigned int device_addr,
+				   unsigned int reg_addr,
+				   unsigned int count,
+				   unsigned char edid[]);
+void rk3288_edp_set_link_bandwidth(struct rk3288_edp *edp, u32 bwtype);
+void rk3288_edp_get_link_bandwidth(struct rk3288_edp *edp, u32 *bwtype);
+void rk3288_edp_set_lane_count(struct rk3288_edp *edp, u32 count);
+void rk3288_edp_get_lane_count(struct rk3288_edp *edp, u32 *count);
+void rk3288_edp_enable_enhanced_mode(struct rk3288_edp *edp, bool enable);
+void rk3288_edp_set_training_pattern(struct rk3288_edp *edp,
+				     enum pattern_set pattern);
+void rk3288_edp_set_lane0_pre_emphasis(struct rk3288_edp *edp, u32 level);
+void rk3288_edp_set_lane1_pre_emphasis(struct rk3288_edp *edp, u32 level);
+void rk3288_edp_set_lane2_pre_emphasis(struct rk3288_edp *edp, u32 level);
+void rk3288_edp_set_lane3_pre_emphasis(struct rk3288_edp *edp, u32 level);
+void rk3288_edp_set_lane0_link_training(struct rk3288_edp *edp,
+					u32 training_lane);
+void rk3288_edp_set_lane1_link_training(struct rk3288_edp *edp,
+					u32 training_lane);
+void rk3288_edp_set_lane2_link_training(struct rk3288_edp *edp,
+					u32 training_lane);
+void rk3288_edp_set_lane3_link_training(struct rk3288_edp *edp,
+					u32 training_lane);
+u32 rk3288_edp_get_lane0_link_training(struct rk3288_edp *edp);
+u32 rk3288_edp_get_lane1_link_training(struct rk3288_edp *edp);
+u32 rk3288_edp_get_lane2_link_training(struct rk3288_edp *edp);
+u32 rk3288_edp_get_lane3_link_training(struct rk3288_edp *edp);
+void rk3288_edp_reset_macro(struct rk3288_edp *edp);
+int rk3288_edp_init_video(struct rk3288_edp *edp);
+
+void rk3288_edp_set_video_color_format(struct rk3288_edp *edp,
+				       u32 color_depth,
+				       u32 color_space,
+				       u32 dynamic_range,
+				       u32 coeff);
+int rk3288_edp_is_slave_video_stream_clock_on(struct rk3288_edp *edp);
+void rk3288_edp_set_video_cr_mn(
+			struct rk3288_edp *edp,
+			enum clock_recovery_m_value_type type,
+			u32 m_value,
+			u32 n_value);
+void rk3288_edp_set_video_timing_mode(struct rk3288_edp *edp, u32 type);
+void rk3288_edp_enable_video_master(struct rk3288_edp *edp, bool enable);
+void rk3288_edp_start_video(struct rk3288_edp *edp);
+int rk3288_edp_is_video_stream_on(struct rk3288_edp *edp);
+void rk3288_edp_config_video_slave_mode(struct rk3288_edp *edp,
+					struct video_info *video_info);
+void rk3288_edp_enable_scrambling(struct rk3288_edp *edp);
+void rk3288_edp_disable_scrambling(struct rk3288_edp *edp);
+void rk3288_edp_rx_control(struct rk3288_edp *edp, bool enable);
+int rk3288_edp_bist_cfg(struct rk3288_edp *edp);
+void rk3288_edp_hw_link_training_en(struct rk3288_edp *edp);
+int rk3288_edp_get_hw_lt_status(struct rk3288_edp *edp);
+int rk3288_edp_wait_hw_lt_done(struct rk3288_edp *edp);
+enum dp_irq_type rk3288_edp_get_irq_type(struct rk3288_edp *edp);
+void rk3288_edp_clear_hotplug_interrupts(struct rk3288_edp *edp);
+
+/* I2C EDID Chip ID, Slave Address */
+#define I2C_EDID_DEVICE_ADDR			0x50
+#define I2C_E_EDID_DEVICE_ADDR			0x30
+
+#define EDID_BLOCK_LENGTH			0x80
+#define EDID_HEADER_PATTERN			0x00
+#define EDID_EXTENSION_FLAG			0x7e
+#define EDID_CHECKSUM				0x7f
+
+/* Definition for DPCD Register */
+#define DPCD_ADDR_DPCD_REV			0x0000
+#define DPCD_ADDR_MAX_LINK_RATE			0x0001
+#define DPCD_ADDR_MAX_LANE_COUNT		0x0002
+#define DPCD_ADDR_LINK_BW_SET			0x0100
+#define DPCD_ADDR_LANE_COUNT_SET		0x0101
+#define DPCD_ADDR_TRAINING_PATTERN_SET		0x0102
+#define DPCD_ADDR_TRAINING_LANE0_SET		0x0103
+#define DPCD_ADDR_LANE0_1_STATUS		0x0202
+#define DPCD_ADDR_LANE_ALIGN_STATUS_UPDATED	0x0204
+#define DPCD_ADDR_ADJUST_REQUEST_LANE0_1	0x0206
+#define DPCD_ADDR_ADJUST_REQUEST_LANE2_3	0x0207
+#define DPCD_ADDR_TEST_REQUEST			0x0218
+#define DPCD_ADDR_TEST_RESPONSE			0x0260
+#define DPCD_ADDR_TEST_EDID_CHECKSUM		0x0261
+#define DPCD_ADDR_SINK_POWER_STATE		0x0600
+
+/* DPCD_ADDR_MAX_LANE_COUNT */
+#define DPCD_ENHANCED_FRAME_CAP(x)		(((x) >> 7) & 0x1)
+#define DPCD_MAX_LANE_COUNT(x)			((x) & 0x1f)
+
+/* DPCD_ADDR_LANE_COUNT_SET */
+#define DPCD_ENHANCED_FRAME_EN			(0x1 << 7)
+#define DPCD_LANE_COUNT_SET(x)			((x) & 0x1f)
+
+/* DPCD_ADDR_TRAINING_PATTERN_SET */
+#define DPCD_SCRAMBLING_DISABLED		(0x1 << 5)
+#define DPCD_SCRAMBLING_ENABLED			(0x0 << 5)
+#define DPCD_TRAINING_PATTERN_2			(0x2 << 0)
+#define DPCD_TRAINING_PATTERN_1			(0x1 << 0)
+#define DPCD_TRAINING_PATTERN_DISABLED		(0x0 << 0)
+
+/* DPCD_ADDR_TRAINING_LANE0_SET */
+#define DPCD_MAX_PRE_EMPHASIS_REACHED		(0x1 << 5)
+#define DPCD_PRE_EMPHASIS_SET(x)		(((x) & 0x3) << 3)
+#define DPCD_PRE_EMPHASIS_GET(x)		(((x) >> 3) & 0x3)
+#define DPCD_PRE_EMPHASIS_PATTERN2_LEVEL0	(0x0 << 3)
+#define DPCD_MAX_SWING_REACHED			(0x1 << 2)
+#define DPCD_VOLTAGE_SWING_SET(x)		(((x) & 0x3) << 0)
+#define DPCD_VOLTAGE_SWING_GET(x)		(((x) >> 0) & 0x3)
+#define DPCD_VOLTAGE_SWING_PATTERN1_LEVEL0	(0x0 << 0)
+
+/* DPCD_ADDR_LANE0_1_STATUS */
+#define DPCD_LANE_SYMBOL_LOCKED			(0x1 << 2)
+#define DPCD_LANE_CHANNEL_EQ_DONE		(0x1 << 1)
+#define DPCD_LANE_CR_DONE			(0x1 << 0)
+#define DPCD_CHANNEL_EQ_BITS			(DPCD_LANE_CR_DONE|	\
+						 DPCD_LANE_CHANNEL_EQ_DONE|\
+						 DPCD_LANE_SYMBOL_LOCKED)
+
+/* DPCD_ADDR_LANE_ALIGN__STATUS_UPDATED */
+#define DPCD_LINK_STATUS_UPDATED		(0x1 << 7)
+#define DPCD_DOWNSTREAM_PORT_STATUS_CHANGED	(0x1 << 6)
+#define DPCD_INTERLANE_ALIGN_DONE		(0x1 << 0)
+
+/* DPCD_ADDR_TEST_REQUEST */
+#define DPCD_TEST_EDID_READ			(0x1 << 2)
+
+/* DPCD_ADDR_TEST_RESPONSE */
+#define DPCD_TEST_EDID_CHECKSUM_WRITE		(0x1 << 2)
+
+/* DPCD_ADDR_SINK_POWER_STATE */
+#define DPCD_SET_POWER_STATE_D0			(0x1 << 0)
+#define DPCD_SET_POWER_STATE_D4			(0x2 << 0)
+
+#define DPCD_SYMBOL_ERR_CONUT_LANE0		0x210
+
+#endif
diff --git a/drivers/gpu/drm/rockchip/connector/rk3288_dp_reg.c b/drivers/gpu/drm/rockchip/connector/rk3288_dp_reg.c
new file mode 100644
index 0000000..f558cea
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/connector/rk3288_dp_reg.c
@@ -0,0 +1,1192 @@
+/*
+* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+* Author:
+*      yxj <yxj-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+*      cym <cym-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+*
+* based on exynos_dp_reg.c
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*/
+
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#include "rk3288_dp_core.h"
+#include "rk3288_dp_reg.h"
+
+void rk3288_edp_enable_video_mute(struct rk3288_edp *edp, bool enable)
+{
+	u32 val;
+
+	if (enable) {
+		val = readl(edp->regs + VIDEO_CTL_1);
+		val |= VIDEO_MUTE;
+		writel(val, edp->regs + VIDEO_CTL_1);
+	} else {
+		val = readl(edp->regs + VIDEO_CTL_1);
+		val &= ~VIDEO_MUTE;
+		writel(val, edp->regs + VIDEO_CTL_1);
+	}
+}
+
+void rk3288_edp_stop_video(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + VIDEO_CTL_1);
+	val &= ~VIDEO_EN;
+	writel(val, edp->regs + VIDEO_CTL_1);
+}
+
+void rk3288_edp_lane_swap(struct rk3288_edp *edp, bool enable)
+{
+	u32 val;
+
+	if (enable)
+		val = LANE3_MAP_LOGIC_LANE_0 | LANE2_MAP_LOGIC_LANE_1 |
+			LANE1_MAP_LOGIC_LANE_2 | LANE0_MAP_LOGIC_LANE_3;
+	else
+		val = LANE3_MAP_LOGIC_LANE_3 | LANE2_MAP_LOGIC_LANE_2 |
+			LANE1_MAP_LOGIC_LANE_1 | LANE0_MAP_LOGIC_LANE_0;
+
+	writel(val, edp->regs + LANE_MAP);
+}
+
+void rk3288_edp_init_refclk(struct rk3288_edp *edp)
+{
+	writel(SEL_24M, edp->regs + ANALOG_CTL_2);
+	writel(REF_CLK_24M, edp->regs + PLL_REG_1);
+
+	writel(0x95, edp->regs + PLL_REG_2);
+	writel(0x40, edp->regs + PLL_REG_3);
+	writel(0x58, edp->regs + PLL_REG_4);
+	writel(0x22, edp->regs + PLL_REG_5);
+	writel(0x19, edp->regs + SSC_REG);
+	writel(0x87, edp->regs + TX_REG_COMMON);
+	writel(0x03, edp->regs + DP_AUX);
+	writel(0x46, edp->regs + DP_BIAS);
+	writel(0x55, edp->regs + DP_RESERVE2);
+}
+
+void rk3288_edp_init_interrupt(struct rk3288_edp *edp)
+{
+	/* Set interrupt pin assertion polarity as high */
+	writel(INT_POL, edp->regs + INT_CTL);
+
+	/* Clear pending valisers */
+	writel(0xff, edp->regs + COMMON_INT_STA_1);
+	writel(0x4f, edp->regs + COMMON_INT_STA_2);
+	writel(0xff, edp->regs + COMMON_INT_STA_3);
+	writel(0x27, edp->regs + COMMON_INT_STA_4);
+
+	writel(0x7f, edp->regs + DP_INT_STA);
+
+	/* 0:mask,1: unmask */
+	writel(0x00, edp->regs + COMMON_INT_MASK_1);
+	writel(0x00, edp->regs + COMMON_INT_MASK_2);
+	writel(0x00, edp->regs + COMMON_INT_MASK_3);
+	writel(0x00, edp->regs + COMMON_INT_MASK_4);
+	writel(0x00, edp->regs + DP_INT_STA_MASK);
+}
+
+void rk3288_edp_reset(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	rk3288_edp_stop_video(edp);
+	rk3288_edp_enable_video_mute(edp, 0);
+
+	val = VID_CAP_FUNC_EN_N | AUD_FIFO_FUNC_EN_N |
+		AUD_FUNC_EN_N | HDCP_FUNC_EN_N | SW_FUNC_EN_N;
+	writel(val, edp->regs + FUNC_EN_1);
+
+	val = SSC_FUNC_EN_N | AUX_FUNC_EN_N |
+		SERDES_FIFO_FUNC_EN_N |
+		LS_CLK_DOMAIN_FUNC_EN_N;
+	writel(val, edp->regs + FUNC_EN_2);
+
+	usleep_range(20, 30);
+
+	rk3288_edp_lane_swap(edp, 0);
+
+	writel(0x0, edp->regs + SYS_CTL_1);
+	writel(0x40, edp->regs + SYS_CTL_2);
+	writel(0x0, edp->regs + SYS_CTL_3);
+	writel(0x0, edp->regs + SYS_CTL_4);
+
+	writel(0x0, edp->regs + PKT_SEND_CTL);
+	writel(0x0, edp->regs + HDCP_CTL);
+
+	writel(0x5e, edp->regs + HPD_DEGLITCH_L);
+	writel(0x1a, edp->regs + HPD_DEGLITCH_H);
+
+	writel(0x10, edp->regs + LINK_DEBUG_CTL);
+
+	writel(0x0, edp->regs + VIDEO_FIFO_THRD);
+	writel(0x20, edp->regs + AUDIO_MARGIN);
+
+	writel(0x4, edp->regs + M_VID_GEN_FILTER_TH);
+	writel(0x2, edp->regs + M_AUD_GEN_FILTER_TH);
+
+	writel(0x0, edp->regs + SOC_GENERAL_CTL);
+}
+
+void rk3288_edp_config_interrupt(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	/* 0: mask, 1: unmask */
+	val = 0;
+	writel(val, edp->regs + COMMON_INT_MASK_1);
+
+	writel(val, edp->regs + COMMON_INT_MASK_2);
+
+	writel(val, edp->regs + COMMON_INT_MASK_3);
+
+	writel(val, edp->regs + COMMON_INT_MASK_4);
+
+	writel(val, edp->regs + DP_INT_STA_MASK);
+}
+
+u32 rk3288_edp_get_pll_lock_status(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + DEBUG_CTL);
+	if (val & PLL_LOCK)
+		return DP_PLL_LOCKED;
+	else
+		return DP_PLL_UNLOCKED;
+}
+
+void rk3288_edp_analog_power_ctr(struct rk3288_edp *edp, bool enable)
+{
+	u32 val;
+
+	if (enable) {
+		val = PD_EXP_BG | PD_AUX | PD_PLL |
+			PD_CH3 | PD_CH2 | PD_CH1 | PD_CH0;
+		writel(val, edp->regs + DP_PWRDN);
+		usleep_range(10, 20);
+		writel(0x0, edp->regs + DP_PWRDN);
+	} else {
+		val = PD_EXP_BG | PD_AUX | PD_PLL |
+			PD_CH3 | PD_CH2 | PD_CH1 | PD_CH0;
+		writel(val, edp->regs + DP_PWRDN);
+	}
+}
+
+void rk3288_edp_init_analog_func(struct rk3288_edp *edp)
+{
+	u32 val;
+	int wt = 0;
+
+	rk3288_edp_analog_power_ctr(edp, 1);
+
+	val = PLL_LOCK_CHG;
+	writel(val, edp->regs + COMMON_INT_STA_1);
+
+	val = readl(edp->regs + DEBUG_CTL);
+	val &= ~(F_PLL_LOCK | PLL_LOCK_CTRL);
+	writel(val, edp->regs + DEBUG_CTL);
+
+	/* Power up PLL */
+	while (wt < 100) {
+		if (rk3288_edp_get_pll_lock_status(edp) == DP_PLL_LOCKED) {
+			dev_dbg(edp->dev, "edp pll locked\n");
+			break;
+		} else {
+			wt++;
+			udelay(5);
+		}
+	}
+
+	/* Enable Serdes FIFO function and Link symbol clock domain module */
+	val = readl(edp->regs + FUNC_EN_2);
+	val &= ~(SERDES_FIFO_FUNC_EN_N | LS_CLK_DOMAIN_FUNC_EN_N
+		| AUX_FUNC_EN_N | SSC_FUNC_EN_N);
+	writel(val, edp->regs + FUNC_EN_2);
+}
+
+void rk3288_edp_init_hpd(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = HOTPLUG_CHG | HPD_LOST | PLUG;
+	writel(val, edp->regs + COMMON_INT_STA_4);
+
+	val = INT_HPD;
+	writel(val, edp->regs + DP_INT_STA);
+
+	val = readl(edp->regs + SYS_CTL_3);
+	val |= (F_HPD | HPD_CTRL);
+	writel(val, edp->regs + SYS_CTL_3);
+}
+
+void rk3288_edp_reset_aux(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	/* Disable AUX channel module */
+	val = readl(edp->regs + FUNC_EN_2);
+	val |= AUX_FUNC_EN_N;
+	writel(val, edp->regs + FUNC_EN_2);
+}
+
+void rk3288_edp_init_aux(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	/* Clear inerrupts related to AUX channel */
+	val = RPLY_RECEIV | AUX_ERR;
+	writel(val, edp->regs + DP_INT_STA);
+
+	rk3288_edp_reset_aux(edp);
+
+	/* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */
+	val = DEFER_CTRL_EN | DEFER_COUNT(1);
+	writel(val, edp->regs + AUX_CH_DEFER_CTL);
+
+	/* Enable AUX channel module */
+	val = readl(edp->regs + FUNC_EN_2);
+	val &= ~AUX_FUNC_EN_N;
+	writel(val, edp->regs + FUNC_EN_2);
+}
+
+int rk3288_edp_get_plug_in_status(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + SYS_CTL_3);
+	if (val & HPD_STATUS)
+		return 0;
+
+	return -EINVAL;
+}
+
+void rk3288_edp_enable_sw_function(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + FUNC_EN_1);
+	val &= ~SW_FUNC_EN_N;
+	writel(val, edp->regs + FUNC_EN_1);
+}
+
+int rk3288_edp_start_aux_transaction(struct rk3288_edp *edp)
+{
+	int val;
+	int retval = 0;
+	int timeout_loop = 0;
+	int aux_timeout = 0;
+
+	/* Enable AUX CH operation */
+	val = readl(edp->regs + AUX_CH_CTL_2);
+	val |= AUX_EN;
+	writel(val, edp->regs + AUX_CH_CTL_2);
+
+	/* Is AUX CH operation enabled? */
+	val = readl(edp->regs + AUX_CH_CTL_2);
+	while (val & AUX_EN) {
+		aux_timeout++;
+		if ((DP_TIMEOUT_LOOP_CNT * 10) < aux_timeout) {
+			dev_err(edp->dev, "AUX CH enable timeout!\n");
+			return -ETIMEDOUT;
+		}
+		val = readl(edp->regs + AUX_CH_CTL_2);
+		usleep_range(100, 110);
+	}
+
+	/* Is AUX CH command redply received? */
+	val = readl(edp->regs + DP_INT_STA);
+	while (!(val & RPLY_RECEIV)) {
+		timeout_loop++;
+		if (DP_TIMEOUT_LOOP_CNT < timeout_loop) {
+			dev_err(edp->dev, "AUX CH command redply failed!\n");
+			return -ETIMEDOUT;
+		}
+		val = readl(edp->regs + DP_INT_STA);
+		usleep_range(10, 20);
+	}
+
+	/* Clear interrupt source for AUX CH command redply */
+	writel(RPLY_RECEIV, edp->regs + DP_INT_STA);
+
+	/* Clear interrupt source for AUX CH access error */
+	val = readl(edp->regs + DP_INT_STA);
+	if (val & AUX_ERR) {
+		writel(AUX_ERR, edp->regs + DP_INT_STA);
+		return -EREMOTEIO;
+	}
+
+	/* Check AUX CH error access status */
+	val = readl(edp->regs + AUX_CH_STA);
+	if ((val & AUX_STATUS_MASK) != 0) {
+		dev_err(edp->dev, "AUX CH error happens: %d\n\n",
+			val & AUX_STATUS_MASK);
+		return -EREMOTEIO;
+	}
+
+	return retval;
+}
+
+int rk3288_edp_write_byte_to_dpcd(struct rk3288_edp *edp,
+				  unsigned int val_addr,
+				  unsigned char data)
+{
+	u32 val;
+	int i;
+	int retval;
+
+	for (i = 0; i < 3; i++) {
+		/* Clear AUX CH data buffer */
+		val = BUF_CLR;
+		writel(val, edp->regs + BUFFER_DATA_CTL);
+
+		/* Select DPCD device address */
+		val = AUX_ADDR_7_0(val_addr);
+		writel(val, edp->regs + DP_AUX_ADDR_7_0);
+		val = AUX_ADDR_15_8(val_addr);
+		writel(val, edp->regs + DP_AUX_ADDR_15_8);
+		val = AUX_ADDR_19_16(val_addr);
+		writel(val, edp->regs + DP_AUX_ADDR_19_16);
+
+		/* Write data buffer */
+		val = (unsigned int)data;
+		writel(val, edp->regs + BUF_DATA_0);
+
+		/*
+		 * Set DisplayPort transaction and write 1 byte
+		 * If bit 3 is 1, DisplayPort transaction.
+		 * If Bit 3 is 0, I2C transaction.
+		 */
+		val = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE;
+		writel(val, edp->regs + AUX_CH_CTL_1);
+
+		/* Start AUX transaction */
+		retval = rk3288_edp_start_aux_transaction(edp);
+		if (retval == 0)
+			break;
+		else
+			dev_dbg(edp->dev, "Aux Transaction fail!\n");
+	}
+
+	return retval;
+}
+
+int rk3288_edp_read_byte_from_dpcd(struct rk3288_edp *edp,
+				   unsigned int val_addr,
+				   unsigned char *data)
+{
+	u32 val;
+	int i;
+	int retval;
+
+	for (i = 0; i < 10; i++) {
+		/* Clear AUX CH data buffer */
+		val = BUF_CLR;
+		writel(val, edp->regs + BUFFER_DATA_CTL);
+
+		/* Select DPCD device address */
+		val = AUX_ADDR_7_0(val_addr);
+		writel(val, edp->regs + DP_AUX_ADDR_7_0);
+		val = AUX_ADDR_15_8(val_addr);
+		writel(val, edp->regs + DP_AUX_ADDR_15_8);
+		val = AUX_ADDR_19_16(val_addr);
+		writel(val, edp->regs + DP_AUX_ADDR_19_16);
+
+		/*
+		 * Set DisplayPort transaction and read 1 byte
+		 * If bit 3 is 1, DisplayPort transaction.
+		 * If Bit 3 is 0, I2C transaction.
+		 */
+		val = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ;
+		writel(val, edp->regs + AUX_CH_CTL_1);
+
+		/* Start AUX transaction */
+		retval = rk3288_edp_start_aux_transaction(edp);
+		if (retval == 0)
+			break;
+		else
+			dev_dbg(edp->dev, "Aux Transaction fail!\n");
+	}
+
+	/* Read data buffer */
+	val = readl(edp->regs + BUF_DATA_0);
+	*data = (unsigned char)(val & 0xff);
+
+	return retval;
+}
+
+int rk3288_edp_write_bytes_to_dpcd(struct rk3288_edp *edp,
+				   unsigned int val_addr,
+				   unsigned int count,
+				   unsigned char data[])
+{
+	u32 val;
+	unsigned int start_offset;
+	unsigned int cur_data_count;
+	unsigned int cur_data_idx;
+	int i;
+	int retval = 0;
+
+	/* Clear AUX CH data buffer */
+	val = BUF_CLR;
+	writel(val, edp->regs + BUFFER_DATA_CTL);
+
+	start_offset = 0;
+	while (start_offset < count) {
+		/* Buffer size of AUX CH is 16 * 4bytes */
+		if ((count - start_offset) > 16)
+			cur_data_count = 16;
+		else
+			cur_data_count = count - start_offset;
+
+		for (i = 0; i < 10; i++) {
+			/* Select DPCD device address */
+			val = AUX_ADDR_7_0(val_addr + start_offset);
+			writel(val, edp->regs + DP_AUX_ADDR_7_0);
+			val = AUX_ADDR_15_8(val_addr + start_offset);
+			writel(val, edp->regs + DP_AUX_ADDR_15_8);
+			val = AUX_ADDR_19_16(val_addr + start_offset);
+			writel(val, edp->regs + DP_AUX_ADDR_19_16);
+
+			for (cur_data_idx = 0; cur_data_idx < cur_data_count;
+			     cur_data_idx++) {
+				val = data[start_offset + cur_data_idx];
+				writel(val, edp->regs + BUF_DATA_0
+							  + 4 * cur_data_idx);
+			}
+
+			/*
+			 * Set DisplayPort transaction and write
+			 * If bit 3 is 1, DisplayPort transaction.
+			 * If Bit 3 is 0, I2C transaction.
+			 */
+			val = AUX_LENGTH(cur_data_count) |
+				AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE;
+			writel(val, edp->regs + AUX_CH_CTL_1);
+
+			/* Start AUX transaction */
+			retval = rk3288_edp_start_aux_transaction(edp);
+			if (retval == 0)
+				break;
+			else
+				dev_dbg(edp->dev, "Aux Transaction fail!\n");
+		}
+
+		start_offset += cur_data_count;
+	}
+
+	return retval;
+}
+
+int rk3288_edp_read_bytes_from_dpcd(struct rk3288_edp *edp,
+				    unsigned int val_addr,
+				    unsigned int count,
+				    unsigned char data[])
+{
+	u32 val;
+	unsigned int start_offset;
+	unsigned int cur_data_count;
+	unsigned int cur_data_idx;
+	int i;
+	int retval = 0;
+
+	/* Clear AUX CH data buffer */
+	val = BUF_CLR;
+	writel(val, edp->regs + BUFFER_DATA_CTL);
+
+	start_offset = 0;
+	while (start_offset < count) {
+		/* Buffer size of AUX CH is 16 * 4bytes */
+		if ((count - start_offset) > 16)
+			cur_data_count = 16;
+		else
+			cur_data_count = count - start_offset;
+
+		/* AUX CH Request Transaction process */
+		for (i = 0; i < 10; i++) {
+			/* Select DPCD device address */
+			val = AUX_ADDR_7_0(val_addr + start_offset);
+			writel(val, edp->regs + DP_AUX_ADDR_7_0);
+			val = AUX_ADDR_15_8(val_addr + start_offset);
+			writel(val, edp->regs + DP_AUX_ADDR_15_8);
+			val = AUX_ADDR_19_16(val_addr + start_offset);
+			writel(val, edp->regs + DP_AUX_ADDR_19_16);
+
+			/*
+			 * Set DisplayPort transaction and read
+			 * If bit 3 is 1, DisplayPort transaction.
+			 * If Bit 3 is 0, I2C transaction.
+			 */
+			val = AUX_LENGTH(cur_data_count) |
+				AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ;
+			writel(val, edp->regs + AUX_CH_CTL_1);
+
+			/* Start AUX transaction */
+			retval = rk3288_edp_start_aux_transaction(edp);
+			if (retval == 0)
+				break;
+			else
+				dev_dbg(edp->dev, "Aux Transaction fail!\n");
+		}
+
+		for (cur_data_idx = 0; cur_data_idx < cur_data_count;
+		    cur_data_idx++) {
+			val = readl(edp->regs + BUF_DATA_0
+						 + 4 * cur_data_idx);
+			data[start_offset + cur_data_idx] =
+				(unsigned char)val;
+		}
+
+		start_offset += cur_data_count;
+	}
+
+	return retval;
+}
+
+int rk3288_edp_select_i2c_device(struct rk3288_edp *edp,
+				 unsigned int device_addr,
+				 unsigned int val_addr)
+{
+	u32 val;
+	int retval;
+
+	/* Set EDID device address */
+	val = device_addr;
+	writel(val, edp->regs + DP_AUX_ADDR_7_0);
+	writel(0x0, edp->regs + DP_AUX_ADDR_15_8);
+	writel(0x0, edp->regs + DP_AUX_ADDR_19_16);
+
+	/* Set offset from base address of EDID device */
+	writel(val_addr, edp->regs + BUF_DATA_0);
+
+	/*
+	 * Set I2C transaction and write address
+	 * If bit 3 is 1, DisplayPort transaction.
+	 * If Bit 3 is 0, I2C transaction.
+	 */
+	val = AUX_TX_COMM_I2C_TRANSACTION | AUX_TX_COMM_MOT |
+		AUX_TX_COMM_WRITE;
+	writel(val, edp->regs + AUX_CH_CTL_1);
+
+	/* Start AUX transaction */
+	retval = rk3288_edp_start_aux_transaction(edp);
+	if (retval != 0)
+		dev_dbg(edp->dev, "Aux Transaction fail!\n");
+
+	return retval;
+}
+
+int rk3288_edp_read_byte_from_i2c(struct rk3288_edp *edp,
+				  unsigned int device_addr,
+				  unsigned int val_addr,
+				  unsigned int *data)
+{
+	u32 val;
+	int i;
+	int retval;
+
+	for (i = 0; i < 10; i++) {
+		/* Clear AUX CH data buffer */
+		val = BUF_CLR;
+		writel(val, edp->regs + BUFFER_DATA_CTL);
+
+		/* Select EDID device */
+		retval = rk3288_edp_select_i2c_device(edp,
+						      device_addr,
+						      val_addr);
+		if (retval != 0) {
+			dev_err(edp->dev, "Select EDID device fail!\n");
+			continue;
+		}
+
+		/*
+		 * Set I2C transaction and read data
+		 * If bit 3 is 1, DisplayPort transaction.
+		 * If Bit 3 is 0, I2C transaction.
+		 */
+		val = AUX_TX_COMM_I2C_TRANSACTION | AUX_TX_COMM_READ;
+		writel(val, edp->regs + AUX_CH_CTL_1);
+
+		/* Start AUX transaction */
+		retval = rk3288_edp_start_aux_transaction(edp);
+		if (retval == 0)
+			break;
+		else
+			dev_dbg(edp->dev, "Aux Transaction fail!\n");
+	}
+
+	/* Read data */
+	if (retval == 0)
+		*data = readl(edp->regs + BUF_DATA_0);
+
+	return retval;
+}
+
+int rk3288_edp_read_bytes_from_i2c(struct rk3288_edp *edp,
+				   unsigned int device_addr,
+				   unsigned int val_addr,
+				   unsigned int count,
+				   unsigned char edid[])
+{
+	u32 val;
+	unsigned int i, j;
+	unsigned int cur_data_idx;
+	unsigned int defer = 0;
+	int retval = 0;
+
+	for (i = 0; i < count; i += 16) {
+		for (j = 0; j < 100; j++) {
+			/* Clear AUX CH data buffer */
+			val = BUF_CLR;
+			writel(val, edp->regs + BUFFER_DATA_CTL);
+
+			/* Set normal AUX CH command */
+			val = readl(edp->regs + AUX_CH_CTL_2);
+			val &= ~ADDR_ONLY;
+			writel(val, edp->regs + AUX_CH_CTL_2);
+
+			/*
+			 * If Rx sends defer, Tx sends only reads
+			 * request without sending addres
+			 */
+			if (!defer)
+				retval = rk3288_edp_select_i2c_device(
+						edp, device_addr, val_addr + i);
+			else
+				defer = 0;
+
+			/*
+			 * Set I2C transaction and write data
+			 * If bit 3 is 1, DisplayPort transaction.
+			 * If Bit 3 is 0, I2C transaction.
+			 */
+			val = AUX_LENGTH(16) | AUX_TX_COMM_I2C_TRANSACTION |
+				AUX_TX_COMM_READ;
+			writel(val, edp->regs + AUX_CH_CTL_1);
+
+			/* Start AUX transaction */
+			retval = rk3288_edp_start_aux_transaction(edp);
+			if (retval == 0)
+				break;
+			else
+				dev_dbg(edp->dev, "Aux Transaction fail!\n");
+
+			/* Check if Rx sends defer */
+			val = readl(edp->regs + AUX_RX_COMM);
+			if (val == AUX_RX_COMM_AUX_DEFER ||
+			    val == AUX_RX_COMM_I2C_DEFER) {
+				dev_err(edp->dev, "Defer: %d\n\n", val);
+				defer = 1;
+			}
+		}
+
+		for (cur_data_idx = 0; cur_data_idx < 16; cur_data_idx++) {
+			val = readl(edp->regs + BUF_DATA_0 + 4 * cur_data_idx);
+			edid[i + cur_data_idx] = (unsigned char)val;
+		}
+	}
+
+	return retval;
+}
+
+void rk3288_edp_set_link_bandwidth(struct rk3288_edp *edp, u32 bwtype)
+{
+	u32 val;
+
+	val = bwtype;
+	if ((bwtype == LINK_RATE_2_70GBPS) || (bwtype == LINK_RATE_1_62GBPS))
+		writel(val, edp->regs + LINK_BW_SET);
+}
+
+void rk3288_edp_get_link_bandwidth(struct rk3288_edp *edp, u32 *bwtype)
+{
+	u32 val;
+
+	val = readl(edp->regs + LINK_BW_SET);
+	*bwtype = val;
+}
+
+void rk3288_edp_hw_link_training_en(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = HW_LT_EN;
+	writel(val, edp->regs + HW_LT_CTL);
+}
+
+int rk3288_edp_wait_hw_lt_done(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + DP_INT_STA);
+	if (val&HW_LT_DONE) {
+		writel(val, edp->regs + DP_INT_STA);
+		return 0;
+	} else {
+		return 1;
+	}
+}
+
+int rk3288_edp_get_hw_lt_status(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + HW_LT_CTL);
+	return (val & HW_LT_ERR_CODE_MASK) >> 4;
+}
+void rk3288_edp_set_lane_count(struct rk3288_edp *edp, u32 count)
+{
+	u32 val;
+
+	val = count;
+	writel(val, edp->regs + LANE_CNT_SET);
+}
+
+void rk3288_edp_get_lane_count(struct rk3288_edp *edp, u32 *count)
+{
+	u32 val;
+
+	val = readl(edp->regs + LANE_CNT_SET);
+	*count = val;
+}
+
+void rk3288_edp_enable_enhanced_mode(struct rk3288_edp *edp, bool enable)
+{
+	u32 val;
+
+	if (enable) {
+		val = readl(edp->regs + SYS_CTL_4);
+		val |= ENHANCED;
+		writel(val, edp->regs + SYS_CTL_4);
+	} else {
+		val = readl(edp->regs + SYS_CTL_4);
+		val &= ~ENHANCED;
+		writel(val, edp->regs + SYS_CTL_4);
+	}
+}
+
+void rk3288_edp_set_training_pattern(struct rk3288_edp *edp,
+				     enum pattern_set pattern)
+{
+	u32 val;
+
+	switch (pattern) {
+	case PRBS7:
+		val = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_PRBS7;
+		writel(val, edp->regs + TRAINING_PTN_SET);
+		break;
+	case D10_2:
+		val = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_D10_2;
+		writel(val, edp->regs + TRAINING_PTN_SET);
+		break;
+	case TRAINING_PTN1:
+		val = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN1;
+		writel(val, edp->regs + TRAINING_PTN_SET);
+		break;
+	case TRAINING_PTN2:
+		val = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN2;
+		writel(val, edp->regs + TRAINING_PTN_SET);
+		break;
+	case DP_NONE:
+		val = SCRAMBLING_ENABLE |
+			LINK_QUAL_PATTERN_SET_DISABLE |
+			SW_TRAINING_PATTERN_SET_DISABLE;
+		writel(val, edp->regs + TRAINING_PTN_SET);
+		break;
+	default:
+		break;
+	}
+}
+
+void rk3288_edp_set_lane0_pre_emphasis(struct rk3288_edp *edp, u32 level)
+{
+	u32 val;
+
+	val = level << PRE_EMPHASIS_SET_SHIFT;
+	writel(val, edp->regs + LN0_LINK_TRAINING_CTL);
+}
+
+void rk3288_edp_set_lane1_pre_emphasis(struct rk3288_edp *edp, u32 level)
+{
+	u32 val;
+
+	val = level << PRE_EMPHASIS_SET_SHIFT;
+	writel(val, edp->regs + LN1_LINK_TRAINING_CTL);
+}
+
+void rk3288_edp_set_lane2_pre_emphasis(struct rk3288_edp *edp, u32 level)
+{
+	u32 val;
+
+	val = level << PRE_EMPHASIS_SET_SHIFT;
+	writel(val, edp->regs + LN2_LINK_TRAINING_CTL);
+}
+
+void rk3288_edp_set_lane3_pre_emphasis(struct rk3288_edp *edp, u32 level)
+{
+	u32 val;
+
+	val = level << PRE_EMPHASIS_SET_SHIFT;
+	writel(val, edp->regs + LN3_LINK_TRAINING_CTL);
+}
+
+void rk3288_edp_set_lane0_link_training(struct rk3288_edp *edp,
+					u32 training_lane)
+{
+	u32 val;
+
+	val = training_lane;
+	writel(val, edp->regs + LN0_LINK_TRAINING_CTL);
+}
+
+void rk3288_edp_set_lane1_link_training(struct rk3288_edp *edp,
+					u32 training_lane)
+{
+	u32 val;
+
+	val = training_lane;
+	writel(val, edp->regs + LN1_LINK_TRAINING_CTL);
+}
+
+void rk3288_edp_set_lane2_link_training(struct rk3288_edp *edp,
+					u32 training_lane)
+{
+	u32 val;
+
+	val = training_lane;
+	writel(val, edp->regs + LN2_LINK_TRAINING_CTL);
+}
+
+void rk3288_edp_set_lane3_link_training(struct rk3288_edp *edp,
+					u32 training_lane)
+{
+	u32 val;
+
+	val = training_lane;
+	writel(val, edp->regs + LN3_LINK_TRAINING_CTL);
+}
+
+u32 rk3288_edp_get_lane0_link_training(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + LN0_LINK_TRAINING_CTL);
+	return val;
+}
+
+u32 rk3288_edp_get_lane1_link_training(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + LN1_LINK_TRAINING_CTL);
+	return val;
+}
+
+u32 rk3288_edp_get_lane2_link_training(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + LN2_LINK_TRAINING_CTL);
+	return val;
+}
+
+u32 rk3288_edp_get_lane3_link_training(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + LN3_LINK_TRAINING_CTL);
+	return val;
+}
+
+void rk3288_edp_reset_macro(struct rk3288_edp *edp)
+{
+}
+
+int rk3288_edp_init_video(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = VSYNC_DET | VID_FORMAT_CHG | VID_CLK_CHG;
+	writel(val, edp->regs + COMMON_INT_STA_1);
+
+	val = 0x0;
+	writel(val, edp->regs + SYS_CTL_1);
+
+	val = CHA_CRI(4) | CHA_CTRL;
+	writel(val, edp->regs + SYS_CTL_2);
+
+	val = VID_HRES_TH(2) | VID_VRES_TH(0);
+	writel(val, edp->regs + VIDEO_CTL_8);
+
+	return 0;
+}
+
+void rk3288_edp_set_video_color_format(struct rk3288_edp *edp,
+				       u32 color_dedpth,
+				       u32 color_space,
+				       u32 dynamic_range,
+				       u32 coeff)
+{
+	u32 val;
+
+	/* Configure the input color dedpth, color space, dynamic range */
+	val = (dynamic_range << IN_D_RANGE_SHIFT) |
+		(color_dedpth << IN_BPC_SHIFT) |
+		(color_space << IN_COLOR_F_SHIFT);
+	writel(val, edp->regs + VIDEO_CTL_2);
+
+	/* Set Input Color YCbCr Coefficients to ITU601 or ITU709 */
+	val = readl(edp->regs + VIDEO_CTL_3);
+	val &= ~IN_YC_COEFFI_MASK;
+	if (coeff)
+		val |= IN_YC_COEFFI_ITU709;
+	else
+		val |= IN_YC_COEFFI_ITU601;
+	writel(val, edp->regs + VIDEO_CTL_3);
+}
+
+int rk3288_edp_is_slave_video_stream_clock_on(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + SYS_CTL_1);
+	writel(val, edp->regs + SYS_CTL_1);
+
+	val = readl(edp->regs + SYS_CTL_1);
+
+	if (!(val & DET_STA)) {
+		dev_dbg(edp->dev, "Input stream clock not detected.\n");
+		return -EINVAL;
+	}
+
+	val = readl(edp->regs + SYS_CTL_2);
+	writel(val, edp->regs + SYS_CTL_2);
+
+	val = readl(edp->regs + SYS_CTL_2);
+	if (val & CHA_STA) {
+		dev_dbg(edp->dev, "Input stream clk is changing\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+void rk3288_edp_set_video_cr_mn(struct rk3288_edp *edp,
+				enum clock_recovery_m_value_type type,
+				u32 m_value,
+				u32 n_value)
+{
+	u32 val;
+
+	if (type == REGISTER_M) {
+		val = readl(edp->regs + SYS_CTL_4);
+		val |= FIX_M_VID;
+		writel(val, edp->regs + SYS_CTL_4);
+		val = m_value & 0xff;
+		writel(val, edp->regs + M_VID_0);
+		val = (m_value >> 8) & 0xff;
+		writel(val, edp->regs + M_VID_1);
+		val = (m_value >> 16) & 0xff;
+		writel(val, edp->regs + M_VID_2);
+
+		val = n_value & 0xff;
+		writel(val, edp->regs + N_VID_0);
+		val = (n_value >> 8) & 0xff;
+		writel(val, edp->regs + N_VID_1);
+		val = (n_value >> 16) & 0xff;
+		writel(val, edp->regs + N_VID_2);
+	} else  {
+		val = readl(edp->regs + SYS_CTL_4);
+		val &= ~FIX_M_VID;
+		writel(val, edp->regs + SYS_CTL_4);
+
+		writel(0x00, edp->regs + N_VID_0);
+		writel(0x80, edp->regs + N_VID_1);
+		writel(0x00, edp->regs + N_VID_2);
+	}
+}
+
+void rk3288_edp_set_video_timing_mode(struct rk3288_edp *edp, u32 type)
+{
+	u32 val;
+
+	if (type == VIDEO_TIMING_FROM_CAPTURE) {
+		val = readl(edp->regs + VIDEO_CTL_10);
+		val &= ~F_SEL;
+		writel(val, edp->regs + VIDEO_CTL_10);
+	} else {
+		val = readl(edp->regs + VIDEO_CTL_10);
+		val |= F_SEL;
+		writel(val, edp->regs + VIDEO_CTL_10);
+	}
+}
+
+int rk3288_edp_bist_cfg(struct rk3288_edp *edp)
+{
+	struct video_info *video_info = &edp->video_info;
+	struct drm_display_mode *mode = &edp->mode;
+	u16 x_total, y_total, x_act;
+	u32 val;
+
+	x_total = mode->htotal;
+	y_total = mode->vtotal;
+	x_act = mode->hdisplay;
+
+	rk3288_edp_set_video_cr_mn(edp, CALCULATED_M, 0, 0);
+	rk3288_edp_set_video_color_format(edp, video_info->color_depth,
+					  video_info->color_space,
+					  video_info->dynamic_range,
+					  video_info->ycbcr_coeff);
+
+	val = y_total & 0xff;
+	writel(val, edp->regs + TOTAL_LINE_CFG_L);
+	val = (y_total >> 8);
+	writel(val, edp->regs + TOTAL_LINE_CFG_H);
+	val = (mode->vdisplay & 0xff);
+	writel(val, edp->regs + ATV_LINE_CFG_L);
+	val = (mode->vdisplay >> 8);
+	writel(val, edp->regs + ATV_LINE_CFG_H);
+	val = (mode->vsync_start - mode->vdisplay);
+	writel(val, edp->regs + VF_PORCH_REG);
+	val = (mode->vsync_end - mode->vsync_start);
+	writel(val, edp->regs + VSYNC_CFG_REG);
+	val = (mode->vtotal - mode->vsync_end);
+	writel(val, edp->regs + VB_PORCH_REG);
+	val = x_total & 0xff;
+	writel(val, edp->regs + TOTAL_PIXELL_REG);
+	val = x_total >> 8;
+	writel(val, edp->regs + TOTAL_PIXELH_REG);
+	val = (x_act & 0xff);
+	writel(val, edp->regs + ATV_PIXELL_REG);
+	val = (x_act >> 8);
+	writel(val, edp->regs + ATV_PIXELH_REG);
+	val = (mode->hsync_start - mode->hdisplay) & 0xff;
+	writel(val, edp->regs + HF_PORCHL_REG);
+	val = (mode->hsync_start - mode->hdisplay) >> 8;
+	writel(val, edp->regs + HF_PORCHH_REG);
+	val = (mode->hsync_end - mode->hsync_start) & 0xff;
+	writel(val, edp->regs + HSYNC_CFGL_REG);
+	val = (mode->hsync_end - mode->hsync_start) >> 8;
+	writel(val, edp->regs + HSYNC_CFGH_REG);
+	val = (mode->htotal - mode->hsync_end) & 0xff;
+	writel(val, edp->regs + HB_PORCHL_REG);
+	val = (mode->htotal - mode->hsync_end)  >> 8;
+	writel(val, edp->regs + HB_PORCHH_REG);
+
+	val = BIST_EN | BIST_WH_64 | BIST_TYPE_COLR_BAR;
+	writel(val, edp->regs + VIDEO_CTL_4);
+
+	val = readl(edp->regs + VIDEO_CTL_10);
+	val &= ~F_SEL;
+	writel(val, edp->regs + VIDEO_CTL_10);
+	return 0;
+}
+
+void rk3288_edp_enable_video_master(struct rk3288_edp *edp, bool enable)
+{
+}
+
+void rk3288_edp_start_video(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + VIDEO_CTL_1);
+	val |= VIDEO_EN;
+	writel(val, edp->regs + VIDEO_CTL_1);
+}
+
+int rk3288_edp_is_video_stream_on(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + SYS_CTL_3);
+	writel(val, edp->regs + SYS_CTL_3);
+
+	val = readl(edp->regs + SYS_CTL_3);
+	if (!(val & STRM_VALID)) {
+		dev_dbg(edp->dev, "Input video stream is not detected.\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+void rk3288_edp_config_video_slave_mode(struct rk3288_edp *edp,
+					struct video_info *video_info)
+{
+	u32 val;
+
+	val = readl(edp->regs + FUNC_EN_1);
+	val &= ~(VID_FIFO_FUNC_EN_N | VID_CAP_FUNC_EN_N);
+	writel(val, edp->regs + FUNC_EN_1);
+
+	val = readl(edp->regs + VIDEO_CTL_10);
+	val &= ~INTERACE_SCAN_CFG;
+	val |= (video_info->interlaced << 2);
+	writel(val, edp->regs + VIDEO_CTL_10);
+
+	val = readl(edp->regs + VIDEO_CTL_10);
+	val &= ~VSYNC_POLARITY_CFG;
+	val |= (video_info->v_sync_polarity << 1);
+	writel(val, edp->regs + VIDEO_CTL_10);
+
+	val = readl(edp->regs + VIDEO_CTL_10);
+	val &= ~HSYNC_POLARITY_CFG;
+	val |= (video_info->h_sync_polarity << 0);
+	writel(val, edp->regs + VIDEO_CTL_10);
+}
+
+void rk3288_edp_enable_scrambling(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + TRAINING_PTN_SET);
+	val &= ~SCRAMBLING_DISABLE;
+	writel(val, edp->regs + TRAINING_PTN_SET);
+}
+
+void rk3288_edp_disable_scrambling(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + TRAINING_PTN_SET);
+	val |= SCRAMBLING_DISABLE;
+	writel(val, edp->regs + TRAINING_PTN_SET);
+}
+
+enum dp_irq_type rk3288_edp_get_irq_type(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	/* Parse hotplug interrupt status register */
+	val = readl(edp->regs + COMMON_INT_STA_4);
+	if (val & PLUG)
+		return DP_IRQ_TYPE_HP_CABLE_IN;
+
+	if (val & HPD_LOST)
+		return DP_IRQ_TYPE_HP_CABLE_OUT;
+
+	if (val & HOTPLUG_CHG)
+		return DP_IRQ_TYPE_HP_CHANGE;
+
+	return DP_IRQ_TYPE_UNKNOWN;
+}
+
+void rk3288_edp_clear_hotplug_interrupts(struct rk3288_edp *edp)
+{
+	u32 val;
+
+	val = HOTPLUG_CHG | HPD_LOST | PLUG;
+	writel(val, edp->regs + COMMON_INT_STA_4);
+
+	val = INT_HPD;
+	writel(val, edp->regs + DP_INT_STA);
+}
diff --git a/drivers/gpu/drm/rockchip/connector/rk3288_dp_reg.h b/drivers/gpu/drm/rockchip/connector/rk3288_dp_reg.h
new file mode 100644
index 0000000..baf8d90
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/connector/rk3288_dp_reg.h
@@ -0,0 +1,378 @@
+/*
+* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+* Author:
+*      yxj <yxj-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+*      cym <cym-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+*
+* based on exynos_dp_reg.h
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*/
+
+#ifndef _RK32XX_DP_REG_H
+#define _RK32XX_DP_REG_H
+
+#define DP_VERSION				0x10
+
+#define TX_SW_RST				0x14
+
+#define FUNC_EN_1				0x18
+#define VID_CAP_FUNC_EN_N			BIT(6)
+#define VID_FIFO_FUNC_EN_N			BIT(5)
+#define AUD_FIFO_FUNC_EN_N			BIT(4)
+#define AUD_FUNC_EN_N				BIT(3)
+#define HDCP_FUNC_EN_N				BIT(2)
+#define SW_FUNC_EN_N				BIT(0)
+
+#define FUNC_EN_2				0x1C
+#define SSC_FUNC_EN_N				BIT(7)
+#define AUX_FUNC_EN_N				BIT(2)
+#define SERDES_FIFO_FUNC_EN_N			BIT(1)
+#define LS_CLK_DOMAIN_FUNC_EN_N			BIT(0)
+
+#define VIDEO_CTL_1				0x20
+#define VIDEO_EN				BIT(7)
+#define VIDEO_MUTE				BIT(6)
+
+#define VIDEO_CTL_2				0x24
+#define IN_D_RANGE_MASK				(0x1 << 7)
+#define IN_D_RANGE_SHIFT			(7)
+#define IN_D_RANGE_CEA				(0x1 << 7)
+#define IN_D_RANGE_VESA				(0x0 << 7)
+#define IN_BPC_MASK				(0x7 << 4)
+#define IN_BPC_SHIFT				(4)
+#define IN_BPC_12_BITS				(0x3 << 4)
+#define IN_BPC_10_BITS				(0x2 << 4)
+#define IN_BPC_8_BITS				(0x1 << 4)
+#define IN_BPC_6_BITS				(0x0 << 4)
+#define IN_COLOR_F_MASK				(0x3 << 0)
+#define IN_COLOR_F_SHIFT			(0)
+#define IN_COLOR_F_YCBCR444			(0x2 << 0)
+#define IN_COLOR_F_YCBCR422			(0x1 << 0)
+#define IN_COLOR_F_RGB				(0x0 << 0)
+
+#define VIDEO_CTL_3				0x28
+#define IN_YC_COEFFI_MASK			(0x1 << 7)
+#define IN_YC_COEFFI_SHIFT			(7)
+#define IN_YC_COEFFI_ITU709			(0x1 << 7)
+#define IN_YC_COEFFI_ITU601			(0x0 << 7)
+#define VID_CHK_UPDATE_TYPE_MASK		(0x1 << 4)
+#define VID_CHK_UPDATE_TYPE_SHIFT		(4)
+#define VID_CHK_UPDATE_TYPE_1			(0x1 << 4)
+#define VID_CHK_UPDATE_TYPE_0			(0x0 << 4)
+
+#define VIDEO_CTL_4				0x2c
+#define BIST_EN					(0x1 << 3)
+#define BIST_WH_64				(0x1 << 2)
+#define BIST_WH_32				(0x0 << 2)
+#define BIST_TYPE_COLR_BAR			(0x0 << 0)
+#define BIST_TYPE_GRAY_BAR			(0x1 << 0)
+#define BIST_TYPE_MOBILE_BAR			(0x2 << 0)
+
+#define VIDEO_CTL_8				0x3C
+#define VID_HRES_TH(x)				(((x) & 0xf) << 4)
+#define VID_VRES_TH(x)				(((x) & 0xf) << 0)
+
+#define VIDEO_CTL_10				0x44
+#define F_SEL					(0x1 << 4)
+#define INTERACE_SCAN_CFG			(0x1 << 2)
+#define VSYNC_POLARITY_CFG			(0x1 << 1)
+#define HSYNC_POLARITY_CFG			(0x1 << 0)
+
+#define TOTAL_LINE_CFG_L			0x48
+#define TOTAL_LINE_CFG_H			0x4c
+#define ATV_LINE_CFG_L			0x50
+#define ATV_LINE_CFG_H			0x54
+#define VF_PORCH_REG			0x58
+#define VSYNC_CFG_REG			0x5c
+#define VB_PORCH_REG			0x60
+#define TOTAL_PIXELL_REG		0x64
+#define TOTAL_PIXELH_REG		0x68
+#define ATV_PIXELL_REG			0x6c
+#define ATV_PIXELH_REG			0x70
+#define HF_PORCHL_REG			0x74
+#define HF_PORCHH_REG			0x78
+#define HSYNC_CFGL_REG			0x7c
+#define HSYNC_CFGH_REG			0x80
+#define HB_PORCHL_REG			0x84
+#define HB_PORCHH_REG			0x88
+
+
+#define SSC_REG					0x104
+#define TX_REG_COMMON				0x114
+#define DP_AUX					0x120
+#define DP_BIAS					0x124
+
+#define PLL_REG_1				0xfc
+#define REF_CLK_24M				(0x1 << 1)
+#define REF_CLK_27M				(0x0 << 1)
+
+#define PLL_REG_2				0x9e4
+#define PLL_REG_3				0x9e8
+#define PLL_REG_4				0x9ec
+#define PLL_REG_5				0xa00
+#define DP_PWRDN				0x12c
+#define PD_INC_BG				BIT(7)
+#define PD_EXP_BG				BIT(6)
+#define PD_AUX					BIT(5)
+#define PD_PLL					BIT(4)
+#define PD_CH3					BIT(3)
+#define PD_CH2					BIT(2)
+#define PD_CH1					BIT(1)
+#define PD_CH0					BIT(0)
+
+#define DP_RESERVE2				0x134
+
+#define LANE_MAP				0x35C
+#define LANE3_MAP_LOGIC_LANE_0			(0x0 << 6)
+#define LANE3_MAP_LOGIC_LANE_1			(0x1 << 6)
+#define LANE3_MAP_LOGIC_LANE_2			(0x2 << 6)
+#define LANE3_MAP_LOGIC_LANE_3			(0x3 << 6)
+#define LANE2_MAP_LOGIC_LANE_0			(0x0 << 4)
+#define LANE2_MAP_LOGIC_LANE_1			(0x1 << 4)
+#define LANE2_MAP_LOGIC_LANE_2			(0x2 << 4)
+#define LANE2_MAP_LOGIC_LANE_3			(0x3 << 4)
+#define LANE1_MAP_LOGIC_LANE_0			(0x0 << 2)
+#define LANE1_MAP_LOGIC_LANE_1			(0x1 << 2)
+#define LANE1_MAP_LOGIC_LANE_2			(0x2 << 2)
+#define LANE1_MAP_LOGIC_LANE_3			(0x3 << 2)
+#define LANE0_MAP_LOGIC_LANE_0			(0x0 << 0)
+#define LANE0_MAP_LOGIC_LANE_1			(0x1 << 0)
+#define LANE0_MAP_LOGIC_LANE_2			(0x2 << 0)
+#define LANE0_MAP_LOGIC_LANE_3			(0x3 << 0)
+
+#define ANALOG_CTL_2				0x374
+#define SEL_24M					(0x1 << 3)
+
+#define AUX_HW_RETRY_CTL			0x390
+
+#define INT_STA					0x3c0
+
+#define COMMON_INT_STA_1			0x3C4
+#define VSYNC_DET				BIT(7)
+#define PLL_LOCK_CHG				BIT(6)
+#define SPDIF_ERR				BIT(5)
+#define SPDIF_UNSTBL				BIT(4)
+#define VID_FORMAT_CHG				BIT(3)
+#define AUD_CLK_CHG				BIT(2)
+#define VID_CLK_CHG				BIT(1)
+#define SW_INT					BIT(0)
+
+#define COMMON_INT_STA_2			0x3C8
+#define ENC_EN_CHG				BIT(6)
+#define HW_BKSV_RDY				BIT(3)
+#define HW_SHA_DONE				BIT(2)
+#define HW_AUTH_STATE_CHG			BIT(1)
+#define HW_AUTH_DONE				BIT(0)
+
+#define COMMON_INT_STA_3			0x3CC
+#define AFIFO_UNDER				BIT(7)
+#define AFIFO_OVER				BIT(6)
+#define R0_CHK_FLAG				BIT(5)
+
+#define COMMON_INT_STA_4			0x3D0
+#define PSR_ACTIVE				BIT(7)
+#define PSR_INACTIVE				BIT(6)
+#define SPDIF_BI_PHASE_ERR			BIT(5)
+#define HOTPLUG_CHG				BIT(2)
+#define HPD_LOST				BIT(1)
+#define PLUG					BIT(0)
+
+#define DP_INT_STA				0x3DC
+#define INT_HPD					BIT(6)
+#define HW_LT_DONE				BIT(5)
+#define SINK_LOST				BIT(3)
+#define LINK_LOST				BIT(2)
+#define RPLY_RECEIV				BIT(1)
+#define AUX_ERR					BIT(0)
+
+#define COMMON_INT_MASK_1			0x3E0
+#define COMMON_INT_MASK_2			0x3E4
+#define COMMON_INT_MASK_3			0x3E8
+#define COMMON_INT_MASK_4			0x3EC
+#define DP_INT_STA_MASK				0x3F8
+
+#define INT_CTL					0x3FC
+#define SOFT_INT_CTRL				BIT(2)
+#define INT_POL					BIT(0)
+
+#define SYS_CTL_1				0x600
+#define DET_STA					BIT(2)
+#define FORCE_DET				BIT(1)
+#define DET_CTRL				BIT(0)
+
+#define SYS_CTL_2				0x604
+#define CHA_CRI(x)				(((x) & 0xf) << 4)
+#define CHA_STA					BIT(2)
+#define FORCE_CHA				BIT(1)
+#define CHA_CTRL				BIT(0)
+
+#define SYS_CTL_3				0x608
+#define HPD_STATUS				BIT(6)
+#define F_HPD					BIT(5)
+#define HPD_CTRL				BIT(4)
+#define HDCP_RDY				BIT(3)
+#define STRM_VALID				BIT(2)
+#define F_VALID					BIT(1)
+#define VALID_CTRL				BIT(0)
+
+#define SYS_CTL_4				0x60C
+#define FIX_M_AUD				BIT(4)
+#define ENHANCED				BIT(3)
+#define FIX_M_VID				BIT(2)
+#define M_VID_UPDATE_CTRL			BIT(0)
+
+#define PKT_SEND_CTL				0x640
+#define HDCP_CTL				0x648
+
+#define LINK_BW_SET				0x680
+#define LANE_CNT_SET				0x684
+
+#define TRAINING_PTN_SET			0x688
+#define SCRAMBLING_DISABLE			(0x1 << 5)
+#define SCRAMBLING_ENABLE			(0x0 << 5)
+#define LINK_QUAL_PATTERN_SET_MASK		(0x7 << 2)
+#define LINK_QUAL_PATTERN_SET_HBR2		(0x5 << 2)
+#define LINK_QUAL_PATTERN_SET_80BIT		(0x4 << 2)
+#define LINK_QUAL_PATTERN_SET_PRBS7		(0x3 << 2)
+#define LINK_QUAL_PATTERN_SET_D10_2		(0x1 << 2)
+#define LINK_QUAL_PATTERN_SET_DISABLE		(0x0 << 2)
+#define SW_TRAINING_PATTERN_SET_MASK		(0x3 << 0)
+#define SW_TRAINING_PATTERN_SET_PTN2		(0x2 << 0)
+#define SW_TRAINING_PATTERN_SET_PTN1		(0x1 << 0)
+#define SW_TRAINING_PATTERN_SET_DISABLE		(0x0 << 0)
+
+#define LN0_LINK_TRAINING_CTL			0x68C
+#define LN1_LINK_TRAINING_CTL			0x690
+#define LN2_LINK_TRAINING_CTL			0x694
+#define LN3_LINK_TRAINING_CTL			0x698
+
+#define HW_LT_CTL				0x6a0
+#define HW_LT_ERR_CODE_MASK			0x70
+#define HW_LT_EN				BIT(0)
+
+#define DEBUG_CTL				0x6C0
+#define PLL_LOCK				BIT(4)
+#define F_PLL_LOCK				BIT(3)
+#define PLL_LOCK_CTRL				BIT(2)
+#define POLL_EN					BIT(1)
+#define PN_INV					BIT(0)
+
+#define HPD_DEGLITCH_L				0x6C4
+#define HPD_DEGLITCH_H				0x6C8
+#define LINK_DEBUG_CTL				0x6E0
+
+#define M_VID_0					0x700
+#define M_VID_1					0x704
+#define M_VID_2					0x708
+#define N_VID_0					0x70C
+#define N_VID_1					0x710
+#define N_VID_2					0x714
+
+#define VIDEO_FIFO_THRD				0x730
+#define AUDIO_MARGIN				0x73C
+
+#define M_VID_GEN_FILTER_TH			0x764
+#define M_AUD_GEN_FILTER_TH			0x778
+
+#define AUX_CH_STA				0x780
+#define AUX_BUSY				(0x1 << 4)
+#define AUX_STATUS_MASK				(0xf << 0)
+
+#define AUX_CH_DEFER_CTL			0x788
+#define DEFER_CTRL_EN				(0x1 << 7)
+#define DEFER_COUNT(x)				(((x) & 0x7f) << 0)
+
+#define AUX_RX_COMM_I2C_DEFER			(0x2 << 2)
+#define AUX_RX_COMM_AUX_DEFER			(0x2 << 0)
+
+#define AUX_RX_COMM				0x78C
+#define BUFFER_DATA_CTL				0x790
+#define BUF_CLR					(0x1 << 7)
+#define BUF_HAVE_DATA				(0x1 << 4)
+#define BUF_DATA_COUNT(x)			(((x) & 0xf) << 0)
+
+#define AUX_CH_CTL_1				0x794
+#define AUX_LENGTH(x)				(((x - 1) & 0xf) << 4)
+#define AUX_TX_COMM_MASK			(0xf << 0)
+#define AUX_TX_COMM_DP_TRANSACTION		(0x1 << 3)
+#define AUX_TX_COMM_I2C_TRANSACTION		(0x0 << 3)
+#define AUX_TX_COMM_MOT				(0x1 << 2)
+#define AUX_TX_COMM_WRITE			(0x0 << 0)
+#define AUX_TX_COMM_READ			(0x1 << 0)
+
+#define DP_AUX_ADDR_7_0				0x798
+#define DP_AUX_ADDR_15_8			0x79C
+#define DP_AUX_ADDR_19_16			0x7A0
+
+#define AUX_CH_CTL_2				0x7A4
+#define PD_AUX_IDLE				BIT(3)
+#define ADDR_ONLY				BIT(1)
+#define AUX_EN					BIT(0)
+
+#define BUF_DATA_0				0x7C0
+
+#define SOC_GENERAL_CTL				0x800
+
+/* TX_SW_RESET */
+#define RST_DP_TX				BIT(0)
+
+/* ANALOG_CTL_1 */
+#define TX_TERMINAL_CTRL_50_OHM			BIT(4)
+
+/* ANALOG_CTL_3 */
+#define DRIVE_DVDD_BIT_1_0625V			(0x4 << 5)
+#define VCO_BIT_600_MICRO			(0x5 << 0)
+
+/* PLL_FILTER_CTL_1 */
+#define PD_RING_OSC				(0x1 << 6)
+#define AUX_TERMINAL_CTRL_37_5_OHM		(0x0 << 4)
+#define AUX_TERMINAL_CTRL_45_OHM		(0x1 << 4)
+#define AUX_TERMINAL_CTRL_50_OHM		(0x2 << 4)
+#define AUX_TERMINAL_CTRL_65_OHM		(0x3 << 4)
+#define TX_CUR1_2X				(0x1 << 2)
+#define TX_CUR_16_MA				(0x3 << 0)
+
+/* TX_AMP_TUNING_CTL */
+#define CH3_AMP_SHIFT				(24)
+#define CH3_AMP_400_MV				(0x0 << 24)
+#define CH2_AMP_SHIFT				(16)
+#define CH2_AMP_400_MV				(0x0 << 16)
+#define CH1_AMP_SHIFT				(8)
+#define CH1_AMP_400_MV				(0x0 << 8)
+#define CH0_AMP_SHIFT				(0)
+#define CH0_AMP_400_MV				(0x0 << 0)
+
+/* AUX_HW_RETRY_CTL */
+#define AUX_BIT_PERIOD_EXPECTED_DELAY(x)	(((x) & 0x7) << 8)
+#define AUX_HW_RETRY_INTERVAL_MASK		(0x3 << 3)
+#define AUX_HW_RETRY_INTERVAL_600_MICROSECONDS	(0x0 << 3)
+#define AUX_HW_RETRY_INTERVAL_800_MICROSECONDS	(0x1 << 3)
+#define AUX_HW_RETRY_INTERVAL_1000_MICROSECONDS	(0x2 << 3)
+#define AUX_HW_RETRY_INTERVAL_1800_MICROSECONDS	(0x3 << 3)
+#define AUX_HW_RETRY_COUNT_SEL(x)		(((x) & 0x7) << 0)
+
+/* LN0_LINK_TRAINING_CTL */
+#define PRE_EMPHASIS_SET_MASK			(0x3 << 3)
+#define PRE_EMPHASIS_SET_SHIFT			(3)
+
+/* PLL_CTL */
+#define DP_PLL_PD				(0x1 << 7)
+#define DP_PLL_RESET				(0x1 << 6)
+#define DP_PLL_LOOP_BIT_DEFAULT			(0x1 << 4)
+#define DP_PLL_REF_BIT_1_1250V			(0x5 << 0)
+#define DP_PLL_REF_BIT_1_2500V			(0x7 << 0)
+
+/* PHY_TEST */
+#define MACRO_RST				BIT(5)
+#define CH1_TEST				BIT(1)
+#define CH0_TEST				BIT(0)
+
+#define AUX_ADDR_7_0(x)			(((x) >> 0) & 0xff)
+#define AUX_ADDR_15_8(x)		(((x) >> 8) & 0xff)
+#define AUX_ADDR_19_16(x)		(((x) >> 16) & 0x0f)
+
+#endif
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index 59187aa..4f1205f 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -564,6 +564,12 @@ static int rockchip_drm_init(void)
 		goto out_lvds;
 #endif
 
+#ifdef CONFIG_RK3288_DP
+	ret = platform_driver_register(&rk3288_edp_driver);
+	if (ret)
+		goto out_edp;
+#endif
+
 	rockchip_drm_pdev = platform_device_register_simple("rockchip-drm", -1,
 							    NULL, 0);
 	if (IS_ERR(rockchip_drm_pdev)) {
@@ -580,6 +586,10 @@ static int rockchip_drm_init(void)
 out_drm_driver:
 	platform_device_unregister(rockchip_drm_pdev);
 out_drm_pdev:
+#ifdef CONFIG_RK3288_DP
+	platform_driver_unregister(&rk3288_edp_driver);
+out_edp:
+#endif
 #ifdef CONFIG_RK3288_LVDS
 	platform_driver_unregister(&rk3288_lvds_driver);
 out_lvds:
@@ -596,6 +606,9 @@ static void rockchip_drm_exit(void)
 {
 	platform_device_unregister(rockchip_drm_pdev);
 	platform_driver_unregister(&rockchip_drm_platform_driver);
+#ifdef CONFIG_RK3288_DP
+	platform_driver_unregister(&rk3288_edp_driver);
+#endif
 #ifdef CONFIG_RK3288_LVDS
 	platform_driver_unregister(&rk3288_lvds_driver);
 #endif
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index d28f4dc..565276e 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -128,4 +128,7 @@ extern struct platform_driver rockchip_lcdc_platform_driver;
 #ifdef CONFIG_RK3288_LVDS
 extern struct platform_driver rk3288_lvds_driver;
 #endif
+#ifdef CONFIG_RK3288_DP
+extern struct platform_driver rk3288_edp_driver;
+#endif
 #endif /* _ROCKCHIP_DRM_DRV_H_ */
-- 
1.7.9.5

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

* Re: [PATCH 1/9] drm: Add drm driver for Rockchip Socs
  2014-08-04  4:45 ` [PATCH 1/9] drm: " mark yao
@ 2014-08-04 14:46     ` Daniel Vetter
  2014-08-07  8:32   ` mark yao
  1 sibling, 0 replies; 38+ messages in thread
From: Daniel Vetter @ 2014-08-04 14:46 UTC (permalink / raw)
  To: mark yao
  Cc: heiko, Rob Clark, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Randy Dunlap, David Airlie,
	Grant Likely, Greg Kroah-Hartman, John Stultz, Rom Lemarchand,
	huangtao, devicetree, zwl, cym, linux-doc, xw, linux-api, zyw,
	zhangqing, linux-kernel, dri-devel, kever.yang, cf, xjq, xxm,
	yxj, wxt, kfx

On Mon, Aug 04, 2014 at 12:45:07PM +0800, mark yao wrote:
> This patch is a DRM Driver for Rockchip Socs, driver provides an abstraction
> for the graphics hardware, such as lcd controller and connector interface.
> 
> Signed-off-by: mark yao <yzq@rock-chips.com>
> ---

Just a quick drive-by comment below, don't consider this a full review by
far ;-)

> +static int lcdc_bind(struct device *dev, struct device *master, void *data)
> +{
> +	struct drm_device *drm_dev = data;
> +	struct rockchip_drm_private *private = drm_dev->dev_private;
> +	struct lcdc_context *ctx = dev_get_drvdata(dev);
> +	struct drm_crtc *crtc;
> +
> +	ctx->drm_dev = drm_dev;
> +
> +	ctx->pipe = rockchip_drm_pipe_get(dev);
> +	ctx->dpms = DRM_MODE_DPMS_OFF;
> +	crtc = &ctx->crtc;
> +
> +	private->crtc[ctx->pipe] = crtc;
> +	ctx->plane = rockchip_plane_init(drm_dev, 1 << ctx->pipe, true);
> +	drm_crtc_init(drm_dev, crtc, &rockchip_crtc_funcs);

This function is deprecated, please use the _with_planes versions so that
this new driver supports universal planes properly.

Thanks, Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* Re: [PATCH 1/9] drm: Add drm driver for Rockchip Socs
@ 2014-08-04 14:46     ` Daniel Vetter
  0 siblings, 0 replies; 38+ messages in thread
From: Daniel Vetter @ 2014-08-04 14:46 UTC (permalink / raw)
  To: mark yao
  Cc: heiko, Rob Clark, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Randy Dunlap, David Airlie,
	Grant Likely, Greg Kroah-Hartman, John Stultz, Rom Lemarchand,
	huangtao, devicetree, zwl, cym, linux-doc, xw, linux-api, zyw,
	zhangqing, linux-kernel, dri-devel, kever.yang, cf, xjq, xxm,
	yxj, wxt, kfx

On Mon, Aug 04, 2014 at 12:45:07PM +0800, mark yao wrote:
> This patch is a DRM Driver for Rockchip Socs, driver provides an abstraction
> for the graphics hardware, such as lcd controller and connector interface.
> 
> Signed-off-by: mark yao <yzq@rock-chips.com>
> ---

Just a quick drive-by comment below, don't consider this a full review by
far ;-)

> +static int lcdc_bind(struct device *dev, struct device *master, void *data)
> +{
> +	struct drm_device *drm_dev = data;
> +	struct rockchip_drm_private *private = drm_dev->dev_private;
> +	struct lcdc_context *ctx = dev_get_drvdata(dev);
> +	struct drm_crtc *crtc;
> +
> +	ctx->drm_dev = drm_dev;
> +
> +	ctx->pipe = rockchip_drm_pipe_get(dev);
> +	ctx->dpms = DRM_MODE_DPMS_OFF;
> +	crtc = &ctx->crtc;
> +
> +	private->crtc[ctx->pipe] = crtc;
> +	ctx->plane = rockchip_plane_init(drm_dev, 1 << ctx->pipe, true);
> +	drm_crtc_init(drm_dev, crtc, &rockchip_crtc_funcs);

This function is deprecated, please use the _with_planes versions so that
this new driver supports universal planes properly.

Thanks, Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* Re: [PATCH 8/9] Add devicetree bindings for Rockchip Soc EDP
  2014-08-04  4:55 ` [PATCH 8/9] Add devicetree bindings for Rockchip Soc EDP mark yao
@ 2014-08-04 19:39     ` Heiko Stübner
  0 siblings, 0 replies; 38+ messages in thread
From: Heiko Stübner @ 2014-08-04 19:39 UTC (permalink / raw)
  To: mark yao
  Cc: Rob Clark, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, Randy Dunlap, David Airlie, Grant Likely,
	Greg Kroah-Hartman, John Stultz, Rom Lemarchand, devicetree,
	linux-doc, linux-kernel, dri-devel, linux-api, olof, djkurtz,
	xjq, kfx, cym, cf, zyw, zwl, xxm, huangtao, kever.yang,
	zhangqing, yxj, wxt, xw

Am Montag, 4. August 2014, 12:55:59 schrieb mark yao:
> Signed-off-by: mark yao <yzq@rock-chips.com>
> ---
>  .../devicetree/bindings/video/rockchip-panel.txt   |   34
> ++++++++++++++++++++ 1 file changed, 34 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/video/rockchip-panel.txt
> b/Documentation/devicetree/bindings/video/rockchip-panel.txt index
> f599806..f6d80f6 100644
> --- a/Documentation/devicetree/bindings/video/rockchip-panel.txt
> +++ b/Documentation/devicetree/bindings/video/rockchip-panel.txt
> @@ -80,3 +80,37 @@ Example:
>  		rockchip,data-width = <24>;
>  		rockchip,panel = <&panel>;
>  	};
> +
> +Rockchip RK3288 EDP interface
> +================================
> +Required properties:
> +-compatible: "rockchip,rk3288-edp";
> +
> +- reg: physical base address of the controller and length
> +- clocks: from common clock binding: handle to dp clock.
> +	of memory mapped region.
> +- clock-names: from common clock binding:
> +	Required elements: "clk_edp"
> +			"clk_edp_24m"
> +			"clk_edp_24m_parent"
> +			"pclk_edp"
> +- rockchip,grf: this soc should set GRF regs, so need get grf here.
> +- resets: Must contain an entry for each entry in reset-names.
> +	See ../reset/reset.txt for details.
> +- reset-names: Must include the following entries:
> +  - edp
> +- rockchip,panel: required a panel node
> +
> +Example:
> +	edp: edp@ff970000 {
> +		compatible = "rockchip,rk3288-edp";
> +                reg = <0xff970000 0x4000>;
> +                interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
> +                clocks = <&cru SCLK_EDP>, <&cru SCLK_EDP_24M>, <&cru
> PCLK_EDP_CTRL>, <&xin24m>; +                clock-names = "clk_edp",
> "clk_edp_24m", "pclk_edp", "clk_edp_24m_parent"; +

clk_epd_24m_parent is not part of the hardware, so instead of referencing the 
target-parent as part of the device clocks, the new
	"clk: Support for clock parents and rates assigned from device tree"
should be used for setting the target parent - so the re-parenting code should 
also move out of the driver.

This commit [0] is part of the clk-pull request and thus will most likely be 
part of 3.17.


Heiko

[0] 
http://git.linaro.org/people/mike.turquette/linux.git/commitdiff/86be408bfbd846fab3c4ac21d6f9298bd2e4b790?hp=09575693a2511b5ddae0105546e0d9cefc936e34



> +                rockchip,grf = <&grf>;
> +                resets = <&cru 111>;
> +                reset-names = "edp";
> +		rockchip,panel = <&panel>;
> +	};


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

* Re: [PATCH 8/9] Add devicetree bindings for Rockchip Soc EDP
@ 2014-08-04 19:39     ` Heiko Stübner
  0 siblings, 0 replies; 38+ messages in thread
From: Heiko Stübner @ 2014-08-04 19:39 UTC (permalink / raw)
  To: mark yao
  Cc: Rob Clark, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, Randy Dunlap, David Airlie, Grant Likely,
	Greg Kroah-Hartman, John Stultz, Rom Lemarchand, devicetree,
	linux-doc, linux-kernel, dri-devel, linux-api, olof, djkurtz,
	xjq, kfx, cym, cf, zyw, zwl, xxm, huangtao, kever.yang,
	zhangqing, yxj, wxt

Am Montag, 4. August 2014, 12:55:59 schrieb mark yao:
> Signed-off-by: mark yao <yzq@rock-chips.com>
> ---
>  .../devicetree/bindings/video/rockchip-panel.txt   |   34
> ++++++++++++++++++++ 1 file changed, 34 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/video/rockchip-panel.txt
> b/Documentation/devicetree/bindings/video/rockchip-panel.txt index
> f599806..f6d80f6 100644
> --- a/Documentation/devicetree/bindings/video/rockchip-panel.txt
> +++ b/Documentation/devicetree/bindings/video/rockchip-panel.txt
> @@ -80,3 +80,37 @@ Example:
>  		rockchip,data-width = <24>;
>  		rockchip,panel = <&panel>;
>  	};
> +
> +Rockchip RK3288 EDP interface
> +================================
> +Required properties:
> +-compatible: "rockchip,rk3288-edp";
> +
> +- reg: physical base address of the controller and length
> +- clocks: from common clock binding: handle to dp clock.
> +	of memory mapped region.
> +- clock-names: from common clock binding:
> +	Required elements: "clk_edp"
> +			"clk_edp_24m"
> +			"clk_edp_24m_parent"
> +			"pclk_edp"
> +- rockchip,grf: this soc should set GRF regs, so need get grf here.
> +- resets: Must contain an entry for each entry in reset-names.
> +	See ../reset/reset.txt for details.
> +- reset-names: Must include the following entries:
> +  - edp
> +- rockchip,panel: required a panel node
> +
> +Example:
> +	edp: edp@ff970000 {
> +		compatible = "rockchip,rk3288-edp";
> +                reg = <0xff970000 0x4000>;
> +                interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
> +                clocks = <&cru SCLK_EDP>, <&cru SCLK_EDP_24M>, <&cru
> PCLK_EDP_CTRL>, <&xin24m>; +                clock-names = "clk_edp",
> "clk_edp_24m", "pclk_edp", "clk_edp_24m_parent"; +

clk_epd_24m_parent is not part of the hardware, so instead of referencing the 
target-parent as part of the device clocks, the new
	"clk: Support for clock parents and rates assigned from device tree"
should be used for setting the target parent - so the re-parenting code should 
also move out of the driver.

This commit [0] is part of the clk-pull request and thus will most likely be 
part of 3.17.


Heiko

[0] 
http://git.linaro.org/people/mike.turquette/linux.git/commitdiff/86be408bfbd846fab3c4ac21d6f9298bd2e4b790?hp=09575693a2511b5ddae0105546e0d9cefc936e34



> +                rockchip,grf = <&grf>;
> +                resets = <&cru 111>;
> +                reset-names = "edp";
> +		rockchip,panel = <&panel>;
> +	};


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

* [PATCH 1/9] drm: Add drm driver for Rockchip Socs
  2014-08-04  4:45 ` [PATCH 1/9] drm: " mark yao
  2014-08-04 14:46     ` Daniel Vetter
@ 2014-08-07  8:32   ` mark yao
  1 sibling, 0 replies; 38+ messages in thread
From: mark yao @ 2014-08-07  8:32 UTC (permalink / raw)
  To: heiko, Rob Clark, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Randy Dunlap, David Airlie,
	Grant Likely, Greg Kroah-Hartman, John Stultz, Rom Lemarchand
  Cc: devicetree, linux-doc, linux-kernel, dri-devel, linux-api, olof,
	djkurtz, xjq, kfx, cym, cf, zyw, zwl, xxm, huangtao, kever.yang,
	zhangqing, yxj, wxt, xw, mark yao

This patch is a DRM Driver for Rockchip Socs, driver provides an abstraction
for the graphics hardware, such as lcd controller and connector interface.

Signed-off-by: mark yao <yzq@rock-chips.com>
---
changes since v1:

Adviced by Daniel Vetter:
- Switch to universal plane API's
---
 drivers/gpu/drm/Kconfig                           |    2 +
 drivers/gpu/drm/Makefile                          |    1 +
 drivers/gpu/drm/rockchip/Kconfig                  |   40 ++
 drivers/gpu/drm/rockchip/Makefile                 |   12 +
 drivers/gpu/drm/rockchip/rockchip_drm_connector.c |  412 ++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_connector.h |   36 +
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c       |  600 +++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h       |  128 ++++
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c        |   48 ++
 drivers/gpu/drm/rockchip/rockchip_drm_fb.h        |   28 +
 drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c     |   63 ++
 drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h     |   24 +
 drivers/gpu/drm/rockchip/rockchip_drm_gem.c       |  163 +++++
 drivers/gpu/drm/rockchip/rockchip_drm_gem.h       |   40 ++
 drivers/gpu/drm/rockchip/rockchip_drm_lcdc.c      |  722 +++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_lcdc.h      |  131 ++++
 include/uapi/drm/rockchip_drm.h                   |  110 ++++
 17 files changed, 2560 insertions(+)
 create mode 100644 drivers/gpu/drm/rockchip/Kconfig
 create mode 100644 drivers/gpu/drm/rockchip/Makefile
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_connector.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_connector.h
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_drv.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_drv.h
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fb.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fb.h
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_gem.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_gem.h
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_lcdc.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_lcdc.h
 create mode 100644 include/uapi/drm/rockchip_drm.h

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index f512004..5951c2c 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -170,6 +170,8 @@ config DRM_SAVAGE
 
 source "drivers/gpu/drm/exynos/Kconfig"
 
+source "drivers/gpu/drm/rockchip/Kconfig"
+
 source "drivers/gpu/drm/vmwgfx/Kconfig"
 
 source "drivers/gpu/drm/gma500/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index dd2ba42..40babd2 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -51,6 +51,7 @@ obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/
 obj-$(CONFIG_DRM_VIA)	+=via/
 obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
 obj-$(CONFIG_DRM_EXYNOS) +=exynos/
+obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/
 obj-$(CONFIG_DRM_GMA500) += gma500/
 obj-$(CONFIG_DRM_UDL) += udl/
 obj-$(CONFIG_DRM_AST) += ast/
diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
new file mode 100644
index 0000000..592e999
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -0,0 +1,40 @@
+config DRM_ROCKCHIP
+	tristate "DRM Support for Rockchip"
+	depends on DRM
+	select DRM_KMS_HELPER
+	select DRM_KMS_FB_HELPER
+	select DRM_KMS_CMA_HELPER
+	select DRM_GEM_CMA_HELPER
+	select DRM_PANEL
+	select FB_CFB_FILLRECT
+	select FB_CFB_COPYAREA
+	select FB_CFB_IMAGEBLIT
+	select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE
+	select VIDEOMODE_HELPERS
+	select OF
+	help
+	  Choose this option if you have a Rockchip soc chipset.
+	  This driver provides kernel mode setting and buffer
+	  management to userspace. This driver does not provides
+	  2D or 3D acceleration; acceleration is performed by other
+	  IP found on the SoC.
+
+config DRM_ROCKCHIP_LCDC
+	bool "ROCKCHIP DRM LCDC"
+	depends on DRM_ROCKCHIP
+	select FB_MODE_HELPERS
+	help
+	  Choose this option if you want to use Rockchip lcdc for DRM.
+	  The driver provides an abstraction for Rockchip lcd controller,
+	  lcd controller is the display interface from memory frame buffer
+	  to display device.
+
+config DRM_ROCKCHIP_CONNECTOR
+	bool "ROCKCHIP DRM CONNECTOR"
+	depends on OF && DRM_ROCKCHIP && DRM_ROCKCHIP_LCDC
+	select FB_MODE_HELPERS
+	select VIDEOMODE_HELPERS
+	help
+	  Choose this option if you want to use Rockchip Primary DISPLAY.
+	  The driver provides an abstraction for Rockchip display devices,
+	  such as lcd plane, lvds, edp , mipi, etc.
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
new file mode 100644
index 0000000..45c9d50
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for the drm device driver.  This driver provides support for the
+# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
+
+ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/rockchip
+
+rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_gem.o \
+		rockchip_drm_fb.o rockchip_drm_fbdev.o
+
+obj-$(CONFIG_DRM_ROCKCHIP_CONNECTOR) += rockchip_drm_connector.o
+obj-$(CONFIG_DRM_ROCKCHIP_LCDC) += rockchip_drm_lcdc.o
+obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_connector.c b/drivers/gpu/drm/rockchip/rockchip_drm_connector.c
new file mode 100644
index 0000000..337b618
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_connector.c
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:mark yao <mark.yao@rock-chips.com>
+ *
+ * based on exynos_drm_dpi.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_panel.h>
+
+#include <linux/component.h>
+
+#include <video/of_videomode.h>
+#include <video/videomode.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_connector.h"
+#include "rockchip_drm_lcdc.h"
+
+struct rockchip_conn_context {
+	struct device *dev;
+
+	struct drm_panel *panel;
+	struct rockchip_connector *conn;
+	struct drm_connector connector;
+	struct drm_encoder encoder;
+
+	struct drm_display_mode mode;
+
+	u32 flags;
+	int type;
+
+	int dpms_mode;
+};
+
+#define connector_to_ctx(c) \
+		container_of(c, struct rockchip_conn_context, connector)
+
+#define encoder_to_ctx(c) \
+		container_of(c, struct rockchip_conn_context, encoder)
+
+static inline int rockchip_convert_conn_type(int type)
+{
+	switch (type) {
+	case ROCKCHIP_DISPLAY_TYPE_RGB:
+	case ROCKCHIP_DISPLAY_TYPE_LVDS:
+		return DRM_MODE_CONNECTOR_LVDS;
+	case ROCKCHIP_DISPLAY_TYPE_EDP:
+		return DRM_MODE_CONNECTOR_eDP;
+	}
+
+	return  DRM_MODE_CONNECTOR_Unknown;
+}
+
+static inline int rockchip_convert_encoder_type(int type)
+{
+	switch (type) {
+	case ROCKCHIP_DISPLAY_TYPE_RGB:
+	case ROCKCHIP_DISPLAY_TYPE_LVDS:
+	case ROCKCHIP_DISPLAY_TYPE_EDP:
+		return DRM_MODE_ENCODER_LVDS;
+	}
+
+	return DRM_MODE_ENCODER_NONE;
+}
+
+static enum drm_connector_status
+rockchip_conn_detect(struct drm_connector *connector, bool force)
+{
+	return true;
+}
+
+static void rockchip_connector_destroy(struct drm_connector *connector)
+{
+	drm_sysfs_connector_remove(connector);
+	drm_connector_cleanup(connector);
+}
+
+static struct drm_connector_funcs rockchip_connector_funcs = {
+	.dpms = drm_helper_connector_dpms,
+	.detect = rockchip_conn_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = rockchip_connector_destroy,
+};
+
+static int rockchip_conn_get_modes(struct drm_connector *connector)
+{
+	struct rockchip_conn_context *ctx = connector_to_ctx(connector);
+	struct drm_panel *panel = ctx->panel;
+
+	return panel->funcs->get_modes(panel);
+}
+
+static struct drm_encoder *
+	rockchip_conn_best_encoder(struct drm_connector *connector)
+{
+	struct rockchip_conn_context *ctx = connector_to_ctx(connector);
+
+	return &ctx->encoder;
+}
+
+static struct drm_connector_helper_funcs rockchip_connector_helper_funcs = {
+	.get_modes = rockchip_conn_get_modes,
+	.best_encoder = rockchip_conn_best_encoder,
+};
+
+static void rockchip_drm_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+	struct rockchip_conn_context *ctx = encoder_to_ctx(encoder);
+	struct rockchip_connector *conn = ctx->conn;
+
+	switch (mode) {
+	case DRM_MODE_DPMS_ON:
+		if (ctx->dpms_mode != DRM_MODE_DPMS_ON) {
+			conn->enable(conn);
+			ctx->panel->funcs->enable(ctx->panel);
+		}
+		break;
+	case DRM_MODE_DPMS_STANDBY:
+	case DRM_MODE_DPMS_SUSPEND:
+	case DRM_MODE_DPMS_OFF:
+		if (ctx->dpms_mode == DRM_MODE_DPMS_ON) {
+			ctx->panel->funcs->disable(ctx->panel);
+			conn->disable(conn);
+		}
+		break;
+	default:
+		break;
+	}
+
+	ctx->dpms_mode = mode;
+}
+
+static bool
+rockchip_drm_encoder_mode_fixup(struct drm_encoder *encoder,
+				const struct drm_display_mode *mode,
+				struct drm_display_mode *adjusted_mode)
+{
+	struct rockchip_panel_special *priv_mode =
+					(void *)adjusted_mode->private;
+	struct rockchip_conn_context *ctx = encoder_to_ctx(encoder);
+
+	priv_mode->out_type = ctx->conn->type;
+
+	return true;
+}
+
+static void rockchip_drm_encoder_mode_set(struct drm_encoder *encoder,
+					  struct drm_display_mode *mode,
+					  struct drm_display_mode *adjusted)
+{
+	/* just set dummy now */
+}
+
+static void rockchip_drm_encoder_prepare(struct drm_encoder *encoder)
+{
+	/* drm framework doesn't check NULL. */
+}
+
+static void rockchip_drm_encoder_commit(struct drm_encoder *encoder)
+{
+	rockchip_drm_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
+}
+
+static void rockchip_drm_encoder_disable(struct drm_encoder *encoder)
+{
+	struct drm_plane *plane;
+	struct drm_device *dev = encoder->dev;
+
+	rockchip_drm_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+
+	/* all planes connected to this encoder should be also disabled. */
+	list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
+		if (plane->crtc && (plane->crtc == encoder->crtc))
+			plane->funcs->disable_plane(plane);
+	}
+}
+
+
+static struct drm_encoder_helper_funcs rockchip_encoder_helper_funcs = {
+	.dpms = rockchip_drm_encoder_dpms,
+	.mode_fixup = rockchip_drm_encoder_mode_fixup,
+	.mode_set = rockchip_drm_encoder_mode_set,
+	.prepare = rockchip_drm_encoder_prepare,
+	.commit = rockchip_drm_encoder_commit,
+	.disable = rockchip_drm_encoder_disable,
+};
+static void rockchip_drm_encoder_destroy(struct drm_encoder *encoder)
+{
+	drm_encoder_cleanup(encoder);
+}
+
+static struct drm_encoder_funcs rockchip_encoder_funcs = {
+	.destroy = rockchip_drm_encoder_destroy,
+};
+
+static unsigned int rockchip_drm_encoder_clones(struct drm_encoder *encoder)
+{
+	struct rockchip_conn_context *ctx = encoder_to_ctx(encoder);
+	struct drm_encoder *clone;
+	struct drm_device *dev = encoder->dev;
+	unsigned int clone_mask = 0;
+	int cnt = 0;
+
+	list_for_each_entry(clone, &dev->mode_config.encoder_list, head) {
+		switch (ctx->type) {
+		case ROCKCHIP_DISPLAY_TYPE_RGB:
+		case ROCKCHIP_DISPLAY_TYPE_LVDS:
+		case ROCKCHIP_DISPLAY_TYPE_EDP:
+		case ROCKCHIP_DISPLAY_TYPE_HDMI:
+			clone_mask |= (1 << (cnt++));
+			break;
+		default:
+			continue;
+		}
+	}
+
+	return clone_mask;
+}
+
+static int rockchip_conn_bind(struct device *dev, struct device *master,
+			      void *data)
+{
+	struct rockchip_conn_context *ctx;
+	unsigned long possible_crtcs = 0;
+	struct drm_encoder *encoder;
+	struct drm_connector *connector;
+	struct device_node *panel_node;
+	struct drm_device *drm_dev = data;
+	int ret;
+
+	ctx = rockchip_drm_component_data_get(dev,
+					      ROCKCHIP_DEVICE_TYPE_CONNECTOR);
+	if (!ctx) {
+		DRM_ERROR("can't find dp content form component\n");
+		return -EINVAL;
+	}
+
+	ret = rockchip_drm_pipe_get(dev);
+	if (ret < 0) {
+		DRM_ERROR("failed to bind display port\n");
+		return ret;
+	}
+
+	possible_crtcs |= 1 << ret;
+
+	encoder = &ctx->encoder;
+	encoder->possible_crtcs = possible_crtcs;
+
+	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
+
+	ret = drm_encoder_init(drm_dev, encoder, &rockchip_encoder_funcs,
+			       rockchip_convert_encoder_type(ctx->type));
+	if (ret) {
+		DRM_ERROR("failed to initialize encoder with drm\n");
+		return ret;
+	}
+
+	drm_encoder_helper_add(encoder, &rockchip_encoder_helper_funcs);
+
+	connector = &ctx->connector;
+	connector->polled = DRM_CONNECTOR_POLL_HPD;
+	connector->dpms = DRM_MODE_DPMS_OFF;
+
+	ret = drm_connector_init(drm_dev, connector,
+				 &rockchip_connector_funcs,
+				 rockchip_convert_conn_type(ctx->type));
+	if (ret) {
+		DRM_ERROR("failed to initialize connector with drm\n");
+		goto err_free_encoder;
+	}
+
+	drm_connector_helper_add(connector,
+				 &rockchip_connector_helper_funcs);
+
+	ret = drm_sysfs_connector_add(connector);
+	if (ret) {
+		DRM_ERROR("failed to add drm_sysfs\n");
+		goto err_free_connector;
+	}
+
+	ret = drm_mode_connector_attach_encoder(connector, encoder);
+	if (ret) {
+		DRM_ERROR("failed to attach connector and encoder\n");
+		goto err_free_connector_sysfs;
+	}
+
+	panel_node = of_parse_phandle(dev->of_node, "rockchip,panel", 0);
+	if (!panel_node) {
+		DRM_ERROR("failed to find diaplay panel\n");
+		goto err_free_connector_sysfs;
+	}
+	ctx->panel = of_drm_find_panel(panel_node);
+	if (!ctx->panel) {
+		DRM_ERROR("failed to find diaplay panel\n");
+		ret = -ENODEV;
+		goto err_free_connector_sysfs;
+	}
+
+	of_node_put(panel_node);
+
+	ret = drm_panel_attach(ctx->panel, connector);
+	if (ret) {
+		DRM_ERROR("failed to attach connector and encoder\n");
+		goto err_free_connector_sysfs;
+	}
+
+	return 0;
+
+err_free_connector_sysfs:
+	drm_sysfs_connector_remove(connector);
+err_free_connector:
+	drm_connector_cleanup(connector);
+err_free_encoder:
+	drm_encoder_cleanup(encoder);
+	return ret;
+}
+
+static void rockchip_conn_unbind(struct device *dev, struct device *master,
+				 void *data)
+{
+	struct rockchip_conn_context *ctx;
+	struct drm_encoder *encoder;
+
+	ctx = rockchip_drm_component_data_get(dev,
+					      ROCKCHIP_DEVICE_TYPE_CONNECTOR);
+	encoder = &ctx->encoder;
+
+	drm_panel_detach(ctx->panel);
+
+	rockchip_drm_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+	encoder->funcs->destroy(encoder);
+	drm_sysfs_connector_remove(&ctx->connector);
+	drm_connector_cleanup(&ctx->connector);
+	drm_encoder_cleanup(encoder);
+
+	rockchip_drm_component_del(dev, ROCKCHIP_DEVICE_TYPE_CONNECTOR);
+}
+
+static const struct component_ops rockchip_conn_component_ops = {
+	.bind = rockchip_conn_bind,
+	.unbind = rockchip_conn_unbind,
+};
+
+void *rockchip_connector_register(struct rockchip_connector *conn)
+{
+	struct rockchip_conn_context *ctx;
+	struct device *dev = conn->dev;
+	int ret;
+
+	if (!dev) {
+		DRM_ERROR("please provide a device at dp register\n");
+		return NULL;
+	}
+
+	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return NULL;
+
+	ret = rockchip_drm_component_add(dev, ROCKCHIP_DEVICE_TYPE_CONNECTOR,
+					 conn->type, ctx);
+	if (ret < 0) {
+		DRM_ERROR("register connector component fail ret =%d\n", ret);
+		return NULL;
+	}
+
+	ctx->conn = conn;
+	ctx->dev = dev;
+	ctx->type = conn->type;
+	ctx->dpms_mode = DRM_MODE_DPMS_OFF;
+
+	ret = component_add(dev, &rockchip_conn_component_ops);
+	if (ret)
+		goto err_del_component;
+
+	DRM_DEBUG_KMS("succes register connector type=%d\n", conn->type);
+
+	return ctx;
+
+err_del_component:
+	rockchip_drm_component_del(dev, ROCKCHIP_DEVICE_TYPE_CONNECTOR);
+	return NULL;
+}
+
+void rockchip_connector_unregister(void *data)
+{
+	struct rockchip_conn_context *ctx = data;
+
+	if (!ctx)
+		return;
+	rockchip_drm_component_del(ctx->dev, ROCKCHIP_DEVICE_TYPE_CONNECTOR);
+	component_del(ctx->dev, &rockchip_conn_component_ops);
+}
+
+void rockchip_drm_encoder_setup(struct drm_device *dev)
+{
+	struct drm_encoder *encoder;
+
+	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
+		encoder->possible_clones =
+				rockchip_drm_encoder_clones(encoder);
+}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_connector.h b/drivers/gpu/drm/rockchip/rockchip_drm_connector.h
new file mode 100644
index 0000000..191f9fc
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_connector.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:mark yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _ROCKCHIP_DRM_CONNECTOR_H_
+#define _ROCKCHIP_DRM_CONNECTOR_H_
+
+#include <drm/drm_crtc.h>
+
+#include "rockchip_drm_drv.h"
+
+struct rockchip_connector {
+	struct device *dev;
+	int type;
+	void *priv;
+	u32 flags;
+
+	void (*enable)(struct rockchip_connector *conn);
+	void (*disable)(struct rockchip_connector *conn);
+	int (*setmode)(struct rockchip_connector *conn,
+		       struct drm_display_mode *mode);
+};
+
+void *rockchip_connector_register(struct rockchip_connector *conn);
+void rockchip_connector_unregister(void *data);
+#endif
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
new file mode 100644
index 0000000..4871867
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -0,0 +1,600 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:mark yao <mark.yao@rock-chips.com>
+ *
+ * based on exynos_drm_drv.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/pm_runtime.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include <linux/anon_inodes.h>
+#include <linux/component.h>
+
+#include <drm/rockchip_drm.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_fb.h"
+#include "rockchip_drm_fbdev.h"
+#include "rockchip_drm_gem.h"
+
+#define DRIVER_NAME	"rockchip-drm"
+#define DRIVER_DESC	"RockChip Soc DRM"
+#define DRIVER_DATE	"20140725"
+#define DRIVER_MAJOR	1
+#define DRIVER_MINOR	0
+
+#define VBLANK_OFF_DELAY	50000
+
+static struct platform_device *rockchip_drm_pdev;
+
+static DEFINE_MUTEX(drm_component_lock);
+static LIST_HEAD(drm_component_list);
+
+struct component_dev {
+	struct list_head list;
+	struct device *crtc_dev;
+	struct device *conn_dev;
+	unsigned int out_type;
+	int pipe;
+	void *crtc_data;
+	void *conn_data;
+};
+
+static int rockchip_drm_load(struct drm_device *dev, unsigned long flags)
+{
+	struct rockchip_drm_private *private;
+	int ret;
+	int nr;
+
+	private = kzalloc(sizeof(*private), GFP_KERNEL);
+	if (!private)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev->dev, dev);
+	dev->dev_private = (void *)private;
+
+	drm_mode_config_init(dev);
+
+	rockchip_drm_mode_config_init(dev);
+
+
+	/* Try to bind all sub drivers. */
+	ret = component_bind_all(dev->dev, dev);
+	if (ret)
+		goto err_cleanup_vblank;
+
+	for (nr = 0; nr < MAX_PLANE; nr++) {
+		struct drm_plane *plane;
+		unsigned long possible_crtcs = (1 << MAX_CRTC) - 1;
+
+		plane = rockchip_plane_init(dev, possible_crtcs, false);
+		if (!plane)
+			goto err_mode_config_cleanup;
+	}
+
+	/* init kms poll for handling hpd */
+	drm_kms_helper_poll_init(dev);
+
+	ret = drm_vblank_init(dev, MAX_CRTC);
+	if (ret)
+		goto err_mode_config_cleanup;
+
+	/* setup possible_clones. */
+	rockchip_drm_encoder_setup(dev);
+
+	drm_vblank_offdelay = VBLANK_OFF_DELAY;
+
+	platform_set_drvdata(dev->platformdev, dev);
+	rockchip_drm_fbdev_init(dev);
+
+	/* force connectors detection */
+	drm_helper_hpd_irq_event(dev);
+
+	return 0;
+
+err_cleanup_vblank:
+	drm_vblank_cleanup(dev);
+err_mode_config_cleanup:
+	drm_mode_config_cleanup(dev);
+	kfree(private);
+
+	return ret;
+}
+
+static int rockchip_drm_unload(struct drm_device *dev)
+{
+	rockchip_drm_fbdev_fini(dev);
+	drm_vblank_cleanup(dev);
+	drm_kms_helper_poll_fini(dev);
+	drm_mode_config_cleanup(dev);
+
+	kfree(dev->dev_private);
+
+	component_unbind_all(dev->dev, dev);
+	dev->dev_private = NULL;
+
+	return 0;
+}
+
+static int rockchip_drm_suspend(struct drm_device *dev, pm_message_t state)
+{
+	struct drm_connector *connector;
+
+	drm_modeset_lock_all(dev);
+	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+		int old_dpms = connector->dpms;
+
+		if (connector->funcs->dpms)
+			connector->funcs->dpms(connector, DRM_MODE_DPMS_OFF);
+
+		/* Set the old mode back to the connector for resume */
+		connector->dpms = old_dpms;
+	}
+	drm_modeset_unlock_all(dev);
+
+	return 0;
+}
+
+static int rockchip_drm_resume(struct drm_device *dev)
+{
+	struct drm_connector *connector;
+
+	drm_modeset_lock_all(dev);
+	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+		if (connector->funcs->dpms)
+			connector->funcs->dpms(connector, connector->dpms);
+	}
+	drm_modeset_unlock_all(dev);
+
+	drm_helper_resume_force_mode(dev);
+
+	return 0;
+}
+
+static int rockchip_drm_open(struct drm_device *dev, struct drm_file *file)
+{
+	return 0;
+}
+
+static void rockchip_drm_postclose(struct drm_device *dev,
+				   struct drm_file *file)
+{
+	struct drm_pending_event *e, *et;
+	unsigned long flags;
+
+	if (!file->driver_priv)
+		return;
+
+	/* Release all events not unhandled by page flip handler. */
+	rockchip_drm_crtc_cancel_pending_flip(dev);
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+
+	/* Release all events handled by page flip handler but not freed. */
+	list_for_each_entry_safe(e, et, &file->event_list, link) {
+		list_del(&e->link);
+		e->destroy(e);
+	}
+
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+
+	kfree(file->driver_priv);
+	file->driver_priv = NULL;
+}
+
+static const struct drm_ioctl_desc rockchip_ioctls[] = {
+	DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_CREATE, rockchip_drm_gem_create_ioctl,
+			  DRM_UNLOCKED | DRM_AUTH),
+	DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_MAP_OFFSET,
+			  rockchip_drm_gem_map_offset_ioctl, DRM_UNLOCKED |
+			  DRM_AUTH),
+	DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_MMAP, rockchip_drm_gem_mmap_ioctl,
+			  DRM_UNLOCKED | DRM_AUTH),
+	DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_GET, rockchip_drm_gem_get_ioctl,
+			  DRM_UNLOCKED),
+};
+
+static const struct file_operations rockchip_drm_driver_fops = {
+	.owner = THIS_MODULE,
+	.open = drm_open,
+	.mmap = drm_gem_cma_mmap,
+	.poll = drm_poll,
+	.read = drm_read,
+	.unlocked_ioctl = drm_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = drm_compat_ioctl,
+#endif
+	.release = drm_release,
+};
+
+static struct drm_driver rockchip_drm_driver = {
+	.driver_features	= DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
+	.load			= rockchip_drm_load,
+	.unload			= rockchip_drm_unload,
+	.suspend		= rockchip_drm_suspend,
+	.resume			= rockchip_drm_resume,
+	.open			= rockchip_drm_open,
+	.postclose		= rockchip_drm_postclose,
+	.get_vblank_counter	= drm_vblank_count,
+	.enable_vblank		= rockchip_drm_crtc_enable_vblank,
+	.disable_vblank		= rockchip_drm_crtc_disable_vblank,
+	.gem_free_object        = drm_gem_cma_free_object,
+	.gem_vm_ops             = &drm_gem_cma_vm_ops,
+	.dumb_create            = drm_gem_cma_dumb_create,
+	.dumb_map_offset        = drm_gem_cma_dumb_map_offset,
+	.dumb_destroy           = drm_gem_dumb_destroy,
+	.prime_handle_to_fd	= drm_gem_prime_handle_to_fd,
+	.prime_fd_to_handle	= drm_gem_prime_fd_to_handle,
+	.gem_prime_import       = drm_gem_prime_import,
+	.gem_prime_export       = drm_gem_prime_export,
+	.gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
+	.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
+	.gem_prime_vmap         = drm_gem_cma_prime_vmap,
+	.gem_prime_vunmap       = drm_gem_cma_prime_vunmap,
+	.gem_prime_mmap         = drm_gem_cma_prime_mmap,
+	.ioctls			= rockchip_ioctls,
+	.num_ioctls		= ARRAY_SIZE(rockchip_ioctls),
+	.fops			= &rockchip_drm_driver_fops,
+	.name	= DRIVER_NAME,
+	.desc	= DRIVER_DESC,
+	.date	= DRIVER_DATE,
+	.major	= DRIVER_MAJOR,
+	.minor	= DRIVER_MINOR,
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int rockchip_drm_sys_suspend(struct device *dev)
+{
+	struct drm_device *drm_dev = dev_get_drvdata(dev);
+	pm_message_t message;
+
+	if (pm_runtime_suspended(dev))
+		return 0;
+
+	message.event = PM_EVENT_SUSPEND;
+
+	return rockchip_drm_suspend(drm_dev, message);
+}
+
+static int rockchip_drm_sys_resume(struct device *dev)
+{
+	struct drm_device *drm_dev = dev_get_drvdata(dev);
+
+	if (pm_runtime_suspended(dev))
+		return 0;
+
+	return rockchip_drm_resume(drm_dev);
+}
+#endif
+
+static const struct dev_pm_ops rockchip_drm_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(rockchip_drm_sys_suspend,
+				rockchip_drm_sys_resume)
+};
+
+int rockchip_drm_pipe_get(struct device *dev)
+{
+	struct component_dev *cdev, *next;
+	int pipe = -1;
+
+	mutex_lock(&drm_component_lock);
+
+	list_for_each_entry_safe(cdev, next, &drm_component_list, list) {
+		if ((cdev->crtc_dev == dev) || (cdev->conn_dev == dev)) {
+			pipe = cdev->pipe;
+			break;
+		}
+	}
+
+	mutex_unlock(&drm_component_lock);
+
+	return pipe;
+}
+
+int rockchip_drm_out_type_get(struct device *dev)
+{
+	struct component_dev *cdev, *next;
+	int type = -1;
+
+	mutex_lock(&drm_component_lock);
+
+	list_for_each_entry_safe(cdev, next, &drm_component_list, list) {
+		if ((cdev->crtc_dev == dev) || (cdev->conn_dev == dev)) {
+			type = cdev->out_type;
+			break;
+		}
+	}
+
+	mutex_unlock(&drm_component_lock);
+
+	return type;
+}
+
+void *rockchip_drm_component_data_get(struct device *dev,
+				      enum rockchip_drm_device_type dev_type)
+{
+	struct component_dev *cdev, *next;
+	void *data = NULL;
+
+	mutex_lock(&drm_component_lock);
+
+	list_for_each_entry_safe(cdev, next, &drm_component_list, list) {
+		if ((cdev->crtc_dev == dev) || (cdev->conn_dev == dev)) {
+			if (dev_type == ROCKCHIP_DEVICE_TYPE_CRTC)
+				data = cdev->crtc_data;
+			else if (dev_type == ROCKCHIP_DEVICE_TYPE_CONNECTOR)
+				data = cdev->conn_data;
+			break;
+		}
+	}
+
+	mutex_unlock(&drm_component_lock);
+
+	return data;
+}
+
+int rockchip_drm_component_add(struct device *dev,
+			       enum rockchip_drm_device_type dev_type,
+			       int out_type, void *data)
+{
+	struct component_dev *cdev;
+	int pipe = -1;
+
+	if (dev_type != ROCKCHIP_DEVICE_TYPE_CRTC &&
+	    dev_type != ROCKCHIP_DEVICE_TYPE_CONNECTOR) {
+		DRM_ERROR("invalid device type.\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&drm_component_lock);
+
+	/*
+	 * Make sure to check if there is a component which has two device
+	 * objects, for connector and for encoder/connector.
+	 * It should make sure that crtc and encoder/connector drivers are
+	 * ready before rockchip drm core binds them.
+	 */
+	list_for_each_entry(cdev, &drm_component_list, list) {
+		pipe++;
+		/*
+		 * out_type from crtc and display port, crtc set possible
+		 * out_type maskbit at out_type, and display posr set out_type
+		 * directly. and if crtc and display port all register, set
+		 * out_type not maskbit;
+		 */
+		if (cdev->out_type & out_type) {
+			if (cdev->crtc_dev && cdev->conn_dev) {
+				DRM_ERROR("already register, not allow");
+				return -EINVAL;
+			}
+
+			if (dev_type == ROCKCHIP_DEVICE_TYPE_CRTC) {
+				cdev->pipe = pipe;
+				cdev->crtc_dev = dev;
+				cdev->crtc_data = data;
+			} else if (dev_type == ROCKCHIP_DEVICE_TYPE_CONNECTOR) {
+				cdev->conn_dev = dev;
+				cdev->conn_data = data;
+				cdev->out_type = out_type;
+			}
+
+			mutex_unlock(&drm_component_lock);
+			return 0;
+		}
+	}
+
+	mutex_unlock(&drm_component_lock);
+
+	cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
+	if (!cdev)
+		return -ENOMEM;
+
+	if (dev_type == ROCKCHIP_DEVICE_TYPE_CRTC) {
+		cdev->crtc_dev = dev;
+		cdev->crtc_data = data;
+	} else if (dev_type == ROCKCHIP_DEVICE_TYPE_CONNECTOR) {
+		cdev->conn_dev = dev;
+		cdev->conn_data = data;
+	}
+
+	cdev->out_type = out_type;
+
+	mutex_lock(&drm_component_lock);
+	list_add_tail(&cdev->list, &drm_component_list);
+	mutex_unlock(&drm_component_lock);
+
+	return 0;
+}
+
+void rockchip_drm_component_del(struct device *dev,
+				enum rockchip_drm_device_type dev_type)
+{
+	struct component_dev *cdev, *next;
+
+	mutex_lock(&drm_component_lock);
+
+	list_for_each_entry_safe(cdev, next, &drm_component_list, list) {
+		if (dev_type == ROCKCHIP_DEVICE_TYPE_CRTC) {
+			if (cdev->crtc_dev == dev)
+				cdev->crtc_dev = NULL;
+		}
+
+		if (dev_type == ROCKCHIP_DEVICE_TYPE_CONNECTOR) {
+			if (cdev->conn_dev == dev)
+				cdev->conn_dev = NULL;
+		}
+
+		/*
+		 * Release cdev object only in case that both of crtc and
+		 * encoder/connector device objects are NULL.
+		 */
+		if (!cdev->crtc_dev && !cdev->conn_dev) {
+			list_del(&cdev->list);
+			kfree(cdev);
+		}
+
+		break;
+	}
+
+	mutex_unlock(&drm_component_lock);
+}
+
+static int compare_of(struct device *dev, void *data)
+{
+	return dev == (struct device *)data;
+}
+
+static int rockchip_drm_add_components(struct device *dev, struct master *m)
+{
+	struct component_dev *cdev;
+	unsigned int attach_cnt = 0;
+
+	mutex_lock(&drm_component_lock);
+
+	list_for_each_entry(cdev, &drm_component_list, list) {
+		int ret;
+
+		/*
+		 * Add components to master only in case that crtc and
+		 * encoder/connector device objects exist.
+		 */
+		if (!cdev->crtc_dev || !cdev->conn_dev)
+			continue;
+
+		attach_cnt++;
+
+		mutex_unlock(&drm_component_lock);
+
+		/*
+		 * Do not chage below call order.
+		 * crtc device first should be added to master because
+		 * connector/encoder need pipe number of crtc when they
+		 * are created.
+		 */
+		ret = component_master_add_child(m, compare_of, cdev->crtc_dev);
+		ret |= component_master_add_child(m, compare_of,
+						  cdev->conn_dev);
+		if (ret < 0)
+			return ret;
+
+		mutex_lock(&drm_component_lock);
+	}
+
+	mutex_unlock(&drm_component_lock);
+
+	return attach_cnt ? 0 : -ENODEV;
+}
+
+static int rockchip_drm_bind(struct device *dev)
+{
+	return drm_platform_init(&rockchip_drm_driver, to_platform_device(dev));
+}
+
+static void rockchip_drm_unbind(struct device *dev)
+{
+	drm_put_dev(dev_get_drvdata(dev));
+}
+
+static const struct component_master_ops rockchip_drm_ops = {
+	.add_components = rockchip_drm_add_components,
+	.bind = rockchip_drm_bind,
+	.unbind = rockchip_drm_unbind,
+};
+
+static int rockchip_drm_platform_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+	rockchip_drm_driver.num_ioctls = ARRAY_SIZE(rockchip_ioctls);
+
+	ret = component_master_add(&pdev->dev, &rockchip_drm_ops);
+	if (ret < 0)
+		DRM_DEBUG_KMS("re-tried by last sub driver probed later.\n");
+
+	return 0;
+}
+
+static int rockchip_drm_platform_remove(struct platform_device *pdev)
+{
+	component_master_del(&pdev->dev, &rockchip_drm_ops);
+
+	return 0;
+}
+
+static struct platform_driver rockchip_drm_platform_driver = {
+	.probe = rockchip_drm_platform_probe,
+	.remove = rockchip_drm_platform_remove,
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "rockchip-drm",
+		.pm = &rockchip_drm_pm_ops,
+	},
+};
+
+static int rockchip_drm_init(void)
+{
+	int ret;
+
+	ret = platform_driver_register(&rockchip_panel_platform_driver);
+	if (ret < 0)
+		return -ENOMEM;
+
+#ifdef CONFIG_DRM_ROCKCHIP_LCDC
+	ret = platform_driver_register(&rockchip_lcdc_platform_driver);
+	if (ret < 0)
+		goto out_lcdc;
+#endif
+
+	rockchip_drm_pdev = platform_device_register_simple("rockchip-drm", -1,
+							    NULL, 0);
+	if (IS_ERR(rockchip_drm_pdev)) {
+		ret = PTR_ERR(rockchip_drm_pdev);
+		goto out_drm_pdev;
+	}
+
+	ret = platform_driver_register(&rockchip_drm_platform_driver);
+	if (ret)
+		goto out_drm_driver;
+
+	return 0;
+
+out_drm_driver:
+	platform_device_unregister(rockchip_drm_pdev);
+out_drm_pdev:
+#ifdef CONFIG_DRM_ROCKCHIP_LCDC
+	platform_driver_unregister(&rockchip_lcdc_platform_driver);
+out_lcdc:
+#endif
+	platform_driver_unregister(&rockchip_panel_platform_driver);
+	return ret;
+}
+
+static void rockchip_drm_exit(void)
+{
+	platform_device_unregister(rockchip_drm_pdev);
+	platform_driver_unregister(&rockchip_drm_platform_driver);
+#ifdef CONFIG_DRM_ROCKCHIP_LCDC
+	platform_driver_unregister(&rockchip_lcdc_platform_driver);
+#endif
+	platform_driver_unregister(&rockchip_panel_platform_driver);
+}
+
+module_init(rockchip_drm_init);
+module_exit(rockchip_drm_exit);
+
+MODULE_AUTHOR("mark yao <mark.yao@rock-chips.com>");
+MODULE_DESCRIPTION("ROCKCHIP DRM Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
new file mode 100644
index 0000000..c0c1d89
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:mark yao <mark.yao@rock-chips.com>
+ *
+ * based on exynos_drm_drv.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _ROCKCHIP_DRM_DRV_H_
+#define _ROCKCHIP_DRM_DRV_H_
+
+#include <linux/module.h>
+
+#define MAX_CRTC	3
+#define MAX_PLANE	5
+#define MAX_FB_BUFFER	4
+#define DEFAULT_ZPOS	-1
+
+struct drm_device;
+struct drm_connector;
+
+/*
+ * display output interface supported by rockchip lcdc
+ */
+#define ROCKCHIP_OUTFACE_P888	0
+#define ROCKCHIP_OUTFACE_P666	1
+#define ROCKCHIP_OUTFACE_P565	2
+/* for use special outface */
+#define ROCKCHIP_OUTFACE_AAAA	15
+
+#define ROCKCHIP_COLOR_SWAP_RG	0x1
+#define ROCKCHIP_COLOR_SWAP_RB	0x2
+#define ROCKCHIP_COLOR_SWAP_GB	0x4
+/*
+ * Special panel info for rockchip
+ *
+ * @out_type: lcd controller need to know the sceen type.
+ * @out_face: the output pin interface.
+ * @color_swap: if want to swap color at output, use this.
+ * @pwr18: choice the power supply 1.8 or 3.3 mode for lcdc
+ * @dither: use dither func at lcd output
+ * @flags: the display flags, now just for pin sync level.
+ */
+struct rockchip_panel_special {
+	int out_type;
+	int out_face;
+	u32 color_swap;
+	bool pwr18;
+	bool dither;
+	u32 flags;
+};
+
+/* This enumerates device type. */
+enum rockchip_drm_device_type {
+	ROCKCHIP_DEVICE_TYPE_NONE,
+	ROCKCHIP_DEVICE_TYPE_CRTC,
+	ROCKCHIP_DEVICE_TYPE_CONNECTOR,
+};
+
+/* this enumerates display type. */
+enum rockchip_drm_output_type {
+	ROCKCHIP_DISPLAY_TYPE_NONE = 0,
+	/* RGB Interface. */
+	ROCKCHIP_DISPLAY_TYPE_RGB = (1 << 0),
+	/* LVDS Interface. */
+	ROCKCHIP_DISPLAY_TYPE_LVDS = (1 << 1),
+	/* DUAL LVDS Interface. */
+	ROCKCHIP_DISPLAY_TYPE_DUAL_LVDS = (1 << 2),
+	/* EDP Interface. */
+	ROCKCHIP_DISPLAY_TYPE_EDP = (1 << 3),
+	/* MIPI Interface. */
+	ROCKCHIP_DISPLAY_TYPE_MIPI = (1 << 4),
+	/* HDMI Interface. */
+	ROCKCHIP_DISPLAY_TYPE_HDMI = (1 << 5),
+};
+
+/*
+ * Rockchip drm private structure.
+ *
+ * @pipe: the pipe number for this crtc/manager.
+ */
+struct rockchip_drm_private {
+	struct drm_fbdev_cma *fbdev_cma;
+	/*
+	 * created crtc object would be contained at this array and
+	 * this array is used to be aware of which crtc did it request vblank.
+	 */
+	struct drm_crtc *crtc[MAX_CRTC];
+	struct drm_property *plane_zpos_property;
+	struct drm_property *crtc_mode_property;
+
+	unsigned int pipe;
+};
+
+
+void rockchip_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe);
+void rockchip_drm_crtc_cancel_pending_flip(struct drm_device *dev);
+int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, int pipe);
+void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, int pipe);
+
+struct drm_plane *rockchip_plane_init(struct drm_device *dev,
+				      unsigned long possible_crtcs, bool priv);
+
+void rockchip_drm_encoder_setup(struct drm_device *dev);
+
+void *rockchip_drm_component_data_get(struct device *dev,
+				      enum rockchip_drm_device_type dev_type);
+int rockchip_drm_pipe_get(struct device *dev);
+
+int rockchip_drm_component_add(struct device *dev,
+			       enum rockchip_drm_device_type dev_type,
+			       int out_type, void *data);
+void rockchip_drm_component_del(struct device *dev,
+				enum rockchip_drm_device_type dev_type);
+
+extern struct platform_driver rockchip_panel_platform_driver;
+#ifdef CONFIG_DRM_ROCKCHIP_LCDC
+extern struct platform_driver rockchip_lcdc_platform_driver;
+#endif
+#endif /* _ROCKCHIP_DRM_DRV_H_ */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
new file mode 100644
index 0000000..a04024b
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:mark yao <mark.yao@rock-chips.com>
+ *
+ * based on exynos_drm_fb.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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_fb_cma_helper.h>
+#include <drm/drm_fb_helper.h>
+
+#include <uapi/drm/rockchip_drm.h>
+
+static struct drm_framebuffer *
+rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
+			struct drm_mode_fb_cmd2 *mode_cmd)
+{
+	return drm_fb_cma_create(dev, file_priv, mode_cmd);
+}
+
+static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
+	.fb_create = rockchip_user_fb_create,
+};
+
+void rockchip_drm_mode_config_init(struct drm_device *dev)
+{
+	dev->mode_config.min_width = 0;
+	dev->mode_config.min_height = 0;
+
+	/*
+	 * 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 = &rockchip_drm_mode_config_funcs;
+}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.h b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h
new file mode 100644
index 0000000..6258de6
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:mark yao <mark.yao@rock-chips.com>
+ *
+ * based on exynos_drm_fb.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _ROCKCHIP_DRM_FB_H_
+#define _ROCKCHIP_DRM_FB_H_
+
+struct drm_framebuffer *
+rockchip_drm_framebuffer_init(struct drm_device *dev,
+			      struct drm_mode_fb_cmd2 *mode_cmd,
+			      struct drm_gem_object *obj);
+
+void rockchip_drm_mode_config_init(struct drm_device *dev);
+
+#endif /* _ROCKCHIP_DRM_FB_H_ */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
new file mode 100644
index 0000000..d32fa57
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:mark yao <mark.yao@rock-chips.com>
+ *
+ * based on exynos_drm_fbdev.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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_fb_cma_helper.h>
+
+#include <drm/rockchip_drm.h>
+
+#include "rockchip_drm_drv.h"
+
+#define MAX_CONNECTOR		4
+#define PREFERRED_BPP		32
+
+int rockchip_drm_fbdev_init(struct drm_device *dev)
+{
+	struct rockchip_drm_private *private = dev->dev_private;
+	struct drm_fbdev_cma *fbdev_cma;
+	unsigned int num_crtc;
+
+	if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector)
+		return 0;
+
+	if (private->fbdev_cma) {
+		DRM_ERROR("no allow to reinit cma fbdev\n");
+		return -EINVAL;
+	}
+
+	num_crtc = dev->mode_config.num_crtc;
+
+	fbdev_cma = drm_fbdev_cma_init(dev, PREFERRED_BPP, num_crtc,
+				       MAX_CONNECTOR);
+	if (!fbdev_cma) {
+		DRM_ERROR("failed to init cma fbdev\n");
+		return -ENOMEM;
+	}
+
+	private->fbdev_cma = fbdev_cma;
+
+	return 0;
+}
+
+void rockchip_drm_fbdev_fini(struct drm_device *dev)
+{
+	struct rockchip_drm_private *private = dev->dev_private;
+
+	if (!private || !private->fbdev_cma)
+		return;
+
+	drm_fbdev_cma_fini(private->fbdev_cma);
+}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
new file mode 100644
index 0000000..91cb535
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:mark yao <mark.yao@rock-chips.com>
+ *
+ * based on exynos_drm_fbdev.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _ROCKCHIP_DRM_FBDEV_H_
+#define _ROCKCHIP_DRM_FBDEV_H_
+
+int rockchip_drm_fbdev_init(struct drm_device *dev);
+void rockchip_drm_fbdev_fini(struct drm_device *dev);
+
+#endif /* _ROCKCHIP_DRM_FBDEV_H_ */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
new file mode 100644
index 0000000..f0219cd
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
@@ -0,0 +1,163 @@
+/*
+ *
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:mark yao <mark.yao@rock-chips.com>
+ *
+ * based on exynos_drm_gem.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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_vma_manager.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include <drm/rockchip_drm.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_gem.h"
+
+int rockchip_drm_gem_create_ioctl(struct drm_device *dev, void *data,
+				  struct drm_file *file_priv)
+{
+	struct drm_rockchip_gem_create *args = data;
+	struct drm_gem_cma_object *cma_obj;
+	struct drm_gem_object *gem_obj;
+	int ret;
+
+	cma_obj = drm_gem_cma_create(dev, args->size);
+	if (IS_ERR(cma_obj))
+		return PTR_ERR_OR_ZERO(cma_obj);
+
+	gem_obj = &cma_obj->base;
+
+	/*
+	 * 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, gem_obj, &args->handle);
+	if (ret)
+		goto err_handle_create;
+
+	/* drop reference from allocate - handle holds it now. */
+	drm_gem_object_unreference_unlocked(gem_obj);
+
+	return PTR_ERR_OR_ZERO(cma_obj);
+
+err_handle_create:
+	drm_gem_cma_free_object(gem_obj);
+	return ret;
+}
+
+int rockchip_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data,
+				      struct drm_file *file_priv)
+{
+	struct drm_rockchip_gem_map_off *args = data;
+
+	DRM_DEBUG_KMS("handle = 0x%x, offset = 0x%lx\n",
+		      args->handle, (unsigned long)args->offset);
+
+	return drm_gem_cma_dumb_map_offset(file_priv, dev, args->handle,
+					   &args->offset);
+}
+
+int rockchip_drm_gem_mmap_ioctl(struct drm_device *dev, void *data,
+				struct drm_file *file_priv)
+{
+	struct drm_rockchip_gem_mmap *args = data;
+	struct drm_gem_object *obj;
+	unsigned long addr;
+
+	mutex_lock(&dev->struct_mutex);
+
+	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+	if (!obj) {
+		DRM_ERROR("failed to lookup gem object.\n");
+		mutex_unlock(&dev->struct_mutex);
+		return -EINVAL;
+	}
+
+	addr = vm_mmap(obj->filp, 0, args->size, PROT_READ | PROT_WRITE,
+		       MAP_SHARED, 0);
+
+	drm_gem_object_unreference(obj);
+
+	if (IS_ERR_VALUE(addr)) {
+		mutex_unlock(&dev->struct_mutex);
+		return (int)addr;
+	}
+
+	mutex_unlock(&dev->struct_mutex);
+
+	args->mapped = addr;
+
+	DRM_DEBUG_KMS("mapped = 0x%lx\n", (unsigned long)args->mapped);
+
+	return 0;
+}
+
+int rockchip_drm_gem_get_ioctl(struct drm_device *dev, void *data,
+			       struct drm_file *file_priv)
+{
+	struct drm_rockchip_gem_info *args = data;
+	struct drm_gem_object *obj;
+
+	mutex_lock(&dev->struct_mutex);
+
+	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+	if (!obj) {
+		DRM_ERROR("failed to lookup gem object.\n");
+		mutex_unlock(&dev->struct_mutex);
+		return -EINVAL;
+	}
+
+	args->size = obj->size;
+
+	drm_gem_object_unreference(obj);
+	mutex_unlock(&dev->struct_mutex);
+
+	return 0;
+}
+
+int rockchip_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;
+}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
new file mode 100644
index 0000000..fe8285f
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:mark yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _ROCKCHIP_DRM_GEM_H_
+#define _ROCKCHIP_DRM_GEM_H_
+
+/*
+ * request gem object creation and buffer allocation as the size
+ * that it is calculated with framebuffer information such as width,
+ * height and bpp.
+ */
+int rockchip_drm_gem_create_ioctl(struct drm_device *dev, void *data,
+				  struct drm_file *file_priv);
+
+/* get buffer offset to map to user space. */
+int rockchip_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data,
+				      struct drm_file *file_priv);
+
+/*
+ * mmap the physically continuous memory that a gem object contains
+ * to user space.
+ */
+int rockchip_drm_gem_mmap_ioctl(struct drm_device *dev, void *data,
+				struct drm_file *file_priv);
+
+/* get buffer information to memory region allocated by gem. */
+int rockchip_drm_gem_get_ioctl(struct drm_device *dev, void *data,
+			       struct drm_file *file_priv);
+#endif
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_lcdc.c b/drivers/gpu/drm/rockchip/rockchip_drm_lcdc.c
new file mode 100644
index 0000000..98bfbab
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_lcdc.c
@@ -0,0 +1,722 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:mark yao <mark.yao@rock-chips.com>
+ *
+ * based on exynos_drm_fimd.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/component.h>
+
+#include <drm/rockchip_drm.h>
+
+#include <video/of_display_timing.h>
+#include <video/of_videomode.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_fbdev.h"
+#include "rockchip_drm_lcdc.h"
+
+#define LCDC_DEFAULT_FRAMERATE 60
+
+#define ROCKCHIP_DISPLAY_TYPE_LCD (ROCKCHIP_DISPLAY_TYPE_RGB | \
+					ROCKCHIP_DISPLAY_TYPE_LVDS | \
+					ROCKCHIP_DISPLAY_TYPE_EDP)
+
+static const uint32_t formats[] = {
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_ARGB8888,
+};
+
+struct rockchip_plane {
+	int zpos;
+	struct drm_plane base;
+};
+
+struct lcdc_context {
+	struct device *dev;
+	struct drm_device *drm_dev;
+	struct drm_crtc crtc;
+	struct drm_pending_vblank_event *event;
+	struct drm_display_mode mode;
+	struct drm_plane *plane;
+	struct lcdc_driver *drv;
+	unsigned int default_win;
+	unsigned int dpms;
+	int pipe;
+	wait_queue_head_t wait_vsync_queue;
+	atomic_t wait_vsync_event;
+};
+
+#define to_lcdc_data(x) ((x)->drv->data)
+#define to_lcdc_ctx(x) container_of(x, struct lcdc_context, crtc)
+#define to_rockchip_plane(x) container_of(x, struct rockchip_plane, base)
+
+const struct of_device_id lcdc_driver_dt_match[] = {
+#ifdef CONFIG_LCDC_RK3288
+	{ .compatible = "rockchip,rk3288-lcdc",
+	  .data = (void *)&rockchip_rk3288_lcdc },
+#endif
+	{},
+};
+
+static inline struct lcdc_driver_data *drm_lcdc_get_driver_data(
+	struct platform_device *pdev)
+{
+	const struct of_device_id *of_id =
+			of_match_device(lcdc_driver_dt_match, &pdev->dev);
+
+	return (struct lcdc_driver_data *)of_id->data;
+}
+
+static int rockchip_plane_get_size(int start, unsigned length, unsigned last)
+{
+	int end = start + length;
+	int size = 0;
+
+	if (start <= 0) {
+		if (end > 0)
+			size = min_t(unsigned, end, last);
+	} else if (start <= last) {
+		size = min_t(unsigned, last - start, length);
+	}
+
+	return size;
+}
+
+static int rockchip_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
+				 struct drm_framebuffer *fb, int crtc_x,
+				 int crtc_y, unsigned int crtc_w,
+				 unsigned int crtc_h, uint32_t src_x,
+				 uint32_t src_y, uint32_t src_w, uint32_t src_h)
+{
+	struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
+	struct lcdc_context *ctx = to_lcdc_ctx(crtc);
+	struct lcdc_driver_data *lcdc_data = to_lcdc_data(ctx);
+	struct drm_gem_cma_object *gem;
+	struct lcdc_win_data *win_data;
+	unsigned long offset;
+	unsigned int actual_w;
+	unsigned int actual_h;
+	int win;
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	gem = drm_fb_cma_get_gem_obj(fb, 0);
+	if (!gem) {
+		DRM_ERROR("fail to get cma object from framebuffer\n");
+		return -EINVAL;
+	}
+
+	actual_w = rockchip_plane_get_size(crtc_x,
+					   crtc_w, crtc->mode.hdisplay);
+	actual_h = rockchip_plane_get_size(crtc_y,
+					   crtc_h, crtc->mode.vdisplay);
+	if (crtc_x < 0) {
+		if (actual_w)
+			src_x -= crtc_x;
+		crtc_x = 0;
+	}
+
+	if (crtc_y < 0) {
+		if (actual_h)
+			src_y -= crtc_y;
+		crtc_y = 0;
+	}
+
+	win = rockchip_plane->zpos;
+	if (win == DEFAULT_ZPOS)
+		win = ctx->default_win;
+
+	if (win < 0 || win >= ZPOS_MAX_NUM)
+		return -EINVAL;
+
+	offset = (src_x >> 16) * (fb->bits_per_pixel >> 3);
+	offset += (src_y >> 16) * fb->pitches[0];
+
+	DRM_DEBUG_KMS("offset = 0x%lx, pitch = %x\n", offset, fb->pitches[0]);
+
+	win_data = lcdc_data->get_win(ctx->drv, win);
+
+	win_data->xpos = crtc_x;
+	win_data->ypos = crtc_y;
+	win_data->xsize = actual_w;
+	win_data->ysize = actual_h;
+	win_data->xact = fb->width;
+	win_data->yact = fb->height;
+	win_data->y_vir_stride = fb->pitches[0] / (fb->bits_per_pixel >> 3);
+	win_data->yrgb_addr = gem->paddr + offset;
+	win_data->uv_addr = 0;
+	win_data->alpha_en = false;
+
+	switch (fb->pixel_format) {
+	case DRM_FORMAT_ARGB8888:
+		win_data->alpha_en = true;
+	case DRM_FORMAT_XRGB8888:
+		win_data->format = ARGB888;
+		break;
+	case DRM_FORMAT_RGB565:
+		win_data->format = RGB565;
+		win_data->y_vir_stride =
+			((win_data->y_vir_stride * 3) >> 2)
+			+ win_data->y_vir_stride % 3;
+		break;
+	default:
+		DRM_DEBUG_KMS("invalid pixel size so using unpacked 24bpp.\n");
+		win_data->alpha_en = false;
+		win_data->format = ARGB888;
+		break;
+	}
+	win_data->enabled = true;
+	DRM_DEBUG_KMS("offset_x = %d, offset_y = %d\n",
+		      win_data->xpos, win_data->ypos);
+	DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
+		      win_data->xsize, win_data->ysize);
+	DRM_DEBUG_KMS("paddr = 0x%lx\n", (unsigned long)win_data->yrgb_addr);
+	DRM_DEBUG_KMS("fb_width = %d, actual_w = %d\n",
+		      fb->width, actual_w);
+
+	lcdc_data->win_commit(ctx->drv, win_data);
+	return 0;
+}
+
+static int rockchip_disable_plane(struct drm_plane *plane)
+{
+	struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
+	struct lcdc_context *ctx = to_lcdc_ctx(plane->crtc);
+	struct lcdc_driver_data *lcdc_data = to_lcdc_data(ctx);
+	struct lcdc_win_data *win_data;
+	int win = rockchip_plane->zpos;
+
+	if (win == DEFAULT_ZPOS)
+		win = ctx->default_win;
+
+	if (win < 0 || win >= ZPOS_MAX_NUM)
+		return -EINVAL;
+
+	win_data = lcdc_data->get_win(ctx->drv, win);
+
+	win_data->enabled = false;
+	lcdc_data->win_commit(ctx->drv, win_data);
+
+	return 0;
+}
+
+static void rockchip_plane_destroy(struct drm_plane *plane)
+{
+	struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	rockchip_disable_plane(plane);
+	drm_plane_cleanup(plane);
+	kfree(rockchip_plane);
+}
+
+static int rockchip_plane_set_property(struct drm_plane *plane,
+				       struct drm_property *property,
+				       uint64_t val)
+{
+	struct drm_device *dev = plane->dev;
+	struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
+	struct rockchip_drm_private *dev_priv = dev->dev_private;
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	if (property == dev_priv->plane_zpos_property) {
+		rockchip_plane->zpos = val;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static struct drm_plane_funcs rockchip_plane_funcs = {
+	.update_plane = rockchip_update_plane,
+	.disable_plane = rockchip_disable_plane,
+	.destroy = rockchip_plane_destroy,
+	.set_property = rockchip_plane_set_property,
+};
+
+static void rockchip_plane_attach_zpos_property(struct drm_plane *plane)
+{
+	struct drm_device *dev = plane->dev;
+	struct rockchip_drm_private *dev_priv = dev->dev_private;
+	struct drm_property *prop;
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	prop = dev_priv->plane_zpos_property;
+	if (!prop) {
+		prop = drm_property_create_range(dev, 0, "zpos", 0,
+						 MAX_PLANE - 1);
+		if (!prop)
+			return;
+
+		dev_priv->plane_zpos_property = prop;
+	}
+
+	drm_object_attach_property(&plane->base, prop, 0);
+}
+
+struct drm_plane *rockchip_plane_init(struct drm_device *dev,
+				      unsigned long possible_crtcs, bool priv)
+{
+	struct rockchip_plane *rockchip_plane;
+	struct rockchip_drm_private *private = dev->dev_private;
+	enum drm_plane_type type;
+	int err;
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	rockchip_plane = kzalloc(sizeof(*rockchip_plane), GFP_KERNEL);
+	if (!rockchip_plane)
+		return NULL;
+
+	type = priv ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY;
+	err = drm_universal_plane_init(dev, &rockchip_plane->base,
+				       possible_crtcs, &rockchip_plane_funcs,
+				       formats, ARRAY_SIZE(formats), type);
+	if (err) {
+		DRM_ERROR("failed to initialize plane\n");
+		kfree(rockchip_plane);
+		return NULL;
+	}
+
+	if (priv) {
+		rockchip_plane->base.crtc = private->crtc[0];
+		rockchip_plane->zpos = DEFAULT_ZPOS;
+	} else {
+		rockchip_plane_attach_zpos_property(&rockchip_plane->base);
+	}
+
+	return &rockchip_plane->base;
+}
+
+int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, int pipe)
+{
+	struct rockchip_drm_private *private = dev->dev_private;
+	struct lcdc_context *ctx = to_lcdc_ctx(private->crtc[pipe]);
+	struct lcdc_driver_data *lcdc_data = to_lcdc_data(ctx);
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	if (ctx->dpms != DRM_MODE_DPMS_ON)
+		return -EPERM;
+
+	lcdc_data->enable_vblank(ctx->drv);
+
+	return 0;
+}
+
+void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, int pipe)
+{
+	struct rockchip_drm_private *private = dev->dev_private;
+	struct lcdc_context *ctx = to_lcdc_ctx(private->crtc[pipe]);
+	struct lcdc_driver_data *lcdc_data = to_lcdc_data(ctx);
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	if (ctx->dpms != DRM_MODE_DPMS_ON)
+		return;
+
+	lcdc_data->disable_vblank(ctx->drv);
+}
+
+static void rockchip_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+	struct lcdc_context *ctx = to_lcdc_ctx(crtc);
+	struct lcdc_driver_data *lcdc_data = to_lcdc_data(ctx);
+
+	DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode);
+
+	if (ctx->dpms == mode) {
+		DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
+		return;
+	}
+
+	if (mode > DRM_MODE_DPMS_ON) {
+		/* wait for the completion of page flip. */
+		if (!wait_event_timeout(ctx->wait_vsync_queue,
+					!atomic_read(&ctx->wait_vsync_event),
+					HZ/20))
+			DRM_DEBUG_KMS("vblank wait timed out.\n");
+		drm_vblank_off(crtc->dev, ctx->pipe);
+	}
+
+	switch (mode) {
+	case DRM_MODE_DPMS_ON:
+		lcdc_data->dpms(ctx->drv, DRM_MODE_DPMS_ON);
+		break;
+	case DRM_MODE_DPMS_STANDBY:
+	case DRM_MODE_DPMS_SUSPEND:
+	case DRM_MODE_DPMS_OFF:
+		lcdc_data->dpms(ctx->drv, DRM_MODE_DPMS_OFF);
+		break;
+	default:
+		DRM_DEBUG_KMS("unspecified mode %d\n", mode);
+		break;
+	}
+
+	ctx->dpms = mode;
+}
+
+static void rockchip_drm_crtc_prepare(struct drm_crtc *crtc)
+{
+	/* drm framework doesn't check NULL. */
+}
+
+static bool rockchip_drm_crtc_mode_fixup(struct drm_crtc *crtc,
+					 const struct drm_display_mode *mode,
+					 struct drm_display_mode *adjusted_mode)
+{
+	/* just do dummy now */
+
+	return true;
+}
+
+static int rockchip_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 *fb)
+{
+	struct lcdc_context *ctx = to_lcdc_ctx(crtc);
+	struct lcdc_driver_data *lcdc_data = to_lcdc_data(ctx);
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	/* nothing to do if we haven't set the mode yet */
+	if (adjusted_mode->htotal == 0 || adjusted_mode->vtotal == 0)
+		return -EINVAL;
+
+	drm_mode_copy(&ctx->mode, adjusted_mode);
+	lcdc_data->mode_set(ctx->drv, &ctx->mode);
+
+	return 0;
+}
+static int rockchip_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+					   struct drm_framebuffer *old_fb)
+{
+	struct lcdc_context *ctx = to_lcdc_ctx(crtc);
+	unsigned int crtc_w;
+	unsigned int crtc_h;
+	int ret;
+
+	crtc_w = crtc->primary->fb->width - crtc->x;
+	crtc_h = crtc->primary->fb->height - crtc->y;
+
+	ret = rockchip_update_plane(ctx->plane, crtc, crtc->primary->fb, 0, 0,
+				    crtc_w, crtc_h, crtc->x, crtc->y, crtc_w,
+				    crtc_h);
+	if (ret < 0) {
+		DRM_ERROR("fail to update plane\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void rockchip_drm_crtc_commit(struct drm_crtc *crtc)
+{
+	rockchip_drm_crtc_mode_set_base(crtc, crtc->x, crtc->y,
+					crtc->primary->fb);
+}
+
+static struct drm_crtc_helper_funcs rockchip_crtc_helper_funcs = {
+	.dpms = rockchip_drm_crtc_dpms,
+	.prepare = rockchip_drm_crtc_prepare,
+	.mode_fixup = rockchip_drm_crtc_mode_fixup,
+	.mode_set = rockchip_drm_crtc_mode_set,
+	.mode_set_base = rockchip_drm_crtc_mode_set_base,
+	.commit = rockchip_drm_crtc_commit,
+};
+
+static int rockchip_drm_crtc_page_flip(struct drm_crtc *crtc,
+				       struct drm_framebuffer *fb,
+				       struct drm_pending_vblank_event *event,
+				       uint32_t page_flip_flags)
+{
+	struct drm_device *dev = crtc->dev;
+	struct lcdc_context *ctx = to_lcdc_ctx(crtc);
+	struct drm_framebuffer *old_fb = crtc->primary->fb;
+	unsigned int crtc_w;
+	unsigned int crtc_h;
+	int ret = -EINVAL;
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	/* when the page flip is requested, crtc's dpms should be on */
+	if (ctx->dpms > DRM_MODE_DPMS_ON) {
+		DRM_ERROR("failed page flip request.\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&dev->struct_mutex);
+
+	/*
+	 * the pipe from user always is 0 so we can set pipe number
+	 * of current owner to event.
+	 */
+	ret = drm_vblank_get(dev, ctx->pipe);
+	if (ret) {
+		DRM_DEBUG("failed to acquire vblank counter\n");
+		goto out;
+	}
+
+	spin_lock_irq(&dev->event_lock);
+	if (ctx->event) {
+		spin_unlock_irq(&dev->event_lock);
+		DRM_ERROR("already pending flip!\n");
+		ret = -EBUSY;
+		goto out;
+	}
+	ctx->event = event;
+	atomic_set(&ctx->wait_vsync_event, 1);
+	spin_unlock_irq(&dev->event_lock);
+
+	crtc->primary->fb = fb;
+	crtc_w = crtc->primary->fb->width - crtc->x;
+	crtc_h = crtc->primary->fb->height - crtc->y;
+
+	ret = rockchip_update_plane(ctx->plane, crtc, fb, 0, 0, crtc_w, crtc_h,
+				    crtc->x, crtc->y, crtc_w, crtc_h);
+	if (ret) {
+		crtc->primary->fb = old_fb;
+
+		spin_lock_irq(&dev->event_lock);
+		drm_vblank_put(dev, ctx->pipe);
+		atomic_set(&ctx->wait_vsync_event, 0);
+		ctx->event = NULL;
+		spin_unlock_irq(&dev->event_lock);
+
+		goto out;
+	}
+out:
+	mutex_unlock(&dev->struct_mutex);
+	return ret;
+}
+
+void rockchip_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe)
+{
+	struct rockchip_drm_private *dev_priv = dev->dev_private;
+	struct drm_crtc *drm_crtc = dev_priv->crtc[pipe];
+	struct lcdc_context *ctx;
+	struct drm_pending_vblank_event *event;
+	unsigned long flags;
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	if (!drm_crtc)
+		return;
+
+	ctx = to_lcdc_ctx(drm_crtc);
+	event = ctx->event;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+
+	if (event) {
+		ctx->event = NULL;
+		drm_send_vblank_event(dev, -1, event);
+		drm_vblank_put(dev, pipe);
+		atomic_set(&ctx->wait_vsync_event, 0);
+		wake_up(&ctx->wait_vsync_queue);
+	}
+
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+void rockchip_drm_crtc_cancel_pending_flip(struct drm_device *dev)
+{
+	int i;
+
+	DRM_DEBUG_KMS("cancle pending flip\n");
+	for (i = 0; i < dev->num_crtcs; i++)
+		rockchip_drm_crtc_finish_pageflip(dev, i);
+}
+
+static void rockchip_drm_crtc_destroy(struct drm_crtc *crtc)
+{
+	struct lcdc_context *ctx = to_lcdc_ctx(crtc);
+	struct rockchip_drm_private *private = crtc->dev->dev_private;
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	private->crtc[ctx->pipe] = NULL;
+
+	drm_crtc_cleanup(crtc);
+}
+
+static struct drm_crtc_funcs rockchip_crtc_funcs = {
+	.set_config = drm_crtc_helper_set_config,
+	.page_flip = rockchip_drm_crtc_page_flip,
+	.destroy = rockchip_drm_crtc_destroy,
+};
+
+void lcdc_vsync_event_handler(struct device *dev)
+{
+	struct drm_pending_vblank_event *event;
+	struct lcdc_context *ctx = dev_get_drvdata(dev);
+	struct drm_device *drm_dev;
+	unsigned long flags;
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	/* check the crtc is detached already from encoder */
+	if (ctx && (ctx->pipe < 0 || !ctx->drm_dev))
+		return;
+
+	drm_handle_vblank(ctx->drm_dev, ctx->pipe);
+
+	event = ctx->event;
+	drm_dev = ctx->drm_dev;
+
+	spin_lock_irqsave(&drm_dev->event_lock, flags);
+
+	if (event) {
+		ctx->event = NULL;
+		drm_send_vblank_event(drm_dev, -1, event);
+		drm_vblank_put(drm_dev, ctx->pipe);
+		atomic_set(&ctx->wait_vsync_event, 0);
+		wake_up(&ctx->wait_vsync_queue);
+	}
+
+	spin_unlock_irqrestore(&drm_dev->event_lock, flags);
+}
+
+static int lcdc_bind(struct device *dev, struct device *master, void *data)
+{
+	struct drm_device *drm_dev = data;
+	struct rockchip_drm_private *private = drm_dev->dev_private;
+	struct lcdc_context *ctx = dev_get_drvdata(dev);
+	struct drm_crtc *crtc;
+
+	ctx->drm_dev = drm_dev;
+
+	ctx->pipe = rockchip_drm_pipe_get(dev);
+	ctx->dpms = DRM_MODE_DPMS_OFF;
+	crtc = &ctx->crtc;
+
+	private->crtc[ctx->pipe] = crtc;
+	ctx->plane = rockchip_plane_init(drm_dev, 1 << ctx->pipe, true);
+	drm_crtc_init_with_planes(drm_dev, crtc, ctx->plane, NULL, &rockchip_crtc_funcs);
+	drm_crtc_helper_add(crtc, &rockchip_crtc_helper_funcs);
+
+	/*
+	 * enable drm irq mode.
+	 * - with irq_enabled = true, we can use the vblank feature.
+	 *
+	 * P.S. note that we wouldn't use drm irq handler but
+	 *      just specific driver own one instead because
+	 *      drm framework supports only one irq handler.
+	 */
+	drm_dev->irq_enabled = true;
+
+	/*
+	 * with vblank_disable_allowed = true, vblank interrupt will be disabled
+	 * by drm timer once a current process gives up ownership of
+	 * vblank event.(after drm_vblank_put function is called)
+	 */
+	drm_dev->vblank_disable_allowed = true;
+
+	return 0;
+}
+
+static void lcdc_unbind(struct device *dev, struct device *master,
+			void *data)
+{	struct drm_device *drm_dev = data;
+	struct rockchip_drm_private *private = drm_dev->dev_private;
+	struct lcdc_context *ctx = dev_get_drvdata(dev);
+	struct drm_crtc *crtc = &ctx->crtc;
+
+	drm_crtc_cleanup(crtc);
+	private->crtc[ctx->pipe] = NULL;
+}
+
+static const struct component_ops lcdc_component_ops = {
+	.bind = lcdc_bind,
+	.unbind = lcdc_unbind,
+};
+
+static int lcdc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct lcdc_context *ctx;
+	struct lcdc_driver *lcdc_drv;
+	struct lcdc_driver_data *lcdc_data = drm_lcdc_get_driver_data(pdev);
+	int ret = -EINVAL;
+
+	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	if (!(lcdc_data->num_win && lcdc_data->init &&
+	      lcdc_data->deinit && lcdc_data->dpms &&
+	      lcdc_data->mode_set && lcdc_data->enable_vblank &&
+	      lcdc_data->disable_vblank &&
+	      lcdc_data->win_commit)) {
+		DRM_ERROR("lcdc driver ops is Incomplete\n");
+		return -EINVAL;
+	}
+	lcdc_drv = lcdc_data->init(pdev);
+	if (!lcdc_drv)
+		return -EINVAL;
+
+	lcdc_drv->data = lcdc_data;
+	ret = rockchip_drm_component_add(&pdev->dev, ROCKCHIP_DEVICE_TYPE_CRTC,
+					 ROCKCHIP_DISPLAY_TYPE_LCD, ctx);
+	if (ret)
+		goto err_deinit_lcdc;
+
+	ctx->dev = dev;
+	ctx->default_win = ZPOS_DEFAULT_WIN;
+
+	ctx->drv = lcdc_drv;
+
+	init_waitqueue_head(&ctx->wait_vsync_queue);
+	atomic_set(&ctx->wait_vsync_event, 0);
+
+	platform_set_drvdata(pdev, ctx);
+
+	pm_runtime_enable(&pdev->dev);
+
+	ret = component_add(&pdev->dev, &lcdc_component_ops);
+	if (ret)
+		goto err_disable_pm_runtime;
+
+	return ret;
+
+err_disable_pm_runtime:
+	pm_runtime_disable(dev);
+	rockchip_drm_component_del(dev, ROCKCHIP_DEVICE_TYPE_CRTC);
+err_deinit_lcdc:
+	lcdc_data->deinit(ctx->drv);
+	return ret;
+}
+
+static int lcdc_remove(struct platform_device *pdev)
+{
+	pm_runtime_disable(&pdev->dev);
+
+	component_del(&pdev->dev, &lcdc_component_ops);
+	rockchip_drm_component_del(&pdev->dev, ROCKCHIP_DEVICE_TYPE_CRTC);
+
+	return 0;
+}
+
+struct platform_driver rockchip_lcdc_platform_driver = {
+	.probe = lcdc_probe,
+	.remove = lcdc_remove,
+	.driver = {
+		.name = "rockchip-lcdc",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(lcdc_driver_dt_match),
+	},
+};
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_lcdc.h b/drivers/gpu/drm/rockchip/rockchip_drm_lcdc.h
new file mode 100644
index 0000000..0a0f9c2
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_lcdc.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:mark yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _ROCKCHIP_DRM_LCDC_H_
+#define _ROCKCHIP_DRM_LCDC_H_
+#include <linux/platform_device.h>
+#include <drm/drm_crtc.h>
+
+#include "rockchip_drm_drv.h"
+
+enum {
+	ZPOS_DEFAULT_WIN = 0,
+	ZPOS_CURSOR_WIN,
+	ZPOS_MAX_NUM,
+	ZPOS_UNUSED_WIN
+};
+
+enum data_format {
+	ARGB888 = 0,
+	RGB888,
+	RGB565,
+	YUV420 = 4,
+	YUV422,
+	YUV444,
+	XRGB888,
+	XBGR888,
+	ABGR888,
+	YUV420_A = 10,
+	YUV422_A,
+	YUV444_A
+};
+
+struct lcdc_win_data {
+	int zpos;
+	int id;
+	enum data_format format;
+	u32 xact;
+	u32 yact;
+	u32 xsize;
+	u32 ysize;
+	u32 xpos;
+	u32 ypos;
+	u32 y_vir_stride;
+	u32 uv_vir_stride;
+	bool alpha_en;
+	bool enabled;
+	bool resume;
+
+	dma_addr_t yrgb_addr;
+	dma_addr_t uv_addr;
+
+	int dsp_stx;
+	int dsp_sty;
+	/* win sel layer */
+	int z_order;
+	u8 fmt_cfg;
+	u8 fmt_10;
+	u8 swap_rb;
+	u32 reserved;
+	u32 area_num;
+	u32 scale_yrgb_x;
+	u32 scale_yrgb_y;
+	u32 scale_cbcr_x;
+	u32 scale_cbcr_y;
+	bool support_3d;
+
+	u8 win_lb_mode;
+
+	u8 bic_coe_el;
+	/* h 01:scale up ;10:down */
+	u8 yrgb_hor_scl_mode;
+	/* v 01:scale up ;10:down */
+	u8 yrgb_ver_scl_mode;
+	/* h scale down mode */
+	u8 yrgb_hsd_mode;
+	/* v scale up mode */
+	u8 yrgb_vsu_mode;
+	/* v scale down mode */
+	u8 yrgb_vsd_mode;
+	u8 cbr_hor_scl_mode;
+	u8 cbr_ver_scl_mode;
+	u8 cbr_hsd_mode;
+	u8 cbr_vsu_mode;
+	u8 cbr_vsd_mode;
+	u8 vsd_yrgb_gt4;
+	u8 vsd_yrgb_gt2;
+	u8 vsd_cbr_gt4;
+	u8 vsd_cbr_gt2;
+
+	u32 alpha_mode;
+	u32 g_alpha_val;
+	u32 color_key_val;
+};
+
+struct lcdc_driver_data {
+	int num_win;
+	struct lcdc_driver * (*init)(struct platform_device *pdev);
+	void (*deinit)(struct lcdc_driver *drv);
+	void (*dpms)(struct lcdc_driver *drv, int mode);
+	void (*mode_set)(struct lcdc_driver *drv,
+			 struct drm_display_mode *mode);
+	void (*enable_vblank)(struct lcdc_driver *drv);
+	void (*disable_vblank)(struct lcdc_driver *drv);
+	struct lcdc_win_data * (*get_win)(struct lcdc_driver *drv, int zpos);
+	void (*win_commit)(struct lcdc_driver *drv,
+			   struct lcdc_win_data *win);
+};
+
+struct lcdc_driver {
+	int id;
+
+	struct lcdc_driver_data *data;
+};
+
+
+void lcdc_vsync_event_handler(struct device *dev);
+#ifdef CONFIG_LCDC_RK3288
+extern struct lcdc_driver_data rockchip_rk3288_lcdc;
+#endif
+#endif /* _ROCKCHIP_DRM_LCDC_H_ */
diff --git a/include/uapi/drm/rockchip_drm.h b/include/uapi/drm/rockchip_drm.h
new file mode 100644
index 0000000..1b567f7
--- /dev/null
+++ b/include/uapi/drm/rockchip_drm.h
@@ -0,0 +1,110 @@
+/*
+ *
+ * Copyright (c) Fuzhou Rockchip Electronics Co.Ltd
+ * Authors:
+ *       mark yao <yzq@rock-chips.com>
+ *
+ * base on exynos_drm.h
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#ifndef _UAPI_ROCKCHIP_DRM_H_
+#define _UAPI_ROCKCHIP_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_rockchip_gem_create {
+	uint64_t size;
+	unsigned int flags;
+	unsigned int 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_rockchip_gem_map_off {
+	unsigned int handle;
+	unsigned int pad;
+	uint64_t offset;
+};
+
+/**
+ * A structure for mapping buffer.
+ *
+ * @handle: a handle to gem object created.
+ * @pad: just padding to be 64-bit aligned.
+ * @size: memory size to be mapped.
+ * @mapped: having user virtual address mmaped.
+ *      - this variable would be filled by rockchip gem module
+ *      of kernel side with user virtual address which is allocated
+ *      by do_mmap().
+ */
+struct drm_rockchip_gem_mmap {
+	unsigned int handle;
+	unsigned int pad;
+	uint64_t size;
+	uint64_t mapped;
+};
+
+/**
+ * A structure to gem information.
+ *
+ * @handle: a handle to gem object created.
+ * @flags: flag value including memory type and cache attribute and
+ *      this value would be set by driver.
+ * @size: size to memory region allocated by gem and this size would
+ *      be set by driver.
+ */
+struct drm_rockchip_gem_info {
+	unsigned int handle;
+	unsigned int flags;
+	uint64_t size;
+};
+
+/* memory type definitions. */
+enum e_drm_rockchip_gem_mem_type {
+	/* non-cachable mapping and used as default. */
+	ROCKCHIP_BO_NONCACHABLE = 0 << 0,
+	/* cachable mapping. */
+	ROCKCHIP_BO_CACHABLE = 1 << 0,
+	/* write-combine mapping. */
+	ROCKCHIP_BO_WC = 1 << 1,
+	ROCKCHIP_BO_MASK = ROCKCHIP_BO_CACHABLE | ROCKCHIP_BO_WC
+};
+
+#define DRM_ROCKCHIP_GEM_CREATE		0x00
+#define DRM_ROCKCHIP_GEM_MAP_OFFSET	0x01
+#define DRM_ROCKCHIP_GEM_MMAP		0x02
+/* Reserved 0x03 ~ 0x05 for rockchip specific gem ioctl */
+#define DRM_ROCKCHIP_GEM_GET		0x04
+
+#define DRM_IOCTL_ROCKCHIP_GEM_CREATE	DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_ROCKCHIP_GEM_CREATE, struct drm_rockchip_gem_create)
+
+#define DRM_IOCTL_ROCKCHIP_GEM_MAP_OFFSET	DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_ROCKCHIP_GEM_MAP_OFFSET, struct drm_rockchip_gem_map_off)
+
+#define DRM_IOCTL_ROCKCHIP_GEM_MMAP	DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_ROCKCHIP_GEM_MMAP, struct drm_rockchip_gem_mmap)
+
+#define DRM_IOCTL_ROCKCHIP_GEM_GET	DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_ROCKCHIP_GEM_GET, struct drm_rockchip_gem_info)
+#endif /* _UAPI_ROCKCHIP_DRM_H_ */
-- 
1.7.9.5



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

* [PATCH v2 0/5] Add drm driver for Rockchip Socs
@ 2014-09-18  9:34   ` Mark yao
  0 siblings, 0 replies; 38+ messages in thread
From: Mark yao @ 2014-09-18  9:34 UTC (permalink / raw)
  To: heiko, Boris BREZILLON, David Airlie, Rob Clark, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Randy Dunlap,
	Grant Likely, Greg Kroah-Hartman, John Stultz, Rom Lemarchand
  Cc: devicetree, linux-doc, linux-kernel, dri-devel, linux-api,
	linux-rockchip, dianders, marcheu, dbehr, olof, djkurtz, xjq,
	kfx, cym, cf, zyw, xxm, huangtao, kever.yang, yxj, wxt, xw,
	mark yao

From: mark yao <yzq@rock-chips.com>

This a series of patches is a DRM Driver for Rockchip Socs, add support
for vop devices, eDP. Future patches will add additional encoders/connectors,
such as HDMI.

The basic "crtc" for rockchip is a "VOP" - Video Output Processor.
the vop devices found on Rockchip rk3288 Soc, rk3288 soc have two similar
Vop devices. Vop devices support iommu mapping, we use dma-mapping API with
ARM_DMA_USE_IOMMU.

Changes in v2:
- add DRM master device node to list all display nodes that comprise
  the graphics subsystem.
- use the component framework to defer main drm driver probe
  until all VOP devices have been probed.
- use dma-mapping API with ARM_DMA_USE_IOMMU, create dma mapping by
  master device and each vop device can shared the drm dma mapping.
- use drm_crtc_init_with_planes and drm_universal_plane_init.
- remove unnecessary middle layers.
- add cursor set, move funcs to rockchip drm crtc.
- use panel-simple driver for primary display.
- add vop and edp reset.

Tested on rk3288 pinky board, use eDP encoders/connector, boot and display OK

Mark yao (5):
  drm/rockchip: Add basic drm driver
  dt-bindings: video: Add for rockchip display subsytem
  dt-bindings: video: Add documentation for rockchip vop
  dt-bindings: video: Add documentation for rockchip edp
  drm/rockchip: Add support for Rockchip Soc EDP


-- 
1.7.9.5



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

* [PATCH v2 0/5] Add drm driver for Rockchip Socs
@ 2014-09-18  9:34   ` Mark yao
  0 siblings, 0 replies; 38+ messages in thread
From: Mark yao @ 2014-09-18  9:34 UTC (permalink / raw)
  To: heiko-4mtYJXux2i+zQB+pC5nmwQ, Boris BREZILLON, David Airlie,
	Rob Clark, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, Randy Dunlap, Grant Likely, Greg Kroah-Hartman,
	John Stultz, Rom Lemarchand
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-api-u79uwXL29TY76Z2rM5mHXA,
	linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	dianders-F7+t8E8rja9g9hUCZPvPmw, marcheu-F7+t8E8rja9g9hUCZPvPmw,
	dbehr-F7+t8E8rja9g9hUCZPvPmw, olof-nZhT3qVonbNeoWH0uzbU5w,
	djkurtz-F7+t8E8rja9g9hUCZPvPmw, xjq-TNX95d0MmH7DzftRWevZcw,
	kfx-TNX95d0MmH7DzftRWevZcw, cym-TNX95d0MmH7DzftRWevZcw,
	cf-TNX95d0MmH7DzftRWevZcw, zyw-TNX95d0MmH7DzftRWevZcw,
	xxm-TNX95d0MmH7DzftRWevZcw, huangtao-TNX95d0MmH7DzftRWevZcw,
	kever.yang-TNX95d0MmH7DzftRWevZcw, yxj-TNX95d0MmH7DzftRWevZcw,
	wxt-TNX95d0MmH7DzftRWevZcw, xw-TNX95d0MmH7DzftRWevZcw, mark yao

From: mark yao <yzq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>

This a series of patches is a DRM Driver for Rockchip Socs, add support
for vop devices, eDP. Future patches will add additional encoders/connectors,
such as HDMI.

The basic "crtc" for rockchip is a "VOP" - Video Output Processor.
the vop devices found on Rockchip rk3288 Soc, rk3288 soc have two similar
Vop devices. Vop devices support iommu mapping, we use dma-mapping API with
ARM_DMA_USE_IOMMU.

Changes in v2:
- add DRM master device node to list all display nodes that comprise
  the graphics subsystem.
- use the component framework to defer main drm driver probe
  until all VOP devices have been probed.
- use dma-mapping API with ARM_DMA_USE_IOMMU, create dma mapping by
  master device and each vop device can shared the drm dma mapping.
- use drm_crtc_init_with_planes and drm_universal_plane_init.
- remove unnecessary middle layers.
- add cursor set, move funcs to rockchip drm crtc.
- use panel-simple driver for primary display.
- add vop and edp reset.

Tested on rk3288 pinky board, use eDP encoders/connector, boot and display OK

Mark yao (5):
  drm/rockchip: Add basic drm driver
  dt-bindings: video: Add for rockchip display subsytem
  dt-bindings: video: Add documentation for rockchip vop
  dt-bindings: video: Add documentation for rockchip edp
  drm/rockchip: Add support for Rockchip Soc EDP


-- 
1.7.9.5

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

* [PATCH v2 1/5] drm/rockchip: Add basic drm driver
@ 2014-09-18  9:36     ` Mark yao
  0 siblings, 0 replies; 38+ messages in thread
From: Mark yao @ 2014-09-18  9:36 UTC (permalink / raw)
  To: heiko, Boris BREZILLON, David Airlie, Rob Clark, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Randy Dunlap,
	Grant Likely, Greg Kroah-Hartman, John Stultz, Rom Lemarchand
  Cc: devicetree, linux-doc, linux-kernel, dri-devel, linux-api,
	linux-rockchip, dianders, marcheu, dbehr, olof, djkurtz, xjq,
	kfx, cym, cf, zyw, xxm, huangtao, kever.yang, yxj, wxt, xw,
	Mark yao

This patch adds the basic structure of a DRM Driver for Rockchip Socs.

Signed-off-by: Mark yao <mark.yao@rock-chips.com>
---
Changes in v2:
- use the component framework to defer main drm driver probe
  until all VOP devices have been probed.
- use dma-mapping API with ARM_DMA_USE_IOMMU, create dma mapping by
  master device and each vop device can shared the drm dma mapping.
- use drm_crtc_init_with_planes and drm_universal_plane_init.
- remove unnecessary middle layers.
- add cursor set, move funcs to rockchip drm crtc.
- use vop reset at first init
- reference framebuffer when used and unreference when swap out vop

 drivers/gpu/drm/Kconfig                       |    2 +
 drivers/gpu/drm/Makefile                      |    1 +
 drivers/gpu/drm/rockchip/Kconfig              |   19 +
 drivers/gpu/drm/rockchip/Makefile             |   10 +
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c   |  502 +++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h   |  120 ++
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c    |  201 ++++
 drivers/gpu/drm/rockchip/rockchip_drm_fb.h    |   28 +
 drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c |  231 ++++
 drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h |   20 +
 drivers/gpu/drm/rockchip/rockchip_drm_gem.c   |  405 +++++++
 drivers/gpu/drm/rockchip/rockchip_drm_gem.h   |   76 ++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c   | 1442 +++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h   |  187 ++++
 include/uapi/drm/rockchip_drm.h               |   97 ++
 15 files changed, 3341 insertions(+)
 create mode 100644 drivers/gpu/drm/rockchip/Kconfig
 create mode 100644 drivers/gpu/drm/rockchip/Makefile
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_drv.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_drv.h
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fb.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fb.h
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_gem.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_gem.h
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop.h
 create mode 100644 include/uapi/drm/rockchip_drm.h

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index b066bb3..7c4c3c6 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -171,6 +171,8 @@ config DRM_SAVAGE
 
 source "drivers/gpu/drm/exynos/Kconfig"
 
+source "drivers/gpu/drm/rockchip/Kconfig"
+
 source "drivers/gpu/drm/vmwgfx/Kconfig"
 
 source "drivers/gpu/drm/gma500/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 4a55d59..d03387a 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/
 obj-$(CONFIG_DRM_VIA)	+=via/
 obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
 obj-$(CONFIG_DRM_EXYNOS) +=exynos/
+obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/
 obj-$(CONFIG_DRM_GMA500) += gma500/
 obj-$(CONFIG_DRM_UDL) += udl/
 obj-$(CONFIG_DRM_AST) += ast/
diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
new file mode 100644
index 0000000..7146c80
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -0,0 +1,19 @@
+config DRM_ROCKCHIP
+	tristate "DRM Support for Rockchip"
+	depends on DRM && ROCKCHIP_IOMMU
+	select ARM_DMA_USE_IOMMU
+	select IOMMU_API
+	select DRM_KMS_HELPER
+	select DRM_KMS_FB_HELPER
+	select DRM_PANEL
+	select FB_CFB_FILLRECT
+	select FB_CFB_COPYAREA
+	select FB_CFB_IMAGEBLIT
+	select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE
+	select VIDEOMODE_HELPERS
+	help
+	  Choose this option if you have a Rockchip soc chipset.
+	  This driver provides kernel mode setting and buffer
+	  management to userspace. This driver does not provides
+	  2D or 3D acceleration; acceleration is performed by other
+	  IP found on the SoC.
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
new file mode 100644
index 0000000..6e6d468
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the drm device driver.  This driver provides support for the
+# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
+
+ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/rockchip
+
+rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \
+		rockchip_drm_gem.o rockchip_drm_vop.o
+
+obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
new file mode 100644
index 0000000..f84dcd8
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -0,0 +1,502 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
+ *
+ * based on exynos_drm_drv.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/dma-iommu.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <linux/dma-mapping.h>
+#include <linux/pm_runtime.h>
+#include <linux/of_graph.h>
+#include <linux/component.h>
+
+#include <drm/rockchip_drm.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_fb.h"
+#include "rockchip_drm_fbdev.h"
+#include "rockchip_drm_gem.h"
+
+#define DRIVER_NAME	"rockchip"
+#define DRIVER_DESC	"RockChip Soc DRM"
+#define DRIVER_DATE	"20140818"
+#define DRIVER_MAJOR	1
+#define DRIVER_MINOR	0
+
+/*
+ * Attach a (component) device to the shared drm dma mapping from master drm
+ * device.  This is used by the VOPs to map GEM buffers to a common DMA
+ * mapping.
+ */
+int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
+				   struct device *dev)
+{
+	struct dma_iommu_mapping *mapping = drm_dev->dev->archdata.mapping;
+	int ret;
+
+	ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
+	if (ret)
+		return ret;
+
+	dma_set_max_seg_size(dev, 0xffffffffu);
+
+	return arm_iommu_attach_device(dev, mapping);
+}
+
+void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
+				    struct device *dev)
+{
+	arm_iommu_detach_device(drm_dev->dev);
+}
+
+static int rockchip_drm_load(struct drm_device *drm_dev, unsigned long flags)
+{
+	struct rockchip_drm_private *private;
+	struct dma_iommu_mapping *mapping;
+	struct device *dev = drm_dev->dev;
+	int ret;
+
+	private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL);
+	if (!private)
+		return -ENOMEM;
+
+	dev_set_drvdata(drm_dev->dev, dev);
+	drm_dev->dev_private = private;
+
+	drm_mode_config_init(drm_dev);
+
+	rockchip_drm_mode_config_init(drm_dev);
+
+	dev->dma_parms = devm_kzalloc(dev, sizeof(*dev->dma_parms),
+				      GFP_KERNEL);
+	if (!dev->dma_parms) {
+		ret = -ENOMEM;
+		goto err_config_cleanup;
+	}
+
+	/* TODO(djkurtz): fetch the mapping start/size from somewhere */
+	mapping = arm_iommu_create_mapping(&platform_bus_type, 0x10000000,
+					   SZ_1G);
+	if (IS_ERR(mapping)) {
+		ret = PTR_ERR(mapping);
+		goto err_config_cleanup;
+	}
+
+	dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
+	dma_set_max_seg_size(dev, 0xffffffffu);
+
+	ret = arm_iommu_attach_device(dev, mapping);
+	if (ret)
+		goto err_release_mapping;
+
+	/* Try to bind all sub drivers. */
+	ret = component_bind_all(dev, drm_dev);
+	if (ret)
+		goto err_detach_device;
+
+	/* init kms poll for handling hpd */
+	drm_kms_helper_poll_init(drm_dev);
+
+	/*
+	 * enable drm irq mode.
+	 * - with irq_enabled = true, we can use the vblank feature.
+	 */
+	drm_dev->irq_enabled = true;
+
+	/*
+	 * with vblank_disable_allowed = true, vblank interrupt will be disabled
+	 * by drm timer once a current process gives up ownership of
+	 * vblank event.(after drm_vblank_put function is called)
+	 */
+	drm_dev->vblank_disable_allowed = true;
+
+	ret = drm_vblank_init(drm_dev, ROCKCHIP_MAX_CRTC);
+	if (ret)
+		goto err_kms_helper_poll_fini;
+
+	rockchip_drm_fbdev_init(drm_dev);
+
+	/* force connectors detection */
+	drm_helper_hpd_irq_event(drm_dev);
+
+	return 0;
+
+err_kms_helper_poll_fini:
+	drm_kms_helper_poll_fini(drm_dev);
+	component_unbind_all(dev, drm_dev);
+err_detach_device:
+	arm_iommu_detach_device(dev);
+err_release_mapping:
+	arm_iommu_release_mapping(dev->archdata.mapping);
+err_config_cleanup:
+	drm_mode_config_cleanup(drm_dev);
+	drm_dev->dev_private = NULL;
+	dev_set_drvdata(dev, NULL);
+	return ret;
+}
+
+static int rockchip_drm_unload(struct drm_device *drm_dev)
+{
+	struct device *dev = drm_dev->dev;
+
+	drm_kms_helper_poll_fini(drm_dev);
+	component_unbind_all(dev, drm_dev);
+	arm_iommu_detach_device(dev);
+	arm_iommu_release_mapping(dev->archdata.mapping);
+	drm_mode_config_cleanup(drm_dev);
+	drm_dev->dev_private = NULL;
+	dev_set_drvdata(dev, NULL);
+
+	return 0;
+}
+
+static int rockchip_drm_suspend(struct drm_device *dev, pm_message_t state)
+{
+	struct drm_connector *connector;
+
+	drm_modeset_lock_all(dev);
+	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+		int old_dpms = connector->dpms;
+
+		if (connector->funcs->dpms)
+			connector->funcs->dpms(connector, DRM_MODE_DPMS_OFF);
+
+		/* Set the old mode back to the connector for resume */
+		connector->dpms = old_dpms;
+	}
+	drm_modeset_unlock_all(dev);
+
+	return 0;
+}
+
+static int rockchip_drm_resume(struct drm_device *dev)
+{
+	struct drm_connector *connector;
+
+	drm_modeset_lock_all(dev);
+	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+		if (connector->funcs->dpms)
+			connector->funcs->dpms(connector, connector->dpms);
+	}
+	drm_modeset_unlock_all(dev);
+
+	drm_helper_resume_force_mode(dev);
+
+	return 0;
+}
+
+void rockchip_drm_lastclose(struct drm_device *dev)
+{
+	struct rockchip_drm_private *priv = dev->dev_private;
+
+	drm_modeset_lock_all(dev);
+	if (priv->fb_helper)
+		drm_fb_helper_restore_fbdev_mode(priv->fb_helper);
+	drm_modeset_unlock_all(dev);
+}
+
+static const struct drm_ioctl_desc rockchip_ioctls[] = {
+	DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_CREATE, rockchip_gem_create_ioctl,
+			  DRM_UNLOCKED | DRM_AUTH),
+	DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_GET, rockchip_gem_get_ioctl,
+			  DRM_UNLOCKED),
+	DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_MAP_OFFSET,
+			  rockchip_gem_map_offset_ioctl, DRM_UNLOCKED |
+			  DRM_AUTH),
+	DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_MMAP, rockchip_gem_mmap_ioctl,
+			  DRM_UNLOCKED | DRM_AUTH),
+};
+
+static const struct file_operations rockchip_drm_driver_fops = {
+	.owner = THIS_MODULE,
+	.open = drm_open,
+	.mmap = drm_gem_mmap,
+	.poll = drm_poll,
+	.read = drm_read,
+	.unlocked_ioctl = drm_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = drm_compat_ioctl,
+#endif
+	.release = drm_release,
+};
+
+const struct vm_operations_struct rockchip_drm_vm_ops = {
+	.open = drm_gem_vm_open,
+	.close = drm_gem_vm_close,
+};
+
+static struct drm_driver rockchip_drm_driver = {
+	.driver_features	= DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
+	.load			= rockchip_drm_load,
+	.unload			= rockchip_drm_unload,
+	.lastclose		= rockchip_drm_lastclose,
+	.suspend		= rockchip_drm_suspend,
+	.resume			= rockchip_drm_resume,
+	.get_vblank_counter	= drm_vblank_count,
+	.enable_vblank		= rockchip_drm_crtc_enable_vblank,
+	.disable_vblank		= rockchip_drm_crtc_disable_vblank,
+	.gem_vm_ops		= &rockchip_drm_vm_ops,
+	.gem_free_object	= rockchip_gem_free_object,
+	.dumb_create		= rockchip_gem_dumb_create,
+	.dumb_map_offset	= rockchip_gem_dumb_map_offset,
+	.dumb_destroy		= drm_gem_dumb_destroy,
+	.prime_handle_to_fd	= drm_gem_prime_handle_to_fd,
+	.prime_fd_to_handle	= drm_gem_prime_fd_to_handle,
+	.gem_prime_import	= drm_gem_prime_import,
+	.gem_prime_export	= drm_gem_prime_export,
+	.gem_prime_get_sg_table	= rockchip_gem_prime_get_sg_table,
+	.gem_prime_import_sg_table	= rockchip_gem_prime_import_sg_table,
+	.gem_prime_vmap		= rockchip_gem_prime_vmap,
+	.gem_prime_vunmap	= rockchip_gem_prime_vunmap,
+	.gem_prime_mmap		= rockchip_gem_prime_mmap,
+	.ioctls			= rockchip_ioctls,
+	.num_ioctls		= ARRAY_SIZE(rockchip_ioctls),
+	.fops			= &rockchip_drm_driver_fops,
+	.name	= DRIVER_NAME,
+	.desc	= DRIVER_DESC,
+	.date	= DRIVER_DATE,
+	.major	= DRIVER_MAJOR,
+	.minor	= DRIVER_MINOR,
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int rockchip_drm_sys_suspend(struct device *dev)
+{
+	struct drm_device *drm_dev = dev_get_drvdata(dev);
+	pm_message_t message;
+
+	if (pm_runtime_suspended(dev))
+		return 0;
+
+	message.event = PM_EVENT_SUSPEND;
+
+	return rockchip_drm_suspend(drm_dev, message);
+}
+
+static int rockchip_drm_sys_resume(struct device *dev)
+{
+	struct drm_device *drm_dev = dev_get_drvdata(dev);
+
+	if (pm_runtime_suspended(dev))
+		return 0;
+
+	return rockchip_drm_resume(drm_dev);
+}
+#endif
+
+static const struct dev_pm_ops rockchip_drm_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(rockchip_drm_sys_suspend,
+				rockchip_drm_sys_resume)
+};
+
+int rockchip_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
+			  struct device_node *np)
+{
+	struct rockchip_drm_private *priv = drm->dev_private;
+	struct device_node *port;
+	int pipe;
+
+	if (priv->num_pipe >= ROCKCHIP_MAX_CRTC)
+		return -EINVAL;
+
+	port = of_get_child_by_name(np, "port");
+	of_node_put(np);
+	if (!port) {
+		dev_err(drm->dev, "no port node found in %s\n",
+			np->full_name);
+		return -ENXIO;
+	}
+	pipe = priv->num_pipe++;
+	crtc->port = port;
+
+	priv->crtc[pipe] = crtc;
+
+	return pipe;
+}
+
+void rockchip_drm_remove_crtc(struct drm_device *drm, int pipe)
+{
+	struct rockchip_drm_private *priv = drm->dev_private;
+
+	priv->num_pipe--;
+	of_node_put(priv->crtc[pipe]->port);
+	priv->crtc[pipe] = NULL;
+}
+
+struct drm_crtc *rockchip_find_crtc(struct drm_device *drm, int pipe)
+{
+	struct rockchip_drm_private *priv = drm->dev_private;
+
+	if (pipe < ROCKCHIP_MAX_CRTC && priv->crtc[pipe])
+		return priv->crtc[pipe];
+
+	return NULL;
+}
+
+/*
+ * @node: device tree node containing encoder input ports
+ * @encoder: drm_encoder
+ */
+int rockchip_drm_encoder_get_mux_id(struct device_node *node,
+				    struct drm_encoder *encoder)
+{
+	struct device_node *ep = NULL;
+	struct drm_crtc *crtc = encoder->crtc;
+	struct of_endpoint endpoint;
+	struct device_node *port;
+	int ret;
+
+	if (!node || !crtc)
+		return -EINVAL;
+
+	do {
+		ep = of_graph_get_next_endpoint(node, ep);
+		if (!ep)
+			break;
+
+		port = of_graph_get_remote_port(ep);
+		of_node_put(port);
+		if (port == crtc->port) {
+			ret = of_graph_parse_endpoint(ep, &endpoint);
+			return ret ? ret : endpoint.id;
+		}
+	} while (ep);
+
+	return -EINVAL;
+}
+
+static int compare_of(struct device *dev, void *data)
+{
+	struct device_node *np = data;
+
+	return dev->of_node == np;
+}
+
+static void rockchip_add_endpoints(struct device *dev,
+				   struct component_match **match,
+				   struct device_node *port)
+{
+	struct device_node *ep, *remote;
+
+	for_each_child_of_node(port, ep) {
+		remote = of_graph_get_remote_port_parent(ep);
+		if (!remote || !of_device_is_available(remote)) {
+			of_node_put(remote);
+			continue;
+		} else if (!of_device_is_available(remote->parent)) {
+			dev_warn(dev, "parent device of %s is not available\n",
+				 remote->full_name);
+			of_node_put(remote);
+			continue;
+		}
+
+		component_match_add(dev, match, compare_of, remote);
+		of_node_put(remote);
+	}
+}
+
+static int rockchip_drm_bind(struct device *dev)
+{
+	return drm_platform_init(&rockchip_drm_driver, to_platform_device(dev));
+}
+
+static void rockchip_drm_unbind(struct device *dev)
+{
+	drm_put_dev(dev_get_drvdata(dev));
+}
+
+static const struct component_master_ops rockchip_drm_ops = {
+	.bind = rockchip_drm_bind,
+	.unbind = rockchip_drm_unbind,
+};
+
+static int rockchip_drm_platform_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct component_match *match = NULL;
+	struct device_node *np = dev->of_node;
+	struct device_node *port;
+	int i;
+	int ret;
+
+	if (!np)
+		return -ENODEV;
+	/*
+	 * Bind the crtc ports first, so that
+	 * drm_of_find_possible_crtcs called from encoder .bind callbacks
+	 * works as expected.
+	 */
+	for (i = 0;; i++) {
+		port = of_parse_phandle(np, "ports", i);
+		if (!port)
+			break;
+
+		component_match_add(dev, &match, compare_of, port->parent);
+		of_node_put(port);
+	}
+
+	if (i == 0) {
+		dev_err(dev, "missing 'ports' property\n");
+		return -ENODEV;
+	}
+	/*
+	 * For each bound crtc, bind the encoders attached to its
+	 * remote endpoint.
+	 */
+	for (i = 0;; i++) {
+		port = of_parse_phandle(np, "ports", i);
+		if (!port)
+			break;
+
+		rockchip_add_endpoints(dev, &match, port);
+		of_node_put(port);
+	}
+
+	ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
+	if (ret)
+		return ret;
+
+	return component_master_add_with_match(dev, &rockchip_drm_ops, match);
+}
+
+static int rockchip_drm_platform_remove(struct platform_device *pdev)
+{
+	component_master_del(&pdev->dev, &rockchip_drm_ops);
+	return 0;
+}
+
+static const struct of_device_id rockchip_drm_dt_ids[] = {
+	{ .compatible = "rockchip,display-subsystem", },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, rockchip_drm_dt_ids);
+
+static struct platform_driver rockchip_drm_platform_driver = {
+	.probe = rockchip_drm_platform_probe,
+	.remove = rockchip_drm_platform_remove,
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "rockchip-drm",
+		.of_match_table = rockchip_drm_dt_ids,
+	},
+};
+
+module_platform_driver(rockchip_drm_platform_driver);
+
+MODULE_AUTHOR("Mark Yao <mark.yao@rock-chips.com>");
+MODULE_DESCRIPTION("ROCKCHIP DRM Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
new file mode 100644
index 0000000..154b3ec
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
+ *
+ * based on exynos_drm_drv.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _ROCKCHIP_DRM_DRV_H
+#define _ROCKCHIP_DRM_DRV_H
+
+#include <linux/module.h>
+#include <linux/component.h>
+
+#define ROCKCHIP_MAX_FB_BUFFER	4
+#define ROCKCHIP_MAX_CONNECTOR	2
+
+struct drm_device;
+struct drm_connector;
+
+/*
+ * display output interface supported by rockchip lcdc
+ */
+#define ROCKCHIP_OUTFACE_P888	0
+#define ROCKCHIP_OUTFACE_P666	1
+#define ROCKCHIP_OUTFACE_P565	2
+/* for use special outface */
+#define ROCKCHIP_OUTFACE_AAAA	15
+
+#define ROCKCHIP_COLOR_SWAP_RG	0x1
+#define ROCKCHIP_COLOR_SWAP_RB	0x2
+#define ROCKCHIP_COLOR_SWAP_GB	0x4
+
+/*
+ * Special mode info for rockchip
+ *
+ * @out_type: lcd controller need to know the sceen type.
+ */
+struct rockchip_display_mode {
+	int out_type;
+};
+
+#define ROCKCHIP_EVENT_HOTPLUG	1
+
+enum rockchip_plane_type {
+	ROCKCHIP_WIN0,
+	ROCKCHIP_WIN1,
+	ROCKCHIP_WIN2,
+	ROCKCHIP_WIN3,
+	ROCKCHIP_CURSOR,
+	ROCKCHIP_MAX_PLANE,
+};
+
+/* This enumerates device type. */
+enum rockchip_drm_device_type {
+	ROCKCHIP_DEVICE_TYPE_NONE,
+	ROCKCHIP_DEVICE_TYPE_CRTC,
+	ROCKCHIP_DEVICE_TYPE_CONNECTOR,
+};
+
+/* this enumerates display type. */
+enum rockchip_drm_output_type {
+	ROCKCHIP_DISPLAY_TYPE_NONE = 0,
+	/* RGB Interface. */
+	ROCKCHIP_DISPLAY_TYPE_RGB,
+	/* LVDS Interface. */
+	ROCKCHIP_DISPLAY_TYPE_LVDS,
+	/* EDP Interface. */
+	ROCKCHIP_DISPLAY_TYPE_EDP,
+	/* MIPI Interface. */
+	ROCKCHIP_DISPLAY_TYPE_MIPI,
+	/* HDMI Interface. */
+	ROCKCHIP_DISPLAY_TYPE_HDMI,
+};
+
+enum rockchip_crtc_type {
+	ROCKCHIP_CRTC_VOPB,
+	ROCKCHIP_CRTC_VOPL,
+	ROCKCHIP_MAX_CRTC,
+};
+
+/*
+ * Rockchip drm private structure.
+ *
+ * @num_pipe: number of pipes for this device.
+ */
+struct rockchip_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[ROCKCHIP_MAX_CRTC];
+
+	unsigned int num_pipe;
+};
+
+int rockchip_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
+			  struct device_node *port);
+void rockchip_drm_remove_crtc(struct drm_device *drm, int pipe);
+struct drm_crtc *rockchip_find_crtc(struct drm_device *drm, int pipe);
+int rockchip_drm_encoder_get_mux_id(struct device_node *node,
+				    struct drm_encoder *encoder);
+void rockchip_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe);
+void rockchip_drm_crtc_cancel_pending_flip(struct drm_device *dev);
+int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, int pipe);
+void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, int pipe);
+int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
+				   struct device *dev);
+void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
+				    struct device *dev);
+#endif /* _ROCKCHIP_DRM_DRV_H_ */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
new file mode 100644
index 0000000..b319505
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <drm/drmP.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <uapi/drm/rockchip_drm.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_gem.h"
+
+#define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
+
+struct rockchip_drm_fb {
+	struct drm_framebuffer fb;
+	struct drm_gem_object *obj[ROCKCHIP_MAX_FB_BUFFER];
+};
+
+struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer *fb,
+					       unsigned int plane)
+{
+	struct rockchip_drm_fb *rk_fb = to_rockchip_fb(fb);
+
+	if (plane >= ROCKCHIP_MAX_FB_BUFFER)
+		return NULL;
+
+	return rk_fb->obj[plane];
+}
+
+static void rockchip_drm_fb_destroy(struct drm_framebuffer *fb)
+{
+	struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
+	struct drm_gem_object *obj;
+	int i;
+
+	for (i = 0; i < ROCKCHIP_MAX_FB_BUFFER; i++) {
+		obj = rockchip_fb->obj[i];
+		if (obj)
+			drm_gem_object_unreference_unlocked(obj);
+	}
+
+	drm_framebuffer_cleanup(fb);
+	kfree(rockchip_fb);
+}
+
+static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
+					 struct drm_file *file_priv,
+					 unsigned int *handle)
+{
+	struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
+
+	return drm_gem_handle_create(file_priv,
+				     rockchip_fb->obj[0], handle);
+}
+
+static struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
+	.destroy	= rockchip_drm_fb_destroy,
+	.create_handle	= rockchip_drm_fb_create_handle,
+};
+
+static struct rockchip_drm_fb *
+rockchip_fb_alloc(struct drm_device *dev, struct drm_mode_fb_cmd2 *mode_cmd,
+		  struct drm_gem_object **obj, unsigned int num_planes)
+{
+	struct rockchip_drm_fb *rockchip_fb;
+	int ret;
+	int i;
+
+	rockchip_fb = kzalloc(sizeof(*rockchip_fb), GFP_KERNEL);
+	if (!rockchip_fb)
+		return ERR_PTR(-ENOMEM);
+
+	drm_helper_mode_fill_fb_struct(&rockchip_fb->fb, mode_cmd);
+
+	for (i = 0; i < num_planes; i++)
+		rockchip_fb->obj[i] = obj[i];
+
+	ret = drm_framebuffer_init(dev, &rockchip_fb->fb,
+				   &rockchip_drm_fb_funcs);
+	if (ret) {
+		dev_err(dev->dev, "Failed to initialize framebuffer: %d\n",
+			ret);
+		kfree(rockchip_fb);
+		return ERR_PTR(ret);
+	}
+
+	return rockchip_fb;
+}
+
+static struct drm_framebuffer *
+rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
+			struct drm_mode_fb_cmd2 *mode_cmd)
+{
+	struct rockchip_drm_fb *rockchip_fb;
+	struct drm_gem_object *objs[ROCKCHIP_MAX_FB_BUFFER];
+	struct drm_gem_object *obj;
+	unsigned int hsub;
+	unsigned int vsub;
+	int num_planes;
+	int ret;
+	int i;
+
+	hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format);
+	vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format);
+	num_planes = min(drm_format_num_planes(mode_cmd->pixel_format),
+			 ROCKCHIP_MAX_FB_BUFFER);
+
+	for (i = 0; i < num_planes; i++) {
+		unsigned int width = mode_cmd->width / (i ? hsub : 1);
+		unsigned int height = mode_cmd->height / (i ? vsub : 1);
+		unsigned int min_size;
+
+		obj = drm_gem_object_lookup(dev, file_priv,
+					    mode_cmd->handles[i]);
+		if (!obj) {
+			dev_err(dev->dev, "Failed to lookup GEM object\n");
+			ret = -ENXIO;
+			goto err_gem_object_unreference;
+		}
+
+		min_size = (height - 1) * mode_cmd->pitches[i] +
+			mode_cmd->offsets[i] +
+			width * drm_format_plane_cpp(mode_cmd->pixel_format, i);
+
+		if (obj->size < min_size) {
+			drm_gem_object_unreference_unlocked(obj);
+			ret = -EINVAL;
+			goto err_gem_object_unreference;
+		}
+		objs[i] = obj;
+	}
+
+	rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, objs, i);
+	if (IS_ERR(rockchip_fb)) {
+		ret = PTR_ERR(rockchip_fb);
+		goto err_gem_object_unreference;
+	}
+
+	return &rockchip_fb->fb;
+
+err_gem_object_unreference:
+	for (i--; i >= 0; i--)
+		drm_gem_object_unreference_unlocked(objs[i]);
+	return ERR_PTR(ret);
+}
+
+static void rockchip_drm_output_poll_changed(struct drm_device *dev)
+{
+	struct rockchip_drm_private *private = dev->dev_private;
+	struct drm_fb_helper *fb_helper = private->fb_helper;
+
+	if (fb_helper)
+		drm_fb_helper_hotplug_event(fb_helper);
+}
+
+static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
+	.fb_create = rockchip_user_fb_create,
+	.output_poll_changed = rockchip_drm_output_poll_changed,
+};
+
+struct drm_framebuffer *
+rockchip_drm_framebuffer_init(struct drm_device *dev,
+			      struct drm_mode_fb_cmd2 *mode_cmd,
+			      struct drm_gem_object *obj)
+{
+	struct rockchip_drm_fb *rockchip_fb;
+
+	rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, &obj, 1);
+	if (IS_ERR(rockchip_fb))
+		return NULL;
+
+	return &rockchip_fb->fb;
+}
+
+void rockchip_drm_mode_config_init(struct drm_device *dev)
+{
+	dev->mode_config.min_width = 0;
+	dev->mode_config.min_height = 0;
+
+	/*
+	 * 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 = &rockchip_drm_mode_config_funcs;
+}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.h b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h
new file mode 100644
index 0000000..09574d4
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _ROCKCHIP_DRM_FB_H
+#define _ROCKCHIP_DRM_FB_H
+
+struct drm_framebuffer *
+rockchip_drm_framebuffer_init(struct drm_device *dev,
+			      struct drm_mode_fb_cmd2 *mode_cmd,
+			      struct drm_gem_object *obj);
+void rockchip_drm_framebuffer_fini(struct drm_framebuffer *fb);
+
+void rockchip_drm_mode_config_init(struct drm_device *dev);
+
+struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer *fb,
+					       unsigned int plane);
+#endif /* _ROCKCHIP_DRM_FB_H */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
new file mode 100644
index 0000000..fe1bb22
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+
+#include <drm/rockchip_drm.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_gem.h"
+#include "rockchip_drm_fb.h"
+
+#define PREFERRED_BPP		32
+#define to_rockchip_fbdev(x) container_of(x, struct rockchip_fbdev, helper)
+
+struct rockchip_fbdev {
+	struct drm_fb_helper helper;
+	struct drm_gem_object *bo;
+};
+
+static int rockchip_fbdev_mmap(struct fb_info *info,
+			       struct vm_area_struct *vma)
+{
+	struct drm_fb_helper *helper = info->par;
+	struct rockchip_fbdev *fbdev = to_rockchip_fbdev(helper);
+
+	return rockchip_gem_mmap(fbdev->bo, vma);
+}
+
+static struct fb_ops rockchip_drm_fbdev_ops = {
+	.owner		= THIS_MODULE,
+	.fb_mmap	= rockchip_fbdev_mmap,
+	.fb_fillrect	= cfb_fillrect,
+	.fb_copyarea	= cfb_copyarea,
+	.fb_imageblit	= cfb_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,
+};
+
+static int rockchip_drm_fbdev_create(struct drm_fb_helper *helper,
+				     struct drm_fb_helper_surface_size *sizes)
+{
+	struct rockchip_fbdev *fbdev = to_rockchip_fbdev(helper);
+	struct drm_mode_fb_cmd2 mode_cmd = { 0 };
+	struct drm_device *dev = helper->dev;
+	struct rockchip_gem_object *rk_obj;
+	struct drm_framebuffer *fb;
+	unsigned int bytes_per_pixel;
+	unsigned long offset;
+	struct fb_info *fbi;
+	size_t size;
+	int ret;
+
+	bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);
+
+	mode_cmd.width = sizes->surface_width;
+	mode_cmd.height = sizes->surface_height;
+	mode_cmd.pitches[0] = sizes->surface_width * bytes_per_pixel;
+	mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
+		sizes->surface_depth);
+
+	size = mode_cmd.pitches[0] * mode_cmd.height;
+
+	rk_obj = rockchip_gem_create_object(dev, size);
+	if (IS_ERR(rk_obj))
+		return -ENOMEM;
+
+	fbdev->bo = &rk_obj->base;
+
+	fbi = framebuffer_alloc(0, dev->dev);
+	if (!fbi) {
+		dev_err(dev->dev, "Failed to allocate framebuffer info.\n");
+		ret = -ENOMEM;
+		goto err_rockchip_gem_free_object;
+	}
+
+	helper->fb = rockchip_drm_framebuffer_init(dev, &mode_cmd, fbdev->bo);
+	if (IS_ERR(helper->fb)) {
+		dev_err(dev->dev, "Failed to allocate DRM framebuffer.\n");
+		ret = PTR_ERR(helper->fb);
+		goto err_framebuffer_release;
+	}
+
+	helper->fbdev = fbi;
+
+	fbi->par = helper;
+	fbi->flags = FBINFO_FLAG_DEFAULT;
+	fbi->fbops = &rockchip_drm_fbdev_ops;
+
+	ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
+	if (ret) {
+		dev_err(dev->dev, "Failed to allocate color map.\n");
+		goto err_drm_framebuffer_unref;
+	}
+
+	fb = helper->fb;
+	drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
+	drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
+
+	offset = fbi->var.xoffset * bytes_per_pixel;
+	offset += fbi->var.yoffset * fb->pitches[0];
+
+	dev->mode_config.fb_base = 0;
+	fbi->screen_base = rk_obj->kvaddr + offset;
+	fbi->screen_size = rk_obj->base.size;
+	fbi->fix.smem_len = rk_obj->base.size;
+
+	DRM_DEBUG_KMS("FB [%dx%d]-%d kvaddr=%p offset=%ld size=%d\n",
+		      fb->width, fb->height, fb->depth, rk_obj->kvaddr,
+		      offset, size);
+	return 0;
+
+err_drm_framebuffer_unref:
+	drm_framebuffer_unreference(helper->fb);
+err_framebuffer_release:
+	framebuffer_release(fbi);
+err_rockchip_gem_free_object:
+	rockchip_gem_free_object(&rk_obj->base);
+	return ret;
+}
+
+static struct drm_fb_helper_funcs rockchip_drm_fb_helper_funcs = {
+	.fb_probe = rockchip_drm_fbdev_create,
+};
+
+int rockchip_drm_fbdev_init(struct drm_device *dev)
+{
+	struct rockchip_drm_private *private = dev->dev_private;
+	struct rockchip_fbdev *fbdev;
+	struct drm_fb_helper *helper;
+	unsigned int num_crtc;
+	int ret;
+
+	if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector)
+		return -EINVAL;
+
+	if (private->fb_helper) {
+		DRM_ERROR("no allow to reinit fbdev\n");
+		return -EINVAL;
+	}
+
+	num_crtc = dev->mode_config.num_crtc;
+
+	fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
+	if (!fbdev)
+		return -ENOMEM;
+
+	fbdev->helper.funcs = &rockchip_drm_fb_helper_funcs;
+	helper = &fbdev->helper;
+
+	ret = drm_fb_helper_init(dev, helper, num_crtc, ROCKCHIP_MAX_CONNECTOR);
+	if (ret < 0) {
+		dev_err(dev->dev, "Failed to initialize drm fb helper.\n");
+		goto err_free;
+	}
+
+	ret = drm_fb_helper_single_add_all_connectors(helper);
+	if (ret < 0) {
+		dev_err(dev->dev, "Failed to add connectors.\n");
+		goto err_drm_fb_helper_fini;
+	}
+
+	/* disable all the possible outputs/crtcs before entering KMS mode */
+	drm_helper_disable_unused_functions(dev);
+
+	ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP);
+	if (ret < 0) {
+		dev_err(dev->dev, "Failed to set initial hw configuration.\n");
+		goto err_drm_fb_helper_fini;
+	}
+
+	private->fb_helper = helper;
+
+	return 0;
+
+err_drm_fb_helper_fini:
+	drm_fb_helper_fini(helper);
+err_free:
+	kfree(fbdev);
+	return ret;
+}
+
+void rockchip_drm_fbdev_fini(struct drm_device *dev)
+{
+	struct rockchip_drm_private *private = dev->dev_private;
+	struct drm_fb_helper *helper;
+	struct rockchip_fbdev *fbdev;
+
+	if (!private || !private->fb_helper)
+		return;
+
+	helper = private->fb_helper;
+	fbdev = to_rockchip_fbdev(helper);
+
+	if (helper->fbdev) {
+		struct fb_info *info;
+		int ret;
+
+		info = helper->fbdev;
+		ret = unregister_framebuffer(info);
+		if (ret < 0)
+			DRM_DEBUG_KMS("failed unregister_framebuffer()\n");
+
+		if (info->cmap.len)
+			fb_dealloc_cmap(&info->cmap);
+
+		framebuffer_release(info);
+	}
+
+	if (helper->fb)
+		drm_framebuffer_unreference(helper->fb);
+
+	drm_fb_helper_fini(helper);
+	kfree(fbdev);
+	private->fb_helper = NULL;
+}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
new file mode 100644
index 0000000..5edcf6a
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _ROCKCHIP_DRM_FBDEV_H
+#define _ROCKCHIP_DRM_FBDEV_H
+
+int rockchip_drm_fbdev_init(struct drm_device *dev);
+
+#endif /* _ROCKCHIP_DRM_FBDEV_H */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
new file mode 100644
index 0000000..fd8c4de
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
@@ -0,0 +1,405 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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_vma_manager.h>
+#include <drm/rockchip_drm.h>
+
+#include <linux/anon_inodes.h>
+#include <linux/dma-attrs.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_gem.h"
+
+static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj)
+{
+	struct drm_gem_object *obj = &rk_obj->base;
+	struct drm_device *drm = obj->dev;
+
+	init_dma_attrs(&rk_obj->dma_attrs);
+	dma_set_attr(DMA_ATTR_WRITE_COMBINE, &rk_obj->dma_attrs);
+
+	/* TODO(djkurtz): Use DMA_ATTR_NO_KERNEL_MAPPING except for fbdev */
+	rk_obj->kvaddr = dma_alloc_attrs(drm->dev, obj->size,
+					 &rk_obj->dma_addr, GFP_KERNEL,
+					 &rk_obj->dma_attrs);
+	if (IS_ERR(rk_obj->kvaddr)) {
+		int ret = PTR_ERR(rk_obj->kvaddr);
+
+		DRM_ERROR("failed to allocate %#x byte dma buffer, %d",
+			  obj->size, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void rockchip_gem_free_buf(struct rockchip_gem_object *rk_obj)
+{
+	struct drm_gem_object *obj = &rk_obj->base;
+	struct drm_device *drm = obj->dev;
+
+	dma_free_attrs(drm->dev, obj->size, rk_obj->kvaddr, rk_obj->dma_addr,
+		       &rk_obj->dma_attrs);
+}
+
+int rockchip_drm_gem_mmap_buffer(struct file *filp,
+				 struct vm_area_struct *vma)
+{
+	struct drm_gem_object *obj = filp->private_data;
+
+	return rockchip_gem_mmap(obj, vma);
+}
+
+static const struct file_operations rockchip_drm_gem_fops = {
+	.mmap = rockchip_drm_gem_mmap_buffer,
+};
+
+struct rockchip_gem_object *
+	rockchip_gem_create_object(struct drm_device *drm, unsigned int size)
+{
+	struct rockchip_gem_object *rk_obj;
+	struct drm_gem_object *obj;
+	struct file *filp;
+	int ret;
+
+	size = round_up(size, PAGE_SIZE);
+
+	rk_obj = kzalloc(sizeof(*rk_obj), GFP_KERNEL);
+	if (!rk_obj)
+		return ERR_PTR(-ENOMEM);
+
+	obj = &rk_obj->base;
+
+	drm_gem_private_object_init(drm, obj, size);
+
+	filp = anon_inode_getfile("rockchip_gem", &rockchip_drm_gem_fops,
+				  obj, 0);
+	if (IS_ERR(filp)) {
+		DRM_ERROR("failed to create anon file object.\n");
+		ret = PTR_ERR(filp);
+		goto err_free_rk_obj;
+	}
+	filp->f_mode = FMODE_READ | FMODE_WRITE;
+	obj->filp = filp;
+
+	ret = drm_gem_create_mmap_offset(obj);
+	if (ret)
+		goto err_free_obj;
+
+	ret = rockchip_gem_alloc_buf(rk_obj);
+	if (ret)
+		goto err_free_mmap_offset;
+
+	return rk_obj;
+
+err_free_mmap_offset:
+	drm_gem_free_mmap_offset(obj);
+err_free_obj:
+	drm_gem_object_release(obj);
+err_free_rk_obj:
+	kfree(rk_obj);
+	return ERR_PTR(ret);
+}
+
+/*
+ * rockchip_gem_free_object - (struct drm_driver)->gem_free_object callback
+ * function
+ */
+void rockchip_gem_free_object(struct drm_gem_object *obj)
+{
+	struct rockchip_gem_object *rk_obj;
+
+	drm_gem_free_mmap_offset(obj);
+
+	rk_obj = to_rockchip_obj(obj);
+
+	rockchip_gem_free_buf(rk_obj);
+	drm_gem_free_mmap_offset(obj);
+
+	drm_gem_object_release(obj);
+
+	kfree(rk_obj);
+}
+
+int rockchip_gem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
+{
+	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
+	struct drm_device *drm = obj->dev;
+	unsigned long vm_size;
+
+	vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
+	vm_size = vma->vm_end - vma->vm_start;
+
+	if (vm_size > obj->size)
+		return -EINVAL;
+
+	return dma_mmap_attrs(drm->dev, vma, rk_obj->kvaddr, rk_obj->dma_addr,
+			     obj->size, &rk_obj->dma_attrs);
+}
+
+/*
+ * rockchip_gem_create_with_handle - allocate an object with the given
+ * size and create a gem handle on it
+ *
+ * returns a struct rockchip_gem_object* on success or ERR_PTR values
+ * on failure.
+ */
+static struct rockchip_gem_object *
+rockchip_gem_create_with_handle(struct drm_file *file_priv,
+				struct drm_device *drm, unsigned int size,
+				unsigned int *handle)
+{
+	struct rockchip_gem_object *rk_obj;
+	struct drm_gem_object *obj;
+	int ret;
+
+	rk_obj = rockchip_gem_create_object(drm, size);
+	if (IS_ERR(rk_obj))
+		return NULL;
+
+	obj = &rk_obj->base;
+
+	/*
+	 * 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, obj, handle);
+	if (ret)
+		goto err_handle_create;
+
+	/* drop reference from allocate - handle holds it now. */
+	drm_gem_object_unreference_unlocked(obj);
+
+	return rk_obj;
+
+err_handle_create:
+	rockchip_gem_free_object(obj);
+
+	return ERR_PTR(ret);
+}
+
+int rockchip_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;
+}
+
+/*
+ * rockchip_gem_dumb_create - (struct drm_driver)->dumb_create callback
+ * function
+ *
+ * This aligns the pitch and size arguments to the minimum required. wrap
+ * this into your own function if you need bigger alignment.
+ */
+int rockchip_gem_dumb_create(struct drm_file *file_priv,
+			     struct drm_device *dev,
+			     struct drm_mode_create_dumb *args)
+{
+	struct rockchip_gem_object *rk_obj;
+	int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
+
+	if (args->pitch < min_pitch)
+		args->pitch = min_pitch;
+
+	if (args->size < args->pitch * args->height)
+		args->size = args->pitch * args->height;
+
+	rk_obj = rockchip_gem_create_with_handle(file_priv, dev, args->size,
+						 &args->handle);
+
+	return PTR_ERR_OR_ZERO(rk_obj);
+}
+
+int rockchip_gem_get_ioctl(struct drm_device *dev, void *data,
+			   struct drm_file *file_priv)
+{
+	struct drm_rockchip_gem_info *args = data;
+	struct rockchip_gem_object *rk_obj;
+	struct drm_gem_object *obj;
+
+	mutex_lock(&dev->struct_mutex);
+
+	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+	if (!obj) {
+		DRM_ERROR("failed to lookup gem object.\n");
+		mutex_unlock(&dev->struct_mutex);
+		return -EINVAL;
+	}
+
+	rk_obj = to_rockchip_obj(obj);
+
+	args->flags = rk_obj->flags;
+	args->size = obj->size;
+
+	drm_gem_object_unreference(obj);
+	mutex_unlock(&dev->struct_mutex);
+
+	return 0;
+}
+
+int rockchip_gem_mmap_ioctl(struct drm_device *dev, void *data,
+			    struct drm_file *file_priv)
+{
+	struct drm_rockchip_gem_mmap *args = data;
+	struct drm_gem_object *obj;
+	unsigned long addr;
+
+	mutex_lock(&dev->struct_mutex);
+
+	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+	if (!obj) {
+		DRM_ERROR("failed to lookup gem object.\n");
+		mutex_unlock(&dev->struct_mutex);
+		return -EINVAL;
+	}
+
+	addr = vm_mmap(obj->filp, 0, args->size, PROT_READ | PROT_WRITE,
+		       MAP_SHARED, 0);
+
+	drm_gem_object_unreference(obj);
+
+	if (IS_ERR_VALUE(addr)) {
+		mutex_unlock(&dev->struct_mutex);
+		return (int)addr;
+	}
+
+	mutex_unlock(&dev->struct_mutex);
+
+	args->mapped = addr;
+
+	DRM_DEBUG_KMS("mapped = 0x%lx\n", (unsigned long)args->mapped);
+
+	return 0;
+}
+
+int rockchip_gem_map_offset_ioctl(struct drm_device *drm, void *data,
+				  struct drm_file *file_priv)
+{
+	struct drm_rockchip_gem_map_off *args = data;
+
+	return rockchip_gem_dumb_map_offset(file_priv, drm, args->handle,
+					    &args->offset);
+}
+
+int rockchip_gem_create_ioctl(struct drm_device *dev, void *data,
+			      struct drm_file *file_priv)
+{
+	struct drm_rockchip_gem_create *args = data;
+	struct rockchip_gem_object *rk_obj;
+
+	rk_obj = rockchip_gem_create_with_handle(file_priv, dev, args->size,
+						 &args->handle);
+	return PTR_ERR_OR_ZERO(rk_obj);
+}
+
+/*
+ * Allocate a sg_table for this GEM object.
+ * Note: Both the table's contents, and the sg_table itself must be freed by
+ *       the caller.
+ * Returns a pointer to the newly allocated sg_table, or an ERR_PTR() error.
+ */
+struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj)
+{
+	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
+	struct drm_device *drm = obj->dev;
+	struct sg_table *sgt = NULL;
+	int ret;
+
+	sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
+	if (!sgt)
+		return ERR_PTR(-ENOMEM);
+
+	ret = dma_get_sgtable_attrs(drm->dev, sgt, rk_obj->kvaddr,
+				    rk_obj->dma_addr, obj->size,
+				    &rk_obj->dma_attrs);
+	if (ret) {
+		DRM_ERROR("failed to allocate sgt, %d\n", ret);
+		kfree(sgt);
+		return ERR_PTR(ret);
+	}
+
+	return sgt;
+}
+
+struct drm_gem_object *
+rockchip_gem_prime_import_sg_table(struct drm_device *dev, size_t size,
+				   struct sg_table *sgt)
+{
+	struct rockchip_gem_object *rk_obj;
+
+	if (sgt->nents != 1)
+		return ERR_PTR(-EINVAL);
+
+	rk_obj = rockchip_gem_create_object(dev, size);
+	if (IS_ERR(rk_obj))
+		return ERR_PTR(-ENOMEM);
+
+	return &rk_obj->base;
+}
+
+void *rockchip_gem_prime_vmap(struct drm_gem_object *obj)
+{
+	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
+
+	return rk_obj->kvaddr;
+}
+
+void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
+{
+	/* Nothing to do */
+}
+
+int rockchip_gem_prime_mmap(struct drm_gem_object *obj,
+			    struct vm_area_struct *vma)
+{
+	struct drm_device *dev = obj->dev;
+	int ret;
+
+	mutex_lock(&dev->struct_mutex);
+	ret = drm_gem_mmap_obj(obj, obj->size, vma);
+	mutex_unlock(&dev->struct_mutex);
+
+	return ret;
+}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
new file mode 100644
index 0000000..7f9eb49
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _ROCKCHIP_DRM_GEM_H
+#define _ROCKCHIP_DRM_GEM_H
+
+#define to_rockchip_obj(x) container_of(x, struct rockchip_gem_object, base)
+
+struct rockchip_gem_object {
+	struct drm_gem_object base;
+	unsigned int flags;
+
+	void *kvaddr;
+	dma_addr_t dma_addr;
+	struct dma_attrs dma_attrs;
+};
+
+struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj);
+struct drm_gem_object *
+rockchip_gem_prime_import_sg_table(struct drm_device *dev, size_t size,
+				   struct sg_table *sgt);
+void *rockchip_gem_prime_vmap(struct drm_gem_object *obj);
+void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
+int rockchip_gem_prime_mmap(struct drm_gem_object *obj,
+			    struct vm_area_struct *vma);
+
+/* mmap a gem object to userspace. */
+int rockchip_gem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma);
+
+struct rockchip_gem_object *
+	rockchip_gem_create_object(struct drm_device *drm, unsigned int size);
+
+void rockchip_gem_free_object(struct drm_gem_object *obj);
+
+int rockchip_gem_dumb_create(struct drm_file *file_priv,
+			     struct drm_device *dev,
+			     struct drm_mode_create_dumb *args);
+int rockchip_gem_dumb_map_offset(struct drm_file *file_priv,
+				 struct drm_device *dev, uint32_t handle,
+				 uint64_t *offset);
+int rockchip_gem_map_offset_ioctl(struct drm_device *drm, void *data,
+				  struct drm_file *file_priv);
+/*
+ * request gem object creation and buffer allocation as the size
+ * that it is calculated with framebuffer information such as width,
+ * height and bpp.
+ */
+int rockchip_gem_create_ioctl(struct drm_device *dev, void *data,
+			      struct drm_file *file_priv);
+
+/* get buffer offset to map to user space. */
+int rockchip_gem_map_offset_ioctl(struct drm_device *dev, void *data,
+				  struct drm_file *file_priv);
+
+/*
+ * mmap the physically continuous memory that a gem object contains
+ * to user space.
+ */
+int rockchip_gem_mmap_ioctl(struct drm_device *dev, void *data,
+			    struct drm_file *file_priv);
+
+/* get buffer information to memory region allocated by gem. */
+int rockchip_gem_get_ioctl(struct drm_device *dev, void *data,
+			   struct drm_file *file_priv);
+#endif /* _ROCKCHIP_DRM_GEM_H */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
new file mode 100644
index 0000000..f8e1228
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -0,0 +1,1442 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/component.h>
+
+#include <linux/reset.h>
+#include <linux/iommu.h>
+#include <linux/delay.h>
+#include <drm/rockchip_drm.h>
+
+#include <video/of_display_timing.h>
+#include <video/of_videomode.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_fbdev.h"
+#include "rockchip_drm_gem.h"
+#include "rockchip_drm_fb.h"
+#include "rockchip_drm_vop.h"
+
+#define VOP_DEFAULT_FRAMERATE	60
+#define VOP_MAX_WIN_SUPPORT	5
+#define VOP_DEFAULT_CURSOR	1
+#define VOP_REG(off, _mask, s) \
+		{.offset = off, \
+		 .mask = _mask, \
+		 .shift = s,}
+
+#define __REG_SET(x, off, mask, shift, v) \
+		vop_mask_write(x, off, (mask) << shift, (v) << shift)
+
+#define REG_SET(x, base, reg, v) \
+		__REG_SET(x, base + reg.offset, reg.mask, reg.shift, v)
+
+#define VOP_WIN_SET(x, win, name, v) \
+		REG_SET(x, win->base, win->phy->name, v)
+#define VOP_CTRL_SET(x, name, v) \
+		REG_SET(x, 0, (x)->data->ctrl->name, v)
+
+#define VOP_WIN_GET_YRGBADDR(ctx, win) \
+		vop_readl(ctx, win->base + win->phy->yrgb_mst.offset)
+
+#define to_vop_ctx(x) container_of(x, struct vop_context, crtc)
+#define to_rockchip_plane(x) container_of(x, struct rockchip_plane, base)
+
+struct rockchip_plane {
+	int id;
+	struct drm_plane base;
+	const struct vop_win *win;
+	struct vop_context *ctx;
+
+	uint32_t pending_yrgb_mst;
+	struct drm_framebuffer *front_fb;
+	struct drm_framebuffer *pending_fb;
+	bool enabled;
+};
+
+struct vop_context {
+	struct device *dev;
+	struct drm_device *drm_dev;
+	struct drm_crtc crtc;
+	struct drm_pending_vblank_event *event;
+	struct drm_framebuffer *cursor_fb;
+	struct vop_driver *drv;
+	unsigned int dpms;
+	unsigned int win_mask;
+	wait_queue_head_t wait_vsync_queue;
+	atomic_t wait_vsync_event;
+
+	struct workqueue_struct *vsync_wq;
+	struct work_struct vsync_work;
+
+	int cursor_x;
+	int cursor_y;
+	/* mutex vsync_ work */
+	struct mutex vsync_mutex;
+	bool vsync_work_pending;
+
+	struct vop_driver_data *data;
+
+	uint32_t *regsbak;
+	void __iomem *regs;
+
+	/* physical map length of vop register */
+	uint32_t len;
+
+	/* one time only one process allowed to config the register */
+	spinlock_t reg_lock;
+	/* lock vop irq reg */
+	spinlock_t irq_lock;
+
+	unsigned int irq;
+
+	/* vop AHP clk */
+	struct clk *hclk;
+	/* vop dclk */
+	struct clk *dclk;
+	/* vop share memory frequency */
+	struct clk *aclk;
+	uint32_t pixclock;
+
+	int pipe;
+	bool clk_on;
+};
+
+enum vop_data_format {
+	VOP_FMT_ARGB8888 = 0,
+	VOP_FMT_RGB888,
+	VOP_FMT_RGB565,
+	VOP_FMT_YUV420SP = 4,
+	VOP_FMT_YUV422SP,
+	VOP_FMT_YUV444SP,
+};
+
+struct vop_reg_data {
+	uint32_t offset;
+	uint32_t value;
+};
+
+struct vop_reg {
+	uint32_t offset;
+	uint32_t shift;
+	uint32_t mask;
+};
+
+struct vop_ctrl {
+	struct vop_reg standby;
+	struct vop_reg gate_en;
+	struct vop_reg mmu_en;
+	struct vop_reg rgb_en;
+	struct vop_reg edp_en;
+	struct vop_reg hdmi_en;
+	struct vop_reg mipi_en;
+	struct vop_reg out_mode;
+	struct vop_reg dither_down;
+	struct vop_reg dither_up;
+	struct vop_reg pin_pol;
+
+	struct vop_reg htotal_pw;
+	struct vop_reg hact_st_end;
+	struct vop_reg vtotal_pw;
+	struct vop_reg vact_st_end;
+	struct vop_reg hpost_st_end;
+	struct vop_reg vpost_st_end;
+};
+
+struct vop_win_phy {
+	const uint32_t *data_formats;
+	uint32_t nformats;
+
+	struct vop_reg enable;
+	struct vop_reg format;
+	struct vop_reg act_info;
+	struct vop_reg dsp_info;
+	struct vop_reg dsp_st;
+	struct vop_reg yrgb_mst;
+	struct vop_reg uv_mst;
+	struct vop_reg yrgb_vir;
+	struct vop_reg uv_vir;
+
+	struct vop_reg dst_alpha_ctl;
+	struct vop_reg src_alpha_ctl;
+};
+
+struct vop_win {
+	uint32_t base;
+	const struct vop_win_phy *phy;
+};
+
+struct vop_driver_data {
+	const void *init_table;
+	int table_size;
+	const struct vop_ctrl *ctrl;
+	const struct vop_win *win[VOP_MAX_WIN_SUPPORT];
+};
+
+static const uint32_t formats_01[] = {
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_NV12,
+	DRM_FORMAT_NV16,
+	DRM_FORMAT_NV24,
+};
+
+static const uint32_t formats_234[] = {
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_RGB565,
+};
+
+static const struct vop_win_phy win01_data = {
+	.data_formats = formats_01,
+	.nformats = ARRAY_SIZE(formats_01),
+	.enable = VOP_REG(WIN0_CTRL0, 0x1, 0),
+	.format = VOP_REG(WIN0_CTRL0, 0x7, 1),
+	.act_info = VOP_REG(WIN0_ACT_INFO, 0x1fff1fff, 0),
+	.dsp_info = VOP_REG(WIN0_DSP_INFO, 0x1fff1fff, 0),
+	.dsp_st = VOP_REG(WIN0_DSP_ST, 0x1fff1fff, 0),
+	.yrgb_mst = VOP_REG(WIN0_YRGB_MST, 0xffffffff, 0),
+	.uv_mst = VOP_REG(WIN0_CBR_MST, 0xffffffff, 0),
+	.yrgb_vir = VOP_REG(WIN0_VIR, 0x3fff, 0),
+	.uv_vir = VOP_REG(WIN0_VIR, 0x3fff, 16),
+	.src_alpha_ctl = VOP_REG(WIN0_SRC_ALPHA_CTRL, 0xff, 0),
+	.dst_alpha_ctl = VOP_REG(WIN0_DST_ALPHA_CTRL, 0xff, 0),
+};
+
+static const struct vop_win_phy win23_data = {
+	.data_formats = formats_234,
+	.nformats = ARRAY_SIZE(formats_234),
+	.enable = VOP_REG(WIN2_CTRL0, 0x1, 0),
+	.format = VOP_REG(WIN2_CTRL0, 0x7, 1),
+	.dsp_info = VOP_REG(WIN2_DSP_INFO0, 0x0fff0fff, 0),
+	.dsp_st = VOP_REG(WIN2_DSP_ST0, 0x1fff1fff, 0),
+	.yrgb_mst = VOP_REG(WIN2_MST0, 0xffffffff, 0),
+	.yrgb_vir = VOP_REG(WIN2_VIR0_1, 0x1fff, 0),
+	.src_alpha_ctl = VOP_REG(WIN2_SRC_ALPHA_CTRL, 0xff, 0),
+	.dst_alpha_ctl = VOP_REG(WIN2_DST_ALPHA_CTRL, 0xff, 0),
+};
+
+static const struct vop_win_phy cursor_data = {
+	.data_formats = formats_234,
+	.nformats = ARRAY_SIZE(formats_234),
+	.enable = VOP_REG(HWC_CTRL0, 0x1, 0),
+	.format = VOP_REG(HWC_CTRL0, 0x7, 1),
+	.dsp_st = VOP_REG(HWC_DSP_ST, 0x1fff1fff, 0),
+	.yrgb_mst = VOP_REG(HWC_MST, 0xffffffff, 0),
+};
+
+static const struct vop_win win0 = {
+	.base = 0,
+	.phy = &win01_data,
+};
+
+static const struct vop_win win1 = {
+	.base = 0x40,
+	.phy = &win01_data,
+};
+
+static const struct vop_win win2 = {
+	.base = 0,
+	.phy = &win23_data,
+};
+
+static const struct vop_win win3 = {
+	.base = 0x50,
+	.phy = &win23_data,
+};
+
+static const struct vop_win win_cursor = {
+	.base = 0,
+	.phy = &cursor_data,
+};
+
+static const struct vop_ctrl ctrl_data = {
+	.standby = VOP_REG(SYS_CTRL, 0x1, 22),
+	.gate_en = VOP_REG(SYS_CTRL, 0x1, 23),
+	.mmu_en = VOP_REG(SYS_CTRL, 0x1, 20),
+	.rgb_en = VOP_REG(SYS_CTRL, 0x1, 12),
+	.hdmi_en = VOP_REG(SYS_CTRL, 0x1, 13),
+	.edp_en = VOP_REG(SYS_CTRL, 0x1, 14),
+	.mipi_en = VOP_REG(SYS_CTRL, 0x1, 15),
+	.dither_down = VOP_REG(DSP_CTRL1, 0xf, 1),
+	.dither_up = VOP_REG(DSP_CTRL1, 0x1, 6),
+	.out_mode = VOP_REG(DSP_CTRL0, 0xf, 0),
+	.pin_pol = VOP_REG(DSP_CTRL0, 0xf, 4),
+	.htotal_pw = VOP_REG(DSP_HTOTAL_HS_END, 0x1fff1fff, 0),
+	.hact_st_end = VOP_REG(DSP_HACT_ST_END, 0x1fff1fff, 0),
+	.vtotal_pw = VOP_REG(DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
+	.vact_st_end = VOP_REG(DSP_VACT_ST_END, 0x1fff1fff, 0),
+	.hpost_st_end = VOP_REG(POST_DSP_HACT_INFO, 0x1fff1fff, 0),
+	.vpost_st_end = VOP_REG(POST_DSP_VACT_INFO, 0x1fff1fff, 0),
+};
+
+static const struct vop_reg_data vop_init_reg_table[] = {
+	{SYS_CTRL, 0x00801000},
+	{DSP_CTRL0, 0x00000000},
+	{WIN0_CTRL0, 0x00000080},
+	{WIN1_CTRL0, 0x00000080},
+};
+
+static const struct vop_driver_data rockchip_rk3288_vop = {
+	.init_table = vop_init_reg_table,
+	.table_size = ARRAY_SIZE(vop_init_reg_table),
+	.ctrl = &ctrl_data,
+	.win[0] = &win0,
+	.win[1] = &win1,
+	.win[2] = &win2,
+	.win[3] = &win3,
+	.win[4] = &win_cursor,
+};
+
+static const struct of_device_id vop_driver_dt_match[] = {
+	{ .compatible = "rockchip,rk3288-vop",
+	  .data = (void *)&rockchip_rk3288_vop },
+	{},
+};
+
+static inline void vop_writel(struct vop_context *ctx,
+			      uint32_t offset, uint32_t v)
+{
+	writel(v, ctx->regs + offset);
+	ctx->regsbak[offset >> 2] = v;
+}
+
+static inline uint32_t vop_readl(struct vop_context *ctx, uint32_t offset)
+{
+	return readl(ctx->regs + offset);
+}
+
+static inline void vop_cfg_done(struct vop_context *ctx)
+{
+	writel(0x01, ctx->regs + REG_CFG_DONE);
+}
+
+static inline void vop_mask_write(struct vop_context *ctx,
+				  uint32_t offset, uint32_t mask, uint32_t v)
+{
+	if (mask) {
+		uint32_t cached_val = ctx->regsbak[offset >> 2];
+
+		cached_val = (cached_val & ~mask) | v;
+		writel(cached_val, ctx->regs + offset);
+		ctx->regsbak[offset >> 2] = cached_val;
+	}
+}
+
+static inline struct vop_driver_data *vop_get_driver_data(struct device *dev)
+{
+	const struct of_device_id *of_id =
+			of_match_device(vop_driver_dt_match, dev);
+
+	return (struct vop_driver_data *)of_id->data;
+}
+
+static enum vop_data_format vop_convert_format(uint32_t format)
+{
+	switch (format) {
+	case DRM_FORMAT_XRGB8888:
+	case DRM_FORMAT_ARGB8888:
+		return VOP_FMT_ARGB8888;
+	case DRM_FORMAT_RGB888:
+		return VOP_FMT_RGB888;
+	case DRM_FORMAT_RGB565:
+		return VOP_FMT_RGB565;
+	case DRM_FORMAT_NV12:
+		return VOP_FMT_YUV420SP;
+	case DRM_FORMAT_NV16:
+		return VOP_FMT_YUV422SP;
+	case DRM_FORMAT_NV24:
+		return VOP_FMT_YUV444SP;
+	default:
+		DRM_ERROR("unsupport format[%08x]\n", format);
+		return -EINVAL;
+	}
+}
+
+static bool is_alpha_support(uint32_t format)
+{
+	switch (format) {
+	case DRM_FORMAT_ARGB8888:
+		return true;
+	default:
+		return false;
+	}
+}
+
+/* TODO(djkurtz): move generic 'setup slave rk_iommu' code somewhere common */
+int vop_iommu_init(struct vop_context *ctx)
+{
+	struct device *dev = ctx->dev;
+	struct device_node *np = dev->of_node;
+	struct platform_device *pd;
+	int count;
+	int ret;
+	struct of_phandle_args args;
+
+	/* Each VOP must have exactly one iommu node, with no args */
+	count = of_count_phandle_with_args(np, "iommus", "#iommu-cells");
+	if (count != 1) {
+		dev_err(dev, "of_count_phandle_with_args(%s) => %d\n",
+			np->full_name, count);
+		return -EINVAL;
+	}
+
+	ret = of_parse_phandle_with_args(np, "iommus", "#iommu-cells", 0,
+					 &args);
+	if (ret) {
+		dev_err(dev, "of_parse_phandle_with_args(%s) => %d\n",
+			np->full_name, ret);
+		return ret;
+	}
+	if (args.args_count != 0) {
+		dev_err(dev, "incorrect number of iommu params found for %s (found %d, expected 0)\n",
+			args.np->full_name, args.args_count);
+		return -EINVAL;
+	}
+
+	pd = of_find_device_by_node(args.np);
+	of_node_put(args.np);
+	if (!pd) {
+		dev_err(dev, "iommu %s not found\n", args.np->full_name);
+		return -EPROBE_DEFER;
+	}
+
+	/* TODO(djkurtz): handle multiple slave iommus for a single master */
+	dev->archdata.iommu = &pd->dev;
+
+	ret = rockchip_drm_dma_attach_device(ctx->drm_dev, dev);
+	if (ret) {
+		dev_err(dev, "failed to attach to drm dma mapping, %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void vop_iommu_fini(struct vop_context *ctx)
+{
+	rockchip_drm_dma_detach_device(ctx->drm_dev, ctx->dev);
+}
+
+static int rockchip_plane_get_size(int start, unsigned length, unsigned last)
+{
+	int end = start + length;
+	int size = 0;
+
+	if (start <= 0) {
+		if (end > 0)
+			size = min_t(unsigned, end, last);
+	} else if (start <= last) {
+		size = min_t(unsigned, last - start, length);
+	}
+
+	return size;
+}
+
+static int vop_clk_enable(struct vop_context *ctx)
+{
+	int ret;
+
+	if (!ctx->clk_on) {
+		ret = clk_prepare_enable(ctx->hclk);
+		if (ret < 0) {
+			dev_err(ctx->dev, "failed to enable hclk\n");
+			return ret;
+		}
+
+		ret = clk_prepare_enable(ctx->dclk);
+		if (ret < 0) {
+			dev_err(ctx->dev, "failed to enable dclk\n");
+			goto err_dclk;
+		}
+
+		ret = clk_prepare_enable(ctx->aclk);
+		if (ret < 0) {
+			dev_err(ctx->dev, "failed to enable aclk\n");
+			goto err_aclk;
+		}
+		ctx->clk_on = true;
+	}
+
+	return ret;
+err_aclk:
+	clk_disable_unprepare(ctx->aclk);
+err_dclk:
+	clk_disable_unprepare(ctx->hclk);
+	return ret;
+}
+
+static void vop_clk_disable(struct vop_context *ctx)
+{
+	if (ctx->clk_on) {
+		clk_disable_unprepare(ctx->dclk);
+		clk_disable_unprepare(ctx->hclk);
+		clk_disable_unprepare(ctx->aclk);
+		ctx->clk_on = false;
+	}
+}
+
+static void vop_power_on(struct vop_context *ctx)
+{
+	if (vop_clk_enable(ctx) < 0) {
+		dev_err(ctx->dev, "failed to enable clks\n");
+		return;
+	}
+
+	spin_lock(&ctx->reg_lock);
+
+	VOP_CTRL_SET(ctx, standby, 0);
+
+	spin_unlock(&ctx->reg_lock);
+}
+
+static void vop_power_off(struct vop_context *ctx)
+{
+	spin_lock(&ctx->reg_lock);
+
+	VOP_CTRL_SET(ctx, standby, 1);
+
+	spin_unlock(&ctx->reg_lock);
+
+	vop_clk_disable(ctx);
+}
+
+static int rockchip_crtc_update_cursor(struct drm_crtc *crtc,
+				       struct drm_plane *plane)
+{
+	struct vop_context *ctx = to_vop_ctx(crtc);
+
+	return plane->funcs->update_plane(plane, crtc, ctx->cursor_fb,
+					  ctx->cursor_x, ctx->cursor_y,
+					  ctx->cursor_fb->width,
+					  ctx->cursor_fb->height, 0, 0,
+					  ctx->cursor_fb->width << 16,
+					  ctx->cursor_fb->height << 16);
+}
+
+static int vop_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file,
+			       uint32_t handle, uint32_t width,
+			       uint32_t height)
+{
+	struct vop_context *ctx = to_vop_ctx(crtc);
+	struct drm_plane *cursor = crtc->cursor;
+	int cpp;
+	struct drm_mode_fb_cmd2 mode_cmd = { 0 };
+	struct drm_gem_object *obj;
+
+	if (!handle) {
+		cursor->funcs->disable_plane(cursor);
+		if (ctx->cursor_fb) {
+			drm_framebuffer_unreference(ctx->cursor_fb);
+			ctx->cursor_fb = NULL;
+		}
+		return 0;
+	}
+
+	obj = drm_gem_object_lookup(crtc->dev, file, handle);
+	if (!obj) {
+		DRM_ERROR("failed to lookup gem object.\n");
+		return -EINVAL;
+	}
+	if (ctx->cursor_fb)
+		drm_framebuffer_unreference(ctx->cursor_fb);
+	cpp = drm_format_plane_cpp(DRM_FORMAT_ARGB8888, 0);
+
+	mode_cmd.width = width;
+	mode_cmd.height = height;
+	mode_cmd.pitches[0] = width * cpp;
+	mode_cmd.pixel_format = DRM_FORMAT_ARGB8888;
+	ctx->cursor_fb = rockchip_drm_framebuffer_init(ctx->drm_dev,
+						       &mode_cmd, obj);
+
+	return rockchip_crtc_update_cursor(crtc, cursor);
+}
+
+static int vop_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
+{
+	struct vop_context *ctx = to_vop_ctx(crtc);
+	struct drm_plane *cursor = crtc->cursor;
+	struct rockchip_plane *rockchip_plane = to_rockchip_plane(cursor);
+
+	if (!rockchip_plane->enabled)
+		return 0;
+
+	ctx->cursor_x = x;
+	ctx->cursor_y = y;
+
+	return rockchip_crtc_update_cursor(crtc, cursor);
+}
+
+static int rockchip_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
+				 struct drm_framebuffer *fb, int crtc_x,
+				 int crtc_y, unsigned int crtc_w,
+				 unsigned int crtc_h, uint32_t src_x,
+				 uint32_t src_y, uint32_t src_w, uint32_t src_h)
+{
+	struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
+	const struct vop_win *win = rockchip_plane->win;
+	struct vop_context *ctx = to_vop_ctx(crtc);
+	struct drm_gem_object *obj;
+	struct rockchip_gem_object *rk_obj;
+	unsigned long offset;
+	unsigned int actual_w;
+	unsigned int actual_h;
+	unsigned int dsp_stx;
+	unsigned int dsp_sty;
+	unsigned int y_vir_stride;
+	dma_addr_t yrgb_mst;
+	enum vop_data_format format;
+	uint32_t val;
+	bool is_alpha;
+
+	if (!win) {
+		DRM_ERROR("can't find win data for vop, failed\n");
+		return -EINVAL;
+	}
+
+	obj = rockchip_fb_get_gem_obj(fb, 0);
+	if (!obj) {
+		DRM_ERROR("fail to get rockchip gem object from framebuffer\n");
+		return -EINVAL;
+	}
+
+	rk_obj = to_rockchip_obj(obj);
+
+	yrgb_mst = rk_obj->dma_addr;
+	if (yrgb_mst <= 0)
+		return -ENOMEM;
+
+	actual_w = rockchip_plane_get_size(crtc_x,
+					   crtc_w, crtc->mode.hdisplay);
+	actual_h = rockchip_plane_get_size(crtc_y,
+					   crtc_h, crtc->mode.vdisplay);
+	if (crtc_x < 0) {
+		if (actual_w)
+			src_x -= crtc_x;
+		crtc_x = 0;
+	}
+
+	if (crtc_y < 0) {
+		if (actual_h)
+			src_y -= crtc_y;
+		crtc_y = 0;
+	}
+
+	dsp_stx = crtc_x + crtc->mode.htotal - crtc->mode.hsync_start;
+	dsp_sty = crtc_y + crtc->mode.vtotal - crtc->mode.vsync_start;
+
+	offset = src_x * (fb->bits_per_pixel >> 3);
+	offset += src_y * fb->pitches[0];
+
+	y_vir_stride = fb->pitches[0] / (fb->bits_per_pixel >> 3);
+	is_alpha = is_alpha_support(fb->pixel_format);
+	format = vop_convert_format(fb->pixel_format);
+
+	spin_lock(&ctx->reg_lock);
+
+	VOP_WIN_SET(ctx, win, format, format);
+	VOP_WIN_SET(ctx, win, yrgb_vir, y_vir_stride);
+	yrgb_mst += offset;
+	VOP_WIN_SET(ctx, win, yrgb_mst, yrgb_mst);
+	VOP_WIN_SET(ctx, win, act_info,
+		    ((actual_h - 1) << 16) | (actual_w - 1));
+	VOP_WIN_SET(ctx, win, dsp_info,
+		    ((actual_h - 1) << 16) | (actual_w - 1));
+	VOP_WIN_SET(ctx, win, dsp_st, (dsp_sty << 16) | dsp_stx);
+	if (is_alpha) {
+		VOP_WIN_SET(ctx, win, dst_alpha_ctl,
+			    DST_FACTOR_M0(ALPHA_SRC_INVERSE));
+		val = SRC_ALPHA_EN(1) | SRC_COLOR_M0(ALPHA_SRC_PRE_MUL) |
+			SRC_ALPHA_M0(ALPHA_STRAIGHT) |
+			SRC_BLEND_M0(ALPHA_PER_PIX) |
+			SRC_ALPHA_CAL_M0(ALPHA_NO_SATURATION) |
+			SRC_FACTOR_M0(ALPHA_ONE);
+		VOP_WIN_SET(ctx, win, src_alpha_ctl, val);
+	} else {
+		VOP_WIN_SET(ctx, win, src_alpha_ctl, SRC_ALPHA_EN(0));
+	}
+
+	VOP_WIN_SET(ctx, win, enable, 1);
+
+	spin_unlock(&ctx->reg_lock);
+
+	mutex_lock(&ctx->vsync_mutex);
+
+	/*
+	 * Because the buffer set to vop take effect at frame start time,
+	 * we need make sure old buffer is not in use before we release
+	 * it.
+	 * reference the framebuffer, and unference it when it swap out of vop.
+	 */
+	if (fb != rockchip_plane->front_fb) {
+		drm_framebuffer_reference(fb);
+		rockchip_plane->pending_fb = fb;
+		rockchip_plane->pending_yrgb_mst = yrgb_mst;
+		ctx->vsync_work_pending = true;
+	}
+	rockchip_plane->enabled = true;
+
+	mutex_unlock(&ctx->vsync_mutex);
+
+	spin_lock(&ctx->reg_lock);
+	vop_cfg_done(ctx);
+	spin_unlock(&ctx->reg_lock);
+
+	return 0;
+}
+
+static int rockchip_disable_plane(struct drm_plane *plane)
+{
+	struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
+	struct vop_context *ctx = rockchip_plane->ctx;
+	const struct vop_win *win = rockchip_plane->win;
+
+	spin_lock(&ctx->reg_lock);
+
+	VOP_WIN_SET(ctx, win, enable, 0);
+	vop_cfg_done(ctx);
+
+	spin_unlock(&ctx->reg_lock);
+
+	mutex_lock(&ctx->vsync_mutex);
+
+	/*
+	* clear the pending framebuffer and set vsync_work_pending true,
+	* so that the framebuffer will unref at the next vblank.
+	*/
+	if (rockchip_plane->pending_fb) {
+		drm_framebuffer_unreference(rockchip_plane->pending_fb);
+		rockchip_plane->pending_fb = NULL;
+	}
+
+	rockchip_plane->enabled = false;
+	ctx->vsync_work_pending = true;
+
+	mutex_unlock(&ctx->vsync_mutex);
+
+	return 0;
+}
+
+static void rockchip_plane_destroy(struct drm_plane *plane)
+{
+	struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
+	struct vop_context *ctx = rockchip_plane->ctx;
+
+	rockchip_disable_plane(plane);
+	drm_plane_cleanup(plane);
+	ctx->win_mask &= ~(1 << rockchip_plane->id);
+	kfree(rockchip_plane);
+}
+
+static const struct drm_plane_funcs rockchip_plane_funcs = {
+	.update_plane = rockchip_update_plane,
+	.disable_plane = rockchip_disable_plane,
+	.destroy = rockchip_plane_destroy,
+};
+
+struct drm_plane *rockchip_plane_init(struct vop_context *ctx,
+				      unsigned long possible_crtcs,
+				      enum drm_plane_type type)
+{
+	struct rockchip_plane *rockchip_plane;
+	struct vop_driver_data *vop_data = ctx->data;
+	const struct vop_win *win;
+	int i;
+	int err;
+
+	rockchip_plane = kzalloc(sizeof(*rockchip_plane), GFP_KERNEL);
+	if (!rockchip_plane)
+		return NULL;
+
+	for (i = 0; i < VOP_MAX_WIN_SUPPORT; i++) {
+		if (!(ctx->win_mask & (1 << i))) {
+			win = vop_data->win[i];
+			break;
+		}
+	}
+
+	if (VOP_MAX_WIN_SUPPORT == i) {
+		DRM_ERROR("failed to find win\n");
+		kfree(rockchip_plane);
+		return NULL;
+	}
+
+	ctx->win_mask |= (1 << i);
+	rockchip_plane->id = i;
+	rockchip_plane->win = win;
+	rockchip_plane->ctx = ctx;
+
+	err = drm_universal_plane_init(ctx->drm_dev, &rockchip_plane->base,
+				       possible_crtcs, &rockchip_plane_funcs,
+				       win->phy->data_formats,
+				       win->phy->nformats, type);
+	if (err) {
+		DRM_ERROR("failed to initialize plane\n");
+		kfree(rockchip_plane);
+		return NULL;
+	}
+
+	return &rockchip_plane->base;
+}
+
+int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, int pipe)
+{
+	struct vop_context *ctx = to_vop_ctx(rockchip_find_crtc(dev, pipe));
+	unsigned long flags;
+
+	if (ctx->dpms != DRM_MODE_DPMS_ON)
+		return -EPERM;
+
+	spin_lock_irqsave(&ctx->irq_lock, flags);
+
+	vop_mask_write(ctx, INTR_CTRL0, LINE_FLAG_INTR_MASK,
+		       LINE_FLAG_INTR_EN(1));
+
+	spin_unlock_irqrestore(&ctx->irq_lock, flags);
+
+	return 0;
+}
+
+void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, int pipe)
+{
+	struct vop_context *ctx = to_vop_ctx(rockchip_find_crtc(dev, pipe));
+	unsigned long flags;
+
+	if (ctx->dpms != DRM_MODE_DPMS_ON)
+		return;
+	spin_lock_irqsave(&ctx->irq_lock, flags);
+	vop_mask_write(ctx, INTR_CTRL0, LINE_FLAG_INTR_MASK,
+		       LINE_FLAG_INTR_EN(0));
+	spin_unlock_irqrestore(&ctx->irq_lock, flags);
+}
+
+static void rockchip_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+	struct vop_context *ctx = to_vop_ctx(crtc);
+
+	DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode);
+
+	if (ctx->dpms == mode) {
+		DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
+		return;
+	}
+	if (mode > DRM_MODE_DPMS_ON) {
+		/* wait for the completion of page flip. */
+		if (!wait_event_timeout(ctx->wait_vsync_queue,
+					!atomic_read(&ctx->wait_vsync_event),
+					HZ/20))
+			DRM_DEBUG_KMS("vblank wait timed out.\n");
+		drm_vblank_off(crtc->dev, ctx->pipe);
+	}
+
+	switch (mode) {
+	case DRM_MODE_DPMS_ON:
+		vop_power_on(ctx);
+		break;
+	case DRM_MODE_DPMS_STANDBY:
+	case DRM_MODE_DPMS_SUSPEND:
+	case DRM_MODE_DPMS_OFF:
+		vop_power_off(ctx);
+		break;
+	default:
+		DRM_DEBUG_KMS("unspecified mode %d\n", mode);
+		break;
+	}
+
+	ctx->dpms = mode;
+}
+
+static void rockchip_drm_crtc_prepare(struct drm_crtc *crtc)
+{
+	rockchip_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
+}
+
+static bool rockchip_drm_crtc_mode_fixup(struct drm_crtc *crtc,
+					 const struct drm_display_mode *mode,
+					 struct drm_display_mode *adjusted_mode)
+{
+	/* just do dummy now */
+
+	return true;
+}
+
+static int rockchip_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+					   struct drm_framebuffer *old_fb);
+
+static int rockchip_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 *fb)
+{
+	struct vop_context *ctx = to_vop_ctx(crtc);
+	u16 hsync_len = adjusted_mode->hsync_end - adjusted_mode->hsync_start;
+	u16 left_margin = adjusted_mode->htotal - adjusted_mode->hsync_end;
+	u16 vsync_len = adjusted_mode->vsync_end - adjusted_mode->vsync_start;
+	u16 upper_margin = adjusted_mode->vtotal - adjusted_mode->vsync_end;
+	u16 hdisplay = adjusted_mode->hdisplay;
+	u16 vdisplay = adjusted_mode->vdisplay;
+	u16 htotal = adjusted_mode->htotal;
+	u16 vtotal = adjusted_mode->vtotal;
+	struct rockchip_display_mode *priv_mode =
+					(void *)adjusted_mode->private;
+	unsigned long flags;
+	int ret;
+	uint32_t val;
+
+	/* nothing to do if we haven't set the mode yet */
+	if (adjusted_mode->htotal == 0 || adjusted_mode->vtotal == 0)
+		return -EINVAL;
+
+	if (!priv_mode) {
+		DRM_ERROR("fail to found display output type[%d]\n",
+			  priv_mode->out_type);
+		return -EINVAL;
+	}
+
+	ret = rockchip_drm_crtc_mode_set_base(crtc, x, y, fb);
+	if (ret)
+		return ret;
+
+	switch (priv_mode->out_type) {
+	case ROCKCHIP_DISPLAY_TYPE_RGB:
+	case ROCKCHIP_DISPLAY_TYPE_LVDS:
+		VOP_CTRL_SET(ctx, rgb_en, 1);
+		VOP_CTRL_SET(ctx, out_mode, ROCKCHIP_OUTFACE_P888);
+		break;
+	case ROCKCHIP_DISPLAY_TYPE_EDP:
+		VOP_CTRL_SET(ctx, edp_en, 1);
+		VOP_CTRL_SET(ctx, out_mode, ROCKCHIP_OUTFACE_AAAA);
+		break;
+	case ROCKCHIP_DISPLAY_TYPE_HDMI:
+		VOP_CTRL_SET(ctx, out_mode, ROCKCHIP_OUTFACE_AAAA);
+		VOP_CTRL_SET(ctx, hdmi_en, 1);
+		break;
+	default:
+		DRM_ERROR("unsupport out type[%d]\n", priv_mode->out_type);
+		return -EINVAL;
+	};
+
+	val = 0x8;
+	val |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0;
+	val |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? (1 << 1) : 0;
+	VOP_CTRL_SET(ctx, pin_pol, val);
+
+	VOP_CTRL_SET(ctx, htotal_pw, (htotal << 16) | hsync_len);
+	val = (hsync_len + left_margin) << 16;
+	val |= hsync_len + left_margin + hdisplay;
+	VOP_CTRL_SET(ctx, hact_st_end, val);
+	VOP_CTRL_SET(ctx, hpost_st_end, val);
+
+	VOP_CTRL_SET(ctx, vtotal_pw, (vtotal << 16) | vsync_len);
+	val = (vsync_len + upper_margin) << 16;
+	val |= vsync_len + upper_margin + vdisplay;
+	VOP_CTRL_SET(ctx, vact_st_end, val);
+	VOP_CTRL_SET(ctx, vpost_st_end, val);
+
+	spin_lock_irqsave(&ctx->irq_lock, flags);
+
+	vop_mask_write(ctx, INTR_CTRL0, DSP_LINE_NUM_MASK,
+		       DSP_LINE_NUM(vsync_len + upper_margin + vdisplay));
+
+	spin_unlock_irqrestore(&ctx->irq_lock, flags);
+
+	clk_set_rate(ctx->dclk, adjusted_mode->clock * 1000);
+
+	return 0;
+}
+
+static int rockchip_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+					   struct drm_framebuffer *old_fb)
+{
+	unsigned int crtc_w;
+	unsigned int crtc_h;
+	int ret;
+
+	crtc_w = crtc->fb->width - crtc->x;
+	crtc_h = crtc->fb->height - crtc->y;
+
+	ret = rockchip_update_plane(crtc->primary, crtc, crtc->fb, 0, 0, crtc_w,
+				    crtc_h, crtc->x, crtc->y, crtc_w, crtc_h);
+	if (ret < 0) {
+		DRM_ERROR("fail to update plane\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void rockchip_drm_crtc_commit(struct drm_crtc *crtc)
+{
+	/* just do dummy now */
+}
+
+static const struct drm_crtc_helper_funcs rockchip_crtc_helper_funcs = {
+	.dpms = rockchip_drm_crtc_dpms,
+	.prepare = rockchip_drm_crtc_prepare,
+	.mode_fixup = rockchip_drm_crtc_mode_fixup,
+	.mode_set = rockchip_drm_crtc_mode_set,
+	.mode_set_base = rockchip_drm_crtc_mode_set_base,
+	.commit = rockchip_drm_crtc_commit,
+};
+
+static int rockchip_drm_crtc_page_flip(struct drm_crtc *crtc,
+				       struct drm_framebuffer *fb,
+				       struct drm_pending_vblank_event *event,
+				       uint32_t page_flip_flags)
+{
+	struct drm_device *dev = crtc->dev;
+	struct vop_context *ctx = to_vop_ctx(crtc);
+	struct drm_framebuffer *old_fb = crtc->fb;
+	unsigned int crtc_w;
+	unsigned int crtc_h;
+	int ret;
+
+	/* when the page flip is requested, crtc's dpms should be on */
+	if (ctx->dpms > DRM_MODE_DPMS_ON) {
+		DRM_DEBUG("failed page flip request at dpms[%d].\n", ctx->dpms);
+		return 0;
+	}
+
+	ret = drm_vblank_get(dev, ctx->pipe);
+	if (ret) {
+		DRM_DEBUG("failed to acquire vblank counter\n");
+		return ret;
+	}
+
+	spin_lock_irq(&dev->event_lock);
+	if (ctx->event) {
+		spin_unlock_irq(&dev->event_lock);
+		DRM_ERROR("already pending flip!\n");
+		return -EBUSY;
+	}
+	ctx->event = event;
+	atomic_set(&ctx->wait_vsync_event, 1);
+	spin_unlock_irq(&dev->event_lock);
+
+	crtc->fb = fb;
+	crtc_w = crtc->fb->width - crtc->x;
+	crtc_h = crtc->fb->height - crtc->y;
+
+	ret = rockchip_update_plane(crtc->primary, crtc, fb, 0, 0, crtc_w,
+				    crtc_h, crtc->x, crtc->y, crtc_w, crtc_h);
+	if (ret) {
+		crtc->fb = old_fb;
+
+		spin_lock_irq(&dev->event_lock);
+		drm_vblank_put(dev, ctx->pipe);
+		atomic_set(&ctx->wait_vsync_event, 0);
+		ctx->event = NULL;
+		spin_unlock_irq(&dev->event_lock);
+	}
+
+	return ret;
+}
+
+void rockchip_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe)
+{
+	struct rockchip_drm_private *dev_priv = dev->dev_private;
+	struct drm_crtc *drm_crtc = dev_priv->crtc[pipe];
+	struct vop_context *ctx;
+	unsigned long flags;
+
+	if (!drm_crtc)
+		return;
+
+	ctx = to_vop_ctx(drm_crtc);
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+
+	if (ctx->event) {
+		drm_send_vblank_event(dev, -1, ctx->event);
+		drm_vblank_put(dev, pipe);
+		atomic_set(&ctx->wait_vsync_event, 0);
+		wake_up(&ctx->wait_vsync_queue);
+		ctx->event = NULL;
+	}
+
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+void rockchip_drm_crtc_cancel_pending_flip(struct drm_device *dev)
+{
+	int i;
+
+	for (i = 0; i < dev->num_crtcs; i++)
+		rockchip_drm_crtc_finish_pageflip(dev, i);
+}
+
+static void rockchip_drm_crtc_destroy(struct drm_crtc *crtc)
+{
+	struct vop_context *ctx = to_vop_ctx(crtc);
+	struct rockchip_drm_private *private = crtc->dev->dev_private;
+
+	private->crtc[ctx->pipe] = NULL;
+	drm_crtc_cleanup(crtc);
+}
+
+static const struct drm_crtc_funcs rockchip_crtc_funcs = {
+	.set_config = drm_crtc_helper_set_config,
+	.page_flip = rockchip_drm_crtc_page_flip,
+	.destroy = rockchip_drm_crtc_destroy,
+	.cursor_set = vop_crtc_cursor_set,
+	.cursor_move = vop_crtc_cursor_move,
+};
+
+static void rockchip_vsync_worker(struct work_struct *work)
+{
+	struct vop_context *ctx = container_of(work, struct vop_context,
+					       vsync_work);
+	struct drm_device *drm = ctx->drm_dev;
+	struct rockchip_drm_private *dev_priv = drm->dev_private;
+	struct drm_crtc *crtc = dev_priv->crtc[ctx->pipe];
+	struct rockchip_plane *rockchip_plane;
+	struct drm_plane *plane;
+	uint32_t yrgb_mst;
+
+	mutex_lock(&ctx->vsync_mutex);
+
+	ctx->vsync_work_pending = false;
+
+	list_for_each_entry(plane, &drm->mode_config.plane_list, head) {
+		rockchip_plane = to_rockchip_plane(plane);
+
+		if (rockchip_plane->ctx != ctx)
+			continue;
+		if (rockchip_plane->enabled && !rockchip_plane->pending_fb)
+			continue;
+		if (!rockchip_plane->enabled && !rockchip_plane->front_fb)
+			continue;
+		/*
+		 * make sure the yrgb_mst take effect, so that
+		 * we can unreference the old framebuffer.
+		 */
+		yrgb_mst = VOP_WIN_GET_YRGBADDR(ctx, rockchip_plane->win);
+		if (rockchip_plane->pending_yrgb_mst != yrgb_mst) {
+			/*
+			 * some plane no complete, unref at next vblank
+			 */
+			ctx->vsync_work_pending = true;
+			continue;
+		}
+
+		/*
+		 * drm_framebuffer_unreference maybe call iommu unmap,
+		 * and iommu not allow unmap buffer at irq context,
+		 * so we do drm_framebuffer_unreference at queue_work.
+		 */
+		if (rockchip_plane->front_fb)
+			drm_framebuffer_unreference(rockchip_plane->front_fb);
+
+		rockchip_plane->front_fb = rockchip_plane->pending_fb;
+		rockchip_plane->pending_fb = NULL;
+
+		/*
+		 * if primary plane flip complete, sending the event to
+		 * userspace
+		 */
+		if (&rockchip_plane->base == crtc->primary)
+			rockchip_drm_crtc_finish_pageflip(ctx->drm_dev,
+							  ctx->pipe);
+	}
+
+	mutex_unlock(&ctx->vsync_mutex);
+}
+
+static irqreturn_t rockchip_vop_isr(int irq, void *data)
+{
+	struct vop_context *ctx = data;
+	uint32_t intr0_reg;
+	unsigned long flags;
+
+	intr0_reg = vop_readl(ctx, INTR_CTRL0);
+	if (intr0_reg & LINE_FLAG_INTR) {
+		spin_lock_irqsave(&ctx->irq_lock, flags);
+		vop_writel(ctx, INTR_CTRL0, intr0_reg | LINE_FLAG_INTR_CLR);
+		spin_unlock_irqrestore(&ctx->irq_lock, flags);
+	} else {
+		return IRQ_NONE;
+	}
+
+	drm_handle_vblank(ctx->drm_dev, ctx->pipe);
+	if (ctx->vsync_work_pending)
+		queue_work(ctx->vsync_wq, &ctx->vsync_work);
+
+	return IRQ_HANDLED;
+}
+
+static int vop_create_crtc(struct vop_context *ctx)
+{
+	struct device *dev = ctx->dev;
+	struct drm_device *drm_dev = ctx->drm_dev;
+	struct drm_plane *primary, *cursor;
+	unsigned long possible_crtcs;
+	struct drm_crtc *crtc;
+	int ret;
+	int nr;
+
+	ctx->win_mask = 0;
+	crtc = &ctx->crtc;
+
+	ret = rockchip_drm_add_crtc(drm_dev, crtc, dev->of_node);
+	if (ret < 0)
+		return ret;
+	ctx->pipe = ret;
+
+	possible_crtcs = (1 << ctx->pipe);
+
+	primary = rockchip_plane_init(ctx, possible_crtcs,
+				      DRM_PLANE_TYPE_PRIMARY);
+	if (!primary) {
+		DRM_ERROR("fail to init primary plane\n");
+		return -EINVAL;
+	}
+
+	for (nr = 1; nr < ROCKCHIP_MAX_PLANE; nr++) {
+		if (nr == VOP_DEFAULT_CURSOR) {
+			cursor = rockchip_plane_init(ctx, possible_crtcs,
+						     DRM_PLANE_TYPE_CURSOR);
+			if (!cursor) {
+				DRM_ERROR("fail to init cursor plane\n");
+				return -EINVAL;
+			}
+		} else {
+			struct drm_plane *plane;
+
+			plane = rockchip_plane_init(ctx, possible_crtcs,
+						    DRM_PLANE_TYPE_OVERLAY);
+			if (!plane) {
+				DRM_ERROR("fail to init overlay plane\n");
+				return -EINVAL;
+			}
+		}
+	}
+
+	drm_crtc_init_with_planes(drm_dev, crtc, primary, cursor,
+				  &rockchip_crtc_funcs);
+	drm_crtc_helper_add(crtc, &rockchip_crtc_helper_funcs);
+
+	return 0;
+}
+
+static int rockchip_vop_initial(struct vop_context *ctx)
+{
+	struct vop_driver_data *vop_data = ctx->data;
+	const struct vop_reg_data *init_table = vop_data->init_table;
+	struct reset_control *rst;
+	int i, ret;
+
+	ctx->hclk = devm_clk_get(ctx->dev, "hclk_vop");
+	if (IS_ERR(ctx->hclk)) {
+		dev_err(ctx->dev, "failed to get hclk source\n");
+		return PTR_ERR(ctx->hclk);
+	}
+	ctx->aclk = devm_clk_get(ctx->dev, "aclk_vop");
+	if (IS_ERR(ctx->aclk)) {
+		dev_err(ctx->dev, "failed to get aclk source\n");
+		return PTR_ERR(ctx->aclk);
+	}
+	ctx->dclk = devm_clk_get(ctx->dev, "dclk_vop");
+	if (IS_ERR(ctx->dclk)) {
+		dev_err(ctx->dev, "failed to get dclk source\n");
+		return PTR_ERR(ctx->dclk);
+	}
+
+	ret = vop_clk_enable(ctx);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * do hclk_reset, reset all vop registers.
+	 */
+	rst = devm_reset_control_get(ctx->dev, "ahb");
+	if (IS_ERR(rst)) {
+		dev_err(ctx->dev, "failed to get ahb reset\n");
+		return PTR_ERR(rst);
+	}
+	reset_control_assert(rst);
+	usleep_range(10, 20);
+	reset_control_deassert(rst);
+
+	memcpy(ctx->regsbak, ctx->regs, ctx->len);
+
+	for (i = 0; i < vop_data->table_size; i++)
+		vop_writel(ctx, init_table[i].offset, init_table[i].value);
+
+	for (i = 0; i < VOP_MAX_WIN_SUPPORT; i++)
+		VOP_WIN_SET(ctx, vop_data->win[i], enable, 0);
+
+	vop_cfg_done(ctx);
+
+	/*
+	 * do dclk_reset, let all win config take affect, and then we can enable
+	 * iommu safe.
+	 */
+	rst = devm_reset_control_get(ctx->dev, "dclk");
+	if (IS_ERR(rst)) {
+		dev_err(ctx->dev, "failed to get dclk reset\n");
+		return PTR_ERR(rst);
+	}
+	reset_control_assert(rst);
+	usleep_range(10, 20);
+	reset_control_deassert(rst);
+
+	ctx->dpms = DRM_MODE_DPMS_ON;
+
+	return 0;
+}
+
+static int vop_bind(struct device *dev, struct device *master, void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct vop_driver_data *vop_data = vop_get_driver_data(dev);
+	struct drm_device *drm_dev = data;
+	struct vop_context *ctx;
+	struct resource *res;
+	int ret;
+
+	if (!vop_data)
+		return -ENODEV;
+
+	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	ctx->dev = dev;
+	ctx->data = vop_data;
+	ctx->drm_dev = drm_dev;
+	dev_set_drvdata(dev, ctx);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	ctx->len = resource_size(res);
+	ctx->regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(ctx->regs))
+		return PTR_ERR(ctx->regs);
+
+	ctx->regsbak = devm_kzalloc(dev, ctx->len, GFP_KERNEL);
+	if (!ctx->regsbak)
+		return -ENOMEM;
+
+	ret = rockchip_vop_initial(ctx);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "cannot initial vop dev - err %d\n", ret);
+		return ret;
+	}
+
+	ctx->irq = platform_get_irq(pdev, 0);
+	if (ctx->irq < 0) {
+		dev_err(dev, "cannot find irq for vop\n");
+		return ctx->irq;
+	}
+
+	spin_lock_init(&ctx->reg_lock);
+	spin_lock_init(&ctx->irq_lock);
+
+	init_waitqueue_head(&ctx->wait_vsync_queue);
+	atomic_set(&ctx->wait_vsync_event, 0);
+
+	ret = vop_iommu_init(ctx);
+	if (ret) {
+		DRM_ERROR("Failed to setup iommu, %d\n", ret);
+		return ret;
+	}
+
+	ctx->vsync_wq = create_singlethread_workqueue("vsync");
+	if (!ctx->vsync_wq) {
+		dev_err(dev, "failed to create workqueue\n");
+		return -EINVAL;
+	}
+	INIT_WORK(&ctx->vsync_work, rockchip_vsync_worker);
+
+	mutex_init(&ctx->vsync_mutex);
+	pm_runtime_enable(&pdev->dev);
+
+	ret = devm_request_irq(dev, ctx->irq, rockchip_vop_isr,
+			       IRQF_SHARED, dev_name(dev), ctx);
+	if (ret) {
+		dev_err(dev, "cannot requeset irq%d - err %d\n", ctx->irq, ret);
+		return ret;
+	}
+
+	return vop_create_crtc(ctx);
+}
+
+static void vop_unbind(struct device *dev, struct device *master,
+		       void *data)
+{
+	struct drm_device *drm_dev = data;
+	struct vop_context *ctx = dev_get_drvdata(dev);
+	struct drm_crtc *crtc = &ctx->crtc;
+
+	drm_crtc_cleanup(crtc);
+	pm_runtime_disable(dev);
+	rockchip_drm_remove_crtc(drm_dev, ctx->pipe);
+
+	vop_iommu_fini(ctx);
+}
+
+static const struct component_ops vop_component_ops = {
+	.bind = vop_bind,
+	.unbind = vop_unbind,
+};
+
+static int vop_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct vop_context *ctx;
+
+	if (!dev->of_node) {
+		dev_err(dev, "can't find vop devices\n");
+		return -ENODEV;
+	}
+
+	platform_set_drvdata(pdev, ctx);
+
+	return component_add(dev, &vop_component_ops);
+}
+
+static int vop_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &vop_component_ops);
+
+	return 0;
+}
+
+struct platform_driver rockchip_vop_platform_driver = {
+	.probe = vop_probe,
+	.remove = vop_remove,
+	.driver = {
+		.name = "rockchip-vop",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(vop_driver_dt_match),
+	},
+};
+
+module_platform_driver(rockchip_vop_platform_driver);
+
+MODULE_AUTHOR("Mark Yao <mark.yao@rock-chips.com>");
+MODULE_DESCRIPTION("ROCKCHIP VOP Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
new file mode 100644
index 0000000..2343760
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _ROCKCHIP_DRM_VOP_H
+#define _ROCKCHIP_DRM_VOP_H
+
+/* register definition */
+#define REG_CFG_DONE			0x0000
+#define VERSION_INFO			0x0004
+#define SYS_CTRL			0x0008
+#define SYS_CTRL1			0x000c
+#define DSP_CTRL0			0x0010
+#define DSP_CTRL1			0x0014
+#define DSP_BG				0x0018
+#define MCU_CTRL			0x001c
+#define INTR_CTRL0			0x0020
+#define INTR_CTRL1			0x0024
+#define WIN0_CTRL0			0x0030
+#define WIN0_CTRL1			0x0034
+#define WIN0_COLOR_KEY			0x0038
+#define WIN0_VIR			0x003c
+#define WIN0_YRGB_MST			0x0040
+#define WIN0_CBR_MST			0x0044
+#define WIN0_ACT_INFO			0x0048
+#define WIN0_DSP_INFO			0x004c
+#define WIN0_DSP_ST			0x0050
+#define WIN0_SCL_FACTOR_YRGB		0x0054
+#define WIN0_SCL_FACTOR_CBR		0x0058
+#define WIN0_SCL_OFFSET			0x005c
+#define WIN0_SRC_ALPHA_CTRL		0x0060
+#define WIN0_DST_ALPHA_CTRL		0x0064
+#define WIN0_FADING_CTRL		0x0068
+/* win1 register */
+#define WIN1_CTRL0			0x0070
+#define WIN1_CTRL1			0x0074
+#define WIN1_COLOR_KEY			0x0078
+#define WIN1_VIR			0x007c
+#define WIN1_YRGB_MST			0x0080
+#define WIN1_CBR_MST			0x0084
+#define WIN1_ACT_INFO			0x0088
+#define WIN1_DSP_INFO			0x008c
+#define WIN1_DSP_ST			0x0090
+#define WIN1_SCL_FACTOR_YRGB		0x0094
+#define WIN1_SCL_FACTOR_CBR		0x0098
+#define WIN1_SCL_OFFSET			0x009c
+#define WIN1_SRC_ALPHA_CTRL		0x00a0
+#define WIN1_DST_ALPHA_CTRL		0x00a4
+#define WIN1_FADING_CTRL		0x00a8
+/* win2 register */
+#define WIN2_CTRL0			0x00b0
+#define WIN2_CTRL1			0x00b4
+#define WIN2_VIR0_1			0x00b8
+#define WIN2_VIR2_3			0x00bc
+#define WIN2_MST0			0x00c0
+#define WIN2_DSP_INFO0			0x00c4
+#define WIN2_DSP_ST0			0x00c8
+#define WIN2_COLOR_KEY			0x00cc
+#define WIN2_MST1			0x00d0
+#define WIN2_DSP_INFO1			0x00d4
+#define WIN2_DSP_ST1			0x00d8
+#define WIN2_SRC_ALPHA_CTRL		0x00dc
+#define WIN2_MST2			0x00e0
+#define WIN2_DSP_INFO2			0x00e4
+#define WIN2_DSP_ST2			0x00e8
+#define WIN2_DST_ALPHA_CTRL		0x00ec
+#define WIN2_MST3			0x00f0
+#define WIN2_DSP_INFO3			0x00f4
+#define WIN2_DSP_ST3			0x00f8
+#define WIN2_FADING_CTRL		0x00fc
+/* win3 register */
+#define WIN3_CTRL0			0x0100
+#define WIN3_CTRL1			0x0104
+#define WIN3_VIR0_1			0x0108
+#define WIN3_VIR2_3			0x010c
+#define WIN3_MST0			0x0110
+#define WIN3_DSP_INFO0			0x0114
+#define WIN3_DSP_ST0			0x0118
+#define WIN3_COLOR_KEY			0x011c
+#define WIN3_MST1			0x0120
+#define WIN3_DSP_INFO1			0x0124
+#define WIN3_DSP_ST1			0x0128
+#define WIN3_SRC_ALPHA_CTRL		0x012c
+#define WIN3_MST2			0x0130
+#define WIN3_DSP_INFO2			0x0134
+#define WIN3_DSP_ST2			0x0138
+#define WIN3_DST_ALPHA_CTRL		0x013c
+#define WIN3_MST3			0x0140
+#define WIN3_DSP_INFO3			0x0144
+#define WIN3_DSP_ST3			0x0148
+#define WIN3_FADING_CTRL		0x014c
+/* hwc register */
+#define HWC_CTRL0			0x0150
+#define HWC_CTRL1			0x0154
+#define HWC_MST				0x0158
+#define HWC_DSP_ST			0x015c
+#define HWC_SRC_ALPHA_CTRL		0x0160
+#define HWC_DST_ALPHA_CTRL		0x0164
+#define HWC_FADING_CTRL			0x0168
+/* post process register */
+#define POST_DSP_HACT_INFO		0x0170
+#define POST_DSP_VACT_INFO		0x0174
+#define POST_SCL_FACTOR_YRGB		0x0178
+#define POST_SCL_CTRL			0x0180
+#define POST_DSP_VACT_INFO_F1		0x0184
+#define DSP_HTOTAL_HS_END		0x0188
+#define DSP_HACT_ST_END			0x018c
+#define DSP_VTOTAL_VS_END		0x0190
+#define DSP_VACT_ST_END			0x0194
+#define DSP_VS_ST_END_F1		0x0198
+#define DSP_VACT_ST_END_F1		0x019c
+/* register definition end */
+
+/* interrupt define */
+#define DSP_HOLD_VALID_INTR		(1 << 0)
+#define FS_INTR				(1 << 1)
+#define LINE_FLAG_INTR			(1 << 2)
+#define BUS_ERROR_INTR			(1 << 3)
+
+#define DSP_HOLD_VALID_INTR_EN(x)	((x) << 4)
+#define FS_INTR_EN(x)			((x) << 5)
+#define LINE_FLAG_INTR_EN(x)		((x) << 6)
+#define BUS_ERROR_INTR_EN(x)		((x) << 7)
+#define DSP_HOLD_VALID_INTR_MASK	(1 << 4)
+#define FS_INTR_EN_MASK			(1 << 5)
+#define LINE_FLAG_INTR_MASK		(1 << 6)
+#define BUS_ERROR_INTR_MASK		(1 << 7)
+
+#define DSP_HOLD_VALID_INTR_CLR		(1 << 8)
+#define FS_INTR_EN_CLR			(1 << 9)
+#define LINE_FLAG_INTR_CLR		(1 << 10)
+#define BUS_ERROR_INTR_CLR		(1 << 11)
+#define DSP_LINE_NUM(x)			(((x) & 0x1fff) << 12)
+#define DSP_LINE_NUM_MASK		(0x1fff << 12)
+
+/* src alpha ctrl define */
+#define SRC_FADING_VALUE(x)		(((x) & 0xff) << 24)
+#define SRC_GLOBAL_ALPHA(x)		(((x) & 0xff) << 16)
+#define SRC_FACTOR_M0(x)		(((x) & 0x7) << 6)
+#define SRC_ALPHA_CAL_M0(x)		(((x) & 0x1) << 5)
+#define SRC_BLEND_M0(x)			(((x) & 0x3) << 3)
+#define SRC_ALPHA_M0(x)			(((x) & 0x1) << 2)
+#define SRC_COLOR_M0(x)			(((x) & 0x1) << 1)
+#define SRC_ALPHA_EN(x)			(((x) & 0x1) << 0)
+/* dst alpha ctrl define */
+#define DST_FACTOR_M0(x)		(((x) & 0x7) << 6)
+
+enum alpha_mode {
+	ALPHA_STRAIGHT,
+	ALPHA_INVERSE,
+};
+
+enum global_blend_mode {
+	ALPHA_GLOBAL,
+	ALPHA_PER_PIX,
+	ALPHA_PER_PIX_GLOBAL,
+};
+
+enum alpha_cal_mode {
+	ALPHA_SATURATION,
+	ALPHA_NO_SATURATION,
+};
+
+enum color_mode {
+	ALPHA_SRC_PRE_MUL,
+	ALPHA_SRC_NO_PRE_MUL,
+};
+
+enum factor_mode {
+	ALPHA_ZERO,
+	ALPHA_ONE,
+	ALPHA_SRC,
+	ALPHA_SRC_INVERSE,
+	ALPHA_SRC_GLOBAL,
+};
+
+#endif /* _ROCKCHIP_DRM_VOP_H */
diff --git a/include/uapi/drm/rockchip_drm.h b/include/uapi/drm/rockchip_drm.h
new file mode 100644
index 0000000..8f8e60e
--- /dev/null
+++ b/include/uapi/drm/rockchip_drm.h
@@ -0,0 +1,97 @@
+/*
+ *
+ * Copyright (c) Fuzhou Rockchip Electronics Co.Ltd
+ * Authors:
+ *       Mark Yao <yzq@rock-chips.com>
+ *
+ * base on exynos_drm.h
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#ifndef _UAPI_ROCKCHIP_DRM_H
+#define _UAPI_ROCKCHIP_DRM_H
+
+#include <drm/drm.h>
+
+/**
+ * User-desired buffer creation information structure.
+ *
+ * @size: user-desired memory allocation size.
+ * @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_rockchip_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_rockchip_gem_map_off {
+	uint32_t handle;
+	uint32_t pad;
+	uint64_t offset;
+};
+
+/**
+ * A structure for mapping buffer.
+ *
+ * @handle: a handle to gem object created.
+ * @pad: just padding to be 64-bit aligned.
+ * @size: memory size to be mapped.
+ * @mapped: having user virtual address mmaped.
+ *      - this variable would be filled by rockchip gem module
+ *      of kernel side with user virtual address which is allocated
+ *      by do_mmap().
+ */
+struct drm_rockchip_gem_mmap {
+	uint32_t handle;
+	uint32_t pad;
+	uint64_t size;
+	uint64_t mapped;
+};
+
+/**
+ * A structure to gem information.
+ *
+ * @handle: a handle to gem object created.
+ * @flags: flag value including memory type and cache attribute and
+ *      this value would be set by driver.
+ * @size: size to memory region allocated by gem and this size would
+ *      be set by driver.
+ */
+struct drm_rockchip_gem_info {
+	uint32_t handle;
+	uint32_t flags;
+	uint64_t size;
+};
+
+#define DRM_ROCKCHIP_GEM_CREATE		0x00
+#define DRM_ROCKCHIP_GEM_MAP_OFFSET	0x01
+#define DRM_ROCKCHIP_GEM_MMAP		0x02
+#define DRM_ROCKCHIP_GEM_GET		0x04
+
+#define DRM_IOCTL_ROCKCHIP_GEM_CREATE	DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_ROCKCHIP_GEM_CREATE, struct drm_rockchip_gem_create)
+
+#define DRM_IOCTL_ROCKCHIP_GEM_MAP_OFFSET	DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_ROCKCHIP_GEM_MAP_OFFSET, struct drm_rockchip_gem_map_off)
+
+#define DRM_IOCTL_ROCKCHIP_GEM_MMAP	DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_ROCKCHIP_GEM_MMAP, struct drm_rockchip_gem_mmap)
+
+#define DRM_IOCTL_ROCKCHIP_GEM_GET	DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_ROCKCHIP_GEM_GET, struct drm_rockchip_gem_info)
+#endif /* _UAPI_ROCKCHIP_DRM_H */
-- 
1.7.9.5



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

* [PATCH v2 1/5] drm/rockchip: Add basic drm driver
@ 2014-09-18  9:36     ` Mark yao
  0 siblings, 0 replies; 38+ messages in thread
From: Mark yao @ 2014-09-18  9:36 UTC (permalink / raw)
  To: heiko-4mtYJXux2i+zQB+pC5nmwQ, Boris BREZILLON, David Airlie,
	Rob Clark, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, Randy Dunlap, Grant Likely, Greg Kroah-Hartman,
	John Stultz, Rom Lemarchand
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-api-u79uwXL29TY76Z2rM5mHXA,
	linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	dianders-F7+t8E8rja9g9hUCZPvPmw, marcheu-F7+t8E8rja9g9hUCZPvPmw,
	dbehr-F7+t8E8rja9g9hUCZPvPmw, olof-nZhT3qVonbNeoWH0uzbU5w,
	djkurtz-F7+t8E8rja9g9hUCZPvPmw, xjq-TNX95d0MmH7DzftRWevZcw,
	kfx-TNX95d0MmH7DzftRWevZcw, cym-TNX95d0MmH7DzftRWevZcw,
	cf-TNX95d0MmH7DzftRWevZcw, zyw-TNX95d0MmH7DzftRWevZcw,
	xxm-TNX95d0MmH7DzftRWevZcw, huangtao-TNX95d0MmH7DzftRWevZcw,
	kever.yang-TNX95d0MmH7DzftRWevZcw, yxj-TNX95d0MmH7DzftRWevZcw,
	wxt-TNX95d0MmH7DzftRWevZcw, xw-TNX95d0MmH7DzftRWevZcw, Mark yao

This patch adds the basic structure of a DRM Driver for Rockchip Socs.

Signed-off-by: Mark yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
Changes in v2:
- use the component framework to defer main drm driver probe
  until all VOP devices have been probed.
- use dma-mapping API with ARM_DMA_USE_IOMMU, create dma mapping by
  master device and each vop device can shared the drm dma mapping.
- use drm_crtc_init_with_planes and drm_universal_plane_init.
- remove unnecessary middle layers.
- add cursor set, move funcs to rockchip drm crtc.
- use vop reset at first init
- reference framebuffer when used and unreference when swap out vop

 drivers/gpu/drm/Kconfig                       |    2 +
 drivers/gpu/drm/Makefile                      |    1 +
 drivers/gpu/drm/rockchip/Kconfig              |   19 +
 drivers/gpu/drm/rockchip/Makefile             |   10 +
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c   |  502 +++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h   |  120 ++
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c    |  201 ++++
 drivers/gpu/drm/rockchip/rockchip_drm_fb.h    |   28 +
 drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c |  231 ++++
 drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h |   20 +
 drivers/gpu/drm/rockchip/rockchip_drm_gem.c   |  405 +++++++
 drivers/gpu/drm/rockchip/rockchip_drm_gem.h   |   76 ++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c   | 1442 +++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h   |  187 ++++
 include/uapi/drm/rockchip_drm.h               |   97 ++
 15 files changed, 3341 insertions(+)
 create mode 100644 drivers/gpu/drm/rockchip/Kconfig
 create mode 100644 drivers/gpu/drm/rockchip/Makefile
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_drv.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_drv.h
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fb.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fb.h
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_gem.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_gem.h
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop.h
 create mode 100644 include/uapi/drm/rockchip_drm.h

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index b066bb3..7c4c3c6 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -171,6 +171,8 @@ config DRM_SAVAGE
 
 source "drivers/gpu/drm/exynos/Kconfig"
 
+source "drivers/gpu/drm/rockchip/Kconfig"
+
 source "drivers/gpu/drm/vmwgfx/Kconfig"
 
 source "drivers/gpu/drm/gma500/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 4a55d59..d03387a 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/
 obj-$(CONFIG_DRM_VIA)	+=via/
 obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
 obj-$(CONFIG_DRM_EXYNOS) +=exynos/
+obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/
 obj-$(CONFIG_DRM_GMA500) += gma500/
 obj-$(CONFIG_DRM_UDL) += udl/
 obj-$(CONFIG_DRM_AST) += ast/
diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
new file mode 100644
index 0000000..7146c80
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -0,0 +1,19 @@
+config DRM_ROCKCHIP
+	tristate "DRM Support for Rockchip"
+	depends on DRM && ROCKCHIP_IOMMU
+	select ARM_DMA_USE_IOMMU
+	select IOMMU_API
+	select DRM_KMS_HELPER
+	select DRM_KMS_FB_HELPER
+	select DRM_PANEL
+	select FB_CFB_FILLRECT
+	select FB_CFB_COPYAREA
+	select FB_CFB_IMAGEBLIT
+	select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE
+	select VIDEOMODE_HELPERS
+	help
+	  Choose this option if you have a Rockchip soc chipset.
+	  This driver provides kernel mode setting and buffer
+	  management to userspace. This driver does not provides
+	  2D or 3D acceleration; acceleration is performed by other
+	  IP found on the SoC.
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
new file mode 100644
index 0000000..6e6d468
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the drm device driver.  This driver provides support for the
+# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
+
+ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/rockchip
+
+rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \
+		rockchip_drm_gem.o rockchip_drm_vop.o
+
+obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
new file mode 100644
index 0000000..f84dcd8
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -0,0 +1,502 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *
+ * based on exynos_drm_drv.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/dma-iommu.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <linux/dma-mapping.h>
+#include <linux/pm_runtime.h>
+#include <linux/of_graph.h>
+#include <linux/component.h>
+
+#include <drm/rockchip_drm.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_fb.h"
+#include "rockchip_drm_fbdev.h"
+#include "rockchip_drm_gem.h"
+
+#define DRIVER_NAME	"rockchip"
+#define DRIVER_DESC	"RockChip Soc DRM"
+#define DRIVER_DATE	"20140818"
+#define DRIVER_MAJOR	1
+#define DRIVER_MINOR	0
+
+/*
+ * Attach a (component) device to the shared drm dma mapping from master drm
+ * device.  This is used by the VOPs to map GEM buffers to a common DMA
+ * mapping.
+ */
+int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
+				   struct device *dev)
+{
+	struct dma_iommu_mapping *mapping = drm_dev->dev->archdata.mapping;
+	int ret;
+
+	ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
+	if (ret)
+		return ret;
+
+	dma_set_max_seg_size(dev, 0xffffffffu);
+
+	return arm_iommu_attach_device(dev, mapping);
+}
+
+void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
+				    struct device *dev)
+{
+	arm_iommu_detach_device(drm_dev->dev);
+}
+
+static int rockchip_drm_load(struct drm_device *drm_dev, unsigned long flags)
+{
+	struct rockchip_drm_private *private;
+	struct dma_iommu_mapping *mapping;
+	struct device *dev = drm_dev->dev;
+	int ret;
+
+	private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL);
+	if (!private)
+		return -ENOMEM;
+
+	dev_set_drvdata(drm_dev->dev, dev);
+	drm_dev->dev_private = private;
+
+	drm_mode_config_init(drm_dev);
+
+	rockchip_drm_mode_config_init(drm_dev);
+
+	dev->dma_parms = devm_kzalloc(dev, sizeof(*dev->dma_parms),
+				      GFP_KERNEL);
+	if (!dev->dma_parms) {
+		ret = -ENOMEM;
+		goto err_config_cleanup;
+	}
+
+	/* TODO(djkurtz): fetch the mapping start/size from somewhere */
+	mapping = arm_iommu_create_mapping(&platform_bus_type, 0x10000000,
+					   SZ_1G);
+	if (IS_ERR(mapping)) {
+		ret = PTR_ERR(mapping);
+		goto err_config_cleanup;
+	}
+
+	dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
+	dma_set_max_seg_size(dev, 0xffffffffu);
+
+	ret = arm_iommu_attach_device(dev, mapping);
+	if (ret)
+		goto err_release_mapping;
+
+	/* Try to bind all sub drivers. */
+	ret = component_bind_all(dev, drm_dev);
+	if (ret)
+		goto err_detach_device;
+
+	/* init kms poll for handling hpd */
+	drm_kms_helper_poll_init(drm_dev);
+
+	/*
+	 * enable drm irq mode.
+	 * - with irq_enabled = true, we can use the vblank feature.
+	 */
+	drm_dev->irq_enabled = true;
+
+	/*
+	 * with vblank_disable_allowed = true, vblank interrupt will be disabled
+	 * by drm timer once a current process gives up ownership of
+	 * vblank event.(after drm_vblank_put function is called)
+	 */
+	drm_dev->vblank_disable_allowed = true;
+
+	ret = drm_vblank_init(drm_dev, ROCKCHIP_MAX_CRTC);
+	if (ret)
+		goto err_kms_helper_poll_fini;
+
+	rockchip_drm_fbdev_init(drm_dev);
+
+	/* force connectors detection */
+	drm_helper_hpd_irq_event(drm_dev);
+
+	return 0;
+
+err_kms_helper_poll_fini:
+	drm_kms_helper_poll_fini(drm_dev);
+	component_unbind_all(dev, drm_dev);
+err_detach_device:
+	arm_iommu_detach_device(dev);
+err_release_mapping:
+	arm_iommu_release_mapping(dev->archdata.mapping);
+err_config_cleanup:
+	drm_mode_config_cleanup(drm_dev);
+	drm_dev->dev_private = NULL;
+	dev_set_drvdata(dev, NULL);
+	return ret;
+}
+
+static int rockchip_drm_unload(struct drm_device *drm_dev)
+{
+	struct device *dev = drm_dev->dev;
+
+	drm_kms_helper_poll_fini(drm_dev);
+	component_unbind_all(dev, drm_dev);
+	arm_iommu_detach_device(dev);
+	arm_iommu_release_mapping(dev->archdata.mapping);
+	drm_mode_config_cleanup(drm_dev);
+	drm_dev->dev_private = NULL;
+	dev_set_drvdata(dev, NULL);
+
+	return 0;
+}
+
+static int rockchip_drm_suspend(struct drm_device *dev, pm_message_t state)
+{
+	struct drm_connector *connector;
+
+	drm_modeset_lock_all(dev);
+	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+		int old_dpms = connector->dpms;
+
+		if (connector->funcs->dpms)
+			connector->funcs->dpms(connector, DRM_MODE_DPMS_OFF);
+
+		/* Set the old mode back to the connector for resume */
+		connector->dpms = old_dpms;
+	}
+	drm_modeset_unlock_all(dev);
+
+	return 0;
+}
+
+static int rockchip_drm_resume(struct drm_device *dev)
+{
+	struct drm_connector *connector;
+
+	drm_modeset_lock_all(dev);
+	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+		if (connector->funcs->dpms)
+			connector->funcs->dpms(connector, connector->dpms);
+	}
+	drm_modeset_unlock_all(dev);
+
+	drm_helper_resume_force_mode(dev);
+
+	return 0;
+}
+
+void rockchip_drm_lastclose(struct drm_device *dev)
+{
+	struct rockchip_drm_private *priv = dev->dev_private;
+
+	drm_modeset_lock_all(dev);
+	if (priv->fb_helper)
+		drm_fb_helper_restore_fbdev_mode(priv->fb_helper);
+	drm_modeset_unlock_all(dev);
+}
+
+static const struct drm_ioctl_desc rockchip_ioctls[] = {
+	DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_CREATE, rockchip_gem_create_ioctl,
+			  DRM_UNLOCKED | DRM_AUTH),
+	DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_GET, rockchip_gem_get_ioctl,
+			  DRM_UNLOCKED),
+	DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_MAP_OFFSET,
+			  rockchip_gem_map_offset_ioctl, DRM_UNLOCKED |
+			  DRM_AUTH),
+	DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_MMAP, rockchip_gem_mmap_ioctl,
+			  DRM_UNLOCKED | DRM_AUTH),
+};
+
+static const struct file_operations rockchip_drm_driver_fops = {
+	.owner = THIS_MODULE,
+	.open = drm_open,
+	.mmap = drm_gem_mmap,
+	.poll = drm_poll,
+	.read = drm_read,
+	.unlocked_ioctl = drm_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = drm_compat_ioctl,
+#endif
+	.release = drm_release,
+};
+
+const struct vm_operations_struct rockchip_drm_vm_ops = {
+	.open = drm_gem_vm_open,
+	.close = drm_gem_vm_close,
+};
+
+static struct drm_driver rockchip_drm_driver = {
+	.driver_features	= DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
+	.load			= rockchip_drm_load,
+	.unload			= rockchip_drm_unload,
+	.lastclose		= rockchip_drm_lastclose,
+	.suspend		= rockchip_drm_suspend,
+	.resume			= rockchip_drm_resume,
+	.get_vblank_counter	= drm_vblank_count,
+	.enable_vblank		= rockchip_drm_crtc_enable_vblank,
+	.disable_vblank		= rockchip_drm_crtc_disable_vblank,
+	.gem_vm_ops		= &rockchip_drm_vm_ops,
+	.gem_free_object	= rockchip_gem_free_object,
+	.dumb_create		= rockchip_gem_dumb_create,
+	.dumb_map_offset	= rockchip_gem_dumb_map_offset,
+	.dumb_destroy		= drm_gem_dumb_destroy,
+	.prime_handle_to_fd	= drm_gem_prime_handle_to_fd,
+	.prime_fd_to_handle	= drm_gem_prime_fd_to_handle,
+	.gem_prime_import	= drm_gem_prime_import,
+	.gem_prime_export	= drm_gem_prime_export,
+	.gem_prime_get_sg_table	= rockchip_gem_prime_get_sg_table,
+	.gem_prime_import_sg_table	= rockchip_gem_prime_import_sg_table,
+	.gem_prime_vmap		= rockchip_gem_prime_vmap,
+	.gem_prime_vunmap	= rockchip_gem_prime_vunmap,
+	.gem_prime_mmap		= rockchip_gem_prime_mmap,
+	.ioctls			= rockchip_ioctls,
+	.num_ioctls		= ARRAY_SIZE(rockchip_ioctls),
+	.fops			= &rockchip_drm_driver_fops,
+	.name	= DRIVER_NAME,
+	.desc	= DRIVER_DESC,
+	.date	= DRIVER_DATE,
+	.major	= DRIVER_MAJOR,
+	.minor	= DRIVER_MINOR,
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int rockchip_drm_sys_suspend(struct device *dev)
+{
+	struct drm_device *drm_dev = dev_get_drvdata(dev);
+	pm_message_t message;
+
+	if (pm_runtime_suspended(dev))
+		return 0;
+
+	message.event = PM_EVENT_SUSPEND;
+
+	return rockchip_drm_suspend(drm_dev, message);
+}
+
+static int rockchip_drm_sys_resume(struct device *dev)
+{
+	struct drm_device *drm_dev = dev_get_drvdata(dev);
+
+	if (pm_runtime_suspended(dev))
+		return 0;
+
+	return rockchip_drm_resume(drm_dev);
+}
+#endif
+
+static const struct dev_pm_ops rockchip_drm_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(rockchip_drm_sys_suspend,
+				rockchip_drm_sys_resume)
+};
+
+int rockchip_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
+			  struct device_node *np)
+{
+	struct rockchip_drm_private *priv = drm->dev_private;
+	struct device_node *port;
+	int pipe;
+
+	if (priv->num_pipe >= ROCKCHIP_MAX_CRTC)
+		return -EINVAL;
+
+	port = of_get_child_by_name(np, "port");
+	of_node_put(np);
+	if (!port) {
+		dev_err(drm->dev, "no port node found in %s\n",
+			np->full_name);
+		return -ENXIO;
+	}
+	pipe = priv->num_pipe++;
+	crtc->port = port;
+
+	priv->crtc[pipe] = crtc;
+
+	return pipe;
+}
+
+void rockchip_drm_remove_crtc(struct drm_device *drm, int pipe)
+{
+	struct rockchip_drm_private *priv = drm->dev_private;
+
+	priv->num_pipe--;
+	of_node_put(priv->crtc[pipe]->port);
+	priv->crtc[pipe] = NULL;
+}
+
+struct drm_crtc *rockchip_find_crtc(struct drm_device *drm, int pipe)
+{
+	struct rockchip_drm_private *priv = drm->dev_private;
+
+	if (pipe < ROCKCHIP_MAX_CRTC && priv->crtc[pipe])
+		return priv->crtc[pipe];
+
+	return NULL;
+}
+
+/*
+ * @node: device tree node containing encoder input ports
+ * @encoder: drm_encoder
+ */
+int rockchip_drm_encoder_get_mux_id(struct device_node *node,
+				    struct drm_encoder *encoder)
+{
+	struct device_node *ep = NULL;
+	struct drm_crtc *crtc = encoder->crtc;
+	struct of_endpoint endpoint;
+	struct device_node *port;
+	int ret;
+
+	if (!node || !crtc)
+		return -EINVAL;
+
+	do {
+		ep = of_graph_get_next_endpoint(node, ep);
+		if (!ep)
+			break;
+
+		port = of_graph_get_remote_port(ep);
+		of_node_put(port);
+		if (port == crtc->port) {
+			ret = of_graph_parse_endpoint(ep, &endpoint);
+			return ret ? ret : endpoint.id;
+		}
+	} while (ep);
+
+	return -EINVAL;
+}
+
+static int compare_of(struct device *dev, void *data)
+{
+	struct device_node *np = data;
+
+	return dev->of_node == np;
+}
+
+static void rockchip_add_endpoints(struct device *dev,
+				   struct component_match **match,
+				   struct device_node *port)
+{
+	struct device_node *ep, *remote;
+
+	for_each_child_of_node(port, ep) {
+		remote = of_graph_get_remote_port_parent(ep);
+		if (!remote || !of_device_is_available(remote)) {
+			of_node_put(remote);
+			continue;
+		} else if (!of_device_is_available(remote->parent)) {
+			dev_warn(dev, "parent device of %s is not available\n",
+				 remote->full_name);
+			of_node_put(remote);
+			continue;
+		}
+
+		component_match_add(dev, match, compare_of, remote);
+		of_node_put(remote);
+	}
+}
+
+static int rockchip_drm_bind(struct device *dev)
+{
+	return drm_platform_init(&rockchip_drm_driver, to_platform_device(dev));
+}
+
+static void rockchip_drm_unbind(struct device *dev)
+{
+	drm_put_dev(dev_get_drvdata(dev));
+}
+
+static const struct component_master_ops rockchip_drm_ops = {
+	.bind = rockchip_drm_bind,
+	.unbind = rockchip_drm_unbind,
+};
+
+static int rockchip_drm_platform_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct component_match *match = NULL;
+	struct device_node *np = dev->of_node;
+	struct device_node *port;
+	int i;
+	int ret;
+
+	if (!np)
+		return -ENODEV;
+	/*
+	 * Bind the crtc ports first, so that
+	 * drm_of_find_possible_crtcs called from encoder .bind callbacks
+	 * works as expected.
+	 */
+	for (i = 0;; i++) {
+		port = of_parse_phandle(np, "ports", i);
+		if (!port)
+			break;
+
+		component_match_add(dev, &match, compare_of, port->parent);
+		of_node_put(port);
+	}
+
+	if (i == 0) {
+		dev_err(dev, "missing 'ports' property\n");
+		return -ENODEV;
+	}
+	/*
+	 * For each bound crtc, bind the encoders attached to its
+	 * remote endpoint.
+	 */
+	for (i = 0;; i++) {
+		port = of_parse_phandle(np, "ports", i);
+		if (!port)
+			break;
+
+		rockchip_add_endpoints(dev, &match, port);
+		of_node_put(port);
+	}
+
+	ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
+	if (ret)
+		return ret;
+
+	return component_master_add_with_match(dev, &rockchip_drm_ops, match);
+}
+
+static int rockchip_drm_platform_remove(struct platform_device *pdev)
+{
+	component_master_del(&pdev->dev, &rockchip_drm_ops);
+	return 0;
+}
+
+static const struct of_device_id rockchip_drm_dt_ids[] = {
+	{ .compatible = "rockchip,display-subsystem", },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, rockchip_drm_dt_ids);
+
+static struct platform_driver rockchip_drm_platform_driver = {
+	.probe = rockchip_drm_platform_probe,
+	.remove = rockchip_drm_platform_remove,
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "rockchip-drm",
+		.of_match_table = rockchip_drm_dt_ids,
+	},
+};
+
+module_platform_driver(rockchip_drm_platform_driver);
+
+MODULE_AUTHOR("Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>");
+MODULE_DESCRIPTION("ROCKCHIP DRM Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
new file mode 100644
index 0000000..154b3ec
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *
+ * based on exynos_drm_drv.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _ROCKCHIP_DRM_DRV_H
+#define _ROCKCHIP_DRM_DRV_H
+
+#include <linux/module.h>
+#include <linux/component.h>
+
+#define ROCKCHIP_MAX_FB_BUFFER	4
+#define ROCKCHIP_MAX_CONNECTOR	2
+
+struct drm_device;
+struct drm_connector;
+
+/*
+ * display output interface supported by rockchip lcdc
+ */
+#define ROCKCHIP_OUTFACE_P888	0
+#define ROCKCHIP_OUTFACE_P666	1
+#define ROCKCHIP_OUTFACE_P565	2
+/* for use special outface */
+#define ROCKCHIP_OUTFACE_AAAA	15
+
+#define ROCKCHIP_COLOR_SWAP_RG	0x1
+#define ROCKCHIP_COLOR_SWAP_RB	0x2
+#define ROCKCHIP_COLOR_SWAP_GB	0x4
+
+/*
+ * Special mode info for rockchip
+ *
+ * @out_type: lcd controller need to know the sceen type.
+ */
+struct rockchip_display_mode {
+	int out_type;
+};
+
+#define ROCKCHIP_EVENT_HOTPLUG	1
+
+enum rockchip_plane_type {
+	ROCKCHIP_WIN0,
+	ROCKCHIP_WIN1,
+	ROCKCHIP_WIN2,
+	ROCKCHIP_WIN3,
+	ROCKCHIP_CURSOR,
+	ROCKCHIP_MAX_PLANE,
+};
+
+/* This enumerates device type. */
+enum rockchip_drm_device_type {
+	ROCKCHIP_DEVICE_TYPE_NONE,
+	ROCKCHIP_DEVICE_TYPE_CRTC,
+	ROCKCHIP_DEVICE_TYPE_CONNECTOR,
+};
+
+/* this enumerates display type. */
+enum rockchip_drm_output_type {
+	ROCKCHIP_DISPLAY_TYPE_NONE = 0,
+	/* RGB Interface. */
+	ROCKCHIP_DISPLAY_TYPE_RGB,
+	/* LVDS Interface. */
+	ROCKCHIP_DISPLAY_TYPE_LVDS,
+	/* EDP Interface. */
+	ROCKCHIP_DISPLAY_TYPE_EDP,
+	/* MIPI Interface. */
+	ROCKCHIP_DISPLAY_TYPE_MIPI,
+	/* HDMI Interface. */
+	ROCKCHIP_DISPLAY_TYPE_HDMI,
+};
+
+enum rockchip_crtc_type {
+	ROCKCHIP_CRTC_VOPB,
+	ROCKCHIP_CRTC_VOPL,
+	ROCKCHIP_MAX_CRTC,
+};
+
+/*
+ * Rockchip drm private structure.
+ *
+ * @num_pipe: number of pipes for this device.
+ */
+struct rockchip_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[ROCKCHIP_MAX_CRTC];
+
+	unsigned int num_pipe;
+};
+
+int rockchip_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
+			  struct device_node *port);
+void rockchip_drm_remove_crtc(struct drm_device *drm, int pipe);
+struct drm_crtc *rockchip_find_crtc(struct drm_device *drm, int pipe);
+int rockchip_drm_encoder_get_mux_id(struct device_node *node,
+				    struct drm_encoder *encoder);
+void rockchip_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe);
+void rockchip_drm_crtc_cancel_pending_flip(struct drm_device *dev);
+int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, int pipe);
+void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, int pipe);
+int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
+				   struct device *dev);
+void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
+				    struct device *dev);
+#endif /* _ROCKCHIP_DRM_DRV_H_ */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
new file mode 100644
index 0000000..b319505
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <drm/drmP.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <uapi/drm/rockchip_drm.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_gem.h"
+
+#define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
+
+struct rockchip_drm_fb {
+	struct drm_framebuffer fb;
+	struct drm_gem_object *obj[ROCKCHIP_MAX_FB_BUFFER];
+};
+
+struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer *fb,
+					       unsigned int plane)
+{
+	struct rockchip_drm_fb *rk_fb = to_rockchip_fb(fb);
+
+	if (plane >= ROCKCHIP_MAX_FB_BUFFER)
+		return NULL;
+
+	return rk_fb->obj[plane];
+}
+
+static void rockchip_drm_fb_destroy(struct drm_framebuffer *fb)
+{
+	struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
+	struct drm_gem_object *obj;
+	int i;
+
+	for (i = 0; i < ROCKCHIP_MAX_FB_BUFFER; i++) {
+		obj = rockchip_fb->obj[i];
+		if (obj)
+			drm_gem_object_unreference_unlocked(obj);
+	}
+
+	drm_framebuffer_cleanup(fb);
+	kfree(rockchip_fb);
+}
+
+static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
+					 struct drm_file *file_priv,
+					 unsigned int *handle)
+{
+	struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
+
+	return drm_gem_handle_create(file_priv,
+				     rockchip_fb->obj[0], handle);
+}
+
+static struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
+	.destroy	= rockchip_drm_fb_destroy,
+	.create_handle	= rockchip_drm_fb_create_handle,
+};
+
+static struct rockchip_drm_fb *
+rockchip_fb_alloc(struct drm_device *dev, struct drm_mode_fb_cmd2 *mode_cmd,
+		  struct drm_gem_object **obj, unsigned int num_planes)
+{
+	struct rockchip_drm_fb *rockchip_fb;
+	int ret;
+	int i;
+
+	rockchip_fb = kzalloc(sizeof(*rockchip_fb), GFP_KERNEL);
+	if (!rockchip_fb)
+		return ERR_PTR(-ENOMEM);
+
+	drm_helper_mode_fill_fb_struct(&rockchip_fb->fb, mode_cmd);
+
+	for (i = 0; i < num_planes; i++)
+		rockchip_fb->obj[i] = obj[i];
+
+	ret = drm_framebuffer_init(dev, &rockchip_fb->fb,
+				   &rockchip_drm_fb_funcs);
+	if (ret) {
+		dev_err(dev->dev, "Failed to initialize framebuffer: %d\n",
+			ret);
+		kfree(rockchip_fb);
+		return ERR_PTR(ret);
+	}
+
+	return rockchip_fb;
+}
+
+static struct drm_framebuffer *
+rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
+			struct drm_mode_fb_cmd2 *mode_cmd)
+{
+	struct rockchip_drm_fb *rockchip_fb;
+	struct drm_gem_object *objs[ROCKCHIP_MAX_FB_BUFFER];
+	struct drm_gem_object *obj;
+	unsigned int hsub;
+	unsigned int vsub;
+	int num_planes;
+	int ret;
+	int i;
+
+	hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format);
+	vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format);
+	num_planes = min(drm_format_num_planes(mode_cmd->pixel_format),
+			 ROCKCHIP_MAX_FB_BUFFER);
+
+	for (i = 0; i < num_planes; i++) {
+		unsigned int width = mode_cmd->width / (i ? hsub : 1);
+		unsigned int height = mode_cmd->height / (i ? vsub : 1);
+		unsigned int min_size;
+
+		obj = drm_gem_object_lookup(dev, file_priv,
+					    mode_cmd->handles[i]);
+		if (!obj) {
+			dev_err(dev->dev, "Failed to lookup GEM object\n");
+			ret = -ENXIO;
+			goto err_gem_object_unreference;
+		}
+
+		min_size = (height - 1) * mode_cmd->pitches[i] +
+			mode_cmd->offsets[i] +
+			width * drm_format_plane_cpp(mode_cmd->pixel_format, i);
+
+		if (obj->size < min_size) {
+			drm_gem_object_unreference_unlocked(obj);
+			ret = -EINVAL;
+			goto err_gem_object_unreference;
+		}
+		objs[i] = obj;
+	}
+
+	rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, objs, i);
+	if (IS_ERR(rockchip_fb)) {
+		ret = PTR_ERR(rockchip_fb);
+		goto err_gem_object_unreference;
+	}
+
+	return &rockchip_fb->fb;
+
+err_gem_object_unreference:
+	for (i--; i >= 0; i--)
+		drm_gem_object_unreference_unlocked(objs[i]);
+	return ERR_PTR(ret);
+}
+
+static void rockchip_drm_output_poll_changed(struct drm_device *dev)
+{
+	struct rockchip_drm_private *private = dev->dev_private;
+	struct drm_fb_helper *fb_helper = private->fb_helper;
+
+	if (fb_helper)
+		drm_fb_helper_hotplug_event(fb_helper);
+}
+
+static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
+	.fb_create = rockchip_user_fb_create,
+	.output_poll_changed = rockchip_drm_output_poll_changed,
+};
+
+struct drm_framebuffer *
+rockchip_drm_framebuffer_init(struct drm_device *dev,
+			      struct drm_mode_fb_cmd2 *mode_cmd,
+			      struct drm_gem_object *obj)
+{
+	struct rockchip_drm_fb *rockchip_fb;
+
+	rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, &obj, 1);
+	if (IS_ERR(rockchip_fb))
+		return NULL;
+
+	return &rockchip_fb->fb;
+}
+
+void rockchip_drm_mode_config_init(struct drm_device *dev)
+{
+	dev->mode_config.min_width = 0;
+	dev->mode_config.min_height = 0;
+
+	/*
+	 * 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 = &rockchip_drm_mode_config_funcs;
+}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.h b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h
new file mode 100644
index 0000000..09574d4
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _ROCKCHIP_DRM_FB_H
+#define _ROCKCHIP_DRM_FB_H
+
+struct drm_framebuffer *
+rockchip_drm_framebuffer_init(struct drm_device *dev,
+			      struct drm_mode_fb_cmd2 *mode_cmd,
+			      struct drm_gem_object *obj);
+void rockchip_drm_framebuffer_fini(struct drm_framebuffer *fb);
+
+void rockchip_drm_mode_config_init(struct drm_device *dev);
+
+struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer *fb,
+					       unsigned int plane);
+#endif /* _ROCKCHIP_DRM_FB_H */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
new file mode 100644
index 0000000..fe1bb22
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+
+#include <drm/rockchip_drm.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_gem.h"
+#include "rockchip_drm_fb.h"
+
+#define PREFERRED_BPP		32
+#define to_rockchip_fbdev(x) container_of(x, struct rockchip_fbdev, helper)
+
+struct rockchip_fbdev {
+	struct drm_fb_helper helper;
+	struct drm_gem_object *bo;
+};
+
+static int rockchip_fbdev_mmap(struct fb_info *info,
+			       struct vm_area_struct *vma)
+{
+	struct drm_fb_helper *helper = info->par;
+	struct rockchip_fbdev *fbdev = to_rockchip_fbdev(helper);
+
+	return rockchip_gem_mmap(fbdev->bo, vma);
+}
+
+static struct fb_ops rockchip_drm_fbdev_ops = {
+	.owner		= THIS_MODULE,
+	.fb_mmap	= rockchip_fbdev_mmap,
+	.fb_fillrect	= cfb_fillrect,
+	.fb_copyarea	= cfb_copyarea,
+	.fb_imageblit	= cfb_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,
+};
+
+static int rockchip_drm_fbdev_create(struct drm_fb_helper *helper,
+				     struct drm_fb_helper_surface_size *sizes)
+{
+	struct rockchip_fbdev *fbdev = to_rockchip_fbdev(helper);
+	struct drm_mode_fb_cmd2 mode_cmd = { 0 };
+	struct drm_device *dev = helper->dev;
+	struct rockchip_gem_object *rk_obj;
+	struct drm_framebuffer *fb;
+	unsigned int bytes_per_pixel;
+	unsigned long offset;
+	struct fb_info *fbi;
+	size_t size;
+	int ret;
+
+	bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);
+
+	mode_cmd.width = sizes->surface_width;
+	mode_cmd.height = sizes->surface_height;
+	mode_cmd.pitches[0] = sizes->surface_width * bytes_per_pixel;
+	mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
+		sizes->surface_depth);
+
+	size = mode_cmd.pitches[0] * mode_cmd.height;
+
+	rk_obj = rockchip_gem_create_object(dev, size);
+	if (IS_ERR(rk_obj))
+		return -ENOMEM;
+
+	fbdev->bo = &rk_obj->base;
+
+	fbi = framebuffer_alloc(0, dev->dev);
+	if (!fbi) {
+		dev_err(dev->dev, "Failed to allocate framebuffer info.\n");
+		ret = -ENOMEM;
+		goto err_rockchip_gem_free_object;
+	}
+
+	helper->fb = rockchip_drm_framebuffer_init(dev, &mode_cmd, fbdev->bo);
+	if (IS_ERR(helper->fb)) {
+		dev_err(dev->dev, "Failed to allocate DRM framebuffer.\n");
+		ret = PTR_ERR(helper->fb);
+		goto err_framebuffer_release;
+	}
+
+	helper->fbdev = fbi;
+
+	fbi->par = helper;
+	fbi->flags = FBINFO_FLAG_DEFAULT;
+	fbi->fbops = &rockchip_drm_fbdev_ops;
+
+	ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
+	if (ret) {
+		dev_err(dev->dev, "Failed to allocate color map.\n");
+		goto err_drm_framebuffer_unref;
+	}
+
+	fb = helper->fb;
+	drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
+	drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
+
+	offset = fbi->var.xoffset * bytes_per_pixel;
+	offset += fbi->var.yoffset * fb->pitches[0];
+
+	dev->mode_config.fb_base = 0;
+	fbi->screen_base = rk_obj->kvaddr + offset;
+	fbi->screen_size = rk_obj->base.size;
+	fbi->fix.smem_len = rk_obj->base.size;
+
+	DRM_DEBUG_KMS("FB [%dx%d]-%d kvaddr=%p offset=%ld size=%d\n",
+		      fb->width, fb->height, fb->depth, rk_obj->kvaddr,
+		      offset, size);
+	return 0;
+
+err_drm_framebuffer_unref:
+	drm_framebuffer_unreference(helper->fb);
+err_framebuffer_release:
+	framebuffer_release(fbi);
+err_rockchip_gem_free_object:
+	rockchip_gem_free_object(&rk_obj->base);
+	return ret;
+}
+
+static struct drm_fb_helper_funcs rockchip_drm_fb_helper_funcs = {
+	.fb_probe = rockchip_drm_fbdev_create,
+};
+
+int rockchip_drm_fbdev_init(struct drm_device *dev)
+{
+	struct rockchip_drm_private *private = dev->dev_private;
+	struct rockchip_fbdev *fbdev;
+	struct drm_fb_helper *helper;
+	unsigned int num_crtc;
+	int ret;
+
+	if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector)
+		return -EINVAL;
+
+	if (private->fb_helper) {
+		DRM_ERROR("no allow to reinit fbdev\n");
+		return -EINVAL;
+	}
+
+	num_crtc = dev->mode_config.num_crtc;
+
+	fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
+	if (!fbdev)
+		return -ENOMEM;
+
+	fbdev->helper.funcs = &rockchip_drm_fb_helper_funcs;
+	helper = &fbdev->helper;
+
+	ret = drm_fb_helper_init(dev, helper, num_crtc, ROCKCHIP_MAX_CONNECTOR);
+	if (ret < 0) {
+		dev_err(dev->dev, "Failed to initialize drm fb helper.\n");
+		goto err_free;
+	}
+
+	ret = drm_fb_helper_single_add_all_connectors(helper);
+	if (ret < 0) {
+		dev_err(dev->dev, "Failed to add connectors.\n");
+		goto err_drm_fb_helper_fini;
+	}
+
+	/* disable all the possible outputs/crtcs before entering KMS mode */
+	drm_helper_disable_unused_functions(dev);
+
+	ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP);
+	if (ret < 0) {
+		dev_err(dev->dev, "Failed to set initial hw configuration.\n");
+		goto err_drm_fb_helper_fini;
+	}
+
+	private->fb_helper = helper;
+
+	return 0;
+
+err_drm_fb_helper_fini:
+	drm_fb_helper_fini(helper);
+err_free:
+	kfree(fbdev);
+	return ret;
+}
+
+void rockchip_drm_fbdev_fini(struct drm_device *dev)
+{
+	struct rockchip_drm_private *private = dev->dev_private;
+	struct drm_fb_helper *helper;
+	struct rockchip_fbdev *fbdev;
+
+	if (!private || !private->fb_helper)
+		return;
+
+	helper = private->fb_helper;
+	fbdev = to_rockchip_fbdev(helper);
+
+	if (helper->fbdev) {
+		struct fb_info *info;
+		int ret;
+
+		info = helper->fbdev;
+		ret = unregister_framebuffer(info);
+		if (ret < 0)
+			DRM_DEBUG_KMS("failed unregister_framebuffer()\n");
+
+		if (info->cmap.len)
+			fb_dealloc_cmap(&info->cmap);
+
+		framebuffer_release(info);
+	}
+
+	if (helper->fb)
+		drm_framebuffer_unreference(helper->fb);
+
+	drm_fb_helper_fini(helper);
+	kfree(fbdev);
+	private->fb_helper = NULL;
+}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
new file mode 100644
index 0000000..5edcf6a
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _ROCKCHIP_DRM_FBDEV_H
+#define _ROCKCHIP_DRM_FBDEV_H
+
+int rockchip_drm_fbdev_init(struct drm_device *dev);
+
+#endif /* _ROCKCHIP_DRM_FBDEV_H */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
new file mode 100644
index 0000000..fd8c4de
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
@@ -0,0 +1,405 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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_vma_manager.h>
+#include <drm/rockchip_drm.h>
+
+#include <linux/anon_inodes.h>
+#include <linux/dma-attrs.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_gem.h"
+
+static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj)
+{
+	struct drm_gem_object *obj = &rk_obj->base;
+	struct drm_device *drm = obj->dev;
+
+	init_dma_attrs(&rk_obj->dma_attrs);
+	dma_set_attr(DMA_ATTR_WRITE_COMBINE, &rk_obj->dma_attrs);
+
+	/* TODO(djkurtz): Use DMA_ATTR_NO_KERNEL_MAPPING except for fbdev */
+	rk_obj->kvaddr = dma_alloc_attrs(drm->dev, obj->size,
+					 &rk_obj->dma_addr, GFP_KERNEL,
+					 &rk_obj->dma_attrs);
+	if (IS_ERR(rk_obj->kvaddr)) {
+		int ret = PTR_ERR(rk_obj->kvaddr);
+
+		DRM_ERROR("failed to allocate %#x byte dma buffer, %d",
+			  obj->size, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void rockchip_gem_free_buf(struct rockchip_gem_object *rk_obj)
+{
+	struct drm_gem_object *obj = &rk_obj->base;
+	struct drm_device *drm = obj->dev;
+
+	dma_free_attrs(drm->dev, obj->size, rk_obj->kvaddr, rk_obj->dma_addr,
+		       &rk_obj->dma_attrs);
+}
+
+int rockchip_drm_gem_mmap_buffer(struct file *filp,
+				 struct vm_area_struct *vma)
+{
+	struct drm_gem_object *obj = filp->private_data;
+
+	return rockchip_gem_mmap(obj, vma);
+}
+
+static const struct file_operations rockchip_drm_gem_fops = {
+	.mmap = rockchip_drm_gem_mmap_buffer,
+};
+
+struct rockchip_gem_object *
+	rockchip_gem_create_object(struct drm_device *drm, unsigned int size)
+{
+	struct rockchip_gem_object *rk_obj;
+	struct drm_gem_object *obj;
+	struct file *filp;
+	int ret;
+
+	size = round_up(size, PAGE_SIZE);
+
+	rk_obj = kzalloc(sizeof(*rk_obj), GFP_KERNEL);
+	if (!rk_obj)
+		return ERR_PTR(-ENOMEM);
+
+	obj = &rk_obj->base;
+
+	drm_gem_private_object_init(drm, obj, size);
+
+	filp = anon_inode_getfile("rockchip_gem", &rockchip_drm_gem_fops,
+				  obj, 0);
+	if (IS_ERR(filp)) {
+		DRM_ERROR("failed to create anon file object.\n");
+		ret = PTR_ERR(filp);
+		goto err_free_rk_obj;
+	}
+	filp->f_mode = FMODE_READ | FMODE_WRITE;
+	obj->filp = filp;
+
+	ret = drm_gem_create_mmap_offset(obj);
+	if (ret)
+		goto err_free_obj;
+
+	ret = rockchip_gem_alloc_buf(rk_obj);
+	if (ret)
+		goto err_free_mmap_offset;
+
+	return rk_obj;
+
+err_free_mmap_offset:
+	drm_gem_free_mmap_offset(obj);
+err_free_obj:
+	drm_gem_object_release(obj);
+err_free_rk_obj:
+	kfree(rk_obj);
+	return ERR_PTR(ret);
+}
+
+/*
+ * rockchip_gem_free_object - (struct drm_driver)->gem_free_object callback
+ * function
+ */
+void rockchip_gem_free_object(struct drm_gem_object *obj)
+{
+	struct rockchip_gem_object *rk_obj;
+
+	drm_gem_free_mmap_offset(obj);
+
+	rk_obj = to_rockchip_obj(obj);
+
+	rockchip_gem_free_buf(rk_obj);
+	drm_gem_free_mmap_offset(obj);
+
+	drm_gem_object_release(obj);
+
+	kfree(rk_obj);
+}
+
+int rockchip_gem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
+{
+	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
+	struct drm_device *drm = obj->dev;
+	unsigned long vm_size;
+
+	vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
+	vm_size = vma->vm_end - vma->vm_start;
+
+	if (vm_size > obj->size)
+		return -EINVAL;
+
+	return dma_mmap_attrs(drm->dev, vma, rk_obj->kvaddr, rk_obj->dma_addr,
+			     obj->size, &rk_obj->dma_attrs);
+}
+
+/*
+ * rockchip_gem_create_with_handle - allocate an object with the given
+ * size and create a gem handle on it
+ *
+ * returns a struct rockchip_gem_object* on success or ERR_PTR values
+ * on failure.
+ */
+static struct rockchip_gem_object *
+rockchip_gem_create_with_handle(struct drm_file *file_priv,
+				struct drm_device *drm, unsigned int size,
+				unsigned int *handle)
+{
+	struct rockchip_gem_object *rk_obj;
+	struct drm_gem_object *obj;
+	int ret;
+
+	rk_obj = rockchip_gem_create_object(drm, size);
+	if (IS_ERR(rk_obj))
+		return NULL;
+
+	obj = &rk_obj->base;
+
+	/*
+	 * 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, obj, handle);
+	if (ret)
+		goto err_handle_create;
+
+	/* drop reference from allocate - handle holds it now. */
+	drm_gem_object_unreference_unlocked(obj);
+
+	return rk_obj;
+
+err_handle_create:
+	rockchip_gem_free_object(obj);
+
+	return ERR_PTR(ret);
+}
+
+int rockchip_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;
+}
+
+/*
+ * rockchip_gem_dumb_create - (struct drm_driver)->dumb_create callback
+ * function
+ *
+ * This aligns the pitch and size arguments to the minimum required. wrap
+ * this into your own function if you need bigger alignment.
+ */
+int rockchip_gem_dumb_create(struct drm_file *file_priv,
+			     struct drm_device *dev,
+			     struct drm_mode_create_dumb *args)
+{
+	struct rockchip_gem_object *rk_obj;
+	int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
+
+	if (args->pitch < min_pitch)
+		args->pitch = min_pitch;
+
+	if (args->size < args->pitch * args->height)
+		args->size = args->pitch * args->height;
+
+	rk_obj = rockchip_gem_create_with_handle(file_priv, dev, args->size,
+						 &args->handle);
+
+	return PTR_ERR_OR_ZERO(rk_obj);
+}
+
+int rockchip_gem_get_ioctl(struct drm_device *dev, void *data,
+			   struct drm_file *file_priv)
+{
+	struct drm_rockchip_gem_info *args = data;
+	struct rockchip_gem_object *rk_obj;
+	struct drm_gem_object *obj;
+
+	mutex_lock(&dev->struct_mutex);
+
+	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+	if (!obj) {
+		DRM_ERROR("failed to lookup gem object.\n");
+		mutex_unlock(&dev->struct_mutex);
+		return -EINVAL;
+	}
+
+	rk_obj = to_rockchip_obj(obj);
+
+	args->flags = rk_obj->flags;
+	args->size = obj->size;
+
+	drm_gem_object_unreference(obj);
+	mutex_unlock(&dev->struct_mutex);
+
+	return 0;
+}
+
+int rockchip_gem_mmap_ioctl(struct drm_device *dev, void *data,
+			    struct drm_file *file_priv)
+{
+	struct drm_rockchip_gem_mmap *args = data;
+	struct drm_gem_object *obj;
+	unsigned long addr;
+
+	mutex_lock(&dev->struct_mutex);
+
+	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+	if (!obj) {
+		DRM_ERROR("failed to lookup gem object.\n");
+		mutex_unlock(&dev->struct_mutex);
+		return -EINVAL;
+	}
+
+	addr = vm_mmap(obj->filp, 0, args->size, PROT_READ | PROT_WRITE,
+		       MAP_SHARED, 0);
+
+	drm_gem_object_unreference(obj);
+
+	if (IS_ERR_VALUE(addr)) {
+		mutex_unlock(&dev->struct_mutex);
+		return (int)addr;
+	}
+
+	mutex_unlock(&dev->struct_mutex);
+
+	args->mapped = addr;
+
+	DRM_DEBUG_KMS("mapped = 0x%lx\n", (unsigned long)args->mapped);
+
+	return 0;
+}
+
+int rockchip_gem_map_offset_ioctl(struct drm_device *drm, void *data,
+				  struct drm_file *file_priv)
+{
+	struct drm_rockchip_gem_map_off *args = data;
+
+	return rockchip_gem_dumb_map_offset(file_priv, drm, args->handle,
+					    &args->offset);
+}
+
+int rockchip_gem_create_ioctl(struct drm_device *dev, void *data,
+			      struct drm_file *file_priv)
+{
+	struct drm_rockchip_gem_create *args = data;
+	struct rockchip_gem_object *rk_obj;
+
+	rk_obj = rockchip_gem_create_with_handle(file_priv, dev, args->size,
+						 &args->handle);
+	return PTR_ERR_OR_ZERO(rk_obj);
+}
+
+/*
+ * Allocate a sg_table for this GEM object.
+ * Note: Both the table's contents, and the sg_table itself must be freed by
+ *       the caller.
+ * Returns a pointer to the newly allocated sg_table, or an ERR_PTR() error.
+ */
+struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj)
+{
+	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
+	struct drm_device *drm = obj->dev;
+	struct sg_table *sgt = NULL;
+	int ret;
+
+	sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
+	if (!sgt)
+		return ERR_PTR(-ENOMEM);
+
+	ret = dma_get_sgtable_attrs(drm->dev, sgt, rk_obj->kvaddr,
+				    rk_obj->dma_addr, obj->size,
+				    &rk_obj->dma_attrs);
+	if (ret) {
+		DRM_ERROR("failed to allocate sgt, %d\n", ret);
+		kfree(sgt);
+		return ERR_PTR(ret);
+	}
+
+	return sgt;
+}
+
+struct drm_gem_object *
+rockchip_gem_prime_import_sg_table(struct drm_device *dev, size_t size,
+				   struct sg_table *sgt)
+{
+	struct rockchip_gem_object *rk_obj;
+
+	if (sgt->nents != 1)
+		return ERR_PTR(-EINVAL);
+
+	rk_obj = rockchip_gem_create_object(dev, size);
+	if (IS_ERR(rk_obj))
+		return ERR_PTR(-ENOMEM);
+
+	return &rk_obj->base;
+}
+
+void *rockchip_gem_prime_vmap(struct drm_gem_object *obj)
+{
+	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
+
+	return rk_obj->kvaddr;
+}
+
+void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
+{
+	/* Nothing to do */
+}
+
+int rockchip_gem_prime_mmap(struct drm_gem_object *obj,
+			    struct vm_area_struct *vma)
+{
+	struct drm_device *dev = obj->dev;
+	int ret;
+
+	mutex_lock(&dev->struct_mutex);
+	ret = drm_gem_mmap_obj(obj, obj->size, vma);
+	mutex_unlock(&dev->struct_mutex);
+
+	return ret;
+}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
new file mode 100644
index 0000000..7f9eb49
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _ROCKCHIP_DRM_GEM_H
+#define _ROCKCHIP_DRM_GEM_H
+
+#define to_rockchip_obj(x) container_of(x, struct rockchip_gem_object, base)
+
+struct rockchip_gem_object {
+	struct drm_gem_object base;
+	unsigned int flags;
+
+	void *kvaddr;
+	dma_addr_t dma_addr;
+	struct dma_attrs dma_attrs;
+};
+
+struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj);
+struct drm_gem_object *
+rockchip_gem_prime_import_sg_table(struct drm_device *dev, size_t size,
+				   struct sg_table *sgt);
+void *rockchip_gem_prime_vmap(struct drm_gem_object *obj);
+void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
+int rockchip_gem_prime_mmap(struct drm_gem_object *obj,
+			    struct vm_area_struct *vma);
+
+/* mmap a gem object to userspace. */
+int rockchip_gem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma);
+
+struct rockchip_gem_object *
+	rockchip_gem_create_object(struct drm_device *drm, unsigned int size);
+
+void rockchip_gem_free_object(struct drm_gem_object *obj);
+
+int rockchip_gem_dumb_create(struct drm_file *file_priv,
+			     struct drm_device *dev,
+			     struct drm_mode_create_dumb *args);
+int rockchip_gem_dumb_map_offset(struct drm_file *file_priv,
+				 struct drm_device *dev, uint32_t handle,
+				 uint64_t *offset);
+int rockchip_gem_map_offset_ioctl(struct drm_device *drm, void *data,
+				  struct drm_file *file_priv);
+/*
+ * request gem object creation and buffer allocation as the size
+ * that it is calculated with framebuffer information such as width,
+ * height and bpp.
+ */
+int rockchip_gem_create_ioctl(struct drm_device *dev, void *data,
+			      struct drm_file *file_priv);
+
+/* get buffer offset to map to user space. */
+int rockchip_gem_map_offset_ioctl(struct drm_device *dev, void *data,
+				  struct drm_file *file_priv);
+
+/*
+ * mmap the physically continuous memory that a gem object contains
+ * to user space.
+ */
+int rockchip_gem_mmap_ioctl(struct drm_device *dev, void *data,
+			    struct drm_file *file_priv);
+
+/* get buffer information to memory region allocated by gem. */
+int rockchip_gem_get_ioctl(struct drm_device *dev, void *data,
+			   struct drm_file *file_priv);
+#endif /* _ROCKCHIP_DRM_GEM_H */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
new file mode 100644
index 0000000..f8e1228
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -0,0 +1,1442 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/component.h>
+
+#include <linux/reset.h>
+#include <linux/iommu.h>
+#include <linux/delay.h>
+#include <drm/rockchip_drm.h>
+
+#include <video/of_display_timing.h>
+#include <video/of_videomode.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_fbdev.h"
+#include "rockchip_drm_gem.h"
+#include "rockchip_drm_fb.h"
+#include "rockchip_drm_vop.h"
+
+#define VOP_DEFAULT_FRAMERATE	60
+#define VOP_MAX_WIN_SUPPORT	5
+#define VOP_DEFAULT_CURSOR	1
+#define VOP_REG(off, _mask, s) \
+		{.offset = off, \
+		 .mask = _mask, \
+		 .shift = s,}
+
+#define __REG_SET(x, off, mask, shift, v) \
+		vop_mask_write(x, off, (mask) << shift, (v) << shift)
+
+#define REG_SET(x, base, reg, v) \
+		__REG_SET(x, base + reg.offset, reg.mask, reg.shift, v)
+
+#define VOP_WIN_SET(x, win, name, v) \
+		REG_SET(x, win->base, win->phy->name, v)
+#define VOP_CTRL_SET(x, name, v) \
+		REG_SET(x, 0, (x)->data->ctrl->name, v)
+
+#define VOP_WIN_GET_YRGBADDR(ctx, win) \
+		vop_readl(ctx, win->base + win->phy->yrgb_mst.offset)
+
+#define to_vop_ctx(x) container_of(x, struct vop_context, crtc)
+#define to_rockchip_plane(x) container_of(x, struct rockchip_plane, base)
+
+struct rockchip_plane {
+	int id;
+	struct drm_plane base;
+	const struct vop_win *win;
+	struct vop_context *ctx;
+
+	uint32_t pending_yrgb_mst;
+	struct drm_framebuffer *front_fb;
+	struct drm_framebuffer *pending_fb;
+	bool enabled;
+};
+
+struct vop_context {
+	struct device *dev;
+	struct drm_device *drm_dev;
+	struct drm_crtc crtc;
+	struct drm_pending_vblank_event *event;
+	struct drm_framebuffer *cursor_fb;
+	struct vop_driver *drv;
+	unsigned int dpms;
+	unsigned int win_mask;
+	wait_queue_head_t wait_vsync_queue;
+	atomic_t wait_vsync_event;
+
+	struct workqueue_struct *vsync_wq;
+	struct work_struct vsync_work;
+
+	int cursor_x;
+	int cursor_y;
+	/* mutex vsync_ work */
+	struct mutex vsync_mutex;
+	bool vsync_work_pending;
+
+	struct vop_driver_data *data;
+
+	uint32_t *regsbak;
+	void __iomem *regs;
+
+	/* physical map length of vop register */
+	uint32_t len;
+
+	/* one time only one process allowed to config the register */
+	spinlock_t reg_lock;
+	/* lock vop irq reg */
+	spinlock_t irq_lock;
+
+	unsigned int irq;
+
+	/* vop AHP clk */
+	struct clk *hclk;
+	/* vop dclk */
+	struct clk *dclk;
+	/* vop share memory frequency */
+	struct clk *aclk;
+	uint32_t pixclock;
+
+	int pipe;
+	bool clk_on;
+};
+
+enum vop_data_format {
+	VOP_FMT_ARGB8888 = 0,
+	VOP_FMT_RGB888,
+	VOP_FMT_RGB565,
+	VOP_FMT_YUV420SP = 4,
+	VOP_FMT_YUV422SP,
+	VOP_FMT_YUV444SP,
+};
+
+struct vop_reg_data {
+	uint32_t offset;
+	uint32_t value;
+};
+
+struct vop_reg {
+	uint32_t offset;
+	uint32_t shift;
+	uint32_t mask;
+};
+
+struct vop_ctrl {
+	struct vop_reg standby;
+	struct vop_reg gate_en;
+	struct vop_reg mmu_en;
+	struct vop_reg rgb_en;
+	struct vop_reg edp_en;
+	struct vop_reg hdmi_en;
+	struct vop_reg mipi_en;
+	struct vop_reg out_mode;
+	struct vop_reg dither_down;
+	struct vop_reg dither_up;
+	struct vop_reg pin_pol;
+
+	struct vop_reg htotal_pw;
+	struct vop_reg hact_st_end;
+	struct vop_reg vtotal_pw;
+	struct vop_reg vact_st_end;
+	struct vop_reg hpost_st_end;
+	struct vop_reg vpost_st_end;
+};
+
+struct vop_win_phy {
+	const uint32_t *data_formats;
+	uint32_t nformats;
+
+	struct vop_reg enable;
+	struct vop_reg format;
+	struct vop_reg act_info;
+	struct vop_reg dsp_info;
+	struct vop_reg dsp_st;
+	struct vop_reg yrgb_mst;
+	struct vop_reg uv_mst;
+	struct vop_reg yrgb_vir;
+	struct vop_reg uv_vir;
+
+	struct vop_reg dst_alpha_ctl;
+	struct vop_reg src_alpha_ctl;
+};
+
+struct vop_win {
+	uint32_t base;
+	const struct vop_win_phy *phy;
+};
+
+struct vop_driver_data {
+	const void *init_table;
+	int table_size;
+	const struct vop_ctrl *ctrl;
+	const struct vop_win *win[VOP_MAX_WIN_SUPPORT];
+};
+
+static const uint32_t formats_01[] = {
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_NV12,
+	DRM_FORMAT_NV16,
+	DRM_FORMAT_NV24,
+};
+
+static const uint32_t formats_234[] = {
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_RGB565,
+};
+
+static const struct vop_win_phy win01_data = {
+	.data_formats = formats_01,
+	.nformats = ARRAY_SIZE(formats_01),
+	.enable = VOP_REG(WIN0_CTRL0, 0x1, 0),
+	.format = VOP_REG(WIN0_CTRL0, 0x7, 1),
+	.act_info = VOP_REG(WIN0_ACT_INFO, 0x1fff1fff, 0),
+	.dsp_info = VOP_REG(WIN0_DSP_INFO, 0x1fff1fff, 0),
+	.dsp_st = VOP_REG(WIN0_DSP_ST, 0x1fff1fff, 0),
+	.yrgb_mst = VOP_REG(WIN0_YRGB_MST, 0xffffffff, 0),
+	.uv_mst = VOP_REG(WIN0_CBR_MST, 0xffffffff, 0),
+	.yrgb_vir = VOP_REG(WIN0_VIR, 0x3fff, 0),
+	.uv_vir = VOP_REG(WIN0_VIR, 0x3fff, 16),
+	.src_alpha_ctl = VOP_REG(WIN0_SRC_ALPHA_CTRL, 0xff, 0),
+	.dst_alpha_ctl = VOP_REG(WIN0_DST_ALPHA_CTRL, 0xff, 0),
+};
+
+static const struct vop_win_phy win23_data = {
+	.data_formats = formats_234,
+	.nformats = ARRAY_SIZE(formats_234),
+	.enable = VOP_REG(WIN2_CTRL0, 0x1, 0),
+	.format = VOP_REG(WIN2_CTRL0, 0x7, 1),
+	.dsp_info = VOP_REG(WIN2_DSP_INFO0, 0x0fff0fff, 0),
+	.dsp_st = VOP_REG(WIN2_DSP_ST0, 0x1fff1fff, 0),
+	.yrgb_mst = VOP_REG(WIN2_MST0, 0xffffffff, 0),
+	.yrgb_vir = VOP_REG(WIN2_VIR0_1, 0x1fff, 0),
+	.src_alpha_ctl = VOP_REG(WIN2_SRC_ALPHA_CTRL, 0xff, 0),
+	.dst_alpha_ctl = VOP_REG(WIN2_DST_ALPHA_CTRL, 0xff, 0),
+};
+
+static const struct vop_win_phy cursor_data = {
+	.data_formats = formats_234,
+	.nformats = ARRAY_SIZE(formats_234),
+	.enable = VOP_REG(HWC_CTRL0, 0x1, 0),
+	.format = VOP_REG(HWC_CTRL0, 0x7, 1),
+	.dsp_st = VOP_REG(HWC_DSP_ST, 0x1fff1fff, 0),
+	.yrgb_mst = VOP_REG(HWC_MST, 0xffffffff, 0),
+};
+
+static const struct vop_win win0 = {
+	.base = 0,
+	.phy = &win01_data,
+};
+
+static const struct vop_win win1 = {
+	.base = 0x40,
+	.phy = &win01_data,
+};
+
+static const struct vop_win win2 = {
+	.base = 0,
+	.phy = &win23_data,
+};
+
+static const struct vop_win win3 = {
+	.base = 0x50,
+	.phy = &win23_data,
+};
+
+static const struct vop_win win_cursor = {
+	.base = 0,
+	.phy = &cursor_data,
+};
+
+static const struct vop_ctrl ctrl_data = {
+	.standby = VOP_REG(SYS_CTRL, 0x1, 22),
+	.gate_en = VOP_REG(SYS_CTRL, 0x1, 23),
+	.mmu_en = VOP_REG(SYS_CTRL, 0x1, 20),
+	.rgb_en = VOP_REG(SYS_CTRL, 0x1, 12),
+	.hdmi_en = VOP_REG(SYS_CTRL, 0x1, 13),
+	.edp_en = VOP_REG(SYS_CTRL, 0x1, 14),
+	.mipi_en = VOP_REG(SYS_CTRL, 0x1, 15),
+	.dither_down = VOP_REG(DSP_CTRL1, 0xf, 1),
+	.dither_up = VOP_REG(DSP_CTRL1, 0x1, 6),
+	.out_mode = VOP_REG(DSP_CTRL0, 0xf, 0),
+	.pin_pol = VOP_REG(DSP_CTRL0, 0xf, 4),
+	.htotal_pw = VOP_REG(DSP_HTOTAL_HS_END, 0x1fff1fff, 0),
+	.hact_st_end = VOP_REG(DSP_HACT_ST_END, 0x1fff1fff, 0),
+	.vtotal_pw = VOP_REG(DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
+	.vact_st_end = VOP_REG(DSP_VACT_ST_END, 0x1fff1fff, 0),
+	.hpost_st_end = VOP_REG(POST_DSP_HACT_INFO, 0x1fff1fff, 0),
+	.vpost_st_end = VOP_REG(POST_DSP_VACT_INFO, 0x1fff1fff, 0),
+};
+
+static const struct vop_reg_data vop_init_reg_table[] = {
+	{SYS_CTRL, 0x00801000},
+	{DSP_CTRL0, 0x00000000},
+	{WIN0_CTRL0, 0x00000080},
+	{WIN1_CTRL0, 0x00000080},
+};
+
+static const struct vop_driver_data rockchip_rk3288_vop = {
+	.init_table = vop_init_reg_table,
+	.table_size = ARRAY_SIZE(vop_init_reg_table),
+	.ctrl = &ctrl_data,
+	.win[0] = &win0,
+	.win[1] = &win1,
+	.win[2] = &win2,
+	.win[3] = &win3,
+	.win[4] = &win_cursor,
+};
+
+static const struct of_device_id vop_driver_dt_match[] = {
+	{ .compatible = "rockchip,rk3288-vop",
+	  .data = (void *)&rockchip_rk3288_vop },
+	{},
+};
+
+static inline void vop_writel(struct vop_context *ctx,
+			      uint32_t offset, uint32_t v)
+{
+	writel(v, ctx->regs + offset);
+	ctx->regsbak[offset >> 2] = v;
+}
+
+static inline uint32_t vop_readl(struct vop_context *ctx, uint32_t offset)
+{
+	return readl(ctx->regs + offset);
+}
+
+static inline void vop_cfg_done(struct vop_context *ctx)
+{
+	writel(0x01, ctx->regs + REG_CFG_DONE);
+}
+
+static inline void vop_mask_write(struct vop_context *ctx,
+				  uint32_t offset, uint32_t mask, uint32_t v)
+{
+	if (mask) {
+		uint32_t cached_val = ctx->regsbak[offset >> 2];
+
+		cached_val = (cached_val & ~mask) | v;
+		writel(cached_val, ctx->regs + offset);
+		ctx->regsbak[offset >> 2] = cached_val;
+	}
+}
+
+static inline struct vop_driver_data *vop_get_driver_data(struct device *dev)
+{
+	const struct of_device_id *of_id =
+			of_match_device(vop_driver_dt_match, dev);
+
+	return (struct vop_driver_data *)of_id->data;
+}
+
+static enum vop_data_format vop_convert_format(uint32_t format)
+{
+	switch (format) {
+	case DRM_FORMAT_XRGB8888:
+	case DRM_FORMAT_ARGB8888:
+		return VOP_FMT_ARGB8888;
+	case DRM_FORMAT_RGB888:
+		return VOP_FMT_RGB888;
+	case DRM_FORMAT_RGB565:
+		return VOP_FMT_RGB565;
+	case DRM_FORMAT_NV12:
+		return VOP_FMT_YUV420SP;
+	case DRM_FORMAT_NV16:
+		return VOP_FMT_YUV422SP;
+	case DRM_FORMAT_NV24:
+		return VOP_FMT_YUV444SP;
+	default:
+		DRM_ERROR("unsupport format[%08x]\n", format);
+		return -EINVAL;
+	}
+}
+
+static bool is_alpha_support(uint32_t format)
+{
+	switch (format) {
+	case DRM_FORMAT_ARGB8888:
+		return true;
+	default:
+		return false;
+	}
+}
+
+/* TODO(djkurtz): move generic 'setup slave rk_iommu' code somewhere common */
+int vop_iommu_init(struct vop_context *ctx)
+{
+	struct device *dev = ctx->dev;
+	struct device_node *np = dev->of_node;
+	struct platform_device *pd;
+	int count;
+	int ret;
+	struct of_phandle_args args;
+
+	/* Each VOP must have exactly one iommu node, with no args */
+	count = of_count_phandle_with_args(np, "iommus", "#iommu-cells");
+	if (count != 1) {
+		dev_err(dev, "of_count_phandle_with_args(%s) => %d\n",
+			np->full_name, count);
+		return -EINVAL;
+	}
+
+	ret = of_parse_phandle_with_args(np, "iommus", "#iommu-cells", 0,
+					 &args);
+	if (ret) {
+		dev_err(dev, "of_parse_phandle_with_args(%s) => %d\n",
+			np->full_name, ret);
+		return ret;
+	}
+	if (args.args_count != 0) {
+		dev_err(dev, "incorrect number of iommu params found for %s (found %d, expected 0)\n",
+			args.np->full_name, args.args_count);
+		return -EINVAL;
+	}
+
+	pd = of_find_device_by_node(args.np);
+	of_node_put(args.np);
+	if (!pd) {
+		dev_err(dev, "iommu %s not found\n", args.np->full_name);
+		return -EPROBE_DEFER;
+	}
+
+	/* TODO(djkurtz): handle multiple slave iommus for a single master */
+	dev->archdata.iommu = &pd->dev;
+
+	ret = rockchip_drm_dma_attach_device(ctx->drm_dev, dev);
+	if (ret) {
+		dev_err(dev, "failed to attach to drm dma mapping, %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void vop_iommu_fini(struct vop_context *ctx)
+{
+	rockchip_drm_dma_detach_device(ctx->drm_dev, ctx->dev);
+}
+
+static int rockchip_plane_get_size(int start, unsigned length, unsigned last)
+{
+	int end = start + length;
+	int size = 0;
+
+	if (start <= 0) {
+		if (end > 0)
+			size = min_t(unsigned, end, last);
+	} else if (start <= last) {
+		size = min_t(unsigned, last - start, length);
+	}
+
+	return size;
+}
+
+static int vop_clk_enable(struct vop_context *ctx)
+{
+	int ret;
+
+	if (!ctx->clk_on) {
+		ret = clk_prepare_enable(ctx->hclk);
+		if (ret < 0) {
+			dev_err(ctx->dev, "failed to enable hclk\n");
+			return ret;
+		}
+
+		ret = clk_prepare_enable(ctx->dclk);
+		if (ret < 0) {
+			dev_err(ctx->dev, "failed to enable dclk\n");
+			goto err_dclk;
+		}
+
+		ret = clk_prepare_enable(ctx->aclk);
+		if (ret < 0) {
+			dev_err(ctx->dev, "failed to enable aclk\n");
+			goto err_aclk;
+		}
+		ctx->clk_on = true;
+	}
+
+	return ret;
+err_aclk:
+	clk_disable_unprepare(ctx->aclk);
+err_dclk:
+	clk_disable_unprepare(ctx->hclk);
+	return ret;
+}
+
+static void vop_clk_disable(struct vop_context *ctx)
+{
+	if (ctx->clk_on) {
+		clk_disable_unprepare(ctx->dclk);
+		clk_disable_unprepare(ctx->hclk);
+		clk_disable_unprepare(ctx->aclk);
+		ctx->clk_on = false;
+	}
+}
+
+static void vop_power_on(struct vop_context *ctx)
+{
+	if (vop_clk_enable(ctx) < 0) {
+		dev_err(ctx->dev, "failed to enable clks\n");
+		return;
+	}
+
+	spin_lock(&ctx->reg_lock);
+
+	VOP_CTRL_SET(ctx, standby, 0);
+
+	spin_unlock(&ctx->reg_lock);
+}
+
+static void vop_power_off(struct vop_context *ctx)
+{
+	spin_lock(&ctx->reg_lock);
+
+	VOP_CTRL_SET(ctx, standby, 1);
+
+	spin_unlock(&ctx->reg_lock);
+
+	vop_clk_disable(ctx);
+}
+
+static int rockchip_crtc_update_cursor(struct drm_crtc *crtc,
+				       struct drm_plane *plane)
+{
+	struct vop_context *ctx = to_vop_ctx(crtc);
+
+	return plane->funcs->update_plane(plane, crtc, ctx->cursor_fb,
+					  ctx->cursor_x, ctx->cursor_y,
+					  ctx->cursor_fb->width,
+					  ctx->cursor_fb->height, 0, 0,
+					  ctx->cursor_fb->width << 16,
+					  ctx->cursor_fb->height << 16);
+}
+
+static int vop_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file,
+			       uint32_t handle, uint32_t width,
+			       uint32_t height)
+{
+	struct vop_context *ctx = to_vop_ctx(crtc);
+	struct drm_plane *cursor = crtc->cursor;
+	int cpp;
+	struct drm_mode_fb_cmd2 mode_cmd = { 0 };
+	struct drm_gem_object *obj;
+
+	if (!handle) {
+		cursor->funcs->disable_plane(cursor);
+		if (ctx->cursor_fb) {
+			drm_framebuffer_unreference(ctx->cursor_fb);
+			ctx->cursor_fb = NULL;
+		}
+		return 0;
+	}
+
+	obj = drm_gem_object_lookup(crtc->dev, file, handle);
+	if (!obj) {
+		DRM_ERROR("failed to lookup gem object.\n");
+		return -EINVAL;
+	}
+	if (ctx->cursor_fb)
+		drm_framebuffer_unreference(ctx->cursor_fb);
+	cpp = drm_format_plane_cpp(DRM_FORMAT_ARGB8888, 0);
+
+	mode_cmd.width = width;
+	mode_cmd.height = height;
+	mode_cmd.pitches[0] = width * cpp;
+	mode_cmd.pixel_format = DRM_FORMAT_ARGB8888;
+	ctx->cursor_fb = rockchip_drm_framebuffer_init(ctx->drm_dev,
+						       &mode_cmd, obj);
+
+	return rockchip_crtc_update_cursor(crtc, cursor);
+}
+
+static int vop_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
+{
+	struct vop_context *ctx = to_vop_ctx(crtc);
+	struct drm_plane *cursor = crtc->cursor;
+	struct rockchip_plane *rockchip_plane = to_rockchip_plane(cursor);
+
+	if (!rockchip_plane->enabled)
+		return 0;
+
+	ctx->cursor_x = x;
+	ctx->cursor_y = y;
+
+	return rockchip_crtc_update_cursor(crtc, cursor);
+}
+
+static int rockchip_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
+				 struct drm_framebuffer *fb, int crtc_x,
+				 int crtc_y, unsigned int crtc_w,
+				 unsigned int crtc_h, uint32_t src_x,
+				 uint32_t src_y, uint32_t src_w, uint32_t src_h)
+{
+	struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
+	const struct vop_win *win = rockchip_plane->win;
+	struct vop_context *ctx = to_vop_ctx(crtc);
+	struct drm_gem_object *obj;
+	struct rockchip_gem_object *rk_obj;
+	unsigned long offset;
+	unsigned int actual_w;
+	unsigned int actual_h;
+	unsigned int dsp_stx;
+	unsigned int dsp_sty;
+	unsigned int y_vir_stride;
+	dma_addr_t yrgb_mst;
+	enum vop_data_format format;
+	uint32_t val;
+	bool is_alpha;
+
+	if (!win) {
+		DRM_ERROR("can't find win data for vop, failed\n");
+		return -EINVAL;
+	}
+
+	obj = rockchip_fb_get_gem_obj(fb, 0);
+	if (!obj) {
+		DRM_ERROR("fail to get rockchip gem object from framebuffer\n");
+		return -EINVAL;
+	}
+
+	rk_obj = to_rockchip_obj(obj);
+
+	yrgb_mst = rk_obj->dma_addr;
+	if (yrgb_mst <= 0)
+		return -ENOMEM;
+
+	actual_w = rockchip_plane_get_size(crtc_x,
+					   crtc_w, crtc->mode.hdisplay);
+	actual_h = rockchip_plane_get_size(crtc_y,
+					   crtc_h, crtc->mode.vdisplay);
+	if (crtc_x < 0) {
+		if (actual_w)
+			src_x -= crtc_x;
+		crtc_x = 0;
+	}
+
+	if (crtc_y < 0) {
+		if (actual_h)
+			src_y -= crtc_y;
+		crtc_y = 0;
+	}
+
+	dsp_stx = crtc_x + crtc->mode.htotal - crtc->mode.hsync_start;
+	dsp_sty = crtc_y + crtc->mode.vtotal - crtc->mode.vsync_start;
+
+	offset = src_x * (fb->bits_per_pixel >> 3);
+	offset += src_y * fb->pitches[0];
+
+	y_vir_stride = fb->pitches[0] / (fb->bits_per_pixel >> 3);
+	is_alpha = is_alpha_support(fb->pixel_format);
+	format = vop_convert_format(fb->pixel_format);
+
+	spin_lock(&ctx->reg_lock);
+
+	VOP_WIN_SET(ctx, win, format, format);
+	VOP_WIN_SET(ctx, win, yrgb_vir, y_vir_stride);
+	yrgb_mst += offset;
+	VOP_WIN_SET(ctx, win, yrgb_mst, yrgb_mst);
+	VOP_WIN_SET(ctx, win, act_info,
+		    ((actual_h - 1) << 16) | (actual_w - 1));
+	VOP_WIN_SET(ctx, win, dsp_info,
+		    ((actual_h - 1) << 16) | (actual_w - 1));
+	VOP_WIN_SET(ctx, win, dsp_st, (dsp_sty << 16) | dsp_stx);
+	if (is_alpha) {
+		VOP_WIN_SET(ctx, win, dst_alpha_ctl,
+			    DST_FACTOR_M0(ALPHA_SRC_INVERSE));
+		val = SRC_ALPHA_EN(1) | SRC_COLOR_M0(ALPHA_SRC_PRE_MUL) |
+			SRC_ALPHA_M0(ALPHA_STRAIGHT) |
+			SRC_BLEND_M0(ALPHA_PER_PIX) |
+			SRC_ALPHA_CAL_M0(ALPHA_NO_SATURATION) |
+			SRC_FACTOR_M0(ALPHA_ONE);
+		VOP_WIN_SET(ctx, win, src_alpha_ctl, val);
+	} else {
+		VOP_WIN_SET(ctx, win, src_alpha_ctl, SRC_ALPHA_EN(0));
+	}
+
+	VOP_WIN_SET(ctx, win, enable, 1);
+
+	spin_unlock(&ctx->reg_lock);
+
+	mutex_lock(&ctx->vsync_mutex);
+
+	/*
+	 * Because the buffer set to vop take effect at frame start time,
+	 * we need make sure old buffer is not in use before we release
+	 * it.
+	 * reference the framebuffer, and unference it when it swap out of vop.
+	 */
+	if (fb != rockchip_plane->front_fb) {
+		drm_framebuffer_reference(fb);
+		rockchip_plane->pending_fb = fb;
+		rockchip_plane->pending_yrgb_mst = yrgb_mst;
+		ctx->vsync_work_pending = true;
+	}
+	rockchip_plane->enabled = true;
+
+	mutex_unlock(&ctx->vsync_mutex);
+
+	spin_lock(&ctx->reg_lock);
+	vop_cfg_done(ctx);
+	spin_unlock(&ctx->reg_lock);
+
+	return 0;
+}
+
+static int rockchip_disable_plane(struct drm_plane *plane)
+{
+	struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
+	struct vop_context *ctx = rockchip_plane->ctx;
+	const struct vop_win *win = rockchip_plane->win;
+
+	spin_lock(&ctx->reg_lock);
+
+	VOP_WIN_SET(ctx, win, enable, 0);
+	vop_cfg_done(ctx);
+
+	spin_unlock(&ctx->reg_lock);
+
+	mutex_lock(&ctx->vsync_mutex);
+
+	/*
+	* clear the pending framebuffer and set vsync_work_pending true,
+	* so that the framebuffer will unref at the next vblank.
+	*/
+	if (rockchip_plane->pending_fb) {
+		drm_framebuffer_unreference(rockchip_plane->pending_fb);
+		rockchip_plane->pending_fb = NULL;
+	}
+
+	rockchip_plane->enabled = false;
+	ctx->vsync_work_pending = true;
+
+	mutex_unlock(&ctx->vsync_mutex);
+
+	return 0;
+}
+
+static void rockchip_plane_destroy(struct drm_plane *plane)
+{
+	struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
+	struct vop_context *ctx = rockchip_plane->ctx;
+
+	rockchip_disable_plane(plane);
+	drm_plane_cleanup(plane);
+	ctx->win_mask &= ~(1 << rockchip_plane->id);
+	kfree(rockchip_plane);
+}
+
+static const struct drm_plane_funcs rockchip_plane_funcs = {
+	.update_plane = rockchip_update_plane,
+	.disable_plane = rockchip_disable_plane,
+	.destroy = rockchip_plane_destroy,
+};
+
+struct drm_plane *rockchip_plane_init(struct vop_context *ctx,
+				      unsigned long possible_crtcs,
+				      enum drm_plane_type type)
+{
+	struct rockchip_plane *rockchip_plane;
+	struct vop_driver_data *vop_data = ctx->data;
+	const struct vop_win *win;
+	int i;
+	int err;
+
+	rockchip_plane = kzalloc(sizeof(*rockchip_plane), GFP_KERNEL);
+	if (!rockchip_plane)
+		return NULL;
+
+	for (i = 0; i < VOP_MAX_WIN_SUPPORT; i++) {
+		if (!(ctx->win_mask & (1 << i))) {
+			win = vop_data->win[i];
+			break;
+		}
+	}
+
+	if (VOP_MAX_WIN_SUPPORT == i) {
+		DRM_ERROR("failed to find win\n");
+		kfree(rockchip_plane);
+		return NULL;
+	}
+
+	ctx->win_mask |= (1 << i);
+	rockchip_plane->id = i;
+	rockchip_plane->win = win;
+	rockchip_plane->ctx = ctx;
+
+	err = drm_universal_plane_init(ctx->drm_dev, &rockchip_plane->base,
+				       possible_crtcs, &rockchip_plane_funcs,
+				       win->phy->data_formats,
+				       win->phy->nformats, type);
+	if (err) {
+		DRM_ERROR("failed to initialize plane\n");
+		kfree(rockchip_plane);
+		return NULL;
+	}
+
+	return &rockchip_plane->base;
+}
+
+int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, int pipe)
+{
+	struct vop_context *ctx = to_vop_ctx(rockchip_find_crtc(dev, pipe));
+	unsigned long flags;
+
+	if (ctx->dpms != DRM_MODE_DPMS_ON)
+		return -EPERM;
+
+	spin_lock_irqsave(&ctx->irq_lock, flags);
+
+	vop_mask_write(ctx, INTR_CTRL0, LINE_FLAG_INTR_MASK,
+		       LINE_FLAG_INTR_EN(1));
+
+	spin_unlock_irqrestore(&ctx->irq_lock, flags);
+
+	return 0;
+}
+
+void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, int pipe)
+{
+	struct vop_context *ctx = to_vop_ctx(rockchip_find_crtc(dev, pipe));
+	unsigned long flags;
+
+	if (ctx->dpms != DRM_MODE_DPMS_ON)
+		return;
+	spin_lock_irqsave(&ctx->irq_lock, flags);
+	vop_mask_write(ctx, INTR_CTRL0, LINE_FLAG_INTR_MASK,
+		       LINE_FLAG_INTR_EN(0));
+	spin_unlock_irqrestore(&ctx->irq_lock, flags);
+}
+
+static void rockchip_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+	struct vop_context *ctx = to_vop_ctx(crtc);
+
+	DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode);
+
+	if (ctx->dpms == mode) {
+		DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
+		return;
+	}
+	if (mode > DRM_MODE_DPMS_ON) {
+		/* wait for the completion of page flip. */
+		if (!wait_event_timeout(ctx->wait_vsync_queue,
+					!atomic_read(&ctx->wait_vsync_event),
+					HZ/20))
+			DRM_DEBUG_KMS("vblank wait timed out.\n");
+		drm_vblank_off(crtc->dev, ctx->pipe);
+	}
+
+	switch (mode) {
+	case DRM_MODE_DPMS_ON:
+		vop_power_on(ctx);
+		break;
+	case DRM_MODE_DPMS_STANDBY:
+	case DRM_MODE_DPMS_SUSPEND:
+	case DRM_MODE_DPMS_OFF:
+		vop_power_off(ctx);
+		break;
+	default:
+		DRM_DEBUG_KMS("unspecified mode %d\n", mode);
+		break;
+	}
+
+	ctx->dpms = mode;
+}
+
+static void rockchip_drm_crtc_prepare(struct drm_crtc *crtc)
+{
+	rockchip_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
+}
+
+static bool rockchip_drm_crtc_mode_fixup(struct drm_crtc *crtc,
+					 const struct drm_display_mode *mode,
+					 struct drm_display_mode *adjusted_mode)
+{
+	/* just do dummy now */
+
+	return true;
+}
+
+static int rockchip_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+					   struct drm_framebuffer *old_fb);
+
+static int rockchip_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 *fb)
+{
+	struct vop_context *ctx = to_vop_ctx(crtc);
+	u16 hsync_len = adjusted_mode->hsync_end - adjusted_mode->hsync_start;
+	u16 left_margin = adjusted_mode->htotal - adjusted_mode->hsync_end;
+	u16 vsync_len = adjusted_mode->vsync_end - adjusted_mode->vsync_start;
+	u16 upper_margin = adjusted_mode->vtotal - adjusted_mode->vsync_end;
+	u16 hdisplay = adjusted_mode->hdisplay;
+	u16 vdisplay = adjusted_mode->vdisplay;
+	u16 htotal = adjusted_mode->htotal;
+	u16 vtotal = adjusted_mode->vtotal;
+	struct rockchip_display_mode *priv_mode =
+					(void *)adjusted_mode->private;
+	unsigned long flags;
+	int ret;
+	uint32_t val;
+
+	/* nothing to do if we haven't set the mode yet */
+	if (adjusted_mode->htotal == 0 || adjusted_mode->vtotal == 0)
+		return -EINVAL;
+
+	if (!priv_mode) {
+		DRM_ERROR("fail to found display output type[%d]\n",
+			  priv_mode->out_type);
+		return -EINVAL;
+	}
+
+	ret = rockchip_drm_crtc_mode_set_base(crtc, x, y, fb);
+	if (ret)
+		return ret;
+
+	switch (priv_mode->out_type) {
+	case ROCKCHIP_DISPLAY_TYPE_RGB:
+	case ROCKCHIP_DISPLAY_TYPE_LVDS:
+		VOP_CTRL_SET(ctx, rgb_en, 1);
+		VOP_CTRL_SET(ctx, out_mode, ROCKCHIP_OUTFACE_P888);
+		break;
+	case ROCKCHIP_DISPLAY_TYPE_EDP:
+		VOP_CTRL_SET(ctx, edp_en, 1);
+		VOP_CTRL_SET(ctx, out_mode, ROCKCHIP_OUTFACE_AAAA);
+		break;
+	case ROCKCHIP_DISPLAY_TYPE_HDMI:
+		VOP_CTRL_SET(ctx, out_mode, ROCKCHIP_OUTFACE_AAAA);
+		VOP_CTRL_SET(ctx, hdmi_en, 1);
+		break;
+	default:
+		DRM_ERROR("unsupport out type[%d]\n", priv_mode->out_type);
+		return -EINVAL;
+	};
+
+	val = 0x8;
+	val |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0;
+	val |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? (1 << 1) : 0;
+	VOP_CTRL_SET(ctx, pin_pol, val);
+
+	VOP_CTRL_SET(ctx, htotal_pw, (htotal << 16) | hsync_len);
+	val = (hsync_len + left_margin) << 16;
+	val |= hsync_len + left_margin + hdisplay;
+	VOP_CTRL_SET(ctx, hact_st_end, val);
+	VOP_CTRL_SET(ctx, hpost_st_end, val);
+
+	VOP_CTRL_SET(ctx, vtotal_pw, (vtotal << 16) | vsync_len);
+	val = (vsync_len + upper_margin) << 16;
+	val |= vsync_len + upper_margin + vdisplay;
+	VOP_CTRL_SET(ctx, vact_st_end, val);
+	VOP_CTRL_SET(ctx, vpost_st_end, val);
+
+	spin_lock_irqsave(&ctx->irq_lock, flags);
+
+	vop_mask_write(ctx, INTR_CTRL0, DSP_LINE_NUM_MASK,
+		       DSP_LINE_NUM(vsync_len + upper_margin + vdisplay));
+
+	spin_unlock_irqrestore(&ctx->irq_lock, flags);
+
+	clk_set_rate(ctx->dclk, adjusted_mode->clock * 1000);
+
+	return 0;
+}
+
+static int rockchip_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+					   struct drm_framebuffer *old_fb)
+{
+	unsigned int crtc_w;
+	unsigned int crtc_h;
+	int ret;
+
+	crtc_w = crtc->fb->width - crtc->x;
+	crtc_h = crtc->fb->height - crtc->y;
+
+	ret = rockchip_update_plane(crtc->primary, crtc, crtc->fb, 0, 0, crtc_w,
+				    crtc_h, crtc->x, crtc->y, crtc_w, crtc_h);
+	if (ret < 0) {
+		DRM_ERROR("fail to update plane\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void rockchip_drm_crtc_commit(struct drm_crtc *crtc)
+{
+	/* just do dummy now */
+}
+
+static const struct drm_crtc_helper_funcs rockchip_crtc_helper_funcs = {
+	.dpms = rockchip_drm_crtc_dpms,
+	.prepare = rockchip_drm_crtc_prepare,
+	.mode_fixup = rockchip_drm_crtc_mode_fixup,
+	.mode_set = rockchip_drm_crtc_mode_set,
+	.mode_set_base = rockchip_drm_crtc_mode_set_base,
+	.commit = rockchip_drm_crtc_commit,
+};
+
+static int rockchip_drm_crtc_page_flip(struct drm_crtc *crtc,
+				       struct drm_framebuffer *fb,
+				       struct drm_pending_vblank_event *event,
+				       uint32_t page_flip_flags)
+{
+	struct drm_device *dev = crtc->dev;
+	struct vop_context *ctx = to_vop_ctx(crtc);
+	struct drm_framebuffer *old_fb = crtc->fb;
+	unsigned int crtc_w;
+	unsigned int crtc_h;
+	int ret;
+
+	/* when the page flip is requested, crtc's dpms should be on */
+	if (ctx->dpms > DRM_MODE_DPMS_ON) {
+		DRM_DEBUG("failed page flip request at dpms[%d].\n", ctx->dpms);
+		return 0;
+	}
+
+	ret = drm_vblank_get(dev, ctx->pipe);
+	if (ret) {
+		DRM_DEBUG("failed to acquire vblank counter\n");
+		return ret;
+	}
+
+	spin_lock_irq(&dev->event_lock);
+	if (ctx->event) {
+		spin_unlock_irq(&dev->event_lock);
+		DRM_ERROR("already pending flip!\n");
+		return -EBUSY;
+	}
+	ctx->event = event;
+	atomic_set(&ctx->wait_vsync_event, 1);
+	spin_unlock_irq(&dev->event_lock);
+
+	crtc->fb = fb;
+	crtc_w = crtc->fb->width - crtc->x;
+	crtc_h = crtc->fb->height - crtc->y;
+
+	ret = rockchip_update_plane(crtc->primary, crtc, fb, 0, 0, crtc_w,
+				    crtc_h, crtc->x, crtc->y, crtc_w, crtc_h);
+	if (ret) {
+		crtc->fb = old_fb;
+
+		spin_lock_irq(&dev->event_lock);
+		drm_vblank_put(dev, ctx->pipe);
+		atomic_set(&ctx->wait_vsync_event, 0);
+		ctx->event = NULL;
+		spin_unlock_irq(&dev->event_lock);
+	}
+
+	return ret;
+}
+
+void rockchip_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe)
+{
+	struct rockchip_drm_private *dev_priv = dev->dev_private;
+	struct drm_crtc *drm_crtc = dev_priv->crtc[pipe];
+	struct vop_context *ctx;
+	unsigned long flags;
+
+	if (!drm_crtc)
+		return;
+
+	ctx = to_vop_ctx(drm_crtc);
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+
+	if (ctx->event) {
+		drm_send_vblank_event(dev, -1, ctx->event);
+		drm_vblank_put(dev, pipe);
+		atomic_set(&ctx->wait_vsync_event, 0);
+		wake_up(&ctx->wait_vsync_queue);
+		ctx->event = NULL;
+	}
+
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+void rockchip_drm_crtc_cancel_pending_flip(struct drm_device *dev)
+{
+	int i;
+
+	for (i = 0; i < dev->num_crtcs; i++)
+		rockchip_drm_crtc_finish_pageflip(dev, i);
+}
+
+static void rockchip_drm_crtc_destroy(struct drm_crtc *crtc)
+{
+	struct vop_context *ctx = to_vop_ctx(crtc);
+	struct rockchip_drm_private *private = crtc->dev->dev_private;
+
+	private->crtc[ctx->pipe] = NULL;
+	drm_crtc_cleanup(crtc);
+}
+
+static const struct drm_crtc_funcs rockchip_crtc_funcs = {
+	.set_config = drm_crtc_helper_set_config,
+	.page_flip = rockchip_drm_crtc_page_flip,
+	.destroy = rockchip_drm_crtc_destroy,
+	.cursor_set = vop_crtc_cursor_set,
+	.cursor_move = vop_crtc_cursor_move,
+};
+
+static void rockchip_vsync_worker(struct work_struct *work)
+{
+	struct vop_context *ctx = container_of(work, struct vop_context,
+					       vsync_work);
+	struct drm_device *drm = ctx->drm_dev;
+	struct rockchip_drm_private *dev_priv = drm->dev_private;
+	struct drm_crtc *crtc = dev_priv->crtc[ctx->pipe];
+	struct rockchip_plane *rockchip_plane;
+	struct drm_plane *plane;
+	uint32_t yrgb_mst;
+
+	mutex_lock(&ctx->vsync_mutex);
+
+	ctx->vsync_work_pending = false;
+
+	list_for_each_entry(plane, &drm->mode_config.plane_list, head) {
+		rockchip_plane = to_rockchip_plane(plane);
+
+		if (rockchip_plane->ctx != ctx)
+			continue;
+		if (rockchip_plane->enabled && !rockchip_plane->pending_fb)
+			continue;
+		if (!rockchip_plane->enabled && !rockchip_plane->front_fb)
+			continue;
+		/*
+		 * make sure the yrgb_mst take effect, so that
+		 * we can unreference the old framebuffer.
+		 */
+		yrgb_mst = VOP_WIN_GET_YRGBADDR(ctx, rockchip_plane->win);
+		if (rockchip_plane->pending_yrgb_mst != yrgb_mst) {
+			/*
+			 * some plane no complete, unref at next vblank
+			 */
+			ctx->vsync_work_pending = true;
+			continue;
+		}
+
+		/*
+		 * drm_framebuffer_unreference maybe call iommu unmap,
+		 * and iommu not allow unmap buffer at irq context,
+		 * so we do drm_framebuffer_unreference at queue_work.
+		 */
+		if (rockchip_plane->front_fb)
+			drm_framebuffer_unreference(rockchip_plane->front_fb);
+
+		rockchip_plane->front_fb = rockchip_plane->pending_fb;
+		rockchip_plane->pending_fb = NULL;
+
+		/*
+		 * if primary plane flip complete, sending the event to
+		 * userspace
+		 */
+		if (&rockchip_plane->base == crtc->primary)
+			rockchip_drm_crtc_finish_pageflip(ctx->drm_dev,
+							  ctx->pipe);
+	}
+
+	mutex_unlock(&ctx->vsync_mutex);
+}
+
+static irqreturn_t rockchip_vop_isr(int irq, void *data)
+{
+	struct vop_context *ctx = data;
+	uint32_t intr0_reg;
+	unsigned long flags;
+
+	intr0_reg = vop_readl(ctx, INTR_CTRL0);
+	if (intr0_reg & LINE_FLAG_INTR) {
+		spin_lock_irqsave(&ctx->irq_lock, flags);
+		vop_writel(ctx, INTR_CTRL0, intr0_reg | LINE_FLAG_INTR_CLR);
+		spin_unlock_irqrestore(&ctx->irq_lock, flags);
+	} else {
+		return IRQ_NONE;
+	}
+
+	drm_handle_vblank(ctx->drm_dev, ctx->pipe);
+	if (ctx->vsync_work_pending)
+		queue_work(ctx->vsync_wq, &ctx->vsync_work);
+
+	return IRQ_HANDLED;
+}
+
+static int vop_create_crtc(struct vop_context *ctx)
+{
+	struct device *dev = ctx->dev;
+	struct drm_device *drm_dev = ctx->drm_dev;
+	struct drm_plane *primary, *cursor;
+	unsigned long possible_crtcs;
+	struct drm_crtc *crtc;
+	int ret;
+	int nr;
+
+	ctx->win_mask = 0;
+	crtc = &ctx->crtc;
+
+	ret = rockchip_drm_add_crtc(drm_dev, crtc, dev->of_node);
+	if (ret < 0)
+		return ret;
+	ctx->pipe = ret;
+
+	possible_crtcs = (1 << ctx->pipe);
+
+	primary = rockchip_plane_init(ctx, possible_crtcs,
+				      DRM_PLANE_TYPE_PRIMARY);
+	if (!primary) {
+		DRM_ERROR("fail to init primary plane\n");
+		return -EINVAL;
+	}
+
+	for (nr = 1; nr < ROCKCHIP_MAX_PLANE; nr++) {
+		if (nr == VOP_DEFAULT_CURSOR) {
+			cursor = rockchip_plane_init(ctx, possible_crtcs,
+						     DRM_PLANE_TYPE_CURSOR);
+			if (!cursor) {
+				DRM_ERROR("fail to init cursor plane\n");
+				return -EINVAL;
+			}
+		} else {
+			struct drm_plane *plane;
+
+			plane = rockchip_plane_init(ctx, possible_crtcs,
+						    DRM_PLANE_TYPE_OVERLAY);
+			if (!plane) {
+				DRM_ERROR("fail to init overlay plane\n");
+				return -EINVAL;
+			}
+		}
+	}
+
+	drm_crtc_init_with_planes(drm_dev, crtc, primary, cursor,
+				  &rockchip_crtc_funcs);
+	drm_crtc_helper_add(crtc, &rockchip_crtc_helper_funcs);
+
+	return 0;
+}
+
+static int rockchip_vop_initial(struct vop_context *ctx)
+{
+	struct vop_driver_data *vop_data = ctx->data;
+	const struct vop_reg_data *init_table = vop_data->init_table;
+	struct reset_control *rst;
+	int i, ret;
+
+	ctx->hclk = devm_clk_get(ctx->dev, "hclk_vop");
+	if (IS_ERR(ctx->hclk)) {
+		dev_err(ctx->dev, "failed to get hclk source\n");
+		return PTR_ERR(ctx->hclk);
+	}
+	ctx->aclk = devm_clk_get(ctx->dev, "aclk_vop");
+	if (IS_ERR(ctx->aclk)) {
+		dev_err(ctx->dev, "failed to get aclk source\n");
+		return PTR_ERR(ctx->aclk);
+	}
+	ctx->dclk = devm_clk_get(ctx->dev, "dclk_vop");
+	if (IS_ERR(ctx->dclk)) {
+		dev_err(ctx->dev, "failed to get dclk source\n");
+		return PTR_ERR(ctx->dclk);
+	}
+
+	ret = vop_clk_enable(ctx);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * do hclk_reset, reset all vop registers.
+	 */
+	rst = devm_reset_control_get(ctx->dev, "ahb");
+	if (IS_ERR(rst)) {
+		dev_err(ctx->dev, "failed to get ahb reset\n");
+		return PTR_ERR(rst);
+	}
+	reset_control_assert(rst);
+	usleep_range(10, 20);
+	reset_control_deassert(rst);
+
+	memcpy(ctx->regsbak, ctx->regs, ctx->len);
+
+	for (i = 0; i < vop_data->table_size; i++)
+		vop_writel(ctx, init_table[i].offset, init_table[i].value);
+
+	for (i = 0; i < VOP_MAX_WIN_SUPPORT; i++)
+		VOP_WIN_SET(ctx, vop_data->win[i], enable, 0);
+
+	vop_cfg_done(ctx);
+
+	/*
+	 * do dclk_reset, let all win config take affect, and then we can enable
+	 * iommu safe.
+	 */
+	rst = devm_reset_control_get(ctx->dev, "dclk");
+	if (IS_ERR(rst)) {
+		dev_err(ctx->dev, "failed to get dclk reset\n");
+		return PTR_ERR(rst);
+	}
+	reset_control_assert(rst);
+	usleep_range(10, 20);
+	reset_control_deassert(rst);
+
+	ctx->dpms = DRM_MODE_DPMS_ON;
+
+	return 0;
+}
+
+static int vop_bind(struct device *dev, struct device *master, void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct vop_driver_data *vop_data = vop_get_driver_data(dev);
+	struct drm_device *drm_dev = data;
+	struct vop_context *ctx;
+	struct resource *res;
+	int ret;
+
+	if (!vop_data)
+		return -ENODEV;
+
+	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	ctx->dev = dev;
+	ctx->data = vop_data;
+	ctx->drm_dev = drm_dev;
+	dev_set_drvdata(dev, ctx);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	ctx->len = resource_size(res);
+	ctx->regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(ctx->regs))
+		return PTR_ERR(ctx->regs);
+
+	ctx->regsbak = devm_kzalloc(dev, ctx->len, GFP_KERNEL);
+	if (!ctx->regsbak)
+		return -ENOMEM;
+
+	ret = rockchip_vop_initial(ctx);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "cannot initial vop dev - err %d\n", ret);
+		return ret;
+	}
+
+	ctx->irq = platform_get_irq(pdev, 0);
+	if (ctx->irq < 0) {
+		dev_err(dev, "cannot find irq for vop\n");
+		return ctx->irq;
+	}
+
+	spin_lock_init(&ctx->reg_lock);
+	spin_lock_init(&ctx->irq_lock);
+
+	init_waitqueue_head(&ctx->wait_vsync_queue);
+	atomic_set(&ctx->wait_vsync_event, 0);
+
+	ret = vop_iommu_init(ctx);
+	if (ret) {
+		DRM_ERROR("Failed to setup iommu, %d\n", ret);
+		return ret;
+	}
+
+	ctx->vsync_wq = create_singlethread_workqueue("vsync");
+	if (!ctx->vsync_wq) {
+		dev_err(dev, "failed to create workqueue\n");
+		return -EINVAL;
+	}
+	INIT_WORK(&ctx->vsync_work, rockchip_vsync_worker);
+
+	mutex_init(&ctx->vsync_mutex);
+	pm_runtime_enable(&pdev->dev);
+
+	ret = devm_request_irq(dev, ctx->irq, rockchip_vop_isr,
+			       IRQF_SHARED, dev_name(dev), ctx);
+	if (ret) {
+		dev_err(dev, "cannot requeset irq%d - err %d\n", ctx->irq, ret);
+		return ret;
+	}
+
+	return vop_create_crtc(ctx);
+}
+
+static void vop_unbind(struct device *dev, struct device *master,
+		       void *data)
+{
+	struct drm_device *drm_dev = data;
+	struct vop_context *ctx = dev_get_drvdata(dev);
+	struct drm_crtc *crtc = &ctx->crtc;
+
+	drm_crtc_cleanup(crtc);
+	pm_runtime_disable(dev);
+	rockchip_drm_remove_crtc(drm_dev, ctx->pipe);
+
+	vop_iommu_fini(ctx);
+}
+
+static const struct component_ops vop_component_ops = {
+	.bind = vop_bind,
+	.unbind = vop_unbind,
+};
+
+static int vop_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct vop_context *ctx;
+
+	if (!dev->of_node) {
+		dev_err(dev, "can't find vop devices\n");
+		return -ENODEV;
+	}
+
+	platform_set_drvdata(pdev, ctx);
+
+	return component_add(dev, &vop_component_ops);
+}
+
+static int vop_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &vop_component_ops);
+
+	return 0;
+}
+
+struct platform_driver rockchip_vop_platform_driver = {
+	.probe = vop_probe,
+	.remove = vop_remove,
+	.driver = {
+		.name = "rockchip-vop",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(vop_driver_dt_match),
+	},
+};
+
+module_platform_driver(rockchip_vop_platform_driver);
+
+MODULE_AUTHOR("Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>");
+MODULE_DESCRIPTION("ROCKCHIP VOP Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
new file mode 100644
index 0000000..2343760
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _ROCKCHIP_DRM_VOP_H
+#define _ROCKCHIP_DRM_VOP_H
+
+/* register definition */
+#define REG_CFG_DONE			0x0000
+#define VERSION_INFO			0x0004
+#define SYS_CTRL			0x0008
+#define SYS_CTRL1			0x000c
+#define DSP_CTRL0			0x0010
+#define DSP_CTRL1			0x0014
+#define DSP_BG				0x0018
+#define MCU_CTRL			0x001c
+#define INTR_CTRL0			0x0020
+#define INTR_CTRL1			0x0024
+#define WIN0_CTRL0			0x0030
+#define WIN0_CTRL1			0x0034
+#define WIN0_COLOR_KEY			0x0038
+#define WIN0_VIR			0x003c
+#define WIN0_YRGB_MST			0x0040
+#define WIN0_CBR_MST			0x0044
+#define WIN0_ACT_INFO			0x0048
+#define WIN0_DSP_INFO			0x004c
+#define WIN0_DSP_ST			0x0050
+#define WIN0_SCL_FACTOR_YRGB		0x0054
+#define WIN0_SCL_FACTOR_CBR		0x0058
+#define WIN0_SCL_OFFSET			0x005c
+#define WIN0_SRC_ALPHA_CTRL		0x0060
+#define WIN0_DST_ALPHA_CTRL		0x0064
+#define WIN0_FADING_CTRL		0x0068
+/* win1 register */
+#define WIN1_CTRL0			0x0070
+#define WIN1_CTRL1			0x0074
+#define WIN1_COLOR_KEY			0x0078
+#define WIN1_VIR			0x007c
+#define WIN1_YRGB_MST			0x0080
+#define WIN1_CBR_MST			0x0084
+#define WIN1_ACT_INFO			0x0088
+#define WIN1_DSP_INFO			0x008c
+#define WIN1_DSP_ST			0x0090
+#define WIN1_SCL_FACTOR_YRGB		0x0094
+#define WIN1_SCL_FACTOR_CBR		0x0098
+#define WIN1_SCL_OFFSET			0x009c
+#define WIN1_SRC_ALPHA_CTRL		0x00a0
+#define WIN1_DST_ALPHA_CTRL		0x00a4
+#define WIN1_FADING_CTRL		0x00a8
+/* win2 register */
+#define WIN2_CTRL0			0x00b0
+#define WIN2_CTRL1			0x00b4
+#define WIN2_VIR0_1			0x00b8
+#define WIN2_VIR2_3			0x00bc
+#define WIN2_MST0			0x00c0
+#define WIN2_DSP_INFO0			0x00c4
+#define WIN2_DSP_ST0			0x00c8
+#define WIN2_COLOR_KEY			0x00cc
+#define WIN2_MST1			0x00d0
+#define WIN2_DSP_INFO1			0x00d4
+#define WIN2_DSP_ST1			0x00d8
+#define WIN2_SRC_ALPHA_CTRL		0x00dc
+#define WIN2_MST2			0x00e0
+#define WIN2_DSP_INFO2			0x00e4
+#define WIN2_DSP_ST2			0x00e8
+#define WIN2_DST_ALPHA_CTRL		0x00ec
+#define WIN2_MST3			0x00f0
+#define WIN2_DSP_INFO3			0x00f4
+#define WIN2_DSP_ST3			0x00f8
+#define WIN2_FADING_CTRL		0x00fc
+/* win3 register */
+#define WIN3_CTRL0			0x0100
+#define WIN3_CTRL1			0x0104
+#define WIN3_VIR0_1			0x0108
+#define WIN3_VIR2_3			0x010c
+#define WIN3_MST0			0x0110
+#define WIN3_DSP_INFO0			0x0114
+#define WIN3_DSP_ST0			0x0118
+#define WIN3_COLOR_KEY			0x011c
+#define WIN3_MST1			0x0120
+#define WIN3_DSP_INFO1			0x0124
+#define WIN3_DSP_ST1			0x0128
+#define WIN3_SRC_ALPHA_CTRL		0x012c
+#define WIN3_MST2			0x0130
+#define WIN3_DSP_INFO2			0x0134
+#define WIN3_DSP_ST2			0x0138
+#define WIN3_DST_ALPHA_CTRL		0x013c
+#define WIN3_MST3			0x0140
+#define WIN3_DSP_INFO3			0x0144
+#define WIN3_DSP_ST3			0x0148
+#define WIN3_FADING_CTRL		0x014c
+/* hwc register */
+#define HWC_CTRL0			0x0150
+#define HWC_CTRL1			0x0154
+#define HWC_MST				0x0158
+#define HWC_DSP_ST			0x015c
+#define HWC_SRC_ALPHA_CTRL		0x0160
+#define HWC_DST_ALPHA_CTRL		0x0164
+#define HWC_FADING_CTRL			0x0168
+/* post process register */
+#define POST_DSP_HACT_INFO		0x0170
+#define POST_DSP_VACT_INFO		0x0174
+#define POST_SCL_FACTOR_YRGB		0x0178
+#define POST_SCL_CTRL			0x0180
+#define POST_DSP_VACT_INFO_F1		0x0184
+#define DSP_HTOTAL_HS_END		0x0188
+#define DSP_HACT_ST_END			0x018c
+#define DSP_VTOTAL_VS_END		0x0190
+#define DSP_VACT_ST_END			0x0194
+#define DSP_VS_ST_END_F1		0x0198
+#define DSP_VACT_ST_END_F1		0x019c
+/* register definition end */
+
+/* interrupt define */
+#define DSP_HOLD_VALID_INTR		(1 << 0)
+#define FS_INTR				(1 << 1)
+#define LINE_FLAG_INTR			(1 << 2)
+#define BUS_ERROR_INTR			(1 << 3)
+
+#define DSP_HOLD_VALID_INTR_EN(x)	((x) << 4)
+#define FS_INTR_EN(x)			((x) << 5)
+#define LINE_FLAG_INTR_EN(x)		((x) << 6)
+#define BUS_ERROR_INTR_EN(x)		((x) << 7)
+#define DSP_HOLD_VALID_INTR_MASK	(1 << 4)
+#define FS_INTR_EN_MASK			(1 << 5)
+#define LINE_FLAG_INTR_MASK		(1 << 6)
+#define BUS_ERROR_INTR_MASK		(1 << 7)
+
+#define DSP_HOLD_VALID_INTR_CLR		(1 << 8)
+#define FS_INTR_EN_CLR			(1 << 9)
+#define LINE_FLAG_INTR_CLR		(1 << 10)
+#define BUS_ERROR_INTR_CLR		(1 << 11)
+#define DSP_LINE_NUM(x)			(((x) & 0x1fff) << 12)
+#define DSP_LINE_NUM_MASK		(0x1fff << 12)
+
+/* src alpha ctrl define */
+#define SRC_FADING_VALUE(x)		(((x) & 0xff) << 24)
+#define SRC_GLOBAL_ALPHA(x)		(((x) & 0xff) << 16)
+#define SRC_FACTOR_M0(x)		(((x) & 0x7) << 6)
+#define SRC_ALPHA_CAL_M0(x)		(((x) & 0x1) << 5)
+#define SRC_BLEND_M0(x)			(((x) & 0x3) << 3)
+#define SRC_ALPHA_M0(x)			(((x) & 0x1) << 2)
+#define SRC_COLOR_M0(x)			(((x) & 0x1) << 1)
+#define SRC_ALPHA_EN(x)			(((x) & 0x1) << 0)
+/* dst alpha ctrl define */
+#define DST_FACTOR_M0(x)		(((x) & 0x7) << 6)
+
+enum alpha_mode {
+	ALPHA_STRAIGHT,
+	ALPHA_INVERSE,
+};
+
+enum global_blend_mode {
+	ALPHA_GLOBAL,
+	ALPHA_PER_PIX,
+	ALPHA_PER_PIX_GLOBAL,
+};
+
+enum alpha_cal_mode {
+	ALPHA_SATURATION,
+	ALPHA_NO_SATURATION,
+};
+
+enum color_mode {
+	ALPHA_SRC_PRE_MUL,
+	ALPHA_SRC_NO_PRE_MUL,
+};
+
+enum factor_mode {
+	ALPHA_ZERO,
+	ALPHA_ONE,
+	ALPHA_SRC,
+	ALPHA_SRC_INVERSE,
+	ALPHA_SRC_GLOBAL,
+};
+
+#endif /* _ROCKCHIP_DRM_VOP_H */
diff --git a/include/uapi/drm/rockchip_drm.h b/include/uapi/drm/rockchip_drm.h
new file mode 100644
index 0000000..8f8e60e
--- /dev/null
+++ b/include/uapi/drm/rockchip_drm.h
@@ -0,0 +1,97 @@
+/*
+ *
+ * Copyright (c) Fuzhou Rockchip Electronics Co.Ltd
+ * Authors:
+ *       Mark Yao <yzq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *
+ * base on exynos_drm.h
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#ifndef _UAPI_ROCKCHIP_DRM_H
+#define _UAPI_ROCKCHIP_DRM_H
+
+#include <drm/drm.h>
+
+/**
+ * User-desired buffer creation information structure.
+ *
+ * @size: user-desired memory allocation size.
+ * @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_rockchip_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_rockchip_gem_map_off {
+	uint32_t handle;
+	uint32_t pad;
+	uint64_t offset;
+};
+
+/**
+ * A structure for mapping buffer.
+ *
+ * @handle: a handle to gem object created.
+ * @pad: just padding to be 64-bit aligned.
+ * @size: memory size to be mapped.
+ * @mapped: having user virtual address mmaped.
+ *      - this variable would be filled by rockchip gem module
+ *      of kernel side with user virtual address which is allocated
+ *      by do_mmap().
+ */
+struct drm_rockchip_gem_mmap {
+	uint32_t handle;
+	uint32_t pad;
+	uint64_t size;
+	uint64_t mapped;
+};
+
+/**
+ * A structure to gem information.
+ *
+ * @handle: a handle to gem object created.
+ * @flags: flag value including memory type and cache attribute and
+ *      this value would be set by driver.
+ * @size: size to memory region allocated by gem and this size would
+ *      be set by driver.
+ */
+struct drm_rockchip_gem_info {
+	uint32_t handle;
+	uint32_t flags;
+	uint64_t size;
+};
+
+#define DRM_ROCKCHIP_GEM_CREATE		0x00
+#define DRM_ROCKCHIP_GEM_MAP_OFFSET	0x01
+#define DRM_ROCKCHIP_GEM_MMAP		0x02
+#define DRM_ROCKCHIP_GEM_GET		0x04
+
+#define DRM_IOCTL_ROCKCHIP_GEM_CREATE	DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_ROCKCHIP_GEM_CREATE, struct drm_rockchip_gem_create)
+
+#define DRM_IOCTL_ROCKCHIP_GEM_MAP_OFFSET	DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_ROCKCHIP_GEM_MAP_OFFSET, struct drm_rockchip_gem_map_off)
+
+#define DRM_IOCTL_ROCKCHIP_GEM_MMAP	DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_ROCKCHIP_GEM_MMAP, struct drm_rockchip_gem_mmap)
+
+#define DRM_IOCTL_ROCKCHIP_GEM_GET	DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_ROCKCHIP_GEM_GET, struct drm_rockchip_gem_info)
+#endif /* _UAPI_ROCKCHIP_DRM_H */
-- 
1.7.9.5

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

* [PATCH v2 2/5] dt-bindings: video: Add for rockchip display subsytem
@ 2014-09-18  9:37     ` Mark yao
  0 siblings, 0 replies; 38+ messages in thread
From: Mark yao @ 2014-09-18  9:37 UTC (permalink / raw)
  To: heiko, Boris BREZILLON, David Airlie, Rob Clark, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Randy Dunlap,
	Grant Likely, Greg Kroah-Hartman, John Stultz, Rom Lemarchand
  Cc: devicetree, linux-doc, linux-kernel, dri-devel, linux-api,
	linux-rockchip, dianders, marcheu, dbehr, olof, djkurtz, xjq,
	kfx, cym, cf, zyw, xxm, huangtao, kever.yang, yxj, wxt, xw,
	Mark yao

This add a display subsystem comprise the all display interface nodes.

Signed-off-by: Mark Yao <mark.yao@rock-chips.com>

---
changes in v2:
- add DRM master device node to list all display nodes that comprise
  the graphics subsystem.

 .../devicetree/bindings/video/rockchip-drm.txt     |   19 +++++++++++++++++++
 1 file changed, 19 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/video/rockchip-drm.txt

diff --git a/Documentation/devicetree/bindings/video/rockchip-drm.txt b/Documentation/devicetree/bindings/video/rockchip-drm.txt
new file mode 100644
index 0000000..7fff582
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/rockchip-drm.txt
@@ -0,0 +1,19 @@
+Rockchip DRM master device
+================================
+
+The Rockchip DRM master device is a virtual device needed to list all
+vop devices or other display interface nodes that comprise the
+graphics subsystem.
+
+Required properties:
+- compatible: Should be "rockchip,display-subsystem"
+- ports: Should contain a list of phandles pointing to display interface port
+  of vop devices. vop definitions as defined in
+  Documentation/devicetree/bindings/video/rockchip-vop.txt
+
+example:
+
+display-subsystem {
+	compatible = "rockchip,display-subsystem";
+	ports = <&vopl_out>, <&vopb_out>;
+};
-- 
1.7.9.5



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

* [PATCH v2 2/5] dt-bindings: video: Add for rockchip display subsytem
@ 2014-09-18  9:37     ` Mark yao
  0 siblings, 0 replies; 38+ messages in thread
From: Mark yao @ 2014-09-18  9:37 UTC (permalink / raw)
  To: heiko-4mtYJXux2i+zQB+pC5nmwQ, Boris BREZILLON, David Airlie,
	Rob Clark, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, Randy Dunlap, Grant Likely, Greg Kroah-Hartman,
	John Stultz, Rom Lemarchand
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-api-u79uwXL29TY76Z2rM5mHXA,
	linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	dianders-F7+t8E8rja9g9hUCZPvPmw, marcheu-F7+t8E8rja9g9hUCZPvPmw,
	dbehr-F7+t8E8rja9g9hUCZPvPmw, olof-nZhT3qVonbNeoWH0uzbU5w,
	djkurtz-F7+t8E8rja9g9hUCZPvPmw, xjq-TNX95d0MmH7DzftRWevZcw,
	kfx-TNX95d0MmH7DzftRWevZcw, cym-TNX95d0MmH7DzftRWevZcw,
	cf-TNX95d0MmH7DzftRWevZcw, zyw-TNX95d0MmH7DzftRWevZcw,
	xxm-TNX95d0MmH7DzftRWevZcw, huangtao-TNX95d0MmH7DzftRWevZcw,
	kever.yang-TNX95d0MmH7DzftRWevZcw, yxj-TNX95d0MmH7DzftRWevZcw,
	wxt-TNX95d0MmH7DzftRWevZcw, xw-TNX95d0MmH7DzftRWevZcw, Mark yao

This add a display subsystem comprise the all display interface nodes.

Signed-off-by: Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>

---
changes in v2:
- add DRM master device node to list all display nodes that comprise
  the graphics subsystem.

 .../devicetree/bindings/video/rockchip-drm.txt     |   19 +++++++++++++++++++
 1 file changed, 19 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/video/rockchip-drm.txt

diff --git a/Documentation/devicetree/bindings/video/rockchip-drm.txt b/Documentation/devicetree/bindings/video/rockchip-drm.txt
new file mode 100644
index 0000000..7fff582
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/rockchip-drm.txt
@@ -0,0 +1,19 @@
+Rockchip DRM master device
+================================
+
+The Rockchip DRM master device is a virtual device needed to list all
+vop devices or other display interface nodes that comprise the
+graphics subsystem.
+
+Required properties:
+- compatible: Should be "rockchip,display-subsystem"
+- ports: Should contain a list of phandles pointing to display interface port
+  of vop devices. vop definitions as defined in
+  Documentation/devicetree/bindings/video/rockchip-vop.txt
+
+example:
+
+display-subsystem {
+	compatible = "rockchip,display-subsystem";
+	ports = <&vopl_out>, <&vopb_out>;
+};
-- 
1.7.9.5

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

* [PATCH v2 3/5] dt-bindings: video: Add documentation for rockchip vop
  2014-09-18  9:34   ` Mark yao
                     ` (2 preceding siblings ...)
  (?)
@ 2014-09-18  9:39   ` Mark yao
  -1 siblings, 0 replies; 38+ messages in thread
From: Mark yao @ 2014-09-18  9:39 UTC (permalink / raw)
  To: heiko, Boris BREZILLON, David Airlie, Rob Clark, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Randy Dunlap,
	Grant Likely, Greg Kroah-Hartman, John Stultz, Rom Lemarchand
  Cc: devicetree, linux-doc, linux-kernel, dri-devel, linux-api,
	linux-rockchip, dianders, marcheu, dbehr, olof, djkurtz, xjq,
	kfx, cym, cf, zyw, xxm, huangtao, kever.yang, yxj, wxt, xw,
	Mark yao

This adds binding documentation for Rockchip SoC VOP driver.

Signed-off-by: Mark Yao <mark.yao@rock-chips.com>
---
changes in v2:
- rename "lcdc" to "vop"
- add vop reset
- add iommu node
- add port for display-subsystem

 .../devicetree/bindings/video/rockchip-vop.txt     |   58 ++++++++++++++++++++
 1 file changed, 58 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/video/rockchip-vop.txt

diff --git a/Documentation/devicetree/bindings/video/rockchip-vop.txt b/Documentation/devicetree/bindings/video/rockchip-vop.txt
new file mode 100644
index 0000000..d15351f
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/rockchip-vop.txt
@@ -0,0 +1,58 @@
+device-tree bindings for rockchip soc display controller (vop)
+
+VOP (Visual Output Processor) is the Display Controller for the Rockchip
+series of SoCs which transfers the image data from a video memory
+buffer to an external LCD interface.
+
+Required properties:
+- compatible: value should be one of the following
+		"rockchip,rk3288-vop";
+
+- interrupts: should contain a list of all VOP IP block interrupts in the
+		 order: VSYNC, LCD_SYSTEM. The interrupt specifier
+		 format depends on the interrupt controller used.
+
+- clocks: must include clock specifiers corresponding to entries in the
+		clock-names property.
+
+- clock-names: Must contain
+		aclk_vop: for ddr buffer transfer.
+		hclk_vop: for ahb bus to R/W the phy regs.
+		dclk_vop: pixel clock.
+
+- resets: Must contain an entry for each entry in reset-names.
+  See ../reset/reset.txt for details.
+- reset-names: Must include the following entries:
+  - axi
+  - ahb
+  - dclk
+
+- iommus: required a iommu node
+
+- port: A port node with endpoint definitions as defined in
+  Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+Example:
+SoC specific DT entry:
+	vopb: vopb@ff930000 {
+		compatible = "rockchip,rk3288-vop";
+		reg = <0xff930000 0x19c>;
+		interrupts = <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru ACLK_VOP0>, <&cru DCLK_VOP0>, <&cru HCLK_VOP0>;
+		clock-names = "aclk_vop", "dclk_vop", "hclk_vop";
+		resets = <&cru SRST_LCDC1_AXI>, <&cru SRST_LCDC1_AHB>, <&cru SRST_LCDC1_DCLK>;
+		reset-names = "axi", "ahb", "dclk";
+		iommus = <&vopb_mmu>;
+		vopb_out: port {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			vopb_out_edp: endpoint@0 {
+				reg = <0>;
+				remote-endpoint=<&edp_in_vopb>;
+			};
+			vopb_out_hdmi: endpoint@1 {
+				reg = <1>;
+				remote-endpoint=<&hdmi_in_vopb>;
+			};
+		};
+	};
-- 
1.7.9.5



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

* [PATCH v2 4/5] dt-bindings: video: Add documentation for rockchip edp
  2014-09-18  9:34   ` Mark yao
                     ` (3 preceding siblings ...)
  (?)
@ 2014-09-18  9:41   ` Mark yao
  -1 siblings, 0 replies; 38+ messages in thread
From: Mark yao @ 2014-09-18  9:41 UTC (permalink / raw)
  To: heiko, Boris BREZILLON, David Airlie, Rob Clark, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Randy Dunlap,
	Grant Likely, Greg Kroah-Hartman, John Stultz, Rom Lemarchand
  Cc: devicetree, linux-doc, linux-kernel, dri-devel, linux-api,
	linux-rockchip, dianders, marcheu, dbehr, olof, djkurtz, xjq,
	kfx, cym, cf, zyw, xxm, huangtao, kever.yang, yxj, wxt, xw,
	Mark yao, Jeff Chen

Add binding documentation for Rockchip SoC EDP driver.

Signed-off-by: Jeff Chen <jeff.chen@rock-chips.com>
Signed-off-by: Mark Yao <mark.yao@rock-chips.com>
---
changes in v2:
- add edp reset
- add panel node
- add port for display-subsystem

 .../devicetree/bindings/video/rockchip-edp.txt     |   50 ++++++++++++++++++++
 1 file changed, 50 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/video/rockchip-edp.txt

diff --git a/Documentation/devicetree/bindings/video/rockchip-edp.txt b/Documentation/devicetree/bindings/video/rockchip-edp.txt
new file mode 100644
index 0000000..515e806
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/rockchip-edp.txt
@@ -0,0 +1,50 @@
+Rockchip RK3288 EDP interface
+================================
+
+Required properties:
+- compatible: "rockchip,rk3288-edp";
+
+- reg: physical base address of the controller and length
+- clocks: from common clock binding: handle to dp clock.
+	of memory mapped region.
+- clock-names: from common clock binding:
+	Required elements: "clk_edp"
+			"clk_edp_24m"
+			"pclk_edp"
+- resets: Must contain an entry for each entry in reset-names.
+	See ../reset/reset.txt for details.
+- reset-names: Must include the name "edp"
+
+- rockchip,grf: this soc should set GRF regs, so need get grf here.
+- rockchip,panel: required a simple panel node as described by
+	Documentation/devicetree/bindings/panel/simple-panel.txt
+
+- ports: contain a port node with endpoint definitions as defined in
+  Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+Example:
+	edp: edp@ff970000 {
+		compatible = "rockchip,rk3288-edp";
+		reg = <0xff970000 0x4000>;
+		interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru SCLK_EDP>, <&cru SCLK_EDP_24M>, <&cru PCLK_EDP_CTRL>;
+		clock-names = "clk_edp", "clk_edp_24m", "pclk_edp";
+		rockchip,grf = <&grf>;
+		resets = <&cru 111>;
+		reset-names = "edp";
+		rockchip,panel = <&panel>;
+		ports {
+			edp_in: port {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				edp_in_vopb: endpoint@0 {
+					reg = <0>;
+					remote-endpoint = <&vopb_out_edp>;
+				};
+				edp_in_vopl: endpoint@1 {
+					reg = <1>;
+					remote-endpoint = <&vopl_out_edp>;
+				};
+			};
+		};
+	};
-- 
1.7.9.5



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

* [PATCH v2 5/5] drm/rockchip: Add support for Rockchip Soc EDP
@ 2014-09-18  9:42     ` Mark yao
  0 siblings, 0 replies; 38+ messages in thread
From: Mark yao @ 2014-09-18  9:42 UTC (permalink / raw)
  To: heiko, Boris BREZILLON, David Airlie, Rob Clark, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Randy Dunlap,
	Grant Likely, Greg Kroah-Hartman, John Stultz, Rom Lemarchand
  Cc: devicetree, linux-doc, linux-kernel, dri-devel, linux-api,
	linux-rockchip, dianders, marcheu, dbehr, olof, djkurtz, xjq,
	kfx, cym, cf, zyw, xxm, huangtao, kever.yang, yxj, wxt, xw,
	Mark yao, Jeff Chen

This adds support for Rockchip soc edp found on rk3288

Signed-off-by: Mark Yao <mark.yao@rock-chips.com>
Signed-off-by: Jeff Chen <jeff.chen@rock-chips.com>
---
change in v2:
- fix code sytle
- use some define from drm_dp_helper.h
- use panel-simple driver for primary display.
- remove unnecessary clock clk_24m_parent.

 drivers/gpu/drm/rockchip/Kconfig             |    9 +
 drivers/gpu/drm/rockchip/Makefile            |    2 +
 drivers/gpu/drm/rockchip/rockchip_edp_core.c |  853 ++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_edp_core.h |  309 +++++++
 drivers/gpu/drm/rockchip/rockchip_edp_reg.c  | 1202 ++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_edp_reg.h  |  345 ++++++++
 6 files changed, 2720 insertions(+)
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_edp_core.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_edp_core.h
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_edp_reg.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_edp_reg.h

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 7146c80..04b1f8c 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -17,3 +17,12 @@ config DRM_ROCKCHIP
 	  management to userspace. This driver does not provides
 	  2D or 3D acceleration; acceleration is performed by other
 	  IP found on the SoC.
+
+config ROCKCHIP_EDP
+	bool "Rockchip edp support"
+	depends on DRM_ROCKCHIP
+	help
+	  Choose this option if you have a Rockchip eDP.
+	  Rockchip rk3288 SoC has eDP TX Controller can be used.
+	  If you have an Embedded DisplayPort Panel, say Y to enable its
+	  driver.
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index 6e6d468..a0fc3a1 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -7,4 +7,6 @@ ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/rockchip
 rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \
 		rockchip_drm_gem.o rockchip_drm_vop.o
 
+rockchipdrm-$(CONFIG_ROCKCHIP_EDP) += rockchip_edp_core.o rockchip_edp_reg.o
+
 obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o
diff --git a/drivers/gpu/drm/rockchip/rockchip_edp_core.c b/drivers/gpu/drm/rockchip/rockchip_edp_core.c
new file mode 100644
index 0000000..4d9caee
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_edp_core.c
@@ -0,0 +1,853 @@
+/*
+* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+* Author:
+*      Andy yan <andy.yan@rock-chips.com>
+*      Jeff chen <jeff.chen@rock-chips.com>
+*
+* based on exynos_dp_core.c
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*/
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_of.h>
+
+#include <linux/component.h>
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#include <video/of_videomode.h>
+#include <video/videomode.h>
+
+#include "rockchip_edp_core.h"
+
+#define connector_to_edp(c) \
+		container_of(c, struct rockchip_edp_device, connector)
+
+#define encoder_to_edp(c) \
+		container_of(c, struct rockchip_edp_device, encoder)
+
+static struct rockchip_edp_soc_data soc_data[2] = {
+	/* rk3288 */
+	{.grf_soc_con6 = 0x025c,
+	 .grf_soc_con12 = 0x0274},
+	/* no edp switching needed */
+	{.grf_soc_con6 = -1,
+	 .grf_soc_con12 = -1},
+};
+
+static const struct of_device_id rockchip_edp_dt_ids[] = {
+	{.compatible = "rockchip,rk3288-edp",
+	 .data = (void *)&soc_data[0] },
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, rockchip_edp_dt_ids);
+
+static int rockchip_edp_clk_enable(struct rockchip_edp_device *edp)
+{
+	int ret = 0;
+
+	if (!edp->clk_on) {
+		ret = clk_prepare_enable(edp->pclk);
+		if (ret < 0) {
+			dev_err(edp->dev, "cannot enable edp pclk %d\n", ret);
+			goto err_pclk;
+		}
+
+		ret = clk_prepare_enable(edp->clk_edp);
+		if (ret < 0) {
+			dev_err(edp->dev, "cannot enable clk_edp %d\n", ret);
+			goto err_clk_edp;
+		}
+
+		ret = clk_set_rate(edp->clk_24m, 24000000);
+		if (ret < 0) {
+			dev_err(edp->dev, "cannot set edp clk_24m %d\n",
+				ret);
+			goto err_clk_24m;
+		}
+
+		ret = clk_prepare_enable(edp->clk_24m);
+		if (ret < 0) {
+			dev_err(edp->dev, "cannot enable edp clk_24m %d\n",
+				ret);
+			goto err_clk_24m;
+		}
+
+		edp->clk_on = true;
+	}
+
+	return 0;
+
+err_clk_24m:
+	clk_disable_unprepare(edp->clk_edp);
+err_clk_edp:
+	clk_disable_unprepare(edp->pclk);
+err_pclk:
+	edp->clk_on = false;
+
+	return ret;
+}
+
+static int rockchip_edp_clk_disable(struct rockchip_edp_device *edp)
+{
+	if (edp->clk_on) {
+		clk_disable_unprepare(edp->pclk);
+		clk_disable_unprepare(edp->clk_edp);
+		clk_disable_unprepare(edp->clk_24m);
+		edp->clk_on = false;
+	}
+
+	return 0;
+}
+
+static int rockchip_edp_pre_init(struct rockchip_edp_device *edp)
+{
+	u32 val;
+	int ret;
+
+	val = GRF_EDP_REF_CLK_SEL_INTER | (GRF_EDP_REF_CLK_SEL_INTER << 16);
+	ret = regmap_write(edp->grf, edp->soc_data->grf_soc_con12, val);
+	if (ret != 0) {
+		dev_err(edp->dev, "Could not write to GRF: %d\n", ret);
+		return ret;
+	}
+
+	reset_control_assert(edp->rst);
+	usleep_range(10, 20);
+	reset_control_deassert(edp->rst);
+
+	return 0;
+}
+
+static int rockchip_edp_init_edp(struct rockchip_edp_device *edp)
+{
+	rockchip_edp_reset(edp);
+	rockchip_edp_init_refclk(edp);
+	rockchip_edp_init_interrupt(edp);
+	rockchip_edp_enable_sw_function(edp);
+	rockchip_edp_init_analog_func(edp);
+	rockchip_edp_init_hpd(edp);
+	rockchip_edp_init_aux(edp);
+
+	return 0;
+}
+
+static int rockchip_edp_get_max_rx_bandwidth(
+					struct rockchip_edp_device *edp,
+					u8 *bandwidth)
+{
+	u8 data;
+	int retval;
+
+	/*
+	 * For DP rev.1.1, Maximum link rate of Main Link lanes
+	 * 0x06 = 1.62 Gbps, 0x0a = 2.7 Gbps
+	 */
+	retval = rockchip_edp_read_byte_from_dpcd(
+			edp, DP_MAX_LINK_RATE, &data);
+	if (retval < 0)
+		*bandwidth = 0;
+	else
+		*bandwidth = data;
+
+	return retval;
+}
+
+static int rockchip_edp_get_max_rx_lane_count(struct rockchip_edp_device *edp,
+					      u8 *lane_count)
+{
+	u8 data;
+	int retval;
+
+	/*
+	 * For DP rev.1.1, Maximum number of Main Link lanes
+	 * 0x01 = 1 lane, 0x02 = 2 lanes, 0x04 = 4 lanes
+	 */
+	retval = rockchip_edp_read_byte_from_dpcd(
+			edp, DP_MAX_LANE_COUNT, &data);
+	if (retval < 0)
+		*lane_count = 0;
+	else
+		*lane_count = DPCD_MAX_LANE_COUNT(data);
+
+	return retval;
+}
+
+static int rockchip_edp_init_training(struct rockchip_edp_device *edp)
+{
+	int retval;
+
+	/*
+	 * MACRO_RST must be applied after the PLL_LOCK to avoid
+	 * the DP inter pair skew issue for at least 10 us
+	 */
+	rockchip_edp_reset_macro(edp);
+
+	retval = rockchip_edp_get_max_rx_bandwidth(
+				edp, &edp->link_train.link_rate);
+	retval = rockchip_edp_get_max_rx_lane_count(
+				edp, &edp->link_train.lane_count);
+	dev_dbg(edp->dev, "max link rate:%d.%dGps max number of lanes:%d\n",
+		edp->link_train.link_rate * 27 / 100,
+		edp->link_train.link_rate * 27 % 100,
+		edp->link_train.lane_count);
+
+	if ((edp->link_train.link_rate != DP_LINK_BW_1_62) &&
+	    (edp->link_train.link_rate != DP_LINK_BW_2_7)) {
+		dev_warn(edp->dev, "Rx Max Link Rate is abnormal :%x !\n"
+			 "use default link rate:%d.%dGps\n",
+			 edp->link_train.link_rate,
+			 edp->video_info.link_rate * 27 / 100,
+			 edp->video_info.link_rate * 27 % 100);
+			 edp->link_train.link_rate = edp->video_info.link_rate;
+	}
+
+	if (edp->link_train.lane_count == 0) {
+		dev_err(edp->dev, "Rx Max Lane count is abnormal :%x !\n"
+			"use default lanes:%d\n",
+			edp->link_train.lane_count,
+			edp->video_info.lane_count);
+		edp->link_train.lane_count = edp->video_info.lane_count;
+	}
+
+	rockchip_edp_analog_power_ctr(edp, 1);
+
+	return 0;
+}
+
+static int rockchip_edp_hw_link_training(struct rockchip_edp_device *edp)
+{
+	u32 cnt = 50;
+	u32 val;
+
+	/* Set link rate and count as you want to establish*/
+	rockchip_edp_set_link_bandwidth(edp, edp->link_train.link_rate);
+	rockchip_edp_set_lane_count(edp, edp->link_train.lane_count);
+	rockchip_edp_hw_link_training_en(edp);
+	val = rockchip_edp_wait_hw_lt_done(edp);
+	while (val) {
+		if (cnt-- <= 0) {
+			dev_err(edp->dev, "hw lt timeout");
+			return -ETIMEDOUT;
+		}
+		mdelay(1);
+		val = rockchip_edp_wait_hw_lt_done(edp);
+	}
+
+	val = rockchip_edp_get_hw_lt_status(edp);
+	if (val)
+		dev_err(edp->dev, "hw lt err:%d\n", val);
+
+	return val;
+}
+
+static int rockchip_edp_set_link_train(struct rockchip_edp_device *edp)
+{
+	int retval;
+
+	rockchip_edp_init_training(edp);
+
+	retval = rockchip_edp_hw_link_training(edp);
+	if (retval < 0)
+		dev_err(edp->dev, "DP hw LT failed!\n");
+
+	return retval;
+}
+
+static int rockchip_edp_config_video(struct rockchip_edp_device *edp,
+				     struct video_info *video_info)
+{
+	int retval = 0;
+	int timeout_loop = 0;
+	int done_count = 0;
+
+	rockchip_edp_config_video_slave_mode(edp, video_info);
+
+	rockchip_edp_set_video_color_format(edp, video_info->color_depth,
+					    video_info->color_space,
+					    video_info->dynamic_range,
+					    video_info->ycbcr_coeff);
+
+	if (rockchip_edp_get_pll_lock_status(edp) == DP_PLL_UNLOCKED) {
+		dev_err(edp->dev, "PLL is not locked yet.\n");
+		return -EINVAL;
+	}
+
+	for (;;) {
+		timeout_loop++;
+		if (rockchip_edp_is_slave_video_stream_clock_on(edp) == 0)
+			break;
+
+		if (DP_TIMEOUT_LOOP_CNT < timeout_loop) {
+			dev_err(edp->dev, "Timeout of video streamclk ok\n");
+			return -ETIMEDOUT;
+		}
+
+		udelay(1);
+	}
+
+	/* Set to use the register calculated M/N video */
+	rockchip_edp_set_video_cr_mn(edp, CALCULATED_M, 0, 0);
+
+	/* Disable video mute */
+	rockchip_edp_enable_video_mute(edp, 0);
+
+	/* Configure video slave mode */
+	rockchip_edp_enable_video_master(edp, 0);
+
+	/* Enable video */
+	rockchip_edp_start_video(edp);
+
+	timeout_loop = 0;
+
+	for (;;) {
+		timeout_loop++;
+		if (rockchip_edp_is_video_stream_on(edp) == 0) {
+			done_count++;
+			if (done_count > 10)
+				break;
+		} else if (done_count) {
+			done_count = 0;
+		}
+		if (DP_TIMEOUT_LOOP_CNT < timeout_loop) {
+			dev_err(edp->dev, "Timeout of video streamclk ok\n");
+			return -ETIMEDOUT;
+		}
+
+		mdelay(1);
+	}
+
+	if (retval != 0)
+		dev_err(edp->dev, "Video stream is not detected!\n");
+
+	return retval;
+}
+
+static irqreturn_t rockchip_edp_isr(int irq, void *arg)
+{
+	struct rockchip_edp_device *edp = arg;
+	enum dp_irq_type irq_type;
+
+	irq_type = rockchip_edp_get_irq_type(edp);
+	switch (irq_type) {
+	case DP_IRQ_TYPE_HP_CABLE_IN:
+		dev_dbg(edp->dev, "Received irq - cable in\n");
+		rockchip_edp_clear_hotplug_interrupts(edp);
+		break;
+	case DP_IRQ_TYPE_HP_CABLE_OUT:
+		dev_dbg(edp->dev, "Received irq - cable out\n");
+		rockchip_edp_clear_hotplug_interrupts(edp);
+		break;
+	case DP_IRQ_TYPE_HP_CHANGE:
+		/*
+		 * We get these change notifications once in a while, but there
+		 * is nothing we can do with them. Just ignore it for now and
+		 * only handle cable changes.
+		 */
+		dev_dbg(edp->dev, "Received irq - hotplug change; ignoring.\n");
+		rockchip_edp_clear_hotplug_interrupts(edp);
+		break;
+	default:
+		dev_err(edp->dev, "Received irq - unknown type[%x]!\n",
+			irq_type);
+		rockchip_edp_clear_hotplug_interrupts(edp);
+		break;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void rockchip_edp_commit(struct drm_encoder *encoder)
+{
+	struct rockchip_edp_device *edp = encoder_to_edp(encoder);
+	int ret;
+
+	ret = rockchip_edp_set_link_train(edp);
+	if (ret)
+		dev_err(edp->dev, "link train failed!\n");
+	else
+		dev_dbg(edp->dev, "link training success.\n");
+
+	rockchip_edp_set_lane_count(edp, edp->link_train.lane_count);
+	rockchip_edp_set_link_bandwidth(edp, edp->link_train.link_rate);
+	rockchip_edp_init_video(edp);
+
+	ret = rockchip_edp_config_video(edp, &edp->video_info);
+	if (ret)
+		dev_err(edp->dev, "unable to config video\n");
+}
+
+static void rockchip_edp_poweron(struct drm_encoder *encoder)
+{
+	struct rockchip_edp_device *edp = encoder_to_edp(encoder);
+	int ret;
+
+	if (edp->dpms_mode == DRM_MODE_DPMS_ON)
+		return;
+
+	if (edp->panel)
+		edp->panel->funcs->enable(edp->panel);
+
+	ret = rockchip_edp_clk_enable(edp);
+	if (ret < 0) {
+		dev_err(edp->dev, "cannot enable edp clk %d\n", ret);
+		return;
+	}
+
+	ret = rockchip_edp_pre_init(edp);
+	if (ret < 0) {
+		dev_err(edp->dev, "edp pre init fail %d\n", ret);
+		return;
+	}
+
+	ret = rockchip_edp_init_edp(edp);
+	if (ret < 0) {
+		dev_err(edp->dev, "edp init fail %d\n", ret);
+		return;
+	}
+
+	enable_irq(edp->irq);
+	rockchip_edp_commit(encoder);
+}
+
+static void rockchip_edp_poweroff(struct drm_encoder *encoder)
+{
+	struct rockchip_edp_device *edp = encoder_to_edp(encoder);
+
+	if (edp->dpms_mode == DRM_MODE_DPMS_OFF)
+		return;
+
+	disable_irq(edp->irq);
+	rockchip_edp_reset(edp);
+	rockchip_edp_analog_power_ctr(edp, 0);
+	rockchip_edp_clk_disable(edp);
+	if (edp->panel)
+		edp->panel->funcs->disable(edp->panel);
+}
+
+static enum drm_connector_status
+rockchip_connector_detect(struct drm_connector *connector, bool force)
+{
+	return connector_status_connected;
+}
+
+static void rockchip_connector_destroy(struct drm_connector *connector)
+{
+	drm_sysfs_connector_remove(connector);
+	drm_connector_cleanup(connector);
+}
+
+static struct drm_connector_funcs rockchip_connector_funcs = {
+	.dpms = drm_helper_connector_dpms,
+	.detect = rockchip_connector_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = rockchip_connector_destroy,
+};
+
+static int rockchip_connector_get_modes(struct drm_connector *connector)
+{
+	struct rockchip_edp_device *edp = connector_to_edp(connector);
+	struct drm_panel *panel = edp->panel;
+
+	return panel->funcs->get_modes(panel);
+}
+
+static struct drm_encoder *
+	rockchip_connector_best_encoder(struct drm_connector *connector)
+{
+	struct rockchip_edp_device *edp = connector_to_edp(connector);
+
+	return &edp->encoder;
+}
+
+static enum drm_mode_status rockchip_connector_mode_valid(
+		struct drm_connector *connector,
+		struct drm_display_mode *mode)
+{
+	/* TODO(rk): verify that the mode is really valid */
+	return MODE_OK;
+}
+
+static struct drm_connector_helper_funcs rockchip_connector_helper_funcs = {
+	.get_modes = rockchip_connector_get_modes,
+	.mode_valid = rockchip_connector_mode_valid,
+	.best_encoder = rockchip_connector_best_encoder,
+};
+
+static void rockchip_drm_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+	struct rockchip_edp_device *edp = encoder_to_edp(encoder);
+
+	if (edp->dpms_mode == mode)
+		return;
+
+	switch (mode) {
+	case DRM_MODE_DPMS_ON:
+		rockchip_edp_poweron(encoder);
+		break;
+	case DRM_MODE_DPMS_STANDBY:
+	case DRM_MODE_DPMS_SUSPEND:
+	case DRM_MODE_DPMS_OFF:
+		rockchip_edp_poweroff(encoder);
+		break;
+	default:
+		break;
+	}
+
+	edp->dpms_mode = mode;
+}
+
+static bool
+rockchip_drm_encoder_mode_fixup(struct drm_encoder *encoder,
+				const struct drm_display_mode *mode,
+				struct drm_display_mode *adjusted_mode)
+{
+	if (!adjusted_mode->private) {
+		struct rockchip_display_mode *priv_mode;
+
+		priv_mode = kzalloc(sizeof(*priv_mode), GFP_KERNEL);
+		priv_mode->out_type = ROCKCHIP_DISPLAY_TYPE_EDP;
+		adjusted_mode->private = (int *)priv_mode;
+	}
+
+	return true;
+}
+
+static void rockchip_drm_encoder_mode_set(struct drm_encoder *encoder,
+					  struct drm_display_mode *mode,
+					  struct drm_display_mode *adjusted)
+{
+	struct rockchip_edp_device *edp = encoder_to_edp(encoder);
+	u32 val;
+	int ret;
+
+	ret = rockchip_drm_encoder_get_mux_id(edp->dev->of_node, encoder);
+	if (ret < 0)
+		return;
+
+	if (ret == ROCKCHIP_CRTC_VOPL)
+		val = EDP_SEL_VOP_LIT | (EDP_SEL_VOP_LIT << 16);
+	else
+		val = EDP_SEL_VOP_LIT << 16;
+
+	dev_info(edp->dev, "vop %s output to edp\n",
+		 (ret == ROCKCHIP_CRTC_VOPL) ? "LIT" : "BIG");
+	ret = regmap_write(edp->grf, edp->soc_data->grf_soc_con6, val);
+	if (ret != 0) {
+		dev_err(edp->dev, "Could not write to GRF: %d\n", ret);
+		return;
+	}
+
+	memcpy(&edp->mode, adjusted, sizeof(*mode));
+}
+
+static void rockchip_drm_encoder_prepare(struct drm_encoder *encoder)
+{
+}
+
+static void rockchip_drm_encoder_commit(struct drm_encoder *encoder)
+{
+	rockchip_drm_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
+}
+
+static void rockchip_drm_encoder_disable(struct drm_encoder *encoder)
+{
+	struct drm_plane *plane;
+	struct drm_device *dev = encoder->dev;
+
+	rockchip_drm_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+
+	/* all planes connected to this encoder should be also disabled. */
+	list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
+		if (plane->crtc && (plane->crtc == encoder->crtc))
+			plane->funcs->disable_plane(plane);
+	}
+}
+
+static struct drm_encoder_helper_funcs rockchip_encoder_helper_funcs = {
+	.dpms = rockchip_drm_encoder_dpms,
+	.mode_fixup = rockchip_drm_encoder_mode_fixup,
+	.mode_set = rockchip_drm_encoder_mode_set,
+	.prepare = rockchip_drm_encoder_prepare,
+	.commit = rockchip_drm_encoder_commit,
+	.disable = rockchip_drm_encoder_disable,
+};
+
+static void rockchip_drm_encoder_destroy(struct drm_encoder *encoder)
+{
+	drm_encoder_cleanup(encoder);
+}
+
+static struct drm_encoder_funcs rockchip_encoder_funcs = {
+	.destroy = rockchip_drm_encoder_destroy,
+};
+
+static int rockchip_edp_init(struct rockchip_edp_device *edp)
+{
+	struct device *dev = edp->dev;
+	struct device_node *np = dev->of_node;
+	struct platform_device *pdev = to_platform_device(dev);
+	struct resource *res;
+	const struct of_device_id *match;
+	int ret;
+
+	if (!np) {
+		dev_err(dev, "Missing device tree node.\n");
+		return -EINVAL;
+	}
+
+	match = of_match_node(rockchip_edp_dt_ids, np);
+	edp->soc_data = (struct rockchip_edp_soc_data *)match->data;
+	/*
+	 * The control bit is located in the GRF register space.
+	 */
+	if (edp->soc_data->grf_soc_con6 >= 0) {
+		edp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+		if (IS_ERR(edp->grf)) {
+			dev_err(dev,
+				"rk3288-edp needs rockchip,grf property\n");
+			return PTR_ERR(edp->grf);
+		}
+	}
+
+	edp->video_info.h_sync_polarity = 0;
+	edp->video_info.v_sync_polarity = 0;
+	edp->video_info.interlaced = 0;
+	edp->video_info.color_space = CS_RGB;
+	edp->video_info.dynamic_range = VESA;
+	edp->video_info.ycbcr_coeff = COLOR_YCBCR601;
+	edp->video_info.color_depth = COLOR_8;
+
+	edp->video_info.link_rate = DP_LINK_BW_1_62;
+	edp->video_info.lane_count = LANE_CNT4;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	edp->regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(edp->regs)) {
+		dev_err(dev, "ioremap reg failed\n");
+		return PTR_ERR(edp->regs);
+	}
+
+	edp->clk_edp = devm_clk_get(dev, "clk_edp");
+	if (IS_ERR(edp->clk_edp)) {
+		dev_err(dev, "cannot get clk_edp\n");
+		return PTR_ERR(edp->clk_edp);
+	}
+
+	edp->clk_24m = devm_clk_get(dev, "clk_edp_24m");
+	if (IS_ERR(edp->clk_24m)) {
+		dev_err(dev, "cannot get clk_edp_24m\n");
+		return PTR_ERR(edp->clk_24m);
+	}
+
+	edp->pclk = devm_clk_get(dev, "pclk_edp");
+	if (IS_ERR(edp->pclk)) {
+		dev_err(dev, "cannot get pclk\n");
+		return PTR_ERR(edp->pclk);
+	}
+
+	edp->rst = devm_reset_control_get(dev, "edp");
+	if (IS_ERR(edp->rst)) {
+		dev_err(dev, "failed to get reset\n");
+		return PTR_ERR(edp->rst);
+	}
+
+	ret = rockchip_edp_clk_enable(edp);
+	if (ret < 0) {
+		dev_err(edp->dev, "cannot enable edp clk %d\n", ret);
+		return ret;
+	}
+
+	ret = rockchip_edp_pre_init(edp);
+	if (ret < 0) {
+		dev_err(edp->dev, "failed to pre init %d\n", ret);
+		return ret;
+	}
+
+	edp->irq = platform_get_irq(pdev, 0);
+	if (edp->irq < 0) {
+		dev_err(dev, "cannot find IRQ\n");
+		return edp->irq;
+	}
+
+	ret = devm_request_irq(dev, edp->irq, rockchip_edp_isr, 0,
+			       dev_name(dev), edp);
+	if (ret) {
+		dev_err(dev, "cannot claim IRQ %d\n", edp->irq);
+		return ret;
+	}
+
+	disable_irq_nosync(edp->irq);
+
+	edp->dpms_mode = DRM_MODE_DPMS_OFF;
+
+	dev_set_name(edp->dev, "rockchip-edp");
+
+	return 0;
+}
+
+static int rockchip_edp_bind(struct device *dev, struct device *master,
+			     void *data)
+{
+	struct rockchip_edp_device *edp = dev_get_drvdata(dev);
+	struct drm_encoder *encoder;
+	struct drm_connector *connector;
+	struct drm_device *drm_dev = data;
+	int ret;
+
+	ret = rockchip_edp_init(edp);
+	if (ret < 0)
+		return ret;
+
+	edp->drm_dev = drm_dev;
+
+	encoder = &edp->encoder;
+
+	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
+							     dev->of_node);
+	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
+
+	ret = drm_encoder_init(drm_dev, encoder, &rockchip_encoder_funcs,
+			       DRM_MODE_ENCODER_LVDS);
+	if (ret) {
+		DRM_ERROR("failed to initialize encoder with drm\n");
+		return ret;
+	}
+
+	drm_encoder_helper_add(encoder, &rockchip_encoder_helper_funcs);
+
+	connector = &edp->connector;
+	connector->polled = DRM_CONNECTOR_POLL_HPD;
+	connector->dpms = DRM_MODE_DPMS_OFF;
+
+	ret = drm_connector_init(drm_dev, connector,
+				 &rockchip_connector_funcs,
+				 DRM_MODE_CONNECTOR_eDP);
+	if (ret) {
+		DRM_ERROR("failed to initialize connector with drm\n");
+		goto err_free_encoder;
+	}
+
+	drm_connector_helper_add(connector,
+				 &rockchip_connector_helper_funcs);
+
+	ret = drm_sysfs_connector_add(connector);
+	if (ret) {
+		DRM_ERROR("failed to add drm_sysfs\n");
+		goto err_free_connector;
+	}
+
+	ret = drm_mode_connector_attach_encoder(connector, encoder);
+	if (ret) {
+		DRM_ERROR("failed to attach connector and encoder\n");
+		goto err_free_connector_sysfs;
+	}
+
+	ret = drm_panel_attach(edp->panel, connector);
+	if (ret) {
+		DRM_ERROR("failed to attach connector and encoder\n");
+		goto err_free_connector_sysfs;
+	}
+
+	return 0;
+
+err_free_connector_sysfs:
+	drm_sysfs_connector_remove(connector);
+err_free_connector:
+	drm_connector_cleanup(connector);
+err_free_encoder:
+	drm_encoder_cleanup(encoder);
+	return ret;
+}
+
+static void rockchip_edp_unbind(struct device *dev, struct device *master,
+				void *data)
+{
+	struct rockchip_edp_device *edp = dev_get_drvdata(dev);
+	struct drm_encoder *encoder;
+
+	encoder = &edp->encoder;
+
+	if (edp->panel)
+		drm_panel_detach(edp->panel);
+
+	rockchip_drm_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+	encoder->funcs->destroy(encoder);
+	drm_sysfs_connector_remove(&edp->connector);
+	drm_connector_cleanup(&edp->connector);
+	drm_encoder_cleanup(encoder);
+}
+
+static const struct component_ops rockchip_edp_component_ops = {
+	.bind = rockchip_edp_bind,
+	.unbind = rockchip_edp_unbind,
+};
+
+static int rockchip_edp_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct drm_panel *panel;
+	struct device_node *panel_node;
+	struct rockchip_edp_device *edp;
+
+	if (!dev->of_node) {
+		dev_err(dev, "can't find eDP devices\n");
+		return -ENODEV;
+	}
+
+	panel_node = of_parse_phandle(dev->of_node, "rockchip,panel", 0);
+	if (!panel_node) {
+		DRM_ERROR("failed to find diaplay panel\n");
+		return -ENODEV;
+	}
+
+	panel = of_drm_find_panel(panel_node);
+	if (!panel) {
+		DRM_ERROR("failed to find diaplay panel\n");
+		of_node_put(panel_node);
+		return -EPROBE_DEFER;
+	}
+
+	of_node_put(panel_node);
+
+	edp = devm_kzalloc(dev, sizeof(*edp), GFP_KERNEL);
+	if (!edp)
+		return -ENOMEM;
+	edp->dev = dev;
+	edp->panel = panel;
+	platform_set_drvdata(pdev, edp);
+
+	return component_add(dev, &rockchip_edp_component_ops);
+}
+
+static int rockchip_edp_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &rockchip_edp_component_ops);
+
+	return 0;
+}
+
+static struct platform_driver rockchip_edp_driver = {
+	.probe = rockchip_edp_probe,
+	.remove = rockchip_edp_remove,
+	.driver = {
+		   .name = "rockchip-edp",
+		   .owner = THIS_MODULE,
+		   .of_match_table = of_match_ptr(rockchip_edp_dt_ids),
+	},
+};
+
+module_platform_driver(rockchip_edp_driver);
+
+MODULE_AUTHOR("Jeff chen <jeff.chen@rock-chips.com>");
+MODULE_DESCRIPTION("ROCKCHIP EDP Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/rockchip/rockchip_edp_core.h b/drivers/gpu/drm/rockchip/rockchip_edp_core.h
new file mode 100644
index 0000000..c13325f
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_edp_core.h
@@ -0,0 +1,309 @@
+/*
+* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+* Author:
+*      Andy yan <andy.yan@rock-chips.com>
+*      Jeff chen <jeff.chen@rock-chips.com>
+*
+* based on exynos_dp_core.h
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*/
+
+#ifndef _ROCKCHIP_EDP_CORE_H
+#define _ROCKCHIP_EDP_CORE_H
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_panel.h>
+#include "rockchip_drm_drv.h"
+
+#define DP_TIMEOUT_LOOP_CNT 100
+#define MAX_CR_LOOP 5
+#define MAX_EQ_LOOP 5
+
+#define GRF_EDP_REF_CLK_SEL_INTER		(1 << 4)
+#define GRF_EDP_HDCP_EN				(1 << 15)
+#define GRF_EDP_BIST_EN				(1 << 14)
+#define GRF_EDP_MEM_CTL_BY_EDP			(1 << 13)
+#define GRF_EDP_SECURE_EN			(1 << 3)
+#define EDP_SEL_VOP_LIT				(1 << 5)
+
+enum link_lane_count_type {
+	LANE_CNT1 = 1,
+	LANE_CNT2 = 2,
+	LANE_CNT4 = 4
+};
+
+enum link_training_state {
+	LT_START,
+	LT_CLK_RECOVERY,
+	LT_EQ_TRAINING,
+	FINISHED,
+	FAILED
+};
+
+enum voltage_swing_level {
+	VOLTAGE_LEVEL_0,
+	VOLTAGE_LEVEL_1,
+	VOLTAGE_LEVEL_2,
+	VOLTAGE_LEVEL_3,
+};
+
+enum pre_emphasis_level {
+	PRE_EMPHASIS_LEVEL_0,
+	PRE_EMPHASIS_LEVEL_1,
+	PRE_EMPHASIS_LEVEL_2,
+	PRE_EMPHASIS_LEVEL_3,
+};
+
+enum pattern_set {
+	PRBS7,
+	D10_2,
+	TRAINING_PTN1,
+	TRAINING_PTN2,
+	DP_NONE
+};
+
+enum color_space {
+	CS_RGB,
+	CS_YCBCR422,
+	CS_YCBCR444
+};
+
+enum color_depth {
+	COLOR_6,
+	COLOR_8,
+	COLOR_10,
+	COLOR_12
+};
+
+enum color_coefficient {
+	COLOR_YCBCR601,
+	COLOR_YCBCR709
+};
+
+enum dynamic_range {
+	VESA,
+	CEA
+};
+
+enum pll_status {
+	DP_PLL_UNLOCKED,
+	DP_PLL_LOCKED
+};
+
+enum clock_recovery_m_value_type {
+	CALCULATED_M,
+	REGISTER_M
+};
+
+enum video_timing_recognition_type {
+	VIDEO_TIMING_FROM_CAPTURE,
+	VIDEO_TIMING_FROM_REGISTER
+};
+
+enum analog_power_block {
+	AUX_BLOCK,
+	CH0_BLOCK,
+	CH1_BLOCK,
+	CH2_BLOCK,
+	CH3_BLOCK,
+	ANALOG_TOTAL,
+	POWER_ALL
+};
+
+enum dp_irq_type {
+	DP_IRQ_TYPE_HP_CABLE_IN,
+	DP_IRQ_TYPE_HP_CABLE_OUT,
+	DP_IRQ_TYPE_HP_CHANGE,
+	DP_IRQ_TYPE_UNKNOWN,
+};
+
+struct video_info {
+	char *name;
+
+	bool h_sync_polarity;
+	bool v_sync_polarity;
+	bool interlaced;
+
+	enum color_space color_space;
+	enum dynamic_range dynamic_range;
+	enum color_coefficient ycbcr_coeff;
+	enum color_depth color_depth;
+
+	u8 link_rate;
+	enum link_lane_count_type lane_count;
+};
+
+struct link_train {
+	int eq_loop;
+	int cr_loop[4];
+
+	u8 link_rate;
+	u8 lane_count;
+	u8 training_lane[4];
+
+	enum link_training_state lt_state;
+};
+
+/*
+ * @grf_offset: offset inside the grf regmap for setting the rk3288 lvds
+ */
+struct rockchip_edp_soc_data {
+	int grf_soc_con6;
+	int grf_soc_con12;
+};
+
+struct rockchip_edp_device {
+	struct device *dev;
+	struct drm_device *drm_dev;
+	struct drm_panel *panel;
+	struct drm_connector connector;
+	struct drm_encoder encoder;
+	struct drm_display_mode mode;
+
+	struct rockchip_edp_soc_data *soc_data;
+
+	void __iomem *regs;
+	struct regmap *grf;
+	unsigned int irq;
+	struct clk *clk_edp;
+	struct clk *clk_24m_parent;
+	struct clk *clk_24m;
+	struct clk *pclk;
+	struct reset_control *rst;
+	struct link_train link_train;
+	struct video_info video_info;
+	bool clk_on;
+
+	int dpms_mode;
+};
+
+void rockchip_edp_enable_video_mute(struct rockchip_edp_device *edp,
+				    bool enable);
+void rockchip_edp_stop_video(struct rockchip_edp_device *edp);
+void rockchip_edp_lane_swap(struct rockchip_edp_device *edp, bool enable);
+void rockchip_edp_init_refclk(struct rockchip_edp_device *edp);
+void rockchip_edp_init_interrupt(struct rockchip_edp_device *edp);
+void rockchip_edp_reset(struct rockchip_edp_device *edp);
+void rockchip_edp_config_interrupt(struct rockchip_edp_device *edp);
+u32 rockchip_edp_get_pll_lock_status(struct rockchip_edp_device *edp);
+void rockchip_edp_analog_power_ctr(struct rockchip_edp_device *edp,
+				   bool enable);
+void rockchip_edp_init_analog_func(struct rockchip_edp_device *edp);
+void rockchip_edp_init_hpd(struct rockchip_edp_device *edp);
+void rockchip_edp_reset_aux(struct rockchip_edp_device *edp);
+void rockchip_edp_init_aux(struct rockchip_edp_device *edp);
+int rockchip_edp_get_plug_in_status(struct rockchip_edp_device *edp);
+void rockchip_edp_enable_sw_function(struct rockchip_edp_device *edp);
+int rockchip_edp_start_aux_transaction(struct rockchip_edp_device *edp);
+int rockchip_edp_write_byte_to_dpcd(struct rockchip_edp_device *edp,
+				    unsigned int reg_addr,
+				    unsigned char data);
+int rockchip_edp_read_byte_from_dpcd(struct rockchip_edp_device *edp,
+				     unsigned int reg_addr,
+				     unsigned char *data);
+int rockchip_edp_write_bytes_to_dpcd(struct rockchip_edp_device *edp,
+				     unsigned int reg_addr,
+				     unsigned int count,
+				     unsigned char data[]);
+int rockchip_edp_read_bytes_from_dpcd(struct rockchip_edp_device *edp,
+				      unsigned int reg_addr,
+				      unsigned int count,
+				      unsigned char data[]);
+int rockchip_edp_select_i2c_device(struct rockchip_edp_device *edp,
+				   unsigned int device_addr,
+				   unsigned int reg_addr);
+int rockchip_edp_read_byte_from_i2c(struct rockchip_edp_device *edp,
+				    unsigned int device_addr,
+				    unsigned int reg_addr,
+				    unsigned int *data);
+int rockchip_edp_read_bytes_from_i2c(struct rockchip_edp_device *edp,
+				     unsigned int device_addr,
+				     unsigned int reg_addr,
+				     unsigned int count,
+				     unsigned char edid[]);
+void rockchip_edp_set_link_bandwidth(struct rockchip_edp_device *edp,
+				     u32 bwtype);
+void rockchip_edp_get_link_bandwidth(struct rockchip_edp_device *edp,
+				     u32 *bwtype);
+void rockchip_edp_set_lane_count(struct rockchip_edp_device *edp,
+				 u32 count);
+void rockchip_edp_get_lane_count(struct rockchip_edp_device *edp,
+				 u32 *count);
+void rockchip_edp_enable_enhanced_mode(struct rockchip_edp_device *edp,
+				       bool enable);
+void rockchip_edp_set_training_pattern(struct rockchip_edp_device *edp,
+				       enum pattern_set pattern);
+void rockchip_edp_set_lane0_pre_emphasis(struct rockchip_edp_device *edp,
+					 u32 level);
+void rockchip_edp_set_lane1_pre_emphasis(struct rockchip_edp_device *edp,
+					 u32 level);
+void rockchip_edp_set_lane2_pre_emphasis(struct rockchip_edp_device *edp,
+					 u32 level);
+void rockchip_edp_set_lane3_pre_emphasis(struct rockchip_edp_device *edp,
+					 u32 level);
+void rockchip_edp_set_lane0_link_training(struct rockchip_edp_device *edp,
+					  u32 training_lane);
+void rockchip_edp_set_lane1_link_training(struct rockchip_edp_device *edp,
+					  u32 training_lane);
+void rockchip_edp_set_lane2_link_training(struct rockchip_edp_device *edp,
+					  u32 training_lane);
+void rockchip_edp_set_lane3_link_training(struct rockchip_edp_device *edp,
+					  u32 training_lane);
+u32 rockchip_edp_get_lane0_link_training(struct rockchip_edp_device *edp);
+u32 rockchip_edp_get_lane1_link_training(struct rockchip_edp_device *edp);
+u32 rockchip_edp_get_lane2_link_training(struct rockchip_edp_device *edp);
+u32 rockchip_edp_get_lane3_link_training(struct rockchip_edp_device *edp);
+void rockchip_edp_reset_macro(struct rockchip_edp_device *edp);
+int rockchip_edp_init_video(struct rockchip_edp_device *edp);
+
+void rockchip_edp_set_video_color_format(struct rockchip_edp_device *edp,
+					 u32 color_depth,
+					 u32 color_space,
+					 u32 dynamic_range,
+					 u32 coeff);
+int
+rockchip_edp_is_slave_video_stream_clock_on(struct rockchip_edp_device *edp);
+void rockchip_edp_set_video_cr_mn(struct rockchip_edp_device *edp,
+				  enum clock_recovery_m_value_type type,
+				  u32 m_value,
+				  u32 n_value);
+void rockchip_edp_set_video_timing_mode(struct rockchip_edp_device *edp,
+					u32 type);
+void rockchip_edp_enable_video_master(struct rockchip_edp_device *edp,
+				      bool enable);
+void rockchip_edp_start_video(struct rockchip_edp_device *edp);
+int rockchip_edp_is_video_stream_on(struct rockchip_edp_device *edp);
+void rockchip_edp_config_video_slave_mode(struct rockchip_edp_device *edp,
+					  struct video_info *video_info);
+void rockchip_edp_enable_scrambling(struct rockchip_edp_device *edp);
+void rockchip_edp_disable_scrambling(struct rockchip_edp_device *edp);
+void rockchip_edp_hw_link_training_en(struct rockchip_edp_device *edp);
+int rockchip_edp_get_hw_lt_status(struct rockchip_edp_device *edp);
+int rockchip_edp_wait_hw_lt_done(struct rockchip_edp_device *edp);
+enum dp_irq_type rockchip_edp_get_irq_type(struct rockchip_edp_device *edp);
+void rockchip_edp_clear_hotplug_interrupts(struct rockchip_edp_device *edp);
+
+/* I2C EDID Chip ID, Slave Address */
+#define I2C_EDID_DEVICE_ADDR			0x50
+#define I2C_E_EDID_DEVICE_ADDR			0x30
+
+/* DPCD_ADDR_MAX_LANE_COUNT */
+#define DPCD_ENHANCED_FRAME_CAP(x)		(((x) >> 7) & 0x1)
+#define DPCD_MAX_LANE_COUNT(x)			((x) & 0x1f)
+
+/* DPCD_ADDR_LANE_COUNT_SET */
+#define DPCD_LANE_COUNT_SET(x)			((x) & 0x1f)
+
+/* DPCD_ADDR_TRAINING_LANE0_SET */
+#define DPCD_PRE_EMPHASIS_SET(x)		(((x) & 0x3) << 3)
+#define DPCD_PRE_EMPHASIS_GET(x)		(((x) >> 3) & 0x3)
+#define DPCD_VOLTAGE_SWING_SET(x)		(((x) & 0x3) << 0)
+#define DPCD_VOLTAGE_SWING_GET(x)		(((x) >> 0) & 0x3)
+
+#endif  /* _ROCKCHIP_EDP_CORE_H */
diff --git a/drivers/gpu/drm/rockchip/rockchip_edp_reg.c b/drivers/gpu/drm/rockchip/rockchip_edp_reg.c
new file mode 100644
index 0000000..f6d641c
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_edp_reg.c
@@ -0,0 +1,1202 @@
+/*
+* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+* Author:
+*      Andy yan <andy.yan@rock-chips.com>
+*      Jeff chen <jeff.chen@rock-chips.com>
+*
+* based on exynos_dp_reg.c
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*/
+
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#include "rockchip_edp_core.h"
+#include "rockchip_edp_reg.h"
+
+void rockchip_edp_enable_video_mute(struct rockchip_edp_device *edp,
+				    bool enable)
+{
+	u32 val;
+
+	if (enable) {
+		val = readl(edp->regs + VIDEO_CTL_1);
+		val |= VIDEO_MUTE;
+		writel(val, edp->regs + VIDEO_CTL_1);
+	} else {
+		val = readl(edp->regs + VIDEO_CTL_1);
+		val &= ~VIDEO_MUTE;
+		writel(val, edp->regs + VIDEO_CTL_1);
+	}
+}
+
+void rockchip_edp_stop_video(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + VIDEO_CTL_1);
+	val &= ~VIDEO_EN;
+	writel(val, edp->regs + VIDEO_CTL_1);
+}
+
+void rockchip_edp_lane_swap(struct rockchip_edp_device *edp, bool enable)
+{
+	u32 val;
+
+	if (enable)
+		val = LANE3_MAP_LOGIC_LANE_0 | LANE2_MAP_LOGIC_LANE_1 |
+			LANE1_MAP_LOGIC_LANE_2 | LANE0_MAP_LOGIC_LANE_3;
+	else
+		val = LANE3_MAP_LOGIC_LANE_3 | LANE2_MAP_LOGIC_LANE_2 |
+			LANE1_MAP_LOGIC_LANE_1 | LANE0_MAP_LOGIC_LANE_0;
+
+	writel(val, edp->regs + LANE_MAP);
+}
+
+void rockchip_edp_init_refclk(struct rockchip_edp_device *edp)
+{
+	writel(SEL_24M, edp->regs + ANALOG_CTL_2);
+	writel(REF_CLK_24M, edp->regs + PLL_REG_1);
+
+	writel(0x95, edp->regs + PLL_REG_2);
+	writel(0x40, edp->regs + PLL_REG_3);
+	writel(0x58, edp->regs + PLL_REG_4);
+	writel(0x22, edp->regs + PLL_REG_5);
+	writel(0x19, edp->regs + SSC_REG);
+	writel(0x87, edp->regs + TX_REG_COMMON);
+	writel(0x03, edp->regs + DP_AUX);
+	writel(0x46, edp->regs + DP_BIAS);
+	writel(0x55, edp->regs + DP_RESERVE2);
+}
+
+void rockchip_edp_init_interrupt(struct rockchip_edp_device *edp)
+{
+	/* Set interrupt pin assertion polarity as high */
+	writel(INT_POL, edp->regs + INT_CTL);
+
+	/* Clear pending valisers */
+	writel(0xff, edp->regs + COMMON_INT_STA_1);
+	writel(0x4f, edp->regs + COMMON_INT_STA_2);
+	writel(0xff, edp->regs + COMMON_INT_STA_3);
+	writel(0x27, edp->regs + COMMON_INT_STA_4);
+
+	writel(0x7f, edp->regs + DP_INT_STA);
+
+	/* 0:mask,1: unmask */
+	writel(0x00, edp->regs + COMMON_INT_MASK_1);
+	writel(0x00, edp->regs + COMMON_INT_MASK_2);
+	writel(0x00, edp->regs + COMMON_INT_MASK_3);
+	writel(0x00, edp->regs + COMMON_INT_MASK_4);
+	writel(0x00, edp->regs + DP_INT_STA_MASK);
+}
+
+void rockchip_edp_reset(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	rockchip_edp_stop_video(edp);
+	rockchip_edp_enable_video_mute(edp, 0);
+
+	val = VID_CAP_FUNC_EN_N | AUD_FIFO_FUNC_EN_N |
+		AUD_FUNC_EN_N | HDCP_FUNC_EN_N | SW_FUNC_EN_N;
+	writel(val, edp->regs + FUNC_EN_1);
+
+	val = SSC_FUNC_EN_N | AUX_FUNC_EN_N |
+		SERDES_FIFO_FUNC_EN_N |
+		LS_CLK_DOMAIN_FUNC_EN_N;
+	writel(val, edp->regs + FUNC_EN_2);
+
+	usleep_range(20, 30);
+
+	rockchip_edp_lane_swap(edp, 0);
+
+	writel(0x0, edp->regs + SYS_CTL_1);
+	writel(0x40, edp->regs + SYS_CTL_2);
+	writel(0x0, edp->regs + SYS_CTL_3);
+	writel(0x0, edp->regs + SYS_CTL_4);
+
+	writel(0x0, edp->regs + PKT_SEND_CTL);
+	writel(0x0, edp->regs + HDCP_CTL);
+
+	writel(0x5e, edp->regs + HPD_DEGLITCH_L);
+	writel(0x1a, edp->regs + HPD_DEGLITCH_H);
+
+	writel(0x10, edp->regs + LINK_DEBUG_CTL);
+
+	writel(0x0, edp->regs + VIDEO_FIFO_THRD);
+	writel(0x20, edp->regs + AUDIO_MARGIN);
+
+	writel(0x4, edp->regs + M_VID_GEN_FILTER_TH);
+	writel(0x2, edp->regs + M_AUD_GEN_FILTER_TH);
+
+	writel(0x0, edp->regs + SOC_GENERAL_CTL);
+}
+
+void rockchip_edp_config_interrupt(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	/* 0: mask, 1: unmask */
+	val = 0;
+	writel(val, edp->regs + COMMON_INT_MASK_1);
+
+	writel(val, edp->regs + COMMON_INT_MASK_2);
+
+	writel(val, edp->regs + COMMON_INT_MASK_3);
+
+	writel(val, edp->regs + COMMON_INT_MASK_4);
+
+	writel(val, edp->regs + DP_INT_STA_MASK);
+}
+
+u32 rockchip_edp_get_pll_lock_status(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + DEBUG_CTL);
+
+	return (val & PLL_LOCK) ? DP_PLL_LOCKED : DP_PLL_UNLOCKED;
+}
+
+void rockchip_edp_analog_power_ctr(struct rockchip_edp_device *edp,
+				   bool enable)
+{
+	u32 val;
+
+	if (enable) {
+		val = PD_EXP_BG | PD_AUX | PD_PLL |
+			PD_CH3 | PD_CH2 | PD_CH1 | PD_CH0;
+		writel(val, edp->regs + DP_PWRDN);
+		usleep_range(10, 20);
+		writel(0x0, edp->regs + DP_PWRDN);
+	} else {
+		val = PD_EXP_BG | PD_AUX | PD_PLL |
+			PD_CH3 | PD_CH2 | PD_CH1 | PD_CH0;
+		writel(val, edp->regs + DP_PWRDN);
+	}
+}
+
+void rockchip_edp_init_analog_func(struct rockchip_edp_device *edp)
+{
+	u32 val;
+	int wt = 0;
+
+	rockchip_edp_analog_power_ctr(edp, 1);
+
+	val = PLL_LOCK_CHG;
+	writel(val, edp->regs + COMMON_INT_STA_1);
+
+	val = readl(edp->regs + DEBUG_CTL);
+	val &= ~(F_PLL_LOCK | PLL_LOCK_CTRL);
+	writel(val, edp->regs + DEBUG_CTL);
+
+	/* Power up PLL */
+	while (wt < 100) {
+		if (rockchip_edp_get_pll_lock_status(edp) == DP_PLL_LOCKED) {
+			dev_dbg(edp->dev, "edp pll locked\n");
+			break;
+		}
+		wt++;
+		udelay(5);
+	}
+
+	/* Enable Serdes FIFO function and Link symbol clock domain module */
+	val = readl(edp->regs + FUNC_EN_2);
+	val &= ~(SERDES_FIFO_FUNC_EN_N | LS_CLK_DOMAIN_FUNC_EN_N
+		| AUX_FUNC_EN_N | SSC_FUNC_EN_N);
+	writel(val, edp->regs + FUNC_EN_2);
+}
+
+void rockchip_edp_init_hpd(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = HOTPLUG_CHG | HPD_LOST | PLUG;
+	writel(val, edp->regs + COMMON_INT_STA_4);
+
+	val = INT_HPD;
+	writel(val, edp->regs + DP_INT_STA);
+
+	val = readl(edp->regs + SYS_CTL_3);
+	val |= (F_HPD | HPD_CTRL);
+	writel(val, edp->regs + SYS_CTL_3);
+}
+
+void rockchip_edp_reset_aux(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	/* Disable AUX channel module */
+	val = readl(edp->regs + FUNC_EN_2);
+	val |= AUX_FUNC_EN_N;
+	writel(val, edp->regs + FUNC_EN_2);
+}
+
+void rockchip_edp_init_aux(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	/* Clear inerrupts related to AUX channel */
+	val = RPLY_RECEIV | AUX_ERR;
+	writel(val, edp->regs + DP_INT_STA);
+
+	rockchip_edp_reset_aux(edp);
+
+	/* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */
+	val = DEFER_CTRL_EN | DEFER_COUNT(1);
+	writel(val, edp->regs + AUX_CH_DEFER_CTL);
+
+	/* Enable AUX channel module */
+	val = readl(edp->regs + FUNC_EN_2);
+	val &= ~AUX_FUNC_EN_N;
+	writel(val, edp->regs + FUNC_EN_2);
+}
+
+int rockchip_edp_get_plug_in_status(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + SYS_CTL_3);
+	if (val & HPD_STATUS)
+		return 0;
+
+	return -EINVAL;
+}
+
+void rockchip_edp_enable_sw_function(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + FUNC_EN_1);
+	val &= ~SW_FUNC_EN_N;
+	writel(val, edp->regs + FUNC_EN_1);
+}
+
+int rockchip_edp_start_aux_transaction(struct rockchip_edp_device *edp)
+{
+	int val;
+	int retval = 0;
+	int timeout_loop = 0;
+	int aux_timeout = 0;
+
+	/* Enable AUX CH operation */
+	val = readl(edp->regs + AUX_CH_CTL_2);
+	val |= AUX_EN;
+	writel(val, edp->regs + AUX_CH_CTL_2);
+
+	/* Is AUX CH operation enabled? */
+	val = readl(edp->regs + AUX_CH_CTL_2);
+	while (val & AUX_EN) {
+		aux_timeout++;
+		if ((DP_TIMEOUT_LOOP_CNT * 10) < aux_timeout) {
+			dev_err(edp->dev, "AUX CH enable timeout!\n");
+			return -ETIMEDOUT;
+		}
+		val = readl(edp->regs + AUX_CH_CTL_2);
+		usleep_range(1000, 2000);
+	}
+
+	/* Is AUX CH command redply received? */
+	val = readl(edp->regs + DP_INT_STA);
+	while (!(val & RPLY_RECEIV)) {
+		timeout_loop++;
+		if (DP_TIMEOUT_LOOP_CNT < timeout_loop) {
+			dev_err(edp->dev, "AUX CH command redply failed!\n");
+			return -ETIMEDOUT;
+		}
+		val = readl(edp->regs + DP_INT_STA);
+		usleep_range(10, 20);
+	}
+
+	/* Clear interrupt source for AUX CH command redply */
+	writel(RPLY_RECEIV, edp->regs + DP_INT_STA);
+
+	/* Clear interrupt source for AUX CH access error */
+	val = readl(edp->regs + DP_INT_STA);
+	if (val & AUX_ERR) {
+		writel(AUX_ERR, edp->regs + DP_INT_STA);
+		return -EREMOTEIO;
+	}
+
+	/* Check AUX CH error access status */
+	val = readl(edp->regs + AUX_CH_STA);
+	if ((val & AUX_STATUS_MASK) != 0) {
+		dev_err(edp->dev, "AUX CH error happens: %d\n\n",
+			val & AUX_STATUS_MASK);
+		return -EREMOTEIO;
+	}
+
+	return retval;
+}
+
+int rockchip_edp_write_byte_to_dpcd(struct rockchip_edp_device *edp,
+				    unsigned int val_addr,
+				    unsigned char data)
+{
+	u32 val;
+	int i;
+	int retval;
+
+	for (i = 0; i < 3; i++) {
+		/* Clear AUX CH data buffer */
+		val = BUF_CLR;
+		writel(val, edp->regs + BUFFER_DATA_CTL);
+
+		/* Select DPCD device address */
+		val = AUX_ADDR_7_0(val_addr);
+		writel(val, edp->regs + DP_AUX_ADDR_7_0);
+		val = AUX_ADDR_15_8(val_addr);
+		writel(val, edp->regs + DP_AUX_ADDR_15_8);
+		val = AUX_ADDR_19_16(val_addr);
+		writel(val, edp->regs + DP_AUX_ADDR_19_16);
+
+		/* Write data buffer */
+		val = (unsigned int)data;
+		writel(val, edp->regs + BUF_DATA_0);
+
+		/*
+		 * Set DisplayPort transaction and write 1 byte
+		 * If bit 3 is 1, DisplayPort transaction.
+		 * If Bit 3 is 0, I2C transaction.
+		 */
+		val = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE;
+		writel(val, edp->regs + AUX_CH_CTL_1);
+
+		/* Start AUX transaction */
+		retval = rockchip_edp_start_aux_transaction(edp);
+		if (retval == 0)
+			break;
+
+		dev_dbg(edp->dev, "Aux Transaction fail!\n");
+	}
+
+	return retval;
+}
+
+int rockchip_edp_read_byte_from_dpcd(struct rockchip_edp_device *edp,
+				     unsigned int val_addr,
+				     unsigned char *data)
+{
+	u32 val;
+	int i;
+	int retval;
+
+	for (i = 0; i < 10; i++) {
+		/* Clear AUX CH data buffer */
+		val = BUF_CLR;
+		writel(val, edp->regs + BUFFER_DATA_CTL);
+
+		/* Select DPCD device address */
+		val = AUX_ADDR_7_0(val_addr);
+		writel(val, edp->regs + DP_AUX_ADDR_7_0);
+		val = AUX_ADDR_15_8(val_addr);
+		writel(val, edp->regs + DP_AUX_ADDR_15_8);
+		val = AUX_ADDR_19_16(val_addr);
+		writel(val, edp->regs + DP_AUX_ADDR_19_16);
+
+		/*
+		 * Set DisplayPort transaction and read 1 byte
+		 * If bit 3 is 1, DisplayPort transaction.
+		 * If Bit 3 is 0, I2C transaction.
+		 */
+		val = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ;
+		writel(val, edp->regs + AUX_CH_CTL_1);
+
+		/* Start AUX transaction */
+		retval = rockchip_edp_start_aux_transaction(edp);
+		if (retval == 0)
+			break;
+
+		dev_dbg(edp->dev, "Aux Transaction fail!\n");
+	}
+
+	/* Read data buffer */
+	val = readl(edp->regs + BUF_DATA_0);
+	*data = (unsigned char)(val & 0xff);
+
+	return retval;
+}
+
+int rockchip_edp_write_bytes_to_dpcd(struct rockchip_edp_device *edp,
+				     unsigned int val_addr,
+				     unsigned int count,
+				     unsigned char data[])
+{
+	u32 val;
+	unsigned int start_offset;
+	unsigned int cur_data_count;
+	unsigned int cur_data_idx;
+	int i;
+	int retval = 0;
+
+	/* Clear AUX CH data buffer */
+	val = BUF_CLR;
+	writel(val, edp->regs + BUFFER_DATA_CTL);
+
+	start_offset = 0;
+	while (start_offset < count) {
+		/* Buffer size of AUX CH is 16 * 4bytes */
+		if ((count - start_offset) > 16)
+			cur_data_count = 16;
+		else
+			cur_data_count = count - start_offset;
+
+		for (i = 0; i < 10; i++) {
+			/* Select DPCD device address */
+			val = AUX_ADDR_7_0(val_addr + start_offset);
+			writel(val, edp->regs + DP_AUX_ADDR_7_0);
+			val = AUX_ADDR_15_8(val_addr + start_offset);
+			writel(val, edp->regs + DP_AUX_ADDR_15_8);
+			val = AUX_ADDR_19_16(val_addr + start_offset);
+			writel(val, edp->regs + DP_AUX_ADDR_19_16);
+
+			for (cur_data_idx = 0; cur_data_idx < cur_data_count;
+			     cur_data_idx++) {
+				val = data[start_offset + cur_data_idx];
+				writel(val, edp->regs + BUF_DATA_0
+							  + 4 * cur_data_idx);
+			}
+
+			/*
+			 * Set DisplayPort transaction and write
+			 * If bit 3 is 1, DisplayPort transaction.
+			 * If Bit 3 is 0, I2C transaction.
+			 */
+			val = AUX_LENGTH(cur_data_count) |
+				AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE;
+			writel(val, edp->regs + AUX_CH_CTL_1);
+
+			/* Start AUX transaction */
+			retval = rockchip_edp_start_aux_transaction(edp);
+			if (retval == 0)
+				break;
+
+			dev_dbg(edp->dev, "Aux Transaction fail!\n");
+		}
+
+		start_offset += cur_data_count;
+	}
+
+	return retval;
+}
+
+int rockchip_edp_read_bytes_from_dpcd(struct rockchip_edp_device *edp,
+				      unsigned int val_addr,
+				      unsigned int count,
+				      unsigned char data[])
+{
+	u32 val;
+	unsigned int start_offset;
+	unsigned int cur_data_count;
+	unsigned int cur_data_idx;
+	int i;
+	int retval = 0;
+
+	/* Clear AUX CH data buffer */
+	val = BUF_CLR;
+	writel(val, edp->regs + BUFFER_DATA_CTL);
+
+	start_offset = 0;
+	while (start_offset < count) {
+		/* Buffer size of AUX CH is 16 * 4bytes */
+		if ((count - start_offset) > 16)
+			cur_data_count = 16;
+		else
+			cur_data_count = count - start_offset;
+
+		/* AUX CH Request Transaction process */
+		for (i = 0; i < 10; i++) {
+			/* Select DPCD device address */
+			val = AUX_ADDR_7_0(val_addr + start_offset);
+			writel(val, edp->regs + DP_AUX_ADDR_7_0);
+			val = AUX_ADDR_15_8(val_addr + start_offset);
+			writel(val, edp->regs + DP_AUX_ADDR_15_8);
+			val = AUX_ADDR_19_16(val_addr + start_offset);
+			writel(val, edp->regs + DP_AUX_ADDR_19_16);
+
+			/*
+			 * Set DisplayPort transaction and read
+			 * If bit 3 is 1, DisplayPort transaction.
+			 * If Bit 3 is 0, I2C transaction.
+			 */
+			val = AUX_LENGTH(cur_data_count) |
+				AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ;
+			writel(val, edp->regs + AUX_CH_CTL_1);
+
+			/* Start AUX transaction */
+			retval = rockchip_edp_start_aux_transaction(edp);
+			if (retval == 0)
+				break;
+
+			dev_dbg(edp->dev, "Aux Transaction fail!\n");
+		}
+
+		for (cur_data_idx = 0; cur_data_idx < cur_data_count;
+		    cur_data_idx++) {
+			val = readl(edp->regs + BUF_DATA_0
+						 + 4 * cur_data_idx);
+			data[start_offset + cur_data_idx] =
+				(unsigned char)val;
+		}
+
+		start_offset += cur_data_count;
+	}
+
+	return retval;
+}
+
+int rockchip_edp_select_i2c_device(struct rockchip_edp_device *edp,
+				   unsigned int device_addr,
+				   unsigned int val_addr)
+{
+	u32 val;
+	int retval;
+
+	/* Set EDID device address */
+	val = device_addr;
+	writel(val, edp->regs + DP_AUX_ADDR_7_0);
+	writel(0x0, edp->regs + DP_AUX_ADDR_15_8);
+	writel(0x0, edp->regs + DP_AUX_ADDR_19_16);
+
+	/* Set offset from base address of EDID device */
+	writel(val_addr, edp->regs + BUF_DATA_0);
+
+	/*
+	 * Set I2C transaction and write address
+	 * If bit 3 is 1, DisplayPort transaction.
+	 * If Bit 3 is 0, I2C transaction.
+	 */
+	val = AUX_TX_COMM_I2C_TRANSACTION | AUX_TX_COMM_MOT |
+		AUX_TX_COMM_WRITE;
+	writel(val, edp->regs + AUX_CH_CTL_1);
+
+	/* Start AUX transaction */
+	retval = rockchip_edp_start_aux_transaction(edp);
+	if (retval != 0)
+		dev_dbg(edp->dev, "Aux Transaction fail!\n");
+
+	return retval;
+}
+
+int rockchip_edp_read_byte_from_i2c(struct rockchip_edp_device *edp,
+				    unsigned int device_addr,
+				    unsigned int val_addr,
+				    unsigned int *data)
+{
+	u32 val;
+	int i;
+	int retval;
+
+	for (i = 0; i < 10; i++) {
+		/* Clear AUX CH data buffer */
+		val = BUF_CLR;
+		writel(val, edp->regs + BUFFER_DATA_CTL);
+
+		/* Select EDID device */
+		retval = rockchip_edp_select_i2c_device(edp,
+							device_addr,
+							val_addr);
+		if (retval != 0) {
+			dev_err(edp->dev, "Select EDID device fail!\n");
+			continue;
+		}
+
+		/*
+		 * Set I2C transaction and read data
+		 * If bit 3 is 1, DisplayPort transaction.
+		 * If Bit 3 is 0, I2C transaction.
+		 */
+		val = AUX_TX_COMM_I2C_TRANSACTION | AUX_TX_COMM_READ;
+		writel(val, edp->regs + AUX_CH_CTL_1);
+
+		/* Start AUX transaction */
+		retval = rockchip_edp_start_aux_transaction(edp);
+		if (retval == 0)
+			break;
+
+		dev_dbg(edp->dev, "Aux Transaction fail!\n");
+	}
+
+	/* Read data */
+	if (retval == 0)
+		*data = readl(edp->regs + BUF_DATA_0);
+
+	return retval;
+}
+
+int rockchip_edp_read_bytes_from_i2c(struct rockchip_edp_device *edp,
+				     unsigned int device_addr,
+				     unsigned int val_addr,
+				     unsigned int count,
+				     unsigned char edid[])
+{
+	u32 val;
+	unsigned int i, j;
+	unsigned int cur_data_idx;
+	unsigned int defer = 0;
+	int retval = 0;
+
+	for (i = 0; i < count; i += 16) {
+		for (j = 0; j < 100; j++) {
+			/* Clear AUX CH data buffer */
+			val = BUF_CLR;
+			writel(val, edp->regs + BUFFER_DATA_CTL);
+
+			/* Set normal AUX CH command */
+			val = readl(edp->regs + AUX_CH_CTL_2);
+			val &= ~ADDR_ONLY;
+			writel(val, edp->regs + AUX_CH_CTL_2);
+
+			/*
+			 * If Rx sends defer, Tx sends only reads
+			 * request without sending addres
+			 */
+			if (!defer)
+				retval = rockchip_edp_select_i2c_device(
+						edp, device_addr, val_addr + i);
+			else
+				defer = 0;
+
+			/*
+			 * Set I2C transaction and write data
+			 * If bit 3 is 1, DisplayPort transaction.
+			 * If Bit 3 is 0, I2C transaction.
+			 */
+			val = AUX_LENGTH(16) | AUX_TX_COMM_I2C_TRANSACTION |
+				AUX_TX_COMM_READ;
+			writel(val, edp->regs + AUX_CH_CTL_1);
+
+			/* Start AUX transaction */
+			retval = rockchip_edp_start_aux_transaction(edp);
+			if (retval == 0)
+				break;
+
+			dev_dbg(edp->dev, "Aux Transaction fail!\n");
+
+			/* Check if Rx sends defer */
+			val = readl(edp->regs + AUX_RX_COMM);
+			if (val == AUX_RX_COMM_AUX_DEFER ||
+			    val == AUX_RX_COMM_I2C_DEFER) {
+				dev_err(edp->dev, "Defer: %d\n\n", val);
+				defer = 1;
+			}
+		}
+
+		for (cur_data_idx = 0; cur_data_idx < 16; cur_data_idx++) {
+			val = readl(edp->regs + BUF_DATA_0 + 4 * cur_data_idx);
+			edid[i + cur_data_idx] = (unsigned char)val;
+		}
+	}
+
+	return retval;
+}
+
+void rockchip_edp_set_link_bandwidth(struct rockchip_edp_device *edp,
+				     u32 bwtype)
+{
+	u32 val;
+
+	val = bwtype;
+	if ((bwtype == DP_LINK_BW_2_7) || (bwtype == DP_LINK_BW_1_62))
+		writel(val, edp->regs + LINK_BW_SET);
+}
+
+void rockchip_edp_get_link_bandwidth(struct rockchip_edp_device *edp,
+				     u32 *bwtype)
+{
+	u32 val;
+
+	val = readl(edp->regs + LINK_BW_SET);
+	*bwtype = val;
+}
+
+void rockchip_edp_hw_link_training_en(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = HW_LT_EN;
+	writel(val, edp->regs + HW_LT_CTL);
+}
+
+int rockchip_edp_wait_hw_lt_done(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + DP_INT_STA);
+	if (val&HW_LT_DONE) {
+		writel(val, edp->regs + DP_INT_STA);
+		return 0;
+	}
+
+	return 1;
+}
+
+int rockchip_edp_get_hw_lt_status(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + HW_LT_CTL);
+
+	return (val & HW_LT_ERR_CODE_MASK) >> 4;
+}
+
+void rockchip_edp_set_lane_count(struct rockchip_edp_device *edp, u32 count)
+{
+	u32 val;
+
+	val = count;
+	writel(val, edp->regs + LANE_CNT_SET);
+}
+
+void rockchip_edp_get_lane_count(struct rockchip_edp_device *edp, u32 *count)
+{
+	u32 val;
+
+	val = readl(edp->regs + LANE_CNT_SET);
+	*count = val;
+}
+
+void rockchip_edp_enable_enhanced_mode(struct rockchip_edp_device *edp,
+				       bool enable)
+{
+	u32 val;
+
+	if (enable) {
+		val = readl(edp->regs + SYS_CTL_4);
+		val |= ENHANCED;
+		writel(val, edp->regs + SYS_CTL_4);
+	} else {
+		val = readl(edp->regs + SYS_CTL_4);
+		val &= ~ENHANCED;
+		writel(val, edp->regs + SYS_CTL_4);
+	}
+}
+
+void rockchip_edp_set_training_pattern(struct rockchip_edp_device *edp,
+				       enum pattern_set pattern)
+{
+	u32 val;
+
+	switch (pattern) {
+	case PRBS7:
+		val = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_PRBS7;
+		writel(val, edp->regs + TRAINING_PTN_SET);
+		break;
+	case D10_2:
+		val = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_D10_2;
+		writel(val, edp->regs + TRAINING_PTN_SET);
+		break;
+	case TRAINING_PTN1:
+		val = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN1;
+		writel(val, edp->regs + TRAINING_PTN_SET);
+		break;
+	case TRAINING_PTN2:
+		val = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN2;
+		writel(val, edp->regs + TRAINING_PTN_SET);
+		break;
+	case DP_NONE:
+		val = SCRAMBLING_ENABLE |
+			LINK_QUAL_PATTERN_SET_DISABLE |
+			SW_TRAINING_PATTERN_SET_DISABLE;
+		writel(val, edp->regs + TRAINING_PTN_SET);
+		break;
+	default:
+		break;
+	}
+}
+
+void rockchip_edp_set_lane0_pre_emphasis(struct rockchip_edp_device *edp,
+					 u32 level)
+{
+	u32 val;
+
+	val = level << PRE_EMPHASIS_SET_SHIFT;
+	writel(val, edp->regs + LN0_LINK_TRAINING_CTL);
+}
+
+void rockchip_edp_set_lane1_pre_emphasis(struct rockchip_edp_device *edp,
+					 u32 level)
+{
+	u32 val;
+
+	val = level << PRE_EMPHASIS_SET_SHIFT;
+	writel(val, edp->regs + LN1_LINK_TRAINING_CTL);
+}
+
+void rockchip_edp_set_lane2_pre_emphasis(struct rockchip_edp_device *edp,
+					 u32 level)
+{
+	u32 val;
+
+	val = level << PRE_EMPHASIS_SET_SHIFT;
+	writel(val, edp->regs + LN2_LINK_TRAINING_CTL);
+}
+
+void rockchip_edp_set_lane3_pre_emphasis(struct rockchip_edp_device *edp,
+					 u32 level)
+{
+	u32 val;
+
+	val = level << PRE_EMPHASIS_SET_SHIFT;
+	writel(val, edp->regs + LN3_LINK_TRAINING_CTL);
+}
+
+void rockchip_edp_set_lane0_link_training(struct rockchip_edp_device *edp,
+					  u32 training_lane)
+{
+	u32 val;
+
+	val = training_lane;
+	writel(val, edp->regs + LN0_LINK_TRAINING_CTL);
+}
+
+void rockchip_edp_set_lane1_link_training(struct rockchip_edp_device *edp,
+					  u32 training_lane)
+{
+	u32 val;
+
+	val = training_lane;
+	writel(val, edp->regs + LN1_LINK_TRAINING_CTL);
+}
+
+void rockchip_edp_set_lane2_link_training(struct rockchip_edp_device *edp,
+					  u32 training_lane)
+{
+	u32 val;
+
+	val = training_lane;
+	writel(val, edp->regs + LN2_LINK_TRAINING_CTL);
+}
+
+void rockchip_edp_set_lane3_link_training(struct rockchip_edp_device *edp,
+					  u32 training_lane)
+{
+	u32 val;
+
+	val = training_lane;
+	writel(val, edp->regs + LN3_LINK_TRAINING_CTL);
+}
+
+u32 rockchip_edp_get_lane0_link_training(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + LN0_LINK_TRAINING_CTL);
+	return val;
+}
+
+u32 rockchip_edp_get_lane1_link_training(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + LN1_LINK_TRAINING_CTL);
+	return val;
+}
+
+u32 rockchip_edp_get_lane2_link_training(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + LN2_LINK_TRAINING_CTL);
+	return val;
+}
+
+u32 rockchip_edp_get_lane3_link_training(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + LN3_LINK_TRAINING_CTL);
+	return val;
+}
+
+void rockchip_edp_reset_macro(struct rockchip_edp_device *edp)
+{
+}
+
+int rockchip_edp_init_video(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = VSYNC_DET | VID_FORMAT_CHG | VID_CLK_CHG;
+	writel(val, edp->regs + COMMON_INT_STA_1);
+
+	val = 0x0;
+	writel(val, edp->regs + SYS_CTL_1);
+
+	val = CHA_CRI(4) | CHA_CTRL;
+	writel(val, edp->regs + SYS_CTL_2);
+
+	val = VID_HRES_TH(2) | VID_VRES_TH(0);
+	writel(val, edp->regs + VIDEO_CTL_8);
+
+	return 0;
+}
+
+void rockchip_edp_set_video_color_format(struct rockchip_edp_device *edp,
+					 u32 color_dedpth,
+					 u32 color_space,
+					 u32 dynamic_range,
+					 u32 coeff)
+{
+	u32 val;
+
+	/* Configure the input color dedpth, color space, dynamic range */
+	val = (dynamic_range << IN_D_RANGE_SHIFT) |
+		(color_dedpth << IN_BPC_SHIFT) |
+		(color_space << IN_COLOR_F_SHIFT);
+	writel(val, edp->regs + VIDEO_CTL_2);
+
+	/* Set Input Color YCbCr Coefficients to ITU601 or ITU709 */
+	val = readl(edp->regs + VIDEO_CTL_3);
+	val &= ~IN_YC_COEFFI_MASK;
+	if (coeff)
+		val |= IN_YC_COEFFI_ITU709;
+	else
+		val |= IN_YC_COEFFI_ITU601;
+	writel(val, edp->regs + VIDEO_CTL_3);
+}
+
+int rockchip_edp_is_slave_video_stream_clock_on(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + SYS_CTL_1);
+	writel(val, edp->regs + SYS_CTL_1);
+
+	val = readl(edp->regs + SYS_CTL_1);
+
+	if (!(val & DET_STA)) {
+		dev_dbg(edp->dev, "Input stream clock not detected.\n");
+		return -EINVAL;
+	}
+
+	val = readl(edp->regs + SYS_CTL_2);
+	writel(val, edp->regs + SYS_CTL_2);
+
+	val = readl(edp->regs + SYS_CTL_2);
+	if (val & CHA_STA) {
+		dev_dbg(edp->dev, "Input stream clk is changing\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+void rockchip_edp_set_video_cr_mn(struct rockchip_edp_device *edp,
+				  enum clock_recovery_m_value_type type,
+				  u32 m_value,
+				  u32 n_value)
+{
+	u32 val;
+
+	if (type == REGISTER_M) {
+		val = readl(edp->regs + SYS_CTL_4);
+		val |= FIX_M_VID;
+		writel(val, edp->regs + SYS_CTL_4);
+		val = m_value & 0xff;
+		writel(val, edp->regs + M_VID_0);
+		val = (m_value >> 8) & 0xff;
+		writel(val, edp->regs + M_VID_1);
+		val = (m_value >> 16) & 0xff;
+		writel(val, edp->regs + M_VID_2);
+
+		val = n_value & 0xff;
+		writel(val, edp->regs + N_VID_0);
+		val = (n_value >> 8) & 0xff;
+		writel(val, edp->regs + N_VID_1);
+		val = (n_value >> 16) & 0xff;
+		writel(val, edp->regs + N_VID_2);
+	} else  {
+		val = readl(edp->regs + SYS_CTL_4);
+		val &= ~FIX_M_VID;
+		writel(val, edp->regs + SYS_CTL_4);
+
+		writel(0x00, edp->regs + N_VID_0);
+		writel(0x80, edp->regs + N_VID_1);
+		writel(0x00, edp->regs + N_VID_2);
+	}
+}
+
+void rockchip_edp_set_video_timing_mode(struct rockchip_edp_device *edp,
+					u32 type)
+{
+	u32 val;
+
+	if (type == VIDEO_TIMING_FROM_CAPTURE) {
+		val = readl(edp->regs + VIDEO_CTL_10);
+		val &= ~F_SEL;
+		writel(val, edp->regs + VIDEO_CTL_10);
+	} else {
+		val = readl(edp->regs + VIDEO_CTL_10);
+		val |= F_SEL;
+		writel(val, edp->regs + VIDEO_CTL_10);
+	}
+}
+
+int rockchip_edp_bist_cfg(struct rockchip_edp_device *edp)
+{
+	struct video_info *video_info = &edp->video_info;
+	struct drm_display_mode *mode = &edp->mode;
+	u16 x_total, y_total, x_act;
+	u32 val;
+
+	x_total = mode->htotal;
+	y_total = mode->vtotal;
+	x_act = mode->hdisplay;
+
+	rockchip_edp_set_video_cr_mn(edp, CALCULATED_M, 0, 0);
+	rockchip_edp_set_video_color_format(edp, video_info->color_depth,
+					    video_info->color_space,
+					    video_info->dynamic_range,
+					    video_info->ycbcr_coeff);
+
+	val = y_total & 0xff;
+	writel(val, edp->regs + TOTAL_LINE_CFG_L);
+	val = (y_total >> 8);
+	writel(val, edp->regs + TOTAL_LINE_CFG_H);
+	val = (mode->vdisplay & 0xff);
+	writel(val, edp->regs + ATV_LINE_CFG_L);
+	val = (mode->vdisplay >> 8);
+	writel(val, edp->regs + ATV_LINE_CFG_H);
+	val = (mode->vsync_start - mode->vdisplay);
+	writel(val, edp->regs + VF_PORCH_REG);
+	val = (mode->vsync_end - mode->vsync_start);
+	writel(val, edp->regs + VSYNC_CFG_REG);
+	val = (mode->vtotal - mode->vsync_end);
+	writel(val, edp->regs + VB_PORCH_REG);
+	val = x_total & 0xff;
+	writel(val, edp->regs + TOTAL_PIXELL_REG);
+	val = x_total >> 8;
+	writel(val, edp->regs + TOTAL_PIXELH_REG);
+	val = (x_act & 0xff);
+	writel(val, edp->regs + ATV_PIXELL_REG);
+	val = (x_act >> 8);
+	writel(val, edp->regs + ATV_PIXELH_REG);
+	val = (mode->hsync_start - mode->hdisplay) & 0xff;
+	writel(val, edp->regs + HF_PORCHL_REG);
+	val = (mode->hsync_start - mode->hdisplay) >> 8;
+	writel(val, edp->regs + HF_PORCHH_REG);
+	val = (mode->hsync_end - mode->hsync_start) & 0xff;
+	writel(val, edp->regs + HSYNC_CFGL_REG);
+	val = (mode->hsync_end - mode->hsync_start) >> 8;
+	writel(val, edp->regs + HSYNC_CFGH_REG);
+	val = (mode->htotal - mode->hsync_end) & 0xff;
+	writel(val, edp->regs + HB_PORCHL_REG);
+	val = (mode->htotal - mode->hsync_end)  >> 8;
+	writel(val, edp->regs + HB_PORCHH_REG);
+
+	val = BIST_EN | BIST_WH_64 | BIST_TYPE_COLR_BAR;
+	writel(val, edp->regs + VIDEO_CTL_4);
+
+	val = readl(edp->regs + VIDEO_CTL_10);
+	val &= ~F_SEL;
+	writel(val, edp->regs + VIDEO_CTL_10);
+	return 0;
+}
+
+void rockchip_edp_enable_video_master(struct rockchip_edp_device *edp,
+				      bool enable)
+{
+}
+
+void rockchip_edp_start_video(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + VIDEO_CTL_1);
+	val |= VIDEO_EN;
+	writel(val, edp->regs + VIDEO_CTL_1);
+}
+
+int rockchip_edp_is_video_stream_on(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + SYS_CTL_3);
+	writel(val, edp->regs + SYS_CTL_3);
+
+	val = readl(edp->regs + SYS_CTL_3);
+	if (!(val & STRM_VALID)) {
+		dev_dbg(edp->dev, "Input video stream is not detected.\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+void rockchip_edp_config_video_slave_mode(struct rockchip_edp_device *edp,
+					  struct video_info *video_info)
+{
+	u32 val;
+
+	val = readl(edp->regs + FUNC_EN_1);
+	val &= ~(VID_FIFO_FUNC_EN_N | VID_CAP_FUNC_EN_N);
+	writel(val, edp->regs + FUNC_EN_1);
+
+	val = readl(edp->regs + VIDEO_CTL_10);
+	val &= ~INTERACE_SCAN_CFG;
+	val |= (video_info->interlaced << 2);
+	writel(val, edp->regs + VIDEO_CTL_10);
+
+	val = readl(edp->regs + VIDEO_CTL_10);
+	val &= ~VSYNC_POLARITY_CFG;
+	val |= (video_info->v_sync_polarity << 1);
+	writel(val, edp->regs + VIDEO_CTL_10);
+
+	val = readl(edp->regs + VIDEO_CTL_10);
+	val &= ~HSYNC_POLARITY_CFG;
+	val |= (video_info->h_sync_polarity << 0);
+	writel(val, edp->regs + VIDEO_CTL_10);
+}
+
+void rockchip_edp_enable_scrambling(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + TRAINING_PTN_SET);
+	val &= ~SCRAMBLING_DISABLE;
+	writel(val, edp->regs + TRAINING_PTN_SET);
+}
+
+void rockchip_edp_disable_scrambling(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + TRAINING_PTN_SET);
+	val |= SCRAMBLING_DISABLE;
+	writel(val, edp->regs + TRAINING_PTN_SET);
+}
+
+enum dp_irq_type rockchip_edp_get_irq_type(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	/* Parse hotplug interrupt status register */
+	val = readl(edp->regs + COMMON_INT_STA_4);
+	if (val & PLUG)
+		return DP_IRQ_TYPE_HP_CABLE_IN;
+
+	if (val & HPD_LOST)
+		return DP_IRQ_TYPE_HP_CABLE_OUT;
+
+	if (val & HOTPLUG_CHG)
+		return DP_IRQ_TYPE_HP_CHANGE;
+
+	return DP_IRQ_TYPE_UNKNOWN;
+}
+
+void rockchip_edp_clear_hotplug_interrupts(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = HOTPLUG_CHG | HPD_LOST | PLUG;
+	writel(val, edp->regs + COMMON_INT_STA_4);
+
+	val = INT_HPD;
+	writel(val, edp->regs + DP_INT_STA);
+}
diff --git a/drivers/gpu/drm/rockchip/rockchip_edp_reg.h b/drivers/gpu/drm/rockchip/rockchip_edp_reg.h
new file mode 100644
index 0000000..b50dd47
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_edp_reg.h
@@ -0,0 +1,345 @@
+/*
+* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+* Author:
+*      Andy yan <andy.yan@rock-chips.com>
+*      Jeff chen <jeff.chen@rock-chips.com>
+*
+* based on exynos_dp_reg.h
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*/
+
+#ifndef _ROCKCHIP_EDP_REG_H
+#define _ROCKCHIP_EDP_REG_H
+
+#include <linux/bitops.h>
+
+#define TX_SW_RST				0x14
+#define FUNC_EN_1				0x18
+#define FUNC_EN_2				0x1C
+#define VIDEO_CTL_1				0x20
+#define VIDEO_CTL_2				0x24
+#define VIDEO_CTL_3				0x28
+#define VIDEO_CTL_4				0x2c
+#define VIDEO_CTL_8				0x3C
+#define VIDEO_CTL_10				0x44
+#define TOTAL_LINE_CFG_L			0x48
+#define TOTAL_LINE_CFG_H			0x4c
+#define ATV_LINE_CFG_L				0x50
+#define ATV_LINE_CFG_H				0x54
+#define VF_PORCH_REG				0x58
+#define VSYNC_CFG_REG				0x5c
+#define VB_PORCH_REG				0x60
+#define TOTAL_PIXELL_REG			0x64
+#define TOTAL_PIXELH_REG			0x68
+#define ATV_PIXELL_REG				0x6c
+#define ATV_PIXELH_REG				0x70
+#define HF_PORCHL_REG				0x74
+#define HF_PORCHH_REG				0x78
+#define HSYNC_CFGL_REG				0x7c
+#define HSYNC_CFGH_REG				0x80
+#define HB_PORCHL_REG				0x84
+#define HB_PORCHH_REG				0x88
+#define PLL_REG_1				0xfc
+
+#define SSC_REG					0x104
+#define TX_REG_COMMON				0x114
+#define DP_AUX					0x120
+#define DP_BIAS					0x124
+#define DP_PWRDN				0x12c
+#define DP_RESERVE2				0x134
+
+#define LANE_MAP				0x35C
+#define ANALOG_CTL_2				0x374
+#define AUX_HW_RETRY_CTL			0x390
+#define COMMON_INT_STA_1			0x3C4
+#define COMMON_INT_STA_2			0x3C8
+#define COMMON_INT_STA_3			0x3CC
+#define COMMON_INT_STA_4			0x3D0
+#define DP_INT_STA				0x3DC
+#define COMMON_INT_MASK_1			0x3E0
+#define COMMON_INT_MASK_2			0x3E4
+#define COMMON_INT_MASK_3			0x3E8
+#define COMMON_INT_MASK_4			0x3EC
+#define DP_INT_STA_MASK				0x3F8
+
+#define SYS_CTL_1				0x600
+#define SYS_CTL_2				0x604
+#define SYS_CTL_3				0x608
+#define SYS_CTL_4				0x60C
+#define PKT_SEND_CTL				0x640
+#define HDCP_CTL				0x648
+#define LINK_BW_SET				0x680
+#define LANE_CNT_SET				0x684
+#define TRAINING_PTN_SET			0x688
+#define LN0_LINK_TRAINING_CTL			0x68C
+#define LN1_LINK_TRAINING_CTL			0x690
+#define LN2_LINK_TRAINING_CTL			0x694
+#define LN3_LINK_TRAINING_CTL			0x698
+#define HW_LT_CTL				0x6a0
+#define DEBUG_CTL				0x6C0
+#define HPD_DEGLITCH_L				0x6C4
+#define HPD_DEGLITCH_H				0x6C8
+#define LINK_DEBUG_CTL				0x6E0
+#define M_VID_0					0x700
+#define M_VID_1					0x704
+#define M_VID_2					0x708
+#define N_VID_0					0x70C
+#define N_VID_1					0x710
+#define N_VID_2					0x714
+#define VIDEO_FIFO_THRD				0x730
+#define AUDIO_MARGIN				0x73C
+#define M_VID_GEN_FILTER_TH			0x764
+#define M_AUD_GEN_FILTER_TH			0x778
+#define AUX_CH_STA				0x780
+#define AUX_CH_DEFER_CTL			0x788
+#define AUX_RX_COMM				0x78C
+#define BUFFER_DATA_CTL				0x790
+#define AUX_CH_CTL_1				0x794
+#define DP_AUX_ADDR_7_0				0x798
+#define DP_AUX_ADDR_15_8			0x79C
+#define DP_AUX_ADDR_19_16			0x7A0
+#define AUX_CH_CTL_2				0x7A4
+#define BUF_DATA_0				0x7C0
+#define SOC_GENERAL_CTL				0x800
+#define PLL_REG_2				0x9e4
+#define PLL_REG_3				0x9e8
+#define PLL_REG_4				0x9ec
+#define PLL_REG_5				0xa00
+
+/* ROCKCHIP_EDP_FUNC_EN_1 */
+#define VID_CAP_FUNC_EN_N			BIT(6)
+#define VID_FIFO_FUNC_EN_N			BIT(5)
+#define AUD_FIFO_FUNC_EN_N			BIT(4)
+#define AUD_FUNC_EN_N				BIT(3)
+#define HDCP_FUNC_EN_N				BIT(2)
+#define SW_FUNC_EN_N				BIT(0)
+
+/* ROCKCHIP_EDP_FUNC_EN_2 */
+#define SSC_FUNC_EN_N				BIT(7)
+#define AUX_FUNC_EN_N				BIT(2)
+#define SERDES_FIFO_FUNC_EN_N			BIT(1)
+#define LS_CLK_DOMAIN_FUNC_EN_N			BIT(0)
+
+/* ROCKCHIP_EDP_VIDEO_CTL_1 */
+#define VIDEO_EN				BIT(7)
+#define VIDEO_MUTE				BIT(6)
+
+/* ROCKCHIP_EDP_VIDEO_CTL_1 */
+#define IN_D_RANGE_MASK				(0x1 << 7)
+#define IN_D_RANGE_SHIFT			(7)
+#define IN_D_RANGE_CEA				(0x1 << 7)
+#define IN_D_RANGE_VESA				(0x0 << 7)
+#define IN_BPC_MASK				(0x7 << 4)
+#define IN_BPC_SHIFT				(4)
+#define IN_BPC_12_BITS				(0x3 << 4)
+#define IN_BPC_10_BITS				(0x2 << 4)
+#define IN_BPC_8_BITS				(0x1 << 4)
+#define IN_BPC_6_BITS				(0x0 << 4)
+#define IN_COLOR_F_MASK				(0x3 << 0)
+#define IN_COLOR_F_SHIFT			(0)
+#define IN_COLOR_F_YCBCR444			(0x2 << 0)
+#define IN_COLOR_F_YCBCR422			(0x1 << 0)
+#define IN_COLOR_F_RGB				(0x0 << 0)
+
+/* ROCKCHIP_EDP_VIDEO_CTL_3 */
+#define IN_YC_COEFFI_MASK			(0x1 << 7)
+#define IN_YC_COEFFI_SHIFT			(7)
+#define IN_YC_COEFFI_ITU709			(0x1 << 7)
+#define IN_YC_COEFFI_ITU601			(0x0 << 7)
+#define VID_CHK_UPDATE_TYPE_MASK		(0x1 << 4)
+#define VID_CHK_UPDATE_TYPE_SHIFT		(4)
+#define VID_CHK_UPDATE_TYPE_1			(0x1 << 4)
+#define VID_CHK_UPDATE_TYPE_0			(0x0 << 4)
+
+/* ROCKCHIP_EDP_VIDEO_CTL_4 */
+#define BIST_EN					(0x1 << 3)
+#define BIST_WH_64				(0x1 << 2)
+#define BIST_WH_32				(0x0 << 2)
+#define BIST_TYPE_COLR_BAR			(0x0 << 0)
+#define BIST_TYPE_GRAY_BAR			(0x1 << 0)
+#define BIST_TYPE_MOBILE_BAR			(0x2 << 0)
+
+/* ROCKCHIP_EDP_VIDEO_CTL_8 */
+#define VID_HRES_TH(x)				(((x) & 0xf) << 4)
+#define VID_VRES_TH(x)				(((x) & 0xf) << 0)
+
+/* ROCKCHIP_EDP_VIDEO_CTL_10 */
+#define F_SEL					(0x1 << 4)
+#define INTERACE_SCAN_CFG			(0x1 << 2)
+#define VSYNC_POLARITY_CFG			(0x1 << 1)
+#define HSYNC_POLARITY_CFG			(0x1 << 0)
+
+/* ROCKCHIP_EDP_PLL_REG_1 */
+#define REF_CLK_24M				(0x1 << 1)
+#define REF_CLK_27M				(0x0 << 1)
+
+/* ROCKCHIP_EDP_DP_PWRDN */
+#define PD_INC_BG				BIT(7)
+#define PD_EXP_BG				BIT(6)
+#define PD_AUX					BIT(5)
+#define PD_PLL					BIT(4)
+#define PD_CH3					BIT(3)
+#define PD_CH2					BIT(2)
+#define PD_CH1					BIT(1)
+#define PD_CH0					BIT(0)
+
+/* ROCKCHIP_EDP_LANE_MAP */
+#define LANE3_MAP_LOGIC_LANE_0			(0x0 << 6)
+#define LANE3_MAP_LOGIC_LANE_1			(0x1 << 6)
+#define LANE3_MAP_LOGIC_LANE_2			(0x2 << 6)
+#define LANE3_MAP_LOGIC_LANE_3			(0x3 << 6)
+#define LANE2_MAP_LOGIC_LANE_0			(0x0 << 4)
+#define LANE2_MAP_LOGIC_LANE_1			(0x1 << 4)
+#define LANE2_MAP_LOGIC_LANE_2			(0x2 << 4)
+#define LANE2_MAP_LOGIC_LANE_3			(0x3 << 4)
+#define LANE1_MAP_LOGIC_LANE_0			(0x0 << 2)
+#define LANE1_MAP_LOGIC_LANE_1			(0x1 << 2)
+#define LANE1_MAP_LOGIC_LANE_2			(0x2 << 2)
+#define LANE1_MAP_LOGIC_LANE_3			(0x3 << 2)
+#define LANE0_MAP_LOGIC_LANE_0			(0x0 << 0)
+#define LANE0_MAP_LOGIC_LANE_1			(0x1 << 0)
+#define LANE0_MAP_LOGIC_LANE_2			(0x2 << 0)
+#define LANE0_MAP_LOGIC_LANE_3			(0x3 << 0)
+
+/* ROCKCHIP_EDP_ANALOG_CTL_2 */
+#define SEL_24M					(0x1 << 3)
+
+/* ROCKCHIP_EDP_COMMON_INT_STA_1 */
+#define VSYNC_DET				BIT(7)
+#define PLL_LOCK_CHG				BIT(6)
+#define SPDIF_ERR				BIT(5)
+#define SPDIF_UNSTBL				BIT(4)
+#define VID_FORMAT_CHG				BIT(3)
+#define AUD_CLK_CHG				BIT(2)
+#define VID_CLK_CHG				BIT(1)
+#define SW_INT					BIT(0)
+
+/* ROCKCHIP_EDP_COMMON_INT_STA_2 */
+#define ENC_EN_CHG				BIT(6)
+#define HW_BKSV_RDY				BIT(3)
+#define HW_SHA_DONE				BIT(2)
+#define HW_AUTH_STATE_CHG			BIT(1)
+#define HW_AUTH_DONE				BIT(0)
+
+/* ROCKCHIP_EDP_COMMON_INT_STA_3 */
+#define AFIFO_UNDER				BIT(7)
+#define AFIFO_OVER				BIT(6)
+#define R0_CHK_FLAG				BIT(5)
+
+/* ROCKCHIP_EDP_COMMON_INT_STA_4 */
+#define PSR_ACTIVE				BIT(7)
+#define PSR_INACTIVE				BIT(6)
+#define SPDIF_BI_PHASE_ERR			BIT(5)
+#define HOTPLUG_CHG				BIT(2)
+#define HPD_LOST				BIT(1)
+#define PLUG					BIT(0)
+
+/* ROCKCHIP_EDP_INT_STA */
+#define INT_HPD					BIT(6)
+#define HW_LT_DONE				BIT(5)
+#define SINK_LOST				BIT(3)
+#define LINK_LOST				BIT(2)
+#define RPLY_RECEIV				BIT(1)
+#define AUX_ERR					BIT(0)
+
+/* ROCKCHIP_EDP_INT_CTL */
+#define INT_CTL					0x3FC
+#define SOFT_INT_CTRL				BIT(2)
+#define INT_POL					BIT(0)
+
+/* ROCKCHIP_EDP_SYS_CTL_1 */
+#define DET_STA					BIT(2)
+#define FORCE_DET				BIT(1)
+#define DET_CTRL				BIT(0)
+
+/* ROCKCHIP_EDP_SYS_CTL_2 */
+#define CHA_CRI(x)				(((x) & 0xf) << 4)
+#define CHA_STA					BIT(2)
+#define FORCE_CHA				BIT(1)
+#define CHA_CTRL				BIT(0)
+
+/* ROCKCHIP_EDP_SYS_CTL_3 */
+#define HPD_STATUS				BIT(6)
+#define F_HPD					BIT(5)
+#define HPD_CTRL				BIT(4)
+#define HDCP_RDY				BIT(3)
+#define STRM_VALID				BIT(2)
+#define F_VALID					BIT(1)
+#define VALID_CTRL				BIT(0)
+
+/* ROCKCHIP_EDP_SYS_CTL_4 */
+#define FIX_M_AUD				BIT(4)
+#define ENHANCED				BIT(3)
+#define FIX_M_VID				BIT(2)
+#define M_VID_UPDATE_CTRL			BIT(0)
+
+/* ROCKCHIP_EDP_TRAINING_PTN_SET */
+#define SCRAMBLING_DISABLE			(0x1 << 5)
+#define SCRAMBLING_ENABLE			(0x0 << 5)
+#define LINK_QUAL_PATTERN_SET_MASK		(0x7 << 2)
+#define LINK_QUAL_PATTERN_SET_PRBS7		(0x3 << 2)
+#define LINK_QUAL_PATTERN_SET_D10_2		(0x1 << 2)
+#define LINK_QUAL_PATTERN_SET_DISABLE		(0x0 << 2)
+#define SW_TRAINING_PATTERN_SET_MASK		(0x3 << 0)
+#define SW_TRAINING_PATTERN_SET_PTN2		(0x2 << 0)
+#define SW_TRAINING_PATTERN_SET_PTN1		(0x1 << 0)
+#define SW_TRAINING_PATTERN_SET_DISABLE		(0x0 << 0)
+
+/* ROCKCHIP_EDP_HW_LT_CTL */
+#define HW_LT_ERR_CODE_MASK			0x70
+#define HW_LT_EN				BIT(0)
+
+/* ROCKCHIP_EDP_LN0_LINK_TRAINING_CTL */
+#define PRE_EMPHASIS_SET_MASK			(0x3 << 3)
+#define PRE_EMPHASIS_SET_SHIFT			(3)
+
+/* ROCKCHIP_EDP_DEBUG_CTL */
+#define PLL_LOCK				BIT(4)
+#define F_PLL_LOCK				BIT(3)
+#define PLL_LOCK_CTRL				BIT(2)
+#define POLL_EN					BIT(1)
+#define PN_INV					BIT(0)
+
+/* ROCKCHIP_EDP_AUX_CH_STA */
+#define AUX_BUSY				(0x1 << 4)
+#define AUX_STATUS_MASK				(0xf << 0)
+
+/* ROCKCHIP_EDP_AUX_CH_DEFER_CTL */
+#define DEFER_CTRL_EN				(0x1 << 7)
+#define DEFER_COUNT(x)				(((x) & 0x7f) << 0)
+
+/* ROCKCHIP_EDP_AUX_RX_COMM */
+#define AUX_RX_COMM_I2C_DEFER			(0x2 << 2)
+#define AUX_RX_COMM_AUX_DEFER			(0x2 << 0)
+
+/* ROCKCHIP_EDP_BUFFER_DATA_CTL */
+#define BUF_CLR					(0x1 << 7)
+#define BUF_DATA_COUNT(x)			(((x) & 0xf) << 0)
+
+/* ROCKCHIP_EDP_AUX_CH_CTL_1 */
+#define AUX_LENGTH(x)				(((x - 1) & 0xf) << 4)
+#define AUX_TX_COMM_MASK			(0xf << 0)
+#define AUX_TX_COMM_DP_TRANSACTION		(0x1 << 3)
+#define AUX_TX_COMM_I2C_TRANSACTION		(0x0 << 3)
+#define AUX_TX_COMM_MOT				(0x1 << 2)
+#define AUX_TX_COMM_WRITE			(0x0 << 0)
+#define AUX_TX_COMM_READ			(0x1 << 0)
+
+/* OCKCHIP_EDP_AUX_ADDR_7_0 */
+#define AUX_ADDR_7_0(x)			(((x) >> 0) & 0xff)
+
+/* ROCKCHIP_EDP_AUX_ADDR_15_8 */
+#define AUX_ADDR_15_8(x)		(((x) >> 8) & 0xff)
+
+/* ROCKCHIP_EDP_AUX_ADDR_19_16 */
+#define AUX_ADDR_19_16(x)		(((x) >> 16) & 0x0f)
+
+/* ROCKCHIP_EDP_AUX_CH_CTL_2 */
+#define ADDR_ONLY				BIT(1)
+#define AUX_EN					BIT(0)
+
+#endif /* _ROCKCHIP_EDP_REG_H */
-- 
1.7.9.5



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

* [PATCH v2 5/5] drm/rockchip: Add support for Rockchip Soc EDP
@ 2014-09-18  9:42     ` Mark yao
  0 siblings, 0 replies; 38+ messages in thread
From: Mark yao @ 2014-09-18  9:42 UTC (permalink / raw)
  To: heiko-4mtYJXux2i+zQB+pC5nmwQ, Boris BREZILLON, David Airlie,
	Rob Clark, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, Randy Dunlap, Grant Likely, Greg Kroah-Hartman,
	John Stultz, Rom Lemarchand
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-api-u79uwXL29TY76Z2rM5mHXA,
	linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	dianders-F7+t8E8rja9g9hUCZPvPmw, marcheu-F7+t8E8rja9g9hUCZPvPmw,
	dbehr-F7+t8E8rja9g9hUCZPvPmw, olof-nZhT3qVonbNeoWH0uzbU5w,
	djkurtz-F7+t8E8rja9g9hUCZPvPmw, xjq-TNX95d0MmH7DzftRWevZcw,
	kfx-TNX95d0MmH7DzftRWevZcw, cym-TNX95d0MmH7DzftRWevZcw,
	cf-TNX95d0MmH7DzftRWevZcw, zyw-TNX95d0MmH7DzftRWevZcw,
	xxm-TNX95d0MmH7DzftRWevZcw, huangtao-TNX95d0MmH7DzftRWevZcw,
	kever.yang-TNX95d0MmH7DzftRWevZcw, yxj-TNX95d0MmH7DzftRWevZcw,
	wxt-TNX95d0MmH7DzftRWevZcw, xw-TNX95d0MmH7DzftRWevZcw, Mark yao,
	Jeff Chen

This adds support for Rockchip soc edp found on rk3288

Signed-off-by: Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Signed-off-by: Jeff Chen <jeff.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
change in v2:
- fix code sytle
- use some define from drm_dp_helper.h
- use panel-simple driver for primary display.
- remove unnecessary clock clk_24m_parent.

 drivers/gpu/drm/rockchip/Kconfig             |    9 +
 drivers/gpu/drm/rockchip/Makefile            |    2 +
 drivers/gpu/drm/rockchip/rockchip_edp_core.c |  853 ++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_edp_core.h |  309 +++++++
 drivers/gpu/drm/rockchip/rockchip_edp_reg.c  | 1202 ++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_edp_reg.h  |  345 ++++++++
 6 files changed, 2720 insertions(+)
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_edp_core.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_edp_core.h
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_edp_reg.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_edp_reg.h

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 7146c80..04b1f8c 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -17,3 +17,12 @@ config DRM_ROCKCHIP
 	  management to userspace. This driver does not provides
 	  2D or 3D acceleration; acceleration is performed by other
 	  IP found on the SoC.
+
+config ROCKCHIP_EDP
+	bool "Rockchip edp support"
+	depends on DRM_ROCKCHIP
+	help
+	  Choose this option if you have a Rockchip eDP.
+	  Rockchip rk3288 SoC has eDP TX Controller can be used.
+	  If you have an Embedded DisplayPort Panel, say Y to enable its
+	  driver.
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index 6e6d468..a0fc3a1 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -7,4 +7,6 @@ ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/rockchip
 rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \
 		rockchip_drm_gem.o rockchip_drm_vop.o
 
+rockchipdrm-$(CONFIG_ROCKCHIP_EDP) += rockchip_edp_core.o rockchip_edp_reg.o
+
 obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o
diff --git a/drivers/gpu/drm/rockchip/rockchip_edp_core.c b/drivers/gpu/drm/rockchip/rockchip_edp_core.c
new file mode 100644
index 0000000..4d9caee
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_edp_core.c
@@ -0,0 +1,853 @@
+/*
+* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+* Author:
+*      Andy yan <andy.yan-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+*      Jeff chen <jeff.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+*
+* based on exynos_dp_core.c
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*/
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_of.h>
+
+#include <linux/component.h>
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#include <video/of_videomode.h>
+#include <video/videomode.h>
+
+#include "rockchip_edp_core.h"
+
+#define connector_to_edp(c) \
+		container_of(c, struct rockchip_edp_device, connector)
+
+#define encoder_to_edp(c) \
+		container_of(c, struct rockchip_edp_device, encoder)
+
+static struct rockchip_edp_soc_data soc_data[2] = {
+	/* rk3288 */
+	{.grf_soc_con6 = 0x025c,
+	 .grf_soc_con12 = 0x0274},
+	/* no edp switching needed */
+	{.grf_soc_con6 = -1,
+	 .grf_soc_con12 = -1},
+};
+
+static const struct of_device_id rockchip_edp_dt_ids[] = {
+	{.compatible = "rockchip,rk3288-edp",
+	 .data = (void *)&soc_data[0] },
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, rockchip_edp_dt_ids);
+
+static int rockchip_edp_clk_enable(struct rockchip_edp_device *edp)
+{
+	int ret = 0;
+
+	if (!edp->clk_on) {
+		ret = clk_prepare_enable(edp->pclk);
+		if (ret < 0) {
+			dev_err(edp->dev, "cannot enable edp pclk %d\n", ret);
+			goto err_pclk;
+		}
+
+		ret = clk_prepare_enable(edp->clk_edp);
+		if (ret < 0) {
+			dev_err(edp->dev, "cannot enable clk_edp %d\n", ret);
+			goto err_clk_edp;
+		}
+
+		ret = clk_set_rate(edp->clk_24m, 24000000);
+		if (ret < 0) {
+			dev_err(edp->dev, "cannot set edp clk_24m %d\n",
+				ret);
+			goto err_clk_24m;
+		}
+
+		ret = clk_prepare_enable(edp->clk_24m);
+		if (ret < 0) {
+			dev_err(edp->dev, "cannot enable edp clk_24m %d\n",
+				ret);
+			goto err_clk_24m;
+		}
+
+		edp->clk_on = true;
+	}
+
+	return 0;
+
+err_clk_24m:
+	clk_disable_unprepare(edp->clk_edp);
+err_clk_edp:
+	clk_disable_unprepare(edp->pclk);
+err_pclk:
+	edp->clk_on = false;
+
+	return ret;
+}
+
+static int rockchip_edp_clk_disable(struct rockchip_edp_device *edp)
+{
+	if (edp->clk_on) {
+		clk_disable_unprepare(edp->pclk);
+		clk_disable_unprepare(edp->clk_edp);
+		clk_disable_unprepare(edp->clk_24m);
+		edp->clk_on = false;
+	}
+
+	return 0;
+}
+
+static int rockchip_edp_pre_init(struct rockchip_edp_device *edp)
+{
+	u32 val;
+	int ret;
+
+	val = GRF_EDP_REF_CLK_SEL_INTER | (GRF_EDP_REF_CLK_SEL_INTER << 16);
+	ret = regmap_write(edp->grf, edp->soc_data->grf_soc_con12, val);
+	if (ret != 0) {
+		dev_err(edp->dev, "Could not write to GRF: %d\n", ret);
+		return ret;
+	}
+
+	reset_control_assert(edp->rst);
+	usleep_range(10, 20);
+	reset_control_deassert(edp->rst);
+
+	return 0;
+}
+
+static int rockchip_edp_init_edp(struct rockchip_edp_device *edp)
+{
+	rockchip_edp_reset(edp);
+	rockchip_edp_init_refclk(edp);
+	rockchip_edp_init_interrupt(edp);
+	rockchip_edp_enable_sw_function(edp);
+	rockchip_edp_init_analog_func(edp);
+	rockchip_edp_init_hpd(edp);
+	rockchip_edp_init_aux(edp);
+
+	return 0;
+}
+
+static int rockchip_edp_get_max_rx_bandwidth(
+					struct rockchip_edp_device *edp,
+					u8 *bandwidth)
+{
+	u8 data;
+	int retval;
+
+	/*
+	 * For DP rev.1.1, Maximum link rate of Main Link lanes
+	 * 0x06 = 1.62 Gbps, 0x0a = 2.7 Gbps
+	 */
+	retval = rockchip_edp_read_byte_from_dpcd(
+			edp, DP_MAX_LINK_RATE, &data);
+	if (retval < 0)
+		*bandwidth = 0;
+	else
+		*bandwidth = data;
+
+	return retval;
+}
+
+static int rockchip_edp_get_max_rx_lane_count(struct rockchip_edp_device *edp,
+					      u8 *lane_count)
+{
+	u8 data;
+	int retval;
+
+	/*
+	 * For DP rev.1.1, Maximum number of Main Link lanes
+	 * 0x01 = 1 lane, 0x02 = 2 lanes, 0x04 = 4 lanes
+	 */
+	retval = rockchip_edp_read_byte_from_dpcd(
+			edp, DP_MAX_LANE_COUNT, &data);
+	if (retval < 0)
+		*lane_count = 0;
+	else
+		*lane_count = DPCD_MAX_LANE_COUNT(data);
+
+	return retval;
+}
+
+static int rockchip_edp_init_training(struct rockchip_edp_device *edp)
+{
+	int retval;
+
+	/*
+	 * MACRO_RST must be applied after the PLL_LOCK to avoid
+	 * the DP inter pair skew issue for at least 10 us
+	 */
+	rockchip_edp_reset_macro(edp);
+
+	retval = rockchip_edp_get_max_rx_bandwidth(
+				edp, &edp->link_train.link_rate);
+	retval = rockchip_edp_get_max_rx_lane_count(
+				edp, &edp->link_train.lane_count);
+	dev_dbg(edp->dev, "max link rate:%d.%dGps max number of lanes:%d\n",
+		edp->link_train.link_rate * 27 / 100,
+		edp->link_train.link_rate * 27 % 100,
+		edp->link_train.lane_count);
+
+	if ((edp->link_train.link_rate != DP_LINK_BW_1_62) &&
+	    (edp->link_train.link_rate != DP_LINK_BW_2_7)) {
+		dev_warn(edp->dev, "Rx Max Link Rate is abnormal :%x !\n"
+			 "use default link rate:%d.%dGps\n",
+			 edp->link_train.link_rate,
+			 edp->video_info.link_rate * 27 / 100,
+			 edp->video_info.link_rate * 27 % 100);
+			 edp->link_train.link_rate = edp->video_info.link_rate;
+	}
+
+	if (edp->link_train.lane_count == 0) {
+		dev_err(edp->dev, "Rx Max Lane count is abnormal :%x !\n"
+			"use default lanes:%d\n",
+			edp->link_train.lane_count,
+			edp->video_info.lane_count);
+		edp->link_train.lane_count = edp->video_info.lane_count;
+	}
+
+	rockchip_edp_analog_power_ctr(edp, 1);
+
+	return 0;
+}
+
+static int rockchip_edp_hw_link_training(struct rockchip_edp_device *edp)
+{
+	u32 cnt = 50;
+	u32 val;
+
+	/* Set link rate and count as you want to establish*/
+	rockchip_edp_set_link_bandwidth(edp, edp->link_train.link_rate);
+	rockchip_edp_set_lane_count(edp, edp->link_train.lane_count);
+	rockchip_edp_hw_link_training_en(edp);
+	val = rockchip_edp_wait_hw_lt_done(edp);
+	while (val) {
+		if (cnt-- <= 0) {
+			dev_err(edp->dev, "hw lt timeout");
+			return -ETIMEDOUT;
+		}
+		mdelay(1);
+		val = rockchip_edp_wait_hw_lt_done(edp);
+	}
+
+	val = rockchip_edp_get_hw_lt_status(edp);
+	if (val)
+		dev_err(edp->dev, "hw lt err:%d\n", val);
+
+	return val;
+}
+
+static int rockchip_edp_set_link_train(struct rockchip_edp_device *edp)
+{
+	int retval;
+
+	rockchip_edp_init_training(edp);
+
+	retval = rockchip_edp_hw_link_training(edp);
+	if (retval < 0)
+		dev_err(edp->dev, "DP hw LT failed!\n");
+
+	return retval;
+}
+
+static int rockchip_edp_config_video(struct rockchip_edp_device *edp,
+				     struct video_info *video_info)
+{
+	int retval = 0;
+	int timeout_loop = 0;
+	int done_count = 0;
+
+	rockchip_edp_config_video_slave_mode(edp, video_info);
+
+	rockchip_edp_set_video_color_format(edp, video_info->color_depth,
+					    video_info->color_space,
+					    video_info->dynamic_range,
+					    video_info->ycbcr_coeff);
+
+	if (rockchip_edp_get_pll_lock_status(edp) == DP_PLL_UNLOCKED) {
+		dev_err(edp->dev, "PLL is not locked yet.\n");
+		return -EINVAL;
+	}
+
+	for (;;) {
+		timeout_loop++;
+		if (rockchip_edp_is_slave_video_stream_clock_on(edp) == 0)
+			break;
+
+		if (DP_TIMEOUT_LOOP_CNT < timeout_loop) {
+			dev_err(edp->dev, "Timeout of video streamclk ok\n");
+			return -ETIMEDOUT;
+		}
+
+		udelay(1);
+	}
+
+	/* Set to use the register calculated M/N video */
+	rockchip_edp_set_video_cr_mn(edp, CALCULATED_M, 0, 0);
+
+	/* Disable video mute */
+	rockchip_edp_enable_video_mute(edp, 0);
+
+	/* Configure video slave mode */
+	rockchip_edp_enable_video_master(edp, 0);
+
+	/* Enable video */
+	rockchip_edp_start_video(edp);
+
+	timeout_loop = 0;
+
+	for (;;) {
+		timeout_loop++;
+		if (rockchip_edp_is_video_stream_on(edp) == 0) {
+			done_count++;
+			if (done_count > 10)
+				break;
+		} else if (done_count) {
+			done_count = 0;
+		}
+		if (DP_TIMEOUT_LOOP_CNT < timeout_loop) {
+			dev_err(edp->dev, "Timeout of video streamclk ok\n");
+			return -ETIMEDOUT;
+		}
+
+		mdelay(1);
+	}
+
+	if (retval != 0)
+		dev_err(edp->dev, "Video stream is not detected!\n");
+
+	return retval;
+}
+
+static irqreturn_t rockchip_edp_isr(int irq, void *arg)
+{
+	struct rockchip_edp_device *edp = arg;
+	enum dp_irq_type irq_type;
+
+	irq_type = rockchip_edp_get_irq_type(edp);
+	switch (irq_type) {
+	case DP_IRQ_TYPE_HP_CABLE_IN:
+		dev_dbg(edp->dev, "Received irq - cable in\n");
+		rockchip_edp_clear_hotplug_interrupts(edp);
+		break;
+	case DP_IRQ_TYPE_HP_CABLE_OUT:
+		dev_dbg(edp->dev, "Received irq - cable out\n");
+		rockchip_edp_clear_hotplug_interrupts(edp);
+		break;
+	case DP_IRQ_TYPE_HP_CHANGE:
+		/*
+		 * We get these change notifications once in a while, but there
+		 * is nothing we can do with them. Just ignore it for now and
+		 * only handle cable changes.
+		 */
+		dev_dbg(edp->dev, "Received irq - hotplug change; ignoring.\n");
+		rockchip_edp_clear_hotplug_interrupts(edp);
+		break;
+	default:
+		dev_err(edp->dev, "Received irq - unknown type[%x]!\n",
+			irq_type);
+		rockchip_edp_clear_hotplug_interrupts(edp);
+		break;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void rockchip_edp_commit(struct drm_encoder *encoder)
+{
+	struct rockchip_edp_device *edp = encoder_to_edp(encoder);
+	int ret;
+
+	ret = rockchip_edp_set_link_train(edp);
+	if (ret)
+		dev_err(edp->dev, "link train failed!\n");
+	else
+		dev_dbg(edp->dev, "link training success.\n");
+
+	rockchip_edp_set_lane_count(edp, edp->link_train.lane_count);
+	rockchip_edp_set_link_bandwidth(edp, edp->link_train.link_rate);
+	rockchip_edp_init_video(edp);
+
+	ret = rockchip_edp_config_video(edp, &edp->video_info);
+	if (ret)
+		dev_err(edp->dev, "unable to config video\n");
+}
+
+static void rockchip_edp_poweron(struct drm_encoder *encoder)
+{
+	struct rockchip_edp_device *edp = encoder_to_edp(encoder);
+	int ret;
+
+	if (edp->dpms_mode == DRM_MODE_DPMS_ON)
+		return;
+
+	if (edp->panel)
+		edp->panel->funcs->enable(edp->panel);
+
+	ret = rockchip_edp_clk_enable(edp);
+	if (ret < 0) {
+		dev_err(edp->dev, "cannot enable edp clk %d\n", ret);
+		return;
+	}
+
+	ret = rockchip_edp_pre_init(edp);
+	if (ret < 0) {
+		dev_err(edp->dev, "edp pre init fail %d\n", ret);
+		return;
+	}
+
+	ret = rockchip_edp_init_edp(edp);
+	if (ret < 0) {
+		dev_err(edp->dev, "edp init fail %d\n", ret);
+		return;
+	}
+
+	enable_irq(edp->irq);
+	rockchip_edp_commit(encoder);
+}
+
+static void rockchip_edp_poweroff(struct drm_encoder *encoder)
+{
+	struct rockchip_edp_device *edp = encoder_to_edp(encoder);
+
+	if (edp->dpms_mode == DRM_MODE_DPMS_OFF)
+		return;
+
+	disable_irq(edp->irq);
+	rockchip_edp_reset(edp);
+	rockchip_edp_analog_power_ctr(edp, 0);
+	rockchip_edp_clk_disable(edp);
+	if (edp->panel)
+		edp->panel->funcs->disable(edp->panel);
+}
+
+static enum drm_connector_status
+rockchip_connector_detect(struct drm_connector *connector, bool force)
+{
+	return connector_status_connected;
+}
+
+static void rockchip_connector_destroy(struct drm_connector *connector)
+{
+	drm_sysfs_connector_remove(connector);
+	drm_connector_cleanup(connector);
+}
+
+static struct drm_connector_funcs rockchip_connector_funcs = {
+	.dpms = drm_helper_connector_dpms,
+	.detect = rockchip_connector_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = rockchip_connector_destroy,
+};
+
+static int rockchip_connector_get_modes(struct drm_connector *connector)
+{
+	struct rockchip_edp_device *edp = connector_to_edp(connector);
+	struct drm_panel *panel = edp->panel;
+
+	return panel->funcs->get_modes(panel);
+}
+
+static struct drm_encoder *
+	rockchip_connector_best_encoder(struct drm_connector *connector)
+{
+	struct rockchip_edp_device *edp = connector_to_edp(connector);
+
+	return &edp->encoder;
+}
+
+static enum drm_mode_status rockchip_connector_mode_valid(
+		struct drm_connector *connector,
+		struct drm_display_mode *mode)
+{
+	/* TODO(rk): verify that the mode is really valid */
+	return MODE_OK;
+}
+
+static struct drm_connector_helper_funcs rockchip_connector_helper_funcs = {
+	.get_modes = rockchip_connector_get_modes,
+	.mode_valid = rockchip_connector_mode_valid,
+	.best_encoder = rockchip_connector_best_encoder,
+};
+
+static void rockchip_drm_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+	struct rockchip_edp_device *edp = encoder_to_edp(encoder);
+
+	if (edp->dpms_mode == mode)
+		return;
+
+	switch (mode) {
+	case DRM_MODE_DPMS_ON:
+		rockchip_edp_poweron(encoder);
+		break;
+	case DRM_MODE_DPMS_STANDBY:
+	case DRM_MODE_DPMS_SUSPEND:
+	case DRM_MODE_DPMS_OFF:
+		rockchip_edp_poweroff(encoder);
+		break;
+	default:
+		break;
+	}
+
+	edp->dpms_mode = mode;
+}
+
+static bool
+rockchip_drm_encoder_mode_fixup(struct drm_encoder *encoder,
+				const struct drm_display_mode *mode,
+				struct drm_display_mode *adjusted_mode)
+{
+	if (!adjusted_mode->private) {
+		struct rockchip_display_mode *priv_mode;
+
+		priv_mode = kzalloc(sizeof(*priv_mode), GFP_KERNEL);
+		priv_mode->out_type = ROCKCHIP_DISPLAY_TYPE_EDP;
+		adjusted_mode->private = (int *)priv_mode;
+	}
+
+	return true;
+}
+
+static void rockchip_drm_encoder_mode_set(struct drm_encoder *encoder,
+					  struct drm_display_mode *mode,
+					  struct drm_display_mode *adjusted)
+{
+	struct rockchip_edp_device *edp = encoder_to_edp(encoder);
+	u32 val;
+	int ret;
+
+	ret = rockchip_drm_encoder_get_mux_id(edp->dev->of_node, encoder);
+	if (ret < 0)
+		return;
+
+	if (ret == ROCKCHIP_CRTC_VOPL)
+		val = EDP_SEL_VOP_LIT | (EDP_SEL_VOP_LIT << 16);
+	else
+		val = EDP_SEL_VOP_LIT << 16;
+
+	dev_info(edp->dev, "vop %s output to edp\n",
+		 (ret == ROCKCHIP_CRTC_VOPL) ? "LIT" : "BIG");
+	ret = regmap_write(edp->grf, edp->soc_data->grf_soc_con6, val);
+	if (ret != 0) {
+		dev_err(edp->dev, "Could not write to GRF: %d\n", ret);
+		return;
+	}
+
+	memcpy(&edp->mode, adjusted, sizeof(*mode));
+}
+
+static void rockchip_drm_encoder_prepare(struct drm_encoder *encoder)
+{
+}
+
+static void rockchip_drm_encoder_commit(struct drm_encoder *encoder)
+{
+	rockchip_drm_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
+}
+
+static void rockchip_drm_encoder_disable(struct drm_encoder *encoder)
+{
+	struct drm_plane *plane;
+	struct drm_device *dev = encoder->dev;
+
+	rockchip_drm_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+
+	/* all planes connected to this encoder should be also disabled. */
+	list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
+		if (plane->crtc && (plane->crtc == encoder->crtc))
+			plane->funcs->disable_plane(plane);
+	}
+}
+
+static struct drm_encoder_helper_funcs rockchip_encoder_helper_funcs = {
+	.dpms = rockchip_drm_encoder_dpms,
+	.mode_fixup = rockchip_drm_encoder_mode_fixup,
+	.mode_set = rockchip_drm_encoder_mode_set,
+	.prepare = rockchip_drm_encoder_prepare,
+	.commit = rockchip_drm_encoder_commit,
+	.disable = rockchip_drm_encoder_disable,
+};
+
+static void rockchip_drm_encoder_destroy(struct drm_encoder *encoder)
+{
+	drm_encoder_cleanup(encoder);
+}
+
+static struct drm_encoder_funcs rockchip_encoder_funcs = {
+	.destroy = rockchip_drm_encoder_destroy,
+};
+
+static int rockchip_edp_init(struct rockchip_edp_device *edp)
+{
+	struct device *dev = edp->dev;
+	struct device_node *np = dev->of_node;
+	struct platform_device *pdev = to_platform_device(dev);
+	struct resource *res;
+	const struct of_device_id *match;
+	int ret;
+
+	if (!np) {
+		dev_err(dev, "Missing device tree node.\n");
+		return -EINVAL;
+	}
+
+	match = of_match_node(rockchip_edp_dt_ids, np);
+	edp->soc_data = (struct rockchip_edp_soc_data *)match->data;
+	/*
+	 * The control bit is located in the GRF register space.
+	 */
+	if (edp->soc_data->grf_soc_con6 >= 0) {
+		edp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+		if (IS_ERR(edp->grf)) {
+			dev_err(dev,
+				"rk3288-edp needs rockchip,grf property\n");
+			return PTR_ERR(edp->grf);
+		}
+	}
+
+	edp->video_info.h_sync_polarity = 0;
+	edp->video_info.v_sync_polarity = 0;
+	edp->video_info.interlaced = 0;
+	edp->video_info.color_space = CS_RGB;
+	edp->video_info.dynamic_range = VESA;
+	edp->video_info.ycbcr_coeff = COLOR_YCBCR601;
+	edp->video_info.color_depth = COLOR_8;
+
+	edp->video_info.link_rate = DP_LINK_BW_1_62;
+	edp->video_info.lane_count = LANE_CNT4;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	edp->regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(edp->regs)) {
+		dev_err(dev, "ioremap reg failed\n");
+		return PTR_ERR(edp->regs);
+	}
+
+	edp->clk_edp = devm_clk_get(dev, "clk_edp");
+	if (IS_ERR(edp->clk_edp)) {
+		dev_err(dev, "cannot get clk_edp\n");
+		return PTR_ERR(edp->clk_edp);
+	}
+
+	edp->clk_24m = devm_clk_get(dev, "clk_edp_24m");
+	if (IS_ERR(edp->clk_24m)) {
+		dev_err(dev, "cannot get clk_edp_24m\n");
+		return PTR_ERR(edp->clk_24m);
+	}
+
+	edp->pclk = devm_clk_get(dev, "pclk_edp");
+	if (IS_ERR(edp->pclk)) {
+		dev_err(dev, "cannot get pclk\n");
+		return PTR_ERR(edp->pclk);
+	}
+
+	edp->rst = devm_reset_control_get(dev, "edp");
+	if (IS_ERR(edp->rst)) {
+		dev_err(dev, "failed to get reset\n");
+		return PTR_ERR(edp->rst);
+	}
+
+	ret = rockchip_edp_clk_enable(edp);
+	if (ret < 0) {
+		dev_err(edp->dev, "cannot enable edp clk %d\n", ret);
+		return ret;
+	}
+
+	ret = rockchip_edp_pre_init(edp);
+	if (ret < 0) {
+		dev_err(edp->dev, "failed to pre init %d\n", ret);
+		return ret;
+	}
+
+	edp->irq = platform_get_irq(pdev, 0);
+	if (edp->irq < 0) {
+		dev_err(dev, "cannot find IRQ\n");
+		return edp->irq;
+	}
+
+	ret = devm_request_irq(dev, edp->irq, rockchip_edp_isr, 0,
+			       dev_name(dev), edp);
+	if (ret) {
+		dev_err(dev, "cannot claim IRQ %d\n", edp->irq);
+		return ret;
+	}
+
+	disable_irq_nosync(edp->irq);
+
+	edp->dpms_mode = DRM_MODE_DPMS_OFF;
+
+	dev_set_name(edp->dev, "rockchip-edp");
+
+	return 0;
+}
+
+static int rockchip_edp_bind(struct device *dev, struct device *master,
+			     void *data)
+{
+	struct rockchip_edp_device *edp = dev_get_drvdata(dev);
+	struct drm_encoder *encoder;
+	struct drm_connector *connector;
+	struct drm_device *drm_dev = data;
+	int ret;
+
+	ret = rockchip_edp_init(edp);
+	if (ret < 0)
+		return ret;
+
+	edp->drm_dev = drm_dev;
+
+	encoder = &edp->encoder;
+
+	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
+							     dev->of_node);
+	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
+
+	ret = drm_encoder_init(drm_dev, encoder, &rockchip_encoder_funcs,
+			       DRM_MODE_ENCODER_LVDS);
+	if (ret) {
+		DRM_ERROR("failed to initialize encoder with drm\n");
+		return ret;
+	}
+
+	drm_encoder_helper_add(encoder, &rockchip_encoder_helper_funcs);
+
+	connector = &edp->connector;
+	connector->polled = DRM_CONNECTOR_POLL_HPD;
+	connector->dpms = DRM_MODE_DPMS_OFF;
+
+	ret = drm_connector_init(drm_dev, connector,
+				 &rockchip_connector_funcs,
+				 DRM_MODE_CONNECTOR_eDP);
+	if (ret) {
+		DRM_ERROR("failed to initialize connector with drm\n");
+		goto err_free_encoder;
+	}
+
+	drm_connector_helper_add(connector,
+				 &rockchip_connector_helper_funcs);
+
+	ret = drm_sysfs_connector_add(connector);
+	if (ret) {
+		DRM_ERROR("failed to add drm_sysfs\n");
+		goto err_free_connector;
+	}
+
+	ret = drm_mode_connector_attach_encoder(connector, encoder);
+	if (ret) {
+		DRM_ERROR("failed to attach connector and encoder\n");
+		goto err_free_connector_sysfs;
+	}
+
+	ret = drm_panel_attach(edp->panel, connector);
+	if (ret) {
+		DRM_ERROR("failed to attach connector and encoder\n");
+		goto err_free_connector_sysfs;
+	}
+
+	return 0;
+
+err_free_connector_sysfs:
+	drm_sysfs_connector_remove(connector);
+err_free_connector:
+	drm_connector_cleanup(connector);
+err_free_encoder:
+	drm_encoder_cleanup(encoder);
+	return ret;
+}
+
+static void rockchip_edp_unbind(struct device *dev, struct device *master,
+				void *data)
+{
+	struct rockchip_edp_device *edp = dev_get_drvdata(dev);
+	struct drm_encoder *encoder;
+
+	encoder = &edp->encoder;
+
+	if (edp->panel)
+		drm_panel_detach(edp->panel);
+
+	rockchip_drm_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+	encoder->funcs->destroy(encoder);
+	drm_sysfs_connector_remove(&edp->connector);
+	drm_connector_cleanup(&edp->connector);
+	drm_encoder_cleanup(encoder);
+}
+
+static const struct component_ops rockchip_edp_component_ops = {
+	.bind = rockchip_edp_bind,
+	.unbind = rockchip_edp_unbind,
+};
+
+static int rockchip_edp_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct drm_panel *panel;
+	struct device_node *panel_node;
+	struct rockchip_edp_device *edp;
+
+	if (!dev->of_node) {
+		dev_err(dev, "can't find eDP devices\n");
+		return -ENODEV;
+	}
+
+	panel_node = of_parse_phandle(dev->of_node, "rockchip,panel", 0);
+	if (!panel_node) {
+		DRM_ERROR("failed to find diaplay panel\n");
+		return -ENODEV;
+	}
+
+	panel = of_drm_find_panel(panel_node);
+	if (!panel) {
+		DRM_ERROR("failed to find diaplay panel\n");
+		of_node_put(panel_node);
+		return -EPROBE_DEFER;
+	}
+
+	of_node_put(panel_node);
+
+	edp = devm_kzalloc(dev, sizeof(*edp), GFP_KERNEL);
+	if (!edp)
+		return -ENOMEM;
+	edp->dev = dev;
+	edp->panel = panel;
+	platform_set_drvdata(pdev, edp);
+
+	return component_add(dev, &rockchip_edp_component_ops);
+}
+
+static int rockchip_edp_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &rockchip_edp_component_ops);
+
+	return 0;
+}
+
+static struct platform_driver rockchip_edp_driver = {
+	.probe = rockchip_edp_probe,
+	.remove = rockchip_edp_remove,
+	.driver = {
+		   .name = "rockchip-edp",
+		   .owner = THIS_MODULE,
+		   .of_match_table = of_match_ptr(rockchip_edp_dt_ids),
+	},
+};
+
+module_platform_driver(rockchip_edp_driver);
+
+MODULE_AUTHOR("Jeff chen <jeff.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org>");
+MODULE_DESCRIPTION("ROCKCHIP EDP Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/rockchip/rockchip_edp_core.h b/drivers/gpu/drm/rockchip/rockchip_edp_core.h
new file mode 100644
index 0000000..c13325f
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_edp_core.h
@@ -0,0 +1,309 @@
+/*
+* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+* Author:
+*      Andy yan <andy.yan-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+*      Jeff chen <jeff.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+*
+* based on exynos_dp_core.h
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*/
+
+#ifndef _ROCKCHIP_EDP_CORE_H
+#define _ROCKCHIP_EDP_CORE_H
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_panel.h>
+#include "rockchip_drm_drv.h"
+
+#define DP_TIMEOUT_LOOP_CNT 100
+#define MAX_CR_LOOP 5
+#define MAX_EQ_LOOP 5
+
+#define GRF_EDP_REF_CLK_SEL_INTER		(1 << 4)
+#define GRF_EDP_HDCP_EN				(1 << 15)
+#define GRF_EDP_BIST_EN				(1 << 14)
+#define GRF_EDP_MEM_CTL_BY_EDP			(1 << 13)
+#define GRF_EDP_SECURE_EN			(1 << 3)
+#define EDP_SEL_VOP_LIT				(1 << 5)
+
+enum link_lane_count_type {
+	LANE_CNT1 = 1,
+	LANE_CNT2 = 2,
+	LANE_CNT4 = 4
+};
+
+enum link_training_state {
+	LT_START,
+	LT_CLK_RECOVERY,
+	LT_EQ_TRAINING,
+	FINISHED,
+	FAILED
+};
+
+enum voltage_swing_level {
+	VOLTAGE_LEVEL_0,
+	VOLTAGE_LEVEL_1,
+	VOLTAGE_LEVEL_2,
+	VOLTAGE_LEVEL_3,
+};
+
+enum pre_emphasis_level {
+	PRE_EMPHASIS_LEVEL_0,
+	PRE_EMPHASIS_LEVEL_1,
+	PRE_EMPHASIS_LEVEL_2,
+	PRE_EMPHASIS_LEVEL_3,
+};
+
+enum pattern_set {
+	PRBS7,
+	D10_2,
+	TRAINING_PTN1,
+	TRAINING_PTN2,
+	DP_NONE
+};
+
+enum color_space {
+	CS_RGB,
+	CS_YCBCR422,
+	CS_YCBCR444
+};
+
+enum color_depth {
+	COLOR_6,
+	COLOR_8,
+	COLOR_10,
+	COLOR_12
+};
+
+enum color_coefficient {
+	COLOR_YCBCR601,
+	COLOR_YCBCR709
+};
+
+enum dynamic_range {
+	VESA,
+	CEA
+};
+
+enum pll_status {
+	DP_PLL_UNLOCKED,
+	DP_PLL_LOCKED
+};
+
+enum clock_recovery_m_value_type {
+	CALCULATED_M,
+	REGISTER_M
+};
+
+enum video_timing_recognition_type {
+	VIDEO_TIMING_FROM_CAPTURE,
+	VIDEO_TIMING_FROM_REGISTER
+};
+
+enum analog_power_block {
+	AUX_BLOCK,
+	CH0_BLOCK,
+	CH1_BLOCK,
+	CH2_BLOCK,
+	CH3_BLOCK,
+	ANALOG_TOTAL,
+	POWER_ALL
+};
+
+enum dp_irq_type {
+	DP_IRQ_TYPE_HP_CABLE_IN,
+	DP_IRQ_TYPE_HP_CABLE_OUT,
+	DP_IRQ_TYPE_HP_CHANGE,
+	DP_IRQ_TYPE_UNKNOWN,
+};
+
+struct video_info {
+	char *name;
+
+	bool h_sync_polarity;
+	bool v_sync_polarity;
+	bool interlaced;
+
+	enum color_space color_space;
+	enum dynamic_range dynamic_range;
+	enum color_coefficient ycbcr_coeff;
+	enum color_depth color_depth;
+
+	u8 link_rate;
+	enum link_lane_count_type lane_count;
+};
+
+struct link_train {
+	int eq_loop;
+	int cr_loop[4];
+
+	u8 link_rate;
+	u8 lane_count;
+	u8 training_lane[4];
+
+	enum link_training_state lt_state;
+};
+
+/*
+ * @grf_offset: offset inside the grf regmap for setting the rk3288 lvds
+ */
+struct rockchip_edp_soc_data {
+	int grf_soc_con6;
+	int grf_soc_con12;
+};
+
+struct rockchip_edp_device {
+	struct device *dev;
+	struct drm_device *drm_dev;
+	struct drm_panel *panel;
+	struct drm_connector connector;
+	struct drm_encoder encoder;
+	struct drm_display_mode mode;
+
+	struct rockchip_edp_soc_data *soc_data;
+
+	void __iomem *regs;
+	struct regmap *grf;
+	unsigned int irq;
+	struct clk *clk_edp;
+	struct clk *clk_24m_parent;
+	struct clk *clk_24m;
+	struct clk *pclk;
+	struct reset_control *rst;
+	struct link_train link_train;
+	struct video_info video_info;
+	bool clk_on;
+
+	int dpms_mode;
+};
+
+void rockchip_edp_enable_video_mute(struct rockchip_edp_device *edp,
+				    bool enable);
+void rockchip_edp_stop_video(struct rockchip_edp_device *edp);
+void rockchip_edp_lane_swap(struct rockchip_edp_device *edp, bool enable);
+void rockchip_edp_init_refclk(struct rockchip_edp_device *edp);
+void rockchip_edp_init_interrupt(struct rockchip_edp_device *edp);
+void rockchip_edp_reset(struct rockchip_edp_device *edp);
+void rockchip_edp_config_interrupt(struct rockchip_edp_device *edp);
+u32 rockchip_edp_get_pll_lock_status(struct rockchip_edp_device *edp);
+void rockchip_edp_analog_power_ctr(struct rockchip_edp_device *edp,
+				   bool enable);
+void rockchip_edp_init_analog_func(struct rockchip_edp_device *edp);
+void rockchip_edp_init_hpd(struct rockchip_edp_device *edp);
+void rockchip_edp_reset_aux(struct rockchip_edp_device *edp);
+void rockchip_edp_init_aux(struct rockchip_edp_device *edp);
+int rockchip_edp_get_plug_in_status(struct rockchip_edp_device *edp);
+void rockchip_edp_enable_sw_function(struct rockchip_edp_device *edp);
+int rockchip_edp_start_aux_transaction(struct rockchip_edp_device *edp);
+int rockchip_edp_write_byte_to_dpcd(struct rockchip_edp_device *edp,
+				    unsigned int reg_addr,
+				    unsigned char data);
+int rockchip_edp_read_byte_from_dpcd(struct rockchip_edp_device *edp,
+				     unsigned int reg_addr,
+				     unsigned char *data);
+int rockchip_edp_write_bytes_to_dpcd(struct rockchip_edp_device *edp,
+				     unsigned int reg_addr,
+				     unsigned int count,
+				     unsigned char data[]);
+int rockchip_edp_read_bytes_from_dpcd(struct rockchip_edp_device *edp,
+				      unsigned int reg_addr,
+				      unsigned int count,
+				      unsigned char data[]);
+int rockchip_edp_select_i2c_device(struct rockchip_edp_device *edp,
+				   unsigned int device_addr,
+				   unsigned int reg_addr);
+int rockchip_edp_read_byte_from_i2c(struct rockchip_edp_device *edp,
+				    unsigned int device_addr,
+				    unsigned int reg_addr,
+				    unsigned int *data);
+int rockchip_edp_read_bytes_from_i2c(struct rockchip_edp_device *edp,
+				     unsigned int device_addr,
+				     unsigned int reg_addr,
+				     unsigned int count,
+				     unsigned char edid[]);
+void rockchip_edp_set_link_bandwidth(struct rockchip_edp_device *edp,
+				     u32 bwtype);
+void rockchip_edp_get_link_bandwidth(struct rockchip_edp_device *edp,
+				     u32 *bwtype);
+void rockchip_edp_set_lane_count(struct rockchip_edp_device *edp,
+				 u32 count);
+void rockchip_edp_get_lane_count(struct rockchip_edp_device *edp,
+				 u32 *count);
+void rockchip_edp_enable_enhanced_mode(struct rockchip_edp_device *edp,
+				       bool enable);
+void rockchip_edp_set_training_pattern(struct rockchip_edp_device *edp,
+				       enum pattern_set pattern);
+void rockchip_edp_set_lane0_pre_emphasis(struct rockchip_edp_device *edp,
+					 u32 level);
+void rockchip_edp_set_lane1_pre_emphasis(struct rockchip_edp_device *edp,
+					 u32 level);
+void rockchip_edp_set_lane2_pre_emphasis(struct rockchip_edp_device *edp,
+					 u32 level);
+void rockchip_edp_set_lane3_pre_emphasis(struct rockchip_edp_device *edp,
+					 u32 level);
+void rockchip_edp_set_lane0_link_training(struct rockchip_edp_device *edp,
+					  u32 training_lane);
+void rockchip_edp_set_lane1_link_training(struct rockchip_edp_device *edp,
+					  u32 training_lane);
+void rockchip_edp_set_lane2_link_training(struct rockchip_edp_device *edp,
+					  u32 training_lane);
+void rockchip_edp_set_lane3_link_training(struct rockchip_edp_device *edp,
+					  u32 training_lane);
+u32 rockchip_edp_get_lane0_link_training(struct rockchip_edp_device *edp);
+u32 rockchip_edp_get_lane1_link_training(struct rockchip_edp_device *edp);
+u32 rockchip_edp_get_lane2_link_training(struct rockchip_edp_device *edp);
+u32 rockchip_edp_get_lane3_link_training(struct rockchip_edp_device *edp);
+void rockchip_edp_reset_macro(struct rockchip_edp_device *edp);
+int rockchip_edp_init_video(struct rockchip_edp_device *edp);
+
+void rockchip_edp_set_video_color_format(struct rockchip_edp_device *edp,
+					 u32 color_depth,
+					 u32 color_space,
+					 u32 dynamic_range,
+					 u32 coeff);
+int
+rockchip_edp_is_slave_video_stream_clock_on(struct rockchip_edp_device *edp);
+void rockchip_edp_set_video_cr_mn(struct rockchip_edp_device *edp,
+				  enum clock_recovery_m_value_type type,
+				  u32 m_value,
+				  u32 n_value);
+void rockchip_edp_set_video_timing_mode(struct rockchip_edp_device *edp,
+					u32 type);
+void rockchip_edp_enable_video_master(struct rockchip_edp_device *edp,
+				      bool enable);
+void rockchip_edp_start_video(struct rockchip_edp_device *edp);
+int rockchip_edp_is_video_stream_on(struct rockchip_edp_device *edp);
+void rockchip_edp_config_video_slave_mode(struct rockchip_edp_device *edp,
+					  struct video_info *video_info);
+void rockchip_edp_enable_scrambling(struct rockchip_edp_device *edp);
+void rockchip_edp_disable_scrambling(struct rockchip_edp_device *edp);
+void rockchip_edp_hw_link_training_en(struct rockchip_edp_device *edp);
+int rockchip_edp_get_hw_lt_status(struct rockchip_edp_device *edp);
+int rockchip_edp_wait_hw_lt_done(struct rockchip_edp_device *edp);
+enum dp_irq_type rockchip_edp_get_irq_type(struct rockchip_edp_device *edp);
+void rockchip_edp_clear_hotplug_interrupts(struct rockchip_edp_device *edp);
+
+/* I2C EDID Chip ID, Slave Address */
+#define I2C_EDID_DEVICE_ADDR			0x50
+#define I2C_E_EDID_DEVICE_ADDR			0x30
+
+/* DPCD_ADDR_MAX_LANE_COUNT */
+#define DPCD_ENHANCED_FRAME_CAP(x)		(((x) >> 7) & 0x1)
+#define DPCD_MAX_LANE_COUNT(x)			((x) & 0x1f)
+
+/* DPCD_ADDR_LANE_COUNT_SET */
+#define DPCD_LANE_COUNT_SET(x)			((x) & 0x1f)
+
+/* DPCD_ADDR_TRAINING_LANE0_SET */
+#define DPCD_PRE_EMPHASIS_SET(x)		(((x) & 0x3) << 3)
+#define DPCD_PRE_EMPHASIS_GET(x)		(((x) >> 3) & 0x3)
+#define DPCD_VOLTAGE_SWING_SET(x)		(((x) & 0x3) << 0)
+#define DPCD_VOLTAGE_SWING_GET(x)		(((x) >> 0) & 0x3)
+
+#endif  /* _ROCKCHIP_EDP_CORE_H */
diff --git a/drivers/gpu/drm/rockchip/rockchip_edp_reg.c b/drivers/gpu/drm/rockchip/rockchip_edp_reg.c
new file mode 100644
index 0000000..f6d641c
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_edp_reg.c
@@ -0,0 +1,1202 @@
+/*
+* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+* Author:
+*      Andy yan <andy.yan-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+*      Jeff chen <jeff.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+*
+* based on exynos_dp_reg.c
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*/
+
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#include "rockchip_edp_core.h"
+#include "rockchip_edp_reg.h"
+
+void rockchip_edp_enable_video_mute(struct rockchip_edp_device *edp,
+				    bool enable)
+{
+	u32 val;
+
+	if (enable) {
+		val = readl(edp->regs + VIDEO_CTL_1);
+		val |= VIDEO_MUTE;
+		writel(val, edp->regs + VIDEO_CTL_1);
+	} else {
+		val = readl(edp->regs + VIDEO_CTL_1);
+		val &= ~VIDEO_MUTE;
+		writel(val, edp->regs + VIDEO_CTL_1);
+	}
+}
+
+void rockchip_edp_stop_video(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + VIDEO_CTL_1);
+	val &= ~VIDEO_EN;
+	writel(val, edp->regs + VIDEO_CTL_1);
+}
+
+void rockchip_edp_lane_swap(struct rockchip_edp_device *edp, bool enable)
+{
+	u32 val;
+
+	if (enable)
+		val = LANE3_MAP_LOGIC_LANE_0 | LANE2_MAP_LOGIC_LANE_1 |
+			LANE1_MAP_LOGIC_LANE_2 | LANE0_MAP_LOGIC_LANE_3;
+	else
+		val = LANE3_MAP_LOGIC_LANE_3 | LANE2_MAP_LOGIC_LANE_2 |
+			LANE1_MAP_LOGIC_LANE_1 | LANE0_MAP_LOGIC_LANE_0;
+
+	writel(val, edp->regs + LANE_MAP);
+}
+
+void rockchip_edp_init_refclk(struct rockchip_edp_device *edp)
+{
+	writel(SEL_24M, edp->regs + ANALOG_CTL_2);
+	writel(REF_CLK_24M, edp->regs + PLL_REG_1);
+
+	writel(0x95, edp->regs + PLL_REG_2);
+	writel(0x40, edp->regs + PLL_REG_3);
+	writel(0x58, edp->regs + PLL_REG_4);
+	writel(0x22, edp->regs + PLL_REG_5);
+	writel(0x19, edp->regs + SSC_REG);
+	writel(0x87, edp->regs + TX_REG_COMMON);
+	writel(0x03, edp->regs + DP_AUX);
+	writel(0x46, edp->regs + DP_BIAS);
+	writel(0x55, edp->regs + DP_RESERVE2);
+}
+
+void rockchip_edp_init_interrupt(struct rockchip_edp_device *edp)
+{
+	/* Set interrupt pin assertion polarity as high */
+	writel(INT_POL, edp->regs + INT_CTL);
+
+	/* Clear pending valisers */
+	writel(0xff, edp->regs + COMMON_INT_STA_1);
+	writel(0x4f, edp->regs + COMMON_INT_STA_2);
+	writel(0xff, edp->regs + COMMON_INT_STA_3);
+	writel(0x27, edp->regs + COMMON_INT_STA_4);
+
+	writel(0x7f, edp->regs + DP_INT_STA);
+
+	/* 0:mask,1: unmask */
+	writel(0x00, edp->regs + COMMON_INT_MASK_1);
+	writel(0x00, edp->regs + COMMON_INT_MASK_2);
+	writel(0x00, edp->regs + COMMON_INT_MASK_3);
+	writel(0x00, edp->regs + COMMON_INT_MASK_4);
+	writel(0x00, edp->regs + DP_INT_STA_MASK);
+}
+
+void rockchip_edp_reset(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	rockchip_edp_stop_video(edp);
+	rockchip_edp_enable_video_mute(edp, 0);
+
+	val = VID_CAP_FUNC_EN_N | AUD_FIFO_FUNC_EN_N |
+		AUD_FUNC_EN_N | HDCP_FUNC_EN_N | SW_FUNC_EN_N;
+	writel(val, edp->regs + FUNC_EN_1);
+
+	val = SSC_FUNC_EN_N | AUX_FUNC_EN_N |
+		SERDES_FIFO_FUNC_EN_N |
+		LS_CLK_DOMAIN_FUNC_EN_N;
+	writel(val, edp->regs + FUNC_EN_2);
+
+	usleep_range(20, 30);
+
+	rockchip_edp_lane_swap(edp, 0);
+
+	writel(0x0, edp->regs + SYS_CTL_1);
+	writel(0x40, edp->regs + SYS_CTL_2);
+	writel(0x0, edp->regs + SYS_CTL_3);
+	writel(0x0, edp->regs + SYS_CTL_4);
+
+	writel(0x0, edp->regs + PKT_SEND_CTL);
+	writel(0x0, edp->regs + HDCP_CTL);
+
+	writel(0x5e, edp->regs + HPD_DEGLITCH_L);
+	writel(0x1a, edp->regs + HPD_DEGLITCH_H);
+
+	writel(0x10, edp->regs + LINK_DEBUG_CTL);
+
+	writel(0x0, edp->regs + VIDEO_FIFO_THRD);
+	writel(0x20, edp->regs + AUDIO_MARGIN);
+
+	writel(0x4, edp->regs + M_VID_GEN_FILTER_TH);
+	writel(0x2, edp->regs + M_AUD_GEN_FILTER_TH);
+
+	writel(0x0, edp->regs + SOC_GENERAL_CTL);
+}
+
+void rockchip_edp_config_interrupt(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	/* 0: mask, 1: unmask */
+	val = 0;
+	writel(val, edp->regs + COMMON_INT_MASK_1);
+
+	writel(val, edp->regs + COMMON_INT_MASK_2);
+
+	writel(val, edp->regs + COMMON_INT_MASK_3);
+
+	writel(val, edp->regs + COMMON_INT_MASK_4);
+
+	writel(val, edp->regs + DP_INT_STA_MASK);
+}
+
+u32 rockchip_edp_get_pll_lock_status(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + DEBUG_CTL);
+
+	return (val & PLL_LOCK) ? DP_PLL_LOCKED : DP_PLL_UNLOCKED;
+}
+
+void rockchip_edp_analog_power_ctr(struct rockchip_edp_device *edp,
+				   bool enable)
+{
+	u32 val;
+
+	if (enable) {
+		val = PD_EXP_BG | PD_AUX | PD_PLL |
+			PD_CH3 | PD_CH2 | PD_CH1 | PD_CH0;
+		writel(val, edp->regs + DP_PWRDN);
+		usleep_range(10, 20);
+		writel(0x0, edp->regs + DP_PWRDN);
+	} else {
+		val = PD_EXP_BG | PD_AUX | PD_PLL |
+			PD_CH3 | PD_CH2 | PD_CH1 | PD_CH0;
+		writel(val, edp->regs + DP_PWRDN);
+	}
+}
+
+void rockchip_edp_init_analog_func(struct rockchip_edp_device *edp)
+{
+	u32 val;
+	int wt = 0;
+
+	rockchip_edp_analog_power_ctr(edp, 1);
+
+	val = PLL_LOCK_CHG;
+	writel(val, edp->regs + COMMON_INT_STA_1);
+
+	val = readl(edp->regs + DEBUG_CTL);
+	val &= ~(F_PLL_LOCK | PLL_LOCK_CTRL);
+	writel(val, edp->regs + DEBUG_CTL);
+
+	/* Power up PLL */
+	while (wt < 100) {
+		if (rockchip_edp_get_pll_lock_status(edp) == DP_PLL_LOCKED) {
+			dev_dbg(edp->dev, "edp pll locked\n");
+			break;
+		}
+		wt++;
+		udelay(5);
+	}
+
+	/* Enable Serdes FIFO function and Link symbol clock domain module */
+	val = readl(edp->regs + FUNC_EN_2);
+	val &= ~(SERDES_FIFO_FUNC_EN_N | LS_CLK_DOMAIN_FUNC_EN_N
+		| AUX_FUNC_EN_N | SSC_FUNC_EN_N);
+	writel(val, edp->regs + FUNC_EN_2);
+}
+
+void rockchip_edp_init_hpd(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = HOTPLUG_CHG | HPD_LOST | PLUG;
+	writel(val, edp->regs + COMMON_INT_STA_4);
+
+	val = INT_HPD;
+	writel(val, edp->regs + DP_INT_STA);
+
+	val = readl(edp->regs + SYS_CTL_3);
+	val |= (F_HPD | HPD_CTRL);
+	writel(val, edp->regs + SYS_CTL_3);
+}
+
+void rockchip_edp_reset_aux(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	/* Disable AUX channel module */
+	val = readl(edp->regs + FUNC_EN_2);
+	val |= AUX_FUNC_EN_N;
+	writel(val, edp->regs + FUNC_EN_2);
+}
+
+void rockchip_edp_init_aux(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	/* Clear inerrupts related to AUX channel */
+	val = RPLY_RECEIV | AUX_ERR;
+	writel(val, edp->regs + DP_INT_STA);
+
+	rockchip_edp_reset_aux(edp);
+
+	/* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */
+	val = DEFER_CTRL_EN | DEFER_COUNT(1);
+	writel(val, edp->regs + AUX_CH_DEFER_CTL);
+
+	/* Enable AUX channel module */
+	val = readl(edp->regs + FUNC_EN_2);
+	val &= ~AUX_FUNC_EN_N;
+	writel(val, edp->regs + FUNC_EN_2);
+}
+
+int rockchip_edp_get_plug_in_status(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + SYS_CTL_3);
+	if (val & HPD_STATUS)
+		return 0;
+
+	return -EINVAL;
+}
+
+void rockchip_edp_enable_sw_function(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + FUNC_EN_1);
+	val &= ~SW_FUNC_EN_N;
+	writel(val, edp->regs + FUNC_EN_1);
+}
+
+int rockchip_edp_start_aux_transaction(struct rockchip_edp_device *edp)
+{
+	int val;
+	int retval = 0;
+	int timeout_loop = 0;
+	int aux_timeout = 0;
+
+	/* Enable AUX CH operation */
+	val = readl(edp->regs + AUX_CH_CTL_2);
+	val |= AUX_EN;
+	writel(val, edp->regs + AUX_CH_CTL_2);
+
+	/* Is AUX CH operation enabled? */
+	val = readl(edp->regs + AUX_CH_CTL_2);
+	while (val & AUX_EN) {
+		aux_timeout++;
+		if ((DP_TIMEOUT_LOOP_CNT * 10) < aux_timeout) {
+			dev_err(edp->dev, "AUX CH enable timeout!\n");
+			return -ETIMEDOUT;
+		}
+		val = readl(edp->regs + AUX_CH_CTL_2);
+		usleep_range(1000, 2000);
+	}
+
+	/* Is AUX CH command redply received? */
+	val = readl(edp->regs + DP_INT_STA);
+	while (!(val & RPLY_RECEIV)) {
+		timeout_loop++;
+		if (DP_TIMEOUT_LOOP_CNT < timeout_loop) {
+			dev_err(edp->dev, "AUX CH command redply failed!\n");
+			return -ETIMEDOUT;
+		}
+		val = readl(edp->regs + DP_INT_STA);
+		usleep_range(10, 20);
+	}
+
+	/* Clear interrupt source for AUX CH command redply */
+	writel(RPLY_RECEIV, edp->regs + DP_INT_STA);
+
+	/* Clear interrupt source for AUX CH access error */
+	val = readl(edp->regs + DP_INT_STA);
+	if (val & AUX_ERR) {
+		writel(AUX_ERR, edp->regs + DP_INT_STA);
+		return -EREMOTEIO;
+	}
+
+	/* Check AUX CH error access status */
+	val = readl(edp->regs + AUX_CH_STA);
+	if ((val & AUX_STATUS_MASK) != 0) {
+		dev_err(edp->dev, "AUX CH error happens: %d\n\n",
+			val & AUX_STATUS_MASK);
+		return -EREMOTEIO;
+	}
+
+	return retval;
+}
+
+int rockchip_edp_write_byte_to_dpcd(struct rockchip_edp_device *edp,
+				    unsigned int val_addr,
+				    unsigned char data)
+{
+	u32 val;
+	int i;
+	int retval;
+
+	for (i = 0; i < 3; i++) {
+		/* Clear AUX CH data buffer */
+		val = BUF_CLR;
+		writel(val, edp->regs + BUFFER_DATA_CTL);
+
+		/* Select DPCD device address */
+		val = AUX_ADDR_7_0(val_addr);
+		writel(val, edp->regs + DP_AUX_ADDR_7_0);
+		val = AUX_ADDR_15_8(val_addr);
+		writel(val, edp->regs + DP_AUX_ADDR_15_8);
+		val = AUX_ADDR_19_16(val_addr);
+		writel(val, edp->regs + DP_AUX_ADDR_19_16);
+
+		/* Write data buffer */
+		val = (unsigned int)data;
+		writel(val, edp->regs + BUF_DATA_0);
+
+		/*
+		 * Set DisplayPort transaction and write 1 byte
+		 * If bit 3 is 1, DisplayPort transaction.
+		 * If Bit 3 is 0, I2C transaction.
+		 */
+		val = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE;
+		writel(val, edp->regs + AUX_CH_CTL_1);
+
+		/* Start AUX transaction */
+		retval = rockchip_edp_start_aux_transaction(edp);
+		if (retval == 0)
+			break;
+
+		dev_dbg(edp->dev, "Aux Transaction fail!\n");
+	}
+
+	return retval;
+}
+
+int rockchip_edp_read_byte_from_dpcd(struct rockchip_edp_device *edp,
+				     unsigned int val_addr,
+				     unsigned char *data)
+{
+	u32 val;
+	int i;
+	int retval;
+
+	for (i = 0; i < 10; i++) {
+		/* Clear AUX CH data buffer */
+		val = BUF_CLR;
+		writel(val, edp->regs + BUFFER_DATA_CTL);
+
+		/* Select DPCD device address */
+		val = AUX_ADDR_7_0(val_addr);
+		writel(val, edp->regs + DP_AUX_ADDR_7_0);
+		val = AUX_ADDR_15_8(val_addr);
+		writel(val, edp->regs + DP_AUX_ADDR_15_8);
+		val = AUX_ADDR_19_16(val_addr);
+		writel(val, edp->regs + DP_AUX_ADDR_19_16);
+
+		/*
+		 * Set DisplayPort transaction and read 1 byte
+		 * If bit 3 is 1, DisplayPort transaction.
+		 * If Bit 3 is 0, I2C transaction.
+		 */
+		val = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ;
+		writel(val, edp->regs + AUX_CH_CTL_1);
+
+		/* Start AUX transaction */
+		retval = rockchip_edp_start_aux_transaction(edp);
+		if (retval == 0)
+			break;
+
+		dev_dbg(edp->dev, "Aux Transaction fail!\n");
+	}
+
+	/* Read data buffer */
+	val = readl(edp->regs + BUF_DATA_0);
+	*data = (unsigned char)(val & 0xff);
+
+	return retval;
+}
+
+int rockchip_edp_write_bytes_to_dpcd(struct rockchip_edp_device *edp,
+				     unsigned int val_addr,
+				     unsigned int count,
+				     unsigned char data[])
+{
+	u32 val;
+	unsigned int start_offset;
+	unsigned int cur_data_count;
+	unsigned int cur_data_idx;
+	int i;
+	int retval = 0;
+
+	/* Clear AUX CH data buffer */
+	val = BUF_CLR;
+	writel(val, edp->regs + BUFFER_DATA_CTL);
+
+	start_offset = 0;
+	while (start_offset < count) {
+		/* Buffer size of AUX CH is 16 * 4bytes */
+		if ((count - start_offset) > 16)
+			cur_data_count = 16;
+		else
+			cur_data_count = count - start_offset;
+
+		for (i = 0; i < 10; i++) {
+			/* Select DPCD device address */
+			val = AUX_ADDR_7_0(val_addr + start_offset);
+			writel(val, edp->regs + DP_AUX_ADDR_7_0);
+			val = AUX_ADDR_15_8(val_addr + start_offset);
+			writel(val, edp->regs + DP_AUX_ADDR_15_8);
+			val = AUX_ADDR_19_16(val_addr + start_offset);
+			writel(val, edp->regs + DP_AUX_ADDR_19_16);
+
+			for (cur_data_idx = 0; cur_data_idx < cur_data_count;
+			     cur_data_idx++) {
+				val = data[start_offset + cur_data_idx];
+				writel(val, edp->regs + BUF_DATA_0
+							  + 4 * cur_data_idx);
+			}
+
+			/*
+			 * Set DisplayPort transaction and write
+			 * If bit 3 is 1, DisplayPort transaction.
+			 * If Bit 3 is 0, I2C transaction.
+			 */
+			val = AUX_LENGTH(cur_data_count) |
+				AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE;
+			writel(val, edp->regs + AUX_CH_CTL_1);
+
+			/* Start AUX transaction */
+			retval = rockchip_edp_start_aux_transaction(edp);
+			if (retval == 0)
+				break;
+
+			dev_dbg(edp->dev, "Aux Transaction fail!\n");
+		}
+
+		start_offset += cur_data_count;
+	}
+
+	return retval;
+}
+
+int rockchip_edp_read_bytes_from_dpcd(struct rockchip_edp_device *edp,
+				      unsigned int val_addr,
+				      unsigned int count,
+				      unsigned char data[])
+{
+	u32 val;
+	unsigned int start_offset;
+	unsigned int cur_data_count;
+	unsigned int cur_data_idx;
+	int i;
+	int retval = 0;
+
+	/* Clear AUX CH data buffer */
+	val = BUF_CLR;
+	writel(val, edp->regs + BUFFER_DATA_CTL);
+
+	start_offset = 0;
+	while (start_offset < count) {
+		/* Buffer size of AUX CH is 16 * 4bytes */
+		if ((count - start_offset) > 16)
+			cur_data_count = 16;
+		else
+			cur_data_count = count - start_offset;
+
+		/* AUX CH Request Transaction process */
+		for (i = 0; i < 10; i++) {
+			/* Select DPCD device address */
+			val = AUX_ADDR_7_0(val_addr + start_offset);
+			writel(val, edp->regs + DP_AUX_ADDR_7_0);
+			val = AUX_ADDR_15_8(val_addr + start_offset);
+			writel(val, edp->regs + DP_AUX_ADDR_15_8);
+			val = AUX_ADDR_19_16(val_addr + start_offset);
+			writel(val, edp->regs + DP_AUX_ADDR_19_16);
+
+			/*
+			 * Set DisplayPort transaction and read
+			 * If bit 3 is 1, DisplayPort transaction.
+			 * If Bit 3 is 0, I2C transaction.
+			 */
+			val = AUX_LENGTH(cur_data_count) |
+				AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ;
+			writel(val, edp->regs + AUX_CH_CTL_1);
+
+			/* Start AUX transaction */
+			retval = rockchip_edp_start_aux_transaction(edp);
+			if (retval == 0)
+				break;
+
+			dev_dbg(edp->dev, "Aux Transaction fail!\n");
+		}
+
+		for (cur_data_idx = 0; cur_data_idx < cur_data_count;
+		    cur_data_idx++) {
+			val = readl(edp->regs + BUF_DATA_0
+						 + 4 * cur_data_idx);
+			data[start_offset + cur_data_idx] =
+				(unsigned char)val;
+		}
+
+		start_offset += cur_data_count;
+	}
+
+	return retval;
+}
+
+int rockchip_edp_select_i2c_device(struct rockchip_edp_device *edp,
+				   unsigned int device_addr,
+				   unsigned int val_addr)
+{
+	u32 val;
+	int retval;
+
+	/* Set EDID device address */
+	val = device_addr;
+	writel(val, edp->regs + DP_AUX_ADDR_7_0);
+	writel(0x0, edp->regs + DP_AUX_ADDR_15_8);
+	writel(0x0, edp->regs + DP_AUX_ADDR_19_16);
+
+	/* Set offset from base address of EDID device */
+	writel(val_addr, edp->regs + BUF_DATA_0);
+
+	/*
+	 * Set I2C transaction and write address
+	 * If bit 3 is 1, DisplayPort transaction.
+	 * If Bit 3 is 0, I2C transaction.
+	 */
+	val = AUX_TX_COMM_I2C_TRANSACTION | AUX_TX_COMM_MOT |
+		AUX_TX_COMM_WRITE;
+	writel(val, edp->regs + AUX_CH_CTL_1);
+
+	/* Start AUX transaction */
+	retval = rockchip_edp_start_aux_transaction(edp);
+	if (retval != 0)
+		dev_dbg(edp->dev, "Aux Transaction fail!\n");
+
+	return retval;
+}
+
+int rockchip_edp_read_byte_from_i2c(struct rockchip_edp_device *edp,
+				    unsigned int device_addr,
+				    unsigned int val_addr,
+				    unsigned int *data)
+{
+	u32 val;
+	int i;
+	int retval;
+
+	for (i = 0; i < 10; i++) {
+		/* Clear AUX CH data buffer */
+		val = BUF_CLR;
+		writel(val, edp->regs + BUFFER_DATA_CTL);
+
+		/* Select EDID device */
+		retval = rockchip_edp_select_i2c_device(edp,
+							device_addr,
+							val_addr);
+		if (retval != 0) {
+			dev_err(edp->dev, "Select EDID device fail!\n");
+			continue;
+		}
+
+		/*
+		 * Set I2C transaction and read data
+		 * If bit 3 is 1, DisplayPort transaction.
+		 * If Bit 3 is 0, I2C transaction.
+		 */
+		val = AUX_TX_COMM_I2C_TRANSACTION | AUX_TX_COMM_READ;
+		writel(val, edp->regs + AUX_CH_CTL_1);
+
+		/* Start AUX transaction */
+		retval = rockchip_edp_start_aux_transaction(edp);
+		if (retval == 0)
+			break;
+
+		dev_dbg(edp->dev, "Aux Transaction fail!\n");
+	}
+
+	/* Read data */
+	if (retval == 0)
+		*data = readl(edp->regs + BUF_DATA_0);
+
+	return retval;
+}
+
+int rockchip_edp_read_bytes_from_i2c(struct rockchip_edp_device *edp,
+				     unsigned int device_addr,
+				     unsigned int val_addr,
+				     unsigned int count,
+				     unsigned char edid[])
+{
+	u32 val;
+	unsigned int i, j;
+	unsigned int cur_data_idx;
+	unsigned int defer = 0;
+	int retval = 0;
+
+	for (i = 0; i < count; i += 16) {
+		for (j = 0; j < 100; j++) {
+			/* Clear AUX CH data buffer */
+			val = BUF_CLR;
+			writel(val, edp->regs + BUFFER_DATA_CTL);
+
+			/* Set normal AUX CH command */
+			val = readl(edp->regs + AUX_CH_CTL_2);
+			val &= ~ADDR_ONLY;
+			writel(val, edp->regs + AUX_CH_CTL_2);
+
+			/*
+			 * If Rx sends defer, Tx sends only reads
+			 * request without sending addres
+			 */
+			if (!defer)
+				retval = rockchip_edp_select_i2c_device(
+						edp, device_addr, val_addr + i);
+			else
+				defer = 0;
+
+			/*
+			 * Set I2C transaction and write data
+			 * If bit 3 is 1, DisplayPort transaction.
+			 * If Bit 3 is 0, I2C transaction.
+			 */
+			val = AUX_LENGTH(16) | AUX_TX_COMM_I2C_TRANSACTION |
+				AUX_TX_COMM_READ;
+			writel(val, edp->regs + AUX_CH_CTL_1);
+
+			/* Start AUX transaction */
+			retval = rockchip_edp_start_aux_transaction(edp);
+			if (retval == 0)
+				break;
+
+			dev_dbg(edp->dev, "Aux Transaction fail!\n");
+
+			/* Check if Rx sends defer */
+			val = readl(edp->regs + AUX_RX_COMM);
+			if (val == AUX_RX_COMM_AUX_DEFER ||
+			    val == AUX_RX_COMM_I2C_DEFER) {
+				dev_err(edp->dev, "Defer: %d\n\n", val);
+				defer = 1;
+			}
+		}
+
+		for (cur_data_idx = 0; cur_data_idx < 16; cur_data_idx++) {
+			val = readl(edp->regs + BUF_DATA_0 + 4 * cur_data_idx);
+			edid[i + cur_data_idx] = (unsigned char)val;
+		}
+	}
+
+	return retval;
+}
+
+void rockchip_edp_set_link_bandwidth(struct rockchip_edp_device *edp,
+				     u32 bwtype)
+{
+	u32 val;
+
+	val = bwtype;
+	if ((bwtype == DP_LINK_BW_2_7) || (bwtype == DP_LINK_BW_1_62))
+		writel(val, edp->regs + LINK_BW_SET);
+}
+
+void rockchip_edp_get_link_bandwidth(struct rockchip_edp_device *edp,
+				     u32 *bwtype)
+{
+	u32 val;
+
+	val = readl(edp->regs + LINK_BW_SET);
+	*bwtype = val;
+}
+
+void rockchip_edp_hw_link_training_en(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = HW_LT_EN;
+	writel(val, edp->regs + HW_LT_CTL);
+}
+
+int rockchip_edp_wait_hw_lt_done(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + DP_INT_STA);
+	if (val&HW_LT_DONE) {
+		writel(val, edp->regs + DP_INT_STA);
+		return 0;
+	}
+
+	return 1;
+}
+
+int rockchip_edp_get_hw_lt_status(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + HW_LT_CTL);
+
+	return (val & HW_LT_ERR_CODE_MASK) >> 4;
+}
+
+void rockchip_edp_set_lane_count(struct rockchip_edp_device *edp, u32 count)
+{
+	u32 val;
+
+	val = count;
+	writel(val, edp->regs + LANE_CNT_SET);
+}
+
+void rockchip_edp_get_lane_count(struct rockchip_edp_device *edp, u32 *count)
+{
+	u32 val;
+
+	val = readl(edp->regs + LANE_CNT_SET);
+	*count = val;
+}
+
+void rockchip_edp_enable_enhanced_mode(struct rockchip_edp_device *edp,
+				       bool enable)
+{
+	u32 val;
+
+	if (enable) {
+		val = readl(edp->regs + SYS_CTL_4);
+		val |= ENHANCED;
+		writel(val, edp->regs + SYS_CTL_4);
+	} else {
+		val = readl(edp->regs + SYS_CTL_4);
+		val &= ~ENHANCED;
+		writel(val, edp->regs + SYS_CTL_4);
+	}
+}
+
+void rockchip_edp_set_training_pattern(struct rockchip_edp_device *edp,
+				       enum pattern_set pattern)
+{
+	u32 val;
+
+	switch (pattern) {
+	case PRBS7:
+		val = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_PRBS7;
+		writel(val, edp->regs + TRAINING_PTN_SET);
+		break;
+	case D10_2:
+		val = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_D10_2;
+		writel(val, edp->regs + TRAINING_PTN_SET);
+		break;
+	case TRAINING_PTN1:
+		val = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN1;
+		writel(val, edp->regs + TRAINING_PTN_SET);
+		break;
+	case TRAINING_PTN2:
+		val = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN2;
+		writel(val, edp->regs + TRAINING_PTN_SET);
+		break;
+	case DP_NONE:
+		val = SCRAMBLING_ENABLE |
+			LINK_QUAL_PATTERN_SET_DISABLE |
+			SW_TRAINING_PATTERN_SET_DISABLE;
+		writel(val, edp->regs + TRAINING_PTN_SET);
+		break;
+	default:
+		break;
+	}
+}
+
+void rockchip_edp_set_lane0_pre_emphasis(struct rockchip_edp_device *edp,
+					 u32 level)
+{
+	u32 val;
+
+	val = level << PRE_EMPHASIS_SET_SHIFT;
+	writel(val, edp->regs + LN0_LINK_TRAINING_CTL);
+}
+
+void rockchip_edp_set_lane1_pre_emphasis(struct rockchip_edp_device *edp,
+					 u32 level)
+{
+	u32 val;
+
+	val = level << PRE_EMPHASIS_SET_SHIFT;
+	writel(val, edp->regs + LN1_LINK_TRAINING_CTL);
+}
+
+void rockchip_edp_set_lane2_pre_emphasis(struct rockchip_edp_device *edp,
+					 u32 level)
+{
+	u32 val;
+
+	val = level << PRE_EMPHASIS_SET_SHIFT;
+	writel(val, edp->regs + LN2_LINK_TRAINING_CTL);
+}
+
+void rockchip_edp_set_lane3_pre_emphasis(struct rockchip_edp_device *edp,
+					 u32 level)
+{
+	u32 val;
+
+	val = level << PRE_EMPHASIS_SET_SHIFT;
+	writel(val, edp->regs + LN3_LINK_TRAINING_CTL);
+}
+
+void rockchip_edp_set_lane0_link_training(struct rockchip_edp_device *edp,
+					  u32 training_lane)
+{
+	u32 val;
+
+	val = training_lane;
+	writel(val, edp->regs + LN0_LINK_TRAINING_CTL);
+}
+
+void rockchip_edp_set_lane1_link_training(struct rockchip_edp_device *edp,
+					  u32 training_lane)
+{
+	u32 val;
+
+	val = training_lane;
+	writel(val, edp->regs + LN1_LINK_TRAINING_CTL);
+}
+
+void rockchip_edp_set_lane2_link_training(struct rockchip_edp_device *edp,
+					  u32 training_lane)
+{
+	u32 val;
+
+	val = training_lane;
+	writel(val, edp->regs + LN2_LINK_TRAINING_CTL);
+}
+
+void rockchip_edp_set_lane3_link_training(struct rockchip_edp_device *edp,
+					  u32 training_lane)
+{
+	u32 val;
+
+	val = training_lane;
+	writel(val, edp->regs + LN3_LINK_TRAINING_CTL);
+}
+
+u32 rockchip_edp_get_lane0_link_training(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + LN0_LINK_TRAINING_CTL);
+	return val;
+}
+
+u32 rockchip_edp_get_lane1_link_training(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + LN1_LINK_TRAINING_CTL);
+	return val;
+}
+
+u32 rockchip_edp_get_lane2_link_training(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + LN2_LINK_TRAINING_CTL);
+	return val;
+}
+
+u32 rockchip_edp_get_lane3_link_training(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + LN3_LINK_TRAINING_CTL);
+	return val;
+}
+
+void rockchip_edp_reset_macro(struct rockchip_edp_device *edp)
+{
+}
+
+int rockchip_edp_init_video(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = VSYNC_DET | VID_FORMAT_CHG | VID_CLK_CHG;
+	writel(val, edp->regs + COMMON_INT_STA_1);
+
+	val = 0x0;
+	writel(val, edp->regs + SYS_CTL_1);
+
+	val = CHA_CRI(4) | CHA_CTRL;
+	writel(val, edp->regs + SYS_CTL_2);
+
+	val = VID_HRES_TH(2) | VID_VRES_TH(0);
+	writel(val, edp->regs + VIDEO_CTL_8);
+
+	return 0;
+}
+
+void rockchip_edp_set_video_color_format(struct rockchip_edp_device *edp,
+					 u32 color_dedpth,
+					 u32 color_space,
+					 u32 dynamic_range,
+					 u32 coeff)
+{
+	u32 val;
+
+	/* Configure the input color dedpth, color space, dynamic range */
+	val = (dynamic_range << IN_D_RANGE_SHIFT) |
+		(color_dedpth << IN_BPC_SHIFT) |
+		(color_space << IN_COLOR_F_SHIFT);
+	writel(val, edp->regs + VIDEO_CTL_2);
+
+	/* Set Input Color YCbCr Coefficients to ITU601 or ITU709 */
+	val = readl(edp->regs + VIDEO_CTL_3);
+	val &= ~IN_YC_COEFFI_MASK;
+	if (coeff)
+		val |= IN_YC_COEFFI_ITU709;
+	else
+		val |= IN_YC_COEFFI_ITU601;
+	writel(val, edp->regs + VIDEO_CTL_3);
+}
+
+int rockchip_edp_is_slave_video_stream_clock_on(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + SYS_CTL_1);
+	writel(val, edp->regs + SYS_CTL_1);
+
+	val = readl(edp->regs + SYS_CTL_1);
+
+	if (!(val & DET_STA)) {
+		dev_dbg(edp->dev, "Input stream clock not detected.\n");
+		return -EINVAL;
+	}
+
+	val = readl(edp->regs + SYS_CTL_2);
+	writel(val, edp->regs + SYS_CTL_2);
+
+	val = readl(edp->regs + SYS_CTL_2);
+	if (val & CHA_STA) {
+		dev_dbg(edp->dev, "Input stream clk is changing\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+void rockchip_edp_set_video_cr_mn(struct rockchip_edp_device *edp,
+				  enum clock_recovery_m_value_type type,
+				  u32 m_value,
+				  u32 n_value)
+{
+	u32 val;
+
+	if (type == REGISTER_M) {
+		val = readl(edp->regs + SYS_CTL_4);
+		val |= FIX_M_VID;
+		writel(val, edp->regs + SYS_CTL_4);
+		val = m_value & 0xff;
+		writel(val, edp->regs + M_VID_0);
+		val = (m_value >> 8) & 0xff;
+		writel(val, edp->regs + M_VID_1);
+		val = (m_value >> 16) & 0xff;
+		writel(val, edp->regs + M_VID_2);
+
+		val = n_value & 0xff;
+		writel(val, edp->regs + N_VID_0);
+		val = (n_value >> 8) & 0xff;
+		writel(val, edp->regs + N_VID_1);
+		val = (n_value >> 16) & 0xff;
+		writel(val, edp->regs + N_VID_2);
+	} else  {
+		val = readl(edp->regs + SYS_CTL_4);
+		val &= ~FIX_M_VID;
+		writel(val, edp->regs + SYS_CTL_4);
+
+		writel(0x00, edp->regs + N_VID_0);
+		writel(0x80, edp->regs + N_VID_1);
+		writel(0x00, edp->regs + N_VID_2);
+	}
+}
+
+void rockchip_edp_set_video_timing_mode(struct rockchip_edp_device *edp,
+					u32 type)
+{
+	u32 val;
+
+	if (type == VIDEO_TIMING_FROM_CAPTURE) {
+		val = readl(edp->regs + VIDEO_CTL_10);
+		val &= ~F_SEL;
+		writel(val, edp->regs + VIDEO_CTL_10);
+	} else {
+		val = readl(edp->regs + VIDEO_CTL_10);
+		val |= F_SEL;
+		writel(val, edp->regs + VIDEO_CTL_10);
+	}
+}
+
+int rockchip_edp_bist_cfg(struct rockchip_edp_device *edp)
+{
+	struct video_info *video_info = &edp->video_info;
+	struct drm_display_mode *mode = &edp->mode;
+	u16 x_total, y_total, x_act;
+	u32 val;
+
+	x_total = mode->htotal;
+	y_total = mode->vtotal;
+	x_act = mode->hdisplay;
+
+	rockchip_edp_set_video_cr_mn(edp, CALCULATED_M, 0, 0);
+	rockchip_edp_set_video_color_format(edp, video_info->color_depth,
+					    video_info->color_space,
+					    video_info->dynamic_range,
+					    video_info->ycbcr_coeff);
+
+	val = y_total & 0xff;
+	writel(val, edp->regs + TOTAL_LINE_CFG_L);
+	val = (y_total >> 8);
+	writel(val, edp->regs + TOTAL_LINE_CFG_H);
+	val = (mode->vdisplay & 0xff);
+	writel(val, edp->regs + ATV_LINE_CFG_L);
+	val = (mode->vdisplay >> 8);
+	writel(val, edp->regs + ATV_LINE_CFG_H);
+	val = (mode->vsync_start - mode->vdisplay);
+	writel(val, edp->regs + VF_PORCH_REG);
+	val = (mode->vsync_end - mode->vsync_start);
+	writel(val, edp->regs + VSYNC_CFG_REG);
+	val = (mode->vtotal - mode->vsync_end);
+	writel(val, edp->regs + VB_PORCH_REG);
+	val = x_total & 0xff;
+	writel(val, edp->regs + TOTAL_PIXELL_REG);
+	val = x_total >> 8;
+	writel(val, edp->regs + TOTAL_PIXELH_REG);
+	val = (x_act & 0xff);
+	writel(val, edp->regs + ATV_PIXELL_REG);
+	val = (x_act >> 8);
+	writel(val, edp->regs + ATV_PIXELH_REG);
+	val = (mode->hsync_start - mode->hdisplay) & 0xff;
+	writel(val, edp->regs + HF_PORCHL_REG);
+	val = (mode->hsync_start - mode->hdisplay) >> 8;
+	writel(val, edp->regs + HF_PORCHH_REG);
+	val = (mode->hsync_end - mode->hsync_start) & 0xff;
+	writel(val, edp->regs + HSYNC_CFGL_REG);
+	val = (mode->hsync_end - mode->hsync_start) >> 8;
+	writel(val, edp->regs + HSYNC_CFGH_REG);
+	val = (mode->htotal - mode->hsync_end) & 0xff;
+	writel(val, edp->regs + HB_PORCHL_REG);
+	val = (mode->htotal - mode->hsync_end)  >> 8;
+	writel(val, edp->regs + HB_PORCHH_REG);
+
+	val = BIST_EN | BIST_WH_64 | BIST_TYPE_COLR_BAR;
+	writel(val, edp->regs + VIDEO_CTL_4);
+
+	val = readl(edp->regs + VIDEO_CTL_10);
+	val &= ~F_SEL;
+	writel(val, edp->regs + VIDEO_CTL_10);
+	return 0;
+}
+
+void rockchip_edp_enable_video_master(struct rockchip_edp_device *edp,
+				      bool enable)
+{
+}
+
+void rockchip_edp_start_video(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + VIDEO_CTL_1);
+	val |= VIDEO_EN;
+	writel(val, edp->regs + VIDEO_CTL_1);
+}
+
+int rockchip_edp_is_video_stream_on(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + SYS_CTL_3);
+	writel(val, edp->regs + SYS_CTL_3);
+
+	val = readl(edp->regs + SYS_CTL_3);
+	if (!(val & STRM_VALID)) {
+		dev_dbg(edp->dev, "Input video stream is not detected.\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+void rockchip_edp_config_video_slave_mode(struct rockchip_edp_device *edp,
+					  struct video_info *video_info)
+{
+	u32 val;
+
+	val = readl(edp->regs + FUNC_EN_1);
+	val &= ~(VID_FIFO_FUNC_EN_N | VID_CAP_FUNC_EN_N);
+	writel(val, edp->regs + FUNC_EN_1);
+
+	val = readl(edp->regs + VIDEO_CTL_10);
+	val &= ~INTERACE_SCAN_CFG;
+	val |= (video_info->interlaced << 2);
+	writel(val, edp->regs + VIDEO_CTL_10);
+
+	val = readl(edp->regs + VIDEO_CTL_10);
+	val &= ~VSYNC_POLARITY_CFG;
+	val |= (video_info->v_sync_polarity << 1);
+	writel(val, edp->regs + VIDEO_CTL_10);
+
+	val = readl(edp->regs + VIDEO_CTL_10);
+	val &= ~HSYNC_POLARITY_CFG;
+	val |= (video_info->h_sync_polarity << 0);
+	writel(val, edp->regs + VIDEO_CTL_10);
+}
+
+void rockchip_edp_enable_scrambling(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + TRAINING_PTN_SET);
+	val &= ~SCRAMBLING_DISABLE;
+	writel(val, edp->regs + TRAINING_PTN_SET);
+}
+
+void rockchip_edp_disable_scrambling(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = readl(edp->regs + TRAINING_PTN_SET);
+	val |= SCRAMBLING_DISABLE;
+	writel(val, edp->regs + TRAINING_PTN_SET);
+}
+
+enum dp_irq_type rockchip_edp_get_irq_type(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	/* Parse hotplug interrupt status register */
+	val = readl(edp->regs + COMMON_INT_STA_4);
+	if (val & PLUG)
+		return DP_IRQ_TYPE_HP_CABLE_IN;
+
+	if (val & HPD_LOST)
+		return DP_IRQ_TYPE_HP_CABLE_OUT;
+
+	if (val & HOTPLUG_CHG)
+		return DP_IRQ_TYPE_HP_CHANGE;
+
+	return DP_IRQ_TYPE_UNKNOWN;
+}
+
+void rockchip_edp_clear_hotplug_interrupts(struct rockchip_edp_device *edp)
+{
+	u32 val;
+
+	val = HOTPLUG_CHG | HPD_LOST | PLUG;
+	writel(val, edp->regs + COMMON_INT_STA_4);
+
+	val = INT_HPD;
+	writel(val, edp->regs + DP_INT_STA);
+}
diff --git a/drivers/gpu/drm/rockchip/rockchip_edp_reg.h b/drivers/gpu/drm/rockchip/rockchip_edp_reg.h
new file mode 100644
index 0000000..b50dd47
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_edp_reg.h
@@ -0,0 +1,345 @@
+/*
+* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+* Author:
+*      Andy yan <andy.yan-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+*      Jeff chen <jeff.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+*
+* based on exynos_dp_reg.h
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*/
+
+#ifndef _ROCKCHIP_EDP_REG_H
+#define _ROCKCHIP_EDP_REG_H
+
+#include <linux/bitops.h>
+
+#define TX_SW_RST				0x14
+#define FUNC_EN_1				0x18
+#define FUNC_EN_2				0x1C
+#define VIDEO_CTL_1				0x20
+#define VIDEO_CTL_2				0x24
+#define VIDEO_CTL_3				0x28
+#define VIDEO_CTL_4				0x2c
+#define VIDEO_CTL_8				0x3C
+#define VIDEO_CTL_10				0x44
+#define TOTAL_LINE_CFG_L			0x48
+#define TOTAL_LINE_CFG_H			0x4c
+#define ATV_LINE_CFG_L				0x50
+#define ATV_LINE_CFG_H				0x54
+#define VF_PORCH_REG				0x58
+#define VSYNC_CFG_REG				0x5c
+#define VB_PORCH_REG				0x60
+#define TOTAL_PIXELL_REG			0x64
+#define TOTAL_PIXELH_REG			0x68
+#define ATV_PIXELL_REG				0x6c
+#define ATV_PIXELH_REG				0x70
+#define HF_PORCHL_REG				0x74
+#define HF_PORCHH_REG				0x78
+#define HSYNC_CFGL_REG				0x7c
+#define HSYNC_CFGH_REG				0x80
+#define HB_PORCHL_REG				0x84
+#define HB_PORCHH_REG				0x88
+#define PLL_REG_1				0xfc
+
+#define SSC_REG					0x104
+#define TX_REG_COMMON				0x114
+#define DP_AUX					0x120
+#define DP_BIAS					0x124
+#define DP_PWRDN				0x12c
+#define DP_RESERVE2				0x134
+
+#define LANE_MAP				0x35C
+#define ANALOG_CTL_2				0x374
+#define AUX_HW_RETRY_CTL			0x390
+#define COMMON_INT_STA_1			0x3C4
+#define COMMON_INT_STA_2			0x3C8
+#define COMMON_INT_STA_3			0x3CC
+#define COMMON_INT_STA_4			0x3D0
+#define DP_INT_STA				0x3DC
+#define COMMON_INT_MASK_1			0x3E0
+#define COMMON_INT_MASK_2			0x3E4
+#define COMMON_INT_MASK_3			0x3E8
+#define COMMON_INT_MASK_4			0x3EC
+#define DP_INT_STA_MASK				0x3F8
+
+#define SYS_CTL_1				0x600
+#define SYS_CTL_2				0x604
+#define SYS_CTL_3				0x608
+#define SYS_CTL_4				0x60C
+#define PKT_SEND_CTL				0x640
+#define HDCP_CTL				0x648
+#define LINK_BW_SET				0x680
+#define LANE_CNT_SET				0x684
+#define TRAINING_PTN_SET			0x688
+#define LN0_LINK_TRAINING_CTL			0x68C
+#define LN1_LINK_TRAINING_CTL			0x690
+#define LN2_LINK_TRAINING_CTL			0x694
+#define LN3_LINK_TRAINING_CTL			0x698
+#define HW_LT_CTL				0x6a0
+#define DEBUG_CTL				0x6C0
+#define HPD_DEGLITCH_L				0x6C4
+#define HPD_DEGLITCH_H				0x6C8
+#define LINK_DEBUG_CTL				0x6E0
+#define M_VID_0					0x700
+#define M_VID_1					0x704
+#define M_VID_2					0x708
+#define N_VID_0					0x70C
+#define N_VID_1					0x710
+#define N_VID_2					0x714
+#define VIDEO_FIFO_THRD				0x730
+#define AUDIO_MARGIN				0x73C
+#define M_VID_GEN_FILTER_TH			0x764
+#define M_AUD_GEN_FILTER_TH			0x778
+#define AUX_CH_STA				0x780
+#define AUX_CH_DEFER_CTL			0x788
+#define AUX_RX_COMM				0x78C
+#define BUFFER_DATA_CTL				0x790
+#define AUX_CH_CTL_1				0x794
+#define DP_AUX_ADDR_7_0				0x798
+#define DP_AUX_ADDR_15_8			0x79C
+#define DP_AUX_ADDR_19_16			0x7A0
+#define AUX_CH_CTL_2				0x7A4
+#define BUF_DATA_0				0x7C0
+#define SOC_GENERAL_CTL				0x800
+#define PLL_REG_2				0x9e4
+#define PLL_REG_3				0x9e8
+#define PLL_REG_4				0x9ec
+#define PLL_REG_5				0xa00
+
+/* ROCKCHIP_EDP_FUNC_EN_1 */
+#define VID_CAP_FUNC_EN_N			BIT(6)
+#define VID_FIFO_FUNC_EN_N			BIT(5)
+#define AUD_FIFO_FUNC_EN_N			BIT(4)
+#define AUD_FUNC_EN_N				BIT(3)
+#define HDCP_FUNC_EN_N				BIT(2)
+#define SW_FUNC_EN_N				BIT(0)
+
+/* ROCKCHIP_EDP_FUNC_EN_2 */
+#define SSC_FUNC_EN_N				BIT(7)
+#define AUX_FUNC_EN_N				BIT(2)
+#define SERDES_FIFO_FUNC_EN_N			BIT(1)
+#define LS_CLK_DOMAIN_FUNC_EN_N			BIT(0)
+
+/* ROCKCHIP_EDP_VIDEO_CTL_1 */
+#define VIDEO_EN				BIT(7)
+#define VIDEO_MUTE				BIT(6)
+
+/* ROCKCHIP_EDP_VIDEO_CTL_1 */
+#define IN_D_RANGE_MASK				(0x1 << 7)
+#define IN_D_RANGE_SHIFT			(7)
+#define IN_D_RANGE_CEA				(0x1 << 7)
+#define IN_D_RANGE_VESA				(0x0 << 7)
+#define IN_BPC_MASK				(0x7 << 4)
+#define IN_BPC_SHIFT				(4)
+#define IN_BPC_12_BITS				(0x3 << 4)
+#define IN_BPC_10_BITS				(0x2 << 4)
+#define IN_BPC_8_BITS				(0x1 << 4)
+#define IN_BPC_6_BITS				(0x0 << 4)
+#define IN_COLOR_F_MASK				(0x3 << 0)
+#define IN_COLOR_F_SHIFT			(0)
+#define IN_COLOR_F_YCBCR444			(0x2 << 0)
+#define IN_COLOR_F_YCBCR422			(0x1 << 0)
+#define IN_COLOR_F_RGB				(0x0 << 0)
+
+/* ROCKCHIP_EDP_VIDEO_CTL_3 */
+#define IN_YC_COEFFI_MASK			(0x1 << 7)
+#define IN_YC_COEFFI_SHIFT			(7)
+#define IN_YC_COEFFI_ITU709			(0x1 << 7)
+#define IN_YC_COEFFI_ITU601			(0x0 << 7)
+#define VID_CHK_UPDATE_TYPE_MASK		(0x1 << 4)
+#define VID_CHK_UPDATE_TYPE_SHIFT		(4)
+#define VID_CHK_UPDATE_TYPE_1			(0x1 << 4)
+#define VID_CHK_UPDATE_TYPE_0			(0x0 << 4)
+
+/* ROCKCHIP_EDP_VIDEO_CTL_4 */
+#define BIST_EN					(0x1 << 3)
+#define BIST_WH_64				(0x1 << 2)
+#define BIST_WH_32				(0x0 << 2)
+#define BIST_TYPE_COLR_BAR			(0x0 << 0)
+#define BIST_TYPE_GRAY_BAR			(0x1 << 0)
+#define BIST_TYPE_MOBILE_BAR			(0x2 << 0)
+
+/* ROCKCHIP_EDP_VIDEO_CTL_8 */
+#define VID_HRES_TH(x)				(((x) & 0xf) << 4)
+#define VID_VRES_TH(x)				(((x) & 0xf) << 0)
+
+/* ROCKCHIP_EDP_VIDEO_CTL_10 */
+#define F_SEL					(0x1 << 4)
+#define INTERACE_SCAN_CFG			(0x1 << 2)
+#define VSYNC_POLARITY_CFG			(0x1 << 1)
+#define HSYNC_POLARITY_CFG			(0x1 << 0)
+
+/* ROCKCHIP_EDP_PLL_REG_1 */
+#define REF_CLK_24M				(0x1 << 1)
+#define REF_CLK_27M				(0x0 << 1)
+
+/* ROCKCHIP_EDP_DP_PWRDN */
+#define PD_INC_BG				BIT(7)
+#define PD_EXP_BG				BIT(6)
+#define PD_AUX					BIT(5)
+#define PD_PLL					BIT(4)
+#define PD_CH3					BIT(3)
+#define PD_CH2					BIT(2)
+#define PD_CH1					BIT(1)
+#define PD_CH0					BIT(0)
+
+/* ROCKCHIP_EDP_LANE_MAP */
+#define LANE3_MAP_LOGIC_LANE_0			(0x0 << 6)
+#define LANE3_MAP_LOGIC_LANE_1			(0x1 << 6)
+#define LANE3_MAP_LOGIC_LANE_2			(0x2 << 6)
+#define LANE3_MAP_LOGIC_LANE_3			(0x3 << 6)
+#define LANE2_MAP_LOGIC_LANE_0			(0x0 << 4)
+#define LANE2_MAP_LOGIC_LANE_1			(0x1 << 4)
+#define LANE2_MAP_LOGIC_LANE_2			(0x2 << 4)
+#define LANE2_MAP_LOGIC_LANE_3			(0x3 << 4)
+#define LANE1_MAP_LOGIC_LANE_0			(0x0 << 2)
+#define LANE1_MAP_LOGIC_LANE_1			(0x1 << 2)
+#define LANE1_MAP_LOGIC_LANE_2			(0x2 << 2)
+#define LANE1_MAP_LOGIC_LANE_3			(0x3 << 2)
+#define LANE0_MAP_LOGIC_LANE_0			(0x0 << 0)
+#define LANE0_MAP_LOGIC_LANE_1			(0x1 << 0)
+#define LANE0_MAP_LOGIC_LANE_2			(0x2 << 0)
+#define LANE0_MAP_LOGIC_LANE_3			(0x3 << 0)
+
+/* ROCKCHIP_EDP_ANALOG_CTL_2 */
+#define SEL_24M					(0x1 << 3)
+
+/* ROCKCHIP_EDP_COMMON_INT_STA_1 */
+#define VSYNC_DET				BIT(7)
+#define PLL_LOCK_CHG				BIT(6)
+#define SPDIF_ERR				BIT(5)
+#define SPDIF_UNSTBL				BIT(4)
+#define VID_FORMAT_CHG				BIT(3)
+#define AUD_CLK_CHG				BIT(2)
+#define VID_CLK_CHG				BIT(1)
+#define SW_INT					BIT(0)
+
+/* ROCKCHIP_EDP_COMMON_INT_STA_2 */
+#define ENC_EN_CHG				BIT(6)
+#define HW_BKSV_RDY				BIT(3)
+#define HW_SHA_DONE				BIT(2)
+#define HW_AUTH_STATE_CHG			BIT(1)
+#define HW_AUTH_DONE				BIT(0)
+
+/* ROCKCHIP_EDP_COMMON_INT_STA_3 */
+#define AFIFO_UNDER				BIT(7)
+#define AFIFO_OVER				BIT(6)
+#define R0_CHK_FLAG				BIT(5)
+
+/* ROCKCHIP_EDP_COMMON_INT_STA_4 */
+#define PSR_ACTIVE				BIT(7)
+#define PSR_INACTIVE				BIT(6)
+#define SPDIF_BI_PHASE_ERR			BIT(5)
+#define HOTPLUG_CHG				BIT(2)
+#define HPD_LOST				BIT(1)
+#define PLUG					BIT(0)
+
+/* ROCKCHIP_EDP_INT_STA */
+#define INT_HPD					BIT(6)
+#define HW_LT_DONE				BIT(5)
+#define SINK_LOST				BIT(3)
+#define LINK_LOST				BIT(2)
+#define RPLY_RECEIV				BIT(1)
+#define AUX_ERR					BIT(0)
+
+/* ROCKCHIP_EDP_INT_CTL */
+#define INT_CTL					0x3FC
+#define SOFT_INT_CTRL				BIT(2)
+#define INT_POL					BIT(0)
+
+/* ROCKCHIP_EDP_SYS_CTL_1 */
+#define DET_STA					BIT(2)
+#define FORCE_DET				BIT(1)
+#define DET_CTRL				BIT(0)
+
+/* ROCKCHIP_EDP_SYS_CTL_2 */
+#define CHA_CRI(x)				(((x) & 0xf) << 4)
+#define CHA_STA					BIT(2)
+#define FORCE_CHA				BIT(1)
+#define CHA_CTRL				BIT(0)
+
+/* ROCKCHIP_EDP_SYS_CTL_3 */
+#define HPD_STATUS				BIT(6)
+#define F_HPD					BIT(5)
+#define HPD_CTRL				BIT(4)
+#define HDCP_RDY				BIT(3)
+#define STRM_VALID				BIT(2)
+#define F_VALID					BIT(1)
+#define VALID_CTRL				BIT(0)
+
+/* ROCKCHIP_EDP_SYS_CTL_4 */
+#define FIX_M_AUD				BIT(4)
+#define ENHANCED				BIT(3)
+#define FIX_M_VID				BIT(2)
+#define M_VID_UPDATE_CTRL			BIT(0)
+
+/* ROCKCHIP_EDP_TRAINING_PTN_SET */
+#define SCRAMBLING_DISABLE			(0x1 << 5)
+#define SCRAMBLING_ENABLE			(0x0 << 5)
+#define LINK_QUAL_PATTERN_SET_MASK		(0x7 << 2)
+#define LINK_QUAL_PATTERN_SET_PRBS7		(0x3 << 2)
+#define LINK_QUAL_PATTERN_SET_D10_2		(0x1 << 2)
+#define LINK_QUAL_PATTERN_SET_DISABLE		(0x0 << 2)
+#define SW_TRAINING_PATTERN_SET_MASK		(0x3 << 0)
+#define SW_TRAINING_PATTERN_SET_PTN2		(0x2 << 0)
+#define SW_TRAINING_PATTERN_SET_PTN1		(0x1 << 0)
+#define SW_TRAINING_PATTERN_SET_DISABLE		(0x0 << 0)
+
+/* ROCKCHIP_EDP_HW_LT_CTL */
+#define HW_LT_ERR_CODE_MASK			0x70
+#define HW_LT_EN				BIT(0)
+
+/* ROCKCHIP_EDP_LN0_LINK_TRAINING_CTL */
+#define PRE_EMPHASIS_SET_MASK			(0x3 << 3)
+#define PRE_EMPHASIS_SET_SHIFT			(3)
+
+/* ROCKCHIP_EDP_DEBUG_CTL */
+#define PLL_LOCK				BIT(4)
+#define F_PLL_LOCK				BIT(3)
+#define PLL_LOCK_CTRL				BIT(2)
+#define POLL_EN					BIT(1)
+#define PN_INV					BIT(0)
+
+/* ROCKCHIP_EDP_AUX_CH_STA */
+#define AUX_BUSY				(0x1 << 4)
+#define AUX_STATUS_MASK				(0xf << 0)
+
+/* ROCKCHIP_EDP_AUX_CH_DEFER_CTL */
+#define DEFER_CTRL_EN				(0x1 << 7)
+#define DEFER_COUNT(x)				(((x) & 0x7f) << 0)
+
+/* ROCKCHIP_EDP_AUX_RX_COMM */
+#define AUX_RX_COMM_I2C_DEFER			(0x2 << 2)
+#define AUX_RX_COMM_AUX_DEFER			(0x2 << 0)
+
+/* ROCKCHIP_EDP_BUFFER_DATA_CTL */
+#define BUF_CLR					(0x1 << 7)
+#define BUF_DATA_COUNT(x)			(((x) & 0xf) << 0)
+
+/* ROCKCHIP_EDP_AUX_CH_CTL_1 */
+#define AUX_LENGTH(x)				(((x - 1) & 0xf) << 4)
+#define AUX_TX_COMM_MASK			(0xf << 0)
+#define AUX_TX_COMM_DP_TRANSACTION		(0x1 << 3)
+#define AUX_TX_COMM_I2C_TRANSACTION		(0x0 << 3)
+#define AUX_TX_COMM_MOT				(0x1 << 2)
+#define AUX_TX_COMM_WRITE			(0x0 << 0)
+#define AUX_TX_COMM_READ			(0x1 << 0)
+
+/* OCKCHIP_EDP_AUX_ADDR_7_0 */
+#define AUX_ADDR_7_0(x)			(((x) >> 0) & 0xff)
+
+/* ROCKCHIP_EDP_AUX_ADDR_15_8 */
+#define AUX_ADDR_15_8(x)		(((x) >> 8) & 0xff)
+
+/* ROCKCHIP_EDP_AUX_ADDR_19_16 */
+#define AUX_ADDR_19_16(x)		(((x) >> 16) & 0x0f)
+
+/* ROCKCHIP_EDP_AUX_CH_CTL_2 */
+#define ADDR_ONLY				BIT(1)
+#define AUX_EN					BIT(0)
+
+#endif /* _ROCKCHIP_EDP_REG_H */
-- 
1.7.9.5


--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v2 1/5] drm/rockchip: Add basic drm driver
  2014-09-18  9:36     ` Mark yao
@ 2014-09-18 14:52       ` Daniel Vetter
  -1 siblings, 0 replies; 38+ messages in thread
From: Daniel Vetter @ 2014-09-18 14:52 UTC (permalink / raw)
  To: Mark yao
  Cc: heiko, Boris BREZILLON, David Airlie, Rob Clark, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Randy Dunlap,
	Grant Likely, Greg Kroah-Hartman, John Stultz, Rom Lemarchand,
	linux-doc, kever.yang, dri-devel, dianders, xjq, zyw, cym,
	linux-rockchip, kfx, wxt, huangtao, devicetree, yxj, marcheu,
	xxm, xw, linux-api, linux-kernel, cf

On Thu, Sep 18, 2014 at 05:36:31PM +0800, Mark yao wrote:
> This patch adds the basic structure of a DRM Driver for Rockchip Socs.
> 
> Signed-off-by: Mark yao <mark.yao@rock-chips.com>
> ---
> Changes in v2:
> - use the component framework to defer main drm driver probe
>   until all VOP devices have been probed.
> - use dma-mapping API with ARM_DMA_USE_IOMMU, create dma mapping by
>   master device and each vop device can shared the drm dma mapping.
> - use drm_crtc_init_with_planes and drm_universal_plane_init.
> - remove unnecessary middle layers.
> - add cursor set, move funcs to rockchip drm crtc.
> - use vop reset at first init
> - reference framebuffer when used and unreference when swap out vop
> 

> +static const struct drm_crtc_funcs rockchip_crtc_funcs = {
> +	.set_config = drm_crtc_helper_set_config,
> +	.page_flip = rockchip_drm_crtc_page_flip,
> +	.destroy = rockchip_drm_crtc_destroy,
> +	.cursor_set = vop_crtc_cursor_set,
> +	.cursor_move = vop_crtc_cursor_move,

If you expose your cursor plane as a universal you don't need to implement
these two cursor functions at all. Actually the core never calls them, see
drm_mode_cursor_universal. So if you want to expose cursors, please use
universal cursor plane support (like i915).

In general that's how new drivers should expose cursors since without
universal planes support cursors will not be supported with the atomic
ioctl. Since your cursor code just calls the relevant plane functions that
should even simplify your driver ;-)

Cheers, Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* Re: [PATCH v2 1/5] drm/rockchip: Add basic drm driver
@ 2014-09-18 14:52       ` Daniel Vetter
  0 siblings, 0 replies; 38+ messages in thread
From: Daniel Vetter @ 2014-09-18 14:52 UTC (permalink / raw)
  To: Mark yao
  Cc: heiko, Boris BREZILLON, David Airlie, Rob Clark, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Randy Dunlap,
	Grant Likely, Greg Kroah-Hartman, John Stultz, Rom Lemarchand,
	linux-doc, kever.yang, dri-devel, dianders, xjq, zyw, cym,
	linux-rockchip, kfx, wxt, huangtao, devicetree, yxj, marcheu,
	xxm

On Thu, Sep 18, 2014 at 05:36:31PM +0800, Mark yao wrote:
> This patch adds the basic structure of a DRM Driver for Rockchip Socs.
> 
> Signed-off-by: Mark yao <mark.yao@rock-chips.com>
> ---
> Changes in v2:
> - use the component framework to defer main drm driver probe
>   until all VOP devices have been probed.
> - use dma-mapping API with ARM_DMA_USE_IOMMU, create dma mapping by
>   master device and each vop device can shared the drm dma mapping.
> - use drm_crtc_init_with_planes and drm_universal_plane_init.
> - remove unnecessary middle layers.
> - add cursor set, move funcs to rockchip drm crtc.
> - use vop reset at first init
> - reference framebuffer when used and unreference when swap out vop
> 

> +static const struct drm_crtc_funcs rockchip_crtc_funcs = {
> +	.set_config = drm_crtc_helper_set_config,
> +	.page_flip = rockchip_drm_crtc_page_flip,
> +	.destroy = rockchip_drm_crtc_destroy,
> +	.cursor_set = vop_crtc_cursor_set,
> +	.cursor_move = vop_crtc_cursor_move,

If you expose your cursor plane as a universal you don't need to implement
these two cursor functions at all. Actually the core never calls them, see
drm_mode_cursor_universal. So if you want to expose cursors, please use
universal cursor plane support (like i915).

In general that's how new drivers should expose cursors since without
universal planes support cursors will not be supported with the atomic
ioctl. Since your cursor code just calls the relevant plane functions that
should even simplify your driver ;-)

Cheers, Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* Re: [PATCH v2 1/5] drm/rockchip: Add basic drm driver
  2014-09-18 14:52       ` Daniel Vetter
@ 2014-09-18 14:53         ` Daniel Vetter
  -1 siblings, 0 replies; 38+ messages in thread
From: Daniel Vetter @ 2014-09-18 14:53 UTC (permalink / raw)
  To: Mark yao
  Cc: heiko, Boris BREZILLON, David Airlie, Rob Clark, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Randy Dunlap,
	Grant Likely, Greg Kroah-Hartman, John Stultz, Rom Lemarchand,
	linux-doc, kever.yang, dri-devel, dianders, xjq, zyw, cym,
	linux-rockchip, kfx, wxt, huangtao, devicetree, yxj, marcheu,
	xxm, xw, linux-api, linux-kernel, cf

On Thu, Sep 18, 2014 at 04:52:14PM +0200, Daniel Vetter wrote:
> On Thu, Sep 18, 2014 at 05:36:31PM +0800, Mark yao wrote:
> > This patch adds the basic structure of a DRM Driver for Rockchip Socs.
> > 
> > Signed-off-by: Mark yao <mark.yao@rock-chips.com>
> > ---
> > Changes in v2:
> > - use the component framework to defer main drm driver probe
> >   until all VOP devices have been probed.
> > - use dma-mapping API with ARM_DMA_USE_IOMMU, create dma mapping by
> >   master device and each vop device can shared the drm dma mapping.
> > - use drm_crtc_init_with_planes and drm_universal_plane_init.
> > - remove unnecessary middle layers.
> > - add cursor set, move funcs to rockchip drm crtc.
> > - use vop reset at first init
> > - reference framebuffer when used and unreference when swap out vop
> > 
> 
> > +static const struct drm_crtc_funcs rockchip_crtc_funcs = {
> > +	.set_config = drm_crtc_helper_set_config,
> > +	.page_flip = rockchip_drm_crtc_page_flip,
> > +	.destroy = rockchip_drm_crtc_destroy,
> > +	.cursor_set = vop_crtc_cursor_set,
> > +	.cursor_move = vop_crtc_cursor_move,
> 
> If you expose your cursor plane as a universal you don't need to implement
> these two cursor functions at all. Actually the core never calls them, see
> drm_mode_cursor_universal. So if you want to expose cursors, please use
> universal cursor plane support (like i915).
> 
> In general that's how new drivers should expose cursors since without
> universal planes support cursors will not be supported with the atomic
> ioctl. Since your cursor code just calls the relevant plane functions that
> should even simplify your driver ;-)

Actually you already initialize with cursor universal planes, so all this
code can simply be removed.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* Re: [PATCH v2 1/5] drm/rockchip: Add basic drm driver
@ 2014-09-18 14:53         ` Daniel Vetter
  0 siblings, 0 replies; 38+ messages in thread
From: Daniel Vetter @ 2014-09-18 14:53 UTC (permalink / raw)
  To: Mark yao
  Cc: heiko, Boris BREZILLON, David Airlie, Rob Clark, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Randy Dunlap,
	Grant Likely, Greg Kroah-Hartman, John Stultz, Rom Lemarchand,
	linux-doc, kever.yang, dri-devel, dianders, xjq, zyw, cym,
	linux-rockchip, kfx, wxt, huangtao, devicetree, yxj, marcheu,
	xxm

On Thu, Sep 18, 2014 at 04:52:14PM +0200, Daniel Vetter wrote:
> On Thu, Sep 18, 2014 at 05:36:31PM +0800, Mark yao wrote:
> > This patch adds the basic structure of a DRM Driver for Rockchip Socs.
> > 
> > Signed-off-by: Mark yao <mark.yao@rock-chips.com>
> > ---
> > Changes in v2:
> > - use the component framework to defer main drm driver probe
> >   until all VOP devices have been probed.
> > - use dma-mapping API with ARM_DMA_USE_IOMMU, create dma mapping by
> >   master device and each vop device can shared the drm dma mapping.
> > - use drm_crtc_init_with_planes and drm_universal_plane_init.
> > - remove unnecessary middle layers.
> > - add cursor set, move funcs to rockchip drm crtc.
> > - use vop reset at first init
> > - reference framebuffer when used and unreference when swap out vop
> > 
> 
> > +static const struct drm_crtc_funcs rockchip_crtc_funcs = {
> > +	.set_config = drm_crtc_helper_set_config,
> > +	.page_flip = rockchip_drm_crtc_page_flip,
> > +	.destroy = rockchip_drm_crtc_destroy,
> > +	.cursor_set = vop_crtc_cursor_set,
> > +	.cursor_move = vop_crtc_cursor_move,
> 
> If you expose your cursor plane as a universal you don't need to implement
> these two cursor functions at all. Actually the core never calls them, see
> drm_mode_cursor_universal. So if you want to expose cursors, please use
> universal cursor plane support (like i915).
> 
> In general that's how new drivers should expose cursors since without
> universal planes support cursors will not be supported with the atomic
> ioctl. Since your cursor code just calls the relevant plane functions that
> should even simplify your driver ;-)

Actually you already initialize with cursor universal planes, so all this
code can simply be removed.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* Re: [PATCH v2 1/5] drm/rockchip: Add basic drm driver
  2014-09-18 14:53         ` Daniel Vetter
@ 2014-09-19  2:03           ` yaozq
  -1 siblings, 0 replies; 38+ messages in thread
From: yaozq @ 2014-09-19  2:03 UTC (permalink / raw)
  To: Mark yao, heiko, Boris BREZILLON, David Airlie, Rob Clark,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Randy Dunlap, Grant Likely, Greg Kroah-Hartman, John Stultz,
	Rom Lemarchand, linux-doc, kever.yang, dri-devel, dianders, xjq,
	zyw, cym, linux-rockchip, kfx, wxt, huangtao, devicetree, yxj,
	marcheu, xxm, xw, linux-api, linux-kernel, cf

On 2014年09月18日 22:53, Daniel Vetter wrote:
> On Thu, Sep 18, 2014 at 04:52:14PM +0200, Daniel Vetter wrote:
>> On Thu, Sep 18, 2014 at 05:36:31PM +0800, Mark yao wrote:
>>> This patch adds the basic structure of a DRM Driver for Rockchip Socs.
>>>
>>> Signed-off-by: Mark yao <mark.yao@rock-chips.com>
>>> ---
>>> Changes in v2:
>>> - use the component framework to defer main drm driver probe
>>>    until all VOP devices have been probed.
>>> - use dma-mapping API with ARM_DMA_USE_IOMMU, create dma mapping by
>>>    master device and each vop device can shared the drm dma mapping.
>>> - use drm_crtc_init_with_planes and drm_universal_plane_init.
>>> - remove unnecessary middle layers.
>>> - add cursor set, move funcs to rockchip drm crtc.
>>> - use vop reset at first init
>>> - reference framebuffer when used and unreference when swap out vop
>>>
>>> +static const struct drm_crtc_funcs rockchip_crtc_funcs = {
>>> +	.set_config = drm_crtc_helper_set_config,
>>> +	.page_flip = rockchip_drm_crtc_page_flip,
>>> +	.destroy = rockchip_drm_crtc_destroy,
>>> +	.cursor_set = vop_crtc_cursor_set,
>>> +	.cursor_move = vop_crtc_cursor_move,
>> If you expose your cursor plane as a universal you don't need to implement
>> these two cursor functions at all. Actually the core never calls them, see
>> drm_mode_cursor_universal. So if you want to expose cursors, please use
>> universal cursor plane support (like i915).
>>
>> In general that's how new drivers should expose cursors since without
>> universal planes support cursors will not be supported with the atomic
>> ioctl. Since your cursor code just calls the relevant plane functions that
>> should even simplify your driver ;-)
> Actually you already initialize with cursor universal planes, so all this
> code can simply be removed.
> -Daniel
I see, drm_mode_cursor_universal is nice, I would test and use it.
-Mark


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

* Re: [PATCH v2 1/5] drm/rockchip: Add basic drm driver
@ 2014-09-19  2:03           ` yaozq
  0 siblings, 0 replies; 38+ messages in thread
From: yaozq @ 2014-09-19  2:03 UTC (permalink / raw)
  To: Mark yao, heiko, Boris BREZILLON, David Airlie, Rob Clark,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Randy Dunlap, Grant Likely, Greg Kroah-Hartman, John Stultz,
	Rom Lemarchand, linux-doc, kever.yang, dri-devel, dianders, xjq,
	zyw, cym, linux-rockchip, kfx, wxt, huangtao, devicetree, yxj

On 2014年09月18日 22:53, Daniel Vetter wrote:
> On Thu, Sep 18, 2014 at 04:52:14PM +0200, Daniel Vetter wrote:
>> On Thu, Sep 18, 2014 at 05:36:31PM +0800, Mark yao wrote:
>>> This patch adds the basic structure of a DRM Driver for Rockchip Socs.
>>>
>>> Signed-off-by: Mark yao <mark.yao@rock-chips.com>
>>> ---
>>> Changes in v2:
>>> - use the component framework to defer main drm driver probe
>>>    until all VOP devices have been probed.
>>> - use dma-mapping API with ARM_DMA_USE_IOMMU, create dma mapping by
>>>    master device and each vop device can shared the drm dma mapping.
>>> - use drm_crtc_init_with_planes and drm_universal_plane_init.
>>> - remove unnecessary middle layers.
>>> - add cursor set, move funcs to rockchip drm crtc.
>>> - use vop reset at first init
>>> - reference framebuffer when used and unreference when swap out vop
>>>
>>> +static const struct drm_crtc_funcs rockchip_crtc_funcs = {
>>> +	.set_config = drm_crtc_helper_set_config,
>>> +	.page_flip = rockchip_drm_crtc_page_flip,
>>> +	.destroy = rockchip_drm_crtc_destroy,
>>> +	.cursor_set = vop_crtc_cursor_set,
>>> +	.cursor_move = vop_crtc_cursor_move,
>> If you expose your cursor plane as a universal you don't need to implement
>> these two cursor functions at all. Actually the core never calls them, see
>> drm_mode_cursor_universal. So if you want to expose cursors, please use
>> universal cursor plane support (like i915).
>>
>> In general that's how new drivers should expose cursors since without
>> universal planes support cursors will not be supported with the atomic
>> ioctl. Since your cursor code just calls the relevant plane functions that
>> should even simplify your driver ;-)
> Actually you already initialize with cursor universal planes, so all this
> code can simply be removed.
> -Daniel
I see, drm_mode_cursor_universal is nice, I would test and use it.
-Mark


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

end of thread, other threads:[~2014-09-19  2:04 UTC | newest]

Thread overview: 38+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-08-04  4:41 [PATCH 0/9] Add drm driver for Rockchip Socs mark yao
2014-08-04  4:45 ` [PATCH 1/9] drm: " mark yao
2014-08-04 14:46   ` Daniel Vetter
2014-08-04 14:46     ` Daniel Vetter
2014-08-07  8:32   ` mark yao
2014-08-04  4:47 ` [PATCH 2/9] Add devicetree bindings for panels used by the Rockchip DRM mark yao
2014-08-04  4:47   ` mark yao
2014-08-04  4:48 ` [PATCH 3/9] drm: add driver " mark yao
2014-08-04  4:48   ` mark yao
2014-08-04  4:50 ` [PATCH 4/9] Add devicetree bindings for Rockchip lcd controller mark yao
2014-08-04  4:50   ` mark yao
2014-08-04  4:51 ` [PATCH 5/9] drm: add Rockchip rk3288 lcd controller driver mark yao
2014-08-04  4:51   ` mark yao
2014-08-04  4:53 ` [PATCH 6/9] Add devicetree bindings for Rockchip Soc LVDS mark yao
2014-08-04  4:53   ` mark yao
2014-08-04  4:54 ` [PATCH 7/9] drm: add Rockchip Soc rk3288 lvds connector mark yao
2014-08-04  4:54   ` mark yao
2014-08-04  4:55 ` [PATCH 8/9] Add devicetree bindings for Rockchip Soc EDP mark yao
2014-08-04 19:39   ` Heiko Stübner
2014-08-04 19:39     ` Heiko Stübner
2014-08-04  4:57 ` [PATCH 9/9] drm: add Rockchip Soc rk3288 edp connector mark yao
2014-08-04  4:57   ` mark yao
2014-09-18  9:34 ` [PATCH v2 0/5] Add drm driver for Rockchip Socs Mark yao
2014-09-18  9:34   ` Mark yao
2014-09-18  9:36   ` [PATCH v2 1/5] drm/rockchip: Add basic drm driver Mark yao
2014-09-18  9:36     ` Mark yao
2014-09-18 14:52     ` Daniel Vetter
2014-09-18 14:52       ` Daniel Vetter
2014-09-18 14:53       ` Daniel Vetter
2014-09-18 14:53         ` Daniel Vetter
2014-09-19  2:03         ` yaozq
2014-09-19  2:03           ` yaozq
2014-09-18  9:37   ` [PATCH v2 2/5] dt-bindings: video: Add for rockchip display subsytem Mark yao
2014-09-18  9:37     ` Mark yao
2014-09-18  9:39   ` [PATCH v2 3/5] dt-bindings: video: Add documentation for rockchip vop Mark yao
2014-09-18  9:41   ` [PATCH v2 4/5] dt-bindings: video: Add documentation for rockchip edp Mark yao
2014-09-18  9:42   ` [PATCH v2 5/5] drm/rockchip: Add support for Rockchip Soc EDP Mark yao
2014-09-18  9:42     ` Mark yao

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.