Android双帧缓冲 ---- 如何设置

 

Android双帧缓冲 ---- 如何设置

以下内容译自:http://blog.chinaunix.net/u3/104807/showart_2162774.html

仅供参考!

 

Patch Framebuffer Driver of PrimeCell Color LCD Controller with Double-Buffering to Support Android’s Page-Flipping

http://www.yuan.se/?p=7

 

参考网址:http://pdk.android.com/online-pdk/guide/display_drivers.html

 

一、简要介绍

Android使用SurfaceFlinger模块来对framebuffer进行操作。每一个Surface都进行双缓冲,前端buffer用于显示,后端buffer用于drawing,一旦drawing完成,Android通过改变framebuffer的yoffset值来进行页切换。根据EGLDisplaySurface初始化过程,如果framebuffer的yres_virtual大于yres,那么framebuffer直接用于双缓冲;否则,后端buffer仅仅是简单的复制到前端buffer,这将导致在buffer-swapping时延时。因此,从性能考虑,framebuffer驱动需很好的支持double-buffering。


二、打补丁
在Android SDK中,用于QEMU模拟器的goldfish帧缓冲驱动支持double-buffering。
现在进行移植Android到一块ARM RealView板,板上的LCD控制芯片是PL111 PrimeCell,AMBA兼容的SoC外设
。对比goldfish帧缓冲驱动(from Android cupcake branch source tree)和ARM LCD控制器帧缓冲驱动(from 2.6.27 vanilla kernel),可以很轻易的看出哪些是需要对ARM LCD帧缓冲驱动(kernel/drivers/video/amba-clcd.c)打上补丁,使之支持双缓冲。

1. 首先,在驱动的初始化部分,改变如下:
     fb->fb.fix.ypanstep = 1;
     fb->fb.var.yres_virtual = fb->panel->mode.yres * 2;
2. 然后,在clcdfb_ops声明中,添加一行代码:
     .fb_pan_display = clcdfb_pan_display,
    同时,定义函数clcdfb_pan_display,在其中调用clcdfb_set_start函数。

3. 在kernel/include/linux/amba/clcd.h中的clcdfb_check函数声明处,将
     var->yres_virtual = var->yres = (var->yres + 1) & ~1;
    改成
     var->yres = (var->yres + 1) & ~1;
     var->yres_virtual = var->yres * 2;
    因为check操作会将yres_virtual值改成和yres一样。

4. 增加帧的大小,在kernel/arch/arm/mach-realview/core.c, 将
     static unsigned long framesize = SZ_1M;
    改成
     static unsigned long framesize = 0x12C000;

    0x12C000 = 640 * 480 * 2 * 2 (VGA, 16bpp and double-buffer),


三、页切换(page-flipping)是如何工作的

1. 在frameworks/base/libs/ui/EGLDisplaySurface.cpp中,函数EGLDisplaySurface::swapBuffers()调用FBIOPUT_VSCREENINFO ioctl()应用。

 

2. 在kernel/drivers/video/fbmem.c中,函数fb_ioctl()响应FBIOPUT_VSCREENINFO操作,其中调用了fb_set_var(),也调用了fbops->fb_set_par()和fb_pan_display()。在fb_pan_display()中调用了fbops->fb_pan_display()。
fbops->fb_set_par()和fbops->fb_pan_display()由AMBA CLCD帧缓冲驱动提供

(kernel/drivers/video/amba-clcd.c)。

 

3. 根据惯例,在fb_set_par()中实现屏的方向和分辨率的改变,在fb_pan_display()中当xoffset或

yoffset改变时re-map帧缓冲。

   通过这种方法,实现了页切换。


四、关于clcdfb_set_par()问题

在amba clcd驱动中,clcdfb_set_par()关闭帧缓冲的电源和时钟源,设置时钟和缓冲地址,然后在打开帧缓冲。这种方式不适合Android的页切换,因为切换过程中,FBIOPUT_VSCREENINFO ioctl()会被频繁调用,这也将导致clcdfb_set_par()频繁调用。

Then the whole system is not operatable at all as a result. It’s obviously that at least the implementation of “clcdfb_set_par” is not written in a way that adheres to the conventions.

Or writting frame buffer implementation in this case still requires keeping the usage of

Android in mind.

解决方法是cache帧缓冲的设置,每一次clcdfb_set_par()被调用时,将新的帧缓冲设置跟cache中的比较,如果屏的orientation, resolution和其他改变不要求复位时钟,那就跳过;否则,cache新的设置并复位时钟。


五、补丁

 

 

diff -Naur kernel-2.6.27.orig/arch/arm/mach-realview/core.c kernel-2.6.27/arch/arm/mach-realview/core.c
--- kernel-2.6.27.orig/arch/arm/mach-realview/core.c    2009-04-29 16:16:29.000000000 +0200
+++ kernel-2.6.27/arch/arm/mach-realview/core.c    2009-04-29 16:12:26.000000000 +0200
@@ -358,7 +358,8 @@
writel(val, sys_clcd);
}

-static unsigned long framesize = SZ_1M;
+/* 640*480*2*2 (VGA, 16bpp and double-buffer) required by Android */
+static unsigned long framesize = 0x12C000;

static int realview_clcd_setup(struct clcd_fb *fb)
{
diff -Naur kernel-2.6.27.orig/drivers/video/amba-clcd.c kernel-2.6.27/drivers/video/amba-clcd.c
--- kernel-2.6.27.orig/drivers/video/amba-clcd.c    2009-04-29 16:16:58.000000000 +0200
+++ kernel-2.6.27/drivers/video/amba-clcd.c    2009-04-29 16:13:16.000000000 +0200
@@ -194,6 +194,37 @@
return ret;
}

+struct fb_var_screeninfo cached_fb_var;
+int is_fb_var_cached = 0;
+
+static int clcdfb_is_fb_changed(struct clcd_fb *fb)
+{
+ if (!is_fb_var_cached ||
+ fb->fb.var.xres != cached_fb_var.xres ||
+ fb->fb.var.yres != cached_fb_var.yres ||
+ fb->fb.var.xres_virtual != cached_fb_var.xres_virtual ||
+ fb->fb.var.yres_virtual != cached_fb_var.yres_virtual ||
+ fb->fb.var.bits_per_pixel != cached_fb_var.bits_per_pixel ||
+ fb->fb.var.grayscale != cached_fb_var.grayscale ||
+ fb->fb.var.green.length != cached_fb_var.green.length ||
+ fb->fb.var.left_margin != cached_fb_var.left_margin ||
+ fb->fb.var.right_margin != cached_fb_var.right_margin ||
+ fb->fb.var.upper_margin != cached_fb_var.upper_margin ||
+ fb->fb.var.lower_margin != cached_fb_var.lower_margin ||
+ fb->fb.var.hsync_len != cached_fb_var.hsync_len ||
+ fb->fb.var.vsync_len != cached_fb_var.vsync_len ||
+ fb->fb.var.sync != cached_fb_var.sync ||
+ fb->fb.var.rotate != cached_fb_var.rotate) {
+
+ cached_fb_var = fb->fb.var;
+ is_fb_var_cached = 1;
+
+ return 1;
+ }
+ else
+ return 0;
+}
+
static int clcdfb_set_par(struct fb_info *info)
{
struct clcd_fb *fb = to_clcd(info);
@@ -207,22 +238,25 @@
else
fb->fb.fix.visual = FB_VISUAL_TRUECOLOR;

-    fb->board->decode(fb, &regs);
+ if (clcdfb_is_fb_changed(fb)) {
+
+     fb->board->decode(fb, &regs);

-    clcdfb_disable(fb);
+     clcdfb_disable(fb);

-    writel(regs.tim0, fb->regs + CLCD_TIM0);
-    writel(regs.tim1, fb->regs + CLCD_TIM1);
-    writel(regs.tim2, fb->regs + CLCD_TIM2);
-    writel(regs.tim3, fb->regs + CLCD_TIM3);
+     writel(regs.tim0, fb->regs + CLCD_TIM0);
+     writel(regs.tim1, fb->regs + CLCD_TIM1);
+     writel(regs.tim2, fb->regs + CLCD_TIM2);
+     writel(regs.tim3, fb->regs + CLCD_TIM3);

-    clcdfb_set_start(fb);
+     clcdfb_set_start(fb);

-    clk_set_rate(fb->clk, (1000000000 / regs.pixclock) * 1000);
+     clk_set_rate(fb->clk, (1000000000 / regs.pixclock) * 1000);

-    fb->clcd_cntl = regs.cntl;
+     fb->clcd_cntl = regs.cntl;

-    clcdfb_enable(fb, regs.cntl);
+     clcdfb_enable(fb, regs.cntl);
+ }

#ifdef DEBUG
printk(KERN_INFO "CLCD: Registers set to/n"
@@ -289,6 +323,17 @@
return regno > 255;
}

+static int clcdfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ struct clcd_fb *fb = NULL;
+
+ info->var = *var;
+    fb = to_clcd(info);
+    clcdfb_set_start(fb);
+
+    return 0;
+}
+
/*
* Blank the screen if blank_mode != 0, else unblank. If blank == NULL
* then the caller blanks by setting the CLUT (Color Look Up Table) to all
@@ -332,6 +377,7 @@
.fb_check_var    = clcdfb_check_var,
.fb_set_par    = clcdfb_set_par,
.fb_setcolreg    = clcdfb_setcolreg,
+    .fb_pan_display = clcdfb_pan_display,
.fb_blank    = clcdfb_blank,
.fb_fillrect    = cfb_fillrect,
.fb_copyarea    = cfb_copyarea,
@@ -367,14 +413,14 @@
fb->fb.fix.type        = FB_TYPE_PACKED_PIXELS;
fb->fb.fix.type_aux    = 0;
fb->fb.fix.xpanstep    = 0;
-    fb->fb.fix.ypanstep    = 0;
+    fb->fb.fix.ypanstep    = 1;
fb->fb.fix.ywrapstep    = 0;
fb->fb.fix.accel    = FB_ACCEL_NONE;
fb->fb.var.xres        = fb->panel->mode.xres;
fb->fb.var.yres        = fb->panel->mode.yres;
fb->fb.var.xres_virtual    = fb->panel->mode.xres;
-    fb->fb.var.yres_virtual    = fb->panel->mode.yres;
+    fb->fb.var.yres_virtual    = fb->panel->mode.yres * 2;
fb->fb.var.bits_per_pixel = fb->panel->bpp;
fb->fb.var.grayscale    = fb->panel->grayscale;
fb->fb.var.pixclock    = fb->panel->mode.pixclock;
diff -Naur kernel-2.6.27.orig/include/linux/amba/clcd.h kernel-2.6.27/include/linux/amba/clcd.h
--- kernel-2.6.27.orig/include/linux/amba/clcd.h    2009-04-29 16:16:20.000000000 +0200
+++ kernel-2.6.27/include/linux/amba/clcd.h    2009-04-29 16:12:53.000000000 +0200
@@ -232,7 +232,9 @@
static inline int clcdfb_check(struct clcd_fb *fb, struct fb_var_screeninfo *var)
{
var->xres_virtual = var->xres = (var->xres + 15) & ~15;
-    var->yres_virtual = var->yres = (var->yres + 1) & ~1;
+    //var->yres_virtual = var->yres = (var->yres + 1) & ~1;
+    var->yres = (var->yres + 1) & ~1;
+    var->yres_virtual = var->yres * 2;

#define CHECK(e,l,h) (var->e < l || var->e > h)
if (CHECK(right_margin, (5+1), 256) ||    /* back porch */

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值