Android double framebuffer(how to set)

 

 

Android double framebuffer(how to set)

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

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

Brief introduction

Android uses a screen composition engine called SurfaceFlinger. It takes the input of Surface objects from each Windows, then writes the output to the frame buffer. Each Surface is double-buffered. The front buffer is taken by SurfaceFlinger for composition, while the back buffer is used to perform drawing. Once the drawing is completed, Android does a page-flipping by changing the offset value on Y axis of the frame buffer to switch them. During the initialization of EGLDisplaySurface, if the virtual resolution on the Y axis of the frame buffer is found out to be bigger than the real resolution of the Y axis, then the frame buffer is used directly for double-buffering. Otherwise, the back buffer is simply copied to the front buffer, which causes delay in buffer-swapping. Thus double-buffering would better to be supported by the frame buffer driver for the sake of performance.

Double-buffering is supported in the goldfish frame buffer driver, which is used by the QEMU-based emulator shipped with Android SDK. I happen to be working on porting Android to an ARM RealView board. The board has PL111 PrimeCell Color LCD Controller, which is AMBA compliant SoC peripheral. By comparing the source code of goldfish frame buffer driver (from Android cupcake branch source tree) and that of the ARM LCD controller frame buffer driver (from 2.6.27 vanilla kernel), one can easily figure out what should be patched in the ARM LCD frame buffer driver (kernel/drivers/video/amba-clcd.c ) to support double-buffering. Firstly, in the initialization of the driver, change to “fb->fb.fix.ypanstep = 1;” and “fb->fb.var.yres_virtual = fb->panel->mode.yres * 2;” Then in the declaration of “clcdfb_ops”, add this line of code “.fb_pan_display = clcdfb_pan_display,”. Also define this new function “clcdfb_pan_display” by putting an invocation to the function “clcdfb_set_start”. In the definition of the function “clcdfb_check” in kernel/include/linux/amba/clcd.h, change from “var->yres_virtual = var->yres = (var->yres + 1) & ~1;” to “var->yres = (var->yres + 1) & ~1; var->yres_virtual = var->yres * 2;” since the checking happens to reset the value of yres_virtual to that of yres. One more step required is to increase the frame size to needed. In kernel/arch/arm/mach-realview/core.c, change from “static unsigned long framesize = SZ_1M;” to “static unsigned long framesize = 0×12C000;”. 0×12C000 is the value of 640 * 480 * 2 * 2 (VGA, 16bpp and double-buffer), because VGA is the largest display resolution that is provided by PL111 and can still support double-buffering due to the limited display memory.

How page-flipping works

- In frameworks/base/libs/ui/EGLDisplaySurface.cpp, function “EGLDisplaySurface::swapBuffers()” does the real work by calling ioctl of FBIOPUT_VSCREENINFO
- In “kernel/drivers/video/fbmem.c”, function “fb_ioctl” handles over the work in “case FBIOPUT_VSCREENINFO:”, then “fb_set_var” is invoked. In that function, both “fbops->fb_set_par” and “fb_pan_display” are invoked. Similarly, in “fb_pan_display”, “fbops->fb_pan_display” is also invoked. “fbops->fb_set_par” and “fbops->fb_pan_display” mentioned above are provided by the amba clcd frame buffer driver in kernel/drivers/video/amba-clcd.c.
- According to the conventions, “fb_set_par” implementation in the frame buffer driver is supposed to react to possible changes of screen orientation and resolution. And “fb_pan_display” is supposed to re-map the frame buffer as the offset changes in X or Y axis. In this way, page-flipping is properly handled.

Problem of “clcdfb_set_par”

In “clcdfb_set_par” of the current amba clcd implementation, it turns off the power and clock source of the frame buffer, sets clocks and the buffer base, then it turns on the frame buffer. This is not applied to Android because the page-flipping gonna invoke FBIOPUT_VSCREENINFO ioctl so often that “clcdfb_set_par” is going to be invoked a lot as well. 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.

The solution to that is to cache the frame buffer settings. Each time “clcd_set_par” is invoked, the new frame buffer settings are compared with the cached ones. If the screen orientation, resolution and other changes that will require reset of clock don’t happen, then simply skip any operations; otherwise, the new frame buffer settings are cached and the clock is reseted.

The actual patch

The patch to the said files above is illustrated below:

 

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 */


 

 原文地址 http://www.yuan.se/?p=7
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android framebuffer是指在Android系统中用于显示图像的一种技术。它使用framebuffer设备文件来管理显示屏的像素数据,并通过SurfaceFlinger作为屏幕合成引擎将图像写入到framebuffer中。在Android中,framebuffer设备文件通常位于/dev/graphics/fb0或/dev/graphics/fb1等路径下。打开可用的framebuffer设备是操作framebuffer的主要步骤之一。 SurfaceFlinger负责管理来自各个窗口的Surface对象,并将它们写入到framebuffer中。它使用前buffer来合成图像,后buffer来绘制图像。一旦绘制完成,Android通过页翻转操作,交换Y轴坐标的偏移量,选择不同的buffer。如果虚拟Y轴分辨率大于实际Y轴分辨率,说明framebuffer可以直接使用双缓冲机制。否则,后buffer需要复制到前buffer,这可能导致页交换延迟。为了提高系统性能,framebuffer驱动最好提供双缓冲机制。 总结来说,Android framebuffer是一种用于显示图像的技术,在Android系统中使用framebuffer设备文件来管理显示屏的像素数据,并通过SurfaceFlinger作为屏幕合成引擎将图像写入到framebuffer中。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Android Framebuffer介绍及使用](https://blog.csdn.net/wx_962464/article/details/77943919)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值