sh: LCDC dcache flush for deferred io
authorPaul Mundt <lethal@linux-sh.org>
Wed, 1 Jul 2009 06:50:31 +0000 (06:50 +0000)
committerPaul Mundt <lethal@linux-sh.org>
Wed, 1 Jul 2009 18:34:37 +0000 (03:34 +0900)
Since writenotify on uncached vmas is unsupported in 2.6.31,
live with cached framebuffer memory in the deferred io
case for now and flush the dcache before forcing refresh.

Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Acked-by: Magnus damm <damm@igel.co.jp>
drivers/video/sh_mobile_lcdcfb.c

index f10d2fbeda060275750f3c61fc5dc5f48634f931..da983b720f080dba756dcd48860359412c6121e5 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 #include <linux/interrupt.h>
+#include <linux/vmalloc.h>
 #include <video/sh_mobile_lcdc.h>
 #include <asm/atomic.h>
 
@@ -33,6 +34,7 @@ struct sh_mobile_lcdc_chan {
        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;
 };
@@ -206,16 +208,38 @@ 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)
+{
+       struct sh_mobile_lcdc_chan *ch = info->par;
+       unsigned int nr_pages_max = info->fix.smem_len >> PAGE_SHIFT;
+       struct page *page;
+       int nr_pages = 0;
+
+       sg_init_table(ch->sglist, nr_pages_max);
+
+       list_for_each_entry(page, pagelist, lru)
+               sg_set_page(&ch->sglist[nr_pages++], page, PAGE_SIZE, 0);
+
+       return nr_pages;
+}
+
 static void sh_mobile_lcdc_deferred_io(struct fb_info *info,
                                       struct list_head *pagelist)
 {
        struct sh_mobile_lcdc_chan *ch = info->par;
+       unsigned int nr_pages;
 
        /* enable clocks before accessing hardware */
        sh_mobile_lcdc_clk_on(ch->lcdc);
 
+       nr_pages = sh_mobile_lcdc_sginit(info, pagelist);
+       dma_map_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
+
        /* trigger panel update */
        lcdc_write_chan(ch, LDSM2R, 1);
+
+       dma_unmap_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
 }
 
 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)
        }
 
        for (i = 0; i < j; i++) {
-               error = register_framebuffer(&priv->ch[i].info);
+               struct sh_mobile_lcdc_chan *ch = priv->ch + i;
+
+               info = &ch->info;
+
+               if (info->fbdefio) {
+                       priv->ch->sglist = vmalloc(sizeof(struct scatterlist) *
+                                       info->fix.smem_len >> PAGE_SHIFT);
+                       if (!priv->ch->sglist) {
+                               dev_err(&pdev->dev, "cannot allocate sglist\n");
+                               goto err1;
+                       }
+               }
+
+               error = register_framebuffer(info);
                if (error < 0)
                        goto err1;
-       }
 
-       for (i = 0; i < j; i++) {
-               info = &priv->ch[i].info;
                dev_info(info->dev,
                         "registered %s/%s as %dx%d %dbpp.\n",
                         pdev->name,
-                        (priv->ch[i].cfg.chan == LCDC_CHAN_MAINLCD) ?
+                        (ch->cfg.chan == LCDC_CHAN_MAINLCD) ?
                         "mainlcd" : "sublcd",
-                        (int) priv->ch[i].cfg.lcd_cfg.xres,
-                        (int) priv->ch[i].cfg.lcd_cfg.yres,
-                        priv->ch[i].cfg.bpp);
+                        (int) ch->cfg.lcd_cfg.xres,
+                        (int) ch->cfg.lcd_cfg.yres,
+                        ch->cfg.bpp);
 
                /* deferred io mode: disable clock to save power */
                if (info->fbdefio)
@@ -892,6 +926,9 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev)
                if (!info->device)
                        continue;
 
+               if (priv->ch[i].sglist)
+                       vfree(priv->ch[i].sglist);
+
                dma_free_coherent(&pdev->dev, info->fix.smem_len,
                                  info->screen_base, priv->ch[i].dma_handle);
                fb_dealloc_cmap(&info->cmap);