[RFC 1/3] atmel: drm: added drm driver for the atmel hlcd controller
Jean-Jacques Hiblot
jjhiblot at traphandler.com
Fri Apr 18 02:45:07 PDT 2014
Signed-off-by: Jean-Jacques Hiblot <jjhiblot at traphandler.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 at traphandler.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 at traphandler.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 at traphandler.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 at traphandler.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
More information about the linux-arm-kernel
mailing list