diff options
author | Ingo Molnar <mingo@elte.hu> | 2010-08-31 09:45:21 +0200 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2010-08-31 09:45:46 +0200 |
commit | daab7fc734a53fdeaf844b7c03053118ad1769da (patch) | |
tree | 575deb3cdcc6dda562acaed6f7c29bc81ae01cf2 /drivers/video | |
parent | 774ea0bcb27f57b6fd521b3b6c43237782fed4b9 (diff) | |
parent | 2bfc96a127bc1cc94d26bfaa40159966064f9c8c (diff) |
Merge commit 'v2.6.36-rc3' into x86/memblock
Conflicts:
arch/x86/kernel/trampoline.c
mm/memblock.c
Merge reason: Resolve the conflicts, update to latest upstream.
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'drivers/video')
74 files changed, 5226 insertions, 1941 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 3d94a147172..8b31fdfefc9 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1505,7 +1505,7 @@ config FB_SIS_315 config FB_VIA tristate "VIA UniChrome (Pro) and Chrome9 display support" - depends on FB && PCI + depends on FB && PCI && X86 select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT @@ -1871,6 +1871,7 @@ config FB_MBX_DEBUG config FB_FSL_DIU tristate "Freescale DIU framebuffer support" depends on FB && FSL_SOC + select FB_MODE_HELPERS select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT @@ -1895,6 +1896,13 @@ config FB_W100 If unsure, say N. +config SH_MIPI_DSI + tristate + depends on (SUPERH || ARCH_SHMOBILE) && HAVE_CLK + +config SH_LCD_MIPI_DSI + bool + config FB_SH_MOBILE_LCDC tristate "SuperH Mobile LCDC framebuffer support" depends on FB && (SUPERH || ARCH_SHMOBILE) && HAVE_CLK @@ -1903,9 +1911,17 @@ config FB_SH_MOBILE_LCDC select FB_SYS_IMAGEBLIT select FB_SYS_FOPS select FB_DEFERRED_IO + select SH_MIPI_DSI if SH_LCD_MIPI_DSI ---help--- Frame buffer driver for the on-chip SH-Mobile LCD controller. +config FB_SH_MOBILE_HDMI + tristate "SuperH Mobile HDMI controller support" + depends on FB_SH_MOBILE_LCDC + select FB_MODE_HELPERS + ---help--- + Driver for the on-chip SH-Mobile HDMI controller. + config FB_TMIO tristate "Toshiba Mobile IO FrameBuffer support" depends on FB && MFD_CORE @@ -1930,7 +1946,7 @@ config FB_TMIO_ACCELL config FB_S3C tristate "Samsung S3C framebuffer support" - depends on FB && ARCH_S3C64XX + depends on FB && S3C_DEV_FB select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT @@ -2229,6 +2245,15 @@ config FB_BROADSHEET and could also have been called by other names when coupled with a bridge adapter. +config FB_JZ4740 + tristate "JZ4740 LCD framebuffer support" + depends on FB && MACH_JZ4740 + select FB_SYS_FILLRECT + select FB_SYS_COPYAREA + select FB_SYS_IMAGEBLIT + help + Framebuffer support for the JZ4740 SoC. + source "drivers/video/omap/Kconfig" source "drivers/video/omap2/Kconfig" diff --git a/drivers/video/Makefile b/drivers/video/Makefile index ddc2af2ba45..485e8ed1318 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -123,6 +123,8 @@ obj-$(CONFIG_FB_IBM_GXT4500) += gxt4500.o obj-$(CONFIG_FB_PS3) += ps3fb.o obj-$(CONFIG_FB_SM501) += sm501fb.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 obj-$(CONFIG_FB_SH_MOBILE_LCDC) += sh_mobile_lcdcfb.o obj-$(CONFIG_FB_OMAP) += omap/ obj-y += omap2/ @@ -131,6 +133,7 @@ obj-$(CONFIG_FB_CARMINE) += carminefb.o obj-$(CONFIG_FB_MB862XX) += mb862xx/ obj-$(CONFIG_FB_MSM) += msm/ obj-$(CONFIG_FB_NUC900) += nuc900fb.o +obj-$(CONFIG_FB_JZ4740) += jz4740_fb.o # Platform or fallback drivers go here obj-$(CONFIG_FB_UVESA) += uvesafb.o diff --git a/drivers/video/amba-clcd.c b/drivers/video/amba-clcd.c index afe21e6eb54..1c2c68356ea 100644 --- a/drivers/video/amba-clcd.c +++ b/drivers/video/amba-clcd.c @@ -80,7 +80,10 @@ static void clcdfb_disable(struct clcd_fb *fb) /* * Disable CLCD clock source. */ - clk_disable(fb->clk); + if (fb->clk_enabled) { + fb->clk_enabled = false; + clk_disable(fb->clk); + } } static void clcdfb_enable(struct clcd_fb *fb, u32 cntl) @@ -88,7 +91,10 @@ static void clcdfb_enable(struct clcd_fb *fb, u32 cntl) /* * Enable the CLCD clock source. */ - clk_enable(fb->clk); + if (!fb->clk_enabled) { + fb->clk_enabled = true; + clk_enable(fb->clk); + } /* * Bring up by first enabling.. diff --git a/drivers/video/backlight/locomolcd.c b/drivers/video/backlight/locomolcd.c index 7571bc26071..d2f59015d51 100644 --- a/drivers/video/backlight/locomolcd.c +++ b/drivers/video/backlight/locomolcd.c @@ -2,7 +2,7 @@ * Backlight control code for Sharp Zaurus SL-5500 * * Copyright 2005 John Lenz <lenz@cs.wisc.edu> - * Maintainer: Pavel Machek <pavel@suse.cz> (unless John wants to :-) + * Maintainer: Pavel Machek <pavel@ucw.cz> (unless John wants to :-) * GPL v2 * * This driver assumes single CPU. That's okay, because collie is @@ -246,6 +246,6 @@ static void __exit locomolcd_exit(void) module_init(locomolcd_init); module_exit(locomolcd_exit); -MODULE_AUTHOR("John Lenz <lenz@cs.wisc.edu>, Pavel Machek <pavel@suse.cz>"); +MODULE_AUTHOR("John Lenz <lenz@cs.wisc.edu>, Pavel Machek <pavel@ucw.cz>"); MODULE_DESCRIPTION("Collie LCD driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/video/bw2.c b/drivers/video/bw2.c index 09f1b9b462f..4dc13467281 100644 --- a/drivers/video/bw2.c +++ b/drivers/video/bw2.c @@ -273,7 +273,7 @@ static int __devinit bw2_do_default_mode(struct bw2_par *par, return 0; } -static int __devinit bw2_probe(struct of_device *op, const struct of_device_id *match) +static int __devinit bw2_probe(struct platform_device *op, const struct of_device_id *match) { struct device_node *dp = op->dev.of_node; struct fb_info *info; @@ -350,7 +350,7 @@ out_err: return err; } -static int __devexit bw2_remove(struct of_device *op) +static int __devexit bw2_remove(struct platform_device *op) { struct fb_info *info = dev_get_drvdata(&op->dev); struct bw2_par *par = info->par; @@ -390,12 +390,12 @@ static int __init bw2_init(void) if (fb_get_options("bw2fb", NULL)) return -ENODEV; - return of_register_driver(&bw2_driver, &of_bus_type); + return of_register_platform_driver(&bw2_driver); } static void __exit bw2_exit(void) { - of_unregister_driver(&bw2_driver); + of_unregister_platform_driver(&bw2_driver); } module_init(bw2_init); diff --git a/drivers/video/carminefb.c b/drivers/video/carminefb.c index d8345fcc4fe..6b19136aa18 100644 --- a/drivers/video/carminefb.c +++ b/drivers/video/carminefb.c @@ -432,7 +432,7 @@ static int init_hardware(struct carmine_hw *hw) u32 loops; u32 ret; - /* Initalize Carmine */ + /* Initialize Carmine */ /* Sets internal clock */ c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_CLOCK_ENABLE, CARMINE_DFLT_IP_CLOCK_ENABLE); diff --git a/drivers/video/cg14.c b/drivers/video/cg14.c index e5dc2241194..24249535ac8 100644 --- a/drivers/video/cg14.c +++ b/drivers/video/cg14.c @@ -446,7 +446,7 @@ static struct sbus_mmap_map __cg14_mmap_map[CG14_MMAP_ENTRIES] __devinitdata = { { .size = 0 } }; -static void cg14_unmap_regs(struct of_device *op, struct fb_info *info, +static void cg14_unmap_regs(struct platform_device *op, struct fb_info *info, struct cg14_par *par) { if (par->regs) @@ -463,7 +463,7 @@ static void cg14_unmap_regs(struct of_device *op, struct fb_info *info, info->screen_base, info->fix.smem_len); } -static int __devinit cg14_probe(struct of_device *op, const struct of_device_id *match) +static int __devinit cg14_probe(struct platform_device *op, const struct of_device_id *match) { struct device_node *dp = op->dev.of_node; struct fb_info *info; @@ -570,7 +570,7 @@ out_err: return err; } -static int __devexit cg14_remove(struct of_device *op) +static int __devexit cg14_remove(struct platform_device *op) { struct fb_info *info = dev_get_drvdata(&op->dev); struct cg14_par *par = info->par; @@ -610,12 +610,12 @@ static int __init cg14_init(void) if (fb_get_options("cg14fb", NULL)) return -ENODEV; - return of_register_driver(&cg14_driver, &of_bus_type); + return of_register_platform_driver(&cg14_driver); } static void __exit cg14_exit(void) { - of_unregister_driver(&cg14_driver); + of_unregister_platform_driver(&cg14_driver); } module_init(cg14_init); diff --git a/drivers/video/cg3.c b/drivers/video/cg3.c index 558d73a948a..09c0c3c4248 100644 --- a/drivers/video/cg3.c +++ b/drivers/video/cg3.c @@ -346,7 +346,7 @@ static int __devinit cg3_do_default_mode(struct cg3_par *par) return 0; } -static int __devinit cg3_probe(struct of_device *op, +static int __devinit cg3_probe(struct platform_device *op, const struct of_device_id *match) { struct device_node *dp = op->dev.of_node; @@ -433,7 +433,7 @@ out_err: return err; } -static int __devexit cg3_remove(struct of_device *op) +static int __devexit cg3_remove(struct platform_device *op) { struct fb_info *info = dev_get_drvdata(&op->dev); struct cg3_par *par = info->par; @@ -477,12 +477,12 @@ static int __init cg3_init(void) if (fb_get_options("cg3fb", NULL)) return -ENODEV; - return of_register_driver(&cg3_driver, &of_bus_type); + return of_register_platform_driver(&cg3_driver); } static void __exit cg3_exit(void) { - of_unregister_driver(&cg3_driver); + of_unregister_platform_driver(&cg3_driver); } module_init(cg3_init); diff --git a/drivers/video/cg6.c b/drivers/video/cg6.c index 480d761a27a..2b5a97058b0 100644 --- a/drivers/video/cg6.c +++ b/drivers/video/cg6.c @@ -718,7 +718,7 @@ static void __devinit cg6_chip_init(struct fb_info *info) sbus_writel(info->var.yres - 1, &fbc->clipmaxy); } -static void cg6_unmap_regs(struct of_device *op, struct fb_info *info, +static void cg6_unmap_regs(struct platform_device *op, struct fb_info *info, struct cg6_par *par) { if (par->fbc) @@ -737,7 +737,7 @@ static void cg6_unmap_regs(struct of_device *op, struct fb_info *info, info->fix.smem_len); } -static int __devinit cg6_probe(struct of_device *op, +static int __devinit cg6_probe(struct platform_device *op, const struct of_device_id *match) { struct device_node *dp = op->dev.of_node; @@ -827,7 +827,7 @@ out_err: return err; } -static int __devexit cg6_remove(struct of_device *op) +static int __devexit cg6_remove(struct platform_device *op) { struct fb_info *info = dev_get_drvdata(&op->dev); struct cg6_par *par = info->par; @@ -870,12 +870,12 @@ static int __init cg6_init(void) if (fb_get_options("cg6fb", NULL)) return -ENODEV; - return of_register_driver(&cg6_driver, &of_bus_type); + return of_register_platform_driver(&cg6_driver); } static void __exit cg6_exit(void) { - of_unregister_driver(&cg6_driver); + of_unregister_platform_driver(&cg6_driver); } module_init(cg6_init); diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig index 8e8f18d29d7..5a35f22372b 100644 --- a/drivers/video/console/Kconfig +++ b/drivers/video/console/Kconfig @@ -6,7 +6,7 @@ menu "Console display driver support" config VGA_CONSOLE bool "VGA text console" if EMBEDDED || !X86 - depends on !ARCH_ACORN && !ARCH_EBSA110 && !4xx && !8xx && !SPARC && !M68K && !PARISC && !FRV && !ARCH_VERSATILE && !SUPERH && !BLACKFIN && !AVR32 && !MN10300 + depends on !4xx && !8xx && !SPARC && !M68K && !PARISC && !FRV && !SUPERH && !BLACKFIN && !AVR32 && !MN10300 && (!ARM || ARCH_FOOTBRIDGE || ARCH_INTEGRATOR || ARCH_NETWINDER) default y help Saying Y here will allow you to use Linux in text mode through a diff --git a/drivers/video/console/bitblit.c b/drivers/video/console/bitblit.c index af88651b073..28b1a834906 100644 --- a/drivers/video/console/bitblit.c +++ b/drivers/video/console/bitblit.c @@ -22,7 +22,7 @@ /* * Accelerated handlers. */ -static inline void update_attr(u8 *dst, u8 *src, int attribute, +static void update_attr(u8 *dst, u8 *src, int attribute, struct vc_data *vc) { int i, offset = (vc->vc_font.height < 10) ? 1 : 2; diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index b0a3fa00706..84f842331df 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -283,10 +283,11 @@ static inline int fbcon_is_inactive(struct vc_data *vc, struct fb_info *info) struct fbcon_ops *ops = info->fbcon_par; return (info->state != FBINFO_STATE_RUNNING || - vc->vc_mode != KD_TEXT || ops->graphics); + vc->vc_mode != KD_TEXT || ops->graphics) && + !vt_force_oops_output(vc); } -static inline int get_color(struct vc_data *vc, struct fb_info *info, +static int get_color(struct vc_data *vc, struct fb_info *info, u16 c, int is_fg) { int depth = fb_get_color_depth(&info->var, &info->fix); @@ -1073,6 +1074,7 @@ static void fbcon_init(struct vc_data *vc, int init) if (p->userfont) charcnt = FNTCHARCNT(p->fontdata); + vc->vc_panic_force_write = !!(info->flags & FBINFO_CAN_FORCE_OUTPUT); vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1); vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800; if (charcnt == 256) { @@ -2342,6 +2344,30 @@ static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch) return 0; } +static int fbcon_debug_enter(struct vc_data *vc) +{ + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fbcon_ops *ops = info->fbcon_par; + + ops->save_graphics = ops->graphics; + ops->graphics = 0; + if (info->fbops->fb_debug_enter) + info->fbops->fb_debug_enter(info); + fbcon_set_palette(vc, color_table); + return 0; +} + +static int fbcon_debug_leave(struct vc_data *vc) +{ + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fbcon_ops *ops = info->fbcon_par; + + ops->graphics = ops->save_graphics; + if (info->fbops->fb_debug_leave) + info->fbops->fb_debug_leave(info); + return 0; +} + static int fbcon_get_font(struct vc_data *vc, struct console_font *font) { u8 *fontdata = vc->vc_font.data; @@ -3276,6 +3302,8 @@ static const struct consw fb_con = { .con_screen_pos = fbcon_screen_pos, .con_getxy = fbcon_getxy, .con_resize = fbcon_resize, + .con_debug_enter = fbcon_debug_enter, + .con_debug_leave = fbcon_debug_leave, }; static struct notifier_block fbcon_event_notifier = { diff --git a/drivers/video/console/fbcon.h b/drivers/video/console/fbcon.h index 89a346880ec..6bd2e0c7f20 100644 --- a/drivers/video/console/fbcon.h +++ b/drivers/video/console/fbcon.h @@ -74,6 +74,7 @@ struct fbcon_ops { int cursor_reset; int blank_state; int graphics; + int save_graphics; /* for debug enter/leave */ int flags; int rotate; int cur_rotate; diff --git a/drivers/video/console/fbcon_ccw.c b/drivers/video/console/fbcon_ccw.c index d135671d996..41b32ae23da 100644 --- a/drivers/video/console/fbcon_ccw.c +++ b/drivers/video/console/fbcon_ccw.c @@ -22,7 +22,7 @@ * Rotation 270 degrees */ -static inline void ccw_update_attr(u8 *dst, u8 *src, int attribute, +static void ccw_update_attr(u8 *dst, u8 *src, int attribute, struct vc_data *vc) { int i, j, offset = (vc->vc_font.height < 10) ? 1 : 2; diff --git a/drivers/video/console/fbcon_cw.c b/drivers/video/console/fbcon_cw.c index 126110f8454..6a737827beb 100644 --- a/drivers/video/console/fbcon_cw.c +++ b/drivers/video/console/fbcon_cw.c @@ -22,7 +22,7 @@ * Rotation 90 degrees */ -static inline void cw_update_attr(u8 *dst, u8 *src, int attribute, +static void cw_update_attr(u8 *dst, u8 *src, int attribute, struct vc_data *vc) { int i, j, offset = (vc->vc_font.height < 10) ? 1 : 2; diff --git a/drivers/video/console/fbcon_ud.c b/drivers/video/console/fbcon_ud.c index 93a3e7381b5..ff0872c0498 100644 --- a/drivers/video/console/fbcon_ud.c +++ b/drivers/video/console/fbcon_ud.c @@ -22,7 +22,7 @@ * Rotation 180 degrees */ -static inline void ud_update_attr(u8 *dst, u8 *src, int attribute, +static void ud_update_attr(u8 *dst, u8 *src, int attribute, struct vc_data *vc) { int i, offset = (vc->vc_font.height < 10) ? 1 : 2; diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index 182dd6f8aad..54e32c51361 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -1108,7 +1108,6 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512) charmap += 4 * cmapsz; #endif - unlock_kernel(); spin_lock_irq(&vga_lock); /* First, the Sequencer */ vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x1); @@ -1192,7 +1191,6 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512) vga_wattr(state->vgabase, VGA_AR_ENABLE_DISPLAY, 0); } spin_unlock_irq(&vga_lock); - lock_kernel(); return 0; } diff --git a/drivers/video/controlfb.c b/drivers/video/controlfb.c index 49fcbe8f18a..c225dcce89e 100644 --- a/drivers/video/controlfb.c +++ b/drivers/video/controlfb.c @@ -40,6 +40,8 @@ #include <linux/vmalloc.h> #include <linux/delay.h> #include <linux/interrupt.h> +#include <linux/of.h> +#include <linux/of_address.h> #include <linux/fb.h> #include <linux/init.h> #include <linux/pci.h> diff --git a/drivers/video/efifb.c b/drivers/video/efifb.c index 4a56f46af40..815f84b0793 100644 --- a/drivers/video/efifb.c +++ b/drivers/video/efifb.c @@ -16,7 +16,7 @@ #include <video/vga.h> -static struct fb_var_screeninfo efifb_defined __initdata = { +static struct fb_var_screeninfo efifb_defined __devinitdata = { .activate = FB_ACTIVATE_NOW, .height = -1, .width = -1, @@ -27,7 +27,7 @@ static struct fb_var_screeninfo efifb_defined __initdata = { .vmode = FB_VMODE_NONINTERLACED, }; -static struct fb_fix_screeninfo efifb_fix __initdata = { +static struct fb_fix_screeninfo efifb_fix __devinitdata = { .id = "EFI VGA", .type = FB_TYPE_PACKED_PIXELS, .accel = FB_ACCEL_NONE, @@ -59,7 +59,7 @@ static struct efifb_dmi_info { int stride; int width; int height; -} dmi_list[] = { +} dmi_list[] __initdata = { [M_I17] = { "i17", 0x80010000, 1472 * 4, 1440, 900 }, [M_I20] = { "i20", 0x80010000, 1728 * 4, 1680, 1050 }, /* guess */ [M_I20_SR] = { "imac7", 0x40010000, 1728 * 4, 1680, 1050 }, @@ -83,7 +83,7 @@ static int set_system(const struct dmi_system_id *id); DMI_MATCH(DMI_PRODUCT_NAME, name) }, \ &dmi_list[enumid] } -static struct dmi_system_id __initdata dmi_system_table[] = { +static const struct dmi_system_id dmi_system_table[] __initconst = { EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "iMac4,1", M_I17), /* At least one of these two will be right; maybe both? */ EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "iMac5,1", M_I20), diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 731fce64df9..b06647517c0 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -1362,6 +1362,7 @@ fb_mmap(struct file *file, struct vm_area_struct * vma) vma->vm_pgoff = off >> PAGE_SHIFT; /* This is an IO map - tell maydump to skip this VMA */ vma->vm_flags |= VM_IO | VM_RESERVED; + vma->vm_page_prot = vm_get_page_prot(vma->vm_flags); fb_pgprotect(file, vma, off); if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, vma->vm_end - vma->vm_start, vma->vm_page_prot)) @@ -1786,7 +1787,7 @@ static int ofonly __read_mostly; int fb_get_options(char *name, char **option) { char *opt, *options = NULL; - int opt_len, retval = 0; + int retval = 0; int name_len = strlen(name), i; if (name_len && ofonly && strncmp(name, "offb", 4)) @@ -1796,8 +1797,7 @@ int fb_get_options(char *name, char **option) for (i = 0; i < FB_MAX; i++) { if (video_options[i] == NULL) continue; - opt_len = strlen(video_options[i]); - if (!opt_len) + if (!video_options[i][0]) continue; opt = video_options[i]; if (!strncmp(name, opt, name_len) && diff --git a/drivers/video/ffb.c b/drivers/video/ffb.c index 95c0227f47f..6739b2af3bc 100644 --- a/drivers/video/ffb.c +++ b/drivers/video/ffb.c @@ -893,7 +893,7 @@ static void ffb_init_fix(struct fb_info *info) info->fix.accel = FB_ACCEL_SUN_CREATOR; } -static int __devinit ffb_probe(struct of_device *op, +static int __devinit ffb_probe(struct platform_device *op, const struct of_device_id *match) { struct device_node *dp = op->dev.of_node; @@ -1023,7 +1023,7 @@ out_err: return err; } -static int __devexit ffb_remove(struct of_device *op) +static int __devexit ffb_remove(struct platform_device *op) { struct fb_info *info = dev_get_drvdata(&op->dev); struct ffb_par *par = info->par; @@ -1067,12 +1067,12 @@ static int __init ffb_init(void) if (fb_get_options("ffb", NULL)) return -ENODEV; - return of_register_driver(&ffb_driver, &of_bus_type); + return of_register_platform_driver(&ffb_driver); } static void __exit ffb_exit(void) { - of_unregister_driver(&ffb_driver); + of_unregister_platform_driver(&ffb_driver); } module_init(ffb_init); diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c index 27455ce298b..8bbbf08fa3c 100644 --- a/drivers/video/fsl-diu-fb.c +++ b/drivers/video/fsl-diu-fb.c @@ -34,7 +34,8 @@ #include <linux/of_platform.h> #include <sysdev/fsl_soc.h> -#include "fsl-diu-fb.h" +#include <linux/fsl-diu-fb.h> +#include "edid.h" /* * These parameters give default parameters @@ -217,6 +218,7 @@ struct mfb_info { int x_aoi_d; /* aoi display x offset to physical screen */ int y_aoi_d; /* aoi display y offset to physical screen */ struct fsl_diu_data *parent; + u8 *edid_data; }; @@ -317,6 +319,17 @@ static void fsl_diu_free(void *virt, size_t size) free_pages_exact(virt, size); } +/* + * Workaround for failed writing desc register of planes. + * Needed with MPC5121 DIU rev 2.0 silicon. + */ +void wr_reg_wa(u32 *reg, u32 val) +{ + do { + out_be32(reg, val); + } while (in_be32(reg) != val); +} + static int fsl_diu_enable_panel(struct fb_info *info) { struct mfb_info *pmfbi, *cmfbi, *mfbi = info->par; @@ -330,7 +343,7 @@ static int fsl_diu_enable_panel(struct fb_info *info) switch (mfbi->index) { case 0: /* plane 0 */ if (hw->desc[0] != ad->paddr) - out_be32(&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; @@ -340,7 +353,7 @@ static int fsl_diu_enable_panel(struct fb_info *info) cpu_to_le32(cmfbi->ad->paddr); else ad->next_ad = 0; - out_be32(&hw->desc[1], ad->paddr); + wr_reg_wa(&hw->desc[1], ad->paddr); } break; case 3: /* plane 2 AOI 0 */ @@ -351,14 +364,14 @@ static int fsl_diu_enable_panel(struct fb_info *info) cpu_to_le32(cmfbi->ad->paddr); else ad->next_ad = 0; - out_be32(&hw->desc[2], ad->paddr); + 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) - out_be32(&hw->desc[1], ad->paddr); + wr_reg_wa(&hw->desc[1], ad->paddr); else /* AOI0 open */ pmfbi->ad->next_ad = cpu_to_le32(ad->paddr); break; @@ -366,7 +379,7 @@ static int fsl_diu_enable_panel(struct fb_info *info) pmfbi = machine_data->fsl_diu_info[3]->par; ad->next_ad = 0; if (hw->desc[2] == machine_data->dummy_ad->paddr) - out_be32(&hw->desc[2], ad->paddr); + wr_reg_wa(&hw->desc[2], ad->paddr); else /* AOI0 was open */ pmfbi->ad->next_ad = cpu_to_le32(ad->paddr); break; @@ -390,27 +403,24 @@ static int fsl_diu_disable_panel(struct fb_info *info) switch (mfbi->index) { case 0: /* plane 0 */ if (hw->desc[0] != machine_data->dummy_ad->paddr) - out_be32(&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 */ cmfbi = machine_data->fsl_diu_info[2]->par; if (cmfbi->count > 0) /* AOI1 is open */ - out_be32(&hw->desc[1], cmfbi->ad->paddr); + wr_reg_wa(&hw->desc[1], cmfbi->ad->paddr); /* move AOI1 to the first */ else /* AOI1 was closed */ - out_be32(&hw->desc[1], - machine_data->dummy_ad->paddr); + wr_reg_wa(&hw->desc[1], machine_data->dummy_ad->paddr); /* close AOI 0 */ break; case 3: /* plane 2 AOI 0 */ cmfbi = machine_data->fsl_diu_info[4]->par; if (cmfbi->count > 0) /* AOI1 is open */ - out_be32(&hw->desc[2], cmfbi->ad->paddr); + wr_reg_wa(&hw->desc[2], cmfbi->ad->paddr); /* move AOI1 to the first */ else /* AOI1 was closed */ - out_be32(&hw->desc[2], - machine_data->dummy_ad->paddr); + wr_reg_wa(&hw->desc[2], machine_data->dummy_ad->paddr); /* close AOI 0 */ break; case 2: /* plane 1 AOI 1 */ @@ -421,7 +431,7 @@ static int fsl_diu_disable_panel(struct fb_info *info) /* AOI0 is open, must be the first */ pmfbi->ad->next_ad = 0; } else /* AOI1 is the first in the chain */ - out_be32(&hw->desc[1], machine_data->dummy_ad->paddr); + wr_reg_wa(&hw->desc[1], machine_data->dummy_ad->paddr); /* close AOI 1 */ break; case 4: /* plane 2 AOI 1 */ @@ -432,7 +442,7 @@ static int fsl_diu_disable_panel(struct fb_info *info) /* AOI0 is open, must be the first */ pmfbi->ad->next_ad = 0; } else /* AOI1 is the first in the chain */ - out_be32(&hw->desc[2], machine_data->dummy_ad->paddr); + wr_reg_wa(&hw->desc[2], machine_data->dummy_ad->paddr); /* close AOI 1 */ break; default: @@ -1100,6 +1110,10 @@ static int fsl_diu_open(struct fb_info *info, int user) struct mfb_info *mfbi = info->par; int res = 0; + /* free boot splash memory on first /dev/fb0 open */ + if (!mfbi->index && diu_ops.release_bootmem) + diu_ops.release_bootmem(); + spin_lock(&diu_lock); mfbi->count++; if (mfbi->count == 1) { @@ -1173,18 +1187,30 @@ static int __devinit install_fb(struct fb_info *info) int rc; struct mfb_info *mfbi = info->par; const char *aoi_mode, *init_aoi_mode = "320x240"; + struct fb_videomode *db = fsl_diu_mode_db; + unsigned int dbsize = ARRAY_SIZE(fsl_diu_mode_db); + int has_default_mode = 1; if (init_fbinfo(info)) return -EINVAL; - if (mfbi->index == 0) /* plane 0 */ + if (mfbi->index == 0) { /* plane 0 */ + if (mfbi->edid_data) { + /* Now build modedb from EDID */ + fb_edid_to_monspecs(mfbi->edid_data, &info->monspecs); + fb_videomode_to_modelist(info->monspecs.modedb, + info->monspecs.modedb_len, + &info->modelist); + db = info->monspecs.modedb; + dbsize = info->monspecs.modedb_len; + } aoi_mode = fb_mode; - else + } else { aoi_mode = init_aoi_mode; + } pr_debug("mode used = %s\n", aoi_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); - + 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"); @@ -1202,10 +1228,50 @@ static int __devinit install_fb(struct fb_info *info) default: pr_debug("rc = %d\n", rc); pr_debug("failed to find mode\n"); - return -EINVAL; + /* + * For plane 0 we continue and look into + * driver's internal modedb. + */ + if (mfbi->index == 0 && 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) + has_default_mode = 1; + } + + /* Still not found, use preferred mode from database if any */ + if (!has_default_mode && info->monspecs.modedb) { + struct fb_monspecs *specs = &info->monspecs; + struct fb_videomode *modedb = &specs->modedb[0]; + + /* + * Get preferred timing. If not found, + * first mode in database will be used. + */ + if (specs->misc & FB_MISC_1ST_DETAIL) { + int i; + + for (i = 0; i < specs->modedb_len; i++) { + if (specs->modedb[i].flag & FB_MODE_IS_FIRST) { + modedb = &specs->modedb[i]; + break; + } + } + } + + info->var.bits_per_pixel = default_bpp; + 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); @@ -1244,6 +1310,9 @@ static void uninstall_fb(struct fb_info *info) if (!mfbi->registered) return; + if (mfbi->index == 0) + kfree(mfbi->edid_data); + unregister_framebuffer(info); unmap_video_memory(info); if (&info->cmap) @@ -1324,7 +1393,7 @@ static void free_irq_local(int irq) * Power management hooks. Note that we won't be called from IRQ context, * unlike the blank functions above, so we may sleep. */ -static int fsl_diu_suspend(struct of_device *ofdev, pm_message_t state) +static int fsl_diu_suspend(struct platform_device *ofdev, pm_message_t state) { struct fsl_diu_data *machine_data; @@ -1334,7 +1403,7 @@ static int fsl_diu_suspend(struct of_device *ofdev, pm_message_t state) return 0; } -static int fsl_diu_resume(struct of_device *ofdev) +static int fsl_diu_resume(struct platform_device *ofdev) { struct fsl_diu_data *machine_data; @@ -1418,7 +1487,7 @@ static ssize_t show_monitor(struct device *device, return diu_ops.show_monitor_port(machine_data->monitor_port, buf); } -static int __devinit fsl_diu_probe(struct of_device *ofdev, +static int __devinit fsl_diu_probe(struct platform_device *ofdev, const struct of_device_id *match) { struct device_node *np = ofdev->dev.of_node; @@ -1427,6 +1496,7 @@ static int __devinit fsl_diu_probe(struct of_device *ofdev, int ret, i, error = 0; struct resource res; struct fsl_diu_data *machine_data; + int diu_mode; machine_data = kzalloc(sizeof(struct fsl_diu_data), GFP_KERNEL); if (!machine_data) @@ -1443,6 +1513,17 @@ static int __devinit fsl_diu_probe(struct of_device *ofdev, mfbi = machine_data->fsl_diu_info[i]->par; memcpy(mfbi, &mfb_template[i], sizeof(struct mfb_info)); mfbi->parent = machine_data; + + if (mfbi->index == 0) { + const u8 *prop; + int len; + + /* Get EDID */ + prop = of_get_property(np, "edid", &len); + if (prop && len == EDID_LENGTH) + mfbi->edid_data = kmemdup(prop, EDID_LENGTH, + GFP_KERNEL); + } } ret = of_address_to_resource(np, 0, &res); @@ -1463,7 +1544,9 @@ static int __devinit fsl_diu_probe(struct of_device *ofdev, goto error2; } - out_be32(&dr.diu_reg->diu_mode, 0); /* disable DIU anyway*/ + diu_mode = in_be32(&dr.diu_reg->diu_mode); + if (diu_mode != MFB_MODE1) + out_be32(&dr.diu_reg->diu_mode, 0); /* disable DIU */ /* Get the IRQ of the DIU */ machine_data->irq = irq_of_parse_and_map(np, 0); @@ -1511,7 +1594,13 @@ static int __devinit fsl_diu_probe(struct of_device *ofdev, machine_data->dummy_ad->offset_xyd = 0; machine_data->dummy_ad->next_ad = 0; - out_be32(&dr.diu_reg->desc[0], machine_data->dummy_ad->paddr); + /* + * 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); + out_be32(&dr.diu_reg->desc[1], machine_data->dummy_ad->paddr); out_be32(&dr.diu_reg->desc[2], machine_data->dummy_ad->paddr); @@ -1578,7 +1667,7 @@ error2: } -static int fsl_diu_remove(struct of_device *ofdev) +static int fsl_diu_remove(struct platform_device *ofdev) { struct fsl_diu_data *machine_data; int i; diff --git a/drivers/video/fsl-diu-fb.h b/drivers/video/fsl-diu-fb.h deleted file mode 100644 index fc295d7ea46..00000000000 --- a/drivers/video/fsl-diu-fb.h +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved. - * - * Freescale DIU Frame Buffer device driver - * - * Authors: Hongjun Chen <hong-jun.chen@freescale.com> - * Paul Widmer <paul.widmer@freescale.com> - * Srikanth Srinivasan <srikanth.srinivasan@freescale.com> - * York Sun <yorksun@freescale.com> - * - * Based on imxfb.c Copyright (C) 2004 S.Hauer, Pengutronix - * - * 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. - * - */ - -#ifndef __FSL_DIU_FB_H__ -#define __FSL_DIU_FB_H__ - -/* Arbitrary threshold to determine the allocation method - * See mpc8610fb_set_par(), map_video_memory(), and unmap_video_memory() - */ -#define MEM_ALLOC_THRESHOLD (1024*768*4+32) -/* Minimum value that the pixel clock can be set to in pico seconds - * This is determined by platform clock/3 where the minimum platform - * clock is 533MHz. This gives 5629 pico seconds. - */ -#define MIN_PIX_CLK 5629 -#define MAX_PIX_CLK 96096 - -#include <linux/types.h> - -struct mfb_alpha { - int enable; - int alpha; -}; - -struct mfb_chroma_key { - int enable; - __u8 red_max; - __u8 green_max; - __u8 blue_max; - __u8 red_min; - __u8 green_min; - __u8 blue_min; -}; - -struct aoi_display_offset { - int x_aoi_d; - int y_aoi_d; -}; - -#define MFB_SET_CHROMA_KEY _IOW('M', 1, struct mfb_chroma_key) -#define MFB_WAIT_FOR_VSYNC _IOW('F', 0x20, u_int32_t) -#define MFB_SET_BRIGHTNESS _IOW('M', 3, __u8) - -#define MFB_SET_ALPHA 0x80014d00 -#define MFB_GET_ALPHA 0x40014d00 -#define MFB_SET_AOID 0x80084d04 -#define MFB_GET_AOID 0x40084d04 -#define MFB_SET_PIXFMT 0x80014d08 -#define MFB_GET_PIXFMT 0x40014d08 - -#define FBIOGET_GWINFO 0x46E0 -#define FBIOPUT_GWINFO 0x46E1 - -#ifdef __KERNEL__ -#include <linux/spinlock.h> - -/* - * These are the fields of area descriptor(in DDR memory) for every plane - */ -struct diu_ad { - /* Word 0(32-bit) in DDR memory */ -/* __u16 comp; */ -/* __u16 pixel_s:2; */ -/* __u16 pallete:1; */ -/* __u16 red_c:2; */ -/* __u16 green_c:2; */ -/* __u16 blue_c:2; */ -/* __u16 alpha_c:3; */ -/* __u16 byte_f:1; */ -/* __u16 res0:3; */ - - __be32 pix_fmt; /* hard coding pixel format */ - - /* Word 1(32-bit) in DDR memory */ - __le32 addr; - - /* Word 2(32-bit) in DDR memory */ -/* __u32 delta_xs:11; */ -/* __u32 res1:1; */ -/* __u32 delta_ys:11; */ -/* __u32 res2:1; */ -/* __u32 g_alpha:8; */ - __le32 src_size_g_alpha; - - /* Word 3(32-bit) in DDR memory */ -/* __u32 delta_xi:11; */ -/* __u32 res3:5; */ -/* __u32 delta_yi:11; */ -/* __u32 res4:3; */ -/* __u32 flip:2; */ - __le32 aoi_size; - - /* Word 4(32-bit) in DDR memory */ - /*__u32 offset_xi:11; - __u32 res5:5; - __u32 offset_yi:11; - __u32 res6:5; - */ - __le32 offset_xyi; - - /* Word 5(32-bit) in DDR memory */ - /*__u32 offset_xd:11; - __u32 res7:5; - __u32 offset_yd:11; - __u32 res8:5; */ - __le32 offset_xyd; - - - /* Word 6(32-bit) in DDR memory */ - __u8 ckmax_r; - __u8 ckmax_g; - __u8 ckmax_b; - __u8 res9; - - /* Word 7(32-bit) in DDR memory */ - __u8 ckmin_r; - __u8 ckmin_g; - __u8 ckmin_b; - __u8 res10; -/* __u32 res10:8; */ - - /* Word 8(32-bit) in DDR memory */ - __le32 next_ad; - - /* Word 9(32-bit) in DDR memory, just for 64-bit aligned */ - __u32 paddr; -} __attribute__ ((packed)); - -/* DIU register map */ -struct diu { - __be32 desc[3]; - __be32 gamma; - __be32 pallete; - __be32 cursor; - __be32 curs_pos; - __be32 diu_mode; - __be32 bgnd; - __be32 bgnd_wb; - __be32 disp_size; - __be32 wb_size; - __be32 wb_mem_addr; - __be32 hsyn_para; - __be32 vsyn_para; - __be32 syn_pol; - __be32 thresholds; - __be32 int_status; - __be32 int_mask; - __be32 colorbar[8]; - __be32 filling; - __be32 plut; -} __attribute__ ((packed)); - -struct diu_hw { - struct diu *diu_reg; - spinlock_t reg_lock; - - __u32 mode; /* DIU operation mode */ -}; - -struct diu_addr { - __u8 __iomem *vaddr; /* Virtual address */ - dma_addr_t paddr; /* Physical address */ - __u32 offset; -}; - -struct diu_pool { - struct diu_addr ad; - struct diu_addr gamma; - struct diu_addr pallete; - struct diu_addr cursor; -}; - -#define FSL_DIU_BASE_OFFSET 0x2C000 /* Offset of DIU */ -#define INT_LCDC 64 /* DIU interrupt number */ - -#define FSL_AOI_NUM 6 /* 5 AOIs and one dummy AOI */ - /* 1 for plane 0, 2 for plane 1&2 each */ - -/* Minimum X and Y resolutions */ -#define MIN_XRES 64 -#define MIN_YRES 64 - -/* HW cursor parameters */ -#define MAX_CURS 32 - -/* Modes of operation of DIU */ -#define MFB_MODE0 0 /* DIU off */ -#define MFB_MODE1 1 /* All three planes output to display */ -#define MFB_MODE2 2 /* Plane 1 to display, planes 2+3 written back*/ -#define MFB_MODE3 3 /* All three planes written back to memory */ -#define MFB_MODE4 4 /* Color bar generation */ - -/* 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 */ - -/* Panels'operation modes */ -#define MFB_TYPE_OUTPUT 0 /* Panel output to display */ -#define MFB_TYPE_OFF 1 /* Panel off */ -#define MFB_TYPE_WB 2 /* Panel written back to memory */ -#define MFB_TYPE_TEST 3 /* Panel generate color bar */ - -#endif /* __KERNEL__ */ -#endif /* __FSL_DIU_FB_H__ */ diff --git a/drivers/video/igafb.c b/drivers/video/igafb.c index 15d20010944..d885c770eb8 100644 --- a/drivers/video/igafb.c +++ b/drivers/video/igafb.c @@ -368,7 +368,7 @@ static int __init iga_init(struct fb_info *info, struct iga_par *par) return 1; } -int __init igafb_init(void) +static int __init igafb_init(void) { struct fb_info *info; struct pci_dev *pdev; @@ -531,6 +531,7 @@ int __init igafb_init(void) iounmap(info->screen_base); kfree(par->mmap_map); kfree(info); + return -ENODEV; } #ifdef CONFIG_SPARC @@ -556,7 +557,7 @@ int __init igafb_init(void) return 0; } -int __init igafb_setup(char *options) +static int __init igafb_setup(char *options) { char *this_opt; diff --git a/drivers/video/imxfb.c b/drivers/video/imxfb.c index 43f0639b1c1..5c363d026f6 100644 --- a/drivers/video/imxfb.c +++ b/drivers/video/imxfb.c @@ -40,6 +40,12 @@ */ #define DEBUG_VAR 1 +#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) || \ + (defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE) && \ + defined(CONFIG_FB_IMX_MODULE)) +#define PWMR_BACKLIGHT_AVAILABLE +#endif + #define DRIVER_NAME "imx-fb" #define LCDC_SSA 0x00 @@ -175,7 +181,9 @@ struct imxfb_info { struct imx_fb_videomode *mode; int num_modes; +#ifdef PWMR_BACKLIGHT_AVAILABLE struct backlight_device *bl; +#endif void (*lcd_power)(int); void (*backlight_power)(int); @@ -450,8 +458,7 @@ static int imxfb_set_par(struct fb_info *info) return 0; } - - +#ifdef PWMR_BACKLIGHT_AVAILABLE static int imxfb_bl_get_brightness(struct backlight_device *bl) { struct imxfb_info *fbi = bl_get_data(bl); @@ -516,6 +523,7 @@ static void imxfb_exit_backlight(struct imxfb_info *fbi) if (fbi->bl) backlight_device_unregister(fbi->bl); } +#endif static void imxfb_enable_controller(struct imxfb_info *fbi) { @@ -647,6 +655,9 @@ static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *inf fbi->regs + LCDC_SIZE); writel(fbi->pcr, fbi->regs + LCDC_PCR); +#ifndef PWMR_BACKLIGHT_AVAILABLE + writel(fbi->pwmr, fbi->regs + LCDC_PWMR); +#endif writel(fbi->lscr1, fbi->regs + LCDC_LSCR1); writel(fbi->dmacr, fbi->regs + LCDC_DMACR); @@ -847,7 +858,9 @@ static int __init imxfb_probe(struct platform_device *pdev) imxfb_enable_controller(fbi); fbi->pdev = pdev; +#ifdef PWMR_BACKLIGHT_AVAILABLE imxfb_init_backlight(fbi); +#endif return 0; @@ -885,7 +898,9 @@ static int __devexit imxfb_remove(struct platform_device *pdev) imxfb_disable_controller(fbi); +#ifdef PWMR_BACKLIGHT_AVAILABLE imxfb_exit_backlight(fbi); +#endif unregister_framebuffer(info); pdata = pdev->dev.platform_data; diff --git a/drivers/video/jz4740_fb.c b/drivers/video/jz4740_fb.c new file mode 100644 index 00000000000..670ecaa0385 --- /dev/null +++ b/drivers/video/jz4740_fb.c @@ -0,0 +1,847 @@ +/* + * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> + * JZ4740 SoC LCD framebuffer driver + * + * 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. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> + +#include <linux/clk.h> +#include <linux/delay.h> + +#include <linux/console.h> +#include <linux/fb.h> + +#include <linux/dma-mapping.h> + +#include <asm/mach-jz4740/jz4740_fb.h> +#include <asm/mach-jz4740/gpio.h> + +#define JZ_REG_LCD_CFG 0x00 +#define JZ_REG_LCD_VSYNC 0x04 +#define JZ_REG_LCD_HSYNC 0x08 +#define JZ_REG_LCD_VAT 0x0C +#define JZ_REG_LCD_DAH 0x10 +#define JZ_REG_LCD_DAV 0x14 +#define JZ_REG_LCD_PS 0x18 +#define JZ_REG_LCD_CLS 0x1C +#define JZ_REG_LCD_SPL 0x20 +#define JZ_REG_LCD_REV 0x24 +#define JZ_REG_LCD_CTRL 0x30 +#define JZ_REG_LCD_STATE 0x34 +#define JZ_REG_LCD_IID 0x38 +#define JZ_REG_LCD_DA0 0x40 +#define JZ_REG_LCD_SA0 0x44 +#define JZ_REG_LCD_FID0 0x48 +#define JZ_REG_LCD_CMD0 0x4C +#define JZ_REG_LCD_DA1 0x50 +#define JZ_REG_LCD_SA1 0x54 +#define JZ_REG_LCD_FID1 0x58 +#define JZ_REG_LCD_CMD1 0x5C + +#define JZ_LCD_CFG_SLCD BIT(31) +#define JZ_LCD_CFG_PS_DISABLE BIT(23) +#define JZ_LCD_CFG_CLS_DISABLE BIT(22) +#define JZ_LCD_CFG_SPL_DISABLE BIT(21) +#define JZ_LCD_CFG_REV_DISABLE BIT(20) +#define JZ_LCD_CFG_HSYNCM BIT(19) +#define JZ_LCD_CFG_PCLKM BIT(18) +#define JZ_LCD_CFG_INV BIT(17) +#define JZ_LCD_CFG_SYNC_DIR BIT(16) +#define JZ_LCD_CFG_PS_POLARITY BIT(15) +#define JZ_LCD_CFG_CLS_POLARITY BIT(14) +#define JZ_LCD_CFG_SPL_POLARITY BIT(13) +#define JZ_LCD_CFG_REV_POLARITY BIT(12) +#define JZ_LCD_CFG_HSYNC_ACTIVE_LOW BIT(11) +#define JZ_LCD_CFG_PCLK_FALLING_EDGE BIT(10) +#define JZ_LCD_CFG_DE_ACTIVE_LOW BIT(9) +#define JZ_LCD_CFG_VSYNC_ACTIVE_LOW BIT(8) +#define JZ_LCD_CFG_18_BIT BIT(7) +#define JZ_LCD_CFG_PDW (BIT(5) | BIT(4)) +#define JZ_LCD_CFG_MODE_MASK 0xf + +#define JZ_LCD_CTRL_BURST_4 (0x0 << 28) +#define JZ_LCD_CTRL_BURST_8 (0x1 << 28) +#define JZ_LCD_CTRL_BURST_16 (0x2 << 28) +#define JZ_LCD_CTRL_RGB555 BIT(27) +#define JZ_LCD_CTRL_OFUP BIT(26) +#define JZ_LCD_CTRL_FRC_GRAYSCALE_16 (0x0 << 24) +#define JZ_LCD_CTRL_FRC_GRAYSCALE_4 (0x1 << 24) +#define JZ_LCD_CTRL_FRC_GRAYSCALE_2 (0x2 << 24) +#define JZ_LCD_CTRL_PDD_MASK (0xff << 16) +#define JZ_LCD_CTRL_EOF_IRQ BIT(13) +#define JZ_LCD_CTRL_SOF_IRQ BIT(12) +#define JZ_LCD_CTRL_OFU_IRQ BIT(11) +#define JZ_LCD_CTRL_IFU0_IRQ BIT(10) +#define JZ_LCD_CTRL_IFU1_IRQ BIT(9) +#define JZ_LCD_CTRL_DD_IRQ BIT(8) +#define JZ_LCD_CTRL_QDD_IRQ BIT(7) +#define JZ_LCD_CTRL_REVERSE_ENDIAN BIT(6) +#define JZ_LCD_CTRL_LSB_FISRT BIT(5) +#define JZ_LCD_CTRL_DISABLE BIT(4) +#define JZ_LCD_CTRL_ENABLE BIT(3) +#define JZ_LCD_CTRL_BPP_1 0x0 +#define JZ_LCD_CTRL_BPP_2 0x1 +#define JZ_LCD_CTRL_BPP_4 0x2 +#define JZ_LCD_CTRL_BPP_8 0x3 +#define JZ_LCD_CTRL_BPP_15_16 0x4 +#define JZ_LCD_CTRL_BPP_18_24 0x5 + +#define JZ_LCD_CMD_SOF_IRQ BIT(15) +#define JZ_LCD_CMD_EOF_IRQ BIT(16) +#define JZ_LCD_CMD_ENABLE_PAL BIT(12) + +#define JZ_LCD_SYNC_MASK 0x3ff + +#define JZ_LCD_STATE_DISABLED BIT(0) + +struct jzfb_framedesc { + uint32_t next; + uint32_t addr; + uint32_t id; + uint32_t cmd; +} __packed; + +struct jzfb { + struct fb_info *fb; + struct platform_device *pdev; + void __iomem *base; + struct resource *mem; + struct jz4740_fb_platform_data *pdata; + + size_t vidmem_size; + void *vidmem; + dma_addr_t vidmem_phys; + struct jzfb_framedesc *framedesc; + dma_addr_t framedesc_phys; + + struct clk *ldclk; + struct clk *lpclk; + + unsigned is_enabled:1; + struct mutex lock; + + uint32_t pseudo_palette[16]; +}; + +static const struct fb_fix_screeninfo jzfb_fix __devinitdata = { + .id = "JZ4740 FB", + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_TRUECOLOR, + .xpanstep = 0, + .ypanstep = 0, + .ywrapstep = 0, + .accel = FB_ACCEL_NONE, +}; + +static const struct jz_gpio_bulk_request jz_lcd_ctrl_pins[] = { + JZ_GPIO_BULK_PIN(LCD_PCLK), + JZ_GPIO_BULK_PIN(LCD_HSYNC), + JZ_GPIO_BULK_PIN(LCD_VSYNC), + JZ_GPIO_BULK_PIN(LCD_DE), + JZ_GPIO_BULK_PIN(LCD_PS), + JZ_GPIO_BULK_PIN(LCD_REV), + JZ_GPIO_BULK_PIN(LCD_CLS), + JZ_GPIO_BULK_PIN(LCD_SPL), +}; + +static const struct jz_gpio_bulk_request jz_lcd_data_pins[] = { + JZ_GPIO_BULK_PIN(LCD_DATA0), + JZ_GPIO_BULK_PIN(LCD_DATA1), + JZ_GPIO_BULK_PIN(LCD_DATA2), + JZ_GPIO_BULK_PIN(LCD_DATA3), + JZ_GPIO_BULK_PIN(LCD_DATA4), + JZ_GPIO_BULK_PIN(LCD_DATA5), + JZ_GPIO_BULK_PIN(LCD_DATA6), + JZ_GPIO_BULK_PIN(LCD_DATA7), + JZ_GPIO_BULK_PIN(LCD_DATA8), + JZ_GPIO_BULK_PIN(LCD_DATA9), + JZ_GPIO_BULK_PIN(LCD_DATA10), + JZ_GPIO_BULK_PIN(LCD_DATA11), + JZ_GPIO_BULK_PIN(LCD_DATA12), + JZ_GPIO_BULK_PIN(LCD_DATA13), + JZ_GPIO_BULK_PIN(LCD_DATA14), + JZ_GPIO_BULK_PIN(LCD_DATA15), + JZ_GPIO_BULK_PIN(LCD_DATA16), + JZ_GPIO_BULK_PIN(LCD_DATA17), +}; + +static unsigned int jzfb_num_ctrl_pins(struct jzfb *jzfb) +{ + unsigned int num; + + switch (jzfb->pdata->lcd_type) { + case JZ_LCD_TYPE_GENERIC_16_BIT: + num = 4; + break; + case JZ_LCD_TYPE_GENERIC_18_BIT: + num = 4; + break; + case JZ_LCD_TYPE_8BIT_SERIAL: + num = 3; + break; + case JZ_LCD_TYPE_SPECIAL_TFT_1: + case JZ_LCD_TYPE_SPECIAL_TFT_2: + case JZ_LCD_TYPE_SPECIAL_TFT_3: + num = 8; + break; + default: + num = 0; + break; + } + return num; +} + +static unsigned int jzfb_num_data_pins(struct jzfb *jzfb) +{ + unsigned int num; + + switch (jzfb->pdata->lcd_type) { + case JZ_LCD_TYPE_GENERIC_16_BIT: + num = 16; + break; + case JZ_LCD_TYPE_GENERIC_18_BIT: + num = 18; + break; + case JZ_LCD_TYPE_8BIT_SERIAL: + num = 8; + break; + case JZ_LCD_TYPE_SPECIAL_TFT_1: + case JZ_LCD_TYPE_SPECIAL_TFT_2: + case JZ_LCD_TYPE_SPECIAL_TFT_3: + if (jzfb->pdata->bpp == 18) + num = 18; + else + num = 16; + break; + default: + num = 0; + break; + } + return num; +} + +/* Based on CNVT_TOHW macro from skeletonfb.c */ +static inline uint32_t jzfb_convert_color_to_hw(unsigned val, + struct fb_bitfield *bf) +{ + return (((val << bf->length) + 0x7FFF - val) >> 16) << bf->offset; +} + +static int jzfb_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, struct fb_info *fb) +{ + uint32_t color; + + if (regno >= 16) + return -EINVAL; + + color = jzfb_convert_color_to_hw(red, &fb->var.red); + color |= jzfb_convert_color_to_hw(green, &fb->var.green); + color |= jzfb_convert_color_to_hw(blue, &fb->var.blue); + color |= jzfb_convert_color_to_hw(transp, &fb->var.transp); + + ((uint32_t *)(fb->pseudo_palette))[regno] = color; + + return 0; +} + +static int jzfb_get_controller_bpp(struct jzfb *jzfb) +{ + switch (jzfb->pdata->bpp) { + case 18: + case 24: + return 32; + case 15: + return 16; + default: + return jzfb->pdata->bpp; + } +} + +static struct fb_videomode *jzfb_get_mode(struct jzfb *jzfb, + struct fb_var_screeninfo *var) +{ + size_t i; + struct fb_videomode *mode = jzfb->pdata->modes; + + for (i = 0; i < jzfb->pdata->num_modes; ++i, ++mode) { + if (mode->xres == var->xres && mode->yres == var->yres) + return mode; + } + + return NULL; +} + +static int jzfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fb) +{ + struct jzfb *jzfb = fb->par; + struct fb_videomode *mode; + + if (var->bits_per_pixel != jzfb_get_controller_bpp(jzfb) && + var->bits_per_pixel != jzfb->pdata->bpp) + return -EINVAL; + + mode = jzfb_get_mode(jzfb, var); + if (mode == NULL) + return -EINVAL; + + fb_videomode_to_var(var, mode); + + switch (jzfb->pdata->bpp) { + case 8: + break; + case 15: + var->red.offset = 10; + var->red.length = 5; + var->green.offset = 6; + var->green.length = 5; + var->blue.offset = 0; + var->blue.length = 5; + break; + case 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; + break; + case 18: + var->red.offset = 16; + var->red.length = 6; + var->green.offset = 8; + var->green.length = 6; + var->blue.offset = 0; + var->blue.length = 6; + var->bits_per_pixel = 32; + break; + case 32: + case 24: + var->transp.offset = 24; + var->transp.length = 8; + 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->bits_per_pixel = 32; + break; + default: + break; + } + + return 0; +} + +static int jzfb_set_par(struct fb_info *info) +{ + struct jzfb *jzfb = info->par; + struct jz4740_fb_platform_data *pdata = jzfb->pdata; + struct fb_var_screeninfo *var = &info->var; + struct fb_videomode *mode; + uint16_t hds, vds; + uint16_t hde, vde; + uint16_t ht, vt; + uint32_t ctrl; + uint32_t cfg; + unsigned long rate; + + mode = jzfb_get_mode(jzfb, var); + if (mode == NULL) + return -EINVAL; + + if (mode == info->mode) + return 0; + + info->mode = mode; + + hds = mode->hsync_len + mode->left_margin; + hde = hds + mode->xres; + ht = hde + mode->right_margin; + + vds = mode->vsync_len + mode->upper_margin; + vde = vds + mode->yres; + vt = vde + mode->lower_margin; + + ctrl = JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_16; + + switch (pdata->bpp) { + case 1: + ctrl |= JZ_LCD_CTRL_BPP_1; + break; + case 2: + ctrl |= JZ_LCD_CTRL_BPP_2; + break; + case 4: + ctrl |= JZ_LCD_CTRL_BPP_4; + break; + case 8: + ctrl |= JZ_LCD_CTRL_BPP_8; + break; + case 15: + ctrl |= JZ_LCD_CTRL_RGB555; /* Falltrough */ + case 16: + ctrl |= JZ_LCD_CTRL_BPP_15_16; + break; + case 18: + case 24: + case 32: + ctrl |= JZ_LCD_CTRL_BPP_18_24; + break; + default: + break; + } + + cfg = pdata->lcd_type & 0xf; + + if (!(mode->sync & FB_SYNC_HOR_HIGH_ACT)) + cfg |= JZ_LCD_CFG_HSYNC_ACTIVE_LOW; + + if (!(mode->sync & FB_SYNC_VERT_HIGH_ACT)) + cfg |= JZ_LCD_CFG_VSYNC_ACTIVE_LOW; + + if (pdata->pixclk_falling_edge) + cfg |= JZ_LCD_CFG_PCLK_FALLING_EDGE; + + if (pdata->date_enable_active_low) + cfg |= JZ_LCD_CFG_DE_ACTIVE_LOW; + + if (pdata->lcd_type == JZ_LCD_TYPE_GENERIC_18_BIT) + cfg |= JZ_LCD_CFG_18_BIT; + + if (mode->pixclock) { + rate = PICOS2KHZ(mode->pixclock) * 1000; + mode->refresh = rate / vt / ht; + } else { + if (pdata->lcd_type == JZ_LCD_TYPE_8BIT_SERIAL) + rate = mode->refresh * (vt + 2 * mode->xres) * ht; + else + rate = mode->refresh * vt * ht; + + mode->pixclock = KHZ2PICOS(rate / 1000); + } + + mutex_lock(&jzfb->lock); + if (!jzfb->is_enabled) + clk_enable(jzfb->ldclk); + else + ctrl |= JZ_LCD_CTRL_ENABLE; + + switch (pdata->lcd_type) { + case JZ_LCD_TYPE_SPECIAL_TFT_1: + case JZ_LCD_TYPE_SPECIAL_TFT_2: + case JZ_LCD_TYPE_SPECIAL_TFT_3: + writel(pdata->special_tft_config.spl, jzfb->base + JZ_REG_LCD_SPL); + writel(pdata->special_tft_config.cls, jzfb->base + JZ_REG_LCD_CLS); + writel(pdata->special_tft_config.ps, jzfb->base + JZ_REG_LCD_PS); + writel(pdata->special_tft_config.ps, jzfb->base + JZ_REG_LCD_REV); + break; + default: + cfg |= JZ_LCD_CFG_PS_DISABLE; + cfg |= JZ_LCD_CFG_CLS_DISABLE; + cfg |= JZ_LCD_CFG_SPL_DISABLE; + cfg |= JZ_LCD_CFG_REV_DISABLE; + break; + } + + writel(mode->hsync_len, jzfb->base + JZ_REG_LCD_HSYNC); + writel(mode->vsync_len, jzfb->base + JZ_REG_LCD_VSYNC); + + writel((ht << 16) | vt, jzfb->base + JZ_REG_LCD_VAT); + + writel((hds << 16) | hde, jzfb->base + JZ_REG_LCD_DAH); + writel((vds << 16) | vde, jzfb->base + JZ_REG_LCD_DAV); + + writel(cfg, jzfb->base + JZ_REG_LCD_CFG); + + writel(ctrl, jzfb->base + JZ_REG_LCD_CTRL); + + if (!jzfb->is_enabled) + clk_disable(jzfb->ldclk); + + mutex_unlock(&jzfb->lock); + + clk_set_rate(jzfb->lpclk, rate); + clk_set_rate(jzfb->ldclk, rate * 3); + + return 0; +} + +static void jzfb_enable(struct jzfb *jzfb) +{ + uint32_t ctrl; + + clk_enable(jzfb->ldclk); + + jz_gpio_bulk_resume(jz_lcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb)); + jz_gpio_bulk_resume(jz_lcd_data_pins, jzfb_num_data_pins(jzfb)); + + writel(0, jzfb->base + JZ_REG_LCD_STATE); + + writel(jzfb->framedesc->next, jzfb->base + JZ_REG_LCD_DA0); + + ctrl = readl(jzfb->base + JZ_REG_LCD_CTRL); + ctrl |= JZ_LCD_CTRL_ENABLE; + ctrl &= ~JZ_LCD_CTRL_DISABLE; + writel(ctrl, jzfb->base + JZ_REG_LCD_CTRL); +} + +static void jzfb_disable(struct jzfb *jzfb) +{ + uint32_t ctrl; + + ctrl = readl(jzfb->base + JZ_REG_LCD_CTRL); + ctrl |= JZ_LCD_CTRL_DISABLE; + writel(ctrl, jzfb->base + JZ_REG_LCD_CTRL); + do { + ctrl = readl(jzfb->base + JZ_REG_LCD_STATE); + } while (!(ctrl & JZ_LCD_STATE_DISABLED)); + + jz_gpio_bulk_suspend(jz_lcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb)); + jz_gpio_bulk_suspend(jz_lcd_data_pins, jzfb_num_data_pins(jzfb)); + + clk_disable(jzfb->ldclk); +} + +static int jzfb_blank(int blank_mode, struct fb_info *info) +{ + struct jzfb *jzfb = info->par; + + switch (blank_mode) { + case FB_BLANK_UNBLANK: + mutex_lock(&jzfb->lock); + if (jzfb->is_enabled) { + mutex_unlock(&jzfb->lock); + return 0; + } + + jzfb_enable(jzfb); + jzfb->is_enabled = 1; + + mutex_unlock(&jzfb->lock); + break; + default: + mutex_lock(&jzfb->lock); + if (!jzfb->is_enabled) { + mutex_unlock(&jzfb->lock); + return 0; + } + + jzfb_disable(jzfb); + jzfb->is_enabled = 0; + + mutex_unlock(&jzfb->lock); + break; + } + + return 0; +} + +static int jzfb_alloc_devmem(struct jzfb *jzfb) +{ + int max_videosize = 0; + struct fb_videomode *mode = jzfb->pdata->modes; + void *page; + int i; + + for (i = 0; i < jzfb->pdata->num_modes; ++mode, ++i) { + if (max_videosize < mode->xres * mode->yres) + max_videosize = mode->xres * mode->yres; + } + + max_videosize *= jzfb_get_controller_bpp(jzfb) >> 3; + + jzfb->framedesc = dma_alloc_coherent(&jzfb->pdev->dev, + sizeof(*jzfb->framedesc), + &jzfb->framedesc_phys, GFP_KERNEL); + + if (!jzfb->framedesc) + return -ENOMEM; + + jzfb->vidmem_size = PAGE_ALIGN(max_videosize); + jzfb->vidmem = dma_alloc_coherent(&jzfb->pdev->dev, + jzfb->vidmem_size, + &jzfb->vidmem_phys, GFP_KERNEL); + + if (!jzfb->vidmem) + goto err_free_framedesc; + + for (page = jzfb->vidmem; + page < jzfb->vidmem + PAGE_ALIGN(jzfb->vidmem_size); + page += PAGE_SIZE) { + SetPageReserved(virt_to_page(page)); + } + + jzfb->framedesc->next = jzfb->framedesc_phys; + jzfb->framedesc->addr = jzfb->vidmem_phys; + jzfb->framedesc->id = 0xdeafbead; + jzfb->framedesc->cmd = 0; + jzfb->framedesc->cmd |= max_videosize / 4; + + return 0; + +err_free_framedesc: + dma_free_coherent(&jzfb->pdev->dev, sizeof(*jzfb->framedesc), + jzfb->framedesc, jzfb->framedesc_phys); + return -ENOMEM; +} + +static void jzfb_free_devmem(struct jzfb *jzfb) +{ + dma_free_coherent(&jzfb->pdev->dev, jzfb->vidmem_size, + jzfb->vidmem, jzfb->vidmem_phys); + dma_free_coherent(&jzfb->pdev->dev, sizeof(*jzfb->framedesc), + jzfb->framedesc, jzfb->framedesc_phys); +} + +static struct fb_ops jzfb_ops = { + .owner = THIS_MODULE, + .fb_check_var = jzfb_check_var, + .fb_set_par = jzfb_set_par, + .fb_blank = jzfb_blank, + .fb_fillrect = sys_fillrect, + .fb_copyarea = sys_copyarea, + .fb_imageblit = sys_imageblit, + .fb_setcolreg = jzfb_setcolreg, +}; + +static int __devinit jzfb_probe(struct platform_device *pdev) +{ + int ret; + struct jzfb *jzfb; + struct fb_info *fb; + struct jz4740_fb_platform_data *pdata = pdev->dev.platform_data; + struct resource *mem; + + if (!pdata) { + dev_err(&pdev->dev, "Missing platform data\n"); + return -ENXIO; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "Failed to get register memory resource\n"); + return -ENXIO; + } + + mem = request_mem_region(mem->start, resource_size(mem), pdev->name); + if (!mem) { + dev_err(&pdev->dev, "Failed to request register memory region\n"); + return -EBUSY; + } + + fb = framebuffer_alloc(sizeof(struct jzfb), &pdev->dev); + if (!fb) { + dev_err(&pdev->dev, "Failed to allocate framebuffer device\n"); + ret = -ENOMEM; + goto err_release_mem_region; + } + + fb->fbops = &jzfb_ops; + fb->flags = FBINFO_DEFAULT; + + jzfb = fb->par; + jzfb->pdev = pdev; + jzfb->pdata = pdata; + jzfb->mem = mem; + + jzfb->ldclk = clk_get(&pdev->dev, "lcd"); + if (IS_ERR(jzfb->ldclk)) { + ret = PTR_ERR(jzfb->ldclk); + dev_err(&pdev->dev, "Failed to get lcd clock: %d\n", ret); + goto err_framebuffer_release; + } + + jzfb->lpclk = clk_get(&pdev->dev, "lcd_pclk"); + if (IS_ERR(jzfb->lpclk)) { + ret = PTR_ERR(jzfb->lpclk); + dev_err(&pdev->dev, "Failed to get lcd pixel clock: %d\n", ret); + goto err_put_ldclk; + } + + jzfb->base = ioremap(mem->start, resource_size(mem)); + if (!jzfb->base) { + dev_err(&pdev->dev, "Failed to ioremap register memory region\n"); + ret = -EBUSY; + goto err_put_lpclk; + } + + platform_set_drvdata(pdev, jzfb); + + mutex_init(&jzfb->lock); + + fb_videomode_to_modelist(pdata->modes, pdata->num_modes, + &fb->modelist); + fb_videomode_to_var(&fb->var, pdata->modes); + fb->var.bits_per_pixel = pdata->bpp; + jzfb_check_var(&fb->var, fb); + + ret = jzfb_alloc_devmem(jzfb); + if (ret) { + dev_err(&pdev->dev, "Failed to allocate video memory\n"); + goto err_iounmap; + } + + fb->fix = jzfb_fix; + fb->fix.line_length = fb->var.bits_per_pixel * fb->var.xres / 8; + fb->fix.mmio_start = mem->start; + fb->fix.mmio_len = resource_size(mem); + fb->fix.smem_start = jzfb->vidmem_phys; + fb->fix.smem_len = fb->fix.line_length * fb->var.yres; + fb->screen_base = jzfb->vidmem; + fb->pseudo_palette = jzfb->pseudo_palette; + + fb_alloc_cmap(&fb->cmap, 256, 0); + + clk_enable(jzfb->ldclk); + jzfb->is_enabled = 1; + + writel(jzfb->framedesc->next, jzfb->base + JZ_REG_LCD_DA0); + + fb->mode = NULL; + jzfb_set_par(fb); + + jz_gpio_bulk_request(jz_lcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb)); + jz_gpio_bulk_request(jz_lcd_data_pins, jzfb_num_data_pins(jzfb)); + + ret = register_framebuffer(fb); + if (ret) { + dev_err(&pdev->dev, "Failed to register framebuffer: %d\n", ret); + goto err_free_devmem; + } + + jzfb->fb = fb; + + return 0; + +err_free_devmem: + jz_gpio_bulk_free(jz_lcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb)); + jz_gpio_bulk_free(jz_lcd_data_pins, jzfb_num_data_pins(jzfb)); + + fb_dealloc_cmap(&fb->cmap); + jzfb_free_devmem(jzfb); +err_iounmap: + iounmap(jzfb->base); +err_put_lpclk: + clk_put(jzfb->lpclk); +err_put_ldclk: + clk_put(jzfb->ldclk); +err_framebuffer_release: + framebuffer_release(fb); +err_release_mem_region: + release_mem_region(mem->start, resource_size(mem)); + return ret; +} + +static int __devexit jzfb_remove(struct platform_device *pdev) +{ + struct jzfb *jzfb = platform_get_drvdata(pdev); + + jzfb_blank(FB_BLANK_POWERDOWN, jzfb->fb); + + jz_gpio_bulk_free(jz_lcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb)); + jz_gpio_bulk_free(jz_lcd_data_pins, jzfb_num_data_pins(jzfb)); + + iounmap(jzfb->base); + release_mem_region(jzfb->mem->start, resource_size(jzfb->mem)); + + fb_dealloc_cmap(&jzfb->fb->cmap); + jzfb_free_devmem(jzfb); + + platform_set_drvdata(pdev, NULL); + + clk_put(jzfb->lpclk); + clk_put(jzfb->ldclk); + + framebuffer_release(jzfb->fb); + + return 0; +} + +#ifdef CONFIG_PM + +static int jzfb_suspend(struct device *dev) +{ + struct jzfb *jzfb = dev_get_drvdata(dev); + + acquire_console_sem(); + fb_set_suspend(jzfb->fb, 1); + release_console_sem(); + + mutex_lock(&jzfb->lock); + if (jzfb->is_enabled) + jzfb_disable(jzfb); + mutex_unlock(&jzfb->lock); + + return 0; +} + +static int jzfb_resume(struct device *dev) +{ + struct jzfb *jzfb = dev_get_drvdata(dev); + clk_enable(jzfb->ldclk); + + mutex_lock(&jzfb->lock); + if (jzfb->is_enabled) + jzfb_enable(jzfb); + mutex_unlock(&jzfb->lock); + + acquire_console_sem(); + fb_set_suspend(jzfb->fb, 0); + release_console_sem(); + + return 0; +} + +static const struct dev_pm_ops jzfb_pm_ops = { + .suspend = jzfb_suspend, + .resume = jzfb_resume, + .poweroff = jzfb_suspend, + .restore = jzfb_resume, +}; + +#define JZFB_PM_OPS (&jzfb_pm_ops) + +#else +#define JZFB_PM_OPS NULL +#endif + +static struct platform_driver jzfb_driver = { + .probe = jzfb_probe, + .remove = __devexit_p(jzfb_remove), + .driver = { + .name = "jz4740-fb", + .pm = JZFB_PM_OPS, + }, +}; + +static int __init jzfb_init(void) +{ + return platform_driver_register(&jzfb_driver); +} +module_init(jzfb_init); + +static void __exit jzfb_exit(void) +{ + platform_driver_unregister(&jzfb_driver); +} +module_exit(jzfb_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("JZ4740 SoC LCD framebuffer driver"); +MODULE_ALIAS("platform:jz4740-fb"); diff --git a/drivers/video/leo.c b/drivers/video/leo.c index 9e8bf7d5e24..b599e5e36ce 100644 --- a/drivers/video/leo.c +++ b/drivers/video/leo.c @@ -529,7 +529,7 @@ static void leo_fixup_var_rgb(struct fb_var_screeninfo *var) var->transp.length = 0; } -static void leo_unmap_regs(struct of_device *op, struct fb_info *info, +static void leo_unmap_regs(struct platform_device *op, struct fb_info *info, struct leo_par *par) { if (par->lc_ss0_usr) @@ -547,7 +547,7 @@ static void leo_unmap_regs(struct of_device *op, struct fb_info *info, of_iounmap(&op->resource[0], info->screen_base, 0x800000); } -static int __devinit leo_probe(struct of_device *op, +static int __devinit leo_probe(struct platform_device *op, const struct of_device_id *match) { struct device_node *dp = op->dev.of_node; @@ -637,7 +637,7 @@ out_err: return err; } -static int __devexit leo_remove(struct of_device *op) +static int __devexit leo_remove(struct platform_device *op) { struct fb_info *info = dev_get_drvdata(&op->dev); struct leo_par *par = info->par; @@ -677,12 +677,12 @@ static int __init leo_init(void) if (fb_get_options("leofb", NULL)) return -ENODEV; - return of_register_driver(&leo_driver, &of_bus_type); + return of_register_platform_driver(&leo_driver); } static void __exit leo_exit(void) { - of_unregister_driver(&leo_driver); + of_unregister_platform_driver(&leo_driver); } module_init(leo_init); diff --git a/drivers/video/matrox/i2c-matroxfb.c b/drivers/video/matrox/i2c-matroxfb.c index 403b14445a7..0fb280ead3d 100644 --- a/drivers/video/matrox/i2c-matroxfb.c +++ b/drivers/video/matrox/i2c-matroxfb.c @@ -191,7 +191,7 @@ static void* i2c_matroxfb_probe(struct matrox_fb_info* minfo) { }; i2c_new_probed_device(&m2info->maven.adapter, - &maven_info, addr_list); + &maven_info, addr_list, NULL); } } return m2info; diff --git a/drivers/video/matrox/matroxfb_base.h b/drivers/video/matrox/matroxfb_base.h index f3a4e15672d..f96a471cb1a 100644 --- a/drivers/video/matrox/matroxfb_base.h +++ b/drivers/video/matrox/matroxfb_base.h @@ -151,13 +151,13 @@ static inline void mga_writel(vaddr_t va, unsigned int offs, u_int32_t value) { static inline void mga_memcpy_toio(vaddr_t va, const void* src, int len) { #if defined(__alpha__) || defined(__i386__) || defined(__x86_64__) /* - * memcpy_toio works for us if: + * iowrite32_rep works for us if: * (1) Copies data as 32bit quantities, not byte after byte, * (2) Performs LE ordered stores, and * (3) It copes with unaligned source (destination is guaranteed to be page * aligned and length is guaranteed to be multiple of 4). */ - memcpy_toio(va.vaddr, src, len); + iowrite32_rep(va.vaddr, src, len >> 2); #else u_int32_t __iomem* addr = va.vaddr; diff --git a/drivers/video/mb862xx/mb862xxfb.c b/drivers/video/mb862xx/mb862xxfb.c index 4e2b8cc3d46..b1c4374cf94 100644 --- a/drivers/video/mb862xx/mb862xxfb.c +++ b/drivers/video/mb862xx/mb862xxfb.c @@ -550,7 +550,7 @@ static int mb862xx_gdc_init(struct mb862xxfb_par *par) return 0; } -static int __devinit of_platform_mb862xx_probe(struct of_device *ofdev, +static int __devinit of_platform_mb862xx_probe(struct platform_device *ofdev, const struct of_device_id *id) { struct device_node *np = ofdev->dev.of_node; @@ -669,7 +669,7 @@ fbrel: return ret; } -static int __devexit of_platform_mb862xx_remove(struct of_device *ofdev) +static int __devexit of_platform_mb862xx_remove(struct platform_device *ofdev) { struct fb_info *fbi = dev_get_drvdata(&ofdev->dev); struct mb862xxfb_par *par = fbi->par; diff --git a/drivers/video/msm/mddi.c b/drivers/video/msm/mddi.c index c1ff271017a..7c316c34dfc 100644 --- a/drivers/video/msm/mddi.c +++ b/drivers/video/msm/mddi.c @@ -187,10 +187,8 @@ static void mddi_wait_interrupt(struct mddi_info *mddi, uint32_t intmask); static void mddi_handle_rev_data_avail(struct mddi_info *mddi) { - union mddi_rev *rev = mddi->rev_data; uint32_t rev_data_count; uint32_t rev_crc_err_count; - int i; struct reg_read_info *ri; size_t prev_offset; uint16_t length; @@ -670,7 +668,7 @@ static int __init mddi_rev_data_setup(struct mddi_info *mddi) return 0; } -static int __init mddi_probe(struct platform_device *pdev) +static int __devinit mddi_probe(struct platform_device *pdev) { struct msm_mddi_platform_data *pdata = pdev->dev.platform_data; struct mddi_info *mddi = &mddi_info[pdev->id]; diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c index 19c01c6208e..3c28db03ad3 100644 --- a/drivers/video/msm/mdp.c +++ b/drivers/video/msm/mdp.c @@ -258,7 +258,6 @@ int get_img(struct mdp_img *img, struct fb_info *info, { int put_needed, ret = 0; struct file *file; - unsigned long vstart; file = fget_light(img->memory_id, &put_needed); if (file == NULL) diff --git a/drivers/video/offb.c b/drivers/video/offb.c index 46dda7d8aae..cb163a5397b 100644 --- a/drivers/video/offb.c +++ b/drivers/video/offb.c @@ -19,13 +19,14 @@ #include <linux/mm.h> #include <linux/vmalloc.h> #include <linux/delay.h> +#include <linux/of.h> +#include <linux/of_address.h> #include <linux/interrupt.h> #include <linux/fb.h> #include <linux/init.h> #include <linux/ioport.h> #include <linux/pci.h> #include <asm/io.h> -#include <asm/prom.h> #ifdef CONFIG_PPC64 #include <asm/pci-bridge.h> diff --git a/drivers/video/omap/lcd_apollon.c b/drivers/video/omap/lcd_apollon.c index 2be94eb3bbf..10459d8bd9a 100644 --- a/drivers/video/omap/lcd_apollon.c +++ b/drivers/video/omap/lcd_apollon.c @@ -25,7 +25,6 @@ #include <linux/platform_device.h> #include <mach/gpio.h> -#include <plat/mux.h> #include "omapfb.h" @@ -34,8 +33,6 @@ static int apollon_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev) { - /* configure LCD PWR_EN */ - omap_cfg_reg(M21_242X_GPIO11); return 0; } diff --git a/drivers/video/omap2/displays/panel-acx565akm.c b/drivers/video/omap2/displays/panel-acx565akm.c index 1f8eb70e293..07fbb8a733b 100644 --- a/drivers/video/omap2/displays/panel-acx565akm.c +++ b/drivers/video/omap2/displays/panel-acx565akm.c @@ -592,7 +592,7 @@ static int acx_panel_power_on(struct omap_dss_device *dssdev) r = omapdss_sdi_display_enable(dssdev); if (r) { pr_err("%s sdi enable failed\n", __func__); - return r; + goto fail_unlock; } /*FIXME tweak me */ @@ -633,6 +633,8 @@ static int acx_panel_power_on(struct omap_dss_device *dssdev) return acx565akm_bl_update_status(md->bl_dev); fail: omapdss_sdi_display_disable(dssdev); +fail_unlock: + mutex_unlock(&md->mutex); return r; } diff --git a/drivers/video/omap2/displays/panel-taal.c b/drivers/video/omap2/displays/panel-taal.c index aaf5d308a04..e1c765d1141 100644 --- a/drivers/video/omap2/displays/panel-taal.c +++ b/drivers/video/omap2/displays/panel-taal.c @@ -28,12 +28,13 @@ #include <linux/fb.h> #include <linux/interrupt.h> #include <linux/gpio.h> -#include <linux/completion.h> #include <linux/workqueue.h> #include <linux/slab.h> +#include <linux/regulator/consumer.h> #include <linux/mutex.h> #include <plat/display.h> +#include <plat/nokia-dsi-panel.h> /* DSI Virtual channel. Hardcoded for now. */ #define TCH 0 @@ -62,11 +63,136 @@ #define DCS_GET_ID2 0xdb #define DCS_GET_ID3 0xdc -/* #define TAAL_USE_ESD_CHECK */ #define TAAL_ESD_CHECK_PERIOD msecs_to_jiffies(5000) +static irqreturn_t taal_te_isr(int irq, void *data); +static void taal_te_timeout_work_callback(struct work_struct *work); static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable); +struct panel_regulator { + struct regulator *regulator; + const char *name; + int min_uV; + int max_uV; +}; + +static void free_regulators(struct panel_regulator *regulators, int n) +{ + int i; + + for (i = 0; i < n; i++) { + /* disable/put in reverse order */ + regulator_disable(regulators[n - i - 1].regulator); + regulator_put(regulators[n - i - 1].regulator); + } +} + +static int init_regulators(struct omap_dss_device *dssdev, + struct panel_regulator *regulators, int n) +{ + int r, i, v; + + for (i = 0; i < n; i++) { + struct regulator *reg; + + reg = regulator_get(&dssdev->dev, regulators[i].name); + if (IS_ERR(reg)) { + dev_err(&dssdev->dev, "failed to get regulator %s\n", + regulators[i].name); + r = PTR_ERR(reg); + goto err; + } + + /* FIXME: better handling of fixed vs. variable regulators */ + v = regulator_get_voltage(reg); + if (v < regulators[i].min_uV || v > regulators[i].max_uV) { + r = regulator_set_voltage(reg, regulators[i].min_uV, + regulators[i].max_uV); + if (r) { + dev_err(&dssdev->dev, + "failed to set regulator %s voltage\n", + regulators[i].name); + regulator_put(reg); + goto err; + } + } + + r = regulator_enable(reg); + if (r) { + dev_err(&dssdev->dev, "failed to enable regulator %s\n", + regulators[i].name); + regulator_put(reg); + goto err; + } + + regulators[i].regulator = reg; + } + + return 0; + +err: + free_regulators(regulators, i); + + return r; +} + +/** + * struct panel_config - panel configuration + * @name: panel name + * @type: panel type + * @timings: panel resolution + * @sleep: various panel specific delays, passed to msleep() if non-zero + * @reset_sequence: reset sequence timings, passed to udelay() if non-zero + * @regulators: array of panel regulators + * @num_regulators: number of regulators in the array + */ +struct panel_config { + const char *name; + int type; + + struct omap_video_timings timings; + + struct { + unsigned int sleep_in; + unsigned int sleep_out; + unsigned int hw_reset; + unsigned int enable_te; + } sleep; + + struct { + unsigned int high; + unsigned int low; + } reset_sequence; + + struct panel_regulator *regulators; + int num_regulators; +}; + +enum { + PANEL_TAAL, +}; + +static struct panel_config panel_configs[] = { + { + .name = "taal", + .type = PANEL_TAAL, + .timings = { + .x_res = 864, + .y_res = 480, + }, + .sleep = { + .sleep_in = 5, + .sleep_out = 5, + .hw_reset = 5, + .enable_te = 100, /* possible panel bug */ + }, + .reset_sequence = { + .high = 10, + .low = 10, + }, + }, +}; + struct taal_data { struct mutex lock; @@ -84,8 +210,15 @@ struct taal_data { bool mirror; bool te_enabled; - bool use_ext_te; - struct completion te_completion; + + atomic_t do_update; + struct { + u16 x; + u16 y; + u16 w; + u16 h; + } update_region; + struct delayed_work te_timeout_work; bool use_dsi_bl; @@ -96,8 +229,16 @@ struct taal_data { struct workqueue_struct *esd_wq; struct delayed_work esd_work; + + struct panel_config *panel_config; }; +static inline struct nokia_dsi_panel_data +*get_panel_data(const struct omap_dss_device *dssdev) +{ + return (struct nokia_dsi_panel_data *) dssdev->data; +} + static void taal_esd_work(struct work_struct *work); static void hw_guard_start(struct taal_data *td, int guard_msec) @@ -159,7 +300,8 @@ static int taal_sleep_in(struct taal_data *td) hw_guard_start(td, 120); - msleep(5); + if (td->panel_config->sleep.sleep_in) + msleep(td->panel_config->sleep.sleep_in); return 0; } @@ -176,7 +318,8 @@ static int taal_sleep_out(struct taal_data *td) hw_guard_start(td, 120); - msleep(5); + if (td->panel_config->sleep.sleep_out) + msleep(td->panel_config->sleep.sleep_out); return 0; } @@ -279,6 +422,7 @@ 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; @@ -290,24 +434,26 @@ static int taal_bl_update_status(struct backlight_device *dev) dev_dbg(&dssdev->dev, "update brightness to %d\n", level); + mutex_lock(&td->lock); + if (td->use_dsi_bl) { if (td->enabled) { dsi_bus_lock(); r = taal_dcs_write_1(DCS_BRIGHTNESS, level); dsi_bus_unlock(); - if (r) - return r; + } else { + r = 0; } } else { - if (!dssdev->set_backlight) - return -EINVAL; - - r = dssdev->set_backlight(dssdev, level); - if (r) - return r; + if (!panel_data->set_backlight) + r = -EINVAL; + else + r = panel_data->set_backlight(dssdev, level); } - return 0; + mutex_unlock(&td->lock); + + return r; } static int taal_bl_get_intensity(struct backlight_device *dev) @@ -344,16 +490,6 @@ static void taal_get_resolution(struct omap_dss_device *dssdev, } } -static irqreturn_t taal_te_isr(int irq, void *data) -{ - struct omap_dss_device *dssdev = data; - struct taal_data *td = dev_get_drvdata(&dssdev->dev); - - complete_all(&td->te_completion); - - return IRQ_HANDLED; -} - static ssize_t taal_num_errors_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -362,6 +498,8 @@ static ssize_t taal_num_errors_show(struct device *dev, u8 errors; int r; + mutex_lock(&td->lock); + if (td->enabled) { dsi_bus_lock(); r = taal_dcs_read_1(DCS_READ_NUM_ERRORS, &errors); @@ -370,6 +508,8 @@ static ssize_t taal_num_errors_show(struct device *dev, r = -ENODEV; } + mutex_unlock(&td->lock); + if (r) return r; @@ -384,6 +524,8 @@ static ssize_t taal_hw_revision_show(struct device *dev, u8 id1, id2, id3; int r; + mutex_lock(&td->lock); + if (td->enabled) { dsi_bus_lock(); r = taal_get_id(&id1, &id2, &id3); @@ -392,6 +534,8 @@ static ssize_t taal_hw_revision_show(struct device *dev, r = -ENODEV; } + mutex_unlock(&td->lock); + if (r) return r; @@ -441,6 +585,8 @@ static ssize_t store_cabc_mode(struct device *dev, if (i == ARRAY_SIZE(cabc_modes)) return -EINVAL; + mutex_lock(&td->lock); + if (td->enabled) { dsi_bus_lock(); if (!td->cabc_broken) @@ -450,6 +596,8 @@ static ssize_t store_cabc_mode(struct device *dev, td->cabc_mode = i; + mutex_unlock(&td->lock); + return count; } @@ -488,47 +636,93 @@ static struct attribute_group taal_attr_group = { .attrs = taal_attrs, }; +static void taal_hw_reset(struct omap_dss_device *dssdev) +{ + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev); + + if (panel_data->reset_gpio == -1) + return; + + gpio_set_value(panel_data->reset_gpio, 1); + if (td->panel_config->reset_sequence.high) + udelay(td->panel_config->reset_sequence.high); + /* reset the panel */ + gpio_set_value(panel_data->reset_gpio, 0); + /* assert reset */ + if (td->panel_config->reset_sequence.low) + udelay(td->panel_config->reset_sequence.low); + gpio_set_value(panel_data->reset_gpio, 1); + /* wait after releasing reset */ + if (td->panel_config->sleep.hw_reset) + msleep(td->panel_config->sleep.hw_reset); +} + static int taal_probe(struct omap_dss_device *dssdev) { struct backlight_properties props; struct taal_data *td; struct backlight_device *bldev; - int r; - - const struct omap_video_timings taal_panel_timings = { - .x_res = 864, - .y_res = 480, - }; + struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev); + struct panel_config *panel_config = NULL; + int r, i; dev_dbg(&dssdev->dev, "probe\n"); + if (!panel_data || !panel_data->name) { + r = -EINVAL; + goto err; + } + + for (i = 0; i < ARRAY_SIZE(panel_configs); i++) { + if (strcmp(panel_data->name, panel_configs[i].name) == 0) { + panel_config = &panel_configs[i]; + break; + } + } + + if (!panel_config) { + r = -EINVAL; + goto err; + } + dssdev->panel.config = OMAP_DSS_LCD_TFT; - dssdev->panel.timings = taal_panel_timings; + dssdev->panel.timings = panel_config->timings; dssdev->ctrl.pixel_size = 24; td = kzalloc(sizeof(*td), GFP_KERNEL); if (!td) { r = -ENOMEM; - goto err0; + goto err; } td->dssdev = dssdev; + td->panel_config = panel_config; mutex_init(&td->lock); + atomic_set(&td->do_update, 0); + + r = init_regulators(dssdev, panel_config->regulators, + panel_config->num_regulators); + if (r) + goto err_reg; + td->esd_wq = create_singlethread_workqueue("taal_esd"); if (td->esd_wq == NULL) { dev_err(&dssdev->dev, "can't create ESD workqueue\n"); r = -ENOMEM; - goto err1; + goto err_wq; } INIT_DELAYED_WORK_DEFERRABLE(&td->esd_work, taal_esd_work); dev_set_drvdata(&dssdev->dev, td); + taal_hw_reset(dssdev); + /* if no platform set_backlight() defined, presume DSI backlight * control */ memset(&props, 0, sizeof(struct backlight_properties)); - if (!dssdev->set_backlight) + if (!panel_data->set_backlight) td->use_dsi_bl = true; if (td->use_dsi_bl) @@ -539,7 +733,7 @@ static int taal_probe(struct omap_dss_device *dssdev) &taal_bl_ops, &props); if (IS_ERR(bldev)) { r = PTR_ERR(bldev); - goto err2; + goto err_bl; } td->bldev = bldev; @@ -553,13 +747,13 @@ static int taal_probe(struct omap_dss_device *dssdev) taal_bl_update_status(bldev); - if (dssdev->phy.dsi.ext_te) { - int gpio = dssdev->phy.dsi.ext_te_gpio; + if (panel_data->use_ext_te) { + int gpio = panel_data->ext_te_gpio; r = gpio_request(gpio, "taal irq"); if (r) { dev_err(&dssdev->dev, "GPIO request failed\n"); - goto err3; + goto err_gpio; } gpio_direction_input(gpio); @@ -571,49 +765,52 @@ static int taal_probe(struct omap_dss_device *dssdev) if (r) { dev_err(&dssdev->dev, "IRQ request failed\n"); gpio_free(gpio); - goto err3; + goto err_irq; } - init_completion(&td->te_completion); + INIT_DELAYED_WORK_DEFERRABLE(&td->te_timeout_work, + taal_te_timeout_work_callback); - td->use_ext_te = true; + dev_dbg(&dssdev->dev, "Using GPIO TE\n"); } r = sysfs_create_group(&dssdev->dev.kobj, &taal_attr_group); if (r) { dev_err(&dssdev->dev, "failed to create sysfs files\n"); - goto err4; + goto err_sysfs; } return 0; -err4: - if (td->use_ext_te) { - int gpio = dssdev->phy.dsi.ext_te_gpio; - free_irq(gpio_to_irq(gpio), dssdev); - gpio_free(gpio); - } -err3: +err_sysfs: + if (panel_data->use_ext_te) + free_irq(gpio_to_irq(panel_data->ext_te_gpio), dssdev); +err_irq: + if (panel_data->use_ext_te) + gpio_free(panel_data->ext_te_gpio); +err_gpio: backlight_device_unregister(bldev); -err2: - cancel_delayed_work_sync(&td->esd_work); +err_bl: destroy_workqueue(td->esd_wq); -err1: +err_wq: + free_regulators(panel_config->regulators, panel_config->num_regulators); +err_reg: kfree(td); -err0: +err: return r; } static void taal_remove(struct omap_dss_device *dssdev) { struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev); struct backlight_device *bldev; dev_dbg(&dssdev->dev, "remove\n"); sysfs_remove_group(&dssdev->dev.kobj, &taal_attr_group); - if (td->use_ext_te) { - int gpio = dssdev->phy.dsi.ext_te_gpio; + if (panel_data->use_ext_te) { + int gpio = panel_data->ext_te_gpio; free_irq(gpio_to_irq(gpio), dssdev); gpio_free(gpio); } @@ -623,9 +820,15 @@ static void taal_remove(struct omap_dss_device *dssdev) taal_bl_update_status(bldev); backlight_device_unregister(bldev); - cancel_delayed_work_sync(&td->esd_work); + cancel_delayed_work(&td->esd_work); destroy_workqueue(td->esd_wq); + /* reset, to be sure that the panel is in a valid state */ + taal_hw_reset(dssdev); + + free_regulators(td->panel_config->regulators, + td->panel_config->num_regulators); + kfree(td); } @@ -635,23 +838,14 @@ static int taal_power_on(struct omap_dss_device *dssdev) u8 id1, id2, id3; int r; - if (dssdev->platform_enable) { - r = dssdev->platform_enable(dssdev); - if (r) - return r; - } - - /* it seems we have to wait a bit until taal is ready */ - msleep(5); - - dsi_bus_lock(); - r = omapdss_dsi_display_enable(dssdev); if (r) { dev_err(&dssdev->dev, "failed to enable DSI\n"); goto err0; } + taal_hw_reset(dssdev); + omapdss_dsi_vc_enable_hs(TCH, false); r = taal_sleep_out(td); @@ -662,34 +856,47 @@ static int taal_power_on(struct omap_dss_device *dssdev) if (r) goto err; - /* on early revisions CABC is broken */ - if (id2 == 0x00 || id2 == 0xff || id2 == 0x81) + /* on early Taal revisions CABC is broken */ + if (td->panel_config->type == PANEL_TAAL && + (id2 == 0x00 || id2 == 0xff || id2 == 0x81)) td->cabc_broken = true; - taal_dcs_write_1(DCS_BRIGHTNESS, 0xff); - taal_dcs_write_1(DCS_CTRL_DISPLAY, (1<<2) | (1<<5)); /* BL | BCTRL */ + r = taal_dcs_write_1(DCS_BRIGHTNESS, 0xff); + if (r) + goto err; + + r = taal_dcs_write_1(DCS_CTRL_DISPLAY, + (1<<2) | (1<<5)); /* BL | BCTRL */ + if (r) + goto err; + + r = taal_dcs_write_1(DCS_PIXEL_FORMAT, 0x7); /* 24bit/pixel */ + if (r) + goto err; - taal_dcs_write_1(DCS_PIXEL_FORMAT, 0x7); /* 24bit/pixel */ + r = taal_set_addr_mode(td->rotate, td->mirror); + if (r) + goto err; - taal_set_addr_mode(td->rotate, td->mirror); - if (!td->cabc_broken) - taal_dcs_write_1(DCS_WRITE_CABC, td->cabc_mode); + if (!td->cabc_broken) { + r = taal_dcs_write_1(DCS_WRITE_CABC, td->cabc_mode); + if (r) + goto err; + } - taal_dcs_write_0(DCS_DISPLAY_ON); + r = taal_dcs_write_0(DCS_DISPLAY_ON); + if (r) + goto err; r = _taal_enable_te(dssdev, td->te_enabled); if (r) goto err; -#ifdef TAAL_USE_ESD_CHECK - queue_delayed_work(td->esd_wq, &td->esd_work, TAAL_ESD_CHECK_PERIOD); -#endif - td->enabled = 1; if (!td->intro_printed) { - dev_info(&dssdev->dev, "revision %02x.%02x.%02x\n", - id1, id2, id3); + dev_info(&dssdev->dev, "%s panel revision %02x.%02x.%02x\n", + td->panel_config->name, id1, id2, id3); if (td->cabc_broken) dev_info(&dssdev->dev, "old Taal version, CABC disabled\n"); @@ -698,46 +905,44 @@ static int taal_power_on(struct omap_dss_device *dssdev) omapdss_dsi_vc_enable_hs(TCH, true); - dsi_bus_unlock(); - return 0; err: + dev_err(&dssdev->dev, "error while enabling panel, issuing HW reset\n"); + + taal_hw_reset(dssdev); + omapdss_dsi_display_disable(dssdev); err0: - dsi_bus_unlock(); - if (dssdev->platform_disable) - dssdev->platform_disable(dssdev); - return r; } static void taal_power_off(struct omap_dss_device *dssdev) { struct taal_data *td = dev_get_drvdata(&dssdev->dev); + int r; - dsi_bus_lock(); - - cancel_delayed_work(&td->esd_work); - - taal_dcs_write_0(DCS_DISPLAY_OFF); - taal_sleep_in(td); + r = taal_dcs_write_0(DCS_DISPLAY_OFF); + if (!r) { + r = taal_sleep_in(td); + /* HACK: wait a bit so that the message goes through */ + msleep(10); + } - /* wait a bit so that the message goes through */ - msleep(10); + if (r) { + dev_err(&dssdev->dev, + "error disabling panel, issuing HW reset\n"); + taal_hw_reset(dssdev); + } omapdss_dsi_display_disable(dssdev); - if (dssdev->platform_disable) - dssdev->platform_disable(dssdev); - td->enabled = 0; - - dsi_bus_unlock(); } static int taal_enable(struct omap_dss_device *dssdev) { struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev); int r; dev_dbg(&dssdev->dev, "enable\n"); @@ -749,10 +954,19 @@ static int taal_enable(struct omap_dss_device *dssdev) goto err; } + dsi_bus_lock(); + r = taal_power_on(dssdev); + + dsi_bus_unlock(); + if (r) goto err; + if (panel_data->use_esd_check) + queue_delayed_work(td->esd_wq, &td->esd_work, + TAAL_ESD_CHECK_PERIOD); + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; mutex_unlock(&td->lock); @@ -772,9 +986,15 @@ static void taal_disable(struct omap_dss_device *dssdev) mutex_lock(&td->lock); + cancel_delayed_work(&td->esd_work); + + dsi_bus_lock(); + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) taal_power_off(dssdev); + dsi_bus_unlock(); + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; mutex_unlock(&td->lock); @@ -794,7 +1014,14 @@ static int taal_suspend(struct omap_dss_device *dssdev) goto err; } + cancel_delayed_work(&td->esd_work); + + dsi_bus_lock(); + taal_power_off(dssdev); + + dsi_bus_unlock(); + dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; mutex_unlock(&td->lock); @@ -808,6 +1035,7 @@ err: static int taal_resume(struct omap_dss_device *dssdev) { struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev); int r; dev_dbg(&dssdev->dev, "resume\n"); @@ -819,8 +1047,20 @@ static int taal_resume(struct omap_dss_device *dssdev) goto err; } + dsi_bus_lock(); + r = taal_power_on(dssdev); - dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + dsi_bus_unlock(); + + if (r) { + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; + } else { + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + if (panel_data->use_esd_check) + queue_delayed_work(td->esd_wq, &td->esd_work, + TAAL_ESD_CHECK_PERIOD); + } mutex_unlock(&td->lock); @@ -837,10 +1077,52 @@ static void taal_framedone_cb(int err, void *data) dsi_bus_unlock(); } +static irqreturn_t taal_te_isr(int irq, void *data) +{ + struct omap_dss_device *dssdev = data; + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + int old; + int r; + + old = atomic_cmpxchg(&td->do_update, 1, 0); + + if (old) { + cancel_delayed_work(&td->te_timeout_work); + + r = omap_dsi_update(dssdev, TCH, + td->update_region.x, + td->update_region.y, + td->update_region.w, + td->update_region.h, + taal_framedone_cb, dssdev); + if (r) + goto err; + } + + return IRQ_HANDLED; +err: + dev_err(&dssdev->dev, "start update failed\n"); + dsi_bus_unlock(); + return IRQ_HANDLED; +} + +static void taal_te_timeout_work_callback(struct work_struct *work) +{ + struct taal_data *td = container_of(work, struct taal_data, + te_timeout_work.work); + struct omap_dss_device *dssdev = td->dssdev; + + dev_err(&dssdev->dev, "TE not received for 250ms!\n"); + + atomic_set(&td->do_update, 0); + dsi_bus_unlock(); +} + static int taal_update(struct omap_dss_device *dssdev, u16 x, u16 y, u16 w, u16 h) { struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev); int r; dev_dbg(&dssdev->dev, "update %d, %d, %d x %d\n", x, y, w, h); @@ -853,7 +1135,7 @@ static int taal_update(struct omap_dss_device *dssdev, goto err; } - r = omap_dsi_prepare_update(dssdev, &x, &y, &w, &h); + r = omap_dsi_prepare_update(dssdev, &x, &y, &w, &h, true); if (r) goto err; @@ -861,10 +1143,21 @@ static int taal_update(struct omap_dss_device *dssdev, if (r) goto err; - r = omap_dsi_update(dssdev, TCH, x, y, w, h, - taal_framedone_cb, dssdev); - if (r) - goto err; + if (td->te_enabled && panel_data->use_ext_te) { + td->update_region.x = x; + td->update_region.y = y; + td->update_region.w = w; + td->update_region.h = h; + barrier(); + schedule_delayed_work(&td->te_timeout_work, + msecs_to_jiffies(250)); + atomic_set(&td->do_update, 1); + } else { + r = omap_dsi_update(dssdev, TCH, x, y, w, h, + taal_framedone_cb, dssdev); + if (r) + goto err; + } /* note: no bus_unlock here. unlock is in framedone_cb */ mutex_unlock(&td->lock); @@ -894,20 +1187,19 @@ static int taal_sync(struct omap_dss_device *dssdev) static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable) { struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev); int r; - td->te_enabled = enable; - if (enable) r = taal_dcs_write_1(DCS_TEAR_ON, 0); else r = taal_dcs_write_0(DCS_TEAR_OFF); - omapdss_dsi_enable_te(dssdev, enable); + if (!panel_data->use_ext_te) + omapdss_dsi_enable_te(dssdev, enable); - /* XXX for some reason, DSI TE breaks if we don't wait here. - * Panel bug? Needs more studying */ - msleep(100); + if (td->panel_config->sleep.enable_te) + msleep(td->panel_config->sleep.enable_te); return r; } @@ -918,10 +1210,26 @@ static int taal_enable_te(struct omap_dss_device *dssdev, bool enable) int r; mutex_lock(&td->lock); + + if (td->te_enabled == enable) + goto end; + dsi_bus_lock(); - r = _taal_enable_te(dssdev, enable); + if (td->enabled) { + r = _taal_enable_te(dssdev, enable); + if (r) + goto err; + } + + td->te_enabled = enable; + + dsi_bus_unlock(); +end: + mutex_unlock(&td->lock); + return 0; +err: dsi_bus_unlock(); mutex_unlock(&td->lock); @@ -948,6 +1256,10 @@ static int taal_rotate(struct omap_dss_device *dssdev, u8 rotate) dev_dbg(&dssdev->dev, "rotate %d\n", rotate); mutex_lock(&td->lock); + + if (td->rotate == rotate) + goto end; + dsi_bus_lock(); if (td->enabled) { @@ -959,6 +1271,7 @@ static int taal_rotate(struct omap_dss_device *dssdev, u8 rotate) td->rotate = rotate; dsi_bus_unlock(); +end: mutex_unlock(&td->lock); return 0; err: @@ -987,6 +1300,10 @@ static int taal_mirror(struct omap_dss_device *dssdev, bool enable) dev_dbg(&dssdev->dev, "mirror %d\n", enable); mutex_lock(&td->lock); + + if (td->mirror == enable) + goto end; + dsi_bus_lock(); if (td->enabled) { r = taal_set_addr_mode(td->rotate, enable); @@ -997,6 +1314,7 @@ static int taal_mirror(struct omap_dss_device *dssdev, bool enable) td->mirror = enable; dsi_bus_unlock(); +end: mutex_unlock(&td->lock); return 0; err: @@ -1024,23 +1342,30 @@ static int taal_run_test(struct omap_dss_device *dssdev, int test_num) int r; mutex_lock(&td->lock); + + if (!td->enabled) { + r = -ENODEV; + goto err1; + } + dsi_bus_lock(); r = taal_dcs_read_1(DCS_GET_ID1, &id1); if (r) - goto err; + goto err2; r = taal_dcs_read_1(DCS_GET_ID2, &id2); if (r) - goto err; + goto err2; r = taal_dcs_read_1(DCS_GET_ID3, &id3); if (r) - goto err; + goto err2; dsi_bus_unlock(); mutex_unlock(&td->lock); return 0; -err: +err2: dsi_bus_unlock(); +err1: mutex_unlock(&td->lock); return r; } @@ -1128,6 +1453,7 @@ static void taal_esd_work(struct work_struct *work) struct taal_data *td = container_of(work, struct taal_data, esd_work.work); struct omap_dss_device *dssdev = td->dssdev; + struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev); u8 state1, state2; int r; @@ -1168,7 +1494,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->use_ext_te && td->te_enabled) { + if (td->te_enabled && panel_data->use_ext_te) { r = taal_dcs_write_1(DCS_TEAR_ON, 0); if (r) goto err; @@ -1184,6 +1510,7 @@ err: dev_err(&dssdev->dev, "performing LCD reset\n"); taal_power_off(dssdev); + taal_hw_reset(dssdev); taal_power_on(dssdev); dsi_bus_unlock(); diff --git a/drivers/video/omap2/displays/panel-toppoly-tdo35s.c b/drivers/video/omap2/displays/panel-toppoly-tdo35s.c index fa434ca6e4b..e320e67d06f 100644 --- a/drivers/video/omap2/displays/panel-toppoly-tdo35s.c +++ b/drivers/video/omap2/displays/panel-toppoly-tdo35s.c @@ -73,8 +73,12 @@ static void toppoly_tdo_panel_power_off(struct omap_dss_device *dssdev) static int toppoly_tdo_panel_probe(struct omap_dss_device *dssdev) { - dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS; + dssdev->panel.config = OMAP_DSS_LCD_TFT | + OMAP_DSS_LCD_IVS | + OMAP_DSS_LCD_IHS | + OMAP_DSS_LCD_IPC | + OMAP_DSS_LCD_ONOFF; + dssdev->panel.timings = toppoly_tdo_panel_timings; return 0; diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c index e777e352dbc..5ecdc000409 100644 --- a/drivers/video/omap2/dss/dispc.c +++ b/drivers/video/omap2/dss/dispc.c @@ -31,6 +31,7 @@ #include <linux/seq_file.h> #include <linux/delay.h> #include <linux/workqueue.h> +#include <linux/hardirq.h> #include <plat/sram.h> #include <plat/clock.h> @@ -335,7 +336,7 @@ void dispc_save_context(void) void dispc_restore_context(void) { RR(SYSCONFIG); - RR(IRQENABLE); + /*RR(IRQENABLE);*/ /*RR(CONTROL);*/ RR(CONFIG); RR(DEFAULT_COLOR0); @@ -472,6 +473,15 @@ void dispc_restore_context(void) /* enable last, because LCD & DIGIT enable are here */ RR(CONTROL); + + /* clear spurious SYNC_LOST_DIGIT interrupts */ + dispc_write_reg(DISPC_IRQSTATUS, DISPC_IRQ_SYNC_LOST_DIGIT); + + /* + * enable last so IRQs won't trigger before + * the context is fully restored + */ + RR(IRQENABLE); } #undef SR @@ -3019,7 +3029,7 @@ void dispc_fake_vsync_irq(void) u32 irqstatus = DISPC_IRQ_VSYNC; int i; - local_irq_disable(); + WARN_ON(!in_interrupt()); for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { struct omap_dispc_isr_data *isr_data; @@ -3031,8 +3041,6 @@ void dispc_fake_vsync_irq(void) if (isr_data->mask & irqstatus) isr_data->isr(isr_data->arg, irqstatus); } - - local_irq_enable(); } #endif diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c index ef8c8529dda..22dd7a474f7 100644 --- a/drivers/video/omap2/dss/display.c +++ b/drivers/video/omap2/dss/display.c @@ -82,6 +82,9 @@ static ssize_t display_upd_mode_store(struct device *dev, int val, r; enum omap_dss_update_mode mode; + if (!dssdev->driver->set_update_mode) + return -EINVAL; + val = simple_strtoul(buf, NULL, 10); switch (val) { @@ -343,7 +346,6 @@ int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev) case OMAP_DISPLAY_TYPE_VENC: case OMAP_DISPLAY_TYPE_SDI: return 24; - return 24; default: BUG(); } diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c index 3af207b2bde..b3fa3a7db91 100644 --- a/drivers/video/omap2/dss/dsi.c +++ b/drivers/video/omap2/dss/dsi.c @@ -165,6 +165,14 @@ struct dsi_reg { u16 idx; }; #define DSI_CIO_IRQ_ERRCONTENTIONLP1_3 (1 << 25) #define DSI_CIO_IRQ_ULPSACTIVENOT_ALL0 (1 << 30) #define DSI_CIO_IRQ_ULPSACTIVENOT_ALL1 (1 << 31) +#define DSI_CIO_IRQ_ERROR_MASK \ + (DSI_CIO_IRQ_ERRSYNCESC1 | DSI_CIO_IRQ_ERRSYNCESC2 | \ + DSI_CIO_IRQ_ERRSYNCESC3 | DSI_CIO_IRQ_ERRESC1 | DSI_CIO_IRQ_ERRESC2 | \ + DSI_CIO_IRQ_ERRESC3 | DSI_CIO_IRQ_ERRCONTROL1 | \ + DSI_CIO_IRQ_ERRCONTROL2 | DSI_CIO_IRQ_ERRCONTROL3 | \ + DSI_CIO_IRQ_ERRCONTENTIONLP0_1 | DSI_CIO_IRQ_ERRCONTENTIONLP1_1 | \ + DSI_CIO_IRQ_ERRCONTENTIONLP0_2 | DSI_CIO_IRQ_ERRCONTENTIONLP1_2 | \ + DSI_CIO_IRQ_ERRCONTENTIONLP0_3 | DSI_CIO_IRQ_ERRCONTENTIONLP1_3) #define DSI_DT_DCS_SHORT_WRITE_0 0x05 #define DSI_DT_DCS_SHORT_WRITE_1 0x15 @@ -232,13 +240,15 @@ static struct unsigned pll_locked; struct completion bta_completion; + void (*bta_callback)(void); int update_channel; struct dsi_update_region update_region; bool te_enabled; - struct work_struct framedone_work; + struct workqueue_struct *workqueue; + void (*framedone_callback)(int, void *); void *framedone_data; @@ -509,9 +519,13 @@ void dsi_irq_handler(void) dss_collect_irq_stats(vcstatus, dsi.irq_stats.vc_irqs[i]); #endif - if (vcstatus & DSI_VC_IRQ_BTA) + if (vcstatus & DSI_VC_IRQ_BTA) { complete(&dsi.bta_completion); + if (dsi.bta_callback) + dsi.bta_callback(); + } + if (vcstatus & DSI_VC_IRQ_ERROR_MASK) { DSSERR("DSI VC(%d) error, vc irqstatus %x\n", i, vcstatus); @@ -536,8 +550,12 @@ void dsi_irq_handler(void) /* flush posted write */ dsi_read_reg(DSI_COMPLEXIO_IRQ_STATUS); - DSSERR("DSI CIO error, cio irqstatus %x\n", ciostatus); - print_irq_status_cio(ciostatus); + if (ciostatus & DSI_CIO_IRQ_ERROR_MASK) { + DSSERR("DSI CIO error, cio irqstatus %x\n", ciostatus); + print_irq_status_cio(ciostatus); + } else if (debug_irq) { + print_irq_status_cio(ciostatus); + } } dsi_write_reg(DSI_IRQSTATUS, irqstatus & ~DSI_IRQ_CHANNEL_MASK); @@ -584,11 +602,8 @@ static void _dsi_initialize_irq(void) for (i = 0; i < 4; ++i) dsi_write_reg(DSI_VC_IRQENABLE(i), l); - /* XXX zonda responds incorrectly, causing control error: - Exit from LP-ESC mode to LP11 uses wrong transition states on the - data lines LP0 and LN0. */ - dsi_write_reg(DSI_COMPLEXIO_IRQ_ENABLE, - -1 & (~DSI_CIO_IRQ_ERRCONTROL2)); + l = DSI_CIO_IRQ_ERROR_MASK; + dsi_write_reg(DSI_COMPLEXIO_IRQ_ENABLE, l); } static u32 dsi_get_errors(void) @@ -1098,6 +1113,7 @@ int dsi_pll_init(struct omap_dss_device *dssdev, bool enable_hsclk, if (wait_for_bit_change(DSI_PLL_STATUS, 0, 1) != 1) { DSSERR("PLL not coming out of reset.\n"); r = -ENODEV; + dispc_pck_free_enable(0); goto err1; } @@ -1740,42 +1756,52 @@ static void dsi_vc_initial_config(int channel) dsi.vc[channel].mode = DSI_VC_MODE_L4; } -static void dsi_vc_config_l4(int channel) +static int dsi_vc_config_l4(int channel) { if (dsi.vc[channel].mode == DSI_VC_MODE_L4) - return; + return 0; DSSDBGF("%d", channel); dsi_vc_enable(channel, 0); - if (REG_GET(DSI_VC_CTRL(channel), 15, 15)) /* VC_BUSY */ + /* VC_BUSY */ + if (wait_for_bit_change(DSI_VC_CTRL(channel), 15, 0) != 0) { DSSERR("vc(%d) busy when trying to config for L4\n", channel); + return -EIO; + } REG_FLD_MOD(DSI_VC_CTRL(channel), 0, 1, 1); /* SOURCE, 0 = L4 */ dsi_vc_enable(channel, 1); dsi.vc[channel].mode = DSI_VC_MODE_L4; + + return 0; } -static void dsi_vc_config_vp(int channel) +static int dsi_vc_config_vp(int channel) { if (dsi.vc[channel].mode == DSI_VC_MODE_VP) - return; + return 0; DSSDBGF("%d", channel); dsi_vc_enable(channel, 0); - if (REG_GET(DSI_VC_CTRL(channel), 15, 15)) /* VC_BUSY */ + /* VC_BUSY */ + if (wait_for_bit_change(DSI_VC_CTRL(channel), 15, 0) != 0) { DSSERR("vc(%d) busy when trying to config for VP\n", channel); + return -EIO; + } REG_FLD_MOD(DSI_VC_CTRL(channel), 1, 1, 1); /* SOURCE, 1 = video port */ dsi_vc_enable(channel, 1); dsi.vc[channel].mode = DSI_VC_MODE_VP; + + return 0; } @@ -1854,19 +1880,19 @@ static u16 dsi_vc_flush_receive_data(int channel) u32 val; u8 dt; val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel)); - DSSDBG("\trawval %#08x\n", val); + DSSERR("\trawval %#08x\n", val); dt = FLD_GET(val, 5, 0); if (dt == DSI_DT_RX_ACK_WITH_ERR) { u16 err = FLD_GET(val, 23, 8); dsi_show_rx_ack_with_err(err); } else if (dt == DSI_DT_RX_SHORT_READ_1) { - DSSDBG("\tDCS short response, 1 byte: %#x\n", + DSSERR("\tDCS short response, 1 byte: %#x\n", FLD_GET(val, 23, 8)); } else if (dt == DSI_DT_RX_SHORT_READ_2) { - DSSDBG("\tDCS short response, 2 byte: %#x\n", + DSSERR("\tDCS short response, 2 byte: %#x\n", FLD_GET(val, 23, 8)); } else if (dt == DSI_DT_RX_DCS_LONG_READ) { - DSSDBG("\tDCS long response, len %d\n", + DSSERR("\tDCS long response, len %d\n", FLD_GET(val, 23, 8)); dsi_vc_flush_long_data(channel); } else { @@ -2087,6 +2113,13 @@ int dsi_vc_dcs_write(int channel, u8 *data, int len) if (r) goto err; + if (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { /* RX_FIFO_NOT_EMPTY */ + DSSERR("rx fifo not empty after write, dumping data:\n"); + dsi_vc_flush_receive_data(channel); + r = -EIO; + goto err; + } + return 0; err: DSSERR("dsi_vc_dcs_write(ch %d, cmd 0x%02x, len %d) failed\n", @@ -2233,11 +2266,12 @@ int dsi_vc_dcs_read_1(int channel, u8 dcs_cmd, u8 *data) } EXPORT_SYMBOL(dsi_vc_dcs_read_1); -int dsi_vc_dcs_read_2(int channel, u8 dcs_cmd, u16 *data) +int dsi_vc_dcs_read_2(int channel, u8 dcs_cmd, u8 *data1, u8 *data2) { + u8 buf[2]; int r; - r = dsi_vc_dcs_read(channel, dcs_cmd, (u8 *)data, 2); + r = dsi_vc_dcs_read(channel, dcs_cmd, buf, 2); if (r < 0) return r; @@ -2245,231 +2279,122 @@ int dsi_vc_dcs_read_2(int channel, u8 dcs_cmd, u16 *data) if (r != 2) return -EIO; + *data1 = buf[0]; + *data2 = buf[1]; + return 0; } EXPORT_SYMBOL(dsi_vc_dcs_read_2); int dsi_vc_set_max_rx_packet_size(int channel, u16 len) { - int r; - r = dsi_vc_send_short(channel, DSI_DT_SET_MAX_RET_PKG_SIZE, + return dsi_vc_send_short(channel, DSI_DT_SET_MAX_RET_PKG_SIZE, len, 0); - - if (r) - return r; - - r = dsi_vc_send_bta_sync(channel); - - return r; } EXPORT_SYMBOL(dsi_vc_set_max_rx_packet_size); -static void dsi_set_lp_rx_timeout(unsigned long ns) +static void dsi_set_lp_rx_timeout(unsigned ticks, bool x4, bool x16) { - u32 r; - unsigned x4, x16; unsigned long fck; - unsigned long ticks; + unsigned long total_ticks; + u32 r; - /* ticks in DSI_FCK */ + BUG_ON(ticks > 0x1fff); + /* ticks in DSI_FCK */ fck = dsi_fclk_rate(); - ticks = (fck / 1000 / 1000) * ns / 1000; - x4 = 0; - x16 = 0; - - if (ticks > 0x1fff) { - ticks = (fck / 1000 / 1000) * ns / 1000 / 4; - x4 = 1; - x16 = 0; - } - - if (ticks > 0x1fff) { - ticks = (fck / 1000 / 1000) * ns / 1000 / 16; - x4 = 0; - x16 = 1; - } - - if (ticks > 0x1fff) { - ticks = (fck / 1000 / 1000) * ns / 1000 / (4 * 16); - x4 = 1; - x16 = 1; - } - - if (ticks > 0x1fff) { - DSSWARN("LP_TX_TO over limit, setting it to max\n"); - ticks = 0x1fff; - x4 = 1; - x16 = 1; - } r = dsi_read_reg(DSI_TIMING2); r = FLD_MOD(r, 1, 15, 15); /* LP_RX_TO */ - r = FLD_MOD(r, x16, 14, 14); /* LP_RX_TO_X16 */ - r = FLD_MOD(r, x4, 13, 13); /* LP_RX_TO_X4 */ + r = FLD_MOD(r, x16 ? 1 : 0, 14, 14); /* LP_RX_TO_X16 */ + r = FLD_MOD(r, x4 ? 1 : 0, 13, 13); /* LP_RX_TO_X4 */ r = FLD_MOD(r, ticks, 12, 0); /* LP_RX_COUNTER */ dsi_write_reg(DSI_TIMING2, r); - DSSDBG("LP_RX_TO %lu ns (%#lx ticks%s%s)\n", - (ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) / - (fck / 1000 / 1000), - ticks, x4 ? " x4" : "", x16 ? " x16" : ""); + total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1); + + DSSDBG("LP_RX_TO %lu ticks (%#x%s%s) = %lu ns\n", + total_ticks, + ticks, x4 ? " x4" : "", x16 ? " x16" : "", + (total_ticks * 1000) / (fck / 1000 / 1000)); } -static void dsi_set_ta_timeout(unsigned long ns) +static void dsi_set_ta_timeout(unsigned ticks, bool x8, bool x16) { - u32 r; - unsigned x8, x16; unsigned long fck; - unsigned long ticks; + unsigned long total_ticks; + u32 r; + + BUG_ON(ticks > 0x1fff); /* ticks in DSI_FCK */ fck = dsi_fclk_rate(); - ticks = (fck / 1000 / 1000) * ns / 1000; - x8 = 0; - x16 = 0; - - if (ticks > 0x1fff) { - ticks = (fck / 1000 / 1000) * ns / 1000 / 8; - x8 = 1; - x16 = 0; - } - - if (ticks > 0x1fff) { - ticks = (fck / 1000 / 1000) * ns / 1000 / 16; - x8 = 0; - x16 = 1; - } - - if (ticks > 0x1fff) { - ticks = (fck / 1000 / 1000) * ns / 1000 / (8 * 16); - x8 = 1; - x16 = 1; - } - - if (ticks > 0x1fff) { - DSSWARN("TA_TO over limit, setting it to max\n"); - ticks = 0x1fff; - x8 = 1; - x16 = 1; - } r = dsi_read_reg(DSI_TIMING1); r = FLD_MOD(r, 1, 31, 31); /* TA_TO */ - r = FLD_MOD(r, x16, 30, 30); /* TA_TO_X16 */ - r = FLD_MOD(r, x8, 29, 29); /* TA_TO_X8 */ + r = FLD_MOD(r, x16 ? 1 : 0, 30, 30); /* TA_TO_X16 */ + r = FLD_MOD(r, x8 ? 1 : 0, 29, 29); /* TA_TO_X8 */ r = FLD_MOD(r, ticks, 28, 16); /* TA_TO_COUNTER */ dsi_write_reg(DSI_TIMING1, r); - DSSDBG("TA_TO %lu ns (%#lx ticks%s%s)\n", - (ticks * (x16 ? 16 : 1) * (x8 ? 8 : 1) * 1000) / - (fck / 1000 / 1000), - ticks, x8 ? " x8" : "", x16 ? " x16" : ""); + total_ticks = ticks * (x16 ? 16 : 1) * (x8 ? 8 : 1); + + DSSDBG("TA_TO %lu ticks (%#x%s%s) = %lu ns\n", + total_ticks, + ticks, x8 ? " x8" : "", x16 ? " x16" : "", + (total_ticks * 1000) / (fck / 1000 / 1000)); } -static void dsi_set_stop_state_counter(unsigned long ns) +static void dsi_set_stop_state_counter(unsigned ticks, bool x4, bool x16) { - u32 r; - unsigned x4, x16; unsigned long fck; - unsigned long ticks; + unsigned long total_ticks; + u32 r; - /* ticks in DSI_FCK */ + BUG_ON(ticks > 0x1fff); + /* ticks in DSI_FCK */ fck = dsi_fclk_rate(); - ticks = (fck / 1000 / 1000) * ns / 1000; - x4 = 0; - x16 = 0; - - if (ticks > 0x1fff) { - ticks = (fck / 1000 / 1000) * ns / 1000 / 4; - x4 = 1; - x16 = 0; - } - - if (ticks > 0x1fff) { - ticks = (fck / 1000 / 1000) * ns / 1000 / 16; - x4 = 0; - x16 = 1; - } - - if (ticks > 0x1fff) { - ticks = (fck / 1000 / 1000) * ns / 1000 / (4 * 16); - x4 = 1; - x16 = 1; - } - - if (ticks > 0x1fff) { - DSSWARN("STOP_STATE_COUNTER_IO over limit, " - "setting it to max\n"); - ticks = 0x1fff; - x4 = 1; - x16 = 1; - } r = dsi_read_reg(DSI_TIMING1); r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */ - r = FLD_MOD(r, x16, 14, 14); /* STOP_STATE_X16_IO */ - r = FLD_MOD(r, x4, 13, 13); /* STOP_STATE_X4_IO */ + r = FLD_MOD(r, x16 ? 1 : 0, 14, 14); /* STOP_STATE_X16_IO */ + r = FLD_MOD(r, x4 ? 1 : 0, 13, 13); /* STOP_STATE_X4_IO */ r = FLD_MOD(r, ticks, 12, 0); /* STOP_STATE_COUNTER_IO */ dsi_write_reg(DSI_TIMING1, r); - DSSDBG("STOP_STATE_COUNTER %lu ns (%#lx ticks%s%s)\n", - (ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) / - (fck / 1000 / 1000), - ticks, x4 ? " x4" : "", x16 ? " x16" : ""); + total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1); + + DSSDBG("STOP_STATE_COUNTER %lu ticks (%#x%s%s) = %lu ns\n", + total_ticks, + ticks, x4 ? " x4" : "", x16 ? " x16" : "", + (total_ticks * 1000) / (fck / 1000 / 1000)); } -static void dsi_set_hs_tx_timeout(unsigned long ns) +static void dsi_set_hs_tx_timeout(unsigned ticks, bool x4, bool x16) { - u32 r; - unsigned x4, x16; unsigned long fck; - unsigned long ticks; + unsigned long total_ticks; + u32 r; - /* ticks in TxByteClkHS */ + BUG_ON(ticks > 0x1fff); + /* ticks in TxByteClkHS */ fck = dsi_get_txbyteclkhs(); - ticks = (fck / 1000 / 1000) * ns / 1000; - x4 = 0; - x16 = 0; - - if (ticks > 0x1fff) { - ticks = (fck / 1000 / 1000) * ns / 1000 / 4; - x4 = 1; - x16 = 0; - } - - if (ticks > 0x1fff) { - ticks = (fck / 1000 / 1000) * ns / 1000 / 16; - x4 = 0; - x16 = 1; - } - - if (ticks > 0x1fff) { - ticks = (fck / 1000 / 1000) * ns / 1000 / (4 * 16); - x4 = 1; - x16 = 1; - } - - if (ticks > 0x1fff) { - DSSWARN("HS_TX_TO over limit, setting it to max\n"); - ticks = 0x1fff; - x4 = 1; - x16 = 1; - } r = dsi_read_reg(DSI_TIMING2); r = FLD_MOD(r, 1, 31, 31); /* HS_TX_TO */ - r = FLD_MOD(r, x16, 30, 30); /* HS_TX_TO_X16 */ - r = FLD_MOD(r, x4, 29, 29); /* HS_TX_TO_X8 (4 really) */ + r = FLD_MOD(r, x16 ? 1 : 0, 30, 30); /* HS_TX_TO_X16 */ + r = FLD_MOD(r, x4 ? 1 : 0, 29, 29); /* HS_TX_TO_X8 (4 really) */ r = FLD_MOD(r, ticks, 28, 16); /* HS_TX_TO_COUNTER */ dsi_write_reg(DSI_TIMING2, r); - DSSDBG("HS_TX_TO %lu ns (%#lx ticks%s%s)\n", - (ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) / - (fck / 1000 / 1000), - ticks, x4 ? " x4" : "", x16 ? " x16" : ""); + total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1); + + DSSDBG("HS_TX_TO %lu ticks (%#x%s%s) = %lu ns\n", + total_ticks, + ticks, x4 ? " x4" : "", x16 ? " x16" : "", + (total_ticks * 1000) / (fck / 1000 / 1000)); } static int dsi_proto_config(struct omap_dss_device *dssdev) { @@ -2487,10 +2412,10 @@ static int dsi_proto_config(struct omap_dss_device *dssdev) DSI_FIFO_SIZE_32); /* XXX what values for the timeouts? */ - dsi_set_stop_state_counter(1000); - dsi_set_ta_timeout(6400000); - dsi_set_lp_rx_timeout(48000); - dsi_set_hs_tx_timeout(1000000); + dsi_set_stop_state_counter(0x1000, false, false); + dsi_set_ta_timeout(0x1fff, true, true); + dsi_set_lp_rx_timeout(0x1fff, true, true); + dsi_set_hs_tx_timeout(0x1fff, true, true); switch (dssdev->ctrl.pixel_size) { case 16: @@ -2759,6 +2684,7 @@ static void dsi_update_screen_dispc(struct omap_dss_device *dssdev, unsigned packet_payload; unsigned packet_len; u32 l; + int r; const unsigned channel = dsi.update_channel; /* line buffer is 1024 x 24bits */ /* XXX: for some reason using full buffer size causes considerable TX @@ -2809,8 +2735,9 @@ static void dsi_update_screen_dispc(struct omap_dss_device *dssdev, dsi_perf_mark_start(); - schedule_delayed_work(&dsi.framedone_timeout_work, + r = queue_delayed_work(dsi.workqueue, &dsi.framedone_timeout_work, msecs_to_jiffies(250)); + BUG_ON(r == 0); dss_start_update(dssdev); @@ -2834,62 +2761,70 @@ static void dsi_te_timeout(unsigned long arg) } #endif -static void dsi_framedone_timeout_work_callback(struct work_struct *work) +static void dsi_handle_framedone(int error) { - int r; const int channel = dsi.update_channel; - DSSERR("Framedone not received for 250ms!\n"); + cancel_delayed_work(&dsi.framedone_timeout_work); + + dsi_vc_disable_bta_irq(channel); /* SIDLEMODE back to smart-idle */ dispc_enable_sidle(); + dsi.bta_callback = NULL; + if (dsi.te_enabled) { /* enable LP_RX_TO again after the TE */ REG_FLD_MOD(DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */ } - /* Send BTA after the frame. We need this for the TE to work, as TE - * trigger is only sent for BTAs without preceding packet. Thus we need - * to BTA after the pixel packets so that next BTA will cause TE - * trigger. - * - * This is not needed when TE is not in use, but we do it anyway to - * make sure that the transfer has been completed. It would be more - * optimal, but more complex, to wait only just before starting next - * transfer. */ - r = dsi_vc_send_bta_sync(channel); - if (r) - DSSERR("BTA after framedone failed\n"); - /* RX_FIFO_NOT_EMPTY */ if (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { DSSERR("Received error during frame transfer:\n"); dsi_vc_flush_receive_data(channel); + if (!error) + error = -EIO; } - dsi.framedone_callback(-ETIMEDOUT, dsi.framedone_data); + dsi.framedone_callback(error, dsi.framedone_data); + + if (!error) + dsi_perf_show("DISPC"); } -static void dsi_framedone_irq_callback(void *data, u32 mask) +static void dsi_framedone_timeout_work_callback(struct work_struct *work) { - /* Note: We get FRAMEDONE when DISPC has finished sending pixels and - * turns itself off. However, DSI still has the pixels in its buffers, - * and is sending the data. - */ + /* XXX While extremely unlikely, we could get FRAMEDONE interrupt after + * 250ms which would conflict with this timeout work. What should be + * done is first cancel the transfer on the HW, and then cancel the + * possibly scheduled framedone work. However, cancelling the transfer + * on the HW is buggy, and would probably require resetting the whole + * DSI */ - /* SIDLEMODE back to smart-idle */ - dispc_enable_sidle(); + DSSERR("Framedone not received for 250ms!\n"); - schedule_work(&dsi.framedone_work); + dsi_handle_framedone(-ETIMEDOUT); } -static void dsi_handle_framedone(void) +static void dsi_framedone_bta_callback(void) +{ + dsi_handle_framedone(0); + +#ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC + dispc_fake_vsync_irq(); +#endif +} + +static void dsi_framedone_irq_callback(void *data, u32 mask) { - int r; const int channel = dsi.update_channel; + int r; - DSSDBG("FRAMEDONE\n"); + /* Note: We get FRAMEDONE when DISPC has finished sending pixels and + * turns itself off. However, DSI still has the pixels in its buffers, + * and is sending the data. + */ if (dsi.te_enabled) { /* enable LP_RX_TO again after the TE */ @@ -2904,37 +2839,30 @@ static void dsi_handle_framedone(void) * This is not needed when TE is not in use, but we do it anyway to * make sure that the transfer has been completed. It would be more * optimal, but more complex, to wait only just before starting next - * transfer. */ - r = dsi_vc_send_bta_sync(channel); - if (r) - DSSERR("BTA after framedone failed\n"); - - /* RX_FIFO_NOT_EMPTY */ - if (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { - DSSERR("Received error during frame transfer:\n"); - dsi_vc_flush_receive_data(channel); - } - -#ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC - dispc_fake_vsync_irq(); -#endif -} - -static void dsi_framedone_work_callback(struct work_struct *work) -{ - DSSDBGF(); + * transfer. + * + * Also, as there's no interrupt telling when the transfer has been + * done and the channel could be reconfigured, the only way is to + * busyloop until TE_SIZE is zero. With BTA we can do this + * asynchronously. + * */ - cancel_delayed_work_sync(&dsi.framedone_timeout_work); + dsi.bta_callback = dsi_framedone_bta_callback; - dsi_handle_framedone(); + barrier(); - dsi_perf_show("DISPC"); + dsi_vc_enable_bta_irq(channel); - dsi.framedone_callback(0, dsi.framedone_data); + r = dsi_vc_send_bta(channel); + if (r) { + DSSERR("BTA after framedone failed\n"); + dsi_handle_framedone(-EIO); + } } int omap_dsi_prepare_update(struct omap_dss_device *dssdev, - u16 *x, u16 *y, u16 *w, u16 *h) + u16 *x, u16 *y, u16 *w, u16 *h, + bool enlarge_update_area) { u16 dw, dh; @@ -2958,7 +2886,8 @@ int omap_dsi_prepare_update(struct omap_dss_device *dssdev, dsi_perf_mark_setup(); if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) { - dss_setup_partial_planes(dssdev, x, y, w, h); + dss_setup_partial_planes(dssdev, x, y, w, h, + enlarge_update_area); dispc_set_lcd_size(*w, *h); } @@ -2973,6 +2902,12 @@ int omap_dsi_update(struct omap_dss_device *dssdev, { dsi.update_channel = channel; + /* OMAP DSS cannot send updates of odd widths. + * omap_dsi_prepare_update() makes the widths even, but add a BUG_ON + * here to make sure we catch erroneous updates. Otherwise we'll only + * 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; @@ -2985,7 +2920,12 @@ int omap_dsi_update(struct omap_dss_device *dssdev, dsi_update_screen_dispc(dssdev, x, y, w, h); } else { - dsi_update_screen_l4(dssdev, x, y, w, h); + int r; + + r = dsi_update_screen_l4(dssdev, x, y, w, h); + if (r) + return r; + dsi_perf_show("L4"); callback(0, data); } @@ -3048,8 +2988,10 @@ static int dsi_configure_dsi_clocks(struct omap_dss_device *dssdev) cinfo.regm3 = dssdev->phy.dsi.div.regm3; cinfo.regm4 = dssdev->phy.dsi.div.regm4; r = dsi_calc_clock_rates(&cinfo); - if (r) + if (r) { + DSSERR("Failed to calc dsi clocks\n"); return r; + } r = dsi_pll_set_clock_div(&cinfo); if (r) { @@ -3147,6 +3089,13 @@ err0: static void dsi_display_uninit_dsi(struct omap_dss_device *dssdev) { + /* disable interface */ + dsi_if_enable(0); + dsi_vc_enable(0, 0); + dsi_vc_enable(1, 0); + dsi_vc_enable(2, 0); + dsi_vc_enable(3, 0); + dss_select_dispc_clk_source(DSS_SRC_DSS1_ALWON_FCLK); dss_select_dsi_clk_source(DSS_SRC_DSS1_ALWON_FCLK); dsi_complexio_uninit(); @@ -3257,7 +3206,7 @@ void dsi_get_overlay_fifo_thresholds(enum omap_plane plane, burst_size_bytes = 16 * 32 / 8; *fifo_high = fifo_size - burst_size_bytes; - *fifo_low = fifo_size - burst_size_bytes * 8; + *fifo_low = fifo_size - burst_size_bytes * 2; } int dsi_init_display(struct omap_dss_device *dssdev) @@ -3274,6 +3223,18 @@ int dsi_init_display(struct omap_dss_device *dssdev) return 0; } +void dsi_wait_dsi1_pll_active(void) +{ + if (wait_for_bit_change(DSI_PLL_STATUS, 7, 1) != 1) + DSSERR("DSI1 PLL clock not active\n"); +} + +void dsi_wait_dsi2_pll_active(void) +{ + if (wait_for_bit_change(DSI_PLL_STATUS, 8, 1) != 1) + DSSERR("DSI2 PLL clock not active\n"); +} + int dsi_init(struct platform_device *pdev) { u32 rev; @@ -3292,7 +3253,10 @@ int dsi_init(struct platform_device *pdev) mutex_init(&dsi.lock); sema_init(&dsi.bus_lock, 1); - INIT_WORK(&dsi.framedone_work, dsi_framedone_work_callback); + dsi.workqueue = create_singlethread_workqueue("dsi"); + if (dsi.workqueue == NULL) + return -ENOMEM; + INIT_DELAYED_WORK_DEFERRABLE(&dsi.framedone_timeout_work, dsi_framedone_timeout_work_callback); @@ -3328,6 +3292,7 @@ int dsi_init(struct platform_device *pdev) err2: iounmap(dsi.base); err1: + destroy_workqueue(dsi.workqueue); return r; } @@ -3335,6 +3300,8 @@ void dsi_exit(void) { iounmap(dsi.base); + destroy_workqueue(dsi.workqueue); + DSSDBG("omap_dsi_exit\n"); } diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c index 24b18258654..77c3621c917 100644 --- a/drivers/video/omap2/dss/dss.c +++ b/drivers/video/omap2/dss/dss.c @@ -265,6 +265,9 @@ void dss_select_dispc_clk_source(enum dss_clk_source clk_src) b = clk_src == DSS_SRC_DSS1_ALWON_FCLK ? 0 : 1; + if (clk_src == DSS_SRC_DSI1_PLL_FCLK) + dsi_wait_dsi1_pll_active(); + REG_FLD_MOD(DSS_CONTROL, b, 0, 0); /* DISPC_CLK_SWITCH */ dss.dispc_clk_source = clk_src; @@ -279,6 +282,9 @@ void dss_select_dsi_clk_source(enum dss_clk_source clk_src) b = clk_src == DSS_SRC_DSS1_ALWON_FCLK ? 0 : 1; + if (clk_src == DSS_SRC_DSI2_PLL_FCLK) + dsi_wait_dsi2_pll_active(); + REG_FLD_MOD(DSS_CONTROL, b, 1, 1); /* DSI_CLK_SWITCH */ dss.dsi_clk_source = clk_src; diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h index 786f433fd57..5c7940d5f28 100644 --- a/drivers/video/omap2/dss/dss.h +++ b/drivers/video/omap2/dss/dss.h @@ -199,7 +199,8 @@ int dss_init_overlay_managers(struct platform_device *pdev); void dss_uninit_overlay_managers(struct platform_device *pdev); int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl); void dss_setup_partial_planes(struct omap_dss_device *dssdev, - u16 *x, u16 *y, u16 *w, u16 *h); + u16 *x, u16 *y, u16 *w, u16 *h, + bool enlarge_update_area); void dss_start_update(struct omap_dss_device *dssdev); /* overlay */ @@ -281,6 +282,8 @@ void dsi_pll_uninit(void); void dsi_get_overlay_fifo_thresholds(enum omap_plane plane, u32 fifo_size, enum omap_burst_size *burst_size, u32 *fifo_low, u32 *fifo_high); +void dsi_wait_dsi1_pll_active(void); +void dsi_wait_dsi2_pll_active(void); #else static inline int dsi_init(struct platform_device *pdev) { @@ -289,6 +292,12 @@ static inline int dsi_init(struct platform_device *pdev) static inline void dsi_exit(void) { } +static inline void dsi_wait_dsi1_pll_active(void) +{ +} +static inline void dsi_wait_dsi2_pll_active(void) +{ +} #endif /* DPI */ diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c index 9e1fbe531bf..6a649ab5539 100644 --- a/drivers/video/omap2/dss/manager.c +++ b/drivers/video/omap2/dss/manager.c @@ -440,6 +440,10 @@ struct manager_cache_data { /* manual update region */ u16 x, y, w, h; + + /* enlarge the update area if the update area contains scaled + * overlays */ + bool enlarge_update_area; }; static struct { @@ -525,7 +529,7 @@ static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr) int i; struct omap_dss_device *dssdev = mgr->device; - if (!dssdev) + if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) return 0; if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) { @@ -596,11 +600,14 @@ int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl) int r; int i; - if (!ovl->manager || !ovl->manager->device) + if (!ovl->manager) return 0; dssdev = ovl->manager->device; + if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + return 0; + if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) { irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN; channel = OMAP_DSS_CHANNEL_DIGIT; @@ -718,6 +725,7 @@ static int configure_overlay(enum omap_plane plane) u16 x, y, w, h; u32 paddr; int r; + u16 orig_w, orig_h, orig_outw, orig_outh; DSSDBGF("%d", plane); @@ -738,8 +746,16 @@ static int configure_overlay(enum omap_plane plane) outh = c->out_height == 0 ? c->height : c->out_height; paddr = c->paddr; + orig_w = w; + orig_h = h; + orig_outw = outw; + orig_outh = outh; + if (c->manual_update && mc->do_manual_update) { unsigned bpp; + unsigned scale_x_m = w, scale_x_d = outw; + unsigned scale_y_m = h, scale_y_d = outh; + /* 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)) { @@ -770,38 +786,47 @@ static int configure_overlay(enum omap_plane plane) BUG(); } - if (dispc_is_overlay_scaled(c)) { - /* If the overlay is scaled, the update area has - * already been enlarged to cover the whole overlay. We - * only need to adjust x/y here */ - x = c->pos_x - mc->x; - y = c->pos_y - mc->y; + if (mc->x > c->pos_x) { + x = 0; + outw -= (mc->x - c->pos_x); + paddr += (mc->x - c->pos_x) * + scale_x_m / scale_x_d * bpp / 8; } else { - if (mc->x > c->pos_x) { - x = 0; - w -= (mc->x - c->pos_x); - paddr += (mc->x - c->pos_x) * bpp / 8; - } else { - x = c->pos_x - mc->x; - } - - if (mc->y > c->pos_y) { - y = 0; - h -= (mc->y - c->pos_y); - paddr += (mc->y - c->pos_y) * c->screen_width * - bpp / 8; - } else { - y = c->pos_y - mc->y; - } - - if (mc->w < (x+w)) - w -= (x+w) - (mc->w); + x = c->pos_x - mc->x; + } - if (mc->h < (y+h)) - h -= (y+h) - (mc->h); + if (mc->y > c->pos_y) { + y = 0; + outh -= (mc->y - c->pos_y); + paddr += (mc->y - c->pos_y) * + scale_y_m / scale_y_d * + c->screen_width * bpp / 8; + } else { + y = c->pos_y - mc->y; + } - outw = w; - outh = h; + if (mc->w < (x + outw)) + outw -= (x + outw) - (mc->w); + + if (mc->h < (y + outh)) + outh -= (y + outh) - (mc->h); + + w = w * outw / orig_outw; + h = h * outh / orig_outh; + + /* YUV mode overlay's input width has to be even and the + * algorithm above may adjust the width to be odd. + * + * Here we adjust the width if needed, preferring to increase + * the width if the original width was bigger. + */ + if ((w & 1) && + (c->color_mode == OMAP_DSS_COLOR_YUV2 || + c->color_mode == OMAP_DSS_COLOR_UYVY)) { + if (orig_w > w) + w += 1; + else + w -= 1; } } @@ -960,7 +985,7 @@ static void make_even(u16 *x, u16 *w) /* Configure dispc for partial update. Return possibly modified update * area */ void dss_setup_partial_planes(struct omap_dss_device *dssdev, - u16 *xi, u16 *yi, u16 *wi, u16 *hi) + u16 *xi, u16 *yi, u16 *wi, u16 *hi, bool enlarge_update_area) { struct overlay_cache_data *oc; struct manager_cache_data *mc; @@ -969,6 +994,7 @@ void dss_setup_partial_planes(struct omap_dss_device *dssdev, int i; u16 x, y, w, h; unsigned long flags; + bool area_changed; x = *xi; y = *yi; @@ -989,73 +1015,91 @@ void dss_setup_partial_planes(struct omap_dss_device *dssdev, spin_lock_irqsave(&dss_cache.lock, flags); - /* We need to show the whole overlay if it is scaled. So look for - * those, and make the update area larger if found. - * Also mark the overlay cache dirty */ - for (i = 0; i < num_ovls; ++i) { - unsigned x1, y1, x2, y2; - unsigned outw, outh; + /* + * Execute the outer loop until the inner loop has completed + * once without increasing the update area. This will ensure that + * all scaled overlays end up completely within the update area. + */ + do { + area_changed = false; - oc = &dss_cache.overlay_cache[i]; + /* We need to show the whole overlay if it is scaled. So look + * for those, and make the update area larger if found. + * Also mark the overlay cache dirty */ + for (i = 0; i < num_ovls; ++i) { + unsigned x1, y1, x2, y2; + unsigned outw, outh; - if (oc->channel != mgr->id) - continue; + oc = &dss_cache.overlay_cache[i]; - oc->dirty = true; + if (oc->channel != mgr->id) + continue; - if (!oc->enabled) - continue; + oc->dirty = true; - if (!dispc_is_overlay_scaled(oc)) - continue; + if (!enlarge_update_area) + continue; - outw = oc->out_width == 0 ? oc->width : oc->out_width; - outh = oc->out_height == 0 ? oc->height : oc->out_height; + if (!oc->enabled) + continue; - /* is the overlay outside the update region? */ - if (!rectangle_intersects(x, y, w, h, - oc->pos_x, oc->pos_y, - outw, outh)) - continue; + if (!dispc_is_overlay_scaled(oc)) + continue; - /* if the overlay totally inside the update region? */ - if (rectangle_subset(oc->pos_x, oc->pos_y, outw, outh, - x, y, w, h)) - continue; + outw = oc->out_width == 0 ? + oc->width : oc->out_width; + outh = oc->out_height == 0 ? + oc->height : oc->out_height; + + /* is the overlay outside the update region? */ + if (!rectangle_intersects(x, y, w, h, + oc->pos_x, oc->pos_y, + outw, outh)) + continue; - if (x > oc->pos_x) - x1 = oc->pos_x; - else - x1 = x; + /* if the overlay totally inside the update region? */ + if (rectangle_subset(oc->pos_x, oc->pos_y, outw, outh, + x, y, w, h)) + continue; - if (y > oc->pos_y) - y1 = oc->pos_y; - else - y1 = y; + if (x > oc->pos_x) + x1 = oc->pos_x; + else + x1 = x; - if ((x + w) < (oc->pos_x + outw)) - x2 = oc->pos_x + outw; - else - x2 = x + w; + if (y > oc->pos_y) + y1 = oc->pos_y; + else + y1 = y; - if ((y + h) < (oc->pos_y + outh)) - y2 = oc->pos_y + outh; - else - y2 = y + h; + if ((x + w) < (oc->pos_x + outw)) + x2 = oc->pos_x + outw; + else + x2 = x + w; - x = x1; - y = y1; - w = x2 - x1; - h = y2 - y1; + if ((y + h) < (oc->pos_y + outh)) + y2 = oc->pos_y + outh; + else + y2 = y + h; - make_even(&x, &w); + x = x1; + y = y1; + w = x2 - x1; + h = y2 - y1; - DSSDBG("changing upd area due to ovl(%d) scaling %d,%d %dx%d\n", + make_even(&x, &w); + + DSSDBG("changing upd area due to ovl(%d) " + "scaling %d,%d %dx%d\n", i, x, y, w, h); - } + + area_changed = true; + } + } while (area_changed); mc = &dss_cache.manager_cache[mgr->id]; mc->do_manual_update = true; + mc->enlarge_update_area = enlarge_update_area; mc->x = x; mc->y = y; mc->w = w; diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c index 82336583ade..244dca81a39 100644 --- a/drivers/video/omap2/dss/overlay.c +++ b/drivers/video/omap2/dss/overlay.c @@ -65,7 +65,7 @@ static ssize_t overlay_manager_store(struct omap_overlay *ovl, const char *buf, for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { mgr = omap_dss_get_overlay_manager(i); - if (strncmp(buf, mgr->name, len) == 0) + if (sysfs_streq(buf, mgr->name)) break; mgr = NULL; diff --git a/drivers/video/omap2/dss/rfbi.c b/drivers/video/omap2/dss/rfbi.c index cc23f53cc62..bbe62464e92 100644 --- a/drivers/video/omap2/dss/rfbi.c +++ b/drivers/video/omap2/dss/rfbi.c @@ -886,7 +886,7 @@ int omap_rfbi_prepare_update(struct omap_dss_device *dssdev, return -EINVAL; if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) { - dss_setup_partial_planes(dssdev, x, y, w, h); + dss_setup_partial_planes(dssdev, x, y, w, h, true); dispc_set_lcd_size(*w, *h); } diff --git a/drivers/video/omap2/omapfb/omapfb-ioctl.c b/drivers/video/omap2/omapfb/omapfb-ioctl.c index 9c7361871d7..6f435450987 100644 --- a/drivers/video/omap2/omapfb/omapfb-ioctl.c +++ b/drivers/video/omap2/omapfb/omapfb-ioctl.c @@ -34,12 +34,37 @@ #include "omapfb.h" +static u8 get_mem_idx(struct omapfb_info *ofbi) +{ + if (ofbi->id == ofbi->region->id) + return 0; + + return OMAPFB_MEM_IDX_ENABLED | ofbi->region->id; +} + +static struct omapfb2_mem_region *get_mem_region(struct omapfb_info *ofbi, + u8 mem_idx) +{ + struct omapfb2_device *fbdev = ofbi->fbdev; + + if (mem_idx & OMAPFB_MEM_IDX_ENABLED) + mem_idx &= OMAPFB_MEM_IDX_MASK; + else + mem_idx = ofbi->id; + + if (mem_idx >= fbdev->num_fbs) + return NULL; + + return &fbdev->regions[mem_idx]; +} + static int omapfb_setup_plane(struct fb_info *fbi, struct omapfb_plane_info *pi) { struct omapfb_info *ofbi = FB2OFB(fbi); struct omapfb2_device *fbdev = ofbi->fbdev; struct omap_overlay *ovl; - struct omap_overlay_info info; + struct omap_overlay_info old_info; + struct omapfb2_mem_region *old_rg, *new_rg; int r = 0; DBG("omapfb_setup_plane\n"); @@ -52,36 +77,106 @@ static int omapfb_setup_plane(struct fb_info *fbi, struct omapfb_plane_info *pi) /* XXX uses only the first overlay */ ovl = ofbi->overlays[0]; - if (pi->enabled && !ofbi->region.size) { + old_rg = ofbi->region; + new_rg = get_mem_region(ofbi, pi->mem_idx); + if (!new_rg) { + r = -EINVAL; + goto out; + } + + /* Take the locks in a specific order to keep lockdep happy */ + if (old_rg->id < new_rg->id) { + omapfb_get_mem_region(old_rg); + omapfb_get_mem_region(new_rg); + } else if (new_rg->id < old_rg->id) { + omapfb_get_mem_region(new_rg); + omapfb_get_mem_region(old_rg); + } else + omapfb_get_mem_region(old_rg); + + if (pi->enabled && !new_rg->size) { /* * This plane's memory was freed, can't enable it * until it's reallocated. */ r = -EINVAL; - goto out; + goto put_mem; } - ovl->get_overlay_info(ovl, &info); + ovl->get_overlay_info(ovl, &old_info); - info.pos_x = pi->pos_x; - info.pos_y = pi->pos_y; - info.out_width = pi->out_width; - info.out_height = pi->out_height; - info.enabled = pi->enabled; + if (old_rg != new_rg) { + ofbi->region = new_rg; + set_fb_fix(fbi); + } - r = ovl->set_overlay_info(ovl, &info); - if (r) - goto out; + if (pi->enabled) { + struct omap_overlay_info info; - if (ovl->manager) { - r = ovl->manager->apply(ovl->manager); + r = omapfb_setup_overlay(fbi, ovl, pi->pos_x, pi->pos_y, + pi->out_width, pi->out_height); if (r) - goto out; + goto undo; + + ovl->get_overlay_info(ovl, &info); + + if (!info.enabled) { + info.enabled = pi->enabled; + r = ovl->set_overlay_info(ovl, &info); + if (r) + goto undo; + } + } else { + struct omap_overlay_info info; + + ovl->get_overlay_info(ovl, &info); + + info.enabled = pi->enabled; + info.pos_x = pi->pos_x; + info.pos_y = pi->pos_y; + info.out_width = pi->out_width; + info.out_height = pi->out_height; + + r = ovl->set_overlay_info(ovl, &info); + if (r) + goto undo; } -out: - if (r) - dev_err(fbdev->dev, "setup_plane failed\n"); + if (ovl->manager) + ovl->manager->apply(ovl->manager); + + /* Release the locks in a specific order to keep lockdep happy */ + if (old_rg->id > new_rg->id) { + omapfb_put_mem_region(old_rg); + omapfb_put_mem_region(new_rg); + } else if (new_rg->id > old_rg->id) { + omapfb_put_mem_region(new_rg); + omapfb_put_mem_region(old_rg); + } else + omapfb_put_mem_region(old_rg); + + return 0; + + undo: + if (old_rg != new_rg) { + ofbi->region = old_rg; + set_fb_fix(fbi); + } + + ovl->set_overlay_info(ovl, &old_info); + put_mem: + /* Release the locks in a specific order to keep lockdep happy */ + if (old_rg->id > new_rg->id) { + omapfb_put_mem_region(old_rg); + omapfb_put_mem_region(new_rg); + } else if (new_rg->id > old_rg->id) { + omapfb_put_mem_region(new_rg); + omapfb_put_mem_region(old_rg); + } else + omapfb_put_mem_region(old_rg); + out: + dev_err(fbdev->dev, "setup_plane failed\n"); + return r; } @@ -92,8 +187,8 @@ static int omapfb_query_plane(struct fb_info *fbi, struct omapfb_plane_info *pi) if (ofbi->num_overlays != 1) { memset(pi, 0, sizeof(*pi)); } else { - struct omap_overlay_info *ovli; struct omap_overlay *ovl; + struct omap_overlay_info *ovli; ovl = ofbi->overlays[0]; ovli = &ovl->info; @@ -103,6 +198,7 @@ static int omapfb_query_plane(struct fb_info *fbi, struct omapfb_plane_info *pi) pi->enabled = ovli->enabled; pi->channel_out = 0; /* xxx */ pi->mirror = 0; + pi->mem_idx = get_mem_idx(ofbi); pi->out_width = ovli->out_width; pi->out_height = ovli->out_height; } @@ -115,7 +211,7 @@ static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) struct omapfb_info *ofbi = FB2OFB(fbi); struct omapfb2_device *fbdev = ofbi->fbdev; struct omapfb2_mem_region *rg; - int r, i; + int r = 0, i; size_t size; if (mi->type > OMAPFB_MEMTYPE_MAX) @@ -123,22 +219,44 @@ static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) size = PAGE_ALIGN(mi->size); - rg = &ofbi->region; + rg = ofbi->region; - for (i = 0; i < ofbi->num_overlays; i++) { - if (ofbi->overlays[i]->info.enabled) - return -EBUSY; + down_write_nested(&rg->lock, rg->id); + atomic_inc(&rg->lock_count); + + if (atomic_read(&rg->map_count)) { + r = -EBUSY; + goto out; + } + + for (i = 0; i < fbdev->num_fbs; i++) { + struct omapfb_info *ofbi2 = FB2OFB(fbdev->fbs[i]); + int j; + + if (ofbi2->region != rg) + continue; + + for (j = 0; j < ofbi2->num_overlays; j++) { + if (ofbi2->overlays[j]->info.enabled) { + r = -EBUSY; + goto out; + } + } } if (rg->size != size || rg->type != mi->type) { r = omapfb_realloc_fbmem(fbi, size, mi->type); if (r) { dev_err(fbdev->dev, "realloc fbmem failed\n"); - return r; + goto out; } } - return 0; + out: + atomic_dec(&rg->lock_count); + up_write(&rg->lock); + + return r; } static int omapfb_query_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) @@ -146,12 +264,14 @@ static int omapfb_query_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) struct omapfb_info *ofbi = FB2OFB(fbi); struct omapfb2_mem_region *rg; - rg = &ofbi->region; + rg = omapfb_get_mem_region(ofbi->region); memset(mi, 0, sizeof(*mi)); mi->size = rg->size; mi->type = rg->type; + omapfb_put_mem_region(rg); + return 0; } @@ -490,6 +610,7 @@ int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg) struct omapfb_vram_info vram_info; struct omapfb_tearsync_info tearsync_info; struct omapfb_display_info display_info; + u32 crt; } p; int r = 0; @@ -648,6 +769,17 @@ int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg) r = -EFAULT; break; + case FBIO_WAITFORVSYNC: + if (get_user(p.crt, (__u32 __user *)arg)) { + r = -EFAULT; + break; + } + if (p.crt != 0) { + r = -ENODEV; + break; + } + /* FALLTHROUGH */ + case OMAPFB_WAITFORVSYNC: DBG("ioctl WAITFORVSYNC\n"); if (!display) { @@ -738,7 +870,7 @@ int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg) break; } - if (!display->driver->enable_te) { + if (!display || !display->driver->enable_te) { r = -ENODEV; break; } diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c index 4b4506da96d..04034d410d6 100644 --- a/drivers/video/omap2/omapfb/omapfb-main.c +++ b/drivers/video/omap2/omapfb/omapfb-main.c @@ -157,7 +157,7 @@ static void fill_fb(struct fb_info *fbi) static unsigned omapfb_get_vrfb_offset(const struct omapfb_info *ofbi, int rot) { - const struct vrfb *vrfb = &ofbi->region.vrfb; + const struct vrfb *vrfb = &ofbi->region->vrfb; unsigned offset; switch (rot) { @@ -185,27 +185,27 @@ static unsigned omapfb_get_vrfb_offset(const struct omapfb_info *ofbi, int rot) static u32 omapfb_get_region_rot_paddr(const struct omapfb_info *ofbi, int rot) { if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { - return ofbi->region.vrfb.paddr[rot] + return ofbi->region->vrfb.paddr[rot] + omapfb_get_vrfb_offset(ofbi, rot); } else { - return ofbi->region.paddr; + return ofbi->region->paddr; } } static u32 omapfb_get_region_paddr(const struct omapfb_info *ofbi) { if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) - return ofbi->region.vrfb.paddr[0]; + return ofbi->region->vrfb.paddr[0]; else - return ofbi->region.paddr; + return ofbi->region->paddr; } static void __iomem *omapfb_get_region_vaddr(const struct omapfb_info *ofbi) { if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) - return ofbi->region.vrfb.vaddr[0]; + return ofbi->region->vrfb.vaddr[0]; else - return ofbi->region.vaddr; + return ofbi->region->vaddr; } static struct omapfb_colormode omapfb_colormodes[] = { @@ -450,7 +450,7 @@ static int check_vrfb_fb_size(unsigned long region_size, static int check_fb_size(const struct omapfb_info *ofbi, struct fb_var_screeninfo *var) { - unsigned long max_frame_size = ofbi->region.size; + unsigned long max_frame_size = ofbi->region->size; int bytespp = var->bits_per_pixel >> 3; unsigned long line_size = var->xres_virtual * bytespp; @@ -497,7 +497,7 @@ static int check_fb_size(const struct omapfb_info *ofbi, static int setup_vrfb_rotation(struct fb_info *fbi) { struct omapfb_info *ofbi = FB2OFB(fbi); - struct omapfb2_mem_region *rg = &ofbi->region; + struct omapfb2_mem_region *rg = ofbi->region; struct vrfb *vrfb = &rg->vrfb; struct fb_var_screeninfo *var = &fbi->var; struct fb_fix_screeninfo *fix = &fbi->fix; @@ -558,9 +558,9 @@ static int setup_vrfb_rotation(struct fb_info *fbi) return r; /* used by open/write in fbmem.c */ - fbi->screen_base = ofbi->region.vrfb.vaddr[0]; + fbi->screen_base = ofbi->region->vrfb.vaddr[0]; - fix->smem_start = ofbi->region.vrfb.paddr[0]; + fix->smem_start = ofbi->region->vrfb.paddr[0]; switch (var->nonstd) { case OMAPFB_COLOR_YUV422: @@ -599,7 +599,7 @@ void set_fb_fix(struct fb_info *fbi) struct fb_fix_screeninfo *fix = &fbi->fix; struct fb_var_screeninfo *var = &fbi->var; struct omapfb_info *ofbi = FB2OFB(fbi); - struct omapfb2_mem_region *rg = &ofbi->region; + struct omapfb2_mem_region *rg = ofbi->region; DBG("set_fb_fix\n"); @@ -668,8 +668,7 @@ int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var) DBG("check_fb_var %d\n", ofbi->id); - if (ofbi->region.size == 0) - return 0; + WARN_ON(!atomic_read(&ofbi->region->lock_count)); r = fb_mode_to_dss_mode(var, &mode); if (r) { @@ -684,13 +683,14 @@ int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var) } } - if (var->rotate < 0 || var->rotate > 3) + if (var->rotate > 3) return -EINVAL; if (check_fb_res_bounds(var)) return -EINVAL; - if (check_fb_size(ofbi, var)) + /* When no memory is allocated ignore the size check */ + if (ofbi->region->size != 0 && check_fb_size(ofbi, var)) return -EINVAL; if (var->xres + var->xoffset > var->xres_virtual) @@ -822,9 +822,43 @@ static unsigned calc_rotation_offset_vrfb(const struct fb_var_screeninfo *var, return offset; } +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) +{ + u32 data_start_p; + void __iomem *data_start_v; + int offset; + + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { + data_start_p = omapfb_get_region_rot_paddr(ofbi, rotation); + data_start_v = NULL; + } 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); + else + 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); + + *paddr = data_start_p; + *vaddr = data_start_v; +} /* setup overlay according to the fb */ -static int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl, +int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl, u16 posx, u16 posy, u16 outw, u16 outh) { int r = 0; @@ -832,9 +866,8 @@ static int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl, struct fb_var_screeninfo *var = &fbi->var; struct fb_fix_screeninfo *fix = &fbi->fix; enum omap_color_mode mode = 0; - int offset; - u32 data_start_p; - void __iomem *data_start_v; + u32 data_start_p = 0; + void __iomem *data_start_v = NULL; struct omap_overlay_info info; int xres, yres; int screen_width; @@ -842,6 +875,8 @@ static int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl, int rotation = var->rotate; int i; + WARN_ON(!atomic_read(&ofbi->region->lock_count)); + for (i = 0; i < ofbi->num_overlays; i++) { if (ovl != ofbi->overlays[i]) continue; @@ -861,28 +896,9 @@ static int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl, yres = var->yres; } - - if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { - data_start_p = omapfb_get_region_rot_paddr(ofbi, rotation); - data_start_v = NULL; - } 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); - else - 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); + if (ofbi->region->size) + omapfb_calc_addr(ofbi, var, fix, rotation, + &data_start_p, &data_start_v); r = fb_mode_to_dss_mode(var, &mode); if (r) { @@ -954,12 +970,14 @@ int omapfb_apply_changes(struct fb_info *fbi, int init) fill_fb(fbi); #endif + WARN_ON(!atomic_read(&ofbi->region->lock_count)); + for (i = 0; i < ofbi->num_overlays; i++) { ovl = ofbi->overlays[i]; DBG("apply_changes, fb %d, ovl %d\n", ofbi->id, ovl->id); - if (ofbi->region.size == 0) { + if (ofbi->region->size == 0) { /* the fb is not available. disable the overlay */ omapfb_overlay_enable(ovl, 0); if (!init && ovl->manager) @@ -1007,36 +1025,48 @@ err: * DO NOT MODIFY PAR */ static int omapfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi) { + struct omapfb_info *ofbi = FB2OFB(fbi); int r; DBG("check_var(%d)\n", FB2OFB(fbi)->id); + omapfb_get_mem_region(ofbi->region); + r = check_fb_var(fbi, var); + omapfb_put_mem_region(ofbi->region); + return r; } /* set the video mode according to info->var */ static int omapfb_set_par(struct fb_info *fbi) { + struct omapfb_info *ofbi = FB2OFB(fbi); int r; DBG("set_par(%d)\n", FB2OFB(fbi)->id); + omapfb_get_mem_region(ofbi->region); + set_fb_fix(fbi); r = setup_vrfb_rotation(fbi); if (r) - return r; + goto out; r = omapfb_apply_changes(fbi, 0); + out: + omapfb_put_mem_region(ofbi->region); + return r; } static int omapfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *fbi) { + struct omapfb_info *ofbi = FB2OFB(fbi); struct fb_var_screeninfo new_var; int r; @@ -1052,23 +1082,31 @@ static int omapfb_pan_display(struct fb_var_screeninfo *var, fbi->var = new_var; + omapfb_get_mem_region(ofbi->region); + r = omapfb_apply_changes(fbi, 0); + omapfb_put_mem_region(ofbi->region); + return r; } static void mmap_user_open(struct vm_area_struct *vma) { - struct omapfb_info *ofbi = (struct omapfb_info *)vma->vm_private_data; + struct omapfb2_mem_region *rg = vma->vm_private_data; - atomic_inc(&ofbi->map_count); + omapfb_get_mem_region(rg); + atomic_inc(&rg->map_count); + omapfb_put_mem_region(rg); } static void mmap_user_close(struct vm_area_struct *vma) { - struct omapfb_info *ofbi = (struct omapfb_info *)vma->vm_private_data; + struct omapfb2_mem_region *rg = vma->vm_private_data; - atomic_dec(&ofbi->map_count); + omapfb_get_mem_region(rg); + atomic_dec(&rg->map_count); + omapfb_put_mem_region(rg); } static struct vm_operations_struct mmap_user_ops = { @@ -1080,9 +1118,11 @@ static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma) { struct omapfb_info *ofbi = FB2OFB(fbi); struct fb_fix_screeninfo *fix = &fbi->fix; + struct omapfb2_mem_region *rg; unsigned long off; unsigned long start; u32 len; + int r = -EINVAL; if (vma->vm_end - vma->vm_start == 0) return 0; @@ -1090,12 +1130,14 @@ static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma) return -EINVAL; off = vma->vm_pgoff << PAGE_SHIFT; + rg = omapfb_get_mem_region(ofbi->region); + start = omapfb_get_region_paddr(ofbi); len = fix->smem_len; if (off >= len) - return -EINVAL; + goto error; if ((vma->vm_end - vma->vm_start + off) > len) - return -EINVAL; + goto error; off += start; @@ -1105,13 +1147,25 @@ static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma) vma->vm_flags |= VM_IO | VM_RESERVED; vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); vma->vm_ops = &mmap_user_ops; - vma->vm_private_data = ofbi; + vma->vm_private_data = rg; if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, - vma->vm_end - vma->vm_start, vma->vm_page_prot)) - return -EAGAIN; + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) { + r = -EAGAIN; + goto error; + } + /* vm_ops.open won't be called for mmap itself. */ - atomic_inc(&ofbi->map_count); + atomic_inc(&rg->map_count); + + omapfb_put_mem_region(rg); + return 0; + + error: + omapfb_put_mem_region(ofbi->region); + + return r; } /* Store a single color palette entry into a pseudo palette or the hardware @@ -1154,11 +1208,6 @@ static int _setcolreg(struct fb_info *fbi, u_int regno, u_int red, u_int green, if (r != 0) break; - if (regno < 0) { - r = -EINVAL; - break; - } - if (regno < 16) { u16 pal; pal = ((red >> (16 - var->red.length)) << @@ -1217,6 +1266,9 @@ static int omapfb_blank(int blank, struct fb_info *fbi) int do_update = 0; int r = 0; + if (!display) + return -EINVAL; + omapfb_lock(fbdev); switch (blank) { @@ -1300,7 +1352,9 @@ static void omapfb_free_fbmem(struct fb_info *fbi) struct omapfb2_device *fbdev = ofbi->fbdev; struct omapfb2_mem_region *rg; - rg = &ofbi->region; + rg = ofbi->region; + + WARN_ON(atomic_read(&rg->map_count)); if (rg->paddr) if (omap_vram_free(rg->paddr, rg->size)) @@ -1355,8 +1409,15 @@ static int omapfb_alloc_fbmem(struct fb_info *fbi, unsigned long size, void __iomem *vaddr; int r; - rg = &ofbi->region; - memset(rg, 0, sizeof(*rg)); + rg = ofbi->region; + + rg->paddr = 0; + rg->vaddr = NULL; + memset(&rg->vrfb, 0, sizeof rg->vrfb); + rg->size = 0; + rg->type = 0; + rg->alloc = false; + rg->map = false; size = PAGE_ALIGN(size); @@ -1609,7 +1670,7 @@ static int omapfb_allocate_all_fbs(struct omapfb2_device *fbdev) for (i = 0; i < fbdev->num_fbs; i++) { struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]); struct omapfb2_mem_region *rg; - rg = &ofbi->region; + rg = ofbi->region; DBG("region%d phys %08x virt %p size=%lu\n", i, @@ -1626,7 +1687,7 @@ int omapfb_realloc_fbmem(struct fb_info *fbi, unsigned long size, int type) struct omapfb_info *ofbi = FB2OFB(fbi); struct omapfb2_device *fbdev = ofbi->fbdev; struct omap_dss_device *display = fb2display(fbi); - struct omapfb2_mem_region *rg = &ofbi->region; + struct omapfb2_mem_region *rg = ofbi->region; unsigned long old_size = rg->size; unsigned long old_paddr = rg->paddr; int old_type = rg->type; @@ -1709,7 +1770,7 @@ static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi) fbi->flags = FBINFO_FLAG_DEFAULT; fbi->pseudo_palette = fbdev->pseudo_palette; - if (ofbi->region.size == 0) { + if (ofbi->region->size == 0) { clear_fb_info(fbi); return 0; } @@ -1871,6 +1932,10 @@ static int omapfb_create_framebuffers(struct omapfb2_device *fbdev) ofbi->fbdev = fbdev; ofbi->id = i; + ofbi->region = &fbdev->regions[i]; + ofbi->region->id = i; + init_rwsem(&ofbi->region->lock); + /* assign these early, so that fb alloc can use them */ ofbi->rotation_type = def_vrfb ? OMAP_DSS_ROT_VRFB : OMAP_DSS_ROT_DMA; @@ -1900,7 +1965,13 @@ static int omapfb_create_framebuffers(struct omapfb2_device *fbdev) /* setup fb_infos */ for (i = 0; i < fbdev->num_fbs; i++) { - r = omapfb_fb_init(fbdev, fbdev->fbs[i]); + struct fb_info *fbi = fbdev->fbs[i]; + struct omapfb_info *ofbi = FB2OFB(fbi); + + omapfb_get_mem_region(ofbi->region); + r = omapfb_fb_init(fbdev, fbi); + omapfb_put_mem_region(ofbi->region); + if (r) { dev_err(fbdev->dev, "failed to setup fb_info\n"); return r; @@ -1921,20 +1992,19 @@ static int omapfb_create_framebuffers(struct omapfb2_device *fbdev) DBG("framebuffers registered\n"); for (i = 0; i < fbdev->num_fbs; i++) { - r = omapfb_apply_changes(fbdev->fbs[i], 1); + struct fb_info *fbi = fbdev->fbs[i]; + struct omapfb_info *ofbi = FB2OFB(fbi); + + omapfb_get_mem_region(ofbi->region); + r = omapfb_apply_changes(fbi, 1); + omapfb_put_mem_region(ofbi->region); + if (r) { dev_err(fbdev->dev, "failed to change mode\n"); return r; } } - DBG("create sysfs for fbs\n"); - r = omapfb_create_sysfs(fbdev); - if (r) { - dev_err(fbdev->dev, "failed to create sysfs entries\n"); - return r; - } - /* Enable fb0 */ if (fbdev->num_fbs > 0) { struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[0]); @@ -1968,11 +2038,11 @@ static int omapfb_mode_to_timings(const char *mode_str, #ifdef CONFIG_OMAP2_DSS_VENC if (strcmp(mode_str, "pal") == 0) { *timings = omap_dss_pal_timings; - *bpp = 0; + *bpp = 24; return 0; } else if (strcmp(mode_str, "ntsc") == 0) { *timings = omap_dss_ntsc_timings; - *bpp = 0; + *bpp = 24; return 0; } #endif @@ -2220,6 +2290,13 @@ static int omapfb_probe(struct platform_device *pdev) } } + DBG("create sysfs for fbs\n"); + r = omapfb_create_sysfs(fbdev); + if (r) { + dev_err(fbdev->dev, "failed to create sysfs entries\n"); + goto cleanup; + } + return 0; cleanup: diff --git a/drivers/video/omap2/omapfb/omapfb-sysfs.c b/drivers/video/omap2/omapfb/omapfb-sysfs.c index 5179219128b..6f9c72cd6bb 100644 --- a/drivers/video/omap2/omapfb/omapfb-sysfs.c +++ b/drivers/video/omap2/omapfb/omapfb-sysfs.c @@ -49,6 +49,7 @@ static ssize_t store_rotate_type(struct device *dev, { struct fb_info *fbi = dev_get_drvdata(dev); struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_mem_region *rg; enum omap_dss_rotation_type rot_type; int r; @@ -64,9 +65,11 @@ static ssize_t store_rotate_type(struct device *dev, if (rot_type == ofbi->rotation_type) goto out; - if (ofbi->region.size) { + rg = omapfb_get_mem_region(ofbi->region); + + if (rg->size) { r = -EBUSY; - goto out; + goto put_region; } ofbi->rotation_type = rot_type; @@ -75,6 +78,8 @@ static ssize_t store_rotate_type(struct device *dev, * Since the VRAM for this FB is not allocated at the moment we don't * need to do any further parameter checking at this point. */ +put_region: + omapfb_put_mem_region(rg); out: unlock_fb_info(fbi); @@ -97,7 +102,7 @@ static ssize_t store_mirror(struct device *dev, { struct fb_info *fbi = dev_get_drvdata(dev); struct omapfb_info *ofbi = FB2OFB(fbi); - bool mirror; + unsigned long mirror; int r; struct fb_var_screeninfo new_var; @@ -111,6 +116,8 @@ static ssize_t store_mirror(struct device *dev, ofbi->mirror = mirror; + omapfb_get_mem_region(ofbi->region); + memcpy(&new_var, &fbi->var, sizeof(new_var)); r = check_fb_var(fbi, &new_var); if (r) @@ -125,6 +132,8 @@ static ssize_t store_mirror(struct device *dev, r = count; out: + omapfb_put_mem_region(ofbi->region); + unlock_fb_info(fbi); return r; @@ -263,11 +272,15 @@ static ssize_t store_overlays(struct device *dev, struct device_attribute *attr, DBG("detaching %d\n", ofbi->overlays[i]->id); + omapfb_get_mem_region(ofbi->region); + omapfb_overlay_enable(ovl, 0); if (ovl->manager) ovl->manager->apply(ovl->manager); + omapfb_put_mem_region(ofbi->region); + for (t = i + 1; t < ofbi->num_overlays; t++) { ofbi->rotation[t-1] = ofbi->rotation[t]; ofbi->overlays[t-1] = ofbi->overlays[t]; @@ -300,7 +313,12 @@ static ssize_t store_overlays(struct device *dev, struct device_attribute *attr, } if (added) { + omapfb_get_mem_region(ofbi->region); + r = omapfb_apply_changes(fbi, 0); + + omapfb_put_mem_region(ofbi->region); + if (r) goto out; } @@ -388,7 +406,12 @@ static ssize_t store_overlays_rotate(struct device *dev, for (i = 0; i < num_ovls; ++i) ofbi->rotation[i] = rotation[i]; + omapfb_get_mem_region(ofbi->region); + r = omapfb_apply_changes(fbi, 0); + + omapfb_put_mem_region(ofbi->region); + if (r) goto out; @@ -408,7 +431,7 @@ static ssize_t show_size(struct device *dev, struct fb_info *fbi = dev_get_drvdata(dev); struct omapfb_info *ofbi = FB2OFB(fbi); - return snprintf(buf, PAGE_SIZE, "%lu\n", ofbi->region.size); + return snprintf(buf, PAGE_SIZE, "%lu\n", ofbi->region->size); } static ssize_t store_size(struct device *dev, struct device_attribute *attr, @@ -416,6 +439,8 @@ static ssize_t store_size(struct device *dev, struct device_attribute *attr, { struct fb_info *fbi = dev_get_drvdata(dev); struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omapfb2_mem_region *rg; unsigned long size; int r; int i; @@ -425,15 +450,33 @@ static ssize_t store_size(struct device *dev, struct device_attribute *attr, if (!lock_fb_info(fbi)) return -ENODEV; - for (i = 0; i < ofbi->num_overlays; i++) { - if (ofbi->overlays[i]->info.enabled) { - r = -EBUSY; - goto out; + rg = ofbi->region; + + down_write_nested(&rg->lock, rg->id); + atomic_inc(&rg->lock_count); + + if (atomic_read(&rg->map_count)) { + r = -EBUSY; + goto out; + } + + for (i = 0; i < fbdev->num_fbs; i++) { + struct omapfb_info *ofbi2 = FB2OFB(fbdev->fbs[i]); + int j; + + if (ofbi2->region != rg) + continue; + + for (j = 0; j < ofbi2->num_overlays; j++) { + if (ofbi2->overlays[j]->info.enabled) { + r = -EBUSY; + goto out; + } } } - if (size != ofbi->region.size) { - r = omapfb_realloc_fbmem(fbi, size, ofbi->region.type); + if (size != ofbi->region->size) { + r = omapfb_realloc_fbmem(fbi, size, ofbi->region->type); if (r) { dev_err(dev, "realloc fbmem failed\n"); goto out; @@ -442,6 +485,9 @@ static ssize_t store_size(struct device *dev, struct device_attribute *attr, r = count; out: + atomic_dec(&rg->lock_count); + up_write(&rg->lock); + unlock_fb_info(fbi); return r; @@ -453,7 +499,7 @@ static ssize_t show_phys(struct device *dev, struct fb_info *fbi = dev_get_drvdata(dev); struct omapfb_info *ofbi = FB2OFB(fbi); - return snprintf(buf, PAGE_SIZE, "%0x\n", ofbi->region.paddr); + return snprintf(buf, PAGE_SIZE, "%0x\n", ofbi->region->paddr); } static ssize_t show_virt(struct device *dev, @@ -462,7 +508,7 @@ static ssize_t show_virt(struct device *dev, struct fb_info *fbi = dev_get_drvdata(dev); struct omapfb_info *ofbi = FB2OFB(fbi); - return snprintf(buf, PAGE_SIZE, "%p\n", ofbi->region.vaddr); + return snprintf(buf, PAGE_SIZE, "%p\n", ofbi->region->vaddr); } static struct device_attribute omapfb_attrs[] = { diff --git a/drivers/video/omap2/omapfb/omapfb.h b/drivers/video/omap2/omapfb/omapfb.h index cd54fdbfd8b..1305fc9880b 100644 --- a/drivers/video/omap2/omapfb/omapfb.h +++ b/drivers/video/omap2/omapfb/omapfb.h @@ -27,6 +27,8 @@ #define DEBUG #endif +#include <linux/rwsem.h> + #include <plat/display.h> #ifdef DEBUG @@ -44,6 +46,7 @@ extern unsigned int omapfb_debug; #define OMAPFB_MAX_OVL_PER_FB 3 struct omapfb2_mem_region { + int id; u32 paddr; void __iomem *vaddr; struct vrfb vrfb; @@ -51,13 +54,15 @@ struct omapfb2_mem_region { u8 type; /* OMAPFB_PLANE_MEM_* */ bool alloc; /* allocated by the driver */ bool map; /* kernel mapped by the driver */ + atomic_t map_count; + struct rw_semaphore lock; + atomic_t lock_count; }; /* appended to fb_info */ struct omapfb_info { int id; - struct omapfb2_mem_region region; - atomic_t map_count; + struct omapfb2_mem_region *region; int num_overlays; struct omap_overlay *overlays[OMAPFB_MAX_OVL_PER_FB]; struct omapfb2_device *fbdev; @@ -76,6 +81,7 @@ struct omapfb2_device { unsigned num_fbs; struct fb_info *fbs[10]; + struct omapfb2_mem_region regions[10]; unsigned num_displays; struct omap_dss_device *displays[10]; @@ -117,6 +123,9 @@ int omapfb_update_window(struct fb_info *fbi, int dss_mode_to_fb_mode(enum omap_color_mode dssmode, struct fb_var_screeninfo *var); +int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl, + u16 posx, u16 posy, u16 outw, u16 outh); + /* find the display connected to this fb, if any */ static inline struct omap_dss_device *fb2display(struct fb_info *fbi) { @@ -148,8 +157,24 @@ static inline int omapfb_overlay_enable(struct omap_overlay *ovl, struct omap_overlay_info info; ovl->get_overlay_info(ovl, &info); + if (info.enabled == enable) + return 0; info.enabled = enable; return ovl->set_overlay_info(ovl, &info); } +static inline struct omapfb2_mem_region * +omapfb_get_mem_region(struct omapfb2_mem_region *rg) +{ + down_read_nested(&rg->lock, rg->id); + atomic_inc(&rg->lock_count); + return rg; +} + +static inline void omapfb_put_mem_region(struct omapfb2_mem_region *rg) +{ + atomic_dec(&rg->lock_count); + up_read(&rg->lock); +} + #endif diff --git a/drivers/video/p9100.c b/drivers/video/p9100.c index 6552751e81a..b6c3fc2db63 100644 --- a/drivers/video/p9100.c +++ b/drivers/video/p9100.c @@ -249,7 +249,7 @@ static void p9100_init_fix(struct fb_info *info, int linebytes, struct device_no info->fix.accel = FB_ACCEL_SUN_CGTHREE; } -static int __devinit p9100_probe(struct of_device *op, const struct of_device_id *match) +static int __devinit p9100_probe(struct platform_device *op, const struct of_device_id *match) { struct device_node *dp = op->dev.of_node; struct fb_info *info; @@ -326,7 +326,7 @@ out_err: return err; } -static int __devexit p9100_remove(struct of_device *op) +static int __devexit p9100_remove(struct platform_device *op) { struct fb_info *info = dev_get_drvdata(&op->dev); struct p9100_par *par = info->par; @@ -367,12 +367,12 @@ static int __init p9100_init(void) if (fb_get_options("p9100fb", NULL)) return -ENODEV; - return of_register_driver(&p9100_driver, &of_bus_type); + return of_register_platform_driver(&p9100_driver); } static void __exit p9100_exit(void) { - of_unregister_driver(&p9100_driver); + of_unregister_platform_driver(&p9100_driver); } module_init(p9100_init); diff --git a/drivers/video/platinumfb.c b/drivers/video/platinumfb.c index 72a1f4c0473..a50e1977b31 100644 --- a/drivers/video/platinumfb.c +++ b/drivers/video/platinumfb.c @@ -533,7 +533,7 @@ static int __init platinumfb_setup(char *options) #define invalidate_cache(addr) #endif -static int __devinit platinumfb_probe(struct of_device* odev, +static int __devinit platinumfb_probe(struct platform_device* odev, const struct of_device_id *match) { struct device_node *dp = odev->dev.of_node; @@ -646,7 +646,7 @@ static int __devinit platinumfb_probe(struct of_device* odev, return rc; } -static int __devexit platinumfb_remove(struct of_device* odev) +static int __devexit platinumfb_remove(struct platform_device* odev) { struct fb_info *info = dev_get_drvdata(&odev->dev); struct fb_info_platinum *pinfo = info->par; diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c index 9682ecc60e1..f9aca9d13d1 100644 --- a/drivers/video/s3c-fb.c +++ b/drivers/video/s3c-fb.c @@ -1,7 +1,7 @@ /* linux/drivers/video/s3c-fb.c * * Copyright 2008 Openmoko Inc. - * Copyright 2008 Simtec Electronics + * Copyright 2008-2010 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> * http://armlinux.simtec.co.uk/ * @@ -9,7 +9,7 @@ * * 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. + * published by the Free Software FoundatIon. */ #include <linux/kernel.h> @@ -21,9 +21,11 @@ #include <linux/clk.h> #include <linux/fb.h> #include <linux/io.h> +#include <linux/uaccess.h> +#include <linux/interrupt.h> #include <mach/map.h> -#include <mach/regs-fb.h> +#include <plat/regs-fb-v4.h> #include <plat/fb.h> /* This driver will export a number of framebuffer interfaces depending @@ -36,9 +38,9 @@ * output timings and as the control for the output power-down state. */ -/* note, some of the functions that get called are derived from including - * <mach/regs-fb.h> as they are specific to the architecture that the code - * is being built for. +/* note, the previous use of <mach/regs-fb.h> to get platform specific data + * has been replaced by using the platform device name to pick the correct + * configuration data for the system. */ #ifdef CONFIG_FB_S3C_DEBUG_REGWRITE @@ -48,13 +50,108 @@ __raw_writel(v, r); } while(0) #endif /* FB_S3C_DEBUG_REGWRITE */ +/* irq_flags bits */ +#define S3C_FB_VSYNC_IRQ_EN 0 + +#define VSYNC_TIMEOUT_MSEC 50 + struct s3c_fb; +#define VALID_BPP(x) (1 << ((x) - 1)) + +#define OSD_BASE(win, variant) ((variant).osd + ((win) * (variant).osd_stride)) +#define VIDOSD_A(win, variant) (OSD_BASE(win, variant) + 0x00) +#define VIDOSD_B(win, variant) (OSD_BASE(win, variant) + 0x04) +#define VIDOSD_C(win, variant) (OSD_BASE(win, variant) + 0x08) +#define VIDOSD_D(win, variant) (OSD_BASE(win, variant) + 0x0C) + +/** + * struct s3c_fb_variant - fb variant information + * @is_2443: Set if S3C2443/S3C2416 style hardware. + * @nr_windows: The number of windows. + * @vidtcon: The base for the VIDTCONx registers + * @wincon: The base for the WINxCON registers. + * @winmap: The base for the WINxMAP registers. + * @keycon: The abse for the WxKEYCON registers. + * @buf_start: Offset of buffer start registers. + * @buf_size: Offset of buffer size registers. + * @buf_end: Offset of buffer end registers. + * @osd: The base for the OSD registers. + * @palette: Address of palette memory, or 0 if none. + * @has_prtcon: Set if has PRTCON register. + * @has_shadowcon: Set if has SHADOWCON register. + */ +struct s3c_fb_variant { + unsigned int is_2443:1; + unsigned short nr_windows; + unsigned short vidtcon; + unsigned short wincon; + unsigned short winmap; + unsigned short keycon; + unsigned short buf_start; + unsigned short buf_end; + unsigned short buf_size; + unsigned short osd; + unsigned short osd_stride; + unsigned short palette[S3C_FB_MAX_WIN]; + + unsigned int has_prtcon:1; + unsigned int has_shadowcon:1; +}; + +/** + * struct s3c_fb_win_variant + * @has_osd_c: Set if has OSD C register. + * @has_osd_d: Set if has OSD D register. + * @has_osd_alpha: Set if can change alpha transparency for a window. + * @palette_sz: Size of palette in entries. + * @palette_16bpp: Set if palette is 16bits wide. + * @osd_size_off: If != 0, supports setting up OSD for a window; the appropriate + * register is located at the given offset from OSD_BASE. + * @valid_bpp: 1 bit per BPP setting to show valid bits-per-pixel. + * + * valid_bpp bit x is set if (x+1)BPP is supported. + */ +struct s3c_fb_win_variant { + unsigned int has_osd_c:1; + unsigned int has_osd_d:1; + unsigned int has_osd_alpha:1; + unsigned int palette_16bpp:1; + unsigned short osd_size_off; + unsigned short palette_sz; + u32 valid_bpp; +}; + +/** + * struct s3c_fb_driverdata - per-device type driver data for init time. + * @variant: The variant information for this driver. + * @win: The window information for each window. + */ +struct s3c_fb_driverdata { + struct s3c_fb_variant variant; + struct s3c_fb_win_variant *win[S3C_FB_MAX_WIN]; +}; + +/** + * struct s3c_fb_palette - palette information + * @r: Red bitfield. + * @g: Green bitfield. + * @b: Blue bitfield. + * @a: Alpha bitfield. + */ +struct s3c_fb_palette { + struct fb_bitfield r; + struct fb_bitfield g; + struct fb_bitfield b; + struct fb_bitfield a; +}; + /** * struct s3c_fb_win - per window private data for each framebuffer. * @windata: The platform data supplied for the window configuration. * @parent: The hardware that this window is part of. * @fbinfo: Pointer pack to the framebuffer info for this window. + * @varint: The variant information for this window. * @palette_buffer: Buffer/cache to hold palette entries. * @pseudo_palette: For use in TRUECOLOUR modes for entries 0..15/ * @index: The window number of this window. @@ -65,6 +162,7 @@ struct s3c_fb_win { struct s3c_fb *parent; struct fb_info *fbinfo; struct s3c_fb_palette palette; + struct s3c_fb_win_variant variant; u32 *palette_buffer; u32 pseudo_palette[16]; @@ -72,37 +170,54 @@ struct s3c_fb_win { }; /** + * struct s3c_fb_vsync - vsync information + * @wait: a queue for processes waiting for vsync + * @count: vsync interrupt count + */ +struct s3c_fb_vsync { + wait_queue_head_t wait; + unsigned int count; +}; + +/** * struct s3c_fb - overall hardware state of the hardware * @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. * @regs: The mapped hardware registers. + * @variant: Variant information for this hardware. * @enabled: A bitmask of enabled hardware windows. * @pdata: The platform configuration data passed with the device. * @windows: The hardware windows that have been claimed. + * @irq_no: IRQ line number + * @irq_flags: irq flags + * @vsync_info: VSYNC-related information (count, queues...) */ struct s3c_fb { struct device *dev; struct resource *regs_res; struct clk *bus_clk; void __iomem *regs; + struct s3c_fb_variant variant; unsigned char enabled; struct s3c_fb_platdata *pdata; struct s3c_fb_win *windows[S3C_FB_MAX_WIN]; + + int irq_no; + unsigned long irq_flags; + struct s3c_fb_vsync vsync_info; }; /** - * s3c_fb_win_has_palette() - determine if a mode has a palette - * @win: The window number being queried. - * @bpp: The number of bits per pixel to test. - * - * Work out if the given window supports palletised data at the specified bpp. + * s3c_fb_validate_win_bpp - validate the bits-per-pixel for this mode. + * @win: The device window. + * @bpp: The bit depth. */ -static int s3c_fb_win_has_palette(unsigned int win, unsigned int bpp) +static bool s3c_fb_validate_win_bpp(struct s3c_fb_win *win, unsigned int bpp) { - return s3c_fb_win_pal_size(win) <= (1 << bpp); + return win->variant.valid_bpp & VALID_BPP(bpp); } /** @@ -125,7 +240,7 @@ static int s3c_fb_check_var(struct fb_var_screeninfo *var, var->xres_virtual = max((unsigned int)windata->virtual_x, var->xres); var->yres_virtual = max((unsigned int)windata->virtual_y, var->yres); - if (!s3c_fb_validate_win_bpp(win->index, var->bits_per_pixel)) { + if (!s3c_fb_validate_win_bpp(win, var->bits_per_pixel)) { dev_dbg(sfb->dev, "win %d: unsupported bpp %d\n", win->index, var->bits_per_pixel); return -EINVAL; @@ -140,7 +255,7 @@ static int s3c_fb_check_var(struct fb_var_screeninfo *var, case 2: case 4: case 8: - if (!s3c_fb_win_has_palette(win->index, var->bits_per_pixel)) { + if (sfb->variant.palette[win->index] != 0) { /* non palletised, A:1,R:2,G:3,B:2 mode */ var->red.offset = 4; var->green.offset = 2; @@ -255,6 +370,66 @@ static int s3c_fb_align_word(unsigned int bpp, unsigned int pix) } /** + * vidosd_set_size() - set OSD size for a window + * + * @win: the window to set OSD size for + * @size: OSD size register value + */ +static void vidosd_set_size(struct s3c_fb_win *win, u32 size) +{ + struct s3c_fb *sfb = win->parent; + + /* OSD can be set up if osd_size_off != 0 for this window */ + if (win->variant.osd_size_off) + writel(size, sfb->regs + OSD_BASE(win->index, sfb->variant) + + win->variant.osd_size_off); +} + +/** + * vidosd_set_alpha() - set alpha transparency for a window + * + * @win: the window to set OSD size for + * @alpha: alpha register value + */ +static void vidosd_set_alpha(struct s3c_fb_win *win, u32 alpha) +{ + struct s3c_fb *sfb = win->parent; + + if (win->variant.has_osd_alpha) + writel(alpha, sfb->regs + VIDOSD_C(win->index, sfb->variant)); +} + +/** + * shadow_protect_win() - disable updating values from shadow registers at vsync + * + * @win: window to protect registers for + * @protect: 1 to protect (disable updates) + */ +static void shadow_protect_win(struct s3c_fb_win *win, bool protect) +{ + struct s3c_fb *sfb = win->parent; + u32 reg; + + if (protect) { + if (sfb->variant.has_prtcon) { + writel(PRTCON_PROTECT, sfb->regs + PRTCON); + } else if (sfb->variant.has_shadowcon) { + reg = readl(sfb->regs + SHADOWCON); + writel(reg | SHADOWCON_WINx_PROTECT(win->index), + sfb->regs + SHADOWCON); + } + } else { + if (sfb->variant.has_prtcon) { + writel(0, sfb->regs + PRTCON); + } else if (sfb->variant.has_shadowcon) { + reg = readl(sfb->regs + SHADOWCON); + writel(reg & ~SHADOWCON_WINx_PROTECT(win->index), + sfb->regs + SHADOWCON); + } + } +} + +/** * s3c_fb_set_par() - framebuffer request to set new framebuffer state. * @info: The framebuffer to change. * @@ -266,14 +441,17 @@ static int s3c_fb_set_par(struct fb_info *info) struct s3c_fb_win *win = info->par; struct s3c_fb *sfb = win->parent; void __iomem *regs = sfb->regs; + void __iomem *buf = regs; int win_no = win->index; - u32 osdc_data = 0; + u32 alpha = 0; u32 data; u32 pagewidth; int clkdiv; dev_dbg(sfb->dev, "setting framebuffer parameters\n"); + shadow_protect_win(win, 1); + switch (var->bits_per_pixel) { case 32: case 24: @@ -282,7 +460,7 @@ static int s3c_fb_set_par(struct fb_info *info) info->fix.visual = FB_VISUAL_TRUECOLOR; break; case 8: - if (s3c_fb_win_has_palette(win_no, 8)) + if (win->variant.palette_sz >= 256) info->fix.visual = FB_VISUAL_PSEUDOCOLOR; else info->fix.visual = FB_VISUAL_TRUECOLOR; @@ -297,12 +475,15 @@ static int s3c_fb_set_par(struct fb_info *info) info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8; + info->fix.xpanstep = info->var.xres_virtual > info->var.xres ? 1 : 0; + info->fix.ypanstep = info->var.yres_virtual > info->var.yres ? 1 : 0; + /* disable the window whilst we update it */ writel(0, regs + WINCON(win_no)); - /* use window 0 as the basis for the lcd output timings */ + /* use platform specified window as the basis for the lcd timings */ - if (win_no == 0) { + if (win_no == sfb->pdata->default_win) { clkdiv = s3c_fb_calc_pixclk(sfb, var->pixclock); data = sfb->pdata->vidcon0; @@ -315,6 +496,9 @@ static int s3c_fb_set_par(struct fb_info *info) /* write the timing data to the panel */ + if (sfb->variant.is_2443) + data |= (1 << 5); + data |= VIDCON0_ENVID | VIDCON0_ENVID_F; writel(data, regs + VIDCON0); @@ -322,53 +506,54 @@ static int s3c_fb_set_par(struct fb_info *info) VIDTCON0_VFPD(var->lower_margin - 1) | VIDTCON0_VSPW(var->vsync_len - 1); - writel(data, regs + VIDTCON0); + writel(data, regs + sfb->variant.vidtcon); data = VIDTCON1_HBPD(var->left_margin - 1) | VIDTCON1_HFPD(var->right_margin - 1) | VIDTCON1_HSPW(var->hsync_len - 1); - writel(data, regs + VIDTCON1); + /* VIDTCON1 */ + writel(data, regs + sfb->variant.vidtcon + 4); data = VIDTCON2_LINEVAL(var->yres - 1) | VIDTCON2_HOZVAL(var->xres - 1); - writel(data, regs + VIDTCON2); + writel(data, regs +sfb->variant.vidtcon + 8 ); } /* write the buffer address */ - writel(info->fix.smem_start, regs + VIDW_BUF_START(win_no)); + /* start and end registers stride is 8 */ + buf = regs + win_no * 8; + + writel(info->fix.smem_start, buf + sfb->variant.buf_start); data = info->fix.smem_start + info->fix.line_length * var->yres; - writel(data, regs + VIDW_BUF_END(win_no)); + writel(data, buf + sfb->variant.buf_end); pagewidth = (var->xres * var->bits_per_pixel) >> 3; data = VIDW_BUF_SIZE_OFFSET(info->fix.line_length - pagewidth) | VIDW_BUF_SIZE_PAGEWIDTH(pagewidth); - writel(data, regs + VIDW_BUF_SIZE(win_no)); + writel(data, regs + sfb->variant.buf_size + (win_no * 4)); /* write 'OSD' registers to control position of framebuffer */ data = VIDOSDxA_TOPLEFT_X(0) | VIDOSDxA_TOPLEFT_Y(0); - writel(data, regs + VIDOSD_A(win_no)); + writel(data, regs + VIDOSD_A(win_no, sfb->variant)); data = VIDOSDxB_BOTRIGHT_X(s3c_fb_align_word(var->bits_per_pixel, var->xres - 1)) | VIDOSDxB_BOTRIGHT_Y(var->yres - 1); - writel(data, regs + VIDOSD_B(win_no)); + writel(data, regs + VIDOSD_B(win_no, sfb->variant)); data = var->xres * var->yres; - osdc_data = VIDISD14C_ALPHA1_R(0xf) | + alpha = VIDISD14C_ALPHA1_R(0xf) | VIDISD14C_ALPHA1_G(0xf) | VIDISD14C_ALPHA1_B(0xf); - if (s3c_fb_has_osd_d(win_no)) { - writel(data, regs + VIDOSD_D(win_no)); - writel(osdc_data, regs + VIDOSD_C(win_no)); - } else - writel(data, regs + VIDOSD_C(win_no)); + vidosd_set_alpha(win, alpha); + vidosd_set_size(win, data); data = WINCONx_ENWIN; @@ -424,13 +609,15 @@ static int s3c_fb_set_par(struct fb_info *info) else data |= WINCON0_BPPMODE_24BPP_888; + data |= WINCONx_WSWP; data |= WINCONx_BURSTLEN_16WORD; break; } - /* It has no color key control register for window0 */ + /* Enable the colour keying for the window below this one */ if (win_no > 0) { u32 keycon0_data = 0, keycon1_data = 0; + void __iomem *keycon = regs + sfb->variant.keycon; keycon0_data = ~(WxKEYCON0_KEYBL_EN | WxKEYCON0_KEYEN_F | @@ -438,12 +625,23 @@ static int s3c_fb_set_par(struct fb_info *info) keycon1_data = WxKEYCON1_COLVAL(0xffffff); - writel(keycon0_data, regs + WxKEYCONy(win_no-1, 0)); - writel(keycon1_data, regs + WxKEYCONy(win_no-1, 1)); + keycon += (win_no - 1) * 8; + + writel(keycon0_data, keycon + WKEYCON0); + writel(keycon1_data, keycon + WKEYCON1); + } + + writel(data, regs + sfb->variant.wincon + (win_no * 4)); + writel(0x0, regs + sfb->variant.winmap + (win_no * 4)); + + /* Enable DMA channel for this window */ + if (sfb->variant.has_shadowcon) { + data = readl(sfb->regs + SHADOWCON); + data |= SHADOWCON_CHx_ENABLE(win_no); + writel(data, sfb->regs + SHADOWCON); } - writel(data, regs + WINCON(win_no)); - writel(0x0, regs + WINxMAP(win_no)); + shadow_protect_win(win, 0); return 0; } @@ -470,7 +668,7 @@ static void s3c_fb_update_palette(struct s3c_fb *sfb, void __iomem *palreg; u32 palcon; - palreg = sfb->regs + s3c_fb_pal_reg(win->index, reg); + palreg = sfb->regs + sfb->variant.palette[win->index]; dev_dbg(sfb->dev, "%s: win %d, reg %d (%p): %08x\n", __func__, win->index, reg, palreg, value); @@ -480,10 +678,10 @@ static void s3c_fb_update_palette(struct s3c_fb *sfb, palcon = readl(sfb->regs + WPALCON); writel(palcon | WPALCON_PAL_UPDATE, sfb->regs + WPALCON); - if (s3c_fb_pal_is16(win->index)) - writew(value, palreg); + if (win->variant.palette_16bpp) + writew(value, palreg + (reg * 2)); else - writel(value, palreg); + writel(value, palreg + (reg * 4)); writel(palcon, sfb->regs + WPALCON); } @@ -532,7 +730,7 @@ static int s3c_fb_setcolreg(unsigned regno, break; case FB_VISUAL_PSEUDOCOLOR: - if (regno < s3c_fb_win_pal_size(win->index)) { + if (regno < win->variant.palette_sz) { val = chan_to_field(red, &win->palette.r); val |= chan_to_field(green, &win->palette.g); val |= chan_to_field(blue, &win->palette.b); @@ -591,7 +789,7 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info) dev_dbg(sfb->dev, "blank mode %d\n", blank_mode); - wincon = readl(sfb->regs + WINCON(index)); + wincon = readl(sfb->regs + sfb->variant.wincon + (index * 4)); switch (blank_mode) { case FB_BLANK_POWERDOWN: @@ -602,11 +800,11 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info) case FB_BLANK_NORMAL: /* disable the DMA and display 0x0 (black) */ writel(WINxMAP_MAP | WINxMAP_MAP_COLOUR(0x0), - sfb->regs + WINxMAP(index)); + sfb->regs + sfb->variant.winmap + (index * 4)); break; case FB_BLANK_UNBLANK: - writel(0x0, sfb->regs + WINxMAP(index)); + writel(0x0, sfb->regs + sfb->variant.winmap + (index * 4)); wincon |= WINCONx_ENWIN; sfb->enabled |= (1 << index); break; @@ -617,7 +815,7 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info) return 1; } - writel(wincon, sfb->regs + WINCON(index)); + writel(wincon, sfb->regs + sfb->variant.wincon + (index * 4)); /* Check the enabled state to see if we need to be running the * main LCD interface, as if there are no active windows then @@ -636,12 +834,185 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info) /* we're stuck with this until we can do something about overriding * the power control using the blanking event for a single fb. */ - if (index == 0) + if (index == sfb->pdata->default_win) s3c_fb_enable(sfb, blank_mode != FB_BLANK_POWERDOWN ? 1 : 0); return 0; } +/** + * s3c_fb_pan_display() - Pan the display. + * + * Note that the offsets can be written to the device at any time, as their + * values are latched at each vsync automatically. This also means that only + * the last call to this function will have any effect on next vsync, but + * there is no need to sleep waiting for it to prevent tearing. + * + * @var: The screen information to verify. + * @info: The framebuffer device. + */ +static int s3c_fb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct s3c_fb_win *win = info->par; + struct s3c_fb *sfb = win->parent; + void __iomem *buf = sfb->regs + win->index * 8; + unsigned int start_boff, end_boff; + + /* Offset in bytes to the start of the displayed area */ + start_boff = var->yoffset * info->fix.line_length; + /* X offset depends on the current bpp */ + if (info->var.bits_per_pixel >= 8) { + start_boff += var->xoffset * (info->var.bits_per_pixel >> 3); + } else { + switch (info->var.bits_per_pixel) { + case 4: + start_boff += var->xoffset >> 1; + break; + case 2: + start_boff += var->xoffset >> 2; + break; + case 1: + start_boff += var->xoffset >> 3; + break; + default: + dev_err(sfb->dev, "invalid bpp\n"); + return -EINVAL; + } + } + /* Offset in bytes to the end of the displayed area */ + end_boff = start_boff + 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 */ + shadow_protect_win(win, 1); + + writel(info->fix.smem_start + start_boff, buf + sfb->variant.buf_start); + writel(info->fix.smem_start + end_boff, buf + sfb->variant.buf_end); + + shadow_protect_win(win, 0); + + return 0; +} + +/** + * s3c_fb_enable_irq() - enable framebuffer interrupts + * @sfb: main hardware state + */ +static void s3c_fb_enable_irq(struct s3c_fb *sfb) +{ + void __iomem *regs = sfb->regs; + u32 irq_ctrl_reg; + + if (!test_and_set_bit(S3C_FB_VSYNC_IRQ_EN, &sfb->irq_flags)) { + /* IRQ disabled, enable it */ + irq_ctrl_reg = readl(regs + VIDINTCON0); + + irq_ctrl_reg |= VIDINTCON0_INT_ENABLE; + irq_ctrl_reg |= VIDINTCON0_INT_FRAME; + + irq_ctrl_reg &= ~VIDINTCON0_FRAMESEL0_MASK; + irq_ctrl_reg |= VIDINTCON0_FRAMESEL0_VSYNC; + irq_ctrl_reg &= ~VIDINTCON0_FRAMESEL1_MASK; + irq_ctrl_reg |= VIDINTCON0_FRAMESEL1_NONE; + + writel(irq_ctrl_reg, regs + VIDINTCON0); + } +} + +/** + * s3c_fb_disable_irq() - disable framebuffer interrupts + * @sfb: main hardware state + */ +static void s3c_fb_disable_irq(struct s3c_fb *sfb) +{ + void __iomem *regs = sfb->regs; + u32 irq_ctrl_reg; + + if (test_and_clear_bit(S3C_FB_VSYNC_IRQ_EN, &sfb->irq_flags)) { + /* IRQ enabled, disable it */ + irq_ctrl_reg = readl(regs + VIDINTCON0); + + irq_ctrl_reg &= ~VIDINTCON0_INT_FRAME; + irq_ctrl_reg &= ~VIDINTCON0_INT_ENABLE; + + writel(irq_ctrl_reg, regs + VIDINTCON0); + } +} + +static irqreturn_t s3c_fb_irq(int irq, void *dev_id) +{ + struct s3c_fb *sfb = dev_id; + void __iomem *regs = sfb->regs; + u32 irq_sts_reg; + + irq_sts_reg = readl(regs + VIDINTCON1); + + if (irq_sts_reg & VIDINTCON1_INT_FRAME) { + + /* VSYNC interrupt, accept it */ + writel(VIDINTCON1_INT_FRAME, regs + VIDINTCON1); + + sfb->vsync_info.count++; + wake_up_interruptible(&sfb->vsync_info.wait); + } + + /* We only support waiting for VSYNC for now, so it's safe + * to always disable irqs here. + */ + s3c_fb_disable_irq(sfb); + + return IRQ_HANDLED; +} + +/** + * s3c_fb_wait_for_vsync() - sleep until next VSYNC interrupt or timeout + * @sfb: main hardware state + * @crtc: head index. + */ +static int s3c_fb_wait_for_vsync(struct s3c_fb *sfb, u32 crtc) +{ + unsigned long count; + int ret; + + if (crtc != 0) + return -ENODEV; + + count = sfb->vsync_info.count; + s3c_fb_enable_irq(sfb); + ret = wait_event_interruptible_timeout(sfb->vsync_info.wait, + count != sfb->vsync_info.count, + msecs_to_jiffies(VSYNC_TIMEOUT_MSEC)); + if (ret == 0) + return -ETIMEDOUT; + + return 0; +} + +static int s3c_fb_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg) +{ + struct s3c_fb_win *win = info->par; + struct s3c_fb *sfb = win->parent; + int ret; + u32 crtc; + + switch (cmd) { + case FBIO_WAITFORVSYNC: + if (get_user(crtc, (u32 __user *)arg)) { + ret = -EFAULT; + break; + } + + ret = s3c_fb_wait_for_vsync(sfb, crtc); + break; + default: + ret = -ENOTTY; + } + + return ret; +} + static struct fb_ops s3c_fb_ops = { .owner = THIS_MODULE, .fb_check_var = s3c_fb_check_var, @@ -651,9 +1022,33 @@ static struct fb_ops s3c_fb_ops = { .fb_fillrect = cfb_fillrect, .fb_copyarea = cfb_copyarea, .fb_imageblit = cfb_imageblit, + .fb_pan_display = s3c_fb_pan_display, + .fb_ioctl = s3c_fb_ioctl, }; /** + * s3c_fb_missing_pixclock() - calculates pixel clock + * @mode: The video mode to change. + * + * Calculate the pixel clock when none has been given through platform data. + */ +static void __devinit s3c_fb_missing_pixclock(struct fb_videomode *mode) +{ + u64 pixclk = 1000000000000ULL; + u32 div; + + div = mode->left_margin + mode->hsync_len + mode->right_margin + + mode->xres; + div *= mode->upper_margin + mode->vsync_len + mode->lower_margin + + mode->yres; + div *= mode->refresh ? : 60; + + do_div(pixclk, div); + + mode->pixclock = pixclk; +} + +/** * s3c_fb_alloc_memory() - allocate display memory for framebuffer window * @sfb: The base resources for the hardware. * @win: The window to initialise memory for. @@ -711,7 +1106,8 @@ static void s3c_fb_free_memory(struct s3c_fb *sfb, struct s3c_fb_win *win) { struct fb_info *fbi = win->fbinfo; - dma_free_writecombine(sfb->dev, PAGE_ALIGN(fbi->fix.smem_len), + if (fbi->screen_base) + dma_free_writecombine(sfb->dev, PAGE_ALIGN(fbi->fix.smem_len), fbi->screen_base, fbi->fix.smem_start); } @@ -724,9 +1120,18 @@ static void s3c_fb_free_memory(struct s3c_fb *sfb, struct s3c_fb_win *win) */ static void s3c_fb_release_win(struct s3c_fb *sfb, struct s3c_fb_win *win) { + u32 data; + if (win->fbinfo) { + if (sfb->variant.has_shadowcon) { + data = readl(sfb->regs + SHADOWCON); + data &= ~SHADOWCON_CHx_ENABLE(win->index); + data &= ~SHADOWCON_CHx_LOCAL_ENABLE(win->index); + writel(data, sfb->regs + SHADOWCON); + } unregister_framebuffer(win->fbinfo); - fb_dealloc_cmap(&win->fbinfo->cmap); + if (win->fbinfo->cmap.len) + fb_dealloc_cmap(&win->fbinfo->cmap); s3c_fb_free_memory(sfb, win); framebuffer_release(win->fbinfo); } @@ -735,12 +1140,14 @@ static void s3c_fb_release_win(struct s3c_fb *sfb, struct s3c_fb_win *win) /** * s3c_fb_probe_win() - register an hardware window * @sfb: The base resources for the hardware + * @variant: The variant information for this window. * @res: Pointer to where to place the resultant window. * * Allocate and do the basic initialisation for one of the hardware's graphics * windows. */ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, + struct s3c_fb_win_variant *variant, struct s3c_fb_win **res) { struct fb_var_screeninfo *var; @@ -751,9 +1158,11 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, int palette_size; int ret; - dev_dbg(sfb->dev, "probing window %d\n", win_no); + dev_dbg(sfb->dev, "probing window %d, variant %p\n", win_no, variant); + + init_waitqueue_head(&sfb->vsync_info.wait); - palette_size = s3c_fb_win_pal_size(win_no); + palette_size = variant->palette_sz * 4; fbinfo = framebuffer_alloc(sizeof(struct s3c_fb_win) + palette_size * sizeof(u32), sfb->dev); @@ -770,7 +1179,9 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, WARN_ON(windata->win_mode.yres == 0); win = fbinfo->par; + *res = win; var = &fbinfo->var; + win->variant = *variant; win->fbinfo = fbinfo; win->parent = sfb; win->windata = windata; @@ -784,7 +1195,24 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, } /* setup the r/b/g positions for the window's palette */ - s3c_fb_init_palette(win_no, &win->palette); + if (win->variant.palette_16bpp) { + /* Set RGB 5:6:5 as default */ + win->palette.r.offset = 11; + win->palette.r.length = 5; + win->palette.g.offset = 5; + win->palette.g.length = 6; + win->palette.b.offset = 0; + win->palette.b.length = 5; + + } else { + /* Set 8bpp or 8bpp and 1bit alpha */ + win->palette.r.offset = 16; + win->palette.r.length = 8; + win->palette.g.offset = 8; + win->palette.g.length = 8; + win->palette.b.offset = 0; + win->palette.b.length = 8; + } /* setup the initial video mode from the window */ fb_videomode_to_var(&fbinfo->var, initmode); @@ -808,7 +1236,7 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, /* create initial colour map */ - ret = fb_alloc_cmap(&fbinfo->cmap, s3c_fb_win_pal_size(win_no), 1); + ret = fb_alloc_cmap(&fbinfo->cmap, win->variant.palette_sz, 1); if (ret == 0) fb_set_cmap(&fbinfo->cmap, fbinfo); else @@ -826,7 +1254,6 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, return ret; } - *res = win; dev_info(sfb->dev, "window %d: fb %s\n", win_no, fbinfo->fix.id); return 0; @@ -842,18 +1269,19 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, static void s3c_fb_clear_win(struct s3c_fb *sfb, int win) { void __iomem *regs = sfb->regs; - - writel(0, regs + WINCON(win)); - writel(0xffffff, regs + WxKEYCONy(win, 0)); - writel(0xffffff, regs + WxKEYCONy(win, 1)); - - writel(0, regs + VIDOSD_A(win)); - writel(0, regs + VIDOSD_B(win)); - writel(0, regs + VIDOSD_C(win)); + u32 reg; + + writel(0, regs + sfb->variant.wincon + (win * 4)); + writel(0, regs + VIDOSD_A(win, sfb->variant)); + writel(0, regs + VIDOSD_B(win, sfb->variant)); + writel(0, regs + VIDOSD_C(win, sfb->variant)); + reg = readl(regs + SHADOWCON); + writel(reg & ~SHADOWCON_WINx_PROTECT(win), regs + SHADOWCON); } static int __devinit s3c_fb_probe(struct platform_device *pdev) { + struct s3c_fb_driverdata *fbdrv; struct device *dev = &pdev->dev; struct s3c_fb_platdata *pd; struct s3c_fb *sfb; @@ -861,6 +1289,13 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) int win; int ret = 0; + fbdrv = (struct s3c_fb_driverdata *)platform_get_device_id(pdev)->driver_data; + + if (fbdrv->variant.nr_windows > S3C_FB_MAX_WIN) { + dev_err(dev, "too many windows, cannot attach\n"); + return -EINVAL; + } + pd = pdev->dev.platform_data; if (!pd) { dev_err(dev, "no platform data specified\n"); @@ -873,8 +1308,11 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) return -ENOMEM; } + dev_dbg(dev, "allocate new framebuffer %p\n", sfb); + sfb->dev = dev; sfb->pdata = pd; + sfb->variant = fbdrv->variant; sfb->bus_clk = clk_get(dev, "lcd"); if (IS_ERR(sfb->bus_clk)) { @@ -906,6 +1344,20 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) goto err_req_region; } + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(dev, "failed to acquire irq resource\n"); + ret = -ENOENT; + goto err_ioremap; + } + sfb->irq_no = res->start; + ret = request_irq(sfb->irq_no, s3c_fb_irq, + 0, "s3c_fb", sfb); + if (ret) { + dev_err(dev, "irq request failed\n"); + goto err_ioremap; + } + dev_dbg(dev, "got resources (regs %p), probing windows\n", sfb->regs); /* setup gpio and output polarity controls */ @@ -916,21 +1368,34 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) /* zero all windows before we do anything */ - for (win = 0; win < S3C_FB_MAX_WIN; win++) + for (win = 0; win < fbdrv->variant.nr_windows; win++) s3c_fb_clear_win(sfb, win); + /* initialise colour key controls */ + for (win = 0; win < (fbdrv->variant.nr_windows - 1); win++) { + void __iomem *regs = sfb->regs + sfb->variant.keycon; + + regs += (win * 8); + writel(0xffffff, regs + WKEYCON0); + writel(0xffffff, regs + WKEYCON1); + } + /* we have the register setup, start allocating framebuffers */ - for (win = 0; win < S3C_FB_MAX_WIN; win++) { + for (win = 0; win < fbdrv->variant.nr_windows; win++) { if (!pd->win[win]) continue; - ret = s3c_fb_probe_win(sfb, win, &sfb->windows[win]); + if (!pd->win[win]->win_mode.pixclock) + s3c_fb_missing_pixclock(&pd->win[win]->win_mode); + + ret = s3c_fb_probe_win(sfb, win, fbdrv->win[win], + &sfb->windows[win]); if (ret < 0) { dev_err(dev, "failed to create window %d\n", win); for (; win >= 0; win--) s3c_fb_release_win(sfb, sfb->windows[win]); - goto err_ioremap; + goto err_irq; } } @@ -938,6 +1403,9 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) return 0; +err_irq: + free_irq(sfb->irq_no, sfb); + err_ioremap: iounmap(sfb->regs); @@ -970,6 +1438,8 @@ static int __devexit s3c_fb_remove(struct platform_device *pdev) if (sfb->windows[win]) s3c_fb_release_win(sfb, sfb->windows[win]); + free_irq(sfb->irq_no, sfb); + iounmap(sfb->regs); clk_disable(sfb->bus_clk); @@ -1016,9 +1486,17 @@ static int s3c_fb_resume(struct platform_device *pdev) writel(pd->vidcon1, sfb->regs + VIDCON1); /* zero all windows before we do anything */ - for (win_no = 0; win_no < S3C_FB_MAX_WIN; win_no++) + for (win_no = 0; win_no < sfb->variant.nr_windows; win_no++) s3c_fb_clear_win(sfb, win_no); + for (win_no = 0; win_no < sfb->variant.nr_windows - 1; win_no++) { + void __iomem *regs = sfb->regs + sfb->variant.keycon; + + regs += (win_no * 8); + writel(0xffffff, regs + WKEYCON0); + writel(0xffffff, regs + WKEYCON1); + } + /* restore framebuffers */ for (win_no = 0; win_no < S3C_FB_MAX_WIN; win_no++) { win = sfb->windows[win_no]; @@ -1036,11 +1514,208 @@ static int s3c_fb_resume(struct platform_device *pdev) #define s3c_fb_resume NULL #endif + +#define VALID_BPP124 (VALID_BPP(1) | VALID_BPP(2) | VALID_BPP(4)) +#define VALID_BPP1248 (VALID_BPP124 | VALID_BPP(8)) + +static struct s3c_fb_win_variant s3c_fb_data_64xx_wins[] = { + [0] = { + .has_osd_c = 1, + .osd_size_off = 0x8, + .palette_sz = 256, + .valid_bpp = VALID_BPP1248 | VALID_BPP(16) | VALID_BPP(24), + }, + [1] = { + .has_osd_c = 1, + .has_osd_d = 1, + .osd_size_off = 0x12, + .has_osd_alpha = 1, + .palette_sz = 256, + .valid_bpp = (VALID_BPP1248 | VALID_BPP(16) | + VALID_BPP(18) | VALID_BPP(19) | + VALID_BPP(24) | VALID_BPP(25)), + }, + [2] = { + .has_osd_c = 1, + .has_osd_d = 1, + .osd_size_off = 0x12, + .has_osd_alpha = 1, + .palette_sz = 16, + .palette_16bpp = 1, + .valid_bpp = (VALID_BPP1248 | VALID_BPP(16) | + VALID_BPP(18) | VALID_BPP(19) | + VALID_BPP(24) | VALID_BPP(25)), + }, + [3] = { + .has_osd_c = 1, + .has_osd_alpha = 1, + .palette_sz = 16, + .palette_16bpp = 1, + .valid_bpp = (VALID_BPP124 | VALID_BPP(16) | + VALID_BPP(18) | VALID_BPP(19) | + VALID_BPP(24) | VALID_BPP(25)), + }, + [4] = { + .has_osd_c = 1, + .has_osd_alpha = 1, + .palette_sz = 4, + .palette_16bpp = 1, + .valid_bpp = (VALID_BPP(1) | VALID_BPP(2) | + VALID_BPP(16) | VALID_BPP(18) | + VALID_BPP(24) | VALID_BPP(25)), + }, +}; + +static struct s3c_fb_driverdata s3c_fb_data_64xx = { + .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] = 0x400, + [1] = 0x800, + [2] = 0x300, + [3] = 0x320, + [4] = 0x340, + }, + + .has_prtcon = 1, + }, + .win[0] = &s3c_fb_data_64xx_wins[0], + .win[1] = &s3c_fb_data_64xx_wins[1], + .win[2] = &s3c_fb_data_64xx_wins[2], + .win[3] = &s3c_fb_data_64xx_wins[3], + .win[4] = &s3c_fb_data_64xx_wins[4], +}; + +static struct s3c_fb_driverdata s3c_fb_data_s5pc100 = { + .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_prtcon = 1, + }, + .win[0] = &s3c_fb_data_64xx_wins[0], + .win[1] = &s3c_fb_data_64xx_wins[1], + .win[2] = &s3c_fb_data_64xx_wins[2], + .win[3] = &s3c_fb_data_64xx_wins[3], + .win[4] = &s3c_fb_data_64xx_wins[4], +}; + +static struct s3c_fb_driverdata s3c_fb_data_s5pv210 = { + .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_64xx_wins[0], + .win[1] = &s3c_fb_data_64xx_wins[1], + .win[2] = &s3c_fb_data_64xx_wins[2], + .win[3] = &s3c_fb_data_64xx_wins[3], + .win[4] = &s3c_fb_data_64xx_wins[4], +}; + +/* S3C2443/S3C2416 style hardware */ +static struct s3c_fb_driverdata s3c_fb_data_s3c2443 = { + .variant = { + .nr_windows = 2, + .is_2443 = 1, + + .vidtcon = 0x08, + .wincon = 0x14, + .winmap = 0xd0, + .keycon = 0xb0, + .osd = 0x28, + .osd_stride = 12, + .buf_start = 0x64, + .buf_size = 0x94, + .buf_end = 0x7c, + + .palette = { + [0] = 0x400, + [1] = 0x800, + }, + }, + .win[0] = &(struct s3c_fb_win_variant) { + .palette_sz = 256, + .valid_bpp = VALID_BPP1248 | VALID_BPP(16) | VALID_BPP(24), + }, + .win[1] = &(struct s3c_fb_win_variant) { + .has_osd_c = 1, + .has_osd_alpha = 1, + .palette_sz = 256, + .valid_bpp = (VALID_BPP1248 | VALID_BPP(16) | + VALID_BPP(18) | VALID_BPP(19) | + VALID_BPP(24) | VALID_BPP(25) | + VALID_BPP(28)), + }, +}; + +static struct platform_device_id s3c_fb_driver_ids[] = { + { + .name = "s3c-fb", + .driver_data = (unsigned long)&s3c_fb_data_64xx, + }, { + .name = "s5pc100-fb", + .driver_data = (unsigned long)&s3c_fb_data_s5pc100, + }, { + .name = "s5pv210-fb", + .driver_data = (unsigned long)&s3c_fb_data_s5pv210, + }, { + .name = "s3c2443-fb", + .driver_data = (unsigned long)&s3c_fb_data_s3c2443, + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, s3c_fb_driver_ids); + static struct platform_driver s3c_fb_driver = { .probe = s3c_fb_probe, .remove = __devexit_p(s3c_fb_remove), .suspend = s3c_fb_suspend, .resume = s3c_fb_resume, + .id_table = s3c_fb_driver_ids, .driver = { .name = "s3c-fb", .owner = THIS_MODULE, diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c new file mode 100644 index 00000000000..5699ce0c178 --- /dev/null +++ b/drivers/video/sh_mipi_dsi.c @@ -0,0 +1,505 @@ +/* + * Renesas SH-mobile MIPI DSI support + * + * Copyright (C) 2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de> + * + * This is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/types.h> + +#include <video/mipi_display.h> +#include <video/sh_mipi_dsi.h> +#include <video/sh_mobile_lcdc.h> + +#define CMTSRTCTR 0x80d0 +#define CMTSRTREQ 0x8070 + +#define DSIINTE 0x0060 + +/* E.g., sh7372 has 2 MIPI-DSIs - one for each LCDC */ +#define MAX_SH_MIPI_DSI 2 + +struct sh_mipi { + void __iomem *base; + struct clk *dsit_clk; + struct clk *dsip_clk; +}; + +static struct sh_mipi *mipi_dsi[MAX_SH_MIPI_DSI]; + +/* Protect the above array */ +static DEFINE_MUTEX(array_lock); + +static struct sh_mipi *sh_mipi_by_handle(int handle) +{ + if (handle >= ARRAY_SIZE(mipi_dsi) || handle < 0) + return NULL; + + return mipi_dsi[handle]; +} + +static int sh_mipi_send_short(struct sh_mipi *mipi, u8 dsi_cmd, + u8 cmd, u8 param) +{ + u32 data = (dsi_cmd << 24) | (cmd << 16) | (param << 8); + int cnt = 100; + + /* transmit a short packet to LCD panel */ + iowrite32(1 | data, mipi->base + 0x80d0); /* CMTSRTCTR */ + iowrite32(1, mipi->base + 0x8070); /* CMTSRTREQ */ + + while ((ioread32(mipi->base + 0x8070) & 1) && --cnt) + udelay(1); + + return cnt ? 0 : -ETIMEDOUT; +} + +#define LCD_CHAN2MIPI(c) ((c) < LCDC_CHAN_MAINLCD || (c) > LCDC_CHAN_SUBLCD ? \ + -EINVAL : (c) - 1) + +static int sh_mipi_dcs(int handle, u8 cmd) +{ + struct sh_mipi *mipi = sh_mipi_by_handle(LCD_CHAN2MIPI(handle)); + if (!mipi) + return -ENODEV; + return sh_mipi_send_short(mipi, MIPI_DSI_DCS_SHORT_WRITE, cmd, 0); +} + +static int sh_mipi_dcs_param(int handle, u8 cmd, u8 param) +{ + struct sh_mipi *mipi = sh_mipi_by_handle(LCD_CHAN2MIPI(handle)); + if (!mipi) + return -ENODEV; + return sh_mipi_send_short(mipi, MIPI_DSI_DCS_SHORT_WRITE_PARAM, cmd, + param); +} + +static void sh_mipi_dsi_enable(struct sh_mipi *mipi, bool enable) +{ + /* + * enable LCDC data tx, transition to LPS after completion of each HS + * packet + */ + iowrite32(0x00000002 | enable, mipi->base + 0x8000); /* DTCTR */ +} + +static void sh_mipi_shutdown(struct platform_device *pdev) +{ + struct sh_mipi *mipi = platform_get_drvdata(pdev); + + sh_mipi_dsi_enable(mipi, false); +} + +static void mipi_display_on(void *arg, struct fb_info *info) +{ + struct sh_mipi *mipi = arg; + + sh_mipi_dsi_enable(mipi, true); +} + +static void mipi_display_off(void *arg) +{ + struct sh_mipi *mipi = arg; + + sh_mipi_dsi_enable(mipi, false); +} + +static int __init sh_mipi_setup(struct sh_mipi *mipi, + struct sh_mipi_dsi_info *pdata) +{ + void __iomem *base = mipi->base; + struct sh_mobile_lcdc_chan_cfg *ch = pdata->lcd_chan; + u32 pctype, datatype, pixfmt; + u32 linelength; + bool yuv; + + /* Select data format */ + switch (pdata->data_format) { + case MIPI_RGB888: + pctype = 0; + datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24; + pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; + linelength = ch->lcd_cfg.xres * 3; + yuv = false; + break; + case MIPI_RGB565: + pctype = 1; + datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16; + pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; + linelength = ch->lcd_cfg.xres * 2; + yuv = false; + break; + case MIPI_RGB666_LP: + pctype = 2; + datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18; + pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; + linelength = ch->lcd_cfg.xres * 3; + yuv = false; + break; + case MIPI_RGB666: + pctype = 3; + datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18; + pixfmt = MIPI_DCS_PIXEL_FMT_18BIT; + linelength = (ch->lcd_cfg.xres * 18 + 7) / 8; + yuv = false; + break; + case MIPI_BGR888: + pctype = 8; + datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24; + pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; + linelength = ch->lcd_cfg.xres * 3; + yuv = false; + break; + case MIPI_BGR565: + pctype = 9; + datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16; + pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; + linelength = ch->lcd_cfg.xres * 2; + yuv = false; + break; + case MIPI_BGR666_LP: + pctype = 0xa; + datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18; + pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; + linelength = ch->lcd_cfg.xres * 3; + yuv = false; + break; + case MIPI_BGR666: + pctype = 0xb; + datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18; + pixfmt = MIPI_DCS_PIXEL_FMT_18BIT; + linelength = (ch->lcd_cfg.xres * 18 + 7) / 8; + yuv = false; + break; + case MIPI_YUYV: + pctype = 4; + datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16; + pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; + linelength = ch->lcd_cfg.xres * 2; + yuv = true; + break; + case MIPI_UYVY: + pctype = 5; + datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16; + pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; + linelength = ch->lcd_cfg.xres * 2; + yuv = true; + break; + case MIPI_YUV420_L: + pctype = 6; + datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12; + pixfmt = MIPI_DCS_PIXEL_FMT_12BIT; + linelength = (ch->lcd_cfg.xres * 12 + 7) / 8; + yuv = true; + break; + case MIPI_YUV420: + pctype = 7; + datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12; + pixfmt = MIPI_DCS_PIXEL_FMT_12BIT; + /* Length of U/V line */ + linelength = (ch->lcd_cfg.xres + 1) / 2; + yuv = true; + break; + default: + return -EINVAL; + } + + if ((yuv && ch->interface_type != YUV422) || + (!yuv && ch->interface_type != RGB24)) + return -EINVAL; + + /* reset DSI link */ + iowrite32(0x00000001, base); /* SYSCTRL */ + /* Hold reset for 100 cycles of the slowest of bus, HS byte and LP clock */ + udelay(50); + iowrite32(0x00000000, base); /* SYSCTRL */ + + /* setup DSI link */ + + /* + * Default = ULPS enable | + * Contention detection enabled | + * EoT packet transmission enable | + * CRC check enable | + * ECC check enable + * additionally enable first two lanes + */ + iowrite32(0x00003703, base + 0x04); /* SYSCONF */ + /* + * T_wakeup = 0x7000 + * T_hs-trail = 3 + * T_hs-prepare = 3 + * T_clk-trail = 3 + * T_clk-prepare = 2 + */ + iowrite32(0x70003332, base + 0x08); /* TIMSET */ + /* no responses requested */ + iowrite32(0x00000000, base + 0x18); /* RESREQSET0 */ + /* request response to packets of type 0x28 */ + iowrite32(0x00000100, base + 0x1c); /* RESREQSET1 */ + /* High-speed transmission timeout, default 0xffffffff */ + iowrite32(0x0fffffff, base + 0x20); /* HSTTOVSET */ + /* LP reception timeout, default 0xffffffff */ + iowrite32(0x0fffffff, base + 0x24); /* LPRTOVSET */ + /* Turn-around timeout, default 0xffffffff */ + iowrite32(0x0fffffff, base + 0x28); /* TATOVSET */ + /* Peripheral reset timeout, default 0xffffffff */ + iowrite32(0x0fffffff, base + 0x2c); /* PRTOVSET */ + /* Enable timeout counters */ + iowrite32(0x00000f00, base + 0x30); /* DSICTRL */ + /* Interrupts not used, disable all */ + iowrite32(0, base + DSIINTE); + /* DSI-Tx bias on */ + iowrite32(0x00000001, base + 0x70); /* PHYCTRL */ + udelay(200); + /* Deassert resets, power on, set multiplier */ + iowrite32(0x03070b01, base + 0x70); /* PHYCTRL */ + + /* setup l-bridge */ + + /* + * Enable transmission of all packets, + * transmit LPS after each HS packet completion + */ + iowrite32(0x00000006, base + 0x8000); /* DTCTR */ + /* VSYNC width = 2 (<< 17) */ + iowrite32(0x00040000 | (pctype << 12) | datatype, base + 0x8020); /* VMCTR1 */ + /* + * Non-burst mode with sync pulses: VSE and HSE are output, + * HSA period allowed, no commands in LP + */ + iowrite32(0x00e00000, base + 0x8024); /* VMCTR2 */ + /* + * 0x660 = 1632 bytes per line (RGB24, 544 pixels: see + * sh_mobile_lcdc_info.ch[0].lcd_cfg.xres), HSALEN = 1 - default + * (unused, since VMCTR2[HSABM] = 0) + */ + iowrite32(1 | (linelength << 16), base + 0x8028); /* VMLEN1 */ + + msleep(5); + + /* setup LCD panel */ + + /* cf. drivers/video/omap/lcd_mipid.c */ + sh_mipi_dcs(ch->chan, MIPI_DCS_EXIT_SLEEP_MODE); + msleep(120); + /* + * [7] - Page Address Mode + * [6] - Column Address Mode + * [5] - Page / Column Address Mode + * [4] - Display Device Line Refresh Order + * [3] - RGB/BGR Order + * [2] - Display Data Latch Data Order + * [1] - Flip Horizontal + * [0] - Flip Vertical + */ + sh_mipi_dcs_param(ch->chan, MIPI_DCS_SET_ADDRESS_MODE, 0x00); + /* cf. set_data_lines() */ + sh_mipi_dcs_param(ch->chan, MIPI_DCS_SET_PIXEL_FORMAT, + pixfmt << 4); + sh_mipi_dcs(ch->chan, MIPI_DCS_SET_DISPLAY_ON); + + return 0; +} + +static int __init sh_mipi_probe(struct platform_device *pdev) +{ + struct sh_mipi *mipi; + struct sh_mipi_dsi_info *pdata = pdev->dev.platform_data; + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + unsigned long rate, f_current; + int idx = pdev->id, ret; + char dsip_clk[] = "dsi.p_clk"; + + if (!res || idx >= ARRAY_SIZE(mipi_dsi) || !pdata) + return -ENODEV; + + mutex_lock(&array_lock); + if (idx < 0) + for (idx = 0; idx < ARRAY_SIZE(mipi_dsi) && mipi_dsi[idx]; idx++) + ; + + if (idx == ARRAY_SIZE(mipi_dsi)) { + ret = -EBUSY; + goto efindslot; + } + + mipi = kzalloc(sizeof(*mipi), GFP_KERNEL); + if (!mipi) { + ret = -ENOMEM; + goto ealloc; + } + + if (!request_mem_region(res->start, resource_size(res), pdev->name)) { + dev_err(&pdev->dev, "MIPI register region already claimed\n"); + ret = -EBUSY; + goto ereqreg; + } + + mipi->base = ioremap(res->start, resource_size(res)); + if (!mipi->base) { + ret = -ENOMEM; + goto emap; + } + + mipi->dsit_clk = clk_get(&pdev->dev, "dsit_clk"); + if (IS_ERR(mipi->dsit_clk)) { + ret = PTR_ERR(mipi->dsit_clk); + goto eclktget; + } + + f_current = clk_get_rate(mipi->dsit_clk); + /* 80MHz required by the datasheet */ + rate = clk_round_rate(mipi->dsit_clk, 80000000); + if (rate > 0 && rate != f_current) + ret = clk_set_rate(mipi->dsit_clk, rate); + else + ret = rate; + if (ret < 0) + goto esettrate; + + dev_dbg(&pdev->dev, "DSI-T clk %lu -> %lu\n", f_current, rate); + + sprintf(dsip_clk, "dsi%1.1dp_clk", idx); + mipi->dsip_clk = clk_get(&pdev->dev, dsip_clk); + if (IS_ERR(mipi->dsip_clk)) { + ret = PTR_ERR(mipi->dsip_clk); + goto eclkpget; + } + + f_current = clk_get_rate(mipi->dsip_clk); + /* Between 10 and 50MHz */ + rate = clk_round_rate(mipi->dsip_clk, 24000000); + if (rate > 0 && rate != f_current) + ret = clk_set_rate(mipi->dsip_clk, rate); + else + ret = rate; + if (ret < 0) + goto esetprate; + + dev_dbg(&pdev->dev, "DSI-P clk %lu -> %lu\n", f_current, rate); + + msleep(10); + + ret = clk_enable(mipi->dsit_clk); + if (ret < 0) + goto eclkton; + + ret = clk_enable(mipi->dsip_clk); + if (ret < 0) + goto eclkpon; + + mipi_dsi[idx] = mipi; + + ret = sh_mipi_setup(mipi, pdata); + if (ret < 0) + goto emipisetup; + + mutex_unlock(&array_lock); + platform_set_drvdata(pdev, mipi); + + /* Set up LCDC callbacks */ + pdata->lcd_chan->board_cfg.board_data = mipi; + pdata->lcd_chan->board_cfg.display_on = mipi_display_on; + pdata->lcd_chan->board_cfg.display_off = mipi_display_off; + + return 0; + +emipisetup: + mipi_dsi[idx] = NULL; + clk_disable(mipi->dsip_clk); +eclkpon: + clk_disable(mipi->dsit_clk); +eclkton: +esetprate: + clk_put(mipi->dsip_clk); +eclkpget: +esettrate: + clk_put(mipi->dsit_clk); +eclktget: + iounmap(mipi->base); +emap: + release_mem_region(res->start, resource_size(res)); +ereqreg: + kfree(mipi); +ealloc: +efindslot: + mutex_unlock(&array_lock); + + return ret; +} + +static int __exit sh_mipi_remove(struct platform_device *pdev) +{ + struct sh_mipi_dsi_info *pdata = pdev->dev.platform_data; + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct sh_mipi *mipi = platform_get_drvdata(pdev); + int i, ret; + + mutex_lock(&array_lock); + + for (i = 0; i < ARRAY_SIZE(mipi_dsi) && mipi_dsi[i] != mipi; i++) + ; + + if (i == ARRAY_SIZE(mipi_dsi)) { + ret = -EINVAL; + } else { + ret = 0; + mipi_dsi[i] = NULL; + } + + mutex_unlock(&array_lock); + + if (ret < 0) + return ret; + + pdata->lcd_chan->board_cfg.display_on = NULL; + pdata->lcd_chan->board_cfg.display_off = NULL; + pdata->lcd_chan->board_cfg.board_data = NULL; + + clk_disable(mipi->dsip_clk); + clk_disable(mipi->dsit_clk); + clk_put(mipi->dsit_clk); + clk_put(mipi->dsip_clk); + iounmap(mipi->base); + if (res) + release_mem_region(res->start, resource_size(res)); + platform_set_drvdata(pdev, NULL); + kfree(mipi); + + return 0; +} + +static struct platform_driver sh_mipi_driver = { + .remove = __exit_p(sh_mipi_remove), + .shutdown = sh_mipi_shutdown, + .driver = { + .name = "sh-mipi-dsi", + }, +}; + +static int __init sh_mipi_init(void) +{ + return platform_driver_probe(&sh_mipi_driver, sh_mipi_probe); +} +module_init(sh_mipi_init); + +static void __exit sh_mipi_exit(void) +{ + platform_driver_unregister(&sh_mipi_driver); +} +module_exit(sh_mipi_exit); + +MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); +MODULE_DESCRIPTION("SuperH / ARM-shmobile MIPI DSI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c new file mode 100644 index 00000000000..2fde08cc66b --- /dev/null +++ b/drivers/video/sh_mobile_hdmi.c @@ -0,0 +1,1028 @@ +/* + * SH-Mobile High-Definition Multimedia Interface (HDMI) driver + * for SLISHDMI13T and SLIPHDMIT IP cores + * + * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de> + * + * 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. + */ + +#include <linux/clk.h> +#include <linux/console.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/workqueue.h> + +#include <video/sh_mobile_hdmi.h> +#include <video/sh_mobile_lcdc.h> + +#define HDMI_SYSTEM_CTRL 0x00 /* System control */ +#define HDMI_L_R_DATA_SWAP_CTRL_RPKT 0x01 /* L/R data swap control, + bits 19..16 of 20-bit N for Audio Clock Regeneration packet */ +#define HDMI_20_BIT_N_FOR_AUDIO_RPKT_15_8 0x02 /* bits 15..8 of 20-bit N for Audio Clock Regeneration packet */ +#define HDMI_20_BIT_N_FOR_AUDIO_RPKT_7_0 0x03 /* bits 7..0 of 20-bit N for Audio Clock Regeneration packet */ +#define HDMI_SPDIF_AUDIO_SAMP_FREQ_CTS 0x04 /* SPDIF audio sampling frequency, + bits 19..16 of Internal CTS */ +#define HDMI_INTERNAL_CTS_15_8 0x05 /* bits 15..8 of Internal CTS */ +#define HDMI_INTERNAL_CTS_7_0 0x06 /* bits 7..0 of Internal CTS */ +#define HDMI_EXTERNAL_CTS_19_16 0x07 /* External CTS */ +#define HDMI_EXTERNAL_CTS_15_8 0x08 /* External CTS */ +#define HDMI_EXTERNAL_CTS_7_0 0x09 /* External CTS */ +#define HDMI_AUDIO_SETTING_1 0x0A /* Audio setting.1 */ +#define HDMI_AUDIO_SETTING_2 0x0B /* Audio setting.2 */ +#define HDMI_I2S_AUDIO_SET 0x0C /* I2S audio setting */ +#define HDMI_DSD_AUDIO_SET 0x0D /* DSD audio setting */ +#define HDMI_DEBUG_MONITOR_1 0x0E /* Debug monitor.1 */ +#define HDMI_DEBUG_MONITOR_2 0x0F /* Debug monitor.2 */ +#define HDMI_I2S_INPUT_PIN_SWAP 0x10 /* I2S input pin swap */ +#define HDMI_AUDIO_STATUS_BITS_SETTING_1 0x11 /* Audio status bits setting.1 */ +#define HDMI_AUDIO_STATUS_BITS_SETTING_2 0x12 /* Audio status bits setting.2 */ +#define HDMI_CATEGORY_CODE 0x13 /* Category code */ +#define HDMI_SOURCE_NUM_AUDIO_WORD_LEN 0x14 /* Source number/Audio word length */ +#define HDMI_AUDIO_VIDEO_SETTING_1 0x15 /* Audio/Video setting.1 */ +#define HDMI_VIDEO_SETTING_1 0x16 /* Video setting.1 */ +#define HDMI_DEEP_COLOR_MODES 0x17 /* Deep Color Modes */ + +/* 12 16- and 10-bit Color space conversion parameters: 0x18..0x2f */ +#define HDMI_COLOR_SPACE_CONVERSION_PARAMETERS 0x18 + +#define HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS 0x30 /* External video parameter settings */ +#define HDMI_EXTERNAL_H_TOTAL_7_0 0x31 /* External horizontal total (LSB) */ +#define HDMI_EXTERNAL_H_TOTAL_11_8 0x32 /* External horizontal total (MSB) */ +#define HDMI_EXTERNAL_H_BLANK_7_0 0x33 /* External horizontal blank (LSB) */ +#define HDMI_EXTERNAL_H_BLANK_9_8 0x34 /* External horizontal blank (MSB) */ +#define HDMI_EXTERNAL_H_DELAY_7_0 0x35 /* External horizontal delay (LSB) */ +#define HDMI_EXTERNAL_H_DELAY_9_8 0x36 /* External horizontal delay (MSB) */ +#define HDMI_EXTERNAL_H_DURATION_7_0 0x37 /* External horizontal duration (LSB) */ +#define HDMI_EXTERNAL_H_DURATION_9_8 0x38 /* External horizontal duration (MSB) */ +#define HDMI_EXTERNAL_V_TOTAL_7_0 0x39 /* External vertical total (LSB) */ +#define HDMI_EXTERNAL_V_TOTAL_9_8 0x3A /* External vertical total (MSB) */ +#define HDMI_AUDIO_VIDEO_SETTING_2 0x3B /* Audio/Video setting.2 */ +#define HDMI_EXTERNAL_V_BLANK 0x3D /* External vertical blank */ +#define HDMI_EXTERNAL_V_DELAY 0x3E /* External vertical delay */ +#define HDMI_EXTERNAL_V_DURATION 0x3F /* External vertical duration */ +#define HDMI_CTRL_PKT_MANUAL_SEND_CONTROL 0x40 /* Control packet manual send control */ +#define HDMI_CTRL_PKT_AUTO_SEND 0x41 /* Control packet auto send with VSYNC control */ +#define HDMI_AUTO_CHECKSUM_OPTION 0x42 /* Auto checksum option */ +#define HDMI_VIDEO_SETTING_2 0x45 /* Video setting.2 */ +#define HDMI_OUTPUT_OPTION 0x46 /* Output option */ +#define HDMI_SLIPHDMIT_PARAM_OPTION 0x51 /* SLIPHDMIT parameter option */ +#define HDMI_HSYNC_PMENT_AT_EMB_7_0 0x52 /* HSYNC placement at embedded sync (LSB) */ +#define HDMI_HSYNC_PMENT_AT_EMB_15_8 0x53 /* HSYNC placement at embedded sync (MSB) */ +#define HDMI_VSYNC_PMENT_AT_EMB_7_0 0x54 /* VSYNC placement at embedded sync (LSB) */ +#define HDMI_VSYNC_PMENT_AT_EMB_14_8 0x55 /* VSYNC placement at embedded sync (MSB) */ +#define HDMI_SLIPHDMIT_PARAM_SETTINGS_1 0x56 /* SLIPHDMIT parameter settings.1 */ +#define HDMI_SLIPHDMIT_PARAM_SETTINGS_2 0x57 /* SLIPHDMIT parameter settings.2 */ +#define HDMI_SLIPHDMIT_PARAM_SETTINGS_3 0x58 /* SLIPHDMIT parameter settings.3 */ +#define HDMI_SLIPHDMIT_PARAM_SETTINGS_5 0x59 /* SLIPHDMIT parameter settings.5 */ +#define HDMI_SLIPHDMIT_PARAM_SETTINGS_6 0x5A /* SLIPHDMIT parameter settings.6 */ +#define HDMI_SLIPHDMIT_PARAM_SETTINGS_7 0x5B /* SLIPHDMIT parameter settings.7 */ +#define HDMI_SLIPHDMIT_PARAM_SETTINGS_8 0x5C /* SLIPHDMIT parameter settings.8 */ +#define HDMI_SLIPHDMIT_PARAM_SETTINGS_9 0x5D /* SLIPHDMIT parameter settings.9 */ +#define HDMI_SLIPHDMIT_PARAM_SETTINGS_10 0x5E /* SLIPHDMIT parameter settings.10 */ +#define HDMI_CTRL_PKT_BUF_INDEX 0x5F /* Control packet buffer index */ +#define HDMI_CTRL_PKT_BUF_ACCESS_HB0 0x60 /* Control packet data buffer access window - HB0 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_HB1 0x61 /* Control packet data buffer access window - HB1 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_HB2 0x62 /* Control packet data buffer access window - HB2 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB0 0x63 /* Control packet data buffer access window - PB0 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB1 0x64 /* Control packet data buffer access window - PB1 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB2 0x65 /* Control packet data buffer access window - PB2 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB3 0x66 /* Control packet data buffer access window - PB3 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB4 0x67 /* Control packet data buffer access window - PB4 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB5 0x68 /* Control packet data buffer access window - PB5 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB6 0x69 /* Control packet data buffer access window - PB6 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB7 0x6A /* Control packet data buffer access window - PB7 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB8 0x6B /* Control packet data buffer access window - PB8 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB9 0x6C /* Control packet data buffer access window - PB9 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB10 0x6D /* Control packet data buffer access window - PB10 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB11 0x6E /* Control packet data buffer access window - PB11 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB12 0x6F /* Control packet data buffer access window - PB12 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB13 0x70 /* Control packet data buffer access window - PB13 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB14 0x71 /* Control packet data buffer access window - PB14 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB15 0x72 /* Control packet data buffer access window - PB15 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB16 0x73 /* Control packet data buffer access window - PB16 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB17 0x74 /* Control packet data buffer access window - PB17 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB18 0x75 /* Control packet data buffer access window - PB18 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB19 0x76 /* Control packet data buffer access window - PB19 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB20 0x77 /* Control packet data buffer access window - PB20 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB21 0x78 /* Control packet data buffer access window - PB21 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB22 0x79 /* Control packet data buffer access window - PB22 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB23 0x7A /* Control packet data buffer access window - PB23 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB24 0x7B /* Control packet data buffer access window - PB24 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB25 0x7C /* Control packet data buffer access window - PB25 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB26 0x7D /* Control packet data buffer access window - PB26 */ +#define HDMI_CTRL_PKT_BUF_ACCESS_PB27 0x7E /* Control packet data buffer access window - PB27 */ +#define HDMI_EDID_KSV_FIFO_ACCESS_WINDOW 0x80 /* EDID/KSV FIFO access window */ +#define HDMI_DDC_BUS_ACCESS_FREQ_CTRL_7_0 0x81 /* DDC bus access frequency control (LSB) */ +#define HDMI_DDC_BUS_ACCESS_FREQ_CTRL_15_8 0x82 /* DDC bus access frequency control (MSB) */ +#define HDMI_INTERRUPT_MASK_1 0x92 /* Interrupt mask.1 */ +#define HDMI_INTERRUPT_MASK_2 0x93 /* Interrupt mask.2 */ +#define HDMI_INTERRUPT_STATUS_1 0x94 /* Interrupt status.1 */ +#define HDMI_INTERRUPT_STATUS_2 0x95 /* Interrupt status.2 */ +#define HDMI_INTERRUPT_MASK_3 0x96 /* Interrupt mask.3 */ +#define HDMI_INTERRUPT_MASK_4 0x97 /* Interrupt mask.4 */ +#define HDMI_INTERRUPT_STATUS_3 0x98 /* Interrupt status.3 */ +#define HDMI_INTERRUPT_STATUS_4 0x99 /* Interrupt status.4 */ +#define HDMI_SOFTWARE_HDCP_CONTROL_1 0x9A /* Software HDCP control.1 */ +#define HDMI_FRAME_COUNTER 0x9C /* Frame counter */ +#define HDMI_FRAME_COUNTER_FOR_RI_CHECK 0x9D /* Frame counter for Ri check */ +#define HDMI_HDCP_CONTROL 0xAF /* HDCP control */ +#define HDMI_RI_FRAME_COUNT_REGISTER 0xB2 /* Ri frame count register */ +#define HDMI_DDC_BUS_CONTROL 0xB7 /* DDC bus control */ +#define HDMI_HDCP_STATUS 0xB8 /* HDCP status */ +#define HDMI_SHA0 0xB9 /* sha0 */ +#define HDMI_SHA1 0xBA /* sha1 */ +#define HDMI_SHA2 0xBB /* sha2 */ +#define HDMI_SHA3 0xBC /* sha3 */ +#define HDMI_SHA4 0xBD /* sha4 */ +#define HDMI_BCAPS_READ 0xBE /* BCAPS read / debug */ +#define HDMI_AKSV_BKSV_7_0_MONITOR 0xBF /* AKSV/BKSV[7:0] monitor */ +#define HDMI_AKSV_BKSV_15_8_MONITOR 0xC0 /* AKSV/BKSV[15:8] monitor */ +#define HDMI_AKSV_BKSV_23_16_MONITOR 0xC1 /* AKSV/BKSV[23:16] monitor */ +#define HDMI_AKSV_BKSV_31_24_MONITOR 0xC2 /* AKSV/BKSV[31:24] monitor */ +#define HDMI_AKSV_BKSV_39_32_MONITOR 0xC3 /* AKSV/BKSV[39:32] monitor */ +#define HDMI_EDID_SEGMENT_POINTER 0xC4 /* EDID segment pointer */ +#define HDMI_EDID_WORD_ADDRESS 0xC5 /* EDID word address */ +#define HDMI_EDID_DATA_FIFO_ADDRESS 0xC6 /* EDID data FIFO address */ +#define HDMI_NUM_OF_HDMI_DEVICES 0xC7 /* Number of HDMI devices */ +#define HDMI_HDCP_ERROR_CODE 0xC8 /* HDCP error code */ +#define HDMI_100MS_TIMER_SET 0xC9 /* 100ms timer setting */ +#define HDMI_5SEC_TIMER_SET 0xCA /* 5sec timer setting */ +#define HDMI_RI_READ_COUNT 0xCB /* Ri read count */ +#define HDMI_AN_SEED 0xCC /* An seed */ +#define HDMI_MAX_NUM_OF_RCIVRS_ALLOWED 0xCD /* Maximum number of receivers allowed */ +#define HDMI_HDCP_MEMORY_ACCESS_CONTROL_1 0xCE /* HDCP memory access control.1 */ +#define HDMI_HDCP_MEMORY_ACCESS_CONTROL_2 0xCF /* HDCP memory access control.2 */ +#define HDMI_HDCP_CONTROL_2 0xD0 /* HDCP Control 2 */ +#define HDMI_HDCP_KEY_MEMORY_CONTROL 0xD2 /* HDCP Key Memory Control */ +#define HDMI_COLOR_SPACE_CONV_CONFIG_1 0xD3 /* Color space conversion configuration.1 */ +#define HDMI_VIDEO_SETTING_3 0xD4 /* Video setting.3 */ +#define HDMI_RI_7_0 0xD5 /* Ri[7:0] */ +#define HDMI_RI_15_8 0xD6 /* Ri[15:8] */ +#define HDMI_PJ 0xD7 /* Pj */ +#define HDMI_SHA_RD 0xD8 /* sha_rd */ +#define HDMI_RI_7_0_SAVED 0xD9 /* Ri[7:0] saved */ +#define HDMI_RI_15_8_SAVED 0xDA /* Ri[15:8] saved */ +#define HDMI_PJ_SAVED 0xDB /* Pj saved */ +#define HDMI_NUM_OF_DEVICES 0xDC /* Number of devices */ +#define HDMI_HOT_PLUG_MSENS_STATUS 0xDF /* Hot plug/MSENS status */ +#define HDMI_BCAPS_WRITE 0xE0 /* bcaps */ +#define HDMI_BSTAT_7_0 0xE1 /* bstat[7:0] */ +#define HDMI_BSTAT_15_8 0xE2 /* bstat[15:8] */ +#define HDMI_BKSV_7_0 0xE3 /* bksv[7:0] */ +#define HDMI_BKSV_15_8 0xE4 /* bksv[15:8] */ +#define HDMI_BKSV_23_16 0xE5 /* bksv[23:16] */ +#define HDMI_BKSV_31_24 0xE6 /* bksv[31:24] */ +#define HDMI_BKSV_39_32 0xE7 /* bksv[39:32] */ +#define HDMI_AN_7_0 0xE8 /* An[7:0] */ +#define HDMI_AN_15_8 0xE9 /* An [15:8] */ +#define HDMI_AN_23_16 0xEA /* An [23:16] */ +#define HDMI_AN_31_24 0xEB /* An [31:24] */ +#define HDMI_AN_39_32 0xEC /* An [39:32] */ +#define HDMI_AN_47_40 0xED /* An [47:40] */ +#define HDMI_AN_55_48 0xEE /* An [55:48] */ +#define HDMI_AN_63_56 0xEF /* An [63:56] */ +#define HDMI_PRODUCT_ID 0xF0 /* Product ID */ +#define HDMI_REVISION_ID 0xF1 /* Revision ID */ +#define HDMI_TEST_MODE 0xFE /* Test mode */ + +enum hotplug_state { + HDMI_HOTPLUG_DISCONNECTED, + HDMI_HOTPLUG_CONNECTED, + HDMI_HOTPLUG_EDID_DONE, +}; + +struct sh_hdmi { + void __iomem *base; + enum hotplug_state hp_state; + struct clk *hdmi_clk; + struct device *dev; + struct fb_info *info; + struct delayed_work edid_work; + struct fb_var_screeninfo var; +}; + +static void hdmi_write(struct sh_hdmi *hdmi, u8 data, u8 reg) +{ + iowrite8(data, hdmi->base + reg); +} + +static u8 hdmi_read(struct sh_hdmi *hdmi, u8 reg) +{ + return ioread8(hdmi->base + reg); +} + +/* External video parameter settings */ +static void hdmi_external_video_param(struct sh_hdmi *hdmi) +{ + struct fb_var_screeninfo *var = &hdmi->var; + u16 htotal, hblank, hdelay, vtotal, vblank, vdelay, voffset; + u8 sync = 0; + + htotal = var->xres + var->right_margin + var->left_margin + var->hsync_len; + + hdelay = var->hsync_len + var->left_margin; + hblank = var->right_margin + hdelay; + + /* + * Vertical timing looks a bit different in Figure 18, + * but let's try the same first by setting offset = 0 + */ + vtotal = var->yres + var->upper_margin + var->lower_margin + var->vsync_len; + + vdelay = var->vsync_len + var->upper_margin; + vblank = var->lower_margin + vdelay; + voffset = min(var->upper_margin / 2, 6U); + + /* + * [3]: VSYNC polarity: Positive + * [2]: HSYNC polarity: Positive + * [1]: Interlace/Progressive: Progressive + * [0]: External video settings enable: used. + */ + if (var->sync & FB_SYNC_HOR_HIGH_ACT) + sync |= 4; + if (var->sync & FB_SYNC_VERT_HIGH_ACT) + sync |= 8; + + pr_debug("H: %u, %u, %u, %u; V: %u, %u, %u, %u; sync 0x%x\n", + htotal, hblank, hdelay, var->hsync_len, + vtotal, vblank, vdelay, var->vsync_len, sync); + + hdmi_write(hdmi, sync | (voffset << 4), HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS); + + hdmi_write(hdmi, htotal, HDMI_EXTERNAL_H_TOTAL_7_0); + hdmi_write(hdmi, htotal >> 8, HDMI_EXTERNAL_H_TOTAL_11_8); + + hdmi_write(hdmi, hblank, HDMI_EXTERNAL_H_BLANK_7_0); + hdmi_write(hdmi, hblank >> 8, HDMI_EXTERNAL_H_BLANK_9_8); + + hdmi_write(hdmi, hdelay, HDMI_EXTERNAL_H_DELAY_7_0); + hdmi_write(hdmi, hdelay >> 8, HDMI_EXTERNAL_H_DELAY_9_8); + + hdmi_write(hdmi, var->hsync_len, HDMI_EXTERNAL_H_DURATION_7_0); + hdmi_write(hdmi, var->hsync_len >> 8, HDMI_EXTERNAL_H_DURATION_9_8); + + hdmi_write(hdmi, vtotal, HDMI_EXTERNAL_V_TOTAL_7_0); + hdmi_write(hdmi, vtotal >> 8, HDMI_EXTERNAL_V_TOTAL_9_8); + + hdmi_write(hdmi, vblank, HDMI_EXTERNAL_V_BLANK); + + hdmi_write(hdmi, vdelay, HDMI_EXTERNAL_V_DELAY); + + hdmi_write(hdmi, var->vsync_len, HDMI_EXTERNAL_V_DURATION); + + /* Set bit 0 of HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS here for manual mode */ +} + +/** + * sh_hdmi_video_config() + */ +static void sh_hdmi_video_config(struct sh_hdmi *hdmi) +{ + /* + * [7:4]: Audio sampling frequency: 48kHz + * [3:1]: Input video format: RGB and YCbCr 4:4:4 (Y on Green) + * [0]: Internal/External DE select: internal + */ + hdmi_write(hdmi, 0x20, HDMI_AUDIO_VIDEO_SETTING_1); + + /* + * [7:6]: Video output format: RGB 4:4:4 + * [5:4]: Input video data width: 8 bit + * [3:1]: EAV/SAV location: channel 1 + * [0]: Video input color space: RGB + */ + hdmi_write(hdmi, 0x34, HDMI_VIDEO_SETTING_1); + + /* + * [7:6]: Together with bit [6] of HDMI_AUDIO_VIDEO_SETTING_2, which is + * left at 0 by default, this configures 24bpp and sets the Color Depth + * (CD) field in the General Control Packet + */ + hdmi_write(hdmi, 0x20, HDMI_DEEP_COLOR_MODES); +} + +/** + * sh_hdmi_audio_config() + */ +static void sh_hdmi_audio_config(struct sh_hdmi *hdmi) +{ + /* + * [7:4] L/R data swap control + * [3:0] appropriate N[19:16] + */ + hdmi_write(hdmi, 0x00, HDMI_L_R_DATA_SWAP_CTRL_RPKT); + /* appropriate N[15:8] */ + hdmi_write(hdmi, 0x18, HDMI_20_BIT_N_FOR_AUDIO_RPKT_15_8); + /* appropriate N[7:0] */ + hdmi_write(hdmi, 0x00, HDMI_20_BIT_N_FOR_AUDIO_RPKT_7_0); + + /* [7:4] 48 kHz SPDIF not used */ + hdmi_write(hdmi, 0x20, HDMI_SPDIF_AUDIO_SAMP_FREQ_CTS); + + /* + * [6:5] set required down sampling rate if required + * [4:3] set required audio source + */ + hdmi_write(hdmi, 0x00, HDMI_AUDIO_SETTING_1); + + /* [3:0] set sending channel number for channel status */ + hdmi_write(hdmi, 0x40, HDMI_AUDIO_SETTING_2); + + /* + * [5:2] set valid I2S source input pin + * [1:0] set input I2S source mode + */ + hdmi_write(hdmi, 0x04, HDMI_I2S_AUDIO_SET); + + /* [7:4] set valid DSD source input pin */ + hdmi_write(hdmi, 0x00, HDMI_DSD_AUDIO_SET); + + /* [7:0] set appropriate I2S input pin swap settings if required */ + hdmi_write(hdmi, 0x00, HDMI_I2S_INPUT_PIN_SWAP); + + /* + * [7] set validity bit for channel status + * [3:0] set original sample frequency for channel status + */ + hdmi_write(hdmi, 0x00, HDMI_AUDIO_STATUS_BITS_SETTING_1); + + /* + * [7] set value for channel status + * [6] set value for channel status + * [5] set copyright bit for channel status + * [4:2] set additional information for channel status + * [1:0] set clock accuracy for channel status + */ + hdmi_write(hdmi, 0x00, HDMI_AUDIO_STATUS_BITS_SETTING_2); + + /* [7:0] set category code for channel status */ + hdmi_write(hdmi, 0x00, HDMI_CATEGORY_CODE); + + /* + * [7:4] set source number for channel status + * [3:0] set word length for channel status + */ + hdmi_write(hdmi, 0x00, HDMI_SOURCE_NUM_AUDIO_WORD_LEN); + + /* [7:4] set sample frequency for channel status */ + hdmi_write(hdmi, 0x20, HDMI_AUDIO_VIDEO_SETTING_1); +} + +/** + * sh_hdmi_phy_config() + */ +static void sh_hdmi_phy_config(struct sh_hdmi *hdmi) +{ + /* 720p, 8bit, 74.25MHz. Might need to be adjusted for other formats */ + hdmi_write(hdmi, 0x19, HDMI_SLIPHDMIT_PARAM_SETTINGS_1); + hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_2); + hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_3); + /* PLLA_CONFIG[7:0]: VCO gain, VCO offset, LPF resistance[0] */ + hdmi_write(hdmi, 0x44, HDMI_SLIPHDMIT_PARAM_SETTINGS_5); + hdmi_write(hdmi, 0x32, HDMI_SLIPHDMIT_PARAM_SETTINGS_6); + hdmi_write(hdmi, 0x4A, HDMI_SLIPHDMIT_PARAM_SETTINGS_7); + hdmi_write(hdmi, 0x0E, HDMI_SLIPHDMIT_PARAM_SETTINGS_8); + hdmi_write(hdmi, 0x25, HDMI_SLIPHDMIT_PARAM_SETTINGS_9); + hdmi_write(hdmi, 0x04, HDMI_SLIPHDMIT_PARAM_SETTINGS_10); +} + +/** + * sh_hdmi_avi_infoframe_setup() - Auxiliary Video Information InfoFrame CONTROL PACKET + */ +static void sh_hdmi_avi_infoframe_setup(struct sh_hdmi *hdmi) +{ + /* AVI InfoFrame */ + hdmi_write(hdmi, 0x06, HDMI_CTRL_PKT_BUF_INDEX); + + /* Packet Type = 0x82 */ + hdmi_write(hdmi, 0x82, HDMI_CTRL_PKT_BUF_ACCESS_HB0); + + /* Version = 0x02 */ + hdmi_write(hdmi, 0x02, HDMI_CTRL_PKT_BUF_ACCESS_HB1); + + /* Length = 13 (0x0D) */ + hdmi_write(hdmi, 0x0D, HDMI_CTRL_PKT_BUF_ACCESS_HB2); + + /* N. A. Checksum */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0); + + /* + * Y = RGB + * A0 = No Data + * B = Bar Data not valid + * S = No Data + */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB1); + + /* + * C = No Data + * M = 16:9 Picture Aspect Ratio + * R = Same as picture aspect ratio + */ + hdmi_write(hdmi, 0x28, HDMI_CTRL_PKT_BUF_ACCESS_PB2); + + /* + * ITC = No Data + * EC = xvYCC601 + * Q = Default (depends on video format) + * SC = No Known non_uniform Scaling + */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB3); + + /* + * VIC = 1280 x 720p: ignored if external config is used + * Send 2 for 720 x 480p, 16 for 1080p + */ + hdmi_write(hdmi, 4, HDMI_CTRL_PKT_BUF_ACCESS_PB4); + + /* PR = No Repetition */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB5); + + /* Line Number of End of Top Bar (lower 8 bits) */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB6); + + /* Line Number of End of Top Bar (upper 8 bits) */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB7); + + /* Line Number of Start of Bottom Bar (lower 8 bits) */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB8); + + /* Line Number of Start of Bottom Bar (upper 8 bits) */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB9); + + /* Pixel Number of End of Left Bar (lower 8 bits) */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB10); + + /* Pixel Number of End of Left Bar (upper 8 bits) */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB11); + + /* Pixel Number of Start of Right Bar (lower 8 bits) */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB12); + + /* Pixel Number of Start of Right Bar (upper 8 bits) */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB13); +} + +/** + * sh_hdmi_audio_infoframe_setup() - Audio InfoFrame of CONTROL PACKET + */ +static void sh_hdmi_audio_infoframe_setup(struct sh_hdmi *hdmi) +{ + /* Audio InfoFrame */ + hdmi_write(hdmi, 0x08, HDMI_CTRL_PKT_BUF_INDEX); + + /* Packet Type = 0x84 */ + hdmi_write(hdmi, 0x84, HDMI_CTRL_PKT_BUF_ACCESS_HB0); + + /* Version Number = 0x01 */ + hdmi_write(hdmi, 0x01, HDMI_CTRL_PKT_BUF_ACCESS_HB1); + + /* 0 Length = 10 (0x0A) */ + hdmi_write(hdmi, 0x0A, HDMI_CTRL_PKT_BUF_ACCESS_HB2); + + /* n. a. Checksum */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0); + + /* Audio Channel Count = Refer to Stream Header */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB1); + + /* Refer to Stream Header */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB2); + + /* Format depends on coding type (i.e. CT0...CT3) */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB3); + + /* Speaker Channel Allocation = Front Right + Front Left */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB4); + + /* Level Shift Value = 0 dB, Down - mix is permitted or no information */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB5); + + /* Reserved (0) */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB6); + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB7); + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB8); + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB9); + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB10); +} + +/** + * sh_hdmi_gamut_metadata_setup() - Gamut Metadata Packet of CONTROL PACKET + */ +static void sh_hdmi_gamut_metadata_setup(struct sh_hdmi *hdmi) +{ + int i; + + /* Gamut Metadata Packet */ + hdmi_write(hdmi, 0x04, HDMI_CTRL_PKT_BUF_INDEX); + + /* Packet Type = 0x0A */ + hdmi_write(hdmi, 0x0A, HDMI_CTRL_PKT_BUF_ACCESS_HB0); + /* Gamut Packet is not used, so default value */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1); + /* Gamut Packet is not used, so default value */ + hdmi_write(hdmi, 0x10, HDMI_CTRL_PKT_BUF_ACCESS_HB2); + + /* GBD bytes 0 through 27 */ + for (i = 0; i <= 27; i++) + /* HDMI_CTRL_PKT_BUF_ACCESS_PB0_63H - PB27_7EH */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i); +} + +/** + * sh_hdmi_acp_setup() - Audio Content Protection Packet (ACP) + */ +static void sh_hdmi_acp_setup(struct sh_hdmi *hdmi) +{ + int i; + + /* Audio Content Protection Packet (ACP) */ + hdmi_write(hdmi, 0x01, HDMI_CTRL_PKT_BUF_INDEX); + + /* Packet Type = 0x04 */ + hdmi_write(hdmi, 0x04, HDMI_CTRL_PKT_BUF_ACCESS_HB0); + /* ACP_Type */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1); + /* Reserved (0) */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB2); + + /* GBD bytes 0 through 27 */ + for (i = 0; i <= 27; i++) + /* HDMI_CTRL_PKT_BUF_ACCESS_PB0 - PB27 */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i); +} + +/** + * sh_hdmi_isrc1_setup() - ISRC1 Packet + */ +static void sh_hdmi_isrc1_setup(struct sh_hdmi *hdmi) +{ + int i; + + /* ISRC1 Packet */ + hdmi_write(hdmi, 0x02, HDMI_CTRL_PKT_BUF_INDEX); + + /* Packet Type = 0x05 */ + hdmi_write(hdmi, 0x05, HDMI_CTRL_PKT_BUF_ACCESS_HB0); + /* ISRC_Cont, ISRC_Valid, Reserved (0), ISRC_Status */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1); + /* Reserved (0) */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB2); + + /* PB0 UPC_EAN_ISRC_0-15 */ + /* Bytes PB16-PB27 shall be set to a value of 0. */ + for (i = 0; i <= 27; i++) + /* HDMI_CTRL_PKT_BUF_ACCESS_PB0 - PB27 */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i); +} + +/** + * sh_hdmi_isrc2_setup() - ISRC2 Packet + */ +static void sh_hdmi_isrc2_setup(struct sh_hdmi *hdmi) +{ + int i; + + /* ISRC2 Packet */ + hdmi_write(hdmi, 0x03, HDMI_CTRL_PKT_BUF_INDEX); + + /* HB0 Packet Type = 0x06 */ + hdmi_write(hdmi, 0x06, HDMI_CTRL_PKT_BUF_ACCESS_HB0); + /* Reserved (0) */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1); + /* Reserved (0) */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB2); + + /* PB0 UPC_EAN_ISRC_16-31 */ + /* Bytes PB16-PB27 shall be set to a value of 0. */ + for (i = 0; i <= 27; i++) + /* HDMI_CTRL_PKT_BUF_ACCESS_PB0 - PB27 */ + hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i); +} + +/** + * sh_hdmi_configure() - Initialise HDMI for output + */ +static void sh_hdmi_configure(struct sh_hdmi *hdmi) +{ + /* Configure video format */ + sh_hdmi_video_config(hdmi); + + /* Configure audio format */ + sh_hdmi_audio_config(hdmi); + + /* Configure PHY */ + sh_hdmi_phy_config(hdmi); + + /* Auxiliary Video Information (AVI) InfoFrame */ + sh_hdmi_avi_infoframe_setup(hdmi); + + /* Audio InfoFrame */ + sh_hdmi_audio_infoframe_setup(hdmi); + + /* Gamut Metadata packet */ + sh_hdmi_gamut_metadata_setup(hdmi); + + /* Audio Content Protection (ACP) Packet */ + sh_hdmi_acp_setup(hdmi); + + /* ISRC1 Packet */ + sh_hdmi_isrc1_setup(hdmi); + + /* ISRC2 Packet */ + sh_hdmi_isrc2_setup(hdmi); + + /* + * Control packet auto send with VSYNC control: auto send + * General control, Gamut metadata, ISRC, and ACP packets + */ + hdmi_write(hdmi, 0x8E, HDMI_CTRL_PKT_AUTO_SEND); + + /* FIXME */ + msleep(10); + + /* PS mode b->d, reset PLLA and PLLB */ + hdmi_write(hdmi, 0x4C, HDMI_SYSTEM_CTRL); + + udelay(10); + + hdmi_write(hdmi, 0x40, HDMI_SYSTEM_CTRL); +} + +static void sh_hdmi_read_edid(struct sh_hdmi *hdmi) +{ + struct fb_var_screeninfo *var = &hdmi->var; + struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; + struct fb_videomode *lcd_cfg = &pdata->lcd_chan->lcd_cfg; + unsigned long height = var->height, width = var->width; + int i; + u8 edid[128]; + + /* Read EDID */ + pr_debug("Read back EDID code:"); + for (i = 0; i < 128; i++) { + edid[i] = hdmi_read(hdmi, HDMI_EDID_KSV_FIFO_ACCESS_WINDOW); +#ifdef DEBUG + if ((i % 16) == 0) { + printk(KERN_CONT "\n"); + printk(KERN_DEBUG "%02X | %02X", i, edid[i]); + } else { + printk(KERN_CONT " %02X", edid[i]); + } +#endif + } +#ifdef DEBUG + printk(KERN_CONT "\n"); +#endif + fb_parse_edid(edid, var); + pr_debug("%u-%u-%u-%u x %u-%u-%u-%u @ %lu kHz monitor detected\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)); + + /* FIXME: Use user-provided configuration instead of EDID */ + var->width = width; + var->xres = lcd_cfg->xres; + var->xres_virtual = lcd_cfg->xres; + var->left_margin = lcd_cfg->left_margin; + var->right_margin = lcd_cfg->right_margin; + var->hsync_len = lcd_cfg->hsync_len; + var->height = height; + var->yres = lcd_cfg->yres; + var->yres_virtual = lcd_cfg->yres * 2; + var->upper_margin = lcd_cfg->upper_margin; + var->lower_margin = lcd_cfg->lower_margin; + var->vsync_len = lcd_cfg->vsync_len; + var->sync = lcd_cfg->sync; + var->pixclock = lcd_cfg->pixclock; + + hdmi_external_video_param(hdmi); +} + +static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id) +{ + struct sh_hdmi *hdmi = dev_id; + u8 status1, status2, mask1, mask2; + + /* mode_b and PLLA and PLLB reset */ + hdmi_write(hdmi, 0x2C, HDMI_SYSTEM_CTRL); + + /* How long shall reset be held? */ + udelay(10); + + /* mode_b and PLLA and PLLB reset release */ + hdmi_write(hdmi, 0x20, HDMI_SYSTEM_CTRL); + + status1 = hdmi_read(hdmi, HDMI_INTERRUPT_STATUS_1); + status2 = hdmi_read(hdmi, HDMI_INTERRUPT_STATUS_2); + + mask1 = hdmi_read(hdmi, HDMI_INTERRUPT_MASK_1); + mask2 = hdmi_read(hdmi, HDMI_INTERRUPT_MASK_2); + + /* Correct would be to ack only set bits, but the datasheet requires 0xff */ + hdmi_write(hdmi, 0xFF, HDMI_INTERRUPT_STATUS_1); + hdmi_write(hdmi, 0xFF, HDMI_INTERRUPT_STATUS_2); + + if (printk_ratelimit()) + pr_debug("IRQ #%d: Status #1: 0x%x & 0x%x, #2: 0x%x & 0x%x\n", + irq, status1, mask1, status2, mask2); + + if (!((status1 & mask1) | (status2 & mask2))) { + return IRQ_NONE; + } else if (status1 & 0xc0) { + u8 msens; + + /* Datasheet specifies 10ms... */ + udelay(500); + + msens = hdmi_read(hdmi, HDMI_HOT_PLUG_MSENS_STATUS); + pr_debug("MSENS 0x%x\n", msens); + /* Check, if hot plug & MSENS pin status are both high */ + if ((msens & 0xC0) == 0xC0) { + /* Display plug in */ + hdmi->hp_state = HDMI_HOTPLUG_CONNECTED; + + /* Set EDID word address */ + hdmi_write(hdmi, 0x00, HDMI_EDID_WORD_ADDRESS); + /* Set EDID segment pointer */ + hdmi_write(hdmi, 0x00, HDMI_EDID_SEGMENT_POINTER); + /* Enable EDID interrupt */ + hdmi_write(hdmi, 0xC6, HDMI_INTERRUPT_MASK_1); + } else if (!(status1 & 0x80)) { + /* Display unplug, beware multiple interrupts */ + if (hdmi->hp_state != HDMI_HOTPLUG_DISCONNECTED) + schedule_delayed_work(&hdmi->edid_work, 0); + + hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; + /* display_off will switch back to mode_a */ + } + } else if (status1 & 2) { + /* EDID error interrupt: retry */ + /* Set EDID word address */ + hdmi_write(hdmi, 0x00, HDMI_EDID_WORD_ADDRESS); + /* Set EDID segment pointer */ + hdmi_write(hdmi, 0x00, HDMI_EDID_SEGMENT_POINTER); + } else if (status1 & 4) { + /* Disable EDID interrupt */ + hdmi_write(hdmi, 0xC0, HDMI_INTERRUPT_MASK_1); + hdmi->hp_state = HDMI_HOTPLUG_EDID_DONE; + schedule_delayed_work(&hdmi->edid_work, msecs_to_jiffies(10)); + } + + return IRQ_HANDLED; +} + +static void hdmi_display_on(void *arg, struct fb_info *info) +{ + struct sh_hdmi *hdmi = arg; + struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; + + if (info->var.xres != 1280 || info->var.yres != 720) { + dev_warn(info->device, "Unsupported framebuffer geometry %ux%u\n", + info->var.xres, info->var.yres); + return; + } + + pr_debug("%s(%p): state %x\n", __func__, pdata->lcd_dev, info->state); + /* + * FIXME: not a good place to store fb_info. And we cannot nullify it + * even on monitor disconnect. What should the lifecycle be? + */ + hdmi->info = info; + switch (hdmi->hp_state) { + case HDMI_HOTPLUG_EDID_DONE: + /* PS mode d->e. All functions are active */ + hdmi_write(hdmi, 0x80, HDMI_SYSTEM_CTRL); + pr_debug("HDMI running\n"); + break; + case HDMI_HOTPLUG_DISCONNECTED: + info->state = FBINFO_STATE_SUSPENDED; + default: + hdmi->var = info->var; + } +} + +static void hdmi_display_off(void *arg) +{ + struct sh_hdmi *hdmi = arg; + struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; + + pr_debug("%s(%p)\n", __func__, pdata->lcd_dev); + /* PS mode e->a */ + hdmi_write(hdmi, 0x10, HDMI_SYSTEM_CTRL); +} + +/* Hotplug interrupt occurred, read EDID */ +static void edid_work_fn(struct work_struct *work) +{ + struct sh_hdmi *hdmi = container_of(work, struct sh_hdmi, edid_work.work); + struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; + + pr_debug("%s(%p): begin, hotplug status %d\n", __func__, + pdata->lcd_dev, hdmi->hp_state); + + if (!pdata->lcd_dev) + return; + + if (hdmi->hp_state == HDMI_HOTPLUG_EDID_DONE) { + pm_runtime_get_sync(hdmi->dev); + /* A device has been plugged in */ + sh_hdmi_read_edid(hdmi); + msleep(10); + sh_hdmi_configure(hdmi); + /* Switched to another (d) power-save mode */ + msleep(10); + + if (!hdmi->info) + return; + + acquire_console_sem(); + + /* HDMI plug in */ + hdmi->info->var = hdmi->var; + if (hdmi->info->state != FBINFO_STATE_RUNNING) + fb_set_suspend(hdmi->info, 0); + else + hdmi_display_on(hdmi, hdmi->info); + + release_console_sem(); + } else { + if (!hdmi->info) + return; + + acquire_console_sem(); + + /* HDMI disconnect */ + fb_set_suspend(hdmi->info, 1); + + release_console_sem(); + pm_runtime_put(hdmi->dev); + } + + pr_debug("%s(%p): end\n", __func__, pdata->lcd_dev); +} + +static int __init sh_hdmi_probe(struct platform_device *pdev) +{ + struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + int irq = platform_get_irq(pdev, 0), ret; + struct sh_hdmi *hdmi; + long rate; + + if (!res || !pdata || irq < 0) + return -ENODEV; + + hdmi = kzalloc(sizeof(*hdmi), GFP_KERNEL); + if (!hdmi) { + dev_err(&pdev->dev, "Cannot allocate device data\n"); + return -ENOMEM; + } + + hdmi->dev = &pdev->dev; + + hdmi->hdmi_clk = clk_get(&pdev->dev, "ick"); + if (IS_ERR(hdmi->hdmi_clk)) { + ret = PTR_ERR(hdmi->hdmi_clk); + dev_err(&pdev->dev, "Unable to get clock: %d\n", ret); + goto egetclk; + } + + rate = PICOS2KHZ(pdata->lcd_chan->lcd_cfg.pixclock) * 1000; + + rate = clk_round_rate(hdmi->hdmi_clk, rate); + if (rate < 0) { + ret = rate; + dev_err(&pdev->dev, "Cannot get suitable rate: %ld\n", rate); + goto erate; + } + + ret = clk_set_rate(hdmi->hdmi_clk, rate); + if (ret < 0) { + dev_err(&pdev->dev, "Cannot set rate %ld: %d\n", rate, ret); + goto erate; + } + + pr_debug("HDMI set frequency %lu\n", rate); + + ret = clk_enable(hdmi->hdmi_clk); + if (ret < 0) { + dev_err(&pdev->dev, "Cannot enable clock: %d\n", ret); + goto eclkenable; + } + + dev_info(&pdev->dev, "Enabled HDMI clock at %luHz\n", rate); + + if (!request_mem_region(res->start, resource_size(res), dev_name(&pdev->dev))) { + dev_err(&pdev->dev, "HDMI register region already claimed\n"); + ret = -EBUSY; + goto ereqreg; + } + + hdmi->base = ioremap(res->start, resource_size(res)); + if (!hdmi->base) { + dev_err(&pdev->dev, "HDMI register region already claimed\n"); + ret = -ENOMEM; + goto emap; + } + + platform_set_drvdata(pdev, hdmi); + +#if 1 + /* Product and revision IDs are 0 in sh-mobile version */ + dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n", + hdmi_read(hdmi, HDMI_PRODUCT_ID), hdmi_read(hdmi, HDMI_REVISION_ID)); +#endif + + /* Set up LCDC callbacks */ + pdata->lcd_chan->board_cfg.board_data = hdmi; + pdata->lcd_chan->board_cfg.display_on = hdmi_display_on; + pdata->lcd_chan->board_cfg.display_off = hdmi_display_off; + + INIT_DELAYED_WORK(&hdmi->edid_work, edid_work_fn); + + pm_runtime_enable(&pdev->dev); + pm_runtime_resume(&pdev->dev); + + ret = request_irq(irq, sh_hdmi_hotplug, 0, + dev_name(&pdev->dev), hdmi); + if (ret < 0) { + dev_err(&pdev->dev, "Unable to request irq: %d\n", ret); + goto ereqirq; + } + + return 0; + +ereqirq: + pm_runtime_disable(&pdev->dev); + iounmap(hdmi->base); +emap: + release_mem_region(res->start, resource_size(res)); +ereqreg: + clk_disable(hdmi->hdmi_clk); +eclkenable: +erate: + clk_put(hdmi->hdmi_clk); +egetclk: + kfree(hdmi); + + return ret; +} + +static int __exit sh_hdmi_remove(struct platform_device *pdev) +{ + struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; + struct sh_hdmi *hdmi = platform_get_drvdata(pdev); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + int irq = platform_get_irq(pdev, 0); + + pdata->lcd_chan->board_cfg.display_on = NULL; + pdata->lcd_chan->board_cfg.display_off = NULL; + pdata->lcd_chan->board_cfg.board_data = NULL; + + free_irq(irq, hdmi); + pm_runtime_disable(&pdev->dev); + cancel_delayed_work_sync(&hdmi->edid_work); + clk_disable(hdmi->hdmi_clk); + clk_put(hdmi->hdmi_clk); + iounmap(hdmi->base); + release_mem_region(res->start, resource_size(res)); + kfree(hdmi); + + return 0; +} + +static struct platform_driver sh_hdmi_driver = { + .remove = __exit_p(sh_hdmi_remove), + .driver = { + .name = "sh-mobile-hdmi", + }, +}; + +static int __init sh_hdmi_init(void) +{ + return platform_driver_probe(&sh_hdmi_driver, sh_hdmi_probe); +} +module_init(sh_hdmi_init); + +static void __exit sh_hdmi_exit(void) +{ + platform_driver_unregister(&sh_hdmi_driver); +} +module_exit(sh_hdmi_exit); + +MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); +MODULE_DESCRIPTION("SuperH / ARM-shmobile HDMI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index 12c451a711e..d72075a9f01 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c @@ -56,6 +56,7 @@ static int lcdc_shared_regs[] = { /* per-channel registers */ enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R, LDSM2R, LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR, + LDHAJR, NR_CH_REGS }; static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = { @@ -74,6 +75,7 @@ static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = { [LDVLNR] = 0x450, [LDVSYNR] = 0x454, [LDPMR] = 0x460, + [LDHAJR] = 0x4a0, }; static unsigned long lcdc_offs_sublcd[NR_CH_REGS] = { @@ -137,6 +139,7 @@ struct sh_mobile_lcdc_priv { struct clk *dot_clk; unsigned long lddckr; struct sh_mobile_lcdc_chan ch[2]; + struct notifier_block notifier; unsigned long saved_shared_regs[NR_SHARED_REGS]; int started; }; @@ -404,6 +407,56 @@ static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv, lcdc_write(priv, _LDDCKSTPR, 1); /* stop dotclock */ } +static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch) +{ + struct fb_var_screeninfo *var = &ch->info->var; + unsigned long h_total, hsync_pos; + 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; + lcdc_write_chan(ch, LDMT1R, tmp); + + /* setup SYS bus */ + lcdc_write_chan(ch, LDMT2R, ch->cfg.sys_bus_cfg.ldmt2r); + lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r); + + /* horizontal configuration */ + h_total = var->xres + var->hsync_len + + var->left_margin + var->right_margin; + tmp = h_total / 8; /* HTCN */ + tmp |= (var->xres / 8) << 16; /* HDCN */ + lcdc_write_chan(ch, LDHCNR, tmp); + + hsync_pos = var->xres + var->right_margin; + tmp = hsync_pos / 8; /* HSYNP */ + tmp |= (var->hsync_len / 8) << 16; /* HSYNW */ + lcdc_write_chan(ch, LDHSYNR, tmp); + + /* vertical configuration */ + tmp = var->yres + var->vsync_len + + var->upper_margin + var->lower_margin; /* VTLN */ + tmp |= var->yres << 16; /* VDLN */ + lcdc_write_chan(ch, LDVLNR, tmp); + + tmp = var->yres + var->lower_margin; /* VSYNP */ + tmp |= var->vsync_len << 16; /* VSYNW */ + lcdc_write_chan(ch, LDVSYNR, tmp); + + /* Adjust horizontal synchronisation for HDMI */ + tmp = ((var->xres & 7) << 24) | + ((h_total & 7) << 16) | + ((var->hsync_len & 7) << 8) | + hsync_pos; + lcdc_write_chan(ch, LDHAJR, tmp); +} + static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) { struct sh_mobile_lcdc_chan *ch; @@ -470,49 +523,11 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) if (!ch->enabled) continue; - tmp = ch->ldmt1r_value; - tmp |= (lcd_cfg->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 1 << 28; - tmp |= (lcd_cfg->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; - lcdc_write_chan(ch, LDMT1R, tmp); - - /* setup SYS bus */ - lcdc_write_chan(ch, LDMT2R, ch->cfg.sys_bus_cfg.ldmt2r); - lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r); - - /* horizontal configuration */ - tmp = lcd_cfg->xres + lcd_cfg->hsync_len; - tmp += lcd_cfg->left_margin; - tmp += lcd_cfg->right_margin; - tmp /= 8; /* HTCN */ - tmp |= (lcd_cfg->xres / 8) << 16; /* HDCN */ - lcdc_write_chan(ch, LDHCNR, tmp); - - tmp = lcd_cfg->xres; - tmp += lcd_cfg->right_margin; - tmp /= 8; /* HSYNP */ - tmp |= (lcd_cfg->hsync_len / 8) << 16; /* HSYNW */ - lcdc_write_chan(ch, LDHSYNR, tmp); + sh_mobile_lcdc_geometry(ch); /* power supply */ lcdc_write_chan(ch, LDPMR, 0); - /* vertical configuration */ - tmp = lcd_cfg->yres + lcd_cfg->vsync_len; - tmp += lcd_cfg->upper_margin; - tmp += lcd_cfg->lower_margin; /* VTLN */ - tmp |= lcd_cfg->yres << 16; /* VDLN */ - lcdc_write_chan(ch, LDVLNR, tmp); - - tmp = lcd_cfg->yres; - tmp += lcd_cfg->lower_margin; /* VSYNP */ - tmp |= lcd_cfg->vsync_len << 16; /* VSYNW */ - lcdc_write_chan(ch, LDVSYNR, tmp); - board_cfg = &ch->cfg.board_cfg; if (board_cfg->setup_sys) ret = board_cfg->setup_sys(board_cfg->board_data, ch, @@ -577,7 +592,7 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) board_cfg = &ch->cfg.board_cfg; if (board_cfg->display_on) - board_cfg->display_on(board_cfg->board_data); + board_cfg->display_on(board_cfg->board_data, ch->info); } return 0; @@ -943,6 +958,62 @@ static const struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = { .runtime_resume = sh_mobile_lcdc_runtime_resume, }; +static int sh_mobile_lcdc_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct fb_event *event = data; + struct fb_info *info = event->info; + struct sh_mobile_lcdc_chan *ch = info->par; + struct sh_mobile_lcdc_board_cfg *board_cfg = &ch->cfg.board_cfg; + struct fb_var_screeninfo *var; + + if (&ch->lcdc->notifier != nb) + return 0; + + dev_dbg(info->dev, "%s(): action = %lu, data = %p\n", + __func__, action, event->data); + + switch(action) { + case FB_EVENT_SUSPEND: + if (board_cfg->display_off) + board_cfg->display_off(board_cfg->board_data); + pm_runtime_put(info->device); + break; + case FB_EVENT_RESUME: + var = &info->var; + + /* HDMI must be enabled before LCDC configuration */ + if (board_cfg->display_on) + board_cfg->display_on(board_cfg->board_data, ch->info); + + /* Check if the new display is not in our modelist */ + if (ch->info->modelist.next && + !fb_match_mode(var, &ch->info->modelist)) { + struct fb_videomode mode; + int ret; + + /* Can we handle this display? */ + if (var->xres > ch->cfg.lcd_cfg.xres || + var->yres > ch->cfg.lcd_cfg.yres) + return -ENOMEM; + + /* Add to the modelist */ + fb_var_to_videomode(&mode, var); + ret = fb_add_videomode(&mode, &ch->info->modelist); + if (ret < 0) + return ret; + } + + pm_runtime_get_sync(info->device); + + sh_mobile_lcdc_geometry(ch); + + break; + } + + return 0; +} + static int sh_mobile_lcdc_remove(struct platform_device *pdev); static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) @@ -1020,15 +1091,19 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) goto err1; } + priv->base = ioremap_nocache(res->start, resource_size(res)); + if (!priv->base) + goto err1; + error = sh_mobile_lcdc_setup_clocks(pdev, pdata->clock_source, priv); if (error) { dev_err(&pdev->dev, "unable to setup clocks\n"); goto err1; } - priv->base = ioremap_nocache(res->start, (res->end - res->start) + 1); - for (i = 0; i < j; i++) { + struct fb_var_screeninfo *var; + struct fb_videomode *lcd_cfg; cfg = &priv->ch[i].cfg; priv->ch[i].info = framebuffer_alloc(0, &pdev->dev); @@ -1039,22 +1114,33 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) } info = priv->ch[i].info; + var = &info->var; + lcd_cfg = &cfg->lcd_cfg; info->fbops = &sh_mobile_lcdc_ops; - info->var.xres = info->var.xres_virtual = cfg->lcd_cfg.xres; - info->var.yres = cfg->lcd_cfg.yres; + var->xres = var->xres_virtual = lcd_cfg->xres; + var->yres = lcd_cfg->yres; /* Default Y virtual resolution is 2x panel size */ - info->var.yres_virtual = info->var.yres * 2; - info->var.width = cfg->lcd_size_cfg.width; - info->var.height = cfg->lcd_size_cfg.height; - info->var.activate = FB_ACTIVATE_NOW; - error = sh_mobile_lcdc_set_bpp(&info->var, cfg->bpp); + var->yres_virtual = var->yres * 2; + var->width = cfg->lcd_size_cfg.width; + var->height = cfg->lcd_size_cfg.height; + var->activate = FB_ACTIVATE_NOW; + var->left_margin = lcd_cfg->left_margin; + var->right_margin = lcd_cfg->right_margin; + var->upper_margin = lcd_cfg->upper_margin; + var->lower_margin = lcd_cfg->lower_margin; + var->hsync_len = lcd_cfg->hsync_len; + var->vsync_len = lcd_cfg->vsync_len; + var->sync = lcd_cfg->sync; + var->pixclock = lcd_cfg->pixclock; + + error = sh_mobile_lcdc_set_bpp(var, cfg->bpp); if (error) break; info->fix = sh_mobile_lcdc_fix; - info->fix.line_length = cfg->lcd_cfg.xres * (cfg->bpp / 8); + info->fix.line_length = lcd_cfg->xres * (cfg->bpp / 8); info->fix.smem_len = info->fix.line_length * - info->var.yres_virtual; + var->yres_virtual; buf = dma_alloc_coherent(&pdev->dev, info->fix.smem_len, &priv->ch[i].dma_handle, GFP_KERNEL); @@ -1119,10 +1205,14 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) ch->cfg.bpp); /* deferred io mode: disable clock to save power */ - if (info->fbdefio) + if (info->fbdefio || info->state == FBINFO_STATE_SUSPENDED) sh_mobile_lcdc_clk_off(priv); } + /* Failure ignored */ + priv->notifier.notifier_call = sh_mobile_lcdc_notify; + fb_register_client(&priv->notifier); + return 0; err1: sh_mobile_lcdc_remove(pdev); @@ -1136,6 +1226,8 @@ static int sh_mobile_lcdc_remove(struct platform_device *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); diff --git a/drivers/video/sunxvr1000.c b/drivers/video/sunxvr1000.c index 489b44e8db8..5dbe06af222 100644 --- a/drivers/video/sunxvr1000.c +++ b/drivers/video/sunxvr1000.c @@ -111,7 +111,7 @@ static int __devinit gfb_set_fbinfo(struct gfb_info *gp) return 0; } -static int __devinit gfb_probe(struct of_device *op, +static int __devinit gfb_probe(struct platform_device *op, const struct of_device_id *match) { struct device_node *dp = op->dev.of_node; @@ -172,7 +172,7 @@ err_out: return err; } -static int __devexit gfb_remove(struct of_device *op) +static int __devexit gfb_remove(struct platform_device *op) { struct fb_info *info = dev_get_drvdata(&op->dev); struct gfb_info *gp = info->par; @@ -213,12 +213,12 @@ static int __init gfb_init(void) if (fb_get_options("gfb", NULL)) return -ENODEV; - return of_register_driver(&gfb_driver, &of_bus_type); + return of_register_platform_driver(&gfb_driver); } static void __exit gfb_exit(void) { - of_unregister_driver(&gfb_driver); + of_unregister_platform_driver(&gfb_driver); } module_init(gfb_init); diff --git a/drivers/video/tcx.c b/drivers/video/tcx.c index cc039b33d2d..77ad27955cf 100644 --- a/drivers/video/tcx.c +++ b/drivers/video/tcx.c @@ -342,7 +342,7 @@ tcx_init_fix(struct fb_info *info, int linebytes) info->fix.accel = FB_ACCEL_SUN_TCX; } -static void tcx_unmap_regs(struct of_device *op, struct fb_info *info, +static void tcx_unmap_regs(struct platform_device *op, struct fb_info *info, struct tcx_par *par) { if (par->tec) @@ -362,7 +362,7 @@ static void tcx_unmap_regs(struct of_device *op, struct fb_info *info, info->screen_base, info->fix.smem_len); } -static int __devinit tcx_probe(struct of_device *op, +static int __devinit tcx_probe(struct platform_device *op, const struct of_device_id *match) { struct device_node *dp = op->dev.of_node; @@ -486,7 +486,7 @@ out_err: return err; } -static int __devexit tcx_remove(struct of_device *op) +static int __devexit tcx_remove(struct platform_device *op) { struct fb_info *info = dev_get_drvdata(&op->dev); struct tcx_par *par = info->par; @@ -526,12 +526,12 @@ static int __init tcx_init(void) if (fb_get_options("tcxfb", NULL)) return -ENODEV; - return of_register_driver(&tcx_driver, &of_bus_type); + return of_register_platform_driver(&tcx_driver); } static void __exit tcx_exit(void) { - of_unregister_driver(&tcx_driver); + of_unregister_platform_driver(&tcx_driver); } module_init(tcx_init); diff --git a/drivers/video/tdfxfb.c b/drivers/video/tdfxfb.c index 98054839004..3ee5e63cfa4 100644 --- a/drivers/video/tdfxfb.c +++ b/drivers/video/tdfxfb.c @@ -1571,8 +1571,8 @@ out_err_iobase: if (default_par->mtrr_handle >= 0) mtrr_del(default_par->mtrr_handle, info->fix.smem_start, info->fix.smem_len); - release_mem_region(pci_resource_start(pdev, 2), - pci_resource_len(pdev, 2)); + release_region(pci_resource_start(pdev, 2), + pci_resource_len(pdev, 2)); out_err_screenbase: if (info->screen_base) iounmap(info->screen_base); diff --git a/drivers/video/tgafb.c b/drivers/video/tgafb.c index 1b3b1c718e8..aba7686b1a3 100644 --- a/drivers/video/tgafb.c +++ b/drivers/video/tgafb.c @@ -305,7 +305,7 @@ tgafb_set_par(struct fb_info *info) TGA_WRITE_REG(par, htimings, TGA_HORIZ_REG); TGA_WRITE_REG(par, vtimings, TGA_VERT_REG); - /* Initalise RAMDAC. */ + /* Initialise RAMDAC. */ if (tga_type == TGA_TYPE_8PLANE && tga_bus_pci) { /* Init BT485 RAMDAC registers. */ diff --git a/drivers/video/uvesafb.c b/drivers/video/uvesafb.c index 7b8839ebf3c..52ec0959d46 100644 --- a/drivers/video/uvesafb.c +++ b/drivers/video/uvesafb.c @@ -1977,8 +1977,7 @@ static void __devexit uvesafb_exit(void) module_exit(uvesafb_exit); -#define param_get_scroll NULL -static int param_set_scroll(const char *val, struct kernel_param *kp) +static int param_set_scroll(const char *val, const struct kernel_param *kp) { ypan = 0; @@ -1993,7 +1992,9 @@ static int param_set_scroll(const char *val, struct kernel_param *kp) return 0; } - +static struct kernel_param_ops param_ops_scroll = { + .set = param_set_scroll, +}; #define param_check_scroll(name, p) __param_check(name, p, void) module_param_named(scroll, ypan, scroll, 0); diff --git a/drivers/video/via/chip.h b/drivers/video/via/chip.h index d9b6e06e070..ef1f3de2e05 100644 --- a/drivers/video/via/chip.h +++ b/drivers/video/via/chip.h @@ -160,7 +160,6 @@ struct lvds_setting_information { int v_active; int bpp; int refresh_rate; - int get_lcd_size_method; int lcd_panel_id; int lcd_panel_hres; int lcd_panel_vres; diff --git a/drivers/video/via/hw.c b/drivers/video/via/hw.c index b996803ae2c..7dcb4d5bb9c 100644 --- a/drivers/video/via/hw.c +++ b/drivers/video/via/hw.c @@ -23,143 +23,341 @@ #include "global.h" static struct pll_map pll_value[] = { - {CLK_25_175M, CLE266_PLL_25_175M, K800_PLL_25_175M, - CX700_25_175M, VX855_25_175M}, - {CLK_29_581M, CLE266_PLL_29_581M, K800_PLL_29_581M, - CX700_29_581M, VX855_29_581M}, - {CLK_26_880M, CLE266_PLL_26_880M, K800_PLL_26_880M, - CX700_26_880M, VX855_26_880M}, - {CLK_31_490M, CLE266_PLL_31_490M, K800_PLL_31_490M, - CX700_31_490M, VX855_31_490M}, - {CLK_31_500M, CLE266_PLL_31_500M, K800_PLL_31_500M, - CX700_31_500M, VX855_31_500M}, - {CLK_31_728M, CLE266_PLL_31_728M, K800_PLL_31_728M, - CX700_31_728M, VX855_31_728M}, - {CLK_32_668M, CLE266_PLL_32_668M, K800_PLL_32_668M, - CX700_32_668M, VX855_32_668M}, - {CLK_36_000M, CLE266_PLL_36_000M, K800_PLL_36_000M, - CX700_36_000M, VX855_36_000M}, - {CLK_40_000M, CLE266_PLL_40_000M, K800_PLL_40_000M, - CX700_40_000M, VX855_40_000M}, - {CLK_41_291M, CLE266_PLL_41_291M, K800_PLL_41_291M, - CX700_41_291M, VX855_41_291M}, - {CLK_43_163M, CLE266_PLL_43_163M, K800_PLL_43_163M, - CX700_43_163M, VX855_43_163M}, - {CLK_45_250M, CLE266_PLL_45_250M, K800_PLL_45_250M, - CX700_45_250M, VX855_45_250M}, - {CLK_46_000M, CLE266_PLL_46_000M, K800_PLL_46_000M, - CX700_46_000M, VX855_46_000M}, - {CLK_46_996M, CLE266_PLL_46_996M, K800_PLL_46_996M, - CX700_46_996M, VX855_46_996M}, - {CLK_48_000M, CLE266_PLL_48_000M, K800_PLL_48_000M, - CX700_48_000M, VX855_48_000M}, - {CLK_48_875M, CLE266_PLL_48_875M, K800_PLL_48_875M, - CX700_48_875M, VX855_48_875M}, - {CLK_49_500M, CLE266_PLL_49_500M, K800_PLL_49_500M, - CX700_49_500M, VX855_49_500M}, - {CLK_52_406M, CLE266_PLL_52_406M, K800_PLL_52_406M, - CX700_52_406M, VX855_52_406M}, - {CLK_52_977M, CLE266_PLL_52_977M, K800_PLL_52_977M, - CX700_52_977M, VX855_52_977M}, - {CLK_56_250M, CLE266_PLL_56_250M, K800_PLL_56_250M, - CX700_56_250M, VX855_56_250M}, - {CLK_57_275M, 0, 0, 0, VX855_57_275M}, - {CLK_60_466M, CLE266_PLL_60_466M, K800_PLL_60_466M, - CX700_60_466M, VX855_60_466M}, - {CLK_61_500M, CLE266_PLL_61_500M, K800_PLL_61_500M, - CX700_61_500M, VX855_61_500M}, - {CLK_65_000M, CLE266_PLL_65_000M, K800_PLL_65_000M, - CX700_65_000M, VX855_65_000M}, - {CLK_65_178M, CLE266_PLL_65_178M, K800_PLL_65_178M, - CX700_65_178M, VX855_65_178M}, - {CLK_66_750M, CLE266_PLL_66_750M, K800_PLL_66_750M, - CX700_66_750M, VX855_66_750M}, - {CLK_68_179M, CLE266_PLL_68_179M, K800_PLL_68_179M, - CX700_68_179M, VX855_68_179M}, - {CLK_69_924M, CLE266_PLL_69_924M, K800_PLL_69_924M, - CX700_69_924M, VX855_69_924M}, - {CLK_70_159M, CLE266_PLL_70_159M, K800_PLL_70_159M, - CX700_70_159M, VX855_70_159M}, - {CLK_72_000M, CLE266_PLL_72_000M, K800_PLL_72_000M, - CX700_72_000M, VX855_72_000M}, - {CLK_78_750M, CLE266_PLL_78_750M, K800_PLL_78_750M, - CX700_78_750M, VX855_78_750M}, - {CLK_80_136M, CLE266_PLL_80_136M, K800_PLL_80_136M, - CX700_80_136M, VX855_80_136M}, - {CLK_83_375M, CLE266_PLL_83_375M, K800_PLL_83_375M, - CX700_83_375M, VX855_83_375M}, - {CLK_83_950M, CLE266_PLL_83_950M, K800_PLL_83_950M, - CX700_83_950M, VX855_83_950M}, - {CLK_84_750M, CLE266_PLL_84_750M, K800_PLL_84_750M, - CX700_84_750M, VX855_84_750M}, - {CLK_85_860M, CLE266_PLL_85_860M, K800_PLL_85_860M, - CX700_85_860M, VX855_85_860M}, - {CLK_88_750M, CLE266_PLL_88_750M, K800_PLL_88_750M, - CX700_88_750M, VX855_88_750M}, - {CLK_94_500M, CLE266_PLL_94_500M, K800_PLL_94_500M, - CX700_94_500M, VX855_94_500M}, - {CLK_97_750M, CLE266_PLL_97_750M, K800_PLL_97_750M, - CX700_97_750M, VX855_97_750M}, - {CLK_101_000M, CLE266_PLL_101_000M, K800_PLL_101_000M, - CX700_101_000M, VX855_101_000M}, - {CLK_106_500M, CLE266_PLL_106_500M, K800_PLL_106_500M, - CX700_106_500M, VX855_106_500M}, - {CLK_108_000M, CLE266_PLL_108_000M, K800_PLL_108_000M, - CX700_108_000M, VX855_108_000M}, - {CLK_113_309M, CLE266_PLL_113_309M, K800_PLL_113_309M, - CX700_113_309M, VX855_113_309M}, - {CLK_118_840M, CLE266_PLL_118_840M, K800_PLL_118_840M, - CX700_118_840M, VX855_118_840M}, - {CLK_119_000M, CLE266_PLL_119_000M, K800_PLL_119_000M, - CX700_119_000M, VX855_119_000M}, - {CLK_121_750M, CLE266_PLL_121_750M, K800_PLL_121_750M, - CX700_121_750M, 0}, - {CLK_125_104M, CLE266_PLL_125_104M, K800_PLL_125_104M, - CX700_125_104M, 0}, - {CLK_133_308M, CLE266_PLL_133_308M, K800_PLL_133_308M, - CX700_133_308M, 0}, - {CLK_135_000M, CLE266_PLL_135_000M, K800_PLL_135_000M, - CX700_135_000M, VX855_135_000M}, - {CLK_136_700M, CLE266_PLL_136_700M, K800_PLL_136_700M, - CX700_136_700M, VX855_136_700M}, - {CLK_138_400M, CLE266_PLL_138_400M, K800_PLL_138_400M, - CX700_138_400M, VX855_138_400M}, - {CLK_146_760M, CLE266_PLL_146_760M, K800_PLL_146_760M, - CX700_146_760M, VX855_146_760M}, - {CLK_153_920M, CLE266_PLL_153_920M, K800_PLL_153_920M, - CX700_153_920M, VX855_153_920M}, - {CLK_156_000M, CLE266_PLL_156_000M, K800_PLL_156_000M, - CX700_156_000M, VX855_156_000M}, - {CLK_157_500M, CLE266_PLL_157_500M, K800_PLL_157_500M, - CX700_157_500M, VX855_157_500M}, - {CLK_162_000M, CLE266_PLL_162_000M, K800_PLL_162_000M, - CX700_162_000M, VX855_162_000M}, - {CLK_187_000M, CLE266_PLL_187_000M, K800_PLL_187_000M, - CX700_187_000M, VX855_187_000M}, - {CLK_193_295M, CLE266_PLL_193_295M, K800_PLL_193_295M, - CX700_193_295M, VX855_193_295M}, - {CLK_202_500M, CLE266_PLL_202_500M, K800_PLL_202_500M, - CX700_202_500M, VX855_202_500M}, - {CLK_204_000M, CLE266_PLL_204_000M, K800_PLL_204_000M, - CX700_204_000M, VX855_204_000M}, - {CLK_218_500M, CLE266_PLL_218_500M, K800_PLL_218_500M, - CX700_218_500M, VX855_218_500M}, - {CLK_234_000M, CLE266_PLL_234_000M, K800_PLL_234_000M, - CX700_234_000M, VX855_234_000M}, - {CLK_267_250M, CLE266_PLL_267_250M, K800_PLL_267_250M, - CX700_267_250M, VX855_267_250M}, - {CLK_297_500M, CLE266_PLL_297_500M, K800_PLL_297_500M, - CX700_297_500M, VX855_297_500M}, - {CLK_74_481M, CLE266_PLL_74_481M, K800_PLL_74_481M, - CX700_74_481M, VX855_74_481M}, - {CLK_172_798M, CLE266_PLL_172_798M, K800_PLL_172_798M, - CX700_172_798M, VX855_172_798M}, - {CLK_122_614M, CLE266_PLL_122_614M, K800_PLL_122_614M, - CX700_122_614M, VX855_122_614M}, - {CLK_74_270M, CLE266_PLL_74_270M, K800_PLL_74_270M, - CX700_74_270M, 0}, - {CLK_148_500M, CLE266_PLL_148_500M, K800_PLL_148_500M, - CX700_148_500M, VX855_148_500M} + {25175000, + {99, 7, 3}, + {85, 3, 4}, /* ignoring bit difference: 0x00008000 */ + {141, 5, 4}, + {141, 5, 4} }, + {29581000, + {33, 4, 2}, + {66, 2, 4}, /* ignoring bit difference: 0x00808000 */ + {166, 5, 4}, /* ignoring bit difference: 0x00008000 */ + {165, 5, 4} }, + {26880000, + {15, 4, 1}, + {30, 2, 3}, /* ignoring bit difference: 0x00808000 */ + {150, 5, 4}, + {150, 5, 4} }, + {31500000, + {53, 3, 3}, /* ignoring bit difference: 0x00008000 */ + {141, 4, 4}, /* ignoring bit difference: 0x00008000 */ + {176, 5, 4}, + {176, 5, 4} }, + {31728000, + {31, 7, 1}, + {177, 5, 4}, /* ignoring bit difference: 0x00008000 */ + {177, 5, 4}, + {142, 4, 4} }, + {32688000, + {73, 4, 3}, + {146, 4, 4}, /* ignoring bit difference: 0x00008000 */ + {183, 5, 4}, + {146, 4, 4} }, + {36000000, + {101, 5, 3}, /* ignoring bit difference: 0x00008000 */ + {161, 4, 4}, /* ignoring bit difference: 0x00008000 */ + {202, 5, 4}, + {161, 4, 4} }, + {40000000, + {89, 4, 3}, + {89, 4, 3}, /* ignoring bit difference: 0x00008000 */ + {112, 5, 3}, + {112, 5, 3} }, + {41291000, + {23, 4, 1}, + {69, 3, 3}, /* ignoring bit difference: 0x00008000 */ + {115, 5, 3}, + {115, 5, 3} }, + {43163000, + {121, 5, 3}, + {121, 5, 3}, /* ignoring bit difference: 0x00008000 */ + {121, 5, 3}, + {121, 5, 3} }, + {45250000, + {127, 5, 3}, + {127, 5, 3}, /* ignoring bit difference: 0x00808000 */ + {127, 5, 3}, + {127, 5, 3} }, + {46000000, + {90, 7, 2}, + {103, 4, 3}, /* ignoring bit difference: 0x00008000 */ + {129, 5, 3}, + {103, 4, 3} }, + {46996000, + {105, 4, 3}, /* ignoring bit difference: 0x00008000 */ + {131, 5, 3}, /* ignoring bit difference: 0x00808000 */ + {131, 5, 3}, /* ignoring bit difference: 0x00808000 */ + {105, 4, 3} }, + {48000000, + {67, 20, 0}, + {134, 5, 3}, /* ignoring bit difference: 0x00808000 */ + {134, 5, 3}, + {134, 5, 3} }, + {48875000, + {99, 29, 0}, + {82, 3, 3}, /* ignoring bit difference: 0x00808000 */ + {82, 3, 3}, /* ignoring bit difference: 0x00808000 */ + {137, 5, 3} }, + {49500000, + {83, 6, 2}, + {83, 3, 3}, /* ignoring bit difference: 0x00008000 */ + {138, 5, 3}, + {83, 3, 3} }, + {52406000, + {117, 4, 3}, + {117, 4, 3}, /* ignoring bit difference: 0x00008000 */ + {117, 4, 3}, + {88, 3, 3} }, + {52977000, + {37, 5, 1}, + {148, 5, 3}, /* ignoring bit difference: 0x00808000 */ + {148, 5, 3}, + {148, 5, 3} }, + {56250000, + {55, 7, 1}, /* ignoring bit difference: 0x00008000 */ + {126, 4, 3}, /* ignoring bit difference: 0x00008000 */ + {157, 5, 3}, + {157, 5, 3} }, + {57275000, + {0, 0, 0}, + {2, 2, 0}, + {2, 2, 0}, + {157, 5, 3} }, /* ignoring bit difference: 0x00808000 */ + {60466000, + {76, 9, 1}, + {169, 5, 3}, /* ignoring bit difference: 0x00808000 */ + {169, 5, 3}, /* FIXED: old = {72, 2, 3} */ + {169, 5, 3} }, + {61500000, + {86, 20, 0}, + {172, 5, 3}, /* ignoring bit difference: 0x00808000 */ + {172, 5, 3}, + {172, 5, 3} }, + {65000000, + {109, 6, 2}, /* ignoring bit difference: 0x00008000 */ + {109, 3, 3}, /* ignoring bit difference: 0x00008000 */ + {109, 3, 3}, + {109, 3, 3} }, + {65178000, + {91, 5, 2}, + {182, 5, 3}, /* ignoring bit difference: 0x00808000 */ + {109, 3, 3}, + {182, 5, 3} }, + {66750000, + {75, 4, 2}, + {150, 4, 3}, /* ignoring bit difference: 0x00808000 */ + {150, 4, 3}, + {112, 3, 3} }, + {68179000, + {19, 4, 0}, + {114, 3, 3}, /* ignoring bit difference: 0x00008000 */ + {190, 5, 3}, + {191, 5, 3} }, + {69924000, + {83, 17, 0}, + {195, 5, 3}, /* ignoring bit difference: 0x00808000 */ + {195, 5, 3}, + {195, 5, 3} }, + {70159000, + {98, 20, 0}, + {196, 5, 3}, /* ignoring bit difference: 0x00808000 */ + {196, 5, 3}, + {195, 5, 3} }, + {72000000, + {121, 24, 0}, + {161, 4, 3}, /* ignoring bit difference: 0x00808000 */ + {161, 4, 3}, + {161, 4, 3} }, + {78750000, + {33, 3, 1}, + {66, 3, 2}, /* ignoring bit difference: 0x00008000 */ + {110, 5, 2}, + {110, 5, 2} }, + {80136000, + {28, 5, 0}, + {68, 3, 2}, /* ignoring bit difference: 0x00008000 */ + {112, 5, 2}, + {112, 5, 2} }, + {83375000, + {93, 2, 3}, + {93, 4, 2}, /* ignoring bit difference: 0x00800000 */ + {93, 4, 2}, /* ignoring bit difference: 0x00800000 */ + {117, 5, 2} }, + {83950000, + {41, 7, 0}, + {117, 5, 2}, /* ignoring bit difference: 0x00008000 */ + {117, 5, 2}, + {117, 5, 2} }, + {84750000, + {118, 5, 2}, + {118, 5, 2}, /* ignoring bit difference: 0x00808000 */ + {118, 5, 2}, + {118, 5, 2} }, + {85860000, + {84, 7, 1}, + {120, 5, 2}, /* ignoring bit difference: 0x00808000 */ + {120, 5, 2}, + {118, 5, 2} }, + {88750000, + {31, 5, 0}, + {124, 5, 2}, /* ignoring bit difference: 0x00808000 */ + {174, 7, 2}, /* ignoring bit difference: 0x00808000 */ + {124, 5, 2} }, + {94500000, + {33, 5, 0}, + {132, 5, 2}, /* ignoring bit difference: 0x00008000 */ + {132, 5, 2}, + {132, 5, 2} }, + {97750000, + {82, 6, 1}, + {137, 5, 2}, /* ignoring bit difference: 0x00808000 */ + {137, 5, 2}, + {137, 5, 2} }, + {101000000, + {127, 9, 1}, + {141, 5, 2}, /* ignoring bit difference: 0x00808000 */ + {141, 5, 2}, + {141, 5, 2} }, + {106500000, + {119, 4, 2}, + {119, 4, 2}, /* ignoring bit difference: 0x00808000 */ + {119, 4, 2}, + {149, 5, 2} }, + {108000000, + {121, 4, 2}, + {121, 4, 2}, /* ignoring bit difference: 0x00808000 */ + {151, 5, 2}, + {151, 5, 2} }, + {113309000, + {95, 12, 0}, + {95, 3, 2}, /* ignoring bit difference: 0x00808000 */ + {95, 3, 2}, + {159, 5, 2} }, + {118840000, + {83, 5, 1}, + {166, 5, 2}, /* ignoring bit difference: 0x00808000 */ + {166, 5, 2}, + {166, 5, 2} }, + {119000000, + {108, 13, 0}, + {133, 4, 2}, /* ignoring bit difference: 0x00808000 */ + {133, 4, 2}, + {167, 5, 2} }, + {121750000, + {85, 5, 1}, + {170, 5, 2}, /* ignoring bit difference: 0x00808000 */ + {68, 2, 2}, + {0, 0, 0} }, + {125104000, + {53, 6, 0}, /* ignoring bit difference: 0x00008000 */ + {106, 3, 2}, /* ignoring bit difference: 0x00008000 */ + {175, 5, 2}, + {0, 0, 0} }, + {135000000, + {94, 5, 1}, + {28, 3, 0}, /* ignoring bit difference: 0x00804000 */ + {151, 4, 2}, + {189, 5, 2} }, + {136700000, + {115, 12, 0}, + {191, 5, 2}, /* ignoring bit difference: 0x00808000 */ + {191, 5, 2}, + {191, 5, 2} }, + {138400000, + {87, 9, 0}, + {116, 3, 2}, /* ignoring bit difference: 0x00808000 */ + {116, 3, 2}, + {194, 5, 2} }, + {146760000, + {103, 5, 1}, + {206, 5, 2}, /* ignoring bit difference: 0x00808000 */ + {206, 5, 2}, + {206, 5, 2} }, + {153920000, + {86, 8, 0}, + {86, 4, 1}, /* ignoring bit difference: 0x00808000 */ + {86, 4, 1}, + {86, 4, 1} }, /* FIXED: old = {84, 2, 1} */ + {156000000, + {109, 5, 1}, + {109, 5, 1}, /* ignoring bit difference: 0x00808000 */ + {109, 5, 1}, + {108, 5, 1} }, + {157500000, + {55, 5, 0}, /* ignoring bit difference: 0x00008000 */ + {22, 2, 0}, /* ignoring bit difference: 0x00802000 */ + {110, 5, 1}, + {110, 5, 1} }, + {162000000, + {113, 5, 1}, + {113, 5, 1}, /* ignoring bit difference: 0x00808000 */ + {113, 5, 1}, + {113, 5, 1} }, + {187000000, + {118, 9, 0}, + {131, 5, 1}, /* ignoring bit difference: 0x00808000 */ + {131, 5, 1}, + {131, 5, 1} }, + {193295000, + {108, 8, 0}, + {81, 3, 1}, /* ignoring bit difference: 0x00808000 */ + {135, 5, 1}, + {135, 5, 1} }, + {202500000, + {99, 7, 0}, + {85, 3, 1}, /* ignoring bit difference: 0x00808000 */ + {142, 5, 1}, + {142, 5, 1} }, + {204000000, + {100, 7, 0}, + {143, 5, 1}, /* ignoring bit difference: 0x00808000 */ + {143, 5, 1}, + {143, 5, 1} }, + {218500000, + {92, 6, 0}, + {153, 5, 1}, /* ignoring bit difference: 0x00808000 */ + {153, 5, 1}, + {153, 5, 1} }, + {234000000, + {98, 6, 0}, + {98, 3, 1}, /* ignoring bit difference: 0x00008000 */ + {98, 3, 1}, + {164, 5, 1} }, + {267250000, + {112, 6, 0}, + {112, 3, 1}, /* ignoring bit difference: 0x00808000 */ + {187, 5, 1}, + {187, 5, 1} }, + {297500000, + {102, 5, 0}, /* ignoring bit difference: 0x00008000 */ + {166, 4, 1}, /* ignoring bit difference: 0x00008000 */ + {208, 5, 1}, + {208, 5, 1} }, + {74481000, + {26, 5, 0}, + {125, 3, 3}, /* ignoring bit difference: 0x00808000 */ + {208, 5, 3}, + {209, 5, 3} }, + {172798000, + {121, 5, 1}, + {121, 5, 1}, /* ignoring bit difference: 0x00808000 */ + {121, 5, 1}, + {121, 5, 1} }, + {122614000, + {60, 7, 0}, + {137, 4, 2}, /* ignoring bit difference: 0x00808000 */ + {137, 4, 2}, + {172, 5, 2} }, + {74270000, + {83, 8, 1}, + {208, 5, 3}, + {208, 5, 3}, + {0, 0, 0} }, + {148500000, + {83, 8, 0}, + {208, 5, 2}, + {166, 4, 2}, + {208, 5, 2} } }; static struct fifo_depth_select display_fifo_depth_reg = { @@ -1360,40 +1558,70 @@ void viafb_load_FIFO_reg(int set_iga, int hor_active, int ver_active) } +static u32 cle266_encode_pll(struct pll_config pll) +{ + return (pll.multiplier << 8) + | (pll.rshift << 6) + | pll.divisor; +} + +static u32 k800_encode_pll(struct pll_config pll) +{ + return ((pll.divisor - 2) << 16) + | (pll.rshift << 10) + | (pll.multiplier - 2); +} + +static u32 vx855_encode_pll(struct pll_config pll) +{ + return (pll.divisor << 16) + | (pll.rshift << 10) + | pll.multiplier; +} + u32 viafb_get_clk_value(int clk) { - int i; + u32 value = 0; + int i = 0; - for (i = 0; i < NUM_TOTAL_PLL_TABLE; i++) { - if (clk == pll_value[i].clk) { - switch (viaparinfo->chip_info->gfx_chip_name) { - case UNICHROME_CLE266: - case UNICHROME_K400: - return pll_value[i].cle266_pll; - - case UNICHROME_K800: - case UNICHROME_PM800: - case UNICHROME_CN700: - return pll_value[i].k800_pll; - - case UNICHROME_CX700: - case UNICHROME_K8M890: - case UNICHROME_P4M890: - case UNICHROME_P4M900: - case UNICHROME_VX800: - return pll_value[i].cx700_pll; - case UNICHROME_VX855: - return pll_value[i].vx855_pll; - } + while (i < NUM_TOTAL_PLL_TABLE && clk != pll_value[i].clk) + i++; + + if (i == NUM_TOTAL_PLL_TABLE) { + printk(KERN_WARNING "viafb_get_clk_value: PLL lookup failed!"); + } else { + switch (viaparinfo->chip_info->gfx_chip_name) { + case UNICHROME_CLE266: + case UNICHROME_K400: + value = cle266_encode_pll(pll_value[i].cle266_pll); + break; + + case UNICHROME_K800: + case UNICHROME_PM800: + case UNICHROME_CN700: + value = k800_encode_pll(pll_value[i].k800_pll); + break; + + case UNICHROME_CX700: + case UNICHROME_CN750: + case UNICHROME_K8M890: + case UNICHROME_P4M890: + case UNICHROME_P4M900: + case UNICHROME_VX800: + value = k800_encode_pll(pll_value[i].cx700_pll); + break; + + case UNICHROME_VX855: + value = vx855_encode_pll(pll_value[i].vx855_pll); + break; } } - DEBUG_MSG(KERN_INFO "Can't find match PLL value\n\n"); - return 0; + return value; } /* Set VCLK*/ -void viafb_set_vclock(u32 CLK, int set_iga) +void viafb_set_vclock(u32 clk, int set_iga) { /* H.W. Reset : ON */ viafb_write_reg_mask(CR17, VIACR, 0x00, BIT7); @@ -1403,26 +1631,23 @@ void viafb_set_vclock(u32 CLK, int set_iga) switch (viaparinfo->chip_info->gfx_chip_name) { case UNICHROME_CLE266: case UNICHROME_K400: - viafb_write_reg(SR46, VIASR, CLK / 0x100); - viafb_write_reg(SR47, VIASR, CLK % 0x100); + via_write_reg(VIASR, SR46, (clk & 0x00FF)); + via_write_reg(VIASR, SR47, (clk & 0xFF00) >> 8); break; case UNICHROME_K800: case UNICHROME_PM800: case UNICHROME_CN700: case UNICHROME_CX700: + case UNICHROME_CN750: case UNICHROME_K8M890: case UNICHROME_P4M890: case UNICHROME_P4M900: case UNICHROME_VX800: case UNICHROME_VX855: - viafb_write_reg(SR44, VIASR, CLK / 0x10000); - DEBUG_MSG(KERN_INFO "\nSR44=%x", CLK / 0x10000); - viafb_write_reg(SR45, VIASR, (CLK & 0xFFFF) / 0x100); - DEBUG_MSG(KERN_INFO "\nSR45=%x", - (CLK & 0xFFFF) / 0x100); - viafb_write_reg(SR46, VIASR, CLK % 0x100); - DEBUG_MSG(KERN_INFO "\nSR46=%x", CLK % 0x100); + via_write_reg(VIASR, SR44, (clk & 0x0000FF)); + via_write_reg(VIASR, SR45, (clk & 0x00FF00) >> 8); + via_write_reg(VIASR, SR46, (clk & 0xFF0000) >> 16); break; } } @@ -1432,22 +1657,23 @@ void viafb_set_vclock(u32 CLK, int set_iga) switch (viaparinfo->chip_info->gfx_chip_name) { case UNICHROME_CLE266: case UNICHROME_K400: - viafb_write_reg(SR44, VIASR, CLK / 0x100); - viafb_write_reg(SR45, VIASR, CLK % 0x100); + via_write_reg(VIASR, SR44, (clk & 0x00FF)); + via_write_reg(VIASR, SR45, (clk & 0xFF00) >> 8); break; case UNICHROME_K800: case UNICHROME_PM800: case UNICHROME_CN700: case UNICHROME_CX700: + case UNICHROME_CN750: case UNICHROME_K8M890: case UNICHROME_P4M890: case UNICHROME_P4M900: case UNICHROME_VX800: case UNICHROME_VX855: - viafb_write_reg(SR4A, VIASR, CLK / 0x10000); - viafb_write_reg(SR4B, VIASR, (CLK & 0xFFFF) / 0x100); - viafb_write_reg(SR4C, VIASR, CLK % 0x100); + via_write_reg(VIASR, SR4A, (clk & 0x0000FF)); + via_write_reg(VIASR, SR4B, (clk & 0x00FF00) >> 8); + via_write_reg(VIASR, SR4C, (clk & 0xFF0000) >> 16); break; } } @@ -1791,8 +2017,6 @@ void viafb_init_chip_info(int chip_type) viafb_set_iga_path(); viaparinfo->lvds_setting_info->display_method = viafb_lcd_dsp_method; - viaparinfo->lvds_setting_info->get_lcd_size_method = - GET_LCD_SIZE_BY_USER_SETTING; viaparinfo->lvds_setting_info->lcd_mode = viafb_lcd_mode; viaparinfo->lvds_setting_info2->display_method = viaparinfo->lvds_setting_info->display_method; @@ -1946,13 +2170,6 @@ static void init_tmds_chip_info(void) static void init_lvds_chip_info(void) { - if (viafb_lcd_panel_id > LCD_PANEL_ID_MAXIMUM) - viaparinfo->lvds_setting_info->get_lcd_size_method = - GET_LCD_SIZE_BY_VGA_BIOS; - else - viaparinfo->lvds_setting_info->get_lcd_size_method = - GET_LCD_SIZE_BY_USER_SETTING; - viafb_lvds_trasmitter_identify(); viafb_init_lcd_size(); viafb_init_lvds_output_interface(&viaparinfo->chip_info->lvds_chip_info, diff --git a/drivers/video/via/hw.h b/drivers/video/via/hw.h index a109de37981..c4439989529 100644 --- a/drivers/video/via/hw.h +++ b/drivers/video/via/hw.h @@ -700,12 +700,18 @@ struct _lcd_scaling_factor { struct _lcd_ver_scaling_factor lcd_ver_scaling_factor; }; +struct pll_config { + u16 multiplier; + u8 divisor; + u8 rshift; +}; + struct pll_map { u32 clk; - u32 cle266_pll; - u32 k800_pll; - u32 cx700_pll; - u32 vx855_pll; + struct pll_config cle266_pll; + struct pll_config k800_pll; + struct pll_config cx700_pll; + struct pll_config vx855_pll; }; struct rgbLUT { diff --git a/drivers/video/via/ioctl.h b/drivers/video/via/ioctl.h index c430fa23008..6010d10b59e 100644 --- a/drivers/video/via/ioctl.h +++ b/drivers/video/via/ioctl.h @@ -35,11 +35,9 @@ #define VIAFB_GET_SAMM_INFO 0x56494107 /* 'VIA\07' */ #define VIAFB_TURN_ON_OUTPUT_DEVICE 0x56494108 /* 'VIA\08' */ #define VIAFB_TURN_OFF_OUTPUT_DEVICE 0x56494109 /* 'VIA\09' */ -#define VIAFB_SET_DEVICE 0x5649410A #define VIAFB_GET_DEVICE 0x5649410B #define VIAFB_GET_DRIVER_VERSION 0x56494112 /* 'VIA\12' */ #define VIAFB_GET_CHIP_INFO 0x56494113 /* 'VIA\13' */ -#define VIAFB_SET_DEVICE_INFO 0x56494114 #define VIAFB_GET_DEVICE_INFO 0x56494115 #define VIAFB_GET_DEVICE_SUPPORT 0x56494118 @@ -50,7 +48,6 @@ #define VIAFB_GET_GAMMA_LUT 0x56494124 #define VIAFB_SET_GAMMA_LUT 0x56494125 #define VIAFB_GET_GAMMA_SUPPORT_STATE 0x56494126 -#define VIAFB_SET_SECOND_MODE 0x56494129 #define VIAFB_SYNC_SURFACE 0x56494130 #define VIAFB_GET_DRIVER_CAPS 0x56494131 #define VIAFB_GET_IGA_SCALING_INFO 0x56494132 diff --git a/drivers/video/via/lcd.c b/drivers/video/via/lcd.c index 2ab0f156439..fc25ae30c5f 100644 --- a/drivers/video/via/lcd.c +++ b/drivers/video/via/lcd.c @@ -75,8 +75,6 @@ static void check_diport_of_integrated_lvds( static struct display_timing lcd_centering_timging(struct display_timing mode_crt_reg, struct display_timing panel_crt_reg); -static void viafb_load_scaling_factor_for_p4m900(int set_hres, - int set_vres, int panel_hres, int panel_vres); static int check_lvds_chip(int device_id_subaddr, int device_id) { @@ -89,33 +87,8 @@ static int check_lvds_chip(int device_id_subaddr, int device_id) void viafb_init_lcd_size(void) { DEBUG_MSG(KERN_INFO "viafb_init_lcd_size()\n"); - DEBUG_MSG(KERN_INFO - "viaparinfo->lvds_setting_info->get_lcd_size_method %d\n", - viaparinfo->lvds_setting_info->get_lcd_size_method); - switch (viaparinfo->lvds_setting_info->get_lcd_size_method) { - case GET_LCD_SIZE_BY_SYSTEM_BIOS: - break; - case GET_LCD_SZIE_BY_HW_STRAPPING: - break; - case GET_LCD_SIZE_BY_VGA_BIOS: - DEBUG_MSG(KERN_INFO "Get LCD Size method by VGA BIOS !!\n"); - fp_id_to_vindex(viafb_lcd_panel_id); - DEBUG_MSG(KERN_INFO "LCD Panel_ID = %d\n", - viaparinfo->lvds_setting_info->lcd_panel_id); - break; - case GET_LCD_SIZE_BY_USER_SETTING: - DEBUG_MSG(KERN_INFO "Get LCD Size method by user setting !!\n"); - fp_id_to_vindex(viafb_lcd_panel_id); - DEBUG_MSG(KERN_INFO "LCD Panel_ID = %d\n", - viaparinfo->lvds_setting_info->lcd_panel_id); - break; - default: - DEBUG_MSG(KERN_INFO "viafb_init_lcd_size fail\n"); - viaparinfo->lvds_setting_info->lcd_panel_id = - LCD_PANEL_ID1_800X600; - fp_id_to_vindex(LCD_PANEL_ID1_800X600); - } + fp_id_to_vindex(viafb_lcd_panel_id); viaparinfo->lvds_setting_info2->lcd_panel_id = viaparinfo->lvds_setting_info->lcd_panel_id; viaparinfo->lvds_setting_info2->lcd_panel_hres = @@ -437,14 +410,9 @@ static void load_lcd_scaling(int set_hres, int set_vres, int panel_hres, /* LCD Scaling Enable */ viafb_write_reg_mask(CR79, VIACR, 0x07, BIT0 + BIT1 + BIT2); - if (UNICHROME_P4M900 == viaparinfo->chip_info->gfx_chip_name) { - viafb_load_scaling_factor_for_p4m900(set_hres, set_vres, - panel_hres, panel_vres); - return; - } /* Check if expansion for horizontal */ - if (set_hres != panel_hres) { + if (set_hres < panel_hres) { /* Load Horizontal Scaling Factor */ switch (viaparinfo->chip_info->gfx_chip_name) { case UNICHROME_CLE266: @@ -464,6 +432,10 @@ static void load_lcd_scaling(int set_hres, int set_vres, int panel_hres, case UNICHROME_CX700: case UNICHROME_K8M890: case UNICHROME_P4M890: + case UNICHROME_P4M900: + case UNICHROME_CN750: + case UNICHROME_VX800: + case UNICHROME_VX855: reg_value = K800_LCD_HOR_SCF_FORMULA(set_hres, panel_hres); /* Horizontal scaling enabled */ @@ -483,7 +455,7 @@ static void load_lcd_scaling(int set_hres, int set_vres, int panel_hres, } /* Check if expansion for vertical */ - if (set_vres != panel_vres) { + if (set_vres < panel_vres) { /* Load Vertical Scaling Factor */ switch (viaparinfo->chip_info->gfx_chip_name) { case UNICHROME_CLE266: @@ -503,6 +475,10 @@ static void load_lcd_scaling(int set_hres, int set_vres, int panel_hres, case UNICHROME_CX700: case UNICHROME_K8M890: case UNICHROME_P4M890: + case UNICHROME_P4M900: + case UNICHROME_CN750: + case UNICHROME_VX800: + case UNICHROME_VX855: reg_value = K800_LCD_VER_SCF_FORMULA(set_vres, panel_vres); /* Vertical scaling enabled */ @@ -648,9 +624,8 @@ void viafb_lcd_set_mode(struct crt_mode_table *mode_crt_table, (mode_crt_reg, panel_crt_reg), IGA1); } else { /* Expansion */ - if ((plvds_setting_info->display_method == - LCD_EXPANDSION) & ((set_hres != panel_hres) - || (set_vres != panel_vres))) { + 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"); @@ -1139,69 +1114,3 @@ bool viafb_lcd_get_mobile_state(bool *mobile) return false; } } - -static void viafb_load_scaling_factor_for_p4m900(int set_hres, - int set_vres, int panel_hres, int panel_vres) -{ - int h_scaling_factor; - int v_scaling_factor; - u8 cra2 = 0; - u8 cr77 = 0; - u8 cr78 = 0; - u8 cr79 = 0; - u8 cr9f = 0; - /* Check if expansion for horizontal */ - if (set_hres < panel_hres) { - /* Load Horizontal Scaling Factor */ - - /* For VIA_K8M800 or later chipsets. */ - h_scaling_factor = - K800_LCD_HOR_SCF_FORMULA(set_hres, panel_hres); - /* HSCaleFactor[1:0] at CR9F[1:0] */ - cr9f = h_scaling_factor & 0x0003; - /* HSCaleFactor[9:2] at CR77[7:0] */ - cr77 = (h_scaling_factor & 0x03FC) >> 2; - /* HSCaleFactor[11:10] at CR79[5:4] */ - cr79 = (h_scaling_factor & 0x0C00) >> 10; - cr79 <<= 4; - - /* Horizontal scaling enabled */ - cra2 = 0xC0; - - DEBUG_MSG(KERN_INFO "Horizontal Scaling value = %d\n", - h_scaling_factor); - } else { - /* Horizontal scaling disabled */ - cra2 = 0x00; - } - - /* Check if expansion for vertical */ - if (set_vres < panel_vres) { - /* Load Vertical Scaling Factor */ - - /* For VIA_K8M800 or later chipsets. */ - v_scaling_factor = - K800_LCD_VER_SCF_FORMULA(set_vres, panel_vres); - - /* Vertical scaling enabled */ - cra2 |= 0x08; - /* VSCaleFactor[0] at CR79[3] */ - cr79 |= ((v_scaling_factor & 0x0001) << 3); - /* VSCaleFactor[8:1] at CR78[7:0] */ - cr78 |= (v_scaling_factor & 0x01FE) >> 1; - /* VSCaleFactor[10:9] at CR79[7:6] */ - cr79 |= ((v_scaling_factor & 0x0600) >> 9) << 6; - - DEBUG_MSG(KERN_INFO "Vertical Scaling value = %d\n", - v_scaling_factor); - } else { - /* Vertical scaling disabled */ - cra2 |= 0x00; - } - - viafb_write_reg_mask(CRA2, VIACR, cra2, BIT3 + BIT6 + BIT7); - viafb_write_reg_mask(CR77, VIACR, cr77, 0xFF); - viafb_write_reg_mask(CR78, VIACR, cr78, 0xFF); - viafb_write_reg_mask(CR79, VIACR, cr79, 0xF8); - viafb_write_reg_mask(CR9F, VIACR, cr9f, BIT0 + BIT1); -} diff --git a/drivers/video/via/lcd.h b/drivers/video/via/lcd.h index 9762ec62b49..b348efc360b 100644 --- a/drivers/video/via/lcd.h +++ b/drivers/video/via/lcd.h @@ -28,11 +28,6 @@ #define VT3271_DEVICE_ID_REG 0x02 #define VT3271_DEVICE_ID 0x71 -#define GET_LCD_SIZE_BY_SYSTEM_BIOS 0x01 -#define GET_LCD_SIZE_BY_VGA_BIOS 0x02 -#define GET_LCD_SZIE_BY_HW_STRAPPING 0x03 -#define GET_LCD_SIZE_BY_USER_SETTING 0x04 - /* Definition DVI Panel ID*/ /* Resolution: 640x480, Channel: single, Dithering: Enable */ #define LCD_PANEL_ID0_640X480 0x00 diff --git a/drivers/video/via/share.h b/drivers/video/via/share.h index 7f0de7f006a..2cbe1031b42 100644 --- a/drivers/video/via/share.h +++ b/drivers/video/via/share.h @@ -631,7 +631,6 @@ #define CLK_25_175M 25175000 #define CLK_26_880M 26880000 #define CLK_29_581M 29581000 -#define CLK_31_490M 31490000 #define CLK_31_500M 31500000 #define CLK_31_728M 31728000 #define CLK_32_668M 32688000 @@ -676,7 +675,6 @@ #define CLK_119_000M 119000000 #define CLK_121_750M 121750000 /* 121.704MHz */ #define CLK_125_104M 125104000 -#define CLK_133_308M 133308000 #define CLK_135_000M 135000000 #define CLK_136_700M 136700000 #define CLK_138_400M 138400000 @@ -699,313 +697,6 @@ #define CLK_172_798M 172798000 #define CLK_122_614M 122614000 -/* CLE266 PLL value -*/ -#define CLE266_PLL_25_175M 0x0000C763 -#define CLE266_PLL_26_880M 0x0000440F -#define CLE266_PLL_29_581M 0x00008421 -#define CLE266_PLL_31_490M 0x00004721 -#define CLE266_PLL_31_500M 0x0000C3B5 -#define CLE266_PLL_31_728M 0x0000471F -#define CLE266_PLL_32_668M 0x0000C449 -#define CLE266_PLL_36_000M 0x0000C5E5 -#define CLE266_PLL_40_000M 0x0000C459 -#define CLE266_PLL_41_291M 0x00004417 -#define CLE266_PLL_43_163M 0x0000C579 -#define CLE266_PLL_45_250M 0x0000C57F /* 45.46MHz */ -#define CLE266_PLL_46_000M 0x0000875A -#define CLE266_PLL_46_996M 0x0000C4E9 -#define CLE266_PLL_48_000M 0x00001443 -#define CLE266_PLL_48_875M 0x00001D63 -#define CLE266_PLL_49_500M 0x00008653 -#define CLE266_PLL_52_406M 0x0000C475 -#define CLE266_PLL_52_977M 0x00004525 -#define CLE266_PLL_56_250M 0x000047B7 -#define CLE266_PLL_60_466M 0x0000494C -#define CLE266_PLL_61_500M 0x00001456 -#define CLE266_PLL_65_000M 0x000086ED -#define CLE266_PLL_65_178M 0x0000855B -#define CLE266_PLL_66_750M 0x0000844B /* 67.116MHz */ -#define CLE266_PLL_68_179M 0x00000413 -#define CLE266_PLL_69_924M 0x00001153 -#define CLE266_PLL_70_159M 0x00001462 -#define CLE266_PLL_72_000M 0x00001879 -#define CLE266_PLL_74_270M 0x00004853 -#define CLE266_PLL_78_750M 0x00004321 -#define CLE266_PLL_80_136M 0x0000051C -#define CLE266_PLL_83_375M 0x0000C25D -#define CLE266_PLL_83_950M 0x00000729 -#define CLE266_PLL_84_750M 0x00008576 /* 84.537MHz */ -#define CLE266_PLL_85_860M 0x00004754 -#define CLE266_PLL_88_750M 0x0000051F -#define CLE266_PLL_94_500M 0x00000521 -#define CLE266_PLL_97_750M 0x00004652 -#define CLE266_PLL_101_000M 0x0000497F -#define CLE266_PLL_106_500M 0x00008477 /* 106.491463 MHz */ -#define CLE266_PLL_108_000M 0x00008479 -#define CLE266_PLL_113_309M 0x00000C5F -#define CLE266_PLL_118_840M 0x00004553 -#define CLE266_PLL_119_000M 0x00000D6C -#define CLE266_PLL_121_750M 0x00004555 /* 121.704MHz */ -#define CLE266_PLL_125_104M 0x000006B5 -#define CLE266_PLL_133_308M 0x0000465F -#define CLE266_PLL_135_000M 0x0000455E -#define CLE266_PLL_136_700M 0x00000C73 -#define CLE266_PLL_138_400M 0x00000957 -#define CLE266_PLL_146_760M 0x00004567 -#define CLE266_PLL_148_500M 0x00000853 -#define CLE266_PLL_153_920M 0x00000856 -#define CLE266_PLL_156_000M 0x0000456D -#define CLE266_PLL_157_500M 0x000005B7 -#define CLE266_PLL_162_000M 0x00004571 -#define CLE266_PLL_187_000M 0x00000976 -#define CLE266_PLL_193_295M 0x0000086C -#define CLE266_PLL_202_500M 0x00000763 -#define CLE266_PLL_204_000M 0x00000764 -#define CLE266_PLL_218_500M 0x0000065C -#define CLE266_PLL_234_000M 0x00000662 -#define CLE266_PLL_267_250M 0x00000670 -#define CLE266_PLL_297_500M 0x000005E6 -#define CLE266_PLL_74_481M 0x0000051A -#define CLE266_PLL_172_798M 0x00004579 -#define CLE266_PLL_122_614M 0x0000073C - -/* K800 PLL value -*/ -#define K800_PLL_25_175M 0x00539001 -#define K800_PLL_26_880M 0x001C8C80 -#define K800_PLL_29_581M 0x00409080 -#define K800_PLL_31_490M 0x006F9001 -#define K800_PLL_31_500M 0x008B9002 -#define K800_PLL_31_728M 0x00AF9003 -#define K800_PLL_32_668M 0x00909002 -#define K800_PLL_36_000M 0x009F9002 -#define K800_PLL_40_000M 0x00578C02 -#define K800_PLL_41_291M 0x00438C01 -#define K800_PLL_43_163M 0x00778C03 -#define K800_PLL_45_250M 0x007D8C83 /* 45.46MHz */ -#define K800_PLL_46_000M 0x00658C02 -#define K800_PLL_46_996M 0x00818C83 -#define K800_PLL_48_000M 0x00848C83 -#define K800_PLL_48_875M 0x00508C81 -#define K800_PLL_49_500M 0x00518C01 -#define K800_PLL_52_406M 0x00738C02 -#define K800_PLL_52_977M 0x00928C83 -#define K800_PLL_56_250M 0x007C8C02 -#define K800_PLL_60_466M 0x00A78C83 -#define K800_PLL_61_500M 0x00AA8C83 -#define K800_PLL_65_000M 0x006B8C01 -#define K800_PLL_65_178M 0x00B48C83 -#define K800_PLL_66_750M 0x00948C82 /* 67.116MHz */ -#define K800_PLL_68_179M 0x00708C01 -#define K800_PLL_69_924M 0x00C18C83 -#define K800_PLL_70_159M 0x00C28C83 -#define K800_PLL_72_000M 0x009F8C82 -#define K800_PLL_74_270M 0x00ce0c03 -#define K800_PLL_78_750M 0x00408801 -#define K800_PLL_80_136M 0x00428801 -#define K800_PLL_83_375M 0x005B0882 -#define K800_PLL_83_950M 0x00738803 -#define K800_PLL_84_750M 0x00748883 /* 84.477MHz */ -#define K800_PLL_85_860M 0x00768883 -#define K800_PLL_88_750M 0x007A8883 -#define K800_PLL_94_500M 0x00828803 -#define K800_PLL_97_750M 0x00878883 -#define K800_PLL_101_000M 0x008B8883 -#define K800_PLL_106_500M 0x00758882 /* 106.491463 MHz */ -#define K800_PLL_108_000M 0x00778882 -#define K800_PLL_113_309M 0x005D8881 -#define K800_PLL_118_840M 0x00A48883 -#define K800_PLL_119_000M 0x00838882 -#define K800_PLL_121_750M 0x00A88883 /* 121.704MHz */ -#define K800_PLL_125_104M 0x00688801 -#define K800_PLL_133_308M 0x005D8801 -#define K800_PLL_135_000M 0x001A4081 -#define K800_PLL_136_700M 0x00BD8883 -#define K800_PLL_138_400M 0x00728881 -#define K800_PLL_146_760M 0x00CC8883 -#define K800_PLL_148_500M 0x00ce0803 -#define K800_PLL_153_920M 0x00548482 -#define K800_PLL_156_000M 0x006B8483 -#define K800_PLL_157_500M 0x00142080 -#define K800_PLL_162_000M 0x006F8483 -#define K800_PLL_187_000M 0x00818483 -#define K800_PLL_193_295M 0x004F8481 -#define K800_PLL_202_500M 0x00538481 -#define K800_PLL_204_000M 0x008D8483 -#define K800_PLL_218_500M 0x00978483 -#define K800_PLL_234_000M 0x00608401 -#define K800_PLL_267_250M 0x006E8481 -#define K800_PLL_297_500M 0x00A48402 -#define K800_PLL_74_481M 0x007B8C81 -#define K800_PLL_172_798M 0x00778483 -#define K800_PLL_122_614M 0x00878882 - -/* PLL for VT3324 */ -#define CX700_25_175M 0x008B1003 -#define CX700_26_719M 0x00931003 -#define CX700_26_880M 0x00941003 -#define CX700_29_581M 0x00A49003 -#define CX700_31_490M 0x00AE1003 -#define CX700_31_500M 0x00AE1003 -#define CX700_31_728M 0x00AF1003 -#define CX700_32_668M 0x00B51003 -#define CX700_36_000M 0x00C81003 -#define CX700_40_000M 0x006E0C03 -#define CX700_41_291M 0x00710C03 -#define CX700_43_163M 0x00770C03 -#define CX700_45_250M 0x007D0C03 /* 45.46MHz */ -#define CX700_46_000M 0x007F0C03 -#define CX700_46_996M 0x00818C83 -#define CX700_48_000M 0x00840C03 -#define CX700_48_875M 0x00508C81 -#define CX700_49_500M 0x00880C03 -#define CX700_52_406M 0x00730C02 -#define CX700_52_977M 0x00920C03 -#define CX700_56_250M 0x009B0C03 -#define CX700_60_466M 0x00460C00 -#define CX700_61_500M 0x00AA0C03 -#define CX700_65_000M 0x006B0C01 -#define CX700_65_178M 0x006B0C01 -#define CX700_66_750M 0x00940C02 /*67.116MHz */ -#define CX700_68_179M 0x00BC0C03 -#define CX700_69_924M 0x00C10C03 -#define CX700_70_159M 0x00C20C03 -#define CX700_72_000M 0x009F0C02 -#define CX700_74_270M 0x00CE0C03 -#define CX700_74_481M 0x00CE0C03 -#define CX700_78_750M 0x006C0803 -#define CX700_80_136M 0x006E0803 -#define CX700_83_375M 0x005B0882 -#define CX700_83_950M 0x00730803 -#define CX700_84_750M 0x00740803 /* 84.537Mhz */ -#define CX700_85_860M 0x00760803 -#define CX700_88_750M 0x00AC8885 -#define CX700_94_500M 0x00820803 -#define CX700_97_750M 0x00870803 -#define CX700_101_000M 0x008B0803 -#define CX700_106_500M 0x00750802 -#define CX700_108_000M 0x00950803 -#define CX700_113_309M 0x005D0801 -#define CX700_118_840M 0x00A40803 -#define CX700_119_000M 0x00830802 -#define CX700_121_750M 0x00420800 /* 121.704MHz */ -#define CX700_125_104M 0x00AD0803 -#define CX700_133_308M 0x00930802 -#define CX700_135_000M 0x00950802 -#define CX700_136_700M 0x00BD0803 -#define CX700_138_400M 0x00720801 -#define CX700_146_760M 0x00CC0803 -#define CX700_148_500M 0x00a40802 -#define CX700_153_920M 0x00540402 -#define CX700_156_000M 0x006B0403 -#define CX700_157_500M 0x006C0403 -#define CX700_162_000M 0x006F0403 -#define CX700_172_798M 0x00770403 -#define CX700_187_000M 0x00810403 -#define CX700_193_295M 0x00850403 -#define CX700_202_500M 0x008C0403 -#define CX700_204_000M 0x008D0403 -#define CX700_218_500M 0x00970403 -#define CX700_234_000M 0x00600401 -#define CX700_267_250M 0x00B90403 -#define CX700_297_500M 0x00CE0403 -#define CX700_122_614M 0x00870802 - -/* PLL for VX855 */ -#define VX855_22_000M 0x007B1005 -#define VX855_25_175M 0x008D1005 -#define VX855_26_719M 0x00961005 -#define VX855_26_880M 0x00961005 -#define VX855_27_000M 0x00971005 -#define VX855_29_581M 0x00A51005 -#define VX855_29_829M 0x00641003 -#define VX855_31_490M 0x00B01005 -#define VX855_31_500M 0x00B01005 -#define VX855_31_728M 0x008E1004 -#define VX855_32_668M 0x00921004 -#define VX855_36_000M 0x00A11004 -#define VX855_40_000M 0x00700C05 -#define VX855_41_291M 0x00730C05 -#define VX855_43_163M 0x00790C05 -#define VX855_45_250M 0x007F0C05 /* 45.46MHz */ -#define VX855_46_000M 0x00670C04 -#define VX855_46_996M 0x00690C04 -#define VX855_48_000M 0x00860C05 -#define VX855_48_875M 0x00890C05 -#define VX855_49_500M 0x00530C03 -#define VX855_52_406M 0x00580C03 -#define VX855_52_977M 0x00940C05 -#define VX855_56_250M 0x009D0C05 -#define VX855_57_275M 0x009D8C85 /* Used by XO panel */ -#define VX855_60_466M 0x00A90C05 -#define VX855_61_500M 0x00AC0C05 -#define VX855_65_000M 0x006D0C03 -#define VX855_65_178M 0x00B60C05 -#define VX855_66_750M 0x00700C03 /*67.116MHz */ -#define VX855_67_295M 0x00BC0C05 -#define VX855_68_179M 0x00BF0C05 -#define VX855_68_369M 0x00BF0C05 -#define VX855_69_924M 0x00C30C05 -#define VX855_70_159M 0x00C30C05 -#define VX855_72_000M 0x00A10C04 -#define VX855_73_023M 0x00CC0C05 -#define VX855_74_481M 0x00D10C05 -#define VX855_78_750M 0x006E0805 -#define VX855_79_466M 0x006F0805 -#define VX855_80_136M 0x00700805 -#define VX855_81_627M 0x00720805 -#define VX855_83_375M 0x00750805 -#define VX855_83_527M 0x00750805 -#define VX855_83_950M 0x00750805 -#define VX855_84_537M 0x00760805 -#define VX855_84_750M 0x00760805 /* 84.537Mhz */ -#define VX855_85_500M 0x00760805 /* 85.909080 MHz*/ -#define VX855_85_860M 0x00760805 -#define VX855_85_909M 0x00760805 -#define VX855_88_750M 0x007C0805 -#define VX855_89_489M 0x007D0805 -#define VX855_94_500M 0x00840805 -#define VX855_96_648M 0x00870805 -#define VX855_97_750M 0x00890805 -#define VX855_101_000M 0x008D0805 -#define VX855_106_500M 0x00950805 -#define VX855_108_000M 0x00970805 -#define VX855_110_125M 0x00990805 -#define VX855_112_000M 0x009D0805 -#define VX855_113_309M 0x009F0805 -#define VX855_115_000M 0x00A10805 -#define VX855_118_840M 0x00A60805 -#define VX855_119_000M 0x00A70805 -#define VX855_121_750M 0x00AA0805 /* 121.704MHz */ -#define VX855_122_614M 0x00AC0805 -#define VX855_126_266M 0x00B10805 -#define VX855_130_250M 0x00B60805 /* 130.250 */ -#define VX855_135_000M 0x00BD0805 -#define VX855_136_700M 0x00BF0805 -#define VX855_137_750M 0x00C10805 -#define VX855_138_400M 0x00C20805 -#define VX855_144_300M 0x00CA0805 -#define VX855_146_760M 0x00CE0805 -#define VX855_148_500M 0x00D00805 -#define VX855_153_920M 0x00540402 -#define VX855_156_000M 0x006C0405 -#define VX855_156_867M 0x006E0405 -#define VX855_157_500M 0x006E0405 -#define VX855_162_000M 0x00710405 -#define VX855_172_798M 0x00790405 -#define VX855_187_000M 0x00830405 -#define VX855_193_295M 0x00870405 -#define VX855_202_500M 0x008E0405 -#define VX855_204_000M 0x008F0405 -#define VX855_218_500M 0x00990405 -#define VX855_229_500M 0x00A10405 -#define VX855_234_000M 0x00A40405 -#define VX855_267_250M 0x00BB0405 -#define VX855_297_500M 0x00D00405 -#define VX855_339_500M 0x00770005 -#define VX855_340_772M 0x00770005 - /* Definition CRTC Timing Index */ #define H_TOTAL_INDEX 0 diff --git a/drivers/video/via/via-core.c b/drivers/video/via/via-core.c index e8cfe839211..66f40303311 100644 --- a/drivers/video/via/via-core.c +++ b/drivers/video/via/via-core.c @@ -64,7 +64,7 @@ static inline int viafb_mmio_read(int reg) */ static u32 viafb_enabled_ints; -static void viafb_int_init(void) +static void __devinit viafb_int_init(void) { viafb_enabled_ints = 0; @@ -489,7 +489,7 @@ out_unmap: return ret; } -static void __devexit via_pci_teardown_mmio(struct viafb_dev *vdev) +static void via_pci_teardown_mmio(struct viafb_dev *vdev) { iounmap(vdev->fbmem); iounmap(vdev->engine_mmio); @@ -548,7 +548,7 @@ static int __devinit via_setup_subdevs(struct viafb_dev *vdev) return 0; } -static void __devexit via_teardown_subdevs(void) +static void via_teardown_subdevs(void) { int i; @@ -613,22 +613,24 @@ static void __devexit via_pci_remove(struct pci_dev *pdev) static struct pci_device_id via_pci_table[] __devinitdata = { { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_CLE266_DID), .driver_data = UNICHROME_CLE266 }, - { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_PM800_DID), - .driver_data = UNICHROME_PM800 }, { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_K400_DID), .driver_data = UNICHROME_K400 }, { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_K800_DID), .driver_data = UNICHROME_K800 }, - { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_P4M890_DID), + { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_PM800_DID), + .driver_data = UNICHROME_PM800 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_CN700_DID), .driver_data = UNICHROME_CN700 }, - { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_K8M890_DID), - .driver_data = UNICHROME_K8M890 }, { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_CX700_DID), .driver_data = UNICHROME_CX700 }, - { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_P4M900_DID), - .driver_data = UNICHROME_P4M900 }, { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_CN750_DID), .driver_data = UNICHROME_CN750 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_K8M890_DID), + .driver_data = UNICHROME_K8M890 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_P4M890_DID), + .driver_data = UNICHROME_P4M890 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_P4M900_DID), + .driver_data = UNICHROME_P4M900 }, { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_VX800_DID), .driver_data = UNICHROME_VX800 }, { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_VX855_DID), diff --git a/drivers/video/via/via-gpio.c b/drivers/video/via/via-gpio.c index 595516aea69..39acb37e7a1 100644 --- a/drivers/video/via/via-gpio.c +++ b/drivers/video/via/via-gpio.c @@ -73,7 +73,7 @@ struct viafb_gpio_cfg { struct gpio_chip gpio_chip; struct viafb_dev *vdev; struct viafb_gpio *active_gpios[VIAFB_NUM_GPIOS]; - char *gpio_names[VIAFB_NUM_GPIOS]; + const char *gpio_names[VIAFB_NUM_GPIOS]; }; /* diff --git a/drivers/video/via/viafbdev.c b/drivers/video/via/viafbdev.c index 1082541358f..bdd0e4130f4 100644 --- a/drivers/video/via/viafbdev.c +++ b/drivers/video/via/viafbdev.c @@ -49,11 +49,6 @@ char *viafb_active_dev; char *viafb_lcd_port = ""; char *viafb_dvi_port = ""; -static void viafb_set_device(struct device_t active_dev); -static int apply_device_setting(struct viafb_ioctl_setting setting_info, - struct fb_info *info); -static void apply_second_mode_setting(struct fb_var_screeninfo - *sec_var); static void retrieve_device_setting(struct viafb_ioctl_setting *setting_info); static int viafb_pan_display(struct fb_var_screeninfo *var, @@ -221,9 +216,9 @@ static int viafb_check_var(struct fb_var_screeninfo *var, /* Adjust var according to our driver's own table */ viafb_fill_var_timing_info(var, viafb_refresh, vmode_entry); - if (info->var.accel_flags & FB_ACCELF_TEXT && + if (var->accel_flags & FB_ACCELF_TEXT && !ppar->shared->vdev->engine_mmio) - info->var.accel_flags = 0; + var->accel_flags = 0; return 0; } @@ -234,6 +229,7 @@ static int viafb_set_par(struct fb_info *info) struct VideoModeTable *vmode_entry, *vmode_entry1 = NULL; DEBUG_MSG(KERN_INFO "viafb_set_par!\n"); + viafb_update_fix(info); viapar->depth = fb_get_color_depth(&info->var, &info->fix); viafb_update_device_setting(viafbinfo->var.xres, viafbinfo->var.yres, viafbinfo->var.bits_per_pixel, viafb_refresh, 0); @@ -257,7 +253,6 @@ static int viafb_set_par(struct fb_info *info) } if (vmode_entry) { - viafb_update_fix(info); if (viafb_dual_fb && viapar->iga_path == IGA2) viafb_bpp1 = info->var.bits_per_pixel; else @@ -478,13 +473,6 @@ static int viafb_ioctl(struct fb_info *info, u_int cmd, u_long arg) if (gpu32 & LCD_Device) viafb_lcd_disable(); break; - case VIAFB_SET_DEVICE: - if (copy_from_user(&u.active_dev, (void *)argp, - sizeof(u.active_dev))) - return -EFAULT; - viafb_set_device(u.active_dev); - viafb_set_par(info); - break; case VIAFB_GET_DEVICE: u.active_dev.crt = viafb_CRT_ON; u.active_dev.dvi = viafb_DVI_ON; @@ -527,21 +515,6 @@ static int viafb_ioctl(struct fb_info *info, u_int cmd, u_long arg) break; - case VIAFB_SET_DEVICE_INFO: - if (copy_from_user(&u.viafb_setting, - argp, sizeof(u.viafb_setting))) - return -EFAULT; - if (apply_device_setting(u.viafb_setting, info) < 0) - return -EINVAL; - - break; - - case VIAFB_SET_SECOND_MODE: - if (copy_from_user(&u.sec_var, argp, sizeof(u.sec_var))) - return -EFAULT; - apply_second_mode_setting(&u.sec_var); - break; - case VIAFB_GET_DEVICE_INFO: retrieve_device_setting(&u.viafb_setting); @@ -913,112 +886,6 @@ static int viafb_sync(struct fb_info *info) return 0; } -static void check_available_device_to_enable(int device_id) -{ - int device_num = 0; - - /* Initialize: */ - viafb_CRT_ON = STATE_OFF; - viafb_DVI_ON = STATE_OFF; - viafb_LCD_ON = STATE_OFF; - viafb_LCD2_ON = STATE_OFF; - viafb_DeviceStatus = None_Device; - - if ((device_id & CRT_Device) && (device_num < MAX_ACTIVE_DEV_NUM)) { - viafb_CRT_ON = STATE_ON; - device_num++; - viafb_DeviceStatus |= CRT_Device; - } - - if ((device_id & DVI_Device) && (device_num < MAX_ACTIVE_DEV_NUM)) { - viafb_DVI_ON = STATE_ON; - device_num++; - viafb_DeviceStatus |= DVI_Device; - } - - if ((device_id & LCD_Device) && (device_num < MAX_ACTIVE_DEV_NUM)) { - viafb_LCD_ON = STATE_ON; - device_num++; - viafb_DeviceStatus |= LCD_Device; - } - - if ((device_id & LCD2_Device) && (device_num < MAX_ACTIVE_DEV_NUM)) { - viafb_LCD2_ON = STATE_ON; - device_num++; - viafb_DeviceStatus |= LCD2_Device; - } - - if (viafb_DeviceStatus == None_Device) { - /* Use CRT as default active device: */ - viafb_CRT_ON = STATE_ON; - viafb_DeviceStatus = CRT_Device; - } - DEBUG_MSG(KERN_INFO "Device Status:%x", viafb_DeviceStatus); -} - -static void viafb_set_device(struct device_t active_dev) -{ - /* Check available device to enable: */ - int device_id = None_Device; - if (active_dev.crt) - device_id |= CRT_Device; - if (active_dev.dvi) - device_id |= DVI_Device; - if (active_dev.lcd) - device_id |= LCD_Device; - - check_available_device_to_enable(device_id); - - /* Check property of LCD: */ - if (viafb_LCD_ON) { - if (active_dev.lcd_dsp_cent) { - viaparinfo->lvds_setting_info->display_method = - viafb_lcd_dsp_method = LCD_CENTERING; - } else { - viaparinfo->lvds_setting_info->display_method = - viafb_lcd_dsp_method = LCD_EXPANDSION; - } - - if (active_dev.lcd_mode == LCD_SPWG) { - viaparinfo->lvds_setting_info->lcd_mode = - viafb_lcd_mode = LCD_SPWG; - } else { - viaparinfo->lvds_setting_info->lcd_mode = - viafb_lcd_mode = LCD_OPENLDI; - } - - if (active_dev.lcd_panel_id <= LCD_PANEL_ID_MAXIMUM) { - viafb_lcd_panel_id = active_dev.lcd_panel_id; - viafb_init_lcd_size(); - } - } - - /* Check property of mode: */ - if (!active_dev.xres1) - viafb_second_xres = 640; - else - viafb_second_xres = active_dev.xres1; - if (!active_dev.yres1) - viafb_second_yres = 480; - else - viafb_second_yres = active_dev.yres1; - if (active_dev.bpp != 0) - viafb_bpp = active_dev.bpp; - if (active_dev.bpp1 != 0) - viafb_bpp1 = active_dev.bpp1; - if (active_dev.refresh != 0) - viafb_refresh = active_dev.refresh; - if (active_dev.refresh1 != 0) - viafb_refresh1 = active_dev.refresh1; - if ((active_dev.samm == STATE_OFF) || (active_dev.samm == STATE_ON)) - viafb_SAMM_ON = active_dev.samm; - viafb_primary_dev = active_dev.primary_dev; - - via_set_primary_address(0); - via_set_secondary_address(viafb_SAMM_ON ? viafb_second_offset : 0); - viafb_set_iga_path(); -} - static int get_primary_device(void) { int primary_device = 0; @@ -1060,124 +927,6 @@ static int get_primary_device(void) return primary_device; } -static void apply_second_mode_setting(struct fb_var_screeninfo - *sec_var) -{ - u32 htotal, vtotal, long_refresh; - - htotal = sec_var->xres + sec_var->left_margin + - sec_var->right_margin + sec_var->hsync_len; - vtotal = sec_var->yres + sec_var->upper_margin + - sec_var->lower_margin + sec_var->vsync_len; - if ((sec_var->xres_virtual * (sec_var->bits_per_pixel >> 3)) & 0x1F) { - /*Is 32 bytes alignment? */ - /*32 pixel alignment */ - sec_var->xres_virtual = (sec_var->xres_virtual + 31) & ~31; - } - - htotal = sec_var->xres + sec_var->left_margin + - sec_var->right_margin + sec_var->hsync_len; - vtotal = sec_var->yres + sec_var->upper_margin + - sec_var->lower_margin + sec_var->vsync_len; - long_refresh = 1000000000UL / sec_var->pixclock * 1000; - long_refresh /= (htotal * vtotal); - - viafb_second_xres = sec_var->xres; - viafb_second_yres = sec_var->yres; - viafb_second_virtual_xres = sec_var->xres_virtual; - viafb_second_virtual_yres = sec_var->yres_virtual; - viafb_bpp1 = sec_var->bits_per_pixel; - viafb_refresh1 = viafb_get_refresh(sec_var->xres, sec_var->yres, - long_refresh); -} - -static int apply_device_setting(struct viafb_ioctl_setting setting_info, - struct fb_info *info) -{ - int need_set_mode = 0; - DEBUG_MSG(KERN_INFO "apply_device_setting\n"); - - if (setting_info.device_flag) { - need_set_mode = 1; - check_available_device_to_enable(setting_info.device_status); - } - - /* Unlock LCD's operation according to LCD flag - and check if the setting value is valid. */ - /* If the value is valid, apply the new setting value to the device. */ - if (viafb_LCD_ON) { - if (setting_info.lcd_operation_flag & OP_LCD_CENTERING) { - need_set_mode = 1; - if (setting_info.lcd_attributes.display_center) { - /* Centering */ - viaparinfo->lvds_setting_info->display_method = - LCD_CENTERING; - viafb_lcd_dsp_method = LCD_CENTERING; - viaparinfo->lvds_setting_info2->display_method = - viafb_lcd_dsp_method = LCD_CENTERING; - } else { - /* expandsion */ - viaparinfo->lvds_setting_info->display_method = - LCD_EXPANDSION; - viafb_lcd_dsp_method = LCD_EXPANDSION; - viaparinfo->lvds_setting_info2->display_method = - LCD_EXPANDSION; - viafb_lcd_dsp_method = LCD_EXPANDSION; - } - } - - if (setting_info.lcd_operation_flag & OP_LCD_MODE) { - need_set_mode = 1; - if (setting_info.lcd_attributes.lcd_mode == - LCD_SPWG) { - viaparinfo->lvds_setting_info->lcd_mode = - viafb_lcd_mode = LCD_SPWG; - } else { - viaparinfo->lvds_setting_info->lcd_mode = - viafb_lcd_mode = LCD_OPENLDI; - } - viaparinfo->lvds_setting_info2->lcd_mode = - viaparinfo->lvds_setting_info->lcd_mode; - } - - if (setting_info.lcd_operation_flag & OP_LCD_PANEL_ID) { - need_set_mode = 1; - if (setting_info.lcd_attributes.panel_id <= - LCD_PANEL_ID_MAXIMUM) { - viafb_lcd_panel_id = - setting_info.lcd_attributes.panel_id; - viafb_init_lcd_size(); - } - } - } - - if (0 != (setting_info.samm_status & OP_SAMM)) { - setting_info.samm_status = - setting_info.samm_status & (~OP_SAMM); - if (setting_info.samm_status == 0 - || setting_info.samm_status == 1) { - viafb_SAMM_ON = setting_info.samm_status; - - if (viafb_SAMM_ON) - viafb_primary_dev = setting_info.primary_device; - - via_set_primary_address(0); - via_set_secondary_address(viafb_SAMM_ON ? - viafb_second_offset : 0); - viafb_set_iga_path(); - } - need_set_mode = 1; - } - - if (!need_set_mode) { - ; - } else { - viafb_set_iga_path(); - viafb_set_par(info); - } - return true; -} - static void retrieve_device_setting(struct viafb_ioctl_setting *setting_info) { @@ -1776,10 +1525,6 @@ int __devinit via_fb_pci_probe(struct viafb_dev *vdev) parse_lcd_port(); parse_dvi_port(); - /* for dual-fb must viafb_SAMM_ON=1 and viafb_dual_fb=1 */ - if (!viafb_SAMM_ON) - viafb_dual_fb = 0; - viafb_init_chip_info(vdev->chip_type); /* * The framebuffer will have been successfully mapped by @@ -1823,30 +1568,13 @@ int __devinit via_fb_pci_probe(struct viafb_dev *vdev) parse_mode(viafb_mode1, &viafb_second_xres, &viafb_second_yres); - if (0 == viafb_second_virtual_xres) { - switch (viafb_second_xres) { - case 1400: - viafb_second_virtual_xres = 1408; - break; - default: - viafb_second_virtual_xres = viafb_second_xres; - break; - } - } - if (0 == viafb_second_virtual_yres) - viafb_second_virtual_yres = viafb_second_yres; + viafb_second_virtual_xres = viafb_second_xres; + viafb_second_virtual_yres = viafb_second_yres; } default_var.xres = default_xres; default_var.yres = default_yres; - switch (default_xres) { - case 1400: - default_var.xres_virtual = 1408; - break; - default: - default_var.xres_virtual = default_xres; - break; - } + default_var.xres_virtual = default_xres; default_var.yres_virtual = default_yres; default_var.bits_per_pixel = viafb_bpp; default_var.pixclock = diff --git a/drivers/video/vt8623fb.c b/drivers/video/vt8623fb.c index d31dc96f838..85d76ec4c63 100644 --- a/drivers/video/vt8623fb.c +++ b/drivers/video/vt8623fb.c @@ -726,7 +726,9 @@ static int __devinit vt8623_pci_probe(struct pci_dev *dev, const struct pci_devi /* Prepare startup mode */ + kparam_block_sysfs_write(mode_option); rc = fb_find_mode(&(info->var), info, mode_option, NULL, 0, NULL, 8); + kparam_unblock_sysfs_write(mode_option); if (! ((rc == 1) || (rc == 2))) { rc = -EINVAL; dev_err(info->device, "mode %s not found\n", mode_option); diff --git a/drivers/video/w100fb.c b/drivers/video/w100fb.c index e66b8b19ce5..d8b12c32e3e 100644 --- a/drivers/video/w100fb.c +++ b/drivers/video/w100fb.c @@ -858,9 +858,9 @@ unsigned long w100fb_gpio_read(int port) void w100fb_gpio_write(int port, unsigned long value) { if (port==W100_GPIO_PORT_A) - value = writel(value, remapped_regs + mmGPIO_DATA); + writel(value, remapped_regs + mmGPIO_DATA); else - value = writel(value, remapped_regs + mmGPIO_DATA2); + writel(value, remapped_regs + mmGPIO_DATA2); } EXPORT_SYMBOL(w100fb_gpio_read); EXPORT_SYMBOL(w100fb_gpio_write); diff --git a/drivers/video/xen-fbfront.c b/drivers/video/xen-fbfront.c index fa97d3e7c21..7c7f42a1279 100644 --- a/drivers/video/xen-fbfront.c +++ b/drivers/video/xen-fbfront.c @@ -684,7 +684,7 @@ static struct xenbus_driver xenfb_driver = { static int __init xenfb_init(void) { - if (!xen_domain()) + if (!xen_pv_domain()) return -ENODEV; /* Nothing to do if running in dom0. */ diff --git a/drivers/video/xilinxfb.c b/drivers/video/xilinxfb.c index 574dc54e12d..0c9ce88e95e 100644 --- a/drivers/video/xilinxfb.c +++ b/drivers/video/xilinxfb.c @@ -397,7 +397,7 @@ static int xilinxfb_release(struct device *dev) */ static int __devinit -xilinxfb_of_probe(struct of_device *op, const struct of_device_id *match) +xilinxfb_of_probe(struct platform_device *op, const struct of_device_id *match) { const u32 *prop; u32 *p; @@ -477,7 +477,7 @@ xilinxfb_of_probe(struct of_device *op, const struct of_device_id *match) return -ENODEV; } -static int __devexit xilinxfb_of_remove(struct of_device *op) +static int __devexit xilinxfb_of_remove(struct platform_device *op) { return xilinxfb_release(&op->dev); } @@ -485,6 +485,8 @@ static int __devexit xilinxfb_of_remove(struct of_device *op) /* Match table for of_platform binding */ static struct of_device_id xilinxfb_of_match[] __devinitdata = { { .compatible = "xlnx,xps-tft-1.00.a", }, + { .compatible = "xlnx,xps-tft-2.00.a", }, + { .compatible = "xlnx,xps-tft-2.01.a", }, { .compatible = "xlnx,plb-tft-cntlr-ref-1.00.a", }, { .compatible = "xlnx,plb-dvi-cntlr-ref-1.00.c", }, {}, |