现在的位置: 首页 > 综合 > 正文

MSM8x25 LCD调试分析(二)

2013年12月05日 ⁄ 综合 ⁄ 共 19716字 ⁄ 字号 评论关闭

一、概述

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),
};
#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的一些显示参数以及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设备打开流程图

                       













































































抱歉!评论已关闭.