Browse Source

Merge tag 'drm-hisilicon-next-2016-04-29' of github.com:xin3liang/linux into drm-next

drm-hisilicon-next for 4.7

Add new hisilicon kirin drm driver:
- Add maintainer for hisilicon DRM driver
- Add support for external bridge
- Add designware dsi host driver
- Add designware dsi encoder driver
- Add cma fbdev and hotplug
- Add vblank driver for ADE
- Add plane driver for ADE
- Add crtc driver for ADE
- Add hisilicon kirin drm master driver
- Add device tree binding for hi6220 display subsystem

* tag 'drm-hisilicon-next-2016-04-29' of github.com:xin3liang/linux:
  MAINTAINERS: Add maintainer for hisilicon DRM driver
  drm/hisilicon: Add support for external bridge
  drm/hisilicon: Add designware dsi host driver
  drm/hisilicon: Add designware dsi encoder driver
  drm/hisilicon: Add cma fbdev and hotplug
  drm/hisilicon: Add vblank driver for ADE
  drm/hisilicon: Add plane driver for ADE
  drm/hisilicon: Add crtc driver for ADE
  drm/hisilicon: Add hisilicon kirin drm master driver
  drm/hisilicon: Add device tree binding for hi6220 display subsystem
Dave Airlie 9 năm trước cách đây
mục cha
commit
4946dd2e14

+ 72 - 0
Documentation/devicetree/bindings/display/hisilicon/dw-dsi.txt

@@ -0,0 +1,72 @@
+Device-Tree bindings for DesignWare DSI Host Controller v1.20a driver
+
+A DSI Host Controller resides in the middle of display controller and external
+HDMI converter or panel.
+
+Required properties:
+- compatible: value should be "hisilicon,hi6220-dsi".
+- reg: physical base address and length of dsi controller's registers.
+- clocks: contains APB clock phandle + clock-specifier pair.
+- clock-names: should be "pclk".
+- ports: contains DSI controller input and output sub port.
+  The input port connects to ADE output port with the reg value "0".
+  The output port with the reg value "1", it could connect to panel or
+  any other bridge endpoints.
+  See Documentation/devicetree/bindings/graph.txt for more device graph info.
+
+A example of HiKey board hi6220 SoC and board specific DT entry:
+Example:
+
+SoC specific:
+	dsi: dsi@f4107800 {
+		compatible = "hisilicon,hi6220-dsi";
+		reg = <0x0 0xf4107800 0x0 0x100>;
+		clocks = <&media_ctrl  HI6220_DSI_PCLK>;
+		clock-names = "pclk";
+		status = "disabled";
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			/* 0 for input port */
+			port@0 {
+				reg = <0>;
+				dsi_in: endpoint {
+					remote-endpoint = <&ade_out>;
+				};
+			};
+		};
+	};
+
+
+Board specific:
+	&dsi {
+		status = "ok";
+
+		ports {
+			/* 1 for output port */
+			port@1 {
+				reg = <1>;
+
+				dsi_out0: endpoint@0 {
+					remote-endpoint = <&adv7533_in>;
+				};
+			};
+		};
+	};
+
+	&i2c2 {
+		...
+
+		adv7533: adv7533@39 {
+			...
+
+			port {
+				adv7533_in: endpoint {
+					remote-endpoint = <&dsi_out0>;
+				};
+			};
+		};
+	};
+

+ 64 - 0
Documentation/devicetree/bindings/display/hisilicon/hisi-ade.txt

@@ -0,0 +1,64 @@
+Device-Tree bindings for hisilicon ADE display controller driver
+
+ADE (Advanced Display Engine) is the display controller which grab image
+data from memory, do composition, do post image processing, generate RGB
+timing stream and transfer to DSI.
+
+Required properties:
+- compatible: value should be "hisilicon,hi6220-ade".
+- reg: physical base address and length of the ADE controller's registers.
+- hisilicon,noc-syscon: ADE NOC QoS syscon.
+- resets: The ADE reset controller node.
+- interrupt: the ldi vblank interrupt number used.
+- clocks: a list of phandle + clock-specifier pairs, one for each entry
+  in clock-names.
+- clock-names: should contain:
+  "clk_ade_core" for the ADE core clock.
+  "clk_codec_jpeg" for the media NOC QoS clock, which use the same clock with
+  jpeg codec.
+  "clk_ade_pix" for the ADE pixel clok.
+- assigned-clocks: Should contain "clk_ade_core" and "clk_codec_jpeg" clocks'
+  phandle + clock-specifier pairs.
+- assigned-clock-rates: clock rates, one for each entry in assigned-clocks.
+  The rate of "clk_ade_core" could be "360000000" or "180000000";
+  The rate of "clk_codec_jpeg" could be or less than "1440000000".
+  These rate values could be configured according to performance and power
+  consumption.
+- port: the output port. This contains one endpoint subnode, with its
+  remote-endpoint set to the phandle of the connected DSI input endpoint.
+  See Documentation/devicetree/bindings/graph.txt for more device graph info.
+
+Optional properties:
+- dma-coherent: Present if dma operations are coherent.
+
+
+A example of HiKey board hi6220 SoC specific DT entry:
+Example:
+
+	ade: ade@f4100000 {
+		compatible = "hisilicon,hi6220-ade";
+		reg = <0x0 0xf4100000 0x0 0x7800>;
+		reg-names = "ade_base";
+		hisilicon,noc-syscon = <&medianoc_ade>;
+		resets = <&media_ctrl MEDIA_ADE>;
+		interrupts = <0 115 4>; /* ldi interrupt */
+
+		clocks = <&media_ctrl HI6220_ADE_CORE>,
+			 <&media_ctrl HI6220_CODEC_JPEG>,
+			 <&media_ctrl HI6220_ADE_PIX_SRC>;
+		/*clock name*/
+		clock-names  = "clk_ade_core",
+			       "clk_codec_jpeg",
+			       "clk_ade_pix";
+
+		assigned-clocks = <&media_ctrl HI6220_ADE_CORE>,
+			<&media_ctrl HI6220_CODEC_JPEG>;
+		assigned-clock-rates = <360000000>, <288000000>;
+		dma-coherent;
+
+		port {
+			ade_out: endpoint {
+				remote-endpoint = <&dsi_in>;
+			};
+		};
+	};

+ 10 - 0
MAINTAINERS

@@ -3853,6 +3853,16 @@ T:	git git://github.com/patjak/drm-gma500
 S:	Maintained
 F:	drivers/gpu/drm/gma500/
 
+DRM DRIVERS FOR HISILICON
+M:	Xinliang Liu <z.liuxinliang@hisilicon.com>
+R:	Xinwei Kong <kong.kongxinwei@hisilicon.com>
+R:	Chen Feng <puck.chen@hisilicon.com>
+L:	dri-devel@lists.freedesktop.org
+T:	git git://github.com/xin3liang/linux.git
+S:	Maintained
+F:	drivers/gpu/drm/hisilicon/
+F:	Documentation/devicetree/bindings/display/hisilicon/
+
 DRM DRIVERS FOR NVIDIA TEGRA
 M:	Thierry Reding <thierry.reding@gmail.com>
 M:	Terje Bergström <tbergstrom@nvidia.com>

+ 2 - 0
drivers/gpu/drm/Kconfig

@@ -285,3 +285,5 @@ source "drivers/gpu/drm/vc4/Kconfig"
 source "drivers/gpu/drm/etnaviv/Kconfig"
 
 source "drivers/gpu/drm/arc/Kconfig"
+
+source "drivers/gpu/drm/hisilicon/Kconfig"

+ 1 - 0
drivers/gpu/drm/Makefile

@@ -80,3 +80,4 @@ obj-y			+= bridge/
 obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu/
 obj-$(CONFIG_DRM_ETNAVIV) += etnaviv/
 obj-$(CONFIG_DRM_ARCPGU)+= arc/
+obj-y			+= hisilicon/

+ 5 - 0
drivers/gpu/drm/hisilicon/Kconfig

@@ -0,0 +1,5 @@
+#
+# hisilicon drm device configuration.
+# Please keep this list sorted alphabetically
+
+source "drivers/gpu/drm/hisilicon/kirin/Kconfig"

+ 5 - 0
drivers/gpu/drm/hisilicon/Makefile

@@ -0,0 +1,5 @@
+#
+# Makefile for hisilicon drm drivers.
+# Please keep this list sorted alphabetically
+
+obj-$(CONFIG_DRM_HISI_KIRIN) += kirin/

+ 18 - 0
drivers/gpu/drm/hisilicon/kirin/Kconfig

@@ -0,0 +1,18 @@
+config DRM_HISI_KIRIN
+	tristate "DRM Support for Hisilicon Kirin series SoCs Platform"
+	depends on DRM && OF && ARM64
+	select DRM_KMS_HELPER
+	select DRM_GEM_CMA_HELPER
+	select DRM_KMS_CMA_HELPER
+	help
+	  Choose this option if you have a hisilicon Kirin chipsets(hi6220).
+	  If M is selected the module will be called kirin-drm.
+
+config HISI_KIRIN_DW_DSI
+	tristate "HiSilicon Kirin specific extensions for Synopsys DW MIPI DSI"
+	depends on DRM_HISI_KIRIN
+	select DRM_MIPI_DSI
+	help
+	 This selects support for HiSilicon Kirin SoC specific extensions for
+	 the Synopsys DesignWare DSI driver. If you want to enable MIPI DSI on
+	 hi6220 based SoC, you should selet this option.

+ 6 - 0
drivers/gpu/drm/hisilicon/kirin/Makefile

@@ -0,0 +1,6 @@
+kirin-drm-y := kirin_drm_drv.o \
+	       kirin_drm_ade.o
+
+obj-$(CONFIG_DRM_HISI_KIRIN) += kirin-drm.o
+
+obj-$(CONFIG_HISI_KIRIN_DW_DSI) += dw_drm_dsi.o

+ 857 - 0
drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c

@@ -0,0 +1,857 @@
+/*
+ * DesignWare MIPI DSI Host Controller v1.02 driver
+ *
+ * Copyright (c) 2016 Linaro Limited.
+ * Copyright (c) 2014-2016 Hisilicon Limited.
+ *
+ * Author:
+ *	Xinliang Liu <z.liuxinliang@hisilicon.com>
+ *	Xinliang Liu <xinliang.liu@linaro.org>
+ *	Xinwei Kong <kong.kongxinwei@hisilicon.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/of_graph.h>
+
+#include <drm/drm_of.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_encoder_slave.h>
+#include <drm/drm_atomic_helper.h>
+
+#include "dw_dsi_reg.h"
+
+#define MAX_TX_ESC_CLK		10
+#define ROUND(x, y)		((x) / (y) + \
+				((x) % (y) * 10 / (y) >= 5 ? 1 : 0))
+#define PHY_REF_CLK_RATE	19200000
+#define PHY_REF_CLK_PERIOD_PS	(1000000000 / (PHY_REF_CLK_RATE / 1000))
+
+#define encoder_to_dsi(encoder) \
+	container_of(encoder, struct dw_dsi, encoder)
+#define host_to_dsi(host) \
+	container_of(host, struct dw_dsi, host)
+
+struct mipi_phy_params {
+	u32 clk_t_lpx;
+	u32 clk_t_hs_prepare;
+	u32 clk_t_hs_zero;
+	u32 clk_t_hs_trial;
+	u32 clk_t_wakeup;
+	u32 data_t_lpx;
+	u32 data_t_hs_prepare;
+	u32 data_t_hs_zero;
+	u32 data_t_hs_trial;
+	u32 data_t_ta_go;
+	u32 data_t_ta_get;
+	u32 data_t_wakeup;
+	u32 hstx_ckg_sel;
+	u32 pll_fbd_div5f;
+	u32 pll_fbd_div1f;
+	u32 pll_fbd_2p;
+	u32 pll_enbwt;
+	u32 pll_fbd_p;
+	u32 pll_fbd_s;
+	u32 pll_pre_div1p;
+	u32 pll_pre_p;
+	u32 pll_vco_750M;
+	u32 pll_lpf_rs;
+	u32 pll_lpf_cs;
+	u32 clklp2hs_time;
+	u32 clkhs2lp_time;
+	u32 lp2hs_time;
+	u32 hs2lp_time;
+	u32 clk_to_data_delay;
+	u32 data_to_clk_delay;
+	u32 lane_byte_clk_kHz;
+	u32 clk_division;
+};
+
+struct dsi_hw_ctx {
+	void __iomem *base;
+	struct clk *pclk;
+};
+
+struct dw_dsi {
+	struct drm_encoder encoder;
+	struct drm_bridge *bridge;
+	struct mipi_dsi_host host;
+	struct drm_display_mode cur_mode;
+	struct dsi_hw_ctx *ctx;
+	struct mipi_phy_params phy;
+
+	u32 lanes;
+	enum mipi_dsi_pixel_format format;
+	unsigned long mode_flags;
+	bool enable;
+};
+
+struct dsi_data {
+	struct dw_dsi dsi;
+	struct dsi_hw_ctx ctx;
+};
+
+struct dsi_phy_range {
+	u32 min_range_kHz;
+	u32 max_range_kHz;
+	u32 pll_vco_750M;
+	u32 hstx_ckg_sel;
+};
+
+static const struct dsi_phy_range dphy_range_info[] = {
+	{   46875,    62500,   1,    7 },
+	{   62500,    93750,   0,    7 },
+	{   93750,   125000,   1,    6 },
+	{  125000,   187500,   0,    6 },
+	{  187500,   250000,   1,    5 },
+	{  250000,   375000,   0,    5 },
+	{  375000,   500000,   1,    4 },
+	{  500000,   750000,   0,    4 },
+	{  750000,  1000000,   1,    0 },
+	{ 1000000,  1500000,   0,    0 }
+};
+
+static u32 dsi_calc_phy_rate(u32 req_kHz, struct mipi_phy_params *phy)
+{
+	u32 ref_clk_ps = PHY_REF_CLK_PERIOD_PS;
+	u32 tmp_kHz = req_kHz;
+	u32 i = 0;
+	u32 q_pll = 1;
+	u32 m_pll = 0;
+	u32 n_pll = 0;
+	u32 r_pll = 1;
+	u32 m_n = 0;
+	u32 m_n_int = 0;
+	u32 f_kHz = 0;
+	u64 temp;
+
+	/*
+	 * Find a rate >= req_kHz.
+	 */
+	do {
+		f_kHz = tmp_kHz;
+
+		for (i = 0; i < ARRAY_SIZE(dphy_range_info); i++)
+			if (f_kHz >= dphy_range_info[i].min_range_kHz &&
+			    f_kHz <= dphy_range_info[i].max_range_kHz)
+				break;
+
+		if (i == ARRAY_SIZE(dphy_range_info)) {
+			DRM_ERROR("%dkHz out of range\n", f_kHz);
+			return 0;
+		}
+
+		phy->pll_vco_750M = dphy_range_info[i].pll_vco_750M;
+		phy->hstx_ckg_sel = dphy_range_info[i].hstx_ckg_sel;
+
+		if (phy->hstx_ckg_sel <= 7 &&
+		    phy->hstx_ckg_sel >= 4)
+			q_pll = 0x10 >> (7 - phy->hstx_ckg_sel);
+
+		temp = f_kHz * (u64)q_pll * (u64)ref_clk_ps;
+		m_n_int = temp / (u64)1000000000;
+		m_n = (temp % (u64)1000000000) / (u64)100000000;
+
+		if (m_n_int % 2 == 0) {
+			if (m_n * 6 >= 50) {
+				n_pll = 2;
+				m_pll = (m_n_int + 1) * n_pll;
+			} else if (m_n * 6 >= 30) {
+				n_pll = 3;
+				m_pll = m_n_int * n_pll + 2;
+			} else {
+				n_pll = 1;
+				m_pll = m_n_int * n_pll;
+			}
+		} else {
+			if (m_n * 6 >= 50) {
+				n_pll = 1;
+				m_pll = (m_n_int + 1) * n_pll;
+			} else if (m_n * 6 >= 30) {
+				n_pll = 1;
+				m_pll = (m_n_int + 1) * n_pll;
+			} else if (m_n * 6 >= 10) {
+				n_pll = 3;
+				m_pll = m_n_int * n_pll + 1;
+			} else {
+				n_pll = 2;
+				m_pll = m_n_int * n_pll;
+			}
+		}
+
+		if (n_pll == 1) {
+			phy->pll_fbd_p = 0;
+			phy->pll_pre_div1p = 1;
+		} else {
+			phy->pll_fbd_p = n_pll;
+			phy->pll_pre_div1p = 0;
+		}
+
+		if (phy->pll_fbd_2p <= 7 && phy->pll_fbd_2p >= 4)
+			r_pll = 0x10 >> (7 - phy->pll_fbd_2p);
+
+		if (m_pll == 2) {
+			phy->pll_pre_p = 0;
+			phy->pll_fbd_s = 0;
+			phy->pll_fbd_div1f = 0;
+			phy->pll_fbd_div5f = 1;
+		} else if (m_pll >= 2 * 2 * r_pll && m_pll <= 2 * 4 * r_pll) {
+			phy->pll_pre_p = m_pll / (2 * r_pll);
+			phy->pll_fbd_s = 0;
+			phy->pll_fbd_div1f = 1;
+			phy->pll_fbd_div5f = 0;
+		} else if (m_pll >= 2 * 5 * r_pll && m_pll <= 2 * 150 * r_pll) {
+			if (((m_pll / (2 * r_pll)) % 2) == 0) {
+				phy->pll_pre_p =
+					(m_pll / (2 * r_pll)) / 2 - 1;
+				phy->pll_fbd_s =
+					(m_pll / (2 * r_pll)) % 2 + 2;
+			} else {
+				phy->pll_pre_p =
+					(m_pll / (2 * r_pll)) / 2;
+				phy->pll_fbd_s =
+					(m_pll / (2 * r_pll)) % 2;
+			}
+			phy->pll_fbd_div1f = 0;
+			phy->pll_fbd_div5f = 0;
+		} else {
+			phy->pll_pre_p = 0;
+			phy->pll_fbd_s = 0;
+			phy->pll_fbd_div1f = 0;
+			phy->pll_fbd_div5f = 1;
+		}
+
+		f_kHz = (u64)1000000000 * (u64)m_pll /
+			((u64)ref_clk_ps * (u64)n_pll * (u64)q_pll);
+
+		if (f_kHz >= req_kHz)
+			break;
+
+		tmp_kHz += 10;
+
+	} while (true);
+
+	return f_kHz;
+}
+
+static void dsi_get_phy_params(u32 phy_req_kHz,
+			       struct mipi_phy_params *phy)
+{
+	u32 ref_clk_ps = PHY_REF_CLK_PERIOD_PS;
+	u32 phy_rate_kHz;
+	u32 ui;
+
+	memset(phy, 0, sizeof(*phy));
+
+	phy_rate_kHz = dsi_calc_phy_rate(phy_req_kHz, phy);
+	if (!phy_rate_kHz)
+		return;
+
+	ui = 1000000 / phy_rate_kHz;
+
+	phy->clk_t_lpx = ROUND(50, 8 * ui);
+	phy->clk_t_hs_prepare = ROUND(133, 16 * ui) - 1;
+
+	phy->clk_t_hs_zero = ROUND(262, 8 * ui);
+	phy->clk_t_hs_trial = 2 * (ROUND(60, 8 * ui) - 1);
+	phy->clk_t_wakeup = ROUND(1000000, (ref_clk_ps / 1000) - 1);
+	if (phy->clk_t_wakeup > 0xff)
+		phy->clk_t_wakeup = 0xff;
+	phy->data_t_wakeup = phy->clk_t_wakeup;
+	phy->data_t_lpx = phy->clk_t_lpx;
+	phy->data_t_hs_prepare = ROUND(125 + 10 * ui, 16 * ui) - 1;
+	phy->data_t_hs_zero = ROUND(105 + 6 * ui, 8 * ui);
+	phy->data_t_hs_trial = 2 * (ROUND(60 + 4 * ui, 8 * ui) - 1);
+	phy->data_t_ta_go = 3;
+	phy->data_t_ta_get = 4;
+
+	phy->pll_enbwt = 1;
+	phy->clklp2hs_time = ROUND(407, 8 * ui) + 12;
+	phy->clkhs2lp_time = ROUND(105 + 12 * ui, 8 * ui);
+	phy->lp2hs_time = ROUND(240 + 12 * ui, 8 * ui) + 1;
+	phy->hs2lp_time = phy->clkhs2lp_time;
+	phy->clk_to_data_delay = 1 + phy->clklp2hs_time;
+	phy->data_to_clk_delay = ROUND(60 + 52 * ui, 8 * ui) +
+				phy->clkhs2lp_time;
+
+	phy->lane_byte_clk_kHz = phy_rate_kHz / 8;
+	phy->clk_division =
+		DIV_ROUND_UP(phy->lane_byte_clk_kHz, MAX_TX_ESC_CLK);
+}
+
+static u32 dsi_get_dpi_color_coding(enum mipi_dsi_pixel_format format)
+{
+	u32 val;
+
+	/*
+	 * TODO: only support RGB888 now, to support more
+	 */
+	switch (format) {
+	case MIPI_DSI_FMT_RGB888:
+		val = DSI_24BITS_1;
+		break;
+	default:
+		val = DSI_24BITS_1;
+		break;
+	}
+
+	return val;
+}
+
+/*
+ * dsi phy reg write function
+ */
+static void dsi_phy_tst_set(void __iomem *base, u32 reg, u32 val)
+{
+	u32 reg_write = 0x10000 + reg;
+
+	/*
+	 * latch reg first
+	 */
+	writel(reg_write, base + PHY_TST_CTRL1);
+	writel(0x02, base + PHY_TST_CTRL0);
+	writel(0x00, base + PHY_TST_CTRL0);
+
+	/*
+	 * then latch value
+	 */
+	writel(val, base + PHY_TST_CTRL1);
+	writel(0x02, base + PHY_TST_CTRL0);
+	writel(0x00, base + PHY_TST_CTRL0);
+}
+
+static void dsi_set_phy_timer(void __iomem *base,
+			      struct mipi_phy_params *phy,
+			      u32 lanes)
+{
+	u32 val;
+
+	/*
+	 * Set lane value and phy stop wait time.
+	 */
+	val = (lanes - 1) | (PHY_STOP_WAIT_TIME << 8);
+	writel(val, base + PHY_IF_CFG);
+
+	/*
+	 * Set phy clk division.
+	 */
+	val = readl(base + CLKMGR_CFG) | phy->clk_division;
+	writel(val, base + CLKMGR_CFG);
+
+	/*
+	 * Set lp and hs switching params.
+	 */
+	dw_update_bits(base + PHY_TMR_CFG, 24, MASK(8), phy->hs2lp_time);
+	dw_update_bits(base + PHY_TMR_CFG, 16, MASK(8), phy->lp2hs_time);
+	dw_update_bits(base + PHY_TMR_LPCLK_CFG, 16, MASK(10),
+		       phy->clkhs2lp_time);
+	dw_update_bits(base + PHY_TMR_LPCLK_CFG, 0, MASK(10),
+		       phy->clklp2hs_time);
+	dw_update_bits(base + CLK_DATA_TMR_CFG, 8, MASK(8),
+		       phy->data_to_clk_delay);
+	dw_update_bits(base + CLK_DATA_TMR_CFG, 0, MASK(8),
+		       phy->clk_to_data_delay);
+}
+
+static void dsi_set_mipi_phy(void __iomem *base,
+			     struct mipi_phy_params *phy,
+			     u32 lanes)
+{
+	u32 delay_count;
+	u32 val;
+	u32 i;
+
+	/* phy timer setting */
+	dsi_set_phy_timer(base, phy, lanes);
+
+	/*
+	 * Reset to clean up phy tst params.
+	 */
+	writel(0, base + PHY_RSTZ);
+	writel(0, base + PHY_TST_CTRL0);
+	writel(1, base + PHY_TST_CTRL0);
+	writel(0, base + PHY_TST_CTRL0);
+
+	/*
+	 * Clock lane timing control setting: TLPX, THS-PREPARE,
+	 * THS-ZERO, THS-TRAIL, TWAKEUP.
+	 */
+	dsi_phy_tst_set(base, CLK_TLPX, phy->clk_t_lpx);
+	dsi_phy_tst_set(base, CLK_THS_PREPARE, phy->clk_t_hs_prepare);
+	dsi_phy_tst_set(base, CLK_THS_ZERO, phy->clk_t_hs_zero);
+	dsi_phy_tst_set(base, CLK_THS_TRAIL, phy->clk_t_hs_trial);
+	dsi_phy_tst_set(base, CLK_TWAKEUP, phy->clk_t_wakeup);
+
+	/*
+	 * Data lane timing control setting: TLPX, THS-PREPARE,
+	 * THS-ZERO, THS-TRAIL, TTA-GO, TTA-GET, TWAKEUP.
+	 */
+	for (i = 0; i < lanes; i++) {
+		dsi_phy_tst_set(base, DATA_TLPX(i), phy->data_t_lpx);
+		dsi_phy_tst_set(base, DATA_THS_PREPARE(i),
+				phy->data_t_hs_prepare);
+		dsi_phy_tst_set(base, DATA_THS_ZERO(i), phy->data_t_hs_zero);
+		dsi_phy_tst_set(base, DATA_THS_TRAIL(i), phy->data_t_hs_trial);
+		dsi_phy_tst_set(base, DATA_TTA_GO(i), phy->data_t_ta_go);
+		dsi_phy_tst_set(base, DATA_TTA_GET(i), phy->data_t_ta_get);
+		dsi_phy_tst_set(base, DATA_TWAKEUP(i), phy->data_t_wakeup);
+	}
+
+	/*
+	 * physical configuration: I, pll I, pll II, pll III,
+	 * pll IV, pll V.
+	 */
+	dsi_phy_tst_set(base, PHY_CFG_I, phy->hstx_ckg_sel);
+	val = (phy->pll_fbd_div5f << 5) + (phy->pll_fbd_div1f << 4) +
+				(phy->pll_fbd_2p << 1) + phy->pll_enbwt;
+	dsi_phy_tst_set(base, PHY_CFG_PLL_I, val);
+	dsi_phy_tst_set(base, PHY_CFG_PLL_II, phy->pll_fbd_p);
+	dsi_phy_tst_set(base, PHY_CFG_PLL_III, phy->pll_fbd_s);
+	val = (phy->pll_pre_div1p << 7) + phy->pll_pre_p;
+	dsi_phy_tst_set(base, PHY_CFG_PLL_IV, val);
+	val = (5 << 5) + (phy->pll_vco_750M << 4) + (phy->pll_lpf_rs << 2) +
+		phy->pll_lpf_cs;
+	dsi_phy_tst_set(base, PHY_CFG_PLL_V, val);
+
+	writel(PHY_ENABLECLK, base + PHY_RSTZ);
+	udelay(1);
+	writel(PHY_ENABLECLK | PHY_UNSHUTDOWNZ, base + PHY_RSTZ);
+	udelay(1);
+	writel(PHY_ENABLECLK | PHY_UNRSTZ | PHY_UNSHUTDOWNZ, base + PHY_RSTZ);
+	usleep_range(1000, 1500);
+
+	/*
+	 * wait for phy's clock ready
+	 */
+	delay_count = 100;
+	while (delay_count--) {
+		val = readl(base +  PHY_STATUS);
+		if ((BIT(0) | BIT(2)) & val)
+			break;
+
+		udelay(1);
+	}
+
+	if (!delay_count)
+		DRM_INFO("phylock and phystopstateclklane is not ready.\n");
+}
+
+static void dsi_set_mode_timing(void __iomem *base,
+				u32 lane_byte_clk_kHz,
+				struct drm_display_mode *mode,
+				enum mipi_dsi_pixel_format format)
+{
+	u32 hfp, hbp, hsw, vfp, vbp, vsw;
+	u32 hline_time;
+	u32 hsa_time;
+	u32 hbp_time;
+	u32 pixel_clk_kHz;
+	int htot, vtot;
+	u32 val;
+	u64 tmp;
+
+	val = dsi_get_dpi_color_coding(format);
+	writel(val, base + DPI_COLOR_CODING);
+
+	val = (mode->flags & DRM_MODE_FLAG_NHSYNC ? 1 : 0) << 2;
+	val |= (mode->flags & DRM_MODE_FLAG_NVSYNC ? 1 : 0) << 1;
+	writel(val, base +  DPI_CFG_POL);
+
+	/*
+	 * The DSI IP accepts vertical timing using lines as normal,
+	 * but horizontal timing is a mixture of pixel-clocks for the
+	 * active region and byte-lane clocks for the blanking-related
+	 * timings.  hfp is specified as the total hline_time in byte-
+	 * lane clocks minus hsa, hbp and active.
+	 */
+	pixel_clk_kHz = mode->clock;
+	htot = mode->htotal;
+	vtot = mode->vtotal;
+	hfp = mode->hsync_start - mode->hdisplay;
+	hbp = mode->htotal - mode->hsync_end;
+	hsw = mode->hsync_end - mode->hsync_start;
+	vfp = mode->vsync_start - mode->vdisplay;
+	vbp = mode->vtotal - mode->vsync_end;
+	vsw = mode->vsync_end - mode->vsync_start;
+	if (vsw > 15) {
+		DRM_DEBUG_DRIVER("vsw exceeded 15\n");
+		vsw = 15;
+	}
+
+	hsa_time = (hsw * lane_byte_clk_kHz) / pixel_clk_kHz;
+	hbp_time = (hbp * lane_byte_clk_kHz) / pixel_clk_kHz;
+	tmp = (u64)htot * (u64)lane_byte_clk_kHz;
+	hline_time = DIV_ROUND_UP(tmp, pixel_clk_kHz);
+
+	/* all specified in byte-lane clocks */
+	writel(hsa_time, base + VID_HSA_TIME);
+	writel(hbp_time, base + VID_HBP_TIME);
+	writel(hline_time, base + VID_HLINE_TIME);
+
+	writel(vsw, base + VID_VSA_LINES);
+	writel(vbp, base + VID_VBP_LINES);
+	writel(vfp, base + VID_VFP_LINES);
+	writel(mode->vdisplay, base + VID_VACTIVE_LINES);
+	writel(mode->hdisplay, base + VID_PKT_SIZE);
+
+	DRM_DEBUG_DRIVER("htot=%d, hfp=%d, hbp=%d, hsw=%d\n",
+			 htot, hfp, hbp, hsw);
+	DRM_DEBUG_DRIVER("vtol=%d, vfp=%d, vbp=%d, vsw=%d\n",
+			 vtot, vfp, vbp, vsw);
+	DRM_DEBUG_DRIVER("hsa_time=%d, hbp_time=%d, hline_time=%d\n",
+			 hsa_time, hbp_time, hline_time);
+}
+
+static void dsi_set_video_mode(void __iomem *base, unsigned long flags)
+{
+	u32 val;
+	u32 mode_mask = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
+		MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
+	u32 non_burst_sync_pulse = MIPI_DSI_MODE_VIDEO |
+		MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
+	u32 non_burst_sync_event = MIPI_DSI_MODE_VIDEO;
+
+	/*
+	 * choose video mode type
+	 */
+	if ((flags & mode_mask) == non_burst_sync_pulse)
+		val = DSI_NON_BURST_SYNC_PULSES;
+	else if ((flags & mode_mask) == non_burst_sync_event)
+		val = DSI_NON_BURST_SYNC_EVENTS;
+	else
+		val = DSI_BURST_SYNC_PULSES_1;
+	writel(val, base + VID_MODE_CFG);
+
+	writel(PHY_TXREQUESTCLKHS, base + LPCLK_CTRL);
+	writel(DSI_VIDEO_MODE, base + MODE_CFG);
+}
+
+static void dsi_mipi_init(struct dw_dsi *dsi)
+{
+	struct dsi_hw_ctx *ctx = dsi->ctx;
+	struct mipi_phy_params *phy = &dsi->phy;
+	struct drm_display_mode *mode = &dsi->cur_mode;
+	u32 bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
+	void __iomem *base = ctx->base;
+	u32 dphy_req_kHz;
+
+	/*
+	 * count phy params
+	 */
+	dphy_req_kHz = mode->clock * bpp / dsi->lanes;
+	dsi_get_phy_params(dphy_req_kHz, phy);
+
+	/* reset Core */
+	writel(RESET, base + PWR_UP);
+
+	/* set dsi phy params */
+	dsi_set_mipi_phy(base, phy, dsi->lanes);
+
+	/* set dsi mode timing */
+	dsi_set_mode_timing(base, phy->lane_byte_clk_kHz, mode, dsi->format);
+
+	/* set dsi video mode */
+	dsi_set_video_mode(base, dsi->mode_flags);
+
+	/* dsi wake up */
+	writel(POWERUP, base + PWR_UP);
+
+	DRM_DEBUG_DRIVER("lanes=%d, pixel_clk=%d kHz, bytes_freq=%d kHz\n",
+			 dsi->lanes, mode->clock, phy->lane_byte_clk_kHz);
+}
+
+static void dsi_encoder_disable(struct drm_encoder *encoder)
+{
+	struct dw_dsi *dsi = encoder_to_dsi(encoder);
+	struct dsi_hw_ctx *ctx = dsi->ctx;
+	void __iomem *base = ctx->base;
+
+	if (!dsi->enable)
+		return;
+
+	writel(0, base + PWR_UP);
+	writel(0, base + LPCLK_CTRL);
+	writel(0, base + PHY_RSTZ);
+	clk_disable_unprepare(ctx->pclk);
+
+	dsi->enable = false;
+}
+
+static void dsi_encoder_enable(struct drm_encoder *encoder)
+{
+	struct dw_dsi *dsi = encoder_to_dsi(encoder);
+	struct dsi_hw_ctx *ctx = dsi->ctx;
+	int ret;
+
+	if (dsi->enable)
+		return;
+
+	ret = clk_prepare_enable(ctx->pclk);
+	if (ret) {
+		DRM_ERROR("fail to enable pclk: %d\n", ret);
+		return;
+	}
+
+	dsi_mipi_init(dsi);
+
+	dsi->enable = true;
+}
+
+static void dsi_encoder_mode_set(struct drm_encoder *encoder,
+				 struct drm_display_mode *mode,
+				 struct drm_display_mode *adj_mode)
+{
+	struct dw_dsi *dsi = encoder_to_dsi(encoder);
+
+	drm_mode_copy(&dsi->cur_mode, adj_mode);
+}
+
+static int dsi_encoder_atomic_check(struct drm_encoder *encoder,
+				    struct drm_crtc_state *crtc_state,
+				    struct drm_connector_state *conn_state)
+{
+	/* do nothing */
+	return 0;
+}
+
+static const struct drm_encoder_helper_funcs dw_encoder_helper_funcs = {
+	.atomic_check	= dsi_encoder_atomic_check,
+	.mode_set	= dsi_encoder_mode_set,
+	.enable		= dsi_encoder_enable,
+	.disable	= dsi_encoder_disable
+};
+
+static const struct drm_encoder_funcs dw_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+static int dw_drm_encoder_init(struct device *dev,
+			       struct drm_device *drm_dev,
+			       struct drm_encoder *encoder)
+{
+	int ret;
+	u32 crtc_mask = drm_of_find_possible_crtcs(drm_dev, dev->of_node);
+
+	if (!crtc_mask) {
+		DRM_ERROR("failed to find crtc mask\n");
+		return -EINVAL;
+	}
+
+	encoder->possible_crtcs = crtc_mask;
+	ret = drm_encoder_init(drm_dev, encoder, &dw_encoder_funcs,
+			       DRM_MODE_ENCODER_DSI, NULL);
+	if (ret) {
+		DRM_ERROR("failed to init dsi encoder\n");
+		return ret;
+	}
+
+	drm_encoder_helper_add(encoder, &dw_encoder_helper_funcs);
+
+	return 0;
+}
+
+static int dsi_host_attach(struct mipi_dsi_host *host,
+			   struct mipi_dsi_device *mdsi)
+{
+	struct dw_dsi *dsi = host_to_dsi(host);
+
+	if (mdsi->lanes < 1 || mdsi->lanes > 4) {
+		DRM_ERROR("dsi device params invalid\n");
+		return -EINVAL;
+	}
+
+	dsi->lanes = mdsi->lanes;
+	dsi->format = mdsi->format;
+	dsi->mode_flags = mdsi->mode_flags;
+
+	return 0;
+}
+
+static int dsi_host_detach(struct mipi_dsi_host *host,
+			   struct mipi_dsi_device *mdsi)
+{
+	/* do nothing */
+	return 0;
+}
+
+static const struct mipi_dsi_host_ops dsi_host_ops = {
+	.attach = dsi_host_attach,
+	.detach = dsi_host_detach,
+};
+
+static int dsi_host_init(struct device *dev, struct dw_dsi *dsi)
+{
+	struct mipi_dsi_host *host = &dsi->host;
+	int ret;
+
+	host->dev = dev;
+	host->ops = &dsi_host_ops;
+	ret = mipi_dsi_host_register(host);
+	if (ret) {
+		DRM_ERROR("failed to register dsi host\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int dsi_bridge_init(struct drm_device *dev, struct dw_dsi *dsi)
+{
+	struct drm_encoder *encoder = &dsi->encoder;
+	struct drm_bridge *bridge = dsi->bridge;
+	int ret;
+
+	/* associate the bridge to dsi encoder */
+	encoder->bridge = bridge;
+	bridge->encoder = encoder;
+
+	ret = drm_bridge_attach(dev, bridge);
+	if (ret) {
+		DRM_ERROR("failed to attach external bridge\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int dsi_bind(struct device *dev, struct device *master, void *data)
+{
+	struct dsi_data *ddata = dev_get_drvdata(dev);
+	struct dw_dsi *dsi = &ddata->dsi;
+	struct drm_device *drm_dev = data;
+	int ret;
+
+	ret = dw_drm_encoder_init(dev, drm_dev, &dsi->encoder);
+	if (ret)
+		return ret;
+
+	ret = dsi_host_init(dev, dsi);
+	if (ret)
+		return ret;
+
+	ret = dsi_bridge_init(drm_dev, dsi);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static void dsi_unbind(struct device *dev, struct device *master, void *data)
+{
+	/* do nothing */
+}
+
+static const struct component_ops dsi_ops = {
+	.bind	= dsi_bind,
+	.unbind	= dsi_unbind,
+};
+
+static int dsi_parse_dt(struct platform_device *pdev, struct dw_dsi *dsi)
+{
+	struct dsi_hw_ctx *ctx = dsi->ctx;
+	struct device_node *np = pdev->dev.of_node;
+	struct device_node *endpoint, *bridge_node;
+	struct drm_bridge *bridge;
+	struct resource *res;
+
+	/*
+	 * Get the endpoint node. In our case, dsi has one output port1
+	 * to which the external HDMI bridge is connected.
+	 */
+	endpoint = of_graph_get_endpoint_by_regs(np, 1, -1);
+	if (!endpoint) {
+		DRM_ERROR("no valid endpoint node\n");
+		return -ENODEV;
+	}
+	of_node_put(endpoint);
+
+	bridge_node = of_graph_get_remote_port_parent(endpoint);
+	if (!bridge_node) {
+		DRM_ERROR("no valid bridge node\n");
+		return -ENODEV;
+	}
+	of_node_put(bridge_node);
+
+	bridge = of_drm_find_bridge(bridge_node);
+	if (!bridge) {
+		DRM_INFO("wait for external HDMI bridge driver.\n");
+		return -EPROBE_DEFER;
+	}
+	dsi->bridge = bridge;
+
+	ctx->pclk = devm_clk_get(&pdev->dev, "pclk");
+	if (IS_ERR(ctx->pclk)) {
+		DRM_ERROR("failed to get pclk clock\n");
+		return PTR_ERR(ctx->pclk);
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	ctx->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(ctx->base)) {
+		DRM_ERROR("failed to remap dsi io region\n");
+		return PTR_ERR(ctx->base);
+	}
+
+	return 0;
+}
+
+static int dsi_probe(struct platform_device *pdev)
+{
+	struct dsi_data *data;
+	struct dw_dsi *dsi;
+	struct dsi_hw_ctx *ctx;
+	int ret;
+
+	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+	if (!data) {
+		DRM_ERROR("failed to allocate dsi data.\n");
+		return -ENOMEM;
+	}
+	dsi = &data->dsi;
+	ctx = &data->ctx;
+	dsi->ctx = ctx;
+
+	ret = dsi_parse_dt(pdev, dsi);
+	if (ret)
+		return ret;
+
+	platform_set_drvdata(pdev, data);
+
+	return component_add(&pdev->dev, &dsi_ops);
+}
+
+static int dsi_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &dsi_ops);
+
+	return 0;
+}
+
+static const struct of_device_id dsi_of_match[] = {
+	{.compatible = "hisilicon,hi6220-dsi"},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, dsi_of_match);
+
+static struct platform_driver dsi_driver = {
+	.probe = dsi_probe,
+	.remove = dsi_remove,
+	.driver = {
+		.name = "dw-dsi",
+		.of_match_table = dsi_of_match,
+	},
+};
+
+module_platform_driver(dsi_driver);
+
+MODULE_AUTHOR("Xinliang Liu <xinliang.liu@linaro.org>");
+MODULE_AUTHOR("Xinliang Liu <z.liuxinliang@hisilicon.com>");
+MODULE_AUTHOR("Xinwei Kong <kong.kongxinwei@hisilicon.com>");
+MODULE_DESCRIPTION("DesignWare MIPI DSI Host Controller v1.02 driver");
+MODULE_LICENSE("GPL v2");

+ 103 - 0
drivers/gpu/drm/hisilicon/kirin/dw_dsi_reg.h

@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2016 Linaro Limited.
+ * Copyright (c) 2014-2016 Hisilicon Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __DW_DSI_REG_H__
+#define __DW_DSI_REG_H__
+
+#define MASK(x)				(BIT(x) - 1)
+
+/*
+ * regs
+ */
+#define PWR_UP                  0x04  /* Core power-up */
+#define RESET                   0
+#define POWERUP                 BIT(0)
+#define PHY_IF_CFG              0xA4  /* D-PHY interface configuration */
+#define CLKMGR_CFG              0x08  /* the internal clock dividers */
+#define PHY_RSTZ                0xA0  /* D-PHY reset control */
+#define PHY_ENABLECLK           BIT(2)
+#define PHY_UNRSTZ              BIT(1)
+#define PHY_UNSHUTDOWNZ         BIT(0)
+#define PHY_TST_CTRL0           0xB4  /* D-PHY test interface control 0 */
+#define PHY_TST_CTRL1           0xB8  /* D-PHY test interface control 1 */
+#define CLK_TLPX                0x10
+#define CLK_THS_PREPARE         0x11
+#define CLK_THS_ZERO            0x12
+#define CLK_THS_TRAIL           0x13
+#define CLK_TWAKEUP             0x14
+#define DATA_TLPX(x)            (0x20 + ((x) << 4))
+#define DATA_THS_PREPARE(x)     (0x21 + ((x) << 4))
+#define DATA_THS_ZERO(x)        (0x22 + ((x) << 4))
+#define DATA_THS_TRAIL(x)       (0x23 + ((x) << 4))
+#define DATA_TTA_GO(x)          (0x24 + ((x) << 4))
+#define DATA_TTA_GET(x)         (0x25 + ((x) << 4))
+#define DATA_TWAKEUP(x)         (0x26 + ((x) << 4))
+#define PHY_CFG_I               0x60
+#define PHY_CFG_PLL_I           0x63
+#define PHY_CFG_PLL_II          0x64
+#define PHY_CFG_PLL_III         0x65
+#define PHY_CFG_PLL_IV          0x66
+#define PHY_CFG_PLL_V           0x67
+#define DPI_COLOR_CODING        0x10  /* DPI color coding */
+#define DPI_CFG_POL             0x14  /* DPI polarity configuration */
+#define VID_HSA_TIME            0x48  /* Horizontal Sync Active time */
+#define VID_HBP_TIME            0x4C  /* Horizontal Back Porch time */
+#define VID_HLINE_TIME          0x50  /* Line time */
+#define VID_VSA_LINES           0x54  /* Vertical Sync Active period */
+#define VID_VBP_LINES           0x58  /* Vertical Back Porch period */
+#define VID_VFP_LINES           0x5C  /* Vertical Front Porch period */
+#define VID_VACTIVE_LINES       0x60  /* Vertical resolution */
+#define VID_PKT_SIZE            0x3C  /* Video packet size */
+#define VID_MODE_CFG            0x38  /* Video mode configuration */
+#define PHY_TMR_CFG             0x9C  /* Data lanes timing configuration */
+#define BTA_TO_CNT              0x8C  /* Response timeout definition */
+#define PHY_TMR_LPCLK_CFG       0x98  /* clock lane timing configuration */
+#define CLK_DATA_TMR_CFG        0xCC
+#define LPCLK_CTRL              0x94  /* Low-power in clock lane */
+#define PHY_TXREQUESTCLKHS      BIT(0)
+#define MODE_CFG                0x34  /* Video or Command mode selection */
+#define PHY_STATUS              0xB0  /* D-PHY PPI status interface */
+
+#define	PHY_STOP_WAIT_TIME      0x30
+
+/*
+ * regs relevant enum
+ */
+enum dpi_color_coding {
+	DSI_24BITS_1 = 5,
+};
+
+enum dsi_video_mode_type {
+	DSI_NON_BURST_SYNC_PULSES = 0,
+	DSI_NON_BURST_SYNC_EVENTS,
+	DSI_BURST_SYNC_PULSES_1,
+	DSI_BURST_SYNC_PULSES_2
+};
+
+enum dsi_work_mode {
+	DSI_VIDEO_MODE = 0,
+	DSI_COMMAND_MODE
+};
+
+/*
+ * Register Write/Read Helper functions
+ */
+static inline void dw_update_bits(void __iomem *addr, u32 bit_start,
+				  u32 mask, u32 val)
+{
+	u32 tmp, orig;
+
+	orig = readl(addr);
+	tmp = orig & ~(mask << bit_start);
+	tmp |= (val & mask) << bit_start;
+	writel(tmp, addr);
+}
+
+#endif /* __DW_DRM_DSI_H__ */

+ 230 - 0
drivers/gpu/drm/hisilicon/kirin/kirin_ade_reg.h

@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2016 Linaro Limited.
+ * Copyright (c) 2014-2016 Hisilicon Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __KIRIN_ADE_REG_H__
+#define __KIRIN_ADE_REG_H__
+
+/*
+ * ADE Registers
+ */
+#define MASK(x)				(BIT(x) - 1)
+
+#define ADE_CTRL			0x0004
+#define FRM_END_START_OFST		0
+#define FRM_END_START_MASK		MASK(2)
+#define AUTO_CLK_GATE_EN_OFST		0
+#define AUTO_CLK_GATE_EN		BIT(0)
+#define ADE_DISP_SRC_CFG		0x0018
+#define ADE_CTRL1			0x008C
+#define ADE_EN				0x0100
+#define ADE_DISABLE			0
+#define ADE_ENABLE			1
+/* reset and reload regs */
+#define ADE_SOFT_RST_SEL(x)		(0x0078 + (x) * 0x4)
+#define ADE_RELOAD_DIS(x)		(0x00AC + (x) * 0x4)
+#define RDMA_OFST			0
+#define CLIP_OFST			15
+#define SCL_OFST			21
+#define CTRAN_OFST			24
+#define OVLY_OFST			37 /* 32+5 */
+/* channel regs */
+#define RD_CH_CTRL(x)			(0x1004 + (x) * 0x80)
+#define RD_CH_ADDR(x)			(0x1008 + (x) * 0x80)
+#define RD_CH_SIZE(x)			(0x100C + (x) * 0x80)
+#define RD_CH_STRIDE(x)			(0x1010 + (x) * 0x80)
+#define RD_CH_SPACE(x)			(0x1014 + (x) * 0x80)
+#define RD_CH_EN(x)			(0x1020 + (x) * 0x80)
+/* overlay regs */
+#define ADE_OVLY1_TRANS_CFG		0x002C
+#define ADE_OVLY_CTL			0x0098
+#define ADE_OVLY_CH_XY0(x)		(0x2004 + (x) * 4)
+#define ADE_OVLY_CH_XY1(x)		(0x2024 + (x) * 4)
+#define ADE_OVLY_CH_CTL(x)		(0x204C + (x) * 4)
+#define ADE_OVLY_OUTPUT_SIZE(x)		(0x2070 + (x) * 8)
+#define OUTPUT_XSIZE_OFST		16
+#define ADE_OVLYX_CTL(x)		(0x209C + (x) * 4)
+#define CH_OVLY_SEL_OFST(x)		((x) * 4)
+#define CH_OVLY_SEL_MASK		MASK(2)
+#define CH_OVLY_SEL_VAL(x)		((x) + 1)
+#define CH_ALP_MODE_OFST		0
+#define CH_ALP_SEL_OFST			2
+#define CH_UNDER_ALP_SEL_OFST		4
+#define CH_EN_OFST			6
+#define CH_ALP_GBL_OFST			15
+#define CH_SEL_OFST			28
+/* ctran regs */
+#define ADE_CTRAN_DIS(x)		(0x5004 + (x) * 0x100)
+#define CTRAN_BYPASS_ON			1
+#define CTRAN_BYPASS_OFF		0
+#define ADE_CTRAN_IMAGE_SIZE(x)		(0x503C + (x) * 0x100)
+/* clip regs */
+#define ADE_CLIP_DISABLE(x)		(0x6800 + (x) * 0x100)
+#define ADE_CLIP_SIZE0(x)		(0x6804 + (x) * 0x100)
+#define ADE_CLIP_SIZE1(x)		(0x6808 + (x) * 0x100)
+
+/*
+ * LDI Registers
+ */
+#define LDI_HRZ_CTRL0			0x7400
+#define HBP_OFST			20
+#define LDI_HRZ_CTRL1			0x7404
+#define LDI_VRT_CTRL0			0x7408
+#define VBP_OFST			20
+#define LDI_VRT_CTRL1			0x740C
+#define LDI_PLR_CTRL			0x7410
+#define FLAG_NVSYNC			BIT(0)
+#define FLAG_NHSYNC			BIT(1)
+#define FLAG_NPIXCLK			BIT(2)
+#define FLAG_NDE			BIT(3)
+#define LDI_DSP_SIZE			0x7414
+#define VSIZE_OFST			20
+#define LDI_INT_EN			0x741C
+#define FRAME_END_INT_EN_OFST		1
+#define LDI_CTRL			0x7420
+#define BPP_OFST			3
+#define DATA_GATE_EN			BIT(2)
+#define LDI_EN				BIT(0)
+#define LDI_MSK_INT			0x7428
+#define LDI_INT_CLR			0x742C
+#define LDI_WORK_MODE			0x7430
+#define LDI_HDMI_DSI_GT			0x7434
+
+/*
+ * ADE media bus service regs
+ */
+#define ADE0_QOSGENERATOR_MODE		0x010C
+#define QOSGENERATOR_MODE_MASK		MASK(2)
+#define ADE0_QOSGENERATOR_EXTCONTROL	0x0118
+#define SOCKET_QOS_EN			BIT(0)
+#define ADE1_QOSGENERATOR_MODE		0x020C
+#define ADE1_QOSGENERATOR_EXTCONTROL	0x0218
+
+/*
+ * ADE regs relevant enums
+ */
+enum frame_end_start {
+	/* regs take effect in every vsync */
+	REG_EFFECTIVE_IN_VSYNC = 0,
+	/* regs take effect in fist ade en and every frame end */
+	REG_EFFECTIVE_IN_ADEEN_FRMEND,
+	/* regs take effect in ade en immediately */
+	REG_EFFECTIVE_IN_ADEEN,
+	/* regs take effect in first vsync and every frame end */
+	REG_EFFECTIVE_IN_VSYNC_FRMEND
+};
+
+enum ade_fb_format {
+	ADE_RGB_565 = 0,
+	ADE_BGR_565,
+	ADE_XRGB_8888,
+	ADE_XBGR_8888,
+	ADE_ARGB_8888,
+	ADE_ABGR_8888,
+	ADE_RGBA_8888,
+	ADE_BGRA_8888,
+	ADE_RGB_888,
+	ADE_BGR_888 = 9,
+	ADE_FORMAT_UNSUPPORT = 800
+};
+
+enum ade_channel {
+	ADE_CH1 = 0,	/* channel 1 for primary plane */
+	ADE_CH_NUM
+};
+
+enum ade_scale {
+	ADE_SCL1 = 0,
+	ADE_SCL2,
+	ADE_SCL3,
+	ADE_SCL_NUM
+};
+
+enum ade_ctran {
+	ADE_CTRAN1 = 0,
+	ADE_CTRAN2,
+	ADE_CTRAN3,
+	ADE_CTRAN4,
+	ADE_CTRAN5,
+	ADE_CTRAN6,
+	ADE_CTRAN_NUM
+};
+
+enum ade_overlay {
+	ADE_OVLY1 = 0,
+	ADE_OVLY2,
+	ADE_OVLY3,
+	ADE_OVLY_NUM
+};
+
+enum ade_alpha_mode {
+	ADE_ALP_GLOBAL = 0,
+	ADE_ALP_PIXEL,
+	ADE_ALP_PIXEL_AND_GLB
+};
+
+enum ade_alpha_blending_mode {
+	ADE_ALP_MUL_COEFF_0 = 0,	/* alpha */
+	ADE_ALP_MUL_COEFF_1,		/* 1-alpha */
+	ADE_ALP_MUL_COEFF_2,		/* 0 */
+	ADE_ALP_MUL_COEFF_3		/* 1 */
+};
+
+/*
+ * LDI regs relevant enums
+ */
+enum dsi_pclk_en {
+	DSI_PCLK_ON = 0,
+	DSI_PCLK_OFF
+};
+
+enum ldi_output_format {
+	LDI_OUT_RGB_565 = 0,
+	LDI_OUT_RGB_666,
+	LDI_OUT_RGB_888
+};
+
+enum ldi_work_mode {
+	TEST_MODE = 0,
+	NORMAL_MODE
+};
+
+enum ldi_input_source {
+	DISP_SRC_NONE = 0,
+	DISP_SRC_OVLY2,
+	DISP_SRC_DISP,
+	DISP_SRC_ROT,
+	DISP_SRC_SCL2
+};
+
+/*
+ * ADE media bus service relevant enums
+ */
+enum qos_generator_mode {
+	FIXED_MODE = 0,
+	LIMITER_MODE,
+	BYPASS_MODE,
+	REGULATOR_MODE
+};
+
+/*
+ * Register Write/Read Helper functions
+ */
+static inline void ade_update_bits(void __iomem *addr, u32 bit_start,
+				   u32 mask, u32 val)
+{
+	u32 tmp, orig;
+
+	orig = readl(addr);
+	tmp = orig & ~(mask << bit_start);
+	tmp |= (val & mask) << bit_start;
+	writel(tmp, addr);
+}
+
+#endif

+ 1057 - 0
drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c

@@ -0,0 +1,1057 @@
+/*
+ * Hisilicon Hi6220 SoC ADE(Advanced Display Engine)'s crtc&plane driver
+ *
+ * Copyright (c) 2016 Linaro Limited.
+ * Copyright (c) 2014-2016 Hisilicon Limited.
+ *
+ * Author:
+ *	Xinliang Liu <z.liuxinliang@hisilicon.com>
+ *	Xinliang Liu <xinliang.liu@linaro.org>
+ *	Xinwei Kong <kong.kongxinwei@hisilicon.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <video/display_timing.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+
+#include "kirin_drm_drv.h"
+#include "kirin_ade_reg.h"
+
+#define PRIMARY_CH	ADE_CH1 /* primary plane */
+#define OUT_OVLY	ADE_OVLY2 /* output overlay compositor */
+#define ADE_DEBUG	1
+
+#define to_ade_crtc(crtc) \
+	container_of(crtc, struct ade_crtc, base)
+
+#define to_ade_plane(plane) \
+	container_of(plane, struct ade_plane, base)
+
+struct ade_hw_ctx {
+	void __iomem  *base;
+	struct regmap *noc_regmap;
+	struct clk *ade_core_clk;
+	struct clk *media_noc_clk;
+	struct clk *ade_pix_clk;
+	struct reset_control *reset;
+	bool power_on;
+	int irq;
+};
+
+struct ade_crtc {
+	struct drm_crtc base;
+	struct ade_hw_ctx *ctx;
+	bool enable;
+	u32 out_format;
+};
+
+struct ade_plane {
+	struct drm_plane base;
+	void *ctx;
+	u8 ch; /* channel */
+};
+
+struct ade_data {
+	struct ade_crtc acrtc;
+	struct ade_plane aplane[ADE_CH_NUM];
+	struct ade_hw_ctx ctx;
+};
+
+/* ade-format info: */
+struct ade_format {
+	u32 pixel_format;
+	enum ade_fb_format ade_format;
+};
+
+static const struct ade_format ade_formats[] = {
+	/* 16bpp RGB: */
+	{ DRM_FORMAT_RGB565, ADE_RGB_565 },
+	{ DRM_FORMAT_BGR565, ADE_BGR_565 },
+	/* 24bpp RGB: */
+	{ DRM_FORMAT_RGB888, ADE_RGB_888 },
+	{ DRM_FORMAT_BGR888, ADE_BGR_888 },
+	/* 32bpp [A]RGB: */
+	{ DRM_FORMAT_XRGB8888, ADE_XRGB_8888 },
+	{ DRM_FORMAT_XBGR8888, ADE_XBGR_8888 },
+	{ DRM_FORMAT_RGBA8888, ADE_RGBA_8888 },
+	{ DRM_FORMAT_BGRA8888, ADE_BGRA_8888 },
+	{ DRM_FORMAT_ARGB8888, ADE_ARGB_8888 },
+	{ DRM_FORMAT_ABGR8888, ADE_ABGR_8888 },
+};
+
+static const u32 channel_formats1[] = {
+	/* channel 1,2,3,4 */
+	DRM_FORMAT_RGB565, DRM_FORMAT_BGR565, DRM_FORMAT_RGB888,
+	DRM_FORMAT_BGR888, DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888, DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_ABGR8888
+};
+
+u32 ade_get_channel_formats(u8 ch, const u32 **formats)
+{
+	switch (ch) {
+	case ADE_CH1:
+		*formats = channel_formats1;
+		return ARRAY_SIZE(channel_formats1);
+	default:
+		DRM_ERROR("no this channel %d\n", ch);
+		*formats = NULL;
+		return 0;
+	}
+}
+
+/* convert from fourcc format to ade format */
+static u32 ade_get_format(u32 pixel_format)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ade_formats); i++)
+		if (ade_formats[i].pixel_format == pixel_format)
+			return ade_formats[i].ade_format;
+
+	/* not found */
+	DRM_ERROR("Not found pixel format!!fourcc_format= %d\n",
+		  pixel_format);
+	return ADE_FORMAT_UNSUPPORT;
+}
+
+static void ade_update_reload_bit(void __iomem *base, u32 bit_num, u32 val)
+{
+	u32 bit_ofst, reg_num;
+
+	bit_ofst = bit_num % 32;
+	reg_num = bit_num / 32;
+
+	ade_update_bits(base + ADE_RELOAD_DIS(reg_num), bit_ofst,
+			MASK(1), !!val);
+}
+
+static u32 ade_read_reload_bit(void __iomem *base, u32 bit_num)
+{
+	u32 tmp, bit_ofst, reg_num;
+
+	bit_ofst = bit_num % 32;
+	reg_num = bit_num / 32;
+
+	tmp = readl(base + ADE_RELOAD_DIS(reg_num));
+	return !!(BIT(bit_ofst) & tmp);
+}
+
+static void ade_init(struct ade_hw_ctx *ctx)
+{
+	void __iomem *base = ctx->base;
+
+	/* enable clk gate */
+	ade_update_bits(base + ADE_CTRL1, AUTO_CLK_GATE_EN_OFST,
+			AUTO_CLK_GATE_EN, ADE_ENABLE);
+	/* clear overlay */
+	writel(0, base + ADE_OVLY1_TRANS_CFG);
+	writel(0, base + ADE_OVLY_CTL);
+	writel(0, base + ADE_OVLYX_CTL(OUT_OVLY));
+	/* clear reset and reload regs */
+	writel(MASK(32), base + ADE_SOFT_RST_SEL(0));
+	writel(MASK(32), base + ADE_SOFT_RST_SEL(1));
+	writel(MASK(32), base + ADE_RELOAD_DIS(0));
+	writel(MASK(32), base + ADE_RELOAD_DIS(1));
+	/*
+	 * for video mode, all the ade registers should
+	 * become effective at frame end.
+	 */
+	ade_update_bits(base + ADE_CTRL, FRM_END_START_OFST,
+			FRM_END_START_MASK, REG_EFFECTIVE_IN_ADEEN_FRMEND);
+}
+
+static void ade_set_pix_clk(struct ade_hw_ctx *ctx,
+			    struct drm_display_mode *mode,
+			    struct drm_display_mode *adj_mode)
+{
+	u32 clk_Hz = mode->clock * 1000;
+	int ret;
+
+	/*
+	 * Success should be guaranteed in mode_valid call back,
+	 * so failure shouldn't happen here
+	 */
+	ret = clk_set_rate(ctx->ade_pix_clk, clk_Hz);
+	if (ret)
+		DRM_ERROR("failed to set pixel clk %dHz (%d)\n", clk_Hz, ret);
+	adj_mode->clock = clk_get_rate(ctx->ade_pix_clk) / 1000;
+}
+
+static void ade_ldi_set_mode(struct ade_crtc *acrtc,
+			     struct drm_display_mode *mode,
+			     struct drm_display_mode *adj_mode)
+{
+	struct ade_hw_ctx *ctx = acrtc->ctx;
+	void __iomem *base = ctx->base;
+	u32 width = mode->hdisplay;
+	u32 height = mode->vdisplay;
+	u32 hfp, hbp, hsw, vfp, vbp, vsw;
+	u32 plr_flags;
+
+	plr_flags = (mode->flags & DRM_MODE_FLAG_NVSYNC) ? FLAG_NVSYNC : 0;
+	plr_flags |= (mode->flags & DRM_MODE_FLAG_NHSYNC) ? FLAG_NHSYNC : 0;
+	hfp = mode->hsync_start - mode->hdisplay;
+	hbp = mode->htotal - mode->hsync_end;
+	hsw = mode->hsync_end - mode->hsync_start;
+	vfp = mode->vsync_start - mode->vdisplay;
+	vbp = mode->vtotal - mode->vsync_end;
+	vsw = mode->vsync_end - mode->vsync_start;
+	if (vsw > 15) {
+		DRM_DEBUG_DRIVER("vsw exceeded 15\n");
+		vsw = 15;
+	}
+
+	writel((hbp << HBP_OFST) | hfp, base + LDI_HRZ_CTRL0);
+	 /* the configured value is actual value - 1 */
+	writel(hsw - 1, base + LDI_HRZ_CTRL1);
+	writel((vbp << VBP_OFST) | vfp, base + LDI_VRT_CTRL0);
+	 /* the configured value is actual value - 1 */
+	writel(vsw - 1, base + LDI_VRT_CTRL1);
+	 /* the configured value is actual value - 1 */
+	writel(((height - 1) << VSIZE_OFST) | (width - 1),
+	       base + LDI_DSP_SIZE);
+	writel(plr_flags, base + LDI_PLR_CTRL);
+
+	/* set overlay compositor output size */
+	writel(((width - 1) << OUTPUT_XSIZE_OFST) | (height - 1),
+	       base + ADE_OVLY_OUTPUT_SIZE(OUT_OVLY));
+
+	/* ctran6 setting */
+	writel(CTRAN_BYPASS_ON, base + ADE_CTRAN_DIS(ADE_CTRAN6));
+	 /* the configured value is actual value - 1 */
+	writel(width * height - 1, base + ADE_CTRAN_IMAGE_SIZE(ADE_CTRAN6));
+	ade_update_reload_bit(base, CTRAN_OFST + ADE_CTRAN6, 0);
+
+	ade_set_pix_clk(ctx, mode, adj_mode);
+
+	DRM_DEBUG_DRIVER("set mode: %dx%d\n", width, height);
+}
+
+static int ade_power_up(struct ade_hw_ctx *ctx)
+{
+	int ret;
+
+	ret = clk_prepare_enable(ctx->media_noc_clk);
+	if (ret) {
+		DRM_ERROR("failed to enable media_noc_clk (%d)\n", ret);
+		return ret;
+	}
+
+	ret = reset_control_deassert(ctx->reset);
+	if (ret) {
+		DRM_ERROR("failed to deassert reset\n");
+		return ret;
+	}
+
+	ret = clk_prepare_enable(ctx->ade_core_clk);
+	if (ret) {
+		DRM_ERROR("failed to enable ade_core_clk (%d)\n", ret);
+		return ret;
+	}
+
+	ade_init(ctx);
+	ctx->power_on = true;
+	return 0;
+}
+
+static void ade_power_down(struct ade_hw_ctx *ctx)
+{
+	void __iomem *base = ctx->base;
+
+	writel(ADE_DISABLE, base + LDI_CTRL);
+	/* dsi pixel off */
+	writel(DSI_PCLK_OFF, base + LDI_HDMI_DSI_GT);
+
+	clk_disable_unprepare(ctx->ade_core_clk);
+	reset_control_assert(ctx->reset);
+	clk_disable_unprepare(ctx->media_noc_clk);
+	ctx->power_on = false;
+}
+
+static void ade_set_medianoc_qos(struct ade_crtc *acrtc)
+{
+	struct ade_hw_ctx *ctx = acrtc->ctx;
+	struct regmap *map = ctx->noc_regmap;
+
+	regmap_update_bits(map, ADE0_QOSGENERATOR_MODE,
+			   QOSGENERATOR_MODE_MASK, BYPASS_MODE);
+	regmap_update_bits(map, ADE0_QOSGENERATOR_EXTCONTROL,
+			   SOCKET_QOS_EN, SOCKET_QOS_EN);
+
+	regmap_update_bits(map, ADE1_QOSGENERATOR_MODE,
+			   QOSGENERATOR_MODE_MASK, BYPASS_MODE);
+	regmap_update_bits(map, ADE1_QOSGENERATOR_EXTCONTROL,
+			   SOCKET_QOS_EN, SOCKET_QOS_EN);
+}
+
+static int ade_enable_vblank(struct drm_device *dev, unsigned int pipe)
+{
+	struct kirin_drm_private *priv = dev->dev_private;
+	struct ade_crtc *acrtc = to_ade_crtc(priv->crtc[pipe]);
+	struct ade_hw_ctx *ctx = acrtc->ctx;
+	void __iomem *base = ctx->base;
+
+	if (!ctx->power_on)
+		(void)ade_power_up(ctx);
+
+	ade_update_bits(base + LDI_INT_EN, FRAME_END_INT_EN_OFST,
+			MASK(1), 1);
+
+	return 0;
+}
+
+static void ade_disable_vblank(struct drm_device *dev, unsigned int pipe)
+{
+	struct kirin_drm_private *priv = dev->dev_private;
+	struct ade_crtc *acrtc = to_ade_crtc(priv->crtc[pipe]);
+	struct ade_hw_ctx *ctx = acrtc->ctx;
+	void __iomem *base = ctx->base;
+
+	if (!ctx->power_on) {
+		DRM_ERROR("power is down! vblank disable fail\n");
+		return;
+	}
+
+	ade_update_bits(base + LDI_INT_EN, FRAME_END_INT_EN_OFST,
+			MASK(1), 0);
+}
+
+static irqreturn_t ade_irq_handler(int irq, void *data)
+{
+	struct ade_crtc *acrtc = data;
+	struct ade_hw_ctx *ctx = acrtc->ctx;
+	struct drm_crtc *crtc = &acrtc->base;
+	void __iomem *base = ctx->base;
+	u32 status;
+
+	status = readl(base + LDI_MSK_INT);
+	DRM_DEBUG_VBL("LDI IRQ: status=0x%X\n", status);
+
+	/* vblank irq */
+	if (status & BIT(FRAME_END_INT_EN_OFST)) {
+		ade_update_bits(base + LDI_INT_CLR, FRAME_END_INT_EN_OFST,
+				MASK(1), 1);
+		drm_crtc_handle_vblank(crtc);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void ade_display_enable(struct ade_crtc *acrtc)
+{
+	struct ade_hw_ctx *ctx = acrtc->ctx;
+	void __iomem *base = ctx->base;
+	u32 out_fmt = acrtc->out_format;
+
+	/* enable output overlay compositor */
+	writel(ADE_ENABLE, base + ADE_OVLYX_CTL(OUT_OVLY));
+	ade_update_reload_bit(base, OVLY_OFST + OUT_OVLY, 0);
+
+	/* display source setting */
+	writel(DISP_SRC_OVLY2, base + ADE_DISP_SRC_CFG);
+
+	/* enable ade */
+	writel(ADE_ENABLE, base + ADE_EN);
+	/* enable ldi */
+	writel(NORMAL_MODE, base + LDI_WORK_MODE);
+	writel((out_fmt << BPP_OFST) | DATA_GATE_EN | LDI_EN,
+	       base + LDI_CTRL);
+	/* dsi pixel on */
+	writel(DSI_PCLK_ON, base + LDI_HDMI_DSI_GT);
+}
+
+#if ADE_DEBUG
+static void ade_rdma_dump_regs(void __iomem *base, u32 ch)
+{
+	u32 reg_ctrl, reg_addr, reg_size, reg_stride, reg_space, reg_en;
+	u32 val;
+
+	reg_ctrl = RD_CH_CTRL(ch);
+	reg_addr = RD_CH_ADDR(ch);
+	reg_size = RD_CH_SIZE(ch);
+	reg_stride = RD_CH_STRIDE(ch);
+	reg_space = RD_CH_SPACE(ch);
+	reg_en = RD_CH_EN(ch);
+
+	val = ade_read_reload_bit(base, RDMA_OFST + ch);
+	DRM_DEBUG_DRIVER("[rdma%d]: reload(%d)\n", ch + 1, val);
+	val = readl(base + reg_ctrl);
+	DRM_DEBUG_DRIVER("[rdma%d]: reg_ctrl(0x%08x)\n", ch + 1, val);
+	val = readl(base + reg_addr);
+	DRM_DEBUG_DRIVER("[rdma%d]: reg_addr(0x%08x)\n", ch + 1, val);
+	val = readl(base + reg_size);
+	DRM_DEBUG_DRIVER("[rdma%d]: reg_size(0x%08x)\n", ch + 1, val);
+	val = readl(base + reg_stride);
+	DRM_DEBUG_DRIVER("[rdma%d]: reg_stride(0x%08x)\n", ch + 1, val);
+	val = readl(base + reg_space);
+	DRM_DEBUG_DRIVER("[rdma%d]: reg_space(0x%08x)\n", ch + 1, val);
+	val = readl(base + reg_en);
+	DRM_DEBUG_DRIVER("[rdma%d]: reg_en(0x%08x)\n", ch + 1, val);
+}
+
+static void ade_clip_dump_regs(void __iomem *base, u32 ch)
+{
+	u32 val;
+
+	val = ade_read_reload_bit(base, CLIP_OFST + ch);
+	DRM_DEBUG_DRIVER("[clip%d]: reload(%d)\n", ch + 1, val);
+	val = readl(base + ADE_CLIP_DISABLE(ch));
+	DRM_DEBUG_DRIVER("[clip%d]: reg_clip_disable(0x%08x)\n", ch + 1, val);
+	val = readl(base + ADE_CLIP_SIZE0(ch));
+	DRM_DEBUG_DRIVER("[clip%d]: reg_clip_size0(0x%08x)\n", ch + 1, val);
+	val = readl(base + ADE_CLIP_SIZE1(ch));
+	DRM_DEBUG_DRIVER("[clip%d]: reg_clip_size1(0x%08x)\n", ch + 1, val);
+}
+
+static void ade_compositor_routing_dump_regs(void __iomem *base, u32 ch)
+{
+	u8 ovly_ch = 0; /* TODO: Only primary plane now */
+	u32 val;
+
+	val = readl(base + ADE_OVLY_CH_XY0(ovly_ch));
+	DRM_DEBUG_DRIVER("[overlay ch%d]: reg_ch_xy0(0x%08x)\n", ovly_ch, val);
+	val = readl(base + ADE_OVLY_CH_XY1(ovly_ch));
+	DRM_DEBUG_DRIVER("[overlay ch%d]: reg_ch_xy1(0x%08x)\n", ovly_ch, val);
+	val = readl(base + ADE_OVLY_CH_CTL(ovly_ch));
+	DRM_DEBUG_DRIVER("[overlay ch%d]: reg_ch_ctl(0x%08x)\n", ovly_ch, val);
+}
+
+static void ade_dump_overlay_compositor_regs(void __iomem *base, u32 comp)
+{
+	u32 val;
+
+	val = ade_read_reload_bit(base, OVLY_OFST + comp);
+	DRM_DEBUG_DRIVER("[overlay%d]: reload(%d)\n", comp + 1, val);
+	writel(ADE_ENABLE, base + ADE_OVLYX_CTL(comp));
+	DRM_DEBUG_DRIVER("[overlay%d]: reg_ctl(0x%08x)\n", comp + 1, val);
+	val = readl(base + ADE_OVLY_CTL);
+	DRM_DEBUG_DRIVER("ovly_ctl(0x%08x)\n", val);
+}
+
+static void ade_dump_regs(void __iomem *base)
+{
+	u32 i;
+
+	/* dump channel regs */
+	for (i = 0; i < ADE_CH_NUM; i++) {
+		/* dump rdma regs */
+		ade_rdma_dump_regs(base, i);
+
+		/* dump clip regs */
+		ade_clip_dump_regs(base, i);
+
+		/* dump compositor routing regs */
+		ade_compositor_routing_dump_regs(base, i);
+	}
+
+	/* dump overlay compositor regs */
+	ade_dump_overlay_compositor_regs(base, OUT_OVLY);
+}
+#else
+static void ade_dump_regs(void __iomem *base) { }
+#endif
+
+static void ade_crtc_enable(struct drm_crtc *crtc)
+{
+	struct ade_crtc *acrtc = to_ade_crtc(crtc);
+	struct ade_hw_ctx *ctx = acrtc->ctx;
+	int ret;
+
+	if (acrtc->enable)
+		return;
+
+	if (!ctx->power_on) {
+		ret = ade_power_up(ctx);
+		if (ret)
+			return;
+	}
+
+	ade_set_medianoc_qos(acrtc);
+	ade_display_enable(acrtc);
+	ade_dump_regs(ctx->base);
+	acrtc->enable = true;
+}
+
+static void ade_crtc_disable(struct drm_crtc *crtc)
+{
+	struct ade_crtc *acrtc = to_ade_crtc(crtc);
+	struct ade_hw_ctx *ctx = acrtc->ctx;
+
+	if (!acrtc->enable)
+		return;
+
+	ade_power_down(ctx);
+	acrtc->enable = false;
+}
+
+static int ade_crtc_atomic_check(struct drm_crtc *crtc,
+				 struct drm_crtc_state *state)
+{
+	/* do nothing */
+	return 0;
+}
+
+static void ade_crtc_mode_set_nofb(struct drm_crtc *crtc)
+{
+	struct ade_crtc *acrtc = to_ade_crtc(crtc);
+	struct ade_hw_ctx *ctx = acrtc->ctx;
+	struct drm_display_mode *mode = &crtc->state->mode;
+	struct drm_display_mode *adj_mode = &crtc->state->adjusted_mode;
+
+	if (!ctx->power_on)
+		(void)ade_power_up(ctx);
+	ade_ldi_set_mode(acrtc, mode, adj_mode);
+}
+
+static void ade_crtc_atomic_begin(struct drm_crtc *crtc,
+				  struct drm_crtc_state *old_state)
+{
+	struct ade_crtc *acrtc = to_ade_crtc(crtc);
+	struct ade_hw_ctx *ctx = acrtc->ctx;
+
+	if (!ctx->power_on)
+		(void)ade_power_up(ctx);
+}
+
+static void ade_crtc_atomic_flush(struct drm_crtc *crtc,
+				  struct drm_crtc_state *old_state)
+
+{
+	struct ade_crtc *acrtc = to_ade_crtc(crtc);
+	struct ade_hw_ctx *ctx = acrtc->ctx;
+	void __iomem *base = ctx->base;
+
+	/* only crtc is enabled regs take effect */
+	if (acrtc->enable) {
+		ade_dump_regs(base);
+		/* flush ade registers */
+		writel(ADE_ENABLE, base + ADE_EN);
+	}
+}
+
+static const struct drm_crtc_helper_funcs ade_crtc_helper_funcs = {
+	.enable		= ade_crtc_enable,
+	.disable	= ade_crtc_disable,
+	.atomic_check	= ade_crtc_atomic_check,
+	.mode_set_nofb	= ade_crtc_mode_set_nofb,
+	.atomic_begin	= ade_crtc_atomic_begin,
+	.atomic_flush	= ade_crtc_atomic_flush,
+};
+
+static const struct drm_crtc_funcs ade_crtc_funcs = {
+	.destroy	= drm_crtc_cleanup,
+	.set_config	= drm_atomic_helper_set_config,
+	.page_flip	= drm_atomic_helper_page_flip,
+	.reset		= drm_atomic_helper_crtc_reset,
+	.set_property = drm_atomic_helper_crtc_set_property,
+	.atomic_duplicate_state	= drm_atomic_helper_crtc_duplicate_state,
+	.atomic_destroy_state	= drm_atomic_helper_crtc_destroy_state,
+};
+
+static int ade_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
+			 struct drm_plane *plane)
+{
+	struct kirin_drm_private *priv = dev->dev_private;
+	struct device_node *port;
+	int ret;
+
+	/* set crtc port so that
+	 * drm_of_find_possible_crtcs call works
+	 */
+	port = of_get_child_by_name(dev->dev->of_node, "port");
+	if (!port) {
+		DRM_ERROR("no port node found in %s\n",
+			  dev->dev->of_node->full_name);
+		return -EINVAL;
+	}
+	of_node_put(port);
+	crtc->port = port;
+
+	ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,
+					&ade_crtc_funcs, NULL);
+	if (ret) {
+		DRM_ERROR("failed to init crtc.\n");
+		return ret;
+	}
+
+	drm_crtc_helper_add(crtc, &ade_crtc_helper_funcs);
+	priv->crtc[drm_crtc_index(crtc)] = crtc;
+
+	return 0;
+}
+
+static void ade_rdma_set(void __iomem *base, struct drm_framebuffer *fb,
+			 u32 ch, u32 y, u32 in_h, u32 fmt)
+{
+	struct drm_gem_cma_object *obj = drm_fb_cma_get_gem_obj(fb, 0);
+	u32 reg_ctrl, reg_addr, reg_size, reg_stride, reg_space, reg_en;
+	u32 stride = fb->pitches[0];
+	u32 addr = (u32)obj->paddr + y * stride;
+
+	DRM_DEBUG_DRIVER("rdma%d: (y=%d, height=%d), stride=%d, paddr=0x%x\n",
+			 ch + 1, y, in_h, stride, (u32)obj->paddr);
+	DRM_DEBUG_DRIVER("addr=0x%x, fb:%dx%d, pixel_format=%d(%s)\n",
+			 addr, fb->width, fb->height, fmt,
+			 drm_get_format_name(fb->pixel_format));
+
+	/* get reg offset */
+	reg_ctrl = RD_CH_CTRL(ch);
+	reg_addr = RD_CH_ADDR(ch);
+	reg_size = RD_CH_SIZE(ch);
+	reg_stride = RD_CH_STRIDE(ch);
+	reg_space = RD_CH_SPACE(ch);
+	reg_en = RD_CH_EN(ch);
+
+	/*
+	 * TODO: set rotation
+	 */
+	writel((fmt << 16) & 0x1f0000, base + reg_ctrl);
+	writel(addr, base + reg_addr);
+	writel((in_h << 16) | stride, base + reg_size);
+	writel(stride, base + reg_stride);
+	writel(in_h * stride, base + reg_space);
+	writel(ADE_ENABLE, base + reg_en);
+	ade_update_reload_bit(base, RDMA_OFST + ch, 0);
+}
+
+static void ade_rdma_disable(void __iomem *base, u32 ch)
+{
+	u32 reg_en;
+
+	/* get reg offset */
+	reg_en = RD_CH_EN(ch);
+	writel(0, base + reg_en);
+	ade_update_reload_bit(base, RDMA_OFST + ch, 1);
+}
+
+static void ade_clip_set(void __iomem *base, u32 ch, u32 fb_w, u32 x,
+			 u32 in_w, u32 in_h)
+{
+	u32 disable_val;
+	u32 clip_left;
+	u32 clip_right;
+
+	/*
+	 * clip width, no need to clip height
+	 */
+	if (fb_w == in_w) { /* bypass */
+		disable_val = 1;
+		clip_left = 0;
+		clip_right = 0;
+	} else {
+		disable_val = 0;
+		clip_left = x;
+		clip_right = fb_w - (x + in_w) - 1;
+	}
+
+	DRM_DEBUG_DRIVER("clip%d: clip_left=%d, clip_right=%d\n",
+			 ch + 1, clip_left, clip_right);
+
+	writel(disable_val, base + ADE_CLIP_DISABLE(ch));
+	writel((fb_w - 1) << 16 | (in_h - 1), base + ADE_CLIP_SIZE0(ch));
+	writel(clip_left << 16 | clip_right, base + ADE_CLIP_SIZE1(ch));
+	ade_update_reload_bit(base, CLIP_OFST + ch, 0);
+}
+
+static void ade_clip_disable(void __iomem *base, u32 ch)
+{
+	writel(1, base + ADE_CLIP_DISABLE(ch));
+	ade_update_reload_bit(base, CLIP_OFST + ch, 1);
+}
+
+static bool has_Alpha_channel(int format)
+{
+	switch (format) {
+	case ADE_ARGB_8888:
+	case ADE_ABGR_8888:
+	case ADE_RGBA_8888:
+	case ADE_BGRA_8888:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static void ade_get_blending_params(u32 fmt, u8 glb_alpha, u8 *alp_mode,
+				    u8 *alp_sel, u8 *under_alp_sel)
+{
+	bool has_alpha = has_Alpha_channel(fmt);
+
+	/*
+	 * get alp_mode
+	 */
+	if (has_alpha && glb_alpha < 255)
+		*alp_mode = ADE_ALP_PIXEL_AND_GLB;
+	else if (has_alpha)
+		*alp_mode = ADE_ALP_PIXEL;
+	else
+		*alp_mode = ADE_ALP_GLOBAL;
+
+	/*
+	 * get alp sel
+	 */
+	*alp_sel = ADE_ALP_MUL_COEFF_3; /* 1 */
+	*under_alp_sel = ADE_ALP_MUL_COEFF_2; /* 0 */
+}
+
+static void ade_compositor_routing_set(void __iomem *base, u8 ch,
+				       u32 x0, u32 y0,
+				       u32 in_w, u32 in_h, u32 fmt)
+{
+	u8 ovly_ch = 0; /* TODO: This is the zpos, only one plane now */
+	u8 glb_alpha = 255;
+	u32 x1 = x0 + in_w - 1;
+	u32 y1 = y0 + in_h - 1;
+	u32 val;
+	u8 alp_sel;
+	u8 under_alp_sel;
+	u8 alp_mode;
+
+	ade_get_blending_params(fmt, glb_alpha, &alp_mode, &alp_sel,
+				&under_alp_sel);
+
+	/* overlay routing setting
+	 */
+	writel(x0 << 16 | y0, base + ADE_OVLY_CH_XY0(ovly_ch));
+	writel(x1 << 16 | y1, base + ADE_OVLY_CH_XY1(ovly_ch));
+	val = (ch + 1) << CH_SEL_OFST | BIT(CH_EN_OFST) |
+		alp_sel << CH_ALP_SEL_OFST |
+		under_alp_sel << CH_UNDER_ALP_SEL_OFST |
+		glb_alpha << CH_ALP_GBL_OFST |
+		alp_mode << CH_ALP_MODE_OFST;
+	writel(val, base + ADE_OVLY_CH_CTL(ovly_ch));
+	/* connect this plane/channel to overlay2 compositor */
+	ade_update_bits(base + ADE_OVLY_CTL, CH_OVLY_SEL_OFST(ovly_ch),
+			CH_OVLY_SEL_MASK, CH_OVLY_SEL_VAL(OUT_OVLY));
+}
+
+static void ade_compositor_routing_disable(void __iomem *base, u32 ch)
+{
+	u8 ovly_ch = 0; /* TODO: Only primary plane now */
+
+	/* disable this plane/channel */
+	ade_update_bits(base + ADE_OVLY_CH_CTL(ovly_ch), CH_EN_OFST,
+			MASK(1), 0);
+	/* dis-connect this plane/channel of overlay2 compositor */
+	ade_update_bits(base + ADE_OVLY_CTL, CH_OVLY_SEL_OFST(ovly_ch),
+			CH_OVLY_SEL_MASK, 0);
+}
+
+/*
+ * Typicaly, a channel looks like: DMA-->clip-->scale-->ctrans-->compositor
+ */
+static void ade_update_channel(struct ade_plane *aplane,
+			       struct drm_framebuffer *fb, int crtc_x,
+			       int crtc_y, unsigned int crtc_w,
+			       unsigned int crtc_h, u32 src_x,
+			       u32 src_y, u32 src_w, u32 src_h)
+{
+	struct ade_hw_ctx *ctx = aplane->ctx;
+	void __iomem *base = ctx->base;
+	u32 fmt = ade_get_format(fb->pixel_format);
+	u32 ch = aplane->ch;
+	u32 in_w;
+	u32 in_h;
+
+	DRM_DEBUG_DRIVER("channel%d: src:(%d, %d)-%dx%d, crtc:(%d, %d)-%dx%d",
+			 ch + 1, src_x, src_y, src_w, src_h,
+			 crtc_x, crtc_y, crtc_w, crtc_h);
+
+	/* 1) DMA setting */
+	in_w = src_w;
+	in_h = src_h;
+	ade_rdma_set(base, fb, ch, src_y, in_h, fmt);
+
+	/* 2) clip setting */
+	ade_clip_set(base, ch, fb->width, src_x, in_w, in_h);
+
+	/* 3) TODO: scale setting for overlay planes */
+
+	/* 4) TODO: ctran/csc setting for overlay planes */
+
+	/* 5) compositor routing setting */
+	ade_compositor_routing_set(base, ch, crtc_x, crtc_y, in_w, in_h, fmt);
+}
+
+static void ade_disable_channel(struct ade_plane *aplane)
+{
+	struct ade_hw_ctx *ctx = aplane->ctx;
+	void __iomem *base = ctx->base;
+	u32 ch = aplane->ch;
+
+	DRM_DEBUG_DRIVER("disable channel%d\n", ch + 1);
+
+	/* disable read DMA */
+	ade_rdma_disable(base, ch);
+
+	/* disable clip */
+	ade_clip_disable(base, ch);
+
+	/* disable compositor routing */
+	ade_compositor_routing_disable(base, ch);
+}
+
+static int ade_plane_prepare_fb(struct drm_plane *plane,
+				const struct drm_plane_state *new_state)
+{
+	/* do nothing */
+	return 0;
+}
+
+static void ade_plane_cleanup_fb(struct drm_plane *plane,
+				 const struct drm_plane_state *old_state)
+{
+	/* do nothing */
+}
+
+static int ade_plane_atomic_check(struct drm_plane *plane,
+				  struct drm_plane_state *state)
+{
+	struct drm_framebuffer *fb = state->fb;
+	struct drm_crtc *crtc = state->crtc;
+	struct drm_crtc_state *crtc_state;
+	u32 src_x = state->src_x >> 16;
+	u32 src_y = state->src_y >> 16;
+	u32 src_w = state->src_w >> 16;
+	u32 src_h = state->src_h >> 16;
+	int crtc_x = state->crtc_x;
+	int crtc_y = state->crtc_y;
+	u32 crtc_w = state->crtc_w;
+	u32 crtc_h = state->crtc_h;
+	u32 fmt;
+
+	if (!crtc || !fb)
+		return 0;
+
+	fmt = ade_get_format(fb->pixel_format);
+	if (fmt == ADE_FORMAT_UNSUPPORT)
+		return -EINVAL;
+
+	crtc_state = drm_atomic_get_crtc_state(state->state, crtc);
+	if (IS_ERR(crtc_state))
+		return PTR_ERR(crtc_state);
+
+	if (src_w != crtc_w || src_h != crtc_h) {
+		DRM_ERROR("Scale not support!!!\n");
+		return -EINVAL;
+	}
+
+	if (src_x + src_w > fb->width ||
+	    src_y + src_h > fb->height)
+		return -EINVAL;
+
+	if (crtc_x < 0 || crtc_y < 0)
+		return -EINVAL;
+
+	if (crtc_x + crtc_w > crtc_state->adjusted_mode.hdisplay ||
+	    crtc_y + crtc_h > crtc_state->adjusted_mode.vdisplay)
+		return -EINVAL;
+
+	return 0;
+}
+
+static void ade_plane_atomic_update(struct drm_plane *plane,
+				    struct drm_plane_state *old_state)
+{
+	struct drm_plane_state	*state	= plane->state;
+	struct ade_plane *aplane = to_ade_plane(plane);
+
+	ade_update_channel(aplane, state->fb, state->crtc_x, state->crtc_y,
+			   state->crtc_w, state->crtc_h,
+			   state->src_x >> 16, state->src_y >> 16,
+			   state->src_w >> 16, state->src_h >> 16);
+}
+
+static void ade_plane_atomic_disable(struct drm_plane *plane,
+				     struct drm_plane_state *old_state)
+{
+	struct ade_plane *aplane = to_ade_plane(plane);
+
+	ade_disable_channel(aplane);
+}
+
+static const struct drm_plane_helper_funcs ade_plane_helper_funcs = {
+	.prepare_fb = ade_plane_prepare_fb,
+	.cleanup_fb = ade_plane_cleanup_fb,
+	.atomic_check = ade_plane_atomic_check,
+	.atomic_update = ade_plane_atomic_update,
+	.atomic_disable = ade_plane_atomic_disable,
+};
+
+static struct drm_plane_funcs ade_plane_funcs = {
+	.update_plane	= drm_atomic_helper_update_plane,
+	.disable_plane	= drm_atomic_helper_disable_plane,
+	.set_property = drm_atomic_helper_plane_set_property,
+	.destroy = drm_plane_cleanup,
+	.reset = drm_atomic_helper_plane_reset,
+	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+static int ade_plane_init(struct drm_device *dev, struct ade_plane *aplane,
+			  enum drm_plane_type type)
+{
+	const u32 *fmts;
+	u32 fmts_cnt;
+	int ret = 0;
+
+	/* get  properties */
+	fmts_cnt = ade_get_channel_formats(aplane->ch, &fmts);
+	if (ret)
+		return ret;
+
+	ret = drm_universal_plane_init(dev, &aplane->base, 1, &ade_plane_funcs,
+				       fmts, fmts_cnt, type, NULL);
+	if (ret) {
+		DRM_ERROR("fail to init plane, ch=%d\n", aplane->ch);
+		return ret;
+	}
+
+	drm_plane_helper_add(&aplane->base, &ade_plane_helper_funcs);
+
+	return 0;
+}
+
+static int ade_dts_parse(struct platform_device *pdev, struct ade_hw_ctx *ctx)
+{
+	struct resource *res;
+	struct device *dev = &pdev->dev;
+	struct device_node *np = pdev->dev.of_node;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	ctx->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(ctx->base)) {
+		DRM_ERROR("failed to remap ade io base\n");
+		return  PTR_ERR(ctx->base);
+	}
+
+	ctx->reset = devm_reset_control_get(dev, NULL);
+	if (IS_ERR(ctx->reset))
+		return PTR_ERR(ctx->reset);
+
+	ctx->noc_regmap =
+		syscon_regmap_lookup_by_phandle(np, "hisilicon,noc-syscon");
+	if (IS_ERR(ctx->noc_regmap)) {
+		DRM_ERROR("failed to get noc regmap\n");
+		return PTR_ERR(ctx->noc_regmap);
+	}
+
+	ctx->irq = platform_get_irq(pdev, 0);
+	if (ctx->irq < 0) {
+		DRM_ERROR("failed to get irq\n");
+		return -ENODEV;
+	}
+
+	ctx->ade_core_clk = devm_clk_get(dev, "clk_ade_core");
+	if (!ctx->ade_core_clk) {
+		DRM_ERROR("failed to parse clk ADE_CORE\n");
+		return -ENODEV;
+	}
+
+	ctx->media_noc_clk = devm_clk_get(dev, "clk_codec_jpeg");
+	if (!ctx->media_noc_clk) {
+		DRM_ERROR("failed to parse clk CODEC_JPEG\n");
+	    return -ENODEV;
+	}
+
+	ctx->ade_pix_clk = devm_clk_get(dev, "clk_ade_pix");
+	if (!ctx->ade_pix_clk) {
+		DRM_ERROR("failed to parse clk ADE_PIX\n");
+	    return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int ade_drm_init(struct drm_device *dev)
+{
+	struct platform_device *pdev = dev->platformdev;
+	struct ade_data *ade;
+	struct ade_hw_ctx *ctx;
+	struct ade_crtc *acrtc;
+	struct ade_plane *aplane;
+	enum drm_plane_type type;
+	int ret;
+	int i;
+
+	ade = devm_kzalloc(dev->dev, sizeof(*ade), GFP_KERNEL);
+	if (!ade) {
+		DRM_ERROR("failed to alloc ade_data\n");
+		return -ENOMEM;
+	}
+	platform_set_drvdata(pdev, ade);
+
+	ctx = &ade->ctx;
+	acrtc = &ade->acrtc;
+	acrtc->ctx = ctx;
+	acrtc->out_format = LDI_OUT_RGB_888;
+
+	ret = ade_dts_parse(pdev, ctx);
+	if (ret)
+		return ret;
+
+	/*
+	 * plane init
+	 * TODO: Now only support primary plane, overlay planes
+	 * need to do.
+	 */
+	for (i = 0; i < ADE_CH_NUM; i++) {
+		aplane = &ade->aplane[i];
+		aplane->ch = i;
+		aplane->ctx = ctx;
+		type = i == PRIMARY_CH ? DRM_PLANE_TYPE_PRIMARY :
+			DRM_PLANE_TYPE_OVERLAY;
+
+		ret = ade_plane_init(dev, aplane, type);
+		if (ret)
+			return ret;
+	}
+
+	/* crtc init */
+	ret = ade_crtc_init(dev, &acrtc->base, &ade->aplane[PRIMARY_CH].base);
+	if (ret)
+		return ret;
+
+	/* vblank irq init */
+	ret = devm_request_irq(dev->dev, ctx->irq, ade_irq_handler,
+			       IRQF_SHARED, dev->driver->name, acrtc);
+	if (ret)
+		return ret;
+	dev->driver->get_vblank_counter = drm_vblank_no_hw_counter;
+	dev->driver->enable_vblank = ade_enable_vblank;
+	dev->driver->disable_vblank = ade_disable_vblank;
+
+	return 0;
+}
+
+static void ade_drm_cleanup(struct drm_device *dev)
+{
+	struct platform_device *pdev = dev->platformdev;
+	struct ade_data *ade = platform_get_drvdata(pdev);
+	struct drm_crtc *crtc = &ade->acrtc.base;
+
+	drm_crtc_cleanup(crtc);
+}
+
+const struct kirin_dc_ops ade_dc_ops = {
+	.init = ade_drm_init,
+	.cleanup = ade_drm_cleanup
+};

+ 367 - 0
drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c

@@ -0,0 +1,367 @@
+/*
+ * Hisilicon Kirin SoCs drm master driver
+ *
+ * Copyright (c) 2016 Linaro Limited.
+ * Copyright (c) 2014-2016 Hisilicon Limited.
+ *
+ * Author:
+ *	Xinliang Liu <z.liuxinliang@hisilicon.com>
+ *	Xinliang Liu <xinliang.liu@linaro.org>
+ *	Xinwei Kong <kong.kongxinwei@hisilicon.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/of_platform.h>
+#include <linux/component.h>
+#include <linux/of_graph.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "kirin_drm_drv.h"
+
+static struct kirin_dc_ops *dc_ops;
+
+static int kirin_drm_kms_cleanup(struct drm_device *dev)
+{
+	struct kirin_drm_private *priv = dev->dev_private;
+
+#ifdef CONFIG_DRM_FBDEV_EMULATION
+	if (priv->fbdev) {
+		drm_fbdev_cma_fini(priv->fbdev);
+		priv->fbdev = NULL;
+	}
+#endif
+	drm_kms_helper_poll_fini(dev);
+	drm_vblank_cleanup(dev);
+	dc_ops->cleanup(dev);
+	drm_mode_config_cleanup(dev);
+	devm_kfree(dev->dev, priv);
+	dev->dev_private = NULL;
+
+	return 0;
+}
+
+#ifdef CONFIG_DRM_FBDEV_EMULATION
+static void kirin_fbdev_output_poll_changed(struct drm_device *dev)
+{
+	struct kirin_drm_private *priv = dev->dev_private;
+
+	if (priv->fbdev) {
+		drm_fbdev_cma_hotplug_event(priv->fbdev);
+	} else {
+		priv->fbdev = drm_fbdev_cma_init(dev, 32,
+				dev->mode_config.num_crtc,
+				dev->mode_config.num_connector);
+		if (IS_ERR(priv->fbdev))
+			priv->fbdev = NULL;
+	}
+}
+#endif
+
+static const struct drm_mode_config_funcs kirin_drm_mode_config_funcs = {
+	.fb_create = drm_fb_cma_create,
+#ifdef CONFIG_DRM_FBDEV_EMULATION
+	.output_poll_changed = kirin_fbdev_output_poll_changed,
+#endif
+	.atomic_check = drm_atomic_helper_check,
+	.atomic_commit = drm_atomic_helper_commit,
+};
+
+static void kirin_drm_mode_config_init(struct drm_device *dev)
+{
+	dev->mode_config.min_width = 0;
+	dev->mode_config.min_height = 0;
+
+	dev->mode_config.max_width = 2048;
+	dev->mode_config.max_height = 2048;
+
+	dev->mode_config.funcs = &kirin_drm_mode_config_funcs;
+}
+
+static int kirin_drm_kms_init(struct drm_device *dev)
+{
+	struct kirin_drm_private *priv;
+	int ret;
+
+	priv = devm_kzalloc(dev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	dev->dev_private = priv;
+	dev_set_drvdata(dev->dev, dev);
+
+	/* dev->mode_config initialization */
+	drm_mode_config_init(dev);
+	kirin_drm_mode_config_init(dev);
+
+	/* display controller init */
+	ret = dc_ops->init(dev);
+	if (ret)
+		goto err_mode_config_cleanup;
+
+	/* bind and init sub drivers */
+	ret = component_bind_all(dev->dev, dev);
+	if (ret) {
+		DRM_ERROR("failed to bind all component.\n");
+		goto err_dc_cleanup;
+	}
+
+	/* vblank init */
+	ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
+	if (ret) {
+		DRM_ERROR("failed to initialize vblank.\n");
+		goto err_unbind_all;
+	}
+	/* with irq_enabled = true, we can use the vblank feature. */
+	dev->irq_enabled = true;
+
+	/* reset all the states of crtc/plane/encoder/connector */
+	drm_mode_config_reset(dev);
+
+	/* init kms poll for handling hpd */
+	drm_kms_helper_poll_init(dev);
+
+	/* force detection after connectors init */
+	(void)drm_helper_hpd_irq_event(dev);
+
+	return 0;
+
+err_unbind_all:
+	component_unbind_all(dev->dev, dev);
+err_dc_cleanup:
+	dc_ops->cleanup(dev);
+err_mode_config_cleanup:
+	drm_mode_config_cleanup(dev);
+	devm_kfree(dev->dev, priv);
+	dev->dev_private = NULL;
+
+	return ret;
+}
+
+static const struct file_operations kirin_drm_fops = {
+	.owner		= THIS_MODULE,
+	.open		= drm_open,
+	.release	= drm_release,
+	.unlocked_ioctl	= drm_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl	= drm_compat_ioctl,
+#endif
+	.poll		= drm_poll,
+	.read		= drm_read,
+	.llseek		= no_llseek,
+	.mmap		= drm_gem_cma_mmap,
+};
+
+static int kirin_gem_cma_dumb_create(struct drm_file *file,
+				     struct drm_device *dev,
+				     struct drm_mode_create_dumb *args)
+{
+	return drm_gem_cma_dumb_create_internal(file, dev, args);
+}
+
+static struct drm_driver kirin_drm_driver = {
+	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
+				  DRIVER_ATOMIC | DRIVER_HAVE_IRQ,
+	.fops			= &kirin_drm_fops,
+	.set_busid		= drm_platform_set_busid,
+
+	.gem_free_object	= drm_gem_cma_free_object,
+	.gem_vm_ops		= &drm_gem_cma_vm_ops,
+	.dumb_create		= kirin_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_export	= drm_gem_prime_export,
+	.gem_prime_import	= drm_gem_prime_import,
+	.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,
+
+	.name			= "kirin",
+	.desc			= "Hisilicon Kirin SoCs' DRM Driver",
+	.date			= "20150718",
+	.major			= 1,
+	.minor			= 0,
+};
+
+static int compare_of(struct device *dev, void *data)
+{
+	return dev->of_node == data;
+}
+
+static int kirin_drm_connectors_register(struct drm_device *dev)
+{
+	struct drm_connector *connector;
+	struct drm_connector *failed_connector;
+	int ret;
+
+	mutex_lock(&dev->mode_config.mutex);
+	drm_for_each_connector(connector, dev) {
+		ret = drm_connector_register(connector);
+		if (ret) {
+			failed_connector = connector;
+			goto err;
+		}
+	}
+	mutex_unlock(&dev->mode_config.mutex);
+
+	return 0;
+
+err:
+	drm_for_each_connector(connector, dev) {
+		if (failed_connector == connector)
+			break;
+		drm_connector_unregister(connector);
+	}
+	mutex_unlock(&dev->mode_config.mutex);
+
+	return ret;
+}
+
+static int kirin_drm_bind(struct device *dev)
+{
+	struct drm_driver *driver = &kirin_drm_driver;
+	struct drm_device *drm_dev;
+	int ret;
+
+	drm_dev = drm_dev_alloc(driver, dev);
+	if (!drm_dev)
+		return -ENOMEM;
+
+	drm_dev->platformdev = to_platform_device(dev);
+
+	ret = kirin_drm_kms_init(drm_dev);
+	if (ret)
+		goto err_drm_dev_unref;
+
+	ret = drm_dev_register(drm_dev, 0);
+	if (ret)
+		goto err_kms_cleanup;
+
+	/* connectors should be registered after drm device register */
+	ret = kirin_drm_connectors_register(drm_dev);
+	if (ret)
+		goto err_drm_dev_unregister;
+
+	DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
+		 driver->name, driver->major, driver->minor, driver->patchlevel,
+		 driver->date, drm_dev->primary->index);
+
+	return 0;
+
+err_drm_dev_unregister:
+	drm_dev_unregister(drm_dev);
+err_kms_cleanup:
+	kirin_drm_kms_cleanup(drm_dev);
+err_drm_dev_unref:
+	drm_dev_unref(drm_dev);
+
+	return ret;
+}
+
+static void kirin_drm_unbind(struct device *dev)
+{
+	drm_put_dev(dev_get_drvdata(dev));
+}
+
+static const struct component_master_ops kirin_drm_ops = {
+	.bind = kirin_drm_bind,
+	.unbind = kirin_drm_unbind,
+};
+
+static struct device_node *kirin_get_remote_node(struct device_node *np)
+{
+	struct device_node *endpoint, *remote;
+
+	/* get the first endpoint, in our case only one remote node
+	 * is connected to display controller.
+	 */
+	endpoint = of_graph_get_next_endpoint(np, NULL);
+	if (!endpoint) {
+		DRM_ERROR("no valid endpoint node\n");
+		return ERR_PTR(-ENODEV);
+	}
+	of_node_put(endpoint);
+
+	remote = of_graph_get_remote_port_parent(endpoint);
+	if (!remote) {
+		DRM_ERROR("no valid remote node\n");
+		return ERR_PTR(-ENODEV);
+	}
+	of_node_put(remote);
+
+	if (!of_device_is_available(remote)) {
+		DRM_ERROR("not available for remote node\n");
+		return ERR_PTR(-ENODEV);
+	}
+
+	return remote;
+}
+
+static int kirin_drm_platform_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct component_match *match = NULL;
+	struct device_node *remote;
+
+	dc_ops = (struct kirin_dc_ops *)of_device_get_match_data(dev);
+	if (!dc_ops) {
+		DRM_ERROR("failed to get dt id data\n");
+		return -EINVAL;
+	}
+
+	remote = kirin_get_remote_node(np);
+	if (IS_ERR(remote))
+		return PTR_ERR(remote);
+
+	component_match_add(dev, &match, compare_of, remote);
+
+	return component_master_add_with_match(dev, &kirin_drm_ops, match);
+
+	return 0;
+}
+
+static int kirin_drm_platform_remove(struct platform_device *pdev)
+{
+	component_master_del(&pdev->dev, &kirin_drm_ops);
+	dc_ops = NULL;
+	return 0;
+}
+
+static const struct of_device_id kirin_drm_dt_ids[] = {
+	{ .compatible = "hisilicon,hi6220-ade",
+	  .data = &ade_dc_ops,
+	},
+	{ /* end node */ },
+};
+MODULE_DEVICE_TABLE(of, kirin_drm_dt_ids);
+
+static struct platform_driver kirin_drm_platform_driver = {
+	.probe = kirin_drm_platform_probe,
+	.remove = kirin_drm_platform_remove,
+	.driver = {
+		.name = "kirin-drm",
+		.of_match_table = kirin_drm_dt_ids,
+	},
+};
+
+module_platform_driver(kirin_drm_platform_driver);
+
+MODULE_AUTHOR("Xinliang Liu <xinliang.liu@linaro.org>");
+MODULE_AUTHOR("Xinliang Liu <z.liuxinliang@hisilicon.com>");
+MODULE_AUTHOR("Xinwei Kong <kong.kongxinwei@hisilicon.com>");
+MODULE_DESCRIPTION("hisilicon Kirin SoCs' DRM master driver");
+MODULE_LICENSE("GPL v2");

+ 31 - 0
drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h

@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2016 Linaro Limited.
+ * Copyright (c) 2014-2016 Hisilicon Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __KIRIN_DRM_DRV_H__
+#define __KIRIN_DRM_DRV_H__
+
+#define MAX_CRTC	2
+
+/* display controller init/cleanup ops */
+struct kirin_dc_ops {
+	int (*init)(struct drm_device *dev);
+	void (*cleanup)(struct drm_device *dev);
+};
+
+struct kirin_drm_private {
+	struct drm_crtc *crtc[MAX_CRTC];
+#ifdef CONFIG_DRM_FBDEV_EMULATION
+	struct drm_fbdev_cma *fbdev;
+#endif
+};
+
+extern const struct kirin_dc_ops ade_dc_ops;
+
+#endif /* __KIRIN_DRM_DRV_H__ */