From: Imre Deak Date: Thu, 9 Feb 2006 07:22:46 +0000 (+0200) Subject: ARM: OMAP: A whopping FB driver update X-Git-Tag: v2.6.16-omap1~89 X-Git-Url: http://pilppa.com/gitweb/?a=commitdiff_plain;h=420e73d551cfa8979e27da543149929aa78e3dfa;p=linux-2.6-omap-h63xx.git ARM: OMAP: A whopping FB driver update - Support for the Philips LPH8923 LCD panel - Support for the Epson HWA742 LCD controller - Support for frame buffer located in SRAM and/or SDRAM - Support for boot loader initialized LCD controller / frame buffer content. - LCD panels will now register a device in the relevant board-* files instead of specifying the LCD type as an OMAP_TAG_LCD. The controller type is still specified in OMAP_TAG_LCD. - A new ATAG OMAP_TAG_FBMEM is used to describe the frame buffer memory configuration (SRAM and SDRAM regions) - Changed the OMAP1 LCD controller driver to export its functions through the lcd_ctrl object. - OMAP1 pixel clock divider will now round up in lcdc.c - Changed the OMAP1 SoSSI driver to export its functions through the lcd_ctrl_extif object. - OMAP1 SoSSI clock calculation goes through all possible clock dividers. Rounding of clock tick values now takes places at each timing parameter. - OMAP2 RFBI clock calculation goes through all possible clock dividers. Rounding of clock tick values now takes places at each timing parameter. - OMAP2 pixel clock divider will now round up in dispc.c Signed-off-by: Imre Deak Signed-off-by: Juha Yrjölä --- diff --git a/arch/arm/configs/n770_defconfig b/arch/arm/configs/n770_defconfig index c1e2b57eb34..6a3f7306d30 100644 --- a/arch/arm/configs/n770_defconfig +++ b/arch/arm/configs/n770_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux kernel version: 2.6.16-rc2-omap1 -# Wed Feb 8 18:52:38 2006 +# Wed Feb 8 19:07:23 2006 # CONFIG_ARM=y CONFIG_MMU=y @@ -814,10 +814,12 @@ CONFIG_FB=y # CONFIG_FB_TILEBLITTING is not set # CONFIG_FB_S1D13XXX is not set CONFIG_FB_OMAP=y -# CONFIG_FB_OMAP_LCDC_INTERNAL is not set CONFIG_FB_OMAP_LCDC_EXTERNAL=y +CONFIG_FB_OMAP_LCDC_HWA742=y CONFIG_FB_OMAP_MANUAL_UPDATE=y -CONFIG_FB_OMAP_DMA_TUNE=y +CONFIG_FB_OMAP_LCD_LPH8923=y +# CONFIG_FB_OMAP_BOOTLOADER_INIT is not set +# CONFIG_FB_OMAP_DMA_TUNE is not set # CONFIG_FB_VIRTUAL is not set # diff --git a/arch/arm/mach-omap1/board-h2.c b/arch/arm/mach-omap1/board-h2.c index 4cc6a31521f..680e67ed654 100644 --- a/arch/arm/mach-omap1/board-h2.c +++ b/arch/arm/mach-omap1/board-h2.c @@ -271,12 +271,18 @@ static struct platform_device h2_irda_device = { .resource = h2_irda_resources, }; +static struct platform_device h2_lcd_device = { + .name = "lcd_h2", + .id = -1, +}; + static struct platform_device *h2_devices[] __initdata = { &h2_nor_device, &h2_nand_device, &h2_smc91x_device, &h2_irda_device, &h2_kp_device, + &h2_lcd_device, }; static void __init h2_init_smc91x(void) @@ -325,7 +331,6 @@ static struct omap_uart_config h2_uart_config __initdata = { }; static struct omap_lcd_config h2_lcd_config __initdata = { - .panel_name = "h2", .ctrl_name = "internal", }; diff --git a/arch/arm/mach-omap1/board-h3.c b/arch/arm/mach-omap1/board-h3.c index 29e17c8397e..96528226b30 100644 --- a/arch/arm/mach-omap1/board-h3.c +++ b/arch/arm/mach-omap1/board-h3.c @@ -349,6 +349,11 @@ static struct platform_device h3_irda_device = { .resource = h3_irda_resources, }; +static struct platform_device h3_lcd_device = { + .name = "lcd_h3", + .id = -1, +}; + static struct platform_device *devices[] __initdata = { &nor_device, &nand_device, @@ -356,6 +361,7 @@ static struct platform_device *devices[] __initdata = { &intlat_device, &h3_irda_device, &h3_kp_device, + &h3_lcd_device, }; static struct omap_usb_config h3_usb_config __initdata = { @@ -385,7 +391,6 @@ static struct omap_uart_config h3_uart_config __initdata = { }; static struct omap_lcd_config h3_lcd_config __initdata = { - .panel_name = "h3", .ctrl_name = "internal", }; diff --git a/arch/arm/mach-omap1/board-innovator.c b/arch/arm/mach-omap1/board-innovator.c index dde06b5fce4..e90c137a4cf 100644 --- a/arch/arm/mach-omap1/board-innovator.c +++ b/arch/arm/mach-omap1/board-innovator.c @@ -169,10 +169,16 @@ static struct platform_device innovator1510_smc91x_device = { .resource = innovator1510_smc91x_resources, }; +static struct platform_device innovator1510_lcd_device = { + .name = "lcd_inn1510", + .id = -1, +}; + static struct platform_device *innovator1510_devices[] __initdata = { &innovator_flash_device, &innovator1510_smc91x_device, &innovator_kp_device, + &innovator1510_lcd_device, }; #endif /* CONFIG_ARCH_OMAP15XX */ @@ -199,10 +205,16 @@ static struct platform_device innovator1610_smc91x_device = { .resource = innovator1610_smc91x_resources, }; +static struct platform_device innovator1610_lcd_device = { + .name = "inn1610_lcd", + .id = -1, +}; + static struct platform_device *innovator1610_devices[] __initdata = { &innovator_flash_device, &innovator1610_smc91x_device, &innovator_kp_device, + &innovator1610_lcd_device, }; #endif /* CONFIG_ARCH_OMAP16XX */ @@ -248,7 +260,6 @@ static struct omap_usb_config innovator1510_usb_config __initdata = { }; static struct omap_lcd_config innovator1510_lcd_config __initdata = { - .panel_name = "inn1510", .ctrl_name = "internal", }; #endif @@ -270,7 +281,6 @@ static struct omap_usb_config h2_usb_config __initdata = { }; static struct omap_lcd_config innovator1610_lcd_config __initdata = { - .panel_name = "inn1610", .ctrl_name = "internal", }; #endif diff --git a/arch/arm/mach-omap1/board-nokia770.c b/arch/arm/mach-omap1/board-nokia770.c index 5438de3ea77..f56c89120b4 100644 --- a/arch/arm/mach-omap1/board-nokia770.c +++ b/arch/arm/mach-omap1/board-nokia770.c @@ -30,6 +30,15 @@ static void __init omap_nokia770_init_irq(void) omap_init_irq(); } +static struct spi_board_info nokia770_spi_board_info[] __initdata = { + [0] = { + .modalias = "lcd_lph8923", + .bus_num = 2, + .chip_select = 3, + .max_speed_hz = 12000000, + }, +}; + static struct platform_device *nokia770_devices[] __initdata = { }; @@ -70,6 +79,8 @@ static void __init omap_nokia770_init(void) nokia770_config[0].data = &nokia770_usb_config; platform_add_devices(nokia770_devices, ARRAY_SIZE(nokia770_devices)); + spi_register_board_info(nokia770_spi_board_info, + ARRAY_SIZE(nokia770_spi_board_info)); omap_board_config = nokia770_config; omap_board_config_size = ARRAY_SIZE(nokia770_config); omap_serial_init(); diff --git a/arch/arm/mach-omap1/board-osk.c b/arch/arm/mach-omap1/board-osk.c index 9d6098b53fe..16e08b476d6 100644 --- a/arch/arm/mach-omap1/board-osk.c +++ b/arch/arm/mach-omap1/board-osk.c @@ -178,12 +178,18 @@ static struct platform_device osk5912_kp_device = { .resource = osk5912_kp_resources, }; +static struct platform_device osk5912_lcd_device = { + .name = "lcd_osk", + .id = -1, +}; + static struct platform_device *osk5912_devices[] __initdata = { &osk5912_flash_device, &osk5912_smc91x_device, &osk5912_cf_device, &osk5912_mcbsp1_device, &osk5912_kp_device, + &osk5912_lcd_device, }; static void __init osk_init_smc91x(void) @@ -238,7 +244,6 @@ static struct omap_uart_config osk_uart_config __initdata = { }; static struct omap_lcd_config osk_lcd_config __initdata = { - .panel_name = "osk", .ctrl_name = "internal", }; diff --git a/arch/arm/mach-omap1/board-palmte.c b/arch/arm/mach-omap1/board-palmte.c index e488f723677..4bc8a62909b 100644 --- a/arch/arm/mach-omap1/board-palmte.c +++ b/arch/arm/mach-omap1/board-palmte.c @@ -38,6 +38,15 @@ static void __init omap_generic_init_irq(void) omap_init_irq(); } +static struct platform_device palmte_lcd_device = { + .name = "lcd_palmte", + .id = -1, +}; + +static struct platform_device *devices[] __initdata = { + &palmte_lcd_device, +}; + static struct omap_usb_config palmte_usb_config __initdata = { .register_dev = 1, .hmc_mode = 0, @@ -55,7 +64,6 @@ static struct omap_mmc_config palmte_mmc_config __initdata = { }; static struct omap_lcd_config palmte_lcd_config __initdata = { - .panel_name = "palmte", .ctrl_name = "internal", }; @@ -69,6 +77,8 @@ static void __init omap_generic_init(void) { omap_board_config = palmte_config; omap_board_config_size = ARRAY_SIZE(palmte_config); + + platform_add_devices(devices, ARRAY_SIZE(devices)); } static void __init omap_generic_map_io(void) diff --git a/arch/arm/mach-omap1/board-perseus2.c b/arch/arm/mach-omap1/board-perseus2.c index 1dc2dbfeb45..64b45d8ae35 100644 --- a/arch/arm/mach-omap1/board-perseus2.c +++ b/arch/arm/mach-omap1/board-perseus2.c @@ -186,11 +186,17 @@ static struct platform_device kp_device = { .resource = kp_resources, }; +static struct platform_device lcd_device = { + .name = "lcd_p2", + .id = -1, +}; + static struct platform_device *devices[] __initdata = { &nor_device, &nand_device, &smc91x_device, &kp_device, + &lcd_device, }; #define P2_NAND_RB_GPIO_PIN 62 @@ -205,7 +211,6 @@ static struct omap_uart_config perseus2_uart_config __initdata = { }; static struct omap_lcd_config perseus2_lcd_config __initdata = { - .panel_name = "p2", .ctrl_name = "internal", }; diff --git a/arch/arm/mach-omap1/io.c b/arch/arm/mach-omap1/io.c index 26d476d26a5..be3a2a4ee2b 100644 --- a/arch/arm/mach-omap1/io.c +++ b/arch/arm/mach-omap1/io.c @@ -18,6 +18,7 @@ #include #include #include +#include extern int omap1_clk_init(void); extern void omap_check_revision(void); @@ -121,6 +122,7 @@ void __init omap1_map_common_io(void) #endif omap_sram_init(); + omapfb_reserve_mem(); } /* diff --git a/arch/arm/mach-omap2/board-apollon.c b/arch/arm/mach-omap2/board-apollon.c index c4c5b926dfc..6c6ba172cdf 100644 --- a/arch/arm/mach-omap2/board-apollon.c +++ b/arch/arm/mach-omap2/board-apollon.c @@ -120,9 +120,15 @@ static struct platform_device apollon_smc91x_device = { .resource = apollon_smc91x_resources, }; +static struct platform_device apollon_lcd_device = { + .name = "apollon_lcd", + .id = -1, +}; + static struct platform_device *apollon_devices[] __initdata = { &apollon_onenand_device, &apollon_smc91x_device, + &apollon_lcd_device, }; static inline void __init apollon_init_smc91x(void) @@ -169,7 +175,6 @@ static struct omap_mmc_config apollon_mmc_config __initdata = { }; static struct omap_lcd_config apollon_lcd_config __initdata = { - .panel_name = "apollon", .ctrl_name = "internal", }; diff --git a/arch/arm/mach-omap2/board-h4.c b/arch/arm/mach-omap2/board-h4.c index 85117eecfcd..9e64842f3f1 100644 --- a/arch/arm/mach-omap2/board-h4.c +++ b/arch/arm/mach-omap2/board-h4.c @@ -250,11 +250,17 @@ static struct platform_device h4_kp_device = { }, }; +static struct platform_device h4_lcd_device = { + .name = "lcd_h4", + .id = -1, +}; + static struct platform_device *h4_devices[] __initdata = { &h4_smc91x_device, &h4_flash_device, &h4_irda_device, &h4_kp_device, + &h4_lcd_device, }; static inline void __init h4_init_smc91x(void) @@ -301,7 +307,6 @@ static struct omap_mmc_config h4_mmc_config __initdata = { }; static struct omap_lcd_config h4_lcd_config __initdata = { - .panel_name = "h4", .ctrl_name = "internal", }; diff --git a/arch/arm/mach-omap2/io.c b/arch/arm/mach-omap2/io.c index cfe155e7503..7d5711611f2 100644 --- a/arch/arm/mach-omap2/io.c +++ b/arch/arm/mach-omap2/io.c @@ -22,6 +22,7 @@ #include #include +#include extern void omap_sram_init(void); extern int omap2_clk_init(void); @@ -59,6 +60,7 @@ void __init omap2_map_common_io(void) omap2_check_revision(); omap_sram_init(); + omapfb_reserve_mem(); } void __init omap2_init_common_hw(void) diff --git a/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile index c6ab7563c24..58548616a0b 100644 --- a/arch/arm/plat-omap/Makefile +++ b/arch/arm/plat-omap/Makefile @@ -3,7 +3,7 @@ # # Common support -obj-y := common.o sram.o sram-fn.o clock.o devices.o dma.o mux.o gpio.o mcbsp.o usb.o +obj-y := common.o sram.o sram-fn.o clock.o devices.o dma.o mux.o gpio.o mcbsp.o usb.o fb.o obj-m := obj-n := obj- := diff --git a/arch/arm/plat-omap/devices.c b/arch/arm/plat-omap/devices.c index 756b3d913f9..079b67deac0 100644 --- a/arch/arm/plat-omap/devices.c +++ b/arch/arm/plat-omap/devices.c @@ -416,40 +416,6 @@ static void omap_init_rng(void) static inline void omap_init_rng(void) {} #endif -#if defined(CONFIG_FB_OMAP) || defined(CONFIG_FB_OMAP_MODULE) - -static struct omap_lcd_config omap_fb_conf; - -static u64 omap_fb_dma_mask = ~(u32)0; - -static struct platform_device omap_fb_device = { - .name = "omapfb", - .id = -1, - .dev = { - .release = omap_nop_release, - .dma_mask = &omap_fb_dma_mask, - .coherent_dma_mask = ~(u32)0, - .platform_data = &omap_fb_conf, - }, - .num_resources = 0, -}; - -static inline void omap_init_fb(void) -{ - const struct omap_lcd_config *conf; - - conf = omap_get_config(OMAP_TAG_LCD, struct omap_lcd_config); - if (conf != NULL) - omap_fb_conf = *conf; - platform_device_register(&omap_fb_device); -} - -#else - -static inline void omap_init_fb(void) {} - -#endif - /* * This gets called after board-specific INIT_MACHINE, and initializes most * on-chip peripherals accessible on this board (except for few like USB): @@ -475,7 +441,6 @@ static int __init omap_init_devices(void) /* please keep these calls, and their implementations above, * in alphabetical order so they're easier to sort through. */ - omap_init_fb(); omap_init_i2c(); omap_init_kp(); omap_init_mmc(); diff --git a/arch/arm/plat-omap/fb.c b/arch/arm/plat-omap/fb.c new file mode 100644 index 00000000000..6dd5292df07 --- /dev/null +++ b/arch/arm/plat-omap/fb.c @@ -0,0 +1,84 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#if defined(CONFIG_FB_OMAP) || defined(CONFIG_FB_OMAP_MODULE) + +static struct omapfb_platform_data omapfb_config; + +static u64 omap_fb_dma_mask = ~(u32)0; + +/* in devices.c */ +extern void omap_nop_release(struct device *dev); + +static struct platform_device omap_fb_device = { + .name = "omapfb", + .id = -1, + .dev = { + .release = omap_nop_release, + .dma_mask = &omap_fb_dma_mask, + .coherent_dma_mask = ~(u32)0, + .platform_data = &omapfb_config, + }, + .num_resources = 0, +}; + +/* called from map_io */ +void omapfb_reserve_mem(void) +{ + const struct omap_fbmem_config *fbmem_conf; + + omapfb_config.fbmem.fb_sram_start = omap_fb_sram_start; + omapfb_config.fbmem.fb_sram_size = omap_fb_sram_size; + + fbmem_conf = omap_get_config(OMAP_TAG_FBMEM, struct omap_fbmem_config); + + if (fbmem_conf != NULL) { + /* indicate that the bootloader already initialized the + * fb device, so we'll skip that part in the fb driver + */ + omapfb_config.fbmem.fb_sdram_start = fbmem_conf->fb_sdram_start; + omapfb_config.fbmem.fb_sdram_size = fbmem_conf->fb_sdram_size; + if (fbmem_conf->fb_sdram_size) { + pr_info("Reserving %u bytes SDRAM for frame buffer\n", + fbmem_conf->fb_sdram_size); + reserve_bootmem(fbmem_conf->fb_sdram_start, + fbmem_conf->fb_sdram_size); + } + } +} + +static inline int omap_init_fb(void) +{ + const struct omap_lcd_config *conf; + + conf = omap_get_config(OMAP_TAG_LCD, struct omap_lcd_config); + if (conf == NULL) + return 0; + + omapfb_config.lcd = *conf; + + return platform_device_register(&omap_fb_device); +} + +arch_initcall(omap_init_fb); + +#else + +void omapfb_reserve_mem(void) {} + +#endif + + diff --git a/arch/arm/plat-omap/sram.c b/arch/arm/plat-omap/sram.c index b8368b5c73f..b7bf09b1b41 100644 --- a/arch/arm/plat-omap/sram.c +++ b/arch/arm/plat-omap/sram.c @@ -23,6 +23,7 @@ #include #include +#include #define OMAP1_SRAM_PA 0x20000000 #define OMAP1_SRAM_VA 0xd0000000 @@ -50,6 +51,9 @@ static unsigned long omap_sram_base; static unsigned long omap_sram_size; static unsigned long omap_sram_ceil; +unsigned long omap_fb_sram_start; +unsigned long omap_fb_sram_size; + /* Depending on the target RAMFS firewall setup, the public usable amount of * SRAM varies. The default accessable size for all device types is 2k. A GP * device allows ARM11 but not other initators for full size. This @@ -74,6 +78,32 @@ static int is_sram_locked(void) return 1; /* assume locked with no PPA or security driver */ } +void get_fb_sram_conf(unsigned long start_avail, unsigned size_avail, + unsigned long *start, unsigned long *size) +{ + const struct omap_fbmem_config *fbmem_conf; + + fbmem_conf = omap_get_config(OMAP_TAG_FBMEM, struct omap_fbmem_config); + if (fbmem_conf != NULL) { + *start = fbmem_conf->fb_sram_start; + *size = fbmem_conf->fb_sram_size; + } else { + *size = 0; + *start = 0; + } + + if (*size && ( + *start < start_avail || + *start + *size > start_avail + size_avail)) { + printk(KERN_ERR "invalid FB SRAM configuration\n"); + *start = start_avail; + *size = size_avail; + } + + if (*size) + pr_info("Reserving %lu bytes SRAM for frame buffer\n", *size); +} + /* * The amount of SRAM depends on the core type. * Note that we cannot try to test for SRAM here because writes @@ -82,12 +112,16 @@ static int is_sram_locked(void) */ void __init omap_detect_sram(void) { + unsigned long sram_start; + if (cpu_is_omap24xx()) { if (is_sram_locked()) { omap_sram_base = OMAP2_SRAM_PUB_VA; + sram_start = OMAP2_SRAM_PUB_PA; omap_sram_size = 0x800; /* 2K */ } else { omap_sram_base = OMAP2_SRAM_VA; + sram_start = OMAP2_SRAM_PA; if (cpu_is_omap242x()) omap_sram_size = 0xa0000; /* 640K */ else if (cpu_is_omap243x()) @@ -95,6 +129,7 @@ void __init omap_detect_sram(void) } } else { omap_sram_base = OMAP1_SRAM_VA; + sram_start = OMAP1_SRAM_PA; if (cpu_is_omap730()) omap_sram_size = 0x32000; /* 200K */ @@ -110,6 +145,12 @@ void __init omap_detect_sram(void) omap_sram_size = 0x4000; } } + get_fb_sram_conf(sram_start + SRAM_BOOTLOADER_SZ, + omap_sram_size - SRAM_BOOTLOADER_SZ, + &omap_fb_sram_start, &omap_fb_sram_size); + if (omap_fb_sram_size) + omap_sram_size -= sram_start + omap_sram_size - + omap_fb_sram_start; omap_sram_ceil = omap_sram_base + omap_sram_size; } diff --git a/drivers/video/omap/Kconfig b/drivers/video/omap/Kconfig index 3bf5df736ba..6411c95095c 100644 --- a/drivers/video/omap/Kconfig +++ b/drivers/video/omap/Kconfig @@ -4,13 +4,6 @@ config FB_OMAP help Frame buffer driver for OMAP based boards. -config FB_OMAP_LCDC_INTERNAL - bool "Internal LCD controller support" - depends on FB_OMAP - help - Say Y here, if you want to have support for the internal OMAP - LCD controller. If unsure, say Y. - config FB_OMAP_LCDC_EXTERNAL bool "External LCD controller support" depends on FB_OMAP @@ -18,6 +11,13 @@ config FB_OMAP_LCDC_EXTERNAL Say Y here, if you want to have support for boards with an external LCD controller connected to the SoSSI/RFBI interface. +config FB_OMAP_LCDC_HWA742 + bool "Epson HWA742 LCD controller support" + depends on FB_OMAP && FB_OMAP_LCDC_EXTERNAL + help + Say Y here if you want to have support for the external + Epson HWA742 LCD controller. + config FB_OMAP_MANUAL_UPDATE bool "Default to manual update mode" depends on FB_OMAP && FB_OMAP_LCDC_EXTERNAL @@ -27,6 +27,21 @@ config FB_OMAP_MANUAL_UPDATE the frame buffer content and thus a reload of the image data to the external frame buffer is required. If unsure, say N. +config FB_OMAP_LCD_LPH8923 + bool "Philips LPH8923 LCD support" + depends on FB_OMAP + help + Say Y here if you want to have support for the Philips + LPH8923 LCD. + +config FB_OMAP_BOOTLOADER_INIT + bool "Check bootloader initializaion" + depends on FB_OMAP + help + Say Y here if you want to enable checking if the bootloader has + already initialized the display controller. In this case the + driver will skip the initialization. + config FB_OMAP_DMA_TUNE bool "Set DMA SDRAM access priority high" depends on FB_OMAP && ARCH_OMAP1 @@ -37,3 +52,4 @@ config FB_OMAP_DMA_TUNE answer yes. Answer no if you have a dedicated video memory, or don't use any of the accelerated features. + diff --git a/drivers/video/omap/Makefile b/drivers/video/omap/Makefile index 3b6350cd062..725ea29c671 100644 --- a/drivers/video/omap/Makefile +++ b/drivers/video/omap/Makefile @@ -6,13 +6,14 @@ obj-$(CONFIG_FB_OMAP) += omapfb.o objs-yy := omapfb_main.o +objs-y$(CONFIG_ARCH_OMAP1) += lcdc.o objs-y$(CONFIG_ARCH_OMAP2) += dispc.o -objs-$(CONFIG_ARCH_OMAP1)$(CONFIG_FB_OMAP_LCDC_INTERNAL) += lcdc.o - objs-$(CONFIG_ARCH_OMAP1)$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += sossi.o objs-$(CONFIG_ARCH_OMAP2)$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += rfbi.o +objs-y$(CONFIG_FB_OMAP_LCDC_HWA742) += hwa742.o + objs-y$(CONFIG_MACH_OMAP_H4) += lcd_h4.o objs-y$(CONFIG_MACH_OMAP_H3) += lcd_h3.o objs-y$(CONFIG_MACH_OMAP_H2) += lcd_h2.o @@ -23,5 +24,7 @@ objs-y$(CONFIG_MACH_OMAP_OSK) += lcd_osk.o objs-y$(CONFIG_MACH_OMAP_PERSEUS2) += lcd_p2.o objs-y$(CONFIG_MACH_OMAP_APOLLON) += lcd_apollon.o +objs-y$(CONFIG_FB_OMAP_LCD_LPH8923) += lcd_lph8923.o + omapfb-objs := $(objs-yy) diff --git a/drivers/video/omap/debug.h b/drivers/video/omap/debug.h index 835f28b6418..a280412aeb9 100644 --- a/drivers/video/omap/debug.h +++ b/drivers/video/omap/debug.h @@ -24,8 +24,6 @@ #ifndef __OMAPFB_DEBUG_H #define __OMAPFB_DEBUG_H -#include - #ifdef OMAPFB_DBG #define DBGPRINT(level, fmt, ...) if (level <= OMAPFB_DBG) do { \ @@ -35,37 +33,11 @@ #define DBGENTER(level) DBGPRINT(level, "Enter\n") #define DBGLEAVE(level) DBGPRINT(level, "Leave\n") -static inline void dump_dma_regs(int lch) -{ -#ifdef CONFIG_ARCH_OMAP1 -#define _R(x) __REG16(OMAP_DMA_##x(lch)) - - DBGPRINT(4, "\nCSDP :%#06x CCR :%#06x CSSA_U :%#06x " - "\nCDSA_L:%#06x CDSA_U :%#06x CEN :%#06x " - "\nCFN :%#06x CSFI :%#06x CSEI :%#06x " - "\nCSAC :%#06x CICR :%#06x CSR :%04x " - "\nCSSA_L:%#06x CDAC :%#06x CDEI :%#06x " - "\nCDFI :%#06x COLOR_L :%#06x COLOR_U :%#06x " - "\nCCR2 :%#06x CLNK_CTRL:%#06x LCH_CTRL:%#06x\n", - _R(CSDP), _R(CCR), _R(CSSA_U), - _R(CDSA_L), _R(CDSA_U), _R(CEN), - _R(CFN), _R(CSFI), _R(CSEI), - _R(CSAC), _R(CICR), 0, /* _R(CSR), */ - _R(CSSA_L), _R(CDAC), _R(CDEI), - _R(CDFI), _R(COLOR_L), _R(COLOR_U), - _R(CCR2), _R(CLNK_CTRL), _R(LCH_CTRL)); -#undef _R -#endif -} - -#define DUMP_DMA_REGS(lch) dump_dma_regs(lch) - #else /* OMAPFB_DBG */ #define DBGPRINT(level, format, ...) #define DBGENTER(level) #define DBGLEAVE(level) -#define DUMP_DMA_REGS(lch) #endif /* OMAPFB_DBG */ diff --git a/drivers/video/omap/dispc.c b/drivers/video/omap/dispc.c index 3ccbcd00fec..3185282887f 100644 --- a/drivers/video/omap/dispc.c +++ b/drivers/video/omap/dispc.c @@ -23,15 +23,18 @@ #include #include +#include #include #include +#include #include +#include #include "dispc.h" -/* #define OMAPFB_DBG 2 */ +/* #define OMAPFB_DBG 1 */ #include "debug.h" @@ -127,6 +130,8 @@ DISPC_IRQ_VID2_FIFO_UNDERFLOW | \ DISPC_IRQ_SYNC_LOST) +#define RFBI_CONTROL 0x48050040 + #define MAX_PALETTE_SIZE (256 * 16) #define pr_err(fmt, args...) printk(KERN_ERR MODULE_NAME ": " fmt, ## args) @@ -138,11 +143,25 @@ static struct { u32 base; - void *vram_virt; - dma_addr_t vram_phys; - int vram_size; + + dma_addr_t fb_sram_paddr; + u32 fb_sram_size; + int fb_sram_lines; + + dma_addr_t fb_sdram_paddr; + void *fb_sdram_vaddr; + u32 fb_sdram_size; + int fb_sdram_lines; + + dma_addr_t palette_paddr; + void *palette_vaddr; + + void *fb_kern_vaddr; + + int multiplane_head; int ext_mode; + int fbmem_allocated; unsigned long enabled_irqs; void (*irq_callback)(void *); @@ -152,8 +171,6 @@ static struct { struct clk *dss_ick, *dss1_fck; struct clk *dss_54m_fck; - int active_plane_mask; - enum omapfb_update_mode update_mode; struct omapfb_device *fbdev; } dispc; @@ -182,6 +199,11 @@ static void enable_rfbi_mode(int enable) l |= 1 << 15; l |= enable ? 0 : (1 << 16); dispc_write_reg(DISPC_CONTROL, l); + + /* Set bypass mode in RFBI module */ + l = __raw_readl(io_p2v(RFBI_CONTROL)); + l |= enable ? 0 : (1 << 1); + __raw_writel(l, io_p2v(RFBI_CONTROL)); } static void set_lcd_data_lines(int data_lines) @@ -267,8 +289,8 @@ void omap_dispc_enable_digit_out(int enable) } EXPORT_SYMBOL(omap_dispc_enable_digit_out); -static int omap_dispc_setup_plane(int plane, int channel_out, - unsigned long offset, int screen_width, +static inline int _setup_plane(int plane, int channel_out, + u32 paddr, int screen_width, int pos_x, int pos_y, int width, int height, int color_mode) { @@ -291,7 +313,10 @@ static int omap_dispc_setup_plane(int plane, int channel_out, int bpp; u32 l; - DBGENTER(1); + DBGPRINT(2, "plane %d channel %d paddr %u scr_width %d pos_x %d pos_y %d " + "width %d height %d color_mode %d\n", + plane, channel_out, paddr, screen_width, pos_x, pos_y, + width, height, color_mode); switch (plane) { case OMAPFB_PLANE_GFX: @@ -352,10 +377,7 @@ static int omap_dispc_setup_plane(int plane, int channel_out, dispc_write_reg(at_reg[plane], l); - - dispc_write_reg(ba_reg[plane], - dispc.vram_phys + PAGE_ALIGN(MAX_PALETTE_SIZE) + offset); - + dispc_write_reg(ba_reg[plane], paddr); MOD_REG_FLD(ps_reg[plane], FLD_MASK(16, 11) | FLD_MASK(0, 11), (pos_y << 16) | pos_x); @@ -364,6 +386,52 @@ static int omap_dispc_setup_plane(int plane, int channel_out, dispc_write_reg(ri_reg[plane], (screen_width - width) * bpp / 8 + 1); + return height * screen_width * bpp / 8; +} + +static int omap_dispc_setup_plane(int plane, int channel_out, + unsigned long offset, int screen_width, + int pos_x, int pos_y, int width, int height, + int color_mode) +{ + int yspan; + u32 paddr; + int mp_head = -1; + int r; + + if (offset > dispc.fb_sram_size + dispc.fb_sdram_size) + return -EINVAL; + + if (offset < dispc.fb_sram_size) { + paddr = dispc.fb_sram_paddr + offset; + yspan = min_t(int, height, dispc.fb_sram_lines); + if (yspan) { + if ((r = _setup_plane(plane, channel_out, paddr, + screen_width, pos_x, pos_y, + width, height, color_mode)) < 0) + return r; + offset += r; + height -= yspan; + pos_y += yspan; + mp_head = plane; + if (++plane > 2) + plane = OMAPFB_PLANE_GFX; + } + } + if (height) { + offset -= dispc.fb_sram_size; + paddr = dispc.fb_sdram_paddr + offset; + yspan = min_t(int, height, dispc.fb_sdram_lines); + if (yspan) { + if ((r = _setup_plane(plane, channel_out, paddr, + screen_width, pos_x, pos_y, + width, height, color_mode)) < 0) + return r; + if (mp_head != -1) + dispc.multiplane_head = mp_head; + } + } + return 0; } @@ -372,11 +440,16 @@ static int omap_dispc_enable_plane(int plane, int enable) const u32 at_reg[] = { DISPC_GFX_ATTRIBUTES, DISPC_VID1_BASE + DISPC_VID_ATTRIBUTES, DISPC_VID2_BASE + DISPC_VID_ATTRIBUTES }; - DBGENTER(1); + DBGENTER(2); if ((unsigned int)plane > 2) return -EINVAL; MOD_REG_FLD(at_reg[plane], 1, enable ? 1 : 0); + if (plane == dispc.multiplane_head) { + if (++plane > 2) + plane = OMAPFB_PLANE_GFX; + MOD_REG_FLD(at_reg[plane], 1, enable ? 1 : 0); + } return 0; } @@ -435,6 +508,7 @@ static int omap_dispc_set_update_mode(enum omapfb_update_mode mode) if (mode != dispc.update_mode) { switch (mode) { case OMAPFB_AUTO_UPDATE: + case OMAPFB_MANUAL_UPDATE: omap_dispc_enable_lcd_out(1); dispc.update_mode = mode; break; @@ -470,7 +544,7 @@ static void calc_ck_div(int is_tft, int pck, int *lck_div, int *pck_div) pck = max(1, pck); fck = clk_get_rate(dispc.dss1_fck); lck = fck; - *pck_div = lck / pck; + *pck_div = (lck + pck - 1) / pck; if (is_tft) *pck_div = max(2, *pck_div); else @@ -489,6 +563,14 @@ static void calc_ck_div(int is_tft, int pck, int *lck_div, int *pck_div) } } +static void set_lcd_tft_mode(int enable) +{ + u32 mask; + + mask = 1 << 3; + MOD_REG_FLD(DISPC_CONTROL, mask, enable ? mask : 0); +} + static void set_lcd_timings(void) { u32 l; @@ -499,10 +581,6 @@ static void set_lcd_timings(void) DBGENTER(1); - /* TFT dither, TFT/STN */ - l = (1 << 7) | (1 << 3); - MOD_REG_FLD(DISPC_CONTROL, l, is_tft ? l : 0); - l = dispc_read_reg(DISPC_TIMING_H); l &= ~(FLD_MASK(0, 6) | FLD_MASK(8, 8) | FLD_MASK(20, 8)); l |= ( max(1, (min(64, panel->hsw))) - 1 ) << 0; @@ -588,7 +666,7 @@ static irqreturn_t omap_dispc_irq_handler(int irq, void *dev, struct pt_regs *re if (stat & DISPC_IRQ_MASK_ERROR) { if (jabber++ < 5) { - pr_err("irq error status %04x\n", stat); + pr_err("irq error status %04x\n", stat & 0x7fff); } else { pr_err("disable irq\n"); dispc_write_reg(DISPC_IRQENABLE, 0); @@ -684,8 +762,191 @@ static void omap_dispc_resume(void) DBGLEAVE(1); } -/* Called when used in bypass mode */ -static int alloc_vram(int req_size) + +static int omap_dispc_update_window(struct omapfb_update_window *win, + void (*complete_callback)(void *arg), + void *complete_callback_data) +{ + return dispc.update_mode == OMAPFB_UPDATE_DISABLED ? -ENODEV : 0; +} + +/* Greatest common divisor */ +static int calc_gcd(int a, int b) +{ + int tmp; + + if (a < b) { + tmp = a; + a = b; + b = tmp; + } + for (;;) { + tmp = a % b; + if (tmp != 0) { + a = b; + b = tmp; + } else + break; + } + + return b; +} + +/* Least common multiple */ +static int calc_lcm(int a, int b) +{ + return a * b / calc_gcd(a, b); +} + +static void omap_dispc_get_vram_layout(unsigned long *size, void **virt, + dma_addr_t *phys) +{ + *size = dispc.fb_sram_size + dispc.fb_sdram_size; + *virt = dispc.fb_kern_vaddr; + /* Physical vram might not be contiguous. No one except own mmap + * should use this addr. + */ + *phys = 0; +} + +static int omap_dispc_mmap_user(struct vm_area_struct *vma) +{ + unsigned long len; + unsigned long offset; + unsigned long vaddr; + int r; + + DBGPRINT(1, "vm_pgoff 0x%08lx vm_start 0x%08lx vm_end 0x%08lx\n", + vma->vm_pgoff, vma->vm_start, vma->vm_end); + + if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) + return -EINVAL; + offset = vma->vm_pgoff << PAGE_SHIFT; + + if (offset >= dispc.fb_sram_size + dispc.fb_sdram_size) + return -EINVAL; + + len = vma->vm_end - vma->vm_start; + if (offset + len > dispc.fb_sram_size + dispc.fb_sdram_size) + return -EINVAL; + + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + vma->vm_flags |= VM_PFNMAP; + + vaddr = vma->vm_start; + + if (dispc.fb_sram_size) { + if (offset < dispc.fb_sram_size) { + int chunk = min_t(int, dispc.fb_sram_size - offset, len); + DBGPRINT(1, "map sram va %08lx pa %08lx size %d\n", + vaddr, dispc.fb_sram_paddr + offset, chunk); + r = io_remap_pfn_range(vma, vaddr, + (dispc.fb_sram_paddr + + offset) >> PAGE_SHIFT, chunk, + vma->vm_page_prot); + if (r == -EINVAL) + return r; + if (r < 0) + return -EAGAIN; + + vaddr += chunk; + len -= chunk; + offset = 0; + } else + offset -= dispc.fb_sram_size; + } + + if (len) { + DBGPRINT(1, "map sdram va %08lx pa %08lx size %ld\n", + vaddr, dispc.fb_sdram_paddr + offset, len); + BUG_ON(offset > dispc.fb_sdram_size || + offset + len > dispc.fb_sdram_size || + vma->vm_end - vaddr != len); + r = io_remap_pfn_range(vma, vaddr, (dispc.fb_sdram_paddr + + offset) >> PAGE_SHIFT, len, + vma->vm_page_prot); + /* no teardown of the previous mapping here. + * do_mmap_pgoff will take core of that. */ + if (r == -EINVAL) + return r; + if (r < 0) + return -EAGAIN; + } + + return 0; +} + +static int mmap_kern(void) +{ + struct vm_struct *kvma; + struct vm_area_struct vma; + pgprot_t pgprot; + unsigned long vaddr; + + DBGENTER(1); + + kvma = get_vm_area(dispc.fb_sram_size + dispc.fb_sdram_size, VM_IOREMAP); + if (kvma == NULL) { + pr_err("can't get kernel vm area\n"); + return -ENOMEM; + } + vma.vm_mm = &init_mm; + + dispc.fb_kern_vaddr = kvma->addr; + vaddr = (unsigned long)kvma->addr; + + pgprot = pgprot_writecombine(pgprot_kernel); + if (dispc.fb_sram_size) { + vma.vm_start = vaddr; + vma.vm_end = vaddr + dispc.fb_sram_size; + if (io_remap_pfn_range(&vma, vaddr, + dispc.fb_sram_paddr >> PAGE_SHIFT, + dispc.fb_sram_size, pgprot) < 0) { + pr_err("kernel mmap for FBMEM failed\n"); + return -EAGAIN; + } + vaddr += dispc.fb_sram_size; + } + if (dispc.fb_sdram_size) { + vma.vm_start = vaddr; + vma.vm_end = vaddr + dispc.fb_sdram_size; + if (io_remap_pfn_range(&vma, vaddr, + dispc.fb_sdram_paddr >> PAGE_SHIFT, + dispc.fb_sdram_size, pgprot) < 0) { + pr_err("kernel mmap for FBMEM failed\n"); + return -EAGAIN; + } + } + + DBGLEAVE(1); + + return 0; +} + +static void unmap_kern(void) +{ + vunmap(dispc.fb_kern_vaddr); +} + +static int alloc_palette_ram(void) +{ + dispc.palette_vaddr = dma_alloc_writecombine(dispc.fbdev->dev, + MAX_PALETTE_SIZE, &dispc.palette_paddr, GFP_KERNEL); + if (dispc.palette_vaddr == NULL) { + pr_err("failed to alloc palette memory\n"); + return -ENOMEM; + } + + return 0; +} + +static void free_palette_ram(void) +{ + dma_free_writecombine(dispc.fbdev->dev, MAX_PALETTE_SIZE, + dispc.palette_vaddr, dispc.palette_paddr); +} + +static int alloc_fbmem(int req_size) { int frame_size; struct lcd_panel *panel = dispc.fbdev->panel; @@ -693,11 +954,11 @@ static int alloc_vram(int req_size) frame_size = PAGE_ALIGN(panel->x_res * panel->bpp / 8 * panel->y_res); if (req_size > frame_size) frame_size = req_size; - dispc.vram_size = PAGE_ALIGN(MAX_PALETTE_SIZE) + frame_size; - dispc.vram_virt = dma_alloc_writecombine(dispc.fbdev->dev, - dispc.vram_size, &dispc.vram_phys, GFP_KERNEL); + dispc.fb_sdram_size = PAGE_ALIGN(MAX_PALETTE_SIZE) + frame_size; + dispc.fb_kern_vaddr = dma_alloc_writecombine(dispc.fbdev->dev, + dispc.fb_sdram_size, &dispc.fb_sdram_paddr, GFP_KERNEL); - if (dispc.vram_virt == 0) { + if (dispc.fb_kern_vaddr == 0) { pr_err("unable to allocate fb DMA memory\n"); return -ENOMEM; } @@ -705,18 +966,75 @@ static int alloc_vram(int req_size) return 0; } -static void free_vram(void) +static void free_fbmem(void) { - dma_free_writecombine(dispc.fbdev->dev, dispc.vram_size, - dispc.vram_virt, dispc.vram_phys); + dma_free_writecombine(dispc.fbdev->dev, dispc.fb_sdram_size, + dispc.fb_kern_vaddr, dispc.fb_sdram_paddr); } -static void omap_dispc_get_vram_layout(unsigned long *size, void **virt, - dma_addr_t *phys) +static int setup_fbmem(int req_size) { - *size = dispc.vram_size - PAGE_ALIGN(MAX_PALETTE_SIZE); - *virt = (u8 *)dispc.vram_virt + PAGE_ALIGN(MAX_PALETTE_SIZE); - *phys = dispc.vram_phys + PAGE_ALIGN(MAX_PALETTE_SIZE); + struct lcd_panel *panel = dispc.fbdev->panel; + struct omapfb_platform_data *conf; + unsigned long size_align; + int line_size; + int frame_size; + int lines; + int r; + + conf = dispc.fbdev->dev->platform_data; + + if (conf->fbmem.fb_sram_size + conf->fbmem.fb_sdram_size == 0) { + if ((r = alloc_fbmem(req_size)) < 0) + return r; + dispc.fbmem_allocated = 1; + dispc.fb_sdram_lines = panel->y_res; + return 0; + } + + dispc.fb_sram_paddr = conf->fbmem.fb_sram_start; + dispc.fb_sram_size = conf->fbmem.fb_sram_size; + dispc.fb_sdram_paddr = conf->fbmem.fb_sdram_start; + dispc.fb_sdram_size = conf->fbmem.fb_sdram_size; + + line_size = panel->x_res * panel->bpp / 8; + frame_size = PAGE_ALIGN(line_size * panel->y_res); + + size_align = calc_lcm(line_size, PAGE_SIZE); + + if (dispc.fb_sram_size + dispc.fb_sdram_size < frame_size || + (dispc.fb_sdram_size && (dispc.fb_sram_size % size_align))) { + pr_err("Invalid FB memory configuration\n"); + return -EINVAL; + } + + if (dispc.fb_sram_size + dispc.fb_sdram_size < req_size) { + pr_err("%d vram was requested, but only %u is available\n", + req_size, dispc.fb_sram_size + dispc.fb_sdram_size); + } + + lines = dispc.fb_sram_size / line_size; + lines = min_t(int, panel->y_res, lines); + dispc.fb_sram_lines = lines; + lines = panel->y_res - lines; + dispc.fb_sdram_lines = lines; + + if ((r = mmap_kern()) < 0) + return r; + + DBGPRINT(1, "fb_sram %08x size %08x fb_sdram %08x size %08x\n", + dispc.fb_sram_paddr, dispc.fb_sram_size, + dispc.fb_sdram_paddr, dispc.fb_sdram_size); + + return 0; +} + +static void cleanup_fbmem(void) +{ + if (dispc.fbmem_allocated) + free_fbmem(); + else + unmap_kern(); } static int omap_dispc_init(struct omapfb_device *fbdev, int ext_mode, @@ -726,6 +1044,7 @@ static int omap_dispc_init(struct omapfb_device *fbdev, int ext_mode, u32 l; struct lcd_panel *panel = fbdev->panel; int tmo = 10000; + int skip_init = 0; DBGENTER(1); @@ -735,39 +1054,50 @@ static int omap_dispc_init(struct omapfb_device *fbdev, int ext_mode, dispc.fbdev = fbdev; dispc.ext_mode = ext_mode; + dispc.multiplane_head = -1; + + if (fbdev->dev->platform_data == NULL) { + pr_err("missing FB configuration\n"); + return -ENOENT; + } + + init_completion(&dispc.frame_done); + if ((r = get_dss_clocks()) < 0) - goto fail0; + return r; enable_lcd_clocks(1); - /* Reset monitoring works only w/ the 54M clk */ - enable_digit_clocks(1); l = dispc_read_reg(DISPC_REVISION); pr_info(MODULE_NAME ": version %d.%d\n", l >> 4 & 0x0f, l & 0x0f); - /* Soft reset */ - MOD_REG_FLD(DISPC_SYSCONFIG, 1 << 1, 1 << 1); - - while (!(dispc_read_reg(DISPC_SYSSTATUS) & 1)) { - if (!--tmo) { - pr_err("soft reset failed\n"); - r = -ENODEV; - enable_digit_clocks(0); - goto fail1; - } +#ifdef CONFIG_FB_OMAP_BOOTLOADER_INIT + l = dispc_read_reg(DISPC_CONTROL); + /* LCD enabled ? */ + if (l & 1) { + pr_info(MODULE_NAME ": skipping hardware initialization\n"); + skip_init = 1; } +#endif - enable_digit_clocks(0); + if (!skip_init) { + /* Reset monitoring works only w/ the 54M clk */ + enable_digit_clocks(1); - if (!ext_mode && (r = alloc_vram(req_vram_size)) < 0) - goto fail1; + /* Soft reset */ + MOD_REG_FLD(DISPC_SYSCONFIG, 1 << 1, 1 << 1); - /* Set logic clock to the fck for now */ - MOD_REG_FLD(DISPC_DIVISOR, FLD_MASK(16, 8), 1); + while (!(dispc_read_reg(DISPC_SYSSTATUS) & 1)) { + if (!--tmo) { + pr_err("soft reset failed\n"); + r = -ENODEV; + enable_digit_clocks(0); + goto fail1; + } + } - setup_plane_fifo(0); - setup_plane_fifo(1); - setup_plane_fifo(2); + enable_digit_clocks(0); + } l = dispc_read_reg(DISPC_IRQSTATUS); dispc_write_reg(l, DISPC_IRQSTATUS); @@ -778,38 +1108,60 @@ static int omap_dispc_init(struct omapfb_device *fbdev, int ext_mode, if ((r = request_irq(INT_24XX_DSS_IRQ, omap_dispc_irq_handler, 0, MODULE_NAME, NULL)) < 0) { pr_err("can't get DSS IRQ\n"); - goto fail2; + goto fail1; } - set_lcd_data_lines(panel->data_lines); - set_load_mode(DISPC_LOAD_FRAME_ONLY); + /* L3 firewall setting: enable access to OCM RAM */ + __raw_writel(0x402000b0, io_p2v(0x680050a0)); - if (!ext_mode) { - omap_dispc_set_lcd_size(panel->x_res, panel->y_res); - set_lcd_timings(); + if ((r = alloc_palette_ram()) < 0) + goto fail2; + + if ((r = setup_fbmem(req_vram_size)) < 0) + goto fail3; + + if (!skip_init) { + memset(dispc.fb_kern_vaddr, 0, + dispc.fb_sram_size + dispc.fb_sdram_size); + + /* Set logic clock to fck, pixel clock to fck/2 for now */ + MOD_REG_FLD(DISPC_DIVISOR, FLD_MASK(16, 8), 1 << 16); + MOD_REG_FLD(DISPC_DIVISOR, FLD_MASK(0, 8), 2 << 0); + + setup_plane_fifo(0); + setup_plane_fifo(1); + setup_plane_fifo(2); + + set_lcd_tft_mode(panel->config & OMAP_LCDC_PANEL_TFT); + set_lcd_data_lines(panel->data_lines); + set_load_mode(DISPC_LOAD_FRAME_ONLY); + + if (!ext_mode) { + omap_dispc_set_lcd_size(panel->x_res, panel->y_res); + set_lcd_timings(); + } + enable_rfbi_mode(ext_mode); } - enable_rfbi_mode(ext_mode); - DBGLEAVE(1); return 0; +fail3: + free_palette_ram(); fail2: - if (ext_mode) - free_vram(); + free_irq(INT_24XX_DSS_IRQ, NULL); fail1: enable_lcd_clocks(0); put_dss_clocks(); -fail0: - DBGLEAVE(1); - return r; + + return r; } static void omap_dispc_cleanup(void) { + cleanup_fbmem(); + free_palette_ram(); free_irq(INT_24XX_DSS_IRQ, NULL); enable_lcd_clocks(0); put_dss_clocks(); - if (dispc.ext_mode) - free_vram(); } static unsigned long omap_dispc_get_caps(void) @@ -822,10 +1174,11 @@ struct lcd_ctrl omap2_int_ctrl = { .init = omap_dispc_init, .cleanup = omap_dispc_cleanup, .get_vram_layout = omap_dispc_get_vram_layout, + .mmap = omap_dispc_mmap_user, .get_caps = omap_dispc_get_caps, .set_update_mode = omap_dispc_set_update_mode, .get_update_mode = omap_dispc_get_update_mode, - .update_window = NULL, + .update_window = omap_dispc_update_window, .suspend = omap_dispc_suspend, .resume = omap_dispc_resume, .setup_plane = omap_dispc_setup_plane, diff --git a/drivers/video/omap/hwa742.c b/drivers/video/omap/hwa742.c new file mode 100644 index 00000000000..899573fe85f --- /dev/null +++ b/drivers/video/omap/hwa742.c @@ -0,0 +1,928 @@ +/* + * File: drivers/video/omap/hwa742.c + * + * Epson HWA742 LCD controller driver + * + * Copyright (C) 2004-2005 Nokia Corporation + * Authors: Juha Yrjölä + * Imre Deak + * YUV support: Jussi Laako + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include +#include +#include +#include +#include +#include + +#include +#include + +/* #define OMAPFB_DBG 1 */ + +#include "hwa742.h" +#include "debug.h" + +#define MODULE_NAME "omapfb-hwa742" + +#define HWA742_REV_CODE_REG 0x0 +#define HWA742_CONFIG_REG 0x2 +#define HWA742_PLL_DIV_REG 0x4 +#define HWA742_PLL_0_REG 0x6 +#define HWA742_PLL_1_REG 0x8 +#define HWA742_PLL_2_REG 0xa +#define HWA742_PLL_3_REG 0xc +#define HWA742_PLL_4_REG 0xe +#define HWA742_CLK_SRC_REG 0x12 +#define HWA742_PANEL_TYPE_REG 0x14 +#define HWA742_H_DISP_REG 0x16 +#define HWA742_H_NDP_REG 0x18 +#define HWA742_V_DISP_1_REG 0x1a +#define HWA742_V_DISP_2_REG 0x1c +#define HWA742_V_NDP_REG 0x1e +#define HWA742_HS_W_REG 0x20 +#define HWA742_HP_S_REG 0x22 +#define HWA742_VS_W_REG 0x24 +#define HWA742_VP_S_REG 0x26 +#define HWA742_PCLK_POL_REG 0x28 +#define HWA742_INPUT_MODE_REG 0x2a +#define HWA742_TRANSL_MODE_REG1 0x2e +#define HWA742_DISP_MODE_REG 0x34 +#define HWA742_WINDOW_TYPE 0x36 +#define HWA742_WINDOW_X_START_0 0x38 +#define HWA742_WINDOW_X_START_1 0x3a +#define HWA742_WINDOW_Y_START_0 0x3c +#define HWA742_WINDOW_Y_START_1 0x3e +#define HWA742_WINDOW_X_END_0 0x40 +#define HWA742_WINDOW_X_END_1 0x42 +#define HWA742_WINDOW_Y_END_0 0x44 +#define HWA742_WINDOW_Y_END_1 0x46 +#define HWA742_MEMORY_WRITE_LSB 0x48 +#define HWA742_MEMORY_WRITE_MSB 0x49 +#define HWA742_MEMORY_READ_0 0x4a +#define HWA742_MEMORY_READ_1 0x4c +#define HWA742_MEMORY_READ_2 0x4e +#define HWA742_POWER_SAVE 0x56 +#define HWA742_NDP_CTRL 0x58 + +#define HWA742_AUTO_UPDATE_TIME (HZ / 20) + +#define pr_err(fmt, args...) printk(KERN_ERR MODULE_NAME ": " fmt, ## args) + +/* Reserve 4 request slots for requests in irq context */ +#define REQ_POOL_SIZE 24 +#define IRQ_REQ_POOL_SIZE 4 + +struct update_param { + int x, y, width, height; + int color_mode; + int flags; +}; + +#define REQ_FROM_IRQ_POOL 0x01 + +#define REQ_COMPLETE 0 +#define REQ_PENDING 1 + +struct hwa742_request { + struct list_head entry; + unsigned int flags; + + int (*handler)(struct hwa742_request *req); + void (*complete)(void *data); + void *complete_data; + + union { + struct update_param update; + struct completion *sync; + } par; +}; + +struct hwa742_struct { + enum omapfb_update_mode update_mode; + enum omapfb_update_mode update_mode_before_suspend; + + struct timer_list auto_update_timer; + int stop_auto_update; + struct omapfb_update_window auto_update_window; + + struct hwa742_request req_pool[REQ_POOL_SIZE]; + struct list_head pending_req_list; + struct list_head free_req_list; + struct semaphore req_sema; + spinlock_t req_lock; + + struct clk *sys_ck; + struct extif_timings reg_timings, lut_timings; + + int prev_color_mode; + int prev_flags; + int window_type; + + u32 max_transmit_size; + u32 extif_clk_period; + + struct omapfb_device *fbdev; + struct lcd_ctrl_extif *extif; + struct lcd_ctrl *int_ctrl; +} hwa742; + +static u8 hwa742_read_reg(u8 reg) +{ + u8 data; + + hwa742.extif->set_bits_per_cycle(8); + hwa742.extif->write_command(®, 1); + hwa742.extif->read_data(&data, 1); + + return data; +} + +static void hwa742_write_reg(u8 reg, u8 data) +{ + hwa742.extif->set_bits_per_cycle(8); + hwa742.extif->write_command(®, 1); + hwa742.extif->write_data(&data, 1); +} + +void hwa742_read_id(int *rev_code, int *config) +{ + *rev_code = hwa742_read_reg(HWA742_REV_CODE_REG); + *config = hwa742_read_reg(HWA742_CONFIG_REG); +} +EXPORT_SYMBOL(hwa742_read_id); + +static void set_window_regs(int x_start, int y_start, int x_end, int y_end) +{ + u8 tmp[8]; + u8 cmd; + + x_end--; + y_end--; + tmp[0] = x_start; + tmp[1] = x_start >> 8; + tmp[2] = y_start; + tmp[3] = y_start >> 8; + tmp[4] = x_end; + tmp[5] = x_end >> 8; + tmp[6] = y_end; + tmp[7] = y_end >> 8; + + hwa742.extif->set_bits_per_cycle(8); + cmd = HWA742_WINDOW_X_START_0; + + hwa742.extif->write_command(&cmd, 1); + + hwa742.extif->write_data(tmp, 8); +} + +static void set_format_regs(int conv, int transl, int flags) +{ + if (flags & OMAPFB_FORMAT_FLAG_DOUBLE) { + hwa742.window_type = ((hwa742.window_type & 0xfc) | 0x01); + DBGPRINT(2, "hwa742: enabled pixel doubling\n"); + } else { + hwa742.window_type = (hwa742.window_type & 0xfc); + DBGPRINT(2, "hwa742: disabled pixel doubling\n"); + } + + hwa742_write_reg(HWA742_INPUT_MODE_REG, conv); + hwa742_write_reg(HWA742_TRANSL_MODE_REG1, transl); + hwa742_write_reg(HWA742_WINDOW_TYPE, hwa742.window_type); +} + +static inline struct hwa742_request *alloc_req(void) +{ + unsigned long flags; + struct hwa742_request *req; + int req_flags = 0; + + if (!in_interrupt()) + down(&hwa742.req_sema); + else + req_flags = REQ_FROM_IRQ_POOL; + + spin_lock_irqsave(&hwa742.req_lock, flags); + BUG_ON(list_empty(&hwa742.free_req_list)); + req = list_entry(hwa742.free_req_list.next, + struct hwa742_request, entry); + list_del(&req->entry); + spin_unlock_irqrestore(&hwa742.req_lock, flags); + + INIT_LIST_HEAD(&req->entry); + req->flags = req_flags; + + return req; +} + +static inline void free_req(struct hwa742_request *req) +{ + unsigned long flags; + + spin_lock_irqsave(&hwa742.req_lock, flags); + + list_del(&req->entry); + list_add(&req->entry, &hwa742.free_req_list); + if (!(req->flags & REQ_FROM_IRQ_POOL)) + up(&hwa742.req_sema); + + spin_unlock_irqrestore(&hwa742.req_lock, flags); +} + +static void process_pending_requests(void) +{ + unsigned long flags; + + DBGENTER(2); + + spin_lock_irqsave(&hwa742.req_lock, flags); + + while (!list_empty(&hwa742.pending_req_list)) { + struct hwa742_request *req; + void (*complete)(void *); + void *complete_data; + + req = list_entry(hwa742.pending_req_list.next, + struct hwa742_request, entry); + spin_unlock_irqrestore(&hwa742.req_lock, flags); + + if (req->handler(req) == REQ_PENDING) + return; + + complete = req->complete; + complete_data = req->complete_data; + free_req(req); + + if (complete) + complete(complete_data); + + spin_lock_irqsave(&hwa742.req_lock, flags); + } + + spin_unlock_irqrestore(&hwa742.req_lock, flags); + + DBGLEAVE(2); +} + +static void submit_req_list(struct list_head *head) +{ + unsigned long flags; + int process = 1; + + DBGENTER(2); + + spin_lock_irqsave(&hwa742.req_lock, flags); + if (likely(!list_empty(&hwa742.pending_req_list))) + process = 0; + list_splice_init(head, hwa742.pending_req_list.prev); + spin_unlock_irqrestore(&hwa742.req_lock, flags); + + if (process) + process_pending_requests(); + + DBGLEAVE(2); +} + +static void request_complete(void *data) +{ + struct hwa742_request *req = (struct hwa742_request *)data; + void (*complete)(void *); + void *complete_data; + + complete = req->complete; + complete_data = req->complete_data; + + free_req(req); + + if (complete) + complete(complete_data); + + process_pending_requests(); +} + +static int send_frame_handler(struct hwa742_request *req) +{ + struct update_param *par = &req->par.update; + int x = par->x; + int y = par->y; + int w = par->width; + int h = par->height; + int bpp; + int conv, transl; + unsigned long offset; + int color_mode = par->color_mode; + int flags = par->flags; + int scr_width = 800; + + DBGPRINT(2, "x %d y %d w %d h %d scr_width %d color_mode %d flags %d\n", + x, y, w, h, scr_width, color_mode, flags); + + switch (color_mode) { + case OMAPFB_COLOR_YUV422: + bpp = 16; + conv = 0x08; + transl = 0x25; + break; + case OMAPFB_COLOR_YUV420: + bpp = 12; + conv = 0x09; + transl = 0x25; + break; + case OMAPFB_COLOR_RGB565: + bpp = 16; + conv = 0x01; + transl = 0x05; + break; + default: + return -EINVAL; + } + + if (hwa742.prev_flags != flags || + hwa742.prev_color_mode != color_mode) { + set_format_regs(conv, transl, flags); + hwa742.prev_color_mode = color_mode; + hwa742.prev_flags = flags; + } + + set_window_regs(x, y, x + w, y + h); + + offset = (scr_width * y + x) * bpp / 8; + + hwa742.int_ctrl->setup_plane(OMAPFB_PLANE_GFX, + OMAPFB_CHANNEL_OUT_LCD, offset, scr_width, 0, 0, w, h, + color_mode); + + hwa742.extif->set_bits_per_cycle(16); + + hwa742.int_ctrl->enable_plane(OMAPFB_PLANE_GFX, 1); + hwa742.extif->transfer_area(w, h, request_complete, req); + + return REQ_PENDING; +} + +static void send_frame_complete(void *data) +{ + hwa742.int_ctrl->enable_plane(OMAPFB_PLANE_GFX, 0); +} + +#define ADD_PREQ(_x, _y, _w, _h) do { \ + req = alloc_req(); \ + req->handler = send_frame_handler; \ + req->complete = send_frame_complete; \ + req->par.update.x = _x; \ + req->par.update.y = _y; \ + req->par.update.width = _w; \ + req->par.update.height = _h; \ + req->par.update.color_mode = color_mode;\ + req->par.update.flags = flags; \ + list_add_tail(&req->entry, req_head); \ +} while(0) + +static void create_req_list(struct omapfb_update_window *win, + struct list_head *req_head) +{ + struct hwa742_request *req; + int x = win->x; + int y = win->y; + int width = win->width; + int height = win->height; + int color_mode; + int flags; + + flags = win->format & OMAPFB_FORMAT_FLAG_DOUBLE; + color_mode = win->format & OMAPFB_FORMAT_MASK; + + if (x & 1) { + ADD_PREQ(x, y, 1, height); + width--; + x++; + } + if (width & ~1) { + unsigned int xspan = width & ~1; + unsigned int ystart = y; + unsigned int yspan = height; + + if (xspan * height * 2 > hwa742.max_transmit_size) { + yspan = hwa742.max_transmit_size / (xspan * 2); + ADD_PREQ(x, ystart, xspan, yspan); + ystart += yspan; + yspan = height - yspan; + } + + ADD_PREQ(x, ystart, xspan, yspan); + x += xspan; + width -= xspan; + } + if (width) + ADD_PREQ(x, y, 1, height); +} + +static void auto_update_complete(void *data) +{ + DBGENTER(2); + + if (!hwa742.stop_auto_update) + mod_timer(&hwa742.auto_update_timer, + jiffies + HWA742_AUTO_UPDATE_TIME); + + DBGLEAVE(2); +} + +static void hwa742_update_window_auto(unsigned long arg) +{ + LIST_HEAD(req_list); + struct hwa742_request *last; + + DBGENTER(2); + + create_req_list(&hwa742.auto_update_window, &req_list); + last = list_entry(req_list.prev, struct hwa742_request, entry); + + last->complete = auto_update_complete; + last->complete_data = NULL; + + submit_req_list(&req_list); + + DBGLEAVE(2); +} + +int hwa742_update_window_async(struct omapfb_update_window *win, + void (*complete_callback)(void *arg), + void *complete_callback_data) +{ + LIST_HEAD(req_list); + struct hwa742_request *last; + int r = 0; + + DBGENTER(2); + + if (hwa742.update_mode != OMAPFB_MANUAL_UPDATE) { + r = -EINVAL; + goto out; + } + if (unlikely(win->format & ~(0x03 | OMAPFB_FORMAT_FLAG_DOUBLE))) { + r = -EINVAL; + goto out; + } + + create_req_list(win, &req_list); + last = list_entry(req_list.prev, struct hwa742_request, entry); + + last->complete = complete_callback; + last->complete_data = (void *)complete_callback_data; + + submit_req_list(&req_list); + +out: + DBGLEAVE(2); + return r; +} +EXPORT_SYMBOL(hwa742_update_window_async); + +static int hwa742_setup_plane(int plane, int channel_out, + unsigned long offset, int screen_width, + int pos_x, int pos_y, int width, int height, + int color_mode) +{ + if (plane != OMAPFB_PLANE_GFX || + channel_out != OMAPFB_CHANNEL_OUT_LCD) + return -EINVAL; + + return 0; +} + +static int hwa742_enable_plane(int plane, int enable) +{ + if (plane != 0) + return -EINVAL; + + hwa742.int_ctrl->enable_plane(plane, enable); + + return 0; +} + +static int sync_handler(struct hwa742_request *req) +{ + complete(req->par.sync); + return REQ_COMPLETE; +} + +static void hwa742_sync(void) +{ + LIST_HEAD(req_list); + struct hwa742_request *req; + struct completion comp; + + DBGENTER(2); + + req = alloc_req(); + + req->handler = sync_handler; + req->complete = NULL; + init_completion(&comp); + req->par.sync = ∁ + + list_add(&req->entry, &req_list); + submit_req_list(&req_list); + + wait_for_completion(&comp); + + DBGLEAVE(2); +} + +static struct notifier_block *hwa742_client_list; + +int hwa742_register_client(struct hwa742_notifier_block *hwa742_nb, + hwa742_notifier_callback_t callback, + void *callback_data) +{ + int r; + + DBGPRINT(1, "update_mode %d\n", hwa742.update_mode); + hwa742_nb->nb.notifier_call = (int (*)(struct notifier_block *, + unsigned long, void *))callback; + hwa742_nb->data = callback_data; + r = notifier_chain_register(&hwa742_client_list, &hwa742_nb->nb); + if (r) + return r; + if (hwa742.update_mode == OMAPFB_MANUAL_UPDATE) { + DBGPRINT(1, "calling client list\n"); + notifier_call_chain(&hwa742_client_list, + HWA742_EVENT_READY, + hwa742.fbdev); + } + return 0; +} +EXPORT_SYMBOL(hwa742_register_client); + +int hwa742_unregister_client(struct hwa742_notifier_block *hwa742_nb) +{ + return notifier_chain_unregister(&hwa742_client_list, + &hwa742_nb->nb); +} +EXPORT_SYMBOL(hwa742_unregister_client); + +static int hwa742_set_update_mode(enum omapfb_update_mode mode) +{ + int r = 0; + + DBGENTER(1); + + if (mode != OMAPFB_MANUAL_UPDATE && mode != OMAPFB_AUTO_UPDATE && + mode != OMAPFB_UPDATE_DISABLED) { + r = -EINVAL; + goto out; + } + + if (mode == hwa742.update_mode) + goto out; + + printk(KERN_INFO "hwa742: setting update mode to %s\n", + mode == OMAPFB_UPDATE_DISABLED ? "disabled" : + (mode == OMAPFB_AUTO_UPDATE ? "auto" : "manual")); + + switch (hwa742.update_mode) { + case OMAPFB_MANUAL_UPDATE: + notifier_call_chain(&hwa742_client_list, + HWA742_EVENT_DISABLED, + hwa742.fbdev); + break; + case OMAPFB_AUTO_UPDATE: + hwa742.stop_auto_update = 1; + del_timer_sync(&hwa742.auto_update_timer); + break; + case OMAPFB_UPDATE_DISABLED: + break; + } + + hwa742.update_mode = mode; + hwa742_sync(); + hwa742.stop_auto_update = 0; + + switch (mode) { + case OMAPFB_MANUAL_UPDATE: + notifier_call_chain(&hwa742_client_list, + HWA742_EVENT_READY, + hwa742.fbdev); + break; + case OMAPFB_AUTO_UPDATE: + hwa742_update_window_auto(0); + break; + case OMAPFB_UPDATE_DISABLED: + break; + } +out: + + DBGLEAVE(1); + return r; +} + +static enum omapfb_update_mode hwa742_get_update_mode(void) +{ + return hwa742.update_mode; +} + +static unsigned long round_to_extif_ticks(unsigned long ps, int div) +{ + int bus_tick = hwa742.extif_clk_period * div; + return (ps + bus_tick - 1) / bus_tick * bus_tick; +} + +static int calc_reg_timing(unsigned long sysclk, int div) +{ + struct extif_timings *t; + unsigned long systim; + + /* CSOnTime 0, WEOnTime 2 ns, REOnTime 2 ns, + * AccessTime 2 ns + 12.2 ns (regs), + * WEOffTime = WEOnTime + 1 ns, + * REOffTime = REOnTime + 16 ns (regs), + * CSOffTime = REOffTime + 1 ns + * ReadCycle = 2ns + 2*SYSCLK (regs), + * WriteCycle = 2*SYSCLK + 2 ns, + * CSPulseWidth = 10 ns */ + systim = 1000000000 / (sysclk / 1000); + DBGPRINT(1, "HWA742 systim %lu ps extif_clk_period %u ps" + "extif_clk_div %d\n", systim, hwa742.extif_clk_period, div); + + t = &hwa742.reg_timings; + memset(t, 0, sizeof(*t)); + t->clk_div = div; + t->cs_on_time = 0; + t->we_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div); + t->re_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div); + t->access_time = round_to_extif_ticks(t->re_on_time + 12200, div); + t->we_off_time = round_to_extif_ticks(t->we_on_time + 1000, div); + t->re_off_time = round_to_extif_ticks(t->re_on_time + 16000, div); + t->cs_off_time = round_to_extif_ticks(t->re_off_time + 1000, div); + t->we_cycle_time = round_to_extif_ticks(2 * systim + 2000, div); + if (t->we_cycle_time < t->we_off_time) + t->we_cycle_time = t->we_off_time; + t->re_cycle_time = round_to_extif_ticks(2 * systim + 2000, div); + if (t->re_cycle_time < t->re_off_time) + t->re_cycle_time = t->re_off_time; + t->cs_pulse_width = 0; + + DBGPRINT(1, "[reg]cson %d csoff %d reon %d reoff %d\n", + t->cs_on_time, t->cs_off_time, t->re_on_time, t->re_off_time); + DBGPRINT(1, "[reg]weon %d weoff %d recyc %d wecyc %d\n", + t->we_on_time, t->we_off_time, t->re_cycle_time, + t->we_cycle_time); + DBGPRINT(1, "[reg]rdaccess %d cspulse %d\n", + t->access_time, t->cs_pulse_width); + + return hwa742.extif->convert_timings(t); +} + +static int calc_lut_timing(unsigned long sysclk, int div) +{ + struct extif_timings *t; + unsigned long systim; + + /* CSOnTime 0, WEOnTime 2 ns, REOnTime 2 ns, + * AccessTime 2 ns + 4 * SYSCLK + 26 (lut), + * WEOffTime = WEOnTime + 1 ns, + * REOffTime = REOnTime + 4*SYSCLK + 26 ns (lut), + * CSOffTime = REOffTime + 1 ns + * ReadCycle = 2ns + 4*SYSCLK + 26 ns (lut), + * WriteCycle = 2*SYSCLK + 2 ns, + * CSPulseWidth = 10 ns + */ + systim = 1000000000 / (sysclk / 1000); + DBGPRINT(1, "HWA742 systim %lu ps extif_clk_period %u ps" + "extif_clk_div %d\n", systim, hwa742.extif_clk_period, div); + + t = &hwa742.lut_timings; + memset(t, 0, sizeof(*t)); + + t->clk_div = div; + + t->cs_on_time = 0; + t->we_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div); + t->re_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div); + t->access_time = round_to_extif_ticks(t->re_on_time + 4 * systim + + 26000, div); + t->we_off_time = round_to_extif_ticks(t->we_on_time + 1000, div); + t->re_off_time = round_to_extif_ticks(t->re_on_time + 4 * systim + + 26000, div); + t->cs_off_time = round_to_extif_ticks(t->re_off_time + 1000, div); + t->we_cycle_time = round_to_extif_ticks(2 * systim + 2000, div); + if (t->we_cycle_time < t->we_off_time) + t->we_cycle_time = t->we_off_time; + t->re_cycle_time = round_to_extif_ticks(2000 + 4 * systim + 26000, div); + if (t->re_cycle_time < t->re_off_time) + t->re_cycle_time = t->re_off_time; + t->cs_pulse_width = 0; + + DBGPRINT(1, "[lut]cson %d csoff %d reon %d reoff %d\n", + t->cs_on_time, t->cs_off_time, t->re_on_time, t->re_off_time); + DBGPRINT(1, "[lut]weon %d weoff %d recyc %d wecyc %d\n", + t->we_on_time, t->we_off_time, t->re_cycle_time, + t->we_cycle_time); + DBGPRINT(1, "[lut]rdaccess %d cspulse %d\n", + t->access_time, t->cs_pulse_width); + + return hwa742.extif->convert_timings(t); +} + +static int calc_extif_timings(unsigned long sysclk) +{ + int max_clk_div; + int div; + + hwa742.extif->get_clk_info(&hwa742.extif_clk_period, &max_clk_div); + for (div = 1; div < max_clk_div; div++) { + if (calc_reg_timing(sysclk, div) == 0) + break; + } + if (div == max_clk_div) + goto err; + + for (div = 1; div < max_clk_div; div++) { + if (calc_lut_timing(sysclk, div) == 0) + break; + } + + if (div < max_clk_div) + return 0; + +err: + pr_err("can't setup timings\n"); + return -1; +} + +static unsigned long hwa742_get_caps(void) +{ + return OMAPFB_CAPS_MANUAL_UPDATE; +} + +static void hwa742_suspend(void) +{ + hwa742.update_mode_before_suspend = hwa742.update_mode; + hwa742_set_update_mode(OMAPFB_UPDATE_DISABLED); + /* Enable sleep mode */ + hwa742_write_reg(HWA742_POWER_SAVE, 1 << 1); + clk_disable(hwa742.sys_ck); +} + +static void hwa742_resume(void) +{ + if (clk_enable(hwa742.sys_ck) != 0) + pr_err("failed to enable SYS clock\n"); + /* Disable sleep mode */ + hwa742_write_reg(HWA742_POWER_SAVE, 0); + while (1) { + /* Loop until PLL output is stabilized */ + if (hwa742_read_reg(HWA742_PLL_DIV_REG) & (1 << 7)) + break; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(5)); + } + hwa742_set_update_mode(hwa742.update_mode_before_suspend); +} + +struct lcd_ctrl hwa742_ctrl; + +static int hwa742_init(struct omapfb_device *fbdev, int ext_mode, int req_vram_size) +{ + int r = 0, i; + u8 rev, conf; + unsigned long sysfreq; + int div, nd; + + DBGENTER(1); + + hwa742.sys_ck = clk_get(0, "bclk"); + if (IS_ERR(hwa742.sys_ck)) { + pr_err("can't get SYS clock\n"); + return PTR_ERR(hwa742.sys_ck); + } + + if ((r = clk_enable(hwa742.sys_ck)) != 0) { + pr_err("can't enable SYS clock\n"); + clk_put(hwa742.sys_ck); + return r; + } + + BUG_ON(!fbdev->ext_if || !fbdev->int_ctrl); + + hwa742.fbdev = fbdev; + hwa742.extif = fbdev->ext_if; + hwa742.int_ctrl = fbdev->int_ctrl; + + spin_lock_init(&hwa742.req_lock); + + if ((r = hwa742.int_ctrl->init(fbdev, 1, req_vram_size)) < 0) + goto err1; + + if ((r = hwa742.extif->init()) < 0) + goto err2; + + hwa742_ctrl.get_vram_layout = hwa742.int_ctrl->get_vram_layout; + hwa742_ctrl.mmap = hwa742.int_ctrl->mmap; + + sysfreq = clk_get_rate(hwa742.sys_ck); + if ((r = calc_extif_timings(sysfreq)) < 0) + goto err3; + hwa742.extif->set_timings(&hwa742.reg_timings); + + div = (hwa742_read_reg(HWA742_PLL_DIV_REG) & 0x3f) + 1; + + nd = (hwa742_read_reg(HWA742_PLL_4_REG) & 0x7f) + 1; + + if ((r = calc_extif_timings(sysfreq / div * nd)) < 0) + goto err3; + hwa742.extif->set_timings(&hwa742.reg_timings); + + rev = hwa742_read_reg(HWA742_REV_CODE_REG); + if ((rev & 0xfc) != 0x80) { + pr_err("invalid revision %02x\n", rev); + r = -ENODEV; + goto err3; + } + + conf = hwa742_read_reg(HWA742_CONFIG_REG); + pr_info(MODULE_NAME ": Epson HWA742 LCD controller rev. %d " + "initialized (CNF pins %x)\n", rev & 0x03, conf & 0x07); + + if (!(hwa742_read_reg(HWA742_PLL_DIV_REG) & 0x80)) { + pr_err("controller not initialized by the bootloader\n"); + r = -ENODEV; + goto err2; + } + + hwa742.max_transmit_size = hwa742.extif->max_transmit_size; + + hwa742.update_mode = OMAPFB_UPDATE_DISABLED; + + hwa742.auto_update_window.x = 0; + hwa742.auto_update_window.y = 0; + hwa742.auto_update_window.width = fbdev->panel->x_res; + hwa742.auto_update_window.height = fbdev->panel->y_res; + hwa742.auto_update_window.format = 0; + + init_timer(&hwa742.auto_update_timer); + hwa742.auto_update_timer.function = hwa742_update_window_auto; + hwa742.auto_update_timer.data = 0; + + hwa742.prev_color_mode = -1; + hwa742.prev_flags = 0; + + hwa742.fbdev = fbdev; + + INIT_LIST_HEAD(&hwa742.free_req_list); + INIT_LIST_HEAD(&hwa742.pending_req_list); + for (i = 0; i < ARRAY_SIZE(hwa742.req_pool); i++) + list_add(&hwa742.req_pool[i].entry, &hwa742.free_req_list); + BUG_ON(i <= IRQ_REQ_POOL_SIZE); + sema_init(&hwa742.req_sema, i - IRQ_REQ_POOL_SIZE); + + return 0; +err3: + hwa742.extif->cleanup(); +err2: + hwa742.int_ctrl->cleanup(); +err1: + clk_disable(hwa742.sys_ck); + clk_put(hwa742.sys_ck); + return r; +} + +static void hwa742_cleanup(void) +{ + hwa742_set_update_mode(OMAPFB_UPDATE_DISABLED); + hwa742.extif->cleanup(); + hwa742.int_ctrl->cleanup(); + clk_disable(hwa742.sys_ck); + clk_put(hwa742.sys_ck); +} + +struct lcd_ctrl hwa742_ctrl = { + .name = "hwa742", + .init = hwa742_init, + .cleanup = hwa742_cleanup, + .get_caps = hwa742_get_caps, + .set_update_mode = hwa742_set_update_mode, + .get_update_mode = hwa742_get_update_mode, + .setup_plane = hwa742_setup_plane, + .enable_plane = hwa742_enable_plane, + .update_window = hwa742_update_window_async, + .sync = hwa742_sync, + .suspend = hwa742_suspend, + .resume = hwa742_resume, +}; + diff --git a/drivers/video/omap/hwa742.h b/drivers/video/omap/hwa742.h new file mode 100644 index 00000000000..7bbe63b4829 --- /dev/null +++ b/drivers/video/omap/hwa742.h @@ -0,0 +1,29 @@ +#ifndef __HWA742_H +#define __HWA742_H + +#include + +#include + +#define HWA742_EVENT_READY 1 +#define HWA742_EVENT_DISABLED 2 + +struct hwa742_notifier_block { + struct notifier_block nb; + void *data; +}; + +typedef int (*hwa742_notifier_callback_t)(struct hwa742_notifier_block *, + unsigned long event, + struct omapfb_device *fbdev); + +extern void hwa742_read_id(int *rev_code, int *config); +extern int hwa742_register_client(struct hwa742_notifier_block *hwa742_nb, + hwa742_notifier_callback_t callback, + void *callback_data); +extern int hwa742_unregister_client(struct hwa742_notifier_block *hwa742_nb); +extern int hwa742_update_window_async(struct omapfb_update_window *win, + void (*complete_callback)(void *arg), + void *complete_callback_data); + +#endif diff --git a/drivers/video/omap/lcd_h2.c b/drivers/video/omap/lcd_h2.c index 1021739b342..128ba247c4a 100644 --- a/drivers/video/omap/lcd_h2.c +++ b/drivers/video/omap/lcd_h2.c @@ -22,6 +22,7 @@ */ #include +#include #include #include @@ -131,3 +132,52 @@ struct lcd_panel h2_panel = { .get_caps = h2_panel_get_caps, }; +static int h2_panel_probe(struct platform_device *pdev) +{ + DBGENTER(1); + omapfb_register_panel(&h2_panel); + return 0; +} + +static int h2_panel_remove(struct platform_device *pdev) +{ + DBGENTER(1); + return 0; +} + +static int h2_panel_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + DBGENTER(1); + return 0; +} + +static int h2_panel_resume(struct platform_device *pdev) +{ + DBGENTER(1); + return 0; +} + +struct platform_driver h2_panel_driver = { + .probe = h2_panel_probe, + .remove = h2_panel_remove, + .suspend = h2_panel_suspend, + .resume = h2_panel_resume, + .driver = { + .name = "lcd_h2", + .owner = THIS_MODULE, + }, +}; + +static int h2_panel_drv_init(void) +{ + return platform_driver_register(&h2_panel_driver); +} + +static void h2_panel_drv_cleanup(void) +{ + platform_driver_unregister(&h2_panel_driver); +} + +module_init(h2_panel_drv_init); +module_exit(h2_panel_drv_cleanup); + diff --git a/drivers/video/omap/lcd_h3.c b/drivers/video/omap/lcd_h3.c index f6e8e62020d..21c9e3ee8bb 100644 --- a/drivers/video/omap/lcd_h3.c +++ b/drivers/video/omap/lcd_h3.c @@ -22,6 +22,7 @@ */ #include +#include #include #include @@ -110,3 +111,52 @@ struct lcd_panel h3_panel = { .get_caps = h3_panel_get_caps, }; +static int h3_panel_probe(struct platform_device *pdev) +{ + DBGENTER(1); + omapfb_register_panel(&h3_panel); + return 0; +} + +static int h3_panel_remove(struct platform_device *pdev) +{ + DBGENTER(1); + return 0; +} + +static int h3_panel_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + DBGENTER(1); + return 0; +} + +static int h3_panel_resume(struct platform_device *pdev) +{ + DBGENTER(1); + return 0; +} + +struct platform_driver h3_panel_driver = { + .probe = h3_panel_probe, + .remove = h3_panel_remove, + .suspend = h3_panel_suspend, + .resume = h3_panel_resume, + .driver = { + .name = "lcd_h3", + .owner = THIS_MODULE, + }, +}; + +static int h3_panel_drv_init(void) +{ + return platform_driver_register(&h3_panel_driver); +} + +static void h3_panel_drv_cleanup(void) +{ + platform_driver_unregister(&h3_panel_driver); +} + +module_init(h3_panel_drv_init); +module_exit(h3_panel_drv_cleanup); + diff --git a/drivers/video/omap/lcd_h4.c b/drivers/video/omap/lcd_h4.c index 48172477dba..ab7782a2fac 100644 --- a/drivers/video/omap/lcd_h4.c +++ b/drivers/video/omap/lcd_h4.c @@ -22,6 +22,7 @@ */ #include +#include #include @@ -84,3 +85,52 @@ struct lcd_panel h4_panel = { .get_caps = h4_panel_get_caps, }; +static int h4_panel_probe(struct platform_device *pdev) +{ + DBGENTER(1); + omapfb_register_panel(&h4_panel); + return 0; +} + +static int h4_panel_remove(struct platform_device *pdev) +{ + DBGENTER(1); + return 0; +} + +static int h4_panel_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + DBGENTER(1); + return 0; +} + +static int h4_panel_resume(struct platform_device *pdev) +{ + DBGENTER(1); + return 0; +} + +struct platform_driver h4_panel_driver = { + .probe = h4_panel_probe, + .remove = h4_panel_remove, + .suspend = h4_panel_suspend, + .resume = h4_panel_resume, + .driver = { + .name = "lcd_h4", + .owner = THIS_MODULE, + }, +}; + +static int h4_panel_drv_init(void) +{ + return platform_driver_register(&h4_panel_driver); +} + +static void h4_panel_drv_cleanup(void) +{ + platform_driver_unregister(&h4_panel_driver); +} + +module_init(h4_panel_drv_init); +module_exit(h4_panel_drv_cleanup); + diff --git a/drivers/video/omap/lcd_inn1510.c b/drivers/video/omap/lcd_inn1510.c index d6ae2f6533c..31988d1b574 100644 --- a/drivers/video/omap/lcd_inn1510.c +++ b/drivers/video/omap/lcd_inn1510.c @@ -22,6 +22,7 @@ */ #include +#include #include @@ -93,3 +94,52 @@ struct lcd_panel innovator1510_panel = { .get_caps = innovator1510_panel_get_caps, }; +static int innovator1510_panel_probe(struct platform_device *pdev) +{ + DBGENTER(1); + omapfb_register_panel(&innovator1510_panel); + return 0; +} + +static int innovator1510_panel_remove(struct platform_device *pdev) +{ + DBGENTER(1); + return 0; +} + +static int innovator1510_panel_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + DBGENTER(1); + return 0; +} + +static int innovator1510_panel_resume(struct platform_device *pdev) +{ + DBGENTER(1); + return 0; +} + +struct platform_driver innovator1510_panel_driver = { + .probe = innovator1510_panel_probe, + .remove = innovator1510_panel_remove, + .suspend = innovator1510_panel_suspend, + .resume = innovator1510_panel_resume, + .driver = { + .name = "lcd_inn1510", + .owner = THIS_MODULE, + }, +}; + +static int innovator1510_panel_drv_init(void) +{ + return platform_driver_register(&innovator1510_panel_driver); +} + +static void innovator1510_panel_drv_cleanup(void) +{ + platform_driver_unregister(&innovator1510_panel_driver); +} + +module_init(innovator1510_panel_drv_init); +module_exit(innovator1510_panel_drv_cleanup); + diff --git a/drivers/video/omap/lcd_inn1610.c b/drivers/video/omap/lcd_inn1610.c index bf71485e427..9a03fce0eff 100644 --- a/drivers/video/omap/lcd_inn1610.c +++ b/drivers/video/omap/lcd_inn1610.c @@ -121,3 +121,52 @@ struct lcd_panel innovator1610_panel = { .get_caps = innovator1610_panel_get_caps, }; +static int innovator1610_panel_probe(struct platform_device *pdev) +{ + DBGENTER(1); + omapfb_register_panel(&innovator1610_panel); + return 0; +} + +static int innovator1610_panel_remove(struct platform_device *pdev) +{ + DBGENTER(1); + return 0; +} + +static int innovator1610_panel_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + DBGENTER(1); + return 0; +} + +static int innovator1610_panel_resume(struct platform_device *pdev) +{ + DBGENTER(1); + return 0; +} + +struct platform_driver innovator1610_panel_driver = { + .probe = innovator1610_panel_probe, + .remove = innovator1610_panel_remove, + .suspend = innovator1610_panel_suspend, + .resume = innovator1610_panel_resume, + .driver = { + .name = "lcd_inn1610", + .owner = THIS_MODULE, + }, +}; + +static int innovator1610_panel_drv_init(void) +{ + return platform_driver_register(&innovator1610_panel_driver); +} + +static void innovator1610_panel_drv_cleanup(void) +{ + platform_driver_unregister(&innovator1610_panel_driver); +} + +module_init(innovator1610_panel_drv_init); +module_exit(innovator1610_panel_drv_cleanup); + diff --git a/drivers/video/omap/lcd_lph8923.c b/drivers/video/omap/lcd_lph8923.c new file mode 100644 index 00000000000..ff331160d9b --- /dev/null +++ b/drivers/video/omap/lcd_lph8923.c @@ -0,0 +1,483 @@ +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "../../cbus/tahvo.h" + +/* #define OMAPFB_DBG 1 */ + +#include "debug.h" + +#define LPH8923_MODULE_NAME "lcd_lph8923" + +#define LPH8923_VER_BUGGY 1 +#define LPH8923_VER_NON_BUGGY 3 + +struct { + int enabled; + int version; + + u8 display_id[3]; + unsigned int saved_bklight_level; + unsigned long hw_guard_end; /* next value of jiffies + when we can issue the + next sleep in/out command */ + unsigned long hw_guard_wait; /* max guard time in jiffies */ + struct omapfb_device *fbdev; + struct spi_device *spi; +} lph8923; + +#define LPH8923_CMD_READ_DISP_ID 0x04 +#define LPH8923_CMD_READ_RED 0x06 +#define LPH8923_CMD_READ_GREEN 0x07 +#define LPH8923_CMD_READ_BLUE 0x08 +#define LPH8923_CMD_READ_DISP_STATUS 0x09 +#define LPH8923_CMD_SLEEP_IN 0x10 +#define LPH8923_CMD_SLEEP_OUT 0x11 +#define LPH8923_CMD_DISP_OFF 0x28 +#define LPH8923_CMD_DISP_ON 0x29 + +static struct lcd_panel lph8923_panel; + +#define LPH8923_SPEED_HZ 12000000 + +static int lph8923_spi_probe(struct spi_device *spi) +{ + DBGENTER(1); + + spi->dev.power.power_state = PMSG_ON; + spi->mode = SPI_MODE_0; + spi->bits_per_word = 9; + spi_setup(spi); + + DBGPRINT(1, "spi %p\n", spi); + lph8923.spi = spi; + + omapfb_register_panel(&lph8923_panel); + + return 0; +} + +static int lph8923_spi_remove(struct spi_device *spi) +{ + DBGENTER(1); + + lph8923.spi = NULL; + + return 0; +} + +static struct spi_driver lph8923_spi_driver = { + .driver = { + .name = LPH8923_MODULE_NAME, + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = lph8923_spi_probe, + .remove = __devexit_p(lph8923_spi_remove), +}; + +static int lph8923_drv_init(void) +{ + spi_register_driver(&lph8923_spi_driver); + + return 0; +} +module_init(lph8923_drv_init); + +static void lph8923_drv_cleanup(void) +{ + spi_unregister_driver(&lph8923_spi_driver); +} +module_exit(lph8923_drv_cleanup); + +static void set_spi_data_width(int width) +{ + if (lph8923.spi->bits_per_word != width) { + lph8923.spi->bits_per_word = width; + spi_setup(lph8923.spi); + } +} + +static void lph8923_read(int cmd, u8 *buf, int len) +{ + struct spi_message m; + struct spi_transfer t; + u16 w; + + BUG_ON(lph8923.spi == NULL); + + spi_message_init(&m); + m.spi = lph8923.spi; + + if (len > 1) { + cmd <<= 1; + set_spi_data_width(10); + } else + set_spi_data_width(9); + + w = cmd; + t.cs_change = len ? 1 : 0; + t.tx_buf = &w; + t.rx_buf = NULL; + t.len = 2; + + spi_message_add_tail(&t, &m); + + spi_sync(m.spi, &m); + + if (!len) + return; + + spi_message_init(&m); + m.spi = lph8923.spi; + + t.cs_change = 0; + t.tx_buf = NULL; + t.rx_buf = buf; + t.len = len; + + set_spi_data_width(8); + + spi_message_add_tail(&t, &m); + + spi_sync(m.spi, &m); +} + +static void lph8923_write(int cmd, const u8 *buf, int len) +{ + struct spi_message m; + struct spi_transfer t; + u16 w; + int i; + + BUG_ON(lph8923.spi == NULL); + + spi_message_init(&m); + m.spi = lph8923.spi; + + if (len > 1) { + cmd <<= 1; + set_spi_data_width(10); + } else { + set_spi_data_width(9); + } + + t.cs_change = 0; + w = cmd; + t.tx_buf = &w; + t.rx_buf = NULL; + t.len = 2; + + spi_message_add_tail(&t, &m); + spi_sync(m.spi, &m); + + if (!len) + return; + + set_spi_data_width(9); + + t.tx_buf = &w; + for (i = 0; i < len; i++) { + u16 w; + + spi_message_init(&m); + m.spi = lph8923.spi; + spi_message_add_tail(&t, &m); + w = buf[i] | (1 << 8); + spi_sync(m.spi, &m); + } +} + +static inline void lph8923_cmd(int cmd) +{ + lph8923_write(cmd, NULL, 0); +} + +struct cmd_data { + u8 cmd; + u8 len; + const u8 *data; +} __attribute__ ((packed));; + +static const struct cmd_data init_cmds_buggy_lph8923[] = { + { 0xb0, 1, "\x08" }, + { 0xb1, 2, "\x0b\x1c" }, + { 0xb2, 4, "\x00\x00\x00\x00" }, + { 0xb3, 4, "\x00\x00\x00\x00" }, + { 0xb4, 1, "\x87" }, + { 0xb5, 4, "\x37\x07\x37\x07" }, + { 0xb6, 2, "\x64\x24" }, + { 0xb7, 1, "\x90" }, + { 0xb8, 3, "\x10\x11\x20" }, + { 0xb9, 2, "\x31\x02" }, + { 0xba, 3, "\x04\xa3\x9d" }, + { 0xbb, 4, "\x15\xb2\x8c\x00" }, + { 0xc2, 3, "\x02\x00\x00" }, +}; + +static const struct cmd_data init_cmds_non_buggy_lph8923[] = { + { 0xc2, 3, "\x02\x00\x00" }, +}; + +static inline void lph8923_set_16bit_mode(void) +{ + lph8923_write(0x3a, "\x50", 1); +} + +static void lph8923_send_init_string(void) +{ + int c; + const struct cmd_data *cd; + + switch (lph8923.version) { + case LPH8923_VER_BUGGY: + c = sizeof(init_cmds_buggy_lph8923)/sizeof(init_cmds_buggy_lph8923[0]); + cd = init_cmds_buggy_lph8923; + break; + case LPH8923_VER_NON_BUGGY: + default: + c = sizeof(init_cmds_non_buggy_lph8923)/sizeof(init_cmds_non_buggy_lph8923[0]); + cd = init_cmds_non_buggy_lph8923; + break; + } + while (c--) { + lph8923_write(cd->cmd, cd->data, cd->len); + cd++; + } + lph8923_set_16bit_mode(); +} + +static void hw_guard_start(int guard_msec) +{ + lph8923.hw_guard_wait = msecs_to_jiffies(guard_msec); + lph8923.hw_guard_end = jiffies + lph8923.hw_guard_wait; +} + +static void hw_guard_wait(void) +{ + unsigned long wait = lph8923.hw_guard_end - jiffies; + + if ((long)wait > 0 && wait <= lph8923.hw_guard_wait) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(wait); + } +} + +static void lph8923_set_sleep_mode(int on) +{ + int cmd, sleep_time = 5; + + if (on) + cmd = LPH8923_CMD_SLEEP_IN; + else + cmd = LPH8923_CMD_SLEEP_OUT; + hw_guard_wait(); + lph8923_cmd(cmd); + hw_guard_start(120); + /* When we enable the panel, it seems we _have_ to sleep + * 120 ms before sending the init string */ + if (!on) + sleep_time = 120; + msleep(sleep_time); +} + +static void lph8923_set_display_state(int enabled) +{ + int cmd = enabled ? LPH8923_CMD_DISP_ON : LPH8923_CMD_DISP_OFF; + + lph8923_cmd(cmd); +} + +static void lph8923_detect(void) +{ + lph8923_read(LPH8923_CMD_READ_DISP_ID, lph8923.display_id, 3); + printk(KERN_INFO "Moscow display id: %02x%02x%02x\n", + lph8923.display_id[0], lph8923.display_id[1], + lph8923.display_id[2]); + + if (lph8923.display_id[0] == 0x45) { + lph8923.version = LPH8923_VER_NON_BUGGY; + printk(KERN_INFO "Non-buggy Moscow detected\n"); + return; + } else { + lph8923.version = LPH8923_VER_BUGGY; + printk(KERN_INFO "Buggy Moscow detected\n"); + } +} + +static int lph8923_enabled(void) +{ + u32 disp_status; + int enabled; + + lph8923_read(LPH8923_CMD_READ_DISP_STATUS, (u8 *)&disp_status, 4); + disp_status = __be32_to_cpu(disp_status); + enabled = (disp_status & (1 << 17)) && (disp_status & (1 << 10)); + DBGPRINT(1, ": panel %senabled by bootloader (status 0x%04x)\n", + enabled ? "" : "not ", disp_status); + return enabled; +} + +static int lph8923_panel_init(struct omapfb_device *fbdev) +{ + lph8923.fbdev = fbdev; + + lph8923.enabled = 1; + lph8923_detect(); + if (lph8923.version == LPH8923_VER_NON_BUGGY) + lph8923.enabled = lph8923_enabled(); + else + /* We can't be sure, but assume the bootloader + * enabled it already */ + lph8923.enabled = 1; + + return 0; +} + +static void lph8923_panel_cleanup(void) +{ +} + +static int lph8923_panel_set_bklight_level(unsigned int level) +{ + if (level > 0xf) + return -EINVAL; + if (!lph8923.enabled) { + lph8923.saved_bklight_level = level; + return 0; + } + level = level * tahvo_get_max_backlight_level() / 0x0f; + tahvo_set_backlight_level(level); + + return 0; +} + +static unsigned int lph8923_panel_get_bklight_level(void) +{ + return tahvo_get_backlight_level() * 0x0f / + tahvo_get_max_backlight_level(); +} + +static unsigned int lph8923_panel_get_bklight_max(void) +{ + return 0x0f; +} + +static int lph8923_panel_enable(void) +{ + if (lph8923.enabled) + return 0; + + lph8923_set_sleep_mode(0); + lph8923.enabled = 1; + lph8923_send_init_string(); + lph8923_set_display_state(1); + lph8923_panel_set_bklight_level(lph8923.saved_bklight_level); + + return 0; +} + +static void lph8923_panel_disable(void) +{ + if (!lph8923.enabled) + return; + lph8923.saved_bklight_level = lph8923_panel_get_bklight_level(); + lph8923_panel_set_bklight_level(0); + lph8923_set_display_state(0); + lph8923_set_sleep_mode(1); + lph8923.enabled = 0; +} + +static unsigned long lph8923_panel_get_caps(void) +{ + return OMAPFB_CAPS_SET_BACKLIGHT; +} + +static u16 read_first_pixel(void) +{ + u8 b; + u16 pixel; + + lph8923_read(LPH8923_CMD_READ_RED, &b, 1); + pixel = (b >> 1) << 11; + lph8923_read(LPH8923_CMD_READ_GREEN, &b, 1); + pixel |= b << 5; + lph8923_read(LPH8923_CMD_READ_BLUE, &b, 1); + pixel |= (b >> 1); + + return pixel; +} + +static int lph8923_panel_test(int test_num) +{ + static const u16 test_values[4] = { + 0x0000, 0xffff, 0xaaaa, 0x5555, + }; + int i; + + if (test_num != LCD_LPH8923_TEST_RGB_LINES) + return LCD_LPH8923_TEST_INVALID; + + for (i = 0; i < ARRAY_SIZE(test_values); i++) { + int delay; + unsigned long tmo; + + omapfb_write_first_pixel(lph8923.fbdev, test_values[i]); + tmo = jiffies + msecs_to_jiffies(100); + delay = msecs_to_jiffies(25); + while (1) { + u16 pixel; + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(delay); + pixel = read_first_pixel(); + if (pixel == test_values[i]) + break; + if (time_after(jiffies, tmo)) { + printk(KERN_ERR "Moscow RGB I/F test failed: " + "expecting %04x, got %04x\n", + test_values[i], pixel); + return LCD_LPH8923_TEST_FAILED; + } + delay = msecs_to_jiffies(10); + } + } + + return 0; +} + +static struct lcd_panel lph8923_panel = { + .name = "lph8923", + .config = OMAP_LCDC_PANEL_TFT, + + .bpp = 16, + .data_lines = 16, + .x_res = 800, + .y_res = 480, + .pixel_clock = 21940, + .hsw = 50, + .hfp = 20, + .hbp = 15, + .vsw = 2, + .vfp = 1, + .vbp = 3, + + .init = lph8923_panel_init, + .cleanup = lph8923_panel_cleanup, + .enable = lph8923_panel_enable, + .disable = lph8923_panel_disable, + .get_caps = lph8923_panel_get_caps, + .set_bklight_level= lph8923_panel_set_bklight_level, + .get_bklight_level= lph8923_panel_get_bklight_level, + .get_bklight_max= lph8923_panel_get_bklight_max, + .run_test = lph8923_panel_test, +}; diff --git a/drivers/video/omap/lcd_osk.c b/drivers/video/omap/lcd_osk.c index 54808d60fea..5751ec6d325 100644 --- a/drivers/video/omap/lcd_osk.c +++ b/drivers/video/omap/lcd_osk.c @@ -23,6 +23,7 @@ */ #include +#include #include #include @@ -113,3 +114,52 @@ struct lcd_panel osk_panel = { .get_caps = osk_panel_get_caps, }; +static int osk_panel_probe(struct platform_device *pdev) +{ + DBGENTER(1); + omapfb_register_panel(&osk_panel); + return 0; +} + +static int osk_panel_remove(struct platform_device *pdev) +{ + DBGENTER(1); + return 0; +} + +static int osk_panel_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + DBGENTER(1); + return 0; +} + +static int osk_panel_resume(struct platform_device *pdev) +{ + DBGENTER(1); + return 0; +} + +struct platform_driver osk_panel_driver = { + .probe = osk_panel_probe, + .remove = osk_panel_remove, + .suspend = osk_panel_suspend, + .resume = osk_panel_resume, + .driver = { + .name = "lcd_osk", + .owner = THIS_MODULE, + }, +}; + +static int osk_panel_drv_init(void) +{ + return platform_driver_register(&osk_panel_driver); +} + +static void osk_panel_drv_cleanup(void) +{ + platform_driver_unregister(&osk_panel_driver); +} + +module_init(osk_panel_drv_init); +module_exit(osk_panel_drv_cleanup); + diff --git a/drivers/video/omap/lcd_p2.c b/drivers/video/omap/lcd_p2.c index 5a3e2dbf790..3f03dca42a6 100644 --- a/drivers/video/omap/lcd_p2.c +++ b/drivers/video/omap/lcd_p2.c @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -305,3 +306,52 @@ struct lcd_panel p2_panel = { .get_caps = p2_panel_get_caps, }; +static int p2_panel_probe(struct platform_device *pdev) +{ + DBGENTER(1); + omapfb_register_panel(&p2_panel); + return 0; +} + +static int p2_panel_remove(struct platform_device *pdev) +{ + DBGENTER(1); + return 0; +} + +static int p2_panel_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + DBGENTER(1); + return 0; +} + +static int p2_panel_resume(struct platform_device *pdev) +{ + DBGENTER(1); + return 0; +} + +struct platform_driver p2_panel_driver = { + .probe = p2_panel_probe, + .remove = p2_panel_remove, + .suspend = p2_panel_suspend, + .resume = p2_panel_resume, + .driver = { + .name = "lcd_p2", + .owner = THIS_MODULE, + }, +}; + +static int p2_panel_drv_init(void) +{ + return platform_driver_register(&p2_panel_driver); +} + +static void p2_panel_drv_cleanup(void) +{ + platform_driver_unregister(&p2_panel_driver); +} + +module_init(p2_panel_drv_init); +module_exit(p2_panel_drv_cleanup); + diff --git a/drivers/video/omap/lcd_palmte.c b/drivers/video/omap/lcd_palmte.c index 0f84b631b35..5cf3851f4e2 100644 --- a/drivers/video/omap/lcd_palmte.c +++ b/drivers/video/omap/lcd_palmte.c @@ -22,6 +22,7 @@ */ #include +#include #include @@ -89,3 +90,52 @@ struct lcd_panel palmte_panel = { .get_caps = palmte_panel_get_caps, }; +static int palmte_panel_probe(struct platform_device *pdev) +{ + DBGENTER(1); + omapfb_register_panel(&palmte_panel); + return 0; +} + +static int palmte_panel_remove(struct platform_device *pdev) +{ + DBGENTER(1); + return 0; +} + +static int palmte_panel_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + DBGENTER(1); + return 0; +} + +static int palmte_panel_resume(struct platform_device *pdev) +{ + DBGENTER(1); + return 0; +} + +struct platform_driver palmte_panel_driver = { + .probe = palmte_panel_probe, + .remove = palmte_panel_remove, + .suspend = palmte_panel_suspend, + .resume = palmte_panel_resume, + .driver = { + .name = "lcd_palmte", + .owner = THIS_MODULE, + }, +}; + +static int palmte_panel_drv_init(void) +{ + return platform_driver_register(&palmte_panel_driver); +} + +static void palmte_panel_drv_cleanup(void) +{ + platform_driver_unregister(&palmte_panel_driver); +} + +module_init(palmte_panel_drv_init); +module_exit(palmte_panel_drv_cleanup); + diff --git a/drivers/video/omap/lcdc.c b/drivers/video/omap/lcdc.c index 25c376dc7ea..bfe94784cbf 100644 --- a/drivers/video/omap/lcdc.c +++ b/drivers/video/omap/lcdc.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -37,7 +38,7 @@ #include -/* #define OMAPFB_DBG 2 */ +/* #define OMAPFB_DBG 1 */ #include "debug.h" @@ -87,13 +88,17 @@ enum lcdc_load_mode { static struct omap_lcd_controller { enum omapfb_update_mode update_mode; + int ext_mode; unsigned long frame_offset; int screen_width; + int xres; + int yres; enum omapfb_color_format color_mode; int bpp; - int palette_org; + void *palette_virt; + dma_addr_t palette_phys; int palette_code; int palette_size; @@ -103,6 +108,10 @@ static struct omap_lcd_controller { struct clk *lcd_ck; struct omapfb_device *fbdev; + void (*dma_callback)(void *data); + void *dma_callback_data; + + int fbmem_allocated; dma_addr_t vram_phys; void *vram_virt; unsigned long vram_size; @@ -207,18 +216,21 @@ static void setup_lcd_dma(void) OMAP_DMA_DATA_TYPE_S32, }; struct fb_var_screeninfo *var = &omap_lcdc.fbdev->fb_info->var; - struct lcd_panel *panel = omap_lcdc.fbdev->panel; unsigned long src; int esize, xelem, yelem; - src = omap_lcdc.vram_phys + PAGE_ALIGN(MAX_PALETTE_SIZE) + - omap_lcdc.frame_offset; + src = omap_lcdc.vram_phys + omap_lcdc.frame_offset; switch (var->rotate) { case 0: - esize = omap_lcdc.fbdev->mirror || (src & 3) ? 2 : 4; - xelem = panel->x_res * omap_lcdc.bpp / 8 / esize; - yelem = panel->y_res; + if (omap_lcdc.fbdev->mirror || (src & 3) || + omap_lcdc.color_mode == OMAPFB_COLOR_YUV420 || + (omap_lcdc.xres & 1)) + esize = 2; + else + esize = 4; + xelem = omap_lcdc.xres * omap_lcdc.bpp / 8 / esize; + yelem = omap_lcdc.yres; break; case 90: case 180: @@ -227,21 +239,27 @@ static void setup_lcd_dma(void) BUG(); } esize = 2; - xelem = panel->y_res * omap_lcdc.bpp / 16; - yelem = panel->x_res; + xelem = omap_lcdc.yres * omap_lcdc.bpp / 16; + yelem = omap_lcdc.xres; break; default: BUG(); return; } - DBGPRINT(1, "setup_dma: src %#010lx esize %d xelem %d yelem %d\n", + DBGPRINT(2, "setup_dma: src %#010lx esize %d xelem %d yelem %d\n", src, esize, xelem, yelem); omap_set_lcd_dma_b1(src, xelem, yelem, dma_elem_type[esize]); - omap_set_lcd_dma_single_transfer(0); if (!cpu_is_omap15xx()) { + int bpp = omap_lcdc.bpp; + + /* YUV support is only for external mode when we have the + * YUV window embedded in a 16bpp frame buffer. + */ + if (omap_lcdc.color_mode == OMAPFB_COLOR_YUV420) + bpp = 16; /* Set virtual xres elem size */ omap_set_lcd_dma_b1_vxres( - omap_lcdc.screen_width * omap_lcdc.bpp / 8 / esize); + omap_lcdc.screen_width * bpp / 8 / esize); /* Setup transformations */ omap_set_lcd_dma_b1_rotation(var->rotate); omap_set_lcd_dma_b1_mirror(omap_lcdc.fbdev->mirror); @@ -305,7 +323,7 @@ static int omap_lcdc_setup_plane(int plane, int channel_out, struct lcd_panel *panel = omap_lcdc.fbdev->panel; int rot_x, rot_y; - DBGENTER(1); + DBGENTER(2); if (var->rotate == 0) { rot_x = panel->x_res; @@ -315,7 +333,7 @@ static int omap_lcdc_setup_plane(int plane, int channel_out, rot_y = panel->x_res; } if (plane != 0 || channel_out != 0 || pos_x != 0 || pos_y != 0 || - width != rot_x || height != rot_y) { + width > rot_x || height > rot_y) { DBGPRINT(1, "invalid plane params plane %d pos_x %d " "pos_y %d w %d h %d\n", plane, pos_x, pos_y, width, height); @@ -323,6 +341,8 @@ static int omap_lcdc_setup_plane(int plane, int channel_out, } omap_lcdc.frame_offset = offset; + omap_lcdc.xres = width; + omap_lcdc.yres = height; omap_lcdc.screen_width = screen_width; omap_lcdc.color_mode = color_mode; @@ -337,6 +357,18 @@ static int omap_lcdc_setup_plane(int plane, int channel_out, omap_lcdc.palette_code = 0x4000; omap_lcdc.palette_size = 32; break; + case OMAPFB_COLOR_YUV420: + if (omap_lcdc.ext_mode) { + omap_lcdc.bpp = 12; + break; + } + /* fallthrough */ + case OMAPFB_COLOR_YUV422: + if (omap_lcdc.ext_mode) { + omap_lcdc.bpp = 16; + break; + } + /* fallthrough */ default: /* FIXME: other BPPs. * bpp1: code 0, size 256 @@ -348,8 +380,10 @@ static int omap_lcdc_setup_plane(int plane, int channel_out, return -1; } - omap_lcdc.palette_org = PAGE_ALIGN(MAX_PALETTE_SIZE) - - omap_lcdc.palette_size; + if (omap_lcdc.ext_mode) { + setup_lcd_dma(); + return 0; + } if (omap_lcdc.update_mode == OMAPFB_AUTO_UPDATE) { disable_controller(); @@ -358,14 +392,17 @@ static int omap_lcdc_setup_plane(int plane, int channel_out, enable_controller(); } - DBGLEAVE(1); + DBGLEAVE(2); return 0; } static int omap_lcdc_enable_plane(int plane, int enable) { - if (plane != 0 || enable != 1) + DBGPRINT(2, "plane %d enable %d update_mode %d ext_mode %d\n", + plane, enable, omap_lcdc.update_mode, + omap_lcdc.ext_mode); + if (plane != OMAPFB_PLANE_GFX) return -EINVAL; return 0; @@ -381,12 +418,12 @@ static void load_palette(void) DBGENTER(1); - palette = (u16 *)((u8 *)omap_lcdc.vram_virt + omap_lcdc.palette_org); + palette = (u16 *)omap_lcdc.palette_virt; *(u16 *)palette &= 0x0fff; *(u16 *)palette |= omap_lcdc.palette_code; - omap_set_lcd_dma_b1(omap_lcdc.vram_phys + omap_lcdc.palette_org, + omap_set_lcd_dma_b1(omap_lcdc.palette_phys, omap_lcdc.palette_size / 4 + 1, 1, OMAP_DMA_DATA_TYPE_S32); omap_set_lcd_dma_single_transfer(1); @@ -403,16 +440,45 @@ static void load_palette(void) disable_irqs(OMAP_LCDC_IRQ_LOADED_PALETTE); omap_stop_lcd_dma(); + omap_set_lcd_dma_single_transfer(omap_lcdc.ext_mode); + DBGLEAVE(1); } +/* Used only in internal controller mode */ +static int omap_lcdc_setcolreg(u_int regno, u16 red, u16 green, u16 blue, + u16 transp, int update_hw_pal) +{ + u16 *palette; + + if (omap_lcdc.color_mode != OMAPFB_COLOR_CLUT_8BPP || regno > 255) + return -EINVAL; + + palette = (u16 *)omap_lcdc.palette_virt; + + palette[regno] &= ~0x0fff; + palette[regno] |= ((red >> 12) << 8) | ((green >> 12) << 4 ) | + (blue >> 12); + + if (update_hw_pal) { + disable_controller(); + omap_stop_lcd_dma(); + load_palette(); + setup_lcd_dma(); + set_load_mode(OMAP_LCDC_LOAD_FRAME); + enable_controller(); + } + + return 0; +} + static void calc_ck_div(int is_tft, int pck, int *pck_div) { unsigned long lck; pck = max(1, pck); lck = clk_get_rate(omap_lcdc.lcd_ck); - *pck_div = lck / pck; + *pck_div = (lck + pck - 1) / pck; if (is_tft) *pck_div = max(2, *pck_div); else @@ -486,7 +552,9 @@ static void inline setup_regs(void) } /* Configure the LCD controller, download the color palette and start a looped - * DMA transfer of the frame image data. */ + * DMA transfer of the frame image data. Called only in internal + * controller mode. + */ static int omap_lcdc_set_update_mode(enum omapfb_update_mode mode) { int r = 0; @@ -527,6 +595,7 @@ static enum omapfb_update_mode omap_lcdc_get_update_mode(void) return omap_lcdc.update_mode; } +/* PM code called only in internal controller mode */ static void omap_lcdc_suspend(void) { if (omap_lcdc.update_mode == OMAPFB_AUTO_UPDATE) { @@ -547,12 +616,184 @@ static void omap_lcdc_resume(void) } } +static unsigned long omap_lcdc_get_caps(void) +{ + return 0; +} + static void omap_lcdc_get_vram_layout(unsigned long *size, void **virt, dma_addr_t *phys) { - *size = omap_lcdc.vram_size - PAGE_ALIGN(MAX_PALETTE_SIZE); - *virt = (u8 *)omap_lcdc.vram_virt + PAGE_ALIGN(MAX_PALETTE_SIZE); - *phys = omap_lcdc.vram_phys + PAGE_ALIGN(MAX_PALETTE_SIZE); + *size = omap_lcdc.vram_size; + *virt = (u8 *)omap_lcdc.vram_virt; + *phys = omap_lcdc.vram_phys; +} + +int omap_lcdc_set_dma_callback(void (*callback)(void *data), void *data) +{ + BUG_ON(callback == NULL); + + if (omap_lcdc.dma_callback) + return -EBUSY; + else { + omap_lcdc.dma_callback = callback; + omap_lcdc.dma_callback_data = data; + } + return 0; +} +EXPORT_SYMBOL(omap_lcdc_set_dma_callback); + +void omap_lcdc_free_dma_callback(void) +{ + omap_lcdc.dma_callback = NULL; +} +EXPORT_SYMBOL(omap_lcdc_free_dma_callback); + +static void lcdc_dma_handler(u16 status, void *data) +{ + DBGENTER(2); + if (omap_lcdc.dma_callback) + omap_lcdc.dma_callback(omap_lcdc.dma_callback_data); +} + +static int mmap_kern(void) +{ + struct vm_struct *kvma; + struct vm_area_struct vma; + pgprot_t pgprot; + unsigned long vaddr; + + DBGENTER(1); + + kvma = get_vm_area(omap_lcdc.vram_size, VM_IOREMAP); + if (kvma == NULL) { + pr_err("can't get kernel vm area\n"); + return -ENOMEM; + } + vma.vm_mm = &init_mm; + + vaddr = (unsigned long)kvma->addr; + vma.vm_start = vaddr; + vma.vm_end = vaddr + omap_lcdc.vram_size; + + pgprot = pgprot_writecombine(pgprot_kernel); + if (io_remap_pfn_range(&vma, vaddr, + omap_lcdc.vram_phys >> PAGE_SHIFT, + omap_lcdc.vram_size, pgprot) < 0) { + pr_err("kernel mmap for FB memory failed\n"); + return -EAGAIN; + } + + omap_lcdc.vram_virt = (void *)vaddr; + + DBGLEAVE(1); + + return 0; +} + +static void unmap_kern(void) +{ + vunmap(omap_lcdc.vram_virt); +} + +static int alloc_palette_ram(void) +{ + omap_lcdc.palette_virt = dma_alloc_writecombine(omap_lcdc.fbdev->dev, + MAX_PALETTE_SIZE, &omap_lcdc.palette_phys, GFP_KERNEL); + if (omap_lcdc.palette_virt == NULL) { + pr_err("failed to alloc palette memory\n"); + return -ENOMEM; + } + memset(omap_lcdc.palette_virt, 0, MAX_PALETTE_SIZE); + + return 0; +} + +static void free_palette_ram(void) +{ + dma_free_writecombine(omap_lcdc.fbdev->dev, MAX_PALETTE_SIZE, + omap_lcdc.palette_virt, omap_lcdc.palette_phys); +} + +static int alloc_fbmem(int req_size) +{ + int frame_size; + struct lcd_panel *panel = omap_lcdc.fbdev->panel; + + frame_size = PAGE_ALIGN(panel->x_res * panel->bpp / 8 * panel->y_res); + if (req_size > frame_size) + frame_size = req_size; + omap_lcdc.vram_size = frame_size; + omap_lcdc.vram_virt = dma_alloc_writecombine(omap_lcdc.fbdev->dev, + omap_lcdc.vram_size, &omap_lcdc.vram_phys, GFP_KERNEL); + + if (omap_lcdc.vram_virt == NULL) { + pr_err("unable to allocate FB DMA memory\n"); + return -ENOMEM; + } + + memset(omap_lcdc.vram_virt, 0, omap_lcdc.vram_size); + + return 0; +} + +static void free_fbmem(void) +{ + dma_free_writecombine(omap_lcdc.fbdev->dev, omap_lcdc.vram_size, + omap_lcdc.vram_virt, omap_lcdc.vram_phys); +} + +static int setup_fbmem(int req_size) +{ + struct lcd_panel *panel = omap_lcdc.fbdev->panel; + struct omapfb_platform_data *conf; + int frame_size; + int r; + + conf = omap_lcdc.fbdev->dev->platform_data; + + if (conf->fbmem.fb_sram_size) { + pr_err("can't use FB SRAM in OMAP1\n"); + return -EINVAL; + } + + if (conf->fbmem.fb_sdram_size == 0) { + omap_lcdc.fbmem_allocated = 1; + if ((r = alloc_fbmem(req_size)) < 0) + return r; + return 0; + } + + frame_size = PAGE_ALIGN(panel->x_res * panel->bpp / 8 * panel->y_res); + + if (conf->fbmem.fb_sdram_size < frame_size) { + pr_err("invalid FB memory configuration\n"); + return -EINVAL; + } + + if (conf->fbmem.fb_sdram_size < req_size) { + pr_err("%d vram was requested, but only %u is available\n", + req_size, conf->fbmem.fb_sdram_size); + } + + omap_lcdc.vram_phys = conf->fbmem.fb_sdram_start; + omap_lcdc.vram_size = conf->fbmem.fb_sdram_size; + + if ((r = mmap_kern()) < 0) + return r; + + DBGPRINT(1, "vram at %08x size %08lx mapped to 0x%p\n", + omap_lcdc.vram_phys, omap_lcdc.vram_size, omap_lcdc.vram_virt); + + return 0; +} + +static void cleanup_fbmem(void) +{ + if (omap_lcdc.fbmem_allocated) + free_fbmem(); + else + unmap_kern(); } static int omap_lcdc_init(struct omapfb_device *fbdev, int ext_mode, @@ -562,14 +803,13 @@ static int omap_lcdc_init(struct omapfb_device *fbdev, int ext_mode, u32 l; int rate; struct clk *tc_ck; - struct lcd_panel *panel = fbdev->panel; - int frame_size; DBGENTER(1); omap_lcdc.irq_mask = 0; omap_lcdc.fbdev = fbdev; + omap_lcdc.ext_mode = ext_mode; pr_info(MODULE_NAME ": init\n"); @@ -612,27 +852,29 @@ static int omap_lcdc_init(struct omapfb_device *fbdev, int ext_mode, goto fail2; } - r = omap_request_lcd_dma(NULL, NULL); + r = omap_request_lcd_dma(lcdc_dma_handler, NULL); if (r) { pr_err("unable to get LCD DMA\n"); goto fail3; } - frame_size = panel->x_res * panel->bpp * panel->y_res / 8; - if (req_vram_size > frame_size) - frame_size = req_vram_size; - omap_lcdc.vram_size = PAGE_ALIGN(MAX_PALETTE_SIZE) + frame_size; - omap_lcdc.vram_virt = dma_alloc_writecombine(fbdev->dev, - omap_lcdc.vram_size, &omap_lcdc.vram_phys, GFP_KERNEL); + omap_set_lcd_dma_single_transfer(ext_mode); + omap_set_lcd_dma_ext_controller(ext_mode); + + if (!ext_mode) + if ((r = alloc_palette_ram()) < 0) + goto fail4; + + req_vram_size = 1024 * 1024; + if ((r = setup_fbmem(req_vram_size)) < 0) + goto fail5; - if (omap_lcdc.vram_virt == NULL) { - pr_err("unable to allocate fb DMA memory\n"); - r = -ENOMEM; - goto fail4; - } DBGLEAVE(1); return 0; +fail5: + if (!ext_mode) + free_palette_ram(); fail4: omap_free_lcd_dma(); fail3: @@ -648,46 +890,15 @@ fail0: static void omap_lcdc_cleanup(void) { - dma_free_writecombine(omap_lcdc.fbdev->dev, omap_lcdc.vram_size, - omap_lcdc.vram_virt, omap_lcdc.vram_phys); + if (!omap_lcdc.ext_mode) + free_palette_ram(); + cleanup_fbmem(); omap_free_lcd_dma(); free_irq(OMAP_LCDC_IRQ, omap_lcdc.fbdev); clk_disable(omap_lcdc.lcd_ck); clk_put(omap_lcdc.lcd_ck); } -static unsigned long omap_lcdc_get_caps(void) -{ - return 0; -} - -static int omap_lcdc_setcolreg(u_int regno, u16 red, u16 green, u16 blue, - u16 transp, int update_hw_pal) -{ - u16 *palette; - - if (omap_lcdc.color_mode != OMAPFB_COLOR_CLUT_8BPP || regno > 255) - return -EINVAL; - - palette = (u16 *)((u8*)omap_lcdc.vram_virt + - PAGE_ALIGN(MAX_PALETTE_SIZE) - omap_lcdc.palette_size); - - palette[regno] &= ~0x0fff; - palette[regno] |= ((red >> 12) << 8) | ((green >> 12) << 4 ) | - (blue >> 12); - - if (update_hw_pal) { - disable_controller(); - omap_stop_lcd_dma(); - load_palette(); - setup_lcd_dma(); - set_load_mode(OMAP_LCDC_LOAD_FRAME); - enable_controller(); - } - - return 0; -} - struct lcd_ctrl omap1_int_ctrl = { .name = "internal", .init = omap_lcdc_init, diff --git a/drivers/video/omap/lcdc.h b/drivers/video/omap/lcdc.h new file mode 100644 index 00000000000..adb731e5314 --- /dev/null +++ b/drivers/video/omap/lcdc.h @@ -0,0 +1,7 @@ +#ifndef LCDC_H +#define LCDC_H + +int omap_lcdc_set_dma_callback(void (*callback)(void *data), void *data); +void omap_lcdc_free_dma_callback(void); + +#endif diff --git a/drivers/video/omap/omapfb_main.c b/drivers/video/omap/omapfb_main.c index fd4b5c7b8ed..fac072188eb 100644 --- a/drivers/video/omap/omapfb_main.c +++ b/drivers/video/omap/omapfb_main.c @@ -80,64 +80,20 @@ static struct caps_table_struct { * LCD panel * --------------------------------------------------------------------------- */ -extern struct lcd_panel h4_panel; -extern struct lcd_panel h3_panel; -extern struct lcd_panel h2_panel; -extern struct lcd_panel p2_panel; -extern struct lcd_panel osk_panel; -extern struct lcd_panel palmte_panel; -extern struct lcd_panel innovator1610_panel; -extern struct lcd_panel innovator1510_panel; -extern struct lcd_panel lph8923_panel; -extern struct lcd_panel apollon_panel; - -static struct lcd_panel *panels[] = { -#ifdef CONFIG_MACH_OMAP_H2 - &h2_panel, -#endif -#ifdef CONFIG_MACH_OMAP_H3 - &h3_panel, -#endif -#ifdef CONFIG_MACH_OMAP_H4 - &h4_panel, -#endif -#ifdef CONFIG_MACH_OMAP_PERSEUS2 - &p2_panel, -#endif -#ifdef CONFIG_MACH_OMAP_OSK - &osk_panel, -#endif -#ifdef CONFIG_MACH_OMAP_PALMTE - &palmte_panel, -#endif - -#ifdef CONFIG_MACH_OMAP_INNOVATOR - -#ifdef CONFIG_ARCH_OMAP15XX - &innovator1510_panel, -#endif -#ifdef CONFIG_ARCH_OMAP16XX - &innovator1610_panel, -#endif - -#endif -#ifdef CONFIG_MACH_OMAP_APOLLON - &apollon_panel, -#endif -}; - extern struct lcd_ctrl omap1_int_ctrl; extern struct lcd_ctrl omap2_int_ctrl; extern struct lcd_ctrl hwa742_ctrl; extern struct lcd_ctrl blizzard_ctrl; static struct lcd_ctrl *ctrls[] = { -#ifdef CONFIG_FB_OMAP_LCDC_INTERNAL #ifdef CONFIG_ARCH_OMAP1 &omap1_int_ctrl, #else &omap2_int_ctrl, #endif + +#ifdef CONFIG_FB_OMAP_LCDC_HWA742 + &hwa742_ctrl, #endif }; @@ -192,7 +148,6 @@ static int ctrl_init(struct omapfb_device *fbdev) fbdev->ctrl->get_vram_layout(&fbdev->vram_size, &fbdev->vram_virt_base, &fbdev->vram_phys_base); - memset((void *)fbdev->vram_virt_base, 0, fbdev->vram_size); DBGPRINT(1, "vram_phys %08x vram_virt %p vram_size=%lu\n", fbdev->vram_phys_base, fbdev->vram_virt_base, @@ -339,12 +294,24 @@ static int omapfb_setcmap(struct fb_cmap *cmap, struct fb_info *info) return 0; } +static int omapfb_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + struct omapfb_device *fbdev = info->par; + int r; + + omapfb_rqueue_lock(fbdev); + r = fbdev->ctrl->mmap(vma); + omapfb_rqueue_unlock(fbdev); + + return r; +} static void omapfb_update_full_screen(struct omapfb_device *fbdev); static int omapfb_blank(int blank, struct fb_info *fbi) { struct omapfb_device *fbdev = (struct omapfb_device *)fbi->par; + int do_update = 0; int r = 0; DBGENTER(1); @@ -359,7 +326,7 @@ static int omapfb_blank(int blank, struct fb_info *fbi) fbdev->state = OMAPFB_ACTIVE; if (fbdev->ctrl->get_update_mode() == OMAPFB_MANUAL_UPDATE) - omapfb_update_full_screen(fbdev); + do_update = 1; } break; case VESA_POWERDOWN: @@ -375,6 +342,9 @@ static int omapfb_blank(int blank, struct fb_info *fbi) } omapfb_rqueue_unlock(fbdev); + if (do_update) + omapfb_update_full_screen(fbdev); + DBGLEAVE(1); return r; } @@ -763,8 +733,6 @@ static int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, } p; int r = 0; - DBGENTER(2); - BUG_ON(!ops); DBGPRINT(2, "cmd=%010x\n", cmd); switch (cmd) @@ -792,6 +760,15 @@ static int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, (enum omapfb_update_mode __user *)arg)) r = -EFAULT; break; + case OMAPFB_UPDATE_WINDOW_OLD: + if (copy_from_user(&p.update_window, (void __user *)arg, + sizeof(struct omapfb_update_window_old))) + r = -EFAULT; + else { + p.update_window.format = 0; + r = omapfb_update_win(fbdev, &p.update_window); + } + break; case OMAPFB_UPDATE_WINDOW: if (copy_from_user(&p.update_window, (void __user *)arg, sizeof(p.update_window))) @@ -1134,45 +1111,21 @@ static void omapfb_free_resources(struct omapfb_device *fbdev, int state) } } -static int omapfb_find_panel(struct omapfb_device *fbdev) -{ - const struct omap_lcd_config *conf; - char name[17]; - int i; - - conf = (struct omap_lcd_config *)fbdev->dev->platform_data; - fbdev->panel = NULL; - if (conf == NULL) - return -1; - - strncpy(name, conf->panel_name, sizeof(name) - 1); - name[sizeof(name) - 1] = 0; - for (i = 0; i < ARRAY_SIZE(panels); i++) { - if (strcmp(panels[i]->name, name) == 0) { - fbdev->panel = panels[i]; - break; - } - } - - if (fbdev->panel == NULL) - return -1; - - return 0; -} - static int omapfb_find_ctrl(struct omapfb_device *fbdev) { - struct omap_lcd_config *conf; + struct omapfb_platform_data *conf; char name[17]; int i; - conf = (struct omap_lcd_config *)fbdev->dev->platform_data; + conf = (struct omapfb_platform_data *)fbdev->dev->platform_data; fbdev->ctrl = NULL; - if (conf == NULL) + if (conf == NULL) { + DBGPRINT(1, "omap_lcd_config not found\n"); return -1; + } - strncpy(name, conf->ctrl_name, sizeof(name) - 1); + strncpy(name, conf->lcd.ctrl_name, sizeof(name) - 1); name[sizeof(name) - 1] = '\0'; if (strcmp(name, "internal") == 0) { @@ -1181,14 +1134,17 @@ static int omapfb_find_ctrl(struct omapfb_device *fbdev) } for (i = 0; i < ARRAY_SIZE(ctrls); i++) { + DBGPRINT(1, "ctrl %s\n", ctrls[i]->name); if (strcmp(ctrls[i]->name, name) == 0) { fbdev->ctrl = ctrls[i]; break; } } - if (fbdev->ctrl == NULL) + if (fbdev->ctrl == NULL) { + DBGPRINT(1, "ctrl %s not supported\n", name); return -1; + } return 0; } @@ -1218,13 +1174,12 @@ static void check_required_callbacks(struct omapfb_device *fbdev) * start LCD frame transfer * 7. register system fb_info structure */ -static int omapfb_probe(struct platform_device *pdev) +static int omapfb_do_probe(struct platform_device *pdev, struct lcd_panel *panel) { struct omapfb_device *fbdev = NULL; struct fb_info *fbi; int init_state; unsigned long phz, hhz, vhz; - struct lcd_panel *panel; int r = 0; DBGENTER(1); @@ -1248,14 +1203,13 @@ static int omapfb_probe(struct platform_device *pdev) fbdev = (struct omapfb_device *)fbi->par; fbdev->fb_info = fbi; fbdev->dev = &pdev->dev; + fbdev->panel = panel; platform_set_drvdata(pdev, fbdev); init_MUTEX(&fbdev->rqueue_sema); #ifdef CONFIG_ARCH_OMAP1 -#ifdef CONFIG_FB_OMAP_LCDC_INTERNAL fbdev->int_ctrl = &omap1_int_ctrl; -#endif #ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL fbdev->ext_if = &sossi_extif; #endif @@ -1271,15 +1225,6 @@ static int omapfb_probe(struct platform_device *pdev) goto cleanup; } - if (omapfb_find_panel(fbdev) < 0) { - pr_err("LCD panel not found, board not supported\n"); - r = -ENODEV; - goto cleanup; - } - - check_required_callbacks(fbdev); - - pr_info(MODULE_NAME ": configured for panel %s\n", fbdev->panel->name); r = fbdev->panel->init(fbdev); @@ -1292,6 +1237,14 @@ static int omapfb_probe(struct platform_device *pdev) goto cleanup; init_state++; + /* We depend on doing this after ctrl_init, since it can redefine + * member functions. + */ + if (fbdev->ctrl->mmap) + omapfb_ops.fb_mmap = omapfb_mmap; + + check_required_callbacks(fbdev); + r = fbinfo_init(fbdev); if (r) goto cleanup; @@ -1308,7 +1261,8 @@ static int omapfb_probe(struct platform_device *pdev) goto cleanup; } - omapfb_enable_plane(fbdev, 0, 1); + if (!manual_update) + omapfb_enable_plane(fbdev, OMAPFB_PLANE_GFX, 1); omapfb_set_update_mode(fbdev, manual_update ? OMAPFB_MANUAL_UPDATE : OMAPFB_AUTO_UPDATE); @@ -1352,6 +1306,30 @@ cleanup: return r; } +static struct platform_device *fbdev_pdev; +static struct lcd_panel *fbdev_panel; + +static int omapfb_probe(struct platform_device *pdev) +{ + BUG_ON(fbdev_pdev != NULL); + + DBGENTER(1); + fbdev_pdev = pdev; + if (fbdev_panel != NULL) + omapfb_do_probe(fbdev_pdev, fbdev_panel); + return 0; +} + +void omapfb_register_panel(struct lcd_panel *panel) +{ + BUG_ON(fbdev_panel != NULL); + + DBGENTER(1); + fbdev_panel = panel; + if (fbdev_pdev != NULL) + omapfb_do_probe(fbdev_pdev, fbdev_panel); +} + /* Called when the device is being detached from the driver */ static int omapfb_remove(struct platform_device *pdev) { diff --git a/drivers/video/omap/rfbi.c b/drivers/video/omap/rfbi.c index d4e959acd68..82415f50a69 100644 --- a/drivers/video/omap/rfbi.c +++ b/drivers/video/omap/rfbi.c @@ -35,6 +35,10 @@ #include "dispc.h" +/* #define OMAPFB_DBG 1 */ + +#include "debug.h" + #define MODULE_NAME "omapfb-rfbi" #define pr_err(fmt, args...) printk(KERN_ERR MODULE_NAME ": " fmt, ## args) @@ -68,8 +72,11 @@ static struct { void (*lcdc_callback)(void *data); void *lcdc_callback_data; unsigned long l4_khz; + int bits_per_cycle; } rfbi; +struct lcd_ctrl_extif rfbi_extif; + static inline void rfbi_write_reg(int idx, u32 val) { __raw_writel(val, rfbi.base + idx); @@ -80,25 +87,18 @@ static inline u32 rfbi_read_reg(int idx) return __raw_readl(rfbi.base + idx); } -static int ns_to_l4_ticks(int time) -{ - unsigned long tick_ps; - int ret; - - /* Calculate in picosecs to yield more exact results */ - tick_ps = 1000000000 / (rfbi.l4_khz); - - ret = (time * 1000 + tick_ps - 1) / tick_ps; - - return ret * 2; -} - #ifdef OMAPFB_DBG -static void print_timings(void) +static void rfbi_print_timings(void) { u32 l; + u32 time; - DBGPRINT(1, "Tick time %lu ps\n", 1000000000 / rfbi.l4_khz); + l = rfbi_read_reg(RFBI_CONFIG0); + time = 1000000000 / rfbi.l4_khz; + if (l & (1 << 4)) + time *= 2; + + DBGPRINT(1, "Tick time %u ps\n", time); l = rfbi_read_reg(RFBI_ONOFF_TIME0); DBGPRINT(1, "CSONTIME %d, CSOFFTIME %d, WEONTIME %d, WEOFFTIME %d, " "REONTIME %d, REOFFTIME %d\n", @@ -109,55 +109,181 @@ static void print_timings(void) "ACCESSTIME %d\n", (l & 0x3f), (l >> 6) & 0x3f, (l >> 12) & 0x3f, (l >> 22) & 0x3f); } +#else +static void rfbi_print_timings(void) {} #endif static void rfbi_set_timings(const struct extif_timings *t) { u32 l; - int on, off; - - on = ns_to_l4_ticks(t->cs_on_time) & 0x0f; - l = on; - off = ns_to_l4_ticks(t->cs_off_time) & 0x3f; - if (off <= on) - off = on + 2; - l |= off << 4; - - on = ns_to_l4_ticks(t->we_on_time) & 0x0f; - l |= on << 10; - off = ns_to_l4_ticks(t->we_off_time) & 0x3f; - if (off <= on) - off = on + 2; - l |= off << 14; - - l |= (ns_to_l4_ticks(t->re_on_time) & 0x0f) << 20; - l |= (ns_to_l4_ticks(t->re_off_time) & 0x3f) << 24; - rfbi_write_reg(RFBI_ONOFF_TIME0, l); - - l = ns_to_l4_ticks(t->we_cycle_time) & 0x3f; - l |= (ns_to_l4_ticks(t->re_cycle_time) & 0x3f) << 6; - l |= (ns_to_l4_ticks(t->cs_pulse_width) & 0x3f) << 12; - l |= (ns_to_l4_ticks(t->access_time) & 0x3f) << 22; - rfbi_write_reg(RFBI_CYCLE_TIME0, l); + + BUG_ON(!t->converted); + + rfbi_write_reg(RFBI_ONOFF_TIME0, t->tim[0]); + rfbi_write_reg(RFBI_CYCLE_TIME0, t->tim[1]); + + l = rfbi_read_reg(RFBI_CONFIG0); + l &= ~(1 << 4); + l |= (t->tim[2] ? 1 : 0) << 4; + rfbi_write_reg(RFBI_CONFIG0, l); + + rfbi_print_timings(); +} + +static void rfbi_get_clk_info(u32 *clk_period, u32 *max_clk_div) +{ + *clk_period = 1000000000 / rfbi.l4_khz; + *max_clk_div = 2; +} + +static int ps_to_rfbi_ticks(int time, int div) +{ + unsigned long tick_ps; + int ret; + + /* Calculate in picosecs to yield more exact results */ + tick_ps = 1000000000 / (rfbi.l4_khz) * div; + + ret = (time + tick_ps - 1) / tick_ps; + + return ret; } -static void rfbi_write_command(u32 cmd) +static int rfbi_convert_timings(struct extif_timings *t) { - rfbi_write_reg(RFBI_CMD, cmd); + u32 l; + int reon, reoff, weon, weoff, cson, csoff, cs_pulse; + int actim, recyc, wecyc; + int div = t->clk_div; + + if (div <= 0 || div > 2) + return -1; + + /* Make sure that after conversion it still holds that: + * weoff > weon, reoff > reon, recyc >= reoff, wecyc >= weoff, + * csoff > cson, csoff >= max(weoff, reoff), actim > reon + */ + weon = ps_to_rfbi_ticks(t->we_on_time, div); + weoff = ps_to_rfbi_ticks(t->we_off_time, div); + if (weoff <= weon) + weoff = weon + 1; + if (weon > 0x0f) + return -1; + if (weoff > 0x3f) + return -1; + + reon = ps_to_rfbi_ticks(t->re_on_time, div); + reoff = ps_to_rfbi_ticks(t->re_off_time, div); + if (reoff <= reon) + reoff = reon + 1; + if (reon > 0x0f) + return -1; + if (reoff > 0x3f) + return -1; + + cson = ps_to_rfbi_ticks(t->cs_on_time, div); + csoff = ps_to_rfbi_ticks(t->cs_off_time, div); + if (csoff <= cson) + csoff = cson + 1; + if (csoff < max(weoff, reoff)) + csoff = max(weoff, reoff); + if (cson > 0x0f) + return -1; + if (csoff > 0x3f) + return -1; + + l = cson; + l |= csoff << 4; + l |= weon << 10; + l |= weoff << 14; + l |= reon << 20; + l |= reoff << 24; + + t->tim[0] = l; + + actim = ps_to_rfbi_ticks(t->access_time, div); + if (actim <= reon) + actim = reon + 1; + if (actim > 0x3f) + return -1; + + wecyc = ps_to_rfbi_ticks(t->we_cycle_time, div); + if (wecyc < weoff) + wecyc = weoff; + if (wecyc > 0x3f) + return -1; + + recyc = ps_to_rfbi_ticks(t->re_cycle_time, div); + if (recyc < reoff) + recyc = reoff; + if (recyc > 0x3f) + return -1; + + cs_pulse = ps_to_rfbi_ticks(t->cs_pulse_width, div); + if (cs_pulse > 0x3f) + return -1; + + l = wecyc; + l |= recyc << 6; + l |= cs_pulse << 12; + l |= actim << 22; + + t->tim[1] = l; + + t->tim[2] = div - 1; + + t->converted = 1; + + return 0; } -static u32 rfbi_read_data(void) +static void rfbi_write_command(const void *buf, unsigned int len) { - u32 val; + if (rfbi.bits_per_cycle == 16) { + const u16 *w = buf; + BUG_ON(len & 1); + for (; len; len -= 2) + rfbi_write_reg(RFBI_CMD, *w++); + } else { + const u8 *b = buf; + BUG_ON(rfbi.bits_per_cycle != 8); + for (; len; len--) + rfbi_write_reg(RFBI_CMD, *b++); + } +} - rfbi_write_reg(RFBI_READ, 0); - val = rfbi_read_reg(RFBI_READ); - return val; +static void rfbi_read_data(void *buf, unsigned int len) +{ + if (rfbi.bits_per_cycle == 16) { + u16 *w = buf; + BUG_ON(len & ~1); + for (; len; len -= 2) { + rfbi_write_reg(RFBI_READ, 0); + *w++ = rfbi_read_reg(RFBI_READ); + } + } else { + u8 *b = buf; + BUG_ON(rfbi.bits_per_cycle != 8); + for (; len; len--) { + rfbi_write_reg(RFBI_READ, 0); + *b++ = rfbi_read_reg(RFBI_READ); + } + } } -static void rfbi_write_data(u32 val) +static void rfbi_write_data(const void *buf, unsigned int len) { - rfbi_write_reg(RFBI_PARAM, val); + if (rfbi.bits_per_cycle == 16) { + const u16 *w = buf; + BUG_ON(len & 1); + for (; len; len -= 2) + rfbi_write_reg(RFBI_PARAM, *w++); + } else { + const u8 *b = buf; + BUG_ON(rfbi.bits_per_cycle != 8); + for (; len; len--) + rfbi_write_reg(RFBI_PARAM, *b++); + } } static void rfbi_transfer_area(int width, int height, @@ -195,13 +321,32 @@ static void rfbi_dma_callback(void *data) rfbi.lcdc_callback(rfbi.lcdc_callback_data); } +static void rfbi_set_bits_per_cycle(int bpc) +{ + u32 l; + + l = rfbi_read_reg(RFBI_CONFIG0); + l &= ~(0x03 << 0); + switch (bpc) + { + case 8: + break; + case 16: + l |= 3; + break; + default: + BUG(); + } + rfbi_write_reg(RFBI_CONFIG0, l); + rfbi.bits_per_cycle = bpc; +} + static int rfbi_init(void) { u32 l; int r; struct clk *dss_ick; - memset(&rfbi, 0, sizeof(rfbi)); rfbi.base = io_p2v(RFBI_BASE); l = rfbi_read_reg(RFBI_REVISION); @@ -212,6 +357,7 @@ static int rfbi_init(void) pr_err("can't get dss_ick\n"); return PTR_ERR(dss_ick); } + rfbi.l4_khz = clk_get_rate(dss_ick) / 1000; clk_put(dss_ick); @@ -229,13 +375,10 @@ static int rfbi_init(void) l |= (0 << 9) | (1 << 20) | (1 << 21); rfbi_write_reg(RFBI_CONFIG0, l); - l = 0x10; - rfbi_write_reg(RFBI_DATA_CYCLE1_0, l); - rfbi_write_reg(RFBI_DATA_CYCLE2_0, l); - rfbi_write_reg(RFBI_DATA_CYCLE3_0, l); + rfbi_write_reg(RFBI_DATA_CYCLE1_0, 0x00000010); l = rfbi_read_reg(RFBI_CONTROL); - /* Select CS0 */ + /* Select CS0, clear bypass mode */ l = (0x01 << 2); rfbi_write_reg(RFBI_CONTROL, l); @@ -255,10 +398,14 @@ static void rfbi_cleanup(void) struct lcd_ctrl_extif rfbi_extif = { .init = rfbi_init, .cleanup = rfbi_cleanup, + .get_clk_info = rfbi_get_clk_info, + .set_bits_per_cycle = rfbi_set_bits_per_cycle, + .convert_timings = rfbi_convert_timings, .set_timings = rfbi_set_timings, .write_command = rfbi_write_command, .read_data = rfbi_read_data, .write_data = rfbi_write_data, .transfer_area = rfbi_transfer_area, + .max_transmit_size = (u32)~0, }; diff --git a/drivers/video/omap/sossi.c b/drivers/video/omap/sossi.c index ba26512a504..986f639f588 100644 --- a/drivers/video/omap/sossi.c +++ b/drivers/video/omap/sossi.c @@ -28,7 +28,14 @@ #include -#include "sossi.h" +#include +#include + +#include "lcdc.h" + +/* #define OMAPFB_DBG 1 */ + +#include "debug.h" #define MODULE_NAME "omapfb-sossi" @@ -48,38 +55,59 @@ #define DMA_LCD_CTRL 0xfffee3c4 #define DMA_LCD_LCH_CTRL 0xfffee3ea +#define RD_ACCESS 0 +#define WR_ACCESS 1 + +#define SOSSI_MAX_XMIT_BYTES (512 * 1024) + #define pr_err(fmt, args...) printk(KERN_ERR MODULE_NAME ": " fmt, ## args) -static int sossi_base = IO_ADDRESS(OMAP_SOSSI_BASE); +static struct sossi { + int base; + unsigned long dpll_khz; + int bus_pick_width; + void (*lcdc_callback)(void *data); + void *lcdc_callback_data; + /* timing for read and write access */ + int clk_div; + u8 clk_tw0[2]; + u8 clk_tw1[2]; + /* if last_access is the same as current we don't have to change + * the timings + */ + int last_access; +} sossi; + +struct lcd_ctrl_extif sossi_extif; static inline u32 sossi_read_reg(int reg) { - return readl(sossi_base + reg); + return readl(sossi.base + reg); } static inline u16 sossi_read_reg16(int reg) { - return readw(sossi_base + reg); + return readw(sossi.base + reg); } static inline u8 sossi_read_reg8(int reg) { - return readb(sossi_base + reg); + return readb(sossi.base + reg); } static inline void sossi_write_reg(int reg, u32 value) { - writel(value, sossi_base + reg); + writel(value, sossi.base + reg); } static inline void sossi_write_reg16(int reg, u16 value) { - writew(value, sossi_base + reg); + writew(value, sossi.base + reg); } static inline void sossi_write_reg8(int reg, u8 value) { - writeb(value, sossi_base + reg); + writeb(value, sossi.base + reg); } static void sossi_set_bits(int reg, u32 bits) @@ -96,14 +124,26 @@ static void sossi_clear_bits(int reg, u32 bits) #define CONF_SOSSI_RESET_R (1 << 23) #define CONF_MOD_SOSSI_CLK_EN_R (1 << 16) -static struct clk *dpll_clk; +static void sossi_dma_callback(void *data); -int sossi_init(void) +static int sossi_init(void) { u32 l, k; + struct clk *dpll_clk; + int r; + + sossi.base = IO_ADDRESS(OMAP_SOSSI_BASE); dpll_clk = clk_get(NULL, "ck_dpll1"); - BUG_ON(dpll_clk == NULL); + if (IS_ERR(dpll_clk)) { + pr_err("can't get dpll1 clock\n"); + return PTR_ERR(dpll_clk); + } + + sossi.dpll_khz = clk_get_rate(dpll_clk) / 1000; + clk_put(dpll_clk); + + sossi_extif.max_transmit_size = SOSSI_MAX_XMIT_BYTES; /* Reset and enable the SoSSI module */ l = omap_readl(MOD_CONF_CTRL_1); @@ -134,6 +174,12 @@ int sossi_init(void) pr_err("Invalid SoSSI sync pattern: %08x, %08x\n", l, k); return -ENODEV; } + + if ((r = omap_lcdc_set_dma_callback(sossi_dma_callback, NULL)) < 0) { + pr_err("can't get LCDC IRQ\n"); + return r; + } + l = sossi_read_reg(SOSSI_ID_REG); /* Component code */ l = sossi_read_reg(SOSSI_ID_REG); pr_info(KERN_INFO MODULE_NAME ": version %d.%d initialized\n", @@ -147,89 +193,201 @@ int sossi_init(void) return 0; } -static unsigned long get_sossi_clk_rate(int div) +static void sossi_cleanup(void) { - return (clk_get_rate(dpll_clk)) / div; + omap_lcdc_free_dma_callback(); } -static unsigned long get_sossi_clk_period(int div) +#define KHZ_TO_PS(x) (1000000000 / (x)) + +static void sossi_get_clk_info(u32 *clk_period, u32 *max_clk_div) { - /* In picoseconds */ - return 1000000000 / (get_sossi_clk_rate(div) / 1000); + *clk_period = KHZ_TO_PS(sossi.dpll_khz); + *max_clk_div = 8; } -static int ns_to_sossi_ticks(int time, int div) +static u32 ps_to_sossi_ticks(u32 ps, int div) { - unsigned long tick_ps; + u32 clk_period = KHZ_TO_PS(sossi.dpll_khz) * div; + return (clk_period + ps - 1) / clk_period; +} + +static int calc_rd_timings(struct extif_timings *t) +{ + u32 tw0, tw1; + int reon, reoff, recyc, actim; + int div = t->clk_div; + + /* Make sure that after conversion it still holds that: + * reoff > reon, recyc >= reoff, actim > reon + */ + reon = ps_to_sossi_ticks(t->re_on_time, div); + /* reon will be exactly one sossi tick */ + if (reon > 1) + return -1; + + reoff = ps_to_sossi_ticks(t->re_off_time, div); + + if (reoff <= reon) + reoff = reon + 1; - /* Calculate in picosecs to yield more exact results */ - tick_ps = get_sossi_clk_period(div); + tw0 = reoff - reon; + if (tw0 > 0x10) + return -1; + + recyc = ps_to_sossi_ticks(t->re_cycle_time, div); + if (recyc <= reoff) + recyc = reoff + 1; + + tw1 = recyc - reoff; + if (tw1 > 0x40) + return -1; + + actim = ps_to_sossi_ticks(t->access_time, div); + if (actim < reoff) + actim++; + /* access time (data hold time) will be exactly one sossi + * tick + */ + if (actim - reoff > 1) + return -1; - return (time * 1000 + tick_ps - 1) / tick_ps; + t->tim[0] = tw0 - 1; + t->tim[1] = tw1 - 1; + + return 0; } -static int set_timings(int div, int tw0, int tw1) +static int calc_wr_timings(struct extif_timings *t) { - u32 l; + u32 tw0, tw1; + int weon, weoff, wecyc; + int div = t->clk_div; + + /* Make sure that after conversion it still holds that: + * weoff > weon, wecyc >= weoff + */ + weon = ps_to_sossi_ticks(t->we_on_time, div); + /* weon will be exactly one sossi tick */ + if (weon > 1) + return -1; + + weoff = ps_to_sossi_ticks(t->we_off_time, div); + if (weoff <= weon) + weoff = weon + 1; + tw0 = weoff - weon; + if (tw0 > 0x10) + return -1; + + wecyc = ps_to_sossi_ticks(t->we_cycle_time, div); + if (wecyc <= weoff) + wecyc = weoff + 1; - if (tw1 * 1000 > 64 * get_sossi_clk_period(div)) + tw1 = wecyc - weoff; + if (tw1 > 0x40) return -1; - if (tw0 * 1000 > 16 * get_sossi_clk_period(div)) + + t->tim[2] = tw0 - 1; + t->tim[3] = tw1 - 1; + + return 0; +} + +static int sossi_convert_timings(struct extif_timings *t) +{ + int r = 0; + int div = t->clk_div; + + t->converted = 0; + + if (div <= 0 || div > 8) return -1; + /* no CS on SOSSI, so ignore cson, csoff, cs_pulsewidth */ + if ((r = calc_rd_timings(t)) < 0) + return r; + + if ((r = calc_wr_timings(t)) < 0) + return r; + + t->tim[4] = div - 1; + + t->converted = 1; + + return 0; +} + +static void sossi_set_timings(const struct extif_timings *t) +{ + BUG_ON(!t->converted); + + sossi.clk_tw0[RD_ACCESS] = t->tim[0]; + sossi.clk_tw1[RD_ACCESS] = t->tim[1]; + + sossi.clk_tw0[WR_ACCESS] = t->tim[2]; + sossi.clk_tw1[WR_ACCESS] = t->tim[3]; + + sossi.clk_div = t->tim[4]; +} + +static void _set_timing(int div, int tw0, int tw1) +{ + u32 l; + + DBGPRINT(2, "Using TW0 = %d, TW1 = %d, div = %d\n", + tw0 + 1, tw1 + 1, div + 1); + l = omap_readl(MOD_CONF_CTRL_1); l &= ~(7 << 17); - l |= (div - 1) << 17; + l |= div << 17; omap_writel(l, MOD_CONF_CTRL_1); - tw0 = ns_to_sossi_ticks(tw0, div) - 1; - tw1 = ns_to_sossi_ticks(tw1, div) - 1; - if (tw0 < 0) - tw0 = 0; - if (tw1 < 0) - tw1 = 0; -#if 0 - printk("Using TW0 = %d, TW1 = %d, div = %d, period = %d ps\n", - tw0, tw1, div, get_sossi_clk_period(div)); -#endif l = sossi_read_reg(SOSSI_INIT1_REG); l &= ~((0x0f << 20) | (0x3f << 24)); - l |= ((tw0 & 0x0f) << 20) | ((tw1 & 0x3f) << 24); + l |= (tw0 << 20) | (tw1 << 24); sossi_write_reg(SOSSI_INIT1_REG, l); - - return 0; } -static struct sossi { - int bus_pick_width; -} sossi; - -void sossi_set_timings(int min_time, int min_tw0, int min_tw1) +static inline void set_timing(int access) { - int div; - - for (div = 1; div <= 8; div++) { - if (min_time * 1000 > get_sossi_clk_period(div)) - continue; - if (set_timings(div, min_tw0, min_tw1) == 0) - break; - } - if (div == 9) { - pr_err("DPLL frequency too high for SoSSI\n"); - BUG(); + if (access != sossi.last_access) { + sossi.last_access = access; + _set_timing(sossi.clk_div, + sossi.clk_tw0[access], sossi.clk_tw1[access]); } } -void sossi_set_xfer_params(int bus_pick_count, int bus_pick_width) +static void sossi_set_bits_per_cycle(int bpc) { u32 l; - + int bus_pick_count, bus_pick_width; + + DBGPRINT(2, "bits_per_cycle %d\n", bpc); + /* We set explicitly the the bus_pick_count as well, although + * with remapping/reordering disabled it will be calculated by HW + * as (32 / bus_pick_width). + */ + switch (bpc) { + case 8: + bus_pick_count = 4; + bus_pick_width = 8; + break; + case 16: + bus_pick_count = 2; + bus_pick_width = 16; + break; + default: + BUG(); + return; + } + l = sossi_read_reg(SOSSI_INIT3_REG); sossi.bus_pick_width = bus_pick_width; - l = ((bus_pick_count - 1) << 5) | ((bus_pick_width - 1) & 0x1f); + l &= ~0x3ff; + l |= ((bus_pick_count - 1) << 5) | ((bus_pick_width - 1) & 0x1f); sossi_write_reg(SOSSI_INIT3_REG, l); } -void sossi_start_transfer(void) +static void sossi_start_transfer(void) { /* WE */ sossi_clear_bits(SOSSI_INIT2_REG, 1 << 4); @@ -238,7 +396,7 @@ void sossi_start_transfer(void) /* FIXME: locking? */ } -void sossi_stop_transfer(void) +static void sossi_stop_transfer(void) { /* WE */ sossi_set_bits(SOSSI_INIT2_REG, 1 << 4); @@ -247,6 +405,12 @@ void sossi_stop_transfer(void) /* FIXME: locking? */ } +static void wait_end_of_write(void) +{ + /* Before reading we must check if some writings are going on */ + while (!(sossi_read_reg(SOSSI_INIT2_REG) & (1 << 3))); +} + static void send_data(const void *data, unsigned int len) { while (len >= 4) { @@ -268,65 +432,71 @@ static void send_data(const void *data, unsigned int len) static void set_cycles(unsigned int len) { - int nr_cycles = len / (sossi.bus_pick_width / 8); + unsigned long nr_cycles = len / (sossi.bus_pick_width / 8); + + BUG_ON((nr_cycles - 1) & ~0x3ffff); sossi_clear_bits(SOSSI_INIT1_REG, 0x3ffff); sossi_set_bits(SOSSI_INIT1_REG, (nr_cycles - 1) & 0x3ffff); } -void sossi_send_cmd(const void *data, unsigned int len) +static void sossi_write_command(const void *data, unsigned int len) { + set_timing(WR_ACCESS); + /* CMD#/DATA */ sossi_clear_bits(SOSSI_INIT1_REG, 1 << 18); set_cycles(len); + sossi_start_transfer(); send_data(data, len); + sossi_stop_transfer(); + wait_end_of_write(); } -void sossi_send_data(const void *data, unsigned int len) +static void sossi_write_data(const void *data, unsigned int len) { + set_timing(WR_ACCESS); + /* CMD#/DATA */ sossi_set_bits(SOSSI_INIT1_REG, 1 << 18); set_cycles(len); + sossi_start_transfer(); send_data(data, len); + sossi_stop_transfer(); + wait_end_of_write(); } -void sossi_prepare_dma_transfer(unsigned int count) +static void sossi_transfer_area(int width, int height, + void (callback)(void *data), void *data) { - sossi_set_bits(SOSSI_INIT1_REG, 1 << 18); - set_cycles(count); -} + BUG_ON(callback == NULL); -void sossi_send_data_const32(u32 data, unsigned int count) -{ + sossi.lcdc_callback = callback; + sossi.lcdc_callback_data = data; + + set_timing(WR_ACCESS); + /* CMD#/DATA */ sossi_set_bits(SOSSI_INIT1_REG, 1 << 18); - set_cycles(count * 4); - while (count > 0) { - sossi_write_reg(SOSSI_FIFO_REG, data); - count--; - } + set_cycles(width * height * sossi.bus_pick_width / 8); + + DBGPRINT(2, "SOSSI_INIT1_REG %08x\n", sossi_read_reg(SOSSI_INIT1_REG)); + + sossi_start_transfer(); + omap_enable_lcd_dma(); } -void sossi_set_tearing(int mode, int hs_counter, int detect_limit, - int vs_counter, int vs_detect_limit, int flags) +static void sossi_dma_callback(void *data) { - u32 l = 0; - - l |= vs_counter << 30; - if (flags & SOSSI_FLAG_HS_INVERTED) - l |= 1 << 29; - if (flags & SOSSI_FLAG_VS_INVERTED) - l |= 1 << 28; - l |= mode << 26; - l |= hs_counter << 15; - l |= vs_detect_limit << 3; - l |= detect_limit; - sossi_write_reg(SOSSI_TEARING_REG, l); + omap_stop_lcd_dma(); + sossi_stop_transfer(); + sossi.lcdc_callback(sossi.lcdc_callback_data); } -void sossi_read_data(void *data, unsigned int len) +static void sossi_read_data(void *data, unsigned int len) { - /* Before reading we must check if some writings are going on */ - while (!(sossi_read_reg(SOSSI_INIT2_REG) & (1 << 3))); + set_timing(RD_ACCESS); + /* CMD#/DATA */ sossi_set_bits(SOSSI_INIT1_REG, 1 << 18); set_cycles(len); + sossi_start_transfer(); while (len >= 4) { *(u32 *) data = sossi_read_reg(SOSSI_FIFO_REG); len -= 4; @@ -342,4 +512,18 @@ void sossi_read_data(void *data, unsigned int len) len--; data++; } + sossi_stop_transfer(); } + +struct lcd_ctrl_extif sossi_extif = { + .init = sossi_init, + .cleanup = sossi_cleanup, + .get_clk_info = sossi_get_clk_info, + .convert_timings = sossi_convert_timings, + .set_timings = sossi_set_timings, + .set_bits_per_cycle = sossi_set_bits_per_cycle, + .write_command = sossi_write_command, + .read_data = sossi_read_data, + .write_data = sossi_write_data, + .transfer_area = sossi_transfer_area, +}; diff --git a/drivers/video/omap/sossi.h b/drivers/video/omap/sossi.h deleted file mode 100644 index b71a4b9c63b..00000000000 --- a/drivers/video/omap/sossi.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef DRIVERS_VIDEO_OMAP_SOSSI_H -#define DRIVERS_VIDEO_OMAP_SOSSI_H - -#define SOSSI_FLAG_HS_INVERTED 0x01 -#define SOSSI_FLAG_VS_INVERTED 0x02 - -extern int sossi_init(void); -extern void sossi_set_xfer_params(int bus_pick_count, int bus_pick_width); -extern void sossi_set_timings(int tick_ns, int tw0_ns, int tw1_ns); -extern void sossi_start_transfer(void); -extern void sossi_stop_transfer(void); -extern void sossi_send_cmd(const void *data, unsigned int len); -extern void sossi_send_data(const void *data, unsigned int len); -extern void sossi_send_data_const32(u32 data, unsigned int count); -extern void sossi_prepare_dma_transfer(unsigned int count); -extern void sossi_read_data(void *data, unsigned int len); -extern void sossi_set_tearing(int mode, int hs_counter, int detect_limit, - int vs_counter, int vs_detect_limit, int flags); - -#endif diff --git a/include/asm-arm/arch-omap/board.h b/include/asm-arm/arch-omap/board.h index 684e871050e..6d6240a4681 100644 --- a/include/asm-arm/arch-omap/board.h +++ b/include/asm-arm/arch-omap/board.h @@ -21,6 +21,7 @@ #define OMAP_TAG_LCD 0x4f05 #define OMAP_TAG_GPIO_SWITCH 0x4f06 #define OMAP_TAG_UART 0x4f07 +#define OMAP_TAG_FBMEM 0x4f08 #define OMAP_TAG_STI_CONSOLE 0x4f09 #define OMAP_TAG_BOOT_REASON 0x4f80 @@ -94,6 +95,13 @@ struct omap_lcd_config { char ctrl_name[16]; }; +struct omap_fbmem_config { + u32 fb_sram_start; + u32 fb_sram_size; + u32 fb_sdram_start; + u32 fb_sdram_size; +}; + /* Cover: * high -> closed * low -> open diff --git a/include/asm-arm/arch-omap/lcd_lph8923.h b/include/asm-arm/arch-omap/lcd_lph8923.h new file mode 100644 index 00000000000..004e67e22ca --- /dev/null +++ b/include/asm-arm/arch-omap/lcd_lph8923.h @@ -0,0 +1,14 @@ +#ifndef __LCD_LPH8923_H +#define __LCD_LPH8923_H + +enum lcd_lph8923_test_num { + LCD_LPH8923_TEST_RGB_LINES, +}; + +enum lcd_lph8923_test_result { + LCD_LPH8923_TEST_SUCCESS, + LCD_LPH8923_TEST_INVALID, + LCD_LPH8923_TEST_FAILED, +}; + +#endif diff --git a/include/asm-arm/arch-omap/omapfb.h b/include/asm-arm/arch-omap/omapfb.h index 4ba2622cc14..ffac7c0f827 100644 --- a/include/asm-arm/arch-omap/omapfb.h +++ b/include/asm-arm/arch-omap/omapfb.h @@ -35,6 +35,7 @@ #define OMAPFB_SYNC_GFX OMAP_IO(37) #define OMAPFB_VSYNC OMAP_IO(38) #define OMAPFB_SET_UPDATE_MODE OMAP_IOW(40, enum omapfb_update_mode) +#define OMAPFB_UPDATE_WINDOW_OLD OMAP_IOW(41, struct omapfb_update_window_old) #define OMAPFB_GET_CAPS OMAP_IOR(42, unsigned long) #define OMAPFB_GET_UPDATE_MODE OMAP_IOW(43, enum omapfb_update_mode) #define OMAPFB_LCD_TEST OMAP_IOW(45, int) @@ -71,6 +72,11 @@ struct omapfb_update_window { u32 format; }; +struct omapfb_update_window_old { + u32 x, y; + u32 width, height; +}; + enum omapfb_plane { OMAPFB_PLANE_GFX = 0, OMAPFB_PLANE_VID1, @@ -121,6 +127,8 @@ enum omapfb_update_mode { #include #include +#include + #define OMAP_LCDC_INV_VSYNC 0x0001 #define OMAP_LCDC_INV_HSYNC 0x0002 #define OMAP_LCDC_INV_PIX_CLOCK 0x0004 @@ -184,17 +192,27 @@ struct extif_timings { int re_cycle_time; int cs_pulse_width; int access_time; + + int clk_div; + + u32 tim[5]; /* set by extif->convert_timings */ + + int converted; }; struct lcd_ctrl_extif { int (*init) (void); void (*cleanup) (void); + void (*get_clk_info) (u32 *clk_period, u32 *max_clk_div); + int (*convert_timings) (struct extif_timings *timings); void (*set_timings) (const struct extif_timings *timings); - void (*write_command) (u32 cmd); - u32 (*read_data) (void); - void (*write_data) (u32 data); + void (*set_bits_per_cycle)(int bpc); + void (*write_command) (const void *buf, unsigned int len); + void (*read_data) (void *buf, unsigned int len); + void (*write_data) (const void *buf, unsigned int len); void (*transfer_area) (int width, int height, void (callback)(void * data), void *data); + unsigned long max_transmit_size; }; struct lcd_ctrl { @@ -207,6 +225,7 @@ struct lcd_ctrl { void (*get_vram_layout)(unsigned long *size, void **virt_base, dma_addr_t *phys_base); + int (*mmap) (struct vm_area_struct *vma); unsigned long (*get_caps) (void); int (*set_update_mode)(enum omapfb_update_mode mode); enum omapfb_update_mode (*get_update_mode)(void); @@ -261,12 +280,10 @@ struct omapfb_device { struct device *dev; }; -extern struct lcd_panel h3_panel; -extern struct lcd_panel h2_panel; -extern struct lcd_panel p2_panel; -extern struct lcd_panel osk_panel; -extern struct lcd_panel innovator1610_panel; -extern struct lcd_panel innovator1510_panel; +struct omapfb_platform_data { + struct omap_lcd_config lcd; + struct omap_fbmem_config fbmem; +}; #ifdef CONFIG_ARCH_OMAP1 extern struct lcd_ctrl omap1_lcd_ctrl; @@ -274,8 +291,12 @@ extern struct lcd_ctrl omap1_lcd_ctrl; extern struct lcd_ctrl omap2_disp_ctrl; #endif +extern void omapfb_register_panel(struct lcd_panel *panel); extern void omapfb_write_first_pixel(struct omapfb_device *fbdev, u16 pixval); +/* in arch/arm/plat-omap/devices.c */ +extern void omapfb_reserve_mem(void); + #endif /* __KERNEL__ */ #endif /* __OMAPFB_H */ diff --git a/include/asm-arm/arch-omap/sram.h b/include/asm-arm/arch-omap/sram.h index e72ccbf0fe0..6fc0dd57b7c 100644 --- a/include/asm-arm/arch-omap/sram.h +++ b/include/asm-arm/arch-omap/sram.h @@ -20,6 +20,8 @@ extern void omap2_sram_reprogram_sdrc(u32 perf_level, u32 dll_val, u32 mem_type); extern u32 omap2_set_prcm(u32 dpll_ctrl_val, u32 sdrc_rfr_val, int bypass); +extern unsigned long omap_fb_sram_start; +extern unsigned long omap_fb_sram_size; /* Do not use these */ extern void sram_reprogram_clock(u32 ckctl, u32 dpllctl);