ARM: dove: add a kms/drm driver
Subsystems (2)
PATCH 1/1 ARM: dove: add a kms/drm driver
This patch adds a KMS/DRM video driver for the LCD and display controllers of the Marvell's Dove SoC.
--- drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/dove/Kconfig | 10 + drivers/gpu/drm/dove/Makefile | 6 + drivers/gpu/drm/dove/dove_crtc.c | 1378 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/dove/dove_dcon.h | 64 +++ drivers/gpu/drm/dove/dove_drv.c | 380 +++++++++++++ drivers/gpu/drm/dove/dove_drv.h | 93 ++++ drivers/gpu/drm/dove/dove_ec.c | 570 ++++++++++++++++++++ drivers/gpu/drm/dove/dove_lcd.h | 519 ++++++++++++++++++ 10 files changed, 3023 insertions(+)
drivers/gpu/drm/Kconfig +2 -0
@@ -220,3 +220,5 @@ source "drivers/gpu/drm/omapdrm/Kconfig" source "drivers/gpu/drm/tilcdc/Kconfig" source "drivers/gpu/drm/qxl/Kconfig" + +source "drivers/gpu/drm/dove/Kconfig"
drivers/gpu/drm/Makefile +1 -0
@@ -52,4 +52,5 @@ obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ obj-$(CONFIG_DRM_OMAP) += omapdrm/ obj-$(CONFIG_DRM_TILCDC) += tilcdc/ obj-$(CONFIG_DRM_QXL) += qxl/ +obj-$(CONFIG_DRM_DOVE) += dove/ obj-y += i2c/
drivers/gpu/drm/dove/Kconfig new file +10 -0
@@ -0,0 +1,10 @@ +config DRM_DOVE + tristate "DRM Support for Marvell Dove" + depends on DRM && ARCH_DOVE + depends on OF + select DRM_KMS_HELPER + select DRM_KMS_CMA_HELPER + select DRM_GEM_CMA_HELPER + help + Choose this option if you have a Marvell Dove chipset. + If M is selected the module will be called dove-drm.
drivers/gpu/drm/dove/Makefile new file +6 -0
@@ -0,0 +1,6 @@ +# +# Makefile for Marvell Dove's DRM device driver +# + +dove-drm-objs := dove_drv.o dove_crtc.o dove_ec.o +obj-$(CONFIG_DRM_DOVE) += dove-drm.o
drivers/gpu/drm/dove/dove_crtc.c new file +1378 -0
1,378 lines not on this page · raw message
drivers/gpu/drm/dove/dove_dcon.h new file +64 -0
64 lines not on this page · raw message
drivers/gpu/drm/dove/dove_drv.c new file +380 -0
380 lines not on this page · raw message
drivers/gpu/drm/dove/dove_drv.h new file +93 -0
93 lines not on this page · raw message
drivers/gpu/drm/dove/dove_ec.c new file +570 -0
570 lines not on this page · raw message
drivers/gpu/drm/dove/dove_lcd.h new file +519 -0
489 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Signature · 2 lines
Ken ar c'henta? | ** Breizh ha Linux atav! ** Jef | http://moinejf.free.fr/
Part of the diff is not shown here. View the raw message.
PATCH 1/1 ARM: dove: add a kms/drm driver
Quoted 4 lines · Jean-Francois Moine
This patch adds a KMS/DRM video driver for the LCD and display controllers of the Marvell's Dove SoC.
Signed-off-by Jean-Francois Moine <[email protected]>
I want to review this as I have a competing driver which offers full support for the Dove cubox including a fully accelerated X server, and overlay support, page flipping and so forth.
Jean-François,
Please run ./scripts/get_maintainer.pl on your patches before submitting. In this case:
$ ./scripts/get_maintainer.pl -f drivers/gpu/drm/ David Airlie <[email protected]> (maintainer:DRM DRIVERS) [email protected] (open list:DRM DRIVERS) [email protected] (open list)
I've added them to the CC:
thx,
Jason.
Quoted 3,115 lines · Jean-Francois Moine
This patch adds a KMS/DRM video driver for the LCD and display controllers of the Marvell's Dove SoC.
Signed-off-by Jean-Francois Moine <[email protected]>--- drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/dove/Kconfig | 10 + drivers/gpu/drm/dove/Makefile | 6 + drivers/gpu/drm/dove/dove_crtc.c | 1378 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/dove/dove_dcon.h | 64 +++ drivers/gpu/drm/dove/dove_drv.c | 380 +++++++++++++ drivers/gpu/drm/dove/dove_drv.h | 93 ++++ drivers/gpu/drm/dove/dove_ec.c | 570 ++++++++++++++++++++ drivers/gpu/drm/dove/dove_lcd.h | 519 ++++++++++++++++++ 10 files changed, 3023 insertions(+)drivers/gpu/drm/Kconfig +2
-0@@ -220,3 +220,5 @@ source "drivers/gpu/drm/omapdrm/Kconfig" source "drivers/gpu/drm/tilcdc/Kconfig" source "drivers/gpu/drm/qxl/Kconfig" + +source "drivers/gpu/drm/dove/Kconfig"drivers/gpu/drm/Makefile +1
-0@@ -52,4 +52,5 @@ obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ obj-$(CONFIG_DRM_OMAP) += omapdrm/ obj-$(CONFIG_DRM_TILCDC) += tilcdc/ obj-$(CONFIG_DRM_QXL) += qxl/ +obj-$(CONFIG_DRM_DOVE) += dove/ obj-y += i2c/drivers/gpu/drm/dove/Kconfig new file +10
-0@@ -0,0 +1,10 @@ +config DRM_DOVE + tristate "DRM Support for Marvell Dove" + depends on DRM && ARCH_DOVE + depends on OF + select DRM_KMS_HELPER + select DRM_KMS_CMA_HELPER + select DRM_GEM_CMA_HELPER + help + Choose this option if you have a Marvell Dove chipset. + If M is selected the module will be called dove-drm.drivers/gpu/drm/dove/Makefile new file +6
-0@@ -0,0 +1,6 @@ +# +# Makefile for Marvell Dove's DRM device driver +# + +dove-drm-objs := dove_drv.o dove_crtc.o dove_ec.o +obj-$(CONFIG_DRM_DOVE) += dove-drm.odrivers/gpu/drm/dove/dove_crtc.c new file +1378
-0@@ -0,0 +1,1378 @@ +/* +* Marvell Dove DRM driver - CRTC +* +* Copyright (C) 2013 +* Jean-Francois Moine <[email protected]> +* Sebastian Hesselbarth <[email protected]> +* +* 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/module.h> +#include <linux/of_irq.h> + +#include "dove_drv.h" +#include "dove_lcd.h" + +#define DOVE_LCD_REG_BASE_MASK 0xfffff +#define DOVE_LCD0_REG_BASE 0x20000 +#define DOVE_LCD1_REG_BASE 0x10000 + +#define to_dove_lcd (x) container_of(x, struct dove_lcd, crtc) + +static inline void dove_write (struct dove_lcd *dove_lcd,u32 reg,u32 data) +{ +writel (data, dove_lcd->mmio + reg); +} +static inline u32 dove_read (struct dove_lcd *dove_lcd,u32 reg) +{ +return readl (dove_lcd->mmio + reg); +} +static inline void dove_set (struct dove_lcd *dove_lcd,u32 reg,u32 mask) +{ +dove_write (dove_lcd, reg,dove_read (dove_lcd, reg) | mask); +} +static inline void dove_clear (struct dove_lcd *dove_lcd,u32 reg,u32 mask) +{ +dove_write (dove_lcd, reg,dove_read (dove_lcd, reg) & ~mask); +} + +/* +* vertical blank functions +*/ +u32 dove_vblank_count (struct drm_device *dev,int crtc) +{ +struct dove_lcd *dove_lcd = dove_drm.lcds[crtc]; + +return STA_GRA_FRAME_COUNT (dove_read (dove_lcd,SPU_IRQ_ISR )); +} + +int dove_enable_vblank (struct drm_device *dev,int crtc) +{ +struct dove_lcd *dove_lcd = dove_drm.lcds[crtc]; + +#ifdef HANDLE_INTERLACE + dove_lcd->vblank_enabled =1 ; +#endif +dove_set (dove_lcd,SPU_IRQ_ENA ,IRQ_GRA_FRAME_DONE ); +return 0 ; +} + +void dove_disable_vblank (struct drm_device *dev,int crtc) +{ +struct dove_lcd *dove_lcd = dove_drm.lcds[crtc]; + +#ifdef HANDLE_INTERLACE + dove_lcd->vblank_enabled =0 ; + if (!dove_lcd->v_sync0) +#endif +dove_clear (dove_lcd,SPU_IRQ_ENA ,IRQ_GRA_FRAME_DONE ); +} + +#ifdef CONFIG_DEBUG_FS +static int dove_lcd_regs_show (struct seq_file *m, +struct dove_lcd *dove_lcd) +{ +u32 x, shl, shh, total_v, total_h, active_h, active_v; +u32 orig_buff_x, orig_buff_y, zoomed_x, zoomed_y; +unsigned i; + +seq_printf (m,"\t\t*** LCD %d ***\n" , dove_lcd->num); + +/* Get resolution */ + x =dove_read (dove_lcd,LCD_SPU_V_H_ACTIVE ); + active_h =H_LCD (x); + active_v =V_LCD (x); + +/* Get total line */ + x =dove_read (dove_lcd,LCD_SPUT_V_H_TOTAL ); + total_h =H_LCD (x); + total_v =V_LCD (x); +seq_printf (m,"----total-------------------------<%4dx%4d>" +"-------------------------\n" +"----active--------------|" , total_h, total_v); + +/* Get H Timings */ + x =dove_read (dove_lcd,LCD_SPU_H_PORCH ); + shl =F_LCD (x); + shh =B_LCD (x); +seq_printf (m,"->front porch(%d)->hsync(%d)->back porch(%d)\n" , + shl, total_h - shl -shh - active_h, shh); + +seq_printf (m,"|\t\t\t|\n" +"|\t\t\t|\n" +"|\t<%4dx%4d>\t|\n" +"|\t\t\t|\n" +"|\t\t\t|\n" +"------------------------|\n" , active_h, active_v); + +/* Get V Timings */ + x =dove_read (dove_lcd,LCD_SPU_V_PORCH ); + shl =F_LCD (x); + shh =B_LCD (x); +seq_printf (m,"|\n|front porch(%d)\n|vsync(%d)\n|back porch(%d)\n" , + shl, total_v - shl - shh - active_v, shh); +seq_printf (m,"----------------------------------" +"-----------------------------------\n" ); + +/* Get Line Pitch */ + x =dove_read (dove_lcd,LCD_CFG_GRA_PITCH ); + shl = x &0x0000ffff ; +seq_printf (m,"gfx line pitch in memory is <%d>\n" , + shl); + +/* Get scaling info */ + x =dove_read (dove_lcd,LCD_SPU_GRA_HPXL_VLN ); + orig_buff_x =H_LCD (x); + orig_buff_y =V_LCD (x); + x =dove_read (dove_lcd,LCD_SPU_GZM_HPXL_VLN ); + zoomed_x =H_LCD (x); + zoomed_y =V_LCD (x); +seq_printf (m,"Scaled from <%dx%d> to <%dx%d>\n" , + orig_buff_x, orig_buff_y, zoomed_x, zoomed_y); + +seq_printf (m,"======================================\n" ); + +for (i =0x0080 ; i <=0x01c4 ; i +=4 ) { + x =dove_read (dove_lcd, i); +seq_printf (m,"0x%04x 0x%08x\n" , i, x); + } +return 0 ; +} + +static int dove_regs_show (struct seq_file *m,void *arg) +{ +struct dove_lcd *dove_lcd; +unsigned i; + +for (i =0 ; i <MAX_DOVE_LCD ; i++) { + dove_lcd = dove_drm.lcds[i]; +if (dove_lcd) +dove_lcd_regs_show (m, dove_lcd); + } +return 0 ; +} + +static struct drm_info_list dove_debugfs_list[] = { + {"lcd" , dove_regs_show,0 }, + {"fb" , drm_fb_cma_debugfs_show,0 }, +}; + +int dove_debugfs_init (struct drm_minor *minor) +{ +struct drm_device *dev = minor->dev; +int ret; + +DRM_DEBUG_DRIVER ("\n" ); + + ret =drm_debugfs_create_files (dove_debugfs_list, +ARRAY_SIZE (dove_debugfs_list), + minor->debugfs_root, minor); +if (ret) +dev_err (dev->dev,"could not install dove_debugfs_list\n" ); + +return ret; +} + +void dove_debugfs_cleanup (struct drm_minor *minor) +{ +drm_debugfs_remove_files (dove_debugfs_list, +ARRAY_SIZE (dove_debugfs_list), minor); +} +#endif + +static void dove_update_base (struct dove_lcd *dove_lcd) +{ +struct drm_crtc *crtc = &dove_lcd->crtc; +struct drm_framebuffer *fb = crtc->fb; +struct drm_gem_cma_object *gem; +unsigned int depth, bpp; +dma_addr_t start; + +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 ; + +dove_write (dove_lcd,LCD_CFG_GRA_START_ADDR0 , start); +#ifdef HANDLE_INTERLACE +if (dove_lcd->crtc.mode.mode->flags &DRM_MODE_FLAG_INTERLACE ) { +dove_write (dove_lcd,LCD_CFG_GRA_START_ADDR1 , + start + fb->pitches[0 ]); +dove_write (dove_lcd,LCD_CFG_GRA_PITCH , fb->pitches[0 ] *2 ); +return ; + } +#endif +dove_write (dove_lcd,LCD_CFG_GRA_START_ADDR1 , start); +dove_write (dove_lcd,LCD_CFG_GRA_PITCH , fb->pitches[0 ]); +} + +static void set_frame_timings (struct dove_lcd *dove_lcd) +{ +struct drm_crtc *crtc = &dove_lcd->crtc; +const struct drm_display_mode *mode = &crtc->mode; +u32 h_active, v_active, h_orig, v_orig, h_zoom, v_zoom; +u32 hfp, hbp, vfp, vbp, hs, vs, v_total; +u32 x; + +/* +* Calc active size, zoomed size, porch. +*/ + h_active = h_zoom = mode->hdisplay; + v_active = v_zoom = mode->vdisplay; + hfp = mode->hsync_start - mode->hdisplay; + hbp = mode->htotal - mode->hsync_end; + vfp = mode->vsync_start - mode->vdisplay; + vbp = mode->vtotal - mode->vsync_end; + hs = mode->hsync_end - mode->hsync_start; + vs = mode->vsync_end - mode->vsync_start; + +/* +* Calc original size. +*/ + h_orig = h_active; + v_orig = v_active; + +#ifdef HANDLE_INTERLACE +/* interlaced workaround */ +if (mode->flags &DRM_MODE_FLAG_INTERLACE ) { + v_active /=2 ; + v_zoom /=2 ; + v_orig /=2 ; + } +#endif + +/* calc total width and height */ + v_total = v_active + vfp + vs + vbp; + +/* apply setting to registers */ +dove_write (dove_lcd,LCD_SPU_V_H_ACTIVE ,LCD_H_V (h_active, v_active)); +dove_write (dove_lcd,LCD_SPU_GRA_HPXL_VLN ,LCD_H_V (h_orig, v_orig)); +dove_write (dove_lcd,LCD_SPU_GZM_HPXL_VLN ,LCD_H_V (h_zoom, v_zoom)); +dove_write (dove_lcd,LCD_SPU_H_PORCH ,LCD_F_B (hfp, hbp)); +dove_write (dove_lcd,LCD_SPU_V_PORCH ,LCD_F_B (vfp, vbp)); +dove_write (dove_lcd,LCD_SPUT_V_H_TOTAL , +LCD_H_V (mode->htotal, v_total)); + +/* configure vsync adjust logic */ + x =dove_read (dove_lcd,LCD_TV_CONTROL1 ); + x &= ~(VSYNC_L_OFFSET_MASK |VSYNC_H_OFFSET_MASK ); + x |=VSYNC_OFFSET_EN |/* VSYNC adjust enable */ +VSYNC_L_OFFSET (h_active + hfp) | +VSYNC_H_OFFSET (h_active + hfp); +#ifdef HANDLE_INTERLACE +if (mode->flags &DRM_MODE_FLAG_INTERLACE ) { + dove_lcd->v_sync0 =VSYNC_L_OFFSET (h_active + hfp) | +VSYNC_H_OFFSET (h_active + hfp); + dove_lcd->v_sync1 =VSYNC_L_OFFSET (h_active /2 + hfp) | +VSYNC_H_OFFSET (h_active /2 + hfp); + }else { + dove_lcd->v_sync0 =0 ; + } +#endif +dove_write (dove_lcd,LCD_TV_CONTROL1 , x); +} + +static int dove_set_clock (struct dove_lcd *dove_lcd) +{ +struct drm_crtc *crtc = &dove_lcd->crtc; +const struct drm_display_mode *mode = &crtc->mode; +struct clk *clk; +u32 x, needed_pixclk, ref_clk, div, fract; +int clk_src; + + fract =0 ; + needed_pixclk = mode->clock *1000 ; +#ifdef HANDLE_INTERLACE +if (mode->flags &DRM_MODE_FLAG_INTERLACE ) + needed_pixclk /=2 ; +#endif + +/* first check if pixclk is multiple of current clock */ + clk_src = dove_lcd->clk_src; + clk = dove_lcd->clk[clk_src]; + ref_clk =clk_get_rate (clk); + +DRM_DEBUG_DRIVER ("clk src %d rate %u needed %u div %u mod %u\n" , + clk_src, ref_clk, needed_pixclk, + ref_clk / needed_pixclk, ref_clk % needed_pixclk); + +if (ref_clk % needed_pixclk ==0 ) { + div = ref_clk / needed_pixclk; + goto set_clock; + } + +/* try to set current clock to requested pixclk */ +clk_set_rate (clk, needed_pixclk); + ref_clk =clk_get_rate (clk); +if (ref_clk == needed_pixclk) { + div =1 ; + goto set_clock; + } + +/* check if any other clock can set pixclk directly */ +for (clk_src =0 ; clk_src <MAX_CLK ; clk_src++) { + clk = dove_lcd->clk[clk_src]; +if (!clk) +continue ; + +/* try to set clock to requested pixclk */ +clk_set_rate (clk, needed_pixclk); + ref_clk =clk_get_rate (clk); + +if (ref_clk % needed_pixclk ==0 ) { + div = ref_clk / needed_pixclk; + goto set_clock; + } + } + +/* fall back to default fix clock source LCD or AXI */ +if (dove_lcd->clk[SCLK_SRC_PLLDIV ]) + clk_src =SCLK_SRC_PLLDIV ; +else + clk_src =SCLK_SRC_AXI ; + clk = dove_lcd->clk[clk_src]; + +clk_set_rate (clk, needed_pixclk); + ref_clk =clk_get_rate (clk); + +/* use internal divider */ +if (false) { +/*fixme: does not work*/ + ref_clk /=1000 ; + needed_pixclk /=1000 ; + x = (ref_clk *0x1000 + needed_pixclk -1 ) / needed_pixclk; + div = x >>12 ; +if (div <1 ) + div =1 ; +else + fract = x &0xfff ; + }else { + div = (ref_clk + needed_pixclk -1 ) / needed_pixclk; +if (div <1 ) + div =1 ; + } + +set_clock: + dove_lcd->clk_src = clk_src; +DRM_DEBUG_DRIVER ("set clk src %d ref %u div %u fract %u needed %u\n" , + clk_src, ref_clk, div, fract, needed_pixclk); + x =SET_SCLK (clk_src, div, fract); +dove_write (dove_lcd,LCD_CFG_SCLK_DIV , x); +return 0 ; +} + +static void set_dma_control (struct dove_lcd *dove_lcd) +{ +const struct drm_display_mode *mode = &dove_lcd->crtc.mode; +u32 x; +int fmt, rbswap; + + rbswap =1 ;/* default */ +switch (dove_lcd->crtc.fb->pixel_format) { +case DRM_FORMAT_BGR888 : + rbswap =0 ; +case DRM_FORMAT_RGB888 : + fmt =GMODE_RGB888PACKED ; +break ; +case DRM_FORMAT_XBGR8888 : + rbswap =0 ; +case DRM_FORMAT_XRGB8888 :/* depth 24 */ + fmt =GMODE_RGBA888 ; +break ; +case DRM_FORMAT_ABGR8888 : + rbswap =0 ; +case DRM_FORMAT_ARGB8888 :/* depth 32 */ + fmt =GMODE_RGB888UNPACKED ; +break ; +case DRM_FORMAT_YVYU : + rbswap =0 ; +case DRM_FORMAT_YUYV : + fmt =GMODE_YUV422PACKED ; +break ; +case DRM_FORMAT_YVU422 : + rbswap =0 ; +case DRM_FORMAT_YUV422 : + fmt =GMODE_YUV422PLANAR ; +break ; +case DRM_FORMAT_YVU420 : + rbswap =0 ; +default : +/* case DRM_FORMAT_YUV420: */ + fmt =GMODE_YUV420PLANAR ; +break ; + } + + x =dove_read (dove_lcd,LCD_SPU_DMA_CTRL0 ); + x &= ~(CFG_PALETTE_ENA |/* true color */ +CFG_GRAFORMAT_MASK | +CFG_GRA_SWAPRB | +CFG_GRA_FTOGGLE ); + x |=CFG_GRA_ENA |/* graphic enable */ +CFG_GRA_HSMOOTH ;/* horiz. smooth scaling */ + x |=CFG_GRAFORMAT (fmt); + +if (!rbswap) + x |=CFG_GRA_SWAPRB ; +#ifdef HANDLE_INTERLACE +if (mode->flags &DRM_MODE_FLAG_INTERLACE ) + x |=CFG_GRA_FTOGGLE ; +#endif +dove_write (dove_lcd,LCD_SPU_DMA_CTRL0 , x); + +/* +* trigger DMA on the falling edge of vsync if vsync is +* active low, or on the rising edge if vsync is active high +*/ +if (!(mode->flags &DRM_MODE_FLAG_PVSYNC )) +dove_set (dove_lcd,LCD_SPU_DMA_CTRL1 ,CFG_VSYNC_INV ); +} + +/* this function is called on mode DRM_MODE_DPMS_ON +* and also at loading time with gpio_only set */ +static void set_dumb_panel_control (struct dove_lcd *dove_lcd, +int gpio_only) +{ +const struct drm_display_mode *mode = &dove_lcd->crtc.mode; +u32 x; + + x =0 ; +if (dove_lcd->dpms ==DRM_MODE_DPMS_ON ) + x =CFG_DUMB_ENA ; +if (!gpio_only) { +if (dove_lcd->dpms ==DRM_MODE_DPMS_ON ) +/* +* When dumb interface isn't under 24bit +* It might be under SPI or GPIO. If set +* to 0x7 will force LCD_D[23:0] output +* blank color and damage GPIO and SPI +* behavior. +*/ + x |=CFG_DUMBMODE (DUMB24_RGB888_0 ); +else + x |=CFG_DUMBMODE (7 ); +/*fixme +if (mode->flags & FB_SYNC_COMP_HIGH_ACT) +x |= CFG_INV_COMPSYNC; +*/ +if (!(mode->flags &DRM_MODE_FLAG_PVSYNC )) + x |=CFG_INV_VSYNC ; + +/* Following is a weired workaround. This bit shouldn't be set +* For now, if it's 1080p or 720 then don't set HOR_HIGH_ACT */ +if ((mode->hdisplay ==1920 && mode->vdisplay ==1080 ) || + (mode->hdisplay ==1280 && mode->vdisplay ==720 )) +/* Do nothing */ + ; +else +if (!(mode->flags &DRM_MODE_FLAG_PHSYNC )) + x |=CFG_INV_HSYNC ; + } + +dove_write (dove_lcd,LCD_SPU_DUMB_CTRL , x); +} + +void dove_crtc_start (struct dove_lcd *dove_lcd) +{ +struct drm_crtc *crtc = &dove_lcd->crtc; +struct drm_display_mode *mode = &crtc->mode; + +DRM_DEBUG_DRIVER ("\n" ); + +if (mode->clock ==0 ) { +dev_err (dove_lcd->dev,"crtc_start: no clock!\n" ); + dove_lcd->dpms =DRM_MODE_DPMS_OFF ; +return ; + } + +set_frame_timings (dove_lcd); +if (dove_set_clock (dove_lcd) <0 ) +return ; +set_dma_control (dove_lcd); +dove_update_base (dove_lcd); +set_dumb_panel_control (dove_lcd,0 ); + +#ifdef HANDLE_INTERLACE +if (dove_lcd->v_sync0) {/* interlace mode on */ +dove_set (dove_lcd,SPU_IRQ_ENA ,IRQ_GRA_FRAME_DONE ); + }else {/* interlace mode off */ +if (!dove_lcd->vblank_enabled) +dove_clear (dove_lcd,SPU_IRQ_ENA ,IRQ_GRA_FRAME_DONE ); + } +#endif + +DRM_DEBUG_DRIVER ("start %s@%d\n" , + crtc->mode.name, crtc->mode.vrefresh); +} + +void dove_crtc_stop (struct dove_lcd *dove_lcd) +{ +DRM_DEBUG_DRIVER ("\n" ); + +dove_clear (dove_lcd,LCD_SPU_DMA_CTRL0 ,CFG_GRA_ENA ); +dove_clear (dove_lcd,LCD_SPU_DUMB_CTRL ,CFG_DUMB_ENA ); +#ifdef HANDLE_INTERLACE +if (dove_lcd->v_sync0 + && !dove_lcd->vblank_enabled) +dove_clear (dove_lcd,SPU_IRQ_ENA ,IRQ_GRA_FRAME_DONE ); +#endif +} + +/* ----------------------------------------------------------------------------- +* cursor +*/ + +/* load the hardware cursor */ +static int load_cursor (struct dove_lcd *dove_lcd, +struct drm_file *file_priv, +uint32_t handle, +int data_len) +{ +struct drm_gem_object *obj; +struct drm_gem_cma_object *cma_obj; +u8 *p_pixel; +u32 u, val; +u32 ram, color; +int i, j, ret; + + obj =drm_gem_object_lookup (dove_drm.drm, file_priv, handle); +if (!obj) +return -ENOENT ; + +if (!obj->map_list.map) { +dev_warn (dove_lcd->dev,"cursor not mapped\n" ); + ret = -EINVAL ; + goto out; + } + +if (data_len != obj->size) { +dev_warn (dove_lcd->dev,"bad cursor size\n" ); + ret = -EINVAL ; + goto out; + } + + cma_obj =to_drm_gem_cma_obj (obj); + p_pixel = cma_obj->vaddr; + + u =CFG_SRAM_INIT_WR_RD (SRAMID_INIT_WRITE ) | +CFG_SRAM_ADDR_LCDID (SRAMID_HWC ); + ram =CFG_SRAM_INIT_WR_RD (SRAMID_INIT_WRITE ); + +/* load the RGBA cursor to SRAM */ +for (i =0 ; i < data_len /4 /4 ; i++) { + color = (p_pixel[3 *4 +0 ] <<24 ) |/* red */ + (p_pixel[2 *4 +0 ] <<16 ) | + (p_pixel[1 *4 +0 ] <<8 ) | + p_pixel[0 ]; +dove_write (dove_lcd,LCD_SPU_SRAM_WRDAT , color); +dove_write (dove_lcd,LCD_SPU_SRAM_CTRL , + ram |CFG_SRAM_ADDR_LCDID (SRAMID_HWC32_RAM1 )); + color = (p_pixel[3 *4 +1 ] <<24 ) |/* green */ + (p_pixel[2 *4 +1 ] <<16 ) | + (p_pixel[1 *4 +1 ] <<8 ) | + p_pixel[1 ]; +dove_write (dove_lcd,LCD_SPU_SRAM_WRDAT , color); +dove_write (dove_lcd,LCD_SPU_SRAM_CTRL , + ram |CFG_SRAM_ADDR_LCDID (SRAMID_HWC32_RAM2 )); + color = (p_pixel[3 *4 +2 ] <<24 ) |/* blue */ + (p_pixel[2 *4 +2 ] <<16 ) | + (p_pixel[1 *4 +2 ] <<8 ) | + p_pixel[2 ]; +dove_write (dove_lcd,LCD_SPU_SRAM_WRDAT , color); +dove_write (dove_lcd,LCD_SPU_SRAM_CTRL , + ram |CFG_SRAM_ADDR_LCDID (SRAMID_HWC32_RAM3 )); + p_pixel +=4 *4 ; +if ((++ram &0xff ) ==0 ) { + ram -=0x100 ;/* I[7:0] */ + ram +=1 <<12 ;/* J[1:0] */ + } + } + +/* set the transparency */ + p_pixel = cma_obj->vaddr; +for (i =0 ; i < data_len /16 /4 ; i++) { + val =0 ; +for (j =16 *4 -4 ; j >=0 ; j -=4 ) { + val <<=2 ; +if (p_pixel[j +3 ])/* alpha */ + val |=1 ;/* not transparent */ + } +dove_write (dove_lcd,LCD_SPU_SRAM_WRDAT , val); +dove_write (dove_lcd,LCD_SPU_SRAM_CTRL , u++); + p_pixel +=16 *4 ; + } + ret =0 ; +out: +drm_gem_object_unreference_unlocked (obj); +return ret; +} + +static int dove_cursor_set (struct drm_crtc *crtc, +struct drm_file *file_priv, +uint32_t handle, +uint32_t width, +uint32_t height) +{ +struct dove_lcd *dove_lcd =to_dove_lcd (crtc); +int ret; + +DRM_DEBUG_DRIVER ("%dx%d handle %d\n" , width, height, handle); + +/* disable cursor */ +dove_clear (dove_lcd,LCD_SPU_DMA_CTRL0 ,CFG_HWC_ENA ); + +if (!handle) +return 0 ;/* cursor off */ + +if (width !=64 || height !=64 ) { +dev_err (dove_lcd->dev,"bad cursor size\n" ); +return -EINVAL ; + } + +/* load the cursor */ + ret =load_cursor (dove_lcd, file_priv, handle, width * height *4 ); +if (ret <0 ) +return ret; + +/* set cursor size */ +dove_write (dove_lcd,LCD_SPU_HWC_HPXL_VLN ,LCD_H_V (width, height)); + +/* enable cursor */ +dove_set (dove_lcd,LCD_SPU_DMA_CTRL0 ,CFG_HWC_ENA ); + +return 0 ; +} + +static int dove_cursor_move (struct drm_crtc *crtc, +int x,int y) +{ +struct dove_lcd *dove_lcd =to_dove_lcd (crtc); + +if (x <0 ) + x =0 ; +if (y <0 ) + y =0 ; +dove_clear (dove_lcd,LCD_SPU_DMA_CTRL0 ,CFG_HWC_ENA ); +dove_write (dove_lcd,LCD_SPU_HWC_OVSA_HPXL_VLN ,LCD_H_V (x, y)); +dove_set (dove_lcd,LCD_SPU_DMA_CTRL0 ,CFG_HWC_ENA ); +return 0 ; +} + +static void dove_crtc_destroy (struct drm_crtc *crtc) +{ +struct dove_lcd *dove_lcd =to_dove_lcd (crtc); + +DRM_DEBUG_DRIVER ("\n" ); + +WARN_ON (dove_lcd->dpms ==DRM_MODE_DPMS_ON ); + +drm_crtc_cleanup (crtc); +} + +static int dove_crtc_page_flip (struct drm_crtc *crtc, +struct drm_framebuffer *fb, +struct drm_pending_vblank_event *event) +{ +struct dove_lcd *dove_lcd =to_dove_lcd (crtc); +struct drm_device *drm = dove_drm.drm; +unsigned long flags; + +DRM_DEBUG_DRIVER ("\n" ); + +spin_lock_irqsave (&drm->event_lock, flags); +if (dove_lcd->event) { +spin_unlock_irqrestore (&drm->event_lock, flags); +dev_err (drm->dev,"already pending page flip!\n" ); +return -EBUSY ; + } +spin_unlock_irqrestore (&drm->event_lock, flags); + + crtc->fb = fb; +dove_update_base (dove_lcd); + +if (event) { + event->pipe =0 ; +spin_lock_irqsave (&drm->event_lock, flags); + dove_lcd->event = event; +spin_unlock_irqrestore (&drm->event_lock, flags); +drm_vblank_get (drm, dove_lcd->num); + } + +return 0 ; +} + +static void dove_crtc_dpms (struct drm_crtc *crtc,int mode) +{ +struct dove_lcd *dove_lcd =to_dove_lcd (crtc); + +/* we really only care about on or off */ +if (mode !=DRM_MODE_DPMS_ON ) + mode =DRM_MODE_DPMS_OFF ; + +DRM_DEBUG_DRIVER ("dpms %s\n" , mode ==DRM_MODE_DPMS_ON ?"on" :"off" ); + +if (dove_lcd->dpms == mode) +return ; + + dove_lcd->dpms = mode; + +if (mode ==DRM_MODE_DPMS_ON ) +dove_crtc_start (dove_lcd); +else +dove_crtc_stop (dove_lcd); +} + +static bool dove_crtc_mode_fixup (struct drm_crtc *crtc, +const struct drm_display_mode *mode, +struct drm_display_mode *adjusted_mode) +{ +DRM_DEBUG_DRIVER ("\n" ); +if (adjusted_mode->vrefresh ==0 ) { +/* drm_mode_set_name(adjusted_mode); */ + adjusted_mode->vrefresh =drm_mode_vrefresh (adjusted_mode); +DRM_DEBUG_DRIVER ("%s@%d\n" , + adjusted_mode->name, adjusted_mode->vrefresh); + } +return true; +} + +static void dove_crtc_prepare (struct drm_crtc *crtc) +{ +DRM_DEBUG_DRIVER ("\n" ); +dove_crtc_dpms (crtc,DRM_MODE_DPMS_OFF ); +} + +static void dove_crtc_commit (struct drm_crtc *crtc) +{ +DRM_DEBUG_DRIVER ("\n" ); +/* dove_crtc_dpms(crtc, DRM_MODE_DPMS_ON); */ +} + +static int dove_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) +{ +unsigned int bandwidth; +DRM_DEBUG_DRIVER ("\n" ); + +if (mode->hdisplay >2048 ) +return MODE_VIRTUAL_X ; + +/* width must be multiple of 16 */ +if (mode->hdisplay &0xf ) +return MODE_VIRTUAL_X ; + +if (mode->vdisplay >2048 ) +return MODE_VIRTUAL_Y ; + +/* filter out modes that would require too much memory bandwidth: */ + bandwidth = mode->hdisplay * mode->vdisplay *drm_mode_vrefresh (mode); +if (bandwidth >1920 *1080 *60 ) +return MODE_BAD ; + +/*fixme: is this useful? */ + mode = &crtc->mode; +if (mode->vrefresh ==0 ) { +drm_mode_set_name (mode); + mode->vrefresh =drm_mode_vrefresh (mode); +DRM_DEBUG_DRIVER ("%s@%d\n" , mode->name, mode->vrefresh); + } +return MODE_OK ; +} + +static int dove_crtc_mode_set_base (struct drm_crtc *crtc,int x,int y, +struct drm_framebuffer *old_fb) +{ +struct dove_lcd *dove_lcd =to_dove_lcd (crtc); + +DRM_DEBUG_DRIVER ("\n" ); +dove_update_base (dove_lcd); +return 0 ; +} + +/* mandatory drm function */ +static void dove_crtc_load_lut (struct drm_crtc *crtc) +{ +DRM_DEBUG_DRIVER ("\n" ); +} + +static const struct drm_crtc_funcs dove_crtc_funcs = { + .cursor_set = dove_cursor_set, + .cursor_move = dove_cursor_move, + .destroy = dove_crtc_destroy, + .set_config = drm_crtc_helper_set_config, + .page_flip = dove_crtc_page_flip, +}; + +static const struct drm_crtc_helper_funcs dove_crtc_helper_funcs = { + .dpms = dove_crtc_dpms, + .mode_fixup = dove_crtc_mode_fixup, + .prepare = dove_crtc_prepare, + .commit = dove_crtc_commit, + .mode_set = dove_crtc_mode_set, + .mode_set_base = dove_crtc_mode_set_base, + .load_lut = dove_crtc_load_lut, +}; + +void dove_crtc_cancel_page_flip (struct dove_lcd *dove_lcd, +struct drm_file *file) +{ +struct drm_pending_vblank_event *event; +struct drm_device *drm = dove_drm.drm; +unsigned long flags; + +DRM_DEBUG_DRIVER ("\n" ); + +/* +* Destroy the pending vertical blanking event associated with the +* pending page flip, if any, and disable vertical blanking interrupts. +*/ +spin_lock_irqsave (&drm->event_lock, flags); + event = dove_lcd->event; +if (event && event->base.file_priv == file) { + dove_lcd->event =NULL ; + event->base.destroy (&event->base); +drm_vblank_put (drm, dove_lcd->num); + } +spin_unlock_irqrestore (&drm->event_lock, flags); +} + +/* configure default register values */ +static void dove_set_defaults (struct dove_lcd *dove_lcd) +{ +u32 x; + +/* set the default clock */ +if (dove_lcd->clk[SCLK_SRC_PLLDIV ]) + dove_lcd->clk_src =SCLK_SRC_PLLDIV ; +else + dove_lcd->clk_src =SCLK_SRC_AXI ; +DRM_DEBUG_DRIVER ("default clock %d\n" , dove_lcd->clk_src); + + x =SET_SCLK (dove_lcd->clk_src,1 ,0 ); +dove_write (dove_lcd,LCD_CFG_SCLK_DIV , x); +dove_write (dove_lcd,LCD_SPU_BLANKCOLOR ,0 ); + +dove_write (dove_lcd,SPU_IOPAD_CONTROL ,IOPAD_DUMB24 ); +dove_write (dove_lcd,LCD_CFG_GRA_START_ADDR1 ,0 ); +dove_write (dove_lcd,LCD_SPU_GRA_OVSA_HPXL_VLN ,0 ); +dove_write (dove_lcd,LCD_SPU_SRAM_PARA0 ,0 ); +dove_write (dove_lcd,LCD_SPU_SRAM_PARA1 , CFG_CSB_256x32 | + CFG_CSB_256x24 | + CFG_CSB_256x8); +dove_write (dove_lcd,LCD_SPU_DMA_CTRL1 ,CFG_VSYNC_TRIG (2 ) | +CFG_GATED_ENA | +CFG_PWRDN_ENA | +CFG_ALPHA_MODE (2 ) | +CFG_ALPHA (0xff ) | +CFG_PXLCMD (0x81 )); + +/* +* Fix me: to avoid jiggling issue for high resolution in +* dual display, we set watermark to affect LCD AXI read +* from MC (default 0x80). Lower watermark means LCD will +* do DMA read more often. +*/ + x =dove_read (dove_lcd,LCD_CFG_RDREG4F ); + x &= ~DMA_WATERMARK_MASK ; + x |=DMA_WATERMARK (0x20 ); + +/* +* Disable LCD SRAM Read Wait State to resolve HWC32 make +* system hang while use external clock. +*/ + x &= ~LCD_SRAM_WAIT ; +dove_write (dove_lcd,LCD_CFG_RDREG4F , x); + +/* prepare the hwc32 */ +dove_set (dove_lcd,LCD_TV_CONTROL1 ,HWC32_ENABLE ); + +/* set hwc32 with 100% static alpha blending factor */ +dove_write (dove_lcd,LCD_SPU_ALPHA_COLOR1 , +HWC32_CFG_ALPHA (0xff )); +} + +static irqreturn_t dove_lcd_irq (int irq,void *dev_id) +{ +struct dove_lcd *dove_lcd = (struct dove_lcd *) dev_id; +struct drm_pending_vblank_event *event; +struct drm_device *drm = dove_drm.drm; +u32 isr; +unsigned long flags; + + isr =dove_read (dove_lcd,SPU_IRQ_ISR ); +dove_write (dove_lcd,SPU_IRQ_ISR ,0 ); + +DRM_DEBUG_DRIVER ("\n" ); + +if (isr &IRQ_GRA_FRAME_DONE ) { +#ifdef HANDLE_INTERLACE +if (dove_lcd->v_sync0) { +u32 x; + + x =dove_read (dove_lcd,LCD_TV_CONTROL1 ); + x &= ~(VSYNC_L_OFFSET_MASK |VSYNC_H_OFFSET_MASK ); +if (isr &IRQ_GRA_FRAME0 ) + x |= dove_lcd->v_sync0; +else + x |= dove_lcd->v_sync1; +dove_write (dove_lcd,LCD_TV_CONTROL1 , x); + } + if (dove_lcd->vblank_enabled) +#endif +drm_handle_vblank (drm, dove_lcd->num); +spin_lock_irqsave (&drm->event_lock, flags); + event = dove_lcd->event; + dove_lcd->event =NULL ; +if (event) +drm_send_vblank_event (drm, dove_lcd->num, event); +spin_unlock_irqrestore (&drm->event_lock, flags); +if (event) +drm_vblank_put (drm, dove_lcd->num); + } + +return IRQ_HANDLED ; +} + +/* initialize a lcd */ +static int dove_crtc_init (struct dove_lcd *dove_lcd) +{ +struct drm_crtc *crtc = &dove_lcd->crtc; +int ret; + +DRM_DEBUG_DRIVER ("\n" ); + + dove_lcd->dpms =DRM_MODE_DPMS_OFF ; + + ret =drm_crtc_init (dove_drm.drm, crtc, &dove_crtc_funcs); +if (ret <0 ) + goto fail; + +dove_write (dove_lcd,SPU_IRQ_ENA ,0 );/* disable interrupts */ + ret =devm_request_irq (dove_lcd->dev, dove_lcd->irq, dove_lcd_irq,0 , + dove_lcd->name, dove_lcd); +if (ret <0 ) { +dev_err (dove_lcd->dev,"unable to request irq %d\n" , + dove_lcd->irq); + goto fail; + } + +if (ret <0 ) { +dev_err (dove_lcd->dev,"failed to install IRQ handler\n" ); + goto fail; + } + +dove_set_defaults (dove_lcd); +set_dumb_panel_control (dove_lcd,1 ); + +drm_crtc_helper_add (crtc, &dove_crtc_helper_funcs); + +return 0 ; + +fail: +dove_crtc_destroy (crtc); +return ret; +} + +/* ----------------------------------------------------------------------------- +* Overlay plane +*/ + +static void plane_update_base (struct dove_lcd *dove_lcd, +int plane_num, +struct drm_framebuffer *fb, +int fmt, +int x,int y, +int w,int h) +{ +struct drm_gem_cma_object *gem; +unsigned int addr; + +DRM_DEBUG_DRIVER ("\n" ); + + gem =drm_fb_cma_get_gem_obj (fb, plane_num); + + addr = gem->paddr + fb->offsets[0 ] + y * fb->pitches[0 ] + x; +dove_write (dove_lcd,LCD_SPU_DMA_START_ADDR_Y0 , addr); + +switch (fmt) { +case VMODE_YUV422PLANAR : +case VMODE_YUV420PLANAR : + addr += w * h /2 ;/* planar */ +break ; + } +dove_write (dove_lcd,LCD_SPU_DMA_START_ADDR_U0 , addr); + +switch (fmt) { +case VMODE_YUV422PLANAR : + addr += w * h /2 ; +break ; +case VMODE_YUV420PLANAR : + addr += w * h /4 ; +break ; + } +dove_write (dove_lcd,LCD_SPU_DMA_START_ADDR_V0 , addr); + +switch (fb->pixel_format) { +case VMODE_YUV422PACKED : +dove_write (dove_lcd,LCD_SPU_DMA_PITCH_YC , +LCD_Y_C (w *2 ,0 )); +dove_write (dove_lcd,LCD_SPU_DMA_PITCH_UV ,LCD_U_V (w, w)); +break ; +default : +/* case VMODE_YUV422PLANAR: */ +/* case VMODE_YUV420PLANAR: */ +dove_write (dove_lcd,LCD_SPU_DMA_PITCH_YC ,LCD_Y_C (w,0 )); +dove_write (dove_lcd,LCD_SPU_DMA_PITCH_UV , +LCD_U_V (w /2 , w /2 )); +break ; + } +} + +static int dove_plane_update (struct drm_plane *plane, +struct drm_crtc *crtc, +struct drm_framebuffer *fb, +int crtc_x,int crtc_y, +unsigned int crtc_w,unsigned int crtc_h, +uint32_t src_x,uint32_t src_y, +uint32_t src_w,uint32_t src_h) +{ +struct dove_lcd *dove_lcd =to_dove_lcd (crtc); +u32 x, x_bk; +int fmt, rbswap; + +DRM_DEBUG_DRIVER ("%d\n" , plane == &dove_lcd->planes[PLANE_VID ]); + +if (plane != &dove_lcd->planes[PLANE_VID ]) +return 0 ; + rbswap =1 ;/* default */ +switch (fb->pixel_format) { +case DRM_FORMAT_YVYU : + rbswap =0 ; +case DRM_FORMAT_YUYV : +case DRM_FORMAT_UYVY : + fmt =VMODE_YUV422PACKED ; +break ; +case DRM_FORMAT_YVU422 : + rbswap =0 ; +case DRM_FORMAT_YUV422 : + fmt =VMODE_YUV422PLANAR ; +break ; +case DRM_FORMAT_YVU420 : + rbswap =0 ; +default : +/* case DRM_FORMAT_YUV420: */ + fmt =VMODE_YUV420PLANAR ; +break ; + } + + x_bk = x =dove_read (dove_lcd,LCD_SPU_DMA_CTRL0 ); +/* clear video layer's field */ + x &= ~(CFG_YUV2RGB_DMA |CFG_DMA_SWAP_MASK | +CFG_DMA_TSTMODE |CFG_DMA_HSMOOTH |CFG_DMA_FTOGGLE | +CFG_DMAFORMAT_MASK |CFG_PALETTE_ENA ); + x |=CFG_DMA_HSMOOTH ;/* enable horizontal smooth scaling */ + x |=CFG_DMAFORMAT (fmt);/* configure hardware pixel format */ +/*fixme: no RGB */ +if (fb->pixel_format ==DRM_FORMAT_UYVY ) { + x |=CFG_YUV2RGB_DMA ; + }else if (fmt ==VMODE_YUV422PACKED ) { + x |=CFG_YUV2RGB_DMA | +CFG_DMA_SWAPYU | +CFG_DMA_SWAPRB ; +if (rbswap) + x |=CFG_DMA_SWAPUV ; + }else {/* planar */ + x |=CFG_YUV2RGB_DMA | +CFG_DMA_SWAPRB ; +if (!rbswap) + x |=CFG_DMA_SWAPUV ; + } +if (x != x_bk) +dove_write (dove_lcd,LCD_SPU_DMA_CTRL0 , x); + +/* set the dma addresses */ +plane_update_base (dove_lcd,PLANE_VID , + fb, fmt, src_x, src_y, src_w, src_h); + +/* original size */ +dove_write (dove_lcd,LCD_SPU_DMA_HPXL_VLN , +LCD_H_V (src_w, src_h)); + +/* scaled size */ +dove_write (dove_lcd,LCD_SPU_DZM_HPXL_VLN , +LCD_H_V (crtc_w, crtc_h)); + +/* update video position offset */ +dove_write (dove_lcd,LCD_SPUT_DMA_OVSA_HPXL_VLN , +LCD_H_V (crtc_x, crtc_y)); +return 0 ; +} + +static int dove_plane_disable (struct drm_plane *plane) +{ +struct dove_lcd *dove_lcd =to_dove_lcd (plane->crtc); + +DRM_DEBUG_DRIVER ("%d\n" , + plane == &dove_lcd->planes[PLANE_VID ]); + +if (plane != &dove_lcd->planes[PLANE_VID ]) +return 0 ; + +dove_clear (dove_lcd,LCD_SPU_DMA_CTRL0 ,CFG_DMA_ENA ); +return 0 ; +} + +static void dove_plane_destroy (struct drm_plane *plane) +{ +dove_plane_disable (plane); +drm_plane_cleanup (plane); +} + +static const struct drm_plane_funcs plane_funcs = { + .update_plane = dove_plane_update, + .disable_plane = dove_plane_disable, + .destroy = dove_plane_destroy, +}; +static const uint32_t gfx_formats[] = { +DRM_FORMAT_BGR888 , +DRM_FORMAT_RGB888 , +DRM_FORMAT_XBGR8888 , +DRM_FORMAT_XRGB8888 , +DRM_FORMAT_ABGR8888 , +DRM_FORMAT_ARGB8888 , +DRM_FORMAT_YVYU , +DRM_FORMAT_YUYV , +DRM_FORMAT_YVU422 , +DRM_FORMAT_YUV422 , +DRM_FORMAT_YVU420 , +DRM_FORMAT_YUV420 , +}; +static const uint32_t vid_formats[] = { +DRM_FORMAT_YVYU , +DRM_FORMAT_YUYV , +DRM_FORMAT_YVU422 , +DRM_FORMAT_YUV422 , +DRM_FORMAT_YVU420 , +DRM_FORMAT_YUV420 , +DRM_FORMAT_UYVY , +}; + +static int dove_planes_init (struct dove_lcd *dove_lcd) +{ +struct drm_device *drm = dove_drm.drm; +struct drm_plane *plane; +int ret; + +if (false) { + plane = &dove_lcd->planes[PLANE_VID ]; + ret =drm_plane_init (drm, plane,1 << dove_lcd->num, + &plane_funcs, + gfx_formats,ARRAY_SIZE (gfx_formats), true); +if (ret <0 ) +return ret; + plane->crtc = &dove_lcd->crtc; + } + plane = &dove_lcd->planes[PLANE_VID ]; + ret =drm_plane_init (drm, plane,1 << dove_lcd->num, + &plane_funcs, + vid_formats,ARRAY_SIZE (vid_formats), false); +if (ret <0 ) +return ret; + plane->crtc = &dove_lcd->crtc; +return ret; +} + +/* ----------------------------------------------------------------------------- +* Initialization +*/ + +int dove_lcd_init (struct dove_lcd *dove_lcd) +{ +int ret; + + ret =dove_crtc_init (dove_lcd); +if (ret <0 ) +return ret; + ret =dove_planes_init (dove_lcd); +if (ret <0 ) +dev_err (dove_lcd->dev,"failed to create the planes\n" ); + +return ret; +} + +/* at probe time, get the possible LCD clocks */ +static int get_lcd_clocks (struct dove_lcd *dove_lcd) +{ +struct device *dev = dove_lcd->dev; +struct device_node *np = dev->of_node; +struct of_phandle_args clkspec; +struct clk *clk; +int i, no_clock, ret; + + no_clock =1 ; +for (i =0 ; i <MAX_CLK ; i++) { + +/* check first if there is a phandle to the clock */ + ret =of_parse_phandle_with_args (np, +"clocks" ,"#clock-cells" , i, + &clkspec); +if (ret) +continue ;/* no defined clock here */ +of_node_put (clkspec.np); + +/* if no clock driver, ignore this clock */ + clk =of_clk_get (np, i); +if (IS_ERR (clk)) { +if (!dove_drm.probe_defer) { + dove_drm.probe_defer =1 ; +return -EPROBE_DEFER ; + } +dev_err (dev,"no driver for clock %i\n" , i); +continue ; + } +DRM_DEBUG_DRIVER ("clock %d ok\n" , i); +clk_prepare_enable (clk); + dove_lcd->clk[i] = clk; + no_clock =0 ; + } +if (no_clock) { +dev_err (dev,"no available clock\n" ); +return -EINVAL ; + } +if (!dove_lcd->clk[SCLK_SRC_PLLDIV ] && !dove_lcd->clk[SCLK_SRC_AXI ]) { +dev_err (dev,"no fixed clock\n" ); +return -EINVAL ; + } +return 0 ; +} + +static int dove_lcd_remove (struct platform_device *pdev) +{ +struct dove_lcd *dove_lcd =platform_get_drvdata (pdev); +struct clk *clk; +int i; + +dove_write (dove_lcd,SPU_IRQ_ENA ,0 );/* disable interrupts */ + +if (dove_drm.lcds[dove_lcd->num] == dove_lcd) + dove_drm.lcds[dove_lcd->num] =NULL ; + +for (i =0 ; i <MAX_CLK ; i++) { + clk = dove_lcd->clk[i]; +if (clk) { +clk_disable_unprepare (clk); +clk_put (clk); + } + } + +kfree (dove_lcd); +return 0 ; +} + +static int dove_lcd_probe (struct platform_device *pdev) +{ +struct device *dev = &pdev->dev; +struct device_node *np = dev->of_node; +struct dove_lcd *dove_lcd; +struct resource *res; +int ret; + +DRM_DEBUG_DRIVER ("\n" ); + +/* bail out early if no DT data */ +if (!np) { +dev_err (dev,"no device-tree\n" ); +return -ENXIO ; + } + + dove_lcd =kzalloc (sizeof *dove_lcd,GFP_KERNEL ); +if (!dove_lcd) { +dev_err (dev,"failed to allocate private data\n" ); +return -ENOMEM ; + } +platform_set_drvdata (pdev, dove_lcd); + dove_lcd->dev = dev; + + res =platform_get_resource (pdev,IORESOURCE_MEM ,0 ); +if (!res) { +dev_err (dev,"failed to get memory resource\n" ); + ret = -EINVAL ; + goto fail; + } + + dove_lcd->mmio =devm_ioremap_resource (dev, res); +if (IS_ERR (dove_lcd->mmio)) { +dev_err (dev,"failed map registers\n" ); + ret =PTR_ERR (dove_lcd->mmio); + dove_lcd->mmio =NULL ; + goto fail; + } + +switch (((u32 ) dove_lcd->mmio) &DOVE_LCD_REG_BASE_MASK ) { +case DOVE_LCD0_REG_BASE : +/* dove_lcd->num = 0; */ +break ; +case DOVE_LCD1_REG_BASE : + dove_lcd->num =1 ; +break ; +default : +dev_err (dev,"unknown lcd reg base %08x\n" , + (u32 ) dove_lcd->mmio); + ret = -EINVAL ; + goto fail; + } +snprintf (dove_lcd->name,sizeof dove_lcd->name,"dove-lcd%d" , + dove_lcd->num); + dove_drm.lcds[dove_lcd->num] = dove_lcd; + + dove_lcd->irq =irq_of_parse_and_map (np,0 ); +if (dove_lcd->irq <0 || dove_lcd->irq ==NO_IRQ ) { +dev_err (dev,"unable to get irq lcd %d\n" , dove_lcd->num); + ret = -EINVAL ; + goto fail; + } + + ret =get_lcd_clocks (dove_lcd); +if (ret <0 ) + goto fail; + +/* check the presence of a possible external slave encoder */ + ret =dove_ec_probe (dove_lcd); +if (ret <0 ) + goto fail; + +/* init done, try to initialize the drm driver */ +return dove_probed (); + +fail: +dove_lcd_remove (pdev); +return ret; +} + +static struct of_device_id dove_lcd_of_match[] = { + { .compatible ="marvell,dove-lcd" }, + { }, +}; +struct platform_driver dove_lcd_platform_driver = { + .driver = { + .owner =THIS_MODULE , + .name ="dove-lcd" , + .of_match_table = dove_lcd_of_match, + }, + .probe = dove_lcd_probe, + .remove = dove_lcd_remove, +};drivers/gpu/drm/dove/dove_dcon.h new file +64
-0@@ -0,0 +1,64 @@ +/* +* Display controller registers of Marvell DOVE +* +* Copyright (C) 2013 +* Jean-Francois Moine <[email protected]> +* Sebastian Hesselbarth <[email protected]> +* +* 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 _DOVE_DCON_H_ +#define _DOVE_DCON_H_ + +/* ------------< DCON register >------------ */ + +#define DCON_CTL0 0x0000 +#define VGA_CLK_DISABLE BIT(25) +#define DCON_CLK_DISABLE BIT(24) +#define DCON_RST BIT(23) +#define LCD_Disable BIT(17) +#define Reverse_Scan BIT(10) +#define LCD_Port_B_Select_MASK 0x00000300 +#define Port_B_Select_LCD1 0x00000000 +#define Port_B_Select_LCD0 0x00000100 +#define Port_B_Select_A_copy 0x00000300 +#define LCD_Port_A_Select_MASK 0x000000c0 +#define Port_A_Select_LCD 0x00000000 +#define Port_A_Select_OLPC 0x00000040 +#define Port_A_Select_Dual 0x00000080 +#define Port_A_Select_ext 0x000000c0 +#define LBUF_EN BIT(5) +#define DCON_IRQ_CTL 0x0008 +#define IRQ_Control_MASK 0x00ff0000 +#define DITHER_REG_R 0x0050 +#define DITHER_REG_G 0x0054 +#define DITHER_REG_B 0x0058 +#define DCON_DITHER_PAT_RL 0x0060 +#define DCON_DITHER_PAT_RH 0x0064 +#define DCON_DITHER_PAT_GL 0x0068 +#define DCON_DITHER_PAT_GH 0x006c +#define DCON_DITHER_PAT_BL 0x0070 +#define DCON_DITHER_PAT_BH 0x0074 +#define VGA_Global 0x0080 +#define VGA_CHA 0x0084 +#define VGA_CHB 0x0088 +#define VGA_CHC 0x008c +#define VGA_CHA_STA 0x0090 +#define VGA_CHB_STA 0x0094 +#define VGA_CHC_STA 0x0098 +#define CT_LUT_INDEX 0x00a4 +#define CT_LUT_DATA 0x00a8 +#define FTDLL_CTL 0x00c0 + +#endif /* _DOVE_DCON_H_ */ drivers/gpu/drm/dove/dove_drv.c new file +380
-0@@ -0,0 +1,380 @@ +/* +* Marvell Dove DRM driver - main +* +* Copyright (C) 2013 +* Jean-Francois Moine <[email protected]> +* Sebastian Hesselbarth <[email protected]> +* +* 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/module.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <linux/of.h> + +#include "dove_drv.h" + +#define DRIVER_NAME "dove-drm" +#define DRIVER_DESC "Marvell Dove DRM" +#define DRIVER_DATE "20130516" +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 0 + +struct dove_drm dove_drm; +static struct platform_device *g_pdev; +static atomic_t probed; + +static struct drm_framebuffer *dove_fb_create (struct drm_device *drm, +struct drm_file *file_priv, +struct drm_mode_fb_cmd2 *mode_cmd) +{ +DRM_DEBUG_DRIVER ("fmt %.4s\n" , (char *) &mode_cmd->pixel_format); + +switch (mode_cmd->pixel_format) { +case DRM_FORMAT_BGR888 : +case DRM_FORMAT_RGB888 : +case DRM_FORMAT_XBGR8888 : +case DRM_FORMAT_XRGB8888 : +case DRM_FORMAT_ABGR8888 : +case DRM_FORMAT_ARGB8888 : +case DRM_FORMAT_YVYU : +case DRM_FORMAT_YUYV : +case DRM_FORMAT_YVU422 : +case DRM_FORMAT_YUV422 : +case DRM_FORMAT_YVU420 : +case DRM_FORMAT_YUV420 : +break ; +default : +return ERR_PTR (-EINVAL ); + } +return drm_fb_cma_create (drm, file_priv, mode_cmd); +} + +static void dove_fb_output_poll_changed (struct drm_device *drm) +{ +DRM_DEBUG_DRIVER ("fb:%d\n" , dove_drm.fbdev !=NULL ); +if (dove_drm.fbdev) +drm_fbdev_cma_hotplug_event (dove_drm.fbdev); +} + +static const struct drm_mode_config_funcs mode_config_funcs = { + .fb_create = dove_fb_create, + .output_poll_changed = dove_fb_output_poll_changed, +}; + +/* +* DRM operations: +*/ +static int dove_unload (struct drm_device *drm) +{ +struct dove_lcd *dove_lcd; +int i; + +DRM_DEBUG_DRIVER ("\n" ); + +for (i =0 ; i <MAX_DOVE_LCD ; i++) { + dove_lcd = dove_drm.lcds[i]; +if (dove_lcd) { +if (dove_lcd->planes[PLANE_VID ].dev) +drm_plane_cleanup (&dove_lcd->planes[PLANE_VID ]); +if (dove_lcd->planes[PLANE_GFX ].dev) +drm_plane_cleanup (&dove_lcd->planes[PLANE_GFX ]); + } + } +drm_kms_helper_poll_fini (drm); +drm_mode_config_cleanup (drm); +drm_vblank_cleanup (drm); + +return 0 ; +} + +/* this function is called when all LCDs and dcon have been probed */ +static int dove_load (struct drm_device *drm,unsigned long flags) +{ +struct platform_device *pdev = drm->platformdev; +struct dove_lcd *dove_lcd; +int i, ret; + +DRM_DEBUG_DRIVER ("\n" ); + + dove_drm.drm = drm; +platform_set_drvdata (pdev, &dove_drm); + drm->dev_private = &dove_drm; + +drm_mode_config_init (drm); + +/* pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); */ + +for (i =0 ; i <MAX_DOVE_LCD ; i++) { + dove_lcd = dove_drm.lcds[i]; +if (dove_lcd) { + ret =dove_lcd_init (dove_lcd); +if (ret <0 ) + goto fail; + ret =dove_ec_init (dove_lcd); +if (ret <0 ) + goto fail; + } + } + + drm->mode_config.min_width =0 ; + drm->mode_config.min_height =0 ; + drm->mode_config.max_width =2048 ; + drm->mode_config.max_height =2048 ; + drm->mode_config.funcs = &mode_config_funcs; + + ret =drm_vblank_init (drm, drm->mode_config.num_crtc); +if (ret <0 ) { +dev_err (drm->dev,"failed to initialize vblank\n" ); + goto fail; + } + + dove_drm.fbdev =drm_fbdev_cma_init (drm, +32 ,/* bpp */ + drm->mode_config.num_crtc, + drm->mode_config.num_connector); + +drm_kms_helper_poll_init (drm); +return 0 ; +fail: +dove_unload (drm); +return ret; +} + +static void dove_preclose (struct drm_device *drm,struct drm_file *file) +{ +struct dove_lcd *dove_lcd; +int i; + +for (i =0 ; i <MAX_DOVE_LCD ; i++) { + dove_lcd = dove_drm.lcds[i]; +if (dove_lcd) +dove_crtc_cancel_page_flip (dove_lcd, file); + } +} + +static void dove_lastclose (struct drm_device *drm) +{ +drm_fbdev_cma_restore_mode (dove_drm.fbdev); +} + +static int dove_gem_cma_dumb_create (struct drm_file *file_priv, +struct drm_device *dev, +struct drm_mode_create_dumb *args) +{ +if (args->height * args->width * args->bpp ==0 ) { +dev_err (dev->dev,"dumb_create %dx%d bpp %d!\n" , + args->height, args->width, args->bpp); +return -ENOMEM ; + } +return drm_gem_cma_dumb_create (file_priv, dev, args); +} + +static const struct file_operations fops = { + .owner =THIS_MODULE , + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, + .poll = drm_poll, + .read = drm_read, + .fasync = drm_fasync, + .llseek = no_llseek, + .mmap = drm_gem_cma_mmap, +}; + +static struct drm_driver dove_driver = { + .driver_features =DRIVER_GEM |DRIVER_MODESET , + .load = dove_load, + .unload = dove_unload, + .preclose = dove_preclose, + .lastclose = dove_lastclose, + .get_vblank_counter = dove_vblank_count, + .enable_vblank = dove_enable_vblank, + .disable_vblank = dove_disable_vblank, + .gem_free_object = drm_gem_cma_free_object, + .gem_vm_ops = &drm_gem_cma_vm_ops, + .dumb_create = dove_gem_cma_dumb_create, + .dumb_map_offset = drm_gem_cma_dumb_map_offset, + .dumb_destroy = drm_gem_cma_dumb_destroy, +#ifdef CONFIG_DEBUG_FS + .debugfs_init = dove_debugfs_init, + .debugfs_cleanup = dove_debugfs_cleanup, +#endif + .fops = &fops, + + .name =DRIVER_NAME , + .desc =DRIVER_DESC , + .date =DRIVER_DATE , + .major =DRIVER_MAJOR , + .minor =DRIVER_MINOR , +}; + +#ifdef CONFIG_PM_SLEEP +/* +* Power management +*/ +static int dove_pm_suspend (struct device *dev) +{ +struct drm_device *drm =dev_get_drvdata (dev); +struct dove_lcd *dove_lcd; +int i; + +drm_kms_helper_poll_disable (drm); +for (i =0 ; i <MAX_DOVE_LCD ; i++) { + dove_lcd = dove_drm.lcds[i]; +if (dove_lcd) +dove_crtc_stop (dove_lcd); + } +return 0 ; +} + +static int dove_pm_resume (struct device *dev) +{ +struct drm_device *drm =dev_get_drvdata (dev); +struct dove_lcd *dove_lcd; +int i; + +for (i =0 ; i <MAX_DOVE_LCD ; i++) { + dove_lcd = dove_drm.lcds[i]; +if (dove_lcd + && dove_lcd->dpms ==DRM_MODE_DPMS_ON ) +dove_crtc_start (dove_lcd); + } +drm_kms_helper_poll_enable (drm); +return 0 ; +} +#endif + +static const struct dev_pm_ops dove_pm_ops = { +SET_SYSTEM_SLEEP_PM_OPS (dove_pm_suspend, dove_pm_resume) +}; + +/* +* Platform driver +*/ + +/* count the number of awaited sub devices */ +static int dove_subdev_cnt (void ) +{ +struct device_node *np; +unsigned int n; +static struct of_device_id dove_of_subdev[] = { + { .compatible ="marvell,dove-lcd" }, + { .compatible ="marvell,dove-dcon" }, + { }, + }; + + n =0 ; + np =NULL ; +for (;;) { + np =of_find_matching_node_and_match (np, + dove_of_subdev,NULL ); +if (!np) +break ; +if (of_device_is_available (np)) + n++; + } +return n; +} + +int dove_probed (void ) +{ +if (atomic_add_return (1 , &probed) ==0 ) +return drm_platform_init (&dove_driver, g_pdev); +return 0 ; +} + +static int dove_pdev_probe (struct platform_device *pdev) +{ +int awaited; + +DRM_DEBUG_DRIVER ("\n" ); + + g_pdev = pdev; + + awaited =dove_subdev_cnt (); +if (awaited ==0 ) { +dev_err (&pdev->dev,"no lcd nor dcon devices\n" ); +return -ENXIO ; + } +if (atomic_sub_return (awaited, &probed) ==0 ) +return drm_platform_init (&dove_driver, pdev); +return 0 ; +} + +static int dove_pdev_remove (struct platform_device *pdev) +{ +drm_platform_exit (&dove_driver, pdev); +return 0 ; +} + +static struct of_device_id dove_of_match[] = { + { .compatible ="marvell,dove-video" }, + { }, +}; +MODULE_DEVICE_TABLE (of, dove_of_match); + +static struct platform_driver dove_platform_driver = { + .probe = dove_pdev_probe, + .remove = dove_pdev_remove, + .driver = { + .owner =THIS_MODULE , + .name ="dove-drm" , + .pm = &dove_pm_ops, + .of_match_table = dove_of_match, + }, +}; + +static int __init dove_drm_init (void ) +{ +int ret; + +/* wait for other drivers to be loaded (si5351, tda998x..) */ +msleep (200 ); + +/* uncomment to activate the drm trace at startup time */ +/* drm_debug = DRM_UT_CORE | DRM_UT_DRIVER | DRM_UT_KMS; */ + +DRM_DEBUG_DRIVER ("\n" ); + + ret =platform_driver_register (&dove_lcd_platform_driver); +if (ret <0 ) +return ret; + ret =platform_driver_register (&dove_dcon_platform_driver); +if (ret <0 ) + goto out1; + ret =platform_driver_register (&dove_platform_driver); +if (ret <0 ) + goto out2; +return 0 ; + +out2: +platform_driver_unregister (&dove_dcon_platform_driver); +out1: +platform_driver_unregister (&dove_lcd_platform_driver); +return ret; +} +static void __exit dove_drm_fini(void) +{ +platform_driver_unregister (&dove_platform_driver); +platform_driver_unregister (&dove_dcon_platform_driver); +platform_driver_unregister (&dove_lcd_platform_driver); +} +module_init (dove_drm_init); +module_exit (dove_drm_fini); + +MODULE_AUTHOR ("Jean-Francois Moine <[email protected]>" ); +MODULE_AUTHOR ("Sebastian Hesselbarth <[email protected]>" ); +MODULE_DESCRIPTION ("Marvell Dove DRM Driver" ); +MODULE_LICENSE ("GPL" );drivers/gpu/drm/dove/dove_drv.h new file +93
-0@@ -0,0 +1,93 @@ +/* +* Copyright (C) 2013 Jean-François Moine +* +* 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 __DOVE_DRV_H__ +#define __DOVE_DRV_H__ + +#include <linux/clk.h> +#include <drm/drmP.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_encoder_slave.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_fb_cma_helper.h> + +/* (not tested) */ +/*#define HANDLE_INTERLACE 1*/ + +#define MAX_DOVE_LCD 2/* max number of dove lcd devices */ +#define MAX_CLK 4/* max number of clocks per crtc */ + +#define PLANE_GFX 0 +#define PLANE_VID 1 +#define NPLANES 2 + +struct dove_lcd { +void __iomem *mmio; +struct device *dev; +struct drm_crtc crtc; + +u8 num;/* index in dove_drm */ +u8 dpms; + +#ifdef HANDLE_INTERLACE +u8 vblank_enabled; +u32 v_sync0; +u32 v_sync1; +#endif + +short clk_src;/* current clock source */ +struct clk *clk[MAX_CLK ]; + +int irq; +char name[16 ]; + +struct drm_pending_vblank_event *event; + +struct drm_plane planes[NPLANES ]; + +struct drm_connector connector; +struct drm_encoder_slave encoder_slave; +}; + +struct dove_drm { +struct drm_device *drm; +struct dove_lcd *lcds[MAX_DOVE_LCD ]; + +struct drm_fbdev_cma *fbdev; +int probe_defer; +}; + +extern struct dove_drm dove_drm; +int dove_probed (void ); + +u32 dove_vblank_count (struct drm_device *dev,int crtc); +int dove_enable_vblank (struct drm_device *dev,int crtc); +void dove_disable_vblank (struct drm_device *dev,int crtc); +int dove_lcd_init (struct dove_lcd *dove_lcd); +void dove_crtc_cancel_page_flip (struct dove_lcd *dove_lcd, +struct drm_file *file); +void dove_crtc_start (struct dove_lcd *dove_lcd); +void dove_crtc_stop (struct dove_lcd *dove_lcd); +#ifdef CONFIG_DEBUG_FS +int dove_debugfs_init (struct drm_minor *minor); +void dove_debugfs_cleanup (struct drm_minor *minor); +#endif +extern struct platform_driver dove_lcd_platform_driver; + +int dove_ec_probe (struct dove_lcd *dove_lcd); +int dove_ec_init (struct dove_lcd *dove_lcd); +extern struct platform_driver dove_dcon_platform_driver; +#endif /* __DOVE_DRV_H__ */ drivers/gpu/drm/dove/dove_ec.c new file +570
-0@@ -0,0 +1,570 @@ +/* +* Marvell Dove DRM driver - encoder / connector and display controller +* +* Copyright (C) 2013 +* Jean-Francois Moine <[email protected]> +* Sebastian Hesselbarth <[email protected]> +* +* 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/i2c.h> +#include <linux/of_i2c.h> +#include <linux/module.h> + +#include "dove_drv.h" +#include "dove_dcon.h" + +struct dove_dcon { +void __iomem *mmio; +struct device *dev; +}; +static struct dove_dcon dove_dcon; + +/* ----------------------------------------------------------------------------- +* Encoder +*/ +/* LVDS and VGA/DAC functions */ +static void dove_encoder_dpms (struct drm_encoder *encoder,int mode) +{ +} +static bool dove_encoder_mode_fixup (struct drm_encoder *encoder, +const struct drm_display_mode *mode, +struct drm_display_mode *adjusted_mode) +{ +return true; +} +static void dove_encoder_prepare (struct drm_encoder *encoder) +{ +} +static void dove_encoder_commit (struct drm_encoder *encoder) +{ +} +static void dove_encoder_mode_set (struct drm_encoder *encoder, +struct drm_display_mode *mode, +struct drm_display_mode *adjusted_mode) +{ +} + +static const struct drm_encoder_helper_funcs lvds_encoder_helper_funcs = { + .dpms = dove_encoder_dpms, + .mode_fixup = dove_encoder_mode_fixup, + .prepare = dove_encoder_prepare, + .commit = dove_encoder_commit, + .mode_set = dove_encoder_mode_set, +}; + +/* HDMI (i2c) functions */ +static const struct drm_encoder_helper_funcs hdmi_encoder_helper_funcs = { + .dpms = drm_i2c_encoder_dpms, + .mode_fixup = drm_i2c_encoder_mode_fixup, + .prepare = drm_i2c_encoder_prepare, + .commit = drm_i2c_encoder_commit, + .mode_set = drm_i2c_encoder_mode_set, +}; + +static void dove_drm_encoder_destroy (struct drm_encoder *encoder) +{ +struct drm_encoder_slave *encoder_slave =to_encoder_slave (encoder); +struct i2c_client *i2c_client; +struct module *module; + +if (encoder_slave->slave_funcs) + encoder_slave->slave_funcs->destroy (encoder); + i2c_client = encoder_slave->bus_priv; +if (i2c_client) { + module = i2c_client->driver->driver.owner; +module_put (module); + } +if (encoder->dev) +drm_encoder_cleanup (encoder); +} + +static const struct drm_encoder_funcs encoder_funcs = { + .destroy = dove_drm_encoder_destroy, +}; + +static int dove_encoder_get_hdmi (struct dove_lcd *dove_lcd) +{ +struct device *dev = dove_lcd->dev; +struct drm_device *drm = dove_drm.drm; +struct drm_encoder_slave *encoder_slave = &dove_lcd->encoder_slave; +struct drm_encoder *encoder = &encoder_slave->base; +struct drm_connector *connector = &dove_lcd->connector; +struct i2c_client *i2c_client; +struct module *module; +struct drm_i2c_encoder_driver *encoder_drv; +int ret; + + i2c_client = encoder_slave->bus_priv; +if (!i2c_client) { +dev_err (dev,"no external-encoder for hdmi\n" ); +return -EINVAL ; + } + + encoder_drv =to_drm_i2c_encoder_driver (i2c_client->driver); +if (!encoder_drv || !encoder_drv->encoder_init) { +dev_err (dev,"no external encoder init\n" ); +return -EINVAL ; + } + +/* lock the external encoder module */ + module = i2c_client->driver->driver.owner; +if (!module || !try_module_get (module)) { +dev_err (dev,"cannot get module %s\n" , module->name); +return -EINVAL ; + } + + ret = encoder_drv->encoder_init (i2c_client, drm, encoder_slave); +if (ret <0 ) { +dev_err (dev,"slave encoder init failed\n" ); +return ret; + } + encoder_slave->slave_funcs->create_resources (encoder, connector); +return ret; +} + +static int dove_encoder_init (struct dove_lcd *dove_lcd, +int mode_encoder) +{ +struct drm_device *drm = dove_drm.drm; +struct drm_encoder_slave *encoder_slave = &dove_lcd->encoder_slave; +struct drm_encoder *encoder = &encoder_slave->base; +int ret; + +DRM_DEBUG_DRIVER ("\n" ); + +/* do early init in case of error */ + ret =drm_encoder_init (drm, encoder, &encoder_funcs, mode_encoder); +if (ret <0 ) { +dev_err (dove_lcd->dev,"drm encoder init failed\n" ); +return ret; + } + + encoder->possible_crtcs =1 << dove_lcd->num; + +/* +* If the display controller is present, +* - the port A cannot be VGA/DAC, +* - the port B can be only VGA/DAC and may receive the lcd 0 output. +*/ +if (dove_dcon.mmio) { +if (dove_lcd->num ==0 ) { +if (mode_encoder ==DRM_MODE_ENCODER_DAC ) { +dev_err (dove_lcd->dev, +"bad lcd 0 port-type\n" ); +return -EINVAL ; + } + }else { +if (mode_encoder !=DRM_MODE_ENCODER_DAC ) { +dev_err (dove_lcd->dev, +"bad lcd 1 port-type\n" ); +return -EINVAL ; + } + encoder->possible_crtcs |=1 ; + +/* the port B may receive the LCD 0 output */ + encoder->possible_clones =1 ; + } + } + +switch (mode_encoder) { +case DRM_MODE_ENCODER_DAC : +/*fixme: to do */ +case DRM_MODE_ENCODER_LVDS : +drm_encoder_helper_add (encoder, &lvds_encoder_helper_funcs); + ret =0 ; +break ; +case DRM_MODE_ENCODER_TMDS : +drm_encoder_helper_add (encoder, &hdmi_encoder_helper_funcs); + ret =dove_encoder_get_hdmi (dove_lcd); +break ; + } +return ret; +} + +/* ----------------------------------------------------------------------------- +* Connector +*/ + +static int dove_lvds_get_modes (struct dove_lcd *dove_lcd) +{ +struct device *dev = dove_lcd->dev; +struct device_node *np = dev->of_node; +struct drm_connector *connector = &dove_lcd->connector; +struct drm_display_mode *mode; +int clock, hdisplay, vdisplay, hfp, hbp, vfp, vbp, hs, vs; +int w_mm, h_mm; +int ret; + +/* same as of_videomode, but simpler! */ + np =of_find_node_by_name (np,"display-timings" ); +if (!np) { +dev_err (dev,"no display-timings\n" ); +return -EINVAL ; + } + np =of_get_next_child (np,NULL ); +if (!np) { +dev_err (dev,"no 'mode' subnode in DT\n" ); +return -EINVAL ; + } + + ret =0 ; + ret |=of_property_read_u32 (np,"hactive" , &hdisplay); + ret |=of_property_read_u32 (np,"vactive" , &vdisplay); + ret |=of_property_read_u32 (np,"hfront-porch" , &hfp); + ret |=of_property_read_u32 (np,"hsync-len" , &hs); + ret |=of_property_read_u32 (np,"hback-porch" , &hbp); + ret |=of_property_read_u32 (np,"vfront-porch" , &vfp); + ret |=of_property_read_u32 (np,"vsync-len" , &vs); + ret |=of_property_read_u32 (np,"vback-porch" , &vbp); + ret |=of_property_read_u32 (np,"clock" , &clock); +if (ret) { +dev_err (dev,"bad display-timings\n" ); +return -EINVAL ; + } +if (clock <15000 || clock >150000 ) { +dev_err (dev,"bad clock\n" ); +return -EINVAL ; + } + + mode =drm_mode_create (dove_drm.drm); +if (!mode) { + ret = -ENOMEM ; +return ret; + } + + mode->clock = clock; + mode->hdisplay = hdisplay; + mode->hsync_start = hdisplay + hfp; + mode->hsync_end = mode->hsync_start + hs; + mode->htotal = mode->hsync_end + hbp; + mode->vdisplay = vdisplay; + mode->vsync_start = vdisplay + vfp; + mode->vsync_end = mode->vsync_start + vs; + mode->vtotal = mode->vsync_end + vbp; + +drm_mode_set_name (mode); + mode->vrefresh =drm_mode_vrefresh (mode); + mode->type =DRM_MODE_TYPE_DRIVER |DRM_MODE_TYPE_PREFERRED ; +drm_mode_probed_add (connector, mode); + +/* optional display dimension */ + ret =of_property_read_u32 (np,"width-mm" , &w_mm); + ret |=of_property_read_u32 (np,"height-mm" , &h_mm); +if (ret >=0 ) { + connector->display_info.width_mm = w_mm; + connector->display_info.height_mm = h_mm; + } +return 1 ; +} + +static int dove_drm_connector_get_modes (struct drm_connector *connector) +{ +struct dove_lcd *dove_lcd = + container_of(connector, struct dove_lcd, connector); +struct drm_encoder_slave *encoder_slave = &dove_lcd->encoder_slave; +int ret; + +switch (connector->connector_type) { +case DRM_MODE_CONNECTOR_VGA : +/*fixme:to do */ +case DRM_MODE_CONNECTOR_LVDS : + ret =dove_lvds_get_modes (dove_lcd); +break ; +default : +/* case DRM_MODE_CONNECTOR_HDMIA: */ +/* case DRM_MODE_CONNECTOR_HDMIB: */ + ret = encoder_slave->slave_funcs->get_modes (&encoder_slave->base, + connector); +break ; + } +DRM_DEBUG_DRIVER ("-> %d\n" , ret); +return ret; +} + +static int dove_drm_connector_mode_valid (struct drm_connector *connector, +struct drm_display_mode *mode) +{ +struct dove_lcd *dove_lcd = + container_of(connector, struct dove_lcd, connector); +struct drm_encoder_slave *encoder_slave = &dove_lcd->encoder_slave; + +if (!encoder_slave->slave_funcs) +return MODE_OK ; +return encoder_slave->slave_funcs->mode_valid (&encoder_slave->base, + mode); +} + +static struct drm_encoder * +dove_drm_connector_best_encoder (struct drm_connector *connector) +{ +struct dove_lcd *dove_lcd = + container_of(connector, struct dove_lcd, connector); + +return &dove_lcd->encoder_slave.base; +} + +static const struct drm_connector_helper_funcs connector_helper_funcs = { + .get_modes = dove_drm_connector_get_modes, + .mode_valid = dove_drm_connector_mode_valid, + .best_encoder = dove_drm_connector_best_encoder, +}; + +static void dove_drm_connector_destroy (struct drm_connector *connector) +{ +if (!connector->dev) +return ; +drm_sysfs_connector_remove (connector); +drm_connector_cleanup (connector); +} + +static enum drm_connector_status +dove_drm_connector_detect (struct drm_connector *connector,bool force) +{ +struct dove_lcd *dove_lcd = + container_of(connector, struct dove_lcd, connector); +struct drm_encoder_slave *encoder_slave = &dove_lcd->encoder_slave; + +DRM_DEBUG_DRIVER ("\n" ); +if (encoder_slave->slave_funcs) +return encoder_slave->slave_funcs->detect (&encoder_slave->base, + connector); +/*fixme: KO with VGA*/ +return connector_status_connected; +} + +static void dove_drm_connector_dpms (struct drm_connector *connector, +int mode) +{ +struct dove_lcd *dove_lcd = + container_of(connector, struct dove_lcd, connector); +struct drm_encoder *encoder = connector->encoder; +struct drm_encoder_slave *encoder_slave =to_encoder_slave (encoder); +struct dove_lcd *dove_lcd2 = + container_of(encoder_slave, struct dove_lcd, encoder_slave); +int modeA, modeB; +u32 reg; + +if (mode == dove_lcd->connector.dpms) +return ; + +/* adjust the port B input */ +if (dove_dcon.mmio) { + reg =readl (dove_dcon.mmio +DCON_CTL0 ); + reg &= ~LCD_Port_B_Select_MASK; +if (dove_lcd2 != dove_lcd) { +if (dove_lcd->num ==0 ) { + modeA = mode; + modeB = dove_lcd2->connector.dpms; + }else { + modeA = dove_lcd->connector.dpms; + modeB = mode; + } + +if (modeB ==DRM_MODE_DPMS_ON ) { +if (modeA ==DRM_MODE_DPMS_ON ) + reg |= Port_B_Select_A_copy; +else + reg |= Port_B_Select_LCD0; + } + } +writel (reg, dove_dcon.mmio +DCON_CTL0 ); +DRM_DEBUG_DRIVER ("port B select %08x\n" , reg); + } + +drm_helper_connector_dpms (connector, mode); +} + +static int dove_connector_set_property (struct drm_connector *connector, +struct drm_property *property, +uint64_t value) +{ +struct dove_lcd *dove_lcd = + container_of(connector, struct dove_lcd, connector); +struct drm_encoder_slave *encoder_slave = &dove_lcd->encoder_slave; + +DRM_DEBUG_DRIVER ("\n" ); +if (!encoder_slave->slave_funcs) +return 0 ; +return encoder_slave->slave_funcs->set_property (&encoder_slave->base, + connector, + property, + value); +} + +static const struct drm_connector_funcs connector_funcs = { + .destroy = dove_drm_connector_destroy, + .dpms = dove_drm_connector_dpms, + .detect = dove_drm_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = dove_connector_set_property, +}; + +/* initialize the couple connector-encoder of a LCD */ +int dove_ec_init (struct dove_lcd *dove_lcd) +{ +struct device *dev = dove_lcd->dev; +struct device_node *np = dev->of_node; +struct drm_device *drm = dove_drm.drm; +struct drm_connector *connector = &dove_lcd->connector; +struct drm_encoder_slave *encoder_slave = &dove_lcd->encoder_slave; +struct drm_encoder *encoder = &encoder_slave->base; +u32 port_type; +int mode_encoder, ret; + +DRM_DEBUG_DRIVER ("\n" ); + +/* get the port (connector) type */ +if (of_property_read_u32 (np,"marvell,port-type" , &port_type)) { +dev_err (dev,"no port-type\n" ); +return -EINVAL ; + } +switch (port_type) { +case DRM_MODE_CONNECTOR_VGA :/* 1 */ + mode_encoder =DRM_MODE_ENCODER_DAC ; +break ; +case DRM_MODE_CONNECTOR_LVDS :/* 7 */ + mode_encoder =DRM_MODE_ENCODER_LVDS ; +break ; +case DRM_MODE_CONNECTOR_HDMIA :/* 11 */ +case DRM_MODE_CONNECTOR_HDMIB :/* 12 */ + mode_encoder =DRM_MODE_ENCODER_TMDS ; +break ; +default : +dev_err (dev,"bad port type %d\n" , port_type); +return -EINVAL ; + } + + ret =drm_connector_init (drm, connector, &connector_funcs, port_type); +if (ret <0 ) +return ret; + +drm_connector_helper_add (connector, &connector_helper_funcs); + +#ifdef HANDLE_INTERLACE + connector->interlace_allowed = true; +#endif + + ret =dove_encoder_init (dove_lcd, mode_encoder); +if (ret <0 ) + goto err; + + ret =drm_mode_connector_attach_encoder (connector, encoder); +if (ret <0 ) + goto err; + + connector->encoder = encoder; + + ret =drm_sysfs_connector_add (connector); +if (ret <0 ) + goto err; + +drm_helper_connector_dpms (connector,DRM_MODE_DPMS_OFF ); + ret =drm_object_property_set_value (&connector->base, + drm->mode_config.dpms_property, +DRM_MODE_DPMS_OFF ); +if (ret <0 ) + goto err; +return 0 ; + +err: +dev_err (dev,"dove_ec_init err %d\n" , ret); +dove_drm_encoder_destroy (&encoder_slave->base); +drm_connector_cleanup (connector); +return ret; +} + +/* at probe time, check the presence of a possible external slave encoder */ +int dove_ec_probe (struct dove_lcd *dove_lcd) +{ +struct device *dev = dove_lcd->dev; +struct device_node *np = dev->of_node; +struct device_node *i2c_node; +struct i2c_client *i2c_client; + +/* get the optional external encoder */ + i2c_node =of_parse_phandle (np,"marvell,external-encoder" ,0 ); +if (!i2c_node) +return 0 ; + + i2c_client =of_find_i2c_device_by_node (i2c_node); +of_node_put (i2c_node); +if (!i2c_client) { +dev_err (dev,"bad external-encoder\n" ); +return -EINVAL ; + } + +/* check if the slave-encoder module is initialized */ +if (!i2c_client->driver) { +if (dove_drm.probe_defer) { +dev_err (dev,"cannot get the external-encoder\n" ); +return -EINVAL ; + } + dove_drm.probe_defer =1 ; +return -EPROBE_DEFER ; + } + + dove_lcd->encoder_slave.bus_priv = i2c_client; +return 0 ; +} + +/* ----------------------------------------------------------------------------- +* Display controller +*/ + +static int dove_dcon_remove (struct platform_device *pdev) +{ + dove_dcon.mmio =NULL ; +return 0 ; +} + +static int dove_dcon_probe (struct platform_device *pdev) +{ +struct resource *res; +void __iomem *mmio; + +DRM_DEBUG_DRIVER ("\n" ); + + res =platform_get_resource (pdev,IORESOURCE_MEM ,0 ); +if (!res) { +dev_err (&pdev->dev,"failed to get dcon resource\n" ); +return -EINVAL ; + } + + mmio =devm_ioremap_resource (&pdev->dev, res); +if (IS_ERR (mmio)) { +dev_err (&pdev->dev,"failed map dcon registers\n" ); +return PTR_ERR (mmio); + } + + dove_dcon.mmio = mmio; + dove_dcon.dev = &pdev->dev; + +/* init done, try to initialize the drm driver */ +return dove_probed (); +} + +static struct of_device_id dove_dcon_of_match[] = { + { .compatible ="marvell,dove-dcon" }, + { }, +}; +struct platform_driver dove_dcon_platform_driver = { + .driver = { + .owner =THIS_MODULE , + .name ="dove-dcon" , + .of_match_table = dove_dcon_of_match, + }, + .probe = dove_dcon_probe, + .remove = dove_dcon_remove, +};drivers/gpu/drm/dove/dove_lcd.h new file +519
-0@@ -0,0 +1,519 @@ +/* +* LCD controller registers of Marvell DOVE +* +* Copyright (C) 2013 +* Jean-Francois Moine <[email protected]> +* Sebastian Hesselbarth <[email protected]> +* +* 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 _DOVE_LCD_H_ +#define _DOVE_LCD_H_ + +/* ------------< LCD register >------------ */ + +/* Video Frame 0&1 start address registers */ +#define LCD_TV_CONTROL1 0x0084 +#define VSYNC_L_OFFSET (o) ((o) << 20) +#define VSYNC_L_OFFSET_MASK (0xfff << 20) +#define HWC32_ENABLE BIT(13) +#define VSYNC_OFFSET_EN BIT(12) +#define VSYNC_H_OFFSET (o) (o) +#define VSYNC_H_OFFSET_MASK 0xfff + +/* Video Frame 0&1 start address registers */ +#define LCD_SPU_DMA_START_ADDR_Y0 0x00c0 +#define LCD_SPU_DMA_START_ADDR_U0 0x00c4 +#define LCD_SPU_DMA_START_ADDR_V0 0x00c8 +#define LCD_CFG_DMA_START_ADDR_0 0x00cc/* Cmd address */ +#define LCD_SPU_DMA_START_ADDR_Y1 0x00d0 +#define LCD_SPU_DMA_START_ADDR_U1 0x00d4 +#define LCD_SPU_DMA_START_ADDR_V1 0x00d8 +#define LCD_CFG_DMA_START_ADDR_1 0x00dc/* Cmd address */ + +/* YC & UV Pitch */ +#define LCD_SPU_DMA_PITCH_YC 0x00e0 +#define LCD_Y_C (y, c) (((c) << 16) | (y)) +#define LCD_SPU_DMA_PITCH_UV 0x00e4 +#define LCD_U_V (u, v) (((v) << 16) | (u)) + +/* Video Starting Point on Screen Register */ +#define LCD_SPUT_DMA_OVSA_HPXL_VLN 0x00e8 + +/* Video Size Register */ +#define LCD_SPU_DMA_HPXL_VLN 0x00ec + +/* Video Size After zooming Register */ +#define LCD_SPU_DZM_HPXL_VLN 0x00f0 + +/* Graphic Frame 0&1 Starting Address Register */ +#define LCD_CFG_GRA_START_ADDR0 0x00f4 +#define LCD_CFG_GRA_START_ADDR1 0x00f8 + +/* Graphic Frame Pitch */ +#define LCD_CFG_GRA_PITCH 0x00fc + +/* Graphic Starting Point on Screen Register */ +#define LCD_SPU_GRA_OVSA_HPXL_VLN 0x0100 + +/* Graphic Size Register */ +#define LCD_SPU_GRA_HPXL_VLN 0x0104 + +/* Graphic Size after Zooming Register */ +#define LCD_SPU_GZM_HPXL_VLN 0x0108 + +/* HW Cursor Starting Point on Screen Register */ +#define LCD_SPU_HWC_OVSA_HPXL_VLN 0x010c + +/* HW Cursor Size */ +#define LCD_SPU_HWC_HPXL_VLN 0x0110 + +/* Total Screen Size Register */ +#define LCD_SPUT_V_H_TOTAL 0x0114 + +/* Total Screen Active Size Register */ +#define LCD_SPU_V_H_ACTIVE 0x0118 +#define LCD_H_V (h, v) (((v) << 16) | (h)) +#define H_LCD (x) ((x) & 0xffff) +#define V_LCD (x) (((x) >> 16) & 0xffff) + +/* Screen H&V Porch Register */ +#define LCD_SPU_H_PORCH 0x011c +#define LCD_SPU_V_PORCH 0x0120 +#define LCD_F_B (f, b) (((b) << 16) | (f)) +#define F_LCD (x) ((x) & 0xffff) +#define B_LCD (x) (((x) >> 16) & 0xffff) + +/* Screen Blank Color Register */ +#define LCD_SPU_BLANKCOLOR 0x0124 + +/* HW Cursor Color 1&2 Register */ +#define LCD_SPU_ALPHA_COLOR1 0x0128 +#define HWC32_CFG_ALPHA (alpha) ((alpha) << 24) +#define LCD_SPU_ALPHA_COLOR2 0x012c +#define COLOR_MASK 0x00ffffff +#define COLOR_RGB (r, g, b) (((b) << 16) | ((g) << 8) | (r)) + +/* Video YUV Color Key Control */ +#define LCD_SPU_COLORKEY_Y 0x0130 +#define CFG_CKEY_Y2 (y2) ((y2) << 24) +#define CFG_CKEY_Y2_MASK 0xff000000 +#define CFG_CKEY_Y1 (y1) ((y1) << 16) +#define CFG_CKEY_Y1_MASK 0x00ff0000 +#define CFG_CKEY_Y (y) ((y) << 8) +#define CFG_CKEY_Y_MASK 0x0000ff00 +#define CFG_ALPHA_Y (y) (y) +#define CFG_ALPHA_Y_MASK 0x000000ff +#define LCD_SPU_COLORKEY_U 0x0134 +#define CFG_CKEY_U2 (u2) ((u2) << 24) +#define CFG_CKEY_U2_MASK 0xff000000 +#define CFG_CKEY_U1 (u1) ((u1) << 16) +#define CFG_CKEY_U1_MASK 0x00ff0000 +#define CFG_CKEY_U (u) ((u) << 8) +#define CFG_CKEY_U_MASK 0x0000ff00 +#define CFG_ALPHA_U (u) (u) +#define CFG_ALPHA_U_MASK 0x000000ff +#define LCD_SPU_COLORKEY_V 0x0138 +#define CFG_CKEY_V2 (v2) ((v2) << 24) +#define CFG_CKEY_V2_MASK 0xff000000 +#define CFG_CKEY_V1 (v1) ((v1) << 16) +#define CFG_CKEY_V1_MASK 0x00ff0000 +#define CFG_CKEY_V (v) ((v) << 8) +#define CFG_CKEY_V_MASK 0x0000ff00 +#define CFG_ALPHA_V (v) (v) +#define CFG_ALPHA_V_MASK 0x000000ff + +/* LCD General Configuration Register */ +#define LCD_CFG_RDREG4F 0x013c +#define LCD_SRAM_WAIT BIT(11) +#define DMA_WATERMARK_MASK 0xff +#define DMA_WATERMARK (m) (m) + +/* SPI Read Data Register */ +#define LCD_SPU_SPI_RXDATA 0x0140 + +/* Smart Panel Read Data Register */ +#define LCD_SPU_ISA_RSDATA 0x0144 +#define ISA_RXDATA_16BIT_1_DATA_MASK 0x000000ff +#define ISA_RXDATA_16BIT_2_DATA_MASK 0x0000ff00 +#define ISA_RXDATA_16BIT_3_DATA_MASK 0x00ff0000 +#define ISA_RXDATA_16BIT_4_DATA_MASK 0xff000000 +#define ISA_RXDATA_32BIT_1_DATA_MASK 0x00ffffff + +/* HWC SRAM Read Data Register */ +#define LCD_SPU_HWC_RDDAT 0x0158 + +/* Gamma Table SRAM Read Data Register */ +#define LCD_SPU_GAMMA_RDDAT 0x015c +#define GAMMA_RDDAT_MASK 0x000000ff + +/* Palette Table SRAM Read Data Register */ +#define LCD_SPU_PALETTE_RDDAT 0x0160 +#define PALETTE_RDDAT_MASK 0x00ffffff + +/* I/O Pads Input Read Only Register */ +#define LCD_SPU_IOPAD_IN 0x0178 +#define IOPAD_IN_MASK 0x0fffffff + +/* Reserved Read Only Registers */ +#define LCD_CFG_RDREG5F 0x017c +#define IRE_FRAME_CNT_MASK 0x000000c0 +#define IPE_FRAME_CNT_MASK 0x00000030 +#define GRA_FRAME_CNT_MASK 0x0000000c/* Graphic */ +#define DMA_FRAME_CNT_MASK 0x00000003/* Video */ + +/* SPI Control Register. */ +#define LCD_SPU_SPI_CTRL 0x0180 +#define CFG_SCLKCNT (div) ((div) << 24) +#define CFG_SCLKCNT_MASK 0xff000000 +#define CFG_RXBITS (rx) ((rx) << 16) +#define CFG_RXBITS_MASK 0x00ff0000 +#define CFG_TXBITS (tx) ((tx) << 8) +#define CFG_TXBITS_MASK 0x0000ff00 +#define CFG_CLKINV BIT(7) +#define CFG_KEEPXFER BIT(6) +#define CFG_RXBITSTO0 BIT(5) +#define CFG_TXBITSTO0 BIT(4) +#define CFG_SPI_ENA BIT(3) +#define CFG_SPI_SEL BIT(2) +#define CFG_SPI_3W4WB BIT(1) +#define CFG_SPI_START BIT(0) + +/* SPI Tx Data Register */ +#define LCD_SPU_SPI_TXDATA 0x0184 + +/* +* 1. Smart Pannel 8-bit Bus Control Register. +* 2. AHB Slave Path Data Port Register +*/ +#define LCD_SPU_SMPN_CTRL 0x0188 + +/* DMA Control 0 Register */ +#define LCD_SPU_DMA_CTRL0 0x0190 +#define CFG_NOBLENDING BIT(31) +#define CFG_GAMMA_ENA BIT(30) +#define CFG_CBSH_ENA BIT(29) +#define CFG_PALETTE_ENA BIT(28) +#define CFG_ARBFAST_ENA BIT(27) +#define CFG_HWC_1BITMOD BIT(26) +#define CFG_HWC_1BITENA BIT(25) +#define CFG_HWC_ENA BIT(24) +#define CFG_DMAFORMAT (dmaformat) ((dmaformat) << 20) +#define CFG_DMAFORMAT_MASK 0x00f00000 +#define CFG_GRAFORMAT (graformat) ((graformat) << 16) +#define CFG_GRAFORMAT_MASK 0x000f0000 +/* for graphic part */ +#define CFG_GRA_FTOGGLE BIT(15) +#define CFG_GRA_HSMOOTH BIT(14) +#define CFG_GRA_TSTMODE BIT(13) +#define CFG_GRA_SWAPRB BIT(12) +#define CFG_GRA_SWAPUV BIT(11) +#define CFG_GRA_SWAPYU BIT(10) +#define CFG_YUV2RGB_GRA BIT(9) +#define CFG_GRA_ENA BIT(8) +/* for video part */ +#define CFG_DMA_FTOGGLE BIT(7) +#define CFG_DMA_HSMOOTH BIT(6) +#define CFG_DMA_TSTMODE BIT(5) +#define CFG_DMA_SWAPRB BIT(4) +#define CFG_DMA_SWAPUV BIT(3) +#define CFG_DMA_SWAPYU BIT(2) +#define CFG_DMA_SWAP_MASK 0x0000001c +#define CFG_YUV2RGB_DMA BIT(1) +#define CFG_DMA_ENA BIT(0) + +/* DMA Control 1 Register */ +#define LCD_SPU_DMA_CTRL1 0x0194 +#define CFG_FRAME_TRIG BIT(31) +#define CFG_VSYNC_TRIG (trig) ((trig) << 28) +#define CFG_VSYNC_TRIG_MASK 0x70000000 +#define CFG_VSYNC_INV BIT(27) +#define CFG_COLOR_KEY_MODE (cmode) ((cmode) << 24) +#define CFG_COLOR_KEY_MASK 0x07000000 +#define CFG_CARRY BIT(23) +#define CFG_GATED_ENA BIT(21) +#define CFG_PWRDN_ENA BIT(20) +#define CFG_DSCALE (dscale) ((dscale) << 18) +#define CFG_DSCALE_MASK 0x000c0000 +#define CFG_ALPHA_MODE (amode) ((amode) << 16) +#define CFG_ALPHA_MODE_MASK 0x00030000 +#define CFG_ALPHA (alpha) ((alpha) << 8) +#define CFG_ALPHA_MASK 0x0000ff00 +#define CFG_PXLCMD (pxlcmd) (pxlcmd) +#define CFG_PXLCMD_MASK 0x000000ff + +/* SRAM Control Register */ +#define LCD_SPU_SRAM_CTRL 0x0198 +#define CFG_SRAM_INIT_WR_RD (mode) ((mode) << 14) +#define CFG_SRAM_INIT_WR_RD_MASK 0x0000c000 +#define CFG_SRAM_ADDR_LCDID (id) ((id) << 8) +#define CFG_SRAM_ADDR_LCDID_MASK 0x00000f00 +#define CFG_SRAM_ADDR (addr) (addr) +#define CFG_SRAM_ADDR_MASK 0x000000ff + +/* SRAM Write Data Register */ +#define LCD_SPU_SRAM_WRDAT 0x019c + +/* SRAM RTC/WTC Control Register */ +#define LCD_SPU_SRAM_PARA0 0x01a0 + +/* SRAM Power Down Control Register */ +#define LCD_SPU_SRAM_PARA1 0x01a4 +#define CFG_CSB_256x32 BIT(15)/* HWC */ +#define CFG_CSB_256x24 BIT(14)/* Palette */ +#define CFG_CSB_256x8 BIT(13)/* Gamma */ +#define CFG_PDWN256x32 BIT(7)/* HWC */ +#define CFG_PDWN256x24 BIT(6)/* Palette */ +#define CFG_PDWN256x8 BIT(5)/* Gamma */ +#define CFG_PDWN32x32 BIT(3) +#define CFG_PDWN16x66 BIT(2) +#define CFG_PDWN32x66 BIT(1) +#define CFG_PDWN64x66 BIT(0) + +/* Smart or Dumb Panel Clock Divider */ +#define LCD_CFG_SCLK_DIV 0x01a8 +#define SET_SCLK (src, div, frac) (((src) << 30) | ((frac) << 16 ) | (div)) + +/* Video Contrast Register */ +#define LCD_SPU_CONTRAST 0x01ac +#define CFG_BRIGHTNESS (bright) ((bright) << 16) +#define CFG_BRIGHTNESS_MASK 0xffff0000 +#define CFG_CONTRAST (contrast) (contrast) +#define CFG_CONTRAST_MASK 0x0000ffff + +/* Video Saturation Register */ +#define LCD_SPU_SATURATION 0x01b0 +#define CFG_C_MULTS (mult) ((mult) << 16) +#define CFG_C_MULTS_MASK 0xffff0000 +#define CFG_SATURATION (sat) (sat) +#define CFG_SATURATION_MASK 0x0000ffff + +/* Video Hue Adjust Register */ +#define LCD_SPU_CBSH_HUE 0x01b4 +#define CFG_SIN0 (sin0) ((sin0) << 16) +#define CFG_SIN0_MASK 0xffff0000 +#define CFG_COS0 (con0) (con0) +#define CFG_COS0_MASK 0x0000ffff + +/* Dump LCD Panel Control Register */ +#define LCD_SPU_DUMB_CTRL 0x01b8 +#define CFG_DUMBMODE (mode) ((mode) << 28) +#define CFG_DUMBMODE_MASK 0xf0000000 +#define CFG_LCDGPIO_O (data) ((data) << 20) +#define CFG_LCDGPIO_O_MASK 0x0ff00000 +#define CFG_LCDGPIO_ENA (gpio) ((gpio) << 12) +#define CFG_LCDGPIO_ENA_MASK 0x000ff000 +#define CFG_BIAS_OUT BIT(8) +#define CFG_REVERSE_RGB BIT(7) +#define CFG_INV_COMPBLANK BIT(6) +#define CFG_INV_COMPSYNC BIT(5) +#define CFG_INV_HENA BIT(4) +#define CFG_INV_VSYNC BIT(3) +#define CFG_INV_HSYNC BIT(2) +#define CFG_INV_PCLK BIT(1) +#define CFG_DUMB_ENA BIT(0) + +/* LCD I/O Pads Control Register */ +#define SPU_IOPAD_CONTROL 0x01bc +#define CFG_VSC_LINEAR (vm) ((vm) << 18)/* gfx */ +#define CFG_VSC_LINEAR_MASK 0x000c0000 +#define CFG_GRA_VM_ENA BIT(15)/* gfx */ +#define CFG_DMA_VM_ENA BIT(14)/* video */ +#define CFG_CMD_VM_ENA BIT(13) +#define CFG_CSC (csc) ((csc) << 8) +#define CFG_CSC_MASK 0x00000300 +#define CFG_AXICTRL (axi) ((axi) << 4) +#define CFG_AXICTRL_MASK 0x000000f0 +#define CFG_IOPADMODE (iopad) (iopad) +#define CFG_IOPADMODE_MASK 0x0000000f + +/* LCD Interrupt Control Register */ +#define SPU_IRQ_ENA 0x1c0 +/* LCD Interrupt Status Register */ +#define SPU_IRQ_ISR 0x1c4 +#define IRQ_DMA_FRAME0 BIT(31) +#define IRQ_DMA_FRAME1 BIT(30) +#define IRQ_DMA_FIFO_UNDERFLOW BIT(29) +#define IRQ_GRA_FRAME0 BIT(27) +#define IRQ_GRA_FRAME1 BIT(26) +#define IRQ_GRA_FIFO_UNDERFLOW BIT(25) +#define IRQ_SMART_VSYNC BIT(23) +#define IRQ_DUMB_FRAME_DONE BIT(22) +#define IRQ_SMART_FRAME_DONE BIT(21) +#define IRQ_HWCURSOR_FRAME_DONE BIT(20) +#define IRQ_AHB_CMD_EMPTY BIT(19) +#define IRQ_SPI_TRANSFER_DONE BIT(18) +#define IRQ_POWERDOWN BIT(17) +#define IRQ_AXI_ERROR BIT(16) +/* read only status */ +#define STA_DMA_FRAME0 BIT(15) +#define STA_DMA_FRAME1 BIT(14) +#define STA_DMA_FRAME_COUNT (x) (((x) & (BIT(13) | BIT(12))) >> 12) +#define STA_GRA_FRAME0 BIT(11) +#define STA_GRA_FRAME1 BIT(10) +#define STA_GRA_FRAME_COUNT (x) (((x) & (BIT(9) | BIT(8))) >> 8) +#define STA_SMART_VSYNC BIT(7) +#define STA_DUMB_FRAME_DONE BIT(6) +#define STA_SMART_FRAME_DONE BIT(5) +#define STA_HWCURSOR_FRAME_DONE BIT(4) +#define STA_AHB_CMD_EMPTY BIT(3) +#define STA_DMA_FIFO_EMPTY BIT(2) +#define STA_GRA_FIFO_EMPTY BIT(1) +#define STA_POWERDOWN BIT(0) + +#define IRQ_DMA_FRAME_DONE (IRQ_DMA_FRAME0 | IRQ_DMA_FRAME1) +#define IRQ_GRA_FRAME_DONE \ + (IRQ_GRA_FRAME0 | IRQ_GRA_FRAME1 | IRQ_SMART_VSYNC) + +/* +* defined Video Memory Color format for DMA control 0 register +* DMA0 bit[23:20] +*/ +#define VMODE_RGB565 0x0 +#define VMODE_RGB1555 0x1 +#define VMODE_RGB888PACKED 0x2 +#define VMODE_RGB888UNPACKED 0x3 +#define VMODE_RGBA888 0x4 +#define VMODE_YUV422PACKED 0x5 +#define VMODE_YUV422PLANAR 0x6 +#define VMODE_YUV420PLANAR 0x7 +#define VMODE_SMPNCMD 0x8 +#define VMODE_PALETTE4BIT 0x9 +#define VMODE_PALETTE8BIT 0xa +#define VMODE_RESERVED 0xb + +/* +* defined Graphic Memory Color format for DMA control 0 register +* DMA0 bit[19:16] +*/ +#define GMODE_RGB565 0x0 +#define GMODE_RGB1555 0x1 +#define GMODE_RGB888PACKED 0x2 +#define GMODE_RGB888UNPACKED 0x3 +#define GMODE_RGBA888 0x4 +#define GMODE_YUV422PACKED 0x5 +#define GMODE_YUV422PLANAR 0x6 +#define GMODE_YUV420PLANAR 0x7 +#define GMODE_SMPNCMD 0x8 +#define GMODE_PALETTE4BIT 0x9 +#define GMODE_PALETTE8BIT 0xa +#define GMODE_RESERVED 0xb + +/* +* define for DMA control 1 register +*/ +#define DMA1_FRAME_TRIG 31/* bit location */ +#define DMA1_VSYNC_MODE 28 +#define DMA1_VSYNC_INV 27 +#define DMA1_CKEY 24 +#define DMA1_CARRY 23 +#define DMA1_LNBUF_ENA 22 +#define DMA1_GATED_ENA 21 +#define DMA1_PWRDN_ENA 20 +#define DMA1_DSCALE 18 +#define DMA1_ALPHA_MODE 16 +#define DMA1_ALPHA 8 +#define DMA1_PXLCMD 0 + +/* +* defined for Configure Dumb Mode +* DUMB LCD Panel bit[31:28] +*/ +#define DUMB16_RGB565_0 0x0 +#define DUMB16_RGB565_1 0x1 +#define DUMB18_RGB666_0 0x2 +#define DUMB18_RGB666_1 0x3 +#define DUMB12_RGB444_0 0x4 +#define DUMB12_RGB444_1 0x5 +#define DUMB24_RGB888_0 0x6 +#define DUMB_BLANK 0x7 + +/* +* defined for Configure I/O Pin Allocation Mode +* LCD LCD I/O Pads control register bit[3:0] +*/ +#define IOPAD_DUMB24 0x0 +#define IOPAD_DUMB18SPI 0x1 +#define IOPAD_DUMB18GPIO 0x2 +#define IOPAD_DUMB16SPI 0x3 +#define IOPAD_DUMB16GPIO 0x4 +#define IOPAD_DUMB12 0x5 +#define IOPAD_SMART18SPI 0x6 +#define IOPAD_SMART16SPI 0x7 +#define IOPAD_SMART8BOTH 0x8 + +/* +* clock source SCLK_Source bit[31:30] +*/ +#define SCLK_SRC_AXI 0 +#define SCLK_SRC_EXTCLK0 1 +#define SCLK_SRC_PLLDIV 2 +#define SCLK_SRC_EXTCLK1 3 + +/* +* defined Dumb Panel Clock Divider register +* SCLK_Source bit[31] +*/ +#define AXI_BUS_SEL 0x80000000/* 0: PLL clock select*/ +#define CCD_CLK_SEL 0x40000000 +#define DCON_CLK_SEL 0x20000000 +#define IDLE_CLK_INT_DIV 0x1/* idle Integer Divider */ +#define DIS_CLK_INT_DIV 0x0/* Disable Integer Divider */ + +/* SRAM ID */ +#define SRAMID_GAMMA_YR 0x0 +#define SRAMID_GAMMA_UG 0x1 +#define SRAMID_GAMMA_VB 0x2 +#define SRAMID_PALETTE 0x3 +#define SRAMID_HWC32_RAM1 0xc +#define SRAMID_HWC32_RAM2 0xd +#define SRAMID_HWC32_RAM3 0xe +#define SRAMID_HWC 0xf + +/* SRAM INIT Read/Write */ +#define SRAMID_INIT_READ 0x0 +#define SRAMID_INIT_WRITE 0x2 +#define SRAMID_INIT_DEFAULT 0x3 + +/* Lines of this file are not shown. View the raw message.
The rest of this diff is not shown. View the raw message.
Signature · 8 lines
-- Ken ar c'hentañ | ** Breizh ha Linux atav! ** Jef | http://moinejf.free.fr/ _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Part of the diff is not shown here. View the raw message.