一、概述
1.1 简介
本文档主要包括LCD模块的驱动流程分析、Framebuffer相关知识、Gralloc等相关内容,以及LCD调试的一些经验和相关bug的分析和讲解。
1.2 开发环境
Android:4.1.2
Ubuntu:需要 10.04以及之后的版本
Gcc: 4.4.7 toolchain
1.3 硬件平台
Msm8x25,pmic(pm8029)
1.4 开发工具
VIM,SourceInsight,JTAG,ADB
闲话少说,直接进入正题了,在上一篇里写了高通平台android2.3里的kernel和bootloader(LK)里LCD驱动的移植,这一篇主要写一下在4.0里LCD驱动的移植。
二、LCD流程分析
2.1 帧缓冲
2.1.1帧缓冲概念
帧缓冲(framebuffer)是Linux系统为显示设备提供的一个接口,它将显示缓冲区抽象,屏蔽图像硬件的底层差异,允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。用户不必关系物理显示缓冲区的具体位置及存放方式,这些都由帧缓冲设备驱动本身来完成。对于帧缓冲设备而言,只要在显示缓冲区中与显示点对应的区域写入颜色值,对应的颜色会自动在屏幕上显示。帧缓冲为标准字符设备,主设备号为29,对应于/dev/fbn。
2.1.2 fb_info结构体
帧缓冲设备最关键的一个数据结构体是fb_info结构,为了便于记忆,简称FBI,这个机构体在fb.h文件中定义了。FBI中包括了关于帧缓冲设备属性和操作的完整描述,这个结构体的定义如下所示。
struct fb_info {
atomic_t count;
int node;
int flags;
struct mutex lock; /*用于open/release/ioctl的锁*/
struct mutex mm_lock; /* Lock for fb_mmap and smem_* fields */
struct fb_var_screeninfo var; /* 用户可修改的显示控制参数,包括像素比特数和屏幕分辨率,比如xres、yres、bits_per_pixel等 */
struct fb_fix_screeninfo fix; /* 用户不可修改的显示控制参数 比如物理地址、长度等 */
struct fb_monspecs monspecs; /* Current Monitor specs */
struct work_struct queue; /* Framebuffer event queue */
struct fb_pixmap pixmap; /* Image hardware mapper */
struct fb_pixmap sprite; /* Cursor hardware mapper */
struct fb_cmap cmap; /* Current cmap */
struct list_head modelist; /* mode list */
struct fb_videomode *mode; /* current mode */
#ifdef CONFIG_FB_BACKLIGHT
/* assigned backlight device */
/* set before framebuffer registration,
remove after unregister */
struct backlight_device *bl_dev;
/* Backlight level curve */
struct mutex bl_curve_mutex;
u8 bl_curve[FB_BACKLIGHT_LEVELS];
#endif
#ifdef CONFIG_FB_DEFERRED_IO
struct delayed_work deferred_work;
struct fb_deferred_io *fbdefio;
#endif
struct fb_ops *fbops;/* 帧缓冲操作 */
struct device *device; /* This is the parent */
struct device *dev; /* This is this fb device */
int class_flag; /* private sysfs flags */
#ifdef CONFIG_FB_TILEBLITTING
struct fb_tile_ops *tileops; /* Tile Blitting */
#endif
char __iomem *screen_base; /* Virtual address */
unsigned long screen_size; /* Amount of ioremapped VRAM or 0虚拟内存大小 */
void *pseudo_palette; /* Fake palette of 16 colors伪16色颜色表 */
#define FBINFO_STATE_RUNNING 0
#define FBINFO_STATE_SUSPENDED 1
u32 state; /* Hardware state i.e suspend */
void *fbcon_par; /* fbcon use-only private area */
/* From here on everything is device dependent */
void *par;
/* we need the PCI or similar aperture base/size not
smem_start/size as smem_start may just be an object
allocated inside the aperture so may not actually overlap */
struct apertures_struct {
unsigned int count;
struct aperture {
resource_size_t base;
resource_size_t size;
} ranges[0];
} *apertures;
};
其中fb_ops、fb_var_screeninfo和fb_fix_screeninfo这三个结构极为重要。FBI的成员变量fbops为指向底层操作的函数指针,这些函数是需要驱动程序开发人员编写的,不过高通平台已经定义好这些接口了,我们只需了解下这些接口的功能,不必修改。
fb_var_screeninfo记录用户可修改的显示控制参数,包括屏幕分辨率和每个像素点的比特数。fb_var_screeninfo中的xres定义屏幕一行有多少个点,yres定义屏幕一列有多少个点,bits_per_pixel定义每个点用多少个字节表示。而fb_fix_screeninfo中记录用户不能修改的显示控制器的参数,如屏幕缓冲区的物理地址、长度。当对帧缓冲设备进行映射操作时,就是从fb_fix_screeninfo中取得缓冲区物理地址的。上述结构体都需要在驱动程序中初始化和设置,在后面的流程分析中会作具体的讲解。
fb_ops结构体
struct fb_ops { /* open/release and usage marking */ struct module *owner; int (*fb_open)(struct fb_info *info, int user); int (*fb_release)(struct fb_info *info, int user); /* For framebuffers with strange non linear layouts or that do not * work with normal memory mapped access */ ssize_t (*fb_read)(struct fb_info *info, char __user *buf, size_t count, loff_t *ppos); ssize_t (*fb_write)(struct fb_info *info, const char __user *buf, size_t count, loff_t *ppos); /* checks var and eventually tweaks it to something supported, * DO NOT MODIFY PAR */ int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info); /* set the video mode according to info->var */ int (*fb_set_par)(struct fb_info *info); /* set color register */ int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *info); /* set color registers in batch */ int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info); /* blank display */ int (*fb_blank)(int blank, struct fb_info *info); /* pan display */ int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info); /* Draws a rectangle */ void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect); /* Copy data from area to another */ void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region); /* Draws a image to the display */ void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image); /* Draws cursor */ int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor); /* Rotates the display */ void (*fb_rotate)(struct fb_info *info, int angle); /* wait for blit idle, optional */ int (*fb_sync)(struct fb_info *info); /* perform fb specific ioctl (optional) */ int (*fb_ioctl)(struct fb_info *info, unsigned int cmd, unsigned long arg); /* Handle 32bit compat ioctl (optional) */ int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd, unsigned long arg); /* perform fb specific mmap */ int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma); /* get capability given var */ void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps, struct fb_var_screeninfo *var); /* teardown any resources to do with this framebuffer */ void (*fb_destroy)(struct fb_info *info); /* called at KDB enter and leave time to prepare the console */ int (*fb_debug_enter)(struct fb_info *info); int (*fb_debug_leave)(struct fb_info *info); };
文件操作结构体
static const struct file_operations fb_fops = { .owner = THIS_MODULE, .read = fb_read, .write = fb_write, .unlocked_ioctl = fb_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = fb_compat_ioctl, #endif .mmap = fb_mmap, .open = fb_open, .release = fb_release, #ifdef HAVE_ARCH_FB_UNMAPPED_AREA .get_unmapped_area = get_fb_unmapped_area, #endif #ifdef CONFIG_FB_DEFERRED_IO .fsync = fb_deferred_io_fsync, #endif .llseek = default_llseek, };
2.1.3 帧缓冲设备驱动结构
从上图可以看出,注册framebuffer时需要用到fb_info结构体,fb_info结构体又包含了fb_ops结构体,而fb_ops结构体中的fb_read、fb_write用于应用层对framebuffer的读写操作,fb_mmap用于应用进程和framebuffer之间的内存映射,fb_ioctl用于应用层对framebuffer进行的一些控制操作,具体的操作会在后面的流程分析中讲到。fb_info结构中的fb_check_var和fb_set_par分别用于获取和设置framebuffer的显示参数。
2.2 LCD driver的注册以及framebuffer的建立
在分析LCD的流程时,从底层往上一层层的分析,这样更容易理解驱动层每一层的作用。
需要完成四个工作:
1、申请FBI结构体内存空间,初始化FBI结构体中fb_var_screeninfo var、fb_fix_screeninfo fix。
2、由LCD特点,完成LCD控制器硬件初始化
3、申请帧缓冲设备的显示缓冲区空间
4、注册帧缓冲设备
2.2.1 LCD驱动的注册以及LCDC device的创建
帧缓冲设备驱动的模块加载/卸载以及平台驱动的探测和移除函数
static struct spi_driver lcdc_toshiba_spi_driver = { .driver = { .name = LCDC_TOSHIBA_SPI_DEVICE_NAME, .owner = THIS_MODULE, }, .probe = lcdc_toshiba_spi_probe, .remove = __devexit_p(lcdc_toshiba_spi_remove), }; #endif static struct platform_driver this_driver = { .probe = toshiba_probe, .driver = { .name = "lcdc_toshiba_wvga", }, }; static struct msm_fb_panel_data toshiba_panel_data = { .on = lcdc_toshiba_panel_on, .off = lcdc_toshiba_panel_off, .set_backlight = lcdc_toshiba_set_backlight, }; static struct platform_device this_device = { .name = "lcdc_toshiba_wvga", .id = 1, .dev = { .platform_data = &toshiba_panel_data, } }; static int __devinit toshiba_probe(struct platform_device *pdev) { if (pdev->id == 0) { lcdc_toshiba_pdata = pdev->dev.platform_data; #ifndef CONFIG_SPI_QSD spi_pin_assign(); #endif return 0; } msm_fb_add_device(pdev); return 0; } #ifdef CONFIG_SPI_QSD static int __devinit lcdc_toshiba_spi_probe(struct spi_device *spi) { lcdc_toshiba_spi_client = spi; lcdc_toshiba_spi_client->bits_per_word = 32; return 0; #ifdef CONFIG_SPI_QSD static int __devinit lcdc_toshiba_spi_probe(struct spi_device *spi) { lcdc_toshiba_spi_client = spi; lcdc_toshiba_spi_client->bits_per_word = 32; return 0; } static int __devexit lcdc_toshiba_spi_remove(struct spi_device *spi) { lcdc_toshiba_spi_client = NULL; return 0; }
在注册LCD驱动前需要设置一些参数,包括分辨率大小、bpp、像素时钟频率等。
pinfo = &toshiba_panel_data.panel_info;
pinfo->xres = 480;
pinfo->yres = 800;
MSM_FB_SINGLE_MODE_PANEL(pinfo);
pinfo->type = LCDC_PANEL;
pinfo->pdest = DISPLAY_1;
pinfo->wait_cycle = 0;
pinfo->bpp = 18;
pinfo->fb_num = 2;
/* 30Mhz mdp_lcdc_pclk and mdp_lcdc_pad_pcl */
pinfo->clk_rate = 30720000;
pinfo->bl_max = 15;
pinfo->bl_min = 1;
pinfo->lcdc.h_back_porch = 184; /* hsw = 8 + hbp=184 */
pinfo->lcdc.h_front_porch = 4;
pinfo->lcdc.h_pulse_width = 8;
pinfo->lcdc.v_back_porch = 2; /* vsw=1 + vbp = 2 */
pinfo->lcdc.v_front_porch = 3;
pinfo->lcdc.v_pulse_width = 1;
pinfo->lcdc.border_clr = 0; /* blk */
pinfo->lcdc.underflow_clr = 0xff; /* blue */
pinfo->lcdc.hsync_skew = 0;
在toshiba_probe函数中会执行msm_fb_add_device(pdev)这个接口,这个接口在msm_fb.c中定义,这个接口的功能就是传递LCD
driver的相关参数并根据LCD的类型(这里假设是RGB接口)创建一个LCDC
device,此外还会创建一个framebuffer结构体,并将其添加到全局的framebuffer列表fb_list里面。
2.2.2 MDP device的创建
在根据LCD的类型创建新设备时,会去执行lcdc.c中的probe函数,这个接口会创建一个mdp
device,然后设置mdp device的一些显示参数以及on和off接口,并将lcdc的pdev结构体的next指针指向mdp
device的设备结构体,即mdp device是lcdc device的父节点。
2.2.3 msm_fbdevice的创建
在创建MDP device时,会去执行mdp.c中的probe函数,初始化MDP相关参数并创建msm_fb
device,其next指针指向mdp device的设备结构体,即msm_fb device是mdp device的父节点。
2.2.4 fb0的创建
在创建msmfb_device时,会去执行msm_fb.c中的probe函数,此接口中最重要的一个函数就是msm_fb_register,该接口会对前面讲到的fb_info结构体进行填充,设置fb_var_screen和fb_fix_screen结构体的显示参数,包括图像显示格式、可见分辨率、虚拟分辨率、红绿蓝色域的偏移、帧率、虚拟基地址等等一些参数,并将高通平台自带的fb_ops接口填充到fb_info结构体里面,然后调用register_framebuffer来创建fb0
device。至此,fb0的建立已经完成,应用层可以对fb0节点的控制来操作framebuffer缓冲区。
2.2.5 fb设备创建流程图
从上图可清楚的看出从注册LCD驱动到创建framebuffer的流程。
2.3 fb设备的打开及framebuffer的使用
上面分析从LCD驱动的注册到fb0建立的流程,那么fb0创建好后,怎么使用它呢?现在来分析下打开fb0操作framebuffer的流程。
2.3.1 gralloc设备的打开过程
显示模块在初始化时会去通过hw_get_module加载gralloc库,该库存在于/system/lib/hw中,在加载成功gralloc库后,会调用framebuffer_open接口,这个接口最终会被指向framebuffer.cpp文件中的fb_device_open函数。执行fb_device_open时,首先会去打开先前已经加载成功的gralloc库。
Gralloc模块在在文件hardware/libhardware/include/hardware/gralloc.h中定义了一个帮助函数gralloc_open,用来打开gralloc设备。gralloc_open最终会指向gralloc.cpp中的gralloc_device_open函数。
int gralloc_device_open(const hw_module_t* module, const char* name,
hw_device_t** device)
{
int status = -EINVAL;
if (!strcmp(name, GRALLOC_HARDWARE_GPU0)) {
gralloc_context_t *dev;
dev = (gralloc_context_t*)malloc(sizeof(*dev));
/* initialize our state here */
memset(dev, 0, sizeof(*dev));
/* initialize the procs */
dev->device.common.tag = HARDWARE_DEVICE_TAG;
dev->device.common.version = 0;
dev->device.common.module = const_cast<hw_module_t*>(module);
dev->device.common.close = gralloc_close;
dev->device.alloc = gralloc_alloc;
dev->device.free = gralloc_free;
*device = &dev->device.common;
status = 0;
} else {
status = fb_device_open(module, name, device);
}
return status;
}
这个函数主要是用来创建一个gralloc_context_t结构体,并且对它的成员变量device进行初始化。结构体gralloc_context_t的成员变量device的类型为gralloc_device_t,它用来描述一个gralloc设备。gralloc设备是用来分配和释放图形缓冲区的,这是通过调用它的成员函数alloc和free来实现的。
2.3.2 fb设备的打开过程
在打开gralloc设备后,会去执行fb_device_open来打开fb设备。fb设备使用结构体framebuffer_device_t来描述。结构体framebuffer_device_t是用来描述系统帧缓冲区的信息,它定义在文hardware/libhardware/include/hardware/fb.h。
typedef struct framebuffer_device_t {
struct hw_device_t common;
/* flags describing some attributes of the framebuffer */
const uint32_t flags;/*记录系统帧缓冲区的标志*/
/* dimensions of the framebuffer in pixels */
const uint32_t width;/*显示屏宽度*/
const uint32_t height;/*显示屏高度*/
/* frambuffer stride in pixels */
const int stride;/*显示屏每行像素点数目*/
/* framebuffer pixel format */
const int format;/帧缓冲区格式/
/* resolution of the framebuffer's display panel in pixel per inch*/
const float xdpi;/*显示屏在宽度上的密度*/
const float ydpi;/*显示屏在高度上的密度*/
/* framebuffer's display panel refresh rate in frames per second */
const float fps;/*描述设备显示屏的刷新频率,它的单位是帧每秒 */
/* min swap interval supported by this framebuffer */
const int minSwapInterval;
/* max swap interval supported by this framebuffer */
const int maxSwapInterval;
int reserved[8];
/*
* requests a specific swap-interval (same definition than EGL)
*
* Returns 0 on success or -errno on error.
*/
int (*setSwapInterval)(struct framebuffer_device_t* window,
int interval);
int (*setUpdateRect)(struct framebuffer_device_t* window,
int left, int top, int width, int height);
int (*post)(struct framebuffer_device_t* dev, buffer_handle_t buffer);
int (*compositionComplete)(struct framebuffer_device_t* dev);
#if defined(OSP_GRALLOC_READ) /* */
int (*read)(struct framebuffer_device_t* dev, buffer_handle_t buffer);
#endif
void (*dump)(struct framebuffer_device_t* dev, char *buff, int buff_len);
/*
* (*enableScreen)() is used to either blank (enable=0) or
* unblank (enable=1) the screen this framebuffer is attached to.
*
* Returns 0 on success or -errno on error.
*/
int (*enableScreen)(struct framebuffer_device_t* dev, int enable);
/*
* Notification to gralloc that the current display rotation angle changed.
*/
void (*setRotation)(struct framebuffer_device_t* dev, int rotation);
void* reserved_proc[6];
} framebuffer_device_t;
Gralloc模块在在文件hardware/libhardware/include/hardware/fb.h中定义了一个帮助函数framebuffer_open,用来打开fb设备。这个接口最终会被指向hardware/qcom/display/libgralloc/framebuffer.cpp文件中的fb_device_open函数。
int fb_device_open(hw_module_t const* module, const char* name,
hw_device_t** device)
{
int status = -EINVAL;
if (!strcmp(name, GRALLOC_HARDWARE_FB0)) {
alloc_device_t* gralloc_device;
status = gralloc_open(module, &gralloc_device);
if (status < 0)
return status;
/* initialize our state here */
fb_context_t *dev = (fb_context_t*)malloc(sizeof(*dev));
memset(dev, 0, sizeof(*dev));
/* initialize the procs */
dev->device.common.tag = HARDWARE_DEVICE_TAG;
dev->device.common.version = 0;
dev->device.common.module = const_cast<hw_module_t*>(module);
dev->device.common.close = fb_close;
dev->device.setSwapInterval = fb_setSwapInterval;
dev->device.post = fb_post;
dev->device.setUpdateRect = 0;
dev->device.compositionComplete = fb_compositionComplete;
private_module_t* m = (private_module_t*)module;
status = mapFrameBuffer(m);
if (status >= 0) {
int stride = m->finfo.line_length / (m->info.bits_per_pixel >> 3);
const_cast<uint32_t&>(dev->device.flags) = 0;
const_cast<uint32_t&>(dev->device.width) = m->info.xres;
const_cast<uint32_t&>(dev->device.height) = m->info.yres;
const_cast<int&>(dev->device.stride) = stride;
const_cast<int&>(dev->device.format) = m->fbFormat;
const_cast<float&>(dev->device.xdpi) = m->xdpi;
const_cast<float&>(dev->device.ydpi) = m->ydpi;
const_cast<float&>(dev->device.fps) = m->fps;
const_cast<int&>(dev->device.minSwapInterval) =
PRIV_MIN_SWAP_INTERVAL;
const_cast<int&>(dev->device.maxSwapInterval) =
PRIV_MAX_SWAP_INTERVAL;
const_cast<int&>(dev->device.numFramebuffers) = m->numBuffers;
if (m->finfo.reserved[0] == 0x5444 &&
m->finfo.reserved[1] == 0x5055) {
dev->device.setUpdateRect = fb_setUpdateRect;
ALOGD("UPDATE_ON_DEMAND supported");
}
*device = &dev->device.common;
}
// Close the gralloc module
gralloc_close(gralloc_device);
}
return status;
fb_device_open用来创建一个fb_context_t结构体,并且对它的成员变量device进行初始化。结构体fb_context_t的成员变量device的类型为framebuffer_device_t,前面提到,它是用来描述fb设备的。fb设备主要是用来渲染图形缓冲区的,这是通过调用它的成员函数post来实现的。从这里可以看出,函数fb_device_open所打开的fb设备的成员函数post被设置为Gralloc模块中的函数fb_post.。
函数fb_device_open在打开fb设备的过程中,会调用另外一个函数mapFrameBuffer来获得系统帧缓冲区的信息,并且将这些信息保存在参数module所描述的一个private_module_t结构体的各个成员变量中。有了系统帧缓冲区的信息之后,函数fb_device_open接下来就可以对前面所打开的一个fb设备的各个成员变量进行初始化。这些成员变量的含义可以参考前面对结构体framebuffer_device_t的介绍。函数mapFrameBuffer除了用来获得系统帧缓冲区的信息之外,还会将系统帧缓冲区映射到当前进程的地址空间来。
函数mapFrameBuffer实现在文件hardware/libhardware/modules/gralloc/framebuffer.cpp,如下所示:
static int mapFrameBuffer(struct private_module_t* module)
{
pthread_mutex_lock(&module->lock);
int err = mapFrameBufferLocked(module);
pthread_mutex_unlock(&module->lock);
return err;
}
这个函数调用了同一个文件中的另外一个函数mapFrameBufferLocked来初始化参数module以及将系统帧缓冲区映射到当前进程的地址空间来。
int mapFrameBufferLocked(struct private_module_t* module)
{
// already initialized...
if (module->framebuffer) {
return 0;
}
char const * const device_template[] = {
"/dev/graphics/fb%u",
"/dev/fb%u",
0 };
int fd = -1;
int i=0;
char name[64];
char property[PROPERTY_VALUE_MAX];
首先在系统中检查是否存在设备文件/dev/graphics/fb0或者/dev/fb0。如果存在的话,那么就调用函数open来打开它,并且将得到的文件描述符保存在变量fd中。这样,接下来函数mapFrameBufferLocked就可以通过文件描述符fd来与内核中的帧缓冲区驱动程序交互
while ((fd==-1) && device_template[i]) { snprintf(name, 64, device_template[i], 0); fd = open(name, O_RDWR, 0); i++; } if (fd < 0) return -errno;
下面代码通过IO控制命令FBIOGET_FSCREENINFO来获得系统帧缓冲区的信息,并存在fb_fix_screeninfo结构体finfo下
struct fb_fix_screeninfo finfo; if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1) return -errno;
下面代码通过IO控制命令FBIOGET_VSCREENINFO来获得系统帧缓冲区的信息,并存在fb_fix_screeninfo结构体info下
struct fb_var_screeninfo info; if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1) return -errno;
设置设备显示屏的虚拟分辨率,结构体fb_var_screeninfo的成员变量xres和yres用来描述显示屏的可视分辨率,而成员变量xres_virtual和yres_virtual用来描述显示屏的虚拟分辨率。这里保持可视分辨率以及虚拟分辨率的宽度值不变,而将虚拟分辨率的高度值设置为可视分辨率的高度值的NUM_BUF
FERS倍。NUM_BUFFERS是一个宏,它的值被定义为2。这样,我们就可以将系统帧缓冲区划分为两个图形缓冲区来使用,即可以通过硬件来实现双
缓冲技术。在结构体fb_var_screeninfo中,与显示屏的可视分辨率和虚拟分辨率相关的另外两个成员变量是xoffset和yoffset,它们用来告诉帧缓冲区当
前要渲染的图形缓冲区是哪一个
info.reserved[0] = 0; info.reserved[1] = 0; info.reserved[2] = 0; info.xoffset = 0; info.yoffset = 0; info.activate = FB_ACTIVATE_NOW; /* Interpretation of offset for color fields: All offsets are from the * right, inside a "pixel" value, which is exactly 'bits_per_pixel' wide * (means: you can use the offset as right argument to <<). A pixel * afterwards is a bit stream and is written to video memory as that * unmodified. This implies big-endian byte order if bits_per_pixel is * greater than 8. */ if(info.bits_per_pixel == 32) { /* * Explicitly request RGBA_8888 */ info.bits_per_pixel = 32; info.red.offset = 24; info.red.length = 8; info.green.offset = 16; info.green.length = 8; info.blue.offset = 8; info.blue.length = 8; info.transp.offset = 0; info.transp.length = 8; /* Note: the GL driver does not have a r=8 g=8 b=8 a=0 config, so if we * do not use the MDP for composition (i.e. hw composition == 0), ask * for RGBA instead of RGBX. */ if (property_get("debug.sf.hw", property, NULL) > 0 && atoi(property) == 0) module->fbFormat = HAL_PIXEL_FORMAT_RGBX_8888; else if(property_get("debug.composition.type", property, NULL) > 0 && (strncmp(property, "mdp", 3) == 0)) module->fbFormat = HAL_PIXEL_FORMAT_RGBX_8888; else module->fbFormat = HAL_PIXEL_FORMAT_RGBA_8888; } else { /* * Explicitly request 5/6/5 */ info.bits_per_pixel = 16; info.red.offset = 11; info.red.length = 5; info.green.offset = 5; info.green.length = 6; info.blue.offset = 0; info.blue.length = 5; info.transp.offset = 0; info.transp.length = 0; module->fbFormat = HAL_PIXEL_FORMAT_RGB_565; } //adreno needs 4k aligned offsets. Max hole size is 4096-1 int size = roundUpToPageSize(info.yres * info.xres * (info.bits_per_pixel/8)); /* * Request NUM_BUFFERS screens (at least 2 for page flipping) */ int numberOfBuffers = (int)(finfo.smem_len/size); ALOGV("num supported framebuffers in kernel = %d", numberOfBuffers); if (property_get("debug.gr.numframebuffers", property, NULL) > 0) { int num = atoi(property); if ((num >= NUM_FRAMEBUFFERS_MIN) && (num <= NUM_FRAMEBUFFERS_MAX)) { numberOfBuffers = num; } } if (numberOfBuffers > NUM_FRAMEBUFFERS_MAX) numberOfBuffers = NUM_FRAMEBUFFERS_MAX; ALOGV("We support %d buffers", numberOfBuffers); //consider the included hole by 4k alignment uint32_t line_length = (info.xres * info.bits_per_pixel / 8); info.yres_virtual = (size * numberOfBuffers) / line_len uint32_t flags = PAGE_FLIP; if (ioctl(fd, FBIOPUT_VSCREENINFO, &info) == -1) { info.yres_virtual = size / line_length; flags &= ~PAGE_FLIP; ALOGW("FBIOPUT_VSCREENINFO failed, page flipping not supported"); } if (info.yres_virtual < ((size * 2) / line_length) ) { // we need at least 2 for page-flipping info.yres_virtual = size / line_length; flags &= ~PAGE_FLIP; ALOGW("page flipping not supported (yres_virtual=%d, requested=%d)", info.yres_virtual, info.yres*2); } if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1) return -errno; if (int(info.width) <= 0 || int(info.height) <= 0) { // the driver doesn't return that information // default to 160 dpi info.width = ((info.xres * 25.4f)/160.0f + 0.5f); info.height = ((info.yres * 25.4f)/160.0f + 0.5f); } float xdpi = (info.xres * 25.4f) / info.width; float ydpi = (info.yres * 25.4f) / info.height; //The reserved[4] field is used to store FPS by the driver. float fps = info.reserved[4];
通过IO控制命令FBIOGET_FSCREENINFO来获得系统帧缓冲区的固定信息,并且保存在fb_fix_screeninfo结构体finfo中,接下来再使用fb_fix_screeninfo结构体finfo以及前面得到的系统帧缓冲区的其它信息来初始化参数module所描述的一个private_module_t结构体
module->flags = flags; module->info = info; module->finfo = finfo; module->xdpi = xdpi; module->ydpi = ydpi; module->fps = fps; module->swapInterval = 1;
int err; module->numBuffers = info.yres_virtual / info.yres;/*计算系统帧缓冲区可以换分出多少个缓冲区使用*/ module->bufferMask = 0; //adreno needs page aligned offsets. Align the fbsize to pagesize. size_t fbSize = roundUpToPageSize(finfo.line_length * info.yres)* module->numBuffers; module->framebuffer = new private_handle_t(fd, fbSize, private_handle_t::PRIV_FLAGS_USES_PMEM, BUFFER_TYPE_UI, module->fbFormat, info.xres, info.yres);
系统帧缓冲区是通过调用函数mmap来映射到当前进程的地址空间来的。映射后得到的地址空间使用一个private_handle_t结构体来描述,这个结构体的成员变量base保存的即为系统帧缓冲区在当前进程的地址空间中的起始地址。这样,Gralloc模块以后就可以从这块地址空间中分配图形缓冲区给当前进程使用
void* vaddr = mmap(0, fbSize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (vaddr == MAP_FAILED) { ALOGE("Error mapping the framebuffer (%s)", strerror(errno)); return -errno; } module->framebuffer->base = intptr_t(vaddr); memset(vaddr, 0, fbSize); module->currentOffset = 0; module->fbPostDone = false; pthread_mutex_init(&(module->fbPostLock), NULL); pthread_cond_init(&(module->fbPostCond), NULL); module->fbPanDone = false; pthread_mutex_init(&(module->fbPanLock), NULL); pthread_cond_init(&(module->fbPanCond), NULL); return 0;
b设备的打开过程的分析完成了,系统缓冲区已经被映射到应用层的进程空间了。在fb设备被打开后,应用层就可以通过ioctl接口对底层的framebuffer进行操作了。
2.3.3 fb设备打开流程图