diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-10-31 02:30:01 +0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-10-31 02:30:01 +0400 |
commit | acff987d94cbdb4049f3706bed1f1792f8ef6837 (patch) | |
tree | 36bded977aab5d80cf9aeb5467f83dd554a25d01 /drivers/video | |
parent | ce949717b559709423c1ef716a9db16d1dcadaed (diff) | |
parent | b57287ba497b63a0d87a058631bbddfed9392e9f (diff) | |
download | linux-acff987d94cbdb4049f3706bed1f1792f8ef6837.tar.xz |
Merge branch 'fbdev-next' of git://github.com/schandinat/linux-2.6
* 'fbdev-next' of git://github.com/schandinat/linux-2.6: (270 commits)
video: platinumfb: Add __devexit_p at necessary place
drivers/video: fsl-diu-fb: merge diu_pool into fsl_diu_data
drivers/video: fsl-diu-fb: merge diu_hw into fsl_diu_data
drivers/video: fsl-diu-fb: only DIU modes 0 and 1 are supported
drivers/video: fsl-diu-fb: remove unused panel operating mode support
drivers/video: fsl-diu-fb: use an enum for the AOI index
drivers/video: fsl-diu-fb: add several new video modes
drivers/video: fsl-diu-fb: remove broken screen blanking support
drivers/video: fsl-diu-fb: move some definitions out of the header file
drivers/video: fsl-diu-fb: fix some ioctls
video: da8xx-fb: Increased resolution configuration of revised LCDC IP
OMAPDSS: picodlp: add missing #include <linux/module.h>
fb: fix au1100fb bitrot.
mx3fb: fix NULL pointer dereference in screen blanking.
video: irq: Remove IRQF_DISABLED
smscufx: change edid data to u8 instead of char
OMAPDSS: DISPC: zorder support for DSS overlays
OMAPDSS: DISPC: VIDEO3 pipeline support
OMAPDSS/OMAP_VOUT: Fix incorrect OMAP3-alpha compatibility setting
video/omap: fix build dependencies
...
Fix up conflicts in:
- drivers/staging/xgifb/XGI_main_26.c
Changes to XGIfb_pan_var()
- drivers/video/omap/{lcd_apollon.c,lcd_ldp.c,lcd_overo.c}
Removed (or in the case of apollon.c, merged into the generic
DSS panel in drivers/video/omap2/displays/panel-generic-dpi.c)
Diffstat (limited to 'drivers/video')
126 files changed, 11087 insertions, 7197 deletions
diff --git a/drivers/video/68328fb.c b/drivers/video/68328fb.c index 75a39eab70c3..a425d65d5ba2 100644 --- a/drivers/video/68328fb.c +++ b/drivers/video/68328fb.c @@ -378,8 +378,8 @@ static int mc68x328fb_pan_display(struct fb_var_screeninfo *var, || var->xoffset) return -EINVAL; } else { - if (var->xoffset + var->xres > info->var.xres_virtual || - var->yoffset + var->yres > info->var.yres_virtual) + if (var->xoffset + info->var.xres > info->var.xres_virtual || + var->yoffset + info->var.yres > info->var.yres_virtual) return -EINVAL; } info->var.xoffset = var->xoffset; diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 549b960667c8..8165c5577d71 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -259,6 +259,15 @@ config FB_TILEBLITTING comment "Frame buffer hardware drivers" depends on FB +config FB_GRVGA + tristate "Aeroflex Gaisler framebuffer support" + depends on FB && SPARC + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + ---help--- + This enables support for the SVGACTRL framebuffer in the GRLIB IP library from Aeroflex Gaisler. + config FB_CIRRUS tristate "Cirrus Logic support" depends on FB && (ZORRO || PCI) @@ -1756,9 +1765,10 @@ config FB_AU1100 config FB_AU1200 bool "Au1200 LCD Driver" depends on (FB = y) && MIPS && SOC_AU1200 - select FB_CFB_FILLRECT - select FB_CFB_COPYAREA - select FB_CFB_IMAGEBLIT + select FB_SYS_FILLRECT + select FB_SYS_COPYAREA + select FB_SYS_IMAGEBLIT + select FB_SYS_FOPS help This is the framebuffer driver for the AMD Au1200 SOC. It can drive various panels and CRTs by passing in kernel cmd line option @@ -2027,7 +2037,7 @@ config FB_TMIO_ACCELL config FB_S3C tristate "Samsung S3C framebuffer support" - depends on FB && S3C_DEV_FB + depends on FB && (S3C_DEV_FB || S5P_DEV_FIMD0) select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT @@ -2110,6 +2120,22 @@ config FB_SM501 If unsure, say N. +config FB_SMSCUFX + tristate "SMSC UFX6000/7000 USB Framebuffer support" + depends on FB && USB + select FB_MODE_HELPERS + select FB_SYS_FILLRECT + select FB_SYS_COPYAREA + select FB_SYS_IMAGEBLIT + select FB_SYS_FOPS + select FB_DEFERRED_IO + ---help--- + This is a kernel framebuffer driver for SMSC UFX USB devices. + Supports fbdev clients like xf86-video-fbdev, kdrive, fbi, and + mplayer -vo fbdev. Supports both UFX6000 (USB 2.0) and UFX7000 + (USB 3.0) devices. + To compile as a module, choose M here: the module name is smscufx. + config FB_UDL tristate "Displaylink USB Framebuffer support" depends on FB && USB diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 8b83129e209c..9b9d8fff7732 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_FB_AMIGA) += amifb.o c2p_planar.o obj-$(CONFIG_FB_ARC) += arcfb.o obj-$(CONFIG_FB_CLPS711X) += clps711xfb.o obj-$(CONFIG_FB_CYBER2000) += cyber2000fb.o +obj-$(CONFIG_FB_GRVGA) += grvga.o obj-$(CONFIG_FB_PM2) += pm2fb.o obj-$(CONFIG_FB_PM3) += pm3fb.o @@ -127,6 +128,7 @@ obj-$(CONFIG_FB_IBM_GXT4500) += gxt4500.o obj-$(CONFIG_FB_PS3) += ps3fb.o obj-$(CONFIG_FB_SM501) += sm501fb.o obj-$(CONFIG_FB_UDL) += udlfb.o +obj-$(CONFIG_FB_SMSCUFX) += smscufx.o obj-$(CONFIG_FB_XILINX) += xilinxfb.o obj-$(CONFIG_SH_MIPI_DSI) += sh_mipi_dsi.o obj-$(CONFIG_FB_SH_MOBILE_HDMI) += sh_mobile_hdmi.o diff --git a/drivers/video/acornfb.c b/drivers/video/acornfb.c index 6183a57eb69d..b303f1715065 100644 --- a/drivers/video/acornfb.c +++ b/drivers/video/acornfb.c @@ -850,9 +850,10 @@ acornfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) u_int y_bottom = var->yoffset; if (!(var->vmode & FB_VMODE_YWRAP)) - y_bottom += var->yres; + y_bottom += info->var.yres; - BUG_ON(y_bottom > var->yres_virtual); + if (y_bottom > info->var.yres_virtual) + return -EINVAL; acornfb_update_dma(info, var); diff --git a/drivers/video/arkfb.c b/drivers/video/arkfb.c index 8686429cbdf0..555dd4c64f5b 100644 --- a/drivers/video/arkfb.c +++ b/drivers/video/arkfb.c @@ -908,13 +908,14 @@ static int arkfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info unsigned int offset; /* Calculate the offset */ - if (var->bits_per_pixel == 0) { - offset = (var->yoffset / 16) * (var->xres_virtual / 2) + (var->xoffset / 2); + if (info->var.bits_per_pixel == 0) { + offset = (var->yoffset / 16) * (info->var.xres_virtual / 2) + + (var->xoffset / 2); offset = offset >> 2; } else { offset = (var->yoffset * info->fix.line_length) + - (var->xoffset * var->bits_per_pixel / 8); - offset = offset >> ((var->bits_per_pixel == 4) ? 2 : 3); + (var->xoffset * info->var.bits_per_pixel / 8); + offset = offset >> ((info->var.bits_per_pixel == 4) ? 2 : 3); } /* Set the offset */ diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c index dda920623c6a..4ac48d9ee665 100644 --- a/drivers/video/atmel_lcdfb.c +++ b/drivers/video/atmel_lcdfb.c @@ -39,7 +39,8 @@ | FBINFO_HWACCEL_YPAN) static inline void atmel_lcdfb_update_dma2d(struct atmel_lcdfb_info *sinfo, - struct fb_var_screeninfo *var) + struct fb_var_screeninfo *var, + struct fb_info *info) { } @@ -50,14 +51,16 @@ static inline void atmel_lcdfb_update_dma2d(struct atmel_lcdfb_info *sinfo, | FBINFO_HWACCEL_YPAN) static void atmel_lcdfb_update_dma2d(struct atmel_lcdfb_info *sinfo, - struct fb_var_screeninfo *var) + struct fb_var_screeninfo *var, + struct fb_info *info) { u32 dma2dcfg; u32 pixeloff; - pixeloff = (var->xoffset * var->bits_per_pixel) & 0x1f; + pixeloff = (var->xoffset * info->var.bits_per_pixel) & 0x1f; - dma2dcfg = ((var->xres_virtual - var->xres) * var->bits_per_pixel) / 8; + dma2dcfg = (info->var.xres_virtual - info->var.xres) + * info->var.bits_per_pixel / 8; dma2dcfg |= pixeloff << ATMEL_LCDC_PIXELOFF_OFFSET; lcdc_writel(sinfo, ATMEL_LCDC_DMA2DCFG, dma2dcfg); @@ -249,14 +252,14 @@ static void atmel_lcdfb_update_dma(struct fb_info *info, unsigned long dma_addr; dma_addr = (fix->smem_start + var->yoffset * fix->line_length - + var->xoffset * var->bits_per_pixel / 8); + + var->xoffset * info->var.bits_per_pixel / 8); dma_addr &= ~3UL; /* Set framebuffer DMA base address and pixel offset */ lcdc_writel(sinfo, ATMEL_LCDC_DMABADDR1, dma_addr); - atmel_lcdfb_update_dma2d(sinfo, var); + atmel_lcdfb_update_dma2d(sinfo, var, info); } static inline void atmel_lcdfb_free_video_memory(struct atmel_lcdfb_info *sinfo) diff --git a/drivers/video/aty/radeon_base.c b/drivers/video/aty/radeon_base.c index 32f8cf6200a7..150684882ef7 100644 --- a/drivers/video/aty/radeon_base.c +++ b/drivers/video/aty/radeon_base.c @@ -845,16 +845,16 @@ static int radeonfb_pan_display (struct fb_var_screeninfo *var, { struct radeonfb_info *rinfo = info->par; - if ((var->xoffset + var->xres > var->xres_virtual) - || (var->yoffset + var->yres > var->yres_virtual)) - return -EINVAL; + if ((var->xoffset + info->var.xres > info->var.xres_virtual) + || (var->yoffset + info->var.yres > info->var.yres_virtual)) + return -EINVAL; if (rinfo->asleep) return 0; radeon_fifo_wait(2); - OUTREG(CRTC_OFFSET, ((var->yoffset * var->xres_virtual + var->xoffset) - * var->bits_per_pixel / 8) & ~7); + OUTREG(CRTC_OFFSET, (var->yoffset * info->fix.line_length + + var->xoffset * info->var.bits_per_pixel / 8) & ~7); return 0; } diff --git a/drivers/video/au1100fb.c b/drivers/video/au1100fb.c index 01a8fde67f20..649cb35de4ed 100644 --- a/drivers/video/au1100fb.c +++ b/drivers/video/au1100fb.c @@ -110,12 +110,6 @@ static struct fb_var_screeninfo au1100fb_var __devinitdata = { .vmode = FB_VMODE_NONINTERLACED, }; -static struct au1100fb_drv_info drv_info; - -static int nocursor = 0; -module_param(nocursor, int, 0644); -MODULE_PARM_DESC(nocursor, "cursor enable/disable"); - /* fb_blank * Blank the screen. Depending on the mode, the screen will be * activated with the backlight color, or desactivated @@ -132,7 +126,7 @@ static int au1100fb_fb_blank(int blank_mode, struct fb_info *fbi) /* Turn on panel */ fbdev->regs->lcd_control |= LCD_CONTROL_GO; #ifdef CONFIG_MIPS_PB1100 - if (drv_info.panel_idx == 1) { + if (fbdev->panel_idx == 1) { au_writew(au_readw(PB1100_G_CONTROL) | (PB1100_G_CONTROL_BL | PB1100_G_CONTROL_VDD), PB1100_G_CONTROL); @@ -147,7 +141,7 @@ static int au1100fb_fb_blank(int blank_mode, struct fb_info *fbi) /* Turn off panel */ fbdev->regs->lcd_control &= ~LCD_CONTROL_GO; #ifdef CONFIG_MIPS_PB1100 - if (drv_info.panel_idx == 1) { + if (fbdev->panel_idx == 1) { au_writew(au_readw(PB1100_G_CONTROL) & ~(PB1100_G_CONTROL_BL | PB1100_G_CONTROL_VDD), PB1100_G_CONTROL); @@ -428,17 +422,6 @@ int au1100fb_fb_mmap(struct fb_info *fbi, struct vm_area_struct *vma) return 0; } -/* fb_cursor - * Used to disable cursor drawing... - */ -int au1100fb_fb_cursor(struct fb_info *info, struct fb_cursor *cursor) -{ - if (nocursor) - return 0; - else - return -EINVAL; /* just to force soft_cursor() call */ -} - static struct fb_ops au1100fb_ops = { .owner = THIS_MODULE, @@ -450,13 +433,53 @@ static struct fb_ops au1100fb_ops = .fb_imageblit = cfb_imageblit, .fb_rotate = au1100fb_fb_rotate, .fb_mmap = au1100fb_fb_mmap, - .fb_cursor = au1100fb_fb_cursor, }; /*-------------------------------------------------------------------------*/ -/* AU1100 LCD controller device driver */ +static int au1100fb_setup(struct au1100fb_device *fbdev) +{ + char *this_opt, *options; + int num_panels = ARRAY_SIZE(known_lcd_panels); + + if (num_panels <= 0) { + print_err("No LCD panels supported by driver!"); + return -ENODEV; + } + + if (fb_get_options(DRIVER_NAME, &options)) + return -ENODEV; + if (!options) + return -ENODEV; + + while ((this_opt = strsep(&options, ",")) != NULL) { + /* Panel option */ + if (!strncmp(this_opt, "panel:", 6)) { + int i; + this_opt += 6; + for (i = 0; i < num_panels; i++) { + if (!strncmp(this_opt, known_lcd_panels[i].name, + strlen(this_opt))) { + fbdev->panel = &known_lcd_panels[i]; + fbdev->panel_idx = i; + break; + } + } + if (i >= num_panels) { + print_warn("Panel '%s' not supported!", this_opt); + return -ENODEV; + } + } + /* Unsupported option */ + else + print_warn("Unsupported option \"%s\"", this_opt); + } + + print_info("Panel=%s", fbdev->panel->name); + + return 0; +} static int __devinit au1100fb_drv_probe(struct platform_device *dev) { @@ -465,22 +488,21 @@ static int __devinit au1100fb_drv_probe(struct platform_device *dev) unsigned long page; u32 sys_clksrc; - if (!dev) - return -EINVAL; - /* Allocate new device private */ - if (!(fbdev = kzalloc(sizeof(struct au1100fb_device), GFP_KERNEL))) { + fbdev = kzalloc(sizeof(struct au1100fb_device), GFP_KERNEL); + if (!fbdev) { print_err("fail to allocate device private record"); return -ENOMEM; } - fbdev->panel = &known_lcd_panels[drv_info.panel_idx]; + if (au1100fb_setup(fbdev)) + goto failed; platform_set_drvdata(dev, (void *)fbdev); /* Allocate region for our registers and map them */ - if (!(regs_res = platform_get_resource(to_platform_device(dev), - IORESOURCE_MEM, 0))) { + regs_res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!regs_res) { print_err("fail to retrieve registers resource"); return -EFAULT; } @@ -500,13 +522,11 @@ static int __devinit au1100fb_drv_probe(struct platform_device *dev) print_dbg("Register memory map at %p", fbdev->regs); print_dbg("phys=0x%08x, size=%d", fbdev->regs_phys, fbdev->regs_len); - - /* Allocate the framebuffer to the maximum screen size * nbr of video buffers */ fbdev->fb_len = fbdev->panel->xres * fbdev->panel->yres * (fbdev->panel->bpp >> 3) * AU1100FB_NBR_VIDEO_BUFFERS; - fbdev->fb_mem = dma_alloc_coherent(dev, PAGE_ALIGN(fbdev->fb_len), + fbdev->fb_mem = dma_alloc_coherent(&dev->dev, PAGE_ALIGN(fbdev->fb_len), &fbdev->fb_phys, GFP_KERNEL); if (!fbdev->fb_mem) { print_err("fail to allocate frambuffer (size: %dK))", @@ -525,7 +545,7 @@ static int __devinit au1100fb_drv_probe(struct platform_device *dev) page < PAGE_ALIGN((unsigned long)fbdev->fb_mem + fbdev->fb_len); page += PAGE_SIZE) { #if CONFIG_DMA_NONCOHERENT - SetPageReserved(virt_to_page(CAC_ADDR(page))); + SetPageReserved(virt_to_page(CAC_ADDR((void *)page))); #else SetPageReserved(virt_to_page(page)); #endif @@ -578,7 +598,8 @@ failed: release_mem_region(fbdev->regs_phys, fbdev->regs_len); } if (fbdev->fb_mem) { - dma_free_noncoherent(dev, fbdev->fb_len, fbdev->fb_mem, fbdev->fb_phys); + dma_free_noncoherent(&dev->dev, fbdev->fb_len, fbdev->fb_mem, + fbdev->fb_phys); } if (fbdev->info.cmap.len != 0) { fb_dealloc_cmap(&fbdev->info.cmap); @@ -608,7 +629,8 @@ int au1100fb_drv_remove(struct platform_device *dev) release_mem_region(fbdev->regs_phys, fbdev->regs_len); - dma_free_coherent(dev, PAGE_ALIGN(fbdev->fb_len), fbdev->fb_mem, fbdev->fb_phys); + dma_free_coherent(&dev->dev, PAGE_ALIGN(fbdev->fb_len), fbdev->fb_mem, + fbdev->fb_phys); fb_dealloc_cmap(&fbdev->info.cmap); kfree(fbdev->info.pseudo_palette); @@ -675,101 +697,18 @@ static struct platform_driver au1100fb_driver = { .resume = au1100fb_drv_resume, }; -/*-------------------------------------------------------------------------*/ - -/* Kernel driver */ - -int au1100fb_setup(char *options) -{ - char* this_opt; - int num_panels = ARRAY_SIZE(known_lcd_panels); - char* mode = NULL; - int panel_idx = 0; - - if (num_panels <= 0) { - print_err("No LCD panels supported by driver!"); - return -EFAULT; - } - - if (options) { - while ((this_opt = strsep(&options,",")) != NULL) { - /* Panel option */ - if (!strncmp(this_opt, "panel:", 6)) { - int i; - this_opt += 6; - for (i = 0; i < num_panels; i++) { - if (!strncmp(this_opt, - known_lcd_panels[i].name, - strlen(this_opt))) { - panel_idx = i; - break; - } - } - if (i >= num_panels) { - print_warn("Panel %s not supported!", this_opt); - } - } - if (!strncmp(this_opt, "nocursor", 8)) { - this_opt += 8; - nocursor = 1; - print_info("Cursor disabled"); - } - /* Mode option (only option that start with digit) */ - else if (isdigit(this_opt[0])) { - mode = kstrdup(this_opt, GFP_KERNEL); - if (!mode) { - print_err("memory allocation failed"); - return -ENOMEM; - } - } - /* Unsupported option */ - else { - print_warn("Unsupported option \"%s\"", this_opt); - } - } - } - - drv_info.panel_idx = panel_idx; - drv_info.opt_mode = mode; - - print_info("Panel=%s Mode=%s", - known_lcd_panels[drv_info.panel_idx].name, - drv_info.opt_mode ? drv_info.opt_mode : "default"); - - return 0; -} - -int __init au1100fb_init(void) +static int __init au1100fb_load(void) { - char* options; - int ret; - - print_info("" DRIVER_DESC ""); - - memset(&drv_info, 0, sizeof(drv_info)); - - if (fb_get_options(DRIVER_NAME, &options)) - return -ENODEV; - - /* Setup driver with options */ - ret = au1100fb_setup(options); - if (ret < 0) { - print_err("Fail to setup driver"); - return ret; - } - return platform_driver_register(&au1100fb_driver); } -void __exit au1100fb_cleanup(void) +static void __exit au1100fb_unload(void) { platform_driver_unregister(&au1100fb_driver); - - kfree(drv_info.opt_mode); } -module_init(au1100fb_init); -module_exit(au1100fb_cleanup); +module_init(au1100fb_load); +module_exit(au1100fb_unload); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); diff --git a/drivers/video/au1100fb.h b/drivers/video/au1100fb.h index 164fe2f231ec..12d9642d5465 100644 --- a/drivers/video/au1100fb.h +++ b/drivers/video/au1100fb.h @@ -108,6 +108,7 @@ struct au1100fb_device { unsigned char* fb_mem; /* FrameBuffer memory map */ size_t fb_len; dma_addr_t fb_phys; + int panel_idx; }; /********************************************************************/ @@ -364,11 +365,6 @@ static struct au1100fb_panel known_lcd_panels[] = }, }; -struct au1100fb_drv_info { - int panel_idx; - char *opt_mode; -}; - /********************************************************************/ /* Inline helpers */ diff --git a/drivers/video/au1200fb.c b/drivers/video/au1200fb.c index 5dff32ac8044..72005598040f 100644 --- a/drivers/video/au1200fb.c +++ b/drivers/video/au1200fb.c @@ -46,18 +46,10 @@ #include <asm/mach-au1x00/au1000.h> #include "au1200fb.h" -#ifdef CONFIG_PM -#include <asm/mach-au1x00/au1xxx_pm.h> -#endif - -#ifndef CONFIG_FB_AU1200_DEVS -#define CONFIG_FB_AU1200_DEVS 4 -#endif - #define DRIVER_NAME "au1200fb" #define DRIVER_DESC "LCD controller driver for AU1200 processors" -#define DEBUG 1 +#define DEBUG 0 #define print_err(f, arg...) printk(KERN_ERR DRIVER_NAME ": " f "\n", ## arg) #define print_warn(f, arg...) printk(KERN_WARNING DRIVER_NAME ": " f "\n", ## arg) @@ -150,7 +142,7 @@ struct au1200_lcd_iodata_t { /* Private, per-framebuffer management information (independent of the panel itself) */ struct au1200fb_device { - struct fb_info fb_info; /* FB driver info record */ + struct fb_info *fb_info; /* FB driver info record */ int plane; unsigned char* fb_mem; /* FrameBuffer memory map */ @@ -158,7 +150,6 @@ struct au1200fb_device { dma_addr_t fb_phys; }; -static struct au1200fb_device _au1200fb_devices[CONFIG_FB_AU1200_DEVS]; /********************************************************************/ /* LCD controller restrictions */ @@ -171,10 +162,18 @@ static struct au1200fb_device _au1200fb_devices[CONFIG_FB_AU1200_DEVS]; /* Default number of visible screen buffer to allocate */ #define AU1200FB_NBR_VIDEO_BUFFERS 1 +/* Default maximum number of fb devices to create */ +#define MAX_DEVICE_COUNT 4 + +/* Default window configuration entry to use (see windows[]) */ +#define DEFAULT_WINDOW_INDEX 2 + /********************************************************************/ +static struct fb_info *_au1200fb_infos[MAX_DEVICE_COUNT]; static struct au1200_lcd *lcd = (struct au1200_lcd *) AU1200_LCD_ADDR; -static int window_index = 2; /* default is zero */ +static int device_count = MAX_DEVICE_COUNT; +static int window_index = DEFAULT_WINDOW_INDEX; /* default is zero */ static int panel_index = 2; /* default is zero */ static struct window_settings *win; static struct panel_settings *panel; @@ -205,12 +204,6 @@ struct window_settings { extern int board_au1200fb_panel_init (void); extern int board_au1200fb_panel_shutdown (void); -#ifdef CONFIG_PM -int au1200fb_pm_callback(au1xxx_power_dev_t *dev, - au1xxx_request_t request, void *data); -au1xxx_power_dev_t *LCD_pm_dev; -#endif - /* * Default window configurations */ @@ -652,25 +645,6 @@ static struct panel_settings known_lcd_panels[] = /********************************************************************/ -#ifdef CONFIG_PM -static int set_brightness(unsigned int brightness) -{ - unsigned int hi1, divider; - - /* limit brightness pwm duty to >= 30/1600 */ - if (brightness < 30) { - brightness = 30; - } - divider = (lcd->pwmdiv & 0x3FFFF) + 1; - hi1 = (lcd->pwmhi >> 16) + 1; - hi1 = (((brightness & 0xFF) + 1) * divider >> 8); - lcd->pwmhi &= 0xFFFF; - lcd->pwmhi |= (hi1 << 16); - - return brightness; -} -#endif /* CONFIG_PM */ - static int winbpp (unsigned int winctrl1) { int bits = 0; @@ -712,8 +686,8 @@ static int fbinfo2index (struct fb_info *fb_info) { int i; - for (i = 0; i < CONFIG_FB_AU1200_DEVS; ++i) { - if (fb_info == (struct fb_info *)(&_au1200fb_devices[i].fb_info)) + for (i = 0; i < device_count; ++i) { + if (fb_info == _au1200fb_infos[i]) return i; } printk("au1200fb: ERROR: fbinfo2index failed!\n"); @@ -962,7 +936,7 @@ static void au1200_setmode(struct au1200fb_device *fbdev) lcd->window[plane].winctrl2 = ( 0 | LCD_WINCTRL2_CKMODE_00 | LCD_WINCTRL2_DBM - | LCD_WINCTRL2_BX_N( fbdev->fb_info.fix.line_length) + | LCD_WINCTRL2_BX_N(fbdev->fb_info->fix.line_length) | LCD_WINCTRL2_SCX_1 | LCD_WINCTRL2_SCY_1 ) ; @@ -1050,7 +1024,7 @@ static void au1200fb_update_fbinfo(struct fb_info *fbi) static int au1200fb_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi) { - struct au1200fb_device *fbdev = (struct au1200fb_device *)fbi; + struct au1200fb_device *fbdev = fbi->par; u32 pixclock; int screen_size, plane; @@ -1142,7 +1116,7 @@ static int au1200fb_fb_check_var(struct fb_var_screeninfo *var, */ static int au1200fb_fb_set_par(struct fb_info *fbi) { - struct au1200fb_device *fbdev = (struct au1200fb_device *)fbi; + struct au1200fb_device *fbdev = fbi->par; au1200fb_update_fbinfo(fbi); au1200_setmode(fbdev); @@ -1246,11 +1220,7 @@ static int au1200fb_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) { unsigned int len; unsigned long start=0, off; - struct au1200fb_device *fbdev = (struct au1200fb_device *) info; - -#ifdef CONFIG_PM - au1xxx_pm_access(LCD_pm_dev); -#endif + struct au1200fb_device *fbdev = info->par; if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) { return -EINVAL; @@ -1461,10 +1431,6 @@ static int au1200fb_ioctl(struct fb_info *info, unsigned int cmd, int plane; int val; -#ifdef CONFIG_PM - au1xxx_pm_access(LCD_pm_dev); -#endif - plane = fbinfo2index(info); print_dbg("au1200fb: ioctl %d on plane %d\n", cmd, plane); @@ -1536,9 +1502,11 @@ static struct fb_ops au1200fb_fb_ops = { .fb_set_par = au1200fb_fb_set_par, .fb_setcolreg = au1200fb_fb_setcolreg, .fb_blank = au1200fb_fb_blank, - .fb_fillrect = cfb_fillrect, - .fb_copyarea = cfb_copyarea, - .fb_imageblit = cfb_imageblit, + .fb_fillrect = sys_fillrect, + .fb_copyarea = sys_copyarea, + .fb_imageblit = sys_imageblit, + .fb_read = fb_sys_read, + .fb_write = fb_sys_write, .fb_sync = NULL, .fb_ioctl = au1200fb_ioctl, .fb_mmap = au1200fb_fb_mmap, @@ -1561,10 +1529,9 @@ static irqreturn_t au1200fb_handle_irq(int irq, void* dev_id) static int au1200fb_init_fbinfo(struct au1200fb_device *fbdev) { - struct fb_info *fbi = &fbdev->fb_info; + struct fb_info *fbi = fbdev->fb_info; int bpp; - memset(fbi, 0, sizeof(struct fb_info)); fbi->fbops = &au1200fb_fb_ops; bpp = winbpp(win->w[fbdev->plane].mode_winctrl1); @@ -1623,24 +1590,36 @@ static int au1200fb_init_fbinfo(struct au1200fb_device *fbdev) /* AU1200 LCD controller device driver */ -static int au1200fb_drv_probe(struct platform_device *dev) +static int __devinit au1200fb_drv_probe(struct platform_device *dev) { struct au1200fb_device *fbdev; + struct fb_info *fbi = NULL; unsigned long page; - int bpp, plane, ret; + int bpp, plane, ret, irq; - if (!dev) - return -EINVAL; + /* shut gcc up */ + ret = 0; + fbdev = NULL; + + /* Kickstart the panel */ + au1200_setpanel(panel); - for (plane = 0; plane < CONFIG_FB_AU1200_DEVS; ++plane) { + for (plane = 0; plane < device_count; ++plane) { bpp = winbpp(win->w[plane].mode_winctrl1); if (win->w[plane].xres == 0) win->w[plane].xres = panel->Xres; if (win->w[plane].yres == 0) win->w[plane].yres = panel->Yres; - fbdev = &_au1200fb_devices[plane]; - memset(fbdev, 0, sizeof(struct au1200fb_device)); + fbi = framebuffer_alloc(sizeof(struct au1200fb_device), + &dev->dev); + if (!fbi) + goto failed; + + _au1200fb_infos[plane] = fbi; + fbdev = fbi->par; + fbdev->fb_info = fbi; + fbdev->plane = plane; /* Allocate the framebuffer to the maximum screen size */ @@ -1673,30 +1652,31 @@ static int au1200fb_drv_probe(struct platform_device *dev) goto failed; /* Register new framebuffer */ - if ((ret = register_framebuffer(&fbdev->fb_info)) < 0) { + ret = register_framebuffer(fbi); + if (ret < 0) { print_err("cannot register new framebuffer"); goto failed; } - au1200fb_fb_set_par(&fbdev->fb_info); + au1200fb_fb_set_par(fbi); #if !defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_LOGO) if (plane == 0) - if (fb_prepare_logo(&fbdev->fb_info, FB_ROTATE_UR)) { + if (fb_prepare_logo(fbi, FB_ROTATE_UR)) { /* Start display and show logo on boot */ - fb_set_cmap(&fbdev->fb_info.cmap, - &fbdev->fb_info); - - fb_show_logo(&fbdev->fb_info, FB_ROTATE_UR); + fb_set_cmap(&fbi->cmap, fbi); + fb_show_logo(fbi, FB_ROTATE_UR); } #endif } /* Now hook interrupt too */ - if ((ret = request_irq(AU1200_LCD_INT, au1200fb_handle_irq, - IRQF_DISABLED | IRQF_SHARED, "lcd", (void *)dev)) < 0) { + irq = platform_get_irq(dev, 0); + ret = request_irq(irq, au1200fb_handle_irq, + IRQF_SHARED, "lcd", (void *)dev); + if (ret) { print_err("fail to request interrupt line %d (err: %d)", - AU1200_LCD_INT, ret); + irq, ret); goto failed; } @@ -1705,84 +1685,108 @@ static int au1200fb_drv_probe(struct platform_device *dev) failed: /* NOTE: This only does the current plane/window that failed; others are still active */ if (fbdev->fb_mem) - dma_free_noncoherent(dev, PAGE_ALIGN(fbdev->fb_len), + dma_free_noncoherent(&dev->dev, PAGE_ALIGN(fbdev->fb_len), fbdev->fb_mem, fbdev->fb_phys); - if (fbdev->fb_info.cmap.len != 0) - fb_dealloc_cmap(&fbdev->fb_info.cmap); - if (fbdev->fb_info.pseudo_palette) - kfree(fbdev->fb_info.pseudo_palette); + if (fbi) { + if (fbi->cmap.len != 0) + fb_dealloc_cmap(&fbi->cmap); + kfree(fbi->pseudo_palette); + } if (plane == 0) free_irq(AU1200_LCD_INT, (void*)dev); return ret; } -static int au1200fb_drv_remove(struct platform_device *dev) +static int __devexit au1200fb_drv_remove(struct platform_device *dev) { struct au1200fb_device *fbdev; + struct fb_info *fbi; int plane; - if (!dev) - return -ENODEV; - /* Turn off the panel */ au1200_setpanel(NULL); - for (plane = 0; plane < CONFIG_FB_AU1200_DEVS; ++plane) - { - fbdev = &_au1200fb_devices[plane]; + for (plane = 0; plane < device_count; ++plane) { + fbi = _au1200fb_infos[plane]; + fbdev = fbi->par; /* Clean up all probe data */ - unregister_framebuffer(&fbdev->fb_info); + unregister_framebuffer(fbi); if (fbdev->fb_mem) dma_free_noncoherent(&dev->dev, PAGE_ALIGN(fbdev->fb_len), fbdev->fb_mem, fbdev->fb_phys); - if (fbdev->fb_info.cmap.len != 0) - fb_dealloc_cmap(&fbdev->fb_info.cmap); - if (fbdev->fb_info.pseudo_palette) - kfree(fbdev->fb_info.pseudo_palette); + if (fbi->cmap.len != 0) + fb_dealloc_cmap(&fbi->cmap); + kfree(fbi->pseudo_palette); + + framebuffer_release(fbi); + _au1200fb_infos[plane] = NULL; } - free_irq(AU1200_LCD_INT, (void *)dev); + free_irq(platform_get_irq(dev, 0), (void *)dev); return 0; } #ifdef CONFIG_PM -static int au1200fb_drv_suspend(struct platform_device *dev, u32 state) +static int au1200fb_drv_suspend(struct device *dev) { - /* TODO */ + au1200_setpanel(NULL); + + lcd->outmask = 0; + au_sync(); + return 0; } -static int au1200fb_drv_resume(struct platform_device *dev) +static int au1200fb_drv_resume(struct device *dev) { - /* TODO */ + struct fb_info *fbi; + int i; + + /* Kickstart the panel */ + au1200_setpanel(panel); + + for (i = 0; i < device_count; i++) { + fbi = _au1200fb_infos[i]; + au1200fb_fb_set_par(fbi); + } + return 0; } + +static const struct dev_pm_ops au1200fb_pmops = { + .suspend = au1200fb_drv_suspend, + .resume = au1200fb_drv_resume, + .freeze = au1200fb_drv_suspend, + .thaw = au1200fb_drv_resume, +}; + +#define AU1200FB_PMOPS (&au1200fb_pmops) + +#else +#define AU1200FB_PMOPS NULL #endif /* CONFIG_PM */ static struct platform_driver au1200fb_driver = { .driver = { - .name = "au1200-lcd", - .owner = THIS_MODULE, + .name = "au1200-lcd", + .owner = THIS_MODULE, + .pm = AU1200FB_PMOPS, }, .probe = au1200fb_drv_probe, - .remove = au1200fb_drv_remove, -#ifdef CONFIG_PM - .suspend = au1200fb_drv_suspend, - .resume = au1200fb_drv_resume, -#endif + .remove = __devexit_p(au1200fb_drv_remove), }; /*-------------------------------------------------------------------------*/ /* Kernel driver */ -static void au1200fb_setup(void) +static int au1200fb_setup(void) { - char* options = NULL; - char* this_opt; + char *options = NULL; + char *this_opt, *endptr; int num_panels = ARRAY_SIZE(known_lcd_panels); int panel_idx = -1; @@ -1827,70 +1831,42 @@ static void au1200fb_setup(void) nohwcursor = 1; } - /* Unsupported option */ - else { - print_warn("Unsupported option \"%s\"", this_opt); + else if (strncmp(this_opt, "devices:", 8) == 0) { + this_opt += 8; + device_count = simple_strtol(this_opt, + &endptr, 0); + if ((device_count < 0) || + (device_count > MAX_DEVICE_COUNT)) + device_count = MAX_DEVICE_COUNT; } - } - } -} -#ifdef CONFIG_PM -static int au1200fb_pm_callback(au1xxx_power_dev_t *dev, - au1xxx_request_t request, void *data) { - int retval = -1; - unsigned int d = 0; - unsigned int brightness = 0; - - if (request == AU1XXX_PM_SLEEP) { - board_au1200fb_panel_shutdown(); - } - else if (request == AU1XXX_PM_WAKEUP) { - if(dev->prev_state == SLEEP_STATE) - { - int plane; - au1200_setpanel(panel); - for (plane = 0; plane < CONFIG_FB_AU1200_DEVS; ++plane) { - struct au1200fb_device *fbdev; - fbdev = &_au1200fb_devices[plane]; - au1200fb_fb_set_par(&fbdev->fb_info); + else if (strncmp(this_opt, "wincfg:", 7) == 0) { + this_opt += 7; + window_index = simple_strtol(this_opt, + &endptr, 0); + if ((window_index < 0) || + (window_index >= ARRAY_SIZE(windows))) + window_index = DEFAULT_WINDOW_INDEX; } - } - d = *((unsigned int*)data); - if(d <=10) brightness = 26; - else if(d<=20) brightness = 51; - else if(d<=30) brightness = 77; - else if(d<=40) brightness = 102; - else if(d<=50) brightness = 128; - else if(d<=60) brightness = 153; - else if(d<=70) brightness = 179; - else if(d<=80) brightness = 204; - else if(d<=90) brightness = 230; - else brightness = 255; - set_brightness(brightness); - } else if (request == AU1XXX_PM_GETSTATUS) { - return dev->cur_state; - } else if (request == AU1XXX_PM_ACCESS) { - if (dev->cur_state != SLEEP_STATE) - return retval; - else { - au1200_setpanel(panel); + else if (strncmp(this_opt, "off", 3) == 0) + return 1; + /* Unsupported option */ + else { + print_warn("Unsupported option \"%s\"", this_opt); + } } - } else if (request == AU1XXX_PM_IDLE) { - } else if (request == AU1XXX_PM_CLEANUP) { } - - return retval; + return 0; } -#endif static int __init au1200fb_init(void) { print_info("" DRIVER_DESC ""); /* Setup driver with options */ - au1200fb_setup(); + if (au1200fb_setup()) + return -ENODEV; /* Point to the panel selected */ panel = &known_lcd_panels[panel_index]; @@ -1899,17 +1875,6 @@ static int __init au1200fb_init(void) printk(DRIVER_NAME ": Panel %d %s\n", panel_index, panel->name); printk(DRIVER_NAME ": Win %d %s\n", window_index, win->name); - /* Kickstart the panel, the framebuffers/windows come soon enough */ - au1200_setpanel(panel); - - #ifdef CONFIG_PM - LCD_pm_dev = new_au1xxx_power_device("LCD", &au1200fb_pm_callback, NULL); - if ( LCD_pm_dev == NULL) - printk(KERN_INFO "Unable to create a power management device entry for the au1200fb.\n"); - else - printk(KERN_INFO "Power management device entry for the au1200fb loaded.\n"); - #endif - return platform_driver_register(&au1200fb_driver); } diff --git a/drivers/video/backlight/adp8860_bl.c b/drivers/video/backlight/adp8860_bl.c index 183b6f639852..66bc74d9ce2a 100644 --- a/drivers/video/backlight/adp8860_bl.c +++ b/drivers/video/backlight/adp8860_bl.c @@ -7,7 +7,6 @@ */ #include <linux/module.h> -#include <linux/version.h> #include <linux/init.h> #include <linux/errno.h> #include <linux/pm.h> diff --git a/drivers/video/backlight/adp8870_bl.c b/drivers/video/backlight/adp8870_bl.c index d06886a2bfb5..98e0304deeaf 100644 --- a/drivers/video/backlight/adp8870_bl.c +++ b/drivers/video/backlight/adp8870_bl.c @@ -7,7 +7,6 @@ */ #include <linux/module.h> -#include <linux/version.h> #include <linux/init.h> #include <linux/errno.h> #include <linux/pm.h> diff --git a/drivers/video/bf54x-lq043fb.c b/drivers/video/bf54x-lq043fb.c index 2464b910b590..56720fb476b3 100644 --- a/drivers/video/bf54x-lq043fb.c +++ b/drivers/video/bf54x-lq043fb.c @@ -633,7 +633,7 @@ static int __devinit bfin_bf54x_probe(struct platform_device *pdev) goto out7; } - if (request_irq(info->irq, bfin_bf54x_irq_error, IRQF_DISABLED, + if (request_irq(info->irq, bfin_bf54x_irq_error, 0, "PPI ERROR", info) < 0) { printk(KERN_ERR DRIVER_NAME ": unable to request PPI ERROR IRQ\n"); diff --git a/drivers/video/bfin-lq035q1-fb.c b/drivers/video/bfin-lq035q1-fb.c index 23b6c4b62c78..c633068372c9 100644 --- a/drivers/video/bfin-lq035q1-fb.c +++ b/drivers/video/bfin-lq035q1-fb.c @@ -695,7 +695,7 @@ static int __devinit bfin_lq035q1_probe(struct platform_device *pdev) goto out7; } - ret = request_irq(info->irq, bfin_lq035q1_irq_error, IRQF_DISABLED, + ret = request_irq(info->irq, bfin_lq035q1_irq_error, 0, DRIVER_NAME" PPI ERROR", info); if (ret < 0) { dev_err(&pdev->dev, "unable to request PPI ERROR IRQ\n"); diff --git a/drivers/video/bfin-t350mcqb-fb.c b/drivers/video/bfin-t350mcqb-fb.c index d8de29f0dd8d..d5e126759612 100644 --- a/drivers/video/bfin-t350mcqb-fb.c +++ b/drivers/video/bfin-t350mcqb-fb.c @@ -529,7 +529,7 @@ static int __devinit bfin_t350mcqb_probe(struct platform_device *pdev) goto out7; } - ret = request_irq(info->irq, bfin_t350mcqb_irq_error, IRQF_DISABLED, + ret = request_irq(info->irq, bfin_t350mcqb_irq_error, 0, "PPI ERROR", info); if (ret < 0) { printk(KERN_ERR DRIVER_NAME diff --git a/drivers/video/bfin_adv7393fb.c b/drivers/video/bfin_adv7393fb.c index 8486f541156b..811dd7f6aa41 100644 --- a/drivers/video/bfin_adv7393fb.c +++ b/drivers/video/bfin_adv7393fb.c @@ -481,7 +481,7 @@ static int __devinit bfin_adv7393_fb_probe(struct i2c_client *client, goto out_4; } - if (request_irq(IRQ_PPI_ERROR, ppi_irq_error, IRQF_DISABLED, + if (request_irq(IRQ_PPI_ERROR, ppi_irq_error, 0, "PPI ERROR", fbdev) < 0) { dev_err(&client->dev, "unable to request PPI ERROR IRQ\n"); ret = -EFAULT; diff --git a/drivers/video/carminefb.c b/drivers/video/carminefb.c index caaa27d4a46a..cb09aa1fa138 100644 --- a/drivers/video/carminefb.c +++ b/drivers/video/carminefb.c @@ -32,11 +32,11 @@ #define CARMINEFB_DEFAULT_VIDEO_MODE 1 static unsigned int fb_mode = CARMINEFB_DEFAULT_VIDEO_MODE; -module_param(fb_mode, uint, 444); +module_param(fb_mode, uint, 0444); MODULE_PARM_DESC(fb_mode, "Initial video mode as integer."); static char *fb_mode_str; -module_param(fb_mode_str, charp, 444); +module_param(fb_mode_str, charp, 0444); MODULE_PARM_DESC(fb_mode_str, "Initial video mode in characters."); /* @@ -46,7 +46,7 @@ MODULE_PARM_DESC(fb_mode_str, "Initial video mode in characters."); * 0b010 Display 1 */ static int fb_displays = CARMINE_USE_DISPLAY0 | CARMINE_USE_DISPLAY1; -module_param(fb_displays, int, 444); +module_param(fb_displays, int, 0444); MODULE_PARM_DESC(fb_displays, "Bit mode, which displays are used"); struct carmine_hw { diff --git a/drivers/video/controlfb.c b/drivers/video/controlfb.c index 9075bea55879..7b2c40abae15 100644 --- a/drivers/video/controlfb.c +++ b/drivers/video/controlfb.c @@ -550,7 +550,7 @@ static void control_set_hardware(struct fb_info_control *p, struct fb_par_contro /* - * Parse user speficied options (`video=controlfb:') + * Parse user specified options (`video=controlfb:') */ static void __init control_setup(char *options) { diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c index fcdac872522d..55f91d9ab00b 100644 --- a/drivers/video/da8xx-fb.c +++ b/drivers/video/da8xx-fb.c @@ -35,6 +35,9 @@ #define DRIVER_NAME "da8xx_lcdc" +#define LCD_VERSION_1 1 +#define LCD_VERSION_2 2 + /* LCD Status Register */ #define LCD_END_OF_FRAME1 BIT(9) #define LCD_END_OF_FRAME0 BIT(8) @@ -49,7 +52,9 @@ #define LCD_DMA_BURST_4 0x2 #define LCD_DMA_BURST_8 0x3 #define LCD_DMA_BURST_16 0x4 -#define LCD_END_OF_FRAME_INT_ENA BIT(2) +#define LCD_V1_END_OF_FRAME_INT_ENA BIT(2) +#define LCD_V2_END_OF_FRAME0_INT_ENA BIT(8) +#define LCD_V2_END_OF_FRAME1_INT_ENA BIT(9) #define LCD_DUAL_FRAME_BUFFER_ENABLE BIT(0) /* LCD Control Register */ @@ -65,12 +70,18 @@ #define LCD_MONO_8BIT_MODE BIT(9) #define LCD_RASTER_ORDER BIT(8) #define LCD_TFT_MODE BIT(7) -#define LCD_UNDERFLOW_INT_ENA BIT(6) -#define LCD_PL_ENABLE BIT(4) +#define LCD_V1_UNDERFLOW_INT_ENA BIT(6) +#define LCD_V2_UNDERFLOW_INT_ENA BIT(5) +#define LCD_V1_PL_INT_ENA BIT(4) +#define LCD_V2_PL_INT_ENA BIT(6) #define LCD_MONOCHROME_MODE BIT(1) #define LCD_RASTER_ENABLE BIT(0) #define LCD_TFT_ALT_ENABLE BIT(23) #define LCD_STN_565_ENABLE BIT(24) +#define LCD_V2_DMA_CLK_EN BIT(2) +#define LCD_V2_LIDD_CLK_EN BIT(1) +#define LCD_V2_CORE_CLK_EN BIT(0) +#define LCD_V2_LPP_B10 26 /* LCD Raster Timing 2 Register */ #define LCD_AC_BIAS_TRANSITIONS_PER_INT(x) ((x) << 16) @@ -82,6 +93,7 @@ #define LCD_INVERT_FRAME_CLOCK BIT(20) /* LCD Block */ +#define LCD_PID_REG 0x0 #define LCD_CTRL_REG 0x4 #define LCD_STAT_REG 0x8 #define LCD_RASTER_CTRL_REG 0x28 @@ -94,6 +106,17 @@ #define LCD_DMA_FRM_BUF_BASE_ADDR_1_REG 0x4C #define LCD_DMA_FRM_BUF_CEILING_ADDR_1_REG 0x50 +/* Interrupt Registers available only in Version 2 */ +#define LCD_RAW_STAT_REG 0x58 +#define LCD_MASKED_STAT_REG 0x5c +#define LCD_INT_ENABLE_SET_REG 0x60 +#define LCD_INT_ENABLE_CLR_REG 0x64 +#define LCD_END_OF_INT_IND_REG 0x68 + +/* Clock registers available only on Version 2 */ +#define LCD_CLK_ENABLE_REG 0x6c +#define LCD_CLK_RESET_REG 0x70 + #define LCD_NUM_BUFFERS 2 #define WSI_TIMEOUT 50 @@ -105,6 +128,8 @@ static resource_size_t da8xx_fb_reg_base; static struct resource *lcdc_regs; +static unsigned int lcd_revision; +static irq_handler_t lcdc_irq_handler; static inline unsigned int lcdc_read(unsigned int addr) { @@ -240,6 +265,7 @@ static void lcd_blit(int load_mode, struct da8xx_fb_par *par) u32 end; u32 reg_ras; u32 reg_dma; + u32 reg_int; /* init reg to clear PLM (loading mode) fields */ reg_ras = lcdc_read(LCD_RASTER_CTRL_REG); @@ -252,7 +278,14 @@ static void lcd_blit(int load_mode, struct da8xx_fb_par *par) end = par->dma_end; reg_ras |= LCD_PALETTE_LOAD_MODE(DATA_ONLY); - reg_dma |= LCD_END_OF_FRAME_INT_ENA; + if (lcd_revision == LCD_VERSION_1) { + reg_dma |= LCD_V1_END_OF_FRAME_INT_ENA; + } else { + reg_int = lcdc_read(LCD_INT_ENABLE_SET_REG) | + LCD_V2_END_OF_FRAME0_INT_ENA | + LCD_V2_END_OF_FRAME1_INT_ENA; + lcdc_write(reg_int, LCD_INT_ENABLE_SET_REG); + } reg_dma |= LCD_DUAL_FRAME_BUFFER_ENABLE; lcdc_write(start, LCD_DMA_FRM_BUF_BASE_ADDR_0_REG); @@ -264,7 +297,14 @@ static void lcd_blit(int load_mode, struct da8xx_fb_par *par) end = start + par->palette_sz - 1; reg_ras |= LCD_PALETTE_LOAD_MODE(PALETTE_ONLY); - reg_ras |= LCD_PL_ENABLE; + + if (lcd_revision == LCD_VERSION_1) { + reg_ras |= LCD_V1_PL_INT_ENA; + } else { + reg_int = lcdc_read(LCD_INT_ENABLE_SET_REG) | + LCD_V2_PL_INT_ENA; + lcdc_write(reg_int, LCD_INT_ENABLE_SET_REG); + } lcdc_write(start, LCD_DMA_FRM_BUF_BASE_ADDR_0_REG); lcdc_write(end, LCD_DMA_FRM_BUF_CEILING_ADDR_0_REG); @@ -348,6 +388,7 @@ static void lcd_cfg_vertical_sync(int back_porch, int pulse_width, static int lcd_cfg_display(const struct lcd_ctrl_config *cfg) { u32 reg; + u32 reg_int; reg = lcdc_read(LCD_RASTER_CTRL_REG) & ~(LCD_TFT_MODE | LCD_MONO_8BIT_MODE | @@ -375,7 +416,13 @@ static int lcd_cfg_display(const struct lcd_ctrl_config *cfg) } /* enable additional interrupts here */ - reg |= LCD_UNDERFLOW_INT_ENA; + if (lcd_revision == LCD_VERSION_1) { + reg |= LCD_V1_UNDERFLOW_INT_ENA; + } else { + reg_int = lcdc_read(LCD_INT_ENABLE_SET_REG) | + LCD_V2_UNDERFLOW_INT_ENA; + lcdc_write(reg_int, LCD_INT_ENABLE_SET_REG); + } lcdc_write(reg, LCD_RASTER_CTRL_REG); @@ -413,18 +460,43 @@ static int lcd_cfg_frame_buffer(struct da8xx_fb_par *par, u32 width, u32 height, /* Set the Panel Width */ /* Pixels per line = (PPL + 1)*16 */ - /*0x3F in bits 4..9 gives max horisontal resolution = 1024 pixels*/ - width &= 0x3f0; + if (lcd_revision == LCD_VERSION_1) { + /* + * 0x3F in bits 4..9 gives max horizontal resolution = 1024 + * pixels. + */ + width &= 0x3f0; + } else { + /* + * 0x7F in bits 4..10 gives max horizontal resolution = 2048 + * pixels. + */ + width &= 0x7f0; + } + reg = lcdc_read(LCD_RASTER_TIMING_0_REG); reg &= 0xfffffc00; - reg |= ((width >> 4) - 1) << 4; + if (lcd_revision == LCD_VERSION_1) { + reg |= ((width >> 4) - 1) << 4; + } else { + width = (width >> 4) - 1; + reg |= ((width & 0x3f) << 4) | ((width & 0x40) >> 3); + } lcdc_write(reg, LCD_RASTER_TIMING_0_REG); /* Set the Panel Height */ + /* Set bits 9:0 of Lines Per Pixel */ reg = lcdc_read(LCD_RASTER_TIMING_1_REG); reg = ((height - 1) & 0x3ff) | (reg & 0xfffffc00); lcdc_write(reg, LCD_RASTER_TIMING_1_REG); + /* Set bit 10 of Lines Per Pixel */ + if (lcd_revision == LCD_VERSION_2) { + reg = lcdc_read(LCD_RASTER_TIMING_2_REG); + reg |= ((height - 1) & 0x400) << 16; + lcdc_write(reg, LCD_RASTER_TIMING_2_REG); + } + /* Set the Raster Order of the Frame Buffer */ reg = lcdc_read(LCD_RASTER_CTRL_REG) & ~(1 << 8); if (raster_order) @@ -511,6 +583,9 @@ static void lcd_reset(struct da8xx_fb_par *par) /* DMA has to be disabled */ lcdc_write(0, LCD_DMA_CTRL_REG); lcdc_write(0, LCD_RASTER_CTRL_REG); + + if (lcd_revision == LCD_VERSION_2) + lcdc_write(0, LCD_INT_ENABLE_SET_REG); } static void lcd_calc_clk_divider(struct da8xx_fb_par *par) @@ -523,6 +598,11 @@ static void lcd_calc_clk_divider(struct da8xx_fb_par *par) /* Configure the LCD clock divisor. */ lcdc_write(LCD_CLK_DIVISOR(div) | (LCD_RASTER_MODE & 0x1), LCD_CTRL_REG); + + if (lcd_revision == LCD_VERSION_2) + lcdc_write(LCD_V2_DMA_CLK_EN | LCD_V2_LIDD_CLK_EN | + LCD_V2_CORE_CLK_EN, LCD_CLK_ENABLE_REG); + } static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg, @@ -583,7 +663,63 @@ static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg, return 0; } -static irqreturn_t lcdc_irq_handler(int irq, void *arg) +/* IRQ handler for version 2 of LCDC */ +static irqreturn_t lcdc_irq_handler_rev02(int irq, void *arg) +{ + struct da8xx_fb_par *par = arg; + u32 stat = lcdc_read(LCD_MASKED_STAT_REG); + u32 reg_int; + + if ((stat & LCD_SYNC_LOST) && (stat & LCD_FIFO_UNDERFLOW)) { + lcd_disable_raster(); + lcdc_write(stat, LCD_MASKED_STAT_REG); + lcd_enable_raster(); + } else if (stat & LCD_PL_LOAD_DONE) { + /* + * Must disable raster before changing state of any control bit. + * And also must be disabled before clearing the PL loading + * interrupt via the following write to the status register. If + * this is done after then one gets multiple PL done interrupts. + */ + lcd_disable_raster(); + + lcdc_write(stat, LCD_MASKED_STAT_REG); + + /* Disable PL completion inerrupt */ + reg_int = lcdc_read(LCD_INT_ENABLE_CLR_REG) | + (LCD_V2_PL_INT_ENA); + lcdc_write(reg_int, LCD_INT_ENABLE_CLR_REG); + + /* Setup and start data loading mode */ + lcd_blit(LOAD_DATA, par); + } else { + lcdc_write(stat, LCD_MASKED_STAT_REG); + + if (stat & LCD_END_OF_FRAME0) { + lcdc_write(par->dma_start, + LCD_DMA_FRM_BUF_BASE_ADDR_0_REG); + lcdc_write(par->dma_end, + LCD_DMA_FRM_BUF_CEILING_ADDR_0_REG); + par->vsync_flag = 1; + wake_up_interruptible(&par->vsync_wait); + } + + if (stat & LCD_END_OF_FRAME1) { + lcdc_write(par->dma_start, + LCD_DMA_FRM_BUF_BASE_ADDR_1_REG); + lcdc_write(par->dma_end, + LCD_DMA_FRM_BUF_CEILING_ADDR_1_REG); + par->vsync_flag = 1; + wake_up_interruptible(&par->vsync_wait); + } + } + + lcdc_write(0, LCD_END_OF_INT_IND_REG); + return IRQ_HANDLED; +} + +/* IRQ handler for version 1 LCDC */ +static irqreturn_t lcdc_irq_handler_rev01(int irq, void *arg) { struct da8xx_fb_par *par = arg; u32 stat = lcdc_read(LCD_STAT_REG); @@ -606,7 +742,7 @@ static irqreturn_t lcdc_irq_handler(int irq, void *arg) /* Disable PL completion inerrupt */ reg_ras = lcdc_read(LCD_RASTER_CTRL_REG); - reg_ras &= ~LCD_PL_ENABLE; + reg_ras &= ~LCD_V1_PL_INT_ENA; lcdc_write(reg_ras, LCD_RASTER_CTRL_REG); /* Setup and start data loading mode */ @@ -877,8 +1013,8 @@ static int da8xx_pan_display(struct fb_var_screeninfo *var, start = fix->smem_start + new_var.yoffset * fix->line_length + - new_var.xoffset * var->bits_per_pixel / 8; - end = start + var->yres * fix->line_length - 1; + new_var.xoffset * fbi->var.bits_per_pixel / 8; + end = start + fbi->var.yres * fix->line_length - 1; par->dma_start = start; par->dma_end = end; } @@ -945,6 +1081,22 @@ static int __devinit fb_probe(struct platform_device *device) if (ret) goto err_clk_put; + /* Determine LCD IP Version */ + switch (lcdc_read(LCD_PID_REG)) { + case 0x4C100102: + lcd_revision = LCD_VERSION_1; + break; + case 0x4F200800: + lcd_revision = LCD_VERSION_2; + break; + default: + dev_warn(&device->dev, "Unknown PID Reg value 0x%x, " + "defaulting to LCD revision 1\n", + lcdc_read(LCD_PID_REG)); + lcd_revision = LCD_VERSION_1; + break; + } + for (i = 0, lcdc_info = known_lcd_panels; i < ARRAY_SIZE(known_lcd_panels); i++, lcdc_info++) { @@ -1085,7 +1237,13 @@ static int __devinit fb_probe(struct platform_device *device) } #endif - ret = request_irq(par->irq, lcdc_irq_handler, 0, DRIVER_NAME, par); + if (lcd_revision == LCD_VERSION_1) + lcdc_irq_handler = lcdc_irq_handler_rev01; + else + lcdc_irq_handler = lcdc_irq_handler_rev02; + + ret = request_irq(par->irq, lcdc_irq_handler, 0, + DRIVER_NAME, par); if (ret) goto irq_freq; return 0; diff --git a/drivers/video/fb-puv3.c b/drivers/video/fb-puv3.c index 27f2c57e06e9..60a787fa32cf 100644 --- a/drivers/video/fb-puv3.c +++ b/drivers/video/fb-puv3.c @@ -624,8 +624,8 @@ static int unifb_pan_display(struct fb_var_screeninfo *var, || var->xoffset) return -EINVAL; } else { - if (var->xoffset + var->xres > info->var.xres_virtual || - var->yoffset + var->yres > info->var.yres_virtual) + if (var->xoffset + info->var.xres > info->var.xres_virtual || + var->yoffset + info->var.yres > info->var.yres_virtual) return -EINVAL; } info->var.xoffset = var->xoffset; diff --git a/drivers/video/fb_defio.c b/drivers/video/fb_defio.c index 32814e8800e0..c27e153d8882 100644 --- a/drivers/video/fb_defio.c +++ b/drivers/video/fb_defio.c @@ -223,8 +223,7 @@ void fb_deferred_io_cleanup(struct fb_info *info) int i; BUG_ON(!fbdefio); - cancel_delayed_work(&info->deferred_work); - flush_scheduled_work(); + cancel_delayed_work_sync(&info->deferred_work); /* clear out the mapping that we setup */ for (i = 0 ; i < info->fix.smem_len; i += PAGE_SIZE) { diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 5aac00eb1830..ad936295d8f4 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -1738,8 +1738,6 @@ void fb_set_suspend(struct fb_info *info, int state) { struct fb_event event; - if (!lock_fb_info(info)) - return; event.info = info; if (state) { fb_notifier_call_chain(FB_EVENT_SUSPEND, &event); @@ -1748,7 +1746,6 @@ void fb_set_suspend(struct fb_info *info, int state) info->state = FBINFO_STATE_RUNNING; fb_notifier_call_chain(FB_EVENT_RESUME, &event); } - unlock_fb_info(info); } /** diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c index 4f57485f8c54..cef65574db6c 100644 --- a/drivers/video/fbmon.c +++ b/drivers/video/fbmon.c @@ -493,7 +493,8 @@ static int get_est_timing(unsigned char *block, struct fb_videomode *mode) return num; } -static int get_std_timing(unsigned char *block, struct fb_videomode *mode) +static int get_std_timing(unsigned char *block, struct fb_videomode *mode, + int ver, int rev) { int xres, yres = 0, refresh, ratio, i; @@ -504,7 +505,11 @@ static int get_std_timing(unsigned char *block, struct fb_videomode *mode) ratio = (block[1] & 0xc0) >> 6; switch (ratio) { case 0: - yres = xres; + /* in EDID 1.3 the meaning of 0 changed to 16:10 (prior 1:1) */ + if (ver < 1 || (ver == 1 && rev < 3)) + yres = xres; + else + yres = (xres * 10)/16; break; case 1: yres = (xres * 3)/4; @@ -533,12 +538,12 @@ static int get_std_timing(unsigned char *block, struct fb_videomode *mode) } static int get_dst_timing(unsigned char *block, - struct fb_videomode *mode) + struct fb_videomode *mode, int ver, int rev) { int j, num = 0; for (j = 0; j < 6; j++, block += STD_TIMING_DESCRIPTION_SIZE) - num += get_std_timing(block, &mode[num]); + num += get_std_timing(block, &mode[num], ver, rev); return num; } @@ -599,6 +604,10 @@ static struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize) struct fb_videomode *mode, *m; unsigned char *block; int num = 0, i, first = 1; + int ver, rev; + + ver = edid[EDID_STRUCT_VERSION]; + rev = edid[EDID_STRUCT_REVISION]; mode = kzalloc(50 * sizeof(struct fb_videomode), GFP_KERNEL); if (mode == NULL) @@ -632,12 +641,12 @@ static struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize) DPRINTK(" Standard Timings\n"); block = edid + STD_TIMING_DESCRIPTIONS_START; for (i = 0; i < STD_TIMING; i++, block += STD_TIMING_DESCRIPTION_SIZE) - num += get_std_timing(block, &mode[num]); + num += get_std_timing(block, &mode[num], ver, rev); block = edid + DETAILED_TIMING_DESCRIPTIONS_START; for (i = 0; i < 4; i++, block+= DETAILED_TIMING_DESCRIPTION_SIZE) { if (block[0] == 0x00 && block[1] == 0x00 && block[3] == 0xfa) - num += get_dst_timing(block + 5, &mode[num]); + num += get_dst_timing(block + 5, &mode[num], ver, rev); } /* Yikes, EDID data is totally useless */ diff --git a/drivers/video/fbsysfs.c b/drivers/video/fbsysfs.c index 04251ce89184..67afa9c2289d 100644 --- a/drivers/video/fbsysfs.c +++ b/drivers/video/fbsysfs.c @@ -399,9 +399,12 @@ static ssize_t store_fbstate(struct device *device, state = simple_strtoul(buf, &last, 0); + if (!lock_fb_info(fb_info)) + return -ENODEV; console_lock(); fb_set_suspend(fb_info, (int)state); console_unlock(); + unlock_fb_info(fb_info); return count; } diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c index 0acc7d65aeaa..a16beeb5f548 100644 --- a/drivers/video/fsl-diu-fb.c +++ b/drivers/video/fsl-diu-fb.c @@ -30,37 +30,40 @@ #include <linux/clk.h> #include <linux/uaccess.h> #include <linux/vmalloc.h> - -#include <linux/of_platform.h> +#include <linux/spinlock.h> #include <sysdev/fsl_soc.h> #include <linux/fsl-diu-fb.h> #include "edid.h" -/* - * These parameters give default parameters - * for video output 1024x768, - * FIXME - change timing to proper amounts - * hsync 31.5kHz, vsync 60Hz - */ -static struct fb_videomode __devinitdata fsl_diu_default_mode = { - .refresh = 60, - .xres = 1024, - .yres = 768, - .pixclock = 15385, - .left_margin = 160, - .right_margin = 24, - .upper_margin = 29, - .lower_margin = 3, - .hsync_len = 136, - .vsync_len = 6, - .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, - .vmode = FB_VMODE_NONINTERLACED +#define FSL_AOI_NUM 6 /* 5 AOIs and one dummy AOI */ + /* 1 for plane 0, 2 for plane 1&2 each */ + +/* HW cursor parameters */ +#define MAX_CURS 32 + +/* INT_STATUS/INT_MASK field descriptions */ +#define INT_VSYNC 0x01 /* Vsync interrupt */ +#define INT_VSYNC_WB 0x02 /* Vsync interrupt for write back operation */ +#define INT_UNDRUN 0x04 /* Under run exception interrupt */ +#define INT_PARERR 0x08 /* Display parameters error interrupt */ +#define INT_LS_BF_VS 0x10 /* Lines before vsync. interrupt */ + +struct diu_addr { + void *vaddr; /* Virtual address */ + dma_addr_t paddr; /* Physical address */ + __u32 offset; }; +/* + * List of supported video modes + * + * The first entry is the default video mode. The remain entries are in + * order if increasing resolution and frequency. The 320x240-60 mode is + * the initial AOI for the second and third planes. + */ static struct fb_videomode __devinitdata fsl_diu_mode_db[] = { { - .name = "1024x768-60", .refresh = 60, .xres = 1024, .yres = 768, @@ -75,7 +78,132 @@ static struct fb_videomode __devinitdata fsl_diu_mode_db[] = { .vmode = FB_VMODE_NONINTERLACED }, { - .name = "1024x768-70", + .refresh = 60, + .xres = 320, + .yres = 240, + .pixclock = 79440, + .left_margin = 16, + .right_margin = 16, + .upper_margin = 16, + .lower_margin = 5, + .hsync_len = 48, + .vsync_len = 1, + .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .vmode = FB_VMODE_NONINTERLACED + }, + { + .refresh = 60, + .xres = 640, + .yres = 480, + .pixclock = 39722, + .left_margin = 48, + .right_margin = 16, + .upper_margin = 33, + .lower_margin = 10, + .hsync_len = 96, + .vsync_len = 2, + .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .vmode = FB_VMODE_NONINTERLACED + }, + { + .refresh = 72, + .xres = 640, + .yres = 480, + .pixclock = 32052, + .left_margin = 128, + .right_margin = 24, + .upper_margin = 28, + .lower_margin = 9, + .hsync_len = 40, + .vsync_len = 3, + .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .vmode = FB_VMODE_NONINTERLACED + }, + { + .refresh = 75, + .xres = 640, + .yres = 480, + .pixclock = 31747, + .left_margin = 120, + .right_margin = 16, + .upper_margin = 16, + .lower_margin = 1, + .hsync_len = 64, + .vsync_len = 3, + .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .vmode = FB_VMODE_NONINTERLACED + }, + { + .refresh = 90, + .xres = 640, + .yres = 480, + .pixclock = 25057, + .left_margin = 120, + .right_margin = 32, + .upper_margin = 14, + .lower_margin = 25, + .hsync_len = 40, + .vsync_len = 14, + .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .vmode = FB_VMODE_NONINTERLACED + }, + { + .refresh = 100, + .xres = 640, + .yres = 480, + .pixclock = 22272, + .left_margin = 48, + .right_margin = 32, + .upper_margin = 17, + .lower_margin = 22, + .hsync_len = 128, + .vsync_len = 12, + .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .vmode = FB_VMODE_NONINTERLACED + }, + { + .refresh = 60, + .xres = 800, + .yres = 480, + .pixclock = 33805, + .left_margin = 96, + .right_margin = 24, + .upper_margin = 10, + .lower_margin = 3, + .hsync_len = 72, + .vsync_len = 7, + .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .vmode = FB_VMODE_NONINTERLACED + }, + { + .refresh = 60, + .xres = 800, + .yres = 600, + .pixclock = 25000, + .left_margin = 88, + .right_margin = 40, + .upper_margin = 23, + .lower_margin = 1, + .hsync_len = 128, + .vsync_len = 4, + .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .vmode = FB_VMODE_NONINTERLACED + }, + { + .refresh = 60, + .xres = 854, + .yres = 480, + .pixclock = 31518, + .left_margin = 104, + .right_margin = 16, + .upper_margin = 13, + .lower_margin = 1, + .hsync_len = 88, + .vsync_len = 3, + .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .vmode = FB_VMODE_NONINTERLACED + }, + { .refresh = 70, .xres = 1024, .yres = 768, @@ -90,7 +218,6 @@ static struct fb_videomode __devinitdata fsl_diu_mode_db[] = { .vmode = FB_VMODE_NONINTERLACED }, { - .name = "1024x768-75", .refresh = 75, .xres = 1024, .yres = 768, @@ -105,7 +232,34 @@ static struct fb_videomode __devinitdata fsl_diu_mode_db[] = { .vmode = FB_VMODE_NONINTERLACED }, { - .name = "1280x1024-60", + .refresh = 60, + .xres = 1280, + .yres = 480, + .pixclock = 18939, + .left_margin = 353, + .right_margin = 47, + .upper_margin = 39, + .lower_margin = 4, + .hsync_len = 8, + .vsync_len = 2, + .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .vmode = FB_VMODE_NONINTERLACED + }, + { + .refresh = 60, + .xres = 1280, + .yres = 720, + .pixclock = 13426, + .left_margin = 192, + .right_margin = 64, + .upper_margin = 22, + .lower_margin = 1, + .hsync_len = 136, + .vsync_len = 3, + .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .vmode = FB_VMODE_NONINTERLACED + }, + { .refresh = 60, .xres = 1280, .yres = 1024, @@ -120,7 +274,6 @@ static struct fb_videomode __devinitdata fsl_diu_mode_db[] = { .vmode = FB_VMODE_NONINTERLACED }, { - .name = "1280x1024-70", .refresh = 70, .xres = 1280, .yres = 1024, @@ -135,7 +288,6 @@ static struct fb_videomode __devinitdata fsl_diu_mode_db[] = { .vmode = FB_VMODE_NONINTERLACED }, { - .name = "1280x1024-75", .refresh = 75, .xres = 1280, .yres = 1024, @@ -150,40 +302,25 @@ static struct fb_videomode __devinitdata fsl_diu_mode_db[] = { .vmode = FB_VMODE_NONINTERLACED }, { - .name = "320x240", /* for AOI only */ .refresh = 60, - .xres = 320, - .yres = 240, - .pixclock = 15385, - .left_margin = 0, - .right_margin = 0, - .upper_margin = 0, - .lower_margin = 0, - .hsync_len = 0, - .vsync_len = 0, - .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, - .vmode = FB_VMODE_NONINTERLACED - }, - { - .name = "1280x480-60", - .refresh = 60, - .xres = 1280, - .yres = 480, - .pixclock = 18939, - .left_margin = 353, - .right_margin = 47, - .upper_margin = 39, - .lower_margin = 4, - .hsync_len = 8, - .vsync_len = 2, + .xres = 1920, + .yres = 1080, + .pixclock = 5787, + .left_margin = 328, + .right_margin = 120, + .upper_margin = 34, + .lower_margin = 1, + .hsync_len = 208, + .vsync_len = 3, .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, .vmode = FB_VMODE_NONINTERLACED }, }; -static char *fb_mode = "1024x768-32@60"; +static char *fb_mode; static unsigned long default_bpp = 32; -static int monitor_port; +static enum fsl_diu_monitor_port monitor_port; +static char *monitor_string; #if defined(CONFIG_NOT_COHERENT_CACHE) static u8 *coherence_data; @@ -201,15 +338,27 @@ struct fsl_diu_data { void *dummy_aoi_virt; unsigned int irq; int fb_enabled; - int monitor_port; + enum fsl_diu_monitor_port monitor_port; + struct diu __iomem *diu_reg; + spinlock_t reg_lock; + struct diu_addr ad; + struct diu_addr gamma; + struct diu_addr pallete; + struct diu_addr cursor; +}; + +enum mfb_index { + PLANE0 = 0, /* Plane 0, only one AOI that fills the screen */ + PLANE1_AOI0, /* Plane 1, first AOI */ + PLANE1_AOI1, /* Plane 1, second AOI */ + PLANE2_AOI0, /* Plane 2, first AOI */ + PLANE2_AOI1, /* Plane 2, second AOI */ }; struct mfb_info { - int index; - int type; + enum mfb_index index; char *id; int registered; - int blank; unsigned long pseudo_palette[16]; struct diu_ad *ad; int cursor_reset; @@ -223,63 +372,82 @@ struct mfb_info { static struct mfb_info mfb_template[] = { - { /* AOI 0 for plane 0 */ - .index = 0, - .type = MFB_TYPE_OUTPUT, - .id = "Panel0", - .registered = 0, - .count = 0, - .x_aoi_d = 0, - .y_aoi_d = 0, + { + .index = PLANE0, + .id = "Panel0", + .registered = 0, + .count = 0, + .x_aoi_d = 0, + .y_aoi_d = 0, }, - { /* AOI 0 for plane 1 */ - .index = 1, - .type = MFB_TYPE_OUTPUT, - .id = "Panel1 AOI0", - .registered = 0, - .g_alpha = 0xff, - .count = 0, - .x_aoi_d = 0, - .y_aoi_d = 0, + { + .index = PLANE1_AOI0, + .id = "Panel1 AOI0", + .registered = 0, + .g_alpha = 0xff, + .count = 0, + .x_aoi_d = 0, + .y_aoi_d = 0, }, - { /* AOI 1 for plane 1 */ - .index = 2, - .type = MFB_TYPE_OUTPUT, - .id = "Panel1 AOI1", - .registered = 0, - .g_alpha = 0xff, - .count = 0, - .x_aoi_d = 0, - .y_aoi_d = 480, + { + .index = PLANE1_AOI1, + .id = "Panel1 AOI1", + .registered = 0, + .g_alpha = 0xff, + .count = 0, + .x_aoi_d = 0, + .y_aoi_d = 480, }, - { /* AOI 0 for plane 2 */ - .index = 3, - .type = MFB_TYPE_OUTPUT, - .id = "Panel2 AOI0", - .registered = 0, - .g_alpha = 0xff, - .count = 0, - .x_aoi_d = 640, - .y_aoi_d = 0, + { + .index = PLANE2_AOI0, + .id = "Panel2 AOI0", + .registered = 0, + .g_alpha = 0xff, + .count = 0, + .x_aoi_d = 640, + .y_aoi_d = 0, }, - { /* AOI 1 for plane 2 */ - .index = 4, - .type = MFB_TYPE_OUTPUT, - .id = "Panel2 AOI1", - .registered = 0, - .g_alpha = 0xff, - .count = 0, - .x_aoi_d = 640, - .y_aoi_d = 480, + { + .index = PLANE2_AOI1, + .id = "Panel2 AOI1", + .registered = 0, + .g_alpha = 0xff, + .count = 0, + .x_aoi_d = 640, + .y_aoi_d = 480, }, }; -static struct diu_hw dr = { - .mode = MFB_MODE1, - .reg_lock = __SPIN_LOCK_UNLOCKED(diu_hw.reg_lock), -}; +/** + * fsl_diu_name_to_port - convert a port name to a monitor port enum + * + * Takes the name of a monitor port ("dvi", "lvds", or "dlvds") and returns + * the enum fsl_diu_monitor_port that corresponds to that string. + * + * For compatibility with older versions, a number ("0", "1", or "2") is also + * supported. + * + * If the string is unknown, DVI is assumed. + * + * If the particular port is not supported by the platform, another port + * (platform-specific) is chosen instead. + */ +static enum fsl_diu_monitor_port fsl_diu_name_to_port(const char *s) +{ + enum fsl_diu_monitor_port port = FSL_DIU_PORT_DVI; + unsigned long val; -static struct diu_pool pool; + if (s) { + if (!strict_strtoul(s, 10, &val) && (val <= 2)) + port = (enum fsl_diu_monitor_port) val; + else if (strncmp(s, "lvds", 4) == 0) + port = FSL_DIU_PORT_LVDS; + else if (strncmp(s, "dlvds", 5) == 0) + port = FSL_DIU_PORT_DLVDS; + } + + return diu_ops.valid_monitor_port(port); +} /** * fsl_diu_alloc - allocate memory for the DIU @@ -292,14 +460,9 @@ static void *fsl_diu_alloc(size_t size, phys_addr_t *phys) { void *virt; - pr_debug("size=%zu\n", size); - virt = alloc_pages_exact(size, GFP_DMA | __GFP_ZERO); - if (virt) { + if (virt) *phys = virt_to_phys(virt); - pr_debug("virt=%p phys=%llx\n", virt, - (unsigned long long)*phys); - } return virt; } @@ -313,8 +476,6 @@ static void *fsl_diu_alloc(size_t size, phys_addr_t *phys) */ static void fsl_diu_free(void *virt, size_t size) { - pr_debug("virt=%p size=%zu\n", virt, size); - if (virt && size) free_pages_exact(virt, size); } @@ -330,82 +491,72 @@ void wr_reg_wa(u32 *reg, u32 val) } while (in_be32(reg) != val); } -static int fsl_diu_enable_panel(struct fb_info *info) +static void fsl_diu_enable_panel(struct fb_info *info) { struct mfb_info *pmfbi, *cmfbi, *mfbi = info->par; - struct diu *hw = dr.diu_reg; struct diu_ad *ad = mfbi->ad; struct fsl_diu_data *machine_data = mfbi->parent; - int res = 0; + struct diu __iomem *hw = machine_data->diu_reg; - pr_debug("enable_panel index %d\n", mfbi->index); - if (mfbi->type != MFB_TYPE_OFF) { - switch (mfbi->index) { - case 0: /* plane 0 */ - if (hw->desc[0] != ad->paddr) - wr_reg_wa(&hw->desc[0], ad->paddr); - break; - case 1: /* plane 1 AOI 0 */ - cmfbi = machine_data->fsl_diu_info[2]->par; - if (hw->desc[1] != ad->paddr) { /* AOI0 closed */ - if (cmfbi->count > 0) /* AOI1 open */ - ad->next_ad = - cpu_to_le32(cmfbi->ad->paddr); - else - ad->next_ad = 0; - wr_reg_wa(&hw->desc[1], ad->paddr); - } - break; - case 3: /* plane 2 AOI 0 */ - cmfbi = machine_data->fsl_diu_info[4]->par; - if (hw->desc[2] != ad->paddr) { /* AOI0 closed */ - if (cmfbi->count > 0) /* AOI1 open */ - ad->next_ad = - cpu_to_le32(cmfbi->ad->paddr); - else - ad->next_ad = 0; - wr_reg_wa(&hw->desc[2], ad->paddr); - } - break; - case 2: /* plane 1 AOI 1 */ - pmfbi = machine_data->fsl_diu_info[1]->par; - ad->next_ad = 0; - if (hw->desc[1] == machine_data->dummy_ad->paddr) - wr_reg_wa(&hw->desc[1], ad->paddr); - else /* AOI0 open */ - pmfbi->ad->next_ad = cpu_to_le32(ad->paddr); - break; - case 4: /* plane 2 AOI 1 */ - pmfbi = machine_data->fsl_diu_info[3]->par; - ad->next_ad = 0; - if (hw->desc[2] == machine_data->dummy_ad->paddr) - wr_reg_wa(&hw->desc[2], ad->paddr); - else /* AOI0 was open */ - pmfbi->ad->next_ad = cpu_to_le32(ad->paddr); - break; - default: - res = -EINVAL; - break; + switch (mfbi->index) { + case PLANE0: + if (hw->desc[0] != ad->paddr) + wr_reg_wa(&hw->desc[0], ad->paddr); + break; + case PLANE1_AOI0: + cmfbi = machine_data->fsl_diu_info[2]->par; + if (hw->desc[1] != ad->paddr) { /* AOI0 closed */ + if (cmfbi->count > 0) /* AOI1 open */ + ad->next_ad = + cpu_to_le32(cmfbi->ad->paddr); + else + ad->next_ad = 0; + wr_reg_wa(&hw->desc[1], ad->paddr); } - } else - res = -EINVAL; - return res; + break; + case PLANE2_AOI0: + cmfbi = machine_data->fsl_diu_info[4]->par; + if (hw->desc[2] != ad->paddr) { /* AOI0 closed */ + if (cmfbi->count > 0) /* AOI1 open */ + ad->next_ad = + cpu_to_le32(cmfbi->ad->paddr); + else + ad->next_ad = 0; + wr_reg_wa(&hw->desc[2], ad->paddr); + } + break; + case PLANE1_AOI1: + pmfbi = machine_data->fsl_diu_info[1]->par; + ad->next_ad = 0; + if (hw->desc[1] == machine_data->dummy_ad->paddr) + wr_reg_wa(&hw->desc[1], ad->paddr); + else /* AOI0 open */ + pmfbi->ad->next_ad = cpu_to_le32(ad->paddr); + break; + case PLANE2_AOI1: + pmfbi = machine_data->fsl_diu_info[3]->par; + ad->next_ad = 0; + if (hw->desc[2] == machine_data->dummy_ad->paddr) + wr_reg_wa(&hw->desc[2], ad->paddr); + else /* AOI0 was open */ + pmfbi->ad->next_ad = cpu_to_le32(ad->paddr); + break; + } } -static int fsl_diu_disable_panel(struct fb_info *info) +static void fsl_diu_disable_panel(struct fb_info *info) { struct mfb_info *pmfbi, *cmfbi, *mfbi = info->par; - struct diu *hw = dr.diu_reg; struct diu_ad *ad = mfbi->ad; struct fsl_diu_data *machine_data = mfbi->parent; - int res = 0; + struct diu __iomem *hw = machine_data->diu_reg; switch (mfbi->index) { - case 0: /* plane 0 */ + case PLANE0: if (hw->desc[0] != machine_data->dummy_ad->paddr) wr_reg_wa(&hw->desc[0], machine_data->dummy_ad->paddr); break; - case 1: /* plane 1 AOI 0 */ + case PLANE1_AOI0: cmfbi = machine_data->fsl_diu_info[2]->par; if (cmfbi->count > 0) /* AOI1 is open */ wr_reg_wa(&hw->desc[1], cmfbi->ad->paddr); @@ -414,7 +565,7 @@ static int fsl_diu_disable_panel(struct fb_info *info) wr_reg_wa(&hw->desc[1], machine_data->dummy_ad->paddr); /* close AOI 0 */ break; - case 3: /* plane 2 AOI 0 */ + case PLANE2_AOI0: cmfbi = machine_data->fsl_diu_info[4]->par; if (cmfbi->count > 0) /* AOI1 is open */ wr_reg_wa(&hw->desc[2], cmfbi->ad->paddr); @@ -423,7 +574,7 @@ static int fsl_diu_disable_panel(struct fb_info *info) wr_reg_wa(&hw->desc[2], machine_data->dummy_ad->paddr); /* close AOI 0 */ break; - case 2: /* plane 1 AOI 1 */ + case PLANE1_AOI1: pmfbi = machine_data->fsl_diu_info[1]->par; if (hw->desc[1] != ad->paddr) { /* AOI1 is not the first in the chain */ @@ -434,7 +585,7 @@ static int fsl_diu_disable_panel(struct fb_info *info) wr_reg_wa(&hw->desc[1], machine_data->dummy_ad->paddr); /* close AOI 1 */ break; - case 4: /* plane 2 AOI 1 */ + case PLANE2_AOI1: pmfbi = machine_data->fsl_diu_info[3]->par; if (hw->desc[2] != ad->paddr) { /* AOI1 is not the first in the chain */ @@ -445,31 +596,26 @@ static int fsl_diu_disable_panel(struct fb_info *info) wr_reg_wa(&hw->desc[2], machine_data->dummy_ad->paddr); /* close AOI 1 */ break; - default: - res = -EINVAL; - break; } - - return res; } static void enable_lcdc(struct fb_info *info) { - struct diu *hw = dr.diu_reg; struct mfb_info *mfbi = info->par; struct fsl_diu_data *machine_data = mfbi->parent; + struct diu __iomem *hw = machine_data->diu_reg; if (!machine_data->fb_enabled) { - out_be32(&hw->diu_mode, dr.mode); + out_be32(&hw->diu_mode, MFB_MODE1); machine_data->fb_enabled++; } } static void disable_lcdc(struct fb_info *info) { - struct diu *hw = dr.diu_reg; struct mfb_info *mfbi = info->par; struct fsl_diu_data *machine_data = mfbi->parent; + struct diu __iomem *hw = machine_data->diu_reg; if (machine_data->fb_enabled) { out_be32(&hw->diu_mode, 0); @@ -482,7 +628,8 @@ static void adjust_aoi_size_position(struct fb_var_screeninfo *var, { struct mfb_info *lower_aoi_mfbi, *upper_aoi_mfbi, *mfbi = info->par; struct fsl_diu_data *machine_data = mfbi->parent; - int available_height, upper_aoi_bottom, index = mfbi->index; + int available_height, upper_aoi_bottom; + enum mfb_index index = mfbi->index; int lower_aoi_is_open, upper_aoi_is_open; __u32 base_plane_width, base_plane_height, upper_aoi_height; @@ -494,14 +641,14 @@ static void adjust_aoi_size_position(struct fb_var_screeninfo *var, if (mfbi->y_aoi_d < 0) mfbi->y_aoi_d = 0; switch (index) { - case 0: + case PLANE0: if (mfbi->x_aoi_d != 0) mfbi->x_aoi_d = 0; if (mfbi->y_aoi_d != 0) mfbi->y_aoi_d = 0; break; - case 1: /* AOI 0 */ - case 3: + case PLANE1_AOI0: + case PLANE2_AOI0: lower_aoi_mfbi = machine_data->fsl_diu_info[index+1]->par; lower_aoi_is_open = lower_aoi_mfbi->count > 0 ? 1 : 0; if (var->xres > base_plane_width) @@ -518,8 +665,8 @@ static void adjust_aoi_size_position(struct fb_var_screeninfo *var, if ((mfbi->y_aoi_d + var->yres) > available_height) mfbi->y_aoi_d = available_height - var->yres; break; - case 2: /* AOI 1 */ - case 4: + case PLANE1_AOI1: + case PLANE2_AOI1: upper_aoi_mfbi = machine_data->fsl_diu_info[index-1]->par; upper_aoi_height = machine_data->fsl_diu_info[index-1]->var.yres; @@ -555,9 +702,6 @@ static void adjust_aoi_size_position(struct fb_var_screeninfo *var, static int fsl_diu_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { - pr_debug("check_var xres: %d\n", var->xres); - pr_debug("check_var yres: %d\n", var->yres); - if (var->xres_virtual < var->xres) var->xres_virtual = var->xres; if (var->yres_virtual < var->yres) @@ -652,7 +796,7 @@ static void set_fix(struct fb_info *info) struct fb_var_screeninfo *var = &info->var; struct mfb_info *mfbi = info->par; - strncpy(fix->id, mfbi->id, strlen(mfbi->id)); + strncpy(fix->id, mfbi->id, sizeof(fix->id)); fix->line_length = var->xres_virtual * var->bits_per_pixel / 8; fix->type = FB_TYPE_PACKED_PIXELS; fix->accel = FB_ACCEL_NONE; @@ -666,45 +810,37 @@ static void update_lcdc(struct fb_info *info) struct fb_var_screeninfo *var = &info->var; struct mfb_info *mfbi = info->par; struct fsl_diu_data *machine_data = mfbi->parent; - struct diu *hw; + struct diu __iomem *hw; int i, j; char __iomem *cursor_base, *gamma_table_base; u32 temp; - hw = dr.diu_reg; - - if (mfbi->type == MFB_TYPE_OFF) { - fsl_diu_disable_panel(info); - return; - } + hw = machine_data->diu_reg; diu_ops.set_monitor_port(machine_data->monitor_port); - gamma_table_base = pool.gamma.vaddr; - cursor_base = pool.cursor.vaddr; + gamma_table_base = machine_data->gamma.vaddr; + cursor_base = machine_data->cursor.vaddr; /* Prep for DIU init - gamma table, cursor table */ for (i = 0; i <= 2; i++) - for (j = 0; j <= 255; j++) - *gamma_table_base++ = j; + for (j = 0; j <= 255; j++) + *gamma_table_base++ = j; - diu_ops.set_gamma_table(machine_data->monitor_port, pool.gamma.vaddr); + diu_ops.set_gamma_table(machine_data->monitor_port, + machine_data->gamma.vaddr); - pr_debug("update-lcdc: HW - %p\n Disabling DIU\n", hw); disable_lcdc(info); /* Program DIU registers */ - out_be32(&hw->gamma, pool.gamma.paddr); - out_be32(&hw->cursor, pool.cursor.paddr); + out_be32(&hw->gamma, machine_data->gamma.paddr); + out_be32(&hw->cursor, machine_data->cursor.paddr); out_be32(&hw->bgnd, 0x007F7F7F); /* BGND */ out_be32(&hw->bgnd_wb, 0); /* BGND_WB */ out_be32(&hw->disp_size, (var->yres << 16 | var->xres)); /* DISP SIZE */ - pr_debug("DIU xres: %d\n", var->xres); - pr_debug("DIU yres: %d\n", var->yres); - out_be32(&hw->wb_size, 0); /* WB SIZE */ out_be32(&hw->wb_mem_addr, 0); /* WB MEM ADDR */ @@ -721,15 +857,6 @@ static void update_lcdc(struct fb_info *info) out_be32(&hw->vsyn_para, temp); - pr_debug("DIU right_margin - %d\n", var->right_margin); - pr_debug("DIU left_margin - %d\n", var->left_margin); - pr_debug("DIU hsync_len - %d\n", var->hsync_len); - pr_debug("DIU upper_margin - %d\n", var->upper_margin); - pr_debug("DIU lower_margin - %d\n", var->lower_margin); - pr_debug("DIU vsync_len - %d\n", var->vsync_len); - pr_debug("DIU HSYNC - 0x%08x\n", hw->hsyn_para); - pr_debug("DIU VSYNC - 0x%08x\n", hw->vsyn_para); - diu_ops.set_pixel_clock(var->pixclock); out_be32(&hw->syn_pol, 0); /* SYNC SIGNALS POLARITY */ @@ -746,14 +873,9 @@ static int map_video_memory(struct fb_info *info) phys_addr_t phys; u32 smem_len = info->fix.line_length * info->var.yres_virtual; - pr_debug("info->var.xres_virtual = %d\n", info->var.xres_virtual); - pr_debug("info->var.yres_virtual = %d\n", info->var.yres_virtual); - pr_debug("info->fix.line_length = %d\n", info->fix.line_length); - pr_debug("MAP_VIDEO_MEMORY: smem_len = %u\n", smem_len); - info->screen_base = fsl_diu_alloc(smem_len, &phys); if (info->screen_base == NULL) { - printk(KERN_ERR "Unable to allocate fb memory\n"); + dev_err(info->dev, "unable to allocate fb memory\n"); return -ENOMEM; } mutex_lock(&info->mm_lock); @@ -762,10 +884,6 @@ static int map_video_memory(struct fb_info *info) mutex_unlock(&info->mm_lock); info->screen_size = info->fix.smem_len; - pr_debug("Allocated fb @ paddr=0x%08lx, size=%d.\n", - info->fix.smem_start, info->fix.smem_len); - pr_debug("screen base %p\n", info->screen_base); - return 0; } @@ -810,9 +928,9 @@ static int fsl_diu_set_par(struct fb_info *info) struct mfb_info *mfbi = info->par; struct fsl_diu_data *machine_data = mfbi->parent; struct diu_ad *ad = mfbi->ad; - struct diu *hw; + struct diu __iomem *hw; - hw = dr.diu_reg; + hw = machine_data->diu_reg; set_fix(info); mfbi->cursor_reset = 1; @@ -822,18 +940,16 @@ static int fsl_diu_set_par(struct fb_info *info) if (len != info->fix.smem_len) { if (info->fix.smem_start) unmap_video_memory(info); - pr_debug("SET PAR: smem_len = %d\n", info->fix.smem_len); /* Memory allocation for framebuffer */ if (map_video_memory(info)) { - printk(KERN_ERR "Unable to allocate fb memory 1\n"); + dev_err(info->dev, "unable to allocate fb memory 1\n"); return -ENOMEM; } } - ad->pix_fmt = - diu_ops.get_pixel_format(var->bits_per_pixel, - machine_data->monitor_port); + ad->pix_fmt = diu_ops.get_pixel_format(machine_data->monitor_port, + var->bits_per_pixel); ad->addr = cpu_to_le32(info->fix.smem_start); ad->src_size_g_alpha = cpu_to_le32((var->yres_virtual << 12) | var->xres_virtual) | mfbi->g_alpha; @@ -851,14 +967,14 @@ static int fsl_diu_set_par(struct fb_info *info) ad->ckmin_g = 255; ad->ckmin_b = 255; - if (mfbi->index == 0) + if (mfbi->index == PLANE0) update_lcdc(info); return 0; } static inline __u32 CNVT_TOHW(__u32 val, __u32 width) { - return ((val<<width) + 0x7FFF - val)>>16; + return ((val << width) + 0x7FFF - val) >> 16; } /* @@ -870,8 +986,9 @@ static inline __u32 CNVT_TOHW(__u32 val, __u32 width) * pseudo_palette in struct fb_info. For pseudocolor mode we have a limited * color palette. */ -static int fsl_diu_setcolreg(unsigned regno, unsigned red, unsigned green, - unsigned blue, unsigned transp, struct fb_info *info) +static int fsl_diu_setcolreg(unsigned int regno, unsigned int red, + unsigned int green, unsigned int blue, + unsigned int transp, struct fb_info *info) { int ret = 1; @@ -906,9 +1023,6 @@ static int fsl_diu_setcolreg(unsigned regno, unsigned red, unsigned green, ret = 0; } break; - case FB_VISUAL_STATIC_PSEUDOCOLOR: - case FB_VISUAL_PSEUDOCOLOR: - break; } return ret; @@ -944,37 +1058,6 @@ static int fsl_diu_pan_display(struct fb_var_screeninfo *var, return 0; } -/* - * Blank the screen if blank_mode != 0, else unblank. Return 0 if blanking - * succeeded, != 0 if un-/blanking failed. - * blank_mode == 2: suspend vsync - * blank_mode == 3: suspend hsync - * blank_mode == 4: powerdown - */ -static int fsl_diu_blank(int blank_mode, struct fb_info *info) -{ - struct mfb_info *mfbi = info->par; - - mfbi->blank = blank_mode; - - switch (blank_mode) { - case FB_BLANK_VSYNC_SUSPEND: - case FB_BLANK_HSYNC_SUSPEND: - /* FIXME: fixes to enable_panel and enable lcdc needed */ - case FB_BLANK_NORMAL: - /* fsl_diu_disable_panel(info);*/ - break; - case FB_BLANK_POWERDOWN: - /* disable_lcdc(info); */ - break; - case FB_BLANK_UNBLANK: - /* fsl_diu_enable_panel(info);*/ - break; - } - - return 0; -} - static int fsl_diu_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) { @@ -989,25 +1072,29 @@ static int fsl_diu_ioctl(struct fb_info *info, unsigned int cmd, if (!arg) return -EINVAL; switch (cmd) { + case MFB_SET_PIXFMT_OLD: + dev_warn(info->dev, + "MFB_SET_PIXFMT value of 0x%08x is deprecated.\n", + MFB_SET_PIXFMT_OLD); case MFB_SET_PIXFMT: if (copy_from_user(&pix_fmt, buf, sizeof(pix_fmt))) return -EFAULT; ad->pix_fmt = pix_fmt; - pr_debug("Set pixel format to 0x%08x\n", ad->pix_fmt); break; + case MFB_GET_PIXFMT_OLD: + dev_warn(info->dev, + "MFB_GET_PIXFMT value of 0x%08x is deprecated.\n", + MFB_GET_PIXFMT_OLD); case MFB_GET_PIXFMT: pix_fmt = ad->pix_fmt; if (copy_to_user(buf, &pix_fmt, sizeof(pix_fmt))) return -EFAULT; - pr_debug("get pixel format 0x%08x\n", ad->pix_fmt); break; case MFB_SET_AOID: if (copy_from_user(&aoi_d, buf, sizeof(aoi_d))) return -EFAULT; mfbi->x_aoi_d = aoi_d.x_aoi_d; mfbi->y_aoi_d = aoi_d.y_aoi_d; - pr_debug("set AOI display offset of index %d to (%d,%d)\n", - mfbi->index, aoi_d.x_aoi_d, aoi_d.y_aoi_d); fsl_diu_check_var(&info->var, info); fsl_diu_set_aoi(info); break; @@ -1016,14 +1103,11 @@ static int fsl_diu_ioctl(struct fb_info *info, unsigned int cmd, aoi_d.y_aoi_d = mfbi->y_aoi_d; if (copy_to_user(buf, &aoi_d, sizeof(aoi_d))) return -EFAULT; - pr_debug("get AOI display offset of index %d (%d,%d)\n", - mfbi->index, aoi_d.x_aoi_d, aoi_d.y_aoi_d); break; case MFB_GET_ALPHA: global_alpha = mfbi->g_alpha; if (copy_to_user(buf, &global_alpha, sizeof(global_alpha))) return -EFAULT; - pr_debug("get global alpha of index %d\n", mfbi->index); break; case MFB_SET_ALPHA: /* set panel information */ @@ -1032,7 +1116,6 @@ static int fsl_diu_ioctl(struct fb_info *info, unsigned int cmd, ad->src_size_g_alpha = (ad->src_size_g_alpha & (~0xff)) | (global_alpha & 0xff); mfbi->g_alpha = global_alpha; - pr_debug("set global alpha for index %d\n", mfbi->index); break; case MFB_SET_CHROMA_KEY: /* set panel winformation */ @@ -1060,27 +1143,9 @@ static int fsl_diu_ioctl(struct fb_info *info, unsigned int cmd, ad->ckmin_g = ck.green_min; ad->ckmin_b = ck.blue_min; } - pr_debug("set chroma key\n"); break; - case FBIOGET_GWINFO: - if (mfbi->type == MFB_TYPE_OFF) - return -ENODEV; - /* get graphic window information */ - if (copy_to_user(buf, ad, sizeof(*ad))) - return -EFAULT; - break; - case FBIOGET_HWCINFO: - pr_debug("FBIOGET_HWCINFO:0x%08x\n", FBIOGET_HWCINFO); - break; - case FBIOPUT_MODEINFO: - pr_debug("FBIOPUT_MODEINFO:0x%08x\n", FBIOPUT_MODEINFO); - break; - case FBIOGET_DISPINFO: - pr_debug("FBIOGET_DISPINFO:0x%08x\n", FBIOGET_DISPINFO); - break; - default: - printk(KERN_ERR "Unknown ioctl command (0x%08X)\n", cmd); + dev_err(info->dev, "unknown ioctl command (0x%08X)\n", cmd); return -ENOIOCTLCMD; } @@ -1095,22 +1160,18 @@ static int fsl_diu_open(struct fb_info *info, int user) int res = 0; /* free boot splash memory on first /dev/fb0 open */ - if (!mfbi->index && diu_ops.release_bootmem) + if ((mfbi->index == PLANE0) && diu_ops.release_bootmem) diu_ops.release_bootmem(); spin_lock(&diu_lock); mfbi->count++; if (mfbi->count == 1) { - pr_debug("open plane index %d\n", mfbi->index); fsl_diu_check_var(&info->var, info); res = fsl_diu_set_par(info); if (res < 0) mfbi->count--; - else { - res = fsl_diu_enable_panel(info); - if (res < 0) - mfbi->count--; - } + else + fsl_diu_enable_panel(info); } spin_unlock(&diu_lock); @@ -1126,12 +1187,9 @@ static int fsl_diu_release(struct fb_info *info, int user) spin_lock(&diu_lock); mfbi->count--; - if (mfbi->count == 0) { - pr_debug("release plane index %d\n", mfbi->index); - res = fsl_diu_disable_panel(info); - if (res < 0) - mfbi->count++; - } + if (mfbi->count == 0) + fsl_diu_disable_panel(info); + spin_unlock(&diu_lock); return res; } @@ -1141,7 +1199,6 @@ static struct fb_ops fsl_diu_ops = { .fb_check_var = fsl_diu_check_var, .fb_set_par = fsl_diu_set_par, .fb_setcolreg = fsl_diu_setcolreg, - .fb_blank = fsl_diu_blank, .fb_pan_display = fsl_diu_pan_display, .fb_fillrect = cfb_fillrect, .fb_copyarea = cfb_copyarea, @@ -1178,7 +1235,7 @@ static int __devinit install_fb(struct fb_info *info) if (init_fbinfo(info)) return -EINVAL; - if (mfbi->index == 0) { /* plane 0 */ + if (mfbi->index == PLANE0) { if (mfbi->edid_data) { /* Now build modedb from EDID */ fb_edid_to_monspecs(mfbi->edid_data, &info->monspecs); @@ -1192,43 +1249,23 @@ static int __devinit install_fb(struct fb_info *info) } else { aoi_mode = init_aoi_mode; } - pr_debug("mode used = %s\n", aoi_mode); - rc = fb_find_mode(&info->var, info, aoi_mode, db, dbsize, - &fsl_diu_default_mode, default_bpp); - switch (rc) { - case 1: - pr_debug("using mode specified in @mode\n"); - break; - case 2: - pr_debug("using mode specified in @mode " - "with ignored refresh rate\n"); - break; - case 3: - pr_debug("using mode default mode\n"); - break; - case 4: - pr_debug("using mode from list\n"); - break; - default: - pr_debug("rc = %d\n", rc); - pr_debug("failed to find mode\n"); + rc = fb_find_mode(&info->var, info, aoi_mode, db, dbsize, NULL, + default_bpp); + if (!rc) { /* * For plane 0 we continue and look into * driver's internal modedb. */ - if (mfbi->index == 0 && mfbi->edid_data) + if ((mfbi->index == PLANE0) && mfbi->edid_data) has_default_mode = 0; else return -EINVAL; - break; } if (!has_default_mode) { rc = fb_find_mode(&info->var, info, aoi_mode, fsl_diu_mode_db, - ARRAY_SIZE(fsl_diu_mode_db), - &fsl_diu_default_mode, - default_bpp); - if (rc > 0 && rc < 5) + ARRAY_SIZE(fsl_diu_mode_db), NULL, default_bpp); + if (rc) has_default_mode = 1; } @@ -1256,33 +1293,22 @@ static int __devinit install_fb(struct fb_info *info) fb_videomode_to_var(&info->var, modedb); } - pr_debug("xres_virtual %d\n", info->var.xres_virtual); - pr_debug("bits_per_pixel %d\n", info->var.bits_per_pixel); - - pr_debug("info->var.yres_virtual = %d\n", info->var.yres_virtual); - pr_debug("info->fix.line_length = %d\n", info->fix.line_length); - - if (mfbi->type == MFB_TYPE_OFF) - mfbi->blank = FB_BLANK_NORMAL; - else - mfbi->blank = FB_BLANK_UNBLANK; - if (fsl_diu_check_var(&info->var, info)) { - printk(KERN_ERR "fb_check_var failed"); + dev_err(info->dev, "fsl_diu_check_var failed\n"); + unmap_video_memory(info); fb_dealloc_cmap(&info->cmap); return -EINVAL; } if (register_framebuffer(info) < 0) { - printk(KERN_ERR "register_framebuffer failed"); + dev_err(info->dev, "register_framebuffer failed\n"); unmap_video_memory(info); fb_dealloc_cmap(&info->cmap); return -EINVAL; } mfbi->registered = 1; - printk(KERN_INFO "fb%d: %s fb device registered successfully.\n", - info->node, info->fix.id); + dev_info(info->dev, "%s registered successfully\n", mfbi->id); return 0; } @@ -1294,7 +1320,7 @@ static void uninstall_fb(struct fb_info *info) if (!mfbi->registered) return; - if (mfbi->index == 0) + if (mfbi->index == PLANE0) kfree(mfbi->edid_data); unregister_framebuffer(info); @@ -1307,20 +1333,20 @@ static void uninstall_fb(struct fb_info *info) static irqreturn_t fsl_diu_isr(int irq, void *dev_id) { - struct diu *hw = dr.diu_reg; + struct diu __iomem *hw = dev_id; unsigned int status = in_be32(&hw->int_status); if (status) { /* This is the workaround for underrun */ if (status & INT_UNDRUN) { out_be32(&hw->diu_mode, 0); - pr_debug("Err: DIU occurs underrun!\n"); udelay(1); out_be32(&hw->diu_mode, 1); } #if defined(CONFIG_NOT_COHERENT_CACHE) else if (status & INT_VSYNC) { unsigned int i; + for (i = 0; i < coherence_data_size; i += d_cache_line_size) __asm__ __volatile__ ( @@ -1333,43 +1359,38 @@ static irqreturn_t fsl_diu_isr(int irq, void *dev_id) return IRQ_NONE; } -static int request_irq_local(int irq) +static int request_irq_local(struct fsl_diu_data *machine_data) { - unsigned long status, ints; - struct diu *hw; + struct diu __iomem *hw = machine_data->diu_reg; + u32 ints; int ret; - hw = dr.diu_reg; - /* Read to clear the status */ - status = in_be32(&hw->int_status); + in_be32(&hw->int_status); - ret = request_irq(irq, fsl_diu_isr, 0, "diu", NULL); - if (ret) - pr_info("Request diu IRQ failed.\n"); - else { + ret = request_irq(machine_data->irq, fsl_diu_isr, 0, "fsl-diu-fb", hw); + if (!ret) { ints = INT_PARERR | INT_LS_BF_VS; #if !defined(CONFIG_NOT_COHERENT_CACHE) ints |= INT_VSYNC; #endif - if (dr.mode == MFB_MODE2 || dr.mode == MFB_MODE3) - ints |= INT_VSYNC_WB; /* Read to clear the status */ - status = in_be32(&hw->int_status); + in_be32(&hw->int_status); out_be32(&hw->int_mask, ints); } + return ret; } -static void free_irq_local(int irq) +static void free_irq_local(struct fsl_diu_data *machine_data) { - struct diu *hw = dr.diu_reg; + struct diu __iomem *hw = machine_data->diu_reg; /* Disable all LCDC interrupt */ out_be32(&hw->int_mask, 0x1f); - free_irq(irq, NULL); + free_irq(machine_data->irq, NULL); } #ifdef CONFIG_PM @@ -1406,49 +1427,42 @@ static int fsl_diu_resume(struct platform_device *ofdev) static int allocate_buf(struct device *dev, struct diu_addr *buf, u32 size, u32 bytes_align) { - u32 offset, ssize; - u32 mask; - dma_addr_t paddr = 0; + u32 offset; + dma_addr_t mask; - ssize = size + bytes_align; - buf->vaddr = dma_alloc_coherent(dev, ssize, &paddr, GFP_DMA | - __GFP_ZERO); + buf->vaddr = + dma_alloc_coherent(dev, size + bytes_align, &buf->paddr, + GFP_DMA | __GFP_ZERO); if (!buf->vaddr) return -ENOMEM; - buf->paddr = (__u32) paddr; - mask = bytes_align - 1; - offset = (u32)buf->paddr & mask; + offset = buf->paddr & mask; if (offset) { buf->offset = bytes_align - offset; - buf->paddr = (u32)buf->paddr + offset; + buf->paddr = buf->paddr + offset; } else buf->offset = 0; + return 0; } static void free_buf(struct device *dev, struct diu_addr *buf, u32 size, u32 bytes_align) { - dma_free_coherent(dev, size + bytes_align, - buf->vaddr, (buf->paddr - buf->offset)); - return; + dma_free_coherent(dev, size + bytes_align, buf->vaddr, + buf->paddr - buf->offset); } static ssize_t store_monitor(struct device *device, struct device_attribute *attr, const char *buf, size_t count) { - int old_monitor_port; - unsigned long val; + enum fsl_diu_monitor_port old_monitor_port; struct fsl_diu_data *machine_data = container_of(attr, struct fsl_diu_data, dev_attr); - if (strict_strtoul(buf, 10, &val)) - return 0; - old_monitor_port = machine_data->monitor_port; - machine_data->monitor_port = diu_ops.set_sysfs_monitor_port(val); + machine_data->monitor_port = fsl_diu_name_to_port(buf); if (old_monitor_port != machine_data->monitor_port) { /* All AOIs need adjust pixel format @@ -1468,16 +1482,25 @@ static ssize_t show_monitor(struct device *device, { struct fsl_diu_data *machine_data = container_of(attr, struct fsl_diu_data, dev_attr); - return diu_ops.show_monitor_port(machine_data->monitor_port, buf); + + switch (machine_data->monitor_port) { + case FSL_DIU_PORT_DVI: + return sprintf(buf, "DVI\n"); + case FSL_DIU_PORT_LVDS: + return sprintf(buf, "Single-link LVDS\n"); + case FSL_DIU_PORT_DLVDS: + return sprintf(buf, "Dual-link LVDS\n"); + } + + return 0; } -static int __devinit fsl_diu_probe(struct platform_device *ofdev) +static int __devinit fsl_diu_probe(struct platform_device *pdev) { - struct device_node *np = ofdev->dev.of_node; + struct device_node *np = pdev->dev.of_node; struct mfb_info *mfbi; - phys_addr_t dummy_ad_addr; + phys_addr_t dummy_ad_addr = 0; int ret, i, error = 0; - struct resource res; struct fsl_diu_data *machine_data; int diu_mode; @@ -1485,11 +1508,13 @@ static int __devinit fsl_diu_probe(struct platform_device *ofdev) if (!machine_data) return -ENOMEM; + spin_lock_init(&machine_data->reg_lock); + for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++) { machine_data->fsl_diu_info[i] = - framebuffer_alloc(sizeof(struct mfb_info), &ofdev->dev); + framebuffer_alloc(sizeof(struct mfb_info), &pdev->dev); if (!machine_data->fsl_diu_info[i]) { - dev_err(&ofdev->dev, "cannot allocate memory\n"); + dev_err(&pdev->dev, "cannot allocate memory\n"); ret = -ENOMEM; goto error2; } @@ -1497,7 +1522,7 @@ static int __devinit fsl_diu_probe(struct platform_device *ofdev) memcpy(mfbi, &mfb_template[i], sizeof(struct mfb_info)); mfbi->parent = machine_data; - if (mfbi->index == 0) { + if (mfbi->index == PLANE0) { const u8 *prop; int len; @@ -1509,60 +1534,49 @@ static int __devinit fsl_diu_probe(struct platform_device *ofdev) } } - ret = of_address_to_resource(np, 0, &res); - if (ret) { - dev_err(&ofdev->dev, "could not obtain DIU address\n"); - goto error; - } - if (!res.start) { - dev_err(&ofdev->dev, "invalid DIU address\n"); - goto error; - } - dev_dbg(&ofdev->dev, "%s, res.start: 0x%08x\n", __func__, res.start); - - dr.diu_reg = ioremap(res.start, sizeof(struct diu)); - if (!dr.diu_reg) { - dev_err(&ofdev->dev, "Err: can't map DIU registers!\n"); + machine_data->diu_reg = of_iomap(np, 0); + if (!machine_data->diu_reg) { + dev_err(&pdev->dev, "cannot map DIU registers\n"); ret = -EFAULT; goto error2; } - diu_mode = in_be32(&dr.diu_reg->diu_mode); - if (diu_mode != MFB_MODE1) - out_be32(&dr.diu_reg->diu_mode, 0); /* disable DIU */ + diu_mode = in_be32(&machine_data->diu_reg->diu_mode); + if (diu_mode == MFB_MODE0) + out_be32(&machine_data->diu_reg->diu_mode, 0); /* disable DIU */ /* Get the IRQ of the DIU */ machine_data->irq = irq_of_parse_and_map(np, 0); if (!machine_data->irq) { - dev_err(&ofdev->dev, "could not get DIU IRQ\n"); + dev_err(&pdev->dev, "could not get DIU IRQ\n"); ret = -EINVAL; goto error; } machine_data->monitor_port = monitor_port; /* Area descriptor memory pool aligns to 64-bit boundary */ - if (allocate_buf(&ofdev->dev, &pool.ad, + if (allocate_buf(&pdev->dev, &machine_data->ad, sizeof(struct diu_ad) * FSL_AOI_NUM, 8)) return -ENOMEM; /* Get memory for Gamma Table - 32-byte aligned memory */ - if (allocate_buf(&ofdev->dev, &pool.gamma, 768, 32)) { + if (allocate_buf(&pdev->dev, &machine_data->gamma, 768, 32)) { ret = -ENOMEM; goto error; } /* For performance, cursor bitmap buffer aligns to 32-byte boundary */ - if (allocate_buf(&ofdev->dev, &pool.cursor, MAX_CURS * MAX_CURS * 2, - 32)) { + if (allocate_buf(&pdev->dev, &machine_data->cursor, + MAX_CURS * MAX_CURS * 2, 32)) { ret = -ENOMEM; goto error; } i = ARRAY_SIZE(machine_data->fsl_diu_info); - machine_data->dummy_ad = (struct diu_ad *) - ((u32)pool.ad.vaddr + pool.ad.offset) + i; - machine_data->dummy_ad->paddr = pool.ad.paddr + + machine_data->dummy_ad = (struct diu_ad *)((u32)machine_data->ad.vaddr + + machine_data->ad.offset) + i; + machine_data->dummy_ad->paddr = machine_data->ad.paddr + i * sizeof(struct diu_ad); machine_data->dummy_aoi_virt = fsl_diu_alloc(64, &dummy_ad_addr); if (!machine_data->dummy_aoi_virt) { @@ -1581,30 +1595,29 @@ static int __devinit fsl_diu_probe(struct platform_device *ofdev) * Let DIU display splash screen if it was pre-initialized * by the bootloader, set dummy area descriptor otherwise. */ - if (diu_mode != MFB_MODE1) - out_be32(&dr.diu_reg->desc[0], machine_data->dummy_ad->paddr); + if (diu_mode == MFB_MODE0) + out_be32(&machine_data->diu_reg->desc[0], + machine_data->dummy_ad->paddr); - out_be32(&dr.diu_reg->desc[1], machine_data->dummy_ad->paddr); - out_be32(&dr.diu_reg->desc[2], machine_data->dummy_ad->paddr); + out_be32(&machine_data->diu_reg->desc[1], machine_data->dummy_ad->paddr); + out_be32(&machine_data->diu_reg->desc[2], machine_data->dummy_ad->paddr); for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++) { machine_data->fsl_diu_info[i]->fix.smem_start = 0; mfbi = machine_data->fsl_diu_info[i]->par; - mfbi->ad = (struct diu_ad *)((u32)pool.ad.vaddr - + pool.ad.offset) + i; - mfbi->ad->paddr = pool.ad.paddr + i * sizeof(struct diu_ad); + mfbi->ad = (struct diu_ad *)((u32)machine_data->ad.vaddr + + machine_data->ad.offset) + i; + mfbi->ad->paddr = + machine_data->ad.paddr + i * sizeof(struct diu_ad); ret = install_fb(machine_data->fsl_diu_info[i]); if (ret) { - dev_err(&ofdev->dev, - "Failed to register framebuffer %d\n", - i); + dev_err(&pdev->dev, "could not register fb %d\n", i); goto error; } } - if (request_irq_local(machine_data->irq)) { - dev_err(machine_data->fsl_diu_info[0]->dev, - "could not request irq for diu."); + if (request_irq_local(machine_data)) { + dev_err(&pdev->dev, "could not claim irq\n"); goto error; } @@ -1616,29 +1629,28 @@ static int __devinit fsl_diu_probe(struct platform_device *ofdev) error = device_create_file(machine_data->fsl_diu_info[0]->dev, &machine_data->dev_attr); if (error) { - dev_err(machine_data->fsl_diu_info[0]->dev, - "could not create sysfs %s file\n", + dev_err(&pdev->dev, "could not create sysfs file %s\n", machine_data->dev_attr.attr.name); } - dev_set_drvdata(&ofdev->dev, machine_data); + dev_set_drvdata(&pdev->dev, machine_data); return 0; error: - for (i = ARRAY_SIZE(machine_data->fsl_diu_info); - i > 0; i--) - uninstall_fb(machine_data->fsl_diu_info[i - 1]); - if (pool.ad.vaddr) - free_buf(&ofdev->dev, &pool.ad, + for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++) + uninstall_fb(machine_data->fsl_diu_info[i]); + + if (machine_data->ad.vaddr) + free_buf(&pdev->dev, &machine_data->ad, sizeof(struct diu_ad) * FSL_AOI_NUM, 8); - if (pool.gamma.vaddr) - free_buf(&ofdev->dev, &pool.gamma, 768, 32); - if (pool.cursor.vaddr) - free_buf(&ofdev->dev, &pool.cursor, MAX_CURS * MAX_CURS * 2, - 32); + if (machine_data->gamma.vaddr) + free_buf(&pdev->dev, &machine_data->gamma, 768, 32); + if (machine_data->cursor.vaddr) + free_buf(&pdev->dev, &machine_data->cursor, + MAX_CURS * MAX_CURS * 2, 32); if (machine_data->dummy_aoi_virt) fsl_diu_free(machine_data->dummy_aoi_virt, 64); - iounmap(dr.diu_reg); + iounmap(machine_data->diu_reg); error2: for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++) @@ -1649,28 +1661,27 @@ error2: return ret; } - -static int fsl_diu_remove(struct platform_device *ofdev) +static int fsl_diu_remove(struct platform_device *pdev) { struct fsl_diu_data *machine_data; int i; - machine_data = dev_get_drvdata(&ofdev->dev); + machine_data = dev_get_drvdata(&pdev->dev); disable_lcdc(machine_data->fsl_diu_info[0]); - free_irq_local(machine_data->irq); - for (i = ARRAY_SIZE(machine_data->fsl_diu_info); i > 0; i--) - uninstall_fb(machine_data->fsl_diu_info[i - 1]); - if (pool.ad.vaddr) - free_buf(&ofdev->dev, &pool.ad, + free_irq_local(machine_data); + for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++) + uninstall_fb(machine_data->fsl_diu_info[i]); + if (machine_data->ad.vaddr) + free_buf(&pdev->dev, &machine_data->ad, sizeof(struct diu_ad) * FSL_AOI_NUM, 8); - if (pool.gamma.vaddr) - free_buf(&ofdev->dev, &pool.gamma, 768, 32); - if (pool.cursor.vaddr) - free_buf(&ofdev->dev, &pool.cursor, MAX_CURS * MAX_CURS * 2, - 32); + if (machine_data->gamma.vaddr) + free_buf(&pdev->dev, &machine_data->gamma, 768, 32); + if (machine_data->cursor.vaddr) + free_buf(&pdev->dev, &machine_data->cursor, + MAX_CURS * MAX_CURS * 2, 32); if (machine_data->dummy_aoi_virt) fsl_diu_free(machine_data->dummy_aoi_virt, 64); - iounmap(dr.diu_reg); + iounmap(machine_data->diu_reg); for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++) if (machine_data->fsl_diu_info[i]) framebuffer_release(machine_data->fsl_diu_info[i]); @@ -1692,8 +1703,7 @@ static int __init fsl_diu_setup(char *options) if (!*opt) continue; if (!strncmp(opt, "monitor=", 8)) { - if (!strict_strtoul(opt + 8, 10, &val) && (val <= 2)) - monitor_port = val; + monitor_port = fsl_diu_name_to_port(opt + 8); } else if (!strncmp(opt, "bpp=", 4)) { if (!strict_strtoul(opt + 4, 10, &val)) default_bpp = val; @@ -1720,7 +1730,7 @@ MODULE_DEVICE_TABLE(of, fsl_diu_match); static struct platform_driver fsl_diu_driver = { .driver = { - .name = "fsl_diu", + .name = "fsl-diu-fb", .owner = THIS_MODULE, .of_match_table = fsl_diu_match, }, @@ -1746,48 +1756,54 @@ static int __init fsl_diu_init(void) if (fb_get_options("fslfb", &option)) return -ENODEV; fsl_diu_setup(option); +#else + monitor_port = fsl_diu_name_to_port(monitor_string); #endif - printk(KERN_INFO "Freescale DIU driver\n"); + pr_info("Freescale Display Interface Unit (DIU) framebuffer driver\n"); #ifdef CONFIG_NOT_COHERENT_CACHE np = of_find_node_by_type(NULL, "cpu"); if (!np) { - printk(KERN_ERR "Err: can't find device node 'cpu'\n"); + pr_err("fsl-diu-fb: can't find 'cpu' device node\n"); return -ENODEV; } prop = of_get_property(np, "d-cache-size", NULL); if (prop == NULL) { + pr_err("fsl-diu-fb: missing 'd-cache-size' property' " + "in 'cpu' node\n"); of_node_put(np); return -ENODEV; } - /* Freescale PLRU requires 13/8 times the cache size to do a proper - displacement flush + /* + * Freescale PLRU requires 13/8 times the cache size to do a proper + * displacement flush */ - coherence_data_size = *prop * 13; + coherence_data_size = be32_to_cpup(prop) * 13; coherence_data_size /= 8; prop = of_get_property(np, "d-cache-line-size", NULL); if (prop == NULL) { + pr_err("fsl-diu-fb: missing 'd-cache-line-size' property' " + "in 'cpu' node\n"); of_node_put(np); return -ENODEV; } - d_cache_line_size = *prop; + d_cache_line_size = be32_to_cpup(prop); of_node_put(np); coherence_data = vmalloc(coherence_data_size); if (!coherence_data) return -ENOMEM; #endif + ret = platform_driver_register(&fsl_diu_driver); if (ret) { - printk(KERN_ERR - "fsl-diu: failed to register platform driver\n"); + pr_err("fsl-diu-fb: failed to register platform driver\n"); #if defined(CONFIG_NOT_COHERENT_CACHE) vfree(coherence_data); #endif - iounmap(dr.diu_reg); } return ret; } @@ -1811,8 +1827,8 @@ module_param_named(mode, fb_mode, charp, 0); MODULE_PARM_DESC(mode, "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" "); module_param_named(bpp, default_bpp, ulong, 0); -MODULE_PARM_DESC(bpp, "Specify bit-per-pixel if not specified mode"); -module_param_named(monitor, monitor_port, int, 0); -MODULE_PARM_DESC(monitor, - "Specify the monitor port (0, 1 or 2) if supported by the platform"); +MODULE_PARM_DESC(bpp, "Specify bit-per-pixel if not specified in 'mode'"); +module_param_named(monitor, monitor_string, charp, 0); +MODULE_PARM_DESC(monitor, "Specify the monitor port " + "(\"dvi\", \"lvds\", or \"dlvds\") if supported by the platform"); diff --git a/drivers/video/g364fb.c b/drivers/video/g364fb.c index d662317d85e3..223896cc5f7d 100644 --- a/drivers/video/g364fb.c +++ b/drivers/video/g364fb.c @@ -149,10 +149,11 @@ int g364fb_cursor(struct fb_info *info, struct fb_cursor *cursor) static int g364fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) { - if (var->xoffset || var->yoffset + var->yres > var->yres_virtual) + if (var->xoffset || + var->yoffset + info->var.yres > info->var.yres_virtual) return -EINVAL; - *(unsigned int *) TOP_REG = var->yoffset * var->xres; + *(unsigned int *) TOP_REG = var->yoffset * info->var.xres; return 0; } diff --git a/drivers/video/grvga.c b/drivers/video/grvga.c new file mode 100644 index 000000000000..f37e02538203 --- /dev/null +++ b/drivers/video/grvga.c @@ -0,0 +1,579 @@ +/* + * Driver for Aeroflex Gaisler SVGACTRL framebuffer device. + * + * 2011 (c) Aeroflex Gaisler AB + * + * Full documentation of the core can be found here: + * http://www.gaisler.com/products/grlib/grip.pdf + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * Contributors: Kristoffer Glembo <kristoffer@gaisler.com> + * + */ + +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/of_platform.h> +#include <linux/of_device.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/tty.h> +#include <linux/mm.h> +#include <linux/fb.h> +#include <linux/io.h> + +struct grvga_regs { + u32 status; /* 0x00 */ + u32 video_length; /* 0x04 */ + u32 front_porch; /* 0x08 */ + u32 sync_length; /* 0x0C */ + u32 line_length; /* 0x10 */ + u32 fb_pos; /* 0x14 */ + u32 clk_vector[4]; /* 0x18 */ + u32 clut; /* 0x20 */ +}; + +struct grvga_par { + struct grvga_regs *regs; + u32 color_palette[16]; /* 16 entry pseudo palette used by fbcon in true color mode */ + int clk_sel; + int fb_alloced; /* = 1 if framebuffer is allocated in main memory */ +}; + + +static const struct fb_videomode grvga_modedb[] = { + { + /* 640x480 @ 60 Hz */ + NULL, 60, 640, 480, 40000, 48, 16, 39, 11, 96, 2, + 0, FB_VMODE_NONINTERLACED + }, { + /* 800x600 @ 60 Hz */ + NULL, 60, 800, 600, 25000, 88, 40, 23, 1, 128, 4, + 0, FB_VMODE_NONINTERLACED + }, { + /* 800x600 @ 72 Hz */ + NULL, 72, 800, 600, 20000, 64, 56, 23, 37, 120, 6, + 0, FB_VMODE_NONINTERLACED + }, { + /* 1024x768 @ 60 Hz */ + NULL, 60, 1024, 768, 15385, 160, 24, 29, 3, 136, 6, + 0, FB_VMODE_NONINTERLACED + } + }; + +static struct fb_fix_screeninfo grvga_fix __initdata = { + .id = "AG SVGACTRL", + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_PSEUDOCOLOR, + .xpanstep = 0, + .ypanstep = 1, + .ywrapstep = 0, + .accel = FB_ACCEL_NONE, +}; + +static int grvga_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct grvga_par *par = info->par; + int i; + + if (!var->xres) + var->xres = 1; + if (!var->yres) + var->yres = 1; + if (var->bits_per_pixel <= 8) + var->bits_per_pixel = 8; + else if (var->bits_per_pixel <= 16) + var->bits_per_pixel = 16; + else if (var->bits_per_pixel <= 24) + var->bits_per_pixel = 24; + else if (var->bits_per_pixel <= 32) + var->bits_per_pixel = 32; + else + return -EINVAL; + + var->xres_virtual = var->xres; + var->yres_virtual = 2*var->yres; + + if (info->fix.smem_len) { + if ((var->yres_virtual*var->xres_virtual*var->bits_per_pixel/8) > info->fix.smem_len) + return -ENOMEM; + } + + /* Which clocks that are available can be read out in these registers */ + for (i = 0; i <= 3 ; i++) { + if (var->pixclock == par->regs->clk_vector[i]) + break; + } + if (i <= 3) + par->clk_sel = i; + else + return -EINVAL; + + switch (info->var.bits_per_pixel) { + case 8: + var->red = (struct fb_bitfield) {0, 8, 0}; /* offset, length, msb-right */ + var->green = (struct fb_bitfield) {0, 8, 0}; + var->blue = (struct fb_bitfield) {0, 8, 0}; + var->transp = (struct fb_bitfield) {0, 0, 0}; + break; + case 16: + var->red = (struct fb_bitfield) {11, 5, 0}; + var->green = (struct fb_bitfield) {5, 6, 0}; + var->blue = (struct fb_bitfield) {0, 5, 0}; + var->transp = (struct fb_bitfield) {0, 0, 0}; + break; + case 24: + case 32: + var->red = (struct fb_bitfield) {16, 8, 0}; + var->green = (struct fb_bitfield) {8, 8, 0}; + var->blue = (struct fb_bitfield) {0, 8, 0}; + var->transp = (struct fb_bitfield) {24, 8, 0}; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int grvga_set_par(struct fb_info *info) +{ + + u32 func = 0; + struct grvga_par *par = info->par; + + __raw_writel(((info->var.yres - 1) << 16) | (info->var.xres - 1), + &par->regs->video_length); + + __raw_writel((info->var.lower_margin << 16) | (info->var.right_margin), + &par->regs->front_porch); + + __raw_writel((info->var.vsync_len << 16) | (info->var.hsync_len), + &par->regs->sync_length); + + __raw_writel(((info->var.yres + info->var.lower_margin + info->var.upper_margin + info->var.vsync_len - 1) << 16) | + (info->var.xres + info->var.right_margin + info->var.left_margin + info->var.hsync_len - 1), + &par->regs->line_length); + + switch (info->var.bits_per_pixel) { + case 8: + info->fix.visual = FB_VISUAL_PSEUDOCOLOR; + func = 1; + break; + case 16: + info->fix.visual = FB_VISUAL_TRUECOLOR; + func = 2; + break; + case 24: + case 32: + info->fix.visual = FB_VISUAL_TRUECOLOR; + func = 3; + break; + default: + return -EINVAL; + } + + __raw_writel((par->clk_sel << 6) | (func << 4) | 1, + &par->regs->status); + + info->fix.line_length = (info->var.xres_virtual*info->var.bits_per_pixel)/8; + return 0; +} + +static int grvga_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *info) +{ + struct grvga_par *par; + par = info->par; + + if (regno >= 256) /* Size of CLUT */ + return -EINVAL; + + if (info->var.grayscale) { + /* grayscale = 0.30*R + 0.59*G + 0.11*B */ + red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8; + } + + + +#define CNVT_TOHW(val, width) ((((val)<<(width))+0x7FFF-(val))>>16) + + red = CNVT_TOHW(red, info->var.red.length); + green = CNVT_TOHW(green, info->var.green.length); + blue = CNVT_TOHW(blue, info->var.blue.length); + transp = CNVT_TOHW(transp, info->var.transp.length); + +#undef CNVT_TOHW + + /* In PSEUDOCOLOR we use the hardware CLUT */ + if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR) + __raw_writel((regno << 24) | (red << 16) | (green << 8) | blue, + &par->regs->clut); + + /* Truecolor uses the pseudo palette */ + else if (info->fix.visual == FB_VISUAL_TRUECOLOR) { + u32 v; + if (regno >= 16) + return -EINVAL; + + + v = (red << info->var.red.offset) | + (green << info->var.green.offset) | + (blue << info->var.blue.offset) | + (transp << info->var.transp.offset); + + ((u32 *) (info->pseudo_palette))[regno] = v; + } + return 0; +} + +static int grvga_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct grvga_par *par = info->par; + struct fb_fix_screeninfo *fix = &info->fix; + u32 base_addr; + + if (var->xoffset != 0) + return -EINVAL; + + base_addr = fix->smem_start + (var->yoffset * fix->line_length); + base_addr &= ~3UL; + + /* Set framebuffer base address */ + __raw_writel(base_addr, + &par->regs->fb_pos); + + return 0; +} + +static struct fb_ops grvga_ops = { + .owner = THIS_MODULE, + .fb_check_var = grvga_check_var, + .fb_set_par = grvga_set_par, + .fb_setcolreg = grvga_setcolreg, + .fb_pan_display = grvga_pan_display, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit +}; + +static int __init grvga_parse_custom(char *options, + struct fb_var_screeninfo *screendata) +{ + char *this_opt; + int count = 0; + if (!options || !*options) + return -1; + + while ((this_opt = strsep(&options, " ")) != NULL) { + if (!*this_opt) + continue; + + switch (count) { + case 0: + screendata->pixclock = simple_strtoul(this_opt, NULL, 0); + count++; + break; + case 1: + screendata->xres = screendata->xres_virtual = simple_strtoul(this_opt, NULL, 0); + count++; + break; + case 2: + screendata->right_margin = simple_strtoul(this_opt, NULL, 0); + count++; + break; + case 3: + screendata->hsync_len = simple_strtoul(this_opt, NULL, 0); + count++; + break; + case 4: + screendata->left_margin = simple_strtoul(this_opt, NULL, 0); + count++; + break; + case 5: + screendata->yres = screendata->yres_virtual = simple_strtoul(this_opt, NULL, 0); + count++; + break; + case 6: + screendata->lower_margin = simple_strtoul(this_opt, NULL, 0); + count++; + break; + case 7: + screendata->vsync_len = simple_strtoul(this_opt, NULL, 0); + count++; + break; + case 8: + screendata->upper_margin = simple_strtoul(this_opt, NULL, 0); + count++; + break; + case 9: + screendata->bits_per_pixel = simple_strtoul(this_opt, NULL, 0); + count++; + break; + default: + return -1; + } + } + screendata->activate = FB_ACTIVATE_NOW; + screendata->vmode = FB_VMODE_NONINTERLACED; + return 0; +} + +static int __devinit grvga_probe(struct platform_device *dev) +{ + struct fb_info *info; + int retval = -ENOMEM; + unsigned long virtual_start; + unsigned long grvga_fix_addr = 0; + unsigned long physical_start = 0; + unsigned long grvga_mem_size = 0; + struct grvga_par *par = NULL; + char *options = NULL, *mode_opt = NULL; + + info = framebuffer_alloc(sizeof(struct grvga_par), &dev->dev); + if (!info) { + dev_err(&dev->dev, "framebuffer_alloc failed\n"); + return -ENOMEM; + } + + /* Expecting: "grvga: modestring, [addr:<framebuffer physical address>], [size:<framebuffer size>] + * + * If modestring is custom:<custom mode string> we parse the string which then contains all videoparameters + * If address is left out, we allocate memory, + * if size is left out we only allocate enough to support the given mode. + */ + if (fb_get_options("grvga", &options)) { + retval = -ENODEV; + goto err; + } + + if (!options || !*options) + options = "640x480-8@60"; + + while (1) { + char *this_opt = strsep(&options, ","); + + if (!this_opt) + break; + + if (!strncmp(this_opt, "custom", 6)) { + if (grvga_parse_custom(this_opt, &info->var) < 0) { + dev_err(&dev->dev, "Failed to parse custom mode (%s).\n", this_opt); + retval = -EINVAL; + goto err1; + } + } else if (!strncmp(this_opt, "addr", 4)) + grvga_fix_addr = simple_strtoul(this_opt + 5, NULL, 16); + else if (!strncmp(this_opt, "size", 4)) + grvga_mem_size = simple_strtoul(this_opt + 5, NULL, 0); + else + mode_opt = this_opt; + } + + par = info->par; + info->fbops = &grvga_ops; + info->fix = grvga_fix; + info->pseudo_palette = par->color_palette; + info->flags = FBINFO_DEFAULT | FBINFO_PARTIAL_PAN_OK | FBINFO_HWACCEL_YPAN; + info->fix.smem_len = grvga_mem_size; + + if (!request_mem_region(dev->resource[0].start, resource_size(&dev->resource[0]), "grlib-svgactrl regs")) { + dev_err(&dev->dev, "registers already mapped\n"); + retval = -EBUSY; + goto err; + } + + par->regs = of_ioremap(&dev->resource[0], 0, + resource_size(&dev->resource[0]), + "grlib-svgactrl regs"); + + if (!par->regs) { + dev_err(&dev->dev, "failed to map registers\n"); + retval = -ENOMEM; + goto err1; + } + + retval = fb_alloc_cmap(&info->cmap, 256, 0); + if (retval < 0) { + dev_err(&dev->dev, "failed to allocate mem with fb_alloc_cmap\n"); + retval = -ENOMEM; + goto err2; + } + + if (mode_opt) { + retval = fb_find_mode(&info->var, info, mode_opt, + grvga_modedb, sizeof(grvga_modedb), &grvga_modedb[0], 8); + if (!retval || retval == 4) { + retval = -EINVAL; + goto err3; + } + } + + if (!grvga_mem_size) + grvga_mem_size = info->var.xres_virtual * info->var.yres_virtual * info->var.bits_per_pixel/8; + + if (grvga_fix_addr) { + /* Got framebuffer base address from argument list */ + + physical_start = grvga_fix_addr; + + if (!request_mem_region(physical_start, grvga_mem_size, dev->name)) { + dev_err(&dev->dev, "failed to request memory region\n"); + retval = -ENOMEM; + goto err3; + } + + virtual_start = (unsigned long) ioremap(physical_start, grvga_mem_size); + + if (!virtual_start) { + dev_err(&dev->dev, "error mapping framebuffer memory\n"); + retval = -ENOMEM; + goto err4; + } + } else { /* Allocate frambuffer memory */ + + unsigned long page; + + virtual_start = (unsigned long) __get_free_pages(GFP_DMA, + get_order(grvga_mem_size)); + if (!virtual_start) { + dev_err(&dev->dev, + "unable to allocate framebuffer memory (%lu bytes)\n", + grvga_mem_size); + retval = -ENOMEM; + goto err3; + } + + physical_start = dma_map_single(&dev->dev, (void *)virtual_start, grvga_mem_size, DMA_TO_DEVICE); + + /* Set page reserved so that mmap will work. This is necessary + * since we'll be remapping normal memory. + */ + for (page = virtual_start; + page < PAGE_ALIGN(virtual_start + grvga_mem_size); + page += PAGE_SIZE) { + SetPageReserved(virt_to_page(page)); + } + + par->fb_alloced = 1; + } + + memset((unsigned long *) virtual_start, 0, grvga_mem_size); + + info->screen_base = (char __iomem *) virtual_start; + info->fix.smem_start = physical_start; + info->fix.smem_len = grvga_mem_size; + + dev_set_drvdata(&dev->dev, info); + + dev_info(&dev->dev, + "Aeroflex Gaisler framebuffer device (fb%d), %dx%d-%d, using %luK of video memory @ %p\n", + info->node, info->var.xres, info->var.yres, info->var.bits_per_pixel, + grvga_mem_size >> 10, info->screen_base); + + retval = register_framebuffer(info); + if (retval < 0) { + dev_err(&dev->dev, "failed to register framebuffer\n"); + goto err4; + } + + __raw_writel(physical_start, &par->regs->fb_pos); + __raw_writel(__raw_readl(&par->regs->status) | 1, /* Enable framebuffer */ + &par->regs->status); + + return 0; + +err4: + dev_set_drvdata(&dev->dev, NULL); + if (grvga_fix_addr) { + release_mem_region(physical_start, grvga_mem_size); + iounmap((void *)virtual_start); + } else + kfree((void *)virtual_start); +err3: + fb_dealloc_cmap(&info->cmap); +err2: + of_iounmap(&dev->resource[0], par->regs, + resource_size(&dev->resource[0])); +err1: + release_mem_region(dev->resource[0].start, resource_size(&dev->resource[0])); +err: + framebuffer_release(info); + + return retval; +} + +static int __devexit grvga_remove(struct platform_device *device) +{ + struct fb_info *info = dev_get_drvdata(&device->dev); + struct grvga_par *par = info->par; + + if (info) { + unregister_framebuffer(info); + fb_dealloc_cmap(&info->cmap); + + of_iounmap(&device->resource[0], par->regs, + resource_size(&device->resource[0])); + release_mem_region(device->resource[0].start, resource_size(&device->resource[0])); + + if (!par->fb_alloced) { + release_mem_region(info->fix.smem_start, info->fix.smem_len); + iounmap(info->screen_base); + } else + kfree((void *)info->screen_base); + + framebuffer_release(info); + dev_set_drvdata(&device->dev, NULL); + } + + return 0; +} + +static struct of_device_id svgactrl_of_match[] = { + { + .name = "GAISLER_SVGACTRL", + }, + { + .name = "01_063", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, svgactrl_of_match); + +static struct platform_driver grvga_driver = { + .driver = { + .name = "grlib-svgactrl", + .owner = THIS_MODULE, + .of_match_table = svgactrl_of_match, + }, + .probe = grvga_probe, + .remove = __devexit_p(grvga_remove), +}; + + +static int __init grvga_init(void) +{ + return platform_driver_register(&grvga_driver); +} + +static void __exit grvga_exit(void) +{ + platform_driver_unregister(&grvga_driver); +} + +module_init(grvga_init); +module_exit(grvga_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Aeroflex Gaisler"); +MODULE_DESCRIPTION("Aeroflex Gaisler framebuffer device driver"); diff --git a/drivers/video/gxt4500.c b/drivers/video/gxt4500.c index 896e53dea906..0fad23f810a3 100644 --- a/drivers/video/gxt4500.c +++ b/drivers/video/gxt4500.c @@ -543,8 +543,8 @@ static int gxt4500_pan_display(struct fb_var_screeninfo *var, if (var->xoffset & 7) return -EINVAL; - if (var->xoffset + var->xres > var->xres_virtual || - var->yoffset + var->yres > var->yres_virtual) + if (var->xoffset + info->var.xres > info->var.xres_virtual || + var->yoffset + info->var.yres > info->var.yres_virtual) return -EINVAL; writereg(par, REFRESH_START, (var->xoffset << 16) | var->yoffset); diff --git a/drivers/video/hgafb.c b/drivers/video/hgafb.c index 4052718eefaa..4394389caf68 100644 --- a/drivers/video/hgafb.c +++ b/drivers/video/hgafb.c @@ -422,8 +422,8 @@ static int hgafb_pan_display(struct fb_var_screeninfo *var, var->xoffset) return -EINVAL; } else { - if (var->xoffset + var->xres > info->var.xres_virtual - || var->yoffset + var->yres > info->var.yres_virtual + if (var->xoffset + info->var.xres > info->var.xres_virtual + || var->yoffset + info->var.yres > info->var.yres_virtual || var->yoffset % 8) return -EINVAL; } diff --git a/drivers/video/imsttfb.c b/drivers/video/imsttfb.c index efb2c10656b0..8149356471e4 100644 --- a/drivers/video/imsttfb.c +++ b/drivers/video/imsttfb.c @@ -749,7 +749,7 @@ set_offset (struct fb_var_screeninfo *var, struct fb_info *info) { struct imstt_par *par = info->par; __u32 off = var->yoffset * (info->fix.line_length >> 3) - + ((var->xoffset * (var->bits_per_pixel >> 3)) >> 3); + + ((var->xoffset * (info->var.bits_per_pixel >> 3)) >> 3); write_reg_le32(par->dc_regs, SSR, off); } diff --git a/drivers/video/intelfb/intelfbhw.c b/drivers/video/intelfb/intelfbhw.c index 38065cf94ac4..fbad61da359f 100644 --- a/drivers/video/intelfb/intelfbhw.c +++ b/drivers/video/intelfb/intelfbhw.c @@ -390,12 +390,12 @@ int intelfbhw_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) xoffset = ROUND_DOWN_TO(var->xoffset, 8); yoffset = var->yoffset; - if ((xoffset + var->xres > var->xres_virtual) || - (yoffset + var->yres > var->yres_virtual)) + if ((xoffset + info->var.xres > info->var.xres_virtual) || + (yoffset + info->var.yres > info->var.yres_virtual)) return -EINVAL; offset = (yoffset * dinfo->pitch) + - (xoffset * var->bits_per_pixel) / 8; + (xoffset * info->var.bits_per_pixel) / 8; offset += dinfo->fb.offset << 12; diff --git a/drivers/video/mb862xx/mb862xx-i2c.c b/drivers/video/mb862xx/mb862xx-i2c.c index b953099edd8e..934081d2b7ae 100644 --- a/drivers/video/mb862xx/mb862xx-i2c.c +++ b/drivers/video/mb862xx/mb862xx-i2c.c @@ -23,7 +23,7 @@ static int mb862xx_i2c_wait_event(struct i2c_adapter *adap) u32 reg; do { - udelay(1); + udelay(10); reg = inreg(i2c, GC_I2C_BCR); if (reg & (I2C_INT | I2C_BER)) break; diff --git a/drivers/video/mb862xx/mb862xxfbdrv.c b/drivers/video/mb862xx/mb862xxfbdrv.c index ee1de3e26dec..c16ff1d62e91 100644 --- a/drivers/video/mb862xx/mb862xxfbdrv.c +++ b/drivers/video/mb862xx/mb862xxfbdrv.c @@ -278,7 +278,7 @@ static int mb862xxfb_pan(struct fb_var_screeninfo *var, reg = pack(var->yoffset, var->xoffset); outreg(disp, GC_L0WY_L0WX, reg); - reg = pack(var->yres_virtual, var->xres_virtual); + reg = pack(info->var.yres_virtual, info->var.xres_virtual); outreg(disp, GC_L0WH_L0WW, reg); return 0; } @@ -737,7 +737,7 @@ static int __devinit of_platform_mb862xx_probe(struct platform_device *ofdev) if (mb862xx_gdc_init(par)) goto io_unmap; - if (request_irq(par->irq, mb862xx_intr, IRQF_DISABLED, + if (request_irq(par->irq, mb862xx_intr, 0, DRV_NAME, (void *)par)) { dev_err(dev, "Cannot request irq\n"); goto io_unmap; @@ -1073,7 +1073,7 @@ static int __devinit mb862xx_pci_probe(struct pci_dev *pdev, if (mb862xx_pci_gdc_init(par)) goto io_unmap; - if (request_irq(par->irq, mb862xx_intr, IRQF_DISABLED | IRQF_SHARED, + if (request_irq(par->irq, mb862xx_intr, IRQF_SHARED, DRV_NAME, (void *)par)) { dev_err(dev, "Cannot request irq\n"); goto io_unmap; diff --git a/drivers/video/modedb.c b/drivers/video/modedb.c index cb175fe7abc0..a9a907c440d7 100644 --- a/drivers/video/modedb.c +++ b/drivers/video/modedb.c @@ -491,55 +491,56 @@ EXPORT_SYMBOL(vesa_modes); static int fb_try_mode(struct fb_var_screeninfo *var, struct fb_info *info, const struct fb_videomode *mode, unsigned int bpp) { - int err = 0; - - DPRINTK("Trying mode %s %dx%d-%d@%d\n", mode->name ? mode->name : "noname", - mode->xres, mode->yres, bpp, mode->refresh); - var->xres = mode->xres; - var->yres = mode->yres; - var->xres_virtual = mode->xres; - var->yres_virtual = mode->yres; - var->xoffset = 0; - var->yoffset = 0; - var->bits_per_pixel = bpp; - var->activate |= FB_ACTIVATE_TEST; - var->pixclock = mode->pixclock; - var->left_margin = mode->left_margin; - var->right_margin = mode->right_margin; - var->upper_margin = mode->upper_margin; - var->lower_margin = mode->lower_margin; - var->hsync_len = mode->hsync_len; - var->vsync_len = mode->vsync_len; - var->sync = mode->sync; - var->vmode = mode->vmode; - if (info->fbops->fb_check_var) - err = info->fbops->fb_check_var(var, info); - var->activate &= ~FB_ACTIVATE_TEST; - return err; + int err = 0; + + DPRINTK("Trying mode %s %dx%d-%d@%d\n", + mode->name ? mode->name : "noname", + mode->xres, mode->yres, bpp, mode->refresh); + var->xres = mode->xres; + var->yres = mode->yres; + var->xres_virtual = mode->xres; + var->yres_virtual = mode->yres; + var->xoffset = 0; + var->yoffset = 0; + var->bits_per_pixel = bpp; + var->activate |= FB_ACTIVATE_TEST; + var->pixclock = mode->pixclock; + var->left_margin = mode->left_margin; + var->right_margin = mode->right_margin; + var->upper_margin = mode->upper_margin; + var->lower_margin = mode->lower_margin; + var->hsync_len = mode->hsync_len; + var->vsync_len = mode->vsync_len; + var->sync = mode->sync; + var->vmode = mode->vmode; + if (info->fbops->fb_check_var) + err = info->fbops->fb_check_var(var, info); + var->activate &= ~FB_ACTIVATE_TEST; + return err; } /** - * fb_find_mode - finds a valid video mode - * @var: frame buffer user defined part of display - * @info: frame buffer info structure - * @mode_option: string video mode to find - * @db: video mode database - * @dbsize: size of @db - * @default_mode: default video mode to fall back to - * @default_bpp: default color depth in bits per pixel + * fb_find_mode - finds a valid video mode + * @var: frame buffer user defined part of display + * @info: frame buffer info structure + * @mode_option: string video mode to find + * @db: video mode database + * @dbsize: size of @db + * @default_mode: default video mode to fall back to + * @default_bpp: default color depth in bits per pixel * - * Finds a suitable video mode, starting with the specified mode - * in @mode_option with fallback to @default_mode. If - * @default_mode fails, all modes in the video mode database will - * be tried. + * Finds a suitable video mode, starting with the specified mode + * in @mode_option with fallback to @default_mode. If + * @default_mode fails, all modes in the video mode database will + * be tried. * - * Valid mode specifiers for @mode_option: + * Valid mode specifiers for @mode_option: * - * <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m] or - * <name>[-<bpp>][@<refresh>] + * <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m] or + * <name>[-<bpp>][@<refresh>] * - * with <xres>, <yres>, <bpp> and <refresh> decimal numbers and - * <name> a string. + * with <xres>, <yres>, <bpp> and <refresh> decimal numbers and + * <name> a string. * * If 'M' is present after yres (and before refresh/bpp if present), * the function will compute the timings using VESA(tm) Coordinated @@ -551,12 +552,12 @@ static int fb_try_mode(struct fb_var_screeninfo *var, struct fb_info *info, * * 1024x768MR-8@60m - Reduced blank with margins at 60Hz. * - * NOTE: The passed struct @var is _not_ cleared! This allows you - * to supply values for e.g. the grayscale and accel_flags fields. + * NOTE: The passed struct @var is _not_ cleared! This allows you + * to supply values for e.g. the grayscale and accel_flags fields. * - * Returns zero for failure, 1 if using specified @mode_option, - * 2 if using specified @mode_option with an ignored refresh rate, - * 3 if default mode is used, 4 if fall back to any valid mode. + * Returns zero for failure, 1 if using specified @mode_option, + * 2 if using specified @mode_option with an ignored refresh rate, + * 3 if default mode is used, 4 if fall back to any valid mode. * */ @@ -566,198 +567,203 @@ int fb_find_mode(struct fb_var_screeninfo *var, const struct fb_videomode *default_mode, unsigned int default_bpp) { - int i; - - /* Set up defaults */ - if (!db) { - db = modedb; - dbsize = ARRAY_SIZE(modedb); - } - - if (!default_mode) - default_mode = &db[0]; - - if (!default_bpp) - default_bpp = 8; - - /* Did the user specify a video mode? */ - if (!mode_option) - mode_option = fb_mode_option; - if (mode_option) { - const char *name = mode_option; - unsigned int namelen = strlen(name); - int res_specified = 0, bpp_specified = 0, refresh_specified = 0; - unsigned int xres = 0, yres = 0, bpp = default_bpp, refresh = 0; - int yres_specified = 0, cvt = 0, rb = 0, interlace = 0, margins = 0; - u32 best, diff, tdiff; - - for (i = namelen-1; i >= 0; i--) { - switch (name[i]) { - case '@': - namelen = i; - if (!refresh_specified && !bpp_specified && - !yres_specified) { - refresh = simple_strtol(&name[i+1], NULL, 10); - refresh_specified = 1; - if (cvt || rb) - cvt = 0; - } else - goto done; - break; - case '-': - namelen = i; - if (!bpp_specified && !yres_specified) { - bpp = simple_strtol(&name[i+1], NULL, 10); - bpp_specified = 1; - if (cvt || rb) - cvt = 0; - } else - goto done; - break; - case 'x': - if (!yres_specified) { - yres = simple_strtol(&name[i+1], NULL, 10); - yres_specified = 1; - } else - goto done; - break; - case '0' ... '9': - break; - case 'M': - if (!yres_specified) - cvt = 1; - break; - case 'R': - if (!cvt) - rb = 1; - break; - case 'm': - if (!cvt) - margins = 1; - break; - case 'i': - if (!cvt) - interlace = 1; - break; - default: - goto done; - } - } - if (i < 0 && yres_specified) { - xres = simple_strtol(name, NULL, 10); - res_specified = 1; - } -done: - if (cvt) { - struct fb_videomode cvt_mode; - int ret; - - DPRINTK("CVT mode %dx%d@%dHz%s%s%s\n", xres, yres, - (refresh) ? refresh : 60, (rb) ? " reduced blanking" : - "", (margins) ? " with margins" : "", (interlace) ? - " interlaced" : ""); - - memset(&cvt_mode, 0, sizeof(cvt_mode)); - cvt_mode.xres = xres; - cvt_mode.yres = yres; - cvt_mode.refresh = (refresh) ? refresh : 60; + int i; - if (interlace) - cvt_mode.vmode |= FB_VMODE_INTERLACED; - else - cvt_mode.vmode &= ~FB_VMODE_INTERLACED; + /* Set up defaults */ + if (!db) { + db = modedb; + dbsize = ARRAY_SIZE(modedb); + } - ret = fb_find_mode_cvt(&cvt_mode, margins, rb); + if (!default_mode) + default_mode = &db[0]; + + if (!default_bpp) + default_bpp = 8; + + /* Did the user specify a video mode? */ + if (!mode_option) + mode_option = fb_mode_option; + if (mode_option) { + const char *name = mode_option; + unsigned int namelen = strlen(name); + int res_specified = 0, bpp_specified = 0, refresh_specified = 0; + unsigned int xres = 0, yres = 0, bpp = default_bpp, refresh = 0; + int yres_specified = 0, cvt = 0, rb = 0, interlace = 0; + int margins = 0; + u32 best, diff, tdiff; + + for (i = namelen-1; i >= 0; i--) { + switch (name[i]) { + case '@': + namelen = i; + if (!refresh_specified && !bpp_specified && + !yres_specified) { + refresh = simple_strtol(&name[i+1], NULL, + 10); + refresh_specified = 1; + if (cvt || rb) + cvt = 0; + } else + goto done; + break; + case '-': + namelen = i; + if (!bpp_specified && !yres_specified) { + bpp = simple_strtol(&name[i+1], NULL, + 10); + bpp_specified = 1; + if (cvt || rb) + cvt = 0; + } else + goto done; + break; + case 'x': + if (!yres_specified) { + yres = simple_strtol(&name[i+1], NULL, + 10); + yres_specified = 1; + } else + goto done; + break; + case '0' ... '9': + break; + case 'M': + if (!yres_specified) + cvt = 1; + break; + case 'R': + if (!cvt) + rb = 1; + break; + case 'm': + if (!cvt) + margins = 1; + break; + case 'i': + if (!cvt) + interlace = 1; + break; + default: + goto done; + } + } + if (i < 0 && yres_specified) { + xres = simple_strtol(name, NULL, 10); + res_specified = 1; + } +done: + if (cvt) { + struct fb_videomode cvt_mode; + int ret; + + DPRINTK("CVT mode %dx%d@%dHz%s%s%s\n", xres, yres, + (refresh) ? refresh : 60, + (rb) ? " reduced blanking" : "", + (margins) ? " with margins" : "", + (interlace) ? " interlaced" : ""); + + memset(&cvt_mode, 0, sizeof(cvt_mode)); + cvt_mode.xres = xres; + cvt_mode.yres = yres; + cvt_mode.refresh = (refresh) ? refresh : 60; + + if (interlace) + cvt_mode.vmode |= FB_VMODE_INTERLACED; + else + cvt_mode.vmode &= ~FB_VMODE_INTERLACED; + + ret = fb_find_mode_cvt(&cvt_mode, margins, rb); + + if (!ret && !fb_try_mode(var, info, &cvt_mode, bpp)) { + DPRINTK("modedb CVT: CVT mode ok\n"); + return 1; + } - if (!ret && !fb_try_mode(var, info, &cvt_mode, bpp)) { - DPRINTK("modedb CVT: CVT mode ok\n"); - return 1; - } + DPRINTK("CVT mode invalid, getting mode from database\n"); + } - DPRINTK("CVT mode invalid, getting mode from database\n"); - } + DPRINTK("Trying specified video mode%s %ix%i\n", + refresh_specified ? "" : " (ignoring refresh rate)", + xres, yres); - DPRINTK("Trying specified video mode%s %ix%i\n", - refresh_specified ? "" : " (ignoring refresh rate)", xres, yres); - - if (!refresh_specified) { - /* - * If the caller has provided a custom mode database and a - * valid monspecs structure, we look for the mode with the - * highest refresh rate. Otherwise we play it safe it and - * try to find a mode with a refresh rate closest to the - * standard 60 Hz. - */ - if (db != modedb && - info->monspecs.vfmin && info->monspecs.vfmax && - info->monspecs.hfmin && info->monspecs.hfmax && - info->monspecs.dclkmax) { - refresh = 1000; - } else { - refresh = 60; + if (!refresh_specified) { + /* + * If the caller has provided a custom mode database and + * a valid monspecs structure, we look for the mode with + * the highest refresh rate. Otherwise we play it safe + * it and try to find a mode with a refresh rate closest + * to the standard 60 Hz. + */ + if (db != modedb && + info->monspecs.vfmin && info->monspecs.vfmax && + info->monspecs.hfmin && info->monspecs.hfmax && + info->monspecs.dclkmax) { + refresh = 1000; + } else { + refresh = 60; + } } - } - diff = -1; - best = -1; - for (i = 0; i < dbsize; i++) { - if ((name_matches(db[i], name, namelen) || - (res_specified && res_matches(db[i], xres, yres))) && - !fb_try_mode(var, info, &db[i], bpp)) { - if (refresh_specified && db[i].refresh == refresh) { - return 1; - } else { + diff = -1; + best = -1; + for (i = 0; i < dbsize; i++) { + if ((name_matches(db[i], name, namelen) || + (res_specified && res_matches(db[i], xres, yres))) && + !fb_try_mode(var, info, &db[i], bpp)) { + if (refresh_specified && db[i].refresh == refresh) + return 1; + if (abs(db[i].refresh - refresh) < diff) { diff = abs(db[i].refresh - refresh); best = i; } } } - } - if (best != -1) { - fb_try_mode(var, info, &db[best], bpp); - return (refresh_specified) ? 2 : 1; - } - - diff = 2 * (xres + yres); - best = -1; - DPRINTK("Trying best-fit modes\n"); - for (i = 0; i < dbsize; i++) { - DPRINTK("Trying %ix%i\n", db[i].xres, db[i].yres); - if (!fb_try_mode(var, info, &db[i], bpp)) { - tdiff = abs(db[i].xres - xres) + - abs(db[i].yres - yres); - - /* - * Penalize modes with resolutions smaller - * than requested. - */ - if (xres > db[i].xres || yres > db[i].yres) - tdiff += xres + yres; + if (best != -1) { + fb_try_mode(var, info, &db[best], bpp); + return (refresh_specified) ? 2 : 1; + } - if (diff > tdiff) { - diff = tdiff; - best = i; + diff = 2 * (xres + yres); + best = -1; + DPRINTK("Trying best-fit modes\n"); + for (i = 0; i < dbsize; i++) { + DPRINTK("Trying %ix%i\n", db[i].xres, db[i].yres); + if (!fb_try_mode(var, info, &db[i], bpp)) { + tdiff = abs(db[i].xres - xres) + + abs(db[i].yres - yres); + + /* + * Penalize modes with resolutions smaller + * than requested. + */ + if (xres > db[i].xres || yres > db[i].yres) + tdiff += xres + yres; + + if (diff > tdiff) { + diff = tdiff; + best = i; + } } } + if (best != -1) { + fb_try_mode(var, info, &db[best], bpp); + return 5; + } } - if (best != -1) { - fb_try_mode(var, info, &db[best], bpp); - return 5; - } - } - DPRINTK("Trying default video mode\n"); - if (!fb_try_mode(var, info, default_mode, default_bpp)) - return 3; + DPRINTK("Trying default video mode\n"); + if (!fb_try_mode(var, info, default_mode, default_bpp)) + return 3; - DPRINTK("Trying all modes\n"); - for (i = 0; i < dbsize; i++) - if (!fb_try_mode(var, info, &db[i], default_bpp)) - return 4; + DPRINTK("Trying all modes\n"); + for (i = 0; i < dbsize; i++) + if (!fb_try_mode(var, info, &db[i], default_bpp)) + return 4; - DPRINTK("No valid mode found\n"); - return 0; + DPRINTK("No valid mode found\n"); + return 0; } /** diff --git a/drivers/video/msm/mddi.c b/drivers/video/msm/mddi.c index 178b0720bd79..4527cbf0a4ec 100644 --- a/drivers/video/msm/mddi.c +++ b/drivers/video/msm/mddi.c @@ -715,7 +715,7 @@ static int __devinit mddi_probe(struct platform_device *pdev) mddi->int_enable = 0; mddi_writel(mddi->int_enable, INTEN); - ret = request_irq(mddi->irq, mddi_isr, IRQF_DISABLED, "mddi", + ret = request_irq(mddi->irq, mddi_isr, 0, "mddi", &mddi->client_data); if (ret) { printk(KERN_ERR "mddi: failed to request enable irq!\n"); diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c index 243d16f09b8a..b9344772bac9 100644 --- a/drivers/video/msm/mdp.c +++ b/drivers/video/msm/mdp.c @@ -421,10 +421,11 @@ int mdp_probe(struct platform_device *pdev) clk = clk_get(&pdev->dev, "mdp_clk"); if (IS_ERR(clk)) { printk(KERN_INFO "mdp: failed to get mdp clk"); - return PTR_ERR(clk); + ret = PTR_ERR(clk); + goto error_get_clk; } - ret = request_irq(mdp->irq, mdp_isr, IRQF_DISABLED, "msm_mdp", mdp); + ret = request_irq(mdp->irq, mdp_isr, 0, "msm_mdp", mdp); if (ret) goto error_request_irq; disable_irq(mdp->irq); @@ -495,6 +496,7 @@ int mdp_probe(struct platform_device *pdev) error_device_register: free_irq(mdp->irq, mdp); error_request_irq: +error_get_clk: iounmap(mdp->base); error_get_irq: error_ioremap: diff --git a/drivers/video/mx3fb.c b/drivers/video/mx3fb.c index 7e3a490e8d76..e3406ab31305 100644 --- a/drivers/video/mx3fb.c +++ b/drivers/video/mx3fb.c @@ -382,6 +382,9 @@ static void sdc_disable_channel(struct mx3fb_info *mx3_fbi) uint32_t enabled; unsigned long flags; + if (mx3_fbi->txd == NULL) + return; + spin_lock_irqsave(&mx3fb->lock, flags); enabled = sdc_fb_uninit(mx3_fbi); @@ -986,9 +989,19 @@ static void __blank(int blank, struct fb_info *fbi) { struct mx3fb_info *mx3_fbi = fbi->par; struct mx3fb_data *mx3fb = mx3_fbi->mx3fb; + int was_blank = mx3_fbi->blank; mx3_fbi->blank = blank; + /* Attention! + * Do not call sdc_disable_channel() for a channel that is disabled + * already! This will result in a kernel NULL pointer dereference + * (mx3_fbi->txd is NULL). Hide the fact, that all blank modes are + * handled equally by this driver. + */ + if (blank > FB_BLANK_UNBLANK && was_blank > FB_BLANK_UNBLANK) + return; + switch (blank) { case FB_BLANK_POWERDOWN: case FB_BLANK_VSYNC_SUSPEND: @@ -1062,15 +1075,15 @@ static int mx3fb_pan_display(struct fb_var_screeninfo *var, y_bottom = var->yoffset; if (!(var->vmode & FB_VMODE_YWRAP)) - y_bottom += var->yres; + y_bottom += fbi->var.yres; if (y_bottom > fbi->var.yres_virtual) return -EINVAL; mutex_lock(&mx3_fbi->mutex); - offset = (var->yoffset * var->xres_virtual + var->xoffset) * - (var->bits_per_pixel / 8); + offset = var->yoffset * fbi->fix.line_length + + var->xoffset * (fbi->var.bits_per_pixel / 8); base = fbi->fix.smem_start + offset; dev_dbg(fbi->device, "Updating SDC BG buf %d address=0x%08lX\n", diff --git a/drivers/video/mxsfb.c b/drivers/video/mxsfb.c index 0b2f2dd41416..d837d63c456f 100644 --- a/drivers/video/mxsfb.c +++ b/drivers/video/mxsfb.c @@ -39,6 +39,7 @@ * the required value in the imx_fb_videomode structure. */ +#include <linux/module.h> #include <linux/kernel.h> #include <linux/platform_device.h> #include <linux/clk.h> diff --git a/drivers/video/neofb.c b/drivers/video/neofb.c index 588527a254c2..feea7b1dc386 100644 --- a/drivers/video/neofb.c +++ b/drivers/video/neofb.c @@ -1185,8 +1185,8 @@ static int neofb_pan_display(struct fb_var_screeninfo *var, DBG("neofb_update_start"); - Base = (var->yoffset * var->xres_virtual + var->xoffset) >> 2; - Base *= (var->bits_per_pixel + 7) / 8; + Base = (var->yoffset * info->var.xres_virtual + var->xoffset) >> 2; + Base *= (info->var.bits_per_pixel + 7) / 8; neoUnlock(); diff --git a/drivers/video/nuc900fb.c b/drivers/video/nuc900fb.c index 0fff59782e45..d1fbbd888cf4 100644 --- a/drivers/video/nuc900fb.c +++ b/drivers/video/nuc900fb.c @@ -39,7 +39,6 @@ #include <mach/regs-clock.h> #include <mach/regs-ldm.h> #include <mach/fb.h> -#include <mach/clkdev.h> #include "nuc900fb.h" @@ -588,7 +587,7 @@ static int __devinit nuc900fb_probe(struct platform_device *pdev) fbinfo->flags = FBINFO_FLAG_DEFAULT; fbinfo->pseudo_palette = &fbi->pseudo_pal; - ret = request_irq(irq, nuc900fb_irqhandler, IRQF_DISABLED, + ret = request_irq(irq, nuc900fb_irqhandler, 0, pdev->name, fbinfo); if (ret) { dev_err(&pdev->dev, "cannot register irq handler %d -err %d\n", diff --git a/drivers/video/omap/Kconfig b/drivers/video/omap/Kconfig index 196fa2e7f438..84ff23208c25 100644 --- a/drivers/video/omap/Kconfig +++ b/drivers/video/omap/Kconfig @@ -9,35 +9,6 @@ config FB_OMAP help Frame buffer driver for OMAP based boards. -config FB_OMAP_LCD_VGA - bool "Use LCD in VGA mode" - depends on MACH_OMAP_3430SDP || MACH_OMAP_LDP - help - Set LCD resolution as VGA (640 X 480). - Default resolution without this option is QVGA(320 X 240). - Please take a look at drivers/video/omap/lcd_ldp.c file - for lcd driver code. -choice - depends on FB_OMAP && MACH_OVERO - prompt "Screen resolution" - default FB_OMAP_079M3R - help - Selected desired screen resolution - -config FB_OMAP_031M3R - boolean "640 x 480 @ 60 Hz Reduced blanking" - -config FB_OMAP_048M3R - boolean "800 x 600 @ 60 Hz Reduced blanking" - -config FB_OMAP_079M3R - boolean "1024 x 768 @ 60 Hz Reduced blanking" - -config FB_OMAP_092M9R - boolean "1280 x 720 @ 60 Hz Reduced blanking" - -endchoice - config FB_OMAP_LCDC_EXTERNAL bool "External LCD controller support" depends on FB_OMAP diff --git a/drivers/video/omap/Makefile b/drivers/video/omap/Makefile index 25db55696e14..ef78550917ff 100644 --- a/drivers/video/omap/Makefile +++ b/drivers/video/omap/Makefile @@ -17,7 +17,6 @@ objs-y$(CONFIG_FB_OMAP_LCDC_HWA742) += hwa742.o objs-y$(CONFIG_FB_OMAP_LCDC_BLIZZARD) += blizzard.o objs-y$(CONFIG_MACH_AMS_DELTA) += lcd_ams_delta.o -objs-y$(CONFIG_MACH_OMAP_H4) += lcd_h4.o objs-y$(CONFIG_MACH_OMAP_H3) += lcd_h3.o objs-y$(CONFIG_MACH_OMAP_PALMTE) += lcd_palmte.o objs-y$(CONFIG_MACH_OMAP_PALMTT) += lcd_palmtt.o @@ -26,14 +25,7 @@ objs-$(CONFIG_ARCH_OMAP16XX)$(CONFIG_MACH_OMAP_INNOVATOR) += lcd_inn1610.o objs-$(CONFIG_ARCH_OMAP15XX)$(CONFIG_MACH_OMAP_INNOVATOR) += lcd_inn1510.o objs-y$(CONFIG_MACH_OMAP_OSK) += lcd_osk.o -objs-y$(CONFIG_MACH_OMAP_APOLLON) += lcd_apollon.o -objs-y$(CONFIG_MACH_OMAP_2430SDP) += lcd_2430sdp.o -objs-y$(CONFIG_MACH_OMAP_3430SDP) += lcd_2430sdp.o -objs-y$(CONFIG_MACH_OMAP_LDP) += lcd_ldp.o -objs-y$(CONFIG_MACH_OMAP3EVM) += lcd_omap3evm.o -objs-y$(CONFIG_MACH_OMAP3_BEAGLE) += lcd_omap3beagle.o objs-y$(CONFIG_FB_OMAP_LCD_MIPID) += lcd_mipid.o -objs-y$(CONFIG_MACH_OVERO) += lcd_overo.o objs-y$(CONFIG_MACH_HERALD) += lcd_htcherald.o omapfb-objs := $(objs-yy) diff --git a/drivers/video/omap/lcd_2430sdp.c b/drivers/video/omap/lcd_2430sdp.c deleted file mode 100644 index e3eccc9af78e..000000000000 --- a/drivers/video/omap/lcd_2430sdp.c +++ /dev/null @@ -1,203 +0,0 @@ -/* - * LCD panel support for the TI 2430SDP board - * - * Copyright (C) 2007 MontaVista - * Author: Hunyue Yau <hyau@mvista.com> - * - * Derived from drivers/video/omap/lcd-apollon.c - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * 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, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/delay.h> -#include <linux/gpio.h> -#include <linux/i2c/twl.h> - -#include <plat/mux.h> -#include <asm/mach-types.h> - -#include "omapfb.h" - -#define SDP2430_LCD_PANEL_BACKLIGHT_GPIO 91 -#define SDP2430_LCD_PANEL_ENABLE_GPIO 154 -#define SDP3430_LCD_PANEL_BACKLIGHT_GPIO 24 -#define SDP3430_LCD_PANEL_ENABLE_GPIO 28 - -static unsigned backlight_gpio; -static unsigned enable_gpio; - -#define LCD_PIXCLOCK_MAX 5400 /* freq 5.4 MHz */ -#define PM_RECEIVER TWL4030_MODULE_PM_RECEIVER -#define ENABLE_VAUX2_DEDICATED 0x09 -#define ENABLE_VAUX2_DEV_GRP 0x20 -#define ENABLE_VAUX3_DEDICATED 0x03 -#define ENABLE_VAUX3_DEV_GRP 0x20 - -#define ENABLE_VPLL2_DEDICATED 0x05 -#define ENABLE_VPLL2_DEV_GRP 0xE0 -#define TWL4030_VPLL2_DEV_GRP 0x33 -#define TWL4030_VPLL2_DEDICATED 0x36 - -#define t2_out(c, r, v) twl_i2c_write_u8(c, r, v) - - -static int sdp2430_panel_init(struct lcd_panel *panel, - struct omapfb_device *fbdev) -{ - if (machine_is_omap_3430sdp()) { - enable_gpio = SDP3430_LCD_PANEL_ENABLE_GPIO; - backlight_gpio = SDP3430_LCD_PANEL_BACKLIGHT_GPIO; - } else { - enable_gpio = SDP2430_LCD_PANEL_ENABLE_GPIO; - backlight_gpio = SDP2430_LCD_PANEL_BACKLIGHT_GPIO; - } - - gpio_request(enable_gpio, "LCD enable"); /* LCD panel */ - gpio_request(backlight_gpio, "LCD bl"); /* LCD backlight */ - gpio_direction_output(enable_gpio, 0); - gpio_direction_output(backlight_gpio, 0); - - return 0; -} - -static void sdp2430_panel_cleanup(struct lcd_panel *panel) -{ - gpio_free(backlight_gpio); - gpio_free(enable_gpio); -} - -static int sdp2430_panel_enable(struct lcd_panel *panel) -{ - u8 ded_val, ded_reg; - u8 grp_val, grp_reg; - - if (machine_is_omap_3430sdp()) { - ded_reg = TWL4030_VAUX3_DEDICATED; - ded_val = ENABLE_VAUX3_DEDICATED; - grp_reg = TWL4030_VAUX3_DEV_GRP; - grp_val = ENABLE_VAUX3_DEV_GRP; - - if (omap_rev() > OMAP3430_REV_ES1_0) { - t2_out(PM_RECEIVER, ENABLE_VPLL2_DEDICATED, - TWL4030_VPLL2_DEDICATED); - t2_out(PM_RECEIVER, ENABLE_VPLL2_DEV_GRP, - TWL4030_VPLL2_DEV_GRP); - } - } else { - ded_reg = TWL4030_VAUX2_DEDICATED; - ded_val = ENABLE_VAUX2_DEDICATED; - grp_reg = TWL4030_VAUX2_DEV_GRP; - grp_val = ENABLE_VAUX2_DEV_GRP; - } - - gpio_set_value(enable_gpio, 1); - gpio_set_value(backlight_gpio, 1); - - if (0 != t2_out(PM_RECEIVER, ded_val, ded_reg)) - return -EIO; - if (0 != t2_out(PM_RECEIVER, grp_val, grp_reg)) - return -EIO; - - return 0; -} - -static void sdp2430_panel_disable(struct lcd_panel *panel) -{ - gpio_set_value(enable_gpio, 0); - gpio_set_value(backlight_gpio, 0); - if (omap_rev() > OMAP3430_REV_ES1_0) { - t2_out(PM_RECEIVER, 0x0, TWL4030_VPLL2_DEDICATED); - t2_out(PM_RECEIVER, 0x0, TWL4030_VPLL2_DEV_GRP); - msleep(4); - } -} - -static unsigned long sdp2430_panel_get_caps(struct lcd_panel *panel) -{ - return 0; -} - -struct lcd_panel sdp2430_panel = { - .name = "sdp2430", - .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC | - OMAP_LCDC_INV_HSYNC, - - .bpp = 16, - .data_lines = 16, - .x_res = 240, - .y_res = 320, - .hsw = 3, /* hsync_len (4) - 1 */ - .hfp = 3, /* right_margin (4) - 1 */ - .hbp = 39, /* left_margin (40) - 1 */ - .vsw = 1, /* vsync_len (2) - 1 */ - .vfp = 2, /* lower_margin */ - .vbp = 7, /* upper_margin (8) - 1 */ - - .pixel_clock = LCD_PIXCLOCK_MAX, - - .init = sdp2430_panel_init, - .cleanup = sdp2430_panel_cleanup, - .enable = sdp2430_panel_enable, - .disable = sdp2430_panel_disable, - .get_caps = sdp2430_panel_get_caps, -}; - -static int sdp2430_panel_probe(struct platform_device *pdev) -{ - omapfb_register_panel(&sdp2430_panel); - return 0; -} - -static int sdp2430_panel_remove(struct platform_device *pdev) -{ - return 0; -} - -static int sdp2430_panel_suspend(struct platform_device *pdev, - pm_message_t mesg) -{ - return 0; -} - -static int sdp2430_panel_resume(struct platform_device *pdev) -{ - return 0; -} - -struct platform_driver sdp2430_panel_driver = { - .probe = sdp2430_panel_probe, - .remove = sdp2430_panel_remove, - .suspend = sdp2430_panel_suspend, - .resume = sdp2430_panel_resume, - .driver = { - .name = "sdp2430_lcd", - .owner = THIS_MODULE, - }, -}; - -static int __init sdp2430_panel_drv_init(void) -{ - return platform_driver_register(&sdp2430_panel_driver); -} - -static void __exit sdp2430_panel_drv_exit(void) -{ - platform_driver_unregister(&sdp2430_panel_driver); -} - -module_init(sdp2430_panel_drv_init); -module_exit(sdp2430_panel_drv_exit); diff --git a/drivers/video/omap/lcd_apollon.c b/drivers/video/omap/lcd_apollon.c deleted file mode 100644 index 4b24f549f9b9..000000000000 --- a/drivers/video/omap/lcd_apollon.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * LCD panel support for the Samsung OMAP2 Apollon board - * - * Copyright (C) 2005,2006 Samsung Electronics - * Author: Kyungmin Park <kyungmin.park@samsung.com> - * - * Derived from drivers/video/omap/lcd-h4.c - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * 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, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include <linux/module.h> -#include <linux/platform_device.h> - -#include <asm/gpio.h> - -#include "omapfb.h" - -/* #define USE_35INCH_LCD 1 */ - -static int apollon_panel_init(struct lcd_panel *panel, - struct omapfb_device *fbdev) -{ - return 0; -} - -static void apollon_panel_cleanup(struct lcd_panel *panel) -{ -} - -static int apollon_panel_enable(struct lcd_panel *panel) -{ - return 0; -} - -static void apollon_panel_disable(struct lcd_panel *panel) -{ -} - -static unsigned long apollon_panel_get_caps(struct lcd_panel *panel) -{ - return 0; -} - -struct lcd_panel apollon_panel = { - .name = "apollon", - .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC | - OMAP_LCDC_INV_HSYNC, - - .bpp = 16, - .data_lines = 18, -#ifdef USE_35INCH_LCD - .x_res = 240, - .y_res = 320, - .hsw = 2, - .hfp = 3, - .hbp = 9, - .vsw = 4, - .vfp = 3, - .vbp = 5, -#else - .x_res = 480, - .y_res = 272, - .hsw = 41, - .hfp = 2, - .hbp = 2, - .vsw = 10, - .vfp = 2, - .vbp = 2, -#endif - .pixel_clock = 6250, - - .init = apollon_panel_init, - .cleanup = apollon_panel_cleanup, - .enable = apollon_panel_enable, - .disable = apollon_panel_disable, - .get_caps = apollon_panel_get_caps, -}; - -static int apollon_panel_probe(struct platform_device *pdev) -{ - omapfb_register_panel(&apollon_panel); - return 0; -} - -static int apollon_panel_remove(struct platform_device *pdev) -{ - return 0; -} - -static int apollon_panel_suspend(struct platform_device *pdev, - pm_message_t mesg) -{ - return 0; -} - -static int apollon_panel_resume(struct platform_device *pdev) -{ - return 0; -} - -struct platform_driver apollon_panel_driver = { - .probe = apollon_panel_probe, - .remove = apollon_panel_remove, - .suspend = apollon_panel_suspend, - .resume = apollon_panel_resume, - .driver = { - .name = "apollon_lcd", - .owner = THIS_MODULE, - }, -}; - -static int __init apollon_panel_drv_init(void) -{ - return platform_driver_register(&apollon_panel_driver); -} - -static void __exit apollon_panel_drv_exit(void) -{ - platform_driver_unregister(&apollon_panel_driver); -} - -module_init(apollon_panel_drv_init); -module_exit(apollon_panel_drv_exit); diff --git a/drivers/video/omap/lcd_h4.c b/drivers/video/omap/lcd_h4.c deleted file mode 100644 index 03a06a982750..000000000000 --- a/drivers/video/omap/lcd_h4.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - * LCD panel support for the TI OMAP H4 board - * - * Copyright (C) 2004 Nokia Corporation - * Author: Imre Deak <imre.deak@nokia.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * 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, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include <linux/module.h> -#include <linux/platform_device.h> - -#include "omapfb.h" - -static int h4_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev) -{ - return 0; -} - -static void h4_panel_cleanup(struct lcd_panel *panel) -{ -} - -static int h4_panel_enable(struct lcd_panel *panel) -{ - return 0; -} - -static void h4_panel_disable(struct lcd_panel *panel) -{ -} - -static unsigned long h4_panel_get_caps(struct lcd_panel *panel) -{ - return 0; -} - -static struct lcd_panel h4_panel = { - .name = "h4", - .config = OMAP_LCDC_PANEL_TFT, - - .bpp = 16, - .data_lines = 16, - .x_res = 240, - .y_res = 320, - .pixel_clock = 6250, - .hsw = 15, - .hfp = 15, - .hbp = 60, - .vsw = 1, - .vfp = 1, - .vbp = 1, - - .init = h4_panel_init, - .cleanup = h4_panel_cleanup, - .enable = h4_panel_enable, - .disable = h4_panel_disable, - .get_caps = h4_panel_get_caps, -}; - -static int h4_panel_probe(struct platform_device *pdev) -{ - omapfb_register_panel(&h4_panel); - return 0; -} - -static int h4_panel_remove(struct platform_device *pdev) -{ - return 0; -} - -static int h4_panel_suspend(struct platform_device *pdev, pm_message_t mesg) -{ - return 0; -} - -static int h4_panel_resume(struct platform_device *pdev) -{ - return 0; -} - -static struct platform_driver h4_panel_driver = { - .probe = h4_panel_probe, - .remove = h4_panel_remove, - .suspend = h4_panel_suspend, - .resume = h4_panel_resume, - .driver = { - .name = "lcd_h4", - .owner = THIS_MODULE, - }, -}; - -static int __init h4_panel_drv_init(void) -{ - return platform_driver_register(&h4_panel_driver); -} - -static void __exit h4_panel_drv_cleanup(void) -{ - platform_driver_unregister(&h4_panel_driver); -} - -module_init(h4_panel_drv_init); -module_exit(h4_panel_drv_cleanup); - diff --git a/drivers/video/omap/lcd_ldp.c b/drivers/video/omap/lcd_ldp.c deleted file mode 100644 index 062466402c0e..000000000000 --- a/drivers/video/omap/lcd_ldp.c +++ /dev/null @@ -1,201 +0,0 @@ -/* - * LCD panel support for the TI LDP board - * - * Copyright (C) 2007 WindRiver - * Author: Stanley Miao <stanley.miao@windriver.com> - * - * Derived from drivers/video/omap/lcd-2430sdp.c - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * 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, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/delay.h> -#include <linux/i2c/twl.h> - -#include <asm/gpio.h> -#include <plat/mux.h> -#include <asm/mach-types.h> - -#include "omapfb.h" - -#define LCD_PANEL_BACKLIGHT_GPIO (15 + OMAP_MAX_GPIO_LINES) -#define LCD_PANEL_ENABLE_GPIO (7 + OMAP_MAX_GPIO_LINES) - -#define LCD_PANEL_RESET_GPIO 55 -#define LCD_PANEL_QVGA_GPIO 56 - -#ifdef CONFIG_FB_OMAP_LCD_VGA -#define LCD_XRES 480 -#define LCD_YRES 640 -#define LCD_PIXCLOCK_MAX 41700 -#else -#define LCD_XRES 240 -#define LCD_YRES 320 -#define LCD_PIXCLOCK_MAX 185186 -#endif - -#define PM_RECEIVER TWL4030_MODULE_PM_RECEIVER -#define ENABLE_VAUX2_DEDICATED 0x09 -#define ENABLE_VAUX2_DEV_GRP 0x20 -#define ENABLE_VAUX3_DEDICATED 0x03 -#define ENABLE_VAUX3_DEV_GRP 0x20 - -#define ENABLE_VPLL2_DEDICATED 0x05 -#define ENABLE_VPLL2_DEV_GRP 0xE0 -#define TWL4030_VPLL2_DEV_GRP 0x33 -#define TWL4030_VPLL2_DEDICATED 0x36 - -#define t2_out(c, r, v) twl_i2c_write_u8(c, r, v) - - -static int ldp_panel_init(struct lcd_panel *panel, - struct omapfb_device *fbdev) -{ - gpio_request(LCD_PANEL_RESET_GPIO, "lcd reset"); - gpio_request(LCD_PANEL_QVGA_GPIO, "lcd qvga"); - gpio_request(LCD_PANEL_ENABLE_GPIO, "lcd panel"); - gpio_request(LCD_PANEL_BACKLIGHT_GPIO, "lcd backlight"); - - gpio_direction_output(LCD_PANEL_QVGA_GPIO, 0); - gpio_direction_output(LCD_PANEL_RESET_GPIO, 0); - gpio_direction_output(LCD_PANEL_ENABLE_GPIO, 0); - gpio_direction_output(LCD_PANEL_BACKLIGHT_GPIO, 0); - -#ifdef CONFIG_FB_OMAP_LCD_VGA - gpio_set_value(LCD_PANEL_QVGA_GPIO, 0); -#else - gpio_set_value(LCD_PANEL_QVGA_GPIO, 1); -#endif - gpio_set_value(LCD_PANEL_RESET_GPIO, 1); - - return 0; -} - -static void ldp_panel_cleanup(struct lcd_panel *panel) -{ - gpio_free(LCD_PANEL_BACKLIGHT_GPIO); - gpio_free(LCD_PANEL_ENABLE_GPIO); - gpio_free(LCD_PANEL_QVGA_GPIO); - gpio_free(LCD_PANEL_RESET_GPIO); -} - -static int ldp_panel_enable(struct lcd_panel *panel) -{ - if (0 != t2_out(PM_RECEIVER, ENABLE_VPLL2_DEDICATED, - TWL4030_VPLL2_DEDICATED)) - return -EIO; - if (0 != t2_out(PM_RECEIVER, ENABLE_VPLL2_DEV_GRP, - TWL4030_VPLL2_DEV_GRP)) - return -EIO; - - gpio_direction_output(LCD_PANEL_ENABLE_GPIO, 1); - gpio_direction_output(LCD_PANEL_BACKLIGHT_GPIO, 1); - - if (0 != t2_out(PM_RECEIVER, ENABLE_VAUX3_DEDICATED, - TWL4030_VAUX3_DEDICATED)) - return -EIO; - if (0 != t2_out(PM_RECEIVER, ENABLE_VAUX3_DEV_GRP, - TWL4030_VAUX3_DEV_GRP)) - return -EIO; - - return 0; -} - -static void ldp_panel_disable(struct lcd_panel *panel) -{ - gpio_direction_output(LCD_PANEL_ENABLE_GPIO, 0); - gpio_direction_output(LCD_PANEL_BACKLIGHT_GPIO, 0); - - t2_out(PM_RECEIVER, 0x0, TWL4030_VPLL2_DEDICATED); - t2_out(PM_RECEIVER, 0x0, TWL4030_VPLL2_DEV_GRP); - msleep(4); -} - -static unsigned long ldp_panel_get_caps(struct lcd_panel *panel) -{ - return 0; -} - -struct lcd_panel ldp_panel = { - .name = "ldp", - .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC | - OMAP_LCDC_INV_HSYNC, - - .bpp = 16, - .data_lines = 18, - .x_res = LCD_XRES, - .y_res = LCD_YRES, - .hsw = 3, /* hsync_len (4) - 1 */ - .hfp = 3, /* right_margin (4) - 1 */ - .hbp = 39, /* left_margin (40) - 1 */ - .vsw = 1, /* vsync_len (2) - 1 */ - .vfp = 2, /* lower_margin */ - .vbp = 7, /* upper_margin (8) - 1 */ - - .pixel_clock = LCD_PIXCLOCK_MAX, - - .init = ldp_panel_init, - .cleanup = ldp_panel_cleanup, - .enable = ldp_panel_enable, - .disable = ldp_panel_disable, - .get_caps = ldp_panel_get_caps, -}; - -static int ldp_panel_probe(struct platform_device *pdev) -{ - omapfb_register_panel(&ldp_panel); - return 0; -} - -static int ldp_panel_remove(struct platform_device *pdev) -{ - return 0; -} - -static int ldp_panel_suspend(struct platform_device *pdev, pm_message_t mesg) -{ - return 0; -} - -static int ldp_panel_resume(struct platform_device *pdev) -{ - return 0; -} - -struct platform_driver ldp_panel_driver = { - .probe = ldp_panel_probe, - .remove = ldp_panel_remove, - .suspend = ldp_panel_suspend, - .resume = ldp_panel_resume, - .driver = { - .name = "ldp_lcd", - .owner = THIS_MODULE, - }, -}; - -static int __init ldp_panel_drv_init(void) -{ - return platform_driver_register(&ldp_panel_driver); -} - -static void __exit ldp_panel_drv_exit(void) -{ - platform_driver_unregister(&ldp_panel_driver); -} - -module_init(ldp_panel_drv_init); -module_exit(ldp_panel_drv_exit); diff --git a/drivers/video/omap/lcd_omap3beagle.c b/drivers/video/omap/lcd_omap3beagle.c deleted file mode 100644 index d7c6c3e0afc6..000000000000 --- a/drivers/video/omap/lcd_omap3beagle.c +++ /dev/null @@ -1,130 +0,0 @@ -/* - * LCD panel support for the TI OMAP3 Beagle board - * - * Author: Koen Kooi <koen@openembedded.org> - * - * Derived from drivers/video/omap/lcd-omap3evm.c - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * 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, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/gpio.h> -#include <linux/i2c/twl.h> - -#include <asm/mach-types.h> - -#include "omapfb.h" - -#define LCD_PANEL_ENABLE_GPIO 170 - -static int omap3beagle_panel_init(struct lcd_panel *panel, - struct omapfb_device *fbdev) -{ - gpio_request(LCD_PANEL_ENABLE_GPIO, "LCD enable"); - return 0; -} - -static void omap3beagle_panel_cleanup(struct lcd_panel *panel) -{ - gpio_free(LCD_PANEL_ENABLE_GPIO); -} - -static int omap3beagle_panel_enable(struct lcd_panel *panel) -{ - gpio_set_value(LCD_PANEL_ENABLE_GPIO, 1); - return 0; -} - -static void omap3beagle_panel_disable(struct lcd_panel *panel) -{ - gpio_set_value(LCD_PANEL_ENABLE_GPIO, 0); -} - -static unsigned long omap3beagle_panel_get_caps(struct lcd_panel *panel) -{ - return 0; -} - -struct lcd_panel omap3beagle_panel = { - .name = "omap3beagle", - .config = OMAP_LCDC_PANEL_TFT, - - .bpp = 16, - .data_lines = 24, - .x_res = 1024, - .y_res = 768, - .hsw = 3, /* hsync_len (4) - 1 */ - .hfp = 3, /* right_margin (4) - 1 */ - .hbp = 39, /* left_margin (40) - 1 */ - .vsw = 1, /* vsync_len (2) - 1 */ - .vfp = 2, /* lower_margin */ - .vbp = 7, /* upper_margin (8) - 1 */ - - .pixel_clock = 64000, - - .init = omap3beagle_panel_init, - .cleanup = omap3beagle_panel_cleanup, - .enable = omap3beagle_panel_enable, - .disable = omap3beagle_panel_disable, - .get_caps = omap3beagle_panel_get_caps, -}; - -static int omap3beagle_panel_probe(struct platform_device *pdev) -{ - omapfb_register_panel(&omap3beagle_panel); - return 0; -} - -static int omap3beagle_panel_remove(struct platform_device *pdev) -{ - return 0; -} - -static int omap3beagle_panel_suspend(struct platform_device *pdev, - pm_message_t mesg) -{ - return 0; -} - -static int omap3beagle_panel_resume(struct platform_device *pdev) -{ - return 0; -} - -struct platform_driver omap3beagle_panel_driver = { - .probe = omap3beagle_panel_probe, - .remove = omap3beagle_panel_remove, - .suspend = omap3beagle_panel_suspend, - .resume = omap3beagle_panel_resume, - .driver = { - .name = "omap3beagle_lcd", - .owner = THIS_MODULE, - }, -}; - -static int __init omap3beagle_panel_drv_init(void) -{ - return platform_driver_register(&omap3beagle_panel_driver); -} - -static void __exit omap3beagle_panel_drv_exit(void) -{ - platform_driver_unregister(&omap3beagle_panel_driver); -} - -module_init(omap3beagle_panel_drv_init); -module_exit(omap3beagle_panel_drv_exit); diff --git a/drivers/video/omap/lcd_omap3evm.c b/drivers/video/omap/lcd_omap3evm.c deleted file mode 100644 index 06840da0b094..000000000000 --- a/drivers/video/omap/lcd_omap3evm.c +++ /dev/null @@ -1,193 +0,0 @@ -/* - * LCD panel support for the TI OMAP3 EVM board - * - * Author: Steve Sakoman <steve@sakoman.com> - * - * Derived from drivers/video/omap/lcd-apollon.c - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * 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, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/gpio.h> -#include <linux/i2c/twl.h> - -#include <plat/mux.h> -#include <asm/mach-types.h> - -#include "omapfb.h" - -#define LCD_PANEL_ENABLE_GPIO 153 -#define LCD_PANEL_LR 2 -#define LCD_PANEL_UD 3 -#define LCD_PANEL_INI 152 -#define LCD_PANEL_QVGA 154 -#define LCD_PANEL_RESB 155 - -#define ENABLE_VDAC_DEDICATED 0x03 -#define ENABLE_VDAC_DEV_GRP 0x20 -#define ENABLE_VPLL2_DEDICATED 0x05 -#define ENABLE_VPLL2_DEV_GRP 0xE0 - -#define TWL_LED_LEDEN 0x00 -#define TWL_PWMA_PWMAON 0x00 -#define TWL_PWMA_PWMAOFF 0x01 - -static unsigned int bklight_level; - -static int omap3evm_panel_init(struct lcd_panel *panel, - struct omapfb_device *fbdev) -{ - gpio_request(LCD_PANEL_LR, "LCD lr"); - gpio_request(LCD_PANEL_UD, "LCD ud"); - gpio_request(LCD_PANEL_INI, "LCD ini"); - gpio_request(LCD_PANEL_RESB, "LCD resb"); - gpio_request(LCD_PANEL_QVGA, "LCD qvga"); - - gpio_direction_output(LCD_PANEL_RESB, 1); - gpio_direction_output(LCD_PANEL_INI, 1); - gpio_direction_output(LCD_PANEL_QVGA, 0); - gpio_direction_output(LCD_PANEL_LR, 1); - gpio_direction_output(LCD_PANEL_UD, 1); - - twl_i2c_write_u8(TWL4030_MODULE_LED, 0x11, TWL_LED_LEDEN); - twl_i2c_write_u8(TWL4030_MODULE_PWMA, 0x01, TWL_PWMA_PWMAON); - twl_i2c_write_u8(TWL4030_MODULE_PWMA, 0x02, TWL_PWMA_PWMAOFF); - bklight_level = 100; - - return 0; -} - -static void omap3evm_panel_cleanup(struct lcd_panel *panel) -{ - gpio_free(LCD_PANEL_QVGA); - gpio_free(LCD_PANEL_RESB); - gpio_free(LCD_PANEL_INI); - gpio_free(LCD_PANEL_UD); - gpio_free(LCD_PANEL_LR); -} - -static int omap3evm_panel_enable(struct lcd_panel *panel) -{ - gpio_set_value(LCD_PANEL_ENABLE_GPIO, 0); - return 0; -} - -static void omap3evm_panel_disable(struct lcd_panel *panel) -{ - gpio_set_value(LCD_PANEL_ENABLE_GPIO, 1); -} - -static unsigned long omap3evm_panel_get_caps(struct lcd_panel *panel) -{ - return 0; -} - -static int omap3evm_bklight_setlevel(struct lcd_panel *panel, - unsigned int level) -{ - u8 c; - if ((level >= 0) && (level <= 100)) { - c = (125 * (100 - level)) / 100 + 2; - twl_i2c_write_u8(TWL4030_MODULE_PWMA, c, TWL_PWMA_PWMAOFF); - bklight_level = level; - } - return 0; -} - -static unsigned int omap3evm_bklight_getlevel(struct lcd_panel *panel) -{ - return bklight_level; -} - -static unsigned int omap3evm_bklight_getmaxlevel(struct lcd_panel *panel) -{ - return 100; -} - -struct lcd_panel omap3evm_panel = { - .name = "omap3evm", - .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC | - OMAP_LCDC_INV_HSYNC, - - .bpp = 16, - .data_lines = 18, - .x_res = 480, - .y_res = 640, - .hsw = 3, /* hsync_len (4) - 1 */ - .hfp = 3, /* right_margin (4) - 1 */ - .hbp = 39, /* left_margin (40) - 1 */ - .vsw = 1, /* vsync_len (2) - 1 */ - .vfp = 2, /* lower_margin */ - .vbp = 7, /* upper_margin (8) - 1 */ - - .pixel_clock = 26000, - - .init = omap3evm_panel_init, - .cleanup = omap3evm_panel_cleanup, - .enable = omap3evm_panel_enable, - .disable = omap3evm_panel_disable, - .get_caps = omap3evm_panel_get_caps, - .set_bklight_level = omap3evm_bklight_setlevel, - .get_bklight_level = omap3evm_bklight_getlevel, - .get_bklight_max = omap3evm_bklight_getmaxlevel, -}; - -static int omap3evm_panel_probe(struct platform_device *pdev) -{ - omapfb_register_panel(&omap3evm_panel); - return 0; -} - -static int omap3evm_panel_remove(struct platform_device *pdev) -{ - return 0; -} - -static int omap3evm_panel_suspend(struct platform_device *pdev, - pm_message_t mesg) -{ - return 0; -} - -static int omap3evm_panel_resume(struct platform_device *pdev) -{ - return 0; -} - -struct platform_driver omap3evm_panel_driver = { - .probe = omap3evm_panel_probe, - .remove = omap3evm_panel_remove, - .suspend = omap3evm_panel_suspend, - .resume = omap3evm_panel_resume, - .driver = { - .name = "omap3evm_lcd", - .owner = THIS_MODULE, - }, -}; - -static int __init omap3evm_panel_drv_init(void) -{ - return platform_driver_register(&omap3evm_panel_driver); -} - -static void __exit omap3evm_panel_drv_exit(void) -{ - platform_driver_unregister(&omap3evm_panel_driver); -} - -module_init(omap3evm_panel_drv_init); -module_exit(omap3evm_panel_drv_exit); diff --git a/drivers/video/omap/lcd_overo.c b/drivers/video/omap/lcd_overo.c deleted file mode 100644 index b8fd5b2ec29c..000000000000 --- a/drivers/video/omap/lcd_overo.c +++ /dev/null @@ -1,180 +0,0 @@ -/* - * LCD panel support for the Gumstix Overo - * - * Author: Steve Sakoman <steve@sakoman.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * 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, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - */ - -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/i2c/twl.h> - -#include <asm/gpio.h> -#include <plat/mux.h> -#include <asm/mach-types.h> - -#include "omapfb.h" - -#define LCD_ENABLE 144 - -static int overo_panel_init(struct lcd_panel *panel, - struct omapfb_device *fbdev) -{ - if ((gpio_request(LCD_ENABLE, "LCD_ENABLE") == 0) && - (gpio_direction_output(LCD_ENABLE, 1) == 0)) - gpio_export(LCD_ENABLE, 0); - else - printk(KERN_ERR "could not obtain gpio for LCD_ENABLE\n"); - - return 0; -} - -static void overo_panel_cleanup(struct lcd_panel *panel) -{ - gpio_free(LCD_ENABLE); -} - -static int overo_panel_enable(struct lcd_panel *panel) -{ - gpio_set_value(LCD_ENABLE, 1); - return 0; -} - -static void overo_panel_disable(struct lcd_panel *panel) -{ - gpio_set_value(LCD_ENABLE, 0); -} - -static unsigned long overo_panel_get_caps(struct lcd_panel *panel) -{ - return 0; -} - -struct lcd_panel overo_panel = { - .name = "overo", - .config = OMAP_LCDC_PANEL_TFT, - .bpp = 16, - .data_lines = 24, - -#if defined CONFIG_FB_OMAP_031M3R - - /* 640 x 480 @ 60 Hz Reduced blanking VESA CVT 0.31M3-R */ - .x_res = 640, - .y_res = 480, - .hfp = 48, - .hsw = 32, - .hbp = 80, - .vfp = 3, - .vsw = 4, - .vbp = 7, - .pixel_clock = 23500, - -#elif defined CONFIG_FB_OMAP_048M3R - - /* 800 x 600 @ 60 Hz Reduced blanking VESA CVT 0.48M3-R */ - .x_res = 800, - .y_res = 600, - .hfp = 48, - .hsw = 32, - .hbp = 80, - .vfp = 3, - .vsw = 4, - .vbp = 11, - .pixel_clock = 35500, - -#elif defined CONFIG_FB_OMAP_079M3R - - /* 1024 x 768 @ 60 Hz Reduced blanking VESA CVT 0.79M3-R */ - .x_res = 1024, - .y_res = 768, - .hfp = 48, - .hsw = 32, - .hbp = 80, - .vfp = 3, - .vsw = 4, - .vbp = 15, - .pixel_clock = 56000, - -#elif defined CONFIG_FB_OMAP_092M9R - - /* 1280 x 720 @ 60 Hz Reduced blanking VESA CVT 0.92M9-R */ - .x_res = 1280, - .y_res = 720, - .hfp = 48, - .hsw = 32, - .hbp = 80, - .vfp = 3, - .vsw = 5, - .vbp = 13, - .pixel_clock = 64000, - -#else - - /* use 640 x 480 if no config option */ - /* 640 x 480 @ 60 Hz Reduced blanking VESA CVT 0.31M3-R */ - .x_res = 640, - .y_res = 480, - .hfp = 48, - .hsw = 32, - .hbp = 80, - .vfp = 3, - .vsw = 4, - .vbp = 7, - .pixel_clock = 23500, - -#endif - - .init = overo_panel_init, - .cleanup = overo_panel_cleanup, - .enable = overo_panel_enable, - .disable = overo_panel_disable, - .get_caps = overo_panel_get_caps, -}; - -static int overo_panel_probe(struct platform_device *pdev) -{ - omapfb_register_panel(&overo_panel); - return 0; -} - -static int overo_panel_remove(struct platform_device *pdev) -{ - /* omapfb does not have unregister_panel */ - return 0; -} - -static struct platform_driver overo_panel_driver = { - .probe = overo_panel_probe, - .remove = overo_panel_remove, - .driver = { - .name = "overo_lcd", - .owner = THIS_MODULE, - }, -}; - -static int __init overo_panel_drv_init(void) -{ - return platform_driver_register(&overo_panel_driver); -} - -static void __exit overo_panel_drv_exit(void) -{ - platform_driver_unregister(&overo_panel_driver); -} - -module_init(overo_panel_drv_init); -module_exit(overo_panel_drv_exit); diff --git a/drivers/video/omap2/displays/Kconfig b/drivers/video/omap2/displays/Kconfig index 609a28073178..8d8e1fe1901c 100644 --- a/drivers/video/omap2/displays/Kconfig +++ b/drivers/video/omap2/displays/Kconfig @@ -10,6 +10,13 @@ config PANEL_GENERIC_DPI Supports LCD Panel used in TI SDP3430 and EVM boards, OMAP3517 EVM boards and CM-T35. +config PANEL_DVI + tristate "DVI output" + depends on OMAP2_DSS_DPI + help + Driver for external monitors, connected via DVI. The driver uses i2c + to read EDID information from the monitor. + config PANEL_LGPHILIPS_LB035Q02 tristate "LG.Philips LB035Q02 LCD Panel" depends on OMAP2_DSS_DPI && SPI @@ -19,20 +26,30 @@ config PANEL_LGPHILIPS_LB035Q02 config PANEL_SHARP_LS037V7DW01 tristate "Sharp LS037V7DW01 LCD Panel" depends on OMAP2_DSS_DPI - select BACKLIGHT_CLASS_DEVICE + depends on BACKLIGHT_CLASS_DEVICE help LCD Panel used in TI's SDP3430 and EVM boards config PANEL_NEC_NL8048HL11_01B tristate "NEC NL8048HL11-01B Panel" depends on OMAP2_DSS_DPI + depends on SPI + depends on BACKLIGHT_CLASS_DEVICE help This NEC NL8048HL11-01B panel is TFT LCD used in the Zoom2/3/3630 sdp boards. +config PANEL_PICODLP + tristate "TI PICO DLP mini-projector" + depends on OMAP2_DSS && I2C + help + A mini-projector used in TI's SDP4430 and EVM boards + For more info please visit http://www.dlp.com/projector/ + config PANEL_TAAL tristate "Taal DSI Panel" depends on OMAP2_DSS_DSI + depends on BACKLIGHT_CLASS_DEVICE help Taal DSI command mode panel from TPO. @@ -45,7 +62,14 @@ config PANEL_TPO_TD043MTEA1 config PANEL_ACX565AKM tristate "ACX565AKM Panel" depends on OMAP2_DSS_SDI && SPI - select BACKLIGHT_CLASS_DEVICE + depends on BACKLIGHT_CLASS_DEVICE help This is the LCD panel used on Nokia N900 + +config PANEL_N8X0 + tristate "N8X0 Panel" + depends on OMAP2_DSS_RFBI && SPI + depends on BACKLIGHT_CLASS_DEVICE + help + This is the LCD panel used on Nokia N8x0 endmenu diff --git a/drivers/video/omap2/displays/Makefile b/drivers/video/omap2/displays/Makefile index 0f601ab3abf4..fbfafc6eebb4 100644 --- a/drivers/video/omap2/displays/Makefile +++ b/drivers/video/omap2/displays/Makefile @@ -1,8 +1,11 @@ obj-$(CONFIG_PANEL_GENERIC_DPI) += panel-generic-dpi.o +obj-$(CONFIG_PANEL_DVI) += panel-dvi.o obj-$(CONFIG_PANEL_LGPHILIPS_LB035Q02) += panel-lgphilips-lb035q02.o obj-$(CONFIG_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o obj-$(CONFIG_PANEL_NEC_NL8048HL11_01B) += panel-nec-nl8048hl11-01b.o obj-$(CONFIG_PANEL_TAAL) += panel-taal.o +obj-$(CONFIG_PANEL_PICODLP) += panel-picodlp.o obj-$(CONFIG_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o obj-$(CONFIG_PANEL_ACX565AKM) += panel-acx565akm.o +obj-$(CONFIG_PANEL_N8X0) += panel-n8x0.o diff --git a/drivers/video/omap2/displays/panel-dvi.c b/drivers/video/omap2/displays/panel-dvi.c new file mode 100644 index 000000000000..03eb14af33e0 --- /dev/null +++ b/drivers/video/omap2/displays/panel-dvi.c @@ -0,0 +1,363 @@ +/* + * DVI output support + * + * Copyright (C) 2011 Texas Instruments Inc + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <video/omapdss.h> +#include <linux/i2c.h> +#include <drm/drm_edid.h> + +#include <video/omap-panel-dvi.h> + +static const struct omap_video_timings panel_dvi_default_timings = { + .x_res = 640, + .y_res = 480, + + .pixel_clock = 23500, + + .hfp = 48, + .hsw = 32, + .hbp = 80, + + .vfp = 3, + .vsw = 4, + .vbp = 7, +}; + +struct panel_drv_data { + struct omap_dss_device *dssdev; + + struct mutex lock; +}; + +static inline struct panel_dvi_platform_data +*get_pdata(const struct omap_dss_device *dssdev) +{ + return dssdev->data; +} + +static int panel_dvi_power_on(struct omap_dss_device *dssdev) +{ + struct panel_dvi_platform_data *pdata = get_pdata(dssdev); + int r; + + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) + return 0; + + r = omapdss_dpi_display_enable(dssdev); + if (r) + goto err0; + + if (pdata->platform_enable) { + r = pdata->platform_enable(dssdev); + if (r) + goto err1; + } + + return 0; +err1: + omapdss_dpi_display_disable(dssdev); +err0: + return r; +} + +static void panel_dvi_power_off(struct omap_dss_device *dssdev) +{ + struct panel_dvi_platform_data *pdata = get_pdata(dssdev); + + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + return; + + if (pdata->platform_disable) + pdata->platform_disable(dssdev); + + omapdss_dpi_display_disable(dssdev); +} + +static int panel_dvi_probe(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata; + + ddata = kzalloc(sizeof(*ddata), GFP_KERNEL); + if (!ddata) + return -ENOMEM; + + dssdev->panel.timings = panel_dvi_default_timings; + dssdev->panel.config = OMAP_DSS_LCD_TFT; + + ddata->dssdev = dssdev; + mutex_init(&ddata->lock); + + dev_set_drvdata(&dssdev->dev, ddata); + + return 0; +} + +static void __exit panel_dvi_remove(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); + + mutex_lock(&ddata->lock); + + dev_set_drvdata(&dssdev->dev, NULL); + + mutex_unlock(&ddata->lock); + + kfree(ddata); +} + +static int panel_dvi_enable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); + int r; + + mutex_lock(&ddata->lock); + + r = panel_dvi_power_on(dssdev); + if (r == 0) + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + mutex_unlock(&ddata->lock); + + return r; +} + +static void panel_dvi_disable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); + + mutex_lock(&ddata->lock); + + panel_dvi_power_off(dssdev); + + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; + + mutex_unlock(&ddata->lock); +} + +static int panel_dvi_suspend(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); + + mutex_lock(&ddata->lock); + + panel_dvi_power_off(dssdev); + + dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; + + mutex_unlock(&ddata->lock); + + return 0; +} + +static int panel_dvi_resume(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); + int r; + + mutex_lock(&ddata->lock); + + r = panel_dvi_power_on(dssdev); + if (r == 0) + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + mutex_unlock(&ddata->lock); + + return r; +} + +static void panel_dvi_set_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); + + mutex_lock(&ddata->lock); + dpi_set_timings(dssdev, timings); + mutex_unlock(&ddata->lock); +} + +static void panel_dvi_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); + + mutex_lock(&ddata->lock); + *timings = dssdev->panel.timings; + mutex_unlock(&ddata->lock); +} + +static int panel_dvi_check_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); + int r; + + mutex_lock(&ddata->lock); + r = dpi_check_timings(dssdev, timings); + mutex_unlock(&ddata->lock); + + return r; +} + + +static int panel_dvi_ddc_read(struct i2c_adapter *adapter, + unsigned char *buf, u16 count, u8 offset) +{ + int r, retries; + + for (retries = 3; retries > 0; retries--) { + struct i2c_msg msgs[] = { + { + .addr = DDC_ADDR, + .flags = 0, + .len = 1, + .buf = &offset, + }, { + .addr = DDC_ADDR, + .flags = I2C_M_RD, + .len = count, + .buf = buf, + } + }; + + r = i2c_transfer(adapter, msgs, 2); + if (r == 2) + return 0; + + if (r != -EAGAIN) + break; + } + + return r < 0 ? r : -EIO; +} + +static int panel_dvi_read_edid(struct omap_dss_device *dssdev, + u8 *edid, int len) +{ + struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); + struct panel_dvi_platform_data *pdata = get_pdata(dssdev); + struct i2c_adapter *adapter; + int r, l, bytes_read; + + mutex_lock(&ddata->lock); + + if (pdata->i2c_bus_num == 0) { + r = -ENODEV; + goto err; + } + + adapter = i2c_get_adapter(pdata->i2c_bus_num); + if (!adapter) { + dev_err(&dssdev->dev, "Failed to get I2C adapter, bus %d\n", + pdata->i2c_bus_num); + r = -EINVAL; + goto err; + } + + l = min(EDID_LENGTH, len); + r = panel_dvi_ddc_read(adapter, edid, l, 0); + if (r) + goto err; + + bytes_read = l; + + /* if there are extensions, read second block */ + if (len > EDID_LENGTH && edid[0x7e] > 0) { + l = min(EDID_LENGTH, len - EDID_LENGTH); + + r = panel_dvi_ddc_read(adapter, edid + EDID_LENGTH, + l, EDID_LENGTH); + if (r) + goto err; + + bytes_read += l; + } + + mutex_unlock(&ddata->lock); + + return bytes_read; + +err: + mutex_unlock(&ddata->lock); + return r; +} + +static bool panel_dvi_detect(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); + struct panel_dvi_platform_data *pdata = get_pdata(dssdev); + struct i2c_adapter *adapter; + unsigned char out; + int r; + + mutex_lock(&ddata->lock); + + if (pdata->i2c_bus_num == 0) + goto out; + + adapter = i2c_get_adapter(pdata->i2c_bus_num); + if (!adapter) + goto out; + + r = panel_dvi_ddc_read(adapter, &out, 1, 0); + + mutex_unlock(&ddata->lock); + + return r == 0; + +out: + mutex_unlock(&ddata->lock); + return true; +} + +static struct omap_dss_driver panel_dvi_driver = { + .probe = panel_dvi_probe, + .remove = __exit_p(panel_dvi_remove), + + .enable = panel_dvi_enable, + .disable = panel_dvi_disable, + .suspend = panel_dvi_suspend, + .resume = panel_dvi_resume, + + .set_timings = panel_dvi_set_timings, + .get_timings = panel_dvi_get_timings, + .check_timings = panel_dvi_check_timings, + + .read_edid = panel_dvi_read_edid, + .detect = panel_dvi_detect, + + .driver = { + .name = "dvi", + .owner = THIS_MODULE, + }, +}; + +static int __init panel_dvi_init(void) +{ + return omap_dss_register_driver(&panel_dvi_driver); +} + +static void __exit panel_dvi_exit(void) +{ + omap_dss_unregister_driver(&panel_dvi_driver); +} + +module_init(panel_dvi_init); +module_exit(panel_dvi_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays/panel-generic-dpi.c b/drivers/video/omap2/displays/panel-generic-dpi.c index 9c90f75653fb..519c47d2057f 100644 --- a/drivers/video/omap2/displays/panel-generic-dpi.c +++ b/drivers/video/omap2/displays/panel-generic-dpi.c @@ -58,30 +58,6 @@ struct panel_config { /* Panel configurations */ static struct panel_config generic_dpi_panels[] = { - /* Generic Panel */ - { - { - .x_res = 640, - .y_res = 480, - - .pixel_clock = 23500, - - .hfp = 48, - .hsw = 32, - .hbp = 80, - - .vfp = 3, - .vsw = 4, - .vbp = 7, - }, - .acbi = 0x0, - .acb = 0x0, - .config = OMAP_DSS_LCD_TFT, - .power_on_delay = 0, - .power_off_delay = 0, - .name = "generic", - }, - /* Sharp LQ043T1DG01 */ { { @@ -232,6 +208,95 @@ static struct panel_config generic_dpi_panels[] = { .power_off_delay = 0, .name = "powertip_ph480272t", }, + + /* Innolux AT070TN83 */ + { + { + .x_res = 800, + .y_res = 480, + + .pixel_clock = 40000, + + .hsw = 48, + .hfp = 1, + .hbp = 1, + + .vsw = 3, + .vfp = 12, + .vbp = 25, + }, + .acbi = 0x0, + .acb = 0x28, + .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | + OMAP_DSS_LCD_IHS, + .power_on_delay = 0, + .power_off_delay = 0, + .name = "innolux_at070tn83", + }, + + /* NEC NL2432DR22-11B */ + { + { + .x_res = 240, + .y_res = 320, + + .pixel_clock = 5400, + + .hsw = 3, + .hfp = 3, + .hbp = 39, + + .vsw = 1, + .vfp = 2, + .vbp = 7, + }, + .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | + OMAP_DSS_LCD_IHS, + .name = "nec_nl2432dr22-11b", + }, + + /* Unknown panel used in OMAP H4 */ + { + { + .x_res = 240, + .y_res = 320, + + .pixel_clock = 6250, + + .hsw = 15, + .hfp = 15, + .hbp = 60, + + .vsw = 1, + .vfp = 1, + .vbp = 1, + }, + .config = OMAP_DSS_LCD_TFT, + + .name = "h4", + }, + + /* Unknown panel used in Samsung OMAP2 Apollon */ + { + { + .x_res = 480, + .y_res = 272, + + .pixel_clock = 6250, + + .hsw = 41, + .hfp = 2, + .hbp = 2, + + .vsw = 10, + .vfp = 2, + .vbp = 2, + }, + .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | + OMAP_DSS_LCD_IHS, + + .name = "apollon", + }, }; struct panel_drv_data { diff --git a/drivers/video/omap2/displays/panel-n8x0.c b/drivers/video/omap2/displays/panel-n8x0.c new file mode 100644 index 000000000000..150e8bae35a1 --- /dev/null +++ b/drivers/video/omap2/displays/panel-n8x0.c @@ -0,0 +1,747 @@ +/* #define DEBUG */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/gpio.h> +#include <linux/spi/spi.h> +#include <linux/backlight.h> +#include <linux/fb.h> + +#include <video/omapdss.h> +#include <video/omap-panel-n8x0.h> + +#define BLIZZARD_REV_CODE 0x00 +#define BLIZZARD_CONFIG 0x02 +#define BLIZZARD_PLL_DIV 0x04 +#define BLIZZARD_PLL_LOCK_RANGE 0x06 +#define BLIZZARD_PLL_CLOCK_SYNTH_0 0x08 +#define BLIZZARD_PLL_CLOCK_SYNTH_1 0x0a +#define BLIZZARD_PLL_MODE 0x0c +#define BLIZZARD_CLK_SRC 0x0e +#define BLIZZARD_MEM_BANK0_ACTIVATE 0x10 +#define BLIZZARD_MEM_BANK0_STATUS 0x14 +#define BLIZZARD_PANEL_CONFIGURATION 0x28 +#define BLIZZARD_HDISP 0x2a +#define BLIZZARD_HNDP 0x2c +#define BLIZZARD_VDISP0 0x2e +#define BLIZZARD_VDISP1 0x30 +#define BLIZZARD_VNDP 0x32 +#define BLIZZARD_HSW 0x34 +#define BLIZZARD_VSW 0x38 +#define BLIZZARD_DISPLAY_MODE 0x68 +#define BLIZZARD_INPUT_WIN_X_START_0 0x6c +#define BLIZZARD_DATA_SOURCE_SELECT 0x8e +#define BLIZZARD_DISP_MEM_DATA_PORT 0x90 +#define BLIZZARD_DISP_MEM_READ_ADDR0 0x92 +#define BLIZZARD_POWER_SAVE 0xE6 +#define BLIZZARD_NDISP_CTRL_STATUS 0xE8 + +/* Data source select */ +/* For S1D13745 */ +#define BLIZZARD_SRC_WRITE_LCD_BACKGROUND 0x00 +#define BLIZZARD_SRC_WRITE_LCD_DESTRUCTIVE 0x01 +#define BLIZZARD_SRC_WRITE_OVERLAY_ENABLE 0x04 +#define BLIZZARD_SRC_DISABLE_OVERLAY 0x05 +/* For S1D13744 */ +#define BLIZZARD_SRC_WRITE_LCD 0x00 +#define BLIZZARD_SRC_BLT_LCD 0x06 + +#define BLIZZARD_COLOR_RGB565 0x01 +#define BLIZZARD_COLOR_YUV420 0x09 + +#define BLIZZARD_VERSION_S1D13745 0x01 /* Hailstorm */ +#define BLIZZARD_VERSION_S1D13744 0x02 /* Blizzard */ + +#define MIPID_CMD_READ_DISP_ID 0x04 +#define MIPID_CMD_READ_RED 0x06 +#define MIPID_CMD_READ_GREEN 0x07 +#define MIPID_CMD_READ_BLUE 0x08 +#define MIPID_CMD_READ_DISP_STATUS 0x09 +#define MIPID_CMD_RDDSDR 0x0F +#define MIPID_CMD_SLEEP_IN 0x10 +#define MIPID_CMD_SLEEP_OUT 0x11 +#define MIPID_CMD_DISP_OFF 0x28 +#define MIPID_CMD_DISP_ON 0x29 + +static struct panel_drv_data { + struct mutex lock; + + struct omap_dss_device *dssdev; + struct spi_device *spidev; + struct backlight_device *bldev; + + int blizzard_ver; +} s_drv_data; + + +static inline +struct panel_n8x0_data *get_board_data(const struct omap_dss_device *dssdev) +{ + return dssdev->data; +} + +static inline +struct panel_drv_data *get_drv_data(const struct omap_dss_device *dssdev) +{ + return &s_drv_data; +} + + +static inline void blizzard_cmd(u8 cmd) +{ + omap_rfbi_write_command(&cmd, 1); +} + +static inline void blizzard_write(u8 cmd, const u8 *buf, int len) +{ + omap_rfbi_write_command(&cmd, 1); + omap_rfbi_write_data(buf, len); +} + +static inline void blizzard_read(u8 cmd, u8 *buf, int len) +{ + omap_rfbi_write_command(&cmd, 1); + omap_rfbi_read_data(buf, len); +} + +static u8 blizzard_read_reg(u8 cmd) +{ + u8 data; + blizzard_read(cmd, &data, 1); + return data; +} + +static void blizzard_ctrl_setup_update(struct omap_dss_device *dssdev, + int x, int y, int w, int h) +{ + struct panel_drv_data *ddata = get_drv_data(dssdev); + u8 tmp[18]; + int x_end, y_end; + + x_end = x + w - 1; + y_end = y + h - 1; + + tmp[0] = x; + tmp[1] = x >> 8; + tmp[2] = y; + tmp[3] = y >> 8; + tmp[4] = x_end; + tmp[5] = x_end >> 8; + tmp[6] = y_end; + tmp[7] = y_end >> 8; + + /* scaling? */ + tmp[8] = x; + tmp[9] = x >> 8; + tmp[10] = y; + tmp[11] = y >> 8; + tmp[12] = x_end; + tmp[13] = x_end >> 8; + tmp[14] = y_end; + tmp[15] = y_end >> 8; + + tmp[16] = BLIZZARD_COLOR_RGB565; + + if (ddata->blizzard_ver == BLIZZARD_VERSION_S1D13745) + tmp[17] = BLIZZARD_SRC_WRITE_LCD_BACKGROUND; + else + tmp[17] = ddata->blizzard_ver == BLIZZARD_VERSION_S1D13744 ? + BLIZZARD_SRC_WRITE_LCD : + BLIZZARD_SRC_WRITE_LCD_DESTRUCTIVE; + + omap_rfbi_configure(dssdev, 16, 8); + + blizzard_write(BLIZZARD_INPUT_WIN_X_START_0, tmp, 18); + + omap_rfbi_configure(dssdev, 16, 16); +} + +static void mipid_transfer(struct spi_device *spi, int cmd, const u8 *wbuf, + int wlen, u8 *rbuf, int rlen) +{ + struct spi_message m; + struct spi_transfer *x, xfer[4]; + u16 w; + int r; + + spi_message_init(&m); + + memset(xfer, 0, sizeof(xfer)); + x = &xfer[0]; + + cmd &= 0xff; + x->tx_buf = &cmd; + x->bits_per_word = 9; + x->len = 2; + spi_message_add_tail(x, &m); + + if (wlen) { + x++; + x->tx_buf = wbuf; + x->len = wlen; + x->bits_per_word = 9; + spi_message_add_tail(x, &m); + } + + if (rlen) { + x++; + x->rx_buf = &w; + x->len = 1; + spi_message_add_tail(x, &m); + + if (rlen > 1) { + /* Arrange for the extra clock before the first + * data bit. + */ + x->bits_per_word = 9; + x->len = 2; + + x++; + x->rx_buf = &rbuf[1]; + x->len = rlen - 1; + spi_message_add_tail(x, &m); + } + } + + r = spi_sync(spi, &m); + if (r < 0) + dev_dbg(&spi->dev, "spi_sync %d\n", r); + + if (rlen) + rbuf[0] = w & 0xff; +} + +static inline void mipid_cmd(struct spi_device *spi, int cmd) +{ + mipid_transfer(spi, cmd, NULL, 0, NULL, 0); +} + +static inline void mipid_write(struct spi_device *spi, + int reg, const u8 *buf, int len) +{ + mipid_transfer(spi, reg, buf, len, NULL, 0); +} + +static inline void mipid_read(struct spi_device *spi, + int reg, u8 *buf, int len) +{ + mipid_transfer(spi, reg, NULL, 0, buf, len); +} + +static void set_data_lines(struct spi_device *spi, int data_lines) +{ + u16 par; + + switch (data_lines) { + case 16: + par = 0x150; + break; + case 18: + par = 0x160; + break; + case 24: + par = 0x170; + break; + } + + mipid_write(spi, 0x3a, (u8 *)&par, 2); +} + +static void send_init_string(struct spi_device *spi) +{ + u16 initpar[] = { 0x0102, 0x0100, 0x0100 }; + mipid_write(spi, 0xc2, (u8 *)initpar, sizeof(initpar)); +} + +static void send_display_on(struct spi_device *spi) +{ + mipid_cmd(spi, MIPID_CMD_DISP_ON); +} + +static void send_display_off(struct spi_device *spi) +{ + mipid_cmd(spi, MIPID_CMD_DISP_OFF); +} + +static void send_sleep_out(struct spi_device *spi) +{ + mipid_cmd(spi, MIPID_CMD_SLEEP_OUT); + msleep(120); +} + +static void send_sleep_in(struct spi_device *spi) +{ + mipid_cmd(spi, MIPID_CMD_SLEEP_IN); + msleep(50); +} + +static int n8x0_panel_power_on(struct omap_dss_device *dssdev) +{ + int r; + struct panel_n8x0_data *bdata = get_board_data(dssdev); + struct panel_drv_data *ddata = get_drv_data(dssdev); + struct spi_device *spi = ddata->spidev; + u8 rev, conf; + u8 display_id[3]; + const char *panel_name; + + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) + return 0; + + gpio_direction_output(bdata->ctrl_pwrdown, 1); + + if (bdata->platform_enable) { + r = bdata->platform_enable(dssdev); + if (r) + goto err_plat_en; + } + + r = omapdss_rfbi_display_enable(dssdev); + if (r) + goto err_rfbi_en; + + rev = blizzard_read_reg(BLIZZARD_REV_CODE); + conf = blizzard_read_reg(BLIZZARD_CONFIG); + + switch (rev & 0xfc) { + case 0x9c: + ddata->blizzard_ver = BLIZZARD_VERSION_S1D13744; + dev_info(&dssdev->dev, "s1d13744 LCD controller rev %d " + "initialized (CNF pins %x)\n", rev & 0x03, conf & 0x07); + break; + case 0xa4: + ddata->blizzard_ver = BLIZZARD_VERSION_S1D13745; + dev_info(&dssdev->dev, "s1d13745 LCD controller rev %d " + "initialized (CNF pins %x)\n", rev & 0x03, conf & 0x07); + break; + default: + dev_err(&dssdev->dev, "invalid s1d1374x revision %02x\n", rev); + r = -ENODEV; + goto err_inv_chip; + } + + /* panel */ + + gpio_direction_output(bdata->panel_reset, 1); + + mipid_read(spi, MIPID_CMD_READ_DISP_ID, display_id, 3); + dev_dbg(&spi->dev, "MIPI display ID: %02x%02x%02x\n", + display_id[0], display_id[1], display_id[2]); + + switch (display_id[0]) { + case 0x45: + panel_name = "lph8923"; + break; + case 0x83: + panel_name = "ls041y3"; + break; + default: + dev_err(&dssdev->dev, "invalid display ID 0x%x\n", + display_id[0]); + r = -ENODEV; + goto err_inv_panel; + } + + dev_info(&dssdev->dev, "%s rev %02x LCD detected\n", + panel_name, display_id[1]); + + send_sleep_out(spi); + send_init_string(spi); + set_data_lines(spi, 24); + send_display_on(spi); + + return 0; + +err_inv_panel: + /* + * HACK: we should turn off the panel here, but there is some problem + * with the initialization sequence, and we fail to init the panel if we + * have turned it off + */ + /* gpio_direction_output(bdata->panel_reset, 0); */ +err_inv_chip: + omapdss_rfbi_display_disable(dssdev); +err_rfbi_en: + if (bdata->platform_disable) + bdata->platform_disable(dssdev); +err_plat_en: + gpio_direction_output(bdata->ctrl_pwrdown, 0); + return r; +} + +static void n8x0_panel_power_off(struct omap_dss_device *dssdev) +{ + struct panel_n8x0_data *bdata = get_board_data(dssdev); + struct panel_drv_data *ddata = get_drv_data(dssdev); + struct spi_device *spi = ddata->spidev; + + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + return; + + send_display_off(spi); + send_sleep_in(spi); + + if (bdata->platform_disable) + bdata->platform_disable(dssdev); + + /* + * HACK: we should turn off the panel here, but there is some problem + * with the initialization sequence, and we fail to init the panel if we + * have turned it off + */ + /* gpio_direction_output(bdata->panel_reset, 0); */ + gpio_direction_output(bdata->ctrl_pwrdown, 0); + omapdss_rfbi_display_disable(dssdev); +} + +static const struct rfbi_timings n8x0_panel_timings = { + .cs_on_time = 0, + + .we_on_time = 9000, + .we_off_time = 18000, + .we_cycle_time = 36000, + + .re_on_time = 9000, + .re_off_time = 27000, + .re_cycle_time = 36000, + + .access_time = 27000, + .cs_off_time = 36000, + + .cs_pulse_width = 0, +}; + +static int n8x0_bl_update_status(struct backlight_device *dev) +{ + struct omap_dss_device *dssdev = dev_get_drvdata(&dev->dev); + struct panel_n8x0_data *bdata = get_board_data(dssdev); + struct panel_drv_data *ddata = get_drv_data(dssdev); + int r; + int level; + + mutex_lock(&ddata->lock); + + if (dev->props.fb_blank == FB_BLANK_UNBLANK && + dev->props.power == FB_BLANK_UNBLANK) + level = dev->props.brightness; + else + level = 0; + + dev_dbg(&dssdev->dev, "update brightness to %d\n", level); + + if (!bdata->set_backlight) + r = -EINVAL; + else + r = bdata->set_backlight(dssdev, level); + + mutex_unlock(&ddata->lock); + + return r; +} + +static int n8x0_bl_get_intensity(struct backlight_device *dev) +{ + if (dev->props.fb_blank == FB_BLANK_UNBLANK && + dev->props.power == FB_BLANK_UNBLANK) + return dev->props.brightness; + + return 0; +} + +static const struct backlight_ops n8x0_bl_ops = { + .get_brightness = n8x0_bl_get_intensity, + .update_status = n8x0_bl_update_status, +}; + +static int n8x0_panel_probe(struct omap_dss_device *dssdev) +{ + struct panel_n8x0_data *bdata = get_board_data(dssdev); + struct panel_drv_data *ddata; + struct backlight_device *bldev; + struct backlight_properties props; + int r; + + dev_dbg(&dssdev->dev, "probe\n"); + + if (!bdata) + return -EINVAL; + + s_drv_data.dssdev = dssdev; + + ddata = &s_drv_data; + + mutex_init(&ddata->lock); + + dssdev->panel.config = OMAP_DSS_LCD_TFT; + dssdev->panel.timings.x_res = 800; + dssdev->panel.timings.y_res = 480; + dssdev->ctrl.pixel_size = 16; + dssdev->ctrl.rfbi_timings = n8x0_panel_timings; + + memset(&props, 0, sizeof(props)); + props.max_brightness = 127; + props.type = BACKLIGHT_PLATFORM; + bldev = backlight_device_register(dev_name(&dssdev->dev), &dssdev->dev, + dssdev, &n8x0_bl_ops, &props); + if (IS_ERR(bldev)) { + r = PTR_ERR(bldev); + dev_err(&dssdev->dev, "register backlight failed\n"); + return r; + } + + ddata->bldev = bldev; + + bldev->props.fb_blank = FB_BLANK_UNBLANK; + bldev->props.power = FB_BLANK_UNBLANK; + bldev->props.brightness = 127; + + n8x0_bl_update_status(bldev); + + return 0; +} + +static void n8x0_panel_remove(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = get_drv_data(dssdev); + struct backlight_device *bldev; + + dev_dbg(&dssdev->dev, "remove\n"); + + bldev = ddata->bldev; + bldev->props.power = FB_BLANK_POWERDOWN; + n8x0_bl_update_status(bldev); + backlight_device_unregister(bldev); + + dev_set_drvdata(&dssdev->dev, NULL); +} + +static int n8x0_panel_enable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = get_drv_data(dssdev); + int r; + + dev_dbg(&dssdev->dev, "enable\n"); + + mutex_lock(&ddata->lock); + + rfbi_bus_lock(); + + r = n8x0_panel_power_on(dssdev); + + rfbi_bus_unlock(); + + if (r) { + mutex_unlock(&ddata->lock); + return r; + } + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + mutex_unlock(&ddata->lock); + + return 0; +} + +static void n8x0_panel_disable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = get_drv_data(dssdev); + + dev_dbg(&dssdev->dev, "disable\n"); + + mutex_lock(&ddata->lock); + + rfbi_bus_lock(); + + n8x0_panel_power_off(dssdev); + + rfbi_bus_unlock(); + + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; + + mutex_unlock(&ddata->lock); +} + +static int n8x0_panel_suspend(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = get_drv_data(dssdev); + + dev_dbg(&dssdev->dev, "suspend\n"); + + mutex_lock(&ddata->lock); + + rfbi_bus_lock(); + + n8x0_panel_power_off(dssdev); + + rfbi_bus_unlock(); + + dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; + + mutex_unlock(&ddata->lock); + + return 0; +} + +static int n8x0_panel_resume(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = get_drv_data(dssdev); + int r; + + dev_dbg(&dssdev->dev, "resume\n"); + + mutex_lock(&ddata->lock); + + rfbi_bus_lock(); + + r = n8x0_panel_power_on(dssdev); + + rfbi_bus_unlock(); + + if (r) { + mutex_unlock(&ddata->lock); + return r; + } + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + mutex_unlock(&ddata->lock); + + return 0; +} + +static void n8x0_panel_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + *timings = dssdev->panel.timings; +} + +static void n8x0_panel_get_resolution(struct omap_dss_device *dssdev, + u16 *xres, u16 *yres) +{ + *xres = dssdev->panel.timings.x_res; + *yres = dssdev->panel.timings.y_res; +} + +static void update_done(void *data) +{ + rfbi_bus_unlock(); +} + +static int n8x0_panel_update(struct omap_dss_device *dssdev, + u16 x, u16 y, u16 w, u16 h) +{ + struct panel_drv_data *ddata = get_drv_data(dssdev); + + dev_dbg(&dssdev->dev, "update\n"); + + mutex_lock(&ddata->lock); + rfbi_bus_lock(); + + omap_rfbi_prepare_update(dssdev, &x, &y, &w, &h); + + blizzard_ctrl_setup_update(dssdev, x, y, w, h); + + omap_rfbi_update(dssdev, x, y, w, h, update_done, NULL); + + mutex_unlock(&ddata->lock); + + return 0; +} + +static int n8x0_panel_sync(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = get_drv_data(dssdev); + + dev_dbg(&dssdev->dev, "sync\n"); + + mutex_lock(&ddata->lock); + rfbi_bus_lock(); + rfbi_bus_unlock(); + mutex_unlock(&ddata->lock); + + return 0; +} + +static struct omap_dss_driver n8x0_panel_driver = { + .probe = n8x0_panel_probe, + .remove = n8x0_panel_remove, + + .enable = n8x0_panel_enable, + .disable = n8x0_panel_disable, + .suspend = n8x0_panel_suspend, + .resume = n8x0_panel_resume, + + .update = n8x0_panel_update, + .sync = n8x0_panel_sync, + + .get_resolution = n8x0_panel_get_resolution, + .get_recommended_bpp = omapdss_default_get_recommended_bpp, + + .get_timings = n8x0_panel_get_timings, + + .driver = { + .name = "n8x0_panel", + .owner = THIS_MODULE, + }, +}; + +/* PANEL */ + +static int mipid_spi_probe(struct spi_device *spi) +{ + dev_dbg(&spi->dev, "mipid_spi_probe\n"); + + spi->mode = SPI_MODE_0; + + s_drv_data.spidev = spi; + + return 0; +} + +static int mipid_spi_remove(struct spi_device *spi) +{ + dev_dbg(&spi->dev, "mipid_spi_remove\n"); + return 0; +} + +static struct spi_driver mipid_spi_driver = { + .driver = { + .name = "lcd_mipid", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = mipid_spi_probe, + .remove = __devexit_p(mipid_spi_remove), +}; + +static int __init n8x0_panel_drv_init(void) +{ + int r; + + r = spi_register_driver(&mipid_spi_driver); + if (r) { + pr_err("n8x0_panel: spi driver registration failed\n"); + return r; + } + + r = omap_dss_register_driver(&n8x0_panel_driver); + if (r) { + pr_err("n8x0_panel: dss driver registration failed\n"); + spi_unregister_driver(&mipid_spi_driver); + return r; + } + + return 0; +} + +static void __exit n8x0_panel_drv_exit(void) +{ + spi_unregister_driver(&mipid_spi_driver); + + omap_dss_unregister_driver(&n8x0_panel_driver); +} + +module_init(n8x0_panel_drv_init); +module_exit(n8x0_panel_drv_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays/panel-picodlp.c b/drivers/video/omap2/displays/panel-picodlp.c new file mode 100644 index 000000000000..98ebdaddab5a --- /dev/null +++ b/drivers/video/omap2/displays/panel-picodlp.c @@ -0,0 +1,594 @@ +/* + * picodlp panel driver + * picodlp_i2c_driver: i2c_client driver + * + * Copyright (C) 2009-2011 Texas Instruments + * Author: Mythri P K <mythripk@ti.com> + * Mayuresh Janorkar <mayur@ti.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/input.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/firmware.h> +#include <linux/slab.h> +#include <linux/mutex.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/gpio.h> + +#include <video/omapdss.h> +#include <video/omap-panel-picodlp.h> + +#include "panel-picodlp.h" + +struct picodlp_data { + struct mutex lock; + struct i2c_client *picodlp_i2c_client; +}; + +static struct i2c_board_info picodlp_i2c_board_info = { + I2C_BOARD_INFO("picodlp_i2c_driver", 0x1b), +}; + +struct picodlp_i2c_data { + struct mutex xfer_lock; +}; + +static struct i2c_device_id picodlp_i2c_id[] = { + { "picodlp_i2c_driver", 0 }, +}; + +struct picodlp_i2c_command { + u8 reg; + u32 value; +}; + +static struct omap_video_timings pico_ls_timings = { + .x_res = 864, + .y_res = 480, + .hsw = 7, + .hfp = 11, + .hbp = 7, + + .pixel_clock = 19200, + + .vsw = 2, + .vfp = 3, + .vbp = 14, +}; + +static inline struct picodlp_panel_data + *get_panel_data(const struct omap_dss_device *dssdev) +{ + return (struct picodlp_panel_data *) dssdev->data; +} + +static u32 picodlp_i2c_read(struct i2c_client *client, u8 reg) +{ + u8 read_cmd[] = {READ_REG_SELECT, reg}, data[4]; + struct picodlp_i2c_data *picodlp_i2c_data = i2c_get_clientdata(client); + struct i2c_msg msg[2]; + + mutex_lock(&picodlp_i2c_data->xfer_lock); + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = 2; + msg[0].buf = read_cmd; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 4; + msg[1].buf = data; + + i2c_transfer(client->adapter, msg, 2); + mutex_unlock(&picodlp_i2c_data->xfer_lock); + return (data[3] | (data[2] << 8) | (data[1] << 16) | (data[0] << 24)); +} + +static int picodlp_i2c_write_block(struct i2c_client *client, + u8 *data, int len) +{ + struct i2c_msg msg; + int i, r, msg_count = 1; + + struct picodlp_i2c_data *picodlp_i2c_data = i2c_get_clientdata(client); + + if (len < 1 || len > 32) { + dev_err(&client->dev, + "too long syn_write_block len %d\n", len); + return -EIO; + } + mutex_lock(&picodlp_i2c_data->xfer_lock); + + msg.addr = client->addr; + msg.flags = 0; + msg.len = len; + msg.buf = data; + r = i2c_transfer(client->adapter, &msg, msg_count); + mutex_unlock(&picodlp_i2c_data->xfer_lock); + + /* + * i2c_transfer returns: + * number of messages sent in case of success + * a negative error number in case of failure + */ + if (r != msg_count) + goto err; + + /* In case of success */ + for (i = 0; i < len; i++) + dev_dbg(&client->dev, + "addr %x bw 0x%02x[%d]: 0x%02x\n", + client->addr, data[0] + i, i, data[i]); + + return 0; +err: + dev_err(&client->dev, "picodlp_i2c_write error\n"); + return r; +} + +static int picodlp_i2c_write(struct i2c_client *client, u8 reg, u32 value) +{ + u8 data[5]; + int i; + + data[0] = reg; + for (i = 1; i < 5; i++) + data[i] = (value >> (32 - (i) * 8)) & 0xFF; + + return picodlp_i2c_write_block(client, data, 5); +} + +static int picodlp_i2c_write_array(struct i2c_client *client, + const struct picodlp_i2c_command commands[], + int count) +{ + int i, r = 0; + for (i = 0; i < count; i++) { + r = picodlp_i2c_write(client, commands[i].reg, + commands[i].value); + if (r) + return r; + } + return r; +} + +static int picodlp_wait_for_dma_done(struct i2c_client *client) +{ + u8 trial = 100; + + do { + msleep(1); + if (!trial--) + return -ETIMEDOUT; + } while (picodlp_i2c_read(client, MAIN_STATUS) & DMA_STATUS); + + return 0; +} + +/** + * picodlp_i2c_init: i2c_initialization routine + * client: i2c_client for communication + * + * return + * 0 : Success, no error + * error code : Failure + */ +static int picodlp_i2c_init(struct i2c_client *client) +{ + int r; + static const struct picodlp_i2c_command init_cmd_set1[] = { + {SOFT_RESET, 1}, + {DMD_PARK_TRIGGER, 1}, + {MISC_REG, 5}, + {SEQ_CONTROL, 0}, + {SEQ_VECTOR, 0x100}, + {DMD_BLOCK_COUNT, 7}, + {DMD_VCC_CONTROL, 0x109}, + {DMD_PARK_PULSE_COUNT, 0xA}, + {DMD_PARK_PULSE_WIDTH, 0xB}, + {DMD_PARK_DELAY, 0x2ED}, + {DMD_SHADOW_ENABLE, 0}, + {FLASH_OPCODE, 0xB}, + {FLASH_DUMMY_BYTES, 1}, + {FLASH_ADDR_BYTES, 3}, + {PBC_CONTROL, 0}, + {FLASH_START_ADDR, CMT_LUT_0_START_ADDR}, + {FLASH_READ_BYTES, CMT_LUT_0_SIZE}, + {CMT_SPLASH_LUT_START_ADDR, 0}, + {CMT_SPLASH_LUT_DEST_SELECT, CMT_LUT_ALL}, + {PBC_CONTROL, 1}, + }; + + static const struct picodlp_i2c_command init_cmd_set2[] = { + {PBC_CONTROL, 0}, + {CMT_SPLASH_LUT_DEST_SELECT, 0}, + {PBC_CONTROL, 0}, + {FLASH_START_ADDR, SEQUENCE_0_START_ADDR}, + {FLASH_READ_BYTES, SEQUENCE_0_SIZE}, + {SEQ_RESET_LUT_START_ADDR, 0}, + {SEQ_RESET_LUT_DEST_SELECT, SEQ_SEQ_LUT}, + {PBC_CONTROL, 1}, + }; + + static const struct picodlp_i2c_command init_cmd_set3[] = { + {PBC_CONTROL, 0}, + {SEQ_RESET_LUT_DEST_SELECT, 0}, + {PBC_CONTROL, 0}, + {FLASH_START_ADDR, DRC_TABLE_0_START_ADDR}, + {FLASH_READ_BYTES, DRC_TABLE_0_SIZE}, + {SEQ_RESET_LUT_START_ADDR, 0}, + {SEQ_RESET_LUT_DEST_SELECT, SEQ_DRC_LUT_ALL}, + {PBC_CONTROL, 1}, + }; + + static const struct picodlp_i2c_command init_cmd_set4[] = { + {PBC_CONTROL, 0}, + {SEQ_RESET_LUT_DEST_SELECT, 0}, + {SDC_ENABLE, 1}, + {AGC_CTRL, 7}, + {CCA_C1A, 0x100}, + {CCA_C1B, 0x0}, + {CCA_C1C, 0x0}, + {CCA_C2A, 0x0}, + {CCA_C2B, 0x100}, + {CCA_C2C, 0x0}, + {CCA_C3A, 0x0}, + {CCA_C3B, 0x0}, + {CCA_C3C, 0x100}, + {CCA_C7A, 0x100}, + {CCA_C7B, 0x100}, + {CCA_C7C, 0x100}, + {CCA_ENABLE, 1}, + {CPU_IF_MODE, 1}, + {SHORT_FLIP, 1}, + {CURTAIN_CONTROL, 0}, + {DMD_PARK_TRIGGER, 0}, + {R_DRIVE_CURRENT, 0x298}, + {G_DRIVE_CURRENT, 0x298}, + {B_DRIVE_CURRENT, 0x298}, + {RGB_DRIVER_ENABLE, 7}, + {SEQ_CONTROL, 0}, + {ACTGEN_CONTROL, 0x10}, + {SEQUENCE_MODE, SEQ_LOCK}, + {DATA_FORMAT, RGB888}, + {INPUT_RESOLUTION, WVGA_864_LANDSCAPE}, + {INPUT_SOURCE, PARALLEL_RGB}, + {CPU_IF_SYNC_METHOD, 1}, + {SEQ_CONTROL, 1} + }; + + r = picodlp_i2c_write_array(client, init_cmd_set1, + ARRAY_SIZE(init_cmd_set1)); + if (r) + return r; + + r = picodlp_wait_for_dma_done(client); + if (r) + return r; + + r = picodlp_i2c_write_array(client, init_cmd_set2, + ARRAY_SIZE(init_cmd_set2)); + if (r) + return r; + + r = picodlp_wait_for_dma_done(client); + if (r) + return r; + + r = picodlp_i2c_write_array(client, init_cmd_set3, + ARRAY_SIZE(init_cmd_set3)); + if (r) + return r; + + r = picodlp_wait_for_dma_done(client); + if (r) + return r; + + r = picodlp_i2c_write_array(client, init_cmd_set4, + ARRAY_SIZE(init_cmd_set4)); + if (r) + return r; + + return 0; +} + +static int picodlp_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct picodlp_i2c_data *picodlp_i2c_data; + + picodlp_i2c_data = kzalloc(sizeof(struct picodlp_i2c_data), GFP_KERNEL); + + if (!picodlp_i2c_data) + return -ENOMEM; + + mutex_init(&picodlp_i2c_data->xfer_lock); + i2c_set_clientdata(client, picodlp_i2c_data); + + return 0; +} + +static int picodlp_i2c_remove(struct i2c_client *client) +{ + struct picodlp_i2c_data *picodlp_i2c_data = + i2c_get_clientdata(client); + kfree(picodlp_i2c_data); + return 0; +} + +static struct i2c_driver picodlp_i2c_driver = { + .driver = { + .name = "picodlp_i2c_driver", + }, + .probe = picodlp_i2c_probe, + .remove = picodlp_i2c_remove, + .id_table = picodlp_i2c_id, +}; + +static int picodlp_panel_power_on(struct omap_dss_device *dssdev) +{ + int r, trial = 100; + struct picodlp_data *picod = dev_get_drvdata(&dssdev->dev); + struct picodlp_panel_data *picodlp_pdata = get_panel_data(dssdev); + + if (dssdev->platform_enable) { + r = dssdev->platform_enable(dssdev); + if (r) + return r; + } + + gpio_set_value(picodlp_pdata->pwrgood_gpio, 0); + msleep(1); + gpio_set_value(picodlp_pdata->pwrgood_gpio, 1); + + while (!gpio_get_value(picodlp_pdata->emu_done_gpio)) { + if (!trial--) { + dev_err(&dssdev->dev, "emu_done signal not" + " going high\n"); + return -ETIMEDOUT; + } + msleep(5); + } + /* + * As per dpp2600 programming guide, + * it is required to sleep for 1000ms after emu_done signal goes high + * then only i2c commands can be successfully sent to dpp2600 + */ + msleep(1000); + r = omapdss_dpi_display_enable(dssdev); + if (r) { + dev_err(&dssdev->dev, "failed to enable DPI\n"); + goto err1; + } + + r = picodlp_i2c_init(picod->picodlp_i2c_client); + if (r) + goto err; + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + return r; +err: + omapdss_dpi_display_disable(dssdev); +err1: + if (dssdev->platform_disable) + dssdev->platform_disable(dssdev); + + return r; +} + +static void picodlp_panel_power_off(struct omap_dss_device *dssdev) +{ + struct picodlp_panel_data *picodlp_pdata = get_panel_data(dssdev); + + omapdss_dpi_display_disable(dssdev); + + gpio_set_value(picodlp_pdata->emu_done_gpio, 0); + gpio_set_value(picodlp_pdata->pwrgood_gpio, 0); + + if (dssdev->platform_disable) + dssdev->platform_disable(dssdev); +} + +static int picodlp_panel_probe(struct omap_dss_device *dssdev) +{ + struct picodlp_data *picod; + struct picodlp_panel_data *picodlp_pdata = get_panel_data(dssdev); + struct i2c_adapter *adapter; + struct i2c_client *picodlp_i2c_client; + int r = 0, picodlp_adapter_id; + + dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_ONOFF | + OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IVS; + dssdev->panel.acb = 0x0; + dssdev->panel.timings = pico_ls_timings; + + picod = kzalloc(sizeof(struct picodlp_data), GFP_KERNEL); + if (!picod) + return -ENOMEM; + + mutex_init(&picod->lock); + + picodlp_adapter_id = picodlp_pdata->picodlp_adapter_id; + + adapter = i2c_get_adapter(picodlp_adapter_id); + if (!adapter) { + dev_err(&dssdev->dev, "can't get i2c adapter\n"); + r = -ENODEV; + goto err; + } + + picodlp_i2c_client = i2c_new_device(adapter, &picodlp_i2c_board_info); + if (!picodlp_i2c_client) { + dev_err(&dssdev->dev, "can't add i2c device::" + " picodlp_i2c_client is NULL\n"); + r = -ENODEV; + goto err; + } + + picod->picodlp_i2c_client = picodlp_i2c_client; + + dev_set_drvdata(&dssdev->dev, picod); + return r; +err: + kfree(picod); + return r; +} + +static void picodlp_panel_remove(struct omap_dss_device *dssdev) +{ + struct picodlp_data *picod = dev_get_drvdata(&dssdev->dev); + + i2c_unregister_device(picod->picodlp_i2c_client); + dev_set_drvdata(&dssdev->dev, NULL); + dev_dbg(&dssdev->dev, "removing picodlp panel\n"); + + kfree(picod); +} + +static int picodlp_panel_enable(struct omap_dss_device *dssdev) +{ + struct picodlp_data *picod = dev_get_drvdata(&dssdev->dev); + int r; + + dev_dbg(&dssdev->dev, "enabling picodlp panel\n"); + + mutex_lock(&picod->lock); + if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) { + mutex_unlock(&picod->lock); + return -EINVAL; + } + + r = picodlp_panel_power_on(dssdev); + mutex_unlock(&picod->lock); + + return r; +} + +static void picodlp_panel_disable(struct omap_dss_device *dssdev) +{ + struct picodlp_data *picod = dev_get_drvdata(&dssdev->dev); + + mutex_lock(&picod->lock); + /* Turn off DLP Power */ + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) + picodlp_panel_power_off(dssdev); + + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; + mutex_unlock(&picod->lock); + + dev_dbg(&dssdev->dev, "disabling picodlp panel\n"); +} + +static int picodlp_panel_suspend(struct omap_dss_device *dssdev) +{ + struct picodlp_data *picod = dev_get_drvdata(&dssdev->dev); + + mutex_lock(&picod->lock); + /* Turn off DLP Power */ + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { + mutex_unlock(&picod->lock); + dev_err(&dssdev->dev, "unable to suspend picodlp panel," + " panel is not ACTIVE\n"); + return -EINVAL; + } + + picodlp_panel_power_off(dssdev); + + dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; + mutex_unlock(&picod->lock); + + dev_dbg(&dssdev->dev, "suspending picodlp panel\n"); + return 0; +} + +static int picodlp_panel_resume(struct omap_dss_device *dssdev) +{ + struct picodlp_data *picod = dev_get_drvdata(&dssdev->dev); + int r; + + mutex_lock(&picod->lock); + if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) { + mutex_unlock(&picod->lock); + dev_err(&dssdev->dev, "unable to resume picodlp panel," + " panel is not ACTIVE\n"); + return -EINVAL; + } + + r = picodlp_panel_power_on(dssdev); + mutex_unlock(&picod->lock); + dev_dbg(&dssdev->dev, "resuming picodlp panel\n"); + return r; +} + +static void picodlp_get_resolution(struct omap_dss_device *dssdev, + u16 *xres, u16 *yres) +{ + *xres = dssdev->panel.timings.x_res; + *yres = dssdev->panel.timings.y_res; +} + +static struct omap_dss_driver picodlp_driver = { + .probe = picodlp_panel_probe, + .remove = picodlp_panel_remove, + + .enable = picodlp_panel_enable, + .disable = picodlp_panel_disable, + + .get_resolution = picodlp_get_resolution, + + .suspend = picodlp_panel_suspend, + .resume = picodlp_panel_resume, + + .driver = { + .name = "picodlp_panel", + .owner = THIS_MODULE, + }, +}; + +static int __init picodlp_init(void) +{ + int r = 0; + + r = i2c_add_driver(&picodlp_i2c_driver); + if (r) { + printk(KERN_WARNING "picodlp_i2c_driver" \ + " registration failed\n"); + return r; + } + + r = omap_dss_register_driver(&picodlp_driver); + if (r) + i2c_del_driver(&picodlp_i2c_driver); + + return r; +} + +static void __exit picodlp_exit(void) +{ + i2c_del_driver(&picodlp_i2c_driver); + omap_dss_unregister_driver(&picodlp_driver); +} + +module_init(picodlp_init); +module_exit(picodlp_exit); + +MODULE_AUTHOR("Mythri P K <mythripk@ti.com>"); +MODULE_DESCRIPTION("picodlp driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays/panel-picodlp.h b/drivers/video/omap2/displays/panel-picodlp.h new file mode 100644 index 000000000000..a34b431a7267 --- /dev/null +++ b/drivers/video/omap2/displays/panel-picodlp.h @@ -0,0 +1,288 @@ +/* + * Header file required by picodlp panel driver + * + * Copyright (C) 2009-2011 Texas Instruments + * Author: Mythri P K <mythripk@ti.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __OMAP2_DISPLAY_PANEL_PICODLP_H +#define __OMAP2_DISPLAY_PANEL_PICODLP_H + +/* Commands used for configuring picodlp panel */ + +#define MAIN_STATUS 0x03 +#define PBC_CONTROL 0x08 +#define INPUT_SOURCE 0x0B +#define INPUT_RESOLUTION 0x0C +#define DATA_FORMAT 0x0D +#define IMG_ROTATION 0x0E +#define LONG_FLIP 0x0F +#define SHORT_FLIP 0x10 +#define TEST_PAT_SELECT 0x11 +#define R_DRIVE_CURRENT 0x12 +#define G_DRIVE_CURRENT 0x13 +#define B_DRIVE_CURRENT 0x14 +#define READ_REG_SELECT 0x15 +#define RGB_DRIVER_ENABLE 0x16 + +#define CPU_IF_MODE 0x18 +#define FRAME_RATE 0x19 +#define CPU_IF_SYNC_METHOD 0x1A +#define CPU_IF_SOF 0x1B +#define CPU_IF_EOF 0x1C +#define CPU_IF_SLEEP 0x1D + +#define SEQUENCE_MODE 0x1E +#define SOFT_RESET 0x1F +#define FRONT_END_RESET 0x21 +#define AUTO_PWR_ENABLE 0x22 + +#define VSYNC_LINE_DELAY 0x23 +#define CPU_PI_HORIZ_START 0x24 +#define CPU_PI_VERT_START 0x25 +#define CPU_PI_HORIZ_WIDTH 0x26 +#define CPU_PI_VERT_HEIGHT 0x27 + +#define PIXEL_MASK_CROP 0x28 +#define CROP_FIRST_LINE 0x29 +#define CROP_LAST_LINE 0x2A +#define CROP_FIRST_PIXEL 0x2B +#define CROP_LAST_PIXEL 0x2C +#define DMD_PARK_TRIGGER 0x2D + +#define MISC_REG 0x30 + +/* AGC registers */ +#define AGC_CTRL 0x50 +#define AGC_CLIPPED_PIXS 0x55 +#define AGC_BRIGHT_PIXS 0x56 +#define AGC_BG_PIXS 0x57 +#define AGC_SAFETY_MARGIN 0x17 + +/* Color Coordinate Adjustment registers */ +#define CCA_ENABLE 0x5E +#define CCA_C1A 0x5F +#define CCA_C1B 0x60 +#define CCA_C1C 0x61 +#define CCA_C2A 0x62 +#define CCA_C2B 0x63 +#define CCA_C2C 0x64 +#define CCA_C3A 0x65 +#define CCA_C3B 0x66 +#define CCA_C3C 0x67 +#define CCA_C7A 0x71 +#define CCA_C7B 0x72 +#define CCA_C7C 0x73 + +/** + * DLP Pico Processor 2600 comes with flash + * We can do DMA operations from flash for accessing Look Up Tables + */ +#define DMA_STATUS 0x100 +#define FLASH_ADDR_BYTES 0x74 +#define FLASH_DUMMY_BYTES 0x75 +#define FLASH_WRITE_BYTES 0x76 +#define FLASH_READ_BYTES 0x77 +#define FLASH_OPCODE 0x78 +#define FLASH_START_ADDR 0x79 +#define FLASH_DUMMY2 0x7A +#define FLASH_WRITE_DATA 0x7B + +#define TEMPORAL_DITH_DISABLE 0x7E +#define SEQ_CONTROL 0x82 +#define SEQ_VECTOR 0x83 + +/* DMD is Digital Micromirror Device */ +#define DMD_BLOCK_COUNT 0x84 +#define DMD_VCC_CONTROL 0x86 +#define DMD_PARK_PULSE_COUNT 0x87 +#define DMD_PARK_PULSE_WIDTH 0x88 +#define DMD_PARK_DELAY 0x89 +#define DMD_SHADOW_ENABLE 0x8E +#define SEQ_STATUS 0x8F +#define FLASH_CLOCK_CONTROL 0x98 +#define DMD_PARK 0x2D + +#define SDRAM_BIST_ENABLE 0x46 +#define DDR_DRIVER_STRENGTH 0x9A +#define SDC_ENABLE 0x9D +#define SDC_BUFF_SWAP_DISABLE 0xA3 +#define CURTAIN_CONTROL 0xA6 +#define DDR_BUS_SWAP_ENABLE 0xA7 +#define DMD_TRC_ENABLE 0xA8 +#define DMD_BUS_SWAP_ENABLE 0xA9 + +#define ACTGEN_ENABLE 0xAE +#define ACTGEN_CONTROL 0xAF +#define ACTGEN_HORIZ_BP 0xB0 +#define ACTGEN_VERT_BP 0xB1 + +/* Look Up Table access */ +#define CMT_SPLASH_LUT_START_ADDR 0xFA +#define CMT_SPLASH_LUT_DEST_SELECT 0xFB +#define CMT_SPLASH_LUT_DATA 0xFC +#define SEQ_RESET_LUT_START_ADDR 0xFD +#define SEQ_RESET_LUT_DEST_SELECT 0xFE +#define SEQ_RESET_LUT_DATA 0xFF + +/* Input source definitions */ +#define PARALLEL_RGB 0 +#define INT_TEST_PATTERN 1 +#define SPLASH_SCREEN 2 +#define CPU_INTF 3 +#define BT656 4 + +/* Standard input resolution definitions */ +#define QWVGA_LANDSCAPE 3 /* (427h*240v) */ +#define WVGA_864_LANDSCAPE 21 /* (864h*480v) */ +#define WVGA_DMD_OPTICAL_TEST 35 /* (608h*684v) */ + +/* Standard data format definitions */ +#define RGB565 0 +#define RGB666 1 +#define RGB888 2 + +/* Test Pattern definitions */ +#define TPG_CHECKERBOARD 0 +#define TPG_BLACK 1 +#define TPG_WHITE 2 +#define TPG_RED 3 +#define TPG_BLUE 4 +#define TPG_GREEN 5 +#define TPG_VLINES_BLACK 6 +#define TPG_HLINES_BLACK 7 +#define TPG_VLINES_ALT 8 +#define TPG_HLINES_ALT 9 +#define TPG_DIAG_LINES 10 +#define TPG_GREYRAMP_VERT 11 +#define TPG_GREYRAMP_HORIZ 12 +#define TPG_ANSI_CHECKERBOARD 13 + +/* sequence mode definitions */ +#define SEQ_FREE_RUN 0 +#define SEQ_LOCK 1 + +/* curtain color definitions */ +#define CURTAIN_BLACK 0 +#define CURTAIN_RED 1 +#define CURTAIN_GREEN 2 +#define CURTAIN_BLUE 3 +#define CURTAIN_YELLOW 4 +#define CURTAIN_MAGENTA 5 +#define CURTAIN_CYAN 6 +#define CURTAIN_WHITE 7 + +/* LUT definitions */ +#define CMT_LUT_NONE 0 +#define CMT_LUT_GREEN 1 +#define CMT_LUT_RED 2 +#define CMT_LUT_BLUE 3 +#define CMT_LUT_ALL 4 +#define SPLASH_LUT 5 + +#define SEQ_LUT_NONE 0 +#define SEQ_DRC_LUT_0 1 +#define SEQ_DRC_LUT_1 2 +#define SEQ_DRC_LUT_2 3 +#define SEQ_DRC_LUT_3 4 +#define SEQ_SEQ_LUT 5 +#define SEQ_DRC_LUT_ALL 6 +#define WPC_PROGRAM_LUT 7 + +#define BITSTREAM_START_ADDR 0x00000000 +#define BITSTREAM_SIZE 0x00040000 + +#define WPC_FW_0_START_ADDR 0x00040000 +#define WPC_FW_0_SIZE 0x00000ce8 + +#define SEQUENCE_0_START_ADDR 0x00044000 +#define SEQUENCE_0_SIZE 0x00001000 + +#define SEQUENCE_1_START_ADDR 0x00045000 +#define SEQUENCE_1_SIZE 0x00000d10 + +#define SEQUENCE_2_START_ADDR 0x00046000 +#define SEQUENCE_2_SIZE 0x00000d10 + +#define SEQUENCE_3_START_ADDR 0x00047000 +#define SEQUENCE_3_SIZE 0x00000d10 + +#define SEQUENCE_4_START_ADDR 0x00048000 +#define SEQUENCE_4_SIZE 0x00000d10 + +#define SEQUENCE_5_START_ADDR 0x00049000 +#define SEQUENCE_5_SIZE 0x00000d10 + +#define SEQUENCE_6_START_ADDR 0x0004a000 +#define SEQUENCE_6_SIZE 0x00000d10 + +#define CMT_LUT_0_START_ADDR 0x0004b200 +#define CMT_LUT_0_SIZE 0x00000600 + +#define CMT_LUT_1_START_ADDR 0x0004b800 +#define CMT_LUT_1_SIZE 0x00000600 + +#define CMT_LUT_2_START_ADDR 0x0004be00 +#define CMT_LUT_2_SIZE 0x00000600 + +#define CMT_LUT_3_START_ADDR 0x0004c400 +#define CMT_LUT_3_SIZE 0x00000600 + +#define CMT_LUT_4_START_ADDR 0x0004ca00 +#define CMT_LUT_4_SIZE 0x00000600 + +#define CMT_LUT_5_START_ADDR 0x0004d000 +#define CMT_LUT_5_SIZE 0x00000600 + +#define CMT_LUT_6_START_ADDR 0x0004d600 +#define CMT_LUT_6_SIZE 0x00000600 + +#define DRC_TABLE_0_START_ADDR 0x0004dc00 +#define DRC_TABLE_0_SIZE 0x00000100 + +#define SPLASH_0_START_ADDR 0x0004dd00 +#define SPLASH_0_SIZE 0x00032280 + +#define SEQUENCE_7_START_ADDR 0x00080000 +#define SEQUENCE_7_SIZE 0x00000d10 + +#define SEQUENCE_8_START_ADDR 0x00081800 +#define SEQUENCE_8_SIZE 0x00000d10 + +#define SEQUENCE_9_START_ADDR 0x00083000 +#define SEQUENCE_9_SIZE 0x00000d10 + +#define CMT_LUT_7_START_ADDR 0x0008e000 +#define CMT_LUT_7_SIZE 0x00000600 + +#define CMT_LUT_8_START_ADDR 0x0008e800 +#define CMT_LUT_8_SIZE 0x00000600 + +#define CMT_LUT_9_START_ADDR 0x0008f000 +#define CMT_LUT_9_SIZE 0x00000600 + +#define SPLASH_1_START_ADDR 0x0009a000 +#define SPLASH_1_SIZE 0x00032280 + +#define SPLASH_2_START_ADDR 0x000cd000 +#define SPLASH_2_SIZE 0x00032280 + +#define SPLASH_3_START_ADDR 0x00100000 +#define SPLASH_3_SIZE 0x00032280 + +#define OPT_SPLASH_0_START_ADDR 0x00134000 +#define OPT_SPLASH_0_SIZE 0x000cb100 + +#endif diff --git a/drivers/video/omap2/displays/panel-taal.c b/drivers/video/omap2/displays/panel-taal.c index 4e888ac09b3f..80c3f6ab1a94 100644 --- a/drivers/video/omap2/displays/panel-taal.c +++ b/drivers/video/omap2/displays/panel-taal.c @@ -35,26 +35,12 @@ #include <video/omapdss.h> #include <video/omap-panel-nokia-dsi.h> +#include <video/mipi_display.h> /* DSI Virtual channel. Hardcoded for now. */ #define TCH 0 #define DCS_READ_NUM_ERRORS 0x05 -#define DCS_READ_POWER_MODE 0x0a -#define DCS_READ_MADCTL 0x0b -#define DCS_READ_PIXEL_FORMAT 0x0c -#define DCS_RDDSDR 0x0f -#define DCS_SLEEP_IN 0x10 -#define DCS_SLEEP_OUT 0x11 -#define DCS_DISPLAY_OFF 0x28 -#define DCS_DISPLAY_ON 0x29 -#define DCS_COLUMN_ADDR 0x2a -#define DCS_PAGE_ADDR 0x2b -#define DCS_MEMORY_WRITE 0x2c -#define DCS_TEAR_OFF 0x34 -#define DCS_TEAR_ON 0x35 -#define DCS_MEM_ACC_CTRL 0x36 -#define DCS_PIXEL_FORMAT 0x3a #define DCS_BRIGHTNESS 0x51 #define DCS_CTRL_DISPLAY 0x53 #define DCS_WRITE_CABC 0x55 @@ -222,8 +208,6 @@ struct taal_data { struct delayed_work te_timeout_work; - bool use_dsi_bl; - bool cabc_broken; unsigned cabc_mode; @@ -302,7 +286,7 @@ static int taal_sleep_in(struct taal_data *td) hw_guard_wait(td); - cmd = DCS_SLEEP_IN; + cmd = MIPI_DCS_ENTER_SLEEP_MODE; r = dsi_vc_dcs_write_nosync(td->dssdev, td->channel, &cmd, 1); if (r) return r; @@ -321,7 +305,7 @@ static int taal_sleep_out(struct taal_data *td) hw_guard_wait(td); - r = taal_dcs_write_0(td, DCS_SLEEP_OUT); + r = taal_dcs_write_0(td, MIPI_DCS_EXIT_SLEEP_MODE); if (r) return r; @@ -356,7 +340,7 @@ static int taal_set_addr_mode(struct taal_data *td, u8 rotate, bool mirror) u8 mode; int b5, b6, b7; - r = taal_dcs_read_1(td, DCS_READ_MADCTL, &mode); + r = taal_dcs_read_1(td, MIPI_DCS_GET_ADDRESS_MODE, &mode); if (r) return r; @@ -390,7 +374,7 @@ static int taal_set_addr_mode(struct taal_data *td, u8 rotate, bool mirror) mode &= ~((1<<7) | (1<<6) | (1<<5)); mode |= (b7 << 7) | (b6 << 6) | (b5 << 5); - return taal_dcs_write_1(td, DCS_MEM_ACC_CTRL, mode); + return taal_dcs_write_1(td, MIPI_DCS_SET_ADDRESS_MODE, mode); } static int taal_set_update_window(struct taal_data *td, @@ -403,7 +387,7 @@ static int taal_set_update_window(struct taal_data *td, u16 y2 = y + h - 1; u8 buf[5]; - buf[0] = DCS_COLUMN_ADDR; + buf[0] = MIPI_DCS_SET_COLUMN_ADDRESS; buf[1] = (x1 >> 8) & 0xff; buf[2] = (x1 >> 0) & 0xff; buf[3] = (x2 >> 8) & 0xff; @@ -413,7 +397,7 @@ static int taal_set_update_window(struct taal_data *td, if (r) return r; - buf[0] = DCS_PAGE_ADDR; + buf[0] = MIPI_DCS_SET_PAGE_ADDRESS; buf[1] = (y1 >> 8) & 0xff; buf[2] = (y1 >> 0) & 0xff; buf[3] = (y2 >> 8) & 0xff; @@ -555,7 +539,6 @@ static int taal_bl_update_status(struct backlight_device *dev) { struct omap_dss_device *dssdev = dev_get_drvdata(&dev->dev); struct taal_data *td = dev_get_drvdata(&dssdev->dev); - struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev); int r; int level; @@ -569,23 +552,16 @@ static int taal_bl_update_status(struct backlight_device *dev) mutex_lock(&td->lock); - if (td->use_dsi_bl) { - if (td->enabled) { - dsi_bus_lock(dssdev); + if (td->enabled) { + dsi_bus_lock(dssdev); - r = taal_wake_up(dssdev); - if (!r) - r = taal_dcs_write_1(td, DCS_BRIGHTNESS, level); + r = taal_wake_up(dssdev); + if (!r) + r = taal_dcs_write_1(td, DCS_BRIGHTNESS, level); - dsi_bus_unlock(dssdev); - } else { - r = 0; - } + dsi_bus_unlock(dssdev); } else { - if (!panel_data->set_backlight) - r = -EINVAL; - else - r = panel_data->set_backlight(dssdev, level); + r = 0; } mutex_unlock(&td->lock); @@ -964,7 +940,7 @@ static int taal_probe(struct omap_dss_device *dssdev) { struct backlight_properties props; struct taal_data *td; - struct backlight_device *bldev; + struct backlight_device *bldev = NULL; struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev); struct panel_config *panel_config = NULL; int r, i; @@ -990,7 +966,7 @@ static int taal_probe(struct omap_dss_device *dssdev) dssdev->panel.config = OMAP_DSS_LCD_TFT; dssdev->panel.timings = panel_config->timings; - dssdev->ctrl.pixel_size = 24; + dssdev->panel.dsi_pix_fmt = OMAP_DSS_DSI_FMT_RGB888; td = kzalloc(sizeof(*td), GFP_KERNEL); if (!td) { @@ -1025,35 +1001,26 @@ static int taal_probe(struct omap_dss_device *dssdev) taal_hw_reset(dssdev); - /* if no platform set_backlight() defined, presume DSI backlight - * control */ - memset(&props, 0, sizeof(struct backlight_properties)); - if (!panel_data->set_backlight) - td->use_dsi_bl = true; - - if (td->use_dsi_bl) + if (panel_data->use_dsi_backlight) { + memset(&props, 0, sizeof(struct backlight_properties)); props.max_brightness = 255; - else - props.max_brightness = 127; - - props.type = BACKLIGHT_RAW; - bldev = backlight_device_register(dev_name(&dssdev->dev), &dssdev->dev, - dssdev, &taal_bl_ops, &props); - if (IS_ERR(bldev)) { - r = PTR_ERR(bldev); - goto err_bl; - } - td->bldev = bldev; + props.type = BACKLIGHT_RAW; + bldev = backlight_device_register(dev_name(&dssdev->dev), + &dssdev->dev, dssdev, &taal_bl_ops, &props); + if (IS_ERR(bldev)) { + r = PTR_ERR(bldev); + goto err_bl; + } + + td->bldev = bldev; - bldev->props.fb_blank = FB_BLANK_UNBLANK; - bldev->props.power = FB_BLANK_UNBLANK; - if (td->use_dsi_bl) + bldev->props.fb_blank = FB_BLANK_UNBLANK; + bldev->props.power = FB_BLANK_UNBLANK; bldev->props.brightness = 255; - else - bldev->props.brightness = 127; - taal_bl_update_status(bldev); + taal_bl_update_status(bldev); + } if (panel_data->use_ext_te) { int gpio = panel_data->ext_te_gpio; @@ -1067,7 +1034,7 @@ static int taal_probe(struct omap_dss_device *dssdev) gpio_direction_input(gpio); r = request_irq(gpio_to_irq(gpio), taal_te_isr, - IRQF_DISABLED | IRQF_TRIGGER_RISING, + IRQF_TRIGGER_RISING, "taal vsync", dssdev); if (r) { @@ -1111,7 +1078,8 @@ err_irq: if (panel_data->use_ext_te) gpio_free(panel_data->ext_te_gpio); err_gpio: - backlight_device_unregister(bldev); + if (bldev != NULL) + backlight_device_unregister(bldev); err_bl: destroy_workqueue(td->workqueue); err_wq: @@ -1140,9 +1108,11 @@ static void __exit taal_remove(struct omap_dss_device *dssdev) } bldev = td->bldev; - bldev->props.power = FB_BLANK_POWERDOWN; - taal_bl_update_status(bldev); - backlight_device_unregister(bldev); + if (bldev != NULL) { + bldev->props.power = FB_BLANK_POWERDOWN; + taal_bl_update_status(bldev); + backlight_device_unregister(bldev); + } taal_cancel_ulps_work(dssdev); taal_cancel_esd_work(dssdev); @@ -1195,7 +1165,8 @@ static int taal_power_on(struct omap_dss_device *dssdev) if (r) goto err; - r = taal_dcs_write_1(td, DCS_PIXEL_FORMAT, 0x7); /* 24bit/pixel */ + r = taal_dcs_write_1(td, MIPI_DCS_SET_PIXEL_FORMAT, + MIPI_DCS_PIXEL_FMT_24BIT); if (r) goto err; @@ -1209,7 +1180,7 @@ static int taal_power_on(struct omap_dss_device *dssdev) goto err; } - r = taal_dcs_write_0(td, DCS_DISPLAY_ON); + r = taal_dcs_write_0(td, MIPI_DCS_SET_DISPLAY_ON); if (r) goto err; @@ -1246,7 +1217,7 @@ static void taal_power_off(struct omap_dss_device *dssdev) struct taal_data *td = dev_get_drvdata(&dssdev->dev); int r; - r = taal_dcs_write_0(td, DCS_DISPLAY_OFF); + r = taal_dcs_write_0(td, MIPI_DCS_SET_DISPLAY_OFF); if (!r) r = taal_sleep_in(td); @@ -1529,9 +1500,9 @@ static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable) int r; if (enable) - r = taal_dcs_write_1(td, DCS_TEAR_ON, 0); + r = taal_dcs_write_1(td, MIPI_DCS_SET_TEAR_ON, 0); else - r = taal_dcs_write_0(td, DCS_TEAR_OFF); + r = taal_dcs_write_0(td, MIPI_DCS_SET_TEAR_OFF); if (!panel_data->use_ext_te) omapdss_dsi_enable_te(dssdev, enable); @@ -1851,7 +1822,7 @@ static void taal_esd_work(struct work_struct *work) goto err; } - r = taal_dcs_read_1(td, DCS_RDDSDR, &state1); + r = taal_dcs_read_1(td, MIPI_DCS_GET_DIAGNOSTIC_RESULT, &state1); if (r) { dev_err(&dssdev->dev, "failed to read Taal status\n"); goto err; @@ -1864,7 +1835,7 @@ static void taal_esd_work(struct work_struct *work) goto err; } - r = taal_dcs_read_1(td, DCS_RDDSDR, &state2); + r = taal_dcs_read_1(td, MIPI_DCS_GET_DIAGNOSTIC_RESULT, &state2); if (r) { dev_err(&dssdev->dev, "failed to read Taal status\n"); goto err; @@ -1880,7 +1851,7 @@ static void taal_esd_work(struct work_struct *work) /* Self-diagnostics result is also shown on TE GPIO line. We need * to re-enable TE after self diagnostics */ if (td->te_enabled && panel_data->use_ext_te) { - r = taal_dcs_write_1(td, DCS_TEAR_ON, 0); + r = taal_dcs_write_1(td, MIPI_DCS_SET_TEAR_ON, 0); if (r) goto err; } diff --git a/drivers/video/omap2/dss/Kconfig b/drivers/video/omap2/dss/Kconfig index 0d12524db14b..7be7c06a249e 100644 --- a/drivers/video/omap2/dss/Kconfig +++ b/drivers/video/omap2/dss/Kconfig @@ -1,5 +1,5 @@ menuconfig OMAP2_DSS - tristate "OMAP2+ Display Subsystem support (EXPERIMENTAL)" + tristate "OMAP2+ Display Subsystem support" depends on ARCH_OMAP2PLUS help OMAP2+ Display Subsystem support. diff --git a/drivers/video/omap2/dss/Makefile b/drivers/video/omap2/dss/Makefile index 10d9d3bb3e24..bd34ac5b2026 100644 --- a/drivers/video/omap2/dss/Makefile +++ b/drivers/video/omap2/dss/Makefile @@ -6,4 +6,4 @@ omapdss-$(CONFIG_OMAP2_DSS_VENC) += venc.o omapdss-$(CONFIG_OMAP2_DSS_SDI) += sdi.o omapdss-$(CONFIG_OMAP2_DSS_DSI) += dsi.o omapdss-$(CONFIG_OMAP4_DSS_HDMI) += hdmi.o \ - hdmi_omap4_panel.o + hdmi_panel.o ti_hdmi_4xxx_ip.o diff --git a/drivers/video/omap2/dss/core.c b/drivers/video/omap2/dss/core.c index 76821fefce9a..86ec12e16c7c 100644 --- a/drivers/video/omap2/dss/core.c +++ b/drivers/video/omap2/dss/core.c @@ -145,6 +145,10 @@ static int dss_initialize_debugfs(void) debugfs_create_file("venc", S_IRUGO, dss_debugfs_dir, &venc_dump_regs, &dss_debug_fops); #endif +#ifdef CONFIG_OMAP4_DSS_HDMI + debugfs_create_file("hdmi", S_IRUGO, dss_debugfs_dir, + &hdmi_dump_regs, &dss_debug_fops); +#endif return 0; } diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c index 0f3961a1ce26..6892cfd2e3b7 100644 --- a/drivers/video/omap2/dss/dispc.c +++ b/drivers/video/omap2/dss/dispc.c @@ -106,7 +106,7 @@ static struct { int irq; struct clk *dss_clk; - u32 fifo_size[3]; + u32 fifo_size[MAX_DSS_OVERLAYS]; spinlock_t irq_lock; u32 irq_error_mask; @@ -171,172 +171,98 @@ static int dispc_get_ctx_loss_count(void) static void dispc_save_context(void) { - int i; + int i, j; DSSDBG("dispc_save_context\n"); SR(IRQENABLE); SR(CONTROL); SR(CONFIG); - SR(DEFAULT_COLOR(OMAP_DSS_CHANNEL_LCD)); - SR(DEFAULT_COLOR(OMAP_DSS_CHANNEL_DIGIT)); - SR(TRANS_COLOR(OMAP_DSS_CHANNEL_LCD)); - SR(TRANS_COLOR(OMAP_DSS_CHANNEL_DIGIT)); SR(LINE_NUMBER); - SR(TIMING_H(OMAP_DSS_CHANNEL_LCD)); - SR(TIMING_V(OMAP_DSS_CHANNEL_LCD)); - SR(POL_FREQ(OMAP_DSS_CHANNEL_LCD)); - SR(DIVISORo(OMAP_DSS_CHANNEL_LCD)); - if (dss_has_feature(FEAT_GLOBAL_ALPHA)) + if (dss_has_feature(FEAT_ALPHA_FIXED_ZORDER) || + dss_has_feature(FEAT_ALPHA_FREE_ZORDER)) SR(GLOBAL_ALPHA); - SR(SIZE_MGR(OMAP_DSS_CHANNEL_DIGIT)); - SR(SIZE_MGR(OMAP_DSS_CHANNEL_LCD)); if (dss_has_feature(FEAT_MGR_LCD2)) { SR(CONTROL2); - SR(DEFAULT_COLOR(OMAP_DSS_CHANNEL_LCD2)); - SR(TRANS_COLOR(OMAP_DSS_CHANNEL_LCD2)); - SR(SIZE_MGR(OMAP_DSS_CHANNEL_LCD2)); - SR(TIMING_H(OMAP_DSS_CHANNEL_LCD2)); - SR(TIMING_V(OMAP_DSS_CHANNEL_LCD2)); - SR(POL_FREQ(OMAP_DSS_CHANNEL_LCD2)); - SR(DIVISORo(OMAP_DSS_CHANNEL_LCD2)); SR(CONFIG2); } - SR(OVL_BA0(OMAP_DSS_GFX)); - SR(OVL_BA1(OMAP_DSS_GFX)); - SR(OVL_POSITION(OMAP_DSS_GFX)); - SR(OVL_SIZE(OMAP_DSS_GFX)); - SR(OVL_ATTRIBUTES(OMAP_DSS_GFX)); - SR(OVL_FIFO_THRESHOLD(OMAP_DSS_GFX)); - SR(OVL_ROW_INC(OMAP_DSS_GFX)); - SR(OVL_PIXEL_INC(OMAP_DSS_GFX)); - SR(OVL_WINDOW_SKIP(OMAP_DSS_GFX)); - SR(OVL_TABLE_BA(OMAP_DSS_GFX)); + for (i = 0; i < dss_feat_get_num_mgrs(); i++) { + SR(DEFAULT_COLOR(i)); + SR(TRANS_COLOR(i)); + SR(SIZE_MGR(i)); + if (i == OMAP_DSS_CHANNEL_DIGIT) + continue; + SR(TIMING_H(i)); + SR(TIMING_V(i)); + SR(POL_FREQ(i)); + SR(DIVISORo(i)); - SR(DATA_CYCLE1(OMAP_DSS_CHANNEL_LCD)); - SR(DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD)); - SR(DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD)); + SR(DATA_CYCLE1(i)); + SR(DATA_CYCLE2(i)); + SR(DATA_CYCLE3(i)); - if (dss_has_feature(FEAT_CPR)) { - SR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD)); - SR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD)); - SR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD)); - } - if (dss_has_feature(FEAT_MGR_LCD2)) { if (dss_has_feature(FEAT_CPR)) { - SR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD2)); - SR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD2)); - SR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD2)); + SR(CPR_COEF_R(i)); + SR(CPR_COEF_G(i)); + SR(CPR_COEF_B(i)); } - - SR(DATA_CYCLE1(OMAP_DSS_CHANNEL_LCD2)); - SR(DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD2)); - SR(DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD2)); } - if (dss_has_feature(FEAT_PRELOAD)) - SR(OVL_PRELOAD(OMAP_DSS_GFX)); - - /* VID1 */ - SR(OVL_BA0(OMAP_DSS_VIDEO1)); - SR(OVL_BA1(OMAP_DSS_VIDEO1)); - SR(OVL_POSITION(OMAP_DSS_VIDEO1)); - SR(OVL_SIZE(OMAP_DSS_VIDEO1)); - SR(OVL_ATTRIBUTES(OMAP_DSS_VIDEO1)); - SR(OVL_FIFO_THRESHOLD(OMAP_DSS_VIDEO1)); - SR(OVL_ROW_INC(OMAP_DSS_VIDEO1)); - SR(OVL_PIXEL_INC(OMAP_DSS_VIDEO1)); - SR(OVL_FIR(OMAP_DSS_VIDEO1)); - SR(OVL_PICTURE_SIZE(OMAP_DSS_VIDEO1)); - SR(OVL_ACCU0(OMAP_DSS_VIDEO1)); - SR(OVL_ACCU1(OMAP_DSS_VIDEO1)); - - for (i = 0; i < 8; i++) - SR(OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, i)); - - for (i = 0; i < 8; i++) - SR(OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, i)); - - for (i = 0; i < 5; i++) - SR(OVL_CONV_COEF(OMAP_DSS_VIDEO1, i)); - - if (dss_has_feature(FEAT_FIR_COEF_V)) { - for (i = 0; i < 8; i++) - SR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, i)); - } - - if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { - SR(OVL_BA0_UV(OMAP_DSS_VIDEO1)); - SR(OVL_BA1_UV(OMAP_DSS_VIDEO1)); - SR(OVL_FIR2(OMAP_DSS_VIDEO1)); - SR(OVL_ACCU2_0(OMAP_DSS_VIDEO1)); - SR(OVL_ACCU2_1(OMAP_DSS_VIDEO1)); - - for (i = 0; i < 8; i++) - SR(OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, i)); - - for (i = 0; i < 8; i++) - SR(OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, i)); - - for (i = 0; i < 8; i++) - SR(OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, i)); - } - if (dss_has_feature(FEAT_ATTR2)) - SR(OVL_ATTRIBUTES2(OMAP_DSS_VIDEO1)); - - if (dss_has_feature(FEAT_PRELOAD)) - SR(OVL_PRELOAD(OMAP_DSS_VIDEO1)); - - /* VID2 */ - SR(OVL_BA0(OMAP_DSS_VIDEO2)); - SR(OVL_BA1(OMAP_DSS_VIDEO2)); - SR(OVL_POSITION(OMAP_DSS_VIDEO2)); - SR(OVL_SIZE(OMAP_DSS_VIDEO2)); - SR(OVL_ATTRIBUTES(OMAP_DSS_VIDEO2)); - SR(OVL_FIFO_THRESHOLD(OMAP_DSS_VIDEO2)); - SR(OVL_ROW_INC(OMAP_DSS_VIDEO2)); - SR(OVL_PIXEL_INC(OMAP_DSS_VIDEO2)); - SR(OVL_FIR(OMAP_DSS_VIDEO2)); - SR(OVL_PICTURE_SIZE(OMAP_DSS_VIDEO2)); - SR(OVL_ACCU0(OMAP_DSS_VIDEO2)); - SR(OVL_ACCU1(OMAP_DSS_VIDEO2)); + for (i = 0; i < dss_feat_get_num_ovls(); i++) { + SR(OVL_BA0(i)); + SR(OVL_BA1(i)); + SR(OVL_POSITION(i)); + SR(OVL_SIZE(i)); + SR(OVL_ATTRIBUTES(i)); + SR(OVL_FIFO_THRESHOLD(i)); + SR(OVL_ROW_INC(i)); + SR(OVL_PIXEL_INC(i)); + if (dss_has_feature(FEAT_PRELOAD)) + SR(OVL_PRELOAD(i)); + if (i == OMAP_DSS_GFX) { + SR(OVL_WINDOW_SKIP(i)); + SR(OVL_TABLE_BA(i)); + continue; + } + SR(OVL_FIR(i)); + SR(OVL_PICTURE_SIZE(i)); + SR(OVL_ACCU0(i)); + SR(OVL_ACCU1(i)); - for (i = 0; i < 8; i++) - SR(OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, i)); + for (j = 0; j < 8; j++) + SR(OVL_FIR_COEF_H(i, j)); - for (i = 0; i < 8; i++) - SR(OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, i)); + for (j = 0; j < 8; j++) + SR(OVL_FIR_COEF_HV(i, j)); - for (i = 0; i < 5; i++) - SR(OVL_CONV_COEF(OMAP_DSS_VIDEO2, i)); + for (j = 0; j < 5; j++) + SR(OVL_CONV_COEF(i, j)); - if (dss_has_feature(FEAT_FIR_COEF_V)) { - for (i = 0; i < 8; i++) - SR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, i)); - } + if (dss_has_feature(FEAT_FIR_COEF_V)) { + for (j = 0; j < 8; j++) + SR(OVL_FIR_COEF_V(i, j)); + } - if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { - SR(OVL_BA0_UV(OMAP_DSS_VIDEO2)); - SR(OVL_BA1_UV(OMAP_DSS_VIDEO2)); - SR(OVL_FIR2(OMAP_DSS_VIDEO2)); - SR(OVL_ACCU2_0(OMAP_DSS_VIDEO2)); - SR(OVL_ACCU2_1(OMAP_DSS_VIDEO2)); + if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { + SR(OVL_BA0_UV(i)); + SR(OVL_BA1_UV(i)); + SR(OVL_FIR2(i)); + SR(OVL_ACCU2_0(i)); + SR(OVL_ACCU2_1(i)); - for (i = 0; i < 8; i++) - SR(OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, i)); + for (j = 0; j < 8; j++) + SR(OVL_FIR_COEF_H2(i, j)); - for (i = 0; i < 8; i++) - SR(OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, i)); + for (j = 0; j < 8; j++) + SR(OVL_FIR_COEF_HV2(i, j)); - for (i = 0; i < 8; i++) - SR(OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, i)); + for (j = 0; j < 8; j++) + SR(OVL_FIR_COEF_V2(i, j)); + } + if (dss_has_feature(FEAT_ATTR2)) + SR(OVL_ATTRIBUTES2(i)); } - if (dss_has_feature(FEAT_ATTR2)) - SR(OVL_ATTRIBUTES2(OMAP_DSS_VIDEO2)); - - if (dss_has_feature(FEAT_PRELOAD)) - SR(OVL_PRELOAD(OMAP_DSS_VIDEO2)); if (dss_has_feature(FEAT_CORE_CLK_DIV)) SR(DIVISOR); @@ -349,7 +275,7 @@ static void dispc_save_context(void) static void dispc_restore_context(void) { - int i, ctx; + int i, j, ctx; DSSDBG("dispc_restore_context\n"); @@ -367,165 +293,89 @@ static void dispc_restore_context(void) /*RR(IRQENABLE);*/ /*RR(CONTROL);*/ RR(CONFIG); - RR(DEFAULT_COLOR(OMAP_DSS_CHANNEL_LCD)); - RR(DEFAULT_COLOR(OMAP_DSS_CHANNEL_DIGIT)); - RR(TRANS_COLOR(OMAP_DSS_CHANNEL_LCD)); - RR(TRANS_COLOR(OMAP_DSS_CHANNEL_DIGIT)); RR(LINE_NUMBER); - RR(TIMING_H(OMAP_DSS_CHANNEL_LCD)); - RR(TIMING_V(OMAP_DSS_CHANNEL_LCD)); - RR(POL_FREQ(OMAP_DSS_CHANNEL_LCD)); - RR(DIVISORo(OMAP_DSS_CHANNEL_LCD)); - if (dss_has_feature(FEAT_GLOBAL_ALPHA)) + if (dss_has_feature(FEAT_ALPHA_FIXED_ZORDER) || + dss_has_feature(FEAT_ALPHA_FREE_ZORDER)) RR(GLOBAL_ALPHA); - RR(SIZE_MGR(OMAP_DSS_CHANNEL_DIGIT)); - RR(SIZE_MGR(OMAP_DSS_CHANNEL_LCD)); - if (dss_has_feature(FEAT_MGR_LCD2)) { - RR(DEFAULT_COLOR(OMAP_DSS_CHANNEL_LCD2)); - RR(TRANS_COLOR(OMAP_DSS_CHANNEL_LCD2)); - RR(SIZE_MGR(OMAP_DSS_CHANNEL_LCD2)); - RR(TIMING_H(OMAP_DSS_CHANNEL_LCD2)); - RR(TIMING_V(OMAP_DSS_CHANNEL_LCD2)); - RR(POL_FREQ(OMAP_DSS_CHANNEL_LCD2)); - RR(DIVISORo(OMAP_DSS_CHANNEL_LCD2)); + if (dss_has_feature(FEAT_MGR_LCD2)) RR(CONFIG2); - } - - RR(OVL_BA0(OMAP_DSS_GFX)); - RR(OVL_BA1(OMAP_DSS_GFX)); - RR(OVL_POSITION(OMAP_DSS_GFX)); - RR(OVL_SIZE(OMAP_DSS_GFX)); - RR(OVL_ATTRIBUTES(OMAP_DSS_GFX)); - RR(OVL_FIFO_THRESHOLD(OMAP_DSS_GFX)); - RR(OVL_ROW_INC(OMAP_DSS_GFX)); - RR(OVL_PIXEL_INC(OMAP_DSS_GFX)); - RR(OVL_WINDOW_SKIP(OMAP_DSS_GFX)); - RR(OVL_TABLE_BA(OMAP_DSS_GFX)); - - RR(DATA_CYCLE1(OMAP_DSS_CHANNEL_LCD)); - RR(DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD)); - RR(DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD)); + for (i = 0; i < dss_feat_get_num_mgrs(); i++) { + RR(DEFAULT_COLOR(i)); + RR(TRANS_COLOR(i)); + RR(SIZE_MGR(i)); + if (i == OMAP_DSS_CHANNEL_DIGIT) + continue; + RR(TIMING_H(i)); + RR(TIMING_V(i)); + RR(POL_FREQ(i)); + RR(DIVISORo(i)); - if (dss_has_feature(FEAT_CPR)) { - RR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD)); - RR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD)); - RR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD)); - } - if (dss_has_feature(FEAT_MGR_LCD2)) { - RR(DATA_CYCLE1(OMAP_DSS_CHANNEL_LCD2)); - RR(DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD2)); - RR(DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD2)); + RR(DATA_CYCLE1(i)); + RR(DATA_CYCLE2(i)); + RR(DATA_CYCLE3(i)); if (dss_has_feature(FEAT_CPR)) { - RR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD2)); - RR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD2)); - RR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD2)); + RR(CPR_COEF_R(i)); + RR(CPR_COEF_G(i)); + RR(CPR_COEF_B(i)); } } - if (dss_has_feature(FEAT_PRELOAD)) - RR(OVL_PRELOAD(OMAP_DSS_GFX)); - - /* VID1 */ - RR(OVL_BA0(OMAP_DSS_VIDEO1)); - RR(OVL_BA1(OMAP_DSS_VIDEO1)); - RR(OVL_POSITION(OMAP_DSS_VIDEO1)); - RR(OVL_SIZE(OMAP_DSS_VIDEO1)); - RR(OVL_ATTRIBUTES(OMAP_DSS_VIDEO1)); - RR(OVL_FIFO_THRESHOLD(OMAP_DSS_VIDEO1)); - RR(OVL_ROW_INC(OMAP_DSS_VIDEO1)); - RR(OVL_PIXEL_INC(OMAP_DSS_VIDEO1)); - RR(OVL_FIR(OMAP_DSS_VIDEO1)); - RR(OVL_PICTURE_SIZE(OMAP_DSS_VIDEO1)); - RR(OVL_ACCU0(OMAP_DSS_VIDEO1)); - RR(OVL_ACCU1(OMAP_DSS_VIDEO1)); - - for (i = 0; i < 8; i++) - RR(OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, i)); - - for (i = 0; i < 8; i++) - RR(OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, i)); - - for (i = 0; i < 5; i++) - RR(OVL_CONV_COEF(OMAP_DSS_VIDEO1, i)); - - if (dss_has_feature(FEAT_FIR_COEF_V)) { - for (i = 0; i < 8; i++) - RR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, i)); - } - - if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { - RR(OVL_BA0_UV(OMAP_DSS_VIDEO1)); - RR(OVL_BA1_UV(OMAP_DSS_VIDEO1)); - RR(OVL_FIR2(OMAP_DSS_VIDEO1)); - RR(OVL_ACCU2_0(OMAP_DSS_VIDEO1)); - RR(OVL_ACCU2_1(OMAP_DSS_VIDEO1)); - - for (i = 0; i < 8; i++) - RR(OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, i)); - - for (i = 0; i < 8; i++) - RR(OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, i)); - - for (i = 0; i < 8; i++) - RR(OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, i)); - } - if (dss_has_feature(FEAT_ATTR2)) - RR(OVL_ATTRIBUTES2(OMAP_DSS_VIDEO1)); - - if (dss_has_feature(FEAT_PRELOAD)) - RR(OVL_PRELOAD(OMAP_DSS_VIDEO1)); - - /* VID2 */ - RR(OVL_BA0(OMAP_DSS_VIDEO2)); - RR(OVL_BA1(OMAP_DSS_VIDEO2)); - RR(OVL_POSITION(OMAP_DSS_VIDEO2)); - RR(OVL_SIZE(OMAP_DSS_VIDEO2)); - RR(OVL_ATTRIBUTES(OMAP_DSS_VIDEO2)); - RR(OVL_FIFO_THRESHOLD(OMAP_DSS_VIDEO2)); - RR(OVL_ROW_INC(OMAP_DSS_VIDEO2)); - RR(OVL_PIXEL_INC(OMAP_DSS_VIDEO2)); - RR(OVL_FIR(OMAP_DSS_VIDEO2)); - RR(OVL_PICTURE_SIZE(OMAP_DSS_VIDEO2)); - RR(OVL_ACCU0(OMAP_DSS_VIDEO2)); - RR(OVL_ACCU1(OMAP_DSS_VIDEO2)); + for (i = 0; i < dss_feat_get_num_ovls(); i++) { + RR(OVL_BA0(i)); + RR(OVL_BA1(i)); + RR(OVL_POSITION(i)); + RR(OVL_SIZE(i)); + RR(OVL_ATTRIBUTES(i)); + RR(OVL_FIFO_THRESHOLD(i)); + RR(OVL_ROW_INC(i)); + RR(OVL_PIXEL_INC(i)); + if (dss_has_feature(FEAT_PRELOAD)) + RR(OVL_PRELOAD(i)); + if (i == OMAP_DSS_GFX) { + RR(OVL_WINDOW_SKIP(i)); + RR(OVL_TABLE_BA(i)); + continue; + } + RR(OVL_FIR(i)); + RR(OVL_PICTURE_SIZE(i)); + RR(OVL_ACCU0(i)); + RR(OVL_ACCU1(i)); - for (i = 0; i < 8; i++) - RR(OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, i)); + for (j = 0; j < 8; j++) + RR(OVL_FIR_COEF_H(i, j)); - for (i = 0; i < 8; i++) - RR(OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, i)); + for (j = 0; j < 8; j++) + RR(OVL_FIR_COEF_HV(i, j)); - for (i = 0; i < 5; i++) - RR(OVL_CONV_COEF(OMAP_DSS_VIDEO2, i)); + for (j = 0; j < 5; j++) + RR(OVL_CONV_COEF(i, j)); - if (dss_has_feature(FEAT_FIR_COEF_V)) { - for (i = 0; i < 8; i++) - RR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, i)); - } + if (dss_has_feature(FEAT_FIR_COEF_V)) { + for (j = 0; j < 8; j++) + RR(OVL_FIR_COEF_V(i, j)); + } - if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { - RR(OVL_BA0_UV(OMAP_DSS_VIDEO2)); - RR(OVL_BA1_UV(OMAP_DSS_VIDEO2)); - RR(OVL_FIR2(OMAP_DSS_VIDEO2)); - RR(OVL_ACCU2_0(OMAP_DSS_VIDEO2)); - RR(OVL_ACCU2_1(OMAP_DSS_VIDEO2)); + if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { + RR(OVL_BA0_UV(i)); + RR(OVL_BA1_UV(i)); + RR(OVL_FIR2(i)); + RR(OVL_ACCU2_0(i)); + RR(OVL_ACCU2_1(i)); - for (i = 0; i < 8; i++) - RR(OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, i)); + for (j = 0; j < 8; j++) + RR(OVL_FIR_COEF_H2(i, j)); - for (i = 0; i < 8; i++) - RR(OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, i)); + for (j = 0; j < 8; j++) + RR(OVL_FIR_COEF_HV2(i, j)); - for (i = 0; i < 8; i++) - RR(OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, i)); + for (j = 0; j < 8; j++) + RR(OVL_FIR_COEF_V2(i, j)); + } + if (dss_has_feature(FEAT_ATTR2)) + RR(OVL_ATTRIBUTES2(i)); } - if (dss_has_feature(FEAT_ATTR2)) - RR(OVL_ATTRIBUTES2(OMAP_DSS_VIDEO2)); - - if (dss_has_feature(FEAT_PRELOAD)) - RR(OVL_PRELOAD(OMAP_DSS_VIDEO2)); if (dss_has_feature(FEAT_CORE_CLK_DIV)) RR(DIVISOR); @@ -570,13 +420,28 @@ void dispc_runtime_put(void) WARN_ON(r < 0); } +static inline bool dispc_mgr_is_lcd(enum omap_channel channel) +{ + if (channel == OMAP_DSS_CHANNEL_LCD || + channel == OMAP_DSS_CHANNEL_LCD2) + return true; + else + return false; +} + +static struct omap_dss_device *dispc_mgr_get_device(enum omap_channel channel) +{ + struct omap_overlay_manager *mgr = + omap_dss_get_overlay_manager(channel); -bool dispc_go_busy(enum omap_channel channel) + return mgr ? mgr->device : NULL; +} + +bool dispc_mgr_go_busy(enum omap_channel channel) { int bit; - if (channel == OMAP_DSS_CHANNEL_LCD || - channel == OMAP_DSS_CHANNEL_LCD2) + if (dispc_mgr_is_lcd(channel)) bit = 5; /* GOLCD */ else bit = 6; /* GODIGIT */ @@ -587,13 +452,12 @@ bool dispc_go_busy(enum omap_channel channel) return REG_GET(DISPC_CONTROL, bit, bit) == 1; } -void dispc_go(enum omap_channel channel) +void dispc_mgr_go(enum omap_channel channel) { int bit; bool enable_bit, go_bit; - if (channel == OMAP_DSS_CHANNEL_LCD || - channel == OMAP_DSS_CHANNEL_LCD2) + if (dispc_mgr_is_lcd(channel)) bit = 0; /* LCDENABLE */ else bit = 1; /* DIGITALENABLE */ @@ -607,8 +471,7 @@ void dispc_go(enum omap_channel channel) if (!enable_bit) return; - if (channel == OMAP_DSS_CHANNEL_LCD || - channel == OMAP_DSS_CHANNEL_LCD2) + if (dispc_mgr_is_lcd(channel)) bit = 5; /* GOLCD */ else bit = 6; /* GODIGIT */ @@ -632,43 +495,44 @@ void dispc_go(enum omap_channel channel) REG_FLD_MOD(DISPC_CONTROL, 1, bit, bit); } -static void _dispc_write_firh_reg(enum omap_plane plane, int reg, u32 value) +static void dispc_ovl_write_firh_reg(enum omap_plane plane, int reg, u32 value) { dispc_write_reg(DISPC_OVL_FIR_COEF_H(plane, reg), value); } -static void _dispc_write_firhv_reg(enum omap_plane plane, int reg, u32 value) +static void dispc_ovl_write_firhv_reg(enum omap_plane plane, int reg, u32 value) { dispc_write_reg(DISPC_OVL_FIR_COEF_HV(plane, reg), value); } -static void _dispc_write_firv_reg(enum omap_plane plane, int reg, u32 value) +static void dispc_ovl_write_firv_reg(enum omap_plane plane, int reg, u32 value) { dispc_write_reg(DISPC_OVL_FIR_COEF_V(plane, reg), value); } -static void _dispc_write_firh2_reg(enum omap_plane plane, int reg, u32 value) +static void dispc_ovl_write_firh2_reg(enum omap_plane plane, int reg, u32 value) { BUG_ON(plane == OMAP_DSS_GFX); dispc_write_reg(DISPC_OVL_FIR_COEF_H2(plane, reg), value); } -static void _dispc_write_firhv2_reg(enum omap_plane plane, int reg, u32 value) +static void dispc_ovl_write_firhv2_reg(enum omap_plane plane, int reg, + u32 value) { BUG_ON(plane == OMAP_DSS_GFX); dispc_write_reg(DISPC_OVL_FIR_COEF_HV2(plane, reg), value); } -static void _dispc_write_firv2_reg(enum omap_plane plane, int reg, u32 value) +static void dispc_ovl_write_firv2_reg(enum omap_plane plane, int reg, u32 value) { BUG_ON(plane == OMAP_DSS_GFX); dispc_write_reg(DISPC_OVL_FIR_COEF_V2(plane, reg), value); } -static void _dispc_set_scale_coef(enum omap_plane plane, int hscaleup, +static void dispc_ovl_set_scale_coef(enum omap_plane plane, int hscaleup, int vscaleup, int five_taps, enum omap_color_component color_comp) { @@ -769,11 +633,11 @@ static void _dispc_set_scale_coef(enum omap_plane plane, int hscaleup, | FLD_VAL(v_coef[i].vc2, 31, 24); if (color_comp == DISPC_COLOR_COMPONENT_RGB_Y) { - _dispc_write_firh_reg(plane, i, h); - _dispc_write_firhv_reg(plane, i, hv); + dispc_ovl_write_firh_reg(plane, i, h); + dispc_ovl_write_firhv_reg(plane, i, hv); } else { - _dispc_write_firh2_reg(plane, i, h); - _dispc_write_firhv2_reg(plane, i, hv); + dispc_ovl_write_firh2_reg(plane, i, h); + dispc_ovl_write_firhv2_reg(plane, i, hv); } } @@ -784,15 +648,16 @@ static void _dispc_set_scale_coef(enum omap_plane plane, int hscaleup, v = FLD_VAL(v_coef[i].vc00, 7, 0) | FLD_VAL(v_coef[i].vc22, 15, 8); if (color_comp == DISPC_COLOR_COMPONENT_RGB_Y) - _dispc_write_firv_reg(plane, i, v); + dispc_ovl_write_firv_reg(plane, i, v); else - _dispc_write_firv2_reg(plane, i, v); + dispc_ovl_write_firv2_reg(plane, i, v); } } } static void _dispc_setup_color_conv_coef(void) { + int i; const struct color_conv_coef { int ry, rcr, rcb, gy, gcr, gcb, by, bcr, bcb; int full_range; @@ -806,65 +671,54 @@ static void _dispc_setup_color_conv_coef(void) ct = &ctbl_bt601_5; - dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 0), - CVAL(ct->rcr, ct->ry)); - dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 1), - CVAL(ct->gy, ct->rcb)); - dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 2), - CVAL(ct->gcb, ct->gcr)); - dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 3), - CVAL(ct->bcr, ct->by)); - dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 4), - CVAL(0, ct->bcb)); - - dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 0), - CVAL(ct->rcr, ct->ry)); - dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 1), - CVAL(ct->gy, ct->rcb)); - dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 2), - CVAL(ct->gcb, ct->gcr)); - dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 3), - CVAL(ct->bcr, ct->by)); - dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 4), - CVAL(0, ct->bcb)); + for (i = 1; i < dss_feat_get_num_ovls(); i++) { + dispc_write_reg(DISPC_OVL_CONV_COEF(i, 0), + CVAL(ct->rcr, ct->ry)); + dispc_write_reg(DISPC_OVL_CONV_COEF(i, 1), + CVAL(ct->gy, ct->rcb)); + dispc_write_reg(DISPC_OVL_CONV_COEF(i, 2), + CVAL(ct->gcb, ct->gcr)); + dispc_write_reg(DISPC_OVL_CONV_COEF(i, 3), + CVAL(ct->bcr, ct->by)); + dispc_write_reg(DISPC_OVL_CONV_COEF(i, 4), + CVAL(0, ct->bcb)); -#undef CVAL + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(i), ct->full_range, + 11, 11); + } - REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(OMAP_DSS_VIDEO1), - ct->full_range, 11, 11); - REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(OMAP_DSS_VIDEO2), - ct->full_range, 11, 11); +#undef CVAL } -static void _dispc_set_plane_ba0(enum omap_plane plane, u32 paddr) +static void dispc_ovl_set_ba0(enum omap_plane plane, u32 paddr) { dispc_write_reg(DISPC_OVL_BA0(plane), paddr); } -static void _dispc_set_plane_ba1(enum omap_plane plane, u32 paddr) +static void dispc_ovl_set_ba1(enum omap_plane plane, u32 paddr) { dispc_write_reg(DISPC_OVL_BA1(plane), paddr); } -static void _dispc_set_plane_ba0_uv(enum omap_plane plane, u32 paddr) +static void dispc_ovl_set_ba0_uv(enum omap_plane plane, u32 paddr) { dispc_write_reg(DISPC_OVL_BA0_UV(plane), paddr); } -static void _dispc_set_plane_ba1_uv(enum omap_plane plane, u32 paddr) +static void dispc_ovl_set_ba1_uv(enum omap_plane plane, u32 paddr) { dispc_write_reg(DISPC_OVL_BA1_UV(plane), paddr); } -static void _dispc_set_plane_pos(enum omap_plane plane, int x, int y) +static void dispc_ovl_set_pos(enum omap_plane plane, int x, int y) { u32 val = FLD_VAL(y, 26, 16) | FLD_VAL(x, 10, 0); dispc_write_reg(DISPC_OVL_POSITION(plane), val); } -static void _dispc_set_pic_size(enum omap_plane plane, int width, int height) +static void dispc_ovl_set_pic_size(enum omap_plane plane, int width, int height) { u32 val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); @@ -874,7 +728,7 @@ static void _dispc_set_pic_size(enum omap_plane plane, int width, int height) dispc_write_reg(DISPC_OVL_PICTURE_SIZE(plane), val); } -static void _dispc_set_vid_size(enum omap_plane plane, int width, int height) +static void dispc_ovl_set_vid_size(enum omap_plane plane, int width, int height) { u32 val; @@ -885,44 +739,61 @@ static void _dispc_set_vid_size(enum omap_plane plane, int width, int height) dispc_write_reg(DISPC_OVL_SIZE(plane), val); } -static void _dispc_set_pre_mult_alpha(enum omap_plane plane, bool enable) +static void dispc_ovl_set_zorder(enum omap_plane plane, u8 zorder) { - if (!dss_has_feature(FEAT_PRE_MULT_ALPHA)) + struct omap_overlay *ovl = omap_dss_get_overlay(plane); + + if ((ovl->caps & OMAP_DSS_OVL_CAP_ZORDER) == 0) return; - if (!dss_has_feature(FEAT_GLOBAL_ALPHA_VID1) && - plane == OMAP_DSS_VIDEO1) + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), zorder, 27, 26); +} + +static void dispc_ovl_enable_zorder_planes(void) +{ + int i; + + if (!dss_has_feature(FEAT_ALPHA_FREE_ZORDER)) return; - REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable ? 1 : 0, 28, 28); + for (i = 0; i < dss_feat_get_num_ovls(); i++) + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(i), 1, 25, 25); } -static void _dispc_setup_global_alpha(enum omap_plane plane, u8 global_alpha) +static void dispc_ovl_set_pre_mult_alpha(enum omap_plane plane, bool enable) { - if (!dss_has_feature(FEAT_GLOBAL_ALPHA)) + struct omap_overlay *ovl = omap_dss_get_overlay(plane); + + if ((ovl->caps & OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA) == 0) return; - if (!dss_has_feature(FEAT_GLOBAL_ALPHA_VID1) && - plane == OMAP_DSS_VIDEO1) + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable ? 1 : 0, 28, 28); +} + +static void dispc_ovl_setup_global_alpha(enum omap_plane plane, u8 global_alpha) +{ + static const unsigned shifts[] = { 0, 8, 16, 24, }; + int shift; + struct omap_overlay *ovl = omap_dss_get_overlay(plane); + + if ((ovl->caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) == 0) return; - if (plane == OMAP_DSS_GFX) - REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, 7, 0); - else if (plane == OMAP_DSS_VIDEO2) - REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, 23, 16); + shift = shifts[plane]; + REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, shift + 7, shift); } -static void _dispc_set_pix_inc(enum omap_plane plane, s32 inc) +static void dispc_ovl_set_pix_inc(enum omap_plane plane, s32 inc) { dispc_write_reg(DISPC_OVL_PIXEL_INC(plane), inc); } -static void _dispc_set_row_inc(enum omap_plane plane, s32 inc) +static void dispc_ovl_set_row_inc(enum omap_plane plane, s32 inc) { dispc_write_reg(DISPC_OVL_ROW_INC(plane), inc); } -static void _dispc_set_color_mode(enum omap_plane plane, +static void dispc_ovl_set_color_mode(enum omap_plane plane, enum omap_color_mode color_mode) { u32 m = 0; @@ -1003,7 +874,7 @@ static void _dispc_set_color_mode(enum omap_plane plane, REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), m, 4, 1); } -void dispc_set_channel_out(enum omap_plane plane, +static void dispc_ovl_set_channel_out(enum omap_plane plane, enum omap_channel channel) { int shift; @@ -1016,6 +887,7 @@ void dispc_set_channel_out(enum omap_plane plane, break; case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: + case OMAP_DSS_VIDEO3: shift = 16; break; default: @@ -1050,24 +922,13 @@ void dispc_set_channel_out(enum omap_plane plane, dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), val); } -static void dispc_set_burst_size(enum omap_plane plane, +static void dispc_ovl_set_burst_size(enum omap_plane plane, enum omap_burst_size burst_size) { + static const unsigned shifts[] = { 6, 14, 14, 14, }; int shift; - switch (plane) { - case OMAP_DSS_GFX: - shift = 6; - break; - case OMAP_DSS_VIDEO1: - case OMAP_DSS_VIDEO2: - shift = 14; - break; - default: - BUG(); - return; - } - + shift = shifts[plane]; REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), burst_size, shift + 1, shift); } @@ -1078,10 +939,10 @@ static void dispc_configure_burst_sizes(void) /* Configure burst size always to maximum size */ for (i = 0; i < omap_dss_get_num_overlays(); ++i) - dispc_set_burst_size(i, burst_size); + dispc_ovl_set_burst_size(i, burst_size); } -u32 dispc_get_burst_size(enum omap_plane plane) +u32 dispc_ovl_get_burst_size(enum omap_plane plane) { unsigned unit = dss_feat_get_burst_size_unit(); /* burst multiplier is always x8 (see dispc_configure_burst_sizes()) */ @@ -1102,7 +963,7 @@ void dispc_enable_gamma_table(bool enable) REG_FLD_MOD(DISPC_CONFIG, enable, 9, 9); } -void dispc_enable_cpr(enum omap_channel channel, bool enable) +void dispc_mgr_enable_cpr(enum omap_channel channel, bool enable) { u16 reg; @@ -1116,12 +977,12 @@ void dispc_enable_cpr(enum omap_channel channel, bool enable) REG_FLD_MOD(reg, enable, 15, 15); } -void dispc_set_cpr_coef(enum omap_channel channel, +void dispc_mgr_set_cpr_coef(enum omap_channel channel, struct omap_dss_cpr_coefs *coefs) { u32 coef_r, coef_g, coef_b; - if (channel != OMAP_DSS_CHANNEL_LCD && channel != OMAP_DSS_CHANNEL_LCD2) + if (!dispc_mgr_is_lcd(channel)) return; coef_r = FLD_VAL(coefs->rr, 31, 22) | FLD_VAL(coefs->rg, 20, 11) | @@ -1136,7 +997,7 @@ void dispc_set_cpr_coef(enum omap_channel channel, dispc_write_reg(DISPC_CPR_COEF_B(channel), coef_b); } -static void _dispc_set_vid_color_conv(enum omap_plane plane, bool enable) +static void dispc_ovl_set_vid_color_conv(enum omap_plane plane, bool enable) { u32 val; @@ -1147,19 +1008,16 @@ static void _dispc_set_vid_color_conv(enum omap_plane plane, bool enable) dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), val); } -void dispc_enable_replication(enum omap_plane plane, bool enable) +static void dispc_ovl_enable_replication(enum omap_plane plane, bool enable) { - int bit; - - if (plane == OMAP_DSS_GFX) - bit = 5; - else - bit = 10; + static const unsigned shifts[] = { 5, 10, 10, 10 }; + int shift; - REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable, bit, bit); + shift = shifts[plane]; + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable, shift, shift); } -void dispc_set_lcd_size(enum omap_channel channel, u16 width, u16 height) +void dispc_mgr_set_lcd_size(enum omap_channel channel, u16 width, u16 height) { u32 val; BUG_ON((width > (1 << 11)) || (height > (1 << 11))); @@ -1186,19 +1044,20 @@ static void dispc_read_plane_fifo_sizes(void) dss_feat_get_reg_field(FEAT_REG_FIFOSIZE, &start, &end); - for (plane = 0; plane < ARRAY_SIZE(dispc.fifo_size); ++plane) { + for (plane = 0; plane < dss_feat_get_num_ovls(); ++plane) { size = REG_GET(DISPC_OVL_FIFO_SIZE_STATUS(plane), start, end); size *= unit; dispc.fifo_size[plane] = size; } } -u32 dispc_get_plane_fifo_size(enum omap_plane plane) +u32 dispc_ovl_get_fifo_size(enum omap_plane plane) { return dispc.fifo_size[plane]; } -void dispc_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high) +static void dispc_ovl_set_fifo_threshold(enum omap_plane plane, u32 low, + u32 high) { u8 hi_start, hi_end, lo_start, lo_end; u32 unit; @@ -1233,7 +1092,7 @@ void dispc_enable_fifomerge(bool enable) REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 14, 14); } -static void _dispc_set_fir(enum omap_plane plane, +static void dispc_ovl_set_fir(enum omap_plane plane, int hinc, int vinc, enum omap_color_component color_comp) { @@ -1256,7 +1115,7 @@ static void _dispc_set_fir(enum omap_plane plane, } } -static void _dispc_set_vid_accu0(enum omap_plane plane, int haccu, int vaccu) +static void dispc_ovl_set_vid_accu0(enum omap_plane plane, int haccu, int vaccu) { u32 val; u8 hor_start, hor_end, vert_start, vert_end; @@ -1270,7 +1129,7 @@ static void _dispc_set_vid_accu0(enum omap_plane plane, int haccu, int vaccu) dispc_write_reg(DISPC_OVL_ACCU0(plane), val); } -static void _dispc_set_vid_accu1(enum omap_plane plane, int haccu, int vaccu) +static void dispc_ovl_set_vid_accu1(enum omap_plane plane, int haccu, int vaccu) { u32 val; u8 hor_start, hor_end, vert_start, vert_end; @@ -1284,7 +1143,8 @@ static void _dispc_set_vid_accu1(enum omap_plane plane, int haccu, int vaccu) dispc_write_reg(DISPC_OVL_ACCU1(plane), val); } -static void _dispc_set_vid_accu2_0(enum omap_plane plane, int haccu, int vaccu) +static void dispc_ovl_set_vid_accu2_0(enum omap_plane plane, int haccu, + int vaccu) { u32 val; @@ -1292,7 +1152,8 @@ static void _dispc_set_vid_accu2_0(enum omap_plane plane, int haccu, int vaccu) dispc_write_reg(DISPC_OVL_ACCU2_0(plane), val); } -static void _dispc_set_vid_accu2_1(enum omap_plane plane, int haccu, int vaccu) +static void dispc_ovl_set_vid_accu2_1(enum omap_plane plane, int haccu, + int vaccu) { u32 val; @@ -1300,7 +1161,7 @@ static void _dispc_set_vid_accu2_1(enum omap_plane plane, int haccu, int vaccu) dispc_write_reg(DISPC_OVL_ACCU2_1(plane), val); } -static void _dispc_set_scale_param(enum omap_plane plane, +static void dispc_ovl_set_scale_param(enum omap_plane plane, u16 orig_width, u16 orig_height, u16 out_width, u16 out_height, bool five_taps, u8 rotation, @@ -1312,15 +1173,16 @@ static void _dispc_set_scale_param(enum omap_plane plane, hscaleup = orig_width <= out_width; vscaleup = orig_height <= out_height; - _dispc_set_scale_coef(plane, hscaleup, vscaleup, five_taps, color_comp); + dispc_ovl_set_scale_coef(plane, hscaleup, vscaleup, five_taps, + color_comp); fir_hinc = 1024 * orig_width / out_width; fir_vinc = 1024 * orig_height / out_height; - _dispc_set_fir(plane, fir_hinc, fir_vinc, color_comp); + dispc_ovl_set_fir(plane, fir_hinc, fir_vinc, color_comp); } -static void _dispc_set_scaling_common(enum omap_plane plane, +static void dispc_ovl_set_scaling_common(enum omap_plane plane, u16 orig_width, u16 orig_height, u16 out_width, u16 out_height, bool ilace, bool five_taps, @@ -1331,7 +1193,7 @@ static void _dispc_set_scaling_common(enum omap_plane plane, int accu1 = 0; u32 l; - _dispc_set_scale_param(plane, orig_width, orig_height, + dispc_ovl_set_scale_param(plane, orig_width, orig_height, out_width, out_height, five_taps, rotation, DISPC_COLOR_COMPONENT_RGB_Y); l = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane)); @@ -1370,11 +1232,11 @@ static void _dispc_set_scaling_common(enum omap_plane plane, } } - _dispc_set_vid_accu0(plane, 0, accu0); - _dispc_set_vid_accu1(plane, 0, accu1); + dispc_ovl_set_vid_accu0(plane, 0, accu0); + dispc_ovl_set_vid_accu1(plane, 0, accu1); } -static void _dispc_set_scaling_uv(enum omap_plane plane, +static void dispc_ovl_set_scaling_uv(enum omap_plane plane, u16 orig_width, u16 orig_height, u16 out_width, u16 out_height, bool ilace, bool five_taps, @@ -1422,7 +1284,7 @@ static void _dispc_set_scaling_uv(enum omap_plane plane, if (out_height != orig_height) scale_y = true; - _dispc_set_scale_param(plane, orig_width, orig_height, + dispc_ovl_set_scale_param(plane, orig_width, orig_height, out_width, out_height, five_taps, rotation, DISPC_COLOR_COMPONENT_UV); @@ -1433,11 +1295,11 @@ static void _dispc_set_scaling_uv(enum omap_plane plane, /* set V scaling */ REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), scale_y ? 1 : 0, 6, 6); - _dispc_set_vid_accu2_0(plane, 0x80, 0); - _dispc_set_vid_accu2_1(plane, 0x80, 0); + dispc_ovl_set_vid_accu2_0(plane, 0x80, 0); + dispc_ovl_set_vid_accu2_1(plane, 0x80, 0); } -static void _dispc_set_scaling(enum omap_plane plane, +static void dispc_ovl_set_scaling(enum omap_plane plane, u16 orig_width, u16 orig_height, u16 out_width, u16 out_height, bool ilace, bool five_taps, @@ -1446,14 +1308,14 @@ static void _dispc_set_scaling(enum omap_plane plane, { BUG_ON(plane == OMAP_DSS_GFX); - _dispc_set_scaling_common(plane, + dispc_ovl_set_scaling_common(plane, orig_width, orig_height, out_width, out_height, ilace, five_taps, fieldmode, color_mode, rotation); - _dispc_set_scaling_uv(plane, + dispc_ovl_set_scaling_uv(plane, orig_width, orig_height, out_width, out_height, ilace, five_taps, @@ -1461,7 +1323,7 @@ static void _dispc_set_scaling(enum omap_plane plane, rotation); } -static void _dispc_set_rotation_attrs(enum omap_plane plane, u8 rotation, +static void dispc_ovl_set_rotation_attrs(enum omap_plane plane, u8 rotation, bool mirroring, enum omap_color_mode color_mode) { bool row_repeat = false; @@ -1789,12 +1651,11 @@ static unsigned long calc_fclk_five_taps(enum omap_channel channel, u16 width, enum omap_color_mode color_mode) { u32 fclk = 0; - /* FIXME venc pclk? */ - u64 tmp, pclk = dispc_pclk_rate(channel); + u64 tmp, pclk = dispc_mgr_pclk_rate(channel); if (height > out_height) { - /* FIXME get real display PPL */ - unsigned int ppl = 800; + struct omap_dss_device *dssdev = dispc_mgr_get_device(channel); + unsigned int ppl = dssdev->panel.timings.x_res; tmp = pclk * height * out_width; do_div(tmp, 2 * out_height * ppl); @@ -1846,114 +1707,120 @@ static unsigned long calc_fclk(enum omap_channel channel, u16 width, else vf = 1; - /* FIXME venc pclk? */ - return dispc_pclk_rate(channel) * vf * hf; + return dispc_mgr_pclk_rate(channel) * vf * hf; } -int dispc_setup_plane(enum omap_plane plane, - u32 paddr, u16 screen_width, - u16 pos_x, u16 pos_y, - u16 width, u16 height, +static int dispc_ovl_calc_scaling(enum omap_plane plane, + enum omap_channel channel, u16 width, u16 height, u16 out_width, u16 out_height, - enum omap_color_mode color_mode, - bool ilace, - enum omap_dss_rotation_type rotation_type, - u8 rotation, bool mirror, - u8 global_alpha, u8 pre_mult_alpha, - enum omap_channel channel, u32 puv_addr) -{ - const int maxdownscale = cpu_is_omap34xx() ? 4 : 2; - bool five_taps = 0; - bool fieldmode = 0; - int cconv = 0; - unsigned offset0, offset1; - s32 row_inc; - s32 pix_inc; - u16 frame_height = height; - unsigned int field_offset = 0; + enum omap_color_mode color_mode, bool *five_taps) +{ + struct omap_overlay *ovl = omap_dss_get_overlay(plane); + const int maxdownscale = dss_feat_get_param_max(FEAT_PARAM_DOWNSCALE); + unsigned long fclk = 0; - DSSDBG("dispc_setup_plane %d, pa %x, sw %d, %d,%d, %dx%d -> " - "%dx%d, ilace %d, cmode %x, rot %d, mir %d chan %d\n", - plane, paddr, screen_width, pos_x, pos_y, - width, height, - out_width, out_height, - ilace, color_mode, - rotation, mirror, channel); + if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) { + if (width != out_width || height != out_height) + return -EINVAL; + else + return 0; + } - if (paddr == 0) + if (out_width < width / maxdownscale || + out_width > width * 8) return -EINVAL; - if (ilace && height == out_height) - fieldmode = 1; + if (out_height < height / maxdownscale || + out_height > height * 8) + return -EINVAL; - if (ilace) { - if (fieldmode) - height /= 2; - pos_y /= 2; - out_height /= 2; + /* Must use 5-tap filter? */ + *five_taps = height > out_height * 2; - DSSDBG("adjusting for ilace: height %d, pos_y %d, " - "out_height %d\n", - height, pos_y, out_height); + if (!*five_taps) { + fclk = calc_fclk(channel, width, height, out_width, + out_height); + + /* Try 5-tap filter if 3-tap fclk is too high */ + if (cpu_is_omap34xx() && height > out_height && + fclk > dispc_fclk_rate()) + *five_taps = true; } - if (!dss_feat_color_mode_supported(plane, color_mode)) + if (width > (2048 >> *five_taps)) { + DSSERR("failed to set up scaling, fclk too low\n"); return -EINVAL; + } - if (plane == OMAP_DSS_GFX) { - if (width != out_width || height != out_height) - return -EINVAL; - } else { - /* video plane */ + if (*five_taps) + fclk = calc_fclk_five_taps(channel, width, height, + out_width, out_height, color_mode); - unsigned long fclk = 0; + DSSDBG("required fclk rate = %lu Hz\n", fclk); + DSSDBG("current fclk rate = %lu Hz\n", dispc_fclk_rate()); - if (out_width < width / maxdownscale || - out_width > width * 8) - return -EINVAL; + if (!fclk || fclk > dispc_fclk_rate()) { + DSSERR("failed to set up scaling, " + "required fclk rate = %lu Hz, " + "current fclk rate = %lu Hz\n", + fclk, dispc_fclk_rate()); + return -EINVAL; + } - if (out_height < height / maxdownscale || - out_height > height * 8) - return -EINVAL; + return 0; +} - if (color_mode == OMAP_DSS_COLOR_YUV2 || - color_mode == OMAP_DSS_COLOR_UYVY || - color_mode == OMAP_DSS_COLOR_NV12) - cconv = 1; +int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi, + bool ilace, enum omap_channel channel, bool replication, + u32 fifo_low, u32 fifo_high) +{ + struct omap_overlay *ovl = omap_dss_get_overlay(plane); + bool five_taps = false; + bool fieldmode = 0; + int r, cconv = 0; + unsigned offset0, offset1; + s32 row_inc; + s32 pix_inc; + u16 frame_height = oi->height; + unsigned int field_offset = 0; - /* Must use 5-tap filter? */ - five_taps = height > out_height * 2; + DSSDBG("dispc_ovl_setup %d, pa %x, pa_uv %x, sw %d, %d,%d, %dx%d -> " + "%dx%d, cmode %x, rot %d, mir %d, ilace %d chan %d repl %d " + "fifo_low %d fifo high %d\n", plane, oi->paddr, oi->p_uv_addr, + oi->screen_width, oi->pos_x, oi->pos_y, oi->width, oi->height, + oi->out_width, oi->out_height, oi->color_mode, oi->rotation, + oi->mirror, ilace, channel, replication, fifo_low, fifo_high); - if (!five_taps) { - fclk = calc_fclk(channel, width, height, out_width, - out_height); + if (oi->paddr == 0) + return -EINVAL; - /* Try 5-tap filter if 3-tap fclk is too high */ - if (cpu_is_omap34xx() && height > out_height && - fclk > dispc_fclk_rate()) - five_taps = true; - } + if (ilace && oi->height == oi->out_height) + fieldmode = 1; - if (width > (2048 >> five_taps)) { - DSSERR("failed to set up scaling, fclk too low\n"); - return -EINVAL; - } + if (ilace) { + if (fieldmode) + oi->height /= 2; + oi->pos_y /= 2; + oi->out_height /= 2; - if (five_taps) - fclk = calc_fclk_five_taps(channel, width, height, - out_width, out_height, color_mode); + DSSDBG("adjusting for ilace: height %d, pos_y %d, " + "out_height %d\n", + oi->height, oi->pos_y, oi->out_height); + } - DSSDBG("required fclk rate = %lu Hz\n", fclk); - DSSDBG("current fclk rate = %lu Hz\n", dispc_fclk_rate()); + if (!dss_feat_color_mode_supported(plane, oi->color_mode)) + return -EINVAL; - if (!fclk || fclk > dispc_fclk_rate()) { - DSSERR("failed to set up scaling, " - "required fclk rate = %lu Hz, " - "current fclk rate = %lu Hz\n", - fclk, dispc_fclk_rate()); - return -EINVAL; - } - } + r = dispc_ovl_calc_scaling(plane, channel, oi->width, oi->height, + oi->out_width, oi->out_height, oi->color_mode, + &five_taps); + if (r) + return r; + + if (oi->color_mode == OMAP_DSS_COLOR_YUV2 || + oi->color_mode == OMAP_DSS_COLOR_UYVY || + oi->color_mode == OMAP_DSS_COLOR_NV12) + cconv = 1; if (ilace && !fieldmode) { /* @@ -1963,69 +1830,76 @@ int dispc_setup_plane(enum omap_plane plane, * so the integer part must be added to the base address of the * bottom field. */ - if (!height || height == out_height) + if (!oi->height || oi->height == oi->out_height) field_offset = 0; else - field_offset = height / out_height / 2; + field_offset = oi->height / oi->out_height / 2; } /* Fields are independent but interleaved in memory. */ if (fieldmode) field_offset = 1; - if (rotation_type == OMAP_DSS_ROT_DMA) - calc_dma_rotation_offset(rotation, mirror, - screen_width, width, frame_height, color_mode, - fieldmode, field_offset, + if (oi->rotation_type == OMAP_DSS_ROT_DMA) + calc_dma_rotation_offset(oi->rotation, oi->mirror, + oi->screen_width, oi->width, frame_height, + oi->color_mode, fieldmode, field_offset, &offset0, &offset1, &row_inc, &pix_inc); else - calc_vrfb_rotation_offset(rotation, mirror, - screen_width, width, frame_height, color_mode, - fieldmode, field_offset, + calc_vrfb_rotation_offset(oi->rotation, oi->mirror, + oi->screen_width, oi->width, frame_height, + oi->color_mode, fieldmode, field_offset, &offset0, &offset1, &row_inc, &pix_inc); DSSDBG("offset0 %u, offset1 %u, row_inc %d, pix_inc %d\n", offset0, offset1, row_inc, pix_inc); - _dispc_set_color_mode(plane, color_mode); + dispc_ovl_set_color_mode(plane, oi->color_mode); - _dispc_set_plane_ba0(plane, paddr + offset0); - _dispc_set_plane_ba1(plane, paddr + offset1); + dispc_ovl_set_ba0(plane, oi->paddr + offset0); + dispc_ovl_set_ba1(plane, oi->paddr + offset1); - if (OMAP_DSS_COLOR_NV12 == color_mode) { - _dispc_set_plane_ba0_uv(plane, puv_addr + offset0); - _dispc_set_plane_ba1_uv(plane, puv_addr + offset1); + if (OMAP_DSS_COLOR_NV12 == oi->color_mode) { + dispc_ovl_set_ba0_uv(plane, oi->p_uv_addr + offset0); + dispc_ovl_set_ba1_uv(plane, oi->p_uv_addr + offset1); } - _dispc_set_row_inc(plane, row_inc); - _dispc_set_pix_inc(plane, pix_inc); + dispc_ovl_set_row_inc(plane, row_inc); + dispc_ovl_set_pix_inc(plane, pix_inc); - DSSDBG("%d,%d %dx%d -> %dx%d\n", pos_x, pos_y, width, height, - out_width, out_height); + DSSDBG("%d,%d %dx%d -> %dx%d\n", oi->pos_x, oi->pos_y, oi->width, + oi->height, oi->out_width, oi->out_height); - _dispc_set_plane_pos(plane, pos_x, pos_y); + dispc_ovl_set_pos(plane, oi->pos_x, oi->pos_y); - _dispc_set_pic_size(plane, width, height); + dispc_ovl_set_pic_size(plane, oi->width, oi->height); - if (plane != OMAP_DSS_GFX) { - _dispc_set_scaling(plane, width, height, - out_width, out_height, + if (ovl->caps & OMAP_DSS_OVL_CAP_SCALE) { + dispc_ovl_set_scaling(plane, oi->width, oi->height, + oi->out_width, oi->out_height, ilace, five_taps, fieldmode, - color_mode, rotation); - _dispc_set_vid_size(plane, out_width, out_height); - _dispc_set_vid_color_conv(plane, cconv); + oi->color_mode, oi->rotation); + dispc_ovl_set_vid_size(plane, oi->out_width, oi->out_height); + dispc_ovl_set_vid_color_conv(plane, cconv); } - _dispc_set_rotation_attrs(plane, rotation, mirror, color_mode); + dispc_ovl_set_rotation_attrs(plane, oi->rotation, oi->mirror, + oi->color_mode); + + dispc_ovl_set_zorder(plane, oi->zorder); + dispc_ovl_set_pre_mult_alpha(plane, oi->pre_mult_alpha); + dispc_ovl_setup_global_alpha(plane, oi->global_alpha); - _dispc_set_pre_mult_alpha(plane, pre_mult_alpha); - _dispc_setup_global_alpha(plane, global_alpha); + dispc_ovl_set_channel_out(plane, channel); + + dispc_ovl_enable_replication(plane, replication); + dispc_ovl_set_fifo_threshold(plane, fifo_low, fifo_high); return 0; } -int dispc_enable_plane(enum omap_plane plane, bool enable) +int dispc_ovl_enable(enum omap_plane plane, bool enable) { DSSDBG("dispc_enable_plane %d, %d\n", plane, enable); @@ -2048,7 +1922,7 @@ static void _enable_lcd_out(enum omap_channel channel, bool enable) REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 0, 0); } -static void dispc_enable_lcd_out(enum omap_channel channel, bool enable) +static void dispc_mgr_enable_lcd_out(enum omap_channel channel, bool enable) { struct completion frame_done_completion; bool is_on; @@ -2095,14 +1969,19 @@ static void _enable_digit_out(bool enable) REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 1, 1); } -static void dispc_enable_digit_out(bool enable) +static void dispc_mgr_enable_digit_out(bool enable) { struct completion frame_done_completion; - int r; + enum dss_hdmi_venc_clk_source_select src; + int r, i; + u32 irq_mask; + int num_irqs; if (REG_GET(DISPC_CONTROL, 1, 1) == enable) return; + src = dss_get_hdmi_venc_clk_source(); + if (enable) { unsigned long flags; /* When we enable digit output, we'll get an extra digit @@ -2119,43 +1998,47 @@ static void dispc_enable_digit_out(bool enable) * wait for the extra sync losts */ init_completion(&frame_done_completion); + if (src == DSS_HDMI_M_PCLK && enable == false) { + irq_mask = DISPC_IRQ_FRAMEDONETV; + num_irqs = 1; + } else { + irq_mask = DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD; + /* XXX I understand from TRM that we should only wait for the + * current field to complete. But it seems we have to wait for + * both fields */ + num_irqs = 2; + } + r = omap_dispc_register_isr(dispc_disable_isr, &frame_done_completion, - DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD); + irq_mask); if (r) - DSSERR("failed to register EVSYNC isr\n"); + DSSERR("failed to register %x isr\n", irq_mask); _enable_digit_out(enable); - /* XXX I understand from TRM that we should only wait for the - * current field to complete. But it seems we have to wait - * for both fields */ - if (!wait_for_completion_timeout(&frame_done_completion, - msecs_to_jiffies(100))) - DSSERR("timeout waiting for EVSYNC\n"); - - if (!wait_for_completion_timeout(&frame_done_completion, - msecs_to_jiffies(100))) - DSSERR("timeout waiting for EVSYNC\n"); + for (i = 0; i < num_irqs; ++i) { + if (!wait_for_completion_timeout(&frame_done_completion, + msecs_to_jiffies(100))) + DSSERR("timeout waiting for digit out to %s\n", + enable ? "start" : "stop"); + } - r = omap_dispc_unregister_isr(dispc_disable_isr, - &frame_done_completion, - DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD); + r = omap_dispc_unregister_isr(dispc_disable_isr, &frame_done_completion, + irq_mask); if (r) - DSSERR("failed to unregister EVSYNC isr\n"); + DSSERR("failed to unregister %x isr\n", irq_mask); if (enable) { unsigned long flags; spin_lock_irqsave(&dispc.irq_lock, flags); - dispc.irq_error_mask = DISPC_IRQ_MASK_ERROR; - if (dss_has_feature(FEAT_MGR_LCD2)) - dispc.irq_error_mask |= DISPC_IRQ_SYNC_LOST2; + dispc.irq_error_mask |= DISPC_IRQ_SYNC_LOST_DIGIT; dispc_write_reg(DISPC_IRQSTATUS, DISPC_IRQ_SYNC_LOST_DIGIT); _omap_dispc_set_irqs(); spin_unlock_irqrestore(&dispc.irq_lock, flags); } } -bool dispc_is_channel_enabled(enum omap_channel channel) +bool dispc_mgr_is_enabled(enum omap_channel channel) { if (channel == OMAP_DSS_CHANNEL_LCD) return !!REG_GET(DISPC_CONTROL, 0, 0); @@ -2167,13 +2050,12 @@ bool dispc_is_channel_enabled(enum omap_channel channel) BUG(); } -void dispc_enable_channel(enum omap_channel channel, bool enable) +void dispc_mgr_enable(enum omap_channel channel, bool enable) { - if (channel == OMAP_DSS_CHANNEL_LCD || - channel == OMAP_DSS_CHANNEL_LCD2) - dispc_enable_lcd_out(channel, enable); + if (dispc_mgr_is_lcd(channel)) + dispc_mgr_enable_lcd_out(channel, enable); else if (channel == OMAP_DSS_CHANNEL_DIGIT) - dispc_enable_digit_out(enable); + dispc_mgr_enable_digit_out(enable); else BUG(); } @@ -2202,7 +2084,7 @@ void dispc_pck_free_enable(bool enable) REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 27, 27); } -void dispc_enable_fifohandcheck(enum omap_channel channel, bool enable) +void dispc_mgr_enable_fifohandcheck(enum omap_channel channel, bool enable) { if (channel == OMAP_DSS_CHANNEL_LCD2) REG_FLD_MOD(DISPC_CONFIG2, enable ? 1 : 0, 16, 16); @@ -2211,7 +2093,7 @@ void dispc_enable_fifohandcheck(enum omap_channel channel, bool enable) } -void dispc_set_lcd_display_type(enum omap_channel channel, +void dispc_mgr_set_lcd_display_type(enum omap_channel channel, enum omap_lcd_display_type type) { int mode; @@ -2242,12 +2124,12 @@ void dispc_set_loadmode(enum omap_dss_load_mode mode) } -void dispc_set_default_color(enum omap_channel channel, u32 color) +void dispc_mgr_set_default_color(enum omap_channel channel, u32 color) { dispc_write_reg(DISPC_DEFAULT_COLOR(channel), color); } -u32 dispc_get_default_color(enum omap_channel channel) +u32 dispc_mgr_get_default_color(enum omap_channel channel) { u32 l; @@ -2260,7 +2142,7 @@ u32 dispc_get_default_color(enum omap_channel channel) return l; } -void dispc_set_trans_key(enum omap_channel ch, +void dispc_mgr_set_trans_key(enum omap_channel ch, enum omap_dss_trans_key_type type, u32 trans_key) { @@ -2274,7 +2156,7 @@ void dispc_set_trans_key(enum omap_channel ch, dispc_write_reg(DISPC_TRANS_COLOR(ch), trans_key); } -void dispc_get_trans_key(enum omap_channel ch, +void dispc_mgr_get_trans_key(enum omap_channel ch, enum omap_dss_trans_key_type *type, u32 *trans_key) { @@ -2293,7 +2175,7 @@ void dispc_get_trans_key(enum omap_channel ch, *trans_key = dispc_read_reg(DISPC_TRANS_COLOR(ch)); } -void dispc_enable_trans_key(enum omap_channel ch, bool enable) +void dispc_mgr_enable_trans_key(enum omap_channel ch, bool enable) { if (ch == OMAP_DSS_CHANNEL_LCD) REG_FLD_MOD(DISPC_CONFIG, enable, 10, 10); @@ -2302,39 +2184,36 @@ void dispc_enable_trans_key(enum omap_channel ch, bool enable) else /* OMAP_DSS_CHANNEL_LCD2 */ REG_FLD_MOD(DISPC_CONFIG2, enable, 10, 10); } -void dispc_enable_alpha_blending(enum omap_channel ch, bool enable) + +void dispc_mgr_enable_alpha_fixed_zorder(enum omap_channel ch, bool enable) { - if (!dss_has_feature(FEAT_GLOBAL_ALPHA)) + if (!dss_has_feature(FEAT_ALPHA_FIXED_ZORDER)) return; if (ch == OMAP_DSS_CHANNEL_LCD) REG_FLD_MOD(DISPC_CONFIG, enable, 18, 18); else if (ch == OMAP_DSS_CHANNEL_DIGIT) REG_FLD_MOD(DISPC_CONFIG, enable, 19, 19); - else /* OMAP_DSS_CHANNEL_LCD2 */ - REG_FLD_MOD(DISPC_CONFIG2, enable, 18, 18); } -bool dispc_alpha_blending_enabled(enum omap_channel ch) + +bool dispc_mgr_alpha_fixed_zorder_enabled(enum omap_channel ch) { bool enabled; - if (!dss_has_feature(FEAT_GLOBAL_ALPHA)) + if (!dss_has_feature(FEAT_ALPHA_FIXED_ZORDER)) return false; if (ch == OMAP_DSS_CHANNEL_LCD) enabled = REG_GET(DISPC_CONFIG, 18, 18); else if (ch == OMAP_DSS_CHANNEL_DIGIT) enabled = REG_GET(DISPC_CONFIG, 19, 19); - else if (ch == OMAP_DSS_CHANNEL_LCD2) - enabled = REG_GET(DISPC_CONFIG2, 18, 18); else BUG(); return enabled; } - -bool dispc_trans_key_enabled(enum omap_channel ch) +bool dispc_mgr_trans_key_enabled(enum omap_channel ch) { bool enabled; @@ -2351,7 +2230,7 @@ bool dispc_trans_key_enabled(enum omap_channel ch) } -void dispc_set_tft_data_lines(enum omap_channel channel, u8 data_lines) +void dispc_mgr_set_tft_data_lines(enum omap_channel channel, u8 data_lines) { int code; @@ -2379,46 +2258,41 @@ void dispc_set_tft_data_lines(enum omap_channel channel, u8 data_lines) REG_FLD_MOD(DISPC_CONTROL, code, 9, 8); } -void dispc_set_parallel_interface_mode(enum omap_channel channel, - enum omap_parallel_interface_mode mode) +void dispc_mgr_set_io_pad_mode(enum dss_io_pad_mode mode) { u32 l; - int stallmode; - int gpout0 = 1; - int gpout1; + int gpout0, gpout1; switch (mode) { - case OMAP_DSS_PARALLELMODE_BYPASS: - stallmode = 0; - gpout1 = 1; + case DSS_IO_PAD_MODE_RESET: + gpout0 = 0; + gpout1 = 0; break; - - case OMAP_DSS_PARALLELMODE_RFBI: - stallmode = 1; + case DSS_IO_PAD_MODE_RFBI: + gpout0 = 1; gpout1 = 0; break; - - case OMAP_DSS_PARALLELMODE_DSI: - stallmode = 1; + case DSS_IO_PAD_MODE_BYPASS: + gpout0 = 1; gpout1 = 1; break; - default: BUG(); return; } - if (channel == OMAP_DSS_CHANNEL_LCD2) { - l = dispc_read_reg(DISPC_CONTROL2); - l = FLD_MOD(l, stallmode, 11, 11); - dispc_write_reg(DISPC_CONTROL2, l); - } else { - l = dispc_read_reg(DISPC_CONTROL); - l = FLD_MOD(l, stallmode, 11, 11); - l = FLD_MOD(l, gpout0, 15, 15); - l = FLD_MOD(l, gpout1, 16, 16); - dispc_write_reg(DISPC_CONTROL, l); - } + l = dispc_read_reg(DISPC_CONTROL); + l = FLD_MOD(l, gpout0, 15, 15); + l = FLD_MOD(l, gpout1, 16, 16); + dispc_write_reg(DISPC_CONTROL, l); +} + +void dispc_mgr_enable_stallmode(enum omap_channel channel, bool enable) +{ + if (channel == OMAP_DSS_CHANNEL_LCD2) + REG_FLD_MOD(DISPC_CONTROL2, enable, 11, 11); + else + REG_FLD_MOD(DISPC_CONTROL, enable, 11, 11); } static bool _dispc_lcd_timings_ok(int hsw, int hfp, int hbp, @@ -2452,7 +2326,7 @@ bool dispc_lcd_timings_ok(struct omap_video_timings *timings) timings->vfp, timings->vbp); } -static void _dispc_set_lcd_timings(enum omap_channel channel, int hsw, +static void _dispc_mgr_set_lcd_timings(enum omap_channel channel, int hsw, int hfp, int hbp, int vsw, int vfp, int vbp) { u32 timing_h, timing_v; @@ -2476,7 +2350,7 @@ static void _dispc_set_lcd_timings(enum omap_channel channel, int hsw, } /* change name to mode? */ -void dispc_set_lcd_timings(enum omap_channel channel, +void dispc_mgr_set_lcd_timings(enum omap_channel channel, struct omap_video_timings *timings) { unsigned xtot, ytot; @@ -2487,11 +2361,11 @@ void dispc_set_lcd_timings(enum omap_channel channel, timings->vfp, timings->vbp)) BUG(); - _dispc_set_lcd_timings(channel, timings->hsw, timings->hfp, + _dispc_mgr_set_lcd_timings(channel, timings->hsw, timings->hfp, timings->hbp, timings->vsw, timings->vfp, timings->vbp); - dispc_set_lcd_size(channel, timings->x_res, timings->y_res); + dispc_mgr_set_lcd_size(channel, timings->x_res, timings->y_res); xtot = timings->x_res + timings->hfp + timings->hsw + timings->hbp; ytot = timings->y_res + timings->vfp + timings->vsw + timings->vbp; @@ -2509,17 +2383,17 @@ void dispc_set_lcd_timings(enum omap_channel channel, DSSDBG("hsync %luHz, vsync %luHz\n", ht, vt); } -static void dispc_set_lcd_divisor(enum omap_channel channel, u16 lck_div, +static void dispc_mgr_set_lcd_divisor(enum omap_channel channel, u16 lck_div, u16 pck_div) { BUG_ON(lck_div < 1); - BUG_ON(pck_div < 2); + BUG_ON(pck_div < 1); dispc_write_reg(DISPC_DIVISORo(channel), FLD_VAL(lck_div, 23, 16) | FLD_VAL(pck_div, 7, 0)); } -static void dispc_get_lcd_divisor(enum omap_channel channel, int *lck_div, +static void dispc_mgr_get_lcd_divisor(enum omap_channel channel, int *lck_div, int *pck_div) { u32 l; @@ -2552,7 +2426,7 @@ unsigned long dispc_fclk_rate(void) return r; } -unsigned long dispc_lclk_rate(enum omap_channel channel) +unsigned long dispc_mgr_lclk_rate(enum omap_channel channel) { struct platform_device *dsidev; int lcd; @@ -2582,19 +2456,34 @@ unsigned long dispc_lclk_rate(enum omap_channel channel) return r / lcd; } -unsigned long dispc_pclk_rate(enum omap_channel channel) +unsigned long dispc_mgr_pclk_rate(enum omap_channel channel) { - int pcd; unsigned long r; - u32 l; - l = dispc_read_reg(DISPC_DIVISORo(channel)); + if (dispc_mgr_is_lcd(channel)) { + int pcd; + u32 l; - pcd = FLD_GET(l, 7, 0); + l = dispc_read_reg(DISPC_DIVISORo(channel)); - r = dispc_lclk_rate(channel); + pcd = FLD_GET(l, 7, 0); - return r / pcd; + r = dispc_mgr_lclk_rate(channel); + + return r / pcd; + } else { + struct omap_dss_device *dssdev = + dispc_mgr_get_device(channel); + + switch (dssdev->type) { + case OMAP_DISPLAY_TYPE_VENC: + return venc_get_pixel_clock(); + case OMAP_DISPLAY_TYPE_HDMI: + return hdmi_get_pixel_clock(); + default: + BUG(); + } + } } void dispc_dump_clocks(struct seq_file *s) @@ -2631,12 +2520,12 @@ void dispc_dump_clocks(struct seq_file *s) dss_get_generic_clk_source_name(lcd_clk_src), dss_feat_get_clk_source_name(lcd_clk_src)); - dispc_get_lcd_divisor(OMAP_DSS_CHANNEL_LCD, &lcd, &pcd); + dispc_mgr_get_lcd_divisor(OMAP_DSS_CHANNEL_LCD, &lcd, &pcd); seq_printf(s, "lck\t\t%-16lulck div\t%u\n", - dispc_lclk_rate(OMAP_DSS_CHANNEL_LCD), lcd); + dispc_mgr_lclk_rate(OMAP_DSS_CHANNEL_LCD), lcd); seq_printf(s, "pck\t\t%-16lupck div\t%u\n", - dispc_pclk_rate(OMAP_DSS_CHANNEL_LCD), pcd); + dispc_mgr_pclk_rate(OMAP_DSS_CHANNEL_LCD), pcd); if (dss_has_feature(FEAT_MGR_LCD2)) { seq_printf(s, "- LCD2 -\n"); @@ -2646,12 +2535,12 @@ void dispc_dump_clocks(struct seq_file *s) dss_get_generic_clk_source_name(lcd_clk_src), dss_feat_get_clk_source_name(lcd_clk_src)); - dispc_get_lcd_divisor(OMAP_DSS_CHANNEL_LCD2, &lcd, &pcd); + dispc_mgr_get_lcd_divisor(OMAP_DSS_CHANNEL_LCD2, &lcd, &pcd); seq_printf(s, "lck\t\t%-16lulck div\t%u\n", - dispc_lclk_rate(OMAP_DSS_CHANNEL_LCD2), lcd); + dispc_mgr_lclk_rate(OMAP_DSS_CHANNEL_LCD2), lcd); seq_printf(s, "pck\t\t%-16lupck div\t%u\n", - dispc_pclk_rate(OMAP_DSS_CHANNEL_LCD2), pcd); + dispc_mgr_pclk_rate(OMAP_DSS_CHANNEL_LCD2), pcd); } dispc_runtime_put(); @@ -2692,6 +2581,10 @@ void dispc_dump_irqs(struct seq_file *s) PIS(VID1_END_WIN); PIS(VID2_FIFO_UNDERFLOW); PIS(VID2_END_WIN); + if (dss_feat_get_num_ovls() > 3) { + PIS(VID3_FIFO_UNDERFLOW); + PIS(VID3_END_WIN); + } PIS(SYNC_LOST); PIS(SYNC_LOST_DIGIT); PIS(WAKEUP); @@ -2707,11 +2600,26 @@ void dispc_dump_irqs(struct seq_file *s) void dispc_dump_regs(struct seq_file *s) { + int i, j; + const char *mgr_names[] = { + [OMAP_DSS_CHANNEL_LCD] = "LCD", + [OMAP_DSS_CHANNEL_DIGIT] = "TV", + [OMAP_DSS_CHANNEL_LCD2] = "LCD2", + }; + const char *ovl_names[] = { + [OMAP_DSS_GFX] = "GFX", + [OMAP_DSS_VIDEO1] = "VID1", + [OMAP_DSS_VIDEO2] = "VID2", + [OMAP_DSS_VIDEO3] = "VID3", + }; + const char **p_names; + #define DUMPREG(r) seq_printf(s, "%-50s %08x\n", #r, dispc_read_reg(r)) if (dispc_runtime_get()) return; + /* DISPC common registers */ DUMPREG(DISPC_REVISION); DUMPREG(DISPC_SYSCONFIG); DUMPREG(DISPC_SYSSTATUS); @@ -2720,247 +2628,139 @@ void dispc_dump_regs(struct seq_file *s) DUMPREG(DISPC_CONTROL); DUMPREG(DISPC_CONFIG); DUMPREG(DISPC_CAPABLE); - DUMPREG(DISPC_DEFAULT_COLOR(OMAP_DSS_CHANNEL_LCD)); - DUMPREG(DISPC_DEFAULT_COLOR(OMAP_DSS_CHANNEL_DIGIT)); - DUMPREG(DISPC_TRANS_COLOR(OMAP_DSS_CHANNEL_LCD)); - DUMPREG(DISPC_TRANS_COLOR(OMAP_DSS_CHANNEL_DIGIT)); DUMPREG(DISPC_LINE_STATUS); DUMPREG(DISPC_LINE_NUMBER); - DUMPREG(DISPC_TIMING_H(OMAP_DSS_CHANNEL_LCD)); - DUMPREG(DISPC_TIMING_V(OMAP_DSS_CHANNEL_LCD)); - DUMPREG(DISPC_POL_FREQ(OMAP_DSS_CHANNEL_LCD)); - DUMPREG(DISPC_DIVISORo(OMAP_DSS_CHANNEL_LCD)); - if (dss_has_feature(FEAT_GLOBAL_ALPHA)) + if (dss_has_feature(FEAT_ALPHA_FIXED_ZORDER) || + dss_has_feature(FEAT_ALPHA_FREE_ZORDER)) DUMPREG(DISPC_GLOBAL_ALPHA); - DUMPREG(DISPC_SIZE_MGR(OMAP_DSS_CHANNEL_DIGIT)); - DUMPREG(DISPC_SIZE_MGR(OMAP_DSS_CHANNEL_LCD)); if (dss_has_feature(FEAT_MGR_LCD2)) { DUMPREG(DISPC_CONTROL2); DUMPREG(DISPC_CONFIG2); - DUMPREG(DISPC_DEFAULT_COLOR(OMAP_DSS_CHANNEL_LCD2)); - DUMPREG(DISPC_TRANS_COLOR(OMAP_DSS_CHANNEL_LCD2)); - DUMPREG(DISPC_TIMING_H(OMAP_DSS_CHANNEL_LCD2)); - DUMPREG(DISPC_TIMING_V(OMAP_DSS_CHANNEL_LCD2)); - DUMPREG(DISPC_POL_FREQ(OMAP_DSS_CHANNEL_LCD2)); - DUMPREG(DISPC_DIVISORo(OMAP_DSS_CHANNEL_LCD2)); - DUMPREG(DISPC_SIZE_MGR(OMAP_DSS_CHANNEL_LCD2)); - } - - DUMPREG(DISPC_OVL_BA0(OMAP_DSS_GFX)); - DUMPREG(DISPC_OVL_BA1(OMAP_DSS_GFX)); - DUMPREG(DISPC_OVL_POSITION(OMAP_DSS_GFX)); - DUMPREG(DISPC_OVL_SIZE(OMAP_DSS_GFX)); - DUMPREG(DISPC_OVL_ATTRIBUTES(OMAP_DSS_GFX)); - DUMPREG(DISPC_OVL_FIFO_THRESHOLD(OMAP_DSS_GFX)); - DUMPREG(DISPC_OVL_FIFO_SIZE_STATUS(OMAP_DSS_GFX)); - DUMPREG(DISPC_OVL_ROW_INC(OMAP_DSS_GFX)); - DUMPREG(DISPC_OVL_PIXEL_INC(OMAP_DSS_GFX)); - DUMPREG(DISPC_OVL_WINDOW_SKIP(OMAP_DSS_GFX)); - DUMPREG(DISPC_OVL_TABLE_BA(OMAP_DSS_GFX)); - - DUMPREG(DISPC_DATA_CYCLE1(OMAP_DSS_CHANNEL_LCD)); - DUMPREG(DISPC_DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD)); - DUMPREG(DISPC_DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD)); - - if (dss_has_feature(FEAT_CPR)) { - DUMPREG(DISPC_CPR_COEF_R(OMAP_DSS_CHANNEL_LCD)); - DUMPREG(DISPC_CPR_COEF_G(OMAP_DSS_CHANNEL_LCD)); - DUMPREG(DISPC_CPR_COEF_B(OMAP_DSS_CHANNEL_LCD)); } - if (dss_has_feature(FEAT_MGR_LCD2)) { - DUMPREG(DISPC_DATA_CYCLE1(OMAP_DSS_CHANNEL_LCD2)); - DUMPREG(DISPC_DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD2)); - DUMPREG(DISPC_DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD2)); + +#undef DUMPREG + +#define DISPC_REG(i, name) name(i) +#define DUMPREG(i, r) seq_printf(s, "%s(%s)%*s %08x\n", #r, p_names[i], \ + 48 - strlen(#r) - strlen(p_names[i]), " ", \ + dispc_read_reg(DISPC_REG(i, r))) + + p_names = mgr_names; + + /* DISPC channel specific registers */ + for (i = 0; i < dss_feat_get_num_mgrs(); i++) { + DUMPREG(i, DISPC_DEFAULT_COLOR); + DUMPREG(i, DISPC_TRANS_COLOR); + DUMPREG(i, DISPC_SIZE_MGR); + + if (i == OMAP_DSS_CHANNEL_DIGIT) + continue; + + DUMPREG(i, DISPC_DEFAULT_COLOR); + DUMPREG(i, DISPC_TRANS_COLOR); + DUMPREG(i, DISPC_TIMING_H); + DUMPREG(i, DISPC_TIMING_V); + DUMPREG(i, DISPC_POL_FREQ); + DUMPREG(i, DISPC_DIVISORo); + DUMPREG(i, DISPC_SIZE_MGR); + + DUMPREG(i, DISPC_DATA_CYCLE1); + DUMPREG(i, DISPC_DATA_CYCLE2); + DUMPREG(i, DISPC_DATA_CYCLE3); if (dss_has_feature(FEAT_CPR)) { - DUMPREG(DISPC_CPR_COEF_R(OMAP_DSS_CHANNEL_LCD2)); - DUMPREG(DISPC_CPR_COEF_G(OMAP_DSS_CHANNEL_LCD2)); - DUMPREG(DISPC_CPR_COEF_B(OMAP_DSS_CHANNEL_LCD2)); + DUMPREG(i, DISPC_CPR_COEF_R); + DUMPREG(i, DISPC_CPR_COEF_G); + DUMPREG(i, DISPC_CPR_COEF_B); + } + } + + p_names = ovl_names; + + for (i = 0; i < dss_feat_get_num_ovls(); i++) { + DUMPREG(i, DISPC_OVL_BA0); + DUMPREG(i, DISPC_OVL_BA1); + DUMPREG(i, DISPC_OVL_POSITION); + DUMPREG(i, DISPC_OVL_SIZE); + DUMPREG(i, DISPC_OVL_ATTRIBUTES); + DUMPREG(i, DISPC_OVL_FIFO_THRESHOLD); + DUMPREG(i, DISPC_OVL_FIFO_SIZE_STATUS); + DUMPREG(i, DISPC_OVL_ROW_INC); + DUMPREG(i, DISPC_OVL_PIXEL_INC); + if (dss_has_feature(FEAT_PRELOAD)) + DUMPREG(i, DISPC_OVL_PRELOAD); + + if (i == OMAP_DSS_GFX) { + DUMPREG(i, DISPC_OVL_WINDOW_SKIP); + DUMPREG(i, DISPC_OVL_TABLE_BA); + continue; + } + + DUMPREG(i, DISPC_OVL_FIR); + DUMPREG(i, DISPC_OVL_PICTURE_SIZE); + DUMPREG(i, DISPC_OVL_ACCU0); + DUMPREG(i, DISPC_OVL_ACCU1); + if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { + DUMPREG(i, DISPC_OVL_BA0_UV); + DUMPREG(i, DISPC_OVL_BA1_UV); + DUMPREG(i, DISPC_OVL_FIR2); + DUMPREG(i, DISPC_OVL_ACCU2_0); + DUMPREG(i, DISPC_OVL_ACCU2_1); } + if (dss_has_feature(FEAT_ATTR2)) + DUMPREG(i, DISPC_OVL_ATTRIBUTES2); + if (dss_has_feature(FEAT_PRELOAD)) + DUMPREG(i, DISPC_OVL_PRELOAD); } - if (dss_has_feature(FEAT_PRELOAD)) - DUMPREG(DISPC_OVL_PRELOAD(OMAP_DSS_GFX)); - - DUMPREG(DISPC_OVL_BA0(OMAP_DSS_VIDEO1)); - DUMPREG(DISPC_OVL_BA1(OMAP_DSS_VIDEO1)); - DUMPREG(DISPC_OVL_POSITION(OMAP_DSS_VIDEO1)); - DUMPREG(DISPC_OVL_SIZE(OMAP_DSS_VIDEO1)); - DUMPREG(DISPC_OVL_ATTRIBUTES(OMAP_DSS_VIDEO1)); - DUMPREG(DISPC_OVL_FIFO_THRESHOLD(OMAP_DSS_VIDEO1)); - DUMPREG(DISPC_OVL_FIFO_SIZE_STATUS(OMAP_DSS_VIDEO1)); - DUMPREG(DISPC_OVL_ROW_INC(OMAP_DSS_VIDEO1)); - DUMPREG(DISPC_OVL_PIXEL_INC(OMAP_DSS_VIDEO1)); - DUMPREG(DISPC_OVL_FIR(OMAP_DSS_VIDEO1)); - DUMPREG(DISPC_OVL_PICTURE_SIZE(OMAP_DSS_VIDEO1)); - DUMPREG(DISPC_OVL_ACCU0(OMAP_DSS_VIDEO1)); - DUMPREG(DISPC_OVL_ACCU1(OMAP_DSS_VIDEO1)); - - DUMPREG(DISPC_OVL_BA0(OMAP_DSS_VIDEO2)); - DUMPREG(DISPC_OVL_BA1(OMAP_DSS_VIDEO2)); - DUMPREG(DISPC_OVL_POSITION(OMAP_DSS_VIDEO2)); - DUMPREG(DISPC_OVL_SIZE(OMAP_DSS_VIDEO2)); - DUMPREG(DISPC_OVL_ATTRIBUTES(OMAP_DSS_VIDEO2)); - DUMPREG(DISPC_OVL_FIFO_THRESHOLD(OMAP_DSS_VIDEO2)); - DUMPREG(DISPC_OVL_FIFO_SIZE_STATUS(OMAP_DSS_VIDEO2)); - DUMPREG(DISPC_OVL_ROW_INC(OMAP_DSS_VIDEO2)); - DUMPREG(DISPC_OVL_PIXEL_INC(OMAP_DSS_VIDEO2)); - DUMPREG(DISPC_OVL_FIR(OMAP_DSS_VIDEO2)); - DUMPREG(DISPC_OVL_PICTURE_SIZE(OMAP_DSS_VIDEO2)); - DUMPREG(DISPC_OVL_ACCU0(OMAP_DSS_VIDEO2)); - DUMPREG(DISPC_OVL_ACCU1(OMAP_DSS_VIDEO2)); - - DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 0)); - DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 1)); - DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 2)); - DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 3)); - DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 4)); - DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 5)); - DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 6)); - DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 7)); - DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 0)); - DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 1)); - DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 2)); - DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 3)); - DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 4)); - DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 5)); - DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 6)); - DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 7)); - DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 0)); - DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 1)); - DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 2)); - DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 3)); - DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 4)); - if (dss_has_feature(FEAT_FIR_COEF_V)) { - DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 0)); - DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 1)); - DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 2)); - DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 3)); - DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 4)); - DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 5)); - DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 6)); - DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 7)); - } - - if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { - DUMPREG(DISPC_OVL_BA0_UV(OMAP_DSS_VIDEO1)); - DUMPREG(DISPC_OVL_BA1_UV(OMAP_DSS_VIDEO1)); - DUMPREG(DISPC_OVL_FIR2(OMAP_DSS_VIDEO1)); - DUMPREG(DISPC_OVL_ACCU2_0(OMAP_DSS_VIDEO1)); - DUMPREG(DISPC_OVL_ACCU2_1(OMAP_DSS_VIDEO1)); - - DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 0)); - DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 1)); - DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 2)); - DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 3)); - DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 4)); - DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 5)); - DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 6)); - DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 7)); - - DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 0)); - DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 1)); - DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 2)); - DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 3)); - DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 4)); - DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 5)); - DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 6)); - DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 7)); - - DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 0)); - DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 1)); - DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 2)); - DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 3)); - DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 4)); - DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 5)); - DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 6)); - DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 7)); - } - if (dss_has_feature(FEAT_ATTR2)) - DUMPREG(DISPC_OVL_ATTRIBUTES2(OMAP_DSS_VIDEO1)); - - - DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 0)); - DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 1)); - DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 2)); - DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 3)); - DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 4)); - DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 5)); - DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 6)); - DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 7)); - DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 0)); - DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 1)); - DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 2)); - DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 3)); - DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 4)); - DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 5)); - DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 6)); - DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 7)); - DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 0)); - DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 1)); - DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 2)); - DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 3)); - DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 4)); - - if (dss_has_feature(FEAT_FIR_COEF_V)) { - DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 0)); - DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 1)); - DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 2)); - DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 3)); - DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 4)); - DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 5)); - DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 6)); - DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 7)); - } - - if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { - DUMPREG(DISPC_OVL_BA0_UV(OMAP_DSS_VIDEO2)); - DUMPREG(DISPC_OVL_BA1_UV(OMAP_DSS_VIDEO2)); - DUMPREG(DISPC_OVL_FIR2(OMAP_DSS_VIDEO2)); - DUMPREG(DISPC_OVL_ACCU2_0(OMAP_DSS_VIDEO2)); - DUMPREG(DISPC_OVL_ACCU2_1(OMAP_DSS_VIDEO2)); - - DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 0)); - DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 1)); - DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 2)); - DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 3)); - DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 4)); - DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 5)); - DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 6)); - DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 7)); - - DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 0)); - DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 1)); - DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 2)); - DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 3)); - DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 4)); - DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 5)); - DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 6)); - DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 7)); - - DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 0)); - DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 1)); - DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 2)); - DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 3)); - DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 4)); - DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 5)); - DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 6)); - DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 7)); - } - if (dss_has_feature(FEAT_ATTR2)) - DUMPREG(DISPC_OVL_ATTRIBUTES2(OMAP_DSS_VIDEO2)); - - if (dss_has_feature(FEAT_PRELOAD)) { - DUMPREG(DISPC_OVL_PRELOAD(OMAP_DSS_VIDEO1)); - DUMPREG(DISPC_OVL_PRELOAD(OMAP_DSS_VIDEO2)); +#undef DISPC_REG +#undef DUMPREG + +#define DISPC_REG(plane, name, i) name(plane, i) +#define DUMPREG(plane, name, i) \ + seq_printf(s, "%s_%d(%s)%*s %08x\n", #name, i, p_names[plane], \ + 46 - strlen(#name) - strlen(p_names[plane]), " ", \ + dispc_read_reg(DISPC_REG(plane, name, i))) + + /* Video pipeline coefficient registers */ + + /* start from OMAP_DSS_VIDEO1 */ + for (i = 1; i < dss_feat_get_num_ovls(); i++) { + for (j = 0; j < 8; j++) + DUMPREG(i, DISPC_OVL_FIR_COEF_H, j); + + for (j = 0; j < 8; j++) + DUMPREG(i, DISPC_OVL_FIR_COEF_HV, j); + + for (j = 0; j < 5; j++) + DUMPREG(i, DISPC_OVL_CONV_COEF, j); + + if (dss_has_feature(FEAT_FIR_COEF_V)) { + for (j = 0; j < 8; j++) + DUMPREG(i, DISPC_OVL_FIR_COEF_V, j); + } + + if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { + for (j = 0; j < 8; j++) + DUMPREG(i, DISPC_OVL_FIR_COEF_H2, j); + + for (j = 0; j < 8; j++) + DUMPREG(i, DISPC_OVL_FIR_COEF_HV2, j); + + for (j = 0; j < 8; j++) + DUMPREG(i, DISPC_OVL_FIR_COEF_V2, j); + } } dispc_runtime_put(); + +#undef DISPC_REG #undef DUMPREG } -static void _dispc_set_pol_freq(enum omap_channel channel, bool onoff, bool rf, - bool ieo, bool ipc, bool ihs, bool ivs, u8 acbi, u8 acb) +static void _dispc_mgr_set_pol_freq(enum omap_channel channel, bool onoff, + bool rf, bool ieo, bool ipc, bool ihs, bool ivs, u8 acbi, + u8 acb) { u32 l = 0; @@ -2979,10 +2779,10 @@ static void _dispc_set_pol_freq(enum omap_channel channel, bool onoff, bool rf, dispc_write_reg(DISPC_POL_FREQ(channel), l); } -void dispc_set_pol_freq(enum omap_channel channel, +void dispc_mgr_set_pol_freq(enum omap_channel channel, enum omap_panel_config config, u8 acbi, u8 acb) { - _dispc_set_pol_freq(channel, (config & OMAP_DSS_LCD_ONOFF) != 0, + _dispc_mgr_set_pol_freq(channel, (config & OMAP_DSS_LCD_ONOFF) != 0, (config & OMAP_DSS_LCD_RF) != 0, (config & OMAP_DSS_LCD_IEO) != 0, (config & OMAP_DSS_LCD_IPC) != 0, @@ -2995,11 +2795,17 @@ void dispc_set_pol_freq(enum omap_channel channel, void dispc_find_clk_divs(bool is_tft, unsigned long req_pck, unsigned long fck, struct dispc_clock_info *cinfo) { - u16 pcd_min = is_tft ? 2 : 3; + u16 pcd_min, pcd_max; unsigned long best_pck; u16 best_ld, cur_ld; u16 best_pd, cur_pd; + pcd_min = dss_feat_get_param_min(FEAT_PARAM_DSS_PCD); + pcd_max = dss_feat_get_param_max(FEAT_PARAM_DSS_PCD); + + if (!is_tft) + pcd_min = 3; + best_pck = 0; best_ld = 0; best_pd = 0; @@ -3007,7 +2813,7 @@ void dispc_find_clk_divs(bool is_tft, unsigned long req_pck, unsigned long fck, for (cur_ld = 1; cur_ld <= 255; ++cur_ld) { unsigned long lck = fck / cur_ld; - for (cur_pd = pcd_min; cur_pd <= 255; ++cur_pd) { + for (cur_pd = pcd_min; cur_pd <= pcd_max; ++cur_pd) { unsigned long pck = lck / cur_pd; long old_delta = abs(best_pck - req_pck); long new_delta = abs(pck - req_pck); @@ -3042,7 +2848,7 @@ int dispc_calc_clock_rates(unsigned long dispc_fclk_rate, { if (cinfo->lck_div > 255 || cinfo->lck_div == 0) return -EINVAL; - if (cinfo->pck_div < 2 || cinfo->pck_div > 255) + if (cinfo->pck_div < 1 || cinfo->pck_div > 255) return -EINVAL; cinfo->lck = dispc_fclk_rate / cinfo->lck_div; @@ -3051,18 +2857,18 @@ int dispc_calc_clock_rates(unsigned long dispc_fclk_rate, return 0; } -int dispc_set_clock_div(enum omap_channel channel, +int dispc_mgr_set_clock_div(enum omap_channel channel, struct dispc_clock_info *cinfo) { DSSDBG("lck = %lu (%u)\n", cinfo->lck, cinfo->lck_div); DSSDBG("pck = %lu (%u)\n", cinfo->pck, cinfo->pck_div); - dispc_set_lcd_divisor(channel, cinfo->lck_div, cinfo->pck_div); + dispc_mgr_set_lcd_divisor(channel, cinfo->lck_div, cinfo->pck_div); return 0; } -int dispc_get_clock_div(enum omap_channel channel, +int dispc_mgr_get_clock_div(enum omap_channel channel, struct dispc_clock_info *cinfo) { unsigned long fck; @@ -3207,6 +3013,8 @@ static void print_irq_status(u32 status) PIS(OCP_ERR); PIS(VID1_FIFO_UNDERFLOW); PIS(VID2_FIFO_UNDERFLOW); + if (dss_feat_get_num_ovls() > 3) + PIS(VID3_FIFO_UNDERFLOW); PIS(SYNC_LOST); PIS(SYNC_LOST_DIGIT); if (dss_has_feature(FEAT_MGR_LCD2)) @@ -3300,178 +3108,72 @@ static void dispc_error_worker(struct work_struct *work) int i; u32 errors; unsigned long flags; + static const unsigned fifo_underflow_bits[] = { + DISPC_IRQ_GFX_FIFO_UNDERFLOW, + DISPC_IRQ_VID1_FIFO_UNDERFLOW, + DISPC_IRQ_VID2_FIFO_UNDERFLOW, + DISPC_IRQ_VID3_FIFO_UNDERFLOW, + }; + + static const unsigned sync_lost_bits[] = { + DISPC_IRQ_SYNC_LOST, + DISPC_IRQ_SYNC_LOST_DIGIT, + DISPC_IRQ_SYNC_LOST2, + }; spin_lock_irqsave(&dispc.irq_lock, flags); errors = dispc.error_irqs; dispc.error_irqs = 0; spin_unlock_irqrestore(&dispc.irq_lock, flags); - if (errors & DISPC_IRQ_GFX_FIFO_UNDERFLOW) { - DSSERR("GFX_FIFO_UNDERFLOW, disabling GFX\n"); - for (i = 0; i < omap_dss_get_num_overlays(); ++i) { - struct omap_overlay *ovl; - ovl = omap_dss_get_overlay(i); - - if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) - continue; - - if (ovl->id == 0) { - dispc_enable_plane(ovl->id, 0); - dispc_go(ovl->manager->id); - mdelay(50); - break; - } - } - } - - if (errors & DISPC_IRQ_VID1_FIFO_UNDERFLOW) { - DSSERR("VID1_FIFO_UNDERFLOW, disabling VID1\n"); - for (i = 0; i < omap_dss_get_num_overlays(); ++i) { - struct omap_overlay *ovl; - ovl = omap_dss_get_overlay(i); - - if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) - continue; - - if (ovl->id == 1) { - dispc_enable_plane(ovl->id, 0); - dispc_go(ovl->manager->id); - mdelay(50); - break; - } - } - } - - if (errors & DISPC_IRQ_VID2_FIFO_UNDERFLOW) { - DSSERR("VID2_FIFO_UNDERFLOW, disabling VID2\n"); - for (i = 0; i < omap_dss_get_num_overlays(); ++i) { - struct omap_overlay *ovl; - ovl = omap_dss_get_overlay(i); - - if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) - continue; + dispc_runtime_get(); - if (ovl->id == 2) { - dispc_enable_plane(ovl->id, 0); - dispc_go(ovl->manager->id); - mdelay(50); - break; - } - } - } - - if (errors & DISPC_IRQ_SYNC_LOST) { - struct omap_overlay_manager *manager = NULL; - bool enable = false; + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { + struct omap_overlay *ovl; + unsigned bit; - DSSERR("SYNC_LOST, disabling LCD\n"); + ovl = omap_dss_get_overlay(i); + bit = fifo_underflow_bits[i]; - for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { - struct omap_overlay_manager *mgr; - mgr = omap_dss_get_overlay_manager(i); - - if (mgr->id == OMAP_DSS_CHANNEL_LCD) { - manager = mgr; - enable = mgr->device->state == - OMAP_DSS_DISPLAY_ACTIVE; - mgr->device->driver->disable(mgr->device); - break; - } - } - - if (manager) { - struct omap_dss_device *dssdev = manager->device; - for (i = 0; i < omap_dss_get_num_overlays(); ++i) { - struct omap_overlay *ovl; - ovl = omap_dss_get_overlay(i); - - if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) - continue; - - if (ovl->id != 0 && ovl->manager == manager) - dispc_enable_plane(ovl->id, 0); - } - - dispc_go(manager->id); + if (bit & errors) { + DSSERR("FIFO UNDERFLOW on %s, disabling the overlay\n", + ovl->name); + dispc_ovl_enable(ovl->id, false); + dispc_mgr_go(ovl->manager->id); mdelay(50); - if (enable) - dssdev->driver->enable(dssdev); } } - if (errors & DISPC_IRQ_SYNC_LOST_DIGIT) { - struct omap_overlay_manager *manager = NULL; - bool enable = false; + for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { + struct omap_overlay_manager *mgr; + unsigned bit; - DSSERR("SYNC_LOST_DIGIT, disabling TV\n"); + mgr = omap_dss_get_overlay_manager(i); + bit = sync_lost_bits[i]; - for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { - struct omap_overlay_manager *mgr; - mgr = omap_dss_get_overlay_manager(i); + if (bit & errors) { + struct omap_dss_device *dssdev = mgr->device; + bool enable; - if (mgr->id == OMAP_DSS_CHANNEL_DIGIT) { - manager = mgr; - enable = mgr->device->state == - OMAP_DSS_DISPLAY_ACTIVE; - mgr->device->driver->disable(mgr->device); - break; - } - } + DSSERR("SYNC_LOST on channel %s, restarting the output " + "with video overlays disabled\n", + mgr->name); - if (manager) { - struct omap_dss_device *dssdev = manager->device; - for (i = 0; i < omap_dss_get_num_overlays(); ++i) { - struct omap_overlay *ovl; - ovl = omap_dss_get_overlay(i); - - if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) - continue; - - if (ovl->id != 0 && ovl->manager == manager) - dispc_enable_plane(ovl->id, 0); - } + enable = dssdev->state == OMAP_DSS_DISPLAY_ACTIVE; + dssdev->driver->disable(dssdev); - dispc_go(manager->id); - mdelay(50); - if (enable) - dssdev->driver->enable(dssdev); - } - } - - if (errors & DISPC_IRQ_SYNC_LOST2) { - struct omap_overlay_manager *manager = NULL; - bool enable = false; - - DSSERR("SYNC_LOST for LCD2, disabling LCD2\n"); - - for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { - struct omap_overlay_manager *mgr; - mgr = omap_dss_get_overlay_manager(i); - - if (mgr->id == OMAP_DSS_CHANNEL_LCD2) { - manager = mgr; - enable = mgr->device->state == - OMAP_DSS_DISPLAY_ACTIVE; - mgr->device->driver->disable(mgr->device); - break; - } - } - - if (manager) { - struct omap_dss_device *dssdev = manager->device; for (i = 0; i < omap_dss_get_num_overlays(); ++i) { struct omap_overlay *ovl; ovl = omap_dss_get_overlay(i); - if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) - continue; - - if (ovl->id != 0 && ovl->manager == manager) - dispc_enable_plane(ovl->id, 0); + if (ovl->id != OMAP_DSS_GFX && + ovl->manager == mgr) + dispc_ovl_enable(ovl->id, false); } - dispc_go(manager->id); + dispc_mgr_go(mgr->id); mdelay(50); + if (enable) dssdev->driver->enable(dssdev); } @@ -3482,9 +3184,7 @@ static void dispc_error_worker(struct work_struct *work) for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { struct omap_overlay_manager *mgr; mgr = omap_dss_get_overlay_manager(i); - - if (mgr->caps & OMAP_DSS_OVL_CAP_DISPC) - mgr->device->driver->disable(mgr->device); + mgr->device->driver->disable(mgr->device); } } @@ -3492,6 +3192,8 @@ static void dispc_error_worker(struct work_struct *work) dispc.irq_error_mask |= errors; _omap_dispc_set_irqs(); spin_unlock_irqrestore(&dispc.irq_lock, flags); + + dispc_runtime_put(); } int omap_dispc_wait_for_irq_timeout(u32 irqmask, unsigned long timeout) @@ -3586,6 +3288,8 @@ static void _omap_dispc_initialize_irq(void) dispc.irq_error_mask = DISPC_IRQ_MASK_ERROR; if (dss_has_feature(FEAT_MGR_LCD2)) dispc.irq_error_mask |= DISPC_IRQ_SYNC_LOST2; + if (dss_feat_get_num_ovls() > 3) + dispc.irq_error_mask |= DISPC_IRQ_VID3_FIFO_UNDERFLOW; /* there's SYNC_LOST_DIGIT waiting after enabling the DSS, * so clear it */ @@ -3635,6 +3339,8 @@ static void _omap_dispc_initial_config(void) dispc_read_plane_fifo_sizes(); dispc_configure_burst_sizes(); + + dispc_ovl_enable_zorder_planes(); } /* DISPC HW IP initialisation */ @@ -3734,7 +3440,6 @@ static int omap_dispchw_remove(struct platform_device *pdev) static int dispc_runtime_suspend(struct device *dev) { dispc_save_context(); - clk_disable(dispc.dss_clk); dss_runtime_put(); return 0; @@ -3748,7 +3453,6 @@ static int dispc_runtime_resume(struct device *dev) if (r < 0) return r; - clk_enable(dispc.dss_clk); dispc_restore_context(); return 0; diff --git a/drivers/video/omap2/dss/dispc.h b/drivers/video/omap2/dss/dispc.h index 6c9ee0a0efb3..c06efc38983e 100644 --- a/drivers/video/omap2/dss/dispc.h +++ b/drivers/video/omap2/dss/dispc.h @@ -291,6 +291,8 @@ static inline u16 DISPC_OVL_BASE(enum omap_plane plane) return 0x00BC; case OMAP_DSS_VIDEO2: return 0x014C; + case OMAP_DSS_VIDEO3: + return 0x0300; default: BUG(); } @@ -304,6 +306,8 @@ static inline u16 DISPC_BA0_OFFSET(enum omap_plane plane) case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: return 0x0000; + case OMAP_DSS_VIDEO3: + return 0x0008; default: BUG(); } @@ -316,6 +320,8 @@ static inline u16 DISPC_BA1_OFFSET(enum omap_plane plane) case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: return 0x0004; + case OMAP_DSS_VIDEO3: + return 0x000C; default: BUG(); } @@ -330,6 +336,8 @@ static inline u16 DISPC_BA0_UV_OFFSET(enum omap_plane plane) return 0x0544; case OMAP_DSS_VIDEO2: return 0x04BC; + case OMAP_DSS_VIDEO3: + return 0x0310; default: BUG(); } @@ -344,6 +352,8 @@ static inline u16 DISPC_BA1_UV_OFFSET(enum omap_plane plane) return 0x0548; case OMAP_DSS_VIDEO2: return 0x04C0; + case OMAP_DSS_VIDEO3: + return 0x0314; default: BUG(); } @@ -356,6 +366,8 @@ static inline u16 DISPC_POS_OFFSET(enum omap_plane plane) case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: return 0x0008; + case OMAP_DSS_VIDEO3: + return 0x009C; default: BUG(); } @@ -368,6 +380,8 @@ static inline u16 DISPC_SIZE_OFFSET(enum omap_plane plane) case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: return 0x000C; + case OMAP_DSS_VIDEO3: + return 0x00A8; default: BUG(); } @@ -381,6 +395,8 @@ static inline u16 DISPC_ATTR_OFFSET(enum omap_plane plane) case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: return 0x0010; + case OMAP_DSS_VIDEO3: + return 0x0070; default: BUG(); } @@ -395,6 +411,8 @@ static inline u16 DISPC_ATTR2_OFFSET(enum omap_plane plane) return 0x0568; case OMAP_DSS_VIDEO2: return 0x04DC; + case OMAP_DSS_VIDEO3: + return 0x032C; default: BUG(); } @@ -408,6 +426,8 @@ static inline u16 DISPC_FIFO_THRESH_OFFSET(enum omap_plane plane) case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: return 0x0014; + case OMAP_DSS_VIDEO3: + return 0x008C; default: BUG(); } @@ -421,6 +441,8 @@ static inline u16 DISPC_FIFO_SIZE_STATUS_OFFSET(enum omap_plane plane) case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: return 0x0018; + case OMAP_DSS_VIDEO3: + return 0x0088; default: BUG(); } @@ -434,6 +456,8 @@ static inline u16 DISPC_ROW_INC_OFFSET(enum omap_plane plane) case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: return 0x001C; + case OMAP_DSS_VIDEO3: + return 0x00A4; default: BUG(); } @@ -447,6 +471,8 @@ static inline u16 DISPC_PIX_INC_OFFSET(enum omap_plane plane) case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: return 0x0020; + case OMAP_DSS_VIDEO3: + return 0x0098; default: BUG(); } @@ -459,6 +485,7 @@ static inline u16 DISPC_WINDOW_SKIP_OFFSET(enum omap_plane plane) return 0x0034; case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: + case OMAP_DSS_VIDEO3: BUG(); default: BUG(); @@ -472,6 +499,7 @@ static inline u16 DISPC_TABLE_BA_OFFSET(enum omap_plane plane) return 0x0038; case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: + case OMAP_DSS_VIDEO3: BUG(); default: BUG(); @@ -486,6 +514,8 @@ static inline u16 DISPC_FIR_OFFSET(enum omap_plane plane) case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: return 0x0024; + case OMAP_DSS_VIDEO3: + return 0x0090; default: BUG(); } @@ -500,6 +530,8 @@ static inline u16 DISPC_FIR2_OFFSET(enum omap_plane plane) return 0x0580; case OMAP_DSS_VIDEO2: return 0x055C; + case OMAP_DSS_VIDEO3: + return 0x0424; default: BUG(); } @@ -513,6 +545,8 @@ static inline u16 DISPC_PIC_SIZE_OFFSET(enum omap_plane plane) case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: return 0x0028; + case OMAP_DSS_VIDEO3: + return 0x0094; default: BUG(); } @@ -527,6 +561,8 @@ static inline u16 DISPC_ACCU0_OFFSET(enum omap_plane plane) case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: return 0x002C; + case OMAP_DSS_VIDEO3: + return 0x0000; default: BUG(); } @@ -541,6 +577,8 @@ static inline u16 DISPC_ACCU2_0_OFFSET(enum omap_plane plane) return 0x0584; case OMAP_DSS_VIDEO2: return 0x0560; + case OMAP_DSS_VIDEO3: + return 0x0428; default: BUG(); } @@ -554,6 +592,8 @@ static inline u16 DISPC_ACCU1_OFFSET(enum omap_plane plane) case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: return 0x0030; + case OMAP_DSS_VIDEO3: + return 0x0004; default: BUG(); } @@ -568,6 +608,8 @@ static inline u16 DISPC_ACCU2_1_OFFSET(enum omap_plane plane) return 0x0588; case OMAP_DSS_VIDEO2: return 0x0564; + case OMAP_DSS_VIDEO3: + return 0x042C; default: BUG(); } @@ -582,6 +624,8 @@ static inline u16 DISPC_FIR_COEF_H_OFFSET(enum omap_plane plane, u16 i) case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: return 0x0034 + i * 0x8; + case OMAP_DSS_VIDEO3: + return 0x0010 + i * 0x8; default: BUG(); } @@ -597,6 +641,8 @@ static inline u16 DISPC_FIR_COEF_H2_OFFSET(enum omap_plane plane, u16 i) return 0x058C + i * 0x8; case OMAP_DSS_VIDEO2: return 0x0568 + i * 0x8; + case OMAP_DSS_VIDEO3: + return 0x0430 + i * 0x8; default: BUG(); } @@ -611,6 +657,8 @@ static inline u16 DISPC_FIR_COEF_HV_OFFSET(enum omap_plane plane, u16 i) case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: return 0x0038 + i * 0x8; + case OMAP_DSS_VIDEO3: + return 0x0014 + i * 0x8; default: BUG(); } @@ -626,6 +674,8 @@ static inline u16 DISPC_FIR_COEF_HV2_OFFSET(enum omap_plane plane, u16 i) return 0x0590 + i * 8; case OMAP_DSS_VIDEO2: return 0x056C + i * 0x8; + case OMAP_DSS_VIDEO3: + return 0x0434 + i * 0x8; default: BUG(); } @@ -639,6 +689,7 @@ static inline u16 DISPC_CONV_COEF_OFFSET(enum omap_plane plane, u16 i) BUG(); case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: + case OMAP_DSS_VIDEO3: return 0x0074 + i * 0x4; default: BUG(); @@ -655,6 +706,8 @@ static inline u16 DISPC_FIR_COEF_V_OFFSET(enum omap_plane plane, u16 i) return 0x0124 + i * 0x4; case OMAP_DSS_VIDEO2: return 0x00B4 + i * 0x4; + case OMAP_DSS_VIDEO3: + return 0x0050 + i * 0x4; default: BUG(); } @@ -670,6 +723,8 @@ static inline u16 DISPC_FIR_COEF_V2_OFFSET(enum omap_plane plane, u16 i) return 0x05CC + i * 0x4; case OMAP_DSS_VIDEO2: return 0x05A8 + i * 0x4; + case OMAP_DSS_VIDEO3: + return 0x0470 + i * 0x4; default: BUG(); } @@ -684,6 +739,8 @@ static inline u16 DISPC_PRELOAD_OFFSET(enum omap_plane plane) return 0x0174; case OMAP_DSS_VIDEO2: return 0x00E8; + case OMAP_DSS_VIDEO3: + return 0x00A0; default: BUG(); } diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c index 94495e45ec5a..be331dc5a61b 100644 --- a/drivers/video/omap2/dss/display.c +++ b/drivers/video/omap2/dss/display.c @@ -45,14 +45,13 @@ static ssize_t display_enabled_store(struct device *dev, const char *buf, size_t size) { struct omap_dss_device *dssdev = to_dss_device(dev); - int r, enabled; + int r; + bool enabled; - r = kstrtoint(buf, 0, &enabled); + r = strtobool(buf, &enabled); if (r) return r; - enabled = !!enabled; - if (enabled != (dssdev->state != OMAP_DSS_DISPLAY_DISABLED)) { if (enabled) { r = dssdev->driver->enable(dssdev); @@ -79,17 +78,16 @@ static ssize_t display_tear_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct omap_dss_device *dssdev = to_dss_device(dev); - int te, r; + int r; + bool te; if (!dssdev->driver->enable_te || !dssdev->driver->get_te) return -ENOENT; - r = kstrtoint(buf, 0, &te); + r = strtobool(buf, &te); if (r) return r; - te = !!te; - r = dssdev->driver->enable_te(dssdev, te); if (r) return r; @@ -195,17 +193,16 @@ static ssize_t display_mirror_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct omap_dss_device *dssdev = to_dss_device(dev); - int mirror, r; + int r; + bool mirror; if (!dssdev->driver->set_mirror || !dssdev->driver->get_mirror) return -ENOENT; - r = kstrtoint(buf, 0, &mirror); + r = strtobool(buf, &mirror); if (r) return r; - mirror = !!mirror; - r = dssdev->driver->set_mirror(dssdev, mirror); if (r) return r; @@ -302,11 +299,15 @@ int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev) return 16; case OMAP_DISPLAY_TYPE_DBI: - case OMAP_DISPLAY_TYPE_DSI: if (dssdev->ctrl.pixel_size == 24) return 24; else return 16; + case OMAP_DISPLAY_TYPE_DSI: + if (dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt) > 16) + return 24; + else + return 16; case OMAP_DISPLAY_TYPE_VENC: case OMAP_DISPLAY_TYPE_SDI: case OMAP_DISPLAY_TYPE_HDMI: @@ -342,9 +343,11 @@ bool dss_use_replication(struct omap_dss_device *dssdev, bpp = 24; break; case OMAP_DISPLAY_TYPE_DBI: - case OMAP_DISPLAY_TYPE_DSI: bpp = dssdev->ctrl.pixel_size; break; + case OMAP_DISPLAY_TYPE_DSI: + bpp = dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt); + break; default: BUG(); } diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c index f053b180ecd7..483888a85cfd 100644 --- a/drivers/video/omap2/dss/dpi.c +++ b/drivers/video/omap2/dss/dpi.c @@ -82,9 +82,11 @@ static int dpi_set_dsi_clk(struct omap_dss_device *dssdev, bool is_tft, dss_select_dispc_clk_source(dssdev->clocks.dispc.dispc_fclk_src); - r = dispc_set_clock_div(dssdev->manager->id, &dispc_cinfo); - if (r) + r = dispc_mgr_set_clock_div(dssdev->manager->id, &dispc_cinfo); + if (r) { + dss_select_dispc_clk_source(OMAP_DSS_CLK_SRC_FCK); return r; + } *fck = dsi_cinfo.dsi_pll_hsdiv_dispc_clk; *lck_div = dispc_cinfo.lck_div; @@ -109,7 +111,7 @@ static int dpi_set_dispc_clk(struct omap_dss_device *dssdev, bool is_tft, if (r) return r; - r = dispc_set_clock_div(dssdev->manager->id, &dispc_cinfo); + r = dispc_mgr_set_clock_div(dssdev->manager->id, &dispc_cinfo); if (r) return r; @@ -129,7 +131,7 @@ static int dpi_set_mode(struct omap_dss_device *dssdev) bool is_tft; int r = 0; - dispc_set_pol_freq(dssdev->manager->id, dssdev->panel.config, + dispc_mgr_set_pol_freq(dssdev->manager->id, dssdev->panel.config, dssdev->panel.acbi, dssdev->panel.acb); is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0; @@ -153,7 +155,7 @@ static int dpi_set_mode(struct omap_dss_device *dssdev) t->pixel_clock = pck; } - dispc_set_lcd_timings(dssdev->manager->id, t); + dispc_mgr_set_lcd_timings(dssdev->manager->id, t); return 0; } @@ -164,11 +166,12 @@ static void dpi_basic_init(struct omap_dss_device *dssdev) is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0; - dispc_set_parallel_interface_mode(dssdev->manager->id, - OMAP_DSS_PARALLELMODE_BYPASS); - dispc_set_lcd_display_type(dssdev->manager->id, is_tft ? + dispc_mgr_set_io_pad_mode(DSS_IO_PAD_MODE_BYPASS); + dispc_mgr_enable_stallmode(dssdev->manager->id, false); + + dispc_mgr_set_lcd_display_type(dssdev->manager->id, is_tft ? OMAP_DSS_LCD_DISPLAY_TFT : OMAP_DSS_LCD_DISPLAY_STN); - dispc_set_tft_data_lines(dssdev->manager->id, + dispc_mgr_set_tft_data_lines(dssdev->manager->id, dssdev->phy.dpi.data_lines); } @@ -176,6 +179,11 @@ int omapdss_dpi_display_enable(struct omap_dss_device *dssdev) { int r; + if (dssdev->manager == NULL) { + DSSERR("failed to enable display: no manager\n"); + return -ENODEV; + } + r = omap_dss_start_device(dssdev); if (r) { DSSERR("failed to start device\n"); @@ -277,7 +285,7 @@ void dpi_set_timings(struct omap_dss_device *dssdev, } dpi_set_mode(dssdev); - dispc_go(dssdev->manager->id); + dispc_mgr_go(dssdev->manager->id); dispc_runtime_put(); dss_runtime_put(); diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c index 7adbbeb84334..43c04a9889c4 100644 --- a/drivers/video/omap2/dss/dsi.c +++ b/drivers/video/omap2/dss/dsi.c @@ -39,6 +39,7 @@ #include <linux/pm_runtime.h> #include <video/omapdss.h> +#include <video/mipi_display.h> #include <plat/clock.h> #include "dss.h" @@ -131,7 +132,7 @@ struct dsi_reg { u16 idx; }; #define DSI_IRQ_TA_TIMEOUT (1 << 20) #define DSI_IRQ_ERROR_MASK \ (DSI_IRQ_HS_TX_TIMEOUT | DSI_IRQ_LP_RX_TIMEOUT | DSI_IRQ_SYNC_LOST | \ - DSI_IRQ_TA_TIMEOUT) + DSI_IRQ_TA_TIMEOUT | DSI_IRQ_SYNC_LOST) #define DSI_IRQ_CHANNEL_MASK 0xf /* Virtual channel interrupts */ @@ -198,18 +199,6 @@ struct dsi_reg { u16 idx; }; DSI_CIO_IRQ_ERRCONTENTIONLP0_4 | DSI_CIO_IRQ_ERRCONTENTIONLP1_4 | \ DSI_CIO_IRQ_ERRCONTENTIONLP0_5 | DSI_CIO_IRQ_ERRCONTENTIONLP1_5) -#define DSI_DT_DCS_SHORT_WRITE_0 0x05 -#define DSI_DT_DCS_SHORT_WRITE_1 0x15 -#define DSI_DT_DCS_READ 0x06 -#define DSI_DT_SET_MAX_RET_PKG_SIZE 0x37 -#define DSI_DT_NULL_PACKET 0x09 -#define DSI_DT_DCS_LONG_WRITE 0x39 - -#define DSI_DT_RX_ACK_WITH_ERR 0x02 -#define DSI_DT_RX_DCS_LONG_READ 0x1c -#define DSI_DT_RX_SHORT_READ_1 0x21 -#define DSI_DT_RX_SHORT_READ_2 0x22 - typedef void (*omap_dsi_isr_t) (void *arg, u32 mask); #define DSI_MAX_NR_ISRS 2 @@ -228,9 +217,9 @@ enum fifo_size { DSI_FIFO_SIZE_128 = 4, }; -enum dsi_vc_mode { - DSI_VC_MODE_L4 = 0, - DSI_VC_MODE_VP, +enum dsi_vc_source { + DSI_VC_SOURCE_L4 = 0, + DSI_VC_SOURCE_VP, }; enum dsi_lane { @@ -274,7 +263,8 @@ struct dsi_data { struct clk *dss_clk; struct clk *sys_clk; - void (*dsi_mux_pads)(bool enable); + int (*enable_pads)(int dsi_id, unsigned lane_mask); + void (*disable_pads)(int dsi_id, unsigned lane_mask); struct dsi_clock_info current_cinfo; @@ -282,7 +272,7 @@ struct dsi_data { struct regulator *vdds_dsi_reg; struct { - enum dsi_vc_mode mode; + enum dsi_vc_source source; struct omap_dss_device *dssdev; enum fifo_size fifo_size; int vc_id; @@ -368,14 +358,9 @@ struct platform_device *dsi_get_dsidev_from_id(int module) return dsi_pdev_map[module]; } -static int dsi_get_dsidev_id(struct platform_device *dsidev) +static inline int dsi_get_dsidev_id(struct platform_device *dsidev) { - /* TEMP: Pass 0 as the dsi module index till the time the dsi platform - * device names aren't changed to the form "omapdss_dsi.0", - * "omapdss_dsi.1" and so on */ - BUG_ON(dsidev->id != -1); - - return 0; + return dsidev->id; } static inline void dsi_write_reg(struct platform_device *dsidev, @@ -437,6 +422,21 @@ static inline int wait_for_bit_change(struct platform_device *dsidev, return value; } +u8 dsi_get_pixel_size(enum omap_dss_dsi_pixel_format fmt) +{ + switch (fmt) { + case OMAP_DSS_DSI_FMT_RGB888: + case OMAP_DSS_DSI_FMT_RGB666: + return 24; + case OMAP_DSS_DSI_FMT_RGB666_PACKED: + return 18; + case OMAP_DSS_DSI_FMT_RGB565: + return 16; + default: + BUG(); + } +} + #ifdef DEBUG static void dsi_perf_mark_setup(struct platform_device *dsidev) { @@ -453,6 +453,7 @@ static void dsi_perf_mark_start(struct platform_device *dsidev) static void dsi_perf_show(struct platform_device *dsidev, const char *name) { struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + struct omap_dss_device *dssdev = dsi->update_region.device; ktime_t t, setup_time, trans_time; u32 total_bytes; u32 setup_us, trans_us, total_us; @@ -476,7 +477,7 @@ static void dsi_perf_show(struct platform_device *dsidev, const char *name) total_bytes = dsi->update_region.w * dsi->update_region.h * - dsi->update_region.device->ctrl.pixel_size / 8; + dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt) / 8; printk(KERN_INFO "DSI(%s): %u us + %u us = %u us (%uHz), " "%u bytes, %u kbytes/sec\n", @@ -1287,7 +1288,7 @@ static int dsi_calc_clock_rates(struct omap_dss_device *dssdev, * with DSS_SYS_CLK source also */ cinfo->highfreq = 0; } else { - cinfo->clkin = dispc_pclk_rate(dssdev->manager->id); + cinfo->clkin = dispc_mgr_pclk_rate(dssdev->manager->id); if (cinfo->clkin < 32000000) cinfo->highfreq = 0; @@ -2360,6 +2361,24 @@ static int dsi_cio_wait_tx_clk_esc_reset(struct omap_dss_device *dssdev) return 0; } +static unsigned dsi_get_lane_mask(struct omap_dss_device *dssdev) +{ + unsigned lanes = 0; + + if (dssdev->phy.dsi.clk_lane != 0) + lanes |= 1 << (dssdev->phy.dsi.clk_lane - 1); + if (dssdev->phy.dsi.data1_lane != 0) + lanes |= 1 << (dssdev->phy.dsi.data1_lane - 1); + if (dssdev->phy.dsi.data2_lane != 0) + lanes |= 1 << (dssdev->phy.dsi.data2_lane - 1); + if (dssdev->phy.dsi.data3_lane != 0) + lanes |= 1 << (dssdev->phy.dsi.data3_lane - 1); + if (dssdev->phy.dsi.data4_lane != 0) + lanes |= 1 << (dssdev->phy.dsi.data4_lane - 1); + + return lanes; +} + static int dsi_cio_init(struct omap_dss_device *dssdev) { struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); @@ -2370,8 +2389,9 @@ static int dsi_cio_init(struct omap_dss_device *dssdev) DSSDBGF(); - if (dsi->dsi_mux_pads) - dsi->dsi_mux_pads(true); + r = dsi->enable_pads(dsidev->id, dsi_get_lane_mask(dssdev)); + if (r) + return r; dsi_enable_scp_clk(dsidev); @@ -2452,6 +2472,12 @@ static int dsi_cio_init(struct omap_dss_device *dssdev) dsi_cio_timings(dsidev); + if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_VIDEO_MODE) { + /* DDR_CLK_ALWAYS_ON */ + REG_FLD_MOD(dsidev, DSI_CLK_CTRL, + dssdev->panel.dsi_vm_data.ddr_clk_always_on, 13, 13); + } + dsi->ulps_enabled = false; DSSDBG("CIO init done\n"); @@ -2467,19 +2493,21 @@ err_cio_pwr: dsi_cio_disable_lane_override(dsidev); err_scp_clk_dom: dsi_disable_scp_clk(dsidev); - if (dsi->dsi_mux_pads) - dsi->dsi_mux_pads(false); + dsi->disable_pads(dsidev->id, dsi_get_lane_mask(dssdev)); return r; } -static void dsi_cio_uninit(struct platform_device *dsidev) +static void dsi_cio_uninit(struct omap_dss_device *dssdev) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + /* DDR_CLK_ALWAYS_ON */ + REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 0, 13, 13); + dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_OFF); dsi_disable_scp_clk(dsidev); - if (dsi->dsi_mux_pads) - dsi->dsi_mux_pads(false); + dsi->disable_pads(dsidev->id, dsi_get_lane_mask(dssdev)); } static void dsi_config_tx_fifo(struct platform_device *dsidev, @@ -2669,10 +2697,10 @@ static int dsi_sync_vc(struct platform_device *dsidev, int channel) if (!dsi_vc_is_enabled(dsidev, channel)) return 0; - switch (dsi->vc[channel].mode) { - case DSI_VC_MODE_VP: + switch (dsi->vc[channel].source) { + case DSI_VC_SOURCE_VP: return dsi_sync_vc_vp(dsidev, channel); - case DSI_VC_MODE_L4: + case DSI_VC_SOURCE_L4: return dsi_sync_vc_l4(dsidev, channel); default: BUG(); @@ -2726,43 +2754,12 @@ static void dsi_vc_initial_config(struct platform_device *dsidev, int channel) dsi_write_reg(dsidev, DSI_VC_CTRL(channel), r); } -static int dsi_vc_config_l4(struct platform_device *dsidev, int channel) +static int dsi_vc_config_source(struct platform_device *dsidev, int channel, + enum dsi_vc_source source) { struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - if (dsi->vc[channel].mode == DSI_VC_MODE_L4) - return 0; - - DSSDBGF("%d", channel); - - dsi_sync_vc(dsidev, channel); - - dsi_vc_enable(dsidev, channel, 0); - - /* VC_BUSY */ - if (wait_for_bit_change(dsidev, DSI_VC_CTRL(channel), 15, 0) != 0) { - DSSERR("vc(%d) busy when trying to config for L4\n", channel); - return -EIO; - } - - REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 0, 1, 1); /* SOURCE, 0 = L4 */ - - /* DCS_CMD_ENABLE */ - if (dss_has_feature(FEAT_DSI_DCS_CMD_CONFIG_VC)) - REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 0, 30, 30); - - dsi_vc_enable(dsidev, channel, 1); - - dsi->vc[channel].mode = DSI_VC_MODE_L4; - - return 0; -} - -static int dsi_vc_config_vp(struct platform_device *dsidev, int channel) -{ - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - - if (dsi->vc[channel].mode == DSI_VC_MODE_VP) + if (dsi->vc[channel].source == source) return 0; DSSDBGF("%d", channel); @@ -2777,21 +2774,22 @@ static int dsi_vc_config_vp(struct platform_device *dsidev, int channel) return -EIO; } - /* SOURCE, 1 = video port */ - REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 1, 1, 1); + /* SOURCE, 0 = L4, 1 = video port */ + REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), source, 1, 1); /* DCS_CMD_ENABLE */ - if (dss_has_feature(FEAT_DSI_DCS_CMD_CONFIG_VC)) - REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 1, 30, 30); + if (dss_has_feature(FEAT_DSI_DCS_CMD_CONFIG_VC)) { + bool enable = source == DSI_VC_SOURCE_VP; + REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), enable, 30, 30); + } dsi_vc_enable(dsidev, channel, 1); - dsi->vc[channel].mode = DSI_VC_MODE_VP; + dsi->vc[channel].source = source; return 0; } - void omapdss_dsi_vc_enable_hs(struct omap_dss_device *dssdev, int channel, bool enable) { @@ -2810,6 +2808,10 @@ void omapdss_dsi_vc_enable_hs(struct omap_dss_device *dssdev, int channel, dsi_if_enable(dsidev, 1); dsi_force_tx_stop_mode_io(dsidev); + + /* start the DDR clock by sending a NULL packet */ + if (dssdev->panel.dsi_vm_data.ddr_clk_always_on && enable) + dsi_vc_send_null(dssdev, channel); } EXPORT_SYMBOL(omapdss_dsi_vc_enable_hs); @@ -2873,16 +2875,16 @@ static u16 dsi_vc_flush_receive_data(struct platform_device *dsidev, val = dsi_read_reg(dsidev, DSI_VC_SHORT_PACKET_HEADER(channel)); DSSERR("\trawval %#08x\n", val); dt = FLD_GET(val, 5, 0); - if (dt == DSI_DT_RX_ACK_WITH_ERR) { + if (dt == MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT) { u16 err = FLD_GET(val, 23, 8); dsi_show_rx_ack_with_err(err); - } else if (dt == DSI_DT_RX_SHORT_READ_1) { + } else if (dt == MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE) { DSSERR("\tDCS short response, 1 byte: %#x\n", FLD_GET(val, 23, 8)); - } else if (dt == DSI_DT_RX_SHORT_READ_2) { + } else if (dt == MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE) { DSSERR("\tDCS short response, 2 byte: %#x\n", FLD_GET(val, 23, 8)); - } else if (dt == DSI_DT_RX_DCS_LONG_READ) { + } else if (dt == MIPI_DSI_RX_DCS_LONG_READ_RESPONSE) { DSSERR("\tDCS long response, len %d\n", FLD_GET(val, 23, 8)); dsi_vc_flush_long_data(dsidev, channel); @@ -3007,7 +3009,7 @@ static int dsi_vc_send_long(struct platform_device *dsidev, int channel, return -EINVAL; } - dsi_vc_config_l4(dsidev, channel); + dsi_vc_config_source(dsidev, channel, DSI_VC_SOURCE_L4); dsi_vc_write_long_header(dsidev, channel, data_type, len, ecc); @@ -3066,7 +3068,7 @@ static int dsi_vc_send_short(struct platform_device *dsidev, int channel, channel, data_type, data & 0xff, (data >> 8) & 0xff); - dsi_vc_config_l4(dsidev, channel); + dsi_vc_config_source(dsidev, channel, DSI_VC_SOURCE_L4); if (FLD_GET(dsi_read_reg(dsidev, DSI_VC_CTRL(channel)), 16, 16)) { DSSERR("ERROR FIFO FULL, aborting transfer\n"); @@ -3085,44 +3087,66 @@ static int dsi_vc_send_short(struct platform_device *dsidev, int channel, int dsi_vc_send_null(struct omap_dss_device *dssdev, int channel) { struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); - u8 nullpkg[] = {0, 0, 0, 0}; - return dsi_vc_send_long(dsidev, channel, DSI_DT_NULL_PACKET, nullpkg, - 4, 0); + return dsi_vc_send_long(dsidev, channel, MIPI_DSI_NULL_PACKET, NULL, + 0, 0); } EXPORT_SYMBOL(dsi_vc_send_null); -int dsi_vc_dcs_write_nosync(struct omap_dss_device *dssdev, int channel, - u8 *data, int len) +static int dsi_vc_write_nosync_common(struct omap_dss_device *dssdev, + int channel, u8 *data, int len, enum dss_dsi_content_type type) { struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); int r; - BUG_ON(len == 0); - - if (len == 1) { - r = dsi_vc_send_short(dsidev, channel, DSI_DT_DCS_SHORT_WRITE_0, - data[0], 0); + if (len == 0) { + BUG_ON(type == DSS_DSI_CONTENT_DCS); + r = dsi_vc_send_short(dsidev, channel, + MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM, 0, 0); + } else if (len == 1) { + r = dsi_vc_send_short(dsidev, channel, + type == DSS_DSI_CONTENT_GENERIC ? + MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM : + MIPI_DSI_DCS_SHORT_WRITE, data[0], 0); } else if (len == 2) { - r = dsi_vc_send_short(dsidev, channel, DSI_DT_DCS_SHORT_WRITE_1, + r = dsi_vc_send_short(dsidev, channel, + type == DSS_DSI_CONTENT_GENERIC ? + MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM : + MIPI_DSI_DCS_SHORT_WRITE_PARAM, data[0] | (data[1] << 8), 0); } else { - /* 0x39 = DCS Long Write */ - r = dsi_vc_send_long(dsidev, channel, DSI_DT_DCS_LONG_WRITE, - data, len, 0); + r = dsi_vc_send_long(dsidev, channel, + type == DSS_DSI_CONTENT_GENERIC ? + MIPI_DSI_GENERIC_LONG_WRITE : + MIPI_DSI_DCS_LONG_WRITE, data, len, 0); } return r; } + +int dsi_vc_dcs_write_nosync(struct omap_dss_device *dssdev, int channel, + u8 *data, int len) +{ + return dsi_vc_write_nosync_common(dssdev, channel, data, len, + DSS_DSI_CONTENT_DCS); +} EXPORT_SYMBOL(dsi_vc_dcs_write_nosync); -int dsi_vc_dcs_write(struct omap_dss_device *dssdev, int channel, u8 *data, - int len) +int dsi_vc_generic_write_nosync(struct omap_dss_device *dssdev, int channel, + u8 *data, int len) +{ + return dsi_vc_write_nosync_common(dssdev, channel, data, len, + DSS_DSI_CONTENT_GENERIC); +} +EXPORT_SYMBOL(dsi_vc_generic_write_nosync); + +static int dsi_vc_write_common(struct omap_dss_device *dssdev, int channel, + u8 *data, int len, enum dss_dsi_content_type type) { struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); int r; - r = dsi_vc_dcs_write_nosync(dssdev, channel, data, len); + r = dsi_vc_write_nosync_common(dssdev, channel, data, len, type); if (r) goto err; @@ -3140,18 +3164,39 @@ int dsi_vc_dcs_write(struct omap_dss_device *dssdev, int channel, u8 *data, return 0; err: - DSSERR("dsi_vc_dcs_write(ch %d, cmd 0x%02x, len %d) failed\n", + DSSERR("dsi_vc_write_common(ch %d, cmd 0x%02x, len %d) failed\n", channel, data[0], len); return r; } + +int dsi_vc_dcs_write(struct omap_dss_device *dssdev, int channel, u8 *data, + int len) +{ + return dsi_vc_write_common(dssdev, channel, data, len, + DSS_DSI_CONTENT_DCS); +} EXPORT_SYMBOL(dsi_vc_dcs_write); +int dsi_vc_generic_write(struct omap_dss_device *dssdev, int channel, u8 *data, + int len) +{ + return dsi_vc_write_common(dssdev, channel, data, len, + DSS_DSI_CONTENT_GENERIC); +} +EXPORT_SYMBOL(dsi_vc_generic_write); + int dsi_vc_dcs_write_0(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd) { return dsi_vc_dcs_write(dssdev, channel, &dcs_cmd, 1); } EXPORT_SYMBOL(dsi_vc_dcs_write_0); +int dsi_vc_generic_write_0(struct omap_dss_device *dssdev, int channel) +{ + return dsi_vc_generic_write(dssdev, channel, NULL, 0); +} +EXPORT_SYMBOL(dsi_vc_generic_write_0); + int dsi_vc_dcs_write_1(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd, u8 param) { @@ -3162,25 +3207,87 @@ int dsi_vc_dcs_write_1(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd, } EXPORT_SYMBOL(dsi_vc_dcs_write_1); -int dsi_vc_dcs_read(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd, - u8 *buf, int buflen) +int dsi_vc_generic_write_1(struct omap_dss_device *dssdev, int channel, + u8 param) +{ + return dsi_vc_generic_write(dssdev, channel, ¶m, 1); +} +EXPORT_SYMBOL(dsi_vc_generic_write_1); + +int dsi_vc_generic_write_2(struct omap_dss_device *dssdev, int channel, + u8 param1, u8 param2) +{ + u8 buf[2]; + buf[0] = param1; + buf[1] = param2; + return dsi_vc_generic_write(dssdev, channel, buf, 2); +} +EXPORT_SYMBOL(dsi_vc_generic_write_2); + +static int dsi_vc_dcs_send_read_request(struct omap_dss_device *dssdev, + int channel, u8 dcs_cmd) { struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - u32 val; - u8 dt; int r; if (dsi->debug_read) - DSSDBG("dsi_vc_dcs_read(ch%d, dcs_cmd %x)\n", channel, dcs_cmd); + DSSDBG("dsi_vc_dcs_send_read_request(ch%d, dcs_cmd %x)\n", + channel, dcs_cmd); - r = dsi_vc_send_short(dsidev, channel, DSI_DT_DCS_READ, dcs_cmd, 0); - if (r) - goto err; + r = dsi_vc_send_short(dsidev, channel, MIPI_DSI_DCS_READ, dcs_cmd, 0); + if (r) { + DSSERR("dsi_vc_dcs_send_read_request(ch %d, cmd 0x%02x)" + " failed\n", channel, dcs_cmd); + return r; + } - r = dsi_vc_send_bta_sync(dssdev, channel); - if (r) - goto err; + return 0; +} + +static int dsi_vc_generic_send_read_request(struct omap_dss_device *dssdev, + int channel, u8 *reqdata, int reqlen) +{ + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + u16 data; + u8 data_type; + int r; + + if (dsi->debug_read) + DSSDBG("dsi_vc_generic_send_read_request(ch %d, reqlen %d)\n", + channel, reqlen); + + if (reqlen == 0) { + data_type = MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM; + data = 0; + } else if (reqlen == 1) { + data_type = MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM; + data = reqdata[0]; + } else if (reqlen == 2) { + data_type = MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM; + data = reqdata[0] | (reqdata[1] << 8); + } else { + BUG(); + } + + r = dsi_vc_send_short(dsidev, channel, data_type, data, 0); + if (r) { + DSSERR("dsi_vc_generic_send_read_request(ch %d, reqlen %d)" + " failed\n", channel, reqlen); + return r; + } + + return 0; +} + +static int dsi_vc_read_rx_fifo(struct platform_device *dsidev, int channel, + u8 *buf, int buflen, enum dss_dsi_content_type type) +{ + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + u32 val; + u8 dt; + int r; /* RX_FIFO_NOT_EMPTY */ if (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20) == 0) { @@ -3193,16 +3300,20 @@ int dsi_vc_dcs_read(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd, if (dsi->debug_read) DSSDBG("\theader: %08x\n", val); dt = FLD_GET(val, 5, 0); - if (dt == DSI_DT_RX_ACK_WITH_ERR) { + if (dt == MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT) { u16 err = FLD_GET(val, 23, 8); dsi_show_rx_ack_with_err(err); r = -EIO; goto err; - } else if (dt == DSI_DT_RX_SHORT_READ_1) { + } else if (dt == (type == DSS_DSI_CONTENT_GENERIC ? + MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE : + MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE)) { u8 data = FLD_GET(val, 15, 8); if (dsi->debug_read) - DSSDBG("\tDCS short response, 1 byte: %02x\n", data); + DSSDBG("\t%s short response, 1 byte: %02x\n", + type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" : + "DCS", data); if (buflen < 1) { r = -EIO; @@ -3212,10 +3323,14 @@ int dsi_vc_dcs_read(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd, buf[0] = data; return 1; - } else if (dt == DSI_DT_RX_SHORT_READ_2) { + } else if (dt == (type == DSS_DSI_CONTENT_GENERIC ? + MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE : + MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE)) { u16 data = FLD_GET(val, 23, 8); if (dsi->debug_read) - DSSDBG("\tDCS short response, 2 byte: %04x\n", data); + DSSDBG("\t%s short response, 2 byte: %04x\n", + type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" : + "DCS", data); if (buflen < 2) { r = -EIO; @@ -3226,11 +3341,15 @@ int dsi_vc_dcs_read(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd, buf[1] = (data >> 8) & 0xff; return 2; - } else if (dt == DSI_DT_RX_DCS_LONG_READ) { + } else if (dt == (type == DSS_DSI_CONTENT_GENERIC ? + MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE : + MIPI_DSI_RX_DCS_LONG_READ_RESPONSE)) { int w; int len = FLD_GET(val, 23, 8); if (dsi->debug_read) - DSSDBG("\tDCS long response, len %d\n", len); + DSSDBG("\t%s long response, len %d\n", + type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" : + "DCS", len); if (len > buflen) { r = -EIO; @@ -3266,58 +3385,126 @@ int dsi_vc_dcs_read(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd, BUG(); err: - DSSERR("dsi_vc_dcs_read(ch %d, cmd 0x%02x) failed\n", - channel, dcs_cmd); + DSSERR("dsi_vc_read_rx_fifo(ch %d type %s) failed\n", channel, + type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" : "DCS"); + return r; +} +int dsi_vc_dcs_read(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd, + u8 *buf, int buflen) +{ + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + int r; + + r = dsi_vc_dcs_send_read_request(dssdev, channel, dcs_cmd); + if (r) + goto err; + + r = dsi_vc_send_bta_sync(dssdev, channel); + if (r) + goto err; + + r = dsi_vc_read_rx_fifo(dsidev, channel, buf, buflen, + DSS_DSI_CONTENT_DCS); + if (r < 0) + goto err; + + if (r != buflen) { + r = -EIO; + goto err; + } + + return 0; +err: + DSSERR("dsi_vc_dcs_read(ch %d, cmd 0x%02x) failed\n", channel, dcs_cmd); + return r; } EXPORT_SYMBOL(dsi_vc_dcs_read); -int dsi_vc_dcs_read_1(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd, - u8 *data) +static int dsi_vc_generic_read(struct omap_dss_device *dssdev, int channel, + u8 *reqdata, int reqlen, u8 *buf, int buflen) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); int r; - r = dsi_vc_dcs_read(dssdev, channel, dcs_cmd, data, 1); + r = dsi_vc_generic_send_read_request(dssdev, channel, reqdata, reqlen); + if (r) + return r; + + r = dsi_vc_send_bta_sync(dssdev, channel); + if (r) + return r; + r = dsi_vc_read_rx_fifo(dsidev, channel, buf, buflen, + DSS_DSI_CONTENT_GENERIC); if (r < 0) return r; - if (r != 1) - return -EIO; + if (r != buflen) { + r = -EIO; + return r; + } return 0; } -EXPORT_SYMBOL(dsi_vc_dcs_read_1); -int dsi_vc_dcs_read_2(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd, - u8 *data1, u8 *data2) +int dsi_vc_generic_read_0(struct omap_dss_device *dssdev, int channel, u8 *buf, + int buflen) { - u8 buf[2]; int r; - r = dsi_vc_dcs_read(dssdev, channel, dcs_cmd, buf, 2); + r = dsi_vc_generic_read(dssdev, channel, NULL, 0, buf, buflen); + if (r) { + DSSERR("dsi_vc_generic_read_0(ch %d) failed\n", channel); + return r; + } - if (r < 0) + return 0; +} +EXPORT_SYMBOL(dsi_vc_generic_read_0); + +int dsi_vc_generic_read_1(struct omap_dss_device *dssdev, int channel, u8 param, + u8 *buf, int buflen) +{ + int r; + + r = dsi_vc_generic_read(dssdev, channel, ¶m, 1, buf, buflen); + if (r) { + DSSERR("dsi_vc_generic_read_1(ch %d) failed\n", channel); return r; + } - if (r != 2) - return -EIO; + return 0; +} +EXPORT_SYMBOL(dsi_vc_generic_read_1); - *data1 = buf[0]; - *data2 = buf[1]; +int dsi_vc_generic_read_2(struct omap_dss_device *dssdev, int channel, + u8 param1, u8 param2, u8 *buf, int buflen) +{ + int r; + u8 reqdata[2]; + + reqdata[0] = param1; + reqdata[1] = param2; + + r = dsi_vc_generic_read(dssdev, channel, reqdata, 2, buf, buflen); + if (r) { + DSSERR("dsi_vc_generic_read_2(ch %d) failed\n", channel); + return r; + } return 0; } -EXPORT_SYMBOL(dsi_vc_dcs_read_2); +EXPORT_SYMBOL(dsi_vc_generic_read_2); int dsi_vc_set_max_rx_packet_size(struct omap_dss_device *dssdev, int channel, u16 len) { struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); - return dsi_vc_send_short(dsidev, channel, DSI_DT_SET_MAX_RET_PKG_SIZE, - len, 0); + return dsi_vc_send_short(dsidev, channel, + MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, len, 0); } EXPORT_SYMBOL(dsi_vc_set_max_rx_packet_size); @@ -3508,6 +3695,75 @@ static void dsi_set_hs_tx_timeout(struct platform_device *dsidev, ticks, x4 ? " x4" : "", x16 ? " x16" : "", (total_ticks * 1000) / (fck / 1000 / 1000)); } + +static void dsi_config_vp_num_line_buffers(struct omap_dss_device *dssdev) +{ + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + int num_line_buffers; + + if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_VIDEO_MODE) { + int bpp = dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt); + unsigned line_buf_size = dsi_get_line_buf_size(dsidev); + struct omap_video_timings *timings = &dssdev->panel.timings; + /* + * Don't use line buffers if width is greater than the video + * port's line buffer size + */ + if (line_buf_size <= timings->x_res * bpp / 8) + num_line_buffers = 0; + else + num_line_buffers = 2; + } else { + /* Use maximum number of line buffers in command mode */ + num_line_buffers = 2; + } + + /* LINE_BUFFER */ + REG_FLD_MOD(dsidev, DSI_CTRL, num_line_buffers, 13, 12); +} + +static void dsi_config_vp_sync_events(struct omap_dss_device *dssdev) +{ + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + int de_pol = dssdev->panel.dsi_vm_data.vp_de_pol; + int hsync_pol = dssdev->panel.dsi_vm_data.vp_hsync_pol; + int vsync_pol = dssdev->panel.dsi_vm_data.vp_vsync_pol; + bool vsync_end = dssdev->panel.dsi_vm_data.vp_vsync_end; + bool hsync_end = dssdev->panel.dsi_vm_data.vp_hsync_end; + u32 r; + + r = dsi_read_reg(dsidev, DSI_CTRL); + r = FLD_MOD(r, de_pol, 9, 9); /* VP_DE_POL */ + r = FLD_MOD(r, hsync_pol, 10, 10); /* VP_HSYNC_POL */ + r = FLD_MOD(r, vsync_pol, 11, 11); /* VP_VSYNC_POL */ + r = FLD_MOD(r, 1, 15, 15); /* VP_VSYNC_START */ + r = FLD_MOD(r, vsync_end, 16, 16); /* VP_VSYNC_END */ + r = FLD_MOD(r, 1, 17, 17); /* VP_HSYNC_START */ + r = FLD_MOD(r, hsync_end, 18, 18); /* VP_HSYNC_END */ + dsi_write_reg(dsidev, DSI_CTRL, r); +} + +static void dsi_config_blanking_modes(struct omap_dss_device *dssdev) +{ + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + int blanking_mode = dssdev->panel.dsi_vm_data.blanking_mode; + int hfp_blanking_mode = dssdev->panel.dsi_vm_data.hfp_blanking_mode; + int hbp_blanking_mode = dssdev->panel.dsi_vm_data.hbp_blanking_mode; + int hsa_blanking_mode = dssdev->panel.dsi_vm_data.hsa_blanking_mode; + u32 r; + + /* + * 0 = TX FIFO packets sent or LPS in corresponding blanking periods + * 1 = Long blanking packets are sent in corresponding blanking periods + */ + r = dsi_read_reg(dsidev, DSI_CTRL); + r = FLD_MOD(r, blanking_mode, 20, 20); /* BLANKING_MODE */ + r = FLD_MOD(r, hfp_blanking_mode, 21, 21); /* HFP_BLANKING */ + r = FLD_MOD(r, hbp_blanking_mode, 22, 22); /* HBP_BLANKING */ + r = FLD_MOD(r, hsa_blanking_mode, 23, 23); /* HSA_BLANKING */ + dsi_write_reg(dsidev, DSI_CTRL, r); +} + static int dsi_proto_config(struct omap_dss_device *dssdev) { struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); @@ -3530,7 +3786,7 @@ static int dsi_proto_config(struct omap_dss_device *dssdev) dsi_set_lp_rx_timeout(dsidev, 0x1fff, true, true); dsi_set_hs_tx_timeout(dsidev, 0x1fff, true, true); - switch (dssdev->ctrl.pixel_size) { + switch (dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt)) { case 16: buswidth = 0; break; @@ -3551,7 +3807,6 @@ static int dsi_proto_config(struct omap_dss_device *dssdev) r = FLD_MOD(r, 1, 4, 4); /* VP_CLK_RATIO, always 1, see errata*/ r = FLD_MOD(r, buswidth, 7, 6); /* VP_DATA_BUS_WIDTH */ r = FLD_MOD(r, 0, 8, 8); /* VP_CLK_POL */ - r = FLD_MOD(r, 2, 13, 12); /* LINE_BUFFER, 2 lines */ r = FLD_MOD(r, 1, 14, 14); /* TRIGGER_RESET_MODE */ r = FLD_MOD(r, 1, 19, 19); /* EOT_ENABLE */ if (!dss_has_feature(FEAT_DSI_DCS_CMD_CONFIG_VC)) { @@ -3562,6 +3817,13 @@ static int dsi_proto_config(struct omap_dss_device *dssdev) dsi_write_reg(dsidev, DSI_CTRL, r); + dsi_config_vp_num_line_buffers(dssdev); + + if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_VIDEO_MODE) { + dsi_config_vp_sync_events(dssdev); + dsi_config_blanking_modes(dssdev); + } + dsi_vc_initial_config(dsidev, 0); dsi_vc_initial_config(dsidev, 1); dsi_vc_initial_config(dsidev, 2); @@ -3580,6 +3842,7 @@ static void dsi_proto_timings(struct omap_dss_device *dssdev) unsigned ddr_clk_pre, ddr_clk_post; unsigned enter_hs_mode_lat, exit_hs_mode_lat; unsigned ths_eot; + int ndl = dsi_get_num_data_lanes_dssdev(dssdev); u32 r; r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG0); @@ -3602,7 +3865,7 @@ static void dsi_proto_timings(struct omap_dss_device *dssdev) /* min 60ns + 52*UI */ tclk_post = ns2ddr(dsidev, 60) + 26; - ths_eot = DIV_ROUND_UP(4, dsi_get_num_data_lanes_dssdev(dssdev)); + ths_eot = DIV_ROUND_UP(4, ndl); ddr_clk_pre = DIV_ROUND_UP(tclk_pre + tlpx + tclk_zero + tclk_prepare, 4); @@ -3632,162 +3895,114 @@ static void dsi_proto_timings(struct omap_dss_device *dssdev) DSSDBG("enter_hs_mode_lat %u, exit_hs_mode_lat %u\n", enter_hs_mode_lat, exit_hs_mode_lat); -} - - -#define DSI_DECL_VARS \ - int __dsi_cb = 0; u32 __dsi_cv = 0; -#define DSI_FLUSH(dsidev, ch) \ - if (__dsi_cb > 0) { \ - /*DSSDBG("sending long packet %#010x\n", __dsi_cv);*/ \ - dsi_write_reg(dsidev, DSI_VC_LONG_PACKET_PAYLOAD(ch), __dsi_cv); \ - __dsi_cb = __dsi_cv = 0; \ + if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_VIDEO_MODE) { + /* TODO: Implement a video mode check_timings function */ + int hsa = dssdev->panel.dsi_vm_data.hsa; + int hfp = dssdev->panel.dsi_vm_data.hfp; + int hbp = dssdev->panel.dsi_vm_data.hbp; + int vsa = dssdev->panel.dsi_vm_data.vsa; + int vfp = dssdev->panel.dsi_vm_data.vfp; + int vbp = dssdev->panel.dsi_vm_data.vbp; + int window_sync = dssdev->panel.dsi_vm_data.window_sync; + bool hsync_end = dssdev->panel.dsi_vm_data.vp_hsync_end; + struct omap_video_timings *timings = &dssdev->panel.timings; + int bpp = dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt); + int tl, t_he, width_bytes; + + t_he = hsync_end ? + ((hsa == 0 && ndl == 3) ? 1 : DIV_ROUND_UP(4, ndl)) : 0; + + width_bytes = DIV_ROUND_UP(timings->x_res * bpp, 8); + + /* TL = t_HS + HSA + t_HE + HFP + ceil((WC + 6) / NDL) + HBP */ + tl = DIV_ROUND_UP(4, ndl) + (hsync_end ? hsa : 0) + t_he + hfp + + DIV_ROUND_UP(width_bytes + 6, ndl) + hbp; + + DSSDBG("HBP: %d, HFP: %d, HSA: %d, TL: %d TXBYTECLKHS\n", hbp, + hfp, hsync_end ? hsa : 0, tl); + DSSDBG("VBP: %d, VFP: %d, VSA: %d, VACT: %d lines\n", vbp, vfp, + vsa, timings->y_res); + + r = dsi_read_reg(dsidev, DSI_VM_TIMING1); + r = FLD_MOD(r, hbp, 11, 0); /* HBP */ + r = FLD_MOD(r, hfp, 23, 12); /* HFP */ + r = FLD_MOD(r, hsync_end ? hsa : 0, 31, 24); /* HSA */ + dsi_write_reg(dsidev, DSI_VM_TIMING1, r); + + r = dsi_read_reg(dsidev, DSI_VM_TIMING2); + r = FLD_MOD(r, vbp, 7, 0); /* VBP */ + r = FLD_MOD(r, vfp, 15, 8); /* VFP */ + r = FLD_MOD(r, vsa, 23, 16); /* VSA */ + r = FLD_MOD(r, window_sync, 27, 24); /* WINDOW_SYNC */ + dsi_write_reg(dsidev, DSI_VM_TIMING2, r); + + r = dsi_read_reg(dsidev, DSI_VM_TIMING3); + r = FLD_MOD(r, timings->y_res, 14, 0); /* VACT */ + r = FLD_MOD(r, tl, 31, 16); /* TL */ + dsi_write_reg(dsidev, DSI_VM_TIMING3, r); } +} -#define DSI_PUSH(dsidev, ch, data) \ - do { \ - __dsi_cv |= (data) << (__dsi_cb * 8); \ - /*DSSDBG("cv = %#010x, cb = %d\n", __dsi_cv, __dsi_cb);*/ \ - if (++__dsi_cb > 3) \ - DSI_FLUSH(dsidev, ch); \ - } while (0) - -static int dsi_update_screen_l4(struct omap_dss_device *dssdev, - int x, int y, int w, int h) +int dsi_video_mode_enable(struct omap_dss_device *dssdev, int channel) { - /* Note: supports only 24bit colors in 32bit container */ struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - int first = 1; - int fifo_stalls = 0; - int max_dsi_packet_size; - int max_data_per_packet; - int max_pixels_per_packet; - int pixels_left; - int bytespp = dssdev->ctrl.pixel_size / 8; - int scr_width; - u32 __iomem *data; - int start_offset; - int horiz_inc; - int current_x; - struct omap_overlay *ovl; - - debug_irq = 0; - - DSSDBG("dsi_update_screen_l4 (%d,%d %dx%d)\n", - x, y, w, h); + int bpp = dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt); + u8 data_type; + u16 word_count; - ovl = dssdev->manager->overlays[0]; - - if (ovl->info.color_mode != OMAP_DSS_COLOR_RGB24U) - return -EINVAL; - - if (dssdev->ctrl.pixel_size != 24) - return -EINVAL; - - scr_width = ovl->info.screen_width; - data = ovl->info.vaddr; - - start_offset = scr_width * y + x; - horiz_inc = scr_width - w; - current_x = x; - - /* We need header(4) + DCSCMD(1) + pixels(numpix*bytespp) bytes - * in fifo */ - - /* When using CPU, max long packet size is TX buffer size */ - max_dsi_packet_size = dsi->vc[0].fifo_size * 32 * 4; - - /* we seem to get better perf if we divide the tx fifo to half, - and while the other half is being sent, we fill the other half - max_dsi_packet_size /= 2; */ - - max_data_per_packet = max_dsi_packet_size - 4 - 1; - - max_pixels_per_packet = max_data_per_packet / bytespp; - - DSSDBG("max_pixels_per_packet %d\n", max_pixels_per_packet); - - pixels_left = w * h; + switch (dssdev->panel.dsi_pix_fmt) { + case OMAP_DSS_DSI_FMT_RGB888: + data_type = MIPI_DSI_PACKED_PIXEL_STREAM_24; + break; + case OMAP_DSS_DSI_FMT_RGB666: + data_type = MIPI_DSI_PIXEL_STREAM_3BYTE_18; + break; + case OMAP_DSS_DSI_FMT_RGB666_PACKED: + data_type = MIPI_DSI_PACKED_PIXEL_STREAM_18; + break; + case OMAP_DSS_DSI_FMT_RGB565: + data_type = MIPI_DSI_PACKED_PIXEL_STREAM_16; + break; + default: + BUG(); + }; - DSSDBG("total pixels %d\n", pixels_left); + dsi_if_enable(dsidev, false); + dsi_vc_enable(dsidev, channel, false); - data += start_offset; + /* MODE, 1 = video mode */ + REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 1, 4, 4); - while (pixels_left > 0) { - /* 0x2c = write_memory_start */ - /* 0x3c = write_memory_continue */ - u8 dcs_cmd = first ? 0x2c : 0x3c; - int pixels; - DSI_DECL_VARS; - first = 0; + word_count = DIV_ROUND_UP(dssdev->panel.timings.x_res * bpp, 8); -#if 1 - /* using fifo not empty */ - /* TX_FIFO_NOT_EMPTY */ - while (FLD_GET(dsi_read_reg(dsidev, DSI_VC_CTRL(0)), 5, 5)) { - fifo_stalls++; - if (fifo_stalls > 0xfffff) { - DSSERR("fifo stalls overflow, pixels left %d\n", - pixels_left); - dsi_if_enable(dsidev, 0); - return -EIO; - } - udelay(1); - } -#elif 1 - /* using fifo emptiness */ - while ((REG_GET(dsidev, DSI_TX_FIFO_VC_EMPTINESS, 7, 0)+1)*4 < - max_dsi_packet_size) { - fifo_stalls++; - if (fifo_stalls > 0xfffff) { - DSSERR("fifo stalls overflow, pixels left %d\n", - pixels_left); - dsi_if_enable(dsidev, 0); - return -EIO; - } - } -#else - while ((REG_GET(dsidev, DSI_TX_FIFO_VC_EMPTINESS, - 7, 0) + 1) * 4 == 0) { - fifo_stalls++; - if (fifo_stalls > 0xfffff) { - DSSERR("fifo stalls overflow, pixels left %d\n", - pixels_left); - dsi_if_enable(dsidev, 0); - return -EIO; - } - } -#endif - pixels = min(max_pixels_per_packet, pixels_left); + dsi_vc_write_long_header(dsidev, channel, data_type, word_count, 0); - pixels_left -= pixels; + dsi_vc_enable(dsidev, channel, true); + dsi_if_enable(dsidev, true); - dsi_vc_write_long_header(dsidev, 0, DSI_DT_DCS_LONG_WRITE, - 1 + pixels * bytespp, 0); + dssdev->manager->enable(dssdev->manager); - DSI_PUSH(dsidev, 0, dcs_cmd); + return 0; +} +EXPORT_SYMBOL(dsi_video_mode_enable); - while (pixels-- > 0) { - u32 pix = __raw_readl(data++); +void dsi_video_mode_disable(struct omap_dss_device *dssdev, int channel) +{ + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); - DSI_PUSH(dsidev, 0, (pix >> 16) & 0xff); - DSI_PUSH(dsidev, 0, (pix >> 8) & 0xff); - DSI_PUSH(dsidev, 0, (pix >> 0) & 0xff); + dsi_if_enable(dsidev, false); + dsi_vc_enable(dsidev, channel, false); - current_x++; - if (current_x == x+w) { - current_x = x; - data += horiz_inc; - } - } + /* MODE, 0 = command mode */ + REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 0, 4, 4); - DSI_FLUSH(dsidev, 0); - } + dsi_vc_enable(dsidev, channel, true); + dsi_if_enable(dsidev, true); - return 0; + dssdev->manager->disable(dssdev->manager); } +EXPORT_SYMBOL(dsi_video_mode_disable); static void dsi_update_screen_dispc(struct omap_dss_device *dssdev, u16 x, u16 y, u16 w, u16 h) @@ -3808,9 +4023,9 @@ static void dsi_update_screen_dispc(struct omap_dss_device *dssdev, DSSDBG("dsi_update_screen_dispc(%d,%d %dx%d)\n", x, y, w, h); - dsi_vc_config_vp(dsidev, channel); + dsi_vc_config_source(dsidev, channel, DSI_VC_SOURCE_VP); - bytespp = dssdev->ctrl.pixel_size / 8; + bytespp = dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt) / 8; bytespl = w * bytespp; bytespf = bytespl * h; @@ -3831,7 +4046,7 @@ static void dsi_update_screen_dispc(struct omap_dss_device *dssdev, l = FLD_VAL(total_len, 23, 0); /* TE_SIZE */ dsi_write_reg(dsidev, DSI_VC_TE(channel), l); - dsi_vc_write_long_header(dsidev, channel, DSI_DT_DCS_LONG_WRITE, + dsi_vc_write_long_header(dsidev, channel, MIPI_DSI_DCS_LONG_WRITE, packet_len, 0); if (dsi->te_enabled) @@ -3956,11 +4171,9 @@ int omap_dsi_prepare_update(struct omap_dss_device *dssdev, dsi_perf_mark_setup(dsidev); - if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) { - dss_setup_partial_planes(dssdev, x, y, w, h, - enlarge_update_area); - dispc_set_lcd_size(dssdev->manager->id, *w, *h); - } + dss_setup_partial_planes(dssdev, x, y, w, h, + enlarge_update_area); + dispc_mgr_set_lcd_size(dssdev->manager->id, *w, *h); return 0; } @@ -3982,27 +4195,16 @@ int omap_dsi_update(struct omap_dss_device *dssdev, * see rather obscure HW error happening, as DSS halts. */ BUG_ON(x % 2 == 1); - if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) { - dsi->framedone_callback = callback; - dsi->framedone_data = data; - - dsi->update_region.x = x; - dsi->update_region.y = y; - dsi->update_region.w = w; - dsi->update_region.h = h; - dsi->update_region.device = dssdev; - - dsi_update_screen_dispc(dssdev, x, y, w, h); - } else { - int r; + dsi->framedone_callback = callback; + dsi->framedone_data = data; - r = dsi_update_screen_l4(dssdev, x, y, w, h); - if (r) - return r; + dsi->update_region.x = x; + dsi->update_region.y = y; + dsi->update_region.w = w; + dsi->update_region.h = h; + dsi->update_region.device = dssdev; - dsi_perf_show(dsidev, "L4"); - callback(0, data); - } + dsi_update_screen_dispc(dssdev, x, y, w, h); return 0; } @@ -4013,28 +4215,9 @@ EXPORT_SYMBOL(omap_dsi_update); static int dsi_display_init_dispc(struct omap_dss_device *dssdev) { int r; - u32 irq; - - irq = dssdev->manager->id == OMAP_DSS_CHANNEL_LCD ? - DISPC_IRQ_FRAMEDONE : DISPC_IRQ_FRAMEDONE2; - r = omap_dispc_register_isr(dsi_framedone_irq_callback, (void *) dssdev, - irq); - if (r) { - DSSERR("can't get FRAMEDONE irq\n"); - return r; - } - - dispc_set_lcd_display_type(dssdev->manager->id, - OMAP_DSS_LCD_DISPLAY_TFT); - - dispc_set_parallel_interface_mode(dssdev->manager->id, - OMAP_DSS_PARALLELMODE_DSI); - dispc_enable_fifohandcheck(dssdev->manager->id, 1); - - dispc_set_tft_data_lines(dssdev->manager->id, dssdev->ctrl.pixel_size); - - { + if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_CMD_MODE) { + u32 irq; struct omap_video_timings timings = { .hsw = 1, .hfp = 1, @@ -4044,21 +4227,46 @@ static int dsi_display_init_dispc(struct omap_dss_device *dssdev) .vbp = 0, }; - dispc_set_lcd_timings(dssdev->manager->id, &timings); + irq = dssdev->manager->id == OMAP_DSS_CHANNEL_LCD ? + DISPC_IRQ_FRAMEDONE : DISPC_IRQ_FRAMEDONE2; + + r = omap_dispc_register_isr(dsi_framedone_irq_callback, + (void *) dssdev, irq); + if (r) { + DSSERR("can't get FRAMEDONE irq\n"); + return r; + } + + dispc_mgr_enable_stallmode(dssdev->manager->id, true); + dispc_mgr_enable_fifohandcheck(dssdev->manager->id, 1); + + dispc_mgr_set_lcd_timings(dssdev->manager->id, &timings); + } else { + dispc_mgr_enable_stallmode(dssdev->manager->id, false); + dispc_mgr_enable_fifohandcheck(dssdev->manager->id, 0); + + dispc_mgr_set_lcd_timings(dssdev->manager->id, + &dssdev->panel.timings); } + dispc_mgr_set_lcd_display_type(dssdev->manager->id, + OMAP_DSS_LCD_DISPLAY_TFT); + dispc_mgr_set_tft_data_lines(dssdev->manager->id, + dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt)); return 0; } static void dsi_display_uninit_dispc(struct omap_dss_device *dssdev) { - u32 irq; + if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_CMD_MODE) { + u32 irq; - irq = dssdev->manager->id == OMAP_DSS_CHANNEL_LCD ? - DISPC_IRQ_FRAMEDONE : DISPC_IRQ_FRAMEDONE2; + irq = dssdev->manager->id == OMAP_DSS_CHANNEL_LCD ? + DISPC_IRQ_FRAMEDONE : DISPC_IRQ_FRAMEDONE2; - omap_dispc_unregister_isr(dsi_framedone_irq_callback, (void *) dssdev, - irq); + omap_dispc_unregister_isr(dsi_framedone_irq_callback, + (void *) dssdev, irq); + } } static int dsi_configure_dsi_clocks(struct omap_dss_device *dssdev) @@ -4106,7 +4314,7 @@ static int dsi_configure_dispc_clocks(struct omap_dss_device *dssdev) return r; } - r = dispc_set_clock_div(dssdev->manager->id, &dispc_cinfo); + r = dispc_mgr_set_clock_div(dssdev->manager->id, &dispc_cinfo); if (r) { DSSERR("Failed to set dispc clocks\n"); return r; @@ -4166,10 +4374,12 @@ static int dsi_display_init_dsi(struct omap_dss_device *dssdev) return 0; err3: - dsi_cio_uninit(dsidev); + dsi_cio_uninit(dssdev); err2: dss_select_dispc_clk_source(OMAP_DSS_CLK_SRC_FCK); dss_select_dsi_clk_source(dsi_module, OMAP_DSS_CLK_SRC_FCK); + dss_select_lcd_clk_source(dssdev->manager->id, OMAP_DSS_CLK_SRC_FCK); + err1: dsi_pll_uninit(dsidev, true); err0: @@ -4195,7 +4405,8 @@ static void dsi_display_uninit_dsi(struct omap_dss_device *dssdev, dss_select_dispc_clk_source(OMAP_DSS_CLK_SRC_FCK); dss_select_dsi_clk_source(dsi_module, OMAP_DSS_CLK_SRC_FCK); - dsi_cio_uninit(dsidev); + dss_select_lcd_clk_source(dssdev->manager->id, OMAP_DSS_CLK_SRC_FCK); + dsi_cio_uninit(dssdev); dsi_pll_uninit(dsidev, disconnect_lanes); } @@ -4211,6 +4422,12 @@ int omapdss_dsi_display_enable(struct omap_dss_device *dssdev) mutex_lock(&dsi->lock); + if (dssdev->manager == NULL) { + DSSERR("failed to enable display: no manager\n"); + r = -ENODEV; + goto err_start_dev; + } + r = omap_dss_start_device(dssdev); if (r) { DSSERR("failed to start device\n"); @@ -4307,9 +4524,10 @@ int dsi_init_display(struct omap_dss_device *dssdev) DSSDBG("DSI init\n"); - /* XXX these should be figured out dynamically */ - dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE | - OMAP_DSS_DISPLAY_CAP_TEAR_ELIM; + if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_CMD_MODE) { + dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE | + OMAP_DSS_DISPLAY_CAP_TEAR_ELIM; + } if (dsi->vdds_dsi_reg == NULL) { struct regulator *vdds_dsi; @@ -4435,10 +4653,7 @@ static int dsi_get_clocks(struct platform_device *dsidev) dsi->dss_clk = clk; - if (cpu_is_omap34xx() || cpu_is_omap3630()) - clk = clk_get(&dsidev->dev, "dss2_alwon_fck"); - else - clk = clk_get(&dsidev->dev, "sys_clk"); + clk = clk_get(&dsidev->dev, "sys_clk"); if (IS_ERR(clk)) { DSSERR("can't get sys_clk\n"); clk_put(dsi->dss_clk); @@ -4462,7 +4677,7 @@ static void dsi_put_clocks(struct platform_device *dsidev) } /* DSI1 HW IP initialisation */ -static int omap_dsi1hw_probe(struct platform_device *dsidev) +static int omap_dsihw_probe(struct platform_device *dsidev) { struct omap_display_platform_data *dss_plat_data; struct omap_dss_board_info *board_info; @@ -4483,7 +4698,8 @@ static int omap_dsi1hw_probe(struct platform_device *dsidev) dss_plat_data = dsidev->dev.platform_data; board_info = dss_plat_data->board_data; - dsi->dsi_mux_pads = board_info->dsi_mux_pads; + dsi->enable_pads = board_info->dsi_enable_pads; + dsi->disable_pads = board_info->dsi_disable_pads; spin_lock_init(&dsi->irq_lock); spin_lock_init(&dsi->errors_lock); @@ -4539,7 +4755,7 @@ static int omap_dsi1hw_probe(struct platform_device *dsidev) /* DSI VCs initialization */ for (i = 0; i < ARRAY_SIZE(dsi->vc); i++) { - dsi->vc[i].mode = DSI_VC_MODE_L4; + dsi->vc[i].source = DSI_VC_SOURCE_L4; dsi->vc[i].dssdev = NULL; dsi->vc[i].vc_id = 0; } @@ -4572,7 +4788,7 @@ err_alloc: return r; } -static int omap_dsi1hw_remove(struct platform_device *dsidev) +static int omap_dsihw_remove(struct platform_device *dsidev) { struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); @@ -4602,10 +4818,6 @@ static int omap_dsi1hw_remove(struct platform_device *dsidev) static int dsi_runtime_suspend(struct device *dev) { - struct dsi_data *dsi = dsi_get_dsidrv_data(to_platform_device(dev)); - - clk_disable(dsi->dss_clk); - dispc_runtime_put(); dss_runtime_put(); @@ -4614,7 +4826,6 @@ static int dsi_runtime_suspend(struct device *dev) static int dsi_runtime_resume(struct device *dev) { - struct dsi_data *dsi = dsi_get_dsidrv_data(to_platform_device(dev)); int r; r = dss_runtime_get(); @@ -4625,8 +4836,6 @@ static int dsi_runtime_resume(struct device *dev) if (r) goto err_get_dispc; - clk_enable(dsi->dss_clk); - return 0; err_get_dispc: @@ -4640,11 +4849,11 @@ static const struct dev_pm_ops dsi_pm_ops = { .runtime_resume = dsi_runtime_resume, }; -static struct platform_driver omap_dsi1hw_driver = { - .probe = omap_dsi1hw_probe, - .remove = omap_dsi1hw_remove, +static struct platform_driver omap_dsihw_driver = { + .probe = omap_dsihw_probe, + .remove = omap_dsihw_remove, .driver = { - .name = "omapdss_dsi1", + .name = "omapdss_dsi", .owner = THIS_MODULE, .pm = &dsi_pm_ops, }, @@ -4652,10 +4861,10 @@ static struct platform_driver omap_dsi1hw_driver = { int dsi_init_platform_driver(void) { - return platform_driver_register(&omap_dsi1hw_driver); + return platform_driver_register(&omap_dsihw_driver); } void dsi_uninit_platform_driver(void) { - return platform_driver_unregister(&omap_dsi1hw_driver); + return platform_driver_unregister(&omap_dsihw_driver); } diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c index 0f9c3a6457a5..3e09726d32c7 100644 --- a/drivers/video/omap2/dss/dss.c +++ b/drivers/video/omap2/dss/dss.c @@ -639,6 +639,17 @@ void dss_select_hdmi_venc_clk_source(enum dss_hdmi_venc_clk_source_select hdmi) REG_FLD_MOD(DSS_CONTROL, hdmi, 15, 15); /* VENC_HDMI_SWITCH */ } +enum dss_hdmi_venc_clk_source_select dss_get_hdmi_venc_clk_source(void) +{ + enum omap_display_type displays; + + displays = dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_DIGIT); + if ((displays & OMAP_DISPLAY_TYPE_HDMI) == 0) + return DSS_VENC_TV_CLK; + + return REG_GET(DSS_CONTROL, 15, 15); +} + static int dss_get_clocks(void) { struct clk *clk; @@ -691,11 +702,6 @@ static void dss_put_clocks(void) clk_put(dss.dss_clk); } -struct clk *dss_get_ick(void) -{ - return clk_get(&dss.pdev->dev, "ick"); -} - int dss_runtime_get(void) { int r; @@ -824,13 +830,11 @@ static int omap_dsshw_remove(struct platform_device *pdev) static int dss_runtime_suspend(struct device *dev) { dss_save_context(); - clk_disable(dss.dss_clk); return 0; } static int dss_runtime_resume(struct device *dev) { - clk_enable(dss.dss_clk); dss_restore_context(); return 0; } diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h index 9c94b1152c20..6308fc59fc9e 100644 --- a/drivers/video/omap2/dss/dss.h +++ b/drivers/video/omap2/dss/dss.h @@ -97,10 +97,10 @@ extern unsigned int dss_debug; #define FLD_MOD(orig, val, start, end) \ (((orig) & ~FLD_MASK(start, end)) | FLD_VAL(val, start, end)) -enum omap_parallel_interface_mode { - OMAP_DSS_PARALLELMODE_BYPASS, /* MIPI DPI */ - OMAP_DSS_PARALLELMODE_RFBI, /* MIPI DBI */ - OMAP_DSS_PARALLELMODE_DSI, +enum dss_io_pad_mode { + DSS_IO_PAD_MODE_RESET, + DSS_IO_PAD_MODE_RFBI, + DSS_IO_PAD_MODE_BYPASS, }; enum dss_hdmi_venc_clk_source_select { @@ -108,6 +108,11 @@ enum dss_hdmi_venc_clk_source_select { DSS_HDMI_M_PCLK = 1, }; +enum dss_dsi_content_type { + DSS_DSI_CONTENT_DCS, + DSS_DSI_CONTENT_GENERIC, +}; + struct dss_clock_info { /* rates that we get with dividers below */ unsigned long fck; @@ -150,16 +155,6 @@ struct dsi_clock_info { bool use_sys_clk; }; -/* HDMI PLL structure */ -struct hdmi_pll_info { - u16 regn; - u16 regm; - u32 regmf; - u16 regm2; - u16 regsd; - u16 dcofreq; -}; - struct seq_file; struct platform_device; @@ -209,9 +204,8 @@ void dss_uninit_platform_driver(void); int dss_runtime_get(void); void dss_runtime_put(void); -struct clk *dss_get_ick(void); - void dss_select_hdmi_venc_clk_source(enum dss_hdmi_venc_clk_source_select); +enum dss_hdmi_venc_clk_source_select dss_get_hdmi_venc_clk_source(void); const char *dss_get_generic_clk_source_name(enum omap_dss_clk_source clk_src); void dss_dump_clocks(struct seq_file *s); @@ -279,6 +273,8 @@ void dsi_create_debugfs_files_reg(struct dentry *debugfs_dir, int dsi_init_display(struct omap_dss_device *display); void dsi_irq_handler(void); +u8 dsi_get_pixel_size(enum omap_dss_dsi_pixel_format fmt); + unsigned long dsi_get_pll_hsdiv_dispc_rate(struct platform_device *dsidev); int dsi_pll_set_clock_div(struct platform_device *dsidev, struct dsi_clock_info *cinfo); @@ -309,6 +305,11 @@ static inline int dsi_runtime_get(struct platform_device *dsidev) static inline void dsi_runtime_put(struct platform_device *dsidev) { } +static inline u8 dsi_get_pixel_size(enum omap_dss_dsi_pixel_format fmt) +{ + WARN("%s: DSI not compiled in, returning pixel_size as 0\n", __func__); + return 0; +} static inline unsigned long dsi_get_pll_hsdiv_dispc_rate(struct platform_device *dsidev) { WARN("%s: DSI not compiled in, returning rate as 0\n", __func__); @@ -385,90 +386,71 @@ void dispc_disable_sidle(void); void dispc_lcd_enable_signal_polarity(bool act_high); void dispc_lcd_enable_signal(bool enable); void dispc_pck_free_enable(bool enable); -void dispc_enable_fifohandcheck(enum omap_channel channel, bool enable); - -void dispc_set_lcd_size(enum omap_channel channel, u16 width, u16 height); void dispc_set_digit_size(u16 width, u16 height); -u32 dispc_get_plane_fifo_size(enum omap_plane plane); -void dispc_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high); void dispc_enable_fifomerge(bool enable); -u32 dispc_get_burst_size(enum omap_plane plane); -void dispc_enable_cpr(enum omap_channel channel, bool enable); -void dispc_set_cpr_coef(enum omap_channel channel, - struct omap_dss_cpr_coefs *coefs); - -void dispc_set_plane_ba0(enum omap_plane plane, u32 paddr); -void dispc_set_plane_ba1(enum omap_plane plane, u32 paddr); -void dispc_set_plane_pos(enum omap_plane plane, u16 x, u16 y); -void dispc_set_plane_size(enum omap_plane plane, u16 width, u16 height); -void dispc_set_channel_out(enum omap_plane plane, - enum omap_channel channel_out); - void dispc_enable_gamma_table(bool enable); -int dispc_setup_plane(enum omap_plane plane, - u32 paddr, u16 screen_width, - u16 pos_x, u16 pos_y, - u16 width, u16 height, - u16 out_width, u16 out_height, - enum omap_color_mode color_mode, - bool ilace, - enum omap_dss_rotation_type rotation_type, - u8 rotation, bool mirror, - u8 global_alpha, u8 pre_mult_alpha, - enum omap_channel channel, - u32 puv_addr); - -bool dispc_go_busy(enum omap_channel channel); -void dispc_go(enum omap_channel channel); -void dispc_enable_channel(enum omap_channel channel, bool enable); -bool dispc_is_channel_enabled(enum omap_channel channel); -int dispc_enable_plane(enum omap_plane plane, bool enable); -void dispc_enable_replication(enum omap_plane plane, bool enable); - -void dispc_set_parallel_interface_mode(enum omap_channel channel, - enum omap_parallel_interface_mode mode); -void dispc_set_tft_data_lines(enum omap_channel channel, u8 data_lines); -void dispc_set_lcd_display_type(enum omap_channel channel, - enum omap_lcd_display_type type); void dispc_set_loadmode(enum omap_dss_load_mode mode); -void dispc_set_default_color(enum omap_channel channel, u32 color); -u32 dispc_get_default_color(enum omap_channel channel); -void dispc_set_trans_key(enum omap_channel ch, - enum omap_dss_trans_key_type type, - u32 trans_key); -void dispc_get_trans_key(enum omap_channel ch, - enum omap_dss_trans_key_type *type, - u32 *trans_key); -void dispc_enable_trans_key(enum omap_channel ch, bool enable); -void dispc_enable_alpha_blending(enum omap_channel ch, bool enable); -bool dispc_trans_key_enabled(enum omap_channel ch); -bool dispc_alpha_blending_enabled(enum omap_channel ch); - bool dispc_lcd_timings_ok(struct omap_video_timings *timings); -void dispc_set_lcd_timings(enum omap_channel channel, - struct omap_video_timings *timings); unsigned long dispc_fclk_rate(void); -unsigned long dispc_lclk_rate(enum omap_channel channel); -unsigned long dispc_pclk_rate(enum omap_channel channel); -void dispc_set_pol_freq(enum omap_channel channel, - enum omap_panel_config config, u8 acbi, u8 acb); void dispc_find_clk_divs(bool is_tft, unsigned long req_pck, unsigned long fck, struct dispc_clock_info *cinfo); int dispc_calc_clock_rates(unsigned long dispc_fclk_rate, struct dispc_clock_info *cinfo); -int dispc_set_clock_div(enum omap_channel channel, + + +u32 dispc_ovl_get_fifo_size(enum omap_plane plane); +u32 dispc_ovl_get_burst_size(enum omap_plane plane); +int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi, + bool ilace, enum omap_channel channel, bool replication, + u32 fifo_low, u32 fifo_high); +int dispc_ovl_enable(enum omap_plane plane, bool enable); + + +void dispc_mgr_enable_fifohandcheck(enum omap_channel channel, bool enable); +void dispc_mgr_set_lcd_size(enum omap_channel channel, u16 width, u16 height); +void dispc_mgr_enable_cpr(enum omap_channel channel, bool enable); +void dispc_mgr_set_cpr_coef(enum omap_channel channel, + struct omap_dss_cpr_coefs *coefs); +bool dispc_mgr_go_busy(enum omap_channel channel); +void dispc_mgr_go(enum omap_channel channel); +void dispc_mgr_enable(enum omap_channel channel, bool enable); +bool dispc_mgr_is_channel_enabled(enum omap_channel channel); +void dispc_mgr_set_io_pad_mode(enum dss_io_pad_mode mode); +void dispc_mgr_enable_stallmode(enum omap_channel channel, bool enable); +void dispc_mgr_set_tft_data_lines(enum omap_channel channel, u8 data_lines); +void dispc_mgr_set_lcd_display_type(enum omap_channel channel, + enum omap_lcd_display_type type); +void dispc_mgr_set_default_color(enum omap_channel channel, u32 color); +u32 dispc_mgr_get_default_color(enum omap_channel channel); +void dispc_mgr_set_trans_key(enum omap_channel ch, + enum omap_dss_trans_key_type type, + u32 trans_key); +void dispc_mgr_get_trans_key(enum omap_channel ch, + enum omap_dss_trans_key_type *type, + u32 *trans_key); +void dispc_mgr_enable_trans_key(enum omap_channel ch, bool enable); +void dispc_mgr_enable_alpha_fixed_zorder(enum omap_channel ch, bool enable); +bool dispc_mgr_trans_key_enabled(enum omap_channel ch); +bool dispc_mgr_alpha_fixed_zorder_enabled(enum omap_channel ch); +void dispc_mgr_set_lcd_timings(enum omap_channel channel, + struct omap_video_timings *timings); +void dispc_mgr_set_pol_freq(enum omap_channel channel, + enum omap_panel_config config, u8 acbi, u8 acb); +unsigned long dispc_mgr_lclk_rate(enum omap_channel channel); +unsigned long dispc_mgr_pclk_rate(enum omap_channel channel); +int dispc_mgr_set_clock_div(enum omap_channel channel, struct dispc_clock_info *cinfo); -int dispc_get_clock_div(enum omap_channel channel, +int dispc_mgr_get_clock_div(enum omap_channel channel, struct dispc_clock_info *cinfo); - /* VENC */ #ifdef CONFIG_OMAP2_DSS_VENC int venc_init_platform_driver(void); void venc_uninit_platform_driver(void); void venc_dump_regs(struct seq_file *s); int venc_init_display(struct omap_dss_device *display); +unsigned long venc_get_pixel_clock(void); #else static inline int venc_init_platform_driver(void) { @@ -477,6 +459,11 @@ static inline int venc_init_platform_driver(void) static inline void venc_uninit_platform_driver(void) { } +static inline unsigned long venc_get_pixel_clock(void) +{ + WARN("%s: VENC not compiled in, returning pclk as 0\n", __func__); + return 0; +} #endif /* HDMI */ @@ -484,6 +471,8 @@ static inline void venc_uninit_platform_driver(void) int hdmi_init_platform_driver(void); void hdmi_uninit_platform_driver(void); int hdmi_init_display(struct omap_dss_device *dssdev); +unsigned long hdmi_get_pixel_clock(void); +void hdmi_dump_regs(struct seq_file *s); #else static inline int hdmi_init_display(struct omap_dss_device *dssdev) { @@ -496,12 +485,19 @@ static inline int hdmi_init_platform_driver(void) static inline void hdmi_uninit_platform_driver(void) { } +static inline unsigned long hdmi_get_pixel_clock(void) +{ + WARN("%s: HDMI not compiled in, returning pclk as 0\n", __func__); + return 0; +} #endif int omapdss_hdmi_display_enable(struct omap_dss_device *dssdev); void omapdss_hdmi_display_disable(struct omap_dss_device *dssdev); void omapdss_hdmi_display_set_timing(struct omap_dss_device *dssdev); int omapdss_hdmi_display_check_timing(struct omap_dss_device *dssdev, struct omap_video_timings *timings); +int omapdss_hdmi_read_edid(u8 *buf, int len); +bool omapdss_hdmi_detect(void); int hdmi_panel_init(void); void hdmi_panel_exit(void); diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c index b415c4ee621d..b402699168a5 100644 --- a/drivers/video/omap2/dss/dss_features.c +++ b/drivers/video/omap2/dss/dss_features.c @@ -47,6 +47,7 @@ struct omap_dss_features { const int num_ovls; const enum omap_display_type *supported_displays; const enum omap_color_mode *supported_color_modes; + const enum omap_overlay_caps *overlay_caps; const char * const *clksrc_names; const struct dss_param_range *dss_params; @@ -209,6 +210,68 @@ static const enum omap_color_mode omap4_dss_supported_color_modes[] = { OMAP_DSS_COLOR_ARGB16 | OMAP_DSS_COLOR_XRGB16_1555 | OMAP_DSS_COLOR_ARGB32 | OMAP_DSS_COLOR_RGBX16 | OMAP_DSS_COLOR_RGBX32, + + /* OMAP_DSS_VIDEO3 */ + OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB12U | + OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_ARGB16_1555 | + OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_NV12 | + OMAP_DSS_COLOR_RGBA16 | OMAP_DSS_COLOR_RGB24U | + OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_UYVY | + OMAP_DSS_COLOR_ARGB16 | OMAP_DSS_COLOR_XRGB16_1555 | + OMAP_DSS_COLOR_ARGB32 | OMAP_DSS_COLOR_RGBX16 | + OMAP_DSS_COLOR_RGBX32, +}; + +static const enum omap_overlay_caps omap2_dss_overlay_caps[] = { + /* OMAP_DSS_GFX */ + 0, + + /* OMAP_DSS_VIDEO1 */ + OMAP_DSS_OVL_CAP_SCALE, + + /* OMAP_DSS_VIDEO2 */ + OMAP_DSS_OVL_CAP_SCALE, +}; + +static const enum omap_overlay_caps omap3430_dss_overlay_caps[] = { + /* OMAP_DSS_GFX */ + OMAP_DSS_OVL_CAP_GLOBAL_ALPHA, + + /* OMAP_DSS_VIDEO1 */ + OMAP_DSS_OVL_CAP_SCALE, + + /* OMAP_DSS_VIDEO2 */ + OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA, +}; + +static const enum omap_overlay_caps omap3630_dss_overlay_caps[] = { + /* OMAP_DSS_GFX */ + OMAP_DSS_OVL_CAP_GLOBAL_ALPHA | OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA, + + /* OMAP_DSS_VIDEO1 */ + OMAP_DSS_OVL_CAP_SCALE, + + /* OMAP_DSS_VIDEO2 */ + OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA | + OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA, +}; + +static const enum omap_overlay_caps omap4_dss_overlay_caps[] = { + /* OMAP_DSS_GFX */ + OMAP_DSS_OVL_CAP_GLOBAL_ALPHA | OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA | + OMAP_DSS_OVL_CAP_ZORDER, + + /* OMAP_DSS_VIDEO1 */ + OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA | + OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA | OMAP_DSS_OVL_CAP_ZORDER, + + /* OMAP_DSS_VIDEO2 */ + OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA | + OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA | OMAP_DSS_OVL_CAP_ZORDER, + + /* OMAP_DSS_VIDEO3 */ + OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA | + OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA | OMAP_DSS_OVL_CAP_ZORDER, }; static const char * const omap2_dss_clk_source_names[] = { @@ -233,32 +296,38 @@ static const char * const omap4_dss_clk_source_names[] = { static const struct dss_param_range omap2_dss_param_range[] = { [FEAT_PARAM_DSS_FCK] = { 0, 173000000 }, + [FEAT_PARAM_DSS_PCD] = { 2, 255 }, [FEAT_PARAM_DSIPLL_REGN] = { 0, 0 }, [FEAT_PARAM_DSIPLL_REGM] = { 0, 0 }, [FEAT_PARAM_DSIPLL_REGM_DISPC] = { 0, 0 }, [FEAT_PARAM_DSIPLL_REGM_DSI] = { 0, 0 }, [FEAT_PARAM_DSIPLL_FINT] = { 0, 0 }, [FEAT_PARAM_DSIPLL_LPDIV] = { 0, 0 }, + [FEAT_PARAM_DOWNSCALE] = { 1, 2 }, }; static const struct dss_param_range omap3_dss_param_range[] = { [FEAT_PARAM_DSS_FCK] = { 0, 173000000 }, + [FEAT_PARAM_DSS_PCD] = { 1, 255 }, [FEAT_PARAM_DSIPLL_REGN] = { 0, (1 << 7) - 1 }, [FEAT_PARAM_DSIPLL_REGM] = { 0, (1 << 11) - 1 }, [FEAT_PARAM_DSIPLL_REGM_DISPC] = { 0, (1 << 4) - 1 }, [FEAT_PARAM_DSIPLL_REGM_DSI] = { 0, (1 << 4) - 1 }, [FEAT_PARAM_DSIPLL_FINT] = { 750000, 2100000 }, [FEAT_PARAM_DSIPLL_LPDIV] = { 1, (1 << 13) - 1}, + [FEAT_PARAM_DOWNSCALE] = { 1, 4 }, }; static const struct dss_param_range omap4_dss_param_range[] = { [FEAT_PARAM_DSS_FCK] = { 0, 186000000 }, + [FEAT_PARAM_DSS_PCD] = { 1, 255 }, [FEAT_PARAM_DSIPLL_REGN] = { 0, (1 << 8) - 1 }, [FEAT_PARAM_DSIPLL_REGM] = { 0, (1 << 12) - 1 }, [FEAT_PARAM_DSIPLL_REGM_DISPC] = { 0, (1 << 5) - 1 }, [FEAT_PARAM_DSIPLL_REGM_DSI] = { 0, (1 << 5) - 1 }, [FEAT_PARAM_DSIPLL_FINT] = { 500000, 2500000 }, [FEAT_PARAM_DSIPLL_LPDIV] = { 0, (1 << 13) - 1 }, + [FEAT_PARAM_DOWNSCALE] = { 1, 4 }, }; /* OMAP2 DSS Features */ @@ -275,6 +344,7 @@ static const struct omap_dss_features omap2_dss_features = { .num_ovls = 3, .supported_displays = omap2_dss_supported_displays, .supported_color_modes = omap2_dss_supported_color_modes, + .overlay_caps = omap2_dss_overlay_caps, .clksrc_names = omap2_dss_clk_source_names, .dss_params = omap2_dss_param_range, .buffer_size_unit = 1, @@ -287,18 +357,19 @@ static const struct omap_dss_features omap3430_dss_features = { .num_reg_fields = ARRAY_SIZE(omap3_dss_reg_fields), .has_feature = - FEAT_GLOBAL_ALPHA | FEAT_LCDENABLEPOL | + FEAT_LCDENABLEPOL | FEAT_LCDENABLESIGNAL | FEAT_PCKFREEENABLE | FEAT_FUNCGATED | FEAT_ROWREPEATENABLE | FEAT_LINEBUFFERSPLIT | FEAT_RESIZECONF | FEAT_DSI_PLL_FREQSEL | FEAT_DSI_REVERSE_TXCLKESC | FEAT_VENC_REQUIRES_TV_DAC_CLK | FEAT_CPR | FEAT_PRELOAD | - FEAT_FIR_COEF_V, + FEAT_FIR_COEF_V | FEAT_ALPHA_FIXED_ZORDER, .num_mgrs = 2, .num_ovls = 3, .supported_displays = omap3430_dss_supported_displays, .supported_color_modes = omap3_dss_supported_color_modes, + .overlay_caps = omap3430_dss_overlay_caps, .clksrc_names = omap3_dss_clk_source_names, .dss_params = omap3_dss_param_range, .buffer_size_unit = 1, @@ -310,18 +381,19 @@ static const struct omap_dss_features omap3630_dss_features = { .num_reg_fields = ARRAY_SIZE(omap3_dss_reg_fields), .has_feature = - FEAT_GLOBAL_ALPHA | FEAT_LCDENABLEPOL | + FEAT_LCDENABLEPOL | FEAT_LCDENABLESIGNAL | FEAT_PCKFREEENABLE | - FEAT_PRE_MULT_ALPHA | FEAT_FUNCGATED | + FEAT_FUNCGATED | FEAT_ROWREPEATENABLE | FEAT_LINEBUFFERSPLIT | FEAT_RESIZECONF | FEAT_DSI_PLL_PWR_BUG | FEAT_DSI_PLL_FREQSEL | FEAT_CPR | FEAT_PRELOAD | - FEAT_FIR_COEF_V, + FEAT_FIR_COEF_V | FEAT_ALPHA_FIXED_ZORDER, .num_mgrs = 2, .num_ovls = 3, .supported_displays = omap3630_dss_supported_displays, .supported_color_modes = omap3_dss_supported_color_modes, + .overlay_caps = omap3630_dss_overlay_caps, .clksrc_names = omap3_dss_clk_source_names, .dss_params = omap3_dss_param_range, .buffer_size_unit = 1, @@ -335,17 +407,18 @@ static const struct omap_dss_features omap4430_es1_0_dss_features = { .num_reg_fields = ARRAY_SIZE(omap4_dss_reg_fields), .has_feature = - FEAT_GLOBAL_ALPHA | FEAT_PRE_MULT_ALPHA | - FEAT_MGR_LCD2 | FEAT_GLOBAL_ALPHA_VID1 | + FEAT_MGR_LCD2 | FEAT_CORE_CLK_DIV | FEAT_LCD_CLK_SRC | FEAT_DSI_DCS_CMD_CONFIG_VC | FEAT_DSI_VC_OCP_WIDTH | FEAT_DSI_GNQ | FEAT_HANDLE_UV_SEPARATE | FEAT_ATTR2 | - FEAT_CPR | FEAT_PRELOAD | FEAT_FIR_COEF_V, + FEAT_CPR | FEAT_PRELOAD | FEAT_FIR_COEF_V | + FEAT_ALPHA_FREE_ZORDER, .num_mgrs = 3, - .num_ovls = 3, + .num_ovls = 4, .supported_displays = omap4_dss_supported_displays, .supported_color_modes = omap4_dss_supported_color_modes, + .overlay_caps = omap4_dss_overlay_caps, .clksrc_names = omap4_dss_clk_source_names, .dss_params = omap4_dss_param_range, .buffer_size_unit = 16, @@ -358,24 +431,50 @@ static const struct omap_dss_features omap4_dss_features = { .num_reg_fields = ARRAY_SIZE(omap4_dss_reg_fields), .has_feature = - FEAT_GLOBAL_ALPHA | FEAT_PRE_MULT_ALPHA | - FEAT_MGR_LCD2 | FEAT_GLOBAL_ALPHA_VID1 | + FEAT_MGR_LCD2 | FEAT_CORE_CLK_DIV | FEAT_LCD_CLK_SRC | FEAT_DSI_DCS_CMD_CONFIG_VC | FEAT_DSI_VC_OCP_WIDTH | FEAT_DSI_GNQ | FEAT_HDMI_CTS_SWMODE | FEAT_HANDLE_UV_SEPARATE | FEAT_ATTR2 | FEAT_CPR | - FEAT_PRELOAD | FEAT_FIR_COEF_V, + FEAT_PRELOAD | FEAT_FIR_COEF_V | FEAT_ALPHA_FREE_ZORDER, .num_mgrs = 3, - .num_ovls = 3, + .num_ovls = 4, .supported_displays = omap4_dss_supported_displays, .supported_color_modes = omap4_dss_supported_color_modes, + .overlay_caps = omap4_dss_overlay_caps, .clksrc_names = omap4_dss_clk_source_names, .dss_params = omap4_dss_param_range, .buffer_size_unit = 16, .burst_size_unit = 16, }; +#if defined(CONFIG_OMAP4_DSS_HDMI) +/* HDMI OMAP4 Functions*/ +static const struct ti_hdmi_ip_ops omap4_hdmi_functions = { + + .video_configure = ti_hdmi_4xxx_basic_configure, + .phy_enable = ti_hdmi_4xxx_phy_enable, + .phy_disable = ti_hdmi_4xxx_phy_disable, + .read_edid = ti_hdmi_4xxx_read_edid, + .detect = ti_hdmi_4xxx_detect, + .pll_enable = ti_hdmi_4xxx_pll_enable, + .pll_disable = ti_hdmi_4xxx_pll_disable, + .video_enable = ti_hdmi_4xxx_wp_video_start, + .dump_wrapper = ti_hdmi_4xxx_wp_dump, + .dump_core = ti_hdmi_4xxx_core_dump, + .dump_pll = ti_hdmi_4xxx_pll_dump, + .dump_phy = ti_hdmi_4xxx_phy_dump, + +}; + +void dss_init_hdmi_ip_ops(struct hdmi_ip_data *ip_data) +{ + if (cpu_is_omap44xx()) + ip_data->ops = &omap4_hdmi_functions; +} +#endif + /* Functions returning values related to a DSS feature */ int dss_feat_get_num_mgrs(void) { @@ -407,6 +506,11 @@ enum omap_color_mode dss_feat_get_supported_color_modes(enum omap_plane plane) return omap_current_dss_features->supported_color_modes[plane]; } +enum omap_overlay_caps dss_feat_get_overlay_caps(enum omap_plane plane) +{ + return omap_current_dss_features->overlay_caps[plane]; +} + bool dss_feat_color_mode_supported(enum omap_plane plane, enum omap_color_mode color_mode) { diff --git a/drivers/video/omap2/dss/dss_features.h b/drivers/video/omap2/dss/dss_features.h index b7398cbcda5f..6a6c05dd45ce 100644 --- a/drivers/video/omap2/dss/dss_features.h +++ b/drivers/video/omap2/dss/dss_features.h @@ -20,16 +20,17 @@ #ifndef __OMAP2_DSS_FEATURES_H #define __OMAP2_DSS_FEATURES_H +#if defined(CONFIG_OMAP4_DSS_HDMI) +#include "ti_hdmi.h" +#endif + #define MAX_DSS_MANAGERS 3 -#define MAX_DSS_OVERLAYS 3 +#define MAX_DSS_OVERLAYS 4 #define MAX_DSS_LCD_MANAGERS 2 #define MAX_NUM_DSI 2 /* DSS has feature id */ enum dss_feat_id { - FEAT_GLOBAL_ALPHA = 1 << 0, - FEAT_GLOBAL_ALPHA_VID1 = 1 << 1, - FEAT_PRE_MULT_ALPHA = 1 << 2, FEAT_LCDENABLEPOL = 1 << 3, FEAT_LCDENABLESIGNAL = 1 << 4, FEAT_PCKFREEENABLE = 1 << 5, @@ -55,6 +56,8 @@ enum dss_feat_id { FEAT_CPR = 1 << 23, FEAT_PRELOAD = 1 << 24, FEAT_FIR_COEF_V = 1 << 25, + FEAT_ALPHA_FIXED_ZORDER = 1 << 26, + FEAT_ALPHA_FREE_ZORDER = 1 << 27, }; /* DSS register field id */ @@ -75,12 +78,14 @@ enum dss_feat_reg_field { enum dss_range_param { FEAT_PARAM_DSS_FCK, + FEAT_PARAM_DSS_PCD, FEAT_PARAM_DSIPLL_REGN, FEAT_PARAM_DSIPLL_REGM, FEAT_PARAM_DSIPLL_REGM_DISPC, FEAT_PARAM_DSIPLL_REGM_DSI, FEAT_PARAM_DSIPLL_FINT, FEAT_PARAM_DSIPLL_LPDIV, + FEAT_PARAM_DOWNSCALE, }; /* DSS Feature Functions */ @@ -90,6 +95,7 @@ unsigned long dss_feat_get_param_min(enum dss_range_param param); unsigned long dss_feat_get_param_max(enum dss_range_param param); enum omap_display_type dss_feat_get_supported_displays(enum omap_channel channel); enum omap_color_mode dss_feat_get_supported_color_modes(enum omap_plane plane); +enum omap_overlay_caps dss_feat_get_overlay_caps(enum omap_plane plane); bool dss_feat_color_mode_supported(enum omap_plane plane, enum omap_color_mode color_mode); const char *dss_feat_get_clk_source_name(enum omap_dss_clk_source id); @@ -100,4 +106,7 @@ u32 dss_feat_get_burst_size_unit(void); /* in bytes */ bool dss_has_feature(enum dss_feat_id id); void dss_feat_get_reg_field(enum dss_feat_reg_field id, u8 *start, u8 *end); void dss_features_init(void); +#if defined(CONFIG_OMAP4_DSS_HDMI) +void dss_init_hdmi_ip_ops(struct hdmi_ip_data *ip_data); +#endif #endif diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c index 256f27a9064a..3262f0f1fa35 100644 --- a/drivers/video/omap2/dss/hdmi.c +++ b/drivers/video/omap2/dss/hdmi.c @@ -37,26 +37,41 @@ defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) #include <sound/soc.h> #include <sound/pcm_params.h> +#include "ti_hdmi_4xxx_ip.h" #endif +#include "ti_hdmi.h" #include "dss.h" -#include "hdmi.h" #include "dss_features.h" +#define HDMI_WP 0x0 +#define HDMI_CORE_SYS 0x400 +#define HDMI_CORE_AV 0x900 +#define HDMI_PLLCTRL 0x200 +#define HDMI_PHY 0x300 + +/* HDMI EDID Length move this */ +#define HDMI_EDID_MAX_LENGTH 256 +#define EDID_TIMING_DESCRIPTOR_SIZE 0x12 +#define EDID_DESCRIPTOR_BLOCK0_ADDRESS 0x36 +#define EDID_DESCRIPTOR_BLOCK1_ADDRESS 0x80 +#define EDID_SIZE_BLOCK0_TIMING_DESCRIPTOR 4 +#define EDID_SIZE_BLOCK1_TIMING_DESCRIPTOR 4 + +#define OMAP_HDMI_TIMINGS_NB 34 + +#define HDMI_DEFAULT_REGN 16 +#define HDMI_DEFAULT_REGM2 1 + static struct { struct mutex lock; struct omap_display_platform_data *pdata; struct platform_device *pdev; - void __iomem *base_wp; /* HDMI wrapper */ + struct hdmi_ip_data ip_data; int code; int mode; - u8 edid[HDMI_EDID_MAX_LENGTH]; - u8 edid_set; - bool custom_set; - struct hdmi_config cfg; struct clk *sys_clk; - struct clk *hdmi_clk; } hdmi; /* @@ -144,30 +159,6 @@ static const int code_vesa[85] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 27, 28, -1, 33}; -static const u8 edid_header[8] = {0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0}; - -static inline void hdmi_write_reg(const struct hdmi_reg idx, u32 val) -{ - __raw_writel(val, hdmi.base_wp + idx.idx); -} - -static inline u32 hdmi_read_reg(const struct hdmi_reg idx) -{ - return __raw_readl(hdmi.base_wp + idx.idx); -} - -static inline int hdmi_wait_for_bit_change(const struct hdmi_reg idx, - int b2, int b1, u32 val) -{ - u32 t = 0; - while (val != REG_GET(idx, b2, b1)) { - udelay(1); - if (t++ > 10000) - return !val; - } - return val; -} - static int hdmi_runtime_get(void) { int r; @@ -193,304 +184,7 @@ int hdmi_init_display(struct omap_dss_device *dssdev) { DSSDBG("init_display\n"); - return 0; -} - -static int hdmi_pll_init(enum hdmi_clk_refsel refsel, int dcofreq, - struct hdmi_pll_info *fmt, u16 sd) -{ - u32 r; - - /* PLL start always use manual mode */ - REG_FLD_MOD(PLLCTRL_PLL_CONTROL, 0x0, 0, 0); - - r = hdmi_read_reg(PLLCTRL_CFG1); - r = FLD_MOD(r, fmt->regm, 20, 9); /* CFG1_PLL_REGM */ - r = FLD_MOD(r, fmt->regn, 8, 1); /* CFG1_PLL_REGN */ - - hdmi_write_reg(PLLCTRL_CFG1, r); - - r = hdmi_read_reg(PLLCTRL_CFG2); - - r = FLD_MOD(r, 0x0, 12, 12); /* PLL_HIGHFREQ divide by 2 */ - r = FLD_MOD(r, 0x1, 13, 13); /* PLL_REFEN */ - r = FLD_MOD(r, 0x0, 14, 14); /* PHY_CLKINEN de-assert during locking */ - - if (dcofreq) { - /* divider programming for frequency beyond 1000Mhz */ - REG_FLD_MOD(PLLCTRL_CFG3, sd, 17, 10); - r = FLD_MOD(r, 0x4, 3, 1); /* 1000MHz and 2000MHz */ - } else { - r = FLD_MOD(r, 0x2, 3, 1); /* 500MHz and 1000MHz */ - } - - hdmi_write_reg(PLLCTRL_CFG2, r); - - r = hdmi_read_reg(PLLCTRL_CFG4); - r = FLD_MOD(r, fmt->regm2, 24, 18); - r = FLD_MOD(r, fmt->regmf, 17, 0); - - hdmi_write_reg(PLLCTRL_CFG4, r); - - /* go now */ - REG_FLD_MOD(PLLCTRL_PLL_GO, 0x1, 0, 0); - - /* wait for bit change */ - if (hdmi_wait_for_bit_change(PLLCTRL_PLL_GO, 0, 0, 1) != 1) { - DSSERR("PLL GO bit not set\n"); - return -ETIMEDOUT; - } - - /* Wait till the lock bit is set in PLL status */ - if (hdmi_wait_for_bit_change(PLLCTRL_PLL_STATUS, 1, 1, 1) != 1) { - DSSWARN("cannot lock PLL\n"); - DSSWARN("CFG1 0x%x\n", - hdmi_read_reg(PLLCTRL_CFG1)); - DSSWARN("CFG2 0x%x\n", - hdmi_read_reg(PLLCTRL_CFG2)); - DSSWARN("CFG4 0x%x\n", - hdmi_read_reg(PLLCTRL_CFG4)); - return -ETIMEDOUT; - } - - DSSDBG("PLL locked!\n"); - - return 0; -} - -/* PHY_PWR_CMD */ -static int hdmi_set_phy_pwr(enum hdmi_phy_pwr val) -{ - /* Command for power control of HDMI PHY */ - REG_FLD_MOD(HDMI_WP_PWR_CTRL, val, 7, 6); - - /* Status of the power control of HDMI PHY */ - if (hdmi_wait_for_bit_change(HDMI_WP_PWR_CTRL, 5, 4, val) != val) { - DSSERR("Failed to set PHY power mode to %d\n", val); - return -ETIMEDOUT; - } - - return 0; -} - -/* PLL_PWR_CMD */ -static int hdmi_set_pll_pwr(enum hdmi_pll_pwr val) -{ - /* Command for power control of HDMI PLL */ - REG_FLD_MOD(HDMI_WP_PWR_CTRL, val, 3, 2); - - /* wait till PHY_PWR_STATUS is set */ - if (hdmi_wait_for_bit_change(HDMI_WP_PWR_CTRL, 1, 0, val) != val) { - DSSERR("Failed to set PHY_PWR_STATUS\n"); - return -ETIMEDOUT; - } - - return 0; -} - -static int hdmi_pll_reset(void) -{ - /* SYSRESET controlled by power FSM */ - REG_FLD_MOD(PLLCTRL_PLL_CONTROL, 0x0, 3, 3); - - /* READ 0x0 reset is in progress */ - if (hdmi_wait_for_bit_change(PLLCTRL_PLL_STATUS, 0, 0, 1) != 1) { - DSSERR("Failed to sysreset PLL\n"); - return -ETIMEDOUT; - } - - return 0; -} - -static int hdmi_phy_init(void) -{ - u16 r = 0; - - r = hdmi_set_phy_pwr(HDMI_PHYPWRCMD_LDOON); - if (r) - return r; - - r = hdmi_set_phy_pwr(HDMI_PHYPWRCMD_TXON); - if (r) - return r; - - /* - * Read address 0 in order to get the SCP reset done completed - * Dummy access performed to make sure reset is done - */ - hdmi_read_reg(HDMI_TXPHY_TX_CTRL); - - /* - * Write to phy address 0 to configure the clock - * use HFBITCLK write HDMI_TXPHY_TX_CONTROL_FREQOUT field - */ - REG_FLD_MOD(HDMI_TXPHY_TX_CTRL, 0x1, 31, 30); - - /* Write to phy address 1 to start HDMI line (TXVALID and TMDSCLKEN) */ - hdmi_write_reg(HDMI_TXPHY_DIGITAL_CTRL, 0xF0000000); - - /* Setup max LDO voltage */ - REG_FLD_MOD(HDMI_TXPHY_POWER_CTRL, 0xB, 3, 0); - - /* Write to phy address 3 to change the polarity control */ - REG_FLD_MOD(HDMI_TXPHY_PAD_CFG_CTRL, 0x1, 27, 27); - - return 0; -} - -static int hdmi_pll_program(struct hdmi_pll_info *fmt) -{ - u16 r = 0; - enum hdmi_clk_refsel refsel; - - r = hdmi_set_pll_pwr(HDMI_PLLPWRCMD_ALLOFF); - if (r) - return r; - - r = hdmi_set_pll_pwr(HDMI_PLLPWRCMD_BOTHON_ALLCLKS); - if (r) - return r; - - r = hdmi_pll_reset(); - if (r) - return r; - - refsel = HDMI_REFSEL_SYSCLK; - - r = hdmi_pll_init(refsel, fmt->dcofreq, fmt, fmt->regsd); - if (r) - return r; - - return 0; -} - -static void hdmi_phy_off(void) -{ - hdmi_set_phy_pwr(HDMI_PHYPWRCMD_OFF); -} - -static int hdmi_core_ddc_edid(u8 *pedid, int ext) -{ - u32 i, j; - char checksum = 0; - u32 offset = 0; - - /* Turn on CLK for DDC */ - REG_FLD_MOD(HDMI_CORE_AV_DPD, 0x7, 2, 0); - - /* - * SW HACK : Without the Delay DDC(i2c bus) reads 0 values / - * right shifted values( The behavior is not consistent and seen only - * with some TV's) - */ - usleep_range(800, 1000); - - if (!ext) { - /* Clk SCL Devices */ - REG_FLD_MOD(HDMI_CORE_DDC_CMD, 0xA, 3, 0); - - /* HDMI_CORE_DDC_STATUS_IN_PROG */ - if (hdmi_wait_for_bit_change(HDMI_CORE_DDC_STATUS, - 4, 4, 0) != 0) { - DSSERR("Failed to program DDC\n"); - return -ETIMEDOUT; - } - - /* Clear FIFO */ - REG_FLD_MOD(HDMI_CORE_DDC_CMD, 0x9, 3, 0); - - /* HDMI_CORE_DDC_STATUS_IN_PROG */ - if (hdmi_wait_for_bit_change(HDMI_CORE_DDC_STATUS, - 4, 4, 0) != 0) { - DSSERR("Failed to program DDC\n"); - return -ETIMEDOUT; - } - - } else { - if (ext % 2 != 0) - offset = 0x80; - } - - /* Load Segment Address Register */ - REG_FLD_MOD(HDMI_CORE_DDC_SEGM, ext/2, 7, 0); - - /* Load Slave Address Register */ - REG_FLD_MOD(HDMI_CORE_DDC_ADDR, 0xA0 >> 1, 7, 1); - - /* Load Offset Address Register */ - REG_FLD_MOD(HDMI_CORE_DDC_OFFSET, offset, 7, 0); - - /* Load Byte Count */ - REG_FLD_MOD(HDMI_CORE_DDC_COUNT1, 0x80, 7, 0); - REG_FLD_MOD(HDMI_CORE_DDC_COUNT2, 0x0, 1, 0); - - /* Set DDC_CMD */ - if (ext) - REG_FLD_MOD(HDMI_CORE_DDC_CMD, 0x4, 3, 0); - else - REG_FLD_MOD(HDMI_CORE_DDC_CMD, 0x2, 3, 0); - - /* HDMI_CORE_DDC_STATUS_BUS_LOW */ - if (REG_GET(HDMI_CORE_DDC_STATUS, 6, 6) == 1) { - DSSWARN("I2C Bus Low?\n"); - return -EIO; - } - /* HDMI_CORE_DDC_STATUS_NO_ACK */ - if (REG_GET(HDMI_CORE_DDC_STATUS, 5, 5) == 1) { - DSSWARN("I2C No Ack\n"); - return -EIO; - } - - i = ext * 128; - j = 0; - while (((REG_GET(HDMI_CORE_DDC_STATUS, 4, 4) == 1) || - (REG_GET(HDMI_CORE_DDC_STATUS, 2, 2) == 0)) && - j < 128) { - - if (REG_GET(HDMI_CORE_DDC_STATUS, 2, 2) == 0) { - /* FIFO not empty */ - pedid[i++] = REG_GET(HDMI_CORE_DDC_DATA, 7, 0); - j++; - } - } - - for (j = 0; j < 128; j++) - checksum += pedid[j]; - - if (checksum != 0) { - DSSERR("E-EDID checksum failed!!\n"); - return -EIO; - } - - return 0; -} - -static int read_edid(u8 *pedid, u16 max_length) -{ - int r = 0, n = 0, i = 0; - int max_ext_blocks = (max_length / 128) - 1; - - r = hdmi_core_ddc_edid(pedid, 0); - if (r) { - return r; - } else { - n = pedid[0x7e]; - - /* - * README: need to comply with max_length set by the caller. - * Better implementation should be to allocate necessary - * memory to store EDID according to nb_block field found - * in first block - */ - if (n > max_ext_blocks) - n = max_ext_blocks; - - for (i = 1; i <= n; i++) { - r = hdmi_core_ddc_edid(pedid, i); - if (r) - return r; - } - } + dss_init_hdmi_ip_ops(&hdmi.ip_data); return 0; } @@ -518,7 +212,7 @@ static struct hdmi_cm hdmi_get_code(struct omap_video_timings *timing) { int i = 0, code = -1, temp_vsync = 0, temp_hsync = 0; int timing_vsync = 0, timing_hsync = 0; - struct omap_video_timings temp; + struct hdmi_video_timings temp; struct hdmi_cm cm = {-1}; DSSDBG("hdmi_get_code\n"); @@ -556,500 +250,6 @@ static struct hdmi_cm hdmi_get_code(struct omap_video_timings *timing) return cm; } -static void get_horz_vert_timing_info(int current_descriptor_addrs, u8 *edid , - struct omap_video_timings *timings) -{ - /* X and Y resolution */ - timings->x_res = (((edid[current_descriptor_addrs + 4] & 0xF0) << 4) | - edid[current_descriptor_addrs + 2]); - timings->y_res = (((edid[current_descriptor_addrs + 7] & 0xF0) << 4) | - edid[current_descriptor_addrs + 5]); - - timings->pixel_clock = ((edid[current_descriptor_addrs + 1] << 8) | - edid[current_descriptor_addrs]); - - timings->pixel_clock = 10 * timings->pixel_clock; - - /* HORIZONTAL FRONT PORCH */ - timings->hfp = edid[current_descriptor_addrs + 8] | - ((edid[current_descriptor_addrs + 11] & 0xc0) << 2); - /* HORIZONTAL SYNC WIDTH */ - timings->hsw = edid[current_descriptor_addrs + 9] | - ((edid[current_descriptor_addrs + 11] & 0x30) << 4); - /* HORIZONTAL BACK PORCH */ - timings->hbp = (((edid[current_descriptor_addrs + 4] & 0x0F) << 8) | - edid[current_descriptor_addrs + 3]) - - (timings->hfp + timings->hsw); - /* VERTICAL FRONT PORCH */ - timings->vfp = ((edid[current_descriptor_addrs + 10] & 0xF0) >> 4) | - ((edid[current_descriptor_addrs + 11] & 0x0f) << 2); - /* VERTICAL SYNC WIDTH */ - timings->vsw = (edid[current_descriptor_addrs + 10] & 0x0F) | - ((edid[current_descriptor_addrs + 11] & 0x03) << 4); - /* VERTICAL BACK PORCH */ - timings->vbp = (((edid[current_descriptor_addrs + 7] & 0x0F) << 8) | - edid[current_descriptor_addrs + 6]) - - (timings->vfp + timings->vsw); - -} - -/* Description : This function gets the resolution information from EDID */ -static void get_edid_timing_data(u8 *edid) -{ - u8 count; - u16 current_descriptor_addrs; - struct hdmi_cm cm; - struct omap_video_timings edid_timings; - - /* search block 0, there are 4 DTDs arranged in priority order */ - for (count = 0; count < EDID_SIZE_BLOCK0_TIMING_DESCRIPTOR; count++) { - current_descriptor_addrs = - EDID_DESCRIPTOR_BLOCK0_ADDRESS + - count * EDID_TIMING_DESCRIPTOR_SIZE; - get_horz_vert_timing_info(current_descriptor_addrs, - edid, &edid_timings); - cm = hdmi_get_code(&edid_timings); - DSSDBG("Block0[%d] value matches code = %d , mode = %d\n", - count, cm.code, cm.mode); - if (cm.code == -1) { - continue; - } else { - hdmi.code = cm.code; - hdmi.mode = cm.mode; - DSSDBG("code = %d , mode = %d\n", - hdmi.code, hdmi.mode); - return; - } - } - if (edid[0x7e] != 0x00) { - for (count = 0; count < EDID_SIZE_BLOCK1_TIMING_DESCRIPTOR; - count++) { - current_descriptor_addrs = - EDID_DESCRIPTOR_BLOCK1_ADDRESS + - count * EDID_TIMING_DESCRIPTOR_SIZE; - get_horz_vert_timing_info(current_descriptor_addrs, - edid, &edid_timings); - cm = hdmi_get_code(&edid_timings); - DSSDBG("Block1[%d] value matches code = %d, mode = %d", - count, cm.code, cm.mode); - if (cm.code == -1) { - continue; - } else { - hdmi.code = cm.code; - hdmi.mode = cm.mode; - DSSDBG("code = %d , mode = %d\n", - hdmi.code, hdmi.mode); - return; - } - } - } - - DSSINFO("no valid timing found , falling back to VGA\n"); - hdmi.code = 4; /* setting default value of 640 480 VGA */ - hdmi.mode = HDMI_DVI; -} - -static void hdmi_read_edid(struct omap_video_timings *dp) -{ - int ret = 0, code; - - memset(hdmi.edid, 0, HDMI_EDID_MAX_LENGTH); - - if (!hdmi.edid_set) - ret = read_edid(hdmi.edid, HDMI_EDID_MAX_LENGTH); - - if (!ret) { - if (!memcmp(hdmi.edid, edid_header, sizeof(edid_header))) { - /* search for timings of default resolution */ - get_edid_timing_data(hdmi.edid); - hdmi.edid_set = true; - } - } else { - DSSWARN("failed to read E-EDID\n"); - } - - if (!hdmi.edid_set) { - DSSINFO("fallback to VGA\n"); - hdmi.code = 4; /* setting default value of 640 480 VGA */ - hdmi.mode = HDMI_DVI; - } - - code = get_timings_index(); - - *dp = cea_vesa_timings[code].timings; -} - -static void hdmi_core_init(struct hdmi_core_video_config *video_cfg, - struct hdmi_core_infoframe_avi *avi_cfg, - struct hdmi_core_packet_enable_repeat *repeat_cfg) -{ - DSSDBG("Enter hdmi_core_init\n"); - - /* video core */ - video_cfg->ip_bus_width = HDMI_INPUT_8BIT; - video_cfg->op_dither_truc = HDMI_OUTPUTTRUNCATION_8BIT; - video_cfg->deep_color_pkt = HDMI_DEEPCOLORPACKECTDISABLE; - video_cfg->pkt_mode = HDMI_PACKETMODERESERVEDVALUE; - video_cfg->hdmi_dvi = HDMI_DVI; - video_cfg->tclk_sel_clkmult = HDMI_FPLL10IDCK; - - /* info frame */ - avi_cfg->db1_format = 0; - avi_cfg->db1_active_info = 0; - avi_cfg->db1_bar_info_dv = 0; - avi_cfg->db1_scan_info = 0; - avi_cfg->db2_colorimetry = 0; - avi_cfg->db2_aspect_ratio = 0; - avi_cfg->db2_active_fmt_ar = 0; - avi_cfg->db3_itc = 0; - avi_cfg->db3_ec = 0; - avi_cfg->db3_q_range = 0; - avi_cfg->db3_nup_scaling = 0; - avi_cfg->db4_videocode = 0; - avi_cfg->db5_pixel_repeat = 0; - avi_cfg->db6_7_line_eoftop = 0 ; - avi_cfg->db8_9_line_sofbottom = 0; - avi_cfg->db10_11_pixel_eofleft = 0; - avi_cfg->db12_13_pixel_sofright = 0; - - /* packet enable and repeat */ - repeat_cfg->audio_pkt = 0; - repeat_cfg->audio_pkt_repeat = 0; - repeat_cfg->avi_infoframe = 0; - repeat_cfg->avi_infoframe_repeat = 0; - repeat_cfg->gen_cntrl_pkt = 0; - repeat_cfg->gen_cntrl_pkt_repeat = 0; - repeat_cfg->generic_pkt = 0; - repeat_cfg->generic_pkt_repeat = 0; -} - -static void hdmi_core_powerdown_disable(void) -{ - DSSDBG("Enter hdmi_core_powerdown_disable\n"); - REG_FLD_MOD(HDMI_CORE_CTRL1, 0x0, 0, 0); -} - -static void hdmi_core_swreset_release(void) -{ - DSSDBG("Enter hdmi_core_swreset_release\n"); - REG_FLD_MOD(HDMI_CORE_SYS_SRST, 0x0, 0, 0); -} - -static void hdmi_core_swreset_assert(void) -{ - DSSDBG("Enter hdmi_core_swreset_assert\n"); - REG_FLD_MOD(HDMI_CORE_SYS_SRST, 0x1, 0, 0); -} - -/* DSS_HDMI_CORE_VIDEO_CONFIG */ -static void hdmi_core_video_config(struct hdmi_core_video_config *cfg) -{ - u32 r = 0; - - /* sys_ctrl1 default configuration not tunable */ - r = hdmi_read_reg(HDMI_CORE_CTRL1); - r = FLD_MOD(r, HDMI_CORE_CTRL1_VEN_FOLLOWVSYNC, 5, 5); - r = FLD_MOD(r, HDMI_CORE_CTRL1_HEN_FOLLOWHSYNC, 4, 4); - r = FLD_MOD(r, HDMI_CORE_CTRL1_BSEL_24BITBUS, 2, 2); - r = FLD_MOD(r, HDMI_CORE_CTRL1_EDGE_RISINGEDGE, 1, 1); - hdmi_write_reg(HDMI_CORE_CTRL1, r); - - REG_FLD_MOD(HDMI_CORE_SYS_VID_ACEN, cfg->ip_bus_width, 7, 6); - - /* Vid_Mode */ - r = hdmi_read_reg(HDMI_CORE_SYS_VID_MODE); - - /* dither truncation configuration */ - if (cfg->op_dither_truc > HDMI_OUTPUTTRUNCATION_12BIT) { - r = FLD_MOD(r, cfg->op_dither_truc - 3, 7, 6); - r = FLD_MOD(r, 1, 5, 5); - } else { - r = FLD_MOD(r, cfg->op_dither_truc, 7, 6); - r = FLD_MOD(r, 0, 5, 5); - } - hdmi_write_reg(HDMI_CORE_SYS_VID_MODE, r); - - /* HDMI_Ctrl */ - r = hdmi_read_reg(HDMI_CORE_AV_HDMI_CTRL); - r = FLD_MOD(r, cfg->deep_color_pkt, 6, 6); - r = FLD_MOD(r, cfg->pkt_mode, 5, 3); - r = FLD_MOD(r, cfg->hdmi_dvi, 0, 0); - hdmi_write_reg(HDMI_CORE_AV_HDMI_CTRL, r); - - /* TMDS_CTRL */ - REG_FLD_MOD(HDMI_CORE_SYS_TMDS_CTRL, - cfg->tclk_sel_clkmult, 6, 5); -} - -static void hdmi_core_aux_infoframe_avi_config( - struct hdmi_core_infoframe_avi info_avi) -{ - u32 val; - char sum = 0, checksum = 0; - - sum += 0x82 + 0x002 + 0x00D; - hdmi_write_reg(HDMI_CORE_AV_AVI_TYPE, 0x082); - hdmi_write_reg(HDMI_CORE_AV_AVI_VERS, 0x002); - hdmi_write_reg(HDMI_CORE_AV_AVI_LEN, 0x00D); - - val = (info_avi.db1_format << 5) | - (info_avi.db1_active_info << 4) | - (info_avi.db1_bar_info_dv << 2) | - (info_avi.db1_scan_info); - hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(0), val); - sum += val; - - val = (info_avi.db2_colorimetry << 6) | - (info_avi.db2_aspect_ratio << 4) | - (info_avi.db2_active_fmt_ar); - hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(1), val); - sum += val; - - val = (info_avi.db3_itc << 7) | - (info_avi.db3_ec << 4) | - (info_avi.db3_q_range << 2) | - (info_avi.db3_nup_scaling); - hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(2), val); - sum += val; - - hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(3), info_avi.db4_videocode); - sum += info_avi.db4_videocode; - - val = info_avi.db5_pixel_repeat; - hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(4), val); - sum += val; - - val = info_avi.db6_7_line_eoftop & 0x00FF; - hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(5), val); - sum += val; - - val = ((info_avi.db6_7_line_eoftop >> 8) & 0x00FF); - hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(6), val); - sum += val; - - val = info_avi.db8_9_line_sofbottom & 0x00FF; - hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(7), val); - sum += val; - - val = ((info_avi.db8_9_line_sofbottom >> 8) & 0x00FF); - hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(8), val); - sum += val; - - val = info_avi.db10_11_pixel_eofleft & 0x00FF; - hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(9), val); - sum += val; - - val = ((info_avi.db10_11_pixel_eofleft >> 8) & 0x00FF); - hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(10), val); - sum += val; - - val = info_avi.db12_13_pixel_sofright & 0x00FF; - hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(11), val); - sum += val; - - val = ((info_avi.db12_13_pixel_sofright >> 8) & 0x00FF); - hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(12), val); - sum += val; - - checksum = 0x100 - sum; - hdmi_write_reg(HDMI_CORE_AV_AVI_CHSUM, checksum); -} - -static void hdmi_core_av_packet_config( - struct hdmi_core_packet_enable_repeat repeat_cfg) -{ - /* enable/repeat the infoframe */ - hdmi_write_reg(HDMI_CORE_AV_PB_CTRL1, - (repeat_cfg.audio_pkt << 5) | - (repeat_cfg.audio_pkt_repeat << 4) | - (repeat_cfg.avi_infoframe << 1) | - (repeat_cfg.avi_infoframe_repeat)); - - /* enable/repeat the packet */ - hdmi_write_reg(HDMI_CORE_AV_PB_CTRL2, - (repeat_cfg.gen_cntrl_pkt << 3) | - (repeat_cfg.gen_cntrl_pkt_repeat << 2) | - (repeat_cfg.generic_pkt << 1) | - (repeat_cfg.generic_pkt_repeat)); -} - -static void hdmi_wp_init(struct omap_video_timings *timings, - struct hdmi_video_format *video_fmt, - struct hdmi_video_interface *video_int) -{ - DSSDBG("Enter hdmi_wp_init\n"); - - timings->hbp = 0; - timings->hfp = 0; - timings->hsw = 0; - timings->vbp = 0; - timings->vfp = 0; - timings->vsw = 0; - - video_fmt->packing_mode = HDMI_PACK_10b_RGB_YUV444; - video_fmt->y_res = 0; - video_fmt->x_res = 0; - - video_int->vsp = 0; - video_int->hsp = 0; - - video_int->interlacing = 0; - video_int->tm = 0; /* HDMI_TIMING_SLAVE */ - -} - -static void hdmi_wp_video_start(bool start) -{ - REG_FLD_MOD(HDMI_WP_VIDEO_CFG, start, 31, 31); -} - -static void hdmi_wp_video_init_format(struct hdmi_video_format *video_fmt, - struct omap_video_timings *timings, struct hdmi_config *param) -{ - DSSDBG("Enter hdmi_wp_video_init_format\n"); - - video_fmt->y_res = param->timings.timings.y_res; - video_fmt->x_res = param->timings.timings.x_res; - - timings->hbp = param->timings.timings.hbp; - timings->hfp = param->timings.timings.hfp; - timings->hsw = param->timings.timings.hsw; - timings->vbp = param->timings.timings.vbp; - timings->vfp = param->timings.timings.vfp; - timings->vsw = param->timings.timings.vsw; -} - -static void hdmi_wp_video_config_format( - struct hdmi_video_format *video_fmt) -{ - u32 l = 0; - - REG_FLD_MOD(HDMI_WP_VIDEO_CFG, video_fmt->packing_mode, 10, 8); - - l |= FLD_VAL(video_fmt->y_res, 31, 16); - l |= FLD_VAL(video_fmt->x_res, 15, 0); - hdmi_write_reg(HDMI_WP_VIDEO_SIZE, l); -} - -static void hdmi_wp_video_config_interface( - struct hdmi_video_interface *video_int) -{ - u32 r; - DSSDBG("Enter hdmi_wp_video_config_interface\n"); - - r = hdmi_read_reg(HDMI_WP_VIDEO_CFG); - r = FLD_MOD(r, video_int->vsp, 7, 7); - r = FLD_MOD(r, video_int->hsp, 6, 6); - r = FLD_MOD(r, video_int->interlacing, 3, 3); - r = FLD_MOD(r, video_int->tm, 1, 0); - hdmi_write_reg(HDMI_WP_VIDEO_CFG, r); -} - -static void hdmi_wp_video_config_timing( - struct omap_video_timings *timings) -{ - u32 timing_h = 0; - u32 timing_v = 0; - - DSSDBG("Enter hdmi_wp_video_config_timing\n"); - - timing_h |= FLD_VAL(timings->hbp, 31, 20); - timing_h |= FLD_VAL(timings->hfp, 19, 8); - timing_h |= FLD_VAL(timings->hsw, 7, 0); - hdmi_write_reg(HDMI_WP_VIDEO_TIMING_H, timing_h); - - timing_v |= FLD_VAL(timings->vbp, 31, 20); - timing_v |= FLD_VAL(timings->vfp, 19, 8); - timing_v |= FLD_VAL(timings->vsw, 7, 0); - hdmi_write_reg(HDMI_WP_VIDEO_TIMING_V, timing_v); -} - -static void hdmi_basic_configure(struct hdmi_config *cfg) -{ - /* HDMI */ - struct omap_video_timings video_timing; - struct hdmi_video_format video_format; - struct hdmi_video_interface video_interface; - /* HDMI core */ - struct hdmi_core_infoframe_avi avi_cfg; - struct hdmi_core_video_config v_core_cfg; - struct hdmi_core_packet_enable_repeat repeat_cfg; - - hdmi_wp_init(&video_timing, &video_format, - &video_interface); - - hdmi_core_init(&v_core_cfg, - &avi_cfg, - &repeat_cfg); - - hdmi_wp_video_init_format(&video_format, - &video_timing, cfg); - - hdmi_wp_video_config_timing(&video_timing); - - /* video config */ - video_format.packing_mode = HDMI_PACK_24b_RGB_YUV444_YUV422; - - hdmi_wp_video_config_format(&video_format); - - video_interface.vsp = cfg->timings.vsync_pol; - video_interface.hsp = cfg->timings.hsync_pol; - video_interface.interlacing = cfg->interlace; - video_interface.tm = 1 ; /* HDMI_TIMING_MASTER_24BIT */ - - hdmi_wp_video_config_interface(&video_interface); - - /* - * configure core video part - * set software reset in the core - */ - hdmi_core_swreset_assert(); - - /* power down off */ - hdmi_core_powerdown_disable(); - - v_core_cfg.pkt_mode = HDMI_PACKETMODE24BITPERPIXEL; - v_core_cfg.hdmi_dvi = cfg->cm.mode; - - hdmi_core_video_config(&v_core_cfg); - - /* release software reset in the core */ - hdmi_core_swreset_release(); - - /* - * configure packet - * info frame video see doc CEA861-D page 65 - */ - avi_cfg.db1_format = HDMI_INFOFRAME_AVI_DB1Y_RGB; - avi_cfg.db1_active_info = - HDMI_INFOFRAME_AVI_DB1A_ACTIVE_FORMAT_OFF; - avi_cfg.db1_bar_info_dv = HDMI_INFOFRAME_AVI_DB1B_NO; - avi_cfg.db1_scan_info = HDMI_INFOFRAME_AVI_DB1S_0; - avi_cfg.db2_colorimetry = HDMI_INFOFRAME_AVI_DB2C_NO; - avi_cfg.db2_aspect_ratio = HDMI_INFOFRAME_AVI_DB2M_NO; - avi_cfg.db2_active_fmt_ar = HDMI_INFOFRAME_AVI_DB2R_SAME; - avi_cfg.db3_itc = HDMI_INFOFRAME_AVI_DB3ITC_NO; - avi_cfg.db3_ec = HDMI_INFOFRAME_AVI_DB3EC_XVYUV601; - avi_cfg.db3_q_range = HDMI_INFOFRAME_AVI_DB3Q_DEFAULT; - avi_cfg.db3_nup_scaling = HDMI_INFOFRAME_AVI_DB3SC_NO; - avi_cfg.db4_videocode = cfg->cm.code; - avi_cfg.db5_pixel_repeat = HDMI_INFOFRAME_AVI_DB5PR_NO; - avi_cfg.db6_7_line_eoftop = 0; - avi_cfg.db8_9_line_sofbottom = 0; - avi_cfg.db10_11_pixel_eofleft = 0; - avi_cfg.db12_13_pixel_sofright = 0; - - hdmi_core_aux_infoframe_avi_config(avi_cfg); - - /* enable/repeat the infoframe */ - repeat_cfg.avi_infoframe = HDMI_PACKETENABLE; - repeat_cfg.avi_infoframe_repeat = HDMI_PACKETREPEATON; - /* wakeup */ - repeat_cfg.audio_pkt = HDMI_PACKETENABLE; - repeat_cfg.audio_pkt_repeat = HDMI_PACKETREPEATON; - hdmi_core_av_packet_config(repeat_cfg); -} - static void update_hdmi_timings(struct hdmi_config *cfg, struct omap_video_timings *timings, int code) { @@ -1066,6 +266,12 @@ static void update_hdmi_timings(struct hdmi_config *cfg, cfg->timings.hsync_pol = cea_vesa_timings[code].hsync_pol; } +unsigned long hdmi_get_pixel_clock(void) +{ + /* HDMI Pixel Clock in Mhz */ + return hdmi.ip_data.cfg.timings.timings.pixel_clock * 10000; +} + static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy, struct hdmi_pll_info *pi) { @@ -1077,15 +283,23 @@ static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy, * Input clock is predivided by N + 1 * out put of which is reference clk */ - pi->regn = dssdev->clocks.hdmi.regn; - refclk = clkin / (pi->regn + 1); + if (dssdev->clocks.hdmi.regn == 0) + pi->regn = HDMI_DEFAULT_REGN; + else + pi->regn = dssdev->clocks.hdmi.regn; + + refclk = clkin / pi->regn; /* * multiplier is pixel_clk/ref_clk * Multiplying by 100 to avoid fractional part removal */ pi->regm = (phy * 100 / (refclk)) / 100; - pi->regm2 = dssdev->clocks.hdmi.regm2; + + if (dssdev->clocks.hdmi.regm2 == 0) + pi->regm2 = HDMI_DEFAULT_REGM2; + else + pi->regm2 = dssdev->clocks.hdmi.regm2; /* * fractional multiplier is remainder of the difference between @@ -1100,7 +314,10 @@ static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy, * is greater than 1000MHz */ pi->dcofreq = phy > 1000 * 100; - pi->regsd = ((pi->regm * clkin / 10) / ((pi->regn + 1) * 250) + 5) / 10; + pi->regsd = ((pi->regm * clkin / 10) / (pi->regn * 250) + 5) / 10; + + /* Set the reference clock to sysclk reference */ + pi->refsel = HDMI_REFSEL_SYSCLK; DSSDBG("M = %d Mf = %d\n", pi->regm, pi->regmf); DSSDBG("range = %d sd = %d\n", pi->dcofreq, pi->regsd); @@ -1109,7 +326,6 @@ static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy, static int hdmi_power_on(struct omap_dss_device *dssdev) { int r, code = 0; - struct hdmi_pll_info pll_data; struct omap_video_timings *p; unsigned long phy; @@ -1117,7 +333,7 @@ static int hdmi_power_on(struct omap_dss_device *dssdev) if (r) return r; - dispc_enable_channel(OMAP_DSS_CHANNEL_DIGIT, 0); + dispc_mgr_enable(OMAP_DSS_CHANNEL_DIGIT, 0); p = &dssdev->panel.timings; @@ -1125,36 +341,31 @@ static int hdmi_power_on(struct omap_dss_device *dssdev) dssdev->panel.timings.x_res, dssdev->panel.timings.y_res); - if (!hdmi.custom_set) { - DSSDBG("Read EDID as no EDID is not set on poweron\n"); - hdmi_read_edid(p); - } code = get_timings_index(); - dssdev->panel.timings = cea_vesa_timings[code].timings; - update_hdmi_timings(&hdmi.cfg, p, code); + update_hdmi_timings(&hdmi.ip_data.cfg, p, code); phy = p->pixel_clock; - hdmi_compute_pll(dssdev, phy, &pll_data); + hdmi_compute_pll(dssdev, phy, &hdmi.ip_data.pll_data); - hdmi_wp_video_start(0); + hdmi.ip_data.ops->video_enable(&hdmi.ip_data, 0); - /* config the PLL and PHY first */ - r = hdmi_pll_program(&pll_data); + /* config the PLL and PHY hdmi_set_pll_pwrfirst */ + r = hdmi.ip_data.ops->pll_enable(&hdmi.ip_data); if (r) { DSSDBG("Failed to lock PLL\n"); goto err; } - r = hdmi_phy_init(); + r = hdmi.ip_data.ops->phy_enable(&hdmi.ip_data); if (r) { DSSDBG("Failed to start PHY\n"); goto err; } - hdmi.cfg.cm.mode = hdmi.mode; - hdmi.cfg.cm.code = hdmi.code; - hdmi_basic_configure(&hdmi.cfg); + hdmi.ip_data.cfg.cm.mode = hdmi.mode; + hdmi.ip_data.cfg.cm.code = hdmi.code; + hdmi.ip_data.ops->video_configure(&hdmi.ip_data); /* Make selection of HDMI in DSS */ dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK); @@ -1174,9 +385,9 @@ static int hdmi_power_on(struct omap_dss_device *dssdev) dispc_set_digit_size(dssdev->panel.timings.x_res, dssdev->panel.timings.y_res); - dispc_enable_channel(OMAP_DSS_CHANNEL_DIGIT, 1); + hdmi.ip_data.ops->video_enable(&hdmi.ip_data, 1); - hdmi_wp_video_start(1); + dispc_mgr_enable(OMAP_DSS_CHANNEL_DIGIT, 1); return 0; err: @@ -1186,14 +397,12 @@ err: static void hdmi_power_off(struct omap_dss_device *dssdev) { - dispc_enable_channel(OMAP_DSS_CHANNEL_DIGIT, 0); + dispc_mgr_enable(OMAP_DSS_CHANNEL_DIGIT, 0); - hdmi_wp_video_start(0); - hdmi_phy_off(); - hdmi_set_pll_pwr(HDMI_PLLPWRCMD_ALLOFF); + hdmi.ip_data.ops->video_enable(&hdmi.ip_data, 0); + hdmi.ip_data.ops->phy_disable(&hdmi.ip_data); + hdmi.ip_data.ops->pll_disable(&hdmi.ip_data); hdmi_runtime_put(); - - hdmi.edid_set = 0; } int omapdss_hdmi_display_check_timing(struct omap_dss_device *dssdev, @@ -1203,7 +412,6 @@ int omapdss_hdmi_display_check_timing(struct omap_dss_device *dssdev, cm = hdmi_get_code(timings); if (cm.code == -1) { - DSSERR("Invalid timing entered\n"); return -EINVAL; } @@ -1215,12 +423,69 @@ void omapdss_hdmi_display_set_timing(struct omap_dss_device *dssdev) { struct hdmi_cm cm; - hdmi.custom_set = 1; cm = hdmi_get_code(&dssdev->panel.timings); hdmi.code = cm.code; hdmi.mode = cm.mode; - omapdss_hdmi_display_enable(dssdev); - hdmi.custom_set = 0; + + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { + int r; + + hdmi_power_off(dssdev); + + r = hdmi_power_on(dssdev); + if (r) + DSSERR("failed to power on device\n"); + } +} + +void hdmi_dump_regs(struct seq_file *s) +{ + mutex_lock(&hdmi.lock); + + if (hdmi_runtime_get()) + return; + + hdmi.ip_data.ops->dump_wrapper(&hdmi.ip_data, s); + hdmi.ip_data.ops->dump_pll(&hdmi.ip_data, s); + hdmi.ip_data.ops->dump_phy(&hdmi.ip_data, s); + hdmi.ip_data.ops->dump_core(&hdmi.ip_data, s); + + hdmi_runtime_put(); + mutex_unlock(&hdmi.lock); +} + +int omapdss_hdmi_read_edid(u8 *buf, int len) +{ + int r; + + mutex_lock(&hdmi.lock); + + r = hdmi_runtime_get(); + BUG_ON(r); + + r = hdmi.ip_data.ops->read_edid(&hdmi.ip_data, buf, len); + + hdmi_runtime_put(); + mutex_unlock(&hdmi.lock); + + return r; +} + +bool omapdss_hdmi_detect(void) +{ + int r; + + mutex_lock(&hdmi.lock); + + r = hdmi_runtime_get(); + BUG_ON(r); + + r = hdmi.ip_data.ops->detect(&hdmi.ip_data); + + hdmi_runtime_put(); + mutex_unlock(&hdmi.lock); + + return r == 1; } int omapdss_hdmi_display_enable(struct omap_dss_device *dssdev) @@ -1231,6 +496,12 @@ int omapdss_hdmi_display_enable(struct omap_dss_device *dssdev) mutex_lock(&hdmi.lock); + if (dssdev->manager == NULL) { + DSSERR("failed to enable display: no manager\n"); + r = -ENODEV; + goto err0; + } + r = omap_dss_start_device(dssdev); if (r) { DSSERR("failed to start device\n"); @@ -1282,219 +553,9 @@ void omapdss_hdmi_display_disable(struct omap_dss_device *dssdev) #if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) -static void hdmi_wp_audio_config_format( - struct hdmi_audio_format *aud_fmt) -{ - u32 r; - - DSSDBG("Enter hdmi_wp_audio_config_format\n"); - - r = hdmi_read_reg(HDMI_WP_AUDIO_CFG); - r = FLD_MOD(r, aud_fmt->stereo_channels, 26, 24); - r = FLD_MOD(r, aud_fmt->active_chnnls_msk, 23, 16); - r = FLD_MOD(r, aud_fmt->en_sig_blk_strt_end, 5, 5); - r = FLD_MOD(r, aud_fmt->type, 4, 4); - r = FLD_MOD(r, aud_fmt->justification, 3, 3); - r = FLD_MOD(r, aud_fmt->sample_order, 2, 2); - r = FLD_MOD(r, aud_fmt->samples_per_word, 1, 1); - r = FLD_MOD(r, aud_fmt->sample_size, 0, 0); - hdmi_write_reg(HDMI_WP_AUDIO_CFG, r); -} - -static void hdmi_wp_audio_config_dma(struct hdmi_audio_dma *aud_dma) -{ - u32 r; - - DSSDBG("Enter hdmi_wp_audio_config_dma\n"); - - r = hdmi_read_reg(HDMI_WP_AUDIO_CFG2); - r = FLD_MOD(r, aud_dma->transfer_size, 15, 8); - r = FLD_MOD(r, aud_dma->block_size, 7, 0); - hdmi_write_reg(HDMI_WP_AUDIO_CFG2, r); - - r = hdmi_read_reg(HDMI_WP_AUDIO_CTRL); - r = FLD_MOD(r, aud_dma->mode, 9, 9); - r = FLD_MOD(r, aud_dma->fifo_threshold, 8, 0); - hdmi_write_reg(HDMI_WP_AUDIO_CTRL, r); -} - -static void hdmi_core_audio_config(struct hdmi_core_audio_config *cfg) -{ - u32 r; - - /* audio clock recovery parameters */ - r = hdmi_read_reg(HDMI_CORE_AV_ACR_CTRL); - r = FLD_MOD(r, cfg->use_mclk, 2, 2); - r = FLD_MOD(r, cfg->en_acr_pkt, 1, 1); - r = FLD_MOD(r, cfg->cts_mode, 0, 0); - hdmi_write_reg(HDMI_CORE_AV_ACR_CTRL, r); - - REG_FLD_MOD(HDMI_CORE_AV_N_SVAL1, cfg->n, 7, 0); - REG_FLD_MOD(HDMI_CORE_AV_N_SVAL2, cfg->n >> 8, 7, 0); - REG_FLD_MOD(HDMI_CORE_AV_N_SVAL3, cfg->n >> 16, 7, 0); - - if (cfg->cts_mode == HDMI_AUDIO_CTS_MODE_SW) { - REG_FLD_MOD(HDMI_CORE_AV_CTS_SVAL1, cfg->cts, 7, 0); - REG_FLD_MOD(HDMI_CORE_AV_CTS_SVAL2, cfg->cts >> 8, 7, 0); - REG_FLD_MOD(HDMI_CORE_AV_CTS_SVAL3, cfg->cts >> 16, 7, 0); - } else { - /* - * HDMI IP uses this configuration to divide the MCLK to - * update CTS value. - */ - REG_FLD_MOD(HDMI_CORE_AV_FREQ_SVAL, cfg->mclk_mode, 2, 0); - - /* Configure clock for audio packets */ - REG_FLD_MOD(HDMI_CORE_AV_AUD_PAR_BUSCLK_1, - cfg->aud_par_busclk, 7, 0); - REG_FLD_MOD(HDMI_CORE_AV_AUD_PAR_BUSCLK_2, - (cfg->aud_par_busclk >> 8), 7, 0); - REG_FLD_MOD(HDMI_CORE_AV_AUD_PAR_BUSCLK_3, - (cfg->aud_par_busclk >> 16), 7, 0); - } - - /* Override of SPDIF sample frequency with value in I2S_CHST4 */ - REG_FLD_MOD(HDMI_CORE_AV_SPDIF_CTRL, cfg->fs_override, 1, 1); - - /* I2S parameters */ - REG_FLD_MOD(HDMI_CORE_AV_I2S_CHST4, cfg->freq_sample, 3, 0); - - r = hdmi_read_reg(HDMI_CORE_AV_I2S_IN_CTRL); - r = FLD_MOD(r, cfg->i2s_cfg.en_high_bitrate_aud, 7, 7); - r = FLD_MOD(r, cfg->i2s_cfg.sck_edge_mode, 6, 6); - r = FLD_MOD(r, cfg->i2s_cfg.cbit_order, 5, 5); - r = FLD_MOD(r, cfg->i2s_cfg.vbit, 4, 4); - r = FLD_MOD(r, cfg->i2s_cfg.ws_polarity, 3, 3); - r = FLD_MOD(r, cfg->i2s_cfg.justification, 2, 2); - r = FLD_MOD(r, cfg->i2s_cfg.direction, 1, 1); - r = FLD_MOD(r, cfg->i2s_cfg.shift, 0, 0); - hdmi_write_reg(HDMI_CORE_AV_I2S_IN_CTRL, r); - - r = hdmi_read_reg(HDMI_CORE_AV_I2S_CHST5); - r = FLD_MOD(r, cfg->freq_sample, 7, 4); - r = FLD_MOD(r, cfg->i2s_cfg.word_length, 3, 1); - r = FLD_MOD(r, cfg->i2s_cfg.word_max_length, 0, 0); - hdmi_write_reg(HDMI_CORE_AV_I2S_CHST5, r); - - REG_FLD_MOD(HDMI_CORE_AV_I2S_IN_LEN, cfg->i2s_cfg.in_length_bits, 3, 0); - - /* Audio channels and mode parameters */ - REG_FLD_MOD(HDMI_CORE_AV_HDMI_CTRL, cfg->layout, 2, 1); - r = hdmi_read_reg(HDMI_CORE_AV_AUD_MODE); - r = FLD_MOD(r, cfg->i2s_cfg.active_sds, 7, 4); - r = FLD_MOD(r, cfg->en_dsd_audio, 3, 3); - r = FLD_MOD(r, cfg->en_parallel_aud_input, 2, 2); - r = FLD_MOD(r, cfg->en_spdif, 1, 1); - hdmi_write_reg(HDMI_CORE_AV_AUD_MODE, r); -} - -static void hdmi_core_audio_infoframe_config( - struct hdmi_core_infoframe_audio *info_aud) -{ - u8 val; - u8 sum = 0, checksum = 0; - - /* - * Set audio info frame type, version and length as - * described in HDMI 1.4a Section 8.2.2 specification. - * Checksum calculation is defined in Section 5.3.5. - */ - hdmi_write_reg(HDMI_CORE_AV_AUDIO_TYPE, 0x84); - hdmi_write_reg(HDMI_CORE_AV_AUDIO_VERS, 0x01); - hdmi_write_reg(HDMI_CORE_AV_AUDIO_LEN, 0x0a); - sum += 0x84 + 0x001 + 0x00a; - - val = (info_aud->db1_coding_type << 4) - | (info_aud->db1_channel_count - 1); - hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(0), val); - sum += val; - - val = (info_aud->db2_sample_freq << 2) | info_aud->db2_sample_size; - hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(1), val); - sum += val; - - hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(2), 0x00); - - val = info_aud->db4_channel_alloc; - hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(3), val); - sum += val; - - val = (info_aud->db5_downmix_inh << 7) | (info_aud->db5_lsv << 3); - hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(4), val); - sum += val; - - hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(5), 0x00); - hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(6), 0x00); - hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(7), 0x00); - hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(8), 0x00); - hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(9), 0x00); - checksum = 0x100 - sum; - hdmi_write_reg(HDMI_CORE_AV_AUDIO_CHSUM, checksum); - - /* - * TODO: Add MPEG and SPD enable and repeat cfg when EDID parsing - * is available. - */ -} - -static int hdmi_config_audio_acr(u32 sample_freq, u32 *n, u32 *cts) -{ - u32 r; - u32 deep_color = 0; - u32 pclk = hdmi.cfg.timings.timings.pixel_clock; - - if (n == NULL || cts == NULL) - return -EINVAL; - /* - * Obtain current deep color configuration. This needed - * to calculate the TMDS clock based on the pixel clock. - */ - r = REG_GET(HDMI_WP_VIDEO_CFG, 1, 0); - switch (r) { - case 1: /* No deep color selected */ - deep_color = 100; - break; - case 2: /* 10-bit deep color selected */ - deep_color = 125; - break; - case 3: /* 12-bit deep color selected */ - deep_color = 150; - break; - default: - return -EINVAL; - } - - switch (sample_freq) { - case 32000: - if ((deep_color == 125) && ((pclk == 54054) - || (pclk == 74250))) - *n = 8192; - else - *n = 4096; - break; - case 44100: - *n = 6272; - break; - case 48000: - if ((deep_color == 125) && ((pclk == 54054) - || (pclk == 74250))) - *n = 8192; - else - *n = 6144; - break; - default: - *n = 0; - return -EINVAL; - } - - /* Calculate CTS. See HDMI 1.3a or 1.4a specifications */ - *cts = pclk * (*n / 128) * deep_color / (sample_freq / 10); - - return 0; -} - -static int hdmi_audio_hw_params(struct snd_pcm_substream *substream, +static int hdmi_audio_hw_params(struct hdmi_ip_data *ip_data, + struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { @@ -1548,7 +609,7 @@ static int hdmi_audio_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - err = hdmi_config_audio_acr(params_rate(params), &n, &cts); + err = hdmi_config_audio_acr(ip_data, params_rate(params), &n, &cts); if (err < 0) return err; @@ -1564,8 +625,8 @@ static int hdmi_audio_hw_params(struct snd_pcm_substream *substream, audio_dma.mode = HDMI_AUDIO_TRANSF_DMA; audio_dma.fifo_threshold = 0x20; /* in number of samples */ - hdmi_wp_audio_config_dma(&audio_dma); - hdmi_wp_audio_config_format(&audio_format); + hdmi_wp_audio_config_dma(ip_data, &audio_dma); + hdmi_wp_audio_config_format(ip_data, &audio_format); /* * I2S config @@ -1609,7 +670,7 @@ static int hdmi_audio_hw_params(struct snd_pcm_substream *substream, /* Use parallel audio interface */ core_cfg.en_parallel_aud_input = true; - hdmi_core_audio_config(&core_cfg); + hdmi_core_audio_config(ip_data, &core_cfg); /* * Configure packet @@ -1623,36 +684,10 @@ static int hdmi_audio_hw_params(struct snd_pcm_substream *substream, aud_if_cfg.db5_downmix_inh = false; aud_if_cfg.db5_lsv = 0; - hdmi_core_audio_infoframe_config(&aud_if_cfg); + hdmi_core_audio_infoframe_config(ip_data, &aud_if_cfg); return 0; } -static int hdmi_audio_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) -{ - int err = 0; - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - REG_FLD_MOD(HDMI_CORE_AV_AUD_MODE, 1, 0, 0); - REG_FLD_MOD(HDMI_WP_AUDIO_CTRL, 1, 31, 31); - REG_FLD_MOD(HDMI_WP_AUDIO_CTRL, 1, 30, 30); - break; - - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - REG_FLD_MOD(HDMI_CORE_AV_AUD_MODE, 0, 0, 0); - REG_FLD_MOD(HDMI_WP_AUDIO_CTRL, 0, 30, 30); - REG_FLD_MOD(HDMI_WP_AUDIO_CTRL, 0, 31, 31); - break; - default: - err = -EINVAL; - } - return err; -} - static int hdmi_audio_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -1698,15 +733,6 @@ static int hdmi_get_clocks(struct platform_device *pdev) hdmi.sys_clk = clk; - clk = clk_get(&pdev->dev, "dss_48mhz_clk"); - if (IS_ERR(clk)) { - DSSERR("can't get hdmi_clk\n"); - clk_put(hdmi.sys_clk); - return PTR_ERR(clk); - } - - hdmi.hdmi_clk = clk; - return 0; } @@ -1714,8 +740,6 @@ static void hdmi_put_clocks(void) { if (hdmi.sys_clk) clk_put(hdmi.sys_clk); - if (hdmi.hdmi_clk) - clk_put(hdmi.hdmi_clk); } /* HDMI HW IP initialisation */ @@ -1736,20 +760,26 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev) } /* Base address taken from platform */ - hdmi.base_wp = ioremap(hdmi_mem->start, resource_size(hdmi_mem)); - if (!hdmi.base_wp) { + hdmi.ip_data.base_wp = ioremap(hdmi_mem->start, + resource_size(hdmi_mem)); + if (!hdmi.ip_data.base_wp) { DSSERR("can't ioremap WP\n"); return -ENOMEM; } r = hdmi_get_clocks(pdev); if (r) { - iounmap(hdmi.base_wp); + iounmap(hdmi.ip_data.base_wp); return r; } pm_runtime_enable(&pdev->dev); + hdmi.ip_data.core_sys_offset = HDMI_CORE_SYS; + hdmi.ip_data.core_av_offset = HDMI_CORE_AV; + hdmi.ip_data.pll_offset = HDMI_PLLCTRL; + hdmi.ip_data.phy_offset = HDMI_PHY; + hdmi_panel_init(); #if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ @@ -1779,14 +809,13 @@ static int omapdss_hdmihw_remove(struct platform_device *pdev) hdmi_put_clocks(); - iounmap(hdmi.base_wp); + iounmap(hdmi.ip_data.base_wp); return 0; } static int hdmi_runtime_suspend(struct device *dev) { - clk_disable(hdmi.hdmi_clk); clk_disable(hdmi.sys_clk); dispc_runtime_put(); @@ -1809,7 +838,6 @@ static int hdmi_runtime_resume(struct device *dev) clk_enable(hdmi.sys_clk); - clk_enable(hdmi.hdmi_clk); return 0; diff --git a/drivers/video/omap2/dss/hdmi_omap4_panel.c b/drivers/video/omap2/dss/hdmi_panel.c index 7d4f2bd7c506..533d5dc634d2 100644 --- a/drivers/video/omap2/dss/hdmi_omap4_panel.c +++ b/drivers/video/omap2/dss/hdmi_panel.c @@ -1,5 +1,5 @@ /* - * hdmi_omap4_panel.c + * hdmi_panel.c * * HDMI library support functions for TI OMAP4 processors. * @@ -25,6 +25,7 @@ #include <linux/mutex.h> #include <linux/module.h> #include <video/omapdss.h> +#include <linux/slab.h> #include "dss.h" @@ -40,13 +41,7 @@ static int hdmi_panel_probe(struct omap_dss_device *dssdev) dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS; - /* - * Initialize the timings to 640 * 480 - * This is only for framebuffer update not for TV timing setting - * Setting TV timing will be done only on enable - */ - dssdev->panel.timings.x_res = 640; - dssdev->panel.timings.y_res = 480; + dssdev->panel.timings = (struct omap_video_timings){640, 480, 25175, 96, 16, 48, 2 , 11, 31}; DSSDBG("hdmi_panel_probe x_res= %d y_res = %d\n", dssdev->panel.timings.x_res, @@ -161,12 +156,7 @@ static void hdmi_set_timings(struct omap_dss_device *dssdev, mutex_lock(&hdmi.hdmi_lock); dssdev->panel.timings = *timings; - - if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { - /* turn the hdmi off and on to get new timings to use */ - omapdss_hdmi_display_disable(dssdev); - omapdss_hdmi_display_set_timing(dssdev); - } + omapdss_hdmi_display_set_timing(dssdev); mutex_unlock(&hdmi.hdmi_lock); } @@ -181,12 +171,54 @@ static int hdmi_check_timings(struct omap_dss_device *dssdev, mutex_lock(&hdmi.hdmi_lock); r = omapdss_hdmi_display_check_timing(dssdev, timings); - if (r) { - DSSERR("Timing cannot be applied\n"); - goto err; + + mutex_unlock(&hdmi.hdmi_lock); + return r; +} + +static int hdmi_read_edid(struct omap_dss_device *dssdev, u8 *buf, int len) +{ + int r; + + mutex_lock(&hdmi.hdmi_lock); + + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { + r = omapdss_hdmi_display_enable(dssdev); + if (r) + goto err; + } + + r = omapdss_hdmi_read_edid(buf, len); + + if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED || + dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) + omapdss_hdmi_display_disable(dssdev); +err: + mutex_unlock(&hdmi.hdmi_lock); + + return r; +} + +static bool hdmi_detect(struct omap_dss_device *dssdev) +{ + int r; + + mutex_lock(&hdmi.hdmi_lock); + + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { + r = omapdss_hdmi_display_enable(dssdev); + if (r) + goto err; } + + r = omapdss_hdmi_detect(); + + if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED || + dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) + omapdss_hdmi_display_disable(dssdev); err: mutex_unlock(&hdmi.hdmi_lock); + return r; } @@ -200,6 +232,8 @@ static struct omap_dss_driver hdmi_driver = { .get_timings = hdmi_get_timings, .set_timings = hdmi_set_timings, .check_timings = hdmi_check_timings, + .read_edid = hdmi_read_edid, + .detect = hdmi_detect, .driver = { .name = "hdmi_panel", .owner = THIS_MODULE, diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c index 13d72d5c714b..6e63845cc7d7 100644 --- a/drivers/video/omap2/dss/manager.c +++ b/drivers/video/omap2/dss/manager.c @@ -106,7 +106,7 @@ put_device: static ssize_t manager_default_color_show(struct omap_overlay_manager *mgr, char *buf) { - return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.default_color); + return snprintf(buf, PAGE_SIZE, "%#x\n", mgr->info.default_color); } static ssize_t manager_default_color_store(struct omap_overlay_manager *mgr, @@ -116,8 +116,9 @@ static ssize_t manager_default_color_store(struct omap_overlay_manager *mgr, u32 color; int r; - if (sscanf(buf, "%d", &color) != 1) - return -EINVAL; + r = kstrtouint(buf, 0, &color); + if (r) + return r; mgr->get_manager_info(mgr, &info); @@ -184,7 +185,7 @@ static ssize_t manager_trans_key_type_store(struct omap_overlay_manager *mgr, static ssize_t manager_trans_key_value_show(struct omap_overlay_manager *mgr, char *buf) { - return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.trans_key); + return snprintf(buf, PAGE_SIZE, "%#x\n", mgr->info.trans_key); } static ssize_t manager_trans_key_value_store(struct omap_overlay_manager *mgr, @@ -194,8 +195,9 @@ static ssize_t manager_trans_key_value_store(struct omap_overlay_manager *mgr, u32 key_value; int r; - if (sscanf(buf, "%d", &key_value) != 1) - return -EINVAL; + r = kstrtouint(buf, 0, &key_value); + if (r) + return r; mgr->get_manager_info(mgr, &info); @@ -222,15 +224,16 @@ static ssize_t manager_trans_key_enabled_store(struct omap_overlay_manager *mgr, const char *buf, size_t size) { struct omap_overlay_manager_info info; - int enable; + bool enable; int r; - if (sscanf(buf, "%d", &enable) != 1) - return -EINVAL; + r = strtobool(buf, &enable); + if (r) + return r; mgr->get_manager_info(mgr, &info); - info.trans_enabled = enable ? true : false; + info.trans_enabled = enable; r = mgr->set_manager_info(mgr, &info); if (r) @@ -246,7 +249,10 @@ static ssize_t manager_trans_key_enabled_store(struct omap_overlay_manager *mgr, static ssize_t manager_alpha_blending_enabled_show( struct omap_overlay_manager *mgr, char *buf) { - return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.alpha_enabled); + WARN_ON(!dss_has_feature(FEAT_ALPHA_FIXED_ZORDER)); + + return snprintf(buf, PAGE_SIZE, "%d\n", + mgr->info.partial_alpha_enabled); } static ssize_t manager_alpha_blending_enabled_store( @@ -254,15 +260,18 @@ static ssize_t manager_alpha_blending_enabled_store( const char *buf, size_t size) { struct omap_overlay_manager_info info; - int enable; + bool enable; int r; - if (sscanf(buf, "%d", &enable) != 1) - return -EINVAL; + WARN_ON(!dss_has_feature(FEAT_ALPHA_FIXED_ZORDER)); + + r = strtobool(buf, &enable); + if (r) + return r; mgr->get_manager_info(mgr, &info); - info.alpha_enabled = enable ? true : false; + info.partial_alpha_enabled = enable; r = mgr->set_manager_info(mgr, &info); if (r) @@ -285,19 +294,16 @@ static ssize_t manager_cpr_enable_store(struct omap_overlay_manager *mgr, const char *buf, size_t size) { struct omap_overlay_manager_info info; - int v; int r; bool enable; if (!dss_has_feature(FEAT_CPR)) return -ENODEV; - r = kstrtoint(buf, 0, &v); + r = strtobool(buf, &enable); if (r) return r; - enable = !!v; - mgr->get_manager_info(mgr, &info); if (info.cpr_enable == enable) @@ -586,6 +592,13 @@ static int omap_dss_unset_device(struct omap_overlay_manager *mgr) return -EINVAL; } + /* + * Don't allow currently enabled displays to have the overlay manager + * pulled out from underneath them + */ + if (mgr->device->state != OMAP_DSS_DISPLAY_DISABLED) + return -EINVAL; + mgr->device->manager = NULL; mgr->device = NULL; mgr->device_changed = true; @@ -801,7 +814,7 @@ static int configure_overlay(enum omap_plane plane) { struct overlay_cache_data *c; struct manager_cache_data *mc; - struct omap_overlay_info *oi; + struct omap_overlay_info *oi, new_oi; struct omap_overlay_manager_info *mi; u16 outw, outh; u16 x, y, w, h; @@ -815,7 +828,7 @@ static int configure_overlay(enum omap_plane plane) oi = &c->info; if (!c->enabled) { - dispc_enable_plane(plane, 0); + dispc_ovl_enable(plane, 0); return 0; } @@ -843,7 +856,7 @@ static int configure_overlay(enum omap_plane plane) /* If the overlay is outside the update region, disable it */ if (!rectangle_intersects(mc->x, mc->y, mc->w, mc->h, x, y, outw, outh)) { - dispc_enable_plane(plane, 0); + dispc_ovl_enable(plane, 0); return 0; } @@ -921,34 +934,27 @@ static int configure_overlay(enum omap_plane plane) } } - r = dispc_setup_plane(plane, - paddr, - oi->screen_width, - x, y, - w, h, - outw, outh, - oi->color_mode, - c->ilace, - oi->rotation_type, - oi->rotation, - oi->mirror, - oi->global_alpha, - oi->pre_mult_alpha, - c->channel, - oi->p_uv_addr); + new_oi = *oi; + + /* update new_oi members which could have been possibly updated */ + new_oi.pos_x = x; + new_oi.pos_y = y; + new_oi.width = w; + new_oi.height = h; + new_oi.out_width = outw; + new_oi.out_height = outh; + new_oi.paddr = paddr; + r = dispc_ovl_setup(plane, &new_oi, c->ilace, c->channel, + c->replication, c->fifo_low, c->fifo_high); if (r) { /* this shouldn't happen */ - DSSERR("dispc_setup_plane failed for ovl %d\n", plane); - dispc_enable_plane(plane, 0); + DSSERR("dispc_ovl_setup failed for ovl %d\n", plane); + dispc_ovl_enable(plane, 0); return r; } - dispc_enable_replication(plane, c->replication); - - dispc_set_fifo_threshold(plane, c->fifo_low, c->fifo_high); - - dispc_enable_plane(plane, 1); + dispc_ovl_enable(plane, 1); return 0; } @@ -962,13 +968,13 @@ static void configure_manager(enum omap_channel channel) /* picking info from the cache */ mi = &dss_cache.manager_cache[channel].info; - dispc_set_default_color(channel, mi->default_color); - dispc_set_trans_key(channel, mi->trans_key_type, mi->trans_key); - dispc_enable_trans_key(channel, mi->trans_enabled); - dispc_enable_alpha_blending(channel, mi->alpha_enabled); + dispc_mgr_set_default_color(channel, mi->default_color); + dispc_mgr_set_trans_key(channel, mi->trans_key_type, mi->trans_key); + dispc_mgr_enable_trans_key(channel, mi->trans_enabled); + dispc_mgr_enable_alpha_fixed_zorder(channel, mi->partial_alpha_enabled); if (dss_has_feature(FEAT_CPR)) { - dispc_enable_cpr(channel, mi->cpr_enable); - dispc_set_cpr_coef(channel, &mi->cpr_coefs); + dispc_mgr_enable_cpr(channel, mi->cpr_enable); + dispc_mgr_set_cpr_coef(channel, &mi->cpr_coefs); } } @@ -992,7 +998,7 @@ static int configure_dispc(void) busy = false; for (i = 0; i < num_mgrs; i++) { - mgr_busy[i] = dispc_go_busy(i); + mgr_busy[i] = dispc_mgr_go_busy(i); mgr_go[i] = false; } @@ -1053,7 +1059,7 @@ static int configure_dispc(void) * always be turned off after frame, and new settings will be * taken in to use at next update */ if (!mc->manual_update) - dispc_go(i); + dispc_mgr_go(i); } if (busy) @@ -1258,7 +1264,7 @@ static void dss_apply_irq_handler(void *data, u32 mask) u32 irq_mask; for (i = 0; i < num_mgrs; i++) - mgr_busy[i] = dispc_go_busy(i); + mgr_busy[i] = dispc_mgr_go_busy(i); spin_lock(&dss_cache.lock); @@ -1280,7 +1286,7 @@ static void dss_apply_irq_handler(void *data, u32 mask) /* re-read busy flags */ for (i = 0; i < num_mgrs; i++) - mgr_busy[i] = dispc_go_busy(i); + mgr_busy[i] = dispc_mgr_go_busy(i); /* keep running as long as there are busy managers, so that * we can collect overlay-applied information */ @@ -1326,11 +1332,13 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) ovl = omap_dss_get_overlay(i); - if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) - continue; - oc = &dss_cache.overlay_cache[ovl->id]; + if (ovl->manager_changed) { + ovl->manager_changed = false; + ovl->info_dirty = true; + } + if (!overlay_enabled(ovl)) { if (oc->enabled) { oc->enabled = false; @@ -1375,9 +1383,6 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) list_for_each_entry(mgr, &manager_list, list) { struct omap_dss_device *dssdev; - if (!(mgr->caps & OMAP_DSS_OVL_MGR_CAP_DISPC)) - continue; - mc = &dss_cache.manager_cache[mgr->id]; if (mgr->device_changed) { @@ -1423,9 +1428,6 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) ovl = omap_dss_get_overlay(i); - if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) - continue; - oc = &dss_cache.overlay_cache[ovl->id]; if (!oc->enabled) @@ -1433,11 +1435,11 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) dssdev = ovl->manager->device; - size = dispc_get_plane_fifo_size(ovl->id); + size = dispc_ovl_get_fifo_size(ovl->id); if (use_fifomerge) size *= 3; - burst_size = dispc_get_burst_size(ovl->id); + burst_size = dispc_ovl_get_burst_size(ovl->id); switch (dssdev->type) { case OMAP_DISPLAY_TYPE_DPI: @@ -1484,12 +1486,17 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) static int dss_check_manager(struct omap_overlay_manager *mgr) { - /* OMAP supports only graphics source transparency color key and alpha - * blending simultaneously. See TRM 15.4.2.4.2.2 Alpha Mode */ - - if (mgr->info.alpha_enabled && mgr->info.trans_enabled && - mgr->info.trans_key_type != OMAP_DSS_COLOR_KEY_GFX_DST) - return -EINVAL; + if (dss_has_feature(FEAT_ALPHA_FIXED_ZORDER)) { + /* + * OMAP3 supports only graphics source transparency color key + * and alpha blending simultaneously. See TRM 15.4.2.4.2.2 + * Alpha Mode + */ + if (mgr->info.partial_alpha_enabled && mgr->info.trans_enabled + && mgr->info.trans_key_type != + OMAP_DSS_COLOR_KEY_GFX_DST) + return -EINVAL; + } return 0; } @@ -1522,13 +1529,13 @@ static void omap_dss_mgr_get_info(struct omap_overlay_manager *mgr, static int dss_mgr_enable(struct omap_overlay_manager *mgr) { - dispc_enable_channel(mgr->id, 1); + dispc_mgr_enable(mgr->id, 1); return 0; } static int dss_mgr_disable(struct omap_overlay_manager *mgr) { - dispc_enable_channel(mgr->id, 0); + dispc_mgr_enable(mgr->id, 0); return 0; } @@ -1580,7 +1587,7 @@ int dss_init_overlay_managers(struct platform_device *pdev) mgr->enable = &dss_mgr_enable; mgr->disable = &dss_mgr_disable; - mgr->caps = OMAP_DSS_OVL_MGR_CAP_DISPC; + mgr->caps = 0; mgr->supported_displays = dss_feat_get_supported_displays(mgr->id); @@ -1597,42 +1604,6 @@ int dss_init_overlay_managers(struct platform_device *pdev) } } -#ifdef L4_EXAMPLE - { - int omap_dss_mgr_apply_l4(struct omap_overlay_manager *mgr) - { - DSSDBG("omap_dss_mgr_apply_l4(%s)\n", mgr->name); - - return 0; - } - - struct omap_overlay_manager *mgr; - mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); - - BUG_ON(mgr == NULL); - - mgr->name = "l4"; - mgr->supported_displays = - OMAP_DISPLAY_TYPE_DBI | OMAP_DISPLAY_TYPE_DSI; - - mgr->set_device = &omap_dss_set_device; - mgr->unset_device = &omap_dss_unset_device; - mgr->apply = &omap_dss_mgr_apply_l4; - mgr->set_manager_info = &omap_dss_mgr_set_info; - mgr->get_manager_info = &omap_dss_mgr_get_info; - - dss_overlay_setup_l4_manager(mgr); - - omap_dss_add_overlay_manager(mgr); - - r = kobject_init_and_add(&mgr->kobj, &manager_ktype, - &pdev->dev.kobj, "managerl4"); - - if (r) - DSSERR("failed to create sysfs file\n"); - } -#endif - return 0; } diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c index c84380c53c39..ab8e40e48759 100644 --- a/drivers/video/omap2/dss/overlay.c +++ b/drivers/video/omap2/dss/overlay.c @@ -211,16 +211,17 @@ static ssize_t overlay_enabled_show(struct omap_overlay *ovl, char *buf) static ssize_t overlay_enabled_store(struct omap_overlay *ovl, const char *buf, size_t size) { - int r, enable; + int r; + bool enable; struct omap_overlay_info info; ovl->get_overlay_info(ovl, &info); - r = kstrtoint(buf, 0, &enable); + r = strtobool(buf, &enable); if (r) return r; - info.enabled = !!enable; + info.enabled = enable; r = ovl->set_overlay_info(ovl, &info); if (r) @@ -248,7 +249,7 @@ static ssize_t overlay_global_alpha_store(struct omap_overlay *ovl, u8 alpha; struct omap_overlay_info info; - if (!dss_has_feature(FEAT_GLOBAL_ALPHA)) + if ((ovl->caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) == 0) return -ENODEV; r = kstrtou8(buf, 0, &alpha); @@ -257,14 +258,7 @@ static ssize_t overlay_global_alpha_store(struct omap_overlay *ovl, ovl->get_overlay_info(ovl, &info); - /* Video1 plane does not support global alpha - * to always make it 255 completely opaque - */ - if (!dss_has_feature(FEAT_GLOBAL_ALPHA_VID1) && - ovl->id == OMAP_DSS_VIDEO1) - info.global_alpha = 255; - else - info.global_alpha = alpha; + info.global_alpha = alpha; r = ovl->set_overlay_info(ovl, &info); if (r) @@ -293,20 +287,52 @@ static ssize_t overlay_pre_mult_alpha_store(struct omap_overlay *ovl, u8 alpha; struct omap_overlay_info info; + if ((ovl->caps & OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA) == 0) + return -ENODEV; + r = kstrtou8(buf, 0, &alpha); if (r) return r; ovl->get_overlay_info(ovl, &info); - /* only GFX and Video2 plane support pre alpha multiplied - * set zero for Video1 plane - */ - if (!dss_has_feature(FEAT_GLOBAL_ALPHA_VID1) && - ovl->id == OMAP_DSS_VIDEO1) - info.pre_mult_alpha = 0; - else - info.pre_mult_alpha = alpha; + info.pre_mult_alpha = alpha; + + r = ovl->set_overlay_info(ovl, &info); + if (r) + return r; + + if (ovl->manager) { + r = ovl->manager->apply(ovl->manager); + if (r) + return r; + } + + return size; +} + +static ssize_t overlay_zorder_show(struct omap_overlay *ovl, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", ovl->info.zorder); +} + +static ssize_t overlay_zorder_store(struct omap_overlay *ovl, + const char *buf, size_t size) +{ + int r; + u8 zorder; + struct omap_overlay_info info; + + if ((ovl->caps & OMAP_DSS_OVL_CAP_ZORDER) == 0) + return -ENODEV; + + r = kstrtou8(buf, 0, &zorder); + if (r) + return r; + + ovl->get_overlay_info(ovl, &info); + + info.zorder = zorder; r = ovl->set_overlay_info(ovl, &info); if (r) @@ -347,6 +373,8 @@ static OVERLAY_ATTR(global_alpha, S_IRUGO|S_IWUSR, static OVERLAY_ATTR(pre_mult_alpha, S_IRUGO|S_IWUSR, overlay_pre_mult_alpha_show, overlay_pre_mult_alpha_store); +static OVERLAY_ATTR(zorder, S_IRUGO|S_IWUSR, + overlay_zorder_show, overlay_zorder_store); static struct attribute *overlay_sysfs_attrs[] = { &overlay_attr_name.attr, @@ -358,6 +386,7 @@ static struct attribute *overlay_sysfs_attrs[] = { &overlay_attr_enabled.attr, &overlay_attr_global_alpha.attr, &overlay_attr_pre_mult_alpha.attr, + &overlay_attr_zorder.attr, NULL }; @@ -407,6 +436,7 @@ int dss_check_overlay(struct omap_overlay *ovl, struct omap_dss_device *dssdev) struct omap_overlay_info *info; u16 outw, outh; u16 dw, dh; + int i; if (!dssdev) return 0; @@ -462,6 +492,31 @@ int dss_check_overlay(struct omap_overlay *ovl, struct omap_dss_device *dssdev) return -EINVAL; } + if (ovl->caps & OMAP_DSS_OVL_CAP_ZORDER) { + if (info->zorder < 0 || info->zorder > 3) { + DSSERR("zorder out of range: %d\n", + info->zorder); + return -EINVAL; + } + /* + * Check that zorder doesn't match with zorder of any other + * overlay which is enabled and is also connected to the same + * manager + */ + for (i = 0; i < omap_dss_get_num_overlays(); i++) { + struct omap_overlay *tmp_ovl = omap_dss_get_overlay(i); + + if (tmp_ovl->id != ovl->id && + tmp_ovl->manager == ovl->manager && + tmp_ovl->info.enabled == true && + tmp_ovl->info.zorder == info->zorder) { + DSSERR("%s and %s have same zorder: %d\n", + ovl->name, tmp_ovl->name, info->zorder); + return -EINVAL; + } + } + } + return 0; } @@ -516,6 +571,7 @@ static int omap_dss_set_manager(struct omap_overlay *ovl, } ovl->manager = mgr; + ovl->manager_changed = true; /* XXX: When there is an overlay on a DSI manual update display, and * the overlay is first disabled, then moved to tv, and enabled, we @@ -529,15 +585,12 @@ static int omap_dss_set_manager(struct omap_overlay *ovl, * Userspace workaround for this is to update the LCD after disabling * the overlay, but before moving the overlay to TV. */ - dispc_set_channel_out(ovl->id, mgr->id); return 0; } static int omap_dss_unset_manager(struct omap_overlay *ovl) { - int r; - if (!ovl->manager) { DSSERR("failed to detach overlay: manager not set\n"); return -EINVAL; @@ -548,11 +601,8 @@ static int omap_dss_unset_manager(struct omap_overlay *ovl) return -EINVAL; } - r = ovl->wait_for_go(ovl); - if (r) - return r; - ovl->manager = NULL; + ovl->manager_changed = true; return 0; } @@ -618,22 +668,29 @@ void dss_init_overlays(struct platform_device *pdev) case 0: ovl->name = "gfx"; ovl->id = OMAP_DSS_GFX; - ovl->caps = OMAP_DSS_OVL_CAP_DISPC; ovl->info.global_alpha = 255; + ovl->info.zorder = 0; break; case 1: ovl->name = "vid1"; ovl->id = OMAP_DSS_VIDEO1; - ovl->caps = OMAP_DSS_OVL_CAP_SCALE | - OMAP_DSS_OVL_CAP_DISPC; ovl->info.global_alpha = 255; + ovl->info.zorder = + dss_has_feature(FEAT_ALPHA_FREE_ZORDER) ? 3 : 0; break; case 2: ovl->name = "vid2"; ovl->id = OMAP_DSS_VIDEO2; - ovl->caps = OMAP_DSS_OVL_CAP_SCALE | - OMAP_DSS_OVL_CAP_DISPC; ovl->info.global_alpha = 255; + ovl->info.zorder = + dss_has_feature(FEAT_ALPHA_FREE_ZORDER) ? 2 : 0; + break; + case 3: + ovl->name = "vid3"; + ovl->id = OMAP_DSS_VIDEO3; + ovl->info.global_alpha = 255; + ovl->info.zorder = + dss_has_feature(FEAT_ALPHA_FREE_ZORDER) ? 1 : 0; break; } @@ -643,6 +700,7 @@ void dss_init_overlays(struct platform_device *pdev) ovl->get_overlay_info = &dss_ovl_get_overlay_info; ovl->wait_for_go = &dss_ovl_wait_for_go; + ovl->caps = dss_feat_get_overlay_caps(ovl->id); ovl->supported_modes = dss_feat_get_supported_color_modes(ovl->id); diff --git a/drivers/video/omap2/dss/rfbi.c b/drivers/video/omap2/dss/rfbi.c index 39f4c597026a..1bd3703e42ff 100644 --- a/drivers/video/omap2/dss/rfbi.c +++ b/drivers/video/omap2/dss/rfbi.c @@ -309,9 +309,9 @@ static void rfbi_transfer_area(struct omap_dss_device *dssdev, u16 width, DSSDBG("rfbi_transfer_area %dx%d\n", width, height); - dispc_set_lcd_size(dssdev->manager->id, width, height); + dispc_mgr_set_lcd_size(dssdev->manager->id, width, height); - dispc_enable_channel(dssdev->manager->id, true); + dispc_mgr_enable(dssdev->manager->id, true); rfbi.framedone_callback = callback; rfbi.framedone_callback_data = data; @@ -783,10 +783,8 @@ int omap_rfbi_prepare_update(struct omap_dss_device *dssdev, if (*w == 0 || *h == 0) return -EINVAL; - if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) { - dss_setup_partial_planes(dssdev, x, y, w, h, true); - dispc_set_lcd_size(dssdev->manager->id, *w, *h); - } + dss_setup_partial_planes(dssdev, x, y, w, h, true); + dispc_mgr_set_lcd_size(dssdev->manager->id, *w, *h); return 0; } @@ -796,22 +794,7 @@ int omap_rfbi_update(struct omap_dss_device *dssdev, u16 x, u16 y, u16 w, u16 h, void (*callback)(void *), void *data) { - if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) { - rfbi_transfer_area(dssdev, w, h, callback, data); - } else { - struct omap_overlay *ovl; - void __iomem *addr; - int scr_width; - - ovl = dssdev->manager->overlays[0]; - scr_width = ovl->info.screen_width; - addr = ovl->info.vaddr; - - omap_rfbi_write_pixels(addr, scr_width, x, y, w, h); - - callback(data); - } - + rfbi_transfer_area(dssdev, w, h, callback, data); return 0; } EXPORT_SYMBOL(omap_rfbi_update); @@ -860,6 +843,11 @@ int omapdss_rfbi_display_enable(struct omap_dss_device *dssdev) { int r; + if (dssdev->manager == NULL) { + DSSERR("failed to enable display: no manager\n"); + return -ENODEV; + } + r = rfbi_runtime_get(); if (r) return r; @@ -877,13 +865,13 @@ int omapdss_rfbi_display_enable(struct omap_dss_device *dssdev) goto err1; } - dispc_set_lcd_display_type(dssdev->manager->id, + dispc_mgr_set_lcd_display_type(dssdev->manager->id, OMAP_DSS_LCD_DISPLAY_TFT); - dispc_set_parallel_interface_mode(dssdev->manager->id, - OMAP_DSS_PARALLELMODE_RFBI); + dispc_mgr_set_io_pad_mode(DSS_IO_PAD_MODE_RFBI); + dispc_mgr_enable_stallmode(dssdev->manager->id, true); - dispc_set_tft_data_lines(dssdev->manager->id, dssdev->ctrl.pixel_size); + dispc_mgr_set_tft_data_lines(dssdev->manager->id, dssdev->ctrl.pixel_size); rfbi_configure(dssdev->phy.rfbi.channel, dssdev->ctrl.pixel_size, @@ -952,10 +940,7 @@ static int omap_rfbihw_probe(struct platform_device *pdev) msleep(10); - if (cpu_is_omap24xx() || cpu_is_omap34xx() || cpu_is_omap3630()) - clk = dss_get_ick(); - else - clk = clk_get(&pdev->dev, "ick"); + clk = clk_get(&pdev->dev, "ick"); if (IS_ERR(clk)) { DSSERR("can't get ick\n"); r = PTR_ERR(clk); diff --git a/drivers/video/omap2/dss/sdi.c b/drivers/video/omap2/dss/sdi.c index 3a688c871a45..695dc04cabba 100644 --- a/drivers/video/omap2/dss/sdi.c +++ b/drivers/video/omap2/dss/sdi.c @@ -35,13 +35,13 @@ static struct { static void sdi_basic_init(struct omap_dss_device *dssdev) { - dispc_set_parallel_interface_mode(dssdev->manager->id, - OMAP_DSS_PARALLELMODE_BYPASS); + dispc_mgr_set_io_pad_mode(DSS_IO_PAD_MODE_BYPASS); + dispc_mgr_enable_stallmode(dssdev->manager->id, false); - dispc_set_lcd_display_type(dssdev->manager->id, + dispc_mgr_set_lcd_display_type(dssdev->manager->id, OMAP_DSS_LCD_DISPLAY_TFT); - dispc_set_tft_data_lines(dssdev->manager->id, 24); + dispc_mgr_set_tft_data_lines(dssdev->manager->id, 24); dispc_lcd_enable_signal_polarity(1); } @@ -55,6 +55,11 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev) unsigned long pck; int r; + if (dssdev->manager == NULL) { + DSSERR("failed to enable display: no manager\n"); + return -ENODEV; + } + r = omap_dss_start_device(dssdev); if (r) { DSSERR("failed to start device\n"); @@ -78,7 +83,7 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev) /* 15.5.9.1.2 */ dssdev->panel.config |= OMAP_DSS_LCD_RF | OMAP_DSS_LCD_ONOFF; - dispc_set_pol_freq(dssdev->manager->id, dssdev->panel.config, + dispc_mgr_set_pol_freq(dssdev->manager->id, dssdev->panel.config, dssdev->panel.acbi, dssdev->panel.acb); r = dss_calc_clock_div(1, t->pixel_clock * 1000, @@ -101,13 +106,13 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev) } - dispc_set_lcd_timings(dssdev->manager->id, t); + dispc_mgr_set_lcd_timings(dssdev->manager->id, t); r = dss_set_clock_div(&dss_cinfo); if (r) goto err_set_dss_clock_div; - r = dispc_set_clock_div(dssdev->manager->id, &dispc_cinfo); + r = dispc_mgr_set_clock_div(dssdev->manager->id, &dispc_cinfo); if (r) goto err_set_dispc_clock_div; diff --git a/drivers/video/omap2/dss/ti_hdmi.h b/drivers/video/omap2/dss/ti_hdmi.h new file mode 100644 index 000000000000..2c3443dabb14 --- /dev/null +++ b/drivers/video/omap2/dss/ti_hdmi.h @@ -0,0 +1,138 @@ +/* + * ti_hdmi.h + * + * HDMI driver definition for TI OMAP4, DM81xx, DM38xx Processor. + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _TI_HDMI_H +#define _TI_HDMI_H + +struct hdmi_ip_data; + +enum hdmi_pll_pwr { + HDMI_PLLPWRCMD_ALLOFF = 0, + HDMI_PLLPWRCMD_PLLONLY = 1, + HDMI_PLLPWRCMD_BOTHON_ALLCLKS = 2, + HDMI_PLLPWRCMD_BOTHON_NOPHYCLK = 3 +}; + +enum hdmi_core_hdmi_dvi { + HDMI_DVI = 0, + HDMI_HDMI = 1 +}; + +enum hdmi_clk_refsel { + HDMI_REFSEL_PCLK = 0, + HDMI_REFSEL_REF1 = 1, + HDMI_REFSEL_REF2 = 2, + HDMI_REFSEL_SYSCLK = 3 +}; + +struct hdmi_video_timings { + u16 x_res; + u16 y_res; + /* Unit: KHz */ + u32 pixel_clock; + u16 hsw; + u16 hfp; + u16 hbp; + u16 vsw; + u16 vfp; + u16 vbp; +}; + +/* HDMI timing structure */ +struct hdmi_timings { + struct hdmi_video_timings timings; + int vsync_pol; + int hsync_pol; +}; + +struct hdmi_cm { + int code; + int mode; +}; + +struct hdmi_config { + struct hdmi_timings timings; + u16 interlace; + struct hdmi_cm cm; +}; + +/* HDMI PLL structure */ +struct hdmi_pll_info { + u16 regn; + u16 regm; + u32 regmf; + u16 regm2; + u16 regsd; + u16 dcofreq; + enum hdmi_clk_refsel refsel; +}; + +struct ti_hdmi_ip_ops { + + void (*video_configure)(struct hdmi_ip_data *ip_data); + + int (*phy_enable)(struct hdmi_ip_data *ip_data); + + void (*phy_disable)(struct hdmi_ip_data *ip_data); + + int (*read_edid)(struct hdmi_ip_data *ip_data, u8 *edid, int len); + + bool (*detect)(struct hdmi_ip_data *ip_data); + + int (*pll_enable)(struct hdmi_ip_data *ip_data); + + void (*pll_disable)(struct hdmi_ip_data *ip_data); + + void (*video_enable)(struct hdmi_ip_data *ip_data, bool start); + + void (*dump_wrapper)(struct hdmi_ip_data *ip_data, struct seq_file *s); + + void (*dump_core)(struct hdmi_ip_data *ip_data, struct seq_file *s); + + void (*dump_pll)(struct hdmi_ip_data *ip_data, struct seq_file *s); + + void (*dump_phy)(struct hdmi_ip_data *ip_data, struct seq_file *s); + +}; + +struct hdmi_ip_data { + void __iomem *base_wp; /* HDMI wrapper */ + unsigned long core_sys_offset; + unsigned long core_av_offset; + unsigned long pll_offset; + unsigned long phy_offset; + const struct ti_hdmi_ip_ops *ops; + struct hdmi_config cfg; + struct hdmi_pll_info pll_data; +}; +int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data); +void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data); +int ti_hdmi_4xxx_read_edid(struct hdmi_ip_data *ip_data, u8 *edid, int len); +bool ti_hdmi_4xxx_detect(struct hdmi_ip_data *ip_data); +void ti_hdmi_4xxx_wp_video_start(struct hdmi_ip_data *ip_data, bool start); +int ti_hdmi_4xxx_pll_enable(struct hdmi_ip_data *ip_data); +void ti_hdmi_4xxx_pll_disable(struct hdmi_ip_data *ip_data); +void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data); +void ti_hdmi_4xxx_wp_dump(struct hdmi_ip_data *ip_data, struct seq_file *s); +void ti_hdmi_4xxx_pll_dump(struct hdmi_ip_data *ip_data, struct seq_file *s); +void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s); +void ti_hdmi_4xxx_phy_dump(struct hdmi_ip_data *ip_data, struct seq_file *s); + +#endif diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c new file mode 100644 index 000000000000..e1a6ce518af6 --- /dev/null +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c @@ -0,0 +1,1239 @@ +/* + * ti_hdmi_4xxx_ip.c + * + * HDMI TI81xx, TI38xx, TI OMAP4 etc IP driver Library + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/ + * Authors: Yong Zhi + * Mythri pk <mythripk@ti.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/seq_file.h> + +#include "ti_hdmi_4xxx_ip.h" +#include "dss.h" + +static inline void hdmi_write_reg(void __iomem *base_addr, + const u16 idx, u32 val) +{ + __raw_writel(val, base_addr + idx); +} + +static inline u32 hdmi_read_reg(void __iomem *base_addr, + const u16 idx) +{ + return __raw_readl(base_addr + idx); +} + +static inline void __iomem *hdmi_wp_base(struct hdmi_ip_data *ip_data) +{ + return ip_data->base_wp; +} + +static inline void __iomem *hdmi_phy_base(struct hdmi_ip_data *ip_data) +{ + return ip_data->base_wp + ip_data->phy_offset; +} + +static inline void __iomem *hdmi_pll_base(struct hdmi_ip_data *ip_data) +{ + return ip_data->base_wp + ip_data->pll_offset; +} + +static inline void __iomem *hdmi_av_base(struct hdmi_ip_data *ip_data) +{ + return ip_data->base_wp + ip_data->core_av_offset; +} + +static inline void __iomem *hdmi_core_sys_base(struct hdmi_ip_data *ip_data) +{ + return ip_data->base_wp + ip_data->core_sys_offset; +} + +static inline int hdmi_wait_for_bit_change(void __iomem *base_addr, + const u16 idx, + int b2, int b1, u32 val) +{ + u32 t = 0; + while (val != REG_GET(base_addr, idx, b2, b1)) { + udelay(1); + if (t++ > 10000) + return !val; + } + return val; +} + +static int hdmi_pll_init(struct hdmi_ip_data *ip_data) +{ + u32 r; + void __iomem *pll_base = hdmi_pll_base(ip_data); + struct hdmi_pll_info *fmt = &ip_data->pll_data; + + /* PLL start always use manual mode */ + REG_FLD_MOD(pll_base, PLLCTRL_PLL_CONTROL, 0x0, 0, 0); + + r = hdmi_read_reg(pll_base, PLLCTRL_CFG1); + r = FLD_MOD(r, fmt->regm, 20, 9); /* CFG1_PLL_REGM */ + r = FLD_MOD(r, fmt->regn - 1, 8, 1); /* CFG1_PLL_REGN */ + + hdmi_write_reg(pll_base, PLLCTRL_CFG1, r); + + r = hdmi_read_reg(pll_base, PLLCTRL_CFG2); + + r = FLD_MOD(r, 0x0, 12, 12); /* PLL_HIGHFREQ divide by 2 */ + r = FLD_MOD(r, 0x1, 13, 13); /* PLL_REFEN */ + r = FLD_MOD(r, 0x0, 14, 14); /* PHY_CLKINEN de-assert during locking */ + r = FLD_MOD(r, fmt->refsel, 22, 21); /* REFSEL */ + + if (fmt->dcofreq) { + /* divider programming for frequency beyond 1000Mhz */ + REG_FLD_MOD(pll_base, PLLCTRL_CFG3, fmt->regsd, 17, 10); + r = FLD_MOD(r, 0x4, 3, 1); /* 1000MHz and 2000MHz */ + } else { + r = FLD_MOD(r, 0x2, 3, 1); /* 500MHz and 1000MHz */ + } + + hdmi_write_reg(pll_base, PLLCTRL_CFG2, r); + + r = hdmi_read_reg(pll_base, PLLCTRL_CFG4); + r = FLD_MOD(r, fmt->regm2, 24, 18); + r = FLD_MOD(r, fmt->regmf, 17, 0); + + hdmi_write_reg(pll_base, PLLCTRL_CFG4, r); + + /* go now */ + REG_FLD_MOD(pll_base, PLLCTRL_PLL_GO, 0x1, 0, 0); + + /* wait for bit change */ + if (hdmi_wait_for_bit_change(pll_base, PLLCTRL_PLL_GO, + 0, 0, 1) != 1) { + pr_err("PLL GO bit not set\n"); + return -ETIMEDOUT; + } + + /* Wait till the lock bit is set in PLL status */ + if (hdmi_wait_for_bit_change(pll_base, + PLLCTRL_PLL_STATUS, 1, 1, 1) != 1) { + pr_err("cannot lock PLL\n"); + pr_err("CFG1 0x%x\n", + hdmi_read_reg(pll_base, PLLCTRL_CFG1)); + pr_err("CFG2 0x%x\n", + hdmi_read_reg(pll_base, PLLCTRL_CFG2)); + pr_err("CFG4 0x%x\n", + hdmi_read_reg(pll_base, PLLCTRL_CFG4)); + return -ETIMEDOUT; + } + + pr_debug("PLL locked!\n"); + + return 0; +} + +/* PHY_PWR_CMD */ +static int hdmi_set_phy_pwr(struct hdmi_ip_data *ip_data, enum hdmi_phy_pwr val) +{ + /* Command for power control of HDMI PHY */ + REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_PWR_CTRL, val, 7, 6); + + /* Status of the power control of HDMI PHY */ + if (hdmi_wait_for_bit_change(hdmi_wp_base(ip_data), + HDMI_WP_PWR_CTRL, 5, 4, val) != val) { + pr_err("Failed to set PHY power mode to %d\n", val); + return -ETIMEDOUT; + } + + return 0; +} + +/* PLL_PWR_CMD */ +static int hdmi_set_pll_pwr(struct hdmi_ip_data *ip_data, enum hdmi_pll_pwr val) +{ + /* Command for power control of HDMI PLL */ + REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_PWR_CTRL, val, 3, 2); + + /* wait till PHY_PWR_STATUS is set */ + if (hdmi_wait_for_bit_change(hdmi_wp_base(ip_data), HDMI_WP_PWR_CTRL, + 1, 0, val) != val) { + pr_err("Failed to set PLL_PWR_STATUS\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int hdmi_pll_reset(struct hdmi_ip_data *ip_data) +{ + /* SYSRESET controlled by power FSM */ + REG_FLD_MOD(hdmi_pll_base(ip_data), PLLCTRL_PLL_CONTROL, 0x0, 3, 3); + + /* READ 0x0 reset is in progress */ + if (hdmi_wait_for_bit_change(hdmi_pll_base(ip_data), + PLLCTRL_PLL_STATUS, 0, 0, 1) != 1) { + pr_err("Failed to sysreset PLL\n"); + return -ETIMEDOUT; + } + + return 0; +} + +int ti_hdmi_4xxx_pll_enable(struct hdmi_ip_data *ip_data) +{ + u16 r = 0; + + r = hdmi_set_pll_pwr(ip_data, HDMI_PLLPWRCMD_ALLOFF); + if (r) + return r; + + r = hdmi_set_pll_pwr(ip_data, HDMI_PLLPWRCMD_BOTHON_ALLCLKS); + if (r) + return r; + + r = hdmi_pll_reset(ip_data); + if (r) + return r; + + r = hdmi_pll_init(ip_data); + if (r) + return r; + + return 0; +} + +void ti_hdmi_4xxx_pll_disable(struct hdmi_ip_data *ip_data) +{ + hdmi_set_pll_pwr(ip_data, HDMI_PLLPWRCMD_ALLOFF); +} + +int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data) +{ + u16 r = 0; + void __iomem *phy_base = hdmi_phy_base(ip_data); + + r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_LDOON); + if (r) + return r; + + r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_TXON); + if (r) + return r; + + /* + * Read address 0 in order to get the SCP reset done completed + * Dummy access performed to make sure reset is done + */ + hdmi_read_reg(phy_base, HDMI_TXPHY_TX_CTRL); + + /* + * Write to phy address 0 to configure the clock + * use HFBITCLK write HDMI_TXPHY_TX_CONTROL_FREQOUT field + */ + REG_FLD_MOD(phy_base, HDMI_TXPHY_TX_CTRL, 0x1, 31, 30); + + /* Write to phy address 1 to start HDMI line (TXVALID and TMDSCLKEN) */ + hdmi_write_reg(phy_base, HDMI_TXPHY_DIGITAL_CTRL, 0xF0000000); + + /* Setup max LDO voltage */ + REG_FLD_MOD(phy_base, HDMI_TXPHY_POWER_CTRL, 0xB, 3, 0); + + /* Write to phy address 3 to change the polarity control */ + REG_FLD_MOD(phy_base, HDMI_TXPHY_PAD_CFG_CTRL, 0x1, 27, 27); + + return 0; +} + +void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data) +{ + hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF); +} + +static int hdmi_core_ddc_init(struct hdmi_ip_data *ip_data) +{ + void __iomem *base = hdmi_core_sys_base(ip_data); + + /* Turn on CLK for DDC */ + REG_FLD_MOD(base, HDMI_CORE_AV_DPD, 0x7, 2, 0); + + /* IN_PROG */ + if (REG_GET(base, HDMI_CORE_DDC_STATUS, 4, 4) == 1) { + /* Abort transaction */ + REG_FLD_MOD(base, HDMI_CORE_DDC_CMD, 0xf, 3, 0); + /* IN_PROG */ + if (hdmi_wait_for_bit_change(base, HDMI_CORE_DDC_STATUS, + 4, 4, 0) != 0) { + DSSERR("Timeout aborting DDC transaction\n"); + return -ETIMEDOUT; + } + } + + /* Clk SCL Devices */ + REG_FLD_MOD(base, HDMI_CORE_DDC_CMD, 0xA, 3, 0); + + /* HDMI_CORE_DDC_STATUS_IN_PROG */ + if (hdmi_wait_for_bit_change(base, HDMI_CORE_DDC_STATUS, + 4, 4, 0) != 0) { + DSSERR("Timeout starting SCL clock\n"); + return -ETIMEDOUT; + } + + /* Clear FIFO */ + REG_FLD_MOD(base, HDMI_CORE_DDC_CMD, 0x9, 3, 0); + + /* HDMI_CORE_DDC_STATUS_IN_PROG */ + if (hdmi_wait_for_bit_change(base, HDMI_CORE_DDC_STATUS, + 4, 4, 0) != 0) { + DSSERR("Timeout clearing DDC fifo\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int hdmi_core_ddc_edid(struct hdmi_ip_data *ip_data, + u8 *pedid, int ext) +{ + void __iomem *base = hdmi_core_sys_base(ip_data); + u32 i; + char checksum; + u32 offset = 0; + + /* HDMI_CORE_DDC_STATUS_IN_PROG */ + if (hdmi_wait_for_bit_change(base, HDMI_CORE_DDC_STATUS, + 4, 4, 0) != 0) { + DSSERR("Timeout waiting DDC to be ready\n"); + return -ETIMEDOUT; + } + + if (ext % 2 != 0) + offset = 0x80; + + /* Load Segment Address Register */ + REG_FLD_MOD(base, HDMI_CORE_DDC_SEGM, ext / 2, 7, 0); + + /* Load Slave Address Register */ + REG_FLD_MOD(base, HDMI_CORE_DDC_ADDR, 0xA0 >> 1, 7, 1); + + /* Load Offset Address Register */ + REG_FLD_MOD(base, HDMI_CORE_DDC_OFFSET, offset, 7, 0); + + /* Load Byte Count */ + REG_FLD_MOD(base, HDMI_CORE_DDC_COUNT1, 0x80, 7, 0); + REG_FLD_MOD(base, HDMI_CORE_DDC_COUNT2, 0x0, 1, 0); + + /* Set DDC_CMD */ + if (ext) + REG_FLD_MOD(base, HDMI_CORE_DDC_CMD, 0x4, 3, 0); + else + REG_FLD_MOD(base, HDMI_CORE_DDC_CMD, 0x2, 3, 0); + + /* HDMI_CORE_DDC_STATUS_BUS_LOW */ + if (REG_GET(base, HDMI_CORE_DDC_STATUS, 6, 6) == 1) { + pr_err("I2C Bus Low?\n"); + return -EIO; + } + /* HDMI_CORE_DDC_STATUS_NO_ACK */ + if (REG_GET(base, HDMI_CORE_DDC_STATUS, 5, 5) == 1) { + pr_err("I2C No Ack\n"); + return -EIO; + } + + for (i = 0; i < 0x80; ++i) { + int t; + + /* IN_PROG */ + if (REG_GET(base, HDMI_CORE_DDC_STATUS, 4, 4) == 0) { + DSSERR("operation stopped when reading edid\n"); + return -EIO; + } + + t = 0; + /* FIFO_EMPTY */ + while (REG_GET(base, HDMI_CORE_DDC_STATUS, 2, 2) == 1) { + if (t++ > 10000) { + DSSERR("timeout reading edid\n"); + return -ETIMEDOUT; + } + udelay(1); + } + + pedid[i] = REG_GET(base, HDMI_CORE_DDC_DATA, 7, 0); + } + + checksum = 0; + for (i = 0; i < 0x80; ++i) + checksum += pedid[i]; + + if (checksum != 0) { + pr_err("E-EDID checksum failed!!\n"); + return -EIO; + } + + return 0; +} + +int ti_hdmi_4xxx_read_edid(struct hdmi_ip_data *ip_data, + u8 *edid, int len) +{ + int r, l; + + if (len < 128) + return -EINVAL; + + r = hdmi_core_ddc_init(ip_data); + if (r) + return r; + + r = hdmi_core_ddc_edid(ip_data, edid, 0); + if (r) + return r; + + l = 128; + + if (len >= 128 * 2 && edid[0x7e] > 0) { + r = hdmi_core_ddc_edid(ip_data, edid + 0x80, 1); + if (r) + return r; + l += 128; + } + + return l; +} + +bool ti_hdmi_4xxx_detect(struct hdmi_ip_data *ip_data) +{ + int r; + + void __iomem *base = hdmi_core_sys_base(ip_data); + + /* HPD */ + r = REG_GET(base, HDMI_CORE_SYS_SYS_STAT, 1, 1); + + return r == 1; +} + +static void hdmi_core_init(struct hdmi_core_video_config *video_cfg, + struct hdmi_core_infoframe_avi *avi_cfg, + struct hdmi_core_packet_enable_repeat *repeat_cfg) +{ + pr_debug("Enter hdmi_core_init\n"); + + /* video core */ + video_cfg->ip_bus_width = HDMI_INPUT_8BIT; + video_cfg->op_dither_truc = HDMI_OUTPUTTRUNCATION_8BIT; + video_cfg->deep_color_pkt = HDMI_DEEPCOLORPACKECTDISABLE; + video_cfg->pkt_mode = HDMI_PACKETMODERESERVEDVALUE; + video_cfg->hdmi_dvi = HDMI_DVI; + video_cfg->tclk_sel_clkmult = HDMI_FPLL10IDCK; + + /* info frame */ + avi_cfg->db1_format = 0; + avi_cfg->db1_active_info = 0; + avi_cfg->db1_bar_info_dv = 0; + avi_cfg->db1_scan_info = 0; + avi_cfg->db2_colorimetry = 0; + avi_cfg->db2_aspect_ratio = 0; + avi_cfg->db2_active_fmt_ar = 0; + avi_cfg->db3_itc = 0; + avi_cfg->db3_ec = 0; + avi_cfg->db3_q_range = 0; + avi_cfg->db3_nup_scaling = 0; + avi_cfg->db4_videocode = 0; + avi_cfg->db5_pixel_repeat = 0; + avi_cfg->db6_7_line_eoftop = 0 ; + avi_cfg->db8_9_line_sofbottom = 0; + avi_cfg->db10_11_pixel_eofleft = 0; + avi_cfg->db12_13_pixel_sofright = 0; + + /* packet enable and repeat */ + repeat_cfg->audio_pkt = 0; + repeat_cfg->audio_pkt_repeat = 0; + repeat_cfg->avi_infoframe = 0; + repeat_cfg->avi_infoframe_repeat = 0; + repeat_cfg->gen_cntrl_pkt = 0; + repeat_cfg->gen_cntrl_pkt_repeat = 0; + repeat_cfg->generic_pkt = 0; + repeat_cfg->generic_pkt_repeat = 0; +} + +static void hdmi_core_powerdown_disable(struct hdmi_ip_data *ip_data) +{ + pr_debug("Enter hdmi_core_powerdown_disable\n"); + REG_FLD_MOD(hdmi_core_sys_base(ip_data), HDMI_CORE_CTRL1, 0x0, 0, 0); +} + +static void hdmi_core_swreset_release(struct hdmi_ip_data *ip_data) +{ + pr_debug("Enter hdmi_core_swreset_release\n"); + REG_FLD_MOD(hdmi_core_sys_base(ip_data), HDMI_CORE_SYS_SRST, 0x0, 0, 0); +} + +static void hdmi_core_swreset_assert(struct hdmi_ip_data *ip_data) +{ + pr_debug("Enter hdmi_core_swreset_assert\n"); + REG_FLD_MOD(hdmi_core_sys_base(ip_data), HDMI_CORE_SYS_SRST, 0x1, 0, 0); +} + +/* HDMI_CORE_VIDEO_CONFIG */ +static void hdmi_core_video_config(struct hdmi_ip_data *ip_data, + struct hdmi_core_video_config *cfg) +{ + u32 r = 0; + void __iomem *core_sys_base = hdmi_core_sys_base(ip_data); + + /* sys_ctrl1 default configuration not tunable */ + r = hdmi_read_reg(core_sys_base, HDMI_CORE_CTRL1); + r = FLD_MOD(r, HDMI_CORE_CTRL1_VEN_FOLLOWVSYNC, 5, 5); + r = FLD_MOD(r, HDMI_CORE_CTRL1_HEN_FOLLOWHSYNC, 4, 4); + r = FLD_MOD(r, HDMI_CORE_CTRL1_BSEL_24BITBUS, 2, 2); + r = FLD_MOD(r, HDMI_CORE_CTRL1_EDGE_RISINGEDGE, 1, 1); + hdmi_write_reg(core_sys_base, HDMI_CORE_CTRL1, r); + + REG_FLD_MOD(core_sys_base, + HDMI_CORE_SYS_VID_ACEN, cfg->ip_bus_width, 7, 6); + + /* Vid_Mode */ + r = hdmi_read_reg(core_sys_base, HDMI_CORE_SYS_VID_MODE); + + /* dither truncation configuration */ + if (cfg->op_dither_truc > HDMI_OUTPUTTRUNCATION_12BIT) { + r = FLD_MOD(r, cfg->op_dither_truc - 3, 7, 6); + r = FLD_MOD(r, 1, 5, 5); + } else { + r = FLD_MOD(r, cfg->op_dither_truc, 7, 6); + r = FLD_MOD(r, 0, 5, 5); + } + hdmi_write_reg(core_sys_base, HDMI_CORE_SYS_VID_MODE, r); + + /* HDMI_Ctrl */ + r = hdmi_read_reg(hdmi_av_base(ip_data), HDMI_CORE_AV_HDMI_CTRL); + r = FLD_MOD(r, cfg->deep_color_pkt, 6, 6); + r = FLD_MOD(r, cfg->pkt_mode, 5, 3); + r = FLD_MOD(r, cfg->hdmi_dvi, 0, 0); + hdmi_write_reg(hdmi_av_base(ip_data), HDMI_CORE_AV_HDMI_CTRL, r); + + /* TMDS_CTRL */ + REG_FLD_MOD(core_sys_base, + HDMI_CORE_SYS_TMDS_CTRL, cfg->tclk_sel_clkmult, 6, 5); +} + +static void hdmi_core_aux_infoframe_avi_config(struct hdmi_ip_data *ip_data, + struct hdmi_core_infoframe_avi info_avi) +{ + u32 val; + char sum = 0, checksum = 0; + void __iomem *av_base = hdmi_av_base(ip_data); + + sum += 0x82 + 0x002 + 0x00D; + hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_TYPE, 0x082); + hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_VERS, 0x002); + hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_LEN, 0x00D); + + val = (info_avi.db1_format << 5) | + (info_avi.db1_active_info << 4) | + (info_avi.db1_bar_info_dv << 2) | + (info_avi.db1_scan_info); + hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_DBYTE(0), val); + sum += val; + + val = (info_avi.db2_colorimetry << 6) | + (info_avi.db2_aspect_ratio << 4) | + (info_avi.db2_active_fmt_ar); + hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_DBYTE(1), val); + sum += val; + + val = (info_avi.db3_itc << 7) | + (info_avi.db3_ec << 4) | + (info_avi.db3_q_range << 2) | + (info_avi.db3_nup_scaling); + hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_DBYTE(2), val); + sum += val; + + hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_DBYTE(3), + info_avi.db4_videocode); + sum += info_avi.db4_videocode; + + val = info_avi.db5_pixel_repeat; + hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_DBYTE(4), val); + sum += val; + + val = info_avi.db6_7_line_eoftop & 0x00FF; + hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_DBYTE(5), val); + sum += val; + + val = ((info_avi.db6_7_line_eoftop >> 8) & 0x00FF); + hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_DBYTE(6), val); + sum += val; + + val = info_avi.db8_9_line_sofbottom & 0x00FF; + hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_DBYTE(7), val); + sum += val; + + val = ((info_avi.db8_9_line_sofbottom >> 8) & 0x00FF); + hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_DBYTE(8), val); + sum += val; + + val = info_avi.db10_11_pixel_eofleft & 0x00FF; + hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_DBYTE(9), val); + sum += val; + + val = ((info_avi.db10_11_pixel_eofleft >> 8) & 0x00FF); + hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_DBYTE(10), val); + sum += val; + + val = info_avi.db12_13_pixel_sofright & 0x00FF; + hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_DBYTE(11), val); + sum += val; + + val = ((info_avi.db12_13_pixel_sofright >> 8) & 0x00FF); + hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_DBYTE(12), val); + sum += val; + + checksum = 0x100 - sum; + hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_CHSUM, checksum); +} + +static void hdmi_core_av_packet_config(struct hdmi_ip_data *ip_data, + struct hdmi_core_packet_enable_repeat repeat_cfg) +{ + /* enable/repeat the infoframe */ + hdmi_write_reg(hdmi_av_base(ip_data), HDMI_CORE_AV_PB_CTRL1, + (repeat_cfg.audio_pkt << 5) | + (repeat_cfg.audio_pkt_repeat << 4) | + (repeat_cfg.avi_infoframe << 1) | + (repeat_cfg.avi_infoframe_repeat)); + + /* enable/repeat the packet */ + hdmi_write_reg(hdmi_av_base(ip_data), HDMI_CORE_AV_PB_CTRL2, + (repeat_cfg.gen_cntrl_pkt << 3) | + (repeat_cfg.gen_cntrl_pkt_repeat << 2) | + (repeat_cfg.generic_pkt << 1) | + (repeat_cfg.generic_pkt_repeat)); +} + +static void hdmi_wp_init(struct omap_video_timings *timings, + struct hdmi_video_format *video_fmt, + struct hdmi_video_interface *video_int) +{ + pr_debug("Enter hdmi_wp_init\n"); + + timings->hbp = 0; + timings->hfp = 0; + timings->hsw = 0; + timings->vbp = 0; + timings->vfp = 0; + timings->vsw = 0; + + video_fmt->packing_mode = HDMI_PACK_10b_RGB_YUV444; + video_fmt->y_res = 0; + video_fmt->x_res = 0; + + video_int->vsp = 0; + video_int->hsp = 0; + + video_int->interlacing = 0; + video_int->tm = 0; /* HDMI_TIMING_SLAVE */ + +} + +void ti_hdmi_4xxx_wp_video_start(struct hdmi_ip_data *ip_data, bool start) +{ + REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, start, 31, 31); +} + +static void hdmi_wp_video_init_format(struct hdmi_video_format *video_fmt, + struct omap_video_timings *timings, struct hdmi_config *param) +{ + pr_debug("Enter hdmi_wp_video_init_format\n"); + + video_fmt->y_res = param->timings.timings.y_res; + video_fmt->x_res = param->timings.timings.x_res; + + timings->hbp = param->timings.timings.hbp; + timings->hfp = param->timings.timings.hfp; + timings->hsw = param->timings.timings.hsw; + timings->vbp = param->timings.timings.vbp; + timings->vfp = param->timings.timings.vfp; + timings->vsw = param->timings.timings.vsw; +} + +static void hdmi_wp_video_config_format(struct hdmi_ip_data *ip_data, + struct hdmi_video_format *video_fmt) +{ + u32 l = 0; + + REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, + video_fmt->packing_mode, 10, 8); + + l |= FLD_VAL(video_fmt->y_res, 31, 16); + l |= FLD_VAL(video_fmt->x_res, 15, 0); + hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_SIZE, l); +} + +static void hdmi_wp_video_config_interface(struct hdmi_ip_data *ip_data, + struct hdmi_video_interface *video_int) +{ + u32 r; + pr_debug("Enter hdmi_wp_video_config_interface\n"); + + r = hdmi_read_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG); + r = FLD_MOD(r, video_int->vsp, 7, 7); + r = FLD_MOD(r, video_int->hsp, 6, 6); + r = FLD_MOD(r, video_int->interlacing, 3, 3); + r = FLD_MOD(r, video_int->tm, 1, 0); + hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, r); +} + +static void hdmi_wp_video_config_timing(struct hdmi_ip_data *ip_data, + struct omap_video_timings *timings) +{ + u32 timing_h = 0; + u32 timing_v = 0; + + pr_debug("Enter hdmi_wp_video_config_timing\n"); + + timing_h |= FLD_VAL(timings->hbp, 31, 20); + timing_h |= FLD_VAL(timings->hfp, 19, 8); + timing_h |= FLD_VAL(timings->hsw, 7, 0); + hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_TIMING_H, timing_h); + + timing_v |= FLD_VAL(timings->vbp, 31, 20); + timing_v |= FLD_VAL(timings->vfp, 19, 8); + timing_v |= FLD_VAL(timings->vsw, 7, 0); + hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_TIMING_V, timing_v); +} + +void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data) +{ + /* HDMI */ + struct omap_video_timings video_timing; + struct hdmi_video_format video_format; + struct hdmi_video_interface video_interface; + /* HDMI core */ + struct hdmi_core_infoframe_avi avi_cfg; + struct hdmi_core_video_config v_core_cfg; + struct hdmi_core_packet_enable_repeat repeat_cfg; + struct hdmi_config *cfg = &ip_data->cfg; + + hdmi_wp_init(&video_timing, &video_format, + &video_interface); + + hdmi_core_init(&v_core_cfg, + &avi_cfg, + &repeat_cfg); + + hdmi_wp_video_init_format(&video_format, &video_timing, cfg); + + hdmi_wp_video_config_timing(ip_data, &video_timing); + + /* video config */ + video_format.packing_mode = HDMI_PACK_24b_RGB_YUV444_YUV422; + + hdmi_wp_video_config_format(ip_data, &video_format); + + video_interface.vsp = cfg->timings.vsync_pol; + video_interface.hsp = cfg->timings.hsync_pol; + video_interface.interlacing = cfg->interlace; + video_interface.tm = 1 ; /* HDMI_TIMING_MASTER_24BIT */ + + hdmi_wp_video_config_interface(ip_data, &video_interface); + + /* + * configure core video part + * set software reset in the core + */ + hdmi_core_swreset_assert(ip_data); + + /* power down off */ + hdmi_core_powerdown_disable(ip_data); + + v_core_cfg.pkt_mode = HDMI_PACKETMODE24BITPERPIXEL; + v_core_cfg.hdmi_dvi = cfg->cm.mode; + + hdmi_core_video_config(ip_data, &v_core_cfg); + + /* release software reset in the core */ + hdmi_core_swreset_release(ip_data); + + /* + * configure packet + * info frame video see doc CEA861-D page 65 + */ + avi_cfg.db1_format = HDMI_INFOFRAME_AVI_DB1Y_RGB; + avi_cfg.db1_active_info = + HDMI_INFOFRAME_AVI_DB1A_ACTIVE_FORMAT_OFF; + avi_cfg.db1_bar_info_dv = HDMI_INFOFRAME_AVI_DB1B_NO; + avi_cfg.db1_scan_info = HDMI_INFOFRAME_AVI_DB1S_0; + avi_cfg.db2_colorimetry = HDMI_INFOFRAME_AVI_DB2C_NO; + avi_cfg.db2_aspect_ratio = HDMI_INFOFRAME_AVI_DB2M_NO; + avi_cfg.db2_active_fmt_ar = HDMI_INFOFRAME_AVI_DB2R_SAME; + avi_cfg.db3_itc = HDMI_INFOFRAME_AVI_DB3ITC_NO; + avi_cfg.db3_ec = HDMI_INFOFRAME_AVI_DB3EC_XVYUV601; + avi_cfg.db3_q_range = HDMI_INFOFRAME_AVI_DB3Q_DEFAULT; + avi_cfg.db3_nup_scaling = HDMI_INFOFRAME_AVI_DB3SC_NO; + avi_cfg.db4_videocode = cfg->cm.code; + avi_cfg.db5_pixel_repeat = HDMI_INFOFRAME_AVI_DB5PR_NO; + avi_cfg.db6_7_line_eoftop = 0; + avi_cfg.db8_9_line_sofbottom = 0; + avi_cfg.db10_11_pixel_eofleft = 0; + avi_cfg.db12_13_pixel_sofright = 0; + + hdmi_core_aux_infoframe_avi_config(ip_data, avi_cfg); + + /* enable/repeat the infoframe */ + repeat_cfg.avi_infoframe = HDMI_PACKETENABLE; + repeat_cfg.avi_infoframe_repeat = HDMI_PACKETREPEATON; + /* wakeup */ + repeat_cfg.audio_pkt = HDMI_PACKETENABLE; + repeat_cfg.audio_pkt_repeat = HDMI_PACKETREPEATON; + hdmi_core_av_packet_config(ip_data, repeat_cfg); +} + +void ti_hdmi_4xxx_wp_dump(struct hdmi_ip_data *ip_data, struct seq_file *s) +{ +#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r,\ + hdmi_read_reg(hdmi_wp_base(ip_data), r)) + + DUMPREG(HDMI_WP_REVISION); + DUMPREG(HDMI_WP_SYSCONFIG); + DUMPREG(HDMI_WP_IRQSTATUS_RAW); + DUMPREG(HDMI_WP_IRQSTATUS); + DUMPREG(HDMI_WP_PWR_CTRL); + DUMPREG(HDMI_WP_IRQENABLE_SET); + DUMPREG(HDMI_WP_VIDEO_CFG); + DUMPREG(HDMI_WP_VIDEO_SIZE); + DUMPREG(HDMI_WP_VIDEO_TIMING_H); + DUMPREG(HDMI_WP_VIDEO_TIMING_V); + DUMPREG(HDMI_WP_WP_CLK); + DUMPREG(HDMI_WP_AUDIO_CFG); + DUMPREG(HDMI_WP_AUDIO_CFG2); + DUMPREG(HDMI_WP_AUDIO_CTRL); + DUMPREG(HDMI_WP_AUDIO_DATA); +} + +void ti_hdmi_4xxx_pll_dump(struct hdmi_ip_data *ip_data, struct seq_file *s) +{ +#define DUMPPLL(r) seq_printf(s, "%-35s %08x\n", #r,\ + hdmi_read_reg(hdmi_pll_base(ip_data), r)) + + DUMPPLL(PLLCTRL_PLL_CONTROL); + DUMPPLL(PLLCTRL_PLL_STATUS); + DUMPPLL(PLLCTRL_PLL_GO); + DUMPPLL(PLLCTRL_CFG1); + DUMPPLL(PLLCTRL_CFG2); + DUMPPLL(PLLCTRL_CFG3); + DUMPPLL(PLLCTRL_CFG4); +} + +void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s) +{ + int i; + +#define CORE_REG(i, name) name(i) +#define DUMPCORE(r) seq_printf(s, "%-35s %08x\n", #r,\ + hdmi_read_reg(hdmi_pll_base(ip_data), r)) +#define DUMPCOREAV(i, r) seq_printf(s, "%s[%d]%*s %08x\n", #r, i, \ + (i < 10) ? 32 - strlen(#r) : 31 - strlen(#r), " ", \ + hdmi_read_reg(hdmi_pll_base(ip_data), CORE_REG(i, r))) + + DUMPCORE(HDMI_CORE_SYS_VND_IDL); + DUMPCORE(HDMI_CORE_SYS_DEV_IDL); + DUMPCORE(HDMI_CORE_SYS_DEV_IDH); + DUMPCORE(HDMI_CORE_SYS_DEV_REV); + DUMPCORE(HDMI_CORE_SYS_SRST); + DUMPCORE(HDMI_CORE_CTRL1); + DUMPCORE(HDMI_CORE_SYS_SYS_STAT); + DUMPCORE(HDMI_CORE_SYS_VID_ACEN); + DUMPCORE(HDMI_CORE_SYS_VID_MODE); + DUMPCORE(HDMI_CORE_SYS_INTR_STATE); + DUMPCORE(HDMI_CORE_SYS_INTR1); + DUMPCORE(HDMI_CORE_SYS_INTR2); + DUMPCORE(HDMI_CORE_SYS_INTR3); + DUMPCORE(HDMI_CORE_SYS_INTR4); + DUMPCORE(HDMI_CORE_SYS_UMASK1); + DUMPCORE(HDMI_CORE_SYS_TMDS_CTRL); + DUMPCORE(HDMI_CORE_SYS_DE_DLY); + DUMPCORE(HDMI_CORE_SYS_DE_CTRL); + DUMPCORE(HDMI_CORE_SYS_DE_TOP); + DUMPCORE(HDMI_CORE_SYS_DE_CNTL); + DUMPCORE(HDMI_CORE_SYS_DE_CNTH); + DUMPCORE(HDMI_CORE_SYS_DE_LINL); + DUMPCORE(HDMI_CORE_SYS_DE_LINH_1); + + DUMPCORE(HDMI_CORE_DDC_CMD); + DUMPCORE(HDMI_CORE_DDC_STATUS); + DUMPCORE(HDMI_CORE_DDC_ADDR); + DUMPCORE(HDMI_CORE_DDC_OFFSET); + DUMPCORE(HDMI_CORE_DDC_COUNT1); + DUMPCORE(HDMI_CORE_DDC_COUNT2); + DUMPCORE(HDMI_CORE_DDC_DATA); + DUMPCORE(HDMI_CORE_DDC_SEGM); + + DUMPCORE(HDMI_CORE_AV_HDMI_CTRL); + DUMPCORE(HDMI_CORE_AV_DPD); + DUMPCORE(HDMI_CORE_AV_PB_CTRL1); + DUMPCORE(HDMI_CORE_AV_PB_CTRL2); + DUMPCORE(HDMI_CORE_AV_AVI_TYPE); + DUMPCORE(HDMI_CORE_AV_AVI_VERS); + DUMPCORE(HDMI_CORE_AV_AVI_LEN); + DUMPCORE(HDMI_CORE_AV_AVI_CHSUM); + + for (i = 0; i < HDMI_CORE_AV_AVI_DBYTE_NELEMS; i++) + DUMPCOREAV(i, HDMI_CORE_AV_AVI_DBYTE); + + for (i = 0; i < HDMI_CORE_AV_SPD_DBYTE_NELEMS; i++) + DUMPCOREAV(i, HDMI_CORE_AV_SPD_DBYTE); + + for (i = 0; i < HDMI_CORE_AV_AUD_DBYTE_NELEMS; i++) + DUMPCOREAV(i, HDMI_CORE_AV_AUD_DBYTE); + + for (i = 0; i < HDMI_CORE_AV_MPEG_DBYTE_NELEMS; i++) + DUMPCOREAV(i, HDMI_CORE_AV_MPEG_DBYTE); + + for (i = 0; i < HDMI_CORE_AV_GEN_DBYTE_NELEMS; i++) + DUMPCOREAV(i, HDMI_CORE_AV_GEN_DBYTE); + + for (i = 0; i < HDMI_CORE_AV_GEN2_DBYTE_NELEMS; i++) + DUMPCOREAV(i, HDMI_CORE_AV_GEN2_DBYTE); + + DUMPCORE(HDMI_CORE_AV_ACR_CTRL); + DUMPCORE(HDMI_CORE_AV_FREQ_SVAL); + DUMPCORE(HDMI_CORE_AV_N_SVAL1); + DUMPCORE(HDMI_CORE_AV_N_SVAL2); + DUMPCORE(HDMI_CORE_AV_N_SVAL3); + DUMPCORE(HDMI_CORE_AV_CTS_SVAL1); + DUMPCORE(HDMI_CORE_AV_CTS_SVAL2); + DUMPCORE(HDMI_CORE_AV_CTS_SVAL3); + DUMPCORE(HDMI_CORE_AV_CTS_HVAL1); + DUMPCORE(HDMI_CORE_AV_CTS_HVAL2); + DUMPCORE(HDMI_CORE_AV_CTS_HVAL3); + DUMPCORE(HDMI_CORE_AV_AUD_MODE); + DUMPCORE(HDMI_CORE_AV_SPDIF_CTRL); + DUMPCORE(HDMI_CORE_AV_HW_SPDIF_FS); + DUMPCORE(HDMI_CORE_AV_SWAP_I2S); + DUMPCORE(HDMI_CORE_AV_SPDIF_ERTH); + DUMPCORE(HDMI_CORE_AV_I2S_IN_MAP); + DUMPCORE(HDMI_CORE_AV_I2S_IN_CTRL); + DUMPCORE(HDMI_CORE_AV_I2S_CHST0); + DUMPCORE(HDMI_CORE_AV_I2S_CHST1); + DUMPCORE(HDMI_CORE_AV_I2S_CHST2); + DUMPCORE(HDMI_CORE_AV_I2S_CHST4); + DUMPCORE(HDMI_CORE_AV_I2S_CHST5); + DUMPCORE(HDMI_CORE_AV_ASRC); + DUMPCORE(HDMI_CORE_AV_I2S_IN_LEN); + DUMPCORE(HDMI_CORE_AV_HDMI_CTRL); + DUMPCORE(HDMI_CORE_AV_AUDO_TXSTAT); + DUMPCORE(HDMI_CORE_AV_AUD_PAR_BUSCLK_1); + DUMPCORE(HDMI_CORE_AV_AUD_PAR_BUSCLK_2); + DUMPCORE(HDMI_CORE_AV_AUD_PAR_BUSCLK_3); + DUMPCORE(HDMI_CORE_AV_TEST_TXCTRL); + DUMPCORE(HDMI_CORE_AV_DPD); + DUMPCORE(HDMI_CORE_AV_PB_CTRL1); + DUMPCORE(HDMI_CORE_AV_PB_CTRL2); + DUMPCORE(HDMI_CORE_AV_AVI_TYPE); + DUMPCORE(HDMI_CORE_AV_AVI_VERS); + DUMPCORE(HDMI_CORE_AV_AVI_LEN); + DUMPCORE(HDMI_CORE_AV_AVI_CHSUM); + DUMPCORE(HDMI_CORE_AV_SPD_TYPE); + DUMPCORE(HDMI_CORE_AV_SPD_VERS); + DUMPCORE(HDMI_CORE_AV_SPD_LEN); + DUMPCORE(HDMI_CORE_AV_SPD_CHSUM); + DUMPCORE(HDMI_CORE_AV_AUDIO_TYPE); + DUMPCORE(HDMI_CORE_AV_AUDIO_VERS); + DUMPCORE(HDMI_CORE_AV_AUDIO_LEN); + DUMPCORE(HDMI_CORE_AV_AUDIO_CHSUM); + DUMPCORE(HDMI_CORE_AV_MPEG_TYPE); + DUMPCORE(HDMI_CORE_AV_MPEG_VERS); + DUMPCORE(HDMI_CORE_AV_MPEG_LEN); + DUMPCORE(HDMI_CORE_AV_MPEG_CHSUM); + DUMPCORE(HDMI_CORE_AV_CP_BYTE1); + DUMPCORE(HDMI_CORE_AV_CEC_ADDR_ID); +} + +void ti_hdmi_4xxx_phy_dump(struct hdmi_ip_data *ip_data, struct seq_file *s) +{ +#define DUMPPHY(r) seq_printf(s, "%-35s %08x\n", #r,\ + hdmi_read_reg(hdmi_phy_base(ip_data), r)) + + DUMPPHY(HDMI_TXPHY_TX_CTRL); + DUMPPHY(HDMI_TXPHY_DIGITAL_CTRL); + DUMPPHY(HDMI_TXPHY_POWER_CTRL); + DUMPPHY(HDMI_TXPHY_PAD_CFG_CTRL); +} + +#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ + defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) +void hdmi_wp_audio_config_format(struct hdmi_ip_data *ip_data, + struct hdmi_audio_format *aud_fmt) +{ + u32 r; + + DSSDBG("Enter hdmi_wp_audio_config_format\n"); + + r = hdmi_read_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CFG); + r = FLD_MOD(r, aud_fmt->stereo_channels, 26, 24); + r = FLD_MOD(r, aud_fmt->active_chnnls_msk, 23, 16); + r = FLD_MOD(r, aud_fmt->en_sig_blk_strt_end, 5, 5); + r = FLD_MOD(r, aud_fmt->type, 4, 4); + r = FLD_MOD(r, aud_fmt->justification, 3, 3); + r = FLD_MOD(r, aud_fmt->sample_order, 2, 2); + r = FLD_MOD(r, aud_fmt->samples_per_word, 1, 1); + r = FLD_MOD(r, aud_fmt->sample_size, 0, 0); + hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CFG, r); +} + +void hdmi_wp_audio_config_dma(struct hdmi_ip_data *ip_data, + struct hdmi_audio_dma *aud_dma) +{ + u32 r; + + DSSDBG("Enter hdmi_wp_audio_config_dma\n"); + + r = hdmi_read_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CFG2); + r = FLD_MOD(r, aud_dma->transfer_size, 15, 8); + r = FLD_MOD(r, aud_dma->block_size, 7, 0); + hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CFG2, r); + + r = hdmi_read_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CTRL); + r = FLD_MOD(r, aud_dma->mode, 9, 9); + r = FLD_MOD(r, aud_dma->fifo_threshold, 8, 0); + hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CTRL, r); +} + +void hdmi_core_audio_config(struct hdmi_ip_data *ip_data, + struct hdmi_core_audio_config *cfg) +{ + u32 r; + void __iomem *av_base = hdmi_av_base(ip_data); + + /* audio clock recovery parameters */ + r = hdmi_read_reg(av_base, HDMI_CORE_AV_ACR_CTRL); + r = FLD_MOD(r, cfg->use_mclk, 2, 2); + r = FLD_MOD(r, cfg->en_acr_pkt, 1, 1); + r = FLD_MOD(r, cfg->cts_mode, 0, 0); + hdmi_write_reg(av_base, HDMI_CORE_AV_ACR_CTRL, r); + + REG_FLD_MOD(av_base, HDMI_CORE_AV_N_SVAL1, cfg->n, 7, 0); + REG_FLD_MOD(av_base, HDMI_CORE_AV_N_SVAL2, cfg->n >> 8, 7, 0); + REG_FLD_MOD(av_base, HDMI_CORE_AV_N_SVAL3, cfg->n >> 16, 7, 0); + + if (cfg->cts_mode == HDMI_AUDIO_CTS_MODE_SW) { + REG_FLD_MOD(av_base, HDMI_CORE_AV_CTS_SVAL1, cfg->cts, 7, 0); + REG_FLD_MOD(av_base, + HDMI_CORE_AV_CTS_SVAL2, cfg->cts >> 8, 7, 0); + REG_FLD_MOD(av_base, + HDMI_CORE_AV_CTS_SVAL3, cfg->cts >> 16, 7, 0); + } else { + /* + * HDMI IP uses this configuration to divide the MCLK to + * update CTS value. + */ + REG_FLD_MOD(av_base, + HDMI_CORE_AV_FREQ_SVAL, cfg->mclk_mode, 2, 0); + + /* Configure clock for audio packets */ + REG_FLD_MOD(av_base, HDMI_CORE_AV_AUD_PAR_BUSCLK_1, + cfg->aud_par_busclk, 7, 0); + REG_FLD_MOD(av_base, HDMI_CORE_AV_AUD_PAR_BUSCLK_2, + (cfg->aud_par_busclk >> 8), 7, 0); + REG_FLD_MOD(av_base, HDMI_CORE_AV_AUD_PAR_BUSCLK_3, + (cfg->aud_par_busclk >> 16), 7, 0); + } + + /* Override of SPDIF sample frequency with value in I2S_CHST4 */ + REG_FLD_MOD(av_base, HDMI_CORE_AV_SPDIF_CTRL, + cfg->fs_override, 1, 1); + + /* I2S parameters */ + REG_FLD_MOD(av_base, HDMI_CORE_AV_I2S_CHST4, + cfg->freq_sample, 3, 0); + + r = hdmi_read_reg(av_base, HDMI_CORE_AV_I2S_IN_CTRL); + r = FLD_MOD(r, cfg->i2s_cfg.en_high_bitrate_aud, 7, 7); + r = FLD_MOD(r, cfg->i2s_cfg.sck_edge_mode, 6, 6); + r = FLD_MOD(r, cfg->i2s_cfg.cbit_order, 5, 5); + r = FLD_MOD(r, cfg->i2s_cfg.vbit, 4, 4); + r = FLD_MOD(r, cfg->i2s_cfg.ws_polarity, 3, 3); + r = FLD_MOD(r, cfg->i2s_cfg.justification, 2, 2); + r = FLD_MOD(r, cfg->i2s_cfg.direction, 1, 1); + r = FLD_MOD(r, cfg->i2s_cfg.shift, 0, 0); + hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_IN_CTRL, r); + + r = hdmi_read_reg(av_base, HDMI_CORE_AV_I2S_CHST5); + r = FLD_MOD(r, cfg->freq_sample, 7, 4); + r = FLD_MOD(r, cfg->i2s_cfg.word_length, 3, 1); + r = FLD_MOD(r, cfg->i2s_cfg.word_max_length, 0, 0); + hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST5, r); + + REG_FLD_MOD(av_base, HDMI_CORE_AV_I2S_IN_LEN, + cfg->i2s_cfg.in_length_bits, 3, 0); + + /* Audio channels and mode parameters */ + REG_FLD_MOD(av_base, HDMI_CORE_AV_HDMI_CTRL, cfg->layout, 2, 1); + r = hdmi_read_reg(av_base, HDMI_CORE_AV_AUD_MODE); + r = FLD_MOD(r, cfg->i2s_cfg.active_sds, 7, 4); + r = FLD_MOD(r, cfg->en_dsd_audio, 3, 3); + r = FLD_MOD(r, cfg->en_parallel_aud_input, 2, 2); + r = FLD_MOD(r, cfg->en_spdif, 1, 1); + hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_MODE, r); +} + +void hdmi_core_audio_infoframe_config(struct hdmi_ip_data *ip_data, + struct hdmi_core_infoframe_audio *info_aud) +{ + u8 val; + u8 sum = 0, checksum = 0; + void __iomem *av_base = hdmi_av_base(ip_data); + + /* + * Set audio info frame type, version and length as + * described in HDMI 1.4a Section 8.2.2 specification. + * Checksum calculation is defined in Section 5.3.5. + */ + hdmi_write_reg(av_base, HDMI_CORE_AV_AUDIO_TYPE, 0x84); + hdmi_write_reg(av_base, HDMI_CORE_AV_AUDIO_VERS, 0x01); + hdmi_write_reg(av_base, HDMI_CORE_AV_AUDIO_LEN, 0x0a); + sum += 0x84 + 0x001 + 0x00a; + + val = (info_aud->db1_coding_type << 4) + | (info_aud->db1_channel_count - 1); + hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(0), val); + sum += val; + + val = (info_aud->db2_sample_freq << 2) | info_aud->db2_sample_size; + hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(1), val); + sum += val; + + hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(2), 0x00); + + val = info_aud->db4_channel_alloc; + hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(3), val); + sum += val; + + val = (info_aud->db5_downmix_inh << 7) | (info_aud->db5_lsv << 3); + hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(4), val); + sum += val; + + hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(5), 0x00); + hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(6), 0x00); + hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(7), 0x00); + hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(8), 0x00); + hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(9), 0x00); + + checksum = 0x100 - sum; + hdmi_write_reg(av_base, + HDMI_CORE_AV_AUDIO_CHSUM, checksum); + + /* + * TODO: Add MPEG and SPD enable and repeat cfg when EDID parsing + * is available. + */ +} + +int hdmi_config_audio_acr(struct hdmi_ip_data *ip_data, + u32 sample_freq, u32 *n, u32 *cts) +{ + u32 r; + u32 deep_color = 0; + u32 pclk = ip_data->cfg.timings.timings.pixel_clock; + + if (n == NULL || cts == NULL) + return -EINVAL; + /* + * Obtain current deep color configuration. This needed + * to calculate the TMDS clock based on the pixel clock. + */ + r = REG_GET(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, 1, 0); + switch (r) { + case 1: /* No deep color selected */ + deep_color = 100; + break; + case 2: /* 10-bit deep color selected */ + deep_color = 125; + break; + case 3: /* 12-bit deep color selected */ + deep_color = 150; + break; + default: + return -EINVAL; + } + + switch (sample_freq) { + case 32000: + if ((deep_color == 125) && ((pclk == 54054) + || (pclk == 74250))) + *n = 8192; + else + *n = 4096; + break; + case 44100: + *n = 6272; + break; + case 48000: + if ((deep_color == 125) && ((pclk == 54054) + || (pclk == 74250))) + *n = 8192; + else + *n = 6144; + break; + default: + *n = 0; + return -EINVAL; + } + + /* Calculate CTS. See HDMI 1.3a or 1.4a specifications */ + *cts = pclk * (*n / 128) * deep_color / (sample_freq / 10); + + return 0; +} + +int hdmi_audio_trigger(struct hdmi_ip_data *ip_data, + struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + int err = 0; + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + REG_FLD_MOD(hdmi_av_base(ip_data), + HDMI_CORE_AV_AUD_MODE, 1, 0, 0); + REG_FLD_MOD(hdmi_wp_base(ip_data), + HDMI_WP_AUDIO_CTRL, 1, 31, 31); + REG_FLD_MOD(hdmi_wp_base(ip_data), + HDMI_WP_AUDIO_CTRL, 1, 30, 30); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + REG_FLD_MOD(hdmi_av_base(ip_data), + HDMI_CORE_AV_AUD_MODE, 0, 0, 0); + REG_FLD_MOD(hdmi_wp_base(ip_data), + HDMI_WP_AUDIO_CTRL, 0, 30, 30); + REG_FLD_MOD(hdmi_wp_base(ip_data), + HDMI_WP_AUDIO_CTRL, 0, 31, 31); + break; + default: + err = -EINVAL; + } + return err; +} +#endif diff --git a/drivers/video/omap2/dss/hdmi.h b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h index c885f9cb0659..204095632d27 100644 --- a/drivers/video/omap2/dss/hdmi.h +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h @@ -1,7 +1,7 @@ /* - * hdmi.h + * ti_hdmi_4xxx_ip.h * - * HDMI driver definition for TI OMAP4 processors. + * HDMI header definition for DM81xx, DM38xx, TI OMAP4 etc processors. * * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/ * @@ -18,202 +18,177 @@ * this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef _OMAP4_DSS_HDMI_H_ -#define _OMAP4_DSS_HDMI_H_ +#ifndef _HDMI_TI_4xxx_H_ +#define _HDMI_TI_4xxx_H_ #include <linux/string.h> #include <video/omapdss.h> - -#define HDMI_WP 0x0 -#define HDMI_CORE_SYS 0x400 -#define HDMI_CORE_AV 0x900 -#define HDMI_PLLCTRL 0x200 -#define HDMI_PHY 0x300 - -struct hdmi_reg { u16 idx; }; - -#define HDMI_REG(idx) ((const struct hdmi_reg) { idx }) +#include "ti_hdmi.h" +#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ + defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) +#include <sound/soc.h> +#include <sound/pcm_params.h> +#endif /* HDMI Wrapper */ -#define HDMI_WP_REG(idx) HDMI_REG(HDMI_WP + idx) - -#define HDMI_WP_REVISION HDMI_WP_REG(0x0) -#define HDMI_WP_SYSCONFIG HDMI_WP_REG(0x10) -#define HDMI_WP_IRQSTATUS_RAW HDMI_WP_REG(0x24) -#define HDMI_WP_IRQSTATUS HDMI_WP_REG(0x28) -#define HDMI_WP_PWR_CTRL HDMI_WP_REG(0x40) -#define HDMI_WP_IRQENABLE_SET HDMI_WP_REG(0x2C) -#define HDMI_WP_VIDEO_CFG HDMI_WP_REG(0x50) -#define HDMI_WP_VIDEO_SIZE HDMI_WP_REG(0x60) -#define HDMI_WP_VIDEO_TIMING_H HDMI_WP_REG(0x68) -#define HDMI_WP_VIDEO_TIMING_V HDMI_WP_REG(0x6C) -#define HDMI_WP_WP_CLK HDMI_WP_REG(0x70) -#define HDMI_WP_AUDIO_CFG HDMI_WP_REG(0x80) -#define HDMI_WP_AUDIO_CFG2 HDMI_WP_REG(0x84) -#define HDMI_WP_AUDIO_CTRL HDMI_WP_REG(0x88) -#define HDMI_WP_AUDIO_DATA HDMI_WP_REG(0x8C) + +#define HDMI_WP_REVISION 0x0 +#define HDMI_WP_SYSCONFIG 0x10 +#define HDMI_WP_IRQSTATUS_RAW 0x24 +#define HDMI_WP_IRQSTATUS 0x28 +#define HDMI_WP_PWR_CTRL 0x40 +#define HDMI_WP_IRQENABLE_SET 0x2C +#define HDMI_WP_VIDEO_CFG 0x50 +#define HDMI_WP_VIDEO_SIZE 0x60 +#define HDMI_WP_VIDEO_TIMING_H 0x68 +#define HDMI_WP_VIDEO_TIMING_V 0x6C +#define HDMI_WP_WP_CLK 0x70 +#define HDMI_WP_AUDIO_CFG 0x80 +#define HDMI_WP_AUDIO_CFG2 0x84 +#define HDMI_WP_AUDIO_CTRL 0x88 +#define HDMI_WP_AUDIO_DATA 0x8C /* HDMI IP Core System */ -#define HDMI_CORE_SYS_REG(idx) HDMI_REG(HDMI_CORE_SYS + idx) - -#define HDMI_CORE_SYS_VND_IDL HDMI_CORE_SYS_REG(0x0) -#define HDMI_CORE_SYS_DEV_IDL HDMI_CORE_SYS_REG(0x8) -#define HDMI_CORE_SYS_DEV_IDH HDMI_CORE_SYS_REG(0xC) -#define HDMI_CORE_SYS_DEV_REV HDMI_CORE_SYS_REG(0x10) -#define HDMI_CORE_SYS_SRST HDMI_CORE_SYS_REG(0x14) -#define HDMI_CORE_CTRL1 HDMI_CORE_SYS_REG(0x20) -#define HDMI_CORE_SYS_SYS_STAT HDMI_CORE_SYS_REG(0x24) -#define HDMI_CORE_SYS_VID_ACEN HDMI_CORE_SYS_REG(0x124) -#define HDMI_CORE_SYS_VID_MODE HDMI_CORE_SYS_REG(0x128) -#define HDMI_CORE_SYS_INTR_STATE HDMI_CORE_SYS_REG(0x1C0) -#define HDMI_CORE_SYS_INTR1 HDMI_CORE_SYS_REG(0x1C4) -#define HDMI_CORE_SYS_INTR2 HDMI_CORE_SYS_REG(0x1C8) -#define HDMI_CORE_SYS_INTR3 HDMI_CORE_SYS_REG(0x1CC) -#define HDMI_CORE_SYS_INTR4 HDMI_CORE_SYS_REG(0x1D0) -#define HDMI_CORE_SYS_UMASK1 HDMI_CORE_SYS_REG(0x1D4) -#define HDMI_CORE_SYS_TMDS_CTRL HDMI_CORE_SYS_REG(0x208) -#define HDMI_CORE_SYS_DE_DLY HDMI_CORE_SYS_REG(0xC8) -#define HDMI_CORE_SYS_DE_CTRL HDMI_CORE_SYS_REG(0xCC) -#define HDMI_CORE_SYS_DE_TOP HDMI_CORE_SYS_REG(0xD0) -#define HDMI_CORE_SYS_DE_CNTL HDMI_CORE_SYS_REG(0xD8) -#define HDMI_CORE_SYS_DE_CNTH HDMI_CORE_SYS_REG(0xDC) -#define HDMI_CORE_SYS_DE_LINL HDMI_CORE_SYS_REG(0xE0) -#define HDMI_CORE_SYS_DE_LINH_1 HDMI_CORE_SYS_REG(0xE4) + +#define HDMI_CORE_SYS_VND_IDL 0x0 +#define HDMI_CORE_SYS_DEV_IDL 0x8 +#define HDMI_CORE_SYS_DEV_IDH 0xC +#define HDMI_CORE_SYS_DEV_REV 0x10 +#define HDMI_CORE_SYS_SRST 0x14 +#define HDMI_CORE_CTRL1 0x20 +#define HDMI_CORE_SYS_SYS_STAT 0x24 +#define HDMI_CORE_SYS_VID_ACEN 0x124 +#define HDMI_CORE_SYS_VID_MODE 0x128 +#define HDMI_CORE_SYS_INTR_STATE 0x1C0 +#define HDMI_CORE_SYS_INTR1 0x1C4 +#define HDMI_CORE_SYS_INTR2 0x1C8 +#define HDMI_CORE_SYS_INTR3 0x1CC +#define HDMI_CORE_SYS_INTR4 0x1D0 +#define HDMI_CORE_SYS_UMASK1 0x1D4 +#define HDMI_CORE_SYS_TMDS_CTRL 0x208 +#define HDMI_CORE_SYS_DE_DLY 0xC8 +#define HDMI_CORE_SYS_DE_CTRL 0xCC +#define HDMI_CORE_SYS_DE_TOP 0xD0 +#define HDMI_CORE_SYS_DE_CNTL 0xD8 +#define HDMI_CORE_SYS_DE_CNTH 0xDC +#define HDMI_CORE_SYS_DE_LINL 0xE0 +#define HDMI_CORE_SYS_DE_LINH_1 0xE4 #define HDMI_CORE_CTRL1_VEN_FOLLOWVSYNC 0x1 #define HDMI_CORE_CTRL1_HEN_FOLLOWHSYNC 0x1 #define HDMI_CORE_CTRL1_BSEL_24BITBUS 0x1 #define HDMI_CORE_CTRL1_EDGE_RISINGEDGE 0x1 /* HDMI DDC E-DID */ -#define HDMI_CORE_DDC_CMD HDMI_CORE_SYS_REG(0x3CC) -#define HDMI_CORE_DDC_STATUS HDMI_CORE_SYS_REG(0x3C8) -#define HDMI_CORE_DDC_ADDR HDMI_CORE_SYS_REG(0x3B4) -#define HDMI_CORE_DDC_OFFSET HDMI_CORE_SYS_REG(0x3BC) -#define HDMI_CORE_DDC_COUNT1 HDMI_CORE_SYS_REG(0x3C0) -#define HDMI_CORE_DDC_COUNT2 HDMI_CORE_SYS_REG(0x3C4) -#define HDMI_CORE_DDC_DATA HDMI_CORE_SYS_REG(0x3D0) -#define HDMI_CORE_DDC_SEGM HDMI_CORE_SYS_REG(0x3B8) +#define HDMI_CORE_DDC_CMD 0x3CC +#define HDMI_CORE_DDC_STATUS 0x3C8 +#define HDMI_CORE_DDC_ADDR 0x3B4 +#define HDMI_CORE_DDC_OFFSET 0x3BC +#define HDMI_CORE_DDC_COUNT1 0x3C0 +#define HDMI_CORE_DDC_COUNT2 0x3C4 +#define HDMI_CORE_DDC_DATA 0x3D0 +#define HDMI_CORE_DDC_SEGM 0x3B8 /* HDMI IP Core Audio Video */ -#define HDMI_CORE_AV_REG(idx) HDMI_REG(HDMI_CORE_AV + idx) - -#define HDMI_CORE_AV_HDMI_CTRL HDMI_CORE_AV_REG(0xBC) -#define HDMI_CORE_AV_DPD HDMI_CORE_AV_REG(0xF4) -#define HDMI_CORE_AV_PB_CTRL1 HDMI_CORE_AV_REG(0xF8) -#define HDMI_CORE_AV_PB_CTRL2 HDMI_CORE_AV_REG(0xFC) -#define HDMI_CORE_AV_AVI_TYPE HDMI_CORE_AV_REG(0x100) -#define HDMI_CORE_AV_AVI_VERS HDMI_CORE_AV_REG(0x104) -#define HDMI_CORE_AV_AVI_LEN HDMI_CORE_AV_REG(0x108) -#define HDMI_CORE_AV_AVI_CHSUM HDMI_CORE_AV_REG(0x10C) -#define HDMI_CORE_AV_AVI_DBYTE(n) HDMI_CORE_AV_REG(n * 4 + 0x110) -#define HDMI_CORE_AV_AVI_DBYTE_NELEMS HDMI_CORE_AV_REG(15) -#define HDMI_CORE_AV_SPD_DBYTE HDMI_CORE_AV_REG(0x190) -#define HDMI_CORE_AV_SPD_DBYTE_NELEMS HDMI_CORE_AV_REG(27) -#define HDMI_CORE_AV_AUD_DBYTE(n) HDMI_CORE_AV_REG(n * 4 + 0x210) -#define HDMI_CORE_AV_AUD_DBYTE_NELEMS HDMI_CORE_AV_REG(10) -#define HDMI_CORE_AV_MPEG_DBYTE HDMI_CORE_AV_REG(0x290) -#define HDMI_CORE_AV_MPEG_DBYTE_NELEMS HDMI_CORE_AV_REG(27) -#define HDMI_CORE_AV_GEN_DBYTE HDMI_CORE_AV_REG(0x300) -#define HDMI_CORE_AV_GEN_DBYTE_NELEMS HDMI_CORE_AV_REG(31) -#define HDMI_CORE_AV_GEN2_DBYTE HDMI_CORE_AV_REG(0x380) -#define HDMI_CORE_AV_GEN2_DBYTE_NELEMS HDMI_CORE_AV_REG(31) -#define HDMI_CORE_AV_ACR_CTRL HDMI_CORE_AV_REG(0x4) -#define HDMI_CORE_AV_FREQ_SVAL HDMI_CORE_AV_REG(0x8) -#define HDMI_CORE_AV_N_SVAL1 HDMI_CORE_AV_REG(0xC) -#define HDMI_CORE_AV_N_SVAL2 HDMI_CORE_AV_REG(0x10) -#define HDMI_CORE_AV_N_SVAL3 HDMI_CORE_AV_REG(0x14) -#define HDMI_CORE_AV_CTS_SVAL1 HDMI_CORE_AV_REG(0x18) -#define HDMI_CORE_AV_CTS_SVAL2 HDMI_CORE_AV_REG(0x1C) -#define HDMI_CORE_AV_CTS_SVAL3 HDMI_CORE_AV_REG(0x20) -#define HDMI_CORE_AV_CTS_HVAL1 HDMI_CORE_AV_REG(0x24) -#define HDMI_CORE_AV_CTS_HVAL2 HDMI_CORE_AV_REG(0x28) -#define HDMI_CORE_AV_CTS_HVAL3 HDMI_CORE_AV_REG(0x2C) -#define HDMI_CORE_AV_AUD_MODE HDMI_CORE_AV_REG(0x50) -#define HDMI_CORE_AV_SPDIF_CTRL HDMI_CORE_AV_REG(0x54) -#define HDMI_CORE_AV_HW_SPDIF_FS HDMI_CORE_AV_REG(0x60) -#define HDMI_CORE_AV_SWAP_I2S HDMI_CORE_AV_REG(0x64) -#define HDMI_CORE_AV_SPDIF_ERTH HDMI_CORE_AV_REG(0x6C) -#define HDMI_CORE_AV_I2S_IN_MAP HDMI_CORE_AV_REG(0x70) -#define HDMI_CORE_AV_I2S_IN_CTRL HDMI_CORE_AV_REG(0x74) -#define HDMI_CORE_AV_I2S_CHST0 HDMI_CORE_AV_REG(0x78) -#define HDMI_CORE_AV_I2S_CHST1 HDMI_CORE_AV_REG(0x7C) -#define HDMI_CORE_AV_I2S_CHST2 HDMI_CORE_AV_REG(0x80) -#define HDMI_CORE_AV_I2S_CHST4 HDMI_CORE_AV_REG(0x84) -#define HDMI_CORE_AV_I2S_CHST5 HDMI_CORE_AV_REG(0x88) -#define HDMI_CORE_AV_ASRC HDMI_CORE_AV_REG(0x8C) -#define HDMI_CORE_AV_I2S_IN_LEN HDMI_CORE_AV_REG(0x90) -#define HDMI_CORE_AV_HDMI_CTRL HDMI_CORE_AV_REG(0xBC) -#define HDMI_CORE_AV_AUDO_TXSTAT HDMI_CORE_AV_REG(0xC0) -#define HDMI_CORE_AV_AUD_PAR_BUSCLK_1 HDMI_CORE_AV_REG(0xCC) -#define HDMI_CORE_AV_AUD_PAR_BUSCLK_2 HDMI_CORE_AV_REG(0xD0) -#define HDMI_CORE_AV_AUD_PAR_BUSCLK_3 HDMI_CORE_AV_REG(0xD4) -#define HDMI_CORE_AV_TEST_TXCTRL HDMI_CORE_AV_REG(0xF0) -#define HDMI_CORE_AV_DPD HDMI_CORE_AV_REG(0xF4) -#define HDMI_CORE_AV_PB_CTRL1 HDMI_CORE_AV_REG(0xF8) -#define HDMI_CORE_AV_PB_CTRL2 HDMI_CORE_AV_REG(0xFC) -#define HDMI_CORE_AV_AVI_TYPE HDMI_CORE_AV_REG(0x100) -#define HDMI_CORE_AV_AVI_VERS HDMI_CORE_AV_REG(0x104) -#define HDMI_CORE_AV_AVI_LEN HDMI_CORE_AV_REG(0x108) -#define HDMI_CORE_AV_AVI_CHSUM HDMI_CORE_AV_REG(0x10C) -#define HDMI_CORE_AV_SPD_TYPE HDMI_CORE_AV_REG(0x180) -#define HDMI_CORE_AV_SPD_VERS HDMI_CORE_AV_REG(0x184) -#define HDMI_CORE_AV_SPD_LEN HDMI_CORE_AV_REG(0x188) -#define HDMI_CORE_AV_SPD_CHSUM HDMI_CORE_AV_REG(0x18C) -#define HDMI_CORE_AV_AUDIO_TYPE HDMI_CORE_AV_REG(0x200) -#define HDMI_CORE_AV_AUDIO_VERS HDMI_CORE_AV_REG(0x204) -#define HDMI_CORE_AV_AUDIO_LEN HDMI_CORE_AV_REG(0x208) -#define HDMI_CORE_AV_AUDIO_CHSUM HDMI_CORE_AV_REG(0x20C) -#define HDMI_CORE_AV_MPEG_TYPE HDMI_CORE_AV_REG(0x280) -#define HDMI_CORE_AV_MPEG_VERS HDMI_CORE_AV_REG(0x284) -#define HDMI_CORE_AV_MPEG_LEN HDMI_CORE_AV_REG(0x288) -#define HDMI_CORE_AV_MPEG_CHSUM HDMI_CORE_AV_REG(0x28C) -#define HDMI_CORE_AV_CP_BYTE1 HDMI_CORE_AV_REG(0x37C) -#define HDMI_CORE_AV_CEC_ADDR_ID HDMI_CORE_AV_REG(0x3FC) + +#define HDMI_CORE_AV_HDMI_CTRL 0xBC +#define HDMI_CORE_AV_DPD 0xF4 +#define HDMI_CORE_AV_PB_CTRL1 0xF8 +#define HDMI_CORE_AV_PB_CTRL2 0xFC +#define HDMI_CORE_AV_AVI_TYPE 0x100 +#define HDMI_CORE_AV_AVI_VERS 0x104 +#define HDMI_CORE_AV_AVI_LEN 0x108 +#define HDMI_CORE_AV_AVI_CHSUM 0x10C +#define HDMI_CORE_AV_AVI_DBYTE(n) (n * 4 + 0x110) +#define HDMI_CORE_AV_AVI_DBYTE_NELEMS 15 +#define HDMI_CORE_AV_SPD_DBYTE(n) (n * 4 + 0x190) +#define HDMI_CORE_AV_SPD_DBYTE_NELEMS 27 +#define HDMI_CORE_AV_AUD_DBYTE(n) (n * 4 + 0x210) +#define HDMI_CORE_AV_AUD_DBYTE_NELEMS 10 +#define HDMI_CORE_AV_MPEG_DBYTE(n) (n * 4 + 0x290) +#define HDMI_CORE_AV_MPEG_DBYTE_NELEMS 27 +#define HDMI_CORE_AV_GEN_DBYTE(n) (n * 4 + 0x300) +#define HDMI_CORE_AV_GEN_DBYTE_NELEMS 31 +#define HDMI_CORE_AV_GEN2_DBYTE(n) (n * 4 + 0x380) +#define HDMI_CORE_AV_GEN2_DBYTE_NELEMS 31 +#define HDMI_CORE_AV_ACR_CTRL 0x4 +#define HDMI_CORE_AV_FREQ_SVAL 0x8 +#define HDMI_CORE_AV_N_SVAL1 0xC +#define HDMI_CORE_AV_N_SVAL2 0x10 +#define HDMI_CORE_AV_N_SVAL3 0x14 +#define HDMI_CORE_AV_CTS_SVAL1 0x18 +#define HDMI_CORE_AV_CTS_SVAL2 0x1C +#define HDMI_CORE_AV_CTS_SVAL3 0x20 +#define HDMI_CORE_AV_CTS_HVAL1 0x24 +#define HDMI_CORE_AV_CTS_HVAL2 0x28 +#define HDMI_CORE_AV_CTS_HVAL3 0x2C +#define HDMI_CORE_AV_AUD_MODE 0x50 +#define HDMI_CORE_AV_SPDIF_CTRL 0x54 +#define HDMI_CORE_AV_HW_SPDIF_FS 0x60 +#define HDMI_CORE_AV_SWAP_I2S 0x64 +#define HDMI_CORE_AV_SPDIF_ERTH 0x6C +#define HDMI_CORE_AV_I2S_IN_MAP 0x70 +#define HDMI_CORE_AV_I2S_IN_CTRL 0x74 +#define HDMI_CORE_AV_I2S_CHST0 0x78 +#define HDMI_CORE_AV_I2S_CHST1 0x7C +#define HDMI_CORE_AV_I2S_CHST2 0x80 +#define HDMI_CORE_AV_I2S_CHST4 0x84 +#define HDMI_CORE_AV_I2S_CHST5 0x88 +#define HDMI_CORE_AV_ASRC 0x8C +#define HDMI_CORE_AV_I2S_IN_LEN 0x90 +#define HDMI_CORE_AV_HDMI_CTRL 0xBC +#define HDMI_CORE_AV_AUDO_TXSTAT 0xC0 +#define HDMI_CORE_AV_AUD_PAR_BUSCLK_1 0xCC +#define HDMI_CORE_AV_AUD_PAR_BUSCLK_2 0xD0 +#define HDMI_CORE_AV_AUD_PAR_BUSCLK_3 0xD4 +#define HDMI_CORE_AV_TEST_TXCTRL 0xF0 +#define HDMI_CORE_AV_DPD 0xF4 +#define HDMI_CORE_AV_PB_CTRL1 0xF8 +#define HDMI_CORE_AV_PB_CTRL2 0xFC +#define HDMI_CORE_AV_AVI_TYPE 0x100 +#define HDMI_CORE_AV_AVI_VERS 0x104 +#define HDMI_CORE_AV_AVI_LEN 0x108 +#define HDMI_CORE_AV_AVI_CHSUM 0x10C +#define HDMI_CORE_AV_SPD_TYPE 0x180 +#define HDMI_CORE_AV_SPD_VERS 0x184 +#define HDMI_CORE_AV_SPD_LEN 0x188 +#define HDMI_CORE_AV_SPD_CHSUM 0x18C +#define HDMI_CORE_AV_AUDIO_TYPE 0x200 +#define HDMI_CORE_AV_AUDIO_VERS 0x204 +#define HDMI_CORE_AV_AUDIO_LEN 0x208 +#define HDMI_CORE_AV_AUDIO_CHSUM 0x20C +#define HDMI_CORE_AV_MPEG_TYPE 0x280 +#define HDMI_CORE_AV_MPEG_VERS 0x284 +#define HDMI_CORE_AV_MPEG_LEN 0x288 +#define HDMI_CORE_AV_MPEG_CHSUM 0x28C +#define HDMI_CORE_AV_CP_BYTE1 0x37C +#define HDMI_CORE_AV_CEC_ADDR_ID 0x3FC #define HDMI_CORE_AV_SPD_DBYTE_ELSIZE 0x4 #define HDMI_CORE_AV_GEN2_DBYTE_ELSIZE 0x4 #define HDMI_CORE_AV_MPEG_DBYTE_ELSIZE 0x4 #define HDMI_CORE_AV_GEN_DBYTE_ELSIZE 0x4 /* PLL */ -#define HDMI_PLL_REG(idx) HDMI_REG(HDMI_PLLCTRL + idx) -#define PLLCTRL_PLL_CONTROL HDMI_PLL_REG(0x0) -#define PLLCTRL_PLL_STATUS HDMI_PLL_REG(0x4) -#define PLLCTRL_PLL_GO HDMI_PLL_REG(0x8) -#define PLLCTRL_CFG1 HDMI_PLL_REG(0xC) -#define PLLCTRL_CFG2 HDMI_PLL_REG(0x10) -#define PLLCTRL_CFG3 HDMI_PLL_REG(0x14) -#define PLLCTRL_CFG4 HDMI_PLL_REG(0x20) +#define PLLCTRL_PLL_CONTROL 0x0 +#define PLLCTRL_PLL_STATUS 0x4 +#define PLLCTRL_PLL_GO 0x8 +#define PLLCTRL_CFG1 0xC +#define PLLCTRL_CFG2 0x10 +#define PLLCTRL_CFG3 0x14 +#define PLLCTRL_CFG4 0x20 /* HDMI PHY */ -#define HDMI_PHY_REG(idx) HDMI_REG(HDMI_PHY + idx) - -#define HDMI_TXPHY_TX_CTRL HDMI_PHY_REG(0x0) -#define HDMI_TXPHY_DIGITAL_CTRL HDMI_PHY_REG(0x4) -#define HDMI_TXPHY_POWER_CTRL HDMI_PHY_REG(0x8) -#define HDMI_TXPHY_PAD_CFG_CTRL HDMI_PHY_REG(0xC) - -/* HDMI EDID Length */ -#define HDMI_EDID_MAX_LENGTH 256 -#define EDID_TIMING_DESCRIPTOR_SIZE 0x12 -#define EDID_DESCRIPTOR_BLOCK0_ADDRESS 0x36 -#define EDID_DESCRIPTOR_BLOCK1_ADDRESS 0x80 -#define EDID_SIZE_BLOCK0_TIMING_DESCRIPTOR 4 -#define EDID_SIZE_BLOCK1_TIMING_DESCRIPTOR 4 -#define OMAP_HDMI_TIMINGS_NB 34 +#define HDMI_TXPHY_TX_CTRL 0x0 +#define HDMI_TXPHY_DIGITAL_CTRL 0x4 +#define HDMI_TXPHY_POWER_CTRL 0x8 +#define HDMI_TXPHY_PAD_CFG_CTRL 0xC -#define REG_FLD_MOD(idx, val, start, end) \ - hdmi_write_reg(idx, FLD_MOD(hdmi_read_reg(idx), val, start, end)) -#define REG_GET(idx, start, end) \ - FLD_GET(hdmi_read_reg(idx), start, end) - -/* HDMI timing structure */ -struct hdmi_timings { - struct omap_video_timings timings; - int vsync_pol; - int hsync_pol; -}; +#define REG_FLD_MOD(base, idx, val, start, end) \ + hdmi_write_reg(base, idx, FLD_MOD(hdmi_read_reg(base, idx),\ + val, start, end)) +#define REG_GET(base, idx, start, end) \ + FLD_GET(hdmi_read_reg(base, idx), start, end) enum hdmi_phy_pwr { HDMI_PHYPWRCMD_OFF = 0, @@ -221,20 +196,6 @@ enum hdmi_phy_pwr { HDMI_PHYPWRCMD_TXON = 2 }; -enum hdmi_pll_pwr { - HDMI_PLLPWRCMD_ALLOFF = 0, - HDMI_PLLPWRCMD_PLLONLY = 1, - HDMI_PLLPWRCMD_BOTHON_ALLCLKS = 2, - HDMI_PLLPWRCMD_BOTHON_NOPHYCLK = 3 -}; - -enum hdmi_clk_refsel { - HDMI_REFSEL_PCLK = 0, - HDMI_REFSEL_REF1 = 1, - HDMI_REFSEL_REF2 = 2, - HDMI_REFSEL_SYSCLK = 3 -}; - enum hdmi_core_inputbus_width { HDMI_INPUT_8BIT = 0, HDMI_INPUT_10BIT = 1, @@ -263,11 +224,6 @@ enum hdmi_core_packet_mode { HDMI_PACKETMODE48BITPERPIXEL = 7 }; -enum hdmi_core_hdmi_dvi { - HDMI_DVI = 0, - HDMI_HDMI = 1 -}; - enum hdmi_core_tclkselclkmult { HDMI_FPLL05IDCK = 0, HDMI_FPLL10IDCK = 1, @@ -495,40 +451,40 @@ struct hdmi_core_video_config { * details about infoframe databytes */ struct hdmi_core_infoframe_avi { + /* Y0, Y1 rgb,yCbCr */ u8 db1_format; - /* Y0, Y1 rgb,yCbCr */ + /* A0 Active information Present */ u8 db1_active_info; - /* A0 Active information Present */ + /* B0, B1 Bar info data valid */ u8 db1_bar_info_dv; - /* B0, B1 Bar info data valid */ + /* S0, S1 scan information */ u8 db1_scan_info; - /* S0, S1 scan information */ + /* C0, C1 colorimetry */ u8 db2_colorimetry; - /* C0, C1 colorimetry */ + /* M0, M1 Aspect ratio (4:3, 16:9) */ u8 db2_aspect_ratio; - /* M0, M1 Aspect ratio (4:3, 16:9) */ + /* R0...R3 Active format aspect ratio */ u8 db2_active_fmt_ar; - /* R0...R3 Active format aspect ratio */ + /* ITC IT content. */ u8 db3_itc; - /* ITC IT content. */ + /* EC0, EC1, EC2 Extended colorimetry */ u8 db3_ec; - /* EC0, EC1, EC2 Extended colorimetry */ + /* Q1, Q0 Quantization range */ u8 db3_q_range; - /* Q1, Q0 Quantization range */ + /* SC1, SC0 Non-uniform picture scaling */ u8 db3_nup_scaling; - /* SC1, SC0 Non-uniform picture scaling */ + /* VIC0..6 Video format identification */ u8 db4_videocode; - /* VIC0..6 Video format identification */ + /* PR0..PR3 Pixel repetition factor */ u8 db5_pixel_repeat; - /* PR0..PR3 Pixel repetition factor */ + /* Line number end of top bar */ u16 db6_7_line_eoftop; - /* Line number end of top bar */ + /* Line number start of bottom bar */ u16 db8_9_line_sofbottom; - /* Line number start of bottom bar */ + /* Pixel number end of left bar */ u16 db10_11_pixel_eofleft; - /* Pixel number end of left bar */ + /* Pixel number start of right bar */ u16 db12_13_pixel_sofright; - /* Pixel number start of right bar */ }; /* * Refer to section 8.2 in HDMI 1.3 specification for @@ -568,17 +524,6 @@ struct hdmi_video_interface { int tm; /* Timing mode */ }; -struct hdmi_cm { - int code; - int mode; -}; - -struct hdmi_config { - struct hdmi_timings timings; - u16 interlace; - struct hdmi_cm cm; -}; - struct hdmi_audio_format { enum hdmi_stereo_channels stereo_channels; u8 active_chnnls_msk; @@ -628,4 +573,21 @@ struct hdmi_core_audio_config { bool en_parallel_aud_input; bool en_spdif; }; + +#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ + defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) +int hdmi_audio_trigger(struct hdmi_ip_data *ip_data, + struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai); +int hdmi_config_audio_acr(struct hdmi_ip_data *ip_data, + u32 sample_freq, u32 *n, u32 *cts); +void hdmi_core_audio_infoframe_config(struct hdmi_ip_data *ip_data, + struct hdmi_core_infoframe_audio *info_aud); +void hdmi_core_audio_config(struct hdmi_ip_data *ip_data, + struct hdmi_core_audio_config *cfg); +void hdmi_wp_audio_config_dma(struct hdmi_ip_data *ip_data, + struct hdmi_audio_dma *aud_dma); +void hdmi_wp_audio_config_format(struct hdmi_ip_data *ip_data, + struct hdmi_audio_format *aud_fmt); +#endif #endif diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c index 173c66430dad..7533458ba4d2 100644 --- a/drivers/video/omap2/dss/venc.c +++ b/drivers/video/omap2/dss/venc.c @@ -295,7 +295,6 @@ static struct { u32 wss_data; struct regulator *vdda_dac_reg; - struct clk *tv_clk; struct clk *tv_dac_clk; } venc; @@ -464,9 +463,11 @@ static void venc_power_off(struct omap_dss_device *dssdev) regulator_disable(venc.vdda_dac_reg); } - - - +unsigned long venc_get_pixel_clock(void) +{ + /* VENC Pixel Clock in Mhz */ + return 13500000; +} /* driver */ static int venc_panel_probe(struct omap_dss_device *dssdev) @@ -732,22 +733,10 @@ static int venc_get_clocks(struct platform_device *pdev) { struct clk *clk; - clk = clk_get(&pdev->dev, "fck"); - if (IS_ERR(clk)) { - DSSERR("can't get fck\n"); - return PTR_ERR(clk); - } - - venc.tv_clk = clk; - if (dss_has_feature(FEAT_VENC_REQUIRES_TV_DAC_CLK)) { - if (cpu_is_omap34xx() || cpu_is_omap3630()) - clk = clk_get(&pdev->dev, "dss_96m_fck"); - else - clk = clk_get(&pdev->dev, "tv_dac_clk"); + clk = clk_get(&pdev->dev, "tv_dac_clk"); if (IS_ERR(clk)) { DSSERR("can't get tv_dac_clk\n"); - clk_put(venc.tv_clk); return PTR_ERR(clk); } } else { @@ -761,8 +750,6 @@ static int venc_get_clocks(struct platform_device *pdev) static void venc_put_clocks(void) { - if (venc.tv_clk) - clk_put(venc.tv_clk); if (venc.tv_dac_clk) clk_put(venc.tv_dac_clk); } @@ -838,7 +825,6 @@ static int venc_runtime_suspend(struct device *dev) { if (venc.tv_dac_clk) clk_disable(venc.tv_dac_clk); - clk_disable(venc.tv_clk); dispc_runtime_put(); dss_runtime_put(); @@ -858,7 +844,6 @@ static int venc_runtime_resume(struct device *dev) if (r < 0) goto err_get_dispc; - clk_enable(venc.tv_clk); if (venc.tv_dac_clk) clk_enable(venc.tv_dac_clk); diff --git a/drivers/video/omap2/omapfb/Kconfig b/drivers/video/omap2/omapfb/Kconfig index aa33386c81ff..83d3fe7ec9ae 100644 --- a/drivers/video/omap2/omapfb/Kconfig +++ b/drivers/video/omap2/omapfb/Kconfig @@ -1,5 +1,5 @@ menuconfig FB_OMAP2 - tristate "OMAP2+ frame buffer support (EXPERIMENTAL)" + tristate "OMAP2+ frame buffer support" depends on FB && OMAP2_DSS select OMAP2_VRAM diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c index 602b71a92d3c..70aa47de7146 100644 --- a/drivers/video/omap2/omapfb/omapfb-main.c +++ b/drivers/video/omap2/omapfb/omapfb-main.c @@ -808,19 +808,15 @@ static unsigned calc_rotation_offset_vrfb(const struct fb_var_screeninfo *var, static void omapfb_calc_addr(const struct omapfb_info *ofbi, const struct fb_var_screeninfo *var, const struct fb_fix_screeninfo *fix, - int rotation, u32 *paddr, void __iomem **vaddr) + int rotation, u32 *paddr) { u32 data_start_p; - void __iomem *data_start_v; int offset; - if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) data_start_p = omapfb_get_region_rot_paddr(ofbi, rotation); - data_start_v = NULL; - } else { + else data_start_p = omapfb_get_region_paddr(ofbi); - data_start_v = omapfb_get_region_vaddr(ofbi); - } if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) offset = calc_rotation_offset_vrfb(var, fix, rotation); @@ -828,16 +824,14 @@ static void omapfb_calc_addr(const struct omapfb_info *ofbi, offset = calc_rotation_offset_dma(var, fix, rotation); data_start_p += offset; - data_start_v += offset; if (offset) DBG("offset %d, %d = %d\n", var->xoffset, var->yoffset, offset); - DBG("paddr %x, vaddr %p\n", data_start_p, data_start_v); + DBG("paddr %x\n", data_start_p); *paddr = data_start_p; - *vaddr = data_start_v; } /* setup overlay according to the fb */ @@ -850,7 +844,6 @@ int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl, struct fb_fix_screeninfo *fix = &fbi->fix; enum omap_color_mode mode = 0; u32 data_start_p = 0; - void __iomem *data_start_v = NULL; struct omap_overlay_info info; int xres, yres; int screen_width; @@ -880,8 +873,7 @@ int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl, } if (ofbi->region->size) - omapfb_calc_addr(ofbi, var, fix, rotation, - &data_start_p, &data_start_v); + omapfb_calc_addr(ofbi, var, fix, rotation, &data_start_p); r = fb_mode_to_dss_mode(var, &mode); if (r) { @@ -910,7 +902,6 @@ int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl, mirror = ofbi->mirror; info.paddr = data_start_p; - info.vaddr = data_start_v; info.screen_width = screen_width; info.width = xres; info.height = yres; @@ -2276,6 +2267,87 @@ static int omapfb_parse_def_modes(struct omapfb2_device *fbdev) return r; } +static void fb_videomode_to_omap_timings(struct fb_videomode *m, + struct omap_video_timings *t) +{ + t->x_res = m->xres; + t->y_res = m->yres; + t->pixel_clock = PICOS2KHZ(m->pixclock); + t->hsw = m->hsync_len; + t->hfp = m->right_margin; + t->hbp = m->left_margin; + t->vsw = m->vsync_len; + t->vfp = m->lower_margin; + t->vbp = m->upper_margin; +} + +static int omapfb_find_best_mode(struct omap_dss_device *display, + struct omap_video_timings *timings) +{ + struct fb_monspecs *specs; + u8 *edid; + int r, i, best_xres, best_idx, len; + + if (!display->driver->read_edid) + return -ENODEV; + + len = 0x80 * 2; + edid = kmalloc(len, GFP_KERNEL); + + r = display->driver->read_edid(display, edid, len); + if (r < 0) + goto err1; + + specs = kzalloc(sizeof(*specs), GFP_KERNEL); + + fb_edid_to_monspecs(edid, specs); + + if (edid[126] > 0) + fb_edid_add_monspecs(edid + 0x80, specs); + + best_xres = 0; + best_idx = -1; + + for (i = 0; i < specs->modedb_len; ++i) { + struct fb_videomode *m; + struct omap_video_timings t; + + m = &specs->modedb[i]; + + if (m->pixclock == 0) + continue; + + /* skip repeated pixel modes */ + if (m->xres == 2880 || m->xres == 1440) + continue; + + fb_videomode_to_omap_timings(m, &t); + + r = display->driver->check_timings(display, &t); + if (r == 0 && best_xres < m->xres) { + best_xres = m->xres; + best_idx = i; + } + } + + if (best_xres == 0) { + r = -ENOENT; + goto err2; + } + + fb_videomode_to_omap_timings(&specs->modedb[best_idx], timings); + + r = 0; + +err2: + fb_destroy_modedb(specs->modedb); + kfree(specs); +err1: + kfree(edid); + + return r; +} + static int omapfb_init_display(struct omapfb2_device *fbdev, struct omap_dss_device *dssdev) { @@ -2373,8 +2445,10 @@ static int omapfb_probe(struct platform_device *pdev) omap_dss_get_device(dssdev); if (!dssdev->driver) { - dev_err(&pdev->dev, "no driver for display\n"); - r = -ENODEV; + dev_warn(&pdev->dev, "no driver for display: %s\n", + dssdev->name); + omap_dss_put_device(dssdev); + continue; } d = &fbdev->displays[fbdev->num_displays++]; @@ -2402,9 +2476,27 @@ static int omapfb_probe(struct platform_device *pdev) for (i = 0; i < fbdev->num_managers; i++) fbdev->managers[i] = omap_dss_get_overlay_manager(i); + /* gfx overlay should be the default one. find a display + * connected to that, and use it as default display */ + ovl = omap_dss_get_overlay(0); + if (ovl->manager && ovl->manager->device) { + def_display = ovl->manager->device; + } else { + dev_warn(&pdev->dev, "cannot find default display\n"); + def_display = NULL; + } + if (def_mode && strlen(def_mode) > 0) { if (omapfb_parse_def_modes(fbdev)) dev_warn(&pdev->dev, "cannot parse default modes\n"); + } else if (def_display && def_display->driver->set_timings && + def_display->driver->check_timings) { + struct omap_video_timings t; + + r = omapfb_find_best_mode(def_display, &t); + + if (r == 0) + def_display->driver->set_timings(def_display, &t); } r = omapfb_create_framebuffers(fbdev); @@ -2421,16 +2513,6 @@ static int omapfb_probe(struct platform_device *pdev) DBG("mgr->apply'ed\n"); - /* gfx overlay should be the default one. find a display - * connected to that, and use it as default display */ - ovl = omap_dss_get_overlay(0); - if (ovl->manager && ovl->manager->device) { - def_display = ovl->manager->device; - } else { - dev_warn(&pdev->dev, "cannot find default display\n"); - def_display = NULL; - } - if (def_display) { r = omapfb_init_display(fbdev, def_display); if (r) { diff --git a/drivers/video/omap2/omapfb/omapfb-sysfs.c b/drivers/video/omap2/omapfb/omapfb-sysfs.c index 153bf1aceebc..1694d5148f32 100644 --- a/drivers/video/omap2/omapfb/omapfb-sysfs.c +++ b/drivers/video/omap2/omapfb/omapfb-sysfs.c @@ -104,16 +104,14 @@ static ssize_t store_mirror(struct device *dev, { struct fb_info *fbi = dev_get_drvdata(dev); struct omapfb_info *ofbi = FB2OFB(fbi); - int mirror; + bool mirror; int r; struct fb_var_screeninfo new_var; - r = kstrtoint(buf, 0, &mirror); + r = strtobool(buf, &mirror); if (r) return r; - mirror = !!mirror; - if (!lock_fb_info(fbi)) return -ENODEV; diff --git a/drivers/video/platinumfb.c b/drivers/video/platinumfb.c index f27ae16ead2e..ae3caa6755c2 100644 --- a/drivers/video/platinumfb.c +++ b/drivers/video/platinumfb.c @@ -490,7 +490,7 @@ static int platinum_var_to_par(struct fb_var_screeninfo *var, /* - * Parse user speficied options (`video=platinumfb:') + * Parse user specified options (`video=platinumfb:') */ static int __init platinumfb_setup(char *options) { @@ -683,7 +683,7 @@ static struct platform_driver platinum_driver = .of_match_table = platinumfb_match, }, .probe = platinumfb_probe, - .remove = platinumfb_remove, + .remove = __devexit_p(platinumfb_remove), }; static int __init platinumfb_init(void) diff --git a/drivers/video/pm2fb.c b/drivers/video/pm2fb.c index 27f93aab6ddc..dc7bfa91e57a 100644 --- a/drivers/video/pm2fb.c +++ b/drivers/video/pm2fb.c @@ -973,8 +973,8 @@ static int pm2fb_pan_display(struct fb_var_screeninfo *var, { struct pm2fb_par *p = info->par; u32 base; - u32 depth = (var->bits_per_pixel + 7) & ~7; - u32 xres = (var->xres + 31) & ~31; + u32 depth = (info->var.bits_per_pixel + 7) & ~7; + u32 xres = (info->var.xres + 31) & ~31; depth = (depth > 32) ? 32 : depth; base = to3264(var->yoffset * xres + var->xoffset, depth, 1); @@ -1773,7 +1773,7 @@ MODULE_DEVICE_TABLE(pci, pm2fb_id_table); #ifndef MODULE /** - * Parse user speficied options. + * Parse user specified options. * * This is, comma-separated options following `video=pm2fb:'. */ diff --git a/drivers/video/pm3fb.c b/drivers/video/pm3fb.c index 6666f45a2f8c..6632ee5ecb7e 100644 --- a/drivers/video/pm3fb.c +++ b/drivers/video/pm3fb.c @@ -1147,9 +1147,9 @@ static int pm3fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) { struct pm3_par *par = info->par; - const u32 xres = (var->xres + 31) & ~31; + const u32 xres = (info->var.xres + 31) & ~31; - par->base = pm3fb_shift_bpp(var->bits_per_pixel, + par->base = pm3fb_shift_bpp(info->var.bits_per_pixel, (var->yoffset * xres) + var->xoffset); PM3_WAIT(par, 1); @@ -1525,7 +1525,7 @@ static int __init pm3fb_setup(char *options) { char *this_opt; - /* Parse user speficied options (`video=pm3fb:') */ + /* Parse user specified options (`video=pm3fb:') */ if (!options || !*options) return 0; diff --git a/drivers/video/ps3fb.c b/drivers/video/ps3fb.c index 65560a1a0439..213fbbcf613b 100644 --- a/drivers/video/ps3fb.c +++ b/drivers/video/ps3fb.c @@ -1082,7 +1082,7 @@ static int __devinit ps3fb_probe(struct ps3_system_bus_device *dev) } retval = request_irq(ps3fb.irq_no, ps3fb_vsync_interrupt, - IRQF_DISABLED, DEVICE_NAME, &dev->core); + 0, DEVICE_NAME, &dev->core); if (retval) { dev_err(&dev->core, "%s: request_irq failed %d\n", __func__, retval); diff --git a/drivers/video/pxa3xx-gcu.c b/drivers/video/pxa3xx-gcu.c index 0283c7021090..1ed8b366618d 100644 --- a/drivers/video/pxa3xx-gcu.c +++ b/drivers/video/pxa3xx-gcu.c @@ -31,8 +31,6 @@ */ #include <linux/module.h> -#include <linux/version.h> - #include <linux/platform_device.h> #include <linux/dma-mapping.h> #include <linux/miscdevice.h> @@ -678,7 +676,7 @@ pxa3xx_gcu_probe(struct platform_device *dev) } ret = request_irq(irq, pxa3xx_gcu_handle_irq, - IRQF_DISABLED, DRV_NAME, priv); + 0, DRV_NAME, priv); if (ret) { dev_err(&dev->dev, "request_irq failed\n"); ret = -EBUSY; diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c index 0f4e8c942f9e..e89778f4081f 100644 --- a/drivers/video/pxafb.c +++ b/drivers/video/pxafb.c @@ -2191,7 +2191,7 @@ static int __devinit pxafb_probe(struct platform_device *dev) goto failed_free_mem; } - ret = request_irq(irq, pxafb_handle_irq, IRQF_DISABLED, "LCD", fbi); + ret = request_irq(irq, pxafb_handle_irq, 0, "LCD", fbi); if (ret) { dev_err(&dev->dev, "request_irq failed: %d\n", ret); ret = -EBUSY; diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c index 4aecf213c9be..0753b1cfcb8b 100644 --- a/drivers/video/s3c-fb.c +++ b/drivers/video/s3c-fb.c @@ -81,6 +81,7 @@ struct s3c_fb; * @palette: Address of palette memory, or 0 if none. * @has_prtcon: Set if has PRTCON register. * @has_shadowcon: Set if has SHADOWCON register. + * @has_clksel: Set if VIDCON0 register has CLKSEL bit. */ struct s3c_fb_variant { unsigned int is_2443:1; @@ -98,6 +99,7 @@ struct s3c_fb_variant { unsigned int has_prtcon:1; unsigned int has_shadowcon:1; + unsigned int has_clksel:1; }; /** @@ -186,6 +188,7 @@ struct s3c_fb_vsync { * @dev: The device that we bound to, for printing, etc. * @regs_res: The resource we claimed for the IO registers. * @bus_clk: The clk (hclk) feeding our interface and possibly pixclk. + * @lcd_clk: The clk (sclk) feeding pixclk. * @regs: The mapped hardware registers. * @variant: Variant information for this hardware. * @enabled: A bitmask of enabled hardware windows. @@ -200,6 +203,7 @@ struct s3c_fb { struct device *dev; struct resource *regs_res; struct clk *bus_clk; + struct clk *lcd_clk; void __iomem *regs; struct s3c_fb_variant variant; @@ -336,10 +340,15 @@ static int s3c_fb_check_var(struct fb_var_screeninfo *var, */ static int s3c_fb_calc_pixclk(struct s3c_fb *sfb, unsigned int pixclk) { - unsigned long clk = clk_get_rate(sfb->bus_clk); + unsigned long clk; unsigned long long tmp; unsigned int result; + if (sfb->variant.has_clksel) + clk = clk_get_rate(sfb->bus_clk); + else + clk = clk_get_rate(sfb->lcd_clk); + tmp = (unsigned long long)clk; tmp *= pixclk; @@ -883,7 +892,7 @@ static int s3c_fb_pan_display(struct fb_var_screeninfo *var, } } /* Offset in bytes to the end of the displayed area */ - end_boff = start_boff + var->yres * info->fix.line_length; + end_boff = start_boff + info->var.yres * info->fix.line_length; /* Temporarily turn off per-vsync update from shadow registers until * both start and end addresses are updated to prevent corruption */ @@ -1354,13 +1363,24 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) clk_enable(sfb->bus_clk); + if (!sfb->variant.has_clksel) { + sfb->lcd_clk = clk_get(dev, "sclk_fimd"); + if (IS_ERR(sfb->lcd_clk)) { + dev_err(dev, "failed to get lcd clock\n"); + ret = PTR_ERR(sfb->lcd_clk); + goto err_bus_clk; + } + + clk_enable(sfb->lcd_clk); + } + pm_runtime_enable(sfb->dev); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(dev, "failed to find registers\n"); ret = -ENOENT; - goto err_clk; + goto err_lcd_clk; } sfb->regs_res = request_mem_region(res->start, resource_size(res), @@ -1368,7 +1388,7 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) if (!sfb->regs_res) { dev_err(dev, "failed to claim register region\n"); ret = -ENOENT; - goto err_clk; + goto err_lcd_clk; } sfb->regs = ioremap(res->start, resource_size(res)); @@ -1450,7 +1470,13 @@ err_ioremap: err_req_region: release_mem_region(sfb->regs_res->start, resource_size(sfb->regs_res)); -err_clk: +err_lcd_clk: + if (!sfb->variant.has_clksel) { + clk_disable(sfb->lcd_clk); + clk_put(sfb->lcd_clk); + } + +err_bus_clk: clk_disable(sfb->bus_clk); clk_put(sfb->bus_clk); @@ -1481,6 +1507,11 @@ static int __devexit s3c_fb_remove(struct platform_device *pdev) iounmap(sfb->regs); + if (!sfb->variant.has_clksel) { + clk_disable(sfb->lcd_clk); + clk_put(sfb->lcd_clk); + } + clk_disable(sfb->bus_clk); clk_put(sfb->bus_clk); @@ -1510,6 +1541,9 @@ static int s3c_fb_suspend(struct device *dev) s3c_fb_blank(FB_BLANK_POWERDOWN, win->fbinfo); } + if (!sfb->variant.has_clksel) + clk_disable(sfb->lcd_clk); + clk_disable(sfb->bus_clk); return 0; } @@ -1524,6 +1558,9 @@ static int s3c_fb_resume(struct device *dev) clk_enable(sfb->bus_clk); + if (!sfb->variant.has_clksel) + clk_enable(sfb->lcd_clk); + /* setup gpio and output polarity controls */ pd->setup_gpio(); writel(pd->vidcon1, sfb->regs + VIDCON1); @@ -1569,6 +1606,9 @@ static int s3c_fb_runtime_suspend(struct device *dev) s3c_fb_blank(FB_BLANK_POWERDOWN, win->fbinfo); } + if (!sfb->variant.has_clksel) + clk_disable(sfb->lcd_clk); + clk_disable(sfb->bus_clk); return 0; } @@ -1583,6 +1623,9 @@ static int s3c_fb_runtime_resume(struct device *dev) clk_enable(sfb->bus_clk); + if (!sfb->variant.has_clksel) + clk_enable(sfb->lcd_clk); + /* setup gpio and output polarity controls */ pd->setup_gpio(); writel(pd->vidcon1, sfb->regs + VIDCON1); @@ -1755,6 +1798,7 @@ static struct s3c_fb_driverdata s3c_fb_data_64xx = { }, .has_prtcon = 1, + .has_clksel = 1, }, .win[0] = &s3c_fb_data_64xx_wins[0], .win[1] = &s3c_fb_data_64xx_wins[1], @@ -1785,6 +1829,7 @@ static struct s3c_fb_driverdata s3c_fb_data_s5pc100 = { }, .has_prtcon = 1, + .has_clksel = 1, }, .win[0] = &s3c_fb_data_s5p_wins[0], .win[1] = &s3c_fb_data_s5p_wins[1], @@ -1815,6 +1860,37 @@ static struct s3c_fb_driverdata s3c_fb_data_s5pv210 = { }, .has_shadowcon = 1, + .has_clksel = 1, + }, + .win[0] = &s3c_fb_data_s5p_wins[0], + .win[1] = &s3c_fb_data_s5p_wins[1], + .win[2] = &s3c_fb_data_s5p_wins[2], + .win[3] = &s3c_fb_data_s5p_wins[3], + .win[4] = &s3c_fb_data_s5p_wins[4], +}; + +static struct s3c_fb_driverdata s3c_fb_data_exynos4 = { + .variant = { + .nr_windows = 5, + .vidtcon = VIDTCON0, + .wincon = WINCON(0), + .winmap = WINxMAP(0), + .keycon = WKEYCON, + .osd = VIDOSD_BASE, + .osd_stride = 16, + .buf_start = VIDW_BUF_START(0), + .buf_size = VIDW_BUF_SIZE(0), + .buf_end = VIDW_BUF_END(0), + + .palette = { + [0] = 0x2400, + [1] = 0x2800, + [2] = 0x2c00, + [3] = 0x3000, + [4] = 0x3400, + }, + + .has_shadowcon = 1, }, .win[0] = &s3c_fb_data_s5p_wins[0], .win[1] = &s3c_fb_data_s5p_wins[1], @@ -1843,6 +1919,7 @@ static struct s3c_fb_driverdata s3c_fb_data_s3c2443 = { [0] = 0x400, [1] = 0x800, }, + .has_clksel = 1, }, .win[0] = &(struct s3c_fb_win_variant) { .palette_sz = 256, @@ -1859,6 +1936,30 @@ static struct s3c_fb_driverdata s3c_fb_data_s3c2443 = { }, }; +static struct s3c_fb_driverdata s3c_fb_data_s5p64x0 = { + .variant = { + .nr_windows = 3, + .vidtcon = VIDTCON0, + .wincon = WINCON(0), + .winmap = WINxMAP(0), + .keycon = WKEYCON, + .osd = VIDOSD_BASE, + .osd_stride = 16, + .buf_start = VIDW_BUF_START(0), + .buf_size = VIDW_BUF_SIZE(0), + .buf_end = VIDW_BUF_END(0), + + .palette = { + [0] = 0x2400, + [1] = 0x2800, + [2] = 0x2c00, + }, + }, + .win[0] = &s3c_fb_data_s5p_wins[0], + .win[1] = &s3c_fb_data_s5p_wins[1], + .win[2] = &s3c_fb_data_s5p_wins[2], +}; + static struct platform_device_id s3c_fb_driver_ids[] = { { .name = "s3c-fb", @@ -1870,8 +1971,14 @@ static struct platform_device_id s3c_fb_driver_ids[] = { .name = "s5pv210-fb", .driver_data = (unsigned long)&s3c_fb_data_s5pv210, }, { + .name = "exynos4-fb", + .driver_data = (unsigned long)&s3c_fb_data_exynos4, + }, { .name = "s3c2443-fb", .driver_data = (unsigned long)&s3c_fb_data_s3c2443, + }, { + .name = "s5p64x0-fb", + .driver_data = (unsigned long)&s3c_fb_data_s5p64x0, }, {}, }; diff --git a/drivers/video/s3c2410fb.c b/drivers/video/s3c2410fb.c index 0aa13761de6e..ee4c0df217f7 100644 --- a/drivers/video/s3c2410fb.c +++ b/drivers/video/s3c2410fb.c @@ -767,7 +767,6 @@ static irqreturn_t s3c2410fb_irq(int irq, void *dev_id) static int s3c2410fb_cpufreq_transition(struct notifier_block *nb, unsigned long val, void *data) { - struct cpufreq_freqs *freqs = data; struct s3c2410fb_info *info; struct fb_info *fbinfo; long delta_f; @@ -911,7 +910,7 @@ static int __devinit s3c24xxfb_probe(struct platform_device *pdev, for (i = 0; i < 256; i++) info->palette_buffer[i] = PALETTE_BUFF_CLEAR; - ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info); + ret = request_irq(irq, s3c2410fb_irq, 0, pdev->name, info); if (ret) { dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret); ret = -EBUSY; diff --git a/drivers/video/s3fb.c b/drivers/video/s3fb.c index 4ca5d0c8fe84..946a949f4c7d 100644 --- a/drivers/video/s3fb.c +++ b/drivers/video/s3fb.c @@ -1019,12 +1019,13 @@ static int s3fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) unsigned int offset; /* Calculate the offset */ - if (var->bits_per_pixel == 0) { - offset = (var->yoffset / 16) * (var->xres_virtual / 2) + (var->xoffset / 2); + if (info->var.bits_per_pixel == 0) { + offset = (var->yoffset / 16) * (info->var.xres_virtual / 2) + + (var->xoffset / 2); offset = offset >> 2; } else { offset = (var->yoffset * info->fix.line_length) + - (var->xoffset * var->bits_per_pixel / 8); + (var->xoffset * info->var.bits_per_pixel / 8); offset = offset >> 2; } @@ -1504,7 +1505,7 @@ static struct pci_driver s3fb_pci_driver = { .resume = s3_pci_resume, }; -/* Parse user speficied options */ +/* Parse user specified options */ #ifndef MODULE static int __init s3fb_setup(char *options) diff --git a/drivers/video/sa1100fb.c b/drivers/video/sa1100fb.c index e8b76d65a070..98d55d0e2da5 100644 --- a/drivers/video/sa1100fb.c +++ b/drivers/video/sa1100fb.c @@ -1457,8 +1457,7 @@ static int __devinit sa1100fb_probe(struct platform_device *pdev) if (ret) goto failed; - ret = request_irq(irq, sa1100fb_handle_irq, IRQF_DISABLED, - "LCD", fbi); + ret = request_irq(irq, sa1100fb_handle_irq, 0, "LCD", fbi); if (ret) { printk(KERN_ERR "sa1100fb: request_irq failed: %d\n", ret); goto failed; diff --git a/drivers/video/savage/savagefb_driver.c b/drivers/video/savage/savagefb_driver.c index 4de541ca9c52..beb495044b24 100644 --- a/drivers/video/savage/savagefb_driver.c +++ b/drivers/video/savage/savagefb_driver.c @@ -1477,15 +1477,9 @@ static void savagefb_set_par_int(struct savagefb_par *par, struct savage_reg *r vgaHWProtect(par, 0); } -static void savagefb_update_start(struct savagefb_par *par, - struct fb_var_screeninfo *var) +static void savagefb_update_start(struct savagefb_par *par, int base) { - int base; - - base = ((var->yoffset * var->xres_virtual + (var->xoffset & ~1)) - * ((var->bits_per_pixel+7) / 8)) >> 2; - - /* now program the start address registers */ + /* program the start address registers */ vga_out16(0x3d4, (base & 0x00ff00) | 0x0c, par); vga_out16(0x3d4, ((base & 0x00ff) << 8) | 0x0d, par); vga_out8(0x3d4, 0x69, par); @@ -1550,8 +1544,12 @@ static int savagefb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) { struct savagefb_par *par = info->par; + int base; + + base = (var->yoffset * info->fix.line_length + + (var->xoffset & ~1) * ((info->var.bits_per_pixel+7) / 8)) >> 2; - savagefb_update_start(par, var); + savagefb_update_start(par, base); return 0; } diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c index 7d54e2c612f7..647ba984f00f 100644 --- a/drivers/video/sh_mobile_hdmi.c +++ b/drivers/video/sh_mobile_hdmi.c @@ -1111,6 +1111,7 @@ static long sh_hdmi_clk_configure(struct sh_hdmi *hdmi, unsigned long hdmi_rate, static void sh_hdmi_edid_work_fn(struct work_struct *work) { struct sh_hdmi *hdmi = container_of(work, struct sh_hdmi, edid_work.work); + struct fb_info *info; struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; struct sh_mobile_lcdc_chan *ch; int ret; @@ -1123,8 +1124,9 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work) mutex_lock(&hdmi->mutex); + info = hdmi->info; + if (hdmi->hp_state == HDMI_HOTPLUG_CONNECTED) { - struct fb_info *info = hdmi->info; unsigned long parent_rate = 0, hdmi_rate; ret = sh_hdmi_read_edid(hdmi, &hdmi_rate, &parent_rate); @@ -1148,42 +1150,45 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work) ch = info->par; - console_lock(); + if (lock_fb_info(info)) { + console_lock(); - /* HDMI plug in */ - if (!sh_hdmi_must_reconfigure(hdmi) && - info->state == FBINFO_STATE_RUNNING) { - /* - * First activation with the default monitor - just turn - * on, if we run a resume here, the logo disappears - */ - if (lock_fb_info(info)) { + /* HDMI plug in */ + if (!sh_hdmi_must_reconfigure(hdmi) && + info->state == FBINFO_STATE_RUNNING) { + /* + * First activation with the default monitor - just turn + * on, if we run a resume here, the logo disappears + */ info->var.width = hdmi->var.width; info->var.height = hdmi->var.height; sh_hdmi_display_on(hdmi, info); - unlock_fb_info(info); + } else { + /* New monitor or have to wake up */ + fb_set_suspend(info, 0); } - } else { - /* New monitor or have to wake up */ - fb_set_suspend(info, 0); - } - console_unlock(); + console_unlock(); + unlock_fb_info(info); + } } else { ret = 0; - if (!hdmi->info) + if (!info) goto out; hdmi->monspec.modedb_len = 0; fb_destroy_modedb(hdmi->monspec.modedb); hdmi->monspec.modedb = NULL; - console_lock(); + if (lock_fb_info(info)) { + console_lock(); - /* HDMI disconnect */ - fb_set_suspend(hdmi->info, 1); + /* HDMI disconnect */ + fb_set_suspend(info, 1); - console_unlock(); + console_unlock(); + unlock_fb_info(info); + } } out: diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index b048417247e8..3a41c013d031 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c @@ -24,39 +24,14 @@ #include <linux/backlight.h> #include <linux/gpio.h> #include <video/sh_mobile_lcdc.h> +#include <video/sh_mobile_meram.h> #include <linux/atomic.h> #include "sh_mobile_lcdcfb.h" -#include "sh_mobile_meram.h" #define SIDE_B_OFFSET 0x1000 #define MIRROR_OFFSET 0x2000 -/* shared registers */ -#define _LDDCKR 0x410 -#define _LDDCKSTPR 0x414 -#define _LDINTR 0x468 -#define _LDSR 0x46c -#define _LDCNT1R 0x470 -#define _LDCNT2R 0x474 -#define _LDRCNTR 0x478 -#define _LDDDSR 0x47c -#define _LDDWD0R 0x800 -#define _LDDRDR 0x840 -#define _LDDWAR 0x900 -#define _LDDRAR 0x904 - -/* shared registers and their order for context save/restore */ -static int lcdc_shared_regs[] = { - _LDDCKR, - _LDDCKSTPR, - _LDINTR, - _LDDDSR, - _LDCNT1R, - _LDCNT2R, -}; -#define NR_SHARED_REGS ARRAY_SIZE(lcdc_shared_regs) - #define MAX_XRES 1920 #define MAX_YRES 1080 @@ -98,22 +73,6 @@ static unsigned long lcdc_offs_sublcd[NR_CH_REGS] = { [LDPMR] = 0x63c, }; -#define START_LCDC 0x00000001 -#define LCDC_RESET 0x00000100 -#define DISPLAY_BEU 0x00000008 -#define LCDC_ENABLE 0x00000001 -#define LDINTR_FE 0x00000400 -#define LDINTR_VSE 0x00000200 -#define LDINTR_VEE 0x00000100 -#define LDINTR_FS 0x00000004 -#define LDINTR_VSS 0x00000002 -#define LDINTR_VES 0x00000001 -#define LDRCNTR_SRS 0x00020000 -#define LDRCNTR_SRC 0x00010000 -#define LDRCNTR_MRS 0x00000002 -#define LDRCNTR_MRC 0x00000001 -#define LDSR_MRS 0x00000100 - static const struct fb_videomode default_720p = { .name = "HDMI 720p", .xres = 1280, @@ -141,7 +100,6 @@ struct sh_mobile_lcdc_priv { unsigned long lddckr; struct sh_mobile_lcdc_chan ch[2]; struct notifier_block notifier; - unsigned long saved_shared_regs[NR_SHARED_REGS]; int started; int forced_bpp; /* 2 channel LCDC must share bpp setting */ struct sh_mobile_meram_info *meram_dev; @@ -218,33 +176,36 @@ static void lcdc_sys_write_index(void *handle, unsigned long data) { struct sh_mobile_lcdc_chan *ch = handle; - lcdc_write(ch->lcdc, _LDDWD0R, data | 0x10000000); - lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); - lcdc_write(ch->lcdc, _LDDWAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0)); - lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); + lcdc_write(ch->lcdc, _LDDWD0R, data | LDDWDxR_WDACT); + lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0); + lcdc_write(ch->lcdc, _LDDWAR, LDDWAR_WA | + (lcdc_chan_is_sublcd(ch) ? 2 : 0)); + lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0); } static void lcdc_sys_write_data(void *handle, unsigned long data) { struct sh_mobile_lcdc_chan *ch = handle; - lcdc_write(ch->lcdc, _LDDWD0R, data | 0x11000000); - lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); - lcdc_write(ch->lcdc, _LDDWAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0)); - lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); + lcdc_write(ch->lcdc, _LDDWD0R, data | LDDWDxR_WDACT | LDDWDxR_RSW); + lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0); + lcdc_write(ch->lcdc, _LDDWAR, LDDWAR_WA | + (lcdc_chan_is_sublcd(ch) ? 2 : 0)); + lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0); } static unsigned long lcdc_sys_read_data(void *handle) { struct sh_mobile_lcdc_chan *ch = handle; - lcdc_write(ch->lcdc, _LDDRDR, 0x01000000); - lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); - lcdc_write(ch->lcdc, _LDDRAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0)); + lcdc_write(ch->lcdc, _LDDRDR, LDDRDR_RSR); + lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0); + lcdc_write(ch->lcdc, _LDDRAR, LDDRAR_RA | + (lcdc_chan_is_sublcd(ch) ? 2 : 0)); udelay(1); - lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); + lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0); - return lcdc_read(ch->lcdc, _LDDRDR) & 0x3ffff; + return lcdc_read(ch->lcdc, _LDDRDR) & LDDRDR_DRD_MASK; } struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = { @@ -256,18 +217,22 @@ struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = { static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv) { if (atomic_inc_and_test(&priv->hw_usecnt)) { - pm_runtime_get_sync(priv->dev); if (priv->dot_clk) clk_enable(priv->dot_clk); + pm_runtime_get_sync(priv->dev); + if (priv->meram_dev && priv->meram_dev->pdev) + pm_runtime_get_sync(&priv->meram_dev->pdev->dev); } } static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv) { if (atomic_sub_return(1, &priv->hw_usecnt) == -1) { + if (priv->meram_dev && priv->meram_dev->pdev) + pm_runtime_put_sync(&priv->meram_dev->pdev->dev); + pm_runtime_put(priv->dev); if (priv->dot_clk) clk_disable(priv->dot_clk); - pm_runtime_put(priv->dev); } } @@ -319,13 +284,13 @@ static void sh_mobile_lcdc_deferred_io(struct fb_info *info, if (bcfg->start_transfer) bcfg->start_transfer(bcfg->board_data, ch, &sh_mobile_lcdc_sys_bus_ops); - lcdc_write_chan(ch, LDSM2R, 1); + lcdc_write_chan(ch, LDSM2R, LDSM2R_OSTRG); dma_unmap_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); } else { if (bcfg->start_transfer) bcfg->start_transfer(bcfg->board_data, ch, &sh_mobile_lcdc_sys_bus_ops); - lcdc_write_chan(ch, LDSM2R, 1); + lcdc_write_chan(ch, LDSM2R, LDSM2R_OSTRG); } } @@ -341,22 +306,16 @@ static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data) { struct sh_mobile_lcdc_priv *priv = data; struct sh_mobile_lcdc_chan *ch; - unsigned long tmp; unsigned long ldintr; int is_sub; int k; - /* acknowledge interrupt */ - ldintr = tmp = lcdc_read(priv, _LDINTR); - /* - * disable further VSYNC End IRQs, preserve all other enabled IRQs, - * write 0 to bits 0-6 to ack all triggered IRQs. - */ - tmp &= 0xffffff00 & ~LDINTR_VEE; - lcdc_write(priv, _LDINTR, tmp); + /* Acknowledge interrupts and disable further VSYNC End IRQs. */ + ldintr = lcdc_read(priv, _LDINTR); + lcdc_write(priv, _LDINTR, (ldintr ^ LDINTR_STATUS_MASK) & ~LDINTR_VEE); /* figure out if this interrupt is for main or sub lcd */ - is_sub = (lcdc_read(priv, _LDSR) & (1 << 10)) ? 1 : 0; + is_sub = (lcdc_read(priv, _LDSR) & LDSR_MSS) ? 1 : 0; /* wake up channel and disable clocks */ for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { @@ -365,7 +324,7 @@ static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data) if (!ch->enabled) continue; - /* Frame Start */ + /* Frame End */ if (ldintr & LDINTR_FS) { if (is_sub == lcdc_chan_is_sublcd(ch)) { ch->frame_end = 1; @@ -391,16 +350,17 @@ static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv, /* start or stop the lcdc */ if (start) - lcdc_write(priv, _LDCNT2R, tmp | START_LCDC); + lcdc_write(priv, _LDCNT2R, tmp | LDCNT2R_DO); else - lcdc_write(priv, _LDCNT2R, tmp & ~START_LCDC); + lcdc_write(priv, _LDCNT2R, tmp & ~LDCNT2R_DO); /* wait until power is applied/stopped on all channels */ for (k = 0; k < ARRAY_SIZE(priv->ch); k++) if (lcdc_read(priv, _LDCNT2R) & priv->ch[k].enabled) while (1) { - tmp = lcdc_read_chan(&priv->ch[k], LDPMR) & 3; - if (start && tmp == 3) + tmp = lcdc_read_chan(&priv->ch[k], LDPMR) + & LDPMR_LPS; + if (start && tmp == LDPMR_LPS) break; if (!start && tmp == 0) break; @@ -418,13 +378,13 @@ static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch) u32 tmp; tmp = ch->ldmt1r_value; - tmp |= (var->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 1 << 28; - tmp |= (var->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 1 << 27; - tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? 1 << 26 : 0; - tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? 1 << 25 : 0; - tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? 1 << 24 : 0; - tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? 1 << 17 : 0; - tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? 1 << 16 : 0; + tmp |= (var->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : LDMT1R_VPOL; + tmp |= (var->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : LDMT1R_HPOL; + tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? LDMT1R_DWPOL : 0; + tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? LDMT1R_DIPOL : 0; + tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? LDMT1R_DAPOL : 0; + tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? LDMT1R_HSCNT : 0; + tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? LDMT1R_DWCNT : 0; lcdc_write_chan(ch, LDMT1R, tmp); /* setup SYS bus */ @@ -463,242 +423,239 @@ static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch) lcdc_write_chan(ch, LDHAJR, tmp); } -static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) +/* + * __sh_mobile_lcdc_start - Configure and tart the LCDC + * @priv: LCDC device + * + * Configure all enabled channels and start the LCDC device. All external + * devices (clocks, MERAM, panels, ...) are not touched by this function. + */ +static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) { struct sh_mobile_lcdc_chan *ch; - struct sh_mobile_lcdc_board_cfg *board_cfg; unsigned long tmp; int bpp = 0; - unsigned long ldddsr; - int k, m, ret; + int k, m; - /* enable clocks before accessing the hardware */ - for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { - if (priv->ch[k].enabled) { - sh_mobile_lcdc_clk_on(priv); - if (!bpp) - bpp = priv->ch[k].info->var.bits_per_pixel; - } - } - - /* reset */ - lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LCDC_RESET); - lcdc_wait_bit(priv, _LDCNT2R, LCDC_RESET, 0); - - /* enable LCDC channels */ - tmp = lcdc_read(priv, _LDCNT2R); - tmp |= priv->ch[0].enabled; - tmp |= priv->ch[1].enabled; - lcdc_write(priv, _LDCNT2R, tmp); - - /* read data from external memory, avoid using the BEU for now */ - lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) & ~DISPLAY_BEU); + /* Enable LCDC channels. Read data from external memory, avoid using the + * BEU for now. + */ + lcdc_write(priv, _LDCNT2R, priv->ch[0].enabled | priv->ch[1].enabled); - /* stop the lcdc first */ + /* Stop the LCDC first and disable all interrupts. */ sh_mobile_lcdc_start_stop(priv, 0); + lcdc_write(priv, _LDINTR, 0); - /* configure clocks */ + /* Configure power supply, dot clocks and start them. */ tmp = priv->lddckr; for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { ch = &priv->ch[k]; - - if (!priv->ch[k].enabled) + if (!ch->enabled) continue; + if (!bpp) + bpp = ch->info->var.bits_per_pixel; + + /* Power supply */ + lcdc_write_chan(ch, LDPMR, 0); + m = ch->cfg.clock_divider; if (!m) continue; - if (m == 1) - m = 1 << 6; - tmp |= m << (lcdc_chan_is_sublcd(ch) ? 8 : 0); - - /* FIXME: sh7724 can only use 42, 48, 54 and 60 for the divider denominator */ + /* FIXME: sh7724 can only use 42, 48, 54 and 60 for the divider + * denominator. + */ lcdc_write_chan(ch, LDDCKPAT1R, 0); lcdc_write_chan(ch, LDDCKPAT2R, (1 << (m/2)) - 1); + + if (m == 1) + m = LDDCKR_MOSEL; + tmp |= m << (lcdc_chan_is_sublcd(ch) ? 8 : 0); } lcdc_write(priv, _LDDCKR, tmp); - - /* start dotclock again */ lcdc_write(priv, _LDDCKSTPR, 0); lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0); - /* interrupts are disabled to begin with */ - lcdc_write(priv, _LDINTR, 0); - + /* Setup geometry, format, frame buffer memory and operation mode. */ for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { ch = &priv->ch[k]; - if (!ch->enabled) continue; sh_mobile_lcdc_geometry(ch); - /* power supply */ - lcdc_write_chan(ch, LDPMR, 0); - - board_cfg = &ch->cfg.board_cfg; - if (board_cfg->setup_sys) { - ret = board_cfg->setup_sys(board_cfg->board_data, - ch, &sh_mobile_lcdc_sys_bus_ops); - if (ret) - return ret; - } - } - - /* word and long word swap */ - ldddsr = lcdc_read(priv, _LDDDSR); - if (priv->ch[0].info->var.nonstd) - lcdc_write(priv, _LDDDSR, ldddsr | 7); - else { - switch (bpp) { - case 16: - lcdc_write(priv, _LDDDSR, ldddsr | 6); - break; - case 24: - lcdc_write(priv, _LDDDSR, ldddsr | 7); - break; - case 32: - lcdc_write(priv, _LDDDSR, ldddsr | 4); - break; - } - } - - for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { - unsigned long base_addr_y; - unsigned long base_addr_c = 0; - int pitch; - ch = &priv->ch[k]; - - if (!priv->ch[k].enabled) - continue; - - /* set bpp format in PKF[4:0] */ - tmp = lcdc_read_chan(ch, LDDFR); - tmp &= ~0x0003031f; if (ch->info->var.nonstd) { - tmp |= (ch->info->var.nonstd << 16); + tmp = (ch->info->var.nonstd << 16); switch (ch->info->var.bits_per_pixel) { case 12: + tmp |= LDDFR_YF_420; break; case 16: - tmp |= (0x1 << 8); + tmp |= LDDFR_YF_422; break; case 24: - tmp |= (0x2 << 8); + default: + tmp |= LDDFR_YF_444; break; } } else { switch (ch->info->var.bits_per_pixel) { case 16: - tmp |= 0x03; + tmp = LDDFR_PKF_RGB16; break; case 24: - tmp |= 0x0b; + tmp = LDDFR_PKF_RGB24; break; case 32: + default: + tmp = LDDFR_PKF_ARGB32; break; } } + lcdc_write_chan(ch, LDDFR, tmp); + lcdc_write_chan(ch, LDMLSR, ch->pitch); + lcdc_write_chan(ch, LDSA1R, ch->base_addr_y); + if (ch->info->var.nonstd) + lcdc_write_chan(ch, LDSA2R, ch->base_addr_c); - base_addr_y = ch->info->fix.smem_start; - base_addr_c = base_addr_y + - ch->info->var.xres * - ch->info->var.yres_virtual; - pitch = ch->info->fix.line_length; + /* When using deferred I/O mode, configure the LCDC for one-shot + * operation and enable the frame end interrupt. Otherwise use + * continuous read mode. + */ + if (ch->ldmt1r_value & LDMT1R_IFM && + ch->cfg.sys_bus_cfg.deferred_io_msec) { + lcdc_write_chan(ch, LDSM1R, LDSM1R_OS); + lcdc_write(priv, _LDINTR, LDINTR_FE); + } else { + lcdc_write_chan(ch, LDSM1R, 0); + } + } - /* test if we can enable meram */ - if (ch->cfg.meram_cfg && priv->meram_dev && - priv->meram_dev->ops) { - struct sh_mobile_meram_cfg *cfg; - struct sh_mobile_meram_info *mdev; - unsigned long icb_addr_y, icb_addr_c; - int icb_pitch; - int pf; + /* Word and long word swap. */ + if (priv->ch[0].info->var.nonstd) + tmp = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS; + else { + switch (bpp) { + case 16: + tmp = LDDDSR_LS | LDDDSR_WS; + break; + case 24: + tmp = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS; + break; + case 32: + default: + tmp = LDDDSR_LS; + break; + } + } + lcdc_write(priv, _LDDDSR, tmp); - cfg = ch->cfg.meram_cfg; - mdev = priv->meram_dev; - /* we need to de-init configured ICBs before we - * we can re-initialize them. - */ - if (ch->meram_enabled) - mdev->ops->meram_unregister(mdev, cfg); + /* Enable the display output. */ + lcdc_write(priv, _LDCNT1R, LDCNT1R_DE); + sh_mobile_lcdc_start_stop(priv, 1); + priv->started = 1; +} - ch->meram_enabled = 0; +static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) +{ + struct sh_mobile_meram_info *mdev = priv->meram_dev; + struct sh_mobile_lcdc_board_cfg *board_cfg; + struct sh_mobile_lcdc_chan *ch; + unsigned long tmp; + int ret; + int k; - if (ch->info->var.nonstd) { - if (ch->info->var.bits_per_pixel == 24) - pf = SH_MOBILE_MERAM_PF_NV24; - else - pf = SH_MOBILE_MERAM_PF_NV; - } else { - pf = SH_MOBILE_MERAM_PF_RGB; - } + /* enable clocks before accessing the hardware */ + for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { + if (priv->ch[k].enabled) + sh_mobile_lcdc_clk_on(priv); + } - ret = mdev->ops->meram_register(mdev, cfg, pitch, - ch->info->var.yres, - pf, - base_addr_y, - base_addr_c, - &icb_addr_y, - &icb_addr_c, - &icb_pitch); - if (!ret) { - /* set LDSA1R value */ - base_addr_y = icb_addr_y; - pitch = icb_pitch; - - /* set LDSA2R value if required */ - if (base_addr_c) - base_addr_c = icb_addr_c; - - ch->meram_enabled = 1; - } - } + /* reset */ + lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LDCNT2R_BR); + lcdc_wait_bit(priv, _LDCNT2R, LDCNT2R_BR, 0); - /* point out our frame buffer */ - lcdc_write_chan(ch, LDSA1R, base_addr_y); - if (ch->info->var.nonstd) - lcdc_write_chan(ch, LDSA2R, base_addr_c); + for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { + ch = &priv->ch[k]; - /* set line size */ - lcdc_write_chan(ch, LDMLSR, pitch); + if (!ch->enabled) + continue; - /* setup deferred io if SYS bus */ - tmp = ch->cfg.sys_bus_cfg.deferred_io_msec; - if (ch->ldmt1r_value & (1 << 12) && tmp) { - ch->defio.deferred_io = sh_mobile_lcdc_deferred_io; - ch->defio.delay = msecs_to_jiffies(tmp); - ch->info->fbdefio = &ch->defio; - fb_deferred_io_init(ch->info); + board_cfg = &ch->cfg.board_cfg; + if (board_cfg->setup_sys) { + ret = board_cfg->setup_sys(board_cfg->board_data, ch, + &sh_mobile_lcdc_sys_bus_ops); + if (ret) + return ret; + } + } - /* one-shot mode */ - lcdc_write_chan(ch, LDSM1R, 1); + /* Compute frame buffer base address and pitch for each channel. */ + for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { + struct sh_mobile_meram_cfg *cfg; + int pixelformat; - /* enable "Frame End Interrupt Enable" bit */ - lcdc_write(priv, _LDINTR, LDINTR_FE); + ch = &priv->ch[k]; + if (!ch->enabled) + continue; - } else { - /* continuous read mode */ - lcdc_write_chan(ch, LDSM1R, 0); + ch->base_addr_y = ch->info->fix.smem_start; + ch->base_addr_c = ch->base_addr_y + + ch->info->var.xres + * ch->info->var.yres_virtual; + ch->pitch = ch->info->fix.line_length; + + /* Enable MERAM if possible. */ + cfg = ch->cfg.meram_cfg; + if (mdev == NULL || mdev->ops == NULL || cfg == NULL) + continue; + + /* we need to de-init configured ICBs before we can + * re-initialize them. + */ + if (ch->meram_enabled) { + mdev->ops->meram_unregister(mdev, cfg); + ch->meram_enabled = 0; } + + if (!ch->info->var.nonstd) + pixelformat = SH_MOBILE_MERAM_PF_RGB; + else if (ch->info->var.bits_per_pixel == 24) + pixelformat = SH_MOBILE_MERAM_PF_NV24; + else + pixelformat = SH_MOBILE_MERAM_PF_NV; + + ret = mdev->ops->meram_register(mdev, cfg, ch->pitch, + ch->info->var.yres, pixelformat, + ch->base_addr_y, ch->base_addr_c, + &ch->base_addr_y, &ch->base_addr_c, + &ch->pitch); + if (!ret) + ch->meram_enabled = 1; } - /* display output */ - lcdc_write(priv, _LDCNT1R, LCDC_ENABLE); + /* Start the LCDC. */ + __sh_mobile_lcdc_start(priv); - /* start the lcdc */ - sh_mobile_lcdc_start_stop(priv, 1); - priv->started = 1; - - /* tell the board code to enable the panel */ + /* Setup deferred I/O, tell the board code to enable the panels, and + * turn backlight on. + */ for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { ch = &priv->ch[k]; if (!ch->enabled) continue; + tmp = ch->cfg.sys_bus_cfg.deferred_io_msec; + if (ch->ldmt1r_value & LDMT1R_IFM && tmp) { + ch->defio.deferred_io = sh_mobile_lcdc_deferred_io; + ch->defio.delay = msecs_to_jiffies(tmp); + ch->info->fbdefio = &ch->defio; + fb_deferred_io_init(ch->info); + } + board_cfg = &ch->cfg.board_cfg; if (board_cfg->display_on && try_module_get(board_cfg->owner)) { board_cfg->display_on(board_cfg->board_data, ch->info); @@ -776,42 +733,42 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv) static int sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch) { - int ifm, miftyp; - - switch (ch->cfg.interface_type) { - case RGB8: ifm = 0; miftyp = 0; break; - case RGB9: ifm = 0; miftyp = 4; break; - case RGB12A: ifm = 0; miftyp = 5; break; - case RGB12B: ifm = 0; miftyp = 6; break; - case RGB16: ifm = 0; miftyp = 7; break; - case RGB18: ifm = 0; miftyp = 10; break; - case RGB24: ifm = 0; miftyp = 11; break; - case SYS8A: ifm = 1; miftyp = 0; break; - case SYS8B: ifm = 1; miftyp = 1; break; - case SYS8C: ifm = 1; miftyp = 2; break; - case SYS8D: ifm = 1; miftyp = 3; break; - case SYS9: ifm = 1; miftyp = 4; break; - case SYS12: ifm = 1; miftyp = 5; break; - case SYS16A: ifm = 1; miftyp = 7; break; - case SYS16B: ifm = 1; miftyp = 8; break; - case SYS16C: ifm = 1; miftyp = 9; break; - case SYS18: ifm = 1; miftyp = 10; break; - case SYS24: ifm = 1; miftyp = 11; break; - default: goto bad; + int interface_type = ch->cfg.interface_type; + + switch (interface_type) { + case RGB8: + case RGB9: + case RGB12A: + case RGB12B: + case RGB16: + case RGB18: + case RGB24: + case SYS8A: + case SYS8B: + case SYS8C: + case SYS8D: + case SYS9: + case SYS12: + case SYS16A: + case SYS16B: + case SYS16C: + case SYS18: + case SYS24: + break; + default: + return -EINVAL; } /* SUBLCD only supports SYS interface */ if (lcdc_chan_is_sublcd(ch)) { - if (ifm == 0) - goto bad; - else - ifm = 0; + if (!(interface_type & LDMT1R_IFM)) + return -EINVAL; + + interface_type &= ~LDMT1R_IFM; } - ch->ldmt1r_value = (ifm << 12) | miftyp; + ch->ldmt1r_value = interface_type; return 0; - bad: - return -EINVAL; } static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev, @@ -819,18 +776,24 @@ static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev, struct sh_mobile_lcdc_priv *priv) { char *str; - int icksel; switch (clock_source) { - case LCDC_CLK_BUS: str = "bus_clk"; icksel = 0; break; - case LCDC_CLK_PERIPHERAL: str = "peripheral_clk"; icksel = 1; break; - case LCDC_CLK_EXTERNAL: str = NULL; icksel = 2; break; + case LCDC_CLK_BUS: + str = "bus_clk"; + priv->lddckr = LDDCKR_ICKSEL_BUS; + break; + case LCDC_CLK_PERIPHERAL: + str = "peripheral_clk"; + priv->lddckr = LDDCKR_ICKSEL_MIPI; + break; + case LCDC_CLK_EXTERNAL: + str = NULL; + priv->lddckr = LDDCKR_ICKSEL_HDMI; + break; default: return -EINVAL; } - priv->lddckr = icksel << 16; - if (str) { priv->dot_clk = clk_get(&pdev->dev, str); if (IS_ERR(priv->dot_clk)) { @@ -914,12 +877,12 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var, unsigned long base_addr_y, base_addr_c; unsigned long c_offset; - if (!var->nonstd) - new_pan_offset = (var->yoffset * info->fix.line_length) + - (var->xoffset * (info->var.bits_per_pixel / 8)); + if (!info->var.nonstd) + new_pan_offset = var->yoffset * info->fix.line_length + + var->xoffset * (info->var.bits_per_pixel / 8); else - new_pan_offset = (var->yoffset * info->fix.line_length) + - (var->xoffset); + new_pan_offset = var->yoffset * info->fix.line_length + + var->xoffset; if (new_pan_offset == ch->pan_offset) return 0; /* No change, do nothing */ @@ -928,44 +891,40 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var, /* Set the source address for the next refresh */ base_addr_y = ch->dma_handle + new_pan_offset; - if (var->nonstd) { + if (info->var.nonstd) { /* Set y offset */ - c_offset = (var->yoffset * - info->fix.line_length * - (info->var.bits_per_pixel - 8)) / 8; - base_addr_c = ch->dma_handle + var->xres * var->yres_virtual + - c_offset; + c_offset = var->yoffset * info->fix.line_length + * (info->var.bits_per_pixel - 8) / 8; + base_addr_c = ch->dma_handle + + info->var.xres * info->var.yres_virtual + + c_offset; /* Set x offset */ if (info->var.bits_per_pixel == 24) base_addr_c += 2 * var->xoffset; else base_addr_c += var->xoffset; - } else - base_addr_c = 0; + } - if (!ch->meram_enabled) { - lcdc_write_chan_mirror(ch, LDSA1R, base_addr_y); - if (base_addr_c) - lcdc_write_chan_mirror(ch, LDSA2R, base_addr_c); - } else { + if (ch->meram_enabled) { struct sh_mobile_meram_cfg *cfg; struct sh_mobile_meram_info *mdev; - unsigned long icb_addr_y, icb_addr_c; int ret; cfg = ch->cfg.meram_cfg; mdev = priv->meram_dev; ret = mdev->ops->meram_update(mdev, cfg, base_addr_y, base_addr_c, - &icb_addr_y, &icb_addr_c); + &base_addr_y, &base_addr_c); if (ret) return ret; + } - lcdc_write_chan_mirror(ch, LDSA1R, icb_addr_y); - if (icb_addr_c) - lcdc_write_chan_mirror(ch, LDSA2R, icb_addr_c); + ch->base_addr_y = base_addr_y; + ch->base_addr_c = base_addr_c; - } + lcdc_write_chan_mirror(ch, LDSA1R, base_addr_y); + if (info->var.nonstd) + lcdc_write_chan_mirror(ch, LDSA2R, base_addr_c); if (lcdc_chan_is_sublcd(ch)) lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_SRS); @@ -985,9 +944,11 @@ static int sh_mobile_wait_for_vsync(struct fb_info *info) unsigned long ldintr; int ret; - /* Enable VSync End interrupt */ + /* Enable VSync End interrupt and be careful not to acknowledge any + * pending interrupt. + */ ldintr = lcdc_read(ch->lcdc, _LDINTR); - ldintr |= LDINTR_VEE; + ldintr |= LDINTR_VEE | LDINTR_STATUS_MASK; lcdc_write(ch->lcdc, _LDINTR, ldintr); ret = wait_for_completion_interruptible_timeout(&ch->vsync_completion, @@ -1037,11 +998,6 @@ static void sh_mobile_fb_reconfig(struct fb_info *info) /* Couldn't reconfigure, hopefully, can continue as before */ return; - if (info->var.nonstd) - info->fix.line_length = mode1.xres; - else - info->fix.line_length = mode1.xres * (ch->cfg.bpp / 8); - /* * fb_set_var() calls the notifier change internally, only if * FBINFO_MISC_USEREVENT flag is set. Since we do not want to fake a @@ -1094,30 +1050,126 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in { struct sh_mobile_lcdc_chan *ch = info->par; struct sh_mobile_lcdc_priv *p = ch->lcdc; + unsigned int best_dist = (unsigned int)-1; + unsigned int best_xres = 0; + unsigned int best_yres = 0; + unsigned int i; - if (var->xres > MAX_XRES || var->yres > MAX_YRES || - var->xres * var->yres * (ch->cfg.bpp / 8) * 2 > info->fix.smem_len) { - dev_warn(info->dev, "Invalid info: %u-%u-%u-%u x %u-%u-%u-%u @ %lukHz!\n", - var->left_margin, var->xres, var->right_margin, var->hsync_len, - var->upper_margin, var->yres, var->lower_margin, var->vsync_len, - PICOS2KHZ(var->pixclock)); + if (var->xres > MAX_XRES || var->yres > MAX_YRES) return -EINVAL; + + /* If board code provides us with a list of available modes, make sure + * we use one of them. Find the mode closest to the requested one. The + * distance between two modes is defined as the size of the + * non-overlapping parts of the two rectangles. + */ + for (i = 0; i < ch->cfg.num_cfg; ++i) { + const struct fb_videomode *mode = &ch->cfg.lcd_cfg[i]; + unsigned int dist; + + /* We can only round up. */ + if (var->xres > mode->xres || var->yres > mode->yres) + continue; + + dist = var->xres * var->yres + mode->xres * mode->yres + - 2 * min(var->xres, mode->xres) + * min(var->yres, mode->yres); + + if (dist < best_dist) { + best_xres = mode->xres; + best_yres = mode->yres; + best_dist = dist; + } + } + + /* If no available mode can be used, return an error. */ + if (ch->cfg.num_cfg != 0) { + if (best_dist == (unsigned int)-1) + return -EINVAL; + + var->xres = best_xres; + var->yres = best_yres; } + /* Make sure the virtual resolution is at least as big as the visible + * resolution. + */ + if (var->xres_virtual < var->xres) + var->xres_virtual = var->xres; + if (var->yres_virtual < var->yres) + var->yres_virtual = var->yres; + + if (var->bits_per_pixel <= 16) { /* RGB 565 */ + var->bits_per_pixel = 16; + var->red.offset = 11; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 6; + var->blue.offset = 0; + var->blue.length = 5; + var->transp.offset = 0; + var->transp.length = 0; + } else if (var->bits_per_pixel <= 24) { /* RGB 888 */ + var->bits_per_pixel = 24; + var->red.offset = 16; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + var->transp.offset = 0; + var->transp.length = 0; + } else if (var->bits_per_pixel <= 32) { /* RGBA 888 */ + var->bits_per_pixel = 32; + var->red.offset = 16; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + var->transp.offset = 24; + var->transp.length = 8; + } else + return -EINVAL; + + var->red.msb_right = 0; + var->green.msb_right = 0; + var->blue.msb_right = 0; + var->transp.msb_right = 0; + + /* Make sure we don't exceed our allocated memory. */ + if (var->xres_virtual * var->yres_virtual * var->bits_per_pixel / 8 > + info->fix.smem_len) + return -EINVAL; + /* only accept the forced_bpp for dual channel configurations */ if (p->forced_bpp && p->forced_bpp != var->bits_per_pixel) return -EINVAL; - switch (var->bits_per_pixel) { - case 16: /* PKF[4:0] = 00011 - RGB 565 */ - case 24: /* PKF[4:0] = 01011 - RGB 888 */ - case 32: /* PKF[4:0] = 00000 - RGBA 888 */ - break; - default: - return -EINVAL; + return 0; +} + +static int sh_mobile_set_par(struct fb_info *info) +{ + struct sh_mobile_lcdc_chan *ch = info->par; + u32 line_length = info->fix.line_length; + int ret; + + sh_mobile_lcdc_stop(ch->lcdc); + + if (info->var.nonstd) + info->fix.line_length = info->var.xres; + else + info->fix.line_length = info->var.xres + * info->var.bits_per_pixel / 8; + + ret = sh_mobile_lcdc_start(ch->lcdc); + if (ret < 0) { + dev_err(info->dev, "%s: unable to restart LCDC\n", __func__); + info->fix.line_length = line_length; } - return 0; + return ret; } /* @@ -1177,6 +1229,7 @@ static struct fb_ops sh_mobile_lcdc_ops = { .fb_open = sh_mobile_open, .fb_release = sh_mobile_release, .fb_check_var = sh_mobile_check_var, + .fb_set_par = sh_mobile_set_par, }; static int sh_mobile_lcdc_update_bl(struct backlight_device *bdev) @@ -1238,66 +1291,6 @@ static void sh_mobile_lcdc_bl_remove(struct backlight_device *bdev) backlight_device_unregister(bdev); } -static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp, - int nonstd) -{ - if (nonstd) { - switch (bpp) { - case 12: - case 16: - case 24: - var->bits_per_pixel = bpp; - var->nonstd = nonstd; - return 0; - default: - return -EINVAL; - } - } - - switch (bpp) { - case 16: /* PKF[4:0] = 00011 - RGB 565 */ - var->red.offset = 11; - var->red.length = 5; - var->green.offset = 5; - var->green.length = 6; - var->blue.offset = 0; - var->blue.length = 5; - var->transp.offset = 0; - var->transp.length = 0; - break; - - case 24: /* PKF[4:0] = 01011 - RGB 888 */ - var->red.offset = 16; - var->red.length = 8; - var->green.offset = 8; - var->green.length = 8; - var->blue.offset = 0; - var->blue.length = 8; - var->transp.offset = 0; - var->transp.length = 0; - break; - - case 32: /* PKF[4:0] = 00000 - RGBA 888 */ - var->red.offset = 16; - var->red.length = 8; - var->green.offset = 8; - var->green.length = 8; - var->blue.offset = 0; - var->blue.length = 8; - var->transp.offset = 24; - var->transp.length = 8; - break; - default: - return -EINVAL; - } - var->bits_per_pixel = bpp; - var->red.msb_right = 0; - var->green.msb_right = 0; - var->blue.msb_right = 0; - var->transp.msb_right = 0; - return 0; -} - static int sh_mobile_lcdc_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); @@ -1316,47 +1309,20 @@ static int sh_mobile_lcdc_resume(struct device *dev) static int sh_mobile_lcdc_runtime_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); - struct sh_mobile_lcdc_priv *p = platform_get_drvdata(pdev); - struct sh_mobile_lcdc_chan *ch; - int k, n; - - /* save per-channel registers */ - for (k = 0; k < ARRAY_SIZE(p->ch); k++) { - ch = &p->ch[k]; - if (!ch->enabled) - continue; - for (n = 0; n < NR_CH_REGS; n++) - ch->saved_ch_regs[n] = lcdc_read_chan(ch, n); - } - - /* save shared registers */ - for (n = 0; n < NR_SHARED_REGS; n++) - p->saved_shared_regs[n] = lcdc_read(p, lcdc_shared_regs[n]); + struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev); /* turn off LCDC hardware */ - lcdc_write(p, _LDCNT1R, 0); + lcdc_write(priv, _LDCNT1R, 0); + return 0; } static int sh_mobile_lcdc_runtime_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); - struct sh_mobile_lcdc_priv *p = platform_get_drvdata(pdev); - struct sh_mobile_lcdc_chan *ch; - int k, n; - - /* restore per-channel registers */ - for (k = 0; k < ARRAY_SIZE(p->ch); k++) { - ch = &p->ch[k]; - if (!ch->enabled) - continue; - for (n = 0; n < NR_CH_REGS; n++) - lcdc_write_chan(ch, n, ch->saved_ch_regs[n]); - } + struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev); - /* restore shared registers */ - for (n = 0; n < NR_SHARED_REGS; n++) - lcdc_write(p, lcdc_shared_regs[n], p->saved_shared_regs[n]); + __sh_mobile_lcdc_start(priv); return 0; } @@ -1408,17 +1374,187 @@ static int sh_mobile_lcdc_notify(struct notifier_block *nb, return NOTIFY_OK; } -static int sh_mobile_lcdc_remove(struct platform_device *pdev); +static int sh_mobile_lcdc_remove(struct platform_device *pdev) +{ + struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev); + struct fb_info *info; + int i; -static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) + fb_unregister_client(&priv->notifier); + + for (i = 0; i < ARRAY_SIZE(priv->ch); i++) + if (priv->ch[i].info && priv->ch[i].info->dev) + unregister_framebuffer(priv->ch[i].info); + + sh_mobile_lcdc_stop(priv); + + for (i = 0; i < ARRAY_SIZE(priv->ch); i++) { + info = priv->ch[i].info; + + if (!info || !info->device) + continue; + + if (priv->ch[i].sglist) + vfree(priv->ch[i].sglist); + + if (info->screen_base) + dma_free_coherent(&pdev->dev, info->fix.smem_len, + info->screen_base, + priv->ch[i].dma_handle); + fb_dealloc_cmap(&info->cmap); + framebuffer_release(info); + } + + for (i = 0; i < ARRAY_SIZE(priv->ch); i++) { + if (priv->ch[i].bl) + sh_mobile_lcdc_bl_remove(priv->ch[i].bl); + } + + if (priv->dot_clk) + clk_put(priv->dot_clk); + + if (priv->dev) + pm_runtime_disable(priv->dev); + + if (priv->base) + iounmap(priv->base); + + if (priv->irq) + free_irq(priv->irq, priv); + kfree(priv); + return 0; +} + +static int __devinit sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_chan *ch, + struct device *dev) { + struct sh_mobile_lcdc_chan_cfg *cfg = &ch->cfg; + const struct fb_videomode *max_mode; + const struct fb_videomode *mode; + struct fb_var_screeninfo *var; struct fb_info *info; - struct sh_mobile_lcdc_priv *priv; + unsigned int max_size; + int num_cfg; + void *buf; + int ret; + int i; + + mutex_init(&ch->open_lock); + + /* Allocate the frame buffer device. */ + ch->info = framebuffer_alloc(0, dev); + if (!ch->info) { + dev_err(dev, "unable to allocate fb_info\n"); + return -ENOMEM; + } + + info = ch->info; + info->fbops = &sh_mobile_lcdc_ops; + info->par = ch; + info->pseudo_palette = &ch->pseudo_palette; + info->flags = FBINFO_FLAG_DEFAULT; + + /* Iterate through the modes to validate them and find the highest + * resolution. + */ + max_mode = NULL; + max_size = 0; + + for (i = 0, mode = cfg->lcd_cfg; i < cfg->num_cfg; i++, mode++) { + unsigned int size = mode->yres * mode->xres; + + /* NV12 buffers must have even number of lines */ + if ((cfg->nonstd) && cfg->bpp == 12 && + (mode->yres & 0x1)) { + dev_err(dev, "yres must be multiple of 2 for YCbCr420 " + "mode.\n"); + return -EINVAL; + } + + if (size > max_size) { + max_mode = mode; + max_size = size; + } + } + + if (!max_size) + max_size = MAX_XRES * MAX_YRES; + else + dev_dbg(dev, "Found largest videomode %ux%u\n", + max_mode->xres, max_mode->yres); + + /* Initialize fixed screen information. Restrict pan to 2 lines steps + * for NV12. + */ + info->fix = sh_mobile_lcdc_fix; + info->fix.smem_len = max_size * 2 * cfg->bpp / 8; + if (cfg->nonstd && cfg->bpp == 12) + info->fix.ypanstep = 2; + + /* Create the mode list. */ + if (cfg->lcd_cfg == NULL) { + mode = &default_720p; + num_cfg = 1; + } else { + mode = cfg->lcd_cfg; + num_cfg = cfg->num_cfg; + } + + fb_videomode_to_modelist(mode, num_cfg, &info->modelist); + + /* Initialize variable screen information using the first mode as + * default. The default Y virtual resolution is twice the panel size to + * allow for double-buffering. + */ + var = &info->var; + fb_videomode_to_var(var, mode); + var->bits_per_pixel = cfg->bpp; + var->width = cfg->lcd_size_cfg.width; + var->height = cfg->lcd_size_cfg.height; + var->yres_virtual = var->yres * 2; + var->activate = FB_ACTIVATE_NOW; + + ret = sh_mobile_check_var(var, info); + if (ret) + return ret; + + /* Allocate frame buffer memory and color map. */ + buf = dma_alloc_coherent(dev, info->fix.smem_len, &ch->dma_handle, + GFP_KERNEL); + if (!buf) { + dev_err(dev, "unable to allocate buffer\n"); + return -ENOMEM; + } + + ret = fb_alloc_cmap(&info->cmap, PALETTE_NR, 0); + if (ret < 0) { + dev_err(dev, "unable to allocate cmap\n"); + dma_free_coherent(dev, info->fix.smem_len, + buf, ch->dma_handle); + return ret; + } + + info->fix.smem_start = ch->dma_handle; + if (var->nonstd) + info->fix.line_length = var->xres; + else + info->fix.line_length = var->xres * (cfg->bpp / 8); + + info->screen_base = buf; + info->device = dev; + ch->display_var = *var; + + return 0; +} + +static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) +{ struct sh_mobile_lcdc_info *pdata = pdev->dev.platform_data; + struct sh_mobile_lcdc_priv *priv; struct resource *res; + int num_channels; int error; - void *buf; - int i, j; + int i; if (!pdata) { dev_err(&pdev->dev, "no platform data defined\n"); @@ -1440,7 +1576,7 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); - error = request_irq(i, sh_mobile_lcdc_irq, IRQF_DISABLED, + error = request_irq(i, sh_mobile_lcdc_irq, 0, dev_name(&pdev->dev), priv); if (error) { dev_err(&pdev->dev, "unable to request irq\n"); @@ -1450,9 +1586,8 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) priv->irq = i; atomic_set(&priv->hw_usecnt, -1); - j = 0; - for (i = 0; i < ARRAY_SIZE(pdata->ch); i++) { - struct sh_mobile_lcdc_chan *ch = priv->ch + j; + for (i = 0, num_channels = 0; i < ARRAY_SIZE(pdata->ch); i++) { + struct sh_mobile_lcdc_chan *ch = priv->ch + num_channels; ch->lcdc = priv; memcpy(&ch->cfg, &pdata->ch[i], sizeof(pdata->ch[i])); @@ -1472,26 +1607,26 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) switch (pdata->ch[i].chan) { case LCDC_CHAN_MAINLCD: - ch->enabled = 1 << 1; + ch->enabled = LDCNT2R_ME; ch->reg_offs = lcdc_offs_mainlcd; - j++; + num_channels++; break; case LCDC_CHAN_SUBLCD: - ch->enabled = 1 << 2; + ch->enabled = LDCNT2R_SE; ch->reg_offs = lcdc_offs_sublcd; - j++; + num_channels++; break; } } - if (!j) { + if (!num_channels) { dev_err(&pdev->dev, "no channels defined\n"); error = -EINVAL; goto err1; } /* for dual channel LCDC (MAIN + SUB) force shared bpp setting */ - if (j == 2) + if (num_channels == 2) priv->forced_bpp = pdata->ch[0].bpp; priv->base = ioremap_nocache(res->start, resource_size(res)); @@ -1506,125 +1641,23 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) priv->meram_dev = pdata->meram_dev; - for (i = 0; i < j; i++) { - struct fb_var_screeninfo *var; - const struct fb_videomode *lcd_cfg, *max_cfg = NULL; + for (i = 0; i < num_channels; i++) { struct sh_mobile_lcdc_chan *ch = priv->ch + i; - struct sh_mobile_lcdc_chan_cfg *cfg = &ch->cfg; - const struct fb_videomode *mode = cfg->lcd_cfg; - unsigned long max_size = 0; - int k; - int num_cfg; - - ch->info = framebuffer_alloc(0, &pdev->dev); - if (!ch->info) { - dev_err(&pdev->dev, "unable to allocate fb_info\n"); - error = -ENOMEM; - break; - } - - info = ch->info; - var = &info->var; - info->fbops = &sh_mobile_lcdc_ops; - info->par = ch; - - mutex_init(&ch->open_lock); - - for (k = 0, lcd_cfg = mode; - k < cfg->num_cfg && lcd_cfg; - k++, lcd_cfg++) { - unsigned long size = lcd_cfg->yres * lcd_cfg->xres; - /* NV12 buffers must have even number of lines */ - if ((cfg->nonstd) && cfg->bpp == 12 && - (lcd_cfg->yres & 0x1)) { - dev_err(&pdev->dev, "yres must be multiple of 2" - " for YCbCr420 mode.\n"); - error = -EINVAL; - goto err1; - } - - if (size > max_size) { - max_cfg = lcd_cfg; - max_size = size; - } - } - - if (!mode) - max_size = MAX_XRES * MAX_YRES; - else if (max_cfg) - dev_dbg(&pdev->dev, "Found largest videomode %ux%u\n", - max_cfg->xres, max_cfg->yres); - info->fix = sh_mobile_lcdc_fix; - info->fix.smem_len = max_size * 2 * cfg->bpp / 8; - - /* Only pan in 2 line steps for NV12 */ - if (cfg->nonstd && cfg->bpp == 12) - info->fix.ypanstep = 2; - - if (!mode) { - mode = &default_720p; - num_cfg = 1; - } else { - num_cfg = cfg->num_cfg; - } - - fb_videomode_to_modelist(mode, num_cfg, &info->modelist); - - fb_videomode_to_var(var, mode); - var->width = cfg->lcd_size_cfg.width; - var->height = cfg->lcd_size_cfg.height; - /* Default Y virtual resolution is 2x panel size */ - var->yres_virtual = var->yres * 2; - var->activate = FB_ACTIVATE_NOW; - - error = sh_mobile_lcdc_set_bpp(var, cfg->bpp, cfg->nonstd); + error = sh_mobile_lcdc_channel_init(ch, &pdev->dev); if (error) - break; - - buf = dma_alloc_coherent(&pdev->dev, info->fix.smem_len, - &ch->dma_handle, GFP_KERNEL); - if (!buf) { - dev_err(&pdev->dev, "unable to allocate buffer\n"); - error = -ENOMEM; - break; - } - - info->pseudo_palette = &ch->pseudo_palette; - info->flags = FBINFO_FLAG_DEFAULT; - - error = fb_alloc_cmap(&info->cmap, PALETTE_NR, 0); - if (error < 0) { - dev_err(&pdev->dev, "unable to allocate cmap\n"); - dma_free_coherent(&pdev->dev, info->fix.smem_len, - buf, ch->dma_handle); - break; - } - - info->fix.smem_start = ch->dma_handle; - if (var->nonstd) - info->fix.line_length = var->xres; - else - info->fix.line_length = var->xres * (cfg->bpp / 8); - - info->screen_base = buf; - info->device = &pdev->dev; - ch->display_var = *var; + goto err1; } - if (error) - goto err1; - error = sh_mobile_lcdc_start(priv); if (error) { dev_err(&pdev->dev, "unable to start hardware\n"); goto err1; } - for (i = 0; i < j; i++) { + for (i = 0; i < num_channels; i++) { struct sh_mobile_lcdc_chan *ch = priv->ch + i; - - info = ch->info; + struct fb_info *info = ch->info; if (info->fbdefio) { ch->sglist = vmalloc(sizeof(struct scatterlist) * @@ -1665,57 +1698,6 @@ err1: return error; } -static int sh_mobile_lcdc_remove(struct platform_device *pdev) -{ - struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev); - struct fb_info *info; - int i; - - fb_unregister_client(&priv->notifier); - - for (i = 0; i < ARRAY_SIZE(priv->ch); i++) - if (priv->ch[i].info && priv->ch[i].info->dev) - unregister_framebuffer(priv->ch[i].info); - - sh_mobile_lcdc_stop(priv); - - for (i = 0; i < ARRAY_SIZE(priv->ch); i++) { - info = priv->ch[i].info; - - if (!info || !info->device) - continue; - - if (priv->ch[i].sglist) - vfree(priv->ch[i].sglist); - - if (info->screen_base) - dma_free_coherent(&pdev->dev, info->fix.smem_len, - info->screen_base, - priv->ch[i].dma_handle); - fb_dealloc_cmap(&info->cmap); - framebuffer_release(info); - } - - for (i = 0; i < ARRAY_SIZE(priv->ch); i++) { - if (priv->ch[i].bl) - sh_mobile_lcdc_bl_remove(priv->ch[i].bl); - } - - if (priv->dot_clk) - clk_put(priv->dot_clk); - - if (priv->dev) - pm_runtime_disable(priv->dev); - - if (priv->base) - iounmap(priv->base); - - if (priv->irq) - free_irq(priv->irq, priv); - kfree(priv); - return 0; -} - static struct platform_driver sh_mobile_lcdc_driver = { .driver = { .name = "sh_mobile_lcdc_fb", diff --git a/drivers/video/sh_mobile_lcdcfb.h b/drivers/video/sh_mobile_lcdcfb.h index aeed6687e6a7..a58a0f38848b 100644 --- a/drivers/video/sh_mobile_lcdcfb.h +++ b/drivers/video/sh_mobile_lcdcfb.h @@ -18,6 +18,13 @@ struct sh_mobile_lcdc_priv; struct fb_info; struct backlight_device; +/* + * struct sh_mobile_lcdc_chan - LCDC display channel + * + * @base_addr_y: Frame buffer viewport base address (luma component) + * @base_addr_c: Frame buffer viewport base address (chroma component) + * @pitch: Frame buffer line pitch + */ struct sh_mobile_lcdc_chan { struct sh_mobile_lcdc_priv *lcdc; unsigned long *reg_offs; @@ -25,7 +32,6 @@ struct sh_mobile_lcdc_chan { unsigned long enabled; /* ME and SE in LDCNT2R */ struct sh_mobile_lcdc_chan_cfg cfg; u32 pseudo_palette[PALETTE_NR]; - unsigned long saved_ch_regs[NR_CH_REGS]; struct fb_info *info; struct backlight_device *bl; dma_addr_t dma_handle; @@ -40,6 +46,10 @@ struct sh_mobile_lcdc_chan { int blank_status; struct mutex open_lock; /* protects the use counter */ int meram_enabled; + + unsigned long base_addr_y; + unsigned long base_addr_c; + unsigned int pitch; }; #endif diff --git a/drivers/video/sh_mobile_meram.c b/drivers/video/sh_mobile_meram.c index cc7d7329dc15..4d63490209cd 100644 --- a/drivers/video/sh_mobile_meram.c +++ b/drivers/video/sh_mobile_meram.c @@ -12,29 +12,103 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/device.h> +#include <linux/pm_runtime.h> #include <linux/io.h> #include <linux/slab.h> #include <linux/platform_device.h> - -#include "sh_mobile_meram.h" +#include <video/sh_mobile_meram.h> /* meram registers */ -#define MExxCTL 0x0 -#define MExxBSIZE 0x4 -#define MExxMNCF 0x8 -#define MExxSARA 0x10 -#define MExxSARB 0x14 -#define MExxSBSIZE 0x18 - -#define MERAM_MExxCTL_VAL(ctl, next_icb, addr) \ - ((ctl) | (((next_icb) & 0x1f) << 11) | (((addr) & 0x7ff) << 16)) -#define MERAM_MExxBSIZE_VAL(a, b, c) \ - (((a) << 28) | ((b) << 16) | (c)) - -#define MEVCR1 0x4 -#define MEACTS 0x10 -#define MEQSEL1 0x40 -#define MEQSEL2 0x44 +#define MEVCR1 0x4 +#define MEVCR1_RST (1 << 31) +#define MEVCR1_WD (1 << 30) +#define MEVCR1_AMD1 (1 << 29) +#define MEVCR1_AMD0 (1 << 28) +#define MEQSEL1 0x40 +#define MEQSEL2 0x44 + +#define MExxCTL 0x400 +#define MExxCTL_BV (1 << 31) +#define MExxCTL_BSZ_SHIFT 28 +#define MExxCTL_MSAR_MASK (0x7ff << MExxCTL_MSAR_SHIFT) +#define MExxCTL_MSAR_SHIFT 16 +#define MExxCTL_NXT_MASK (0x1f << MExxCTL_NXT_SHIFT) +#define MExxCTL_NXT_SHIFT 11 +#define MExxCTL_WD1 (1 << 10) +#define MExxCTL_WD0 (1 << 9) +#define MExxCTL_WS (1 << 8) +#define MExxCTL_CB (1 << 7) +#define MExxCTL_WBF (1 << 6) +#define MExxCTL_WF (1 << 5) +#define MExxCTL_RF (1 << 4) +#define MExxCTL_CM (1 << 3) +#define MExxCTL_MD_READ (1 << 0) +#define MExxCTL_MD_WRITE (2 << 0) +#define MExxCTL_MD_ICB_WB (3 << 0) +#define MExxCTL_MD_ICB (4 << 0) +#define MExxCTL_MD_FB (7 << 0) +#define MExxCTL_MD_MASK (7 << 0) +#define MExxBSIZE 0x404 +#define MExxBSIZE_RCNT_SHIFT 28 +#define MExxBSIZE_YSZM1_SHIFT 16 +#define MExxBSIZE_XSZM1_SHIFT 0 +#define MExxMNCF 0x408 +#define MExxMNCF_KWBNM_SHIFT 28 +#define MExxMNCF_KRBNM_SHIFT 24 +#define MExxMNCF_BNM_SHIFT 16 +#define MExxMNCF_XBV (1 << 15) +#define MExxMNCF_CPL_YCBCR444 (1 << 12) +#define MExxMNCF_CPL_YCBCR420 (2 << 12) +#define MExxMNCF_CPL_YCBCR422 (3 << 12) +#define MExxMNCF_CPL_MSK (3 << 12) +#define MExxMNCF_BL (1 << 2) +#define MExxMNCF_LNM_SHIFT 0 +#define MExxSARA 0x410 +#define MExxSARB 0x414 +#define MExxSBSIZE 0x418 +#define MExxSBSIZE_HDV (1 << 31) +#define MExxSBSIZE_HSZ16 (0 << 28) +#define MExxSBSIZE_HSZ32 (1 << 28) +#define MExxSBSIZE_HSZ64 (2 << 28) +#define MExxSBSIZE_HSZ128 (3 << 28) +#define MExxSBSIZE_SBSIZZ_SHIFT 0 + +#define MERAM_MExxCTL_VAL(next, addr) \ + ((((next) << MExxCTL_NXT_SHIFT) & MExxCTL_NXT_MASK) | \ + (((addr) << MExxCTL_MSAR_SHIFT) & MExxCTL_MSAR_MASK)) +#define MERAM_MExxBSIZE_VAL(rcnt, yszm1, xszm1) \ + (((rcnt) << MExxBSIZE_RCNT_SHIFT) | \ + ((yszm1) << MExxBSIZE_YSZM1_SHIFT) | \ + ((xszm1) << MExxBSIZE_XSZM1_SHIFT)) + +#define SH_MOBILE_MERAM_ICB_NUM 32 + +static unsigned long common_regs[] = { + MEVCR1, + MEQSEL1, + MEQSEL2, +}; +#define CMN_REGS_SIZE ARRAY_SIZE(common_regs) + +static unsigned long icb_regs[] = { + MExxCTL, + MExxBSIZE, + MExxMNCF, + MExxSARA, + MExxSARB, + MExxSBSIZE, +}; +#define ICB_REGS_SIZE ARRAY_SIZE(icb_regs) + +struct sh_mobile_meram_priv { + void __iomem *base; + struct mutex lock; + unsigned long used_icb; + int used_meram_cache_regions; + unsigned long used_meram_cache[SH_MOBILE_MERAM_ICB_NUM]; + unsigned long cmn_saved_regs[CMN_REGS_SIZE]; + unsigned long icb_saved_regs[ICB_REGS_SIZE * SH_MOBILE_MERAM_ICB_NUM]; +}; /* settings */ #define MERAM_SEC_LINE 15 @@ -44,8 +118,7 @@ * MERAM/ICB access functions */ -#define MERAM_ICB_OFFSET(base, idx, off) \ - ((base) + (0x400 + ((idx) * 0x20) + (off))) +#define MERAM_ICB_OFFSET(base, idx, off) ((base) + (off) + (idx) * 0x20) static inline void meram_write_icb(void __iomem *base, int idx, int off, unsigned long val) @@ -280,17 +353,18 @@ static int meram_init(struct sh_mobile_meram_priv *priv, /* * Set MERAM for framebuffer * - * 0x70f: WD = 0x3, WS=0x1, CM=0x1, MD=FB mode * we also chain the cache_icb and the marker_icb. * we also split the allocated MERAM buffer between two ICBs. */ meram_write_icb(priv->base, icb->cache_icb, MExxCTL, - MERAM_MExxCTL_VAL(0x70f, icb->marker_icb, - icb->meram_offset)); + MERAM_MExxCTL_VAL(icb->marker_icb, icb->meram_offset) | + MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM | + MExxCTL_MD_FB); meram_write_icb(priv->base, icb->marker_icb, MExxCTL, - MERAM_MExxCTL_VAL(0x70f, icb->cache_icb, - icb->meram_offset + - icb->meram_size / 2)); + MERAM_MExxCTL_VAL(icb->cache_icb, icb->meram_offset + + icb->meram_size / 2) | + MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM | + MExxCTL_MD_FB); return 0; } @@ -299,8 +373,10 @@ static void meram_deinit(struct sh_mobile_meram_priv *priv, struct sh_mobile_meram_icb *icb) { /* disable ICB */ - meram_write_icb(priv->base, icb->cache_icb, MExxCTL, 0); - meram_write_icb(priv->base, icb->marker_icb, MExxCTL, 0); + meram_write_icb(priv->base, icb->cache_icb, MExxCTL, + MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF); + meram_write_icb(priv->base, icb->marker_icb, MExxCTL, + MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF); icb->cache_unit = 0; } @@ -337,24 +413,22 @@ static int sh_mobile_meram_register(struct sh_mobile_meram_info *pdata, xres, yres, (!pixelformat) ? "yuv" : "rgb", base_addr_y, base_addr_c); - mutex_lock(&priv->lock); - /* we can't handle wider than 8192px */ if (xres > 8192) { dev_err(&pdev->dev, "width exceeding the limit (> 8192)."); - error = -EINVAL; - goto err; - } - - if (priv->used_meram_cache_regions + 2 > SH_MOBILE_MERAM_ICB_NUM) { - dev_err(&pdev->dev, "no more ICB available."); - error = -EINVAL; - goto err; + return -EINVAL; } /* do we have at least one ICB config? */ if (cfg->icb[0].marker_icb < 0 || cfg->icb[0].cache_icb < 0) { dev_err(&pdev->dev, "at least one ICB is required."); + return -EINVAL; + } + + mutex_lock(&priv->lock); + + if (priv->used_meram_cache_regions + 2 > SH_MOBILE_MERAM_ICB_NUM) { + dev_err(&pdev->dev, "no more ICB available."); error = -EINVAL; goto err; } @@ -460,6 +534,57 @@ static int sh_mobile_meram_update(struct sh_mobile_meram_info *pdata, return 0; } +static int sh_mobile_meram_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev); + int k, j; + + for (k = 0; k < CMN_REGS_SIZE; k++) + priv->cmn_saved_regs[k] = meram_read_reg(priv->base, + common_regs[k]); + + for (j = 0; j < 32; j++) { + if (!test_bit(j, &priv->used_icb)) + continue; + for (k = 0; k < ICB_REGS_SIZE; k++) { + priv->icb_saved_regs[j * ICB_REGS_SIZE + k] = + meram_read_icb(priv->base, j, icb_regs[k]); + /* Reset ICB on resume */ + if (icb_regs[k] == MExxCTL) + priv->icb_saved_regs[j * ICB_REGS_SIZE + k] |= + MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF; + } + } + return 0; +} + +static int sh_mobile_meram_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev); + int k, j; + + for (j = 0; j < 32; j++) { + if (!test_bit(j, &priv->used_icb)) + continue; + for (k = 0; k < ICB_REGS_SIZE; k++) { + meram_write_icb(priv->base, j, icb_regs[k], + priv->icb_saved_regs[j * ICB_REGS_SIZE + k]); + } + } + + for (k = 0; k < CMN_REGS_SIZE; k++) + meram_write_reg(priv->base, common_regs[k], + priv->cmn_saved_regs[k]); + return 0; +} + +static const struct dev_pm_ops sh_mobile_meram_dev_pm_ops = { + .runtime_suspend = sh_mobile_meram_runtime_suspend, + .runtime_resume = sh_mobile_meram_runtime_resume, +}; + static struct sh_mobile_meram_ops sh_mobile_meram_ops = { .module = THIS_MODULE, .meram_register = sh_mobile_meram_register, @@ -513,7 +638,9 @@ static int __devinit sh_mobile_meram_probe(struct platform_device *pdev) /* initialize ICB addressing mode */ if (pdata->addr_mode == SH_MOBILE_MERAM_MODE1) - meram_write_reg(priv->base, MEVCR1, 1 << 29); + meram_write_reg(priv->base, MEVCR1, MEVCR1_AMD1); + + pm_runtime_enable(&pdev->dev); dev_info(&pdev->dev, "sh_mobile_meram initialized."); @@ -530,6 +657,8 @@ static int sh_mobile_meram_remove(struct platform_device *pdev) { struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev); + pm_runtime_disable(&pdev->dev); + if (priv->base) iounmap(priv->base); @@ -544,6 +673,7 @@ static struct platform_driver sh_mobile_meram_driver = { .driver = { .name = "sh_mobile_meram", .owner = THIS_MODULE, + .pm = &sh_mobile_meram_dev_pm_ops, }, .probe = sh_mobile_meram_probe, .remove = sh_mobile_meram_remove, diff --git a/drivers/video/sh_mobile_meram.h b/drivers/video/sh_mobile_meram.h deleted file mode 100644 index 82c54fbce8bd..000000000000 --- a/drivers/video/sh_mobile_meram.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef __sh_mobile_meram_h__ -#define __sh_mobile_meram_h__ - -#include <linux/mutex.h> -#include <video/sh_mobile_meram.h> - -/* - * MERAM private - */ - -#define MERAM_ICB_Y 0x1 -#define MERAM_ICB_C 0x2 - -/* MERAM cache size */ -#define SH_MOBILE_MERAM_ICB_NUM 32 - -#define SH_MOBILE_MERAM_CACHE_OFFSET(p) ((p) >> 16) -#define SH_MOBILE_MERAM_CACHE_SIZE(p) ((p) & 0xffff) - -struct sh_mobile_meram_priv { - void __iomem *base; - struct mutex lock; - unsigned long used_icb; - int used_meram_cache_regions; - unsigned long used_meram_cache[SH_MOBILE_MERAM_ICB_NUM]; -}; - -int sh_mobile_meram_alloc_icb(const struct sh_mobile_meram_cfg *cfg, - int xres, - int yres, - unsigned int base_addr, - int yuv_mode, - int *marker_icb, - int *out_pitch); - -void sh_mobile_meram_free_icb(int marker_icb); - -#define SH_MOBILE_MERAM_START(ind, ab) \ - (0xC0000000 | ((ab & 0x1) << 23) | ((ind & 0x1F) << 24)) - -#endif /* !__sh_mobile_meram_h__ */ diff --git a/drivers/video/sis/sis_main.c b/drivers/video/sis/sis_main.c index 75259845933d..078ca2167d6f 100644 --- a/drivers/video/sis/sis_main.c +++ b/drivers/video/sis/sis_main.c @@ -1333,19 +1333,14 @@ sisfb_set_base_CRT2(struct sis_video_info *ivideo, unsigned int base) } static int -sisfb_pan_var(struct sis_video_info *ivideo, struct fb_var_screeninfo *var) +sisfb_pan_var(struct sis_video_info *ivideo, struct fb_info *info, + struct fb_var_screeninfo *var) { - if(var->xoffset > (var->xres_virtual - var->xres)) { - return -EINVAL; - } - if(var->yoffset > (var->yres_virtual - var->yres)) { - return -EINVAL; - } - - ivideo->current_base = (var->yoffset * var->xres_virtual) + var->xoffset; + ivideo->current_base = var->yoffset * info->var.xres_virtual + + var->xoffset; /* calculate base bpp dep. */ - switch(var->bits_per_pixel) { + switch (info->var.bits_per_pixel) { case 32: break; case 16: @@ -1635,20 +1630,15 @@ sisfb_pan_display(struct fb_var_screeninfo *var, struct fb_info* info) struct sis_video_info *ivideo = (struct sis_video_info *)info->par; int err; - if(var->xoffset > (var->xres_virtual - var->xres)) - return -EINVAL; - - if(var->yoffset > (var->yres_virtual - var->yres)) - return -EINVAL; - - if(var->vmode & FB_VMODE_YWRAP) + if (var->vmode & FB_VMODE_YWRAP) return -EINVAL; - if(var->xoffset + info->var.xres > info->var.xres_virtual || - var->yoffset + info->var.yres > info->var.yres_virtual) + if (var->xoffset + info->var.xres > info->var.xres_virtual || + var->yoffset + info->var.yres > info->var.yres_virtual) return -EINVAL; - if((err = sisfb_pan_var(ivideo, var)) < 0) + err = sisfb_pan_var(ivideo, info, var); + if (err < 0) return err; info->var.xoffset = var->xoffset; diff --git a/drivers/video/skeletonfb.c b/drivers/video/skeletonfb.c index 89158bc71da2..30f7a815a62b 100644 --- a/drivers/video/skeletonfb.c +++ b/drivers/video/skeletonfb.c @@ -989,7 +989,7 @@ static struct platform_device *xxxfb_device; */ int __init xxxfb_setup(char *options) { - /* Parse user speficied options (`video=xxxfb:') */ + /* Parse user specified options (`video=xxxfb:') */ } #endif /* MODULE */ diff --git a/drivers/video/sm501fb.c b/drivers/video/sm501fb.c index 6294dca95500..a78254cf8e83 100644 --- a/drivers/video/sm501fb.c +++ b/drivers/video/sm501fb.c @@ -582,7 +582,7 @@ static int sm501fb_pan_crt(struct fb_var_screeninfo *var, { struct sm501fb_par *par = info->par; struct sm501fb_info *fbi = par->info; - unsigned int bytes_pixel = var->bits_per_pixel / 8; + unsigned int bytes_pixel = info->var.bits_per_pixel / 8; unsigned long reg; unsigned long xoffs; @@ -614,10 +614,10 @@ static int sm501fb_pan_pnl(struct fb_var_screeninfo *var, struct sm501fb_info *fbi = par->info; unsigned long reg; - reg = var->xoffset | (var->xres_virtual << 16); + reg = var->xoffset | (info->var.xres_virtual << 16); smc501_writel(reg, fbi->regs + SM501_DC_PANEL_FB_WIDTH); - reg = var->yoffset | (var->yres_virtual << 16); + reg = var->yoffset | (info->var.yres_virtual << 16); smc501_writel(reg, fbi->regs + SM501_DC_PANEL_FB_HEIGHT); sm501fb_sync_regs(fbi); diff --git a/drivers/video/smscufx.c b/drivers/video/smscufx.c new file mode 100644 index 000000000000..aaccffac67ab --- /dev/null +++ b/drivers/video/smscufx.c @@ -0,0 +1,1994 @@ +/* + * smscufx.c -- Framebuffer driver for SMSC UFX USB controller + * + * Copyright (C) 2011 Steve Glendinning <steve.glendinning@smsc.com> + * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it> + * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com> + * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com> + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + * + * Based on udlfb, with work from Florian Echtler, Henrik Bjerregaard Pedersen, + * and others. + * + * Works well with Bernie Thompson's X DAMAGE patch to xf86-video-fbdev + * available from http://git.plugable.com + * + * Layout is based on skeletonfb by James Simmons and Geert Uytterhoeven, + * usb-skeleton by GregKH. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/usb.h> +#include <linux/uaccess.h> +#include <linux/mm.h> +#include <linux/fb.h> +#include <linux/vmalloc.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include "edid.h" + +#define check_warn(status, fmt, args...) \ + ({ if (status < 0) pr_warn(fmt, ##args); }) + +#define check_warn_return(status, fmt, args...) \ + ({ if (status < 0) { pr_warn(fmt, ##args); return status; } }) + +#define check_warn_goto_error(status, fmt, args...) \ + ({ if (status < 0) { pr_warn(fmt, ##args); goto error; } }) + +#define all_bits_set(x, bits) (((x) & (bits)) == (bits)) + +#define USB_VENDOR_REQUEST_WRITE_REGISTER 0xA0 +#define USB_VENDOR_REQUEST_READ_REGISTER 0xA1 + +/* + * TODO: Propose standard fb.h ioctl for reporting damage, + * using _IOWR() and one of the existing area structs from fb.h + * Consider these ioctls deprecated, but they're still used by the + * DisplayLink X server as yet - need both to be modified in tandem + * when new ioctl(s) are ready. + */ +#define UFX_IOCTL_RETURN_EDID (0xAD) +#define UFX_IOCTL_REPORT_DAMAGE (0xAA) + +/* -BULK_SIZE as per usb-skeleton. Can we get full page and avoid overhead? */ +#define BULK_SIZE (512) +#define MAX_TRANSFER (PAGE_SIZE*16 - BULK_SIZE) +#define WRITES_IN_FLIGHT (4) + +#define GET_URB_TIMEOUT (HZ) +#define FREE_URB_TIMEOUT (HZ*2) + +#define BPP 2 + +#define UFX_DEFIO_WRITE_DELAY 5 /* fb_deferred_io.delay in jiffies */ +#define UFX_DEFIO_WRITE_DISABLE (HZ*60) /* "disable" with long delay */ + +struct dloarea { + int x, y; + int w, h; +}; + +struct urb_node { + struct list_head entry; + struct ufx_data *dev; + struct delayed_work release_urb_work; + struct urb *urb; +}; + +struct urb_list { + struct list_head list; + spinlock_t lock; + struct semaphore limit_sem; + int available; + int count; + size_t size; +}; + +struct ufx_data { + struct usb_device *udev; + struct device *gdev; /* &udev->dev */ + struct fb_info *info; + struct urb_list urbs; + struct kref kref; + int fb_count; + bool virtualized; /* true when physical usb device not present */ + struct delayed_work free_framebuffer_work; + atomic_t usb_active; /* 0 = update virtual buffer, but no usb traffic */ + atomic_t lost_pixels; /* 1 = a render op failed. Need screen refresh */ + u8 *edid; /* null until we read edid from hw or get from sysfs */ + size_t edid_size; + u32 pseudo_palette[256]; +}; + +static struct fb_fix_screeninfo ufx_fix = { + .id = "smscufx", + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_TRUECOLOR, + .xpanstep = 0, + .ypanstep = 0, + .ywrapstep = 0, + .accel = FB_ACCEL_NONE, +}; + +static const u32 smscufx_info_flags = FBINFO_DEFAULT | FBINFO_READS_FAST | + FBINFO_VIRTFB | FBINFO_HWACCEL_IMAGEBLIT | FBINFO_HWACCEL_FILLRECT | + FBINFO_HWACCEL_COPYAREA | FBINFO_MISC_ALWAYS_SETPAR; + +static struct usb_device_id id_table[] = { + {USB_DEVICE(0x0424, 0x9d00),}, + {USB_DEVICE(0x0424, 0x9d01),}, + {}, +}; +MODULE_DEVICE_TABLE(usb, id_table); + +/* module options */ +static int console; /* Optionally allow fbcon to consume first framebuffer */ +static int fb_defio = true; /* Optionally enable fb_defio mmap support */ + +/* ufx keeps a list of urbs for efficient bulk transfers */ +static void ufx_urb_completion(struct urb *urb); +static struct urb *ufx_get_urb(struct ufx_data *dev); +static int ufx_submit_urb(struct ufx_data *dev, struct urb * urb, size_t len); +static int ufx_alloc_urb_list(struct ufx_data *dev, int count, size_t size); +static void ufx_free_urb_list(struct ufx_data *dev); + +/* reads a control register */ +static int ufx_reg_read(struct ufx_data *dev, u32 index, u32 *data) +{ + u32 *buf = kmalloc(4, GFP_KERNEL); + int ret; + + BUG_ON(!dev); + + if (!buf) + return -ENOMEM; + + ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), + USB_VENDOR_REQUEST_READ_REGISTER, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 00, index, buf, 4, USB_CTRL_GET_TIMEOUT); + + le32_to_cpus(buf); + *data = *buf; + kfree(buf); + + if (unlikely(ret < 0)) + pr_warn("Failed to read register index 0x%08x\n", index); + + return ret; +} + +/* writes a control register */ +static int ufx_reg_write(struct ufx_data *dev, u32 index, u32 data) +{ + u32 *buf = kmalloc(4, GFP_KERNEL); + int ret; + + BUG_ON(!dev); + + if (!buf) + return -ENOMEM; + + *buf = data; + cpu_to_le32s(buf); + + ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), + USB_VENDOR_REQUEST_WRITE_REGISTER, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 00, index, buf, 4, USB_CTRL_SET_TIMEOUT); + + kfree(buf); + + if (unlikely(ret < 0)) + pr_warn("Failed to write register index 0x%08x with value " + "0x%08x\n", index, data); + + return ret; +} + +static int ufx_reg_clear_and_set_bits(struct ufx_data *dev, u32 index, + u32 bits_to_clear, u32 bits_to_set) +{ + u32 data; + int status = ufx_reg_read(dev, index, &data); + check_warn_return(status, "ufx_reg_clear_and_set_bits error reading " + "0x%x", index); + + data &= (~bits_to_clear); + data |= bits_to_set; + + status = ufx_reg_write(dev, index, data); + check_warn_return(status, "ufx_reg_clear_and_set_bits error writing " + "0x%x", index); + + return 0; +} + +static int ufx_reg_set_bits(struct ufx_data *dev, u32 index, u32 bits) +{ + return ufx_reg_clear_and_set_bits(dev, index, 0, bits); +} + +static int ufx_reg_clear_bits(struct ufx_data *dev, u32 index, u32 bits) +{ + return ufx_reg_clear_and_set_bits(dev, index, bits, 0); +} + +static int ufx_lite_reset(struct ufx_data *dev) +{ + int status; + u32 value; + + status = ufx_reg_write(dev, 0x3008, 0x00000001); + check_warn_return(status, "ufx_lite_reset error writing 0x3008"); + + status = ufx_reg_read(dev, 0x3008, &value); + check_warn_return(status, "ufx_lite_reset error reading 0x3008"); + + return (value == 0) ? 0 : -EIO; +} + +/* If display is unblanked, then blank it */ +static int ufx_blank(struct ufx_data *dev, bool wait) +{ + u32 dc_ctrl, dc_sts; + int i; + + int status = ufx_reg_read(dev, 0x2004, &dc_sts); + check_warn_return(status, "ufx_blank error reading 0x2004"); + + status = ufx_reg_read(dev, 0x2000, &dc_ctrl); + check_warn_return(status, "ufx_blank error reading 0x2000"); + + /* return success if display is already blanked */ + if ((dc_sts & 0x00000100) || (dc_ctrl & 0x00000100)) + return 0; + + /* request the DC to blank the display */ + dc_ctrl |= 0x00000100; + status = ufx_reg_write(dev, 0x2000, dc_ctrl); + check_warn_return(status, "ufx_blank error writing 0x2000"); + + /* return success immediately if we don't have to wait */ + if (!wait) + return 0; + + for (i = 0; i < 250; i++) { + status = ufx_reg_read(dev, 0x2004, &dc_sts); + check_warn_return(status, "ufx_blank error reading 0x2004"); + + if (dc_sts & 0x00000100) + return 0; + } + + /* timed out waiting for display to blank */ + return -EIO; +} + +/* If display is blanked, then unblank it */ +static int ufx_unblank(struct ufx_data *dev, bool wait) +{ + u32 dc_ctrl, dc_sts; + int i; + + int status = ufx_reg_read(dev, 0x2004, &dc_sts); + check_warn_return(status, "ufx_unblank error reading 0x2004"); + + status = ufx_reg_read(dev, 0x2000, &dc_ctrl); + check_warn_return(status, "ufx_unblank error reading 0x2000"); + + /* return success if display is already unblanked */ + if (((dc_sts & 0x00000100) == 0) || ((dc_ctrl & 0x00000100) == 0)) + return 0; + + /* request the DC to unblank the display */ + dc_ctrl &= ~0x00000100; + status = ufx_reg_write(dev, 0x2000, dc_ctrl); + check_warn_return(status, "ufx_unblank error writing 0x2000"); + + /* return success immediately if we don't have to wait */ + if (!wait) + return 0; + + for (i = 0; i < 250; i++) { + status = ufx_reg_read(dev, 0x2004, &dc_sts); + check_warn_return(status, "ufx_unblank error reading 0x2004"); + + if ((dc_sts & 0x00000100) == 0) + return 0; + } + + /* timed out waiting for display to unblank */ + return -EIO; +} + +/* If display is enabled, then disable it */ +static int ufx_disable(struct ufx_data *dev, bool wait) +{ + u32 dc_ctrl, dc_sts; + int i; + + int status = ufx_reg_read(dev, 0x2004, &dc_sts); + check_warn_return(status, "ufx_disable error reading 0x2004"); + + status = ufx_reg_read(dev, 0x2000, &dc_ctrl); + check_warn_return(status, "ufx_disable error reading 0x2000"); + + /* return success if display is already disabled */ + if (((dc_sts & 0x00000001) == 0) || ((dc_ctrl & 0x00000001) == 0)) + return 0; + + /* request the DC to disable the display */ + dc_ctrl &= ~(0x00000001); + status = ufx_reg_write(dev, 0x2000, dc_ctrl); + check_warn_return(status, "ufx_disable error writing 0x2000"); + + /* return success immediately if we don't have to wait */ + if (!wait) + return 0; + + for (i = 0; i < 250; i++) { + status = ufx_reg_read(dev, 0x2004, &dc_sts); + check_warn_return(status, "ufx_disable error reading 0x2004"); + + if ((dc_sts & 0x00000001) == 0) + return 0; + } + + /* timed out waiting for display to disable */ + return -EIO; +} + +/* If display is disabled, then enable it */ +static int ufx_enable(struct ufx_data *dev, bool wait) +{ + u32 dc_ctrl, dc_sts; + int i; + + int status = ufx_reg_read(dev, 0x2004, &dc_sts); + check_warn_return(status, "ufx_enable error reading 0x2004"); + + status = ufx_reg_read(dev, 0x2000, &dc_ctrl); + check_warn_return(status, "ufx_enable error reading 0x2000"); + + /* return success if display is already enabled */ + if ((dc_sts & 0x00000001) || (dc_ctrl & 0x00000001)) + return 0; + + /* request the DC to enable the display */ + dc_ctrl |= 0x00000001; + status = ufx_reg_write(dev, 0x2000, dc_ctrl); + check_warn_return(status, "ufx_enable error writing 0x2000"); + + /* return success immediately if we don't have to wait */ + if (!wait) + return 0; + + for (i = 0; i < 250; i++) { + status = ufx_reg_read(dev, 0x2004, &dc_sts); + check_warn_return(status, "ufx_enable error reading 0x2004"); + + if (dc_sts & 0x00000001) + return 0; + } + + /* timed out waiting for display to enable */ + return -EIO; +} + +static int ufx_config_sys_clk(struct ufx_data *dev) +{ + int status = ufx_reg_write(dev, 0x700C, 0x8000000F); + check_warn_return(status, "error writing 0x700C"); + + status = ufx_reg_write(dev, 0x7014, 0x0010024F); + check_warn_return(status, "error writing 0x7014"); + + status = ufx_reg_write(dev, 0x7010, 0x00000000); + check_warn_return(status, "error writing 0x7010"); + + status = ufx_reg_clear_bits(dev, 0x700C, 0x0000000A); + check_warn_return(status, "error clearing PLL1 bypass in 0x700C"); + msleep(1); + + status = ufx_reg_clear_bits(dev, 0x700C, 0x80000000); + check_warn_return(status, "error clearing output gate in 0x700C"); + + return 0; +} + +static int ufx_config_ddr2(struct ufx_data *dev) +{ + int status, i = 0; + u32 tmp; + + status = ufx_reg_write(dev, 0x0004, 0x001F0F77); + check_warn_return(status, "error writing 0x0004"); + + status = ufx_reg_write(dev, 0x0008, 0xFFF00000); + check_warn_return(status, "error writing 0x0008"); + + status = ufx_reg_write(dev, 0x000C, 0x0FFF2222); + check_warn_return(status, "error writing 0x000C"); + + status = ufx_reg_write(dev, 0x0010, 0x00030814); + check_warn_return(status, "error writing 0x0010"); + + status = ufx_reg_write(dev, 0x0014, 0x00500019); + check_warn_return(status, "error writing 0x0014"); + + status = ufx_reg_write(dev, 0x0018, 0x020D0F15); + check_warn_return(status, "error writing 0x0018"); + + status = ufx_reg_write(dev, 0x001C, 0x02532305); + check_warn_return(status, "error writing 0x001C"); + + status = ufx_reg_write(dev, 0x0020, 0x0B030905); + check_warn_return(status, "error writing 0x0020"); + + status = ufx_reg_write(dev, 0x0024, 0x00000827); + check_warn_return(status, "error writing 0x0024"); + + status = ufx_reg_write(dev, 0x0028, 0x00000000); + check_warn_return(status, "error writing 0x0028"); + + status = ufx_reg_write(dev, 0x002C, 0x00000042); + check_warn_return(status, "error writing 0x002C"); + + status = ufx_reg_write(dev, 0x0030, 0x09520000); + check_warn_return(status, "error writing 0x0030"); + + status = ufx_reg_write(dev, 0x0034, 0x02223314); + check_warn_return(status, "error writing 0x0034"); + + status = ufx_reg_write(dev, 0x0038, 0x00430043); + check_warn_return(status, "error writing 0x0038"); + + status = ufx_reg_write(dev, 0x003C, 0xF00F000F); + check_warn_return(status, "error writing 0x003C"); + + status = ufx_reg_write(dev, 0x0040, 0xF380F00F); + check_warn_return(status, "error writing 0x0040"); + + status = ufx_reg_write(dev, 0x0044, 0xF00F0496); + check_warn_return(status, "error writing 0x0044"); + + status = ufx_reg_write(dev, 0x0048, 0x03080406); + check_warn_return(status, "error writing 0x0048"); + + status = ufx_reg_write(dev, 0x004C, 0x00001000); + check_warn_return(status, "error writing 0x004C"); + + status = ufx_reg_write(dev, 0x005C, 0x00000007); + check_warn_return(status, "error writing 0x005C"); + + status = ufx_reg_write(dev, 0x0100, 0x54F00012); + check_warn_return(status, "error writing 0x0100"); + + status = ufx_reg_write(dev, 0x0104, 0x00004012); + check_warn_return(status, "error writing 0x0104"); + + status = ufx_reg_write(dev, 0x0118, 0x40404040); + check_warn_return(status, "error writing 0x0118"); + + status = ufx_reg_write(dev, 0x0000, 0x00000001); + check_warn_return(status, "error writing 0x0000"); + + while (i++ < 500) { + status = ufx_reg_read(dev, 0x0000, &tmp); + check_warn_return(status, "error reading 0x0000"); + + if (all_bits_set(tmp, 0xC0000000)) + return 0; + } + + pr_err("DDR2 initialisation timed out, reg 0x0000=0x%08x", tmp); + return -ETIMEDOUT; +} + +struct pll_values { + u32 div_r0; + u32 div_f0; + u32 div_q0; + u32 range0; + u32 div_r1; + u32 div_f1; + u32 div_q1; + u32 range1; +}; + +static u32 ufx_calc_range(u32 ref_freq) +{ + if (ref_freq >= 88000000) + return 7; + + if (ref_freq >= 54000000) + return 6; + + if (ref_freq >= 34000000) + return 5; + + if (ref_freq >= 21000000) + return 4; + + if (ref_freq >= 13000000) + return 3; + + if (ref_freq >= 8000000) + return 2; + + return 1; +} + +/* calculates PLL divider settings for a desired target frequency */ +static void ufx_calc_pll_values(const u32 clk_pixel_pll, struct pll_values *asic_pll) +{ + const u32 ref_clk = 25000000; + u32 div_r0, div_f0, div_q0, div_r1, div_f1, div_q1; + u32 min_error = clk_pixel_pll; + + for (div_r0 = 1; div_r0 <= 32; div_r0++) { + u32 ref_freq0 = ref_clk / div_r0; + if (ref_freq0 < 5000000) + break; + + if (ref_freq0 > 200000000) + continue; + + for (div_f0 = 1; div_f0 <= 256; div_f0++) { + u32 vco_freq0 = ref_freq0 * div_f0; + + if (vco_freq0 < 350000000) + continue; + + if (vco_freq0 > 700000000) + break; + + for (div_q0 = 0; div_q0 < 7; div_q0++) { + u32 pllout_freq0 = vco_freq0 / (1 << div_q0); + + if (pllout_freq0 < 5000000) + break; + + if (pllout_freq0 > 200000000) + continue; + + for (div_r1 = 1; div_r1 <= 32; div_r1++) { + u32 ref_freq1 = pllout_freq0 / div_r1; + + if (ref_freq1 < 5000000) + break; + + for (div_f1 = 1; div_f1 <= 256; div_f1++) { + u32 vco_freq1 = ref_freq1 * div_f1; + + if (vco_freq1 < 350000000) + continue; + + if (vco_freq1 > 700000000) + break; + + for (div_q1 = 0; div_q1 < 7; div_q1++) { + u32 pllout_freq1 = vco_freq1 / (1 << div_q1); + int error = abs(pllout_freq1 - clk_pixel_pll); + + if (pllout_freq1 < 5000000) + break; + + if (pllout_freq1 > 700000000) + continue; + + if (error < min_error) { + min_error = error; + + /* final returned value is equal to calculated value - 1 + * because a value of 0 = divide by 1 */ + asic_pll->div_r0 = div_r0 - 1; + asic_pll->div_f0 = div_f0 - 1; + asic_pll->div_q0 = div_q0; + asic_pll->div_r1 = div_r1 - 1; + asic_pll->div_f1 = div_f1 - 1; + asic_pll->div_q1 = div_q1; + + asic_pll->range0 = ufx_calc_range(ref_freq0); + asic_pll->range1 = ufx_calc_range(ref_freq1); + + if (min_error == 0) + return; + } + } + } + } + } + } + } +} + +/* sets analog bit PLL configuration values */ +static int ufx_config_pix_clk(struct ufx_data *dev, u32 pixclock) +{ + struct pll_values asic_pll = {0}; + u32 value, clk_pixel, clk_pixel_pll; + int status; + + /* convert pixclock (in ps) to frequency (in Hz) */ + clk_pixel = PICOS2KHZ(pixclock) * 1000; + pr_debug("pixclock %d ps = clk_pixel %d Hz", pixclock, clk_pixel); + + /* clk_pixel = 1/2 clk_pixel_pll */ + clk_pixel_pll = clk_pixel * 2; + + ufx_calc_pll_values(clk_pixel_pll, &asic_pll); + + /* Keep BYPASS and RESET signals asserted until configured */ + status = ufx_reg_write(dev, 0x7000, 0x8000000F); + check_warn_return(status, "error writing 0x7000"); + + value = (asic_pll.div_f1 | (asic_pll.div_r1 << 8) | + (asic_pll.div_q1 << 16) | (asic_pll.range1 << 20)); + status = ufx_reg_write(dev, 0x7008, value); + check_warn_return(status, "error writing 0x7008"); + + value = (asic_pll.div_f0 | (asic_pll.div_r0 << 8) | + (asic_pll.div_q0 << 16) | (asic_pll.range0 << 20)); + status = ufx_reg_write(dev, 0x7004, value); + check_warn_return(status, "error writing 0x7004"); + + status = ufx_reg_clear_bits(dev, 0x7000, 0x00000005); + check_warn_return(status, + "error clearing PLL0 bypass bits in 0x7000"); + msleep(1); + + status = ufx_reg_clear_bits(dev, 0x7000, 0x0000000A); + check_warn_return(status, + "error clearing PLL1 bypass bits in 0x7000"); + msleep(1); + + status = ufx_reg_clear_bits(dev, 0x7000, 0x80000000); + check_warn_return(status, "error clearing gate bits in 0x7000"); + + return 0; +} + +static int ufx_set_vid_mode(struct ufx_data *dev, struct fb_var_screeninfo *var) +{ + u32 temp; + u16 h_total, h_active, h_blank_start, h_blank_end, h_sync_start, h_sync_end; + u16 v_total, v_active, v_blank_start, v_blank_end, v_sync_start, v_sync_end; + + int status = ufx_reg_write(dev, 0x8028, 0); + check_warn_return(status, "ufx_set_vid_mode error disabling RGB pad"); + + status = ufx_reg_write(dev, 0x8024, 0); + check_warn_return(status, "ufx_set_vid_mode error disabling VDAC"); + + /* shut everything down before changing timing */ + status = ufx_blank(dev, true); + check_warn_return(status, "ufx_set_vid_mode error blanking display"); + + status = ufx_disable(dev, true); + check_warn_return(status, "ufx_set_vid_mode error disabling display"); + + status = ufx_config_pix_clk(dev, var->pixclock); + check_warn_return(status, "ufx_set_vid_mode error configuring pixclock"); + + status = ufx_reg_write(dev, 0x2000, 0x00000104); + check_warn_return(status, "ufx_set_vid_mode error writing 0x2000"); + + /* set horizontal timings */ + h_total = var->xres + var->right_margin + var->hsync_len + var->left_margin; + h_active = var->xres; + h_blank_start = var->xres + var->right_margin; + h_blank_end = var->xres + var->right_margin + var->hsync_len; + h_sync_start = var->xres + var->right_margin; + h_sync_end = var->xres + var->right_margin + var->hsync_len; + + temp = ((h_total - 1) << 16) | (h_active - 1); + status = ufx_reg_write(dev, 0x2008, temp); + check_warn_return(status, "ufx_set_vid_mode error writing 0x2008"); + + temp = ((h_blank_start - 1) << 16) | (h_blank_end - 1); + status = ufx_reg_write(dev, 0x200C, temp); + check_warn_return(status, "ufx_set_vid_mode error writing 0x200C"); + + temp = ((h_sync_start - 1) << 16) | (h_sync_end - 1); + status = ufx_reg_write(dev, 0x2010, temp); + check_warn_return(status, "ufx_set_vid_mode error writing 0x2010"); + + /* set vertical timings */ + v_total = var->upper_margin + var->yres + var->lower_margin + var->vsync_len; + v_active = var->yres; + v_blank_start = var->yres + var->lower_margin; + v_blank_end = var->yres + var->lower_margin + var->vsync_len; + v_sync_start = var->yres + var->lower_margin; + v_sync_end = var->yres + var->lower_margin + var->vsync_len; + + temp = ((v_total - 1) << 16) | (v_active - 1); + status = ufx_reg_write(dev, 0x2014, temp); + check_warn_return(status, "ufx_set_vid_mode error writing 0x2014"); + + temp = ((v_blank_start - 1) << 16) | (v_blank_end - 1); + status = ufx_reg_write(dev, 0x2018, temp); + check_warn_return(status, "ufx_set_vid_mode error writing 0x2018"); + + temp = ((v_sync_start - 1) << 16) | (v_sync_end - 1); + status = ufx_reg_write(dev, 0x201C, temp); + check_warn_return(status, "ufx_set_vid_mode error writing 0x201C"); + + status = ufx_reg_write(dev, 0x2020, 0x00000000); + check_warn_return(status, "ufx_set_vid_mode error writing 0x2020"); + + status = ufx_reg_write(dev, 0x2024, 0x00000000); + check_warn_return(status, "ufx_set_vid_mode error writing 0x2024"); + + /* Set the frame length register (#pix * 2 bytes/pixel) */ + temp = var->xres * var->yres * 2; + temp = (temp + 7) & (~0x7); + status = ufx_reg_write(dev, 0x2028, temp); + check_warn_return(status, "ufx_set_vid_mode error writing 0x2028"); + + /* enable desired output interface & disable others */ + status = ufx_reg_write(dev, 0x2040, 0); + check_warn_return(status, "ufx_set_vid_mode error writing 0x2040"); + + status = ufx_reg_write(dev, 0x2044, 0); + check_warn_return(status, "ufx_set_vid_mode error writing 0x2044"); + + status = ufx_reg_write(dev, 0x2048, 0); + check_warn_return(status, "ufx_set_vid_mode error writing 0x2048"); + + /* set the sync polarities & enable bit */ + temp = 0x00000001; + if (var->sync & FB_SYNC_HOR_HIGH_ACT) + temp |= 0x00000010; + + if (var->sync & FB_SYNC_VERT_HIGH_ACT) + temp |= 0x00000008; + + status = ufx_reg_write(dev, 0x2040, temp); + check_warn_return(status, "ufx_set_vid_mode error writing 0x2040"); + + /* start everything back up */ + status = ufx_enable(dev, true); + check_warn_return(status, "ufx_set_vid_mode error enabling display"); + + /* Unblank the display */ + status = ufx_unblank(dev, true); + check_warn_return(status, "ufx_set_vid_mode error unblanking display"); + + /* enable RGB pad */ + status = ufx_reg_write(dev, 0x8028, 0x00000003); + check_warn_return(status, "ufx_set_vid_mode error enabling RGB pad"); + + /* enable VDAC */ + status = ufx_reg_write(dev, 0x8024, 0x00000007); + check_warn_return(status, "ufx_set_vid_mode error enabling VDAC"); + + return 0; +} + +static int ufx_ops_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + unsigned long start = vma->vm_start; + unsigned long size = vma->vm_end - vma->vm_start; + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + unsigned long page, pos; + + if (offset + size > info->fix.smem_len) + return -EINVAL; + + pos = (unsigned long)info->fix.smem_start + offset; + + pr_debug("mmap() framebuffer addr:%lu size:%lu\n", + pos, size); + + while (size > 0) { + page = vmalloc_to_pfn((void *)pos); + if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; + + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */ + return 0; +} + +static void ufx_raw_rect(struct ufx_data *dev, u16 *cmd, int x, int y, + int width, int height) +{ + size_t packed_line_len = ALIGN((width * 2), 4); + size_t packed_rect_len = packed_line_len * height; + int line; + + BUG_ON(!dev); + BUG_ON(!dev->info); + + /* command word */ + *((u32 *)&cmd[0]) = cpu_to_le32(0x01); + + /* length word */ + *((u32 *)&cmd[2]) = cpu_to_le32(packed_rect_len + 16); + + cmd[4] = cpu_to_le16(x); + cmd[5] = cpu_to_le16(y); + cmd[6] = cpu_to_le16(width); + cmd[7] = cpu_to_le16(height); + + /* frame base address */ + *((u32 *)&cmd[8]) = cpu_to_le32(0); + + /* color mode and horizontal resolution */ + cmd[10] = cpu_to_le16(0x4000 | dev->info->var.xres); + + /* vertical resolution */ + cmd[11] = cpu_to_le16(dev->info->var.yres); + + /* packed data */ + for (line = 0; line < height; line++) { + const int line_offset = dev->info->fix.line_length * (y + line); + const int byte_offset = line_offset + (x * BPP); + memcpy(&cmd[(24 + (packed_line_len * line)) / 2], + (char *)dev->info->fix.smem_start + byte_offset, width * BPP); + } +} + +int ufx_handle_damage(struct ufx_data *dev, int x, int y, + int width, int height) +{ + size_t packed_line_len = ALIGN((width * 2), 4); + int len, status, urb_lines, start_line = 0; + + if ((width <= 0) || (height <= 0) || + (x + width > dev->info->var.xres) || + (y + height > dev->info->var.yres)) + return -EINVAL; + + if (!atomic_read(&dev->usb_active)) + return 0; + + while (start_line < height) { + struct urb *urb = ufx_get_urb(dev); + if (!urb) { + pr_warn("ufx_handle_damage unable to get urb"); + return 0; + } + + /* assume we have enough space to transfer at least one line */ + BUG_ON(urb->transfer_buffer_length < (24 + (width * 2))); + + /* calculate the maximum number of lines we could fit in */ + urb_lines = (urb->transfer_buffer_length - 24) / packed_line_len; + + /* but we might not need this many */ + urb_lines = min(urb_lines, (height - start_line)); + + memset(urb->transfer_buffer, 0, urb->transfer_buffer_length); + + ufx_raw_rect(dev, urb->transfer_buffer, x, (y + start_line), width, urb_lines); + len = 24 + (packed_line_len * urb_lines); + + status = ufx_submit_urb(dev, urb, len); + check_warn_return(status, "Error submitting URB"); + + start_line += urb_lines; + } + + return 0; +} + +/* Path triggered by usermode clients who write to filesystem + * e.g. cat filename > /dev/fb1 + * Not used by X Windows or text-mode console. But useful for testing. + * Slow because of extra copy and we must assume all pixels dirty. */ +static ssize_t ufx_ops_write(struct fb_info *info, const char __user *buf, + size_t count, loff_t *ppos) +{ + ssize_t result; + struct ufx_data *dev = info->par; + u32 offset = (u32) *ppos; + + result = fb_sys_write(info, buf, count, ppos); + + if (result > 0) { + int start = max((int)(offset / info->fix.line_length) - 1, 0); + int lines = min((u32)((result / info->fix.line_length) + 1), + (u32)info->var.yres); + + ufx_handle_damage(dev, 0, start, info->var.xres, lines); + } + + return result; +} + +static void ufx_ops_copyarea(struct fb_info *info, + const struct fb_copyarea *area) +{ + + struct ufx_data *dev = info->par; + + sys_copyarea(info, area); + + ufx_handle_damage(dev, area->dx, area->dy, + area->width, area->height); +} + +static void ufx_ops_imageblit(struct fb_info *info, + const struct fb_image *image) +{ + struct ufx_data *dev = info->par; + + sys_imageblit(info, image); + + ufx_handle_damage(dev, image->dx, image->dy, + image->width, image->height); +} + +static void ufx_ops_fillrect(struct fb_info *info, + const struct fb_fillrect *rect) +{ + struct ufx_data *dev = info->par; + + sys_fillrect(info, rect); + + ufx_handle_damage(dev, rect->dx, rect->dy, rect->width, + rect->height); +} + +/* NOTE: fb_defio.c is holding info->fbdefio.mutex + * Touching ANY framebuffer memory that triggers a page fault + * in fb_defio will cause a deadlock, when it also tries to + * grab the same mutex. */ +static void ufx_dpy_deferred_io(struct fb_info *info, + struct list_head *pagelist) +{ + struct page *cur; + struct fb_deferred_io *fbdefio = info->fbdefio; + struct ufx_data *dev = info->par; + + if (!fb_defio) + return; + + if (!atomic_read(&dev->usb_active)) + return; + + /* walk the written page list and render each to device */ + list_for_each_entry(cur, &fbdefio->pagelist, lru) { + /* create a rectangle of full screen width that encloses the + * entire dirty framebuffer page */ + const int x = 0; + const int width = dev->info->var.xres; + const int y = (cur->index << PAGE_SHIFT) / (width * 2); + int height = (PAGE_SIZE / (width * 2)) + 1; + height = min(height, (int)(dev->info->var.yres - y)); + + BUG_ON(y >= dev->info->var.yres); + BUG_ON((y + height) > dev->info->var.yres); + + ufx_handle_damage(dev, x, y, width, height); + } +} + +static int ufx_ops_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg) +{ + struct ufx_data *dev = info->par; + struct dloarea *area = NULL; + + if (!atomic_read(&dev->usb_active)) + return 0; + + /* TODO: Update X server to get this from sysfs instead */ + if (cmd == UFX_IOCTL_RETURN_EDID) { + u8 __user *edid = (u8 __user *)arg; + if (copy_to_user(edid, dev->edid, dev->edid_size)) + return -EFAULT; + return 0; + } + + /* TODO: Help propose a standard fb.h ioctl to report mmap damage */ + if (cmd == UFX_IOCTL_REPORT_DAMAGE) { + /* If we have a damage-aware client, turn fb_defio "off" + * To avoid perf imact of unecessary page fault handling. + * Done by resetting the delay for this fb_info to a very + * long period. Pages will become writable and stay that way. + * Reset to normal value when all clients have closed this fb. + */ + if (info->fbdefio) + info->fbdefio->delay = UFX_DEFIO_WRITE_DISABLE; + + area = (struct dloarea *)arg; + + if (area->x < 0) + area->x = 0; + + if (area->x > info->var.xres) + area->x = info->var.xres; + + if (area->y < 0) + area->y = 0; + + if (area->y > info->var.yres) + area->y = info->var.yres; + + ufx_handle_damage(dev, area->x, area->y, area->w, area->h); + } + + return 0; +} + +/* taken from vesafb */ +static int +ufx_ops_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, struct fb_info *info) +{ + int err = 0; + + if (regno >= info->cmap.len) + return 1; + + if (regno < 16) { + if (info->var.red.offset == 10) { + /* 1:5:5:5 */ + ((u32 *) (info->pseudo_palette))[regno] = + ((red & 0xf800) >> 1) | + ((green & 0xf800) >> 6) | ((blue & 0xf800) >> 11); + } else { + /* 0:5:6:5 */ + ((u32 *) (info->pseudo_palette))[regno] = + ((red & 0xf800)) | + ((green & 0xfc00) >> 5) | ((blue & 0xf800) >> 11); + } + } + + return err; +} + +/* It's common for several clients to have framebuffer open simultaneously. + * e.g. both fbcon and X. Makes things interesting. + * Assumes caller is holding info->lock (for open and release at least) */ +static int ufx_ops_open(struct fb_info *info, int user) +{ + struct ufx_data *dev = info->par; + + /* fbcon aggressively connects to first framebuffer it finds, + * preventing other clients (X) from working properly. Usually + * not what the user wants. Fail by default with option to enable. */ + if (user == 0 && !console) + return -EBUSY; + + /* If the USB device is gone, we don't accept new opens */ + if (dev->virtualized) + return -ENODEV; + + dev->fb_count++; + + kref_get(&dev->kref); + + if (fb_defio && (info->fbdefio == NULL)) { + /* enable defio at last moment if not disabled by client */ + + struct fb_deferred_io *fbdefio; + + fbdefio = kmalloc(sizeof(struct fb_deferred_io), GFP_KERNEL); + + if (fbdefio) { + fbdefio->delay = UFX_DEFIO_WRITE_DELAY; + fbdefio->deferred_io = ufx_dpy_deferred_io; + } + + info->fbdefio = fbdefio; + fb_deferred_io_init(info); + } + + pr_debug("open /dev/fb%d user=%d fb_info=%p count=%d", + info->node, user, info, dev->fb_count); + + return 0; +} + +/* + * Called when all client interfaces to start transactions have been disabled, + * and all references to our device instance (ufx_data) are released. + * Every transaction must have a reference, so we know are fully spun down + */ +static void ufx_free(struct kref *kref) +{ + struct ufx_data *dev = container_of(kref, struct ufx_data, kref); + + /* this function will wait for all in-flight urbs to complete */ + if (dev->urbs.count > 0) + ufx_free_urb_list(dev); + + pr_debug("freeing ufx_data %p", dev); + + kfree(dev); +} + +static void ufx_release_urb_work(struct work_struct *work) +{ + struct urb_node *unode = container_of(work, struct urb_node, + release_urb_work.work); + + up(&unode->dev->urbs.limit_sem); +} + +static void ufx_free_framebuffer_work(struct work_struct *work) +{ + struct ufx_data *dev = container_of(work, struct ufx_data, + free_framebuffer_work.work); + struct fb_info *info = dev->info; + int node = info->node; + + unregister_framebuffer(info); + + if (info->cmap.len != 0) + fb_dealloc_cmap(&info->cmap); + if (info->monspecs.modedb) + fb_destroy_modedb(info->monspecs.modedb); + if (info->screen_base) + vfree(info->screen_base); + + fb_destroy_modelist(&info->modelist); + + dev->info = 0; + + /* Assume info structure is freed after this point */ + framebuffer_release(info); + + pr_debug("fb_info for /dev/fb%d has been freed", node); + + /* ref taken in probe() as part of registering framebfufer */ + kref_put(&dev->kref, ufx_free); +} + +/* + * Assumes caller is holding info->lock mutex (for open and release at least) + */ +static int ufx_ops_release(struct fb_info *info, int user) +{ + struct ufx_data *dev = info->par; + + dev->fb_count--; + + /* We can't free fb_info here - fbmem will touch it when we return */ + if (dev->virtualized && (dev->fb_count == 0)) + schedule_delayed_work(&dev->free_framebuffer_work, HZ); + + if ((dev->fb_count == 0) && (info->fbdefio)) { + fb_deferred_io_cleanup(info); + kfree(info->fbdefio); + info->fbdefio = NULL; + info->fbops->fb_mmap = ufx_ops_mmap; + } + + pr_debug("released /dev/fb%d user=%d count=%d", + info->node, user, dev->fb_count); + + kref_put(&dev->kref, ufx_free); + + return 0; +} + +/* Check whether a video mode is supported by the chip + * We start from monitor's modes, so don't need to filter that here */ +static int ufx_is_valid_mode(struct fb_videomode *mode, + struct fb_info *info) +{ + if ((mode->xres * mode->yres) > (2048 * 1152)) { + pr_debug("%dx%d too many pixels", + mode->xres, mode->yres); + return 0; + } + + if (mode->pixclock < 5000) { + pr_debug("%dx%d %dps pixel clock too fast", + mode->xres, mode->yres, mode->pixclock); + return 0; + } + + pr_debug("%dx%d (pixclk %dps %dMHz) valid mode", mode->xres, mode->yres, + mode->pixclock, (1000000 / mode->pixclock)); + return 1; +} + +static void ufx_var_color_format(struct fb_var_screeninfo *var) +{ + const struct fb_bitfield red = { 11, 5, 0 }; + const struct fb_bitfield green = { 5, 6, 0 }; + const struct fb_bitfield blue = { 0, 5, 0 }; + + var->bits_per_pixel = 16; + var->red = red; + var->green = green; + var->blue = blue; +} + +static int ufx_ops_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct fb_videomode mode; + + /* TODO: support dynamically changing framebuffer size */ + if ((var->xres * var->yres * 2) > info->fix.smem_len) + return -EINVAL; + + /* set device-specific elements of var unrelated to mode */ + ufx_var_color_format(var); + + fb_var_to_videomode(&mode, var); + + if (!ufx_is_valid_mode(&mode, info)) + return -EINVAL; + + return 0; +} + +static int ufx_ops_set_par(struct fb_info *info) +{ + struct ufx_data *dev = info->par; + int result; + u16 *pix_framebuffer; + int i; + + pr_debug("set_par mode %dx%d", info->var.xres, info->var.yres); + result = ufx_set_vid_mode(dev, &info->var); + + if ((result == 0) && (dev->fb_count == 0)) { + /* paint greenscreen */ + pix_framebuffer = (u16 *) info->screen_base; + for (i = 0; i < info->fix.smem_len / 2; i++) + pix_framebuffer[i] = 0x37e6; + + ufx_handle_damage(dev, 0, 0, info->var.xres, info->var.yres); + } + + /* re-enable defio if previously disabled by damage tracking */ + if (info->fbdefio) + info->fbdefio->delay = UFX_DEFIO_WRITE_DELAY; + + return result; +} + +/* In order to come back from full DPMS off, we need to set the mode again */ +static int ufx_ops_blank(int blank_mode, struct fb_info *info) +{ + struct ufx_data *dev = info->par; + ufx_set_vid_mode(dev, &info->var); + return 0; +} + +static struct fb_ops ufx_ops = { + .owner = THIS_MODULE, + .fb_read = fb_sys_read, + .fb_write = ufx_ops_write, + .fb_setcolreg = ufx_ops_setcolreg, + .fb_fillrect = ufx_ops_fillrect, + .fb_copyarea = ufx_ops_copyarea, + .fb_imageblit = ufx_ops_imageblit, + .fb_mmap = ufx_ops_mmap, + .fb_ioctl = ufx_ops_ioctl, + .fb_open = ufx_ops_open, + .fb_release = ufx_ops_release, + .fb_blank = ufx_ops_blank, + .fb_check_var = ufx_ops_check_var, + .fb_set_par = ufx_ops_set_par, +}; + +/* Assumes &info->lock held by caller + * Assumes no active clients have framebuffer open */ +static int ufx_realloc_framebuffer(struct ufx_data *dev, struct fb_info *info) +{ + int retval = -ENOMEM; + int old_len = info->fix.smem_len; + int new_len; + unsigned char *old_fb = info->screen_base; + unsigned char *new_fb; + + pr_debug("Reallocating framebuffer. Addresses will change!"); + + new_len = info->fix.line_length * info->var.yres; + + if (PAGE_ALIGN(new_len) > old_len) { + /* + * Alloc system memory for virtual framebuffer + */ + new_fb = vmalloc(new_len); + if (!new_fb) { + pr_err("Virtual framebuffer alloc failed"); + goto error; + } + + if (info->screen_base) { + memcpy(new_fb, old_fb, old_len); + vfree(info->screen_base); + } + + info->screen_base = new_fb; + info->fix.smem_len = PAGE_ALIGN(new_len); + info->fix.smem_start = (unsigned long) new_fb; + info->flags = smscufx_info_flags; + } + + retval = 0; + +error: + return retval; +} + +/* sets up I2C Controller for 100 Kbps, std. speed, 7-bit addr, master, + * restart enabled, but no start byte, enable controller */ +static int ufx_i2c_init(struct ufx_data *dev) +{ + u32 tmp; + + /* disable the controller before it can be reprogrammed */ + int status = ufx_reg_write(dev, 0x106C, 0x00); + check_warn_return(status, "failed to disable I2C"); + + /* Setup the clock count registers + * (12+1) = 13 clks @ 2.5 MHz = 5.2 uS */ + status = ufx_reg_write(dev, 0x1018, 12); + check_warn_return(status, "error writing 0x1018"); + + /* (6+8) = 14 clks @ 2.5 MHz = 5.6 uS */ + status = ufx_reg_write(dev, 0x1014, 6); + check_warn_return(status, "error writing 0x1014"); + + status = ufx_reg_read(dev, 0x1000, &tmp); + check_warn_return(status, "error reading 0x1000"); + + /* set speed to std mode */ + tmp &= ~(0x06); + tmp |= 0x02; + + /* 7-bit (not 10-bit) addressing */ + tmp &= ~(0x10); + + /* enable restart conditions and master mode */ + tmp |= 0x21; + + status = ufx_reg_write(dev, 0x1000, tmp); + check_warn_return(status, "error writing 0x1000"); + + /* Set normal tx using target address 0 */ + status = ufx_reg_clear_and_set_bits(dev, 0x1004, 0xC00, 0x000); + check_warn_return(status, "error setting TX mode bits in 0x1004"); + + /* Enable the controller */ + status = ufx_reg_write(dev, 0x106C, 0x01); + check_warn_return(status, "failed to enable I2C"); + + return 0; +} + +/* sets the I2C port mux and target address */ +static int ufx_i2c_configure(struct ufx_data *dev) +{ + int status = ufx_reg_write(dev, 0x106C, 0x00); + check_warn_return(status, "failed to disable I2C"); + + status = ufx_reg_write(dev, 0x3010, 0x00000000); + check_warn_return(status, "failed to write 0x3010"); + + /* A0h is std for any EDID, right shifted by one */ + status = ufx_reg_clear_and_set_bits(dev, 0x1004, 0x3FF, (0xA0 >> 1)); + check_warn_return(status, "failed to set TAR bits in 0x1004"); + + status = ufx_reg_write(dev, 0x106C, 0x01); + check_warn_return(status, "failed to enable I2C"); + + return 0; +} + +/* wait for BUSY to clear, with a timeout of 50ms with 10ms sleeps. if no + * monitor is connected, there is no error except for timeout */ +static int ufx_i2c_wait_busy(struct ufx_data *dev) +{ + u32 tmp; + int i, status; + + for (i = 0; i < 15; i++) { + status = ufx_reg_read(dev, 0x1100, &tmp); + check_warn_return(status, "0x1100 read failed"); + + /* if BUSY is clear, check for error */ + if ((tmp & 0x80000000) == 0) { + if (tmp & 0x20000000) { + pr_warn("I2C read failed, 0x1100=0x%08x", tmp); + return -EIO; + } + + return 0; + } + + /* perform the first 10 retries without delay */ + if (i >= 10) + msleep(10); + } + + pr_warn("I2C access timed out, resetting I2C hardware"); + status = ufx_reg_write(dev, 0x1100, 0x40000000); + check_warn_return(status, "0x1100 write failed"); + + return -ETIMEDOUT; +} + +/* reads a 128-byte EDID block from the currently selected port and TAR */ +static int ufx_read_edid(struct ufx_data *dev, u8 *edid, int edid_len) +{ + int i, j, status; + u32 *edid_u32 = (u32 *)edid; + + BUG_ON(edid_len != EDID_LENGTH); + + status = ufx_i2c_configure(dev); + if (status < 0) { + pr_err("ufx_i2c_configure failed"); + return status; + } + + memset(edid, 0xff, EDID_LENGTH); + + /* Read the 128-byte EDID as 2 bursts of 64 bytes */ + for (i = 0; i < 2; i++) { + u32 temp = 0x28070000 | (63 << 20) | (((u32)(i * 64)) << 8); + status = ufx_reg_write(dev, 0x1100, temp); + check_warn_return(status, "Failed to write 0x1100"); + + temp |= 0x80000000; + status = ufx_reg_write(dev, 0x1100, temp); + check_warn_return(status, "Failed to write 0x1100"); + + status = ufx_i2c_wait_busy(dev); + check_warn_return(status, "Timeout waiting for I2C BUSY to clear"); + + for (j = 0; j < 16; j++) { + u32 data_reg_addr = 0x1110 + (j * 4); + status = ufx_reg_read(dev, data_reg_addr, edid_u32++); + check_warn_return(status, "Error reading i2c data"); + } + } + + /* all FF's in the first 16 bytes indicates nothing is connected */ + for (i = 0; i < 16; i++) { + if (edid[i] != 0xFF) { + pr_debug("edid data read succesfully"); + return EDID_LENGTH; + } + } + + pr_warn("edid data contains all 0xff"); + return -ETIMEDOUT; +} + +/* 1) use sw default + * 2) Parse into various fb_info structs + * 3) Allocate virtual framebuffer memory to back highest res mode + * + * Parses EDID into three places used by various parts of fbdev: + * fb_var_screeninfo contains the timing of the monitor's preferred mode + * fb_info.monspecs is full parsed EDID info, including monspecs.modedb + * fb_info.modelist is a linked list of all monitor & VESA modes which work + * + * If EDID is not readable/valid, then modelist is all VESA modes, + * monspecs is NULL, and fb_var_screeninfo is set to safe VESA mode + * Returns 0 if successful */ +static int ufx_setup_modes(struct ufx_data *dev, struct fb_info *info, + char *default_edid, size_t default_edid_size) +{ + const struct fb_videomode *default_vmode = NULL; + u8 *edid; + int i, result = 0, tries = 3; + + if (info->dev) /* only use mutex if info has been registered */ + mutex_lock(&info->lock); + + edid = kmalloc(EDID_LENGTH, GFP_KERNEL); + if (!edid) { + result = -ENOMEM; + goto error; + } + + fb_destroy_modelist(&info->modelist); + memset(&info->monspecs, 0, sizeof(info->monspecs)); + + /* Try to (re)read EDID from hardware first + * EDID data may return, but not parse as valid + * Try again a few times, in case of e.g. analog cable noise */ + while (tries--) { + i = ufx_read_edid(dev, edid, EDID_LENGTH); + + if (i >= EDID_LENGTH) + fb_edid_to_monspecs(edid, &info->monspecs); + + if (info->monspecs.modedb_len > 0) { + dev->edid = edid; + dev->edid_size = i; + break; + } + } + + /* If that fails, use a previously returned EDID if available */ + if (info->monspecs.modedb_len == 0) { + pr_err("Unable to get valid EDID from device/display\n"); + + if (dev->edid) { + fb_edid_to_monspecs(dev->edid, &info->monspecs); + if (info->monspecs.modedb_len > 0) + pr_err("Using previously queried EDID\n"); + } + } + + /* If that fails, use the default EDID we were handed */ + if (info->monspecs.modedb_len == 0) { + if (default_edid_size >= EDID_LENGTH) { + fb_edid_to_monspecs(default_edid, &info->monspecs); + if (info->monspecs.modedb_len > 0) { + memcpy(edid, default_edid, default_edid_size); + dev->edid = edid; + dev->edid_size = default_edid_size; + pr_err("Using default/backup EDID\n"); + } + } + } + + /* If we've got modes, let's pick a best default mode */ + if (info->monspecs.modedb_len > 0) { + + for (i = 0; i < info->monspecs.modedb_len; i++) { + if (ufx_is_valid_mode(&info->monspecs.modedb[i], info)) + fb_add_videomode(&info->monspecs.modedb[i], + &info->modelist); + else /* if we've removed top/best mode */ + info->monspecs.misc &= ~FB_MISC_1ST_DETAIL; + } + + default_vmode = fb_find_best_display(&info->monspecs, + &info->modelist); + } + + /* If everything else has failed, fall back to safe default mode */ + if (default_vmode == NULL) { + + struct fb_videomode fb_vmode = {0}; + + /* Add the standard VESA modes to our modelist + * Since we don't have EDID, there may be modes that + * overspec monitor and/or are incorrect aspect ratio, etc. + * But at least the user has a chance to choose + */ + for (i = 0; i < VESA_MODEDB_SIZE; i++) { + if (ufx_is_valid_mode((struct fb_videomode *) + &vesa_modes[i], info)) + fb_add_videomode(&vesa_modes[i], + &info->modelist); + } + + /* default to resolution safe for projectors + * (since they are most common case without EDID) + */ + fb_vmode.xres = 800; + fb_vmode.yres = 600; + fb_vmode.refresh = 60; + default_vmode = fb_find_nearest_mode(&fb_vmode, + &info->modelist); + } + + /* If we have good mode and no active clients */ + if ((default_vmode != NULL) && (dev->fb_count == 0)) { + + fb_videomode_to_var(&info->var, default_vmode); + ufx_var_color_format(&info->var); + + /* with mode size info, we can now alloc our framebuffer */ + memcpy(&info->fix, &ufx_fix, sizeof(ufx_fix)); + info->fix.line_length = info->var.xres * + (info->var.bits_per_pixel / 8); + + result = ufx_realloc_framebuffer(dev, info); + + } else + result = -EINVAL; + +error: + if (edid && (dev->edid != edid)) + kfree(edid); + + if (info->dev) + mutex_unlock(&info->lock); + + return result; +} + +static int ufx_usb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *usbdev; + struct ufx_data *dev; + struct fb_info *info = 0; + int retval = -ENOMEM; + u32 id_rev, fpga_rev; + + /* usb initialization */ + usbdev = interface_to_usbdev(interface); + BUG_ON(!usbdev); + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + dev_err(&usbdev->dev, "ufx_usb_probe: failed alloc of dev struct\n"); + goto error; + } + + /* we need to wait for both usb and fbdev to spin down on disconnect */ + kref_init(&dev->kref); /* matching kref_put in usb .disconnect fn */ + kref_get(&dev->kref); /* matching kref_put in free_framebuffer_work */ + + dev->udev = usbdev; + dev->gdev = &usbdev->dev; /* our generic struct device * */ + usb_set_intfdata(interface, dev); + + dev_dbg(dev->gdev, "%s %s - serial #%s\n", + usbdev->manufacturer, usbdev->product, usbdev->serial); + dev_dbg(dev->gdev, "vid_%04x&pid_%04x&rev_%04x driver's ufx_data struct at %p\n", + usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, + usbdev->descriptor.bcdDevice, dev); + dev_dbg(dev->gdev, "console enable=%d\n", console); + dev_dbg(dev->gdev, "fb_defio enable=%d\n", fb_defio); + + if (!ufx_alloc_urb_list(dev, WRITES_IN_FLIGHT, MAX_TRANSFER)) { + retval = -ENOMEM; + dev_err(dev->gdev, "ufx_alloc_urb_list failed\n"); + goto error; + } + + /* We don't register a new USB class. Our client interface is fbdev */ + + /* allocates framebuffer driver structure, not framebuffer memory */ + info = framebuffer_alloc(0, &usbdev->dev); + if (!info) { + retval = -ENOMEM; + dev_err(dev->gdev, "framebuffer_alloc failed\n"); + goto error; + } + + dev->info = info; + info->par = dev; + info->pseudo_palette = dev->pseudo_palette; + info->fbops = &ufx_ops; + + retval = fb_alloc_cmap(&info->cmap, 256, 0); + if (retval < 0) { + dev_err(dev->gdev, "fb_alloc_cmap failed %x\n", retval); + goto error; + } + + INIT_DELAYED_WORK(&dev->free_framebuffer_work, + ufx_free_framebuffer_work); + + INIT_LIST_HEAD(&info->modelist); + + retval = ufx_reg_read(dev, 0x3000, &id_rev); + check_warn_goto_error(retval, "error %d reading 0x3000 register from device", retval); + dev_dbg(dev->gdev, "ID_REV register value 0x%08x", id_rev); + + retval = ufx_reg_read(dev, 0x3004, &fpga_rev); + check_warn_goto_error(retval, "error %d reading 0x3004 register from device", retval); + dev_dbg(dev->gdev, "FPGA_REV register value 0x%08x", fpga_rev); + + dev_dbg(dev->gdev, "resetting device"); + retval = ufx_lite_reset(dev); + check_warn_goto_error(retval, "error %d resetting device", retval); + + dev_dbg(dev->gdev, "configuring system clock"); + retval = ufx_config_sys_clk(dev); + check_warn_goto_error(retval, "error %d configuring system clock", retval); + + dev_dbg(dev->gdev, "configuring DDR2 controller"); + retval = ufx_config_ddr2(dev); + check_warn_goto_error(retval, "error %d initialising DDR2 controller", retval); + + dev_dbg(dev->gdev, "configuring I2C controller"); + retval = ufx_i2c_init(dev); + check_warn_goto_error(retval, "error %d initialising I2C controller", retval); + + dev_dbg(dev->gdev, "selecting display mode"); + retval = ufx_setup_modes(dev, info, NULL, 0); + check_warn_goto_error(retval, "unable to find common mode for display and adapter"); + + retval = ufx_reg_set_bits(dev, 0x4000, 0x00000001); + check_warn_goto_error(retval, "error %d enabling graphics engine", retval); + + /* ready to begin using device */ + atomic_set(&dev->usb_active, 1); + + dev_dbg(dev->gdev, "checking var"); + retval = ufx_ops_check_var(&info->var, info); + check_warn_goto_error(retval, "error %d ufx_ops_check_var", retval); + + dev_dbg(dev->gdev, "setting par"); + retval = ufx_ops_set_par(info); + check_warn_goto_error(retval, "error %d ufx_ops_set_par", retval); + + dev_dbg(dev->gdev, "registering framebuffer"); + retval = register_framebuffer(info); + check_warn_goto_error(retval, "error %d register_framebuffer", retval); + + dev_info(dev->gdev, "SMSC UDX USB device /dev/fb%d attached. %dx%d resolution." + " Using %dK framebuffer memory\n", info->node, + info->var.xres, info->var.yres, info->fix.smem_len >> 10); + + return 0; + +error: + if (dev) { + if (info) { + if (info->cmap.len != 0) + fb_dealloc_cmap(&info->cmap); + if (info->monspecs.modedb) + fb_destroy_modedb(info->monspecs.modedb); + if (info->screen_base) + vfree(info->screen_base); + + fb_destroy_modelist(&info->modelist); + + framebuffer_release(info); + } + + kref_put(&dev->kref, ufx_free); /* ref for framebuffer */ + kref_put(&dev->kref, ufx_free); /* last ref from kref_init */ + + /* dev has been deallocated. Do not dereference */ + } + + return retval; +} + +static void ufx_usb_disconnect(struct usb_interface *interface) +{ + struct ufx_data *dev; + struct fb_info *info; + + dev = usb_get_intfdata(interface); + info = dev->info; + + pr_debug("USB disconnect starting\n"); + + /* we virtualize until all fb clients release. Then we free */ + dev->virtualized = true; + + /* When non-active we'll update virtual framebuffer, but no new urbs */ + atomic_set(&dev->usb_active, 0); + + usb_set_intfdata(interface, NULL); + + /* if clients still have us open, will be freed on last close */ + if (dev->fb_count == 0) + schedule_delayed_work(&dev->free_framebuffer_work, 0); + + /* release reference taken by kref_init in probe() */ + kref_put(&dev->kref, ufx_free); + + /* consider ufx_data freed */ +} + +static struct usb_driver ufx_driver = { + .name = "smscufx", + .probe = ufx_usb_probe, + .disconnect = ufx_usb_disconnect, + .id_table = id_table, +}; + +static int __init ufx_module_init(void) +{ + int res; + + res = usb_register(&ufx_driver); + if (res) + err("usb_register failed. Error number %d", res); + + return res; +} + +static void __exit ufx_module_exit(void) +{ + usb_deregister(&ufx_driver); +} + +module_init(ufx_module_init); +module_exit(ufx_module_exit); + +static void ufx_urb_completion(struct urb *urb) +{ + struct urb_node *unode = urb->context; + struct ufx_data *dev = unode->dev; + unsigned long flags; + + /* sync/async unlink faults aren't errors */ + if (urb->status) { + if (!(urb->status == -ENOENT || + urb->status == -ECONNRESET || + urb->status == -ESHUTDOWN)) { + pr_err("%s - nonzero write bulk status received: %d\n", + __func__, urb->status); + atomic_set(&dev->lost_pixels, 1); + } + } + + urb->transfer_buffer_length = dev->urbs.size; /* reset to actual */ + + spin_lock_irqsave(&dev->urbs.lock, flags); + list_add_tail(&unode->entry, &dev->urbs.list); + dev->urbs.available++; + spin_unlock_irqrestore(&dev->urbs.lock, flags); + + /* When using fb_defio, we deadlock if up() is called + * while another is waiting. So queue to another process */ + if (fb_defio) + schedule_delayed_work(&unode->release_urb_work, 0); + else + up(&dev->urbs.limit_sem); +} + +static void ufx_free_urb_list(struct ufx_data *dev) +{ + int count = dev->urbs.count; + struct list_head *node; + struct urb_node *unode; + struct urb *urb; + int ret; + unsigned long flags; + + pr_debug("Waiting for completes and freeing all render urbs\n"); + + /* keep waiting and freeing, until we've got 'em all */ + while (count--) { + /* Getting interrupted means a leak, but ok at shutdown*/ + ret = down_interruptible(&dev->urbs.limit_sem); + if (ret) + break; + + spin_lock_irqsave(&dev->urbs.lock, flags); + + node = dev->urbs.list.next; /* have reserved one with sem */ + list_del_init(node); + + spin_unlock_irqrestore(&dev->urbs.lock, flags); + + unode = list_entry(node, struct urb_node, entry); + urb = unode->urb; + + /* Free each separately allocated piece */ + usb_free_coherent(urb->dev, dev->urbs.size, + urb->transfer_buffer, urb->transfer_dma); + usb_free_urb(urb); + kfree(node); + } +} + +static int ufx_alloc_urb_list(struct ufx_data *dev, int count, size_t size) +{ + int i = 0; + struct urb *urb; + struct urb_node *unode; + char *buf; + + spin_lock_init(&dev->urbs.lock); + + dev->urbs.size = size; + INIT_LIST_HEAD(&dev->urbs.list); + + while (i < count) { + unode = kzalloc(sizeof(struct urb_node), GFP_KERNEL); + if (!unode) + break; + unode->dev = dev; + + INIT_DELAYED_WORK(&unode->release_urb_work, + ufx_release_urb_work); + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + kfree(unode); + break; + } + unode->urb = urb; + + buf = usb_alloc_coherent(dev->udev, size, GFP_KERNEL, + &urb->transfer_dma); + if (!buf) { + kfree(unode); + usb_free_urb(urb); + break; + } + + /* urb->transfer_buffer_length set to actual before submit */ + usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, 1), + buf, size, ufx_urb_completion, unode); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + list_add_tail(&unode->entry, &dev->urbs.list); + + i++; + } + + sema_init(&dev->urbs.limit_sem, i); + dev->urbs.count = i; + dev->urbs.available = i; + + pr_debug("allocated %d %d byte urbs\n", i, (int) size); + + return i; +} + +static struct urb *ufx_get_urb(struct ufx_data *dev) +{ + int ret = 0; + struct list_head *entry; + struct urb_node *unode; + struct urb *urb = NULL; + unsigned long flags; + + /* Wait for an in-flight buffer to complete and get re-queued */ + ret = down_timeout(&dev->urbs.limit_sem, GET_URB_TIMEOUT); + if (ret) { + atomic_set(&dev->lost_pixels, 1); + pr_warn("wait for urb interrupted: %x available: %d\n", + ret, dev->urbs.available); + goto error; + } + + spin_lock_irqsave(&dev->urbs.lock, flags); + + BUG_ON(list_empty(&dev->urbs.list)); /* reserved one with limit_sem */ + entry = dev->urbs.list.next; + list_del_init(entry); + dev->urbs.available--; + + spin_unlock_irqrestore(&dev->urbs.lock, flags); + + unode = list_entry(entry, struct urb_node, entry); + urb = unode->urb; + +error: + return urb; +} + +static int ufx_submit_urb(struct ufx_data *dev, struct urb *urb, size_t len) +{ + int ret; + + BUG_ON(len > dev->urbs.size); + + urb->transfer_buffer_length = len; /* set to actual payload len */ + ret = usb_submit_urb(urb, GFP_KERNEL); + if (ret) { + ufx_urb_completion(urb); /* because no one else will */ + atomic_set(&dev->lost_pixels, 1); + pr_err("usb_submit_urb error %x\n", ret); + } + return ret; +} + +module_param(console, bool, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP); +MODULE_PARM_DESC(console, "Allow fbcon to be used on this display"); + +module_param(fb_defio, bool, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP); +MODULE_PARM_DESC(fb_defio, "Enable fb_defio mmap support"); + +MODULE_AUTHOR("Steve Glendinning <steve.glendinning@smsc.com>"); +MODULE_DESCRIPTION("SMSC UFX kernel framebuffer driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/tmiofb.c b/drivers/video/tmiofb.c index cd1c4dcef8fd..8e4a446b5ed1 100644 --- a/drivers/video/tmiofb.c +++ b/drivers/video/tmiofb.c @@ -744,7 +744,7 @@ static int __devinit tmiofb_probe(struct platform_device *dev) goto err_ioremap_vram; } - retval = request_irq(irq, &tmiofb_irq, IRQF_DISABLED, + retval = request_irq(irq, &tmiofb_irq, 0, dev_name(&dev->dev), info); if (retval) diff --git a/drivers/video/tridentfb.c b/drivers/video/tridentfb.c index c6c77562839d..34cf019bba44 100644 --- a/drivers/video/tridentfb.c +++ b/drivers/video/tridentfb.c @@ -987,8 +987,8 @@ static int tridentfb_pan_display(struct fb_var_screeninfo *var, unsigned int offset; debug("enter\n"); - offset = (var->xoffset + (var->yoffset * var->xres_virtual)) - * var->bits_per_pixel / 32; + offset = (var->xoffset + (var->yoffset * info->var.xres_virtual)) + * info->var.bits_per_pixel / 32; set_screen_start(par, offset); debug("exit\n"); return 0; diff --git a/drivers/video/udlfb.c b/drivers/video/udlfb.c index 087fc9960bb9..3473e75ce785 100644 --- a/drivers/video/udlfb.c +++ b/drivers/video/udlfb.c @@ -48,20 +48,30 @@ static const u32 udlfb_info_flags = FBINFO_DEFAULT | FBINFO_READS_FAST | FBINFO_HWACCEL_COPYAREA | FBINFO_MISC_ALWAYS_SETPAR; /* - * There are many DisplayLink-based products, all with unique PIDs. We are able - * to support all volume ones (circa 2009) with a single driver, so we match - * globally on VID. TODO: Probe() needs to detect when we might be running - * "future" chips, and bail on those, so a compatible driver can match. + * There are many DisplayLink-based graphics products, all with unique PIDs. + * So we match on DisplayLink's VID + Vendor-Defined Interface Class (0xff) + * We also require a match on SubClass (0x00) and Protocol (0x00), + * which is compatible with all known USB 2.0 era graphics chips and firmware, + * but allows DisplayLink to increment those for any future incompatible chips */ static struct usb_device_id id_table[] = { - {.idVendor = 0x17e9, .match_flags = USB_DEVICE_ID_MATCH_VENDOR,}, + {.idVendor = 0x17e9, + .bInterfaceClass = 0xff, + .bInterfaceSubClass = 0x00, + .bInterfaceProtocol = 0x00, + .match_flags = USB_DEVICE_ID_MATCH_VENDOR | + USB_DEVICE_ID_MATCH_INT_CLASS | + USB_DEVICE_ID_MATCH_INT_SUBCLASS | + USB_DEVICE_ID_MATCH_INT_PROTOCOL, + }, {}, }; MODULE_DEVICE_TABLE(usb, id_table); /* module options */ -static int console; /* Optionally allow fbcon to consume first framebuffer */ -static int fb_defio; /* Optionally enable experimental fb_defio mmap support */ +static int console = 1; /* Allow fbcon to open framebuffer */ +static int fb_defio = 1; /* Detect mmap writes using page faults */ +static int shadow = 1; /* Optionally disable shadow framebuffer */ /* dlfb keeps a list of urbs for efficient bulk transfers */ static void dlfb_urb_completion(struct urb *urb); @@ -94,17 +104,39 @@ static char *dlfb_vidreg_unlock(char *buf) } /* - * On/Off for driving the DisplayLink framebuffer to the display - * 0x00 H and V sync on - * 0x01 H and V sync off (screen blank but powered) - * 0x07 DPMS powerdown (requires modeset to come back) + * Map FB_BLANK_* to DisplayLink register + * DLReg FB_BLANK_* + * ----- ----------------------------- + * 0x00 FB_BLANK_UNBLANK (0) + * 0x01 FB_BLANK (1) + * 0x03 FB_BLANK_VSYNC_SUSPEND (2) + * 0x05 FB_BLANK_HSYNC_SUSPEND (3) + * 0x07 FB_BLANK_POWERDOWN (4) Note: requires modeset to come back */ -static char *dlfb_enable_hvsync(char *buf, bool enable) +static char *dlfb_blanking(char *buf, int fb_blank) { - if (enable) - return dlfb_set_register(buf, 0x1F, 0x00); - else - return dlfb_set_register(buf, 0x1F, 0x07); + u8 reg; + + switch (fb_blank) { + case FB_BLANK_POWERDOWN: + reg = 0x07; + break; + case FB_BLANK_HSYNC_SUSPEND: + reg = 0x05; + break; + case FB_BLANK_VSYNC_SUSPEND: + reg = 0x03; + break; + case FB_BLANK_NORMAL: + reg = 0x01; + break; + default: + reg = 0x00; + } + + buf = dlfb_set_register(buf, 0x1F, reg); + + return buf; } static char *dlfb_set_color_depth(char *buf, u8 selection) @@ -272,13 +304,15 @@ static int dlfb_set_video_mode(struct dlfb_data *dev, wrptr = dlfb_set_base8bpp(wrptr, dev->info->fix.smem_len); wrptr = dlfb_set_vid_cmds(wrptr, var); - wrptr = dlfb_enable_hvsync(wrptr, true); + wrptr = dlfb_blanking(wrptr, FB_BLANK_UNBLANK); wrptr = dlfb_vidreg_unlock(wrptr); writesize = wrptr - buf; retval = dlfb_submit_urb(dev, urb, writesize); + dev->blank_mode = FB_BLANK_UNBLANK; + return retval; } @@ -752,14 +786,13 @@ static int dlfb_ops_ioctl(struct fb_info *info, unsigned int cmd, { struct dlfb_data *dev = info->par; - struct dloarea *area = NULL; if (!atomic_read(&dev->usb_active)) return 0; /* TODO: Update X server to get this from sysfs instead */ if (cmd == DLFB_IOCTL_RETURN_EDID) { - char *edid = (char *)arg; + void __user *edid = (void __user *)arg; if (copy_to_user(edid, dev->edid, dev->edid_size)) return -EFAULT; return 0; @@ -767,6 +800,11 @@ static int dlfb_ops_ioctl(struct fb_info *info, unsigned int cmd, /* TODO: Help propose a standard fb.h ioctl to report mmap damage */ if (cmd == DLFB_IOCTL_REPORT_DAMAGE) { + struct dloarea area; + + if (copy_from_user(&area, (void __user *)arg, + sizeof(struct dloarea))) + return -EFAULT; /* * If we have a damage-aware client, turn fb_defio "off" @@ -778,21 +816,19 @@ static int dlfb_ops_ioctl(struct fb_info *info, unsigned int cmd, if (info->fbdefio) info->fbdefio->delay = DL_DEFIO_WRITE_DISABLE; - area = (struct dloarea *)arg; + if (area.x < 0) + area.x = 0; - if (area->x < 0) - area->x = 0; + if (area.x > info->var.xres) + area.x = info->var.xres; - if (area->x > info->var.xres) - area->x = info->var.xres; + if (area.y < 0) + area.y = 0; - if (area->y < 0) - area->y = 0; + if (area.y > info->var.yres) + area.y = info->var.yres; - if (area->y > info->var.yres) - area->y = info->var.yres; - - dlfb_handle_damage(dev, area->x, area->y, area->w, area->h, + dlfb_handle_damage(dev, area.x, area.y, area.w, area.h, info->screen_base); } @@ -840,7 +876,7 @@ static int dlfb_ops_open(struct fb_info *info, int user) * preventing other clients (X) from working properly. Usually * not what the user wants. Fail by default with option to enable. */ - if ((user == 0) & (!console)) + if ((user == 0) && (!console)) return -EBUSY; /* If the USB device is gone, we don't accept new opens */ @@ -1039,32 +1075,57 @@ static int dlfb_ops_set_par(struct fb_info *info) return result; } +/* To fonzi the jukebox (e.g. make blanking changes take effect) */ +static char *dlfb_dummy_render(char *buf) +{ + *buf++ = 0xAF; + *buf++ = 0x6A; /* copy */ + *buf++ = 0x00; /* from address*/ + *buf++ = 0x00; + *buf++ = 0x00; + *buf++ = 0x01; /* one pixel */ + *buf++ = 0x00; /* to address */ + *buf++ = 0x00; + *buf++ = 0x00; + return buf; +} + /* * In order to come back from full DPMS off, we need to set the mode again */ static int dlfb_ops_blank(int blank_mode, struct fb_info *info) { struct dlfb_data *dev = info->par; + char *bufptr; + struct urb *urb; - if (blank_mode != FB_BLANK_UNBLANK) { - char *bufptr; - struct urb *urb; - - urb = dlfb_get_urb(dev); - if (!urb) - return 0; + pr_info("/dev/fb%d FB_BLANK mode %d --> %d\n", + info->node, dev->blank_mode, blank_mode); - bufptr = (char *) urb->transfer_buffer; - bufptr = dlfb_vidreg_lock(bufptr); - bufptr = dlfb_enable_hvsync(bufptr, false); - bufptr = dlfb_vidreg_unlock(bufptr); + if ((dev->blank_mode == FB_BLANK_POWERDOWN) && + (blank_mode != FB_BLANK_POWERDOWN)) { - dlfb_submit_urb(dev, urb, bufptr - - (char *) urb->transfer_buffer); - } else { + /* returning from powerdown requires a fresh modeset */ dlfb_set_video_mode(dev, &info->var); } + urb = dlfb_get_urb(dev); + if (!urb) + return 0; + + bufptr = (char *) urb->transfer_buffer; + bufptr = dlfb_vidreg_lock(bufptr); + bufptr = dlfb_blanking(bufptr, blank_mode); + bufptr = dlfb_vidreg_unlock(bufptr); + + /* seems like a render op is needed to have blank change take effect */ + bufptr = dlfb_dummy_render(bufptr); + + dlfb_submit_urb(dev, urb, bufptr - + (char *) urb->transfer_buffer); + + dev->blank_mode = blank_mode; + return 0; } @@ -1097,7 +1158,7 @@ static int dlfb_realloc_framebuffer(struct dlfb_data *dev, struct fb_info *info) int new_len; unsigned char *old_fb = info->screen_base; unsigned char *new_fb; - unsigned char *new_back; + unsigned char *new_back = 0; pr_warn("Reallocating framebuffer. Addresses will change!\n"); @@ -1129,7 +1190,8 @@ static int dlfb_realloc_framebuffer(struct dlfb_data *dev, struct fb_info *info) * But with imperfect damage info we may send pixels over USB * that were, in fact, unchanged - wasting limited USB bandwidth */ - new_back = vzalloc(new_len); + if (shadow) + new_back = vzalloc(new_len); if (!new_back) pr_info("No shadow/backing buffer allocated\n"); else { @@ -1430,21 +1492,30 @@ static int dlfb_select_std_channel(struct dlfb_data *dev) } static int dlfb_parse_vendor_descriptor(struct dlfb_data *dev, - struct usb_device *usbdev) + struct usb_interface *interface) { char *desc; char *buf; char *desc_end; - u8 total_len = 0; + int total_len = 0; buf = kzalloc(MAX_VENDOR_DESCRIPTOR_SIZE, GFP_KERNEL); if (!buf) return false; desc = buf; - total_len = usb_get_descriptor(usbdev, 0x5f, /* vendor specific */ - 0, desc, MAX_VENDOR_DESCRIPTOR_SIZE); + total_len = usb_get_descriptor(interface_to_usbdev(interface), + 0x5f, /* vendor specific */ + 0, desc, MAX_VENDOR_DESCRIPTOR_SIZE); + + /* if not found, look in configuration descriptor */ + if (total_len < 0) { + if (0 == usb_get_extra_descriptor(interface->cur_altsetting, + 0x5f, &desc)) + total_len = (int) desc[0]; + } + if (total_len > 5) { pr_info("vendor descriptor length:%x data:%02x %02x %02x %02x" \ "%02x %02x %02x %02x %02x %02x %02x\n", @@ -1485,6 +1556,8 @@ static int dlfb_parse_vendor_descriptor(struct dlfb_data *dev, } desc += length; } + } else { + pr_info("vendor descriptor not available (%d)\n", total_len); } goto success; @@ -1531,10 +1604,11 @@ static int dlfb_usb_probe(struct usb_interface *interface, usbdev->descriptor.bcdDevice, dev); pr_info("console enable=%d\n", console); pr_info("fb_defio enable=%d\n", fb_defio); + pr_info("shadow enable=%d\n", shadow); dev->sku_pixel_limit = 2048 * 1152; /* default to maximum */ - if (!dlfb_parse_vendor_descriptor(dev, usbdev)) { + if (!dlfb_parse_vendor_descriptor(dev, interface)) { pr_err("firmware not recognized. Assume incompatible device\n"); goto error; } @@ -1548,7 +1622,7 @@ static int dlfb_usb_probe(struct usb_interface *interface, /* We don't register a new USB class. Our client interface is fbdev */ /* allocates framebuffer driver structure, not framebuffer memory */ - info = framebuffer_alloc(0, &usbdev->dev); + info = framebuffer_alloc(0, &interface->dev); if (!info) { retval = -ENOMEM; pr_err("framebuffer_alloc failed\n"); @@ -1883,10 +1957,13 @@ static int dlfb_submit_urb(struct dlfb_data *dev, struct urb *urb, size_t len) } module_param(console, bool, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP); -MODULE_PARM_DESC(console, "Allow fbcon to consume first framebuffer found"); +MODULE_PARM_DESC(console, "Allow fbcon to open framebuffer"); module_param(fb_defio, bool, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP); -MODULE_PARM_DESC(fb_defio, "Enable fb_defio mmap support. *Experimental*"); +MODULE_PARM_DESC(fb_defio, "Page fault detection of mmap writes"); + +module_param(shadow, bool, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP); +MODULE_PARM_DESC(shadow, "Shadow vid mem. Disable to save mem but lose perf"); MODULE_AUTHOR("Roberto De Ioris <roberto@unbit.it>, " "Jaya Kumar <jayakumar.lkml@gmail.com>, " diff --git a/drivers/video/valkyriefb.c b/drivers/video/valkyriefb.c index 6b52bf65f0b5..3f5a041601da 100644 --- a/drivers/video/valkyriefb.c +++ b/drivers/video/valkyriefb.c @@ -555,7 +555,7 @@ static int __init valkyrie_init_info(struct fb_info *info, /* - * Parse user speficied options (`video=valkyriefb:') + * Parse user specified options (`video=valkyriefb:') */ int __init valkyriefb_setup(char *options) { diff --git a/drivers/video/vfb.c b/drivers/video/vfb.c index bc67251f1a2f..bf2f78065cf9 100644 --- a/drivers/video/vfb.c +++ b/drivers/video/vfb.c @@ -395,8 +395,8 @@ static int vfb_pan_display(struct fb_var_screeninfo *var, || var->xoffset) return -EINVAL; } else { - if (var->xoffset + var->xres > info->var.xres_virtual || - var->yoffset + var->yres > info->var.yres_virtual) + if (var->xoffset + info->var.xres > info->var.xres_virtual || + var->yoffset + info->var.yres > info->var.yres_virtual) return -EINVAL; } info->var.xoffset = var->xoffset; diff --git a/drivers/video/vga16fb.c b/drivers/video/vga16fb.c index 305c975b1787..0267acd8dc83 100644 --- a/drivers/video/vga16fb.c +++ b/drivers/video/vga16fb.c @@ -207,7 +207,7 @@ static void vga16fb_pan_var(struct fb_info *info, * granularity if someone supports xoffset in bit resolution */ vga_io_r(VGA_IS1_RC); /* reset flip-flop */ vga_io_w(VGA_ATT_IW, VGA_ATC_PEL); - if (var->bits_per_pixel == 8) + if (info->var.bits_per_pixel == 8) vga_io_w(VGA_ATT_IW, (xoffset & 3) << 1); else vga_io_w(VGA_ATT_IW, xoffset & 7); diff --git a/drivers/video/via/dvi.c b/drivers/video/via/dvi.c index b1f364745ca0..9138e517267c 100644 --- a/drivers/video/via/dvi.c +++ b/drivers/video/via/dvi.c @@ -172,30 +172,20 @@ static int tmds_register_read_bytes(int index, u8 *buff, int buff_len) } /* DVI Set Mode */ -void viafb_dvi_set_mode(struct VideoModeTable *mode, int mode_bpp, - int set_iga) +void viafb_dvi_set_mode(const struct fb_var_screeninfo *var, int iga) { - struct VideoModeTable *rb_mode; - struct crt_mode_table *pDviTiming; - unsigned long desirePixelClock, maxPixelClock; - pDviTiming = mode->crtc; - desirePixelClock = pDviTiming->refresh_rate - * pDviTiming->crtc.hor_total * pDviTiming->crtc.ver_total - / 1000000; - maxPixelClock = (unsigned long)viaparinfo-> - tmds_setting_info->max_pixel_clock; - - DEBUG_MSG(KERN_INFO "\nDVI_set_mode!!\n"); - - if ((maxPixelClock != 0) && (desirePixelClock > maxPixelClock)) { - rb_mode = viafb_get_rb_mode(mode->crtc[0].crtc.hor_addr, - mode->crtc[0].crtc.ver_addr); - if (rb_mode) { - mode = rb_mode; - pDviTiming = rb_mode->crtc; - } + struct fb_var_screeninfo dvi_var = *var; + struct crt_mode_table *rb_mode; + int maxPixelClock; + + maxPixelClock = viaparinfo->shared->tmds_setting_info.max_pixel_clock; + if (maxPixelClock && PICOS2KHZ(var->pixclock) / 1000 > maxPixelClock) { + rb_mode = viafb_get_best_rb_mode(var->xres, var->yres, 60); + if (rb_mode) + viafb_fill_var_timing_info(&dvi_var, rb_mode); } - viafb_fill_crtc_timing(pDviTiming, mode, mode_bpp / 8, set_iga); + + viafb_fill_crtc_timing(&dvi_var, iga); } /* Sense DVI Connector */ diff --git a/drivers/video/via/dvi.h b/drivers/video/via/dvi.h index f473dd010977..e2116aaf797a 100644 --- a/drivers/video/via/dvi.h +++ b/drivers/video/via/dvi.h @@ -59,7 +59,6 @@ void viafb_dvi_enable(void); bool __devinit viafb_tmds_trasmitter_identify(void); void __devinit viafb_init_dvi_size(struct tmds_chip_information *tmds_chip, struct tmds_setting_information *tmds_setting); -void viafb_dvi_set_mode(struct VideoModeTable *videoMode, int mode_bpp, - int set_iga); +void viafb_dvi_set_mode(const struct fb_var_screeninfo *var, int iga); #endif /* __DVI_H__ */ diff --git a/drivers/video/via/global.c b/drivers/video/via/global.c index e10d8249534c..3102171c1674 100644 --- a/drivers/video/via/global.c +++ b/drivers/video/via/global.c @@ -35,6 +35,8 @@ int viafb_LCD_ON ; int viafb_LCD2_ON; int viafb_SAMM_ON; int viafb_dual_fb; +unsigned int viafb_second_xres = 640; +unsigned int viafb_second_yres = 480; int viafb_hotplug_Xres = 640; int viafb_hotplug_Yres = 480; int viafb_hotplug_bpp = 32; diff --git a/drivers/video/via/global.h b/drivers/video/via/global.h index ff969dc34593..275dbbbd6b81 100644 --- a/drivers/video/via/global.h +++ b/drivers/video/via/global.h @@ -67,6 +67,8 @@ extern int viafb_lcd_dsp_method; extern int viafb_lcd_mode; extern int viafb_CRT_ON; +extern unsigned int viafb_second_xres; +extern unsigned int viafb_second_yres; extern int viafb_hotplug_Xres; extern int viafb_hotplug_Yres; extern int viafb_hotplug_bpp; diff --git a/drivers/video/via/hw.c b/drivers/video/via/hw.c index 47b13535ed2b..d5aaca9cfa7e 100644 --- a/drivers/video/via/hw.c +++ b/drivers/video/via/hw.c @@ -191,67 +191,6 @@ static struct fetch_count fetch_count_reg = { {IGA2_FETCH_COUNT_REG_NUM, {{CR65, 0, 7}, {CR67, 2, 3} } } }; -static struct iga1_crtc_timing iga1_crtc_reg = { - /* IGA1 Horizontal Total */ - {IGA1_HOR_TOTAL_REG_NUM, {{CR00, 0, 7}, {CR36, 3, 3} } }, - /* IGA1 Horizontal Addressable Video */ - {IGA1_HOR_ADDR_REG_NUM, {{CR01, 0, 7} } }, - /* IGA1 Horizontal Blank Start */ - {IGA1_HOR_BLANK_START_REG_NUM, {{CR02, 0, 7} } }, - /* IGA1 Horizontal Blank End */ - {IGA1_HOR_BLANK_END_REG_NUM, - {{CR03, 0, 4}, {CR05, 7, 7}, {CR33, 5, 5} } }, - /* IGA1 Horizontal Sync Start */ - {IGA1_HOR_SYNC_START_REG_NUM, {{CR04, 0, 7}, {CR33, 4, 4} } }, - /* IGA1 Horizontal Sync End */ - {IGA1_HOR_SYNC_END_REG_NUM, {{CR05, 0, 4} } }, - /* IGA1 Vertical Total */ - {IGA1_VER_TOTAL_REG_NUM, - {{CR06, 0, 7}, {CR07, 0, 0}, {CR07, 5, 5}, {CR35, 0, 0} } }, - /* IGA1 Vertical Addressable Video */ - {IGA1_VER_ADDR_REG_NUM, - {{CR12, 0, 7}, {CR07, 1, 1}, {CR07, 6, 6}, {CR35, 2, 2} } }, - /* IGA1 Vertical Blank Start */ - {IGA1_VER_BLANK_START_REG_NUM, - {{CR15, 0, 7}, {CR07, 3, 3}, {CR09, 5, 5}, {CR35, 3, 3} } }, - /* IGA1 Vertical Blank End */ - {IGA1_VER_BLANK_END_REG_NUM, {{CR16, 0, 7} } }, - /* IGA1 Vertical Sync Start */ - {IGA1_VER_SYNC_START_REG_NUM, - {{CR10, 0, 7}, {CR07, 2, 2}, {CR07, 7, 7}, {CR35, 1, 1} } }, - /* IGA1 Vertical Sync End */ - {IGA1_VER_SYNC_END_REG_NUM, {{CR11, 0, 3} } } -}; - -static struct iga2_crtc_timing iga2_crtc_reg = { - /* IGA2 Horizontal Total */ - {IGA2_HOR_TOTAL_REG_NUM, {{CR50, 0, 7}, {CR55, 0, 3} } }, - /* IGA2 Horizontal Addressable Video */ - {IGA2_HOR_ADDR_REG_NUM, {{CR51, 0, 7}, {CR55, 4, 6} } }, - /* IGA2 Horizontal Blank Start */ - {IGA2_HOR_BLANK_START_REG_NUM, {{CR52, 0, 7}, {CR54, 0, 2} } }, - /* IGA2 Horizontal Blank End */ - {IGA2_HOR_BLANK_END_REG_NUM, - {{CR53, 0, 7}, {CR54, 3, 5}, {CR5D, 6, 6} } }, - /* IGA2 Horizontal Sync Start */ - {IGA2_HOR_SYNC_START_REG_NUM, - {{CR56, 0, 7}, {CR54, 6, 7}, {CR5C, 7, 7}, {CR5D, 7, 7} } }, - /* IGA2 Horizontal Sync End */ - {IGA2_HOR_SYNC_END_REG_NUM, {{CR57, 0, 7}, {CR5C, 6, 6} } }, - /* IGA2 Vertical Total */ - {IGA2_VER_TOTAL_REG_NUM, {{CR58, 0, 7}, {CR5D, 0, 2} } }, - /* IGA2 Vertical Addressable Video */ - {IGA2_VER_ADDR_REG_NUM, {{CR59, 0, 7}, {CR5D, 3, 5} } }, - /* IGA2 Vertical Blank Start */ - {IGA2_VER_BLANK_START_REG_NUM, {{CR5A, 0, 7}, {CR5C, 0, 2} } }, - /* IGA2 Vertical Blank End */ - {IGA2_VER_BLANK_END_REG_NUM, {{CR5B, 0, 7}, {CR5C, 3, 5} } }, - /* IGA2 Vertical Sync Start */ - {IGA2_VER_SYNC_START_REG_NUM, {{CR5E, 0, 7}, {CR5F, 5, 7} } }, - /* IGA2 Vertical Sync End */ - {IGA2_VER_SYNC_END_REG_NUM, {{CR5F, 0, 4} } } -}; - static struct rgbLUT palLUT_table[] = { /* {R,G,B} */ /* Index 0x00~0x03 */ @@ -1528,302 +1467,40 @@ void viafb_set_vclock(u32 clk, int set_iga) via_write_misc_reg_mask(0x0C, 0x0C); /* select external clock */ } -void viafb_load_crtc_timing(struct display_timing device_timing, - int set_iga) +static struct display_timing var_to_timing(const struct fb_var_screeninfo *var) { - int i; - int viafb_load_reg_num = 0; - int reg_value = 0; - struct io_register *reg = NULL; - - viafb_unlock_crt(); - - for (i = 0; i < 12; i++) { - if (set_iga == IGA1) { - switch (i) { - case H_TOTAL_INDEX: - reg_value = - IGA1_HOR_TOTAL_FORMULA(device_timing. - hor_total); - viafb_load_reg_num = - iga1_crtc_reg.hor_total.reg_num; - reg = iga1_crtc_reg.hor_total.reg; - break; - case H_ADDR_INDEX: - reg_value = - IGA1_HOR_ADDR_FORMULA(device_timing. - hor_addr); - viafb_load_reg_num = - iga1_crtc_reg.hor_addr.reg_num; - reg = iga1_crtc_reg.hor_addr.reg; - break; - case H_BLANK_START_INDEX: - reg_value = - IGA1_HOR_BLANK_START_FORMULA - (device_timing.hor_blank_start); - viafb_load_reg_num = - iga1_crtc_reg.hor_blank_start.reg_num; - reg = iga1_crtc_reg.hor_blank_start.reg; - break; - case H_BLANK_END_INDEX: - reg_value = - IGA1_HOR_BLANK_END_FORMULA - (device_timing.hor_blank_start, - device_timing.hor_blank_end); - viafb_load_reg_num = - iga1_crtc_reg.hor_blank_end.reg_num; - reg = iga1_crtc_reg.hor_blank_end.reg; - break; - case H_SYNC_START_INDEX: - reg_value = - IGA1_HOR_SYNC_START_FORMULA - (device_timing.hor_sync_start); - viafb_load_reg_num = - iga1_crtc_reg.hor_sync_start.reg_num; - reg = iga1_crtc_reg.hor_sync_start.reg; - break; - case H_SYNC_END_INDEX: - reg_value = - IGA1_HOR_SYNC_END_FORMULA - (device_timing.hor_sync_start, - device_timing.hor_sync_end); - viafb_load_reg_num = - iga1_crtc_reg.hor_sync_end.reg_num; - reg = iga1_crtc_reg.hor_sync_end.reg; - break; - case V_TOTAL_INDEX: - reg_value = - IGA1_VER_TOTAL_FORMULA(device_timing. - ver_total); - viafb_load_reg_num = - iga1_crtc_reg.ver_total.reg_num; - reg = iga1_crtc_reg.ver_total.reg; - break; - case V_ADDR_INDEX: - reg_value = - IGA1_VER_ADDR_FORMULA(device_timing. - ver_addr); - viafb_load_reg_num = - iga1_crtc_reg.ver_addr.reg_num; - reg = iga1_crtc_reg.ver_addr.reg; - break; - case V_BLANK_START_INDEX: - reg_value = - IGA1_VER_BLANK_START_FORMULA - (device_timing.ver_blank_start); - viafb_load_reg_num = - iga1_crtc_reg.ver_blank_start.reg_num; - reg = iga1_crtc_reg.ver_blank_start.reg; - break; - case V_BLANK_END_INDEX: - reg_value = - IGA1_VER_BLANK_END_FORMULA - (device_timing.ver_blank_start, - device_timing.ver_blank_end); - viafb_load_reg_num = - iga1_crtc_reg.ver_blank_end.reg_num; - reg = iga1_crtc_reg.ver_blank_end.reg; - break; - case V_SYNC_START_INDEX: - reg_value = - IGA1_VER_SYNC_START_FORMULA - (device_timing.ver_sync_start); - viafb_load_reg_num = - iga1_crtc_reg.ver_sync_start.reg_num; - reg = iga1_crtc_reg.ver_sync_start.reg; - break; - case V_SYNC_END_INDEX: - reg_value = - IGA1_VER_SYNC_END_FORMULA - (device_timing.ver_sync_start, - device_timing.ver_sync_end); - viafb_load_reg_num = - iga1_crtc_reg.ver_sync_end.reg_num; - reg = iga1_crtc_reg.ver_sync_end.reg; - break; - - } - } - - if (set_iga == IGA2) { - switch (i) { - case H_TOTAL_INDEX: - reg_value = - IGA2_HOR_TOTAL_FORMULA(device_timing. - hor_total); - viafb_load_reg_num = - iga2_crtc_reg.hor_total.reg_num; - reg = iga2_crtc_reg.hor_total.reg; - break; - case H_ADDR_INDEX: - reg_value = - IGA2_HOR_ADDR_FORMULA(device_timing. - hor_addr); - viafb_load_reg_num = - iga2_crtc_reg.hor_addr.reg_num; - reg = iga2_crtc_reg.hor_addr.reg; - break; - case H_BLANK_START_INDEX: - reg_value = - IGA2_HOR_BLANK_START_FORMULA - (device_timing.hor_blank_start); - viafb_load_reg_num = - iga2_crtc_reg.hor_blank_start.reg_num; - reg = iga2_crtc_reg.hor_blank_start.reg; - break; - case H_BLANK_END_INDEX: - reg_value = - IGA2_HOR_BLANK_END_FORMULA - (device_timing.hor_blank_start, - device_timing.hor_blank_end); - viafb_load_reg_num = - iga2_crtc_reg.hor_blank_end.reg_num; - reg = iga2_crtc_reg.hor_blank_end.reg; - break; - case H_SYNC_START_INDEX: - reg_value = - IGA2_HOR_SYNC_START_FORMULA - (device_timing.hor_sync_start); - if (UNICHROME_CN700 <= - viaparinfo->chip_info->gfx_chip_name) - viafb_load_reg_num = - iga2_crtc_reg.hor_sync_start. - reg_num; - else - viafb_load_reg_num = 3; - reg = iga2_crtc_reg.hor_sync_start.reg; - break; - case H_SYNC_END_INDEX: - reg_value = - IGA2_HOR_SYNC_END_FORMULA - (device_timing.hor_sync_start, - device_timing.hor_sync_end); - viafb_load_reg_num = - iga2_crtc_reg.hor_sync_end.reg_num; - reg = iga2_crtc_reg.hor_sync_end.reg; - break; - case V_TOTAL_INDEX: - reg_value = - IGA2_VER_TOTAL_FORMULA(device_timing. - ver_total); - viafb_load_reg_num = - iga2_crtc_reg.ver_total.reg_num; - reg = iga2_crtc_reg.ver_total.reg; - break; - case V_ADDR_INDEX: - reg_value = - IGA2_VER_ADDR_FORMULA(device_timing. - ver_addr); - viafb_load_reg_num = - iga2_crtc_reg.ver_addr.reg_num; - reg = iga2_crtc_reg.ver_addr.reg; - break; - case V_BLANK_START_INDEX: - reg_value = - IGA2_VER_BLANK_START_FORMULA - (device_timing.ver_blank_start); - viafb_load_reg_num = - iga2_crtc_reg.ver_blank_start.reg_num; - reg = iga2_crtc_reg.ver_blank_start.reg; - break; - case V_BLANK_END_INDEX: - reg_value = - IGA2_VER_BLANK_END_FORMULA - (device_timing.ver_blank_start, - device_timing.ver_blank_end); - viafb_load_reg_num = - iga2_crtc_reg.ver_blank_end.reg_num; - reg = iga2_crtc_reg.ver_blank_end.reg; - break; - case V_SYNC_START_INDEX: - reg_value = - IGA2_VER_SYNC_START_FORMULA - (device_timing.ver_sync_start); - viafb_load_reg_num = - iga2_crtc_reg.ver_sync_start.reg_num; - reg = iga2_crtc_reg.ver_sync_start.reg; - break; - case V_SYNC_END_INDEX: - reg_value = - IGA2_VER_SYNC_END_FORMULA - (device_timing.ver_sync_start, - device_timing.ver_sync_end); - viafb_load_reg_num = - iga2_crtc_reg.ver_sync_end.reg_num; - reg = iga2_crtc_reg.ver_sync_end.reg; - break; - - } - } - viafb_load_reg(reg_value, viafb_load_reg_num, reg, VIACR); - } - - viafb_lock_crt(); + struct display_timing timing; + + timing.hor_addr = var->xres; + timing.hor_sync_start = timing.hor_addr + var->right_margin; + timing.hor_sync_end = timing.hor_sync_start + var->hsync_len; + timing.hor_total = timing.hor_sync_end + var->left_margin; + timing.hor_blank_start = timing.hor_addr; + timing.hor_blank_end = timing.hor_total; + timing.ver_addr = var->yres; + timing.ver_sync_start = timing.ver_addr + var->lower_margin; + timing.ver_sync_end = timing.ver_sync_start + var->vsync_len; + timing.ver_total = timing.ver_sync_end + var->upper_margin; + timing.ver_blank_start = timing.ver_addr; + timing.ver_blank_end = timing.ver_total; + return timing; } -void viafb_fill_crtc_timing(struct crt_mode_table *crt_table, - struct VideoModeTable *video_mode, int bpp_byte, int set_iga) +void viafb_fill_crtc_timing(const struct fb_var_screeninfo *var, int iga) { - struct display_timing crt_reg; - int i; - int index = 0; - int h_addr, v_addr; - u32 clock, refresh = viafb_refresh; - - if (viafb_SAMM_ON && set_iga == IGA2) - refresh = viafb_refresh1; - - for (i = 0; i < video_mode->mode_array; i++) { - index = i; - - if (crt_table[i].refresh_rate == refresh) - break; - } + struct display_timing crt_reg = var_to_timing(var); - crt_reg = crt_table[index].crtc; + if (iga == IGA1) + via_set_primary_timing(&crt_reg); + else if (iga == IGA2) + via_set_secondary_timing(&crt_reg); - /* Mode 640x480 has border, but LCD/DFP didn't have border. */ - /* So we would delete border. */ - if ((viafb_LCD_ON | viafb_DVI_ON) - && video_mode->crtc[0].crtc.hor_addr == 640 - && video_mode->crtc[0].crtc.ver_addr == 480 - && refresh == 60) { - /* The border is 8 pixels. */ - crt_reg.hor_blank_start = crt_reg.hor_blank_start - 8; - - /* Blanking time should add left and right borders. */ - crt_reg.hor_blank_end = crt_reg.hor_blank_end + 16; - } - - h_addr = crt_reg.hor_addr; - v_addr = crt_reg.ver_addr; - if (set_iga == IGA1) { - viafb_unlock_crt(); - viafb_write_reg_mask(CR17, VIACR, 0x00, BIT7); - } - - switch (set_iga) { - case IGA1: - viafb_load_crtc_timing(crt_reg, IGA1); - break; - case IGA2: - viafb_load_crtc_timing(crt_reg, IGA2); - break; - } - - viafb_lock_crt(); - viafb_write_reg_mask(CR17, VIACR, 0x80, BIT7); - viafb_load_fetch_count_reg(h_addr, bpp_byte, set_iga); - - /* load FIFO */ - if ((viaparinfo->chip_info->gfx_chip_name != UNICHROME_CLE266) - && (viaparinfo->chip_info->gfx_chip_name != UNICHROME_K400)) - viafb_load_FIFO_reg(set_iga, h_addr, v_addr); - - clock = crt_reg.hor_total * crt_reg.ver_total - * crt_table[index].refresh_rate; - viafb_set_vclock(clock, set_iga); + viafb_load_fetch_count_reg(var->xres, var->bits_per_pixel / 8, iga); + if (viaparinfo->chip_info->gfx_chip_name != UNICHROME_CLE266 + && viaparinfo->chip_info->gfx_chip_name != UNICHROME_K400) + viafb_load_FIFO_reg(iga, var->xres, var->yres); + viafb_set_vclock(PICOS2KHZ(var->pixclock) * 1000, iga); } void __devinit viafb_init_chip_info(int chip_type) @@ -2092,23 +1769,9 @@ static u8 get_sync(struct fb_info *info) return polarity; } -int viafb_setmode(struct VideoModeTable *vmode_tbl, int video_bpp, - struct VideoModeTable *vmode_tbl1, int video_bpp1) +static void hw_init(void) { - int i, j; - int port; - u32 devices = viaparinfo->shared->iga1_devices - | viaparinfo->shared->iga2_devices; - u8 value, index, mask; - struct crt_mode_table *crt_timing; - struct crt_mode_table *crt_timing1 = NULL; - - device_screen_off(); - crt_timing = vmode_tbl->crtc; - - if (viafb_SAMM_ON == 1) { - crt_timing1 = vmode_tbl1->crtc; - } + int i; inb(VIAStatus); outb(0x00, VIAAR); @@ -2147,9 +1810,8 @@ int viafb_setmode(struct VideoModeTable *vmode_tbl, int video_bpp, break; } + /* probably this should go to the scaling code one day */ viafb_write_regx(scaling_parameters, ARRAY_SIZE(scaling_parameters)); - device_off(); - via_set_state(devices, VIA_STATE_OFF); /* Fill VPIT Parameters */ /* Write Misc Register */ @@ -2175,12 +1837,29 @@ int viafb_setmode(struct VideoModeTable *vmode_tbl, int video_bpp, inb(VIAStatus); outb(0x20, VIAAR); + load_fix_bit_crtc_reg(); +} + +int viafb_setmode(int video_bpp, int video_bpp1) +{ + int j; + int port; + u32 devices = viaparinfo->shared->iga1_devices + | viaparinfo->shared->iga2_devices; + u8 value, index, mask; + struct fb_var_screeninfo var2; + + device_screen_off(); + device_off(); + via_set_state(devices, VIA_STATE_OFF); + + hw_init(); + /* Update Patch Register */ if ((viaparinfo->chip_info->gfx_chip_name == UNICHROME_CLE266 - || viaparinfo->chip_info->gfx_chip_name == UNICHROME_K400) - && vmode_tbl->crtc[0].crtc.hor_addr == 1024 - && vmode_tbl->crtc[0].crtc.ver_addr == 768) { + || viaparinfo->chip_info->gfx_chip_name == UNICHROME_K400) + && viafbinfo->var.xres == 1024 && viafbinfo->var.yres == 768) { for (j = 0; j < res_patch_table[0].table_length; j++) { index = res_patch_table[0].io_reg_table[j].index; port = res_patch_table[0].io_reg_table[j].port; @@ -2190,7 +1869,6 @@ int viafb_setmode(struct VideoModeTable *vmode_tbl, int video_bpp, } } - load_fix_bit_crtc_reg(); via_set_primary_pitch(viafbinfo->fix.line_length); via_set_secondary_pitch(viafb_dual_fb ? viafbinfo1->fix.line_length : viafbinfo->fix.line_length); @@ -2208,23 +1886,28 @@ int viafb_setmode(struct VideoModeTable *vmode_tbl, int video_bpp, /* Clear On Screen */ + if (viafb_dual_fb) { + var2 = viafbinfo1->var; + } else if (viafb_SAMM_ON) { + viafb_fill_var_timing_info(&var2, viafb_get_best_mode( + viafb_second_xres, viafb_second_yres, viafb_refresh1)); + var2.bits_per_pixel = viafbinfo->var.bits_per_pixel; + } + /* CRT set mode */ if (viafb_CRT_ON) { - if (viafb_SAMM_ON && - viaparinfo->shared->iga2_devices & VIA_CRT) { - viafb_fill_crtc_timing(crt_timing1, vmode_tbl1, - video_bpp1 / 8, IGA2); - } else { - viafb_fill_crtc_timing(crt_timing, vmode_tbl, - video_bpp / 8, + if (viaparinfo->shared->iga2_devices & VIA_CRT + && viafb_SAMM_ON) + viafb_fill_crtc_timing(&var2, IGA2); + else + viafb_fill_crtc_timing(&viafbinfo->var, (viaparinfo->shared->iga1_devices & VIA_CRT) ? IGA1 : IGA2); - } /* Patch if set_hres is not 8 alignment (1366) to viafb_setmode to 8 alignment (1368),there is several pixels (2 pixels) on right side of screen. */ - if (vmode_tbl->crtc[0].crtc.hor_addr % 8) { + if (viafbinfo->var.xres % 8) { viafb_unlock_crt(); viafb_write_reg(CR02, VIACR, viafb_read_reg(VIACR, CR02) - 1); @@ -2233,31 +1916,20 @@ int viafb_setmode(struct VideoModeTable *vmode_tbl, int video_bpp, } if (viafb_DVI_ON) { - if (viafb_SAMM_ON && - (viaparinfo->tmds_setting_info->iga_path == IGA2)) { - viafb_dvi_set_mode(viafb_get_mode - (viaparinfo->tmds_setting_info->h_active, - viaparinfo->tmds_setting_info-> - v_active), - video_bpp1, viaparinfo-> - tmds_setting_info->iga_path); - } else { - viafb_dvi_set_mode(viafb_get_mode - (viaparinfo->tmds_setting_info->h_active, - viaparinfo-> - tmds_setting_info->v_active), - video_bpp, viaparinfo-> - tmds_setting_info->iga_path); - } + if (viaparinfo->shared->tmds_setting_info.iga_path == IGA2 + && viafb_SAMM_ON) + viafb_dvi_set_mode(&var2, IGA2); + else + viafb_dvi_set_mode(&viafbinfo->var, + viaparinfo->tmds_setting_info->iga_path); } if (viafb_LCD_ON) { if (viafb_SAMM_ON && (viaparinfo->lvds_setting_info->iga_path == IGA2)) { viaparinfo->lvds_setting_info->bpp = video_bpp1; - viafb_lcd_set_mode(crt_timing1, viaparinfo-> - lvds_setting_info, - &viaparinfo->chip_info->lvds_chip_info); + viafb_lcd_set_mode(viaparinfo->lvds_setting_info, + &viaparinfo->chip_info->lvds_chip_info); } else { /* IGA1 doesn't have LCD scaling, so set it center. */ if (viaparinfo->lvds_setting_info->iga_path == IGA1) { @@ -2265,18 +1937,16 @@ int viafb_setmode(struct VideoModeTable *vmode_tbl, int video_bpp, LCD_CENTERING; } viaparinfo->lvds_setting_info->bpp = video_bpp; - viafb_lcd_set_mode(crt_timing, viaparinfo-> - lvds_setting_info, - &viaparinfo->chip_info->lvds_chip_info); + viafb_lcd_set_mode(viaparinfo->lvds_setting_info, + &viaparinfo->chip_info->lvds_chip_info); } } if (viafb_LCD2_ON) { if (viafb_SAMM_ON && (viaparinfo->lvds_setting_info2->iga_path == IGA2)) { viaparinfo->lvds_setting_info2->bpp = video_bpp1; - viafb_lcd_set_mode(crt_timing1, viaparinfo-> - lvds_setting_info2, - &viaparinfo->chip_info->lvds_chip_info2); + viafb_lcd_set_mode(viaparinfo->lvds_setting_info2, + &viaparinfo->chip_info->lvds_chip_info2); } else { /* IGA1 doesn't have LCD scaling, so set it center. */ if (viaparinfo->lvds_setting_info2->iga_path == IGA1) { @@ -2284,9 +1954,8 @@ int viafb_setmode(struct VideoModeTable *vmode_tbl, int video_bpp, LCD_CENTERING; } viaparinfo->lvds_setting_info2->bpp = video_bpp; - viafb_lcd_set_mode(crt_timing, viaparinfo-> - lvds_setting_info2, - &viaparinfo->chip_info->lvds_chip_info2); + viafb_lcd_set_mode(viaparinfo->lvds_setting_info2, + &viaparinfo->chip_info->lvds_chip_info2); } } @@ -2296,8 +1965,8 @@ int viafb_setmode(struct VideoModeTable *vmode_tbl, int video_bpp, /* If set mode normally, save resolution information for hot-plug . */ if (!viafb_hotplug) { - viafb_hotplug_Xres = vmode_tbl->crtc[0].crtc.hor_addr; - viafb_hotplug_Yres = vmode_tbl->crtc[0].crtc.ver_addr; + viafb_hotplug_Xres = viafbinfo->var.xres; + viafb_hotplug_Yres = viafbinfo->var.yres; viafb_hotplug_bpp = video_bpp; viafb_hotplug_refresh = viafb_refresh; @@ -2348,42 +2017,14 @@ int viafb_setmode(struct VideoModeTable *vmode_tbl, int video_bpp, return 1; } -int viafb_get_pixclock(int hres, int vres, int vmode_refresh) -{ - int i; - struct crt_mode_table *best; - struct VideoModeTable *vmode = viafb_get_mode(hres, vres); - - if (!vmode) - return RES_640X480_60HZ_PIXCLOCK; - - best = &vmode->crtc[0]; - for (i = 1; i < vmode->mode_array; i++) { - if (abs(vmode->crtc[i].refresh_rate - vmode_refresh) - < abs(best->refresh_rate - vmode_refresh)) - best = &vmode->crtc[i]; - } - - return 1000000000 / (best->crtc.hor_total * best->crtc.ver_total) - * 1000 / best->refresh_rate; -} - int viafb_get_refresh(int hres, int vres, u32 long_refresh) { - int i; struct crt_mode_table *best; - struct VideoModeTable *vmode = viafb_get_mode(hres, vres); - if (!vmode) + best = viafb_get_best_mode(hres, vres, long_refresh); + if (!best) return 60; - best = &vmode->crtc[0]; - for (i = 1; i < vmode->mode_array; i++) { - if (abs(vmode->crtc[i].refresh_rate - long_refresh) - < abs(best->refresh_rate - long_refresh)) - best = &vmode->crtc[i]; - } - if (abs(best->refresh_rate - long_refresh) > 3) { if (hres == 1200 && vres == 900) return 49; /* OLPC DCON only supports 50 Hz */ @@ -2485,21 +2126,14 @@ void viafb_set_dpa_gfx(int output_interface, struct GFX_DPA_SETTING\ } /*According var's xres, yres fill var's other timing information*/ -void viafb_fill_var_timing_info(struct fb_var_screeninfo *var, int refresh, - struct VideoModeTable *vmode_tbl) +void viafb_fill_var_timing_info(struct fb_var_screeninfo *var, + struct crt_mode_table *mode) { - struct crt_mode_table *crt_timing = NULL; struct display_timing crt_reg; - int i = 0, index = 0; - crt_timing = vmode_tbl->crtc; - for (i = 0; i < vmode_tbl->mode_array; i++) { - index = i; - if (crt_timing[i].refresh_rate == refresh) - break; - } - crt_reg = crt_timing[index].crtc; - var->pixclock = viafb_get_pixclock(var->xres, var->yres, refresh); + crt_reg = mode->crtc; + var->pixclock = 1000000000 / (crt_reg.hor_total * crt_reg.ver_total) + * 1000 / mode->refresh_rate; var->left_margin = crt_reg.hor_total - (crt_reg.hor_sync_start + crt_reg.hor_sync_end); var->right_margin = crt_reg.hor_sync_start - crt_reg.hor_addr; @@ -2509,8 +2143,8 @@ void viafb_fill_var_timing_info(struct fb_var_screeninfo *var, int refresh, var->lower_margin = crt_reg.ver_sync_start - crt_reg.ver_addr; var->vsync_len = crt_reg.ver_sync_end; var->sync = 0; - if (crt_timing[index].h_sync_polarity == POSITIVE) + if (mode->h_sync_polarity == POSITIVE) var->sync |= FB_SYNC_HOR_HIGH_ACT; - if (crt_timing[index].v_sync_polarity == POSITIVE) + if (mode->v_sync_polarity == POSITIVE) var->sync |= FB_SYNC_VERT_HIGH_ACT; } diff --git a/drivers/video/via/hw.h b/drivers/video/via/hw.h index c7239eb83bae..4db5b6e8d8d0 100644 --- a/drivers/video/via/hw.h +++ b/drivers/video/via/hw.h @@ -51,40 +51,6 @@ #define VIA_HSYNC_NEGATIVE 0x01 #define VIA_VSYNC_NEGATIVE 0x02 -/*************************************************** -* Definition IGA1 Design Method of CRTC Registers * -****************************************************/ -#define IGA1_HOR_TOTAL_FORMULA(x) (((x)/8)-5) -#define IGA1_HOR_ADDR_FORMULA(x) (((x)/8)-1) -#define IGA1_HOR_BLANK_START_FORMULA(x) (((x)/8)-1) -#define IGA1_HOR_BLANK_END_FORMULA(x, y) (((x+y)/8)-1) -#define IGA1_HOR_SYNC_START_FORMULA(x) ((x)/8) -#define IGA1_HOR_SYNC_END_FORMULA(x, y) ((x+y)/8) - -#define IGA1_VER_TOTAL_FORMULA(x) ((x)-2) -#define IGA1_VER_ADDR_FORMULA(x) ((x)-1) -#define IGA1_VER_BLANK_START_FORMULA(x) ((x)-1) -#define IGA1_VER_BLANK_END_FORMULA(x, y) ((x+y)-1) -#define IGA1_VER_SYNC_START_FORMULA(x) ((x)-1) -#define IGA1_VER_SYNC_END_FORMULA(x, y) ((x+y)-1) - -/*************************************************** -** Definition IGA2 Design Method of CRTC Registers * -****************************************************/ -#define IGA2_HOR_TOTAL_FORMULA(x) ((x)-1) -#define IGA2_HOR_ADDR_FORMULA(x) ((x)-1) -#define IGA2_HOR_BLANK_START_FORMULA(x) ((x)-1) -#define IGA2_HOR_BLANK_END_FORMULA(x, y) ((x+y)-1) -#define IGA2_HOR_SYNC_START_FORMULA(x) ((x)-1) -#define IGA2_HOR_SYNC_END_FORMULA(x, y) ((x+y)-1) - -#define IGA2_VER_TOTAL_FORMULA(x) ((x)-1) -#define IGA2_VER_ADDR_FORMULA(x) ((x)-1) -#define IGA2_VER_BLANK_START_FORMULA(x) ((x)-1) -#define IGA2_VER_BLANK_END_FORMULA(x, y) ((x+y)-1) -#define IGA2_VER_SYNC_START_FORMULA(x) ((x)-1) -#define IGA2_VER_SYNC_END_FORMULA(x, y) ((x+y)-1) - /**********************************************************/ /* Definition IGA2 Design Method of CRTC Shadow Registers */ /**********************************************************/ @@ -97,33 +63,6 @@ #define IGA2_VER_SYNC_START_SHADOW_FORMULA(x) (x) #define IGA2_VER_SYNC_END_SHADOW_FORMULA(x, y) (x+y) -/* Define Register Number for IGA1 CRTC Timing */ - -/* location: {CR00,0,7},{CR36,3,3} */ -#define IGA1_HOR_TOTAL_REG_NUM 2 -/* location: {CR01,0,7} */ -#define IGA1_HOR_ADDR_REG_NUM 1 -/* location: {CR02,0,7} */ -#define IGA1_HOR_BLANK_START_REG_NUM 1 -/* location: {CR03,0,4},{CR05,7,7},{CR33,5,5} */ -#define IGA1_HOR_BLANK_END_REG_NUM 3 -/* location: {CR04,0,7},{CR33,4,4} */ -#define IGA1_HOR_SYNC_START_REG_NUM 2 -/* location: {CR05,0,4} */ -#define IGA1_HOR_SYNC_END_REG_NUM 1 -/* location: {CR06,0,7},{CR07,0,0},{CR07,5,5},{CR35,0,0} */ -#define IGA1_VER_TOTAL_REG_NUM 4 -/* location: {CR12,0,7},{CR07,1,1},{CR07,6,6},{CR35,2,2} */ -#define IGA1_VER_ADDR_REG_NUM 4 -/* location: {CR15,0,7},{CR07,3,3},{CR09,5,5},{CR35,3,3} */ -#define IGA1_VER_BLANK_START_REG_NUM 4 -/* location: {CR16,0,7} */ -#define IGA1_VER_BLANK_END_REG_NUM 1 -/* location: {CR10,0,7},{CR07,2,2},{CR07,7,7},{CR35,1,1} */ -#define IGA1_VER_SYNC_START_REG_NUM 4 -/* location: {CR11,0,3} */ -#define IGA1_VER_SYNC_END_REG_NUM 1 - /* Define Register Number for IGA2 Shadow CRTC Timing */ /* location: {CR6D,0,7},{CR71,3,3} */ @@ -143,37 +82,6 @@ /* location: {CR76,0,3} */ #define IGA2_SHADOW_VER_SYNC_END_REG_NUM 1 -/* Define Register Number for IGA2 CRTC Timing */ - -/* location: {CR50,0,7},{CR55,0,3} */ -#define IGA2_HOR_TOTAL_REG_NUM 2 -/* location: {CR51,0,7},{CR55,4,6} */ -#define IGA2_HOR_ADDR_REG_NUM 2 -/* location: {CR52,0,7},{CR54,0,2} */ -#define IGA2_HOR_BLANK_START_REG_NUM 2 -/* location: CLE266: {CR53,0,7},{CR54,3,5} => CLE266's CR5D[6] -is reserved, so it may have problem to set 1600x1200 on IGA2. */ -/* Others: {CR53,0,7},{CR54,3,5},{CR5D,6,6} */ -#define IGA2_HOR_BLANK_END_REG_NUM 3 -/* location: {CR56,0,7},{CR54,6,7},{CR5C,7,7} */ -/* VT3314 and Later: {CR56,0,7},{CR54,6,7},{CR5C,7,7}, {CR5D,7,7} */ -#define IGA2_HOR_SYNC_START_REG_NUM 4 - -/* location: {CR57,0,7},{CR5C,6,6} */ -#define IGA2_HOR_SYNC_END_REG_NUM 2 -/* location: {CR58,0,7},{CR5D,0,2} */ -#define IGA2_VER_TOTAL_REG_NUM 2 -/* location: {CR59,0,7},{CR5D,3,5} */ -#define IGA2_VER_ADDR_REG_NUM 2 -/* location: {CR5A,0,7},{CR5C,0,2} */ -#define IGA2_VER_BLANK_START_REG_NUM 2 -/* location: {CR5E,0,7},{CR5C,3,5} */ -#define IGA2_VER_BLANK_END_REG_NUM 2 -/* location: {CR5E,0,7},{CR5F,5,7} */ -#define IGA2_VER_SYNC_START_REG_NUM 2 -/* location: {CR5F,0,4} */ -#define IGA2_VER_SYNC_END_REG_NUM 1 - /* Define Fetch Count Register*/ /* location: {SR1C,0,7},{SR1D,0,1} */ @@ -446,87 +354,12 @@ is reserved, so it may have problem to set 1600x1200 on IGA2. */ /* location: {CR78,0,7},{CR79,6,7} */ #define LCD_VER_SCALING_FACTOR_REG_NUM_CLE 2 -/************************************************ - ***** Define IGA1 Display Timing ***** - ************************************************/ struct io_register { u8 io_addr; u8 start_bit; u8 end_bit; }; -/* IGA1 Horizontal Total */ -struct iga1_hor_total { - int reg_num; - struct io_register reg[IGA1_HOR_TOTAL_REG_NUM]; -}; - -/* IGA1 Horizontal Addressable Video */ -struct iga1_hor_addr { - int reg_num; - struct io_register reg[IGA1_HOR_ADDR_REG_NUM]; -}; - -/* IGA1 Horizontal Blank Start */ -struct iga1_hor_blank_start { - int reg_num; - struct io_register reg[IGA1_HOR_BLANK_START_REG_NUM]; -}; - -/* IGA1 Horizontal Blank End */ -struct iga1_hor_blank_end { - int reg_num; - struct io_register reg[IGA1_HOR_BLANK_END_REG_NUM]; -}; - -/* IGA1 Horizontal Sync Start */ -struct iga1_hor_sync_start { - int reg_num; - struct io_register reg[IGA1_HOR_SYNC_START_REG_NUM]; -}; - -/* IGA1 Horizontal Sync End */ -struct iga1_hor_sync_end { - int reg_num; - struct io_register reg[IGA1_HOR_SYNC_END_REG_NUM]; -}; - -/* IGA1 Vertical Total */ -struct iga1_ver_total { - int reg_num; - struct io_register reg[IGA1_VER_TOTAL_REG_NUM]; -}; - -/* IGA1 Vertical Addressable Video */ -struct iga1_ver_addr { - int reg_num; - struct io_register reg[IGA1_VER_ADDR_REG_NUM]; -}; - -/* IGA1 Vertical Blank Start */ -struct iga1_ver_blank_start { - int reg_num; - struct io_register reg[IGA1_VER_BLANK_START_REG_NUM]; -}; - -/* IGA1 Vertical Blank End */ -struct iga1_ver_blank_end { - int reg_num; - struct io_register reg[IGA1_VER_BLANK_END_REG_NUM]; -}; - -/* IGA1 Vertical Sync Start */ -struct iga1_ver_sync_start { - int reg_num; - struct io_register reg[IGA1_VER_SYNC_START_REG_NUM]; -}; - -/* IGA1 Vertical Sync End */ -struct iga1_ver_sync_end { - int reg_num; - struct io_register reg[IGA1_VER_SYNC_END_REG_NUM]; -}; - /***************************************************** ** Define IGA2 Shadow Display Timing **** *****************************************************/ @@ -579,82 +412,6 @@ struct iga2_shadow_ver_sync_end { struct io_register reg[IGA2_SHADOW_VER_SYNC_END_REG_NUM]; }; -/***************************************************** -** Define IGA2 Display Timing **** -******************************************************/ - -/* IGA2 Horizontal Total */ -struct iga2_hor_total { - int reg_num; - struct io_register reg[IGA2_HOR_TOTAL_REG_NUM]; -}; - -/* IGA2 Horizontal Addressable Video */ -struct iga2_hor_addr { - int reg_num; - struct io_register reg[IGA2_HOR_ADDR_REG_NUM]; -}; - -/* IGA2 Horizontal Blank Start */ -struct iga2_hor_blank_start { - int reg_num; - struct io_register reg[IGA2_HOR_BLANK_START_REG_NUM]; -}; - -/* IGA2 Horizontal Blank End */ -struct iga2_hor_blank_end { - int reg_num; - struct io_register reg[IGA2_HOR_BLANK_END_REG_NUM]; -}; - -/* IGA2 Horizontal Sync Start */ -struct iga2_hor_sync_start { - int reg_num; - struct io_register reg[IGA2_HOR_SYNC_START_REG_NUM]; -}; - -/* IGA2 Horizontal Sync End */ -struct iga2_hor_sync_end { - int reg_num; - struct io_register reg[IGA2_HOR_SYNC_END_REG_NUM]; -}; - -/* IGA2 Vertical Total */ -struct iga2_ver_total { - int reg_num; - struct io_register reg[IGA2_VER_TOTAL_REG_NUM]; -}; - -/* IGA2 Vertical Addressable Video */ -struct iga2_ver_addr { - int reg_num; - struct io_register reg[IGA2_VER_ADDR_REG_NUM]; -}; - -/* IGA2 Vertical Blank Start */ -struct iga2_ver_blank_start { - int reg_num; - struct io_register reg[IGA2_VER_BLANK_START_REG_NUM]; -}; - -/* IGA2 Vertical Blank End */ -struct iga2_ver_blank_end { - int reg_num; - struct io_register reg[IGA2_VER_BLANK_END_REG_NUM]; -}; - -/* IGA2 Vertical Sync Start */ -struct iga2_ver_sync_start { - int reg_num; - struct io_register reg[IGA2_VER_SYNC_START_REG_NUM]; -}; - -/* IGA2 Vertical Sync End */ -struct iga2_ver_sync_end { - int reg_num; - struct io_register reg[IGA2_VER_SYNC_END_REG_NUM]; -}; - /* IGA1 Fetch Count Register */ struct iga1_fetch_count { int reg_num; @@ -817,21 +574,6 @@ struct display_queue_expire_num { iga2_display_queue_expire_num_reg; }; -struct iga1_crtc_timing { - struct iga1_hor_total hor_total; - struct iga1_hor_addr hor_addr; - struct iga1_hor_blank_start hor_blank_start; - struct iga1_hor_blank_end hor_blank_end; - struct iga1_hor_sync_start hor_sync_start; - struct iga1_hor_sync_end hor_sync_end; - struct iga1_ver_total ver_total; - struct iga1_ver_addr ver_addr; - struct iga1_ver_blank_start ver_blank_start; - struct iga1_ver_blank_end ver_blank_end; - struct iga1_ver_sync_start ver_sync_start; - struct iga1_ver_sync_end ver_sync_end; -}; - struct iga2_shadow_crtc_timing { struct iga2_shadow_hor_total hor_total_shadow; struct iga2_shadow_hor_blank_end hor_blank_end_shadow; @@ -843,21 +585,6 @@ struct iga2_shadow_crtc_timing { struct iga2_shadow_ver_sync_end ver_sync_end_shadow; }; -struct iga2_crtc_timing { - struct iga2_hor_total hor_total; - struct iga2_hor_addr hor_addr; - struct iga2_hor_blank_start hor_blank_start; - struct iga2_hor_blank_end hor_blank_end; - struct iga2_hor_sync_start hor_sync_start; - struct iga2_hor_sync_end hor_sync_end; - struct iga2_ver_total ver_total; - struct iga2_ver_addr ver_addr; - struct iga2_ver_blank_start ver_blank_start; - struct iga2_ver_blank_end ver_blank_end; - struct iga2_ver_sync_start ver_sync_start; - struct iga2_ver_sync_end ver_sync_end; -}; - /* device ID */ #define CLE266_FUNCTION3 0x3123 #define KM400_FUNCTION3 0x3205 @@ -910,9 +637,7 @@ extern int viafb_LCD_ON; extern int viafb_DVI_ON; extern int viafb_hotplug; -void viafb_fill_crtc_timing(struct crt_mode_table *crt_table, - struct VideoModeTable *video_mode, int bpp_byte, int set_iga); - +void viafb_fill_crtc_timing(const struct fb_var_screeninfo *var, int iga); void viafb_set_vclock(u32 CLK, int set_iga); void viafb_load_reg(int timing_value, int viafb_load_reg_num, struct io_register *reg, @@ -932,13 +657,11 @@ void viafb_load_FIFO_reg(int set_iga, int hor_active, int ver_active); void viafb_set_dpa_gfx(int output_interface, struct GFX_DPA_SETTING\ *p_gfx_dpa_setting); -int viafb_setmode(struct VideoModeTable *vmode_tbl, int video_bpp, - struct VideoModeTable *vmode_tbl1, int video_bpp1); -void viafb_fill_var_timing_info(struct fb_var_screeninfo *var, int refresh, - struct VideoModeTable *vmode_tbl); +int viafb_setmode(int video_bpp, int video_bpp1); +void viafb_fill_var_timing_info(struct fb_var_screeninfo *var, + struct crt_mode_table *mode); void __devinit viafb_init_chip_info(int chip_type); void __devinit viafb_init_dac(int set_iga); -int viafb_get_pixclock(int hres, int vres, int vmode_refresh); int viafb_get_refresh(int hres, int vres, u32 float_refresh); void viafb_update_device_setting(int hres, int vres, int bpp, int flag); diff --git a/drivers/video/via/lcd.c b/drivers/video/via/lcd.c index 6e06981d638b..5f3b4e394e82 100644 --- a/drivers/video/via/lcd.c +++ b/drivers/video/via/lcd.c @@ -548,9 +548,8 @@ static void lcd_patch_skew(struct lvds_setting_information } /* LCD Set Mode */ -void viafb_lcd_set_mode(struct crt_mode_table *mode_crt_table, - struct lvds_setting_information *plvds_setting_info, - struct lvds_chip_information *plvds_chip_info) +void viafb_lcd_set_mode(struct lvds_setting_information *plvds_setting_info, + struct lvds_chip_information *plvds_chip_info) { int set_iga = plvds_setting_info->iga_path; int mode_bpp = plvds_setting_info->bpp; @@ -559,16 +558,15 @@ void viafb_lcd_set_mode(struct crt_mode_table *mode_crt_table, int panel_hres = plvds_setting_info->lcd_panel_hres; int panel_vres = plvds_setting_info->lcd_panel_vres; u32 clock; - struct display_timing mode_crt_reg, panel_crt_reg; - struct crt_mode_table *panel_crt_table = NULL; - struct VideoModeTable *vmode_tbl = viafb_get_mode(panel_hres, - panel_vres); + struct display_timing mode_crt_reg, panel_crt_reg, timing; + struct crt_mode_table *mode_crt_table, *panel_crt_table; DEBUG_MSG(KERN_INFO "viafb_lcd_set_mode!!\n"); /* Get mode table */ + mode_crt_table = viafb_get_best_mode(set_hres, set_vres, 60); mode_crt_reg = mode_crt_table->crtc; /* Get panel table Pointer */ - panel_crt_table = vmode_tbl->crtc; + panel_crt_table = viafb_get_best_mode(panel_hres, panel_vres, 60); panel_crt_reg = panel_crt_table->crtc; DEBUG_MSG(KERN_INFO "bellow viafb_lcd_set_mode!!\n"); if (VT1636_LVDS == plvds_chip_info->lvds_chip_name) @@ -576,31 +574,28 @@ void viafb_lcd_set_mode(struct crt_mode_table *mode_crt_table, clock = panel_crt_reg.hor_total * panel_crt_reg.ver_total * panel_crt_table->refresh_rate; plvds_setting_info->vclk = clock; - if (set_iga == IGA1) { - /* IGA1 doesn't have LCD scaling, so set it as centering. */ - viafb_load_crtc_timing(lcd_centering_timging - (mode_crt_reg, panel_crt_reg), IGA1); + + if (set_iga == IGA2 && (set_hres < panel_hres || set_vres < panel_vres) + && plvds_setting_info->display_method == LCD_EXPANDSION) { + timing = panel_crt_reg; + load_lcd_scaling(set_hres, set_vres, panel_hres, panel_vres); } else { - /* Expansion */ - if (plvds_setting_info->display_method == LCD_EXPANDSION - && (set_hres < panel_hres || set_vres < panel_vres)) { - /* expansion timing IGA2 loaded panel set timing*/ - viafb_load_crtc_timing(panel_crt_reg, IGA2); - DEBUG_MSG(KERN_INFO "viafb_load_crtc_timing!!\n"); - load_lcd_scaling(set_hres, set_vres, panel_hres, - panel_vres); - DEBUG_MSG(KERN_INFO "load_lcd_scaling!!\n"); - } else { /* Centering */ - /* centering timing IGA2 always loaded panel - and mode releative timing */ - viafb_load_crtc_timing(lcd_centering_timging - (mode_crt_reg, panel_crt_reg), IGA2); - viafb_write_reg_mask(CR79, VIACR, 0x00, + timing = lcd_centering_timging(mode_crt_reg, panel_crt_reg); + if (set_iga == IGA2) + /* disable scaling */ + via_write_reg_mask(VIACR, 0x79, 0x00, BIT0 + BIT1 + BIT2); - /* LCD scaling disabled */ - } } + timing.hor_blank_end += timing.hor_blank_start; + timing.hor_sync_end += timing.hor_sync_start; + timing.ver_blank_end += timing.ver_blank_start; + timing.ver_sync_end += timing.ver_sync_start; + if (set_iga == IGA1) + via_set_primary_timing(&timing); + else if (set_iga == IGA2) + via_set_secondary_timing(&timing); + /* Fetch count for IGA2 only */ viafb_load_fetch_count_reg(set_hres, mode_bpp / 8, set_iga); diff --git a/drivers/video/via/lcd.h b/drivers/video/via/lcd.h index 75f60a655b0e..77ca7b862e68 100644 --- a/drivers/video/via/lcd.h +++ b/drivers/video/via/lcd.h @@ -76,16 +76,13 @@ void __devinit viafb_init_lvds_output_interface(struct lvds_chip_information *plvds_chip_info, struct lvds_setting_information *plvds_setting_info); -void viafb_lcd_set_mode(struct crt_mode_table *mode_crt_table, - struct lvds_setting_information *plvds_setting_info, - struct lvds_chip_information *plvds_chip_info); +void viafb_lcd_set_mode(struct lvds_setting_information *plvds_setting_info, + struct lvds_chip_information *plvds_chip_info); bool __devinit viafb_lvds_trasmitter_identify(void); void viafb_init_lvds_output_interface(struct lvds_chip_information *plvds_chip_info, struct lvds_setting_information *plvds_setting_info); bool viafb_lcd_get_mobile_state(bool *mobile); -void viafb_load_crtc_timing(struct display_timing device_timing, - int set_iga); #endif /* __LCD_H__ */ diff --git a/drivers/video/via/share.h b/drivers/video/via/share.h index 61b0bd596b85..69d882cbe709 100644 --- a/drivers/video/via/share.h +++ b/drivers/video/via/share.h @@ -22,6 +22,8 @@ #ifndef __SHARE_H__ #define __SHARE_H__ +#include "via_modesetting.h" + /* Define Bit Field */ #define BIT0 0x01 #define BIT1 0x02 @@ -634,10 +636,6 @@ #define V_SYNC_SATRT_SHADOW_INDEX 18 #define V_SYNC_END_SHADOW_INDEX 19 -/* Definition Video Mode Pixel Clock (picoseconds) -*/ -#define RES_640X480_60HZ_PIXCLOCK 39722 - /* LCD display method */ #define LCD_EXPANDSION 0x00 @@ -648,23 +646,6 @@ #define LCD_OPENLDI 0x00 #define LCD_SPWG 0x01 -/* Define display timing -*/ -struct display_timing { - u16 hor_total; - u16 hor_addr; - u16 hor_blank_start; - u16 hor_blank_end; - u16 hor_sync_start; - u16 hor_sync_end; - u16 ver_total; - u16 ver_addr; - u16 ver_blank_start; - u16 ver_blank_end; - u16 ver_sync_start; - u16 ver_sync_end; -}; - struct crt_mode_table { int refresh_rate; int h_sync_polarity; diff --git a/drivers/video/via/via-core.c b/drivers/video/via/via-core.c index eb112b621735..dd58b530c0df 100644 --- a/drivers/video/via/via-core.c +++ b/drivers/video/via/via-core.c @@ -35,7 +35,7 @@ static struct via_port_cfg adap_configs[] = { * The OLPC XO-1.5 puts the camera power and reset lines onto * GPIO 2C. */ -static const struct via_port_cfg olpc_adap_configs[] = { +static struct via_port_cfg olpc_adap_configs[] = { [VIA_PORT_26] = { VIA_PORT_I2C, VIA_MODE_I2C, VIASR, 0x26 }, [VIA_PORT_31] = { VIA_PORT_I2C, VIA_MODE_I2C, VIASR, 0x31 }, [VIA_PORT_25] = { VIA_PORT_GPIO, VIA_MODE_GPIO, VIASR, 0x25 }, diff --git a/drivers/video/via/via_modesetting.c b/drivers/video/via/via_modesetting.c index 3cddcff88ab9..0e431aee17bb 100644 --- a/drivers/video/via/via_modesetting.c +++ b/drivers/video/via/via_modesetting.c @@ -29,6 +29,110 @@ #include "share.h" #include "debug.h" + +void via_set_primary_timing(const struct display_timing *timing) +{ + struct display_timing raw; + + raw.hor_total = timing->hor_total / 8 - 5; + raw.hor_addr = timing->hor_addr / 8 - 1; + raw.hor_blank_start = timing->hor_blank_start / 8 - 1; + raw.hor_blank_end = timing->hor_blank_end / 8 - 1; + raw.hor_sync_start = timing->hor_sync_start / 8; + raw.hor_sync_end = timing->hor_sync_end / 8; + raw.ver_total = timing->ver_total - 2; + raw.ver_addr = timing->ver_addr - 1; + raw.ver_blank_start = timing->ver_blank_start - 1; + raw.ver_blank_end = timing->ver_blank_end - 1; + raw.ver_sync_start = timing->ver_sync_start - 1; + raw.ver_sync_end = timing->ver_sync_end - 1; + + /* unlock timing registers */ + via_write_reg_mask(VIACR, 0x11, 0x00, 0x80); + + via_write_reg(VIACR, 0x00, raw.hor_total & 0xFF); + via_write_reg(VIACR, 0x01, raw.hor_addr & 0xFF); + via_write_reg(VIACR, 0x02, raw.hor_blank_start & 0xFF); + via_write_reg_mask(VIACR, 0x03, raw.hor_blank_end & 0x1F, 0x1F); + via_write_reg(VIACR, 0x04, raw.hor_sync_start & 0xFF); + via_write_reg_mask(VIACR, 0x05, (raw.hor_sync_end & 0x1F) + | (raw.hor_blank_end << (7 - 5) & 0x80), 0x9F); + via_write_reg(VIACR, 0x06, raw.ver_total & 0xFF); + via_write_reg_mask(VIACR, 0x07, (raw.ver_total >> 8 & 0x01) + | (raw.ver_addr >> (8 - 1) & 0x02) + | (raw.ver_sync_start >> (8 - 2) & 0x04) + | (raw.ver_blank_start >> (8 - 3) & 0x08) + | (raw.ver_total >> (9 - 5) & 0x20) + | (raw.ver_addr >> (9 - 6) & 0x40) + | (raw.ver_sync_start >> (9 - 7) & 0x80), 0xEF); + via_write_reg_mask(VIACR, 0x09, raw.ver_blank_start >> (9 - 5) & 0x20, + 0x20); + via_write_reg(VIACR, 0x10, raw.ver_sync_start & 0xFF); + via_write_reg_mask(VIACR, 0x11, raw.ver_sync_end & 0x0F, 0x0F); + via_write_reg(VIACR, 0x12, raw.ver_addr & 0xFF); + via_write_reg(VIACR, 0x15, raw.ver_blank_start & 0xFF); + via_write_reg(VIACR, 0x16, raw.ver_blank_end & 0xFF); + via_write_reg_mask(VIACR, 0x33, (raw.hor_sync_start >> (8 - 4) & 0x10) + | (raw.hor_blank_end >> (6 - 5) & 0x20), 0x30); + via_write_reg_mask(VIACR, 0x35, (raw.ver_total >> 10 & 0x01) + | (raw.ver_sync_start >> (10 - 1) & 0x02) + | (raw.ver_addr >> (10 - 2) & 0x04) + | (raw.ver_blank_start >> (10 - 3) & 0x08), 0x0F); + via_write_reg_mask(VIACR, 0x36, raw.hor_total >> (8 - 3) & 0x08, 0x08); + + /* lock timing registers */ + via_write_reg_mask(VIACR, 0x11, 0x80, 0x80); + + /* reset timing control */ + via_write_reg_mask(VIACR, 0x17, 0x00, 0x80); + via_write_reg_mask(VIACR, 0x17, 0x80, 0x80); +} + +void via_set_secondary_timing(const struct display_timing *timing) +{ + struct display_timing raw; + + raw.hor_total = timing->hor_total - 1; + raw.hor_addr = timing->hor_addr - 1; + raw.hor_blank_start = timing->hor_blank_start - 1; + raw.hor_blank_end = timing->hor_blank_end - 1; + raw.hor_sync_start = timing->hor_sync_start - 1; + raw.hor_sync_end = timing->hor_sync_end - 1; + raw.ver_total = timing->ver_total - 1; + raw.ver_addr = timing->ver_addr - 1; + raw.ver_blank_start = timing->ver_blank_start - 1; + raw.ver_blank_end = timing->ver_blank_end - 1; + raw.ver_sync_start = timing->ver_sync_start - 1; + raw.ver_sync_end = timing->ver_sync_end - 1; + + via_write_reg(VIACR, 0x50, raw.hor_total & 0xFF); + via_write_reg(VIACR, 0x51, raw.hor_addr & 0xFF); + via_write_reg(VIACR, 0x52, raw.hor_blank_start & 0xFF); + via_write_reg(VIACR, 0x53, raw.hor_blank_end & 0xFF); + via_write_reg(VIACR, 0x54, (raw.hor_blank_start >> 8 & 0x07) + | (raw.hor_blank_end >> (8 - 3) & 0x38) + | (raw.hor_sync_start >> (8 - 6) & 0xC0)); + via_write_reg_mask(VIACR, 0x55, (raw.hor_total >> 8 & 0x0F) + | (raw.hor_addr >> (8 - 4) & 0x70), 0x7F); + via_write_reg(VIACR, 0x56, raw.hor_sync_start & 0xFF); + via_write_reg(VIACR, 0x57, raw.hor_sync_end & 0xFF); + via_write_reg(VIACR, 0x58, raw.ver_total & 0xFF); + via_write_reg(VIACR, 0x59, raw.ver_addr & 0xFF); + via_write_reg(VIACR, 0x5A, raw.ver_blank_start & 0xFF); + via_write_reg(VIACR, 0x5B, raw.ver_blank_end & 0xFF); + via_write_reg(VIACR, 0x5C, (raw.ver_blank_start >> 8 & 0x07) + | (raw.ver_blank_end >> (8 - 3) & 0x38) + | (raw.hor_sync_end >> (8 - 6) & 0x40) + | (raw.hor_sync_start >> (10 - 7) & 0x80)); + via_write_reg(VIACR, 0x5D, (raw.ver_total >> 8 & 0x07) + | (raw.ver_addr >> (8 - 3) & 0x38) + | (raw.hor_blank_end >> (11 - 6) & 0x40) + | (raw.hor_sync_start >> (11 - 7) & 0x80)); + via_write_reg(VIACR, 0x5E, raw.ver_sync_start & 0xFF); + via_write_reg(VIACR, 0x5F, (raw.ver_sync_end & 0x1F) + | (raw.ver_sync_start >> (8 - 5) & 0xE0)); +} + void via_set_primary_address(u32 addr) { DEBUG_MSG(KERN_DEBUG "via_set_primary_address(0x%08X)\n", addr); diff --git a/drivers/video/via/via_modesetting.h b/drivers/video/via/via_modesetting.h index ae35cfdeb37c..06e09fe351ae 100644 --- a/drivers/video/via/via_modesetting.h +++ b/drivers/video/via/via_modesetting.h @@ -28,6 +28,29 @@ #include <linux/types.h> + +#define VIA_PITCH_SIZE (1<<3) +#define VIA_PITCH_MAX 0x3FF8 + + +struct display_timing { + u16 hor_total; + u16 hor_addr; + u16 hor_blank_start; + u16 hor_blank_end; + u16 hor_sync_start; + u16 hor_sync_end; + u16 ver_total; + u16 ver_addr; + u16 ver_blank_start; + u16 ver_blank_end; + u16 ver_sync_start; + u16 ver_sync_end; +}; + + +void via_set_primary_timing(const struct display_timing *timing); +void via_set_secondary_timing(const struct display_timing *timing); void via_set_primary_address(u32 addr); void via_set_secondary_address(u32 addr); void via_set_primary_pitch(u32 pitch); diff --git a/drivers/video/via/viafbdev.c b/drivers/video/via/viafbdev.c index 53aa4430d86e..a13c258bd32f 100644 --- a/drivers/video/via/viafbdev.c +++ b/drivers/video/via/viafbdev.c @@ -38,8 +38,6 @@ static char *viafb_mode1; static int viafb_bpp = 32; static int viafb_bpp1 = 32; -static unsigned int viafb_second_xres = 640; -static unsigned int viafb_second_yres = 480; static unsigned int viafb_second_offset; static int viafb_second_size; @@ -151,7 +149,8 @@ static void viafb_update_fix(struct fb_info *info) info->fix.visual = bpp == 8 ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; - info->fix.line_length = (info->var.xres_virtual * bpp / 8 + 7) & ~7; + info->fix.line_length = ALIGN(info->var.xres_virtual * bpp / 8, + VIA_PITCH_SIZE); } static void viafb_setup_fixinfo(struct fb_fix_screeninfo *fix, @@ -200,7 +199,6 @@ static int viafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { int depth, refresh; - struct VideoModeTable *vmode_entry; struct viafb_par *ppar = info->par; u32 line; @@ -210,8 +208,10 @@ static int viafb_check_var(struct fb_var_screeninfo *var, if (var->vmode & FB_VMODE_INTERLACED || var->vmode & FB_VMODE_DOUBLE) return -EINVAL; - vmode_entry = viafb_get_mode(var->xres, var->yres); - if (!vmode_entry) { + /* the refresh rate is not important here, as we only want to know + * whether the resolution exists + */ + if (!viafb_get_best_mode(var->xres, var->yres, 60)) { DEBUG_MSG(KERN_INFO "viafb: Mode %dx%dx%d not supported!!\n", var->xres, var->yres, var->bits_per_pixel); @@ -238,8 +238,12 @@ static int viafb_check_var(struct fb_var_screeninfo *var, depth = 24; viafb_fill_var_color_info(var, depth); - line = (var->xres_virtual * var->bits_per_pixel / 8 + 7) & ~7; - if (line * var->yres_virtual > ppar->memsize) + if (var->xres_virtual < var->xres) + var->xres_virtual = var->xres; + + line = ALIGN(var->xres_virtual * var->bits_per_pixel / 8, + VIA_PITCH_SIZE); + if (line > VIA_PITCH_MAX || line * var->yres_virtual > ppar->memsize) return -EINVAL; /* Based on var passed in to calculate the refresh, @@ -249,7 +253,8 @@ static int viafb_check_var(struct fb_var_screeninfo *var, get_var_refresh(var)); /* Adjust var according to our driver's own table */ - viafb_fill_var_timing_info(var, refresh, vmode_entry); + viafb_fill_var_timing_info(var, + viafb_get_best_mode(var->xres, var->yres, refresh)); if (var->accel_flags & FB_ACCELF_TEXT && !ppar->shared->vdev->engine_mmio) var->accel_flags = 0; @@ -260,7 +265,6 @@ static int viafb_check_var(struct fb_var_screeninfo *var, static int viafb_set_par(struct fb_info *info) { struct viafb_par *viapar = info->par; - struct VideoModeTable *vmode_entry, *vmode_entry1 = NULL; int refresh; DEBUG_MSG(KERN_INFO "viafb_set_par!\n"); @@ -269,10 +273,7 @@ static int viafb_set_par(struct fb_info *info) viafb_update_device_setting(viafbinfo->var.xres, viafbinfo->var.yres, viafbinfo->var.bits_per_pixel, 0); - vmode_entry = viafb_get_mode(viafbinfo->var.xres, viafbinfo->var.yres); if (viafb_dual_fb) { - vmode_entry1 = viafb_get_mode(viafbinfo1->var.xres, - viafbinfo1->var.yres); viafb_update_device_setting(viafbinfo1->var.xres, viafbinfo1->var.yres, viafbinfo1->var.bits_per_pixel, 1); @@ -280,8 +281,6 @@ static int viafb_set_par(struct fb_info *info) DEBUG_MSG(KERN_INFO "viafb_second_xres = %d, viafb_second_yres = %d, bpp = %d\n", viafb_second_xres, viafb_second_yres, viafb_bpp1); - vmode_entry1 = viafb_get_mode(viafb_second_xres, - viafb_second_yres); viafb_update_device_setting(viafb_second_xres, viafb_second_yres, viafb_bpp1, 1); @@ -289,7 +288,8 @@ static int viafb_set_par(struct fb_info *info) refresh = viafb_get_refresh(info->var.xres, info->var.yres, get_var_refresh(&info->var)); - if (vmode_entry) { + if (viafb_get_best_mode(viafbinfo->var.xres, viafbinfo->var.yres, + refresh)) { if (viafb_dual_fb && viapar->iga_path == IGA2) { viafb_bpp1 = info->var.bits_per_pixel; viafb_refresh1 = refresh; @@ -302,8 +302,7 @@ static int viafb_set_par(struct fb_info *info) info->flags &= ~FBINFO_HWACCEL_DISABLED; else info->flags |= FBINFO_HWACCEL_DISABLED; - viafb_setmode(vmode_entry, info->var.bits_per_pixel, - vmode_entry1, viafb_bpp1); + viafb_setmode(info->var.bits_per_pixel, viafb_bpp1); viafb_pan_display(&info->var, info); } @@ -348,8 +347,9 @@ static int viafb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) { struct viafb_par *viapar = info->par; - u32 vram_addr = (var->yoffset * var->xres_virtual + var->xoffset) - * (var->bits_per_pixel / 8) + viapar->vram_addr; + u32 vram_addr = viapar->vram_addr + + var->yoffset * info->fix.line_length + + var->xoffset * info->var.bits_per_pixel / 8; DEBUG_MSG(KERN_DEBUG "viafb_pan_display, address = %d\n", vram_addr); if (!viafb_dual_fb) { @@ -1158,7 +1158,8 @@ static ssize_t viafb_dvp0_proc_write(struct file *file, for (i = 0; i < 3; i++) { value = strsep(&pbuf, " "); if (value != NULL) { - strict_strtoul(value, 0, (unsigned long *)®_val); + if (kstrtou8(value, 0, ®_val) < 0) + return -EINVAL; DEBUG_MSG(KERN_INFO "DVP0:reg_val[%l]=:%x\n", i, reg_val); switch (i) { @@ -1228,7 +1229,8 @@ static ssize_t viafb_dvp1_proc_write(struct file *file, for (i = 0; i < 3; i++) { value = strsep(&pbuf, " "); if (value != NULL) { - strict_strtoul(value, 0, (unsigned long *)®_val); + if (kstrtou8(value, 0, ®_val) < 0) + return -EINVAL; switch (i) { case 0: viafb_write_reg_mask(CR9B, VIACR, @@ -1286,7 +1288,8 @@ static ssize_t viafb_dfph_proc_write(struct file *file, if (copy_from_user(&buf[0], buffer, length)) return -EFAULT; buf[length - 1] = '\0'; /*Ensure end string */ - strict_strtoul(&buf[0], 0, (unsigned long *)®_val); + if (kstrtou8(buf, 0, ®_val) < 0) + return -EINVAL; viafb_write_reg_mask(CR97, VIACR, reg_val, 0x0f); return count; } @@ -1325,7 +1328,8 @@ static ssize_t viafb_dfpl_proc_write(struct file *file, if (copy_from_user(&buf[0], buffer, length)) return -EFAULT; buf[length - 1] = '\0'; /*Ensure end string */ - strict_strtoul(&buf[0], 0, (unsigned long *)®_val); + if (kstrtou8(buf, 0, ®_val) < 0) + return -EINVAL; viafb_write_reg_mask(CR99, VIACR, reg_val, 0x0f); return count; } @@ -1394,8 +1398,8 @@ static ssize_t viafb_vt1636_proc_write(struct file *file, for (i = 0; i < 2; i++) { value = strsep(&pbuf, " "); if (value != NULL) { - strict_strtoul(value, 0, - (unsigned long *)®_val.Data); + if (kstrtou8(value, 0, ®_val.Data) < 0) + return -EINVAL; switch (i) { case 0: reg_val.Index = 0x08; @@ -1431,8 +1435,8 @@ static ssize_t viafb_vt1636_proc_write(struct file *file, for (i = 0; i < 2; i++) { value = strsep(&pbuf, " "); if (value != NULL) { - strict_strtoul(value, 0, - (unsigned long *)®_val.Data); + if (kstrtou8(value, 0, ®_val.Data) < 0) + return -EINVAL; switch (i) { case 0: reg_val.Index = 0x08; @@ -1729,7 +1733,6 @@ static struct viafb_pm_hooks viafb_fb_pm_hooks = { int __devinit via_fb_pci_probe(struct viafb_dev *vdev) { u32 default_xres, default_yres; - struct VideoModeTable *vmode_entry; struct fb_var_screeninfo default_var; int rc; u32 viafb_par_length; @@ -1802,7 +1805,6 @@ int __devinit via_fb_pci_probe(struct viafb_dev *vdev) } parse_mode(viafb_mode, &default_xres, &default_yres); - vmode_entry = viafb_get_mode(default_xres, default_yres); if (viafb_SAMM_ON == 1) parse_mode(viafb_mode1, &viafb_second_xres, &viafb_second_yres); @@ -1812,9 +1814,8 @@ int __devinit via_fb_pci_probe(struct viafb_dev *vdev) default_var.xres_virtual = default_xres; default_var.yres_virtual = default_yres; default_var.bits_per_pixel = viafb_bpp; - viafb_fill_var_timing_info(&default_var, viafb_get_refresh( - default_var.xres, default_var.yres, viafb_refresh), - viafb_get_mode(default_var.xres, default_var.yres)); + viafb_fill_var_timing_info(&default_var, viafb_get_best_mode( + default_var.xres, default_var.yres, viafb_refresh)); viafb_setup_fixinfo(&viafbinfo->fix, viaparinfo); viafbinfo->var = default_var; @@ -1853,9 +1854,8 @@ int __devinit via_fb_pci_probe(struct viafb_dev *vdev) default_var.xres_virtual = viafb_second_xres; default_var.yres_virtual = viafb_second_yres; default_var.bits_per_pixel = viafb_bpp1; - viafb_fill_var_timing_info(&default_var, viafb_get_refresh( - default_var.xres, default_var.yres, viafb_refresh1), - viafb_get_mode(default_var.xres, default_var.yres)); + viafb_fill_var_timing_info(&default_var, viafb_get_best_mode( + default_var.xres, default_var.yres, viafb_refresh1)); viafb_setup_fixinfo(&viafbinfo1->fix, viaparinfo1); viafb_check_var(&default_var, viafbinfo1); @@ -1950,61 +1950,67 @@ static int __init viafb_setup(void) if (!*this_opt) continue; - if (!strncmp(this_opt, "viafb_mode1=", 12)) + if (!strncmp(this_opt, "viafb_mode1=", 12)) { viafb_mode1 = kstrdup(this_opt + 12, GFP_KERNEL); - else if (!strncmp(this_opt, "viafb_mode=", 11)) + } else if (!strncmp(this_opt, "viafb_mode=", 11)) { viafb_mode = kstrdup(this_opt + 11, GFP_KERNEL); - else if (!strncmp(this_opt, "viafb_bpp1=", 11)) - strict_strtoul(this_opt + 11, 0, - (unsigned long *)&viafb_bpp1); - else if (!strncmp(this_opt, "viafb_bpp=", 10)) - strict_strtoul(this_opt + 10, 0, - (unsigned long *)&viafb_bpp); - else if (!strncmp(this_opt, "viafb_refresh1=", 15)) - strict_strtoul(this_opt + 15, 0, - (unsigned long *)&viafb_refresh1); - else if (!strncmp(this_opt, "viafb_refresh=", 14)) - strict_strtoul(this_opt + 14, 0, - (unsigned long *)&viafb_refresh); - else if (!strncmp(this_opt, "viafb_lcd_dsp_method=", 21)) - strict_strtoul(this_opt + 21, 0, - (unsigned long *)&viafb_lcd_dsp_method); - else if (!strncmp(this_opt, "viafb_lcd_panel_id=", 19)) - strict_strtoul(this_opt + 19, 0, - (unsigned long *)&viafb_lcd_panel_id); - else if (!strncmp(this_opt, "viafb_accel=", 12)) - strict_strtoul(this_opt + 12, 0, - (unsigned long *)&viafb_accel); - else if (!strncmp(this_opt, "viafb_SAMM_ON=", 14)) - strict_strtoul(this_opt + 14, 0, - (unsigned long *)&viafb_SAMM_ON); - else if (!strncmp(this_opt, "viafb_active_dev=", 17)) + } else if (!strncmp(this_opt, "viafb_bpp1=", 11)) { + if (kstrtouint(this_opt + 11, 0, &viafb_bpp1) < 0) + return -EINVAL; + } else if (!strncmp(this_opt, "viafb_bpp=", 10)) { + if (kstrtouint(this_opt + 10, 0, &viafb_bpp) < 0) + return -EINVAL; + } else if (!strncmp(this_opt, "viafb_refresh1=", 15)) { + if (kstrtoint(this_opt + 15, 0, &viafb_refresh1) < 0) + return -EINVAL; + } else if (!strncmp(this_opt, "viafb_refresh=", 14)) { + if (kstrtoint(this_opt + 14, 0, &viafb_refresh) < 0) + return -EINVAL; + } else if (!strncmp(this_opt, "viafb_lcd_dsp_method=", 21)) { + if (kstrtoint(this_opt + 21, 0, + &viafb_lcd_dsp_method) < 0) + return -EINVAL; + } else if (!strncmp(this_opt, "viafb_lcd_panel_id=", 19)) { + if (kstrtoint(this_opt + 19, 0, + &viafb_lcd_panel_id) < 0) + return -EINVAL; + } else if (!strncmp(this_opt, "viafb_accel=", 12)) { + if (kstrtoint(this_opt + 12, 0, &viafb_accel) < 0) + return -EINVAL; + } else if (!strncmp(this_opt, "viafb_SAMM_ON=", 14)) { + if (kstrtoint(this_opt + 14, 0, &viafb_SAMM_ON) < 0) + return -EINVAL; + } else if (!strncmp(this_opt, "viafb_active_dev=", 17)) { viafb_active_dev = kstrdup(this_opt + 17, GFP_KERNEL); - else if (!strncmp(this_opt, - "viafb_display_hardware_layout=", 30)) - strict_strtoul(this_opt + 30, 0, - (unsigned long *)&viafb_display_hardware_layout); - else if (!strncmp(this_opt, "viafb_second_size=", 18)) - strict_strtoul(this_opt + 18, 0, - (unsigned long *)&viafb_second_size); - else if (!strncmp(this_opt, - "viafb_platform_epia_dvi=", 24)) - strict_strtoul(this_opt + 24, 0, - (unsigned long *)&viafb_platform_epia_dvi); - else if (!strncmp(this_opt, - "viafb_device_lcd_dualedge=", 26)) - strict_strtoul(this_opt + 26, 0, - (unsigned long *)&viafb_device_lcd_dualedge); - else if (!strncmp(this_opt, "viafb_bus_width=", 16)) - strict_strtoul(this_opt + 16, 0, - (unsigned long *)&viafb_bus_width); - else if (!strncmp(this_opt, "viafb_lcd_mode=", 15)) - strict_strtoul(this_opt + 15, 0, - (unsigned long *)&viafb_lcd_mode); - else if (!strncmp(this_opt, "viafb_lcd_port=", 15)) + } else if (!strncmp(this_opt, + "viafb_display_hardware_layout=", 30)) { + if (kstrtoint(this_opt + 30, 0, + &viafb_display_hardware_layout) < 0) + return -EINVAL; + } else if (!strncmp(this_opt, "viafb_second_size=", 18)) { + if (kstrtoint(this_opt + 18, 0, &viafb_second_size) < 0) + return -EINVAL; + } else if (!strncmp(this_opt, + "viafb_platform_epia_dvi=", 24)) { + if (kstrtoint(this_opt + 24, 0, + &viafb_platform_epia_dvi) < 0) + return -EINVAL; + } else if (!strncmp(this_opt, + "viafb_device_lcd_dualedge=", 26)) { + if (kstrtoint(this_opt + 26, 0, + &viafb_device_lcd_dualedge) < 0) + return -EINVAL; + } else if (!strncmp(this_opt, "viafb_bus_width=", 16)) { + if (kstrtoint(this_opt + 16, 0, &viafb_bus_width) < 0) + return -EINVAL; + } else if (!strncmp(this_opt, "viafb_lcd_mode=", 15)) { + if (kstrtoint(this_opt + 15, 0, &viafb_lcd_mode) < 0) + return -EINVAL; + } else if (!strncmp(this_opt, "viafb_lcd_port=", 15)) { viafb_lcd_port = kstrdup(this_opt + 15, GFP_KERNEL); - else if (!strncmp(this_opt, "viafb_dvi_port=", 15)) + } else if (!strncmp(this_opt, "viafb_dvi_port=", 15)) { viafb_dvi_port = kstrdup(this_opt + 15, GFP_KERNEL); + } } return 0; } @@ -2028,9 +2034,9 @@ int __init viafb_init(void) return r; #endif if (parse_mode(viafb_mode, &dummy_x, &dummy_y) - || !viafb_get_mode(dummy_x, dummy_y) + || !viafb_get_best_mode(dummy_x, dummy_y, viafb_refresh) || parse_mode(viafb_mode1, &dummy_x, &dummy_y) - || !viafb_get_mode(dummy_x, dummy_y) + || !viafb_get_best_mode(dummy_x, dummy_y, viafb_refresh1) || viafb_bpp < 0 || viafb_bpp > 32 || viafb_bpp1 < 0 || viafb_bpp1 > 32 || parse_active_dev()) diff --git a/drivers/video/via/viamode.c b/drivers/video/via/viamode.c index 58df74e1417e..0911cac1b2ff 100644 --- a/drivers/video/via/viamode.c +++ b/drivers/video/via/viamode.c @@ -281,7 +281,7 @@ static struct crt_mode_table CRTM640x480[] = { /*r_rate,hsp,vsp */ /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ {REFRESH_60, M640X480_R60_HSP, M640X480_R60_VSP, - {800, 640, 648, 144, 656, 96, 525, 480, 480, 45, 490, 2} }, + {800, 640, 640, 160, 656, 96, 525, 480, 480, 45, 490, 2} }, {REFRESH_75, M640X480_R75_HSP, M640X480_R75_VSP, {840, 640, 640, 200, 656, 64, 500, 480, 480, 20, 481, 3} }, {REFRESH_85, M640X480_R85_HSP, M640X480_R85_VSP, @@ -863,26 +863,56 @@ int NUM_TOTAL_CLE266_ModeXregs = ARRAY_SIZE(CLE266_ModeXregs); int NUM_TOTAL_PATCH_MODE = ARRAY_SIZE(res_patch_table); -struct VideoModeTable *viafb_get_mode(int hres, int vres) +static struct VideoModeTable *get_modes(struct VideoModeTable *vmt, int n, + int hres, int vres) { - u32 i; - for (i = 0; i < ARRAY_SIZE(viafb_modes); i++) - if (viafb_modes[i].mode_array && - viafb_modes[i].crtc[0].crtc.hor_addr == hres && - viafb_modes[i].crtc[0].crtc.ver_addr == vres) + int i; + + for (i = 0; i < n; i++) + if (vmt[i].mode_array && + vmt[i].crtc[0].crtc.hor_addr == hres && + vmt[i].crtc[0].crtc.ver_addr == vres) return &viafb_modes[i]; return NULL; } -struct VideoModeTable *viafb_get_rb_mode(int hres, int vres) +static struct crt_mode_table *get_best_mode(struct VideoModeTable *vmt, + int refresh) { - u32 i; - for (i = 0; i < ARRAY_SIZE(viafb_rb_modes); i++) - if (viafb_rb_modes[i].mode_array && - viafb_rb_modes[i].crtc[0].crtc.hor_addr == hres && - viafb_rb_modes[i].crtc[0].crtc.ver_addr == vres) - return &viafb_rb_modes[i]; + struct crt_mode_table *best; + int i; - return NULL; + if (!vmt) + return NULL; + + best = &vmt->crtc[0]; + for (i = 1; i < vmt->mode_array; i++) { + if (abs(vmt->crtc[i].refresh_rate - refresh) + < abs(best->refresh_rate - refresh)) + best = &vmt->crtc[i]; + } + + return best; +} + +static struct VideoModeTable *viafb_get_mode(int hres, int vres) +{ + return get_modes(viafb_modes, ARRAY_SIZE(viafb_modes), hres, vres); +} + +struct crt_mode_table *viafb_get_best_mode(int hres, int vres, int refresh) +{ + return get_best_mode(viafb_get_mode(hres, vres), refresh); +} + +static struct VideoModeTable *viafb_get_rb_mode(int hres, int vres) +{ + return get_modes(viafb_rb_modes, ARRAY_SIZE(viafb_rb_modes), hres, + vres); +} + +struct crt_mode_table *viafb_get_best_rb_mode(int hres, int vres, int refresh) +{ + return get_best_mode(viafb_get_rb_mode(hres, vres), refresh); } diff --git a/drivers/video/via/viamode.h b/drivers/video/via/viamode.h index 3751289eb450..5917a2b00e1b 100644 --- a/drivers/video/via/viamode.h +++ b/drivers/video/via/viamode.h @@ -60,7 +60,7 @@ extern struct io_reg PM1024x768[]; extern struct patch_table res_patch_table[]; extern struct VPITTable VPIT; -struct VideoModeTable *viafb_get_mode(int hres, int vres); -struct VideoModeTable *viafb_get_rb_mode(int hres, int vres); +struct crt_mode_table *viafb_get_best_mode(int hres, int vres, int refresh); +struct crt_mode_table *viafb_get_best_rb_mode(int hres, int vres, int refresh); #endif /* __VIAMODE_H__ */ diff --git a/drivers/video/vt8500lcdfb.c b/drivers/video/vt8500lcdfb.c index 0e120d67eb65..777c21dd7a6b 100644 --- a/drivers/video/vt8500lcdfb.c +++ b/drivers/video/vt8500lcdfb.c @@ -210,8 +210,8 @@ static int vt8500lcd_pan_display(struct fb_var_screeninfo *var, struct vt8500lcd_info *fbi = to_vt8500lcd_info(info); writel((1 << 31) - | (((var->xres_virtual - var->xres) * pixlen / 4) << 20) - | (off >> 2), fbi->regbase + 0x20); + | (((info->var.xres_virtual - info->var.xres) * pixlen / 4) << 20) + | (off >> 2), fbi->regbase + 0x20); return 0; } @@ -355,7 +355,7 @@ static int __devinit vt8500lcd_probe(struct platform_device *pdev) goto failed_free_palette; } - ret = request_irq(irq, vt8500lcd_handle_irq, IRQF_DISABLED, "LCD", fbi); + ret = request_irq(irq, vt8500lcd_handle_irq, 0, "LCD", fbi); if (ret) { dev_err(&pdev->dev, "request_irq failed: %d\n", ret); ret = -EBUSY; diff --git a/drivers/video/vt8623fb.c b/drivers/video/vt8623fb.c index f9b3e3dc2421..4e74d262cf3e 100644 --- a/drivers/video/vt8623fb.c +++ b/drivers/video/vt8623fb.c @@ -620,13 +620,14 @@ static int vt8623fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *i unsigned int offset; /* Calculate the offset */ - if (var->bits_per_pixel == 0) { - offset = (var->yoffset / 16) * var->xres_virtual + var->xoffset; + if (info->var.bits_per_pixel == 0) { + offset = (var->yoffset / 16) * info->var.xres_virtual + + var->xoffset; offset = offset >> 3; } else { offset = (var->yoffset * info->fix.line_length) + - (var->xoffset * var->bits_per_pixel / 8); - offset = offset >> ((var->bits_per_pixel == 4) ? 2 : 1); + (var->xoffset * info->var.bits_per_pixel / 8); + offset = offset >> ((info->var.bits_per_pixel == 4) ? 2 : 1); } /* Set the offset */ diff --git a/drivers/video/xilinxfb.c b/drivers/video/xilinxfb.c index 77dea015ff69..fcb6cd90f64d 100644 --- a/drivers/video/xilinxfb.c +++ b/drivers/video/xilinxfb.c @@ -23,7 +23,6 @@ #include <linux/device.h> #include <linux/module.h> #include <linux/kernel.h> -#include <linux/version.h> #include <linux/errno.h> #include <linux/string.h> #include <linux/mm.h> |