diff options
| -rw-r--r-- | drivers/video/sh_mobile_lcdcfb.c | 53 |
1 files changed, 45 insertions, 8 deletions
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index f10d2fbeda06..da983b720f08 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 | ||
| @@ -33,6 +34,7 @@ struct sh_mobile_lcdc_chan { | |||
| 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) |
| @@ -846,21 +870,31 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev) | |||
| 846 | } | 870 | } |
| 847 | 871 | ||
| 848 | for (i = 0; i < j; i++) { | 872 | for (i = 0; i < j; i++) { |
| 849 | error = register_framebuffer(&priv->ch[i].info); | 873 | struct sh_mobile_lcdc_chan *ch = priv->ch + i; |
| 874 | |||
| 875 | info = &ch->info; | ||
| 876 | |||
| 877 | if (info->fbdefio) { | ||
| 878 | priv->ch->sglist = vmalloc(sizeof(struct scatterlist) * | ||
| 879 | info->fix.smem_len >> PAGE_SHIFT); | ||
| 880 | if (!priv->ch->sglist) { | ||
| 881 | dev_err(&pdev->dev, "cannot allocate sglist\n"); | ||
| 882 | goto err1; | ||
| 883 | } | ||
| 884 | } | ||
| 885 | |||
| 886 | error = register_framebuffer(info); | ||
| 850 | if (error < 0) | 887 | if (error < 0) |
| 851 | goto err1; | 888 | goto err1; |
| 852 | } | ||
| 853 | 889 | ||
| 854 | for (i = 0; i < j; i++) { | ||
| 855 | info = &priv->ch[i].info; | ||
| 856 | dev_info(info->dev, | 890 | dev_info(info->dev, |
| 857 | "registered %s/%s as %dx%d %dbpp.\n", | 891 | "registered %s/%s as %dx%d %dbpp.\n", |
| 858 | pdev->name, | 892 | pdev->name, |
| 859 | (priv->ch[i].cfg.chan == LCDC_CHAN_MAINLCD) ? | 893 | (ch->cfg.chan == LCDC_CHAN_MAINLCD) ? |
| 860 | "mainlcd" : "sublcd", | 894 | "mainlcd" : "sublcd", |
| 861 | (int) priv->ch[i].cfg.lcd_cfg.xres, | 895 | (int) ch->cfg.lcd_cfg.xres, |
| 862 | (int) priv->ch[i].cfg.lcd_cfg.yres, | 896 | (int) ch->cfg.lcd_cfg.yres, |
| 863 | priv->ch[i].cfg.bpp); | 897 | ch->cfg.bpp); |
| 864 | 898 | ||
| 865 | /* deferred io mode: disable clock to save power */ | 899 | /* deferred io mode: disable clock to save power */ |
| 866 | if (info->fbdefio) | 900 | if (info->fbdefio) |
| @@ -892,6 +926,9 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev) | |||
| 892 | if (!info->device) | 926 | if (!info->device) |
| 893 | continue; | 927 | continue; |
| 894 | 928 | ||
| 929 | if (priv->ch[i].sglist) | ||
| 930 | vfree(priv->ch[i].sglist); | ||
| 931 | |||
| 895 | dma_free_coherent(&pdev->dev, info->fix.smem_len, | 932 | dma_free_coherent(&pdev->dev, info->fix.smem_len, |
| 896 | info->screen_base, priv->ch[i].dma_handle); | 933 | info->screen_base, priv->ch[i].dma_handle); |
| 897 | fb_dealloc_cmap(&info->cmap); | 934 | fb_dealloc_cmap(&info->cmap); |
