Signed-off-by: Jean-Jacques Hiblot <jjhiblot@xxxxxxxxxxxxxxx> --- 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@xxxxxxxxxxxxxxx) + * + * 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@xxxxxxxxxxxxxxx) + * + * 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@xxxxxxxxxxxxxxx) + * + * 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@xxxxxxxxxxxxxxx"); +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 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/dri-devel