From 727dc3fde82969609e1e69f1f12de83c2fe41238 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 7 Jul 2009 10:30:02 +0900 Subject: video: sh_mobile_lcdcfb: depends on HAVE_CLK. This deifdefs the driver and adds an explicit HAVE_CLK dependency. Given that all SH platforms provide it, there is no reason to keep this as an ifdef. Other architectures that implement support for this driver will already have to provide clock framework support for timers and so on already, so adding this as an additional dependency is not terribly probematic. Signed-off-by: Paul Mundt --- drivers/video/Kconfig | 2 +- drivers/video/sh_mobile_lcdcfb.c | 16 +--------------- 2 files changed, 2 insertions(+), 16 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 8afcf08eba9..ca330b1b365 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1866,7 +1866,7 @@ config FB_W100 config FB_SH_MOBILE_LCDC tristate "SuperH Mobile LCDC framebuffer support" - depends on FB && SUPERH + depends on FB && SUPERH && HAVE_CLK select FB_SYS_FILLRECT select FB_SYS_COPYAREA select FB_SYS_IMAGEBLIT diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index da983b720f0..65806ec3313 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c @@ -42,11 +42,9 @@ struct sh_mobile_lcdc_chan { struct sh_mobile_lcdc_priv { void __iomem *base; int irq; -#ifdef CONFIG_HAVE_CLK atomic_t clk_usecnt; struct clk *dot_clk; struct clk *clk; -#endif unsigned long lddckr; struct sh_mobile_lcdc_chan ch[2]; int started; @@ -185,7 +183,6 @@ struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = { lcdc_sys_read_data, }; -#ifdef CONFIG_HAVE_CLK static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv) { if (atomic_inc_and_test(&priv->clk_usecnt)) { @@ -203,10 +200,6 @@ static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv) clk_disable(priv->clk); } } -#else -static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv) {} -static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv) {} -#endif static int sh_mobile_lcdc_sginit(struct fb_info *info, struct list_head *pagelist) @@ -515,7 +508,6 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv) board_cfg = &ch->cfg.board_cfg; if (board_cfg->display_off) board_cfg->display_off(board_cfg->board_data); - } /* stop the lcdc */ @@ -574,9 +566,7 @@ static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev, int clock_source, struct sh_mobile_lcdc_priv *priv) { -#ifdef CONFIG_HAVE_CLK char clk_name[8]; -#endif char *str; int icksel; @@ -590,7 +580,6 @@ static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev, priv->lddckr = icksel << 16; -#ifdef CONFIG_HAVE_CLK atomic_set(&priv->clk_usecnt, -1); snprintf(clk_name, sizeof(clk_name), "lcdc%d", pdev->id); priv->clk = clk_get(&pdev->dev, clk_name); @@ -598,7 +587,7 @@ static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev, dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); return PTR_ERR(priv->clk); } - + if (str) { priv->dot_clk = clk_get(&pdev->dev, str); if (IS_ERR(priv->dot_clk)) { @@ -607,7 +596,6 @@ static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev, return PTR_ERR(priv->dot_clk); } } -#endif return 0; } @@ -934,11 +922,9 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev) fb_dealloc_cmap(&info->cmap); } -#ifdef CONFIG_HAVE_CLK if (priv->dot_clk) clk_put(priv->dot_clk); clk_put(priv->clk); -#endif if (priv->base) iounmap(priv->base); -- cgit v1.2.3 From 909f10de5de81668e4d0a401f3cb5ca6b8a3d20d Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Thu, 6 Aug 2009 14:28:12 +0000 Subject: sh: LCDC SYS bus access wait fix Update the SuperH Mobile LCDC driver to wait for SYS bus to become idle after reading or writing. This is needed by the kfr2r09 board, but also fixes potential problems on other boards making use of the LCDC in a SYS configuration. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- drivers/video/sh_mobile_lcdcfb.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/video') diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index 98fb82f1161..d1eb9656ca5 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c @@ -154,6 +154,7 @@ static void lcdc_sys_write_index(void *handle, unsigned long data) lcdc_write(ch->lcdc, _LDDWD0R, data | 0x10000000); lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); lcdc_write(ch->lcdc, _LDDWAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0)); + lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); } static void lcdc_sys_write_data(void *handle, unsigned long data) @@ -163,6 +164,7 @@ static void lcdc_sys_write_data(void *handle, unsigned long data) lcdc_write(ch->lcdc, _LDDWD0R, data | 0x11000000); lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); lcdc_write(ch->lcdc, _LDDWAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0)); + lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); } static unsigned long lcdc_sys_read_data(void *handle) @@ -173,6 +175,7 @@ static unsigned long lcdc_sys_read_data(void *handle) lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); lcdc_write(ch->lcdc, _LDDRAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0)); udelay(1); + lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); return lcdc_read(ch->lcdc, _LDDRDR) & 0xffff; } -- cgit v1.2.3 From ec56b66fed526e3b7dd58dba8945c405448f48d1 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Thu, 6 Aug 2009 14:34:38 +0000 Subject: sh: 18-bit SYS panel fix for SuperH Mobile LCDC Fix the SuperH Mobile LCDC driver to make use of the full 18-bit DRD field in the LDDRDR register. Without this patch only 16-bit register access is possible. Needed by 18-bit SYS panels such as the one used on kfr2r09. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- drivers/video/sh_mobile_lcdcfb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/video') diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index d1eb9656ca5..cff406de3d1 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c @@ -177,7 +177,7 @@ static unsigned long lcdc_sys_read_data(void *handle) udelay(1); lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); - return lcdc_read(ch->lcdc, _LDDRDR) & 0xffff; + return lcdc_read(ch->lcdc, _LDDRDR) & 0x3ffff; } struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = { -- cgit v1.2.3 From 0246c4712c40294bd5e8335f0c15a38c8e52709f Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Fri, 14 Aug 2009 10:49:08 +0000 Subject: video: Runtime PM for SuperH Mobile LCDC This patch modifies the SuperH Mobile LCDC framebuffer driver to support Runtime PM. The driver is using the functions - pm_runtime_get_sync() - pm_runtime_put_sync() to inform the bus code if the hardware is idle or not. If the hardware is idle then the bus code may call the runtime dev_pm_ops callbacks to save and restore state. pm_runtime_resume() is used to allow the driver to access the hardware from probe(). Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- drivers/video/sh_mobile_lcdcfb.c | 156 +++++++++++++++++++++++++++------------ 1 file changed, 110 insertions(+), 46 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index fc3f9662cea..1cb5213c1a0 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -23,33 +24,6 @@ #define PALETTE_NR 16 -struct sh_mobile_lcdc_priv; -struct sh_mobile_lcdc_chan { - struct sh_mobile_lcdc_priv *lcdc; - unsigned long *reg_offs; - unsigned long ldmt1r_value; - unsigned long enabled; /* ME and SE in LDCNT2R */ - struct sh_mobile_lcdc_chan_cfg cfg; - u32 pseudo_palette[PALETTE_NR]; - struct fb_info *info; - dma_addr_t dma_handle; - struct fb_deferred_io defio; - struct scatterlist *sglist; - unsigned long frame_end; - wait_queue_head_t frame_end_wait; -}; - -struct sh_mobile_lcdc_priv { - void __iomem *base; - int irq; - atomic_t clk_usecnt; - struct clk *dot_clk; - struct clk *clk; - unsigned long lddckr; - struct sh_mobile_lcdc_chan ch[2]; - int started; -}; - /* shared registers */ #define _LDDCKR 0x410 #define _LDDCKSTPR 0x414 @@ -63,11 +37,23 @@ struct sh_mobile_lcdc_priv { #define _LDDWAR 0x900 #define _LDDRAR 0x904 +/* shared registers and their order for context save/restore */ +static int lcdc_shared_regs[] = { + _LDDCKR, + _LDDCKSTPR, + _LDINTR, + _LDDDSR, + _LDCNT1R, + _LDCNT2R, +}; +#define NR_SHARED_REGS ARRAY_SIZE(lcdc_shared_regs) + /* per-channel registers */ enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R, - LDSM2R, LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR }; + LDSM2R, LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR, + NR_CH_REGS }; -static unsigned long lcdc_offs_mainlcd[] = { +static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = { [LDDCKPAT1R] = 0x400, [LDDCKPAT2R] = 0x404, [LDMT1R] = 0x418, @@ -85,7 +71,7 @@ static unsigned long lcdc_offs_mainlcd[] = { [LDPMR] = 0x460, }; -static unsigned long lcdc_offs_sublcd[] = { +static unsigned long lcdc_offs_sublcd[NR_CH_REGS] = { [LDDCKPAT1R] = 0x408, [LDDCKPAT2R] = 0x40c, [LDMT1R] = 0x600, @@ -110,6 +96,35 @@ static unsigned long lcdc_offs_sublcd[] = { #define LDINTR_FE 0x00000400 #define LDINTR_FS 0x00000004 +struct sh_mobile_lcdc_priv; +struct sh_mobile_lcdc_chan { + struct sh_mobile_lcdc_priv *lcdc; + unsigned long *reg_offs; + unsigned long ldmt1r_value; + unsigned long enabled; /* ME and SE in LDCNT2R */ + struct sh_mobile_lcdc_chan_cfg cfg; + u32 pseudo_palette[PALETTE_NR]; + unsigned long saved_ch_regs[NR_CH_REGS]; + struct fb_info *info; + dma_addr_t dma_handle; + struct fb_deferred_io defio; + struct scatterlist *sglist; + unsigned long frame_end; + wait_queue_head_t frame_end_wait; +}; + +struct sh_mobile_lcdc_priv { + void __iomem *base; + int irq; + atomic_t hw_usecnt; + struct device *dev; + struct clk *dot_clk; + unsigned long lddckr; + struct sh_mobile_lcdc_chan ch[2]; + unsigned long saved_shared_regs[NR_SHARED_REGS]; + int started; +}; + static void lcdc_write_chan(struct sh_mobile_lcdc_chan *chan, int reg_nr, unsigned long data) { @@ -188,8 +203,8 @@ struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = { static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv) { - if (atomic_inc_and_test(&priv->clk_usecnt)) { - clk_enable(priv->clk); + if (atomic_inc_and_test(&priv->hw_usecnt)) { + pm_runtime_get_sync(priv->dev); if (priv->dot_clk) clk_enable(priv->dot_clk); } @@ -197,10 +212,10 @@ static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv) static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv) { - if (atomic_sub_return(1, &priv->clk_usecnt) == -1) { + if (atomic_sub_return(1, &priv->hw_usecnt) == -1) { if (priv->dot_clk) clk_disable(priv->dot_clk); - clk_disable(priv->clk); + pm_runtime_put(priv->dev); } } @@ -574,7 +589,6 @@ static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev, int clock_source, struct sh_mobile_lcdc_priv *priv) { - char clk_name[8]; char *str; int icksel; @@ -588,23 +602,21 @@ static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev, priv->lddckr = icksel << 16; - atomic_set(&priv->clk_usecnt, -1); - snprintf(clk_name, sizeof(clk_name), "lcdc%d", pdev->id); - priv->clk = clk_get(&pdev->dev, clk_name); - if (IS_ERR(priv->clk)) { - dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); - return PTR_ERR(priv->clk); - } - if (str) { priv->dot_clk = clk_get(&pdev->dev, str); if (IS_ERR(priv->dot_clk)) { dev_err(&pdev->dev, "cannot get dot clock %s\n", str); - clk_put(priv->clk); return PTR_ERR(priv->dot_clk); } } - + atomic_set(&priv->hw_usecnt, -1); + + /* Runtime PM support involves two step for this driver: + * 1) Enable Runtime PM + * 2) Force Runtime PM Resume since hardware is accessed from probe() + */ + pm_runtime_enable(priv->dev); + pm_runtime_resume(priv->dev); return 0; } @@ -722,9 +734,59 @@ static int sh_mobile_lcdc_resume(struct device *dev) return sh_mobile_lcdc_start(platform_get_drvdata(pdev)); } +static int sh_mobile_lcdc_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct sh_mobile_lcdc_priv *p = platform_get_drvdata(pdev); + struct sh_mobile_lcdc_chan *ch; + int k, n; + + /* save per-channel registers */ + for (k = 0; k < ARRAY_SIZE(p->ch); k++) { + ch = &p->ch[k]; + if (!ch->enabled) + continue; + for (n = 0; n < NR_CH_REGS; n++) + ch->saved_ch_regs[n] = lcdc_read_chan(ch, n); + } + + /* save shared registers */ + for (n = 0; n < NR_SHARED_REGS; n++) + p->saved_shared_regs[n] = lcdc_read(p, lcdc_shared_regs[n]); + + /* turn off LCDC hardware */ + lcdc_write(p, _LDCNT1R, 0); + return 0; +} + +static int sh_mobile_lcdc_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct sh_mobile_lcdc_priv *p = platform_get_drvdata(pdev); + struct sh_mobile_lcdc_chan *ch; + int k, n; + + /* restore per-channel registers */ + for (k = 0; k < ARRAY_SIZE(p->ch); k++) { + ch = &p->ch[k]; + if (!ch->enabled) + continue; + for (n = 0; n < NR_CH_REGS; n++) + lcdc_write_chan(ch, n, ch->saved_ch_regs[n]); + } + + /* restore shared registers */ + for (n = 0; n < NR_SHARED_REGS; n++) + lcdc_write(p, lcdc_shared_regs[n], p->saved_shared_regs[n]); + + return 0; +} + static struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = { .suspend = sh_mobile_lcdc_suspend, .resume = sh_mobile_lcdc_resume, + .runtime_suspend = sh_mobile_lcdc_runtime_suspend, + .runtime_resume = sh_mobile_lcdc_runtime_resume, }; static int sh_mobile_lcdc_remove(struct platform_device *pdev); @@ -769,6 +831,7 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev) } priv->irq = i; + priv->dev = &pdev->dev; platform_set_drvdata(pdev, priv); pdata = pdev->dev.platform_data; @@ -940,7 +1003,8 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev) if (priv->dot_clk) clk_put(priv->dot_clk); - clk_put(priv->clk); + + pm_runtime_disable(priv->dev); if (priv->base) iounmap(priv->base); -- cgit v1.2.3 From 941132606c7611246d2034cb7b01f9270c2d1ede Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Fri, 28 Aug 2009 10:50:33 -0700 Subject: OMAP: Remove OMAP_IO_ADDRESS, use OMAP1_IO_ADDRESS and OMAP2_IO_ADDRESS instead Search and replace OMAP_IO_ADDRESS with OMAP1_IO_ADDRESS and OMAP2_IO_ADDRESS, and convert omap_read/write into a functions instead of a macros. Also rename OMAP_MPUIO_VBASE to OMAP1_MPUIO_VBASE. In the long run, most code should use ioremap + __raw_read/write instead. Signed-off-by: Tony Lindgren --- drivers/video/omap/dispc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/omap/dispc.c b/drivers/video/omap/dispc.c index 148cbcc3960..915439dc05a 100644 --- a/drivers/video/omap/dispc.c +++ b/drivers/video/omap/dispc.c @@ -212,9 +212,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) @@ -1421,7 +1421,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; -- cgit v1.2.3 From 9dd38819c2257375ea05bcb92b1f607a1d523c84 Mon Sep 17 00:00:00 2001 From: Phil Edworthy Date: Tue, 15 Sep 2009 12:00:18 +0000 Subject: video: sh_mobile_lcdcfb: implement display panning Signed-off-by: Phil Edworthy Signed-off-by: Guennadi Liakhovetski Signed-off-by: Paul Mundt --- drivers/video/sh_mobile_lcdcfb.c | 76 ++++++++++++++++++++++++++++++++++------ 1 file changed, 66 insertions(+), 10 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index 1cb5213c1a0..7f30cb33a20 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c @@ -31,6 +31,7 @@ #define _LDSR 0x46c #define _LDCNT1R 0x470 #define _LDCNT2R 0x474 +#define _LDRCNTR 0x478 #define _LDDDSR 0x47c #define _LDDWD0R 0x800 #define _LDDRDR 0x840 @@ -94,7 +95,11 @@ static unsigned long lcdc_offs_sublcd[NR_CH_REGS] = { #define DISPLAY_BEU 0x00000008 #define LCDC_ENABLE 0x00000001 #define LDINTR_FE 0x00000400 +#define LDINTR_VSE 0x00000200 +#define LDINTR_VEE 0x00000100 #define LDINTR_FS 0x00000004 +#define LDINTR_VSS 0x00000002 +#define LDINTR_VES 0x00000001 struct sh_mobile_lcdc_priv; struct sh_mobile_lcdc_chan { @@ -110,6 +115,8 @@ struct sh_mobile_lcdc_chan { struct fb_deferred_io defio; struct scatterlist *sglist; unsigned long frame_end; + unsigned long pan_offset; + unsigned long new_pan_offset; wait_queue_head_t frame_end_wait; }; @@ -266,30 +273,46 @@ static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data) struct sh_mobile_lcdc_priv *priv = data; struct sh_mobile_lcdc_chan *ch; unsigned long tmp; + unsigned long ldintr; int is_sub; int k; /* acknowledge interrupt */ - tmp = lcdc_read(priv, _LDINTR); - tmp &= 0xffffff00; /* mask in high 24 bits */ - tmp |= 0x000000ff ^ LDINTR_FS; /* status in low 8 */ + ldintr = tmp = lcdc_read(priv, _LDINTR); + /* + * disable further VSYNC End IRQs, preserve all other enabled IRQs, + * write 0 to bits 0-6 to ack all triggered IRQs. + */ + tmp &= 0xffffff00 & ~LDINTR_VEE; lcdc_write(priv, _LDINTR, tmp); /* figure out if this interrupt is for main or sub lcd */ is_sub = (lcdc_read(priv, _LDSR) & (1 << 10)) ? 1 : 0; - /* wake up channel and disable clocks*/ + /* wake up channel and disable clocks */ for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { ch = &priv->ch[k]; if (!ch->enabled) continue; - if (is_sub == lcdc_chan_is_sublcd(ch)) { - ch->frame_end = 1; - wake_up(&ch->frame_end_wait); + /* Frame Start */ + if (ldintr & LDINTR_FS) { + if (is_sub == lcdc_chan_is_sublcd(ch)) { + ch->frame_end = 1; + wake_up(&ch->frame_end_wait); - sh_mobile_lcdc_clk_off(priv); + sh_mobile_lcdc_clk_off(priv); + } + } + + /* VSYNC End */ + if (ldintr & LDINTR_VES) { + /* Set the source address for the next refresh */ + lcdc_write_chan(ch, LDSA1R, ch->dma_handle + + ch->new_pan_offset); + lcdc_write(ch->lcdc, _LDRCNTR, 0); + ch->pan_offset = ch->new_pan_offset; } } @@ -649,6 +672,9 @@ static struct fb_fix_screeninfo sh_mobile_lcdc_fix = { .type = FB_TYPE_PACKED_PIXELS, .visual = FB_VISUAL_TRUECOLOR, .accel = FB_ACCEL_NONE, + .xpanstep = 0, + .ypanstep = 1, + .ywrapstep = 0, }; static void sh_mobile_lcdc_fillrect(struct fb_info *info, @@ -672,13 +698,38 @@ static void sh_mobile_lcdc_imageblit(struct fb_info *info, sh_mobile_lcdc_deferred_io_touch(info); } +static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct sh_mobile_lcdc_chan *ch = info->par; + + if (info->var.xoffset == var->xoffset && + info->var.yoffset == var->yoffset) + return 0; /* No change, do nothing */ + + ch->new_pan_offset = (var->yoffset * info->fix.line_length) + + (var->xoffset * (info->var.bits_per_pixel / 8)); + + if (ch->new_pan_offset != ch->pan_offset) { + unsigned long ldintr; + ldintr = lcdc_read(ch->lcdc, _LDINTR); + ldintr |= LDINTR_VEE; + lcdc_write(ch->lcdc, _LDINTR, ldintr); + sh_mobile_lcdc_deferred_io_touch(info); + } + + return 0; +} + static struct fb_ops sh_mobile_lcdc_ops = { + .owner = THIS_MODULE, .fb_setcolreg = sh_mobile_lcdc_setcolreg, .fb_read = fb_sys_read, .fb_write = fb_sys_write, .fb_fillrect = sh_mobile_lcdc_fillrect, .fb_copyarea = sh_mobile_lcdc_copyarea, .fb_imageblit = sh_mobile_lcdc_imageblit, + .fb_pan_display = sh_mobile_fb_pan_display, }; static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp) @@ -846,6 +897,8 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev) goto err1; } init_waitqueue_head(&priv->ch[i].frame_end_wait); + priv->ch[j].pan_offset = 0; + priv->ch[j].new_pan_offset = 0; switch (pdata->ch[i].chan) { case LCDC_CHAN_MAINLCD: @@ -888,7 +941,9 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev) info = priv->ch[i].info; info->fbops = &sh_mobile_lcdc_ops; info->var.xres = info->var.xres_virtual = cfg->lcd_cfg.xres; - info->var.yres = info->var.yres_virtual = cfg->lcd_cfg.yres; + info->var.yres = cfg->lcd_cfg.yres; + /* Default Y virtual resolution is 2x panel size */ + info->var.yres_virtual = info->var.yres * 2; info->var.width = cfg->lcd_size_cfg.width; info->var.height = cfg->lcd_size_cfg.height; info->var.activate = FB_ACTIVATE_NOW; @@ -898,7 +953,8 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev) info->fix = sh_mobile_lcdc_fix; info->fix.line_length = cfg->lcd_cfg.xres * (cfg->bpp / 8); - info->fix.smem_len = info->fix.line_length * cfg->lcd_cfg.yres; + info->fix.smem_len = info->fix.line_length * + info->var.yres_virtual; buf = dma_alloc_coherent(&pdev->dev, info->fix.smem_len, &priv->ch[i].dma_handle, GFP_KERNEL); -- cgit v1.2.3 From a6f15ade97989d414e9bf33874c9d5d1f39808ec Mon Sep 17 00:00:00 2001 From: Phil Edworthy Date: Tue, 15 Sep 2009 12:00:30 +0000 Subject: video: sh_mobile_lcdcfb: use both register sets for display panning Switch to using both register sets - side A and side B for display panning. Signed-off-by: Phil Edworthy Signed-off-by: Guennadi Liakhovetski Signed-off-by: Paul Mundt --- drivers/video/sh_mobile_lcdcfb.c | 47 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index 7f30cb33a20..3ad5157f989 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c @@ -23,6 +23,8 @@ #include #define PALETTE_NR 16 +#define SIDE_B_OFFSET 0x1000 +#define MIRROR_OFFSET 0x2000 /* shared registers */ #define _LDDCKR 0x410 @@ -100,6 +102,10 @@ static unsigned long lcdc_offs_sublcd[NR_CH_REGS] = { #define LDINTR_FS 0x00000004 #define LDINTR_VSS 0x00000002 #define LDINTR_VES 0x00000001 +#define LDRCNTR_SRS 0x00020000 +#define LDRCNTR_SRC 0x00010000 +#define LDRCNTR_MRS 0x00000002 +#define LDRCNTR_MRC 0x00000001 struct sh_mobile_lcdc_priv; struct sh_mobile_lcdc_chan { @@ -132,10 +138,39 @@ struct sh_mobile_lcdc_priv { int started; }; +static bool banked(int reg_nr) +{ + switch (reg_nr) { + case LDMT1R: + case LDMT2R: + case LDMT3R: + case LDDFR: + case LDSM1R: + case LDSA1R: + case LDMLSR: + case LDHCNR: + case LDHSYNR: + case LDVLNR: + case LDVSYNR: + return true; + } + return false; +} + static void lcdc_write_chan(struct sh_mobile_lcdc_chan *chan, int reg_nr, unsigned long data) { iowrite32(data, chan->lcdc->base + chan->reg_offs[reg_nr]); + if (banked(reg_nr)) + iowrite32(data, chan->lcdc->base + chan->reg_offs[reg_nr] + + SIDE_B_OFFSET); +} + +static void lcdc_write_chan_mirror(struct sh_mobile_lcdc_chan *chan, + int reg_nr, unsigned long data) +{ + iowrite32(data, chan->lcdc->base + chan->reg_offs[reg_nr] + + MIRROR_OFFSET); } static unsigned long lcdc_read_chan(struct sh_mobile_lcdc_chan *chan, @@ -308,10 +343,16 @@ static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data) /* VSYNC End */ if (ldintr & LDINTR_VES) { + unsigned long ldrcntr = lcdc_read(priv, _LDRCNTR); /* Set the source address for the next refresh */ - lcdc_write_chan(ch, LDSA1R, ch->dma_handle + - ch->new_pan_offset); - lcdc_write(ch->lcdc, _LDRCNTR, 0); + lcdc_write_chan_mirror(ch, LDSA1R, ch->dma_handle + + ch->new_pan_offset); + if (lcdc_chan_is_sublcd(ch)) + lcdc_write(ch->lcdc, _LDRCNTR, + ldrcntr ^ LDRCNTR_SRS); + else + lcdc_write(ch->lcdc, _LDRCNTR, + ldrcntr ^ LDRCNTR_MRS); ch->pan_offset = ch->new_pan_offset; } } -- cgit v1.2.3 From 6d6c971778c5257fc815e1ebe01779fefda6293c Mon Sep 17 00:00:00 2001 From: Jaswinder Singh Rajput Date: Sun, 20 Sep 2009 15:28:01 +0530 Subject: includecheck fix: drivers/video, vgacon.c fix the following 'make includecheck' warning: drivers/video/console/vgacon.c: linux/slab.h is included more than once. Signed-off-by: Jaswinder Singh Rajput Cc: Martin Mares Cc: mchehab@infradead.org Cc: Sam Ravnborg LKML-Reference: <1247067624.4382.88.camel@ht.satnam> --- drivers/video/console/vgacon.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/video') diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index 59d7d5ec17a..74e96cf83b7 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -180,7 +180,6 @@ static inline void vga_set_mem_top(struct vc_data *c) } #ifdef CONFIG_VGACON_SOFT_SCROLLBACK -#include /* software scrollback */ static void *vgacon_scrollback; static int vgacon_scrollback_tail; -- cgit v1.2.3 From 3f6db50f1ebc4fa797445dd89af7211cafc52a92 Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Fri, 3 Jul 2009 14:39:40 +0200 Subject: trivial: media/omap: adding __init/__exit macros to lcd_drivers Trivial patch which adds the __init and __exit macros to the module_init / module_exit functions to several files in drivers/video/omap/ Signed-off-by: Peter Huewe Signed-off-by: Jiri Kosina --- drivers/video/omap/lcd_h3.c | 4 ++-- drivers/video/omap/lcd_h4.c | 4 ++-- drivers/video/omap/lcd_inn1510.c | 4 ++-- drivers/video/omap/lcd_inn1610.c | 4 ++-- drivers/video/omap/lcd_osk.c | 4 ++-- drivers/video/omap/lcd_palmte.c | 4 ++-- drivers/video/omap/lcd_palmtt.c | 4 ++-- drivers/video/omap/lcd_palmz71.c | 4 ++-- 8 files changed, 16 insertions(+), 16 deletions(-) (limited to 'drivers/video') 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_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_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); } -- cgit v1.2.3 From a419aef8b858a2bdb98df60336063d28df4b272f Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 18 Aug 2009 11:18:35 -0700 Subject: trivial: remove unnecessary semicolons Signed-off-by: Joe Perches Signed-off-by: Jiri Kosina --- drivers/video/cfbcopyarea.c | 2 +- drivers/video/imxfb.c | 2 +- drivers/video/s3c2410fb.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/cfbcopyarea.c b/drivers/video/cfbcopyarea.c index df03f3776dc..79e5f40e648 100644 --- a/drivers/video/cfbcopyarea.c +++ b/drivers/video/cfbcopyarea.c @@ -114,7 +114,7 @@ bitcpy(struct fb_info *p, unsigned long __iomem *dst, int dst_idx, d0 >>= right; } else if (src_idx+n <= bits) { // Single source word - d0 <<= left;; + d0 <<= left; } else { // 2 source words d1 = FB_READL(src + 1); diff --git a/drivers/video/imxfb.c b/drivers/video/imxfb.c index 30ae3022f63..66358fa825f 100644 --- a/drivers/video/imxfb.c +++ b/drivers/video/imxfb.c @@ -710,7 +710,7 @@ static int __init imxfb_probe(struct platform_device *pdev) fbi->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(fbi->clk)) { - ret = PTR_ERR(fbi->clk);; + ret = PTR_ERR(fbi->clk); dev_err(&pdev->dev, "unable to get clock: %d\n", ret); goto failed_getclock; } diff --git a/drivers/video/s3c2410fb.c b/drivers/video/s3c2410fb.c index 7da0027e240..5ffca2adc6a 100644 --- a/drivers/video/s3c2410fb.c +++ b/drivers/video/s3c2410fb.c @@ -1119,7 +1119,7 @@ int __init s3c2410fb_init(void) int ret = platform_driver_register(&s3c2410fb_driver); if (ret == 0) - ret = platform_driver_register(&s3c2412fb_driver);; + ret = platform_driver_register(&s3c2412fb_driver); return ret; } -- cgit v1.2.3 From e0626e3844e8f430fc1a4417f523a00797df7ca6 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Tue, 22 Sep 2009 16:46:08 -0700 Subject: spi: prefix modalias with "spi:" This makes it consistent with other buses (platform, i2c, vio, ...). I'm not sure why we use the prefixes, but there must be a reason. This was easy enough to do it, and I did it. Signed-off-by: Anton Vorontsov Cc: David Brownell Cc: David Woodhouse Cc: Grant Likely Cc: Jean Delvare Cc: Ben Dooks Cc: Benjamin Herrenschmidt Cc: Dmitry Torokhov Cc: Samuel Ortiz Cc: "John W. Linville" Acked-by: Mike Frysinger Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/backlight/corgi_lcd.c | 1 + drivers/video/backlight/ltv350qv.c | 1 + drivers/video/backlight/tdo24m.c | 1 + drivers/video/backlight/tosa_lcd.c | 2 +- drivers/video/backlight/vgg2432a4.c | 3 +-- 5 files changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/backlight/corgi_lcd.c b/drivers/video/backlight/corgi_lcd.c index f8a4bb20f41..2211a852af9 100644 --- a/drivers/video/backlight/corgi_lcd.c +++ b/drivers/video/backlight/corgi_lcd.c @@ -639,3 +639,4 @@ module_exit(corgi_lcd_exit); MODULE_DESCRIPTION("LCD and backlight driver for SHARP C7x0/Cxx00"); MODULE_AUTHOR("Eric Miao "); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:corgi-lcd"); diff --git a/drivers/video/backlight/ltv350qv.c b/drivers/video/backlight/ltv350qv.c index 2eb206bf73e..4631ca8fa4a 100644 --- a/drivers/video/backlight/ltv350qv.c +++ b/drivers/video/backlight/ltv350qv.c @@ -328,3 +328,4 @@ module_exit(ltv350qv_exit); MODULE_AUTHOR("Haavard Skinnemoen "); MODULE_DESCRIPTION("Samsung LTV350QV LCD Driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:ltv350qv"); diff --git a/drivers/video/backlight/tdo24m.c b/drivers/video/backlight/tdo24m.c index 51422fc4f60..bbfb502add6 100644 --- a/drivers/video/backlight/tdo24m.c +++ b/drivers/video/backlight/tdo24m.c @@ -472,3 +472,4 @@ module_exit(tdo24m_exit); MODULE_AUTHOR("Eric Miao "); MODULE_DESCRIPTION("Driver for Toppoly TDO24M LCD Panel"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:tdo24m"); diff --git a/drivers/video/backlight/tosa_lcd.c b/drivers/video/backlight/tosa_lcd.c index b7fbc75a62f..50ec17dfc51 100644 --- a/drivers/video/backlight/tosa_lcd.c +++ b/drivers/video/backlight/tosa_lcd.c @@ -300,4 +300,4 @@ module_exit(tosa_lcd_exit); MODULE_AUTHOR("Dmitry Baryshkov"); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("LCD/Backlight control for Sharp SL-6000 PDA"); - +MODULE_ALIAS("spi:tosa-lcd"); diff --git a/drivers/video/backlight/vgg2432a4.c b/drivers/video/backlight/vgg2432a4.c index 8e653b8a6f1..b49063c831e 100644 --- a/drivers/video/backlight/vgg2432a4.c +++ b/drivers/video/backlight/vgg2432a4.c @@ -280,5 +280,4 @@ module_exit(vgg2432a4_exit); MODULE_AUTHOR("Ben Dooks "); MODULE_DESCRIPTION("VGG2432A4 LCD Driver"); MODULE_LICENSE("GPL v2"); - - +MODULE_ALIAS("spi:VGG2432A4"); -- cgit v1.2.3 From 28ff0c12b7069f076f08a936df66f1d5052e9121 Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Tue, 22 Sep 2009 16:46:40 -0700 Subject: omapfb: add support for the Apollon LCD Signed-off-by: Kyungmin Park Signed-off-by: Imre Deak Acked-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/omap/Makefile | 2 + drivers/video/omap/lcd_apollon.c | 138 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 drivers/video/omap/lcd_apollon.c (limited to 'drivers/video') diff --git a/drivers/video/omap/Makefile b/drivers/video/omap/Makefile index ed13889c116..d0534982f6d 100644 --- a/drivers/video/omap/Makefile +++ b/drivers/video/omap/Makefile @@ -24,5 +24,7 @@ objs-$(CONFIG_ARCH_OMAP16XX)$(CONFIG_MACH_OMAP_INNOVATOR) += lcd_inn1610.o objs-$(CONFIG_ARCH_OMAP15XX)$(CONFIG_MACH_OMAP_INNOVATOR) += lcd_inn1510.o objs-y$(CONFIG_MACH_OMAP_OSK) += lcd_osk.o +objs-y$(CONFIG_MACH_OMAP_APOLLON) += lcd_apollon.o + omapfb-objs := $(objs-yy) 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 + * + * 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 +#include + +#include +#include +#include + +/* #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); -- cgit v1.2.3 From 66d2f99d0bb5a2972fb5c1d88b61169510e540d6 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 22 Sep 2009 16:46:41 -0700 Subject: omapfb: add support for MIPI-DCS compatible LCDs Fixed-by: Mike Wege Fixed-by: Arnaud Patard Fixed-by: Timo Savola Fixed-by: Hiroshi DOYU Fixed-by: Trilok Soni Signed-off-by: Imre Deak Signed-off-by: Juha Yrjola Acked-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/omap/Kconfig | 8 + drivers/video/omap/Makefile | 1 + drivers/video/omap/lcd_mipid.c | 625 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 634 insertions(+) create mode 100644 drivers/video/omap/lcd_mipid.c (limited to 'drivers/video') diff --git a/drivers/video/omap/Kconfig b/drivers/video/omap/Kconfig index 44408850e2e..574453f49e2 100644 --- a/drivers/video/omap/Kconfig +++ b/drivers/video/omap/Kconfig @@ -7,6 +7,14 @@ config FB_OMAP help Frame buffer driver for OMAP based boards. +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 diff --git a/drivers/video/omap/Makefile b/drivers/video/omap/Makefile index d0534982f6d..d86d54a63a6 100644 --- a/drivers/video/omap/Makefile +++ b/drivers/video/omap/Makefile @@ -25,6 +25,7 @@ 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_FB_OMAP_LCD_MIPID) += lcd_mipid.o omapfb-objs := $(objs-yy) 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 + * + * 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 + +#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"); -- cgit v1.2.3 From 92e0ed074a4f134084bef47a44e948b6c6429a89 Mon Sep 17 00:00:00 2001 From: Jonathan McDowell Date: Tue, 22 Sep 2009 16:46:44 -0700 Subject: omapfb: add support for the Amstrad Delta LCD This is an updated version of the LCD driver for the Amstrad Delta to take into account the recent changes to the omapfb infrastructure. The Delta features a 480x320 12 bit DSTN panel. Signed-off-by: Jonathan McDowell Signed-off-by: Tony Lindgren Signed-off-by: Imre Deak Acked-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/omap/Makefile | 1 + drivers/video/omap/lcd_ams_delta.c | 137 +++++++++++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 drivers/video/omap/lcd_ams_delta.c (limited to 'drivers/video') diff --git a/drivers/video/omap/Makefile b/drivers/video/omap/Makefile index d86d54a63a6..2bf94ad891f 100644 --- a/drivers/video/omap/Makefile +++ b/drivers/video/omap/Makefile @@ -15,6 +15,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 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 + * + * 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 + +#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); -- cgit v1.2.3 From 3a76a819dda9a6de0ab83d04b48b3735a4d10b89 Mon Sep 17 00:00:00 2001 From: Hunyue Yau Date: Tue, 22 Sep 2009 16:46:45 -0700 Subject: omapfb: add support for the 2430SDP LCD Add glue to control the 2430SDP LCD as a frame buffer device using the existing dispc.c driver under omapfb. Fixed-by: Kevin Hilman Fixed-by: Sergio Aguirre Fixed-by: Francisco Alecrim Fixed-by: Tony Lindgren Fixed-by: David Brownell Signed-off-by: Hunyue Yau Signed-off-by: Kevin Hilman Signed-off-by: Tony Lindgren Signed-off-by: Imre Deak Acked-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/omap/Makefile | 1 + drivers/video/omap/lcd_2430sdp.c | 198 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 199 insertions(+) create mode 100644 drivers/video/omap/lcd_2430sdp.c (limited to 'drivers/video') diff --git a/drivers/video/omap/Makefile b/drivers/video/omap/Makefile index 2bf94ad891f..7a37b0396e9 100644 --- a/drivers/video/omap/Makefile +++ b/drivers/video/omap/Makefile @@ -26,6 +26,7 @@ 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_FB_OMAP_LCD_MIPID) += lcd_mipid.o omapfb-objs := $(objs-yy) diff --git a/drivers/video/omap/lcd_2430sdp.c b/drivers/video/omap/lcd_2430sdp.c new file mode 100644 index 00000000000..284cfcec55d --- /dev/null +++ b/drivers/video/omap/lcd_2430sdp.c @@ -0,0 +1,198 @@ +/* + * LCD panel support for the TI 2430SDP board + * + * Copyright (C) 2007 MontaVista + * Author: Hunyue Yau + * + * 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 +#include +#include +#include +#include + +#include +#include +#include + +#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_PANEL_BACKLIGHT_GPIO 91 +#define LCD_PANEL_ENABLE_GPIO 154 +#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 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); -- cgit v1.2.3 From 42acff34f85c5892c8c762145441e993a5596a13 Mon Sep 17 00:00:00 2001 From: arun c Date: Tue, 22 Sep 2009 16:46:47 -0700 Subject: omapfb: add support for the OMAP2EVM LCD omap2evm LCD supports VGA and QVGA resolution, by default its in VGA mode. Fixed-by: Jarkko Nikula Fixed-by: David Brownell Signed-off-by: Arun C Signed-off-by: Tony Lindgren Signed-off-by: Imre Deak Acked-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/omap/Makefile | 1 + drivers/video/omap/lcd_omap2evm.c | 191 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 192 insertions(+) create mode 100644 drivers/video/omap/lcd_omap2evm.c (limited to 'drivers/video') diff --git a/drivers/video/omap/Makefile b/drivers/video/omap/Makefile index 7a37b0396e9..c2475e34424 100644 --- a/drivers/video/omap/Makefile +++ b/drivers/video/omap/Makefile @@ -27,6 +27,7 @@ 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_OMAP2EVM) += lcd_omap2evm.o objs-y$(CONFIG_FB_OMAP_LCD_MIPID) += lcd_mipid.o omapfb-objs := $(objs-yy) 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 + * + * 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 +#include +#include +#include + +#include +#include +#include + +#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); -- cgit v1.2.3 From fb49b78452503f6cdf8b243ccb40cda81aae9998 Mon Sep 17 00:00:00 2001 From: Kevin Hilman Date: Tue, 22 Sep 2009 16:46:48 -0700 Subject: omapfb: add support for the 3430SDP LCD The 3430SDP uses the same panel as the 2430SDP. The main difference are in the GPIO lines used for panel enable and backlight, and the VAUX register/commands sent to the TWL4030 power subsystem. Also, some misc. whitespace cleanups. Fixed-by: Tony Lindgren Signed-off-by: Kevin Hilman Signed-off-by: Tony Lindgren Signed-off-by: Imre Deak Acked-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/omap/Makefile | 2 ++ drivers/video/omap/lcd_2430sdp.c | 12 ++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/omap/Makefile b/drivers/video/omap/Makefile index c2475e34424..96d2d4349bd 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 @@ -27,6 +28,7 @@ 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_OMAP2EVM) += lcd_omap2evm.o objs-y$(CONFIG_FB_OMAP_LCD_MIPID) += lcd_mipid.o diff --git a/drivers/video/omap/lcd_2430sdp.c b/drivers/video/omap/lcd_2430sdp.c index 284cfcec55d..393712b6f36 100644 --- a/drivers/video/omap/lcd_2430sdp.c +++ b/drivers/video/omap/lcd_2430sdp.c @@ -39,13 +39,17 @@ static unsigned backlight_gpio; static unsigned enable_gpio; -#define LCD_PANEL_BACKLIGHT_GPIO 91 -#define LCD_PANEL_ENABLE_GPIO 154 #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) @@ -146,7 +150,7 @@ struct lcd_panel sdp2430_panel = { .init = sdp2430_panel_init, .cleanup = sdp2430_panel_cleanup, - .enable = sdp2430_panel_enable, + .enable = sdp2430_panel_enable, .disable = sdp2430_panel_disable, .get_caps = sdp2430_panel_get_caps, }; @@ -176,7 +180,7 @@ static int sdp2430_panel_resume(struct platform_device *pdev) struct platform_driver sdp2430_panel_driver = { .probe = sdp2430_panel_probe, .remove = sdp2430_panel_remove, - .suspend = sdp2430_panel_suspend, + .suspend = sdp2430_panel_suspend, .resume = sdp2430_panel_resume, .driver = { .name = "sdp2430_lcd", -- cgit v1.2.3 From 2a8729c47a9dfe5c8ba55883b86b719b33e6e099 Mon Sep 17 00:00:00 2001 From: Steve Sakoman Date: Tue, 22 Sep 2009 16:46:49 -0700 Subject: omapfb: add support for the OMAP3 EVM LCD Add LCD support for OMAP3 EVM Backlight support by Arun C Fixed-by: Jarkko Nikula Fixed-by: David Brownell Signed-off-by: Steve Sakoman Acked-by: Syed Mohammed Khasim Signed-off-by: Tony Lindgren Signed-off-by: Imre Deak Acked-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/omap/Makefile | 1 + drivers/video/omap/lcd_omap3evm.c | 192 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 193 insertions(+) create mode 100644 drivers/video/omap/lcd_omap3evm.c (limited to 'drivers/video') diff --git a/drivers/video/omap/Makefile b/drivers/video/omap/Makefile index 96d2d4349bd..4345157b23e 100644 --- a/drivers/video/omap/Makefile +++ b/drivers/video/omap/Makefile @@ -30,6 +30,7 @@ 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_OMAP2EVM) += lcd_omap2evm.o +objs-y$(CONFIG_MACH_OMAP3EVM) += lcd_omap3evm.o objs-y$(CONFIG_FB_OMAP_LCD_MIPID) += lcd_mipid.o omapfb-objs := $(objs-yy) 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 + * + * 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 +#include +#include +#include + +#include +#include +#include + +#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); -- cgit v1.2.3 From 27969ccc2840c42bfbe4f55d08f0c7ec254d4e93 Mon Sep 17 00:00:00 2001 From: Koen Kooi Date: Tue, 22 Sep 2009 16:46:50 -0700 Subject: omapfb: add support for the OMAP3 Beagle DVI output The default resolution is 1024x768@24bit This version addresses the comments from Felipe Balbi adn Arun Edarath Fixed-by: Felipe Contreras Fixed-by: Steve Sakoman Fixed-by: Jarkko Nikula Fixed-by: David Brownell Signed-off-by: Koen Kooi Signed-off-by: Tony Lindgren Signed-off-by: Imre Deak Acked-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/omap/Makefile | 1 + drivers/video/omap/lcd_omap3beagle.c | 130 +++++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 drivers/video/omap/lcd_omap3beagle.c (limited to 'drivers/video') diff --git a/drivers/video/omap/Makefile b/drivers/video/omap/Makefile index 4345157b23e..9ff1815bed6 100644 --- a/drivers/video/omap/Makefile +++ b/drivers/video/omap/Makefile @@ -31,6 +31,7 @@ objs-y$(CONFIG_MACH_OMAP_2430SDP) += lcd_2430sdp.o objs-y$(CONFIG_MACH_OMAP_3430SDP) += lcd_2430sdp.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 omapfb-objs := $(objs-yy) 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 + * + * 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 +#include +#include +#include + +#include +#include +#include + +#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); -- cgit v1.2.3 From be481941c6ddfe96e5c17a5d6f077b00e7b7bdf1 Mon Sep 17 00:00:00 2001 From: Steve Sakoman Date: Tue, 22 Sep 2009 16:46:51 -0700 Subject: omapfb: add support for the Gumstix Overo LCD Signed-off-by: Steve Sakoman Signed-off-by: Tony Lindgren Signed-off-by: Imre Deak Acked-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/omap/Kconfig | 21 +++++ drivers/video/omap/Makefile | 1 + drivers/video/omap/lcd_overo.c | 179 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 201 insertions(+) create mode 100644 drivers/video/omap/lcd_overo.c (limited to 'drivers/video') diff --git a/drivers/video/omap/Kconfig b/drivers/video/omap/Kconfig index 574453f49e2..eb05e099938 100644 --- a/drivers/video/omap/Kconfig +++ b/drivers/video/omap/Kconfig @@ -7,6 +7,27 @@ config FB_OMAP help Frame buffer driver for OMAP based boards. +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_LCD_MIPID bool "MIPI DBI-C/DCS compatible LCD support" depends on FB_OMAP && SPI_MASTER diff --git a/drivers/video/omap/Makefile b/drivers/video/omap/Makefile index 9ff1815bed6..0d2996a41b3 100644 --- a/drivers/video/omap/Makefile +++ b/drivers/video/omap/Makefile @@ -33,6 +33,7 @@ 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/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 + * + * 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 + +#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); -- cgit v1.2.3 From 8fea8844a72f95ef22b108f5dc5c4237019771dd Mon Sep 17 00:00:00 2001 From: "Stanley.Miao" Date: Tue, 22 Sep 2009 16:46:52 -0700 Subject: omapfb: add support for the ZOOM MDK LCD Add glue to control the OMAP_LDP LCD as a frame buffer device using the existing dispc.c driver under omapfb. Patch updated for mainline kernel. Note that the drivers/video/omap should be updated to pass omap_lcd_config in platform_data. The patch should also be updated to compile if twl4030 is not selected, and eventually to use the regulator framework. Fixed-by: Jarkko Nikula Fixed-by: Tony Lindgren Signed-off-by: Stanley.Miao Signed-off-by: Tony Lindgren Signed-off-by: Imre Deak Acked-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/omap/Kconfig | 4 + drivers/video/omap/Makefile | 1 + drivers/video/omap/lcd_ldp.c | 200 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 205 insertions(+) create mode 100644 drivers/video/omap/lcd_ldp.c (limited to 'drivers/video') diff --git a/drivers/video/omap/Kconfig b/drivers/video/omap/Kconfig index eb05e099938..2bffc07f317 100644 --- a/drivers/video/omap/Kconfig +++ b/drivers/video/omap/Kconfig @@ -7,6 +7,10 @@ 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" diff --git a/drivers/video/omap/Makefile b/drivers/video/omap/Makefile index 0d2996a41b3..b63b198d1f0 100644 --- a/drivers/video/omap/Makefile +++ b/drivers/video/omap/Makefile @@ -29,6 +29,7 @@ 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 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 + * + * 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 +#include +#include +#include + +#include +#include +#include +#include + +#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); -- cgit v1.2.3 From 2f21a62f16136f369788795a98aa5c313261f07b Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Tue, 22 Sep 2009 16:46:53 -0700 Subject: omapfb: add support for rotation on the Blizzard LCD ctrl The LCD controller (EPSON S1D13744) supports rotation (0, 90, 180 and 270 degrees) on hardware just setting the bits 0 and 1 of 0x28 register (LCD Panel Configuration Register). Now it is possible to use this caps only setting the angle degree on var rotate of fb_var_screeninfo using the FBIOPUT_VSCREENINFO ioctl. Fixed-by: Siarhei Siamashka Signed-off-by: Rodrigo Vivi Signed-off-by: Tony Lindgren Signed-off-by: Imre Deak Acked-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/omap/blizzard.c | 91 +++++++++++++++++++++++++++++++++++++--- drivers/video/omap/omapfb_main.c | 52 ++++++++++++++++------- 2 files changed, 124 insertions(+), 19 deletions(-) (limited to 'drivers/video') 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/omapfb_main.c b/drivers/video/omap/omapfb_main.c index 8862233d57b..db05f7e316e 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, @@ -600,7 +610,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 +717,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; -- cgit v1.2.3 From fd0eecbdfbb61076e75d34034cc5cd5ca1321a81 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 22 Sep 2009 16:46:54 -0700 Subject: omapfb: dispc: various typo fixes - value and register offset was swapped in a dispc write - DISPC_CONTROL register was used instead of DISPC_SYSCONFIG - FIFO size bit field had incorrect length for OMAP3 Fixed-by: arun Fixed-by: Kalle Jokiniemi Fixed-by: Andrzej Zaborowski Signed-off-by: Tony Lindgren Signed-off-by: Imre Deak Acked-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/omap/dispc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/omap/dispc.c b/drivers/video/omap/dispc.c index 915439dc05a..f1308aa04fe 100644 --- a/drivers/video/omap/dispc.c +++ b/drivers/video/omap/dispc.c @@ -286,7 +286,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 +294,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); } @@ -1397,7 +1397,7 @@ static int omap_dispc_init(struct omapfb_device *fbdev, int ext_mode, } /* Enable smart idle and autoidle */ - l = dispc_read_reg(DISPC_CONTROL); + l = dispc_read_reg(DISPC_SYSCONFIG); l &= ~((3 << 12) | (3 << 3)); l |= (2 << 12) | (2 << 3) | (1 << 0); dispc_write_reg(DISPC_SYSCONFIG, l); @@ -1409,7 +1409,7 @@ 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); -- cgit v1.2.3 From f9e2bc8d7b8c2d9dd05a6702fce77aca3d4f2320 Mon Sep 17 00:00:00 2001 From: Jouni Hogander Date: Tue, 22 Sep 2009 16:46:55 -0700 Subject: omapfb: dispc: disable iface clocks along with func clocks Leaving interface clocks enabled causes dss pwrdm to stay in active state when mpu is in active state. This fix puts dss to sleep state when it is not needed. Earlier version broke framebuffer on 24xx. This is fixed by enabling clocks before trying to access DISPC_IRQSTATUS register. Signed-off-by: Jouni Hogander Signed-off-by: Tony Lindgren Signed-off-by: Imre Deak Acked-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/omap/dispc.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/omap/dispc.c b/drivers/video/omap/dispc.c index f1308aa04fe..a85694900e4 100644 --- a/drivers/video/omap/dispc.c +++ b/drivers/video/omap/dispc.c @@ -858,8 +858,11 @@ 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; + enable_lcd_clocks(1); + + stat = dispc_read_reg(DISPC_IRQSTATUS); if (stat & DISPC_IRQ_FRAMEMASK) complete(&dispc.frame_done); @@ -875,6 +878,8 @@ static irqreturn_t omap_dispc_irq_handler(int irq, void *dev) dispc_write_reg(DISPC_IRQSTATUS, stat); + enable_lcd_clocks(0); + return IRQ_HANDLED; } @@ -913,18 +918,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 +1365,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 @@ -1469,7 +1468,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 +1485,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); } -- cgit v1.2.3 From 48a00e7fe9a6abeedb62c99ca7b7860754aae3d8 Mon Sep 17 00:00:00 2001 From: Jouni Hogander Date: Tue, 22 Sep 2009 16:46:56 -0700 Subject: omapfb: dispc: enable wake up capability Without wakeup enable omap doesn't wake up on dispc interrupts. This causes problems in a case where mpu is in sleep state and dispc interrupt fires. Signed-off-by: Jouni Hogander Signed-off-by: Tony Lindgren Signed-off-by: Imre Deak Acked-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/omap/dispc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/omap/dispc.c b/drivers/video/omap/dispc.c index a85694900e4..04b7b8ae558 100644 --- a/drivers/video/omap/dispc.c +++ b/drivers/video/omap/dispc.c @@ -1395,10 +1395,10 @@ static int omap_dispc_init(struct omapfb_device *fbdev, int ext_mode, enable_digit_clocks(0); } - /* Enable smart idle and autoidle */ + /* 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); -- cgit v1.2.3 From 4c88ef170f0f9b1f26923b43af526c973c5a74da Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 22 Sep 2009 16:46:57 -0700 Subject: omapfb: dispc: allow multiple external IRQ handlers Previously, the only external (to dispc.c) IRQ handler was RFBI's frame done handler. dispc's IRQ framework was very dumb: you could only have one handler, and the semantics of {request,free}_irq were odd, to say the least. The new framework allows multiple consumers to register arbitrary IRQ masks. Signed-off-by: Daniel Stone Signed-off-by: Tony Lindgren Signed-off-by: Imre Deak Acked-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/omap/dispc.c | 95 ++++++++++++++++++++++++++++------------------ drivers/video/omap/dispc.h | 7 ++-- drivers/video/omap/rfbi.c | 7 +++- 3 files changed, 67 insertions(+), 42 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/omap/dispc.c b/drivers/video/omap/dispc.c index 04b7b8ae558..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]; @@ -809,56 +813,70 @@ 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; + int i = 0; enable_lcd_clocks(1); @@ -873,8 +891,12 @@ 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); @@ -1410,8 +1432,7 @@ static int omap_dispc_init(struct omapfb_device *fbdev, int ext_mode, l = dispc_read_reg(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) { 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/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); } -- cgit v1.2.3 From b6c2b66d353dba229ce167acef49639b9ddf90d9 Mon Sep 17 00:00:00 2001 From: Jouni Hogander Date: Tue, 22 Sep 2009 16:46:58 -0700 Subject: omapfb: suspend/resume only if FB device is already initialized Check wether fbdev is NULL in suspend / resume functions. Fbdev is NULL, if there is no lcd or it is not enabled in configuration. Signed-off-by: Jouni Hogander Signed-off-by: Tony Lindgren Signed-off-by: Imre Deak Acked-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/omap/omapfb_main.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/omap/omapfb_main.c b/drivers/video/omap/omapfb_main.c index db05f7e316e..7eab0f0b4cc 100644 --- a/drivers/video/omap/omapfb_main.c +++ b/drivers/video/omap/omapfb_main.c @@ -1846,8 +1846,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; } @@ -1856,7 +1856,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; } -- cgit v1.2.3 From 5910d35cd6c0122a490000a6de0774ac7ebcc2de Mon Sep 17 00:00:00 2001 From: arun c Date: Tue, 22 Sep 2009 16:46:59 -0700 Subject: omapfb: fix coding style / remove dead line - use __iomem type attribute where appropriate - expand (a ? : b) to (a ? a : b) As suggested by Russel King - remove a dead line from omapfb_main.c Signed-off-by: Arun C Signed-off-by: Tony Lindgren Signed-off-by: Imre Deak Acked-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/omap/omapfb_main.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/omap/omapfb_main.c b/drivers/video/omap/omapfb_main.c index 7eab0f0b4cc..125e605b8c6 100644 --- a/drivers/video/omap/omapfb_main.c +++ b/drivers/video/omap/omapfb_main.c @@ -564,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; @@ -1723,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++; -- cgit v1.2.3 From b1d51dbb79c6398c88b45d4211545621b4d1a11c Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 22 Sep 2009 16:47:00 -0700 Subject: omapfb: add FB manual update option to Kconfig Also move the controller specific options up in the menu, to a more logical spot. Signed-off-by: Imre Deak Acked-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/omap/Kconfig | 49 ++++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 19 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/omap/Kconfig b/drivers/video/omap/Kconfig index 2bffc07f317..551e3e9c4cb 100644 --- a/drivers/video/omap/Kconfig +++ b/drivers/video/omap/Kconfig @@ -32,6 +32,36 @@ config FB_OMAP_092M9R 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 @@ -69,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. -- cgit v1.2.3 From 366ec51ba9622c44191fcf4d77ed4dc84acbdeec Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 22 Sep 2009 16:47:00 -0700 Subject: omapfb: HWA742: fix pointer to be const Fixes the following: warning: assignment discards qualifiers from pointer target type Signed-off-by: Imre Deak Acked-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/omap/hwa742.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/video') 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; -- cgit v1.2.3 From 689620100172e24fdf0981e9978a9559e8769258 Mon Sep 17 00:00:00 2001 From: Ville Syrjala Date: Tue, 22 Sep 2009 16:47:01 -0700 Subject: atyfb: coding style cleanup Fix a bunch of coding style problems in atyfb_base.c. Signed-off-by: Ville Syrjala Cc: "H Hartley Sweeten" Cc: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/aty/atyfb_base.c | 829 +++++++++++++++++++++++------------------ 1 file changed, 458 insertions(+), 371 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c index 63d3739d43a..913b4a47ae5 100644 --- a/drivers/video/aty/atyfb_base.c +++ b/drivers/video/aty/atyfb_base.c @@ -132,7 +132,7 @@ #endif #define PRINTKI(fmt, args...) printk(KERN_INFO "atyfb: " fmt, ## args) -#define PRINTKE(fmt, args...) printk(KERN_ERR "atyfb: " fmt, ## args) +#define PRINTKE(fmt, args...) printk(KERN_ERR "atyfb: " fmt, ## args) #if defined(CONFIG_PM) || defined(CONFIG_PMAC_BACKLIGHT) || \ defined (CONFIG_FB_ATY_GENERIC_LCD) || defined(CONFIG_FB_ATY_BACKLIGHT) @@ -188,24 +188,23 @@ u32 aty_ld_lcd(int index, const struct atyfb_par *par) */ static void ATIReduceRatio(int *Numerator, int *Denominator) { - int Multiplier, Divider, Remainder; + int Multiplier, Divider, Remainder; - Multiplier = *Numerator; - Divider = *Denominator; + Multiplier = *Numerator; + Divider = *Denominator; - while ((Remainder = Multiplier % Divider)) - { - Multiplier = Divider; - Divider = Remainder; - } + while ((Remainder = Multiplier % Divider)) { + Multiplier = Divider; + Divider = Remainder; + } - *Numerator /= Divider; - *Denominator /= Divider; + *Numerator /= Divider; + *Denominator /= Divider; } #endif - /* - * The Hardware parameters for each card - */ +/* + * The Hardware parameters for each card + */ struct pci_mmap_map { unsigned long voff; @@ -223,17 +222,19 @@ static struct fb_fix_screeninfo atyfb_fix __devinitdata = { .ypanstep = 1, }; - /* - * Frame buffer device API - */ +/* + * Frame buffer device API + */ static int atyfb_open(struct fb_info *info, int user); static int atyfb_release(struct fb_info *info, int user); -static int atyfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info); +static int atyfb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info); static int atyfb_set_par(struct fb_info *info); static int atyfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, - u_int transp, struct fb_info *info); -static int atyfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info); + u_int transp, struct fb_info *info); +static int atyfb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info); static int atyfb_blank(int blank, struct fb_info *info); static int atyfb_ioctl(struct fb_info *info, u_int cmd, u_long arg); #ifdef __sparc__ @@ -241,9 +242,9 @@ static int atyfb_mmap(struct fb_info *info, struct vm_area_struct *vma); #endif static int atyfb_sync(struct fb_info *info); - /* - * Internal routines - */ +/* + * Internal routines + */ static int aty_init(struct fb_info *info); @@ -254,8 +255,11 @@ static int store_video_par(char *videopar, unsigned char m64_num); static void aty_get_crtc(const struct atyfb_par *par, struct crtc *crtc); static void aty_set_crtc(const struct atyfb_par *par, const struct crtc *crtc); -static int aty_var_to_crtc(const struct fb_info *info, const struct fb_var_screeninfo *var, struct crtc *crtc); -static int aty_crtc_to_var(const struct crtc *crtc, struct fb_var_screeninfo *var); +static int aty_var_to_crtc(const struct fb_info *info, + const struct fb_var_screeninfo *var, + struct crtc *crtc); +static int aty_crtc_to_var(const struct crtc *crtc, + struct fb_var_screeninfo *var); static void set_off_pitch(struct atyfb_par *par, const struct fb_info *info); #ifdef CONFIG_PPC static int read_aty_sense(const struct atyfb_par *par); @@ -264,9 +268,9 @@ static int read_aty_sense(const struct atyfb_par *par); static DEFINE_MUTEX(reboot_lock); static struct fb_info *reboot_info; - /* - * Interface used by the world - */ +/* + * Interface used by the world + */ static struct fb_var_screeninfo default_var = { /* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */ @@ -452,14 +456,14 @@ static int __devinit correct_chipset(struct atyfb_par *par) type = chip_id & CFG_CHIP_TYPE; rev = (chip_id & CFG_CHIP_REV) >> 24; - switch(par->pci_id) { + switch (par->pci_id) { #ifdef CONFIG_FB_ATY_GX case PCI_CHIP_MACH64GX: - if(type != 0x00d7) + if (type != 0x00d7) return -ENODEV; break; case PCI_CHIP_MACH64CX: - if(type != 0x0057) + if (type != 0x0057) return -ENODEV; break; #endif @@ -564,7 +568,8 @@ static char *aty_xl_ram[8] __devinitdata = { }; #endif /* CONFIG_FB_ATY_CT */ -static u32 atyfb_get_pixclock(struct fb_var_screeninfo *var, struct atyfb_par *par) +static u32 atyfb_get_pixclock(struct fb_var_screeninfo *var, + struct atyfb_par *par) { u32 pixclock = var->pixclock; #ifdef CONFIG_FB_ATY_GENERIC_LCD @@ -572,7 +577,7 @@ static u32 atyfb_get_pixclock(struct fb_var_screeninfo *var, struct atyfb_par *p par->pll.ct.xres = 0; if (par->lcd_table != 0) { lcd_on_off = aty_ld_lcd(LCD_GEN_CNTL, par); - if(lcd_on_off & LCD_ON) { + if (lcd_on_off & LCD_ON) { par->pll.ct.xres = var->xres; pixclock = par->lcd_pixclock; } @@ -584,7 +589,7 @@ static u32 atyfb_get_pixclock(struct fb_var_screeninfo *var, struct atyfb_par *p #if defined(CONFIG_PPC) /* - * Apple monitor sense + * Apple monitor sense */ static int __devinit read_aty_sense(const struct atyfb_par *par) @@ -625,16 +630,16 @@ static int __devinit read_aty_sense(const struct atyfb_par *par) /* ------------------------------------------------------------------------- */ /* - * CRTC programming + * CRTC programming */ static void aty_get_crtc(const struct atyfb_par *par, struct crtc *crtc) { #ifdef CONFIG_FB_ATY_GENERIC_LCD if (par->lcd_table != 0) { - if(!M64_HAS(LT_LCD_REGS)) { - crtc->lcd_index = aty_ld_le32(LCD_INDEX, par); - aty_st_le32(LCD_INDEX, crtc->lcd_index, par); + if (!M64_HAS(LT_LCD_REGS)) { + crtc->lcd_index = aty_ld_le32(LCD_INDEX, par); + aty_st_le32(LCD_INDEX, crtc->lcd_index, par); } crtc->lcd_config_panel = aty_ld_lcd(CNFG_PANEL, par); crtc->lcd_gen_cntl = aty_ld_lcd(LCD_GEN_CNTL, par); @@ -642,7 +647,7 @@ static void aty_get_crtc(const struct atyfb_par *par, struct crtc *crtc) /* switch to non shadow registers */ aty_st_lcd(LCD_GEN_CNTL, crtc->lcd_gen_cntl & - ~(CRTC_RW_SELECT | SHADOW_EN | SHADOW_RW_EN), par); + ~(CRTC_RW_SELECT | SHADOW_EN | SHADOW_RW_EN), par); /* save stretching */ crtc->horz_stretching = aty_ld_lcd(HORZ_STRETCHING, par); @@ -663,7 +668,7 @@ static void aty_get_crtc(const struct atyfb_par *par, struct crtc *crtc) if (par->lcd_table != 0) { /* switch to shadow registers */ aty_st_lcd(LCD_GEN_CNTL, (crtc->lcd_gen_cntl & ~CRTC_RW_SELECT) | - SHADOW_EN | SHADOW_RW_EN, par); + SHADOW_EN | SHADOW_RW_EN, par); crtc->shadow_h_tot_disp = aty_ld_le32(CRTC_H_TOTAL_DISP, par); crtc->shadow_h_sync_strt_wid = aty_ld_le32(CRTC_H_SYNC_STRT_WID, par); @@ -680,21 +685,20 @@ static void aty_set_crtc(const struct atyfb_par *par, const struct crtc *crtc) #ifdef CONFIG_FB_ATY_GENERIC_LCD if (par->lcd_table != 0) { /* stop CRTC */ - aty_st_le32(CRTC_GEN_CNTL, crtc->gen_cntl & ~(CRTC_EXT_DISP_EN | CRTC_EN), par); + aty_st_le32(CRTC_GEN_CNTL, crtc->gen_cntl & + ~(CRTC_EXT_DISP_EN | CRTC_EN), par); /* update non-shadow registers first */ aty_st_lcd(CNFG_PANEL, crtc->lcd_config_panel, par); aty_st_lcd(LCD_GEN_CNTL, crtc->lcd_gen_cntl & - ~(CRTC_RW_SELECT | SHADOW_EN | SHADOW_RW_EN), par); + ~(CRTC_RW_SELECT | SHADOW_EN | SHADOW_RW_EN), par); /* temporarily disable stretching */ - aty_st_lcd(HORZ_STRETCHING, - crtc->horz_stretching & - ~(HORZ_STRETCH_MODE | HORZ_STRETCH_EN), par); - aty_st_lcd(VERT_STRETCHING, - crtc->vert_stretching & - ~(VERT_STRETCH_RATIO1 | VERT_STRETCH_RATIO2 | - VERT_STRETCH_USE0 | VERT_STRETCH_EN), par); + aty_st_lcd(HORZ_STRETCHING, crtc->horz_stretching & + ~(HORZ_STRETCH_MODE | HORZ_STRETCH_EN), par); + aty_st_lcd(VERT_STRETCHING, crtc->vert_stretching & + ~(VERT_STRETCH_RATIO1 | VERT_STRETCH_RATIO2 | + VERT_STRETCH_USE0 | VERT_STRETCH_EN), par); } #endif /* turn off CRT */ @@ -702,17 +706,19 @@ static void aty_set_crtc(const struct atyfb_par *par, const struct crtc *crtc) DPRINTK("setting up CRTC\n"); DPRINTK("set primary CRT to %ix%i %c%c composite %c\n", - ((((crtc->h_tot_disp>>16) & 0xff) + 1)<<3), (((crtc->v_tot_disp>>16) & 0x7ff) + 1), - (crtc->h_sync_strt_wid & 0x200000)?'N':'P', (crtc->v_sync_strt_wid & 0x200000)?'N':'P', - (crtc->gen_cntl & CRTC_CSYNC_EN)?'P':'N'); - - DPRINTK("CRTC_H_TOTAL_DISP: %x\n",crtc->h_tot_disp); - DPRINTK("CRTC_H_SYNC_STRT_WID: %x\n",crtc->h_sync_strt_wid); - DPRINTK("CRTC_V_TOTAL_DISP: %x\n",crtc->v_tot_disp); - DPRINTK("CRTC_V_SYNC_STRT_WID: %x\n",crtc->v_sync_strt_wid); + ((((crtc->h_tot_disp >> 16) & 0xff) + 1) << 3), + (((crtc->v_tot_disp >> 16) & 0x7ff) + 1), + (crtc->h_sync_strt_wid & 0x200000) ? 'N' : 'P', + (crtc->v_sync_strt_wid & 0x200000) ? 'N' : 'P', + (crtc->gen_cntl & CRTC_CSYNC_EN) ? 'P' : 'N'); + + DPRINTK("CRTC_H_TOTAL_DISP: %x\n", crtc->h_tot_disp); + DPRINTK("CRTC_H_SYNC_STRT_WID: %x\n", crtc->h_sync_strt_wid); + DPRINTK("CRTC_V_TOTAL_DISP: %x\n", crtc->v_tot_disp); + DPRINTK("CRTC_V_SYNC_STRT_WID: %x\n", crtc->v_sync_strt_wid); DPRINTK("CRTC_OFF_PITCH: %x\n", crtc->off_pitch); DPRINTK("CRTC_VLINE_CRNT_VLINE: %x\n", crtc->vline_crnt_vline); - DPRINTK("CRTC_GEN_CNTL: %x\n",crtc->gen_cntl); + DPRINTK("CRTC_GEN_CNTL: %x\n", crtc->gen_cntl); aty_st_le32(CRTC_H_TOTAL_DISP, crtc->h_tot_disp, par); aty_st_le32(CRTC_H_SYNC_STRT_WID, crtc->h_sync_strt_wid, par); @@ -732,16 +738,22 @@ static void aty_set_crtc(const struct atyfb_par *par, const struct crtc *crtc) if (par->lcd_table != 0) { /* switch to shadow registers */ aty_st_lcd(LCD_GEN_CNTL, (crtc->lcd_gen_cntl & ~CRTC_RW_SELECT) | - (SHADOW_EN | SHADOW_RW_EN), par); + SHADOW_EN | SHADOW_RW_EN, par); DPRINTK("set shadow CRT to %ix%i %c%c\n", - ((((crtc->shadow_h_tot_disp>>16) & 0xff) + 1)<<3), (((crtc->shadow_v_tot_disp>>16) & 0x7ff) + 1), - (crtc->shadow_h_sync_strt_wid & 0x200000)?'N':'P', (crtc->shadow_v_sync_strt_wid & 0x200000)?'N':'P'); - - DPRINTK("SHADOW CRTC_H_TOTAL_DISP: %x\n", crtc->shadow_h_tot_disp); - DPRINTK("SHADOW CRTC_H_SYNC_STRT_WID: %x\n", crtc->shadow_h_sync_strt_wid); - DPRINTK("SHADOW CRTC_V_TOTAL_DISP: %x\n", crtc->shadow_v_tot_disp); - DPRINTK("SHADOW CRTC_V_SYNC_STRT_WID: %x\n", crtc->shadow_v_sync_strt_wid); + ((((crtc->shadow_h_tot_disp >> 16) & 0xff) + 1) << 3), + (((crtc->shadow_v_tot_disp >> 16) & 0x7ff) + 1), + (crtc->shadow_h_sync_strt_wid & 0x200000) ? 'N' : 'P', + (crtc->shadow_v_sync_strt_wid & 0x200000) ? 'N' : 'P'); + + DPRINTK("SHADOW CRTC_H_TOTAL_DISP: %x\n", + crtc->shadow_h_tot_disp); + DPRINTK("SHADOW CRTC_H_SYNC_STRT_WID: %x\n", + crtc->shadow_h_sync_strt_wid); + DPRINTK("SHADOW CRTC_V_TOTAL_DISP: %x\n", + crtc->shadow_v_tot_disp); + DPRINTK("SHADOW CRTC_V_SYNC_STRT_WID: %x\n", + crtc->shadow_v_sync_strt_wid); aty_st_le32(CRTC_H_TOTAL_DISP, crtc->shadow_h_tot_disp, par); aty_st_le32(CRTC_H_SYNC_STRT_WID, crtc->shadow_h_sync_strt_wid, par); @@ -752,16 +764,16 @@ static void aty_set_crtc(const struct atyfb_par *par, const struct crtc *crtc) DPRINTK("LCD_GEN_CNTL: %x\n", crtc->lcd_gen_cntl); DPRINTK("HORZ_STRETCHING: %x\n", crtc->horz_stretching); DPRINTK("VERT_STRETCHING: %x\n", crtc->vert_stretching); - if(!M64_HAS(LT_LCD_REGS)) - DPRINTK("EXT_VERT_STRETCH: %x\n", crtc->ext_vert_stretch); + if (!M64_HAS(LT_LCD_REGS)) + DPRINTK("EXT_VERT_STRETCH: %x\n", crtc->ext_vert_stretch); aty_st_lcd(LCD_GEN_CNTL, crtc->lcd_gen_cntl, par); aty_st_lcd(HORZ_STRETCHING, crtc->horz_stretching, par); aty_st_lcd(VERT_STRETCHING, crtc->vert_stretching, par); - if(!M64_HAS(LT_LCD_REGS)) { - aty_st_lcd(EXT_VERT_STRETCH, crtc->ext_vert_stretch, par); - aty_ld_le32(LCD_INDEX, par); - aty_st_le32(LCD_INDEX, crtc->lcd_index, par); + if (!M64_HAS(LT_LCD_REGS)) { + aty_st_lcd(EXT_VERT_STRETCH, crtc->ext_vert_stretch, par); + aty_ld_le32(LCD_INDEX, par); + aty_st_le32(LCD_INDEX, crtc->lcd_index, par); } } #endif /* CONFIG_FB_ATY_GENERIC_LCD */ @@ -779,7 +791,8 @@ static u32 calc_line_length(struct atyfb_par *par, u32 vxres, u32 bpp) } static int aty_var_to_crtc(const struct fb_info *info, - const struct fb_var_screeninfo *var, struct crtc *crtc) + const struct fb_var_screeninfo *var, + struct crtc *crtc) { struct atyfb_par *par = (struct atyfb_par *) info->par; u32 xres, yres, vxres, vyres, xoffset, yoffset, bpp; @@ -814,34 +827,32 @@ static int aty_var_to_crtc(const struct fb_info *info, if (bpp <= 8) { bpp = 8; pix_width = CRTC_PIX_WIDTH_8BPP; - dp_pix_width = - HOST_8BPP | SRC_8BPP | DST_8BPP | - BYTE_ORDER_LSB_TO_MSB; + dp_pix_width = HOST_8BPP | SRC_8BPP | DST_8BPP | + BYTE_ORDER_LSB_TO_MSB; dp_chain_mask = DP_CHAIN_8BPP; } else if (bpp <= 15) { bpp = 16; pix_width = CRTC_PIX_WIDTH_15BPP; dp_pix_width = HOST_15BPP | SRC_15BPP | DST_15BPP | - BYTE_ORDER_LSB_TO_MSB; + BYTE_ORDER_LSB_TO_MSB; dp_chain_mask = DP_CHAIN_15BPP; } else if (bpp <= 16) { bpp = 16; pix_width = CRTC_PIX_WIDTH_16BPP; dp_pix_width = HOST_16BPP | SRC_16BPP | DST_16BPP | - BYTE_ORDER_LSB_TO_MSB; + BYTE_ORDER_LSB_TO_MSB; dp_chain_mask = DP_CHAIN_16BPP; } else if (bpp <= 24 && M64_HAS(INTEGRATED)) { bpp = 24; pix_width = CRTC_PIX_WIDTH_24BPP; - dp_pix_width = - HOST_8BPP | SRC_8BPP | DST_8BPP | - BYTE_ORDER_LSB_TO_MSB; + dp_pix_width = HOST_8BPP | SRC_8BPP | DST_8BPP | + BYTE_ORDER_LSB_TO_MSB; dp_chain_mask = DP_CHAIN_24BPP; } else if (bpp <= 32) { bpp = 32; pix_width = CRTC_PIX_WIDTH_32BPP; dp_pix_width = HOST_32BPP | SRC_32BPP | DST_32BPP | - BYTE_ORDER_LSB_TO_MSB; + BYTE_ORDER_LSB_TO_MSB; dp_chain_mask = DP_CHAIN_32BPP; } else FAIL("invalid bpp"); @@ -854,9 +865,9 @@ static int aty_var_to_crtc(const struct fb_info *info, h_sync_pol = sync & FB_SYNC_HOR_HIGH_ACT ? 0 : 1; v_sync_pol = sync & FB_SYNC_VERT_HIGH_ACT ? 0 : 1; - if((xres > 1600) || (yres > 1200)) { + if ((xres > 1600) || (yres > 1200)) { FAIL("MACH64 chips are designed for max 1600x1200\n" - "select anoter resolution."); + "select anoter resolution."); } h_sync_strt = h_disp + var->right_margin; h_sync_end = h_sync_strt + var->hsync_len; @@ -869,11 +880,12 @@ static int aty_var_to_crtc(const struct fb_info *info, #ifdef CONFIG_FB_ATY_GENERIC_LCD if (par->lcd_table != 0) { - if(!M64_HAS(LT_LCD_REGS)) { - u32 lcd_index = aty_ld_le32(LCD_INDEX, par); - crtc->lcd_index = lcd_index & - ~(LCD_INDEX_MASK | LCD_DISPLAY_DIS | LCD_SRC_SEL | CRTC2_DISPLAY_DIS); - aty_st_le32(LCD_INDEX, lcd_index, par); + if (!M64_HAS(LT_LCD_REGS)) { + u32 lcd_index = aty_ld_le32(LCD_INDEX, par); + crtc->lcd_index = lcd_index & + ~(LCD_INDEX_MASK | LCD_DISPLAY_DIS | + LCD_SRC_SEL | CRTC2_DISPLAY_DIS); + aty_st_le32(LCD_INDEX, lcd_index, par); } if (!M64_HAS(MOBIL_BUS)) @@ -888,12 +900,14 @@ static int aty_var_to_crtc(const struct fb_info *info, USE_SHADOWED_ROWCUR | SHADOW_EN | SHADOW_RW_EN); crtc->lcd_gen_cntl |= DONT_SHADOW_VPAR | LOCK_8DOT; - if((crtc->lcd_gen_cntl & LCD_ON) && - ((xres > par->lcd_width) || (yres > par->lcd_height))) { - /* We cannot display the mode on the LCD. If the CRT is enabled - we can turn off the LCD. - If the CRT is off, it isn't a good idea to switch it on; we don't - know if one is connected. So it's better to fail then. + if ((crtc->lcd_gen_cntl & LCD_ON) && + ((xres > par->lcd_width) || (yres > par->lcd_height))) { + /* + * We cannot display the mode on the LCD. If the CRT is + * enabled we can turn off the LCD. + * If the CRT is off, it isn't a good idea to switch it + * on; we don't know if one is connected. So it's better + * to fail then. */ if (crtc->lcd_gen_cntl & CRT_ON) { if (!(var->activate & FB_ACTIVATE_TEST)) @@ -916,17 +930,18 @@ static int aty_var_to_crtc(const struct fb_info *info, vmode &= ~(FB_VMODE_DOUBLE | FB_VMODE_INTERLACED); - /* This is horror! When we simulate, say 640x480 on an 800x600 - LCD monitor, the CRTC should be programmed 800x600 values for - the non visible part, but 640x480 for the visible part. - This code has been tested on a laptop with it's 1400x1050 LCD - monitor and a conventional monitor both switched on. - Tested modes: 1280x1024, 1152x864, 1024x768, 800x600, - works with little glitches also with DOUBLESCAN modes + /* + * This is horror! When we simulate, say 640x480 on an 800x600 + * LCD monitor, the CRTC should be programmed 800x600 values for + * the non visible part, but 640x480 for the visible part. + * This code has been tested on a laptop with it's 1400x1050 LCD + * monitor and a conventional monitor both switched on. + * Tested modes: 1280x1024, 1152x864, 1024x768, 800x600, + * works with little glitches also with DOUBLESCAN modes */ if (yres < par->lcd_height) { VScan = par->lcd_height / yres; - if(VScan > 1) { + if (VScan > 1) { VScan = 2; vmode |= FB_VMODE_DOUBLE; } @@ -952,7 +967,7 @@ static int aty_var_to_crtc(const struct fb_info *info, FAIL_MAX("h_disp too large", h_disp, 0xff); FAIL_MAX("h_sync_strt too large", h_sync_strt, 0x1ff); /*FAIL_MAX("h_sync_wid too large", h_sync_wid, 0x1f);*/ - if(h_sync_wid > 0x1f) + if (h_sync_wid > 0x1f) h_sync_wid = 0x1f; FAIL_MAX("h_total too large", h_total, 0x1ff); @@ -978,7 +993,7 @@ static int aty_var_to_crtc(const struct fb_info *info, FAIL_MAX("v_disp too large", v_disp, 0x7ff); FAIL_MAX("v_sync_stsrt too large", v_sync_strt, 0x7ff); /*FAIL_MAX("v_sync_wid too large", v_sync_wid, 0x1f);*/ - if(v_sync_wid > 0x1f) + if (v_sync_wid > 0x1f) v_sync_wid = 0x1f; FAIL_MAX("v_total too large", v_total, 0x7ff); @@ -995,11 +1010,13 @@ static int aty_var_to_crtc(const struct fb_info *info, ((line_length / bpp) << 22); crtc->vline_crnt_vline = 0; - crtc->h_tot_disp = h_total | (h_disp<<16); - crtc->h_sync_strt_wid = (h_sync_strt & 0xff) | (h_sync_dly<<8) | - ((h_sync_strt & 0x100)<<4) | (h_sync_wid<<16) | (h_sync_pol<<21); - crtc->v_tot_disp = v_total | (v_disp<<16); - crtc->v_sync_strt_wid = v_sync_strt | (v_sync_wid<<16) | (v_sync_pol<<21); + crtc->h_tot_disp = h_total | (h_disp << 16); + crtc->h_sync_strt_wid = (h_sync_strt & 0xff) | (h_sync_dly << 8) | + ((h_sync_strt & 0x100) << 4) | (h_sync_wid << 16) | + (h_sync_pol << 21); + crtc->v_tot_disp = v_total | (v_disp << 16); + crtc->v_sync_strt_wid = v_sync_strt | (v_sync_wid << 16) | + (v_sync_pol << 21); /* crtc->gen_cntl = aty_ld_le32(CRTC_GEN_CNTL, par) & CRTC_PRESERVED_MASK; */ crtc->gen_cntl = CRTC_EXT_DISP_EN | CRTC_EN | pix_width | c_sync; @@ -1014,13 +1031,15 @@ static int aty_var_to_crtc(const struct fb_info *info, #ifdef CONFIG_FB_ATY_GENERIC_LCD if (par->lcd_table != 0) { vdisplay = yres; - if(vmode & FB_VMODE_DOUBLE) + if (vmode & FB_VMODE_DOUBLE) vdisplay <<= 1; crtc->gen_cntl &= ~(CRTC2_EN | CRTC2_PIX_WIDTH); crtc->lcd_gen_cntl &= ~(HORZ_DIVBY2_EN | DIS_HOR_CRT_DIVBY2 | - /*TVCLK_PM_EN | VCLK_DAC_PM_EN |*/ - USE_SHADOWED_VEND | USE_SHADOWED_ROWCUR | SHADOW_EN | SHADOW_RW_EN); - crtc->lcd_gen_cntl |= (DONT_SHADOW_VPAR/* | LOCK_8DOT*/); + /*TVCLK_PM_EN | VCLK_DAC_PM_EN |*/ + USE_SHADOWED_VEND | + USE_SHADOWED_ROWCUR | + SHADOW_EN | SHADOW_RW_EN); + crtc->lcd_gen_cntl |= DONT_SHADOW_VPAR/* | LOCK_8DOT*/; /* MOBILITY M1 tested, FIXME: LT */ crtc->horz_stretching = aty_ld_lcd(HORZ_STRETCHING, par); @@ -1028,28 +1047,32 @@ static int aty_var_to_crtc(const struct fb_info *info, crtc->ext_vert_stretch = aty_ld_lcd(EXT_VERT_STRETCH, par) & ~(AUTO_VERT_RATIO | VERT_STRETCH_MODE | VERT_STRETCH_RATIO3); - crtc->horz_stretching &= - ~(HORZ_STRETCH_RATIO | HORZ_STRETCH_LOOP | AUTO_HORZ_RATIO | - HORZ_STRETCH_MODE | HORZ_STRETCH_EN); + crtc->horz_stretching &= ~(HORZ_STRETCH_RATIO | + HORZ_STRETCH_LOOP | AUTO_HORZ_RATIO | + HORZ_STRETCH_MODE | HORZ_STRETCH_EN); if (xres < par->lcd_width && crtc->lcd_gen_cntl & LCD_ON) { do { /* - * The horizontal blender misbehaves when HDisplay is less than a - * a certain threshold (440 for a 1024-wide panel). It doesn't - * stretch such modes enough. Use pixel replication instead of - * blending to stretch modes that can be made to exactly fit the - * panel width. The undocumented "NoLCDBlend" option allows the - * pixel-replicated mode to be slightly wider or narrower than the - * panel width. It also causes a mode that is exactly half as wide - * as the panel to be pixel-replicated, rather than blended. - */ + * The horizontal blender misbehaves when + * HDisplay is less than a certain threshold + * (440 for a 1024-wide panel). It doesn't + * stretch such modes enough. Use pixel + * replication instead of blending to stretch + * modes that can be made to exactly fit the + * panel width. The undocumented "NoLCDBlend" + * option allows the pixel-replicated mode to + * be slightly wider or narrower than the + * panel width. It also causes a mode that is + * exactly half as wide as the panel to be + * pixel-replicated, rather than blended. + */ int HDisplay = xres & ~7; int nStretch = par->lcd_width / HDisplay; int Remainder = par->lcd_width % HDisplay; if ((!Remainder && ((nStretch > 2))) || - (((HDisplay * 16) / par->lcd_width) < 7)) { - static const char StretchLoops[] = {10, 12, 13, 15, 16}; + (((HDisplay * 16) / par->lcd_width) < 7)) { + static const char StretchLoops[] = { 10, 12, 13, 15, 16 }; int horz_stretch_loop = -1, BestRemainder; int Numerator = HDisplay, Denominator = par->lcd_width; int Index = 5; @@ -1098,12 +1121,12 @@ static int aty_var_to_crtc(const struct fb_info *info, (((vdisplay * (VERT_STRETCH_RATIO0 + 1)) / par->lcd_height) & VERT_STRETCH_RATIO0)); if (!M64_HAS(LT_LCD_REGS) && - xres <= (M64_HAS(MOBIL_BUS)?1024:800)) + xres <= (M64_HAS(MOBIL_BUS) ? 1024 : 800)) crtc->ext_vert_stretch |= VERT_STRETCH_MODE; } else { /* - * Don't use vertical blending if the mode is too wide or not - * vertically stretched. + * Don't use vertical blending if the mode is too wide + * or not vertically stretched. */ crtc->vert_stretching = 0; } @@ -1125,11 +1148,11 @@ static int aty_var_to_crtc(const struct fb_info *info, return 0; } -static int aty_crtc_to_var(const struct crtc *crtc, struct fb_var_screeninfo *var) +static int aty_crtc_to_var(const struct crtc *crtc, + struct fb_var_screeninfo *var) { u32 xres, yres, bpp, left, right, upper, lower, hslen, vslen, sync; - u32 h_total, h_disp, h_sync_strt, h_sync_dly, h_sync_wid, - h_sync_pol; + u32 h_total, h_disp, h_sync_strt, h_sync_dly, h_sync_wid, h_sync_pol; u32 v_total, v_disp, v_sync_strt, v_sync_wid, v_sync_pol, c_sync; u32 pix_width; u32 double_scan, interlace; @@ -1161,8 +1184,8 @@ static int aty_crtc_to_var(const struct crtc *crtc, struct fb_var_screeninfo *va lower = v_sync_strt - v_disp; vslen = v_sync_wid; sync = (h_sync_pol ? 0 : FB_SYNC_HOR_HIGH_ACT) | - (v_sync_pol ? 0 : FB_SYNC_VERT_HIGH_ACT) | - (c_sync ? FB_SYNC_COMP_HIGH_ACT : 0); + (v_sync_pol ? 0 : FB_SYNC_VERT_HIGH_ACT) | + (c_sync ? FB_SYNC_COMP_HIGH_ACT : 0); switch (pix_width) { #if 0 @@ -1252,20 +1275,21 @@ static int aty_crtc_to_var(const struct crtc *crtc, struct fb_var_screeninfo *va var->vsync_len = vslen; var->sync = sync; var->vmode = FB_VMODE_NONINTERLACED; - /* In double scan mode, the vertical parameters are doubled, so we need to - half them to get the right values. - In interlaced mode the values are already correct, so no correction is - necessary. + /* + * In double scan mode, the vertical parameters are doubled, + * so we need to halve them to get the right values. + * In interlaced mode the values are already correct, + * so no correction is necessary. */ if (interlace) var->vmode = FB_VMODE_INTERLACED; if (double_scan) { var->vmode = FB_VMODE_DOUBLE; - var->yres>>=1; - var->upper_margin>>=1; - var->lower_margin>>=1; - var->vsync_len>>=1; + var->yres >>= 1; + var->upper_margin >>= 1; + var->lower_margin >>= 1; + var->vsync_len >>= 1; } return 0; @@ -1286,7 +1310,8 @@ static int atyfb_set_par(struct fb_info *info) if (par->asleep) return 0; - if ((err = aty_var_to_crtc(info, var, &par->crtc))) + err = aty_var_to_crtc(info, var, &par->crtc); + if (err) return err; pixclock = atyfb_get_pixclock(var, par); @@ -1295,7 +1320,9 @@ static int atyfb_set_par(struct fb_info *info) PRINTKE("Invalid pixclock\n"); return -EINVAL; } else { - if((err = par->pll_ops->var_to_pll(info, pixclock, var->bits_per_pixel, &par->pll))) + err = par->pll_ops->var_to_pll(info, pixclock, + var->bits_per_pixel, &par->pll); + if (err) return err; } @@ -1313,22 +1340,23 @@ static int atyfb_set_par(struct fb_info *info) wait_for_idle(par); aty_set_crtc(par, &par->crtc); - par->dac_ops->set_dac(info, &par->pll, var->bits_per_pixel, par->accel_flags); + par->dac_ops->set_dac(info, &par->pll, + var->bits_per_pixel, par->accel_flags); par->pll_ops->set_pll(info, &par->pll); #ifdef DEBUG - if(par->pll_ops && par->pll_ops->pll_to_var) - pixclock_in_ps = par->pll_ops->pll_to_var(info, &(par->pll)); + if (par->pll_ops && par->pll_ops->pll_to_var) + pixclock_in_ps = par->pll_ops->pll_to_var(info, &par->pll); else pixclock_in_ps = 0; - if(0 == pixclock_in_ps) { + if (0 == pixclock_in_ps) { PRINTKE("ALERT ops->pll_to_var get 0\n"); pixclock_in_ps = pixclock; } memset(&debug, 0, sizeof(debug)); - if(!aty_crtc_to_var(&(par->crtc), &debug)) { + if (!aty_crtc_to_var(&par->crtc, &debug)) { u32 hSync, vRefresh; u32 h_disp, h_sync_strt, h_sync_end, h_total; u32 v_disp, v_sync_strt, v_sync_end, v_total; @@ -1344,16 +1372,20 @@ static int atyfb_set_par(struct fb_info *info) hSync = 1000000000 / (pixclock_in_ps * h_total); vRefresh = (hSync * 1000) / v_total; - if (par->crtc.gen_cntl & CRTC_INTERLACE_EN) - vRefresh *= 2; - if (par->crtc.gen_cntl & CRTC_DBL_SCAN_EN) - vRefresh /= 2; + if (par->crtc.gen_cntl & CRTC_INTERLACE_EN) + vRefresh *= 2; + if (par->crtc.gen_cntl & CRTC_DBL_SCAN_EN) + vRefresh /= 2; DPRINTK("atyfb_set_par\n"); - DPRINTK(" Set Visible Mode to %ix%i-%i\n", var->xres, var->yres, var->bits_per_pixel); - DPRINTK(" Virtual resolution %ix%i, pixclock_in_ps %i (calculated %i)\n", - var->xres_virtual, var->yres_virtual, pixclock, pixclock_in_ps); - DPRINTK(" Dot clock: %i MHz\n", 1000000 / pixclock_in_ps); + DPRINTK(" Set Visible Mode to %ix%i-%i\n", + var->xres, var->yres, var->bits_per_pixel); + DPRINTK(" Virtual resolution %ix%i, " + "pixclock_in_ps %i (calculated %i)\n", + var->xres_virtual, var->yres_virtual, + pixclock, pixclock_in_ps); + DPRINTK(" Dot clock: %i MHz\n", + 1000000 / pixclock_in_ps); DPRINTK(" Horizontal sync: %i kHz\n", hSync); DPRINTK(" Vertical refresh: %i Hz\n", vRefresh); DPRINTK(" x style: %i.%03i %i %i %i %i %i %i %i %i\n", @@ -1448,7 +1480,8 @@ static int atyfb_set_par(struct fb_info *info) base = 0x2000; printk("debug atyfb: Mach64 non-shadow register values:"); for (i = 0; i < 256; i = i+4) { - if(i%16 == 0) printk("\ndebug atyfb: 0x%04X: ", base + i); + if (i % 16 == 0) + printk("\ndebug atyfb: 0x%04X: ", base + i); printk(" %08X", aty_ld_le32(i, par)); } printk("\n\n"); @@ -1458,8 +1491,10 @@ static int atyfb_set_par(struct fb_info *info) base = 0x00; printk("debug atyfb: Mach64 PLL register values:"); for (i = 0; i < 64; i++) { - if(i%16 == 0) printk("\ndebug atyfb: 0x%02X: ", base + i); - if(i%4 == 0) printk(" "); + if (i % 16 == 0) + printk("\ndebug atyfb: 0x%02X: ", base + i); + if (i % 4 == 0) + printk(" "); printk("%02X", aty_ld_pll_ct(i, par)); } printk("\n\n"); @@ -1470,19 +1505,21 @@ static int atyfb_set_par(struct fb_info *info) /* LCD registers */ base = 0x00; printk("debug atyfb: LCD register values:"); - if(M64_HAS(LT_LCD_REGS)) { - for(i = 0; i <= POWER_MANAGEMENT; i++) { - if(i == EXT_VERT_STRETCH) - continue; - printk("\ndebug atyfb: 0x%04X: ", lt_lcd_regs[i]); - printk(" %08X", aty_ld_lcd(i, par)); - } - + if (M64_HAS(LT_LCD_REGS)) { + for (i = 0; i <= POWER_MANAGEMENT; i++) { + if (i == EXT_VERT_STRETCH) + continue; + printk("\ndebug atyfb: 0x%04X: ", + lt_lcd_regs[i]); + printk(" %08X", aty_ld_lcd(i, par)); + } } else { - for (i = 0; i < 64; i++) { - if(i%4 == 0) printk("\ndebug atyfb: 0x%02X: ", base + i); - printk(" %08X", aty_ld_lcd(i, par)); - } + for (i = 0; i < 64; i++) { + if (i % 4 == 0) + printk("\ndebug atyfb: 0x%02X: ", + base + i); + printk(" %08X", aty_ld_lcd(i, par)); + } } printk("\n\n"); } @@ -1500,9 +1537,10 @@ static int atyfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) union aty_pll pll; u32 pixclock; - memcpy(&pll, &(par->pll), sizeof(pll)); + memcpy(&pll, &par->pll, sizeof(pll)); - if((err = aty_var_to_crtc(info, var, &crtc))) + err = aty_var_to_crtc(info, var, &crtc); + if (err) return err; pixclock = atyfb_get_pixclock(var, par); @@ -1512,7 +1550,9 @@ static int atyfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) PRINTKE("Invalid pixclock\n"); return -EINVAL; } else { - if((err = par->pll_ops->var_to_pll(info, pixclock, var->bits_per_pixel, &pll))) + err = par->pll_ops->var_to_pll(info, pixclock, + var->bits_per_pixel, &pll); + if (err) return err; } @@ -1539,9 +1579,9 @@ static void set_off_pitch(struct atyfb_par *par, const struct fb_info *info) } - /* - * Open/Release the frame buffer device - */ +/* + * Open/Release the frame buffer device + */ static int atyfb_open(struct fb_info *info, int user) { @@ -1553,7 +1593,7 @@ static int atyfb_open(struct fb_info *info, int user) par->mmaped = 0; #endif } - return (0); + return 0; } static irqreturn_t aty_irq(int irq, void *dev_id) @@ -1568,7 +1608,8 @@ static irqreturn_t aty_irq(int irq, void *dev_id) if (int_cntl & CRTC_VBLANK_INT) { /* clear interrupt */ - aty_st_le32(CRTC_INT_CNTL, (int_cntl & CRTC_INT_EN_MASK) | CRTC_VBLANK_INT_AK, par); + aty_st_le32(CRTC_INT_CNTL, (int_cntl & CRTC_INT_EN_MASK) | + CRTC_VBLANK_INT_AK, par); par->vblank.count++; if (par->vblank.pan_display) { par->vblank.pan_display = 0; @@ -1603,9 +1644,11 @@ static int aty_enable_irq(struct atyfb_par *par, int reenable) spin_lock_irq(&par->int_lock); int_cntl = aty_ld_le32(CRTC_INT_CNTL, par) & CRTC_INT_EN_MASK; if (!(int_cntl & CRTC_VBLANK_INT_EN)) { - printk("atyfb: someone disabled IRQ [%08x]\n", int_cntl); + printk("atyfb: someone disabled IRQ [%08x]\n", + int_cntl); /* re-enable interrupt */ - aty_st_le32(CRTC_INT_CNTL, int_cntl | CRTC_VBLANK_INT_EN, par ); + aty_st_le32(CRTC_INT_CNTL, int_cntl | + CRTC_VBLANK_INT_EN, par); } spin_unlock_irq(&par->int_lock); } @@ -1625,7 +1668,7 @@ static int aty_disable_irq(struct atyfb_par *par) spin_lock_irq(&par->int_lock); int_cntl = aty_ld_le32(CRTC_INT_CNTL, par) & CRTC_INT_EN_MASK; /* disable interrupt */ - aty_st_le32(CRTC_INT_CNTL, int_cntl & ~CRTC_VBLANK_INT_EN, par ); + aty_st_le32(CRTC_INT_CNTL, int_cntl & ~CRTC_VBLANK_INT_EN, par); spin_unlock_irq(&par->int_lock); free_irq(par->irq, par); } @@ -1636,50 +1679,62 @@ static int aty_disable_irq(struct atyfb_par *par) static int atyfb_release(struct fb_info *info, int user) { struct atyfb_par *par = (struct atyfb_par *) info->par; - if (user) { - par->open--; - mdelay(1); - wait_for_idle(par); - if (!par->open) { #ifdef __sparc__ - int was_mmaped = par->mmaped; + int was_mmaped; +#endif - par->mmaped = 0; + if (!user) + return 0; - if (was_mmaped) { - struct fb_var_screeninfo var; + par->open--; + mdelay(1); + wait_for_idle(par); - /* Now reset the default display config, we have no - * idea what the program(s) which mmap'd the chip did - * to the configuration, nor whether it restored it - * correctly. - */ - var = default_var; - if (noaccel) - var.accel_flags &= ~FB_ACCELF_TEXT; - else - var.accel_flags |= FB_ACCELF_TEXT; - if (var.yres == var.yres_virtual) { - u32 videoram = (info->fix.smem_len - (PAGE_SIZE << 2)); - var.yres_virtual = ((videoram * 8) / var.bits_per_pixel) / var.xres_virtual; - if (var.yres_virtual < var.yres) - var.yres_virtual = var.yres; - } - } -#endif - aty_disable_irq(par); + if (par->open) + return 0; + +#ifdef __sparc__ + was_mmaped = par->mmaped; + + par->mmaped = 0; + + if (was_mmaped) { + struct fb_var_screeninfo var; + + /* + * Now reset the default display config, we have + * no idea what the program(s) which mmap'd the + * chip did to the configuration, nor whether it + * restored it correctly. + */ + var = default_var; + if (noaccel) + var.accel_flags &= ~FB_ACCELF_TEXT; + else + var.accel_flags |= FB_ACCELF_TEXT; + if (var.yres == var.yres_virtual) { + u32 videoram = (info->fix.smem_len - (PAGE_SIZE << 2)); + var.yres_virtual = + ((videoram * 8) / var.bits_per_pixel) / + var.xres_virtual; + if (var.yres_virtual < var.yres) + var.yres_virtual = var.yres; } } - return (0); +#endif + aty_disable_irq(par); + + return 0; } - /* - * Pan or Wrap the Display - * - * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag - */ +/* + * Pan or Wrap the Display + * + * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag + */ -static int atyfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) +static int atyfb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) { struct atyfb_par *par = (struct atyfb_par *) info->par; u32 xres, yres, xoffset, yoffset; @@ -1690,7 +1745,8 @@ static int atyfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info yres >>= 1; xoffset = (var->xoffset + 7) & ~7; yoffset = var->yoffset; - if (xoffset + xres > par->crtc.vxres || yoffset + yres > par->crtc.vyres) + if (xoffset + xres > par->crtc.vxres || + yoffset + yres > par->crtc.vyres) return -EINVAL; info->var.xoffset = xoffset; info->var.yoffset = yoffset; @@ -1727,10 +1783,10 @@ static int aty_waitforvblank(struct atyfb_par *par, u32 crtc) return ret; count = vbl->count; - ret = wait_event_interruptible_timeout(vbl->wait, count != vbl->count, HZ/10); - if (ret < 0) { + ret = wait_event_interruptible_timeout(vbl->wait, + count != vbl->count, HZ/10); + if (ret < 0) return ret; - } if (ret == 0) { aty_enable_irq(par, 1); return -ETIMEDOUT; @@ -1784,7 +1840,8 @@ static int atyfb_ioctl(struct fb_info *info, u_int cmd, u_long arg) fbtyp.fb_depth = info->var.bits_per_pixel; fbtyp.fb_cmsize = info->cmap.len; fbtyp.fb_size = info->fix.smem_len; - if (copy_to_user((struct fbtype __user *) arg, &fbtyp, sizeof(fbtyp))) + if (copy_to_user((struct fbtype __user *) arg, &fbtyp, + sizeof(fbtyp))) return -EFAULT; break; #endif /* __sparc__ */ @@ -1804,7 +1861,7 @@ static int atyfb_ioctl(struct fb_info *info, u_int cmd, u_long arg) case ATYIO_CLKR: if (M64_HAS(INTEGRATED)) { struct atyclk clk; - union aty_pll *pll = &(par->pll); + union aty_pll *pll = &par->pll; u32 dsp_config = pll->ct.dsp_config; u32 dsp_on_off = pll->ct.dsp_on_off; clk.ref_clk_per = par->ref_clk_per; @@ -1829,8 +1886,9 @@ static int atyfb_ioctl(struct fb_info *info, u_int cmd, u_long arg) case ATYIO_CLKW: if (M64_HAS(INTEGRATED)) { struct atyclk clk; - union aty_pll *pll = &(par->pll); - if (copy_from_user(&clk, (struct atyclk __user *) arg, sizeof(clk))) + union aty_pll *pll = &par->pll; + if (copy_from_user(&clk, (struct atyclk __user *) arg, + sizeof(clk))) return -EFAULT; par->ref_clk_per = clk.ref_clk_per; pll->ct.pll_ref_div = clk.pll_ref_div; @@ -1841,8 +1899,10 @@ static int atyfb_ioctl(struct fb_info *info, u_int cmd, u_long arg) pll->ct.vclk_fb_div = clk.vclk_fb_div; pll->ct.vclk_post_div_real = clk.vclk_post_div; pll->ct.dsp_config = (clk.dsp_xclks_per_row & 0x3fff) | - ((clk.dsp_loop_latency & 0xf)<<16)| ((clk.dsp_precision & 7)<<20); - pll->ct.dsp_on_off = (clk.dsp_off & 0x7ff) | ((clk.dsp_on & 0x7ff)<<16); + ((clk.dsp_loop_latency & 0xf) << 16) | + ((clk.dsp_precision & 7) << 20); + pll->ct.dsp_on_off = (clk.dsp_off & 0x7ff) | + ((clk.dsp_on & 0x7ff) << 16); /*aty_calc_pll_ct(info, &pll->ct);*/ aty_set_pll_ct(info, pll); } else @@ -1913,8 +1973,7 @@ static int atyfb_mmap(struct fb_info *info, struct vm_area_struct *vma) continue; map_size = par->mmap_map[i].size - (offset - start); - map_offset = - par->mmap_map[i].poff + (offset - start); + map_offset = par->mmap_map[i].poff + (offset - start); break; } if (!map_size) { @@ -1924,8 +1983,7 @@ static int atyfb_mmap(struct fb_info *info, struct vm_area_struct *vma) if (page + map_size > size) map_size = size - page; - pgprot_val(vma->vm_page_prot) &= - ~(par->mmap_map[i].prot_mask); + pgprot_val(vma->vm_page_prot) &= ~(par->mmap_map[i].prot_mask); pgprot_val(vma->vm_page_prot) |= par->mmap_map[i].prot_flag; if (remap_pfn_range(vma, vma->vm_start + page, @@ -2029,7 +2087,8 @@ static int atyfb_pci_suspend(struct pci_dev *pdev, pm_message_t state) par->asleep = 1; par->lock_blank = 1; - /* Because we may change PCI D state ourselves, we need to + /* + * Because we may change PCI D state ourselves, we need to * first save the config space content so the core can * restore it properly on resume. */ @@ -2080,7 +2139,8 @@ static int atyfb_pci_resume(struct pci_dev *pdev) acquire_console_sem(); - /* PCI state will have been restored by the core, so + /* + * PCI state will have been restored by the core, so * we should be in D0 now with our config space fully * restored */ @@ -2192,8 +2252,8 @@ static void aty_bl_init(struct atyfb_par *par) info->bl_dev = bd; fb_bl_default_curve(info, 0, - 0x3F * FB_BACKLIGHT_MAX / MAX_LEVEL, - 0xFF * FB_BACKLIGHT_MAX / MAX_LEVEL); + 0x3F * FB_BACKLIGHT_MAX / MAX_LEVEL, + 0xFF * FB_BACKLIGHT_MAX / MAX_LEVEL); bd->props.max_brightness = FB_BACKLIGHT_LEVELS - 1; bd->props.brightness = bd->props.max_brightness; @@ -2236,16 +2296,16 @@ static void __devinit aty_calc_mem_refresh(struct atyfb_par *par, int xclk) size = ARRAY_SIZE(ragepro_tbl); } - for (i=0; i < size; i++) { + for (i = 0; i < size; i++) { if (xclk < refresh_tbl[i]) - break; + break; } par->mem_refresh_rate = i; } - /* - * Initialisation - */ +/* + * Initialisation + */ static struct fb_info *fb_list = NULL; @@ -2375,8 +2435,10 @@ static int __devinit aty_init(struct fb_info *info) } #endif #ifdef CONFIG_PPC_PMAC - /* The Apple iBook1 uses non-standard memory frequencies. We detect it - * and set the frequency manually. */ + /* + * The Apple iBook1 uses non-standard memory frequencies. + * We detect it and set the frequency manually. + */ if (machine_is_compatible("PowerBook2,1")) { par->pll_limits.mclk = 70; par->pll_limits.xclk = 53; @@ -2421,13 +2483,14 @@ static int __devinit aty_init(struct fb_info *info) /* save previous video mode */ aty_get_crtc(par, &par->saved_crtc); - if(par->pll_ops->get_pll) + if (par->pll_ops->get_pll) par->pll_ops->get_pll(info, &par->saved_pll); par->mem_cntl = aty_ld_le32(MEM_CNTL, par); gtb_memsize = M64_HAS(GTB_DSP); if (gtb_memsize) - switch (par->mem_cntl & 0xF) { /* 0xF used instead of MEM_SIZE_ALIAS */ + /* 0xF used instead of MEM_SIZE_ALIAS */ + switch (par->mem_cntl & 0xF) { case MEM_SIZE_512K: info->fix.smem_len = 0x80000; break; @@ -2496,8 +2559,8 @@ static int __devinit aty_init(struct fb_info *info) } /* - * Reg Block 0 (CT-compatible block) is at mmio_start - * Reg Block 1 (multimedia extensions) is at mmio_start - 0x400 + * Reg Block 0 (CT-compatible block) is at mmio_start + * Reg Block 1 (multimedia extensions) is at mmio_start - 0x400 */ if (M64_HAS(GX)) { info->fix.mmio_len = 0x400; @@ -2516,84 +2579,98 @@ static int __devinit aty_init(struct fb_info *info) } PRINTKI("%d%c %s, %s MHz XTAL, %d MHz PLL, %d Mhz MCLK, %d MHz XCLK\n", - info->fix.smem_len == 0x80000 ? 512 : (info->fix.smem_len >> 20), - info->fix.smem_len == 0x80000 ? 'K' : 'M', ramname, xtal, par->pll_limits.pll_max, - par->pll_limits.mclk, par->pll_limits.xclk); + info->fix.smem_len == 0x80000 ? 512 : (info->fix.smem_len>>20), + info->fix.smem_len == 0x80000 ? 'K' : 'M', ramname, xtal, + par->pll_limits.pll_max, par->pll_limits.mclk, + par->pll_limits.xclk); #if defined(DEBUG) && defined(CONFIG_FB_ATY_CT) if (M64_HAS(INTEGRATED)) { int i; - printk("debug atyfb: BUS_CNTL DAC_CNTL MEM_CNTL EXT_MEM_CNTL CRTC_GEN_CNTL " - "DSP_CONFIG DSP_ON_OFF CLOCK_CNTL\n" - "debug atyfb: %08x %08x %08x %08x %08x %08x %08x %08x\n" + printk("debug atyfb: BUS_CNTL DAC_CNTL MEM_CNTL " + "EXT_MEM_CNTL CRTC_GEN_CNTL DSP_CONFIG " + "DSP_ON_OFF CLOCK_CNTL\n" + "debug atyfb: %08x %08x %08x " + "%08x %08x %08x " + "%08x %08x\n" "debug atyfb: PLL", - aty_ld_le32(BUS_CNTL, par), aty_ld_le32(DAC_CNTL, par), - aty_ld_le32(MEM_CNTL, par), aty_ld_le32(EXT_MEM_CNTL, par), - aty_ld_le32(CRTC_GEN_CNTL, par), aty_ld_le32(DSP_CONFIG, par), - aty_ld_le32(DSP_ON_OFF, par), aty_ld_le32(CLOCK_CNTL, par)); + aty_ld_le32(BUS_CNTL, par), + aty_ld_le32(DAC_CNTL, par), + aty_ld_le32(MEM_CNTL, par), + aty_ld_le32(EXT_MEM_CNTL, par), + aty_ld_le32(CRTC_GEN_CNTL, par), + aty_ld_le32(DSP_CONFIG, par), + aty_ld_le32(DSP_ON_OFF, par), + aty_ld_le32(CLOCK_CNTL, par)); for (i = 0; i < 40; i++) printk(" %02x", aty_ld_pll_ct(i, par)); printk("\n"); } #endif - if(par->pll_ops->init_pll) + if (par->pll_ops->init_pll) par->pll_ops->init_pll(info, &par->pll); if (par->pll_ops->resume_pll) par->pll_ops->resume_pll(info, &par->pll); /* - * Last page of 8 MB (4 MB on ISA) aperture is MMIO, - * unless the auxiliary register aperture is used. + * Last page of 8 MB (4 MB on ISA) aperture is MMIO, + * unless the auxiliary register aperture is used. */ - if (!par->aux_start && - (info->fix.smem_len == 0x800000 || (par->bus_type == ISA && info->fix.smem_len == 0x400000))) + (info->fix.smem_len == 0x800000 || + (par->bus_type == ISA && info->fix.smem_len == 0x400000))) info->fix.smem_len -= GUI_RESERVE; /* - * Disable register access through the linear aperture - * if the auxiliary aperture is used so we can access - * the full 8 MB of video RAM on 8 MB boards. + * Disable register access through the linear aperture + * if the auxiliary aperture is used so we can access + * the full 8 MB of video RAM on 8 MB boards. */ if (par->aux_start) - aty_st_le32(BUS_CNTL, aty_ld_le32(BUS_CNTL, par) | BUS_APER_REG_DIS, par); + aty_st_le32(BUS_CNTL, aty_ld_le32(BUS_CNTL, par) | + BUS_APER_REG_DIS, par); #ifdef CONFIG_MTRR par->mtrr_aper = -1; par->mtrr_reg = -1; if (!nomtrr) { /* Cover the whole resource. */ - par->mtrr_aper = mtrr_add(par->res_start, par->res_size, MTRR_TYPE_WRCOMB, 1); - if (par->mtrr_aper >= 0 && !par->aux_start) { + par->mtrr_aper = mtrr_add(par->res_start, par->res_size, + MTRR_TYPE_WRCOMB, 1); + if (par->mtrr_aper >= 0 && !par->aux_start) { /* Make a hole for mmio. */ - par->mtrr_reg = mtrr_add(par->res_start + 0x800000 - GUI_RESERVE, - GUI_RESERVE, MTRR_TYPE_UNCACHABLE, 1); + par->mtrr_reg = mtrr_add(par->res_start + 0x800000 - + GUI_RESERVE, GUI_RESERVE, + MTRR_TYPE_UNCACHABLE, 1); if (par->mtrr_reg < 0) { mtrr_del(par->mtrr_aper, 0, 0); par->mtrr_aper = -1; } - } + } } #endif info->fbops = &atyfb_ops; info->pseudo_palette = par->pseudo_palette; info->flags = FBINFO_DEFAULT | - FBINFO_HWACCEL_IMAGEBLIT | - FBINFO_HWACCEL_FILLRECT | - FBINFO_HWACCEL_COPYAREA | - FBINFO_HWACCEL_YPAN; + FBINFO_HWACCEL_IMAGEBLIT | + FBINFO_HWACCEL_FILLRECT | + FBINFO_HWACCEL_COPYAREA | + FBINFO_HWACCEL_YPAN; #ifdef CONFIG_PMAC_BACKLIGHT if (M64_HAS(G3_PB_1_1) && machine_is_compatible("PowerBook1,1")) { - /* these bits let the 101 powerbook wake up from sleep -- paulus */ - aty_st_lcd(POWER_MANAGEMENT, aty_ld_lcd(POWER_MANAGEMENT, par) - | (USE_F32KHZ | TRISTATE_MEM_EN), par); + /* + * these bits let the 101 powerbook + * wake up from sleep -- paulus + */ + aty_st_lcd(POWER_MANAGEMENT, aty_ld_lcd(POWER_MANAGEMENT, par) | + USE_F32KHZ | TRISTATE_MEM_EN, par); } else #endif if (M64_HAS(MOBIL_BUS) && backlight) { #ifdef CONFIG_FB_ATY_BACKLIGHT - aty_bl_init (par); + aty_bl_init(par); #endif } @@ -2601,8 +2678,8 @@ static int __devinit aty_init(struct fb_info *info) #ifdef CONFIG_PPC if (machine_is(powermac)) { /* - * FIXME: The NVRAM stuff should be put in a Mac-specific file, as it - * applies to all Mac video cards + * FIXME: The NVRAM stuff should be put in a Mac-specific file, + * as it applies to all Mac video cards */ if (mode) { if (mac_find_mode(&var, info, mode, 8)) @@ -2615,8 +2692,7 @@ static int __devinit aty_init(struct fb_info *info) default_vmode = VMODE_1024_768_60; else if (machine_is_compatible("iMac")) default_vmode = VMODE_1024_768_75; - else if (machine_is_compatible - ("PowerBook2,1")) + else if (machine_is_compatible("PowerBook2,1")) /* iBook with 800x600 LCD */ default_vmode = VMODE_800_600_60; else @@ -2630,7 +2706,7 @@ static int __devinit aty_init(struct fb_info *info) if (default_cmode < CMODE_8 || default_cmode > CMODE_32) default_cmode = CMODE_8; if (!mac_vmode_to_var(default_vmode, default_cmode, - &var)) + &var)) has_var = 1; } } @@ -2702,12 +2778,12 @@ aty_init_exit: #ifdef CONFIG_MTRR if (par->mtrr_reg >= 0) { - mtrr_del(par->mtrr_reg, 0, 0); - par->mtrr_reg = -1; + mtrr_del(par->mtrr_reg, 0, 0); + par->mtrr_reg = -1; } if (par->mtrr_aper >= 0) { - mtrr_del(par->mtrr_aper, 0, 0); - par->mtrr_aper = -1; + mtrr_del(par->mtrr_aper, 0, 0); + par->mtrr_aper = -1; } #endif return ret; @@ -2735,18 +2811,18 @@ static int __devinit store_video_par(char *video_str, unsigned char m64_num) phys_size[m64_num] = size; phys_guiregbase[m64_num] = guiregbase; PRINTKI("stored them all: $%08lX $%08lX $%08lX \n", vmembase, size, - guiregbase); + guiregbase); return 0; - mach64_invalid: + mach64_invalid: phys_vmembase[m64_num] = 0; return -1; } #endif /* CONFIG_ATARI */ - /* - * Blank the display. - */ +/* + * Blank the display. + */ static int atyfb_blank(int blank, struct fb_info *info) { @@ -2768,20 +2844,20 @@ static int atyfb_blank(int blank, struct fb_info *info) gen_cntl = aty_ld_le32(CRTC_GEN_CNTL, par); gen_cntl &= ~0x400004c; switch (blank) { - case FB_BLANK_UNBLANK: - break; - case FB_BLANK_NORMAL: - gen_cntl |= 0x4000040; - break; - case FB_BLANK_VSYNC_SUSPEND: - gen_cntl |= 0x4000048; - break; - case FB_BLANK_HSYNC_SUSPEND: - gen_cntl |= 0x4000044; - break; - case FB_BLANK_POWERDOWN: - gen_cntl |= 0x400004c; - break; + case FB_BLANK_UNBLANK: + break; + case FB_BLANK_NORMAL: + gen_cntl |= 0x4000040; + break; + case FB_BLANK_VSYNC_SUSPEND: + gen_cntl |= 0x4000048; + break; + case FB_BLANK_HSYNC_SUSPEND: + gen_cntl |= 0x4000044; + break; + case FB_BLANK_POWERDOWN: + gen_cntl |= 0x400004c; + break; } aty_st_le32(CRTC_GEN_CNTL, gen_cntl, par); @@ -2806,15 +2882,15 @@ static void aty_st_pal(u_int regno, u_int red, u_int green, u_int blue, aty_st_8(DAC_DATA, blue, par); } - /* - * Set a single color register. The values supplied are already - * rounded down to the hardware's capabilities (according to the - * entries in the var structure). Return != 0 for invalid regno. - * !! 4 & 8 = PSEUDO, > 8 = DIRECTCOLOR - */ +/* + * Set a single color register. The values supplied are already + * rounded down to the hardware's capabilities (according to the + * entries in the var structure). Return != 0 for invalid regno. + * !! 4 & 8 = PSEUDO, > 8 = DIRECTCOLOR + */ static int atyfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, - u_int transp, struct fb_info *info) + u_int transp, struct fb_info *info) { struct atyfb_par *par = (struct atyfb_par *) info->par; int i, depth; @@ -2868,16 +2944,15 @@ static int atyfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, if (depth == 16) { if (regno < 32) aty_st_pal(regno << 3, red, - par->palette[regno<<1].green, + par->palette[regno << 1].green, blue, par); - red = par->palette[regno>>1].red; - blue = par->palette[regno>>1].blue; + red = par->palette[regno >> 1].red; + blue = par->palette[regno >> 1].blue; regno <<= 2; } else if (depth == 15) { regno <<= 3; - for(i = 0; i < 8; i++) { - aty_st_pal(regno + i, red, green, blue, par); - } + for (i = 0; i < 8; i++) + aty_st_pal(regno + i, red, green, blue, par); } } aty_st_pal(regno, red, green, blue, par); @@ -2890,7 +2965,8 @@ static int atyfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, #ifdef __sparc__ static int __devinit atyfb_setup_sparc(struct pci_dev *pdev, - struct fb_info *info, unsigned long addr) + struct fb_info *info, + unsigned long addr) { struct atyfb_par *par = info->par; struct device_node *dp; @@ -2978,7 +3054,8 @@ static int __devinit atyfb_setup_sparc(struct pci_dev *pdev, j++; } - if((ret = correct_chipset(par))) + ret = correct_chipset(par); + if (ret) return ret; if (IS_XL(pdev->device)) { @@ -3108,28 +3185,28 @@ static void __devinit aty_init_lcd(struct atyfb_par *par, u32 bios_base) u32 driv_inf_tab, sig; u16 lcd_ofs; - /* To support an LCD panel, we should know it's dimensions and + /* + * To support an LCD panel, we should know it's dimensions and * it's desired pixel clock. * There are two ways to do it: * - Check the startup video mode and calculate the panel * size from it. This is unreliable. * - Read it from the driver information table in the video BIOS. - */ + */ /* Address of driver information table is at offset 0x78. */ driv_inf_tab = bios_base + *((u16 *)(bios_base+0x78)); /* Check for the driver information table signature. */ - sig = (*(u32 *)driv_inf_tab); + sig = *(u32 *)driv_inf_tab; if ((sig == 0x54504c24) || /* Rage LT pro */ - (sig == 0x544d5224) || /* Rage mobility */ - (sig == 0x54435824) || /* Rage XC */ - (sig == 0x544c5824)) { /* Rage XL */ + (sig == 0x544d5224) || /* Rage mobility */ + (sig == 0x54435824) || /* Rage XC */ + (sig == 0x544c5824)) { /* Rage XL */ PRINTKI("BIOS contains driver information table.\n"); - lcd_ofs = (*(u16 *)(driv_inf_tab + 10)); + lcd_ofs = *(u16 *)(driv_inf_tab + 10); par->lcd_table = 0; - if (lcd_ofs != 0) { + if (lcd_ofs != 0) par->lcd_table = bios_base + lcd_ofs; - } } if (par->lcd_table != 0) { @@ -3144,14 +3221,16 @@ static void __devinit aty_init_lcd(struct atyfb_par *par, u32 bios_base) u16 width, height, panel_type, refresh_rates; u16 *lcdmodeptr; u32 format; - u8 lcd_refresh_rates[16] = {50,56,60,67,70,72,75,76,85,90,100,120,140,150,160,200}; - /* The most important information is the panel size at + u8 lcd_refresh_rates[16] = { 50, 56, 60, 67, 70, 72, 75, 76, 85, + 90, 100, 120, 140, 150, 160, 200 }; + /* + * The most important information is the panel size at * offset 25 and 27, but there's some other nice information * which we print to the screen. */ id = *(u8 *)par->lcd_table; - strncpy(model,(char *)par->lcd_table+1,24); - model[23]=0; + strncpy(model, (char *)par->lcd_table+1, 24); + model[23] = 0; width = par->lcd_width = *(u16 *)(par->lcd_table+25); height = par->lcd_height = *(u16 *)(par->lcd_table+27); @@ -3164,7 +3243,7 @@ static void __devinit aty_init_lcd(struct atyfb_par *par, u32 bios_base) txtdual = "dual (split) "; else txtdual = ""; - tech = (panel_type>>2) & 63; + tech = (panel_type >> 2) & 63; switch (tech) { case 0: txtmonitor = "passive matrix"; @@ -3224,22 +3303,24 @@ static void __devinit aty_init_lcd(struct atyfb_par *par, u32 bios_base) } } PRINTKI("%s%s %s monitor detected: %s\n", - txtdual ,txtcolour, txtmonitor, model); + txtdual, txtcolour, txtmonitor, model); PRINTKI(" id=%d, %dx%d pixels, %s\n", id, width, height, txtformat); refresh_rates_buf[0] = 0; refresh_rates = *(u16 *)(par->lcd_table+62); m = 1; f = 0; - for (i=0;i<16;i++) { + for (i = 0; i < 16; i++) { if (refresh_rates & m) { if (f == 0) { - sprintf(strbuf, "%d", lcd_refresh_rates[i]); + sprintf(strbuf, "%d", + lcd_refresh_rates[i]); f++; } else { - sprintf(strbuf, ",%d", lcd_refresh_rates[i]); + sprintf(strbuf, ",%d", + lcd_refresh_rates[i]); } - strcat(refresh_rates_buf,strbuf); + strcat(refresh_rates_buf, strbuf); } m = m << 1; } @@ -3247,7 +3328,8 @@ static void __devinit aty_init_lcd(struct atyfb_par *par, u32 bios_base) PRINTKI(" supports refresh rates [%s], default %d Hz\n", refresh_rates_buf, lcd_refresh_rates[default_refresh_rate]); par->lcd_refreshrate = lcd_refresh_rates[default_refresh_rate]; - /* We now need to determine the crtc parameters for the + /* + * We now need to determine the crtc parameters for the * LCD monitor. This is tricky, because they are not stored * individually in the BIOS. Instead, the BIOS contains a * table of display modes that work for this monitor. @@ -3382,7 +3464,9 @@ static int __devinit init_from_bios(struct atyfb_par *par) } #endif /* __i386__ */ -static int __devinit atyfb_setup_generic(struct pci_dev *pdev, struct fb_info *info, unsigned long addr) +static int __devinit atyfb_setup_generic(struct pci_dev *pdev, + struct fb_info *info, + unsigned long addr) { struct atyfb_par *par = info->par; u16 tmp; @@ -3429,10 +3513,12 @@ static int __devinit atyfb_setup_generic(struct pci_dev *pdev, struct fb_info *i goto atyfb_setup_generic_fail; } - if((ret = correct_chipset(par))) + ret = correct_chipset(par); + if (ret) goto atyfb_setup_generic_fail; #ifdef __i386__ - if((ret = init_from_bios(par))) + ret = init_from_bios(par); + if (ret) goto atyfb_setup_generic_fail; #endif if (!(aty_ld_le32(CRTC_GEN_CNTL, par) & CRTC_EXT_DISP_EN)) @@ -3457,7 +3543,8 @@ atyfb_setup_generic_fail: #endif /* !__sparc__ */ -static int __devinit atyfb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +static int __devinit atyfb_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) { unsigned long addr, res_start, res_size; struct fb_info *info; @@ -3482,10 +3569,10 @@ static int __devinit atyfb_pci_probe(struct pci_dev *pdev, const struct pci_devi /* Reserve space */ res_start = rp->start; res_size = rp->end - rp->start + 1; - if (!request_mem_region (res_start, res_size, "atyfb")) + if (!request_mem_region(res_start, res_size, "atyfb")) return -EBUSY; - /* Allocate framebuffer */ + /* Allocate framebuffer */ info = framebuffer_alloc(sizeof(struct atyfb_par), &pdev->dev); if (!info) { PRINTKE("atyfb_pci_probe() can't alloc fb_info\n"); @@ -3573,7 +3660,8 @@ static int __init atyfb_atari_probe(void) for (m64_num = 0; m64_num < mach64_count; m64_num++) { if (!phys_vmembase[m64_num] || !phys_size[m64_num] || !phys_guiregbase[m64_num]) { - PRINTKI("phys_*[%d] parameters not set => returning early. \n", m64_num); + PRINTKI("phys_*[%d] parameters not set => " + "returning early. \n", m64_num); continue; } @@ -3589,8 +3677,8 @@ static int __init atyfb_atari_probe(void) par->irq = (unsigned int) -1; /* something invalid */ /* - * Map the video memory (physical address given) to somewhere in the - * kernel address space. + * Map the video memory (physical address given) + * to somewhere in the kernel address space. */ info->screen_base = ioremap(phys_vmembase[m64_num], phys_size[m64_num]); info->fix.smem_start = (unsigned long)info->screen_base; /* Fake! */ @@ -3661,12 +3749,12 @@ static void __devexit atyfb_remove(struct fb_info *info) #ifdef CONFIG_MTRR if (par->mtrr_reg >= 0) { - mtrr_del(par->mtrr_reg, 0, 0); - par->mtrr_reg = -1; + mtrr_del(par->mtrr_reg, 0, 0); + par->mtrr_reg = -1; } if (par->mtrr_aper >= 0) { - mtrr_del(par->mtrr_aper, 0, 0); - par->mtrr_aper = -1; + mtrr_del(par->mtrr_aper, 0, 0); + par->mtrr_aper = -1; } #endif #ifndef __sparc__ @@ -3900,29 +3988,29 @@ static const struct dmi_system_id atyfb_reboot_ids[] = { static int __init atyfb_init(void) { - int err1 = 1, err2 = 1; + int err1 = 1, err2 = 1; #ifndef MODULE - char *option = NULL; + char *option = NULL; - if (fb_get_options("atyfb", &option)) - return -ENODEV; - atyfb_setup(option); + if (fb_get_options("atyfb", &option)) + return -ENODEV; + atyfb_setup(option); #endif #ifdef CONFIG_PCI - err1 = pci_register_driver(&atyfb_driver); + err1 = pci_register_driver(&atyfb_driver); #endif #ifdef CONFIG_ATARI - err2 = atyfb_atari_probe(); + err2 = atyfb_atari_probe(); #endif - if (err1 && err2) - return -ENODEV; + if (err1 && err2) + return -ENODEV; - if (dmi_check_system(atyfb_reboot_ids)) - register_reboot_notifier(&atyfb_reboot_notifier); + if (dmi_check_system(atyfb_reboot_ids)) + register_reboot_notifier(&atyfb_reboot_notifier); - return 0; + return 0; } static void __exit atyfb_exit(void) @@ -3951,8 +4039,7 @@ MODULE_PARM_DESC(mclk, "int: override memory clock"); module_param(xclk, int, 0); MODULE_PARM_DESC(xclk, "int: override accelerated engine clock"); module_param(comp_sync, int, 0); -MODULE_PARM_DESC(comp_sync, - "Set composite sync signal to low (0) or high (1)"); +MODULE_PARM_DESC(comp_sync, "Set composite sync signal to low (0) or high (1)"); module_param(mode, charp, 0); MODULE_PARM_DESC(mode, "Specify resolution as \"x[-][@]\" "); #ifdef CONFIG_MTRR -- cgit v1.2.3 From d480ace08d5b59133575e672a0bd1c97b0f8400f Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Tue, 22 Sep 2009 16:47:03 -0700 Subject: fbdev: framebuffer support for HTC Dream Add a framebuffer driver for Qualcomm MSM/QSD SoCs, tested on HTC Dream smartphone (aka T-Mobile G1, aka ADP1). Brian said: I did the original quick and dirty version for bringup. Rebecca took over and (re)wrote the bulk of the driver, getting things stable for production ship of Dream and Sapphire, and Dima is currently adding support for later Qualcomm chipsets (QSD8x50, etc). Signed-off-by: Pavel Machek Cc: Brian Swetland Cc: Krzysztof Helt Cc: Rebecca Schultz Zavin Cc: Dima Zavin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/Kconfig | 8 + drivers/video/Makefile | 1 + drivers/video/msm/Makefile | 19 + drivers/video/msm/mddi.c | 828 ++++++++++++++++++++++++++++++++ drivers/video/msm/mddi_client_dummy.c | 97 ++++ drivers/video/msm/mddi_client_nt35399.c | 255 ++++++++++ drivers/video/msm/mddi_client_toshiba.c | 283 +++++++++++ drivers/video/msm/mddi_hw.h | 305 ++++++++++++ drivers/video/msm/mdp.c | 538 +++++++++++++++++++++ drivers/video/msm/mdp_csc_table.h | 582 ++++++++++++++++++++++ drivers/video/msm/mdp_hw.h | 621 ++++++++++++++++++++++++ drivers/video/msm/mdp_ppp.c | 750 +++++++++++++++++++++++++++++ drivers/video/msm/mdp_scale_tables.c | 766 +++++++++++++++++++++++++++++ drivers/video/msm/mdp_scale_tables.h | 38 ++ drivers/video/msm/msm_fb.c | 636 ++++++++++++++++++++++++ 15 files changed, 5727 insertions(+) create mode 100644 drivers/video/msm/Makefile create mode 100644 drivers/video/msm/mddi.c create mode 100644 drivers/video/msm/mddi_client_dummy.c create mode 100644 drivers/video/msm/mddi_client_nt35399.c create mode 100644 drivers/video/msm/mddi_client_toshiba.c create mode 100644 drivers/video/msm/mddi_hw.h create mode 100644 drivers/video/msm/mdp.c create mode 100644 drivers/video/msm/mdp_csc_table.h create mode 100644 drivers/video/msm/mdp_hw.h create mode 100644 drivers/video/msm/mdp_ppp.c create mode 100644 drivers/video/msm/mdp_scale_tables.c create mode 100644 drivers/video/msm/mdp_scale_tables.h create mode 100644 drivers/video/msm/msm_fb.c (limited to 'drivers/video') diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 11af4cb8924..05184a1c3e9 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -2124,6 +2124,14 @@ config FB_PRE_INIT_FB Select this option if display contents should be inherited as set by the bootloader. +config FB_MSM + tristate + depends on FB && ARCH_MSM + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + default y + config FB_MX3 tristate "MX3 Framebuffer support" depends on FB && MX3_IPU diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 01a819f4737..f4b6c150bd0 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -126,6 +126,7 @@ obj-$(CONFIG_FB_OMAP) += omap/ obj-$(CONFIG_XEN_FBDEV_FRONTEND) += xen-fbfront.o obj-$(CONFIG_FB_CARMINE) += carminefb.o obj-$(CONFIG_FB_MB862XX) += mb862xx/ +obj-$(CONFIG_FB_MSM) += msm/ # Platform or fallback drivers go here obj-$(CONFIG_FB_UVESA) += uvesafb.o diff --git a/drivers/video/msm/Makefile b/drivers/video/msm/Makefile new file mode 100644 index 00000000000..802d6ae523f --- /dev/null +++ b/drivers/video/msm/Makefile @@ -0,0 +1,19 @@ + +# core framebuffer +# +obj-y := msm_fb.o + +# MDP DMA/PPP engine +# +obj-y += mdp.o mdp_scale_tables.o mdp_ppp.o + +# MDDI interface +# +obj-y += mddi.o + +# MDDI client/panel drivers +# +obj-y += mddi_client_dummy.o +obj-y += mddi_client_toshiba.o +obj-y += mddi_client_nt35399.o + diff --git a/drivers/video/msm/mddi.c b/drivers/video/msm/mddi.c new file mode 100644 index 00000000000..f2de5a1acd6 --- /dev/null +++ b/drivers/video/msm/mddi.c @@ -0,0 +1,828 @@ +/* + * MSM MDDI Transport + * + * Copyright (C) 2007 Google Incorporated + * Copyright (C) 2007 QUALCOMM Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "mddi_hw.h" + +#define FLAG_DISABLE_HIBERNATION 0x0001 +#define FLAG_HAVE_CAPS 0x0002 +#define FLAG_HAS_VSYNC_IRQ 0x0004 +#define FLAG_HAVE_STATUS 0x0008 + +#define CMD_GET_CLIENT_CAP 0x0601 +#define CMD_GET_CLIENT_STATUS 0x0602 + +union mddi_rev { + unsigned char raw[MDDI_REV_BUFFER_SIZE]; + struct mddi_rev_packet hdr; + struct mddi_client_status status; + struct mddi_client_caps caps; + struct mddi_register_access reg; +}; + +struct reg_read_info { + struct completion done; + uint32_t reg; + uint32_t status; + uint32_t result; +}; + +struct mddi_info { + uint16_t flags; + uint16_t version; + char __iomem *base; + int irq; + struct clk *clk; + struct msm_mddi_client_data client_data; + + /* buffer for rev encap packets */ + void *rev_data; + dma_addr_t rev_addr; + struct mddi_llentry *reg_write_data; + dma_addr_t reg_write_addr; + struct mddi_llentry *reg_read_data; + dma_addr_t reg_read_addr; + size_t rev_data_curr; + + spinlock_t int_lock; + uint32_t int_enable; + uint32_t got_int; + wait_queue_head_t int_wait; + + struct mutex reg_write_lock; + struct mutex reg_read_lock; + struct reg_read_info *reg_read; + + struct mddi_client_caps caps; + struct mddi_client_status status; + + void (*power_client)(struct msm_mddi_client_data *, int); + + /* client device published to bind us to the + * appropriate mddi_client driver + */ + char client_name[20]; + + struct platform_device client_pdev; +}; + +static void mddi_init_rev_encap(struct mddi_info *mddi); + +#define mddi_readl(r) readl(mddi->base + (MDDI_##r)) +#define mddi_writel(v, r) writel((v), mddi->base + (MDDI_##r)) + +void mddi_activate_link(struct msm_mddi_client_data *cdata) +{ + struct mddi_info *mddi = container_of(cdata, struct mddi_info, + client_data); + + mddi_writel(MDDI_CMD_LINK_ACTIVE, CMD); +} + +static void mddi_handle_link_list_done(struct mddi_info *mddi) +{ +} + +static void mddi_reset_rev_encap_ptr(struct mddi_info *mddi) +{ + printk(KERN_INFO "mddi: resetting rev ptr\n"); + mddi->rev_data_curr = 0; + mddi_writel(mddi->rev_addr, REV_PTR); + mddi_writel(mddi->rev_addr, REV_PTR); + mddi_writel(MDDI_CMD_FORCE_NEW_REV_PTR, CMD); +} + +static void mddi_handle_rev_data(struct mddi_info *mddi, union mddi_rev *rev) +{ + int i; + struct reg_read_info *ri; + + if ((rev->hdr.length <= MDDI_REV_BUFFER_SIZE - 2) && + (rev->hdr.length >= sizeof(struct mddi_rev_packet) - 2)) { + + switch (rev->hdr.type) { + case TYPE_CLIENT_CAPS: + memcpy(&mddi->caps, &rev->caps, + sizeof(struct mddi_client_caps)); + mddi->flags |= FLAG_HAVE_CAPS; + wake_up(&mddi->int_wait); + break; + case TYPE_CLIENT_STATUS: + memcpy(&mddi->status, &rev->status, + sizeof(struct mddi_client_status)); + mddi->flags |= FLAG_HAVE_STATUS; + wake_up(&mddi->int_wait); + break; + case TYPE_REGISTER_ACCESS: + ri = mddi->reg_read; + if (ri == 0) { + printk(KERN_INFO "rev: got reg %x = %x without " + " pending read\n", + rev->reg.register_address, + rev->reg.register_data_list); + break; + } + if (ri->reg != rev->reg.register_address) { + printk(KERN_INFO "rev: got reg %x = %x for " + "wrong register, expected " + "%x\n", + rev->reg.register_address, + rev->reg.register_data_list, ri->reg); + break; + } + mddi->reg_read = NULL; + ri->status = 0; + ri->result = rev->reg.register_data_list; + complete(&ri->done); + break; + default: + printk(KERN_INFO "rev: unknown reverse packet: " + "len=%04x type=%04x CURR_REV_PTR=%x\n", + rev->hdr.length, rev->hdr.type, + mddi_readl(CURR_REV_PTR)); + for (i = 0; i < rev->hdr.length + 2; i++) { + if ((i % 16) == 0) + printk(KERN_INFO "\n"); + printk(KERN_INFO " %02x", rev->raw[i]); + } + printk(KERN_INFO "\n"); + mddi_reset_rev_encap_ptr(mddi); + } + } else { + printk(KERN_INFO "bad rev length, %d, CURR_REV_PTR %x\n", + rev->hdr.length, mddi_readl(CURR_REV_PTR)); + mddi_reset_rev_encap_ptr(mddi); + } +} + +static void mddi_wait_interrupt(struct mddi_info *mddi, uint32_t intmask); + +static void mddi_handle_rev_data_avail(struct mddi_info *mddi) +{ + union mddi_rev *rev = mddi->rev_data; + uint32_t rev_data_count; + uint32_t rev_crc_err_count; + int i; + struct reg_read_info *ri; + size_t prev_offset; + uint16_t length; + + union mddi_rev *crev = mddi->rev_data + mddi->rev_data_curr; + + /* clear the interrupt */ + mddi_writel(MDDI_INT_REV_DATA_AVAIL, INT); + rev_data_count = mddi_readl(REV_PKT_CNT); + rev_crc_err_count = mddi_readl(REV_CRC_ERR); + if (rev_data_count > 1) + printk(KERN_INFO "rev_data_count %d\n", rev_data_count); + + if (rev_crc_err_count) { + printk(KERN_INFO "rev_crc_err_count %d, INT %x\n", + rev_crc_err_count, mddi_readl(INT)); + ri = mddi->reg_read; + if (ri == 0) { + printk(KERN_INFO "rev: got crc error without pending " + "read\n"); + } else { + mddi->reg_read = NULL; + ri->status = -EIO; + ri->result = -1; + complete(&ri->done); + } + } + + if (rev_data_count == 0) + return; + + prev_offset = mddi->rev_data_curr; + + length = *((uint8_t *)mddi->rev_data + mddi->rev_data_curr); + mddi->rev_data_curr++; + if (mddi->rev_data_curr == MDDI_REV_BUFFER_SIZE) + mddi->rev_data_curr = 0; + length += *((uint8_t *)mddi->rev_data + mddi->rev_data_curr) << 8; + mddi->rev_data_curr += 1 + length; + if (mddi->rev_data_curr >= MDDI_REV_BUFFER_SIZE) + mddi->rev_data_curr = + mddi->rev_data_curr % MDDI_REV_BUFFER_SIZE; + + if (length > MDDI_REV_BUFFER_SIZE - 2) { + printk(KERN_INFO "mddi: rev data length greater than buffer" + "size\n"); + mddi_reset_rev_encap_ptr(mddi); + return; + } + + if (prev_offset + 2 + length >= MDDI_REV_BUFFER_SIZE) { + union mddi_rev tmprev; + size_t rem = MDDI_REV_BUFFER_SIZE - prev_offset; + memcpy(&tmprev.raw[0], mddi->rev_data + prev_offset, rem); + memcpy(&tmprev.raw[rem], mddi->rev_data, 2 + length - rem); + mddi_handle_rev_data(mddi, &tmprev); + } else { + mddi_handle_rev_data(mddi, crev); + } + + if (prev_offset < MDDI_REV_BUFFER_SIZE / 2 && + mddi->rev_data_curr >= MDDI_REV_BUFFER_SIZE / 2) { + mddi_writel(mddi->rev_addr, REV_PTR); + } +} + +static irqreturn_t mddi_isr(int irq, void *data) +{ + struct msm_mddi_client_data *cdata = data; + struct mddi_info *mddi = container_of(cdata, struct mddi_info, + client_data); + uint32_t active, status; + + spin_lock(&mddi->int_lock); + + active = mddi_readl(INT); + status = mddi_readl(STAT); + + mddi_writel(active, INT); + + /* ignore any interrupts we have disabled */ + active &= mddi->int_enable; + + mddi->got_int |= active; + wake_up(&mddi->int_wait); + + if (active & MDDI_INT_PRI_LINK_LIST_DONE) { + mddi->int_enable &= (~MDDI_INT_PRI_LINK_LIST_DONE); + mddi_handle_link_list_done(mddi); + } + if (active & MDDI_INT_REV_DATA_AVAIL) + mddi_handle_rev_data_avail(mddi); + + if (active & ~MDDI_INT_NEED_CLEAR) + mddi->int_enable &= ~(active & ~MDDI_INT_NEED_CLEAR); + + if (active & MDDI_INT_LINK_ACTIVE) { + mddi->int_enable &= (~MDDI_INT_LINK_ACTIVE); + mddi->int_enable |= MDDI_INT_IN_HIBERNATION; + } + + if (active & MDDI_INT_IN_HIBERNATION) { + mddi->int_enable &= (~MDDI_INT_IN_HIBERNATION); + mddi->int_enable |= MDDI_INT_LINK_ACTIVE; + } + + mddi_writel(mddi->int_enable, INTEN); + spin_unlock(&mddi->int_lock); + + return IRQ_HANDLED; +} + +static long mddi_wait_interrupt_timeout(struct mddi_info *mddi, + uint32_t intmask, int timeout) +{ + unsigned long irq_flags; + + spin_lock_irqsave(&mddi->int_lock, irq_flags); + mddi->got_int &= ~intmask; + mddi->int_enable |= intmask; + mddi_writel(mddi->int_enable, INTEN); + spin_unlock_irqrestore(&mddi->int_lock, irq_flags); + return wait_event_timeout(mddi->int_wait, mddi->got_int & intmask, + timeout); +} + +static void mddi_wait_interrupt(struct mddi_info *mddi, uint32_t intmask) +{ + if (mddi_wait_interrupt_timeout(mddi, intmask, HZ/10) == 0) + printk(KERN_INFO KERN_ERR "mddi_wait_interrupt %d, timeout " + "waiting for %x, INT = %x, STAT = %x gotint = %x\n", + current->pid, intmask, mddi_readl(INT), mddi_readl(STAT), + mddi->got_int); +} + +static void mddi_init_rev_encap(struct mddi_info *mddi) +{ + memset(mddi->rev_data, 0xee, MDDI_REV_BUFFER_SIZE); + mddi_writel(mddi->rev_addr, REV_PTR); + mddi_writel(MDDI_CMD_FORCE_NEW_REV_PTR, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); +} + +void mddi_set_auto_hibernate(struct msm_mddi_client_data *cdata, int on) +{ + struct mddi_info *mddi = container_of(cdata, struct mddi_info, + client_data); + mddi_writel(MDDI_CMD_POWERDOWN, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_IN_HIBERNATION); + mddi_writel(MDDI_CMD_HIBERNATE | !!on, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); +} + + +static uint16_t mddi_init_registers(struct mddi_info *mddi) +{ + mddi_writel(0x0001, VERSION); + mddi_writel(MDDI_HOST_BYTES_PER_SUBFRAME, BPS); + mddi_writel(0x0003, SPM); /* subframes per media */ + mddi_writel(0x0005, TA1_LEN); + mddi_writel(MDDI_HOST_TA2_LEN, TA2_LEN); + mddi_writel(0x0096, DRIVE_HI); + /* 0x32 normal, 0x50 for Toshiba display */ + mddi_writel(0x0050, DRIVE_LO); + mddi_writel(0x003C, DISP_WAKE); /* wakeup counter */ + mddi_writel(MDDI_HOST_REV_RATE_DIV, REV_RATE_DIV); + + mddi_writel(MDDI_REV_BUFFER_SIZE, REV_SIZE); + mddi_writel(MDDI_MAX_REV_PKT_SIZE, REV_ENCAP_SZ); + + /* disable periodic rev encap */ + mddi_writel(MDDI_CMD_PERIODIC_REV_ENCAP, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + + if (mddi_readl(PAD_CTL) == 0) { + /* If we are turning on band gap, need to wait 5us before + * turning on the rest of the PAD */ + mddi_writel(0x08000, PAD_CTL); + udelay(5); + } + + /* Recommendation from PAD hw team */ + mddi_writel(0xa850f, PAD_CTL); + + + /* Need an even number for counts */ + mddi_writel(0x60006, DRIVER_START_CNT); + + mddi_set_auto_hibernate(&mddi->client_data, 0); + + mddi_writel(MDDI_CMD_DISP_IGNORE, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + + mddi_init_rev_encap(mddi); + return mddi_readl(CORE_VER) & 0xffff; +} + +static void mddi_suspend(struct msm_mddi_client_data *cdata) +{ + struct mddi_info *mddi = container_of(cdata, struct mddi_info, + client_data); + /* turn off the client */ + if (mddi->power_client) + mddi->power_client(&mddi->client_data, 0); + /* turn off the link */ + mddi_writel(MDDI_CMD_RESET, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + /* turn off the clock */ + clk_disable(mddi->clk); +} + +static void mddi_resume(struct msm_mddi_client_data *cdata) +{ + struct mddi_info *mddi = container_of(cdata, struct mddi_info, + client_data); + mddi_set_auto_hibernate(&mddi->client_data, 0); + /* turn on the client */ + if (mddi->power_client) + mddi->power_client(&mddi->client_data, 1); + /* turn on the clock */ + clk_enable(mddi->clk); + /* set up the local registers */ + mddi->rev_data_curr = 0; + mddi_init_registers(mddi); + mddi_writel(mddi->int_enable, INTEN); + mddi_writel(MDDI_CMD_LINK_ACTIVE, CMD); + mddi_writel(MDDI_CMD_SEND_RTD, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + mddi_set_auto_hibernate(&mddi->client_data, 1); +} + +static int __init mddi_get_client_caps(struct mddi_info *mddi) +{ + int i, j; + + /* clear any stale interrupts */ + mddi_writel(0xffffffff, INT); + + mddi->int_enable = MDDI_INT_LINK_ACTIVE | + MDDI_INT_IN_HIBERNATION | + MDDI_INT_PRI_LINK_LIST_DONE | + MDDI_INT_REV_DATA_AVAIL | + MDDI_INT_REV_OVERFLOW | + MDDI_INT_REV_OVERWRITE | + MDDI_INT_RTD_FAILURE; + mddi_writel(mddi->int_enable, INTEN); + + mddi_writel(MDDI_CMD_LINK_ACTIVE, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + + for (j = 0; j < 3; j++) { + /* the toshiba vga panel does not respond to get + * caps unless you SEND_RTD, but the first SEND_RTD + * will fail... + */ + for (i = 0; i < 4; i++) { + uint32_t stat; + + mddi_writel(MDDI_CMD_SEND_RTD, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + stat = mddi_readl(STAT); + printk(KERN_INFO "mddi cmd send rtd: int %x, stat %x, " + "rtd val %x\n", mddi_readl(INT), stat, + mddi_readl(RTD_VAL)); + if ((stat & MDDI_STAT_RTD_MEAS_FAIL) == 0) + break; + msleep(1); + } + + mddi_writel(CMD_GET_CLIENT_CAP, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + wait_event_timeout(mddi->int_wait, mddi->flags & FLAG_HAVE_CAPS, + HZ / 100); + + if (mddi->flags & FLAG_HAVE_CAPS) + break; + printk(KERN_INFO KERN_ERR "mddi_init, timeout waiting for " + "caps\n"); + } + return mddi->flags & FLAG_HAVE_CAPS; +} + +/* link must be active when this is called */ +int mddi_check_status(struct mddi_info *mddi) +{ + int ret = -1, retry = 3; + mutex_lock(&mddi->reg_read_lock); + mddi_writel(MDDI_CMD_PERIODIC_REV_ENCAP | 1, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + + do { + mddi->flags &= ~FLAG_HAVE_STATUS; + mddi_writel(CMD_GET_CLIENT_STATUS, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + wait_event_timeout(mddi->int_wait, + mddi->flags & FLAG_HAVE_STATUS, + HZ / 100); + + if (mddi->flags & FLAG_HAVE_STATUS) { + if (mddi->status.crc_error_count) + printk(KERN_INFO "mddi status: crc_error " + "count: %d\n", + mddi->status.crc_error_count); + else + ret = 0; + break; + } else + printk(KERN_INFO "mddi status: failed to get client " + "status\n"); + mddi_writel(MDDI_CMD_SEND_RTD, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + } while (--retry); + + mddi_writel(MDDI_CMD_PERIODIC_REV_ENCAP | 0, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + mutex_unlock(&mddi->reg_read_lock); + return ret; +} + + +void mddi_remote_write(struct msm_mddi_client_data *cdata, uint32_t val, + uint32_t reg) +{ + struct mddi_info *mddi = container_of(cdata, struct mddi_info, + client_data); + struct mddi_llentry *ll; + struct mddi_register_access *ra; + + mutex_lock(&mddi->reg_write_lock); + + ll = mddi->reg_write_data; + + ra = &(ll->u.r); + ra->length = 14 + 4; + ra->type = TYPE_REGISTER_ACCESS; + ra->client_id = 0; + ra->read_write_info = MDDI_WRITE | 1; + ra->crc16 = 0; + + ra->register_address = reg; + ra->register_data_list = val; + + ll->flags = 1; + ll->header_count = 14; + ll->data_count = 4; + ll->data = mddi->reg_write_addr + offsetof(struct mddi_llentry, + u.r.register_data_list); + ll->next = 0; + ll->reserved = 0; + + mddi_writel(mddi->reg_write_addr, PRI_PTR); + + mddi_wait_interrupt(mddi, MDDI_INT_PRI_LINK_LIST_DONE); + mutex_unlock(&mddi->reg_write_lock); +} + +uint32_t mddi_remote_read(struct msm_mddi_client_data *cdata, uint32_t reg) +{ + struct mddi_info *mddi = container_of(cdata, struct mddi_info, + client_data); + struct mddi_llentry *ll; + struct mddi_register_access *ra; + struct reg_read_info ri; + unsigned s; + int retry_count = 2; + unsigned long irq_flags; + + mutex_lock(&mddi->reg_read_lock); + + ll = mddi->reg_read_data; + + ra = &(ll->u.r); + ra->length = 14; + ra->type = TYPE_REGISTER_ACCESS; + ra->client_id = 0; + ra->read_write_info = MDDI_READ | 1; + ra->crc16 = 0; + + ra->register_address = reg; + + ll->flags = 0x11; + ll->header_count = 14; + ll->data_count = 0; + ll->data = 0; + ll->next = 0; + ll->reserved = 0; + + s = mddi_readl(STAT); + + ri.reg = reg; + ri.status = -1; + + do { + init_completion(&ri.done); + mddi->reg_read = &ri; + mddi_writel(mddi->reg_read_addr, PRI_PTR); + + mddi_wait_interrupt(mddi, MDDI_INT_PRI_LINK_LIST_DONE); + + /* Enable Periodic Reverse Encapsulation. */ + mddi_writel(MDDI_CMD_PERIODIC_REV_ENCAP | 1, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + if (wait_for_completion_timeout(&ri.done, HZ/10) == 0 && + !ri.done.done) { + printk(KERN_INFO "mddi_remote_read(%x) timeout " + "(%d %d %d)\n", + reg, ri.status, ri.result, ri.done.done); + spin_lock_irqsave(&mddi->int_lock, irq_flags); + mddi->reg_read = NULL; + spin_unlock_irqrestore(&mddi->int_lock, irq_flags); + ri.status = -1; + ri.result = -1; + } + if (ri.status == 0) + break; + + mddi_writel(MDDI_CMD_SEND_RTD, CMD); + mddi_writel(MDDI_CMD_LINK_ACTIVE, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + printk(KERN_INFO "mddi_remote_read: failed, sent " + "MDDI_CMD_SEND_RTD: int %x, stat %x, rtd val %x " + "curr_rev_ptr %x\n", mddi_readl(INT), mddi_readl(STAT), + mddi_readl(RTD_VAL), mddi_readl(CURR_REV_PTR)); + } while (retry_count-- > 0); + /* Disable Periodic Reverse Encapsulation. */ + mddi_writel(MDDI_CMD_PERIODIC_REV_ENCAP | 0, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + mddi->reg_read = NULL; + mutex_unlock(&mddi->reg_read_lock); + return ri.result; +} + +static struct mddi_info mddi_info[2]; + +static int __init mddi_clk_setup(struct platform_device *pdev, + struct mddi_info *mddi, + unsigned long clk_rate) +{ + int ret; + + /* set up the clocks */ + mddi->clk = clk_get(&pdev->dev, "mddi_clk"); + if (IS_ERR(mddi->clk)) { + printk(KERN_INFO "mddi: failed to get clock\n"); + return PTR_ERR(mddi->clk); + } + ret = clk_enable(mddi->clk); + if (ret) + goto fail; + ret = clk_set_rate(mddi->clk, clk_rate); + if (ret) + goto fail; + return 0; + +fail: + clk_put(mddi->clk); + return ret; +} + +static int __init mddi_rev_data_setup(struct mddi_info *mddi) +{ + void *dma; + dma_addr_t dma_addr; + + /* set up dma buffer */ + dma = dma_alloc_coherent(NULL, 0x1000, &dma_addr, GFP_KERNEL); + if (dma == 0) + return -ENOMEM; + mddi->rev_data = dma; + mddi->rev_data_curr = 0; + mddi->rev_addr = dma_addr; + mddi->reg_write_data = dma + MDDI_REV_BUFFER_SIZE; + mddi->reg_write_addr = dma_addr + MDDI_REV_BUFFER_SIZE; + mddi->reg_read_data = mddi->reg_write_data + 1; + mddi->reg_read_addr = mddi->reg_write_addr + + sizeof(*mddi->reg_write_data); + return 0; +} + +static int __init mddi_probe(struct platform_device *pdev) +{ + struct msm_mddi_platform_data *pdata = pdev->dev.platform_data; + struct mddi_info *mddi = &mddi_info[pdev->id]; + struct resource *resource; + int ret, i; + + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!resource) { + printk(KERN_ERR "mddi: no associated mem resource!\n"); + return -ENOMEM; + } + mddi->base = ioremap(resource->start, resource->end - resource->start); + if (!mddi->base) { + printk(KERN_ERR "mddi: failed to remap base!\n"); + ret = -EINVAL; + goto error_ioremap; + } + resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!resource) { + printk(KERN_ERR "mddi: no associated irq resource!\n"); + ret = -EINVAL; + goto error_get_irq_resource; + } + mddi->irq = resource->start; + printk(KERN_INFO "mddi: init() base=0x%p irq=%d\n", mddi->base, + mddi->irq); + mddi->power_client = pdata->power_client; + + mutex_init(&mddi->reg_write_lock); + mutex_init(&mddi->reg_read_lock); + spin_lock_init(&mddi->int_lock); + init_waitqueue_head(&mddi->int_wait); + + ret = mddi_clk_setup(pdev, mddi, pdata->clk_rate); + if (ret) { + printk(KERN_ERR "mddi: failed to setup clock!\n"); + goto error_clk_setup; + } + + ret = mddi_rev_data_setup(mddi); + if (ret) { + printk(KERN_ERR "mddi: failed to setup rev data!\n"); + goto error_rev_data; + } + + mddi->int_enable = 0; + mddi_writel(mddi->int_enable, INTEN); + ret = request_irq(mddi->irq, mddi_isr, IRQF_DISABLED, "mddi", + &mddi->client_data); + if (ret) { + printk(KERN_ERR "mddi: failed to request enable irq!\n"); + goto error_request_irq; + } + + /* turn on the mddi client bridge chip */ + if (mddi->power_client) + mddi->power_client(&mddi->client_data, 1); + + /* initialize the mddi registers */ + mddi_set_auto_hibernate(&mddi->client_data, 0); + mddi_writel(MDDI_CMD_RESET, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + mddi->version = mddi_init_registers(mddi); + if (mddi->version < 0x20) { + printk(KERN_ERR "mddi: unsupported version 0x%x\n", + mddi->version); + ret = -ENODEV; + goto error_mddi_version; + } + + /* read the capabilities off the client */ + if (!mddi_get_client_caps(mddi)) { + printk(KERN_INFO "mddi: no client found\n"); + /* power down the panel */ + mddi_writel(MDDI_CMD_POWERDOWN, CMD); + printk(KERN_INFO "mddi powerdown: stat %x\n", mddi_readl(STAT)); + msleep(100); + printk(KERN_INFO "mddi powerdown: stat %x\n", mddi_readl(STAT)); + return 0; + } + mddi_set_auto_hibernate(&mddi->client_data, 1); + + if (mddi->caps.Mfr_Name == 0 && mddi->caps.Product_Code == 0) + pdata->fixup(&mddi->caps.Mfr_Name, &mddi->caps.Product_Code); + + mddi->client_pdev.id = 0; + for (i = 0; i < pdata->num_clients; i++) { + if (pdata->client_platform_data[i].product_id == + (mddi->caps.Mfr_Name << 16 | mddi->caps.Product_Code)) { + mddi->client_data.private_client_data = + pdata->client_platform_data[i].client_data; + mddi->client_pdev.name = + pdata->client_platform_data[i].name; + mddi->client_pdev.id = + pdata->client_platform_data[i].id; + /* XXX: possibly set clock */ + break; + } + } + + if (i >= pdata->num_clients) + mddi->client_pdev.name = "mddi_c_dummy"; + printk(KERN_INFO "mddi: registering panel %s\n", + mddi->client_pdev.name); + + mddi->client_data.suspend = mddi_suspend; + mddi->client_data.resume = mddi_resume; + mddi->client_data.activate_link = mddi_activate_link; + mddi->client_data.remote_write = mddi_remote_write; + mddi->client_data.remote_read = mddi_remote_read; + mddi->client_data.auto_hibernate = mddi_set_auto_hibernate; + mddi->client_data.fb_resource = pdata->fb_resource; + if (pdev->id == 0) + mddi->client_data.interface_type = MSM_MDDI_PMDH_INTERFACE; + else if (pdev->id == 1) + mddi->client_data.interface_type = MSM_MDDI_EMDH_INTERFACE; + else { + printk(KERN_ERR "mddi: can not determine interface %d!\n", + pdev->id); + ret = -EINVAL; + goto error_mddi_interface; + } + + mddi->client_pdev.dev.platform_data = &mddi->client_data; + printk(KERN_INFO "mddi: publish: %s\n", mddi->client_name); + platform_device_register(&mddi->client_pdev); + return 0; + +error_mddi_interface: +error_mddi_version: + free_irq(mddi->irq, 0); +error_request_irq: + dma_free_coherent(NULL, 0x1000, mddi->rev_data, mddi->rev_addr); +error_rev_data: +error_clk_setup: +error_get_irq_resource: + iounmap(mddi->base); +error_ioremap: + + printk(KERN_INFO "mddi: mddi_init() failed (%d)\n", ret); + return ret; +} + + +static struct platform_driver mddi_driver = { + .probe = mddi_probe, + .driver = { .name = "msm_mddi" }, +}; + +static int __init _mddi_init(void) +{ + return platform_driver_register(&mddi_driver); +} + +module_init(_mddi_init); diff --git a/drivers/video/msm/mddi_client_dummy.c b/drivers/video/msm/mddi_client_dummy.c new file mode 100644 index 00000000000..ebbae87885b --- /dev/null +++ b/drivers/video/msm/mddi_client_dummy.c @@ -0,0 +1,97 @@ +/* drivers/video/msm_fb/mddi_client_dummy.c + * + * Support for "dummy" mddi client devices which require no + * special initialization code. + * + * Copyright (C) 2007 Google Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#include +#include +#include + +#include + +struct panel_info { + struct platform_device pdev; + struct msm_panel_data panel_data; +}; + +static int mddi_dummy_suspend(struct msm_panel_data *panel_data) +{ + return 0; +} + +static int mddi_dummy_resume(struct msm_panel_data *panel_data) +{ + return 0; +} + +static int mddi_dummy_blank(struct msm_panel_data *panel_data) +{ + return 0; +} + +static int mddi_dummy_unblank(struct msm_panel_data *panel_data) +{ + return 0; +} + +static int mddi_dummy_probe(struct platform_device *pdev) +{ + struct msm_mddi_client_data *client_data = pdev->dev.platform_data; + struct panel_info *panel = + kzalloc(sizeof(struct panel_info), GFP_KERNEL); + int ret; + if (!panel) + return -ENOMEM; + platform_set_drvdata(pdev, panel); + panel->panel_data.suspend = mddi_dummy_suspend; + panel->panel_data.resume = mddi_dummy_resume; + panel->panel_data.blank = mddi_dummy_blank; + panel->panel_data.unblank = mddi_dummy_unblank; + panel->panel_data.caps = MSMFB_CAP_PARTIAL_UPDATES; + panel->pdev.name = "msm_panel"; + panel->pdev.id = pdev->id; + platform_device_add_resources(&panel->pdev, + client_data->fb_resource, 1); + panel->panel_data.fb_data = client_data->private_client_data; + panel->pdev.dev.platform_data = &panel->panel_data; + ret = platform_device_register(&panel->pdev); + if (ret) { + kfree(panel); + return ret; + } + return 0; +} + +static int mddi_dummy_remove(struct platform_device *pdev) +{ + struct panel_info *panel = platform_get_drvdata(pdev); + kfree(panel); + return 0; +} + +static struct platform_driver mddi_client_dummy = { + .probe = mddi_dummy_probe, + .remove = mddi_dummy_remove, + .driver = { .name = "mddi_c_dummy" }, +}; + +static int __init mddi_client_dummy_init(void) +{ + platform_driver_register(&mddi_client_dummy); + return 0; +} + +module_init(mddi_client_dummy_init); + diff --git a/drivers/video/msm/mddi_client_nt35399.c b/drivers/video/msm/mddi_client_nt35399.c new file mode 100644 index 00000000000..9c78050ac79 --- /dev/null +++ b/drivers/video/msm/mddi_client_nt35399.c @@ -0,0 +1,255 @@ +/* drivers/video/msm_fb/mddi_client_nt35399.c + * + * Support for Novatek NT35399 MDDI client of Sapphire + * + * Copyright (C) 2008 HTC Incorporated + * Author: Solomon Chiu (solomon_chiu@htc.com) + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +static DECLARE_WAIT_QUEUE_HEAD(nt35399_vsync_wait); + +struct panel_info { + struct msm_mddi_client_data *client_data; + struct platform_device pdev; + struct msm_panel_data panel_data; + struct msmfb_callback *fb_callback; + struct work_struct panel_work; + struct workqueue_struct *fb_wq; + int nt35399_got_int; +}; + +static void +nt35399_request_vsync(struct msm_panel_data *panel_data, + struct msmfb_callback *callback) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + panel->fb_callback = callback; + if (panel->nt35399_got_int) { + panel->nt35399_got_int = 0; + client_data->activate_link(client_data); /* clears interrupt */ + } +} + +static void nt35399_wait_vsync(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + if (panel->nt35399_got_int) { + panel->nt35399_got_int = 0; + client_data->activate_link(client_data); /* clears interrupt */ + } + + if (wait_event_timeout(nt35399_vsync_wait, panel->nt35399_got_int, + HZ/2) == 0) + printk(KERN_ERR "timeout waiting for VSYNC\n"); + + panel->nt35399_got_int = 0; + /* interrupt clears when screen dma starts */ +} + +static int nt35399_suspend(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + int ret; + + ret = bridge_data->uninit(bridge_data, client_data); + if (ret) { + printk(KERN_INFO "mddi nt35399 client: non zero return from " + "uninit\n"); + return ret; + } + client_data->suspend(client_data); + return 0; +} + +static int nt35399_resume(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + int ret; + + client_data->resume(client_data); + ret = bridge_data->init(bridge_data, client_data); + if (ret) + return ret; + return 0; +} + +static int nt35399_blank(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + + return bridge_data->blank(bridge_data, client_data); +} + +static int nt35399_unblank(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + + return bridge_data->unblank(bridge_data, client_data); +} + +irqreturn_t nt35399_vsync_interrupt(int irq, void *data) +{ + struct panel_info *panel = data; + + panel->nt35399_got_int = 1; + + if (panel->fb_callback) { + panel->fb_callback->func(panel->fb_callback); + panel->fb_callback = NULL; + } + + wake_up(&nt35399_vsync_wait); + + return IRQ_HANDLED; +} + +static int setup_vsync(struct panel_info *panel, int init) +{ + int ret; + int gpio = 97; + unsigned int irq; + + if (!init) { + ret = 0; + goto uninit; + } + ret = gpio_request(gpio, "vsync"); + if (ret) + goto err_request_gpio_failed; + + ret = gpio_direction_input(gpio); + if (ret) + goto err_gpio_direction_input_failed; + + ret = irq = gpio_to_irq(gpio); + if (ret < 0) + goto err_get_irq_num_failed; + + ret = request_irq(irq, nt35399_vsync_interrupt, IRQF_TRIGGER_RISING, + "vsync", panel); + if (ret) + goto err_request_irq_failed; + + printk(KERN_INFO "vsync on gpio %d now %d\n", + gpio, gpio_get_value(gpio)); + return 0; + +uninit: + free_irq(gpio_to_irq(gpio), panel->client_data); +err_request_irq_failed: +err_get_irq_num_failed: +err_gpio_direction_input_failed: + gpio_free(gpio); +err_request_gpio_failed: + return ret; +} + +static int mddi_nt35399_probe(struct platform_device *pdev) +{ + struct msm_mddi_client_data *client_data = pdev->dev.platform_data; + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + + int ret; + + struct panel_info *panel = kzalloc(sizeof(struct panel_info), + GFP_KERNEL); + + printk(KERN_DEBUG "%s: enter.\n", __func__); + + if (!panel) + return -ENOMEM; + platform_set_drvdata(pdev, panel); + + ret = setup_vsync(panel, 1); + if (ret) { + dev_err(&pdev->dev, "mddi_nt35399_setup_vsync failed\n"); + return ret; + } + + panel->client_data = client_data; + panel->panel_data.suspend = nt35399_suspend; + panel->panel_data.resume = nt35399_resume; + panel->panel_data.wait_vsync = nt35399_wait_vsync; + panel->panel_data.request_vsync = nt35399_request_vsync; + panel->panel_data.blank = nt35399_blank; + panel->panel_data.unblank = nt35399_unblank; + panel->panel_data.fb_data = &bridge_data->fb_data; + panel->panel_data.caps = 0; + + panel->pdev.name = "msm_panel"; + panel->pdev.id = pdev->id; + panel->pdev.resource = client_data->fb_resource; + panel->pdev.num_resources = 1; + panel->pdev.dev.platform_data = &panel->panel_data; + + if (bridge_data->init) + bridge_data->init(bridge_data, client_data); + + platform_device_register(&panel->pdev); + + return 0; +} + +static int mddi_nt35399_remove(struct platform_device *pdev) +{ + struct panel_info *panel = platform_get_drvdata(pdev); + + setup_vsync(panel, 0); + kfree(panel); + return 0; +} + +static struct platform_driver mddi_client_0bda_8a47 = { + .probe = mddi_nt35399_probe, + .remove = mddi_nt35399_remove, + .driver = { .name = "mddi_c_0bda_8a47" }, +}; + +static int __init mddi_client_nt35399_init(void) +{ + return platform_driver_register(&mddi_client_0bda_8a47); +} + +module_init(mddi_client_nt35399_init); + diff --git a/drivers/video/msm/mddi_client_toshiba.c b/drivers/video/msm/mddi_client_toshiba.c new file mode 100644 index 00000000000..80d0f5fdf0b --- /dev/null +++ b/drivers/video/msm/mddi_client_toshiba.c @@ -0,0 +1,283 @@ +/* drivers/video/msm_fb/mddi_client_toshiba.c + * + * Support for Toshiba TC358720XBG mddi client devices which require no + * special initialization code. + * + * Copyright (C) 2007 Google Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + + +#define LCD_CONTROL_BLOCK_BASE 0x110000 +#define CMN (LCD_CONTROL_BLOCK_BASE|0x10) +#define INTFLG (LCD_CONTROL_BLOCK_BASE|0x18) +#define HCYCLE (LCD_CONTROL_BLOCK_BASE|0x34) +#define HDE_START (LCD_CONTROL_BLOCK_BASE|0x3C) +#define VPOS (LCD_CONTROL_BLOCK_BASE|0xC0) +#define MPLFBUF (LCD_CONTROL_BLOCK_BASE|0x20) +#define WAKEUP (LCD_CONTROL_BLOCK_BASE|0x54) +#define WSYN_DLY (LCD_CONTROL_BLOCK_BASE|0x58) +#define REGENB (LCD_CONTROL_BLOCK_BASE|0x5C) + +#define BASE5 0x150000 +#define BASE6 0x160000 +#define BASE7 0x170000 + +#define GPIOIEV (BASE5 + 0x10) +#define GPIOIE (BASE5 + 0x14) +#define GPIORIS (BASE5 + 0x18) +#define GPIOMIS (BASE5 + 0x1C) +#define GPIOIC (BASE5 + 0x20) + +#define INTMASK (BASE6 + 0x0C) +#define INTMASK_VWAKEOUT (1U << 0) +#define INTMASK_VWAKEOUT_ACTIVE_LOW (1U << 8) +#define GPIOSEL (BASE7 + 0x00) +#define GPIOSEL_VWAKEINT (1U << 0) + +static DECLARE_WAIT_QUEUE_HEAD(toshiba_vsync_wait); + +struct panel_info { + struct msm_mddi_client_data *client_data; + struct platform_device pdev; + struct msm_panel_data panel_data; + struct msmfb_callback *toshiba_callback; + int toshiba_got_int; +}; + + +static void toshiba_request_vsync(struct msm_panel_data *panel_data, + struct msmfb_callback *callback) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + panel->toshiba_callback = callback; + if (panel->toshiba_got_int) { + panel->toshiba_got_int = 0; + client_data->activate_link(client_data); + } +} + +static void toshiba_clear_vsync(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + client_data->activate_link(client_data); +} + +static void toshiba_wait_vsync(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + if (panel->toshiba_got_int) { + panel->toshiba_got_int = 0; + client_data->activate_link(client_data); /* clears interrupt */ + } + if (wait_event_timeout(toshiba_vsync_wait, panel->toshiba_got_int, + HZ/2) == 0) + printk(KERN_ERR "timeout waiting for VSYNC\n"); + panel->toshiba_got_int = 0; + /* interrupt clears when screen dma starts */ +} + +static int toshiba_suspend(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + int ret; + + ret = bridge_data->uninit(bridge_data, client_data); + if (ret) { + printk(KERN_INFO "mddi toshiba client: non zero return from " + "uninit\n"); + return ret; + } + client_data->suspend(client_data); + return 0; +} + +static int toshiba_resume(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + int ret; + + client_data->resume(client_data); + ret = bridge_data->init(bridge_data, client_data); + if (ret) + return ret; + return 0; +} + +static int toshiba_blank(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + + return bridge_data->blank(bridge_data, client_data); +} + +static int toshiba_unblank(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + + return bridge_data->unblank(bridge_data, client_data); +} + +irqreturn_t toshiba_vsync_interrupt(int irq, void *data) +{ + struct panel_info *panel = data; + + panel->toshiba_got_int = 1; + if (panel->toshiba_callback) { + panel->toshiba_callback->func(panel->toshiba_callback); + panel->toshiba_callback = 0; + } + wake_up(&toshiba_vsync_wait); + return IRQ_HANDLED; +} + +static int setup_vsync(struct panel_info *panel, + int init) +{ + int ret; + int gpio = 97; + unsigned int irq; + + if (!init) { + ret = 0; + goto uninit; + } + ret = gpio_request(gpio, "vsync"); + if (ret) + goto err_request_gpio_failed; + + ret = gpio_direction_input(gpio); + if (ret) + goto err_gpio_direction_input_failed; + + ret = irq = gpio_to_irq(gpio); + if (ret < 0) + goto err_get_irq_num_failed; + + ret = request_irq(irq, toshiba_vsync_interrupt, IRQF_TRIGGER_RISING, + "vsync", panel); + if (ret) + goto err_request_irq_failed; + printk(KERN_INFO "vsync on gpio %d now %d\n", + gpio, gpio_get_value(gpio)); + return 0; + +uninit: + free_irq(gpio_to_irq(gpio), panel); +err_request_irq_failed: +err_get_irq_num_failed: +err_gpio_direction_input_failed: + gpio_free(gpio); +err_request_gpio_failed: + return ret; +} + +static int mddi_toshiba_probe(struct platform_device *pdev) +{ + int ret; + struct msm_mddi_client_data *client_data = pdev->dev.platform_data; + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + struct panel_info *panel = + kzalloc(sizeof(struct panel_info), GFP_KERNEL); + if (!panel) + return -ENOMEM; + platform_set_drvdata(pdev, panel); + + /* mddi_remote_write(mddi, 0, WAKEUP); */ + client_data->remote_write(client_data, GPIOSEL_VWAKEINT, GPIOSEL); + client_data->remote_write(client_data, INTMASK_VWAKEOUT, INTMASK); + + ret = setup_vsync(panel, 1); + if (ret) { + dev_err(&pdev->dev, "mddi_bridge_setup_vsync failed\n"); + return ret; + } + + panel->client_data = client_data; + panel->panel_data.suspend = toshiba_suspend; + panel->panel_data.resume = toshiba_resume; + panel->panel_data.wait_vsync = toshiba_wait_vsync; + panel->panel_data.request_vsync = toshiba_request_vsync; + panel->panel_data.clear_vsync = toshiba_clear_vsync; + panel->panel_data.blank = toshiba_blank; + panel->panel_data.unblank = toshiba_unblank; + panel->panel_data.fb_data = &bridge_data->fb_data; + panel->panel_data.caps = MSMFB_CAP_PARTIAL_UPDATES; + + panel->pdev.name = "msm_panel"; + panel->pdev.id = pdev->id; + panel->pdev.resource = client_data->fb_resource; + panel->pdev.num_resources = 1; + panel->pdev.dev.platform_data = &panel->panel_data; + bridge_data->init(bridge_data, client_data); + platform_device_register(&panel->pdev); + + return 0; +} + +static int mddi_toshiba_remove(struct platform_device *pdev) +{ + struct panel_info *panel = platform_get_drvdata(pdev); + + setup_vsync(panel, 0); + kfree(panel); + return 0; +} + +static struct platform_driver mddi_client_d263_0000 = { + .probe = mddi_toshiba_probe, + .remove = mddi_toshiba_remove, + .driver = { .name = "mddi_c_d263_0000" }, +}; + +static int __init mddi_client_toshiba_init(void) +{ + platform_driver_register(&mddi_client_d263_0000); + return 0; +} + +module_init(mddi_client_toshiba_init); + diff --git a/drivers/video/msm/mddi_hw.h b/drivers/video/msm/mddi_hw.h new file mode 100644 index 00000000000..45cc01fc1e7 --- /dev/null +++ b/drivers/video/msm/mddi_hw.h @@ -0,0 +1,305 @@ +/* drivers/video/msm_fb/mddi_hw.h + * + * MSM MDDI Hardware Registers and Structures + * + * Copyright (C) 2007 QUALCOMM Incorporated + * Copyright (C) 2007 Google Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#ifndef _MDDI_HW_H_ +#define _MDDI_HW_H_ + +#include + +#define MDDI_CMD 0x0000 +#define MDDI_VERSION 0x0004 +#define MDDI_PRI_PTR 0x0008 +#define MDDI_SEC_PTR 0x000c +#define MDDI_BPS 0x0010 +#define MDDI_SPM 0x0014 +#define MDDI_INT 0x0018 +#define MDDI_INTEN 0x001c +#define MDDI_REV_PTR 0x0020 +#define MDDI_REV_SIZE 0x0024 +#define MDDI_STAT 0x0028 +#define MDDI_REV_RATE_DIV 0x002c +#define MDDI_REV_CRC_ERR 0x0030 +#define MDDI_TA1_LEN 0x0034 +#define MDDI_TA2_LEN 0x0038 +#define MDDI_TEST_BUS 0x003c +#define MDDI_TEST 0x0040 +#define MDDI_REV_PKT_CNT 0x0044 +#define MDDI_DRIVE_HI 0x0048 +#define MDDI_DRIVE_LO 0x004c +#define MDDI_DISP_WAKE 0x0050 +#define MDDI_REV_ENCAP_SZ 0x0054 +#define MDDI_RTD_VAL 0x0058 +#define MDDI_PAD_CTL 0x0068 +#define MDDI_DRIVER_START_CNT 0x006c +#define MDDI_NEXT_PRI_PTR 0x0070 +#define MDDI_NEXT_SEC_PTR 0x0074 +#define MDDI_MISR_CTL 0x0078 +#define MDDI_MISR_DATA 0x007c +#define MDDI_SF_CNT 0x0080 +#define MDDI_MF_CNT 0x0084 +#define MDDI_CURR_REV_PTR 0x0088 +#define MDDI_CORE_VER 0x008c + +#define MDDI_INT_PRI_PTR_READ 0x0001 +#define MDDI_INT_SEC_PTR_READ 0x0002 +#define MDDI_INT_REV_DATA_AVAIL 0x0004 +#define MDDI_INT_DISP_REQ 0x0008 +#define MDDI_INT_PRI_UNDERFLOW 0x0010 +#define MDDI_INT_SEC_UNDERFLOW 0x0020 +#define MDDI_INT_REV_OVERFLOW 0x0040 +#define MDDI_INT_CRC_ERROR 0x0080 +#define MDDI_INT_MDDI_IN 0x0100 +#define MDDI_INT_PRI_OVERWRITE 0x0200 +#define MDDI_INT_SEC_OVERWRITE 0x0400 +#define MDDI_INT_REV_OVERWRITE 0x0800 +#define MDDI_INT_DMA_FAILURE 0x1000 +#define MDDI_INT_LINK_ACTIVE 0x2000 +#define MDDI_INT_IN_HIBERNATION 0x4000 +#define MDDI_INT_PRI_LINK_LIST_DONE 0x8000 +#define MDDI_INT_SEC_LINK_LIST_DONE 0x10000 +#define MDDI_INT_NO_CMD_PKTS_PEND 0x20000 +#define MDDI_INT_RTD_FAILURE 0x40000 +#define MDDI_INT_REV_PKT_RECEIVED 0x80000 +#define MDDI_INT_REV_PKTS_AVAIL 0x100000 + +#define MDDI_INT_NEED_CLEAR ( \ + MDDI_INT_REV_DATA_AVAIL | \ + MDDI_INT_PRI_UNDERFLOW | \ + MDDI_INT_SEC_UNDERFLOW | \ + MDDI_INT_REV_OVERFLOW | \ + MDDI_INT_CRC_ERROR | \ + MDDI_INT_REV_PKT_RECEIVED) + + +#define MDDI_STAT_LINK_ACTIVE 0x0001 +#define MDDI_STAT_NEW_REV_PTR 0x0002 +#define MDDI_STAT_NEW_PRI_PTR 0x0004 +#define MDDI_STAT_NEW_SEC_PTR 0x0008 +#define MDDI_STAT_IN_HIBERNATION 0x0010 +#define MDDI_STAT_PRI_LINK_LIST_DONE 0x0020 +#define MDDI_STAT_SEC_LINK_LIST_DONE 0x0040 +#define MDDI_STAT_PENDING_TIMING_PKT 0x0080 +#define MDDI_STAT_PENDING_REV_ENCAP 0x0100 +#define MDDI_STAT_PENDING_POWERDOWN 0x0200 +#define MDDI_STAT_RTD_MEAS_FAIL 0x0800 +#define MDDI_STAT_CLIENT_WAKEUP_REQ 0x1000 + + +#define MDDI_CMD_POWERDOWN 0x0100 +#define MDDI_CMD_POWERUP 0x0200 +#define MDDI_CMD_HIBERNATE 0x0300 +#define MDDI_CMD_RESET 0x0400 +#define MDDI_CMD_DISP_IGNORE 0x0501 +#define MDDI_CMD_DISP_LISTEN 0x0500 +#define MDDI_CMD_SEND_REV_ENCAP 0x0600 +#define MDDI_CMD_GET_CLIENT_CAP 0x0601 +#define MDDI_CMD_GET_CLIENT_STATUS 0x0602 +#define MDDI_CMD_SEND_RTD 0x0700 +#define MDDI_CMD_LINK_ACTIVE 0x0900 +#define MDDI_CMD_PERIODIC_REV_ENCAP 0x0A00 +#define MDDI_CMD_FORCE_NEW_REV_PTR 0x0C00 + + + +#define MDDI_VIDEO_REV_PKT_SIZE 0x40 +#define MDDI_CLIENT_CAPABILITY_REV_PKT_SIZE 0x60 +#define MDDI_MAX_REV_PKT_SIZE 0x60 + +/* #define MDDI_REV_BUFFER_SIZE 128 */ +#define MDDI_REV_BUFFER_SIZE (MDDI_MAX_REV_PKT_SIZE * 4) + +/* MDP sends 256 pixel packets, so lower value hibernates more without + * significantly increasing latency of waiting for next subframe */ +#define MDDI_HOST_BYTES_PER_SUBFRAME 0x3C00 +#define MDDI_HOST_TA2_LEN 0x000c +#define MDDI_HOST_REV_RATE_DIV 0x0002 + + +struct __attribute__((packed)) mddi_rev_packet { + uint16_t length; + uint16_t type; + uint16_t client_id; +}; + +struct __attribute__((packed)) mddi_client_status { + uint16_t length; + uint16_t type; + uint16_t client_id; + uint16_t reverse_link_request; /* bytes needed in rev encap message */ + uint8_t crc_error_count; + uint8_t capability_change; + uint16_t graphics_busy_flags; + uint16_t crc16; +}; + +struct __attribute__((packed)) mddi_client_caps { + uint16_t length; /* length, exclusive of this field */ + uint16_t type; /* 66 */ + uint16_t client_id; + + uint16_t Protocol_Version; + uint16_t Minimum_Protocol_Version; + uint16_t Data_Rate_Capability; + uint8_t Interface_Type_Capability; + uint8_t Number_of_Alt_Displays; + uint16_t PostCal_Data_Rate; + uint16_t Bitmap_Width; + uint16_t Bitmap_Height; + uint16_t Display_Window_Width; + uint16_t Display_Window_Height; + uint32_t Color_Map_Size; + uint16_t Color_Map_RGB_Width; + uint16_t RGB_Capability; + uint8_t Monochrome_Capability; + uint8_t Reserved_1; + uint16_t Y_Cb_Cr_Capability; + uint16_t Bayer_Capability; + uint16_t Alpha_Cursor_Image_Planes; + uint32_t Client_Feature_Capability_Indicators; + uint8_t Maximum_Video_Frame_Rate_Capability; + uint8_t Minimum_Video_Frame_Rate_Capability; + uint16_t Minimum_Sub_frame_Rate; + uint16_t Audio_Buffer_Depth; + uint16_t Audio_Channel_Capability; + uint16_t Audio_Sample_Rate_Capability; + uint8_t Audio_Sample_Resolution; + uint8_t Mic_Audio_Sample_Resolution; + uint16_t Mic_Sample_Rate_Capability; + uint8_t Keyboard_Data_Format; + uint8_t pointing_device_data_format; + uint16_t content_protection_type; + uint16_t Mfr_Name; + uint16_t Product_Code; + uint16_t Reserved_3; + uint32_t Serial_Number; + uint8_t Week_of_Manufacture; + uint8_t Year_of_Manufacture; + + uint16_t crc16; +} mddi_client_capability_type; + + +struct __attribute__((packed)) mddi_video_stream { + uint16_t length; + uint16_t type; /* 16 */ + uint16_t client_id; /* 0 */ + + uint16_t video_data_format_descriptor; +/* format of each pixel in the Pixel Data in the present stream in the + * present packet. + * If bits [15:13] = 000 monochrome + * If bits [15:13] = 001 color pixels (palette). + * If bits [15:13] = 010 color pixels in raw RGB + * If bits [15:13] = 011 data in 4:2:2 Y Cb Cr format + * If bits [15:13] = 100 Bayer pixels + */ + + uint16_t pixel_data_attributes; +/* interpreted as follows: + * Bits [1:0] = 11 pixel data is displayed to both eyes + * Bits [1:0] = 10 pixel data is routed to the left eye only. + * Bits [1:0] = 01 pixel data is routed to the right eye only. + * Bits [1:0] = 00 pixel data is routed to the alternate display. + * Bit 2 is 0 Pixel Data is in the standard progressive format. + * Bit 2 is 1 Pixel Data is in interlace format. + * Bit 3 is 0 Pixel Data is in the standard progressive format. + * Bit 3 is 1 Pixel Data is in alternate pixel format. + * Bit 4 is 0 Pixel Data is to or from the display frame buffer. + * Bit 4 is 1 Pixel Data is to or from the camera. + * Bit 5 is 0 pixel data contains the next consecutive row of pixels. + * Bit 5 is 1 X Left Edge, Y Top Edge, X Right Edge, Y Bottom Edge, + * X Start, and Y Start parameters are not defined and + * shall be ignored by the client. + * Bits [7:6] = 01 Pixel data is written to the offline image buffer. + * Bits [7:6] = 00 Pixel data is written to the buffer to refresh display. + * Bits [7:6] = 11 Pixel data is written to all image buffers. + * Bits [7:6] = 10 Invalid. Reserved for future use. + * Bits 8 through 11 alternate display number. + * Bits 12 through 14 are reserved for future use and shall be set to zero. + * Bit 15 is 1 the row of pixels is the last row of pixels in a frame. + */ + + uint16_t x_left_edge; + uint16_t y_top_edge; + /* X,Y coordinate of the top left edge of the screen window */ + + uint16_t x_right_edge; + uint16_t y_bottom_edge; + /* X,Y coordinate of the bottom right edge of the window being + * updated. */ + + uint16_t x_start; + uint16_t y_start; + /* (X Start, Y Start) is the first pixel in the Pixel Data field + * below. */ + + uint16_t pixel_count; + /* number of pixels in the Pixel Data field below. */ + + uint16_t parameter_CRC; + /* 16-bit CRC of all bytes from the Packet Length to the Pixel Count. */ + + uint16_t reserved; + /* 16-bit variable to make structure align on 4 byte boundary */ +}; + +#define TYPE_VIDEO_STREAM 16 +#define TYPE_CLIENT_CAPS 66 +#define TYPE_REGISTER_ACCESS 146 +#define TYPE_CLIENT_STATUS 70 + +struct __attribute__((packed)) mddi_register_access { + uint16_t length; + uint16_t type; /* 146 */ + uint16_t client_id; + + uint16_t read_write_info; + /* Bits 13:0 a 14-bit unsigned integer that specifies the number of + * 32-bit Register Data List items to be transferred in the + * Register Data List field. + * Bits[15:14] = 00 Write to register(s); + * Bits[15:14] = 10 Read from register(s); + * Bits[15:14] = 11 Response to a Read. + * Bits[15:14] = 01 this value is reserved for future use. */ +#define MDDI_WRITE (0 << 14) +#define MDDI_READ (2 << 14) +#define MDDI_READ_RESP (3 << 14) + + uint32_t register_address; + /* the register address that is to be written to or read from. */ + + uint16_t crc16; + + uint32_t register_data_list; + /* list of 4-byte register data values for/from client registers */ +}; + +struct __attribute__((packed)) mddi_llentry { + uint16_t flags; + uint16_t header_count; + uint16_t data_count; + dma_addr_t data; /* 32 bit */ + struct mddi_llentry *next; + uint16_t reserved; + union { + struct mddi_video_stream v; + struct mddi_register_access r; + uint32_t _[12]; + } u; +}; + +#endif diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c new file mode 100644 index 00000000000..99636a2b20f --- /dev/null +++ b/drivers/video/msm/mdp.c @@ -0,0 +1,538 @@ +/* drivers/video/msm_fb/mdp.c + * + * MSM MDP Interface (used by framebuffer core) + * + * Copyright (C) 2007 QUALCOMM Incorporated + * Copyright (C) 2007 Google Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_ANDROID_PMEM +#include +#endif +#include + +#include +#include +#include + +#include "mdp_hw.h" + +struct class *mdp_class; + +#define MDP_CMD_DEBUG_ACCESS_BASE (0x10000) + +static uint16_t mdp_default_ccs[] = { + 0x254, 0x000, 0x331, 0x254, 0xF38, 0xE61, 0x254, 0x409, 0x000, + 0x010, 0x080, 0x080 +}; + +static DECLARE_WAIT_QUEUE_HEAD(mdp_dma2_waitqueue); +static DECLARE_WAIT_QUEUE_HEAD(mdp_ppp_waitqueue); +static struct msmfb_callback *dma_callback; +static struct clk *clk; +static unsigned int mdp_irq_mask; +static DEFINE_SPINLOCK(mdp_lock); +DEFINE_MUTEX(mdp_mutex); + +static int enable_mdp_irq(struct mdp_info *mdp, uint32_t mask) +{ + unsigned long irq_flags; + int ret = 0; + + BUG_ON(!mask); + + spin_lock_irqsave(&mdp_lock, irq_flags); + /* if the mask bits are already set return an error, this interrupt + * is already enabled */ + if (mdp_irq_mask & mask) { + printk(KERN_ERR "mdp irq already on already on %x %x\n", + mdp_irq_mask, mask); + ret = -1; + } + /* if the mdp irq is not already enabled enable it */ + if (!mdp_irq_mask) { + if (clk) + clk_enable(clk); + enable_irq(mdp->irq); + } + + /* update the irq mask to reflect the fact that the interrupt is + * enabled */ + mdp_irq_mask |= mask; + spin_unlock_irqrestore(&mdp_lock, irq_flags); + return ret; +} + +static int locked_disable_mdp_irq(struct mdp_info *mdp, uint32_t mask) +{ + /* this interrupt is already disabled! */ + if (!(mdp_irq_mask & mask)) { + printk(KERN_ERR "mdp irq already off %x %x\n", + mdp_irq_mask, mask); + return -1; + } + /* update the irq mask to reflect the fact that the interrupt is + * disabled */ + mdp_irq_mask &= ~(mask); + /* if no one is waiting on the interrupt, disable it */ + if (!mdp_irq_mask) { + disable_irq(mdp->irq); + if (clk) + clk_disable(clk); + } + return 0; +} + +static int disable_mdp_irq(struct mdp_info *mdp, uint32_t mask) +{ + unsigned long irq_flags; + int ret; + + spin_lock_irqsave(&mdp_lock, irq_flags); + ret = locked_disable_mdp_irq(mdp, mask); + spin_unlock_irqrestore(&mdp_lock, irq_flags); + return ret; +} + +static irqreturn_t mdp_isr(int irq, void *data) +{ + uint32_t status; + unsigned long irq_flags; + struct mdp_info *mdp = data; + + spin_lock_irqsave(&mdp_lock, irq_flags); + + status = mdp_readl(mdp, MDP_INTR_STATUS); + mdp_writel(mdp, status, MDP_INTR_CLEAR); + + status &= mdp_irq_mask; + if (status & DL0_DMA2_TERM_DONE) { + if (dma_callback) { + dma_callback->func(dma_callback); + dma_callback = NULL; + } + wake_up(&mdp_dma2_waitqueue); + } + + if (status & DL0_ROI_DONE) + wake_up(&mdp_ppp_waitqueue); + + if (status) + locked_disable_mdp_irq(mdp, status); + + spin_unlock_irqrestore(&mdp_lock, irq_flags); + return IRQ_HANDLED; +} + +static uint32_t mdp_check_mask(uint32_t mask) +{ + uint32_t ret; + unsigned long irq_flags; + + spin_lock_irqsave(&mdp_lock, irq_flags); + ret = mdp_irq_mask & mask; + spin_unlock_irqrestore(&mdp_lock, irq_flags); + return ret; +} + +static int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq) +{ + int ret = 0; + unsigned long irq_flags; + + wait_event_timeout(*wq, !mdp_check_mask(mask), HZ); + + spin_lock_irqsave(&mdp_lock, irq_flags); + if (mdp_irq_mask & mask) { + locked_disable_mdp_irq(mdp, mask); + printk(KERN_WARNING "timeout waiting for mdp to complete %x\n", + mask); + ret = -ETIMEDOUT; + } + spin_unlock_irqrestore(&mdp_lock, irq_flags); + + return ret; +} + +void mdp_dma_wait(struct mdp_device *mdp_dev) +{ +#define MDP_MAX_TIMEOUTS 20 + static int timeout_count; + struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); + + if (mdp_wait(mdp, DL0_DMA2_TERM_DONE, &mdp_dma2_waitqueue) == -ETIMEDOUT) + timeout_count++; + else + timeout_count = 0; + + if (timeout_count > MDP_MAX_TIMEOUTS) { + printk(KERN_ERR "mdp: dma failed %d times, somethings wrong!\n", + MDP_MAX_TIMEOUTS); + BUG(); + } +} + +static int mdp_ppp_wait(struct mdp_info *mdp) +{ + return mdp_wait(mdp, DL0_ROI_DONE, &mdp_ppp_waitqueue); +} + +void mdp_dma_to_mddi(struct mdp_info *mdp, uint32_t addr, uint32_t stride, + uint32_t width, uint32_t height, uint32_t x, uint32_t y, + struct msmfb_callback *callback) +{ + uint32_t dma2_cfg; + uint16_t ld_param = 0; /* 0=PRIM, 1=SECD, 2=EXT */ + + if (enable_mdp_irq(mdp, DL0_DMA2_TERM_DONE)) { + printk(KERN_ERR "mdp_dma_to_mddi: busy\n"); + return; + } + + dma_callback = callback; + + dma2_cfg = DMA_PACK_TIGHT | + DMA_PACK_ALIGN_LSB | + DMA_PACK_PATTERN_RGB | + DMA_OUT_SEL_AHB | + DMA_IBUF_NONCONTIGUOUS; + + dma2_cfg |= DMA_IBUF_FORMAT_RGB565; + + dma2_cfg |= DMA_OUT_SEL_MDDI; + + dma2_cfg |= DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY; + + dma2_cfg |= DMA_DITHER_EN; + + /* setup size, address, and stride */ + mdp_writel(mdp, (height << 16) | (width), + MDP_CMD_DEBUG_ACCESS_BASE + 0x0184); + mdp_writel(mdp, addr, MDP_CMD_DEBUG_ACCESS_BASE + 0x0188); + mdp_writel(mdp, stride, MDP_CMD_DEBUG_ACCESS_BASE + 0x018C); + + /* 666 18BPP */ + dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; + + /* set y & x offset and MDDI transaction parameters */ + mdp_writel(mdp, (y << 16) | (x), MDP_CMD_DEBUG_ACCESS_BASE + 0x0194); + mdp_writel(mdp, ld_param, MDP_CMD_DEBUG_ACCESS_BASE + 0x01a0); + mdp_writel(mdp, (MDDI_VDO_PACKET_DESC << 16) | MDDI_VDO_PACKET_PRIM, + MDP_CMD_DEBUG_ACCESS_BASE + 0x01a4); + + mdp_writel(mdp, dma2_cfg, MDP_CMD_DEBUG_ACCESS_BASE + 0x0180); + + /* start DMA2 */ + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0044); +} + +void mdp_dma(struct mdp_device *mdp_dev, uint32_t addr, uint32_t stride, + uint32_t width, uint32_t height, uint32_t x, uint32_t y, + struct msmfb_callback *callback, int interface) +{ + struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); + + if (interface == MSM_MDDI_PMDH_INTERFACE) { + mdp_dma_to_mddi(mdp, addr, stride, width, height, x, y, + callback); + } +} + +int get_img(struct mdp_img *img, struct fb_info *info, + unsigned long *start, unsigned long *len, + struct file **filep) +{ + int put_needed, ret = 0; + struct file *file; + unsigned long vstart; + +#ifdef CONFIG_ANDROID_PMEM + if (!get_pmem_file(img->memory_id, start, &vstart, len, filep)) + return 0; +#endif + + file = fget_light(img->memory_id, &put_needed); + if (file == NULL) + return -1; + + if (MAJOR(file->f_dentry->d_inode->i_rdev) == FB_MAJOR) { + *start = info->fix.smem_start; + *len = info->fix.smem_len; + } else + ret = -1; + fput_light(file, put_needed); + + return ret; +} + +void put_img(struct file *src_file, struct file *dst_file) +{ +#ifdef CONFIG_ANDROID_PMEM + if (src_file) + put_pmem_file(src_file); + if (dst_file) + put_pmem_file(dst_file); +#endif +} + +int mdp_blit(struct mdp_device *mdp_dev, struct fb_info *fb, + struct mdp_blit_req *req) +{ + int ret; + unsigned long src_start = 0, src_len = 0, dst_start = 0, dst_len = 0; + struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); + struct file *src_file = 0, *dst_file = 0; + + /* WORKAROUND FOR HARDWARE BUG IN BG TILE FETCH */ + if (unlikely(req->src_rect.h == 0 || + req->src_rect.w == 0)) { + printk(KERN_ERR "mpd_ppp: src img of zero size!\n"); + return -EINVAL; + } + if (unlikely(req->dst_rect.h == 0 || + req->dst_rect.w == 0)) + return -EINVAL; + + /* do this first so that if this fails, the caller can always + * safely call put_img */ + if (unlikely(get_img(&req->src, fb, &src_start, &src_len, &src_file))) { + printk(KERN_ERR "mpd_ppp: could not retrieve src image from " + "memory\n"); + return -EINVAL; + } + + if (unlikely(get_img(&req->dst, fb, &dst_start, &dst_len, &dst_file))) { + printk(KERN_ERR "mpd_ppp: could not retrieve dst image from " + "memory\n"); +#ifdef CONFIG_ANDROID_PMEM + put_pmem_file(src_file); +#endif + return -EINVAL; + } + mutex_lock(&mdp_mutex); + + /* transp_masking unimplemented */ + req->transp_mask = MDP_TRANSP_NOP; + if (unlikely((req->transp_mask != MDP_TRANSP_NOP || + req->alpha != MDP_ALPHA_NOP || + HAS_ALPHA(req->src.format)) && + (req->flags & MDP_ROT_90 && + req->dst_rect.w <= 16 && req->dst_rect.h >= 16))) { + int i; + unsigned int tiles = req->dst_rect.h / 16; + unsigned int remainder = req->dst_rect.h % 16; + req->src_rect.w = 16*req->src_rect.w / req->dst_rect.h; + req->dst_rect.h = 16; + for (i = 0; i < tiles; i++) { + enable_mdp_irq(mdp, DL0_ROI_DONE); + ret = mdp_ppp_blit(mdp, req, src_file, src_start, + src_len, dst_file, dst_start, + dst_len); + if (ret) + goto err_bad_blit; + ret = mdp_ppp_wait(mdp); + if (ret) + goto err_wait_failed; + req->dst_rect.y += 16; + req->src_rect.x += req->src_rect.w; + } + if (!remainder) + goto end; + req->src_rect.w = remainder*req->src_rect.w / req->dst_rect.h; + req->dst_rect.h = remainder; + } + enable_mdp_irq(mdp, DL0_ROI_DONE); + ret = mdp_ppp_blit(mdp, req, src_file, src_start, src_len, dst_file, + dst_start, + dst_len); + if (ret) + goto err_bad_blit; + ret = mdp_ppp_wait(mdp); + if (ret) + goto err_wait_failed; +end: + put_img(src_file, dst_file); + mutex_unlock(&mdp_mutex); + return 0; +err_bad_blit: + disable_mdp_irq(mdp, DL0_ROI_DONE); +err_wait_failed: + put_img(src_file, dst_file); + mutex_unlock(&mdp_mutex); + return ret; +} + +void mdp_set_grp_disp(struct mdp_device *mdp_dev, unsigned disp_id) +{ + struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); + + disp_id &= 0xf; + mdp_writel(mdp, disp_id, MDP_FULL_BYPASS_WORD43); +} + +int register_mdp_client(struct class_interface *cint) +{ + if (!mdp_class) { + pr_err("mdp: no mdp_class when registering mdp client\n"); + return -ENODEV; + } + cint->class = mdp_class; + return class_interface_register(cint); +} + +#include "mdp_csc_table.h" +#include "mdp_scale_tables.h" + +int mdp_probe(struct platform_device *pdev) +{ + struct resource *resource; + int ret; + int n; + struct mdp_info *mdp; + + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!resource) { + pr_err("mdp: can not get mdp mem resource!\n"); + return -ENOMEM; + } + + mdp = kzalloc(sizeof(struct mdp_info), GFP_KERNEL); + if (!mdp) + return -ENOMEM; + + mdp->irq = platform_get_irq(pdev, 0); + if (mdp->irq < 0) { + pr_err("mdp: can not get mdp irq\n"); + ret = mdp->irq; + goto error_get_irq; + } + + mdp->base = ioremap(resource->start, + resource->end - resource->start); + if (mdp->base == 0) { + printk(KERN_ERR "msmfb: cannot allocate mdp regs!\n"); + ret = -ENOMEM; + goto error_ioremap; + } + + mdp->mdp_dev.dma = mdp_dma; + mdp->mdp_dev.dma_wait = mdp_dma_wait; + mdp->mdp_dev.blit = mdp_blit; + mdp->mdp_dev.set_grp_disp = mdp_set_grp_disp; + + clk = clk_get(&pdev->dev, "mdp_clk"); + if (IS_ERR(clk)) { + printk(KERN_INFO "mdp: failed to get mdp clk"); + return PTR_ERR(clk); + } + + ret = request_irq(mdp->irq, mdp_isr, IRQF_DISABLED, "msm_mdp", mdp); + if (ret) + goto error_request_irq; + disable_irq(mdp->irq); + mdp_irq_mask = 0; + + /* debug interface write access */ + mdp_writel(mdp, 1, 0x60); + + mdp_writel(mdp, MDP_ANY_INTR_MASK, MDP_INTR_ENABLE); + mdp_writel(mdp, 1, MDP_EBI2_PORTMAP_MODE); + + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01f8); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01fc); + + for (n = 0; n < ARRAY_SIZE(csc_table); n++) + mdp_writel(mdp, csc_table[n].val, csc_table[n].reg); + + /* clear up unused fg/main registers */ + /* comp.plane 2&3 ystride */ + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0120); + + /* unpacked pattern */ + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x012c); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0130); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0134); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0158); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x015c); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0160); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0170); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0174); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x017c); + + /* comp.plane 2 & 3 */ + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0114); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0118); + + /* clear unused bg registers */ + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01c8); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01d0); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01dc); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01e0); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01e4); + + for (n = 0; n < ARRAY_SIZE(mdp_upscale_table); n++) + mdp_writel(mdp, mdp_upscale_table[n].val, + mdp_upscale_table[n].reg); + + for (n = 0; n < 9; n++) + mdp_writel(mdp, mdp_default_ccs[n], 0x40440 + 4 * n); + mdp_writel(mdp, mdp_default_ccs[9], 0x40500 + 4 * 0); + mdp_writel(mdp, mdp_default_ccs[10], 0x40500 + 4 * 0); + mdp_writel(mdp, mdp_default_ccs[11], 0x40500 + 4 * 0); + + /* register mdp device */ + mdp->mdp_dev.dev.parent = &pdev->dev; + mdp->mdp_dev.dev.class = mdp_class; + snprintf(mdp->mdp_dev.dev.bus_id, BUS_ID_SIZE, "mdp%d", pdev->id); + + /* if you can remove the platform device you'd have to implement + * this: + mdp_dev.release = mdp_class; */ + + ret = device_register(&mdp->mdp_dev.dev); + if (ret) + goto error_device_register; + return 0; + +error_device_register: + free_irq(mdp->irq, mdp); +error_request_irq: + iounmap(mdp->base); +error_get_irq: +error_ioremap: + kfree(mdp); + return ret; +} + +static struct platform_driver msm_mdp_driver = { + .probe = mdp_probe, + .driver = {.name = "msm_mdp"}, +}; + +static int __init mdp_init(void) +{ + mdp_class = class_create(THIS_MODULE, "msm_mdp"); + if (IS_ERR(mdp_class)) { + printk(KERN_ERR "Error creating mdp class\n"); + return PTR_ERR(mdp_class); + } + return platform_driver_register(&msm_mdp_driver); +} + +subsys_initcall(mdp_init); diff --git a/drivers/video/msm/mdp_csc_table.h b/drivers/video/msm/mdp_csc_table.h new file mode 100644 index 00000000000..d1cde30ead5 --- /dev/null +++ b/drivers/video/msm/mdp_csc_table.h @@ -0,0 +1,582 @@ +/* drivers/video/msm_fb/mdp_csc_table.h + * + * Copyright (C) 2007 QUALCOMM Incorporated + * Copyright (C) 2007 Google Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +static struct { + uint32_t reg; + uint32_t val; +} csc_table[] = { + { 0x40400, 0x83 }, + { 0x40404, 0x102 }, + { 0x40408, 0x32 }, + { 0x4040c, 0xffffffb5 }, + { 0x40410, 0xffffff6c }, + { 0x40414, 0xe1 }, + { 0x40418, 0xe1 }, + { 0x4041c, 0xffffff45 }, + { 0x40420, 0xffffffdc }, + { 0x40440, 0x254 }, + { 0x40444, 0x0 }, + { 0x40448, 0x331 }, + { 0x4044c, 0x254 }, + { 0x40450, 0xffffff38 }, + { 0x40454, 0xfffffe61 }, + { 0x40458, 0x254 }, + { 0x4045c, 0x409 }, + { 0x40460, 0x0 }, + { 0x40480, 0x5d }, + { 0x40484, 0x13a }, + { 0x40488, 0x20 }, + { 0x4048c, 0xffffffcd }, + { 0x40490, 0xffffff54 }, + { 0x40494, 0xe1 }, + { 0x40498, 0xe1 }, + { 0x4049c, 0xffffff35 }, + { 0x404a0, 0xffffffec }, + { 0x404c0, 0x254 }, + { 0x404c4, 0x0 }, + { 0x404c8, 0x396 }, + { 0x404cc, 0x254 }, + { 0x404d0, 0xffffff94 }, + { 0x404d4, 0xfffffef0 }, + { 0x404d8, 0x254 }, + { 0x404dc, 0x43a }, + { 0x404e0, 0x0 }, + { 0x40500, 0x10 }, + { 0x40504, 0x80 }, + { 0x40508, 0x80 }, + { 0x40540, 0x10 }, + { 0x40544, 0x80 }, + { 0x40548, 0x80 }, + { 0x40580, 0x10 }, + { 0x40584, 0xeb }, + { 0x40588, 0x10 }, + { 0x4058c, 0xf0 }, + { 0x405c0, 0x10 }, + { 0x405c4, 0xeb }, + { 0x405c8, 0x10 }, + { 0x405cc, 0xf0 }, + { 0x40800, 0x0 }, + { 0x40804, 0x151515 }, + { 0x40808, 0x1d1d1d }, + { 0x4080c, 0x232323 }, + { 0x40810, 0x272727 }, + { 0x40814, 0x2b2b2b }, + { 0x40818, 0x2f2f2f }, + { 0x4081c, 0x333333 }, + { 0x40820, 0x363636 }, + { 0x40824, 0x393939 }, + { 0x40828, 0x3b3b3b }, + { 0x4082c, 0x3e3e3e }, + { 0x40830, 0x404040 }, + { 0x40834, 0x434343 }, + { 0x40838, 0x454545 }, + { 0x4083c, 0x474747 }, + { 0x40840, 0x494949 }, + { 0x40844, 0x4b4b4b }, + { 0x40848, 0x4d4d4d }, + { 0x4084c, 0x4f4f4f }, + { 0x40850, 0x515151 }, + { 0x40854, 0x535353 }, + { 0x40858, 0x555555 }, + { 0x4085c, 0x565656 }, + { 0x40860, 0x585858 }, + { 0x40864, 0x5a5a5a }, + { 0x40868, 0x5b5b5b }, + { 0x4086c, 0x5d5d5d }, + { 0x40870, 0x5e5e5e }, + { 0x40874, 0x606060 }, + { 0x40878, 0x616161 }, + { 0x4087c, 0x636363 }, + { 0x40880, 0x646464 }, + { 0x40884, 0x666666 }, + { 0x40888, 0x676767 }, + { 0x4088c, 0x686868 }, + { 0x40890, 0x6a6a6a }, + { 0x40894, 0x6b6b6b }, + { 0x40898, 0x6c6c6c }, + { 0x4089c, 0x6e6e6e }, + { 0x408a0, 0x6f6f6f }, + { 0x408a4, 0x707070 }, + { 0x408a8, 0x717171 }, + { 0x408ac, 0x727272 }, + { 0x408b0, 0x747474 }, + { 0x408b4, 0x757575 }, + { 0x408b8, 0x767676 }, + { 0x408bc, 0x777777 }, + { 0x408c0, 0x787878 }, + { 0x408c4, 0x797979 }, + { 0x408c8, 0x7a7a7a }, + { 0x408cc, 0x7c7c7c }, + { 0x408d0, 0x7d7d7d }, + { 0x408d4, 0x7e7e7e }, + { 0x408d8, 0x7f7f7f }, + { 0x408dc, 0x808080 }, + { 0x408e0, 0x818181 }, + { 0x408e4, 0x828282 }, + { 0x408e8, 0x838383 }, + { 0x408ec, 0x848484 }, + { 0x408f0, 0x858585 }, + { 0x408f4, 0x868686 }, + { 0x408f8, 0x878787 }, + { 0x408fc, 0x888888 }, + { 0x40900, 0x898989 }, + { 0x40904, 0x8a8a8a }, + { 0x40908, 0x8b8b8b }, + { 0x4090c, 0x8c8c8c }, + { 0x40910, 0x8d8d8d }, + { 0x40914, 0x8e8e8e }, + { 0x40918, 0x8f8f8f }, + { 0x4091c, 0x8f8f8f }, + { 0x40920, 0x909090 }, + { 0x40924, 0x919191 }, + { 0x40928, 0x929292 }, + { 0x4092c, 0x939393 }, + { 0x40930, 0x949494 }, + { 0x40934, 0x959595 }, + { 0x40938, 0x969696 }, + { 0x4093c, 0x969696 }, + { 0x40940, 0x979797 }, + { 0x40944, 0x989898 }, + { 0x40948, 0x999999 }, + { 0x4094c, 0x9a9a9a }, + { 0x40950, 0x9b9b9b }, + { 0x40954, 0x9c9c9c }, + { 0x40958, 0x9c9c9c }, + { 0x4095c, 0x9d9d9d }, + { 0x40960, 0x9e9e9e }, + { 0x40964, 0x9f9f9f }, + { 0x40968, 0xa0a0a0 }, + { 0x4096c, 0xa0a0a0 }, + { 0x40970, 0xa1a1a1 }, + { 0x40974, 0xa2a2a2 }, + { 0x40978, 0xa3a3a3 }, + { 0x4097c, 0xa4a4a4 }, + { 0x40980, 0xa4a4a4 }, + { 0x40984, 0xa5a5a5 }, + { 0x40988, 0xa6a6a6 }, + { 0x4098c, 0xa7a7a7 }, + { 0x40990, 0xa7a7a7 }, + { 0x40994, 0xa8a8a8 }, + { 0x40998, 0xa9a9a9 }, + { 0x4099c, 0xaaaaaa }, + { 0x409a0, 0xaaaaaa }, + { 0x409a4, 0xababab }, + { 0x409a8, 0xacacac }, + { 0x409ac, 0xadadad }, + { 0x409b0, 0xadadad }, + { 0x409b4, 0xaeaeae }, + { 0x409b8, 0xafafaf }, + { 0x409bc, 0xafafaf }, + { 0x409c0, 0xb0b0b0 }, + { 0x409c4, 0xb1b1b1 }, + { 0x409c8, 0xb2b2b2 }, + { 0x409cc, 0xb2b2b2 }, + { 0x409d0, 0xb3b3b3 }, + { 0x409d4, 0xb4b4b4 }, + { 0x409d8, 0xb4b4b4 }, + { 0x409dc, 0xb5b5b5 }, + { 0x409e0, 0xb6b6b6 }, + { 0x409e4, 0xb6b6b6 }, + { 0x409e8, 0xb7b7b7 }, + { 0x409ec, 0xb8b8b8 }, + { 0x409f0, 0xb8b8b8 }, + { 0x409f4, 0xb9b9b9 }, + { 0x409f8, 0xbababa }, + { 0x409fc, 0xbababa }, + { 0x40a00, 0xbbbbbb }, + { 0x40a04, 0xbcbcbc }, + { 0x40a08, 0xbcbcbc }, + { 0x40a0c, 0xbdbdbd }, + { 0x40a10, 0xbebebe }, + { 0x40a14, 0xbebebe }, + { 0x40a18, 0xbfbfbf }, + { 0x40a1c, 0xc0c0c0 }, + { 0x40a20, 0xc0c0c0 }, + { 0x40a24, 0xc1c1c1 }, + { 0x40a28, 0xc1c1c1 }, + { 0x40a2c, 0xc2c2c2 }, + { 0x40a30, 0xc3c3c3 }, + { 0x40a34, 0xc3c3c3 }, + { 0x40a38, 0xc4c4c4 }, + { 0x40a3c, 0xc5c5c5 }, + { 0x40a40, 0xc5c5c5 }, + { 0x40a44, 0xc6c6c6 }, + { 0x40a48, 0xc6c6c6 }, + { 0x40a4c, 0xc7c7c7 }, + { 0x40a50, 0xc8c8c8 }, + { 0x40a54, 0xc8c8c8 }, + { 0x40a58, 0xc9c9c9 }, + { 0x40a5c, 0xc9c9c9 }, + { 0x40a60, 0xcacaca }, + { 0x40a64, 0xcbcbcb }, + { 0x40a68, 0xcbcbcb }, + { 0x40a6c, 0xcccccc }, + { 0x40a70, 0xcccccc }, + { 0x40a74, 0xcdcdcd }, + { 0x40a78, 0xcecece }, + { 0x40a7c, 0xcecece }, + { 0x40a80, 0xcfcfcf }, + { 0x40a84, 0xcfcfcf }, + { 0x40a88, 0xd0d0d0 }, + { 0x40a8c, 0xd0d0d0 }, + { 0x40a90, 0xd1d1d1 }, + { 0x40a94, 0xd2d2d2 }, + { 0x40a98, 0xd2d2d2 }, + { 0x40a9c, 0xd3d3d3 }, + { 0x40aa0, 0xd3d3d3 }, + { 0x40aa4, 0xd4d4d4 }, + { 0x40aa8, 0xd4d4d4 }, + { 0x40aac, 0xd5d5d5 }, + { 0x40ab0, 0xd6d6d6 }, + { 0x40ab4, 0xd6d6d6 }, + { 0x40ab8, 0xd7d7d7 }, + { 0x40abc, 0xd7d7d7 }, + { 0x40ac0, 0xd8d8d8 }, + { 0x40ac4, 0xd8d8d8 }, + { 0x40ac8, 0xd9d9d9 }, + { 0x40acc, 0xd9d9d9 }, + { 0x40ad0, 0xdadada }, + { 0x40ad4, 0xdbdbdb }, + { 0x40ad8, 0xdbdbdb }, + { 0x40adc, 0xdcdcdc }, + { 0x40ae0, 0xdcdcdc }, + { 0x40ae4, 0xdddddd }, + { 0x40ae8, 0xdddddd }, + { 0x40aec, 0xdedede }, + { 0x40af0, 0xdedede }, + { 0x40af4, 0xdfdfdf }, + { 0x40af8, 0xdfdfdf }, + { 0x40afc, 0xe0e0e0 }, + { 0x40b00, 0xe0e0e0 }, + { 0x40b04, 0xe1e1e1 }, + { 0x40b08, 0xe1e1e1 }, + { 0x40b0c, 0xe2e2e2 }, + { 0x40b10, 0xe3e3e3 }, + { 0x40b14, 0xe3e3e3 }, + { 0x40b18, 0xe4e4e4 }, + { 0x40b1c, 0xe4e4e4 }, + { 0x40b20, 0xe5e5e5 }, + { 0x40b24, 0xe5e5e5 }, + { 0x40b28, 0xe6e6e6 }, + { 0x40b2c, 0xe6e6e6 }, + { 0x40b30, 0xe7e7e7 }, + { 0x40b34, 0xe7e7e7 }, + { 0x40b38, 0xe8e8e8 }, + { 0x40b3c, 0xe8e8e8 }, + { 0x40b40, 0xe9e9e9 }, + { 0x40b44, 0xe9e9e9 }, + { 0x40b48, 0xeaeaea }, + { 0x40b4c, 0xeaeaea }, + { 0x40b50, 0xebebeb }, + { 0x40b54, 0xebebeb }, + { 0x40b58, 0xececec }, + { 0x40b5c, 0xececec }, + { 0x40b60, 0xededed }, + { 0x40b64, 0xededed }, + { 0x40b68, 0xeeeeee }, + { 0x40b6c, 0xeeeeee }, + { 0x40b70, 0xefefef }, + { 0x40b74, 0xefefef }, + { 0x40b78, 0xf0f0f0 }, + { 0x40b7c, 0xf0f0f0 }, + { 0x40b80, 0xf1f1f1 }, + { 0x40b84, 0xf1f1f1 }, + { 0x40b88, 0xf2f2f2 }, + { 0x40b8c, 0xf2f2f2 }, + { 0x40b90, 0xf2f2f2 }, + { 0x40b94, 0xf3f3f3 }, + { 0x40b98, 0xf3f3f3 }, + { 0x40b9c, 0xf4f4f4 }, + { 0x40ba0, 0xf4f4f4 }, + { 0x40ba4, 0xf5f5f5 }, + { 0x40ba8, 0xf5f5f5 }, + { 0x40bac, 0xf6f6f6 }, + { 0x40bb0, 0xf6f6f6 }, + { 0x40bb4, 0xf7f7f7 }, + { 0x40bb8, 0xf7f7f7 }, + { 0x40bbc, 0xf8f8f8 }, + { 0x40bc0, 0xf8f8f8 }, + { 0x40bc4, 0xf9f9f9 }, + { 0x40bc8, 0xf9f9f9 }, + { 0x40bcc, 0xfafafa }, + { 0x40bd0, 0xfafafa }, + { 0x40bd4, 0xfafafa }, + { 0x40bd8, 0xfbfbfb }, + { 0x40bdc, 0xfbfbfb }, + { 0x40be0, 0xfcfcfc }, + { 0x40be4, 0xfcfcfc }, + { 0x40be8, 0xfdfdfd }, + { 0x40bec, 0xfdfdfd }, + { 0x40bf0, 0xfefefe }, + { 0x40bf4, 0xfefefe }, + { 0x40bf8, 0xffffff }, + { 0x40bfc, 0xffffff }, + { 0x40c00, 0x0 }, + { 0x40c04, 0x0 }, + { 0x40c08, 0x0 }, + { 0x40c0c, 0x0 }, + { 0x40c10, 0x0 }, + { 0x40c14, 0x0 }, + { 0x40c18, 0x0 }, + { 0x40c1c, 0x0 }, + { 0x40c20, 0x0 }, + { 0x40c24, 0x0 }, + { 0x40c28, 0x0 }, + { 0x40c2c, 0x0 }, + { 0x40c30, 0x0 }, + { 0x40c34, 0x0 }, + { 0x40c38, 0x0 }, + { 0x40c3c, 0x0 }, + { 0x40c40, 0x10101 }, + { 0x40c44, 0x10101 }, + { 0x40c48, 0x10101 }, + { 0x40c4c, 0x10101 }, + { 0x40c50, 0x10101 }, + { 0x40c54, 0x10101 }, + { 0x40c58, 0x10101 }, + { 0x40c5c, 0x10101 }, + { 0x40c60, 0x10101 }, + { 0x40c64, 0x10101 }, + { 0x40c68, 0x20202 }, + { 0x40c6c, 0x20202 }, + { 0x40c70, 0x20202 }, + { 0x40c74, 0x20202 }, + { 0x40c78, 0x20202 }, + { 0x40c7c, 0x20202 }, + { 0x40c80, 0x30303 }, + { 0x40c84, 0x30303 }, + { 0x40c88, 0x30303 }, + { 0x40c8c, 0x30303 }, + { 0x40c90, 0x30303 }, + { 0x40c94, 0x40404 }, + { 0x40c98, 0x40404 }, + { 0x40c9c, 0x40404 }, + { 0x40ca0, 0x40404 }, + { 0x40ca4, 0x40404 }, + { 0x40ca8, 0x50505 }, + { 0x40cac, 0x50505 }, + { 0x40cb0, 0x50505 }, + { 0x40cb4, 0x50505 }, + { 0x40cb8, 0x60606 }, + { 0x40cbc, 0x60606 }, + { 0x40cc0, 0x60606 }, + { 0x40cc4, 0x70707 }, + { 0x40cc8, 0x70707 }, + { 0x40ccc, 0x70707 }, + { 0x40cd0, 0x70707 }, + { 0x40cd4, 0x80808 }, + { 0x40cd8, 0x80808 }, + { 0x40cdc, 0x80808 }, + { 0x40ce0, 0x90909 }, + { 0x40ce4, 0x90909 }, + { 0x40ce8, 0xa0a0a }, + { 0x40cec, 0xa0a0a }, + { 0x40cf0, 0xa0a0a }, + { 0x40cf4, 0xb0b0b }, + { 0x40cf8, 0xb0b0b }, + { 0x40cfc, 0xb0b0b }, + { 0x40d00, 0xc0c0c }, + { 0x40d04, 0xc0c0c }, + { 0x40d08, 0xd0d0d }, + { 0x40d0c, 0xd0d0d }, + { 0x40d10, 0xe0e0e }, + { 0x40d14, 0xe0e0e }, + { 0x40d18, 0xe0e0e }, + { 0x40d1c, 0xf0f0f }, + { 0x40d20, 0xf0f0f }, + { 0x40d24, 0x101010 }, + { 0x40d28, 0x101010 }, + { 0x40d2c, 0x111111 }, + { 0x40d30, 0x111111 }, + { 0x40d34, 0x121212 }, + { 0x40d38, 0x121212 }, + { 0x40d3c, 0x131313 }, + { 0x40d40, 0x131313 }, + { 0x40d44, 0x141414 }, + { 0x40d48, 0x151515 }, + { 0x40d4c, 0x151515 }, + { 0x40d50, 0x161616 }, + { 0x40d54, 0x161616 }, + { 0x40d58, 0x171717 }, + { 0x40d5c, 0x171717 }, + { 0x40d60, 0x181818 }, + { 0x40d64, 0x191919 }, + { 0x40d68, 0x191919 }, + { 0x40d6c, 0x1a1a1a }, + { 0x40d70, 0x1b1b1b }, + { 0x40d74, 0x1b1b1b }, + { 0x40d78, 0x1c1c1c }, + { 0x40d7c, 0x1c1c1c }, + { 0x40d80, 0x1d1d1d }, + { 0x40d84, 0x1e1e1e }, + { 0x40d88, 0x1f1f1f }, + { 0x40d8c, 0x1f1f1f }, + { 0x40d90, 0x202020 }, + { 0x40d94, 0x212121 }, + { 0x40d98, 0x212121 }, + { 0x40d9c, 0x222222 }, + { 0x40da0, 0x232323 }, + { 0x40da4, 0x242424 }, + { 0x40da8, 0x242424 }, + { 0x40dac, 0x252525 }, + { 0x40db0, 0x262626 }, + { 0x40db4, 0x272727 }, + { 0x40db8, 0x272727 }, + { 0x40dbc, 0x282828 }, + { 0x40dc0, 0x292929 }, + { 0x40dc4, 0x2a2a2a }, + { 0x40dc8, 0x2b2b2b }, + { 0x40dcc, 0x2c2c2c }, + { 0x40dd0, 0x2c2c2c }, + { 0x40dd4, 0x2d2d2d }, + { 0x40dd8, 0x2e2e2e }, + { 0x40ddc, 0x2f2f2f }, + { 0x40de0, 0x303030 }, + { 0x40de4, 0x313131 }, + { 0x40de8, 0x323232 }, + { 0x40dec, 0x333333 }, + { 0x40df0, 0x333333 }, + { 0x40df4, 0x343434 }, + { 0x40df8, 0x353535 }, + { 0x40dfc, 0x363636 }, + { 0x40e00, 0x373737 }, + { 0x40e04, 0x383838 }, + { 0x40e08, 0x393939 }, + { 0x40e0c, 0x3a3a3a }, + { 0x40e10, 0x3b3b3b }, + { 0x40e14, 0x3c3c3c }, + { 0x40e18, 0x3d3d3d }, + { 0x40e1c, 0x3e3e3e }, + { 0x40e20, 0x3f3f3f }, + { 0x40e24, 0x404040 }, + { 0x40e28, 0x414141 }, + { 0x40e2c, 0x424242 }, + { 0x40e30, 0x434343 }, + { 0x40e34, 0x444444 }, + { 0x40e38, 0x464646 }, + { 0x40e3c, 0x474747 }, + { 0x40e40, 0x484848 }, + { 0x40e44, 0x494949 }, + { 0x40e48, 0x4a4a4a }, + { 0x40e4c, 0x4b4b4b }, + { 0x40e50, 0x4c4c4c }, + { 0x40e54, 0x4d4d4d }, + { 0x40e58, 0x4f4f4f }, + { 0x40e5c, 0x505050 }, + { 0x40e60, 0x515151 }, + { 0x40e64, 0x525252 }, + { 0x40e68, 0x535353 }, + { 0x40e6c, 0x545454 }, + { 0x40e70, 0x565656 }, + { 0x40e74, 0x575757 }, + { 0x40e78, 0x585858 }, + { 0x40e7c, 0x595959 }, + { 0x40e80, 0x5b5b5b }, + { 0x40e84, 0x5c5c5c }, + { 0x40e88, 0x5d5d5d }, + { 0x40e8c, 0x5e5e5e }, + { 0x40e90, 0x606060 }, + { 0x40e94, 0x616161 }, + { 0x40e98, 0x626262 }, + { 0x40e9c, 0x646464 }, + { 0x40ea0, 0x656565 }, + { 0x40ea4, 0x666666 }, + { 0x40ea8, 0x686868 }, + { 0x40eac, 0x696969 }, + { 0x40eb0, 0x6a6a6a }, + { 0x40eb4, 0x6c6c6c }, + { 0x40eb8, 0x6d6d6d }, + { 0x40ebc, 0x6f6f6f }, + { 0x40ec0, 0x707070 }, + { 0x40ec4, 0x717171 }, + { 0x40ec8, 0x737373 }, + { 0x40ecc, 0x747474 }, + { 0x40ed0, 0x767676 }, + { 0x40ed4, 0x777777 }, + { 0x40ed8, 0x797979 }, + { 0x40edc, 0x7a7a7a }, + { 0x40ee0, 0x7c7c7c }, + { 0x40ee4, 0x7d7d7d }, + { 0x40ee8, 0x7f7f7f }, + { 0x40eec, 0x808080 }, + { 0x40ef0, 0x828282 }, + { 0x40ef4, 0x838383 }, + { 0x40ef8, 0x858585 }, + { 0x40efc, 0x868686 }, + { 0x40f00, 0x888888 }, + { 0x40f04, 0x898989 }, + { 0x40f08, 0x8b8b8b }, + { 0x40f0c, 0x8d8d8d }, + { 0x40f10, 0x8e8e8e }, + { 0x40f14, 0x909090 }, + { 0x40f18, 0x919191 }, + { 0x40f1c, 0x939393 }, + { 0x40f20, 0x959595 }, + { 0x40f24, 0x969696 }, + { 0x40f28, 0x989898 }, + { 0x40f2c, 0x9a9a9a }, + { 0x40f30, 0x9b9b9b }, + { 0x40f34, 0x9d9d9d }, + { 0x40f38, 0x9f9f9f }, + { 0x40f3c, 0xa1a1a1 }, + { 0x40f40, 0xa2a2a2 }, + { 0x40f44, 0xa4a4a4 }, + { 0x40f48, 0xa6a6a6 }, + { 0x40f4c, 0xa7a7a7 }, + { 0x40f50, 0xa9a9a9 }, + { 0x40f54, 0xababab }, + { 0x40f58, 0xadadad }, + { 0x40f5c, 0xafafaf }, + { 0x40f60, 0xb0b0b0 }, + { 0x40f64, 0xb2b2b2 }, + { 0x40f68, 0xb4b4b4 }, + { 0x40f6c, 0xb6b6b6 }, + { 0x40f70, 0xb8b8b8 }, + { 0x40f74, 0xbababa }, + { 0x40f78, 0xbbbbbb }, + { 0x40f7c, 0xbdbdbd }, + { 0x40f80, 0xbfbfbf }, + { 0x40f84, 0xc1c1c1 }, + { 0x40f88, 0xc3c3c3 }, + { 0x40f8c, 0xc5c5c5 }, + { 0x40f90, 0xc7c7c7 }, + { 0x40f94, 0xc9c9c9 }, + { 0x40f98, 0xcbcbcb }, + { 0x40f9c, 0xcdcdcd }, + { 0x40fa0, 0xcfcfcf }, + { 0x40fa4, 0xd1d1d1 }, + { 0x40fa8, 0xd3d3d3 }, + { 0x40fac, 0xd5d5d5 }, + { 0x40fb0, 0xd7d7d7 }, + { 0x40fb4, 0xd9d9d9 }, + { 0x40fb8, 0xdbdbdb }, + { 0x40fbc, 0xdddddd }, + { 0x40fc0, 0xdfdfdf }, + { 0x40fc4, 0xe1e1e1 }, + { 0x40fc8, 0xe3e3e3 }, + { 0x40fcc, 0xe5e5e5 }, + { 0x40fd0, 0xe7e7e7 }, + { 0x40fd4, 0xe9e9e9 }, + { 0x40fd8, 0xebebeb }, + { 0x40fdc, 0xeeeeee }, + { 0x40fe0, 0xf0f0f0 }, + { 0x40fe4, 0xf2f2f2 }, + { 0x40fe8, 0xf4f4f4 }, + { 0x40fec, 0xf6f6f6 }, + { 0x40ff0, 0xf8f8f8 }, + { 0x40ff4, 0xfbfbfb }, + { 0x40ff8, 0xfdfdfd }, + { 0x40ffc, 0xffffff }, +}; diff --git a/drivers/video/msm/mdp_hw.h b/drivers/video/msm/mdp_hw.h new file mode 100644 index 00000000000..4e3deb4e592 --- /dev/null +++ b/drivers/video/msm/mdp_hw.h @@ -0,0 +1,621 @@ +/* drivers/video/msm_fb/mdp_hw.h + * + * Copyright (C) 2007 QUALCOMM Incorporated + * Copyright (C) 2007 Google Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ +#ifndef _MDP_HW_H_ +#define _MDP_HW_H_ + +#include +#include + +struct mdp_info { + struct mdp_device mdp_dev; + char * __iomem base; + int irq; +}; +struct mdp_blit_req; +struct mdp_device; +int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, + struct file *src_file, unsigned long src_start, + unsigned long src_len, struct file *dst_file, + unsigned long dst_start, unsigned long dst_len); +#define mdp_writel(mdp, value, offset) writel(value, mdp->base + offset) +#define mdp_readl(mdp, offset) readl(mdp->base + offset) + +#define MDP_SYNC_CONFIG_0 (0x00000) +#define MDP_SYNC_CONFIG_1 (0x00004) +#define MDP_SYNC_CONFIG_2 (0x00008) +#define MDP_SYNC_STATUS_0 (0x0000c) +#define MDP_SYNC_STATUS_1 (0x00010) +#define MDP_SYNC_STATUS_2 (0x00014) +#define MDP_SYNC_THRESH_0 (0x00018) +#define MDP_SYNC_THRESH_1 (0x0001c) +#define MDP_INTR_ENABLE (0x00020) +#define MDP_INTR_STATUS (0x00024) +#define MDP_INTR_CLEAR (0x00028) +#define MDP_DISPLAY0_START (0x00030) +#define MDP_DISPLAY1_START (0x00034) +#define MDP_DISPLAY_STATUS (0x00038) +#define MDP_EBI2_LCD0 (0x0003c) +#define MDP_EBI2_LCD1 (0x00040) +#define MDP_DISPLAY0_ADDR (0x00054) +#define MDP_DISPLAY1_ADDR (0x00058) +#define MDP_EBI2_PORTMAP_MODE (0x0005c) +#define MDP_MODE (0x00060) +#define MDP_TV_OUT_STATUS (0x00064) +#define MDP_HW_VERSION (0x00070) +#define MDP_SW_RESET (0x00074) +#define MDP_AXI_ERROR_MASTER_STOP (0x00078) +#define MDP_SEL_CLK_OR_HCLK_TEST_BUS (0x0007c) +#define MDP_PRIMARY_VSYNC_OUT_CTRL (0x00080) +#define MDP_SECONDARY_VSYNC_OUT_CTRL (0x00084) +#define MDP_EXTERNAL_VSYNC_OUT_CTRL (0x00088) +#define MDP_VSYNC_CTRL (0x0008c) +#define MDP_CGC_EN (0x00100) +#define MDP_CMD_STATUS (0x10008) +#define MDP_PROFILE_EN (0x10010) +#define MDP_PROFILE_COUNT (0x10014) +#define MDP_DMA_START (0x10044) +#define MDP_FULL_BYPASS_WORD0 (0x10100) +#define MDP_FULL_BYPASS_WORD1 (0x10104) +#define MDP_COMMAND_CONFIG (0x10104) +#define MDP_FULL_BYPASS_WORD2 (0x10108) +#define MDP_FULL_BYPASS_WORD3 (0x1010c) +#define MDP_FULL_BYPASS_WORD4 (0x10110) +#define MDP_FULL_BYPASS_WORD6 (0x10118) +#define MDP_FULL_BYPASS_WORD7 (0x1011c) +#define MDP_FULL_BYPASS_WORD8 (0x10120) +#define MDP_FULL_BYPASS_WORD9 (0x10124) +#define MDP_PPP_SOURCE_CONFIG (0x10124) +#define MDP_FULL_BYPASS_WORD10 (0x10128) +#define MDP_FULL_BYPASS_WORD11 (0x1012c) +#define MDP_FULL_BYPASS_WORD12 (0x10130) +#define MDP_FULL_BYPASS_WORD13 (0x10134) +#define MDP_FULL_BYPASS_WORD14 (0x10138) +#define MDP_PPP_OPERATION_CONFIG (0x10138) +#define MDP_FULL_BYPASS_WORD15 (0x1013c) +#define MDP_FULL_BYPASS_WORD16 (0x10140) +#define MDP_FULL_BYPASS_WORD17 (0x10144) +#define MDP_FULL_BYPASS_WORD18 (0x10148) +#define MDP_FULL_BYPASS_WORD19 (0x1014c) +#define MDP_FULL_BYPASS_WORD20 (0x10150) +#define MDP_PPP_DESTINATION_CONFIG (0x10150) +#define MDP_FULL_BYPASS_WORD21 (0x10154) +#define MDP_FULL_BYPASS_WORD22 (0x10158) +#define MDP_FULL_BYPASS_WORD23 (0x1015c) +#define MDP_FULL_BYPASS_WORD24 (0x10160) +#define MDP_FULL_BYPASS_WORD25 (0x10164) +#define MDP_FULL_BYPASS_WORD26 (0x10168) +#define MDP_FULL_BYPASS_WORD27 (0x1016c) +#define MDP_FULL_BYPASS_WORD29 (0x10174) +#define MDP_FULL_BYPASS_WORD30 (0x10178) +#define MDP_FULL_BYPASS_WORD31 (0x1017c) +#define MDP_FULL_BYPASS_WORD32 (0x10180) +#define MDP_DMA_CONFIG (0x10180) +#define MDP_FULL_BYPASS_WORD33 (0x10184) +#define MDP_FULL_BYPASS_WORD34 (0x10188) +#define MDP_FULL_BYPASS_WORD35 (0x1018c) +#define MDP_FULL_BYPASS_WORD37 (0x10194) +#define MDP_FULL_BYPASS_WORD39 (0x1019c) +#define MDP_FULL_BYPASS_WORD40 (0x101a0) +#define MDP_FULL_BYPASS_WORD41 (0x101a4) +#define MDP_FULL_BYPASS_WORD43 (0x101ac) +#define MDP_FULL_BYPASS_WORD46 (0x101b8) +#define MDP_FULL_BYPASS_WORD47 (0x101bc) +#define MDP_FULL_BYPASS_WORD48 (0x101c0) +#define MDP_FULL_BYPASS_WORD49 (0x101c4) +#define MDP_FULL_BYPASS_WORD50 (0x101c8) +#define MDP_FULL_BYPASS_WORD51 (0x101cc) +#define MDP_FULL_BYPASS_WORD52 (0x101d0) +#define MDP_FULL_BYPASS_WORD53 (0x101d4) +#define MDP_FULL_BYPASS_WORD54 (0x101d8) +#define MDP_FULL_BYPASS_WORD55 (0x101dc) +#define MDP_FULL_BYPASS_WORD56 (0x101e0) +#define MDP_FULL_BYPASS_WORD57 (0x101e4) +#define MDP_FULL_BYPASS_WORD58 (0x101e8) +#define MDP_FULL_BYPASS_WORD59 (0x101ec) +#define MDP_FULL_BYPASS_WORD60 (0x101f0) +#define MDP_VSYNC_THRESHOLD (0x101f0) +#define MDP_FULL_BYPASS_WORD61 (0x101f4) +#define MDP_FULL_BYPASS_WORD62 (0x101f8) +#define MDP_FULL_BYPASS_WORD63 (0x101fc) +#define MDP_TFETCH_TEST_MODE (0x20004) +#define MDP_TFETCH_STATUS (0x20008) +#define MDP_TFETCH_TILE_COUNT (0x20010) +#define MDP_TFETCH_FETCH_COUNT (0x20014) +#define MDP_TFETCH_CONSTANT_COLOR (0x20040) +#define MDP_CSC_BYPASS (0x40004) +#define MDP_SCALE_COEFF_LSB (0x5fffc) +#define MDP_TV_OUT_CTL (0xc0000) +#define MDP_TV_OUT_FIR_COEFF (0xc0004) +#define MDP_TV_OUT_BUF_ADDR (0xc0008) +#define MDP_TV_OUT_CC_DATA (0xc000c) +#define MDP_TV_OUT_SOBEL (0xc0010) +#define MDP_TV_OUT_Y_CLAMP (0xc0018) +#define MDP_TV_OUT_CB_CLAMP (0xc001c) +#define MDP_TV_OUT_CR_CLAMP (0xc0020) +#define MDP_TEST_MODE_CLK (0xd0000) +#define MDP_TEST_MISR_RESET_CLK (0xd0004) +#define MDP_TEST_EXPORT_MISR_CLK (0xd0008) +#define MDP_TEST_MISR_CURR_VAL_CLK (0xd000c) +#define MDP_TEST_MODE_HCLK (0xd0100) +#define MDP_TEST_MISR_RESET_HCLK (0xd0104) +#define MDP_TEST_EXPORT_MISR_HCLK (0xd0108) +#define MDP_TEST_MISR_CURR_VAL_HCLK (0xd010c) +#define MDP_TEST_MODE_DCLK (0xd0200) +#define MDP_TEST_MISR_RESET_DCLK (0xd0204) +#define MDP_TEST_EXPORT_MISR_DCLK (0xd0208) +#define MDP_TEST_MISR_CURR_VAL_DCLK (0xd020c) +#define MDP_TEST_CAPTURED_DCLK (0xd0210) +#define MDP_TEST_MISR_CAPT_VAL_DCLK (0xd0214) +#define MDP_LCDC_CTL (0xe0000) +#define MDP_LCDC_HSYNC_CTL (0xe0004) +#define MDP_LCDC_VSYNC_CTL (0xe0008) +#define MDP_LCDC_ACTIVE_HCTL (0xe000c) +#define MDP_LCDC_ACTIVE_VCTL (0xe0010) +#define MDP_LCDC_BORDER_CLR (0xe0014) +#define MDP_LCDC_H_BLANK (0xe0018) +#define MDP_LCDC_V_BLANK (0xe001c) +#define MDP_LCDC_UNDERFLOW_CLR (0xe0020) +#define MDP_LCDC_HSYNC_SKEW (0xe0024) +#define MDP_LCDC_TEST_CTL (0xe0028) +#define MDP_LCDC_LINE_IRQ (0xe002c) +#define MDP_LCDC_CTL_POLARITY (0xe0030) +#define MDP_LCDC_DMA_CONFIG (0xe1000) +#define MDP_LCDC_DMA_SIZE (0xe1004) +#define MDP_LCDC_DMA_IBUF_ADDR (0xe1008) +#define MDP_LCDC_DMA_IBUF_Y_STRIDE (0xe100c) + + +#define MDP_DMA2_TERM 0x1 +#define MDP_DMA3_TERM 0x2 +#define MDP_PPP_TERM 0x3 + +/* MDP_INTR_ENABLE */ +#define DL0_ROI_DONE (1<<0) +#define DL1_ROI_DONE (1<<1) +#define DL0_DMA2_TERM_DONE (1<<2) +#define DL1_DMA2_TERM_DONE (1<<3) +#define DL0_PPP_TERM_DONE (1<<4) +#define DL1_PPP_TERM_DONE (1<<5) +#define TV_OUT_DMA3_DONE (1<<6) +#define TV_ENC_UNDERRUN (1<<7) +#define DL0_FETCH_DONE (1<<11) +#define DL1_FETCH_DONE (1<<12) + +#define MDP_PPP_BUSY_STATUS (DL0_ROI_DONE| \ + DL1_ROI_DONE| \ + DL0_PPP_TERM_DONE| \ + DL1_PPP_TERM_DONE) + +#define MDP_ANY_INTR_MASK (DL0_ROI_DONE| \ + DL1_ROI_DONE| \ + DL0_DMA2_TERM_DONE| \ + DL1_DMA2_TERM_DONE| \ + DL0_PPP_TERM_DONE| \ + DL1_PPP_TERM_DONE| \ + DL0_FETCH_DONE| \ + DL1_FETCH_DONE| \ + TV_ENC_UNDERRUN) + +#define MDP_TOP_LUMA 16 +#define MDP_TOP_CHROMA 0 +#define MDP_BOTTOM_LUMA 19 +#define MDP_BOTTOM_CHROMA 3 +#define MDP_LEFT_LUMA 22 +#define MDP_LEFT_CHROMA 6 +#define MDP_RIGHT_LUMA 25 +#define MDP_RIGHT_CHROMA 9 + +#define CLR_G 0x0 +#define CLR_B 0x1 +#define CLR_R 0x2 +#define CLR_ALPHA 0x3 + +#define CLR_Y CLR_G +#define CLR_CB CLR_B +#define CLR_CR CLR_R + +/* from lsb to msb */ +#define MDP_GET_PACK_PATTERN(a, x, y, z, bit) \ + (((a)<<(bit*3))|((x)<<(bit*2))|((y)< +#include +#include +#include +#include +#include + +#include "mdp_hw.h" +#include "mdp_scale_tables.h" + +#define DLOG(x...) do {} while (0) + +#define MDP_DOWNSCALE_BLUR (MDP_DOWNSCALE_MAX + 1) +static int downscale_y_table = MDP_DOWNSCALE_MAX; +static int downscale_x_table = MDP_DOWNSCALE_MAX; + +struct mdp_regs { + uint32_t src0; + uint32_t src1; + uint32_t dst0; + uint32_t dst1; + uint32_t src_cfg; + uint32_t dst_cfg; + uint32_t src_pack; + uint32_t dst_pack; + uint32_t src_rect; + uint32_t dst_rect; + uint32_t src_ystride; + uint32_t dst_ystride; + uint32_t op; + uint32_t src_bpp; + uint32_t dst_bpp; + uint32_t edge; + uint32_t phasex_init; + uint32_t phasey_init; + uint32_t phasex_step; + uint32_t phasey_step; +}; + +static uint32_t pack_pattern[] = { + PPP_ARRAY0(PACK_PATTERN) +}; + +static uint32_t src_img_cfg[] = { + PPP_ARRAY1(CFG, SRC) +}; + +static uint32_t dst_img_cfg[] = { + PPP_ARRAY1(CFG, DST) +}; + +static uint32_t bytes_per_pixel[] = { + [MDP_RGB_565] = 2, + [MDP_RGB_888] = 3, + [MDP_XRGB_8888] = 4, + [MDP_ARGB_8888] = 4, + [MDP_RGBA_8888] = 4, + [MDP_BGRA_8888] = 4, + [MDP_Y_CBCR_H2V1] = 1, + [MDP_Y_CBCR_H2V2] = 1, + [MDP_Y_CRCB_H2V1] = 1, + [MDP_Y_CRCB_H2V2] = 1, + [MDP_YCRYCB_H2V1] = 2 +}; + +static uint32_t dst_op_chroma[] = { + PPP_ARRAY1(CHROMA_SAMP, DST) +}; + +static uint32_t src_op_chroma[] = { + PPP_ARRAY1(CHROMA_SAMP, SRC) +}; + +static uint32_t bg_op_chroma[] = { + PPP_ARRAY1(CHROMA_SAMP, BG) +}; + +static void rotate_dst_addr_x(struct mdp_blit_req *req, struct mdp_regs *regs) +{ + regs->dst0 += (req->dst_rect.w - + min((uint32_t)16, req->dst_rect.w)) * regs->dst_bpp; + regs->dst1 += (req->dst_rect.w - + min((uint32_t)16, req->dst_rect.w)) * regs->dst_bpp; +} + +static void rotate_dst_addr_y(struct mdp_blit_req *req, struct mdp_regs *regs) +{ + regs->dst0 += (req->dst_rect.h - + min((uint32_t)16, req->dst_rect.h)) * + regs->dst_ystride; + regs->dst1 += (req->dst_rect.h - + min((uint32_t)16, req->dst_rect.h)) * + regs->dst_ystride; +} + +static void blit_rotate(struct mdp_blit_req *req, + struct mdp_regs *regs) +{ + if (req->flags == MDP_ROT_NOP) + return; + + regs->op |= PPP_OP_ROT_ON; + if ((req->flags & MDP_ROT_90 || req->flags & MDP_FLIP_LR) && + !(req->flags & MDP_ROT_90 && req->flags & MDP_FLIP_LR)) + rotate_dst_addr_x(req, regs); + if (req->flags & MDP_ROT_90) + regs->op |= PPP_OP_ROT_90; + if (req->flags & MDP_FLIP_UD) { + regs->op |= PPP_OP_FLIP_UD; + rotate_dst_addr_y(req, regs); + } + if (req->flags & MDP_FLIP_LR) + regs->op |= PPP_OP_FLIP_LR; +} + +static void blit_convert(struct mdp_blit_req *req, struct mdp_regs *regs) +{ + if (req->src.format == req->dst.format) + return; + if (IS_RGB(req->src.format) && IS_YCRCB(req->dst.format)) { + regs->op |= PPP_OP_CONVERT_RGB2YCBCR | PPP_OP_CONVERT_ON; + } else if (IS_YCRCB(req->src.format) && IS_RGB(req->dst.format)) { + regs->op |= PPP_OP_CONVERT_YCBCR2RGB | PPP_OP_CONVERT_ON; + if (req->dst.format == MDP_RGB_565) + regs->op |= PPP_OP_CONVERT_MATRIX_SECONDARY; + } +} + +#define GET_BIT_RANGE(value, high, low) \ + (((1 << (high - low + 1)) - 1) & (value >> low)) +static uint32_t transp_convert(struct mdp_blit_req *req) +{ + uint32_t transp = 0; + if (req->src.format == MDP_RGB_565) { + /* pad each value to 8 bits by copying the high bits into the + * low end, convert RGB to RBG by switching low 2 components */ + transp |= ((GET_BIT_RANGE(req->transp_mask, 15, 11) << 3) | + (GET_BIT_RANGE(req->transp_mask, 15, 13))) << 16; + + transp |= ((GET_BIT_RANGE(req->transp_mask, 4, 0) << 3) | + (GET_BIT_RANGE(req->transp_mask, 4, 2))) << 8; + + transp |= (GET_BIT_RANGE(req->transp_mask, 10, 5) << 2) | + (GET_BIT_RANGE(req->transp_mask, 10, 9)); + } else { + /* convert RGB to RBG */ + transp |= (GET_BIT_RANGE(req->transp_mask, 15, 8)) | + (GET_BIT_RANGE(req->transp_mask, 23, 16) << 16) | + (GET_BIT_RANGE(req->transp_mask, 7, 0) << 8); + } + return transp; +} +#undef GET_BIT_RANGE + +static void blit_blend(struct mdp_blit_req *req, struct mdp_regs *regs) +{ + /* TRANSP BLEND */ + if (req->transp_mask != MDP_TRANSP_NOP) { + req->transp_mask = transp_convert(req); + if (req->alpha != MDP_ALPHA_NOP) { + /* use blended transparancy mode + * pixel = (src == transp) ? dst : blend + * blend is combo of blend_eq_sel and + * blend_alpha_sel */ + regs->op |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON | + PPP_OP_BLEND_ALPHA_BLEND_NORMAL | + PPP_OP_BLEND_CONSTANT_ALPHA | + PPP_BLEND_ALPHA_TRANSP; + } else { + /* simple transparancy mode + * pixel = (src == transp) ? dst : src */ + regs->op |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON | + PPP_OP_BLEND_SRCPIXEL_TRANSP; + } + } + + req->alpha &= 0xff; + /* ALPHA BLEND */ + if (HAS_ALPHA(req->src.format)) { + regs->op |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON | + PPP_OP_BLEND_SRCPIXEL_ALPHA; + } else if (req->alpha < MDP_ALPHA_NOP) { + /* just blend by alpha */ + regs->op |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON | + PPP_OP_BLEND_ALPHA_BLEND_NORMAL | + PPP_OP_BLEND_CONSTANT_ALPHA; + } + + regs->op |= bg_op_chroma[req->dst.format]; +} + +#define ONE_HALF (1LL << 32) +#define ONE (1LL << 33) +#define TWO (2LL << 33) +#define THREE (3LL << 33) +#define FRAC_MASK (ONE - 1) +#define INT_MASK (~FRAC_MASK) + +static int scale_params(uint32_t dim_in, uint32_t dim_out, uint32_t origin, + uint32_t *phase_init, uint32_t *phase_step) +{ + /* to improve precicsion calculations are done in U31.33 and converted + * to U3.29 at the end */ + int64_t k1, k2, k3, k4, tmp; + uint64_t n, d, os, os_p, od, od_p, oreq; + unsigned rpa = 0; + int64_t ip64, delta; + + if (dim_out % 3 == 0) + rpa = !(dim_in % (dim_out / 3)); + + n = ((uint64_t)dim_out) << 34; + d = dim_in; + if (!d) + return -1; + do_div(n, d); + k3 = (n + 1) >> 1; + if ((k3 >> 4) < (1LL << 27) || (k3 >> 4) > (1LL << 31)) { + DLOG("crap bad scale\n"); + return -1; + } + n = ((uint64_t)dim_in) << 34; + d = (uint64_t)dim_out; + if (!d) + return -1; + do_div(n, d); + k1 = (n + 1) >> 1; + k2 = (k1 - ONE) >> 1; + + *phase_init = (int)(k2 >> 4); + k4 = (k3 - ONE) >> 1; + + if (rpa) { + os = ((uint64_t)origin << 33) - ONE_HALF; + tmp = (dim_out * os) + ONE_HALF; + if (!dim_in) + return -1; + do_div(tmp, dim_in); + od = tmp - ONE_HALF; + } else { + os = ((uint64_t)origin << 1) - 1; + od = (((k3 * os) >> 1) + k4); + } + + od_p = od & INT_MASK; + if (od_p != od) + od_p += ONE; + + if (rpa) { + tmp = (dim_in * od_p) + ONE_HALF; + if (!dim_in) + return -1; + do_div(tmp, dim_in); + os_p = tmp - ONE_HALF; + } else { + os_p = ((k1 * (od_p >> 33)) + k2); + } + + oreq = (os_p & INT_MASK) - ONE; + + ip64 = os_p - oreq; + delta = ((int64_t)(origin) << 33) - oreq; + ip64 -= delta; + /* limit to valid range before the left shift */ + delta = (ip64 & (1LL << 63)) ? 4 : -4; + delta <<= 33; + while (abs((int)(ip64 >> 33)) > 4) + ip64 += delta; + *phase_init = (int)(ip64 >> 4); + *phase_step = (uint32_t)(k1 >> 4); + return 0; +} + +static void load_scale_table(const struct mdp_info *mdp, + struct mdp_table_entry *table, int len) +{ + int i; + for (i = 0; i < len; i++) + mdp_writel(mdp, table[i].val, table[i].reg); +} + +enum { +IMG_LEFT, +IMG_RIGHT, +IMG_TOP, +IMG_BOTTOM, +}; + +static void get_edge_info(uint32_t src, uint32_t src_coord, uint32_t dst, + uint32_t *interp1, uint32_t *interp2, + uint32_t *repeat1, uint32_t *repeat2) { + if (src > 3 * dst) { + *interp1 = 0; + *interp2 = src - 1; + *repeat1 = 0; + *repeat2 = 0; + } else if (src == 3 * dst) { + *interp1 = 0; + *interp2 = src; + *repeat1 = 0; + *repeat2 = 1; + } else if (src > dst && src < 3 * dst) { + *interp1 = -1; + *interp2 = src; + *repeat1 = 1; + *repeat2 = 1; + } else if (src == dst) { + *interp1 = -1; + *interp2 = src + 1; + *repeat1 = 1; + *repeat2 = 2; + } else { + *interp1 = -2; + *interp2 = src + 1; + *repeat1 = 2; + *repeat2 = 2; + } + *interp1 += src_coord; + *interp2 += src_coord; +} + +static int get_edge_cond(struct mdp_blit_req *req, struct mdp_regs *regs) +{ + int32_t luma_interp[4]; + int32_t luma_repeat[4]; + int32_t chroma_interp[4]; + int32_t chroma_bound[4]; + int32_t chroma_repeat[4]; + uint32_t dst_w, dst_h; + + memset(&luma_interp, 0, sizeof(int32_t) * 4); + memset(&luma_repeat, 0, sizeof(int32_t) * 4); + memset(&chroma_interp, 0, sizeof(int32_t) * 4); + memset(&chroma_bound, 0, sizeof(int32_t) * 4); + memset(&chroma_repeat, 0, sizeof(int32_t) * 4); + regs->edge = 0; + + if (req->flags & MDP_ROT_90) { + dst_w = req->dst_rect.h; + dst_h = req->dst_rect.w; + } else { + dst_w = req->dst_rect.w; + dst_h = req->dst_rect.h; + } + + if (regs->op & (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON)) { + get_edge_info(req->src_rect.h, req->src_rect.y, dst_h, + &luma_interp[IMG_TOP], &luma_interp[IMG_BOTTOM], + &luma_repeat[IMG_TOP], &luma_repeat[IMG_BOTTOM]); + get_edge_info(req->src_rect.w, req->src_rect.x, dst_w, + &luma_interp[IMG_LEFT], &luma_interp[IMG_RIGHT], + &luma_repeat[IMG_LEFT], &luma_repeat[IMG_RIGHT]); + } else { + luma_interp[IMG_LEFT] = req->src_rect.x; + luma_interp[IMG_RIGHT] = req->src_rect.x + req->src_rect.w - 1; + luma_interp[IMG_TOP] = req->src_rect.y; + luma_interp[IMG_BOTTOM] = req->src_rect.y + req->src_rect.h - 1; + luma_repeat[IMG_LEFT] = 0; + luma_repeat[IMG_TOP] = 0; + luma_repeat[IMG_RIGHT] = 0; + luma_repeat[IMG_BOTTOM] = 0; + } + + chroma_interp[IMG_LEFT] = luma_interp[IMG_LEFT]; + chroma_interp[IMG_RIGHT] = luma_interp[IMG_RIGHT]; + chroma_interp[IMG_TOP] = luma_interp[IMG_TOP]; + chroma_interp[IMG_BOTTOM] = luma_interp[IMG_BOTTOM]; + + chroma_bound[IMG_LEFT] = req->src_rect.x; + chroma_bound[IMG_RIGHT] = req->src_rect.x + req->src_rect.w - 1; + chroma_bound[IMG_TOP] = req->src_rect.y; + chroma_bound[IMG_BOTTOM] = req->src_rect.y + req->src_rect.h - 1; + + if (IS_YCRCB(req->src.format)) { + chroma_interp[IMG_LEFT] = chroma_interp[IMG_LEFT] >> 1; + chroma_interp[IMG_RIGHT] = (chroma_interp[IMG_RIGHT] + 1) >> 1; + + chroma_bound[IMG_LEFT] = chroma_bound[IMG_LEFT] >> 1; + chroma_bound[IMG_RIGHT] = chroma_bound[IMG_RIGHT] >> 1; + } + + if (req->src.format == MDP_Y_CBCR_H2V2 || + req->src.format == MDP_Y_CRCB_H2V2) { + chroma_interp[IMG_TOP] = (chroma_interp[IMG_TOP] - 1) >> 1; + chroma_interp[IMG_BOTTOM] = (chroma_interp[IMG_BOTTOM] + 1) + >> 1; + chroma_bound[IMG_TOP] = (chroma_bound[IMG_TOP] + 1) >> 1; + chroma_bound[IMG_BOTTOM] = chroma_bound[IMG_BOTTOM] >> 1; + } + + chroma_repeat[IMG_LEFT] = chroma_bound[IMG_LEFT] - + chroma_interp[IMG_LEFT]; + chroma_repeat[IMG_RIGHT] = chroma_interp[IMG_RIGHT] - + chroma_bound[IMG_RIGHT]; + chroma_repeat[IMG_TOP] = chroma_bound[IMG_TOP] - + chroma_interp[IMG_TOP]; + chroma_repeat[IMG_BOTTOM] = chroma_interp[IMG_BOTTOM] - + chroma_bound[IMG_BOTTOM]; + + if (chroma_repeat[IMG_LEFT] < 0 || chroma_repeat[IMG_LEFT] > 3 || + chroma_repeat[IMG_RIGHT] < 0 || chroma_repeat[IMG_RIGHT] > 3 || + chroma_repeat[IMG_TOP] < 0 || chroma_repeat[IMG_TOP] > 3 || + chroma_repeat[IMG_BOTTOM] < 0 || chroma_repeat[IMG_BOTTOM] > 3 || + luma_repeat[IMG_LEFT] < 0 || luma_repeat[IMG_LEFT] > 3 || + luma_repeat[IMG_RIGHT] < 0 || luma_repeat[IMG_RIGHT] > 3 || + luma_repeat[IMG_TOP] < 0 || luma_repeat[IMG_TOP] > 3 || + luma_repeat[IMG_BOTTOM] < 0 || luma_repeat[IMG_BOTTOM] > 3) + return -1; + + regs->edge |= (chroma_repeat[IMG_LEFT] & 3) << MDP_LEFT_CHROMA; + regs->edge |= (chroma_repeat[IMG_RIGHT] & 3) << MDP_RIGHT_CHROMA; + regs->edge |= (chroma_repeat[IMG_TOP] & 3) << MDP_TOP_CHROMA; + regs->edge |= (chroma_repeat[IMG_BOTTOM] & 3) << MDP_BOTTOM_CHROMA; + regs->edge |= (luma_repeat[IMG_LEFT] & 3) << MDP_LEFT_LUMA; + regs->edge |= (luma_repeat[IMG_RIGHT] & 3) << MDP_RIGHT_LUMA; + regs->edge |= (luma_repeat[IMG_TOP] & 3) << MDP_TOP_LUMA; + regs->edge |= (luma_repeat[IMG_BOTTOM] & 3) << MDP_BOTTOM_LUMA; + return 0; +} + +static int blit_scale(const struct mdp_info *mdp, struct mdp_blit_req *req, + struct mdp_regs *regs) +{ + uint32_t phase_init_x, phase_init_y, phase_step_x, phase_step_y; + uint32_t scale_factor_x, scale_factor_y; + uint32_t downscale; + uint32_t dst_w, dst_h; + + if (req->flags & MDP_ROT_90) { + dst_w = req->dst_rect.h; + dst_h = req->dst_rect.w; + } else { + dst_w = req->dst_rect.w; + dst_h = req->dst_rect.h; + } + if ((req->src_rect.w == dst_w) && (req->src_rect.h == dst_h) && + !(req->flags & MDP_BLUR)) { + regs->phasex_init = 0; + regs->phasey_init = 0; + regs->phasex_step = 0; + regs->phasey_step = 0; + return 0; + } + + if (scale_params(req->src_rect.w, dst_w, 1, &phase_init_x, + &phase_step_x) || + scale_params(req->src_rect.h, dst_h, 1, &phase_init_y, + &phase_step_y)) + return -1; + + scale_factor_x = (dst_w * 10) / req->src_rect.w; + scale_factor_y = (dst_h * 10) / req->src_rect.h; + + if (scale_factor_x > 8) + downscale = MDP_DOWNSCALE_PT8TO1; + else if (scale_factor_x > 6) + downscale = MDP_DOWNSCALE_PT6TOPT8; + else if (scale_factor_x > 4) + downscale = MDP_DOWNSCALE_PT4TOPT6; + else + downscale = MDP_DOWNSCALE_PT2TOPT4; + if (downscale != downscale_x_table) { + load_scale_table(mdp, mdp_downscale_x_table[downscale], 64); + downscale_x_table = downscale; + } + + if (scale_factor_y > 8) + downscale = MDP_DOWNSCALE_PT8TO1; + else if (scale_factor_y > 6) + downscale = MDP_DOWNSCALE_PT6TOPT8; + else if (scale_factor_y > 4) + downscale = MDP_DOWNSCALE_PT4TOPT6; + else + downscale = MDP_DOWNSCALE_PT2TOPT4; + if (downscale != downscale_y_table) { + load_scale_table(mdp, mdp_downscale_y_table[downscale], 64); + downscale_y_table = downscale; + } + + regs->phasex_init = phase_init_x; + regs->phasey_init = phase_init_y; + regs->phasex_step = phase_step_x; + regs->phasey_step = phase_step_y; + regs->op |= (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON); + return 0; + +} + +static void blit_blur(const struct mdp_info *mdp, struct mdp_blit_req *req, + struct mdp_regs *regs) +{ + if (!(req->flags & MDP_BLUR)) + return; + + if (!(downscale_x_table == MDP_DOWNSCALE_BLUR && + downscale_y_table == MDP_DOWNSCALE_BLUR)) { + load_scale_table(mdp, mdp_gaussian_blur_table, 128); + downscale_x_table = MDP_DOWNSCALE_BLUR; + downscale_y_table = MDP_DOWNSCALE_BLUR; + } + + regs->op |= (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON); +} + + +#define IMG_LEN(rect_h, w, rect_w, bpp) (((rect_h) * w) * bpp) + +#define Y_TO_CRCB_RATIO(format) \ + ((format == MDP_Y_CBCR_H2V2 || format == MDP_Y_CRCB_H2V2) ? 2 :\ + (format == MDP_Y_CBCR_H2V1 || format == MDP_Y_CRCB_H2V1) ? 1 : 1) + +static void get_len(struct mdp_img *img, struct mdp_rect *rect, uint32_t bpp, + uint32_t *len0, uint32_t *len1) +{ + *len0 = IMG_LEN(rect->h, img->width, rect->w, bpp); + if (IS_PSEUDOPLNR(img->format)) + *len1 = *len0/Y_TO_CRCB_RATIO(img->format); + else + *len1 = 0; +} + +static int valid_src_dst(unsigned long src_start, unsigned long src_len, + unsigned long dst_start, unsigned long dst_len, + struct mdp_blit_req *req, struct mdp_regs *regs) +{ + unsigned long src_min_ok = src_start; + unsigned long src_max_ok = src_start + src_len; + unsigned long dst_min_ok = dst_start; + unsigned long dst_max_ok = dst_start + dst_len; + uint32_t src0_len, src1_len, dst0_len, dst1_len; + get_len(&req->src, &req->src_rect, regs->src_bpp, &src0_len, + &src1_len); + get_len(&req->dst, &req->dst_rect, regs->dst_bpp, &dst0_len, + &dst1_len); + + if (regs->src0 < src_min_ok || regs->src0 > src_max_ok || + regs->src0 + src0_len > src_max_ok) { + DLOG("invalid_src %x %x %lx %lx\n", regs->src0, + src0_len, src_min_ok, src_max_ok); + return 0; + } + if (regs->src_cfg & PPP_SRC_PLANE_PSEUDOPLNR) { + if (regs->src1 < src_min_ok || regs->src1 > src_max_ok || + regs->src1 + src1_len > src_max_ok) { + DLOG("invalid_src1"); + return 0; + } + } + if (regs->dst0 < dst_min_ok || regs->dst0 > dst_max_ok || + regs->dst0 + dst0_len > dst_max_ok) { + DLOG("invalid_dst"); + return 0; + } + if (regs->dst_cfg & PPP_SRC_PLANE_PSEUDOPLNR) { + if (regs->dst1 < dst_min_ok || regs->dst1 > dst_max_ok || + regs->dst1 + dst1_len > dst_max_ok) { + DLOG("invalid_dst1"); + return 0; + } + } + return 1; +} + + +static void flush_imgs(struct mdp_blit_req *req, struct mdp_regs *regs, + struct file *src_file, struct file *dst_file) +{ +#ifdef CONFIG_ANDROID_PMEM + uint32_t src0_len, src1_len, dst0_len, dst1_len; + + /* flush src images to memory before dma to mdp */ + get_len(&req->src, &req->src_rect, regs->src_bpp, &src0_len, + &src1_len); + flush_pmem_file(src_file, req->src.offset, src0_len); + if (IS_PSEUDOPLNR(req->src.format)) + flush_pmem_file(src_file, req->src.offset + src0_len, + src1_len); + + /* flush dst images */ + get_len(&req->dst, &req->dst_rect, regs->dst_bpp, &dst0_len, + &dst1_len); + flush_pmem_file(dst_file, req->dst.offset, dst0_len); + if (IS_PSEUDOPLNR(req->dst.format)) + flush_pmem_file(dst_file, req->dst.offset + dst0_len, + dst1_len); +#endif +} + +static void get_chroma_addr(struct mdp_img *img, struct mdp_rect *rect, + uint32_t base, uint32_t bpp, uint32_t cfg, + uint32_t *addr, uint32_t *ystride) +{ + uint32_t compress_v = Y_TO_CRCB_RATIO(img->format); + uint32_t compress_h = 2; + uint32_t offset; + + if (IS_PSEUDOPLNR(img->format)) { + offset = (rect->x / compress_h) * compress_h; + offset += rect->y == 0 ? 0 : + ((rect->y + 1) / compress_v) * img->width; + *addr = base + (img->width * img->height * bpp); + *addr += offset * bpp; + *ystride |= *ystride << 16; + } else { + *addr = 0; + } +} + +static int send_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, + struct mdp_regs *regs, struct file *src_file, + struct file *dst_file) +{ + mdp_writel(mdp, 1, 0x060); + mdp_writel(mdp, regs->src_rect, PPP_ADDR_SRC_ROI); + mdp_writel(mdp, regs->src0, PPP_ADDR_SRC0); + mdp_writel(mdp, regs->src1, PPP_ADDR_SRC1); + mdp_writel(mdp, regs->src_ystride, PPP_ADDR_SRC_YSTRIDE); + mdp_writel(mdp, regs->src_cfg, PPP_ADDR_SRC_CFG); + mdp_writel(mdp, regs->src_pack, PPP_ADDR_SRC_PACK_PATTERN); + + mdp_writel(mdp, regs->op, PPP_ADDR_OPERATION); + mdp_writel(mdp, regs->phasex_init, PPP_ADDR_PHASEX_INIT); + mdp_writel(mdp, regs->phasey_init, PPP_ADDR_PHASEY_INIT); + mdp_writel(mdp, regs->phasex_step, PPP_ADDR_PHASEX_STEP); + mdp_writel(mdp, regs->phasey_step, PPP_ADDR_PHASEY_STEP); + + mdp_writel(mdp, (req->alpha << 24) | (req->transp_mask & 0xffffff), + PPP_ADDR_ALPHA_TRANSP); + + mdp_writel(mdp, regs->dst_cfg, PPP_ADDR_DST_CFG); + mdp_writel(mdp, regs->dst_pack, PPP_ADDR_DST_PACK_PATTERN); + mdp_writel(mdp, regs->dst_rect, PPP_ADDR_DST_ROI); + mdp_writel(mdp, regs->dst0, PPP_ADDR_DST0); + mdp_writel(mdp, regs->dst1, PPP_ADDR_DST1); + mdp_writel(mdp, regs->dst_ystride, PPP_ADDR_DST_YSTRIDE); + + mdp_writel(mdp, regs->edge, PPP_ADDR_EDGE); + if (regs->op & PPP_OP_BLEND_ON) { + mdp_writel(mdp, regs->dst0, PPP_ADDR_BG0); + mdp_writel(mdp, regs->dst1, PPP_ADDR_BG1); + mdp_writel(mdp, regs->dst_ystride, PPP_ADDR_BG_YSTRIDE); + mdp_writel(mdp, src_img_cfg[req->dst.format], PPP_ADDR_BG_CFG); + mdp_writel(mdp, pack_pattern[req->dst.format], + PPP_ADDR_BG_PACK_PATTERN); + } + flush_imgs(req, regs, src_file, dst_file); + mdp_writel(mdp, 0x1000, MDP_DISPLAY0_START); + return 0; +} + +int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, + struct file *src_file, unsigned long src_start, unsigned long src_len, + struct file *dst_file, unsigned long dst_start, unsigned long dst_len) +{ + struct mdp_regs regs = {0}; + + if (unlikely(req->src.format >= MDP_IMGTYPE_LIMIT || + req->dst.format >= MDP_IMGTYPE_LIMIT)) { + printk(KERN_ERR "mpd_ppp: img is of wrong format\n"); + return -EINVAL; + } + + if (unlikely(req->src_rect.x > req->src.width || + req->src_rect.y > req->src.height || + req->dst_rect.x > req->dst.width || + req->dst_rect.y > req->dst.height)) { + printk(KERN_ERR "mpd_ppp: img rect is outside of img!\n"); + return -EINVAL; + } + + /* set the src image configuration */ + regs.src_cfg = src_img_cfg[req->src.format]; + regs.src_cfg |= (req->src_rect.x & 0x1) ? PPP_SRC_BPP_ROI_ODD_X : 0; + regs.src_cfg |= (req->src_rect.y & 0x1) ? PPP_SRC_BPP_ROI_ODD_Y : 0; + regs.src_rect = (req->src_rect.h << 16) | req->src_rect.w; + regs.src_pack = pack_pattern[req->src.format]; + + /* set the dest image configuration */ + regs.dst_cfg = dst_img_cfg[req->dst.format] | PPP_DST_OUT_SEL_AXI; + regs.dst_rect = (req->dst_rect.h << 16) | req->dst_rect.w; + regs.dst_pack = pack_pattern[req->dst.format]; + + /* set src, bpp, start pixel and ystride */ + regs.src_bpp = bytes_per_pixel[req->src.format]; + regs.src0 = src_start + req->src.offset; + regs.src_ystride = req->src.width * regs.src_bpp; + get_chroma_addr(&req->src, &req->src_rect, regs.src0, regs.src_bpp, + regs.src_cfg, ®s.src1, ®s.src_ystride); + regs.src0 += (req->src_rect.x + (req->src_rect.y * req->src.width)) * + regs.src_bpp; + + /* set dst, bpp, start pixel and ystride */ + regs.dst_bpp = bytes_per_pixel[req->dst.format]; + regs.dst0 = dst_start + req->dst.offset; + regs.dst_ystride = req->dst.width * regs.dst_bpp; + get_chroma_addr(&req->dst, &req->dst_rect, regs.dst0, regs.dst_bpp, + regs.dst_cfg, ®s.dst1, ®s.dst_ystride); + regs.dst0 += (req->dst_rect.x + (req->dst_rect.y * req->dst.width)) * + regs.dst_bpp; + + if (!valid_src_dst(src_start, src_len, dst_start, dst_len, req, + ®s)) { + printk(KERN_ERR "mpd_ppp: final src or dst location is " + "invalid, are you trying to make an image too large " + "or to place it outside the screen?\n"); + return -EINVAL; + } + + /* set up operation register */ + regs.op = 0; + blit_rotate(req, ®s); + blit_convert(req, ®s); + if (req->flags & MDP_DITHER) + regs.op |= PPP_OP_DITHER_EN; + blit_blend(req, ®s); + if (blit_scale(mdp, req, ®s)) { + printk(KERN_ERR "mpd_ppp: error computing scale for img.\n"); + return -EINVAL; + } + blit_blur(mdp, req, ®s); + regs.op |= dst_op_chroma[req->dst.format] | + src_op_chroma[req->src.format]; + + /* if the image is YCRYCB, the x and w must be even */ + if (unlikely(req->src.format == MDP_YCRYCB_H2V1)) { + req->src_rect.x = req->src_rect.x & (~0x1); + req->src_rect.w = req->src_rect.w & (~0x1); + req->dst_rect.x = req->dst_rect.x & (~0x1); + req->dst_rect.w = req->dst_rect.w & (~0x1); + } + if (get_edge_cond(req, ®s)) + return -EINVAL; + + send_blit(mdp, req, ®s, src_file, dst_file); + return 0; +} diff --git a/drivers/video/msm/mdp_scale_tables.c b/drivers/video/msm/mdp_scale_tables.c new file mode 100644 index 00000000000..604783b2e17 --- /dev/null +++ b/drivers/video/msm/mdp_scale_tables.c @@ -0,0 +1,766 @@ +/* drivers/video/msm_fb/mdp_scale_tables.c + * + * Copyright (C) 2007 QUALCOMM Incorporated + * Copyright (C) 2007 Google Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#include "mdp_scale_tables.h" +#include "mdp_hw.h" + +struct mdp_table_entry mdp_upscale_table[] = { + { 0x5fffc, 0x0 }, + { 0x50200, 0x7fc00000 }, + { 0x5fffc, 0xff80000d }, + { 0x50204, 0x7ec003f9 }, + { 0x5fffc, 0xfec0001c }, + { 0x50208, 0x7d4003f3 }, + { 0x5fffc, 0xfe40002b }, + { 0x5020c, 0x7b8003ed }, + { 0x5fffc, 0xfd80003c }, + { 0x50210, 0x794003e8 }, + { 0x5fffc, 0xfcc0004d }, + { 0x50214, 0x76c003e4 }, + { 0x5fffc, 0xfc40005f }, + { 0x50218, 0x73c003e0 }, + { 0x5fffc, 0xfb800071 }, + { 0x5021c, 0x708003de }, + { 0x5fffc, 0xfac00085 }, + { 0x50220, 0x6d0003db }, + { 0x5fffc, 0xfa000098 }, + { 0x50224, 0x698003d9 }, + { 0x5fffc, 0xf98000ac }, + { 0x50228, 0x654003d8 }, + { 0x5fffc, 0xf8c000c1 }, + { 0x5022c, 0x610003d7 }, + { 0x5fffc, 0xf84000d5 }, + { 0x50230, 0x5c8003d7 }, + { 0x5fffc, 0xf7c000e9 }, + { 0x50234, 0x580003d7 }, + { 0x5fffc, 0xf74000fd }, + { 0x50238, 0x534003d8 }, + { 0x5fffc, 0xf6c00112 }, + { 0x5023c, 0x4e8003d8 }, + { 0x5fffc, 0xf6800126 }, + { 0x50240, 0x494003da }, + { 0x5fffc, 0xf600013a }, + { 0x50244, 0x448003db }, + { 0x5fffc, 0xf600014d }, + { 0x50248, 0x3f4003dd }, + { 0x5fffc, 0xf5c00160 }, + { 0x5024c, 0x3a4003df }, + { 0x5fffc, 0xf5c00172 }, + { 0x50250, 0x354003e1 }, + { 0x5fffc, 0xf5c00184 }, + { 0x50254, 0x304003e3 }, + { 0x5fffc, 0xf6000195 }, + { 0x50258, 0x2b0003e6 }, + { 0x5fffc, 0xf64001a6 }, + { 0x5025c, 0x260003e8 }, + { 0x5fffc, 0xf6c001b4 }, + { 0x50260, 0x214003eb }, + { 0x5fffc, 0xf78001c2 }, + { 0x50264, 0x1c4003ee }, + { 0x5fffc, 0xf80001cf }, + { 0x50268, 0x17c003f1 }, + { 0x5fffc, 0xf90001db }, + { 0x5026c, 0x134003f3 }, + { 0x5fffc, 0xfa0001e5 }, + { 0x50270, 0xf0003f6 }, + { 0x5fffc, 0xfb4001ee }, + { 0x50274, 0xac003f9 }, + { 0x5fffc, 0xfcc001f5 }, + { 0x50278, 0x70003fb }, + { 0x5fffc, 0xfe4001fb }, + { 0x5027c, 0x34003fe }, +}; + +static struct mdp_table_entry mdp_downscale_x_table_PT2TOPT4[] = { + { 0x5fffc, 0x740008c }, + { 0x50280, 0x33800088 }, + { 0x5fffc, 0x800008e }, + { 0x50284, 0x33400084 }, + { 0x5fffc, 0x8400092 }, + { 0x50288, 0x33000080 }, + { 0x5fffc, 0x9000094 }, + { 0x5028c, 0x3300007b }, + { 0x5fffc, 0x9c00098 }, + { 0x50290, 0x32400077 }, + { 0x5fffc, 0xa40009b }, + { 0x50294, 0x32000073 }, + { 0x5fffc, 0xb00009d }, + { 0x50298, 0x31c0006f }, + { 0x5fffc, 0xbc000a0 }, + { 0x5029c, 0x3140006b }, + { 0x5fffc, 0xc8000a2 }, + { 0x502a0, 0x31000067 }, + { 0x5fffc, 0xd8000a5 }, + { 0x502a4, 0x30800062 }, + { 0x5fffc, 0xe4000a8 }, + { 0x502a8, 0x2fc0005f }, + { 0x5fffc, 0xec000aa }, + { 0x502ac, 0x2fc0005b }, + { 0x5fffc, 0xf8000ad }, + { 0x502b0, 0x2f400057 }, + { 0x5fffc, 0x108000b0 }, + { 0x502b4, 0x2e400054 }, + { 0x5fffc, 0x114000b2 }, + { 0x502b8, 0x2e000050 }, + { 0x5fffc, 0x124000b4 }, + { 0x502bc, 0x2d80004c }, + { 0x5fffc, 0x130000b6 }, + { 0x502c0, 0x2d000049 }, + { 0x5fffc, 0x140000b8 }, + { 0x502c4, 0x2c800045 }, + { 0x5fffc, 0x150000b9 }, + { 0x502c8, 0x2c000042 }, + { 0x5fffc, 0x15c000bd }, + { 0x502cc, 0x2b40003e }, + { 0x5fffc, 0x16c000bf }, + { 0x502d0, 0x2a80003b }, + { 0x5fffc, 0x17c000bf }, + { 0x502d4, 0x2a000039 }, + { 0x5fffc, 0x188000c2 }, + { 0x502d8, 0x29400036 }, + { 0x5fffc, 0x19c000c4 }, + { 0x502dc, 0x28800032 }, + { 0x5fffc, 0x1ac000c5 }, + { 0x502e0, 0x2800002f }, + { 0x5fffc, 0x1bc000c7 }, + { 0x502e4, 0x2740002c }, + { 0x5fffc, 0x1cc000c8 }, + { 0x502e8, 0x26c00029 }, + { 0x5fffc, 0x1dc000c9 }, + { 0x502ec, 0x26000027 }, + { 0x5fffc, 0x1ec000cc }, + { 0x502f0, 0x25000024 }, + { 0x5fffc, 0x200000cc }, + { 0x502f4, 0x24800021 }, + { 0x5fffc, 0x210000cd }, + { 0x502f8, 0x23800020 }, + { 0x5fffc, 0x220000ce }, + { 0x502fc, 0x2300001d }, +}; + +static struct mdp_table_entry mdp_downscale_x_table_PT4TOPT6[] = { + { 0x5fffc, 0x740008c }, + { 0x50280, 0x33800088 }, + { 0x5fffc, 0x800008e }, + { 0x50284, 0x33400084 }, + { 0x5fffc, 0x8400092 }, + { 0x50288, 0x33000080 }, + { 0x5fffc, 0x9000094 }, + { 0x5028c, 0x3300007b }, + { 0x5fffc, 0x9c00098 }, + { 0x50290, 0x32400077 }, + { 0x5fffc, 0xa40009b }, + { 0x50294, 0x32000073 }, + { 0x5fffc, 0xb00009d }, + { 0x50298, 0x31c0006f }, + { 0x5fffc, 0xbc000a0 }, + { 0x5029c, 0x3140006b }, + { 0x5fffc, 0xc8000a2 }, + { 0x502a0, 0x31000067 }, + { 0x5fffc, 0xd8000a5 }, + { 0x502a4, 0x30800062 }, + { 0x5fffc, 0xe4000a8 }, + { 0x502a8, 0x2fc0005f }, + { 0x5fffc, 0xec000aa }, + { 0x502ac, 0x2fc0005b }, + { 0x5fffc, 0xf8000ad }, + { 0x502b0, 0x2f400057 }, + { 0x5fffc, 0x108000b0 }, + { 0x502b4, 0x2e400054 }, + { 0x5fffc, 0x114000b2 }, + { 0x502b8, 0x2e000050 }, + { 0x5fffc, 0x124000b4 }, + { 0x502bc, 0x2d80004c }, + { 0x5fffc, 0x130000b6 }, + { 0x502c0, 0x2d000049 }, + { 0x5fffc, 0x140000b8 }, + { 0x502c4, 0x2c800045 }, + { 0x5fffc, 0x150000b9 }, + { 0x502c8, 0x2c000042 }, + { 0x5fffc, 0x15c000bd }, + { 0x502cc, 0x2b40003e }, + { 0x5fffc, 0x16c000bf }, + { 0x502d0, 0x2a80003b }, + { 0x5fffc, 0x17c000bf }, + { 0x502d4, 0x2a000039 }, + { 0x5fffc, 0x188000c2 }, + { 0x502d8, 0x29400036 }, + { 0x5fffc, 0x19c000c4 }, + { 0x502dc, 0x28800032 }, + { 0x5fffc, 0x1ac000c5 }, + { 0x502e0, 0x2800002f }, + { 0x5fffc, 0x1bc000c7 }, + { 0x502e4, 0x2740002c }, + { 0x5fffc, 0x1cc000c8 }, + { 0x502e8, 0x26c00029 }, + { 0x5fffc, 0x1dc000c9 }, + { 0x502ec, 0x26000027 }, + { 0x5fffc, 0x1ec000cc }, + { 0x502f0, 0x25000024 }, + { 0x5fffc, 0x200000cc }, + { 0x502f4, 0x24800021 }, + { 0x5fffc, 0x210000cd }, + { 0x502f8, 0x23800020 }, + { 0x5fffc, 0x220000ce }, + { 0x502fc, 0x2300001d }, +}; + +static struct mdp_table_entry mdp_downscale_x_table_PT6TOPT8[] = { + { 0x5fffc, 0xfe000070 }, + { 0x50280, 0x4bc00068 }, + { 0x5fffc, 0xfe000078 }, + { 0x50284, 0x4bc00060 }, + { 0x5fffc, 0xfe000080 }, + { 0x50288, 0x4b800059 }, + { 0x5fffc, 0xfe000089 }, + { 0x5028c, 0x4b000052 }, + { 0x5fffc, 0xfe400091 }, + { 0x50290, 0x4a80004b }, + { 0x5fffc, 0xfe40009a }, + { 0x50294, 0x4a000044 }, + { 0x5fffc, 0xfe8000a3 }, + { 0x50298, 0x4940003d }, + { 0x5fffc, 0xfec000ac }, + { 0x5029c, 0x48400037 }, + { 0x5fffc, 0xff0000b4 }, + { 0x502a0, 0x47800031 }, + { 0x5fffc, 0xff8000bd }, + { 0x502a4, 0x4640002b }, + { 0x5fffc, 0xc5 }, + { 0x502a8, 0x45000026 }, + { 0x5fffc, 0x8000ce }, + { 0x502ac, 0x43800021 }, + { 0x5fffc, 0x10000d6 }, + { 0x502b0, 0x4240001c }, + { 0x5fffc, 0x18000df }, + { 0x502b4, 0x40800018 }, + { 0x5fffc, 0x24000e6 }, + { 0x502b8, 0x3f000014 }, + { 0x5fffc, 0x30000ee }, + { 0x502bc, 0x3d400010 }, + { 0x5fffc, 0x40000f5 }, + { 0x502c0, 0x3b80000c }, + { 0x5fffc, 0x50000fc }, + { 0x502c4, 0x39800009 }, + { 0x5fffc, 0x6000102 }, + { 0x502c8, 0x37c00006 }, + { 0x5fffc, 0x7000109 }, + { 0x502cc, 0x35800004 }, + { 0x5fffc, 0x840010e }, + { 0x502d0, 0x33800002 }, + { 0x5fffc, 0x9800114 }, + { 0x502d4, 0x31400000 }, + { 0x5fffc, 0xac00119 }, + { 0x502d8, 0x2f4003fe }, + { 0x5fffc, 0xc40011e }, + { 0x502dc, 0x2d0003fc }, + { 0x5fffc, 0xdc00121 }, + { 0x502e0, 0x2b0003fb }, + { 0x5fffc, 0xf400125 }, + { 0x502e4, 0x28c003fa }, + { 0x5fffc, 0x11000128 }, + { 0x502e8, 0x268003f9 }, + { 0x5fffc, 0x12c0012a }, + { 0x502ec, 0x244003f9 }, + { 0x5fffc, 0x1480012c }, + { 0x502f0, 0x224003f8 }, + { 0x5fffc, 0x1640012e }, + { 0x502f4, 0x200003f8 }, + { 0x5fffc, 0x1800012f }, + { 0x502f8, 0x1e0003f8 }, + { 0x5fffc, 0x1a00012f }, + { 0x502fc, 0x1c0003f8 }, +}; + +static struct mdp_table_entry mdp_downscale_x_table_PT8TO1[] = { + { 0x5fffc, 0x0 }, + { 0x50280, 0x7fc00000 }, + { 0x5fffc, 0xff80000d }, + { 0x50284, 0x7ec003f9 }, + { 0x5fffc, 0xfec0001c }, + { 0x50288, 0x7d4003f3 }, + { 0x5fffc, 0xfe40002b }, + { 0x5028c, 0x7b8003ed }, + { 0x5fffc, 0xfd80003c }, + { 0x50290, 0x794003e8 }, + { 0x5fffc, 0xfcc0004d }, + { 0x50294, 0x76c003e4 }, + { 0x5fffc, 0xfc40005f }, + { 0x50298, 0x73c003e0 }, + { 0x5fffc, 0xfb800071 }, + { 0x5029c, 0x708003de }, + { 0x5fffc, 0xfac00085 }, + { 0x502a0, 0x6d0003db }, + { 0x5fffc, 0xfa000098 }, + { 0x502a4, 0x698003d9 }, + { 0x5fffc, 0xf98000ac }, + { 0x502a8, 0x654003d8 }, + { 0x5fffc, 0xf8c000c1 }, + { 0x502ac, 0x610003d7 }, + { 0x5fffc, 0xf84000d5 }, + { 0x502b0, 0x5c8003d7 }, + { 0x5fffc, 0xf7c000e9 }, + { 0x502b4, 0x580003d7 }, + { 0x5fffc, 0xf74000fd }, + { 0x502b8, 0x534003d8 }, + { 0x5fffc, 0xf6c00112 }, + { 0x502bc, 0x4e8003d8 }, + { 0x5fffc, 0xf6800126 }, + { 0x502c0, 0x494003da }, + { 0x5fffc, 0xf600013a }, + { 0x502c4, 0x448003db }, + { 0x5fffc, 0xf600014d }, + { 0x502c8, 0x3f4003dd }, + { 0x5fffc, 0xf5c00160 }, + { 0x502cc, 0x3a4003df }, + { 0x5fffc, 0xf5c00172 }, + { 0x502d0, 0x354003e1 }, + { 0x5fffc, 0xf5c00184 }, + { 0x502d4, 0x304003e3 }, + { 0x5fffc, 0xf6000195 }, + { 0x502d8, 0x2b0003e6 }, + { 0x5fffc, 0xf64001a6 }, + { 0x502dc, 0x260003e8 }, + { 0x5fffc, 0xf6c001b4 }, + { 0x502e0, 0x214003eb }, + { 0x5fffc, 0xf78001c2 }, + { 0x502e4, 0x1c4003ee }, + { 0x5fffc, 0xf80001cf }, + { 0x502e8, 0x17c003f1 }, + { 0x5fffc, 0xf90001db }, + { 0x502ec, 0x134003f3 }, + { 0x5fffc, 0xfa0001e5 }, + { 0x502f0, 0xf0003f6 }, + { 0x5fffc, 0xfb4001ee }, + { 0x502f4, 0xac003f9 }, + { 0x5fffc, 0xfcc001f5 }, + { 0x502f8, 0x70003fb }, + { 0x5fffc, 0xfe4001fb }, + { 0x502fc, 0x34003fe }, +}; + +struct mdp_table_entry *mdp_downscale_x_table[MDP_DOWNSCALE_MAX] = { + [MDP_DOWNSCALE_PT2TOPT4] = mdp_downscale_x_table_PT2TOPT4, + [MDP_DOWNSCALE_PT4TOPT6] = mdp_downscale_x_table_PT4TOPT6, + [MDP_DOWNSCALE_PT6TOPT8] = mdp_downscale_x_table_PT6TOPT8, + [MDP_DOWNSCALE_PT8TO1] = mdp_downscale_x_table_PT8TO1, +}; + +static struct mdp_table_entry mdp_downscale_y_table_PT2TOPT4[] = { + { 0x5fffc, 0x740008c }, + { 0x50300, 0x33800088 }, + { 0x5fffc, 0x800008e }, + { 0x50304, 0x33400084 }, + { 0x5fffc, 0x8400092 }, + { 0x50308, 0x33000080 }, + { 0x5fffc, 0x9000094 }, + { 0x5030c, 0x3300007b }, + { 0x5fffc, 0x9c00098 }, + { 0x50310, 0x32400077 }, + { 0x5fffc, 0xa40009b }, + { 0x50314, 0x32000073 }, + { 0x5fffc, 0xb00009d }, + { 0x50318, 0x31c0006f }, + { 0x5fffc, 0xbc000a0 }, + { 0x5031c, 0x3140006b }, + { 0x5fffc, 0xc8000a2 }, + { 0x50320, 0x31000067 }, + { 0x5fffc, 0xd8000a5 }, + { 0x50324, 0x30800062 }, + { 0x5fffc, 0xe4000a8 }, + { 0x50328, 0x2fc0005f }, + { 0x5fffc, 0xec000aa }, + { 0x5032c, 0x2fc0005b }, + { 0x5fffc, 0xf8000ad }, + { 0x50330, 0x2f400057 }, + { 0x5fffc, 0x108000b0 }, + { 0x50334, 0x2e400054 }, + { 0x5fffc, 0x114000b2 }, + { 0x50338, 0x2e000050 }, + { 0x5fffc, 0x124000b4 }, + { 0x5033c, 0x2d80004c }, + { 0x5fffc, 0x130000b6 }, + { 0x50340, 0x2d000049 }, + { 0x5fffc, 0x140000b8 }, + { 0x50344, 0x2c800045 }, + { 0x5fffc, 0x150000b9 }, + { 0x50348, 0x2c000042 }, + { 0x5fffc, 0x15c000bd }, + { 0x5034c, 0x2b40003e }, + { 0x5fffc, 0x16c000bf }, + { 0x50350, 0x2a80003b }, + { 0x5fffc, 0x17c000bf }, + { 0x50354, 0x2a000039 }, + { 0x5fffc, 0x188000c2 }, + { 0x50358, 0x29400036 }, + { 0x5fffc, 0x19c000c4 }, + { 0x5035c, 0x28800032 }, + { 0x5fffc, 0x1ac000c5 }, + { 0x50360, 0x2800002f }, + { 0x5fffc, 0x1bc000c7 }, + { 0x50364, 0x2740002c }, + { 0x5fffc, 0x1cc000c8 }, + { 0x50368, 0x26c00029 }, + { 0x5fffc, 0x1dc000c9 }, + { 0x5036c, 0x26000027 }, + { 0x5fffc, 0x1ec000cc }, + { 0x50370, 0x25000024 }, + { 0x5fffc, 0x200000cc }, + { 0x50374, 0x24800021 }, + { 0x5fffc, 0x210000cd }, + { 0x50378, 0x23800020 }, + { 0x5fffc, 0x220000ce }, + { 0x5037c, 0x2300001d }, +}; + +static struct mdp_table_entry mdp_downscale_y_table_PT4TOPT6[] = { + { 0x5fffc, 0x740008c }, + { 0x50300, 0x33800088 }, + { 0x5fffc, 0x800008e }, + { 0x50304, 0x33400084 }, + { 0x5fffc, 0x8400092 }, + { 0x50308, 0x33000080 }, + { 0x5fffc, 0x9000094 }, + { 0x5030c, 0x3300007b }, + { 0x5fffc, 0x9c00098 }, + { 0x50310, 0x32400077 }, + { 0x5fffc, 0xa40009b }, + { 0x50314, 0x32000073 }, + { 0x5fffc, 0xb00009d }, + { 0x50318, 0x31c0006f }, + { 0x5fffc, 0xbc000a0 }, + { 0x5031c, 0x3140006b }, + { 0x5fffc, 0xc8000a2 }, + { 0x50320, 0x31000067 }, + { 0x5fffc, 0xd8000a5 }, + { 0x50324, 0x30800062 }, + { 0x5fffc, 0xe4000a8 }, + { 0x50328, 0x2fc0005f }, + { 0x5fffc, 0xec000aa }, + { 0x5032c, 0x2fc0005b }, + { 0x5fffc, 0xf8000ad }, + { 0x50330, 0x2f400057 }, + { 0x5fffc, 0x108000b0 }, + { 0x50334, 0x2e400054 }, + { 0x5fffc, 0x114000b2 }, + { 0x50338, 0x2e000050 }, + { 0x5fffc, 0x124000b4 }, + { 0x5033c, 0x2d80004c }, + { 0x5fffc, 0x130000b6 }, + { 0x50340, 0x2d000049 }, + { 0x5fffc, 0x140000b8 }, + { 0x50344, 0x2c800045 }, + { 0x5fffc, 0x150000b9 }, + { 0x50348, 0x2c000042 }, + { 0x5fffc, 0x15c000bd }, + { 0x5034c, 0x2b40003e }, + { 0x5fffc, 0x16c000bf }, + { 0x50350, 0x2a80003b }, + { 0x5fffc, 0x17c000bf }, + { 0x50354, 0x2a000039 }, + { 0x5fffc, 0x188000c2 }, + { 0x50358, 0x29400036 }, + { 0x5fffc, 0x19c000c4 }, + { 0x5035c, 0x28800032 }, + { 0x5fffc, 0x1ac000c5 }, + { 0x50360, 0x2800002f }, + { 0x5fffc, 0x1bc000c7 }, + { 0x50364, 0x2740002c }, + { 0x5fffc, 0x1cc000c8 }, + { 0x50368, 0x26c00029 }, + { 0x5fffc, 0x1dc000c9 }, + { 0x5036c, 0x26000027 }, + { 0x5fffc, 0x1ec000cc }, + { 0x50370, 0x25000024 }, + { 0x5fffc, 0x200000cc }, + { 0x50374, 0x24800021 }, + { 0x5fffc, 0x210000cd }, + { 0x50378, 0x23800020 }, + { 0x5fffc, 0x220000ce }, + { 0x5037c, 0x2300001d }, +}; + +static struct mdp_table_entry mdp_downscale_y_table_PT6TOPT8[] = { + { 0x5fffc, 0xfe000070 }, + { 0x50300, 0x4bc00068 }, + { 0x5fffc, 0xfe000078 }, + { 0x50304, 0x4bc00060 }, + { 0x5fffc, 0xfe000080 }, + { 0x50308, 0x4b800059 }, + { 0x5fffc, 0xfe000089 }, + { 0x5030c, 0x4b000052 }, + { 0x5fffc, 0xfe400091 }, + { 0x50310, 0x4a80004b }, + { 0x5fffc, 0xfe40009a }, + { 0x50314, 0x4a000044 }, + { 0x5fffc, 0xfe8000a3 }, + { 0x50318, 0x4940003d }, + { 0x5fffc, 0xfec000ac }, + { 0x5031c, 0x48400037 }, + { 0x5fffc, 0xff0000b4 }, + { 0x50320, 0x47800031 }, + { 0x5fffc, 0xff8000bd }, + { 0x50324, 0x4640002b }, + { 0x5fffc, 0xc5 }, + { 0x50328, 0x45000026 }, + { 0x5fffc, 0x8000ce }, + { 0x5032c, 0x43800021 }, + { 0x5fffc, 0x10000d6 }, + { 0x50330, 0x4240001c }, + { 0x5fffc, 0x18000df }, + { 0x50334, 0x40800018 }, + { 0x5fffc, 0x24000e6 }, + { 0x50338, 0x3f000014 }, + { 0x5fffc, 0x30000ee }, + { 0x5033c, 0x3d400010 }, + { 0x5fffc, 0x40000f5 }, + { 0x50340, 0x3b80000c }, + { 0x5fffc, 0x50000fc }, + { 0x50344, 0x39800009 }, + { 0x5fffc, 0x6000102 }, + { 0x50348, 0x37c00006 }, + { 0x5fffc, 0x7000109 }, + { 0x5034c, 0x35800004 }, + { 0x5fffc, 0x840010e }, + { 0x50350, 0x33800002 }, + { 0x5fffc, 0x9800114 }, + { 0x50354, 0x31400000 }, + { 0x5fffc, 0xac00119 }, + { 0x50358, 0x2f4003fe }, + { 0x5fffc, 0xc40011e }, + { 0x5035c, 0x2d0003fc }, + { 0x5fffc, 0xdc00121 }, + { 0x50360, 0x2b0003fb }, + { 0x5fffc, 0xf400125 }, + { 0x50364, 0x28c003fa }, + { 0x5fffc, 0x11000128 }, + { 0x50368, 0x268003f9 }, + { 0x5fffc, 0x12c0012a }, + { 0x5036c, 0x244003f9 }, + { 0x5fffc, 0x1480012c }, + { 0x50370, 0x224003f8 }, + { 0x5fffc, 0x1640012e }, + { 0x50374, 0x200003f8 }, + { 0x5fffc, 0x1800012f }, + { 0x50378, 0x1e0003f8 }, + { 0x5fffc, 0x1a00012f }, + { 0x5037c, 0x1c0003f8 }, +}; + +static struct mdp_table_entry mdp_downscale_y_table_PT8TO1[] = { + { 0x5fffc, 0x0 }, + { 0x50300, 0x7fc00000 }, + { 0x5fffc, 0xff80000d }, + { 0x50304, 0x7ec003f9 }, + { 0x5fffc, 0xfec0001c }, + { 0x50308, 0x7d4003f3 }, + { 0x5fffc, 0xfe40002b }, + { 0x5030c, 0x7b8003ed }, + { 0x5fffc, 0xfd80003c }, + { 0x50310, 0x794003e8 }, + { 0x5fffc, 0xfcc0004d }, + { 0x50314, 0x76c003e4 }, + { 0x5fffc, 0xfc40005f }, + { 0x50318, 0x73c003e0 }, + { 0x5fffc, 0xfb800071 }, + { 0x5031c, 0x708003de }, + { 0x5fffc, 0xfac00085 }, + { 0x50320, 0x6d0003db }, + { 0x5fffc, 0xfa000098 }, + { 0x50324, 0x698003d9 }, + { 0x5fffc, 0xf98000ac }, + { 0x50328, 0x654003d8 }, + { 0x5fffc, 0xf8c000c1 }, + { 0x5032c, 0x610003d7 }, + { 0x5fffc, 0xf84000d5 }, + { 0x50330, 0x5c8003d7 }, + { 0x5fffc, 0xf7c000e9 }, + { 0x50334, 0x580003d7 }, + { 0x5fffc, 0xf74000fd }, + { 0x50338, 0x534003d8 }, + { 0x5fffc, 0xf6c00112 }, + { 0x5033c, 0x4e8003d8 }, + { 0x5fffc, 0xf6800126 }, + { 0x50340, 0x494003da }, + { 0x5fffc, 0xf600013a }, + { 0x50344, 0x448003db }, + { 0x5fffc, 0xf600014d }, + { 0x50348, 0x3f4003dd }, + { 0x5fffc, 0xf5c00160 }, + { 0x5034c, 0x3a4003df }, + { 0x5fffc, 0xf5c00172 }, + { 0x50350, 0x354003e1 }, + { 0x5fffc, 0xf5c00184 }, + { 0x50354, 0x304003e3 }, + { 0x5fffc, 0xf6000195 }, + { 0x50358, 0x2b0003e6 }, + { 0x5fffc, 0xf64001a6 }, + { 0x5035c, 0x260003e8 }, + { 0x5fffc, 0xf6c001b4 }, + { 0x50360, 0x214003eb }, + { 0x5fffc, 0xf78001c2 }, + { 0x50364, 0x1c4003ee }, + { 0x5fffc, 0xf80001cf }, + { 0x50368, 0x17c003f1 }, + { 0x5fffc, 0xf90001db }, + { 0x5036c, 0x134003f3 }, + { 0x5fffc, 0xfa0001e5 }, + { 0x50370, 0xf0003f6 }, + { 0x5fffc, 0xfb4001ee }, + { 0x50374, 0xac003f9 }, + { 0x5fffc, 0xfcc001f5 }, + { 0x50378, 0x70003fb }, + { 0x5fffc, 0xfe4001fb }, + { 0x5037c, 0x34003fe }, +}; + +struct mdp_table_entry *mdp_downscale_y_table[MDP_DOWNSCALE_MAX] = { + [MDP_DOWNSCALE_PT2TOPT4] = mdp_downscale_y_table_PT2TOPT4, + [MDP_DOWNSCALE_PT4TOPT6] = mdp_downscale_y_table_PT4TOPT6, + [MDP_DOWNSCALE_PT6TOPT8] = mdp_downscale_y_table_PT6TOPT8, + [MDP_DOWNSCALE_PT8TO1] = mdp_downscale_y_table_PT8TO1, +}; + +struct mdp_table_entry mdp_gaussian_blur_table[] = { + /* max variance */ + { 0x5fffc, 0x20000080 }, + { 0x50280, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50284, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50288, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5028c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50290, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50294, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50298, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5029c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502a0, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502a4, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502a8, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502ac, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502b0, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502b4, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502b8, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502bc, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502c0, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502c4, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502c8, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502cc, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502d0, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502d4, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502d8, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502dc, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502e0, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502e4, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502e8, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502ec, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502f0, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502f4, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502f8, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502fc, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50300, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50304, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50308, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5030c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50310, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50314, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50318, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5031c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50320, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50324, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50328, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5032c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50330, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50334, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50338, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5033c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50340, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50344, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50348, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5034c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50350, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50354, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50358, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5035c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50360, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50364, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50368, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5036c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50370, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50374, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50378, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5037c, 0x20000080 }, +}; diff --git a/drivers/video/msm/mdp_scale_tables.h b/drivers/video/msm/mdp_scale_tables.h new file mode 100644 index 00000000000..34077b1af60 --- /dev/null +++ b/drivers/video/msm/mdp_scale_tables.h @@ -0,0 +1,38 @@ +/* drivers/video/msm_fb/mdp_scale_tables.h + * + * Copyright (C) 2007 QUALCOMM Incorporated + * Copyright (C) 2007 Google Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ +#ifndef _MDP_SCALE_TABLES_H_ +#define _MDP_SCALE_TABLES_H_ + +#include +struct mdp_table_entry { + uint32_t reg; + uint32_t val; +}; + +extern struct mdp_table_entry mdp_upscale_table[64]; + +enum { + MDP_DOWNSCALE_PT2TOPT4, + MDP_DOWNSCALE_PT4TOPT6, + MDP_DOWNSCALE_PT6TOPT8, + MDP_DOWNSCALE_PT8TO1, + MDP_DOWNSCALE_MAX, +}; + +extern struct mdp_table_entry *mdp_downscale_x_table[MDP_DOWNSCALE_MAX]; +extern struct mdp_table_entry *mdp_downscale_y_table[MDP_DOWNSCALE_MAX]; +extern struct mdp_table_entry mdp_gaussian_blur_table[]; + +#endif diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c new file mode 100644 index 00000000000..49101dda45e --- /dev/null +++ b/drivers/video/msm/msm_fb.c @@ -0,0 +1,636 @@ +/* drivers/video/msm/msm_fb.c + * + * Core MSM framebuffer driver. + * + * Copyright (C) 2007 Google Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PRINT_FPS 0 +#define PRINT_BLIT_TIME 0 + +#define SLEEPING 0x4 +#define UPDATING 0x3 +#define FULL_UPDATE_DONE 0x2 +#define WAKING 0x1 +#define AWAKE 0x0 + +#define NONE 0 +#define SUSPEND_RESUME 0x1 +#define FPS 0x2 +#define BLIT_TIME 0x4 +#define SHOW_UPDATES 0x8 + +#define DLOG(mask, fmt, args...) \ +do { \ + if (msmfb_debug_mask & mask) \ + printk(KERN_INFO "msmfb: "fmt, ##args); \ +} while (0) + +static int msmfb_debug_mask; +module_param_named(msmfb_debug_mask, msmfb_debug_mask, int, + S_IRUGO | S_IWUSR | S_IWGRP); + +struct mdp_device *mdp; + +struct msmfb_info { + struct fb_info *fb; + struct msm_panel_data *panel; + int xres; + int yres; + unsigned output_format; + unsigned yoffset; + unsigned frame_requested; + unsigned frame_done; + int sleeping; + unsigned update_frame; + struct { + int left; + int top; + int eright; /* exclusive */ + int ebottom; /* exclusive */ + } update_info; + char *black; + + spinlock_t update_lock; + struct mutex panel_init_lock; + wait_queue_head_t frame_wq; + struct workqueue_struct *resume_workqueue; + struct work_struct resume_work; + struct msmfb_callback dma_callback; + struct msmfb_callback vsync_callback; + struct hrtimer fake_vsync; + ktime_t vsync_request_time; +}; + +static int msmfb_open(struct fb_info *info, int user) +{ + return 0; +} + +static int msmfb_release(struct fb_info *info, int user) +{ + return 0; +} + +/* Called from dma interrupt handler, must not sleep */ +static void msmfb_handle_dma_interrupt(struct msmfb_callback *callback) +{ + unsigned long irq_flags; + struct msmfb_info *msmfb = container_of(callback, struct msmfb_info, + dma_callback); + + spin_lock_irqsave(&msmfb->update_lock, irq_flags); + msmfb->frame_done = msmfb->frame_requested; + if (msmfb->sleeping == UPDATING && + msmfb->frame_done == msmfb->update_frame) { + DLOG(SUSPEND_RESUME, "full update completed\n"); + queue_work(msmfb->resume_workqueue, &msmfb->resume_work); + } + spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); + wake_up(&msmfb->frame_wq); +} + +static int msmfb_start_dma(struct msmfb_info *msmfb) +{ + uint32_t x, y, w, h; + unsigned addr; + unsigned long irq_flags; + uint32_t yoffset; + s64 time_since_request; + struct msm_panel_data *panel = msmfb->panel; + + spin_lock_irqsave(&msmfb->update_lock, irq_flags); + time_since_request = ktime_to_ns(ktime_sub(ktime_get(), + msmfb->vsync_request_time)); + if (time_since_request > 20 * NSEC_PER_MSEC) { + uint32_t us; + us = do_div(time_since_request, NSEC_PER_MSEC) / NSEC_PER_USEC; + printk(KERN_WARNING "msmfb_start_dma %lld.%03u ms after vsync " + "request\n", time_since_request, us); + } + if (msmfb->frame_done == msmfb->frame_requested) { + spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); + return -1; + } + if (msmfb->sleeping == SLEEPING) { + DLOG(SUSPEND_RESUME, "tried to start dma while asleep\n"); + spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); + return -1; + } + x = msmfb->update_info.left; + y = msmfb->update_info.top; + w = msmfb->update_info.eright - x; + h = msmfb->update_info.ebottom - y; + yoffset = msmfb->yoffset; + msmfb->update_info.left = msmfb->xres + 1; + msmfb->update_info.top = msmfb->yres + 1; + msmfb->update_info.eright = 0; + msmfb->update_info.ebottom = 0; + if (unlikely(w > msmfb->xres || h > msmfb->yres || + w == 0 || h == 0)) { + printk(KERN_INFO "invalid update: %d %d %d " + "%d\n", x, y, w, h); + msmfb->frame_done = msmfb->frame_requested; + goto error; + } + spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); + + addr = ((msmfb->xres * (yoffset + y) + x) * 2); + mdp->dma(mdp, addr + msmfb->fb->fix.smem_start, + msmfb->xres * 2, w, h, x, y, &msmfb->dma_callback, + panel->interface_type); + return 0; +error: + spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); + /* some clients need to clear their vsync interrupt */ + if (panel->clear_vsync) + panel->clear_vsync(panel); + wake_up(&msmfb->frame_wq); + return 0; +} + +/* Called from esync interrupt handler, must not sleep */ +static void msmfb_handle_vsync_interrupt(struct msmfb_callback *callback) +{ + struct msmfb_info *msmfb = container_of(callback, struct msmfb_info, + vsync_callback); + msmfb_start_dma(msmfb); +} + +static enum hrtimer_restart msmfb_fake_vsync(struct hrtimer *timer) +{ + struct msmfb_info *msmfb = container_of(timer, struct msmfb_info, + fake_vsync); + msmfb_start_dma(msmfb); + return HRTIMER_NORESTART; +} + +static void msmfb_pan_update(struct fb_info *info, uint32_t left, uint32_t top, + uint32_t eright, uint32_t ebottom, + uint32_t yoffset, int pan_display) +{ + struct msmfb_info *msmfb = info->par; + struct msm_panel_data *panel = msmfb->panel; + unsigned long irq_flags; + int sleeping; + int retry = 1; + + DLOG(SHOW_UPDATES, "update %d %d %d %d %d %d\n", + left, top, eright, ebottom, yoffset, pan_display); +restart: + spin_lock_irqsave(&msmfb->update_lock, irq_flags); + + /* if we are sleeping, on a pan_display wait 10ms (to throttle back + * drawing otherwise return */ + if (msmfb->sleeping == SLEEPING) { + DLOG(SUSPEND_RESUME, "drawing while asleep\n"); + spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); + if (pan_display) + wait_event_interruptible_timeout(msmfb->frame_wq, + msmfb->sleeping != SLEEPING, HZ/10); + return; + } + + sleeping = msmfb->sleeping; + /* on a full update, if the last frame has not completed, wait for it */ + if (pan_display && (msmfb->frame_requested != msmfb->frame_done || + sleeping == UPDATING)) { + int ret; + spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); + ret = wait_event_interruptible_timeout(msmfb->frame_wq, + msmfb->frame_done == msmfb->frame_requested && + msmfb->sleeping != UPDATING, 5 * HZ); + if (ret <= 0 && (msmfb->frame_requested != msmfb->frame_done || + msmfb->sleeping == UPDATING)) { + if (retry && panel->request_vsync && + (sleeping == AWAKE)) { + panel->request_vsync(panel, + &msmfb->vsync_callback); + retry = 0; + printk(KERN_WARNING "msmfb_pan_display timeout " + "rerequest vsync\n"); + } else { + printk(KERN_WARNING "msmfb_pan_display timeout " + "waiting for frame start, %d %d\n", + msmfb->frame_requested, + msmfb->frame_done); + return; + } + } + goto restart; + } + + + msmfb->frame_requested++; + /* if necessary, update the y offset, if this is the + * first full update on resume, set the sleeping state */ + if (pan_display) { + msmfb->yoffset = yoffset; + if (left == 0 && top == 0 && eright == info->var.xres && + ebottom == info->var.yres) { + if (sleeping == WAKING) { + msmfb->update_frame = msmfb->frame_requested; + DLOG(SUSPEND_RESUME, "full update starting\n"); + msmfb->sleeping = UPDATING; + } + } + } + + /* set the update request */ + if (left < msmfb->update_info.left) + msmfb->update_info.left = left; + if (top < msmfb->update_info.top) + msmfb->update_info.top = top; + if (eright > msmfb->update_info.eright) + msmfb->update_info.eright = eright; + if (ebottom > msmfb->update_info.ebottom) + msmfb->update_info.ebottom = ebottom; + DLOG(SHOW_UPDATES, "update queued %d %d %d %d %d\n", + msmfb->update_info.left, msmfb->update_info.top, + msmfb->update_info.eright, msmfb->update_info.ebottom, + msmfb->yoffset); + spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); + + /* if the panel is all the way on wait for vsync, otherwise sleep + * for 16 ms (long enough for the dma to panel) and then begin dma */ + msmfb->vsync_request_time = ktime_get(); + if (panel->request_vsync && (sleeping == AWAKE)) { + panel->request_vsync(panel, &msmfb->vsync_callback); + } else { + if (!hrtimer_active(&msmfb->fake_vsync)) { + hrtimer_start(&msmfb->fake_vsync, + ktime_set(0, NSEC_PER_SEC/60), + HRTIMER_MODE_REL); + } + } +} + +static void msmfb_update(struct fb_info *info, uint32_t left, uint32_t top, + uint32_t eright, uint32_t ebottom) +{ + msmfb_pan_update(info, left, top, eright, ebottom, 0, 0); +} + +static void power_on_panel(struct work_struct *work) +{ + struct msmfb_info *msmfb = + container_of(work, struct msmfb_info, resume_work); + struct msm_panel_data *panel = msmfb->panel; + unsigned long irq_flags; + + mutex_lock(&msmfb->panel_init_lock); + DLOG(SUSPEND_RESUME, "turning on panel\n"); + if (msmfb->sleeping == UPDATING) { + if (panel->unblank(panel)) { + printk(KERN_INFO "msmfb: panel unblank failed," + "not starting drawing\n"); + goto error; + } + spin_lock_irqsave(&msmfb->update_lock, irq_flags); + msmfb->sleeping = AWAKE; + wake_up(&msmfb->frame_wq); + spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); + } +error: + mutex_unlock(&msmfb->panel_init_lock); +} + + +static int msmfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + if ((var->xres != info->var.xres) || + (var->yres != info->var.yres) || + (var->xres_virtual != info->var.xres_virtual) || + (var->yres_virtual != info->var.yres_virtual) || + (var->xoffset != info->var.xoffset) || + (var->bits_per_pixel != info->var.bits_per_pixel) || + (var->grayscale != info->var.grayscale)) + return -EINVAL; + return 0; +} + +int msmfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct msmfb_info *msmfb = info->par; + struct msm_panel_data *panel = msmfb->panel; + + /* "UPDT" */ + if ((panel->caps & MSMFB_CAP_PARTIAL_UPDATES) && + (var->reserved[0] == 0x54445055)) { + msmfb_pan_update(info, var->reserved[1] & 0xffff, + var->reserved[1] >> 16, + var->reserved[2] & 0xffff, + var->reserved[2] >> 16, var->yoffset, 1); + } else { + msmfb_pan_update(info, 0, 0, info->var.xres, info->var.yres, + var->yoffset, 1); + } + return 0; +} + +static void msmfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect) +{ + cfb_fillrect(p, rect); + msmfb_update(p, rect->dx, rect->dy, rect->dx + rect->width, + rect->dy + rect->height); +} + +static void msmfb_copyarea(struct fb_info *p, const struct fb_copyarea *area) +{ + cfb_copyarea(p, area); + msmfb_update(p, area->dx, area->dy, area->dx + area->width, + area->dy + area->height); +} + +static void msmfb_imageblit(struct fb_info *p, const struct fb_image *image) +{ + cfb_imageblit(p, image); + msmfb_update(p, image->dx, image->dy, image->dx + image->width, + image->dy + image->height); +} + + +static int msmfb_blit(struct fb_info *info, + void __user *p) +{ + struct mdp_blit_req req; + struct mdp_blit_req_list req_list; + int i; + int ret; + + if (copy_from_user(&req_list, p, sizeof(req_list))) + return -EFAULT; + + for (i = 0; i < req_list.count; i++) { + struct mdp_blit_req_list *list = + (struct mdp_blit_req_list *)p; + if (copy_from_user(&req, &list->req[i], sizeof(req))) + return -EFAULT; + ret = mdp->blit(mdp, info, &req); + if (ret) + return ret; + } + return 0; +} + + +DEFINE_MUTEX(mdp_ppp_lock); + +static int msmfb_ioctl(struct fb_info *p, unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int ret; + + switch (cmd) { + case MSMFB_GRP_DISP: + mdp->set_grp_disp(mdp, arg); + break; + case MSMFB_BLIT: + ret = msmfb_blit(p, argp); + if (ret) + return ret; + break; + default: + printk(KERN_INFO "msmfb unknown ioctl: %d\n", cmd); + return -EINVAL; + } + return 0; +} + +static struct fb_ops msmfb_ops = { + .owner = THIS_MODULE, + .fb_open = msmfb_open, + .fb_release = msmfb_release, + .fb_check_var = msmfb_check_var, + .fb_pan_display = msmfb_pan_display, + .fb_fillrect = msmfb_fillrect, + .fb_copyarea = msmfb_copyarea, + .fb_imageblit = msmfb_imageblit, + .fb_ioctl = msmfb_ioctl, +}; + +static unsigned PP[16]; + + + +#define BITS_PER_PIXEL 16 + +static void setup_fb_info(struct msmfb_info *msmfb) +{ + struct fb_info *fb_info = msmfb->fb; + int r; + + /* finish setting up the fb_info struct */ + strncpy(fb_info->fix.id, "msmfb", 16); + fb_info->fix.ypanstep = 1; + + fb_info->fbops = &msmfb_ops; + fb_info->flags = FBINFO_DEFAULT; + + fb_info->fix.type = FB_TYPE_PACKED_PIXELS; + fb_info->fix.visual = FB_VISUAL_TRUECOLOR; + fb_info->fix.line_length = msmfb->xres * 2; + + fb_info->var.xres = msmfb->xres; + fb_info->var.yres = msmfb->yres; + fb_info->var.width = msmfb->panel->fb_data->width; + fb_info->var.height = msmfb->panel->fb_data->height; + fb_info->var.xres_virtual = msmfb->xres; + fb_info->var.yres_virtual = msmfb->yres * 2; + fb_info->var.bits_per_pixel = BITS_PER_PIXEL; + fb_info->var.accel_flags = 0; + + fb_info->var.yoffset = 0; + + if (msmfb->panel->caps & MSMFB_CAP_PARTIAL_UPDATES) { + fb_info->var.reserved[0] = 0x54445055; + fb_info->var.reserved[1] = 0; + fb_info->var.reserved[2] = (uint16_t)msmfb->xres | + ((uint32_t)msmfb->yres << 16); + } + + fb_info->var.red.offset = 11; + fb_info->var.red.length = 5; + fb_info->var.red.msb_right = 0; + fb_info->var.green.offset = 5; + fb_info->var.green.length = 6; + fb_info->var.green.msb_right = 0; + fb_info->var.blue.offset = 0; + fb_info->var.blue.length = 5; + fb_info->var.blue.msb_right = 0; + + r = fb_alloc_cmap(&fb_info->cmap, 16, 0); + fb_info->pseudo_palette = PP; + + PP[0] = 0; + for (r = 1; r < 16; r++) + PP[r] = 0xffffffff; +} + +static int setup_fbmem(struct msmfb_info *msmfb, struct platform_device *pdev) +{ + struct fb_info *fb = msmfb->fb; + struct resource *resource; + unsigned long size = msmfb->xres * msmfb->yres * + (BITS_PER_PIXEL >> 3) * 2; + unsigned char *fbram; + + /* board file might have attached a resource describing an fb */ + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!resource) + return -EINVAL; + + /* check the resource is large enough to fit the fb */ + if (resource->end - resource->start < size) { + printk(KERN_ERR "allocated resource is too small for " + "fb\n"); + return -ENOMEM; + } + fb->fix.smem_start = resource->start; + fb->fix.smem_len = resource->end - resource->start; + fbram = ioremap(resource->start, + resource->end - resource->start); + if (fbram == 0) { + printk(KERN_ERR "msmfb: cannot allocate fbram!\n"); + return -ENOMEM; + } + fb->screen_base = fbram; + return 0; +} + +static int msmfb_probe(struct platform_device *pdev) +{ + struct fb_info *fb; + struct msmfb_info *msmfb; + struct msm_panel_data *panel = pdev->dev.platform_data; + int ret; + + if (!panel) { + pr_err("msmfb_probe: no platform data\n"); + return -EINVAL; + } + if (!panel->fb_data) { + pr_err("msmfb_probe: no fb_data\n"); + return -EINVAL; + } + + fb = framebuffer_alloc(sizeof(struct msmfb_info), &pdev->dev); + if (!fb) + return -ENOMEM; + msmfb = fb->par; + msmfb->fb = fb; + msmfb->panel = panel; + msmfb->xres = panel->fb_data->xres; + msmfb->yres = panel->fb_data->yres; + + ret = setup_fbmem(msmfb, pdev); + if (ret) + goto error_setup_fbmem; + + setup_fb_info(msmfb); + + spin_lock_init(&msmfb->update_lock); + mutex_init(&msmfb->panel_init_lock); + init_waitqueue_head(&msmfb->frame_wq); + msmfb->resume_workqueue = create_workqueue("panel_on"); + if (msmfb->resume_workqueue == NULL) { + printk(KERN_ERR "failed to create panel_on workqueue\n"); + ret = -ENOMEM; + goto error_create_workqueue; + } + INIT_WORK(&msmfb->resume_work, power_on_panel); + msmfb->black = kzalloc(msmfb->fb->var.bits_per_pixel*msmfb->xres, + GFP_KERNEL); + + printk(KERN_INFO "msmfb_probe() installing %d x %d panel\n", + msmfb->xres, msmfb->yres); + + msmfb->dma_callback.func = msmfb_handle_dma_interrupt; + msmfb->vsync_callback.func = msmfb_handle_vsync_interrupt; + hrtimer_init(&msmfb->fake_vsync, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + + + msmfb->fake_vsync.function = msmfb_fake_vsync; + + ret = register_framebuffer(fb); + if (ret) + goto error_register_framebuffer; + + msmfb->sleeping = WAKING; + + return 0; + +error_register_framebuffer: + destroy_workqueue(msmfb->resume_workqueue); +error_create_workqueue: + iounmap(fb->screen_base); +error_setup_fbmem: + framebuffer_release(msmfb->fb); + return ret; +} + +static struct platform_driver msm_panel_driver = { + /* need to write remove */ + .probe = msmfb_probe, + .driver = {.name = "msm_panel"}, +}; + + +static int msmfb_add_mdp_device(struct device *dev, + struct class_interface *class_intf) +{ + /* might need locking if mulitple mdp devices */ + if (mdp) + return 0; + mdp = container_of(dev, struct mdp_device, dev); + return platform_driver_register(&msm_panel_driver); +} + +static void msmfb_remove_mdp_device(struct device *dev, + struct class_interface *class_intf) +{ + /* might need locking if mulitple mdp devices */ + if (dev != &mdp->dev) + return; + platform_driver_unregister(&msm_panel_driver); + mdp = NULL; +} + +static struct class_interface msm_fb_interface = { + .add_dev = &msmfb_add_mdp_device, + .remove_dev = &msmfb_remove_mdp_device, +}; + +static int __init msmfb_init(void) +{ + return register_mdp_client(&msm_fb_interface); +} + +module_init(msmfb_init); -- cgit v1.2.3 From 6e3658f0df6f708202159302b4f3915d9b97b8dc Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Tue, 22 Sep 2009 16:47:04 -0700 Subject: platinumfb: fix misplaced parenthesis Since `+' has a higher precedence than the trinary operator `?', this added `hres * (1 << color_mode)' to the boolean testing videomode and depth. Signed-off-by: Roel Kluin Cc: Geert Uytterhoeven Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Ville Syrjala Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/platinumfb.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/platinumfb.c b/drivers/video/platinumfb.c index bacfabd9ce1..0a366d86f08 100644 --- a/drivers/video/platinumfb.c +++ b/drivers/video/platinumfb.c @@ -223,10 +223,14 @@ static int platinumfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, static inline int platinum_vram_reqd(int video_mode, int color_mode) { - return vmode_attrs[video_mode-1].vres * - (vmode_attrs[video_mode-1].hres * (1< CMODE_8)) ? 0x10 : 0x20) + 0x1000; + int baseval = vmode_attrs[video_mode-1].hres * (1< CMODE_8)) + baseval += 0x10; + else + baseval += 0x20; + + return vmode_attrs[video_mode-1].vres * baseval + 0x1000; } #define STORE_D2(a, d) { \ -- cgit v1.2.3 From 4ed824d9aead77a6a4eb1e89c3b3d270ba386fad Mon Sep 17 00:00:00 2001 From: Sudhakar Rajashekhara Date: Tue, 22 Sep 2009 16:47:06 -0700 Subject: davinci: fb: Frame Buffer driver for TI DA8xx/OMAP-L1xx Add LCD controller (LCDC) driver for TI's DA8xx/OMAP-L1xx architecture. LCDC specifications can be found at http://www.ti.com/litv/pdf/sprufm0a. LCDC on DA8xx consists of two independent controllers, the Raster Controller and the LCD Interface Display Driver (LIDD) controller. LIDD further supports character and graphic displays. This patch adds support for the graphic display (Sharp LQ035Q3DG01) found on the DA830 based EVM. The EVM details can be found at: http://support.spectrumdigital.com/boards/dskda830/revc/. Signed-off-by: Sudhakar Rajashekhara Signed-off-by: Pavel Kiryukhin Signed-off-by: Steve Chen Acked-by: Krzysztof Helt DESC davinci-fb-frame-buffer-driver-for-ti-da8xx-omap-l1xx-fix EDESC From: Andrew Morton fix kconfig indenting Cc: Krzysztof Helt Cc: Pavel Kiryukhin Cc: Steve Chen Cc: Sudhakar Rajashekhara Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/Kconfig | 11 + drivers/video/Makefile | 1 + drivers/video/da8xx-fb.c | 909 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 921 insertions(+) create mode 100644 drivers/video/da8xx-fb.c (limited to 'drivers/video') diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 05184a1c3e9..a7944ef48a2 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -2041,6 +2041,17 @@ config FB_SH7760 and 8, 15 or 16 bpp color; 90 degrees clockwise display rotation for panels <= 320 pixel horizontal resolution. +config FB_DA8XX + tristate "DA8xx/OMAP-L1xx Framebuffer support" + depends on FB && ARCH_DAVINCI_DA8XX + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + ---help--- + This is the frame buffer device driver for the TI LCD controller + found on DA8xx/OMAP-L1xx SoCs. + If unsure, say N. + config FB_VIRTUAL tristate "Virtual Frame Buffer support (ONLY FOR TESTING!)" depends on FB diff --git a/drivers/video/Makefile b/drivers/video/Makefile index f4b6c150bd0..85b2d2322dc 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -137,6 +137,7 @@ obj-$(CONFIG_FB_OF) += offb.o obj-$(CONFIG_FB_BF54X_LQ043) += bf54x-lq043fb.o obj-$(CONFIG_FB_BFIN_T350MCQB) += bfin-t350mcqb-fb.o obj-$(CONFIG_FB_MX3) += mx3fb.o +obj-$(CONFIG_FB_DA8XX) += da8xx-fb.o # the test framebuffer is last obj-$(CONFIG_FB_VIRTUAL) += vfb.o diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c new file mode 100644 index 00000000000..e0bbc66499c --- /dev/null +++ b/drivers/video/da8xx-fb.c @@ -0,0 +1,909 @@ +/* + * Copyright (C) 2008-2009 MontaVista Software Inc. + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * Based on the LCD driver for TI Avalanche processors written by + * Ajay Singh and Shalom Hai. + * + * 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 +#include +#include +#include