aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/sh_mobile_lcdcfb.c
diff options
context:
space:
mode:
authorPaul Mundt <lethal@linux-sh.org>2009-11-04 01:59:04 -0500
committerPaul Mundt <lethal@linux-sh.org>2009-11-04 01:59:04 -0500
commit5c1a56b5f616f7063f91eb85f0ea209658f387dc (patch)
tree227964f2cbde7986538836e153da33879ed4822d /drivers/video/sh_mobile_lcdcfb.c
parent9016332014404ae1dca7198f93804ac67ba9e918 (diff)
video: sh_mobile_lcdcfb: Don't attempt to map zero-length scatterlists.
More aggressive DMA mapping debugging has uncovered a long-standing buglet in the way that the sh_mobile_lcdcfb driver implements its deferred I/O callback. When used as a console driver the acceleration routines are called by the kernel which subsequently cause the deferred I/O work to be scheduled, resulting in the deferred I/O callback being entered without any dirty pages on the pagelist (the normal case for userspace accesses). It's also possible to get in to this situation via explicit calling of fsync() when nothing has dirtied the region. Unfortunately it's not sufficient to skip over the callback when the pagelist is empty given the console driver use case, so instead the callback has to conditionalize the work for panel updates and DMA mapping depending on whether anything is resident on the pagelist or not. Reported-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de> Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'drivers/video/sh_mobile_lcdcfb.c')
-rw-r--r--drivers/video/sh_mobile_lcdcfb.c32
1 files changed, 24 insertions, 8 deletions
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index 3ad5157f989..b4b5de930cf 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -281,18 +281,34 @@ static void sh_mobile_lcdc_deferred_io(struct fb_info *info,
281 struct list_head *pagelist) 281 struct list_head *pagelist)
282{ 282{
283 struct sh_mobile_lcdc_chan *ch = info->par; 283 struct sh_mobile_lcdc_chan *ch = info->par;
284 unsigned int nr_pages;
285 284
286 /* enable clocks before accessing hardware */ 285 /* enable clocks before accessing hardware */
287 sh_mobile_lcdc_clk_on(ch->lcdc); 286 sh_mobile_lcdc_clk_on(ch->lcdc);
288 287
289 nr_pages = sh_mobile_lcdc_sginit(info, pagelist); 288 /*
290 dma_map_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); 289 * It's possible to get here without anything on the pagelist via
291 290 * sh_mobile_lcdc_deferred_io_touch() or via a userspace fsync()
292 /* trigger panel update */ 291 * invocation. In the former case, the acceleration routines are
293 lcdc_write_chan(ch, LDSM2R, 1); 292 * stepped in to when using the framebuffer console causing the
294 293 * workqueue to be scheduled without any dirty pages on the list.
295 dma_unmap_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); 294 *
295 * Despite this, a panel update is still needed given that the
296 * acceleration routines have their own methods for writing in
297 * that still need to be updated.
298 *
299 * The fsync() and empty pagelist case could be optimized for,
300 * but we don't bother, as any application exhibiting such
301 * behaviour is fundamentally broken anyways.
302 */
303 if (!list_empty(pagelist)) {
304 unsigned int nr_pages = sh_mobile_lcdc_sginit(info, pagelist);
305
306 /* trigger panel update */
307 dma_map_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
308 lcdc_write_chan(ch, LDSM2R, 1);
309 dma_unmap_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
310 } else
311 lcdc_write_chan(ch, LDSM2R, 1);
296} 312}
297 313
298static void sh_mobile_lcdc_deferred_io_touch(struct fb_info *info) 314static void sh_mobile_lcdc_deferred_io_touch(struct fb_info *info)