lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1397814309-32160-2-git-send-email-jjhiblot@traphandler.com>
Date:	Fri, 18 Apr 2014 11:45:07 +0200
From:	Jean-Jacques Hiblot <jjhiblot@...phandler.com>
To:	nicolas.ferre@...el.com, boris.brezillon@...e-electrons.com,
	linux-arm-kernel@...ts.infradead.org,
	dri-devel@...ts.freedesktop.org
Cc:	plagnioj@...osoft.com, airlied@...ux.ie, robdclark@...il.com,
	linux-kernel@...r.kernel.org,
	Jean-Jacques Hiblot <jjhiblot@...phandler.com>
Subject: [RFC 1/3] atmel: drm: added drm driver for the atmel hlcd controller

Signed-off-by: Jean-Jacques Hiblot <jjhiblot@...phandler.com>
---
 drivers/gpu/drm/Kconfig                            |   2 +
 drivers/gpu/drm/Makefile                           |   1 +
 drivers/gpu/drm/atmel_hlcdc/Kconfig                |  13 +
 drivers/gpu/drm/atmel_hlcdc/Makefile               |  12 +
 drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc.h          | 771 +++++++++++++++++++++
 .../gpu/drm/atmel_hlcdc/atmel_hlcdc_backlight.c    |  92 +++
 .../gpu/drm/atmel_hlcdc/atmel_hlcdc_backlight.h    |  25 +
 drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_crtc.c     | 702 +++++++++++++++++++
 drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_drv.c      | 586 ++++++++++++++++
 drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_drv.h      | 124 ++++
 drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_ovl.h      | 190 +++++
 drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_panel.c    | 459 ++++++++++++
 drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_panel.h    |  28 +
 13 files changed, 3005 insertions(+)
 create mode 100644 drivers/gpu/drm/atmel_hlcdc/Kconfig
 create mode 100644 drivers/gpu/drm/atmel_hlcdc/Makefile
 create mode 100644 drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc.h
 create mode 100644 drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_backlight.c
 create mode 100644 drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_backlight.h
 create mode 100644 drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_crtc.c
 create mode 100644 drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_drv.c
 create mode 100644 drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_drv.h
 create mode 100644 drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_ovl.h
 create mode 100644 drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_panel.c
 create mode 100644 drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_panel.h

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 8e7fa4d..13fec638 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -190,6 +190,8 @@ source "drivers/gpu/drm/omapdrm/Kconfig"
 
 source "drivers/gpu/drm/tilcdc/Kconfig"
 
+source "drivers/gpu/drm/atmel_hlcdc/Kconfig"
+
 source "drivers/gpu/drm/qxl/Kconfig"
 
 source "drivers/gpu/drm/bochs/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 292a79d..8245aa5 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -57,6 +57,7 @@ obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/
 obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
 obj-$(CONFIG_DRM_OMAP)	+= omapdrm/
 obj-$(CONFIG_DRM_TILCDC)	+= tilcdc/
+obj-$(CONFIG_DRM_ATMEL_HLCDC)	+= atmel_hlcdc/
 obj-$(CONFIG_DRM_QXL) += qxl/
 obj-$(CONFIG_DRM_BOCHS) += bochs/
 obj-$(CONFIG_DRM_MSM) += msm/
diff --git a/drivers/gpu/drm/atmel_hlcdc/Kconfig b/drivers/gpu/drm/atmel_hlcdc/Kconfig
new file mode 100644
index 0000000..6ee5989
--- /dev/null
+++ b/drivers/gpu/drm/atmel_hlcdc/Kconfig
@@ -0,0 +1,13 @@
+config DRM_ATMEL_HLCDC
+	tristate "DRM Support for ATMEL HLCDC Display Controller"
+	depends on DRM && OF && ARM
+	select DRM_KMS_HELPER
+	select DRM_KMS_FB_HELPER
+	select DRM_KMS_CMA_HELPER
+	select DRM_GEM_CMA_HELPER
+	select VIDEOMODE_HELPERS
+	select BACKLIGHT_CLASS_DEVICE
+	select BACKLIGHT_LCD_SUPPORT
+	help
+	  Choose this option if you have an ATMEL SoC with HLCDC display
+	  controller, for example SAMA5D36EK.
diff --git a/drivers/gpu/drm/atmel_hlcdc/Makefile b/drivers/gpu/drm/atmel_hlcdc/Makefile
new file mode 100644
index 0000000..4935c36
--- /dev/null
+++ b/drivers/gpu/drm/atmel_hlcdc/Makefile
@@ -0,0 +1,12 @@
+ccflags-y := -Iinclude/drm
+ifeq (, $(findstring -W,$(EXTRA_CFLAGS)))
+	ccflags-y += -Werror
+endif
+
+atmel_hlcdc-y := \
+	atmel_hlcdc_crtc.o \
+	atmel_hlcdc_panel.o \
+	atmel_hlcdc_backlight.o \
+	atmel_hlcdc_drv.o
+
+obj-$(CONFIG_DRM_ATMEL_HLCDC)	+= atmel_hlcdc.o
diff --git a/drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc.h b/drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc.h
new file mode 100644
index 0000000..8ed0767
--- /dev/null
+++ b/drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc.h
@@ -0,0 +1,771 @@
+/*
+ *  Header file for AT91 High end LCD Controller
+ *
+ *  Data structure and register user interface
+ *
+ *  Copyright (C) 2010 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef __ATMEL_HLCD_H__
+#define __ATMEL_HLCD_H__
+
+/* Lcdc hardware registers */
+#define ATMEL_LCDC_LCDCFG0	0x0000
+#define LCDC_LCDCFG0_CLKPOL		(0x1 << 0)
+#define LCDC_LCDCFG0_CLKSEL		(0x1 << 2)
+#define LCDC_LCDCFG0_CLKPWMSEL		(0x1 << 3)
+#define LCDC_LCDCFG0_CGDISBASE		(0x1 << 8)
+#define LCDC_LCDCFG0_CGDISOVR1		(0x1 << 9)
+#define LCDC_LCDCFG0_CGDISOVR2		(0x1 << 10)
+#define LCDC_LCDCFG0_CGDISHEO		(0x1 << 11)
+#define LCDC_LCDCFG0_CGDISHCR		(0x1 << 12)
+#define LCDC_LCDCFG0_CGDISPP		(0x1 << 13)
+#define LCDC_LCDCFG0_CLKDIV_OFFSET	16
+#define LCDC_LCDCFG0_CLKDIV		(0xff << LCDC_LCDCFG0_CLKDIV_OFFSET)
+
+#define ATMEL_LCDC_LCDCFG1	0x0004
+#define LCDC_LCDCFG1_HSPW_OFFSET	0
+#define LCDC_LCDCFG1_HSPW		(0x3f << LCDC_LCDCFG1_HSPW_OFFSET)
+#define LCDC_LCDCFG1_VSPW_OFFSET	16
+#define LCDC_LCDCFG1_VSPW		(0x3f << LCDC_LCDCFG1_VSPW_OFFSET)
+
+#define ATMEL_LCDC_LCDCFG2	0x0008
+#define LCDC_LCDCFG2_VFPW_OFFSET	0
+#define LCDC_LCDCFG2_VFPW		(0x3f << LCDC_LCDCFG2_VFPW_OFFSET)
+#define LCDC_LCDCFG2_VBPW_OFFSET	16
+#define LCDC_LCDCFG2_VBPW		(0x3f << LCDC_LCDCFG2_VBPW_OFFSET)
+
+#define ATMEL_LCDC_LCDCFG3	0x000C
+#define LCDC_LCDCFG3_HFPW_OFFSET	0
+#define LCDC_LCDCFG3_HFPW		(0xff << LCDC_LCDCFG3_HFPW_OFFSET)
+#define LCDC2_LCDCFG3_HFPW		(0x1ff << LCDC_LCDCFG3_HFPW_OFFSET)
+#define LCDC_LCDCFG3_HBPW_OFFSET	16
+#define LCDC_LCDCFG3_HBPW		(0xff << LCDC_LCDCFG3_HBPW_OFFSET)
+#define LCDC2_LCDCFG3_HBPW		(0x1ff << LCDC_LCDCFG3_HBPW_OFFSET)
+
+#define ATMEL_LCDC_LCDCFG4	0x0010
+#define LCDC_LCDCFG4_PPL_OFFSET		0
+#define LCDC_LCDCFG4_PPL		(0x7ff << LCDC_LCDCFG4_PPL_OFFSET)
+#define LCDC_LCDCFG4_RPF_OFFSET		16
+#define LCDC_LCDCFG4_RPF		(0x7ff << LCDC_LCDCFG4_RPF_OFFSET)
+
+#define ATMEL_LCDC_LCDCFG5	0x0014
+#define LCDC_LCDCFG5_HSPOL		(0x1 << 0)
+#define LCDC_LCDCFG5_VSPOL		(0x1 << 1)
+#define LCDC_LCDCFG5_VSPDLYS		(0x1 << 2)
+#define LCDC_LCDCFG5_VSPDLYE		(0x1 << 3)
+#define LCDC_LCDCFG5_DISPPOL		(0x1 << 4)
+#define LCDC_LCDCFG5_SERIAL		(0x1 << 5)
+#define LCDC_LCDCFG5_DITHER		(0x1 << 6)
+#define LCDC_LCDCFG5_DISPDLY		(0x1 << 7)
+#define LCDC_LCDCFG5_MODE_OFFSET	8
+#define LCDC_LCDCFG5_MODE		(0x3 << LCDC_LCDCFG5_MODE_OFFSET)
+#define		LCDC_LCDCFG5_MODE_OUTPUT_12BPP		(0x0 << 8)
+#define		LCDC_LCDCFG5_MODE_OUTPUT_16BPP		(0x1 << 8)
+#define		LCDC_LCDCFG5_MODE_OUTPUT_18BPP		(0x2 << 8)
+#define		LCDC_LCDCFG5_MODE_OUTPUT_24BPP		(0x3 << 8)
+#define LCDC_LCDCFG5_PP			(0x1 << 10)
+#define LCDC_LCDCFG5_VSPSU		(0x1 << 12)
+#define LCDC_LCDCFG5_VSPHO		(0x1 << 13)
+#define LCDC_LCDCFG5_GUARDTIME_OFFSET	16
+#define LCDC_LCDCFG5_GUARDTIME		(0x1f << LCDC_LCDCFG5_GUARDTIME_OFFSET)
+
+#define ATMEL_LCDC_LCDCFG6	0x0018
+#define LCDC_LCDCFG6_PWMPS_OFFSET	0
+#define LCDC_LCDCFG6_PWMPS		(0x7 << LCDC_LCDCFG6_PWMPS_OFFSET)
+#define LCDC_LCDCFG6_PWMPOL		(0x1 << 4)
+#define LCDC_LCDCFG6_PWMCVAL_OFFSET	8
+#define LCDC_LCDCFG6_PWMCVAL		(0xff << LCDC_LCDCFG6_PWMCVAL_OFFSET)
+
+#define ATMEL_LCDC_LCDEN	0x0020
+#define LCDC_LCDEN_CLKEN		(0x1 << 0)
+#define LCDC_LCDEN_SYNCEN		(0x1 << 1)
+#define LCDC_LCDEN_DISPEN		(0x1 << 2)
+#define LCDC_LCDEN_PWMEN		(0x1 << 3)
+
+#define ATMEL_LCDC_LCDDIS	0x0024
+#define LCDC_LCDDIS_CLKDIS		(0x1 << 0)
+#define LCDC_LCDDIS_SYNCDIS		(0x1 << 1)
+#define LCDC_LCDDIS_DISPDIS		(0x1 << 2)
+#define LCDC_LCDDIS_PWMDIS		(0x1 << 3)
+#define LCDC_LCDDIS_CLKRST		(0x1 << 8)
+#define LCDC_LCDDIS_SYNCRST		(0x1 << 9)
+#define LCDC_LCDDIS_DISPRST		(0x1 << 10)
+#define LCDC_LCDDIS_PWMRST		(0x1 << 11)
+
+#define ATMEL_LCDC_LCDSR	0x0028
+#define LCDC_LCDSR_CLKSTS		(0x1 << 0)
+#define LCDC_LCDSR_LCDSTS		(0x1 << 1)
+#define LCDC_LCDSR_DISPSTS		(0x1 << 2)
+#define LCDC_LCDSR_PWMSTS		(0x1 << 3)
+#define LCDC_LCDSR_SIPSTS		(0x1 << 4)
+
+#define ATMEL_LCDC_LCDIER	0x002C
+#define LCDC_LCDIER_SOFIE		(0x1 << 0)
+#define LCDC_LCDIER_DISIE		(0x1 << 1)
+#define LCDC_LCDIER_DISPIE		(0x1 << 2)
+#define LCDC_LCDIER_FIFOERRIE		(0x1 << 4)
+#define LCDC_LCDIER_BASEIE		(0x1 << 8)
+#define LCDC_LCDIER_OVR1IE		(0x1 << 9)
+#define LCDC_LCDIER_OVR2IE		(0x1 << 10)
+#define LCDC_LCDIER_HEOIE		(0x1 << 11)
+#define LCDC_LCDIER_HCRIE		(0x1 << 12)
+#define LCDC_LCDIER_PPIE		(0x1 << 13)
+
+#define ATMEL_LCDC_LCDIDR	0x0030
+#define LCDC_LCDIDR_SOFID		(0x1 << 0)
+#define LCDC_LCDIDR_DISID		(0x1 << 1)
+#define LCDC_LCDIDR_DISPID		(0x1 << 2)
+#define LCDC_LCDIDR_FIFOERRID		(0x1 << 4)
+#define LCDC_LCDIDR_BASEID		(0x1 << 8)
+#define LCDC_LCDIDR_OVR1ID		(0x1 << 9)
+#define LCDC_LCDIDR_OVR2ID		(0x1 << 10)
+#define LCDC_LCDIDR_HEOID		(0x1 << 11)
+#define LCDC_LCDIDR_HCRID		(0x1 << 12)
+#define LCDC_LCDIDR_PPID		(0x1 << 13)
+
+#define ATMEL_LCDC_LCDIMR	0x0034
+#define LCDC_LCDIMR_SOFIM		(0x1 << 0)
+#define LCDC_LCDIMR_DISIM		(0x1 << 1)
+#define LCDC_LCDIMR_DISPIM		(0x1 << 2)
+#define LCDC_LCDIMR_FIFOERRIM		(0x1 << 4)
+#define LCDC_LCDIMR_BASEIM		(0x1 << 8)
+#define LCDC_LCDIMR_OVR1IM		(0x1 << 9)
+#define LCDC_LCDIMR_OVR2IM		(0x1 << 10)
+#define LCDC_LCDIMR_HEOIM		(0x1 << 11)
+#define LCDC_LCDIMR_HCRIM		(0x1 << 12)
+#define LCDC_LCDIMR_PPIM		(0x1 << 13)
+
+#define ATMEL_LCDC_LCDISR	0x0038
+#define LCDC_LCDISR_SOF			(0x1 << 0)
+#define LCDC_LCDISR_DIS			(0x1 << 1)
+#define LCDC_LCDISR_DISP		(0x1 << 2)
+#define LCDC_LCDISR_FIFOERR		(0x1 << 4)
+#define LCDC_LCDISR_BASE		(0x1 << 8)
+#define LCDC_LCDISR_OVR1		(0x1 << 9)
+#define LCDC_LCDISR_OVR2		(0x1 << 10)
+#define LCDC_LCDISR_HEO			(0x1 << 11)
+#define LCDC_LCDISR_HCR			(0x1 << 12)
+#define LCDC_LCDISR_PP			(0x1 << 13)
+
+#define ATMEL_LCDC_BASECHER	0x0040
+#define LCDC_BASECHER_CHEN		(0x1 << 0)
+#define LCDC_BASECHER_UPDATEEN		(0x1 << 1)
+#define LCDC_BASECHER_A2QEN		(0x1 << 2)
+
+#define ATMEL_LCDC_BASECHDR	0x0044
+#define LCDC_BASECHDR_CHDIS		(0x1 << 0)
+#define LCDC_BASECHDR_CHRST		(0x1 << 8)
+
+#define ATMEL_LCDC_BASECHSR	0x0048
+#define LCDC_BASECHSR_CHSR		(0x1 << 0)
+#define LCDC_BASECHSR_UPDATESR		(0x1 << 1)
+#define LCDC_BASECHSR_A2QSR		(0x1 << 2)
+
+#define ATMEL_LCDC_BASEIER	0x004C
+#define LCDC_BASEIER_DMA		(0x1 << 2)
+#define LCDC_BASEIER_DSCR		(0x1 << 3)
+#define LCDC_BASEIER_ADD		(0x1 << 4)
+#define LCDC_BASEIER_DONE		(0x1 << 5)
+#define LCDC_BASEIER_OVR		(0x1 << 6)
+
+#define ATMEL_LCDC_BASEIDR	0x0050
+#define LCDC_BASEIDR_DMA		(0x1 << 2)
+#define LCDC_BASEIDR_DSCR		(0x1 << 3)
+#define LCDC_BASEIDR_ADD		(0x1 << 4)
+#define LCDC_BASEIDR_DONE		(0x1 << 5)
+#define LCDC_BASEIDR_OVR		(0x1 << 6)
+
+#define ATMEL_LCDC_BASEIMR	0x0054
+#define LCDC_BASEIMR_DMA		(0x1 << 2)
+#define LCDC_BASEIMR_DSCR		(0x1 << 3)
+#define LCDC_BASEIMR_ADD		(0x1 << 4)
+#define LCDC_BASEIMR_DONE		(0x1 << 5)
+#define LCDC_BASEIMR_OVR		(0x1 << 6)
+
+#define ATMEL_LCDC_BASEISR	0x0058
+#define LCDC_BASEISR_DMA		(0x1 << 2)
+#define LCDC_BASEISR_DSCR		(0x1 << 3)
+#define LCDC_BASEISR_ADD		(0x1 << 4)
+#define LCDC_BASEISR_DONE		(0x1 << 5)
+#define LCDC_BASEISR_OVR		(0x1 << 6)
+
+#define ATMEL_LCDC_BASEHEAD	0x005C
+
+#define ATMEL_LCDC_BASEADDR	0x0060
+
+#define ATMEL_LCDC_BASECTRL	0x0064
+#define LCDC_BASECTRL_DFETCH		(0x1 << 0)
+#define LCDC_BASECTRL_LFETCH		(0x1 << 1)
+#define LCDC_BASECTRL_DMAIEN		(0x1 << 2)
+#define LCDC_BASECTRL_DSCRIEN		(0x1 << 3)
+#define LCDC_BASECTRL_ADDIEN		(0x1 << 4)
+#define LCDC_BASECTRL_DONEIEN		(0x1 << 5)
+
+#define ATMEL_LCDC_BASENEXT	0x0068
+
+#define ATMEL_LCDC_BASECFG0	0x006C
+#define LCDC_BASECFG0_SIF		(0x1 << 0)
+#define LCDC_BASECFG0_BLEN_OFFSET 4
+#define LCDC_BASECFG0_BLEN		(0x3 << LCDC_BASECFG0_BLEN_OFFSET)
+#define		LCDC_BASECFG0_BLEN_AHB_SINGLE		(0x0 << 4)
+#define		LCDC_BASECFG0_BLEN_AHB_INCR4		(0x1 << 4)
+#define		LCDC_BASECFG0_BLEN_AHB_INCR8		(0x2 << 4)
+#define		LCDC_BASECFG0_BLEN_AHB_INCR16		(0x3 << 4)
+#define LCDC_BASECFG0_DLBO		(0x1 << 8)
+
+#define ATMEL_LCDC_BASECFG1	0x0070
+#define LCDC_BASECFG1_CLUTEN		(0x1 << 0)
+#define LCDC_BASECFG1_RGBMODE_OFFSET	4
+#define LCDC_BASECFG1_RGBMODE		(0xf << LCDC_BASECFG1_RGBMODE_OFFSET)
+#define		LCDC_BASECFG1_RGBMODE_12BPP_RGB_444	(0x0 << 4)
+#define		LCDC_BASECFG1_RGBMODE_16BPP_ARGB_4444	(0x1 << 4)
+#define		LCDC_BASECFG1_RGBMODE_16BPP_RGBA_4444	(0x2 << 4)
+#define		LCDC_BASECFG1_RGBMODE_16BPP_RGB_565	(0x3 << 4)
+#define		LCDC_BASECFG1_RGBMODE_16BPP_TRGB_1555	(0x4 << 4)
+#define		LCDC_BASECFG1_RGBMODE_18BPP_RGB_666	(0x5 << 4)
+#define		LCDC_BASECFG1_RGBMODE_18BPP_RGB_666_PACKED	(0x6 << 4)
+#define		LCDC_BASECFG1_RGBMODE_19BPP_TRGB_1666	(0x7 << 4)
+#define		LCDC_BASECFG1_RGBMODE_19BPP_TRGB_PACKED	(0x8 << 4)
+#define		LCDC_BASECFG1_RGBMODE_24BPP_RGB_888	(0x9 << 4)
+#define		LCDC_BASECFG1_RGBMODE_24BPP_RGB_888_PACKED	(0xA << 4)
+#define		LCDC_BASECFG1_RGBMODE_25BPP_TRGB_1888	(0xB << 4)
+#define		LCDC_BASECFG1_RGBMODE_32BPP_ARGB_8888	(0xC << 4)
+#define		LCDC_BASECFG1_RGBMODE_32BPP_RGBA_8888	(0xD << 4)
+#define LCDC_BASECFG1_CLUTMODE_OFFSET	8
+#define LCDC_BASECFG1_CLUTMODE		(0x3 << LCDC_BASECFG1_CLUTMODE_OFFSET)
+#define		LCDC_BASECFG1_CLUTMODE_1BPP		(0x0 << 8)
+#define		LCDC_BASECFG1_CLUTMODE_2BPP		(0x1 << 8)
+#define		LCDC_BASECFG1_CLUTMODE_4BPP		(0x2 << 8)
+#define		LCDC_BASECFG1_CLUTMODE_8BPP		(0x3 << 8)
+
+#define ATMEL_LCDC_BASECFG2	0x0074
+
+#define ATMEL_LCDC_BASECFG3	0x0078
+#define LCDC_BASECFG3_BDEF_OFFSET	0
+#define LCDC_BASECFG3_BDEF		(0xff << LCDC_BASECFG3_BDEF_OFFSET)
+#define LCDC_BASECFG3_GDEF_OFFSET	8
+#define LCDC_BASECFG3_GDEF		(0xff << LCDC_BASECFG3_GDEF_OFFSET)
+#define LCDC_BASECFG3_RDEF_OFFSET	16
+#define LCDC_BASECFG3_RDEF		(0xff << LCDC_BASECFG3_RDEF_OFFSET)
+
+#define ATMEL_LCDC_BASECFG4	0x007C
+#define LCDC_BASECFG4_DMA		(0x1 << 8)
+#define LCDC_BASECFG4_REP		(0x1 << 9)
+#define LCDC_BASECFG4_DISCEN		(0x1 << 11)
+
+#define ATMEL_LCDC_BASECFG5	0x0080
+#define LCDC_BASECFG5_DISCXPOS_OFFSET	0
+#define LCDC_BASECFG5_DISCXPOS		(0x7ff << LCDC_BASECFG5_DISCXPOS_OFFSET)
+#define LCDC_BASECFG5_DISCYPOS_OFFSET	16
+#define LCDC_BASECFG5_DISCYPOS		(0x7ff << LCDC_BASECFG5_DISCYPOS_OFFSET)
+
+#define ATMEL_LCDC_BASECFG6	0x0084
+#define LCDC_BASECFG6_DISCXSIZE_OFFSET	0
+#define LCDC_BASECFG6_DISCXSIZE       (0x7ff << LCDC_BASECFG6_DISCXSIZE_OFFSET)
+#define LCDC_BASECFG6_DISCYSIZE_OFFSET	16
+#define LCDC_BASECFG6_DISCYSIZE       (0x7ff << LCDC_BASECFG6_DISCYSIZE_OFFSET)
+
+#define ATMEL_LCDC_HEOCHER	0x0280
+#define ATMEL_LCDC2_HEOCHER	0x0340
+#define LCDC_HEOCHER_CHEN		(0x1 << 0)
+#define LCDC_HEOCHER_UPDATEEN		(0x1 << 1)
+#define LCDC_HEOCHER_A2QEN		(0x1 << 2)
+
+#define ATMEL_LCDC_HEOCHDR	0x0284
+#define LCDC_HEOCHDR_CHDIS		(0x1 << 0)
+#define LCDC_HEOCHDR_CHRST		(0x1 << 8)
+
+#define ATMEL_LCDC_HEOCHSR	0x0288
+#define LCDC_HEOCHSR_CHSR		(0x1 << 0)
+#define LCDC_HEOCHSR_UPDATESR		(0x1 << 1)
+#define LCDC_HEOCHSR_A2QSR		(0x1 << 2)
+
+#define ATMEL_LCDC_HEOIER	0x028C
+#define LCDC_HEOIER_DMA			(0x1 << 2)
+#define LCDC_HEOIER_DSCR		(0x1 << 3)
+#define LCDC_HEOIER_ADD			(0x1 << 4)
+#define LCDC_HEOIER_DONE		(0x1 << 5)
+#define LCDC_HEOIER_OVR			(0x1 << 6)
+#define LCDC_HEOIER_UDMA		(0x1 << 10)
+#define LCDC_HEOIER_UDSCR		(0x1 << 11)
+#define LCDC_HEOIER_UADD		(0x1 << 12)
+#define LCDC_HEOIER_UDONE		(0x1 << 13)
+#define LCDC_HEOIER_UOVR		(0x1 << 14)
+#define LCDC_HEOIER_VDMA		(0x1 << 18)
+#define LCDC_HEOIER_VDSCR		(0x1 << 19)
+#define LCDC_HEOIER_VADD		(0x1 << 20)
+#define LCDC_HEOIER_VDONE		(0x1 << 21)
+#define LCDC_HEOIER_VOVR		(0x1 << 22)
+
+#define ATMEL_LCDC_HEOIDR	0x0290
+#define LCDC_HEOIDR_DMA			(0x1 << 2)
+#define LCDC_HEOIDR_DSCR		(0x1 << 3)
+#define LCDC_HEOIDR_ADD			(0x1 << 4)
+#define LCDC_HEOIDR_DONE		(0x1 << 5)
+#define LCDC_HEOIDR_OVR			(0x1 << 6)
+#define LCDC_HEOIDR_UDMA		(0x1 << 10)
+#define LCDC_HEOIDR_UDSCR		(0x1 << 11)
+#define LCDC_HEOIDR_UADD		(0x1 << 12)
+#define LCDC_HEOIDR_UDONE		(0x1 << 13)
+#define LCDC_HEOIDR_UOVR		(0x1 << 14)
+#define LCDC_HEOIDR_VDMA		(0x1 << 18)
+#define LCDC_HEOIDR_VDSCR		(0x1 << 19)
+#define LCDC_HEOIDR_VADD		(0x1 << 20)
+#define LCDC_HEOIDR_VDONE		(0x1 << 21)
+#define LCDC_HEOIDR_VOVR		(0x1 << 22)
+
+#define ATMEL_LCDC_HEOIMR	0x0294
+#define LCDC_HEOIMR_DMA			(0x1 << 2)
+#define LCDC_HEOIMR_DSCR		(0x1 << 3)
+#define LCDC_HEOIMR_ADD			(0x1 << 4)
+#define LCDC_HEOIMR_DONE		(0x1 << 5)
+#define LCDC_HEOIMR_OVR			(0x1 << 6)
+#define LCDC_HEOIMR_UDMA		(0x1 << 10)
+#define LCDC_HEOIMR_UDSCR		(0x1 << 11)
+#define LCDC_HEOIMR_UADD		(0x1 << 12)
+#define LCDC_HEOIMR_UDONE		(0x1 << 13)
+#define LCDC_HEOIMR_UOVR		(0x1 << 14)
+#define LCDC_HEOIMR_VDMA		(0x1 << 18)
+#define LCDC_HEOIMR_VDSCR		(0x1 << 19)
+#define LCDC_HEOIMR_VADD		(0x1 << 20)
+#define LCDC_HEOIMR_VDONE		(0x1 << 21)
+#define LCDC_HEOIMR_VOVR		(0x1 << 22)
+
+#define ATMEL_LCDC_HEOISR	0x0298
+#define LCDC_HEOISR_DMA			(0x1 << 2)
+#define LCDC_HEOISR_DSCR		(0x1 << 3)
+#define LCDC_HEOISR_ADD			(0x1 << 4)
+#define LCDC_HEOISR_DONE		(0x1 << 5)
+#define LCDC_HEOISR_OVR			(0x1 << 6)
+#define LCDC_HEOISR_UDMA		(0x1 << 10)
+#define LCDC_HEOISR_UDSCR		(0x1 << 11)
+#define LCDC_HEOISR_UADD		(0x1 << 12)
+#define LCDC_HEOISR_UDONE		(0x1 << 13)
+#define LCDC_HEOISR_UOVR		(0x1 << 14)
+#define LCDC_HEOISR_VDMA		(0x1 << 18)
+#define LCDC_HEOISR_VDSCR		(0x1 << 19)
+#define LCDC_HEOISR_VADD		(0x1 << 20)
+#define LCDC_HEOISR_VDONE		(0x1 << 21)
+#define LCDC_HEOISR_VOVR		(0x1 << 22)
+
+#define ATMEL_LCDC_HEOHEAD	0x029C
+
+#define ATMEL_LCDC_HEOADDR	0x02A0
+
+#define ATMEL_LCDC_HEOCTRL	0x02A4
+#define LCDC_HEOCTRL_DFETCH		(0x1 << 0)
+#define LCDC_HEOCTRL_LFETCH		(0x1 << 1)
+#define LCDC_HEOCTRL_DMAIEN		(0x1 << 2)
+#define LCDC_HEOCTRL_DSCRIEN		(0x1 << 3)
+#define LCDC_HEOCTRL_ADDIEN		(0x1 << 4)
+#define LCDC_HEOCTRL_DONEIEN		(0x1 << 5)
+
+#define ATMEL_LCDC_HEONEXT	0x02A8
+
+#define ATMEL_LCDC_HEOUHEAD	0x02AC
+
+#define ATMEL_LCDC_HEOUADDR	0x02B0
+
+#define ATMEL_LCDC_HEOUCTRL	0x02B4
+#define LCDC_HEOUCTRL_UDFETCH		(0x1 << 0)
+#define LCDC_HEOUCTRL_UDMAIEN		(0x1 << 2)
+#define LCDC_HEOUCTRL_UDSCRIEN		(0x1 << 3)
+#define LCDC_HEOUCTRL_UADDIEN		(0x1 << 4)
+#define LCDC_HEOUCTRL_UDONEIEN		(0x1 << 5)
+
+#define ATMEL_LCDC_HEOUNEXT	0x02B8
+
+#define ATMEL_LCDC_HEOVHEAD	0x02BC
+
+#define ATMEL_LCDC_HEOVADDR	0x02C0
+
+#define ATMEL_LCDC_HEOVCTRL	0x02C4
+#define LCDC_HEOVCTRL_VDFETCH		(0x1 << 0)
+#define LCDC_HEOVCTRL_VDMAIEN		(0x1 << 2)
+#define LCDC_HEOVCTRL_VDSCRIEN		(0x1 << 3)
+#define LCDC_HEOVCTRL_VADDIEN		(0x1 << 4)
+#define LCDC_HEOVCTRL_VDONEIEN		(0x1 << 5)
+
+#define ATMEL_LCDC_HEOVNEXT	0x02C8
+
+#define ATMEL_LCDC_HEOCFG0	0x02CC
+#define LCDC_HEOCFG0_BLEN_OFFSET	4
+#define LCDC_HEOCFG0_BLEN		(0x3 << LCDC_HEOCFG0_BLEN_OFFSET)
+#define		LCDC_HEOCFG0_BLEN_AHB_SINGLE		(0x0 << 4)
+#define		LCDC_HEOCFG0_BLEN_AHB_INCR4		(0x1 << 4)
+#define		LCDC_HEOCFG0_BLEN_AHB_INCR8		(0x2 << 4)
+#define		LCDC_HEOCFG0_BLEN_AHB_INCR16		(0x3 << 4)
+#define LCDC_HEOCFG0_BLENUV_OFFSET	6
+#define LCDC_HEOCFG0_BLENUV		(0x3 << LCDC_HEOCFG0_BLENUV_OFFSET)
+#define		LCDC_HEOCFG0_BLENUV_AHB_SINGLE		(0x0 << 6)
+#define		LCDC_HEOCFG0_BLENUV_AHB_INCR4		(0x1 << 6)
+#define		LCDC_HEOCFG0_BLENUV_AHB_INCR8		(0x2 << 6)
+#define		LCDC_HEOCFG0_BLENUV_AHB_INCR16		(0x3 << 6)
+#define LCDC_HEOCFG0_DLBO		(0x1 << 8)
+#define LCDC_HEOCFG0_ROTDIS		(0x1 << 12)
+#define LCDC_HEOCFG0_LOCKDIS		(0x1 << 13)
+
+#define ATMEL_LCDC_HEOCFG1	0x02D0
+#define LCDC_HEOCFG1_CLUTEN		(0x1 << 0)
+#define LCDC_HEOCFG1_YUVEN		(0x1 << 1)
+#define LCDC_HEOCFG1_RGBMODE_OFFSET	4
+#define LCDC_HEOCFG1_RGBMODE		(0xf << LCDC_HEOCFG1_RGBMODE_OFFSET)
+#define		LCDC_HEOCFG1_RGBMODE_12BPP_RGB_444	(0x0 << 4)
+#define		LCDC_HEOCFG1_RGBMODE_16BPP_ARGB_4444	(0x1 << 4)
+#define		LCDC_HEOCFG1_RGBMODE_16BPP_RGBA_4444	(0x2 << 4)
+#define		LCDC_HEOCFG1_RGBMODE_16BPP_RGB_565	(0x3 << 4)
+#define		LCDC_HEOCFG1_RGBMODE_16BPP_TRGB_1555	(0x4 << 4)
+#define		LCDC_HEOCFG1_RGBMODE_18BPP_RGB_666	(0x5 << 4)
+#define		LCDC_HEOCFG1_RGBMODE_18BPP_RGB_666_PACKED	(0x6 << 4)
+#define		LCDC_HEOCFG1_RGBMODE_19BPP_TRGB_1666	(0x7 << 4)
+#define		LCDC_HEOCFG1_RGBMODE_19BPP_TRGB_PACKED	(0x8 << 4)
+#define		LCDC_HEOCFG1_RGBMODE_24BPP_RGB_888	(0x9 << 4)
+#define		LCDC_HEOCFG1_RGBMODE_24BPP_RGB_888_PACKED	(0xA << 4)
+#define		LCDC_HEOCFG1_RGBMODE_25BPP_TRGB_1888	(0xB << 4)
+#define		LCDC_HEOCFG1_RGBMODE_32BPP_ARGB_8888	(0xC << 4)
+#define		LCDC_HEOCFG1_RGBMODE_32BPP_RGBA_8888	(0xD << 4)
+#define LCDC_HEOCFG1_CLUTMODE_OFFSET	8
+#define LCDC_HEOCFG1_CLUTMODE		(0x3 << LCDC_HEOCFG1_CLUTMODE_OFFSET)
+#define		LCDC_HEOCFG1_CLUTMODE_1BPP		(0x0 << 8)
+#define		LCDC_HEOCFG1_CLUTMODE_2BPP		(0x1 << 8)
+#define		LCDC_HEOCFG1_CLUTMODE_4BPP		(0x2 << 8)
+#define		LCDC_HEOCFG1_CLUTMODE_8BPP		(0x3 << 8)
+#define LCDC_HEOCFG1_YUVMODE_OFFSET	12
+#define LCDC_HEOCFG1_YUVMODE		(0xf << LCDC_HEOCFG1_YUVMODE_OFFSET)
+#define		LCDC_HEOCFG1_YUVMODE_32BPP_AYCBCR	(0x0 << 12)
+#define		LCDC_HEOCFG1_YUVMODE_16BPP_YCBCR_MODE0	(0x1 << 12)
+#define		LCDC_HEOCFG1_YUVMODE_16BPP_YCBCR_MODE1	(0x2 << 12)
+#define		LCDC_HEOCFG1_YUVMODE_16BPP_YCBCR_MODE2	(0x3 << 12)
+#define		LCDC_HEOCFG1_YUVMODE_16BPP_YCBCR_MODE3	(0x4 << 12)
+#define		LCDC_HEOCFG1_YUVMODE_16BPP_YCBCR_SEMIPLANAR	(0x5 << 12)
+#define		LCDC_HEOCFG1_YUVMODE_16BPP_YCBCR_PLANAR	(0x6 << 12)
+#define		LCDC_HEOCFG1_YUVMODE_12BPP_YCBCR_SEMIPLANAR	(0x7 << 12)
+#define		LCDC_HEOCFG1_YUVMODE_12BPP_YCBCR_PLANAR	(0x8 << 12)
+#define LCDC_HEOCFG1_YUV422ROT		(0x1 << 16)
+#define LCDC_HEOCFG1_YUV422SWP		(0x1 << 17)
+
+#define ATMEL_LCDC_HEOCFG2	0x02D4
+#define LCDC_HEOCFG2_XOFFSET_OFFSET	0
+#define LCDC_HEOCFG2_XOFFSET		(0x7ff << LCDC_HEOCFG2_XOFFSET_OFFSET)
+#define LCDC_HEOCFG2_YOFFSET_OFFSET	16
+#define LCDC_HEOCFG2_YOFFSET		(0x7ff << LCDC_HEOCFG2_YOFFSET_OFFSET)
+
+#define ATMEL_LCDC_HEOCFG3	0x02D8
+#define LCDC_HEOCFG3_XSIZE_OFFSET	0
+#define LCDC_HEOCFG3_XSIZE		(0x7ff << LCDC_HEOCFG3_XSIZE_OFFSET)
+#define LCDC_HEOCFG3_YSIZE_OFFSET	16
+#define LCDC_HEOCFG3_YSIZE		(0x7ff << LCDC_HEOCFG3_YSIZE_OFFSET)
+
+#define ATMEL_LCDC_HEOCFG4	0x02DC
+#define LCDC_HEOCFG4_XMEM_SIZE_OFFSET	0
+#define LCDC_HEOCFG4_XMEM_SIZE		(0x7ff << LCDC_HEOCFG4_XMEM_SIZE_OFFSET)
+#define LCDC_HEOCFG4_YMEM_SIZE_OFFSET	16
+#define LCDC_HEOCFG4_YMEM_SIZE		(0x7ff << LCDC_HEOCFG4_YMEM_SIZE_OFFSET)
+
+#define ATMEL_LCDC_HEOCFG5	0x02E0
+
+#define ATMEL_LCDC_HEOCFG6	0x02E4
+
+#define ATMEL_LCDC_HEOCFG7	0x02E8
+
+#define ATMEL_LCDC_HEOCFG8	0x02EC
+
+#define ATMEL_LCDC_HEOCFG9	0x02F0
+#define LCDC_HEOCFG9_BDEF_OFFSET	0
+#define LCDC_HEOCFG9_BDEF		(0xff << LCDC_HEOCFG9_BDEF_OFFSET)
+#define LCDC_HEOCFG9_GDEF_OFFSET	8
+#define LCDC_HEOCFG9_GDEF		(0xff << LCDC_HEOCFG9_GDEF_OFFSET)
+#define LCDC_HEOCFG9_RDEF_OFFSET	16
+#define LCDC_HEOCFG9_RDEF		(0xff << LCDC_HEOCFG9_RDEF_OFFSET)
+
+#define ATMEL_LCDC_HEOCFG10	0x02F4
+#define LCDC_HEOCFG10_BKEY_OFFSET	0
+#define LCDC_HEOCFG10_BKEY		(0xff << LCDC_HEOCFG10_BKEY_OFFSET)
+#define LCDC_HEOCFG10_GKEY_OFFSET	8
+#define LCDC_HEOCFG10_GKEY		(0xff << LCDC_HEOCFG10_GKEY_OFFSET)
+#define LCDC_HEOCFG10_RKEY_OFFSET	16
+#define LCDC_HEOCFG10_RKEY		(0xff << LCDC_HEOCFG10_RKEY_OFFSET)
+
+#define ATMEL_LCDC_HEOCFG11	0x02F8
+#define LCDC_HEOCFG11_BMASK_OFFSET	0
+#define LCDC_HEOCFG11_BMASK		(0xff << LCDC_HEOCFG11_BMASK_OFFSET)
+#define LCDC_HEOCFG11_GMASK_OFFSET	8
+#define LCDC_HEOCFG11_GMASK		(0xff << LCDC_HEOCFG11_GMASK_OFFSET)
+#define LCDC_HEOCFG11_RMASK_OFFSET	16
+#define LCDC_HEOCFG11_RMASK		(0xff << LCDC_HEOCFG11_RMASK_OFFSET)
+
+#define ATMEL_LCDC_HEOCFG12	0x02FC
+#define LCDC_HEOCFG12_CRKEY		(0x1 << 0)
+#define LCDC_HEOCFG12_INV		(0x1 << 1)
+#define LCDC_HEOCFG12_ITER2BL		(0x1 << 2)
+#define LCDC_HEOCFG12_ITER		(0x1 << 3)
+#define LCDC_HEOCFG12_REVALPHA		(0x1 << 4)
+#define LCDC_HEOCFG12_GAEN		(0x1 << 5)
+#define LCDC_HEOCFG12_LAEN		(0x1 << 6)
+#define LCDC_HEOCFG12_OVR		(0x1 << 7)
+#define LCDC_HEOCFG12_DMA		(0x1 << 8)
+#define LCDC_HEOCFG12_REP		(0x1 << 9)
+#define LCDC_HEOCFG12_DSTKEY		(0x1 << 10)
+#define LCDC_HEOCFG12_VIDPRI		(0x1 << 12)
+#define LCDC_HEOCFG12_GA_OFFSET		16
+#define LCDC_HEOCFG12_GA		(0xff << LCDC_HEOCFG12_GA_OFFSET)
+
+#define ATMEL_LCDC_HEOCFG13	0x0300
+#define LCDC_HEOCFG13_XFACTOR_OFFSET	0
+#define LCDC_HEOCFG13_XFACTOR		(0x1fff << LCDC_HEOCFG13_XFACTOR_OFFSET)
+#define LCDC_HEOCFG13_YFACTOR_OFFSET	16
+#define LCDC_HEOCFG13_YFACTOR		(0x1fff << LCDC_HEOCFG13_YFACTOR_OFFSET)
+#define LCDC_HEOCFG13_SCALEN		(0x1 << 31)
+
+#define ATMEL_LCDC_HEOCFG14	0x0304
+#define LCDC_HEOCFG14_CSCRY_OFFSET	0
+#define LCDC_HEOCFG14_CSCRY		(0x3ff << LCDC_HEOCFG14_CSCRY_OFFSET)
+#define LCDC_HEOCFG14_CSCRU_OFFSET	10
+#define LCDC_HEOCFG14_CSCRU		(0x3ff << LCDC_HEOCFG14_CSCRU_OFFSET)
+#define LCDC_HEOCFG14_CSCRV_OFFSET	20
+#define LCDC_HEOCFG14_CSCRV		(0x3ff << LCDC_HEOCFG14_CSCRV_OFFSET)
+#define LCDC_HEOCFG14_CSCYOFF		(0x1 << 30)
+
+#define ATMEL_LCDC_HEOCFG15	0x0308
+#define LCDC_HEOCFG15_CSCGY_OFFSET	0
+#define LCDC_HEOCFG15_CSCGY		(0x3ff << LCDC_HEOCFG15_CSCGY_OFFSET)
+#define LCDC_HEOCFG15_CSCGU_OFFSET	10
+#define LCDC_HEOCFG15_CSCGU		(0x3ff << LCDC_HEOCFG15_CSCGU_OFFSET)
+#define LCDC_HEOCFG15_CSCGV_OFFSET	20
+#define LCDC_HEOCFG15_CSCGV		(0x3ff << LCDC_HEOCFG15_CSCGV_OFFSET)
+#define LCDC_HEOCFG15_CSCUOFF		(0x1 << 30)
+
+#define ATMEL_LCDC_HEOCFG16	0x030C
+#define LCDC_HEOCFG16_CSCBY_OFFSET	0
+#define LCDC_HEOCFG16_CSCBY		(0x3ff << LCDC_HEOCFG16_CSCBY_OFFSET)
+#define LCDC_HEOCFG16_CSCBU_OFFSET	10
+#define LCDC_HEOCFG16_CSCBU		(0x3ff << LCDC_HEOCFG16_CSCBU_OFFSET)
+#define LCDC_HEOCFG16_CSCBV_OFFSET	20
+#define LCDC_HEOCFG16_CSCBV		(0x3ff << LCDC_HEOCFG16_CSCBV_OFFSET)
+#define LCDC_HEOCFG16_CSCVOFF		(0x1 << 30)
+
+#define ATMEL_LCDC_HCRCHER	0x0340
+#define LCDC_HCRCHER_CHEN		(0x1 << 0)
+#define LCDC_HCRCHER_UPDATEEN		(0x1 << 1)
+#define LCDC_HCRCHER_A2QEN		(0x1 << 2)
+
+#define ATMEL_LCDC_HCRCHDR	0x0344
+#define LCDC_HCRCHDR_CHDIS		(0x1 << 0)
+#define LCDC_HCRCHDR_CHRST		(0x1 << 8)
+
+#define ATMEL_LCDC_HCRCHSR	0x0348
+#define LCDC_HCRCHSR_CHSR		(0x1 << 0)
+#define LCDC_HCRCHSR_UPDATESR		(0x1 << 1)
+#define LCDC_HCRCHSR_A2QSR		(0x1 << 2)
+
+#define ATMEL_LCDC_HCRIER	0x034C
+#define LCDC_HCRIER_DMA			(0x1 << 2)
+#define LCDC_HCRIER_DSCR		(0x1 << 3)
+#define LCDC_HCRIER_ADD			(0x1 << 4)
+#define LCDC_HCRIER_DONE		(0x1 << 5)
+#define LCDC_HCRIER_OVR			(0x1 << 6)
+
+#define ATMEL_LCDC_HCRIDR	0x0350
+#define LCDC_HCRIDR_DMA			(0x1 << 2)
+#define LCDC_HCRIDR_DSCR		(0x1 << 3)
+#define LCDC_HCRIDR_ADD			(0x1 << 4)
+#define LCDC_HCRIDR_DONE		(0x1 << 5)
+#define LCDC_HCRIDR_OVR			(0x1 << 6)
+
+#define ATMEL_LCDC_HCRIMR	0x0354
+#define LCDC_HCRIMR_DMA			(0x1 << 2)
+#define LCDC_HCRIMR_DSCR		(0x1 << 3)
+#define LCDC_HCRIMR_ADD			(0x1 << 4)
+#define LCDC_HCRIMR_DONE		(0x1 << 5)
+#define LCDC_HCRIMR_OVR			(0x1 << 6)
+
+#define ATMEL_LCDC_HCRISR	0x0358
+#define LCDC_HCRISR_DMA			(0x1 << 2)
+#define LCDC_HCRISR_DSCR		(0x1 << 3)
+#define LCDC_HCRISR_ADD			(0x1 << 4)
+#define LCDC_HCRISR_DONE		(0x1 << 5)
+#define LCDC_HCRISR_OVR			(0x1 << 6)
+
+#define ATMEL_LCDC_HCRHEAD	0x035C
+
+#define ATMEL_LCDC_HCRADDR	0x0360
+
+#define ATMEL_LCDC_HCRCTRL	0x0364
+#define LCDC_HCRCTRL_DFETCH		(0x1 << 0)
+#define LCDC_HCRCTRL_LFETCH		(0x1 << 1)
+#define LCDC_HCRCTRL_DMAIEN		(0x1 << 2)
+#define LCDC_HCRCTRL_DSCRIEN		(0x1 << 3)
+#define LCDC_HCRCTRL_ADDIEN		(0x1 << 4)
+#define LCDC_HCRCTRL_DONEIEN		(0x1 << 5)
+
+#define ATMEL_LCDC_HCRNEXT	0x0368
+
+#define ATMEL_LCDC_HCRCFG0	0x036C
+#define LCDC_HCRCFG0_BLEN_OFFSET	4
+#define LCDC_HCRCFG0_BLEN		(0x3 << LCDC_HCRCFG0_BLEN_OFFSET)
+#define		LCDC_HCRCFG0_BLEN_AHB_SINGLE		(0x0 << 4)
+#define		LCDC_HCRCFG0_BLEN_AHB_INCR4		(0x1 << 4)
+#define		LCDC_HCRCFG0_BLEN_AHB_INCR8		(0x2 << 4)
+#define		LCDC_HCRCFG0_BLEN_AHB_INCR16		(0x3 << 4)
+#define LCDC_HCRCFG0_DLBO		(0x1 << 8)
+
+#define ATMEL_LCDC_HCRCFG1	0x0370
+#define LCDC_HCRCFG1_CLUTEN		(0x1 << 0)
+#define LCDC_HCRCFG1_RGBMODE_OFFSET	4
+#define LCDC_HCRCFG1_RGBMODE		(0xf << LCDC_HCRCFG1_RGBMODE_OFFSET)
+#define		LCDC_HCRCFG1_RGBMODE_12BPP_RGB_444	(0x0 << 4)
+#define		LCDC_HCRCFG1_RGBMODE_16BPP_ARGB_4444	(0x1 << 4)
+#define		LCDC_HCRCFG1_RGBMODE_16BPP_RGBA_4444	(0x2 << 4)
+#define		LCDC_HCRCFG1_RGBMODE_16BPP_RGB_565	(0x3 << 4)
+#define		LCDC_HCRCFG1_RGBMODE_16BPP_TRGB_1555	(0x4 << 4)
+#define		LCDC_HCRCFG1_RGBMODE_18BPP_RGB_666	(0x5 << 4)
+#define		LCDC_HCRCFG1_RGBMODE_18BPP_RGB_666_PACKED	(0x6 << 4)
+#define		LCDC_HCRCFG1_RGBMODE_19BPP_TRGB_1666	(0x7 << 4)
+#define		LCDC_HCRCFG1_RGBMODE_19BPP_TRGB_PACKED	(0x8 << 4)
+#define		LCDC_HCRCFG1_RGBMODE_24BPP_RGB_888	(0x9 << 4)
+#define		LCDC_HCRCFG1_RGBMODE_24BPP_RGB_888_PACKED	(0xA << 4)
+#define		LCDC_HCRCFG1_RGBMODE_25BPP_TRGB_1888	(0xB << 4)
+#define		LCDC_HCRCFG1_RGBMODE_32BPP_ARGB_8888	(0xC << 4)
+#define		LCDC_HCRCFG1_RGBMODE_32BPP_RGBA_8888	(0xD << 4)
+#define LCDC_HCRCFG1_CLUTMODE_OFFSET	8
+#define LCDC_HCRCFG1_CLUTMODE		(0x3 << LCDC_HCRCFG1_CLUTMODE_OFFSET)
+#define		LCDC_HCRCFG1_CLUTMODE_1BPP		(0x0 << 8)
+#define		LCDC_HCRCFG1_CLUTMODE_2BPP		(0x1 << 8)
+#define		LCDC_HCRCFG1_CLUTMODE_4BPP		(0x2 << 8)
+#define		LCDC_HCRCFG1_CLUTMODE_8BPP		(0x3 << 8)
+
+#define ATMEL_LCDC_HCRCFG2	0x0374
+#define LCDC_HCRCFG2_XOFFSET_OFFSET	0
+#define LCDC_HCRCFG2_XOFFSET		(0x7ff << LCDC_HCRCFG2_XOFFSET_OFFSET)
+#define LCDC_HCRCFG2_YOFFSET_OFFSET	16
+#define LCDC_HCRCFG2_YOFFSET		(0x7ff << LCDC_HCRCFG2_YOFFSET_OFFSET)
+
+#define ATMEL_LCDC_HCRCFG3	0x0378
+#define LCDC_HCRCFG3_XSIZE_OFFSET	0
+#define LCDC_HCRCFG3_XSIZE		(0x7f << LCDC_HCRCFG3_XSIZE_OFFSET)
+#define LCDC_HCRCFG3_YSIZE_OFFSET	16
+#define LCDC_HCRCFG3_YSIZE		(0x7f << LCDC_HCRCFG3_YSIZE_OFFSET)
+
+#define ATMEL_LCDC_HCRCFG4	0x037C
+
+#define ATMEL_LCDC_HCRCFG6	0x0384
+#define LCDC_HCRCFG6_BDEF_OFFSET	0
+#define LCDC_HCRCFG6_BDEF		(0xff << LCDC_HCRCFG6_BDEF_OFFSET)
+#define LCDC_HCRCFG6_GDEF_OFFSET	8
+#define LCDC_HCRCFG6_GDEF		(0xff << LCDC_HCRCFG6_GDEF_OFFSET)
+#define LCDC_HCRCFG6_RDEF_OFFSET	16
+#define LCDC_HCRCFG6_RDEF		(0xff << LCDC_HCRCFG6_RDEF_OFFSET)
+
+#define ATMEL_LCDC_HCRCFG7	0x0388
+#define LCDC_HCRCFG7_BKEY_OFFSET	0
+#define LCDC_HCRCFG7_BKEY		(0xff << LCDC_HCRCFG7_BKEY_OFFSET)
+#define LCDC_HCRCFG7_GKEY_OFFSET	8
+#define LCDC_HCRCFG7_GKEY		(0xff << LCDC_HCRCFG7_GKEY_OFFSET)
+#define LCDC_HCRCFG7_RKEY_OFFSET	16
+#define LCDC_HCRCFG7_RKEY		(0xff << LCDC_HCRCFG7_RKEY_OFFSET)
+
+#define ATMEL_LCDC_HCRCFG8	0x038C
+#define LCDC_HCRCFG8_BMASK_OFFSET	0
+#define LCDC_HCRCFG8_BMASK		(0xff << LCDC_HCRCFG8_BMASK_OFFSET)
+#define LCDC_HCRCFG8_GMASK_OFFSET	8
+#define LCDC_HCRCFG8_GMASK		(0xff << LCDC_HCRCFG8_GMASK_OFFSET)
+#define LCDC_HCRCFG8_RMASK_OFFSET	16
+#define LCDC_HCRCFG8_RMASK		(0xff << LCDC_HCRCFG8_RMASK_OFFSET)
+
+#define ATMEL_LCDC_HCRCFG9	0x0390
+#define LCDC_HCRCFG9_CRKEY		(0x1 << 0)
+#define LCDC_HCRCFG9_INV		(0x1 << 1)
+#define LCDC_HCRCFG9_ITER2BL		(0x1 << 2)
+#define LCDC_HCRCFG9_ITER		(0x1 << 3)
+#define LCDC_HCRCFG9_REVALPHA		(0x1 << 4)
+#define LCDC_HCRCFG9_GAEN		(0x1 << 5)
+#define LCDC_HCRCFG9_LAEN		(0x1 << 6)
+#define LCDC_HCRCFG9_OVR		(0x1 << 7)
+#define LCDC_HCRCFG9_DMA		(0x1 << 8)
+#define LCDC_HCRCFG9_REP		(0x1 << 9)
+#define LCDC_HCRCFG9_DSTKEY		(0x1 << 10)
+#define LCDC_HCRCFG9_GA_OFFSET		16
+#define LCDC_HCRCFG9_GA_Msk		(0xff << LCDC_HCRCFG9_GA_OFFSET)
+
+#define ATMEL_LCDC_BASECLUT	0x400
+#define ATMEL_LCDC2_BASECLUT	0x600
+#define LCDC_BASECLUT_BCLUT_OFFSET	0
+#define LCDC_BASECLUT_BCLUT		(0xff << LCDC_BASECLUT_BCLUT_OFFSET)
+#define LCDC_BASECLUT_GCLUT_OFFSET	8
+#define LCDC_BASECLUT_GCLUT		(0xff << LCDC_BASECLUT_GCLUT_OFFSET)
+#define LCDC_BASECLUT_RCLUT_OFFSET	16
+#define LCDC_BASECLUT_RCLUT		(0xff << LCDC_BASECLUT_RCLUT_OFFSET)
+
+#define ATMEL_LCDC_OVR1CLUT	0x800
+#define ATMEL_LCDC2_OVR1CLUT	0xa00
+#define LCDC_OVR1CLUT_BCLUT_OFFSET	0
+#define LCDC_OVR1CLUT_BCLUT		(0xff << LCDC_OVR1CLUT_BCLUT_OFFSET)
+#define LCDC_OVR1CLUT_GCLUT_OFFSET	8
+#define LCDC_OVR1CLUT_GCLUT		(0xff << LCDC_OVR1CLUT_GCLUT_OFFSET)
+#define LCDC_OVR1CLUT_RCLUT_OFFSET	16
+#define LCDC_OVR1CLUT_RCLUT		(0xff << LCDC_OVR1CLUT_RCLUT_OFFSET)
+#define LCDC_OVR1CLUT_ACLUT_OFFSET	24
+#define LCDC_OVR1CLUT_ACLUT		(0xff << LCDC_OVR1CLUT_ACLUT_OFFSET)
+
+#define ATMEL_LCDC_OVR2CLUT	0xe00
+#define LCDC_OVR2CLUT_BCLUT_OFFSET	0
+#define LCDC_OVR2CLUT_BCLUT		(0xff << LCDC_OVR2CLUT_BCLUT_OFFSET)
+#define LCDC_OVR2CLUT_GCLUT_OFFSET	8
+#define LCDC_OVR2CLUT_GCLUT		(0xff << LCDC_OVR2CLUT_GCLUT_OFFSET)
+#define LCDC_OVR2CLUT_RCLUT_OFFSET	16
+#define LCDC_OVR2CLUT_RCLUT		(0xff << LCDC_OVR2CLUT_RCLUT_OFFSET)
+#define LCDC_OVR2CLUT_ACLUT_OFFSET	24
+#define LCDC_OVR2CLUT_ACLUT		(0xff << LCDC_OVR2CLUT_ACLUT_OFFSET)
+
+#define ATMEL_LCDC_HEOCLUT	0x1000
+#define ATMEL_LCDC2_HEOCLUT	0x1200
+#define LCDC_HEOCLUT_BCLUT_OFFSET	0
+#define LCDC_HEOCLUT_BCLUT		(0xff << LCDC_HEOCLUT_BCLUT_OFFSET)
+#define LCDC_HEOCLUT_GCLUT_OFFSET	8
+#define LCDC_HEOCLUT_GCLUT		(0xff << LCDC_HEOCLUT_GCLUT_OFFSET)
+#define LCDC_HEOCLUT_RCLUT_OFFSET	16
+#define LCDC_HEOCLUT_RCLUT		(0xff << LCDC_HEOCLUT_RCLUT_OFFSET)
+#define LCDC_HEOCLUT_ACLUT_OFFSET	24
+#define LCDC_HEOCLUT_ACLUT		(0xff << LCDC_HEOCLUT_ACLUT_OFFSET)
+
+#define ATMEL_LCDC_HCRCLUT	0x1400
+#define ATMEL_LCDC2_HCRCLUT	0x1600
+#define LCDC_HCRCLUT_BCLUT_OFFSET	0
+#define LCDC_HCRCLUT_BCLUT		(0xff << LCDC_HCRCLUT_BCLUT_OFFSET)
+#define LCDC_HCRCLUT_GCLUT_OFFSET	8
+#define LCDC_HCRCLUT_GCLUT		(0xff << LCDC_HCRCLUT_GCLUT_OFFSET)
+#define LCDC_HCRCLUT_RCLUT_OFFSET	16
+#define LCDC_HCRCLUT_RCLUT		(0xff << LCDC_HCRCLUT_RCLUT_OFFSET)
+#define LCDC_HCRCLUT_ACLUT_OFFSET	24
+#define LCDC_HCRCLUT_ACLUT		(0xff << LCDC_HCRCLUT_ACLUT_OFFSET)
+
+/* Base layer CLUT */
+#define ATMEL_HLCDC_LUT			0x0400
+
+
+static inline void hlcdc_write(struct drm_device *dev, u32 reg, u32 data)
+{
+	struct atmel_hlcdc_drm_private *priv = dev->dev_private;
+	iowrite32(data, priv->mmio + reg);
+}
+
+static inline u32 hlcdc_read(struct drm_device *dev, u32 reg)
+{
+	struct atmel_hlcdc_drm_private *priv = dev->dev_private;
+	return ioread32(priv->mmio + reg);
+}
+
+#endif /* __ATMEL_HLCDC4_H__ */
diff --git a/drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_backlight.c b/drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_backlight.c
new file mode 100644
index 0000000..143ba72
--- /dev/null
+++ b/drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_backlight.c
@@ -0,0 +1,92 @@
+/*
+ * atmel_hlcdc_backlight.c  --  Backlight driver for the atmel HLCDC controller
+ *
+ * Copyright (C) 2014 Traphandler
+ *
+ * Author: Jean-Jacques Hiblot <jjhiblot@...phandler.com)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/backlight.h>
+
+#include "atmel_hlcdc_drv.h"
+#include "atmel_hlcdc.h"
+
+#define ATMEL_LCDC_CVAL_DEFAULT         0xc8
+
+
+static int get_brightness(struct backlight_device *backlight)
+{
+	struct drm_device *dev = bl_get_data(backlight);
+
+	return (hlcdc_read(dev, ATMEL_LCDC_LCDCFG6) & LCDC_LCDCFG6_PWMCVAL)
+		>> LCDC_LCDCFG6_PWMCVAL_OFFSET;
+}
+
+static int update_status(struct backlight_device *backlight)
+{
+	struct drm_device *dev = bl_get_data(backlight);
+	int brightness = backlight->props.brightness;
+	u32 reg;
+
+	if (backlight->props.power != FB_BLANK_UNBLANK ||
+	    backlight->props.state & BL_CORE_SUSPENDED)
+		brightness = 0;
+
+	reg = hlcdc_read(dev, ATMEL_LCDC_LCDCFG6) & ~LCDC_LCDCFG6_PWMCVAL;
+	reg |= brightness << LCDC_LCDCFG6_PWMCVAL_OFFSET;
+	hlcdc_write(dev, ATMEL_LCDC_LCDCFG6, reg);
+	DBG("new brightness is : %d\n", get_brightness(backlight));
+	return 0;
+}
+
+
+
+static const struct backlight_ops atmel_drm_backlight_ops = {
+	.options	= BL_CORE_SUSPENDRESUME,
+	.update_status	= update_status,
+	.get_brightness	= get_brightness,
+};
+
+int atmel_drm_backlight_init(struct drm_device *dev)
+{
+	struct atmel_hlcdc_drm_private *priv = dev->dev_private;
+	struct backlight_device *backlight;
+
+	backlight = backlight_device_register("backlight", dev->dev, dev,
+					      &atmel_drm_backlight_ops , NULL);
+	if (IS_ERR(backlight)) {
+		dev_err(dev->dev, "unable to register backlight device: %ld\n",
+			PTR_ERR(backlight));
+		return PTR_ERR(backlight);
+	}
+	hlcdc_write(dev, ATMEL_LCDC_LCDCFG6, LCDC_LCDCFG6_PWMPOL |
+		(ATMEL_LCDC_CVAL_DEFAULT << LCDC_LCDCFG6_PWMCVAL_OFFSET));
+
+	backlight->props.max_brightness = 0xFF;
+	backlight->props.brightness = get_brightness(backlight);
+	backlight->props.power = FB_BLANK_UNBLANK;
+	backlight_update_status(backlight);
+
+	priv->backlight = backlight;
+
+	return 0;
+}
+
+void atmel_drm_backlight_exit(struct drm_device *dev)
+{
+	struct atmel_hlcdc_drm_private *priv = dev->dev_private;
+
+	backlight_device_unregister(priv->backlight);
+}
diff --git a/drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_backlight.h b/drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_backlight.h
new file mode 100644
index 0000000..6a99101
--- /dev/null
+++ b/drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_backlight.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2014 Traphandler
+ *
+ * Author: Jean-Jacques Hiblot <jjhiblot@...phandler.com)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ATMEL_HLCDC_BACKLIGHT_H__
+#define __ATMEL_HLCDC_BACKLIGHT_H__
+
+int atmel_drm_backlight_init(struct drm_device *dev);
+void atmel_drm_backlight_exit(struct drm_device *dev);
+
+#endif
diff --git a/drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_crtc.c
new file mode 100644
index 0000000..649fa19
--- /dev/null
+++ b/drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_crtc.c
@@ -0,0 +1,702 @@
+/*
+ * Copyright (C) 2014 Traphandler
+ *
+ * Author: Jean-Jacques Hiblot <jjhiblot@...phandler.com)
+ *
+ * Base on the tilcdc driver
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "drm_flip_work.h"
+
+#include "atmel_hlcdc_drv.h"
+#include "atmel_hlcdc.h"
+#include "atmel_hlcdc_ovl.h"
+
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
+
+
+struct atmel_hlcd_dma_desc {
+	u32	address;
+	u32	control;
+	u32	next;
+	u32     dummy; /* for 64-bit alignment */
+};
+
+enum {
+	DMA_BASE = 0,
+	DMA_OVR_1,
+	DMA_OVR_2,
+	DMA_HEO,
+	DMA_HCR,
+	DMA_PP,
+	DMA_MAX
+};
+
+struct atmel_hlcdc_crtc {
+	struct drm_crtc base;
+	uint32_t dirty;
+	dma_addr_t start, end;
+	struct drm_pending_vblank_event *event;
+	int dpms;
+
+	struct atmel_hlcd_dma_desc *dma_descs[DMA_MAX];
+	dma_addr_t dma_descs_phys[DMA_MAX];
+
+	/* fb currently set to scanout 0/1: */
+	struct drm_framebuffer *fb;
+
+	/* for deferred fb unref's: */
+	struct drm_flip_work unref_work;
+#ifdef USE_LUT
+	u8 lut_r[256], lut_g[256], lut_b[256];
+#endif
+};
+#define to_atmel_hlcdc_crtc(x) container_of(x, struct atmel_hlcdc_crtc, base)
+
+static void unref_worker(struct drm_flip_work *work, void *val)
+{
+	struct atmel_hlcdc_crtc *atmel_hlcdc_crtc =
+		container_of(work, struct atmel_hlcdc_crtc, unref_work);
+	struct drm_device *dev = atmel_hlcdc_crtc->base.dev;
+
+	mutex_lock(&dev->mode_config.mutex);
+	drm_framebuffer_unreference(val);
+	mutex_unlock(&dev->mode_config.mutex);
+}
+
+static void update_scanout(struct drm_crtc *crtc)
+{
+	struct atmel_hlcdc_crtc *hlcdc_crtc = to_atmel_hlcdc_crtc(crtc);
+	struct drm_device *dev = crtc->dev;
+	struct atmel_hlcdc_drm_private *priv = dev->dev_private;
+	struct drm_framebuffer *fb = crtc->fb;
+
+	struct drm_gem_cma_object *gem;
+	struct atmel_hlcd_dma_desc *desc = hlcdc_crtc->dma_descs[DMA_BASE];
+	unsigned int depth, bpp;
+	dma_addr_t start;
+	dma_addr_t desc_phys = hlcdc_crtc->dma_descs_phys[DMA_BASE];
+
+	drm_fb_get_bpp_depth(fb->pixel_format, &depth, &bpp);
+	gem = drm_fb_cma_get_gem_obj(fb, 0);
+
+	start = gem->paddr + fb->offsets[0] +
+			(crtc->y * fb->pitches[0]) + (crtc->x * bpp/8);
+
+	if (hlcdc_crtc->fb != fb) {
+		struct drm_framebuffer *oldfb = hlcdc_crtc->fb;
+		drm_framebuffer_reference(fb);
+		hlcdc_crtc->fb = fb;
+		if (oldfb) {
+			drm_flip_work_queue(&hlcdc_crtc->unref_work, oldfb);
+			drm_flip_work_commit(&hlcdc_crtc->unref_work, priv->wq);
+		}
+	}
+
+	if (desc->address != start) {
+		desc->address = start;
+		desc->next = desc_phys;
+		desc->control = LCDC_OVRCTRL_DFETCH;
+	}
+
+	if (hlcdc_read(dev, ATMEL_LCDC_BASENEXT) != desc_phys) {
+		hlcdc_write(dev, ATMEL_LCDC_BASENEXT, desc_phys);
+		hlcdc_write(dev, ATMEL_LCDC_BASECHER, LCDC_BASECHER_UPDATEEN);
+	}
+}
+
+static void start(struct drm_crtc *crtc)
+{
+	struct drm_device *dev = crtc->dev;
+
+	hlcdc_write(dev, ATMEL_LCDC_LCDEN, LCDC_LCDEN_CLKEN);
+	while (!(hlcdc_read(dev, ATMEL_LCDC_LCDSR) & LCDC_LCDSR_CLKSTS))
+		cpu_relax();
+	hlcdc_write(dev, ATMEL_LCDC_LCDEN, LCDC_LCDEN_SYNCEN);
+	while (!(hlcdc_read(dev, ATMEL_LCDC_LCDSR) & LCDC_LCDSR_LCDSTS))
+		cpu_relax();
+	hlcdc_write(dev, ATMEL_LCDC_LCDEN, LCDC_LCDEN_DISPEN);
+	while (!(hlcdc_read(dev, ATMEL_LCDC_LCDSR) & LCDC_LCDSR_DISPSTS))
+		cpu_relax();
+	hlcdc_write(dev, ATMEL_LCDC_LCDEN, LCDC_LCDEN_PWMEN);
+	while (!(hlcdc_read(dev, ATMEL_LCDC_LCDSR) & LCDC_LCDSR_PWMSTS))
+		cpu_relax();
+}
+
+static void stop(struct drm_crtc *crtc)
+{
+	struct drm_device *dev = crtc->dev;
+
+	/* Disable DISP signal */
+	hlcdc_write(dev, ATMEL_LCDC_LCDDIS, LCDC_LCDDIS_DISPDIS);
+	while ((hlcdc_read(dev, ATMEL_LCDC_LCDSR) & LCDC_LCDSR_DISPSTS))
+		cpu_relax();
+	/* Disable synchronization */
+	hlcdc_write(dev, ATMEL_LCDC_LCDDIS, LCDC_LCDDIS_SYNCDIS);
+	while ((hlcdc_read(dev, ATMEL_LCDC_LCDSR) & LCDC_LCDSR_LCDSTS))
+		cpu_relax();
+	/* Disable pixel clock */
+	hlcdc_write(dev, ATMEL_LCDC_LCDDIS, LCDC_LCDDIS_CLKDIS);
+	while ((hlcdc_read(dev, ATMEL_LCDC_LCDSR) & LCDC_LCDSR_CLKSTS))
+		cpu_relax();
+	/* Disable PWM */
+	hlcdc_write(dev, ATMEL_LCDC_LCDDIS, LCDC_LCDDIS_PWMDIS);
+	while ((hlcdc_read(dev, ATMEL_LCDC_LCDSR) & LCDC_LCDSR_PWMSTS))
+		cpu_relax();
+}
+
+static void atmel_hlcdc_crtc_destroy(struct drm_crtc *crtc)
+{
+	struct atmel_hlcdc_crtc *atmel_hlcdc_crtc = to_atmel_hlcdc_crtc(crtc);
+	struct drm_device *dev = crtc->dev;
+
+	WARN_ON(atmel_hlcdc_crtc->dpms == DRM_MODE_DPMS_ON);
+
+	drm_crtc_cleanup(crtc);
+	drm_flip_work_cleanup(&atmel_hlcdc_crtc->unref_work);
+
+	if (atmel_hlcdc_crtc->dma_descs[0])
+		dma_free_writecombine(dev->dev,
+			      sizeof(struct atmel_hlcd_dma_desc) * DMA_MAX,
+			      atmel_hlcdc_crtc->dma_descs[0],
+			      atmel_hlcdc_crtc->dma_descs_phys[0]);
+	kfree(atmel_hlcdc_crtc);
+}
+
+static int atmel_hlcdc_crtc_page_flip(struct drm_crtc *crtc,
+		struct drm_framebuffer *fb,
+		struct drm_pending_vblank_event *event,
+		uint32_t page_flip_flags)
+{
+	struct atmel_hlcdc_crtc *atmel_hlcdc_crtc = to_atmel_hlcdc_crtc(crtc);
+	struct drm_device *dev = crtc->dev;
+
+	if (atmel_hlcdc_crtc->event) {
+		dev_err(dev->dev, "already pending page flip!\n");
+		return -EBUSY;
+	}
+
+	crtc->fb = fb;
+	atmel_hlcdc_crtc->event = event;
+	update_scanout(crtc);
+	return 0;
+}
+
+static void atmel_hlcdc_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+	struct atmel_hlcdc_crtc *atmel_hlcdc_crtc = to_atmel_hlcdc_crtc(crtc);
+	struct drm_device *dev = crtc->dev;
+
+	/* we really only care about on or off: */
+	if (mode != DRM_MODE_DPMS_ON)
+		mode = DRM_MODE_DPMS_OFF;
+
+	if (atmel_hlcdc_crtc->dpms == mode)
+		return;
+
+	atmel_hlcdc_crtc->dpms = mode;
+
+	pm_runtime_get_sync(dev->dev);
+
+	if (mode == DRM_MODE_DPMS_ON) {
+		pm_runtime_forbid(dev->dev);
+		start(crtc);
+	} else {
+		stop(crtc);
+		pm_runtime_allow(dev->dev);
+	}
+
+	pm_runtime_put_sync(dev->dev);
+}
+
+static bool atmel_hlcdc_crtc_mode_fixup(struct drm_crtc *crtc,
+		const struct drm_display_mode *mode,
+		struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+static void atmel_hlcdc_crtc_prepare(struct drm_crtc *crtc)
+{
+	atmel_hlcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+}
+
+static void atmel_hlcdc_crtc_commit(struct drm_crtc *crtc)
+{
+	atmel_hlcdc_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
+}
+
+static u32 atmel_hlcdfb_get_rgbmode(struct device *dev, int depth, int bpp)
+{
+	u32 value = 0;
+
+	switch (depth) {
+	case 1:
+		value = LCDC_BASECFG1_CLUTMODE_1BPP | LCDC_BASECFG1_CLUTEN;
+		break;
+	case 2:
+		value = LCDC_BASECFG1_CLUTMODE_2BPP | LCDC_BASECFG1_CLUTEN;
+		break;
+	case 4:
+		value = LCDC_BASECFG1_CLUTMODE_4BPP | LCDC_BASECFG1_CLUTEN;
+		break;
+	case 8:
+		value = LCDC_BASECFG1_CLUTMODE_8BPP | LCDC_BASECFG1_CLUTEN;
+		break;
+	case 12:
+		value = LCDC_BASECFG1_RGBMODE_12BPP_RGB_444;
+		break;
+	case 16:
+		value = LCDC_BASECFG1_RGBMODE_16BPP_RGB_565;
+		break;
+	case 18:
+		value = LCDC_BASECFG1_RGBMODE_18BPP_RGB_666_PACKED;
+		break;
+	case 24:
+		value = LCDC_BASECFG1_RGBMODE_24BPP_RGB_888_PACKED;
+		break;
+	case 32:
+		value = LCDC_BASECFG1_RGBMODE_32BPP_ARGB_8888;
+		break;
+	default:
+		dev_err(dev, "Cannot set video mode for depth %d, bpp %d\n",
+			depth, bpp);
+		break;
+	}
+
+	return value;
+}
+
+
+void atmel_hlcdc_crtc_update_clk(struct drm_crtc *crtc, int clock)
+{
+	struct drm_device *dev = crtc->dev;
+	struct atmel_hlcdc_drm_private *priv = dev->dev_private;
+	unsigned long value;
+	unsigned long clk_value_khz;
+
+	if (clock == 0)
+		clock = crtc->mode.clock;
+
+	pm_runtime_get_sync(dev->dev);
+
+	clk_value_khz = clk_get_rate(priv->clk) / 1000;
+
+	value = DIV_ROUND_CLOSEST(clk_value_khz, clock);
+
+	if (value < 1) {
+		dev_notice(dev->dev, "using system clock as pixel clock\n");
+		value = LCDC_LCDCFG0_CLKPWMSEL | LCDC_LCDCFG0_CGDISBASE;
+		hlcdc_write(dev, ATMEL_LCDC_LCDCFG0, value);
+	} else {
+		dev_dbg(dev->dev, "  updated pixclk:     %lu KHz\n",
+				clk_value_khz / value);
+		value = value - 2;
+		dev_dbg(dev->dev, "  * programming CLKDIV = 0x%08lx\n",
+					value);
+		value = LCDC_LCDCFG0_CLKPWMSEL |
+			(value << LCDC_LCDCFG0_CLKDIV_OFFSET)
+			| LCDC_LCDCFG0_CGDISBASE;
+		hlcdc_write(dev, ATMEL_LCDC_LCDCFG0, value);
+	}
+
+	pm_runtime_put_sync(dev->dev);
+}
+
+static int atmel_hlcdc_crtc_mode_set(struct drm_crtc *crtc,
+		struct drm_display_mode *mode,
+		struct drm_display_mode *adjusted_mode,
+		int x, int y,
+		struct drm_framebuffer *old_fb)
+{
+	struct atmel_hlcdc_crtc *atmel_hlcdc_crtc = to_atmel_hlcdc_crtc(crtc);
+	struct drm_device *dev = crtc->dev;
+	struct atmel_hlcdc_drm_private *priv = dev->dev_private;
+	int dpms = atmel_hlcdc_crtc->dpms;
+	int hbp, hfp, hsw, vbp, vfp, vsw;
+	unsigned int depth, bpp;
+	unsigned long value;
+	int ret;
+
+	ret = atmel_hlcdc_crtc_mode_valid(crtc, mode);
+	if (WARN_ON(ret))
+		return ret;
+
+	pm_runtime_get_sync(dev->dev);
+
+	if (dpms == DRM_MODE_DPMS_ON)
+		atmel_hlcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+
+	dev_dbg(dev->dev, "%s:\n", __func__);
+
+
+	/* Set pixel clock */
+	atmel_hlcdc_crtc_update_clk(crtc, mode->clock);
+
+	/* Initialize control register 5 */
+	value = priv->default_lcdcfg5;
+	value |= (priv->guard_time << LCDC_LCDCFG5_GUARDTIME_OFFSET)
+		| LCDC_LCDCFG5_DISPDLY
+		| LCDC_LCDCFG5_VSPDLYS;
+
+	if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC)
+		value |= LCDC_LCDCFG5_HSPOL;
+
+	if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC)
+		value |= LCDC_LCDCFG5_VSPOL;
+
+	dev_dbg(dev->dev, "  * LCDC_LCDCFG5 = %08lx\n", value);
+	hlcdc_write(dev, ATMEL_LCDC_LCDCFG5, value);
+
+
+	/* Configure timings: */
+	hbp = MAX(mode->htotal - mode->hsync_end, 1);
+	hfp = MAX(mode->hsync_start - mode->hdisplay, 1);
+	hsw = MAX(mode->hsync_end - mode->hsync_start, 1);
+	vbp = MAX(mode->vtotal - mode->vsync_end, 0);
+	vfp = MAX(mode->vsync_start - mode->vdisplay, 1);
+	vsw = MAX(mode->vsync_end - mode->vsync_start, 1);
+
+	DBG("%dx%d, hbp=%u, hfp=%u, hsw=%u, vbp=%u, vfp=%u, vsw=%u",
+		mode->hdisplay, mode->vdisplay, hbp, hfp, hsw, vbp, vfp, vsw);
+
+	/* Vertical & Horizontal Timing */
+	value = (vsw - 1) << LCDC_LCDCFG1_VSPW_OFFSET;
+	value |= (hsw - 1) << LCDC_LCDCFG1_HSPW_OFFSET;
+	dev_dbg(dev->dev, "  * LCDC_LCDCFG1 = %08lx\n", value);
+	hlcdc_write(dev, ATMEL_LCDC_LCDCFG1, value);
+
+	value = (vbp) << LCDC_LCDCFG2_VBPW_OFFSET;
+	value |= (vfp - 1) << LCDC_LCDCFG2_VFPW_OFFSET;
+	dev_dbg(dev->dev, "  * LCDC_LCDCFG2 = %08lx\n", value);
+	hlcdc_write(dev, ATMEL_LCDC_LCDCFG2, value);
+
+	value = (hbp - 1) << LCDC_LCDCFG3_HBPW_OFFSET;
+	value |= (hfp - 1) << LCDC_LCDCFG3_HFPW_OFFSET;
+	dev_dbg(dev->dev, "  * LCDC_LCDCFG3 = %08lx\n", value);
+	hlcdc_write(dev, ATMEL_LCDC_LCDCFG3, value);
+
+	/* Display size */
+	value = (mode->vdisplay - 1) << LCDC_LCDCFG4_RPF_OFFSET;
+	value |= (mode->hdisplay - 1) << LCDC_LCDCFG4_PPL_OFFSET;
+	dev_dbg(dev->dev, "  * LCDC_LCDCFG4 = %08lx\n", value);
+	hlcdc_write(dev, ATMEL_LCDC_LCDCFG4, value);
+
+	hlcdc_write(dev, ATMEL_LCDC_BASECFG0,
+		    LCDC_BASECFG0_BLEN_AHB_INCR16 | LCDC_BASECFG0_DLBO);
+
+	drm_fb_get_bpp_depth(crtc->fb->pixel_format, &depth, &bpp);
+	hlcdc_write(dev, ATMEL_LCDC_BASECFG1,
+		    atmel_hlcdfb_get_rgbmode(dev->dev, depth, bpp));
+	hlcdc_write(dev, ATMEL_LCDC_BASECFG2, 0);
+	hlcdc_write(dev, ATMEL_LCDC_BASECFG3, 0);	/* Default color */
+	hlcdc_write(dev, ATMEL_LCDC_BASECFG4, LCDC_BASECFG4_DMA);
+
+	/* Disable all interrupts */
+	hlcdc_write(dev, ATMEL_LCDC_LCDIDR, ~0UL);
+	hlcdc_write(dev, ATMEL_LCDC_BASEIDR, ~0UL);
+	/* Enable BASE LAYER overflow interrupts */
+	hlcdc_write(dev, ATMEL_LCDC_BASEIER, LCDC_BASEIER_OVR);
+	/* Enable FIFO error interrupt and  the global BASE LAYER interrupt*/
+	hlcdc_write(dev, ATMEL_LCDC_LCDIER, LCDC_LCDIER_FIFOERRIE |
+					    LCDC_LCDIER_BASEIE);
+
+
+	update_scanout(crtc);
+
+	atmel_hlcdc_crtc_dpms(crtc, dpms);
+
+	pm_runtime_put_sync(dev->dev);
+	return 0;
+}
+
+static int atmel_hlcdc_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+		struct drm_framebuffer *old_fb)
+{
+	update_scanout(crtc);
+	return 0;
+}
+
+#ifdef USE_LUT
+static void atmel_hlcdc_crtc_load_lut(struct drm_crtc *crtc)
+{
+	struct atmel_hlcdc_crtc *atmel_hlcdc_crtc = to_atmel_hlcdc_crtc(crtc);
+	struct drm_device *dev = crtc->dev;
+	int i;
+
+	if (!crtc->enabled)
+		return;
+
+	for (i = 0; i < 256; i++)
+		hlcdc_write(dev, ATMEL_HLCDC_LUT + (i*4),
+			(atmel_hlcdc_crtc->lut_r[i] << 16) |
+			(atmel_hlcdc_crtc->lut_g[i] << 8)  |
+			(atmel_hlcdc_crtc->lut_b[i] << 0));
+}
+void atmel_hlcdc_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
+			      u16 blue, int regno)
+{
+	struct atmel_hlcdc_crtc *atmel_hlcdc_crtc = to_atmel_hlcdc_crtc(crtc);
+
+	atmel_hlcdc_crtc->lut_r[regno] = red;
+	atmel_hlcdc_crtc->lut_g[regno] = green;
+	atmel_hlcdc_crtc->lut_b[regno] = blue;
+}
+
+void atmel_hlcdc_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
+			      u16 *blue, int regno)
+{
+	struct atmel_hlcdc_crtc *atmel_hlcdc_crtc = to_atmel_hlcdc_crtc(crtc);
+
+	*red = atmel_hlcdc_crtc->lut_r[regno];
+	*green = atmel_hlcdc_crtc->lut_g[regno];
+	*blue = atmel_hlcdc_crtc->lut_b[regno];
+}
+#endif
+
+static const struct drm_crtc_funcs atmel_hlcdc_crtc_funcs = {
+		.destroy        = atmel_hlcdc_crtc_destroy,
+		.set_config     = drm_crtc_helper_set_config,
+		.page_flip      = atmel_hlcdc_crtc_page_flip,
+};
+
+static const struct drm_crtc_helper_funcs atmel_hlcdc_crtc_helper_funcs = {
+		.dpms           = atmel_hlcdc_crtc_dpms,
+		.mode_fixup     = atmel_hlcdc_crtc_mode_fixup,
+		.prepare        = atmel_hlcdc_crtc_prepare,
+		.commit         = atmel_hlcdc_crtc_commit,
+		.mode_set       = atmel_hlcdc_crtc_mode_set,
+		.mode_set_base  = atmel_hlcdc_crtc_mode_set_base,
+#ifdef USE_LUT
+		.load_lut	= atmel_hlcdc_crtc_load_lut,
+#endif
+};
+
+int atmel_hlcdc_crtc_mode_valid(struct drm_crtc *crtc,
+				struct drm_display_mode *mode)
+{
+	struct atmel_hlcdc_drm_private *priv = crtc->dev->dev_private;
+	uint32_t hbp, hfp, hsw, vbp, vfp, vsw;
+
+	/*
+	 * check to see if the width is within the range that
+	 * the LCD Controller physically supports
+	 */
+	if (mode->hdisplay > 2048)
+		return MODE_VIRTUAL_X;
+
+	/* width must be multiple of 4 */
+	if (mode->hdisplay & 0x3)
+		return MODE_VIRTUAL_X;
+
+	if (mode->vdisplay > 2048)
+		return MODE_VIRTUAL_Y;
+
+	DBG("Processing mode %dx%d@%d with pixel clock %d",
+		mode->hdisplay, mode->vdisplay,
+		drm_mode_vrefresh(mode), mode->clock);
+
+	hbp = mode->htotal - mode->hsync_end;
+	hfp = mode->hsync_start - mode->hdisplay;
+	hsw = mode->hsync_end - mode->hsync_start;
+	vbp = mode->vtotal - mode->vsync_end;
+	vfp = mode->vsync_start - mode->vdisplay;
+	vsw = mode->vsync_end - mode->vsync_start;
+
+	if ((hbp-1) > 0x1ff) {
+		DBG("Pruning mode: Horizontal Back Porch out of range");
+		return MODE_HBLANK_WIDE;
+	}
+
+	if ((hfp-1) > 0x1ff) {
+		DBG("Pruning mode: Horizontal Front Porch out of range");
+		return MODE_HBLANK_WIDE;
+	}
+
+	if ((hsw-1) > 0x3f) {
+		DBG("Pruning mode: Horizontal Sync Width out of range");
+		return MODE_HSYNC_WIDE;
+	}
+
+	if (vbp > 0x3f) {
+		DBG("Pruning mode: Vertical Back Porch out of range");
+		return MODE_VBLANK_WIDE;
+	}
+
+	if ((vfp - 1) > 0x3f) {
+		DBG("Pruning mode: Vertical Front Porch out of range");
+		return MODE_VBLANK_WIDE;
+	}
+
+	if ((vsw - 1) > 0x3f) {
+		DBG("Pruning mode: Vertical Sync Width out of range");
+		return MODE_VSYNC_WIDE;
+	}
+
+	/*
+	 * some devices have a maximum allowed pixel clock
+	 * configured from the DT
+	 */
+	if (mode->clock > priv->max_pixelclock) {
+		DBG("Pruning mode: pixel clock too high");
+		return MODE_CLOCK_HIGH;
+	}
+
+	return MODE_OK;
+}
+
+irqreturn_t handle_base_irq(struct drm_device *dev)
+{
+	  uint32_t status = hlcdc_read(dev, ATMEL_LCDC_LCDISR) &
+			    hlcdc_read(dev, ATMEL_LCDC_LCDIMR);
+
+	  if (status & LCDC_BASEISR_OVR)
+		dev_warn(dev->dev, "base layer overflow %#x\n", status);
+
+	  if (status)
+		return IRQ_HANDLED;
+
+	  return IRQ_NONE;
+}
+irqreturn_t handle_ovr_irq(struct drm_device *dev, int id)
+{
+	  uint32_t status = hlcdc_ovl_read(dev, id, ATMEL_LCDC_OVRISR) &
+			    hlcdc_ovl_read(dev, id, ATMEL_LCDC_OVRIMR);
+	  if (status)
+		return IRQ_HANDLED;
+	  return IRQ_NONE;
+}
+irqreturn_t handle_heo_irq(struct drm_device *dev)
+{
+	  uint32_t status = hlcdc_read(dev, ATMEL_LCDC_HEOISR) &
+			    hlcdc_read(dev, ATMEL_LCDC_HEOIMR);
+	  if (status)
+		return IRQ_HANDLED;
+	  return IRQ_NONE;
+}
+irqreturn_t handle_hcr_irq(struct drm_device *dev)
+{
+	  uint32_t status = hlcdc_read(dev, ATMEL_LCDC_HCRISR) &
+			    hlcdc_read(dev, ATMEL_LCDC_HCRIMR);
+	  if (status)
+		return IRQ_HANDLED;
+	  return IRQ_NONE;
+}
+irqreturn_t handle_pp_irq(struct drm_device *dev)
+{
+	  return IRQ_NONE;
+}
+
+irqreturn_t atmel_hlcdc_crtc_irq(struct drm_crtc *crtc)
+{
+	uint32_t status;
+	struct drm_device *dev = crtc->dev;
+
+	status = hlcdc_read(dev, ATMEL_LCDC_LCDISR) &
+		 hlcdc_read(dev, ATMEL_LCDC_LCDIMR);
+	if (!status) {
+		dev_warn(dev->dev, "spurious interrupt!\n");
+		return IRQ_NONE;
+	}
+
+	if (status & LCDC_LCDISR_BASE)
+		handle_base_irq(dev);
+	if (status & LCDC_LCDISR_OVR1)
+		handle_ovr_irq(dev, 0);
+	if (status & LCDC_LCDISR_OVR2)
+		handle_ovr_irq(dev, 1);
+	if (status & LCDC_LCDISR_HEO)
+		handle_heo_irq(dev);
+	if (status & LCDC_LCDISR_HCR)
+		handle_hcr_irq(dev);
+	if (status & LCDC_LCDISR_PP)
+		handle_pp_irq(dev);
+
+	if (status & LCDC_LCDISR_FIFOERR)
+		dev_warn(dev->dev, "FIFO underflow %#x\n", status);
+
+	return IRQ_HANDLED;
+}
+
+void atmel_hlcdc_crtc_cancel_page_flip(struct drm_crtc *crtc,
+				       struct drm_file *file)
+{
+	struct atmel_hlcdc_crtc *atmel_hlcdc_crtc = to_atmel_hlcdc_crtc(crtc);
+	struct drm_pending_vblank_event *event;
+	struct drm_device *dev = crtc->dev;
+	unsigned long flags;
+
+	/* Destroy the pending vertical blanking event associated with the
+	 * pending page flip, if any, and disable vertical blanking interrupts.
+	 */
+	spin_lock_irqsave(&dev->event_lock, flags);
+	event = atmel_hlcdc_crtc->event;
+	if (event && event->base.file_priv == file) {
+		atmel_hlcdc_crtc->event = NULL;
+		event->base.destroy(&event->base);
+		drm_vblank_put(dev, 0);
+	}
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+struct drm_crtc *atmel_hlcdc_crtc_create(struct drm_device *dev)
+{
+	int i;
+	struct atmel_hlcdc_crtc *hlcdc;
+	struct drm_crtc *crtc;
+	int ret;
+
+	hlcdc = kzalloc(sizeof(struct atmel_hlcdc_crtc), GFP_KERNEL);
+	if (!hlcdc) {
+		dev_err(dev->dev, "allocation failed\n");
+		return NULL;
+	}
+
+	crtc = &hlcdc->base;
+
+	hlcdc->dpms = DRM_MODE_DPMS_OFF;
+
+	hlcdc->dma_descs[0] = dma_alloc_writecombine(dev->dev,
+				sizeof(struct atmel_hlcd_dma_desc) * DMA_MAX,
+				&(hlcdc->dma_descs_phys[0]),
+				GFP_KERNEL);
+	for (i = 1; i < DMA_MAX; i++) {
+		hlcdc->dma_descs[i] = hlcdc->dma_descs[0] + i;
+		hlcdc->dma_descs_phys[i] = hlcdc->dma_descs_phys[0] +
+				(i * sizeof(struct atmel_hlcd_dma_desc));
+		hlcdc->dma_descs[i]->address = 0;
+		hlcdc->dma_descs[i]->control = 0;
+		hlcdc->dma_descs[i]->next = 0;
+	}
+
+	ret = drm_flip_work_init(&hlcdc->unref_work, 16,
+			"unref", unref_worker);
+	if (ret) {
+		dev_err(dev->dev, "could not allocate unref FIFO\n");
+		goto fail;
+	}
+
+	ret = drm_crtc_init(dev, crtc, &atmel_hlcdc_crtc_funcs);
+	if (ret < 0)
+		goto fail;
+
+	drm_crtc_helper_add(crtc, &atmel_hlcdc_crtc_helper_funcs);
+
+	return crtc;
+
+fail:
+	atmel_hlcdc_crtc_destroy(crtc);
+	return NULL;
+}
diff --git a/drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_drv.c b/drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_drv.c
new file mode 100644
index 0000000..c8aedc8
--- /dev/null
+++ b/drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_drv.c
@@ -0,0 +1,586 @@
+/*
+ * Copyright (C) 2014 Traphandler
+ * Copyright (C) 2012 Texas Instruments
+ *
+ * Base on the tilcdc driver
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "atmel_hlcdc_drv.h"
+#include "atmel_hlcdc.h"
+#include "atmel_hlcdc_ovl.h"
+#include "atmel_hlcdc_panel.h"
+#include "atmel_hlcdc_backlight.h"
+
+#include "drm_fb_helper.h"
+
+static LIST_HEAD(module_list);
+static bool slave_probing;
+
+void atmel_hlcdc_module_init(struct atmel_hlcdc_module *mod, const char *name,
+		const struct atmel_hlcdc_module_ops *funcs)
+{
+	mod->name = name;
+	mod->funcs = funcs;
+	INIT_LIST_HEAD(&mod->list);
+	list_add(&mod->list, &module_list);
+}
+
+void atmel_hlcdc_module_cleanup(struct atmel_hlcdc_module *mod)
+{
+	list_del(&mod->list);
+}
+
+void atmel_hlcdc_slave_probedefer(bool defered)
+{
+	slave_probing = defered;
+}
+
+static struct of_device_id atmel_hlcdc_of_match[];
+
+static struct drm_framebuffer *atmel_hlcdc_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 void atmel_hlcdc_fb_output_poll_changed(struct drm_device *dev)
+{
+	struct atmel_hlcdc_drm_private *priv = dev->dev_private;
+
+	if (priv->fbdev)
+		drm_fbdev_cma_hotplug_event(priv->fbdev);
+}
+
+static const struct drm_mode_config_funcs mode_config_funcs = {
+	.fb_create = atmel_hlcdc_fb_create,
+	.output_poll_changed = atmel_hlcdc_fb_output_poll_changed,
+};
+
+static int modeset_init(struct drm_device *dev)
+{
+	struct atmel_hlcdc_drm_private *priv = dev->dev_private;
+	struct atmel_hlcdc_module *mod;
+
+	atmel_drm_backlight_init(dev);
+
+	drm_mode_config_init(dev);
+
+	priv->crtc = atmel_hlcdc_crtc_create(dev);
+
+	list_for_each_entry(mod, &module_list, list) {
+		DBG("loading module: %s", mod->name);
+		mod->funcs->modeset_init(mod, dev);
+	}
+
+	if ((priv->num_encoders == 0) || (priv->num_connectors == 0)) {
+		/* oh nos! */
+		dev_err(dev->dev, "no encoders/connectors found\n");
+		return -ENXIO;
+	}
+
+	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 = &mode_config_funcs;
+	return 0;
+}
+
+#ifdef CONFIG_CPU_FREQ
+static int cpufreq_transition(struct notifier_block *nb,
+				     unsigned long val, void *data)
+{
+	struct atmel_hlcdc_drm_private *priv = container_of(nb,
+			struct atmel_hlcdc_drm_private, freq_transition);
+
+	if (val == CPUFREQ_POSTCHANGE) {
+		if (priv->lcd_fck_rate != clk_get_rate(priv->clk)) {
+			priv->lcd_fck_rate = clk_get_rate(priv->clk);
+			atmel_hlcdc_crtc_update_clk(priv->crtc, 0);
+		}
+	}
+
+	return 0;
+}
+#endif
+
+/*
+ * DRM operations:
+ */
+
+static int atmel_hlcdc_unload(struct drm_device *dev)
+{
+	struct atmel_hlcdc_drm_private *priv = dev->dev_private;
+	struct atmel_hlcdc_module *mod, *cur;
+
+	drm_kms_helper_poll_fini(dev);
+	drm_mode_config_cleanup(dev);
+	drm_vblank_cleanup(dev);
+
+	pm_runtime_get_sync(dev->dev);
+	drm_irq_uninstall(dev);
+	pm_runtime_put_sync(dev->dev);
+
+#ifdef CONFIG_CPU_FREQ
+	cpufreq_unregister_notifier(&priv->freq_transition,
+			CPUFREQ_TRANSITION_NOTIFIER);
+#endif
+
+	if (priv->clk)
+		clk_put(priv->clk);
+
+	if (priv->mmio)
+		iounmap(priv->mmio);
+
+	flush_workqueue(priv->wq);
+	destroy_workqueue(priv->wq);
+
+	dev->dev_private = NULL;
+
+	pm_runtime_disable(dev->dev);
+
+	list_for_each_entry_safe(mod, cur, &module_list, list) {
+		DBG("destroying module: %s", mod->name);
+		mod->funcs->destroy(mod);
+	}
+
+	kfree(priv);
+
+	return 0;
+}
+
+static int atmel_hlcdc_load(struct drm_device *dev, unsigned long flags)
+{
+	struct platform_device *pdev = dev->platformdev;
+	struct device_node *node = pdev->dev.of_node;
+	struct atmel_hlcdc_drm_private *priv;
+	struct atmel_hlcdc_module *mod;
+	struct resource *res;
+	u32 bpp = 0;
+	int ret;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		dev_err(dev->dev, "failed to allocate private data\n");
+		return -ENOMEM;
+	}
+
+	dev->dev_private = priv;
+
+	priv->wq = alloc_ordered_workqueue("atmel_hlcdc", 0);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev->dev, "failed to get memory resource\n");
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	priv->mmio = ioremap_nocache(res->start, resource_size(res));
+	if (!priv->mmio) {
+		dev_err(dev->dev, "failed to ioremap\n");
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	priv->clk = clk_get(dev->dev, "lcdc_clk");
+	if (IS_ERR(priv->clk)) {
+		dev_err(dev->dev, "failed to get lcd clock\n");
+		ret = -ENODEV;
+		goto fail;
+	}
+	clk_prepare_enable(priv->clk);
+
+	priv->bus_clk = clk_get(dev->dev, "bus_clk");
+	if (IS_ERR(priv->bus_clk)) {
+		dev_dbg(dev->dev, "no bus clock defined.\n");
+		priv->bus_clk = NULL;
+	}
+	if (priv->bus_clk)
+		clk_prepare_enable(priv->bus_clk);
+
+#ifdef CONFIG_CPU_FREQ
+	priv->lcd_fck_rate = clk_get_rate(priv->clk);
+	priv->freq_transition.notifier_call = cpufreq_transition;
+	ret = cpufreq_register_notifier(&priv->freq_transition,
+			CPUFREQ_TRANSITION_NOTIFIER);
+	if (ret) {
+		dev_err(dev->dev, "failed to register cpufreq notifier\n");
+		goto fail;
+	}
+#endif
+
+	if (of_property_read_u32(node, "default-lcdcfg5",
+				  &priv->default_lcdcfg5))
+		priv->default_lcdcfg5 = LCDC_LCDCFG5_MODE_OUTPUT_24BPP;
+
+	DBG("LCDCFG5 default value 0x%x", priv->default_lcdcfg5);
+
+	if (of_property_read_u32(node, "guard-time", &priv->guard_time))
+		priv->guard_time = 9;
+
+	DBG("Guard time Value 0x%x", priv->guard_time);
+
+	if (of_property_read_u32(node, "hlcdc,max-pixelclock",
+					&priv->max_pixelclock))
+		priv->max_pixelclock = ATMEL_HLCDC_DEFAULT_MAX_PIXELCLOCK;
+
+	DBG("Maximum Pixel Clock Value %dKHz", priv->max_pixelclock);
+
+	pm_runtime_enable(dev->dev);
+
+	ret = modeset_init(dev);
+	if (ret < 0) {
+		dev_err(dev->dev, "failed to initialize mode setting\n");
+		goto fail;
+	}
+
+	ret = drm_vblank_init(dev, 1);
+	if (ret < 0) {
+		dev_err(dev->dev, "failed to initialize vblank\n");
+		goto fail;
+	}
+
+	pm_runtime_get_sync(dev->dev);
+	ret = drm_irq_install(dev);
+	pm_runtime_put_sync(dev->dev);
+	if (ret < 0) {
+		dev_err(dev->dev, "failed to install IRQ handler\n");
+		goto fail;
+	}
+
+	platform_set_drvdata(pdev, dev);
+
+	list_for_each_entry(mod, &module_list, list) {
+		DBG("%s: preferred_bpp: %d", mod->name, mod->preferred_bpp);
+		bpp = mod->preferred_bpp;
+		if (bpp > 0)
+			break;
+	}
+
+	priv->fbdev = drm_fbdev_cma_init(dev, bpp,
+			dev->mode_config.num_crtc,
+			dev->mode_config.num_connector);
+	drm_kms_helper_poll_init(dev);
+
+	return 0;
+
+fail:
+	atmel_hlcdc_unload(dev);
+	return ret;
+}
+
+static void atmel_hlcdc_preclose(struct drm_device *dev, struct drm_file *file)
+{
+	struct atmel_hlcdc_drm_private *priv = dev->dev_private;
+	atmel_hlcdc_crtc_cancel_page_flip(priv->crtc, file);
+}
+
+static void atmel_hlcdc_lastclose(struct drm_device *dev)
+{
+	struct atmel_hlcdc_drm_private *priv = dev->dev_private;
+	drm_fbdev_cma_restore_mode(priv->fbdev);
+}
+
+static irqreturn_t atmel_hlcdc_irq(int irq, void *arg)
+{
+	struct drm_device *dev = arg;
+	struct atmel_hlcdc_drm_private *priv = dev->dev_private;
+	return atmel_hlcdc_crtc_irq(priv->crtc);
+}
+
+static void atmel_hlcdc_irq_preinstall(struct drm_device *dev)
+{
+	/* disable all interrupts */
+	hlcdc_write(dev, ATMEL_LCDC_LCDIDR, 0xFFFFFFFF);
+	hlcdc_write(dev, ATMEL_LCDC_BASEIDR, 0xFFFFFFFF);
+	hlcdc_write(dev, ATMEL_LCDC_HEOIDR, 0xFFFFFFFF);
+	hlcdc_write(dev, ATMEL_LCDC_HCRIDR, 0xFFFFFFFF);
+	hlcdc_write(dev, ATMEL_LCDC_HCRIDR, 0xFFFFFFFF);
+	hlcdc_ovl_write(dev, 0, ATMEL_LCDC_OVRIDR, 0xFFFFFFFF);
+	hlcdc_ovl_write(dev, 1, ATMEL_LCDC_OVRIDR, 0xFFFFFFFF);
+	/* read the IQR to clear all bits */
+	hlcdc_read(dev, ATMEL_LCDC_LCDISR);
+}
+
+static int atmel_hlcdc_irq_postinstall(struct drm_device *dev)
+{
+	/* enable FIFO underflow irq: */
+	hlcdc_write(dev, ATMEL_LCDC_BASEIER, LCDC_BASEIER_OVR);
+	hlcdc_write(dev, ATMEL_LCDC_LCDIER,
+		    LCDC_LCDISR_BASE | LCDC_LCDISR_FIFOERR);
+
+	return 0;
+}
+
+static void atmel_hlcdc_irq_uninstall(struct drm_device *dev)
+{
+	/* disable all interrupts */
+	hlcdc_write(dev, ATMEL_LCDC_LCDIDR, 0xFFFFFFFF);
+	hlcdc_write(dev, ATMEL_LCDC_BASEIDR, 0xFFFFFFFF);
+	hlcdc_write(dev, ATMEL_LCDC_HEOIDR, 0xFFFFFFFF);
+	hlcdc_write(dev, ATMEL_LCDC_HCRIDR, 0xFFFFFFFF);
+	hlcdc_write(dev, ATMEL_LCDC_HCRIDR, 0xFFFFFFFF);
+	hlcdc_ovl_write(dev, 0, ATMEL_LCDC_OVRIDR, 0xFFFFFFFF);
+	hlcdc_ovl_write(dev, 1, ATMEL_LCDC_OVRIDR, 0xFFFFFFFF);
+}
+
+static int atmel_hlcdc_enable_vblank(struct drm_device *dev, int crtc)
+{
+	hlcdc_write(dev, ATMEL_LCDC_BASEIER, LCDC_LCDIER_SOFIE);
+	return 0;
+}
+
+static void atmel_hlcdc_disable_vblank(struct drm_device *dev, int crtc)
+{
+	hlcdc_write(dev, ATMEL_LCDC_BASEIER, LCDC_LCDIER_SOFIE);
+}
+
+
+
+#if defined(CONFIG_DEBUG_FS)
+enum reg_type {
+	RW, /* normal (read write at the same address) */
+	CS, /* 'set', 'clear' and 'read' are consecutive registers */
+};
+
+static const struct {
+	const char *name;
+	uint32_t reg;
+	enum reg_type type;
+} registers[] =		{
+#define REG(type, reg) { #reg, reg, type }
+		REG(RW, ATMEL_LCDC_LCDCFG0),
+		REG(RW, ATMEL_LCDC_LCDCFG1),
+		REG(RW, ATMEL_LCDC_LCDCFG2),
+		REG(RW, ATMEL_LCDC_LCDCFG3),
+		REG(RW, ATMEL_LCDC_LCDCFG4),
+		REG(RW, ATMEL_LCDC_LCDCFG5),
+		REG(RW, ATMEL_LCDC_LCDCFG6),
+		REG(CS, ATMEL_LCDC_LCDEN),
+		REG(CS, ATMEL_LCDC_LCDIER),
+		REG(CS, ATMEL_LCDC_BASEIER),
+		REG(RW, ATMEL_LCDC_BASEHEAD),
+		REG(RW, ATMEL_LCDC_BASEADDR),
+		REG(RW, ATMEL_LCDC_BASECTRL),
+		REG(RW, ATMEL_LCDC_BASENEXT),
+		REG(RW, ATMEL_LCDC_BASECFG0),
+		REG(RW, ATMEL_LCDC_BASECFG1),
+		REG(RW, ATMEL_LCDC_BASECFG2),
+		REG(RW, ATMEL_LCDC_BASECFG3),
+		REG(RW, ATMEL_LCDC_BASECFG4),
+		REG(RW, ATMEL_LCDC_BASECFG5),
+		REG(RW, ATMEL_LCDC_BASECFG6),
+
+		REG(RW, ATMEL_LCDC_LCDISR),
+		REG(RW, ATMEL_LCDC_BASEISR),
+#undef REG
+};
+
+static int atmel_hlcdc_regs_show(struct seq_file *m, void *arg)
+{
+	struct drm_info_node *node = (struct drm_info_node *) m->private;
+	struct drm_device *dev = node->minor->dev;
+	struct atmel_hlcdc_drm_private *priv = dev->dev_private;
+	unsigned i;
+
+	pm_runtime_get_sync(dev->dev);
+
+	seq_printf(m, "revision: %d\n", priv->rev);
+
+	for (i = 0; i < ARRAY_SIZE(registers); i++)
+		seq_printf(m, "%s:\t %08x\n", registers[i].name,
+			hlcdc_read(dev, registers[i].reg));
+
+	pm_runtime_put_sync(dev->dev);
+
+	return 0;
+}
+
+static int atmel_hlcdc_mm_show(struct seq_file *m, void *arg)
+{
+	struct drm_info_node *node = (struct drm_info_node *) m->private;
+	struct drm_device *dev = node->minor->dev;
+	return drm_mm_dump_table(m, &dev->vma_offset_manager->vm_addr_space_mm);
+}
+
+static struct drm_info_list atmel_hlcdc_debugfs_list[] = {
+		{ "regs", atmel_hlcdc_regs_show, 0 },
+		{ "mm",   atmel_hlcdc_mm_show,   0 },
+		{ "fb",   drm_fb_cma_debugfs_show, 0 },
+};
+
+static int atmel_hlcdc_debugfs_init(struct drm_minor *minor)
+{
+	struct drm_device *dev = minor->dev;
+	struct atmel_hlcdc_module *mod;
+	int ret;
+
+	ret = drm_debugfs_create_files(atmel_hlcdc_debugfs_list,
+			ARRAY_SIZE(atmel_hlcdc_debugfs_list),
+			minor->debugfs_root, minor);
+
+	list_for_each_entry(mod, &module_list, list)
+		if (mod->funcs->debugfs_init)
+			mod->funcs->debugfs_init(mod, minor);
+
+	if (ret) {
+		dev_err(dev->dev, "could not install atmel_hlcdc_debugfs_list\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static void atmel_hlcdc_debugfs_cleanup(struct drm_minor *minor)
+{
+	struct atmel_hlcdc_module *mod;
+	drm_debugfs_remove_files(atmel_hlcdc_debugfs_list,
+			ARRAY_SIZE(atmel_hlcdc_debugfs_list), minor);
+
+	list_for_each_entry(mod, &module_list, list)
+		if (mod->funcs->debugfs_cleanup)
+			mod->funcs->debugfs_cleanup(mod, minor);
+}
+#endif
+
+
+static const struct file_operations 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 struct drm_driver atmel_hlcdc_driver = {
+	.driver_features    = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET,
+	.load               = atmel_hlcdc_load,
+	.unload             = atmel_hlcdc_unload,
+	.preclose           = atmel_hlcdc_preclose,
+	.lastclose          = atmel_hlcdc_lastclose,
+	.irq_handler        = atmel_hlcdc_irq,
+	.irq_preinstall     = atmel_hlcdc_irq_preinstall,
+	.irq_postinstall    = atmel_hlcdc_irq_postinstall,
+	.irq_uninstall      = atmel_hlcdc_irq_uninstall,
+	.get_vblank_counter = drm_vblank_count,
+	.enable_vblank      = atmel_hlcdc_enable_vblank,
+	.disable_vblank     = atmel_hlcdc_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,
+#ifdef CONFIG_DEBUG_FS
+	.debugfs_init       = atmel_hlcdc_debugfs_init,
+	.debugfs_cleanup    = atmel_hlcdc_debugfs_cleanup,
+#endif
+	.fops               = &fops,
+	.name               = "atmel_hlcdc",
+	.desc               = "ATMEL HLCD Controller DRM",
+	.date               = "20141504",
+	.major              = 1,
+	.minor              = 0,
+};
+
+/*
+ * Power management:
+ */
+
+#ifdef CONFIG_PM_SLEEP
+static int atmel_hlcdc_pm_suspend(struct device *dev)
+{
+	return 0;
+}
+
+static int atmel_hlcdc_pm_resume(struct device *dev)
+{
+	return 0;
+}
+
+static const struct dev_pm_ops atmel_hlcdc_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(atmel_hlcdc_pm_suspend, atmel_hlcdc_pm_resume)
+};
+#endif
+
+/*
+ * Platform driver:
+ */
+
+static int atmel_hlcdc_pdev_probe(struct platform_device *pdev)
+{
+	/* bail out early if no DT data: */
+	if (!pdev->dev.of_node) {
+		dev_err(&pdev->dev, "device-tree data is missing\n");
+		return -ENXIO;
+	}
+
+	/* defer probing if slave is in deferred probing */
+	if (slave_probing == true)
+		return -EPROBE_DEFER;
+
+	return drm_platform_init(&atmel_hlcdc_driver, pdev);
+}
+
+static int atmel_hlcdc_pdev_remove(struct platform_device *pdev)
+{
+	drm_put_dev(platform_get_drvdata(pdev));
+	return 0;
+}
+
+static struct of_device_id atmel_hlcdc_of_match[] = {
+		{ .compatible = "atmel,hlcdc", },
+		{ },
+};
+MODULE_DEVICE_TABLE(of, atmel_hlcdc_of_match);
+
+static struct platform_driver atmel_hlcdc_platform_driver = {
+	.probe      = atmel_hlcdc_pdev_probe,
+	.remove     = atmel_hlcdc_pdev_remove,
+	.driver     = {
+		.owner  = THIS_MODULE,
+		.name   = "atmel_hlcdc",
+#ifdef CONFIG_PM_SLEEP
+		.pm     = &atmel_hlcdc_pm_ops,
+#endif
+		.of_match_table = atmel_hlcdc_of_match,
+	},
+};
+
+static int __init atmel_hlcdc_drm_init(void)
+{
+	atmel_hlcdc_panel_init();
+	return platform_driver_register(&atmel_hlcdc_platform_driver);
+}
+
+static void __exit atmel_hlcdc_drm_fini(void)
+{
+	atmel_hlcdc_panel_fini();
+	platform_driver_unregister(&atmel_hlcdc_platform_driver);
+}
+
+late_initcall(atmel_hlcdc_drm_init);
+module_exit(atmel_hlcdc_drm_fini);
+
+MODULE_AUTHOR("Jean-Jacques Hiblot <jjhiblot@...phandler.com");
+MODULE_DESCRIPTION("ATMEL HLCDC DRM Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_drv.h b/drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_drv.h
new file mode 100644
index 0000000..e5d6a76
--- /dev/null
+++ b/drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_drv.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2014 Traphandler
+ * Copyright (C) 2012 Texas Instruments
+ *
+ * Base on the tilcdc driver
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ATMEL_HLCDC_DRV_H__
+#define __ATMEL_HLCDC_DRV_H__
+
+#include <linux/clk.h>
+#include <linux/cpufreq.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/list.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+
+/* Defaulting to pixel clock defined on AM335x */
+#define ATMEL_HLCDC_DEFAULT_MAX_PIXELCLOCK  126000
+
+
+struct atmel_hlcdc_drm_private {
+	void __iomem *mmio;
+
+	struct clk *bus_clk;     /* bus clock*/
+	struct clk *clk;         /* functional clock */
+	int rev;                 /* IP revision */
+
+	uint32_t guard_time;
+
+	uint32_t default_lcdcfg5;
+	/*
+	 * Pixel Clock will be restricted to some value as
+	 * defined in the device datasheet measured in KHz
+	 */
+	uint32_t max_pixelclock;
+
+#ifdef CONFIG_CPU_FREQ
+	struct notifier_block freq_transition;
+	unsigned int lcd_fck_rate;
+#endif
+
+	struct workqueue_struct *wq;
+
+	struct drm_fbdev_cma *fbdev;
+
+	struct drm_crtc *crtc;
+
+	struct backlight_device *backlight;
+
+	unsigned int num_encoders;
+	struct drm_encoder *encoders[8];
+
+	unsigned int num_connectors;
+	struct drm_connector *connectors[8];
+};
+
+/* Sub-module for display.  Since we don't know at compile time what panels
+ * or display adapter(s) might be present (for ex, off chip dvi/tfp410,
+ * hdmi encoder, various lcd panels), the connector/encoder(s) are split into
+ * separate drivers.  If they are probed and found to be present, they
+ * register themselves with atmel_hlcdc_register_module().
+ */
+struct atmel_hlcdc_module;
+
+struct atmel_hlcdc_module_ops {
+	/* create appropriate encoders/connectors: */
+	int (*modeset_init)(struct atmel_hlcdc_module *mod,
+			    struct drm_device *dev);
+	void (*destroy)(struct atmel_hlcdc_module *mod);
+#ifdef CONFIG_DEBUG_FS
+	/* create debugfs nodes (can be NULL): */
+	int (*debugfs_init)(struct atmel_hlcdc_module *mod,
+			    struct drm_minor *minor);
+	/* cleanup debugfs nodes (can be NULL): */
+	void (*debugfs_cleanup)(struct atmel_hlcdc_module *mod,
+				struct drm_minor *minor);
+#endif
+};
+
+struct atmel_hlcdc_module {
+	const char *name;
+	struct list_head list;
+	const struct atmel_hlcdc_module_ops *funcs;
+	unsigned int preferred_bpp;
+};
+
+void atmel_hlcdc_module_init(struct atmel_hlcdc_module *mod, const char *name,
+		const struct atmel_hlcdc_module_ops *funcs);
+void atmel_hlcdc_module_cleanup(struct atmel_hlcdc_module *mod);
+void atmel_hlcdc_slave_probedefer(bool defered);
+
+#define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
+
+struct drm_crtc *atmel_hlcdc_crtc_create(struct drm_device *dev);
+void atmel_hlcdc_crtc_cancel_page_flip(struct drm_crtc *crtc,
+				       struct drm_file *file);
+irqreturn_t atmel_hlcdc_crtc_irq(struct drm_crtc *crtc);
+void atmel_hlcdc_crtc_update_clk(struct drm_crtc *crtc, int clock);
+int atmel_hlcdc_crtc_mode_valid(struct drm_crtc *crtc,
+				struct drm_display_mode *mode);
+
+#endif /* __ATMEL_HLCDC_DRV_H__ */
diff --git a/drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_ovl.h b/drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_ovl.h
new file mode 100644
index 0000000..a54dfd5
--- /dev/null
+++ b/drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_ovl.h
@@ -0,0 +1,190 @@
+/*
+ *  Header file for AT91 High end LCD Controller
+ *
+ *  Data structure and register user interface
+ *
+ *  Copyright (C) 2010 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __ATMEL_HLCD_OVL_H__
+#define __ATMEL_HLCD_OVL_H__
+
+/*
+ * OVL has a seperate resource which already starts at offset 0x100.
+ * So, these defines start at 0x0. The manual will list them at 0x100.
+ */
+
+#define ATMEL_LCDC_OVRCHER	0x0000
+#define LCDC_OVRCHER_CHEN		(0x1 << 0)
+#define LCDC_OVRCHER_UPDATEEN		(0x1 << 1)
+#define LCDC_OVRCHER_A2QEN		(0x1 << 2)
+
+#define ATMEL_LCDC_OVRCHDR	0x0004
+#define LCDC_OVRCHDR_CHDIS		(0x1 << 0)
+#define LCDC_OVRCHDR_CHRST		(0x1 << 8)
+
+#define ATMEL_LCDC_OVRCHSR	0x0008
+#define LCDC_OVRCHSR_CHSR		(0x1 << 0)
+#define LCDC_OVRCHSR_UPDATESR		(0x1 << 1)
+#define LCDC_OVRCHSR_A2QSR		(0x1 << 2)
+
+#define ATMEL_LCDC_OVRIER	0x000C
+#define LCDC_OVRIER_DMA			(0x1 << 2)
+#define LCDC_OVRIER_DSCR		(0x1 << 3)
+#define LCDC_OVRIER_ADD			(0x1 << 4)
+#define LCDC_OVRIER_DONE		(0x1 << 5)
+#define LCDC_OVRIER_OVR			(0x1 << 6)
+
+#define ATMEL_LCDC_OVRIDR	0x0010
+#define LCDC_OVRIDR_DMA			(0x1 << 2)
+#define LCDC_OVRIDR_DSCR		(0x1 << 3)
+#define LCDC_OVRIDR_ADD			(0x1 << 4)
+#define LCDC_OVRIDR_DONE		(0x1 << 5)
+#define LCDC_OVRIDR_OVR			(0x1 << 6)
+
+#define ATMEL_LCDC_OVRIMR	0x0014
+#define LCDC_OVRIMR_DMA			(0x1 << 2)
+#define LCDC_OVRIMR_DSCR		(0x1 << 3)
+#define LCDC_OVRIMR_ADD			(0x1 << 4)
+#define LCDC_OVRIMR_DONE		(0x1 << 5)
+#define LCDC_OVRIMR_OVR			(0x1 << 6)
+
+#define ATMEL_LCDC_OVRISR	0x0018
+#define LCDC_OVRISR_DMA			(0x1 << 2)
+#define LCDC_OVRISR_DSCR		(0x1 << 3)
+#define LCDC_OVRISR_ADD			(0x1 << 4)
+#define LCDC_OVRISR_DONE		(0x1 << 5)
+#define LCDC_OVRISR_OVR			(0x1 << 6)
+
+#define ATMEL_LCDC_OVRHEAD	0x001C
+
+#define ATMEL_LCDC_OVRADDR	0x0020
+
+#define ATMEL_LCDC_OVRCTRL	0x0024
+#define LCDC_OVRCTRL_DFETCH		(0x1 << 0)
+#define LCDC_OVRCTRL_LFETCH		(0x1 << 1)
+#define LCDC_OVRCTRL_DMAIEN		(0x1 << 2)
+#define LCDC_OVRCTRL_DSCRIEN		(0x1 << 3)
+#define LCDC_OVRCTRL_ADDIEN		(0x1 << 4)
+#define LCDC_OVRCTRL_DONEIEN		(0x1 << 5)
+
+#define ATMEL_LCDC_OVRNEXT	0x0028
+
+#define ATMEL_LCDC_OVRCFG0	0x002C
+#define LCDC_OVRCFG0_SIF		(0x1 << 0)
+#define LCDC_OVRCFG0_BLEN_OFFSET	4
+#define LCDC_OVRCFG0_BLEN		(0x3 << LCDC_OVRCFG0_BLEN_OFFSET)
+#define		LCDC_OVRCFG0_BLEN_AHB_SINGLE		(0x0 << 4)
+#define		LCDC_OVRCFG0_BLEN_AHB_INCR4		(0x1 << 4)
+#define		LCDC_OVRCFG0_BLEN_AHB_INCR8		(0x2 << 4)
+#define		LCDC_OVRCFG0_BLEN_AHB_INCR16		(0x3 << 4)
+#define LCDC_OVRCFG0_DLBO		(0x1 << 8)
+#define LCDC_OVRCFG0_ROTDIS		(0x1 << 12)
+#define LCDC_OVRCFG0_LOCKDIS		(0x1 << 13)
+
+#define ATMEL_LCDC_OVRCFG1	0x0030
+#define LCDC_OVRCFG1_CLUTEN		(0x1 << 0)
+#define LCDC_OVRCFG1_RGBMODE_OFFSET	4
+#define LCDC_OVRCFG1_RGBMODE		(0xf << LCDC_OVRCFG1_RGBMODE_OFFSET)
+#define		LCDC_OVRCFG1_RGBMODE_12BPP_RGB_444	(0x0 << 4)
+#define		LCDC_OVRCFG1_RGBMODE_16BPP_ARGB_4444	(0x1 << 4)
+#define		LCDC_OVRCFG1_RGBMODE_16BPP_RGBA_4444	(0x2 << 4)
+#define		LCDC_OVRCFG1_RGBMODE_16BPP_RGB_565	(0x3 << 4)
+#define		LCDC_OVRCFG1_RGBMODE_16BPP_TRGB_1555	(0x4 << 4)
+#define		LCDC_OVRCFG1_RGBMODE_18BPP_RGB_666	(0x5 << 4)
+#define		LCDC_OVRCFG1_RGBMODE_18BPP_RGB_666_PACKED	(0x6 << 4)
+#define		LCDC_OVRCFG1_RGBMODE_19BPP_TRGB_1666	(0x7 << 4)
+#define		LCDC_OVRCFG1_RGBMODE_19BPP_TRGB_PACKED	(0x8 << 4)
+#define		LCDC_OVRCFG1_RGBMODE_24BPP_RGB_888	(0x9 << 4)
+#define		LCDC_OVRCFG1_RGBMODE_24BPP_RGB_888_PACKED	(0xA << 4)
+#define		LCDC_OVRCFG1_RGBMODE_25BPP_TRGB_1888	(0xB << 4)
+#define		LCDC_OVRCFG1_RGBMODE_32BPP_ARGB_8888	(0xC << 4)
+#define		LCDC_OVRCFG1_RGBMODE_32BPP_RGBA_8888	(0xD << 4)
+#define LCDC_OVRCFG1_CLUTMODE_OFFSET	8
+#define LCDC_OVRCFG1_CLUTMODE		(0x3 << LCDC_OVRCFG1_CLUTMODE_OFFSET)
+#define		LCDC_OVRCFG1_CLUTMODE_1BPP		(0x0 << 8)
+#define		LCDC_OVRCFG1_CLUTMODE_2BPP		(0x1 << 8)
+#define		LCDC_OVRCFG1_CLUTMODE_4BPP		(0x2 << 8)
+#define		LCDC_OVRCFG1_CLUTMODE_8BPP		(0x3 << 8)
+
+#define ATMEL_LCDC_OVRCFG2	0x0034
+#define LCDC_OVRCFG2_XOFFSET_OFFSET	0
+#define LCDC_OVRCFG2_XOFFSET		(0x7ff << LCDC_OVRCFG2_XOFFSET_OFFSET)
+#define LCDC_OVRCFG2_YOFFSET_OFFSET	16
+#define LCDC_OVRCFG2_YOFFSET		(0x7ff << LCDC_OVRCFG2_YOFFSET_OFFSET)
+
+#define ATMEL_LCDC_OVRCFG3	0x0038
+#define LCDC_OVRCFG3_XSIZE_OFFSET	0
+#define LCDC_OVRCFG3_XSIZE		(0x7ff << LCDC_OVRCFG3_XSIZE_OFFSET)
+#define LCDC_OVRCFG3_YSIZE_OFFSET	16
+#define LCDC_OVRCFG3_YSIZE		(0x7ff << LCDC_OVRCFG3_YSIZE_OFFSET)
+
+#define ATMEL_LCDC_OVRCFG4	0x003C
+
+#define ATMEL_LCDC_OVRCFG5	0x0040
+
+#define ATMEL_LCDC_OVRCFG6	0x0044
+#define LCDC_OVRCFG6_BDEF_OFFSET	0
+#define LCDC_OVRCFG6_BDEF		(0xff << LCDC_OVRCFG6_BDEF_OFFSET)
+#define LCDC_OVRCFG6_GDEF_OFFSET	8
+#define LCDC_OVRCFG6_GDEF		(0xff << LCDC_OVRCFG6_GDEF_OFFSET)
+#define LCDC_OVRCFG6_RDEF_OFFSET	16
+#define LCDC_OVRCFG6_RDEF		(0xff << LCDC_OVRCFG6_RDEF_OFFSET)
+
+#define ATMEL_LCDC_OVRCFG7	0x0048
+#define LCDC_OVRCFG7_BKEY_OFFSET	0
+#define LCDC_OVRCFG7_BKEY		(0xff << LCDC_OVRCFG7_BKEY_OFFSET)
+#define LCDC_OVRCFG7_GKEY_OFFSET	8
+#define LCDC_OVRCFG7_GKEY		(0xff << LCDC_OVRCFG7_GKEY_OFFST)
+#define LCDC_OVRCFG7_RKEY_OFFSET	16
+#define LCDC_OVRCFG7_RKEY		(0xff << LCDC_OVRCFG7_RKEY_OFFSET)
+
+#define ATMEL_LCDC_OVRCFG8	0x004C
+#define LCDC_OVRCFG8_BMASK_OFFSET	0
+#define LCDC_OVRCFG8_BMASK		(0xff << LCDC_OVRCFG8_BMASK_OFFSET)
+#define LCDC_OVRCFG8_GMASK_OFFSET	8
+#define LCDC_OVRCFG8_GMASK		(0xff << LCDC_OVRCFG8_GMASK_OFFSET)
+#define LCDC_OVRCFG8_RMASK_OFFSET	16
+#define LCDC_OVRCFG8_RMASK		(0xff << LCDC_OVRCFG8_RMASK_OFFSET)
+
+#define ATMEL_LCDC_OVRCFG9	0x0050
+#define LCDC_OVRCFG9_CRKEY		(0x1 << 0)
+#define LCDC_OVRCFG9_INV		(0x1 << 1)
+#define LCDC_OVRCFG9_ITER2BL		(0x1 << 2)
+#define LCDC_OVRCFG9_ITER		(0x1 << 3)
+#define LCDC_OVRCFG9_REVALPHA		(0x1 << 4)
+#define LCDC_OVRCFG9_GAEN		(0x1 << 5)
+#define LCDC_OVRCFG9_LAEN		(0x1 << 6)
+#define LCDC_OVRCFG9_OVR		(0x1 << 7)
+#define LCDC_OVRCFG9_DMA		(0x1 << 8)
+#define LCDC_OVRCFG9_REP		(0x1 << 9)
+#define LCDC_OVRCFG9_DSTKEY		(0x1 << 10)
+#define LCDC_OVRCFG9_GA_OFFSET		16
+#define LCDC_OVRCFG9_GA			(0xff << LCDC_OVRCFG9_GA_OFFSET)
+
+
+static inline void hlcdc_ovl_write(struct drm_device *dev, int overlay,
+				   u32 reg, u32 data)
+{
+	hlcdc_write(dev, (0x100 * overlay) + 0x140 + reg, data);
+}
+
+static inline u32 hlcdc_ovl_read(struct drm_device *dev, int overlay, u32 reg)
+{
+	return hlcdc_read(dev, (0x100 * overlay) + 0x140 + reg);
+}
+
+#endif /* __ATMEL_HLCD_OVL_H__ */
diff --git a/drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_panel.c b/drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_panel.c
new file mode 100644
index 0000000..b004247
--- /dev/null
+++ b/drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_panel.c
@@ -0,0 +1,459 @@
+/*
+ * Copyright (C) 2014 Traphandler
+ * Copyright (C) 2012 Texas Instruments
+ *
+ * Base on the tilcdc panel driver
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/backlight.h>
+#include <video/display_timing.h>
+#include <video/of_display_timing.h>
+#include <video/videomode.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include "atmel_hlcdc_drv.h"
+
+struct panel_module {
+	struct atmel_hlcdc_module base;
+	struct atmel_hlcdc_panel_info *info;
+	struct display_timings *timings;
+	struct backlight_device *backlight;
+	u32 preferred_bpp;
+	u32 enable_gpio_flags;
+	int enable_gpio;
+	int hsync_neg_pol;
+	int vsync_neg_pol;
+};
+#define to_panel_module(x) container_of(x, struct panel_module, base)
+
+
+/*
+ * Encoder:
+ */
+
+struct panel_encoder {
+	struct drm_encoder base;
+	struct panel_module *mod;
+};
+#define to_panel_encoder(x) container_of(x, struct panel_encoder, base)
+
+
+static void panel_encoder_destroy(struct drm_encoder *encoder)
+{
+	struct panel_encoder *panel_encoder = to_panel_encoder(encoder);
+	drm_encoder_cleanup(encoder);
+	kfree(panel_encoder);
+}
+
+static void panel_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+	struct panel_encoder *panel_encoder = to_panel_encoder(encoder);
+	struct backlight_device *backlight = panel_encoder->mod->backlight;
+
+	if (!backlight)
+		return;
+
+	backlight->props.power = mode == DRM_MODE_DPMS_ON
+				     ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
+	backlight_update_status(backlight);
+
+	if (gpio_is_valid(panel_encoder->mod->enable_gpio)) {
+		int value = (mode == DRM_MODE_DPMS_ON) ? 1 : 0;
+
+		if (panel_encoder->mod->enable_gpio_flags & GPIO_ACTIVE_LOW)
+			value ^= 1;
+
+		gpio_set_value(panel_encoder->mod->enable_gpio, value);
+	}
+}
+
+static bool panel_encoder_mode_fixup(struct drm_encoder *encoder,
+		const struct drm_display_mode *mode,
+		struct drm_display_mode *adjusted_mode)
+{
+	struct panel_encoder *panel_encoder = to_panel_encoder(encoder);
+
+	if (panel_encoder->mod->hsync_neg_pol)
+		adjusted_mode->flags |= DRM_MODE_FLAG_NHSYNC;
+
+	if (panel_encoder->mod->vsync_neg_pol)
+		adjusted_mode->flags |= DRM_MODE_FLAG_NVSYNC;
+
+	return true;
+}
+
+static void panel_encoder_prepare(struct drm_encoder *encoder)
+{
+	panel_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+}
+
+static void panel_encoder_commit(struct drm_encoder *encoder)
+{
+	panel_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
+}
+
+static void panel_encoder_mode_set(struct drm_encoder *encoder,
+		struct drm_display_mode *mode,
+		struct drm_display_mode *adjusted_mode)
+{
+	/* nothing needed */
+}
+
+static const struct drm_encoder_funcs panel_encoder_funcs = {
+		.destroy        = panel_encoder_destroy,
+};
+
+static const struct drm_encoder_helper_funcs panel_encoder_helper_funcs = {
+		.dpms           = panel_encoder_dpms,
+		.mode_fixup     = panel_encoder_mode_fixup,
+		.prepare        = panel_encoder_prepare,
+		.commit         = panel_encoder_commit,
+		.mode_set       = panel_encoder_mode_set,
+};
+
+static struct drm_encoder *panel_encoder_create(struct drm_device *dev,
+		struct panel_module *mod)
+{
+	struct panel_encoder *panel_encoder;
+	struct drm_encoder *encoder;
+	int ret;
+
+	panel_encoder = kzalloc(sizeof(*panel_encoder), GFP_KERNEL);
+	if (!panel_encoder) {
+		dev_err(dev->dev, "allocation failed\n");
+		return NULL;
+	}
+
+	panel_encoder->mod = mod;
+
+	encoder = &panel_encoder->base;
+	encoder->possible_crtcs = 1;
+
+	ret = drm_encoder_init(dev, encoder, &panel_encoder_funcs,
+			DRM_MODE_ENCODER_LVDS);
+	if (ret < 0)
+		goto fail;
+
+	drm_encoder_helper_add(encoder, &panel_encoder_helper_funcs);
+
+	return encoder;
+
+fail:
+	panel_encoder_destroy(encoder);
+	return NULL;
+}
+
+/*
+ * Connector:
+ */
+
+struct panel_connector {
+	struct drm_connector base;
+
+	struct drm_encoder *encoder;  /* our connected encoder */
+	struct panel_module *mod;
+};
+#define to_panel_connector(x) container_of(x, struct panel_connector, base)
+
+
+static void panel_connector_destroy(struct drm_connector *connector)
+{
+	struct panel_connector *panel_con = to_panel_connector(connector);
+	drm_connector_cleanup(connector);
+	kfree(panel_con);
+}
+
+static enum drm_connector_status panel_connector_detect(
+		struct drm_connector *connector,
+		bool force)
+{
+	return connector_status_connected;
+}
+
+static int panel_connector_get_modes(struct drm_connector *connector)
+{
+	struct drm_device *dev = connector->dev;
+	struct panel_connector *panel_con = to_panel_connector(connector);
+	struct display_timings *timings = panel_con->mod->timings;
+	int i;
+
+	for (i = 0; i < timings->num_timings; i++) {
+		struct drm_display_mode *mode = drm_mode_create(dev);
+		struct videomode vm;
+
+		if (videomode_from_timings(timings, &vm, i))
+			break;
+
+		drm_display_mode_from_videomode(&vm, mode);
+
+		mode->type = DRM_MODE_TYPE_DRIVER;
+
+		if (timings->native_mode == i)
+			mode->type |= DRM_MODE_TYPE_PREFERRED;
+
+		drm_mode_set_name(mode);
+		drm_mode_probed_add(connector, mode);
+	}
+
+	return i;
+}
+
+static int panel_connector_mode_valid(struct drm_connector *connector,
+		  struct drm_display_mode *mode)
+{
+	struct atmel_hlcdc_drm_private *priv = connector->dev->dev_private;
+	/* our only constraints are what the crtc can generate: */
+	return atmel_hlcdc_crtc_mode_valid(priv->crtc, mode);
+}
+
+static struct drm_encoder *panel_connector_best_encoder(
+		struct drm_connector *connector)
+{
+	struct panel_connector *panel_connector = to_panel_connector(connector);
+	return panel_connector->encoder;
+}
+
+static const struct drm_connector_funcs panel_connector_funcs = {
+	.destroy            = panel_connector_destroy,
+	.dpms               = drm_helper_connector_dpms,
+	.detect             = panel_connector_detect,
+	.fill_modes         = drm_helper_probe_single_connector_modes,
+};
+
+static const struct drm_connector_helper_funcs panel_connector_helper_funcs = {
+	.get_modes          = panel_connector_get_modes,
+	.mode_valid         = panel_connector_mode_valid,
+	.best_encoder       = panel_connector_best_encoder,
+};
+
+static struct drm_connector *panel_connector_create(struct drm_device *dev,
+		struct panel_module *mod, struct drm_encoder *encoder)
+{
+	struct panel_connector *panel_connector;
+	struct drm_connector *connector;
+	int ret;
+
+	panel_connector = kzalloc(sizeof(*panel_connector), GFP_KERNEL);
+	if (!panel_connector) {
+		dev_err(dev->dev, "allocation failed\n");
+		return NULL;
+	}
+
+	panel_connector->encoder = encoder;
+	panel_connector->mod = mod;
+
+	connector = &panel_connector->base;
+
+	drm_connector_init(dev, connector, &panel_connector_funcs,
+			DRM_MODE_CONNECTOR_LVDS);
+	drm_connector_helper_add(connector, &panel_connector_helper_funcs);
+
+	connector->interlace_allowed = 0;
+	connector->doublescan_allowed = 0;
+
+	ret = drm_mode_connector_attach_encoder(connector, encoder);
+	if (ret)
+		goto fail;
+
+	drm_sysfs_connector_add(connector);
+
+	return connector;
+
+fail:
+	panel_connector_destroy(connector);
+	return NULL;
+}
+
+/*
+ * Module:
+ */
+
+static int panel_modeset_init(struct atmel_hlcdc_module *mod,
+			      struct drm_device *dev)
+{
+	struct panel_module *panel_mod = to_panel_module(mod);
+	struct atmel_hlcdc_drm_private *priv = dev->dev_private;
+	struct drm_encoder *encoder;
+	struct drm_connector *connector;
+
+	encoder = panel_encoder_create(dev, panel_mod);
+	if (!encoder)
+		return -ENOMEM;
+
+	connector = panel_connector_create(dev, panel_mod, encoder);
+	if (!connector)
+		return -ENOMEM;
+
+	priv->encoders[priv->num_encoders++] = encoder;
+	priv->connectors[priv->num_connectors++] = connector;
+
+	return 0;
+}
+
+static void panel_destroy(struct atmel_hlcdc_module *mod)
+{
+	struct panel_module *panel_mod = to_panel_module(mod);
+
+	if (panel_mod->timings) {
+		display_timings_release(panel_mod->timings);
+		kfree(panel_mod->timings);
+	}
+
+	atmel_hlcdc_module_cleanup(mod);
+	kfree(panel_mod->info);
+	kfree(panel_mod);
+}
+
+static const struct atmel_hlcdc_module_ops panel_module_ops = {
+		.modeset_init = panel_modeset_init,
+		.destroy = panel_destroy,
+};
+
+/*
+ * Device:
+ */
+
+/* maybe move this somewhere common if it is needed by other outputs? */
+static int of_get_panel_info(struct device *dev, struct device_node *np,
+			     struct panel_module *panel)
+{
+	enum of_gpio_flags flags;
+
+	int err = 0;
+
+	if (!np) {
+		pr_err("%s: no devicenode given\n", __func__);
+		return -EINVAL;
+	}
+	if (of_property_read_u32(np, "preferred-bpp",
+				 &panel->preferred_bpp) < 0)
+		panel->preferred_bpp = 16;
+	panel->hsync_neg_pol = of_property_read_bool(np, "invert-hsync");
+	panel->vsync_neg_pol = of_property_read_bool(np, "invert-vsync");
+	panel->enable_gpio = of_get_named_gpio_flags(np, "enable-gpio",
+						    0, &flags);
+	if (gpio_is_valid(panel->enable_gpio)) {
+		unsigned int value;
+
+		if (flags & OF_GPIO_ACTIVE_LOW)
+			panel->enable_gpio_flags |= GPIO_ACTIVE_LOW;
+
+		err = gpio_request(panel->enable_gpio, "bkl_enable");
+		if (err < 0) {
+			dev_err(dev, "failed to request GPIO#%u: %d\n",
+				panel->enable_gpio, err);
+			return err;
+		}
+
+		value = (panel->enable_gpio_flags & GPIO_ACTIVE_LOW) != 0;
+
+		err = gpio_direction_output(panel->enable_gpio, value);
+		if (err < 0) {
+			dev_err(dev, "failed to setup GPIO%u: %d\n",
+				panel->enable_gpio, err);
+			goto free_gpio;
+		}
+	}
+
+	return 0;
+
+free_gpio:
+	gpio_free(panel->enable_gpio);
+	return err;
+}
+
+static struct of_device_id panel_of_match[];
+
+static int panel_probe(struct platform_device *pdev)
+{
+	struct device_node *node = pdev->dev.of_node;
+	struct panel_module *panel_mod;
+	struct atmel_hlcdc_module *mod;
+	struct pinctrl *pinctrl;
+	int ret = -EINVAL;
+
+	/* bail out early if no DT data: */
+	if (!node) {
+		dev_err(&pdev->dev, "device-tree data is missing\n");
+		return -ENXIO;
+	}
+	panel_mod = kzalloc(sizeof(*panel_mod), GFP_KERNEL);
+	if (!panel_mod)
+		return -ENOMEM;
+
+	mod = &panel_mod->base;
+
+	pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+	if (IS_ERR(pinctrl))
+		dev_warn(&pdev->dev, "pins are not configured\n");
+
+	panel_mod->timings = of_get_display_timings(node);
+	if (!panel_mod->timings) {
+		dev_err(&pdev->dev, "could not get panel timings\n");
+		goto fail;
+	}
+
+	if (of_get_panel_info(&pdev->dev, node, panel_mod) < 0) {
+		dev_err(&pdev->dev, "could not get panel info\n");
+		goto fail;
+	}
+
+	mod->preferred_bpp = panel_mod->preferred_bpp;
+
+	panel_mod->backlight = of_find_backlight_by_node(node);
+	if (panel_mod->backlight)
+		dev_info(&pdev->dev, "found backlight\n");
+
+
+	atmel_hlcdc_module_init(mod, "panel", &panel_module_ops);
+
+	return 0;
+
+fail:
+	panel_destroy(mod);
+	return ret;
+}
+
+static int panel_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static struct of_device_id panel_of_match[] = {
+		{ .compatible = "atmel,hlcdc,panel", },
+		{ },
+};
+
+struct platform_driver panel_driver = {
+	.probe = panel_probe,
+	.remove = panel_remove,
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "panel",
+		.of_match_table = panel_of_match,
+	},
+};
+
+int __init atmel_hlcdc_panel_init(void)
+{
+	return platform_driver_register(&panel_driver);
+}
+
+void __exit atmel_hlcdc_panel_fini(void)
+{
+	platform_driver_unregister(&panel_driver);
+}
diff --git a/drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_panel.h b/drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_panel.h
new file mode 100644
index 0000000..0f66169
--- /dev/null
+++ b/drivers/gpu/drm/atmel_hlcdc/atmel_hlcdc_panel.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 Traphandler
+ * Copyright (C) 2012 Texas Instruments
+ *
+ * Base on the tilcdc panel driver
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ATMEL_HLCDC_PANEL_H__
+#define __ATMEL_HLCDC_PANEL_H__
+
+/* sub-module for generic lcd panel output */
+
+int atmel_hlcdc_panel_init(void);
+void atmel_hlcdc_panel_fini(void);
+
+#endif /* __ATMEL_HLCDC_PANEL_H__ */
-- 
1.9.1

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

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ