summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSiarhei Siamashka <siarhei.siamashka@gmail.com>2013-01-08 09:25:59 +0400
committerAlejandro Mery <amery@geeks.cl>2013-01-08 19:39:19 +0400
commit5636bd2377b2cc01b57dbe062d17d14bfff366af (patch)
treea4e1cf44445c2580a639127545fb35abbff42167
parent3358ff60249126be7bc4ec300cfbd0ea83d535a0 (diff)
downloadlinux-sunxi-5636bd2377b2cc01b57dbe062d17d14bfff366af.tar.xz
HACK: sunxi: video: change kernel framebuffer mapping to writecombine
The kernelspace mapping of framebuffer (needed for fbcon) had CPU caching enabled by some mistake. This caused glitches and artifacts in the framebuffer console, which can be easily reproduced as reported on IRC: <buZz> supereasy to reproduce; 1) type a multiword command on (fbdev) console 2) ctrl-left a word 3) delete its first character 4) tada, sticky pixels The solution is to change the kernel framebuffer memory mapping to writecombine, just like it is done for the userspace mapping. This patch is definitely *not* a clean solution. It is cutting some corners by doing exactly what was NAK'ed by the ARM kernel maintainers: http://lists.infradead.org/pipermail/linux-arm-kernel/2011-April/048844.html http://lists.infradead.org/pipermail/linux-arm-kernel/2011-June/052233.html The ioremap restriction (intended to prevent creating aliased mappings with different memory attributes) has been introduced with 309caa9cc6ff39d2, later relaxed in 06c10884486a63a1, and finally re-introduced again in 67cfa23ac9df810d. Even though this patch is not perfect, it can be used as a stopgap solution. = changes in v2 revision: Fix for the problems with disp module loading/unloading in dual-monitor configuration. Added debugging messages and fallback code for the case if ioremap_wc() fails. Signed-off-by: Siarhei Siamashka <siarhei.siamashka@gmail.com>
-rw-r--r--arch/arm/mm/ioremap.c2
-rw-r--r--drivers/video/sunxi/disp/dev_fb.c31
-rw-r--r--drivers/video/sunxi/disp/dev_fb.h2
3 files changed, 28 insertions, 7 deletions
diff --git a/arch/arm/mm/ioremap.c b/arch/arm/mm/ioremap.c
index ab506272b2d3..fb0ddb08e7e7 100644
--- a/arch/arm/mm/ioremap.c
+++ b/arch/arm/mm/ioremap.c
@@ -201,11 +201,13 @@ void __iomem * __arm_ioremap_pfn_caller(unsigned long pfn,
if (pfn >= 0x100000 && (__pfn_to_phys(pfn) & ~SUPERSECTION_MASK))
return NULL;
+#if 0 /* HACK - do allow RAM to be mapped, the problems are a bit overrated */
/*
* Don't allow RAM to be mapped - this causes problems with ARMv6+
*/
if (WARN_ON(pfn_valid(pfn)))
return NULL;
+#endif
type = get_mem_type(mtype);
if (!type)
diff --git a/drivers/video/sunxi/disp/dev_fb.c b/drivers/video/sunxi/disp/dev_fb.c
index 66a89d013570..15531a76b25d 100644
--- a/drivers/video/sunxi/disp/dev_fb.c
+++ b/drivers/video/sunxi/disp/dev_fb.c
@@ -441,7 +441,7 @@ fb_draw_gray_pictures(__u32 base, __u32 width, __u32 height,
}
#endif /* UNUSED */
-static int __init Fb_map_video_memory(struct fb_info *info)
+static int __init Fb_map_video_memory(__u32 fb_id, struct fb_info *info)
{
#ifndef CONFIG_FB_SUNXI_RESERVED_MEM
unsigned map_size = PAGE_ALIGN(info->fix.smem_len);
@@ -460,8 +460,18 @@ static int __init Fb_map_video_memory(struct fb_info *info)
return -ENOMEM;
}
#else
- info->screen_base = (char __iomem *)disp_malloc(info->fix.smem_len);
- info->fix.smem_start = (unsigned long)__pa(info->screen_base);
+ g_fbi.malloc_screen_base[fb_id] = disp_malloc(info->fix.smem_len);
+ info->fix.smem_start = (unsigned long)
+ __pa(g_fbi.malloc_screen_base[fb_id]);
+ info->screen_base = ioremap_wc(info->fix.smem_start,
+ info->fix.smem_len);
+ __inf("Fb_map_video_memory: fb_id=%d, disp_malloc=%p, ioremap_wc=%p\n",
+ fb_id, g_fbi.malloc_screen_base[fb_id], info->screen_base);
+ if (!info->screen_base) {
+ __wrn("ioremap_wc() failed, falling back to the existing "
+ "cached mapping\n");
+ info->screen_base = g_fbi.malloc_screen_base[fb_id];
+ }
memset_io(info->screen_base, 0, info->fix.smem_len);
__inf("Fb_map_video_memory, pa=0x%08lx size:0x%x\n",
@@ -471,14 +481,21 @@ static int __init Fb_map_video_memory(struct fb_info *info)
#endif
}
-static inline void Fb_unmap_video_memory(struct fb_info *info)
+static inline void Fb_unmap_video_memory(__u32 fb_id, struct fb_info *info)
{
#ifndef CONFIG_FB_SUNXI_RESERVED_MEM
unsigned map_size = PAGE_ALIGN(info->fix.smem_len);
free_pages((unsigned long)info->screen_base, get_order(map_size));
#else
- disp_free((void __kernel __force *) info->screen_base);
+ if ((void *)info->screen_base != g_fbi.malloc_screen_base[fb_id]) {
+ __inf("Fb_unmap_video_memory: fb_id=%d, iounmap(%p)\n",
+ fb_id, info->screen_base);
+ iounmap(info->screen_base);
+ }
+ __inf("Fb_unmap_video_memory: fb_id=%d, disp_free(%p)\n",
+ fb_id, g_fbi.malloc_screen_base[fb_id]);
+ disp_free(g_fbi.malloc_screen_base[fb_id]);
#endif
}
@@ -1380,7 +1397,7 @@ __s32 Display_Fb_Request(__u32 fb_id, __disp_fb_create_para_t * fb_para)
(fb_para->width * info->var.bits_per_pixel) >> 3;
info->fix.smem_len =
info->fix.line_length * fb_para->height * fb_para->buffer_num;
- Fb_map_video_memory(info);
+ Fb_map_video_memory(fb_id, info);
for (sel = 0; sel < 2; sel++) {
if (((sel == 0) && (fb_para->fb_mode != FB_MODE_SCREEN1)) ||
@@ -1509,7 +1526,7 @@ __s32 Display_Fb_Release(__u32 fb_id)
g_fbi.fb_enable[fb_id] = 0;
fb_dealloc_cmap(&info->cmap);
- Fb_unmap_video_memory(info);
+ Fb_unmap_video_memory(fb_id, info);
return DIS_SUCCESS;
}
diff --git a/drivers/video/sunxi/disp/dev_fb.h b/drivers/video/sunxi/disp/dev_fb.h
index 937cafb68d09..0ccfb457de0b 100644
--- a/drivers/video/sunxi/disp/dev_fb.h
+++ b/drivers/video/sunxi/disp/dev_fb.h
@@ -59,6 +59,8 @@ typedef struct {
#ifdef CONFIG_FB_SUNXI_UMP
ump_dd_handle ump_wrapped_buffer[SUNXI_MAX_FB][2];
#endif
+ /* screen_base address, allocated with disp_malloc() */
+ void *malloc_screen_base[SUNXI_MAX_FB];
} fb_info_t;
extern fb_info_t g_fbi;