diff options
Diffstat (limited to 'drivers/video/omap')
25 files changed, 2302 insertions, 125 deletions
diff --git a/drivers/video/omap/Kconfig b/drivers/video/omap/Kconfig index 44408850e2e..551e3e9c4cb 100644 --- a/drivers/video/omap/Kconfig +++ b/drivers/video/omap/Kconfig @@ -7,6 +7,69 @@ config FB_OMAP help Frame buffer driver for OMAP based boards. +config FB_OMAP_LCD_VGA + bool "Use LCD in VGA mode" + depends on MACH_OMAP_3430SDP || MACH_OMAP_LDP + +choice + depends on FB_OMAP && MACH_OVERO + prompt "Screen resolution" + default FB_OMAP_079M3R + help + Selected desired screen resolution + +config FB_OMAP_031M3R + boolean "640 x 480 @ 60 Hz Reduced blanking" + +config FB_OMAP_048M3R + boolean "800 x 600 @ 60 Hz Reduced blanking" + +config FB_OMAP_079M3R + boolean "1024 x 768 @ 60 Hz Reduced blanking" + +config FB_OMAP_092M9R + boolean "1280 x 720 @ 60 Hz Reduced blanking" + +endchoice + +config FB_OMAP_LCDC_EXTERNAL + bool "External LCD controller support" + depends on FB_OMAP + help + 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_LCDC_BLIZZARD + bool "Epson Blizzard 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 Blizzard LCD controller. + +config FB_OMAP_MANUAL_UPDATE + bool "Default to manual update mode" + depends on FB_OMAP && FB_OMAP_LCDC_EXTERNAL + help + Say Y here, if your user-space applications are capable of + notifying the frame buffer driver when a change has occured in + 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_MIPID + bool "MIPI DBI-C/DCS compatible LCD support" + depends on FB_OMAP && SPI_MASTER + help + Say Y here if you want to have support for LCDs compatible with + the Mobile Industry Processor Interface DBI-C/DCS + specification. (Supported LCDs: Philips LPH8923, Sharp LS041Y3) + config FB_OMAP_BOOTLOADER_INIT bool "Check bootloader initialization" depends on FB_OMAP @@ -36,23 +99,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. -config FB_OMAP_LCDC_EXTERNAL - bool "External LCD controller support" - depends on FB_OMAP - help - 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_LCDC_BLIZZARD - bool "Epson Blizzard 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 Blizzard LCD controller. diff --git a/drivers/video/omap/Makefile b/drivers/video/omap/Makefile index ed13889c116..b63b198d1f0 100644 --- a/drivers/video/omap/Makefile +++ b/drivers/video/omap/Makefile @@ -8,6 +8,7 @@ objs-yy := omapfb_main.o objs-y$(CONFIG_ARCH_OMAP1) += lcdc.o objs-y$(CONFIG_ARCH_OMAP2) += dispc.o +objs-y$(CONFIG_ARCH_OMAP3) += dispc.o objs-$(CONFIG_ARCH_OMAP1)$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += sossi.o objs-$(CONFIG_ARCH_OMAP2)$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += rfbi.o @@ -15,6 +16,7 @@ objs-$(CONFIG_ARCH_OMAP2)$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += rfbi.o objs-y$(CONFIG_FB_OMAP_LCDC_HWA742) += hwa742.o objs-y$(CONFIG_FB_OMAP_LCDC_BLIZZARD) += blizzard.o +objs-y$(CONFIG_MACH_AMS_DELTA) += lcd_ams_delta.o objs-y$(CONFIG_MACH_OMAP_H4) += lcd_h4.o objs-y$(CONFIG_MACH_OMAP_H3) += lcd_h3.o objs-y$(CONFIG_MACH_OMAP_PALMTE) += lcd_palmte.o @@ -24,5 +26,15 @@ objs-$(CONFIG_ARCH_OMAP16XX)$(CONFIG_MACH_OMAP_INNOVATOR) += lcd_inn1610.o objs-$(CONFIG_ARCH_OMAP15XX)$(CONFIG_MACH_OMAP_INNOVATOR) += lcd_inn1510.o objs-y$(CONFIG_MACH_OMAP_OSK) += lcd_osk.o +objs-y$(CONFIG_MACH_OMAP_APOLLON) += lcd_apollon.o +objs-y$(CONFIG_MACH_OMAP_2430SDP) += lcd_2430sdp.o +objs-y$(CONFIG_MACH_OMAP_3430SDP) += lcd_2430sdp.o +objs-y$(CONFIG_MACH_OMAP_LDP) += lcd_ldp.o +objs-y$(CONFIG_MACH_OMAP2EVM) += lcd_omap2evm.o +objs-y$(CONFIG_MACH_OMAP3EVM) += lcd_omap3evm.o +objs-y$(CONFIG_MACH_OMAP3_BEAGLE) += lcd_omap3beagle.o +objs-y$(CONFIG_FB_OMAP_LCD_MIPID) += lcd_mipid.o +objs-y$(CONFIG_MACH_OVERO) += lcd_overo.o + omapfb-objs := $(objs-yy) diff --git a/drivers/video/omap/blizzard.c b/drivers/video/omap/blizzard.c index 9dfcf39d336..d5e59556f9e 100644 --- a/drivers/video/omap/blizzard.c +++ b/drivers/video/omap/blizzard.c @@ -44,6 +44,7 @@ #define BLIZZARD_CLK_SRC 0x0e #define BLIZZARD_MEM_BANK0_ACTIVATE 0x10 #define BLIZZARD_MEM_BANK0_STATUS 0x14 +#define BLIZZARD_PANEL_CONFIGURATION 0x28 #define BLIZZARD_HDISP 0x2a #define BLIZZARD_HNDP 0x2c #define BLIZZARD_VDISP0 0x2e @@ -162,6 +163,10 @@ struct blizzard_struct { int vid_scaled; int last_color_mode; int zoom_on; + int zoom_area_gx1; + int zoom_area_gx2; + int zoom_area_gy1; + int zoom_area_gy2; int screen_width; int screen_height; unsigned te_connected:1; @@ -513,6 +518,13 @@ static int do_full_screen_update(struct blizzard_request *req) return REQ_PENDING; } +static int check_1d_intersect(int a1, int a2, int b1, int b2) +{ + if (a2 <= b1 || b2 <= a1) + return 0; + return 1; +} + /* Setup all planes with an overlapping area with the update window. */ static int do_partial_update(struct blizzard_request *req, int plane, int x, int y, int w, int h, @@ -525,6 +537,7 @@ static int do_partial_update(struct blizzard_request *req, int plane, int color_mode; int flags; int zoom_off; + int have_zoom_for_this_update = 0; /* Global coordinates, relative to pixel 0,0 of the LCD */ gx1 = x + blizzard.plane[plane].pos_x; @@ -544,10 +557,6 @@ static int do_partial_update(struct blizzard_request *req, int plane, gx2_out = gx1_out + w_out; gy2_out = gy1_out + h_out; } - zoom_off = blizzard.zoom_on && gx1 == 0 && gy1 == 0 && - w == blizzard.screen_width && h == blizzard.screen_height; - blizzard.zoom_on = (!zoom_off && blizzard.zoom_on) || - (w < w_out || h < h_out); for (i = 0; i < OMAPFB_PLANE_NUM; i++) { struct plane_info *p = &blizzard.plane[i]; @@ -653,8 +662,49 @@ static int do_partial_update(struct blizzard_request *req, int plane, else disable_tearsync(); + if ((gx2_out - gx1_out) != (gx2 - gx1) || + (gy2_out - gy1_out) != (gy2 - gy1)) + have_zoom_for_this_update = 1; + + /* 'background' type of screen update (as opposed to 'destructive') + can be used to disable scaling if scaling is active */ + zoom_off = blizzard.zoom_on && !have_zoom_for_this_update && + (gx1_out == 0) && (gx2_out == blizzard.screen_width) && + (gy1_out == 0) && (gy2_out == blizzard.screen_height) && + (gx1 == 0) && (gy1 == 0); + + if (blizzard.zoom_on && !have_zoom_for_this_update && !zoom_off && + check_1d_intersect(blizzard.zoom_area_gx1, blizzard.zoom_area_gx2, + gx1_out, gx2_out) && + check_1d_intersect(blizzard.zoom_area_gy1, blizzard.zoom_area_gy2, + gy1_out, gy2_out)) { + /* Previous screen update was using scaling, current update + * is not using it. Additionally, current screen update is + * going to overlap with the scaled area. Scaling needs to be + * disabled in order to avoid 'magnifying glass' effect. + * Dummy setup of background window can be used for this. + */ + set_window_regs(0, 0, blizzard.screen_width, + blizzard.screen_height, + 0, 0, blizzard.screen_width, + blizzard.screen_height, + BLIZZARD_COLOR_RGB565, 1, flags); + blizzard.zoom_on = 0; + } + + /* remember scaling settings if we have scaled update */ + if (have_zoom_for_this_update) { + blizzard.zoom_on = 1; + blizzard.zoom_area_gx1 = gx1_out; + blizzard.zoom_area_gx2 = gx2_out; + blizzard.zoom_area_gy1 = gy1_out; + blizzard.zoom_area_gy2 = gy2_out; + } + set_window_regs(gx1, gy1, gx2, gy2, gx1_out, gy1_out, gx2_out, gy2_out, color_mode, zoom_off, flags); + if (zoom_off) + blizzard.zoom_on = 0; blizzard.extif->set_bits_per_cycle(16); /* set_window_regs has left the register index at the right @@ -908,6 +958,35 @@ static int blizzard_set_scale(int plane, int orig_w, int orig_h, return 0; } +static int blizzard_set_rotate(int angle) +{ + u32 l; + + l = blizzard_read_reg(BLIZZARD_PANEL_CONFIGURATION); + l &= ~0x03; + + switch (angle) { + case 0: + l = l | 0x00; + break; + case 90: + l = l | 0x03; + break; + case 180: + l = l | 0x02; + break; + case 270: + l = l | 0x01; + break; + default: + return -EINVAL; + } + + blizzard_write_reg(BLIZZARD_PANEL_CONFIGURATION, l); + + return 0; +} + static int blizzard_enable_plane(int plane, int enable) { if (enable) @@ -1285,7 +1364,8 @@ static void blizzard_get_caps(int plane, struct omapfb_caps *caps) caps->ctrl |= OMAPFB_CAPS_MANUAL_UPDATE | OMAPFB_CAPS_WINDOW_PIXEL_DOUBLE | OMAPFB_CAPS_WINDOW_SCALE | - OMAPFB_CAPS_WINDOW_OVERLAY; + OMAPFB_CAPS_WINDOW_OVERLAY | + OMAPFB_CAPS_WINDOW_ROTATE; if (blizzard.te_connected) caps->ctrl |= OMAPFB_CAPS_TEARSYNC; caps->wnd_color |= (1 << OMAPFB_COLOR_RGB565) | @@ -1560,6 +1640,7 @@ struct lcd_ctrl blizzard_ctrl = { .setup_plane = blizzard_setup_plane, .set_scale = blizzard_set_scale, .enable_plane = blizzard_enable_plane, + .set_rotate = blizzard_set_rotate, .update_window = blizzard_update_window_async, .sync = blizzard_sync, .suspend = blizzard_suspend, diff --git a/drivers/video/omap/dispc.c b/drivers/video/omap/dispc.c index 148cbcc3960..80a11d078df 100644 --- a/drivers/video/omap/dispc.c +++ b/drivers/video/omap/dispc.c @@ -155,6 +155,8 @@ struct resmap { unsigned long *map; }; +#define MAX_IRQ_HANDLERS 4 + static struct { void __iomem *base; @@ -167,9 +169,11 @@ static struct { int ext_mode; - unsigned long enabled_irqs; - void (*irq_callback)(void *); - void *irq_callback_data; + struct { + u32 irq_mask; + void (*callback)(void *); + void *data; + } irq_handlers[MAX_IRQ_HANDLERS]; struct completion frame_done; int fir_hinc[OMAPFB_PLANE_NUM]; @@ -212,9 +216,9 @@ static void enable_rfbi_mode(int enable) dispc_write_reg(DISPC_CONTROL, l); /* Set bypass mode in RFBI module */ - l = __raw_readl(IO_ADDRESS(RFBI_CONTROL)); + l = __raw_readl(OMAP2_IO_ADDRESS(RFBI_CONTROL)); l |= enable ? 0 : (1 << 1); - __raw_writel(l, IO_ADDRESS(RFBI_CONTROL)); + __raw_writel(l, OMAP2_IO_ADDRESS(RFBI_CONTROL)); } static void set_lcd_data_lines(int data_lines) @@ -286,7 +290,7 @@ static void setup_plane_fifo(int plane, int ext_mode) BUG_ON(plane > 2); l = dispc_read_reg(fsz_reg[plane]); - l &= FLD_MASK(0, 9); + l &= FLD_MASK(0, 11); if (ext_mode) { low = l * 3 / 4; high = l; @@ -294,7 +298,7 @@ static void setup_plane_fifo(int plane, int ext_mode) low = l / 4; high = l * 3 / 4; } - MOD_REG_FLD(ftrs_reg[plane], FLD_MASK(16, 9) | FLD_MASK(0, 9), + MOD_REG_FLD(ftrs_reg[plane], FLD_MASK(16, 12) | FLD_MASK(0, 12), (high << 16) | low); } @@ -809,57 +813,74 @@ static void set_lcd_timings(void) panel->pixel_clock = fck / lck_div / pck_div / 1000; } -int omap_dispc_request_irq(void (*callback)(void *data), void *data) +static void recalc_irq_mask(void) { - int r = 0; + int i; + unsigned long irq_mask = DISPC_IRQ_MASK_ERROR; - BUG_ON(callback == NULL); + for (i = 0; i < MAX_IRQ_HANDLERS; i++) { + if (!dispc.irq_handlers[i].callback) + continue; - if (dispc.irq_callback) - r = -EBUSY; - else { - dispc.irq_callback = callback; - dispc.irq_callback_data = data; + irq_mask |= dispc.irq_handlers[i].irq_mask; } - return r; -} -EXPORT_SYMBOL(omap_dispc_request_irq); - -void omap_dispc_enable_irqs(int irq_mask) -{ enable_lcd_clocks(1); - dispc.enabled_irqs = irq_mask; - irq_mask |= DISPC_IRQ_MASK_ERROR; MOD_REG_FLD(DISPC_IRQENABLE, 0x7fff, irq_mask); enable_lcd_clocks(0); } -EXPORT_SYMBOL(omap_dispc_enable_irqs); -void omap_dispc_disable_irqs(int irq_mask) +int omap_dispc_request_irq(unsigned long irq_mask, void (*callback)(void *data), + void *data) { - enable_lcd_clocks(1); - dispc.enabled_irqs &= ~irq_mask; - irq_mask &= ~DISPC_IRQ_MASK_ERROR; - MOD_REG_FLD(DISPC_IRQENABLE, 0x7fff, irq_mask); - enable_lcd_clocks(0); + int i; + + BUG_ON(callback == NULL); + + for (i = 0; i < MAX_IRQ_HANDLERS; i++) { + if (dispc.irq_handlers[i].callback) + continue; + + dispc.irq_handlers[i].irq_mask = irq_mask; + dispc.irq_handlers[i].callback = callback; + dispc.irq_handlers[i].data = data; + recalc_irq_mask(); + + return 0; + } + + return -EBUSY; } -EXPORT_SYMBOL(omap_dispc_disable_irqs); +EXPORT_SYMBOL(omap_dispc_request_irq); -void omap_dispc_free_irq(void) +void omap_dispc_free_irq(unsigned long irq_mask, void (*callback)(void *data), + void *data) { - enable_lcd_clocks(1); - omap_dispc_disable_irqs(DISPC_IRQ_MASK_ALL); - dispc.irq_callback = NULL; - dispc.irq_callback_data = NULL; - enable_lcd_clocks(0); + int i; + + for (i = 0; i < MAX_IRQ_HANDLERS; i++) { + if (dispc.irq_handlers[i].callback == callback && + dispc.irq_handlers[i].data == data) { + dispc.irq_handlers[i].irq_mask = 0; + dispc.irq_handlers[i].callback = NULL; + dispc.irq_handlers[i].data = NULL; + recalc_irq_mask(); + return; + } + } + + BUG(); } EXPORT_SYMBOL(omap_dispc_free_irq); static irqreturn_t omap_dispc_irq_handler(int irq, void *dev) { - u32 stat = dispc_read_reg(DISPC_IRQSTATUS); + u32 stat; + int i = 0; + + enable_lcd_clocks(1); + stat = dispc_read_reg(DISPC_IRQSTATUS); if (stat & DISPC_IRQ_FRAMEMASK) complete(&dispc.frame_done); @@ -870,11 +891,17 @@ static irqreturn_t omap_dispc_irq_handler(int irq, void *dev) } } - if ((stat & dispc.enabled_irqs) && dispc.irq_callback) - dispc.irq_callback(dispc.irq_callback_data); + for (i = 0; i < MAX_IRQ_HANDLERS; i++) { + if (unlikely(dispc.irq_handlers[i].callback && + (stat & dispc.irq_handlers[i].irq_mask))) + dispc.irq_handlers[i].callback( + dispc.irq_handlers[i].data); + } dispc_write_reg(DISPC_IRQSTATUS, stat); + enable_lcd_clocks(0); + return IRQ_HANDLED; } @@ -913,18 +940,13 @@ static void put_dss_clocks(void) static void enable_lcd_clocks(int enable) { - if (enable) + if (enable) { + clk_enable(dispc.dss_ick); clk_enable(dispc.dss1_fck); - else + } else { clk_disable(dispc.dss1_fck); -} - -static void enable_interface_clocks(int enable) -{ - if (enable) - clk_enable(dispc.dss_ick); - else clk_disable(dispc.dss_ick); + } } static void enable_digit_clocks(int enable) @@ -1365,7 +1387,6 @@ static int omap_dispc_init(struct omapfb_device *fbdev, int ext_mode, if ((r = get_dss_clocks()) < 0) goto fail0; - enable_interface_clocks(1); enable_lcd_clocks(1); #ifdef CONFIG_FB_OMAP_BOOTLOADER_INIT @@ -1396,10 +1417,10 @@ static int omap_dispc_init(struct omapfb_device *fbdev, int ext_mode, enable_digit_clocks(0); } - /* Enable smart idle and autoidle */ - l = dispc_read_reg(DISPC_CONTROL); + /* Enable smart standby/idle, autoidle and wakeup */ + l = dispc_read_reg(DISPC_SYSCONFIG); l &= ~((3 << 12) | (3 << 3)); - l |= (2 << 12) | (2 << 3) | (1 << 0); + l |= (2 << 12) | (2 << 3) | (1 << 2) | (1 << 0); dispc_write_reg(DISPC_SYSCONFIG, l); omap_writel(1 << 0, DSS_BASE + DSS_SYSCONFIG); @@ -1409,10 +1430,9 @@ static int omap_dispc_init(struct omapfb_device *fbdev, int ext_mode, dispc_write_reg(DISPC_CONFIG, l); l = dispc_read_reg(DISPC_IRQSTATUS); - dispc_write_reg(l, DISPC_IRQSTATUS); + dispc_write_reg(DISPC_IRQSTATUS, l); - /* Enable those that we handle always */ - omap_dispc_enable_irqs(DISPC_IRQ_FRAMEMASK); + recalc_irq_mask(); if ((r = request_irq(INT_24XX_DSS_IRQ, omap_dispc_irq_handler, 0, MODULE_NAME, fbdev)) < 0) { @@ -1421,7 +1441,7 @@ static int omap_dispc_init(struct omapfb_device *fbdev, int ext_mode, } /* L3 firewall setting: enable access to OCM RAM */ - __raw_writel(0x402000b0, IO_ADDRESS(0x680050a0)); + __raw_writel(0x402000b0, OMAP2_IO_ADDRESS(0x680050a0)); if ((r = alloc_palette_ram()) < 0) goto fail2; @@ -1469,7 +1489,6 @@ fail2: free_irq(INT_24XX_DSS_IRQ, fbdev); fail1: enable_lcd_clocks(0); - enable_interface_clocks(0); put_dss_clocks(); fail0: iounmap(dispc.base); @@ -1487,7 +1506,6 @@ static void omap_dispc_cleanup(void) cleanup_fbmem(); free_palette_ram(); free_irq(INT_24XX_DSS_IRQ, dispc.fbdev); - enable_interface_clocks(0); put_dss_clocks(); iounmap(dispc.base); } diff --git a/drivers/video/omap/dispc.h b/drivers/video/omap/dispc.h index ef720a78f6d..c15ea77f060 100644 --- a/drivers/video/omap/dispc.h +++ b/drivers/video/omap/dispc.h @@ -37,9 +37,10 @@ extern void omap_dispc_set_lcd_size(int width, int height); extern void omap_dispc_enable_lcd_out(int enable); extern void omap_dispc_enable_digit_out(int enable); -extern int omap_dispc_request_irq(void (*callback)(void *data), void *data); -extern void omap_dispc_free_irq(void); +extern int omap_dispc_request_irq(unsigned long irq_mask, + void (*callback)(void *data), void *data); +extern void omap_dispc_free_irq(unsigned long irq_mask, + void (*callback)(void *data), void *data); extern const struct lcd_ctrl omap2_int_ctrl; - #endif diff --git a/drivers/video/omap/hwa742.c b/drivers/video/omap/hwa742.c index 5d4f34887a2..ca51583ec98 100644 --- a/drivers/video/omap/hwa742.c +++ b/drivers/video/omap/hwa742.c @@ -131,7 +131,7 @@ struct { struct omapfb_device *fbdev; struct lcd_ctrl_extif *extif; - struct lcd_ctrl *int_ctrl; + const struct lcd_ctrl *int_ctrl; struct clk *sys_ck; } hwa742; diff --git a/drivers/video/omap/lcd_2430sdp.c b/drivers/video/omap/lcd_2430sdp.c new file mode 100644 index 00000000000..393712b6f36 --- /dev/null +++ b/drivers/video/omap/lcd_2430sdp.c @@ -0,0 +1,202 @@ +/* + * LCD panel support for the TI 2430SDP board + * + * Copyright (C) 2007 MontaVista + * Author: Hunyue Yau <hyau@mvista.com> + * + * Derived from drivers/video/omap/lcd-apollon.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/i2c/twl4030.h> + +#include <mach/mux.h> +#include <mach/omapfb.h> +#include <asm/mach-types.h> + +#define SDP2430_LCD_PANEL_BACKLIGHT_GPIO 91 +#define SDP2430_LCD_PANEL_ENABLE_GPIO 154 +#define SDP3430_LCD_PANEL_BACKLIGHT_GPIO 24 +#define SDP3430_LCD_PANEL_ENABLE_GPIO 28 + +static unsigned backlight_gpio; +static unsigned enable_gpio; + +#define LCD_PIXCLOCK_MAX 5400 /* freq 5.4 MHz */ +#define PM_RECEIVER TWL4030_MODULE_PM_RECEIVER +#define ENABLE_VAUX2_DEDICATED 0x09 +#define ENABLE_VAUX2_DEV_GRP 0x20 +#define ENABLE_VAUX3_DEDICATED 0x03 +#define ENABLE_VAUX3_DEV_GRP 0x20 + +#define ENABLE_VPLL2_DEDICATED 0x05 +#define ENABLE_VPLL2_DEV_GRP 0xE0 +#define TWL4030_VPLL2_DEV_GRP 0x33 +#define TWL4030_VPLL2_DEDICATED 0x36 + +#define t2_out(c, r, v) twl4030_i2c_write_u8(c, r, v) + + +static int sdp2430_panel_init(struct lcd_panel *panel, + struct omapfb_device *fbdev) +{ + if (machine_is_omap_3430sdp()) { + enable_gpio = SDP3430_LCD_PANEL_ENABLE_GPIO; + backlight_gpio = SDP3430_LCD_PANEL_BACKLIGHT_GPIO; + } else { + enable_gpio = SDP2430_LCD_PANEL_ENABLE_GPIO; + backlight_gpio = SDP2430_LCD_PANEL_BACKLIGHT_GPIO; + } + + gpio_request(enable_gpio, "LCD enable"); /* LCD panel */ + gpio_request(backlight_gpio, "LCD bl"); /* LCD backlight */ + gpio_direction_output(enable_gpio, 0); + gpio_direction_output(backlight_gpio, 0); + + return 0; +} + +static void sdp2430_panel_cleanup(struct lcd_panel *panel) +{ + gpio_free(backlight_gpio); + gpio_free(enable_gpio); +} + +static int sdp2430_panel_enable(struct lcd_panel *panel) +{ + u8 ded_val, ded_reg; + u8 grp_val, grp_reg; + + if (machine_is_omap_3430sdp()) { + ded_reg = TWL4030_VAUX3_DEDICATED; + ded_val = ENABLE_VAUX3_DEDICATED; + grp_reg = TWL4030_VAUX3_DEV_GRP; + grp_val = ENABLE_VAUX3_DEV_GRP; + + if (omap_rev() > OMAP3430_REV_ES1_0) { + t2_out(PM_RECEIVER, ENABLE_VPLL2_DEDICATED, + TWL4030_VPLL2_DEDICATED); + t2_out(PM_RECEIVER, ENABLE_VPLL2_DEV_GRP, + TWL4030_VPLL2_DEV_GRP); + } + } else { + ded_reg = TWL4030_VAUX2_DEDICATED; + ded_val = ENABLE_VAUX2_DEDICATED; + grp_reg = TWL4030_VAUX2_DEV_GRP; + grp_val = ENABLE_VAUX2_DEV_GRP; + } + + gpio_set_value(enable_gpio, 1); + gpio_set_value(backlight_gpio, 1); + + if (0 != t2_out(PM_RECEIVER, ded_val, ded_reg)) + return -EIO; + if (0 != t2_out(PM_RECEIVER, grp_val, grp_reg)) + return -EIO; + + return 0; +} + +static void sdp2430_panel_disable(struct lcd_panel *panel) +{ + gpio_set_value(enable_gpio, 0); + gpio_set_value(backlight_gpio, 0); + if (omap_rev() > OMAP3430_REV_ES1_0) { + t2_out(PM_RECEIVER, 0x0, TWL4030_VPLL2_DEDICATED); + t2_out(PM_RECEIVER, 0x0, TWL4030_VPLL2_DEV_GRP); + msleep(4); + } +} + +static unsigned long sdp2430_panel_get_caps(struct lcd_panel *panel) +{ + return 0; +} + +struct lcd_panel sdp2430_panel = { + .name = "sdp2430", + .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC | + OMAP_LCDC_INV_HSYNC, + + .bpp = 16, + .data_lines = 16, + .x_res = 240, + .y_res = 320, + .hsw = 3, /* hsync_len (4) - 1 */ + .hfp = 3, /* right_margin (4) - 1 */ + .hbp = 39, /* left_margin (40) - 1 */ + .vsw = 1, /* vsync_len (2) - 1 */ + .vfp = 2, /* lower_margin */ + .vbp = 7, /* upper_margin (8) - 1 */ + + .pixel_clock = LCD_PIXCLOCK_MAX, + + .init = sdp2430_panel_init, + .cleanup = sdp2430_panel_cleanup, + .enable = sdp2430_panel_enable, + .disable = sdp2430_panel_disable, + .get_caps = sdp2430_panel_get_caps, +}; + +static int sdp2430_panel_probe(struct platform_device *pdev) +{ + omapfb_register_panel(&sdp2430_panel); + return 0; +} + +static int sdp2430_panel_remove(struct platform_device *pdev) +{ + return 0; +} + +static int sdp2430_panel_suspend(struct platform_device *pdev, + pm_message_t mesg) +{ + return 0; +} + +static int sdp2430_panel_resume(struct platform_device *pdev) +{ + return 0; +} + +struct platform_driver sdp2430_panel_driver = { + .probe = sdp2430_panel_probe, + .remove = sdp2430_panel_remove, + .suspend = sdp2430_panel_suspend, + .resume = sdp2430_panel_resume, + .driver = { + .name = "sdp2430_lcd", + .owner = THIS_MODULE, + }, +}; + +static int __init sdp2430_panel_drv_init(void) +{ + return platform_driver_register(&sdp2430_panel_driver); +} + +static void __exit sdp2430_panel_drv_exit(void) +{ + platform_driver_unregister(&sdp2430_panel_driver); +} + +module_init(sdp2430_panel_drv_init); +module_exit(sdp2430_panel_drv_exit); diff --git a/drivers/video/omap/lcd_ams_delta.c b/drivers/video/omap/lcd_ams_delta.c new file mode 100644 index 00000000000..1f7439955e0 --- /dev/null +++ b/drivers/video/omap/lcd_ams_delta.c @@ -0,0 +1,137 @@ +/* + * Based on drivers/video/omap/lcd_inn1510.c + * + * LCD panel support for the Amstrad E3 (Delta) videophone. + * + * Copyright (C) 2006 Jonathan McDowell <noodles@earth.li> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/delay.h> + +#include <mach/board-ams-delta.h> +#include <mach/hardware.h> +#include <mach/omapfb.h> + +#define AMS_DELTA_DEFAULT_CONTRAST 112 + +static int ams_delta_panel_init(struct lcd_panel *panel, + struct omapfb_device *fbdev) +{ + return 0; +} + +static void ams_delta_panel_cleanup(struct lcd_panel *panel) +{ +} + +static int ams_delta_panel_enable(struct lcd_panel *panel) +{ + ams_delta_latch2_write(AMS_DELTA_LATCH2_LCD_NDISP, + AMS_DELTA_LATCH2_LCD_NDISP); + ams_delta_latch2_write(AMS_DELTA_LATCH2_LCD_VBLEN, + AMS_DELTA_LATCH2_LCD_VBLEN); + + omap_writeb(1, OMAP_PWL_CLK_ENABLE); + omap_writeb(AMS_DELTA_DEFAULT_CONTRAST, OMAP_PWL_ENABLE); + + return 0; +} + +static void ams_delta_panel_disable(struct lcd_panel *panel) +{ + ams_delta_latch2_write(AMS_DELTA_LATCH2_LCD_VBLEN, 0); + ams_delta_latch2_write(AMS_DELTA_LATCH2_LCD_NDISP, 0); +} + +static unsigned long ams_delta_panel_get_caps(struct lcd_panel *panel) +{ + return 0; +} + +static struct lcd_panel ams_delta_panel = { + .name = "ams-delta", + .config = 0, + + .bpp = 12, + .data_lines = 16, + .x_res = 480, + .y_res = 320, + .pixel_clock = 4687, + .hsw = 3, + .hfp = 1, + .hbp = 1, + .vsw = 1, + .vfp = 0, + .vbp = 0, + .pcd = 0, + .acb = 37, + + .init = ams_delta_panel_init, + .cleanup = ams_delta_panel_cleanup, + .enable = ams_delta_panel_enable, + .disable = ams_delta_panel_disable, + .get_caps = ams_delta_panel_get_caps, +}; + +static int ams_delta_panel_probe(struct platform_device *pdev) +{ + omapfb_register_panel(&ams_delta_panel); + return 0; +} + +static int ams_delta_panel_remove(struct platform_device *pdev) +{ + return 0; +} + +static int ams_delta_panel_suspend(struct platform_device *pdev, + pm_message_t mesg) +{ + return 0; +} + +static int ams_delta_panel_resume(struct platform_device *pdev) +{ + return 0; +} + +struct platform_driver ams_delta_panel_driver = { + .probe = ams_delta_panel_probe, + .remove = ams_delta_panel_remove, + .suspend = ams_delta_panel_suspend, + .resume = ams_delta_panel_resume, + .driver = { + .name = "lcd_ams_delta", + .owner = THIS_MODULE, + }, +}; + +static int ams_delta_panel_drv_init(void) +{ + return platform_driver_register(&ams_delta_panel_driver); +} + +static void ams_delta_panel_drv_cleanup(void) +{ + platform_driver_unregister(&ams_delta_panel_driver); +} + +module_init(ams_delta_panel_drv_init); +module_exit(ams_delta_panel_drv_cleanup); diff --git a/drivers/video/omap/lcd_apollon.c b/drivers/video/omap/lcd_apollon.c new file mode 100644 index 00000000000..626ae3a532f --- /dev/null +++ b/drivers/video/omap/lcd_apollon.c @@ -0,0 +1,138 @@ +/* + * LCD panel support for the Samsung OMAP2 Apollon board + * + * Copyright (C) 2005,2006 Samsung Electronics + * Author: Kyungmin Park <kyungmin.park@samsung.com> + * + * Derived from drivers/video/omap/lcd-h4.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <mach/gpio.h> +#include <mach/mux.h> +#include <mach/omapfb.h> + +/* #define USE_35INCH_LCD 1 */ + +static int apollon_panel_init(struct lcd_panel *panel, + struct omapfb_device *fbdev) +{ + /* configure LCD PWR_EN */ + omap_cfg_reg(M21_242X_GPIO11); + return 0; +} + +static void apollon_panel_cleanup(struct lcd_panel *panel) +{ +} + +static int apollon_panel_enable(struct lcd_panel *panel) +{ + return 0; +} + +static void apollon_panel_disable(struct lcd_panel *panel) +{ +} + +static unsigned long apollon_panel_get_caps(struct lcd_panel *panel) +{ + return 0; +} + +struct lcd_panel apollon_panel = { + .name = "apollon", + .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC | + OMAP_LCDC_INV_HSYNC, + + .bpp = 16, + .data_lines = 18, +#ifdef USE_35INCH_LCD + .x_res = 240, + .y_res = 320, + .hsw = 2, + .hfp = 3, + .hbp = 9, + .vsw = 4, + .vfp = 3, + .vbp = 5, +#else + .x_res = 480, + .y_res = 272, + .hsw = 41, + .hfp = 2, + .hbp = 2, + .vsw = 10, + .vfp = 2, + .vbp = 2, +#endif + .pixel_clock = 6250, + + .init = apollon_panel_init, + .cleanup = apollon_panel_cleanup, + .enable = apollon_panel_enable, + .disable = apollon_panel_disable, + .get_caps = apollon_panel_get_caps, +}; + +static int apollon_panel_probe(struct platform_device *pdev) +{ + omapfb_register_panel(&apollon_panel); + return 0; +} + +static int apollon_panel_remove(struct platform_device *pdev) +{ + return 0; +} + +static int apollon_panel_suspend(struct platform_device *pdev, + pm_message_t mesg) +{ + return 0; +} + +static int apollon_panel_resume(struct platform_device *pdev) +{ + return 0; +} + +struct platform_driver apollon_panel_driver = { + .probe = apollon_panel_probe, + .remove = apollon_panel_remove, + .suspend = apollon_panel_suspend, + .resume = apollon_panel_resume, + .driver = { + .name = "apollon_lcd", + .owner = THIS_MODULE, + }, +}; + +static int __init apollon_panel_drv_init(void) +{ + return platform_driver_register(&apollon_panel_driver); +} + +static void __exit apollon_panel_drv_exit(void) +{ + platform_driver_unregister(&apollon_panel_driver); +} + +module_init(apollon_panel_drv_init); +module_exit(apollon_panel_drv_exit); diff --git a/drivers/video/omap/lcd_h3.c b/drivers/video/omap/lcd_h3.c index 2486237ebba..417ae5efa8b 100644 --- a/drivers/video/omap/lcd_h3.c +++ b/drivers/video/omap/lcd_h3.c @@ -124,12 +124,12 @@ struct platform_driver h3_panel_driver = { }, }; -static int h3_panel_drv_init(void) +static int __init h3_panel_drv_init(void) { return platform_driver_register(&h3_panel_driver); } -static void h3_panel_drv_cleanup(void) +static void __exit h3_panel_drv_cleanup(void) { platform_driver_unregister(&h3_panel_driver); } diff --git a/drivers/video/omap/lcd_h4.c b/drivers/video/omap/lcd_h4.c index 6ff56430341..0c398bda760 100644 --- a/drivers/video/omap/lcd_h4.c +++ b/drivers/video/omap/lcd_h4.c @@ -102,12 +102,12 @@ static struct platform_driver h4_panel_driver = { }, }; -static int h4_panel_drv_init(void) +static int __init h4_panel_drv_init(void) { return platform_driver_register(&h4_panel_driver); } -static void h4_panel_drv_cleanup(void) +static void __exit h4_panel_drv_cleanup(void) { platform_driver_unregister(&h4_panel_driver); } diff --git a/drivers/video/omap/lcd_inn1510.c b/drivers/video/omap/lcd_inn1510.c index 6953ed4b582..cdbd8bb607b 100644 --- a/drivers/video/omap/lcd_inn1510.c +++ b/drivers/video/omap/lcd_inn1510.c @@ -109,12 +109,12 @@ struct platform_driver innovator1510_panel_driver = { }, }; -static int innovator1510_panel_drv_init(void) +static int __init innovator1510_panel_drv_init(void) { return platform_driver_register(&innovator1510_panel_driver); } -static void innovator1510_panel_drv_cleanup(void) +static void __exit innovator1510_panel_drv_cleanup(void) { platform_driver_unregister(&innovator1510_panel_driver); } diff --git a/drivers/video/omap/lcd_inn1610.c b/drivers/video/omap/lcd_inn1610.c index 4c4f7ee6d73..268f7f808a4 100644 --- a/drivers/video/omap/lcd_inn1610.c +++ b/drivers/video/omap/lcd_inn1610.c @@ -133,12 +133,12 @@ struct platform_driver innovator1610_panel_driver = { }, }; -static int innovator1610_panel_drv_init(void) +static int __init innovator1610_panel_drv_init(void) { return platform_driver_register(&innovator1610_panel_driver); } -static void innovator1610_panel_drv_cleanup(void) +static void __exit innovator1610_panel_drv_cleanup(void) { platform_driver_unregister(&innovator1610_panel_driver); } diff --git a/drivers/video/omap/lcd_ldp.c b/drivers/video/omap/lcd_ldp.c new file mode 100644 index 00000000000..dbfe8974fb9 --- /dev/null +++ b/drivers/video/omap/lcd_ldp.c @@ -0,0 +1,200 @@ +/* + * LCD panel support for the TI LDP board + * + * Copyright (C) 2007 WindRiver + * Author: Stanley Miao <stanley.miao@windriver.com> + * + * Derived from drivers/video/omap/lcd-2430sdp.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/i2c/twl4030.h> + +#include <mach/gpio.h> +#include <mach/mux.h> +#include <mach/omapfb.h> +#include <asm/mach-types.h> + +#define LCD_PANEL_BACKLIGHT_GPIO (15 + OMAP_MAX_GPIO_LINES) +#define LCD_PANEL_ENABLE_GPIO (7 + OMAP_MAX_GPIO_LINES) + +#define LCD_PANEL_RESET_GPIO 55 +#define LCD_PANEL_QVGA_GPIO 56 + +#ifdef CONFIG_FB_OMAP_LCD_VGA +#define LCD_XRES 480 +#define LCD_YRES 640 +#define LCD_PIXCLOCK_MAX 41700 +#else +#define LCD_XRES 240 +#define LCD_YRES 320 +#define LCD_PIXCLOCK_MAX 185186 +#endif + +#define PM_RECEIVER TWL4030_MODULE_PM_RECEIVER +#define ENABLE_VAUX2_DEDICATED 0x09 +#define ENABLE_VAUX2_DEV_GRP 0x20 +#define ENABLE_VAUX3_DEDICATED 0x03 +#define ENABLE_VAUX3_DEV_GRP 0x20 + +#define ENABLE_VPLL2_DEDICATED 0x05 +#define ENABLE_VPLL2_DEV_GRP 0xE0 +#define TWL4030_VPLL2_DEV_GRP 0x33 +#define TWL4030_VPLL2_DEDICATED 0x36 + +#define t2_out(c, r, v) twl4030_i2c_write_u8(c, r, v) + + +static int ldp_panel_init(struct lcd_panel *panel, + struct omapfb_device *fbdev) +{ + gpio_request(LCD_PANEL_RESET_GPIO, "lcd reset"); + gpio_request(LCD_PANEL_QVGA_GPIO, "lcd qvga"); + gpio_request(LCD_PANEL_ENABLE_GPIO, "lcd panel"); + gpio_request(LCD_PANEL_BACKLIGHT_GPIO, "lcd backlight"); + + gpio_direction_output(LCD_PANEL_QVGA_GPIO, 0); + gpio_direction_output(LCD_PANEL_RESET_GPIO, 0); + gpio_direction_output(LCD_PANEL_ENABLE_GPIO, 0); + gpio_direction_output(LCD_PANEL_BACKLIGHT_GPIO, 0); + +#ifdef CONFIG_FB_OMAP_LCD_VGA + gpio_set_value(LCD_PANEL_QVGA_GPIO, 0); +#else + gpio_set_value(LCD_PANEL_QVGA_GPIO, 1); +#endif + gpio_set_value(LCD_PANEL_RESET_GPIO, 1); + + return 0; +} + +static void ldp_panel_cleanup(struct lcd_panel *panel) +{ + gpio_free(LCD_PANEL_BACKLIGHT_GPIO); + gpio_free(LCD_PANEL_ENABLE_GPIO); + gpio_free(LCD_PANEL_QVGA_GPIO); + gpio_free(LCD_PANEL_RESET_GPIO); +} + +static int ldp_panel_enable(struct lcd_panel *panel) +{ + if (0 != t2_out(PM_RECEIVER, ENABLE_VPLL2_DEDICATED, + TWL4030_VPLL2_DEDICATED)) + return -EIO; + if (0 != t2_out(PM_RECEIVER, ENABLE_VPLL2_DEV_GRP, + TWL4030_VPLL2_DEV_GRP)) + return -EIO; + + gpio_direction_output(LCD_PANEL_ENABLE_GPIO, 1); + gpio_direction_output(LCD_PANEL_BACKLIGHT_GPIO, 1); + + if (0 != t2_out(PM_RECEIVER, ENABLE_VAUX3_DEDICATED, + TWL4030_VAUX3_DEDICATED)) + return -EIO; + if (0 != t2_out(PM_RECEIVER, ENABLE_VAUX3_DEV_GRP, + TWL4030_VAUX3_DEV_GRP)) + return -EIO; + + return 0; +} + +static void ldp_panel_disable(struct lcd_panel *panel) +{ + gpio_direction_output(LCD_PANEL_ENABLE_GPIO, 0); + gpio_direction_output(LCD_PANEL_BACKLIGHT_GPIO, 0); + + t2_out(PM_RECEIVER, 0x0, TWL4030_VPLL2_DEDICATED); + t2_out(PM_RECEIVER, 0x0, TWL4030_VPLL2_DEV_GRP); + msleep(4); +} + +static unsigned long ldp_panel_get_caps(struct lcd_panel *panel) +{ + return 0; +} + +struct lcd_panel ldp_panel = { + .name = "ldp", + .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC | + OMAP_LCDC_INV_HSYNC, + + .bpp = 16, + .data_lines = 18, + .x_res = LCD_XRES, + .y_res = LCD_YRES, + .hsw = 3, /* hsync_len (4) - 1 */ + .hfp = 3, /* right_margin (4) - 1 */ + .hbp = 39, /* left_margin (40) - 1 */ + .vsw = 1, /* vsync_len (2) - 1 */ + .vfp = 2, /* lower_margin */ + .vbp = 7, /* upper_margin (8) - 1 */ + + .pixel_clock = LCD_PIXCLOCK_MAX, + + .init = ldp_panel_init, + .cleanup = ldp_panel_cleanup, + .enable = ldp_panel_enable, + .disable = ldp_panel_disable, + .get_caps = ldp_panel_get_caps, +}; + +static int ldp_panel_probe(struct platform_device *pdev) +{ + omapfb_register_panel(&ldp_panel); + return 0; +} + +static int ldp_panel_remove(struct platform_device *pdev) +{ + return 0; +} + +static int ldp_panel_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + return 0; +} + +static int ldp_panel_resume(struct platform_device *pdev) +{ + return 0; +} + +struct platform_driver ldp_panel_driver = { + .probe = ldp_panel_probe, + .remove = ldp_panel_remove, + .suspend = ldp_panel_suspend, + .resume = ldp_panel_resume, + .driver = { + .name = "ldp_lcd", + .owner = THIS_MODULE, + }, +}; + +static int __init ldp_panel_drv_init(void) +{ + return platform_driver_register(&ldp_panel_driver); +} + +static void __exit ldp_panel_drv_exit(void) +{ + platform_driver_unregister(&ldp_panel_driver); +} + +module_init(ldp_panel_drv_init); +module_exit(ldp_panel_drv_exit); diff --git a/drivers/video/omap/lcd_mipid.c b/drivers/video/omap/lcd_mipid.c new file mode 100644 index 00000000000..918ee893419 --- /dev/null +++ b/drivers/video/omap/lcd_mipid.c @@ -0,0 +1,625 @@ +/* + * LCD driver for MIPI DBI-C / DCS compatible LCDs + * + * Copyright (C) 2006 Nokia Corporation + * Author: Imre Deak <imre.deak@nokia.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/workqueue.h> +#include <linux/spi/spi.h> + +#include <mach/omapfb.h> +#include <mach/lcd_mipid.h> + +#define MIPID_MODULE_NAME "lcd_mipid" + +#define MIPID_CMD_READ_DISP_ID 0x04 +#define MIPID_CMD_READ_RED 0x06 +#define MIPID_CMD_READ_GREEN 0x07 +#define MIPID_CMD_READ_BLUE 0x08 +#define MIPID_CMD_READ_DISP_STATUS 0x09 +#define MIPID_CMD_RDDSDR 0x0F +#define MIPID_CMD_SLEEP_IN 0x10 +#define MIPID_CMD_SLEEP_OUT 0x11 +#define MIPID_CMD_DISP_OFF 0x28 +#define MIPID_CMD_DISP_ON 0x29 + +#define MIPID_ESD_CHECK_PERIOD msecs_to_jiffies(5000) + +#define to_mipid_device(p) container_of(p, struct mipid_device, \ + panel) +struct mipid_device { + int enabled; + int revision; + 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; + struct mutex mutex; + struct lcd_panel panel; + + struct workqueue_struct *esd_wq; + struct delayed_work esd_work; + void (*esd_check)(struct mipid_device *m); +}; + +static void mipid_transfer(struct mipid_device *md, int cmd, const u8 *wbuf, + int wlen, u8 *rbuf, int rlen) +{ + struct spi_message m; + struct spi_transfer *x, xfer[4]; + u16 w; + int r; + + BUG_ON(md->spi == NULL); + + spi_message_init(&m); + + memset(xfer, 0, sizeof(xfer)); + x = &xfer[0]; + + cmd &= 0xff; + x->tx_buf = &cmd; + x->bits_per_word = 9; + x->len = 2; + spi_message_add_tail(x, &m); + + if (wlen) { + x++; + x->tx_buf = wbuf; + x->len = wlen; + x->bits_per_word = 9; + spi_message_add_tail(x, &m); + } + + if (rlen) { + x++; + x->rx_buf = &w; + x->len = 1; + spi_message_add_tail(x, &m); + + if (rlen > 1) { + /* Arrange for the extra clock before the first + * data bit. + */ + x->bits_per_word = 9; + x->len = 2; + + x++; + x->rx_buf = &rbuf[1]; + x->len = rlen - 1; + spi_message_add_tail(x, &m); + } + } + + r = spi_sync(md->spi, &m); + if (r < 0) + dev_dbg(&md->spi->dev, "spi_sync %d\n", r); + + if (rlen) + rbuf[0] = w & 0xff; +} + +static inline void mipid_cmd(struct mipid_device *md, int cmd) +{ + mipid_transfer(md, cmd, NULL, 0, NULL, 0); +} + +static inline void mipid_write(struct mipid_device *md, + int reg, const u8 *buf, int len) +{ + mipid_transfer(md, reg, buf, len, NULL, 0); +} + +static inline void mipid_read(struct mipid_device *md, + int reg, u8 *buf, int len) +{ + mipid_transfer(md, reg, NULL, 0, buf, len); +} + +static void set_data_lines(struct mipid_device *md, int data_lines) +{ + u16 par; + + switch (data_lines) { + case 16: + par = 0x150; + break; + case 18: + par = 0x160; + break; + case 24: + par = 0x170; + break; + } + mipid_write(md, 0x3a, (u8 *)&par, 2); +} + +static void send_init_string(struct mipid_device *md) +{ + u16 initpar[] = { 0x0102, 0x0100, 0x0100 }; + + mipid_write(md, 0xc2, (u8 *)initpar, sizeof(initpar)); + set_data_lines(md, md->panel.data_lines); +} + +static void hw_guard_start(struct mipid_device *md, int guard_msec) +{ + md->hw_guard_wait = msecs_to_jiffies(guard_msec); + md->hw_guard_end = jiffies + md->hw_guard_wait; +} + +static void hw_guard_wait(struct mipid_device *md) +{ + unsigned long wait = md->hw_guard_end - jiffies; + + if ((long)wait > 0 && wait <= md->hw_guard_wait) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(wait); + } +} + +static void set_sleep_mode(struct mipid_device *md, int on) +{ + int cmd, sleep_time = 50; + + if (on) + cmd = MIPID_CMD_SLEEP_IN; + else + cmd = MIPID_CMD_SLEEP_OUT; + hw_guard_wait(md); + mipid_cmd(md, cmd); + hw_guard_start(md, 120); + /* + * When we enable the panel, it seems we _have_ to sleep + * 120 ms before sending the init string. When disabling the + * panel we'll sleep for the duration of 2 frames, so that the + * controller can still provide the PCLK,HS,VS signals. + */ + if (!on) + sleep_time = 120; + msleep(sleep_time); +} + +static void set_display_state(struct mipid_device *md, int enabled) +{ + int cmd = enabled ? MIPID_CMD_DISP_ON : MIPID_CMD_DISP_OFF; + + mipid_cmd(md, cmd); +} + +static int mipid_set_bklight_level(struct lcd_panel *panel, unsigned int level) +{ + struct mipid_device *md = to_mipid_device(panel); + struct mipid_platform_data *pd = md->spi->dev.platform_data; + + if (pd->get_bklight_max == NULL || pd->set_bklight_level == NULL) + return -ENODEV; + if (level > pd->get_bklight_max(pd)) + return -EINVAL; + if (!md->enabled) { + md->saved_bklight_level = level; + return 0; + } + pd->set_bklight_level(pd, level); + + return 0; +} + +static unsigned int mipid_get_bklight_level(struct lcd_panel *panel) +{ + struct mipid_device *md = to_mipid_device(panel); + struct mipid_platform_data *pd = md->spi->dev.platform_data; + + if (pd->get_bklight_level == NULL) + return -ENODEV; + return pd->get_bklight_level(pd); +} + +static unsigned int mipid_get_bklight_max(struct lcd_panel *panel) +{ + struct mipid_device *md = to_mipid_device(panel); + struct mipid_platform_data *pd = md->spi->dev.platform_data; + + if (pd->get_bklight_max == NULL) + return -ENODEV; + + return pd->get_bklight_max(pd); +} + +static unsigned long mipid_get_caps(struct lcd_panel *panel) +{ + return OMAPFB_CAPS_SET_BACKLIGHT; +} + +static u16 read_first_pixel(struct mipid_device *md) +{ + u16 pixel; + u8 red, green, blue; + + mutex_lock(&md->mutex); + mipid_read(md, MIPID_CMD_READ_RED, &red, 1); + mipid_read(md, MIPID_CMD_READ_GREEN, &green, 1); + mipid_read(md, MIPID_CMD_READ_BLUE, &blue, 1); + mutex_unlock(&md->mutex); + + switch (md->panel.data_lines) { + case 16: + pixel = ((red >> 1) << 11) | (green << 5) | (blue >> 1); + break; + case 24: + /* 24 bit -> 16 bit */ + pixel = ((red >> 3) << 11) | ((green >> 2) << 5) | + (blue >> 3); + break; + default: + pixel = 0; + BUG(); + } + + return pixel; +} + +static int mipid_run_test(struct lcd_panel *panel, int test_num) +{ + struct mipid_device *md = to_mipid_device(panel); + static const u16 test_values[4] = { + 0x0000, 0xffff, 0xaaaa, 0x5555, + }; + int i; + + if (test_num != MIPID_TEST_RGB_LINES) + return MIPID_TEST_INVALID; + + for (i = 0; i < ARRAY_SIZE(test_values); i++) { + int delay; + unsigned long tmo; + + omapfb_write_first_pixel(md->fbdev, test_values[i]); + tmo = jiffies + msecs_to_jiffies(100); + delay = 25; + while (1) { + u16 pixel; + + msleep(delay); + pixel = read_first_pixel(md); + if (pixel == test_values[i]) + break; + if (time_after(jiffies, tmo)) { + dev_err(&md->spi->dev, + "MIPI LCD RGB I/F test failed: " + "expecting %04x, got %04x\n", + test_values[i], pixel); + return MIPID_TEST_FAILED; + } + delay = 10; + } + } + + return 0; +} + +static void ls041y3_esd_recover(struct mipid_device *md) +{ + dev_err(&md->spi->dev, "performing LCD ESD recovery\n"); + set_sleep_mode(md, 1); + set_sleep_mode(md, 0); +} + +static void ls041y3_esd_check_mode1(struct mipid_device *md) +{ + u8 state1, state2; + + mipid_read(md, MIPID_CMD_RDDSDR, &state1, 1); + set_sleep_mode(md, 0); + mipid_read(md, MIPID_CMD_RDDSDR, &state2, 1); + dev_dbg(&md->spi->dev, "ESD mode 1 state1 %02x state2 %02x\n", + state1, state2); + /* Each sleep out command will trigger a self diagnostic and flip + * Bit6 if the test passes. + */ + if (!((state1 ^ state2) & (1 << 6))) + ls041y3_esd_recover(md); +} + +static void ls041y3_esd_check_mode2(struct mipid_device *md) +{ + int i; + u8 rbuf[2]; + static const struct { + int cmd; + int wlen; + u16 wbuf[3]; + } *rd, rd_ctrl[7] = { + { 0xb0, 4, { 0x0101, 0x01fe, } }, + { 0xb1, 4, { 0x01de, 0x0121, } }, + { 0xc2, 4, { 0x0100, 0x0100, } }, + { 0xbd, 2, { 0x0100, } }, + { 0xc2, 4, { 0x01fc, 0x0103, } }, + { 0xb4, 0, }, + { 0x00, 0, }, + }; + + rd = rd_ctrl; + for (i = 0; i < 3; i++, rd++) + mipid_write(md, rd->cmd, (u8 *)rd->wbuf, rd->wlen); + + udelay(10); + mipid_read(md, rd->cmd, rbuf, 2); + rd++; + + for (i = 0; i < 3; i++, rd++) { + udelay(10); + mipid_write(md, rd->cmd, (u8 *)rd->wbuf, rd->wlen); + } + + dev_dbg(&md->spi->dev, "ESD mode 2 state %02x\n", rbuf[1]); + if (rbuf[1] == 0x00) + ls041y3_esd_recover(md); +} + +static void ls041y3_esd_check(struct mipid_device *md) +{ + ls041y3_esd_check_mode1(md); + if (md->revision >= 0x88) + ls041y3_esd_check_mode2(md); +} + +static void mipid_esd_start_check(struct mipid_device *md) +{ + if (md->esd_check != NULL) + queue_delayed_work(md->esd_wq, &md->esd_work, + MIPID_ESD_CHECK_PERIOD); +} + +static void mipid_esd_stop_check(struct mipid_device *md) +{ + if (md->esd_check != NULL) + cancel_rearming_delayed_workqueue(md->esd_wq, &md->esd_work); +} + +static void mipid_esd_work(struct work_struct *work) +{ + struct mipid_device *md = container_of(work, struct mipid_device, + esd_work.work); + + mutex_lock(&md->mutex); + md->esd_check(md); + mutex_unlock(&md->mutex); + mipid_esd_start_check(md); +} + +static int mipid_enable(struct lcd_panel *panel) +{ + struct mipid_device *md = to_mipid_device(panel); + + mutex_lock(&md->mutex); + + if (md->enabled) { + mutex_unlock(&md->mutex); + return 0; + } + set_sleep_mode(md, 0); + md->enabled = 1; + send_init_string(md); + set_display_state(md, 1); + mipid_set_bklight_level(panel, md->saved_bklight_level); + mipid_esd_start_check(md); + + mutex_unlock(&md->mutex); + return 0; +} + +static void mipid_disable(struct lcd_panel *panel) +{ + struct mipid_device *md = to_mipid_device(panel); + + /* + * A final ESD work might be called before returning, + * so do this without holding the lock. + */ + mipid_esd_stop_check(md); + mutex_lock(&md->mutex); + + if (!md->enabled) { + mutex_unlock(&md->mutex); + return; + } + md->saved_bklight_level = mipid_get_bklight_level(panel); + mipid_set_bklight_level(panel, 0); + set_display_state(md, 0); + set_sleep_mode(md, 1); + md->enabled = 0; + + mutex_unlock(&md->mutex); +} + +static int panel_enabled(struct mipid_device *md) +{ + u32 disp_status; + int enabled; + + mipid_read(md, MIPID_CMD_READ_DISP_STATUS, (u8 *)&disp_status, 4); + disp_status = __be32_to_cpu(disp_status); + enabled = (disp_status & (1 << 17)) && (disp_status & (1 << 10)); + dev_dbg(&md->spi->dev, + "LCD panel %senabled by bootloader (status 0x%04x)\n", + enabled ? "" : "not ", disp_status); + return enabled; +} + +static int mipid_init(struct lcd_panel *panel, + struct omapfb_device *fbdev) +{ + struct mipid_device *md = to_mipid_device(panel); + + md->fbdev = fbdev; + md->esd_wq = create_singlethread_workqueue("mipid_esd"); + if (md->esd_wq == NULL) { + dev_err(&md->spi->dev, "can't create ESD workqueue\n"); + return -ENOMEM; + } + INIT_DELAYED_WORK(&md->esd_work, mipid_esd_work); + mutex_init(&md->mutex); + + md->enabled = panel_enabled(md); + + if (md->enabled) + mipid_esd_start_check(md); + else + md->saved_bklight_level = mipid_get_bklight_level(panel); + + return 0; +} + +static void mipid_cleanup(struct lcd_panel *panel) +{ + struct mipid_device *md = to_mipid_device(panel); + + if (md->enabled) + mipid_esd_stop_check(md); + destroy_workqueue(md->esd_wq); +} + +static struct lcd_panel mipid_panel = { + .config = OMAP_LCDC_PANEL_TFT, + + .bpp = 16, + .x_res = 800, + .y_res = 480, + .pixel_clock = 21940, + .hsw = 50, + .hfp = 20, + .hbp = 15, + .vsw = 2, + .vfp = 1, + .vbp = 3, + + .init = mipid_init, + .cleanup = mipid_cleanup, + .enable = mipid_enable, + .disable = mipid_disable, + .get_caps = mipid_get_caps, + .set_bklight_level = mipid_set_bklight_level, + .get_bklight_level = mipid_get_bklight_level, + .get_bklight_max = mipid_get_bklight_max, + .run_test = mipid_run_test, +}; + +static int mipid_detect(struct mipid_device *md) +{ + struct mipid_platform_data *pdata; + u8 display_id[3]; + + pdata = md->spi->dev.platform_data; + if (pdata == NULL) { + dev_err(&md->spi->dev, "missing platform data\n"); + return -ENOENT; + } + + mipid_read(md, MIPID_CMD_READ_DISP_ID, display_id, 3); + dev_dbg(&md->spi->dev, "MIPI display ID: %02x%02x%02x\n", + display_id[0], display_id[1], display_id[2]); + + switch (display_id[0]) { + case 0x45: + md->panel.name = "lph8923"; + break; + case 0x83: + md->panel.name = "ls041y3"; + md->esd_check = ls041y3_esd_check; + break; + default: + md->panel.name = "unknown"; + dev_err(&md->spi->dev, "invalid display ID\n"); + return -ENODEV; + } + + md->revision = display_id[1]; + md->panel.data_lines = pdata->data_lines; + pr_info("omapfb: %s rev %02x LCD detected, %d data lines\n", + md->panel.name, md->revision, md->panel.data_lines); + + return 0; +} + +static int mipid_spi_probe(struct spi_device *spi) +{ + struct mipid_device *md; + int r; + + md = kzalloc(sizeof(*md), GFP_KERNEL); + if (md == NULL) { + dev_err(&spi->dev, "out of memory\n"); + return -ENOMEM; + } + + spi->mode = SPI_MODE_0; + md->spi = spi; + dev_set_drvdata(&spi->dev, md); + md->panel = mipid_panel; + + r = mipid_detect(md); + if (r < 0) + return r; + + omapfb_register_panel(&md->panel); + + return 0; +} + +static int mipid_spi_remove(struct spi_device *spi) +{ + struct mipid_device *md = dev_get_drvdata(&spi->dev); + + mipid_disable(&md->panel); + kfree(md); + + return 0; +} + +static struct spi_driver mipid_spi_driver = { + .driver = { + .name = MIPID_MODULE_NAME, + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = mipid_spi_probe, + .remove = __devexit_p(mipid_spi_remove), +}; + +static int mipid_drv_init(void) +{ + spi_register_driver(&mipid_spi_driver); + + return 0; +} +module_init(mipid_drv_init); + +static void mipid_drv_cleanup(void) +{ + spi_unregister_driver(&mipid_spi_driver); +} +module_exit(mipid_drv_cleanup); + +MODULE_DESCRIPTION("MIPI display driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap/lcd_omap2evm.c b/drivers/video/omap/lcd_omap2evm.c new file mode 100644 index 00000000000..7a2bbe2ecec --- /dev/null +++ b/drivers/video/omap/lcd_omap2evm.c @@ -0,0 +1,191 @@ +/* + * LCD panel support for the MISTRAL OMAP2EVM board + * + * Author: Arun C <arunedarath@mistralsolutions.com> + * + * Derived from drivers/video/omap/lcd_omap3evm.c + * Derived from drivers/video/omap/lcd-apollon.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/gpio.h> +#include <linux/i2c/twl4030.h> + +#include <mach/mux.h> +#include <mach/omapfb.h> +#include <asm/mach-types.h> + +#define LCD_PANEL_ENABLE_GPIO 154 +#define LCD_PANEL_LR 128 +#define LCD_PANEL_UD 129 +#define LCD_PANEL_INI 152 +#define LCD_PANEL_QVGA 148 +#define LCD_PANEL_RESB 153 + +#define TWL_LED_LEDEN 0x00 +#define TWL_PWMA_PWMAON 0x00 +#define TWL_PWMA_PWMAOFF 0x01 + +static unsigned int bklight_level; + +static int omap2evm_panel_init(struct lcd_panel *panel, + struct omapfb_device *fbdev) +{ + gpio_request(LCD_PANEL_ENABLE_GPIO, "LCD enable"); + gpio_request(LCD_PANEL_LR, "LCD lr"); + gpio_request(LCD_PANEL_UD, "LCD ud"); + gpio_request(LCD_PANEL_INI, "LCD ini"); + gpio_request(LCD_PANEL_QVGA, "LCD qvga"); + gpio_request(LCD_PANEL_RESB, "LCD resb"); + + gpio_direction_output(LCD_PANEL_ENABLE_GPIO, 1); + gpio_direction_output(LCD_PANEL_RESB, 1); + gpio_direction_output(LCD_PANEL_INI, 1); + gpio_direction_output(LCD_PANEL_QVGA, 0); + gpio_direction_output(LCD_PANEL_LR, 1); + gpio_direction_output(LCD_PANEL_UD, 1); + + twl4030_i2c_write_u8(TWL4030_MODULE_LED, 0x11, TWL_LED_LEDEN); + twl4030_i2c_write_u8(TWL4030_MODULE_PWMA, 0x01, TWL_PWMA_PWMAON); + twl4030_i2c_write_u8(TWL4030_MODULE_PWMA, 0x02, TWL_PWMA_PWMAOFF); + bklight_level = 100; + + return 0; +} + +static void omap2evm_panel_cleanup(struct lcd_panel *panel) +{ + gpio_free(LCD_PANEL_RESB); + gpio_free(LCD_PANEL_QVGA); + gpio_free(LCD_PANEL_INI); + gpio_free(LCD_PANEL_UD); + gpio_free(LCD_PANEL_LR); + gpio_free(LCD_PANEL_ENABLE_GPIO); +} + +static int omap2evm_panel_enable(struct lcd_panel *panel) +{ + gpio_set_value(LCD_PANEL_ENABLE_GPIO, 0); + return 0; +} + +static void omap2evm_panel_disable(struct lcd_panel *panel) +{ + gpio_set_value(LCD_PANEL_ENABLE_GPIO, 1); +} + +static unsigned long omap2evm_panel_get_caps(struct lcd_panel *panel) +{ + return 0; +} + +static int omap2evm_bklight_setlevel(struct lcd_panel *panel, + unsigned int level) +{ + u8 c; + if ((level >= 0) && (level <= 100)) { + c = (125 * (100 - level)) / 100 + 2; + twl4030_i2c_write_u8(TWL4030_MODULE_PWMA, c, TWL_PWMA_PWMAOFF); + bklight_level = level; + } + return 0; +} + +static unsigned int omap2evm_bklight_getlevel(struct lcd_panel *panel) +{ + return bklight_level; +} + +static unsigned int omap2evm_bklight_getmaxlevel(struct lcd_panel *panel) +{ + return 100; +} + +struct lcd_panel omap2evm_panel = { + .name = "omap2evm", + .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC | + OMAP_LCDC_INV_HSYNC, + + .bpp = 16, + .data_lines = 18, + .x_res = 480, + .y_res = 640, + .hsw = 3, + .hfp = 0, + .hbp = 28, + .vsw = 2, + .vfp = 1, + .vbp = 0, + + .pixel_clock = 20000, + + .init = omap2evm_panel_init, + .cleanup = omap2evm_panel_cleanup, + .enable = omap2evm_panel_enable, + .disable = omap2evm_panel_disable, + .get_caps = omap2evm_panel_get_caps, + .set_bklight_level = omap2evm_bklight_setlevel, + .get_bklight_level = omap2evm_bklight_getlevel, + .get_bklight_max = omap2evm_bklight_getmaxlevel, +}; + +static int omap2evm_panel_probe(struct platform_device *pdev) +{ + omapfb_register_panel(&omap2evm_panel); + return 0; +} + +static int omap2evm_panel_remove(struct platform_device *pdev) +{ + return 0; +} + +static int omap2evm_panel_suspend(struct platform_device *pdev, + pm_message_t mesg) +{ + return 0; +} + +static int omap2evm_panel_resume(struct platform_device *pdev) +{ + return 0; +} + +struct platform_driver omap2evm_panel_driver = { + .probe = omap2evm_panel_probe, + .remove = omap2evm_panel_remove, + .suspend = omap2evm_panel_suspend, + .resume = omap2evm_panel_resume, + .driver = { + .name = "omap2evm_lcd", + .owner = THIS_MODULE, + }, +}; + +static int __init omap2evm_panel_drv_init(void) +{ + return platform_driver_register(&omap2evm_panel_driver); +} + +static void __exit omap2evm_panel_drv_exit(void) +{ + platform_driver_unregister(&omap2evm_panel_driver); +} + +module_init(omap2evm_panel_drv_init); +module_exit(omap2evm_panel_drv_exit); diff --git a/drivers/video/omap/lcd_omap3beagle.c b/drivers/video/omap/lcd_omap3beagle.c new file mode 100644 index 00000000000..4011910123b --- /dev/null +++ b/drivers/video/omap/lcd_omap3beagle.c @@ -0,0 +1,130 @@ +/* + * LCD panel support for the TI OMAP3 Beagle board + * + * Author: Koen Kooi <koen@openembedded.org> + * + * Derived from drivers/video/omap/lcd-omap3evm.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/gpio.h> +#include <linux/i2c/twl4030.h> + +#include <mach/mux.h> +#include <mach/omapfb.h> +#include <asm/mach-types.h> + +#define LCD_PANEL_ENABLE_GPIO 170 + +static int omap3beagle_panel_init(struct lcd_panel *panel, + struct omapfb_device *fbdev) +{ + gpio_request(LCD_PANEL_ENABLE_GPIO, "LCD enable"); + return 0; +} + +static void omap3beagle_panel_cleanup(struct lcd_panel *panel) +{ + gpio_free(LCD_PANEL_ENABLE_GPIO); +} + +static int omap3beagle_panel_enable(struct lcd_panel *panel) +{ + gpio_set_value(LCD_PANEL_ENABLE_GPIO, 1); + return 0; +} + +static void omap3beagle_panel_disable(struct lcd_panel *panel) +{ + gpio_set_value(LCD_PANEL_ENABLE_GPIO, 0); +} + +static unsigned long omap3beagle_panel_get_caps(struct lcd_panel *panel) +{ + return 0; +} + +struct lcd_panel omap3beagle_panel = { + .name = "omap3beagle", + .config = OMAP_LCDC_PANEL_TFT, + + .bpp = 16, + .data_lines = 24, + .x_res = 1024, + .y_res = 768, + .hsw = 3, /* hsync_len (4) - 1 */ + .hfp = 3, /* right_margin (4) - 1 */ + .hbp = 39, /* left_margin (40) - 1 */ + .vsw = 1, /* vsync_len (2) - 1 */ + .vfp = 2, /* lower_margin */ + .vbp = 7, /* upper_margin (8) - 1 */ + + .pixel_clock = 64000, + + .init = omap3beagle_panel_init, + .cleanup = omap3beagle_panel_cleanup, + .enable = omap3beagle_panel_enable, + .disable = omap3beagle_panel_disable, + .get_caps = omap3beagle_panel_get_caps, +}; + +static int omap3beagle_panel_probe(struct platform_device *pdev) +{ + omapfb_register_panel(&omap3beagle_panel); + return 0; +} + +static int omap3beagle_panel_remove(struct platform_device *pdev) +{ + return 0; +} + +static int omap3beagle_panel_suspend(struct platform_device *pdev, + pm_message_t mesg) +{ + return 0; +} + +static int omap3beagle_panel_resume(struct platform_device *pdev) +{ + return 0; +} + +struct platform_driver omap3beagle_panel_driver = { + .probe = omap3beagle_panel_probe, + .remove = omap3beagle_panel_remove, + .suspend = omap3beagle_panel_suspend, + .resume = omap3beagle_panel_resume, + .driver = { + .name = "omap3beagle_lcd", + .owner = THIS_MODULE, + }, +}; + +static int __init omap3beagle_panel_drv_init(void) +{ + return platform_driver_register(&omap3beagle_panel_driver); +} + +static void __exit omap3beagle_panel_drv_exit(void) +{ + platform_driver_unregister(&omap3beagle_panel_driver); +} + +module_init(omap3beagle_panel_drv_init); +module_exit(omap3beagle_panel_drv_exit); diff --git a/drivers/video/omap/lcd_omap3evm.c b/drivers/video/omap/lcd_omap3evm.c new file mode 100644 index 00000000000..b6a4c2c57a2 --- /dev/null +++ b/drivers/video/omap/lcd_omap3evm.c @@ -0,0 +1,192 @@ +/* + * LCD panel support for the TI OMAP3 EVM board + * + * Author: Steve Sakoman <steve@sakoman.com> + * + * Derived from drivers/video/omap/lcd-apollon.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/gpio.h> +#include <linux/i2c/twl4030.h> + +#include <mach/mux.h> +#include <mach/omapfb.h> +#include <asm/mach-types.h> + +#define LCD_PANEL_ENABLE_GPIO 153 +#define LCD_PANEL_LR 2 +#define LCD_PANEL_UD 3 +#define LCD_PANEL_INI 152 +#define LCD_PANEL_QVGA 154 +#define LCD_PANEL_RESB 155 + +#define ENABLE_VDAC_DEDICATED 0x03 +#define ENABLE_VDAC_DEV_GRP 0x20 +#define ENABLE_VPLL2_DEDICATED 0x05 +#define ENABLE_VPLL2_DEV_GRP 0xE0 + +#define TWL_LED_LEDEN 0x00 +#define TWL_PWMA_PWMAON 0x00 +#define TWL_PWMA_PWMAOFF 0x01 + +static unsigned int bklight_level; + +static int omap3evm_panel_init(struct lcd_panel *panel, + struct omapfb_device *fbdev) +{ + gpio_request(LCD_PANEL_LR, "LCD lr"); + gpio_request(LCD_PANEL_UD, "LCD ud"); + gpio_request(LCD_PANEL_INI, "LCD ini"); + gpio_request(LCD_PANEL_RESB, "LCD resb"); + gpio_request(LCD_PANEL_QVGA, "LCD qvga"); + + gpio_direction_output(LCD_PANEL_RESB, 1); + gpio_direction_output(LCD_PANEL_INI, 1); + gpio_direction_output(LCD_PANEL_QVGA, 0); + gpio_direction_output(LCD_PANEL_LR, 1); + gpio_direction_output(LCD_PANEL_UD, 1); + + twl4030_i2c_write_u8(TWL4030_MODULE_LED, 0x11, TWL_LED_LEDEN); + twl4030_i2c_write_u8(TWL4030_MODULE_PWMA, 0x01, TWL_PWMA_PWMAON); + twl4030_i2c_write_u8(TWL4030_MODULE_PWMA, 0x02, TWL_PWMA_PWMAOFF); + bklight_level = 100; + + return 0; +} + +static void omap3evm_panel_cleanup(struct lcd_panel *panel) +{ + gpio_free(LCD_PANEL_QVGA); + gpio_free(LCD_PANEL_RESB); + gpio_free(LCD_PANEL_INI); + gpio_free(LCD_PANEL_UD); + gpio_free(LCD_PANEL_LR); +} + +static int omap3evm_panel_enable(struct lcd_panel *panel) +{ + gpio_set_value(LCD_PANEL_ENABLE_GPIO, 0); + return 0; +} + +static void omap3evm_panel_disable(struct lcd_panel *panel) +{ + gpio_set_value(LCD_PANEL_ENABLE_GPIO, 1); +} + +static unsigned long omap3evm_panel_get_caps(struct lcd_panel *panel) +{ + return 0; +} + +static int omap3evm_bklight_setlevel(struct lcd_panel *panel, + unsigned int level) +{ + u8 c; + if ((level >= 0) && (level <= 100)) { + c = (125 * (100 - level)) / 100 + 2; + twl4030_i2c_write_u8(TWL4030_MODULE_PWMA, c, TWL_PWMA_PWMAOFF); + bklight_level = level; + } + return 0; +} + +static unsigned int omap3evm_bklight_getlevel(struct lcd_panel *panel) +{ + return bklight_level; +} + +static unsigned int omap3evm_bklight_getmaxlevel(struct lcd_panel *panel) +{ + return 100; +} + +struct lcd_panel omap3evm_panel = { + .name = "omap3evm", + .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC | + OMAP_LCDC_INV_HSYNC, + + .bpp = 16, + .data_lines = 18, + .x_res = 480, + .y_res = 640, + .hsw = 3, /* hsync_len (4) - 1 */ + .hfp = 3, /* right_margin (4) - 1 */ + .hbp = 39, /* left_margin (40) - 1 */ + .vsw = 1, /* vsync_len (2) - 1 */ + .vfp = 2, /* lower_margin */ + .vbp = 7, /* upper_margin (8) - 1 */ + + .pixel_clock = 26000, + + .init = omap3evm_panel_init, + .cleanup = omap3evm_panel_cleanup, + .enable = omap3evm_panel_enable, + .disable = omap3evm_panel_disable, + .get_caps = omap3evm_panel_get_caps, + .set_bklight_level = omap3evm_bklight_setlevel, + .get_bklight_level = omap3evm_bklight_getlevel, + .get_bklight_max = omap3evm_bklight_getmaxlevel, +}; + +static int omap3evm_panel_probe(struct platform_device *pdev) +{ + omapfb_register_panel(&omap3evm_panel); + return 0; +} + +static int omap3evm_panel_remove(struct platform_device *pdev) +{ + return 0; +} + +static int omap3evm_panel_suspend(struct platform_device *pdev, + pm_message_t mesg) +{ + return 0; +} + +static int omap3evm_panel_resume(struct platform_device *pdev) +{ + return 0; +} + +struct platform_driver omap3evm_panel_driver = { + .probe = omap3evm_panel_probe, + .remove = omap3evm_panel_remove, + .suspend = omap3evm_panel_suspend, + .resume = omap3evm_panel_resume, + .driver = { + .name = "omap3evm_lcd", + .owner = THIS_MODULE, + }, +}; + +static int __init omap3evm_panel_drv_init(void) +{ + return platform_driver_register(&omap3evm_panel_driver); +} + +static void __exit omap3evm_panel_drv_exit(void) +{ + platform_driver_unregister(&omap3evm_panel_driver); +} + +module_init(omap3evm_panel_drv_init); +module_exit(omap3evm_panel_drv_exit); diff --git a/drivers/video/omap/lcd_osk.c b/drivers/video/omap/lcd_osk.c index 379c96d36da..b3fa88bc626 100644 --- a/drivers/video/omap/lcd_osk.c +++ b/drivers/video/omap/lcd_osk.c @@ -127,12 +127,12 @@ struct platform_driver osk_panel_driver = { }, }; -static int osk_panel_drv_init(void) +static int __init osk_panel_drv_init(void) { return platform_driver_register(&osk_panel_driver); } -static void osk_panel_drv_cleanup(void) +static void __exit osk_panel_drv_cleanup(void) { platform_driver_unregister(&osk_panel_driver); } diff --git a/drivers/video/omap/lcd_overo.c b/drivers/video/omap/lcd_overo.c new file mode 100644 index 00000000000..2bc5c9268e5 --- /dev/null +++ b/drivers/video/omap/lcd_overo.c @@ -0,0 +1,179 @@ +/* + * LCD panel support for the Gumstix Overo + * + * Author: Steve Sakoman <steve@sakoman.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/i2c/twl4030.h> + +#include <mach/gpio.h> +#include <mach/mux.h> +#include <mach/omapfb.h> +#include <asm/mach-types.h> + +#define LCD_ENABLE 144 + +static int overo_panel_init(struct lcd_panel *panel, + struct omapfb_device *fbdev) +{ + if ((gpio_request(LCD_ENABLE, "LCD_ENABLE") == 0) && + (gpio_direction_output(LCD_ENABLE, 1) == 0)) + gpio_export(LCD_ENABLE, 0); + else + printk(KERN_ERR "could not obtain gpio for LCD_ENABLE\n"); + + return 0; +} + +static void overo_panel_cleanup(struct lcd_panel *panel) +{ + gpio_free(LCD_ENABLE); +} + +static int overo_panel_enable(struct lcd_panel *panel) +{ + gpio_set_value(LCD_ENABLE, 1); + return 0; +} + +static void overo_panel_disable(struct lcd_panel *panel) +{ + gpio_set_value(LCD_ENABLE, 0); +} + +static unsigned long overo_panel_get_caps(struct lcd_panel *panel) +{ + return 0; +} + +struct lcd_panel overo_panel = { + .name = "overo", + .config = OMAP_LCDC_PANEL_TFT, + .bpp = 16, + .data_lines = 24, + +#if defined CONFIG_FB_OMAP_031M3R + + /* 640 x 480 @ 60 Hz Reduced blanking VESA CVT 0.31M3-R */ + .x_res = 640, + .y_res = 480, + .hfp = 48, + .hsw = 32, + .hbp = 80, + .vfp = 3, + .vsw = 4, + .vbp = 7, + .pixel_clock = 23500, + +#elif defined CONFIG_FB_OMAP_048M3R + + /* 800 x 600 @ 60 Hz Reduced blanking VESA CVT 0.48M3-R */ + .x_res = 800, + .y_res = 600, + .hfp = 48, + .hsw = 32, + .hbp = 80, + .vfp = 3, + .vsw = 4, + .vbp = 11, + .pixel_clock = 35500, + +#elif defined CONFIG_FB_OMAP_079M3R + + /* 1024 x 768 @ 60 Hz Reduced blanking VESA CVT 0.79M3-R */ + .x_res = 1024, + .y_res = 768, + .hfp = 48, + .hsw = 32, + .hbp = 80, + .vfp = 3, + .vsw = 4, + .vbp = 15, + .pixel_clock = 56000, + +#elif defined CONFIG_FB_OMAP_092M9R + + /* 1280 x 720 @ 60 Hz Reduced blanking VESA CVT 0.92M9-R */ + .x_res = 1280, + .y_res = 720, + .hfp = 48, + .hsw = 32, + .hbp = 80, + .vfp = 3, + .vsw = 5, + .vbp = 13, + .pixel_clock = 64000, + +#else + + /* use 640 x 480 if no config option */ + /* 640 x 480 @ 60 Hz Reduced blanking VESA CVT 0.31M3-R */ + .x_res = 640, + .y_res = 480, + .hfp = 48, + .hsw = 32, + .hbp = 80, + .vfp = 3, + .vsw = 4, + .vbp = 7, + .pixel_clock = 23500, + +#endif + + .init = overo_panel_init, + .cleanup = overo_panel_cleanup, + .enable = overo_panel_enable, + .disable = overo_panel_disable, + .get_caps = overo_panel_get_caps, +}; + +static int overo_panel_probe(struct platform_device *pdev) +{ + omapfb_register_panel(&overo_panel); + return 0; +} + +static int overo_panel_remove(struct platform_device *pdev) +{ + /* omapfb does not have unregister_panel */ + return 0; +} + +static struct platform_driver overo_panel_driver = { + .probe = overo_panel_probe, + .remove = overo_panel_remove, + .driver = { + .name = "overo_lcd", + .owner = THIS_MODULE, + }, +}; + +static int __init overo_panel_drv_init(void) +{ + return platform_driver_register(&overo_panel_driver); +} + +static void __exit overo_panel_drv_exit(void) +{ + platform_driver_unregister(&overo_panel_driver); +} + +module_init(overo_panel_drv_init); +module_exit(overo_panel_drv_exit); diff --git a/drivers/video/omap/lcd_palmte.c b/drivers/video/omap/lcd_palmte.c index 218317366e6..4bf3c79f3cc 100644 --- a/drivers/video/omap/lcd_palmte.c +++ b/drivers/video/omap/lcd_palmte.c @@ -108,12 +108,12 @@ struct platform_driver palmte_panel_driver = { }, }; -static int palmte_panel_drv_init(void) +static int __init palmte_panel_drv_init(void) { return platform_driver_register(&palmte_panel_driver); } -static void palmte_panel_drv_cleanup(void) +static void __exit palmte_panel_drv_cleanup(void) { platform_driver_unregister(&palmte_panel_driver); } diff --git a/drivers/video/omap/lcd_palmtt.c b/drivers/video/omap/lcd_palmtt.c index 57b0f6cf6a5..48ea1f9f2cb 100644 --- a/drivers/video/omap/lcd_palmtt.c +++ b/drivers/video/omap/lcd_palmtt.c @@ -113,12 +113,12 @@ struct platform_driver palmtt_panel_driver = { }, }; -static int palmtt_panel_drv_init(void) +static int __init palmtt_panel_drv_init(void) { return platform_driver_register(&palmtt_panel_driver); } -static void palmtt_panel_drv_cleanup(void) +static void __exit palmtt_panel_drv_cleanup(void) { platform_driver_unregister(&palmtt_panel_driver); } diff --git a/drivers/video/omap/lcd_palmz71.c b/drivers/video/omap/lcd_palmz71.c index d33d78b1172..0697d29b4d3 100644 --- a/drivers/video/omap/lcd_palmz71.c +++ b/drivers/video/omap/lcd_palmz71.c @@ -109,12 +109,12 @@ struct platform_driver palmz71_panel_driver = { }, }; -static int palmz71_panel_drv_init(void) +static int __init palmz71_panel_drv_init(void) { return platform_driver_register(&palmz71_panel_driver); } -static void palmz71_panel_drv_cleanup(void) +static void __exit palmz71_panel_drv_cleanup(void) { platform_driver_unregister(&palmz71_panel_driver); } diff --git a/drivers/video/omap/omapfb_main.c b/drivers/video/omap/omapfb_main.c index 8862233d57b..125e605b8c6 100644 --- a/drivers/video/omap/omapfb_main.c +++ b/drivers/video/omap/omapfb_main.c @@ -67,6 +67,7 @@ static struct caps_table_struct ctrl_caps[] = { { OMAPFB_CAPS_WINDOW_PIXEL_DOUBLE, "pixel double window" }, { OMAPFB_CAPS_WINDOW_SCALE, "scale window" }, { OMAPFB_CAPS_WINDOW_OVERLAY, "overlay window" }, + { OMAPFB_CAPS_WINDOW_ROTATE, "rotate window" }, { OMAPFB_CAPS_SET_BACKLIGHT, "backlight setting" }, }; @@ -215,6 +216,15 @@ static int ctrl_change_mode(struct fb_info *fbi) offset, var->xres_virtual, plane->info.pos_x, plane->info.pos_y, var->xres, var->yres, plane->color_mode); + if (r < 0) + return r; + + if (fbdev->ctrl->set_rotate != NULL) { + r = fbdev->ctrl->set_rotate(var->rotate); + if (r < 0) + return r; + } + if (fbdev->ctrl->set_scale != NULL) r = fbdev->ctrl->set_scale(plane->idx, var->xres, var->yres, @@ -554,7 +564,6 @@ static int set_fb_var(struct fb_info *fbi, var->xoffset = var->xres_virtual - var->xres; if (var->yres + var->yoffset > var->yres_virtual) var->yoffset = var->yres_virtual - var->yres; - line_size = var->xres * bpp / 8; if (plane->color_mode == OMAPFB_COLOR_RGB444) { var->red.offset = 8; var->red.length = 4; @@ -600,7 +609,7 @@ static void omapfb_rotate(struct fb_info *fbi, int rotate) struct omapfb_device *fbdev = plane->fbdev; omapfb_rqueue_lock(fbdev); - if (cpu_is_omap15xx() && rotate != fbi->var.rotate) { + if (rotate != fbi->var.rotate) { struct fb_var_screeninfo *new_var = &fbdev->new_var; memcpy(new_var, &fbi->var, sizeof(*new_var)); @@ -707,28 +716,42 @@ int omapfb_update_window_async(struct fb_info *fbi, void (*callback)(void *), void *callback_data) { + int xres, yres; struct omapfb_plane_struct *plane = fbi->par; struct omapfb_device *fbdev = plane->fbdev; - struct fb_var_screeninfo *var; + struct fb_var_screeninfo *var = &fbi->var; + + switch (var->rotate) { + case 0: + case 180: + xres = fbdev->panel->x_res; + yres = fbdev->panel->y_res; + break; + case 90: + case 270: + xres = fbdev->panel->y_res; + yres = fbdev->panel->x_res; + break; + default: + return -EINVAL; + } - var = &fbi->var; - if (win->x >= var->xres || win->y >= var->yres || - win->out_x > var->xres || win->out_y >= var->yres) + if (win->x >= xres || win->y >= yres || + win->out_x > xres || win->out_y > yres) return -EINVAL; if (!fbdev->ctrl->update_window || fbdev->ctrl->get_update_mode() != OMAPFB_MANUAL_UPDATE) return -ENODEV; - if (win->x + win->width >= var->xres) - win->width = var->xres - win->x; - if (win->y + win->height >= var->yres) - win->height = var->yres - win->y; - /* The out sizes should be cropped to the LCD size */ - if (win->out_x + win->out_width > fbdev->panel->x_res) - win->out_width = fbdev->panel->x_res - win->out_x; - if (win->out_y + win->out_height > fbdev->panel->y_res) - win->out_height = fbdev->panel->y_res - win->out_y; + if (win->x + win->width > xres) + win->width = xres - win->x; + if (win->y + win->height > yres) + win->height = yres - win->y; + if (win->out_x + win->out_width > xres) + win->out_width = xres - win->out_x; + if (win->out_y + win->out_height > yres) + win->out_height = yres - win->out_y; if (!win->width || !win->height || !win->out_width || !win->out_height) return 0; @@ -1699,8 +1722,8 @@ static int omapfb_do_probe(struct platform_device *pdev, pr_info("omapfb: configured for panel %s\n", fbdev->panel->name); - def_vxres = def_vxres ? : fbdev->panel->x_res; - def_vyres = def_vyres ? : fbdev->panel->y_res; + def_vxres = def_vxres ? def_vxres : fbdev->panel->x_res; + def_vyres = def_vyres ? def_vyres : fbdev->panel->y_res; init_state++; @@ -1822,8 +1845,8 @@ static int omapfb_suspend(struct platform_device *pdev, pm_message_t mesg) { struct omapfb_device *fbdev = platform_get_drvdata(pdev); - omapfb_blank(FB_BLANK_POWERDOWN, fbdev->fb_info[0]); - + if (fbdev != NULL) + omapfb_blank(FB_BLANK_POWERDOWN, fbdev->fb_info[0]); return 0; } @@ -1832,7 +1855,8 @@ static int omapfb_resume(struct platform_device *pdev) { struct omapfb_device *fbdev = platform_get_drvdata(pdev); - omapfb_blank(FB_BLANK_UNBLANK, fbdev->fb_info[0]); + if (fbdev != NULL) + omapfb_blank(FB_BLANK_UNBLANK, fbdev->fb_info[0]); return 0; } diff --git a/drivers/video/omap/rfbi.c b/drivers/video/omap/rfbi.c index 9332d6ca645..ee01e84e19c 100644 --- a/drivers/video/omap/rfbi.c +++ b/drivers/video/omap/rfbi.c @@ -57,6 +57,7 @@ #define DISPC_BASE 0x48050400 #define DISPC_CONTROL 0x0040 +#define DISPC_IRQ_FRAMEMASK 0x0001 static struct { void __iomem *base; @@ -553,7 +554,9 @@ static int rfbi_init(struct omapfb_device *fbdev) l = (0x01 << 2); rfbi_write_reg(RFBI_CONTROL, l); - if ((r = omap_dispc_request_irq(rfbi_dma_callback, NULL)) < 0) { + r = omap_dispc_request_irq(DISPC_IRQ_FRAMEMASK, rfbi_dma_callback, + NULL); + if (r < 0) { dev_err(fbdev->dev, "can't get DISPC irq\n"); rfbi_enable_clocks(0); return r; @@ -570,7 +573,7 @@ static int rfbi_init(struct omapfb_device *fbdev) static void rfbi_cleanup(void) { - omap_dispc_free_irq(); + omap_dispc_free_irq(DISPC_IRQ_FRAMEMASK, rfbi_dma_callback, NULL); rfbi_put_clocks(); iounmap(rfbi.base); } |