diff options
Diffstat (limited to 'drivers/video/sh_mobile_lcdcfb.c')
-rw-r--r-- | drivers/video/sh_mobile_lcdcfb.c | 91 |
1 files changed, 68 insertions, 23 deletions
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index f10d2fbeda06..8f24564f77b0 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include <linux/platform_device.h> | 17 | #include <linux/platform_device.h> |
18 | #include <linux/dma-mapping.h> | 18 | #include <linux/dma-mapping.h> |
19 | #include <linux/interrupt.h> | 19 | #include <linux/interrupt.h> |
20 | #include <linux/vmalloc.h> | ||
20 | #include <video/sh_mobile_lcdc.h> | 21 | #include <video/sh_mobile_lcdc.h> |
21 | #include <asm/atomic.h> | 22 | #include <asm/atomic.h> |
22 | 23 | ||
@@ -30,9 +31,10 @@ struct sh_mobile_lcdc_chan { | |||
30 | unsigned long enabled; /* ME and SE in LDCNT2R */ | 31 | unsigned long enabled; /* ME and SE in LDCNT2R */ |
31 | struct sh_mobile_lcdc_chan_cfg cfg; | 32 | struct sh_mobile_lcdc_chan_cfg cfg; |
32 | u32 pseudo_palette[PALETTE_NR]; | 33 | u32 pseudo_palette[PALETTE_NR]; |
33 | struct fb_info info; | 34 | struct fb_info *info; |
34 | dma_addr_t dma_handle; | 35 | dma_addr_t dma_handle; |
35 | struct fb_deferred_io defio; | 36 | struct fb_deferred_io defio; |
37 | struct scatterlist *sglist; | ||
36 | unsigned long frame_end; | 38 | unsigned long frame_end; |
37 | wait_queue_head_t frame_end_wait; | 39 | wait_queue_head_t frame_end_wait; |
38 | }; | 40 | }; |
@@ -206,16 +208,38 @@ static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv) {} | |||
206 | static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv) {} | 208 | static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv) {} |
207 | #endif | 209 | #endif |
208 | 210 | ||
211 | static int sh_mobile_lcdc_sginit(struct fb_info *info, | ||
212 | struct list_head *pagelist) | ||
213 | { | ||
214 | struct sh_mobile_lcdc_chan *ch = info->par; | ||
215 | unsigned int nr_pages_max = info->fix.smem_len >> PAGE_SHIFT; | ||
216 | struct page *page; | ||
217 | int nr_pages = 0; | ||
218 | |||
219 | sg_init_table(ch->sglist, nr_pages_max); | ||
220 | |||
221 | list_for_each_entry(page, pagelist, lru) | ||
222 | sg_set_page(&ch->sglist[nr_pages++], page, PAGE_SIZE, 0); | ||
223 | |||
224 | return nr_pages; | ||
225 | } | ||
226 | |||
209 | static void sh_mobile_lcdc_deferred_io(struct fb_info *info, | 227 | static void sh_mobile_lcdc_deferred_io(struct fb_info *info, |
210 | struct list_head *pagelist) | 228 | struct list_head *pagelist) |
211 | { | 229 | { |
212 | struct sh_mobile_lcdc_chan *ch = info->par; | 230 | struct sh_mobile_lcdc_chan *ch = info->par; |
231 | unsigned int nr_pages; | ||
213 | 232 | ||
214 | /* enable clocks before accessing hardware */ | 233 | /* enable clocks before accessing hardware */ |
215 | sh_mobile_lcdc_clk_on(ch->lcdc); | 234 | sh_mobile_lcdc_clk_on(ch->lcdc); |
216 | 235 | ||
236 | nr_pages = sh_mobile_lcdc_sginit(info, pagelist); | ||
237 | dma_map_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); | ||
238 | |||
217 | /* trigger panel update */ | 239 | /* trigger panel update */ |
218 | lcdc_write_chan(ch, LDSM2R, 1); | 240 | lcdc_write_chan(ch, LDSM2R, 1); |
241 | |||
242 | dma_unmap_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); | ||
219 | } | 243 | } |
220 | 244 | ||
221 | static void sh_mobile_lcdc_deferred_io_touch(struct fb_info *info) | 245 | static void sh_mobile_lcdc_deferred_io_touch(struct fb_info *info) |
@@ -418,22 +442,22 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) | |||
418 | /* set bpp format in PKF[4:0] */ | 442 | /* set bpp format in PKF[4:0] */ |
419 | tmp = lcdc_read_chan(ch, LDDFR); | 443 | tmp = lcdc_read_chan(ch, LDDFR); |
420 | tmp &= ~(0x0001001f); | 444 | tmp &= ~(0x0001001f); |
421 | tmp |= (priv->ch[k].info.var.bits_per_pixel == 16) ? 3 : 0; | 445 | tmp |= (ch->info->var.bits_per_pixel == 16) ? 3 : 0; |
422 | lcdc_write_chan(ch, LDDFR, tmp); | 446 | lcdc_write_chan(ch, LDDFR, tmp); |
423 | 447 | ||
424 | /* point out our frame buffer */ | 448 | /* point out our frame buffer */ |
425 | lcdc_write_chan(ch, LDSA1R, ch->info.fix.smem_start); | 449 | lcdc_write_chan(ch, LDSA1R, ch->info->fix.smem_start); |
426 | 450 | ||
427 | /* set line size */ | 451 | /* set line size */ |
428 | lcdc_write_chan(ch, LDMLSR, ch->info.fix.line_length); | 452 | lcdc_write_chan(ch, LDMLSR, ch->info->fix.line_length); |
429 | 453 | ||
430 | /* setup deferred io if SYS bus */ | 454 | /* setup deferred io if SYS bus */ |
431 | tmp = ch->cfg.sys_bus_cfg.deferred_io_msec; | 455 | tmp = ch->cfg.sys_bus_cfg.deferred_io_msec; |
432 | if (ch->ldmt1r_value & (1 << 12) && tmp) { | 456 | if (ch->ldmt1r_value & (1 << 12) && tmp) { |
433 | ch->defio.deferred_io = sh_mobile_lcdc_deferred_io; | 457 | ch->defio.deferred_io = sh_mobile_lcdc_deferred_io; |
434 | ch->defio.delay = msecs_to_jiffies(tmp); | 458 | ch->defio.delay = msecs_to_jiffies(tmp); |
435 | ch->info.fbdefio = &ch->defio; | 459 | ch->info->fbdefio = &ch->defio; |
436 | fb_deferred_io_init(&ch->info); | 460 | fb_deferred_io_init(ch->info); |
437 | 461 | ||
438 | /* one-shot mode */ | 462 | /* one-shot mode */ |
439 | lcdc_write_chan(ch, LDSM1R, 1); | 463 | lcdc_write_chan(ch, LDSM1R, 1); |
@@ -479,12 +503,12 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv) | |||
479 | * flush frame, and wait for frame end interrupt | 503 | * flush frame, and wait for frame end interrupt |
480 | * clean up deferred io and enable clock | 504 | * clean up deferred io and enable clock |
481 | */ | 505 | */ |
482 | if (ch->info.fbdefio) { | 506 | if (ch->info->fbdefio) { |
483 | ch->frame_end = 0; | 507 | ch->frame_end = 0; |
484 | schedule_delayed_work(&ch->info.deferred_work, 0); | 508 | schedule_delayed_work(&ch->info->deferred_work, 0); |
485 | wait_event(ch->frame_end_wait, ch->frame_end); | 509 | wait_event(ch->frame_end_wait, ch->frame_end); |
486 | fb_deferred_io_cleanup(&ch->info); | 510 | fb_deferred_io_cleanup(ch->info); |
487 | ch->info.fbdefio = NULL; | 511 | ch->info->fbdefio = NULL; |
488 | sh_mobile_lcdc_clk_on(priv); | 512 | sh_mobile_lcdc_clk_on(priv); |
489 | } | 513 | } |
490 | 514 | ||
@@ -793,9 +817,16 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev) | |||
793 | priv->base = ioremap_nocache(res->start, (res->end - res->start) + 1); | 817 | priv->base = ioremap_nocache(res->start, (res->end - res->start) + 1); |
794 | 818 | ||
795 | for (i = 0; i < j; i++) { | 819 | for (i = 0; i < j; i++) { |
796 | info = &priv->ch[i].info; | ||
797 | cfg = &priv->ch[i].cfg; | 820 | cfg = &priv->ch[i].cfg; |
798 | 821 | ||
822 | priv->ch[i].info = framebuffer_alloc(0, &pdev->dev); | ||
823 | if (!priv->ch[i].info) { | ||
824 | dev_err(&pdev->dev, "unable to allocate fb_info\n"); | ||
825 | error = -ENOMEM; | ||
826 | break; | ||
827 | } | ||
828 | |||
829 | info = priv->ch[i].info; | ||
799 | info->fbops = &sh_mobile_lcdc_ops; | 830 | info->fbops = &sh_mobile_lcdc_ops; |
800 | info->var.xres = info->var.xres_virtual = cfg->lcd_cfg.xres; | 831 | info->var.xres = info->var.xres_virtual = cfg->lcd_cfg.xres; |
801 | info->var.yres = info->var.yres_virtual = cfg->lcd_cfg.yres; | 832 | info->var.yres = info->var.yres_virtual = cfg->lcd_cfg.yres; |
@@ -846,21 +877,31 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev) | |||
846 | } | 877 | } |
847 | 878 | ||
848 | for (i = 0; i < j; i++) { | 879 | for (i = 0; i < j; i++) { |
849 | error = register_framebuffer(&priv->ch[i].info); | 880 | struct sh_mobile_lcdc_chan *ch = priv->ch + i; |
881 | |||
882 | info = ch->info; | ||
883 | |||
884 | if (info->fbdefio) { | ||
885 | priv->ch->sglist = vmalloc(sizeof(struct scatterlist) * | ||
886 | info->fix.smem_len >> PAGE_SHIFT); | ||
887 | if (!priv->ch->sglist) { | ||
888 | dev_err(&pdev->dev, "cannot allocate sglist\n"); | ||
889 | goto err1; | ||
890 | } | ||
891 | } | ||
892 | |||
893 | error = register_framebuffer(info); | ||
850 | if (error < 0) | 894 | if (error < 0) |
851 | goto err1; | 895 | goto err1; |
852 | } | ||
853 | 896 | ||
854 | for (i = 0; i < j; i++) { | ||
855 | info = &priv->ch[i].info; | ||
856 | dev_info(info->dev, | 897 | dev_info(info->dev, |
857 | "registered %s/%s as %dx%d %dbpp.\n", | 898 | "registered %s/%s as %dx%d %dbpp.\n", |
858 | pdev->name, | 899 | pdev->name, |
859 | (priv->ch[i].cfg.chan == LCDC_CHAN_MAINLCD) ? | 900 | (ch->cfg.chan == LCDC_CHAN_MAINLCD) ? |
860 | "mainlcd" : "sublcd", | 901 | "mainlcd" : "sublcd", |
861 | (int) priv->ch[i].cfg.lcd_cfg.xres, | 902 | (int) ch->cfg.lcd_cfg.xres, |
862 | (int) priv->ch[i].cfg.lcd_cfg.yres, | 903 | (int) ch->cfg.lcd_cfg.yres, |
863 | priv->ch[i].cfg.bpp); | 904 | ch->cfg.bpp); |
864 | 905 | ||
865 | /* deferred io mode: disable clock to save power */ | 906 | /* deferred io mode: disable clock to save power */ |
866 | if (info->fbdefio) | 907 | if (info->fbdefio) |
@@ -881,20 +922,24 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev) | |||
881 | int i; | 922 | int i; |
882 | 923 | ||
883 | for (i = 0; i < ARRAY_SIZE(priv->ch); i++) | 924 | for (i = 0; i < ARRAY_SIZE(priv->ch); i++) |
884 | if (priv->ch[i].info.dev) | 925 | if (priv->ch[i].info->dev) |
885 | unregister_framebuffer(&priv->ch[i].info); | 926 | unregister_framebuffer(priv->ch[i].info); |
886 | 927 | ||
887 | sh_mobile_lcdc_stop(priv); | 928 | sh_mobile_lcdc_stop(priv); |
888 | 929 | ||
889 | for (i = 0; i < ARRAY_SIZE(priv->ch); i++) { | 930 | for (i = 0; i < ARRAY_SIZE(priv->ch); i++) { |
890 | info = &priv->ch[i].info; | 931 | info = priv->ch[i].info; |
891 | 932 | ||
892 | if (!info->device) | 933 | if (!info || !info->device) |
893 | continue; | 934 | continue; |
894 | 935 | ||
936 | if (priv->ch[i].sglist) | ||
937 | vfree(priv->ch[i].sglist); | ||
938 | |||
895 | dma_free_coherent(&pdev->dev, info->fix.smem_len, | 939 | dma_free_coherent(&pdev->dev, info->fix.smem_len, |
896 | info->screen_base, priv->ch[i].dma_handle); | 940 | info->screen_base, priv->ch[i].dma_handle); |
897 | fb_dealloc_cmap(&info->cmap); | 941 | fb_dealloc_cmap(&info->cmap); |
942 | framebuffer_release(info); | ||
898 | } | 943 | } |
899 | 944 | ||
900 | #ifdef CONFIG_HAVE_CLK | 945 | #ifdef CONFIG_HAVE_CLK |