MSM8x25 LCD调试分析(二)

来源:互联网 发布:nginx 静态服务器缓存 编辑:程序博客网 时间:2024/05/16 11:36

一、概述

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_readfb_write用于应用层对framebuffer的读写操作,fb_mmap用于应用进程和framebuffer之间的内存映射,fb_ioctl用于应用层对framebuffer进行的一些控制操作,具体的操作会在后面的流程分析中讲到。fb_info结构中的fb_check_varfb_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),};#endifstatic 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_QSDstatic 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_QSDstatic 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的一些显示参数以及onoff接口,并将lcdcpdev结构体的next指针指向mdp device的设备结构体,即mdp devicelcdc device的父节点。

 

2.2.3 msm_fbdevice的创建

         在创建MDP device时,会去执行mdp.c中的probe函数,初始化MDP相关参数并创建msm_fb device,其next指针指向mdp device的设备结构体,即msm_fb devicemdp device的父节点。

 

2.2.4 fb0的创建

         在创建msmfb_device时,会去执行msm_fb.c中的probe函数,此接口中最重要的一个函数就是msm_fb_register,该接口会对前面讲到的fb_info结构体进行填充,设置fb_var_screenfb_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设备是用来分配和释放图形缓冲区的,这是通过调用它的成员函数allocfree来实现的。

 

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设备打开流程图

                       


	
				
		
原创粉丝点击