Android板级支持与硬件相关子系统
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

4.4 显示BSP的实现

↘4.4.1 模拟器显示系统的实现

模拟器使用的显示系统的驱动程序是goldfish的Framebuffer驱动程序,使用的硬件抽象层是默认的gralloc模块。

1.Framebuffer驱动程序

goldfish虚拟处理器的Framebuffer驱动程序在内核的路径drivers/video/goldfishfb.c中实现。这是一个标准Framebuffer的驱动程序,其实现的基础主机的显示器。

这个驱动程序的初始化工作的内容是goldfish_fb_probe,如下所示:

    static int goldfish_fb_probe(struct platform_device *pdev){
      struct goldfish_fb *fb;
      fb->fb.fix.type   = FB_TYPE_PACKED_PIXELS;
      fb->fb.fix.visual = FB_VISUAL_TRUECOLOR;
      fb->fb.fix.line_length = width * 2;                         // RGB565每个像素占用l6位,2个字节
      fb->fb.fix.accel  = FB_ACCEL_NONE;
      fb->fb.fix.ypanstep = l;
      fb->fb.var.xres       = width;                               // 实际显示区域
      fb->fb.var.yres       = height;
      fb->fb.var.xres_virtual    = width;                          // 虚拟显示区域(高度为实际的两倍)
      fb->fb.var.yres_virtual    = height * 2;
      fb->fb.var.bits_per_pixel = l6;
      fb->fb.var.activate   = FB_ACTIVATE_NOW;
      fb->fb.var.height = readl(fb->reg_base + FB_GET_PHYS_HEIGHT);// 读取虚拟寄存器
      fb->fb.var.width  = readl(fb->reg_base + FB_GET_PHYS_WIDTH);
      fb->fb.var.red.offset = ll;                                  // RGB565的颜色空间
      fb->fb.var.red.length = 5;
      fb->fb.var.green.offset = 5;
      fb->fb.var.green.length = 6;
      fb->fb.var.blue.offset = 0;
      fb->fb.var.blue.length = 5;
      framesize = width * height * 2 * 2;                       // 显示缓冲区的大小,考虑每像素大小的虚拟区域
        // 进行内存映射
      fb->fb.screen_base = dma_alloc_writecombine(&pdev->dev, framesize,
                                            &fbpaddr, GFP_KERNEL);
        // 省略部分内容
      fb->fb.fix.smem_start = fbpaddr;
      fb->fb.fix.smem_len = framesize;
      ret = fb_set_var(&fb->fb, &fb->fb.var);                      //  设置参数
        // 省略部分内容
      ret = request_irq(fb->irq, goldfish_fb_interrupt, IRQF_SHARED, pdev->name, fb);
        // 省略部分内容
      writel(FB_INT_BASE_UPDATE_DONE, fb->reg_base + FB_INT_ENABLE);
      goldfish_fb_pan_display(&fb->fb.var, &fb->fb);               // 更新显示
      ret = register_framebuffer(&fb->fb);                         // 注册驱动程序
        // 省略部分内容
    }

虽然基于主机的显示实现,但是对“硬件”的操作依然通过寄存器完成。goldfish虚拟处理器的Framebuffer驱动程序实现了RGB565的颜色空间支持,虚拟显示的y为实际显示的两倍,用于双缓冲。由于RGB565颜色格式每个像素占用两个字节,又具有双显示缓冲,因此显示缓冲区的大小为width×height×2×2。在用户空间,两个显示缓冲区的切换可以通过调用ioctl命令FBIOPAN_DISPLAY来控制。

2.默认的gralloc模块的实现

默认的gralloc模块不仅可以给模拟器实现,对于实现了标准Framebuffer驱动程序的硬件系统,也可以使用这个模块。

默认的gralloc模块实现的代码路径为:hardware/libhardware/modules/gralloc/。

根据硬件抽象层的接口特点,实现分为gralloc_module_t部分、framebuffer_device_t部分、alloc_device_t部分。其源代码如下所示。

·gralloc.cpp:实现gralloc_module_t模块和alloc_device_t设备。

·framebuffer.cpp:实现framebuffer_device_t设备。

·mapper.cpp:实现Buffer操作等函数。

默认的gralloc模块的实现如图4-3所示。

图4-3 默认的gralloc模块的实现

默认gralloc模块所使用的硬件设备只有Framebuffer驱动程序。因此,实际上不仅framebuffer_device_t设备基于Framebuffer驱动程序,而且gralloc_module_t模块中的registerBuffer接口和alloc_device_t设备中的alloc等接口都是通过间接调用Framebuffer驱动程序实现的。

gralloc.cpp中实现gralloc_module_t模块的是显示模块接口,其打开函数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)) {     // 打开alloc_device_t设备
            gralloc_context_t *dev;
            dev = (gralloc_context_t*)malloc(sizeof(*dev));
            memset(dev, 0, sizeof(*dev));
            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;        // alloc_device_t的alloc接口
            dev->device.free    = gralloc_free;         // alloc_device_t的free接口
            *device = &dev->device.common;
            status = 0;
        } else {                                        // 打开framebuffer_device_t设备
            status = fb_device_open(module, name, device);
        }
        return status;
    }

gralloc_priv.h中定义的private_module_t类型的结构体类型扩展了gralloc_module_t结构体,其中第1个成员指针base就是gralloc_module_t类型。

private_module_t结构体的实现如下所示:

    struct private_module_t HAL_MODULE_INFO_SYM = {
        base: {                                         // 来自gralloc_module_t结构体
            common: {
                tag: HARDWARE_MODULE_TAG,
                version_major: l,
                version_minor: 0,
                id: GRALLOC_HARDWARE_MODULE_ID,
                name: "Graphics Memory Allocator Module",
                author: "The Android Open Source Project",
                methods: &gralloc_module_methods
            },
            registerBuffer: gralloc_register_buffer,   // 模块的几个函数指针
            unregisterBuffer: gralloc_unregister_buffer,
            lock: gralloc_lock,
            unlock: gralloc_unlock,
        },
        framebuffer: 0,                                 // 附加成员
        flags: 0,
        numBuffers: 0,
        bufferMask: 0,
        lock: PTHREAD_MUTEX_INITIALIZER,
        currentBuffer: 0,
    };

gralloc_register_buffer、gralloc_unregister_buffer、gralloc_lock和gralloc_unlock几个函数是操作的执行者,它们在mapper.cpp中实现。

模块的register_buffer实际上是通过映射打开Framebuffer设备的文件描述符来实现的。而这个文件描述符是在framebuffer_device_t打开后才得到的。

framebuffer.cpp实现了framebuffer_device_t设备,这里使用了双缓冲的实现方式。

framebuffer_device_t设备的打开部分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);
            fb_context_t *dev = (fb_context_t*)malloc(sizeof(*dev));
            memset(dev, 0, sizeof(*dev));
            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;
            private_module_t* m = (private_module_t*)module;
            status = mapFrameBuffer(m);      // 映射framebuffer设备
            if (status >= 0) {               // 填充framebuffer_device_t设备的各个内容
                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) = HAL_PIXEL_FORMAT_RGB_565;
                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) = l;
                const_cast<int&>(dev->device.maxSwapInterval) = l;
                *device = &dev->device.common;
            }
        }
        return status;
    }

fb_device_open()的功能就是初始化了一个framebuffer_device_t设备,其中的主体部分在mapFrameBufferLocked()中实现,如下所示:

    int mapFrameBufferLocked(struct private_module_t* module)
    {
        // 省略部分内容
        char const * const device_template[] = {                       // 定义framebuffer设备
                "/dev/graphics/fb%u","/dev/fb%u",0 };
        int fd = -l;
        int i=0;
        char name[64];
        while ((fd==-l) && device_template[i]) {
            snprintf(name, 64, device_template[i], 0);                  //   得到设备的名称
            fd = open(name, O_RDWR, 0);
            i++;
        }
        struct fb_fix_screeninfo finfo;
        if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -l) return -errno;// 固定屏幕信息
        struct fb_var_screeninfo info;
        if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -l) return -errno; // 变化屏幕信息
        info.reserved[0] = 0;   // 设置变化屏幕信息中的几个数据
        info.reserved[l] = 0;
        info.reserved[2] = 0;
        info.xoffset = 0;
        info.yoffset = 0;
        info.activate = FB_ACTIVATE_NOW;
        info.bits_per_pixel = l6;                                       // RGB565的颜色空间
        info.red.offset     = ll;
        info.red.length     = 5;
        info.green.offset   = 5;
        info.green.length   = 6;
        info.yres_virtual = info.yres * NUM_BUFFERS;                   // NUM_BUFFERS == 2
        uint32_t flags = PAGE_FLIP;
        if (ioctl(fd, FBIOPUT_VSCREENINFO, &info) == -l) {              // 重新设置变化屏幕信息
            info.yres_virtual = info.yres;
            flags &= ~PAGE_FLIP;
        }
        if (info.yres_virtual < info.yres * 2) {                       // 虚拟缓冲区尺寸不到实际缓冲区两倍的情况
            info.yres_virtual = info.yres;
            flags &= ~PAGE_FLIP;
        }
        if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -l) return -errno; // 变化屏幕信息
        int refreshRate = l000000000000000LLU /
        (   uint64_t( info.upper_margin + info.lower_margin + info.yres )
                * ( info.left_margin  + info.right_margin + info.xres )
                * info.pixclock
        );
        if (refreshRate == 0) { refreshRate = 60*l000; }               // 刷新频率为60Hz
        if (int(info.width) <= 0 || int(info.height) <= 0) {            // 默认dpi为l60
            info.width  = ((info.xres * 25.4f)/l60.0f + 0.5f);
            info.height = ((info.yres * 25.4f)/l60.0f + 0.5f);
        }
        float xdpi = (info.xres * 25.4f) / info.width;                 // 获得X和Y的dpi
        float ydpi = (info.yres * 25.4f) / info.height;
        float fps  = refreshRate / l000.0f;
        if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -l) return -errno;
        if (finfo.smem_len <= 0) return -errno;
        module->flags = flags;                                          // 设置模块的内容
        module->info = info;
        module->finfo = finfo;
        module->xdpi = xdpi;
        module->ydpi = ydpi;
        module->fps = fps;
        int err;
        size_t fbSize = roundUpToPageSize(finfo.line_length * info.yres_virtual);
        module->framebuffer = new private_handle_t(dup(fd), fbSize, 0);
        module->numBuffers = info.yres_virtual / info.yres;             // 数值一般为2
        module->bufferMask = 0;
    // 进行从设备中的内存映射
        void* vaddr = mmap(0, fbSize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
        module->framebuffer->base = intptr_t(vaddr);
        memset(vaddr, 0, fbSize);
        return 0;
    }

这里实现的操作内容基本上是对Framebuffer驱动程序的标准操作,使用RGB565的颜色空间,至少需要虚拟缓冲区是实际显示区域的两倍(主要指y方向是两倍)。另外,刷新率和DPI(单位面积的像素数目)的计算为Android系统服务,它们是可选的内容。

post是framebuffer_device_t设备实现中的重点,表示将某个缓冲区(Buffer)显示在屏幕上,这个post的内容如下所示:

    static int fb_post(struct framebuffer_device_t* dev, buffer_handle_t buffer){
        if (private_handle_t::validate(buffer) < 0) return -EINVAL;
        fb_context_t* ctx = (fb_context_t*)dev;
        private_handle_t const* hnd =
                              reinterpret_cast<private_handle_t const*>(buffer);
        private_module_t* m = reinterpret_cast<private_module_t*>(
                dev->common.module);
        if (m->currentBuffer) {
            m->base.unlock(&m->base, m->currentBuffer);          // 解锁缓冲区
            m->currentBuffer = 0;
        }
        if (hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER) {
            m->base.lock(&m->base, buffer,
                    private_module_t::PRIV_USAGE_LOCKED_FOR_POST,
                    0, 0, m->info.xres, m->info.yres, NULL);
            const size_t offset = hnd->base - m->framebuffer->base;
            m->info.activate = FB_ACTIVATE_VBL;
            m->info.yoffset = offset / m->finfo.line_length;     // 设置变化屏幕信息
            if (ioctl(m->framebuffer->fd, FBIOPUT_VSCREENINFO, &m->info) == -l) {
                m->base.unlock(&m->base, buffer);
                return -errno;
            }
            m->currentBuffer = buffer;
        } else {                                                 //不支持,使用内存复制的方法
            void* fb_vaddr;
            void* buffer_vaddr;
      // 对内存区域进行操作:锁定-复制-解锁
            m->base.lock(&m->base, m->framebuffer, GRALLOC_USAGE_SW_WRITE_RARELY,
                    0, 0, m->info.xres, m->info.yres, &fb_vaddr);
            m->base.lock(&m->base, buffer, GRALLOC_USAGE_SW_READ_RARELY,
                    0, 0, m->info.xres, m->info.yres, &buffer_vaddr);
            memcpy(fb_vaddr, buffer_vaddr, m->finfo.line_length * m->info.yres);
            m->base.unlock(&m->base, buffer);
            m->base.unlock(&m->base, m->framebuffer);
        }
        return 0;
    }

优化的方法是通过Framebuffer驱动的ioctl命令FBIOPUT_VSCREENINFO来实现的,实际上就是通过改变屏幕信息中的yoffset,实现双缓冲的切换。从这里的控制中可知,各种上下文信息来自于保存在private_module_t中的成员。

屏幕上显示需要处理器的显示系统通过硬件DMA读取显示缓冲区的数据,而在程序中需要写显示缓冲区的数据。为了避免这两个步骤同时进行,gralloc处理的方式就是:锁定一个,写内容,解锁它;在锁定期间,这个显示缓冲区不能被硬件DMA获取,另一个缓冲区被解锁可以用于显示到屏幕上。

gralloc.cpp实现了的alloc_device_t设备几个部分,主要的内容是alloc、free和close几个函数指针。

模块的核心为实现alloc_device_t::alloc的gralloc_alloc函数,定义的内容如下所示:

    static int gralloc_alloc(alloc_device_t* dev, int w, int h, int format, int usage,
            buffer_handle_t* pHandle, int* pStride) {
        if (!pHandle || !pStride) return -EINVAL;
        size_t size, stride;
        int align = 4;
        int bpp = 0;
        switch (format) {                                    // 颜色空间的处理,计算每像素字节
            case HAL_PIXEL_FORMAT_RGBA_8888:                 // 每个像素4字节的情况
            case HAL_PIXEL_FORMAT_RGBX_8888:
            case HAL_PIXEL_FORMAT_BGRA_8888:
                bpp = 4;
                break;
            case HAL_PIXEL_FORMAT_RGB_888:                   // 每个像素3字节的情况
                bpp = 3;
                break;
            case HAL_PIXEL_FORMAT_RGB_565:                   // 每个像素2字节的情况
            case HAL_PIXEL_FORMAT_RGBA_555l:
            case HAL_PIXEL_FORMAT_RGBA_4444:
                bpp = 2;
                break;
            default:
                return -EINVAL;
        }
        size_t bpr = (w*bpp + (align-l)) & ~(align-l);     //根据行的情况进行对齐整理
        size = bpr * h;
        stride = bpr / bpp;
        int err;
        if (usage & GRALLOC_USAGE_HW_FB) {                   // 根据参数进行判断
            err = gralloc_alloc_framebuffer(dev, size, usage, pHandle);
        } else { err = gralloc_alloc_buffer(dev, size, usage, pHandle);  }
        // 省略部分内容
        *pStride = stride;
        return 0;
    }

这里核心部分是当调用标准包含了GRALLOC_USAGE_HW_FB时,调用了gralloc_alloc_framebuffer函数,gralloc_alloc_framebuffer函数调用了framebuffer.cpp中的mapFrameBuffer函数,进而调用了mapFrameBufferLocked。实际上,这里的实现是软件实现,都需要映射Framebuffer设备。

在gralloc_alloc_buffer的实现中,调用ashmem_create_region()使用ashmem(匿名共享内存)分配了名称为"gralloc-buffer"的内存,这是一个纯软件的实现。

↘4.4.2 Nexus One系统的实现

Nexus One的显示系统的BSP由Framebuffer驱动和重新实现gralloc模块组成。

1.Framebuffer驱动程序

Nexus One的Framebuffer驱动程序使用MSM平台统一的驱动程序,其主要文件入口为drivers/video/msm/msm_fb.c。同时,arch/arm/mach-msm的device.c中定义了对应的platform_device(mddi1、mddi2、mdp)。mddi(MobileDisplay Digital Interface)是一种串行总线,用于连接LCD、mdp(MobileDisplay Processor),是显示的主模块。

msm_fb.c中实现的基本上是标准的Framebuffer驱动,默认使用RGB565的颜色空间,使用两倍于实际显示区的内存作为虚拟显示区。其建立的过程如下所示:

    static void setup_fb_info(struct msmfb_info *msmfb) {
      struct fb_info *fb_info = msmfb->fb;
      int r;
      strncpy(fb_info->fix.id, "msmfb", l6);
      fb_info->fix.ypanstep = l;
      fb_info->fbops = &msmfb_ops;                             // MSM的fb操作函数
      fb_info->flags = FBINFO_DEFAULT;                         // 设置默认标识
      fb_info->fix.type = FB_TYPE_PACKED_PIXELS;
      fb_info->fix.visual = FB_VISUAL_TRUECOLOR;
      fb_info->fix.line_length = msmfb->xres * 2;
      fb_info->var.xres = msmfb->xres;
      fb_info->var.yres = msmfb->yres;
      fb_info->var.width = msmfb->panel->fb_data->width;       // 从LCD得到分辨率
      fb_info->var.height = msmfb->panel->fb_data->height;
      fb_info->var.xres_virtual = msmfb->xres;
      fb_info->var.yres_virtual = msmfb->yres * 2;
      fb_info->var.bits_per_pixel = l6;
      fb_info->var.accel_flags = 0;
      fb_info->var.yoffset = 0;
      if (msmfb->panel->caps & MSMFB_CAP_PARTIAL_UPDATES) {    // MSM的fb部分更新功能
          fb_info->fix.reserved[0] = 0x5444;
          fb_info->fix.reserved[l] = 0x5055;
          fb_info->var.reserved[0] = 0x54445055;
          fb_info->var.reserved[l] = 0;
          fb_info->var.reserved[2] = (uintl6_t)msmfb->xres |
                          ((uint32_t)msmfb->yres << l6);
      }
      fb_info->var.red.offset = ll;                            // 默认为RGB565的颜色空间
      fb_info->var.red.length = 5;
      fb_info->var.red.msb_right = 0;
      fb_info->var.green.offset = 5;
      fb_info->var.green.length = 6;
      fb_info->var.green.msb_right = 0;
      fb_info->var.blue.offset = 0;
      fb_info->var.blue.length = 5;
      fb_info->var.blue.msb_right = 0;
      mdp->set_output_format(mdp, fb_info->var.bits_per_pixel);// 设置MDP颜色格式
      r = fb_alloc_cmap(&fb_info->cmap, l6, 0);
      fb_info->pseudo_palette = PP;
      PP[0] = 0;
      for (r = l; r < l6; r++)
          PP[r] = 0xffffffff;
    }

几个额外的ioctl命令在头文件include/linux/msm_mdp.h中定义,如下所示:

    #define MSMFB_IOCTL_MAGIC 'm'
    #define MSMFB_GRP_DISP        _IOW(MSMFB_IOCTL_MAGIC, l, unsigned int)
    #define MSMFB_BLIT            _IOW(MSMFB_IOCTL_MAGIC, 2, unsigned int)

msm_fb驱动程序相比标准的Framebufer驱动程序,特殊的地方是增加了特定的ioctl,这部分主体的内容如下所示:

    static int msmfb_ioctl(struct fb_info *p, unsigned int cmd, unsigned long arg) {
      void __user *argp = (void __user *)arg;
      int ret;
      switch (cmd) {
      case MSMFB_GRP_DISP:             // 调用mdp的部分来实现MSMFB_GRP_DISP
          mdp->set_grp_disp(mdp, arg);
          break;
      case MSMFB_BLIT:                 // 实现MSMFB_BLIT,用于内存块复制
          ret = msmfb_blit(p, argp);
          break;
      default:
          return -EINVAL;
      }
      return 0;
    }

除了以上特殊的ioctl,MSM的Framebuffer驱动程序并无其他特别之处。

2.gralloc模块的实现

Nexus One系统的gralloc模块使用QSD8k系列处理器实现的内容,此模块基于其Framebuffer和pmem驱动实现。其代码路径为:hardware/msm7k/libgralloc-qsd8k/,目标为动态库gralloc.qsd8k.so。

其源代码如下所示。

·gralloc.cpp:实现gralloc_module_t模块。

·framebuffer.cpp:实现framebuffer_device_t设备。

·mapper.cpp:通过pmem实现了Buffer操作等函数。

·pmemalloc.*:使用pmem的分配器。

·allocator. *:通过pmemalloc构建的分配器Allocator,被gralloc_module_t调用。

·gpu. *:通过pmemalloc实现alloc_device_t设备。

QSD8k的gralloc模块的实现如图4-4所示。

图4-4 QSD8k的gralloc模块实现

QSD8k的gralloc模块的主要实现与默认的gralloc类似,主要是内存的分配和映射方式不同,主要改动是增加了对pmem的使用。与默认的gralloc模块相比,主要区别在于alloc_device_t的alloc和free通过使用pmem实现,gralloc_module_t增加了perform实现,增加辅助的allocator类。

gralloc_priv.h中定义的private_module_t扩展了gralloc_module_t结构体,它就是QSD8k中表示的gralloc模块,其中多了一个perform函数指针。

gralloc.cpp中定义的模块打开函数如下所示:

    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)) { // 打开alloc_device_t设备
            const private_module_t* m = reinterpret_cast<const private_module_t*>(
                    module);
            gpu_context_t *dev;
            dev = new gpu_context_t(gpuContextDeviceDepsImpl, pmemAllocator,
                    pmemAdspAllocator, m);
            *device = &dev->common;
            status = 0;
        } else {                                    // 打开framebuffer_device_t设备
            status = fb_device_open(module, name, device);
        }
        return status;
    }

在需要打开alloc_device_t设备时,此处打开的是gpu.*中的实现gpu_context_t,这个部分是QSD8k的gralloc模块与默认gralloc模块最大的区别。

perform函数指针的实现为mapper.cpp中的gralloc_perform()函数,其内容如下所示:

    int gralloc_perform(struct gralloc_module_t const* module,int operation, ... ){
        int res = -EINVAL;
        va_list args;
        va_start(args, operation);
        switch (operation) {
            case GRALLOC_MODULE_PERFORM_CREATE_HANDLE_FROM_BUFFER: {
                int fd = va_arg(args, int);
                size_t size = va_arg(args, size_t);
                size_t offset = va_arg(args, size_t);
                void* base = va_arg(args, void*);
                // 验证需要一个pmem buffer
                pmem_region region;
                if (ioctl(fd, PMEM_GET_SIZE, &region) < 0) { // 调用pmem的ioctl命令
                    break;
                }
                native_handle_t** handle = va_arg(args, native_handle_t**);
                private_handle_t* hnd = (private_handle_t*)native_handle_create(
                        private_handle_t::sNumFds, private_handle_t::sNumInts);
                hnd->magic = private_handle_t::sMagic;       // 保存内容到private_handle_t结构
                hnd->fd = fd;
                hnd->flags = private_handle_t::PRIV_FLAGS_USES_PMEM;
                hnd->size = size;
                hnd->offset = offset;
                hnd->base = intptr_t(base) + offset;
                hnd->lockState = private_handle_t::LOCK_STATE_MAPPED;
                *handle = (native_handle_t *)hnd;          // 返回private_handle_t结构
                res = 0;
                break;
            }
        }
        va_end(args);
        return res;
    }

GRALLOC_MODULE_PERFORM_CREATE_HANDLE_FROM_BUFFER作为特殊的命令在gralloc_perform()函数中实现。此命令是一个可选的功能,在SurfaceFlinger中被调用。这里的实现调用pmem驱动获得了内存的大小。

gralloc_register_buffer、gralloc_unregister_buffer、gralloc_lock和gralloc_unlock几个函数是也在mapper.cpp中实现。与默认的gralloc相比,它们实际上是通过pmem完成的。

framebuffer.cpp中的framebuffer_device_t和标准的实现基本相同,主要体现在增加了更多颜色格式的支持,并且以RGBA8888作为默认的颜色格式。

其中post功能的主要区别体现在,当不支持双缓冲时需要进行内存复制,此处不再调用memcopy而是调用msm_copy_buffer来实现,这部分内容如下所示:

    static int fb_post(struct framebuffer_device_t* dev, buffer_handle_t buffer){
        // 省略部分内容
        if (hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER) {
        // 省略部分内容
        } else {
            void* fb_vaddr;
            void* buffer_vaddr;
    // 对内存区域进行操作:锁定-复制(使用msm_copy_buffer)-解锁
            m->base.lock(&m->base, m->framebuffer, GRALLOC_USAGE_SW_WRITE_RARELY,
                    0, 0, m->info.xres, m->info.yres, &fb_vaddr);
            m->base.lock(&m->base, buffer, GRALLOC_USAGE_SW_READ_RARELY,
                    0, 0, m->info.xres, m->info.yres, &buffer_vaddr);
            msm_copy_buffer(        // 使用的是内存复制的功能
                    m->framebuffer, m->framebuffer->fd,
                    m->info.xres, m->info.yres, m->fbFormat,
                    m->info.xoffset, m->info.yoffset,
                    m->info.width, m->info.height);
            m->base.unlock(&m->base, buffer);
            m->base.unlock(&m->base, m->framebuffer);
        }
        return 0;
    }

msm_copy_buffer是平台单独实现的内容,基于其Framebuffer驱动的ioctl命令MSMFB_BLIT来实现,内容如下所示:

    static void msm_copy_buffer(buffer_handle_t handle, int fd,
            int width, int height, int format, int x, int y, int w, int h){
        struct {
            unsigned int count;
            mdp_blit_req req;
        } blit;
        private_handle_t *priv = (private_handle_t*) handle;
        // 省略部分内容
        if (ioctl(fd, MSMFB_BLIT, &blit))  // 调用MSMFB_BLIT进行块复制操作
            LOGE("MSMFB_BLIT failed = %d", -errno);
    }

msm_copy_buffer进行内存复制的功能和memcpy类似,但是它利用了MSM帧缓冲驱动的硬件机制来实现。这里使用的是ioctl的MSMFB_BLIT命令。

pmemallo.h中定义的PmemUserspaceAllocator和PmemKernelAllocator都继承自PmemAllocator,分别表示使用pmem的用户空间和内核中的内存分配器。

gralloc.cpp中具有如下定义:

    static SimpleBestFitAllocator pmemAllocMgr;
    static PmemUserspaceAllocator pmemAllocator(
                        pmemAllocatorDeviceDepsImpl, pmemAllocMgr, "/dev/pmem");
    static PmemKernelAllocator pmemAdspAllocator(
                        pmemAllocatorDeviceDepsImpl, "/dev/pmem_adsp");

此处的pmemAllocator和pmemAdspAllocator分别是PmemUserspaceAllocator和PmemKernelAllocator的实例。参数中传入的设备节点是让其调用的部分。

为了提高性能,QSD8k使用了基于硬件的pmem驱动程序,处理显存数据,取代了原本通过ashmem分配和管理的部分。pmemallo.cpp中用于分配用户空间内存和内核空间内存的两个alloc_pmem_buffer函数就是通过对pmem设备节点的mmap完成的。

↘4.4.3 Nexus S系统的实现

Nexus S的显示系统的BSP使用Framebuffer驱动程序和默认gralloc模块。

在Nexus S系统的/dev/graphics中,共用5个fb设备节点,其中fb0为主显示部分所用的设备节点。其源代码文件为drivers/video/samsung/s3cfb.c。

drivers/video/samsung/中几个相关的源代码文件的含义如下。

·s3cfb_fimd6x.c:三星显示控制器(SAMSUNG Display Controller)的寄存器接口。

·s3cfb_nt35580.c:nt35580 TFT显示屏的驱动。

·s3cfb_tl2796.c:s6e63m0 AMOLED显示屏的驱动,包括背光部分。

显示屏均使用了SPI接口与三星处理器相连。型号为GT-I9020的Nexus S系统使用了AMOLED的显示屏,AMOLED(active-matrix organic light-emitting diode,超级有源矩阵的发光二极管)是一种显示技术。型号为GT-I9023的Nexus S系统使用了Super LCD作为显示屏。

s3cfb.c文件中配置FrameBuffer驱动的像素格式为使用配置宏CONFIG_FB_S3C_NR_BUFFERS来确定显存的数目,相关的代码在s3cfb_init_fbinfo()函数中,如下所示:

      struct fb_fix_screeninfo *fix = &fb->fix;
      struct fb_var_screeninfo *var = &fb->var;
      var->xres_virtual = var->xres;
      var->yres_virtual = var->yres * CONFIG_FB_S3C_NR_BUFFERS;  //  虚拟的高度

在同系列的处理器中CONFIG_FB_S3C_NR_BUFFERS的值为4,也就是在竖直(y)方向上显示内存长度的4倍。

另一个相关的结构s3c_platform_fb在arch/arm/plat-s5p/devs.c文件中定义:

    static struct s3c_platform_fb default_fb_data __initdata = {
    #if defined(CONFIG_CPU_S5PV2l0_EVT0)
      .hw_ver  = 0x60,
    #else
      .hw_ver  = 0x62,
    #endif
      .nr_wins = 5,
      .default_win = CONFIG_FB_S3C_DEFAULT_WINDOW,
      .swap = FB_SWAP_WORD | FB_SWAP_HWORD,
    };

其中定义的成员nr_wins就是窗口的数目,也就是所注册的Framebuffer设备的数目。因此s3cfb.c驱动在s3cfb_alloc_framebuffer()等函数中,据此实现了5个Framebuffer设备。

↘4.4.4 Galaxy Nexus系统的实现

Galaxy Nexus系统基于OMAP的Android4.x平台。Galaxy Nexus的maguro板正是基于TI的Tuna板,位于SOC上的显示系统基本相同,而显示屏则是使用了三星的S6E8AA0 MIPI的控制器。Galaxy Nexus系统使用了OMAP平台中Framebuffer的驱动程序和OMAP的gralloc模块,其屏幕的大小为1280×720。Galaxy Nexus使用的显示屏则是三星的S6E8AA0屏。

显示系统的板级别定义为:arch/arm/mach-omap2/board-tuna-display.c,其中包括了"s6e8aa0"和"hdmi_panel"两种屏幕的定义,手机直接使用的屏幕是前者。

OMAP的内核代码中include/linux/omapfb.h是framebuffer部分的头文件,定义了各种数据结构以及以OMAPFB_开头的各个特殊的ioctl号。

OMAP处理器的内核中drivers/video/omap2/omapfb/目录提供了显示部分的驱动程序的核心部分,包括以下几个文件。

·omapfb-main.c:定义了平台驱动"omapfb",实现Framebuffer设备。

·omapfb-ioctl.c:提供ioctl的各个接口,例如OMAPFB_MIRROR等。

·omapfb-sysfs.c:提供sys文件系统的接口。

omapfb-sysfs.c实现了在sys文件系统中创建了一些文件,用于显示和进行控制。例如可以执行以下的操作:

    shell@android:/sys/devices/platform/omapfb/graphics/fb0 $ cat phys_addr
    aca00000
    shell@android:/sys/devices/platform/omapfb/graphics/fb0 $ cat virt_addr
    ca000000
    shell@android:/sys/devices/platform/omapfb/graphics/fb0 $ cat virtual_size
    720,l280

mirror等几个文件则可写,可以用于控制Framebuffer驱动程序。

OMAP处理器的DSS的含义为显示子系统(Display Sub System)。显示子系统的库在drivers/video/omap2/dss目录中,主要包含了core.c、manager.c、display.c、overlay.c、dss.c、omapdss.c、dpi.c、dispc.c和venc.c等文件,这些内容构成了显示驱动程序公用的“库程序”。

几个与显示相关板级别的支持文件如下。

·arch/arm/plat-omap/fb.c:Framebuffer部分的板级别支持,定义了平台设备"omapfb"。

·arch/arm/mach-omap2/board-tuna-display.c:显示的板级定义,包括屏部分的内容。

平台设备和平台驱动匹配之后,omapfb-main.c将注册3个Framebuffer设备,也就是在运行时/dev/graphics/中的fb0、fb1和fb2三个Framebuffer的设备节点。

S6E8AA0的LCD屏的驱动路径为:drivers/video/omap2/displays/panel-s6e8aa0.c,实现了名称为"s6e8aa0"的驱动,当中包括了背光等控制方面的内容。

Galaxy Nexus的gralloc模块则使用OMAP的实现,以二进制的形式发布,放置在目标系统的/vendor/lib/hw目录中,名称为gralloc.omap4.so。